From c8e8688664620182d7fc68d9b9d80e6f9e7764f8 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 18 Sep 2011 22:35:19 +0900 Subject: [PATCH] Initial Import --- Files | 319 ++ Porting | 172 + README | 226 ++ binary/license | 95 + build/dgncomp.dsp | 297 ++ build/dgnstuff.dsp | 97 + build/dgnstuff.mak | 59 + build/dlb_main.dsp | 179 + build/levcomp.dsp | 198 + build/levstuff.dsp | 97 + build/levstuff.mak | 59 + build/makedefs.dsp | 198 + build/nethackw.dsp | 1107 ++++++ build/recover.dsp | 148 + build/tile2bmp.dsp | 146 + build/tilemap.dsp | 281 ++ build/tiles.dsp | 97 + build/tiles.mak | 21 + build/uudecode.dsp | 146 + dat/Arch.des | 460 +++ dat/Barb.des | 368 ++ dat/Caveman.des | 316 ++ dat/Healer.des | 377 ++ dat/Knight.des | 408 +++ dat/Monk.des | 353 ++ dat/Priest.des | 337 ++ dat/Ranger.des | 355 ++ dat/Rogue.des | 479 +++ dat/Samurai.des | 426 +++ dat/Tourist.des | 519 +++ dat/Valkyrie.des | 332 ++ dat/Wizard.des | 439 +++ dat/bigroom.des | 436 +++ dat/castle.des | 241 ++ dat/cmdhelp | 121 + dat/data.base | 4411 ++++++++++++++++++++++ dat/dungeon.def | 138 + dat/endgame.des | 616 ++++ dat/gehennom.des | 674 ++++ dat/help | 198 + dat/hh | 116 + dat/history | 199 + dat/knox.des | 104 + dat/license | 95 + dat/medusa.des | 215 ++ dat/mines.des | 1021 ++++++ dat/opthelp | 226 ++ dat/oracle.des | 56 + dat/oracles.txt | 99 + dat/quest.txt | 3507 ++++++++++++++++++ dat/rumors.fal | 392 ++ dat/rumors.tru | 359 ++ dat/sokoban.des | 623 ++++ dat/tower.des | 136 + dat/wizhelp | 23 + dat/yendor.des | 276 ++ doc/Guidebook.mn | 2746 ++++++++++++++ doc/Guidebook.tex | 3395 +++++++++++++++++ doc/Guidebook.txt | 3630 ++++++++++++++++++ doc/dgn_comp.6 | 402 ++ doc/dgn_comp.txt | 330 ++ doc/dlb.6 | 83 + doc/dlb.txt | 132 + doc/fixes22.0 | 353 ++ doc/fixes30.0 | 164 + doc/fixes31.1 | 110 + doc/fixes31.2 | 92 + doc/fixes31.3 | 22 + doc/fixes32.0 | 344 ++ doc/fixes32.1 | 133 + doc/fixes32.2 | 128 + doc/fixes32.3 | 30 + doc/fixes33.0 | 372 ++ doc/fixes33.1 | 444 +++ doc/fixes34.0 | 595 +++ doc/fixes34.1 | 471 +++ doc/fixes34.2 | 180 + doc/fixes34.3 | 146 + doc/lev_comp.6 | 572 +++ doc/lev_comp.txt | 726 ++++ doc/nethack.6 | 305 ++ doc/nethack.txt | 208 ++ doc/recover.6 | 116 + doc/recover.txt | 132 + doc/tmac.n | 764 ++++ doc/window.doc | 805 ++++ include/align.h | 42 + include/amiconf.h | 190 + include/artifact.h | 64 + include/artilist.h | 255 ++ include/attrib.h | 44 + include/beconf.h | 36 + include/bitmfile.h | 37 + include/color.h | 52 + include/config.h | 358 ++ include/config1.h | 204 ++ include/coord.h | 12 + include/decl.h | 390 ++ include/def_os2.h | 212 ++ include/dgn_file.h | 76 + include/display.h | 390 ++ include/dlb.h | 141 + include/dungeon.h | 169 + include/edog.h | 33 + include/emin.h | 14 + include/engrave.h | 27 + include/epri.h | 24 + include/eshk.h | 45 + include/extern.h | 2387 ++++++++++++ include/flag.h | 313 ++ include/func_tab.h | 23 + include/gem_rsc.h | 65 + include/global.h | 345 ++ include/hack.h | 341 ++ include/lev.h | 49 + include/load_img.h | 46 + include/mac-carbon.h | 32 + include/mac-qt.h | 33 + include/mac-term.h | 34 + include/macconf.h | 119 + include/macpopup.h | 15 + include/mactty.h | 347 ++ include/macwin.h | 246 ++ include/mail.h | 20 + include/mfndpos.h | 29 + include/micro.h | 21 + include/mkroom.h | 103 + include/monattk.h | 100 + include/mondata.h | 193 + include/monflag.h | 201 + include/monst.h | 181 + include/monsym.h | 147 + include/mttypriv.h | 60 + include/nhlan.h | 48 + include/ntconf.h | 201 + include/obj.h | 306 ++ include/objclass.h | 186 + include/os2conf.h | 107 + include/patchlevel.h | 404 +++ include/pcconf.h | 338 ++ include/permonst.h | 81 + include/prop.h | 148 + include/qt_clust.h | 29 + include/qt_kde0.h | 10 + include/qt_win.h | 845 +++++ include/qt_xpms.h | 1406 +++++++ include/qtext.h | 112 + include/qttableview.h | 251 ++ include/quest.h | 41 + include/rect.h | 13 + include/region.h | 67 + include/rm.h | 526 +++ include/skills.h | 121 + include/sp_lev.h | 246 ++ include/spell.h | 22 + include/system.h | 551 +++ include/tcap.h | 57 + include/tile2x11.h | 22 + include/timeout.h | 44 + include/tosconf.h | 86 + include/tradstdc.h | 273 ++ include/trampoli.h | 332 ++ include/trap.h | 77 + include/unixconf.h | 351 ++ include/vault.h | 27 + include/vision.h | 58 + include/vmsconf.h | 265 ++ include/wceconf.h | 354 ++ include/winGnome.h | 18 + include/winX.h | 411 +++ include/winami.h | 126 + include/wingem.h | 112 + include/winprocs.h | 220 ++ include/wintty.h | 255 ++ include/wintype.h | 71 + include/xwindow.h | 95 + include/xwindowp.h | 72 + include/you.h | 368 ++ include/youprop.h | 342 ++ nethack.dsw | 212 ++ src/Makefile | 1419 ++++++++ src/Makefile.bcc | 1378 +++++++ src/Makefile.gcc | 1352 +++++++ src/allmain.c | 633 ++++ src/alloc.c | 144 + src/apply.c | 3057 ++++++++++++++++ src/artifact.c | 1461 ++++++++ src/attrib.c | 731 ++++ src/ball.c | 790 ++++ src/bones.c | 474 +++ src/botl.c | 309 ++ src/cmd.c | 2555 +++++++++++++ src/dbridge.c | 942 +++++ src/decl.c | 281 ++ src/detect.c | 1299 +++++++ src/dig.c | 1571 ++++++++ src/display.c | 2196 +++++++++++ src/dlb.c | 544 +++ src/do.c | 1687 +++++++++ src/do_name.c | 1044 ++++++ src/do_wear.c | 2185 +++++++++++ src/dog.c | 939 +++++ src/dogmove.c | 867 +++++ src/dokick.c | 1491 ++++++++ src/dothrow.c | 1772 +++++++++ src/drawing.c | 901 +++++ src/dungeon.c | 1744 +++++++++ src/eat.c | 2586 +++++++++++++ src/end.c | 1106 ++++++ src/engrave.c | 1259 +++++++ src/exper.c | 248 ++ src/explode.c | 580 +++ src/extralev.c | 346 ++ src/files.c | 2424 +++++++++++++ src/fountain.c | 602 +++ src/hack.c | 2303 ++++++++++++ src/hacklib.c | 615 ++++ src/invent.c | 2934 +++++++++++++++ src/light.c | 626 ++++ src/lock.c | 921 +++++ src/mail.c | 625 ++++ src/makemon.c | 1800 +++++++++ src/mapglyph.c | 241 ++ src/mcastu.c | 788 ++++ src/mhitm.c | 1460 ++++++++ src/mhitu.c | 2633 ++++++++++++++ src/minion.c | 322 ++ src/mklev.c | 1592 ++++++++ src/mkmap.c | 479 +++ src/mkmaze.c | 1415 ++++++++ src/mkobj.c | 1639 +++++++++ src/mkroom.c | 781 ++++ src/mon.c | 2817 ++++++++++++++ src/mondata.c | 757 ++++ src/monmove.c | 1369 +++++++ src/monst.c | 3477 ++++++++++++++++++ src/mplayer.c | 340 ++ src/mthrowu.c | 824 +++++ src/muse.c | 2182 +++++++++++ src/music.c | 755 ++++ src/o_init.c | 427 +++ src/objects.c | 991 +++++ src/objnam.c | 2787 ++++++++++++++ src/options.c | 3862 ++++++++++++++++++++ src/pager.c | 964 +++++ src/pickup.c | 2417 ++++++++++++ src/pline.c | 440 +++ src/polyself.c | 1339 +++++++ src/potion.c | 2012 ++++++++++ src/pray.c | 1872 ++++++++++ src/priest.c | 721 ++++ src/quest.c | 387 ++ src/questpgr.c | 442 +++ src/read.c | 1878 ++++++++++ src/rect.c | 194 + src/region.c | 985 +++++ src/restore.c | 1093 ++++++ src/rip.c | 175 + src/rnd.c | 145 + src/role.c | 1491 ++++++++ src/rumors.c | 382 ++ src/save.c | 1120 ++++++ src/shk.c | 4098 +++++++++++++++++++++ src/shknam.c | 530 +++ src/sit.c | 451 +++ src/sounds.c | 1033 ++++++ src/sp_lev.c | 2663 ++++++++++++++ src/spell.c | 1264 +++++++ src/steal.c | 635 ++++ src/steed.c | 641 ++++ src/teleport.c | 1291 +++++++ src/timeout.c | 1856 ++++++++++ src/topten.c | 977 +++++ src/track.c | 68 + src/trap.c | 4015 ++++++++++++++++++++ src/u_init.c | 1062 ++++++ src/uhitm.c | 2512 +++++++++++++ src/vault.c | 826 +++++ src/version.c | 185 + src/vision.c | 2622 +++++++++++++ src/weapon.c | 1319 +++++++ src/were.c | 165 + src/wield.c | 800 ++++ src/windows.c | 146 + src/wizard.c | 638 ++++ src/worm.c | 748 ++++ src/worn.c | 794 ++++ src/write.c | 241 ++ src/zap.c | 4146 +++++++++++++++++++++ sys/amiga/Build.ami | 146 + sys/amiga/Install.ami | 200 + sys/amiga/Makefile.agc | 1337 +++++++ sys/amiga/Makefile.ami | 1679 +++++++++ sys/amiga/NetHack.cnf | 213 ++ sys/amiga/amidos.c | 490 +++ sys/amiga/amidos.p | 42 + sys/amiga/amifont.uu | 9 + sys/amiga/amifont8.uu | 59 + sys/amiga/amigst.c | 43 + sys/amiga/amii.hlp | 31 + sys/amiga/amimenu.c | 95 + sys/amiga/amirip.c | 405 +++ sys/amiga/amisnd.c | 284 ++ sys/amiga/amistack.c | 21 + sys/amiga/amitty.c | 67 + sys/amiga/amiwind.c | 949 +++++ sys/amiga/amiwind.p | 40 + sys/amiga/clipwin.c | 268 ++ sys/amiga/colorwin.c | 256 ++ sys/amiga/cvtsnd.c | 96 + sys/amiga/grave16.xpm | 223 ++ sys/amiga/ifchange | 56 + sys/amiga/mkdmake | 14 + sys/amiga/txt2iff.c | 457 +++ sys/amiga/winami.c | 1725 +++++++++ sys/amiga/winami.p | 57 + sys/amiga/winchar.c | 1246 +++++++ sys/amiga/windefs.h | 203 ++ sys/amiga/winext.h | 144 + sys/amiga/winfuncs.c | 2451 +++++++++++++ sys/amiga/winkey.c | 106 + sys/amiga/winmenu.c | 1597 ++++++++ sys/amiga/winproto.h | 151 + sys/amiga/winreq.c | 1177 ++++++ sys/amiga/winstr.c | 522 +++ sys/amiga/xpm2iff.c | 353 ++ sys/atari/Install.tos | 124 + sys/atari/atarifnt.uue | 119 + sys/atari/nethack.mnu | 53 + sys/atari/setup.g | 17 + sys/atari/tos.c | 372 ++ sys/atari/unx2atar.sed | 23 + sys/be/README | 63 + sys/be/bemain.c | 268 ++ sys/mac/Files.r | 148 + sys/mac/Install.mw | 289 ++ sys/mac/MacHelp | 158 + sys/mac/NHDeflts | 161 + sys/mac/NHrsrc.hqx | 991 +++++ sys/mac/NHsound.hqx | 4197 +++++++++++++++++++++ sys/mac/News | 9 + sys/mac/README | 36 + sys/mac/dprintf.c | 47 + sys/mac/maccurs.c | 223 ++ sys/mac/macerrs.c | 170 + sys/mac/macfile.c | 465 +++ sys/mac/machelp.hqx | 67 + sys/mac/macmain.c | 288 ++ sys/mac/macmenu.c | 1196 ++++++ sys/mac/macsnd.c | 107 + sys/mac/mactopl.c | 65 + sys/mac/mactty.c | 1225 +++++++ sys/mac/macunix.c | 38 + sys/mac/macwin.c | 2650 ++++++++++++++ sys/mac/mgetline.c | 74 + sys/mac/mmodal.c | 30 + sys/mac/mrecover.c | 1419 ++++++++ sys/mac/mrecover.hqx | 69 + sys/mac/mttymain.c | 586 +++ sys/msdos/Install.dos | 268 ++ sys/msdos/Makefile.BC | 2081 +++++++++++ sys/msdos/Makefile.GCC | 1260 +++++++ sys/msdos/Makefile.MSC | 1113 ++++++ sys/msdos/NHAccess.nh | 134 + sys/msdos/moveinit.pat | 37 + sys/msdos/msdos.c | 520 +++ sys/msdos/msdoshlp.txt | 188 + sys/msdos/nhico.uu | 25 + sys/msdos/nhpif.uu | 16 + sys/msdos/ovlinit.c | 154 + sys/msdos/pckeys.c | 88 + sys/msdos/pctiles.c | 259 ++ sys/msdos/pctiles.h | 65 + sys/msdos/pcvideo.h | 283 ++ sys/msdos/portio.h | 71 + sys/msdos/schema1.BC | 252 ++ sys/msdos/schema2.BC | 248 ++ sys/msdos/schema3.MSC | 429 +++ sys/msdos/setup.bat | 167 + sys/msdos/sound.c | 332 ++ sys/msdos/tile2bin.c | 312 ++ sys/msdos/video.c | 969 +++++ sys/msdos/vidtxt.c | 472 +++ sys/msdos/vidvga.c | 1480 ++++++++ sys/os2/Install.os2 | 276 ++ sys/os2/Makefile.os2 | 1782 +++++++++ sys/os2/nhpmico.uu | 23 + sys/os2/os2.c | 364 ++ sys/share/Makefile.lib | 21 + sys/share/NetHack.cnf | 168 + sys/share/dgn_comp.h | 27 + sys/share/dgn_lex.c | 1475 ++++++++ sys/share/dgn_yacc.c | 1054 ++++++ sys/share/ioctl.c | 187 + sys/share/lev_comp.h | 79 + sys/share/lev_lex.c | 2088 +++++++++++ sys/share/lev_yacc.c | 2418 ++++++++++++ sys/share/nhlan.c | 191 + sys/share/pcmain.c | 714 ++++ sys/share/pcsys.c | 541 +++ sys/share/pctty.c | 85 + sys/share/pcunix.c | 289 ++ sys/share/random.c | 386 ++ sys/share/sounds/README | 34 + sys/share/sounds/bell.uu | 407 +++ sys/share/sounds/bugle.uu | 328 ++ sys/share/sounds/erthdrum.uu | 863 +++++ sys/share/sounds/firehorn.uu | 299 ++ sys/share/sounds/frsthorn.uu | 259 ++ sys/share/sounds/lethdrum.uu | 433 +++ sys/share/sounds/mgcflute.uu | 413 +++ sys/share/sounds/mgcharp.uu | 407 +++ sys/share/sounds/toolhorn.uu | 345 ++ sys/share/sounds/wdnflute.uu | 328 ++ sys/share/sounds/wdnharp.uu | 282 ++ sys/share/tclib.c | 482 +++ sys/share/termcap | 173 + sys/share/termcap.uu | 504 +++ sys/share/unixtty.c | 450 +++ sys/share/uudecode.c | 252 ++ sys/unix/Install.unx | 268 ++ sys/unix/Makefile.dat | 144 + sys/unix/Makefile.doc | 95 + sys/unix/Makefile.src | 814 +++++ sys/unix/Makefile.top | 266 ++ sys/unix/Makefile.utl | 403 ++ sys/unix/README.linux | 107 + sys/unix/cpp1.shr | 1783 +++++++++ sys/unix/cpp2.shr | 1763 +++++++++ sys/unix/cpp3.shr | 1901 ++++++++++ sys/unix/depend.awk | 151 + sys/unix/nethack.sh | 68 + sys/unix/setup.sh | 41 + sys/unix/snd86unx.shr | 1064 ++++++ sys/unix/unixmain.c | 541 +++ sys/unix/unixres.c | 209 ++ sys/unix/unixunix.c | 349 ++ sys/vms/Install.vms | 479 +++ sys/vms/Makefile.dat | 137 + sys/vms/Makefile.doc | 68 + sys/vms/Makefile.src | 463 +++ sys/vms/Makefile.top | 147 + sys/vms/Makefile.utl | 372 ++ sys/vms/install.com | 268 ++ sys/vms/lev_lex.h | 25 + sys/vms/nethack.com | 53 + sys/vms/oldcrtl.c | 192 + sys/vms/spec_lev.com | 88 + sys/vms/vmsbuild.com | 307 ++ sys/vms/vmsfiles.c | 293 ++ sys/vms/vmsmail.c | 486 +++ sys/vms/vmsmain.c | 454 +++ sys/vms/vmsmisc.c | 30 + sys/vms/vmstty.c | 491 +++ sys/vms/vmsunix.c | 474 +++ sys/wince/Install.ce | 136 + sys/wince/bootstrp.mak | 882 +++++ sys/wince/ceinc/assert.h | 16 + sys/wince/ceinc/errno.h | 4 + sys/wince/ceinc/fcntl.h | 67 + sys/wince/ceinc/sys/stat.h | 2 + sys/wince/celib.c | 824 +++++ sys/wince/cesetup.bat | 40 + sys/wince/cesound.c | 27 + sys/wince/defaults.nh | 139 + sys/wince/keypad.uu | 8 + sys/wince/menubar.uu | 12 + sys/wince/mhaskyn.c | 12 + sys/wince/mhaskyn.h | 11 + sys/wince/mhcmd.c | 1378 +++++++ sys/wince/mhcmd.h | 25 + sys/wince/mhcolor.c | 217 ++ sys/wince/mhcolor.h | 17 + sys/wince/mhdlg.c | 787 ++++ sys/wince/mhdlg.h | 15 + sys/wince/mhfont.c | 177 + sys/wince/mhfont.h | 14 + sys/wince/mhinput.c | 86 + sys/wince/mhinput.h | 36 + sys/wince/mhmain.c | 1150 ++++++ sys/wince/mhmain.h | 17 + sys/wince/mhmap.c | 914 +++++ sys/wince/mhmap.h | 21 + sys/wince/mhmenu.c | 1568 ++++++++ sys/wince/mhmenu.h | 17 + sys/wince/mhmsg.h | 60 + sys/wince/mhmsgwnd.c | 591 +++ sys/wince/mhmsgwnd.h | 15 + sys/wince/mhrip.c | 17 + sys/wince/mhrip.h | 15 + sys/wince/mhstatus.c | 273 ++ sys/wince/mhstatus.h | 14 + sys/wince/mhtext.c | 236 ++ sys/wince/mhtext.h | 14 + sys/wince/mhtxtbuf.c | 238 ++ sys/wince/mhtxtbuf.h | 15 + sys/wince/mswproc.c | 1966 ++++++++++ sys/wince/newres.h | 40 + sys/wince/resource.h | 159 + sys/wince/winMS.h | 182 + sys/wince/winhack.c | 362 ++ sys/wince/winhack.rc | 370 ++ sys/wince/winhcksp.rc | 346 ++ sys/wince/winmain.c | 110 + sys/winnt/Install.nt | 450 +++ sys/winnt/Makefile.bcc | 1378 +++++++ sys/winnt/Makefile.gcc | 1352 +++++++ sys/winnt/Makefile.msc | 1419 ++++++++ sys/winnt/console.rc | 49 + sys/winnt/defaults.nh | 185 + sys/winnt/mapimail.c | 482 +++ sys/winnt/nethack.def | 9 + sys/winnt/nh340key.c | 297 ++ sys/winnt/nhdefkey.c | 329 ++ sys/winnt/nhico.uu | 27 + sys/winnt/nhraykey.c | 599 +++ sys/winnt/nhsetup.bat | 114 + sys/winnt/ntsound.c | 28 + sys/winnt/nttty.c | 961 +++++ sys/winnt/porthelp | 300 ++ sys/winnt/win32api.h | 27 + sys/winnt/winnt.c | 324 ++ util/dgn_comp.l | 137 + util/dgn_comp.y | 678 ++++ util/dgn_main.c | 189 + util/dlb_main.c | 543 +++ util/lev_comp.l | 240 ++ util/lev_comp.y | 1681 +++++++++ util/lev_main.c | 1580 ++++++++ util/makedefs.c | 2199 +++++++++++ util/panic.c | 61 + util/recover.c | 399 ++ win/Qt/Info.plist | 20 + win/Qt/Install.Qt | 92 + win/Qt/knethack.lnk | 18 + win/Qt/knh-mini.xpm | 30 + win/Qt/knh.xpm | 67 + win/Qt/nhicns.uu | 1112 ++++++ win/Qt/nhsplash.xpm | 374 ++ win/Qt/qpe-nethack.control | 9 + win/Qt/qt_clust.cpp | 168 + win/Qt/qt_win.cpp | 5264 +++++++++++++++++++++++++++ win/Qt/qttableview.cpp | 2275 ++++++++++++ win/Qt/tileedit.cpp | 413 +++ win/Qt/tileedit.h | 128 + win/X11/Install.X11 | 202 ++ win/X11/NetHack.ad | 190 + win/X11/Window.c | 170 + win/X11/dialogs.c | 340 ++ win/X11/ibm.bdf | 5404 +++++++++++++++++++++++++++ win/X11/nethack.rc | 79 + win/X11/nh10.bdf | 6940 +++++++++++++++++++++++++++++++++++ win/X11/nh32icon | 20 + win/X11/nh56icon | 42 + win/X11/nh72icon | 63 + win/X11/nh_icon.xpm | 49 + win/X11/pet_mark.xbm | 6 + win/X11/rip.xpm | 297 ++ win/X11/tile2x11.c | 235 ++ win/X11/winX.c | 2076 +++++++++++ win/X11/winmap.c | 1654 +++++++++ win/X11/winmenu.c | 1146 ++++++ win/X11/winmesg.c | 625 ++++ win/X11/winmisc.c | 928 +++++ win/X11/winstat.c | 991 +++++ win/X11/wintext.c | 626 ++++ win/X11/winval.c | 177 + win/gem/Install.gem | 38 + win/gem/bitmfile.c | 340 ++ win/gem/gem_rsc.uu | 230 ++ win/gem/gem_rso.uu | 22 + win/gem/gr_rect.c | 181 + win/gem/gr_rect.h | 14 + win/gem/load_img.c | 289 ++ win/gem/tile2img.c | 156 + win/gem/title.uu | 426 +++ win/gem/wingem.c | 1242 +++++++ win/gem/wingem1.c | 2995 +++++++++++++++ win/gem/xpm2img.c | 164 + win/gnome/README | 53 + win/gnome/gn_xpms.h | 1450 ++++++++ win/gnome/gnaskstr.c | 61 + win/gnome/gnaskstr.h | 14 + win/gnome/gnbind.c | 1197 ++++++ win/gnome/gnbind.h | 94 + win/gnome/gnglyph.c | 230 ++ win/gnome/gnglyph.h | 42 + win/gnome/gnmain.c | 839 +++++ win/gnome/gnmain.h | 26 + win/gnome/gnmap.c | 619 ++++ win/gnome/gnmap.h | 16 + win/gnome/gnmenu.c | 755 ++++ win/gnome/gnmenu.h | 32 + win/gnome/gnmesg.c | 102 + win/gnome/gnmesg.h | 23 + win/gnome/gnomeprv.h | 18 + win/gnome/gnopts.c | 117 + win/gnome/gnopts.h | 13 + win/gnome/gnplayer.c | 103 + win/gnome/gnplayer.h | 10 + win/gnome/gnsignal.c | 442 +++ win/gnome/gnsignal.h | 56 + win/gnome/gnstatus.c | 941 +++++ win/gnome/gnstatus.h | 14 + win/gnome/gntext.c | 158 + win/gnome/gntext.h | 22 + win/gnome/gnworn.c | 105 + win/gnome/gnworn.h | 14 + win/gnome/gnyesno.c | 76 + win/gnome/gnyesno.h | 12 + win/gnome/mapbg.xpm | 353 ++ win/share/gifread.c | 706 ++++ win/share/monsters.txt | 7502 ++++++++++++++++++++++++++++++++++++++ win/share/objects.txt | 8281 ++++++++++++++++++++++++++++++++++++++++++ win/share/other.txt | 4348 ++++++++++++++++++++++ win/share/ppmwrite.c | 171 + win/share/thintile.c | 129 + win/share/tile.doc | 140 + win/share/tile.h | 46 + win/share/tile2bmp.c | 340 ++ win/share/tilemap.c | 529 +++ win/share/tiletext.c | 332 ++ win/tty/getline.c | 284 ++ win/tty/termcap.c | 1185 ++++++ win/tty/topl.c | 467 +++ win/tty/wintty.c | 2612 +++++++++++++ win/win32/dgncomp.dsp | 297 ++ win/win32/dgnstuff.dsp | 97 + win/win32/dgnstuff.mak | 59 + win/win32/dlb_main.dsp | 179 + win/win32/levcomp.dsp | 198 + win/win32/levstuff.dsp | 97 + win/win32/levstuff.mak | 59 + win/win32/makedefs.dsp | 198 + win/win32/mhaskyn.c | 11 + win/win32/mhaskyn.h | 11 + win/win32/mhdlg.c | 758 ++++ win/win32/mhdlg.h | 15 + win/win32/mhfont.c | 205 ++ win/win32/mhfont.h | 16 + win/win32/mhinput.c | 85 + win/win32/mhinput.h | 35 + win/win32/mhmain.c | 966 +++++ win/win32/mhmain.h | 16 + win/win32/mhmap.c | 976 +++++ win/win32/mhmap.h | 21 + win/win32/mhmenu.c | 1448 ++++++++ win/win32/mhmenu.h | 18 + win/win32/mhmsg.h | 62 + win/win32/mhmsgwnd.c | 742 ++++ win/win32/mhmsgwnd.h | 15 + win/win32/mhrip.c | 265 ++ win/win32/mhrip.h | 15 + win/win32/mhsplash.c | 248 ++ win/win32/mhsplash.h | 13 + win/win32/mhstatus.c | 176 + win/win32/mhstatus.h | 14 + win/win32/mhtext.c | 254 ++ win/win32/mhtext.h | 14 + win/win32/mnsel.uu | 6 + win/win32/mnselcnt.uu | 6 + win/win32/mnunsel.uu | 6 + win/win32/mswproc.c | 2353 ++++++++++++ win/win32/nethack.dsw | 212 ++ win/win32/nethackw.dsp | 1107 ++++++ win/win32/petmark.uu | 9 + win/win32/recover.dsp | 148 + win/win32/resource.h | 151 + win/win32/rip.uu | 1805 +++++++++ win/win32/splash.uu | 2374 ++++++++++++ win/win32/tile2bmp.dsp | 146 + win/win32/tilemap.dsp | 281 ++ win/win32/tiles.dsp | 97 + win/win32/tiles.mak | 21 + win/win32/uudecode.dsp | 146 + win/win32/winMS.h | 202 ++ win/win32/winhack.c | 273 ++ win/win32/winhack.rc | 380 ++ 678 files changed, 396151 insertions(+) create mode 100644 Files create mode 100644 Porting create mode 100644 README create mode 100644 binary/license create mode 100644 build/dgncomp.dsp create mode 100644 build/dgnstuff.dsp create mode 100644 build/dgnstuff.mak create mode 100644 build/dlb_main.dsp create mode 100644 build/levcomp.dsp create mode 100644 build/levstuff.dsp create mode 100644 build/levstuff.mak create mode 100644 build/makedefs.dsp create mode 100644 build/nethackw.dsp create mode 100644 build/recover.dsp create mode 100644 build/tile2bmp.dsp create mode 100644 build/tilemap.dsp create mode 100644 build/tiles.dsp create mode 100644 build/tiles.mak create mode 100644 build/uudecode.dsp create mode 100644 dat/Arch.des create mode 100644 dat/Barb.des create mode 100644 dat/Caveman.des create mode 100644 dat/Healer.des create mode 100644 dat/Knight.des create mode 100644 dat/Monk.des create mode 100644 dat/Priest.des create mode 100644 dat/Ranger.des create mode 100644 dat/Rogue.des create mode 100644 dat/Samurai.des create mode 100644 dat/Tourist.des create mode 100644 dat/Valkyrie.des create mode 100644 dat/Wizard.des create mode 100644 dat/bigroom.des create mode 100644 dat/castle.des create mode 100644 dat/cmdhelp create mode 100644 dat/data.base create mode 100644 dat/dungeon.def create mode 100644 dat/endgame.des create mode 100644 dat/gehennom.des create mode 100644 dat/help create mode 100644 dat/hh create mode 100644 dat/history create mode 100644 dat/knox.des create mode 100644 dat/license create mode 100644 dat/medusa.des create mode 100644 dat/mines.des create mode 100644 dat/opthelp create mode 100644 dat/oracle.des create mode 100644 dat/oracles.txt create mode 100644 dat/quest.txt create mode 100644 dat/rumors.fal create mode 100644 dat/rumors.tru create mode 100644 dat/sokoban.des create mode 100644 dat/tower.des create mode 100644 dat/wizhelp create mode 100644 dat/yendor.des create mode 100644 doc/Guidebook.mn create mode 100644 doc/Guidebook.tex create mode 100644 doc/Guidebook.txt create mode 100644 doc/dgn_comp.6 create mode 100644 doc/dgn_comp.txt create mode 100644 doc/dlb.6 create mode 100644 doc/dlb.txt create mode 100644 doc/fixes22.0 create mode 100644 doc/fixes30.0 create mode 100644 doc/fixes31.1 create mode 100644 doc/fixes31.2 create mode 100644 doc/fixes31.3 create mode 100644 doc/fixes32.0 create mode 100644 doc/fixes32.1 create mode 100644 doc/fixes32.2 create mode 100644 doc/fixes32.3 create mode 100644 doc/fixes33.0 create mode 100644 doc/fixes33.1 create mode 100644 doc/fixes34.0 create mode 100644 doc/fixes34.1 create mode 100644 doc/fixes34.2 create mode 100644 doc/fixes34.3 create mode 100644 doc/lev_comp.6 create mode 100644 doc/lev_comp.txt create mode 100644 doc/nethack.6 create mode 100644 doc/nethack.txt create mode 100644 doc/recover.6 create mode 100644 doc/recover.txt create mode 100644 doc/tmac.n create mode 100644 doc/window.doc create mode 100644 include/align.h create mode 100644 include/amiconf.h create mode 100644 include/artifact.h create mode 100644 include/artilist.h create mode 100644 include/attrib.h create mode 100644 include/beconf.h create mode 100644 include/bitmfile.h create mode 100644 include/color.h create mode 100644 include/config.h create mode 100644 include/config1.h create mode 100644 include/coord.h create mode 100644 include/decl.h create mode 100644 include/def_os2.h create mode 100644 include/dgn_file.h create mode 100644 include/display.h create mode 100644 include/dlb.h create mode 100644 include/dungeon.h create mode 100644 include/edog.h create mode 100644 include/emin.h create mode 100644 include/engrave.h create mode 100644 include/epri.h create mode 100644 include/eshk.h create mode 100644 include/extern.h create mode 100644 include/flag.h create mode 100644 include/func_tab.h create mode 100644 include/gem_rsc.h create mode 100644 include/global.h create mode 100644 include/hack.h create mode 100644 include/lev.h create mode 100644 include/load_img.h create mode 100644 include/mac-carbon.h create mode 100644 include/mac-qt.h create mode 100644 include/mac-term.h create mode 100644 include/macconf.h create mode 100644 include/macpopup.h create mode 100644 include/mactty.h create mode 100644 include/macwin.h create mode 100644 include/mail.h create mode 100644 include/mfndpos.h create mode 100644 include/micro.h create mode 100644 include/mkroom.h create mode 100644 include/monattk.h create mode 100644 include/mondata.h create mode 100644 include/monflag.h create mode 100644 include/monst.h create mode 100644 include/monsym.h create mode 100644 include/mttypriv.h create mode 100644 include/nhlan.h create mode 100644 include/ntconf.h create mode 100644 include/obj.h create mode 100644 include/objclass.h create mode 100644 include/os2conf.h create mode 100644 include/patchlevel.h create mode 100644 include/pcconf.h create mode 100644 include/permonst.h create mode 100644 include/prop.h create mode 100644 include/qt_clust.h create mode 100644 include/qt_kde0.h create mode 100644 include/qt_win.h create mode 100644 include/qt_xpms.h create mode 100644 include/qtext.h create mode 100644 include/qttableview.h create mode 100644 include/quest.h create mode 100644 include/rect.h create mode 100644 include/region.h create mode 100644 include/rm.h create mode 100644 include/skills.h create mode 100644 include/sp_lev.h create mode 100644 include/spell.h create mode 100644 include/system.h create mode 100644 include/tcap.h create mode 100644 include/tile2x11.h create mode 100644 include/timeout.h create mode 100644 include/tosconf.h create mode 100644 include/tradstdc.h create mode 100644 include/trampoli.h create mode 100644 include/trap.h create mode 100644 include/unixconf.h create mode 100644 include/vault.h create mode 100644 include/vision.h create mode 100644 include/vmsconf.h create mode 100644 include/wceconf.h create mode 100644 include/winGnome.h create mode 100644 include/winX.h create mode 100644 include/winami.h create mode 100644 include/wingem.h create mode 100644 include/winprocs.h create mode 100644 include/wintty.h create mode 100644 include/wintype.h create mode 100644 include/xwindow.h create mode 100644 include/xwindowp.h create mode 100644 include/you.h create mode 100644 include/youprop.h create mode 100644 nethack.dsw create mode 100644 src/Makefile create mode 100644 src/Makefile.bcc create mode 100644 src/Makefile.gcc create mode 100644 src/allmain.c create mode 100644 src/alloc.c create mode 100644 src/apply.c create mode 100644 src/artifact.c create mode 100644 src/attrib.c create mode 100644 src/ball.c create mode 100644 src/bones.c create mode 100644 src/botl.c create mode 100644 src/cmd.c create mode 100644 src/dbridge.c create mode 100644 src/decl.c create mode 100644 src/detect.c create mode 100644 src/dig.c create mode 100644 src/display.c create mode 100644 src/dlb.c create mode 100644 src/do.c create mode 100644 src/do_name.c create mode 100644 src/do_wear.c create mode 100644 src/dog.c create mode 100644 src/dogmove.c create mode 100644 src/dokick.c create mode 100644 src/dothrow.c create mode 100644 src/drawing.c create mode 100644 src/dungeon.c create mode 100644 src/eat.c create mode 100644 src/end.c create mode 100644 src/engrave.c create mode 100644 src/exper.c create mode 100644 src/explode.c create mode 100644 src/extralev.c create mode 100644 src/files.c create mode 100644 src/fountain.c create mode 100644 src/hack.c create mode 100644 src/hacklib.c create mode 100644 src/invent.c create mode 100644 src/light.c create mode 100644 src/lock.c create mode 100644 src/mail.c create mode 100644 src/makemon.c create mode 100644 src/mapglyph.c create mode 100644 src/mcastu.c create mode 100644 src/mhitm.c create mode 100644 src/mhitu.c create mode 100644 src/minion.c create mode 100644 src/mklev.c create mode 100644 src/mkmap.c create mode 100644 src/mkmaze.c create mode 100644 src/mkobj.c create mode 100644 src/mkroom.c create mode 100644 src/mon.c create mode 100644 src/mondata.c create mode 100644 src/monmove.c create mode 100644 src/monst.c create mode 100644 src/mplayer.c create mode 100644 src/mthrowu.c create mode 100644 src/muse.c create mode 100644 src/music.c create mode 100644 src/o_init.c create mode 100644 src/objects.c create mode 100644 src/objnam.c create mode 100644 src/options.c create mode 100644 src/pager.c create mode 100644 src/pickup.c create mode 100644 src/pline.c create mode 100644 src/polyself.c create mode 100644 src/potion.c create mode 100644 src/pray.c create mode 100644 src/priest.c create mode 100644 src/quest.c create mode 100644 src/questpgr.c create mode 100644 src/read.c create mode 100644 src/rect.c create mode 100644 src/region.c create mode 100644 src/restore.c create mode 100644 src/rip.c create mode 100644 src/rnd.c create mode 100644 src/role.c create mode 100644 src/rumors.c create mode 100644 src/save.c create mode 100644 src/shk.c create mode 100644 src/shknam.c create mode 100644 src/sit.c create mode 100644 src/sounds.c create mode 100644 src/sp_lev.c create mode 100644 src/spell.c create mode 100644 src/steal.c create mode 100644 src/steed.c create mode 100644 src/teleport.c create mode 100644 src/timeout.c create mode 100644 src/topten.c create mode 100644 src/track.c create mode 100644 src/trap.c create mode 100644 src/u_init.c create mode 100644 src/uhitm.c create mode 100644 src/vault.c create mode 100644 src/version.c create mode 100644 src/vision.c create mode 100644 src/weapon.c create mode 100644 src/were.c create mode 100644 src/wield.c create mode 100644 src/windows.c create mode 100644 src/wizard.c create mode 100644 src/worm.c create mode 100644 src/worn.c create mode 100644 src/write.c create mode 100644 src/zap.c create mode 100644 sys/amiga/Build.ami create mode 100644 sys/amiga/Install.ami create mode 100644 sys/amiga/Makefile.agc create mode 100644 sys/amiga/Makefile.ami create mode 100644 sys/amiga/NetHack.cnf create mode 100644 sys/amiga/amidos.c create mode 100644 sys/amiga/amidos.p create mode 100644 sys/amiga/amifont.uu create mode 100644 sys/amiga/amifont8.uu create mode 100644 sys/amiga/amigst.c create mode 100644 sys/amiga/amii.hlp create mode 100644 sys/amiga/amimenu.c create mode 100644 sys/amiga/amirip.c create mode 100644 sys/amiga/amisnd.c create mode 100644 sys/amiga/amistack.c create mode 100644 sys/amiga/amitty.c create mode 100644 sys/amiga/amiwind.c create mode 100644 sys/amiga/amiwind.p create mode 100644 sys/amiga/clipwin.c create mode 100644 sys/amiga/colorwin.c create mode 100644 sys/amiga/cvtsnd.c create mode 100644 sys/amiga/grave16.xpm create mode 100644 sys/amiga/ifchange create mode 100644 sys/amiga/mkdmake create mode 100644 sys/amiga/txt2iff.c create mode 100644 sys/amiga/winami.c create mode 100644 sys/amiga/winami.p create mode 100644 sys/amiga/winchar.c create mode 100644 sys/amiga/windefs.h create mode 100644 sys/amiga/winext.h create mode 100644 sys/amiga/winfuncs.c create mode 100644 sys/amiga/winkey.c create mode 100644 sys/amiga/winmenu.c create mode 100644 sys/amiga/winproto.h create mode 100644 sys/amiga/winreq.c create mode 100644 sys/amiga/winstr.c create mode 100644 sys/amiga/xpm2iff.c create mode 100644 sys/atari/Install.tos create mode 100644 sys/atari/atarifnt.uue create mode 100644 sys/atari/nethack.mnu create mode 100644 sys/atari/setup.g create mode 100644 sys/atari/tos.c create mode 100644 sys/atari/unx2atar.sed create mode 100644 sys/be/README create mode 100644 sys/be/bemain.c create mode 100644 sys/mac/Files.r create mode 100644 sys/mac/Install.mw create mode 100644 sys/mac/MacHelp create mode 100644 sys/mac/NHDeflts create mode 100644 sys/mac/NHrsrc.hqx create mode 100644 sys/mac/NHsound.hqx create mode 100644 sys/mac/News create mode 100644 sys/mac/README create mode 100644 sys/mac/dprintf.c create mode 100644 sys/mac/maccurs.c create mode 100644 sys/mac/macerrs.c create mode 100644 sys/mac/macfile.c create mode 100644 sys/mac/machelp.hqx create mode 100644 sys/mac/macmain.c create mode 100644 sys/mac/macmenu.c create mode 100644 sys/mac/macsnd.c create mode 100644 sys/mac/mactopl.c create mode 100644 sys/mac/mactty.c create mode 100644 sys/mac/macunix.c create mode 100644 sys/mac/macwin.c create mode 100644 sys/mac/mgetline.c create mode 100644 sys/mac/mmodal.c create mode 100644 sys/mac/mrecover.c create mode 100644 sys/mac/mrecover.hqx create mode 100644 sys/mac/mttymain.c create mode 100644 sys/msdos/Install.dos create mode 100644 sys/msdos/Makefile.BC create mode 100644 sys/msdos/Makefile.GCC create mode 100644 sys/msdos/Makefile.MSC create mode 100644 sys/msdos/NHAccess.nh create mode 100644 sys/msdos/moveinit.pat create mode 100644 sys/msdos/msdos.c create mode 100644 sys/msdos/msdoshlp.txt create mode 100644 sys/msdos/nhico.uu create mode 100644 sys/msdos/nhpif.uu create mode 100644 sys/msdos/ovlinit.c create mode 100644 sys/msdos/pckeys.c create mode 100644 sys/msdos/pctiles.c create mode 100644 sys/msdos/pctiles.h create mode 100644 sys/msdos/pcvideo.h create mode 100644 sys/msdos/portio.h create mode 100644 sys/msdos/schema1.BC create mode 100644 sys/msdos/schema2.BC create mode 100644 sys/msdos/schema3.MSC create mode 100644 sys/msdos/setup.bat create mode 100644 sys/msdos/sound.c create mode 100644 sys/msdos/tile2bin.c create mode 100644 sys/msdos/video.c create mode 100644 sys/msdos/vidtxt.c create mode 100644 sys/msdos/vidvga.c create mode 100644 sys/os2/Install.os2 create mode 100644 sys/os2/Makefile.os2 create mode 100644 sys/os2/nhpmico.uu create mode 100644 sys/os2/os2.c create mode 100644 sys/share/Makefile.lib create mode 100644 sys/share/NetHack.cnf create mode 100644 sys/share/dgn_comp.h create mode 100644 sys/share/dgn_lex.c create mode 100644 sys/share/dgn_yacc.c create mode 100644 sys/share/ioctl.c create mode 100644 sys/share/lev_comp.h create mode 100644 sys/share/lev_lex.c create mode 100644 sys/share/lev_yacc.c create mode 100644 sys/share/nhlan.c create mode 100644 sys/share/pcmain.c create mode 100644 sys/share/pcsys.c create mode 100644 sys/share/pctty.c create mode 100644 sys/share/pcunix.c create mode 100644 sys/share/random.c create mode 100644 sys/share/sounds/README create mode 100644 sys/share/sounds/bell.uu create mode 100644 sys/share/sounds/bugle.uu create mode 100644 sys/share/sounds/erthdrum.uu create mode 100644 sys/share/sounds/firehorn.uu create mode 100644 sys/share/sounds/frsthorn.uu create mode 100644 sys/share/sounds/lethdrum.uu create mode 100644 sys/share/sounds/mgcflute.uu create mode 100644 sys/share/sounds/mgcharp.uu create mode 100644 sys/share/sounds/toolhorn.uu create mode 100644 sys/share/sounds/wdnflute.uu create mode 100644 sys/share/sounds/wdnharp.uu create mode 100644 sys/share/tclib.c create mode 100644 sys/share/termcap create mode 100644 sys/share/termcap.uu create mode 100644 sys/share/unixtty.c create mode 100644 sys/share/uudecode.c create mode 100644 sys/unix/Install.unx create mode 100644 sys/unix/Makefile.dat create mode 100644 sys/unix/Makefile.doc create mode 100644 sys/unix/Makefile.src create mode 100644 sys/unix/Makefile.top create mode 100644 sys/unix/Makefile.utl create mode 100644 sys/unix/README.linux create mode 100644 sys/unix/cpp1.shr create mode 100644 sys/unix/cpp2.shr create mode 100644 sys/unix/cpp3.shr create mode 100644 sys/unix/depend.awk create mode 100644 sys/unix/nethack.sh create mode 100644 sys/unix/setup.sh create mode 100644 sys/unix/snd86unx.shr create mode 100644 sys/unix/unixmain.c create mode 100644 sys/unix/unixres.c create mode 100644 sys/unix/unixunix.c create mode 100644 sys/vms/Install.vms create mode 100644 sys/vms/Makefile.dat create mode 100644 sys/vms/Makefile.doc create mode 100644 sys/vms/Makefile.src create mode 100644 sys/vms/Makefile.top create mode 100644 sys/vms/Makefile.utl create mode 100644 sys/vms/install.com create mode 100644 sys/vms/lev_lex.h create mode 100644 sys/vms/nethack.com create mode 100644 sys/vms/oldcrtl.c create mode 100644 sys/vms/spec_lev.com create mode 100644 sys/vms/vmsbuild.com create mode 100644 sys/vms/vmsfiles.c create mode 100644 sys/vms/vmsmail.c create mode 100644 sys/vms/vmsmain.c create mode 100644 sys/vms/vmsmisc.c create mode 100644 sys/vms/vmstty.c create mode 100644 sys/vms/vmsunix.c create mode 100644 sys/wince/Install.ce create mode 100644 sys/wince/bootstrp.mak create mode 100644 sys/wince/ceinc/assert.h create mode 100644 sys/wince/ceinc/errno.h create mode 100644 sys/wince/ceinc/fcntl.h create mode 100644 sys/wince/ceinc/sys/stat.h create mode 100644 sys/wince/celib.c create mode 100644 sys/wince/cesetup.bat create mode 100644 sys/wince/cesound.c create mode 100644 sys/wince/defaults.nh create mode 100644 sys/wince/keypad.uu create mode 100644 sys/wince/menubar.uu create mode 100644 sys/wince/mhaskyn.c create mode 100644 sys/wince/mhaskyn.h create mode 100644 sys/wince/mhcmd.c create mode 100644 sys/wince/mhcmd.h create mode 100644 sys/wince/mhcolor.c create mode 100644 sys/wince/mhcolor.h create mode 100644 sys/wince/mhdlg.c create mode 100644 sys/wince/mhdlg.h create mode 100644 sys/wince/mhfont.c create mode 100644 sys/wince/mhfont.h create mode 100644 sys/wince/mhinput.c create mode 100644 sys/wince/mhinput.h create mode 100644 sys/wince/mhmain.c create mode 100644 sys/wince/mhmain.h create mode 100644 sys/wince/mhmap.c create mode 100644 sys/wince/mhmap.h create mode 100644 sys/wince/mhmenu.c create mode 100644 sys/wince/mhmenu.h create mode 100644 sys/wince/mhmsg.h create mode 100644 sys/wince/mhmsgwnd.c create mode 100644 sys/wince/mhmsgwnd.h create mode 100644 sys/wince/mhrip.c create mode 100644 sys/wince/mhrip.h create mode 100644 sys/wince/mhstatus.c create mode 100644 sys/wince/mhstatus.h create mode 100644 sys/wince/mhtext.c create mode 100644 sys/wince/mhtext.h create mode 100644 sys/wince/mhtxtbuf.c create mode 100644 sys/wince/mhtxtbuf.h create mode 100644 sys/wince/mswproc.c create mode 100644 sys/wince/newres.h create mode 100644 sys/wince/resource.h create mode 100644 sys/wince/winMS.h create mode 100644 sys/wince/winhack.c create mode 100644 sys/wince/winhack.rc create mode 100644 sys/wince/winhcksp.rc create mode 100644 sys/wince/winmain.c create mode 100644 sys/winnt/Install.nt create mode 100644 sys/winnt/Makefile.bcc create mode 100644 sys/winnt/Makefile.gcc create mode 100644 sys/winnt/Makefile.msc create mode 100644 sys/winnt/console.rc create mode 100644 sys/winnt/defaults.nh create mode 100644 sys/winnt/mapimail.c create mode 100644 sys/winnt/nethack.def create mode 100644 sys/winnt/nh340key.c create mode 100644 sys/winnt/nhdefkey.c create mode 100644 sys/winnt/nhico.uu create mode 100644 sys/winnt/nhraykey.c create mode 100644 sys/winnt/nhsetup.bat create mode 100644 sys/winnt/ntsound.c create mode 100644 sys/winnt/nttty.c create mode 100644 sys/winnt/porthelp create mode 100644 sys/winnt/win32api.h create mode 100644 sys/winnt/winnt.c create mode 100644 util/dgn_comp.l create mode 100644 util/dgn_comp.y create mode 100644 util/dgn_main.c create mode 100644 util/dlb_main.c create mode 100644 util/lev_comp.l create mode 100644 util/lev_comp.y create mode 100644 util/lev_main.c create mode 100644 util/makedefs.c create mode 100644 util/panic.c create mode 100644 util/recover.c create mode 100644 win/Qt/Info.plist create mode 100644 win/Qt/Install.Qt create mode 100644 win/Qt/knethack.lnk create mode 100644 win/Qt/knh-mini.xpm create mode 100644 win/Qt/knh.xpm create mode 100644 win/Qt/nhicns.uu create mode 100644 win/Qt/nhsplash.xpm create mode 100644 win/Qt/qpe-nethack.control create mode 100644 win/Qt/qt_clust.cpp create mode 100644 win/Qt/qt_win.cpp create mode 100644 win/Qt/qttableview.cpp create mode 100644 win/Qt/tileedit.cpp create mode 100644 win/Qt/tileedit.h create mode 100644 win/X11/Install.X11 create mode 100644 win/X11/NetHack.ad create mode 100644 win/X11/Window.c create mode 100644 win/X11/dialogs.c create mode 100644 win/X11/ibm.bdf create mode 100644 win/X11/nethack.rc create mode 100644 win/X11/nh10.bdf create mode 100644 win/X11/nh32icon create mode 100644 win/X11/nh56icon create mode 100644 win/X11/nh72icon create mode 100644 win/X11/nh_icon.xpm create mode 100644 win/X11/pet_mark.xbm create mode 100644 win/X11/rip.xpm create mode 100644 win/X11/tile2x11.c create mode 100644 win/X11/winX.c create mode 100644 win/X11/winmap.c create mode 100644 win/X11/winmenu.c create mode 100644 win/X11/winmesg.c create mode 100644 win/X11/winmisc.c create mode 100644 win/X11/winstat.c create mode 100644 win/X11/wintext.c create mode 100644 win/X11/winval.c create mode 100644 win/gem/Install.gem create mode 100644 win/gem/bitmfile.c create mode 100644 win/gem/gem_rsc.uu create mode 100644 win/gem/gem_rso.uu create mode 100644 win/gem/gr_rect.c create mode 100644 win/gem/gr_rect.h create mode 100644 win/gem/load_img.c create mode 100644 win/gem/tile2img.c create mode 100644 win/gem/title.uu create mode 100644 win/gem/wingem.c create mode 100644 win/gem/wingem1.c create mode 100644 win/gem/xpm2img.c create mode 100644 win/gnome/README create mode 100644 win/gnome/gn_xpms.h create mode 100644 win/gnome/gnaskstr.c create mode 100644 win/gnome/gnaskstr.h create mode 100644 win/gnome/gnbind.c create mode 100644 win/gnome/gnbind.h create mode 100644 win/gnome/gnglyph.c create mode 100644 win/gnome/gnglyph.h create mode 100644 win/gnome/gnmain.c create mode 100644 win/gnome/gnmain.h create mode 100644 win/gnome/gnmap.c create mode 100644 win/gnome/gnmap.h create mode 100644 win/gnome/gnmenu.c create mode 100644 win/gnome/gnmenu.h create mode 100644 win/gnome/gnmesg.c create mode 100644 win/gnome/gnmesg.h create mode 100644 win/gnome/gnomeprv.h create mode 100644 win/gnome/gnopts.c create mode 100644 win/gnome/gnopts.h create mode 100644 win/gnome/gnplayer.c create mode 100644 win/gnome/gnplayer.h create mode 100644 win/gnome/gnsignal.c create mode 100644 win/gnome/gnsignal.h create mode 100644 win/gnome/gnstatus.c create mode 100644 win/gnome/gnstatus.h create mode 100644 win/gnome/gntext.c create mode 100644 win/gnome/gntext.h create mode 100644 win/gnome/gnworn.c create mode 100644 win/gnome/gnworn.h create mode 100644 win/gnome/gnyesno.c create mode 100644 win/gnome/gnyesno.h create mode 100644 win/gnome/mapbg.xpm create mode 100644 win/share/gifread.c create mode 100644 win/share/monsters.txt create mode 100644 win/share/objects.txt create mode 100644 win/share/other.txt create mode 100644 win/share/ppmwrite.c create mode 100644 win/share/thintile.c create mode 100644 win/share/tile.doc create mode 100644 win/share/tile.h create mode 100644 win/share/tile2bmp.c create mode 100644 win/share/tilemap.c create mode 100644 win/share/tiletext.c create mode 100644 win/tty/getline.c create mode 100644 win/tty/termcap.c create mode 100644 win/tty/topl.c create mode 100644 win/tty/wintty.c create mode 100644 win/win32/dgncomp.dsp create mode 100644 win/win32/dgnstuff.dsp create mode 100644 win/win32/dgnstuff.mak create mode 100644 win/win32/dlb_main.dsp create mode 100644 win/win32/levcomp.dsp create mode 100644 win/win32/levstuff.dsp create mode 100644 win/win32/levstuff.mak create mode 100644 win/win32/makedefs.dsp create mode 100644 win/win32/mhaskyn.c create mode 100644 win/win32/mhaskyn.h create mode 100644 win/win32/mhdlg.c create mode 100644 win/win32/mhdlg.h create mode 100644 win/win32/mhfont.c create mode 100644 win/win32/mhfont.h create mode 100644 win/win32/mhinput.c create mode 100644 win/win32/mhinput.h create mode 100644 win/win32/mhmain.c create mode 100644 win/win32/mhmain.h create mode 100644 win/win32/mhmap.c create mode 100644 win/win32/mhmap.h create mode 100644 win/win32/mhmenu.c create mode 100644 win/win32/mhmenu.h create mode 100644 win/win32/mhmsg.h create mode 100644 win/win32/mhmsgwnd.c create mode 100644 win/win32/mhmsgwnd.h create mode 100644 win/win32/mhrip.c create mode 100644 win/win32/mhrip.h create mode 100644 win/win32/mhsplash.c create mode 100644 win/win32/mhsplash.h create mode 100644 win/win32/mhstatus.c create mode 100644 win/win32/mhstatus.h create mode 100644 win/win32/mhtext.c create mode 100644 win/win32/mhtext.h create mode 100644 win/win32/mnsel.uu create mode 100644 win/win32/mnselcnt.uu create mode 100644 win/win32/mnunsel.uu create mode 100644 win/win32/mswproc.c create mode 100644 win/win32/nethack.dsw create mode 100644 win/win32/nethackw.dsp create mode 100644 win/win32/petmark.uu create mode 100644 win/win32/recover.dsp create mode 100644 win/win32/resource.h create mode 100644 win/win32/rip.uu create mode 100644 win/win32/splash.uu create mode 100644 win/win32/tile2bmp.dsp create mode 100644 win/win32/tilemap.dsp create mode 100644 win/win32/tiles.dsp create mode 100644 win/win32/tiles.mak create mode 100644 win/win32/uudecode.dsp create mode 100644 win/win32/winMS.h create mode 100644 win/win32/winhack.c create mode 100644 win/win32/winhack.rc diff --git a/Files b/Files new file mode 100644 index 0000000..1c1fdd9 --- /dev/null +++ b/Files @@ -0,0 +1,319 @@ +This is a listing of all files in a full NetHack 3.4 distribution, organized +in their standard manner on a UNIX system. It indicates which files are +necessary for which versions, so that you can tell which files may be deleted +from or not transferred to your system if you wish. + + +.: +(files in top directory) +Files Porting README + +dat: +(files for all versions) +Arch.des Barb.des Caveman.des Healer.des Knight.des +Monk.des Priest.des Ranger.des Rogue.des Samurai.des +Tourist.des Valkyrie.des Wizard.des bigroom.des castle.des +cmdhelp data.base dungeon.def endgame.des gehennom.des +help hh history knox.des license +medusa.des mines.des opthelp oracle.des oracles.txt +quest.txt rumors.fal rumors.tru sokoban.des tower.des +wizhelp yendor.des + +doc: +(files for all versions) +Guidebook.mn Guidebook.tex Guidebook.txt dgn_comp.6 dgn_comp.txt +dlb.6 dlb.txt fixes22.0 fixes30.0 fixes31.1 +fixes31.2 fixes31.3 fixes32.0 fixes32.1 fixes32.2 +fixes32.3 fixes33.0 fixes33.1 fixes34.0 fixes34.1 +fixes34.2 fixes34.3 lev_comp.6 lev_comp.txt nethack.6 +nethack.txt recover.6 recover.txt tmac.n window.doc + +include: +(files for all versions) +align.h amiconf.h artifact.h artilist.h attrib.h +beconf.h color.h config.h config1.h coord.h +decl.h def_os2.h dgn_file.h display.h dlb.h +dungeon.h edog.h emin.h engrave.h epri.h +eshk.h extern.h flag.h func_tab.h global.h +hack.h lev.h mail.h mfndpos.h micro.h +mkroom.h monattk.h mondata.h monflag.h monst.h +monsym.h nhlan.h ntconf.h obj.h objclass.h +os2conf.h patchlevel.h pcconf.h permonst.h prop.h +qtext.h quest.h rect.h region.h rm.h +skills.h sp_lev.h spell.h system.h tcap.h +timeout.h tosconf.h tradstdc.h trampoli.h trap.h +unixconf.h vault.h vision.h vmsconf.h wceconf.h +winami.h winprocs.h wintype.h you.h youprop.h +(file for tty versions) +wintty.h +(files for X versions) +tile2x11.h winX.h xwindow.h xwindowp.h +(files for Qt versions) +qt_clust.h qt_kde0.h qt_win.h qt_xpms.h qttableview.h +(files for Gem versions) +bitmfile.h gem_rsc.h load_img.h wingem.h +(file for GNOME versions) +winGnome.h +(files for various Macintosh versions) +mac-carbon.h mac-qt.h mac-term.h macconf.h macpopup.h +mactty.h macwin.h mttypriv.h + +src: +(files for all versions) +allmain.c alloc.c apply.c artifact.c attrib.c +ball.c bones.c botl.c cmd.c dbridge.c +decl.c detect.c dig.c display.c dlb.c +do.c do_name.c do_wear.c dog.c dogmove.c +dokick.c dothrow.c drawing.c dungeon.c eat.c +end.c engrave.c exper.c explode.c extralev.c +files.c fountain.c hack.c hacklib.c invent.c +light.c lock.c mail.c makemon.c mapglyph.c +mcastu.c mhitm.c mhitu.c minion.c mklev.c +mkmap.c mkmaze.c mkobj.c mkroom.c mon.c +mondata.c monmove.c monst.c mplayer.c mthrowu.c +muse.c music.c o_init.c objects.c objnam.c +options.c pager.c pickup.c pline.c polyself.c +potion.c pray.c priest.c quest.c questpgr.c +read.c rect.c region.c restore.c rip.c +rnd.c role.c rumors.c save.c shk.c +shknam.c sit.c sounds.c sp_lev.c spell.c +steal.c steed.c teleport.c timeout.c topten.c +track.c trap.c u_init.c uhitm.c vault.c +version.c vision.c weapon.c were.c wield.c +windows.c wizard.c worm.c worn.c write.c +zap.c + +sys/amiga: +(files for Amiga versions) +Build.ami Install.ami Makefile.agc Makefile.ami NetHack.cnf +amidos.c amidos.p amifont.uu amifont8.uu amigst.c +amii.hlp amimenu.c amirip.c amisnd.c amistack.c +amitty.c amiwind.c amiwind.p clipwin.c colorwin.c +cvtsnd.c grave16.xpm ifchange mkdmake txt2iff.c +winami.c winami.p winchar.c windefs.h winext.h +winfuncs.c winkey.c winmenu.c winproto.h winreq.c +winstr.c xpm2iff.c + +sys/atari: +(files for Atari version) +Install.tos atarifnt.uue nethack.mnu setup.g tos.c +unx2atar.sed + +sys/be: +(files for BeOS version) +README bemain.c + +sys/mac: +(files for Macintosh versions) +Files.r Install.mw MacHelp NHDeflts NHrsrc.hqx +NHsound.hqx News README dprintf.c maccurs.c +macerrs.c macfile.c machelp.hqx macmain.c macmenu.c +macsnd.c mactopl.c mactty.c macunix.c macwin.c +mgetline.c mmodal.c mrecover.c mrecover.hqx mttymain.c + +sys/msdos: +(files for MSDOS version) +Install.dos Makefile.BC Makefile.GCC Makefile.MSC NHAccess.nh +moveinit.pat msdos.c msdoshlp.txt ovlinit.c pckeys.c +pctiles.c pctiles.h pcvideo.h portio.h schema1.BC +schema2.BC schema3.MSC setup.bat sound.c tile2bin.c +video.c vidtxt.c vidvga.c +(files for running MSDOS binary under Windows) +nhico.uu nhpif.uu + +sys/os2: +(files for OS/2 version) +Install.os2 Makefile.os2 nhpmico.uu os2.c + +sys/share: +(files for MSDOS and OS/2 versions) +Makefile.lib termcap.uu +(file for MSDOS, OS/2, NT, Amiga, and Atari versions) +pcmain.c +(files for MSDOS, OS/2, NT, and Atari versions) +pcsys.c pcunix.c +(file for MSDOS, OS/2, and Atari versions) +NetHack.cnf pctty.c +(files for UNIX and Be versions) +ioctl.c unixtty.c +(file for NT version) +nhlan.c +(Berkeley random number file, which may be included in any version) +random.c +(Berkeley uudecode file, which may be used in build process of any version) +uudecode.c +(file for VMS version) +tclib.c +(file for MSDOS, OS/2, and VMS versions) +termcap +(lex/yacc output for special level and dungeon compilers) +dgn_comp.h dgn_lex.c dgn_yacc.c lev_comp.h lev_lex.c +lev_yacc.c + +sys/share/sounds: +(files for Amiga and Macintosh versions) +README bell.uu bugle.uu erthdrum.uu firehorn.uu +frsthorn.uu lethdrum.uu mgcflute.uu mgcharp.uu toolhorn.uu +wdnflute.uu wdnharp.uu + +sys/unix: +(files for UNIX versions) +Install.unx Makefile.dat Makefile.doc Makefile.src Makefile.top +Makefile.utl README.linux depend.awk nethack.sh setup.sh +unixmain.c unixres.c unixunix.c +(files for replacement cpp, apparently only needed by some UNIX systems) +cpp1.shr cpp2.shr cpp3.shr +(file for sound driver for 386 UNIX) +snd86unx.shr + +sys/vms: +(files for VMS version) +Install.vms Makefile.dat Makefile.doc Makefile.src Makefile.top +Makefile.utl install.com lev_lex.h nethack.com oldcrtl.c +spec_lev.com vmsbuild.com vmsfiles.c vmsmail.c vmsmain.c +vmsmisc.c vmstty.c vmsunix.c + +sys/wince: +(files for Windows CE and PocketPC) +Install.ce bootstrp.mak celib.c cesetup.bat cesound.c +defaults.nh keypad.uu menubar.uu mhaskyn.c mhaskyn.h +mhcmd.c mhcmd.h mhcolor.c mhcolor.h mhdlg.c +mhdlg.h mhfont.c mhfont.h mhinput.c mhinput.h +mhmain.c mhmain.h mhmap.c mhmap.h mhmenu.c +mhmenu.h mhmsg.h mhmsgwnd.c mhmsgwnd.h mhrip.c +mhrip.h mhstatus.c mhstatus.h mhtext.c mhtext.h +mhtxtbuf.c mhtxtbuf.h mswproc.c newres.h resource.h +winMS.h winhack.c winhack.rc winhcksp.rc winmain.c + +sys/wince/ceinc: +(header files for Windows CE and PocketPC) +assert.h errno.h fcntl.h + +sys/wince/ceinc/sys: +(sys/stat.h for Windows CE and PocketPC) +stat.h + +sys/winnt: +(files for Windows 9x, NT and Windows2000 version) +Install.nt Makefile.bcc Makefile.gcc Makefile.msc console.rc +defaults.nh mapimail.c nethack.def nh340key.c nhdefkey.c +nhico.uu nhraykey.c nhsetup.bat ntsound.c nttty.c +porthelp win32api.h winnt.c + +util: +(files for all versions) +dgn_main.c dlb_main.c lev_main.c makedefs.c panic.c +recover.c +(lex/yacc input for special level and dungeon compilers) +dgn_comp.l dgn_comp.y lev_comp.l lev_comp.y + +win/Qt: +(files for the Qt widget library - X11, Windows, Mac OS X, or Qtopia) +Info.plist Install.Qt knethack.lnk knh-mini.xpm knh.xpm +nhicns.uu nhsplash.xpm qt_clust.cpp qt_win.cpp qttableview.cpp +tileedit.cpp tileedit.h qpe-nethack.control + +win/X11: +(files for X versions) +Install.X11 NetHack.ad Window.c dialogs.c ibm.bdf +nethack.rc nh10.bdf nh32icon nh56icon nh72icon +nh_icon.xpm pet_mark.xbm rip.xpm tile2x11.c winX.c +winmap.c winmenu.c winmesg.c winmisc.c winstat.c +wintext.c winval.c + +win/gem: +(files for GEM versions) +Install.gem bitmfile.c gem_rsc.uu gem_rso.uu gr_rect.c +gr_rect.h load_img.c tile2img.c title.uu wingem.c +wingem1.c xpm2img.c + +win/gnome: +(files for GNOME versions) +README gn_xpms.h gnaskstr.c gnaskstr.h gnbind.c +gnbind.h gnglyph.c gnglyph.h gnmain.c gnmain.h +gnmap.c gnmap.h gnmenu.c gnmenu.h gnmesg.c +gnmesg.h gnomeprv.h gnopts.c gnopts.h gnplayer.c +gnplayer.h gnsignal.c gnsignal.h gnstatus.c gnstatus.h +gntext.c gntext.h gnworn.c gnworn.h gnyesno.c +gnyesno.h mapbg.xpm + +win/share: +(files for versions using optional tiles) +gifread.c monsters.txt objects.txt other.txt ppmwrite.c +thintile.c tile.doc tile.h tile2bmp.c tilemap.c +tiletext.c + +win/tty: +(files for tty versions) +getline.c termcap.c topl.c wintty.c + +win/win32: +(files for Windows 9x, NT, Windows 2000, and Windows XP version) +dgncomp.dsp dgnstuff.dsp dgnstuff.mak dlb_main.dsp levcomp.dsp +levstuff.dsp levstuff.mak makedefs.dsp mhaskyn.c mhaskyn.h +mhdlg.c mhdlg.h mhfont.c mhfont.h mhinput.c +mhinput.h mhmain.c mhmain.h mhmap.c mhmap.h +mhmenu.c mhmenu.h mhmsg.h mhmsgwnd.c mhmsgwnd.h +mhrip.c mhrip.h mhsplash.c mhsplash.h mhstatus.c +mhstatus.h mhtext.c mhtext.h mnsel.uu mnselcnt.uu +mnunsel.uu mswproc.c nethack.dsw nethackw.dsp petmark.uu +recover.dsp resource.h rip.uu splash.uu tile2bmp.dsp +tilemap.dsp tiles.dsp tiles.mak uudecode.dsp winMS.h +winhack.c winhack.rc + + + +This is a list of files produced by auxiliary programs. They can all be +regenerated from the files in the distribution. + +dat: +(files generated by makedefs at playground creation time) +data dungeon.pdf options oracles quest.dat +rumors +(file generated by dgn_comp at playground creation time) +dungeon +(files generated by lev_comp at playground creation time) +Arc-fila.lev Arc-filb.lev Arc-goal.lev Arc-loca.lev Arc-strt.lev +Bar-fila.lev Bar-filb.lev Bar-goal.lev Bar-loca.lev Bar-strt.lev +Cav-fila.lev Cav-filb.lev Cav-goal.lev Cav-loca.lev Cav-strt.lev +Hea-fila.lev Hea-filb.lev Hea-goal.lev Hea-loca.lev Hea-strt.lev +Kni-fila.lev Kni-filb.lev Kni-goal.lev Kni-loca.lev Kni-strt.lev +Mon-fila.lev Mon-filb.lev Mon-goal.lev Mon-loca.lev Mon-strt.lev +Pri-fila.lev Pri-filb.lev Pri-goal.lev Pri-loca.lev Pri-strt.lev +Ran-fila.lev Ran-filb.lev Ran-goal.lev Ran-loca.lev Ran-strt.lev +Rog-fila.lev Rog-filb.lev Rog-goal.lev Rog-loca.lev Rog-strt.lev +Sam-fila.lev Sam-filb.lev Sam-goal.lev Sam-loca.lev Sam-strt.lev +Tou-fila.lev Tou-filb.lev Tou-goal.lev Tou-loca.lev Tou-strt.lev +Val-fila.lev Val-filb.lev Val-goal.lev Val-loca.lev Val-strt.lev +Wiz-fila.lev Wiz-filb.lev Wiz-goal.lev Wiz-loca.lev Wiz-strt.lev +air.lev asmodeus.lev astral.lev baalz.lev bigrm-1.lev +bigrm-2.lev bigrm-3.lev bigrm-4.lev bigrm-5.lev castle.lev +earth.lev fakewiz1.lev fakewiz2.lev fire.lev juiblex.lev +knox.lev medusa-1.lev medusa-2.lev minefill.lev minend-1.lev +minend-2.lev minetn-1.lev minetn-2.lev oracle.lev orcus.lev +sanctum.lev soko1-1.lev soko1-2.lev soko2-1.lev soko2-2.lev +soko3-1.lev soko3-2.lev soko4-1.lev soko4-2.lev tower1.lev +tower2.lev tower3.lev valley.lev water.lev wizard1.lev +wizard2.lev wizard3.lev +(tile files optionally generated for X ports at playground creation time) +pet_mark.xbm rip.xpm x11tiles +(files generated for Qt interface on Mac OS X) +nethack.icns Info.plist + +include: +(files generated by makedefs at compile time) +date.h onames.h pm.h vis_tab.h + +src: +(files generated by makedefs at compile time) +monstr.c vis_tab.c +(file optionally generated by tilemap at compile time) +tile.c +(files generated by 'moc' for Qt interface at compile time) +qt_kde0.moc qt_win.moc qttableview.moc + +NOTE: If your binaries were compiled with the data librarian (DLB) option, + your playground will not contain all of the files listed here. All + of the files listed as being required for the playground must still + have been built by your compiler, but the DLB code will roll them up + into another file (or files). diff --git a/Porting b/Porting new file mode 100644 index 0000000..16f9cc2 --- /dev/null +++ b/Porting @@ -0,0 +1,172 @@ + NetHack Porting Guidelines v 3.4 1999-11-29 + + + 1.0 Introduction + + This document goes through the steps required to port NetHack to a +new machine. The basic steps in porting the program are: + + 1. Get the code onto your machine. The parts of the current + directory setup you definitely need include src (NetHack code + shared by all systems), include (include files), util (code + for utility programs), and dat (various data files). The + documentation in doc is strongly recommended. You already + have the files in the top directory since you're reading this + one. :-) + + A full list of the distribution files and their associated + OSes may be found in the top-level file "Files". + + If your machine uses an OS already supported, you need the sys + subdirectory for that OS and possibly sys/share. Otherwise, + get the closest match (say sys/msdos for single-tasking OSes + and sys/unix for multi-user OSes, along with sys/share, if + nothing else comes to mind). You may want others for + comparison. + + If your machine uses a windowing system already supported, + you need the win subdirectory for that system (or the + appropriate sys subdirectory if the windowing system was + previously considered restricted to one OS). + + 2. Modify the appropriate include files to customize NetHack to + your system. You may need to add a new OS-specific "*conf.h" + file (see unixconf.h, pcconf.h, tosconf.h, etc. as examples). + + 3. If your machine uses a new OS instead of a variant of existing + OSes, add a new sys subdirectory. Add, if required, a OS- + specific copy of "main.c", "tty.c" and "unix.c". Possibly + add an OS-specific library (see "msdos.c" and "tos.c" as + examples) to provide functions NetHack wants and your OS lacks. + + 4. If your machine uses a new windowing system, follow doc/window.doc + carefully. Put files implementing these routines in a win or + sys subdirectory as appropriate. + + 5. If your compilation environment isn't close to one already + supported, try starting from the UNIX makefiles. Modify the + top level makefile and the src makefile as required. Then run + an initial compile. You are bound to get some errors. You + should be able to fix them in a fairly simple fashion. If + things seem to be getting too complex, take a step back, and + possibly send us some mail. We might be able to help. + + 6. Mail all of your fixes to us in a contextual form so that we can + easily integrate them into the code. + + One general rule of thumb exists. Always add code. Don't delete +somebody else's code for yours -- it won't work on their machine if you do. +Always add your OS specific code inside #ifdef / #else / #endif constructs +so that it will be able to be folded back into the original code easily. + + + 2.0 Include Files + + 2.1 config.h + + The file "config.h" is a master configuration file that determines +the basic features of the game, as well as many of the security options. +It is intended that end users configure the game by editing "config.h" and +an appropriate "*conf.h" file, so any #defines for individual preferences +should be added to those files. OS-specific #defines that are not intended +to be changed should also go in "*conf.h"; try to find the most appropriate +place for other #defines. + + The following sections may require modification: + + - Section 1: OS and window system selection. + You may have to put a #define for your OS here. + If your OS is yet another UNIX variant, put the + #define in unixconf.h instead. + An unfortunately large amount of stuff shares + this section because the #definitions have to + be seen before *conf.h is reached. Don't add + to this unless necessary. + + - Section 2: Global parameters and filenames. + These will have to be customized to your system. + + - Section 3: Type definitions and other compiler behavior. + These will have to be matched to your compiler. + + 2.2 global.h + + This file defines things specific to NetHack that should not +require modification by an end user. For a new port, you may have to add +automatic inclusion of another auxiliary config file (*conf.h) which you +wrote for your system. + + 2.3 extern.h + + If you create any new source modules or new functions in old modules, +you must enter the names of the new external references (the functions defined +there for external use) in this file. + + 2.4 system.h + + This file contains references for all hooks into the OS (via the +standard "C" libraries). Depending on what your standard library looks like, +you may have to put new entries into this file. + + + 3.0 Source files + + The first step in getting the game up is to get the "makedefs" +program running. This program is used to create configuration-specific +files for the game. + + Once "makedefs" has been built, the rest of the game can be compiled. +You may have to create an OS-specific module to handle things you want to +use, like a mouse or a ram-disk. + + The utility compilers "dgn_comp" and "lev_comp" may be a better +place to start. They also require "makedefs" but are independent of +"nethack". They are usually the last programs made, but since they are +much smaller they may be more tractable when first arguing with the include +files. These programs create binary data files that "nethack" uses to +guide its dungeon creation. + + 3.1 Makefiles + + This distribution provides makefiles for several kinds of systems. +There are joint makefiles for the various varieties of UNIX, makefiles for +MSDOS, a makefile for NT, and so on. You may have to create a new +makefile for your specific machine. You may even have to translate some +makefiles into a form more congenial to your system. If possible, however, +add to one of those provided. + + 3.2 termcap.c + + If your system wants to use tty windowing and it doesn't run off +of a termcap or terminfo database, you may have to put the appropriate +terminal control strings into termcap.c. This has already been done for +MSDOS, and these mods can be used as an example. You can also consider +using the termcap code from sys/share/tclib.c or sys/share/termcap.uu, +especially if your system supports multiple kinds of terminals. + + 3.3 main.c + + You may need to create a new "main.c" module. If you do, call it +[OS]main.c where the [OS] is replaced with the name of the OS you are porting +to. This file contains the mainline module, which reads options from the +command line (or wherever) and processes them. It also contains various +functions associated with game startup. + + 3.4 tty.c + + You may need to create a new "tty.c" module. If you do, call it +[OS]tty.c where the [OS] is replaced with the name of the OS you are porting +to. This file contains the routines that configure the terminal/console +for raw I/O, etc. + + 3.5 unix.c + + You may need to create a new "unix.c" module. If you do, call it +[OS]unix.c where the [OS] is replaced with the name of the OS you are porting +to. This file contains some OS dependencies concerning time and filename +creation. + + + An object of the NetHack development project is to get the game +working on as many different types of hardware and under as many different +operating systems as is practical. Any assistance will be appreciated. diff --git a/README b/README new file mode 100644 index 0000000..8c16d3e --- /dev/null +++ b/README @@ -0,0 +1,226 @@ + NetHack 3.4.3 -- General information + +NetHack 3.4 is an enhancement to the dungeon exploration game NetHack. +It is a distant descendent of Rogue and Hack, and a direct descendent of +NetHack 3.3. + +NetHack 3.4.3 is a bugfix release for NetHack 3.4.2. + * Several dozen general bug fixes including at least one fatal bug + * Correct several inconsistencies + * Handle level completely filled with monsters better + * win32tty performance enhancements when playing on Windows 98 and Windows Me + * win32gui player selection fixes + * X11 player selection fixes, one of which could be fatal + * Eliminated a gold-in-shop-container cheat + * Include bones file version compatibility info in options file + +A fuller list of changes for this release can be found in the file +doc/fixes34.3 in the source distribution. The text in there was written +for the development team's own use and is provided "as is", so please do +not ask us to further explain the entries in that file. + +The internal structure of bones and save files has not changed between +NetHack 3.4.0, 3.4.1, 3.4.2 and now 3.4.3. That means that if you use the +same compiler, the same compiler version and compiler switches, the same +NetHack compile-time options, and you have not incorporated any additional +source code patches that altered the save file format on your system, then +bones and save files from 3.4.0 through 3.4.3 should be compatible. + + + - - - - - - - - - - - + +Please read items (1), (2) and (3) BEFORE doing anything with your new code. + +1. Unpack the code in a dedicated new directory. We will refer to that + directory as the 'Top' directory. It makes no difference what you + call it. + +2. If there is no flaw in the packaging, many sub-directories will be + automatically created, and files will be deposited in them: + + a. A 'dat' directory, which contains a variety of data files. + b. A 'doc' directory, which contains various documentation. + c. An 'include' directory, which contains *.h files. + d. A 'src' directory, which contains game *.c files used by all versions. + e. A 'util' directory, which contains files for utility programs. + f. A 'sys' directory, which contains subdirectories for files that + are operating-system specific. + g. A 'sys/share' subdirectory, which contains files shared by some OSs. + h. A 'sys/share/sounds' subsubdirectory, which contains sound files + shared by some OSs. + i. A 'sys/amiga' subdirectory, which contains files specific to AmigaDOS. + j. A 'sys/amiga/ship' subsubdirectory + k. A 'sys/atari' subdirectory, which contains files specific to TOS. + l. A 'sys/be' subdirectory, which contains files specific to Be OS. + m. A 'sys/mac' subdirectory, which contains files specific to MacOS. + n. A 'sys/msdos' subdirectory, which contains files specific to MS-DOS. + o. A 'sys/os2' subdirectory, which contains files specific to OS/2. + p. A 'sys/unix' subdirectory, which contains files specific to UNIX. + q. A 'sys/vms' subdirectory, which contains files specific to VMS. + r. A 'sys/wince' subdirectory, which contains files specific to Windows CE. + s. A 'sys/winnt' subdirectory, which contains files specific to Windows NT. + t. A 'win' directory, which contains subdirectories for files that + are windowing-system specific (but not operating-system specific). + u. A 'win/share' subdirectory, which contains files shared by some + windowing systems. + v. A 'win/Qt' subdirectory, which contains files specific to Qt. + w. A 'win/X11' subdirectory, which contains files specific to X11. + x. A 'win/gem' subdirectory, which contains files specific to GEM. + y. A 'win/gnome' subdirectory, which contains files specific to GNOME. + z. A 'win/tty' subdirectory, which contains files specific to ttys. + A. A 'win/win32' subdirectory, which contains files specific to the + Windows Win32 API. + + The names of these directories should not be changed unless you are + ready to go through the makefiles and the makedefs program and change + all the directory references in them. + +3. Having unpacked, you should have a file called 'Files' in your Top + directory. This file contains the list of all the files you now SHOULD + have in each directory. Please check the files in each directory + against this list to make sure that you have a complete set. + +4. Before you do anything else, please read carefully the file called + "license" in the 'dat' subdirectory. It is expected that you comply + with the terms of that license, and we are very serious about it. + +5. If everything is in order, you can now turn to trying to get the program + to compile and run on your particular system. It is worth mentioning + that the default configuration is SysV/Sun/Solaris2.x (simply because + the code was housed on such a system). It is also worth mentioning + here that NetHack 3.4 is a huge program. If you intend to run it on a + small machine, you'll have to make hard choices among the options + available in config.h. + + The files sys/*/Install.* were written to guide you in configuring the + program for your operating system. The files win/*/Install.* are + available, where necessary, to help you in configuring the program + for particular windowing environments. Reading them, and the man pages, + should answer most of your questions. + + At the time of this release, NetHack 3.4 is known to run/compile on: + + Apple Macintosh running MacOS 7.5 or higher, LinuxPPC, BeOS 4.0 + Atari ST/TT/Falcon running TOS (or MultiTOS) with GCC + Commodore Amiga running AmigaDOS 3.0 or higher with SAS/C 6.x + (but see Makefile.ami about DICE and Manx) + DEC Alpha/VMS (aka OpenVMS AXP), running V1.x through V7.1 + DEC VAX/VMS, running V4.6 through V7.1 + HP 9000s700 running HP-UX 10.x, 11.x + IBM PS/2 and AT compatibles running OS/2 - 2.0 and up with GCC emx + Intel 80386 or greater (or clone) boxes running MS-DOS with DPMI. + Intel 80386 or greater (or clone) boxes running Linux, or BSDI. + Intel 80386 or greater (or clone) boxes running Windows 95/98/Me. + Intel 80386 or greater (or clone) boxes running Windows NT/2000/XP/2003. + Intel Pentium or better (or clone) running BeOS 4.5 + Sun SPARC based machine running SunOS 4.x, Solaris 2.x, or Solaris 7 + + NetHack 3.4 will also run on the following, but a cross-compiler hosted + on another platform, such as win32, is required to build from source. + + Pocket PC devices running Windows CE 3.0 and higher + H/PC Pro devices running Windows CE 2.11 and higher. + Palm Size PC 1.1 devices running Windows CE 2.11 + + Previous versions of NetHack were tested on the following systems, + and we expect that NetHack 3.4 will work on them as well: + + AT&T 3B1 running System V (3.51) + AT&T 3B2/600 & 3B2/622 running System V R3.2.1 + AT&T 3B2/1000 Model 80 running System V R3.2.2 + AT&T 3B4000 running System V + AT&T 6386 running System V R3.2 + Data General AViiON systems running DG/UX + DEC vaxen running BSD, Ultrix + Decstations running Ultrix 3.1, 4.x + Encore Multimax running UMAX 4.2 + Gould NP1 running UTX 3/2 + HP 9000s300 running HP-UX + HP 9000s700 running HP-UX 9.x + IBM PC/RT and RS/6000 running AIX 3.x + IBM PS/2 and AT compatibles running OS/2 1.1 - 2.0 (and probably + Warp) with Microsoft 6.0, and OS/2 2.0 and up with IBM CSet++ 2.0. + Intel 80386 or greater (or clone) running 386BSD + Mips M2000 running RiscOS 4.1 + NeXT running Mach (using BSD configuration) + Pyramid 9820x running OSx 4.4c + SGI Iris running IRIX + Stardent Vistra 800 running SysV R4.0 + Stride 460 running UniStride 2.1 + Sun-3s, -4s, and -386is running SunOS 3.x + Sun-3s and -386is running SunOS 4.x + Valid Logic Systems SCALD-System + + Unless otherwise mentioned, the compiler used was the OS-vendor's + C compiler. + + With the demise of Windows NT on the DEC Alpha, no attempt has been + made to build NetHack 3.4.3 on that platform. + + Windows 98/Me have been the most problematic Windows platforms for + running NetHack so far. Patches for 3.4.2 (courtesy Michael Lehotay) + have been incorporated into 3.4.3 to help make them work better. Your + results may vary. + + A build for Intel 80286 machines and DOS "real mode" overlaid versions + has not been produced for 3.4.3. Nobody on the porting team has + the time or the software to attempt the necessary tuning that will allow + it to achieve the balance of having just the right amount of available + memory, and still have acceptable performance. The sources necessary + to do so are still included in the source distribution, so if someone + has access to a real-mode compiler and lots of spare time on their hands, + you may be able to get things working. Of course you do so at your own risk. + + - - - - - - - - - - - + +If you have problems building the game, or you find bugs in it, we recommend +filing a bug report from our "Contact Us" web page at: + http://www.nethack.org/ + +When sending correspondence, please observe the following: +o Please be sure to include your machine type, OS, and patchlevel. +o Never send us binary files (e.g. save files or bones files). Whichever + platform you are using, only a small minority of the development team has + access to it, and you will rapidly annoy the others. If you have found + a bug and think that your save file would aid in solving the problem, + send us a description in words of the problem, your machine type, your + operating system, and the version of NetHack. Tell us that you have a + save file, but do not actually send it. + In the rare case that we think your save file would be helpful, you will + be contacted by a member of the development team with the address of a + specific person to send the save file to. +o Though we make an effort to reply to each bug report, it may take some + time before you receive feedback. This is especially true during the + period immediately after a new release, when we get the most bug reports. +o We don't give hints for playing the game. +o Don't bother to ask when the next version will be out. You will not get + a reply. + +If you don't have access to the world wide web, or if you want to submit +a patch for the NetHack source code via email directly, you can direct it +to this address: + nethack-bugs (at) nethack.org + +If you've changed something to get NetHack to run on your system, it's likely +that others have done it by making slightly different modifications. By routing +your patches through the development team, we should be able to avoid making +everyone else choose among variant patches claiming to do the same thing, to keep +most of the copies of 3.4 synchronized by means of official patches, and to +maintain the painfully-created file organization. (This process has been working +since the time when everyone just posted their own patches to 2.3. At that time, +there were no archived bug-fixes to give to people who got 2.3 after its initial +release, so the same bugs kept being discovered by new batches of people.) +We have been successful in preventing this from happening since the 3.0 +release. Please cooperate to keep this from happening to 3.4. + +It is inevitable that we will reject some proposed additions of new features +either because they do not fit our conception of the game, or because they +require more code than we consider they're worth. If we reject your feature, +you are free, of course, to post the patches to the net yourself and let the +marketplace decide their worth. + +All of this amounts to the following: If you decide to apply a free-lanced +patch to your 3.4 code, you are on your own. In our own patches, we will +assume that your code is synchronized with ours. + + -- Good luck, and happy Hacking -- diff --git a/binary/license b/binary/license new file mode 100644 index 0000000..5ad7e34 --- /dev/null +++ b/binary/license @@ -0,0 +1,95 @@ + NETHACK GENERAL PUBLIC LICENSE + (Copyright 1989 M. Stephenson) + + (Based on the BISON general public license, + copyright 1988 Richard M. Stallman) + + Everyone is permitted to copy and distribute verbatim copies of this + license, but changing it is not allowed. You can also use this wording to + make the terms for other programs. + + The license agreements of most software companies keep you at the mercy of +those companies. By contrast, our general public license is intended to give +everyone the right to share NetHack. To make sure that you get the rights we +want you to have, we need to make restrictions that forbid anyone to deny you +these rights or to ask you to surrender the rights. Hence this license +agreement. + + Specifically, we want to make sure that you have the right to give away +copies of NetHack, that you receive source code or else can get it if you +want it, that you can change NetHack or use pieces of it in new free +programs, and that you know you can do these things. + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute copies +of NetHack, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must tell them their rights. + + Also, for our own protection, we must make certain that everyone finds out +that there is no warranty for NetHack. If NetHack is modified by someone +else and passed on, we want its recipients to know that what they have is +not what we distributed. + + Therefore we (Mike Stephenson and other holders of NetHack copyrights) make +the following terms which say what you must do to be allowed to distribute or +change NetHack. + + + COPYING POLICIES + + 1. You may copy and distribute verbatim copies of NetHack source code as +you receive it, in any medium, provided that you keep intact the notices on +all files that refer to copyrights, to this License Agreement, and to the +absence of any warranty; and give any other recipients of the NetHack +program a copy of this License Agreement along with the program. + + 2. You may modify your copy or copies of NetHack or any portion of it, and +copy and distribute such modifications under the terms of Paragraph 1 above +(including distributing this License Agreement), provided that you also do the +following: + + a) cause the modified files to carry prominent notices stating that you + changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that in + whole or in part contains or is a derivative of NetHack or any part + thereof, to be licensed at no charge to all third parties on terms + identical to those contained in this License Agreement (except that you + may choose to grant more extensive warranty protection to some or all + third parties, at your option) + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty protection + in exchange for a fee. + + 3. You may copy and distribute NetHack (or a portion or derivative of it, +under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete machine-readable source code, which + must be distributed under the terms of Paragraphs 1 and 2 above; or, + + b) accompany it with full information as to how to obtain the complete + machine-readable source code from an appropriate archive site. (This + alternative is allowed only for noncommercial distribution.) + +For these purposes, complete source code means either the full source +distribution as originally released over Usenet or updated copies of the +files in this distribution used to create the object code or executable. + + 4. You may not copy, sublicense, distribute or transfer NetHack except as +expressly provided under this License Agreement. Any attempt otherwise to +copy, sublicense, distribute or transfer NetHack is void and your rights to +use the program under this License agreement shall be automatically +terminated. However, parties who have received computer software programs +from you with this License Agreement will not have their licenses terminated +so long as such parties remain in full compliance. + + +Stated plainly: You are permitted to modify NetHack, or otherwise use parts +of NetHack, provided that you comply with the conditions specified above; +in particular, your modified NetHack or program containing parts of NetHack +must remain freely available as provided in this License Agreement. In +other words, go ahead and share NetHack, but don't try to stop anyone else +from sharing it farther. diff --git a/build/dgncomp.dsp b/build/dgncomp.dsp new file mode 100644 index 0000000..815384c --- /dev/null +++ b/build/dgncomp.dsp @@ -0,0 +1,297 @@ +# Microsoft Developer Studio Project File - Name="dgncomp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dgncomp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dgncomp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dgncomp.mak" CFG="dgncomp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dgncomp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dgncomp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dgncomp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "..\util" +# PROP BASE Intermediate_Dir "dgncomp___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\dgn_comp.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=dgncomp +PostBuild_Cmds=echo Building dungeon echo chdir ..\dat chdir ..\dat echo ..\util\dgn_comp.exe dungeon.pdf ..\util\dgn_comp.exe dungeon.pdf echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "dgncomp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "dgncomp___Win32_Debug" +# PROP BASE Intermediate_Dir "dgncomp___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\dgn_comp.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=dgncomp +PostBuild_Cmds=echo Building dungeon echo chdir ..\dat chdir ..\dat echo ..\util\dgn_comp.exe dungeon.pdf ..\util\dgn_comp.exe dungeon.pdf echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "dgncomp - Win32 Release" +# Name "dgncomp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_lex.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_yacc.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_comp.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\onames.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\pm.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/dgnstuff.dsp b/build/dgnstuff.dsp new file mode 100644 index 0000000..ab1e95a --- /dev/null +++ b/build/dgnstuff.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="dgnstuff" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=dgnstuff - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dgnstuff.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dgnstuff.mak" CFG="dgnstuff - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dgnstuff - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "dgnstuff - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "dgnstuff - Win32 Release" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f dgnstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "dgnstuff.exe" +# PROP BASE Bsc_Name "dgnstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "dgnstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\dgncomp.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "dgnstuff - Win32 Debug" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f dgnstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "dgnstuff.exe" +# PROP BASE Bsc_Name "dgnstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "dgnstuff___Win32_Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake /f "dgnstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\dgncomp.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "dgnstuff - Win32 Release" +# Name "dgnstuff - Win32 Debug" + +!IF "$(CFG)" == "dgnstuff - Win32 Release" + +!ELSEIF "$(CFG)" == "dgnstuff - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/dgnstuff.mak b/build/dgnstuff.mak new file mode 100644 index 0000000..8e5aa81 --- /dev/null +++ b/build/dgnstuff.mak @@ -0,0 +1,59 @@ +#Set all of these or none of them +#YACC = byacc.exe +#LEX = flex.exe +#YTABC = y_tab.c +#YTABH = y_tab.h +#LEXYYC = lexyy.c + +!IF "$(YACC)"!="" +@echo Yacc-alike set to $(YACC) +@echo YTABC set to $(YTABC) +@echo YTABH set to $(YTABH) +!ENDIF + +!IF "$(LEX)"!="" +@echo Lex-alike set to $(LEX) +@echo LEXYYC set to $(LEXYYC) +!ENDIF + +default: all + +all: ..\util\dgn_yacc.c ..\util\dgn_lex.c + +rebuild: clean all + +clean: + -del ..\util\dgn_lex.c + -del ..\util\dgn_yacc.c + -del ..\include\dgn_comp.h + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +..\util\dgn_yacc.c ..\include\dgn_comp.h : ..\util\dgn_comp.y +!IF "$(YACC)"=="" + @echo Using pre-built dgn_yacc.c and dgn_comp.h + @copy ..\sys\share\dgn_yacc.c ..\util\dgn_yacc.c + @copy ..\sys\share\dgn_comp.h ..\include\dgn_comp.h +!ELSE + chdir ..\util + $(YACC) -d dgn_comp.y + copy $(YTABC) $@ + copy $(YTABH) ..\include\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir ..\build +!ENDIF + +..\util\dgn_lex.c: ..\util\dgn_comp.l +!IF "$(LEX)"=="" + @echo Using pre-built dgn_lex.c + @copy ..\sys\share\dgn_lex.c $@ +!ELSE + chdir ..\util + $(LEX) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir ..\build +!ENDIF diff --git a/build/dlb_main.dsp b/build/dlb_main.dsp new file mode 100644 index 0000000..8dff70f --- /dev/null +++ b/build/dlb_main.dsp @@ -0,0 +1,179 @@ +# Microsoft Developer Studio Project File - Name="dlb_main" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dlb_main - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dlb_main.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dlb_main.mak" CFG="dlb_main - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dlb_main - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dlb_main - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dlb_main - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "DLB" /D "WIN32CON" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\dlb_main.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Packaging via DLB +PostBuild_Cmds=echo chdir ..\dat \ +chdir ..\dat \ +chdir \ + echo data >dlb.lst \ + echo oracles >>dlb.lst \ + if exist options echo options >>dlb.lst \ + if exist ttyoptions echo ttyoptions >>dlb.lst \ + if exist guioptions echo guioptions >>dlb.lst \ + if NOT exist porthelp copy ..\sys\winnt\porthelp porthelp \ + if exist porthelp echo porthelp >>dlb.lst \ + echo quest.dat >>dlb.lst \ + echo rumors >>dlb.lst \ + echo help >>dlb.lst \ + echo hh >>dlb.lst \ + echo cmdhelp >>dlb.lst \ + echo history >>dlb.lst \ + echo opthelp >>dlb.lst \ + echo wizhelp >>dlb.lst \ + echo dungeon >>dlb.lst \ + echo license >>dlb.lst \ + for %%N in (*.lev) do echo %%N >>dlb.lst \ + ..\util\dlb_main.exe cIf dlb.lst nhdat \ + echo chdir ..\build \ +chdir ..\build \ +echo if NOT exist ..\binary\*.* mkdir ..\binary \ + if NOT exist ..\binary\*.* mkdir ..\binary +# End Special Build Tool + +!ELSEIF "$(CFG)" == "dlb_main - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "DLB" /D "WIN32CON" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\dlb_main.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Packaging via dlb +PostBuild_Cmds=echo chdir ..\dat \ +chdir ..\dat \ +chdir \ + echo data >dlb.lst \ + echo oracles >>dlb.lst \ + if exist options echo options >>dlb.lst \ + if exist ttyoptions echo ttyoptions >>dlb.lst \ + if exist guioptions echo guioptions >>dlb.lst \ + if NOT exist porthelp copy ..\sys\winnt\porthelp porthelp \ + if exist porthelp echo porthelp >>dlb.lst \ + echo quest.dat >>dlb.lst \ + echo rumors >>dlb.lst \ + echo help >>dlb.lst \ + echo hh >>dlb.lst \ + echo cmdhelp >>dlb.lst \ + echo history >>dlb.lst \ + echo opthelp >>dlb.lst \ + echo wizhelp >>dlb.lst \ + echo dungeon >>dlb.lst \ + echo license >>dlb.lst \ + for %%N in (*.lev) do echo %%N >>dlb.lst \ + ..\util\dlb_main.exe cIf dlb.lst nhdat \ +echo chdir ..\build \ +chdir ..\build \ +echo if NOT exist ..\binary\*.* mkdir ..\binary \ +if NOT exist ..\binary\*.* mkdir ..\binary +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "dlb_main - Win32 Release" +# Name "dlb_main - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\dlb.c +# End Source File +# Begin Source File + +SOURCE=..\util\dlb_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\dlb.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/levcomp.dsp b/build/levcomp.dsp new file mode 100644 index 0000000..7c1486e --- /dev/null +++ b/build/levcomp.dsp @@ -0,0 +1,198 @@ +# Microsoft Developer Studio Project File - Name="levcomp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=levcomp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "levcomp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "levcomp.mak" CFG="levcomp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "levcomp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "levcomp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "levcomp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=levcomp +PostBuild_Cmds=echo Building special levels echo chdir ..\dat chdir ..\dat \ +echo arch.des ..\util\levcomp.exe arch.des \ +echo barb.des ..\util\levcomp.exe barb.des \ +echo bigroom.des ..\util\levcomp.exe bigroom.des \ +echo castle.des ..\util\levcomp.exe castle.des \ +echo caveman.des ..\util\levcomp.exe caveman.des \ +echo endgame.des ..\util\levcomp.exe endgame.des \ +echo gehennom.des ..\util\levcomp.exe gehennom.des \ +echo healer.des ..\util\levcomp.exe healer.des \ +echo knight.des ..\util\levcomp.exe knight.des \ +echo knox.des ..\util\levcomp.exe knox.des \ +echo medusa.des ..\util\levcomp.exe medusa.des \ +echo mines.des ..\util\levcomp.exe mines.des \ +echo monk.des ..\util\levcomp.exe monk.des \ +echo oracle.des ..\util\levcomp.exe oracle.des \ +echo priest.des ..\util\levcomp.exe priest.des \ +echo ranger.des ..\util\levcomp.exe ranger.des \ +echo rogue.des ..\util\levcomp.exe rogue.des \ +echo samurai.des ..\util\levcomp.exe samurai.des \ +echo sokoban.des ..\util\levcomp.exe sokoban.des \ +echo tourist.des ..\util\levcomp.exe tourist.des \ +echo tower.des ..\util\levcomp.exe tower.des \ +echo valkyrie.des ..\util\levcomp.exe valkyrie.des \ +echo wizard .des ..\util\levcomp.exe wizard.des \ +echo yendor.des ..\util\levcomp.exe yendor.des \ +echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "levcomp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=levcomp +PostBuild_Cmds=echo Building special levels echo chdir ..\dat chdir ..\dat \ +echo arch.des ..\util\levcomp.exe arch.des \ +echo barb.des ..\util\levcomp.exe barb.des \ +echo bigroom.des ..\util\levcomp.exe bigroom.des \ +echo castle.des ..\util\levcomp.exe castle.des \ +echo caveman.des ..\util\levcomp.exe caveman.des \ +echo endgame.des ..\util\levcomp.exe endgame.des \ +echo gehennom.des ..\util\levcomp.exe gehennom.des \ +echo healer.des ..\util\levcomp.exe healer.des \ +echo knight.des ..\util\levcomp.exe knight.des \ +echo knox.des ..\util\levcomp.exe knox.des \ +echo medusa.des ..\util\levcomp.exe medusa.des \ +echo mines.des ..\util\levcomp.exe mines.des \ +echo monk.des ..\util\levcomp.exe monk.des \ +echo oracle.des ..\util\levcomp.exe oracle.des \ +echo priest.des ..\util\levcomp.exe priest.des \ +echo ranger.des ..\util\levcomp.exe ranger.des \ +echo rogue.des ..\util\levcomp.exe rogue.des \ +echo samurai.des ..\util\levcomp.exe samurai.des \ +echo sokoban.des ..\util\levcomp.exe sokoban.des \ +echo tourist.des ..\util\levcomp.exe tourist.des \ +echo tower.des ..\util\levcomp.exe tower.des \ +echo valkyrie.des ..\util\levcomp.exe valkyrie.des \ +echo wizard .des ..\util\levcomp.exe wizard.des \ +echo yendor.des ..\util\levcomp.exe yendor.des \ +echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "levcomp - Win32 Release" +# Name "levcomp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_lex.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_yacc.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\lev_comp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/levstuff.dsp b/build/levstuff.dsp new file mode 100644 index 0000000..276ad81 --- /dev/null +++ b/build/levstuff.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="levstuff" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=levstuff - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "levstuff.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "levstuff.mak" CFG="levstuff - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "levstuff - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "levstuff - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "levstuff - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f levstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "levstuff.exe" +# PROP BASE Bsc_Name "levstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "levstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\lev_lex.c" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "levstuff - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "levstuff___Win32_Debug0" +# PROP BASE Intermediate_Dir "levstuff___Win32_Debug0" +# PROP BASE Cmd_Line "NMAKE /f levstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "levstuff.exe" +# PROP BASE Bsc_Name "levstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "levstuff___Win32_Debug0" +# PROP Intermediate_Dir "levstuff___Win32_Debug0" +# PROP Cmd_Line "nmake /f "levstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\lev_lex.c" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "levstuff - Win32 Release" +# Name "levstuff - Win32 Debug" + +!IF "$(CFG)" == "levstuff - Win32 Release" + +!ELSEIF "$(CFG)" == "levstuff - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/levstuff.mak b/build/levstuff.mak new file mode 100644 index 0000000..c7540c4 --- /dev/null +++ b/build/levstuff.mak @@ -0,0 +1,59 @@ +#YACC = byacc.exe +#LEX = flex.exe +#YTABC = y_tab.c +#YTABH = y_tab.h +#LEXYYC = lexyy.c + +!IF "$(YACC)"!="" +@echo Yacc-alike set to $(YACC) +@echo YTABC set to $(YTABC) +@echo YTABH set to $(YTABH) +!ENDIF + +!IF "$(LEX)"!="" +@echo Lex-alike set to $(LEX) +@echo LEXYYC set to $(LEXYYC) +!ENDIF + + +default: all + +all: ..\util\lev_yacc.c ..\util\lev_lex.c + +rebuild: clean all + +clean: + -del ..\util\lev_lex.c + -del ..\util\lev_yacc.c + -del ..\include\lev_comp.h + +#========================================== +# Level Compiler Stuff +#========================================== +..\util\lev_yacc.c ..\include\lev_comp.h: ..\util\lev_comp.y +!IF "$(YACC)"=="" + @echo Using pre-built lev_yacc.c and lev_comp.h + @copy ..\sys\share\lev_yacc.c ..\util\lev_yacc.c + @copy ..\sys\share\lev_comp.h ..\include\lev_comp.h +!ELSE + chdir ..\util + $(YACC) -d lev_comp.y + copy $(YTABC) $@ + copy $(YTABH) ..\include\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir ..\build +!ENDIF + +..\util\lev_lex.c: ..\util\lev_comp.l +!IF "$(LEX)"=="" + @echo Using pre-built lev_lex.c + @copy ..\sys\share\lev_lex.c $@ +!ELSE + chdir ..\util + $(LEX) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir ..\build +!ENDIF + diff --git a/build/makedefs.dsp b/build/makedefs.dsp new file mode 100644 index 0000000..5ecdc88 --- /dev/null +++ b/build/makedefs.dsp @@ -0,0 +1,198 @@ +# Microsoft Developer Studio Project File - Name="makedefs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=makedefs - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makedefs.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makedefs.mak" CFG="makedefs - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makedefs - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "makedefs - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "makedefs - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Running makedefs +PostBuild_Cmds=echo chdir ..\util chdir ..\util chdir \ +echo makedefs.exe -v makedefs.exe -v \ +echo makedefs.exe -o makedefs.exe -o \ +echo makedefs.exe -p makedefs.exe -p \ +echo makedefs.exe -m makedefs.exe -m \ +echo makedefs.exe -z makedefs.exe -z \ +echo chdir ..\dat chdir ..\dat chdir \ +echo Generating NetHack database echo ..\util\makedefs.exe -d ..\util\makedefs.exe -d \ +echo Generating rumors echo ..\util\makedefs.exe -r ..\util\makedefs.exe -r \ +echo Generating quests echo ..\util\makedefs.exe -q ..\util\makedefs.exe -q \ +echo Generating oracles echo ..\util\makedefs.exe -h ..\util\makedefs.exe -h \ +echo Generating dungeon.pdf echo ..\util\makedefs.exe -e ..\util\makedefs.exe -e \ +echo chdir ..\build chdir ..\build \ +copy ..\win\share\tilemap.c ..\win\share\tiletxt.c +# End Special Build Tool + +!ELSEIF "$(CFG)" == "makedefs - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "." /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Running makedefs +PostBuild_Cmds=echo chdir ..\util chdir ..\util chdir \ +echo makedefs.exe -v makedefs.exe -v \ +echo makedefs.exe -o makedefs.exe -o \ +echo makedefs.exe -p makedefs.exe -p \ +echo makedefs.exe -m makedefs.exe -m \ +echo makedefs.exe -z makedefs.exe -z \ +echo chdir ..\dat chdir ..\dat chdir \ +echo Generating NetHack database echo ..\util\makedefs.exe -d ..\util\makedefs.exe -d \ +echo Generating rumors echo ..\util\makedefs.exe -r ..\util\makedefs.exe -r \ +echo Generating quests echo ..\util\makedefs.exe -q ..\util\makedefs.exe -q \ +echo Generating oracles echo ..\util\makedefs.exe -h ..\util\makedefs.exe -h \ +echo Generating dungeon.pdf echo ..\util\makedefs.exe -e ..\util\makedefs.exe -e \ +echo chdir ..\build chdir ..\build \ +copy ..\win\share\tilemap.c ..\win\share\tiletxt.c +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "makedefs - Win32 Release" +# Name "makedefs - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\util\makedefs.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monflag.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\patchlevel.h +# End Source File +# Begin Source File + +SOURCE=..\include\qtext.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/nethackw.dsp b/build/nethackw.dsp new file mode 100644 index 0000000..03521aa --- /dev/null +++ b/build/nethackw.dsp @@ -0,0 +1,1107 @@ +# Microsoft Developer Studio Project File - Name="NetHackW" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=NetHackW - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "NetHackW.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "NetHackW.mak" CFG="NetHackW - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "NetHackW - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "NetHackW - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "NetHackW - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Og /Oy /Ob1 /Gs /Gf /Gy /Oi- /Ot /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /Og /Oy /Ob1 /Gs /Gf /Gy /Oi- /Ot /I "..\win\win32" /I "..\include" /I "..\sys\winnt" /I "..\sys\share" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /map /debug /machine:I386 /MAPINFO:EXPORTS /MAPINFO:LINES +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +OutDir=.\Release +SOURCE="$(InputPath)" +PostBuild_Desc=Install exe +PostBuild_Cmds=copy $(OutDir)\NetHackW.exe ..\binary \ +copy ..\dat\nhdat ..\binary \ +copy ..\dat\license ..\binary \ +if exist tiles.bmp copy tiles.bmp ..\binary \ +if exist ..\doc\Guidebook.txt copy ..\doc\Guidebook.txt ..\binary\Guidebook.txt \ +if exist ..\doc\nethack.txt copy ..\doc\nethack.txt ..\binary\NetHack.txt \ +copy ..\sys\winnt\defaults.nh ..\binary\defaults.nh +# End Special Build Tool + +!ELSEIF "$(CFG)" == "NetHackW - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\win\win32" /I "..\include" /I "..\sys\winnt" /I "..\sys\share" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Debug +SOURCE="$(InputPath)" +PostBuild_Desc=Install exe +PostBuild_Cmds=if NOT exist ..\binary\*.* mkdir ..\binary \ +copy $(OutDir)\NetHackW.exe ..\binary \ +copy ..\dat\nhdat ..\binary \ +copy ..\dat\license ..\binary \ +if exist tiles.bmp copy tiles.bmp ..\binary \ +if exist ..\doc\Guidebook.txt copy ..\doc\Guidebook.txt ..\binary\Guidebook.txt \ +if exist ..\doc\nethack.txt copy ..\doc\nethack.txt ..\binary\NetHack.txt \ +copy ..\sys\winnt\defaults.nh ..\binary\defaults.nh +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "NetHackW - Win32 Release" +# Name "NetHackW - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\allmain.c +# End Source File +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\apply.c +# End Source File +# Begin Source File + +SOURCE=..\src\artifact.c +# End Source File +# Begin Source File + +SOURCE=..\src\attrib.c +# End Source File +# Begin Source File + +SOURCE=..\src\ball.c +# End Source File +# Begin Source File + +SOURCE=..\src\bones.c +# End Source File +# Begin Source File + +SOURCE=..\src\botl.c +# End Source File +# Begin Source File + +SOURCE=..\src\cmd.c +# End Source File +# Begin Source File + +SOURCE=..\src\dbridge.c +# End Source File +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\detect.c +# End Source File +# Begin Source File + +SOURCE=..\src\dig.c +# End Source File +# Begin Source File + +SOURCE=..\src\display.c +# End Source File +# Begin Source File + +SOURCE=..\src\dlb.c +# End Source File +# Begin Source File + +SOURCE=..\src\do.c +# End Source File +# Begin Source File + +SOURCE=..\src\do_name.c +# End Source File +# Begin Source File + +SOURCE=..\src\do_wear.c +# End Source File +# Begin Source File + +SOURCE=..\src\dog.c +# End Source File +# Begin Source File + +SOURCE=..\src\dogmove.c +# End Source File +# Begin Source File + +SOURCE=..\src\dokick.c +# End Source File +# Begin Source File + +SOURCE=..\src\dothrow.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\src\dungeon.c +# End Source File +# Begin Source File + +SOURCE=..\src\eat.c +# End Source File +# Begin Source File + +SOURCE=..\src\end.c +# End Source File +# Begin Source File + +SOURCE=..\src\engrave.c +# End Source File +# Begin Source File + +SOURCE=..\src\exper.c +# End Source File +# Begin Source File + +SOURCE=..\src\explode.c +# End Source File +# Begin Source File + +SOURCE=..\src\extralev.c +# End Source File +# Begin Source File + +SOURCE=..\src\files.c +# End Source File +# Begin Source File + +SOURCE=..\src\fountain.c +# End Source File +# Begin Source File + +SOURCE=..\win\tty\getline.c +# End Source File +# Begin Source File + +SOURCE=..\src\hack.c +# End Source File +# Begin Source File + +SOURCE=..\src\hacklib.c +# End Source File +# Begin Source File + +SOURCE=..\src\invent.c +# End Source File +# Begin Source File + +SOURCE=..\src\light.c +# End Source File +# Begin Source File + +SOURCE=..\src\lock.c +# End Source File +# Begin Source File + +SOURCE=..\src\mail.c +# End Source File +# Begin Source File + +SOURCE=..\src\makemon.c +# End Source File +# Begin Source File + +SOURCE=..\src\mapglyph.c +# End Source File +# Begin Source File + +SOURCE=..\src\mcastu.c +# End Source File +# Begin Source File + +SOURCE=..\src\mhitm.c +# End Source File +# Begin Source File + +SOURCE=..\src\mhitu.c +# End Source File +# Begin Source File + +SOURCE=..\src\minion.c +# End Source File +# Begin Source File + +SOURCE=..\src\mklev.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkmap.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkmaze.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkobj.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkroom.c +# End Source File +# Begin Source File + +SOURCE=..\src\mon.c +# End Source File +# Begin Source File + +SOURCE=..\src\mondata.c +# End Source File +# Begin Source File + +SOURCE=..\src\monmove.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\monstr.c +# End Source File +# Begin Source File + +SOURCE=..\src\mplayer.c +# End Source File +# Begin Source File + +SOURCE=..\src\mthrowu.c +# End Source File +# Begin Source File + +SOURCE=..\src\muse.c +# End Source File +# Begin Source File + +SOURCE=..\src\music.c +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\ntsound.c +# End Source File +# Begin Source File + +SOURCE=..\src\o_init.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\src\objnam.c +# End Source File +# Begin Source File + +SOURCE=..\src\options.c +# End Source File +# Begin Source File + +SOURCE=..\src\pager.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcmain.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcsys.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcunix.c +# End Source File +# Begin Source File + +SOURCE=..\src\pickup.c +# End Source File +# Begin Source File + +SOURCE=..\src\pline.c +# End Source File +# Begin Source File + +SOURCE=..\src\polyself.c +# End Source File +# Begin Source File + +SOURCE=..\src\potion.c +# End Source File +# Begin Source File + +SOURCE=..\src\pray.c +# End Source File +# Begin Source File + +SOURCE=..\src\priest.c +# End Source File +# Begin Source File + +SOURCE=..\src\quest.c +# End Source File +# Begin Source File + +SOURCE=..\src\questpgr.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\random.c +# End Source File +# Begin Source File + +SOURCE=..\src\read.c +# End Source File +# Begin Source File + +SOURCE=..\src\rect.c +# End Source File +# Begin Source File + +SOURCE=..\src\region.c +# End Source File +# Begin Source File + +SOURCE=..\src\restore.c +# End Source File +# Begin Source File + +SOURCE=..\src\rip.c +# End Source File +# Begin Source File + +SOURCE=..\src\rnd.c +# End Source File +# Begin Source File + +SOURCE=..\src\role.c +# End Source File +# Begin Source File + +SOURCE=..\src\rumors.c +# End Source File +# Begin Source File + +SOURCE=..\src\save.c +# End Source File +# Begin Source File + +SOURCE=..\src\shk.c +# End Source File +# Begin Source File + +SOURCE=..\src\shknam.c +# End Source File +# Begin Source File + +SOURCE=..\src\sit.c +# End Source File +# Begin Source File + +SOURCE=..\src\sounds.c +# End Source File +# Begin Source File + +SOURCE=..\src\sp_lev.c +# End Source File +# Begin Source File + +SOURCE=..\src\spell.c +# End Source File +# Begin Source File + +SOURCE=..\src\steal.c +# End Source File +# Begin Source File + +SOURCE=..\src\steed.c +# End Source File +# Begin Source File + +SOURCE=..\src\teleport.c +# End Source File +# Begin Source File + +SOURCE=..\src\tile.c +# End Source File +# Begin Source File + +SOURCE=..\src\timeout.c +# End Source File +# Begin Source File + +SOURCE=..\src\topten.c +# End Source File +# Begin Source File + +SOURCE=..\src\track.c +# End Source File +# Begin Source File + +SOURCE=..\src\trap.c +# End Source File +# Begin Source File + +SOURCE=..\src\u_init.c +# End Source File +# Begin Source File + +SOURCE=..\src\uhitm.c +# End Source File +# Begin Source File + +SOURCE=..\src\vault.c +# End Source File +# Begin Source File + +SOURCE=..\src\version.c +# End Source File +# Begin Source File + +SOURCE=..\src\vision.c +# End Source File +# Begin Source File + +SOURCE=..\src\weapon.c +# End Source File +# Begin Source File + +SOURCE=..\src\were.c +# End Source File +# Begin Source File + +SOURCE=..\src\wield.c +# End Source File +# Begin Source File + +SOURCE=..\src\windows.c +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\winnt.c +# End Source File +# Begin Source File + +SOURCE=..\win\tty\wintty.c +# End Source File +# Begin Source File + +SOURCE=..\src\wizard.c +# End Source File +# Begin Source File + +SOURCE=..\src\worm.c +# End Source File +# Begin Source File + +SOURCE=..\src\worn.c +# End Source File +# Begin Source File + +SOURCE=..\src\write.c +# End Source File +# Begin Source File + +SOURCE=..\src\zap.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\amiconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\artifact.h +# End Source File +# Begin Source File + +SOURCE=..\include\artilist.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\beconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\bitmfile.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\def_os2.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dlb.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\edog.h +# End Source File +# Begin Source File + +SOURCE=..\include\emin.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\epri.h +# End Source File +# Begin Source File + +SOURCE=..\include\eshk.h +# End Source File +# Begin Source File + +SOURCE=..\include\extern.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\func_tab.h +# End Source File +# Begin Source File + +SOURCE=..\include\gem_rsc.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\hack.h +# End Source File +# Begin Source File + +SOURCE=..\include\lev.h +# End Source File +# Begin Source File + +SOURCE=..\include\load_img.h +# End Source File +# Begin Source File + +SOURCE=..\include\macconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\macpopup.h +# End Source File +# Begin Source File + +SOURCE=..\include\mactty.h +# End Source File +# Begin Source File + +SOURCE=..\include\macwin.h +# End Source File +# Begin Source File + +SOURCE=..\include\mail.h +# End Source File +# Begin Source File + +SOURCE=..\include\mfndpos.h +# End Source File +# Begin Source File + +SOURCE=..\include\micro.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\mondata.h +# End Source File +# Begin Source File + +SOURCE=..\include\monflag.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\mttypriv.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\os2conf.h +# End Source File +# Begin Source File + +SOURCE=..\include\patchlevel.h +# End Source File +# Begin Source File + +SOURCE=..\include\pcconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_clust.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_kde0.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_win.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_xpms.h +# End Source File +# Begin Source File + +SOURCE=..\include\qtext.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\sp_lev.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\system.h +# End Source File +# Begin Source File + +SOURCE=..\include\tcap.h +# End Source File +# Begin Source File + +SOURCE=..\include\tile2x11.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tosconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\unixconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\vault.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\vmsconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\winami.h +# End Source File +# Begin Source File + +SOURCE=..\include\wingem.h +# End Source File +# Begin Source File + +SOURCE=..\include\winGnome.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\winX.h +# End Source File +# Begin Source File + +SOURCE=..\include\xwindow.h +# End Source File +# Begin Source File + +SOURCE=..\include\xwindowp.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\win\win32\bitmap1.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\bitmap2.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mnsel.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mnunsel.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\NETHACK.ICO +# End Source File +# Begin Source File + +SOURCE=..\win\win32\small.ico +# End Source File +# Begin Source File + +SOURCE=..\win\win32\tiles.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.ico +# End Source File +# End Group +# Begin Group "wnd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\win\win32\mhaskyn.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhaskyn.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhdlg.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhdlg.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhfont.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhfont.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhinput.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhinput.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmain.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmain.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmap.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmap.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmenu.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmenu.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsg.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsgwnd.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsgwnd.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhsplash.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhrip.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhsplash.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhrip.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhstatus.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhstatus.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhtext.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhtext.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mswproc.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\resource.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.rc +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winMS.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\win\win32\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/build/recover.dsp b/build/recover.dsp new file mode 100644 index 0000000..7052afa --- /dev/null +++ b/build/recover.dsp @@ -0,0 +1,148 @@ +# Microsoft Developer Studio Project File - Name="recover" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=recover - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "recover.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "recover.mak" CFG="recover - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "recover - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "recover - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "recover - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Release +SOURCE="$(InputPath)" +PostBuild_Cmds=copy $(OutDir)\recover.exe ..\binary \ +if exist ..\doc\recover.txt copy ..\doc\recover.txt ..\binary\recover.txt +# End Special Build Tool + +!ELSEIF "$(CFG)" == "recover - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Debug +SOURCE="$(InputPath)" +PostBuild_Desc=install exe +PostBuild_Cmds=copy $(OutDir)\recover.exe ..\binary \ +if exist ..\doc\recover.txt copy ..\doc\recover.txt ..\binary\recover.txt +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "recover - Win32 Release" +# Name "recover - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\util\recover.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\win32api.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/tile2bmp.dsp b/build/tile2bmp.dsp new file mode 100644 index 0000000..5a534e2 --- /dev/null +++ b/build/tile2bmp.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="tile2bmp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tile2bmp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tile2bmp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tile2bmp.mak" CFG="tile2bmp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tile2bmp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tile2bmp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\tile2bmp.exe" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\util\tile2bmp.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "tile2bmp - Win32 Release" +# Name "tile2bmp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\win\share\tile2bmp.c + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# ADD CPP /D "PACKED_FILE" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\win\share\tiletext.c + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# ADD CPP /Zi + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\win\share\tiletxt.c +# ADD CPP /D "TILETEXT" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/tilemap.dsp b/build/tilemap.dsp new file mode 100644 index 0000000..37d71ef --- /dev/null +++ b/build/tilemap.dsp @@ -0,0 +1,281 @@ +# Microsoft Developer Studio Project File - Name="tilemap" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tilemap - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tilemap.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tilemap.mak" CFG="tilemap - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tilemap - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tilemap - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tilemap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt\include" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\tilemap.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Generating src\tile.c +PostBuild_Cmds=echo chdir ..\src chdir ..\src ..\util\tilemap.exe echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "tilemap - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt\include" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\tilemap.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Generating src\tile.c +PostBuild_Cmds=echo chdir ..\src chdir ..\src ..\util\tilemap.exe echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "tilemap - Win32 Release" +# Name "tilemap - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\win\share\tilemap.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_comp.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\onames.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\pm.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/tiles.dsp b/build/tiles.dsp new file mode 100644 index 0000000..ab2207a --- /dev/null +++ b/build/tiles.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="tiles" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=tiles - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tiles.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tiles.mak" CFG="tiles - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tiles - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "tiles - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "tiles - Win32 Release" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f tiles.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "tiles.exe" +# PROP BASE Bsc_Name "tiles.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "tiles.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\win\win32\tiles.bmp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "tiles - Win32 Debug" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f tiles.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "tiles.exe" +# PROP BASE Bsc_Name "tiles.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake /f "tiles.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\win\win32\tiles.bmp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "tiles - Win32 Release" +# Name "tiles - Win32 Debug" + +!IF "$(CFG)" == "tiles - Win32 Release" + +!ELSEIF "$(CFG)" == "tiles - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/build/tiles.mak b/build/tiles.mak new file mode 100644 index 0000000..26af1ea --- /dev/null +++ b/build/tiles.mak @@ -0,0 +1,21 @@ +default: all + +all: ..\win\win32\tiles.bmp + +clean: + -del ..\src\win\win32\tiles.bmp + -del ..\win\win32\tiles.bmp + +#========================================== +# Building the tiles file tile.bmp +#========================================== + +..\src\tiles.bmp : ..\win\share\monsters.txt ..\win\share\objects.txt \ + ..\win\share\other.txt + chdir ..\src + ..\util\tile2bmp.exe tiles.bmp + chdir ..\build + +..\win\win32\tiles.bmp: ..\src\tiles.bmp + @copy ..\src\tiles.bmp ..\win\win32\tiles.bmp + diff --git a/build/uudecode.dsp b/build/uudecode.dsp new file mode 100644 index 0000000..a9cf1be --- /dev/null +++ b/build/uudecode.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="uudecode" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=uudecode - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "uudecode.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "uudecode.mak" CFG="uudecode - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "uudecode - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "uudecode - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "uudecode - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\uudecode.exe" +# SUBTRACT LINK32 /nodefaultlib +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=echo chdir ..\win\win32 chdir ..\win\win32 \ +echo decoding icon (nhico.uu to NetHack.ico) \ +..\..\util\uudecode.exe ../../sys/winnt/nhico.uu \ +echo decoding mnsel (mnsel.uu to mnsel.bmp) \ +..\..\util\uudecode.exe mnsel.uu \ +echo decoding mnselcnt (mnselcnt.uu to mnselcnt.bmp) \ +..\..\util\uudecode.exe mnselcnt.uu \ +echo decoding mnunsel (mnunsel.uu to mnunsel.bmp) \ +..\..\util\uudecode.exe mnunsel.uu \ +echo decoding petmark (petmark.uu to petmark.bmp) \ +..\..\util\uudecode.exe petmark.uu \ +echo decoding splash (splash.uu to splash.bmp) \ +..\..\util\uudecode.exe splash.uu \ +echo decoding tombstone (rip.uu to rip.bmp) \ +..\..\util\uudecode.exe rip.uu \ +chdir ..\..\binary + +# End Special Build Tool + +!ELSEIF "$(CFG)" == "uudecode - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\util\uudecode.exe" /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=echo chdir ..\win\win32 chdir ..\win\win32 \ +echo decoding icon (nhico.uu to NetHack.ico) \ +..\..\util\uudecode.exe ../../sys/winnt/nhico.uu \ +echo decoding mnsel (mnsel.uu to mnsel.bmp) \ +..\..\util\uudecode.exe mnsel.uu \ +echo decoding mnselcnt (mnselcnt.uu to mnselcnt.bmp) \ +..\..\util\uudecode.exe mnselcnt.uu \ +echo decoding mnunsel (mnunsel.uu to mnunsel.bmp) \ +..\..\util\uudecode.exe mnunsel.uu \ +echo decoding petmark (petmark.uu to petmark.bmp) \ +..\..\util\uudecode.exe petmark.uu \ +echo decoding splash (splash.uu to splash.bmp) \ +..\..\util\uudecode.exe splash.uu \ +echo decoding tombstone (rip.uu to rip.bmp) \ +..\..\util\uudecode.exe rip.uu \ +chdir ..\..\binary + +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "uudecode - Win32 Release" +# Name "uudecode - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\sys\share\uudecode.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/dat/Arch.des b/dat/Arch.des new file mode 100644 index 0000000..414cf50 --- /dev/null +++ b/dat/Arch.des @@ -0,0 +1,460 @@ +# SCCS Id: @(#)Arch.des 3.4 1997/01/31 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Lord Carnarvon +# and receive your quest assignment. +# +MAZE: "Arc-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +............................................................................ +............................................................................ +............................................................................ +............................................................................ +....................}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}................. +....................}-------------------------------------}................. +....................}|..S......+.................+.......|}................. +....................}-S---------------+----------|.......|}................. +....................}|.|...............|.......+.|.......|}................. +....................}|.|...............---------.---------}................. +....................}|.S.\.............+.................+.................. +....................}|.|...............---------.---------}................. +....................}|.|...............|.......+.|.......|}................. +....................}-S---------------+----------|.......|}................. +....................}|..S......+.................+.......|}................. +....................}-------------------------------------}................. +....................}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}................. +............................................................................ +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION: (22,06,23,06),unlit,"ordinary" +REGION: (25,06,30,06),unlit,"ordinary" +REGION: (32,06,48,06),unlit,"ordinary" +REGION: (50,06,56,08),lit,"ordinary" +REGION: (40,08,46,08),unlit,"ordinary" +REGION: (22,08,22,12),unlit,"ordinary" +REGION: (24,08,38,12),unlit,"ordinary" +REGION: (48,08,48,08),lit,"ordinary" +REGION: (40,10,56,10),lit,"ordinary" +REGION: (48,12,48,12),lit,"ordinary" +REGION: (40,12,46,12),unlit,"ordinary" +REGION: (50,12,56,14),lit,"ordinary" +REGION: (22,14,23,14),unlit,"ordinary" +REGION: (25,14,30,14),unlit,"ordinary" +REGION: (32,14,48,14),unlit,"ordinary" +# Stairs +STAIR:(55,07),down +# Portal arrival point +BRANCH:(63,06,63,06),(0,0,0,0) +# Doors +DOOR:closed,(22,07) +DOOR:closed,(38,07) +DOOR:locked,(47,08) +DOOR:locked,(23,10) +DOOR:locked,(39,10) +DOOR:locked,(57,10) +DOOR:locked,(47,12) +DOOR:closed,(22,13) +DOOR:closed,(38,13) +DOOR:locked,(24,14) +DOOR:closed,(31,14) +DOOR:locked,(49,14) +# Lord Carnarvon +MONSTER:'@',"Lord Carnarvon",(25,10) +# The treasure of Lord Carnarvon +OBJECT:'(',"chest",(25,10) +# student guards for the audience chamber +MONSTER:'@',"student",(26,09) +MONSTER:'@',"student",(27,09) +MONSTER:'@',"student",(28,09) +MONSTER:'@',"student",(26,10) +MONSTER:'@',"student",(28,10) +MONSTER:'@',"student",(26,11) +MONSTER:'@',"student",(27,11) +MONSTER:'@',"student",(28,11) +# city watch guards in the antechambers +MONSTER:'@',"watchman",(50,06) +MONSTER:'@',"watchman",(50,14) +# Eels in the moat +MONSTER:';',"giant eel",(20,10) +MONSTER:';',"giant eel",(45,04) +MONSTER:';',"giant eel",(33,16) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'S',random,(60,09) +MONSTER: 'M',random,(60,10) +MONSTER: 'S',random,(60,11) +MONSTER: 'S',random,(60,12) +MONSTER: 'M',random,(60,13) +MONSTER: 'S',random,(61,10) +MONSTER: 'S',random,(61,11) +MONSTER: 'S',random,(61,12) +MONSTER: 'S',random,(30,03) +MONSTER: 'M',random,(20,17) +MONSTER: 'S',random,(67,02) +MONSTER: 'S',random,(10,19) + +# +# The "locate" level for the quest. +# +# Here you have to find the Entrance to the Tomb of the Toltec Kings +# to go further towards your assigned quest. +# + +MAZE: "Arc-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP +............................................................................ +............................................................................ +............................................................................ +........................-------------------------------..................... +........................|....|.S......................|..................... +........................|....|.|.|+------------------.|..................... +........................|....|.|.|.|.........|......|.|..................... +........................|....|.|.|.|.........|......|.|..................... +........................|---+-.|.|.|..---....+......|.|..................... +........................|....|.|.|.---|.|....|......|.|..................... +........................|....S.|.|.+..S.|--S-----S--|.|..................... +........................|....|.|.|.---|.|....|......+.|..................... +........................|---+-.|.|.|..---....|.------.|..................... +........................|....|.|.|.|.........|.|....+.|..................... +........................|....|.|.|.|.........|+|....|-|..................... +........................|....|.|.|------------+------.S..................... +........................|....|.S......................|..................... +........................-------------------------------..................... +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(25,04,28,07),lit,"temple" +REGION:(25,09,28,11),unlit,"temple" +REGION:(25,13,28,16),lit,"temple" +REGION:(30,04,30,16),lit,"ordinary" +REGION:(32,04,32,16),unlit,"ordinary" +REGION:(33,04,53,04),unlit,"ordinary",unfilled,true +REGION:(36,10,37,10),unlit,"ordinary" +REGION:(39,09,39,11),unlit,"ordinary" +REGION:(36,06,42,08),unlit,"ordinary",unfilled,true +REGION:(36,12,42,14),unlit,"ordinary",unfilled,true +REGION:(46,06,51,09),unlit,"ordinary" +REGION:(46,11,49,11),unlit,"ordinary",unfilled,true +REGION:(48,13,51,14),unlit,"ordinary" +# Doors +DOOR:closed,(31,04) +DOOR:closed,(28,08) +DOOR:locked,(29,10) +DOOR:closed,(28,12) +DOOR:closed,(31,16) +DOOR:locked,(34,05) +DOOR:locked,(35,10) +DOOR:locked,(38,10) +DOOR:closed,(43,10) +DOOR:closed,(45,08) +DOOR:locked,(46,14) +DOOR:locked,(46,15) +DOOR:locked,(49,10) +DOOR:locked,(52,11) +DOOR:closed,(52,13) +DOOR:closed,(54,15) +# Stairs +STAIR:(03,17),up +STAIR:(39,10),down +# Altars - three types. All are unattended. +ALTAR:(26,05),align[0],altar +ALTAR:(26,10),align[1],altar +ALTAR:(26,15),align[2],altar +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Treasure? +ENGRAVING:random,engrave,"X marks the spot." +ENGRAVING:random,engrave,"X marks the spot." +ENGRAVING:random,engrave,"X marks the spot." +ENGRAVING:random,engrave,"X marks the spot." +# Random traps +TRAP:"spiked pit",(24,02) +TRAP:"spiked pit",(37,00) +TRAP:"spiked pit",(23,05) +TRAP:"spiked pit",(26,19) +TRAP:"spiked pit",(55,10) +TRAP:"spiked pit",(55,08) +TRAP:"pit",(51,01) +TRAP:"pit",(23,18) +TRAP:"pit",(31,18) +TRAP:"pit",(48,19) +TRAP:"pit",(55,15) +TRAP:"magic",(60,04) +TRAP:"statue",(72,07) +TRAP:"statue",random +TRAP:"statue",random +TRAP:"anti magic",(64,12) +TRAP:"sleep gas",random +TRAP:"sleep gas",random +TRAP:"dart",random +TRAP:"dart",random +TRAP:"dart",random +TRAP:"rolling boulder",(32,10) +TRAP:"rolling boulder",(40,16) +# Random monsters. +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'M',random,random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',random,random + +# +# The "goal" level for the quest. +# +# Here you meet Minion of Huhetotl your nemesis monster. You have to +# defeat Minion of Huhetotl in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Arc-goal", ' ' +GEOMETRY:center,center +MAP + + --------- + |..|.|..| + -----------|..S.S..|----------- + |.|........|+-|.|-+|........|.| + |.S........S..|.|..S........S.| + |.|........|..|.|..|........|.| + ------------------+------------------ + |..|..........|.......|..........|..| + |..|..........+.......|..........S..| + |..S..........|.......+..........|..| + |..|..........|.......|..........|..| + ------------------+------------------ + |.|........|..|.|..|........|.| + |.S........S..|.|..S........S.| + |.|........|+-|.|-+|........|.| + -----------|..S.S..|----------- + |..|.|..| + --------- + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(35,02,36,03),unlit,"ordinary" +REGION:(40,02,41,03),unlit,"ordinary" +REGION:(24,04,24,06),unlit,"ordinary" +REGION:(26,04,33,06),lit,"ordinary" +REGION:(38,02,38,06),unlit,"ordinary" +REGION:(43,04,50,06),lit,"ordinary" +REGION:(52,04,52,06),unlit,"ordinary" +REGION:(35,05,36,06),unlit,"ordinary" +REGION:(40,05,41,06),unlit,"ordinary" +REGION:(21,08,22,11),unlit,"ordinary" +REGION:(24,08,33,11),lit,"ordinary" +REGION:(35,08,41,11),unlit,"ordinary" +REGION:(43,08,52,11),lit,"ordinary" +REGION:(54,08,55,11),unlit,"ordinary" +REGION:(24,13,24,15),unlit,"ordinary" +REGION:(26,13,33,15),unlit,"ordinary" +REGION:(35,13,36,14),unlit,"ordinary" +REGION:(35,16,36,17),unlit,"ordinary" +REGION:(38,13,38,17),unlit,"ordinary" +REGION:(40,13,41,14),unlit,"ordinary" +REGION:(40,16,41,17),unlit,"ordinary" +REGION:(43,13,50,15),unlit,"temple" +REGION:(52,13,52,15),unlit,"ordinary" +# Stairs +STAIR:(38,10),up +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# The altar of Huhetotl. Unattended. +ALTAR:(50,14),chaos,altar +# Objects +OBJECT:'(',"crystal ball",(50,14),blessed,5,"The Orb of Detection" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:"rolling boulder",(46,14) +# Random monsters. +MONSTER:'&',"Minion of Huhetotl",(50,14) +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'S',random,random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',"human mummy",random +MONSTER:'M',random,random + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +LEVEL: "Arc-fila" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'S', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'S', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'S', random, random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random +MONSTER: 'M', "human mummy", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random + +RANDOM_CORRIDORS + +LEVEL: "Arc-filb" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'M', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'M', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'M', random, random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random +MONSTER: 'M', "human mummy", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'S', random, random + +RANDOM_CORRIDORS diff --git a/dat/Barb.des b/dat/Barb.des new file mode 100644 index 0000000..c746295 --- /dev/null +++ b/dat/Barb.des @@ -0,0 +1,368 @@ +# SCCS Id: @(#)Barb.des 3.4 1991/12/22 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Pelias, +# and receive your quest assignment. +# +MAZE: "Bar-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +..................................PP........................................ +...................................PP....................................... +...................................PP....................................... +....................................PP...................................... +........--------------......-----....PPP.................................... +........|...S........|......+...|...PPP..................................... +........|----........|......|...|....PP..................................... +........|.\..........+......-----........................................... +........|----........|...............PP..................................... +........|...S........|...-----.......PPP.................................... +........--------------...+...|......PPPPP................................... +.........................|...|.......PPP.................................... +...-----......-----......-----........PP.................................... +...|...+......|...+..--+--.............PP................................... +...|...|......|...|..|...|..............PP.................................. +...-----......-----..|...|.............PPPP................................. +.....................-----............PP..PP................................ +.....................................PP...PP................................ +....................................PP...PP................................. +....................................PP....PP................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(09,05,11,05),unlit,"ordinary" +REGION:(09,07,11,07),lit,"ordinary" +REGION:(09,09,11,09),unlit,"ordinary" +REGION:(13,05,20,09),lit,"ordinary" +REGION:(29,05,31,06),lit,"ordinary" +REGION:(26,10,28,11),lit,"ordinary" +REGION:(04,13,06,14),lit,"ordinary" +REGION:(15,13,17,14),lit,"ordinary" +REGION:(22,14,24,15),lit,"ordinary" +# Stairs +STAIR:(09,09),down +# Portal arrival point +BRANCH:(62,02,62,02),(0,0,0,0) +# Doors +DOOR:locked,(12,05) +DOOR:locked,(12,09) +DOOR:closed,(21,07) +DOOR:open,(07,13) +DOOR:open,(18,13) +DOOR:open,(23,13) +DOOR:open,(25,10) +DOOR:open,(28,05) +# Elder +MONSTER:'@',"Pelias",(10,07) +# The treasure of Pelias +OBJECT:'(',"chest",(09,05) +# chieftain guards for the audience chamber +MONSTER:'@',"chieftain",(10,05) +MONSTER:'@',"chieftain",(10,09) +MONSTER:'@',"chieftain",(11,05) +MONSTER:'@',"chieftain",(11,09) +MONSTER:'@',"chieftain",(14,05) +MONSTER:'@',"chieftain",(14,09) +MONSTER:'@',"chieftain",(16,05) +MONSTER:'@',"chieftain",(16,09) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# One trap to keep the ogres at bay. +TRAP:"spiked pit",(37,07) +# Eels in the river +MONSTER:';',"giant eel",(36,01) +MONSTER:';',"giant eel",(37,09) +MONSTER:';',"giant eel",(39,15) +# Monsters on siege duty. +MONSTER:'O',"ogre",(40,08),hostile +MONSTER:'O',"ogre",(41,06),hostile +MONSTER:'O',"ogre",(41,07),hostile +MONSTER:'O',"ogre",(41,08),hostile +MONSTER:'O',"ogre",(41,09),hostile +MONSTER:'O',"ogre",(41,10),hostile +MONSTER:'O',"ogre",(42,06),hostile +MONSTER:'O',"ogre",(42,07),hostile +MONSTER:'O',"ogre",(42,08),hostile +MONSTER:'O',"ogre",(42,09),hostile +MONSTER:'O',"ogre",(42,10),hostile + +# +# The "locate" level for the quest. +# +# Here you have to infiltrate the Duali Oasis to go +# further towards your assigned quest. +# + +MAZE: "Bar-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP +..........PPP......................................... +...........PP.......................................... ....... +..........PP...........-----..........------------------ .......... +...........PP..........+...|..........|....S...........|.. ............ +..........PPP..........|...|..........|-----...........|... ............. +...........PPP.........-----..........+....+...........|... ............. +..........PPPPPPPPP...................+....+...........S................. +........PPPPPPPPPPPPP.........-----...|-----...........|................ +......PPPPPPPPPPPPPP..P.......+...|...|....S...........| ... +.....PPPPPPP......P..PPPP.....|...|...------------------.. ... +....PPPPPPP.........PPPPPP....-----........................ ........ +...PPPPPPP..........PPPPPPP.................................. .......... +....PPPPPPP........PPPPPPP.................................... .......... +.....PPPPP........PPPPPPP.........-----........................ ........ +......PPP..PPPPPPPPPPPP...........+...|......................... ..... +..........PPPPPPPPPPP.............|...|......................... .... +..........PPPPPPPPP...............-----......................... . +..............PPP................................................. +...............PP.................................................... +................PPP................................................... +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(24,03,26,04),unlit,"ordinary" +REGION:(31,08,33,09),unlit,"ordinary" +REGION:(35,14,37,15),unlit,"ordinary" +REGION:(39,03,54,08),lit,"ordinary" +REGION:(56,00,75,08),unlit,"ordinary" +REGION:(64,09,75,16),unlit,"ordinary" +# Doors +DOOR:open,(23,03) +DOOR:open,(30,08) +DOOR:open,(34,14) +DOOR:locked,(38,05) +DOOR:locked,(38,06) +DOOR:closed,(43,03) +DOOR:closed,(43,05) +DOOR:closed,(43,06) +DOOR:closed,(43,08) +DOOR:locked,(55,06) +# Stairs +STAIR:(05,02),up +STAIR:(70,13),down +# Objects +OBJECT:random,random,(42,03) +OBJECT:random,random,(42,03) +OBJECT:random,random,(42,03) +OBJECT:random,random,(41,03) +OBJECT:random,random,(41,03) +OBJECT:random,random,(41,03) +OBJECT:random,random,(41,03) +OBJECT:random,random,(41,08) +OBJECT:random,random,(41,08) +OBJECT:random,random,(42,08) +OBJECT:random,random,(42,08) +OBJECT:random,random,(42,08) +OBJECT:random,random,(71,13) +OBJECT:random,random,(71,13) +OBJECT:random,random,(71,13) +# Random traps +TRAP:"spiked pit",(10,13) +TRAP:"spiked pit",(21,07) +TRAP:"spiked pit",(67,08) +TRAP:"spiked pit",(68,09) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'O',"ogre",(12,09),hostile +MONSTER:'O',"ogre",(18,11),hostile +MONSTER:'O',"ogre",(45,05),hostile +MONSTER:'O',"ogre",(45,06),hostile +MONSTER:'O',"ogre",(47,05),hostile +MONSTER:'O',"ogre",(46,05),hostile +MONSTER:'O',"ogre",(56,03),hostile +MONSTER:'O',"ogre",(56,04),hostile +MONSTER:'O',"ogre",(56,05),hostile +MONSTER:'O',"ogre",(56,06),hostile +MONSTER:'O',"ogre",(57,03),hostile +MONSTER:'O',"ogre",(57,04),hostile +MONSTER:'O',"ogre",(57,05),hostile +MONSTER:'O',"ogre",(57,06),hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',random,random,hostile +MONSTER:'T',random,random,hostile +MONSTER:'T',"rock troll",(46,06),hostile +MONSTER:'T',"rock troll",(47,06),hostile +MONSTER:'T',"rock troll",(56,07),hostile +MONSTER:'T',"rock troll",(57,07),hostile +MONSTER:'T',"rock troll",(70,13),hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',random,random,hostile + +# +# The "goal" level for the quest. +# +# Here you meet Thoth Amon, your nemesis monster. You have to +# defeat Thoth Amon in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Bar-goal", ' ' +GEOMETRY:center,center +MAP + + ............. + .................. + .... ......................... .... + ....... .......................... ....... + ...... ........................ ....... + .. ...................................... .. + .. ..................... .. + .. .................. .. + .. ..S...S.............. ................ + .. ........ ... + ......... .. + ...... .. ... .... + .. ... .. ...... ........ + .... .. .................. ........ ...... + ...... ...................... ...... .. + .... .................. ........... + .............. + ........... + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),unlit,"ordinary" +# Secret doors +DOOR:locked,(22,09) +DOOR:locked,(26,09) +# Stairs +STAIR:(36,05),up +# The altar. Unattended. +ALTAR:(63,04),noncoaligned,altar +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:'*',"luckstone",(63,04),blessed,0,"The Heart of Ahriman" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Thoth Amon",(63,04),hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',"ogre",random,hostile +MONSTER:'O',random,random,hostile +MONSTER:'O',random,random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',"rock troll",random,hostile +MONSTER:'T',random,random,hostile +WALLIFY + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Bar-fila" , ' ' +INIT_MAP: '.' , '.' , true , true , unlit , false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', random, random, hostile +MONSTER: 'T', "rock troll", random, hostile + +MAZE: "Bar-filb" , ' ' +INIT_MAP: '.' , ' ' , true , true , unlit , true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', "ogre", random, hostile +MONSTER: 'O', random , random, hostile +MONSTER: 'T', "rock troll", random, hostile +MONSTER: 'T', "rock troll", random, hostile +MONSTER: 'T', "rock troll", random, hostile +MONSTER: 'T', random , random, hostile diff --git a/dat/Caveman.des b/dat/Caveman.des new file mode 100644 index 0000000..3f408fe --- /dev/null +++ b/dat/Caveman.des @@ -0,0 +1,316 @@ +# SCCS Id: @(#)Caveman.des 3.4 1995/10/07 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Shaman Karnov +# and receive your quest assignment. +# +MAZE: "Cav-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP + + ...... .......................... ... .... ...... + ...... .......................... ........ .... ..... + ..BB ............................. ......... .... .. + .. ...................... ....... .. .... .. + .. .................... .. ....... .. ... + .. S BB ..... ....... .... .... + .. ... . .. ........ .. .. .. ... + .. ...... .. ............ .. ... + . .... .. ........ .. ........... + ... .. .. ............. ................... + ..... ..... ............................... ........... + .....B................ ... ... + ..... . .......... .... . ... .......... ... + ... .. ............. .. ................... .... + BB .. ......... BB ... .......... .. ... ... + ...... ..... B ........ .. .. .... ... + .......... .......... ..... ... ..... ........ + .. ... . ..... .... .. ... .. + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),unlit,"ordinary" +REGION:(13,01,40,05),lit,"temple",unfilled,true +# The occupied rooms. +REGION:(02,01,08,03),lit,"ordinary",unfilled,true +REGION:(01,11,06,14),lit,"ordinary",unfilled,true +REGION:(13,08,18,10),lit,"ordinary",unfilled,true +REGION:(05,17,14,18),lit,"ordinary",unfilled,true +REGION:(17,16,23,18),lit,"ordinary",unfilled,true +REGION:(35,16,44,18),lit,"ordinary",unfilled,true +# Stairs +STAIR:(02,03),down +# Portal arrival point +BRANCH:(71,09,71,09),(0,0,0,0) +# Doors +DOOR:locked,(19,06) +# The temple altar (this will force a priest(ess) to be created) +ALTAR:(36,02),coaligned,shrine +# Shaman Karnov +MONSTER:'@',"Shaman Karnov",(35,02) +# The treasure of Shaman Karnov +OBJECT:'(',"chest",(34,02) +# neanderthal guards for the audience chamber +MONSTER:'@',"neanderthal",(20,03) +MONSTER:'@',"neanderthal",(20,02) +MONSTER:'@',"neanderthal",(20,01) +MONSTER:'@',"neanderthal",(21,03) +MONSTER:'@',"neanderthal",(21,02) +MONSTER:'@',"neanderthal",(21,01) +MONSTER:'@',"neanderthal",(22,01) +MONSTER:'@',"neanderthal",(26,09) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:"pit",(47,11) +TRAP:"pit",(57,10) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty (in the outer caves). +MONSTER: 'h',"bugbear",(47,02),hostile +MONSTER: 'h',"bugbear",(48,03),hostile +MONSTER: 'h',"bugbear",(49,04),hostile +MONSTER: 'h',"bugbear",(67,03),hostile +MONSTER: 'h',"bugbear",(69,04),hostile +MONSTER: 'h',"bugbear",(51,13),hostile +MONSTER: 'h',"bugbear",(53,14),hostile +MONSTER: 'h',"bugbear",(55,15),hostile +MONSTER: 'h',"bugbear",(63,10),hostile +MONSTER: 'h',"bugbear",(65,09),hostile +MONSTER: 'h',"bugbear",(67,10),hostile +MONSTER: 'h',"bugbear",(69,11),hostile +WALLIFY + +# +# The "locate" level for the quest. +# +# Here you have to find the lair of Tiamat to go +# further towards your assigned quest. +# + +MAZE: "Cav-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP + + ............. ........... + ............... ............. + ............. ............... .......... + ........... ............. ............... + ... ... .................. + ... .......... ... .................. + ... ............ BBB................... + ... .......... ...................... + ..... .. .....B........................ + .... ............... . ........B.......................... + ...... .. .............S.............. .................. + .... .. ........... ............... + .. ... .................... + .... BB................... + .. .. .. ............... + .. ....... .... ..... .... .. ....... S + ............ ....... .. ....... ..... ... .... + ....... ..... ...... ....... + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),unlit,"ordinary" +REGION:(52,06,73,15),lit,"ordinary",unfilled,true +# Doors +DOOR:locked,(28,11) +# Stairs +STAIR:(04,03),up +STAIR:(73,10),down +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'h',"bugbear",(02,10),hostile +MONSTER:'h',"bugbear",(03,11),hostile +MONSTER:'h',"bugbear",(04,12),hostile +MONSTER:'h',"bugbear",(02,11),hostile +MONSTER:'h',"bugbear",(16,16),hostile +MONSTER:'h',"bugbear",(17,17),hostile +MONSTER:'h',"bugbear",(18,18),hostile +MONSTER:'h',"bugbear",(19,16),hostile +MONSTER:'h',"bugbear",(30,06),hostile +MONSTER:'h',"bugbear",(31,07),hostile +MONSTER:'h',"bugbear",(32,08),hostile +MONSTER:'h',"bugbear",(33,06),hostile +MONSTER:'h',"bugbear",(34,07),hostile +MONSTER:'h',"bugbear",random,hostile +MONSTER:'h',"bugbear",random,hostile +MONSTER:'h',"bugbear",random,hostile +MONSTER:'h',"bugbear",random,hostile +MONSTER:'h',random,random,hostile +MONSTER:'H',random,random,hostile +MONSTER:'H',"hill giant",(03,12),hostile +MONSTER:'H',"hill giant",(20,17),hostile +MONSTER:'H',"hill giant",(35,08),hostile +MONSTER:'H',"hill giant",random,hostile +MONSTER:'H',"hill giant",random,hostile +MONSTER:'H',"hill giant",random,hostile +MONSTER:'H',"hill giant",random,hostile +MONSTER:'H',random,random,hostile +WALLIFY + +# +# The "goal" level for the quest. +# +# Here you meet Tiamat your nemesis monster. You have to +# defeat Tiamat in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Cav-goal", ' ' +GEOMETRY:center,center +MAP + + ..................... + ....................... + ......................... + ........................... + ............................. + ............................... + ................................. + ................................... + ..................................... + ....................................... + ..................................... + ................................... + ................................. + ............................... + ............................. + ........................... + ......................... + ....................... + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +# Stairs +STAIR:random,up +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:')',"mace",(23,10),blessed,0,"The Sceptre of Might" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# monsters. +MONSTER:'D',"Chromatic Dragon",(23,10),asleep +MONSTER:'F',"shrieker",(26,13) +MONSTER:'F',"shrieker",(25,8) +MONSTER:'F',"shrieker",(45,11) +WALLIFY + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Cav-fila" , ' ' +INIT_MAP: '.' , ' ' , true , true , random , true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', random, random, hostile +MONSTER: 'H', "hill giant", random, hostile + +MAZE: "Cav-filb" , ' ' +INIT_MAP: '.' , ' ' , true , true , random , true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', "bugbear", random, hostile +MONSTER: 'h', random, random, hostile +MONSTER: 'h', random, random, hostile +MONSTER: 'H', "hill giant", random, hostile +MONSTER: 'H', "hill giant", random, hostile diff --git a/dat/Healer.des b/dat/Healer.des new file mode 100644 index 0000000..9142ba7 --- /dev/null +++ b/dat/Healer.des @@ -0,0 +1,377 @@ +# SCCS Id: @(#)Healer.des 3.4 1995/04/16 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991, 1993 by M. Stephenson, P. Winner +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Hippocrates +# and receive your quest assignment. +# +MAZE: "Hea-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP +PPPP........PPPP.....PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP.P..PPPPP......PPPPPPPP +PPP..........PPPP...PPPPP.........................PPPP..PPPPP........PPPPPPP +PP............PPPPPPPP..............................PPP...PPPP......PPPPPPPP +P.....PPPPPPPPPPPPPPP................................PPPPPPPPPPPPPPPPPPPPPPP +PPPP....PPPPPPPPPPPP...................................PPPPP.PPPPPPPPPPPPPPP +PPPP........PPPPP.........-----------------------........PP...PPPPPPP.....PP +PPP............PPPPP....--|.|......S..........S.|--.....PPPP.PPPPPPP.......P +PPPP..........PPPPP.....|.S.|......-----------|S|.|......PPPPPP.PPP.......PP +PPPPPP......PPPPPP......|.|.|......|...|......|.|.|.....PPPPPP...PP.......PP +PPPPPPPPPPPPPPPPPPP.....+.|.|......S.\.S......|.|.+......PPPPPP.PPPP.......P +PPP...PPPPP...PPPP......|.|.|......|...|......|.|.|.......PPPPPPPPPPP.....PP +PP.....PPP.....PPP......|.|S|-----------......|.S.|......PPPPPPPPPPPPPPPPPPP +PPP..PPPPP...PPPP.......--|.S..........S......|.|--.....PPPPPPPPP....PPPPPPP +PPPPPPPPPPPPPPPP..........-----------------------..........PPPPP..........PP +PPPPPPPPPPPPPPPPP........................................PPPPPP............P +PPP.............PPPP...................................PPP..PPPP..........PP +PP...............PPPPP................................PPPP...PPPP........PPP +PPP.............PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP....PPPPPP +PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +# Stairs +STAIR:(37,9),down +# Portal arrival point +BRANCH:(04,12,04,12),(0,0,0,0) +# altar for the Temple +ALTAR:(32,09),neutral,altar +# Doors +DOOR:locked,(24,10) +DOOR:closed,(26,08) +DOOR:closed,(27,12) +DOOR:locked,(28,13) +DOOR:closed,(35,07) +DOOR:locked,(35,10) +DOOR:locked,(39,10) +DOOR:closed,(39,13) +DOOR:locked,(46,07) +DOOR:closed,(47,08) +DOOR:closed,(48,12) +DOOR:locked,(50,10) +# Hippocrates +MONSTER:'@',"Hippocrates",(37,10) +# The treasure of Hippocrates +OBJECT:'(',"chest",(37,10) +# intern guards for the audience chamber +MONSTER:'@',"attendant",(29,08) +MONSTER:'@',"attendant",(29,09) +MONSTER:'@',"attendant",(29,10) +MONSTER:'@',"attendant",(29,11) +MONSTER:'@',"attendant",(40,09) +MONSTER:'@',"attendant",(40,10) +MONSTER:'@',"attendant",(40,11) +MONSTER:'@',"attendant",(40,13) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: 'r',"rabid rat",random +MONSTER: ';',"giant eel",random +MONSTER: ';',"shark",random +MONSTER: ';', random, random +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +# +# The "locate" level for the quest. +# +# Here you have to find the Temple of Coeus to go +# further towards your assigned quest. +# + +MAZE: "Hea-loca",' ' +FLAGS: hardfloor +# +INIT_MAP: '.' , 'P', true , true , lit , false +GEOMETRY:center,center +MAP +PPPPPPPPPPPPP.......PPPPPPPPPPP +PPPPPPPP...............PPPPPPPP +PPPP.....-------------...PPPPPP +PPPPP....|.S.........|....PPPPP +PPP......+.|.........|...PPPPPP +PPP......+.|.........|..PPPPPPP +PPPP.....|.S.........|..PPPPPPP +PPPPP....-------------....PPPPP +PPPPPPPP...............PPPPPPPP +PPPPPPPPPPP........PPPPPPPPPPPP +ENDMAP +# Dungeon Description +REGION:(00,00,30,09),lit,"ordinary" +REGION:(12,03,20,06),lit,"temple" +# Doors +DOOR:closed,(09,04) +DOOR:closed,(09,05) +DOOR:locked,(11,03) +DOOR:locked,(11,06) +# Stairs +STAIR:(04,04),up +STAIR:(20,06),down +# Non diggable walls +NON_DIGGABLE:(11,02,21,07) +# Altar in the temple. +ALTAR:(13,05), chaos, shrine +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',random,random,hostile +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"kraken",random +MONSTER:';',"shark",random +MONSTER:';',"shark",random +MONSTER:';',random, random,hostile +MONSTER:';',random, random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile + +# +# The "goal" level for the quest. +# +# Here you meet Cyclops your nemesis monster. You have to +# defeat Cyclops in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Hea-goal", 'P' +# +INIT_MAP: '.' , 'P' , false , true , lit , false +GEOMETRY:center,center +MAP +.P....................................PP. +PP.......PPPPPPP....PPPPPPP....PPPP...PP. +...PPPPPPP....PPPPPPP.....PPPPPP..PPP...P +...PP..............................PPP... +..PP..............................PP..... +..PP..............................PPP.... +..PPP..............................PP.... +.PPP..............................PPPP... +...PP............................PPP...PP +..PPPP...PPPPP..PPPP...PPPPP.....PP...PP. +P....PPPPP...PPPP..PPPPP...PPPPPPP...PP.. +PPP..................................PPP. +ENDMAP +# Dungeon Description +REGION:(00,00,40,11),lit,"ordinary" +# Stairs +STAIR:(39,10),up +# Non diggable walls +NON_DIGGABLE:(00,00,40,11) +# Objects +OBJECT:')',"quarterstaff",(20,06),blessed,0,"The Staff of Aesculapius" +OBJECT:'/',"lightning",(20,06) +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'H',"Cyclops",(20,06),hostile +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',"rabid rat",random +MONSTER:'r',random,random,hostile +MONSTER:'r',random,random,hostile +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"shark",random +MONSTER:';',"shark",random +MONSTER:';',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Hea-fila" , 'P' +INIT_MAP: '.' , 'P' , false , true , lit , false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'r', "rabid rat", random +MONSTER: 'r', random, random,hostile +MONSTER: 'r', random, random,hostile +MONSTER: ';', "giant eel", random +MONSTER: ';', "giant eel", random +MONSTER: ';', "electric eel", random +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random + +MAZE: "Hea-filb" , 'P' +INIT_MAP: '.' , 'P' , false , true , lit , false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'r', "rabid rat", random +MONSTER: 'r', "rabid rat", random +MONSTER: 'r', random, random,hostile +MONSTER: 'r', random, random,hostile +MONSTER: ';', "giant eel", random +MONSTER: ';', "giant eel", random +MONSTER: ';', "giant eel", random +MONSTER: ';', "giant eel", random +MONSTER: ';', "giant eel", random +MONSTER: ';', "electric eel", random +MONSTER: ';', "electric eel", random +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'D',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +MONSTER: 'S',random,random,hostile +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random diff --git a/dat/Knight.des b/dat/Knight.des new file mode 100644 index 0000000..ba63140 --- /dev/null +++ b/dat/Knight.des @@ -0,0 +1,408 @@ +# SCCS Id: @(#)Knight.des 3.4 1995/04/16 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991,92 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, King Arthur +# and receive your quest assignment. +# +MAZE: "Kni-strt",'.' +FLAGS: noteleport,hardfloor +# This is a kludge to init the level as a lit field. +INIT_MAP: '.' , '.' , false , false , lit , false +GEOMETRY:center,center +MAP +.................................................. +.-----......................................-----. +.|...|......................................|...|. +.--|+-------------------++-------------------+|--. +...|...................+..+...................|... +...|.|-----------------|++|-----------------|.|... +...|.|.................|..|.........|.......|.|... +...|.|...\.............+..+.........|.......|.|... +...|.|.................+..+.........+.......|.|... +...|.|.................|..|.........|.......|.|... +...|.|--------------------------------------|.|... +...|..........................................|... +.--|+----------------------------------------+|--. +.|...|......................................|...|. +.-----......................................-----. +.................................................. +ENDMAP +# Dungeon Description +REGION:(00,00,49,15),lit,"ordinary" +REGION:(04,04,45,11),unlit,"ordinary" +REGION:(06,06,22,09),lit,"throne" , unfilled +REGION:(27,06,43,09),lit,"ordinary" +# Portal arrival point +BRANCH:(20,14,20,14),(0,0,0,0) +# Stairs +STAIR:(40,7),down +# Doors +# Outside Doors +DOOR:locked,(24,03) +DOOR:locked,(25,03) +# Inside Doors +DOOR:closed,(23,04) +DOOR:closed,(26,04) +DOOR:locked,(24,05) +DOOR:locked,(25,05) +DOOR:closed,(23,07) +DOOR:closed,(26,07) +DOOR:closed,(23,08) +DOOR:closed,(26,08) +DOOR:closed,(36,08) +# Watchroom Doors +DOOR:closed,(04,03) +DOOR:closed,(45,03) +DOOR:closed,(04,12) +DOOR:closed,(45,12) +# King Arthur +MONSTER:'@',"King Arthur",(09,07) +# The treasure of King Arthur +OBJECT:'(',"chest",(09,07) +# knight guards for the watchrooms +MONSTER:'@',"knight",(04,02),peaceful +MONSTER:'@',"knight",(04,13),peaceful +MONSTER:'@',"knight",(45,02),peaceful +MONSTER:'@',"knight",(45,13),peaceful +# page guards for the audience chamber +MONSTER:'@',"page",(16,06) +MONSTER:'@',"page",(18,06) +MONSTER:'@',"page",(20,06) +MONSTER:'@',"page",(16,09) +MONSTER:'@',"page",(18,09) +MONSTER:'@',"page",(20,09) +# Non diggable walls +NON_DIGGABLE:(00,00,49,15) +# Random traps +TRAP:"sleep gas",(24,04) +TRAP:"sleep gas",(25,04) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'i',"quasit",(14,00),hostile +MONSTER: 'i',"quasit",(16,00),hostile +MONSTER: 'i',"quasit",(18,00),hostile +MONSTER: 'i',"quasit",(20,00),hostile +MONSTER: 'i',"quasit",(22,00),hostile +MONSTER: 'i',"quasit",(24,00),hostile +MONSTER: 'i',"quasit",(26,00),hostile +MONSTER: 'i',"quasit",(28,00),hostile +MONSTER: 'i',"quasit",(30,00),hostile +MONSTER: 'i',"quasit",(32,00),hostile +MONSTER: 'i',"quasit",(34,00),hostile +MONSTER: 'i',"quasit",(36,00),hostile + +# +# The "locate" level for the quest. +# +# Here you have to find your way to the Isle of Glass to go +# further towards your assigned quest. +# + +MAZE: "Kni-loca",' ' +FLAGS: hardfloor +INIT_MAP: '.' , 'P' , false , true , lit , false +GEOMETRY:center,center +MAP +...PPP.........PPPP..............PPPP... +.PPPP...........PP................PPPP.. +PP.................................PPP.. +....................................PPP. +.....................................PP. +.......................................P +........................................ +PP...................................PPP +.PPP...............................PPP.. +..PP.............................PPPP... +..PPP...........................PPPPPP.. +....PPPP.........PPP.........PPPP..PP... +ENDMAP +# Dungeon Description +# The Isle of Glass is a Tor rising out of the swamps surrounding it. +REGION:(00,00,39,11),lit,"ordinary" +# The top area of the Tor is a holy site. +REGION:(09,02,27,09),lit,"temple" +# Stairs +STAIR:(38,0),up +STAIR:(18,05),down +# The altar atop the Tor and its attendant (creating altar makes the priest). +ALTAR:(17,05),neutral,shrine +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +# All of the avenues are guarded by magic except for the East. +# South +TRAP:"magic",(08,11) +TRAP:"magic",(09,11) +TRAP:"magic",(10,11) +TRAP:"magic",(11,11) +TRAP:"magic",(12,11) +TRAP:"magic",(13,11) +TRAP:"magic",(14,11) +TRAP:"magic",(15,11) +TRAP:"magic",(16,11) +TRAP:"magic",(20,11) +TRAP:"magic",(21,11) +TRAP:"magic",(22,11) +TRAP:"magic",(23,11) +TRAP:"magic",(24,11) +TRAP:"magic",(25,11) +TRAP:"magic",(26,11) +TRAP:"magic",(27,11) +TRAP:"magic",(28,11) +# West +TRAP:"magic",(00,03) +TRAP:"magic",(00,04) +TRAP:"magic",(00,05) +TRAP:"magic",(00,06) +# North +TRAP:"magic",(06,00) +TRAP:"magic",(07,00) +TRAP:"magic",(08,00) +TRAP:"magic",(09,00) +TRAP:"magic",(10,00) +TRAP:"magic",(11,00) +TRAP:"magic",(12,00) +TRAP:"magic",(13,00) +TRAP:"magic",(14,00) +TRAP:"magic",(19,00) +TRAP:"magic",(20,00) +TRAP:"magic",(21,00) +TRAP:"magic",(22,00) +TRAP:"magic",(23,00) +TRAP:"magic",(24,00) +TRAP:"magic",(25,00) +TRAP:"magic",(26,00) +TRAP:"magic",(27,00) +TRAP:"magic",(28,00) +TRAP:"magic",(29,00) +TRAP:"magic",(30,00) +TRAP:"magic",(31,00) +TRAP:"magic",(32,00) +# Even so, there are magic "sinkholes" around. +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"anti magic",random +# Random monsters. +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'j',random,random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',random,random,hostile + +# +# The "goal" level for the quest. +# +# Here you meet Ixoth your nemesis monster. You have to +# defeat Ixoth in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Kni-goal", ' ' +GEOMETRY:center,center +MAP +....PPPP..PPP.. +.PPPPP...PP.. .......... ................................. +..PPPPP...P.. ........... ................................... +..PPP....... ........... ...................................... +...PPP....... ......... ............... ..................... +........... ............ ............ ...................... +............ ............. ....... ..................... +.............................. ......................... +............................... .................................. +............................. .................................... +......... ...................................................... +.....PP... ..................................................... +.....PPP.... .................................................... +......PPP.... .............. .................................... +.......PPP.... ............. ..................................... +........PP... ............ ...................................... +...PPP........ .......... .................................. +..PPPPP........ .......... .............................. +....PPPPP...... ......... .......................... +.......PPPP... +ENDMAP +# Dungeon Description +REGION:(00,00,14,19),lit,"ordinary" +REGION:(15,00,75,19),unlit,"ordinary" +# Stairs +STAIR:(03,08),up +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:'(',"mirror",(50,06),blessed,0,"The Magic Mirror of Merlin" +OBJECT:random,random,(33,01) +OBJECT:random,random,(33,02) +OBJECT:random,random,(33,03) +OBJECT:random,random,(33,04) +OBJECT:random,random,(33,05) +OBJECT:random,random,(34,01) +OBJECT:random,random,(34,02) +OBJECT:random,random,(34,03) +OBJECT:random,random,(34,04) +OBJECT:random,random,(34,05) +OBJECT:random,random,(35,01) +OBJECT:random,random,(35,02) +OBJECT:random,random,(35,03) +OBJECT:random,random,(35,04) +OBJECT:random,random,(35,05) +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"spiked pit",(13,07) +TRAP:"spiked pit",(12,08) +TRAP:"spiked pit",(12,09) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'D',"Ixoth",(50,06),hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',"quasit",random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',"ochre jelly",random,hostile +MONSTER:'j',random,random,hostile + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Kni-fila" , '.' +INIT_MAP: '.' , 'P' , false , true , lit , false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', random, random, hostile +MONSTER: 'j', "ochre jelly", random, hostile +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random + +MAZE: "Kni-filb" , '.' +INIT_MAP: '.' , 'P' , false , true , lit , false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', "quasit", random, hostile +MONSTER: 'i', random, random, hostile +MONSTER: 'j', "ochre jelly", random, hostile +MONSTER: 'j', "ochre jelly", random, hostile +MONSTER: 'j', "ochre jelly", random, hostile +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random diff --git a/dat/Monk.des b/dat/Monk.des new file mode 100644 index 0000000..14d7695 --- /dev/null +++ b/dat/Monk.des @@ -0,0 +1,353 @@ +# SCCS Id: @(#)Monk.des 3.4 2002/04/08 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991-2 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, the Grand Master +# and receive your quest assignment. +# +MAZE: "Mon-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +............................................................................ +............................................................................ +............................................................................ +....................------------------------------------.................... +....................|................|.....|.....|.....|.................... +....................|..------------..|--+-----+-----+--|.................... +....................|..|..........|..|.................|.................... +....................|..|..........|..|+---+---+-----+--|.................... +..................---..|..........|......|...|...|.....|.................... +..................+....|..........+......|...|...|.....|.................... +..................+....|..........+......|...|...|.....|.................... +..................---..|..........|......|...|...|.....|.................... +....................|..|..........|..|+-----+---+---+--|.................... +....................|..|..........|..|.................|.................... +....................|..------------..|--+-----+-----+--|.................... +....................|................|.....|.....|.....|.................... +....................------------------------------------.................... +............................................................................ +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(24,06,33,13),lit,"temple" +# Portal arrival point +BRANCH:(05,04,05,04),(0,0,0,0) +# Stairs +STAIR:(52,09),down +# Doors +DOOR:locked,(18,09) +DOOR:locked,(18,10) +DOOR:closed,(34,09) +DOOR:closed,(34,10) +DOOR:closed,(40,05) +DOOR:closed,(46,05) +DOOR:closed,(52,05) +DOOR:locked,(38,07) +DOOR:closed,(42,07) +DOOR:closed,(46,07) +DOOR:closed,(52,07) +DOOR:locked,(38,12) +DOOR:closed,(44,12) +DOOR:closed,(48,12) +DOOR:closed,(52,12) +DOOR:closed,(40,14) +DOOR:closed,(46,14) +DOOR:closed,(52,14) +# Unattended Altar - unaligned due to conflict - player must align it. +ALTAR:(28,09),noalign,altar +# The Grand Master +MONSTER:'@',"Grand Master",(28,10) +# No treasure chest! +# guards for the audience chamber +MONSTER:'@',"abbot",(32,07) +MONSTER:'@',"abbot",(32,08) +MONSTER:'@',"abbot",(32,11) +MONSTER:'@',"abbot",(32,12) +MONSTER:'@',"abbot",(33,07) +MONSTER:'@',"abbot",(33,08) +MONSTER:'@',"abbot",(33,11) +MONSTER:'@',"abbot",(33,12) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:"dart",(20,09) +TRAP:"dart",(20,10) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'E',"earth elemental",(37,01) +MONSTER: 'E',"earth elemental",(37,18) +MONSTER: 'E',"earth elemental",(03,03) +MONSTER: 'E',"earth elemental",(65,04) +MONSTER: 'E',"earth elemental",(12,11) +MONSTER: 'E',"earth elemental",(60,12) +MONSTER: 'E',"earth elemental",(14,08) +MONSTER: 'E',"earth elemental",(55,00) +MONSTER: 'X',"xorn",(18,18) +MONSTER: 'X',"xorn",(59,10) +MONSTER: 'X',"xorn",(13,09) +MONSTER: 'X',"xorn",(01,17) + +# +# The "locate" level for the quest. +# +# Here you have to locate the Monastery of the Earth-Lord to +# go further towards your assigned quest. +# + +MAZE: "Mon-loca",' ' +GEOMETRY:center,center +# 1 2 3 4 5 6 7 +#123456789012345678901234567890123456789012345678901234567890123456789012345 +MAP + ---------------------------------------------------- -------- + ---.................................................- --.....| + ---...--------........------........................--- ---...| + ---.....- --.......- ----..................---- --.-- + ---.....---- --------- --..................-- --..| + ---...----- ----.----.....----.....--- --..|| +----..---- -----..--- |...--- |.......--- --...| +|...--- ----....--- |.--- |.........-- --...|| +|...- ----.....--- ---- |..........---....| +|...---- ----......--- | |...|.......-....|| +|......----- ---.........- | -----...|............| +|..........----- ----...........--- -------......||...........|| +|..............-----................--- |............|||..........| +|-S----...............................--- |...........|| |.........|| +|.....|..............------.............-----..........|| ||........| +|.....|.............-- ---.........................|| |.......|| +|.....|.............- ---.....................--| ||......| +|---S--------.......---- --.................---- |.....|| +|...........|..........--------..............----- ||....| +|...........|............................----- |....| +------------------------------------------ ------ +ENDMAP +# Random Monsters +RANDOM_MONSTERS: 'E', 'X' +# Dungeon Description +REGION:(00,00,75,20),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,75,20) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random + +# +# The "goal" level for the quest. +# +# Here you meet Master Kaen, your nemesis monster. You have to +# defeat Master Kaen in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Mon-goal", ' ' +INIT_MAP: 'L' , '.' , false , false , unlit , false +GEOMETRY:center,center +MAP +.L......L.LLL.......LL.... +.LLL.......L......LL...... +LL.LL.............L.LL.... +.......................... +......................LL.. +......................LLL. +LL........................ +.LL....................... +.LL................LL.L... +..LL.....L.LL.......LLL... +.........LLL.........L.... +ENDMAP +# Dungeon Description +RANDOM_PLACES:(14,04),(13,07) +REGION:(00,00,25,10),unlit,"ordinary" +# Stairs +STAIR:(20,05),up +# Objects +OBJECT:'(',"lenses",place[0],blessed,0,"The Eyes of the Overworld" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Master Kaen",place[0] +ALTAR:place[0],noalign,altar +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'E',"earth elemental",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random +MONSTER: 'X',"xorn",random + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "fila" is the upper filler, between the +# start and locate levels, and "filb" the lower between the locate +# and goal levels. +# + +LEVEL: "Mon-fila" +# Random Monsters +RANDOM_MONSTERS: 'E', 'X' +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'E', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'E', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'X', "xorn", random +MONSTER: 'E', "earth elemental", random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'E', random, random, hostile +MONSTER: 'E', "earth elemental", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'E', "earth elemental", random + +RANDOM_CORRIDORS + +LEVEL: "Mon-filb" +# Random Monsters +RANDOM_MONSTERS: 'E', 'X' +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'E', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'E', random, random, hostile +MONSTER: 'E', "earth elemental", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'E', "earth elemental", random + +RANDOM_CORRIDORS + diff --git a/dat/Priest.des b/dat/Priest.des new file mode 100644 index 0000000..86f2d36 --- /dev/null +++ b/dat/Priest.des @@ -0,0 +1,337 @@ +# SCCS Id: @(#)Priest.des 3.4 2002/04/08 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991-2 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, High Priest +# and receive your quest assignment. +# +MAZE: "Pri-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +............................................................................ +............................................................................ +............................................................................ +....................------------------------------------.................... +....................|................|.....|.....|.....|.................... +....................|..------------..|--+-----+-----+--|.................... +....................|..|..........|..|.................|.................... +....................|..|..........|..|+---+---+-----+--|.................... +..................---..|..........|......|...|...|.....|.................... +..................+....|..........+......|...|...|.....|.................... +..................+....|..........+......|...|...|.....|.................... +..................---..|..........|......|...|...|.....|.................... +....................|..|..........|..|+-----+---+---+--|.................... +....................|..|..........|..|.................|.................... +....................|..------------..|--+-----+-----+--|.................... +....................|................|.....|.....|.....|.................... +....................------------------------------------.................... +............................................................................ +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(24,06,33,13),lit,"temple" +# Portal arrival point +BRANCH:(05,04,05,04),(0,0,0,0) +# Stairs +STAIR:(52,09),down +# Doors +DOOR:locked,(18,09) +DOOR:locked,(18,10) +DOOR:closed,(34,09) +DOOR:closed,(34,10) +DOOR:closed,(40,05) +DOOR:closed,(46,05) +DOOR:closed,(52,05) +DOOR:locked,(38,07) +DOOR:closed,(42,07) +DOOR:closed,(46,07) +DOOR:closed,(52,07) +DOOR:locked,(38,12) +DOOR:closed,(44,12) +DOOR:closed,(48,12) +DOOR:closed,(52,12) +DOOR:closed,(40,14) +DOOR:closed,(46,14) +DOOR:closed,(52,14) +# Unattended Altar - unaligned due to conflict - player must align it. +ALTAR:(28,09),noalign,altar +# High Priest +MONSTER:'@',"Arch Priest",(28,10) +# The treasure of High Priest +OBJECT:'(',"chest",(27,10) +# knight guards for the audience chamber +MONSTER:'@',"acolyte",(32,07) +MONSTER:'@',"acolyte",(32,08) +MONSTER:'@',"acolyte",(32,11) +MONSTER:'@',"acolyte",(32,12) +MONSTER:'@',"acolyte",(33,07) +MONSTER:'@',"acolyte",(33,08) +MONSTER:'@',"acolyte",(33,11) +MONSTER:'@',"acolyte",(33,12) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:"dart",(20,09) +TRAP:"dart",(20,10) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'Z',"human zombie",(37,01) +MONSTER: 'Z',"human zombie",(37,18) +MONSTER: 'Z',"human zombie",(03,03) +MONSTER: 'Z',"human zombie",(65,04) +MONSTER: 'Z',"human zombie",(12,11) +MONSTER: 'Z',"human zombie",(60,12) +MONSTER: 'Z',"human zombie",(14,08) +MONSTER: 'Z',"human zombie",(55,00) +MONSTER: 'Z',"human zombie",(18,18) +MONSTER: 'Z',"human zombie",(59,10) +MONSTER: 'Z',"human zombie",(13,09) +MONSTER: 'Z',"human zombie",(01,17) + +# +# The "locate" level for the quest. +# +# Here you have to locate the Temple of Nalzok to go +# further towards your assigned quest. +# + +MAZE: "Pri-loca",' ' +FLAGS: hardfloor +# This is a kludge to init the level as a lit field. +INIT_MAP: '.' , '.' , false , false , lit , false +GEOMETRY:center,center +MAP +........................................ +........................................ +..........----------+----------......... +..........|........|.|........|......... +..........|........|.|........|......... +..........|----.----.----.----|......... +..........+...................+......... +..........+...................+......... +..........|----.----.----.----|......... +..........|........|.|........|......... +..........|........|.|........|......... +..........----------+----------......... +........................................ +........................................ +ENDMAP +# Dungeon Description +REGION:(00,00,09,13),unlit,"morgue" +REGION:(09,00,30,01),unlit,"morgue" +REGION:(09,12,30,13),unlit,"morgue" +REGION:(31,00,39,13),unlit,"morgue" +REGION:(11,03,29,10),lit,"temple",filled,true +# The altar inside the temple +ALTAR:(20,07),noalign,shrine +MONSTER:'@',"aligned priest",(20,07),noalign,hostile +# Doors +DOOR:locked,(10,06) +DOOR:locked,(10,07) +DOOR:locked,(20,02) +DOOR:locked,(20,11) +DOOR:locked,(30,06) +DOOR:locked,(30,07) +# Stairs +# Note: The up stairs are *intentionally* off of the map. +STAIR:(43,05),up +STAIR:(20,06),down +# Non diggable walls +NON_DIGGABLE:(10,02,30,13) +# Objects (inside the antechambers). +OBJECT:random,random,(14,03) +OBJECT:random,random,(15,03) +OBJECT:random,random,(16,03) +OBJECT:random,random,(14,10) +OBJECT:random,random,(15,10) +OBJECT:random,random,(16,10) +OBJECT:random,random,(17,10) +OBJECT:random,random,(24,03) +OBJECT:random,random,(25,03) +OBJECT:random,random,(26,03) +OBJECT:random,random,(27,03) +OBJECT:random,random,(24,10) +OBJECT:random,random,(25,10) +OBJECT:random,random,(26,10) +OBJECT:random,random,(27,10) +# Random traps +TRAP:random,(15,04) +TRAP:random,(25,04) +TRAP:random,(15,09) +TRAP:random,(25,09) +TRAP:random,random +TRAP:random,random +# No random monsters - the morgue generation will put them in. + +# +# The "goal" level for the quest. +# +# Here you meet Nalzok your nemesis monster. You have to +# defeat Nalzok in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Pri-goal", ' ' +INIT_MAP: 'L' , '.' , false , false , unlit , false +GEOMETRY:center,center +MAP +.L......L.LLL.......LL.... +.LLL.......L......LL...... +LL.LL.............L.LL.... +.......................... +......................LL.. +......................LLL. +LL........................ +.LL....................... +.LL................LL.L... +..LL.....L.LL.......LLL... +.........LLL.........L.... +ENDMAP +# Dungeon Description +RANDOM_PLACES:(14,04),(13,07) +REGION:(00,00,25,10),unlit,"ordinary" +# Stairs +STAIR:(20,05),up +# Objects +OBJECT:'[',"helm of brilliance",place[0],blessed,0,"The Mitre of Holiness" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'&',"Nalzok",place[0] +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',"wraith",random +MONSTER:'W',random,random + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +LEVEL: "Pri-fila" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'Z', "human zombie", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'Z', "human zombie", random + +ROOM: "morgue" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'W', "wraith", random + +ROOM: "morgue" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random + +RANDOM_CORRIDORS + +LEVEL: "Pri-filb" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'Z', "human zombie", random +MONSTER: 'W', "wraith", random + +ROOM: "morgue" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random,random,random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'Z', "human zombie", random +MONSTER: 'W', "wraith", random + +ROOM: "morgue" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'Z', "human zombie", random +MONSTER: 'W', "wraith", random + +ROOM: "morgue" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random + +RANDOM_CORRIDORS diff --git a/dat/Ranger.des b/dat/Ranger.des new file mode 100644 index 0000000..78df233 --- /dev/null +++ b/dat/Ranger.des @@ -0,0 +1,355 @@ +# SCCS Id: @(#)Ranger.des 3.4 2001/02/01 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Orion, +# and receive your quest assignment. +# +MAZE: "Ran-strt",'.' +FLAGS: noteleport,hardfloor,arboreal +INIT_MAP:'.','.',true,true,lit,false +GEOMETRY:left,center +#1234567890123456789012345678901234567890123456789012345678901234567890 +MAP + .. + ................................... . + .. .. + .. ...............F............... .. + . .. .F. .. . + . .. .............F............. .. . + . . .. .. . . + . . .. ....................... .. ... + . . . .. .. . + ... . .. .|..................... ...... + FFF . . ..S.................. + ... . .. .|................. .... ... + . . . .. .. . . . + . . .. ....................... .. . . + . . .. .. . . + . .. .............F............. .. . + . .. .F. .. . + .. ...............F............... .. + .. .. + ................................... . + .. +ENDMAP +# Dungeon Description +REGION:(00,00,40,20),lit,"ordinary" +# Stairs +STAIR:(10,10),down +# Portal arrival point; just about anywhere on the right hand side of the map +BRANCH:levregion(51,2,77,18),(0,0,40,20) +# Orion +MONSTER:'@',"Orion",(20,10) +# The treasure of Orion +OBJECT:'(',"chest",(20,10) +# Guards for the audience chamber +MONSTER:'@',"hunter",(19,09) +MONSTER:'@',"hunter",(20,09) +MONSTER:'@',"hunter",(21,09) +MONSTER:'@',"hunter",(19,10) +MONSTER:'@',"hunter",(21,10) +MONSTER:'@',"hunter",(19,11) +MONSTER:'@',"hunter",(20,11) +MONSTER:'@',"hunter",(21,11) +# Non diggable walls +NON_DIGGABLE:(00,00,40,20) +# Traps +TRAP:"arrow",(30,09) +TRAP:"arrow",(30,10) +TRAP:"pit",(40,09) +TRAP:"spiked pit",random +TRAP:"bear",random +TRAP:"bear",random +# Monsters on siege duty. +MONSTER: 'H',"minotaur",(33,09),hostile,asleep +MONSTER: 'C',"forest centaur",(19,03),hostile +MONSTER: 'C',"forest centaur",(19,04),hostile +MONSTER: 'C',"forest centaur",(19,05),hostile +MONSTER: 'C',"forest centaur",(21,03),hostile +MONSTER: 'C',"forest centaur",(21,04),hostile +MONSTER: 'C',"forest centaur",(21,05),hostile +MONSTER: 'C',"forest centaur",(01,09),hostile +MONSTER: 'C',"forest centaur",(02,09),hostile +MONSTER: 'C',"forest centaur",(03,09),hostile +MONSTER: 'C',"forest centaur",(01,11),hostile +MONSTER: 'C',"forest centaur",(02,11),hostile +MONSTER: 'C',"forest centaur",(03,11),hostile +MONSTER: 'C',"forest centaur",(19,15),hostile +MONSTER: 'C',"forest centaur",(19,16),hostile +MONSTER: 'C',"forest centaur",(19,17),hostile +MONSTER: 'C',"forest centaur",(21,15),hostile +MONSTER: 'C',"forest centaur",(21,16),hostile +MONSTER: 'C',"forest centaur",(21,17),hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 'C',"plains centaur",random,hostile +MONSTER: 's',"scorpion",random,hostile +MONSTER: 's',"scorpion",random,hostile + + +# +# The "locate" level for the quest. +# +# Here you have to infiltrate the Cave of the Wumpus to go +# further towards your assigned quest. +# + +MAZE: "Ran-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +#1234567890123456789012345678901234567890123456789012345678901234567890 +MAP + ....... ......... ....... + ................... ................... + .... ....... ....... .... +... ..... . ..... . ..... ... +. .......... ..... ........... ..... .......... . +. .. ..... .......... ..... .......... ..... .. . +. . . ..... . ..... . . . +. . ..... ............. ..... . . +. . ................ ....... ................ . . +. . ..... ....... ..... . . +. . . ...... ...... . . . +. . ........... ......... ........... . . +. . .......... .......... . . +. .. ..... . ..... . ..... .. . +. .......... ..... ........... ..... .......... . +. ..... .......... ..... .......... ..... . +. . ..... . ..... . . +... ....... ....... ....... ... + .............. ............. .............. + ....... ....... ....... ....... ....... +ENDMAP +# Dungeon Description +REGION:(00,00,54,19),lit,"ordinary" +# Stairs +STAIR:(25,05),up +STAIR:(27,18),down +# Non diggable walls +NON_DIGGABLE:(00,00,54,19) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"spiked pit",random +TRAP:"spiked pit",random +TRAP:"teleport",random +TRAP:"teleport",random +TRAP:"arrow",random +TRAP:"arrow",random +# Random monsters. +MONSTER:'q',"wumpus",(27,18),hostile,asleep +MONSTER:'B',"giant bat",random,hostile +MONSTER:'B',"giant bat",random,hostile +MONSTER:'B',"giant bat",random,hostile +MONSTER:'B',"giant bat",random,hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',random,random,hostile +MONSTER:'s',random,random,hostile + + +# +# The "goal" level for the quest. +# +# Here you meet Scorpius, your nemesis monster. You have to +# defeat Scorpius in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Ran-goal", ' ' +GEOMETRY:center,center +MAP + + ... ... + .......................................................................... + ... + ... + . ............ ....... . ....... . + . ............................. . ........ .........S.. . + . ............ . ...... . . . ....... .. . + . ......... . .... + . ... . .. . + . S . ......... .S. .S............... . + . ... . ... . ......... . . + . ........ .....S.+.......+....\....+........+. . + . ... ... S ......... .. ..... . + . .. ......... .. ...... . + . ....... ... + .... .... .......... . + . .............. .. . ...... .. ............. . + . ............. . .......... ...... . + ... + ... + .......................................................................... + ... ... + +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +# Stairs +STAIR:(19,10),up +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:')',"bow",(37,10),blessed,0,"The Longbow of Diana" +OBJECT:'(',"chest",(37,10) +OBJECT:random,random,(36,09) +OBJECT:random,random,(36,10) +OBJECT:random,random,(36,11) +OBJECT:random,random,(37,09) +OBJECT:random,random,(37,11) +OBJECT:random,random,(38,09) +OBJECT:random,random,(38,10) +OBJECT:random,random,(38,11) +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# doors +DOOR:locked,(12,08) +DOOR:closed,(22,10) +DOOR:locked,(24,10) +DOOR:closed,(25,11) +DOOR:closed,(32,10) +DOOR:closed,(37,03) +DOOR:closed,(37,07) +DOOR:closed,(37,13) +DOOR:closed,(37,16) +DOOR:closed,(42,10) +DOOR:locked,(46,08) +DOOR:closed,(51,10) +DOOR:locked,(53,08) +DOOR:closed,(65,05) +# Random monsters. +MONSTER:'s',"Scorpius",(37,10),hostile +MONSTER:'C',"forest centaur",(36,09),hostile +MONSTER:'C',"forest centaur",(36,10),hostile +MONSTER:'C',"forest centaur",(36,11),hostile +MONSTER:'C',"forest centaur",(37,09),hostile +MONSTER:'C',"forest centaur",(37,11),hostile +MONSTER:'C',"forest centaur",(38,09),hostile +MONSTER:'C',"mountain centaur",(38,10),hostile +MONSTER:'C',"mountain centaur",(38,11),hostile +MONSTER:'C',"mountain centaur",(02,02),hostile +MONSTER:'C',"mountain centaur",(71,02),hostile +MONSTER:'C',"mountain centaur",(02,16),hostile +MONSTER:'C',"mountain centaur",(71,16),hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"forest centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',"mountain centaur",random,hostile +MONSTER:'C',random,random,hostile +MONSTER:'C',random,random,hostile +MONSTER:'s',"scorpion",(03,02),hostile +MONSTER:'s',"scorpion",(72,02),hostile +MONSTER:'s',"scorpion",(03,17),hostile +MONSTER:'s',"scorpion",(72,17),hostile +MONSTER:'s',"scorpion",(41,10),hostile +MONSTER:'s',"scorpion",(33,09),hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',"scorpion",random,hostile +MONSTER:'s',random,random,hostile + +WALLIFY + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "fila" is the upper filler, between the +# start and locate levels, and "filb" the lower between the locate +# and goal levels. +# + +MAZE: "Ran-fila" , ' ' +INIT_MAP: '.' , 'T', true, true, random, true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', "forest centaur", random, hostile +MONSTER: 'C', "forest centaur", random, hostile +MONSTER: 'C', "forest centaur", random, hostile +MONSTER: 'C', random, random, hostile +MONSTER: 's', "scorpion", random, hostile + +MAZE: "Ran-filb" , ' ' +INIT_MAP: '.' , ' ', true, true, random, true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', "mountain centaur", random, hostile +MONSTER: 'C', random, random, hostile +MONSTER: 's', "scorpion", random, hostile +MONSTER: 's', "scorpion", random, hostile + diff --git a/dat/Rogue.des b/dat/Rogue.des new file mode 100644 index 0000000..cd38a97 --- /dev/null +++ b/dat/Rogue.des @@ -0,0 +1,479 @@ +# SCCS Id: @(#)Rogue.des 3.4 2002/02/15 +# Copyright (c) 1992 by Dean Luick +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Master of Thieves +# and receive your quest assignment. +# +MAZE: "Rog-strt",' ' +FLAGS: noteleport, hardfloor, nommap +GEOMETRY:center,center +# 1 2 3 4 5 6 7 +#123456789012345678901234567890123456789012345678901234567890123456789012345 +MAP +---------------------------------.------------------------------------------ +|.....|.||..........|....|......|.|.........|.......+............---.......| +|.....|..+..........+....---....S.|...-S-----.-----.|............+.+.......| +|.....+.||........---......|....|.|...|.....|.|...|.---.....------.--------| +|-----|.-------|..|........------.-----.....|.--..|...-------..............| +|.....|........------+------..........+.....|..--S---.........------.-----.. +|.....|.------...............-----.}}.--------.|....-------.---....|.+...--| +|..-+--.|....|-----.--------.|...|.....+.....|.|....|.....+.+......|.--....| +|..|....|....|....+.|......|.|...-----.|.....|.--...|.....|.|......|..|....| +|..|.-----S----...|.+....-----...|...|.----..|..|.---....--.---S-----.|----| +|..|.|........|...------.|.S.....|...|....-----.+.|......|..|.......|.|....| +|---.-------..|...|....|.|.|.....|...----.|...|.|---.....|.|-.......|.---..| +...........|..S...|....---.----S----..|...|...+.|..-------.---+-....|...--+| +|---------.---------...|......|....S..|.---...|.|..|...........----.---....| +|........|.........|...+.------....|---.---...|.--+-.----.----....|.+...--+| +|........|.---+---.|----.--........|......-----......|..|..|.--+-.|.-S-.|..| +|........|.|.....|........----------.----.......---.--..|-.|....|.-----.|..| +|----....+.|.....----+---............|..|--------.+.|...SS.|....|.......|..| +|...--+-----.....|......|.------------............---...||.------+--+----..| +|..........S.....|......|.|..........S............|.....||...|.....|....|..| +-------------------------.-------------------------------------------------- +ENDMAP +# Dungeon Description +#REGION:(00,00,75,20),lit,"ordinary" +# The down stairs is at one of the 4 "exits". The others are mimics, +# mimicing stairwells. +RANDOM_PLACES: (33,0), (0,12), (25,20), (75,05) +STAIR:place[0],down +MONSTER:'m',"giant mimic", place[1], m_feature "staircase down" +MONSTER:'m',"large mimic", place[2], m_feature "staircase down" +MONSTER:'m',"small mimic", place[3], m_feature "staircase down" +# Portal arrival point +BRANCH:(19,09,19,09),(0,0,0,0) +# Doors (secret) +#DOOR:locked|closed|open,(xx,yy) +DOOR: locked, (32, 2) +DOOR: locked, (63, 9) +DOOR: locked, (27,10) +DOOR: locked, (31,12) +DOOR: locked, (35,13) +DOOR: locked, (69,15) +DOOR: locked, (56,17) +DOOR: locked, (57,17) +DOOR: locked, (11,19) +DOOR: locked, (37,19) +DOOR: locked, (39, 2) +DOOR: locked, (49, 5) +DOOR: locked, (10, 9) +DOOR: locked, (14,12) +# Doors (regular) +DOOR: closed, (52, 1) +DOOR: closed, ( 9, 2) +DOOR: closed, (20, 2) +DOOR: closed, (65, 2) +DOOR: closed, (67, 2) +DOOR: closed, ( 6, 3) +DOOR: closed, (21, 5) +DOOR: closed, (38, 5) +DOOR: closed, (69, 6) +DOOR: closed, ( 4, 7) +DOOR: closed, (39, 7) +DOOR: closed, (58, 7) +DOOR: closed, (60, 7) +DOOR: closed, (18, 8) +DOOR: closed, (20, 9) +DOOR: closed, (48,10) +DOOR: closed, (46,12) +DOOR: closed, (62,12) +DOOR: closed, (74,12) +DOOR: closed, (23,14) +DOOR: closed, (23,14) +DOOR: closed, (50,14) +DOOR: closed, (68,14) +DOOR: closed, (74,14) +DOOR: closed, (14,15) +DOOR: closed, (63,15) +DOOR: closed, ( 9,17) +DOOR: closed, (21,17) +DOOR: closed, (50,17) +DOOR: closed, ( 6,18) +DOOR: closed, (65,18) +DOOR: closed, (68,18) +# Master of Thieves +MONSTER:'@',"Master of Thieves",(36,11) +# The treasure of Master of Thieves +OBJECT:'(',"chest",(36,11) +# thug guards, room #1 +MONSTER:'@',"thug",(28,10) +MONSTER:'@',"thug",(29,11) +MONSTER:'@',"thug",(30,09) +MONSTER:'@',"thug",(31,07) +# thug guards, room #2 +MONSTER:'@',"thug",(31,13) +MONSTER:'@',"thug",(33,14) +MONSTER:'@',"thug",(30,15) +#thug guards, room #3 +MONSTER:'@',"thug",(35,09) +MONSTER:'@',"thug",(36,13) +# Non diggable walls +NON_DIGGABLE:(00,00,75,20) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# +# Monsters to get in the way. +# +# West exit +MONSTER: 'l',"leprechaun",(01,12),hostile +MONSTER: 'n',"water nymph",(02,12),hostile +# North exit +MONSTER: 'n',"water nymph",(33,01),hostile +MONSTER: 'l',"leprechaun",(33,02),hostile +# East exit +MONSTER: 'n',"water nymph",(74,05),hostile +MONSTER: 'l',"leprechaun",(74,04),hostile +# South exit +MONSTER: 'l',"leprechaun",(25,19),hostile +MONSTER: 'n',"water nymph",(25,18),hostile +# Wandering the streets. What I'd really like for this is a random +# location, but make sure we're on a given type, e.g. street (if they +# existed, of course). +MONSTER: 'n',"water nymph",(07,05),hostile +MONSTER: 'l',"leprechaun",(28,06),hostile +MONSTER: 'n',"water nymph",(38,07),hostile +MONSTER: 'l',"leprechaun",(45,01),hostile +MONSTER: 'n',"water nymph",(59,07),hostile +MONSTER: 'l',"leprechaun",(62,14),hostile +MONSTER: 'n',"water nymph",(71,14),hostile +MONSTER: 'l',"leprechaun",(39,13),hostile +MONSTER: 'n',"water nymph",(18,14),hostile +MONSTER: ':',"chameleon",(19,08),hostile +MONSTER: ':',"chameleon",(22,08),hostile +MONSTER: ':',"chameleon",(16,08),hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile + +# +# The "locate" level for the quest. +# +# Here you have to find the entrance to the Assassins' Guild to go +# further towards your assigned quest. +# + +MAZE: "Rog-loca",' ' +GEOMETRY:center,center +# 1 2 3 4 5 6 7 +#123456789012345678901234567890123456789012345678901234567890123456789012345 +MAP + ---------------------------------------------------- -------- + ---.................................................- --.....| + ---...--------........-------.......................--- ---...| + ---.....- ---......- ---..................---- --.-- + ---.....---- -------- --..................-- --..| + ---...----- ----.----.....----.....--- --..|| +----..---- -----..--- |...--- |.......--- --...| +|...--- ----....--- |.--- |.........-- --...|| +|...- ----.....--- ---- |..........---....| +|...---- ----......--- | |...|.......-....|| +|......----- ---.........- | -----...|............| +|..........----- ----...........--- -------......||...........|| +|..............-----................--- |............|||..........| +|------...............................--- |...........|| |.........|| +|.....|..............------.............-----..........|| ||........| +|.....|.............-- ---.........................|| |.......|| +|.....|.............- ---.....................--| ||......| +|-S----------.......---- --.................---- |.....|| +|...........|..........--------..............----- ||....| +|...........|............................----- |....| +------------------------------------------ ------ +ENDMAP +# Dungeon Description +REGION:(00,00,75,20),lit,"ordinary" +# Doors +#DOOR:locked|closed|open,(xx,yy) +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,75,20) +# Objects +OBJECT:'?',"teleportation",(11,18),cursed,0 +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',random,random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',random,random,hostile +MONSTER:'N',random,random,hostile +MONSTER:'N',random,random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile + +# +# The "goal" level for the quest. Teleportation and digging are +# disallowed. +# +# You have to reach The Master Assassin via some means other than +# simple searching or digging since there is no path between your +# arrival point and his location. +# +MAZE: "Rog-goal", ' ' +FLAGS: noteleport +GEOMETRY:center,center +# 1 2 3 4 5 6 7 +#123456789012345678901234567890123456789012345678901234567890123456789012345 +MAP +----- -------.......................................|-----------------| +|...| -----.....|.......................................|.................| +|...----...|.....|.......................................|....---------....| +|.---......---..--.................................------------.......|....| +|...............|..................................|..|...|...----........-| +|.....-----....--.................................|-..--..-|.....----S----| +|--S---...|....|.................................|-........-|....|........| +|.........---------.............................|-....}}....-|...|...|....| +|....|.....S......|............................|-.....}}.....-|..--.------| +|-----.....--.....|...........................|-...}}}}}}}}...-|....|.....-- +|...........--....------S-----...............|-....}}}}}}}}....-|..........| +|............--........|...| |..............--.....}}.}}........----------S- +|.............|........|...| |..............|......}}}}}}}}......|...|.....| +|S-.---.---.---.---.---|...| ------------...--........}}.}}.....--..---....| +|.---.---.---.---.-S-..----- |....|.....|....|-....}}}}}}}}....---..S.|--..| +|...|.......|..........|...---....---...S.....|-...}}}}}}}}...-|.S..|...|..| +|...|..|....|..........|............|..--..----|-.....}}.....-|..----...-S-- +|...|---....----.......|----- ......|...---| |-....}}....-|...|..--.--..| +-----.....---.....--.---....--...--------..| |-........-|....|.........| + |.............|..........|.............S... |S-------|.....|..-----..| + ---------------------------------------- ...... ---------- ---- +ENDMAP +# Dungeon Description +REGION:(00,00,75,20),lit,"ordinary" +# Stairs +STAIR:levregion(01,00,15,20),(01,18,04,20),up +# Doors +# Non diggable walls +NON_DIGGABLE:(00,00,75,20) +# One trap to keep the gnomes at bay. +TRAP:"spiked pit",(37,07) +# Objects +OBJECT:'(',"skeleton key",(38,10),blessed,0,"The Master Key of Thievery" +OBJECT:'%',"tin",(26,12),"chameleon",0 +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Master Assassin",(38,10),hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',"leprechaun",random,hostile +MONSTER:'l',random,random,hostile +MONSTER:'l',random,random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',"guardian naga",random,hostile +MONSTER:'N',random,random,hostile +MONSTER:'N',random,random,hostile +MONSTER:'N',random,random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER: ':',"chameleon",random,hostile +MONSTER:';',"shark",(51,14),hostile +MONSTER:';',"shark",(53,09),hostile +MONSTER:';',"shark",(55,15),hostile +MONSTER:';',"shark",(58,10),hostile + +# +# The "fill" level for the quest. +# +# This level is used to fill out any levels not occupied by specific +# levels as defined above. +# +LEVEL: "Rog-fila" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'l', "leprechaun", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'l', "leprechaun", random, hostile +MONSTER: 'N', "guardian naga", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'n', "water nymph", random, hostile + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', random, random, hostile +MONSTER: 'N', "guardian naga", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', "leprechaun", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', "leprechaun", random, hostile +MONSTER: 'n', "water nymph", random, hostile + +RANDOM_CORRIDORS + +# +# currently a & b are the same. +# +LEVEL: "Rog-filb" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'l', "leprechaun", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'l', "leprechaun", random, hostile +MONSTER: 'N', "guardian naga", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'n', "water nymph", random, hostile + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', random, random, hostile +MONSTER: 'N', "guardian naga", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', "leprechaun", random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +TRAP: random, random +MONSTER: 'l', "leprechaun", random, hostile +MONSTER: 'n', "water nymph", random, hostile + +RANDOM_CORRIDORS diff --git a/dat/Samurai.des b/dat/Samurai.des new file mode 100644 index 0000000..5cb513f --- /dev/null +++ b/dat/Samurai.des @@ -0,0 +1,426 @@ +# SCCS Id: @(#)Samurai.des 3.4 2002/04/08 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991-92 by M. Stephenson, P. Winner +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Lord Sato +# and receive your quest assignment. +# +MAZE: "Sam-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +..............................................................PP............ +...............................................................PP........... +..........---------------------------------------------------...PPP......... +..........|......|.........|...|..............|...|.........|....PPPPP...... +......... |......|.........S...|..............|...S.........|.....PPPP...... +..........|......|.........|---|..............|---|.........|.....PPP....... +..........+......|.........+...-------++-------...+.........|......PP....... +..........+......|.........|......................|.........|......PP....... +......... |......---------------------++--------------------|........PP..... +..........|.................................................|.........PP.... +..........|.................................................|...........PP.. +..........----------------------------------------...-------|............PP. +..........................................|.................|.............PP +.............. ................. .........|.................|..............P +............. } ............... } ........|.................|............... +.............. ........PP....... .........|.................|............... +.....................PPP..................|.................|............... +......................PP..................-------------------............... +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(18,03,26,07),lit,"throne",unfilled +# Portal arrival zone +BRANCH:(62,12,70,17),(0,0,0,0) +# Stairs +STAIR:(29,04),down +# Doors +DOOR:locked,(10,06) +DOOR:locked,(10,07) +DOOR:closed,(27,04) +DOOR:closed,(27,06) +DOOR:closed,(38,06) +DOOR:locked,(38,08) +DOOR:closed,(39,06) +DOOR:locked,(39,08) +DOOR:closed,(50,04) +DOOR:closed,(50,06) +# Lord Sato +MONSTER:'@',"Lord Sato",(20,04) +# The treasure of Lord Sato +OBJECT:'(',"chest",(20,04) +# roshi guards for the audience chamber +MONSTER:'@',"roshi",(18,04) +MONSTER:'@',"roshi",(18,05) +MONSTER:'@',"roshi",(18,06) +MONSTER:'@',"roshi",(18,07) +MONSTER:'@',"roshi",(26,04) +MONSTER:'@',"roshi",(26,05) +MONSTER:'@',"roshi",(26,06) +MONSTER:'@',"roshi",(26,07) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: '@',"ninja",(64,00),hostile +MONSTER: 'd',"wolf",(65,01) +MONSTER: '@',"ninja",(67,02),hostile +MONSTER: '@',"ninja",(69,05),hostile +MONSTER: '@',"ninja",(69,06),hostile +MONSTER: 'd',"wolf",(69,07) +MONSTER: '@',"ninja",(70,06),hostile +MONSTER: '@',"ninja",(70,07),hostile +MONSTER: '@',"ninja",(72,01),hostile +MONSTER: 'd',"wolf",(75,09) +MONSTER: '@',"ninja",(73,05),hostile +MONSTER: '@',"ninja",(68,02),hostile +MONSTER:'E',"stalker",random + +# +# The "locate" level for the quest. +# +# Here you have to invade the Shogun's Castle to go +# further towards your assigned quest. +# + +MAZE: "Sam-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP +............................................................................ +............................................................................ +........-----..................................................-----........ +........|...|..................................................|...|........ +........|...---..}..--+------------------------------+--..}..---...|........ +........|-|...|.....|...|....|....|....|....|....|.|...|.....|...|-|........ +..........|...-------...|....|....|....|....|....S.|...-------...|.......... +..........|-|.........------+----+-+-------+-+--------.........|-|.......... +............|..--------.|}........................}|.--------..|............ +............|..+........+..........................+........+..|............ +............|..+........+..........................+........+..|............ +............|..--------.|}........................}|.--------..|............ +..........|-|.........--------+-+-------+-+----+------.........|-|.......... +..........|...-------...|.S....|....|....|....|....|...-------...|.......... +........|-|...|.....|...|.|....|....|....|....|....|...|.....|...|-|........ +........|...---..}..--+------------------------------+--..}..---...|........ +........|...|..................................................|...|........ +........-----..................................................-----........ +............................................................................ +............................................................................ +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +# Doors +DOOR:locked,(22,04) +DOOR:locked,(22,15) +DOOR:locked,(53,04) +DOOR:locked,(53,15) +DOOR:locked,(49,06) +DOOR:locked,(26,13) +DOOR:locked,(28,07) +DOOR:locked,(30,12) +DOOR:locked,(33,07) +DOOR:locked,(32,12) +DOOR:locked,(35,07) +DOOR:locked,(40,12) +DOOR:locked,(43,07) +DOOR:locked,(42,12) +DOOR:locked,(45,07) +DOOR:locked,(47,12) +DOOR:closed,(15,09) +DOOR:closed,(15,10) +DOOR:closed,(24,09) +DOOR:closed,(24,10) +DOOR:closed,(51,09) +DOOR:closed,(51,10) +DOOR:closed,(60,09) +DOOR:closed,(60,10) +# Stairs +STAIR:(10,10),up +STAIR:(25,14),down +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Objects +OBJECT:'*',random,(25,05) +OBJECT:'*',random,(26,05) +OBJECT:'*',random,(27,05) +OBJECT:'*',random,(28,05) +OBJECT:'*',random,(25,06) +OBJECT:'*',random,(26,06) +OBJECT:'*',random,(27,06) +OBJECT:'*',random,(28,06) +# +OBJECT:'[',random,(40,05) +OBJECT:'[',random,(41,05) +OBJECT:'[',random,(42,05) +OBJECT:'[',random,(43,05) +OBJECT:'[',random,(40,06) +OBJECT:'[',random,(41,06) +OBJECT:'[',random,(42,06) +OBJECT:'[',random,(43,06) +# +OBJECT:')',random,(27,13) +OBJECT:')',random,(28,13) +OBJECT:')',random,(29,13) +OBJECT:')',random,(30,13) +OBJECT:')',random,(27,14) +OBJECT:')',random,(28,14) +OBJECT:')',random,(29,14) +OBJECT:')',random,(30,14) +# +OBJECT:'(',random,(37,13) +OBJECT:'(',random,(38,13) +OBJECT:'(',random,(39,13) +OBJECT:'(',random,(40,13) +OBJECT:'(',random,(37,14) +OBJECT:'(',random,(38,14) +OBJECT:'(',random,(39,14) +OBJECT:'(',random,(40,14) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"ninja",(15,05),hostile +MONSTER:'@',"ninja",(16,05),hostile +MONSTER:'d',"wolf",(17,05) +MONSTER:'d',"wolf",(18,05) +MONSTER:'@',"ninja",(19,05),hostile +MONSTER:'d',"wolf",(15,14) +MONSTER:'d',"wolf",(16,14) +MONSTER:'@',"ninja",(17,14),hostile +MONSTER:'@',"ninja",(18,14),hostile +MONSTER:'d',"wolf",(56,05) +MONSTER:'@',"ninja",(57,05),hostile +MONSTER:'d',"wolf",(58,05) +MONSTER:'d',"wolf",(59,05) +MONSTER:'@',"ninja",(56,14),hostile +MONSTER:'d',"wolf",(57,14) +MONSTER:'@',"ninja",(58,14),hostile +MONSTER:'d',random,(59,14) +MONSTER:'d',"wolf",(60,14) +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +# "guards" for the central courtyard. +MONSTER:'@',"samurai",(30,05),hostile +MONSTER:'@',"samurai",(31,05),hostile +MONSTER:'@',"samurai",(32,05),hostile +MONSTER:'@',"samurai",(32,14),hostile +MONSTER:'@',"samurai",(33,14),hostile +MONSTER:'@',"samurai",(34,14),hostile + +# +# The "goal" level for the quest. +# +# Here you meet Takauji, your nemesis monster. You have to +# defeat him in combat to gain the artifact you have been +# assigned to retrieve. +# + +MAZE: "Sam-goal", ' ' +FLAGS: noteleport +GEOMETRY:center,center +MAP + + ....................... + ......---------.---------...... + ......----.................----...... + ....----.....-------------.....----.... + ....--.....----...........----.....--.... + ...||....---....---------....---....||... + ...|....--....---.......---....--....|... + ....|...||...---...--+--...---...||...|.... + ....|...|....|....|-...-|....|....|...|.... + ....|...|....|....+.....+....|........|.... + ....|...|....|....|-...-|....|....|...|.... + ....|...||...---...--+--...---...||...|.... + ...|....--....---.......---....--....|... + ...||....---....----.----....---....||... + ....--.....----...........----.....--.... + ....----.....-------------.....----.... + ......----.................----...... + ......-------------------...... + ....................... +ENDMAP +# Dungeon Description +RANDOM_PLACES:(02,11),(42,09) +REGION:(00,00,44,19),unlit,"ordinary" +# Doors +DOOR:closed,(19,10) +DOOR:closed,(22,08) +DOOR:closed,(22,12) +DOOR:closed,(25,10) +# Stairs +STAIR:place[0],up +# Non diggable walls +NON_DIGGABLE:(00,00,44,19) +# Objects +OBJECT:')',"tsurugi",(22,10),blessed,0,"The Tsurugi of Muramasa" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# +TRAP:"board",(22,09) +TRAP:"board",(24,10) +TRAP:"board",(22,11) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Ashikaga Takauji",(22,10) +MONSTER:'@',"samurai",random,hostile +MONSTER:'@',"samurai",random,hostile +MONSTER:'@',"samurai",random,hostile +MONSTER:'@',"samurai",random,hostile +MONSTER:'@',"samurai",random,hostile +MONSTER:'@',"ninja",random,hostile +MONSTER:'@',"ninja",random,hostile +MONSTER:'@',"ninja",random,hostile +MONSTER:'@',"ninja",random,hostile +MONSTER:'@',"ninja",random,hostile +MONSTER:'d',"wolf",random +MONSTER:'d',"wolf",random +MONSTER:'d',"wolf",random +MONSTER:'d',"wolf",random +MONSTER:'d',random,random +MONSTER:'d',random,random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random +MONSTER:'E',"stalker",random + + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Sam-fila", ' ' +INIT_MAP: '.' , 'P', true, true, random, true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'd', random, random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'E', "stalker", random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random + +MAZE: "Sam-filb", ' ' +GEOMETRY:center,center +MAP +------------- ------------- +|...........| |...........| +|...-----...|----------------------------------|...-----...| +|...| |...|..................................|...| |...| +|...-----..........................................-----...| +|...........|--S----------------------------S--|...........| +----...--------.|..........................|.--------...---- + |...|........+..........................+........|...| + |...|........+..........................+........|...| +----...--------.|..........................|.--------...---- +|...........|--S----------------------------S--|...........| +|...-----..........................................-----...| +|...| |...|..................................|...| |...| +|...-----...|----------------------------------|...-----...| +|...........| |...........| +------------- ------------- +ENDMAP +REGION:(00,00,59,15),unlit,"ordinary" +# Doors +DOOR:closed,(16,07) +DOOR:closed,(16,08) +DOOR:closed,(43,07) +DOOR:closed,(43,08) +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'd', random, random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'd', "wolf", random +MONSTER: 'E', "stalker", random +MONSTER: 'E', "stalker", random +MONSTER: 'E', "stalker", random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random diff --git a/dat/Tourist.des b/dat/Tourist.des new file mode 100644 index 0000000..348b9b7 --- /dev/null +++ b/dat/Tourist.des @@ -0,0 +1,519 @@ +# SCCS Id: @(#)Tourist.des 3.4 1992/09/26 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991,92 by M. Stephenson, P. Winner +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Twoflower +# and receive your quest assignment. +# +MAZE: "Tou-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +.......}}....---------..-------------------------------------------------... +........}}...|.......|..|.-------------------------------------------...|... +.........}}..|.......|..|.|......|......|.............|......|......|...|... +..........}}.|.......|..|.|......+......+.............+......+..\...|...|... +...........}}}..........|.|......|......|.............|......|......|...|... +.............}}.........|.|----S-|--S---|S----------S-|---S--|------|...|... +..............}}}.......|...............................................|... +................}}}.....----S------++--S----------S----------S-----------... +..................}}........... .. ................................... +......-------......}}}}........}}}}..}}}}..}}}}..}}}}....................... +......|.....|.......}}}}}}..}}}} .. }}}}..}}}}..}}}..................... +......|.....+...........}}}}}}........................}}}..}}}}..}}}..}}}... +......|.....|...........................................}}}}..}}}..}}}}.}}}} +......-------............................................................... +............................................................................ +...-------......-------..................................................... +...|.....|......|.....|..................................................... +...|.....+......+.....|..................................................... +...|.....|......|.....|..................................................... +...-------......-------..................................................... +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(14,01,20,03),unlit,"morgue" +REGION:(07,10,11,12),unlit,"ordinary" +REGION:(04,16,08,18),unlit,"ordinary" +REGION:(17,16,21,18),unlit,"ordinary" +REGION:(27,02,32,04),unlit,"ordinary" +REGION:(34,02,39,04),unlit,"ordinary" +REGION:(41,02,53,04),unlit,"ordinary" +REGION:(55,02,60,04),unlit,"ordinary" +REGION:(62,02,67,04),lit,"ordinary" +# Stairs +STAIR:(66,03),down +# Portal arrival point +BRANCH:(68,14,68,14),(0,0,0,0) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Doors +DOOR:locked,(31,05) +DOOR:locked,(36,05) +DOOR:locked,(41,05) +DOOR:locked,(52,05) +DOOR:locked,(58,05) +DOOR:locked,(28,07) +DOOR:locked,(39,07) +DOOR:locked,(50,07) +DOOR:locked,(61,07) +DOOR:closed,(33,03) +DOOR:closed,(40,03) +DOOR:closed,(54,03) +DOOR:closed,(61,03) +DOOR:open,(12,11) +DOOR:open,(09,17) +DOOR:open,(16,17) +DOOR:locked,(35,07) +DOOR:locked,(36,07) +# Monsters on siege duty. +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',"giant spider",random +MONSTER: 's',random,random +MONSTER: 's',random,random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',"forest centaur",random +MONSTER: 'C',random,random +# Twoflower +MONSTER:'@',"Twoflower",(64,03) +# The treasure of Twoflower +OBJECT:'(',"chest",(64,03) +# guides for the audience chamber +MONSTER:'@',"guide",(29,03) +MONSTER:'@',"guide",(32,04) +MONSTER:'@',"guide",(35,02) +MONSTER:'@',"guide",(38,03) +MONSTER:'@',"guide",(45,03) +MONSTER:'@',"guide",(48,02) +MONSTER:'@',"guide",(49,04) +MONSTER:'@',"guide",(51,03) +MONSTER:'@',"guide",(57,03) +MONSTER:'@',"guide",(62,04) +MONSTER:'@',"guide",(66,04) +# path guards +MONSTER:'@',"watchman",(35,08) +MONSTER:'@',"watchman",(36,08) +# river monsters +MONSTER:';',"giant eel",(62,12) +MONSTER:';',"piranha",(47,10) +MONSTER:';',"piranha",(29,11) +MONSTER:';',"kraken",(34,09) +MONSTER:';',"kraken",(37,09) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random + +# +# The "locate" level for the quest. +# +# Here you have to find the Thieves' Guild Hall to go +# further towards your assigned quest. +# + +MAZE: "Tou-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP +---------------------------------------------------------------------------- +|....|......|..........|......|......|...|....|.....|......|...............| +|....|......|.|------|.|......|......|.|.|....|..}..|......|.|----------|..| +|....|--+----.|......|.|-S---+|+-----|.|.S....|.....|---+--|.|..........+..| +|....|........|......|.|...|.........|.|------|..............|..........|-+| +|....+...}}...+......|.|...|.|-----|.|..............|--+----------------|..| +|----|........|------|.|---|.|.....|......|-----+-|.|.......|...........|--| +|............................|.....|.|--+-|.......|.|.......|...........|..| +|----|.....|-------------|...|--+--|.|....|.......|.|-----------+-------|..| +|....+.....+.........S...|...........|....|-------|........................| +|....|.....|.........|...|.|---------|....|.........|-------|.|----------|.| +|....|.....|---------|---|.|......|..+....|-------|.|.......|.+......S.\.|.| +|....|.....+.........S...|.|......|..|....|.......|.|.......|.|......|...|.| +|-------|..|.........|---|.|+-------------------+-|.|.......+.|----------|.| +|.......+..|---------|.........|.........|..........|.......|.|..........|.| +|.......|..............|--+--|.|.........|.|----+-----------|.|..........|.| +|---------+-|--+-----|-|.....|.|.........|.|........|.|.....+.|..........+.| +|...........|........|.S.....|.|----+----|.|--------|.|.....|.|----------|.| +|...........|........|.|.....|........................|.....|..............| +---------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +NON_DIGGABLE:(00,00,75,19) +# +REGION:(01,01,04,05),unlit,"morgue" +REGION:(15,03,20,05),lit,"shop" +REGION:(62,03,71,04),lit,"shop" +REGION:(01,17,11,18),lit,"barracks" +REGION:(12,09,20,10),lit,"barracks" +REGION:(53,11,59,14),lit,"zoo" +REGION:(63,14,72,16),lit,"barracks" +REGION:(32,14,40,16),lit,"temple" +# +REGION:(06,01,11,02),random,"ordinary" +REGION:(24,01,29,02),random,"ordinary" +REGION:(31,01,36,02),random,"ordinary" +REGION:(42,01,45,03),random,"ordinary" +REGION:(53,01,58,02),random,"ordinary" +REGION:(24,04,26,05),random,"ordinary" +REGION:(30,06,34,07),random,"ordinary" +REGION:(73,05,74,05),unlit,"ordinary" +REGION:(01,09,04,12),random,"ordinary" +REGION:(01,14,07,15),random,"ordinary" +REGION:(12,12,20,13),random,"ordinary" +REGION:(13,17,20,18),random,"ordinary" +REGION:(22,09,24,10),random,"ordinary" +REGION:(22,12,24,12),random,"ordinary" +REGION:(24,16,28,18),random,"ordinary" +REGION:(28,11,33,12),random,"ordinary" +REGION:(35,11,36,12),lit,"ordinary" +REGION:(38,08,41,12),random,"ordinary" +REGION:(43,07,49,08),random,"ordinary" +REGION:(43,12,49,12),random,"ordinary" +REGION:(44,16,51,16),random,"ordinary" +REGION:(53,06,59,07),random,"ordinary" +REGION:(61,06,71,07),random,"ordinary" +REGION:(55,16,59,18),random,"ordinary" +REGION:(63,11,68,12),random,"ordinary" +REGION:(70,11,72,12),random,"ordinary" +# Stairs +STAIR:(10,04),up +STAIR:(73,05),down +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +DOOR:closed,(05,05) +DOOR:closed,(05,09) +DOOR:closed,(08,14) +DOOR:closed,(08,03) +DOOR:closed,(11,09) +DOOR:closed,(11,12) +DOOR:closed,(10,16) +DOOR:closed,(14,05) +DOOR:closed,(15,16) +DOOR:locked,(21,09) +DOOR:locked,(21,12) +DOOR:closed,(23,17) +DOOR:closed,(25,03) +DOOR:closed,(26,15) +DOOR:closed,(29,03) +DOOR:closed,(28,13) +DOOR:closed,(31,03) +DOOR:closed,(32,08) +DOOR:closed,(37,11) +DOOR:closed,(36,17) +DOOR:locked,(41,03) +DOOR:closed,(40,07) +DOOR:closed,(48,06) +DOOR:closed,(48,13) +DOOR:closed,(48,15) +DOOR:closed,(56,03) +DOOR:closed,(55,05) +DOOR:closed,(72,03) +DOOR:locked,(74,04) +DOOR:closed,(64,08) +DOOR:closed,(62,11) +DOOR:closed,(69,11) +DOOR:closed,(60,13) +DOOR:closed,(60,16) +DOOR:closed,(73,16) + +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Toilet paper +OBJECT:'?',"blank paper",(71,12) +OBJECT:'?',"blank paper",(71,12) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',random,random +MONSTER:'s',random,random + +# +# The "goal" level for the quest. +# +# Here you meet the Master of Thieves your nemesis monster. You have to +# defeat the Master of Thieves in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Tou-goal", ' ' +GEOMETRY:center,center +MAP +---------------------------------------------------------------------------- +|.........|.........|..........|..| |.................|........|........|..| +|.........|.........|..........|..| |....--------.....|........|........|..| +|------S--|--+-----------+------..| |....|......|.....|........|........|..| +|.........|.......................| |....|......+.....--+-------------+--..| +|.........|.......................| |....|......|..........................| +|-S-----S-|......----------.......| |....|......|..........................| +|..|..|...|......|........|.......| |....-----------.........----..........| +|..+..+...|......|........|.......| |....|.........|.........|}}|..........| +|..|..|...|......+........|.......| |....|.........+.........|}}|..........| +|..|..|...|......|........|.......S.S....|.........|.........----..........| +|---..----|......|........|.......| |....|.........|.......................| +|.........+......|+F-+F-+F|.......| |....-----------.......................| +|---..----|......|..|..|..|.......| |......................--------------..| +|..|..|...|......--F-F--F--.......| |......................+............|..| +|..+..+...|.......................| |--.---...-----+-----..|............|..| +|--|..----|--+-----------+------..| |.....|...|.........|..|------------|..| +|..+..+...|.........|..........|..| |.....|...|.........|..+............|..| +|..|..|...|.........|..........|..| |.....|...|.........|..|............|..| +---------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +# The Inn +REGION:(01,01,09,02),lit,"ordinary" +REGION:(01,04,09,05),lit,"barracks" +REGION:(01,07,02,10),unlit,"ordinary" +REGION:(07,07,09,10),unlit,"ordinary" +REGION:(01,14,02,15),unlit,"ordinary" +REGION:(07,14,09,15),unlit,"ordinary" +REGION:(01,17,02,18),unlit,"ordinary" +REGION:(07,17,09,18),unlit,"ordinary" +# +REGION:(11,01,19,02),unlit,"barracks" +REGION:(21,01,30,02),unlit,"ordinary" +REGION:(11,17,19,18),unlit,"barracks" +REGION:(21,17,30,18),unlit,"ordinary" +# Police Station +REGION:(18,07,25,11),lit,"ordinary" +REGION:(18,13,19,13),unlit,"ordinary" +REGION:(21,13,22,13),unlit,"ordinary" +REGION:(24,13,25,13),unlit,"ordinary" +# The town itself +REGION:(42,03,47,06),unlit,"ordinary" +REGION:(42,08,50,11),unlit,"ordinary" +REGION:(37,16,41,18),unlit,"morgue" +REGION:(47,16,55,18),unlit,"ordinary" +REGION:(55,01,62,03),unlit,"ordinary" +REGION:(64,01,71,03),unlit,"ordinary" +REGION:(60,14,71,15),lit,"shop" +REGION:(60,17,71,18),lit,"shop" +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Stairs +STAIR:(70,08),up +# Doors +DOOR:locked,(07,03) +DOOR:locked,(02,06) +DOOR:locked,(08,06) +DOOR:closed,(03,08) +DOOR:closed,(06,08) +DOOR:open,(10,12) +DOOR:closed,(03,15) +DOOR:closed,(06,15) +DOOR:closed,(03,17) +DOOR:closed,(06,17) +DOOR:closed,(13,03) +DOOR:random,(25,03) +DOOR:closed,(13,16) +DOOR:random,(25,16) +DOOR:locked,(17,09) +DOOR:locked,(18,12) +DOOR:locked,(21,12) +DOOR:locked,(24,12) +DOOR:locked,(34,10) +DOOR:locked,(36,10) +DOOR:random,(48,04) +DOOR:random,(56,04) +DOOR:random,(70,04) +DOOR:random,(51,09) +DOOR:random,(51,15) +DOOR:open,(59,14) +DOOR:open,(59,17) +# Objects +OBJECT:'(',"credit card",(04,01),blessed,0,"The Platinum Yendorian Express Card" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Master of Thieves",(04,01),hostile +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',"giant spider",random +MONSTER:'s',random,random +MONSTER:'s',random,random +# ladies of the evening +MONSTER:'&',"succubus",(02,08) +MONSTER:'&',"succubus",(08,08) +MONSTER:'&',"incubus",(02,14) +MONSTER:'&',"incubus",(08,14) +MONSTER:'&',"incubus",(02,17) +MONSTER:'&',"incubus",(08,17) +# Police station (with drunken prisoners) +MONSTER:'K',"Kop Kaptain",(24,09),hostile +MONSTER:'K',"Kop Lieutenant",(20,09),hostile +MONSTER:'K',"Kop Lieutenant",(22,11),hostile +MONSTER:'K',"Kop Lieutenant",(22,07),hostile +MONSTER:'K',"Keystone Kop",(19,07),hostile +MONSTER:'K',"Keystone Kop",(19,08),hostile +MONSTER:'K',"Keystone Kop",(22,09),hostile +MONSTER:'K',"Keystone Kop",(24,11),hostile +MONSTER:'K',"Keystone Kop",(19,11),hostile +MONSTER:'@',"prisoner",(19,13) +MONSTER:'@',"prisoner",(21,13) +MONSTER:'@',"prisoner",(24,13) +# +MONSTER:'@',"watchman",(33,10),hostile + +WALLIFY + +# +# The "fill" level for the quest. +# +# This level is used to fill out any levels not occupied by specific +# levels as defined above. +# + +MAZE: "Tou-fila" , ' ' +INIT_MAP: '.' , ' ', true, true, random, true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: '@', "soldier", random, hostile +MONSTER: '@', "soldier", random, hostile +MONSTER: '@', "soldier", random, hostile +MONSTER: '@', "soldier", random, hostile +MONSTER: '@', "soldier", random, hostile +MONSTER: 'H', random, random, hostile +MONSTER: 'C', random, random, hostile + +MAZE: "Tou-filb" , ' ' +INIT_MAP: '.' , ' ', true, true, random, true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +# +MONSTER: '@', "soldier", random, hostile +MONSTER: '@', "captain", random, hostile +MONSTER: '@', "captain", random, hostile +MONSTER: 'H', random, random, hostile +MONSTER: 'H', random, random, hostile +MONSTER: 'C', random, random, hostile +MONSTER: 's', random, random diff --git a/dat/Valkyrie.des b/dat/Valkyrie.des new file mode 100644 index 0000000..58116d5 --- /dev/null +++ b/dat/Valkyrie.des @@ -0,0 +1,332 @@ +# SCCS Id: @(#)Valkyrie.des 3.4 2002/05/02 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1991-2 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, the Norn, +# and receive your quest assignment. +# +MAZE: "Val-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +IIIIIIPPPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII +IIIIPPPPPIIIIIIII..IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII...IIIIIIIIIIIIIIIIIIIII +IIIIPLLPPIIIIIII..IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII..{..IIIIIIIIIIIIIIIIIIII +IIIIPLPPIIIIIII..IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII.....IIIIIIPPPIIIIIIIIII +IIIPPPPPIIIIII..IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII.IIIIIPPLPIIIIIIIIII +IIIIPIIIIIIII..IIIIPPPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII.IIIIIPLPPIIIIIIIIII +IIIIIIIIIIII..IIIIIPLPPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII..IIIIIPPPIIIIIIIIIII +IIIIIIII.....IIIIIIPPPIIII|----------------|IIIIIPPPIII.IIIIIIIIIIIIIIIIIIII +IIIIIII..III...IIIIIIIIIII|................|IIIIIPLPII..IIIIIIIIIIIIIIIIIIII +IIIIII..IIIIII......IIIII.|................|.IIIIPPPII.IIIIIIIIIIIIIIIIIIIII +IIIII..IIIIIIIIIIII.......+................+...IIIIIII.IIIIIIIIIIIIIIIIIIIII +IIII..IIIIIIIII.....IIIII.|................|.I...IIIII.IIIIIIIIIIIIIIIIIIIII +III..IIIIIIIII..IIIIIIIIII|................|IIII.......IIIIIIIIIIIIIIIIIIIII +IIII..IIIIIII..IIIIIIIIIII|----------------|IIIIIIIIII...IIIIIIIIIIIIIIIIIII +IIIIII..IIII..IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIPPPPIIII...IIIIIIIIIIIIIIIII +IIIIIII......IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIPLLPPIIIII...IIIIIIIIIIIIIII +IIIIPPPIP...IIIIIIIIIIIPIIIIIIIIIIIIIIIIIIIIIIIIPPPPIIIIIIII...I......IIIIII +IIIPPLPPIIIIIIIIIIIIIIPPPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII.........IIIII +IIIIPPPIIIIIIIIIIIIIIPPLPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII.......IIIIII +IIIIIIIIIIIIIIIIIIIIIIPPPIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(27,08,42,12),lit,"ordinary" +# Portal arrival point +BRANCH:(66,17,66,17),(0,0,0,0) +# Stairs +STAIR:(18,01),down +FOUNTAIN:(53,02) +# Doors +DOOR:locked,(26,10) +DOOR:locked,(43,10) +# Norn +MONSTER:'@',"Norn",(35,10) +# The treasure of the Norn +OBJECT:'(',"chest",(36,10) +# valkyrie guards for the audience chamber +MONSTER:'@',"warrior",(27,08) +MONSTER:'@',"warrior",(27,09) +MONSTER:'@',"warrior",(27,11) +MONSTER:'@',"warrior",(27,12) +MONSTER:'@',"warrior",(42,08) +MONSTER:'@',"warrior",(42,09) +MONSTER:'@',"warrior",(42,11) +MONSTER:'@',"warrior",(42,12) +# Non diggable walls +NON_DIGGABLE:(26,07,43,13) +# Random traps +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +# Monsters on siege duty. +MONSTER: 'a',"fire ant",(04,12) +MONSTER: 'a',"fire ant",(08,08) +MONSTER: 'a',"fire ant",(14,04) +MONSTER: 'a',"fire ant",(17,11) +MONSTER: 'a',"fire ant",(24,10) +MONSTER: 'a',"fire ant",(45,10) +MONSTER: 'a',"fire ant",(54,02) +MONSTER: 'a',"fire ant",(55,07) +MONSTER: 'a',"fire ant",(58,14) +MONSTER: 'a',"fire ant",(63,17) +MONSTER: 'H',"fire giant",(18,01),hostile +MONSTER: 'H',"fire giant",(10,16),hostile + +# +# The "locate" level for the quest. +# +# Here you have to find the cave of Surtur to go +# further towards your assigned quest. +# + +MAZE: "Val-loca",' ' +FLAGS: hardfloor +INIT_MAP: '.' , 'I' , true , true , lit , false +GEOMETRY:center,center +MAP +PPPP.... ....PPPPP. +PLP... .PPLLLPP +PPP ....................... PPPLLP +.. ............................ PPPP +. ............................... .... + ................................. .. +.................................... . + ................................... +. .................................. . +.. .............................. PP +.PPP .......................... PLP +.PLLP ..PLLP +.PPPP.. ....PPPP +ENDMAP +# Dungeon Description +REGION:(00,00,39,12),lit,"ordinary" +# Stairs +STAIR:(48,14),up +STAIR:(20,06),down +# Non diggable walls +NON_DIGGABLE:(00,00,39,12) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',random,random +MONSTER:'H',random,random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',random,random,hostile + +# +# The "goal" level for the quest. +# +# Here you meet Lord Surtur your nemesis monster. You have to +# defeat Lord Surtur in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Val-goal", 'L' +INIT_MAP: '.' , 'L' , true , true , lit , false +GEOMETRY:center,center +MAP +.L............................LLLLL +LLL.........LLLLL.LLLLL.........LLL +.LLL......LLLLLLLLLLLLLLL.......LL. +.LLL.....LLL|---------|LLL.....L... +..LL....LL|--.........--|LL.....LLL +.......LL|-...LLLLLLL...-|LL.....L. +.......LL|...LL.....LL...|LL....... +......LL|-..LL.......LL..-|LL...... +......LL|.................|LL...... +......LL|-..LL.......LL..-|LL...... +.......LL|...LL.....LL...|LL....... +.......LL|-...LLLLLLL...-|LL....... +..L.....LL|--.........--|LL.....LL. +..LL.....LLL|---------|LLL....LLLL. +..LLL.....LLLLLLLLLLLLLLL...LLLLL.. +.LLLL.......LLLLL.LLLLL.....LLLL... +..LL............................... +ENDMAP +# Dungeon Description +REGION:(00,00,34,16),lit,"ordinary" +# Stairs +# Note: The up stairs are *intentionally* off of the map. +STAIR:(45,10),up +# Non diggable walls +NON_DIGGABLE:(00,00,34,16) +# Drawbridges +DRAWBRIDGE:(17,02),south,open +DRAWBRIDGE:(17,14),north,open +# Objects +OBJECT:'(',"crystal ball",(17,08),blessed,5,"The Orb of Fate" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Traps +TRAP:"board",(13,08) +TRAP:"board",(21,08) +# Random traps +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"board",random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'H',"Lord Surtur",(17,08) +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',"fire ant",random +MONSTER:'a',random,random +MONSTER:'a',random,random +MONSTER:'H',"fire giant",(10,06),hostile +MONSTER:'H',"fire giant",(10,07),hostile +MONSTER:'H',"fire giant",(10,08),hostile +MONSTER:'H',"fire giant",(10,09),hostile +MONSTER:'H',"fire giant",(10,10),hostile +MONSTER:'H',"fire giant",(24,06),hostile +MONSTER:'H',"fire giant",(24,07),hostile +MONSTER:'H',"fire giant",(24,08),hostile +MONSTER:'H',"fire giant",(24,09),hostile +MONSTER:'H',"fire giant",(24,10),hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',"fire giant",random,hostile +MONSTER:'H',random,random,hostile + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +MAZE: "Val-fila" , 'I' +INIT_MAP: '.' , 'I' , true , true , lit, false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', random, random +MONSTER: 'H', "fire giant", random, hostile +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random + +MAZE: "Val-filb" , 'L' +INIT_MAP: '.' , 'L' , true , true , lit, false +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', "fire ant", random +MONSTER: 'a', random, random +MONSTER: 'H', "fire giant", random, hostile +MONSTER: 'H', "fire giant", random, hostile +MONSTER: 'H', "fire giant", random, hostile +# +TRAP: "fire", random +TRAP: "fire", random +TRAP: "fire", random +TRAP: "fire", random +TRAP: "fire", random +TRAP: random, random +TRAP: random, random diff --git a/dat/Wizard.des b/dat/Wizard.des new file mode 100644 index 0000000..14f99c9 --- /dev/null +++ b/dat/Wizard.des @@ -0,0 +1,439 @@ +# SCCS Id: @(#)Wizard.des 3.4 1992/07/11 +# Copyright (c) 1992 by David Cohrs +# NetHack may be freely redistributed. See license for details. +# +# The "start" level for the quest. +# +# Here you meet your (besieged) class leader, Neferet the Green +# and receive your quest assignment. +# +MAZE: "Wiz-strt",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:center,center +MAP +............................................................................ +.....................C....CC.C........................C..................... +..........CCC.....................CCC....................................... +........CC........-----------.......C.C...C...C....C........................ +.......C.....---------------------...C..C..C..C............................. +......C..C...------....\....------....C.....C............................... +........C...||....|.........|....||......................................... +.......C....||....|.........+....||......................................... +.......C...||---+--.........|....|||........................................ +......C....||...............|--S--||........................................ +...........||--+--|++----|---|..|.SS..........C......C...................... +........C..||.....|..|...|...|--|.||..CC..C.....C..........C................ +.......C...||.....|..|.--|.|.|....||.................C..C................... +.....C......||....|..|.....|.|.--||..C..C..........C...........}}}.......... +......C.C...||....|..-----.|.....||...C.C.C..............C....}}}}}}........ +.........C...------........|------....C..C.....C..CC.C......}}}}}}}}}}}..... +.........CC..---------------------...C.C..C.....CCCCC.C.......}}}}}}}}...... +.........C........-----------..........C.C.......CCC.........}}}}}}}}}...... +..........C.C.........................C............C...........}}}}}........ +......................CCC.C................................................. +ENDMAP +# Dungeon Description +REGION:(00,00,75,19),lit,"ordinary" +REGION:(35,00,49,03),unlit,"ordinary" +REGION:(43,12,49,16),unlit,"ordinary" +REGION:(19,11,33,15),unlit,"ordinary",unfilled,true +REGION:(30,10,31,10),unlit,"ordinary" +# Stairs +STAIR:(30,10),down +# Portal arrival point +BRANCH:(63,06,63,06),(0,0,0,0) +# Doors +DOOR:closed,(31,09) +DOOR:closed,(16,08) +DOOR:closed,(28,07) +DOOR:locked,(34,10) +DOOR:locked,(35,10) +DOOR:closed,(15,10) +DOOR:locked,(19,10) +DOOR:locked,(20,10) +# Neferet the Green, the quest leader +MONSTER:'@',"Neferet the Green",(23,05) +# The treasure of the quest leader +OBJECT:'(',"chest",(24,05) +# apprentice guards for the audience chamber +MONSTER:'@',"apprentice",(30,07) +MONSTER:'@',"apprentice",(24,06) +MONSTER:'@',"apprentice",(15,06) +MONSTER:'@',"apprentice",(15,12) +MONSTER:'@',"apprentice",(26,11) +MONSTER:'@',"apprentice",(27,11) +MONSTER:'@',"apprentice",(19,09) +MONSTER:'@',"apprentice",(20,09) +# Eels in the pond +MONSTER:';',"giant eel",(62,14) +MONSTER:';',"giant eel",(69,15) +MONSTER:';',"giant eel",(67,17) +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters on siege duty. +MONSTER: 'B',random,(60,09),hostile +MONSTER: 'W',random,(60,10),hostile +MONSTER: 'B',random,(60,11),hostile +MONSTER: 'B',random,(60,12),hostile +MONSTER: 'i',random,(60,13),hostile +MONSTER: 'B',random,(61,10),hostile +MONSTER: 'B',random,(61,11),hostile +MONSTER: 'B',random,(61,12),hostile +MONSTER: 'B',random,(35,03),hostile +MONSTER: 'i',random,(35,17),hostile +MONSTER: 'B',random,(36,17),hostile +MONSTER: 'B',random,(34,16),hostile +MONSTER: 'i',random,(34,17),hostile +MONSTER: 'W',random,(67,02),hostile +MONSTER: 'B',random,(10,19),hostile + +# +# The "locate" level for the quest. +# +# Here you have to find the Entrance to the Tower of Darkness to go +# further towards your assigned quest. +# + +MAZE: "Wiz-loca",' ' +FLAGS: hardfloor +GEOMETRY:center,center +MAP +............. ....................................................... +.............. .............}}}}}}}.}}}}}}}}}}}}}}}}}}}.}}}}}}}....... +.............. ..............}.................................}....... +.............. ..............}.---------S---------------------.}....... +............... .........C....}.|.............................|.}....... +............... ..........C....}.|.---------------------------.|.}....... +............... .........CCC.....|.|.........................|.|......... +................ ....C....CCC...}.|.|.---S-------------------.|.|.}....... +.......C..C..... .....C....CCC...}.|.|.|......+.......+......|.|.|.}....... +.............C..CC.....C....CCC...}.|.|.|......|-------|......|.|.|.}....... +................ ....C....CCC...}.|.|.|......|.......S......|.|.|.}....... +......C..C..... ....C....CCC...}.|.|.|......|-------|......|.|.|.}....... +............C.. ...C....CCC...}.|.|.|......+.......+......|.|.|.}....... +........C...... ....C....CCC...}.|.|.-----------------------.|.|.}....... +....C......C... ........CCC.....|.|.........................|.|......... +......C..C.... .........C....}.|.--------------------S------.|.}....... +.............. .........C....}.|.............................|.}....... +............. ..............}.-------------------------------.}....... +............. .............}.................................}....... +............. .............}}}}}}}.}}}}}}}}}}}}}}}}}}}.}}}}}}}....... +............. ....................................................... +ENDMAP +# Dungeon Description +REGION:(00,00,75,20),lit,"ordinary" +REGION:(37,04,65,16),unlit,"ordinary" +REGION:(41,08,46,12),lit,"ordinary" +REGION:(56,08,61,12),lit,"ordinary" +REGION:(48,08,54,08),unlit,"ordinary" +REGION:(48,12,54,12),unlit,"ordinary" +REGION:(48,10,54,10),unlit,"ordinary" + +# Doors +DOOR:locked,(45,03) +DOOR:locked,(43,07) +DOOR:locked,(58,15) +DOOR:locked,(55,10) +DOOR:locked,(55,08) +DOOR:locked,(55,12) +DOOR:locked,(47,08) +DOOR:locked,(47,12) +# Stairs +STAIR:(03,17),up +STAIR:(48,10),down +# Non diggable walls +NON_DIGGABLE:(00,00,75,20) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:"spiked pit",(24,02) +TRAP:"spiked pit",(07,10) +TRAP:"spiked pit",(23,05) +TRAP:"spiked pit",(26,19) +TRAP:"spiked pit",(72,02) +TRAP:"spiked pit",(72,12) +TRAP:"falling rock",(45,16) +TRAP:"falling rock",(65,13) +TRAP:"falling rock",(55,06) +TRAP:"falling rock",(39,11) +TRAP:"falling rock",(57,09) +TRAP:"magic",random +TRAP:"statue",random +TRAP:"statue",random +TRAP:"polymorph",random +TRAP:"anti magic",(53,10) +TRAP:"sleep gas",random +TRAP:"sleep gas",random +TRAP:"dart",random +TRAP:"dart",random +TRAP:"dart",random +# Random monsters. +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'i',random,random,hostile + +# +# The "goal" level for the quest. +# +# Here you meet the Dark One, your nemesis monster. You have to +# defeat the Dark One in combat to gain the artifact you have +# been assigned to retrieve. +# + +MAZE: "Wiz-goal", ' ' +GEOMETRY:center,center +MAP + + + + ------------- ------------- + |...........| |...........| + -------|...........-------------------...........| + |......S...........|..|..|..|..|..|..|...........| + |......|...........|..|..|..|..|..|..|...........| + |......|...........-F+-F+-F+-F+-F+-F+-...........| + --S----|...........S.................+...........| + |......|...........-F+-F+-F+-F+-F+-F+-...........| + |......|...........|..|..|..|..|..|..|...........| + |......|...........|..|..|..|..|..|..|...........| + -------|...........-------------------...........| + |...........| |...........| + ------------- ------------- + + + + +ENDMAP +# Dungeon Description +REGION:(13,10,18,12),unlit,"temple" +REGION:(13,06,18,08),lit,"ordinary" +REGION:(20,04,30,14),unlit,"ordinary" +REGION:(32,06,33,07),unlit,"ordinary" +REGION:(35,06,36,07),unlit,"ordinary" +REGION:(38,06,39,07),unlit,"ordinary" +REGION:(41,06,42,07),unlit,"ordinary" +REGION:(44,06,45,07),unlit,"ordinary" +REGION:(47,06,48,07),unlit,"ordinary" +REGION:(32,09,48,09),unlit,"ordinary" +REGION:(32,11,33,12),unlit,"ordinary" +REGION:(35,11,36,12),unlit,"ordinary" +REGION:(38,11,39,12),unlit,"ordinary" +REGION:(41,11,42,12),unlit,"ordinary" +REGION:(44,11,45,12),unlit,"ordinary" +REGION:(47,11,48,12),unlit,"ordinary" +REGION:(50,04,60,14),lit,"ordinary" +# Doors +DOOR:locked,(19,06) +DOOR:locked,(14,09) +DOOR:locked,(31,09) +DOOR:locked,(33,08) +DOOR:locked,(36,08) +DOOR:locked,(39,08) +DOOR:locked,(42,08) +DOOR:locked,(45,08) +DOOR:locked,(48,08) +DOOR:locked,(33,10) +DOOR:locked,(36,10) +DOOR:locked,(39,10) +DOOR:locked,(42,10) +DOOR:locked,(45,10) +DOOR:locked,(48,10) +DOOR:locked,(49,09) +# Stairs +STAIR:(55,05),up +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# The altar. This is not a shrine. +ALTAR:(16,11),noncoaligned,altar +# Objects +OBJECT:'"',"amulet of ESP",(16,11),blessed,0,"The Eye of the Aethiopica" +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'@',"Dark One",(16,11) +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'B',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'i',random,random,hostile +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'i',random,random,hostile +# Captive Monsters in the dungeon +MONSTER:'@',"rogue",(35,06),peaceful,"Pug" +MONSTER:'Y',"owlbear",(47,06),peaceful,asleep +MONSTER:'@',"wizard",(32,11),peaceful,asleep,"Newt" +MONSTER:'@',"Grey-elf",(44,11),peaceful +MONSTER:'H',"hill giant",(47,11),peaceful,asleep +MONSTER:'G',"gnomish wizard",(38,06),peaceful +MONSTER:'@',"prisoner",(35,11),peaceful +MONSTER:'@',"prisoner",(41,11),peaceful,asleep + +# +# The "fill" levels for the quest. +# +# These levels are used to fill out any levels not occupied by specific +# levels as defined above. "filla" is the upper filler, between the +# start and locate levels, and "fillb" the lower between the locate +# and goal levels. +# + +LEVEL: "Wiz-fila" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'i', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'i', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'B', "vampire bat", random +MONSTER: 'B', "vampire bat", random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'i', random, random, hostile +MONSTER: 'B', "vampire bat", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'i', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'B', "vampire bat", random + +RANDOM_CORRIDORS + +LEVEL: "Wiz-filb" +# +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random,random,random +MONSTER: 'i', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +OBJECT: random,random,random +MONSTER: 'X', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'i', random, random, hostile +MONSTER: 'B', "vampire bat", random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'i', random, random, hostile + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: 'B', "vampire bat", random + +RANDOM_CORRIDORS diff --git a/dat/bigroom.des b/dat/bigroom.des new file mode 100644 index 0000000..142ca85 --- /dev/null +++ b/dat/bigroom.des @@ -0,0 +1,436 @@ +# SCCS Id: @(#)bigroom.des 3.4 1990/04/15 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1990 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# These are the bigroom levels: +# + +MAZE:"bigrm-1",' ' +GEOMETRY:center,center +MAP +--------------------------------------------------------------------------- +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +--------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +REGION:(01,01,73,16),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,74,17) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random + +# Here, just play with the lighting... + +MAZE:"bigrm-2",' ' +GEOMETRY:center,center +MAP +--------------------------------------------------------------------------- +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +--------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +REGION:(01,01,23,06),lit,"ordinary" +REGION:(01,07,23,10),unlit,"ordinary" +REGION:(01,11,23,16),lit,"ordinary" +REGION:(24,01,50,06),unlit,"ordinary" +REGION:(24,07,50,10),lit,"ordinary" +REGION:(24,11,50,16),unlit,"ordinary" +REGION:(51,01,73,06),lit,"ordinary" +REGION:(51,07,73,10),unlit,"ordinary" +REGION:(51,11,73,16),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,74,17) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random + +# Now, let's get fancy... + +MAZE:"bigrm-3",' ' +GEOMETRY:center,center +MAP +--------------------------------------------------------------------------- +|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|..............---.......................................---..............| +|...............|.........................................|...............| +|.....|.|.|.|.|---|.|.|.|.|...................|.|.|.|.|.|---|.|.|.|.|.....| +|.....|-------- --------|...................|---------- --------|.....| +|.....|.|.|.|.|---|.|.|.|.|...................|.|.|.|.|.|---|.|.|.|.|.....| +|...............|.........................................|...............| +|..............---.......................................---..............| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.........................................................................| +|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.|.| +--------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +REGION:(01,01,73,16),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,74,17) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:random,random,(01,01) +MONSTER:random,random,(13,01) +MONSTER:random,random,(25,01) +MONSTER:random,random,(37,01) +MONSTER:random,random,(49,01) +MONSTER:random,random,(61,01) +MONSTER:random,random,(73,01) +MONSTER:random,random,(07,07) +MONSTER:random,random,(13,07) +MONSTER:random,random,(25,07) +MONSTER:random,random,(37,07) +MONSTER:random,random,(49,07) +MONSTER:random,random,(61,07) +MONSTER:random,random,(67,07) +MONSTER:random,random,(07,09) +MONSTER:random,random,(13,09) +MONSTER:random,random,(25,09) +MONSTER:random,random,(37,09) +MONSTER:random,random,(49,09) +MONSTER:random,random,(61,09) +MONSTER:random,random,(67,09) +MONSTER:random,random,(01,16) +MONSTER:random,random,(13,16) +MONSTER:random,random,(25,16) +MONSTER:random,random,(37,16) +MONSTER:random,random,(49,16) +MONSTER:random,random,(61,16) +MONSTER:random,random,(73,16) +MAZE:"bigrm-4",' ' +GEOMETRY:center,center +MAP +----------- ----------- +|.........| |.........| +|.........|-----------| |-----------|.........| +|-|...................|----------| |----------|...................|-| + -|.............................|-------|.............................|- + -|.................................................................|- + -|...............................................................|- + -|.............................................................|- + -|...........................................................|- + -|...........................................................|- + -|.............................................................|- + -|...............................................................|- + -|.................................................................|- + -|.............................|-------|.............................|- +|-|...................|----------| |----------|...................|-| +|.........|-----------| |-----------|.........| +|.........| |.........| +----------- ----------- +ENDMAP +# Dungeon Description +REGION:(01,01,73,16),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,74,17) +# Fountains +FOUNTAIN:(05,02) +FOUNTAIN:(05,15) +FOUNTAIN:(69,02) +FOUNTAIN:(69,15) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random + +# Try an oval room... + +MAZE:"bigrm-5",' ' +GEOMETRY:center,center +MAP + ------------------ + ---------................--------- + -------................................------- + ------............................................------ + ----......................................................---- + ---............................................................--- + ---................................................................--- +---....................................................................--- +|........................................................................| +|........................................................................| +|........................................................................| +---....................................................................--- + ---................................................................--- + ---............................................................--- + ----......................................................---- + ------............................................------ + -------................................------- + ---------................--------- + ------------------ +ENDMAP +# Dungeon Description +REGION:(00,00,72,18),lit,"ordinary" +# Stairs +STAIR:random,up +STAIR:random,down +# Non diggable walls +NON_DIGGABLE:(00,00,72,18) +# Objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random diff --git a/dat/castle.des b/dat/castle.des new file mode 100644 index 0000000..346eecc --- /dev/null +++ b/dat/castle.des @@ -0,0 +1,241 @@ +# SCCS Id: @(#)castle.des 3.4 2002/05/02 +# Copyright (c) 1989 by Jean-Christophe Collet +# NetHack may be freely redistributed. See license for details. +# +# This is the stronghold level : +# there are several ways to enter it : +# - opening the drawbridge (wand of opening, knock spell, playing +# the appropriate tune) +# +# - enter via the back entry (this suppose a ring of levitation, boots +# of water walking, etc.) +# +# Note : If you don't play the right tune, you get indications like in the +# MasterMind game... +# +# To motivate the player : there are 4 storerooms (armors, weapons, food and +# gems) and a wand of wishing in one of the 4 towers... + +MAZE:"castle",random +FLAGS: noteleport +GEOMETRY:center,center +MAP +}}}}}}}}}.............................................}}}}}}}}} +}-------}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-------} +}|.....|-----------------------------------------------|.....|} +}|.....+...............................................+.....|} +}-------------------------------+-----------------------------} +}}}}}}|........|..........+...........|.......S.S.......|}}}}}} +.....}|........|..........|...........|.......|.|.......|}..... +.....}|........------------...........---------S---------}..... +.....}|...{....+..........+.........\.S.................+...... +.....}|........------------...........---------S---------}..... +.....}|........|..........|...........|.......|.|.......|}..... +}}}}}}|........|..........+...........|.......S.S.......|}}}}}} +}-------------------------------+-----------------------------} +}|.....+...............................................+.....|} +}|.....|-----------------------------------------------|.....|} +}-------}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-------} +}}}}}}}}}.............................................}}}}}}}}} +ENDMAP + +# Random registers initialisation +RANDOM_OBJECTS:'[',')','*','%' +RANDOM_PLACES:(04,02),(58,02),(04,14),(58,14) +RANDOM_MONSTERS:'L','N','E','H','M','O','R','T','X','Z' + +TELEPORT_REGION:levregion(01,00,10,20),(1,1,61,15),down +TELEPORT_REGION:levregion(69,00,79,20),(1,1,61,15),up +STAIR:levregion(01,00,10,20),(0,0,62,16),up +FOUNTAIN:(10,08) +# Doors +DOOR:closed,(07,03) +DOOR:closed,(55,03) +DOOR:locked,(32,04) +DOOR:locked,(26,05) +DOOR:locked,(46,05) +DOOR:locked,(48,05) +DOOR:locked,(47,07) +DOOR:closed,(15,08) +DOOR:closed,(26,08) +DOOR:locked,(38,08) +DOOR:locked,(56,08) +DOOR:locked,(47,09) +DOOR:locked,(26,11) +DOOR:locked,(46,11) +DOOR:locked,(48,11) +DOOR:locked,(32,12) +DOOR:closed,(07,13) +DOOR:closed,(55,13) +# The drawbridge +DRAWBRIDGE:(05,08),east,closed +# Storeroom number 1 +OBJECT:object[0],random,(39,05) +OBJECT:object[0],random,(40,05) +OBJECT:object[0],random,(41,05) +OBJECT:object[0],random,(42,05) +OBJECT:object[0],random,(43,05) +OBJECT:object[0],random,(44,05) +OBJECT:object[0],random,(45,05) +OBJECT:object[0],random,(39,06) +OBJECT:object[0],random,(40,06) +OBJECT:object[0],random,(41,06) +OBJECT:object[0],random,(42,06) +OBJECT:object[0],random,(43,06) +OBJECT:object[0],random,(44,06) +OBJECT:object[0],random,(45,06) +# Storeroom number 2 +OBJECT:object[1],random,(49,05) +OBJECT:object[1],random,(50,05) +OBJECT:object[1],random,(51,05) +OBJECT:object[1],random,(52,05) +OBJECT:object[1],random,(53,05) +OBJECT:object[1],random,(54,05) +OBJECT:object[1],random,(55,05) +OBJECT:object[1],random,(49,06) +OBJECT:object[1],random,(50,06) +OBJECT:object[1],random,(51,06) +OBJECT:object[1],random,(52,06) +OBJECT:object[1],random,(53,06) +OBJECT:object[1],random,(54,06) +OBJECT:object[1],random,(55,06) +# Storeroom number 3 +OBJECT:object[2],random,(39,10) +OBJECT:object[2],random,(40,10) +OBJECT:object[2],random,(41,10) +OBJECT:object[2],random,(42,10) +OBJECT:object[2],random,(43,10) +OBJECT:object[2],random,(44,10) +OBJECT:object[2],random,(45,10) +OBJECT:object[2],random,(39,11) +OBJECT:object[2],random,(40,11) +OBJECT:object[2],random,(41,11) +OBJECT:object[2],random,(42,11) +OBJECT:object[2],random,(43,11) +OBJECT:object[2],random,(44,11) +OBJECT:object[2],random,(45,11) +# Storeroom number 4 +OBJECT:object[3],random,(49,10) +OBJECT:object[3],random,(50,10) +OBJECT:object[3],random,(51,10) +OBJECT:object[3],random,(52,10) +OBJECT:object[3],random,(53,10) +OBJECT:object[3],random,(54,10) +OBJECT:object[3],random,(55,10) +OBJECT:object[3],random,(49,11) +OBJECT:object[3],random,(50,11) +OBJECT:object[3],random,(51,11) +OBJECT:object[3],random,(52,11) +OBJECT:object[3],random,(53,11) +OBJECT:object[3],random,(54,11) +OBJECT:object[3],random,(55,11) +# THE WAND OF WISHING in 1 of the 4 towers +CONTAINER:'(',"chest",place[0] +OBJECT:'/',"wishing",contained +# Prevent monsters from eating it. (@'s never eat objects) +ENGRAVING:place[0],burn,"Elbereth" +# The treasure of the lord +OBJECT:'(',"chest",(37,08) +# Traps +TRAP:"trap door",(40,08) +TRAP:"trap door",(44,08) +TRAP:"trap door",(48,08) +TRAP:"trap door",(52,08) +TRAP:"trap door",(55,08) +# Soldiers guarding the entry hall +MONSTER:'@',"soldier",(08,06) +MONSTER:'@',"soldier",(09,05) +MONSTER:'@',"soldier",(11,05) +MONSTER:'@',"soldier",(12,06) +MONSTER:'@',"soldier",(08,10) +MONSTER:'@',"soldier",(09,11) +MONSTER:'@',"soldier",(11,11) +MONSTER:'@',"soldier",(12,10) +MONSTER:'@',"lieutenant",(09,08) +# Soldiers guarding the towers +MONSTER:'@',"soldier",(03,02) +MONSTER:'@',"soldier",(05,02) +MONSTER:'@',"soldier",(57,02) +MONSTER:'@',"soldier",(59,02) +MONSTER:'@',"soldier",(03,14) +MONSTER:'@',"soldier",(05,14) +MONSTER:'@',"soldier",(57,14) +MONSTER:'@',"soldier",(59,14) +# The four dragons that are guarding the storerooms +MONSTER:'D',random,(47,05) +MONSTER:'D',random,(47,06) +MONSTER:'D',random,(47,10) +MONSTER:'D',random,(47,11) +# Sea monsters in the moat +MONSTER:';',"giant eel",(05,07) +MONSTER:';',"giant eel",(05,09) +MONSTER:';',"giant eel",(57,07) +MONSTER:';',"giant eel",(57,09) +MONSTER:';',"shark",(05,00) +MONSTER:';',"shark",(05,16) +MONSTER:';',"shark",(57,00) +MONSTER:';',"shark",(57,16) +# The throne room and the court monsters +MONSTER:monster[0],random,(27,05) +MONSTER:monster[1],random,(30,05) +MONSTER:monster[2],random,(33,05) +MONSTER:monster[3],random,(36,05) +MONSTER:monster[4],random,(28,06) +MONSTER:monster[5],random,(31,06) +MONSTER:monster[6],random,(34,06) +MONSTER:monster[7],random,(37,06) +MONSTER:monster[8],random,(27,07) +MONSTER:monster[9],random,(30,07) +MONSTER:monster[0],random,(33,07) +MONSTER:monster[1],random,(36,07) +MONSTER:monster[2],random,(28,08) +MONSTER:monster[3],random,(31,08) +MONSTER:monster[4],random,(34,08) +MONSTER:monster[5],random,(27,09) +MONSTER:monster[6],random,(30,09) +MONSTER:monster[7],random,(33,09) +MONSTER:monster[8],random,(36,09) +MONSTER:monster[9],random,(28,10) +MONSTER:monster[0],random,(31,10) +MONSTER:monster[1],random,(34,10) +MONSTER:monster[2],random,(37,10) +MONSTER:monster[3],random,(27,11) +MONSTER:monster[4],random,(30,11) +MONSTER:monster[5],random,(33,11) +MONSTER:monster[6],random,(36,11) +# MazeWalks +MAZEWALK:(00,10),west +MAZEWALK:(62,06),east +# Non diggable walls +NON_DIGGABLE:(00,00,62,16) +# Subrooms: +# Entire castle area +REGION:(00,00,62,16),unlit,"ordinary" +# Courtyards +REGION:(00,05,05,11),lit,"ordinary" +REGION:(57,05,62,11),lit,"ordinary" +# Throne room +REGION:(27,05,37,11),lit,"throne",unfilled +# Antechamber +REGION:(07,05,14,11),lit,"ordinary" +# Storerooms +REGION:(39,05,45,06),lit,"ordinary" +REGION:(39,10,45,11),lit,"ordinary" +REGION:(49,05,55,06),lit,"ordinary" +REGION:(49,10,55,11),lit,"ordinary" +# Corners +REGION:(02,02,06,03),lit,"ordinary" +REGION:(56,02,60,03),lit,"ordinary" +REGION:(02,13,06,14),lit,"ordinary" +REGION:(56,13,60,14),lit,"ordinary" +# Barracks +REGION:(16,05,25,06),lit,"barracks" +REGION:(16,10,25,11),lit,"barracks" +# Hallways +REGION:(08,03,54,03),unlit,"ordinary" +REGION:(08,13,54,13),unlit,"ordinary" +REGION:(16,08,25,08),unlit,"ordinary" +REGION:(39,08,55,08),unlit,"ordinary" +# Storeroom alcoves +REGION:(47,05,47,06),unlit,"ordinary" +REGION:(47,10,47,11),unlit,"ordinary" diff --git a/dat/cmdhelp b/dat/cmdhelp new file mode 100644 index 0000000..af4b50e --- /dev/null +++ b/dat/cmdhelp @@ -0,0 +1,121 @@ +^ Show the type of a trap +^[ Cancel command +^A Redo the previous command +^C Quit the game +^D Kick something (usually a door, chest, or box) +^E Search a room (available in debug mode only) +^F Map the level (available in debug mode only) +^G Create a monster (available in debug mode only) +^I Identify all items (available in debug mode only) +^O Show location of special levels (available in debug mode only) +^P Toggle through previously displayed game messages +^R Redraw screen +^T Teleport around level +^V Teleport between levels (available in debug mode only) +^W Wish (available in debug mode only) +^X Show your attributes (intrinsic ones included in debug or explore mode) +^Z Suspend game (only if defined) +a Apply (use) a tool +A Remove all armor +b Go southwest 1 space +B Go southwest until you are on top of something +^B Go southwest until you are near something +c Close a door +C Call (name) a particular monster +d Drop an item +D Drop specific item types +e Eat something +E Engrave writing on the floor +f Fire ammunition from quiver +F Followed by direction, fight a monster (even if you don't sense it) +g Followed by direction, move until you are near something +G Followed by direction, same as control-direction +h Go west 1 space (if number_pad is on, display help message) +H Go west until you are on top of something +^H Go west until you are near something +i Show your inventory +I Inventory specific item types +j Go south 1 space (or if number_pad is on, jump to another location) +J Go south until you are on top of something +^J Go south until you are near something +k Go north 1 space (or if number_pad is on, kick something) +K Go north until you are on top of something +^K Go north until you are near something +l Go east 1 space (or if number_pad is on, loot a box on the floor) +L Go east until you are on top of something +^L Go east until you are near something +m Followed by direction, move without picking anything up or fighting +M Followed by direction, move a distance without picking anything up +n Go southeast 1 space +N Go southeast until you are on something (if number_pad, name an object) +^N Go southeast until you are near something +o Open a door +O Show option settings, possibly change them +p Pay your shopping bill +P Put on an accessory (ring, amulet, etc) +q Quaff (drink) something (potion, water, etc) +Q Select ammunition for quiver +r Read a scroll or spellbook +R Remove an accessory (ring, amulet, etc) +s Search for traps and secret doors +S Save the game +t Throw something +T Take off one piece of armor +u Go northeast 1 space (or if number_pad is on, untrap something) +U Go northeast until you are on top of something +^U Go northeast until you are near something +v Show version +V Show long version and game history +w Wield (put in use) a weapon +W Wear a piece of armor +x Swap wielded and secondary weapons +X Enter explore (discovery) mode (only if defined) +y Go northwest 1 space +Y Go northwest until you are on top of something +^Y Go northwest until you are near something +z Zap a wand +Z Zap (cast) a spell +< Go up a staircase +> Go down a staircase +/ Show what type of thing a symbol corresponds to +? Give a help message +& Tell what a command does +! Do a shell escape (only if defined) +\ Show what object types have been discovered +_ Travel via a shortest-path algorithm to a point on the map +. Rest one move while doing nothing + Rest one move while doing nothing (if rest_on_space option is on) +: Look at what is on the floor +; Show what type of thing a map symbol on the level corresponds to +, Pick up things at the current location +@ Toggle the pickup option on/off +) Show the weapon currently wielded +[ Show the armor currently worn += Show the ring(s) currently worn +" Show the amulet currently worn +( Show the tools currently in use +* Show all equipment in use (combination of the ),[,=,",( commands) +$ Count your gold ++ List known spells +# Perform an extended command +M-? Display extended command help (if the platform allows this) +M-2 Toggle two-weapon combat (unless number_pad is enabled) +M-a Adjust inventory letters +M-c Talk to someone +M-d Dip an object into something +M-e Advance or check weapons skills +M-f Force a lock +M-i Invoke an object's special powers +M-j Jump to another location +M-l Loot a box on the floor +M-m Use a monster's special ability +M-n Name an item or type of object +M-o Offer a sacrifice to the gods +M-p Pray to the gods for help +M-q Quit +M-r Rub a lamp +M-s Sit down +M-t Turn undead +M-u Untrap something (trap, door, or chest) +M-v Print compile time options for this version of NetHack +M-w Wipe off your face diff --git a/dat/data.base b/dat/data.base new file mode 100644 index 0000000..6fc8604 --- /dev/null +++ b/dat/data.base @@ -0,0 +1,4411 @@ +# SCCS Id: @(#)data.base 3.4 2003/07/23 +# Copyright (c) 1994, 1995, 1996 by the NetHack Development Team +# Copyright (c) 1994 by Boudewijn Wayers +# NetHack may be freely redistributed. See license for details. +# +# This is the source file for the "data" file generated by `makedefs -d'. +# A line starting with a # is a comment and is ignored by makedefs. +# Any other line not starting with whitespace is a creature or an item. +# +# Each entry should be comprised of: +# the thing/person being described on a line by itself, in lowercase; +# on each succeeding line a description. +# +# If the first character of a key field is "~", then anything which matches +# the rest of that key will be treated as if it did not match any of the +# following keys for that entry. For instance, `~orc ??m*' preceding `orc*' +# prevents "orc mummy" and "orc zombie" from matching. +# +abbot + For it had been long apparent to Count Landulf that nothing + could be done with his seventh son Thomas, except to make him + an Abbot or something of that kind. Born in 1226, he had from + childhood a mysterious objection to becoming a predatory eagle, + or even to taking an ordinary interest in falconry or tilting + or any other gentlemanly pursuits. He was a large and heavy and + quiet boy, and phenomenally silent, scarcely opening his mouth + except to say suddenly to his schoolmaster in an explosive + manner, "What is God?" The answer is not recorded but it is + probable that the asker went on worrying out answers for himself. + [ The Runaway Abbot, by G. K. Chesterton ] +aclys +aklys + A short studded or spiked club attached to a cord allowing + it to be drawn back to the wielder after having been thrown. + It should not be confused with the atlatl, which is a device + used to throw spears for longer distances. +aleax + Said to be a doppelganger sent to inflict divine punishment + for alignment violations. +*altar + Altars are of three types: + 1. In Temples. These are for Sacrifices [...]. The stone + top will have grooves for blood, and the whole will be covered + with _dry brown stains of a troubling kind_ from former + Sacrifices. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] + + To every man upon this earth + Death cometh soon or late; + And how can man die better + Than facing fearful odds + For the ashes of his fathers + And the temples of his gods? + [ Lays of Ancient Rome, by Thomas B. Macaulay ] +amaterasu omikami + The Shinto sun goddess, Amaterasu Omikami is the central + figure of Shintoism and the ancestral deity of the imperial + house. One of the daughters of the primordial god Izanagi + and said to be his favourite offspring, she was born from + his left eye. + [ Encyclopedia of Gods, by Michael Jordan ] +amber* + "Tree sap," Wu explained, "often flows over insects and traps + them. The insects are then perfectly preserved within the + fossil. One finds all kinds of insects in amber - including + biting insects that have sucked blood from larger animals." + [ Jurassic Park, by Michael Crichton ] +*amnesia +maud + Get thee hence, nor come again, + Mix not memory with doubt, + Pass, thou deathlike type of pain, + Pass and cease to move about! + 'Tis the blot upon the brain + That will show itself without. + ... + For, Maud, so tender and true, + As long as my life endures + I feel I shall owe you a debt, + That I never can hope to pay; + And if ever I should forget + That I owe this debt to you + And for your sweet sake to yours; + O then, what then shall I say? - + If ever I should forget, + May God make me more wretched + Than ever I have been yet! + [ Maud, And Other Poems by Alfred, Lord Tennyson ] +~amulet of yendor +*amulet +amulet of * + "The complete Amulet can keep off all the things that make + people unhappy -- jealousy, bad temper, pride, disagreeableness, + greediness, selfishness, laziness. Evil spirits, people called + them when the Amulet was made. Don't you think it would be nice + to have it?" + "Very," said the children, quite without enthusiasm. + "And it can give you strength and courage." + "That's better," said Cyril. + "And virtue." + "I suppose it's nice to have that," said Jane, but not with much + interest. + "And it can give you your heart's desire." + "Now you're talking," said Robert. + [ The Story of the Amulet, by Edith Nesbit ] +amulet of yendor + This mysterious talisman is the object of your quest. It is + said to possess powers which mere mortals can scarcely + comprehend, let alone utilize. The gods will grant the gift of + immortality to the adventurer who can deliver it from the + depths of Moloch's Sanctum and offer it on the appropriate high + altar on the Astral Plane. +angel* + He answered and said unto them, he that soweth the good seed + is the Son of man; the field is the world, and the good seed + are the children of the kingdom; but the weeds are the + children of the wicked one; the enemy that sowed them is the + devil; the harvest is the end of the world; and the reapers + are the angels. As therefore the weeds are gathered and + burned in the fire; so shall it be in the end of this world. + [...] So shall it be at the end of the world; the angels + shall come forth, and sever the wicked from among the just, + and shall cast them into the furnace of fire; there shall be + wailing and gnashing of teeth. + [ The Gospel According to Matthew, 13:37-42, 49-50 ] +anhur + An Egyptian god of war and a great hunter, few gods can match + his fury. Unlike many gods of war, he is a force for good. + The wrath of Anhur is slow to come, but it is inescapable + once earned. Anhur is a mighty figure with four arms. He + is often seen with a powerful lance that requires both of + his right arms to wield and which is tipped with a fragment + of the sun. He is married to Mehut, a lion-headed goddess. +ankh-morpork + The twin city of Ankh-Morpork, foremost of all the cities + bounding the Circle Sea, was as a matter of course the home + of a large number of gangs, thieves' guilds, syndicates and + similar organisations. This was one of the reasons for its + wealth. Most of the humbler folk on the widdershin side of + the river, in Morpork's mazy alleys, supplemented their + meagre incomes by filling some small role for one or other + of the competing gangs. + [ The Colour of Magic by Terry Pratchett ] +anshar + A primordial Babylonian-Akkadian deity, Anshar is mentioned + in the Babylonian creation epic _Enuma Elish_ as one of a + pair of offspring (with Kishar) of Lahmu and Lahamu. Anshar + is linked with heaven while Kishar is identified with earth. + [ Encyclopedia of Gods, by Michael Jordan ] +ant +* ant + This giant variety of the ordinary ant will fight just as + fiercely as its small, distant cousin. Various varieties + exist, and they are known and feared for their relentless + persecution of their victims. +anu + Anu was the Babylonian god of the heavens, the monarch of + the north star. He was the oldest of the Babylonian gods, + the father of all gods, and the ruler of heaven and destiny. + Anu features strongly in the _atiku_ festival in + Babylon, Uruk and other cities. +ape +* ape + The most highly evolved of all the primates, as shown by + all their anatomical characters and particularly the + development of the brain. Both arboreal and terrestrial, + the apes have the forelimbs much better developed than + the hind limbs. Tail entirely absent. Growth is slow + and sexual maturity reached at quite an advanced age. + [ A Field Guide to the Larger Mammals of Africa by Dorst ] + + Aldo the gorilla had a plan. It was a good plan. It was + right. He knew it. He smacked his lips in anticipation as + he thought of it. Yes. Apes should be strong. Apes should + be masters. Apes should be proud. Apes should make the + Earth shake when they walked. Apes should _rule_ the Earth. + [ Battle for the Planet of the Apes, + by David Gerrold ] +apple + NEWTONIAN, adj. Pertaining to a philosophy of the universe + invented by Newton, who discovered that an apple will fall + to the ground, but was unable to say why. His successors + and disciples have advanced so far as to be able to say + when. + [ The Devil's Dictionary, by Ambrose Bierce ] +archeologist +* archeologist + Archeology is the search for fact, not truth. [...] + So forget any ideas you've got about lost cities, exotic travel, + and digging up the world. We do not follow maps to buried + treasure, and X never, ever, marks the spot. + [ Indiana Jones and the Last Crusade ] +archon + Archons are the predominant inhabitants of the heavens. + However unusual their appearance, they are not generally + evil. They are beings at peace with themselves and their + surroundings. +arioch + Arioch, the patron demon of Elric's ancestors; one of the most + powerful of all the Dukes of Hell, who was called Knight of + the Swords, Lord of the Seven Darks, Lord of the Higher Hell + and many more names besides. + [ Elric of Melnibone, by Michael Moorcock ] +*arrow + I shot an arrow into the air, + It fell to earth, I knew not where; + For, so swiftly it flew, the sight + Could not follow it in its flight. + + I breathed a song into the air, + It fell to earth, I knew not where; + For who has sight so keen and strong + That it can follow the flight of song? + + Long, long afterward, in an oak + I found the arrow still unbroke; + And the song, from beginning to end, + I found again in the heart of a friend. + [ The Arrow and the Song, + by Henry Wadsworth Longfellow ] +ashikaga takauji + Ashikaga Takauji was a daimyo of the Minamoto clan who + joined forces with the Go-Daigo to defeat the Hojo armies. + Later when Go-Daigo attempted to reduce the powers of the + samurai clans he rebelled against him. He defeated Go- + Daigo and established the emperor Komyo on the throne. + Go-Daigo eventually escaped and established another + government in the town of Yoshino. This period of dual + governments was known as the Nambokucho. + [ Samurai - The Story of a Warrior Tradition, by Cook ] +asmodeus + It is said that Asmodeus is the overlord over all of hell. + His appearance, unlike many other demons and devils, is + human apart from his horns and tail. He can freeze flesh + with a touch. +athame + The consecrated ritual knife of a Wiccan initiate (one of + four basic tools, together with the wand, chalice and + pentacle). Traditionally, the athame is a double-edged, + black-handled, cross-hilted dagger of between six and + eighteen inches length. +athen* + Athene was the offspring of Zeus, and without a mother. She + sprang forth from his head completely armed. Her favourite + bird was the owl, and the plant sacred to her is the olive. + [ Bulfinch's Mythology by Thomas Bulfinch ] +axolotl + A mundane salamander, harmless. +bag +bag of * +sack + "Now, this third handkerchief," Mein Herr proceeded, "has also + four edges, which you can trace continuously round and round: + all you need do is to join its four edges to the four edges of + the opening. The Purse is then complete, and its outer + surface--" + "I see!" Lady Muriel eagerly interrupted. "Its outer surface + will be continuous with its inner surface! But it will take + time. I'll sew it up after tea." She laid aside the bag, and + resumed her cup of tea. "But why do you call it Fortunatus's + Purse, Mein Herr?" + The dear old man beamed upon her, with a jolly smile, looking + more exactly like the Professor than ever. "Don't you see, + my child--I should say Miladi? Whatever is inside that Purse, + is outside it; and whatever is outside it, is inside it. So + you have all the wealth of the world in that leetle Purse!" + [ Sylvie and Bruno Concluded, by Lewis Carroll ] +b*lzebub + The "lord of the flies" is a translation of the Hebrew + Ba'alzevuv (Beelzebub in Greek). It has been suggested that + it was a mistranslation of a mistransliterated word which + gave us this pungent and suggestive name of the Devil, a + devil whose name suggests that he is devoted to decay, + destruction, demoralization, hysteria and panic... + [ Notes on _Lord of the Flies_, by E. L. Epstein ] +balrog + ... It came to the edge of the fire and the light faded as + if a cloud had bent over it. Then with a rush it leaped + the fissure. The flames roared up to greet it, and wreathed + about it; and a black smoke swirled in the air. Its streaming + mane kindled, and blazed behind it. In its right hand + was a blade like a stabbing tongue of fire; in its left it + held a whip of many thongs. + 'Ai, ai!' wailed Legolas. 'A Balrog! A Balrog is come!' + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +baluchitherium +titanothere + Extinct rhinos include a variety of forms, the most + spectacular being _Baluchitherium_ from the Oligocene of + Asia, which is the largest known land mammal. Its body, 18 + feet high at the shoulder and carried on massive limbs, + allowed the 4-foot-long head to browse on the higher branches + of trees. Though not as enormous, the titanotheres of the + early Tertiary were also large perissodactyls, _Brontotherium_ + of the Oligocene being 8 feet high at the shoulder. + [ Prehistoric Animals, by Barry Cox ] +banana + He took another step and she cocked her right wrist in + viciously. She heard the spring click. Weight slapped into + her hand. + "Here!" she shrieked hysterically, and brought her arm up in + a hard sweep, meaning to gut him, leaving him to blunder + around the room with his intestines hanging out in steaming + loops. Instead he roared laughter, hands on his hips, + flaming face cocked back, squeezing and contorting with great + good humor. + "Oh, my dear!" he cried, and went off into another gale of + laughter. + She looked stupidly down at her hand. It held a firm yellow + banana with a blue and white Chiquita sticker on it. She + dropped it, horrified, to the carpet, where it became a + sickly yellow grin, miming Flagg's own. + "You'll tell," he whispered. "Oh yes indeed you will." + And Dayna knew he was right. + [ The Stand, by Stephen King ] +barbarian +* barbarian + They dressed alike -- in buckskin boots, leathern breeks and + deerskin shirts, with broad girdles that held axes and short + swords; and they were all gaunt and scarred and hard-eyed; + sinewy and taciturn. + They were wild men, of a sort, yet there was still a wide + gulf between them and the Cimmerian. They were sons of + civilization, reverted to a semi-barbarism. He was a + barbarian of a thousand generations of barbarians. They had + acquired stealth and craft, but he had been born to these + things. He excelled them even in lithe economy of motion. + They were wolves, but he was a tiger. + [ Conan - The Warrior, by Robert E. Howard ] +barbed devil + Barbed devils lack any real special abilities, though they + are quite difficult to kill. +*bat + A bat, flitting in the darkness outside, took the wrong turn + as it made its nightly rounds and came in through the window + which had been left healthfully open. It then proceeded to + circle the room in the aimless fat-headed fashion habitual + with bats, who are notoriously among the less intellectually + gifted of God's creatures. Show me a bat, says the old + proverb, and I will show you something that ought to be in + some kind of a home. + [ A Pelican at Blandings, by P. G. Wodehouse ] +*bee + This giant variety of its useful normal cousin normally + appears in small groups, looking for raw material to produce + the royal jelly needed to feed their queen. On rare + occasions, one may stumble upon a bee-hive, in which the + queen bee is being well provided for, and guarded against + intruders. +*beetle + [ The Creator ] has an inordinate fondness for beetles. + [ attributed to biologist J.B.S. Haldane ] + + The common name for the insects with wings shaped like + shields (_Coleoptera_), one of the ten sub-species into + which the insects are divided. They are characterized by + the shields (the front pair of wings) under which the back + wings are folded. + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] +bell of opening + "A bell, book and candle job." + The Bursar sighed. "We tried that, Archchancellor." + The Archchancellor leaned towards him. + "Eh?" he said. + "I _said_, we tried that Archchancellor," said the Bursar loudly, + directing his voice at the old man's ear. "After dinner, you + remember? We used Humptemper's _Names of the Ants_ and rang Old + Tom."* + "Did we, indeed. Worked, did it?" + "_No_, Archchancellor." + + * Old Tom was the single cracked bronze bell in the University + bell tower. + [ Eric, by Terry Pratchett ] +blindfold + The blindfolding was performed by binding a piece of the + yellowish linen whereof those of the Amahagger who condescended + to wear anything in particular made their dresses tightly round + the eyes. This linen I afterwards discovered was taken from the + tombs, and was not, as I had first supposed, of native + manufacture. The bandage was then knotted at the back of the + head, and finally brought down again and the ends bound under + the chin to prevent its slipping. Ustane was, by the way, also + blindfolded, I do not know why, unless it was from fear that she + should impart the secrets of the route to us. + [ She, by H. Rider Haggard ] +blind io + On this particular day Blind Io, by dint of constant vigilance + the chief of the gods, sat with his chin on his hand + and looked at the gaming board on the red marble table in + front of him. Blind Io had got his name because, where his + eye sockets should have been, there were nothing but two + areas of blank skin. His eyes, of which he had an impressively + large number, led a semi-independent life of their + own. Several were currently hovering above the table. + [ The Colour of Magic, by Terry Pratchett ] +* blob +gelatinous cube +ooze +* ooze +*pudding +* slime + These giant amoeboid creatures look like nothing more than + puddles of slime, but they both live and move, feeding on + metal or wood as well as the occasional dungeon explorer to + supplement their diet. + + But we were not on a station platform. We were on the track ahead + as the nightmare, plastic column of fetid black iridescence oozed + tightly onward through its fifteen-foot sinus, gathering unholy + speed and driving before it a spiral, re-thickening cloud of the + pallid abyss vapor. It was a terrible, indescribable thing vaster + than any subway train -- a shapeless congeries of protoplasmic + bubbles, faintly self-luminous, and with myriads of temporary eyes + forming and unforming as pustules of greenish light all over the + tunnel-filling front that bore down upon us, crushing the frantic + penguins and slithering over the glistening floor that it and its + kind had swept so evilly free of all litter. + [ At the Mountains of Madness, by H.P. Lovecraft ] +bone devil + Bone devils attack with weapons and with a great hooked tail + which causes a loss of strength to those they sting. +book of the dead +candelabrum* +*candle + Faustus: Come on Mephistopheles. What shall we do? + Mephistopheles: Nay, I know not. We shall be cursed with bell, + book, and candle. + Faustus: How? Bell, book, and candle, candle, book, and bell, + Forward and backward, to curse Faustus to hell. + Anon you shall hear a hog grunt, a calf bleat, and an ass bray, + Because it is Saint Peter's holy day. + (Enter all the Friars to sing the dirge) + [ Doctor Faustus and Other Plays, by Christopher Marlowe ] +*boot* + In Fantasyland these are remarkable in that they seldom or + never wear out and are suitable for riding or walking in + without the need of Socks. Boots never pinch, rub, or get + stones in them; nor do nails stick upwards into the feet from + the soles. They are customarily mid-calf length or knee-high, + slip on and off easily and never smell of feet. Unfortunately, + the formula for making this splendid footwear is a closely + guarded secret, possibly derived from nonhumans (see Dwarfs, + Elves, and Gnomes). + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +boulder + I worked the lever well under, and stretched my back; the end + of the stone rose up, and I kicked the fulcrum under. Then, + when I was going to bear down, I remembered there was + something to get out from below; when I let go of the lever, + the stone would fall again. I sat down to think, on the root + of the oak tree; and, seeing it stand about the ground, I saw + my way. It was lucky I had brought a longer lever. It would + just reach to wedge under the oak root. + Bearing it down so far would have been easy for a heavy man, + but was a hard fight for me. But this time I meant to do it + if it killed me, because I knew it could be done. Twice I + got it nearly there, and twice the weight bore it up again; + but when I flung myself on it the third time, I heard in my + ears the sea-sound of Poseidon. Then I knew this time I + would do it; and so I did. + [ The King Must Die, by Mary Renault ] +~*longbow of diana +bow +* bow + "Stand to it, my hearts of gold," said the old bowman as he + passed from knot to knot. "By my hilt! we are in luck this + journey. Bear in mind the old saying of the Company." + "What is that, Aylward?" cried several, leaning on their bows + and laughing at him. + "'Tis the master-bowyer's rede: 'Every bow well bent. Every + shaft well sent. Every stave well nocked. Every string well + locked.' There, with that jingle in his head, a bracer on + his left hand, a shooting glove on his right, and a + farthing's-worth of wax in his girdle, what more doth a + bowman need?" + "It would not be amiss," said Hordle John, "if under his + girdle he had four farthings'-worth of wine." + [ The White Company, by Sir Arthur Conan Doyle ] +brigit + Brigit (Brigid, Bride, Banfile), which means the Exalted One, + was the Celtic (continental European and Irish) fertility + goddess. She was originally celebrated on February first in + the festival of Imbolc, which coincided with the beginning + of lactation in ewes and was regarded in Scotland as the date + on which Brigit deposed the blue-faced hag of winter. The + Christian calendar adopted the same date for the Feast of St. + Brigit. There is no record that a Christian saint ever + actually existed, but in Irish mythology she became the + midwife to the Virgin Mary. + [ Encyclopedia of Gods, by Michael Jordan ] +~stormbringer +*broadsword + Bring me my broadsword + And clear understanding. + Bring me my cross of gold, + As a talisman. + [ "Broadsword" (refrain) by Ian Anderson ] +bugbear + Bugbears are relatives of goblins, although they tend to be + larger and more hairy. They are aggressive carnivores and + sometimes kill just for the treasure their victims may be + carrying. +bugle + 'I read you by your bugle horn + And by your palfrey good, + I read you for a Ranger sworn + To keep the King's green-wood.' + 'A Ranger, Lady, winds his horn, + And 'tis at peep of light; + His blast is heard at merry morn, + And mine at dead of night.' + [ Brignall Banks, by Sir Walter Scott ] +*camaxtli + A classical Mesoamerican Aztec god, also known as Mixcoatl- + Camaxtli (the Cloud Serpent), Camaxtli is the god of war. He + is also a deity of hunting and fire who received human + sacrifice of captured prisoners. According to tradition, the + sun god Tezcatlipoca transformed himself into Mixcoatl-Camaxtli + to make fire by twirling the sacred fire sticks. + [ Encyclopedia of Gods, by Michael Jordan ] +candy bar + Only once a year, on his birthday, did Charlie Bucket ever + get to taste a bit of chocolate. The whole family saved up + their money for that special occasion, and when the great + day arrived, Charlie was always presented with one small + chocolate bar to eat all by himself. And each time he + received it, on those marvelous birthday mornings, he would + place it carefully in a small wooden box that he owned, and + treasure it as though it were a bar of solid gold; and for + the next few days, he would allow himself only to look at it, + but never to touch it. Then at last, when he could stand it + no longer, he would peel back a tiny bit of the paper + wrapping at one corner to expose a tiny bit of chocolate, and + then he would take a tiny nibble - just enough to allow the + lovely sweet taste to spread out slowly over his tongue. The + next day, he would take another tiny nibble, and so on, and + so on. And in this way, Charlie would make his ten-cent bar + of birthday chocolate last him for more than a month. + [ Charlie and the Chocolate Factory, by Roald Dahl ] +s*d*g*r* cat + Imagine a sealed container, so perfectly constructed that no + physical influence can pass either inwards or outwards across its + walls. Imagine that inside the container is a cat, and also a + device that can be triggered by some quantum event. If that event + takes place, then the device smashes a phial containing cyanide and + the cat is killed. If the event does not take place, the cat lives + on. In Schroedinger's original version, the quantum event was the + decay of a radioactive atom. ... To the outside observer, the cat + is indeed in a linear combination of being alive and dead, and only + when the container is finally opened would the cat's state vector + collapse into one or the other. On the other hand, to a (suitably + protected) observer inside the container, the cat's state-vector + would have collapsed much earlier, and the outside observer's + linear combination has no relevance. + [ The Emperor's New Mind, by Roger Penrose ] +*cat +kitten + Well-known quadruped domestic animal from the family of + predatory felines (_Felis ochreata domestica_), with a thick, + soft pelt; often kept as a pet. Various folklores have the + cat associated with magic and the gods of ancient Egypt. + + So Ulthar went to sleep in vain anger; and when the people + awakened at dawn - behold! Every cat was back at his + accustomed hearth! Large and small, black, grey, striped, + yellow and white, none was missing. Very sleek and fat did + the cats appear, and sonorous with purring content. + [ The Cats of Ulthar, by H.P. Lovecraft ] +# this one doesn't work very well for dwarven and gnomish cavemen +cave*man +human cave*man + Now it was light enough to leave. Moon-Watcher picked up + the shriveled corpse and dragged it after him as he bent + under the low overhang of the cave. Once outside, he + threw the body over his shoulder and stood upright - the + only animal in all this world able to do so. + Among his kind, Moon-Watcher was almost a giant. He was + nearly five feet high, and though badly undernourished + weighed over a hundred pounds. His hairy, muscular body + was halfway between ape and man, but his head was already + much nearer to man than ape. The forehead was low, and + there were ridges over the eye sockets, yet he unmistakably + held in his genes the promise of humanity. + [ 2001: A Space Odyssey, by Arthur C. Clarke ] +*centaur + Of all the monsters put together by the Greek imagination + the Centaurs (Kentauroi) constituted a class in themselves. + Despite a strong streak of sensuality, in their make-up, + their normal behaviour was moral, and they took a kindly + thought of man's welfare. The attempted outrage of Nessos on + Deianeira, and that of the whole tribe of Centaurs on the + Lapith women, are more than offset by the hospitality of + Pholos and by the wisdom of Cheiron, physician, prophet, + lyrist, and the instructor of Achilles. Further, the + Centaurs were peculiar in that their nature, which united the + body of a horse with the trunk and head of a man, involved + an unthinkable duplication of vital organs and important + members. So grotesque a combination seems almost un-Greek. + These strange creatures were said to live in the caves and + clefts of the mountains, myths associating them especially + with the hills of Thessaly and the range of Erymanthos. + [ Mythology of all races, Vol. 1, pp. 270-271 ] +centipede + I observed here, what I had often seen before, that certain + districts abound in centipedes. Here they have light + reddish bodies and blue legs; great myriapedes are seen + crawling every where. Although they do no harm, they excite + in man a feeling of loathing. Perhaps our appearance + produces a similar feeling in the elephant and other large + animals. Where they have been much disturbed, they + certainly look upon us with great distrust, as the horrid + biped that ruins their peace. + [ Travels and Researches in South Africa, + by Dr. David Livingstone ] +cerberus +kerberos + Cerberus, (or Kerberos in Greek), was the three-headed dog + that guarded the Gates of Hell. He allowed any dead to enter, + and likewise prevented them all from ever leaving. He was + bested only twice: once when Orpheus put him to sleep by + playing bewitching music on his lyre, and the other time when + Hercules confronted him and took him to the world of the + living (as his twelfth and last labor). +chameleon + Name of a family (_Chameleonidae_) and race (_Chameleo_) of + scaly lizards, especially the _Chameleo vulgaris_ species, + with a short neck, claws, a grasping tail, a long, extendible + tongue and mutually independent moving eyes. When it is + scared or angry, it inflates itself and its transparent skin + shows its blood: the skin first appears greenish, then + gradually changes color until it is a spotted red. The final + color depends on the background color as well, hence the + (figurative) implication of unreliability. [Capitalized:] + a constellation of the southern hemisphere (Chameleo). + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] +charo*n + When an ancient Greek died, his soul went to the nether world: + the Hades. To reach the nether world, the souls had to cross + the river Styx, the river that separated the living from the + dead. The Styx could be crossed by ferry, whose shabby ferry- + man, advanced in age, was called Charon. The deceased's next- + of-kin would place a coin under his tongue, to pay the ferry- + man. +chest +large box + Dantes rapidly cleared away the earth around the chest. Soon + the center lock appeared, then the handles at each end, all + delicately wrought in the manner of that period when art made + precious even the basest of metals. He took the chest by the + two handles and tried to lift it, but it was impossible. He + tried to open it; it was locked. He inserted the sharp end + of his pickaxe between the chest and the lid and pushed down + on the handle. The lid creaked, then flew open. + Dantes was seized with a sort of giddy fever. He cocked his + gun and placed it beside him. The he closed his eyes like a + child, opened them and stood dumbfounded. + The chest was divided into three compartments. In the first + were shining gold coins. In the second, unpolished gold + ingots packed in orderly stacks. From the third compartment, + which was half full, Dantes picked up handfuls of diamonds, + pearls and rubies. As they fell through his fingers in a + glittering cascade, they gave forth the sound of hail beating + against the windowpanes. + [ The Count of Monte Cristo, by Alexandre Dumas ] +chih*sung*tzu + A Chinese rain god. +chromatic dragon +tiamat + Tiamat is said to be the mother of evil dragonkind. She is + extremely vain. +~elven cloak +~oilskin cloak +*cloak* + Cloaks are the universal outer garb of everyone who is not a + Barbarian. It is hard to see why. They are open in front + and require you at most times to use one hand to hold them + shut. On horseback they leave the shirt-sleeved arms and + most of the torso exposed to wind and Weather. The OMTs + [ Official Management Terms ] for Cloaks well express their + difficulties. They are constantly _swirling and dripping_ + and becoming _heavy with water_ in rainy Weather, _entangling + with trees_ or _swords_, or needing to be _pulled close + around her/his shivering body_. This seems to suggest they + are less than practical for anyone on an arduous Tour. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +cloud* + I wandered lonely as a cloud + That floats on high o'er vales and hills, + When all at once I saw a crowd, + A host, of golden daffodils; + Beside the lake, beneath the trees, + Fluttering and dancing in the breeze. + [ I Wandered Lonely as a Cloud, by William Wordsworth ] +cobra + Darzee and his wife only cowered down in the nest without + answering, for from the thick grass at the foot of the bush + there came a low hiss -- a horrid cold sound that made + Rikki-tikki jump back two clear feet. Then inch by inch out of + the grass rose up the head and spread hood of Nag, the big + black cobra, and he was five feet long from tongue to tail. + When he had lifted one-third of himself clear of the ground, + he stayed balancing to and fro exactly as a dandelion-tuft + balances in the wind, and he looked at Rikki-tikki with the + wicked snake's eyes that never change their expression, + whatever the snake may be thinking of. + 'Who is Nag?' said he. '_I_ am Nag. The great God Brahm put + his mark upon all our people, when the first cobra spread his + hood to keep the sun off Brahm as he slept. Look, and be + afraid!' + [ Rikki-tikki-tavi, by Rudyard Kipling ] +c*ckatrice + Once in a great while, when the positions of the stars are + just right, a seven-year-old rooster will lay an egg. Then, + along will come a snake, to coil around the egg, or a toad, + to squat upon the egg, keeping it warm and helping it to + hatch. When it hatches, out comes a creature called basilisk, + or cockatrice, the most deadly of all creatures. A single + glance from its yellow, piercing toad's eyes will kill both + man and beast. Its power of destruction is said to be so + great that sometimes simply to hear its hiss can prove fatal. + Its breath is so venomous that it causes all vegetation + to wither. + + There is, however, one creature which can withstand the + basilisk's deadly gaze, and this is the weasel. No one knows + why this is so, but although the fierce weasel can slay the + basilisk, it will itself be killed in the struggle. Perhaps + the weasel knows the basilisk's fatal weakness: if it ever + sees its own reflection in a mirror it will perish instantly. + But even a dead basilisk is dangerous, for it is said that + merely touching its lifeless body can cause a person to + sicken and die. + [ Mythical Beasts by Deirdre Headon (The Leprechaun Library) + and other sources ] +cornuthaum + He was dressed in a flowing gown with fur tippets which had + the signs of the zodiac embroidered over it, with various + cabalistic signs, such as triangles with eyes in them, queer + crosses, leaves of trees, bones of birds and animals, and a + planetarium whose stars shone like bits of looking-glass with + the sun on them. He had a pointed hat like a dunce's cap, or + like the headgear worn by ladies of that time, except that + the ladies were accustomed to have a bit of veil floating + from the top of it. + [ The Once and Future King, by T.H. White ] + + "A wizard!" Dooley exclaimed, astounded. + "At your service, sirs," said the wizard. "How + perceptive of you to notice. I suppose my hat rather gives me + away. Something of a beacon, I don't doubt." His hat was + pretty much that, tall and cone-shaped with stars and crescent + moons all over it. All in all, it couldn't have been more + wizardish. + [ The Elfin Ship, James P. Blaylock ] +couatl + A mythical feathered serpent. The couatl are very rare. +coyote + This carnivore is known for its voracious appetite and + inflated view of its own intelligence. +cram* + If you want to know what cram is, I can only say that I don't + know the recipe; but it is biscuitish, keeps good indefinitely, + is supposed to be sustaining, and is certainly not entertaining, + being in fact very uninteresting except as a chewing + exercise. It was made by the Lake-men for long journeys. + [ The Hobbit, by J.R.R. Tolkien ] +*crocodile + A big animal with the appearance of a lizard, constituting + an order of the reptiles (_Loricata_ or _Crocodylia_), the + crocodile is a large, dangerous predator native to tropical + and subtropical climes. It spends most of its time in large + bodies of water. +croesus +kroisos +creosote + Croesus (in Greek: Kroisos), the wealthy last king of Lydia; + his empire was destroyed when he attacked Cyrus in 549, after + the Oracle of Delphi (q.v.) had told him: "if you attack the + Persians, you will destroy a mighty empire". Herodotus + relates of his legendary conversation with Solon of Athens, + who impressed upon him that being rich does not imply being + happy and that no one should be considered fortunate before + his death. +crom + Warily Conan scanned his surroundings, all of his senses alert + for signs of possible danger. Off in the distance, he could + see the familiar shapes of the Camp of the Duali tribe. + Suddenly, the hairs on his neck stand on end as he detects the + aura of evil magic in the air. Without thought, he readies + his weapon, and mutters under his breath: + "By Crom, there will be blood spilt today." + + [ Conan the Avenger by Robert E. Howard, Bjorn Nyberg, and + L. Sprague de Camp ] +crossbow* + "God save thee, ancient Mariner! + From the fiends, that plague thee thus! - + Why look'st thou so?" - With my cross-bow + I shot the Albatross. + [ The Rime of the Ancient Mariner, by Samuel Taylor + Coleridge ] +crystal ball + You look into one of these and see _vapours swirling like + clouds_. These shortly clear away to show a sort of video + without sound of something that is going to happen to you + soon. It is seldom good news. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +curse* + Curses are longstanding ill-wishings which, in Fantasyland, + often manifest as semisentient. They have to be broken or + dispelled. The method varies according to the type and + origin of the Curse: + [...] + 4. Curses on Rings and Swords. You have problems. Rings + have to be returned whence they came, preferably at over a + thousand degrees Fahrenheit, and the Curse means you won't + want to do this. Swords usually resist all attempts to + raise their Curses. Your best source is to hide the Sword + or give it to someone you dislike. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +cwn*n + A pack of snow-white, red-eared spectral hounds which + sometimes took part in the kidnappings and raids the + inhabitants of the underworld sometimes make on this world + (the Wild Hunt). They are associated in Wales with the sounds + of migrating wild geese, and are said to be leading the souls + of the damned to hell. The phantom chase is usually heard or + seen in midwinter and is accompanied by a howling wind. + [ Encyclopedia Mythica, ed. M.F. Lindemans ] +cyclops + And after he had milked his cattle swiftly, + he again took hold of two of my men + and had them as his supper. + Then I went, with a tub of red wine, + to stand before the Cyclops, saying: + "A drop of wine after all this human meat, + so you can taste the delicious wine + that is stored in our ship, Cyclops." + He took the tub and emptied it. + He appreciated the priceless wine that much + that he promptly asked me for a second tub. + "Give it", he said, "and give me your name as well". + ... + Thrice I filled the tub, + and after the wine had clouded his mind, + I said to him, in a tone as sweet as honey: + "You have asked my name, Cyclops? Well, + my name is very well known. I'll give it to you, + if you give me the gift you promised me as a guest. + My name is Nobody. All call me thus: + my father and my mother and my friends." + Ruthlessly he answered to this: + "Nobody, I will eat you last of all; + your host of friends will completely precede you. + That will be my present to you, my friend." + And after these words he fell down backwards, + restrained by the all-restrainer Hupnos. + His monstrous neck slid into the dust; + the red wine squirted from his throat; + the drunk vomited lumps of human flesh. + [ The Odyssey, (chapter Epsilon), by Homer ] +~sting +*dagger + Is this a dagger which I see before me, + The handle toward my hand? Come, let me clutch thee. + I have thee not, and yet I see thee still. + Art thou not, fatal vision, sensible + To feeling as to sight? or art thou but + A dagger of the mind, a false creation, + Proceeding from the heat-oppressed brain? + I see thee yet, in form as palpable + As this which now I draw. + [ Macbeth, by William Shakespeare ] +dark one + ... But he ruled rather by force and fear, if they might + avail; and those who perceived his shadow spreading over the + world called him the Dark Lord and named him the Enemy; and + he gathered again under his government all the evil things of + the days of Morgoth that remained on earth or beneath it, + and the Orcs were at his command and multiplied like flies. + Thus the Black Years began ... + [ The Silmarillion, by J.R.R. Tolkien ] +demogorgon + Demogorgon, the prince of demons, wallows in filth and can + spread a quickly fatal illness to his victims while rending + them. He is a mighty spellcaster, and he can drain the life + of mortals with a touch of his tail. +demon + It is often very hard to discover what any given Demon looks + like, apart from a general impression of large size, huge + fangs, staring eyes, many limbs, and an odd color; but all + accounts agree that Demons are very powerful, very Magic (in + a nonhuman manner), and made of some substance that can squeeze + through a keyhole yet not be pierced with a Sword. This makes + them difficult to deal with, even on the rare occasions when + they are friendly. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +dingo + A wolflike wild dog, Canis dingo, of Australia, having a + reddish- or yellowish-brown coat, believed to have been + introduced by the aborigines. + [Webster's Encyclopedic Unabridged Dictionary of the English Language] +disenchanter + Ask not, what your magic can do to it. Ask what it can do to your magic. +dispater + Dispater is an arch-devil who rules the city of Dis. He is + a powerful mage. +djinn* + The djinn are genies from the elemental plane of Air. There, + among their kind, they have their own societies. They are + sometimes encountered on earth and may even be summoned here + to perform some service for powerful wizards. The wizards + often leave them about for later service, safely tucked away + in a flask or lamp. Once in a while, such a tool is found by + a lucky rogue, and some djinn are known to be so grateful + when released that they might grant their rescuer a wish. +~hachi +~slasher +~sirius +*dog +pup* + A domestic animal, the _tame dog_ (_Canis familiaris_), of + which numerous breeds exist. The male is called a dog, + while the female is called a bitch. Because of its known + loyalty to man and gentleness with children, it is the + world's most popular domestic animal. It can easily be + trained to perform various tasks. +~trap*door +*door +doorway + Through me you pass into the city of woe: + Through me you pass into eternal pain: + Through me among the people lost for aye. + Justice the founder of my fabric mov'd: + To rear me was the task of power divine, + Supremest wisdom, and primeval love. + Before me things create were none, save things + Eternal, and eternal I endure. + All hope abandon ye who enter here. + [ The Inferno, from The Divine Comedy of Dante + Alighieri, translated by H.F. Cary ] +doppelganger + "Then we can only give thanks that this is Antarctica, where + there is not one, single, solitary, living thing for it to + imitate, except these animals in camp." + + "Us," Blair giggled. "It can imitate us. Dogs can't make four + hundred miles to the sea; there's no food. There aren't any + skua gulls to imitate at this season. There aren't any + penguins this far inland. There's nothing that can reach the + sea from this point - except us. We've got brains. We can do + it. Don't you see - it's got to imitate us - it's got to be one + of us - that's the only way it can fly an airplane - fly a plane + for two hours, and rule - be - all Earth's inhabitants. A world + for the taking - if it imitates us! + [ Who Goes There?, by John W. Campbell ] + + Xander: Let go! I have to kill the demon bot! + Xander Double (grabbing the gun): Anya, get out of the way. + Buffy: Xander! + Xander Double: That's all right, Buffy. I have him. + Xander: No, Buffy, I'm me. Help me! + Anya: My gun, he's got my gun. + Riley: You own a gun? + Buffy: Xander, gun holding Xander, give it to me. + Anya: Buffy, which one's real? + Xander: I am. + Xander Double: No, _I_ am. + [ Buffy the Vampire Slayer, Episode 5.03, "The Replacement" ] +*dragon +*xoth + In the West the dragon was the natural enemy of man. Although + preferring to live in bleak and desolate regions, whenever it + was seen among men it left in its wake a trail of destruction + and disease. Yet any attempt to slay this beast was a perilous + undertaking. For the dragon's assailant had to contend + not only with clouds of sulphurous fumes pouring from its fire + breathing nostrils, but also with the thrashings of its tail, + the most deadly part of its serpent-like body. + [ Mythical Beasts by Deirdre Headon (The Leprechaun Library) ] + + "One whom the dragons will speak with," he said, "that is a + dragonlord, or at least that is the center of the matter. It's + not a trick of mastering the dragons, as most people think. + Dragons have no masters. The question is always the same, with + a dragon: will he talk to you or will he eat you? If you can + count upon his doing the former, and not doing the latter, why + then you're a dragonlord." + [ The Tombs of Atuan, by Ursula K. Le Guin ] +*drum* + Many travelers have seen the drums of the great apes, and + some have heard the sounds of their beating and the noise of + the wild, weird revelry of these first lords of the jungle, + but Tarzan, Lord Greystoke, is, doubtless, the only human + being who ever joined in the fierce, mad, intoxicating revel + of the Dum-Dum. + [ Tarzan of the Apes, by Edgar Rice Burroughs ] +~dwarf ??m* +dwarf* +dwar* cave*man + Dwarfs have faces like men (ugly men, with wrinkled, leathery + skins), but are generally either flat-footed, duck-footed, or + have feet pointing backwards. They are of the earth, earthy, + living in the darkest of caverns and venturing forth only + with the cloaks by which they can make themselves invisible, + and others disguised as toads. Miners often come across them, + and sometimes establish reasonably close relations with them. + ... The miners of Cornwall were always delighted to hear a + bucca busily mining away, for all dwarfs have an infallible + nose for precious metals. + Among other things, dwarfs are rightly valued for their skill + as blacksmiths and jewellers: they made Odin his famous spear + Gungnir, and Thor his hammer; for Freya they designed a + magnificent necklace, and for Frey a golden boar. And in their + spare time they are excellent bakers. Ironically, despite + their odd feet, they are particularly fond of dancing. They + can also see into the future, and consequently are excellent + meteorologists. They can be free with presents to people + they like, and a dwarvish gift is likely to turn to gold in + the hand. But on the whole they are a snappish lot. + [ The Immortals, by Derek and Julia Parker ] +earendil +elwing + In after days, when because of the triumph of Morgoth Elves and + Men became estranged, as he most wished, those of the Elven-race + that lived still in Middle-earth waned and faded, and Men usurped + the sunlight. Then the Quendi wandered in the lonely places of the + great lands and the isles, and took to the moonlight and the + starlight, and to the woods and the caves, becoming as shadows + and memories, save those who ever and anon set sail into the West + and vanished from Middle-earth. But in the dawn of years Elves + and Men were allies and held themselves akin, and there were some + among Men that learned the wisdom of the Eldar, and became great + and valiant among the captains of the Noldor. And in the glory + and beauty of the Elves, and in their fate, full share had the + offspring of elf and mortal, Earendil, and Elwing, and Elrond + their child. + [ The Silmarillion, by J.R.R. Tolkien ] +eel +giant eel + The behaviour of eels in fresh water extends the air of + mystery surrounding them. They move freely into muddy, silty + bottoms of lakes, lying buried in the daylight hours in summer. + [...] Eels are voracious carnivores, feeding mainly at + night and consuming a wide variety of fishes and invertebrate + creatures. Contrary to earlier thinking, eels seek living + rather than dead creatures and are not habitual eaters of + carrion. + [ Freshwater Fishes of Canada, by Scott and Crossman ] +egg + But I asked why not keep it and let the hen sit on it till it + hatched, and then we could see what would come out of it. + "Nothing good, I'm certain of that," Mom said. "It would + probably be something horrible. But just remember, if it's a + crocodile or a dragon or something like that, I won't have it + in my house for one minute." + [ The Enormous Egg, by Oliver Butterworth ] +elbereth + ... Even as they stepped over the threshold a single clear + voice rose in song. + + A Elbereth Gilthoniel, + silivren penna miriel + o menel aglar elenath! + Na-chaered palan-diriel + o galadhremmin ennorath, + Fanuilos, le linnathon + nef aear, si nef aearon! + + Frodo halted for a moment, looking back. Elrond was in his + chair and the fire was on his face like summer-light upon the + trees. Near him sat the Lady Arwen. [...] + He stood still enchanted, while the sweet syllables of the + elvish song fell like clear jewels of blended word and melody. + "It is a song to Elbereth," said Bilbo. "They will sing that, + and other songs of the Blessed Realm, many times tonight. + Come on!" + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +electric eel + South-American fish (_Gymnotus electricus_), living in fresh + water. Shaped like a serpent, it can grow up to 2 metres. + This eel is known for its electrical organ which enables it + to paralyse creatures up to the size of a horse. + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] +*elemental + Elementals are manifestations of the basic nature of the + universe. There are four known forms of elementals: air, fire, + water, and earth. Some mystics have postulated the necessity + for a fifth type, the spirit elemental, but none have ever + been encountered, at least on this plane of existence. +~elf ??m* +*elf* +elvenking + The Elves sat round the fire upon the grass or upon the sawn + rings of old trunks. Some went to and fro bearing cups and + pouring drinks; others brought food on heaped plates and + dishes. + "This is poor fare," they said to the hobbits; "for we are + lodging in the greenwood far from our halls. If ever you are + our guests at home, we will treat you better." + "It seems to me good enough for a birthday-party," said Frodo. + Pippin afterwards recalled little of either food or drink, for + his mind was filled with the light upon the elf-faces, and the + sound of voices so various and so beautiful that he felt in a + waking dream. [...] + Sam could never describe in words, nor picture clearly to + himself, what he felt or thought that night, though it remained + in his memory as one of the chief events of his life. The + nearest he ever got was to say: "Well, sir, if I could grow + apples like that, I would call myself a gardener. But it was + the singing that went to my heart, if you know what I mean." + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +elven cloak + The Elves next unwrapped and gave to each of the Company the + clothes they had brought. For each they had provided a hood + and cloak, made according to his size, of the light but warm + silken stuff that the Galadrim wove. It was hard to say of + what colour they were: grey with the hue of twilight under + the trees they seemed to be; and yet if they were moved, or + set in another light, they were green as shadowed leaves, or + brown as fallow fields by night, dusk-silver as water under + the stars. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +emerald + 'Put off that mask of burning gold + With emerald eyes.' + 'O no, my dear, you make so bold + To find if hearts be wild and wise, + And yet not cold.' + + 'I would but find what's there to find, + Love or deceit.' + 'It was the mask engaged your mind, + And after set your heart to beat, + Not what's behind.' + + 'But lest you are my enemy, + I must enquire.' + 'O no, my dear, let all that be; + What matter, so there is but fire + In you, in me?' + [ The Mask, by W.B. Yeats ] +erinys +erinyes + These female-seeming devils named after the Furies of mythology + attack hand to hand and poison their unwary victims as well. +ettin + The two-headed giant, or ettin, is a vicious and unpredictable + hunter that stalks by night and eats any meat it can catch. +excalibur + At first only its tip was visible, but then it rose, straight, + proud, all that was noble and great and wondrous. The tip of + the blade pointed toward the moon, as if it would cleave it + in two. The blade itself gleamed like a beacon in the night. + There was no light source for the sword to be reflecting + from, for the moon had darted behind a cloud in fear. The + sword was glowing from the intensity of its strength and + power and knowledge that it was justice incarnate, and that + after a slumber of uncounted years its time had again come. + After the blade broke the surface, the hilt was visible, and + holding the sword was a single strong, yet feminine hand, + wearing several rings that bore jewels sparkling with the + blue-green color of the ocean. + [ Knight Life, by Peter David ] +expensive camera + There was a time when Rincewind had quite liked the iconoscope. + He believed, against all experience, that the world was + fundamentally understandable, and that if he could only equip + himself with the right mental toolbox he could take the back off + and see how it worked. He was, of course, dead wrong. The + iconoscope didn't take pictures by letting light fall onto + specially treated paper, as he had surmised, but by the far + simpler method of imprisoning a small demon with a good eye for + colour and a speedy hand with a paintbrush. He had been very + upset to find that out. + [ The Light Fantastic, by Terry Pratchett ] +eye of the aethiopica + This is a powerful amulet of ESP. In addition to its standard + powers, it regenerates the energy of anyone who carries + it, allowing them to cast spells more often. It also reduces + any spell damage to the person who carries it by half, and + protects from magic missiles. Finally, when invoked it has + the power to instantly open a portal to any other area of the + dungeon, allowing its invoker to travel quickly between + areas. +eyes of the overworld + ... and finally there is "the Eyes of the Overworld". This + obscure artifact pushes the wearer's view sense into the + "overworld" -- another name for a segment of the Astral Plane. + Usually, there is nothing to be seen. However, the wearer + is also able to look back and see the area around herself, + much like looking on a map. Why anyone would want to ... +figurine* + Then it appeared in Paris at just about the time that Paris + was full of Carlists who had to get out of Spain. One of + them must have brought it with him, but, whoever he was, it's + likely he knew nothing about its real value. It had been -- + no doubt as a precaution during the Carlist trouble in Spain + -- painted or enameled over to look like nothing more than a + fairly interesting black statuette. And in that disguise, + sir, it was, you might say, kicked around Paris for seventy + years by private owners and dealers too stupid to see what + it was under the skin. + [ The Maltese Falcon, by Dashiell Hammett ] +floating eye + Floating eyes, not surprisingly, are large, floating eyeballs + which drift about the dungeon. Though not dangerous in and + of themselves, their power to paralyse those who gaze at + their large eye in combat is widely feared. Many are the + tales of those who struck a floating eye, were paralysed by + its mystic powers, and then nibbled to death by some other + creature that lurked around nearby. +flesh golem + With an anxiety that almost amounted to agony, I collected + the instruments of life around me, that I might infuse a spark + of being into the lifeless thing that lay at my feet. It was + already one in the morning; the rain pattered dismally against + the panes, and my candle was nearly burnt out, when, by the + glimmer of the half-extinguished light, I saw the dull yellow + eye of the creature open; it breathed hard, and a convulsive + motion agitated its limbs. + + How can I describe my emotions at this catastrophe, or how + delineate the wretch whom with such infinite pains and care I + had endeavoured to form? His limbs were in proportion, and I + had selected his features as beautiful. Beautiful!--Great God! + His yellow skin scarcely covered the work of muscles and + arteries beneath; his hair was of a lustrous black, and + flowing; his teeth of a pearly whiteness; but these luxuriances + only formed a more horrid contrast with his watery eyes, that + seemed almost of the same colour as the dun white sockets in + which they were set, his shrivelled complexion and straight + black lips. + [ Frankenstein, by Mary Wollstonecraft Shelley ] +*flute + With this thou canst do mighty deeds + And change men's passions for thy needs: + A man's despair with joy allay, + Turn bachelors old to lovers gay. + [ The Magic Flute, by Wolfgang Amadeus Mozart ] +fog cloud + The fog comes + on little cat feet. + + It sits looking + over harbor and city + on silent haunches + and then moves on. + [ Fog, by Carl Sandburg ] +fountain + Rest! This little Fountain runs + Thus for aye: -- It never stays + For the look of summer suns, + Nor the cold of winter days. + Whose'er shall wander near, + When the Syrian heat is worst, + Let him hither come, nor fear + Lest he may not slake his thirst: + He will find this little river + Running still, as bright as ever. + Let him drink, and onward hie, + Bearing but in thought, that I, + Erotas, bade the Naiad fall, + And thank the great god Pan for all! + [ For a Fountain, by Bryan Waller Procter ] +fox + One hot summer's day a Fox was strolling through an orchard + till he came to a bunch of Grapes just ripening on a vine + which had been trained over a lofty branch. "Just the thing + to quench my thirst," quoth he. Drawing back a few paces, he + took a run and a jump, and just missed the bunch. Turning + round again with a One, Two, Three, he jumped up, but with + no greater success. Again and again he tried after the + tempting morsel, but at last had to give it up, and walked + away with his nose in the air, saying: "I am sure they are + sour." + [ Aesop's Fables ] +*fung* + Fungi, division of simple plants that lack chlorophyll, true + stems, roots, and leaves. Unlike algae, fungi cannot + photosynthesize, and live as parasites or saprophytes. The + division comprises the slime molds and true fungi. True + fungi are multicellular (with the exception of yeasts); the + body of most true fungi consists of slender cottony + filaments, or hyphae. All fungi are capable of asexual + reproduction by cell division, budding, fragmentation, or + spores. Those that reproduce sexually alternate a sexual + generation (gametophyte) with a spore-producing one. The + four classes of true fungi are the algaelike fungi (e.g., + black bread mold and downy mildew), sac fungi (e.g., yeasts, + powdery mildews, truffles, and blue and green molds such as + Penicillium), basidium fungi (e.g., mushrooms and puffballs) + and imperfect fungi (e.g., species that cause athlete's foot + and ringworm). Fungi help decompose organic matter (important + in soil renewal); are valuable as a source of antibiotics, + vitamins, and various chemicals; and for their role in + fermentation, e.g., in bread and alcoholic beverage + production. + [ The Concise Columbia Encyclopedia ] +*gargoyle + And so it came to pass that while Man ruled on Earth, the + gargoyles waited, lurking, hidden from the light. Reborn + every 600 years in Man's reckoning of time, the gargoyles + joined battle against Man to gain dominion over the Earth. + + In each coming, the gargoyles were nearly destroyed by Men + who flourished in greater numbers. Now it has been so many + hundreds of years that it seems the ancient statues and + paintings of gargoyles are just products of Man's + imagination. In this year, with Man's thoughts turned toward + the many ills he has brought among himself, Man has forgotten + his most ancient adversary, the gargoyles. + [ Excerpt from the opening narration to the movie + _Gargoyles_, written by Stephen and Elinor Karpf ] +*garlic + 1 November - All day long we have travelled, and at a good + speed. The horses seem to know that they are being kindly + treated, for they go willingly their full stage at best + speed. We have now had so many changes and find the same + thing so constantly that we are encouraged to think that the + journey will be an easy one. Dr. Van Helsing is laconic, he + tells the farmers that he is hurrying to Bistritz, and pays + them well to make the exchange of horses. We get hot soup, + or coffee, or tea, and off we go. It is a lovely country. + Full of beauties of all imaginable kinds, and the people are + brave, and strong, and simple, and seem full of nice + qualities. They are very, very superstitious. In the first + house where we stopped, when the woman who served us saw the + scar on my forehead, she crossed herself and put out two + fingers towards me, to keep off the evil eye. I believe they + went to the trouble of putting an extra amount of garlic into + our food, and I can't abide garlic. Ever since then I have + taken care not to take off my hat or veil, and so have + escaped their suspicions. + [ Dracula, by Bram Stoker ] +# gas spore -- see *spore +geryon + Geryon is an arch-devil sometimes called the Wild Beast, + attacking with his claws and poison sting. His ranking in + Hell is rumored to be quite low. +*ghost + And now the souls of the dead who had gone below came swarming + up from Erebus -- fresh brides, unmarried youths, old men + with life's long suffering behind them, tender young girls + still nursing this first anguish in their hearts, and a great + throng of warriors killed in battle, their spear-wounds gaping + yet and all their armour stained with blood. From this + multitude of souls, as they fluttered to and fro by the + trench, there came a moaning that was horrible to hear. + Panic drained the blood from my cheeks. + [ The Odyssey, (chapter Lambda), by Homer ] +ghoul + The forces of the gloom know each other, and are strangely + balanced by each other. Teeth and claws fear what they cannot + grasp. Blood-drinking bestiality, voracious appetites, hunger + in search of prey, the armed instincts of nails and jaws which + have for source and aim the belly, glare and smell out + uneasily the impassive spectral forms straying beneath a + shroud, erect in its vague and shuddering robe, and which seem + to them to live with a dead and terrible life. These + brutalities, which are only matter, entertain a confused fear + of having to deal with the immense obscurity condensed into an + unknown being. A black figure barring the way stops the wild + beast short. That which emerges from the cemetery intimidates + and disconcerts that which emerges from the cave; the + ferocious fear the sinister; wolves recoil when they encounter + a ghoul. + [ Les Miserables, by Victor Hugo ] +*giant +giant humanoid + Giants have always walked the earth, though they are rare in + these times. They range in size from little over nine feet + to a towering twenty feet or more. The larger ones use huge + boulders as weapons, hurling them over large distances. All + types of giants share a love for men - roasted, boiled, or + fried. Their table manners are legendary. +# note: "gnomish wizard" is a monster; cave*man entry doesn't fit nonhumans +~gnome ??m* +gnome* +gnomish wizard +gnom* cave*man + ... And then a gnome came by, carrying a bundle, an old + fellow three times as large as an imp and wearing clothes of + a sort, especially a hat. And he was clearly just as frightened + as the imps though he could not go so fast. Ramon Alonzo + saw that there must be some great trouble that was vexing + magical things; and, since gnomes speak the language of men, and + will answer if spoken to gently, he raised his hat, and asked + of the gnome his name. The gnome did not stop his hasty + shuffle a moment as he answered 'Alaraba' and grabbed the rim + of his hat but forgot to doff it. + 'What is the trouble, Alaraba?' said Ramon Alonzo. + 'White magic. Run!' said the gnome .. + [ The Charwoman's Shadow, by Lord Dunsany ] + + "Muggles have garden gnomes, too, you know," Harry told Ron as + they crossed the lawn. + "Yeah, I've seen those things they think are gnomes," said Ron, + bent double with his head in a peony bush, "like fat little + Santa Clauses with fishing rods..." + There was a violent scuffling noise, the peony bush shuddered, + and Ron straightened up. "This is a gnome," he said grimly. + "Geroff me! Gerroff me!" squealed the gnome. + It was certainly nothing like Santa Claus. It was small and + leathery looking, with a large, knobby, bald head exactly like + a potato. Ron held it at arm's length as it kicked out at him + with its horny little feet; he grasped it around the ankles + and turned it upside down. + [ Harry Potter and the Chamber of Secrets, by J. K. Rowling ] +goblin + Now goblins are cruel, wicked, and bad-hearted. They make + no beautiful things, but they make many clever ones. They + can tunnel and mine as well as any but the most skilled + dwarves, when they take the trouble, though they are usually + untidy and dirty. Hammers, axes, swords, daggers, pickaxes, + tongs, and also instruments of torture, they make very well, + or get other people to make to their design, prisoners and + slaves that have to work till they die for want of air and + light. + [ The Hobbit, by J.R.R. Tolkien ] +god +goddess + Goddesses and Gods operate in ones, threesomes, or whole + pantheons of nine or more (see Religion). Most of them claim + to have made the world, and this is indeed a likely claim in + the case of threesomes or pantheons: Fantasyland does have + the air of having been made by a committee. But all Goddesses + and Gods, whether they say they made the world or not, have + very detailed short-term plans for it which they are determined + to carry out. Consequently they tend to push people into the + required actions by the use of coincidence or Prophecy, or just + by narrowing down your available choices of what to do next: + if a deity is pushing you, things will go miserably badly until + there is only one choice left to you. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +gold +gold piece +zorkmid + A metal of characteristic yellow colour, the most precious + metal used as a common commercial medium of exchange. Symbol, + Au; at. no. 79; at. wt. 197.2. It is the most malleable + and ductile of all metals, and very heavy (sp. gr., 19.3). + It is quite unalterable by heat, moisture, and most + corrosive agents, and therefore well suited for its use in + coin and jewelry. + [ Webster's New International Dictionary + of the English Language, Second Edition ] +gold golem + The bellows he set away from the fire, and gathered all the tools + wherewith he wrought into a silver chest; and with a sponge wiped + he his face and his two hands withal, and his mighty neck and + shaggy breast, and put upon him a tunic, and grasped a stout staff, + and went forth halting; but there moved swiftly to support their + lord handmaidens wrought of gold in the semblance of living maids. + In them is understanding in their hearts, and in them speech and + strength, and they know cunning handiwork by gift of the immortal + gods. + [ The Iliad, by Homer ] +~gold golem +~flesh golem +*golem + "The original story harks back, so they say, to the sixteenth + century. Using long-lost formulas from the Kabbala, a rabbi is + said to have made an artificial man -- the so-called Golem -- to + help ring the bells in the Synagogue and for all kinds of other + menial work. + "But he hadn't made a full man, and it was animated by some sort + of vegetable half-life. What life it had, too, so the story + runs, was only derived from the magic charm placed behind its + teeth each day, that drew down to itself what was known as the + `free sidereal strength of the universe.' + "One evening, before evening prayers, the rabbi forgot to take + the charm out of the Golem's mouth, and it fell into a frenzy. + It raged through the dark streets, smashing everything in its + path, until the rabbi caught up with it, removed the charm, and + destroyed it. Then the Golem collapsed, lifeless. All that was + left of it was a small clay image, which you can still see in + the Old Synagogue." ... + [ The Golem, by Gustav Meyrink ] +grave + "Who'd care to dig 'em," said the old, old man, + "Those six feet marked in chalk? + Much I talk, more I walk; + Time I were buried," said the old, old man. + [ Three Songs to the Same Tune, by W.B. Yeats ] +grayswandir + Why had I been wearing Grayswandir? Would another weapon have + affected a Logrus-ghost as strongly? Had it really been my + father, then, who had brought me here? And had he felt I might + need the extra edge his weapon could provide? I wanted to + think so, to believe that he had been more than a Pattern-ghost. + [ Knight of Shadows, by Roger Zelazny ] +*grease + ANOINT, v.t. To grease a king or other great functionary + already sufficiently slippery. + [ The Devil's Dictionary, by Ambrose Bierce ] +gremlin + The gremlin is a highly intelligent and completely evil + creature. It lives to torment other creatures and will go + to great lengths to inflict pain or cause injury. + + Suddenly, Wilson thought about war, about the newspaper + stories which recounted the alleged existence of creatures in + the sky who plagued the Allied pilots in their duties. They + called them gremlins, he remembered. Were there, actually, + such beings? Did they, truly, exist up here, never falling, + riding on the wind, apparently of bulk and weight, yet + impervious to gravity? + He was thinking that when the man appeared again. + [ Nightmare at 20,000 Feet, by Richard Matheson ] +grid bug + These electronically based creatures are not native to this + universe. They appear to come from a world whose laws of + motion are radically different from ours. + + Tron looked to his mate and pilot. "I'm going to check on + the beam connection, Yori. You two can keep a watch out for + grid bugs." Tron paced forward along the slender catwalk + that still seemed awfully insubstantial to Flynn, though he + knew it to be amazingly sturdy. He gazed after Tron, asking + himself what in the world a grid bug was, and hoping that the + beam connection -- to which he'd given no thought whatsoever + until this moment -- was healthy and sound." + [ Tron, novel by Brian Daley, story by Steven Lisberger ] +gunyoki + The samurai's last meal before battle. It was usually made + up of cooked chestnuts, dried seaweed, and sake. +hachi + Hachi was a dog that went with his master, a professor, to + the Shibuya train station every morning. In the afternoon, + when his master was to return from work Hachi would be there + waiting. One day his master died at the office, and did not + return. For over ten years Hachi returned to the station + every afternoon to wait for his master. When Hachi died a + statue was erected on the station platform in his honor. It + is said to bring you luck if you touch his statue. +*harp + A triangular stringed instrument, often Magic. Even when not + Magic, a Harp is surprisingly portable and tough and can be + carried everywhere on the back of the Bard or Harper in all + weathers. A Harp seldom goes out of tune and never warps. + Its strings break only in very rare instances, usually + because the Harper is sulking or crossed in love. This is + just as well as no one seems to make or sell spare strings. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] + + After breakfast was over, the ogre called out: "Wife, wife, + bring me my golden harp." So she brought it and put it on + the table before him. Then he said: "Sing!" and the golden + harp sang most beautifully. And it went on singing till the + ogre fell asleep, and commenced to snore like thunder. + Then Jack lifted up the copper-lid very quietly and got down + like a mouse and crept on hands and knees till he came to the + table, when up he crawled, caught hold of the golden harp and + dashed with it towards the door. But the harp called out + quite loud: "Master! Master!" and the ogre woke up just in + time to see Jack running off with his harp. + [ Jack and the Beanstalk, from English Fairy Tales, + by Joseph Jacobs ] +healer +* healer +attendant +doctor +physician + I swear by Apollo the physician, and Aesculapius, and Health, + and All-heal, and all the gods and goddesses, that, according + to my ability and judgment, I will keep this Oath and this + stipulation -- to reckon him who taught me this Art equally dear + to me as my parents, to share my substance with him, and relieve + his necessities if required; to look upon his offspring in the + same footing as my own brothers, and to teach them this art, if + they shall wish to learn it, without fee or stipulation; and + that by precept, lecture, and every other mode of instruction, + I will impart a knowledge of the Art to my own sons, and those + of my teachers, and to disciples bound by a stipulation and oath + according to the law of medicine, but to none others. I will + follow that system of regimen which, according to my ability and + judgment, I consider for the benefit of my patients, and abstain + from whatever is deleterious and mischievous. [...] + [ Hippocrates' Oath, translated by Francis Adams ] + + PHYSICIAN, n. One upon whom we set our hopes when ill and our + dogs when well. + [ The Devil's Dictionary, by Ambrose Bierce ] +heart of ahriman + The other three drew in their breath sharply, and the dark, + powerful man who stood at the head of the sarcophagus whispered: + "The Heart of Ahriman!" The other lifted a quick hand + for silence. Somewhere a dog began howling dolefully, and a + stealthy step padded outside the barred and bolted door. ... + But none looked aside from the mummy case over which the man + in the ermine-trimmed robe was now moving the great flaming + jewel, while he muttered an incantation that was old when + Atlantis sank. The glare of the gem dazzled their eyes, so + that they could not be sure what they saw; but with a + splintering crash, the carven lid of the sarcophagus burst + outward as if from some irresistible pressure applied from + within and the four men, bending eagerly forward, saw the + occupant -- a huddled, withered, wizened shape, with dried + brown limbs like dead wood showing through moldering bandages. + "Bring that thing back?" muttered the small dark man who + stood on the right, with a short, sardonic laugh. "It is + ready to crumble at a touch. We are fools ---" + [ Conan The Conqueror, by Robert E. Howard ] +hell hound* + Hell hounds are fire-breathing canines from another plane of + existence brought here in the service of evil beings. A hell + hound resembles a large hound with rust-red or red-brown fur, + and red, glowing eyes. The markings, teeth, and tongue are + soot black. It stands two to three feet high at the shoulder + and has a distinct odour of smoke and sulphur. The baying + sounds it makes have an eerie, hollow tone that sends a shiver + through any who hear them. +hermes + Messenger and herald of the Olympians. Being required to do + a great deal of travelling and speaking in public, he became + the god of eloquence, travellers, merchants, and thieves. He + was one of the most energetic of the Greek gods, a + Machiavellian character full of trickery and sexual vigour. + Like other Greek gods, he is endowed with not-inconsiderable + sexual prowess which he directs towards countryside nymphs. + He is a god of boundaries, guardian of graves and patron deity + of shepherds. He is usually depicted as a handsome young + man wearing winged golden sandals and holding a magical + herald's staff consisting of intertwined serpents, the + kerykeion. He is reputedly the only being able to find his way + to the underworld ferry of Charon and back again. He is said + to have invented, among other things, the lyre, Pan's Pipes, + numbers, the alphabet, weights and measures, and sacrificing. +hezrou + "Hezrou" is the common name for the type II demon. It is + among the weaker of demons, but still quite formidable. +hippocrates + Greek physician, recognized as the father of medicine. He + is believed to have been born on the island of Cos, to have + studied under his father, a physician, to have traveled for + some time, perhaps studying in Athens, and to have then + returned to practice, teach, and write at Cos. The + Hippocratic or Coan school that formed around him was of + enormous importance in separating medicine from superstition + and philosophic speculation, placing it on a strictly + scientific plane based on objective observation and critical + deductive reasoning. + [ The Columbia Encyclopedia, Sixth Edition ] +hobbit + Hobbits are an unobtrusive but very ancient people, more + numerous formerly than they are today; for they love peace + and quiet and good tilled earth: a well-ordered and well- + farmed countryside was their favourite haunt. They do not + and did not understand or like machines more complicated + than a forge-bellows, a water-mill, or a handloom, although + they were skillful with tools. Even in ancient days they + were, as a rule, shy of "the Big Folk", as they call us, and + now they avoid us with dismay and are becoming hard to find. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +hobgoblin + Hobgoblin. Used by the Puritans and in later times for + wicked goblin spirits, as in Bunyan's "Hobgoblin nor foul + friend", but its more correct use is for the friendly spirits + of the brownie type. In "A midsummer night's dream" a + fairy says to Shakespeare's Puck: + Those that Hobgoblin call you, and sweet Puck, + You do their work, and they shall have good luck: + Are you not he? + and obviously Puck would not wish to be called a hobgoblin + if that was an ill-omened word. + Hobgoblins are on the whole, good-humoured and ready to be + helpful, but fond of practical joking, and like most of the + fairies rather nasty people to annoy. Boggarts hover on the + verge of hobgoblindom. Bogles are just over the edge. + One Hob mentioned by Henderson, was Hob Headless who haunted + the road between Hurworth and Neasham, but could not cross + the little river Kent, which flowed into the Tess. He was + exorcised and laid under a large stone by the roadside for + ninety-nine years and a day. If anyone was so unwary as to + sit on that stone, he would be unable to quit it for ever. + The ninety-nine years is nearly up, so trouble may soon be + heard of on the road between Hurworth and Neasham. + [ A Dictionary of Fairies, by Katharine Briggs ] +holy water + "We want a word with you," said Ligur (in a tone of voice + intended to imply that "word" was synonymous with "horrifically + painful eternity"), and the squat demon pushed open the office + door. + The bucket teetered, then fell neatly on Ligur's head. + Drop a lump of sodium in water. Watch it flame and burn and + spin around crazily, flaring and sputtering. This was like + that, just nastier. + The demon peeled and flared and flickered. Oily brown smoke + oozed from it, and it screamed and it screamed and it screamed. + Then it crumpled, folded in on itself, and what was left lay + glistening on the burnt and blackened circle of carpet, looking + like a handful of mashed slugs. + "Hi," said Crowley to Hastur, who had been walking behind Ligur, + and had unfortunately not been so much as splashed. + There are some things that are unthinkable; there are some + depths that not even demons would believe other demons would + stoop to. + ". . . Holy water. You bastard," said Hastur. "You complete + _bastard_. He hadn't never done nothing to _you_." + "Yet," corrected Crowley. + [ Good Omens, by Neil Gaiman and Terry Pratchett ] +hom*nculus + A homunculus is a creature summoned by a mage to perform some + particular task. They are particularly good at spying. They + are smallish creatures, but very agile. They can put their + victims to sleep with a venomous bite, but due to their size, + the effect does not last long on humans. + + "Tothapis cut him off. 'Be still and hearken. You will travel + aboard the sacred wingboat. Of it you may not have heard; but + it will bear you thither in a night and a day and a night. + With you will go a homunculus that can relay your words to me, + and mine to you, across the leagues between at the speed of + thought.'" + [ Conan the Rebel, by Poul Anderson ] +# also gets 'pruning hook' aka guisarme +*hook + But as for Queequeg -- why, Queequeg sat there among them -- + at the head of the table, too, it so chanced; as cool as an + icicle. To be sure I cannot say much for his breeding. His + greatest admirer could not have cordially justified his + bringing his harpoon into breakfast with him, and using it + there without ceremony; reaching over the table with it, to + the imminent jeopardy of many heads, and grappling the + beefsteaks towards him. + [ Moby Dick, by Herman Melville ] +~unicorn horn +*horn + Roland hath set the Olifant to his mouth, + He grasps it well, and with great virtue sounds. + High are those peaks, afar it rings and loud, + Thirty great leagues they hear its echoes mount. + So Charles heard, and all his comrades round; + Then said that King: "Battle they do, our counts!" + And Guenelun answered, contrarious: + "That were a lie, in any other mouth." + [ The Song of Roland ] +horned devil + Horned devils lack any real special abilities, though they + are quite difficult to kill. +~horsem* +*horse + King Richard III: A horse! a horse! my kingdom for a horse! + Catesby: Withdraw, my lord; I'll help you to a horse. + King Richard III: Slave, I have set my life upon a cast, + And I will stand the hazard of the die: + I think there be six Richmonds in the field; + Five have I slain to-day instead of him. + A horse! a horse! my kingdom for a horse! + [ King Richard III, by William Shakespeare ] +*horsem* +rider* +death +famine +pestilence +war +hunger + [Pestilence:] And I saw when the Lamb opened one of the seals, + and I heard, as it were the noise of thunder, one of the four + beasts saying, Come and see. And I saw, and behold a white + horse: and he that sat on him had a bow; and a crown was given + unto him: and he went forth conquering, and to conquer. + + [War:] And when he had opened the second seal, I heard the + second beast say, Come and see. And there went out another + horse that was red: and power was given to him that sat thereon + to take peace from the earth, and that they should kill one + another: and there was given unto him a great sword. + + [Famine:] And when he had opened the third seal, I heard the + third beast say, Come and see. And I beheld, and lo a black + horse; and he that sat on him had a pair of balances in his + hand. And I heard a voice in the midst of the four beasts say, + A measure of wheat for a penny, and three measures of barley + for a penny; and see thou hurt not the oil and the wine. + + [Death:] And when he had opened the fourth seal, I heard the + voice of the fourth beast say, Come and see. And I looked, and + behold a pale horse: and his name that sat on him was Death, + and Hell followed with him. And power was given unto them over + the fourth part of the earth, to kill with sword, and with + hunger, and with death, and with the beasts of the earth. + [ Revelations of John, 6:1-8 ] +huan*ti + The first of five mythical Chinese emperors, Huan Ti is known + as the yellow emperor. He rules the _moving_ heavens, as + opposed to the _dark_ heavens. He is an inventor, said to + have given mankind among other things, the wheel, armour, and + the compass. He is the god of fortune telling and war. +hu*h*eto*l +minion of huhetotl + Huehuetotl, or Huhetotl, which means Old God, was the Aztec + (classical Mesoamerican) god of fire. He is generally + associated with paternalism and one of the group classed + as the Xiuhtecuhtli complex. He is known to send his + minions to wreak havoc upon ordinary humans. + [ after the Encyclopedia of Gods, by Michael Jordan ] +humanoid + Humanoids are all approximately the size of a human, and may + be mistaken for one at a distance. They are usually of a + tribal nature, and will fiercely defend their lairs. Usually + hostile, they may even band together to raid and pillage + human settlements. +human +chieftain +guard +ninja +nurse +page +ronin +shopkeeper +student +thug +warrior +*watch* +player + These strange creatures live mostly on the surface of the + earth, gathering together in societies of various forms, but + occasionally a stray will descend into the depths and commit + mayhem among the dungeon residents who, naturally, often + resent the intrusion of such beasts. They are capable of + using weapons and magic, and it is even rumored that the + Wizard of Yendor is a member of this species. +hunter + What of the hunting, hunter bold? + Brother, the watch was long and cold. + What of the quarry ye went to kill? + Brother, he crops in the jungle still. + Where is the power that made your pride? + Brother, it ebbs from my flank and side. + Where is the haste that ye hurry by? + Brother, I go to my lair to die. + [ The Jungle Book, by Rudyard Kipling ] +ice devil + Ice devils are large semi-insectoid creatures, who are + equally at home in the fires of Hell and the cold of Limbo, + and who can cause the traveller to feel the latter with just + a touch of their tail. +imp + ... imps ... little creatures of two feet high that could + gambol and jump prodigiously; ... + [ The Charwoman's Shadow, by Lord Dunsany ] + + An 'imp' is an off-shoot or cutting. Thus an 'ymp tree' was + a grafted tree, or one grown from a cutting, not from seed. + 'Imp' properly means a small devil, an off-shoot of Satan, + but the distinction between goblins or bogles and imps from + hell is hard to make, and many in the Celtic countries as + well as the English Puritans regarded all fairies as devils. + The fairies of tradition often hover uneasily between the + ghostly and the diabolic state. + [ A Dictionary of Fairies, by Katharine Briggs ] +incubus +succubus + The incubus and succubus are male and female versions of the + same demon, one who lies with a human for its own purposes, + usually to the detriment of the mortals who are unwise in + their dealings with them. +*iron ball +*iron chain + "You are fettered, " said Scrooge, trembling. "Tell me why?" + "I wear the chain I forged in life," replied the Ghost. "I + made it link by link, and yard by yard; I girded it on of my + own free will, and of my own free will I wore it. Is its + pattern strange to you?" + Scrooge trembled more and more. + "Or would you know," pursued the Ghost, "the weight and + length of the strong coil you bear yourself? It was full as + heavy and as long as this, seven Christmas Eves ago. You + have laboured on it, since. It is a ponderous chain!" + [ A Christmas Carol, by Charles Dickens ] +ishtar + Ishtar (the star of heaven) is the Mesopotamian goddess of + fertility and war. She is usually depicted with wings and + weapon cases at her shoulders, carrying a ceremonial double- + headed mace-scimitar embellished with lion heads, frequently + being accompanied by a lion. She is symbolized by an eight- + pointed star. + [ Encyclopedia of Gods, by Michael Jordan ] +issek + Now Issek of the Jug, whom Fafhrd chose to serve, was once + of the most lowly and unsuccessful of the gods, godlets + rather, in Lankhmar. He had dwelt there for about thirteen + years, during which time he had traveled only two squares up + the Street of the Gods and was now back again, ready for + oblivion. He is not to be confused with Issek the Armless, + Issek of the Burnt Legs, Flayed Issek, or any other of the + numerous and colorfully mutilated divinities of that name. + Indeed, his unpopularity may have been due in part to the + fact that the manner of his death -- racking -- was not + deemed particularly spectacular. ... However, after Fafhrd + became his acolyte, things somehow began to change. + [ Swords In The Mist, by Fritz Leiber ] +izchak + The shopkeeper of the lighting shop in the town level of the + gnomish mines is a tribute to Izchak Miller, a founding member + of the NetHack development team and a personal friend of a large + number of us. Izchak contributed greatly to the game, coding a + large amount of the shopkeep logic (hence the nature of the tribute) + as well as a good part of the alignment system, the prayer code and + the rewrite of "hell" in the 3.1 release. Izchak was a professor + of Philosophy, who taught at many respected institutions, including + MIT and Stanford, and who also worked, for a period of time, at + Xerox PARC. Izchak was the first "librarian" of the NetHack project, + and was a founding member of the DevTeam, joining in 1986 while he + was working at the University of Pennsylvania (hence our former + mailing list address). Until the 3.1.3 release, Izchak carefully + kept all of the code synchronized and arbitrated disputes between + members of the development teams. Izchak Miller passed away at the + age of 58, in the early morning hours of April 1, 1994 from + complications due to cancer. We then dedicated NetHack 3.2 in his + memory. + [ Mike Stephenson, for the NetHack DevTeam ] +jabberwock +vorpal* + "Beware the Jabberwock, my son! + The jaws that bite, the claws that catch! + Beware the Jubjub bird, and shun + The frumious Bandersnatch!" + + He took his vorpal sword in hand; + Long time the manxome foe he sought -- + So rested he by the Tumtum tree, + And stood awhile in thought. + + And, as in uffish thought he stood, + The Jabberwock, with eyes of flame, + Came whiffling through the tulgey wood, + And burbled as it came! + + One, two! One, two! And through and through + The vorpal blade went snicker-snack! + He left it dead, and with its head + He went galumphing back. + [ Jabberwocky, by Lewis Carroll ] +jackal + In Asiatic folktale, jackal provides for the lion; he scares + up game, which the lion kills and eats, and receives what is + left as reward. In stories from northern India he is + sometimes termed "minister to the king," i.e. to the lion. + From the legend that he does not kill his own food has arisen + the legend of his cowardice. Jackal's heart must never be + eaten, for instance, in the belief of peoples indigenous to + the regions where the jackal abounds. ... In Hausa Negro + folktale Jackal plays the role of sagacious judge and is + called "O Learned One of the Forest." The Bushmen say that + Jackal goes around behaving the way he does "because he is + Jackal". + [ Funk & Wagnalls Standard Dictionary of Folklore ] +jade* + Nothing grew among the ruins of the city. The streets were + broken and the walls of the houses had fallen, but there were + no weeds flowering in the cracks and it seemed that the city + had but recently been brought down by an earthquake. Only + one thing still stood intact, towering over the ruins. It + was a gigantic statue of white, gray and green jade - the + statue of a naked youth with a face of almost feminine beauty + that turned sightless eyes toward the north. + "The eyes!" Duke Avan Astran said. "They're gone!" + [ The Jade Man's Eyes, by Michael Moorcock ] +jaguar + Large, flesh-eating animal of the cat family, of Central and + South America. This feline predator (_Panthera onca_) is + sometimes incorrectly called a panther. + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] +jellyfish + I do not care to share the seas + With jellyfishes such as these; + Particularly Portuguese. + [ Lines on Meeting a Portuguese Man-o'-war while + Bathing, by Michael Flanders ] +juiblex +jubilex + Little is known about the Faceless Lord, even the correct + spelling of his name. He does not have a physical form as + we know it, and those who have peered into his realm claim + he is a slime-like creature who swallows other creatures + alive, spits acidic secretions, and causes disease in his + victims which can be almost instantly fatal. +kabuto + The kabuto is the helmet worn by the samurai. It was + characterized by a prominent beaked front which jutted out over + the brow to protect the wearer's face; a feature that gives + rise to their modern Japanese name of 'shokaku tsuki kabuto' + (battering-ram helmet). Their main constructional element + was an oval plate, the shokaku bo, slightly domed for the + head with a narrow prolongation in front that curved forwards + and downwards where it developed a pronounced central + fold. Two horizontal strips encircling the head were riveted + to this frontal strip: the lower one, the koshimaki (hip + wrap), formed the lower edge of the helmet bowl; the other, + the do maki (body wrap), was set at about the level of the + temples. Filling the gaps between these strips and the shokaku + bo were small plates, sometimes triangular but more commonly + rectangular in shape. Because the front projected so + far from the head, the triangular gap beneath was filled by + a small plate, the shoshaku tei ita, whose rear edge bent + downwards into a flange that rested against the forehead. + [ Arms & Armour of the Samurai, by Bottomley & Hopson ] +katana + The katana is a long, single-edged samurai sword with a + slightly curved blade. Its long handle is designed to allow + it to be wielded with either one or two hands. +ki-rin + The ki-rin is a strange-looking flying creature. It has + scales, a mane like a lion, a tail, hooves, and a horn. It + is brightly colored, and can usually be found flying in the + sky looking for good deeds to reward. +king arthur +*arthur + Ector took both his sons to the church before which the + anvil had been placed. There, standing before the anvil, he + commanded Kay: "Put the sword back into the steel if you + really think the throne is yours!" But the sword glanced + off the steel. "Now it is your turn", Ector said facing + Arthur. + The young man lifted the sword and thrust with both arms; the + blade whizzed through the air with a flash and drilled the + metal as if it were mere butter. Ector and Kay dropped to + their knees before Arthur. + "Why, father and brother, do you bow before me?", Arthur asked + with wonder in his voice. + "Because now I know for sure that you are the king, not only + by birth but also by law", Ector said. "You are no son of + mine nor are you Kay's brother. Immediately after your birth, + Merlin the Wise brought you to me to be raised safely. And + though it was me that named you Arthur when you were baptized, + you are really the son of brave king Uther Pendragon and queen + Igraine..." + And after these words, the lord rose and went to see the arch- + bishop to impart to him what had passed. + [ Van Gouden Tijden Zingen de Harpen, by Vladimir Hulpach, + Emanuel Frynta, and Vackav Cibula ] +knife +stiletto + Possibly perceiving an expression of dubiosity on their + faces, the globetrotter went on adhering to his adventures. + + -- And I seen a man killed in Trieste by an Italian chap. + Knife in his back. Knife like that. + + Whilst speaking he produced a dangerous looking clasp knife, + quite in keeping with his character, and held it in the + striking position. + + -- In a knockingshop it was count of a tryon between two + smugglers. Fellow hid behind a door, come up behind him. + Like that. Prepare to meet your God, says he. Chuck! It + went into his back up to the butt. + [ Ulysses, by James Joyce ] +knight +* knight + Here lies the noble fearless knight, + Whose valour rose to such a height; + When Death at last had struck him down, + His was the victory and renown. + He reck'd the world of little prize, + And was a bugbear in men's eyes; + But had the fortune in his age + To live a fool and die a sage. + [ Don Quixote of La Mancha by Miquel de + Cervantes Saavedra ] +~kobold ??m* +*kobold* + The race of kobolds are reputed to be an artificial creation + of a master wizard (demi-god?). They are about 3' tall with + a vaguely dog-like face. They bear a violent dislike of the + Elven race, and will go out of their way to cause trouble + for Elves at any time. +*kop* + The Kops are a brilliant concept. To take a gaggle of inept + policemen and display them over and over again in a series of + riotously funny physical punishments plays equally well to the + peanut gallery and the expensive box seats. People hate cops. + Even people who have never had anything to do with cops hate + them. Of course, we count on them to keep order and to protect + us when we need protecting, and we love them on television shows + in which they have nerves of steel and hearts of gold, but in + the abstract, as a nation, collectively we hate them. They are + too much like high school principals. We're very happy to see + their pants fall down, and they look good to us with pie on + their faces. The Keystone Kops turn up--and they get punished + for it, as they crash into each other, fall down, and suffer + indignity after indignity. Here is pure movie satisfaction. + + The Kops are very skillfully presented. The comic originality + and timing in one of their chase scenes requires imagination + to think up, talent to execute, understanding of the medium, + and, of course, raw courage to perform. The Kops are madmen + presented as incompetents, and they're madmen rushing around + in modern machines. What's more, the machines they were operating + in their routines were newly invented and not yet experienced + by the average moviegoer. (In the early days of automobiles, + it was reported that there were only two cars registered in all + of Kansas City, and they ran into each other. There is both + poetry and philosophy in this fact, but most of all, there is + humor. Sennett got the humor.) + [ Silent Stars, by Jeanine Basinger ] +kos + "I am not a coward!" he cried. "I'll dare Thieves' House + and fetch you Krovas' head and toss it with blood a-drip at + Vlana's feet. I swear that, witness me, Kos the god of + dooms, by the brown bones of Nalgron my father and by his + sword Graywand here at my side!" + [ Swords and Deviltry, by Fritz Leiber ] +koto + A Japanese harp. +kraken + Out from the water a long sinuous tentacle had crawled; it + was pale-green and luminous and wet. Its fingered end had + hold of Frodo's foot, and was dragging him into the water. + Sam on his knees was now slashing at it with a knife. The + arm let go of Frodo, and Sam pulled him away, crying out + for help. Twenty other arms came rippling out. The dark + water boiled, and there was a hideous stench. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +*lady +offler + Blind Io took up the dice-box, which was a skull whose various + orifices had been stoppered with rubies, and with several of + his eyes on the Lady he rolled three fives. She smiled. This + was the nature of the Lady's eyes: they were bright green, + lacking iris or pupil, and they glowed from within. + + The room was silent as she scrabbled in her box of pieces and, + from the very bottom, produced a couple that she set down on + the board with two decisive clicks. The rest of the players, + as one God, craned forward to peer at them. + + "A wenegade wiffard and fome fort of clerk," said Offler the + Crocodile God, hindered as usual by his tusks. "Well, + weally!" With one claw he pushed a pile of bone-white tokens + into the centre of the table. + + The Lady nodded slightly. She picked up the dice-cup and held + it as steady as a rock, yet all the Gods could hear the three + cubes rattling about inside. And then she sent them bouncing + across the table. + + A six. A three. A five. + + Something was happening to the five, however. Battered by the + chance collision of several billion molecules, the die flipped + onto a point, spun gently and came down a seven. Blind Io + picked up the cube and counted the sides. + + "Come _on_," he said wearily, "Play fair." + [ The Colour of Magic, by Terry Pratchett ] +*lamp + When he came to himself he told his mother what had passed, + and showed her the lamp and the fruits he had gathered in the + garden, which were in reality precious stones. He then asked + for some food. + + "Alas! child," she said, "I have nothing in the house, but I + have spun a little cotton and will go and sell it." + + Aladdin bade her keep her cotton, for he would sell the lamp + instead. As it was very dirty she began to rub it, that it + might fetch a higher price. Instantly a hideous genie + appeared, and asked what she would have. She fainted away, + but Aladdin, snatching the lamp, said boldly: + "Fetch me something to eat!" + [ Aladdin, from The Arabian Nights, by Andrew Lang ] +lance + With this the wind increased, and the mill sails began to turn + about; which Don Quixote espying, said, 'Although thou movest + more arms than the giant Briareus thou shalt stoop to me.' + And, after saying this, and commending himself most devoutly + to his Lady Dulcinea, desiring her to succor him in that trance, + covering himself well with his buckler, and setting his lance + on his rest, he spurred on Rozinante, and encountered with the + first mill that was before him, and, striking his lance into + the sail, the wind swung it about with such fury, that it broke + his lance into shivers, carrying him and his horse after it, + and finally tumbled him a good way off from it on the field in + evil plight. + [ Don Quixote of La Mancha by Miquel de + Cervantes Saavedra ] +leash + They had splendid heads, fine shoulders, strong legs, and + straight tails. The spots on their bodies were jet-black and + mostly the size of a two-shilling piece; they had smaller + spots on their heads, legs, and tails. Their noses and eye- + rims were black. Missis had a most winning expression. + Pongo, though a dog born to command, had a twinkle in his + eye. They walked side by side with great dignity, only + putting the Dearlys on the leash to lead them over crossings. + [ The Hundred and One Dalmatians, by Dodie Smith ] +lembas* + In the morning, as they were beginning to pack their slender + goods, Elves that could speak their tongue came to them and + brought them many gifts of food and clothing for their + journey. The food was mostly in the form of very thin cakes, + made of a meal that was baked a light brown on the outside, + and inside was the colour of cream. Gimli took up one of the + cakes and looked at it with a doubtful eye. + 'Cram,' he said under his breath, as he broke off a crisp + corner and nibbled at it. His expression quickly changed, + and he ate all the rest of the cake with relish. + 'No more, no more!' cried the Elves laughing. 'You have + eaten enough already for a long day's march.' + 'I thought it was only a kind of cram, such as the Dalemen + make for journeys in the wild,' said the Dwarf. + 'So it is,' they answered. 'But we call it lembas or + waybread, and it is more strengthening than any foods made by + Men, and it is more pleasant than cram, by all accounts.' + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +lemure + The lowliest of the inhabitants of hell. +leocrotta +leu*otta + ... the leucrocotta, a wild beast of extraordinary swiftness, + the size of the wild ass, with the legs of a Stag, the neck, + tail, and breast of a lion, the head of a badger, a cloven + hoof, the mouth slit up as far as the ears, and one continuous + bone instead of teeth; it is said, too, that this animal can + imitate the human voice. + [ Curious Creatures in Zoology, by John Ashton ] +leprechaun + The Irish Leprechaun is the Faeries' shoemaker and is known + under various names in different parts of Ireland: + Cluricaune in Cork, Lurican in Kerry, Lurikeen in Kildare + and Lurigadaun in Tipperary. Although he works for the + Faeries, the Leprechaun is not of the same species. He is + small, has dark skin and wears strange clothes. His nature + has something of the manic-depressive about it: first he + is quite happy, whistling merrily as he nails a sole on to a + shoe; a few minutes later, he is sullen and morose, drunk + on his home-made heather ale. The Leprechaun's two great + loves are tobacco and whiskey, and he is a first-rate con-man, + impossible to out-fox. No one, no matter how clever, has ever + managed to cheat him out of his hidden pot of gold or his + magic shilling. At the last minute he always thinks of some + way to divert his captor's attention and vanishes in the + twinkling of an eye. + [ A Field Guide to the Little People + by Nancy Arrowsmith & George Moorse ] +*lich + But on its heels ere the sunset faded, there came a second + apparition, striding with incredible strides and halting when + it loomed almost upon me in the red twilight-the monstrous mummy + of some ancient king still crowned with untarnished gold but + turning to my gaze a visage that more than time or the worm had + wasted. Broken swathings flapped about the skeleton legs, and + above the crown that was set with sapphires and orange rubies, a + black something swayed and nodded horribly; but, for an instant, + I did not dream what it was. Then, in its middle, two oblique + and scarlet eyes opened and glowed like hellish coals, and two + ophidian fangs glittered in an ape-like mouth. A squat, furless, + shapeless head on a neck of disproportionate extent leaned + unspeakably down and whispered in the mummy's ear. Then, with + one stride, the titanic lich took half the distance between us, + and from out the folds of the tattered sere-cloth a gaunt arm + arose, and fleshless, taloned fingers laden with glowering gems, + reached out and fumbled for my throat . . . + [ The Abominations of Yondo, Clark Ashton Smith, 1926 ] +lichen + The chamber was of unhewn rock, round, as near as might + be, eighteen or twenty feet across, and gay with rich + variety of fern and moss and lichen. The fern was in + its winter still, or coiling for the spring-tide; but + moss was in abundant life, some feathering, and some + gobleted, and some with fringe of red to it. + [ Lorna Doone, by R.D. Blackmore ] +~* of light +* light + Strange creatures formed from energy rather than matter, + lights are given to self-destructive behavior when battling + foes. +gecko +iguana +lizard + Lizards, snakes and the burrowing amphisbaenids make up the + order Squamata, meaning the scaly ones. The elongate, slim, + long-tailed bodies of lizards have become modified to enable + them to live in a wide range of habitats. Lizards can be + expert burrowers, runners, swimmers and climbers, and a few + can manage crude, short-distance gliding on rib-supported + "wings". Most are carnivores, feeding on invertebrate and + small vertebrate prey, but others feed on vegetation. + [ Macmillan Illustrated Animal Encyclopedia ] +loki + Loki, or Lopt, is described in Snorri's _Edda_ as being + "pleasing and handsome in appearance, evil in character, and + very capricious in behaviour". He is the son of the giant + Farbauti and of Laufey. + Loki is the Norse god of cunning, evil, thieves, and fire. + He hated the other gods and wanted to ruin them and overthrow + the universe. He committed many murders. As a thief, he + stole Freyja's necklace, Thor's belt and gauntlets of power, + and the apples of youth. Able to shapechange at will, he is + said to have impersonated at various times a mare, flea, fly, + falcon, seal, and an old crone. As a mare he gave birth to + Odin's horse Sleipnir. He also allegedly sired the serpent + Midgard, the mistress of the netherworld, Hel, and the wolf + Fenrir, who will devour the sun at Ragnarok. +*longbow of diana + This legendary bow grants ESP when carried and can reflect magical + attacks when wielded. When invoked it provides a supply of arrows. +# long worm -- see "worm" +looking glass +mirror + But as Snow White grew, she became more and more beautiful, + and by the time she was seven years old she was as beautiful + as the day and more beautiful than the queen herself. One + day when the queen said to her mirror: + + "Mirror, Mirror, here I stand. + Who is the fairest in the land?" - + + the mirror replied: + + "You, O Queen, are the fairest here, + But Snow White is a thousand times more fair." + [ Snow White, by Jakob and Wilhelm Grimm ] +lord carnarvon + Lord Carnarvon was a personality who could have been produced + nowhere but in England, a mixture of sportsman and collector, + gentleman and world traveler, a realist in action and a + romantic in feeling. ... In 1903 he went for the first time + to Egypt in search of a mild climate and while there visited + the excavation sites of several archaeological expeditions. + ... In 1906 he began his own excavations. + [ Gods, Graves, and Scholars, by C. W. Ceram ] +lord sato + Lord Sato was the family head of the Taro Clan, and a mighty + daimyo. He is a loyal servant of the Emperor, and will do + everything in his power to further the imperial cause. +lord surt* + Yet first was the world in the southern region, which was + named Muspell; it is light and hot; that region is glowing + and burning, and impassable to such as are outlanders and + have not their holdings there. He who sits there at the + land's-end, to defend the land, is called Surtr; he brandishes + a flaming sword, and at the end of the world he shall go forth + and harry, and overcome all the gods, and burn all the + world with fire. + [ The Prose Edda, by Snorri Sturluson ] +lug* + Lugh, or Lug, was the sun god of the Irish Celts. One of his + weapons was a rod-sling which worshippers sometimes saw in + the sky as a rainbow. As a tribal god, he was particularly + skilled in the use of his massive, invincible spear, which + fought on its own accord. One of his epithets is _lamfhada_ + (of the long arm). He was a young and apparently more + attractive deity than Dagda, the father of the gods. Being + able to shapeshift, his name translates as lynx. +lurker* + These dungeon scavengers are very adept at blending into the + surrounding walls and ceilings of the dungeon due to the + stone-like coloring of their skin. +lycanthrope +were* +human were* +*were + In 1573, the Parliament of Dole published a decree, permitting + the inhabitants of the Franche-Comte to pursue and kill a + were-wolf or loup-garou, which infested that province, + "notwithstanding the existing laws concerning the chase." + The people were empowered to "assemble with javelins, + halberds, pikes, arquebuses and clubs, to hunt and pursue the + said were-wolf in all places where they could find it, and to + take, burn, and kill it, without incurring any fine or other + penalty." The hunt seems to have been successful, if we may + judge from the fact that the same tribunal in the following + year condemned to be burned a man named Giles Garnier, who + ran on all fours in the forest and fields and devoured little + children, "even on Friday." The poor lycanthrope, it appears, + had as slight respect for ecclesiastical feasts as the French + pig, which was not restrained by any feeling of piety from + eating infants on a fast day. + [ The History of Vampires, by Dudley Wright ] +lynx + To dream of seeing a lynx, enemies are undermining your + business and disrupting your home affairs. For a woman, + this dream indicates that she has a wary woman rivaling her + in the affections of her lover. If she kills the lynx, she + will overcome her rival. + [ 10,000 Dreams Interpreted, by Gustavus Hindman Miller ] +magic marker + The pen is mightier than the sword. + [ Richelieu, by Edward Bulwer-Lytton ] +magic mirror of merlin + This powerful mirror was created by Merlin, the druid, in ages + past, when trees sang and rocks danced. It protects all who + carry it from magic missiles, and gives them ESP. +mail d*emon + It is rumoured that these strange creatures can be harmed by + domesticated canines only. +ma*annan* + Normally called Manannan, Ler's son was the patron of + merchants and sailors. Manannan had a sword which never + failed to slay, a boat which propelled itself wherever its + owner wished, a horse which was swifter than the wind, and + magic armour which no sword could pierce. He later became + god of the sea, beneath which he lived in Tir na nOc, the + underworld. +manes + The gnats of the dungeon, these swarming monsters are rarely + seen alone. +marduk + First insisting on recognition as supreme commander, Marduk + defeated the Dragon, cut her body in two, and from it created + heaven and earth, peopling the world with human beings who not + unnaturally showed intense gratitude for their lives. The + gods were also properly grateful, invested him with many + titles, and eventually permitted themselves to be embodied in + him, so that he became supreme god, plotting the whole course + of known life from the paths of the planets to the daily + events in the lives of men. + [ The Immortals, by Derek and Julia Parker ] +marilith + The marilith has a torso shaped like that of a human female, + and the lower body of a great snake. It has multiple arms, + and can freely attack with all of them. Since it is + intelligent enough to use weapons, this means it can cause + great damage. +mars + The god of war, and one of the most prominent and worshipped + gods. In early Roman history he was a god of spring, growth in + nature, and fertility, and the protector of cattle. Mars is + also mentioned as a chthonic god (earth-god) and this could + explain why he became a god of death and finally a god of war. + He is the son of Jupiter and Juno. + [ Encyclopedia Mythica, ed. M.F. Lindemans ] +master assassin + He strolled down the stairs, followed by a number of assassins. + When he was directly in front of Ymor he said: "I've come for + the tourist." ... + "One step more and you'll leave here with fewer eyeballs than + you came with," said the thiefmaster. "So sit down and have + a drink, Zlorf, and let's talk about this sensibly. _I_ + thought we had an agreement. You don't rob -- I don't kill. + Not for payment, that is," he added after a pause. + Zlorf took the proffered beer. + "So?" he said. "I'll kill him. Then you rob him. Is he that + funny looking one over there?" + "Yes." + Zlorf stared at Twoflower, who grinned at him. He shrugged. + He seldom wasted time wondering why people wanted other people + dead. It was just a living. + "Who is your client, may I ask?" said Ymor. + Zlorf held up a hand. "Please!" he protested. "Professional + etiquette." + [ The Colour of Magic, by Terry Pratchett ] +master key of thievery + This skeleton key was fashioned in ages past and imbued with + a powerful magic which allows it to open any lock. When + carried, it grants its owner warning, teleport control, and + reduces all physical damage by half. Finally, when invoked, + it has the ability to disarm any trap. +master of thieves + There was a flutter of wings at the window. Ymor shifted his + bulk out of the chair and crossed the room, coming back with + a large raven. After he'd unfastened the message capsule from + its leg it flew up to join its fellows lurking among the + rafters. Withel regarded it without love. Ymor's ravens were + notoriously loyal to their master, to the extent that Withel's + one attempt to promote himself to the rank of greatest thief + in Ankh-Morpork had cost their master's right hand man his + left eye. But not his life, however. Ymor never grudged a + man his ambitions. + [ The Colour of Magic, by Terry Pratchett ] +mastodon + Any large, elephantlike mammal of the genera Mammut, Mastodon, + etc., from the Oligocene and Pleistocene epochs, having + conical projections on the molar teeth. + [ Webster's Encyclopedic Unabridged Dictionary + of the English Language ] +meat* +huge chunk of meat + Some hae meat and canna eat, + And some would eat that want it; + But we hae meat, and we can eat, + Sae let the Lord be thankit. + [ Grace Before Meat, by Robert Burns ] +medusa + Medusa, one of the three Gorgons or Graeae, is the only one + of her sisters to have assumed mortal form and inhabited the + dungeon world. + + When Perseus was grown up Polydectes sent him to attempt the + conquest of Medusa, a terrible monster who had laid waste the + country. She was once a beautiful maiden whose hair was her + chief glory, but as she dared to vie in beauty with Minerva, + the goddess deprived her of her charms and changed her + beautiful ringlets into hissing serpents. She became a cruel + monster of so frightful an aspect that no living thing could + behold her without being turned into stone. All around the + cavern where she dwelt might be seen the stony figures of men + and animals which had chanced to catch a glimpse of her and + had been petrified with the sight. Perseus, favoured by + Minerva and Mercury, the former of whom lent him her shield + and the latter his winged shoes, approached Medusa while she + slept and taking care not to look directly at her, but guided + by her image reflected in the bright shield which he bore, he + cut off her head and gave it to Minerva, who fixed it in the + middle of her Aegis. + [ Bulfinch's Mythology, by Thomas Bulfinch ] +melon + "What is it, Umbopa, son of a fool?" I shouted in Zulu. + "It is food and water, Macumazahn," and again he waved the + green thing. + Then I saw what he had got. It was a melon. We had hit upon + a patch of wild melons, thousands of them, and dead ripe. + "Melons!" I yelled to Good, who was next me; and in another + second he had his false teeth fixed in one. + I think we ate about six each before we had done, and, poor + fruit as they were, I doubt if I ever thought anything nicer. + [ King Solomon's Mines, by H. Rider Haggard ] +mercury + Roman god of commerce, trade and travellers. He is commonly + depicted carrying a caduceus (a staff with two snakes + intertwining around it) and a purse. +*mimic + The ancestors of the modern day chameleon, these creatures can + assume the form of anything in their surroundings. They may + assume the shape of objects or dungeon features. Unlike the + chameleon though, which assumes the shape of another creature + and goes in hunt of food, the mimic waits patiently for its + meals to come in search of it. +*mind flayer + This creature has a humanoid body, tentacles around its + covered mouth, and three long fingers on each hand. Mind + flayers are telepathic, and love to devour intelligent beings, + especially humans. If they hit their victim with a tentacle, + the mind flayer will slowly drain it of all intelligence, + eventually killing its victim. +mine* + Made by Dwarfs. The Rule here is that the Mine is either long + deserted or at most is inhabited by a few survivors who will + make confused claims to have been driven out/decimated by humans/ + other Dwarfs/Minions of the Dark Lord. Inhabited or not, this + Mine will be very complex, with many levels of galleries, + beautifully carved and engineered. What was being mined here + is not always evident, but at least some of the time it will + appear to have been Jewels, since it is customary to find + unwanted emeralds, etc., still embedded in the rock of the + walls. Metal will also be present, but only when made up into + armor and weapons (_wondrous_). + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +minotaur + The Minotaur was a monster, half bull, half human, the + offspring of Minos' wife Pasiphae and a wonderfully beautiful + bull. ... When the Minotaur was born Minos did not kill him. + He had Daedalus, a great architect and inventor, construct a + place of confinement for him from which escape was impossible. + Daedalus built the Labyrinth, famous throughout the world. + Once inside, one would go endlessly along its twisting paths + without ever finding the exit. + [ Mythology, by Edith Hamilton ] +mit*ra* + Originating in India (Mitra), Mithra is a god of light who + was translated into the attendant of the god Ahura Mazda in + the light religion of Persia; from this he was adopted as + the Roman deity Mithras. He is not generally regarded as a + sky god but a personification of the fertilizing power of + warm, light air. According to the _Avesta_, he possesses + 10,000 eyes and ears and rides in a chariot drawn by white + horses. Mithra, according to Zarathustra, is concerned with + the endless battle between light and dark forces: he + represents truth. He is responsible for the keeping of oaths + and contracts. He is attributed with the creation of both + plants and animals. His chief adversary is Ahriman, the + power of darkness. + [ The Encyclopaedia of Myths and Legends of All + Nations, by Herbert Spencer Robinson and + Knox Wilson ] +*mithril* + _Mithril_! All folk desired it. It could be beaten like + copper, and polished like glass; and the Dwarves could make + of it a metal, light and yet harder than tempered steel. + Its beauty was like to that of common silver, but the beauty + of _mithril_ did not tarnish or grow dim. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +*mitre of holiness + This helm of brilliance performs all of the normal functions + of a helm of brilliance, but also has the ability to protect + anyone who carries it from fire. When invoked, it boosts + the energy of the invoker, allowing them to cast more spells. +mjollnir + Forged by the dwarves Eitri and Brokk, in response to Loki's + challenge, Mjollnir is an indestructible war hammer. It has + two magical properties: when thrown it always returned to + Thor's hand; and it could be made to shrink in size until it + could fit inside Thor's shirt. Its only flaw is that it has + a short handle. The other gods judged Mjollnir the winner of + the contest because, of all the treasures created, it alone had + the power to protect them from the giants. As the legends + surrounding Mjollnir grew, it began to take on the quality of + "vigja", or consecration. Thor used it to consecrate births, + weddings, and even to raise his goats from the dead. In the + Norse mythologies Mjollnir is considered to represent Thor's + governance over the entire cycle of life - fertility, birth, + destruction, and resurrection. +~slime mold +*mold + Mold, multicellular organism of the division Fungi, typified + by plant bodies composed of a network of cottony filaments. + The colors of molds are due to spores borne on the filaments. + Most molds are saprophytes. Some species (e.g., penicillium) + are used in making cheese and antibiotics. + [ The Concise Columbia Encyclopedia ] +mol?ch + And the Lord spake unto Moses, saying, + Again, thou shalt say to the children of Israel, Whosoever + he be of the children of Israel, or of the strangers that + sojourn in Israel, that giveth any of his seed unto Molech; + he shall surely be put to death: the people of the land shall + stone him with stones. + And I will set my face against that man, and will cut him off + from among his people; because he hath given of his seed unto + Molech, to defile my sanctuary, and to profane my holy name. + And if the people of the land do any ways hide their eyes + from the man, when he giveth of his seed unto Molech, and kill + him not: + Then I will set my face against that man, and against his + family, and will cut him off, and all that go a whoring after + him, to commit whoredom with Molech, from among their people. + [ Leviticus 20:1-5 ] +monk +* monk +grand master +master kaen + One day, an army general invited the Buddhist monk I-Hsiu + (literally, "One Rest") to his military head office for a + dinner. I-Hsiu was not accustomed to wearing luxurious + clothings and so he just put on an old ordinary casual + robe to go to the military base. To him, "form is void". + + As he approached the base, two soldiers appeared before him + and shouted, "Where does this beggar came from? Identify + yourself! You do not have permission to be around here!" + + "My name is I-Hsiu Dharma Master. I am invited by your + general for a supper." + + The two soldiers examined the monk closely and said, "You + liar. How come my general invites such a shabby monk to + dinner? He invites the very solemn venerable I-Hsiu to our + base for a great ceremony today, not you. Now, get out!" + + I-Hsiu was unable to convince the soldiers that he was + indeed the invited guest, so he returned to the temple + and changed to a very formal solemn ceremonial robe for + the dinner. And as he returned to the military base, the + soldiers observed that he was such a great Buddhist monk, + let him in with honour. + + At the dinner, I-Hsiu sat in front of the table full of + food but, instead of putting the food into his month, he + picked up the food with his chopsticks and put it into + his sleeves. The general was curious, and whispered to + him, "This is very embarrassing. Do you want to take + some food back to the temple? I will order the cook to + prepare some take out orders for you." "No" replied the + monk. "When I came here, I was not allowed into the + base by your soldiers until I wear this ceremonial robe. + You do not invite me for a dinner. You invite my robe. + Therefore, my robe is eating the food, not me." + [ Dining with a General - a Zen Buddhism Koan ] +monkey + "Listen, man-cub," said the Bear, and his voice rumbled like + thunder on a hot night. "I have taught thee all the Law of + the Jungle for all the peoples of the jungle--except the + Monkey-Folk who live in the trees. They have no law. They + are outcasts. They have no speech of their own, but use the + stolen words which they overhear when they listen, and peep, + and wait up above in the branches. Their way is not our way. + They are without leaders. They have no remembrance. They + boast and chatter and pretend that they are a great people + about to do great affairs in the jungle, but the falling of + a nut turns their minds to laughter and all is forgotten. + We of the jungle have no dealings with them. We do not drink + where the monkeys drink; we do not go where the monkeys go; + we do not hunt where they hunt; we do not die where they die...." + [ The Jungle Book, by Rudyard Kipling ] +mumak* + ... the Mumak of Harad was indeed a beast of vast bulk, and + the like of him does not walk now in Middle-Earth; his kin + that live still in latter days are but memories of his girth + and majesty. On he came, ... his great legs like trees, + enormous sail-like ears spread out, long snout upraised like + a huge serpent about to strike, his small red eyes raging. + His upturned hornlike tusks ... dripped with blood. + [ The Two Towers, by J.R.R. Tolkien ] +*mummy + But for an account of the manner in which the body was + bandaged, and a list of the unguents and other materials + employed in the process, and the words of power which were + spoken as each bandage was laid in its place, we must have + recourse to a very interesting papyrus which has been edited + and translated by M. Maspero under the title of Le Rituel de + l'Embaumement. ... + Everything that could be done to preserve the body was now + done, and every member of it was, by means of the words of + power which changed perishable substances into imperishable, + protected to all eternity; when the final covering of purple + or white linen had been fastened upon it, the body was ready + for the tomb. + [ Egyptian Magic, by E.A. Wallis Budge ] +mummy wrapping + He held a white cloth -- it was a serviette he had brought + with him -- over the lower part of his face, so that his + mouth and jaws were completely hidden, and that was the + reason for his muffled voice. But it was not that which + startled Mrs. Hall. It was the fact that all his forehead + above his blue glasses was covered by a white bandage, and + that another covered his ears, leaving not a scrap of his + face exposed excepting only his pink, peaked nose. It was + bright, pink, and shiny just as it had been at first. He + wore a dark-brown velvet jacket with a high, black, linen- + lined collar turned up about his neck. The thick black + hair, escaping as it could below and between the cross + bandages, project in curious tails and horns, giving him + the strangest appearance conceivable. + [ The Invisible Man, by H.G. Wells ] +*naga* +*naja* + The naga is a mystical creature with the body of a snake and + the head of a man or woman. They will fiercely protect the + territory they consider their own. Some nagas can be forced + to serve as guardians by a spellcaster of great power. +naginata + A Japanese pole-arm, fitted with a curved single-edged blade. + The blades ranged in length from two to four feet, mounted on + shafts about four to five feet long. The naginata were cut + with a series of short grooves near to the tang, above which + the back edge was thinned, but not sharpened, so that the + greater part of the blade was a flattened diamond shape in + section. Seen in profile, the curve is slight or non- + existent near the tang, becoming more pronounced towards the + point. + + "With his naginata he killed five, but with the sixth it + snapped asunder in the midst and, flinging it away, he drew + his sword, wielding it in the zigzag style, the interlacing, + cross, reversed dragonfly, waterwheel, and eight-sides-at- + once styles of fencing and cutting down eight men; but as he + brought down the ninth with a mighty blow on the helmet, the + blade snapped at the hilt." + [ Story of Tsutsui no Jomio Meishu from Tales of Heike ] +nalfeshnee + Not only do these demons do physical damage with their claws + and bite, but they are capable of using magic as well. +nalzok + Nalzok is Moloch's cunning and unfailingly loyal battle + lieutenant, to whom he trusts the command of warfare when he + does not wish to exercise it himself. Nalzok is a major + demon, known to command the undead. He is hungry for power, + and secretly covets Moloch's position. Moloch doesn't trust + him, but, trusting his own power enough, chooses to allow + Nalzok his position because he is useful. +neanderthal* + 1. Valley between Duesseldorf and Elberfeld in Germany, + where an ancient skull of a prehistoric ancestor to modern + man was found. 2. Human(oid) of the race mentioned above. +neferet +neferet the green + Neferet the Green holds office in her hidden tower, only + reachable by magical means, where she teaches her apprentices + the enigmatic skills of occultism. Despite her many years, she + continues to investigate new spells, especially those involving + translocation. It is further rumored that when she was an + apprentice herself, she accidentally turned her skin green, and + has kept it that way ever since. +newt + (kinds of) small animal, like a lizard, which spends most of + its time in the water. + [ Oxford's Student's Dictionary of Current English ] + + "Fillet of a fenny snake, + In the cauldron boil and bake; + Eye of newt and toe of frog, + Wool of bat and tongue of dog, + Adder's fork and blind-worm's sting, + Lizard's leg and howlet's wing, + For a charm of powerful trouble, + Like a hell-broth boil and bubble." + [ Macbeth, by William Shakespeare ] +ninja-to + A Japanese broadsword. +*norn + The Norns were the three Norse Fates, or the goddesses of fate. + Female giants, they brought the wonderful Golden Age to an end. + They cast lots over the cradle of every child that was born, + and placed gifts in the cradle. Their names were Urda, + Verdandi, and Skuld, representing the past, the present, and + the future. Urda and Verdandi were kindly disposed, but Skuld + was cruel and savage. Their tasks were to sew the web of + fate, to water the sacred ash, Yggdrasil, and to keep it in + good condition by placing fresh earth around it daily. In her + fury, Skuld often spoiled the work of her sisters by tearing + the web to shreds. + [ The Encyclopedia of Myths and Legends of All + Nations by Herbert Spencer Robinson and Knox + Wilson ] +nunchaku + A Japanese flail. +*nymph + A female creature from Roman and Greek mythology, the nymph + occupied rivers, forests, ponds, etc. A nymph's beauty is + beyond words: an ever-young woman with sleek figure and + long, thick hair, radiant skin and perfect teeth, full lips + and gentle eyes. A nymph's scent is delightful, and her + long robe glows, hemmed with golden threads and embroidered + with rainbow hues of unearthly magnificence. A nymph's + demeanour is graceful and charming, her mind quick and witty. + + "Theseus felt her voice pulling him down into fathoms of + sleep. The song was the skeleton of his dream, and the dream + was full of terror. Demon girls were after him, and a bull- + man was goring him. Everywhere there was blood. There was + pain. There was fear. But his head was in the nymph's lap + and her musk was about him, her voice weaving the dream. He + knew then that she had been sent to tell him of something + dreadful that was to happen to him later. Her song was a + warning. But she had brought him a new kind of joy, one that + made him see everything differently. The boy, who was to + become a hero, suddenly knew then what most heroes learn + later -- and some too late -- that joy blots suffering and + that the road to nymphs is beset by monsters." + [ The Minotaur by Bernard Evslin ] +odin + Also called Sigtyr (god of Victory), Val-father (father of + the slain), One-Eyed, Hanga-god (god of the hanged), Farma- + god (god of cargoes), Hapta-god (god of prisoners), and + Othin. He is the prime god of the Norsemen: god of war and + victory, wisdom and prophecy, poetry, the dead, air and wind, + hospitality, and magic. + As the god of war and victory, Odin is ruler of the Valkyries, + warrior-maidens who lived in the halls of Valhalla in Asgard, + the hall of dead heroes where he held his court. + These chosen ones will defend the realm of the gods against + the Frost Giants on the final day of reckoning, Ragnarok. + As god of the wind, Odin rides through the air on his eight- + footed horse, Sleipnir, wielding Gungner, his spear, normally + accompanied by his ravens, Hugin and Munin, who he would also + use as his spies. + As a god of hospitality, he enjoys visiting the earth in + disguise to see how people were behaving and to see how they + would treat him, not knowing who he was. + Odin is usually represented as a one-eyed wise old man with a + long white beard and a wide-brimmed hat (he gave one of his + eyes to Mimir, the guardian of the well of wisdom in Hel, in + exchange for a draught of knowledge). +ogre* + Anyone who has met a gluttonous, nude, angry ogre, will not + easily forget this encounter -- if he survives it at all. + Both male and female ogres can easily grow as tall as three + metres. Build and facial expressions would remind one of a + Neanderthal. Its small, pointy, keen teeth are striking. + Since ogres avoid direct sunlight, their ragged, unfurry + skin is as white as a sheet. They enjoy coating their body + with lard and usually wear nothing but a loin-cloth. An elf + would smell its rancid stench at ten metres distance. + Ogres are solitary creatures: very rarely one may encounter + a female with two or three young. They are the only real + carnivores among the humanoids, and its favourite meal is -- + not surprisingly -- human flesh. They sometimes ally with + orcs or goblins, but only when they anticipate a good meaty + meal. + [ het Boek van de Regels; Het Oog des Meesters ] +oilskin cloak + During our watches below we overhauled our clothes, and made + and mended everything for bad weather. Each of us had made + for himself a suit of oil-cloth or tarpaulin, and these we + got out, and gave thorough coatings of oil or tar, and hung + upon the stays to dry. Our stout boots, too, we covered + over with a thick mixture of melted grease and tar. Thus we + took advantage of the warm sun and fine weather of the + Pacific to prepare for its other face. + [ Two Years Before the Mast, by Richard Henry Dana ] +oilskin sack + Summer passed all too quickly. On the last day of camp, Mr. + Brickle called his counselors together and paid them what he + owed them. Louis received one hundred dollars - the first + money he had ever earned. He had no wallet and no pockets, + so Mr. Brickle placed the money in a waterproof bag that had + a drawstring. He hung this moneybag around Louis' neck, + along with the trumpet, the slate, the chalk pencil, and the + lifesaving medal. + [ The Trumpet of the Swan, by E.B. White ] +olog-hai + But at the end of the Third Age a troll-race not before seen + appeared in southern Mirkwood and in the mountain borders of + Mordor. Olog-hai they were called in the Black Speech. That + Sauron bred them none doubted, though from what stock was not + known. Some held that they were not Trolls but giant Orcs; + but the Olog-hai were in fashion of body and mind quite unlike + even the largest of Orc-kind, whom they far surpassed in size + and power. Trolls they were, but filled with the evil will + of their master: a fell race, strong, agile, fierce and + cunning, but harder than stone. Unlike the older race of the + Twilight they could endure the Sun.... They spoke little, + and the only tongue they knew was the Black Speech of Barad-dur. + [ The Return of the King, by J.R.R. Tolkien ] +oracle +delphi +p*thia + Delphi under towering Parnassus, where Apollo's oracle was, + plays an important part in mythology. Castalia was its + sacred spring; Cephissus its river. It was held to be the + center of the world, so many pilgrims came to it, from + foreign countries as well as Greece. No other shrine rivaled + it. The answers to the questions asked by the anxious + seekers for Truth were delivered by a priestess who went into + a trance before she spoke. + [ Mythology, by Edith Hamilton ] +orange +pear + What was the fruit like? Unfortunately, no one can describe + a taste. All I can say is that, compared with those fruits, + the freshest grapefruit you've ever eaten was dull, and the + juiciest orange was dry, and the most melting pear was hard + and woody, and the sweetest wild strawberry was sour. And + there were no seeds or stones, and no wasps. If you had once + eaten that fruit, all the nicest things in this world would + taste like medicines after it. But I can't describe it. You + can't find out what it is like unless you can get to that + country and taste it for yourself. + [ The Last Battle, by C.S. Lewis ] +pyrolisk + At first glance around the corner, I thought it was another + cockatrice. I had encountered the wretched creatures two or + three times since leaving the open area. I quickly ducked my + head back and considered what to do next. My heart had begun + to thump audibly as I patted my pack to make sure I still had + the dead lizards at close reach. A check of my attire showed + no obvious holes or damage. I had to keep moving. One deep + breath, and a count of three, two, one, and around the corner + I bolted. But it was no cockatrice! I felt a sudden intense + searing of the skin around my face, and flames began to leap + from my pack. I tossed it to the ground, and quickly retreated + back, around that corner, desperately striving to get out of + its sight. +*orb of detection + This Orb is a crystal ball of exceptional powers. When + carried, it grants ESP, limits damage done by spells, and + protects the carrier from magic missiles. When invoked it + allows the carrier to become invisible. +orb of fate + Some say that Odin himself created this ancient crystal ball, + although others argue that Loki created it and forged Odin's + signature on the bottom. In any case, it is a powerful + artifact. Anyone who carries it is granted the gift of + warning, and damage, both spell and physical, is partially + absorbed by the orb itself. When invoked it has the power + to teleport the invoker between levels. +goblin king +orcrist + The Great Goblin gave a truly awful howl of rage when he + looked at it, and all his soldiers gnashed their teeth, + clashed their shields, and stamped. They knew the sword at + once. It had killed hundreds of goblins in its time, when + the fair elves of Gondolin hunted them in the hills or did + battle before their walls. They had called it Orcrist, + Goblin-cleaver, but the goblins called it simply Biter. + They hated it and hated worse any one that carried it. + [ The Hobbit, by J.R.R. Tolkien ] +orcus + Orcus, Prince of the Undead, has a ram's head and a poison + stinger. He is most feared, though, for his powerful magic + abilities. His wand causes death to those he chooses. +~orc ??m* +~orcish barbarian +~orcish ranger +~orcish rogue +~orcish wizard +orc* +* orc +uruk*hai + Orcs, bipeds with a humanoid appearance, are related to the + goblins, but much bigger and more dangerous. The average orc + is only moderately intelligent, has broad, muscled shoulders, + a short neck, a sloping forehead and a thick, dark fur. + Their lower eye-teeth are pointing forward, like a boar's. + Female orcs are more lightly built and bare-chested. Not + needing any clothing, they do like to dress in variegated + apparels. Suspicious by nature, orcs live in tribes or + hordes. They tend to live underground as well as above + ground (but they dislike sunlight). Orcs can use all weapons, + tools and armours that are used by men. Since they don't have + the talent to fashion these themselves, they are constantly + hunting for them. There is nothing a horde of orcs cannot + use. + [ het Boek van de Regels; Het Oog des Meesters ] +orion +sirius + Orion was the son of Neptune. He was a handsome giant and a + mighty hunter. His father gave him the power of wading + through the depths of the sea, or, as others say, of + walking on its surface. + + He dwelt as a hunter with Diana (Artemis), with whom he + was a favourite, and it is even said she was about to marry + him. Her brother was highly displeased and often chid her, + but to no purpose. One day, observing Orion wading through + the sea with his head just above the water, Apollo pointed + it out to his sister and maintained that she could not hit + that black thing on the sea. The archer-goddess discharged + a shaft with fatal aim. The waves rolled the dead body of + Orion to the land, and bewailing her fatal error with many + tears, Diana placed him among the stars, where he appears + as a giant, with a girdle, sword, lion's skin, and + club. Sirius, his dog, follows him, and the Pleiads fly + before him. + [ Bulfinch's Mythology, by Thomas Bulfinch ] +osaku + The osaku is a small tool for picking locks. +owlbear + Owlbears are probably the crossbreed creation of a demented + wizard; given the lethal nature of this creation, it is quite + likely the wizard who created them is no longer alive. As + the name might already suggest, owlbears are a cross between + a giant owl and a bear. They are covered with fur and + feathers. +panther + And lo! almost where the ascent began, + A panther light and swift exceedingly, + Which with a spotted skin was covered o'er! + + And never moved she from before my face, + Nay, rather did impede so much my way, + That many times I to return had turned. + [ Dante's Inferno, as translated + by Henry Wadsworth Longfellow ] +pelias + Conan cried out sharply and recoiled, thrusting his companion + back. Before them rose the great shimmering white form of Satha, + an ageless hate in its eyes. Conan tensed himself for one mad + berserker onslaught -- to thrust the glowing faggot into that + fiendish countenance and throw his life into the ripping sword- + stroke. But the snake was not looking at him. It was glaring + over his shoulder at the man called Pelias, who stood with his + arms folded, smiling. And in the great, cold, yellow eyes + slowly the hate died out in a glitter of pure fear -- the only + time Conan ever saw such an expression in a reptile's eyes. + With a swirling rush like the sweep of a strong wind, the great + snake was gone. + "What did he see to frighten him?" asked Conan, eyeing his + companion uneasily. + "The scaled people see what escapes the mortal eye," answered + Pelias cryptically. "You see my fleshy guise, he saw my naked + soul." + [ Conan the Usurper, by Robert E. Howard and L. Sprague de Camp ] +pick*ax* + The mine is full of holes; + With the wound of pickaxes. + But look at the goldsmith's store. + There, there is gold everywhere. + [ Divan-i Kebir Meter 2, by Mevlana Celaleddin Rumi ] +*piercer + Ye Piercer doth look like unto a stalactyte, and hangeth + from the roofs of caves and caverns. Unto the height of a + man, and thicker than a man's thigh do they grow, and in + groups do they hang. If a creature doth pass beneath them, + they will by its heat and noise perceive it, and fall upon + it to kill and devour it, though in any other way they move + but exceeding slow. + [ the Bestiary of Xygag ] +piranha + They live in "schools." Many times they will wait for prey + to come to the shallow water of the river. Then the large + group of piranhas will attack. These large groups are able + to kill large animals... Their lower teeth fit perfectly + into the spaces of their upper teeth, creating a tremendous + vice-like bite... Piranhas are attracted to any disturbance + in the water. + [ http://www.animalsoftherainforest.com ] +pit +spiked pit + Amid the thought of the fiery destruction that impended, the + idea of the coolness of the well came over my soul like balm. + I rushed to its deadly brink. I threw my straining vision + below. The glare from the enkindled roof illumined its inmost + recesses. Yet, for a wild moment, did my spirit refuse to + comprehend the meaning of what I saw. At length it forced -- + it wrestled its way into my soul -- it burned itself in upon my + shuddering reason. Oh! for a voice to speak! -- oh! horror! -- + oh! any horror but this! + [ The Pit and the Pendulum, by Edgar Allan Poe ] +pit fiend + Pit fiends are among the more powerful of devils, capable of + attacking twice with weapons as well as grabbing and crushing + the life out of those unwary enough to enter their + domains. +platinum yendorian express card + This is an ancient artifact made of an unknown material. It + is rectangular in shape, very thin, and inscribed with + unreadable ancient runes. When carried, it grants the one + who carries it ESP, and reduces all spell induced damage done to + the carrier by half. It also protects from magic missile + attacks. Finally, its power is such that when invoked, it + can charge other objects. +pony + Hey! now! Come hoy now! Whither do you wander? + Up, down, near or far, here, there or yonder? + Sharp-ears, Wise-nose, Swish-tail and Bumpkin, + White-socks my little lad, and old Fatty Lumpkin! + + [...] + Tom called them one by one and they climbed over the brow and + stood in a line. Then Tom bowed to the hobbits. + + "Here are your ponies, now!" he said. "They've more sense (in some + ways) than you wandering hobbits have -- more sense in their noses. + For they sniff danger ahead which you walk right into; and if they + run to save themselves, then they run the right way." + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +*portal + Portals can be Mirrors, Pictures, Standing Stones, Stone + Circles, Windows, and special gates set up for the purpose. + You will travel through them both to distant parts of the + continent and to and from our own world. The precise manner + of their working is a Management secret. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +poseido*n + Poseido(o)n, lord of the seas and father of rivers and + fountains, was the son of Chronos and Rhea, brother of Zeus, + Hades, Hera, Hestia and Demeter. His rank of ruler of the + waves he received by lot at the Council Meeting of the Gods, + at which Zeus took the upper world for himself and gave + dominion over the lower world to Hades. + Poseidon is associated in many ways with horses and thus is + the god of horses. He taught men how to ride and manage the + animal he invented and is looked upon as the originator and + guardian deity of horse races. + His symbol is the familiar trident or three-pronged spear + with which he can split rocks, cause or quell storms, and + shake the earth, a power which makes him the god of + earthquakes as well. Physically, he is shown as a strong and + powerful ruler, every inch a king. + [ The Encyclopedia of Myths and Legends of All + Nations, by Herbert Robinson and Knox Wilson ] +*potion* + POTABLE, n. Suitable for drinking. Water is said to be + potable; indeed, some declare it our natural beverage, + although even they find it palatable only when suffering + from the recurrent disorder known as thirst, for which it + is a medicine. Upon nothing has so great and diligent + ingenuity been brought to bear in all ages and in all + countries, except the most uncivilized, as upon the + invention of substitutes for water. To hold that this + general aversion to that liquid has no basis in the + preservative instinct of the race is to be unscientific -- + and without science we are as the snakes and toads. + [ The Devil's Dictionary, by Ambrose Bierce ] +priest* +* priest* +acolyte + [...] For the two priests were talking exactly like priests, + piously, with learning and leisure, about the most aerial + enigmas of theology. The little Essex priest spoke the more + simply, with his round face turned to the strengthening stars; + the other talked with his head bowed, as if he were not even + worthy to look at them. But no more innocently clerical + conversation could have been heard in any white Italian cloister + or black Spanish cathedral. The first he heard was the tail of + one of Father Brown's sentences, which ended: "... what they + really meant in the Middle Ages by the heavens being + incorruptible." The taller priest nodded his bowed head and + said: "Ah, yes, these modern infidels appeal to their reason; + but who can look at those millions of worlds and not feel that + there may well be wonderful universes above us where reason is + utterly unreasonable?" + [ The Innocence of Father Brown, by G.K. Chesterton ] +prisoner + Where am I? + In the Village. + What do you want? + Information. + Whose side are you on? + That would be telling. We want information ... + information ... + You won't get it. + By hook or by crook, we will. + Who are you? + The new Number 2. + Who is Number 1? + You are Number 6. + I am not a number! I am a free man! + [ The Prisoner, by Patrick McGoohan ] +ptah + Known under various names (Nu, Neph, Cenubis, Amen-Kneph, + Khery-Bakef), Ptah is the creator god and god of craftsmen. + He is usually depicted as wearing a closely fitting robe + with only his hands free. His most distinctive features are + the invariable skull-cap exposing only his face and ears, + and the _was_ or rod of domination which he holds, + consisting of a staff surmounted by the _ankh_ symbol of + life. He is otherwise symbolized by his sacred animal, the + bull. +*purple worm + A gargantuan version of the harmless rain-worm, the purple + worm poses a huge threat to the ordinary adventurer. It is + known to swallow whole and digest its victims within only a + few minutes. These worms are always on guard, sensitive + to the most minute vibrations in the earth, but may also + be awakened by a remote shriek. +quadruped + The woodlands and other regions are inhabited by multitudes + of four-legged creatures which cannot be simply classified. + They might not have fiery breath or deadly stings, but + adventurers have nevertheless met their end numerous times + due to the claws, hooves, or bites of such animals. +quantum mechanic + These creatures are not native to this universe; they seem + to have strangely derived powers, and unknown motives. +quasit + Quasits are small, evil creatures, related to imps. Their + talons release a very toxic poison when used in an attack. +quest + Many, possibly most, Tours are organized as a Quest. This + is like a large-scale treasure hunt, with clues scattered + all over the continent, a few false leads, Mystical Masters + as game-show hosts, and the Dark Lord and the Terrain to + make the Quest interestingly difficult. [...] + In order to be assured of your future custom, the Management + has a further Rule: Tourists, far from being rewarded for + achieving their Quest Object, must then go on to conquer + the Dark Lord or set about Saving the World, or both. And + why not? By then you will have had a lot of practice in + that sort of thing and, besides, the Quest Object is usually + designed to help you do it. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +quetzalcoatl + One of the principal Aztec-Toltec gods was the great and wise + Quetzalcoatl, who was called Kukumatz in Guatemala, and + Kukulcan in Yucatan. His image, the plumed serpent, is found + on both the oldest and the most recent Indian edifices. ... + The legend tells how the Indian deity Quetzalcoatl came from + the "Land of the Rising Sun". He wore a long white robe and + had a beard; he taught the people crafts and customs and laid + down wise laws. He created an empire in which the ears of + corn were as long as men are tall, and caused bolls of colored + cotton to grow on cotton plants. But for some reason or other + he had to leave his empire. ... But all the legends of + Quetzalcoatl unanimously agree that he promised to come again. + [ Gods, Graves, and Scholars, by C. W. Ceram ] +quit* + Maltar: [...] I remembered a little saying I learned my first + day at the academy. + Natalie: Yeah, yeah, I know. Winners never quit and quitters + never win. + Maltar: What? No! Winners never quit and quitters should be + cast into the flaming pit of death. + [ Snow Day, directed by Chris Koch, + written by Will McRobb and Chris Viscardi ] +raijin +raiden + The god of thunder. +ranger +* ranger + "Lonely men are we, Rangers of the wild, hunters -- but hunters + ever of the servants of the Enemy; for they are found in many + places, not in Mordor only. + If Gondor, Boromir, has been a stalwart tower, we have played + another part. Many evil things there are that your strong walls + and bright swords do not stay. You know little of the lands + beyond your bounds. Peace and freedom, do you say? The North + would have known them little but for us. Fear would have + destroyed them. But when dark things come from the houseless + hills, or creep from sunless woods, they fly from us. What + roads would any dare to tread, what safety would there be in + quiet lands, or in the homes of simple men at night, if the + Dunedain were asleep, or were all gone into the grave?" + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +rat +* rat + Rats are long-tailed rodents. They are aggressive, + omnivorous, and adaptable, often carrying diseases. + + "The rat," said O'Brien, still addressing his invisible + audience, "although a rodent, is carnivorous. You are aware + of that. You will have heard of the things that happen in + the poor quarters of this town. In some streets a woman dare + not leave her baby alone in the house, even for five minutes. + The rats are certain to attack it. Within quite a small time + they will strip it to the bones. They also attack sick or + dying people. They show astonishing intelligence in knowing + when a human being is helpless." + [ 1984, by George Orwell ] +raven + But the raven, sitting lonely on the placid bust, spoke only + That one word, as if his soul in that one word he did outpour. + Nothing further then he uttered -- not a feather then he fluttered-- + Till I scarcely more than muttered, 'other friends have flown before-- + On the morrow *he* will leave me, as my hopes have flown before.' + Then the bird said, 'Nevermore.' + [ The Raven - Edgar Allan Poe ] +*ring +ring of * + Three Rings for the Elven-kings under the sky, + Seven for the Dwarf-lords in their halls of stone, + Nine for Mortal Men doomed to die, + One for the Dark Lord on his dark throne, + In the Land of Mordor where the Shadows lie. + One Ring to rule them all, One Ring to find them, + One Ring to bring them all and in the darkness bind them + In the Land of Mordor where the Shadows lie. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +robe + Robes are the only garments, apart from Shirts, ever to have + sleeves. They have three uses: + 1. As the official uniform of Priests, Priestesses, Monks, + Nuns (see Nunnery), and Wizards. The OMT [ Official Management + Term ] prescribed for the Robes of Priests and Nuns is that + they _fall in severe folds_; of Priestesses that they _float_; + and of Wizards that they _swirl_. You can thus see who you + are dealing with. + 2. For Kings. The OMT here is _falling in stately folds_. + 3. As the garb of Desert Nomads. [...] + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +rock + Bilbo saw that the moment had come when he must do something. + He could not get up at the brutes and he had nothing to shoot + with; but looking about he saw that in this place there were + many stones lying in what appeared to be a now dry little + watercourse. Bilbo was a pretty fair shot with a stone, and + it did not take him long to find a nice smooth egg-shaped one + that fitted his hand cosily. As a boy he used to practise + throwing stones at things, until rabbits and squirrels, and + even birds, got out of his way as quick as lightning if they + saw him stoop; and even grownup he had still spent a deal of + his time at quoits, dart-throwing, shooting at the wand, + bowls, ninepins and other quiet games of the aiming and + throwing sort - indeed he could do lots of things, besides + blowing smoke-rings, asking riddles and cooking, that I + haven't time to tell you about. There is no time now. While + he was picking up stones, the spider had reached Bombur, and + soon he would have been dead. At that moment Bilbo threw. + The stone struck the spider plunk on the head, and it dropped + senseless off the tree, flop to the ground, with all its legs + curled up. + [ The Hobbit, by J.R.R. Tolkien ] +rock mole + A rock mole is a member of the rodent family. They get their + name from their ability to tunnel through rock in the same + fashion that a mole tunnels through earth. They are known to + eat anything they come across in their diggings, although it + is still unknown how they convert some of these things into + something of nutritional value. +rogue +* rogue + I understand the business, I hear it: to have an open ear, a + quick eye, and a nimble hand, is necessary for a cut-purse; a + good nose is requisite also, to smell out work for the other + senses. I see this is the time that the unjust man doth + thrive. <...> The prince himself is about a piece of iniquity, + stealing away from his father with his clog at his heels: if + I thought it were a piece of honesty to acquaint the king + withal, I would not do't: I hold it the more knavery to + conceal it; and therein am I constant to my profession. + [ Autolycus the Rogue, from The Winter's Tale by + William Shakespeare ] +rothe + The rothe (pronounced roth-AY) is a musk ox-like creature with + an aversion to light. It prefers to live underground near + lichen and moss. +*royal jelly + "'Royal Jelly,'" he read aloud, "'must be a substance of + tremendous nourishing power, for on this diet alone, the + honey-bee larva increases in weight fifteen hundred times in + five days!'" + + "How much?" + + "Fifteen hundred times, Mabel. And you know what that means + if you put it in terms of a human being? It means," he said, + lowering his voice, leaning forward, fixing her with those + small pale eyes, "it means that in five days a baby weighing + seven and a half pounds to start off with would increase in + weight to five tons!" + [ Royal Jelly, by Roald Dahl ] +rust monster + These strange creatures live on a diet of metals. They can + turn a suit of armour into so much useless rusted scrap in no + time at all. +*saber +*sabre + Flashed all their sabres bare, + Flashed as they turned in air, + Sab'ring the gunners there, + Charging an army, while + All the world wondered: + Plunged in the battery smoke, + Right through the line they broke; + Cossack and Russian + Reeled from the sabre-stroke + Shattered and sundered. + Then they rode back, but not-- + Not the six hundred. + [ The Charge of the Light Brigade, + by Alfred, Lord Tennyson ] +saddle + The horseman serves the horse, + The neat-herd serves the neat, + The merchant serves the purse, + The eater serves his meat; + 'Tis the day of the chattel, + Web to weave, and corn to grind, + Things are in the saddle, + And ride mankind. + [ Ode, by Ralph Waldo Emerson ] +sake + Japanese rice wine. +salamander + For hundreds of years, many people believed that salamanders + were magical. In England in the Middle Ages, people thought + that fire created salamanders. When they set fire to damp + logs, dozens of the slimy creatures scurried out. The word + salamander, in fact, comes from a Greek word meaning "fire + animal". + [ Salamanders, by Cherie Winner ] +samurai +* samurai + By that time, Narahara had already slipped his arm from the + sleeve of his outer robe, drew out his two-and-a-half-foot + Fujiwara Tadahiro sword, and, brandishing it over his head, + began barreling toward the foreigners. In less than a minute, + he had charged upon them and cut one of them through the torso. + The man fled, clutching his bulging guts, finally to fall from + his horse at the foot of a pine tree about a thousand yards + away. Kaeda Takeji finished him off. The other two Englishmen + were severely wounded as they tried to flee. Only the woman + managed to escape virtually unscathed. + [ The Fox-horse, from Drunk as a Lord, by Ryotaro Shiba ] +sandestin + Ildefonse left the terrace and almost immediately sounds + of contention came from the direction of the work-room. + Ildefonse presently returned to the terrace, followed by + Osherl and a second sandestin using the guise of a gaunt blue + bird-like creature, some six feet in height. + + Ildefonse spoke in scathing tones: "Behold these two + creatures! They can roam the chronoplex as easily as you + or I can walk around the table; yet neither has the wit to + announce his presence upon arrival. I found Osherl asleep + in his fulgurite and Sarsem perched in the rafters." + [...] + "No matter," said Rhialto. "He has brought Sarsem, and this + was his requirement. In the main, Osherl, you have done well!" + + "And my indenture point?" + + "Much depends upon Sarsem's testimony. Sarsem, will you sit?" + + "In this guise, I find it more convenient to stand." + + "Then why not alter to human form and join us in comfort at + the table?" + + "That is a good idea." Sarsem became a naked young epicene + in an integument of lavender scales with puffs of purple hair + like pom-poms growing down his back. He seated himself at + the table but declined refreshment. "This human semblance, + though typical, is after all, only a guise. If I were to put + such things inside myself, I might well become uneasy." + [ Rhialto the Marvellous, by Jack Vance ] +sasquatch + The name _Sasquatch_ doesn't really become important in Canada + until the 1930s, when it appeared in the works of J. W. Burns, + a British Columbian writer who used a great deal of Indian + lore in his stories. Burn's Sasquatch was a giant Indian who + lived in the wilderness. He was hairy only in the sense that + he had long hair on his head, and while this Sasquatch lived a + wild and primitive life, he was fully human. + Burns's character proved to be quite popular. There was a + Sasquatch Inn near the town of Harrison, British Columbia, and + Harrison even had a local celebration called "Sasquatch Days." + The celebration which had been dormant for years was revived + as part of British Columbia's centennial, and one of the + events was to be a Sasquatch hunt. The hunt never took place, + perhaps it was never supposed to, but the publicity about it + did bring out a number of people who said they had encountered + a Sasquatch -- not Burns's giant Indian, but the hairy apelike + creature that we have all come to know. + [ The Encyclopedia of Monsters, by Daniel Cohen ] +*sceptre of might + This mace was created aeons ago in some unknown cave, + and has been passed down from generation to generation of + cave dwellers. It is a very mighty mace indeed, and in + addition will protect anyone who carries it from magic + missile attacks. When invoked, it causes conflict in the + area around it. +scimitar + Oh, how handsome, how noble was the Vizier Ali Tebelin, + my father, as he stood there in the midst of the shot, his + scimitar in his hand, his face black with powder! How his + enemies fled before him! + [ The Count of Monte Cristo, by Alexandre Dumas ] +scorpio* + A sub-species of the spider (_Scorpionidae_), the scorpion + distinguishes itself from them by having a lower body that + ends in a long, jointed tail tapering to a poisonous stinger. + They have eight legs and pincers. + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] +scorpius + Since early times, the Scorpion has represented death, darkness, + and evil. Scorpius is the reputed slayer of Orion the Hunter. + [...] The gods put both scorpion and hunter among the stars, but + on opposite sides of the sky so they would never fight again. + As Scorpius rises in the east, Orion sets in the west. + [ 365 Starry Nights, by Chet Raymo ] +*scroll +scroll * + And I was gazing on the surges prone, + With many a scalding tear and many a groan, + When at my feet emerg'd an old man's hand, + Grasping this scroll, and this same slender wand. + I knelt with pain--reached out my hand--had grasp'd + Those treasures--touch'd the knuckles--they unclasp'd-- + I caught a finger: but the downward weight + O'erpowered me--it sank. Then 'gan abate + The storm, and through chill aguish gloom outburst + The comfortable sun. I was athirst + To search the book, and in the warming air + Parted its dripping leaves with eager care. + Strange matters did it treat of, and drew on + My soul page after page, till well-nigh won + Into forgetfulness; when, stupefied, + I read these words, and read again, and tried + My eyes against the heavens, and read again. + [ Endymion, by John Keats ] +shad* + Shades are undead creatures. They differ from zombies in + that a zombie is an undead animation of a corpse, while a + shade is an undead creature magically created by the use + of black magic. +shaman karnov + Making his quarters in the Caves of the Ancestors, Shaman + Karnov unceasingly tries to shield his neanderthal people + from Tiamat's minions' harassments. +shan*lai*ching + The Chinese god of Mountains and Seas, also the name of an + old book (also Shan Hai Tjing), the book of mountains and + seas - which deals with the monster Kung Kung trying to + seize power from Yao, the fourth emperor. + [ Spectrum Atlas van de Mythologie ] +shark + As the shark moved, its dark top reflected virtually no + light. The denticles on its skin muted the whoosh of its + movements as the shark rose, driven by the power of the + great tail sweeping from side to side, like a scythe. + The fish exploded upward. + Charles Bruder felt a slight vacuum tug in the motion of + the sea, noted it as a passing current, the pull of a wave, + the tickle of undertow. He could not have heard the faint + sucking rush of water not far beneath him. He couldn't + have seen or heard what was hurtling from the murk at + astonishing speed, jaws unhinging, widening, for the + enormous first bite. It was the classic attack + that no other creature in nature could make -- a bomb from + the depths. + [ Close to Shore, by Michael Capuzzo ] +shito + A Japanese stabbing knife. +shrieker + With a single, savage thrust of her spear, the warrior-woman + impaled the fungus, silencing it. However, it was too late: + the alarm had been raised[...] + Suddenly, a large, dark shape rose from the abyss before them, + its fetid bulk looming overhead...The monster was some kind of + great dark worm, but that was about all they were sure of. + [ The Adventurers, Epic IV, by Thomas A. Miller ] +skeleton + A skeleton is a magically animated undead creature. Unlike + shades, only a humanoid creature can be used to create a + skeleton. No one knows why this is true, but it has become + an accepted fact amongst the practitioners of the black arts. +slasher + "That dog belonged to a settler who tried to build his cabin + on the bank of the river a few miles south of the fort," + grunted Conan. ... "We took him to the fort and dressed his + wounds, but after he recovered he took to the woods and turned + wild. -- What now, Slasher, are you hunting the men who + killed your master?" ... "Let him come," muttered Conan. + "He can smell the devils before we can see them." ... + Slasher cleared the timbers with a bound and leaped into the + bushes. They were violently shaken and then the dog slunk + back to Balthus' side, his jaws crimson. ... "He was a man," + said Conan. "I drink to his shade, and to the shade of the + dog, who knew no fear." He quaffed part of the wine, then + emptied the rest upon the floor, with a curious heathen + gesture, and smashed the goblet. "The heads of ten Picts + shall pay for this, and seven heads for the dog, who was a + better warrior than many a man." + [ Conan The Warrior, by Robert E Howard ] +slime mold + Slime mold or slime fungus, organism usually classified with + the fungi, but showing equal affinity to the protozoa. Slime + molds have complex life cycles with an animal-like motile + phase, in which feeding and growth occur, and a plant-like + immotile reproductive phase. The motile phase, commonly + found under rotting logs and damp leaves, consists of either + solitary amoebalike cells or a brightly colored multinucleate + mass of protoplasm called a plasmodium, which creeps about + and feeds by amoeboid movement. + [ The Concise Columbia Encyclopedia ] +sling + And it came to pass, when the Philistine arose, and came and + drew nigh to meet David, that David hasted, and ran toward + the army to meet the Philistine. + And David put his hand in his bag, and took thence a stone, + and slang it, and smote the Philistine in his forehead, that + the stone sunk into his forehead; and he fell upon his face + to the earth. + So David prevailed over the Philistine with a sling and with + a stone, and smote the Philistine, and slew him; but there + was no sword in the hand of David. + [ 1 Samuel 17:48-50 ] +*snake +serpent +water moccasin +python +pit viper + Now the serpent was more subtle than any beast of the field + which the Lord God had made. And he said unto the woman, Yea, + hath God said, Ye shall not eat of every tree of the garden? + And the woman said unto the serpent, We may eat of the fruit of + the trees of the garden: but of the fruit of the tree which is + in the midst of the garden, God hath said, Ye shall not eat of + it, neither shall ye touch it, lest ye die. And the serpent + said unto the woman, Ye shall not surely die: for God doth + know that in the day ye eat thereof, then your eyes shall be + opened, and ye shall be as gods, knowing good and evil. And + when the woman saw that the tree was good for food, and that it + was pleasant to the eyes, and a tree to be desired to make one + wise, she took of the fruit thereof, and did eat, and gave also + unto her husband with her; and he did eat. + + And the Lord God said unto the woman, What is this that thou + hast done? And the woman said, The serpent beguiled me, and I + did eat. And the Lord God said unto the serpent, Because thou + hast done this, thou art cursed above all cattle, and above + every beast of the field; upon thy belly shalt thou go, and + dust shalt thou eat all the days of thy life: And I will put + enmity between thee and the woman, and between thy seed and her + seed; it shall bruise thy head, and thou shalt bruise his heel. + [ Genesis 3:1-6,13-15 ] +snickersnee + Ah, never shall I forget the cry, + or the shriek that shrieked he, + As I gnashed my teeth, and from my sheath + I drew my Snickersnee! + --Koko, Lord high executioner of Titipu + [ The Mikado, by Sir W.S. Gilbert ] +sokoban + Sokoban (Japanese for "warehouse person") is a puzzle-type + game where the player must push around treasure to a goal + area. It apparently won first prize in a Japanese programming + contest. + [ Xsokoban web site ] +*soldier +sergeant +lieutenant +captain + The soldiers of Yendor are well-trained in the art of war, + many trained by the Wizard himself. Some say the soldiers + are explorers who were unfortunate enough to be captured, + and put under the Wizard's spell. Those who have survived + encounters with soldiers say they travel together in platoons, + and are fierce fighters. Because of the load of their combat + gear, however, one can usually run away from them, and doing + so is considered a wise thing. +*spear +javelin + - they come together with great random, and a spear is brast, + and one party brake his shield and the other one goes down, + horse and man, over his horse-tail and brake his neck, and + then the next candidate comes randoming in, and brast his + spear, and the other man brast his shield, and down he goes, + horse and man, over his horse-tail, and brake his neck, and + then there's another elected, and another and another and + still another, till the material is all used up; and when you + come to figure up results, you can't tell one fight from + another, nor who whipped; and as a picture of living, raging, + roaring battle, sho! why it's pale and noiseless - just + ghosts scuffling in a fog. Dear me, what would this barren + vocabulary get out of the mightiest spectacle? - the burning + of Rome in Nero's time, for instance? Why, it would merely + say 'Town burned down; no insurance; boy brast a window, + fireman brake his neck!' Why, that ain't a picture! + [ A Connecticut Yankee in King Arthur's Court, by Mark + Twain ] +*spellbook* + The Book of Three lay closed on the table. Taran had never + been allowed to read the volume for himself; now he was sure + it held more than Dallben chose to tell him. In the sun- + filled room, with Dallben still meditating and showing no + sign of stopping, Taran rose and moved through the shimmering + beams. From the forest came the monotonous tick of a beetle. + His hands reached for the cover. Taran gasped in pain and + snatched them away. They smarted as if each of his fingers + had been stung by hornets. He jumped back, stumbled against + the bench, and dropped to the floor, where he put his fingers + woefully into his mouth. + Dallben's eyes blinked open. He peered at Taran and yawned + slowly. "You had better see Coll about a lotion for those + hands," he advised. "Otherwise, I shouldn't be surprised if + they blistered." + [ The Book of Three, by Lloyd Alexander ] +*spider + Eight legged creature capable of spinning webs to trap prey. + + "You mean you eat flies?" gasped Wilbur. + "Certainly. Flies, bugs, grasshoppers, choice beetles, + moths, butterflies, tasty cockroaches, gnats, midges, daddy + longlegs, centipedes, mosquitoes, crickets - anything that is + careless enough to get caught in my web. I have to live, + don't I?" + "Why, yes, of course," said Wilbur. + [ Charlotte's Web, by E.B. White ] +*spore +*sphere + The attack by those who want to die -- this is the attack + against which you cannot prepare a perfect defense. + --Human aphorism + [ The Dosadi Experiment, by Frank Herbert ] +~*aesculapius +*staff + So they stood, each in his place, neither moving a finger's + breadth back, for one good hour, and many blows were given + and received by each in that time, till here and there were + sore bones and bumps, yet neither thought of crying "Enough," + or seemed likely to fall from off the bridge. Now and then + they stopped to rest, and each thought that he never had seen + in all his life before such a hand at quarterstaff. At last + Robin gave the stranger a blow upon the ribs that made his + jacket smoke like a damp straw thatch in the sun. So shrewd + was the stroke that the stranger came within a hair's breadth + of falling off the bridge; but he regained himself right + quickly, and, by a dexterous blow, gave Robin a crack on the + crown that caused the blood to flow. Then Robin grew mad + with anger, and smote with all his might at the other; but + the stranger warded the blow, and once again thwacked Robin, + and this time so fairly that he fell heels over head into the + water, as the queen pin falls in a game of bowls. + [ The Merry Adventures of Robin Hood, by Howard Pyle ] +*staff of aesculapius + This staff is considered sacred to all healers, as it truly + holds the powers of life and death. When wielded, it + protects its user from all life draining attacks, and + additionally gives the wielder the power of regeneration. + When invoked it performs healing magic. +stair* + Up he went -- very quickly at first -- then more slowly -- then + in a little while even more slowly than that -- and finally, + after many minutes of climbing up the endless stairway, one + weary foot was barely able to follow the other. Milo suddenly + realized that with all his effort he was no closer to the top + than when he began, and not a great deal further from the + bottom. But he struggled on for a while longer, until at last, + completely exhausted, he collapsed onto one of the steps. + "I should have known it," he mumbled, resting his tired legs + and filling his lungs with air. "This is just like the line + that goes on forever, and I'll never get there." + "You wouldn't like it much anyway," someone replied gently. + "Infinity is a dreadfully poor place. They can never manage to + make ends meet." + [ The Phantom Tollbooth, by Norton Juster ] + + Dr. Ray Stantz: Hey, where do those stairs go? + Dr. Peter Venkman: They go up. + [ Ghostbusters, directed by Ivan Reitman, + written by Dan Ackroyd and Harold Ramis ] +~statue trap +statue* + Then at last he began to wonder why the lion was standing so + still - for it hadn't moved one inch since he first set eyes + on it. Edmund now ventured a little nearer, still keeping in + the shadow of the arch as much as he could. He now saw from + the way the lion was standing that it couldn't have been + looking at him at all. ("But supposing it turns its head?" + thought Edmund.) In fact it was staring at something else - + namely a little dwarf who stood with his back to it about + four feet away. "Aha!" thought Edmund. "When it springs at + the dwarf then will be my chance to escape." But still the + lion never moved, nor did the dwarf. And now at last Edmund + remembered what the others had said about the White Witch + turning people into stone. Perhaps this was only a stone + lion. And as soon as he had thought of that he noticed that + the lion's back and the top of its head were covered with + snow. Of course it must be only a statue! + [ The Lion, the Witch and the Wardrobe by C.S. Lewis ] +sting + There was the usual dim grey light of the forest-day about + him when he came to his senses. The spider lay dead beside + him, and his sword-blade was stained black. Somehow the + killing of the giant spider, all alone and by himself in the + dark without the help of the wizard or the dwarves or of + anyone else, made a great difference to Mr. Baggins. He felt + a different person, and much fiercer and bolder in spite of + an empty stomach, as he wiped his sword on the grass and put + it back into its sheath. + "I will give you a name," he said to it, "and I shall call + you Sting." + [ The Hobbit, by J.R.R. Tolkien ] +stormbringer + There were sounds in the distance, incongruent with the + sounds of even this nameless, timeless sea: thin sounds, + agonized and terrible, for all that they remained remote - + yet the ship followed them, as if drawn by them; they grew + louder - pain and despair were there, but terror was + predominant. + Elric had heard such sounds echoing from his cousin Yyrkoon's + sardonically named 'Pleasure Chambers' in the days before he + had fled the responsibilities of ruling all that remained of + the old Melnibonean Empire. These were the voices of men + whose very souls were under siege; men to whom death meant + not mere extinction, but a continuation of existence, forever + in thrall to some cruel and supernatural master. He had + heard men cry so when his salvation and his nemesis, his + great black battle-blade Stormbringer, drank their souls. + [ The Lands Beyond the World, by Michael Moorcock ] +susano*o + The Shinto chthonic and weather god and brother of the sun + goddess Amaterasu, he was born from the nose of the + primordial creator god Izanagi and represents the physical, + material world. He has been expelled from heaven and taken + up residence on earth. + [ Encyclopedia of Gods, by Michael Jordan ] +tanko + Samurai plate armor of the Yamato period (AD 300 - 710). +tengu + The tengu was the most troublesome creature of Japanese + legend. Part bird and part man, with red beak for a nose + and flashing eyes, the tengu was notorious for stirring up + feuds and prolonging enmity between families. Indeed, the + belligerent tengu were supposed to have been man's first + instructors in the use of arms. + [ Mythical Beasts, by Deirdre Headon (The Leprechaun Library) ] +thoth + The Egyptian god of the moon and wisdom, Thoth is the patron + deity of scribes and of knowledge, including scientific, + medical and mathematical writing, and is said to have given + mankind the art of hieroglyphic writing. He is important as + a mediator and counsellor amongst the gods and is the scribe + of the Heliopolis Ennead pantheon. According to mythology, + he was born from the head of the god Seth. He may be + depicted in human form with the head of an ibis, wholly as an + ibis, or as a seated baboon sometimes with its torso covered + in feathers. His attributes include a crown which consists + of a crescent moon surmounted by a moon disc. + Thoth is generally regarded as a benign deity. He is also + scrupulously fair and is responsible not only for entering + in the record the souls who pass to afterlife, but of + adjudicating in the Hall of the Two Truths. The Pyramid + Texts reveal a violent side of his nature by which he + decapitates the adversaries of truth and wrenches out their + hearts. + [ Encyclopedia of Gods, by Michael Jordan ] +thoth*amon + Men say that he [Thutothmes] has opposed Thoth-Amon, who is + master of all priests of Set, and dwells in Luxor, and that + Thutothmes seeks hidden power [The Heart of Ahriman] to + overthrow the Great One. + [ Conan the Conqueror, by Robert E. Howard ] +*throne + Methought I saw the footsteps of a throne + Which mists and vapours from mine eyes did shroud-- + Nor view of who might sit thereon allowed; + But all the steps and ground about were strown + With sights the ruefullest that flesh and bone + Ever put on; a miserable crowd, + Sick, hale, old, young, who cried before that cloud, + "Thou art our king, + O Death! to thee we groan." + Those steps I clomb; the mists before me gave + Smooth way; and I beheld the face of one + Sleeping alone within a mossy cave, + With her face up to heaven; that seemed to have + Pleasing remembrance of a thought foregone; + A lovely Beauty in a summer grave! + [ Sonnet, by William Wordsworth ] +tiger + 1. A well-known tropical predator (_Felis tigris_): a + feline. It has a yellowish skin with darker spots or + stripes. 2. Figurative: _a paper tiger_, something that is + meant to scare, but has no really scaring effect whatsoever, + (after a statement by Mao Ze Dong, August 1946). + [ Van Dale's Groot Woordenboek der Nederlandse Taal ] + + Tyger! Tyger! burning bright + In the forests of the night, + What immortal hand or eye + Could frame thy fearful symmetry? + [ The Tyger, by William Blake ] +tin +tin of * +tinning kit + "You know salmon, Sarge," said Nobby. + "It is a fish of which I am aware, yes." + "You know they sell kind of slices of it in tins..." + "So I am given to understand, yes." + "Weell...how come all the tins are the same size? Salmon + gets thinner at both ends." + "Interesting point, Nobby. I think-" + [ Soul Music, by Terry Pratchett ] +tin opener + Less than thirty Cat tribes now survived, roaming the cargo + decks on their hind legs in a desperate search for food. + But the food had gone. + The supplies were finished. + Weak and ailing, they prayed at the supply hold's silver + mountains: huge towering acres of metal rocks which, in their + pagan way, the mutant Cats believed watched over them. + Amid the wailing and the screeching one Cat stood up and held + aloft the sacred icon. The icon which had been passed down + as holy, and one day would make its use known. + It was a piece of V-shaped metal with a revolving handle on + its head. + He took down a silver rock from the silver mountain, while + the other Cats cowered and screamed at the blasphemy. + He placed the icon on the rim of the rock, and turned the + handle. + And the handle turned. + And the rock opened. + And inside the rock was Alphabetti spaghetti in tomato sauce. + [ Red Dwarf, by Rob Grant and Doug Naylor ] +titan + Gaea, mother earth, arose from the Chaos and gave birth to + Uranus, heaven, who became her consort. Uranus hated all + their children, because he feared they might challenge his + own authority. Those children, the Titans, the Gigantes, + and the Cyclops, were banished to the nether world. Their + enraged mother eventually released the youngest titan, + Chronos (time), and encouraged him to castrate his father and + rule in his place. Later, he too was challenged by his own + son, Zeus, and he and his fellow titans were ousted from + Mount Olympus. + [ Greek Mythology, by Richard Patrick ] +touch*stone + "Gold is tried by a touchstone, men by gold." + [ Chilon (c. 560 BC) ] +tourist +* tourist + The road from Ankh-Morpork to Chrim is high, white and + winding, a thirty-league stretch of potholes and half-buried + rocks that spirals around mountains and dips into cool green + valleys of citrus trees, crosses liana-webbed gorges on + creaking rope bridges and is generally more picturesque than + useful. + Picturesque. That was a new word to Rincewind the wizard + (BMgc, Unseen University [failed]). It was one of a number + he had picked up since leaving the charred ruins of + Ankh-Morpork. Quaint was another one. Picturesque meant -- + he decided after careful observation of the scenery that + inspired Twoflower to use the word -- that the landscape was + horribly precipitous. Quaint, when used to describe the + occasional village through which they passed, meant fever- + ridden and tumbledown. + Twoflower was a tourist, the first ever seen on the discworld. + Tourist, Rincewind had decided, meant "idiot". + [ The Colour of Magic, by Terry Pratchett ] +towel + The Hitchhiker's Guide to the Galaxy has a few things to say + on the subject of towels. + A towel, it says, is about the most massively useful thing + an interstellar hitchhiker can have. Partly it has great + practical value. You can wrap it around you for warmth as + you bound across the cold moons of Jaglan Beta; you can lie + on it on the brilliant marble-sanded beaches of Santraginus + V, inhaling the heady sea vapors; you can sleep under it + beneath the stars which shine so redly on the desert world of + Kakrafoon; use it to sail a miniraft down down the slow heavy + River Moth; wet it for use in hand-to-hand combat; wrap it + round your head to ward off noxious fumes or avoid the gaze + of the Ravenous Bugblatter Beast of Traal (a mind-bogglingly + stupid animal, it assumes that if you can't see it, it can't + see you - daft as a brush, but very very ravenous); you can + wave your towel in emergencies as a distress signal, and of + course dry yourself off with it if it still seems to be clean + enough. + [ The Hitchhiker's Guide to the Galaxy, + by Douglas Adams ] +*tower + Towers (_brooding_, _dark_) stand alone in Waste Areas and + almost always belong to Wizards. All are several stories high, + round, doorless, virtually windowless, and composed of smooth + blocks of masonry that make them very hard to climb. [...] + You will have to go to a Tower and then break into it at some + point towards the end of your Tour. + [ The Tough Guide to Fantasyland, by Diana Wynne Jones ] +trap*door + I knew my Erik too well to feel at all comfortable on jumping + into his house. I knew what he had made of a certain palace at + Mazenderan. From being the most honest building conceivable, he + soon turned it into a house of the very devil, where you could + not utter a word but it was overheard or repeated by an echo. + With his trap-doors the monster was responsible for endless + tragedies of all kinds. + [ The Phantom of the Opera, by Gaston Leroux ] +trapper + The trapper is a creature which has evolved a chameleon-like + ability to blend into the dungeon surroundings. It captures + its prey by remaining very still and blending into the + surrounding dungeon features, until an unsuspecting creature + passes by. It wraps itself around its prey and digests it. +tree + I think that I shall never see + A poem lovely as a tree. + A tree whose hungry mouth is prest + Against the earth's sweet flowing breast; + A tree that looks at God all day, + And lifts her leafy arms to pray; + A tree that may in Summer wear + A nest of robins in her hair; + Upon whose bosom snow has lain; + Who intimately lives with rain. + Poems are made by fools like me, + But only God can make a tree. + [ Trees - Joyce Kilmer ] +tripe +tripe ration + If you start from scratch, cooking tripe is a long-drawn-out + affair. Fresh whole tripe calls for a minimum of 12 hours of + cooking, some time-honored recipes demanding as much as 24. + To prepare fresh tripe, trim if necessary. Wash it thoroughly, + soaking overnight, and blanch, for 1/2 hour in salted water. + Wash well again, drain and cut for cooking. When cooked, the + texture of tripe should be like that of soft gristle. More + often, alas, because the heat has not been kept low enough, + it has the consistency of wet shoe leather. + [ Joy of Cooking, by I Rombauer and M Becker ] +*troll + The troll shambled closer. He was perhaps eight feet tall, + perhaps more. His forward stoop, with arms dangling past + thick claw-footed legs to the ground, made it hard to tell. + The hairless green skin moved upon his body. His head was a + gash of a mouth, a yard-long nose, and two eyes which drank + the feeble torchlight and never gave back a gleam. + [...] + Like a huge green spider, the troll's severed hand ran on its + fingers. Across the mounded floor, up onto a log with one + taloned forefinger to hook it over the bark, down again it + scrambled, until it found the cut wrist. And there it grew + fast. The troll's smashed head seethed and knit together. + He clambered back on his feet and grinned at them. The + waning faggot cast red light over his fangs. + [ Three Hearts and Three Lions, by Poul Anderson ] +*tsurugi of muramasa + This most ancient of swords has been passed down through the + leadership of the Samurai legions for hundreds of years. It + is said to grant luck to its wielder, but its main power is + terrible to behold. It has the capability to cut in half any + creature it is wielded against, instantly killing them. +~*muramasa +tsurugi + The tsurugi, also known as the long samurai sword, is an + extremely sharp, two-handed blade favored by the samurai. + It is made of hardened steel, and is manufactured using a + special process, causing it to never rust. The tsurugi is + rumored to be so sharp that it can occasionally cut + opponents in half! +twoflower +guide + "Rincewind!" + Twoflower sprang off the bed. The wizard jumped back, + wrenching his features into a smile. + "My dear chap, right on time! We'll just have lunch, and + then I'm sure you've got a wonderful programme lined up for + this afternoon!" + "Er --" + "That's great!" + Rincewind took a deep breath. "Look," he said desperately, + "let's eat somewhere else. There's been a bit of a fight + down below." + "A tavern brawl? Why didn't you wake me up?" + "Well, you see, I - _what_?" + "I thought I made myself clear this morning, Rincewind. I + want to see genuine Morporkian life - the slave market, the + Whore Pits, the Temple of Small Gods, the Beggar's Guild... + and a genuine tavern brawl." A faint note of suspicion + entered Twoflower's voice. "You _do_ have them, don't you? + You know, people swinging on chandeliers, swordfights over + the table, the sort of thing Hrun the Barbarian and the + Weasel are always getting involved in. You know -- + _excitement_." + [ The Colour of Magic, by Terry Pratchett ] +tyr + Yet remains that one of the Aesir who is called Tyr: + he is most daring, and best in stoutness of heart, and he + has much authority over victory in battle; it is good for + men of valor to invoke him. It is a proverb, that he is + Tyr-valiant, who surpasses other men and does not waver. + He is wise, so that it is also said, that he that is wisest + is Tyr-prudent. This is one token of his daring: when the + Aesir enticed Fenris-Wolf to take upon him the fetter Gleipnir, + the wolf did not believe them, that they would loose him, + until they laid Tyr's hand into his mouth as a pledge. But + when the Aesir would not loose him, then he bit off the hand + at the place now called 'the wolf's joint;' and Tyr is one- + handed, and is not called a reconciler of men. + [ The Prose Edda, by Snorri Sturluson ] +*hulk + Umber hulks are powerful subterranean predators whose + iron-like claws allow them to burrow through solid stone in + search of prey. They are tremendously strong; muscles bulge + beneath their thick, scaly hides and their powerful arms and + legs all end in great claws. +*unicorn +unicorn horn + Men have always sought the elusive unicorn, for the single + twisted horn which projected from its forehead was thought to + be a powerful talisman. It was said that the unicorn had + simply to dip the tip of its horn in a muddy pool for the water + to become pure. Men also believed that to drink from this horn + was a protection against all sickness, and that if the horn was + ground to a powder it would act as an antidote to all poisons. + Less than 200 years ago in France, the horn of a unicorn was + used in a ceremony to test the royal food for poison. + + Although only the size of a small horse, the unicorn is a very + fierce beast, capable of killing an elephant with a single + thrust from its horn. Its fleetness of foot also makes this + solitary creature difficult to capture. However, it can be + tamed and captured by a maiden. Made gentle by the sight of a + virgin, the unicorn can be lured to lay its head in her lap, and + in this docile mood, the maiden may secure it with a golden rope. + [ Mythical Beasts, by Deirdre Headon (The Leprechaun Library) ] + + Martin took a small sip of beer. "Almost ready," he said. + "You hold your beer awfully well." + Tlingel laughed. "A unicorn's horn is a detoxicant. Its + possession is a universal remedy. I wait until I reach the + warm glow stage, then I use my horn to burn off any excess and + keep me right there." + [ Unicorn Variations, by Roger Zelazny ] +valkyrie +* valkyrie + The Valkyries were the thirteen choosers of the slain, the + beautiful warrior-maids of Odin who rode through the air and + over the sea. They watched the progress of the battle and + selected the heroes who were to fall fighting. After they + were dead, the maidens rewarded the heroes by kissing them + and then led their souls to Valhalla, where the warriors + lived happily in an ideal existence, drinking and eating + without restraint and fighting over again the battles in + which they died and in which they had won their deathless + fame. + [ The Encyclopaedia of Myths and Legends of All + Nations, by Herbert Robinson and Knox + Wilson ] +vampire +vampire bat +vampire lord + The Oxford English Dictionary is quite unequivocal: + _vampire_ - "a preternatural being of a malignant nature (in + the original and usual form of the belief, a reanimated + corpse), supposed to seek nourishment, or do harm, by sucking + the blood of sleeping persons. ..." +venus + Venus, the goddess of love and beauty, was the daughter of + Jupiter and Dione. Others say that Venus sprang from the + foam of the sea. The zephyr wafted her along the waves to + the Isle of Cyprus, where she was received and attired by + the Seasons, and then led to the assembly of the gods. All + were charmed with her beauty, and each one demanded her + for his wife. Jupiter gave her to Vulcan, in gratitude for + the service he had rendered in forging thunderbolts. So + the most beautiful of the goddesses became the wife of the + most ill-favoured of gods. + [ Bulfinch's Mythology, by Thomas Bulfinch ] +vlad* + Vlad Dracula the Impaler was a 15th-Century monarch of the + Birgau region of the Carpathian Mountains, in what is now + Romania. In Romanian history he is best known for two things. + One was his skilled handling of the Ottoman Turks, which kept + them from making further inroads into Christian Europe. The + other was the ruthless manner in which he ran his fiefdom. + He dealt with perceived challengers to his rule by impaling + them upright on wooden stakes. Visiting dignitaries who + failed to doff their hats had them nailed to their head. +*vortex +vortices + Swirling clouds of pure elemental energies, the vortices are + thought to be related to the larger elementals. Though the + vortices do no damage when touched, they are noted for being + able to envelop unwary travellers. The hapless fool thus + swallowed by a vortex will soon perish from exposure to the + element the vortex is composed of. +vrock + The vrock is one of the weaker forms of demon. It resembles + a cross between a human being and a vulture and does physical + damage by biting and by using the claws on both its arms and + feet. +wakizashi + The samurai warrior traditionally wears two swords; the + wakizashi is the shorter of the two. See also katana. +wand of * +*wand + 'Saruman!' he cried, and his voice grew in power and authority. + 'Behold, I am not Gandalf the Grey, whom you betrayed. I am + Gandalf the White, who has returned from death. You have no + colour now, and I cast you from the order and from the Council.' + He raised his hand, and spoke slowly in a clear cold voice. + 'Saruman, your staff is broken.' There was a crack, and the + staff split asunder in Saruman's hand, and the head of it + fell down at Gandalf's feet. 'Go!' said Gandalf. With a cry + Saruman fell back and crawled away. + [ The Two Towers, by J.R.R. Tolkien ] +warg + Suddenly Aragorn leapt to his feet. "How the wind howls!" + he cried. "It is howling with wolf-voices. The Wargs have + come west of the Mountains!" + "Need we wait until morning then?" said Gandalf. "It is as I + said. The hunt is up! Even if we live to see the dawn, who + now will wish to journey south by night with the wild wolves + on his trail?" + "How far is Moria?" asked Boromir. + "There was a door south-west of Caradhras, some fifteen miles + as the crow flies, and maybe twenty as the wolf runs," + answered Gandalf grimly. + "Then let us start as soon as it is light tomorrow, if we can," + said Boromir. "The wolf that one hears is worse than the orc + that one fears." + "True!" said Aragorn, loosening his sword in its sheath. "But + where the warg howls, there also the orc prowls." + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +~mjollnir +war*hammer + They had come together at the ford of the Trident while the + battle crashed around them, Robert with his warhammer and his + great antlered helm, the Targaryen prince armored all in + black. On his breastplate was the three-headed dragon of his + House, wrought all in rubies that flashed like fire in the + sunlight. The waters of the Trident ran red around the + hooves of their destriers as they circled and clashed, again + and again, until at last a crushing blow from Robert's hammer + stove in the dragon and the chest behind it. When Ned had + finally come on the scene, Rhaegar lay dead in the stream, + while men of both armies scrambled in the swirling waters for + rubies knocked free of his armor. + [ A Game of Thrones, by George R.R. Martin ] +water + Day after day, day after day, + We stuck, nor breath nor motion; + As idle as a painted ship + Upon a painted ocean. + + Water, water, everywhere, + And all the boards did shrink; + Water, water, everywhere + Nor any drop to drink. + [ The Rime of the Ancient Mariner, by Samuel Taylor + Coleridge ] +web + Oh what a tangled web we weave, + When first we practise to deceive! + [ Marmion, by Sir Walter Scott ] +# werecritter -- see "lycanthrope" +*wight + When he came to himself again, for a moment he could recall + nothing except a sense of dread. Then suddenly he knew that + he was imprisoned, caught hopelessly; he was in a barrow. A + Barrow-wight had taken him, and he was probably already under + the dreadful spells of the Barrow-wights about which whispered + tales spoke. He dared not move, but lay as he found himself: + flat on his back upon a cold stone with his hands on his + breast. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +# note: need to convert player character "gnomish wizard" into just "wizard" +# in the lookup code to avoid conflict with the monster of that same name +~gnomish wizard +wizard +* wizard +apprentice + Ebenezum walked before me along the closest thing we could + find to a path in these overgrown woods. Every few paces he + would pause, so that I, burdened with a pack stuffed with + arcane and heavy paraphernalia, could catch up with his + wizardly strides. He, as usual, carried nothing, preferring, + as he often said, to keep his hands free for quick conjuring + and his mind free for the thoughts of a mage. + [ A Dealing with Demons, by Craig Shaw Gardner ] +wizard of yendor + No one knows how old this mighty wizard is, or from whence he + came. It is known that, having lived a span far greater than + any normal man's, he grew weary of lesser mortals; and so, + spurning all human company, he forsook the dwellings of men + and went to live in the depths of the Earth. He took with + him a dreadful artifact, the Book of the Dead, which is said + to hold great power indeed. Many have sought to find the + wizard and his treasure, but none have found him and lived to + tell the tale. Woe be to the incautious adventurer who + disturbs this mighty sorcerer! +wolf +*wolf +*wolf cub + The ancestors of the modern day domestic dog, wolves are + powerful muscular animals with bushy tails. Intelligent, + social animals, wolves live in family groups or packs made + up of multiple family units. These packs cooperate in hunting + down prey. +woodchuck + The Usenet Oracle requires an answer to this question! + + > How much wood could a woodchuck chuck if a woodchuck could + > chuck wood? + + "Oh, heck! I'll handle *this* one!" The Oracle spun the terminal + back toward himself, unlocked the ZOT-guard lock, and slid the + glass guard away from the ZOT key. "Ummmm....could you turn around + for a minute? ZOTs are too graphic for the uninitiated. Even *I* + get a little squeamish sometimes..." The neophyte turned around, + and heard the Oracle slam his finger on a computer key, followed + by a loud ZZZZOTTTTT and the smell of ozone. + [ Excerpted from Internet Oracularity 576.6 ] +*worm +long worm tail +worm tooth +crysknife + [The crysknife] is manufactured in two forms from teeth taken + from dead sandworms. The two forms are "fixed" and "unfixed". + An unfixed knife requires proximity to a human body's + electrical field to prevent disintegration. Fixed knives + are treated for storage. All are about 20 centimeters long. + [ Dune, by Frank Herbert ] +wraith +nazgul + Immediately, though everything else remained as before, dim + and dark, the shapes became terribly clear. He was able to + see beneath their black wrappings. There were five tall + figures: two standing on the lip of the dell, three advancing. + In their white faces burned keen and merciless eyes; under + their mantles were long grey robes; upon their grey hairs + were helms of silver; in their haggard hands were swords of + steel. Their eyes fell on him and pierced him, as they + rushed towards him. Desperate, he drew his own sword, and + it seemed to him that it flickered red, as if it was a + firebrand. Two of the figures halted. The third was taller + than the others: his hair was long and gleaming and on his + helm was a crown. In one hand he held a long sword, and in + the other a knife; both the knife and the hand that held it + glowed with a pale light. He sprang forward and bore down + on Frodo. + [ The Fellowship of the Ring, by J.R.R. Tolkien ] +wumpus + The Wumpus, by the way, is not bothered by the hazards since + he has sucker feet and is too big for a bat to lift. If you + try to shoot him and miss, there's also a chance that he'll + up and move himself into another cave, though by nature the + Wumpus is a sedentary creature. + [ wump (6) -- "Hunt the Wumpus" ] +xan + They sent their friend the mosquito [xan] ahead of them to + find out what lay ahead. "Since you are the one who sucks + the blood of men walking along paths," they told the mosquito, + "go and sting the men of Xibalba." The mosquito flew + down the dark road to the Underworld. Entering the house of + the Lords of Death, he stung the first person that he saw... + + The mosquito stung this man as well, and when he yelled, the + man next to him asked, "Gathered Blood, what's wrong?" So + he flew along the row stinging all the seated men until he + knew the names of all twelve. + [ Popul Vuh, as translated by Ralph Nelson ] +xorn + A distant cousin of the earth elemental, the xorn has the + ability to shift the cells of its body around in such a way + that it becomes porous to inert material. This gives it the + ability to pass through any obstacle that might be between it + and its next meal. +ya + The arrow of choice of the samurai, ya are made of very + straight bamboo, and are tipped with hardened steel. +yeenoghu + Yeenoghu, the demon lord of gnolls, still exists although + all his followers have been wiped off the face of the earth. + He casts magic projectiles at those close to him, and a mere + gaze into his piercing eyes may hopelessly confuse the + battle-weary adventurer. +yeti + The Abominable Snowman, or yeti, is one of the truly great + unknown animals of the twentieth century. It is a large hairy + biped that lives in the Himalayan region of Asia ... The story + of the Abominable Snowman is filled with mysteries great and + small, and one of the most difficult of all is how it got that + awful name. The creature is neither particularly abominable, + nor does it necessarily live in the snows. _Yeti_ is a Tibetan + word which may apply either to a real, but unknown animal of + the Himalayas, or to a mountain spirit or demon -- no one is + quite sure which. And after nearly half a century in which + Westerners have trampled around looking for the yeti, and + asking all sorts of questions, the original native traditions + concerning the creature have become even more muddled and + confused. + [ The Encyclopedia of Monsters, by Daniel Cohen ] +*yugake + Japanese leather archery gloves. Gloves made for use while + practicing had thumbs reinforced with horn. Those worn into + battle had thumbs reinforced with a double layer of leather. +yumi + The samurai is highly trained with a special type of bow, + the yumi. Like the ya, the yumi is made of bamboo. With + the yumi-ya, the bow and arrow, the samurai is an extremely + accurate and deadly warrior. +*zombie + The zombi... is a soulless human corpse, still dead, but + taken from the grave and endowed by sorcery with a + mechanical semblance of life, -- it is a dead body which is + made to walk and act and move as if it were alive. + [ W. B. Seabrook ] +zruty + The zruty are wild and gigantic beings, living in the + wildernesses of the Tatra mountains. diff --git a/dat/dungeon.def b/dat/dungeon.def new file mode 100644 index 0000000..fbb17bc --- /dev/null +++ b/dat/dungeon.def @@ -0,0 +1,138 @@ +# SCCS Id: @(#)dungeon.def 3.4 1996/03/10 +# Copyright (c) 1990-95 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The dungeon description file for the "standard" 3.1 NetHack. +# +# Note: The order of the definition of dungeons in this file +# reflects in their order of creation in the real dungeon. +# The "Main" branch must *always* be first. +# Also note that the order of the dungeons in this file +# determines the order in which branch levels are assigned. +# If two dungeons have overlapping ranges for their entry +# points, then you should list the dungeon with the _least_ +# number of choices for its branch level _first_. +# + +DUNGEON: "The Dungeons of Doom" "D" (25, 5) +ALIGNMENT: unaligned +BRANCH: "The Gnomish Mines" @ (2, 3) +%REINCARNATION LEVEL: "rogue" "R" @ (15, 4) +LEVEL: "oracle" "O" @ (5, 5) +LEVALIGN: neutral +CHAINBRANCH: "Sokoban" "oracle" + (1, 0) up +RNDLEVEL: "bigrm" "B" @ (10, 3) 40 5 +CHAINBRANCH: "The Quest" "oracle" + (6, 2) portal +BRANCH: "Fort Ludios" @ (18, 4) portal +RNDLEVEL: "medusa" "none" @ (-5, 4) 2 +LEVALIGN: chaotic +LEVEL: "castle" "none" @ (-1, 0) +CHAINBRANCH: "Gehennom" "castle" + (0, 0) no_down +BRANCH: "The Elemental Planes" @ (1, 0) no_down up + +# +# Gehennom +# +# Now re-worked for 3.1, hell is hopefully going to be a little +# less boring. Also, in 3.1, the tower is not considered as a +# part of hell, but is set up as a separate dungeon. +# +# Gehennom is no longer considered "hellish" as a complete dungeon. +# That is, fire resistance is no longer a condition for survival in +# it. However, Gehennom, and the special levels in it in particular, +# is abundant with fire traps. As a result, fire resistance is still +# a prudent survival strategy in Gehennom. +# +# Note: Gehennom *must* be the second dungeon defined so that +# monsters can properly migrate here under certain +# circumstances. +# +DUNGEON: "Gehennom" "G" (20, 5) +DESCRIPTION: mazelike +DESCRIPTION: hellish +ALIGNMENT: noalign +BRANCH: "Vlad's Tower" @ (9, 5) up +LEVEL: "valley" "V" @ (1, 0) +LEVEL: "sanctum" "none" @ (-1, 0) +LEVEL: "juiblex" "J" @ (4, 4) +LEVEL: "baalz" "B" @ (6, 4) +LEVEL: "asmodeus" "A" @ (2, 6) +LEVEL: "wizard1" "none" @ (11, 6) +CHAINLEVEL: "wizard2" "X" "wizard1" + (1, 0) +CHAINLEVEL: "wizard3" "Y" "wizard1" + (2, 0) +LEVEL: "orcus" "O" @ (10, 6) +LEVEL: "fakewiz1" "F" @ (-6,4) +LEVEL: "fakewiz2" "G" @ (-6,4) + +# +# The Mines of the Gnomes of Zurich. +# +DUNGEON: "The Gnomish Mines" "M" (8, 2) +ALIGNMENT: lawful +DESCRIPTION: mazelike +RNDLEVEL: "minetn" "T" @ (3, 2) 7 +LEVELDESC: town +RNDLEVEL: "minend" "E" @ (-1, 0) 3 + +# +# The Questdungeon +# +# This is a proto-dungeon. The level file names will be prepended with +# the first letter of the character name during initialization. +# A special "x-fill" level must be defined in the levels description +# file. It will be used for all levels not defined explicitly below. +# +DUNGEON: "The Quest" "Q" (5, 2) +LEVEL: "x-strt" "none" @ (1, 1) +LEVEL: "x-loca" "L" @ (3, 1) +LEVEL: "x-goal" "none" @ (-1, 0) + +# +# Sokoban +# +DUNGEON: "Sokoban" "none" (4, 0) +DESCRIPTION: mazelike +ALIGNMENT: neutral +ENTRY: -1 +RNDLEVEL: "soko1" "none" @ (1, 0) 2 +RNDLEVEL: "soko2" "none" @ (2, 0) 2 +RNDLEVEL: "soko3" "none" @ (3, 0) 2 +RNDLEVEL: "soko4" "none" @ (4, 0) 2 + +# +# The Central Vault of Croesus. +# +DUNGEON: "Fort Ludios" "K" (1, 0) +DESCRIPTION: mazelike +ALIGNMENT: unaligned +LEVEL: "knox" "K" @ (-1, 0) +# +# Vlad's Tower +# +# It has been removed from Gehennom, and it is surrounded by stone. +# Must not allow bones files for its top level. +# +DUNGEON: "Vlad's Tower" "T" (3, 0) +PROTOFILE: "tower" +DESCRIPTION: mazelike +ALIGNMENT: chaotic +ENTRY: -1 +LEVEL: "tower1" "none" @ (1, 0) + +# +# The Endgame levels +# +# Enter on 2nd level from bottom; 1st (from bottom) is a +# placeholder for surface level, and should be unreachable. +# [Note: the name "dummy" is checked for in init_dungeons().] +# +DUNGEON: "The Elemental Planes" "E" (6, 0) +DESCRIPTION: mazelike +ALIGNMENT: unaligned +ENTRY: -2 +LEVEL: "astral" "none" @ (1, 0) +LEVEL: "water" "none" @ (2, 0) +LEVEL: "fire" "none" @ (3, 0) +LEVEL: "air" "none" @ (4, 0) +LEVEL: "earth" "none" @ (5, 0) +LEVEL: "dummy" "none" @ (6, 0) diff --git a/dat/endgame.des b/dat/endgame.des new file mode 100644 index 0000000..60500e1 --- /dev/null +++ b/dat/endgame.des @@ -0,0 +1,616 @@ +# SCCS Id: @(#)endgame.des 3.4 2002/01/19 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1992,1993 by Izchak Miller, David Cohrs, +# and Timo Hakulinen +# NetHack may be freely redistributed. See license for details. +# +# These are the ENDGAME levels: earth, air, fire, water, and astral. +# The top-most level, the Astral Level, has 3 temples and shrines. +# Players are supposed to sacrifice the Amulet of Yendor on the appropriate +# shrine. + +MAZE:"earth",' ' +FLAGS: noteleport,hardfloor,shortsighted +MESSAGE: "Well done, mortal!" +MESSAGE: "But now thou must face the final Test..." +MESSAGE: "Prove thyself worthy or perish!" + +GEOMETRY:center,center +# The player lands, upon arrival, in the +# lower-right cavern. The location of the +# portal to the next level is randomly chosen. +# This map has no visible outer boundary, and +# is mostly diggable "rock". +MAP + + ... + .... .. + ..... ... .. + .... .... ... + .... ... .... ... . + .. .. ....... . .. + .. ... . + . .. . ... + .. .. . .. . + .. ... . + ... ... + .. ... .. + .... .. + .. ... + .. ..... + ... ... + .... + .. + +ENDMAP +# Since there are no stairs, this forces the hero's initial placement +TELEPORT_REGION:(69,16,69,16),(0,0,0,0) +PORTAL:(0,0,75,19),(65,13,75,19),"air" +# Some helpful monsters. Making sure a +# pick axe and at least one wand of digging +# are available. +MONSTER:'@',"Elvenking",(67,16) +MONSTER:'H',"minotaur",(67,14) +# An assortment of earth-appropriate nasties +# in each cavern. +MONSTER:'E',"earth elemental",(52,13),hostile +MONSTER:'E',"earth elemental",(53,13),hostile +MONSTER:'T',"rock troll",(53,12) +MONSTER:'H',"stone giant",(54,12) +# +MONSTER:'S',"pit viper",(70,05) +MONSTER:'&',"barbed devil",(69,06) +MONSTER:'H',"stone giant",(69,08) +MONSTER:''',"stone golem",(71,08) +MONSTER:'&',"pit fiend",(70,09) +MONSTER:'E',"earth elemental",(70,08),hostile +# +MONSTER:'E',"earth elemental",(60,03),hostile +MONSTER:'H',"stone giant",(61,04) +MONSTER:'E',"earth elemental",(62,04),hostile +MONSTER:'E',"earth elemental",(61,05),hostile +MONSTER:'s',"scorpion",(62,05) +MONSTER:'p',"rock piercer",(63,05) +# +MONSTER:'U',"umber hulk",(40,05) +MONSTER:'v',"dust vortex",(42,05) +MONSTER:'T',"rock troll",(38,06) +MONSTER:'E',"earth elemental",(39,06),hostile +MONSTER:'E',"earth elemental",(41,06),hostile +MONSTER:'E',"earth elemental",(38,07),hostile +MONSTER:'H',"stone giant",(39,07) +MONSTER:'E',"earth elemental",(43,07),hostile +MONSTER:''',"stone golem",(37,08) +MONSTER:'S',"pit viper",(43,08) +MONSTER:'S',"pit viper",(43,09) +MONSTER:'T',"rock troll",(44,10) +# +MONSTER:'E',"earth elemental",(02,01),hostile +MONSTER:'E',"earth elemental",(03,01),hostile +MONSTER:''',"stone golem",(01,02) +MONSTER:'E',"earth elemental",(02,02),hostile +MONSTER:'T',"rock troll",(04,03) +MONSTER:'T',"rock troll",(03,03) +MONSTER:'&',"pit fiend",(03,04) +MONSTER:'E',"earth elemental",(04,05),hostile +MONSTER:'S',"pit viper",(05,06) +# +MONSTER:'E',"earth elemental",(21,02),hostile +MONSTER:'E',"earth elemental",(21,03),hostile +MONSTER:'H',"minotaur",(21,04) +MONSTER:'E',"earth elemental",(21,05),hostile +MONSTER:'T',"rock troll",(22,05) +MONSTER:'E',"earth elemental",(22,06),hostile +MONSTER:'E',"earth elemental",(23,06),hostile +# +MONSTER:'S',"pit viper",(14,08) +MONSTER:'&',"barbed devil",(14,09) +MONSTER:'E',"earth elemental",(13,10),hostile +MONSTER:'T',"rock troll",(12,11) +MONSTER:'E',"earth elemental",(14,12),hostile +MONSTER:'E',"earth elemental",(15,13),hostile +MONSTER:'H',"stone giant",(17,13) +MONSTER:''',"stone golem",(18,13) +MONSTER:'&',"pit fiend",(18,12) +MONSTER:'E',"earth elemental",(18,11),hostile +MONSTER:'E',"earth elemental",(18,10),hostile +# +MONSTER:'&',"barbed devil",(02,16) +MONSTER:'E',"earth elemental",(03,16),hostile +MONSTER:'T',"rock troll",(02,17) +MONSTER:'E',"earth elemental",(04,17),hostile +MONSTER:'E',"earth elemental",(04,18),hostile + +OBJECT:'`',"boulder",random + + +MAZE:"air",' ' +FLAGS: noteleport,hardfloor,shortsighted +# The following messages are somewhat obtuse, to make then +# equally meaningful if the player can see or not. +MESSAGE: "What a strange feeling!" +MESSAGE: "You notice that there is no gravity here." +GEOMETRY:center,center +# The player lands, upon arrival, in the +# lower-left area. The location of the +# portal to the next level is randomly chosen. +# This map has no visible outer boundary, and +# is all "air". +MAP +AAAAAAAAAAAAAAAAAAAAAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAACCCCCCAAAAAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAACCAACCCCCAAAAAACCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAACCACCCCCCCAAAAACCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCAAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAACCCCAAACCAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAACCCCAAAAAACCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAACCCCCCCAAAACCACCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAACCCCAAAAAAACCACAACCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCAACCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACCCCAAACCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACACCCCCAAACCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACCCCCCCAAAACCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACACCCCCCCCAAACCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCAACCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAACACCCCCCCCCCACCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAACCCCCCAAACCCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAACCAAAAAACCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +ENDMAP +# Use up and down regions to partition the level into three parts; +# teleportation can't cross from one part into another. +# The up region is where you'll arrive after activating the portal from +# the preceding level; the exit portal is placed inside the down region. +TELEPORT_REGION:levregion(01,00,24,20),levregion(25,00,79,20),up +TELEPORT_REGION:levregion(56,00,79,20),levregion(01,00,55,20),down +PORTAL:levregion(57,01,78,19),(0,0,0,0),"fire" +REGION:(00,00,75,19),lit,"ordinary" +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile +MONSTER:'E',"air elemental",random,hostile + +MONSTER:'e',"floating eye",random,hostile +MONSTER:'e',"floating eye",random,hostile +MONSTER:'e',"floating eye",random,hostile + +MONSTER:'y',"yellow light",random,hostile +MONSTER:'y',"yellow light",random,hostile +MONSTER:'y',"yellow light",random,hostile + +MONSTER:'A',"couatl",random + +MONSTER:'D',random,random +MONSTER:'D',random,random +MONSTER:'D',random,random +MONSTER:'D',random,random +MONSTER:'D',random,random + +MONSTER:'E',random,random +MONSTER:'E',random,random +MONSTER:'E',random,random +MONSTER:'J',random,random +MONSTER:'J',random,random + +MONSTER:'&',"djinni",random,hostile +MONSTER:'&',"djinni",random,hostile +MONSTER:'&',"djinni",random,hostile + +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"fog cloud",random,hostile +MONSTER:'v',"energy vortex",random,hostile +MONSTER:'v',"energy vortex",random,hostile +MONSTER:'v',"energy vortex",random,hostile +MONSTER:'v',"energy vortex",random,hostile +MONSTER:'v',"energy vortex",random,hostile +MONSTER:'v',"steam vortex",random,hostile +MONSTER:'v',"steam vortex",random,hostile +MONSTER:'v',"steam vortex",random,hostile +MONSTER:'v',"steam vortex",random,hostile +MONSTER:'v',"steam vortex",random,hostile + + +MAZE:"fire",' ' +FLAGS: noteleport,hardfloor,shortsighted +GEOMETRY:center,center +# The player lands, upon arrival, in the +# lower-right. The location of the +# portal to the next level is randomly chosen. +# This map has no visible outer boundary, and +# is mostly open area, with lava lakes and bunches of fire traps. +MAP +............................................................................ +....LLLLLLLL............L.......................LLL......................... +...LL...................L......................LLLL................LL....... +...L.............LLLL...LL....LL...............LLLLL.............LLL........ +.LLLL..............LL....L.....LLL..............LLLL..............LLLL...... +..........LLLL...LLLL...LLL....LLL......L........LLLL....LL........LLL...... +........LLLLLLL...LL.....L......L......LL.........LL......LL........LL...L.. +........LL..LLL..LL......LL......LLLL..L.........LL......LLL............LL.. +....L..LL....LLLLL.................LLLLLLL.......L......LL............LLLLLL +....L..L.....LL.LLLL.......L............L........LLLLL.LL......LL.........LL +....LL........L...LL......LL.............LLL.....L...LLL.......LLL.........L +.....LLLLLL........L.......LLL.............L....LL...L.LLL......LLLLLLL..... +..........LLLL............LL.L.............L....L...LL.........LLL..LLL..... +...........................LLLLL...........LL...L...L........LLLL..LLLLLL... +.....LLLL.............LL....LL.......LLL...LL.......L..LLL....LLLLLLL....... +.......LLL.........LLLLLLLLLLL......LLLLL...L...........LL...LL...LL........ +.........LL.......LL.........LL.......LLL....L..LLL....LL.........LL........ +..........LLLLLLLLL...........LL....LLL.......LLLLL.....LL........LL........ +.................L.............LLLLLL............LL...LLLL.........LL....... +.................................LL....................LL................... +ENDMAP +TELEPORT_REGION:(69,16,69,16),(0,0,0,0) +PORTAL:(0,0,75,19),(65,13,75,19),"water" + +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +TRAP:"fire",random +# An assortment of fire-appropriate nasties +MONSTER:'D',"red dragon",random +MONSTER:'&',"balrog",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'v',"fire vortex",random +MONSTER:'d',"hell hound",random +# +MONSTER:'H',"fire giant",random +MONSTER:'&',"barbed devil",random +MONSTER:'d',"hell hound",random +MONSTER:''',"stone golem",random +MONSTER:'&',"pit fiend",random +MONSTER:'E',"fire elemental",random,hostile +# +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'d',"hell hound",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'s',"scorpion",random +MONSTER:'H',"fire giant",random +# +MONSTER:'d',"hell hound",random +MONSTER:'v',"dust vortex",random +MONSTER:'v',"fire vortex",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'d',"hell hound",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:''',"stone golem",random +MONSTER:'S',"pit viper",random +MONSTER:'S',"pit viper",random +MONSTER:'v',"fire vortex",random +# +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'H',"fire giant",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'v',"fire vortex",random +MONSTER:'v',"fire vortex",random +MONSTER:'&',"pit fiend",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'S',"pit viper",random +# +MONSTER:':',"salamander",random,hostile +MONSTER:':',"salamander",random,hostile +MONSTER:'H',"minotaur",random +MONSTER:':',"salamander",random,hostile +MONSTER:'v',"steam vortex",random +MONSTER:':',"salamander",random,hostile +MONSTER:':',"salamander",random,hostile +# +MONSTER:'H',"fire giant",random +MONSTER:'&',"barbed devil",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'v',"fire vortex",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'d',"hell hound",random +MONSTER:'H',"fire giant",random +MONSTER:'&',"pit fiend",random +MONSTER:'E',"fire elemental",random,hostile +MONSTER:'E',"fire elemental",random,hostile +# +MONSTER:'&',"barbed devil",random +MONSTER:':',"salamander",random,hostile +MONSTER:'v',"steam vortex",random +MONSTER:':',"salamander",random,hostile +MONSTER:':',"salamander",random,hostile + +OBJECT:'`',"boulder",random +OBJECT:'`',"boulder",random +OBJECT:'`',"boulder",random +OBJECT:'`',"boulder",random +OBJECT:'`',"boulder",random + + +MAZE:"water",' ' +FLAGS: noteleport,hardfloor,shortsighted +MESSAGE: "You find yourself suspended in an air bubble surrounded by water." +GEOMETRY:center,center +# The player lands upon arrival to an air bubble +# within the leftmost third of the level. The +# portal to the next level is randomly located in an air +# bubble within the rightmost third of the level. +# Bubbles are generated by special code in mkmaze.c for now. +MAP +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW +ENDMAP +TELEPORT_REGION:(0,0,25,19),(0,0,0,0) +PORTAL:(51,0,75,19),(0,0,0,0),"astral" +# A fisherman's dream... +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"giant eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"electric eel",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"kraken",random +MONSTER:';',"shark",random +MONSTER:';',"shark",random +MONSTER:';',"shark",random +MONSTER:';',"shark",random +MONSTER:';',"piranha",random +MONSTER:';',"piranha",random +MONSTER:';',"piranha",random +MONSTER:';',"piranha",random +MONSTER:';',"jellyfish",random +MONSTER:';',"jellyfish",random +MONSTER:';',"jellyfish",random +MONSTER:';',"jellyfish",random +MONSTER:';',random,random +MONSTER:';',random,random +MONSTER:';',random,random +MONSTER:';',random,random +# These guys feel like home here +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile +MONSTER:'E',"water elemental",random,hostile + + +MAZE:"astral",' ' +FLAGS: noteleport,hardfloor,nommap,shortsighted +MESSAGE: "You arrive on the Astral Plane!" +MESSAGE: "Here the High Temples of the aligned gods are located." +MESSAGE: "You sense alarm, hostility, and excitement in the air!" +GEOMETRY:center,center +MAP + --------------- + |.............| + |..---------..| + |..|.......|..| +--------------- |..|.......|..| --------------- +|.............| |..|.......|..| |.............| +|..---------..-| |-------| |..|.......|..| |-------| |-..---------..| +|..|.......|...-| |-.......-| |..|.......|..| |-.......-| |-...|.......|..| +|..|.......|....-|-.........-||..----+----..||-.........-|-....|.......|..| +|..|.......+.....+...........||.............||...........+.....+.......|..| +|..|.......|....-|-.........-|--|.........|--|-.........-|-....|.......|..| +|..|.......|...-| |-.......-| -|---+---|- |-.......-| |-...|.......|..| +|..---------..-| |---+---| |-.......-| |---+---| |-..---------..| +|.............| |...|-----|-.........-|-----|...| |.............| +--------------- |.........|...........|.........| --------------- + -------...|-.........-|...------- + |....|-.......-|....| + ---...|---+---|...--- + |...............| + ----------------- +ENDMAP +# Rider locations +RANDOM_PLACES:(23,9),(37,14),(51,9) +# Where the player will land on arrival +TELEPORT_REGION:(29,15,45,15),(30,15,44,15) +# Lit courts +REGION:(01,05,16,14),lit,"ordinary",filled,true +REGION:(31,01,44,10),lit,"ordinary",filled,true +REGION:(61,05,74,14),lit,"ordinary",filled,true +# A Sanctum for each alignment +# The shrines' alignments are shuffled for +# each game +REGION:(04,07,10,11),lit,"temple" +REGION:(34,03,40,07),lit,"temple" +REGION:(64,07,70,11),lit,"temple" +ALTAR:(07,09),align[0],sanctum +ALTAR:(37,05),align[1],sanctum +ALTAR:(67,09),align[2],sanctum +# Doors +DOOR:closed,(11,09) +DOOR:closed,(17,09) +DOOR:locked,(23,12) +DOOR:locked,(37,08) +DOOR:closed,(37,11) +DOOR:closed,(37,17) +DOOR:locked,(51,12) +DOOR:locked,(57,09) +DOOR:closed,(63,09) +# Non diggable and phazeable everywhere +NON_DIGGABLE:(00,00,74,19) +NON_PASSWALL:(00,00,74,19) +# Moloch's horde +# West round room +MONSTER:'@',"aligned priest",(18,09),noalign,hostile +MONSTER:'@',"aligned priest",(19,08),noalign,hostile +MONSTER:'@',"aligned priest",(19,09),noalign,hostile +MONSTER:'@',"aligned priest",(19,10),noalign,hostile +MONSTER:'A',"Angel",(20,09),noalign,hostile +MONSTER:'A',"Angel",(20,10),noalign,hostile +MONSTER:'&',"Pestilence",place[0],hostile +# South-central round room +MONSTER:'@',"aligned priest",(36,12),noalign,hostile +MONSTER:'@',"aligned priest",(37,12),noalign,hostile +MONSTER:'@',"aligned priest",(38,12),noalign,hostile +MONSTER:'@',"aligned priest",(36,13),noalign,hostile +MONSTER:'A',"Angel",(38,13),noalign,hostile +MONSTER:'A',"Angel",(37,13),noalign,hostile +MONSTER:'&',"Death",place[1],hostile +# East round room +MONSTER:'@',"aligned priest",(56,09),noalign,hostile +MONSTER:'@',"aligned priest",(55,08),noalign,hostile +MONSTER:'@',"aligned priest",(55,09),noalign,hostile +MONSTER:'@',"aligned priest",(55,10),noalign,hostile +MONSTER:'A',"Angel",(54,09),noalign,hostile +MONSTER:'A',"Angel",(54,10),noalign,hostile +MONSTER:'&',"Famine",place[2],hostile +# +# The aligned horde +# +# We do not know in advance the alignment of the +# player. The mpeaceful bit will need resetting +# when the level is created. The setting here is +# but a place holder. +# +# West court +MONSTER:'@',"aligned priest",(12,07),chaos,hostile +MONSTER:'@',"aligned priest",(13,07),chaos,peaceful +MONSTER:'@',"aligned priest",(14,07),law,hostile +MONSTER:'@',"aligned priest",(12,11),law,peaceful +MONSTER:'@',"aligned priest",(13,11),neutral,hostile +MONSTER:'@',"aligned priest",(14,11),neutral,peaceful +MONSTER:'A',"Angel",(11,05),chaos,hostile +MONSTER:'A',"Angel",(12,05),chaos,peaceful +MONSTER:'A',"Angel",(13,05),law,hostile +MONSTER:'A',"Angel",(11,13),law,peaceful +MONSTER:'A',"Angel",(12,13),neutral,hostile +MONSTER:'A',"Angel",(13,13),neutral,peaceful +# Central court +MONSTER:'@',"aligned priest",(32,09),chaos,hostile +MONSTER:'@',"aligned priest",(33,09),chaos,peaceful +MONSTER:'@',"aligned priest",(34,09),law,hostile +MONSTER:'@',"aligned priest",(40,09),law,peaceful +MONSTER:'@',"aligned priest",(41,09),neutral,hostile +MONSTER:'@',"aligned priest",(42,09),neutral,peaceful +MONSTER:'A',"Angel",(31,08),chaos,hostile +MONSTER:'A',"Angel",(32,08),chaos,peaceful +MONSTER:'A',"Angel",(31,09),law,hostile +MONSTER:'A',"Angel",(42,08),law,peaceful +MONSTER:'A',"Angel",(43,08),neutral,hostile +MONSTER:'A',"Angel",(43,09),neutral,peaceful +# East court +MONSTER:'@',"aligned priest",(60,07),chaos,hostile +MONSTER:'@',"aligned priest",(61,07),chaos,peaceful +MONSTER:'@',"aligned priest",(62,07),law,hostile +MONSTER:'@',"aligned priest",(60,11),law,peaceful +MONSTER:'@',"aligned priest",(61,11),neutral,hostile +MONSTER:'@',"aligned priest",(62,11),neutral,peaceful +MONSTER:'A',"Angel",(61,05),chaos,hostile +MONSTER:'A',"Angel",(62,05),chaos,peaceful +MONSTER:'A',"Angel",(63,05),law,hostile +MONSTER:'A',"Angel",(61,13),law,peaceful +MONSTER:'A',"Angel",(62,13),neutral,hostile +MONSTER:'A',"Angel",(63,13),neutral,peaceful +# +# Assorted nasties +MONSTER:'L',random,random,hostile +MONSTER:'L',random,random,hostile +MONSTER:'L',random,random,hostile +MONSTER:'V',random,random,hostile +MONSTER:'V',random,random,hostile +MONSTER:'V',random,random,hostile +MONSTER:'D',random,random,hostile +MONSTER:'D',random,random,hostile +MONSTER:'D',random,random,hostile diff --git a/dat/gehennom.des b/dat/gehennom.des new file mode 100644 index 0000000..bee336f --- /dev/null +++ b/dat/gehennom.des @@ -0,0 +1,674 @@ +# SCCS Id: @(#)gehennom.des 3.4 1996/11/09 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1992 by M. Stephenson and Izchak Miller +# NetHack may be freely redistributed. See license for details. +# + +MAZE: "valley", ' ' +FLAGS: noteleport,hardfloor,nommap +GEOMETRY:center,center +MAP +---------------------------------------------------------------------------- +|...S.|..|.....| |.....-| |................| |...............| |...| +|---|.|.--.---.| |......--- ----..........-----.-----....---........---.-.| +| |.|.|..| |.| --........| |.............| |.......---| |-...........--| +| |...S..| |.| |.......-----.......------| |--------..---......------- | +|----------- |.| |-......| |....|...-- |...-----................---- | +|.....S....---.| |.......| |....|...| |..............----------- | +|.....|.|......| |.....--- |......--- |....---.......| | +|.....|.|------| |....-- --....-- |-------- ----....--------------- | +|.....|--......---BBB-| |...-- |.......| |..................| | +|..........||........-| --...| |.......| |...||.............| | +|.....|...-||-........------....| |.......---- |...||.............-- | +|.....|--......---...........--------..........| |.......---------...-- | +|.....| |------| |--.......--| |..B......----- -----....| |.| |....--- | +|.....| |......--| ------..| |----..B......| |.--------.-- |-.....---| +|------ |........| |.|....| |.....----BBBB---------...........---.........| +| |........| |...|..| |.....| |-.............--------...........---| +| --.....-----------.| |....-----.....---------- |.........---- | +| |..|..B...........| |.|..........|.| |.|........| | +---------------------------------------------------------------------------- +ENDMAP +# Dungeon Description +# The shrine to Moloch. +REGION:(01,06,05,14),lit,"temple" +# The Morgues +REGION:(19,01,24,08),unlit,"morgue",filled,true +REGION:(09,14,16,18),unlit,"morgue",filled,true +REGION:(37,09,43,14),unlit,"morgue",filled,true +# Stairs +STAIR:(01,01),down +# Branch location +BRANCH:(66,17,66,17),(0,0,0,0) +TELEPORT_REGION:(58,09,72,18),(0,0,0,0),down + +# Secret Doors +DOOR:locked,(04,01) +DOOR:locked,(08,04) +DOOR:locked,(06,06) + +# The altar of Moloch. +ALTAR:(03,10),noalign,shrine + +# Non diggable walls - everywhere! +NON_DIGGABLE:(00,00,75,19) + +# Objects +# **LOTS** of dead bodies (all human). +# note: no priest(esse)s or monks - maybe Moloch has a *special* +# fate reserved for members of *those* classes. +# +OBJECT:'%',"corpse",random,"archeologist",0 +OBJECT:'%',"corpse",random,"archeologist",0 +OBJECT:'%',"corpse",random,"barbarian",0 +OBJECT:'%',"corpse",random,"barbarian",0 +OBJECT:'%',"corpse",random,"caveman",0 +OBJECT:'%',"corpse",random,"cavewoman",0 +OBJECT:'%',"corpse",random,"healer",0 +OBJECT:'%',"corpse",random,"healer",0 +OBJECT:'%',"corpse",random,"knight",0 +OBJECT:'%',"corpse",random,"knight",0 +OBJECT:'%',"corpse",random,"ranger",0 +OBJECT:'%',"corpse",random,"ranger",0 +OBJECT:'%',"corpse",random,"rogue",0 +OBJECT:'%',"corpse",random,"rogue",0 +OBJECT:'%',"corpse",random,"samurai",0 +OBJECT:'%',"corpse",random,"samurai",0 +OBJECT:'%',"corpse",random,"tourist",0 +OBJECT:'%',"corpse",random,"tourist",0 +OBJECT:'%',"corpse",random,"valkyrie",0 +OBJECT:'%',"corpse",random,"valkyrie",0 +OBJECT:'%',"corpse",random,"wizard",0 +OBJECT:'%',"corpse",random,"wizard",0 +# +# Some random weapons and armor. +# +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:')',random,random +OBJECT:')',random,random +OBJECT:')',random,random +OBJECT:')',random,random +# +# Some random loot. +# +OBJECT:'*',"ruby",random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'/',random,random +OBJECT:'/',random,random +OBJECT:'=',random,random +OBJECT:'=',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random +OBJECT:'(',random,random +OBJECT:'(',random,random +OBJECT:'(',random,random + +# (Not so) Random traps. +TRAP:"spiked pit", (05,02) +TRAP:"spiked pit", (14,05) +TRAP:"sleep gas", (03,01) +TRAP:"board", (21,12) +TRAP:"board", random +TRAP:"dart", (60,01) +TRAP:"dart", (26,17) +TRAP:"anti magic", random +TRAP:"anti magic", random +TRAP:"magic", random +TRAP:"magic", random + +# Random monsters. +# The ghosts. +MONSTER:' ',"ghost",random +MONSTER:' ',"ghost",random +MONSTER:' ',"ghost",random +MONSTER:' ',"ghost",random +MONSTER:' ',"ghost",random +MONSTER:' ',"ghost",random +# Add a few bats for atmosphere. +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +MONSTER:'B',"vampire bat",random +# And a lich for good measure. +MONSTER:'L',random,random +# Some undead nasties for good measure +MONSTER:'V',random,random +MONSTER:'V',random,random +MONSTER:'V',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +# +# The Juiblex level +# +MAZE:"juiblex",' ' +FLAGS:noteleport,shortsighted +INIT_MAP:'.','}',true,true,unlit,false +# guarantee at least one open spot to ensure successful stair placement +GEOMETRY:left,bottom +MAP +}}}}}}}} +}}...}}} +}}}...}} +}}}}.}}} +}}}}}}}} +ENDMAP +OBJECT:'`',"boulder",random +GEOMETRY:right,top +MAP +}}}}}}}} +}}}}.}}} +}}}...}} +}}...}}} +}}}}}}}} +ENDMAP +OBJECT:'`',"boulder",random +# lair +GEOMETRY:center,center +MAP +..}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.. +.}}}.}}}}}..}}}..}}}}}..}}}..}}}}}..}}}..}}}}}.}}}. +}}}...}}..}}.}.}}.}}.}}}...}}}.}}}..}}}..}}}}...}}} +.}}}.}}.}}}.}}.}}.}}...}}.}}.....}}.....}....}.}}}. +..}}}..}}}.}}.}}.}}..}}.....}}.}}}.}}.}}}}}}}}}}}.. +.}}}..}}}}}.}}.}}.}}...}}}}}.....}}.}}}}}}.....}}}. +}}}..}}...}}..}}.}}}.}}}...}}}.}}}.}.}}}}..P.P..}}} +}}.}}}}...}}}}}.}...}}}..P..}}}.}.}}}.}}}}.....}}}} +}.}}}}.}}.}..}.}}}}}}}..P.P..}}}.}}}.}}..}}...}}}}. +.}}}}.}}}}....}}}}}.}}}..P..}}}.}}}}.}}..}}...}}}.} +}}}}..}}.}}..}}}}...}}}}...}}}.}}}}}.}}}}.}}}}}}.}} +}}}...}}...}}}..}}}}}}}}}}}}.....}}}}.}}...}..}.}}} +.}}}..}}.}}}}....}}..}}}..}}.....}}}}.}}}.}....}}}. +..}}}.}}}}..}}..}}..}}..}}..}}.}}}..}.}..}}}..}}}.. +.}}}.}}}}....}}}}..}}....}}}}}}}...}}}....}}}}.}}}. +}}}...}}}....}}}..}}}....}}}..}}...}}}....}}}...}}} +.}}}.}}}}}..}}}..}}}}}..}}}..}}}}}..}}}..}}}}}.}}}. +..}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.}}}}}.. +ENDMAP +# Random registers +RANDOM_MONSTERS:'j','b','P','F' +RANDOM_PLACES:(04,02),(46,02),(04,15),(46,15) +# Dungeon description +REGION:(00,00,50,17),unlit,"swamp" +MAZEWALK:(00,09),west +MAZEWALK:(50,08),east +STAIR:levregion(01,00,11,20),(0,0,50,17),down +STAIR:levregion(69,00,79,20),(0,0,50,17),up +BRANCH:levregion(01,00,11,20),(0,0,50,17) +TELEPORT_REGION:levregion(01,00,11,20),(0,0,50,17),up +TELEPORT_REGION:levregion(69,00,79,20),(0,0,50,17),down +FOUNTAIN:place[0] +MONSTER:'m',"giant mimic",place[1],m_feature "fountain" +MONSTER:'m',"giant mimic",place[2],m_feature "fountain" +MONSTER:'m',"giant mimic",place[3],m_feature "fountain" +# The demon of the swamp +MONSTER:'&',"Juiblex",(25,08) +# And a couple demons +MONSTER:'i',"lemure",(43,08) +MONSTER:'i',"lemure",(44,08) +MONSTER:'i',"lemure",(45,08) +# Some liquids and gems +OBJECT:'*',random,(43,06) +OBJECT:'*',random,(45,06) +OBJECT:'!',random,(43,09) +OBJECT:'!',random,(44,09) +OBJECT:'!',random,(45,09) +# And lots of blobby monsters +MONSTER:monster[0],random,(25,06) +MONSTER:monster[1],random,(24,07) +MONSTER:monster[2],random,(26,07) +MONSTER:monster[3],random,(23,08) +MONSTER:monster[3],random,(27,08) +MONSTER:monster[2],random,(24,09) +MONSTER:monster[1],random,(26,09) +MONSTER:monster[0],random,(25,10) +MONSTER:'j',random,random +MONSTER:'j',random,random +MONSTER:'j',random,random +MONSTER:'j',random,random +MONSTER:'P',random,random +MONSTER:'P',random,random +MONSTER:'P',random,random +MONSTER:'P',random,random +MONSTER:'b',random,random +MONSTER:'b',random,random +MONSTER:'b',random,random +MONSTER:'F',random,random +MONSTER:'F',random,random +MONSTER:'F',random,random +MONSTER:'m',random,random +MONSTER:'m',random,random +MONSTER:';',"jellyfish",random +MONSTER:';',"jellyfish",random +# Some random objects +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'`',"boulder",random +# Some traps +TRAP:"sleep gas",random +TRAP:"sleep gas",random +TRAP:"anti magic",random +TRAP:"anti magic",random +TRAP:"magic",random +TRAP:"magic",random +# +# The Orcus Level +# +MAZE:"orcus",random +FLAGS: noteleport,shortsighted +GEOMETRY:right,center +# A ghost town +MAP +.|....|....|....|..............|....|........ +.|....|....|....|..............|....|........ +.|....|....|....|--...-+-------|............. +.|....|....|....|..............+............. +.|.........|....|..............|....|........ +.--+-...-+----+--....-------...--------.-+--- +.....................|.....|................. +.....................|.....|................. +.--+----....-+---....|.....|...----------+--- +.|....|....|....|....---+---...|......|...... +.|.........|....|..............|......|...... +.----...---------.....-----....+......|...... +.|........................|....|......|...... +.----------+-...--+--|....|....----------+--- +.|....|..............|....+....|............. +.|....+.......|......|....|....|............. +.|....|.......|......|....|....|............. +ENDMAP +MAZEWALK:(00,06),west +# Entire main area +REGION:(01,00,44,16),unlit,"ordinary" +STAIR:(33,15),down +STAIR:levregion(01,00,12,20),levregion(20,01,70,20),up +BRANCH:levregion(01,00,12,20),levregion(20,01,70,20) +TELEPORT_REGION:levregion(01,00,12,20),levregion(20,01,70,20) +# Wall "ruins" +OBJECT:'`',"boulder",(19,02) +OBJECT:'`',"boulder",(20,02) +OBJECT:'`',"boulder",(21,02) +OBJECT:'`',"boulder",(36,02) +OBJECT:'`',"boulder",(36,03) +OBJECT:'`',"boulder",(06,04) +OBJECT:'`',"boulder",(05,05) +OBJECT:'`',"boulder",(06,05) +OBJECT:'`',"boulder",(07,05) +OBJECT:'`',"boulder",(39,05) +OBJECT:'`',"boulder",(08,08) +OBJECT:'`',"boulder",(09,08) +OBJECT:'`',"boulder",(10,08) +OBJECT:'`',"boulder",(11,08) +OBJECT:'`',"boulder",(06,10) +OBJECT:'`',"boulder",(05,11) +OBJECT:'`',"boulder",(06,11) +OBJECT:'`',"boulder",(07,11) +OBJECT:'`',"boulder",(21,11) +OBJECT:'`',"boulder",(21,12) +OBJECT:'`',"boulder",(13,13) +OBJECT:'`',"boulder",(14,13) +OBJECT:'`',"boulder",(15,13) +OBJECT:'`',"boulder",(14,14) +# Doors +DOOR:closed,(23,02) +DOOR:open,(31,03) +DOOR:nodoor,(03,05) +DOOR:closed,(09,05) +DOOR:closed,(14,05) +DOOR:closed,(41,05) +DOOR:open,(03,08) +DOOR:nodoor,(13,08) +DOOR:open,(41,08) +DOOR:closed,(24,09) +DOOR:closed,(31,11) +DOOR:open,(11,13) +DOOR:closed,(18,13) +DOOR:closed,(41,13) +DOOR:open,(26,14) +DOOR:closed,(06,15) +# Special rooms +ALTAR:(24,07),noalign,sanctum +REGION:(22,12,25,16),unlit,"morgue" +REGION:(32,09,37,12),lit,"shop" +REGION:(12,00,15,04),lit,"shop" +# Some traps. +TRAP:"spiked pit", random +TRAP:"sleep gas", random +TRAP:"anti magic", random +TRAP:"fire", random +TRAP:"fire", random +TRAP:"fire", random +TRAP:"magic", random +TRAP:"magic", random +# Some random objects +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# The resident nasty +MONSTER:'&',"Orcus",(33,15) +# And its preferred companions +MONSTER:'Z',"human zombie",(32,15) +MONSTER:' ',"shade",(32,14) +MONSTER:' ',"shade",(32,16) +MONSTER:'V',"vampire",(35,16) +MONSTER:'V',"vampire",(35,14) +MONSTER:'V',"vampire lord",(36,14) +MONSTER:'V',"vampire lord",(36,15) +# Randomly placed companions +MONSTER:'Z',"skeleton",random +MONSTER:'Z',"skeleton",random +MONSTER:'Z',"skeleton",random +MONSTER:'Z',"skeleton",random +MONSTER:'Z',"skeleton",random +MONSTER:' ',"shade",random +MONSTER:' ',"shade",random +MONSTER:' ',"shade",random +MONSTER:' ',"shade",random +MONSTER:'Z',"giant zombie",random +MONSTER:'Z',"giant zombie",random +MONSTER:'Z',"giant zombie",random +MONSTER:'Z',"ettin zombie",random +MONSTER:'Z',"ettin zombie",random +MONSTER:'Z',"ettin zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'Z',"human zombie",random +MONSTER:'V',"vampire",random +MONSTER:'V',"vampire",random +MONSTER:'V',"vampire",random +MONSTER:'V',"vampire lord",random +MONSTER:'V',"vampire lord",random +# A few more for the party +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +# +# The Asmodeus Level +# +MAZE:"asmodeus",random +FLAGS: noteleport +# First part +GEOMETRY:half-left,center +MAP +--------------------- +|.............|.....| +|.............S.....| +|---+------------...| +|.....|.........|-+-- +|..---|.........|.... +|..|..S.........|.... +|..|..|.........|.... +|..|..|.........|-+-- +|..|..-----------...| +|..S..........|.....| +--------------------- +ENDMAP +STAIR:levregion(01,00,6,20),levregion(6,1,70,16),up +BRANCH:levregion(01,00,6,20),levregion(6,1,70,16) +TELEPORT_REGION:levregion(01,00,6,20),levregion(6,1,70,16) + +# Doors +DOOR:closed,(04,03) +DOOR:locked,(18,04) +DOOR:closed,(18,08) +# +STAIR:(13,07),down +# Non diggable walls +NON_DIGGABLE:(00,00,20,11) +# Entire main area +REGION:(01,01,20,10),unlit,"ordinary" +# The fellow in residence +MONSTER:'&',"Asmodeus",(12,07) +# Some random weapons and armor. +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:')',random,random +OBJECT:')',random,random +OBJECT:'*',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +# Some traps. +TRAP:"spiked pit", (05,02) +TRAP:"fire", (08,06) +TRAP:"sleep gas", random +TRAP:"anti magic", random +TRAP:"fire", random +TRAP:"magic", random +TRAP:"magic", random +# Random monsters. +MONSTER:' ',"ghost",(11,07) +MONSTER:'&',"horned devil",(10,05) +MONSTER:'L',random,random +# Some Vampires for good measure +MONSTER:'V',random,random +MONSTER:'V',random,random +MONSTER:'V',random,random +# Second part +GEOMETRY:half-right,center +MAP +--------------------------------- +................................| +................................+ +................................| +--------------------------------- +ENDMAP +MAZEWALK:(32,02),east +# Non diggable walls +NON_DIGGABLE:(00,00,32,04) +DOOR:closed,(32,02) +MONSTER:'&',random,random +MONSTER:'&',random,random +MONSTER:'&',random,random +TRAP:"anti magic", random +TRAP:"fire", random +TRAP:"magic", random + +# +# The Baalzebub level +# +MAZE:"baalz",random +FLAGS: noteleport +GEOMETRY:right,center +MAP +------------------------------------------------- +| --- ---- +| ---- | ------------ | +| ------ | --------|..........|--- +| |....| -------|...........-------------- +---....|--|..................S............|---- +....--....S..----------------|............S...| +---....|--|..................|............|---- +| |....| -------|...........-----S-------- +| ------ | --------|..........|--- +| ---- | ------------ | +| --- ---- +------------------------------------------------- +ENDMAP +STAIR:levregion(01,00,15,20),levregion(15,1,70,16),up +BRANCH:levregion(01,00,15,20),levregion(15,1,70,16) +TELEPORT_REGION:levregion(01,00,15,20),levregion(15,1,70,16) +NON_DIGGABLE:(00,00,46,12) +MAZEWALK:(00,06),west +STAIR:(44,06),down +# The fellow in residence +MONSTER:'&',"Baalzebub",(35,06) +# Some random weapons and armor. +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:')',random,random +OBJECT:')',random,random +OBJECT:'*',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +# Some traps. +TRAP:"spiked pit", random +TRAP:"fire", random +TRAP:"sleep gas", random +TRAP:"anti magic", random +TRAP:"fire", random +TRAP:"magic", random +TRAP:"magic", random +# Random monsters. +MONSTER:' ',"ghost",(37,07) +MONSTER:'&',"horned devil",(32,05) +MONSTER:'&',"barbed devil",(38,07) +MONSTER:'L',random,random +# Some Vampires for good measure +MONSTER:'V',random,random +MONSTER:'V',random,random +MONSTER:'V',random,random +# +# The Sanctum Level +# +MAZE:"sanctum", ' ' +FLAGS: noteleport,hardfloor,nommap +GEOMETRY:center,center +MAP +---------------------------------------------------------------------------- +| -------------- | +| |............| ------- | +| -------............----- |.....| | +| |......................| --.....| --------- | +| ----......................---------|......---- |.......| | +| |........---------..........|......+.........| ------+---..| | +| ---........|.......|..........--S----|.........| |........|..| | +| |..........|.......|.............| |.........-------..---------- | +| |..........|.......|..........---- |..........|....|..|......| | +| |..........|.......|..........| --.......----+---S---S--..| | +| |..........---------..........| |.......|.............|..| | +| ---...........................| -----+-------S---------S--- | +| |...........................| |...| |......| |....|-- | +| ----.....................---- |...---....--- ---......| | +| |.....................| |..........| |.....---- | +| -------...........----- --...------- |.....| | +| |...........| |...| |.....| | +| ------------- ----- ------- | +---------------------------------------------------------------------------- +ENDMAP +REGION:(15,07,21,10),lit,"temple" +ALTAR:(18,08),noalign,sanctum +REGION:(41,06,48,11),unlit,"morgue",filled,true +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Invisible barrier separating the left & right halves of the level +NON_PASSWALL:(37,00,39,19) +# Doors +DOOR:closed,(40,06) +DOOR:locked,(62,06) +DOOR:closed,(46,12) +DOOR:closed,(53,10) +# Surround the temple with fire +TRAP:"fire",(13,05) +TRAP:"fire",(14,05) +TRAP:"fire",(15,05) +TRAP:"fire",(16,05) +TRAP:"fire",(17,05) +TRAP:"fire",(18,05) +TRAP:"fire",(19,05) +TRAP:"fire",(20,05) +TRAP:"fire",(21,05) +TRAP:"fire",(22,05) +TRAP:"fire",(23,05) +TRAP:"fire",(13,12) +TRAP:"fire",(14,12) +TRAP:"fire",(15,12) +TRAP:"fire",(16,12) +TRAP:"fire",(17,12) +TRAP:"fire",(18,12) +TRAP:"fire",(19,12) +TRAP:"fire",(20,12) +TRAP:"fire",(21,12) +TRAP:"fire",(22,12) +TRAP:"fire",(23,12) +TRAP:"fire",(13,06) +TRAP:"fire",(13,07) +TRAP:"fire",(13,08) +TRAP:"fire",(13,09) +TRAP:"fire",(13,10) +TRAP:"fire",(13,11) +TRAP:"fire",(23,06) +TRAP:"fire",(23,07) +TRAP:"fire",(23,08) +TRAP:"fire",(23,09) +TRAP:"fire",(23,10) +TRAP:"fire",(23,11) +# Some traps. +TRAP:"spiked pit", random +TRAP:"fire", random +TRAP:"sleep gas", random +TRAP:"anti magic", random +TRAP:"fire", random +TRAP:"magic", random +# Some random objects +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:'[',random,random +OBJECT:')',random,random +OBJECT:')',random,random +OBJECT:'*',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +# Some monsters. +MONSTER:'&',"horned devil",(14,12),hostile +MONSTER:'&',"barbed devil",(18,08),hostile +MONSTER:'&',"erinys",(10,04),hostile +MONSTER:'&',"marilith",(07,09),hostile +MONSTER:'&',"nalfeshnee",(27,08),hostile +# Moloch's horde +MONSTER:'@',"aligned priest",(20,03),noalign,hostile +MONSTER:'@',"aligned priest",(15,04),noalign,hostile +MONSTER:'@',"aligned priest",(11,05),noalign,hostile +MONSTER:'@',"aligned priest",(11,07),noalign,hostile +MONSTER:'@',"aligned priest",(11,09),noalign,hostile +MONSTER:'@',"aligned priest",(11,12),noalign,hostile +MONSTER:'@',"aligned priest",(15,13),noalign,hostile +MONSTER:'@',"aligned priest",(17,13),noalign,hostile +MONSTER:'@',"aligned priest",(21,13),noalign,hostile +# A few nasties +MONSTER:'L',random,random +MONSTER:'L',random,random +MONSTER:'V',random,random +MONSTER:'V',random,random +MONSTER:'V',random,random +STAIR:(63,15),up diff --git a/dat/help b/dat/help new file mode 100644 index 0000000..00abe24 --- /dev/null +++ b/dat/help @@ -0,0 +1,198 @@ + Welcome to NetHack! ( description of version 3.4 ) + + NetHack is a Dungeons and Dragons like game where you (the adventurer) +descend into the depths of the dungeon in search of the Amulet of Yendor, +reputed to be hidden somewhere below the twentieth level. You begin your +adventure with a pet that can help you in many ways, and can be trained +to do all sorts of things. On the way you will find useful (or useless) +items, quite possibly with magic properties, and assorted monsters. You can +attack a monster by trying to move onto the space a monster is on (but often +it is much wiser to leave it alone). + + Unlike most adventure games, which give you a verbal description of +your location, NetHack gives you a visual image of the dungeon level you are +on. + + NetHack uses the following symbols: + + - and | The walls of a room, possibly also open doors or a grave. + . The floor of a room or a doorway. + # A corridor, or iron bars, or a tree, or possibly a kitchen + sink (if your dungeon has sinks), or a drawbridge. + > Stairs down: a way to the next level. + < Stairs up: a way to the previous level. + @ You (usually), or another human. + ) A weapon of some sort. + [ A suit or piece of armor. + % Something edible (not necessarily healthy). + / A wand. + = A ring. + ? A scroll. + ! A potion. + ( Some other useful object (pick-axe, key, lamp...) + $ A pile of gold. + * A gem or rock (possibly valuable, possibly worthless). + + A closed door, or a spellbook containing a spell + you can learn. + ^ A trap (once you detect it). + " An amulet, or a spider web. + 0 An iron ball. + _ An altar, or an iron chain. + { A fountain. + } A pool of water or moat or a pool of lava. + \ An opulent throne. + ` A boulder or statue. + A to Z, a to z, and several others: Monsters. + I Invisible or unseen monster's last known location + + You can find out what a symbol represents by typing + '/' and following the directions to move the cursor + to the symbol in question. For instance, a 'd' may + turn out to be a dog. + + +y k u 7 8 9 Move commands: + \|/ \|/ yuhjklbn: go one step in specified direction +h-.-l 4-.-6 YUHJKLBN: go in specified direction until you + /|\ /|\ hit a wall or run into something +b j n 1 2 3 g: run in direction until something + numberpad interesting is seen + G, same, except a branching corridor isn't + < up ^: considered interesting (the ^ in this + case means the Control key, not a caret) + > down m: move without picking up objects + F: fight even if you don't sense a monster + If the number_pad option is set, the number keys move instead. + Depending on the platform, Shift number (on the numberpad), + Meta number, or Alt number will invoke the YUHJKLBN commands. + Control may or may not work when number_pad is enabled, + depending on the platform's capabilities. + +Commands: + NetHack knows the following commands: + ? Help menu. + / Tell what a symbol represents. You may choose to specify + a location or give a symbol argument. + & Tell what a command does. + < Go up a staircase (if you are standing on it). + > Go down a staircase (if you are standing on it). + . Rest, do nothing for one turn. + _ Travel via a shortest-path algorithm to a point on the map + a Apply (use) a tool (pick-axe, key, lamp...) + A Remove all armor. + ^A Redo the previous command + c Close a door. + C Call (name) an individual monster. + d Drop something. d7a: drop seven items of object a. + D Drop multiple items. This command is implemented in two + different ways. One way is: + "D" displays a list of all of your items, from which you can + pick and choose what to drop. A "+" next to an item means + that it will be dropped, a "-" means that it will not be + dropped. Toggle an item to be selected/deselected by typing + the letter adjacent to its description. Select all items + with "+", deselect all items with "=". The moves + you from one page of the listing to the next. + The other way is: + "D" will ask the question "What kinds of things do you want + to drop? [!%= au]". You should type zero or more object + symbols possibly followed by 'a' and/or 'u'. + Da - drop all objects, without asking for confirmation. + Du - drop only unpaid objects (when in a shop). + D%u - drop only unpaid food. + ^D Kick (for doors, usually). + e Eat food. + E Engrave a message on the floor. + E- - write in the dust with your fingers. + f Fire ammunition from quiver. + F Followed by direction, fight a monster (even if you don't + sense it). + i Display your inventory. + I Display selected parts of your inventory, as in + I* - list all gems in inventory. + Iu - list all unpaid items. + Ix - list all used up items that are on your shopping bill. + I$ - count your money. + o Open a door. + O Review current options and possibly change them. + A menu displaying the option settings will be displayed + and most can be changed by simply selecting their entry. + Options are usually set before the game with a NETHACKOPTIONS + environment variable, or via a config file (defaults.nh, + NetHack Defaults, nethack.cnf, .nethackrc, etc.), not with + the 'O' command. + p Pay your shopping bill. + P Put on an accessory (ring, amulet, etc). + ^P Repeat last message (subsequent ^P's repeat earlier messages). + The behavior can be varied via the msg_window option. + q Drink (quaff) something (potion, water, etc). + Q Select ammunition for quiver. + r Read a scroll or spellbook. + R Remove an accessory (ring, amulet, etc). + ^R Redraw the screen. + s Search for secret doors and traps around you. + S Save the game. + t Throw an object or shoot a projectile. + T Take off armor. + ^T Teleport, if you are able. + v Displays the version number. + V Display a longer identification of the version, including the + history of the game. + w Wield weapon. w- means wield nothing, use bare hands. + W Wear armor. + x Swap wielded and secondary weapons. + X Switch the game to explore (discovery) mode. + ^X Show your attributes. + z Zap a wand. + Z Cast a spell. + ^Z Suspend the game. + : Look at what is here. + ; Look at what is somewhere else. + , Pick up some things. + @ Toggle the pickup option. + ^ Ask for the type of a trap you found earlier. + ) Tell what weapon you are wielding. + [ Tell what armor you are wearing. + = Tell what rings you are wearing. + " Tell what amulet you are wearing. + ( Tell what tools you are using. + * Tell what equipment you are using; combines the preceding five. + $ Count your gold pieces. + + List the spells you know; also rearrange them if desired. + \ Show what types of objects have been discovered. + ! Escape to a shell, if supported in your version and OS. + # Introduces one of the "extended" commands. To get a list of + the commands you can use with "#" type "#?". The extended + commands you can use depends upon what options the game was + compiled with, along with your class and what type of monster + you most closely resemble at a given moment. If your keyboard + has a meta key (which, when pressed in combination with another + key, modifies it by setting the 'meta' (8th, or 'high') bit), + these extended commands can be invoked by meta-ing the first + letter of the command. An alt key may have a similar effect. + + If the "number_pad" option is on, some additional letter commands + are available: + + h displays the help menu, like '?' + j Jump to another location. + k Kick (for doors, usually). + l Loot a box on the floor. + n followed by number of times to repeat the next command + N Name an object or type of object. + u Untrap a trapped object or door. + + You can put a number before a command to repeat it that many times, + as in "40." or "20s.". If you have the number_pad option set, you + must type 'n' to prefix the count, as in "n40." or "n20s". + + + Some information is displayed on the bottom line or perhaps in a + box, depending on the platform you are using. You see your + attributes, your alignment, what dungeon level you are on, how many + hit points you have now (and will have when fully recovered), what + your armor class is (the lower the better), your experience level, + and the state of your stomach. Optionally, you may or may not see + other information such as spell points, how much gold you have, etc. + + Have Fun, and Happy Hacking! diff --git a/dat/hh b/dat/hh new file mode 100644 index 0000000..e61f564 --- /dev/null +++ b/dat/hh @@ -0,0 +1,116 @@ +y k u 7 8 9 Move commands: + \|/ \|/ yuhjklbn: go one step in specified direction +h-.-l 4-.-6 YUHJKLBN: go in specified direction until you + /|\ /|\ hit a wall or run into something +b j n 1 2 3 g: run in direction until something + numberpad interesting is seen + G, same, except a branching corridor isn't + < up ^: considered interesting (the ^ in this + case means the Control key, not a caret) + > down m: move without picking up objects/fighting + F: fight even if you don't sense a monster + If the number_pad option is set, the number keys move instead. + Depending on the platform, Shift number (on the numberpad), + Meta number, or Alt number will invoke the YUHJKLBN commands. + Control may or may not work when number_pad is enabled, + depending on the platform's capabilities. + +General commands: +? help display one of several informative texts +#quit quit end the game without saving current game +S save save the game (to be continued later) and exit +! sh escape to some SHELL (if allowed) +^Z suspend suspend the game (independent of your current suspend char) +O options set options +/ whatis tell what a map symbol represents +\ known display list of what's been discovered +v version display version number +V history display game history +X explore switch the game to explore (discovery) mode +^A again redo the previous command (^A denotes the keystroke CTRL-A) +^R redraw redraw the screen +^P prevmsg repeat previous message (subsequent ^P's repeat earlier ones) +# introduces an extended command (#? for a list of them) + +Game commands: +^D kick kick (a door, or something else) +^T 'port teleport (if you can) +^X show show your attributes +a apply apply or use a tool (pick-axe, key, camera, etc.) +A armor take off all armor +c close close a door +C call name an individual monster (ex. baptize your dog) +d drop drop an object. d7a: drop seven items of object 'a' +D Drop drop selected types of objects +e eat eat something +E engrave write a message in the dust on the floor (E- use fingers) +f fire fire ammunition from quiver +F fight followed by direction, fight a monster +i invent list your inventory (all objects you are carrying) +I Invent list selected parts of your inventory + Iu: list unpaid objects + Ix: list unpaid but used up items + I$: count your money +o open open a door +p pay pay your bill (in a shop) +P puton put on an accessory (ring, amulet, etc) +q quaff drink something (potion, water, etc) +Q quiver select ammunition for quiver +r read read a scroll or spellbook +R remove remove an accessory (ring, amulet, etc) +s search search for secret doors, hidden traps and monsters +t throw throw or shoot a weapon +T takeoff take off some armor +w wield wield a weapon (w- wield nothing) +W wear put on some armor +x xchange swap wielded and secondary weapons +z zap zap a wand +Z Zap cast a spell +< up go up the stairs +> down go down the stairs +^ trap_id identify a previously found trap +),[,=,",( ask for current items of specified symbol in use +* ask for combination of ),[,=,",( all at once +$ gold count your gold ++ spells list the spells you know; also rearrange them if desired +_ travel move via a shortest-path algorithm to a point on the map +. rest wait a moment +, pickup pick up all you can carry +@ toggle "pickup" (auto pickup) option on and off +: look look at what is here +; farlook look at what is somewhere else by selecting a map symbol + +Keyboards that have a meta key can also use these extended commands +via the meta modifier instead of the # prefix: + +M-? Display extended command help (if the platform allows this) +M-2 twoweapon toggle two-weapon combat (unless number_pad is enabled) +M-a adjust adjust inventory letters +M-c chat talk to someone +M-d dip dip an object into something +M-e enhance advance or check weapons skills +M-f force force a lock +M-i invoke invoke an object's special powers +M-j jump jump to another location +M-l loot loot a box on the floor +M-m monster use a monster's special ability +M-n name name an item or type of object +M-o offer offer a sacrifice to the gods +M-p pray pray to the gods for help +M-q quit stop playing +M-r rub rub a lamp or a stone +M-s sit sit down +M-t turn turn undead +M-u untrap untrap something +M-v version print compile time options for this version +M-w wipe wipe off your face + +If the "number_pad" option is on, these additional variants are available: + +n followed by number of times to repeat the next command +h help display one of several informative texts, like '?' +j jump jump to another location +k kick kick something (usually a door) +l loot loot a box on the floor +N name name an item or type of object +u untrap untrap something (usually a trapped object) diff --git a/dat/history b/dat/history new file mode 100644 index 0000000..0f6ef01 --- /dev/null +++ b/dat/history @@ -0,0 +1,199 @@ +NetHack History file for release 3.4 + +Behold, mortal, the origins of NetHack... + +Jay Fenlason wrote the original Hack with help from Kenny Woodland, +Mike Thome, and Jon Payne. + +Andries Brouwer did a major re-write, transforming Hack into a very different +game, and published (at least) three versions (1.0.1, 1.0.2, and 1.0.3) for +UNIX(tm) machines to the Usenet. + +Don G. Kneller ported Hack 1.0.3 to Microsoft(tm) C and MS-DOS(tm), producing +PC HACK 1.01e, added support for DEC Rainbow graphics in version 1.03g, and +went on to produce at least four more versions (3.0, 3.2, 3.51, and 3.6). + +R. Black ported PC HACK 3.51 to Lattice(tm) C and the Atari 520/1040ST, +producing ST Hack 1.03. + +Mike Stephenson merged these various versions back together, incorporating +many of the added features, and produced NetHack version 1.4. He then +coordinated a cast of thousands in enhancing and debugging NetHack 1.4 and +released NetHack versions 2.2 and 2.3. + +Later, Mike coordinated a major rewrite of the game, heading a team which +included Ken Arromdee, Jean-Christophe Collet, Steve Creps, Eric Hendrickson, +Izchak Miller, Eric S. Raymond, John Rupley, Mike Threepoint, and Janet Walz, +to produce NetHack 3.0c. The same group subsequently released ten patch- +level revisions and updates of 3.0. + +NetHack 3.0 was ported to the Atari by Eric R. Smith, to OS/2 by Timo +Hakulinen, and to VMS by David Gentzel. The three of them and Kevin Darcy +later joined the main development team to produce subsequent revisions of +3.0. + +Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm Meluch, Stephen +Spackman and Pierre Martineau designed overlay code for PC NetHack 3.0. +Johnny Lee ported NetHack 3.0 to the Macintosh. Along with various other +Dungeoneers, they continued to enhance the PC, Macintosh, and Amiga ports +through the later revisions of 3.0. + +Headed by Mike Stephenson and coordinated by Izchak Miller and Janet Walz, +the development team which now included Ken Arromdee, David Cohrs, +Jean-Christophe Collet, Kevin Darcy, Matt Day, Timo Hakulinen, Steve Linhart, +Dean Luick, Pat Rankin, Eric Raymond, and Eric Smith undertook a radical +revision of 3.0. They re-structured the game's design, and re-wrote major +parts of the code. They added multiple dungeons, a new display, special +individual character quests, a new endgame and many other new features, and +produced NetHack 3.1. + +Ken Lorber, Gregg Wonderly and Greg Olson, with help from Richard Addison, +Mike Passaretti, and Olaf Seibert, developed NetHack 3.1 for the Amiga. + +Norm Meluch and Kevin Smolkowski, with help from Carl Schelin, Stephen +Spackman, Steve VanDevender, and Paul Winner, ported NetHack 3.1 to the PC. + +Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike Engber, David +Hairston, Michael Hamel, Jonathan Handler, Johnny Lee, Tim Lennan, Rob Menke, +and Andy Swanson developed NetHack 3.1 for the Macintosh, porting it for +MPW. Building on their development, Barton House added a Think C port. + +Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith ported NetHack 3.1 +to the Atari. Pat Rankin, with help from Joshua Delahunty, is responsible +for the VMS version of NetHack 3.1. Michael Allison ported NetHack 3.1 to +Windows NT. + +Dean Luick, with help from David Cohrs, developed NetHack 3.1 for X11. +Warwick Allison wrote a tiled version of NetHack for the Atari; +he later contributed the tiles to the DevTeam and tile support was +then added to other platforms. + +The 3.2 development team, comprised of Michael Allison, Ken Arromdee, David +Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, Steve +Linhart, Dean Luick, Pat Rankin, Eric Smith, Mike Stephenson, Janet Walz, and +Paul Winner, released version 3.2 in April of 1996. + +Version 3.2 marked the tenth anniversary of the formation of the development +team. In a testament to their dedication to the game, all thirteen members +of the original development team remained on the team at the start of work +on that release. During the interval between the release of 3.1.3 and 3.2, +one of the founding members of the development team, Dr. Izchak Miller, +passed away. That release of the game was dedicated to him by the +development and porting teams. + +Version 3.2 proved to be more stable than previous versions. Many bugs +were fixed, abuses eliminated, and game features tuned for better game +play. + +During the lifespan of NetHack 3.1 and 3.2, several enthusiasts of the game +added their own modifications to the game and made these "variants" publicly +available: + +Tom Proudfoot and Yuval Oren created NetHack++, which was quickly renamed +NetHack--. Working independently, Stephen White wrote NetHack Plus. +Tom Proudfoot later merged NetHack Plus and his own NetHack-- to produce +SLASH. Larry Stewart-Zerba and Warwick Allison improved the spellcasting +system with the Wizard Patch. Warwick Allison also ported NetHack to use +the Qt interface. + +Warren Cheung combined SLASH with the Wizard Patch to produce Slash'em, and +with the help of Kevin Hugo, added more features. Kevin later joined the +DevTeam and incorporated the best of these ideas in NetHack 3.3. + +The final update to 3.2 was the bug fix release 3.2.3, which was released +simultaneously with 3.3.0 in December 1999 just in time for the Year 2000. + +The 3.3 development team, consisting of Michael Allison, Ken Arromdee, +David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, Timo Hakulinen, +Kevin Hugo, Steve Linhart, Ken Lorber, Dean Luick, Pat Rankin, Eric Smith, +Mike Stephenson, Janet Walz, and Paul Winner, released 3.3.0 in +December 1999 and 3.3.1 in August of 2000. + +Version 3.3 offered many firsts. It was the first version to separate race +and profession. The Elf class was removed in preference to an elf race, +and the races of dwarves, gnomes, and orcs made their first appearance in +the game alongside the familiar human race. Monk and Ranger roles joined +Archeologists, Barbarians, Cavemen, Healers, Knights, Priests, Rogues, +Samurai, Tourists, Valkyries and of course, Wizards. It was also the first +version to allow you to ride a steed, and was the first version to have a +publicly available web-site listing all the bugs that had been discovered. +Despite that constantly growing bug list, 3.3 proved stable enough to last +for more than a year and a half. + + +The 3.4 development team initially consisted of Michael Allison, Ken Arromdee, +David Cohrs, Jessie Collet, Kevin Hugo, Ken Lorber, Dean Luick, Pat Rankin, +Mike Stephenson, Janet Walz, and Paul Winner, with Warwick Allison joining +just before the release of NetHack 3.4.0 in March 2002. + +As with version 3.3, various people contributed to the game as a whole as +well as supporting ports on the different platforms that NetHack runs on: + +Pat Rankin maintained 3.4 for VMS. + +Michael Allison maintained NetHack 3.4 for the MS-DOS platform. +Paul Winner and Yitzhak Sapir provided encouragement. + +Dean Luick, Mark Modrall, and Kevin Hugo maintained and enhanced the +Macintosh port of 3.4. + +Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, and Yitzhak Sapir +maintained and enhanced 3.4 for the Microsoft Windows platform. Alex Kompel +contributed a new graphical interface for the Windows port. Alex Kompel also +contributed a Windows CE port for 3.4.1. + +Ron Van Iwaarden maintained 3.4 for OS/2. + +Janne Salmijarvi and Teemu Suikki maintained and enhanced the +Amiga port of 3.4 after Janne Salmijarvi resurrected it for 3.3.1. + +Christian `Marvin' Bressler maintained 3.4 for the Atari after he +resurrected it for 3.3.1. + +There is a NetHack web site maintained by Ken Lorber at http://www.nethack.org/. + + - - - - - - - - - - + +From time to time, some depraved individual out there in netland sends a +particularly intriguing modification to help out with the game. The Gods of +the Dungeon sometimes make note of the names of the worst of these miscreants +in this, the list of Dungeoneers: + + Adam Aronow Izchak Miller Mike Stephenson + Alex Kompel J. Ali Harlow Norm Meluch + Andreas Dorn Janet Walz Olaf Seibert + Andy Church Janne Salmijarvi Pasi Kallinen + Andy Swanson Jean-Christophe Collet Pat Rankin + Ari Huttunen Jochen Erwied Paul Winner + Barton House John Kallen Pierre Martineau + Benson I. Margulies John Rupley Ralf Brown + Bill Dyer John S. Bien Ray Chason + Boudewijn Waijers Johnny Lee Richard Addison + Bruce Cox Jon W{tte Richard Beigel + Bruce Holloway Jonathan Handler Richard P. Hughey + Bruce Mewborne Joshua Delahunty Rob Menke + Carl Schelin Keizo Yamamoto Robin Johnson + Chris Russo Ken Arnold Roderick Schertler + David Cohrs Ken Arromdee Roland McGrath + David Damerell Ken Lorber Ron Van Iwaarden + David Gentzel Ken Washikita Ronnen Miller + David Hairston Kevin Darcy Ross Brown + Dean Luick Kevin Hugo Sascha Wostmann + Del Lamb Kevin Sitze Scott Bigham + Deron Meranda Kevin Smolkowski Scott R. Turner + Dion Nicolaas Kevin Sweet Stephen Spackman + Dylan O'Donnell Lars Huttar Stephen White + Eric Backus Malcolm Ryan Steve Creps + Eric Hendrickson Mark Gooderum Steve Linhart + Eric R. Smith Mark Modrall Steve VanDevender + Eric S. Raymond Marvin Bressler Teemu Suikki + Erik Andersen Matthew Day Tim Lennan + Frederick Roeber Merlyn LeRoy Timo Hakulinen + Gil Neiger Michael Allison Tom Almy + Greg Laskin Michael Feir Tom West + Greg Olson Michael Hamel Warren Cheung + Gregg Wonderly Michael Sokolov Warwick Allison + Hao-yang Wang Mike Engber Yitzhak Sapir + Helge Hafting Mike Gallop + Irina Rempt-Drijfhout Mike Passaretti + diff --git a/dat/knox.des b/dat/knox.des new file mode 100644 index 0000000..8ff123a --- /dev/null +++ b/dat/knox.des @@ -0,0 +1,104 @@ +# SCCS Id: @(#)knox.des 3.4 1994/08/20 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1992 by Izchak Miller +# NetHack may be freely redistributed. See license for details. +# +MAZE:"knox",' ' +FLAGS: noteleport +GEOMETRY:center,center +MAP +---------------------------------------------------------------------------- +| |........|...............................................................| +| |........|.................................................------------..| +| -------+--.................................................|..........|..| +| |........}}}}}}}....................}}}}}}}..........|..........|..| +| |........}-----}....................}-----}..........--+--+--...|..| +| ---........}|...|}}}}}}}}}}}}}}}}}}}}}}|...|}.................|...|..| +| |..........}---S------------------------S---}.................|...|..| +| |..........}}}|...............|..........|}}}.................+...|..| +| -------..........}|...............S..........|}...................|...|..| +| |.....|..........}|...............|......\...S}...................|...|..| +| |.....+........}}}|...............|..........|}}}.................+...|..| +| |.....|........}---S------------------------S---}.................|...|..| +| |.....|........}|...|}}}}}}}}}}}}}}}}}}}}}}|...|}.................|...|..| +| |..-S----......}-----}....................}-----}..........--+--+--...|..| +| |..|....|......}}}}}}}....................}}}}}}}..........|..........|..| +| |..|....|..................................................|..........|..| +| -----------................................................------------..| +| |..............................................................| +---------------------------------------------------------------------------- +ENDMAP +# Non diggable walls +NON_DIGGABLE:(00,00,75,19) +# Portal arrival point +BRANCH:(08,16,08,16),(0,0,0,0) +# Throne room, with Croesus on the throne +REGION:(37,08,46,11),lit,"throne" +MONSTER:'@',"Croesus",(43,10),hostile +# The Vault +# Using unfilled morgue for +# identification in mkmaze.c +REGION:(21,08,35,11),lit,"morgue",unfilled +# Corner towers +REGION:(19,06,21,06),lit,"ordinary" +REGION:(46,06,48,06),lit,"ordinary" +REGION:(19,13,21,13),lit,"ordinary" +REGION:(46,13,48,13),lit,"ordinary" +# A welcoming committee +REGION:(03,10,07,13),lit,"zoo",filled,true +# arrival chamber; needs to be a real room to control migrating monsters, +# and `unfilled' is a kludge to force an ordinary room to remain a room +REGION:(06,15,09,16),unlit,"ordinary",unfilled +# Barracks +REGION:(62,03,71,04),lit,"barracks",filled,true +# Doors +DOOR:closed,(06,14) +DOOR:closed,(09,03) +DOOR:open,(63,05) +DOOR:open,(66,05) +DOOR:open,(68,08) +DOOR:locked,(08,11) +DOOR:open,(68,11) +DOOR:closed,(63,14) +DOOR:closed,(66,14) +# Soldiers guarding the fort +MONSTER:'@',"soldier",(12,14) +MONSTER:'@',"soldier",(12,13) +MONSTER:'@',"soldier",(11,10) +MONSTER:'@',"soldier",(13,02) +MONSTER:'@',"soldier",(14,03) +MONSTER:'@',"soldier",(20,02) +MONSTER:'@',"soldier",(30,02) +MONSTER:'@',"soldier",(40,02) +MONSTER:'@',"soldier",(30,16) +MONSTER:'@',"soldier",(32,16) +MONSTER:'@',"soldier",(40,16) +MONSTER:'@',"soldier",(54,16) +MONSTER:'@',"soldier",(54,14) +MONSTER:'@',"soldier",(54,13) +MONSTER:'@',"soldier",(57,10) +MONSTER:'@',"soldier",(57,09) +MONSTER:'@',"lieutenant",(15,08) +# Four dragons guarding each side +MONSTER:'D',random,(18,09) +MONSTER:'D',random,(49,10) +MONSTER:'D',random,(33,05) +MONSTER:'D',random,(33,14) +# Eels in the moat +MONSTER:';',"giant eel",(17,08) +MONSTER:';',"giant eel",(17,11) +MONSTER:';',"giant eel",(48,08) +MONSTER:';',"giant eel",(48,11) +# The corner rooms treasures +OBJECT:'*',"diamond",(19,06) +OBJECT:'*',"diamond",(20,06) +OBJECT:'*',"diamond",(21,06) +OBJECT:'*',"emerald",(19,13) +OBJECT:'*',"emerald",(20,13) +OBJECT:'*',"emerald",(21,13) +OBJECT:'*',"ruby",(46,06) +OBJECT:'*',"ruby",(47,06) +OBJECT:'*',"ruby",(48,06) +OBJECT:'*',"amethyst",(46,13) +OBJECT:'*',"amethyst",(47,13) +OBJECT:'*',"amethyst",(48,13) diff --git a/dat/license b/dat/license new file mode 100644 index 0000000..5ad7e34 --- /dev/null +++ b/dat/license @@ -0,0 +1,95 @@ + NETHACK GENERAL PUBLIC LICENSE + (Copyright 1989 M. Stephenson) + + (Based on the BISON general public license, + copyright 1988 Richard M. Stallman) + + Everyone is permitted to copy and distribute verbatim copies of this + license, but changing it is not allowed. You can also use this wording to + make the terms for other programs. + + The license agreements of most software companies keep you at the mercy of +those companies. By contrast, our general public license is intended to give +everyone the right to share NetHack. To make sure that you get the rights we +want you to have, we need to make restrictions that forbid anyone to deny you +these rights or to ask you to surrender the rights. Hence this license +agreement. + + Specifically, we want to make sure that you have the right to give away +copies of NetHack, that you receive source code or else can get it if you +want it, that you can change NetHack or use pieces of it in new free +programs, and that you know you can do these things. + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute copies +of NetHack, you must give the recipients all the rights that you have. You +must make sure that they, too, receive or can get the source code. And you +must tell them their rights. + + Also, for our own protection, we must make certain that everyone finds out +that there is no warranty for NetHack. If NetHack is modified by someone +else and passed on, we want its recipients to know that what they have is +not what we distributed. + + Therefore we (Mike Stephenson and other holders of NetHack copyrights) make +the following terms which say what you must do to be allowed to distribute or +change NetHack. + + + COPYING POLICIES + + 1. You may copy and distribute verbatim copies of NetHack source code as +you receive it, in any medium, provided that you keep intact the notices on +all files that refer to copyrights, to this License Agreement, and to the +absence of any warranty; and give any other recipients of the NetHack +program a copy of this License Agreement along with the program. + + 2. You may modify your copy or copies of NetHack or any portion of it, and +copy and distribute such modifications under the terms of Paragraph 1 above +(including distributing this License Agreement), provided that you also do the +following: + + a) cause the modified files to carry prominent notices stating that you + changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that in + whole or in part contains or is a derivative of NetHack or any part + thereof, to be licensed at no charge to all third parties on terms + identical to those contained in this License Agreement (except that you + may choose to grant more extensive warranty protection to some or all + third parties, at your option) + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty protection + in exchange for a fee. + + 3. You may copy and distribute NetHack (or a portion or derivative of it, +under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete machine-readable source code, which + must be distributed under the terms of Paragraphs 1 and 2 above; or, + + b) accompany it with full information as to how to obtain the complete + machine-readable source code from an appropriate archive site. (This + alternative is allowed only for noncommercial distribution.) + +For these purposes, complete source code means either the full source +distribution as originally released over Usenet or updated copies of the +files in this distribution used to create the object code or executable. + + 4. You may not copy, sublicense, distribute or transfer NetHack except as +expressly provided under this License Agreement. Any attempt otherwise to +copy, sublicense, distribute or transfer NetHack is void and your rights to +use the program under this License agreement shall be automatically +terminated. However, parties who have received computer software programs +from you with this License Agreement will not have their licenses terminated +so long as such parties remain in full compliance. + + +Stated plainly: You are permitted to modify NetHack, or otherwise use parts +of NetHack, provided that you comply with the conditions specified above; +in particular, your modified NetHack or program containing parts of NetHack +must remain freely available as provided in this License Agreement. In +other words, go ahead and share NetHack, but don't try to stop anyone else +from sharing it farther. diff --git a/dat/medusa.des b/dat/medusa.des new file mode 100644 index 0000000..6134470 --- /dev/null +++ b/dat/medusa.des @@ -0,0 +1,215 @@ +# SCCS Id: @(#)medusa.des 3.4 1996/05/11 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1990, 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# These are the Medusa's levels : +# + +MAZE:"medusa-1",' ' +FLAGS: noteleport +GEOMETRY:center,center +MAP +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}}.}}}}}..}}}}}......}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}....}}}...}}}}} +}...}}.....}}}}}....}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}...............} +}....}}}}}}}}}}....}}}..}}}}}}}}}}}.......}}}}}}}}}}}}}}}}..}}.....}}}...}} +}....}}}}}}}}.....}}}}..}}}}}}.................}}}}}}}}}}}.}}}}.....}}...}} +}....}}}}}}}}}}}}.}}}}.}}}}}}.-----------------.}}}}}}}}}}}}}}}}}.........} +}....}}}}}}}}}}}}}}}}}}.}}}...|...............S...}}}}}}}}}}}}}}}}}}}....}} +}.....}.}}....}}}}}}}}}.}}....--------+--------....}}}}}}..}}}}}}}}}}}...}} +}......}}}}..}}}}}}}}}}}}}........|.......|........}}}}}....}}}}}}}}}}}}}}} +}.....}}}}}}}}}}}}}}}}}}}}........|.......|........}}}}}...}}}}}}}}}.}}}}}} +}.....}}}}}}}}}}}}}}}}}}}}....--------+--------....}}}}}}.}.}}}}}}}}}}}}}}} +}......}}}}}}}}}}}}}}}}}}}}...S...............|...}}}}}}}}}}}}}}}}}.}}}}}}} +}.......}}}}}}}..}}}}}}}}}}}}.-----------------.}}}}}}}}}}}}}}}}}....}}}}}} +}........}}.}}....}}}}}}}}}}}}.................}}}}}..}}}}}}}}}.......}}}}} +}.......}}}}}}}......}}}}}}}}}}}}}}.......}}}}}}}}}.....}}}}}}...}}..}}}}}} +}.....}}}}}}}}}}}.....}}}}}}}}}}}}}}}}}}}}}}.}}}}}}}..}}}}}}}}}}....}}}}}}} +}}..}}}}}}}}}}}}}....}}}}}}}}}}}}}}}}}}}}}}...}}..}}}}}}}.}}.}}}}..}}}}}}}} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +ENDMAP +# Dungeon Description +# (must maintain one room definition; `unfilled' forces its room to be kept) +REGION:(00,00,74,19),lit,"ordinary" +REGION:(31,07,45,07),unlit,"ordinary" +REGION:(35,09,41,10),unlit,"ordinary",unfilled +REGION:(31,12,45,12),unlit,"ordinary" +# Teleport: down to up stairs island, up to Medusa's island +TELEPORT_REGION:(01,01,05,17),(0,0,0,0),down +TELEPORT_REGION:(26,04,50,15),(0,0,0,0),up +# Stairs +STAIR:(05,14),up +STAIR:(36,10),down +# Doors +DOOR:closed,(46,07) +DOOR:locked,(38,08) +DOOR:locked,(38,11) +DOOR:closed,(30,12) +# Branch, not allowed inside Medusa's building. +BRANCH:levregion(01,00,79,20),(30,06,46,13) +# Non diggable walls +NON_DIGGABLE:(30,06,46,13) +# Objects +CONTAINER:'`',"statue",(36,10),uncursed,"knight",3,"Perseus" +OBJECT[75%]:'[',"shield of reflection",contained,cursed,+0 +OBJECT[25%]:'[',"levitation boots",contained,random,+0 +OBJECT[50%]:')',"scimitar",contained,blessed,+2 +OBJECT[50%]:'(',"sack",contained +# These aren't really containers, but specifying CONTAINER forces them to be +# empty, since CONTAINERs contain only what is explicitly specified. +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +CONTAINER:'`',"statue",random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:"board",(38,07) +TRAP:"board",(38,12) +# Random monsters +MONSTER:'@',"Medusa",(36,10),asleep +MONSTER:';',"giant eel",(11,06) +MONSTER:';',"giant eel",(23,13) +MONSTER:';',"giant eel",(29,02) +MONSTER:';',"jellyfish",(02,02) +MONSTER:';',"jellyfish",(00,08) +MONSTER:';',"jellyfish",(04,18) +MONSTER:'T',"water troll",(51,03) +MONSTER:'T',"water troll",(64,11) +MONSTER:'S',random,(38,07) +MONSTER:'S',random,(38,12) +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random + +MAZE:"medusa-2",' ' +FLAGS: noteleport +GEOMETRY:center,center +MAP +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}------}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-------}}}}}}}}--------------} +}|....|}}}}}}}}}..}.}}..}}}}}}}}}}}}}..}}}}}}-.....--}}}}}}}|............|} +}|....|.}}}}}}}}}}}.}...}}..}}}}}}}}}}}}}}}}}---......}}}}}.|............|} +}S....|.}}}}}}---}}}}}}}}}}}}}}}}}}}}}}}}}}---...|..-}}}}}}.S..----------|} +}|....|.}}}}}}-...}}}}}}}}}.}}...}.}}}}.}}}......----}}}}}}.|............|} +}|....|.}}}}}}-....--}}}}}}}}}}}}}}}}}}}}}}----...--}}}}}}}.|..--------+-|} +}|....|.}}}}}}}......}}}}...}}}}}}.}}}}}}}}}}}---..---}}}}}.|..|..S...|..|} +}|....|.}}}}}}-....-}}}}}}}------}}}}}}}}}}}}}}-...|.-}}}}}.|..|..|...|..|} +}|....|.}}}}}}}}}---}}}}}}}........}}}}}}}}}}---.|....}}}}}.|..|..|...|..|} +}|....|.}}}}}}}}}}}}}}}}}}-....|...-}}}}}}}}--...----.}}}}}.|..|..|...|..|} +}|....|.}}}}}}..}}}}}}}}}}---..--------}}}}}-..---}}}}}}}}}.|..|..-------|} +}|...}|...}}}.}}}}}}...}}}}}--..........}}}}..--}}}}}}}}}}}.|..|.........|} +}|...}S...}}.}}}}}}}}}}}}}}}-..--------}}}}}}}}}}}}}}...}}}.|..--------..S} +}|...}|...}}}}}}}..}}}}}}----..|....-}}}}}}}}}}}}}}}}}..}}}.|............|} +}|....|}}}}}....}}}}..}}.-.......----}}......}}}}}}.......}}|............|} +}------}}}}}}}}}}}}}}}}}}---------}}}}}}}}}}}}}}}}}}}}}}}}}}--------------} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} +ENDMAP +# Dungeon Description +REGION:(00,00,74,19),lit,"ordinary" +REGION:(02,03,05,16),unlit,"ordinary" +REGION:(61,03,72,16),unlit,"ordinary",unfilled,true +REGION:(71,08,72,11),unlit,"ordinary" +REGION:(67,08,69,11),lit,"ordinary" +# Teleport: down to up stairs island, up to Medusa's island +TELEPORT_REGION:(02,03,05,16),(0,0,0,0),down +TELEPORT_REGION:(61,03,72,16),(0,0,0,0),up +# Stairs +STAIR:(04,09),up +STAIR:(68,10),down +# Doors +DOOR:locked,(71,07) +# Branch, not allowed on Medusa's island. +BRANCH:levregion(01,00,79,20),(59,01,73,17) +# Non diggable walls +NON_DIGGABLE:(01,02,06,17) +NON_DIGGABLE:(60,02,73,17) +# Objects +CONTAINER:'`',"statue",(68,10),uncursed,"knight",3,"Perseus" +OBJECT[25%]:'[',"shield of reflection",contained,cursed,+0 +OBJECT[75%]:'[',"levitation boots",contained,random,+0 +OBJECT[50%]:')',"scimitar",contained,blessed,+2 +OBJECT[50%]:'(',"sack",contained +CONTAINER:'`',"statue",(64,08) +CONTAINER:'`',"statue",(65,08) +CONTAINER:'`',"statue",(64,09) +CONTAINER:'`',"statue",(65,09) +CONTAINER:'`',"statue",(64,10) +CONTAINER:'`',"statue",(65,10) +CONTAINER:'`',"statue",(64,11) +CONTAINER:'`',"statue",(65,11) +OBJECT:'`',"boulder",(04,04) +OBJECT:'/',random,(52,09) +OBJECT:'`',"boulder",(52,09) +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Traps +TRAP:"magic",(03,12) +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Monsters. +MONSTER:'@',"Medusa",(68,10),asleep +MONSTER:'g',"gremlin",(02,14) +MONSTER:'H',"titan",(02,05) +MONSTER:';',"electric eel",(10,13) +MONSTER:';',"electric eel",(11,13) +MONSTER:';',"electric eel",(10,14) +MONSTER:';',"electric eel",(11,14) +MONSTER:';',"electric eel",(10,15) +MONSTER:';',"electric eel",(11,15) +MONSTER:';',"jellyfish",(01,01) +MONSTER:';',"jellyfish",(00,08) +MONSTER:';',"jellyfish",(04,19) +MONSTER:''',"stone golem",(64,08),asleep +MONSTER:''',"stone golem",(65,08),asleep +MONSTER:''',"stone golem",(64,09),asleep +MONSTER:''',"stone golem",(65,09),asleep +MONSTER:'S',"cobra",(64,10),asleep +MONSTER:'S',"cobra",(65,10),asleep +MONSTER:'A',random,(72,08) +MONSTER:'y',"yellow light",(72,11),asleep +MONSTER:random,random,(17,07) +MONSTER:random,random,(28,11) +MONSTER:random,random,(32,13) +MONSTER:random,random,(49,09) +MONSTER:random,random,(48,07) +MONSTER:random,random,(65,03) +MONSTER:random,random,(70,04) +MONSTER:random,random,(70,15) +MONSTER:random,random,(65,16) +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random diff --git a/dat/mines.des b/dat/mines.des new file mode 100644 index 0000000..81f3a02 --- /dev/null +++ b/dat/mines.des @@ -0,0 +1,1021 @@ +# SCCS Id: @(#)mines.des 3.4 2002/05/02 +# Copyright (c) 1989-95 by Jean-Christophe Collet +# Copyright (c) 1991-95 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# + +# The "fill" level for the mines. +# +# This level is used to fill out any levels not occupied by +# specific levels as defined below. +# +MAZE: "minefill" , ' ' +INIT_MAP: '.' , ' ' , true , true , random , true +NOMAP +# +STAIR: random, up +STAIR: random, down +# +OBJECT: '*', random, random +OBJECT: '*', random, random +OBJECT: '*', random, random +OBJECT: '(', random, random +OBJECT: random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +# +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome lord", random +MONSTER: 'h', "dwarf", random +MONSTER: 'h', "dwarf", random +MONSTER: 'G', random, random +MONSTER: 'G', random, random +MONSTER: 'h', random, random +# +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random +TRAP: random, random + + +# Minetown variant 1 +# "Frontier Town" +# +LEVEL: "minetn-1" + +ROOM: "ordinary" , lit, (3,3), (center,center), (31,15) +NAME: "town" +FOUNTAIN: (13, 7) +FOUNTAIN: (20, 7) + +# The Town Watch +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful + +SUBROOM: "shop" , lit, (2,2), (3,4), "town" +CHANCE: 90 +DOOR: false, closed, south, random + +SUBROOM: "tool shop", lit, (2,9), (3,4), "town" +CHANCE: 90 +DOOR: false, closed, north, random + +SUBROOM: "ordinary", unlit, (6,2), (3,4), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", lit, (6,9), (3,4), "town" +DOOR: false, closed, north, random + +SUBROOM: "food shop", lit, (10,2), (2,3), "town" +CHANCE: 90 +DOOR: false, closed, south, random + +SUBROOM: "candle shop", lit, (22,2), (3,3), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", unlit, (10,10), (2,3), "town" +DOOR: false, locked, east, random +MONSTER: 'G', "gnome", random + +SUBROOM: "ordinary", lit, (19,2), (2,3), "town" +DOOR: false, locked, west, random +MONSTER: 'G', "gnome", random + +SUBROOM: "temple", lit, (15,9), (4,4), "town" +DOOR: false, closed, north, random +ALTAR:(02,02),align[0],shrine +MONSTER: 'G', "gnomish wizard", random +MONSTER: 'G', "gnomish wizard", random + +SUBROOM: "ordinary", lit, (22,10), (2,3), "town" +DOOR: false, locked, west, random + +SUBROOM: "ordinary", lit, (26,2), (3,3), "town" +DOOR: false, closed, south, random +MONSTER: 'G', "gnome lord", random + +SUBROOM: "ordinary", unlit, (25,10), (4,3), "town" +DOOR: false, closed, north, random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, up + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +TRAP: random, random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random + +ROOM: "ordinary" , random, random, random, random +MONSTER: 'h', "dwarf", random + +ROOM: "ordinary" , random, random, random, random +TRAP: random, random +MONSTER: 'G', "gnome", random + +RANDOM_CORRIDORS + + +# Minetown variant 2 +# "Town Square" +# +LEVEL: "minetn-2" +ROOM: "ordinary" , lit, (3,3), (center,center), (31,15) +NAME: "town" +FOUNTAIN: (17, 5) +FOUNTAIN: (13, 8) + +# The Town Watch +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful + +SUBROOM: "ordinary", random, (2,0), (2,2), "town" +DOOR: false, closed, west, random + +SUBROOM: "ordinary", unlit, (5,0), (2,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (8,0), (2,2), "town" +DOOR: false, closed, east, random + +SUBROOM: "ordinary", lit, (16,0), (2,2), "town" +DOOR: false, closed, west, random + +SUBROOM: "ordinary", unlit, (19,0), (2,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (22,0), (2,2), "town" +DOOR: false, locked, south, random +MONSTER: 'G', "gnome", random + +SUBROOM: "ordinary", unlit, (25,0), (2,2), "town" +DOOR: false, closed, east, random + +SUBROOM: "ordinary", lit, (2,5), (2,2), "town" +DOOR: false, closed, north, random + +SUBROOM: "ordinary", lit, (5,5), (2,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (8,5), (2,2), "town" +DOOR: false, locked, north, random +MONSTER: 'G', "gnome", random + +SUBROOM: "shop" , lit, (2,10), (4,3), "town" +CHANCE: 90 +DOOR: false, closed, west, random + +SUBROOM: "tool shop", lit, (23,10), (4,3), "town" +CHANCE: 90 +DOOR: false, closed, east, random + +SUBROOM: "food shop", lit, (24,5), (3,4), "town" +CHANCE: 90 +DOOR: false, closed, north, random + +SUBROOM: "candle shop", lit, (11,10), (4,3), "town" +DOOR: false, closed, east, random + +SUBROOM: "ordinary", unlit, (7,10), (3,3), "town" +DOOR: false, locked, north, random +MONSTER: 'G', "gnome", random + +SUBROOM: "temple", lit, (19,5), (4,4), "town" +DOOR: false, closed, north, random +ALTAR:(02,02),align[0],shrine +MONSTER: 'G', "gnomish wizard", random +MONSTER: 'G', "gnomish wizard", random + +SUBROOM: "ordinary", lit, (18,10), (4,3), "town" +DOOR: false, locked, west, random +MONSTER: 'G', "gnome lord", random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, up + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +TRAP: random, random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random + +ROOM: "ordinary" , random, random, random, random +MONSTER: 'h', "dwarf", random + +ROOM: "ordinary" , random, random, random, random +TRAP: random, random +MONSTER: 'G', "gnome", random + +RANDOM_CORRIDORS + + +# Minetown variant 3 by Kelly Bailey +# "Alley Town" +# +LEVEL: "minetn-3" +ROOM: "ordinary",lit,(3,3),(center,center),(31,15) +NAME: "town" +FOUNTAIN:(01,06) +FOUNTAIN:(29,13) +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful + +SUBROOM:"ordinary",random,(2,2),(2,2),"town" +DOOR: false,closed,south,random + +SUBROOM:"tool shop",lit,(5,3),(2,3),"town" +CHANCE: 30 +DOOR: false,closed,south,random + +SUBROOM:"ordinary",random,(2,10),(2,3),"town" +DOOR: false, locked, north, random +MONSTER: 'G',random,random + +SUBROOM:"ordinary",random,(5,9),(2,2),"town" +DOOR: false,closed,north,random + +SUBROOM:"temple",lit,(10,2),(3,4),"town" +DOOR: false,closed,east,random +ALTAR:(1,1),align[0],shrine +MONSTER: 'G', "gnomish wizard", random +MONSTER: 'G', "gnomish wizard", random + +SUBROOM:"ordinary",random,(11,7),(2,2),"town" +DOOR: false,closed,west,random + +SUBROOM:"shop",lit,(10,10),(3,3),"town" +DOOR:false,closed,west,random + +SUBROOM:"ordinary",random,(14,8),(2,2),"town" +DOOR:false,locked,north,random +MONSTER: 'G',random,random + +SUBROOM:"ordinary",random,(14,11),(2,2),"town" +DOOR:false,closed,south,random + +SUBROOM:"tool shop",lit,(17,10),(3,3),"town" +CHANCE:40 +DOOR:false,closed,north,random + +SUBROOM:"ordinary",random,(21,11),(2,2),"town" +DOOR:false,locked,east,random +MONSTER:'G',random,random + +SUBROOM:"food shop",lit,(26,8),(3,2),"town" +CHANCE:90 +DOOR:false,closed,west,random + +SUBROOM:"ordinary",random,(16,2),(2,2),"town" +DOOR:false,closed,west,random + +SUBROOM:"ordinary",random,(19,2),(2,2),"town" +DOOR:false,closed,north,random + +SUBROOM:"wand shop",lit,(19,5),(3,2),"town" +CHANCE:30 +DOOR:false,closed,west,random + +SUBROOM: "candle shop",lit,(25,2),(3,3),"town" +DOOR:false,closed,south,random + +ROOM: "ordinary", random, random, random, random +STAIR: random, up + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +TRAP: random, random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random + +ROOM: "ordinary" , random, random, random, random +MONSTER: 'h', "dwarf", random + +ROOM: "ordinary" , random, random, random, random +TRAP: random, random +MONSTER: 'G', "gnome", random + +RANDOM_CORRIDORS + + +# Minetown variant 4 by Kelly Bailey +# "College Town" +# +LEVEL: "minetn-4" +ROOM: "ordinary",lit,(3,3),(center,center),(30,15) +NAME: "town" +FOUNTAIN:(08,07) +FOUNTAIN:(18,07) +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful + +SUBROOM:"book shop",lit,(4,2),(3,3),"town" +DOOR: false,closed,south,random + +SUBROOM:"ordinary",random,(8,2),(2,2),"town" +DOOR: false,closed,south,random + +SUBROOM:"temple",lit,(11,3),(5,4),"town" +DOOR: false,closed,south,random +ALTAR:(2,1),align[0],shrine +MONSTER: 'G', "gnomish wizard", random +MONSTER: 'G', "gnomish wizard", random + +SUBROOM:"ordinary",random,(19,2),(2,2),"town" +DOOR: false,closed,south,random +MONSTER: 'G', random, random + +SUBROOM:"candle shop",lit,(22,2),(3,3),"town" +DOOR:false,closed,south,random + +SUBROOM:"ordinary",random,(26,2),(2,2),"town" +DOOR:false,locked,east,random +MONSTER: 'G',random,random + +SUBROOM:"tool shop",lit,(4,10),(3,3),"town" +CHANCE:90 +DOOR:false,closed,north,random + +SUBROOM:"ordinary",random,(8,11),(2,2),"town" +DOOR:false,locked,south,random +MONSTER: 'k',"kobold shaman",random +MONSTER: 'k',"kobold shaman",random +MONSTER: 'f',"kitten",random +MONSTER: 'f',random,random + +SUBROOM:"food shop",lit,(11,11),(3,2),"town" +CHANCE:90 +DOOR:false,closed,east,random + +SUBROOM:"ordinary",random,(17,11),(2,2),"town" +DOOR:false,closed,west,random + +SUBROOM:"ordinary",random,(20,10),(2,2),"town" +DOOR:false,locked,north,random +MONSTER:'G',random,random + +SUBROOM:"shop",lit,(23,10),(3,3),"town" +CHANCE:90 +DOOR:false,closed,north,random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, up + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +TRAP: random, random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random + +ROOM: "ordinary" , random, random, random, random +MONSTER: 'h', "dwarf", random + +ROOM: "ordinary" , random, random, random, random +TRAP: random, random +MONSTER: 'G', "gnome", random + +RANDOM_CORRIDORS + + +# "Grotto Town" by Kelly Bailey +# +MAZE: "minetn-5",' ' +GEOMETRY:center,center +MAP +----- --------- +|...--- ------.......-- ------- --------------- +|.....----.........--..| |.....| ------- |.............| +--..-....-.----------..| |.....| |.....| --+---+--.----+- + --.--.....---- ---- |.....| ------ --....---- |..-...--.-.+..| + ---.........---- ----- ---+--- |..+.| ---..-..----..---+-..---..| + ----.-....|..----...-- |.| |..|.| ---+-.....-+--........--+- + -----..|....-.....---- |.| |..|.------......--................| + ------ |..|.............---.-- ----.+..|-.......--..--------+--..-- + |....| --......---...........----- |.|..|-...{....---|.........|..-- + |....| |........-...-...........----.|..|--.......| |.........|...| + ---+--------....-------...---......--.-------....---- -----------...| + ------.---...--...--..-..--...-..---...|.--..-...-....------- |.......-- + |..|-.........-..---..-..---.....--....|........---...-|....| |.------- + |..+...............-+---+-----..--..........--....--...+....| |.|...S. +-----.....{....----...............-...........--...-...-|....| |.|...| +|..............-- --+--.---------.........--..-........------- |.--+------- +-+-----.........| |...|.|....| --.......------...|....---------.....|....| +|...| --..------- |...|.+....| ---...--- --..|...--......-...{..+..-+| +|...| ---- ------|....| ----- -----.....----........|..|.| +----- ------ ------- --------------- +ENDMAP + +STAIR:(01,01),up +STAIR:(46,03),down +FOUNTAIN:(50,09) +FOUNTAIN:(10,15) +FOUNTAIN:(66,18) + +REGION:(00,00,74,20),unlit,"ordinary" +REGION:(09,13,11,17),lit,"ordinary" +REGION:(08,14,12,16),lit,"ordinary" +REGION:(49,07,51,11),lit,"ordinary" +REGION:(48,08,52,10),lit,"ordinary" +REGION:(64,17,68,19),lit,"ordinary" +REGION:(37,13,39,17),lit,"ordinary" +REGION:(36,14,40,17),lit,"ordinary" +REGION:(59,02,72,10),lit,"ordinary" + +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome lord", random +MONSTER: 'G', "gnome lord", random +MONSTER: 'h', "dwarf", random +MONSTER: 'h', "dwarf", random +MONSTER: 'h', "dwarf", random + +# The shops +REGION:(25,17,28,19),lit,"candle shop" +DOOR:closed,(24,18) +REGION:(59,9,67,10),lit,"shop" +DOOR:closed,(66,08) +REGION:(57,13,60,15),lit,"tool shop" +DOOR:closed,(56,14) +REGION:(05,09,08,10),lit,"food shop" +DOOR:closed,(07,11) +# Gnome homes +DOOR:closed,(04,14) +DOOR:locked,(01,17) +MONSTER: 'G', "gnomish wizard", (02,19) +DOOR:locked,(20,16) +MONSTER: 'G', random, (20,18) +DOOR:random,(21,14) +DOOR:random,(25,14) +DOOR:random,(42,08) +DOOR:locked,(40,05) +MONSTER: 'G', random, (38,07) +DOOR:random,(59,03) +DOOR:random,(58,06) +DOOR:random,(63,03) +DOOR:random,(63,05) +DOOR:locked,(71,03) +DOOR:locked,(71,06) +DOOR:closed,(69,04) +DOOR:closed,(67,16) +MONSTER: 'G', "gnomish wizard", (67,14) +OBJECT: '=', random, (70,14) +DOOR:locked,(69,18) +MONSTER: 'G', "gnome lord", (71,19) +DOOR:locked,(73,18) +OBJECT: '(', "chest", (73,19) +DOOR:locked,(50,06) +OBJECT: '(', random, (50,03) +OBJECT: '`', "statue", (38,15), "gnome king", 1 +# Temple +REGION:(29,02,33,04),lit,"temple" +DOOR:closed,(31,05) +ALTAR:(31,03),align[0],shrine + + +# "Bustling Town" by Kelly Bailey +# +MAZE: "minetn-6",' ' +INIT_MAP:'.','-',true,true,lit,true +GEOMETRY:center,top +MAP +.-----................----------------.- +.|...|................|...|..|...|...|.. +.|...+..--+--.........|...|..|...|...|.. +.|...|..|...|..-----..|...|..|-+---+--.. +.-----..|...|--|...|..--+---+-.........| +........|...|..|...+.............-----.. +........-----..|...|......--+-...|...|.. +.----...|...|+------..{...|..|...+...|.. +.|..+...|...|.............|..|...|...|.. +.|..|...|...|-+-.....---+-------------.| +.----...--+--..|..-+-|.................. +...|........|..|..|..|----....---------. +...|..T.....----..|..|...+....|......|-. +...|-....{........|..|...|....+......|-. +...--..-....T.....--------....|......|-. +.......--.....................---------- +ENDMAP + +REGION:(00,00,38,15),lit,"ordinary" +STAIR:levregion(01,03,20,19),(0,0,39,15),up +STAIR:levregion(61,03,75,19),(0,0,39,15),down +FOUNTAIN:(22,07) +FOUNTAIN:(09,13) +REGION:(13,5,14,6),unlit,"ordinary" +REGION:(9,7,11,9),lit,"candle shop" +REGION:(16,4,18,6),lit,"tool shop" +REGION:(23,1,25,3),lit,"shop" +REGION:(22,12,24,13),lit,"food shop" +REGION:(31,12,36,14),lit,"temple" +ALTAR:(35,13),align[0],shrine + +DOOR:closed,(5,2) +DOOR:locked,(4,8) +DOOR:closed,(10,2) +DOOR:closed,(10,10) +DOOR:locked,(13,7) +DOOR:locked,(14,9) +DOOR:closed,(19,5) +DOOR:closed,(19,10) +DOOR:closed,(24,4) +DOOR:closed,(24,9) +DOOR:closed,(25,12) +DOOR:closed,(28,4) +DOOR:locked,(28,6) +DOOR:closed,(30,13) +DOOR:closed,(31,3) +DOOR:closed,(35,3) +DOOR:closed,(33,7) + +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", (14,6) +MONSTER: 'G', "gnome lord", (14,5) +MONSTER: 'G', "gnome", (27,8) +MONSTER: 'G', "gnome lord", random +MONSTER: 'G', "gnome lord", random +MONSTER: 'h', "dwarf", random +MONSTER: 'h', "dwarf", random +MONSTER: 'h', "dwarf", random +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful +MONSTER: '@', "watch captain", random, peaceful + + +# "Bazaar Town" by Kelly Bailey +# +LEVEL: "minetn-7" +ROOM: "ordinary" , lit, (3,3), (center,center), (30,15) +NAME: "town" +FOUNTAIN: (12, 07) +FOUNTAIN: (11, 13) + +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watchman", random, peaceful +MONSTER: '@', "watch captain", random, peaceful +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome lord",random +MONSTER:'Y',"monkey",random +MONSTER:'Y',"monkey",random + +SUBROOM: "ordinary", random, (2,2), (4,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (7,2), (2,2), "town" +DOOR: false, closed, north, random + +SUBROOM: "ordinary", random, (7,5), (2,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", lit, (10,2), (3,4), "town" +MONSTER:'G',"gnome",random +MONSTER:'Y',"monkey",random +MONSTER:'Y',"monkey",random +MONSTER:'Y',"monkey",random +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (14,2), (4,2), "town" +DOOR: false, closed, south, 0 +MONSTER: 'n', random, random + +SUBROOM: "ordinary", random, (16,5), (2,2), "town" +DOOR: false, closed, south, random + +SUBROOM: "ordinary", unlit, (19,2), (2,2), "town" +DOOR: false, locked, east, random +MONSTER: 'G',"gnome king",random + +SUBROOM: "food shop", lit, (19,5), (2,3), "town" +CHANCE: 50 +DOOR: false, closed, south, random + +SUBROOM: "ordinary", random, (2,7), (2,2), "town" +DOOR: false, closed, east, random + +SUBROOM: "tool shop", lit, (2,10), (2,3), "town" +CHANCE: 50 +DOOR: false, closed, south, random + +SUBROOM: "candle shop", lit, (5,10),(3,3), "town" +DOOR: false, closed, north, random + +SUBROOM: "ordinary", random, (11,10), (2,2), "town" +DOOR: false, locked, west, random +MONSTER: 'G',random,random + +SUBROOM: "shop", lit, (14,10), (2,3), "town" +CHANCE: 60 +DOOR: false, closed, north, random + +SUBROOM: "ordinary", random, (17,11), (4,2), "town" +DOOR: false, closed, north, random + +SUBROOM: "ordinary", random, (22,11), (2,2), "town" +DOOR: false, closed, south, random +SINK: (00,00) + +SUBROOM: "food shop", lit, (25,11), (3,2), "town" +CHANCE: 50 +DOOR: false, closed, east, random + +SUBROOM: "tool shop", lit, (25,2), (3,3), "town" +CHANCE: 30 +DOOR: false, closed, west, random + +SUBROOM: "temple", lit, (24,6), (4,4), "town" +DOOR: false, closed, west, random +ALTAR:(02,01),align[0],shrine +MONSTER: 'G', "gnomish wizard", random +MONSTER: 'G', "gnomish wizard", random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, up + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +TRAP: random, random +MONSTER: 'G', "gnome", random +MONSTER: 'G', "gnome", random + +ROOM: "ordinary" , random, random, random, random +MONSTER: 'h', "dwarf", random + +ROOM: "ordinary" , random, random, random, random +TRAP: random, random +MONSTER: 'G', "gnome", random + +RANDOM_CORRIDORS + + +# Mine end level variant 1 +# "Mimic of the Mines" +# +MAZE: "minend-1", ' ' +GEOMETRY:center,center +#1234567890123456789012345678901234567890123456789012345678901234567890 +MAP +------------------------------------------------------------------ ------ +| |.......| |.......-...| |.....|. | +| --------- ----.......-------...........| ---...-S- | +| |.......| |..........................-S- --.......| | +| |......------- ---........................|. |.......-- | +| |..--........-----..........................|. -.-..---- | +| --..--.-----........-.....................--- --..-- | +| --..--..| -----------..................---.----------..-- | +| |...--.| |..S...S..............---................-- | +| ----..----- ------------........--- ------------...--- | +| |.........-- ---------- ---...-- ----- | +| --.....---..-- -------- --...---...-- | +| ----..-..-- --..--------------------- --......-- ---........| | +|--....----- --..-..................--- |........| |.......-- | +|.......| --......................S.. --......-- ---..---- | +|--.--.-- ----.................--- ------..------...-- | +| |....S.. |...............-..| ..S...........| | +-------- -------------------- ------------------------ +ENDMAP + +# Dungeon Description +RANDOM_PLACES:(08,16),(13,07),(21,08),(41,14),(50,04),(50,16),(66,01) +REGION:(26,01,32,01),unlit,"ordinary",filled,true +REGION:(20,08,21,08),unlit,"ordinary" +REGION:(23,08,25,08),unlit,"ordinary" +# Secret doors +DOOR:locked,(07,16) +DOOR:locked,(22,08) +DOOR:locked,(26,08) +DOOR:locked,(40,14) +DOOR:locked,(50,03) +DOOR:locked,(51,16) +DOOR:locked,(66,02) +# Stairs +STAIR:(36,04),up +# Non diggable walls +NON_DIGGABLE:(00,00,74,17) +# Niches +# Note: place[6] empty +OBJECT:'*',"diamond",place[0] +OBJECT:'*',"emerald",place[0] +OBJECT:'*',"worthless piece of violet glass",place[0] +MONSTER:'m',random,place[0], m_object "luckstone" +OBJECT:'*',"worthless piece of white glass",place[1] +OBJECT:'*',"emerald",place[1] +OBJECT:'*',"amethyst",place[1] +MONSTER:'m',random,place[1], m_object "loadstone" +OBJECT:'*',"diamond",place[2] +OBJECT:'*',"worthless piece of green glass",place[2] +OBJECT:'*',"amethyst",place[2] +MONSTER:'m',random,place[2], m_object "flint" +OBJECT:'*',"worthless piece of white glass",place[3] +OBJECT:'*',"emerald",place[3] +OBJECT:'*',"worthless piece of violet glass",place[3] +MONSTER:'m',random,place[3], m_object "touchstone" +OBJECT:'*',"worthless piece of red glass",place[4] +OBJECT:'*',"ruby",place[4] +OBJECT:'*',"loadstone",place[4] +OBJECT:'*',"ruby",place[5] +OBJECT:'*',"worthless piece of red glass",place[5] +OBJECT:'*',"luckstone",place[5] +# Random objects +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'(',random,random +OBJECT:'(',random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters +MONSTER:'G',"gnome king",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnomish wizard",random +MONSTER:'G',"gnomish wizard",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'h',"hobbit",random +MONSTER:'h',"hobbit",random +MONSTER:'h',"dwarf",random +MONSTER:'h',"dwarf",random +MONSTER:'h',"dwarf",random +MONSTER:'h',random,random + + +# Mine end level variant 2 +# "Gnome King's Wine Cellar" +# +MAZE: "minend-2", ' ' +GEOMETRY:center,center +MAP +--------------------------------------------------------------------------- +|...................................................| | +|.|---------S--.--|...|--------------------------|..| | +|.||---| |.||-| |...|..........................|..| | +|.||...| |-|.|.|---...|.............................| .. | +|.||...|-|.....|....|-|..........................|..|. .. | +|.||.....|-S|..|....|............................|..|.. | +|.||--|..|..|..|-|..|----------------------------|..|-. | +|.| |..|..|....|..................................|... | +|.| |..|..|----|..-----------------------------|..|.... | +|.|---|..|--|.......|----------------------------|..|..... | +|...........|----.--|......................| |..|....... | +|-----------|...|.| |------------------|.|.|-----|..|.....|.. | +|-----------|.{.|.|--------------------|.|..........|.....|.... | +|...............|.S......................|-------------..-----... | +|.--------------|.|--------------------|.|......................... | +|.................| |.....................|........ | +--------------------------------------------------------------------------- +ENDMAP + +# Dungeon Description +FOUNTAIN:(14,13) +REGION:(23,03,48,06),lit,"ordinary" +REGION:(21,06,22,06),lit,"ordinary" +REGION:(14,04,14,04),unlit,"ordinary" +REGION:(10,05,14,08),unlit,"ordinary" +REGION:(10,09,11,09),unlit,"ordinary" +REGION:(15,08,16,08),unlit,"ordinary" +# Secret doors +DOOR:locked,(12,02) +DOOR:locked,(11,06) +# Stairs +STAIR:(36,04),up +# Non diggable walls +NON_DIGGABLE:(00,00,52,17) +NON_DIGGABLE:(53,00,74,00) +NON_DIGGABLE:(53,17,74,17) +NON_DIGGABLE:(74,01,74,16) +NON_DIGGABLE:(53,07,55,07) +NON_DIGGABLE:(53,14,61,14) +# The Gnome King's wine cellar. +ENGRAVING:(12,03),engrave,"You are now entering the Gnome King's wine cellar." +ENGRAVING:(12,04),engrave,"Trespassers will be persecuted!" +OBJECT:'!',"booze",(10,07) +OBJECT:'!',"booze",(10,07) +OBJECT:'!',random,(10,07) +OBJECT:'!',"booze",(10,08) +OBJECT:'!',"booze",(10,08) +OBJECT:'!',random,(10,08) +OBJECT:'!',"booze",(10,09) +OBJECT:'!',"booze",(10,09) +OBJECT:'!',"object detection",(10,09) +# Objects +# The Treasure chamber... +OBJECT:'*',"diamond",(69,04) +OBJECT:'*',random,(69,04) +OBJECT:'*',"diamond",(69,04) +OBJECT:'*',random,(69,04) +OBJECT:'*',"emerald",(70,04) +OBJECT:'*',random,(70,04) +OBJECT:'*',"emerald",(70,04) +OBJECT:'*',random,(70,04) +OBJECT:'*',"emerald",(69,05) +OBJECT:'*',random,(69,05) +OBJECT:'*',"ruby",(69,05) +OBJECT:'*',random,(69,05) +OBJECT:'*',"ruby",(70,05) +OBJECT:'*',"amethyst",(70,05) +OBJECT:'*',random,(70,05) +OBJECT:'*',"amethyst",(70,05) +OBJECT:'*',"luckstone",(70,05) +# Scattered gems... +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'*',random,random +OBJECT:'(',random,random +OBJECT:'(',random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +# Random traps +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# Random monsters. +MONSTER:'G',"gnome king",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnome lord",random +MONSTER:'G',"gnomish wizard",random +MONSTER:'G',"gnomish wizard",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'G',"gnome",random +MONSTER:'h',"hobbit",random +MONSTER:'h',"hobbit",random +MONSTER:'h',"dwarf",random +MONSTER:'h',"dwarf",random +MONSTER:'h',"dwarf",random +MONSTER:'h',random,random + + +# "Catacombs" by Kelly Bailey +# Relies on some very specific behavior of MAZEWALK. +# +MAZE:"minend-3",'-' +FLAGS:nommap +GEOMETRY:center,bottom +MAP + - - - - - - - - - - -- -- - - . - - - - - - - - - -- - - -- - - - - . - - | +------...---------.-----------...-----.-------.------- ----------------| + - - - - - - - - - - - . - - - . - - - - - - - - - - -- - -- - . - - - - - | +------------.---------...-------------------------.--- ------------------| + - - - - - - - - - - . . - - --- - . - - - - - - - - -- -- - - - - |.....| | +--.---------------.......------------------------------- ----------|.....S-| + - - - - |.. ..| - ....... . - - - - |.........| - - - --- - - - - |.....| | +----.----|.....|------.......--------|.........|--------------.------------| + - - - - |..{..| - - -.... . --- - -.S.........S - - - - - - - - - - - - - | +---------|.....|--.---...------------|.........|---------------------------| + - - - - |.. ..| - - - . - - - - - - |.........| - --- . - - - - - - - - - | +----------------------...-------.---------------------...------------------| +---..| - - - - - - - - . --- - - - - - - - - - - - - - . - - --- - - --- - | +-.S..|----.-------.------- ---------.-----------------...----- -----.------- +---..| - - - - - - - -- - - -- . - - - - - . - - - . - . - - -- -- - - - -- +-.S..|--------.---.--- -...---------------...{.--------- --------- +--|. - - - - - - - -- - - - -- . - - - --- - - - . . - - - - -- - - - - - - +ENDMAP + +RANDOM_PLACES:(1,15),(68,6),(1,13) +NON_DIGGABLE:(67,3,73,7) +NON_DIGGABLE:(0,12,2,16) +FOUNTAIN:(12,08) +FOUNTAIN:(51,15) +REGION:(0,0,75,16),unlit,"ordinary" +REGION:(38,6,46,10),lit,"ordinary" +DOOR:closed,(37,8) +DOOR:closed,(47,8) +DOOR:closed,(73,5) +DOOR:closed,(2,15) +MAZEWALK:(36,8),west +STAIR:(42,8),up + +# Objects +OBJECT:'*',"diamond",random +OBJECT:'*',random,random +OBJECT:'*',"diamond",random +OBJECT:'*',random,random +OBJECT:'*',"emerald",random +OBJECT:'*',random,random +OBJECT:'*',"emerald",random +OBJECT:'*',random,random +OBJECT:'*',"emerald",random +OBJECT:'*',random,random +OBJECT:'*',"ruby",random +OBJECT:'*',random,random +OBJECT:'*',"ruby",random +OBJECT:'*',"amethyst",random +OBJECT:'*',random,random +OBJECT:'*',"amethyst",random +OBJECT:'*',"luckstone",place[0] +OBJECT:'*',"flint",place[1] +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random +OBJECT:random,random,random +OBJECT:random,random,random +OBJECT:random,random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +TRAP:random,random +# One-time annoyance factor +TRAP:"level teleport",place[0] +TRAP:"level teleport",place[1] +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',random,random +MONSTER:'M',"ettin mummy",random +MONSTER:'V',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'Z',random,random +MONSTER:'V',random,random +MONSTER:'e',random,random +MONSTER:'e',random,random +MONSTER:'e',random,random +MONSTER:'e',random,random + + +# end mines.des diff --git a/dat/opthelp b/dat/opthelp new file mode 100644 index 0000000..4dade58 --- /dev/null +++ b/dat/opthelp @@ -0,0 +1,226 @@ +Boolean options not under specific compile flags (with default values in []): +(You can learn which options exist in your version by checking your current +option setting, which is reached via the 'O' cmd.) + +autodig dig if moving and wielding digging tool [FALSE] +autopickup automatically pick up objects you move over [TRUE] +autoquiver when firing with an empty quiver, select some + suitable inventory weapon to fill the quiver [FALSE] +BIOS allow the use of IBM ROM BIOS calls [FALSE] +cmdassist give help for errors on direction & other commands [TRUE] +confirm ask before hitting tame or peaceful monsters [TRUE] +DECgraphics use DEC/VT line-drawing characters for the dungeon [FALSE] +eight_bit_tty send 8-bit characters straight to terminal [FALSE] +extmenu use a menu for selecting extended commands (#) [FALSE] +fixinv try to retain the same letter for the same object [TRUE] +help print all available info when using the / command [TRUE] +IBMgraphics use IBM extended characters for the dungeon [FALSE] +ignintr ignore interrupt signal, including breaks [FALSE] +legacy print introductory message [TRUE] +lit_corridor show a dark corridor as lit if in sight [FALSE] +lootabc use a/b/c rather than o/i/b when looting [FALSE] +mail enable the mail daemon [TRUE] +null allow nulls to be sent to your terminal [TRUE] + try turning this option off (forcing NetHack to use its own + delay code) if moving objects seem to teleport across rooms +number_pad use the number keys to move instead of yuhjklbn [FALSE] +perm_invent keep inventory in a permanent window [FALSE] +prayconfirm use confirmation prompt when #pray command issued [TRUE] +pushweapon when wielding a new weapon, put your previously + wielded weapon into the secondary weapon slot [FALSE] +rawio allow the use of raw I/O [FALSE] +rest_on_space count the space bar as a rest character [FALSE] +safe_pet prevent you from (knowingly) attacking your pet(s) [TRUE] +showrace show yourself by your race rather than by role [FALSE] +silent don't use your terminal's bell sound [TRUE] +sortpack group similar kinds of objects in inventory [TRUE] +sound enable messages about what your character hears [TRUE] + (note: this has nothing to do with your computer's audio + capabilities, and the game resets it periodically) +sparkle display sparkly effect for resisted magical [TRUE] + attacks (e.g. fire attack on fire-resistant monster) +standout use standout mode for --More-- on messages [FALSE] +time display elapsed game time, in moves [FALSE] +tombstone print tombstone when you die [TRUE] +toptenwin print topten in a window rather than stdout [FALSE] +travel enable the command to travel to a map location via [TRUE] + a shortest-path algorithm, usually invoked by '_'. +verbose print more commentary during the game [TRUE] + + + +There are further boolean options controlled by compilation flags. + +Boolean option if INSURANCE was set at compile time: +checkpoint save game state after each level change, for possible [TRUE] + recovery after program crash + +Boolean option if NEWS was set at compile time: +news print any news from game administrator on startup [TRUE] + +Boolean option if MFLOPPY was set at compile time: +checkspace check free disk space before writing files to disk [TRUE] + +Boolean option if EXP_ON_BOTL was set at compile time: +showexp display your accumulated experience points [FALSE] + +Boolean option if SCORE_ON_BOTL was set at compile time: +showscore display your approximate accumulated score [FALSE] + +Boolean options if TEXTCOLOR was set at compile time: +color use different colors for objects on screen [TRUE for micros] +hilite_pet display pets in a highlighted manner [FALSE] + +Boolean option if TIMED_DELAY was set at compile time (tty interface only): +timed_delay on unix and VMS, use a timer instead of sending + extra screen output when attempting to pause for + display effect. on MSDOS without the termcap + lib, whether or not to pause for visual effect. [TRUE] + +Boolean option if USE_TILES was set at compile time (MSDOS protected mode only): +preload_tiles control whether tiles get pre-loaded into RAM at the + start of the game. Doing so enhances performance + of the tile graphics, but uses more memory. [TRUE] + +Any Boolean option can be negated by prefixing it with a '!' or 'no'. + + +Compound options are written as option_name:option_value. + +Compound options which can be set during the game are: + +boulder override the default boulder symbol with another default: [`] +disclose the types of information you want offered at the end of the + game [ni na nv ng nc] +fruit the name of a fruit you enjoy eating [slime mold] + (basically a whimsy which NetHack uses from time to time). +menustyle user interface for selection of multiple objects: + Traditional -- one object at a time prompting; + Combination -- prompt for classes of interest, then menu; + Partial -- skip class prompt, use menu of all objects; + Full -- menu for classes of interest, then object menu; + only the first letter ('T','C','P','F') matters; 'N' (None) + is a synonym for 'T', as is boolean style negation [Full] +packorder a list of default symbols for kinds of objects that gives the + order in which your pack will be displayed [")[%?+!=/(*`0_] + (If you specify only some kinds of items, the others from the + default order will be appended to the end.) +pickup_burden when you pick up an item that exceeds this encumberance + level (Unburdened, Burdened, streSsed, straiNed, overTaxed, + or overLoaded), you will be asked if you want to continue. [S] +pickup_types a list of default symbols for kinds of objects to autopickup + when that option is on [all] +runmode controls how often the map window is updated when performing + multi-step movement (various running modes or travel command): + teleport -- don't update map until movement stops; + run -- periodically update map (interval is seven steps); + walk -- update map after every step; + crawl -- like walk, but delay after making each step. + (This only affects screen display, not actual movement.) [run] +scores the parts of the score list you wish to see when the game ends + You choose a combination of top scores, scores around the top + scores, and all of your own scores. [!own/3 top/2 around] +suppress_alert disable various version-specific warnings about changes + in game play or the user interface, such as notification given + for the 'Q' command that quitting is now done via #quit + (e.g., use suppress_alert:3.3.1 to stop that and any other + notifications added in that version or earlier) default: [(none)] + +Compound options which may be set only on startup are: + +align Your starting alignment (align:lawful, align:neutral, + or align:chaotic). You may specify just the first letter. [RANDOM] +catname the name of your first cat [NONE] +dogname the name of your first dog [NONE] +dungeon a list of symbols to be used in place of the default ones for + drawing the dungeon. + The symbols are subjected to a fair amount of processing, so + that you can use C-style escapes such as \n or \081 as well as + indicate control characters by ^x or meta characters by \Mx. + As usual, \ can force the next character to be taken literally. + Since many of the default symbols are overloaded, they are + given here by name instead of symbol, with some added notes: + stone (solid rock, normally ' ') + vwall hwall tlcorn trcorn blcorn brcorn (room boundaries) + crwall tuwall tdwall tlwall trwall (wallified maze characters) + nodoor vodoor hodoor (no, vertical, horizontal open door) + vcdoor hcdoor (vertical, horizontal closed door) + ironbars tree room darkcorr litcorr + upstair dnstair upladder dnladder + altar grave throne sink fountain pool ice lava + vodbridge hodbridge (vertical, horizontal open drawbridge) + vcdbridge hcdbridge (vertical, horizontal closed drawbridge) + air cloud water + default: \ |--------||.-|++##.##<><>_\\#{}.}..##\ #} +effects like dungeon, but for special effects symbols + vbeam hbeam lslant rslant (generic zap beams) + digbeam flashbeam (special beams for digging and cameras) + boomleft boomright (boomerangs) + ss1 ss2 ss3 ss4 (shielding sequence) + sw_topl, sw_topm, sw_topr, (swallow, top row) + sw_midl, sw_midr, (swallow, middle row [no center]) + sw_botl, sw_botm, sw_botr (swallow, bottom row) + extl extm extr (explosion matrix top row) + exml exmm exmr (explosion matrix middle row) + exbl exbm exbr (explosion matrix bottom row) + default: |-\\/*!)(0#@*/-\\||\\-//-\\|\ |\\-/ +gender Your starting gender (gender:male or gender:female). + You may specify just the first letter. Although you can + still denote your gender using the "male" and "female" + options, the "gender" option will take precedence. [RANDOM] +horsename the name of your first horse [NONE] +menu_* create single character accelerators for menu commands. Below + is a list of all commands. Each is followed by a list of window- + ports that implement them: 'x' is X11, 't' is tty, 'g' is Gem, + 'a' is Amiga. + menu_deselect_all deselect all items in a menu [-](gxta) + menu_deselect_page deselect all items on this page of a menu [\](gta) + menu_first_page jump to the first page in a menu [^](gta) + menu_invert_all invert all items in a menu [@](gxta) + menu_invert_page invert all items on this page of a menu [~](gta) + menu_last_page jump to the last page in a menu [|](gta) + menu_next_page goto the next menu page [>](gta) + menu_previous_page goto the previous menu page [<](gta) + menu_search search for a menu item [:](gxa) + menu_select_all select all items in a menu [.](gxta) + menu_select_page select all items on this page of a menu [,](gta) +monsters like dungeon, but for monster symbols + default: abcdefghijklmnopqrstuvwxyz + ABCDEFGHIJKLMNOPQRSTUVWXYZ@\ \\&;:~] +msghistory number of top line messages to save [20] +name the name of your character [obtained by asking the system or + the player] +objects like dungeon, but for object symbols + default: ])[="(%!?+/$*`0_. +pettype your preferred type of pet (cat or dog), if your character + class uses both types; or none for no pet [RANDOM] +race Your starting race (e.g., race:Human, race:Elf). [RANDOM] +role Your starting role (e.g., role:Barbarian, role:Valk). + Although you can specify just the first letter(s), it will + choose only the first role it finds that matches; thus, it + is recommended that you spell out as much of the role name + as possible. You can also still denote your role by + appending it to the "name" option (e.g., name:Vic-V), but the + "role" option will take precedence. [RANDOM] +traps like dungeon, but for trap symbols + arrow_trap dart_trap falling_rock_trap squeaky_board + bear_trap land_mine rolling_boulder_trap sleeping_gas_trap + rust_trap fire_trap pit spiked_pit hole trap_door + teleportation_trap level_teleporter magic_portal web statue_trap + magic_trap anti_magic_trap polymorph_trap + default: ^^^^^^^^^^^^^^^^^"^^^^ + +windowtype windowing system to be used [depends on operating system] + +Compound option if TTY_GRAPHICS was set at compile time: +msg_window the type of message window to use: + single -- One message at a time + full -- Full window with all saved top line messages + reverse -- Same as full, but messages printed most-recent-first + combination -- Two single messages, then as full + default: single + +Some sample options lists are: +!autopickup,!tombstone,name:Gandalf,scores:own/3 top/2 around +female,nonews,dogname:Rover,dungeon: |--------||.-|++.##<><>_\\#{}.}..## #} +rest_on_space,!verbose,menustyle:traditional diff --git a/dat/oracle.des b/dat/oracle.des new file mode 100644 index 0000000..0e8f176 --- /dev/null +++ b/dat/oracle.des @@ -0,0 +1,56 @@ +# SCCS Id: @(#)oracle.des 3.4 1995/10/07 +# NetHack may be freely redistributed. See license for details. +# +# Oracle level +# + +LEVEL: "oracle" + +ROOM: "ordinary" , lit, (3,3), (center,center), (11,9) +NAME: "central" +OBJECT:'`',"statue",(0,0),"forest centaur",1 +OBJECT:'`',"statue",(0,8),"mountain centaur",1 +OBJECT:'`',"statue",(10,0),"mountain centaur",1 +OBJECT:'`',"statue",(10,8),"forest centaur",1 +OBJECT:'`',"statue",(5,1),"plains centaur",1 +OBJECT:'`',"statue",(5,7),"plains centaur",1 +OBJECT:'`',"statue",(2,4),"plains centaur",1 +OBJECT:'`',"statue",(8,4),"plains centaur",1 +MONSTER: random, random, random +MONSTER: random, random, random + +SUBROOM: "delphi" , lit , (4,3) , (3,3), "central" +FOUNTAIN: (0, 1) +FOUNTAIN: (1, 0) +FOUNTAIN: (1, 2) +FOUNTAIN: (2, 1) +MONSTER: '@', "Oracle", (1,1) +DOOR: false , nodoor , random, random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, up +OBJECT: random,random,random + +ROOM: "ordinary" , random, random, random, random +STAIR: random, down +OBJECT: random, random, random +TRAP: random, random +MONSTER: random, random, random +MONSTER: random, random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +OBJECT: random, random, random +MONSTER: random, random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: random, random, random + +ROOM: "ordinary" , random, random, random, random +OBJECT: random, random, random +TRAP: random, random +MONSTER: random, random, random + +RANDOM_CORRIDORS diff --git a/dat/oracles.txt b/dat/oracles.txt new file mode 100644 index 0000000..8a37a4f --- /dev/null +++ b/dat/oracles.txt @@ -0,0 +1,99 @@ +----- +If thy wand hath run out of charges, thou mayst zap it again and again; though +naught will happen at first, verily, thy persistence shall be rewarded, as +one last charge may yet be wrested from it! +----- +Though the shopkeepers be wary, thieves have nevertheless stolen much by using +their digging wands to hasten exits through the pavement. +----- +If thou hast had trouble with rust on thine armor or weapons, thou shouldst +know that thou canst prevent this by, while in a confused state, reading the +magical parchments which normally are used to cause their enchantment. +Unguents of lubrication may provide similar protection, albeit of a +transitory nature. +----- +Behold the cockatrice, whose diminutive stature belies its hidden might. The +cockatrice can petrify any ordinary being it contacts--save those wise +adventurers who eat a dead lizard or blob of acid when they feel themselves +slowly turning to stone. +----- +While some wayfarers rely on scrounging finished armour in the dungeon, the +resourceful know the mystical means by which mail may be fashioned out of +scales from a dragon's hide. +----- +It is customarily known among travelers that extra-healing draughts may clear +thy senses when thou art addled by delusory visions. But never forget, the +lowly potion which makes one sick may be used for the same purpose. +----- +While the consumption of lizard flesh or water beloved of the gods may clear +the muddled head, the application of the horn of a creature of utmost purity +can alleviate many other afflictions as well. +----- +If thou wouldst travel quickly between distant locations, thou must be +able to control thy teleports, and in a confused state misread the scroll +which usually teleports thyself locally. Daring adventurers have also +performed the same feat sans need for scrolls or potions by stepping into +a particular ambuscade. +----- +Almost all adventurers who come this way hope to pass the dread Medusa. To +do this, the best advice is to keep thine eyes blindfolded and to cause the +creature to espy its own reflection in a mirror. +----- +And where it is written "ad aerarium", diligent searching will often reveal +the way to a trap which sends one to the Magic Memory Vault, where the riches +of Croesus are stored; however, escaping from the vault with its gold is much +harder than getting in. +----- +It is well known that wily shopkeepers raise their prices whene'er they +espy the garish apparel of the approaching tourist or the countenance of a +disfavored patron. They favor the gentle of manner and the fair of face. +The boor may expect unprofitable transactions. +----- SINKS +The cliche of the kitchen sink swallowing any unfortunate rings that contact +its pernicious surface reflecteth greater truth than many homilies, yet +even so, few have developed the skill to identify enchanted rings by the +transfigurations effected upon the voracious device's frame. +----- +The meat of enchanted creatures ofttimes conveyeth magical properties +unto the consumer. A fresh corpse of floating eye doth fetch a high +price among wizards for its utility in conferring Telepathy, by which +the sightless may locate surrounding minds. +----- +The detection of blessings and curses is in the domain of the gods. They will +make this information available to mortals who request it at their places of +worship, or elsewhere for those mortals who devote themselves to the service +of the gods. +----- +At times, the gods may favor worthy supplicants with named blades whose +powers echo throughout legend. Learned wayfarers can reproduce blades of +elven lineage, hated of the orcs, without the need for such intervention. +----- +There are many stories of a mighty amulet, the origins of which are said +to be ancient Yendor. This amulet doth have awesome power, and the gods +desire it greatly. Mortals mayst tap only portions of its terrible +abilities. The stories tell of mortals seeing what their eyes cannot +see and seeking places of magical transportation, while having this +amulet in their possession. Others say a mortal must wear the amulet to +obtain these powers. But verily, such power comes at great cost, to +preserve the balance. +----- +It is said that thou mayst gain entry to Moloch's sanctuary, if thou +darest, from a place where the ground vibrateth in the deepest depths of +Gehennom. Thou needs must have the aid of three magical items. The +pure sound of a silver bell shall announce thee. The terrible runes, +read from Moloch's book, shall cause the earth to tremble mightily. The +light of an enchanted candelabrum shall show thee the way. +----- +In the deepest recesses of the Dungeons of Doom, guarding access to the +nether regions, there standeth a castle, wherein lieth a wand of wishes. +If thou wouldst gain entry, bear with thee an instrument of music, for the +pontlevis may be charmed down with the proper melody. What notes comprise +it only the gods know, but a musical mastermind may yet succeed by witful +improvisation. However, the less perspicacious are not without recourse, +should they be prepared to circumambulate the castle to the postern. +----- ELBERETH +The name of Elbereth may strike fear into the hearts of thine enemies, if +thou dost write it upon the ground at thy feet. If thou maintainest the +utmost calm, thy safety will be aided greatly, but beware lest thy clumsy +feet scuff the inscription, cancelling its potence. +----- diff --git a/dat/quest.txt b/dat/quest.txt new file mode 100644 index 0000000..7cfb724 --- /dev/null +++ b/dat/quest.txt @@ -0,0 +1,3507 @@ +# SCCS Id: @(#)quest.txt 3.4 2002/01/30 +# Copyright (c) 1991 by M. Stephenson +# NetHack may be freely redistributed. See license for details. +# +# The quest text file for NetHack 3.4 +# +# These are the "standard" message numbers from qtext.h. All class +# dialogue must have at least these entries. +# +# QT_FIRSTTIME 1 +# QT_NEXTTIME 2 +# QT_OTHERTIME 3 +# +# QT_GUARDTALK 5 /* 5 random things guards say before quest */ +# QT_GUARDTALK2 10 /* 5 random things guards say after quest */ +# +# QT_FIRSTLEADER 15 +# QT_NEXTLEADER 16 +# QT_OTHERLEADER 17 +# QT_LASTLEADER 18 +# QT_BADLEVEL 19 +# QT_BADALIGN 20 +# QT_ASSIGNQUEST 21 +# +# QT_ENCOURAGE 25 /* 1-10 random encouragement messages */ +# +# QT_FIRSTLOCATE 35 +# QT_NEXTLOCATE 36 +# +# QT_FIRSTACQUIRE 40 +# QT_NEXTACQUIRE 41 +# +# QT_FIRSTNEMESIS 50 +# QT_NEXTNEMESIS 51 +# QT_OTHERNEMESIS 52 +# QT_NEMWANTSIT 53 /* you somehow got the artifact */ +# +# QT_DISCOURAGE 60 /* 1-10 random maledictive messages */ +# +# QT_GOTIT 70 +# +# QT_KILLEDNEM 80 +# QT_OFFEREDIT 81 +# QT_OFFEREDIT2 82 /* if you throw artifact to leader after #81 */ +# +# QT_POSTHANKS 90 +# QT_HASAMULET 91 +# +# +# Archeologist +# +%Cc Arc 00001 +You are suddenly in familiar surroundings. The buildings in the distance +appear to be those of your old alma mater, but something is wrong. It looks +as if there has been a riot recently, or %H has +been under siege. + +All of the windows are boarded up, and there are objects scattered around +the entrance. + +Strange forbidding shapes seem to be moving in the distance. +%E +%Cp Arc 00002 +Once again, you are back at %H. +%E +%Cp Arc 00003 +You are back at %H. +You have an odd feeling this may be the last time you ever come here. +%E +%Cp Arc 00005 +"Did you see Lash LaRue in 'Song of Old Wyoming' the other night?" +%E +%Cp Arc 00006 +"Hey man, got any potions of hallucination for sale?" +%E +%Cp Arc 00007 +"Did you see the artifact %l brought back from the last dig?" +%E +%Cp Arc 00008 +"So what species do *you* think we evolved from?" +%E +%Cp Arc 00009 +"So you're %ls prize pupil! I don't know what he sees in you." +%E +%Cp Arc 00010 +"Did you see Lash LaRue in 'Song of Old Wyoming' the other night?" +%E +%Cp Arc 00011 +"Hey man, got any potions of hallucination for sale?" +%E +%Cp Arc 00012 +"I guess you are guaranteed to make full professor now." +%E +%Cp Arc 00013 +"So, what was worse, %n or your entrance exams?" +%E +%Cp Arc 00014 +"%oC is impressive, but nothing like the bones I dug up!" +%E +%Cc Arc 00015 +"Finally you have returned, %p. You were always +my most promising student. Allow me to see if you are ready for the +most difficult task of your career." +%E +%Cp Arc 00016 +"Again, %p, you stand before me. +Let me see if you have gained experience in the interim." +%E +%Cp Arc 00017 +"Once more, %p, you have returned from the field. +Are you finally ready for the task that must be accomplished?" +%E +%Cc Arc 00018 +"%p, you have failed us. All of my careful training has been in +vain. Begone! Your tenure at this college has been revoked! + +"You are a disgrace to the profession!" +%E +%Cc Arc 00019 +"%p, you are yet too inexperienced to undertake such a demanding +quest. A mere %r could not possibly face the rigors demanded and +survive. Go forth, and come here again when your adventures have further +taught you." +%E +%Cc Arc 00020 +"%pC! I've heard that you've been using sloppy techniques. Your +results lately can hardly be called suitable for %ra! + +"How could you have strayed from the %a path? Go from here, and come +back only when you have purified yourself." +%E +%Cc Arc 00021 +"Grave times have befallen the college, for %na has +stolen %o. Without it, the board of directors of +the university will soon have no choice but to revoke our research grants. + +"You must locate the entrance to %i. Within it, +you will find %n. + +"You must then defeat %n and return %o +to me. + +"Only in this way will we be able to prevent the budget cuts that could +close this college. + +"May the wisdom of %d be your guide." +%E +%Cp Arc 00025 +"Beware, for %n is powerful and cunning." +%E +%Cp Arc 00026 +"To locate the entrance to %i, you must pass +many traps." +%E +%Cp Arc 00027 +"A %nt may be vulnerable to attacks by magical cold." +%E +%Cp Arc 00028 +"Call upon %d when you encounter %n." +%E +%Cp Arc 00029 +"You must destroy %n. It will pursue you otherwise." +%E +%Cp Arc 00030 +"%oC is a mighty talisman. With it you +can destroy %n." +%E +%Cp Arc 00031 +"Go forth with the blessings of %d." +%E +%Cp Arc 00032 +"I will have my %gP watch for your return." +%E +%Cp Arc 00033 +"Remember not to stray from the true %a path." +%E +%Cp Arc 00034 +"You may be able to sense %o when you are near." +%E +%Cc Arc 00035 +A plain opens before you. Beyond the plain lies a foreboding edifice. + +You have the feeling that you will soon find the entrance to +%i. +%E +%Cp Arc 00036 +Once again, you are near the entrance to %i. +%E +%Cc Arc 00040 +A strange feeling washes over you, and you think back to things you +learned during the many lectures of %l. + +You realize the feeling must be the presence of %o. +%E +%Cp Arc 00041 +The familiar presence of %o is in the ether. +%E +%Cc Arc 00050 +"So, %p, you think that you can succeed in recovering +%o, when your teacher, %l, has already failed. + +"Come, try your best! I shall destroy you, and gnaw on your bones." +%E +%Cc Arc 00051 +"Again you try to best me, eh %p? Well, you shall fail again. + +"You shall never recover %o. + +"I shall bear your soul to the Plane of Origins for my master's pleasure." +%E +%Cp Arc 00052 +"You persist yet %p! Good. Now, you shall die!" +%E +%Cp Arc 00053 +"I shall have %o from you, %p, then feast +upon your entrails!" +%E +%Cp Arc 00060 +"Try your best, %p. You cannot defeat me." +%E +%Cp Arc 00061 +"I shall rend the flesh from your body whilst you still breathe!" +%E +%Cp Arc 00062 +"First you, %p, then I shall destroy your mentor, %l." +%E +%Cp Arc 00063 +"Tiring yet, %p? I draw my power from my master and cannot +falter!" +%E +%Cp Arc 00064 +"I shall rend thy soul from thy body and consume it!" +%E +%Cp Arc 00065 +"You are far too %a -- it weakens you. You shall die in this place." +%E +%Cp Arc 00066 +"%d has forsaken you! You are lost now!" +%E +%Cp Arc 00067 +"A mere %r cannot hope to defeat me!" +%E +%Cp Arc 00068 +"If you are the best %l can send, I have nothing to fear." +%E +%Cp Arc 00069 +"Die %c! I shall exhibit your carcass as a trophy." +%E +%Cc Arc 00070 +The power of %o flows through your body! You feel +as if you could now take on the Wizard of Yendor himself and win, but +you know you must return %o to %l. +%E +%Cp Arc 00080 +The body of %n dissipates in a cloud of noxious fumes. +%E +%Cc Arc 00081 +%lC touches %o briefly, gazes into it, +then smiles at you and says: + +"Well done, %p. You have defeated %n and +recovered %o. But I fear that it shall never be safe +here. + +Please take %o with you. You, %p, can +guard it now far better than I. + +May the blessings of %d follow you and guard you." +%E +# assumes Orb of Detection (glass object) +%Cc Arc 00082 +"Careful, %p! %oC might break, and that would be +a tragic loss. You are its keeper now, and the time has come to +resume your search for the Amulet. %Z await your +return through the magic portal that brought you here." +%E +%Cc Arc 00090 +"Welcome back, %p. Have you progressed with your quest to +regain the Amulet of Yendor for %d?" +%E +%Cc Arc 00091 +"Congratulations, %p. I wondered if anyone could prevail against +the Wizard and the minions of Moloch. Now, you must embark on one +final adventure. + +"Take the Amulet, and find your way onto the astral plane. +There you must find the altar of %d and sacrifice the +Amulet on that altar to fulfill your destiny. + +"Remember, your path now should always be upwards." +%E +# +# Barbarian +# +%Cc Bar 00001 +Warily you scan your surroundings, all of your senses alert for signs +of possible danger. Off in the distance, you can %x the familiar shapes +of %H. + +But why, you think, should %l be there? + +Suddenly, the hairs on your neck stand on end as you detect the aura of +evil magic in the air. + +Without thought, you ready your weapon, and mutter under your breath: + + "By %d, there will be blood spilt today." +%E +%Cp Bar 00002 +Once again, you near %H. You know that %l +will be waiting. +%E +%Cp Bar 00003 +Again, and you think possibly for the last time, you approach +%H. +%E +%Cp Bar 00005 +"The battles here have been good -- our enemies' blood soaks the soil!" +%E +%Cp Bar 00006 +"Remember that glory is crushing your enemies beneath your feet!" +%E +%Cp Bar 00007 +"There has been little treasure to loot, since the horde arrived." +%E +%Cp Bar 00008 +"The horde is mighty in numbers, but they have little courage." +%E +%Cp Bar 00009 +"%lC is a strange one, but he has helped defend us." +%E +%Cp Bar 00010 +"The battles here have been good -- our enemies' blood soaks the soil!" +%E +%Cp Bar 00011 +"Remember that glory is crushing your enemies beneath your feet!" +%E +%Cp Bar 00012 +"Times will be good again, now that the horde is vanquished." +%E +%Cp Bar 00013 +"You have brought our clan much honor in defeating %n." +%E +%Cp Bar 00014 +"You will be a worthy successor to %l." +%E +%Cc Bar 00015 +"Ah, %p. You have returned at last. The world is in dire +need of your help. There is a great quest you must undertake. + +"But first, I must see if you are ready to take on such a challenge." +%E +%Cp Bar 00016 +"%p, you are back. Are you ready now for the challenge?" +%E +%Cp Bar 00017 +"Again, you stand before me, %p. Surely you have prepared yourself." +%E +%Cc Bar 00018 +"Pah! You have betrayed the gods, %p. You will never attain +the glory which you aspire to. Your failure to follow the true path has +closed this future to you. + +"I will protect these people as best I can, but soon %n will overcome +me and destroy all who once called you %s. Now begone!" +%E +%Cc Bar 00019 +"%p, I fear that you are as yet too inexperienced to face +%n. Only %Ra with the help of %d could ever hope to +defeat him." +%E +%Cc Bar 00020 +"%pC! You have wandered from the path of the %a! +If you attempt to overcome %n in this state, he will surely +enslave your soul. Your only hope, and ours, lies in your purification. +Go forth, and return when you feel ready." +%E +%Cc Bar 00021 +"The world is in great need of your assistance, %p. + +"About six months ago, I learned that a mysterious sorcerer, known +as %n, had begun to gather a large group of cutthroats and brigands +about him. + +"At about the same time, these people you once rode with `liberated' a +potent magical talisman, %o, from a Turanian caravan. + +"%nC and his Black Horde swept down upon %i and defeated +the people there, driving them out into the desert. He has taken +%o, and seeks to bend it to his will. I detected the +subtle changes in the currents of fate, and joined these people. +Then I sent forth a summons for you. + +"If %n can bend %o to his will, he will become +almost indestructible. He will then be able to enslave the minds of +men across the world. You are the only hope. The gods smile upon you, +and with %d behind you, you alone can defeat %n. + +"You must go to %i. From there, you can track down +%n, defeat him, and return %o to us. Only +then will the world be safe." +%E +%Cp Bar 00025 +"%nC is strong in the dark arts, but not immune to cold steel." +%E +%Cp Bar 00026 +"Remember that %n is a great sorcerer. He lived in the time +of Atlantis." +%E +%Cp Bar 00027 +"If you fail, %p, I will not be able to protect these people long." +%E +%Cp Bar 00028 +"To enter %i, you must be very stealthy. The horde will be on +guard." +%E +%Cp Bar 00029 +"Call upon %d in your time of need." +%E +%Cp Bar 00030 +"May %d protect you, and guide your steps." +%E +%Cp Bar 00031 +"If you can lay hands upon %o, carry it for good fortune." +%E +%Cp Bar 00032 +"I cannot stand against %ns sorcery. But %d will help you." +%E +%Cp Bar 00033 +"Do not fear %n. I know you can defeat him." +%E +%Cp Bar 00034 +"You have a great road to travel, %p, but only after you defeat +%n." +%E +%Cc Bar 00035 +The scent of water comes to you in the desert breeze. You know that +you have located %i. +%E +%Cp Bar 00036 +Yet again you have a chance to infiltrate %i. +%E +%Cc Bar 00040 +The hairs on the nape of your neck lift as you sense an energy in the +very air around you. You fight down a primordial panic that seeks to +make you turn and run. This is surely the lair of %n. +%E +%Cp Bar 00041 +Yet again you feel the air around you heavy with malevolent magical energy. +%E +%Cc Bar 00050 +"So. This is what that second rate sorcerer %l sends to do his bidding. +I have slain many before you. You shall give me little sport. + +"Prepare to die, %c." +%E +%Cp Bar 00051 +"I have wasted too much time on you already. Now, you shall die." +%E +%Cp Bar 00052 +"You return yet again, %c! Are you prepared for death now?" +%E +%Cp Bar 00053 +"I shall have %o back, you pitiful excuse for %ca. +And your life as well." +%E +%Cp Bar 00060 +"My pets will dine on your carcass tonight!" +%E +%Cp Bar 00061 +"You are a sorry excuse for %ra." +%E +%Cp Bar 00062 +"Run while you can, %c. My next spell will be your last." +%E +%Cp Bar 00063 +"I shall use your very skin to bind my next grimoire." +%E +%Cp Bar 00064 +"%d cannot protect you now. Here, you die." +%E +%Cp Bar 00065 +"Your %a nature makes you weak. You cannot defeat me." +%E +%Cp Bar 00066 +"Come, %c. I shall kill you, then unleash the horde on your tribe." +%E +%Cp Bar 00067 +"Once you are dead, my horde shall finish off %l, and your tribe." +%E +%Cp Bar 00068 +"Fight, %c, or are you afraid of the mighty %n?" +%E +%Cp Bar 00069 +"You have failed, %c. Now, my victory is complete." +%E +%Cc Bar 00070 +As you pick up %o, you feel the power of it +flowing through your hands. It seems to be in two or more places +at once, even though you are holding it. +%E +%Cc Bar 00080 +%nC falls to the ground, and utters a last curse at you. Then his +body fades slowly, seemingly dispersing into the air around you. You +slowly become aware that the overpowering aura of magic in the air has +begun to fade. +%E +%Cc Bar 00081 +When %l sees %o, he smiles, and says: + + Well done, %p. You have saved the world from certain doom. + What, now, should be done with %o? + + These people, brave as they are, cannot hope to guard it from + other sorcerers who will detect it, as surely as %n did. + + Take %o with you, %p. It will guard you in + your adventures, and you can best guard it. You embark on a + quest far greater than you realize. + + Remember me, %p, and return when you have triumphed. I + will tell you then of what you must do. You will understand when the + time comes. +%E +%Cc Bar 00082 +%l gazes reverently at %o, then back at you. + +"You are its keeper now, and the time has come to resume your search +for the Amulet. %Z await your return through the +magic portal which brought you here." +%E +%Cp Bar 00090 +"Tell us, %p, have you fared well on your great quest?" +%E +%Cc Bar 00091 +"This is wondrous, %p. I feared that you could not possibly +succeed in your quest, but here you are in possession of the Amulet +of Yendor! + +"I have studied the texts of the magi constantly since you left. In +the Book of Skelos, I found this: + + %d will cause a child to be sent into the world. This child is to + be made strong by trial of battle and magic, for %d has willed it so. + It is said that the child of %d will recover the Amulet of Yendor + that was stolen from the Creator at the beginning of time. + +"As you now possess the amulet, %p, I suspect that the Book +speaks of you. + + The child of %d will take the Amulet, and travel to the Astral + Plane, where the Great Temple of %d is to be found. The Amulet + will be sacrificed to %d, there on His altar. Then the child will + stand by %d as champion of all %cP for eternity. + +"This is all I know, %p. I hope it will help you." +%E +# +# Cave(wo)man +# +%Cc Cav 00001 +You descend through a barely familiar stairwell that you remember +%l showing you when you embarked upon your vision quest. + +You arrive back at %H, but something seems +wrong here. The usual smoke and glowing light of the fires of the +outer caves are absent, and an uneasy quiet fills the damp air. +%E +%Cp Cav 00002 +Once again, you arrive back at %H. +%E +%Cp Cav 00003 +For some reason, you think that this may be the last time you will +enter %H. +%E +%Cp Cav 00005 +"We have not been able to gather as much food since the Giants sealed +off our access to the outer world." +%E +%Cp Cav 00006 +"Since %n sent her minions, we have been constantly fighting." +%E +%Cp Cav 00007 +"I have heard your vision quest was successful. Is this so?" +%E +%Cp Cav 00008 +"So, tell me, %p, how have you fared?" +%E +%Cp Cav 00009 +"%lC grows old. We know not who will guide us after he ascends." +%E +%Cp Cav 00010 +"The rains have returned and the land grows lush again." +%E +%Cp Cav 00011 +"Peace has returned, give thanks to %d!" +%E +%Cp Cav 00012 +"Welcome back! Did you find %o?" +%E +%Cp Cav 00013 +"So, %p, tell us the story of your fight with %n." +%E +%Cp Cav 00014 +"%lC grows old. Perhaps you will guide us after he ascends." +%E +%Cc Cav 00015 +"You have returned from your vision quest, %p. Thank %d. + +"We are in dire need of your help, my %S. + +"But first, I must see if you are yet capable of the quest I would +ask you to undertake." +%E +%Cp Cav 00016 +"Again, you return to us, %p. Let me see if you are ready now." +%E +%Cp Cav 00017 +"Ah, %p. Are you finally ready?" +%E +%Cc Cav 00018 +"%pC! You have sealed our fate. You seem unable to reform yourself, +so I must select another to take your place. + +"Begone from %H! You have betrayed us by choosing +the path of the %C over the true path of the %L. + +"You no longer live in our eyes." +%E +%Cc Cav 00019 +"Alas, %p, you are as yet too inexperienced to embark upon such +a difficult quest as that I propose to give you. + +"%rA could not possibly survive the rigors demanded to find +%i, never mind to confront %n herself. + +"Adventure some more, and you will learn the skills you will require. +%d decrees it." +%E +%Cc Cav 00020 +"%pC! You have deviated from my teachings. You no longer follow +the path of the %a as you should. I banish you from these caves, to +go forth and purify yourself. Then, you might be able to accomplish this +quest." +%E +%Cc Cav 00021 +"You are indeed ready now, %p. I shall tell you a tale of +great suffering among your people: + +"Shortly after you left on your vision quest, the caves were invaded by +the creatures sent against us by %n. + +"She, herself, could not attack us due to her great size, but her minions +have harassed us ever since. In the first attacks, many died, and the +minions of %n managed to steal %o. +They took it to %i and there, none of our +%g warriors have been able to go. + +"You must find %i, and within it wrest +%o from %n. She guards it as +jealously as she guards all treasures she attains. But with it, +we can make our caves safe once more. + +"Please, %p, recover %o for us, and return it here." +%E +%Cp Cav 00025 +"%nC is immune to her own breath weapons. +You should use magic upon her that she does not use herself." +%E +%Cp Cav 00026 +"When you encounter %n, call upon %d for assistance." +%E +%Cp Cav 00027 +"There will be nowhere to hide inside %ns inner sanctum." +%E +%Cp Cav 00028 +"Your best chance with %n will be to keep moving." +%E +%Cp Cav 00029 +"Do not be distracted by the great treasures in %ns lair. +Concentrate on %o." +%E +%Cp Cav 00030 +"%oC is the only object that %n truly fears." +%E +%Cp Cav 00031 +"Do not be fooled by %ns size. She is fast, and it is +rumored that she uses magic." +%E +%Cp Cav 00032 +"I would send a party of %gP with you, but we will need all +of our strength to defend ourselves." +%E +%Cp Cav 00033 +"Remember, be %a at all times. This is your strength." +%E +%Cp Cav 00034 +"If only we had an amulet of reflection, this would not have happened." +%E +%Cc Cav 00035 +You %x many large claw marks on the ground. The tunnels ahead +of you are larger than most of those in any cave complex you have +ever been in before. + +Your nose detects the smell of carrion from within, and bones litter +the sides of the tunnels. +%E +%Cp Cav 00036 +Once again, you approach %i. +%E +%Cc Cav 00040 +You find yourself in a large cavern, with neatly polished walls, that +nevertheless show signs of being scorched by fire. + +Bones litter the floor, and there are objects scattered everywhere. +The air is close with the stench of sulphurous fumes. + +%nC is clearly visible, but she seems to be asleep. +%E +%Cp Cav 00041 +Once again, you find yourself in the lair of %n. +%E +%Cc Cav 00050 +"So, follower of %l, you seek to invade the lair of %n. +Only my meals are allowed down here. Prepare to be eaten!" +%E +%Cp Cav 00051 +"So, again you face me, %c. No one has ever before escaped me. +Now I shall kill you." +%E +%Cp Cav 00052 +"You are getting annoying, %c. Prepare to die." +%E +%Cp Cav 00053 +"I'll have %o from you, %c. You shall die." +%E +%Cp Cav 00060 +"You are weak, %c. No challenge for the Mother of all Dragons." +%E +%Cp Cav 00061 +"I grow hungry, %r. You look like a nice appetizer!" +%E +%Cp Cav 00062 +"Join me for lunch? You're the main course, %c." +%E +%Cp Cav 00063 +"With %o, I am invincible! You cannot succeed." +%E +%Cp Cav 00064 +"Your mentor, %l has failed. You are nothing to fear." +%E +%Cp Cav 00065 +"You shall die here, %c. %rA cannot hope to defeat me." +%E +%Cp Cav 00066 +"You, a mere %r challenge the might of %n? Hah!" +%E +%Cp Cav 00067 +"I am the Mother of all Dragons! You cannot hope to defeat me." +%E +%Cp Cav 00068 +"My claws are sharp now. I shall rip you to shreds!" +%E +%Cp Cav 00069 +"%d has deserted you, %c. This is my domain." +%E +%Cc Cav 00070 +As you pick up %o it seems heavy at first, but as you +hold it strength flows into your arms. + +You suddenly feel full of power, as if nothing could possibly stand +in your path. +%E +%Cp Cav 00080 +%nC sinks to the ground, her heads flailing about. +As she dies, a cloud of noxious fumes billows about her. +%E +%Cc Cav 00081 +%lC glimpses %o in your possession. +He smiles and says: + + You have done it! We are saved. But I fear that %o + will always be a target for %C forces who will want it for their + own. + + To prevent further trouble, I would like you, %p, + to take %o away with you. It will help you as you + quest for the Amulet of Yendor. +%E +%Cc Cav 00082 +%l grasps %o proudly for a moment, then looks at you. + +"You are its keeper now, and the time has come to resume your search +for the Amulet. %Z await your return through the +magic portal which brought you here." +%E +%Cp Cav 00090 +"%pC! Welcome back. +How goes your quest to recover the Amulet for %d?" +%E +%Cc Cav 00091 +"You have been successful, I see, %p. + +"Now that the Amulet of Yendor is yours, here is what you must do: + +"Journey upwards to the open air. The Amulet you carry will then +take you into the Astral Planes, where the Great Temple of %d +casts its influence throughout our world. + +"Sacrifice the Amulet on the altar. Thus shall %d become supreme!" +%E +# +# Healer +# +%Cc Hea 00001 +What sorcery has brought you back to %H? The smell +of fresh funeral pyres tells you that something is amiss with the healing +powers that used to practice here. + +No rhizotomists are tending the materia medica gardens, and where are the +common folk who used to come for the cures? + +You know that you must quickly make your way to the collegium, and +%ls iatreion, and find out what has happened in your +absence. +%E +%Cp Hea 00002 +After your last experience you expected to be here, but you certainly +did not expect to see things so much worse. This time you must +succeed. +%E +%Cp Hea 00003 +Again, you %x %H in the distance. + +The smell of death and disease permeates the air. You do not have +to be %Ra to know that %n is on the verge of victory. +%E +%Cp Hea 00005 +"Did you read that new treatise on the therapeutic use of leeches?" +%E +%Cp Hea 00006 +"Paint a red caduceus on your shield and monsters won't hit you." +%E +%Cp Hea 00007 +"I passed handwriting so they are demoting me a rank." +%E +%Cp Hea 00008 +"I've heard that even %l has not been able to cure Chiron." +%E +%Cp Hea 00009 +"We think %n has used his alchemists, and %o, +to unleash a new disease we call 'the cold' on Gehennom." +%E +%Cp Hea 00010 +"Did you read that new treatise on the therapeutic use of leeches?" +%E +%Cp Hea 00011 +"Paint a red caduceus on your shield and monsters won't hit you." +%E +%Cp Hea 00012 +"How are you feeling? Perhaps a good bleeding will improve your sprits." +%E +%Cp Hea 00013 +"Have you heard the absurd new theory that diseases are caused by +microscopic organisms, and not ill humors?" +%E +%Cp Hea 00014 +"I see that you bring %o, now you can cure this plague!" +%E +%Cc Hea 00015 +Feebly, %l raises his head to look at you. + +"It is good to see you again, %p. I see the concern in your +eyes, but do not worry for me. I am not ready for Hades yet. We have +exhausted much of our healing powers holding off %n. +I need your fresh strength to carry on our work. + +"Come closer and let me lay hands on you, and determine if you have +the skills necessary to accomplish this mission." +%E +%Cp Hea 00016 +"Again you return to me, %p. I sense that each trip back +the pleurisy and maladies of our land begin to infect you. Let us +hope and pray to %d that you become ready for your task before +you fall victim to the bad humors." +%E +%Cp Hea 00017 +"Chiron has fallen, Hermes has fallen, what else must I tell you to +impress upon you the importance of your mission! I hope that you +have come prepared this time." +%E +%Cc Hea 00018 +"You have failed us, %p. You are a quack! A charlatan! + +"Hades will be happy to hear that you are once again practicing your +arts on the unsuspecting." +%E +%Cc Hea 00019 +"Alas, %p, you are yet too inexperienced to deal with the rigors +of such a task. You must be able to draw on the knowledge of botany, +vetenary, and alchemy before I can send you on this quest with good +conscience. + +"Return when you wear %Ra's caduceus." +%E +%Cc Hea 00020 +"You have learned much of the remedies that benefit, but you must also +know which physic for which ail. That is why %ds teachings are a +part of your training. + +"Return to us when you have healed thyself." +%E +%Cc Hea 00021 +For the first time, you sense a smile on %ls face. + + You have indeed learned as much as we can teach you in preparation + for this task. Let me tell you what I know of the symptoms and hope + that you can provide a cure. + + A short while ago, the dreaded %nt was fooled by the gods + into thinking that he could use %o to find a + cure for old age. Think of it, eternal youth! But his good + health is accomplished by drawing the health from those around him. + + He has exhausted his own supply of healthy people and now he seeks to + extend his influence into our world. You must recover from him + %o and break the spell. + + You must travel into the swamps to %i, and from there + follow the trail to %ns island lair. Be careful. +%E +%Cp Hea 00025 +"Remember, %p, to always wash your hands before operating." +%E +%Cp Hea 00026 +"%nC has no real magic of his own. To this he is vulnerable." +%E +%Cp Hea 00027 +"If you have been true to %d, you can draw on the power of +%o." +%E +%Cp Hea 00028 +"Bring with you antidotes for poisons." +%E +%Cp Hea 00029 +"Remember this, %n can twist the powers of %o +to hurt instead of heal." +%E +%Cp Hea 00030 +"I have sent for Chiron, but I am afraid he will come too late." +%E +%Cp Hea 00031 +"Maybe when you return the snakes will once again begin to shed." +%E +%Cp Hea 00032 +"The plague grows worse as we speak. Hurry, %p!" +%E +%Cp Hea 00033 +"Many times %n has caused trouble in these lands. It is +time that he was eradicated like the diseases he has caused." +%E +%Cp Hea 00034 +"With but one eye, %n should be easy to blind. Remember this." +%E +%Cc Hea 00035 +You stand before the entrance to %i. Strange +scratching noises come from within the building. + +The swampy ground around you seems to stink with disease. +%E +%Cp Hea 00036 +Once again you stand at the entrance to %i. +%E +%Cc Hea 00040 +You stand within sight of the infamous Isle of %n. Even +the words of %l had not prepared you for this. + +Steeling yourself against the wails of the ill that pierce your ears, +you hurry on your task. Maybe with %o you can +heal them on your return, but not now. +%E +%Cp Hea 00041 +Once again, you %x the Isle of %n in the distance. +%E +%Cc Hea 00050 +"They have made a mistake in sending you, %p. + +"When I add your youth to mine, it will just make it easier for me +to defeat %l." +%E +%Cp Hea 00051 +"Unlike your patients, you seem to keep coming back, %p!" +%E +%Cp Hea 00052 +"Which would you like, %p? Boils, pleurisy, convulsions?" +%E +%Cp Hea 00053 +"I'll have %o back from you, %r. You are +not going to live to escape this place." +%E +%Cp Hea 00060 +"They might as well give scalpels to wizards as to let you try to +use %o!" +%E +%Cp Hea 00061 +"If I could strike %l, surrounded by his %gP, imagine what I +can do to you here by yourself." +%E +%Cp Hea 00062 +"I will put my %Rp to work making a physic out of your ashes." +%E +%Cp Hea 00063 +"As we speak, Hades gathers your patients to join you." +%E +%Cp Hea 00064 +"After I'm done with you, I'll destroy %l as well." +%E +%Cp Hea 00065 +"You will have to kill me if you ever hope to leave this place." +%E +%Cp Hea 00066 +"I will impale your head on my caduceus for all to see." +%E +%Cp Hea 00067 +"There is no materia medica in your sack which will cure you of me!" +%E +%Cp Hea 00068 +"Do not fight too hard, I want your soul strong, not weakened!" +%E +%Cp Hea 00069 +"You should have stopped studying at vetenary." +%E +%Cc Hea 00070 +As you pick up %o, you feel its healing begin to +warm your soul. You curse Zeus for taking it from its rightful owner, +but at least you hope that %l can put it to good use once +again. +%E +%Cc Hea 00080 +The battered body of %n slumps to the ground and gasps +out one last curse: + + You have defeated me, %p, but I shall have my revenge. + How, I shall not say, but this curse shall be like a + cancer on you. + +With that %n dies. +%E +%Cc Hea 00081 +As soon as %l sees %o he summons his %gP. + +Gently, %l reaches out and touches %o. +He instructs each of the assembled to do the same. When everyone +has finished he speaks to you. + + Now that we have been replenished we can defeat this plague. You must + take %o with you and replenish the worlds you have + been called upon to travel next. I wish you could ride Chiron to the + end of your journey, but I need him to help me spread the cure. Go + now and continue your journey. +%E +%Cc Hea 00082 +%l cautiously handles %o while watching you. + +"You are its keeper now, and the time has come to resume your search +for the Amulet. %Z await your return through the +magic portal which brought you here." +%E +%Cp Hea 00090 +"You have again returned to us, %p. We have done well in your +absence, yes? How fare you upon your quest for the Amulet?" +%E +%Cc Hea 00091 +"Ah, you have recovered the Amulet, %p. Well done! + +"Now, you should know that you must travel through the elemental planes +to the astral, and there return the Amulet to %d. Go forth and +may our prayers be as a wind upon your back." +%E +# +# Knight +# +%Cc Kni 00001 +You materialize in the shadows of %H. Immediately, you notice +that something is wrong. The fields around the castle are trampled and +withered, as if some great battle has been recently fought. + +Looking closer, you %x long gouges in the walls of %H. +You know of only one creature that makes those kinds of marks... +%E +%Cp Kni 00002 +Once again you stand in the shadows of %H. +%E +%Cp Kni 00003 +Again, you stand before %H. You vaguely sense that this +may be the last time you stand before %l. +%E +%Cp Kni 00005 +"Hail, %p! Verily, thou lookest well." +%E +%Cp Kni 00006 +"There is word, %p, that %n hath been sighted in the fens +near %i." +%E +%Cp Kni 00007 +"Thou art our only hope now, %p." +%E +%Cp Kni 00008 +"Verily, %l could have no better champion, %p." +%E +%Cp Kni 00009 +"Many brave %cP died when %n attacked." +%E +%Cp Kni 00010 +"Hail, %p! Verily, thou lookest well." +%E +%Cp Kni 00011 +"So, %p, didst thou find %n in the fens +near %i?" +%E +%Cp Kni 00012 +"Worthy %p, hast thou proven thy right purpose on the body of %n?" +%E +%Cp Kni 00013 +"Verily, %l could have no better champion, %p." +%E +%Cp Kni 00014 +"Hast thou indeed recovered %o?" +%E +%Cc Kni 00015 +"Ah, %p. We see thou hast received Our summons. +We are in dire need of thy prowess. But first, We must needs +decide if thou art ready for this great undertaking." +%E +%Cp Kni 00016 +"Welcome again, %p. We hope thou art ready now." +%E +%Cp Kni 00017 +"Once again, thou standest before Us, %p. Art thou ready now?" +%E +%Cc Kni 00018 +"Thou disgracest this noble court with thine impure presence. We have been +lenient with thee, but no more. Thy name shall be spoken no more. We +hereby strip thee of thy title, thy lands, and thy standing as %ca. +Begone from Our sight!" +%E +%Cc Kni 00019 +"Verily, %p, thou hast done well. That thou hast survived thus +far is a credit to thy valor, but thou art yet unprepared for +the demands required as Our Champion. %rA, no matter how +pure, could never hope to defeat the foul %n. + +"Journey forth from this place, and hone thy skills. Return to +Our presence when thou hast attained the noble title of %R." +%E +%Cc Kni 00020 +"Thou dishonourest Us, %p! Thou hast strayed from the path of +chivalry! Go from Our presence and do penance. Only when thou art again +pure mayst thou return hence." +%E +%Cc Kni 00021 +"Ah, %p. Thou art truly ready, as no %c before thee hath +been. Hear now Our words: + +"As thou noticed as thou approached %H, a great battle hath +been fought recently in these fields. Know thou that Merlin himself +came to aid Us here as We battled the foul %n. In the midst of that +battle, %n struck Merlin a great blow, felling him. Then, as Our +forces were pressed back, %n stole %o. + +"We eventually turned the tide, but lost many %cP in doing so. +Merlin was taken off by his apprentice, but hath not recovered. We have +been told that so long as %n possesseth %o, +Merlin will not regain his health. + +"We hereby charge thee with this most important of duties: + +"Go forth from this place, to the fens, and there thou wilt find +%i. From there, thou must track down %n. Destroy the +beast, and return to Us %o. Only then can +We restore Merlin to health." +%E +%Cp Kni 00025 +"Remember, %p, follow always the path of %d." +%E +%Cp Kni 00026 +"Though %n is verily a mighty foe, We have confidence in thy victory." +%E +%Cp Kni 00027 +"Beware, for %n hath surrounded himself with hordes of foul creatures." +%E +%Cp Kni 00028 +"Great treasure, 'tis said, is hoarded in the lair of %n." +%E +%Cp Kni 00029 +"If thou possessest %o, %p, %ns magic +shall therewith be thwarted." +%E +%Cp Kni 00030 +"The gates of %i are guarded by forces unseen, %p. +Go carefully." +%E +%Cp Kni 00031 +"Return %o to Us quickly, %p." +%E +%Cp Kni 00032 +"Destroy %n, %p, else %H shall surely fall." +%E +%Cp Kni 00033 +"Call upon %d when thou art in need." +%E +%Cp Kni 00034 +"To find %i, thou must keep thy heart pure." +%E +%Cc Kni 00035 +You stand at the foot of %i. Atop, you can %x a shrine. +Strange energies seem to be focused here, and the hair on the back +of your neck stands on end. +%E +%Cp Kni 00036 +Again, you stand at the foot of %i. +%E +%Cc Kni 00040 +As you exit the swamps, you %x before you a huge, gaping hole in the +side of a hill. From within, you smell the foul stench of carrion. + +The pools on either side of the entrance are fouled with blood, and +pieces of rusted metal and broken weapons show above the surface. +%E +%Cp Kni 00041 +Again, you stand at the entrance to %ns lair. +%E +%Cc Kni 00050 +"Hah! Another puny %c seeks death. I shall dine well tonight, +then tomorrow, %H shall fall!" +%E +%Cp Kni 00051 +"Again, thou challengest me, %r? So be it. Thou wilt die here." +%E +%Cp Kni 00052 +"Thou art truly foolish, %r. I shall dispatch thee anon." +%E +%Cp Kni 00053 +"So, thou darest touch MY property! I shall have that bauble back, +puny %r. Thou wilt die in agony!" +%E +%Cp Kni 00060 +"A mere %r can never withstand me!" +%E +%Cp Kni 00061 +"I shall kill thee now, and feast!" +%E +%Cp Kni 00062 +"Puny %c. What manner of death dost thou wish?" +%E +%Cp Kni 00063 +"First thee, %p, then I shall feast upon %l." +%E +%Cp Kni 00064 +"Hah! Thou hast failed, %r. Now thou shalt die." +%E +%Cp Kni 00065 +"Die, %c. Thou art as nothing against my might." +%E +%Cp Kni 00066 +"I shall suck the marrow from thy bones, %c." +%E +%Cp Kni 00067 +"Let's see... Broiled? No. Fried? Nay. Baked? Yea verily, +that is the way I like my %c for dinner." +%E +%Cp Kni 00068 +"Thy strength waneth, %p. The time of thy death draweth near." +%E +%Cp Kni 00069 +"Call upon thy precious %d, %p. It shall not avail thee." +%E +%Cc Kni 00070 +As you pick up %o, you feel its protective fields +form around your body. You also feel a faint stirring in your mind, +as if you are in two places at once, and in the second, you are waking +from a long sleep. +%E +%Cc Kni 00080 +As %n sinks to the ground, blood gushing from his open mouth, he +defiantly curses you and %l: + + Thou hast not won yet, %r. By the gods, I shall return and dog + thy steps to the grave! + +His tail flailing madly, %n tries to crawl towards you, but slumps +to the ground and dies in a pool of his own blood. +%E +%Cc Kni 00081 +As you approach %l, he beams at you and says: + + Well done! Thou art truly the Champion of %H. We + have received word that Merlin is recovering, and shall soon + rejoin Us. + + He hath instructed Us that thou art now to be the guardian of + %o. He feeleth that thou mayst have need of + its powers in thine adventures. It is Our wish that thou keepest + %o with thee as thou searchest for the fabled + Amulet of Yendor. +%E +# assumes Magic Mirror of Merlin (glass object) +%Cc Kni 00082 +"Careful, %p! %oC might break, and that would +be a tragic loss. Thou art its keeper now, and the time hath come +to resume thy search for the Amulet. %Z await thy +return through the magic portal that brought thee here." +%E +%Cp Kni 00090 +"Well met, %p. How goeth thy search for the Amulet of Yendor?" +%E +%Cc Kni 00091 +"Thou hast succeeded, We see, %p! Now thou art commanded to take +the Amulet to be sacrificed to %d in the plane of the astral. + +"Merlin hath counseled Us that thou must travel always upwards through +the planes of the elements, to achieve this goal. + +"Go with %d, %p." +%E +# +# Monk +# +# The quest artifact is "The Eyes of the Overworld", hence needs +# to be treated as plural by messages which use %o. +# +%Cc Mon 00001 +You find yourself standing in sight of %H. Something +is obviously wrong here. Strange shapes lumber around +outside %H! + +You realize that the %l needs your assistance! +%E +%Cp Mon 00002 +Once again, you stand before %H. +%E +%Cp Mon 00003 +Again you face %H. Your intuition hints that this may be +the final time you come here. +%E +%Cp Mon 00005 +"Greetings, honorable %r. It is good to see you." +%E +%Cp Mon 00006 +"Ah, %p! Surely you can help us in our hour of need." +%E +%Cp Mon 00007 +"Greetings, %s. %lC has great need of your help." +%E +%Cp Mon 00008 +"Alas, it seems as if even %d has deserted us." +%E +%Cp Mon 00009 +"May %d be with you, %s." +%E +%Cp Mon 00010 +"Greetings, honorable %r. It is good to see you again." +%E +%Cp Mon 00011 +"Ah, %p! Our deepest gratitude for all of your help." +%E +%Cp Mon 00012 +"Greetings, %s. Perhaps you will take some time to meditate with us?" +%E +%Cp Mon 00013 +"With this test behind you, may %d bring you enlightenment." +%E +%Cp Mon 00014 +"May %d be with you, %s." +%E +%Cc Mon 00015 +"Ah, %p, my %S. You have returned to us at last. +A great blow has befallen our order; perhaps you can help us. +First, however, I must determine if you are prepared for this +great challenge." +%E +%Cp Mon 00016 +"Again, my %S, you stand before me. Are you ready now to help us?" +%E +%Cp Mon 00017 +"Once more, %p, you stand within the sanctum. Are you ready now?" +%E +%Cc Mon 00018 +"You are a heretic, %p! How can you, %ra, deviate so from the +teachings of %d? Begone from this temple. You are no longer +%sa to this order. We will pray to %d for other assistance, +as you have failed us utterly." +%E +%Cc Mon 00019 +"Alas, %p, it is not yet to be. A mere %r could never +withstand the might of %n. Go forth, again into the world, and return +when you have attained the post of %R." +%E +%Cc Mon 00020 +"This is terrible, %p. You have deviated from the true path! +You know that %d requires the most strident devotion of this +order. The %shood must stand for utmost piety. + +"Go from here, atone for your sins against %d. Return only when +you have purified yourself." +%E +%Cc Mon 00021 +"Yes, %p. You are truly ready now. Attend to me and I shall +tell you of what has transpired: + +"During one of the Great Meditations a short time ago, %n and +a legion of elementals invaded %H. Many %gP were +killed, including the one bearing %o. + +Now, there are barely enough %gP left to keep the +elementals at bay. + +"We need you to find %i, then, from there, travel +to %ns lair. If you can manage to defeat %n +and return %o here, we can then drive off the +legions of elementals that slay our students. + +"Go with %d as your guide, %p." +%E +%Cp Mon 00025 +"You can prevail, if you rely on %d." +%E +%Cp Mon 00026 +"Remember that %n has great magic at his command." +%E +%Cp Mon 00027 +"Be pure, my %S." +%E +%Cp Mon 00028 +"Beware, %i is surrounded by hordes of earth elementals." +%E +%Cp Mon 00029 +"Remember your studies, and you will prevail!" +%E +%Cp Mon 00030 +"Acquire and wear %o if you can. They will aid you +against %n." +%E +%Cp Mon 00031 +"Call upon %d when your need is greatest. You will be answered." +%E +%Cp Mon 00032 +"Remember to use the elementals' strength against them!" +%E +%Cp Mon 00033 +"Do not lose faith, %p. If you do so, %n will grow stronger." +%E +%Cp Mon 00034 +"Wear %o. They will assist you in your efforts." +%E +%Cc Mon 00035 +You remember the descriptions of %i, given to you +by the %l. It is ahead that you will find %n's trail. +%E +%Cp Mon 00036 +Again, you stand before %i. +%E +%Cc Mon 00040 +The stench of brimstone is all about you, and the elementals close in +from all sides! + +Ahead, there is a small clearing amidst the bubbling pits of lava... +%E +%Cp Mon 00041 +Again, you have invaded %ns domain. +%E +%Cc Mon 00050 +"Ah, so %l has sent another %g to retrieve +%o. + +"No, I see you are no %g. Perhaps I shall have some fun today +after all. Prepare to die, %r! You shall never regain +%o." +%E +%Cp Mon 00051 +"So, %r. Again you challenge me." +%E +%Cp Mon 00052 +"Die now, %r. %d has no power here to aid you." +%E +%Cp Mon 00053 +"You shall die, %r, and I will have %o back." +%E +%Cp Mon 00060 +"Submit to my will, %c, and I shall spare you." +%E +%Cp Mon 00061 +"Your puny powers are no match for me, %c." +%E +%Cp Mon 00062 +"I shall have you turned into a zombie for my pleasure!" +%E +%Cp Mon 00063 +"Despair now, %r. %d cannot help you." +%E +%Cp Mon 00064 +"I shall feast upon your soul for many days, %c." +%E +%Cp Mon 00065 +"Your death will be slow and painful. That I promise!" +%E +%Cp Mon 00066 +"You cannot defeat %n, you fool. I shall kill you now." +%E +%Cp Mon 00067 +"Your precious %lt will be my next victim." +%E +%Cp Mon 00068 +"I feel your powers failing you, %r. You shall die now." +%E +%Cp Mon 00069 +"With %o, nothing can stand in my way." +%E +%Cc Mon 00070 +As you pick up %o, you feel the essence of +%d fill your soul. You know now why %n stole them from +%H, for with them, %ca of %d could +easily defeat his plans. + +You sense a message from %d. Though not verbal, you +get the impression that you must return to %l as soon +as possible. +%E +%Cc Mon 00080 +%nC gasps: + + "You have only defeated this mortal body. Know this: my spirit + is strong. I shall return and reclaim what is mine!" + +With that, %n expires. +%E +%Cc Mon 00081 +"You have returned, %p. And with %o, I see. +Congratulations. + +"I have been in meditation, and have received direction from +a minion of %d. %d commands that you retain +%o. With them, you must recover the Amulet +of Yendor. + +"Go forth, and let %d guide your steps." +%E +%Cc Mon 00082 +%lC studies %o for a moment, +then returns his gaze to you. + +"%oC must remain with you. Use them +as you resume your search for the Amulet. +%Z await your return through the magic portal +that brought you here." +%E +%Cp Mon 00090 +"Welcome back, %p. How is your quest for the Amulet going?" +%E +%Cc Mon 00091 +"You have prevailed, %p! %d is surely with you. Now, +you must take the amulet, and sacrifice it on %ds altar on +the Astral plane. I suspect that I shall never see you again in this +life, but I hope to at %ds feet." +%E +# +# Priest +# +%Cc Pri 00001 +You find yourself standing in sight of %H. Something +is obviously wrong here. The doors to %H, which usually +stand open, are closed. Strange human shapes shamble around +outside. + +You realize that %l needs your assistance! +%E +%Cp Pri 00002 +Once again, you stand before %H. +%E +%Cp Pri 00003 +Again you face %H. Your intuition hints that this may be +the final time you come here. +%E +%Cp Pri 00005 +"Greetings, honored %r. It is good to see you." +%E +%Cp Pri 00006 +"Ah, %p! Surely you can help us in our hour of need." +%E +%Cp Pri 00007 +"Greetings, %s. %lC has great need of your help." +%E +%Cp Pri 00008 +"Alas, it seems as if even %d has deserted us." +%E +%Cp Pri 00009 +"May %d be with you, %s." +%E +%Cp Pri 00010 +"Greetings, %r. It is good to see you again." +%E +%Cp Pri 00011 +"Ah, %p! Our deepest gratitude for all of your help." +%E +%Cp Pri 00012 +"Welcome back, %s! With %o, no undead can stand against us." +%E +%Cp Pri 00013 +"Praise be to %d, for delivering us from %n." +%E +%Cp Pri 00014 +"May %d be with you, %s." +%E +%Cc Pri 00015 +"Ah, %p, my %S. You have returned to us at last. +A great blow has befallen our order; perhaps you can help us. +First, however, I must determine if you are prepared for this +great challenge." +%E +%Cp Pri 00016 +"Again, my %S, you stand before me. Are you ready now to help us?" +%E +%Cp Pri 00017 +"Once more, %p, you stand within the sanctum. Are you ready now?" +%E +%Cc Pri 00018 +"You are a heretic, %p! How can you, %ra, deviate so from the +teachings of %d? Begone from this temple. You are no longer +%sa to this order. We will pray to %d for other assistance, +as you have failed us utterly." +%E +%Cc Pri 00019 +"Alas, %p, it is not yet to be. A mere %r could never +withstand the might of %n. Go forth, again into the world, and return +when you have attained the post of %R." +%E +%Cc Pri 00020 +"This is terrible, %p. You have deviated from the true path! +You know that %d requires the most strident devotion of this +order. The %shood must stand for utmost piety. + +"Go from here, atone for your sins against %d. Return only when +you have purified yourself." +%E +%Cc Pri 00021 +"Yes, %p. You are truly ready now. Attend to me and I shall +tell you of what has transpired: + +"At one of the Great Festivals a short time ago, %n and a legion +of undead invaded %H. Many %gP were killed, including +the one carrying %o. + +"As a final act of vengefulness, %n desecrated the altar here. +Without it, we could not mount a counter-attack. Now, there are +barely enough %gP left to keep the undead at bay. + +"We need you to find %i, then, from there, travel +to %ns lair. If you can manage to defeat %n and return +%o here, we can then drive off the legions of +undead that befoul the land. + +"Go with %d as your guide, %p." +%E +%Cp Pri 00025 +"You can prevail, if you rely on %d." +%E +%Cp Pri 00026 +"Remember that %n has great magic at his command." +%E +%Cp Pri 00027 +"Be pure, my %S." +%E +%Cp Pri 00028 +"Beware, %i is surrounded by a great graveyard." +%E +%Cp Pri 00029 +"You may be able to affect %n with magical cold." +%E +%Cp Pri 00030 +"Acquire and wear %o if you can. It will aid you +against %n." +%E +%Cp Pri 00031 +"Call upon %d when your need is greatest. You will be answered." +%E +%Cp Pri 00032 +"The undead legions are weakest during the daylight hours." +%E +%Cp Pri 00033 +"Do not lose faith, %p. If you do so, %n will grow stronger." +%E +%Cp Pri 00034 +"Wear %o. It will assist you against the undead." +%E +%Cc Pri 00035 +You stand facing a large graveyard. The sky above is filled with clouds +that seem to get thicker closer to the center. You sense the presence of +undead in larger numbers than you have ever encountered before. + +You remember the descriptions of %i, given to you by +%lC. It is ahead that you will find %ns trail. +%E +%Cp Pri 00036 +Again, you stand before %i. +%E +%Cc Pri 00040 +The stench of brimstone is all about you, and the shrieks and moans +of tortured souls assault your psyche. + +Ahead, there is a small clearing amidst the bubbling pits of lava... +%E +%Cp Pri 00041 +Again, you have invaded %ns domain. +%E +%Cc Pri 00050 +"Ah, so %lC has sent another %g to retrieve +%o. + +"No, I see you are no %g. Perhaps I shall have some fun today +after all. Prepare to die, %r! You shall never regain +%o." +%E +%Cp Pri 00051 +"So, %r. Again you challenge me." +%E +%Cp Pri 00052 +"Die now, %r. %d has no power here to aid you." +%E +%Cp Pri 00053 +"You shall die, %r, and I will have %o back." +%E +%Cp Pri 00060 +"Submit to my will, %c, and I shall spare you." +%E +%Cp Pri 00061 +"Your puny powers are no match for me, %c." +%E +%Cp Pri 00062 +"I shall have you turned into a zombie for my pleasure!" +%E +%Cp Pri 00063 +"Despair now, %r. %d cannot help you." +%E +%Cp Pri 00064 +"I shall feast upon your soul for many days, %c." +%E +%Cp Pri 00065 +"Your death will be slow and painful. That I promise!" +%E +%Cp Pri 00066 +"You cannot defeat %n, you fool. I shall kill you now." +%E +%Cp Pri 00067 +"Your precious %lt will be my next victim." +%E +%Cp Pri 00068 +"I feel your powers failing you, %r. You shall die now." +%E +%Cp Pri 00069 +"With %o, nothing can stand in my way." +%E +%Cc Pri 00070 +As you pick up %o, you feel the essence of +%d fill your soul. You know now why %n stole it from +%H, for with it, %ca of %d could +easily defeat his plans. + +You sense a message from %d. Though not verbal, you +get the impression that you must return to %lC as soon +as possible. +%E +%Cc Pri 00080 +You feel a wrenching shift in the ether as %ns body dissolves +into a cloud of noxious gas. + +Suddenly, a voice booms out: + + Thou hast defeated the least of my minions, %r. + Know now that Moloch is aware of thy presence. + As for thee, %n, I shall deal with thy failure + at my leisure. + +You then hear the voice of %n, screaming in terror... +%E +%Cc Pri 00081 +"You have returned, %p. And with %o, I see. +Congratulations. + +"I have been in meditation, and have received direction from +a minion of %d. %d commands that you retain +%o. With it, you must recover the Amulet +of Yendor. + +"Go forth, and let %d guide your steps." +%E +%Cc Pri 00082 +%l reiterates that %o is yours now. + +"The time has come to resume your search for the Amulet. +%Z await your return through the magic portal +that brought you here." +%E +%Cp Pri 00090 +"Welcome back, %p. How is your quest for the Amulet going?" +%E +%Cc Pri 00091 +"You have prevailed, %p! %d is surely with you. Now, +you must take the amulet, and sacrifice it on %ds altar on +the Astral plane. I suspect that I shall never see you again in this +life, but I hope to at %ds feet." +%E +# +# Ranger +# +%Cc Ran 00001 +You arrive in familiar surroundings. In the distance, you %x the +ancient forest grove, the place of worship to %d. + +Something is wrong, though. Surrounding the grove are centaurs! +And they've noticed you! +%E +%Cp Ran 00002 +Once again, you stand before %H. +%E +%Cp Ran 00003 +You have the oddest feeling that this may be the last time you +are to enter %H. +%E +%Cp Ran 00005 +"%pC! I have not seen you in many moons. How do you fare?" +%E +%Cp Ran 00006 +"%nC continues to threaten the grove. But we hold fast." +%E +%Cp Ran 00007 +"%lC is growing weak. The magic required to defend the grove drains us." +%E +%Cp Ran 00008 +"Remember %i is hard to enter. Beware the +distraction of leatherwings." +%E +%Cp Ran 00009 +"We must regain %o. Without it we will be overrun." +%E +%Cp Ran 00010 +"%pC! I have not seen you in many moons. How do you fare?" +%E +%Cp Ran 00011 +"Birdsong has returned to the grove, surely this means you have defeated %n." +%E +%Cp Ran 00012 +"%lC seems to have regained some of his strength." +%E +%Cp Ran 00013 +"So, tell us how you entered %i, in case some new evil arises there." +%E +%Cp Ran 00014 +"Is that truely %o that I see you carrying?" +%E +%Cc Ran 00015 +"%pC! You have returned! Thank %d. + +"We have great need of you. But first, I must see if you have the +required abilities to take on this responsibility." +%E +%Cp Ran 00016 +"Once again, %p, you stand in our midst. Are you ready now?" +%E +%Cp Ran 00017 +"Ah, you are here again, %p. Allow me to determine your readiness..." +%E +%Cc Ran 00018 +"%pC! You have doomed us all. You fairly radiate %L influences +and weaken the power we have raised in this grove as a result! + +"Begone! We renounce your %shood with us! You are an outcast now!" +%E +%Cc Ran 00019 +"%p, you are yet too inexperienced to withstand the demands of that +which we need you to do. %RA might just be able to do this thing. + +"Return to us when you have learned more, my %S." +%E +%Cc Ran 00020 +"You have strayed, %p! You know that %d requires that +we maintain a pure devotion to things %a! + +"You must go from us. Return when you have purified yourself." +%E +%Cc Ran 00021 +"You are indeed ready, %p. I shall tell you what has transpired, +and why we so desperately need your help: + +"A short time ago, the mountain centaurs to the east invaded +and enslaved the plains centaurs in this area. The local +leader is now only a figurehead, and serves %n. + +"During our last gathering of worship here, we were beset by hordes of +hostile centaurs, as you witnessed. In the first onslaught a group, +headed by %n himself, managed to breach the grove and +steal %o. + +"Since then, we have been besieged. We do not know how much longer +we will be able to maintain our magical barriers. + +"If we are to survive, you, %p, must infiltrate +%i. There, you will find a pathway down, to the +underground cavern of %n. He has always coveted +%o, and will surely keep it. + +"Recover %o for us, %p! Only then will %d be safe." +%E +%Cp Ran 00025 +"It is rumored that the Forest and Mountain Centaurs have resolved +their ancient feud and now band together against us." +%E +%Cp Ran 00026 +"%nC is strong, and very smart." +%E +%Cp Ran 00027 +"Use %o, when you find it. It will help you survive +to reach us." +%E +%Cp Ran 00028 +"Remember, let %d be your guide." +%E +%Cp Ran 00029 +"Call upon %d when you face %n. +The very act of doing so will infuriate him, and give you advantage." +%E +%Cp Ran 00030 +"%n and his kind have always hated us." +%E +%Cp Ran 00031 +"We cannot hold the grove much longer, %p. Hurry!" +%E +%Cp Ran 00032 +"To infiltrate %i, you must be very stealthy." +%E +%Cp Ran 00033 +"Remember that %n is a braggart. Trust not what he says." +%E +%Cp Ran 00034 +"You can triumph, %p, if you trust in %d." +%E +%Cc Ran 00035 +This must be %i. + +You are in a cave built of many different rooms, all interconnected +by tunnels. Your quest is to find and shoot the evil wumpus that +resides elsewhere in the cave without running into any bottomless +pits or using up your limited supply of arrows. Good luck. + +You are in room 9 of the cave. There are tunnels to rooms +5, 8, and 10. +*rustle* *rustle* (must be bats nearby) +*sniff* (I can smell the evil wumpus nearby!) +%E +%Cc Ran 00036 +Once again, you descend into %i. + +*whoosh* (I feel a draft from some pits). +*rustle* *rustle* (must be bats nearby) +%E +%Cc Ran 00040 +You descend into a weird place, in which roughly cut cave-like walls +join with smooth, finished ones, as if someone was in the midst of +finishing off the construction of a subterranean complex. + +Off in the distance, you hear a sound like the clattering of many +hooves on rock. +%E +%Cp Ran 00041 +Once again, you enter the distorted castle of %n. +%E +%Cc Ran 00050 +"So, %c. %lC has sent you to recover %o. + +"Well, I shall keep that bauble. It pleases me. You, %c, shall die." +%E +%Cp Ran 00051 +"Back again, eh? Well, a mere %r is no threat to me! Die, %c!" +%E +%Cp Ran 00052 +"You haven't learned your lesson, %c. You can't kill me! You shall die now." +%E +%Cp Ran 00053 +"I shall have %o from you, %r. Then I shall +kill you." +%E +%Cp Ran 00060 +"Your %d is nothing, %c. You are mine now!" +%E +%Cp Ran 00061 +"Run away little %c! You can never hope to defeat %n!" +%E +%Cp Ran 00062 +"My servants will rip you to shreds!" +%E +%Cp Ran 00063 +"I shall display your head as a trophy. What do you think about that wall?" +%E +%Cp Ran 00064 +"I shall break your %ls grove, and destroy all the %gP!" +%E +%Cp Ran 00065 +"%d has abandoned you, %c. You are doomed." +%E +%Cp Ran 00066 +"%rA? %lC sends a mere %r against me? Hah!" +%E +%Cp Ran 00067 +"%lC has failed, %c. %oC will never leave here." +%E +%Cp Ran 00068 +"You really think you can defeat me, eh %c? You are wrong!" +%E +%Cp Ran 00069 +"You weaken, %c. I shall kill you now." +%E +%Cc Ran 00070 +As you pick up %o, it seems to glow, and a warmth +fills you completely. You realize that its power is what has protected +your %sp against their enemies for so long. + +You must now return it to %l without delay -- their lives depend +on your speed. +%E +%Cc Ran 00080 +%nC collapses to the ground, cursing you and %l, then says: + + You have defeated me, %r! But I curse you one final time, with my + dying breath! You shall die before you leave my castle! +%E +%Cc Ran 00081 +"%pC! You have succeeded! I feared it was not possible! + +"You have returned with %o! + +"I fear, now, that the Centaurs will regroup and plot yet another raid. +This will take some time, but if you can recover the Amulet of Yendor +for %d before that happens, we will be eternally safe. + +"Take %o with you. It will aid in your quest for +the Amulet." +%E +# assumes The Longbow of Diana +%Cc Ran 00082 +%l flexs %o reverently. + +"With this wondrous bow, one need never run out of arrows. +You are its keeper now, and the time has come to resume your +search for the Amulet. %Z await your return +through the magic portal that brought you here." +%E +%Cp Ran 00090 +"Welcome, %p. How have you fared on your quest for the Amulet +of Yendor?" +%E +%Cc Ran 00091 +"You have it! You have recovered the Amulet of Yendor! +Now attend to me, %p, and I will tell you what must be done: + +"The Amulet has within it magic, the capability to transport you to +the Astral Plane, where the primary circle of %d resides. + +"To activate this magic, you must travel upwards as far as you can. +When you reach the temple, sacrifice the Amulet to %d. + +"Thus will you fulfill your destiny." +%E +# +# Rogue (with apologies to all Norsk speakers -dean) +# +%Cc Rog 00001 +Unexpectedly, you find yourself back in Ransmannsby, where you trained to +be a thief. Quickly you make the guild sign, hoping that you AND word +of your arrival reach %ls den. +%E +%Cp Rog 00002 +Once again, you find yourself back in Ransmannsby. Fond memories are +replaced by fear, knowing that %l is waiting for you. +%E +%Cp Rog 00003 +You rub your hands through your hair, hoping that the little ones on +the back of your neck stay down, and prepare yourself for your meeting +with %l. +%E +%Cp Rog 00005 +"I hear that Lady Tyvefelle's household is lightly guarded." +%E +%Cp Rog 00006 +"You're back? Even the Twain don't come back anymore." +%E +%Cp Rog 00007 +"Can you spare an old cutpurse a zorkmid for some grog?" +%E +%Cp Rog 00008 +"Fritz tried to join the other side, and now he's hell-hound chow." +%E +%Cp Rog 00009 +"Be careful what you steal, I hear the boss has perfected turning +rocks into worthless pieces of glass." +%E +%Cp Rog 00010 +"I was sure wrong about Lady Tyvefelle's house; I barely got away with my +life and lost my lock pick in the process." +%E +%Cp Rog 00011 +"You're back? Even the Twain don't come back anymore." +%E +%Cp Rog 00012 +"Can you spare an old cutpurse a zorkmid for some grog?" +%E +%Cp Rog 00013 +"Fritz tried to join the other side, and now he's hell-hound chow." +%E +%Cp Rog 00014 +"Be careful what you steal, I hear the boss has perfected turning +rocks into worthless pieces of glass." +%E +%Cc Rog 00015 +"Well, look who it is boys -- %p has come home. You seem to have +fallen behind in your dues. I should kill you as an example to these +other worthless cutpurses, but I have a better plan. If you are ready +maybe you could work off your back dues by performing a little job for +me. Let us just see if you are ready..." +%E +%Cp Rog 00016 +"Well, I didn't expect to see you back. It shows that you are either stupid, +or you are finally ready to accept my offer. Let us hope for your sake it +isn't stupidity that brings you back." +%E +%Cp Rog 00017 +"Did you perhaps mistake me for some other %lt? You must +think me as stupid as your behavior. I warn you not to try my patience." +%E +%Cc Rog 00018 +"Well %gp, it looks like our friend has forgotten who is the boss +around here. Our friend seems to think that %rp have been put in +charge. Wrong. DEAD WRONG!" + +Your sudden shift in surroundings prevents you from hearing the end +of %ls curse. +%E +%Cc Rog 00019 +"In the time that you've been gone you've only been able to master the +arts of %ra? I've trained ten times again as many %Rp +in that time. Maybe I should send one of them, no? Where would that +leave you, %p? Oh yeah, I remember, I was going to kill you!" +%E +%Cc Rog 00020 +"Maybe I should chain you to my perch here for a while. Perhaps watching +real %a men at work will bring some sense back to you. I don't +think I could stand the sight of you for that long though. Come back +when you can be trusted to act properly." +%E +%Cc Rog 00021 +"Will everyone not going to retrieve %o from that +jerk, %n, take one step backwards. Good choice, +%p, because I was going to send you anyway. My other %gp +are too valuable to me. + +"Here's the deal. I want %o, %n +has %o. You are going to get %o +and bring it back to me. So simple an assignment even you can understand +it." +%E +%Cp Rog 00025 +"You don't seem to understand, +%o isn't here so neither should you be!" +%E +%Cp Rog 00026 +"May %d curse you with lead fingers. Get going!" +%E +%Cp Rog 00027 +"We don't have all year. GET GOING!" +%E +%Cp Rog 00028 +"How would you like a scar necklace? I'm just the jeweler to do it!" +%E +%Cp Rog 00029 +"Lazy S.O.B. Maybe I should call up someone else..." +%E +%Cp Rog 00030 +"Maybe I should open your skull and see if my instructions are inside?" +%E +%Cp Rog 00031 +"This is not a task you can complete in the afterlife, you know." +%E +%Cp Rog 00032 +"Inside every living person is a dead person trying to get out, +and I have your key!" +%E +%Cp Rog 00033 +"We're almost out of hell-hound chow, so why don't you just get moving!" +%E +%Cp Rog 00034 +"You know, %o isn't going to come when you +whistle. You must get it yourself." +%E +%Cc Rog 00035 +Those damn little hairs tell you that you are nearer to +%o. +%E +%Cp Rog 00036 +Not wanting to face %l without having stolen +%o, you continue. +%E +%Cc Rog 00040 +You feel a great swelling up of courage, sensing the presence of +%o. Or is it fear? +%E +%Cp Rog 00041 +The hairs on the back of your neck whisper -- it's fear. +%E +%Cc Rog 00050 +"Ah! You must be %ls ... er, `hero'. A pleasure +to meet you." +%E +%Cp Rog 00051 +"We meet again. Please reconsider your actions." +%E +%Cp Rog 00052 +"Surely, %p, you have learned that you cannot trust any bargains +that %l has made. I can show you how to continue on +your quest without having to run into him again." +%E +%Cp Rog 00053 +"Please, think for a moment about what you are doing. Do you truly +believe that %d would want %l to have +%o?" +%E +%Cp Rog 00060 +"May I suggest a compromise. Are you interested in gold or gems?" +%E +%Cp Rog 00061 +"Please don't force me to kill you." +%E +%Cp Rog 00062 +"Grim times are upon us all. Will you not see reason?" +%E +%Cp Rog 00063 +"I knew %l, and you're no %lt, thankfully." +%E +%Cp Rog 00064 +"It is a shame that we are not meeting under more pleasant circumstances." +%E +%Cp Rog 00065 +"I was once like you are now, %p. Believe in me -- our way +is better." +%E +%Cp Rog 00066 +"Stay with me, and I will make you %os guardian." +%E +%Cp Rog 00067 +"When you return, with or without %o, +%l will have you killed." +%E +%Cp Rog 00068 +"Do not be fooled; I am prepared to kill to defend %o." +%E +%Cp Rog 00069 +"I can reunite you with the Twain. Oh, the stories you can swap." +%E +%Cc Rog 00070 +As you pick up %o, the hairs on the back of your +neck fall out. At once you realize why %n was +willing to die to keep it out of %ls hands. Somehow +you know that you must do likewise. +%E +%Cc Rog 00080 +"I know what you are thinking, %p. It is not too late for you +to use %o wisely. For the sake of your guild +%sp, do what is right." + +You sit and wait for death to come for %n, and then you +brace yourself for your next meeting with %l! +%E +%Cc Rog 00081 +"Well, I'll be damned. You got it. I am proud of you, a fine %r +you've turned out to be. + +"While you were gone I got to thinking, you and %o +together could bring me more treasure than either of you apart, so why don't +you take it with you. All I ask is a cut of whatever loot you come by. +That is a better deal than I offered %n. + +"But, you see what happened to %n when he refused. +Don't make me find another to send after you this time." +%E +# assumes Master Key of Thievery (small object) +%Cc Rog 00082 +%l seems tempted to swap %o for +the mundane one you detect in his pocket, but noticing your alertness, +evidently chickens out. + +"Go filch the Amulet before someone else beats you to it. +%Z are back the way you came, through the magic portal." +%E +%Cc Rog 00090 +"Quite the little thief, aren't we, %p. Can I interest you in a +swap for %o. Look around, anything in the keep +is yours for the asking." +%E +%Cc Rog 00091 +"I see that with your abilities, and my brains, we could rule this world. + +"All that we would need to be all-powerful is for you to take that little +trinket you've got there up to the Astral plane. From there, %d will +show you what to do with it. Once that's done, we will be invincible!" +%E +# +# Samurai +# +%Cc Sam 00001 +Even before your senses adjust, you recognize the kami of +%H. + +You %x the standard of your teki, %n, flying above +the town. How could such a thing have happened? Why are ninja +wandering freely; where are the samurai of your daimyo, %l? + +You quickly say a prayer to Izanagi and Izanami and walk towards +town. +%E +%Cp Sam 00002 +Once again, you are back at %H. +%E +%Cp Sam 00003 +You are back at %H. + +Instantly you sense a subtle change in your karma. You seem to know that +if you do not succeed in your quest, %n will have destroyed +the kami of %H before you return again. +%E +%Cp Sam 00005 +"To succeed, you must walk like a butterfly on the wind." +%E +%Cp Sam 00006 +"Ikaga desu ka?" +%E +%Cp Sam 00007 +"I fear for The Land of The Gods." +%E +%Cp Sam 00008 +"%nC has hired the Ninja -- be careful." +%E +%Cp Sam 00009 +"If %o is not returned, we will all be ninja." +%E +%Cp Sam 00010 +"Come, join us in celebrating with some sake." +%E +%Cp Sam 00011 +"Ikaga desu ka?" +%E +%Cp Sam 00012 +"You have brought our clan and %l much honor." +%E +%Cp Sam 00013 +"Please %r, sit for a while and tell us how you overcame the Ninja." +%E +%Cp Sam 00014 +"%lC still lives! You have saved us from becoming ronin." +%E +%Cc Sam 00015 +"Ah, %p-san, it is good to see you again. I need someone who can +lead my samurai against %n. If you are ready, you will be +that person." +%E +%Cp Sam 00016 +"Once again, %p-san, you kneel before me. Are you yet capable of +being my vassal?" +%E +%Cp Sam 00017 +"You begin to test my matsu, %p-san. +If you cannot determine what I want in a samurai, how can I rely on you +to figure out what I need from a samurai?" +%E +%Cc Sam 00018 +"You are no longer my samurai, %p. + +"Hara-kiri is denied. You are ordered to shave your head and then to +become a monk. Your fief and family are forfeit. Wakarimasu?" +%E +%Cc Sam 00019 +"%p-san, you have learned well and honored your family. +I require the skills of %Ra in order to defeat %n. +Go and seek out teachers. Learn what they have learned. When you +are ready, return to me." +%E +%Cc Sam 00020 +"%p-san, you would do better to join the kyokaku. + +"You have skills, but until you can call upon the bushido to know when and +how to use them you are not samurai. When you can think %a and +act %a then return." +%E +%Cc Sam 00021 +"Domo %p-san, indeed you are ready. I can now tell you what +it is that I require of you. + +"The daimyo, %n, has betrayed us. He has stolen from us +%o and taken it to his donjon deep within +%i. + +"If I cannot show the emperor %o when he comes +for the festival he will know that I have failed in my duty, and +request that I commit seppuku. + +"You must gain entrance to %i and retrieve the +emperor's property. Be quick! The emperor will be here for the +cha-no-you in 5 sticks. + +"Wakarimasu?" +%E +%Cp Sam 00025 +"To defeat %n you must overcome the seven emotions: +hate, adoration, joy, anxiety, anger, grief, and fear." +%E +%Cp Sam 00026 +"Remember your honor is my honor, you perform in my name." +%E +%Cp Sam 00027 +"I will go to the temple and burn incense for your safe return." +%E +%Cp Sam 00028 +"Sayonara." +%E +%Cp Sam 00029 +"There can be honor in defeat, but no gain." +%E +%Cp Sam 00030 +"Your kami must be strong in order to succeed." +%E +%Cp Sam 00031 +"You are indeed a worthy %R, but now you must be a worthy samurai." +%E +%Cp Sam 00032 +"If you fail, %n will be like a tai-fun on the land." +%E +%Cp Sam 00033 +"If you are truly %a, %d will listen." +%E +%Cp Sam 00034 +"Sharpen your swords and your wits for the task before you." +%E +%Cc Sam 00035 +You instinctively reach for your swords. You do not recognize the +lay of this land, but you know that your teki are everywhere. +%E +%Cp Sam 00036 +Thankful that your %sp at %H cannot see +your fear, you prepare again to advance. +%E +%Cc Sam 00040 +In your mind, you hear the taunts of %n. + +You become like the rice plant and bend to the ground, offering a +prayer to %d. But when the wind has passed, you stand +proudly again. Putting your kami in the hands of fate, you advance. +%E +%Cp Sam 00041 +As you arrive once again at the home of %n, your thoughts +turn only to %o. +%E +%Cc Sam 00050 +"Ah, so it is to be you, %p-san. I offer you seppuku. +I will be your second if you wish." +%E +%Cp Sam 00051 +"I have offered you the honorable exit. Now I will have your +head to send unwashed to %l." +%E +%Cp Sam 00052 +"After I have dispatched you, I will curse your kami." +%E +%Cp Sam 00053 +"You have fought my samurai; surely you must know that you +will not be able to take %o back to +%H." +%E +%Cp Sam 00060 +"Ahh, I finally meet the daimyo of the kyokaku!" +%E +%Cp Sam 00061 +"There is no honor for me in your death." +%E +%Cp Sam 00062 +"You know that I cannot resash my swords until they have killed." +%E +%Cp Sam 00063 +"Your presence only compounds the dishonor of %l in not coming himself." +%E +%Cp Sam 00064 +"I will make tea with your hair and serve it to %l." +%E +%Cp Sam 00065 +"Your fear shows in your eyes, coward!" +%E +%Cp Sam 00066 +"I have not heard of you, %p-san; has your life been that unworthy?" +%E +%Cp Sam 00067 +"If you will not obey me, you will die." +%E +%Cp Sam 00068 +"Kneel now and make the two cuts of honor. I will tell your %sp +of your honorable death." +%E +%Cp Sam 00069 +"Your master was a poor teacher. You will pay for his mistakes in +your teaching." +%E +%Cc Sam 00070 +As you pick up %o, you feel the strength of its karma. +You realize at once why so many good samurai had to die to defend it. +You are humbled knowing that you hold one of the artifacts of the +sun goddess. +%E +%Cc Sam 00080 +Your healing skills tell you that %ns wounds are mortal. + +You know that the bushido tells you to finish him and let his kami +die with honor, but the thought of so many samurai dead due to this +man's dishonor prevents you from giving the final blow. + +You order that his unwashed head be given to the crows and his body +thrown into the sea. +%E +%Cc Sam 00081 +As you bow before %l, he welcomes you: + + You have brought your family great honor, %p-sama. + + While you have been gone the emperor's advisors have discovered in + the ancient texts that the karma of the samurai who seeks to recover + the amulet and the karma of %o are joined + as the seasons join to make a year. + + Because you have shown such fidelity, the emperor requests + that you take leave of other obligations and continue on the + road that fate has set your feet upon. I would consider it + an honor if you would allow me to watch your household until + you return with the amulet. + +With that, %l bows, and places his sword atop +%o. +%E +%Cc Sam 00082 +%l holds %o tightly for a moment, then returns +his gaze to you. + +"The time is ripe to recover the Amulet. Return to %Z +through the magic portal that transported you here so that you may +achieve the destiny which awaits you." +%E +%Cp Sam 00090 +%lC bows. "%p-sama, tell us of your search for the Amulet." +%E +%Cc Sam 00091 +"Ah, %p-sama. You have wasted your efforts returning home. +Now that you are in possession of the Amulet, you are honor-bound to +finish the quest you have undertaken. There will be plenty of time +for saki and stories when you have finished. + +"Go now, and may our prayers be a wind at your back." +%E +# +# Tourist +# +%Cc Tou 00001 +You breathe a sigh of relief as you find yourself back in the familiar +surroundings of %H. + +You quickly notice that things do not appear the way they did when you +left. The town is dark and quiet. There are no sounds coming from +behind the town walls, and no campfires burning in the fields. As a +matter of fact, you do not %x any movement in the fields at all, and +the crops look as though they have been untended for many weeks. +%E +%Cp Tou 00002 +Once again, you are back at %H. +%E +%Cp Tou 00003 +You are back at %H. +Things appear to have become so bad that you fear that soon +%H will not be here to return to. +%E +%Cp Tou 00005 +"Gehennom on 5 zorkmids a day -- more like 500 a day if you ask me." +%E +%Cp Tou 00006 +"Do you know where I could find some nice postcards of The Gnomish Mines?" +%E +%Cp Tou 00007 +"Have you tried the weird toilets?" +%E +%Cp Tou 00008 +"Don't stay at the Inn, I hear the food is terrible and it has rats." +%E +%Cp Tou 00009 +"They told me that this was the off season!" +%E +%Cp Tou 00010 +"Gehennom on 5 zorkmids a day -- more like 500 a day if you ask me." +%E +%Cp Tou 00011 +"Do you know where I could find some nice postcards of The Gnomish Mines?" +%E +%Cp Tou 00012 +"Have you tried the weird toilets?" +%E +%Cp Tou 00013 +"If you stick around, I'll show you the pictures from my latest trip." +%E +%Cp Tou 00014 +"Did you bring me back any souvenirs?" +%E +%Cc Tou 00015 +"Is it really you, %p! I had given up hope for your return. +As you can %x, we are desperately in need of your talents. Someone must +defeat %n if our town is become what it once was. + +"Let me see if you are ready to be that someone." +%E +%Cp Tou 00016 +"Things are getting worse, %p. +I hope that this time you are ready." +%E +%Cp Tou 00017 +"I hope that for the sake of %H you have prepared +yourself this time." +%E +%Cc Tou 00018 +"It is too late, %p. You are not even worthy to die amongst us. +Leave %H and never return." +%E +%Cc Tou 00019 +"There is still too much that you have to learn before you can undertake +the next step. Return to us as a proven %R, and perhaps then +you will be ready. + +"Go back now, and may the teachings of %d serve you well." +%E +%Cc Tou 00020 +"It would be an affront to %d to have one not true to the +%a path undertake her bidding. + +"You must not return to us until you have purified yourself of these +bad influences on your actions. Remember, only by following the %a +path can you hope to overcome the obstacles you will face." +%E +%Cc Tou 00021 +"You have indeed proven yourself a worthy %c, %p. + +"But now your kinfolk and I must ask you to put aside your travels and +help us in our time of need. After you left us we elected a new mayor, +%n. He proved to be a most heinous and vile creature. + +"Soon after taking office he absconded with %o +and fled town, leaving behind his henchmen to rule over us. In order +for us to regain control of our town, you must enter %i +and recover %o. + +"Do not be distracted on your quest. If you do not return quickly I fear +that all will be lost. Let us both pray now that %d will guide you +and keep you safe." +%E +%Cp Tou 00025 +"Do not be fooled by the false promises of %n." +%E +%Cp Tou 00026 +"To enter %i you must pass many traps." +%E +%Cp Tou 00027 +"If you do not return with %o, your quest +will be in vain." +%E +%Cp Tou 00028 +"Do not be afraid to call upon %d if you truly need help." +%E +%Cp Tou 00029 +"If you do not destroy %n, he will follow you back here!" +%E +%Cp Tou 00030 +"Take %o from %n +and you may be able to defeat him." +%E +%Cp Tou 00031 +"You must hurry, %p!" +%E +%Cp Tou 00032 +"You are like %Sa to me, %p. Do not let me down." +%E +%Cp Tou 00033 +"If you are %a at all times you may succeed, %p." +%E +%Cp Tou 00034 +"Let all who meet you on your journey know that you are on an quest for +%l and grant safe passage." +%E +%Cc Tou 00035 +Only your faith in %d keeps you from trembling. You %x +the handiwork of %ns henchlings everywhere. +%E +%Cp Tou 00036 +You know that this time you must find and destroy %n. +%E +%Cc Tou 00040 +You sense the presence of %o. +%E +%Cp Tou 00041 +You gain confidence, knowing that you may soon be united with +%o. +%E +%Cc Tou 00050 +"So, %p, %l thinks that you can wrest +%o from me! + +"It only proves how desperate he has become that he sends %ra to +try and defeat me. When this day is over, I will have you enslaved +in the mines where you will rue the day that you ever entered +%i." +%E +%Cp Tou 00051 +"I let you live the last time because it gave me pleasure. +This time I will destroy you, %p." +%E +%Cc Tou 00052 +"These meetings come to bore me. You disturb my workings with +%o. + +"If you do not run away now, I will inflict so much suffering on you that +%l will feel guilty for ever having sent his %S to me!" +%E +%Cc Tou 00053 +"You fool. You do not know how to call upon the powers of +%o. + +"Return it to me and I will teach you how to use it, and together we +will rule %H. But do so now, as my patience +grows thin." +%E +%Cp Tou 00060 +"I defeated %l and I will defeat you, %p." +%E +%Cp Tou 00061 +"Where is %d now! You must realize no one can help you here." +%E +%Cp Tou 00062 +"Beg for mercy now and I may be lenient on you." +%E +%Cp Tou 00063 +"If you were not so %a, you might have stood a chance." +%E +%Cp Tou 00064 +"Vengeance is mine at last, %p." +%E +%Cp Tou 00065 +"I only wish that %l had a more worthy %r to send against me." +%E +%Cp Tou 00066 +"With %o in my possession you cannot +hope to defeat me." +%E +%Cp Tou 00067 +"%nC has never been defeated, NEVER!" +%E +%Cp Tou 00068 +"Are you truly the best %H has to send against me? +I pity %l." +%E +%Cp Tou 00069 +"How do you spell %p? I want to ensure the marker on your grave is +correct as a warning to your %sp." +%E +%Cc Tou 00070 +As you pick up %o, you feel a great +weight has been lifted from your shoulders. Your only thoughts are +to quickly return to %H and find %l. +%E +%Cc Tou 00080 +You turn in the direction of %n. As his earthly body begins +to vanish before your eyes, you hear him curse: + + You shall never be rid of me, %p! + I will find you where ever you go and regain what is rightly mine. +%E +%Cc Tou 00081 +As %l detects the presence of %o, +he almost smiles for the first time in many a full moon. + +As he looks up from %o he says: + + You have recovered %o. You are its + owner now, but not its master. Let it work with you as you continue + your journey. With its help, and %d to guide you on the + %a path, you may yet recover the Amulet of Yendor. +%E +%Cc Tou 00082 +"%oC is yours now. %Z await your +return through the magic portal that brought you here." +%E +%Cp Tou 00090 +"I could not be more proud than if you were my own %S, %p! +Tell me of your adventures in quest of the Amulet of Yendor." +%E +%Cc Tou 00091 +"Stand back and let me look at you, %p. +Now that you have recovered the Amulet of Yendor, I'm afraid living +out your days in %H would seem pretty tame. + +"You have come too far to stop now, for there are still more tasks that +our oral history foretells for you. Forever more, though, your name shall +be spoken by the %gP with awe. You are truly an inspiration to your +%sp!" +%E +# +# Valkyrie +# +%Cc Val 00001 +You materialize at the base of a snowy hill. Atop the hill sits +a place you know well, %H. You immediately realize +that something here is very wrong! + +In places, the snow and ice have been melted into steaming pools of +water. Fumaroles and pools of bubbling lava surround the hill. +The stench of sulphur is carried through the air, and you %x creatures +that should not be able to live in this environment moving towards you. +%E +%Cp Val 00002 +Once again, you are near the abode of %l. +%E +%Cp Val 00003 +Again you materialize near %ls abode. You have a nagging feeling +that this may be the last time you come here. +%E +%Cp Val 00005 +"Hail, and well met, brave %c." +%E +%Cp Val 00006 +"May %d guide your steps, %p." +%E +%Cp Val 00007 +"%lC weakens. Without %o, her foresight is dim." +%E +%Cp Val 00008 +"You must hurry, %p, else Ragnarok may well come." +%E +%Cp Val 00009 +"I would deal with this foul %n myself, but %d forbids it." +%E +%Cp Val 00010 +"Hail, and well met, brave %c." +%E +%Cp Val 00011 +"May %d guide your steps, %p." +%E +%Cp Val 00012 +"%lC told us you had succeeded!" +%E +%Cp Val 00013 +"You recovered %o just in time, %p." +%E +%Cp Val 00014 +"Hail %d, for delivering %o back to us." +%E +%Cc Val 00015 +"Ah, %p, my %S. You have returned to %H +at last. We are in dire need of your aid, but I must determine if you +are yet ready for such an undertaking. + +"Let me read your fate..." +%E +%Cp Val 00016 +"Let me read the future for you now, %p, perhaps you have managed to +change it enough..." +%E +%Cp Val 00017 +"Again, I shall read your fate, my %S. Let us both hope that you have +made changes to become ready for this task..." +%E +%Cc Val 00018 +"No, %p. Your fate is sealed. I must cast about for another +champion. Begone from my presence, and never return. Know this, that +you shall never succeed in this life, and Valhalla is denied to you." +%E +%Cc Val 00019 +"I see you and %n fighting, %p. But you are not prepared and +shall die at %ns hand if you proceed. No. This will not do. +Go back out into the world, and grow more experienced at the ways of +war. Only when you have returned %Ra will you be able to defeat +%n." +%E +%Cc Val 00020 +"NO! This is terrible. I see you becoming an ally of %n, and +leading his armies in the final great battles. This must not come to +pass! You have strayed from the %a path. You must purge yourself, +and return here only when you have regained a state of purity." +%E +%Cc Val 00021 +"It is not clear, %p, for my sight is limited without +%o. But it is now likely that you can defeat %n, +and recover %o. + +"A short time ago, %n and his minions attacked this place. They +opened the huge volcanic vents you %x about the hill, and attacked. +I knew that this was to come to pass, and had asked %d for a group +of %gP to help defend this place. The few you %x here are the +mightiest of Valhalla's own, and are all that are left of one hundred +%d sent. + +"Despite the great and glorious battle we fought, %n managed at last +to steal %o. This has upset the balance of the universe, and +unless %o is returned into my care, %n may start Ragnarok. + +"You must find the entrance to %i. Travel downward +from there and you will find %ns lair. Defeat him and +return %o to me." +%E +%Cp Val 00025 +"Go with the blessings of %d." +%E +%Cp Val 00026 +"Call upon %d when you are in need." +%E +%Cp Val 00027 +"Use %o if you can. It will protect you." +%E +%Cp Val 00028 +"Magical cold is very effective against %n." +%E +%Cp Val 00029 +"To face %n, you will need to be immune to fire." +%E +%Cp Val 00030 +"May %d strengthen your sword-arm." +%E +%Cp Val 00031 +"Trust in %d. He will not desert you." +%E +%Cp Val 00032 +"It becomes more likely that Ragnarok will come with every passing moment. +You must hurry, %p." +%E +%Cp Val 00033 +"If %n can master %o, he will be powerful enough to +face %d far earlier than is fated. This must not be!" +%E +%Cp Val 00034 +"Remember your training, %p. You can succeed." +%E +%Cc Val 00035 +The ice and snow gives way to a valley floor. You %x ahead of you +a huge round hill surrounded by pools of lava. This then is the entrance +to %i. It looks like you're not going to get in without +a fight though. +%E +%Cp Val 00036 +Once again, you stand before the entrance to %i. +%E +%Cc Val 00040 +Through clouds of sulphurous gasses, you %x a rock palisade +surrounded with a moat of bubbling lava. You remember the description +from something that %l said. This is the lair of %n. +%E +%Cp Val 00041 +Once again, you stand in sight of %ns lair. +%E +%Cc Val 00050 +"So! %lC has finally sent %ca to challenge me! + +"I thought that mastering %o would enable me to challenge %d, +but it has shown me that first I must kill you! +So come, little %s. Once I defeat you, I can at last begin +the final battle with %d." +%E +%Cp Val 00051 +"Again you challenge me, %r. Good. I will kill you now." +%E +%Cp Val 00052 +"Have you not learned yet? You cannot defeat %n!" +%E +%Cp Val 00053 +"I will kill you, %c, and wrest %o from your mangled hands." +%E +%Cp Val 00060 +"I am your death, %c." +%E +%Cp Val 00061 +"You cannot prevail, %r. I have foreseen your every move." +%E +%Cp Val 00062 +"With you out of the way, Valhalla will be mine for the taking." +%E +%Cp Val 00063 +"I killed scores of %ds best when I took %o. +Do you really think that one %c can stand against me?" +%E +%Cp Val 00064 +"Who bears the souls of %cP to Valhalla, %r?" +%E +%Cp Val 00065 +"No, %d cannot help you here." +%E +%Cp Val 00066 +"Some instrument of %d you are, %p. You are a weakling!" +%E +%Cp Val 00067 +"Never have I seen %ca so clumsy in battle." +%E +%Cp Val 00068 +"You die now, little %s." +%E +%Cp Val 00069 +"Your body I destroy now, your soul when my hordes overrun Valhalla!" +%E +%Cc Val 00070 +As you pick up %o, your mind is suddenly filled with images, +and you perceive all of the possibilities of each potential choice you +could make. As you begin to control and channel your thoughts, you +realize that you must return %o to %lC immediately. +%E +%Cc Val 00080 +A look of surprise and horror appears on %ns face. + + No!!! %o has lied to me! I have been misled! + +Suddenly, %n grasps his head and screams in agony, then dies. +%E +%Cc Val 00081 +As you approach, %lC rises and touches %o. + +"You may take %o with you, %p. I have removed from +it the power to foretell the future, for that power no mortal should +have. Its other abilities, however, you have at your disposal. + +"You must now begin in %ds name to search for the Amulet of Yendor. +May your steps be guided by %d, my %S." +%E +# assumes Orb of Fate (glass object) +%Cc Val 00082 +"Careful, %p! %oC might break, and that would be +a tragic loss. You are its keeper now, and the time has come to +resume your search for the Amulet. %Z await your +return through the magic portal that brought you here." +%E +%Cp Val 00090 +"Greetings, %p. I have not been able to pay as much attention to +your search for the Amulet as I have wished. How do you fare?" +%E +%Cc Val 00091 +"Excellent, %p. I see you have recovered the Amulet! + +"You must take the Amulet to the Great Temple of %d, on the Astral +plane. There you must offer the Amulet to %d. + +"Go now, my %S. I cannot tell you your fate, as the power of the +Amulet interferes with mine. I hope for your success." +%E +# +# Wizard +# +%Cc Wiz 00001 +You are suddenly in familiar surroundings. You notice what appears to +be a large, squat stone structure nearby. Wait! That looks like the +tower of your former teacher, %l. + +However, things are not the same as when you were last here. Mists and +areas of unexplained darkness surround the tower. There is movement in +the shadows. + +Your teacher would never allow such unaesthetic forms to surround the +tower... unless something were dreadfully wrong! +%E +%Cp Wiz 00002 +Once again, you are back at %H. +%E +%Cp Wiz 00003 +You are back at %H. +You have an odd feeling this may be the last time you ever come here. +%E +%Cp Wiz 00005 +"Would you happen to have some eye of newt in that overstuffed pack, %s?" +%E +%Cp Wiz 00006 +"Ah, the spell to create the magic portal worked. Outstanding!" +%E +%Cp Wiz 00007 +"Hurry! %lC may not survive that casting of the +portal spell!!" +%E +%Cp Wiz 00008 +"The spells of %n were just too powerful for us to withstand." +%E +%Cp Wiz 00009 +"I, too, will venture into the world, because %n is but one of +many evils to be vanquished." +%E +%Cp Wiz 00010 +"I have some eye of newt to trade, do you have a spare blind-worm's sting?" +%E +%Cp Wiz 00011 +"The magic portal now seems like it will remain stable for quite some time." +%E +%Cp Wiz 00012 +"Have you noticed how much stronger %l is since %o was recovered?" +%E +%Cp Wiz 00013 +"Thank %d! We weren't positive you would defeat %n." +%E +%Cp Wiz 00014 +"I, too, will venture into the world, because %n was but one of +many evils to be vanquished." +%E +%Cc Wiz 00015 +"Come closer, %p, for my voice falters in my old age. +Yes, I see that you have come a long way since you went out into the +world, leaving the safe confines of this tower. However, I must first +determine if you have all of the skills required to take on the task +I require of you." +%E +%Cp Wiz 00016 +"Well, %p, you have returned. Perhaps you are now ready..." +%E +%Cp Wiz 00017 +"This is getting tedious, %p, but perseverance is a sign of a true mage. +I certainly hope that you are truly ready this time!" +%E +%Cc Wiz 00018 +"You fool, %p! Why did I waste all of those years teaching you +the esoteric arts? Get out of here! I shall find another." +%E +%Cc Wiz 00019 +"Alas, %p, you have not yet shown your proficiency as a worthy +spellcaster. As %ra, you would surely be overcome in the challenge +ahead. Go, now, expand your horizons, and return when you have attained +renown as %Ra." +%E +%Cc Wiz 00020 +"You amaze me, %p! How many times did I tell you that the way of a mage +is an exacting one. One must use the world with care, lest one leave it +in ruins and simplify the task of %n. + +"You must go back and show your worthiness. Do not return until you are +truly ready for this quest. May %d guide you in this task." +%E +%Cc Wiz 00021 +"Yes, %p, you truly are ready for this dire task. Listen, +carefully, for what I tell you now will be of vital importance. + +"Since you left us to hone your skills in the world, we unexpectedly came +under attack by the forces of %n. As you know, we thought +%n had perished at the end of the last age, but, alas, this was +not the case. + +"%nC sent an army of abominations against us. Among them was a +minion, mindless and ensorcelled, and thus, in the confusion, it was able +to penetrate our defenses. Alas, this creature has stolen +%o and I fear it has delivered %o +to %n. + +"Over the years, I had woven most of my power into this amulet, and thus, +without it, I have but a shadow of my former power, and I fear that I +shall soon perish. + +"You must travel to %i, and within its dungeons, +find and overcome %n, and return %o to me. + +"Go now, with %d, and complete this quest before it is too late." +%E +%Cp Wiz 00025 +"Beware, for %n is immune to most magical attacks." +%E +%Cp Wiz 00026 +"To enter %i you must pass many traps." +%E +%Cp Wiz 00027 +"%nC may be vulnerable to physical attacks." +%E +%Cp Wiz 00028 +"%d will come to your aid when you call." +%E +%Cp Wiz 00029 +"You must utterly destroy %n. He will pursue you otherwise." +%E +%Cp Wiz 00030 +"%oC is a mighty artifact. With it you can +destroy %n." +%E +%Cp Wiz 00031 +"Go forth with the blessings of %d." +%E +%Cp Wiz 00032 +"I will have my %gP watch for your return." +%E +%Cp Wiz 00033 +"Feel free to take any items in that chest that might aid you." +%E +%Cp Wiz 00034 +"You will know when %o is near. Proceed with care!" +%E +%Cc Wiz 00035 +Wisps of fog swirl nearby. You feel that %ns lair is close. +%E +%Cp Wiz 00036 +You believe that you may once again invade %i. +%E +%Cc Wiz 00040 +You feel your mentor's presence; perhaps %o is nearby. +%E +%Cp Wiz 00041 +The aura of %o tingles at the edge of your perception. +%E +%Cc Wiz 00050 +"Ah, I recognize you, %p. So, %l has sent you to steal +%o from me, hmmm? Well, %l is a +fool to send such a mental weakling against me. + +"Your destruction, however, should make for good sport. In the end, you +shall beg me to kill you!" +%E +%Cc Wiz 00051 +"How nice of you to return, %p! I enjoyed our last meeting. Are you +still hungry for more pain? + +"Come! Your soul, like %o, shall soon be mine to +command." +%E +%Cp Wiz 00052 +"I'm sure that your perseverance shall be the subject of innumerable +ballads, but you shall not be around to hear them, I fear!" +%E +%Cp Wiz 00053 +"Thief! %o belongs to me, now. I shall feed +your living flesh to my minions." +%E +%Cp Wiz 00060 +"Your puny powers are no match for me, fool!" +%E +%Cp Wiz 00061 +"When you are defeated, your torment will last for a thousand years." +%E +%Cp Wiz 00062 +"After your downfall, %p, I shall devour %l +for dessert!" +%E +%Cp Wiz 00063 +"Are you ready yet to beg for mercy? I could be lenient..." +%E +%Cp Wiz 00064 +"Your soul shall join the enslaved multitude I command!" +%E +%Cp Wiz 00065 +"Your lack of will is evident, and you shall die as a result." +%E +%Cp Wiz 00066 +"Your faith in %d is for naught! Come, submit to me now!" +%E +%Cp Wiz 00067 +"A mere %r is nothing compared to my skill!" +%E +%Cp Wiz 00068 +"So, you are the best hope of %l? How droll." +%E +%Cp Wiz 00069 +"Feel my power, %c! My victory is imminent!" +%E +%Cc Wiz 00070 +As you touch %o, its comforting power infuses you +with new energy. You feel as if you can detect others' thoughts flowing +through it. Although you yearn to wear %o +and attack the Wizard of Yendor, you know you must return it to its +rightful owner, %l. +%E +%Cc Wiz 00080 +%nC croaks out, as his body begins to shrivel up: + + I shall haunt your progress until the end of time. A thousand + curses on you and %l. + +Then, the body bursts into a cloud of choking dust, and blows away. +%E +%Cc Wiz 00081 +%lC notices %o in your possession, +beams at you and says: + + I knew you could defeat %n and retrieve + %o. We shall never forget this + brave service. + + Take %o with you in your quest for + the Amulet of Yendor. I can sense that it has attuned + itself to you already. + + May %d guide you in your quest, and keep you from harm. +%E +%Cc Wiz 00082 +"You are the keeper of %o now. It is time to +recover the /other/ Amulet. %Z await your return through +the magic portal which brought you here." +%E +%Cp Wiz 00090 +"Come near, my %S, and share your adventures with me. +So, have you succeeded in your quest for the Amulet of Yendor?" +%E +%Cc Wiz 00091 +"Congratulations, %p. I always knew that if anyone could succeed +in defeating the Wizard of Yendor and his minions, it would be you. + +"Go now, and take the Amulet to the astral plane. Once there, present +the Amulet on the altar of %d. Along the way you shall pass through the +four elemental planes. These planes are like nothing you have ever +experienced before, so be prepared! + +"For this you were born, %s! I am very proud of you." +%E +# +# General +# +%Cc - 00001 +It is written in the Book of %d: + + After the Creation, the cruel god Moloch rebelled + against the authority of Marduk the Creator. + Moloch stole from Marduk the most powerful of all + the artifacts of the gods, the Amulet of Yendor, + and he hid it in the dark cavities of Gehennom, the + Under World, where he now lurks, and bides his time. + +Your %G %d seeks to possess the Amulet, and with it +to gain deserved ascendance over the other gods. + +You, a newly trained %r, have been heralded +from birth as the instrument of %d. You are destined +to recover the Amulet for your deity, or die in the +attempt. Your hour of destiny has come. For the sake +of us all: Go bravely with %d! +%E +%Cp - 00002 +You receive a faint telepathic message from %l: +Your help is urgently needed at %H! +Look for a ...ic transporter. +You couldn't quite make out that last message. +%E +%Cp - 00003 +You again sense %l pleading for help. +%E +%Cp - 00004 +You again sense %l demanding your attendance. +%E +# Completed the quest by returning with artifact, but not carrying +# the Bell of Opening; quest leader lets you know that it is needed. +#[ Should this be role-specific so that each leader has variant text? ] +%Cp - 00005 +"The silver bell which was hoarded by %n will be +essential in locating the Amulet of Yendor." +%E +# +# Angelic maledictions. +# +%Cp - 00010 +"Repent, and thou shalt be saved!" +%E +%Cp - 00011 +"Thou shalt pay for thine insolence!" +%E +%Cp - 00012 +"Very soon, my child, thou shalt meet thy maker." +%E +%Cp - 00013 +"The great %D has sent me to make you pay for your sins!" +%E +%Cp - 00014 +"The wrath of %D is now upon you!" +%E +%Cp - 00015 +"Thy life belongs to %D now!" +%E +%Cp - 00016 +"Dost thou wish to receive thy final blessing?" +%E +%Cp - 00017 +"Thou art but a godless void." +%E +%Cp - 00018 +"Thou art not worthy to seek the Amulet." +%E +%Cp - 00019 +"No one expects the Spanish Inquisition!" +%E +# +# Demonic maledictions. +# +%Cp - 00030 +"I first mistook thee for a statue, when I regarded thy head of stone." +%E +%Cp - 00031 +"Come here often?" +%E +%Cp - 00032 +"Doth pain excite thee? Wouldst thou prefer the whip?" +%E +%Cp - 00033 +"Thinkest thou it shall tickle as I rip out thy lungs?" +%E +%Cp - 00034 +"Eat slime and die!" +%E +%Cp - 00035 +"Go ahead, fetch thy mama! I shall wait." +%E +%Cp - 00036 +"Go play leapfrog with a herd of unicorns!" +%E +%Cp - 00037 +"Hast thou been drinking, or art thou always so clumsy?" +%E +%Cp - 00038 +"This time I shall let thee off with a spanking, but let it not happen again." +%E +%Cp - 00039 +"I've met smarter (and prettier) acid blobs." +%E +%Cp - 00040 +"Look! Thy bootlace is undone!" +%E +%Cp - 00041 +"Mercy! Dost thou wish me to die of laughter?" +%E +%Cp - 00042 +"Run away! Live to flee another day!" +%E +%Cp - 00043 +"Thou hadst best fight better than thou canst dress!" +%E +%Cp - 00044 +"Twixt thy cousin and thee, Medusa is the prettier." +%E +%Cp - 00045 +"Methinks thou wert unnaturally stirred by yon corpse back there, eh, varlet?" +%E +%Cp - 00046 +"Up thy nose with a rubber hose!" +%E +%Cp - 00047 +"Verily, thy corpse could not smell worse!" +%E +%Cp - 00048 +"Wait! I shall polymorph into a grid bug to give thee a fighting chance!" +%E +%Cp - 00049 +"Why search for the Amulet? Thou wouldst but lose it, cretin." +%E +# +# Banishment message (for converted hero) +# +%Cc - 00060 +"You have betrayed all those who hold allegiance to %d, as you once did. +My allegiance to %d holds fast and I cannot condone or accept what you +have done. + +Leave this place. You shall never set foot at %H again. +That which you seek is now lost forever, for without the Bell of Opening, +you will never be able to enter the place where he who has the Amulet +resides. + +Go now! You are banished from this place. +%E +# +# TEST PATTERN +# +%Cc - 00099 + %p: return(plname); + %c: return(pl_character); + %r: return((char *)rank_of(u.ulevel)); + %R: return((char *)rank_of(MIN_QUEST_LEVEL)); + %s: return((flags.female) ? "sister" : "brother" ); + %S: return((flags.female) ? "daughter" : "son" ); + %l: return((char *)ldrname()); + %i: return(intermed()); + %o: return(artiname()); + %n: return((char *)neminame()); + %g: return((char *)guardname()); + %G: return((char *)align_gtitle(u.ualignbase[1])); + %H: return((char *)homebase()); + %a: return(Alignnam(u.ualignbase[1])); + %A: return(Alignnam(u.ualign.type)); + %d: return((char *)align_gname(u.ualignbase[1])); + %D: return((char *)align_gname(A_LAWFUL)); + %C: return("chaotic"); + %N: return("neutral"); + %L: return("lawful"); + %x: return((Blind) ? "sense" : "see"); + %Z: return("The Dungeons of Doom"); + %%: return(percent_sign); + a suffix: return an(root); + A suffix: return An(root); + C suffix: return capitalized(root); + p suffix: return makeplural(root); + P suffix: return makeplural(capitalized(root)); + s suffix: return s_suffix(root); + S suffix: return s_suffix(capitalized(root)); + t suffix: return strip_the_prefix(root); +%E diff --git a/dat/rumors.fal b/dat/rumors.fal new file mode 100644 index 0000000..90675fb --- /dev/null +++ b/dat/rumors.fal @@ -0,0 +1,392 @@ +"So when I die, the first thing I will see in heaven is a score list?" +1st Law of Hacking: leaving is much more difficult than entering. +2nd Law of Hacking: first in, first out. +3rd Law of Hacking: the last blow counts most. +4th Law of Hacking: you will find the exit at the entrance. +A chameleon imitating a mail daemon often delivers scrolls of fire. +A cockatrice corpse is guaranteed to be untainted! +A dead cockatrice is just a dead lizard. +A dragon is just a snake that ate a scroll of fire. +A fading corridor enlightens your insight. +A glowing potion is too hot to drink. +A good amulet may protect you against guards. +A lizard corpse is a good thing to turn undead. +A long worm can be defined recursively. So how should you attack it? +A monstrous mind is a toy forever. +A nymph will be very pleased if you call her by her real name: Lorelei. +A ring of dungeon master control is a great find. +A ring of extra ring finger is useless if not enchanted. +A rope may form a trail in a maze. +A staff may recharge if you drop it for awhile. +A visit to the Zoo is very educational; you meet interesting animals. +A wand of deaf is a more dangerous weapon than a wand of sheep. +A wand of vibration might bring the whole cave crashing about your ears. +A winner never quits. A quitter never wins. +A wish? Okay, make me a fortune cookie! +Afraid of mimics? Try to wear a ring of true seeing. +All monsters are created evil, but some are more evil than others. +Always attack a floating eye from behind! +An elven cloak is always the height of fashion. +Any small object that is accidentally dropped will hide under a larger object. +Archeologists find more bones piles. +Austin Powers says: My Mojo is back! Yeah, baby! +Balrogs do not appear above level 20. +Banana peels work especially well against Keystone Kops. +Be careful when eating bananas. Monsters might slip on the peels. +Better leave the dungeon; otherwise you might get hurt badly. +Beware of the potion of nitroglycerin -- it's not for the weak of heart. +Beware: there's always a chance that your wand explodes as you try to zap it! +Beyond the 23rd level lies a happy retirement in a room of your own. +Changing your suit without dropping your sword? You must be kidding! +Close the door! You're letting the heat out! +Cockatrices might turn themselves to stone faced with a mirror. +Consumption of home-made food is strictly forbidden in this dungeon. +Dark room? Your chance to develop your photographs! +Dark rooms are not *completely* dark: just wait and let your eyes adjust... +David London sez, "Hey guys, *WIELD* a lizard corpse against a cockatrice!" +Death is just life's way of telling you you've been fired. +Demi-gods don't need any help from the gods. +Demons *HATE* Priests and Priestesses. +Didn't you forget to pay? +Didn't your mother tell you not to eat food off the floor? +Direct a direct hit on your direct opponent, directing in the right direction. +Do you want to make more money? Sure, we all do! Join the Fort Ludios guard! +Does your boss know what you're doing right now? +Don't bother wishing for things. You'll probably find one on the next level. +Don't eat too much: you might start hiccoughing! +Don't play NetHack at your work; your boss might hit you! +Don't tell a soul you found a secret door, otherwise it isn't a secret anymore. +Drinking potions of booze may land you in jail if you are under 21. +Drop your vanity and get rid of your jewels! Pickpockets about! +Eat 10 cloves of garlic and keep all humans at a two-square distance. +Eels hide under mud. Use a unicorn to clear the water and make them visible. +Elf has extra speed. +Engrave your wishes with a wand of wishing. +Eventually you will come to admire the swift elegance of a retreating nymph. +Ever heard hissing outside? I *knew* you hadn't! +Ever lifted a dragon corpse? +Ever seen a leocrotta dancing the tengu? +Ever seen your weapon glow plaid? +Ever tamed a shopkeeper? +Ever tried digging through a Vault Guard? +Ever tried enchanting a rope? +Floating eyes can't stand Hawaiian shirts. +For any remedy there is a misery. +Giant bats turn into giant vampires. +Good day for overcoming obstacles. Try a steeplechase. +Half Moon tonight. (At least it's better than no Moon at all.) +Help! I'm being held prisoner in a fortune cookie factory! +Housecats have nine lives, kittens only one. +How long can you tread water? +Hungry? There is an abundance of food on the next level. +I guess you've never hit a mail daemon with the Amulet of Yendor... +If you are the shopkeeper, you can take things for free. +If you ask really nicely, the Wizard will give you the Amulet. +If you can't learn to do it well, learn to enjoy doing it badly. +If you thought the Wizard was bad, just wait till you meet the Warlord! +If you turn blind, don't expect your dog to be turned into a seeing-eye dog. +If you want to feel great, you must eat something real big. +If you want to float, you'd better eat a floating eye. +If your ghost kills a player, it increases your score. +Increase mindpower: Tame your own ghost! +It furthers one to see the great man. +It's easy to overlook a monster in a wood. +Just below any trap door there may be another one. Just keep falling! +Katanas are very sharp; watch you don't cut yourself. +Keep a clear mind: quaff clear potions. +Kicking the terminal doesn't hurt the monsters. +Killer bees keep appearing till you kill their queen. +Killer bunnies can be tamed with carrots only. +Latest news? Put `rec.games.roguelike.nethack' in your .newsrc! +Learn how to spell. Play NetHack! +Leprechauns hide their gold in a secret room. +Let your fingers do the walking on the yulkjhnb keys. +Let's face it: this time you're not going to win. +Let's have a party, drink a lot of booze. +Liquor sellers do not drink; they hate to see you twice. +Lunar eclipse tonight. May as well quit now! +Meeting your own ghost decreases your luck considerably! +Money to invest? Take it to the local branch of the Magic Memory Vault! +Monsters come from nowhere to hit you everywhere. +Monsters sleep because you are boring, not because they ever get tired. +Most monsters prefer minced meat. That's why they are hitting you! +Most of the bugs in NetHack are on the floor. +Much ado Nothing Happens. +Multi-player NetHack is a myth. +NetHack is addictive. Too late, you're already hooked. +Never ask a shopkeeper for a price list. +Never burn a tree, unless you like getting whacked with a +5 shovel. +Never eat with glowing hands! +Never mind the monsters hitting you: they just replace the charwomen. +Never play leapfrog with a unicorn. +Never step on a cursed engraving. +Never swim with a camera: there's nothing to take pictures of. +Never teach your pet rust monster to fetch. +Never trust a random generator in magic fields. +Never use a wand of death. +No level contains two shops. The maze is no level. So... +No part of this fortune may be reproduced, stored in a retrieval system, ... +Not all rumors are as misleading as this one. +Nymphs and nurses like beautiful rings. +Nymphs are blondes. Are you a gentleman? +Offering a unicorn a worthless piece of glass might prove to be fatal! +Old hackers never die: young ones do. +One has to leave shops before closing time. +One homunculus a day keeps the doctor away. +One level further down somebody is getting killed, right now. +Only a wizard can use a magic whistle. +Only adventurers of evil alignment think of killing their dog. +Only chaotic evils kill sleeping monsters. +Only real trappers escape traps. +Only real wizards can write scrolls. +Operation OVERKILL has started now. +Ouch. I hate when that happens. +PLEASE ignore previous rumor. +Polymorph into an ettin; meet your opponents face to face to face. +Praying will frighten demons. +Row (3x) that boat gently down the stream, Charon (4x), death is but a dream. +Running is good for your legs. +Screw up your courage! You've screwed up everything else. +Seepage? Leaky pipes? Rising damp? Summon the plumber! +Segmentation fault (core dumped). +Shopkeepers are insured by Croesus himself! +Shopkeepers sometimes die from old age. +Some mazes (especially small ones) have no solutions, says man 6 maze. +Some questions the Sphynx asks just *don't* have any answers. +Sometimes "mu" is the answer. +Sorry, no fortune this time. Better luck next cookie! +Spare your scrolls of make-edible until it's really necessary! +Stormbringer doesn't steal souls. People steal souls. +Suddenly, the dungeon will collapse... +Taming a mail daemon may cause a system security violation. +The crowd was so tough, the Stooges won't play the Dungeon anymore, nyuk nyuk. +The leprechauns hide their treasure in a small hidden room. +The longer the wand the better. +The magic word is "XYZZY". +The meek shall inherit your bones files. +The mines are dark and deep, and I have levels to go before I sleep. +The use of dynamite is dangerous. +There are no worms in the UNIX version. +There is a trap on this level! +They say that Demogorgon, Asmodeus, Orcus, Yeenoghu & Juiblex is no law firm. +They say that Geryon has an evil twin, beware! +They say that Medusa would make a terrible pet. +They say that NetHack bugs are Seldon planned. +They say that NetHack comes in 256 flavors. +They say that NetHack is just a computer game. +They say that NetHack is more than just a computer game. +They say that NetHack is never what it used to be. +They say that a baby dragon is too small to hurt or help you. +They say that a black pudding is simply a brown pudding gone bad. +They say that a black sheep has 3 bags full of wool. +They say that a blank scroll is like a blank check. +They say that a cat named Morris has nine lives. +They say that a desperate shopper might pay any price in a shop. +They say that a diamond dog is everybody's best friend. +They say that a dwarf lord can carry a pick-axe because his armor is light. +They say that a floating eye can defeat Medusa. +They say that a fortune only has 1 line and you can't read between it. +They say that a fortune only has 1 line, but you can read between it. +They say that a fountain looks nothing like a regularly erupting geyser. +They say that a gold doubloon is worth more than its weight in gold. +They say that a grid bug won't pay a shopkeeper for zapping you in a shop. +They say that a gypsy could tell your fortune for a price. +They say that a hacker named Alice once level teleported by using a mirror. +They say that a hacker named David once slew a giant with a sling and a rock. +They say that a hacker named Dorothy once rode a fog cloud to Oz. +They say that a hacker named Mary once lost a white sheep in the mazes. +They say that a helm of brilliance is not to be taken lightly. +They say that a hot dog and a hell hound are the same thing. +They say that a lamp named Aladdin's Lamp contains a djinni with 3 wishes. +They say that a large dog named Lassie will lead you to the amulet. +They say that a long sword is not a light sword. +They say that a manes won't mince words with you. +They say that a mind is a terrible thing to waste. +They say that a plain nymph will only wear a wire ring in one ear. +They say that a plumed hat could be a previously used crested helmet. +They say that a potion of oil is difficult to grasp. +They say that a potion of yogurt is a cancelled potion of sickness. +They say that a purple worm is not a baby purple dragon. +They say that a quivering blob tastes different than a gelatinous cube. +They say that a runed broadsword named Stormbringer attracts vortices. +They say that a scroll of summoning has other names. +They say that a shaman can bestow blessings but usually doesn't. +They say that a shaman will bless you for an eye of newt and wing of bat. +They say that a shimmering gold shield is not a polished silver shield. +They say that a spear will hit a neo-otyugh. (Do YOU know what that is?) +They say that a spotted dragon is the ultimate shape changer. +They say that a stethoscope is no good if you can only hear your heartbeat. +They say that a succubus named Suzy will sometimes warn you of danger. +They say that a wand of cancellation is not like a wand of polymorph. +They say that a wood golem named Pinocchio would be easy to control. +They say that after killing a dragon it's time for a change of scenery. +They say that an amulet of strangulation is worse than ring around the collar. +They say that an attic is the best place to hide your toys. +They say that an axe named Cleaver once belonged to a hacker named Beaver. +They say that an eye of newt and a wing of bat are double the trouble. +They say that an incubus named Izzy sometimes makes women feel sensitive. +They say that an opulent throne room is rarely a place to wish you'd be in. +They say that an unlucky hacker once had a nose bleed at an altar and died. +They say that and they say this but they never say never, never! +They say that any quantum mechanic knows that speed kills. +They say that applying a unicorn horn means you've missed the point. +They say that blue stones are radioactive, beware. +They say that building a dungeon is a team effort. +They say that chaotic characters never get a kick out of altars. +They say that collapsing a dungeon often creates a panic. +They say that counting your eggs before they hatch shows that you care. +They say that dipping a bag of tricks in a fountain won't make it an icebox. +They say that dipping an eel and brown mold in hot water makes bouillabaisse. +They say that donating a doubloon is extremely pious charity. +They say that dungeoneers prefer dark chocolate. +They say that eating royal jelly attracts grizzly owlbears. +They say that eggs, pancakes and juice are just a mundane breakfast. +They say that everyone knows why Medusa stands alone in the dark. +They say that everyone wanted rec.games.hack to undergo a name change. +They say that finding a winning strategy is a deliberate move on your part. +They say that finding worthless glass is worth something. +They say that fortune cookies are food for thought. +They say that gold is only wasted on a pet dragon. +They say that good things come to those that wait. +They say that greased objects will slip out of monsters' hands. +They say that if you can't spell then you'll wish you had a spellbook. +They say that if you live by the sword, you'll die by the sword. +They say that if you play like a monster you'll have a better game. +They say that if you sleep with a demon you might awake with a headache. +They say that if you step on a crack you could break your mother's back. +They say that if you're invisible you can still be heard! +They say that if you're lucky you can feel the runes on a scroll. +They say that in the big picture gold is only small change. +They say that in the dungeon it's not what you know that really matters. +They say that in the dungeon moon rocks are really dilithium crystals. +They say that in the dungeon the boorish customer is never right. +They say that in the dungeon you don't need a watch to tell time. +They say that in the dungeon you need something old, new, burrowed and blue. +They say that in the dungeon you should always count your blessings. +They say that iron golem plate mail isn't worth wishing for. +They say that it takes four quarterstaffs to make one staff. +They say that it's not over till the fat ladies sing. +They say that it's not over till the fat lady shouts `Off with its head'. +They say that kicking a heavy statue is really a dumb move. +They say that kicking a valuable gem doesn't seem to make sense. +They say that leprechauns know Latin and you should too. +They say that minotaurs get lost outside of the mazes. +They say that most trolls are born again. +They say that naming your cat Garfield will make you more attractive. +They say that no one knows everything about everything in the dungeon. +They say that no one plays NetHack just for the fun of it. +They say that no one really subscribes to rec.games.roguelike.nethack. +They say that no one will admit to starting a rumor. +They say that nurses sometimes carry scalpels and never use them. +They say that once you've met one wizard you've met them all. +They say that one troll is worth 10,000 newts. +They say that only David can find the zoo! +They say that only angels play their harps for their pets. +They say that only big spenders carry gold. +They say that orc shamans are healthy, wealthy and wise. +They say that playing NetHack is like walking into a death trap. +They say that problem breathing is best treated by a proper diet. +They say that quaffing many potions of levitation can give you a headache. +They say that queen bees get that way by eating royal jelly. +They say that reading a scare monster scroll is the same as saying Elbereth. +They say that real hackers always are controlled. +They say that real hackers never sleep. +They say that shopkeepers are insured by Croesus himself! +They say that shopkeepers never carry more than 20 gold pieces, at night. +They say that shopkeepers never sell blessed potions of invisibility. +They say that soldiers wear kid gloves and silly helmets. +They say that some Kops are on the take. +They say that some guards' palms can be greased. +They say that some monsters may kiss your boots to stop your drum playing. +They say that sometimes you can be the hit of the party when playing a horn. +They say that the NetHack gods generally welcome your sacrifices. +They say that the Three Rings are named Vilya, Nenya and Narya. +They say that the Wizard of Yendor has a death wish. +They say that the `hair of the dog' is sometimes an effective remedy. +They say that the best time to save your game is now before it's too late. +They say that the biggest obstacle in NetHack is your mind. +They say that the gods are angry when they hit you with objects. +They say that the priesthood are specially favored by the gods. +They say that the way to make a unicorn happy is to give it what it wants. +They say that there are no black or white stones, only gray. +They say that there are no skeletons hence there are no skeleton keys. +They say that there is a clever rogue in every hacker just dying to escape. +They say that there is no such thing as free advice. +They say that there is only one way to win at NetHack. +They say that there once was a fearsome chaotic samurai named Luk No. +They say that there was a time when cursed holy water wasn't water. +They say that there's no point in crying over a gray ooze. +They say that there's only hope left after you've opened Pandora's box. +They say that trap doors should always be marked `Caution: Trap Door'. +They say that using an amulet of change isn't a difficult operation. +They say that water walking boots are better if you are fast like Hermes. +They say that when you wear a circular amulet you might resemble a troll. +They say that when you're hungry you can get a pizza in 30 moves or it's free. +They say that when your god is angry you should try another one. +They say that wielding a unicorn horn takes strength. +They say that with speed boots you never worry about hit and run accidents. +They say that you can defeat a killer bee with a unicorn horn. +They say that you can only cross the River Styx in Charon's boat. +They say that you can only kill a lich once and then you'd better be careful. +They say that you can only wish for things you've already had. +They say that you can train a cat by talking gently to it. +They say that you can train a dog by talking firmly to it. +They say that you can trust your gold with the king. +They say that you can't wipe your greasy bare hands on a blank scroll. +They say that you cannot trust scrolls of rumor. +They say that you could fall head over heels for an energy vortex. +They say that you need a key in order to open locked doors. +They say that you need a mirror to notice a mimic in an antique shop. +They say that you really can use a pick-axe unless you really can't. +They say that you should always store your tools in the cellar. +They say that you should be careful while climbing the ladder to success. +They say that you should call your armor `rustproof'. +They say that you should name your dog Spuds to have a cool pet. +They say that you should name your weapon after your first monster kill. +They say that you should never introduce a rope golem to a succubus. +They say that you should never sleep near invisible ring wraiths. +They say that you should never try to leave the dungeon with a bag of gems. +They say that you should remove your armor before sitting on a throne. +This fortune cookie is copy protected. +This fortune cookie is the property of Fortune Cookies, Inc. +This release contains 10% recycled material. +Time stands still as the succubus changes her calendar to January 1, 2000. +Tired? Try a scroll of charging on yourself. +To achieve the next higher rating, you need 3 more points. +To reach heaven, escape the dungeon while wearing a ring of levitation. +Tourists wear shirts loud enough to wake the dead. +Try calling your katana Moulinette. +Ulch! That meat was painted! +Unfortunately, this message was left intentionally blank. +Using a morning star in the evening has no effect. +Waltz, dumb nymph, for quick jigs vex. +Want a hint? Zap a wand of make invisible on your weapon! +Want to ascend in a hurry? Apply at Gizmonic Institute. +Wanted: shopkeepers. Send a scroll of mail to Mage of Yendor/Level 35/Dungeon. +Warning: fortune reading can be hazardous to your health. +We have new ways of detecting treachery... +Wet towels make great weapons! +What a pity, you cannot read it! +Whatever can go wrong, will go wrong. +When a piercer drops in on you, you will be tempted to hit the ceiling! +When in a maze follow the right wall and you will never get lost. +When you have a key, you don't have to wait for the guard. +Why are you wasting time reading fortunes? +Wish for a master key and open the Magic Memory Vault! +Wizard expects every monster to do its duty. +Wow! You could've had a potion of fruit juice! +Yet Another Silly Message (YASM). +You are destined to be misled by a fortune. +You can get a genuine Amulet of Yendor by doing the following: --More-- +You can make holy water by boiling the hell out of it. +You can protect yourself from black dragons by doing the following: --More-- +You can't get by the snake. +You choke on the fortune cookie. --More-- +You feel like someone is pulling your leg. +You have to outwit the Sphynx or pay her. +You hear the fortune cookie's hissing! +You may get rich selling letters, but beware of being blackmailed! +You offend Shai-Hulud by sheathing your crysknife without having drawn blood. +You swallowed the fortune! +You want to regain strength? Two levels ahead is a guesthouse! +You will encounter a tall, dark, and gruesome creature... diff --git a/dat/rumors.tru b/dat/rumors.tru new file mode 100644 index 0000000..cfbdb8b --- /dev/null +++ b/dat/rumors.tru @@ -0,0 +1,359 @@ +A blindfold can be very useful if you're telepathic. +A candelabrum affixed with seven candles shows the way with a magical light. +A crystal plate mail will not rust. +A katana might slice a worm in two. +A magic vomit pump could be useful for gourmands. +A nymph knows how to unlock chains. +A potion of blindness lets you see invisible things. +A priest can get the gods to listen easily. +A priestess and a virgin you might be, but that unicorn won't care. +A ring of conflict is a bad thing if there is a nurse in the room. +A short sword is not as good as a long sword. +A succubus will go farther than a nymph. +A wand can exorcize a past explorer's ghost. +Acid blobs should be attacked bare-handed. +Affairs with nymphs are often very expensive. +Afraid of nymphs? Wear a ring of adornment. +Afraid of your valuables being stolen? Carry more junk! +Always be aware of the phase of the moon! +Always sweep the floor before engraving important messages. +Amulets of Yendor are hard to make. Even for a wand of wishing. +An elven cloak protects against magic. +An umber hulk can be a confusing sight. +As Crom is my witness, I'll never go hungry again! +Asking about monsters may be very useful. +Attack long worms from the rear -- that is so much safer! +Attacking an eel where there is none is usually a fatal mistake! +Bandaging wounds helps keep up appearances. +Bashing monsters with a bow is not such a good idea. +Be careful! The Wizard may plan an ambush! +Be nice to a nurse: Put away your weapon and take off your clothes. +Being digested is a painfully slow process. +Blank scrolls make more interesting reading. +Blind? Catch a floating eye! +Booksellers never read scrolls; they might get carried away. +Chemistry 101: Never pour water into acid. +Concise conquest: Control, confuse, conjure, condemn. +Conserve energy, turn off the lights. +Digging up a grave could be a bad idea... +Dilithium crystals are rare indeed. +Dogs are attracted by the smell of tripe. +Dogs are superstitious; they never step on cursed items. +Dogs of ghosts aren't angry, just hungry. +Don't forget! Large dogs are MUCH harder to kill than little dogs. +Don't mess with shopkeepers, or you'll get the Guild after you. +Dragons never whip their children; they wouldn't feel it! +Eat your carrots. They're good for your eyes. +Eating a freezing sphere is like eating a yeti. +Eating a killer bee is like eating a scorpion. +Eating a tengu is like eating a nymph. +Eating a wraith is a rewarding experience! +Eating unpaid leprechauns may be advantageous. +Elbereth has quite a reputation around these parts. +Elf corpses are incompatible with the sandman, and at times the gods as well. +Elven cloaks cannot rust. +Even evil players have a guardian angel. +Ever fought with an enchanted tooth? +Ever tried reading while confused? +Ever tried to put a troll into a large box? +Ever wondered why one would want to dip something in a potion? +Expensive cameras have penetrating flash lights. +Extra staircases lead to extra levels. +Fiery letters might deter monsters. +For a good time engrave `Elbereth'. +Gems are too precious to be thrown away carelessly. +Getting hungry? Stop wearing rings! +Getting too warm? Take off that Amulet of Yendor and stay away from the exit! +Gods expect the best from their priesthood. +Gods look down their noses at demigods. +Got a question? Try rec.games.roguelike.nethack. +Grave robbers sometimes get rich. +Guy Montag keeps his scrolls in a bag. +Handle your flasks carefully -- there might be a ghost inside! +Holy water has many uses. +Horses trust their riders, even when not so deserved. +Hunger is a confusing experience for a dog! +I once knew a hacker who ate too fast and choked to death. +I smell a maze of twisty little passages. +I wish I never wished a wand of wishing. (Wishful thinking.) +I wouldn't advise playing catch with a giant. +I'm watching you. -- The Wizard of Yendor +Ice boxes keep your food fresh. +If you are being punished, it's done with a deadly weapon. +If you kill the Wizard, you get promoted to demi-god. +If you need a wand of digging, kindly ask the minotaur. +If you want to hit, use a dagger. +If you want to rob a shop, train your dog. +If you're lost, try buying a map next time you're in a shop. +Inside a shop you better take a look at the price tags before buying anything. +It is bad manners to use a wand in a shop. +It is dangerous to visit a graveyard at midnight. +It is not always a good idea to whistle for your dog. +It is rumored that the Wizard has hired some help. +It is the letter 'c' and not 'e' that changes status to statue. +It might be a good idea to offer the unicorn a ruby. +It would be peculiarly sad were your dog turned to stone. +It's a `d' eats `d' world. +Keep your armors away from rust. +Keep your weaponry away from acids. +Kill a unicorn of your color and you kill your luck. +Leather is waterproof. Ever see a cow with an umbrella? +Leprechauns are the most skilled cutpurses in this dungeon. +Lizard corpses protect against cockatrices. +Money lost, little lost; honor lost, much lost; pluck lost, all lost. +Most monsters can't swim. +Music hath charms to affect the stubborn drawbridge. +Music hath charms to soothe the savage beast. +Never attack a guard. +Never ride a long worm. +Never use your best weapon to engrave a curse. +No easy fighting with a heavy load! +Nurses are trained to touch naked persons: they don't harm them. +Nymphs can unlink more than your chain mail. +Once your little dog will be a big dog, and you will be proud of it. +Only female monsters can lay eggs. +Opening a tin is difficult, especially when you attempt it bare handed! +Orcs and killer bees share their lifestyle. +Orcs do not procreate in dark rooms. +Plain nymphs are harmless. +Playing AD&D may be helpful. +Playing Gauntlet might be enlightening in some situations. +Playing billiards pays when you are in a shop. +Polymorphing a shopkeeper might make you safer. +Polymorphing your dog probably makes you safer. +Potions don't usually mix, but sometimes... +Psst! It's done with mirrors! +Put on a ring of teleportation: it will take you away from onslaught. +Rays aren't boomerangs, of course, but still... +Read the manual before entering the cave -- you might get killed otherwise. +Reading Herbert might be enlightening in one case. +Reading Tolkien might help you. +Reading scrolls after drinking booze can give confusing results. +Riding a dragon can be an uplifting experience. +Rust monsters love water. There are potions they hate, however. +Sacks protect contents from temperatures up to 452 degrees fahrenheit. +Scrolls fading? It's not the heat, it's the humidity. +Shopkeepers accept credit cards, as long as you pay cash. +Shopkeepers can spot a tourist a mile away with those Hawaiian shirts. +Shopkeepers can't tell identical twins apart. +Shopkeepers don't read, so what use is engraving in a shop? +Shopkeepers have incredible patience. +Shopkeepers might raise their prices for tourists. +Shopkeepers value money more than revenge. +Some monsters can be tamed. I once saw a hacker with a tame dragon! +Someone once said that what goes up < might come down >. +Someone's been spiking the pits! +Sometimes monsters are more likely to fight each other than attack you. +Spinach, carrot, and jelly -- a meal fit for a nurse! +Tainted meat is even more sickening than poison! +Telepathy is just a trick: once you know how to do it, it's easy. +The Leprechaun Gold Tru$t is no division of the Magic Memory Vault. +The Wizard finds death to be quite an experience. +The best equipment for your work is, of course, the most expensive. +The gods don't appreciate pesky priesthood. +The gods will get angry if you kill your dog. +The magic marker is mightier than the sword. +The moon is not the only heavenly body to influence this game. +The orc swings his orcish broadsword named Elfrist at you. You die... +The secret of wands of Nothing Happens: try again! +There has always been something mystical about mirrors. +There is a Mastermind deep in the dungeon. +There is a big treasure hidden in the zoo! +There is more magic in this cave than meets the eye. +There is no harm in praising a large dog. +There is nothing like eating a mimic. +There once was a Knight named Lancelot who liked to ride with his lance a lot. +They say a gelatinous cube can paralyze you... +They say that Juiblex is afraid of a wand of digging. +They say that Medusa would like to put you on a pedestal. +They say that Vlad lives!!! ... in the mazes. +They say that `Elbereth' is often written about. +They say that a bag of holding can't hold everything. +They say that a blessed tin of quasit meat is a quick meal. +They say that a cat avoids traps. +They say that a cave spider will occasionally eat cave spider eggs. +They say that a clever wizard can have stats: 18/** 24 18 24 24 24. +They say that a clove of garlic makes a good talisman if handled right. +They say that a cursed scroll of teleportation could land you in trouble. +They say that a diamond is another kind of luck stone. +They say that a dog can be trained to fetch objects. +They say that a gelatinous cube makes a healthy breakfast. +They say that a giant gets strong by eating right, try it! +They say that a grid bug won't hit you when you cross it. +They say that a lembas wafer is a very light snack. +They say that a loadstone has a strange attraction and is not bad luck. +They say that a lock pick by any other name is still a lock pick. +They say that a lucky amulet will block poisoned arrows. +They say that a mirror will freeze a floating eye but you can still see it. +They say that a neutral character might get Giantslayer. +They say that a polymorph trap is magic and magic protection prevents it. +They say that a potion of healing can cancel a potion of sickness. +They say that a potion of monster detection sometimes works both ways. +They say that a sink looks different from high above the floor. +They say that a summoned demon could improve your game. +They say that a tin of wraith meat is a rare dining experience. +They say that a unicorn might bring you luck. +They say that a wand of cancellation is like a wand of polymorph. +They say that a wand of locking can close more than just doors. +They say that a wand of polymorph can change your game. +They say that a wizard is even more powerful the second time around. +They say that a xorn knows of no obstacles when pursuing you. +They say that abusing a credit card could shock you sooner or later. +They say that amulets, like most things, can be deadly or life saving. +They say that an altar can identify blessings. +They say that an ooze will bite your boots and a rockmole will eat them. +They say that an unlucky hacker was once killed by an exploding tin. +They say that antique dealers are always interested in precious stones. +They say that bandaging one's wounds helps to keep up one's appearance. +They say that booze can be diluted but not cancelled. +They say that by listening carefully, you can hear a secret door! +They say that carrots and carrot juice may improve your vision. +They say that cave spiders are not considered expensive health food. +They say that demigods must leave behind their prized earthly possessions. +They say that disturbing a djinni can be a costly mistake. +They say that dragon scales can be quite enchanting. +They say that dropping coins into a fountain will not grant you a wish. +They say that dwarves lawfully mind their own business. +They say that eating a bat corpse will make you batty, for a while. +They say that eating a cram ration is a smart move. +They say that eating blue jelly is cool if you don't fight the feeling. +They say that escaping a dungeon is only the beginning of the end. +They say that feeling an unexpected draft of air is sort of a breakthrough. +They say that finding a cursed gray stone is always bad luck. +They say that gaining a level is an experience that can raise your sights. +They say that garter snake meat rarely tastes good but it's still healthy. +They say that gauntlets of dexterity have a hidden enchanted touch. +They say that going to heaven is just another way of escaping the dungeon. +They say that golden nagas are law-abiding denizens as long as you are too. +They say that gremlins can make you feel cooler than you are now. +They say that grid bugs only exist in a strictly Cartesian sense. +They say that hackers often feel jumpy about eating nymphs. +They say that having polymorph control won't shock you. +They say that if it's hard getting your food down another bite could kill. +They say that if you don't wear glasses why bother with carrots? +They say that if you notice a loose board beneath you, don't step on it. +They say that if you start at the bottom the only place to go is up. +They say that if you teleport to heaven you're presumed to be dead already. +They say that in a shop you can be charged for old charges. +They say that in lighter moments you could think of ways to pass a stone. +They say that in the dungeon breaking a mirror can be seven years bad luck. +They say that in the dungeon you don't usually have any luck at all. +They say that in time a blessed luckstone can make your god happy. +They say that it is easier to kill the Wizard than to make him stand still. +They say that it only takes 1 zorkmid to meet the Kops. +They say that it's a blast when you mix the right potions together. +They say that it's not blind luck if you catch a glimpse of Medusa. +They say that killing a shopkeeper brings bad luck. +They say that monsters never step on a scare monster scroll. +They say that most monsters find flute recitals extremely boring. +They say that mummy corpses are not well preserved. +They say that naturally a wand of wishing would be heavily guarded. +They say that no one notices the junk underneath a boulder. +They say that nobody expects a unicorn horn to rust. +They say that nobody knows if an explorer can live forever. Do you? +They say that nothing can change the fact that some potions contain a djinni. +They say that nothing can change the fact that some potions contain a ghost. +They say that nymphs always fall for rock'n'roll, try it! +They say that once an Olog-Hai is canned it never shows its face again. +They say that once upon a time xans would never scratch your boots. +They say that only an experienced wizard can do the tengu shuffle. +They say that only chaotics can kill shopkeepers and get away with it. +They say that only female monsters can lay eggs. +They say that playing a horn really bad is really good. +They say that rubbing a glowing potion does not make it a magic lamp. +They say that scalpels become dull because they're not athames. +They say that shopkeepers don't like pick-axes. +They say that shopkeepers don't mind you bringing your pets in the shop. +They say that shopkeepers don't usually mind if you sneak into a shop. +They say that shopkeepers often have a large amount of money in their purses. +They say that shopkeepers often remember things that you might forget. +They say that sinks and armor don't mix, take your cloak off now! +They say that sinks run hot and cold and many flavors in between. +They say that snake charmers aren't charismatic, just musical. +They say that soldiers are always prepared and usually protected. +They say that some eggs could hatch in your pack, lucky or not. +They say that some fire ants will make you a hot meal. +They say that some horns play hot music and others are too cool for words. +They say that some humanoids are nonetheless quite human. +They say that some shopkeepers consider gems to be family heirlooms. +They say that some shopkeepers recognize gems but they won't tell you. +They say that some stones are much much heavier than others. +They say that some yetis are full of hot air. +They say that something very special would be in a well-protected place. +They say that speed boots aren't fast enough to let you walk on water. +They say that teleport traps are the devil's work. +They say that tengu don't wear rings, why should you? +They say that tengu never steal gold although they would be good at it. +They say that that which was stolen once can be stolen again, ask any nymph. +They say that the Delphic Oracle knows that lizard corpses aren't confusing. +They say that the Hand of Elbereth can hold up your prayers. +They say that the Leprechaun King is rich as Croesus. +They say that the Wizard of Yendor is schizophrenic and suicidal. +They say that the experienced character knows how to convert an altar. +They say that the gods are happy when they drop objects at your feet. +They say that the idea of invisible Nazguls has a certain ring to it. +They say that the lady of the lake now lives in a fountain somewhere. +They say that the local shopkeeper frowns upon the rude tourist. +They say that the only door to the vampire's tower is on its lowest level. +They say that the only good djinni is a grateful djinni. +They say that the thing about genocide is that it works both ways. +They say that the unicorn horn rule is if it ain't broke then don't fix it. +They say that the view from a fog cloud is really very moving. +They say that the walls in shops are made of extra hard material. +They say that there are at least 15 ways to lose a pair of levitation boots. +They say that throwing glass gems is the same as throwing rocks. +They say that trespassing a boulder is probably beneath you. +They say that unicorns are fond of precious gems. +They say that prayer at an altar can sometimes make the water there holy. +They say that what goes down the drain might come back up. +They say that wielded, a long sword named Fire Brand makes you feel cooler. +They say that wielded, a long sword named Frost Brand makes you hot stuff. +They say that wiping its face is impossible for a floating eye. +They say that with a floating eye you could see in the dark. +They say that you are lucky if you can get a unicorn to catch a ruby. +They say that you are what you eat. +They say that you can find named weapons at an altar if you're lucky. +They say that you can safely touch cockatrice eggs but why bother? +They say that you can't break an amulet of reflection. +They say that you don't always get what you wish for. +They say that you should always be prepared for a final challenge. +They say that you should ask a dwarf to let you into a locked shop. +They say that you should pray for divine inspiration. +They say that you should religiously give your gold away. +They say that you will never get healthy by eating geckos. +They say that zapping yourself with a wand of undead turning is stupid. +They say the Wizard's castle is booby-trapped! +They say the gods get angry if you kill your dog. +They say the gods get angry if you pray too much. +They say there is a powerful magic item hidden in a castle deep down! +Those who wield a cockatrice corpse have a rocky road ahead of them. +Throwing food at a wild dog might tame him. +To a full belly all food is bad. +Trolls are described as rubbery: they keep bouncing back. +Try the fall-back end-run play against ghosts. +Try using your magic marker on wet scrolls. +Two wrongs don't make a right, but three lefts do. +Valkyries come from the north, and have commensurate abilities. +Vampires hate garlic. +Vault guards never disturb their Lords. +Vegetarians enjoy lichen and seaweed. +Visitors are requested not to apply genocide to shopkeepers. +Watch out, the Wizard might come back. +Water traps have no effect on dragons. +What is a cockatrice going to eat when it gets hungry? +Who needs an apron if they're made of glass? +Why do you suppose they call them MAGIC markers? +Why do you think they call them mercenaries? +Why would anybody in his sane mind engrave "Elbereth"? +Wishing too much may bring you too little. +You can't bribe soldier ants. +You can't leave a shop through the back door: there isn't one! +You may discover a fine spirit inside a potion bottle. +You may want to dip into a potion of bottled blessings. +You might be able to bribe a demon lord. +You might trick a shopkeeper if you're invisible. +You should certainly learn about quantum mechanics. +You're going into the morgue at midnight??? +Your dog knows what to eat; maybe you should take lessons. +Zap yourself and see what happens... +Zapping a wand of undead turning might bring your dog back to life. diff --git a/dat/sokoban.des b/dat/sokoban.des new file mode 100644 index 0000000..2e1fd0e --- /dev/null +++ b/dat/sokoban.des @@ -0,0 +1,623 @@ +# SCCS Id: @(#)sokoban.des 3.4 1999/03/15 +# Copyright (c) 1998-1999 by Kevin Hugo +# NetHack may be freely redistributed. See license for details. +# +# In case you haven't played the game Sokoban, you'll learn +# quickly. This branch isn't particularly difficult, just time +# consuming. Some players may wish to skip this branch. +# +# The following actions are currently permitted without penalty: +# Carrying or throwing a boulder already in inventory +# (player or nonplayer). +# Teleporting boulders. +# Digging in the floor. +# The following actions are permitted, but with a luck penalty: +# Breaking boulders. +# Stone-to-fleshing boulders. +# Creating new boulders (e.g., with a scroll of earth). +# Jumping. +# Being pulled by a thrown iron ball. +# Hurtling through the air from Newton's 3rd law. +# Squeezing past boulders when naked or as a giant. +# These actions are not permitted: +# Moving diagonally between two boulders and/or walls. +# Pushing a boulder diagonally. +# Picking up boulders (player or nonplayer). +# Digging or walking through walls. +# Teleporting within levels or between levels of this branch. +# Using cursed potions of gain level. +# Escaping a pit/hole (e.g., by flying, levitation, or +# passing a dexterity check). +# Bones files are not permitted. + + +### Bottom (first) level of Sokoban ### +MAZE:"soko4-1",' ' +FLAGS:noteleport,hardfloor +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP +------ ----- +|....| |...| +|....----...| +|...........| +|..|-|.|-|..| +---------|.--- +|......|.....| +|..----|.....| +--.| |.....| + |.|---|.....| + |...........| + |..|--------- + ---- +ENDMAP +BRANCH:(06,04,06,04),(0,0,0,0) +STAIR:(06,06),up +REGION:(00,00,13,12),lit,"ordinary" +NON_DIGGABLE:(00,00,13,12) +NON_PASSWALL:(00,00,13,12) + +# Boulders +OBJECT:'`',"boulder",(02,02) +OBJECT:'`',"boulder",(02,03) +# +OBJECT:'`',"boulder",(10,02) +OBJECT:'`',"boulder",(09,03) +OBJECT:'`',"boulder",(10,04) +# +OBJECT:'`',"boulder",(08,07) +OBJECT:'`',"boulder",(09,08) +OBJECT:'`',"boulder",(09,09) +OBJECT:'`',"boulder",(08,10) +OBJECT:'`',"boulder",(10,10) + +# Traps +TRAP:"pit",(03,06) +TRAP:"pit",(04,06) +TRAP:"pit",(05,06) +TRAP:"pit",(02,08) +TRAP:"pit",(02,09) +TRAP:"pit",(04,10) +TRAP:"pit",(05,10) +TRAP:"pit",(06,10) +TRAP:"pit",(07,10) + +# A little help +OBJECT:'?',"earth",(02,11) +OBJECT:'?',"earth",(03,11) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +MAZE:"soko4-2",' ' +FLAGS:noteleport,hardfloor +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP +-------- ------ +|.|....|-|....| +|.|-..........| +|.||....|.....| +|.||....|.....| +|.|-----|.----- +|.| |......| +|.-----|......| +|.............| +|..|---|......| +---- -------- +ENDMAP +BRANCH:(03,01,03,01),(0,0,0,0) +STAIR:(01,01),up +REGION:(00,00,14,10),lit,"ordinary" +NON_DIGGABLE:(00,00,14,10) +NON_PASSWALL:(00,00,14,10) + +# Boulders +OBJECT:'`',"boulder",(05,02) +OBJECT:'`',"boulder",(06,02) +OBJECT:'`',"boulder",(06,03) +OBJECT:'`',"boulder",(07,03) +# +OBJECT:'`',"boulder",(09,05) +OBJECT:'`',"boulder",(10,03) +OBJECT:'`',"boulder",(11,02) +OBJECT:'`',"boulder",(12,03) +# +OBJECT:'`',"boulder",(07,08) +OBJECT:'`',"boulder",(08,08) +OBJECT:'`',"boulder",(09,08) +OBJECT:'`',"boulder",(10,08) + +# Traps +TRAP:"pit",(01,02) +TRAP:"pit",(01,03) +TRAP:"pit",(01,04) +TRAP:"pit",(01,05) +TRAP:"pit",(01,06) +TRAP:"pit",(01,07) +TRAP:"pit",(03,08) +TRAP:"pit",(04,08) +TRAP:"pit",(05,08) +TRAP:"pit",(06,08) + +# A little help +OBJECT:'?',"earth",(01,09) +OBJECT:'?',"earth",(02,09) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +### Second level ### +MAZE:"soko3-1",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP +----------- ----------- +|....|....|-- |.........| +|....|......| |.........| +|.........|-- |.........| +|....|....| |.........| +|-.--------- |.........| +|....|.....| |.........| +|....|.....| |.........| +|..........| |.........| +|....|.....|---------------+| +|....|......................| +----------------------------- +ENDMAP +STAIR:(11,02),down +STAIR:(23,04),up +DOOR:locked,(27,09) +REGION:(00,00,28,11),lit,"ordinary" +NON_DIGGABLE:(00,00,28,11) +NON_PASSWALL:(00,00,28,11) + +# Boulders +OBJECT:'`',"boulder",(03,02) +OBJECT:'`',"boulder",(04,02) +# +OBJECT:'`',"boulder",(06,02) +OBJECT:'`',"boulder",(06,03) +OBJECT:'`',"boulder",(07,02) +# +OBJECT:'`',"boulder",(03,06) +OBJECT:'`',"boulder",(02,07) +OBJECT:'`',"boulder",(03,07) +OBJECT:'`',"boulder",(03,08) +OBJECT:'`',"boulder",(02,09) +OBJECT:'`',"boulder",(03,09) +OBJECT:'`',"boulder",(04,09) +# +OBJECT:'`',"boulder",(06,07) +OBJECT:'`',"boulder",(06,09) +OBJECT:'`',"boulder",(08,07) +OBJECT:'`',"boulder",(08,10) +OBJECT:'`',"boulder",(09,08) +OBJECT:'`',"boulder",(09,09) +OBJECT:'`',"boulder",(10,07) +OBJECT:'`',"boulder",(10,10) + +# Traps +TRAP:"hole",(12,10) +TRAP:"hole",(13,10) +TRAP:"hole",(14,10) +TRAP:"hole",(15,10) +TRAP:"hole",(16,10) +TRAP:"hole",(17,10) +TRAP:"hole",(18,10) +TRAP:"hole",(19,10) +TRAP:"hole",(20,10) +TRAP:"hole",(21,10) +TRAP:"hole",(22,10) +TRAP:"hole",(23,10) +TRAP:"hole",(24,10) +TRAP:"hole",(25,10) +TRAP:"hole",(26,10) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +MAZE:"soko3-2",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP + ---- ----------- +-|..|------- |.........| +|..........| |.........| +|..-----.-.| |.........| +|..|...|...| |.........| +|.........-| |.........| +|.......|..| |.........| +|.----..--.| |.........| +|........|.-- |.........| +|.---.-.....------------+| +|...|...-................| +|.........---------------- +----|..|..| + ------- +ENDMAP +STAIR:(03,01),down +STAIR:(20,04),up +DOOR:locked,(24,09) +REGION:(00,00,25,13),lit,"ordinary" +NON_DIGGABLE:(00,00,25,13) +NON_PASSWALL:(00,00,25,13) + +# Boulders +OBJECT:'`',"boulder",(02,03) +OBJECT:'`',"boulder",(08,03) +OBJECT:'`',"boulder",(09,04) +OBJECT:'`',"boulder",(02,05) +OBJECT:'`',"boulder",(04,05) +OBJECT:'`',"boulder",(09,05) +OBJECT:'`',"boulder",(02,06) +OBJECT:'`',"boulder",(05,06) +OBJECT:'`',"boulder",(06,07) +OBJECT:'`',"boulder",(03,08) +OBJECT:'`',"boulder",(07,08) +OBJECT:'`',"boulder",(05,09) +OBJECT:'`',"boulder",(10,09) +OBJECT:'`',"boulder",(07,10) +OBJECT:'`',"boulder",(10,10) +OBJECT:'`',"boulder",(03,11) + +# Traps +TRAP:"hole",(12,10) +TRAP:"hole",(13,10) +TRAP:"hole",(14,10) +TRAP:"hole",(15,10) +TRAP:"hole",(16,10) +TRAP:"hole",(17,10) +TRAP:"hole",(18,10) +TRAP:"hole",(19,10) +TRAP:"hole",(20,10) +TRAP:"hole",(21,10) +TRAP:"hole",(22,10) +TRAP:"hole",(23,10) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +### Third level ### +MAZE:"soko2-1",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP +-------------------- +|........|...|.....| +|.....-..|.-.|.....| +|..|.....|...|.....| +|-.|..-..|.-.|.....| +|...--.......|.....| +|...|...-...-|.....| +|...|..|...--|.....| +|-..|..|----------+| +|..................| +|...|..|------------ +-------- +ENDMAP +STAIR:(06,10),down +STAIR:(16,04),up +DOOR:locked,(18,08) +REGION:(00,00,19,11),lit,"ordinary" +NON_DIGGABLE:(00,00,19,11) +NON_PASSWALL:(00,00,19,11) + +# Boulders +OBJECT:'`',"boulder",(02,02) +OBJECT:'`',"boulder",(03,02) +# +OBJECT:'`',"boulder",(05,03) +OBJECT:'`',"boulder",(07,03) +OBJECT:'`',"boulder",(07,02) +OBJECT:'`',"boulder",(08,02) +# +OBJECT:'`',"boulder",(10,03) +OBJECT:'`',"boulder",(11,03) +# +OBJECT:'`',"boulder",(02,07) +OBJECT:'`',"boulder",(02,08) +OBJECT:'`',"boulder",(03,09) +# +OBJECT:'`',"boulder",(05,07) +OBJECT:'`',"boulder",(06,06) + +# Traps +TRAP:"hole",(08,09) +TRAP:"hole",(09,09) +TRAP:"hole",(10,09) +TRAP:"hole",(11,09) +TRAP:"hole",(12,09) +TRAP:"hole",(13,09) +TRAP:"hole",(14,09) +TRAP:"hole",(15,09) +TRAP:"hole",(16,09) +TRAP:"hole",(17,09) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +MAZE:"soko2-2",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP + -------- +--|.|....| +|........|---------- +|.-...-..|.|.......| +|...-......|.......| +|.-....|...|.......| +|....-.--.-|.......| +|..........|.......| +|.--...|...|.......| +|....-.|---|.......| +--|....|----------+| + |................| + ------------------ +ENDMAP +STAIR:(06,11),down +STAIR:(15,06),up +DOOR:locked,(18,10) +REGION:(00,00,19,12),lit,"ordinary" +NON_DIGGABLE:(00,00,19,12) +NON_PASSWALL:(00,00,19,12) + +# Boulders +OBJECT:'`',"boulder",(04,02) +OBJECT:'`',"boulder",(04,03) +OBJECT:'`',"boulder",(05,03) +OBJECT:'`',"boulder",(07,03) +OBJECT:'`',"boulder",(08,03) +OBJECT:'`',"boulder",(02,04) +OBJECT:'`',"boulder",(03,04) +OBJECT:'`',"boulder",(05,05) +OBJECT:'`',"boulder",(06,06) +OBJECT:'`',"boulder",(09,06) +OBJECT:'`',"boulder",(03,07) +OBJECT:'`',"boulder",(04,07) +OBJECT:'`',"boulder",(07,07) +OBJECT:'`',"boulder",(06,09) +OBJECT:'`',"boulder",(05,10) +OBJECT:'`',"boulder",(05,11) + +# Traps +TRAP:"hole",(07,11) +TRAP:"hole",(08,11) +TRAP:"hole",(09,11) +TRAP:"hole",(10,11) +TRAP:"hole",(11,11) +TRAP:"hole",(12,11) +TRAP:"hole",(13,11) +TRAP:"hole",(14,11) +TRAP:"hole",(15,11) +TRAP:"hole",(16,11) +TRAP:"hole",(17,11) + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + + +### Top (last) level of Sokoban ### +MAZE:"soko1-1",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP +-------------------------- +|........................| +|.......|---------------.| +-------.------ |.| + |...........| |.| + |...........| |.| +--------.----- |.| +|............| |.| +|............| |.| +-----.-------- ------|.| + |..........| --|.....|.| + |..........| |.+.....|.| + |.........|- |-|.....|.| +-------.---- |.+.....+.| +|........| |-|.....|-- +|........| |.+.....| +|...|----- --|.....| +----- ------- +ENDMAP +RANDOM_PLACES:(16,11),(16,13),(16,15) +STAIR:(01,01),down +REGION:(00,00,25,17),lit,"ordinary" +NON_DIGGABLE:(00,00,25,17) +NON_PASSWALL:(00,00,25,17) + +# Boulders +OBJECT:'`',"boulder",(03,05) +OBJECT:'`',"boulder",(05,05) +OBJECT:'`',"boulder",(07,05) +OBJECT:'`',"boulder",(09,05) +OBJECT:'`',"boulder",(11,05) +# +OBJECT:'`',"boulder",(04,07) +OBJECT:'`',"boulder",(04,08) +OBJECT:'`',"boulder",(06,07) +OBJECT:'`',"boulder",(09,07) +OBJECT:'`',"boulder",(11,07) +# +OBJECT:'`',"boulder",(03,12) +OBJECT:'`',"boulder",(04,10) +OBJECT:'`',"boulder",(05,12) +OBJECT:'`',"boulder",(06,10) +OBJECT:'`',"boulder",(07,11) +OBJECT:'`',"boulder",(08,10) +OBJECT:'`',"boulder",(09,12) +# +OBJECT:'`',"boulder",(03,14) + +# Traps +TRAP:"hole",(08,01) +TRAP:"hole",(09,01) +TRAP:"hole",(10,01) +TRAP:"hole",(11,01) +TRAP:"hole",(12,01) +TRAP:"hole",(13,01) +TRAP:"hole",(14,01) +TRAP:"hole",(15,01) +TRAP:"hole",(16,01) +TRAP:"hole",(17,01) +TRAP:"hole",(18,01) +TRAP:"hole",(19,01) +TRAP:"hole",(20,01) +TRAP:"hole",(21,01) +TRAP:"hole",(22,01) +TRAP:"hole",(23,01) + +MONSTER:'m',"giant mimic", random, m_object "boulder" +MONSTER:'m',"giant mimic", random, m_object "boulder" + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + +# Rewards +DOOR:locked,(23,13) +DOOR:closed,(17,11) +DOOR:closed,(17,13) +DOOR:closed,(17,15) +REGION:(18,10,22,16),lit,"zoo",filled,true +OBJECT:'(',"bag of holding",place[0] +ENGRAVING:place[0],burn,"Elbereth" + + +MAZE:"soko1-2",' ' +FLAGS:noteleport +GEOMETRY:center,center +#12345678901234567890123456789012345678901234567890 +MAP + ------------------------ + |......................| + |..-------------------.| +----.| ----- |.| +|..|.-- --...| |.| +|.....|--|....| |.| +|.....|..|....| |.| +--....|......-- |.| + |.......|...| ------|.| + |....|..|...| --|.....|.| + |....|--|...| |.+.....|.| + |.......|..-- |-|.....|.| + ----....|.-- |.+.....+.| + ---.--.| |-|.....|-- + |.....| |.+.....| + |..|..| --|.....| + ------- ------- +ENDMAP +RANDOM_PLACES:(16,10),(16,12),(16,14) +STAIR:(06,15),down +REGION:(00,00,25,16),lit,"ordinary" +NON_DIGGABLE:(00,00,25,16) +NON_PASSWALL:(00,00,25,16) + +# Boulders +OBJECT:'`',"boulder",(04,04) +OBJECT:'`',"boulder",(02,06) +OBJECT:'`',"boulder",(03,06) +OBJECT:'`',"boulder",(04,07) +OBJECT:'`',"boulder",(05,07) +OBJECT:'`',"boulder",(02,08) +OBJECT:'`',"boulder",(05,08) +OBJECT:'`',"boulder",(03,09) +OBJECT:'`',"boulder",(04,09) +OBJECT:'`',"boulder",(03,10) +OBJECT:'`',"boulder",(05,10) +OBJECT:'`',"boulder",(06,12) +# +OBJECT:'`',"boulder",(07,14) +# +OBJECT:'`',"boulder",(11,05) +OBJECT:'`',"boulder",(12,06) +OBJECT:'`',"boulder",(10,07) +OBJECT:'`',"boulder",(11,07) +OBJECT:'`',"boulder",(10,08) +OBJECT:'`',"boulder",(12,09) +OBJECT:'`',"boulder",(11,10) + +# Traps +TRAP:"hole",(05,01) +TRAP:"hole",(06,01) +TRAP:"hole",(07,01) +TRAP:"hole",(08,01) +TRAP:"hole",(09,01) +TRAP:"hole",(10,01) +TRAP:"hole",(11,01) +TRAP:"hole",(12,01) +TRAP:"hole",(13,01) +TRAP:"hole",(14,01) +TRAP:"hole",(15,01) +TRAP:"hole",(16,01) +TRAP:"hole",(17,01) +TRAP:"hole",(18,01) +TRAP:"hole",(19,01) +TRAP:"hole",(20,01) +TRAP:"hole",(21,01) +TRAP:"hole",(22,01) + +MONSTER:'m',"giant mimic", random, m_object "boulder" +MONSTER:'m',"giant mimic", random, m_object "boulder" + +# Random objects +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'%',random,random +OBJECT:'=',random,random +OBJECT:'/',random,random + +# Rewards +DOOR:locked,(23,12) +DOOR:closed,(17,10) +DOOR:closed,(17,12) +DOOR:closed,(17,14) +REGION:(18,09,22,15),lit,"zoo",filled,true +OBJECT:'"',"amulet of reflection",place[0] +ENGRAVING:place[0],burn,"Elbereth" diff --git a/dat/tower.des b/dat/tower.des new file mode 100644 index 0000000..f5cc292 --- /dev/null +++ b/dat/tower.des @@ -0,0 +1,136 @@ +# SCCS Id: @(#)tower.des 3.4 1990/02/26 +# Copyright (c) 1989 by Jean-Christophe Collet +# NetHack may be freely redistributed. See license for details. +# +# Upper stage of Vlad's tower +MAZE:"tower1",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:half-left,center +MAP + --- --- --- + |.| |.| |.| +---S---S---S--- +|.......+.+...| +---+-----.----- + |...\.|.+.| +---+-----.----- +|.......+.+...| +---S---S---S--- + |.| |.| |.| + --- --- --- +ENDMAP +LADDER:(11,05),down +# The lord and his court +MONSTER:'V',"Vlad the Impaler",(06,05) +MONSTER:'V',random,(03,09) +MONSTER:'V',random,(07,09) +MONSTER:'V',random,(11,09) +MONSTER:'V',random,(03,01) +MONSTER:'V',random,(07,01) +MONSTER:'V',random,(11,01) +# The doors +DOOR:closed,(08,03) +DOOR:closed,(10,03) +DOOR:closed,(03,04) +DOOR:locked,(10,05) +DOOR:locked,(08,07) +DOOR:locked,(10,07) +DOOR:closed,(03,06) +# treasures +OBJECT:'(',"chest",(07,05) +OBJECT:'(',"chest",(03,09) +OBJECT:'(',"chest",(07,09) +OBJECT:'(',"chest",(11,09) +OBJECT:'(',"chest",(03,01) +OBJECT:'(',"chest",(07,01) +OBJECT:'(',"chest",(11,01) +# We have to protect the tower against outside attacks +NON_DIGGABLE:(00,00,14,10) + + +# Intermediate stage of Vlad's tower +MAZE:"tower2",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:half-left,center +MAP + --- --- --- + |.| |.| |.| +---S---S---S--- +|.S.........S.| +---.------+---- + |......|..| +--------.------ +|.S......+..S.| +---S---S---S--- + |.| |.| |.| + --- --- --- +ENDMAP +# Random places are the 10 niches +RANDOM_PLACES:(03,01),(07,01),(11,01),(01,03),(13,03), + (01,07),(13,07),(03,09),(07,09),(11,09) +LADDER:(11,05),up +LADDER:(03,07),down +DOOR:locked,(10,04) +DOOR:locked,(09,07) +MONSTER:'&',random,place[0] +MONSTER:'&',random,place[1] +MONSTER:'d',"hell hound pup",place[2] +MONSTER:'d',"hell hound pup",place[3] +MONSTER:'d',"winter wolf",place[4] +CONTAINER:'(',"chest",place[5] +OBJECT:'"',"amulet of life saving",contained +CONTAINER:'(',"chest",place[6] +OBJECT:'"',"amulet of strangulation",contained +OBJECT:'[',"water walking boots",place[7] +OBJECT:'[',"crystal plate mail",place[8] +OBJECT:'+',"invisibility",place[9] +# Walls in the tower are non diggable +NON_DIGGABLE:(00,00,14,10) + + +# Bottom most stage of Vlad's tower +MAZE:"tower3",' ' +FLAGS: noteleport,hardfloor +GEOMETRY:half-left,center +MAP + --- --- --- + |.| |.| |.| + ---S---S---S--- + |.S.........S.| +-----.........----- +|...|.........+...| +|.---.........---.| +|.|.S.........S.|.| +|.---S---S---S---.| +|...|.|.|.|.|.|...| +---.---.---.---.--- + |.............| + --------------- +ENDMAP +# Random places are the 10 niches +RANDOM_PLACES:(05,01),(09,01),(13,01),(03,03),(15,03), + (03,07),(15,07),(05,09),(09,09),(13,09) +BRANCH:(02,05,02,05),(00,00,00,00) +LADDER:(05,07),up +# Entry door is, of course, locked +DOOR:locked,(14,05) +# Let's put a dragon behind the door, just for the fun... +MONSTER:'D',random,(13,05) +MONSTER:random,random,(12,04) +MONSTER:random,random,(12,06) +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +MONSTER:random,random,random +OBJECT:')',"long sword",place[0] +TRAP:random,place[0] +OBJECT:'(',"lock pick",place[1] +TRAP:random,place[1] +OBJECT:'[',"elven cloak",place[2] +TRAP:random,place[2] +OBJECT:'(',"blindfold",place[3] +TRAP:random,place[3] +# Walls in the tower are non diggable +NON_DIGGABLE:(00,00,18,12) diff --git a/dat/wizhelp b/dat/wizhelp new file mode 100644 index 0000000..6b38bc8 --- /dev/null +++ b/dat/wizhelp @@ -0,0 +1,23 @@ +Debug-Mode Quick Reference: + +^E == detect secret doors and traps. +^F == do magic mapping. +^G == create monster. +^I == identify items in pack. +^O == tell locations of special levels. +^T == do intra-level teleport. +^V == do trans-level teleport. +^W == make wish. +^X == show attributes including intrinsic attributes. + +#levelchange == change experience level +#lightsources == show mobile light sources +#monpolycontrol == control monster polymorphs +#panic == panic test +#polyself == polymorph self +#seenv == show seen vectors +#stats == show memory statistics +#timeout == look at timeout queue +#vision == show vision array +#wmode == show wall modes + diff --git a/dat/yendor.des b/dat/yendor.des new file mode 100644 index 0000000..8583457 --- /dev/null +++ b/dat/yendor.des @@ -0,0 +1,276 @@ +# SCCS Id: @(#)yendor.des 3.4 1996/10/20 +# Copyright (c) 1989 by Jean-Christophe Collet +# Copyright (c) 1992 by M. Stephenson and Izchak Miller +# NetHack may be freely redistributed. See license for details. +# +# The top (real) wizard level. +# Keeping the Moat for old-time's sake +MAZE:"wizard1",random +FLAGS:noteleport,hardfloor +GEOMETRY:center,center +MAP +----------------------------. +|.......|..|.........|.....|. +|.......S..|.}}}}}}}.|.....|. +|..--S--|..|.}}---}}.|---S-|. +|..|....|..|.}--.--}.|..|..|. +|..|....|..|.}|...|}.|..|..|. +|..--------|.}--.--}.|..|..|. +|..|.......|.}}---}}.|..|..|. +|..S.......|.}}}}}}}.|..|..|. +|..|.......|.........|..|..|. +|..|.......|-----------S-S-|. +|..|.......S...............|. +----------------------------. +ENDMAP +STAIR:levregion(01,00,79,20),(0,0,28,12),up +STAIR:levregion(01,00,79,20),(0,0,28,12),down +BRANCH:levregion(01,00,79,20),(0,0,28,12) +TELEPORT_REGION:levregion(01,00,79,20),(0,0,27,12) +# Make it a morgue for rm id in mkmaze.c +# for the purpose of random sdoor placement +REGION:(12,01,20,09),unlit,"morgue",unfilled +MAZEWALK:(28,05),east +LADDER:(06,05),down +# Non diggable walls +# Walls inside the moat stay diggable +NON_DIGGABLE:(00,00,11,12) +NON_DIGGABLE:(11,00,21,00) +NON_DIGGABLE:(11,10,27,12) +NON_DIGGABLE:(21,00,27,10) +# Non passable walls +NON_PASSWALL:(00,00,11,12) +NON_PASSWALL:(11,00,21,00) +NON_PASSWALL:(11,10,27,12) +NON_PASSWALL:(21,00,27,10) +# The wizard and his guards +MONSTER:'@',"Wizard of Yendor",(16,05),asleep +MONSTER:'d',"hell hound",(15,05) +MONSTER:'V',"vampire lord",(17,05) +# The local treasure +OBJECT:'+',"Book of the Dead",(16,05) +# Surrounding terror +MONSTER:';',"kraken",(14,02) +MONSTER:';',"giant eel",(17,02) +MONSTER:';',"kraken",(13,04) +MONSTER:';',"giant eel",(13,06) +MONSTER:';',"kraken",(19,04) +MONSTER:';',"giant eel",(19,06) +MONSTER:';',"kraken",(15,08) +MONSTER:';',"giant eel",(17,08) +MONSTER:';',"piranha",(15,02) +MONSTER:';',"piranha",(19,08) +# Random monsters +MONSTER:'D',random,random +MONSTER:'H',random,random +MONSTER:'&',random,random +MONSTER:'&',random,random +MONSTER:'&',random,random +MONSTER:'&',random,random +# And to make things a little harder. +TRAP:"board",(16,04) +TRAP:"board",(16,06) +TRAP:"board",(15,05) +TRAP:"board",(17,05) +# Random traps. +TRAP:"spiked pit",random +TRAP:"sleep gas",random +TRAP:"anti magic",random +TRAP:"magic",random +# Some random loot. +OBJECT:'*',"ruby",random +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random +OBJECT:'+',random,random + + +# The middle wizard level. +MAZE:"wizard2",random +FLAGS:noteleport,hardfloor +GEOMETRY:center,center +MAP +----------------------------. +|.....|.S....|.............|. +|.....|.-------S--------S--|. +|.....|.|.........|........|. +|..-S--S|.........|........|. +|..|....|.........|------S-|. +|..|....|.........|.....|..|. +|-S-----|.........|.....|..|. +|.......|.........|S--S--..|. +|.......|.........|.|......|. +|-----S----S-------.|......|. +|............|....S.|......|. +----------------------------. +ENDMAP +STAIR:levregion(01,00,79,20),(0,0,28,12),up +STAIR:levregion(01,00,79,20),(0,0,28,12),down +BRANCH:levregion(01,00,79,20),(0,0,28,12) +TELEPORT_REGION:levregion(01,00,79,20),(0,0,27,12) +REGION:(09,03,17,09),unlit,"zoo" +DOOR:closed,(15,02) +DOOR:closed,(11,10) +MAZEWALK:(28,05),east +LADDER:(12,01),up +LADDER:(14,11),down +# Non diggable walls everywhere +NON_DIGGABLE:(00,00,27,12) +# +NON_PASSWALL:(00,00,06,12) +NON_PASSWALL:(06,00,27,02) +NON_PASSWALL:(16,02,27,12) +NON_PASSWALL:(06,12,16,12) +# Random traps. +TRAP:"spiked pit",random +TRAP:"sleep gas",random +TRAP:"anti magic",random +TRAP:"magic",random +# Some random loot. +OBJECT:'!',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'+',random,random +# treasures +OBJECT:'"',random,(04,06) + + +# The bottom wizard level. +# Memorialize the fakewiz setup. +MAZE:"wizard3",random +FLAGS:noteleport,hardfloor +GEOMETRY:center,center +MAP +----------------------------. +|..|............S..........|. +|..|..------------------S--|. +|..|..|.........|..........|. +|..S..|.}}}}}}}.|..........|. +|..|..|.}}---}}.|-S--------|. +|..|..|.}--.--}.|..|.......|. +|..|..|.}|...|}.|..|.......|. +|..---|.}--.--}.|..|.......|. +|.....|.}}---}}.|..|.......|. +|.....S.}}}}}}}.|..|.......|. +|.....|.........|..S.......|. +----------------------------. +ENDMAP +STAIR:levregion(01,00,79,20),(0,0,28,12),up +STAIR:levregion(01,00,79,20),(0,0,28,12),down +BRANCH:levregion(01,00,79,20),(0,0,28,12) +TELEPORT_REGION:levregion(01,00,79,20),(0,0,27,12) +PORTAL:(25,11,25,11),(0,0,0,0),"fakewiz1" +MAZEWALK:(28,09),east +REGION:(07,03,15,11),unlit,"morgue",unfilled +REGION:(17,06,18,11),unlit,"beehive" +# make the entry chamber a real room; it affects monster arrival; +# `unfilled' is a kludge to force an ordinary room to remain a room +REGION:(20,06,26,11),unlit,"ordinary",unfilled +DOOR:closed,(18,05) +DOOR:closed,(19,11) +LADDER:(11,07),up +# Non diggable walls +# Walls inside the moat stay diggable +NON_DIGGABLE:(00,00,06,12) +NON_DIGGABLE:(06,00,27,02) +NON_DIGGABLE:(16,02,27,12) +NON_DIGGABLE:(06,12,16,12) +# +NON_PASSWALL:(00,00,06,12) +NON_PASSWALL:(06,00,27,02) +NON_PASSWALL:(16,02,27,12) +NON_PASSWALL:(06,12,16,12) +# +MONSTER:'L',random,(10,07) +MONSTER:'V',"vampire lord",(12,07) +# Some surrounding horrors +MONSTER:';',"kraken",(08,05) +MONSTER:';',"giant eel",(08,08) +MONSTER:';',"kraken",(14,05) +MONSTER:';',"giant eel",(14,08) +# Other monsters +MONSTER:'L',random,random +MONSTER:'D',random,random +MONSTER:'D',random,(26,09) +MONSTER:'&',random,random +MONSTER:'&',random,random +MONSTER:'&',random,random +# And to make things a little harder. +TRAP:"board",(10,07) +TRAP:"board",(12,07) +TRAP:"board",(11,06) +TRAP:"board",(11,08) +# Some loot +OBJECT:')',random,random +OBJECT:'!',random,random +OBJECT:'?',random,random +OBJECT:'?',random,random +OBJECT:'(',random,random +# treasures +OBJECT:'"',random,(11,07) + + +# The former decoy wizard levels. +# There are two of these, and we need to +# distinguish between them for the portal. +MAZE:"fakewiz1",random +GEOMETRY:center,center +MAP +......... +.}}}}}}}. +.}}---}}. +.}--.--}. +.}|...|}. +.}--.--}. +.}}---}}. +.}}}}}}}. +ENDMAP +STAIR:levregion(01,00,79,20),(0,0,8,7),up +STAIR:levregion(01,00,79,20),(0,0,8,7),down +BRANCH:levregion(01,00,79,20),(0,0,8,7) +TELEPORT_REGION:levregion(01,00,79,20),(2,2,6,6) +PORTAL:(4,4,4,4),(0,0,0,0),"wizard3" +MAZEWALK:(08,05),east +REGION:(04,03,06,06),unlit,"ordinary",unfilled,true +MONSTER:'L',random,(04,04) +MONSTER:'V',"vampire lord",(03,04) +MONSTER:';',"kraken",(06,06) +# And to make things a little harder. +TRAP:"board",(04,03) +TRAP:"board",(04,05) +TRAP:"board",(03,04) +TRAP:"board",(05,04) + + +MAZE:"fakewiz2",random +GEOMETRY:center,center +MAP +......... +.}}}}}}}. +.}}---}}. +.}--.--}. +.}|...|}. +.}--.--}. +.}}---}}. +.}}}}}}}. +ENDMAP +STAIR:levregion(01,00,79,20),(0,0,8,7),up +STAIR:levregion(01,00,79,20),(0,0,8,7),down +BRANCH:levregion(01,00,79,20),(0,0,8,7) +TELEPORT_REGION:levregion(01,00,79,20),(2,2,6,6) +MAZEWALK:(08,05),east +REGION:(04,03,06,06),unlit,"ordinary",unfilled,true +MONSTER:'L',random,(04,04) +MONSTER:'V',"vampire lord",(03,04) +MONSTER:';',"kraken",(06,06) +# And to make things a little harder. +TRAP:"board",(04,03) +TRAP:"board",(04,05) +TRAP:"board",(03,04) +TRAP:"board",(05,04) +# treasures +OBJECT:'"',random,(04,04) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn new file mode 100644 index 0000000..f67f02f --- /dev/null +++ b/doc/Guidebook.mn @@ -0,0 +1,2746 @@ +.\" $Revision: 1.61.2.20 $ $Date: 2003/12/03 03:00:47 $ +.ds h0 "NetHack Guidebook +.ds h1 +.ds h2 % +.ds vr "NetHack 3.4 +.ds f0 "\*(vr +.ds f1 +.ds f2 "December 2, 2003 +.mt +A Guide to the Mazes of Menace +(Guidebook for NetHack) +.au +Eric S. Raymond +(Extensively edited and expanded for 3.4) +.hn 1 +Introduction + +Recently, you have begun to find yourself unfulfilled and distant +in your daily occupation. Strange dreams of prospecting, stealing, +crusading, and combat have haunted you in your sleep for many months, +but you aren't sure of the reason. You wonder whether you have in +fact been having those dreams all your life, and somehow managed to +forget about them until now. Some nights you awaken suddenly +and cry out, terrified at the vivid recollection of the strange and +powerful creatures that seem to be lurking behind every corner of the +dungeon in your dream. Could these details haunting your dreams be real? +As each night passes, you feel the desire to enter the mysterious caverns +near the ruins grow stronger. Each morning, however, you quickly put +the idea out of your head as you recall the tales of those who entered +the caverns before you and did not return. Eventually you can resist +the yearning to seek out the fantastic place in your dreams no longer. +After all, when other adventurers came back this way after spending time +in the caverns, they usually seemed better off than when they passed +through the first time. And who was to say that all of those who did +not return had not just kept going? + +.pg +Asking around, you hear about a bauble, called the Amulet of Yendor by some, +which, if you can find it, will bring you great wealth. One legend you were +told even mentioned that the one who finds the amulet will be granted +immortality by the gods. The amulet is rumored to be somewhere beyond the +Valley of Gehennom, deep within the Mazes of Menace. Upon hearing the +legends, you immediately realize that there is some profound and +undiscovered reason that you are to descend into the caverns and seek +out that amulet of which they spoke. Even if the rumors of the amulet's +powers are untrue, you decide that you should at least be able to sell the +tales of your adventures to the local minstrels for a tidy sum, especially +if you encounter any of the terrifying and magical creatures of +your dreams along the way. You spend one last night fortifying yourself +at the local inn, becoming more and more depressed as you watch the odds +of your success being posted on the inn's walls getting lower and lower. + +.pg + In the morning you awake, collect your belongings, and +set off for the dungeon. After several days of uneventful +travel, you see the ancient ruins that mark the entrance to the +Mazes of Menace. It is late at night, so you make camp at the entrance +and spend the night sleeping under the open skies. In the morning, you +gather your gear, eat what may be your last meal outside, and enter the +dungeon... + +.hn 1 +What is going on here? +.pg +You have just begun a game of NetHack. Your goal is to grab as much +treasure as you can, retrieve the Amulet of Yendor, and escape the +Mazes of Menace alive. +.pg +Your abilities and strengths for dealing with the hazards of adventure +will vary with your background and training: +.pg +\fIArcheologists\fP understand dungeons pretty well; this enables them +to move quickly and sneak up on the local nasties. They start equipped +with the tools for a proper scientific expedition. +.pg +\fIBarbarians\fP are warriors out of the hinterland, hardened to battle. +They begin their quests with naught but uncommon strength, a trusty hauberk, +and a great two-handed sword. +.pg +\fICavemen\fP and \fICavewomen\fP start with exceptional strength but, +unfortunately, with neolithic weapons. +.pg +\fIHealers\fP are wise in medicine and apothecary. They know the +herbs and simples that can restore vitality, ease pain, anesthetize, +and neutralize poisons; and with their instruments, they can divine a +being's state of health or sickness. Their medical practice earns them +quite reasonable amounts of money, with which they enter the dungeon. +.pg +\fIKnights\fP are distinguished from the common skirmisher by their +devotion to the ideals of chivalry and by the surpassing excellence of +their armor. +.pg +\fIMonks\fP are ascetics, who by rigorous practice of physical and mental +disciplines have become capable of fighting as effectively without weapons +as with. They wear no armor but make up for it with increased mobility. +.pg +\fIPriests\fP and \fIPriestesses\fP are clerics militant, crusaders +advancing the cause of righteousness with arms, armor, and arts +thaumaturgic. Their ability to commune with deities via prayer +occasionally extricates them from peril, but can also put them in it. +.pg +\fIRangers\fP are most at home in the woods, and some say slightly out +of place in a dungeon. They are, however, experts in archery as well +as tracking and stealthy movement. +.pg +\fIRogues\fP are agile and stealthy thieves, with knowledge of locks, +traps, and poisons. Their advantage lies in surprise, which they employ +to great advantage. +.pg +\fISamurai\fP are the elite warriors of feudal Nippon. They are lightly +armored and quick, and wear the \fIdai-sho\fP, two swords of the deadliest +keenness. +.pg +\fITourists\fP start out with lots of gold (suitable for shopping with), +a credit card, lots of food, some maps, and an expensive camera. Most +monsters don't like being photographed. +.pg +\fIValkyries\fP are hardy warrior women. Their upbringing in the harsh +Northlands makes them strong, inures them to extremes of cold, and instills +in them stealth and cunning. +.pg +\fIWizards\fP start out with a knowledge of magic, a selection of magical +items, and a particular affinity for dweomercraft. Although seemingly weak +and easy to overcome at first sight, an experienced Wizard is a deadly foe. +.pg +You may also choose the race of your character: +.pg +\fIDwarves\fP are smaller than humans or elves, but are stocky and solid +individuals. Dwarves' most notable trait is their great expertise in mining +and metalwork. Dwarvish armor is said to be second in quality not even to the +mithril armor of the Elves. +.pg +\fIElves\fP are agile, quick, and perceptive; very little of what goes +on will escape an Elf. The quality of Elven craftsmanship often gives +them an advantage in arms and armor. +.pg +\fIGnomes\fP are smaller than but generally similar to dwarves. Gnomes are +known to be expert miners, and it is known that a secret underground mine +complex built by this race exists within the Mazes of Menace, filled with +both riches and danger. +.pg +\fIHumans\fP are by far the most common race of the surface world, and +are thus the norm by which other races are often compared. Although +they have no special abilities, they can succeed in any role. +.pg +\fIOrcs\fP are a cruel and barbaric race that hate every living thing +(including other orcs). Above all others, Orcs hate Elves with a passion +unequalled, and will go out of their way to kill one at any opportunity. +The armor and weapons fashioned by the Orcs are typically of inferior quality. +.hn 1 +What do all those things on the screen mean? +.pg +On the screen is kept a map of where you have +been and what you have seen on the current dungeon level; as you +explore more of the level, it appears on the screen in front of you. +.pg +When NetHack's ancestor \fIrogue\fP first appeared, its screen +orientation was almost unique among computer fantasy games. Since +then, screen orientation has become the norm rather than the +exception; NetHack continues this fine tradition. Unlike text +adventure games that accept commands in pseudo-English sentences and +explain the results in words, NetHack commands are all one or two +keystrokes and the results are displayed graphically on the screen. A +minimum screen size of 24 lines by 80 columns is recommended; if the +screen is larger, only a 21x80 section will be used for the map. +.pg +NetHack can even be played by blind players, with the assistance of Braille +readers or speech synthesisers. Instructions for configuring NetHack for +the blind are included later in this document. +.pg +NetHack generates a new dungeon every time you play it; even the +authors still find it an entertaining and exciting game despite +having won several times. +.pg +NetHack offers a variety of display options. The options available to you +will vary from port to port, depending on the capabilities of your +hardware and software, and whether various compile-time options were +enabled when your executable was created. The three possible display +options are: a monochrome character interface, a color character interface, +and a graphical interface using small pictures called tiles. The two +character interfaces allow fonts with other characters to be substituted, +but the default assignments use standard ASCII characters to represent +everything. There is no difference between the various display options +with respect to game play. Because we cannot reproduce the tiles or +colors in the Guidebook, and because it is common to all ports, we will +use the default ASCII characters from the monochrome character display +when referring to things you might see on the screen during your game. +.pg +In order to understand what is going on in NetHack, first you must +understand what NetHack is doing with the screen. The NetHack screen +replaces the ``You see ...'' descriptions of text adventure games. +Figure 1 is a sample of what a NetHack screen might look like. +The way the screen looks for you depends on your platform. + +.TS S +center tab(~); +a. +_ +The bat bites! + + ------ + |....| ---------- + |.<..|####...@...$.| + |....-# |...B....+ + |....| |.d......| + ------ -------|-- + + + +Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral +Dlvl:1 $:0 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:257 Weak + +_ +.TE +.ce 1 +Figure 1 + +.hn 2 +The status lines (bottom) +.pg +The bottom two lines of the screen contain several cryptic pieces of +information describing your current status. If either status line +becomes longer than the width of the screen, you might not see all of +it. Here are explanations of what the various status items mean +(though your configuration may not have all the status items listed +below): +.lp "Rank " +Your character's name and professional ranking (based on the +experience level, see below). +.lp Strength +A measure of your character's strength; one of your six basic +attributes. A human character's attributes can range from 3 to 18 inclusive; +non-humans may exceed these limits +(occasionally you may get super-strengths of the form 18/xx, and magic can +also cause attributes to exceed the normal limits). The +higher your strength, the stronger you are. Strength affects how +successfully you perform physical tasks, how much damage you do in +combat, and how much loot you can carry. +.lp Dexterity +Dexterity affects your chances to hit in combat, to avoid traps, and +do other tasks requiring agility or manipulation of objects. +.lp Constitution +Constitution affects your ability to recover from injuries and other +strains on your stamina. +.lp Intelligence +Intelligence affects your ability to cast spells and read spellbooks. +.lp Wisdom +Wisdom comes from your practical experience (especially when dealing with +magic). It affects your magical energy. +.lp Charisma +Charisma affects how certain creatures react toward you. In +particular, it can affect the prices shopkeepers offer you. +.lp Alignment +\fBLawful\fP, \fBNeutral\fP, or \fBChaotic\fP. Often, Lawful is +taken as good and Chaotic as evil, but legal and ethical do not always +coincide. Your alignment influences how other +monsters react toward you. Monsters of a like alignment are more likely +to be non-aggressive, while those of an opposing alignment are more likely +to be seriously offended at your presence. +.lp "Dungeon Level +How deep you are in the dungeon. You start at level one and the number +increases as you go deeper into the dungeon. Some levels are special, +and are identified by a name and not a number. The Amulet of Yendor is +reputed to be somewhere beneath the twentieth level. +.lp "Gold " +The number of gold pieces you are openly carrying. Gold which you have +concealed in containers is not counted. +.lp "Hit Points +Your current and maximum hit points. Hit points indicate how much +damage you can take before you die. The more you get hit in a fight, +the lower they get. You can regain hit points by resting, or by using +certain magical items or spells. The number in parentheses is the maximum +number your hit points can reach. +.lp Power +Spell points. This tells you how much mystic energy (\fImana\fP) +you have available for spell casting. Again, resting will regenerate the +amount available. +.lp "Armor Class +A measure of how effectively your armor stops blows from unfriendly +creatures. The lower this number is, the more effective the armor; it +is quite possible to have negative armor class. +.lp Experience +Your current experience level and experience points. As you +adventure, you gain experience points. At certain experience point +totals, you gain an experience level. The more experienced you are, +the better you fight and withstand magical attacks. Many dungeons +show only your experience level here. +.lp "Time " +The number of turns elapsed so far, displayed if you have the +.op time +option set. +.lp "Hunger status +Your current hunger status, ranging from \fBSatiated\fP down to +\fBFainting\fP. If your hunger status is normal, it is not displayed. +.pg +Additional status flags may appear after the hunger status: \fBConf\fP +when you're confused, \fBFoodPois\fP or \fBIll\fP +when sick, \fBBlind\fP when you can't +see, \fBStun\fP when stunned, and \fBHallu\fP when hallucinating. +.hn 2 +The message line (top) +.pg +The top line of the screen is reserved for messages that describe +things that are impossible to represent visually. If you see a +``\fB--More--\fP'' on the top line, this means that NetHack has +another message to display on the screen, but it wants to make certain +that you've read the one that is there first. To read the next message, +just press the space bar. +.hn 2 +The map (rest of the screen) +.pg +The rest of the screen is the map of the level as you have explored it +so far. Each symbol on the screen represents something. You can set +various graphics options to change some of the symbols the game uses; +otherwise, the game will use default symbols. Here is a list of what the +default symbols mean: +.lp "- and | +The walls of a room, or an open door. Or a grave (|). +.lp . +The floor of a room, ice, or a doorless doorway. +.lp # +A corridor, or iron bars, or a tree, or possibly a kitchen sink (if +your dungeon has sinks), or a drawbridge. +.lp > +Stairs down: a way to the next level. +.lp < +Stairs up: a way to the previous level. +.lp + +A closed door, or a spellbook containing a spell you may be able to learn. +.lp @ +Your character or a human. +.lp $ +A pile of gold. +.lp ^ +A trap (once you have detected it). +.lp ) +A weapon. +.lp [ +A suit or piece of armor. +.lp % +Something edible (not necessarily healthy). +.lp ? +A scroll. +.lp / +A wand. +.lp = +A ring. +.lp ! +A potion. +.lp ( +A useful item (pick-axe, key, lamp...). +.lp """ +An amulet or a spider web. +.lp * +A gem or rock (possibly valuable, possibly worthless). +.lp ` +A boulder or statue. +.lp 0 +An iron ball. +.lp _ +An altar, or an iron chain. +.lp { +A fountain. +.lp } +A pool of water or moat or a pool of lava. +.lp "\e +An opulent throne. +.lp "a-zA-Z and other symbols +Letters and certain other symbols represent the various inhabitants +of the Mazes of Menace. Watch out, they can be nasty and vicious. +Sometimes, however, they can be helpful. +.lp I +This marks the last known location of an invisible or otherwise unseen +monster. Note that the monster could have moved. The 'F' and 'm' commands +may be useful here. +.pg +You need not memorize all these symbols; you can ask the game what any +symbol represents with the `/' command (see the next section for +more info). + +.hn 1 +Commands +.pg +Commands are initiated by typing one or two characters. Some commands, +like ``search'', do not require that any more information be collected by +NetHack. Other commands might require additional information, for +example a direction, or an object to be used. For those commands that +require additional information, NetHack will present you with either a +menu of choices or with a command line prompt requesting information. Which +you are presented with will depend chiefly on how you have set the +.op menustyle +option. +.pg +For example, a common question, in the form ``What do you want to +use?\ [a-zA-Z\ ?*]'', asks you to choose an object you are carrying. +Here, ``a-zA-Z'' are the inventory letters of your possible choices. +Typing `?' gives you an inventory list of these items, so you can see +what each letter refers to. In this example, there is also a `*' +indicating that you may choose an object not on the list, if you +wanted to use something unexpected. Typing a `*' lists your entire +inventory, so you can see the inventory letters of every object you're +carrying. Finally, if you change your mind and decide you don't want +to do this command after all, you can press the ESC key to abort the +command. +.pg +You can put a number before some commands to repeat them that many +times; for example, ``10s'' will search ten times. If you have the +.op number_pad +option set, you must type `n' to prefix a count, so the example above +would be typed ``n10s'' instead. Commands for which counts make no +sense ignore them. In addition, movement commands can be prefixed for +greater control (see below). To cancel a count or a prefix, press the +ESC key. +.pg +The list of commands is rather long, but it can be read at any time +during the game through the `?' command, which accesses a menu of +helpful texts. Here are the commands for your reference: +.lp ? +Help menu: display one of several help texts available. +.lp / +Tell what a symbol represents. You may choose to specify a location +or type a symbol (or even a whole word) to explain. +Specifying a location is done by moving the cursor to a particular spot +on the map and then pressing one of `.', `,', `;', +or `:'. `.' will explain the symbol at the chosen location, +conditionally check for ``More info?'' depending upon whether the +.op help +option is on, and then you will be asked to pick another location; +`,' will explain the symbol but skip any additional +information; `;' will skip additional info and also not bother asking +you to choose another location to examine; `:' will show additional +info, if any, without asking for confirmation. When picking a location, +pressing the ESC key will terminate this command, or pressing `?' +will give a brief reminder about how it works. +.pg +Specifying a name rather than a location +always gives any additional information available about that name. +.lp & +Tell what a command does. +.lp < +Go up to the previous level (if you are on a staircase or ladder). +.lp > +Go down to the next level (if you are on a staircase or ladder). +.lp [yuhjklbn] +Go one step in the direction indicated (see Figure 2). If you sense +or remember +a monster there, you will fight the monster instead. Only these +one-step movement commands cause you to fight monsters; the others +(below) are ``safe.'' +.sd +.TS S +center; +c c. +y k u 7 8 9 +\e | / \e | / +h- . -l 4- . -6 +/ | \e / | \e +b j n 1 2 3 + (if \fBnumber_pad\fP is set) +.TE +.ed +.ce 1 +Figure 2 + +.lp [YUHJKLBN] +Go in that direction until you hit a wall or run into something. +.lp m[yuhjklbn] +Prefix: move without picking up objects or fighting (even if you remember +a monster there) +.lp F[yuhjklbn] +Prefix: fight a monster (even if you only guess one is there) +.lp M[yuhjklbn] +Prefix: move far, no pickup. +.lp "g[yuhjklbn] +Prefix: move until something interesting is found. +.lp "G[yuhjklbn] or [yuhjklbn] +Prefix: same as `g', but forking of corridors is not considered interesting. +.lp _ +Travel to a map location via a shortest-path algorithm. The shortest path +is computed over map locations the hero knows about (e.g. seen or +previously traversed). If there is no known path, a guess is made instead. +Stops on most of +the same conditions as the `G' command, but without picking up +objects, similar to the `M' command. For ports with mouse +support, the command is also invoked when a mouse-click takes place on a +location other than the current position. +.lp . +Rest, do nothing for one turn. +.lp a +Apply (use) a tool (pick-axe, key, lamp...). +.lp A +Remove one or more worn items, such as armor. +Use `T' (take off) to take off only one piece of armor +or `R' (remove) to take off only one accessory. +.lp ^A +Redo the previous command. +.lp c +Close a door. +.lp C +Call (name) an individual monster. +.lp ^C +Panic button. Quit the game. +.lp d +Drop something. Ex. ``d7a'' means drop seven items of object \fIa\fP. +.lp D +Drop several things. In answer to the question +``What kinds of things do you want to drop? [!%= BUCXaium]'' +you should type zero or more object symbols possibly followed by +`a' and/or `i' and/or `u' and/or `m'. In addition, one or more of +the blessed/uncursed/cursed groups may be typed. +.sd +.si +DB - drop all objects known to be blessed. +DU - drop all objects known to be uncursed. +DC - drop all objects known to be cursed. +DX - drop all objects of unknown B/U/C status. +Da - drop all objects, without asking for confirmation. +Di - examine your inventory before dropping anything. +Du - drop only unpaid objects (when in a shop). +Dm - use a menu to pick which object(s) to drop. +D%u - drop only unpaid food. +.ei +.ed +.lp ^D +Kick something (usually a door). +.lp e +Eat food. +.\" Make sure Elbereth is not hyphenated below, the exact spelling matters +.hw Elbereth +.lp E +Engrave a message on the floor. +Engraving the word ``Elbereth'' will cause most monsters to not attack +you hand-to-hand (but if you attack, you will rub it out); this is +often useful to give yourself a breather. (This feature may be compiled out +of the game, so your version might not have it.) +.sd +.si +E- - write in the dust with your fingers. +.ei +.ed +.lp f +Fire one of the objects placed in your quiver. You may select +ammunition with a previous `Q' command, or let the computer pick +something appropriate if +.op autoquiver +is true. +.lp i +List your inventory (everything you're carrying). +.lp I +List selected parts of your inventory. +.sd +.si +I* - list all gems in inventory; +Iu - list all unpaid items; +Ix - list all used up items that are on your shopping bill; +I$ - count your money. +.ei +.ed +.lp o +Open a door. +.lp O +Set options. A menu showing the current option values will be +displayed. You can change most values simply by selecting the menu +entry for the given option (ie, by typing its letter or clicking upon +it, depending on your user interface). For the non-boolean choices, +a further menu or prompt will appear once you've closed this menu. +The available options +are listed later in this Guidebook. Options are usually set before the +game rather than with the `O' command; see the section on options below. +.lp p +Pay your shopping bill. +.lp P +Put on a ring or other accessory (amulet, blindfold). +.lp ^P +Repeat previous message. Subsequent ^P's repeat earlier messages. +The behavior can be varied via the msg_window option. +.lp q +Quaff (drink) something (potion, water, etc). +.lp Q +Select an object for your quiver. You can then throw this using +the `f' command. (In versions prior to 3.3 this was the command to quit +the game, which has now been moved to `#quit'.) +.lp r +Read a scroll or spellbook. +.lp R +Remove an accessory (ring, amulet, etc). +.lp ^R +Redraw the screen. +.lp s +Search for secret doors and traps around you. It usually takes several +tries to find something. +.lp S +Save (and suspend) the game. The game will be restored automatically the +next time you play. +.lp t +Throw an object or shoot a projectile. +.lp T +Take off armor. +.lp ^T +Teleport, if you have the ability. +.lp v +Display version number. +.lp V +Display the game history. +.lp w +Wield weapon. +.sd +.si +w- - wield nothing, use your bare hands. +.ei +.ed +.lp W +Wear armor. +.lp x +Exchange your wielded weapon with the item in your alternate +weapon slot. The latter is used as your secondary weapon when engaging in +two-weapon combat. Note that if one of these slots is empty, +the exchange still takes place. +.lp X +Enter explore (discovery) mode, explained in its own section later. +.lp ^X +Display your name, role, race, gender, and alignment as well as +the various deities in your game. +.lp z +Zap a wand. To aim at yourself, use `.' for the direction. +.lp Z +Zap (cast) a spell. To cast at yourself, use `.' for the direction. +.lp ^Z +Suspend the game +.ux " versions with job control only)." ( +.lp : +Look at what is here. +.lp ; +Show what type of thing a visible symbol corresponds to. +.lp , +Pick up some things. May be preceded by `m' to force a selection menu. +.lp @ +Toggle the +.op autopickup +option on and off. +.lp ^ +Ask for the type of a trap you found earlier. +.lp ) +Tell what weapon you are wielding. +.lp [ +Tell what armor you are wearing. +.lp = +Tell what rings you are wearing. +.lp """ +Tell what amulet you are wearing. +.lp ( +Tell what tools you are using. +.lp * +Tell what equipment you are using; combines the preceding five type-specific +commands into one. +.lp $ +Count your gold pieces. +.lp + +List the spells you know. Using this command, you can also rearrange +the order in which your spells are listed. They are shown via a menu, +and if you select a spell in that menu, you'll be re-prompted for +another spell to swap places with it, and then have opportunity to +make further exchanges. +.lp "\e +Show what types of objects have been discovered. +.lp ! +Escape to a shell. +.lp # +Perform an extended command. As you can see, the authors of NetHack +used up all the letters, so this is a way to introduce the less frequently +used commands. +What extended commands are available depends on what features the game was +compiled with. +.lp #adjust +Adjust inventory letters (most useful when the +.op fixinv +option is ``on''). +.lp #chat +Talk to someone. +.lp #conduct +List which challenges you have adhered to. See the section below entitled +``Conduct'' for details. +.lp #dip +Dip an object into something. +.lp #enhance +Advance or check weapons and spell skills. +.lp #force +Force a lock. +.lp #invoke +Invoke an object's special powers. +.lp #jump +Jump to another location. +.lp #loot +Loot a box or bag on the floor beneath you, or the saddle +from a horse standing next to you. +.lp #monster +Use a monster's special ability (when polymorphed into monster form). +.lp #name +Name an item or type of object. +.lp #offer +Offer a sacrifice to the gods. +.lp #pray +Pray to the gods for help. +.lp #quit +Quit the program without saving your game. +.lp #ride +Ride (or stop riding) a monster. +.lp #rub +Rub a lamp or a stone. +.lp #sit +Sit down. +.lp #turn +Turn undead. +.lp #twoweapon +Toggle two-weapon combat on or off. Note that you must +use suitable weapons for this type of combat, or it will +be automatically turned off. +.lp #untrap +Untrap something (trap, door, or chest). +.lp #version +Print compile time options for this version of NetHack. +.lp #wipe +Wipe off your face. +.lp #? +Help menu: get the list of available extended commands. +.pg +If your keyboard has a meta key (which, when pressed in combination +with another key, modifies it by setting the `meta' [8th, or `high'] +bit), you can invoke many extended commands by meta-ing the first +letter of the command. +.\" In {\it NT, OS/2, PC\/ {\rm and} ST NetHack}, +.\" the `Alt' key can be used in this fashion; +.\" on the Amiga set the {\it altmeta\/} option to get this behavior. +In NT, OS/2, and PC NetHack, the `Alt' key +can be used in this fashion. +.lp M-? +#? (not supported by all platforms) +.lp M-2 +#twoweapon (unless the number_pad option is enabled) +.lp M-a +#adjust +.lp M-c +#chat +.lp M-d +#dip +.lp M-e +#enhance +.lp M-f +#force +.lp M-i +#invoke +.lp M-j +#jump +.lp M-l +#loot +.lp M-m +#monster +.lp M-n +#name +.lp M-o +#offer +.lp M-p +#pray +.lp M-q +#quit +.lp M-r +#rub +.lp M-s +#sit +.lp M-t +#turn +.lp M-u +#untrap +.lp M-v +#version +.lp M-w +#wipe +.pg +If the +.op number_pad +option is on, some additional letter commands are available: +.lp h +Help menu: display one of several help texts available, like ``?''. +.lp j +Jump to another location. Same as ``#jump'' or ``M-j''. +.lp k +Kick something (usually a door). Same as `^D'. +.lp l +Loot a box or bag on the floor beneath you, or the saddle +from a horse standing next to you. Same as ``#loot'' or ``M-l''. +.lp N +Name an item or type of object. Same as ``#name'' or ``M-n''. +.lp u +Untrap a trap, door, or chest. Same as ``#untrap'' or ``M-u''. + +.hn 1 +Rooms and corridors +.pg +Rooms and corridors in the dungeon are either lit or dark. +Any lit areas within your line of sight will be displayed; +dark areas are only displayed if they are within one space of you. +Walls and corridors remain on the map as you explore them. +.pg +Secret corridors are hidden. You can find them with the `s' (search) +command. +.hn 2 +Doorways +.pg +Doorways connect rooms and corridors. Some doorways have no doors; +you can walk right through. Others have doors in them, which may be +open, closed, or locked. To open a closed door, use the `o' (open) +command; to close it again, use the `c' (close) command. +.pg +You can get through a locked door by using a tool to pick the lock +with the `a' (apply) command, or by kicking it open with the `^D' +(kick) command. +.pg +Open doors cannot be entered diagonally; you must approach them +straight on, horizontally or vertically. Doorways without doors are +not restricted in this fashion. +.pg +Doors can be useful for shutting out monsters. Most monsters cannot +open doors, although a few don't need to (ex. ghosts can walk through +doors). +.pg +Secret doors are hidden. You can find them with the `s' (search) +command. Once found they are in all ways equivalent to normal doors. +.hn 2 +Traps (`^') +.pg +There are traps throughout the dungeon to snare the unwary delver. +For example, you may suddenly fall into a pit and be stuck for a few +turns trying to climb out. Traps don't appear on your map until you +see one triggered by moving onto it, see something fall into it, or you +discover it with the `s' (search) command. Monsters can fall prey to +traps, too, which can be a very useful defensive strategy. +.pg +There is a special pre-mapped branch of the dungeon based on the +classic computer game ``Sokoban.'' The goal is to push the boulders +into the pits or holes. With careful foresight, it is possible to +complete all of the levels according to the traditional rules of +Sokoban. Some allowances are permitted in case the player gets stuck; +however, they will lower your luck. +.hn 2 +Stairs (`<', `>') +.pg +In general, each level in the dungeon will have a staircase going up +(`<') to the previous level and another going down (`>') to the next +level. There are some exceptions though. For instance, fairly early +in the dungeon you will find a level with two down staircases, one +continuing into the dungeon and the other branching into an area +known as the Gnomish Mines. Those mines eventually hit a dead end, +so after exploring them (if you choose to do so), you'll need to +climb back up to the main dungeon. +.pg +When you traverse a set of stairs, or trigger a trap which sends you +to another level, the level you're leaving will be deactivated and +stored in a file on disk. If you're moving to a previously visited +level, it will be loaded from its file on disk and reactivated. If +you're moving to a level which has not yet been visited, it will be +created (from scratch for most random levels, from a template for +some ``special'' levels, or loaded from the remains of an earlier game +for a ``bones'' level as briefly described below). Monsters are only +active on the current level; those on other levels are essentially +placed into stasis. +.pg +Ordinarily when you climb a set of stairs, you will arrive on the +corresponding staircase at your destination. However, pets (see below) +and some other monsters will follow along if they're close enough when +you travel up or down stairs, and occasionally one of these creatures +will displace you during the climb. When that occurs, the pet or other +monster will arrive on the staircase and you will end up nearby. +.hn 2 +Ladders (`<', `>') +.pg +Ladders serve the same purpose as staircases, and the two types of +inter-level connections are nearly indistinguishable during game play. +.hn 2 +Shops and shopping +.pg +Occasionally you will run across a room with a shopkeeper near the door +and many items lying on the floor. You can buy items by picking them +up and then using the `p' command. You can inquire about the price +of an item prior to picking it up by using the ``#chat'' command +while standing on it. Using an item prior to paying for it will incur a +charge, and the shopkeeper won't allow you to leave the shop until you +have paid any debt you owe. +.pg +You can sell items to a shopkeeper by dropping them to the floor while +inside a shop. You will either be offered an amount of gold and asked +whether you're willing to sell, or you'll be told that the shopkeeper +isn't interested (generally, your item needs to be compatible with the +type of merchandise carried by the shop). +.pg +If you drop something in a shop by accident, the shopkeeper will usually +claim ownership without offering any compensation. You'll have to buy +it back if you want to reclaim it. +.pg +Shopkeepers sometimes run out of money. When that happens, you'll be +offered credit instead of gold when you try to sell something. Credit +can be used to pay for purchases, but it is only good in the shop where +it was obtained; other shopkeepers won't honor it. (If you happen to +find a "credit card" in the dungeon, don't bother trying to use it in +shops; shopkeepers will not accept it.) +.pg +The `$' command, which reports the amount of gold you are carrying +(in inventory, not inside bags or boxes), will also show current shop +debt or credit, if any. The `Iu' command lists unpaid items +(those which still belong to the shop) if you are carrying any. +The `Ix' command shows an inventory-like display of any unpaid +items which have been used up, along with other shop fees, if any. +.hn 3 +Shop idiosyncracies +.pg +Several aspects of shop behavior might be unexpected. +.\" note: using * instead of \(bu is better for plain text output +.lp * 2 +The price of a given item can vary due to a variety of factors. +.lp * 2 +A shopkeeper treats the spot immediately inside the door as if it were +outside the shop. +.lp * 2 +While the shopkeeper watches you like a hawk, he will generally ignore +any other customers. +.lp * 2 +If a shop is "closed for inventory", it will not open of its own accord. +.lp * 2 +Shops do not get restocked with new items, regardless of inventory depletion. + +.hn 1 +Monsters +.pg +Monsters you cannot see are not displayed on the screen. Beware! +You may suddenly come upon one in a dark place. Some magic items can +help you locate them before they locate you (which some monsters can do +very well). +.pg +The commands `/' and `;' may be used to obtain information about those +monsters who are displayed on the screen. The command `C' allows you +to assign a name to a monster, which may be useful to help distinguish +one from another when multiple monsters are present. Assigning a name +which is just a space will remove any prior name. +.pg +The extended command ``#chat'' can be used to interact with an adjacent +monster. There is no actual dialog (in other words, you don't get to +choose what you'll say), but chatting with some monsters such as a +shopkeeper or the Oracle of Delphi can produce useful results. +.hn 2 +Fighting +.pg +If you see a monster and you wish to fight it, just attempt to walk +into it. Many monsters you find will mind their own business unless +you attack them. Some of them are very dangerous when angered. +Remember: discretion is the better part of valor. +.pg +If you can't see a monster (if it is invisible, or if you are blinded), +the symbol `I' will be shown when you learn of its presence. +If you attempt to walk into it, you will try to fight it just like +a monster that you can see; of course, +if the monster has moved, you will attack empty air. If you guess +that the monster has moved and you don't wish to fight, you can use the `m' +command to move without fighting; likewise, if you don't remember a monster +but want to try fighting anyway, you can use the `F' command. +.hn 2 +Your pet +.pg +You start the game with a little dog (`d'), cat (`f'), or pony (`u'), which +follows you about the dungeon and fights monsters with you. Like you, your +pet needs food to survive. It usually feeds itself on fresh carrion +and other meats. If you're worried about it or want to train it, you +can feed it, too, by throwing it food. A properly trained pet can be +very useful under certain circumstances. +.pg +Your pet also gains experience from killing monsters, and can grow +over time, gaining hit points and doing more damage. Initially, your +pet may even be better at killing things than you, which makes pets +useful for low-level characters. +.pg +Your pet will follow you up and down staircases if it is next to you +when you move. Otherwise your pet will be stranded and may become +wild. Similarly, when you trigger certain types of traps which alter +your location (for instance, a trap door which drops you to a lower +dungeon level), any adjacent pet will accompany you and any non-adjacent +pet will be left behind. Your pet may trigger such traps itself; you +will not be carried along with it even if adjacent at the time. +.hn 2 +Steeds +.pg +Some types of creatures in the dungeon can actually be ridden if you +have the right equipment and skill. Convincing a wild beast to let +you saddle it up is difficult to say the least. Many a dungeoneer +has had to resort to magic and wizardry in order to forge the alliance. +Once you do have the beast under your control however, you can +easily climb in and out of the saddle with the `#ride' command. Lead +the beast around the dungeon when riding, in the same manner as +you would move yourself. It is the beast that you will see displayed +on the map. +.pg +Riding skill is managed by the `#enhance' command. See the section +on Weapon proficiency for more information about that. +.hn 2 +Bones levels +.pg +You may encounter the shades and corpses of other adventurers (or even +former incarnations of yourself!) and their personal effects. Ghosts +are hard to kill, but easy to avoid, since they're slow and do little +damage. You can plunder the deceased adventurer's possessions; +however, they are likely to be cursed. Beware of whatever killed the +former player; it is probably still lurking around, gloating over its +last victory. + +.hn 1 +Objects +.pg +When you find something in the dungeon, it is common to want to pick +it up. In NetHack, this is accomplished automatically by walking over +the object (unless you turn off the +.op autopickup +option (see below), or move with the `m' prefix (see above)), or +manually by using the `,' command. +.pg +If you're carrying too many items, NetHack will tell you so and you won't +be able to pick +up anything more. Otherwise, it will add the object(s) to your pack and tell +you what you just picked up. +.pg +As you add items to your inventory, you also add the weight of that object +to your load. The amount that you can carry depends on your strength and +your constitution. The +stronger you are, the less the additional load will affect you. There comes +a point, though, when the weight of all of that stuff you are carrying around +with you through the dungeon will encumber you. Your reactions +will get slower and you'll burn calories faster, requiring food more frequently +to cope with it. Eventually, you'll be so overloaded that you'll either have +to discard some of what you're carrying or collapse under its weight. +.pg +NetHack will tell you how badly you have loaded yourself. The symbols +`Burdened', `Stressed', `Strained', `Overtaxed' and `Overloaded' are +displayed on the bottom line display to indicate your condition. +.pg +When you pick up an object, it is assigned an inventory letter. Many +commands that operate on objects must ask you to find out which object +you want to use. When NetHack asks you to choose a particular object +you are carrying, you are usually presented with a list of inventory +letters to choose from (see Commands, above). +.pg +Some objects, such as weapons, are easily differentiated. Others, like +scrolls and potions, are given descriptions which vary according to +type. During a game, any two objects with the same description are +the same type. However, the descriptions will vary from game to game. +.pg +When you use one of these objects, if its effect is obvious, NetHack +will remember what it is for you. If its effect isn't extremely +obvious, you will be asked what you want to call this type of object +so you will recognize it later. You can also use the ``#name'' +command for the same purpose at any time, to name all objects of a +particular type or just an individual object. +When you use ``#name'' on an object which has already been named, +specifying a space as the value will remove the prior name instead +of assigning a new one. +.hn 2 +Curses and Blessings +.pg +Any object that you find may be cursed, even if the object is +otherwise helpful. The most common effect of a curse is being stuck +with (and to) the item. Cursed weapons weld themselves to your hand +when wielded, so you cannot unwield them. Any cursed item you wear +is not removable by ordinary means. In addition, cursed arms and armor +usually, but not always, bear negative enchantments that make them +less effective in combat. Other cursed objects may act poorly or +detrimentally in other ways. +.pg +Objects can also be blessed. Blessed items usually work better or +more beneficially than normal uncursed items. For example, a blessed +weapon will do more damage against demons. +.pg +There are magical means of bestowing or removing curses upon objects, +so even if you are stuck with one, you can still have the curse +lifted and the item removed. Priests and Priestesses have an innate +sensitivity to this property in any object, so they can more easily avoid +cursed objects than other character roles. +.pg +An item with unknown status will be reported in your inventory with no prefix. +An item which you know the state of will be distinguished in your inventory +by the presence of the word ``cursed'', ``uncursed'' or ``blessed'' in the +description of the item. +.hn 2 +Weapons (`)') +.pg +Given a chance, most monsters in the Mazes of Menace will gratuitously try to +kill you. You need weapons for self-defense (killing them first). Without a +weapon, you do only 1-2 hit points of damage (plus bonuses, if any). +Monk characters are an exception; they normally do much more damage with +bare hands than they do with weapons. +.pg +There are wielded weapons, like maces and swords, and thrown weapons, +like arrows and spears. To hit monsters with a weapon, you must wield it and +attack them, or throw it at them. You can simply elect to throw a spear. +To shoot an arrow, you should first wield a bow, then throw the arrow. +Crossbows shoot crossbow bolts. Slings hurl rocks and (other) stones +(like gems). +.pg +Enchanted weapons have a ``plus'' (or ``to hit enhancement'' which can be +either positive or negative) that adds to your chance to +hit and the damage you do to a monster. The only way to determine a weapon's +enchantment is to have it magically identified somehow. +Most weapons are subject to some type of damage like rust. Such +``erosion'' damage can be repaired. +.pg +The chance that an attack will successfully hit a monster, and the amount +of damage such a hit will do, depends upon many factors. Among them are: +type of weapon, quality of weapon (enchantment and/or erosion), experience +level, strength, dexterity, encumbrance, and proficiency (see below). The +monster's armor class - a general defense rating, not necessarily due to +wearing of armor - is a factor too; also, some monsters are particularly +vulnerable to certain types of weapons. +.pg +Many weapons can be wielded in one hand; some require both hands. +When wielding a two-handed weapon, you can not wear a shield, and +vice versa. When wielding a one-handed weapon, you can have another +weapon ready to use by setting things up with the `x' command, which +exchanges your primary (the one being wielded) and alternate weapons. +And if you have proficiency in the ``two weapon combat'' skill, you +may wield both weapons simultaneously as primary and secondary; use the +`#twoweapon' extended command to engage or disengage that. Only +some types of characters (barbarians, for instance) have the necessary +skill available. Even with that skill, using two weapons at once incurs +a penalty in the chance to hit your target compared to using just one +weapon at a time. +.pg +There might be times when you'd rather not wield any weapon at all. +To accomplish that, wield `-', or else use the `A' command which +allows you to unwield the current weapon in addition to taking off +other worn items. +.pg +Those of you in the audience who are AD&D players, be aware that each +weapon which existed in AD&D does roughly the same damage to monsters in +NetHack. Some of the more obscure weapons (such as the \fIaklys\fP, +\fIlucern hammer\fP, and \fIbec-de-corbin\fP) are defined in an +appendix to \fIUnearthed Arcana\fP, an AD&D supplement. +.pg +The commands to use weapons are `w' (wield), `t' (throw), +`f' (fire, an alternative way of throwing), `Q' (quiver), +`x' (exchange), `#twoweapon', and `#enhance' (see below). +.hn 3 +Throwing and shooting +.pg +You can throw just about anything via the `t' command. It will prompt +for the item to throw; picking `?' will list things in your inventory +which are considered likely to be thrown, or picking `*' will list +your entire inventory. After you've chosen what to throw, you will +be prompted for a direction rather than for a specific target. The +distance something can be thrown depends mainly on the type of object +and your strength. Arrows can be thrown by hand, but can be thrown +much farther and will be more likely to hit when thrown while you are +wielding a bow. +.pg +You can simplify the throwing operation by using the `Q' command to +select your preferred ``missile'', then using the `f' command to +throw it. You'll be prompted for a direction as above, but you don't +have to specify which item to throw each time you use `f'. There is +also an option, +.op autoquiver, +which has NetHack choose another item to automatically fill your +quiver when the inventory slot used for `Q' runs out. +.pg +Some characters have the ability to fire a volley of multiple items in a +single turn. Knowing how to load several rounds of ammunition at +once -- or hold several missiles in your hand -- and still hit a +target is not an easy task. Rangers are among those who are adept +at this task, as are those with a high level of proficiency in the +relevant weapon skill (in bow skill if you're wielding one to +shoot arrows, in crossbow skill if you're wielding one to shoot bolts, +or in sling skill if you're wielding one to shoot stones). +The number of items that the character has a chance to fire varies from +turn to turn. You can explicitly limit the number of shots by using a +numeric prefix before the `t' or `f' command. +For example, ``2f'' (or ``n2f'' if using +.op number_pad +mode) would ensure that at most 2 arrows are shot +even if you could have fired 3. If you specify +a larger number than would have been shot (``4f'' in this example), +you'll just end up shooting the same number (3, here) as if no limit +had been specified. Once the volley is in motion, all of the items +will travel in the same direction; if the first ones kill a monster, +the others can still continue beyond that spot. +.hn 3 +Weapon proficiency +.pg +You will have varying degrees of skill in the weapons available. +Weapon proficiency, or weapon skills, affect how well you can use +particular types of weapons, and you'll be able to improve your skills +as you progress through a game, depending on your role, your experience +level, and use of the weapons. +.pg +For the purposes of proficiency, weapons have +been divided up into various groups such as daggers, broadswords, and +polearms. Each role has a limit on what level of proficiency a character +can achieve for each group. For instance, wizards can become highly +skilled in daggers or staves but not in swords or bows. +.pg +The `#enhance' extended command is used to review current weapons proficiency +(also spell proficiency) and to choose which skill(s) to improve when +you've used one or more skills enough to become eligible to do so. The +skill rankings are ``none'' (sometimes also referred to as ``restricted'', +because you won't be able to advance), ``unskilled'', ``basic'', ``skilled'', +and ``expert''. Restricted skills simply will not appear in the list +shown by `#enhance'. (Divine intervention might unrestrict a particular +skill, in which case it will start at unskilled and be limited to basic.) +Some characters can enhance their barehanded combat or martial arts skill +beyond expert to ``master'' or ``grand master''. +.pg +Use of a weapon in which you're restricted or unskilled +will incur a modest penalty in the chance to hit a monster and also in +the amount of damage done when you do hit; at basic level, there is no +penalty or bonus; at skilled level, you receive a modest bonus in the +chance to hit and amount of damage done; at expert level, the bonus is +higher. A successful hit has a chance to boost your training towards +the next skill level (unless you've already reached the limit for this +skill). Once such training reaches the threshold for that next level, +you'll be told that you feel more confident in your skills. At that +point you can use `#enhance' to increase one or more skills. Such skills +are not increased automatically because there is a limit to your total +overall skills, so you need to actively choose which skills to enhance +and which to ignore. +.hn 2 +Armor (`[') +.pg +Lots of unfriendly things lurk about; you need armor to protect +yourself from their blows. Some types of armor offer better +protection than others. Your armor class is a measure of this +protection. Armor class (AC) is measured as in AD&D, with 10 being +the equivalent of no armor, and lower numbers meaning better armor. +Each suit of armor which exists in AD&D gives the same protection in +NetHack. Here is an (incomplete) list of the armor classes provided by +various suits of armor: + +.TS S +center; +a n. +dragon scale mail 1 +plate mail 3 +crystal plate mail 3 +bronze plate mail 4 +splint mail 4 +banded mail 4 +dwarvish mithril-coat 4 +elven mithril-coat 5 +chain mail 5 +orcish chain mail 6 +scale mail 6 +studded leather armor 7 +ring mail 7 +orcish ring mail 8 +leather armor 8 +leather jacket 9 +no armor 10 +.TE +.pg +You can also wear other pieces of armor (ex. helmets, boots, shields, cloaks) +to lower your armor class even further, but you can only wear one item +of each category (one suit of armor, one cloak, one helmet, one +shield, and so on) at a time. +.pg +If a piece of armor is enchanted, its armor protection will be better +(or worse) than normal, and its ``plus'' (or minus) will subtract from +your armor class. For example, a +1 chain mail would give you +better protection than normal chain mail, lowering your armor class one +unit further to 4. When you put on a piece of armor, you immediately +find out the armor class and any ``plusses'' it provides. Cursed +pieces of armor usually have negative enchantments (minuses) in +addition to being unremovable. +.pg +Many types of armor are subject to some kind of damage like rust. Such +damage can be repaired. Some types of armor may inhibit spell casting. +.pg +The commands to use armor are `W' (wear) and `T' (take off). +The `A' command can also be used to take off armor as well as other +worn items. +.hn 2 +Food (`%') +.pg +Food is necessary to survive. If you go too long without eating you +will faint, and eventually die of starvation. Some types of food will +spoil, and become unhealthy to eat, if not protected. Food stored in +ice boxes or tins (``cans'') will usually stay fresh, but +ice boxes are heavy, and tins take a while to open. +.pg +When you kill monsters, they usually leave corpses which are also +``food.'' Many, but not all, of these are edible; some also give you +special powers when you eat them. A good rule of thumb is ``you are +what you eat.'' +.pg +Some character roles and some monsters are vegetarian. Vegetarian monsters +will typically never eat animal corpses, while vegetarian players can, +but with some rather unpleasant side-effects. +.pg +You can name one food item after something you like to eat with the +.op fruit +option. +.pg +The command to eat food is `e'. +.hn 2 +Scrolls (`?') +.pg +Scrolls are labeled with various titles, probably chosen by ancient wizards +for their amusement value (ex. ``READ ME,'' or ``THANX MAUD'' backwards). +Scrolls disappear after you read them (except for blank ones, without +magic spells on them). +.pg +One of the most useful of these is the \fIscroll of identify\fP, which +can be used to determine what another object is, whether it is cursed or +blessed, and how many uses it has left. Some objects of subtle +enchantment are difficult to identify without these. +.pg +A mail daemon may run up and deliver mail to you as a +\fIscroll of mail\fP (on versions compiled with this feature). +To use this feature on versions where NetHack mail delivery is triggered +by electronic mail appearing in your system mailbox, +you must let NetHack know where to look for new mail by setting +the ``MAIL'' environment variable to the file name of your mailbox. +You may also want to set the ``MAILREADER'' environment +variable to the file name of your favorite reader, so NetHack can shell to it +when you read the scroll. +On versions of NetHack where mail is randomly generated internal to the game, +these environment variables are ignored. +You can disable the mail daemon by turning off the +.op mail +option. +.pg +The command to read a scroll is `r'. +.hn 2 +Potions (`!') +.pg +Potions are distinguished by the color of the liquid inside the flask. +They disappear after you quaff them. +.pg +Clear potions are potions of water. Sometimes these are blessed or cursed, +resulting in holy or unholy water. Holy water is the bane of the undead, so +potions of holy water are good things to throw (`t') at them. It is also +sometimes very useful to dip (``#dip'') an object into a potion. +.pg +The command to drink a potion is `q' (quaff). +.hn 2 +Wands (`/') +.pg +Magic wands usually have multiple magical charges. Some wands are +directional\(emyou must give a direction in which to zap them. You can also +zap them at yourself (just give a `.' or `s' for the direction). Be warned, +however, for this is often unwise. Other wands are nondirectional\(emthey +don't require a direction. The number of charges in a wand is random and +decreases by one whenever you use it. +.pg +When the number of charges left in a wand becomes zero, attempts to use the +wand will usually result in nothing happening. Occasionally, however, it may +be possible to squeeze the last few mana points from an otherwise spent wand, +destroying it in the process. A wand may be recharged by using suitable +magic, but doing so runs the risk of causing it to explode. The chance +for such an explosion starts out very small and increases each time the +wand is recharged. +.pg +In a truly desperate situation, when your back is up against the wall, you +might decide to go for broke and break your wand. This is not for the faint +of heart. Doing so will almost certainly cause a catastrophic release of +magical energies. +.pg +When you have fully identified a particular wand, inventory display will +include additional information in parentheses: the number of times it has +been recharged followed by a colon and then by its current number of charges. +A current charge count of -1 is a special case indicating that the wand +has been cancelled. +.pg +The command to use a wand is `z' (zap). To break one, use the `a' (apply) +command. +.hn 2 +Rings (`=') +.pg +Rings are very useful items, since they are relatively permanent +magic, unlike the usually fleeting effects of potions, scrolls, and +wands. +.pg +Putting on a ring activates its magic. You can wear only two +rings, one on each ring finger. +.pg +Most rings also cause you to grow hungry more rapidly, the rate +varying with the type of ring. +.pg +The commands to use rings are `P' (put on) and `R' (remove). +.hn 2 +Spellbooks (`+') +.pg +Spellbooks are tomes of mighty magic. When studied with the `r' (read) +command, they transfer to the reader the knowledge of a spell (and +therefore eventually become unreadable) \(em unless the attempt backfires. +Reading a cursed spellbook or one with mystic runes beyond +your ken can be harmful to your health! +.pg +A spell (even when learned) can also backfire when you cast it. If you +attempt to cast a spell well above your experience level, or if you have +little skill with the appropriate spell type, or cast it at +a time when your luck is particularly bad, you can end up wasting both the +energy and the time required in casting. +.pg +Casting a spell calls forth magical energies and focuses them with +your naked mind. Some of the magical energy released comes from within +you, and casting several spells in a row may tire you. +Casting of spells also requires practice. With practice, your +skill in each category of spell casting will improve. Over time, however, +your memory of each spell will dim, and you will need to relearn it. +.pg +Some spells are +directional\(emyou must give a direction in which to cast them. You can also +cast them at yourself (just give a `.' or `s' for the direction). Be warned, +however, for this is often unwise. Other spells are nondirectional\(emthey +don't require a direction. +.pg +Just as weapons are divided into groups in which a character can become +proficient (to varying degrees), spells are similarly grouped. +Successfully casting a spell exercises the skill group; sufficient skill +may increase the potency of the spell and reduce the risk of spell failure. +Skill slots are shared with weapons skills. (See also the section on +``Weapon proficiency''.) +.pg +Casting a spell also requires flexible movement, and wearing various types +of armor may interfere with that. +.pg +The command to read a spellbook is the same as for scrolls, `r' +(read). The `+' command lists your current spells, their levels, +categories, and chances for failure. +The `Z' (cast) command casts a spell. +.hn 2 +Tools (`(') +.pg +Tools are miscellaneous objects with various purposes. Some tools +have a limited number of uses, akin to wand charges. For example, lamps burn +out after a while. Other tools are containers, which objects can +be placed into or taken out of. +.pg +The command to use tools is `a' (apply). +.hn 3 +Containers +.pg +You may encounter bags, boxes, and chests in your travels. A tool of +this sort can be opened with the ``#loot'' extended command when +you are standing on top of it (that is, on the same floor spot), +or with the `a' (apply) command when you are carrying it. However, +chests are often locked, and are in any case unwieldy objects. +You must set one down before unlocking it by +using a key or lock-picking tool with the `a' (apply) command, +by kicking it with the `^D' command, +or by using a weapon to force the lock with the ``#force'' extended command. +.pg +Some chests are trapped, causing nasty things to happen when you +unlock or open them. You can check for and try to deactivate traps +with the ``#untrap'' extended command. +.hn 2 +Amulets (`"') +.pg +Amulets are very similar to rings, and often more powerful. Like +rings, amulets have various magical properties, some beneficial, +some harmful, which are activated by putting them on. +.pg +Only one amulet may be worn at a time, around your neck. +.pg +The commands to use amulets are the same as for rings, `P' (put on) +and `R' (remove). +.hn 2 +Gems (`*') +.pg +Some gems are valuable, and can be sold for a lot of gold. They are also +a far more efficient way of carrying your riches. Valuable gems increase +your score if you bring them with you when you exit. +.pg +Other small rocks are also categorized as gems, but they are much less +valuable. All rocks, however, can be used as projectile weapons (if you +have a sling). In the most desperate of cases, you can still throw them +by hand. +.hn 2 +Large rocks (`\`') +.pg +Statues and boulders are not particularly useful, and are generally +heavy. It is rumored that some statues are not what they seem. +.pg +Very large humanoids (giants and their ilk) have been known to use boulders +as weapons. +.hn 2 +Gold (`$') +.pg +Gold adds to your score, and you can buy things in shops with it. +There are a number +of monsters in the dungeon that may be influenced by the amount of gold +you are carrying (shopkeepers aside). + +.hn 1 +Conduct +.pg +As if winning NetHack were not difficult enough, certain players +seek to challenge themselves by imposing restrictions on the +way they play the game. The game automatically tracks some of +these challenges, which can be checked at any time with the #conduct +command or at the end of the game. When you perform an action which +breaks a challenge, it will no longer be listed. This gives +players extra ``bragging rights'' for winning the game with these +challenges. Note that it is perfectly acceptable to win the game +without resorting to these restrictions and that it is unusual for +players to adhere to challenges the first time they win the game. +.pg +Several of the challenges are related to eating behavior. The most +difficult of these is the foodless challenge. Although creatures +can survive long periods of time without food, there is a physiological +need for water; thus there is no restriction on drinking beverages, +even if they provide some minor food benefits. +Calling upon your god for help with starvation does +not violate any food challenges either. +.pg +A strict vegan diet is one which avoids any food derived from animals. +The primary source of nutrition is fruits and vegetables. The +corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') are +also considered to be vegetable matter. Certain human +food is prepared without animal products; namely, lembas wafers, cram +rations, food rations (gunyoki), K-rations, and C-rations. +Metal or another normally indigestible material eaten while polymorphed +into a creature that can digest it is also considered vegan food. +Note however that eating such items still counts against foodless conduct. +.pg +Vegetarians do not eat animals; +however, they are less selective about eating animal byproducts than vegans. +In addition to the vegan items listed above, they may eat any kind +of pudding (`P') other than the black puddings, +eggs and food made from eggs (fortune cookies and pancakes), +food made with milk (cream pies and candy bars), and lumps of +royal jelly. Monks are expected to observe a vegetarian diet. +.pg +Eating any kind of meat violates the vegetarian, vegan, and foodless +conducts. This includes tripe rations, the corpses or tins of any +monsters not mentioned above, and the various other chunks of meat +found in the dungeon. Swallowing and digesting a monster while polymorphed +is treated as if you ate the creature's corpse. +Eating leather, dragon hide, or bone items while +polymorphed into a creature that can digest it, or eating monster brains +while polymorphed into a mind flayer, is considered eating +an animal, although wax is only an animal byproduct. +.pg +Regardless of conduct, there will be some items which are indigestible, +and others which are hazardous to eat. Using a swallow-and-digest +attack against a monster is equivalent to eating the monster's corpse. +Please note that the term ``vegan'' is used here only in the context of +diet. You are still free to choose not to use or wear items derived +from animals (e.g. leather, dragon hide, bone, horns, coral), but the +game will not keep track of this for you. Also note that ``milky'' +potions may be a translucent white, but they do not contain milk, +so they are compatible with a vegan diet. Slime molds or +player-defined ``fruits'', although they could be anything +from ``cherries'' to ``pork chops'', are also assumed to be vegan. +.pg +An atheist is one who rejects religion. This means that you cannot +#pray, #offer sacrifices to any god, #turn undead, or #chat with a priest. +Particularly selective readers may argue that playing Monk or Priest +characters should violate this conduct; that is a choice left to the +player. Offering the Amulet of Yendor to your god is necessary to +win the game and is not counted against this conduct. You are also +not penalized for being spoken to by an angry god, priest(ess), or +other religious figure; a true atheist would hear the words but +attach no special meaning to them. +.pg +Most players fight with a wielded weapon (or tool intended to be +wielded as a weapon). Another challenge is to win the game without +using such a wielded weapon. You are still permitted to throw, +fire, and kick weapons; use a wand, spell, or other type of item; +or fight with your hands and feet. +.pg +In NetHack, a pacifist refuses to cause the death of any other monster +(i.e. if you would get experience for the death). This is a particularly +difficult challenge, although it is still possible to gain experience +by other means. +.pg +An illiterate character cannot read or write. This includes reading +a scroll, spellbook, fortune cookie message, or t-shirt; writing a +scroll; or making an engraving of anything other than a single ``x'' (the +traditional signature of an illiterate person). Reading an engraving, +or any item that is absolutely necessary to win the game, is not counted +against this conduct. The identity of scrolls and spellbooks (and +knowledge of spells) in your starting inventory is assumed to be +learned from your teachers prior to the start of the game and isn't +counted. +.pg +There are several other challenges tracked by the game. It is possible +to eliminate one or more species of monsters by genocide; playing without +this feature is considered a challenge. When the game offers you an +opportunity to genocide monsters, you may respond with the monster type +``none'' if you want to decline. You can change the form of an item into +another item of the same type (``polypiling'') or the form of your own +body into another creature (``polyself'') by wand, spell, or potion of +polymorph; avoiding these effects are each considered challenges. +Polymorphing monsters, including pets, does not break either of these +challenges. +Finally, you may sometimes receive wishes; a game without an attempt to +wish for any items is a challenge, as is a game without wishing for +an artifact (even if the artifact immediately disappears). When the +game offers you an opportunity to make a wish for an item, you may +choose ``nothing'' if you want to decline. + +.hn 1 +Options +.pg +Due to variations in personal tastes and conceptions of how NetHack +should do things, there are options you can set to change how NetHack +behaves. +.hn 2 +Setting the options +.pg +Options may be set in a number of ways. Within the game, the `O' +command allows you to view all options and change most of them. +You can also set options automatically by placing them in the +NETHACKOPTIONS environment variable or in a configuration file. +Some versions of NetHack also have front-end programs that allow +you to set options before starting the game. +.hn 2 +Using the NETHACKOPTIONS environment variable +.pg +The NETHACKOPTIONS variable is a comma-separated list of initial +values for the various options. Some can only be turned on or off. +You turn one of these on by adding the name of the option to the list, +and turn it off by typing a `!' or ``no'' before the name. Others take a +character string as a value. You can set string options by typing +the option name, a colon or equals sign, and then the value of the string. +The value is terminated by the next comma or the end of string. +.pg +For example, to set up an environment variable so that ``autoquiver'' is on, +``autopickup'' is off, the name is set to ``Blue Meanie'', and the fruit +is set to ``papaya'', you would enter the command +.sd +% \fBsetenv NETHACKOPTIONS "autoquiver,\e!autopickup,name:Blue Meanie,fruit:papaya"\fP +.ed +in \fIcsh\fP (note the need to escape the ! since it's special to the shell), or +.sd +$ \fBNETHACKOPTIONS="autoquiver,!autopickup,name:Blue Meanie,fruit:papaya"\fP +$ \fBexport NETHACKOPTIONS\fP +.ed +in \fIsh\fP or \fIksh\fP. +.hn 2 +Using a configuration file +.pg +Any line in the configuration file starting with `#' is treated as a comment. +Any line in the configuration file starting with ``OPTIONS='' may be +filled out with options in the same syntax as in NETHACKOPTIONS. +Any line starting with ``DUNGEON='', ``EFFECTS='', ``MONSTERS='', +``OBJECTS='', ``TRAPS='', or ``BOULDER='' +is taken as defining the corresponding +.op dungeon, +.op effects, +.op monsters, +.op objects +.op traps +or +.op boulder +option in a different syntax, +a sequence of decimal numbers giving the character position +in the current font to be used in displaying each entry. +A zero in any entry in such a sequence leaves the display of that +entry unchanged; this feature is not available using the option syntax. +Such a sequence can be continued to multiple lines by putting a `\e' +at the end of each line to be continued. +.pg +If your copy of the game included the compile time AUTOPICKUP_EXCEPTIONS +option, then any line starting with ``AUTOPICKUP_EXCEPTION='' is taken +as defining an exception to the +.op pickup_types +option. +There is a section of this Guidebook that discusses that. +.pg +The default name of the configuration file varies on different +operating systems, but NETHACKOPTIONS can also be set to +the full name of a file you want to use (possibly preceded by an `@'). +.hn 2 +Customization options +.pg +Here are explanations of what the various options do. +Character strings that are too long may be truncated. +Some of the options listed may be inactive in your dungeon. +.lp align +Your starting alignment (align:lawful, align:neutral, +or align:chaotic). You may specify just the first letter. +The default is to randomly pick an appropriate alignment. +Cannot be set with the `O' command. +.lp autodig +Automatically dig if you are wielding a digging tool and moving into a place +that can be dug (default false). +.lp "autopickup " +Automatically pick up things onto which you move (default on). +See +.op pickup_types +to refine the behavior. +.lp "autoquiver " +This option controls what happens when you attempt the `f' (fire) +command with an empty quiver. When true, the computer will fill +your quiver with some suitable weapon. Note that it will not take +into account the blessed/cursed status, enchantment, damage, or +quality of the weapon; you are free to manually fill your quiver with +the `Q' command instead. If no weapon is found or the option is +false, the `t' (throw) command is executed instead. (default false) +.lp boulder +Set the character used to display boulders (default is rock class symbol). +.lp catname +Name your starting cat (ex. ``catname:Morris''). +Cannot be set with the `O' command. +.lp character +Pick your type of character (ex. ``character:Monk''); +synonym for ``role''. See ``name'' for an alternate method +of specifying your role. Normally only the first letter of +the value is examined; the string ``random'' is an exception. +.lp checkpoint +Save game state after each level change, for possible recovery after +program crash (default on). +.lp checkspace +Check free disk space before writing files to disk (default on). +You may have to turn this off if you have more than 2 GB free space +on the partition used for your save and level files. +Only applies when MFLOPPY was defined during compilation. +.lp cmdassist +Have the game provide some additional command assistance for +new players if it detects some anticipated mistakes (default on). +.lp "confirm " +Have user confirm attacks on pets, shopkeepers, and other +peaceable creatures (default on). +.lp DECgraphics +Use a predefined selection of characters from the DEC VT-xxx/DEC +Rainbow/ANSI line-drawing character set to display the dungeon/effects/traps +instead of having to define a full graphics set yourself (default off). +This option also sets up proper handling of graphics +characters for such terminals, so you should specify it when appropriate +even if you override the selections with your own graphics strings. +.lp disclose +Controls options for disclosing various information when the game ends (defaults +to all possibilities being disclosed). +The possibilities are: +.sd +.si +i - disclose your inventory. +a - disclose your attributes. +v - summarize monsters that have been vanquished. +g - list monster species that have been genocided. +c - display your conduct. +.ei +.ed +Each disclosure possibility can optionally be preceded by a prefix which +let you refine how it behaves. Here are the valid prefixes: +.sd +.si +y - prompt you and default to yes on the prompt. +n - prompt you and default to no on the prompt. ++ - disclose it without prompting. +- - do not disclose it and do not prompt. +.ei +.ed +(ex. ``disclose:yi na +v -g -c'') +The example sets +.op inventory +to prompt and default to yes, +.op attributes +to prompt and default to no, +.op vanquished +to disclose without prompting, +.op genocided +to not disclose and not to prompt, +.op conduct +to not disclose and not to prompt. +Note that the vanquished monsters list includes all monsters killed by +traps and each other as well as by you. +.lp dogname +Name your starting dog (ex. ``dogname:Fang''). +Cannot be set with the `O' command. +.lp dungeon +Set the graphics symbols for displaying the dungeon +(default \&``\ |--------||.-|++##.##<><>_|\e\e#{}.}..##\ #}''). +The +.op dungeon +option should be followed by a string of 1-41 +characters to be used instead of the default map-drawing characters. +The dungeon map will use the characters you specify instead of the +default symbols, and default symbols for any you do not specify. +Remember that you may need to escape some of these characters +on a command line if they are special to your shell. + +Note that NetHack escape-processes this option string in conventional C +fashion. This means that `\e' is a prefix to take the following +character literally. Thus `\e' needs to be represented as `\e\e'. +The special escape +form `\em' switches on the meta bit in the following character, and the `^' +prefix causes the following character to be treated as a control +character. + +The order of the symbols is: solid rock, vertical wall, horizontal +wall, upper left corner, upper right corner, lower left corner, lower +right corner, cross wall, upward T wall, downward T wall, leftward T +wall, rightward T wall, no door, vertical open door, horizontal open +door, vertical closed door, horizontal closed door, iron bars, tree, +floor of a room, dark corridor, lit corridor, stairs up, stairs down, +ladder up, ladder down, altar, grave, throne, kitchen sink, fountain, pool or moat, +ice, lava, vertical lowered drawbridge, horizontal lowered drawbridge, +vertical raised drawbridge, horizontal raised drawbridge, air, cloud, +under water. + +You might want to use `+' for the corners and T walls for a more +aesthetic, boxier display. Note that in the next release, new symbols +may be added, or the present ones rearranged. + +Cannot be set with the `O' command. +.lp effects +Set the graphics symbols for displaying special effects +(default \&``|-\e\e/*!)(0#@*/-\e\e||\e\e-//-\e\e|\ |\e\e-/''). +The +.op effects +option should be followed by a string of 1-29 +characters to be used instead of the default special-effects characters. +This string is subjected to the same processing as the +.op dungeon +option. + +The order of the symbols is: vertical beam, horizontal beam, left slant, +right slant, digging beam, camera flash beam, left boomerang, right boomerang, +four glyphs giving the sequence for magic resistance displays, +the eight surrounding glyphs for swallowed display, +nine glyphs for explosions. +An explosion consists of three rows (top, middle, and bottom) of three +characters. The explosion is centered in the center of this 3 by 3 +array. + +Note that in the next release, new symbols may be added, +or the present ones rearranged. + +Cannot be set with the `O' command. +.lp extmenu +Changes the extended commands interface to pop-up a menu of available commands. +It is keystroke compatible with the traditional interface except that it does +not require that you hit Enter. It is implemented only by the tty port +(default off), when the game has been compiled to support tty graphics. +.lp female +An obsolete synonym for ``gender:female''. +Cannot be set with the `O' command. +.lp fixinv +An object's inventory letter sticks to it when it's dropped (default on). +If this is off, dropping an object shifts all the remaining inventory letters. +.lp "fruit " +Name a fruit after something you enjoy eating (ex. ``fruit:mango'') +(default ``slime mold''). Basically a nostalgic whimsy that NetHack uses +from time to time. You should set this to something you find more +appetizing than slime mold. Apples, oranges, pears, bananas, and melons +already exist in NetHack, so don't use those. +.lp gender +Your starting gender (gender:male or gender:female). +You may specify just the first letter. Although you can +still denote your gender using the ``male'' and ``female'' +options, the ``gender'' option will take precedence. +The default is to randomly pick an appropriate gender. +Cannot be set with the `O' command. +.lp help +If more information is available for an object looked at +with the `/' command, ask if you want to see it (default on). Turning help +off makes just looking at things faster, since you aren't interrupted with the +``More info?'' prompt, but it also means that you might miss some +interesting and/or important information. +.lp horsename +Name your starting horse (ex. ``horsename:Trigger''). +Cannot be set with the `O' command. +.lp IBMgraphics +Use a predefined selection of IBM extended ASCII characters to display the +dungeon/effects/traps instead of having to define a full graphics set +yourself (default off). +This option also sets up proper handling of graphics +characters for such terminals, so you should specify it when appropriate +even if you override the selections with your own graphics strings. +.lp ignintr +Ignore interrupt signals, including breaks (default off). +.lp legacy +Display an introductory message when starting the game (default on). +.lp lit_corridor +Show corridor squares seen by night vision or a light source held by your +character as lit (default off). +.lp lootabc +Use the old `a', `b', and `c' keyboard shortcuts when +looting, rather than the mnemonics `o', `i', and `b' (default off). +.lp "mail " +Enable mail delivery during the game (default on). +.lp "male " +An obsolete synonym for ``gender:male''. +Cannot be set with the `O' command. +.lp menustyle +Controls the interface used when you need to choose various objects (in +response to the Drop command, for instance). The value specified should +be the first letter of one of the following: traditional, combination, +partial, or full. Traditional was the only interface available for +earlier versions; it consists of a prompt for object class characters, +followed by an object-by-object prompt for all items matching the selected +object class(es). Combination starts with a prompt for object class(es) +of interest, but then displays a menu of matching objects rather than +prompting one-by-one. Partial skips the object class filtering and +immediately displays a menu of all objects. Full displays a menu of +object classes rather than a character prompt, and then a menu of matching +objects for selection. +.lp menu_deselect_all +Menu character accelerator to deselect all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default '-'. +.lp menu_deselect_page +Menu character accelerator to deselect all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default '\e'. +.lp menu_first_page +Menu character accelerator to jump to the first page in a menu. +Implemented by the Amiga, Gem and tty ports. +Default '^'. +.lp menu_headings +Controls how the headings in a menu are highlighted. +Values are 'bold', 'inverse', or 'underline'. +Not all ports can actually display all three types. +.lp menu_invert_all +Menu character accelerator to invert all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default '@'. +.lp menu_invert_page +Menu character accelerator to invert all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default '~'. +.lp menu_last_page +Menu character accelerator to jump to the last page in a menu. +Implemented by the Amiga, Gem and tty ports. +Default '|'. +.lp menu_next_page +Menu character accelerator to goto the next menu page. +Implemented by the Amiga, Gem and tty ports. +Default '>'. +.lp menu_previous_page +Menu character accelerator to goto the previous menu page. +Implemented by the Amiga, Gem and tty ports. +Default '<'. +.lp menu_search +Menu character accelerator to search for a menu item. +Implemented by the Amiga, Gem and X11 ports. +Default ':'. +.lp menu_select_all +Menu character accelerator to select all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default '.'. +.lp menu_select_page +Menu character accelerator to select all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default ','. +.lp monsters +Set the characters used to display monster classes (default +``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@\ \'&;:~]''). +This string is subjected to the same processing as the +.op dungeon +option. +The order of the symbols is +ant or other insect, blob, cockatrice, +dog or other canine, eye or sphere, feline, +gremlin, humanoid, imp or minor demon, +jelly, kobold, leprechaun, +mimic, nymph, orc, +piercer, quadruped, rodent, +arachnid or centipede, trapper or lurker above, horse or unicorn, +vortex, worm, xan or other mythical/fantastic insect, +light, zruty, +angelic being, bat or bird, centaur, +dragon, elemental, fungus or mold, +gnome, giant humanoid, invisible monster, +jabberwock, Keystone Kop, lich, +mummy, naga, ogre, +pudding or ooze, quantum mechanic, rust monster, +snake, troll, umber hulk, +vampire, wraith, xorn, +apelike creature, zombie, +human, ghost, golem, +demon, sea monster, lizard, +long worm tail, and mimic. +Cannot be set with the `O' command. +.lp msghistory +The number of top line messages to save (and recall with ^P) (default 20). +Cannot be set with the `O' command. +.lp msg_window +Allows you to change the way recalled messages are displayed. +(It is currently implemented for tty only.) +The possible values are: +.sd +.si +s - single message (default, this was the behavior before 3.4.0). +c - combination, two messages as `single', then as `full'. +f - full window, oldest message first. +r - full window, newest message first. +.ei +.ed +For backward compatibility, no value needs to be specified (which +defaults to `full'), or it can be negated (which defaults to `single'). +.lp "name " +Set your character's name (defaults to your user name). You can also +set your character's role by appending a dash and one or more letters of +the role (that is, by suffixing one of +.op "-A -B -C -H -K -M -P -Ra -Ro -S -T -V -W" ). +If +.op "-@" +is used for the role, then a random one will be automatically chosen. +Cannot be set with the `O' command. +.lp "news " +Read the NetHack news file, if present (default on). +Since the news is shown at the beginning of the game, there's no point +in setting this with the `O' command. +.lp "null " +Send padding nulls to the terminal (default off). +.lp number_pad +Use the number keys to move instead of [yuhjklbn] (default 0 or off). +(number_pad:2 invokes the old DOS behavior where `5' means `g', meta-`5' +means `G', and meta-`0' means `I'.) +.lp objects +Set the characters used to display object classes +(default ``])[="(%!?+/$*`0_.''). +This string is subjected to the same processing as the +.op dungeon +option. +The order of the symbols is +illegal-object (should never be seen), weapon, armor, ring, amulet, tool, +food, potion, scroll, spellbook, wand, gold, gem or rock, boulder or statue, +iron ball, chain, and venom. +Cannot be set with the `O' command. +.lp packorder +Specify the order to list object types in (default ``")[%?+!=/(*`0_''). +The value of this option should be a string containing the +symbols for the various object types. Any omitted types are filled in +at the end from the previous order. +.lp perm_invent +If true, always display your current inventory in a window. This only +makes sense for windowing system interfaces that implement this feature. +.lp pettype +Specify the type of your initial pet, if you are playing a character class +that uses multiple types of pets; or choose to have no initial pet at all. +Possible values are ``cat'', ``dog'' and ``none''. +Cannot be set with the `O' command. +.lp pickup_burden +When you pick up an item that would exceed this encumbrance +level (Unburdened, Burdened, streSsed, straiNed, overTaxed, +or overLoaded), you will be asked if you want to continue. +(Default `S'). +.lp pickup_types +Specify the object types to be picked up when +.op autopickup +is on. Default is all types. If your copy of the game has the experimental +compile time option AUTOPICKUP_EXCEPTIONS included, you may be able to use +.op autopickup_exception +configuration file lines to further refine +.op autopickup +behavior. +.lp prayconfirm +Prompt for confirmation before praying (default on). +.lp pushweapon +Using the `w' (wield) command when already wielding +something pushes the old item into your alternate weapon slot (default off). +.lp race +Selects your race (for example, ``race:human''). Default is random. +Cannot be set with the `O' command. +.lp rest_on_space +Make the space bar a synonym for the `.' (rest) command (default off). +.lp "role " +Pick your type of character (ex. ``role:Samurai''); +synonym for ``character''. See ``name'' for an alternate method +of specifying your role. Normally only the first letter of the +value is examined; `r' is an exception with ``Rogue'', ``Ranger'', +and ``random'' values. +.lp runmode +Controls the amount of screen updating for the map window when engaged +in multi-turn movement (running via shift+direction or control+direction +and so forth, or via the travel command or mouse click). +The possible values are: +.sd +.si +teleport - update the map after movement has finished; +run - update the map after every seven or so steps; +walk - update the map after each step; +crawl - like walk, but pause briefly after each step. +.ei +.ed +This option only affects the game's screen display, not the actual +results of moving. The default is `run'; versions prior to 3.4.1 +used `teleport' only. Whether or not the effect is noticeable will +depend upon the window port used or on the type of terminal. +.lp safe_pet +Prevent you from (knowingly) attacking your pets (default on). +.lp scores +Control what parts of the score list you are shown at the end (ex. +``scores:5 top scores/4 around my score/own scores''). Only the first +letter of each category (`t', `a', or `o') is necessary. +.lp showexp +Show your accumulated experience points on bottom line (default off). +.lp showrace +Display yourself as the glyph for your race, rather than the glyph +for your role (default off). Note that this setting affects only +the appearance of the display, not the way the game treats you. +.lp showscore +Show your approximate accumulated score on bottom line (default off). +.lp "silent " +Suppress terminal beeps (default on). +.lp sortpack +Sort the pack contents by type when displaying inventory (default on). +.lp sound +Enable messages about what your character hears (default on). +Note that this has nothing to do with your computer's audio capabilities. +This option is only partly under player control. The game toggles it +off and on during and after sleep, for example. +.lp sparkle +Display a sparkly effect when a monster (including yourself) is hit by an +attack to which it is resistant (default on). +.lp standout +Boldface monsters and ``\fB--More--\fP'' (default off). +.lp suppress_alert +This option may be set to a NetHack version level to suppress +alert notification messages about feature changes for that +and prior versions (ex. ``suppress_alert:3.3.1''). +.lp "time " +Show the elapsed game time in turns on bottom line (default off). +.lp timed_delay +When pausing momentarily for display effect, such as with explosions and +moving objects, use a timer rather than sending extra characters to the +screen. (Applies to ``tty'' interface only; ``X11'' interface always +uses a timer based delay. The default is on if configured into the +program.) +.lp tombstone +Draw a tombstone graphic upon your death (default on). +.lp toptenwin +Put the ending display in a NetHack window instead of on stdout (default off). +Setting this option makes the score list visible when a windowing version +of NetHack is started without a parent window, but it no longer leaves +the score list around after game end on a terminal or emulating window. +.lp traps +Set the graphics symbols for displaying traps +(default \&``^^^^^^^^^^^^^^^^^"^^^^''). +The +.op traps +option should be followed by a string of 1-22 +characters to be used instead of the default traps characters. +This string is subjected to the same processing as the +.op dungeon +option. + +The order of the symbols is: +arrow trap, dart trap, falling rock trap, squeaky board, bear trap, +land mine, rolling boulder trap, sleeping gas trap, rust trap, fire trap, +pit, spiked pit, hole, trap door, teleportation trap, level teleporter, +magic portal, web, statue trap, magic trap, anti-magic field, polymorph trap. + +Cannot be set with the `O' command. +.lp travel +Allow the travel command (default on). Turning this option off will +prevent the game from attempting unintended moves if you make inadvertent +mouse clicks on the map window. +.lp verbose +Provide more commentary during the game (default on). +.lp windowtype +Select which windowing system to use, such as ``tty'' or ``X11'' +(default depends on version). +Cannot be set with the `O' command. +.hn 2 +Window Port Customization options +.pg +Here are explanations of the various options that are +used to customize and change the characteristics of the +windowtype that you have chosen. +Character strings that are too long may be truncated. +Not all window ports will adjust for all settings listed +here. You can safely add any of these options to your config +file, and if the window port is capable of adjusting to +suit your preferences, it will attempt to do so. If it +can't it will silently ignore it. You can find out if an +option is supported by the window port that you are currently +using by checking to see if it shows up in the Options list. +Some options are dynamic and can be specified during the game +with the `O' command. +.lp align_message +Where to align or place the message window (top, bottom, left, or right) +.lp align_status +Where to align or place the status window (top, bottom, left, or right). +.lp ascii_map +NetHack should display an ascii character map if it can. +.lp color +NetHack should display color if it can for different monsters, +objects, and dungeon features +.lp eight_bit_tty +NetHack should pass eight-bit character values (for example, specified with the +.op traps +option) straight through to your terminal (default off). +.lp font_map +NetHack should use a font by the chosen name for the map window. +.lp font_menu +NetHack should use a font by the chosen name for menu windows. +.lp font_message +NetHack should use a font by the chosen name for the message window. +.lp font_status +NetHack should use a font by the chosen name for the status window. +.lp font_text +NetHack should use a font by the chosen name for text windows. +.lp font_size_map +NetHack should use this size font for the map window. +.lp font_size_menu +NetHack should use this size font for menu windows. +.lp font_size_message +NetHack should use this size font for the message window. +.lp font_size_status +NetHack should use this size font for the status window. +.lp font_size_text +NetHack should use this size font for text windows. +.lp fullscreen +NetHack should try and display on the entire screen rather than in a window. +.lp hilite_pet +Visually distinguish pets from similar animals (default off). +The behavior of this option depends on the type of windowing you use. +In text windowing, text highlighting or inverse video is often used; +with tiles, generally displays a heart symbol near pets. +.lp large_font +NetHack should use a large font. +.lp map_mode +NetHack should display the map in the manner specified. +.lp mouse_support +Allow use of the mouse for input and travel. +.lp player_selection +NetHack should pop up dialog boxes, or use prompts for character selection. +.lp popup_dialog +NetHack should pop up dialog boxes for input. +.lp preload_tiles +NetHack should preload tiles into memory. +For example, in the protected mode MSDOS version, control whether tiles +get pre-loaded into RAM at the start of the game. Doing so +enhances performance of the tile graphics, but uses more memory. (default on). +Cannot be set with the `O' command. +.lp scroll_amount +NetHack should scroll the display by this number of cells +when the hero reaches the scroll_margin. +.lp scroll_margin +NetHack should scroll the display when the hero or cursor +is this number of cells away from the edge of the window. +.lp softkeyboard +Display an onscreen keyboard. Handhelds are most likely to support this option. +.lp splash_screen +NetHack should display an opening splash screen when it starts up (default yes). +.lp tiled_map +NetHack should display a tiled map if it can. +.lp tile_file +Specify the name of an alternative tile file to override the default. +.lp tile_height +Specify the preferred height of each tile in a tile capable port. +.lp tile_width +Specify the preferred width of each tile in a tile capable port +.lp use_inverse +NetHack should display inverse when the game specifies it. +.lp vary_msgcount +NetHack should display this number of messages at a time in +the message window. +.lp windowcolors +NetHack should display windows with the specified foreground/background +colors if it can. +.lp wraptext +NetHack port should wrap long lines of text if they don't fit in +the visible area of the window. +.hn 2 +Platform-specific Customization options +.pg +Here are explanations of options that are used by specific platforms or ports +to customize and change the port behavior. +.lp altkeyhandler +Select an alternate keystroke handler dll to load (Win32 tty NetHack only). +The name of the handler is specified without the .dll extension and without any +path information. +Cannot be set with the `O' command. +.lp altmeta +(default on, AMIGA NetHack only). +.lp "BIOS " +Use BIOS calls to update the screen +display quickly and to read the keyboard (allowing the use of arrow +keys to move) on machines with an IBM PC compatible BIOS ROM (default off, +OS/2, PC, and ST NetHack only). +.lp flush +(default off, AMIGA NetHack only). +.lp "MACgraphics" +(default on, Mac NetHack only). +.lp page_wait +(default on, Mac NetHack only). +.lp "rawio " +Force raw (non-cbreak) mode for faster output and more +bulletproof input (MS-DOS sometimes treats `^P' as a printer toggle +without it) (default off, OS/2, PC, and ST NetHack only). +Note: DEC Rainbows hang if this is turned on. +Cannot be set with the `O' command. +.lp soundcard +(default on, PC NetHack only). +Cannot be set with the `O' command. +.lp subkeyvalue +(Win32 tty NetHack only). +May be used to alter the value of keystrokes that the operating system +returns to NetHack to help compensate for international keyboard issues. +OPTIONS=subkeyvalue:171/92 +will return 92 to NetHack, if 171 was originally going to be returned. +You can use multiple subkeyvalue statements in the config file if needed. +Cannot be set with the `O' command. +.lp video +Set the video mode used (PC NetHack only). +Values are `autodetect', `default', or `vga'. +Setting `vga' (or `autodetect' with vga hardware present) will cause +the game to display tiles. +Cannot be set with the `O' command. +.lp videocolors +Set the color palette for PC systems using NO_TERMS +(default 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). +The order of colors is red, green, brown, blue, magenta, cyan, +bright.white, bright.red, bright.green, yellow, bright.blue, +bright.magenta, and bright.cyan. +Cannot be set with the `O' command. +.lp videoshades +Set the intensity level of the three gray scales available +(default dark normal light, PC NetHack only). +If the game display is difficult to read, try adjusting these scales; +if this does not correct the problem, try !color. +Cannot be set with the `O' command. +.hn 2 +Configuring autopickup exceptions +.pg +There is an experimental compile time option called AUTOPICKUP_EXCEPTIONS. +If your copy of the game was built with that option defined, you can +further refine the behavior of the +.op autopickup +option beyond what is available through the +.op pickup_types +option. +.pg +By placing +.op autopickup_exception +lines in your configuration +file, you can define patterns to be checked when the game is about to +autopickup something. +.lp autopickup_exception +Sets an exception to the +.op pickup_types +option. +The +.op autopickup_exception +option should be followed by a string of 1-80 characters to be used as a +pattern to match against the singular form of the description of an +object at your location. +.pg +You may use the following special characters in a pattern: +.sd +.si + *--- matches 0 or more characters. + ?--- matches any single character. +.ei +.ed +.pg +In addition, some characters are treated specially if they occur as the first +character in the string pattern, specifically: +.sd +.si +< - always pickup an object that matches the pattern that follows. +> - never pickup an object that matches the pattern that follows. +.ei +.ed +.pg +Can be set with the `O' command, but the setting is not preserved +across saves and restores. +.pg +Here's a couple of examples of autopickup_exceptions: +.sd +.si +autopickup_exception="<*arrow" +autopickup_exception=">*corpse" +autopickup_exception=">* cursed*" +.ei +.ed +The first example above will result in autopickup of any type of arrow. +The second example results in the exclusion of any corpse from autopickup. +The last example results in the exclusion of items known to be cursed from autopickup. +A `never pickup' rule takes precedence over an `always pickup' rule if both match. +.hn 2 +Configuring User Sounds +.pg +Some platforms allow you to define sound files to be played when a message +that matches a user-defined pattern is delivered to the message window. +At this time the Qt port and the win32tty and win32gui ports support the +use of user sounds. +.pg +The following config file entries are relevant to mapping user sounds +to messages: +.lp SOUNDDIR +The directory that houses the sound files to be played. +.lp SOUND +An entry that maps a sound file to a user-specified message pattern. +Each SOUND entry is broken down into the following parts: +.sd +.si +MESG - message window mapping (the only one supported in 3.4). +pattern - the pattern to match. +sound file - the sound file to play. +volume - the volume to be set while playing the sound file. +.ei +.ed +.pg +The exact format for the pattern depends on whether the platform is +built to use ``regular expressions'' or NetHack's own internal pattern +matching facility. The ``regular expressions'' matching can be much more +sophisticated than the internal NetHack pattern matching, but requires +3rd party libraries on some platforms. There are plenty of references +available elsewhere for explaining ``regular expressions''. You can verify +which pattern matching is used by your port with the #version command. +.pg +NetHack's internal pattern matching routine uses the following +special characters in its pattern matching: +.sd +.si + *--- matches 0 or more characters. + ?--- matches any single character. +.ei +.ed +.pg +Here's an example of a sound mapping using NetHack's internal +pattern matching facility: +.sd + SOUND=MESG "*chime of a cash register*" "gong.wav" 50 +.ed +specifies that any message with "chime of a cash register" contained +in it will trigger the playing of "gong.wav". You can have multiple +SOUND entries in your config file. +.pg +.hn 2 +Configuring NetHack for Play by the Blind +.pg +NetHack can be set up to use only standard ASCII characters for making +maps of the dungeons. This makes the MS-DOS versions of NetHack completely +accessible to the blind who use speech and/or Braille access technologies. +Players will require a good working knowledge of their screen-reader's +review features, and will have to know how to navigate horizontally and +vertically character by character. They will also find the search +capabilities of their screen-readers to be quite valuable. Be certain to +examine this Guidebook before playing so you have an idea what the screen +layout is like. You'll also need to be able to locate the PC cursor. It is +always where your character is located. Merely searching for an @-sign will +not always find your character since there are other humanoids represented +by the same sign. Your screen-reader should also have a function which +gives you the row and column of your review cursor and the PC cursor. +These co-ordinates are often useful in giving players a better sense of the +overall location of items on the screen. +.pg +While it is not difficult for experienced users to edit the \fBdefaults.nh\fP +file to accomplish this, novices may find this task somewhat daunting. +Included in all official distributions of NetHack is a file called +\fBNHAccess.nh\fP. Replacing \fBdefaults.nh\fP with this file will cause +the game to run in a manner accessible to the blind. After you have gained +some experience with the game and with editing files, you may want to alter +settings to better suit your preferences. Instructions on how to do this +are included in the \fBNHAccess.nh\fP file itself. The most crucial settings to +make the game accessible are: +.pg +.lp IBMgraphics +Disable IBMgraphics by commenting out this option. +.lp menustyle:traditional +This will assist in the interface to speech synthesizers. +.lp number_pad +A lot of speech access programs use the number-pad to review the screen. +If this is the case, disable the number_pad option and use the traditional +Rogue-like commands. +.lp "Character graphics" +Comment out all character graphics sets found near the bottom of the +\fBdefaults.nh\fP file. Most of these replace \fBNetHack\fP's +default representation of the dungeon using standard ASCII characters +with fancier characters from extended character sets, and these fancier +characters can annoy screen-readers. +.hn 1 +Scoring +.pg +NetHack maintains a list of the top scores or scorers on your machine, +depending on how it is set up. In the latter case, each account on +the machine can post only one non-winning score on this list. If +you score higher than someone else on this list, or better your +previous score, you will be inserted in the proper place under your +current name. How many scores are kept can also be set up when +NetHack is compiled. +.pg +Your score is chiefly based upon how much experience you gained, how +much loot you accumulated, how deep you explored, and how the game +ended. If you quit the game, you escape with all of your gold intact. +If, however, you get killed in the Mazes of Menace, the guild will +only hear about 90% of your gold when your corpse is discovered +(adventurers have been known to collect finder's fees). So, consider +whether you want to take one last hit at that monster and possibly +live, or quit and stop with whatever you have. If you quit, you keep +all your gold, but if you swing and live, you might find more. +.pg +If you just want to see what the current top players/games list is, you +can type \fBnethack -s all\fP on most versions. + +.hn 1 +Explore mode +.pg +NetHack is an intricate and difficult game. Novices might falter +in fear, aware of their ignorance of the means to survive. Well, fear +not. Your dungeon may come equipped with an ``explore'' or ``discovery'' +mode that enables you to keep old save files and cheat death, at the +paltry cost of not getting on the high score list. +.pg +There are two ways of enabling explore mode. One is to start the game +with the +.op -X +switch. The other is to issue the `X' command while already playing +the game. The other benefits of explore mode are left for the trepid +reader to discover. + +.hn +Credits +.pg +The original \fIhack\fP game was modeled on the Berkeley +.ux +\fIrogue\fP game. Large portions of this paper were shamelessly +cribbed from \fIA Guide to the Dungeons of Doom\fP, by Michael C. Toy +and Kenneth C. R. C. Arnold. Small portions were adapted from +\fIFurther Exploration of the Dungeons of Doom\fP, by Ken Arromdee. +.pg +NetHack is the product of literally dozens of people's work. +Main events in the course of the game development are described below: + +.pg +\fBJay Fenlason\fP wrote the original Hack, with help from +\fBKenny Woodland\fP, \fBMike Thome\fP and \fBJon Payne\fP. +.pg +\fBAndries Brouwer\fP did a major re-write, transforming Hack into a +very different game, and published (at least) three versions (1.0.1, +1.0.2, and 1.0.3) for +.ux +machines to the Usenet. +.pg +\fBDon G. Kneller\fP ported Hack 1.0.3 to Microsoft C and MS-DOS, producing PC +HACK 1.01e, added support for DEC Rainbow graphics in version 1.03g, and went +on to produce at least four more versions (3.0, 3.2, 3.51, and 3.6). +.pg +\fBR. Black\fP ported PC HACK 3.51 to Lattice C and the Atari 520/1040ST, +producing ST Hack 1.03. +.pg +\fBMike Stephenson\fP merged these various versions back together, +incorporating many of the added features, and produced NetHack 1.4. +He then coordinated a cast of thousands in enhancing and debugging +NetHack 1.4 and released NetHack versions 2.2 and 2.3. +.pg +Later, Mike coordinated a major rewrite of the game, heading a +team which included \fBKen Arromdee\fP, \fBJean-Christophe Collet\fP, \fBSteve +Creps\fP, \fBEric Hendrickson\fP, \fBIzchak Miller\fP, \fBJohn Rupley\fP, +\fBMike Threepoint\fP, and \fBJanet Walz\fP, to produce NetHack 3.0c. +.pg +NetHack 3.0 was ported to the Atari by \fBEric R. Smith\fP, to OS/2 by +\fBTimo Hakulinen\fP, and to VMS by \fBDavid Gentzel\fP. The three of them +and \fBKevin Darcy\fP later joined the main development team to produce +subsequent revisions of 3.0. +.pg +\fBOlaf Seibert\fP ported NetHack 2.3 and 3.0 to the Amiga. +\fBNorm Meluch\fP, \fBStephen Spackman\fP and \fBPierre Martineau\fP designed +overlay code for PC NetHack 3.0. \fBJohnny Lee\fP ported +NetHack 3.0 to the Macintosh. Along with various other Dungeoneers, they +continued to enhance the PC, Macintosh, and Amiga ports through the later +revisions of 3.0. +.pg +Headed by \fBMike Stephenson\fP and coordinated by \fBIzchak Miller\fP and +\fBJanet Walz\fP, the development team which now included \fBKen Arromdee\fP, +\fBDavid Cohrs\fP, \fBJean-Christophe Collet\fP, \fBKevin Darcy\fP, +\fBMatt Day\fP, \fBTimo Hakulinen\fP, \fBSteve Linhart\fP, \fBDean Luick\fP, +\fBPat Rankin\fP, \fBEric Raymond\fP, and \fBEric Smith\fP undertook a radical +revision of 3.0. They re-structured the game's design, and re-wrote major +parts of the code. They added multiple dungeons, a new display, special +individual character quests, a new endgame and many other new features, and +produced NetHack 3.1. +.pg +\fBKen Lorber\fP, \fBGregg Wonderly\fP and \fBGreg Olson\fP, with help +from \fBRichard Addison\fP, \fBMike Passaretti\fP, and \fBOlaf Seibert\fP, +developed NetHack 3.1 for the Amiga. +.pg +\fBNorm Meluch\fP and \fBKevin Smolkowski\fP, with help from +\fBCarl Schelin\fP, \fBStephen Spackman\fP, \fBSteve VanDevender\fP, +and \fBPaul Winner\fP, ported NetHack 3.1 to the PC. +.pg +\fBJon W{tte\fP and \fBHao-yang Wang\fP, with help from \fBRoss Brown\fP, +\fBMike Engber\fP, \fBDavid Hairston\fP, \fBMichael Hamel\fP, +\fBJonathan Handler\fP, \fBJohnny Lee\fP, \fBTim Lennan\fP, \fBRob Menke\fP, +and \fBAndy Swanson\fP, developed NetHack 3.1 for the Macintosh, +porting it for MPW. Building on their development, \fBBarton House\fP +added a Think C port. +.pg +\fBTimo Hakulinen\fP ported NetHack 3.1 to OS/2. \fBEric Smith\fP +ported NetHack 3.1 to the Atari. \fBPat Rankin\fP, with help from +\fBJoshua Delahunty\fP, was responsible for the VMS version of NetHack 3.1. +\fBMichael Allison\fP ported NetHack 3.1 to Windows NT. +.pg +\fBDean Luick\fP, with help from \fBDavid Cohrs\fP, developed NetHack +3.1 for X11. +\fBWarwick Allison\fP wrote a tiled version of NetHack for the Atari; +he later contributed the tiles to the DevTeam and tile support was +then added to other platforms. +.pg +The 3.2 development team, comprised of \fBMichael Allison\fP, \fBKen +Arromdee\fP, \fBDavid Cohrs\fP, \fBJessie Collet\fP, \fBSteve Creps\fP, +\fBKevin Darcy\fP, \fBTimo Hakulinen\fP, \fBSteve Linhart\fP, \fBDean +Luick\fP, \fBPat Rankin\fP, \fBEric Smith\fP, \fBMike Stephenson\fP, +\fBJanet Walz\fP, and \fBPaul Winner\fP, released version 3.2 in April of +1996. +.pg +Version 3.2 marked the tenth anniversary of the formation of the development +team. In a testament to their dedication to the game, all thirteen members +of the original development team remained on the team at the start of work +on that release. During the interval between the release of 3.1.3 +and 3.2, one of the founding members of the development team, \fBDr. Izchak +Miller\fP, was diagnosed with cancer and passed away. That release of the +game was dedicated to him by the development and porting teams. +.pg +During the lifespan of NetHack 3.1 and 3.2, several enthusiasts +of the game added +their own modifications to the game and made these ``variants'' publicly +available: +.pg +\fBTom Proudfoot\fP and \fBYuval Oren\fP created NetHack++, +which was quickly renamed NetHack--. +Working independently, \fBStephen White\fP wrote NetHack Plus. +\fBTom Proudfoot\fP later merged NetHack Plus +and his own NetHack-- to produce SLASH. +\fBLarry Stewart-Zerba\fP and \fBWarwick Allison\fP improved the spell +casting system with the Wizard Patch. +\fBWarwick Allison\fP also ported NetHack to use the Qt interface. +.pg +\fBWarren Cheung\fP combined SLASH with the Wizard Patch to produce Slash'em, +and with the help of \fBKevin Hugo\fP, added more features. +Kevin later joined the +DevTeam and incorporated the best of these ideas in NetHack 3.3. +.pg +The final update to 3.2 was the bug fix release 3.2.3, which was released +simultaneously with 3.3.0 in December 1999 just in time for the Year 2000. +.pg +The 3.3 development team, consisting of \fBMichael Allison\fP, \fBKen Arromdee\fP, +\fBDavid Cohrs\fP, \fBJessie Collet\fP, \fBSteve Creps\fP, \fBKevin Darcy\fP, +\fBTimo Hakulinen\fP, \fBKevin Hugo\fP, \fBSteve Linhart\fP, \fBKen Lorber\fP, +\fBDean Luick\fP, \fBPat Rankin\fP, \fBEric Smith\fP, \fBMike Stephenson\fP, +\fBJanet Walz\fP, and \fBPaul Winner\fP, released 3.3.0 in +December 1999 and 3.3.1 in August of 2000. +.pg +Version 3.3 offered many firsts. It was the first version to separate race +and profession. The Elf class was removed in preference to an elf race, +and the races of dwarves, gnomes, and orcs made their first appearance in +the game alongside the familiar human race. Monk and Ranger roles joined +Archeologists, Barbarians, Cavemen, Healers, Knights, Priests, Rogues, Samurai, +Tourists, Valkyries and of course, Wizards. It was also the first version +to allow you to ride a steed, and was the first version to have a publicly +available web-site listing all the bugs that had been discovered. Despite +that constantly growing bug list, 3.3 proved stable enough to last for +more than a year and a half. +.pg +The 3.4 development team initially consisted of +\fBMichael Allison\fP, \fBKen Arromdee\fP, +\fBDavid Cohrs\fP, \fBJessie Collet\fP, \fBKevin Hugo\fP, \fBKen Lorber\fP, +\fBDean Luick\fP, \fBPat Rankin\fP, \fBMike Stephenson\fP, +\fBJanet Walz\fP, and \fBPaul Winner\fP, with \fB Warwick Allison\fP joining +just before the release of NetHack 3.4.0 in March 2002. +.pg +As with version 3.3, various people contributed to the game as a whole as +well as supporting ports on the different platforms that NetHack runs on: +.pg +\fBPat Rankin\fP maintained 3.4 for VMS. +.pg +\fBMichael Allison\fP maintained NetHack 3.4 for the MS-DOS platform. \fBPaul Winner\fP +and \fBYitzhak Sapir\fP provided encouragement. +.pg +\fBDean Luick\fP, \fBMark Modrall\fP, and \fBKevin Hugo\fP maintained and enhanced the +Macintosh port of 3.4. +.pg +\fBMichael Allison\fP, \fBDavid Cohrs\fP, \fBAlex Kompel\fP, \fBDion Nicolaas\fP, and +\fBYitzhak Sapir\fP maintained and enhanced 3.4 for the Microsoft Windows platform. +\fBAlex Kompel\fP contributed a new graphical interface for the Windows port. +\fBAlex Kompel\fP also contributed a Windows CE port for 3.4.1. +.pg +\fBRon Van Iwaarden\fP maintained 3.4 for OS/2. +.pg +\fBJanne Salmijarvi\fP and \fBTeemu Suikki\fP maintained and +enhanced the Amiga port of 3.4 after \fBJanne Salmijarvi\fP resurrected +it for 3.3.1. +.pg +\fBChristian ``Marvin'' Bressler\fP maintained 3.4 for the Atari after he +resurrected it for 3.3.1. +.pg +There is a NetHack web site maintained by \fBKen Lorber\fP at http://www.nethack.org/. +.pg + - - - - - - - - - - +.pg +From time to time, some depraved individual out there in netland sends a +particularly intriguing modification to help out with the game. The Gods of +the Dungeon sometimes make note of the names of the worst of these miscreants +in this, the list of Dungeoneers: + +.sd +.TS S +center; +c c c. +.\"TABLE_START +Adam Aronow Izchak Miller Mike Stephenson +Alex Kompel J. Ali Harlow Norm Meluch +Andreas Dorn Janet Walz Olaf Seibert +Andy Church Janne Salmijarvi Pasi Kallinen +Andy Swanson Jean-Christophe Collet Pat Rankin +Ari Huttunen Jochen Erwied Paul Winner +Barton House John Kallen Pierre Martineau +Benson I. Margulies John Rupley Ralf Brown +Bill Dyer John S. Bien Ray Chason +Boudewijn Waijers Johnny Lee Richard Addison +Bruce Cox Jon W{tte Richard Beigel +Bruce Holloway Jonathan Handler Richard P. Hughey +Bruce Mewborne Joshua Delahunty Rob Menke +Carl Schelin Keizo Yamamoto Robin Johnson +Chris Russo Ken Arnold Roderick Schertler +David Cohrs Ken Arromdee Roland McGrath +David Damerell Ken Lorber Ron Van Iwaarden +David Gentzel Ken Washikita Ronnen Miller +David Hairston Kevin Darcy Ross Brown +Dean Luick Kevin Hugo Sascha Wostmann +Del Lamb Kevin Sitze Scott Bigham +Deron Meranda Kevin Smolkowski Scott R. Turner +Dion Nicolaas Kevin Sweet Stephen Spackman +Dylan O'Donnell Lars Huttar Stephen White +Eric Backus Malcolm Ryan Steve Creps +Eric Hendrickson Mark Gooderum Steve Linhart +Eric R. Smith Mark Modrall Steve VanDevender +Eric S. Raymond Marvin Bressler Teemu Suikki +Erik Andersen Matthew Day Tim Lennan +Frederick Roeber Merlyn LeRoy Timo Hakulinen +Gil Neiger Michael Allison Tom Almy +Greg Laskin Michael Feir Tom West +Greg Olson Michael Hamel Warren Cheung +Gregg Wonderly Michael Sokolov Warwick Allison +Hao-yang Wang Mike Engber Yitzhak Sapir +Helge Hafting Mike Gallop +Irina Rempt-Drijfhout Mike Passaretti +.\"TABLE_END Do not delete this line. +.TE +.ed + +.\"Microsoft and MS-DOS are registered trademarks of Microsoft Corporation. +.\"Lattice is a trademark of Lattice, Inc. +.\"Atari and 1040ST are trademarks of Atari, Inc. +.\"AMIGA is a trademark of Commodore-Amiga, Inc. +.sm "Brand and product names are trademarks or registered trademarks \ +of their respective holders." diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex new file mode 100644 index 0000000..f76398f --- /dev/null +++ b/doc/Guidebook.tex @@ -0,0 +1,3395 @@ +\documentstyle[titlepage]{article} + +\textheight 220mm +\textwidth 160mm +\oddsidemargin 0mm +\evensidemargin 0mm +\topmargin 0mm + +\newcommand{\nd}{\noindent} + +\newcommand{\tb}[1]{\tt #1 \hfill} +\newcommand{\bb}[1]{\bf #1 \hfill} +\newcommand{\ib}[1]{\it #1 \hfill} + +\newcommand{\blist}[1] +{\begin{list}{$\bullet$} + {\leftmargin 30mm \topsep 2mm \partopsep 0mm \parsep 0mm \itemsep 1mm + \labelwidth 28mm \labelsep 2mm + #1}} + +\newcommand{\elist}{\end{list}} + +% this will make \tt underscores look better, but requires that +% math subscripts will never be used in this document +\catcode`\_=12 + +\begin{document} +% +% input file: guidebook.mn +% $Revision: 1.61.2.19 $ $Date: 2003/12/03 03:00:50 $ +% +%.ds h0 " +%.ds h1 %.ds h2 \% +%.ds f0 " + +%.mt +\title{\LARGE A Guide to the Mazes of Menace:\\ +\Large Guidebook for {\it NetHack\/}} + +%.au +\author{Eric S. Raymond\\ +(Extensively edited and expanded for 3.4)} +\date{December 2, 2003} + +\maketitle + +%.hn 1 +\section{Introduction} + +%.pg + +Recently, you have begun to find yourself unfulfilled and distant +in your daily occupation. Strange dreams of prospecting, stealing, +crusading, and combat have haunted you in your sleep for many months, +but you aren't sure of the reason. You wonder whether you have in +fact been having those dreams all your life, and somehow managed to +forget about them until now. Some nights you awaken suddenly +and cry out, terrified at the vivid recollection of the strange and +powerful creatures that seem to be lurking behind every corner of the +dungeon in your dream. Could these details haunting your dreams be real? +As each night passes, you feel the desire to enter the mysterious caverns +near the ruins grow stronger. Each morning, however, you quickly put +the idea out of your head as you recall the tales of those who entered +the caverns before you and did not return. Eventually you can resist +the yearning to seek out the fantastic place in your dreams no longer. +After all, when other adventurers came back this way after spending time +in the caverns, they usually seemed better off than when they passed +through the first time. And who was to say that all of those who did +not return had not just kept going? +%.pg + +Asking around, you hear about a bauble, called the Amulet of Yendor by some, +which, if you can find it, will bring you great wealth. One legend you were +told even mentioned that the one who finds the amulet will be granted +immortality by the gods. The amulet is rumored to be somewhere beyond the +Valley of Gehennom, deep within the Mazes of Menace. Upon hearing the +legends, you immediately realize that there is some profound and +undiscovered reason that you are to descend into the caverns and seek +out that amulet of which they spoke. Even if the rumors of the amulet's +powers are untrue, you decide that you should at least be able to sell the +tales of your adventures to the local minstrels for a tidy sum, especially +if you encounter any of the terrifying and magical creatures of +your dreams along the way. You spend one last night fortifying yourself +at the local inn, becoming more and more depressed as you watch the odds +of your success being posted on the inn's walls getting lower and lower. + +%.pg +\nd In the morning you awake, collect your belongings, and +set off for the dungeon. After several days of uneventful +travel, you see the ancient ruins that mark the entrance to the +Mazes of Menace. It is late at night, so you make camp at the entrance +and spend the night sleeping under the open skies. In the morning, you +gather your gear, eat what may be your last meal outside, and enter the +dungeon\ldots + +%.hn 1 +\section{What is going on here?} + +%.pg +You have just begun a game of {\it NetHack}. Your goal is to grab as much +treasure as you can, retrieve the Amulet of Yendor, and escape the +Mazes of Menace alive. + +%.pg +Your abilities and strengths for dealing with the hazards of adventure +will vary with your background and training: + +%.pg +% +\blist{} +\item[\bb{Archeologists}]% +understand dungeons pretty well; this enables them +to move quickly and sneak up on the local nasties. They start equipped +with the tools for a proper scientific expedition. +%.pg +% +\item[\bb{Barbarians}]% +are warriors out of the hinterland, hardened to battle. +They begin their quests with naught but uncommon strength, a trusty hauberk, +and a great two-handed sword. +%.pg +% +\item[\bb{Cavemen {\rm and} Cavewomen}] +start with exceptional strength, but unfortunately, neolithic weapons. +%.pg +% +\item[\bb{Healers}]% +are wise in medicine and apothecary. They know the +herbs and simples that can restore vitality, ease pain, anesthetize, +and neutralize +poisons; and with their instruments, they can divine a being's state +of health or sickness. Their medical practice earns them quite reasonable +amounts of money, with which they enter the dungeon. +%.pg +% +\item[\bb{Knights}]% +are distinguished from the common skirmisher by their +devotion to the ideals of chivalry and by the surpassing excellence of +their armor. +%.pg +% +\item[\bb{Monks}]% +are ascetics, who by rigorous practice of physical and mental +disciplines have become capable of fighting as effectively without weapons +as with. They wear no armor but make up for it with increased mobility. +%.pg +% +\item[\bb{Priests {\rm and} Priestesses}]% +are clerics militant, crusaders +advancing the cause of righteousness with arms, armor, and arts +thaumaturgic. Their ability to commune with deities via prayer +occasionally extricates them from peril, but can also put them in it. +%.pg +% +\item[\bb{Rangers}]% +are most at home in the woods, and some say slightly out +of place in a dungeon. They are, however, experts in archery as well +as tracking and stealthy movement. +%.pg +% +\item[\bb{Rogues}]% +are agile and stealthy thieves, with knowledge of locks, +traps, and poisons. Their advantage lies in surprise, which they employ +to great advantage. +%.pg +% +\item[\bb{Samurai}]% +are the elite warriors of feudal Nippon. They are lightly +armored and quick, and wear the % +{\it dai-sho}, two swords of the deadliest +keenness. +%.pg +% +\item[\bb{Tourists}]% +start out with lots of gold (suitable for shopping with), +a credit card, lots of food, some maps, and an expensive camera. Most +monsters don't like being photographed. +%.pg +% +\item[\bb{Valkyries}]% +are hardy warrior women. Their upbringing in the harsh +Northlands makes them strong, inures them to extremes of cold, and instills +in them stealth and cunning. +%.pg +% +\item[\bb{Wizards}]% +start out with a knowledge of magic, a selection of magical +items, and a particular affinity for dweomercraft. Although seemingly weak +and easy to overcome at first sight, an experienced Wizard is a deadly foe. +\elist + +%.pg +You may also choose the race of your character: + +%.pg +% +\blist{} +\item[\bb{Dwarves}]% +are smaller than humans or elves, but are stocky and solid +individuals. Dwarves' most notable trait is their great expertise in mining +and metalwork. Dwarvish armor is said to be second in quality not even to the +mithril armor of the Elves. +%.pg +% +\item[\bb{Elves}]% +are agile, quick, and perceptive; very little of what goes +on will escape an Elf. The quality of Elven craftsmanship often gives +them an advantage in arms and armor. +%.pg +% +\item[\bb{Gnomes}]% +are smaller than but generally similar to dwarves. Gnomes are +known to be expert miners, and it is known that a secret underground mine +complex built by this race exists within the Mazes of Menace, filled with +both riches and danger. +%.pg +% +\item[\bb{Humans}]% +are by far the most common race of the surface world, and +are thus the norm by which other races are often compared. Although +they have no special abilities, they can succeed in any role. +%.pg +% +\item[\bb{Orcs}]% +are a cruel and barbaric race that hate every living thing +(including other orcs). Above all others, Orcs hate Elves with a passion +unequalled, and will go out of their way to kill one at any opportunity. +The armor and weapons fashioned by the Orcs are typically of inferior quality. +\elist + +%.hn 1 +\section{What do all those things on the screen mean?} +%.pg +On the screen is kept a map of where you have been and what you have +seen on the current dungeon level; as you explore more of the level, +it appears on the screen in front of you. + +%.pg +When {\it NetHack\/}'s ancestor {\it rogue\/} first appeared, its screen +orientation was almost unique among computer fantasy games. Since +then, screen orientation has become the norm rather than the +exception; {\it NetHack\/} continues this fine tradition. Unlike text +adventure games that accept commands in pseudo-English sentences and +explain the results in words, {\it NetHack\/} commands are all one or two +keystrokes and the results are displayed graphically on the screen. A +minimum screen size of 24 lines by 80 columns is recommended; if the +screen is larger, only a $21\times80$ section will be used for the map. + +%.pg +{\it NetHack\/} can even be played by blind players, with the assistance of +Braille readers or speech synthesisers. Instructions for configuring +{\it NetHack\/} for the blind are included later in this document. + +%.pg +{\it NetHack\/} generates a new dungeon every time you play it; even the +authors still find it an entertaining and exciting game despite +having won several times. + +%.pg +{\it NetHack\/} offers a variety of display options. The options available to +you will vary from port to port, depending on the capabilities of your +hardware and software, and whether various compile-time options were +enabled when your executable was created. The three possible display +options are: a monochrome character interface, a color character interface, +and a graphical interface using small pictures called tiles. The two +character interfaces allow fonts with other characters to be substituted, +but the default assignments use standard ASCII characters to represent +everything. There is no difference between the various display options +with respect to game play. Because we cannot reproduce the tiles or +colors in the Guidebook, and because it is common to all ports, we will +use the default ASCII characters from the monochrome character display +when referring to things you might see on the screen during your game. +%.pg +In order to understand what is going on in {\it NetHack}, first you must +understand what {\it NetHack\/} is doing with the screen. The {\it NetHack\/} +screen replaces the ``You see \ldots'' descriptions of text adventure games. +Figure 1 is a sample of what a {\it NetHack\/} screen might look like. +The way the screen looks for you depends on your platform. + +\vbox{ +\begin{verbatim} + The bat bites! + + ------ + |....| ---------- + |.<..|####...@...$.| + |....-# |...B....+ + |....| |.d......| + ------ -------|-- + + + + Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral + Dlvl:1 $:0 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:257 Weak +\end{verbatim} +\begin{center} +Figure 1 +\end{center} +} + +%.hn 2 +\subsection*{The status lines (bottom)} + +%.pg +The bottom two lines of the screen contain several cryptic pieces of +information describing your current status. If either status line +becomes longer than the width of the screen, you might not see all of +it. Here are explanations of what the various status items mean +(though your configuration may not have all the status items listed +below): + +%.lp +\blist{} +\item[\bb{Rank}] +Your character's name and professional ranking (based on the +experience level, see below). +%.lp +\item[\bb{Strength}] +A measure of your character's strength; one of your six basic +attributes. A human character's attributes can range from 3 to 18 inclusive; +non-humans may exceed these limits +(occasionally you may get super-strengths of the form 18/xx, and magic can +also cause attributes to exceed the normal limits). The +higher your strength, the stronger you are. Strength affects how +successfully you perform physical tasks, how much damage you do in +combat, and how much loot you can carry. +%.lp +\item[\bb{Dexterity}] +Dexterity affects your chances to hit in combat, to avoid traps, and +do other tasks requiring agility or manipulation of objects. +%.lp +\item[\bb{Constitution}] +Constitution affects your ability to recover from injuries and other +strains on your stamina. +%.lp +\item[\bb{Intelligence}] +Intelligence affects your ability to cast spells and read spellbooks. +%.lp +\item[\bb{Wisdom}] +Wisdom comes from your practical experience (especially when dealing with +magic). It affects your magical energy. +%.lp +\item[\bb{Charisma}] +Charisma affects how certain creatures react toward you. In +particular, it can affect the prices shopkeepers offer you. +%.lp +\item[\bb{Alignment}] +% +{\it Lawful}, {\it Neutral\/} or {\it Chaotic}. Often, Lawful is +taken as good and Chaotic is evil, but legal and ethical do not always +coincide. Your alignment influences how other +monsters react toward you. Monsters of a like alignment are more likely +to be non-aggressive, while those of an opposing alignment are more likely +to be seriously offended at your presence. +%.lp +\item[\bb{Dungeon Level}] +How deep you are in the dungeon. You start at level one and the number +increases as you go deeper into the dungeon. Some levels are special, +and are identified by a name and not a number. The Amulet of Yendor is +reputed to be somewhere beneath the twentieth level. +%.lp +\item[\bb{Gold}] +The number of gold pieces you are openly carrying. Gold which you have +concealed in containers is not counted. +%.lp +\item[\bb{Hit Points}] +Your current and maximum hit points. Hit points indicate how much +damage you can take before you die. The more you get hit in a fight, +the lower they get. You can regain hit points by resting, or by using +certain magical items or spells. The number in parentheses is the maximum +number your hit points can reach. +%.lp +\item[\bb{Power}] +Spell points. This tells you how much mystic energy ({\it mana\/}) +you have available for spell casting. Again, resting will regenerate the +amount available. +%.lp +\item[\bb{Armor Class}] +A measure of how effectively your armor stops blows from unfriendly +creatures. The lower this number is, the more effective the armor; it +is quite possible to have negative armor class. +%.lp +\item[\bb{Experience}] +Your current experience level and experience points. As you +adventure, you gain experience points. At certain experience point +totals, you gain an experience level. The more experienced you are, +the better you fight and withstand magical attacks. Many dungeons +show only your experience level here. +%.lp +\item[\bb{Time}] +The number of turns elapsed so far, displayed if you have the +{\it time\/} option set. +%.lp +\item[\bb{Hunger Status}] +Your current hunger status, ranging from % +{\it Satiated\/} down to {\it Fainting}. If your hunger status is normal, +it is not displayed. +%.pg +Additional status flags may appear after the hunger status: +{\it Conf\/} when you're confused, {\it FoodPois\/} or {\it Ill\/} +when sick, {\it Blind\/} +when you can't see, {\it Stun\/} when stunned, and {\it Hallu\/} when +hallucinating. +\elist + +%.hn 2 +\subsection*{The message line (top)} + +%.pg +The top line of the screen is reserved for messages that describe +things that are impossible to represent visually. If you see a +``{\tt --More--}'' on the top line, this means that {\it NetHack\/} has +another message to display on the screen, but it wants to make certain +that you've read the one that is there first. To read the next message, +just press the space bar. + +%.hn 2 +\subsection*{The map (rest of the screen)} + +%.pg +The rest of the screen is the map of the level as you have explored it +so far. Each symbol on the screen represents something. You can set +various graphics +options to change some of the symbols the game uses; otherwise, the +game will use default symbols. Here is a list of what the default +symbols mean: + +\blist{} +%.lp +\item[\tb{- {\rm and} |}] +The walls of a room, or an open door. Or a grave ({\tt |}). +%.lp +\item[\tb{.}] +The floor of a room, ice, or a doorless doorway. +%.lp +\item[\tb{\#}] +A corridor, or iron bars, or a tree, or possibly a kitchen sink (if +your dungeon has sinks), or a drawbridge. +%.lp +\item[\tb{>}] +Stairs down: a way to the next level. +%.lp +\item[\tb{<}] +Stairs up: a way to the previous level. +%.lp +\item[\tb{+}] +A closed door, or a spellbook containing a spell you may be able to learn. +%.lp +\item[\tb{@}] +Your character or a human. +%.lp +\item[\tb{\$}] +A pile of gold. +%.lp +\item[\tb{\^}] +A trap (once you have detected it). +%.lp +\item[\tb{)}] +A weapon. +%.lp +\item[\tb{[}] +A suit or piece of armor. +%.lp +\item[\tb{\%}] +Something edible (not necessarily healthy). +%.lp +\item[\tb{?}] +A scroll. +%.lp +\item[\tb{/}] +A wand. +%.lp +\item[\tb{=}] +A ring. +%.lp +\item[\tb{!}] +A potion. +%.lp +\item[\tb{(}] +A useful item (pick-axe, key, lamp \ldots). +%.lp +\item[\tb{"}] +An amulet or a spider web. +%.lp +\item[\tb{*}] +A gem or rock (possibly valuable, possibly worthless). +%.lp +\item[\tb{`}] +A boulder or statue. +%.lp +\item[\tb{0}] +An iron ball. +%.lp +\item[\tb{_}] +An altar, or an iron chain. +%.lp +\item[\tb{\{}] +A fountain. +%.lp +\item[\tb{\}}] +A pool of water or moat or a pool of lava. +%.lp +\item[\tb{$\backslash$}] +An opulent throne. +%.lp +\item[\tb{a-zA-Z {\rm \& other symbols}}] +Letters and certain other symbols represent the various inhabitants +of the Mazes of Menace. Watch out, they can be nasty and vicious. +Sometimes, however, they can be helpful. +%.lp +\item[\tb{I}] +This marks the last known location of an invisible or otherwise unseen +monster. Note that the monster could have moved. The `F' and `m' commands +may be useful here. + +\elist +%.pg +You need not memorize all these symbols; you can ask the game what any +symbol represents with the `{\tt /}' command (see the next section for +more info). + +%.hn 1 +\section{Commands} + +%.pg +Commands are initiated by typing one or two characters. Some commands, +like ``{\tt search}'', do not require that any more information be collected +by {\it NetHack\/}. Other commands might require additional information, for +example a direction, or an object to be used. For those commands that +require additional information, {\it NetHack\/} will present you with either +a menu of choices, or with a command line prompt requesting information. Which +you are presented with will depend chiefly on how you have set the +`{\it menustyle\/}' +option. + +%.pg +For example, a common question in the form ``{\tt What do you want to +use? [a-zA-Z\ ?*]}'', asks you to choose an object you are carrying. +Here, ``{\tt a-zA-Z}'' are the inventory letters of your possible choices. +Typing `{\tt ?}' gives you an inventory list of these items, so you can see +what each letter refers to. In this example, there is also a `{\tt *}' +indicating that you may choose an object not on the list, if you +wanted to use something unexpected. Typing a `{\tt *}' lists your entire +inventory, so you can see the inventory letters of every object you're +carrying. Finally, if you change your mind and decide you don't want +to do this command after all, you can press the `ESC' key to abort the +command. + +%.pg +You can put a number before some commands to repeat them that many +times; for example, ``{\tt 10s}'' will search ten times. If you have the +{\it number\_pad\/} +option set, you must type `{\tt n}' to prefix a count, so the example above +would be typed ``{\tt n10s}'' instead. Commands for which counts make no +sense ignore them. In addition, movement commands can be prefixed for +greater control (see below). To cancel a count or a prefix, press the +`ESC' key. + +%.pg +The list of commands is rather long, but it can be read at any time +during the game through the `{\tt ?}' command, which accesses a menu of +helpful texts. Here are the commands for your reference: + +\blist{} +%.lp +\item[\tb{?}] +Help menu: display one of several help texts available. +%.lp +\item[\tb{/}] +Tell what a symbol represents. You may choose to specify a location +or type a symbol (or even a whole word) to explain. +Specifying a location is done by moving the cursor to a particular spot +on the map and then pressing one of `{\tt .}', `{\tt ,}', `{\tt ;}', +or `{\tt :}'. `{\tt .}' will explain the symbol at the chosen location, +conditionally check for ``{\tt More info?}'' depending upon whether the +{\it help\/} +option is on, and then you will be asked to pick another location; +`{\tt ,}' will explain the symbol but skip any additional +information; `{\tt ;}' will skip additional info and also not bother asking +you to choose another location to examine; `{\tt :}' will show additional +info, if any, without asking for confirmation. When picking a location, +pressing the {\tt ESC} key will terminate this command, or pressing `{\tt ?}' +will give a brief reminder about how it works. + +%.pg +Specifying a name rather than a location +always gives any additional information available about that name. +%.lp +\item[\tb{\&}] +Tell what a command does. +%.lp +\item[\tb{<}] +Go up to the previous level (if you are on a staircase or ladder). +%.lp +\item[\tb{>}] +Go down to the next level (if you are on a staircase or ladder). +%.lp +\item[\tb{[yuhjklbn]}] +Go one step in the direction indicated (see Figure 2). If you sense +or remember +a monster there, you will fight the monster instead. Only these +one-step movement commands cause you to fight monsters; the others +(below) are ``safe.'' +%.sd +\begin{center} +\begin{tabular}{cc} +\verb+ y k u + & \verb+ 7 8 9 +\\ +\verb+ \ | / + & \verb+ \ | / +\\ +\verb+ h- . -l + & \verb+ 4- . -6 +\\ +\verb+ / | \ + & \verb+ / | \ +\\ +\verb+ b j n + & \verb+ 1 2 3 +\\ + & (if {\it number\_pad\/} set) +\end{tabular} +\end{center} +%.ed +\begin{center} +Figure 2 +\end{center} +%.lp +\item[\tb{[YUHJKLBN]}] +Go in that direction until you hit a wall or run into something. +%.lp +\item[\tb{m[yuhjklbn]}] +Prefix: move without picking up objects or fighting (even if you remember +a monster there) +%.lp +\item[\tb{F[yuhjklbn]}] +Prefix: fight a monster (even if you only guess one is there) +%.lp +\item[\tb{M[yuhjklbn]}] +Prefix: Move far, no pickup. +%.lp +\item[\tb{g[yuhjklbn]}] +Prefix: Move until something interesting is found. +%.lp +\item[\tb{G[yuhjklbn] {\rm or} [yuhjklbn]}] +Prefix: Same as `{\tt g}', but forking of corridors is not considered +interesting. +%.lp +\item[\tb{_}] +Travel to a map location via a shortest-path algorithm. The shortest path +is computed over map locations the hero knows about (e.g. seen or +previously traversed). If there is no known path, a guess is made instead. +Stops on most of +the same conditions as the `G' command, but without picking up +objects, similar to the `M' command. For ports with mouse +support, the command is also invoked when a mouse-click takes place on a +location other than the current position. +%.lp +\item[\tb{.}] +Rest, do nothing for one turn. +%.lp +\item[\tb{a}] +Apply (use) a tool (pick-axe, key, lamp \ldots). +%.lp +\item[\tb{A}] +Remove one or more worn items, such as armor. +Use `{\tt T}' (take off) to take off only one piece of armor +or `{\tt R}' (remove) to take off only one accessory. +%.lp +\item[\tb{\^{}A}] +Redo the previous command. +%.lp +\item[\tb{c}] +Close a door. +%.lp +\item[\tb{C}] +Call (name) an individual monster. +%.lp +\item[\tb{\^{}C}] +Panic button. Quit the game. +%.lp +\item[\tb{d}] +Drop something.\\ +{\tt d7a} --- drop seven items of object +{\it a}. +%.lp +\item[\tb{D}] +Drop several things. In answer to the question +``{\tt What kinds of things do you want to drop? [!\%= BUCXaium]}'' +you should type zero or more object symbols possibly followed by +`{\tt a}' and/or `{\tt i}' and/or `{\tt u}' and/or `{\tt m}'. +In addition, one or more of +the blessed/uncursed/cursed groups may be typed.\\ +%.sd +%.si +{\tt DB} --- drop all objects known to be blessed.\\ +{\tt DU} --- drop all objects known to be uncursed.\\ +{\tt DC} --- drop all objects known to be cursed.\\ +{\tt DX} --- drop all objects of unknown B/U/C status.\\ +{\tt Da} --- drop all objects, without asking for confirmation.\\ +{\tt Di} --- examine your inventory before dropping anything.\\ +{\tt Du} --- drop only unpaid objects (when in a shop).\\ +{\tt Dm} --- use a menu to pick which object(s) to drop.\\ +{\tt D\%u} --- drop only unpaid food. +%.ei +%.ed +%.lp +\item[\tb{\^{}D}] +Kick something (usually a door). +%.lp +\item[\tb{e}] +Eat food. +%.lp +% Make sure Elbereth is not hyphenated below, the exact spelling matters. +% (Only specified here to parallel Guidebook.mn; use of \tt font implicity +% prevents automatic hyphenation in TeX and LaTeX.) +\hyphenation{Elbereth} %override the deduced syllable breaks +\item[\tb{E}] +Engrave a message on the floor. +Engraving the word ``{\tt Elbereth}'' will cause most monsters to not attack +you hand-to-hand (but if you attack, you will rub it out); this is +often useful to give yourself a breather. (This feature may be compiled out +of the game, so your version might not have it.)\\ +%.sd +%.si +{\tt E-} --- write in the dust with your fingers. +%.ei +%.ed +%.Ip +\item[\tb{f}] +Fire one of the objects placed in your quiver. You may select +ammunition with a previous `{\tt Q}' command, or let the computer pick +something appropriate if {\it autoquiver\/} is true. +%.lp +\item[\tb{i}] +List your inventory (everything you're carrying). +%.lp +\item[\tb{I}] +List selected parts of your inventory.\\ +%.sd +%.si +{\tt I*} --- list all gems in inventory;\\ +{\tt Iu} --- list all unpaid items;\\ +{\tt Ix} --- list all used up items that are on your shopping bill;\\ +{\tt I\$} --- count your money. +%.ei +%.ed +%.lp +\item[\tb{o}] +Open a door. +%.lp +\item[\tb{O}] +Set options. A menu showing the current option values will be +displayed. You can change most values simply by selecting the menu +entry for the given option (ie, by typing its letter or clicking upon +it, depending on your user interface). For the non-boolean choices, +a further menu or prompt will appear once you've closed this menu. +The available options +are listed later in this Guidebook. Options are usually set before the +game rather than with the `{\tt O}' command; see the section on options below. +%.lp +\item[\tb{p}] +Pay your shopping bill. +%.lp +\item[\tb{P}] +Put on a ring or other accessory (amulet, blindfold). +%.lp +\item[\tb{\^{}P}] +Repeat previous message. Subsequent {\tt \^{}P}'s repeat earlier messages. +The behavior can be varied via the msg_window option. +%.lp +\item[\tb{q}] +Quaff (drink) something (potion, water, etc). +%.lp +\item[\tb{Q}] +Select an object for your quiver. You can then throw this using +the `f' command. (In versions prior to 3.3 this was the command to quit +the game, which has now been moved to `{\tt \#quit}'.) +%.lp +\item[\tb{r}] +Read a scroll or spellbook. +%.lp +\item[\tb{R}] +Remove an accessory (ring, amulet, etc). +%.lp +\item[\tb{\^{}R}] +Redraw the screen. +%.lp +\item[\tb{s}] +Search for secret doors and traps around you. It usually takes several +tries to find something. +%.lp +\item[\tb{S}] +Save (and suspend) the game. The game will be restored automatically the +next time you play. +%.lp +\item[\tb{t}] +Throw an object or shoot a projectile. +%.lp +\item[\tb{T}] +Take off armor. +%.lp +\item[\tb{\^{}T}] +Teleport, if you have the ability. +%.lp +\item[\tb{v}] +Display version number. +%.lp +\item[\tb{V}] +Display the game history. +%.lp +\item[\tb{w}] +Wield weapon.\\ +%.sd +%.si +{\tt w-} --- wield nothing, use your bare hands. +%.ei +%.ed +%.lp +\item[\tb{W}] +Wear armor. +%.lp +\item[\tb{x}] +Exchange your wielded weapon with the item in your alternate +weapon slot. The latter is used as your secondary weapon when engaging in +two-weapon combat. Note that if one of these slots is empty, +the exchange still takes place. +%.lp +\item[\tb{X}] +Enter explore (discovery) mode, explained in its own section later. +%.lp +\item[\tb{\^{}X}] +Display your name, role, race, gender, and alignment as well as +the various deities in your game. +%.lp +\item[\tb{z}] +Zap a wand. To aim at yourself, use `{\tt .}' for the direction. +%.lp +\item[\tb{Z}] +Zap (cast) a spell. To cast at yourself, use `{\tt .}' for the direction. +%.lp +\item[\tb{\^{}Z}] +Suspend the game (UNIX versions with job control only). +%.lp +\item[\tb{:}] +Look at what is here. +%.lp +\item[\tb{;}] +Show what type of thing a visible symbol corresponds to. +%.lp +\item[\tb{,}] +Pick up some things. May be preceded by `{\tt m}' to force a selection menu. +%.lp +\item[\tb{@}] +Toggle the {\it autopickup\/} option on and off. +%.lp +\item[\tb{\^{}}] +Ask for the type of a trap you found earlier. +%.lp +\item[\tb{)}] +Tell what weapon you are wielding. +%.lp +\item[\tb{[}] +Tell what armor you are wearing. +%.lp +\item[\tb{=}] +Tell what rings you are wearing. +%.lp +\item[\tb{"}] +Tell what amulet you are wearing. +%.lp +\item[\tb{(}] +Tell what tools you are using. +%.lp +\item[\tb{*}] +Tell what equipment you are using; combines the preceding five type-specific +commands into one. +%.lp +\item[\tb{\$}] +Count your gold pieces. +%.lp +\item[\tb{+}] +List the spells you know. Using this command, you can also rearrange +the order in which your spells are listed. They are shown via a menu, +and if you select a spell in that menu, you'll be re-prompted for +another spell to swap places with it, and then have opportunity to +make further exchanges. +%.lp +\item[\tb{$\backslash$}] +Show what types of objects have been discovered. +%.lp +\item[\tb{!}] +Escape to a shell. +%.lp +\item[\tb{\#}] +Perform an extended command. As you can see, the authors of {\it NetHack\/} +used up all the letters, so this is a way to introduce the less frequently +used commands. +What extended commands are available depends on what features +the game was compiled with. +%.lp +\item[\tb{\#adjust}] +Adjust inventory letters (most useful when the +{\it fixinv\/} +option is ``on''). +%.lp +\item[\tb{\#chat}] +Talk to someone. +%.lp +\item[\tb{\#conduct}] +List which challenges you have adhered to. See the section below entitled +``Conduct'' for details. +%.lp +\item[\tb{\#dip}] +Dip an object into something. +%.lp +\item[\tb{\#enhance}] +Advance or check weapons and spell skills. +%.lp +\item[\tb{\#force}] +Force a lock. +%.lp +\item[\tb{\#invoke}] +Invoke an object's special powers. +%.lp +\item[\tb{\#jump}] +Jump to another location. +%.lp +\item[\tb{\#loot}] +Loot a box or bag on the floor beneath you, or the saddle +from a horse standing next to you. +%.lp +\item[\tb{\#monster}] +Use a monster's special ability (when polymorphed into monster form). +%.lp +\item[\tb{\#name}] +Name an item or type of object. +%.lp +\item[\tb{\#offer}] +Offer a sacrifice to the gods. +%.lp +\item[\tb{\#pray}] +Pray to the gods for help. +%.lp +\item[\tb{\#quit}] +Quit the program without saving your game. +%.lp +\item[\tb{\#ride}] +Ride (or stop riding) a monster. +%.lp +\item[\tb{\#rub}] +Rub a lamp or a stone. +%.lp +\item[\tb{\#sit}] +Sit down. +%.lp +\item[\tb{\#turn}] +Turn undead. +%.lp +\item[\tb{\#twoweapon}] +Toggle two-weapon combat on or off. Note that you must +use suitable weapons for this type of combat, or it will +be automatically turned off. +%.lp +\item[\tb{\#untrap}] +Untrap something (trap, door, or chest). +%.lp +\item[\tb{\#version}] +Print compile time options for this version of {\it NetHack}. +%.lp +\item[\tb{\#wipe}] +Wipe off your face. +%.lp +\item[\tb{\#?}] +Help menu: get the list of available extended commands. +\elist + +%.pg +\nd If your keyboard has a meta key (which, when pressed in combination +with another key, modifies it by setting the `meta' [8th, or `high'] +bit), you can invoke many extended commands by meta-ing the first +letter of the command. +%- In {\it NT, OS/2, PC\/ {\rm and} ST NetHack}, +%- the `Alt' key can be used in this fashion; +%- on the Amiga set the {\it altmeta\/} option to get this behavior. +In {\it NT, OS/2, {\rm and} PC NetHack}, +the `Alt' key can be used in this fashion. +\blist{} +%.lp +\item[\tb{M-?}] +{\tt\#?} (not supported by all platforms) +%.lp +\item[\tb{M-2}] +{\tt\#twoweapon} (unless the {\it number\_pad\/} option is enabled) +%.lp +\item[\tb{M-a}] +{\tt\#adjust} +%.lp +\item[\tb{M-c}] +{\tt\#chat} +%.lp +\item[\tb{M-d}] +{\tt\#dip} +%.lp +\item[\tb{M-e}] +{\tt\#enhance} +%.lp +\item[\tb{M-f}] +{\tt\#force} +%.lp +\item[\tb{M-i}] +{\tt\#invoke} +%.lp +\item[\tb{M-j}] +{\tt\#jump} +%.lp +\item[\tb{M-l}] +{\tt\#loot} +%.lp +\item[\tb{M-m}] +{\tt\#monster} +%.lp +\item[\tb{M-n}] +{\tt\#name} +%.lp +\item[\tb{M-o}] +{\tt\#offer} +%.lp +\item[\tb{M-p}] +{\tt\#pray} +%.Ip +\item[\tb{M-q}] +{\tt\#quit} +%.lp +\item[\tb{M-r}] +{\tt\#rub} +%.lp +\item[\tb{M-s}] +{\tt\#sit} +%.lp +\item[\tb{M-t}] +{\tt\#turn} +%.lp +\item[\tb{M-u}] +{\tt\#untrap} +%.lp +\item[\tb{M-v}] +{\tt\#version} +%.lp +\item[\tb{M-w}] +{\tt\#wipe} +\elist + +%.pg +\nd If the {\it number\_pad\/} option is on, some additional letter commands +are available: +\blist{} +%.lp +\item[\tb{h}] +Help menu: display one of several help texts available, like ``{\tt ?}''. +%.lp +\item[\tb{j}] +Jump to another location. Same as ``{\tt \#jump}'' or ``{\tt M-j}''. +%.lp +\item[\tb{k}] +Kick something (usually a door). Same as `{\tt \^{}D}'. +%.lp +\item[\tb{l}] +Loot a box or bag on the floor beneath you, or the saddle +from a horse standing next to you. Same as ``{\tt \#loot}'' or ``{\tt M-l}''. +%.lp +\item[\tb{N}] +Name an object or type of object. Same as ``{\tt \#name}'' or ``{\tt M-n}''. +%.lp +\item[\tb{u}] +Untrap a trap, door, or chest. Same as ``{\tt \#untrap}'' or ``{\tt M-u}''. +\elist + +%.hn 1 +\section{Rooms and corridors} + +%.pg +Rooms and corridors in the dungeon are either lit or dark. +Any lit areas within your line of sight will be displayed; +dark areas are only displayed if they are within one space of you. +Walls and corridors remain on the map as you explore them. + +%.pg +Secret corridors are hidden. You can find them with the `{\tt s}' (search) +command. + +%.hn 2 +\subsection*{Doorways} + +%.pg +Doorways connect rooms and corridors. Some doorways have no doors; +you can walk right through. Others have doors in them, which may be +open, closed, or locked. To open a closed door, use the `{\tt o}' (open) +command; to close it again, use the `{\tt c}' (close) command. + +%.pg +You can get through a locked door by using a tool to pick the lock +with the `{\tt a}' (apply) command, or by kicking it open with the +`{\tt \^{}D}' (kick) command. + +%.pg +Open doors cannot be entered diagonally; you must approach them +straight on, horizontally or vertically. Doorways without doors are +not restricted in this fashion. + +%.pg +Doors can be useful for shutting out monsters. Most monsters cannot +open doors, although a few don't need to (ex.\ ghosts can walk through +doors). + +%.pg +Secret doors are hidden. You can find them with the `{\tt s}' (search) +command. Once found they are in all ways equivalent to normal doors. + +%.hn 2 +\subsection*{Traps (`{\tt \^{}}')} + +%.pg +There are traps throughout the dungeon to snare the unwary delver. +For example, you may suddenly fall into a pit and be stuck for a few +turns trying to climb out. Traps don't appear on your map until you +see one triggered by moving onto it, see something fall into it, or you +discover it with the `{\tt s}' (search) command. Monsters can fall prey to +traps, too, which can be a very useful defensive strategy. + +%.pg +There is a special pre-mapped branch of the dungeon based on the +classic computer game ``{\tt Sokoban}.'' The goal is to push the boulders +into the pits or holes. With careful foresight, it is possible to +complete all of the levels according to the traditional rules of +Sokoban. Some allowances are permitted in case the player gets stuck; +however, they will lower your luck. + +\subsection*{Stairs (`{\tt <}', `{\tt >}')} + +%.pg +In general, each level in the dungeon will have a staircase going up +(`{\tt <}') to the previous level and another going down (`{\tt >}') +to the next +level. There are some exceptions though. For instance, fairly early +in the dungeon you will find a level with two down staircases, one +continuing into the dungeon and the other branching into an area +known as the Gnomish Mines. Those mines eventually hit a dead end, +so after exploring them (if you choose to do so), you'll need to +climb back up to the main dungeon. + +%.pg +When you traverse a set of stairs, or trigger a trap which sends you +to another level, the level you're leaving will be deactivated and +stored in a file on disk. If you're moving to a previously visited +level, it will be loaded from its file on disk and reactivated. If +you're moving to a level which has not yet been visited, it will be +created (from scratch for most random levels, from a template for +some ``special'' levels, or loaded from the remains of an earlier game +for a ``bones'' level as briefly described below). Monsters are only +active on the current level; those on other levels are essentially +placed into stasis. + +%.pg +Ordinarily when you climb a set of stairs, you will arrive on the +corresponding staircase at your destination. However, pets (see below) +and some other monsters will follow along if they're close enough when +you travel up or down stairs, and occasionally one of these creatures +will displace you during the climb. When that occurs, the pet or other +monster will arrive on the staircase and you will end up nearby. + +\subsection*{Ladders (`{\tt <}', `{\tt >}')} + +%.pg +Ladders serve the same purpose as staircases, and the two types of +inter-level connections are nearly indistinguishable during game play. + +%.hn 2 +\subsection*{Shops and shopping} + +%.pg +Occasionally you will run across a room with a shopkeeper near the door +and many items lying on the floor. You can buy items by picking them +up and then using the `{\tt p}' command. You can inquire about the price +of an item prior to picking it up by using the ``{\tt \#chat}'' command +while standing on it. Using an item prior to paying for it will incur a +charge, and the shopkeeper won't allow you to leave the shop until you +have paid any debt you owe. + +%.pg +You can sell items to a shopkeeper by dropping them to the floor while +inside a shop. You will either be offered an amount of gold and asked +whether you're willing to sell, or you'll be told that the shopkeeper +isn't interested (generally, your item needs to be compatible with the +type of merchandise carried by the shop). + +%.pg +If you drop something in a shop by accident, the shopkeeper will usually +claim ownership without offering any compensation. You'll have to buy +it back if you want to reclaim it. + +%.pg +Shopkeepers sometimes run out of money. When that happens, you'll be +offered credit instead of gold when you try to sell something. Credit +can be used to pay for purchases, but it is only good in the shop where +it was obtained; other shopkeepers won't honor it. (If you happen to +find a ``credit card'' in the dungeon, don't bother trying to use it in +shops; shopkeepers will not accept it.) + +%.pg +The {\tt \$} command, which reports the amount of gold you are carrying +(in inventory, not inside bags or boxes), will also show current shop +debt or credit, if any. The {\tt Iu} command lists unpaid items +(those which still belong to the shop) if you are carrying any. +The {\tt Ix} command shows an inventory-like display of any unpaid +items which have been used up, along with other shop fees, if any. + +%.hn 3 +\subsubsection*{Shop idiosyncracies} + +%.pg +Several aspects of shop behavior might be unexpected. + +\begin{itemize} +% note: a bullet is the default item label so we could omit [$\bullet$] here +%.lp \(bu 2 +\item[$\bullet$] +The price of a given item can vary due to a variety of factors. +%.lp \(bu 2 +\item[$\bullet$] +A shopkeeper treats the spot immediately inside the door as if it were +outside the shop. +%.lp \(bu 2 +\item[$\bullet$] +While the shopkeeper watches you like a hawk, he will generally ignore +any other customers. +%.lp \(bu 2 +\item[$\bullet$] +If a shop is ``closed for inventory'', it will not open of its own accord. +%.lp \(bu 2 +\item[$\bullet$] +Shops do not get restocked with new items, regardless of inventory depletion. +\end{itemize} + +%.hn 1 +\section{Monsters} + +%.pg +Monsters you cannot see are not displayed on the screen. Beware! +You may suddenly come upon one in a dark place. Some magic items can +help you locate them before they locate you (which some monsters can do +very well). + +%.pg +The commands `{\tt /}' and `{\tt ;}' may be used to obtain information +about those +monsters who are displayed on the screen. The command `{\tt C}' allows you +to assign a name to a monster, which may be useful to help distinguish +one from another when multiple monsters are present. Assigning a name +which is just a space will remove any prior name. + +%.pg +The extended command ``{\tt \#chat}'' can be used to interact with an adjacent +monster. There is no actual dialog (in other words, you don't get to +choose what you'll say), but chatting with some monsters such as a +shopkeeper or the Oracle of Delphi can produce useful results. + +%.hn 2 +\subsection*{Fighting} + +%.pg +If you see a monster and you wish to fight it, just attempt to walk +into it. Many monsters you find will mind their own business unless +you attack them. Some of them are very dangerous when angered. +Remember: discretion is the better part of valor. + +%.pg +If you can't see a monster (if it is invisible, or if you are blinded), +the symbol `I' will be shown when you learn of its presence. +If you attempt to walk into it, you will try to fight it just like +a monster that you can see; of course, +if the monster has moved, you will attack empty air. If you guess +that the monster has moved and you don't wish to fight, you can use the `m' +command to move without fighting; likewise, if you don't remember a monster +but want to try fighting anyway, you can use the `F' command. + +%.hn 2 +\subsection*{Your pet} + +%.pg +You start the game with a little dog (`{\tt d}'), cat (`{\tt f}'), +or pony (`{\tt u}'), which follows +you about the dungeon and fights monsters with you. Like you, your +pet needs food to survive. It usually feeds itself on fresh carrion +and other meats. If you're worried about it or want to train it, you +can feed it, too, by throwing it food. A properly trained pet can be +very useful under certain circumstances. + +%.pg +Your pet also gains experience from killing monsters, and can grow +over time, gaining hit points and doing more damage. Initially, your +pet may even be better at killing things than you, which makes pets +useful for low-level characters. + +%.pg +Your pet will follow you up and down staircases if it is next to you +when you move. Otherwise your pet will be stranded and may become +wild. Similarly, when you trigger certain types of traps which alter +your location (for instance, a trap door which drops you to a lower +dungeon level), any adjacent pet will accompany you and any non-adjacent +pet will be left behind. Your pet may trigger such traps itself; you +will not be carried along with it even if adjacent at the time. + +%.hn 2 +\subsection*{Steeds} + +%.pg +Some types of creatures in the dungeon can actually be ridden if you +have the right equipment and skill. Convincing a wild beast to let +you saddle it up is difficult to say the least. Many a dungeoneer +has had to resort to magic and wizardry in order to forge the alliance. +Once you do have the beast under your control however, you can +easily climb in and out of the saddle with the `{\tt \#ride}' command. Lead +the beast around the dungeon when riding, in the same manner as +you would move yourself. It is the beast that you will see displayed +on the map. + +%.pg +Riding skill is managed by the `{\tt \#enhance}' command. See the section +on Weapon proficiency for more information about that. + +%.hn 2 +\subsection*{Bones levels} + +%.pg +You may encounter the shades and corpses of other adventurers (or even +former incarnations of yourself!) and their personal effects. Ghosts +are hard to kill, but easy to avoid, since they're slow and do little +damage. You can plunder the deceased adventurer's possessions; +however, they are likely to be cursed. Beware of whatever killed the +former player; it is probably still lurking around, gloating over its +last victory. + +%.hn 1 +\section{Objects} + +%.pg +When you find something in the dungeon, it is common to want to pick +it up. In {\it NetHack}, this is accomplished automatically by walking over +the object (unless you turn off the {\it autopickup\/} +option (see below), or move with the `{\tt m}' prefix (see above)), or +manually by using the `{\tt ,}' command. +%.pg +If you're carrying too many items, {\it NetHack\/} will tell you so and you +won't be able to pick up anything more. Otherwise, it will add the object(s) +to your pack and tell you what you just picked up. +%.pg +As you add items to your inventory, you also add the weight of that object +to your load. The amount that you can carry depends on your strength and +your constitution. The +stronger you are, the less the additional load will affect you. There comes +a point, though, when the weight of all of that stuff you are carrying around +with you through the dungeon will encumber you. Your reactions +will get slower and you'll burn calories faster, requiring food more frequently +to cope with it. Eventually, you'll be so overloaded that you'll either have +to discard some of what you're carrying or collapse under its weight. +%.pg +NetHack will tell you how badly you have loaded yourself. The symbols +`Burdened', `Stressed', `Strained', `Overtaxed' and `Overloaded' are +displayed on the bottom line display to indicate your condition. + +%.pg +When you pick up an object, it is assigned an inventory letter. Many +commands that operate on objects must ask you to find out which object +you want to use. When {\it NetHack\/} asks you to choose a particular object +you are carrying, you are usually presented with a list of inventory +letters to choose from (see Commands, above). + +%.pg +Some objects, such as weapons, are easily differentiated. Others, like +scrolls and potions, are given descriptions which vary according to +type. During a game, any two objects with the same description are +the same type. However, the descriptions will vary from game to game. + +%.pg +When you use one of these objects, if its effect is obvious, {\it NetHack\/} +will remember what it is for you. If its effect isn't extremely +obvious, you will be asked what you want to call this type of object +so you will recognize it later. You can also use the ``{\tt \#name}'' +command for the same purpose at any time, to name all objects of a +particular type or just an individual object. +When you use ``{\tt \#name}'' on an object which has already been named, +specifying a space as the value will remove the prior name instead +of assigning a new one. + +%.hn 2 +\subsection*{Curses and Blessings} + +%.pg +Any object that you find may be cursed, even if the object is +otherwise helpful. The most common effect of a curse is being stuck +with (and to) the item. Cursed weapons weld themselves to your hand +when wielded, so you cannot unwield them. Any cursed item you wear +is not removable by ordinary means. In addition, cursed arms and armor +usually, but not always, bear negative enchantments that make them +less effective in combat. Other cursed objects may act poorly or +detrimentally in other ways. + +%.pg +Objects can also be blessed. Blessed items usually work better or +more beneficially than normal uncursed items. For example, a blessed +weapon will do more damage against demons. + +%.pg +There are magical means of bestowing or removing curses upon objects, +so even if you are stuck with one, you can still have the curse +lifted and the item removed. Priests and Priestesses have an innate +sensitivity to this property in any object, so they can more easily avoid +cursed objects than other character roles. + +%.pg +An item with unknown status will be reported in your inventory with no prefix. +An item which you know the state of will be distinguished in your inventory +by the presence of the word ``cursed'', ``uncursed'' or ``blessed'' in the +description of the item. + +%.hn 2 +\subsection*{Weapons (`{\tt )}')} + +%.pg +Given a chance, most monsters in the Mazes of Menace will gratuitously try to +kill you. You need weapons for self-defense (killing them first). Without a +weapon, you do only 1--2 hit points of damage (plus bonuses, if any). +Monk characters are an exception; they normally do much more damage with +bare hands than they do with weapons. + +%.pg +There are wielded weapons, like maces and swords, and thrown weapons, +like arrows and spears. To hit monsters with a weapon, you must wield it and +attack them, or throw it at them. You can simply elect to throw a spear. +To shoot an arrow, you should first wield a bow, then throw the arrow. +Crossbows shoot crossbow bolts. Slings hurl rocks and (other) stones +(like gems). + +%.pg +Enchanted weapons have a ``plus'' (or ``to hit enhancement'' which can be +either positive or negative) that adds to your chance to +hit and the damage you do to a monster. The only way to determine a weapon's +enchantment is to have it magically identified somehow. +Most weapons are subject to some type of damage like rust. Such +``erosion'' damage can be repaired. + +%.pg +The chance that an attack will successfully hit a monster, and the amount +of damage such a hit will do, depends upon many factors. Among them are: +type of weapon, quality of weapon (enchantment and/or erosion), experience +level, strength, dexterity, encumbrance, and proficiency (see below). The +monster's armor class---a general defense rating, not necessarily due to +wearing of armor---is a factor too; also, some monsters are particularly +vulnerable to certain types of weapons. + +%.pg +Many weapons can be wielded in one hand; some require both hands. +When wielding a two-handed weapon, you can not wear a shield, and +vice versa. When wielding a one-handed weapon, you can have another +weapon ready to use by setting things up with the `{\tt x}' command, which +exchanges your primary (the one being wielded) and alternate weapons. +And if you have proficiency in the ``two weapon combat'' skill, you +may wield both weapons simultaneously as primary and secondary; use the +`{\tt \#twoweapon}' extended command to engage or disengage that. Only +some types of characters (barbarians, for instance) have the necessary +skill available. Even with that skill, using two weapons at once incurs +a penalty in the chance to hit your target compared to using just one +weapon at a time. + +%.pg +There might be times when you'd rather not wield any weapon at all. +To accomplish that, wield `{\tt -}', or else use the `{\tt A}' command which +allows you to unwield the current weapon in addition to taking off +other worn items. + +%.pg +Those of you in the audience who are AD\&D players, be aware that each +weapon which existed in AD\&D does roughly the same damage to monsters in +{\it NetHack}. Some of the more obscure weapons (such as the % +{\it aklys}, {\it lucern hammer}, and {\it bec-de-corbin\/}) are defined +in an appendix to {\it Unearthed Arcana}, an AD\&D supplement. + +%.pg +The commands to use weapons are `{\tt w}' (wield), `{\tt t}' (throw), +`{\tt f}' (fire, an alternative way of throwing), `{\tt Q}' (quiver), +`{\tt x}' (exchange), `{\tt \#twoweapon}', and `{\tt \#enhance}' (see below). + +%.hn 3 +\subsection*{Throwing and shooting} + +%.pg +You can throw just about anything via the `{\tt t}' command. It will prompt +for the item to throw; picking `{\tt ?}' will list things in your inventory +which are considered likely to be thrown, or picking `{\tt *}' will list +your entire inventory. After you've chosen what to throw, you will +be prompted for a direction rather than for a specific target. The +distance something can be thrown depends mainly on the type of object +and your strength. Arrows can be thrown by hand, but can be thrown +much farther and will be more likely to hit when thrown while you are +wielding a bow. + +%.pg +You can simplify the throwing operation by using the `{\tt Q}' command to +select your preferred ``missile'', then using the `{\tt f}' command to +throw it. You'll be prompted for a direction as above, but you don't +have to specify which item to throw each time you use `{\tt f}'. There is +also an option, +{\it autoquiver}, +which has {\it NetHack\/} choose another item to automatically fill your +quiver when the inventory slot used for `{\tt Q}' runs out. + +%.pg +Some characters have the ability to fire a volley of multiple items in a +single turn. Knowing how to load several rounds of ammunition at +once---or hold several missiles in your hand---and still hit a +target is not an easy task. Rangers are among those who are adept +at this task, as are those with a high level of proficiency in the +relevant weapon skill (in bow skill if you're wielding one to +shoot arrows, in crossbow skill if you're wielding one to shoot bolts, +or in sling skill if you're wielding one to shoot stones). +The number of items that the character has a chance to fire varies from +turn to turn. You can explicitly limit the number of shots by using a +numeric prefix before the `{\tt t}' or `{\tt f}' command. +For example, ``{\tt 2f}'' (or ``{\tt n2f}'' if using +{\it number\_pad\/} +mode) would ensure that at most 2 arrows are shot +even if you could have fired 3. If you specify +a larger number than would have been shot (``{\tt 4f}'' in this example), +you'll just end up shooting the same number (3, here) as if no limit +had been specified. Once the volley is in motion, all of the items +will travel in the same direction; if the first ones kill a monster, +the others can still continue beyond that spot. + +%.hn 3 +\subsection*{Weapon proficiency} + +%.pg +You will have varying degrees of skill in the weapons available. +Weapon proficiency, or weapon skills, affect how well you can use +particular types of weapons, and you'll be able to improve your skills +as you progress through a game, depending on your role, your experience +level, and use of the weapons. + +%.pg +For the purposes of proficiency, weapons have +been divided up into various groups such as daggers, broadswords, and +polearms. Each role has a limit on what level of proficiency a character +can achieve for each group. For instance, wizards can become highly +skilled in daggers or staves but not in swords or bows. + +%.pg +The `{\tt \#enhance}' extended command is used to review current weapons proficiency +(also spell proficiency) and to choose which skill(s) to improve when +you've used one or more skills enough to become eligible to do so. The +skill rankings are ``none'' (sometimes also referred to as ``restricted'', +because you won't be able to advance), ``unskilled'', ``basic'', ``skilled'', +and ``expert''. Restricted skills simply will not appear in the list +shown by `{\tt \#enhance}'. (Divine intervention might unrestrict a particular +skill, in which case it will start at unskilled and be limited to basic.) +Some characters can enhance their barehanded combat or martial arts skill +beyond expert to ``master'' or ``grand master''. + +%.pg +Use of a weapon in which you're restricted or unskilled +will incur a modest penalty in the chance to hit a monster and also in +the amount of damage done when you do hit; at basic level, there is no +penalty or bonus; at skilled level, you receive a modest bonus in the +chance to hit and amount of damage done; at expert level, the bonus is +higher. A successful hit has a chance to boost your training towards +the next skill level (unless you've already reached the limit for this +skill). Once such training reaches the threshold for that next level, +you'll be told that you feel more confident in your skills. At that +point you can use `{\tt \#enhance}' to increase one or more skills. Such skills +are not increased automatically because there is a limit to your total +overall skills, so you need to actively choose which skills to enhance +and which to ignore. + +%.hn 2 +\subsection*{Armor (`{\tt [}')} + +%.pg +Lots of unfriendly things lurk about; you need armor to protect +yourself from their blows. Some types of armor offer better +protection than others. Your armor class is a measure of this +protection. Armor class (AC) is measured as in AD\&D, with 10 being +the equivalent of no armor, and lower numbers meaning better armor. +Each suit of armor which exists in AD\&D gives the same protection in +{\it NetHack}. Here is an (incomplete) list of the armor classes provided by +various suits of armor: + +\begin{center} +\begin{tabular}{lllll} +dragon scale mail & 1 & \makebox[20mm]{} & plate mail & 3\\ +crystal plate mail & 3 & & bronze plate mail & 4\\ +splint mail & 4 & & banded mail & 4\\ +dwarvish mithril-coat & 4 & & elven mithril-coat & 5\\ +chain mail & 5 & & orcish chain mail & 6\\ +scale mail & 6 & & studded leather armor & 7\\ +ring mail & 7 & & orcish ring mail & 8\\ +leather armor & 8 & & leather jacket & 9\\ +no armor & 10 +\end{tabular} +\end{center} + +%.pg +\nd You can also wear other pieces of armor (ex.\ helmets, boots, +shields, cloaks) +to lower your armor class even further, but you can only wear one item +of each category (one suit of armor, one cloak, one helmet, one +shield, and so on) at a time. + +%.pg +If a piece of armor is enchanted, its armor protection will be better +(or worse) than normal, and its ``plus'' (or minus) will subtract from +your armor class. For example, a +1 chain mail would give you +better protection than normal chain mail, lowering your armor class one +unit further to 4. When you put on a piece of armor, you immediately +find out the armor class and any ``plusses'' it provides. Cursed +pieces of armor usually have negative enchantments (minuses) in +addition to being unremovable. + +%.pg +Many types of armor are subject to some kind of damage like rust. Such +damage can be repaired. Some types of armor may inhibit spell casting. + +%.pg +The commands to use armor are `{\tt W}' (wear) and `{\tt T}' (take off). +The `{\tt A}' command can also be used to take off armor as well as other +worn items. + +%.hn 2 +\subsection*{Food (`{\tt \%}')} + +%.pg +Food is necessary to survive. If you go too long without eating you +will faint, and eventually die of starvation. +Some types of food will spoil, and become unhealthy to eat, +if not protected. +Food stored in ice boxes or tins (``cans'') +will usually stay fresh, but ice boxes are heavy, and tins +take a while to open. + +%.pg +When you kill monsters, they usually leave corpses which are also +``food.'' Many, but not all, of these are edible; some also give you +special powers when you eat them. A good rule of thumb is ``you are +what you eat.'' + +%.pg +Some character roles and some monsters are vegetarian. Vegetarian monsters +will typically never eat animal corpses, while vegetarian players can, +but with some rather unpleasant side-effects. + +%.pg +You can name one food item after something you like to eat with the +{\it fruit\/} option. + +%.pg +The command to eat food is `{\tt e}'. + +%.hn 2 +\subsection*{Scrolls (`{\tt ?}')} + +%.pg +Scrolls are labeled with various titles, probably chosen by ancient wizards +for their amusement value (ex.\ ``READ ME,'' or ``THANX MAUD'' backwards). +Scrolls disappear after you read them (except for blank ones, without +magic spells on them). + +%.pg +One of the most useful of these is the % +{\it scroll of identify}, which +can be used to determine what another object is, whether it is cursed or +blessed, and how many uses it has left. Some objects of subtle +enchantment are difficult to identify without these. + +%.pg +A mail daemon may run up and deliver mail to you as a % +{\it scroll of mail} (on versions compiled with this feature). +To use this feature on versions where {\it NetHack\/} +mail delivery is triggered by electronic mail appearing in your system mailbox, +you must let {\it NetHack\/} know where to look for new mail by setting the +``MAIL'' environment variable to the file name of your mailbox. +You may also want to set the ``MAILREADER'' environment variable to the +file name of your favorite reader, so {\it NetHack\/} can shell to it when you +read the scroll. +On versions of {\it NetHack\/} where mail is randomly +generated internal to the game, these environment variables are ignored. +You can disable the mail daemon by turning off the +{\it mail\/} option. + +%.pg +The command to read a scroll is `{\tt r}'. + +%.hn 2 +\subsection*{Potions (`{\tt !}')} + +%.pg +Potions are distinguished by the color of the liquid inside the flask. +They disappear after you quaff them. + +%.pg +Clear potions are potions of water. Sometimes these are +blessed or cursed, resulting in holy or unholy water. Holy water is +the bane of the undead, so potions of holy water are good things to +throw (`{\tt t}') at them. It is also sometimes very useful to dip +(``{\tt \#dip}'') an object into a potion. + +%.pg +The command to drink a potion is `{\tt q}' (quaff). + +%.hn 2 +\subsection*{Wands (`{\tt /}')} + +%.pg +Magic wands usually have multiple magical charges. Some wands are +directional---you must give a direction in which to zap them. You can also +zap them at yourself (just give a `{\tt .}' or `{\tt s}' for the direction). +Be warned, however, for this is often unwise. Other wands are +nondirectional---they don't require a direction. The number of charges in a +wand is random and decreases by one whenever you use it. + +%.pg +When the number of charges left in a wand becomes zero, attempts to use the +wand will usually result in nothing happening. Occasionally, however, it may +be possible to squeeze the last few mana points from an otherwise spent wand, +destroying it in the process. A wand may be recharged by using suitable +magic, but doing so runs the risk of causing it to explode. The chance +for such an explosion starts out very small and increases each time the +wand is recharged. + +%.pg +In a truly desperate situation, when your back is up against the wall, you +might decide to go for broke and break your wand. This is not for the faint +of heart. Doing so will almost certainly cause a catastrophic release of +magical energies. + +%.pg +When you have fully identified a particular wand, inventory display will +include additional information in parentheses: the number of times it has +been recharged followed by a colon and then by its current number of charges. +A current charge count of {\tt -1} is a special case indicating that the wand +has been cancelled. + +%.pg +The command to use a wand is `{\tt z}' (zap). To break one, use the `{\tt a}' +(apply) command. + +%.hn 2 +\subsection*{Rings (`{\tt =}')} + +%.pg +Rings are very useful items, since they are relatively permanent +magic, unlike the usually fleeting effects of potions, scrolls, and +wands. + +%.pg +Putting on a ring activates its magic. You can wear only two +rings, one on each ring finger. + +%.pg +Most rings also cause you to grow hungry more rapidly, the rate +varying with the type of ring. + +%.pg +The commands to use rings are `{\tt P}' (put on) and `{\tt R}' (remove). + +%.hn 2 +\subsection*{Spellbooks (`{\tt +}')} + +%.pg +Spellbooks are tomes of mighty magic. When studied with the `{\tt r}' (read) +command, they transfer to the reader the knowledge of a spell (and +therefore eventually become unreadable) --- unless the attempt backfires. +Reading a cursed spellbook or one with mystic runes beyond +your ken can be harmful to your health! + +%.pg +A spell (even when learned) can also backfire when you cast it. If you +attempt to cast a spell well above your experience level, or if you have +little skill with the appropriate spell type, or cast it at +a time when your luck is particularly bad, you can end up wasting both the +energy and the time required in casting. + +%.pg +Casting a spell calls forth magical energies and focuses them with +your naked mind. Some of the magical energy released comes from within +you, and casting several spells in a row may tire you. +Casting of spells also requires practice. With practice, your +skill in each category of spell casting will improve. Over time, however, +your memory of each spell will dim, and you will need to relearn it. + +%.pg +Some spells are +directional---you must give a direction in which to cast them. You can also +cast them at yourself (just give a `{\tt .}' or `{\tt s}' for the direction). +Be warned, however, for this is often unwise. Other spells are +nondirectional---they don't require a direction. + +%.pg +Just as weapons are divided into groups in which a character can become +proficient (to varying degrees), spells are similarly grouped. +Successfully casting a spell exercises the skill group; sufficient skill +may increase the potency of the spell and reduce the risk of spell failure. +Skill slots are shared with weapons skills. (See also the section on +``Weapon proficiency''.) + +%.pg +Casting a spell also requires flexible movement, and wearing various types +of armor may interfere with that. + +%.pg +The command to read a spellbook is the same as for scrolls, `{\tt r}' +(read). The `{\tt +}' command lists your current spells, their levels, +categories, and chances for failure. +The `{\tt Z}' (cast) command casts a spell. + +%.hn 2 +\subsection*{Tools (`{\tt (}')} + +%.pg +Tools are miscellaneous objects with various purposes. Some tools +have a limited number of uses, akin to wand charges. For example, lamps burn +out after a while. Other tools are containers, which objects can +be placed into or taken out of. + +%.pg +The command to use tools is `{\tt a}' (apply). + +%.hn 3 +\subsection*{Containers} + +%.pg +You may encounter bags, boxes, and chests in your travels. A tool of +this sort can be opened with the ``{\tt \#loot}'' extended command when +you are standing on top of it (that is, on the same floor spot), +or with the `{\tt a}' (apply) command when you are carrying it. However, +chests are often locked, and are in any case unwieldy objects. +You must set one down before unlocking it by +using a key or lock-picking tool with the `{\tt a}' (apply) command, +by kicking it with the `{\tt \^{}D}' command, +or by using a weapon to force the lock with the ``{\tt \#force}'' +extended command. + +%.pg +Some chests are trapped, causing nasty things to happen when you +unlock or open them. You can check for and try to deactivate traps +with the ``{\tt \#untrap}'' extended command. + +%.hn 2 +\subsection*{Amulets (`{\tt "}')} + +%.pg +Amulets are very similar to rings, and often more powerful. Like +rings, amulets have various magical properties, some beneficial, +some harmful, which are activated by putting them on. + +%.pg +Only one amulet may be worn at a time, around your neck. + +%.pg +The commands to use amulets are the same as for rings, `{\tt P}' (put on) +and `{\tt R}' (remove). + +%.hn 2 +\subsection*{Gems (`{\tt *}')} + +%.pg +Some gems are valuable, and can be sold for a lot of gold. They are also +a far more efficient way of carrying your riches. Valuable gems increase +your score if you bring them with you when you exit. + +%.pg +Other small rocks are also categorized as gems, but they are much less +valuable. All rocks, however, can be used as projectile weapons (if you +have a sling). In the most desperate of cases, you can still throw them +by hand. + +%.hn 2 +\subsection*{Large rocks (`{\tt `}')} +%.pg +Statues and boulders are not particularly useful, and are generally +heavy. It is rumored that some statues are not what they seem. + +%.pg +Very large humanoids (giants and their ilk) have been known to use boulders +as weapons. + +%.hn 2 +\subsection*{Gold (`{\tt \$}')} + +%.pg +Gold adds to your score, and you can buy things in shops with it. +There are a number +of monsters in the dungeon that may be influenced by the amount of gold +you are carrying (shopkeepers aside). + +%.hn 1 +\section{Conduct} + +%.pg +As if winning {\it NetHack\/} were not difficult enough, certain players +seek to challenge themselves by imposing restrictions on the +way they play the game. The game automatically tracks some of +these challenges, which can be checked at any time with the {\tt \#conduct} +command or at the end of the game. When you perform an action which +breaks a challenge, it will no longer be listed. This gives +players extra ``bragging rights'' for winning the game with these +challenges. Note that it is perfectly acceptable to win the game +without resorting to these restrictions and that it is unusual for +players to adhere to challenges the first time they win the game. + +%.pg +Several of the challenges are related to eating behavior. The most +difficult of these is the foodless challenge. Although creatures +can survive long periods of time without food, there is a physiological +need for water; thus there is no restriction on drinking beverages, +even if they provide some minor food benefits. +Calling upon your god for help with starvation does +not violate any food challenges either. + +%.pg +A strict vegan diet is one which avoids any food derived from animals. +The primary source of nutrition is fruits and vegetables. The +corpses and tins of blobs (`b'), jellies (`j'), and fungi (`F') are +also considered to be vegetable matter. Certain human +food is prepared without animal products; namely, lembas wafers, cram +rations, food rations (gunyoki), K-rations, and C-rations. +Metal or another normally indigestible material eaten while polymorphed +into a creature that can digest it is also considered vegan food. +Note however that eating such items still counts against foodless conduct. + +%.pg +Vegetarians do not eat animals; +however, they are less selective about eating animal byproducts than vegans. +In addition to the vegan items listed above, they may eat any kind +of pudding (`P') other than the black puddings, +eggs and food made from eggs (fortune cookies and pancakes), +food made with milk (cream pies and candy bars), and lumps of +royal jelly. Monks are expected to observe a vegetarian diet. + +%.pg +Eating any kind of meat violates the vegetarian, vegan, and foodless +conducts. This includes tripe rations, the corpses or tins of any +monsters not mentioned above, and the various other chunks of meat +found in the dungeon. Swallowing and digesting a monster while polymorphed +is treated as if you ate the creature's corpse. +Eating leather, dragon hide, or bone items while +polymorphed into a creature that can digest it, or eating monster brains +while polymorphed into a mind flayer, is considered eating +an animal, although wax is only an animal byproduct. + +%.pg +Regardless of conduct, there will be some items which are indigestible, +and others which are hazardous to eat. Using a swallow-and-digest +attack against a monster is equivalent to eating the monster's corpse. +Please note that the term ``vegan'' is used here only in the context of +diet. You are still free to choose not to use or wear items derived +from animals (e.g. leather, dragon hide, bone, horns, coral), but the +game will not keep track of this for you. Also note that ``milky'' +potions may be a translucent white, but they do not contain milk, +so they are compatible with a vegan diet. Slime molds or +player-defined ``fruits'', although they could be anything +from ``cherries'' to ``pork chops'', are also assumed to be vegan. + +%.pg +An atheist is one who rejects religion. This means that you cannot +{\tt \#pray}, {\tt \#offer} sacrifices to any god, +{\tt \#turn} undead, or {\tt \#chat} with a priest. +Particularly selective readers may argue that playing Monk or Priest +characters should violate this conduct; that is a choice left to the +player. Offering the Amulet of Yendor to your god is necessary to +win the game and is not counted against this conduct. You are also +not penalized for being spoken to by an angry god, priest(ess), or +other religious figure; a true atheist would hear the words but +attach no special meaning to them. + +%.pg +Most players fight with a wielded weapon (or tool intended to be +wielded as a weapon). Another challenge is to win the game without +using such a wielded weapon. You are still permitted to throw, +fire, and kick weapons; use a wand, spell, or other type of item; +or fight with your hands and feet. + +%.pg +In {\it NetHack\/}, a pacifist refuses to cause the death of any other monster +(i.e. if you would get experience for the death). This is a particularly +difficult challenge, although it is still possible to gain experience +by other means. + +%.pg +An illiterate character cannot read or write. This includes reading +a scroll, spellbook, fortune cookie message, or t-shirt; writing a +scroll; or making an engraving of anything other than a single ``x'' (the +traditional signature of an illiterate person). Reading an engraving, +or any item that is absolutely necessary to win the game, is not counted +against this conduct. The identity of scrolls and spellbooks (and +knowledge of spells) in your starting inventory is assumed to be +learned from your teachers prior to the start of the game and isn't +counted. + +%.pg +There are several other challenges tracked by the game. It is possible +to eliminate one or more species of monsters by genocide; playing without +this feature is considered a challenge. When the game offers you an +opportunity to genocide monsters, you may respond with the monster type +``none'' if you want to decline. You can change the form of an item into +another item of the same type (``polypiling'') or the form of your own +body into another creature (``polyself'') by wand, spell, or potion of +polymorph; avoiding these effects are each considered challenges. +Polymorphing monsters, including pets, does not break either of these +challenges. +Finally, you may sometimes receive wishes; a game without an attempt to +wish for any items is a challenge, as is a game without wishing for +an artifact (even if the artifact immediately disappears). When the +game offers you an opportunity to make a wish for an item, you may +choose ``nothing'' if you want to decline. + +%.hn 1 +\section{Options} + +%.pg +Due to variations in personal tastes and conceptions of how {\it NetHack\/} +should do things, there are options you can set to change how {\it NetHack\/} +behaves. + +%.hn 2 +\subsection*{Setting the options} + +%.pg +Options may be set in a number of ways. Within the game, the `{\tt O}' +command allows you to view all options and change most of them. +You can also set options automatically by placing them in the +``NETHACKOPTIONS'' environment variable or in a configuration file. +Some versions of {\it NetHack\/} also have front-end programs that allow +you to set options before starting the game. + +%.hn 2 +\subsection*{Using the NETHACKOPTIONS environment variable} + +%.pg +The NETHACKOPTIONS variable is a comma-separated list of initial +values for the various options. Some can only be turned on or off. +You turn one of these on by adding the name of the option to the list, +and turn it off by typing a `{\tt !}' or ``{\tt no}'' before the name. +Others take a +character string as a value. You can set string options by typing +the option name, a colon or equals sign, and then the value of the string. +The value is terminated by the next comma or the end of string. + +%.pg +For example, to set up an environment variable so that {\it autoquiver\/} +is on, {\it autopickup\/} is off, the {\it name\/} is set to ``Blue Meanie'', +and the {\it fruit\/} is set to ``papaya'', you would enter the command +%.sd +\begin{verbatim} + setenv NETHACKOPTIONS "autoquiver,\!autopickup,name:Blue Meanie,fruit:papaya" +\end{verbatim} +%.ed + +\nd in {\it csh} +(note the need to escape the ! since it's special to the shell), or +%.sd +\begin{verbatim} + NETHACKOPTIONS="autoquiver,!autopickup,name:Blue Meanie,fruit:papaya" + export NETHACKOPTIONS +\end{verbatim} +%.ed + +\nd in {\it sh\/} or {\it ksh}. + +%.hn 2 +\subsection*{Using a configuration file} + +%.pg +Any line in the configuration file starting with `{\tt \#}' is treated as a comment. +Any line in the configuration file starting with ``{\tt OPTIONS=}'' may be +filled out with options in the same syntax as in NETHACKOPTIONS. +Any line starting with ``{\tt DUNGEON=}'', ``{\tt EFFECTS=}'', +``{\tt MONSTERS=}'', ``{\tt OBJECTS=}'', ``{\tt TRAPS=}'', +or ``{\tt BOULDER=}'' +is taken as defining the corresponding {\it dungeon}, +{\it effects}, {\it monsters}, {\it objects}, {\it traps\/} or +{\it boulder\/} option in a different syntax, +a sequence of decimal numbers giving the character position +in the current font to be used in displaying each entry. +A zero in any entry in such a sequence leaves the display of that +entry unchanged; this feature is not available using the option syntax. +Such a sequence can be continued to multiple lines by putting a +`{\tt \verb+\+}' at the end of each line to be continued. + +%.pg +If your copy of the game included the compile time AUTOPICKUP\_EXCEPTIONS +option, then any line starting with ``{\tt AUTOPICKUP\_EXCEPTION=}'' +is taken as defining an exception to the ``{\tt pickup\_types}'' option. +There is a section of this Guidebook that discusses that. + +%.pg +The default name of the configuration file varies on different +operating systems, but NETHACKOPTIONS can also be set to +the full name of a file you want to use (possibly preceded by an `{\tt @}'). + +%.hn 2 +\subsection*{Customization options} + +%.pg +Here are explanations of what the various options do. +Character strings that are too long may be truncated. +Some of the options listed may be inactive in your dungeon. + +\blist{} +%.lp +\item[\ib{align}] +Your starting alignment ({\tt align:lawful}, {\tt align:neutral}, +or {\tt align:chaotic}). You may specify just the first letter. +The default is to randomly pick an appropriate alignment. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{autodig}] +Automatically dig if you are wielding a digging tool and moving into a place +that can be dug (default false). +%.lp +\item[\ib{autopickup}] +Automatically pick up things onto which you move (default on). +See ``{\it pickup\_types\/}'' to refine the behavior. +%.lp +\item[\ib{autoquiver}] +This option controls what happens when you attempt the `f' (fire) +command with an empty quiver. When true, the computer will fill +your quiver with some suitable weapon. Note that it will not take +into account the blessed/cursed status, enchantment, damage, or +quality of the weapon; you are free to manually fill your quiver with +the `Q' command instead. If no weapon is found or the option is +false, the `t' (throw) command is executed instead. (default false) +%.lp +\item[\ib{boulder}] +Set the character used to display boulders (default is rock class symbol). +%.lp +\item[\ib{catname}] +Name your starting cat (ex.\ ``{\tt catname:Morris}''). +Cannot be set with the `{\tt O}' command. +%.lp character +\item[\ib{character}] +Pick your type of character (ex.\ ``{\tt character:Monk}''); +synonym for ``{\it role\/}''. See ``{\it name\/}'' for an alternate method +of specifying your role. Normally only the first letter of +the value is examined; the string ``{\tt random}'' is an exception. +%.lp +\item[\ib{checkpoint}] +Save game state after each level change, for possible recovery after +program crash (default on). +%.lp +\item[\ib{checkspace}] +Check free disk space before writing files to disk (default on). +You may have to turn this off if you have more than 2 GB free space +on the partition used for your save and level files. +Only applies when MFLOPPY was defined during compilation. +%.lp +\item[\ib{cmdassist}] +Have the game provide some additional command assistance for new +players if it detects some anticipated mistakes (default on). +%.lp +\item[\ib{confirm}] +Have user confirm attacks on pets, shopkeepers, and other +peaceable creatures (default on). +%.lp +\item[\ib{DECgraphics}] +Use a predefined selection of characters from the DEC VT-xxx/DEC +Rainbow/ANSI line-drawing character set to display the dungeon/effects/traps +instead of having to define a full graphics set yourself (default off). +This option also sets up proper handling of graphics +characters for such terminals, so you should specify it when appropriate +even if you override the selections with your own graphics strings. +%.lp +\item[\ib{disclose}] +Controls options for disclosing various information when the game ends (defaults +to all possibilities being disclosed). +The possibilities are: + +%.sd +%.si +{\tt i} --- disclose your inventory.\\ +{\tt a} --- disclose your attributes.\\ +{\tt v} --- summarize monsters that have been vanquished.\\ +{\tt g} --- list monster species that have been genocided.\\ +{\tt c} --- display your conduct. +%.ei +%.ed + +Each disclosure possibility can optionally be preceded by a prefix which +let you refine how it behaves. Here are the valid prefixes: + +%.sd +%.si +{\tt y} --- prompt you and default to yes on the prompt.\\ +{\tt n} --- prompt you and default to no on the prompt.\\ +{\tt +} --- disclose it without prompting.\\ +{\tt -} --- do not disclose it and do not prompt. +%.ei +%.ed + +(ex.\ ``{\tt disclose:yi na +v -g -c}'') +The example sets {\it inventory\/} to {\it prompt\/} and default to {\it yes\/}, +{\it attributes\/} to {\it prompt\/} and default to {\it no\/}, +{\it vanquished\/} to {\it disclose without prompting\/}, +{\it genocided\/} to {\it not disclose\/} and not to {\it prompt\/}, and +{\it conduct\/} to {\it not disclose\/} and not to {\it prompt\/}. +Note that the vanquished monsters list includes all monsters killed by +traps and each other as well as by you. +%.lp +\item[\ib{dogname}] +Name your starting dog (ex.\ ``{\tt dogname:Fang}''). +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{dungeon}] +Set the graphics symbols for displaying the dungeon (default +``\verb& |--------||.-|++##& \verb&.##<><>_|\\#{}.}..## #}&''). +The {\it dungeon\/} option should be +followed by a string of 1--41 +characters to be used instead of the default map-drawing characters. +The dungeon map will use the characters you specify instead of the +default symbols, and default symbols for any you do not specify. +Remember that you may need to escape some of these characters +on a command line if they are special to your shell. + +Note that {\it NetHack\/} escape-processes this option string in conventional C +fashion. This means that `\verb+\+' is a prefix to take the following +character literally. Thus `\verb+\+' needs to be represented as `\verb+\\+'. +The special escape form +`\verb+\m+' switches on the meta bit in the following character, and the +`{\tt \^{}}' prefix causes the following character to be treated as a control +character. + +The order of the symbols is: solid rock, vertical wall, horizontal +wall, upper left corner, upper right corner, lower left corner, lower +right corner, cross wall, upward T wall, downward T wall, leftward T +wall, rightward T wall, no door, vertical open door, horizontal open +door, vertical closed door, horizontal closed door, iron bars, tree, +floor of a room, dark corridor, lit corridor, stairs up, stairs down, +ladder up, ladder down, altar, grave, throne, kitchen sink, fountain, pool or moat, +ice, lava, vertical lowered drawbridge, horizontal lowered drawbridge, +vertical raised drawbridge, horizontal raised drawbridge, air, cloud, +under water. + +You might want to use `{\tt +}' for the corners and T walls for a more +aesthetic, boxier display. Note that in the next release, new symbols +may be added, or the present ones rearranged. + +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{effects}] +Set the graphics symbols for displaying special effects (default +``\verb&|-\\/*!)(0#@*/-\\& \verb&||\\-//-\\| |\\-/&''). +The {\it effects\/} option should be +followed by a string of 1--29 +characters to be used instead of the default special-effects characters. +This string is subjected to the same processing as the {\it dungeon\/} option. + +The order of the symbols is: vertical beam, horizontal beam, left slant, +right slant, digging beam, camera flash beam, left boomerang, right boomerang, +four glyphs giving the sequence for magic resistance displays, +the eight surrounding glyphs for swallowed display, +nine glyphs for explosions. +An explosion consists of three rows (top, middle, and bottom) of three +characters. The explosion is centered in the center of this $3 \times 3$ +array. + +Note that in the next release, new symbols may be added, +or the present ones rearranged. + +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{extmenu}] +Changes the extended commands interface to pop-up a menu of available commands. +It is keystroke compatible with the traditional interface except that it does +not require that you hit Enter. It is implemented only by the tty port +(default off), when the game has been compiled to support tty graphics. +%.lp +\item[\ib{female}] +An obsolete synonym for ``{\tt gender:female}''. Cannot be set with the +`{\tt O}' command. +%.lp +\item[\ib{fixinv}] +An object's inventory letter sticks to it when it's dropped (default on). +If this is off, dropping an object shifts all the remaining inventory letters. +%.lp +\item[\ib{fruit}] +Name a fruit after something you enjoy eating (ex.\ ``{\tt fruit:mango}'') +(default ``{\tt slime mold}''). Basically a nostalgic whimsy that +{\it NetHack\/} uses from time to time. You should set this to something you +find more appetizing than slime mold. Apples, oranges, pears, bananas, and +melons already exist in {\it NetHack}, so don't use those. +%.Ip +\item[\ib{gender}] +Your starting gender ({\tt gender:male} or {\tt gender:female}). +You may specify just the first letter. Although you can +still denote your gender using the ``{\tt male}'' and ``{\tt female}'' +options, the ``{\tt gender}'' option will take precedence. +The default is to randomly pick an appropriate gender. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{help}] +If more information is available for an object looked at +with the `{\tt /}' command, ask if you want to see it (default on). +Turning help off makes just looking at things faster, since you aren't +interrupted with the ``{\tt More info?}'' prompt, but it also means that you +might miss some interesting and/or important information. +%.lp +\item[\ib{horsename}] +Name your starting horse (ex.\ ``{\tt horsename:Trigger}''). +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{IBMgraphics}] +Use a predefined selection of IBM extended ASCII characters to display the +dungeon/effects/traps instead of having to define a full graphics set +yourself (default off). +This option also sets up proper handling of graphics +characters for such terminals, so you should specify it when appropriate +even if you override the selections with your own graphics strings. +%.lp +\item[\ib{ignintr}] +Ignore interrupt signals, including breaks (default off). +%.lp +\item[\ib{legacy}] +Display an introductory message when starting the game (default on). +%.lp +\item[\ib{lit\_corridor}] +Show corridor squares seen by night vision or a light source held by your +character as lit (default off). +%.lp +\item[\ib{lootabc}] +Use the old `{\tt a}', `{\tt b}', and `{\tt c}' keyboard shortcuts when +looting, rather than the mnemonics `{\tt o}', `{\tt i}', and `{\tt b}' (default off). +%.lp +\item[\ib{mail}] +Enable mail delivery during the game (default on). +%.lp +\item[\ib{male}] +An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the +`{\tt O}' command. +%.lp +\item[\ib{menustyle}] +Controls the interface used when you need to choose various objects (in +response to the Drop command, for instance). The value specified should +be the first letter of one of the following: traditional, combination, +partial, or full. Traditional was the only interface available for +earlier versions; it consists of a prompt for object class characters, +followed by an object-by-object prompt for all items matching the selected +object class(es). Combination starts with a prompt for object class(es) +of interest, but then displays a menu of matching objects rather than +prompting one-by-one. Partial skips the object class filtering and +immediately displays a menu of all objects. Full displays a menu of +object classes rather than a character prompt, and then a menu of matching +objects for selection. +\item[\ib{menu\_deselect\_all}] +Menu character accelerator to deselect all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default `-'. +\item[\ib{menu\_deselect\_page}] +Menu character accelerator to deselect all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+\+'. +\item[\ib{menu\_first\_page}] +Menu character accelerator to jump to the first page in a menu. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+^+'. +\item[\ib{menu\_headings}] +Controls how the headings in a menu are highlighted. +Values are ``{\tt bold}'', ``{\tt inverse}'', or ``{\tt underline}''. +Not all ports can actually display all three types. +\item[\ib{menu\_invert\_all}] +Menu character accelerator to invert all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default `@'. +\item[\ib{menu\_invert\_page}] +Menu character accelerator to invert all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+~+'. +\item[\ib{menu\_last\_page}] +Menu character accelerator to jump to the last page in a menu. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+|+'. +\item[\ib{menu\_next\_page}] +Menu character accelerator to goto the next menu page. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+>+'. +\item[\ib{menu\_previous\_page}] +Menu character accelerator to goto the previous menu page. +Implemented by the Amiga, Gem and tty ports. +Default `\verb+<+'. +\item[\ib{menu\_search}] +Menu character accelerator to search for a menu item. +Implemented by the Amiga, Gem and X11 ports. +Default `:'. +\item[\ib{menu\_select\_all}] +Menu character accelerator to select all items in a menu. +Implemented by the Amiga, Gem, X11 and tty ports. +Default `.'. +\item[\ib{menu\_select\_page}] +Menu character accelerator to select all items on this page of a menu. +Implemented by the Amiga, Gem and tty ports. +Default `,'. +%.lp +\item[\ib{monsters}] +Set the characters used to display monster classes (default +``\verb+abcdefghijklmnopqrstuv+ +\verb+wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@ '&;:~]+''). +This string is subjected to the same processing as the {\it dungeon\/} option. +The order of the symbols is +ant or other insect, blob, cockatrice, +dog or other canine, eye or sphere, feline, +gremlin, humanoid, imp or minor demon, +jelly, kobold, leprechaun, +mimic, nymph, orc, +piercer, quadruped, rodent, +arachnid or centipede, trapper or lurker above, horse or unicorn, +vortex, worm, xan or other mythical/fantastic insect, +light, zruty, +angelic being, bat or bird, centaur, +dragon, elemental, fungus or mold, +gnome, giant humanoid, invisible monster, +jabberwock, Keystone Kop, lich, +mummy, naga, ogre, +pudding or ooze, quantum mechanic, rust monster, +snake, troll, umber hulk, +vampire, wraith, xorn, +apelike creature, zombie, +human, ghost, golem, +demon, sea monster, lizard, +long worm tail, and mimic. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{msghistory}] +The number of top line messages to save (and recall with `{\tt \^{}P}') +(default 20). Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{msg\_window}] +Allows you to change the way recalled messages are displayed. +(It is currently implemented for tty only.) The possible values are: + +%.sd +%.si +{\tt s} --- single message (default, this was the behavior before 3.4.0).\\ +{\tt c} --- combination, two messages as {\it single\/}, then as {\it full\/}.\\ +{\tt f} --- full window, oldest message first.\\ +{\tt r} --- full window, newest message first. +%.ei +%.ed + +For backward compatibility, no value needs to be specified (which +defaults to {\it full\/}), or it can be negated (which defaults +to {\it single\/}). +%.lp +\item[\ib{name}] +Set your character's name (defaults to your user name). You can also +set your character's role by appending a dash and one or more letters of +the role (that is, by suffixing one of +``{\tt -A -B -C -H -K -M -P -Ra -Ro -S -T -V -W}''). +If ``{\tt -@}'' is used for the role, then a random one will be +automatically chosen. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{news}] +Read the {\it NetHack\/} news file, if present (default on). +Since the news is shown at the beginning of the game, there's no point +in setting this with the `{\tt O}' command. +%.lp +\item[\ib{null}] +Send padding nulls to the terminal (default off). +%.lp +\item[\ib{number\_pad}] +Use the number keys to move instead of {\tt [yuhjklbn]} (default 0 or off). +(number\_pad:2 invokes the old DOS behavior where `{\tt 5}' means `{\tt g}', +meta-`{\tt 5}' means `{\tt G}', and meta-`{\tt 0}' means `{\tt I}'.) +%.lp +\item[\ib{objects}] +Set the characters used to display object classes (default +``\verb&])[="(%!?+/$*`0_.&''). +This string is subjected to the same processing as the {\it dungeon\/} option. +The order of the symbols is +illegal-object (should never be seen), weapon, armor, ring, amulet, tool, +food, potion, scroll, spellbook, wand, gold, gem or rock, boulder or statue, +iron ball, chain, and venom. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{packorder}] +Specify the order to list object types in (default +``\verb&")[%?+!=/(*`0_&''). The value of this option should be a string +containing the symbols for the various object types. Any omitted types +are filled in at the end from the previous order. +%.lp +\item[\ib{perm\_invent}] +If true, always display your current inventory in a window. This only +makes sense for windowing system interfaces that implement this feature. +%.lp +\item[\ib{pettype}] +Specify the type of your initial pet, if you are playing a character class +that uses multiple types of pets; or choose to have no initial pet at all. +Possible values are ``{\tt cat}'', ``{\tt dog}'' and ``{\tt none}''. +Cannot be set with the `{\tt O}' command. +%.Ip +\item[\ib{pickup\_burden}] +When you pick up an item that would exceed this encumbrance +level (Unburdened, Burdened, streSsed, straiNed, overTaxed, +or overLoaded), you will be asked if you want to continue. +(Default `S'). +%.lp +\item[\ib{pickup\_types}] +Specify the object types to be picked up when ``{\it autopickup\/}'' +is on. Default is all types. If your copy of the game has the +experimental compile time option AUTOPICKUP\_EXCEPTIONS included, +you may be able to use ``{\it autopickup\_exception\/}'' configuration +file lines to further refine ``{\it autopickup\/}'' behavior. +%.lp +\item[\ib{prayconfirm}] +Prompt for confirmation before praying (default on). +%.lp +\item[\ib{pushweapon}] +Using the `w' (wield) command when already wielding +something pushes the old item into your alternate weapon slot (default off). +%.Ip +\item[\ib{race}] +Selects your race (for example, ``{\tt race:human}''). Default is random. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{rest\_on\_space}] +Make the space bar a synonym for the `{\tt .}' (rest) command (default off). +%.lp +\item[\ib{role}] +Pick your type of character (ex.\ ``{\tt role:Samurai}''); +synonym for ``{\it character\/}''. See ``{\it name\/}'' for an alternate method +of specifying your role. Normally only the first letter of the +value is examined; `r' is an exception with ``{\tt Rogue}'', {\tt Ranger}'', +and ``{\tt random}'' values. +%.lp +\item[\ib{runmode}] +Controls the amount of screen updating for the map window when engaged +in multi-turn movement (running via {\tt shift}+direction +or {\tt control}+direction +and so forth, or via the travel command or mouse click). +The possible values are: + +%.sd +%.si +{\tt teleport} --- update the map after movement has finished;\\ +{\tt run} --- update the map after every seven or so steps;\\ +{\tt walk} --- update the map after each step;\\ +{\tt crawl} --- like {\it walk\/}, but pause briefly after each step. +%.ei +%.ed + +This option only affects the game's screen display, not the actual +results of moving. The default is {\it run\/}; versions prior to 3.4.1 +used {\it teleport\/} only. Whether or not the effect is noticeable will +depend upon the window port used or on the type of terminal. +%.lp +\item[\ib{safe\_pet}] +Prevent you from (knowingly) attacking your pets (default on). +%.lp +\item[\ib{scores}] +Control what parts of the score list you are shown at the end (ex.\ +``{\tt scores:5top scores/4around my score/own scores}''). Only the first +letter of each category (`{\tt t}', `{\tt a}' or `{\tt o}') is necessary. +%.lp +\item[\ib{showexp}] +Show your accumulated experience points on bottom line (default off). +%.lp +\item[\ib{showrace}] +Display yourself as the glyph for your race, rather than the glyph +for your role (default off). Note that this setting affects only +the appearance of the display, not the way the game treats you. +%.lp +\item[\ib{showscore}] +Show your approximate accumulated score on bottom line (default off). +%.lp +\item[\ib{silent}] +Suppress terminal beeps (default on). +%.lp +\item[\ib{sortpack}] +Sort the pack contents by type when displaying inventory (default on). +%.lp +\item[\ib{sound}] +Enable messages about what your character hears (default on). +Note that this has nothing to do with your computer's audio capabilities. +This option is only partly under player control. The game toggles it +off and on during and after sleep, for example. +%.lp +\item[\ib{standout}] +Boldface monsters and ``{\tt --More--}'' (default off). +%.lp +\item[\ib{sparkle}] +Display a sparkly effect when a monster (including yourself) is hit by an +attack to which it is resistant (default on). +%.lp +\item[\ib{suppress\_alert}] +This option may be set to a NetHack version level to suppress +alert notification messages about feature changes for that +and prior versions (ex.\ ``{\tt suppress\_alert:3.3.1}'') +%.lp +\item[\ib{time}] +Show the elapsed game time in turns on bottom line (default off). +%.lp +\item[\ib{timed\_delay}] +When pausing momentarily for display effect, such as with explosions and +moving objects, use a timer rather than sending extra characters to the +screen. (Applies to ``tty'' interface only; ``X11'' interface always +uses a timer based delay. The default is on if configured into the +program.) +%.lp +\item[\ib{tombstone}] +Draw a tombstone graphic upon your death (default on). +%.lp +\item[\ib{toptenwin}] +Put the ending display in a NetHack window instead of on stdout (default off). +Setting this option makes the score list visible when a windowing version +of NetHack is started without a parent window, but it no longer leaves +the score list around after game end on a terminal or emulating window. +%.lp +\item[\ib{traps}] +Set the graphics symbols for displaying traps (default +``\verb&^^^^^^^^^^^^^^^^^"^^^^&''). +The {\it traps\/} option should be followed by a string of 1--22 +characters to be used instead of the default traps characters. +This string is subjected to the same processing as the {\it dungeon\/} option. + +The order of the symbols is: +arrow trap, dart trap, falling rock trap, squeaky board, bear trap, +land mine, rolling boulder trap, sleeping gas trap, rust trap, fire trap, +pit, spiked pit, hole, trap door, teleportation trap, level teleporter, +magic portal, web, statue trap, magic trap, anti-magic field, polymorph trap. + +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{travel}] +Allow the travel command (default on). Turning this option off will +prevent the game from attempting unintended moves if you make inadvertent +mouse clicks on the map window. +%.lp +\item[\ib{verbose}] +Provide more commentary during the game (default on). +%.lp +\item[\ib{windowtype}] +Select which windowing system to use, such as ``{\tt tty}'' or ``{\tt X11}'' +(default depends on version). +Cannot be set with the `{\tt O}' command. +\elist + +%.hn 2 +\subsection*{Window Port Customization options} + +%.pg +Here are explanations of the various options that are +used to customize and change the characteristics of the +windowtype that you have chosen. +Character strings that are too long may be truncated. +Not all window ports will adjust for all settings listed +here. You can safely add any of these options to your +config file, and if the window port is capable of adjusting +to suit your preferences, it will attempt to do so. If it +can't it will silently ignore it. You can find out if an +option is supported by the window port that you are currently +using by checking to see if it shows up in the Options list. +Some options are dynamic and can be specified during the game +with the `{\tt O}' command. + +\blist{} +%.lp +\item[\ib{align\_message}] + Where to align or place the message window (top, bottom, left, or right) +%.lp +\item[\ib{align\_status}] + Where to align or place the status window (top, bottom, left, or right). +%.lp +\item[\ib{ascii\_map}] +NetHack should display an ascii map if it can. +%.lp +\item[\ib{color}] +NetHack should display color if it can for different monsters, +objects, and dungeon features +%.lp +\item[\ib{eight\_bit\_tty}] +Pass eight-bit character values (for example, specified with the {\it +traps \/} option) straight through to your terminal (default off). +%.lp +\item[\ib{font\_map}] +NetHack should use a font by the chosen name for the map window. +%.lp +\item[\ib{font\_menu}] +NetHack should use a font by the chosen name for menu windows. +%.lp +\item[\ib{font\_message}] +NetHack should use a font by the chosen name for the message window. +%.lp +\item[\ib{font\_status}] +NetHack should use a font by the chosen name for the status window. +%.lp +\item[\ib{font\_text}] +NetHack should use a font by the chosen name for text windows. +%.lp +\item[\ib{font\_size\_map}] +NetHack should use this size font for the map window. +%.lp +\item[\ib{font\_size\_menu}] +NetHack should use this size font for menu windows. +%.lp +\item[\ib{font\_size\_message}] +NetHack should use this size font for the message window. +%.lp +\item[\ib{font\_size\_status}] +NetHack should use this size font for the status window. +%.lp +\item[\ib{font\_size\_text}] +NetHack should use this size font for text windows. +%.lp +\item[\ib{fullscreen}] +NetHack should try and display on the entire screen rather than in a window. +%.lp +\item[\ib{hilite\_pet}] +Visually distinguish pets from similar animals (default off). +The behavior of this option depends on the type of windowing you use. +In text windowing, text highlighting or inverse video is often used; +with tiles, generally displays a heart symbol near pets. +%.lp +\item[\ib{large\_font}] +NetHack should use a large font. +%.lp +\item[\ib{map\_mode}] +NetHack should display the map in the manner specified. +%.lp +\item[\ib{mouse\_support}] +Allow use of the mouse for input and travel. +%.lp +\item[\ib{player\_selection}] +NetHack should pop up dialog boxes or use prompts for character selection. +%.lp +\item[\ib{popup\_dialog}] +NetHack should pop up dialog boxes for input. +%.lp +\item[\ib{preload\_tiles}] +NetHack should preload tiles into memory. +For example, in the protected mode MSDOS version, control whether tiles +get pre-loaded into RAM at the start of the game. Doing so +enhances performance of the tile graphics, but uses more memory. (default on). +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{scroll\_amount}] +NetHack should scroll the display by this number of cells +when the hero reaches the scroll\_margin. +%.lp +\item[\ib{scroll\_margin}] +NetHack should scroll the display when the hero or cursor +is this number of cells away from the edge of the window. +%.lp +\item[\ib{softkeyboard}] +Display an onscreen keyboard. Handhelds are most likely to support this option. +%.lp +\item[\ib{splash\_screen}] +NetHack should display an opening splash screen when it starts up (default yes). +%.lp +\item[\ib{tiled\_map}] +NetHack should display a tiled map if it can. +%.lp +\item[\ib{tile\_file}] +Specify the name of an alternative tile file to override the default. +%.lp +\item[\ib{tile\_height}] +Specify the preferred height of each tile in a tile capable port. +%.lp +\item[\ib{tile\_width}] +Specify the preferred width of each tile in a tile capable port +%.lp +\item[\ib{use\_inverse}] +NetHack should display inverse when the game specifies it. +%.lp +\item[\ib{vary\_msgcount}] +NetHack should display this number of messages at a time in the message window. +%.lp +\item[\ib{windowcolors}] +NetHack should display windows with the specified foreground/background +colors if it can. +%.lp +\item[\ib{wraptext}] +NetHack port should wrap long lines of text if they don't fit in +the visible area of the window. +\elist + +%.hn 2 +\subsection*{Platform-specific Customization options} + +%.pg +Here are explanations of options that are used by specific platforms +or ports to customize and change the port behavior. + +\blist{} +%.lp +\item[\ib{altkeyhandler}] +Select an alternate keystroke handler dll to load ({\it Win32 tty\/ NetHack\/} only). +The name of the handler is specified without the .dll extension and without any +path information. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{altmeta}] +(default on, {\it Amiga NetHack \/} only). +%.lp +\item[\ib{BIOS}] +Use BIOS calls to update the screen display quickly and to read the keyboard +(allowing the use of arrow keys to move) on machines with an IBM PC +compatible BIOS ROM (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +%.lp +\item[\ib{flush}] +(default off, {\it Amiga NetHack \/} only). +%.lp +\item[\ib{Macgraphics}] +(default on, {\it Mac NetHack \/} only). +%.lp +\item[\ib{page\_wait}] +(default off, {\it Mac NetHack \/} only). +%.lp +\item[\ib{rawio}] +Force raw (non-cbreak) mode for faster output and more +bulletproof input (MS-DOS sometimes treats `{\tt \^{}P}' as a printer toggle +without it) (default off, {\it OS/2, PC\/ {\rm and} ST NetHack\/} only). +Note: DEC Rainbows hang if this is turned on. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{soundcard}] +(default off, {\it PC NetHack \/} only). +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{subkeyvalue}] +({\it Win32 tty NetHack \/} only). +May be used to alter the value of keystrokes that the operating system +returns to NetHack to help compensate for international keyboard issues. +OPTIONS=subkeyvalue:171/92 +will return 92 to NetHack, if 171 was originally going to be returned. +You can use multiple subkeyvalue statements in the config file if needed. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{video}] +Set the video mode used ({\it PC\/ NetHack\/} only). +Values are {\it autodetect\/}, {\it default\/}, or {\it vga\/}. +Setting {\it vga\/} (or {\it autodetect\/} with vga hardware present) will cause +the game to display tiles. +Cannot be set with the `{\tt O}' command. +%.lp +\item[\ib{videocolors}] +\begin{sloppypar} +Set the color palette for PC systems using NO\_TERMS +(default 4-2-6-1-5-3-15-12-10-14-9-13-11, {\it PC\/ NetHack\/} only). +The order of colors is red, green, brown, blue, magenta, cyan, +bright.white, bright.red, bright.green, yellow, bright.blue, +bright.magenta, and bright.cyan. +Cannot be set with the `{\tt O}' command. +\end{sloppypar} +%.lp +\item[\ib{videoshades}] +Set the intensity level of the three gray scales available +(default dark normal light, {\it PC\/ NetHack\/} only). +If the game display is difficult to read, try adjusting these scales; +if this does not correct the problem, try {\tt !color}. +Cannot be set with the `{\tt O}' command. +\elist + +%.lp +%.hn 2 +\subsection*{Configuring autopickup exceptions} + +%.pg +There is an experimental compile time option called AUTOPICKUP_EXCEPTIONS. +If your copy of the game was built with that option defined, you can +further refine the behavior of the ``{\tt autopickup}'' option beyond +what is available through the ``{\tt pickup\_types}'' option. + +%.pg +By placing ``{\tt autopickup\_exception}'' lines in your configuration +file, you can define patterns to be checked when the game is about to +autopickup something. + +\blist{} +%.lp +\item[\ib{autopickup\_exception}] +Sets an exception to the `{\it pickup\_types}' option. +The {\it autopickup\_exception\/} option should be followed by a string of 1--80 +characters to be used as a pattern to match against the singular form +of the description of an object at your location. + +%.pg +You may use the following special characters in a pattern: + +\begin{verbatim} + *--- matches 0 or more characters. + ?--- matches any single character. +\end{verbatim} + +In addition, some characters are treated specially if they occur as the first +character in the specified string pattern, specifically: + +%.sd +%.si +{\tt <} --- always pickup an object that matches the pattern that follows.\\ +{\tt >} --- never pickup an object that matches the pattern that follows. +%.ei +%.ed + +Can be set with the `{\tt O}' command, but the setting is not preserved +across saves and restores. +\elist + +%.pg +Here's a couple of examples of autopickup\_exceptions: +\begin{verbatim} + autopickup_exception="<*arrow" + autopickup_exception=">*corpse" + autopickup_exception=">* cursed*" +\end{verbatim} + +The first example above will result in autopickup of any type of arrow. +The second example results in the exclusion of any corpse from autopickup. +The last example results in the exclusion of items known to be cursed from autopickup. +A `never pickup' rule takes precedence over an `always pickup' rule if both match. + +%.lp +%.hn 2 +\subsection*{Configuring User Sounds} + +%.pg +Some platforms allow you to define sound files to be played when a message +that matches a user-defined pattern is delivered to the message window. +At this time the Qt port and the win32tty and win32gui ports support the +use of user sounds. + +%.pg +The following config file entries are relevant to mapping user sounds +to messages: + +\blist{} +%.lp +\item[\ib{SOUNDDIR}] +The directory that houses the sound files to be played. +%.lp +\item[\ib{SOUND}] +An entry that maps a sound file to a user-specified message pattern. +Each SOUND entry is broken down into the following parts: + +%.sd +%.si +{\tt MESG } --- message window mapping (the only one supported in 3.4).\\ +{\tt pattern } --- the pattern to match.\\ +{\tt sound file} --- the sound file to play.\\ +{\tt volume } --- the volume to be set while playing the sound file. +%.ei +%.ed +\elist + +%.pg +The exact format for the pattern depends on whether the platform is +built to use {\it regular expressions \/} or NetHack's own internal pattern +matching facility. The {\it regular expressions \/} matching can be much more +sophisticated than the internal NetHack pattern matching, but requires +3rd party libraries on some platforms. There are plenty of references +available elsewhere for explaining {\it regular expressions \/}. You can verify +which pattern matching is used by your port with the +\#version command. + +%.pg +NetHack's internal pattern matching routine uses the following +special characters in its pattern matching: + +\begin{verbatim} + *--- matches 0 or more characters. + ?--- matches any single character. +\end{verbatim} + +%.pg +Here's an example of a sound mapping using NetHack's internal +pattern matching facility: +\begin{verbatim} + SOUND=MESG "*chime of a cash register*" "gong.wav" 50 +\end{verbatim} +specifies that any message with "chime of a cash register" contained +in it will trigger the playing of "gong.wav". You can have multiple +SOUND entries in your config file. + +%.lp +%.hn 2 +\subsection*{Configuring NetHack for Play by the Blind} + +%.pg +NetHack can be set up to use only standard ASCII characters for making +maps of the dungeons. This makes the MS-DOS versions of NetHack completely +accessible to the blind who use speech and/or Braille access technologies. +Players will require a good working knowledge of their screen-reader's +review features, and will have to know how to navigate horizontally and +vertically character by character. They will also find the search +capabilities of their screen-readers to be quite valuable. Be certain to +examine this Guidebook before playing so you have an idea what the screen +layout is like. You'll also need to be able to locate the PC cursor. It is +always where your character is located. Merely searching for an @-sign will +not always find your character since there are other humanoids represented +by the same sign. Your screen-reader should also have a function which +gives you the row and column of your review cursor and the PC cursor. +These co-ordinates are often useful in giving players a better sense of the +overall location of items on the screen. +%.pg +While it is not difficult for experienced users to edit the {\it defaults.nh\/} +file to accomplish this, novices may find this task somewhat daunting. +Included in all official distributions of NetHack is a file called +{\it NHAccess.nh\/}. Replacing {\it defaults.nh\/} with this file will cause +the game to run in a manner accessible to the blind. After you have gained +some experience with the game and with editing files, you may want to alter +settings to better suit your preferences. Instructions on how to do this +are included in the {\it NHAccess.nh\/} file itself. The most crucial +settings to make the game accessible are: +%.pg +\blist{} +%.lp +\item[\ib{IBMgraphics}] +Disable IBMgraphics by commenting out this option. +%.lp +\item[\ib{menustyle:traditional}] +This will assist in the interface to speech synthesizers. +%.lp +\item[\ib{number\_pad}] +A lot of speech access programs use the number-pad to review the screen. +If this is the case, disable the number\_pad option and use the traditional +Rogue-like commands. +%.lp +\item[\ib{Character graphics}] +Comment out all character graphics sets found near the bottom of the +{\it defaults.nh\/} file. Most of these replace {\it NetHack\/}'s +default representation of the dungeon using standard ASCII characters +with fancier characters from extended character sets, and these fancier +characters can annoy screen-readers. +\elist + +%.hn 1 +\section{Scoring} + +%.pg +{\it NetHack\/} maintains a list of the top scores or scorers on your machine, +depending on how it is set up. In the latter case, each account on +the machine can post only one non-winning score on this list. If +you score higher than someone else on this list, or better your +previous score, you will be inserted in the proper place under your +current name. How many scores are kept can also be set up when +{\it NetHack\/} is compiled. + +%.pg +Your score is chiefly based upon how much experience you gained, how +much loot you accumulated, how deep you explored, and how the game +ended. If you quit the game, you escape with all of your gold intact. +If, however, you get killed in the Mazes of Menace, the guild will +only hear about 90\,\% of your gold when your corpse is discovered +(adventurers have been known to collect finder's fees). So, consider +whether you want to take one last hit at that monster and possibly +live, or quit and stop with whatever you have. If you quit, you keep +all your gold, but if you swing and live, you might find more. + +%.pg +If you just want to see what the current top players/games list is, you +can type +\begin{verbatim} + nethack -s all +\end{verbatim} +on most versions. + +%.hn 1 +\section{Explore mode} + +%.pg +{\it NetHack\/} is an intricate and difficult game. Novices might falter +in fear, aware of their ignorance of the means to survive. Well, fear +not. Your dungeon may come equipped with an ``explore'' or ``discovery'' +mode that enables you to keep old save files and cheat death, at the +paltry cost of not getting on the high score list. + +%.pg +There are two ways of enabling explore mode. One is to start the game +with the {\tt -X} +switch. The other is to issue the `{\tt X}' command while already playing +the game. The other benefits of explore mode are left for the trepid +reader to discover. + +%.hn +\section{Credits} +%.pg +The original % +{\it hack\/} game was modeled on the Berkeley +%.ux +UNIX +{\it rogue\/} game. Large portions of this paper were shamelessly +cribbed from % +{\it A Guide to the Dungeons of Doom}, by Michael C. Toy +and Kenneth C. R. C. Arnold. Small portions were adapted from +{\it Further Exploration of the Dungeons of Doom}, by Ken Arromdee. + +%.pg +{\it NetHack\/} is the product of literally dozens of people's work. +Main events in the course of the game development are described below: + +%.pg +\bigskip +\nd {\it Jay Fenlason\/} wrote the original {\it Hack\/} with help from {\it +Kenny Woodland}, {\it Mike Thome}, and {\it Jon Payne}. + +%.pg +\medskip +\nd {\it Andries Brouwer\/} did a major re-write, transforming {\it Hack\/} +into a very different game, and published (at least) three versions (1.0.1, +1.0.2, and 1.0.3) for UNIX machines to the Usenet. + +%.pg +\medskip +\nd {\it Don G. Kneller\/} ported {\it Hack\/} 1.0.3 to Microsoft C and MS-DOS, +producing {\it PC Hack\/} 1.01e, added support for DEC Rainbow graphics in +version 1.03g, and went on to produce at least four more versions (3.0, 3.2, +3.51, and 3.6). + +%.pg +\medskip +\nd {\it R. Black\/} ported {\it PC Hack\/} 3.51 to Lattice C and the Atari +520/1040ST, producing {\it ST Hack\/} 1.03. + +%.pg +\medskip +\nd {\it Mike Stephenson\/} merged these various versions back together, +incorporating many of the added features, and produced {\it NetHack\/} version +1.4. He then coordinated a cast of thousands in enhancing and debugging +{\it NetHack\/} 1.4 and released {\it NetHack\/} versions 2.2 and 2.3. + +%.pg +\medskip +\nd Later, Mike coordinated a major rewrite of the game, heading a team which +included {\it Ken Arromdee}, {\it Jean-Christophe Collet}, {\it Steve Creps}, +{\it Eric Hendrickson}, {\it Izchak Miller}, {\it Eric S. Raymond}, {\it John +Rupley}, {\it Mike Threepoint}, and {\it Janet Walz}, to produce {\it +NetHack\/} 3.0c. + +%.pg +\medskip +\nd {\it NetHack\/} 3.0 was ported to the Atari by {\it Eric R. Smith}, to OS/2 by +{\it Timo Hakulinen}, and to VMS by {\it David Gentzel}. The three of them +and {\it Kevin Darcy\/} later joined the main development team to produce +subsequent revisions of 3.0. + +%.pg +\medskip +\nd {\it Olaf Seibert\/} ported {\it NetHack\/} 2.3 and 3.0 to the Amiga. {\it +Norm Meluch}, {\it Stephen Spackman\/} and {\it Pierre Martineau\/} designed +overlay code for {\it PC NetHack\/} 3.0. {\it Johnny Lee\/} ported {\it +NetHack\/} 3.0 to the Macintosh. Along with various other Dungeoneers, they +continued to enhance the PC, Macintosh, and Amiga ports through the later +revisions of 3.0. + +%.pg +\medskip +\nd Headed by {\it Mike Stephenson\/} and coordinated by {\it Izchak Miller\/} and +{\it Janet Walz}, the development team which now included {\it Ken Arromdee}, +{\it David Cohrs}, {\it Jean-Christophe Collet}, {\it Kevin Darcy}, +{\it Matt Day}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, +{\it Pat Rankin}, {\it Eric Raymond}, and {\it Eric Smith\/} undertook a radical +revision of 3.0. They re-structured the game's design, and re-wrote major +parts of the code. They added multiple dungeons, a new display, special +individual character quests, a new endgame and many other new features, and +produced {\it NetHack\/} 3.1. + +%.pg +\medskip +\nd {\it Ken Lorber}, {\it Gregg Wonderly\/} and {\it Greg Olson}, with help +from {\it Richard Addison}, {\it Mike Passaretti}, and {\it Olaf Seibert}, +developed {\it NetHack\/} 3.1 for the Amiga. + +%.pg +\medskip +\nd {\it Norm Meluch\/} and {\it Kevin Smolkowski}, with help from +{\it Carl Schelin}, {\it Stephen Spackman}, {\it Steve VanDevender}, +and {\it Paul Winner}, ported {\it NetHack\/} 3.1 to the PC. + +%.pg +\medskip +\nd {\it Jon W\{tte} and {\it Hao-yang Wang}, +with help from {\it Ross Brown}, {\it Mike Engber}, {\it David Hairston}, +{\it Michael Hamel}, {\it Jonathan Handler}, {\it Johnny Lee}, +{\it Tim Lennan}, {\it Rob Menke}, and {\it Andy Swanson}, +developed {\it NetHack\/} 3.1 for the Macintosh, porting it for MPW. +Building on their development, {\it Barton House} added a Think C port. + +%.pg +\medskip +\nd {\it Timo Hakulinen\/} ported {\it NetHack\/} 3.1 to OS/2. +{\it Eric Smith\/} ported {\it NetHack\/} 3.1 to the Atari. +{\it Pat Rankin}, with help from {\it Joshua Delahunty}, +was responsible for the VMS version of {\it NetHack\/} 3.1. +{\it Michael Allison} ported {\it NetHack\/} 3.1 to Windows NT. + +%.pg +\medskip +\nd {\it Dean Luick}, with help from {\it David Cohrs}, developed {\it NetHack\/} +3.1 for X11. +{\it Warwick Allison} wrote a tiled version of NetHack for the Atari; +he later contributed the tiles to the DevTeam and tile support was +then added to other platforms. + +%.pg +\medskip +\nd The 3.2 development team, comprised of {\it Michael Allison}, {\it Ken +Arromdee}, {\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it +Kevin Darcy}, {\it Timo Hakulinen}, {\it Steve Linhart}, {\it Dean Luick}, +{\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, {\it Janet Walz}, +and {\it Paul Winner}, released version 3.2 in April of 1996. + +%.pg +\medskip +\nd Version 3.2 marked the tenth anniversary of the formation of the development +team. In a testament to their dedication to the game, all thirteen members +of the original development team remained on the team at the start of work on +that release. During the interval between the release of 3.1.3 and 3.2, +one of the founding members of the development team, {\it Dr. Izchak Miller}, +was diagnosed with cancer and passed away. That release of the game was +dedicated to him by the development and porting teams. + +%.pg +\medskip +During the lifespan of {\it NetHack\/} 3.1 and 3.2, several enthusiasts +of the game added +their own modifications to the game and made these ``variants'' publicly +available: + +%.pg +\medskip +{\it Tom Proudfoot} and {\it Yuval Oren} created {\it NetHack++}, +which was quickly renamed {\it NetHack$--$}. +Working independently, {\it Stephen White} wrote {\it NetHack Plus}. +{\it Tom Proudfoot} later merged {\it NetHack Plus} +and his own {\it NetHack$--$} to produce {\it SLASH}. +{\it Larry Stewart-Zerba} and {\it Warwick Allison} improved the spell +casting system with the Wizard Patch. +{\it Warwick Allison} also ported NetHack to use the Qt interface. + +%.pg +\medskip +{\it Warren Cheung} combined {\it SLASH} with the Wizard Patch +to produce {\it Slash'em\/}, and +with the help of {\it Kevin Hugo}, added more features. +Kevin later joined the +DevTeam and incorporated the best of these ideas into NetHack 3.3. + +%.pg +\medskip +The final update to 3.2 was the bug fix release 3.2.3, which was released +simultaneously with 3.3.0 in December 1999 just in time for the Year 2000. + +%.pg +\medskip +The 3.3 development team, consisting of {\it Michael Allison}, {\it Ken Arromdee}, +{\it David Cohrs}, {\it Jessie Collet}, {\it Steve Creps}, {\it Kevin Darcy}, +{\it Timo Hakulinen}, {\it Kevin Hugo}, {\it Steve Linhart}, {\it Ken Lorber}, +{\it Dean Luick}, {\it Pat Rankin}, {\it Eric Smith}, {\it Mike Stephenson}, +{\it Janet Walz}, and {\it Paul Winner}, released 3.3.0 in +December 1999 and 3.3.1 in August of 2000. + +%.pg +\medskip +Version 3.3 offered many firsts. It was the first version to separate race +and profession. The Elf class was removed in preference to an elf race, +and the races of dwarves, gnomes, and orcs made their first appearance in +the game alongside the familiar human race. Monk and Ranger roles joined +Archeologists, Barbarians, Cavemen, Healers, Knights, Priests, Rogues, Samurai, +Tourists, Valkyries and of course, Wizards. It was also the first version +to allow you to ride a steed, and was the first version to have a publicly +available web-site listing all the bugs that had been discovered. Despite +that constantly growing bug list, 3.3 proved stable enough to last for +more than a year and a half. + +%.pg +\medskip +The 3.4 development team initially consisted of +{\it Michael Allison}, {\it Ken Arromdee}, +{\it David Cohrs}, {\it Jessie Collet}, {\it Kevin Hugo}, {\it Ken Lorber}, +{\it Dean Luick}, {\it Pat Rankin}, {\it Mike Stephenson}, +{\it Janet Walz}, and {\it Paul Winner}, with {\it Warwick Allison} joining +just before the release of NetHack 3.4.0 in March 2002. + +%.pg +\medskip +As with version 3.3, various people contributed to the game as a whole as +well as supporting ports on the different platforms that {\it NetHack\/} +runs on: + +%.pg +\medskip +\nd{\it Pat Rankin} maintained 3.4 for VMS. + +%.pg +\medskip +\nd {\it Michael Allison} maintained NetHack 3.4 for the MS-DOS platform. +{\it Paul Winner} and {\it Yitzhak Sapir} provided encouragement. + +%.pg +\medskip +\nd {\it Dean Luick}, {\it Mark Modrall}, and {\it Kevin Hugo} maintained and +enhanced the Macintosh port of 3.4. + +%.pg +\medskip +\nd {\it Michael Allison}, {\it David Cohrs}, {\it Alex Kompel}, {\it Dion Nicolaas}, and +{\it Yitzhak Sapir} maintained and enhanced 3.4 for the Microsoft Windows platform. +{\it Alex Kompel} contributed a new graphical interface for the Windows port. +{\it Alex Kompel} also contributed a Windows CE port for 3.4.1. + +%.pg +\medskip +\nd {\it Ron Van Iwaarden} maintained 3.4 for OS/2. + +%.pg +\medskip +\nd {\it Janne Salmij\"{a}rvi} and {\it Teemu Suikki} maintained +and enhanced the Amiga port of 3.4 after {\it Janne Salmij\"{a}rvi} resurrected +it for 3.3.1. + +%.pg +\medskip +\nd {\it Christian ``Marvin'' Bressler} maintained 3.4 for the Atari after he +resurrected it for 3.3.1. + +%.pg +\medskip +\nd There is a NetHack web site maintained by {\it Ken Lorber} at +http:{\tt /}{\tt /}www.nethack.org{\tt /}. + +%.pg +\bigskip +\nd From time to time, some depraved individual out there in netland sends a +particularly intriguing modification to help out with the game. The Gods of +the Dungeon sometimes make note of the names of the worst of these miscreants +in this, the list of Dungeoneers: + +%.sd +\begin{center} +\begin{tabular}{lll} +%TABLE_START +Adam Aronow & Izchak Miller & Mike Stephenson\\ +Alex Kompel & J. Ali Harlow & Norm Meluch\\ +Andreas Dorn & Janet Walz & Olaf Seibert\\ +Andy Church & Janne Salmij\"{a}rvi & Pasi Kallinen\\ +Andy Swanson & Jean-Christophe Collet & Pat Rankin\\ +Ari Huttunen & Jochen Erwied & Paul Winner\\ +Barton House & John Kallen & Pierre Martineau\\ +Benson I. Margulies & John Rupley & Ralf Brown\\ +Bill Dyer & John S. Bien & Ray Chason\\ +Boudewijn Waijers & Johnny Lee & Richard Addison\\ +Bruce Cox & Jon W\{tte & Richard Beigel\\ +Bruce Holloway & Jonathan Handler & Richard P. Hughey\\ +Bruce Mewborne & Joshua Delahunty & Rob Menke\\ +Carl Schelin & Keizo Yamamoto & Robin Johnson\\ +Chris Russo & Ken Arnold & Roderick Schertler\\ +David Cohrs & Ken Arromdee & Roland McGrath\\ +David Damerell & Ken Lorber & Ron Van Iwaarden\\ +David Gentzel & Ken Washikita & Ronnen Miller\\ +David Hairston & Kevin Darcy & Ross Brown\\ +Dean Luick & Kevin Hugo & Sascha Wostmann\\ +Del Lamb & Kevin Sitze & Scott Bigham\\ +Deron Meranda & Kevin Smolkowski & Scott R. Turner\\ +Dion Nicolaas & Kevin Sweet & Stephen Spackman\\ +Dylan O'Donnell & Lars Huttar & Stephen White\\ +Eric Backus & Malcolm Ryan & Steve Creps\\ +Eric Hendrickson & Mark Gooderum & Steve Linhart\\ +Eric R. Smith & Mark Modrall & Steve VanDevender\\ +Eric S. Raymond & Marvin Bressler & Teemu Suikki\\ +Erik Andersen & Matthew Day & Tim Lennan\\ +Frederick Roeber & Merlyn LeRoy & Timo Hakulinen\\ +Gil Neiger & Michael Allison & Tom Almy\\ +Greg Laskin & Michael Feir & Tom West\\ +Greg Olson & Michael Hamel & Warren Cheung\\ +Gregg Wonderly & Michael Sokolov & Warwick Allison\\ +Hao-yang Wang & Mike Engber & Yitzhak Sapir\\ +Helge Hafting & Mike Gallop\\ +Irina Rempt-Drijfhout & Mike Passaretti +%TABLE_END Do not delete this line. +\end{tabular} +\end{center} +%.ed + +%\vfill +%\begin{flushleft} +%\small +%Microsoft and MS-DOS are registered trademarks of Microsoft Corporation.\\ +%%%Don't need next line if a UNIX macro automatically inserts footnotes. +%UNIX is a registered trademark of AT\&T.\\ +%Lattice is a trademark of Lattice, Inc.\\ +%Atari and 1040ST are trademarks of Atari, Inc.\\ +%AMIGA is a trademark of Commodore-Amiga, Inc.\\ +%%.sm +%Brand and product names are trademarks or registered trademarks +%of their respective holders. +%\end{flushleft} + +\end{document} diff --git a/doc/Guidebook.txt b/doc/Guidebook.txt new file mode 100644 index 0000000..f0f9f7d --- /dev/null +++ b/doc/Guidebook.txt @@ -0,0 +1,3630 @@ + + + + + + + + + + + A Guide to the Mazes of Menace + (Guidebook for NetHack) + + + Eric S. Raymond + (Extensively edited and expanded for 3.4) + + + + 1. Introduction + + Recently, you have begun to find yourself unfulfilled and distant + in your daily occupation. Strange dreams of prospecting, steal- + ing, crusading, and combat have haunted you in your sleep for + many months, but you aren't sure of the reason. You wonder + whether you have in fact been having those dreams all your life, + and somehow managed to forget about them until now. Some nights + you awaken suddenly and cry out, terrified at the vivid recollec- + tion of the strange and powerful creatures that seem to be lurk- + ing behind every corner of the dungeon in your dream. Could + these details haunting your dreams be real? As each night pass- + es, you feel the desire to enter the mysterious caverns near the + ruins grow stronger. Each morning, however, you quickly put the + idea out of your head as you recall the tales of those who en- + tered the caverns before you and did not return. Eventually you + can resist the yearning to seek out the fantastic place in your + dreams no longer. After all, when other adventurers came back + this way after spending time in the caverns, they usually seemed + better off than when they passed through the first time. And who + was to say that all of those who did not return had not just kept + going? + + + Asking around, you hear about a bauble, called the Amulet of + Yendor by some, which, if you can find it, will bring you great + wealth. One legend you were told even mentioned that the one who + finds the amulet will be granted immortality by the gods. The + amulet is rumored to be somewhere beyond the Valley of Gehennom, + deep within the Mazes of Menace. Upon hearing the legends, you + immediately realize that there is some profound and undiscovered + reason that you are to descend into the caverns and seek out that + amulet of which they spoke. Even if the rumors of the amulet's + powers are untrue, you decide that you should at least be able to + sell the tales of your adventures to the local minstrels for a + tidy sum, especially if you encounter any of the terrifying and + magical creatures of your dreams along the way. You spend one + last night fortifying yourself at the local inn, becoming more + and more depressed as you watch the odds of your success being + posted on the inn's walls getting lower and lower. + + + + NetHack Guidebook 1 + + + + + + NetHack Guidebook 2 + + + + In the morning you awake, collect your belongings, and set + off for the dungeon. After several days of uneventful travel, + you see the ancient ruins that mark the entrance to the Mazes of + Menace. It is late at night, so you make camp at the entrance + and spend the night sleeping under the open skies. In the morn- + ing, you gather your gear, eat what may be your last meal out- + side, and enter the dungeon... + + + 2. What is going on here? + + You have just begun a game of NetHack. Your goal is to grab + as much treasure as you can, retrieve the Amulet of Yendor, and + escape the Mazes of Menace alive. + + Your abilities and strengths for dealing with the hazards of + adventure will vary with your background and training: + + Archeologists understand dungeons pretty well; this enables + them to move quickly and sneak up on the local nasties. They + start equipped with the tools for a proper scientific expedition. + + Barbarians are warriors out of the hinterland, hardened to + battle. They begin their quests with naught but uncommon + strength, a trusty hauberk, and a great two-handed sword. + + Cavemen and Cavewomen start with exceptional strength but, + unfortunately, with neolithic weapons. + + Healers are wise in medicine and apothecary. They know the + herbs and simples that can restore vitality, ease pain, anes- + thetize, and neutralize poisons; and with their instruments, they + can divine a being's state of health or sickness. Their medical + practice earns them quite reasonable amounts of money, with which + they enter the dungeon. + + Knights are distinguished from the common skirmisher by + their devotion to the ideals of chivalry and by the surpassing + excellence of their armor. + + Monks are ascetics, who by rigorous practice of physical and + mental disciplines have become capable of fighting as effectively + without weapons as with. They wear no armor but make up for it + with increased mobility. + + Priests and Priestesses are clerics militant, crusaders ad- + vancing the cause of righteousness with arms, armor, and arts + thaumaturgic. Their ability to commune with deities via prayer + occasionally extricates them from peril, but can also put them in + it. + + Rangers are most at home in the woods, and some say slightly + out of place in a dungeon. They are, however, experts in archery + as well as tracking and stealthy movement. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 3 + + + + Rogues are agile and stealthy thieves, with knowledge of + locks, traps, and poisons. Their advantage lies in surprise, + which they employ to great advantage. + + Samurai are the elite warriors of feudal Nippon. They are + lightly armored and quick, and wear the dai-sho, two swords of + the deadliest keenness. + + Tourists start out with lots of gold (suitable for shopping + with), a credit card, lots of food, some maps, and an expensive + camera. Most monsters don't like being photographed. + + Valkyries are hardy warrior women. Their upbringing in the + harsh Northlands makes them strong, inures them to extremes of + cold, and instills in them stealth and cunning. + + Wizards start out with a knowledge of magic, a selection of + magical items, and a particular affinity for dweomercraft. Al- + though seemingly weak and easy to overcome at first sight, an ex- + perienced Wizard is a deadly foe. + + You may also choose the race of your character: + + Dwarves are smaller than humans or elves, but are stocky and + solid individuals. Dwarves' most notable trait is their great + expertise in mining and metalwork. Dwarvish armor is said to be + second in quality not even to the mithril armor of the Elves. + + Elves are agile, quick, and perceptive; very little of what + goes on will escape an Elf. The quality of Elven craftsmanship + often gives them an advantage in arms and armor. + + Gnomes are smaller than but generally similar to dwarves. + Gnomes are known to be expert miners, and it is known that a se- + cret underground mine complex built by this race exists within + the Mazes of Menace, filled with both riches and danger. + + Humans are by far the most common race of the surface world, + and are thus the norm by which other races are often compared. + Although they have no special abilities, they can succeed in any + role. + + Orcs are a cruel and barbaric race that hate every living + thing (including other orcs). Above all others, Orcs hate Elves + with a passion unequalled, and will go out of their way to kill + one at any opportunity. The armor and weapons fashioned by the + Orcs are typically of inferior quality. + + 3. What do all those things on the screen mean? + + On the screen is kept a map of where you have been and what + you have seen on the current dungeon level; as you explore more + of the level, it appears on the screen in front of you. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 4 + + + + When NetHack's ancestor rogue first appeared, its screen + orientation was almost unique among computer fantasy games. + Since then, screen orientation has become the norm rather than + the exception; NetHack continues this fine tradition. Unlike + text adventure games that accept commands in pseudo-English sen- + tences and explain the results in words, NetHack commands are all + one or two keystrokes and the results are displayed graphically + on the screen. A minimum screen size of 24 lines by 80 columns + is recommended; if the screen is larger, only a 21x80 section + will be used for the map. + + NetHack can even be played by blind players, with the assis- + tance of Braille readers or speech synthesisers. Instructions + for configuring NetHack for the blind are included later in this + document. + + NetHack generates a new dungeon every time you play it; even + the authors still find it an entertaining and exciting game de- + spite having won several times. + + NetHack offers a variety of display options. The options + available to you will vary from port to port, depending on the + capabilities of your hardware and software, and whether various + compile-time options were enabled when your executable was creat- + ed. The three possible display options are: a monochrome charac- + ter interface, a color character interface, and a graphical in- + terface using small pictures called tiles. The two character in- + terfaces allow fonts with other characters to be substituted, but + the default assignments use standard ASCII characters to repre- + sent everything. There is no difference between the various dis- + play options with respect to game play. Because we cannot repro- + duce the tiles or colors in the Guidebook, and because it is com- + mon to all ports, we will use the default ASCII characters from + the monochrome character display when referring to things you + might see on the screen during your game. + + In order to understand what is going on in NetHack, first + you must understand what NetHack is doing with the screen. The + NetHack screen replaces the ``You see ...'' descriptions of text + adventure games. Figure 1 is a sample of what a NetHack screen + might look like. The way the screen looks for you depends on + your platform. + + -------------------------------------------------------------------- + The bat bites! + + ------ + |....| ---------- + |.<..|####...@...$.| + |....-# |...B....+ + |....| |.d......| + ------ -------|-- + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 5 + + + + + + Player the Rambler St:12 Dx:7 Co:18 In:11 Wi:9 Ch:15 Neutral + Dlvl:1 $:0 HP:9(12) Pw:3(3) AC:10 Exp:1/19 T:257 Weak + + -------------------------------------------------------------------- + Figure 1 + + + 3.1. The status lines (bottom) + + The bottom two lines of the screen contain several cryptic + pieces of information describing your current status. If either + status line becomes longer than the width of the screen, you + might not see all of it. Here are explanations of what the vari- + ous status items mean (though your configuration may not have all + the status items listed below): + + Rank + Your character's name and professional ranking (based on the + experience level, see below). + + Strength + A measure of your character's strength; one of your six ba- + sic attributes. A human character's attributes can range + from 3 to 18 inclusive; non-humans may exceed these limits + (occasionally you may get super-strengths of the form 18/xx, + and magic can also cause attributes to exceed the normal + limits). The higher your strength, the stronger you are. + Strength affects how successfully you perform physical + tasks, how much damage you do in combat, and how much loot + you can carry. + + Dexterity + Dexterity affects your chances to hit in combat, to avoid + traps, and do other tasks requiring agility or manipulation + of objects. + + Constitution + Constitution affects your ability to recover from injuries + and other strains on your stamina. + + Intelligence + Intelligence affects your ability to cast spells and read + spellbooks. + + Wisdom + Wisdom comes from your practical experience (especially when + dealing with magic). It affects your magical energy. + + Charisma + Charisma affects how certain creatures react toward you. In + particular, it can affect the prices shopkeepers offer you. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 6 + + + + Alignment + Lawful, Neutral, or Chaotic. Often, Lawful is taken as good + and Chaotic as evil, but legal and ethical do not always co- + incide. Your alignment influences how other monsters react + toward you. Monsters of a like alignment are more likely to + be non-aggressive, while those of an opposing alignment are + more likely to be seriously offended at your presence. + + Dungeon Level + How deep you are in the dungeon. You start at level one and + the number increases as you go deeper into the dungeon. + Some levels are special, and are identified by a name and + not a number. The Amulet of Yendor is reputed to be some- + where beneath the twentieth level. + + Gold + The number of gold pieces you are openly carrying. Gold + which you have concealed in containers is not counted. + + Hit Points + Your current and maximum hit points. Hit points indicate + how much damage you can take before you die. The more you + get hit in a fight, the lower they get. You can regain hit + points by resting, or by using certain magical items or + spells. The number in parentheses is the maximum number + your hit points can reach. + + Power + Spell points. This tells you how much mystic energy (mana) + you have available for spell casting. Again, resting will + regenerate the amount available. + + Armor Class + A measure of how effectively your armor stops blows from un- + friendly creatures. The lower this number is, the more ef- + fective the armor; it is quite possible to have negative ar- + mor class. + + Experience + Your current experience level and experience points. As you + adventure, you gain experience points. At certain experi- + ence point totals, you gain an experience level. The more + experienced you are, the better you fight and withstand mag- + ical attacks. Many dungeons show only your experience level + here. + + Time + The number of turns elapsed so far, displayed if you have + the time option set. + + Hunger status + Your current hunger status, ranging from Satiated down to + Fainting. If your hunger status is normal, it is not dis- + played. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 7 + + + + Additional status flags may appear after the hunger status: + Conf when you're confused, FoodPois or Ill when sick, Blind when + you can't see, Stun when stunned, and Hallu when hallucinating. + + 3.2. The message line (top) + + The top line of the screen is reserved for messages that de- + scribe things that are impossible to represent visually. If you + see a ``--More--'' on the top line, this means that NetHack has + another message to display on the screen, but it wants to make + certain that you've read the one that is there first. To read + the next message, just press the space bar. + + 3.3. The map (rest of the screen) + + The rest of the screen is the map of the level as you have + explored it so far. Each symbol on the screen represents some- + thing. You can set various graphics options to change some of + the symbols the game uses; otherwise, the game will use default + symbols. Here is a list of what the default symbols mean: + + - and | + The walls of a room, or an open door. Or a grave (|). + + . The floor of a room, ice, or a doorless doorway. + + # A corridor, or iron bars, or a tree, or possibly a kitchen + sink (if your dungeon has sinks), or a drawbridge. + + > Stairs down: a way to the next level. + + < Stairs up: a way to the previous level. + + + A closed door, or a spellbook containing a spell you may be + able to learn. + + @ Your character or a human. + + $ A pile of gold. + + ^ A trap (once you have detected it). + + ) A weapon. + + [ A suit or piece of armor. + + % Something edible (not necessarily healthy). + + ? A scroll. + + / A wand. + + = A ring. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 8 + + + + ! A potion. + + ( A useful item (pick-axe, key, lamp...). + + " An amulet or a spider web. + + * A gem or rock (possibly valuable, possibly worthless). + + ` A boulder or statue. + + 0 An iron ball. + + _ An altar, or an iron chain. + + { A fountain. + + } A pool of water or moat or a pool of lava. + + \ An opulent throne. + + a-zA-Z and other symbols + Letters and certain other symbols represent the various in- + habitants of the Mazes of Menace. Watch out, they can be + nasty and vicious. Sometimes, however, they can be helpful. + + I This marks the last known location of an invisible or other- + wise unseen monster. Note that the monster could have + moved. The 'F' and 'm' commands may be useful here. + + You need not memorize all these symbols; you can ask the + game what any symbol represents with the `/' command (see the + next section for more info). + + + 4. Commands + + Commands are initiated by typing one or two characters. + Some commands, like ``search'', do not require that any more in- + formation be collected by NetHack. Other commands might require + additional information, for example a direction, or an object to + be used. For those commands that require additional information, + NetHack will present you with either a menu of choices or with a + command line prompt requesting information. Which you are pre- + sented with will depend chiefly on how you have set the menustyle + option. + + For example, a common question, in the form ``What do you + want to use? [a-zA-Z ?*]'', asks you to choose an object you are + carrying. Here, ``a-zA-Z'' are the inventory letters of your + possible choices. Typing `?' gives you an inventory list of + these items, so you can see what each letter refers to. In this + example, there is also a `*' indicating that you may choose an + object not on the list, if you wanted to use something unexpect- + ed. Typing a `*' lists your entire inventory, so you can see the + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 9 + + + + inventory letters of every object you're carrying. Finally, if + you change your mind and decide you don't want to do this command + after all, you can press the ESC key to abort the command. + + You can put a number before some commands to repeat them + that many times; for example, ``10s'' will search ten times. If + you have the number_pad option set, you must type `n' to prefix a + count, so the example above would be typed ``n10s'' instead. + Commands for which counts make no sense ignore them. In addi- + tion, movement commands can be prefixed for greater control (see + below). To cancel a count or a prefix, press the ESC key. + + The list of commands is rather long, but it can be read at + any time during the game through the `?' command, which accesses + a menu of helpful texts. Here are the commands for your refer- + ence: + + ? Help menu: display one of several help texts available. + + / Tell what a symbol represents. You may choose to specify a + location or type a symbol (or even a whole word) to explain. + Specifying a location is done by moving the cursor to a par- + ticular spot on the map and then pressing one of `.', `,', + `;', or `:'. `.' will explain the symbol at the chosen lo- + cation, conditionally check for ``More info?'' depending up- + on whether the help option is on, and then you will be asked + to pick another location; `,' will explain the symbol but + skip any additional information; `;' will skip additional + info and also not bother asking you to choose another loca- + tion to examine; `:' will show additional info, if any, + without asking for confirmation. When picking a location, + pressing the ESC key will terminate this command, or press- + ing `?' will give a brief reminder about how it works. + + Specifying a name rather than a location always gives any + additional information available about that name. + + & Tell what a command does. + + < Go up to the previous level (if you are on a staircase or + ladder). + + > Go down to the next level (if you are on a staircase or lad- + der). + + [yuhjklbn] + Go one step in the direction indicated (see Figure 2). If + you sense or remember a monster there, you will fight the + monster instead. Only these one-step movement commands + cause you to fight monsters; the others (below) are + ``safe.'' + + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 10 + + + + y k u 7 8 9 + \ | / \ | / + h- . -l 4- . -6 + / | \ / | \ + b j n 1 2 3 + (if number_pad is set) + + Figure 2 + + + [YUHJKLBN] + Go in that direction until you hit a wall or run into some- + thing. + + m[yuhjklbn] + Prefix: move without picking up objects or fighting (even + if you remember a monster there) + + F[yuhjklbn] + Prefix: fight a monster (even if you only guess one is + there) + + M[yuhjklbn] + Prefix: move far, no pickup. + + g[yuhjklbn] + Prefix: move until something interesting is found. + + G[yuhjklbn] or [yuhjklbn] + Prefix: same as `g', but forking of corridors is not con- + sidered interesting. + + _ Travel to a map location via a shortest-path algorithm. The + shortest path is computed over map locations the hero knows + about (e.g. seen or previously traversed). If there is no + known path, a guess is made instead. Stops on most of the + same conditions as the `G' command, but without picking up + objects, similar to the `M' command. For ports with mouse + support, the command is also invoked when a mouse-click + takes place on a location other than the current position. + + . Rest, do nothing for one turn. + + a Apply (use) a tool (pick-axe, key, lamp...). + + A Remove one or more worn items, such as armor. Use `T' (take + off) to take off only one piece of armor or `R' (remove) to + take off only one accessory. + + ^A Redo the previous command. + + c Close a door. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 11 + + + + C Call (name) an individual monster. + + ^C Panic button. Quit the game. + + d Drop something. Ex. ``d7a'' means drop seven items of ob- + ject a. + + D Drop several things. In answer to the question ``What kinds + of things do you want to drop? [!%= BUCXaium]'' you should + type zero or more object symbols possibly followed by `a' + and/or `i' and/or `u' and/or `m'. In addition, one or more + of the blessed/uncursed/cursed groups may be typed. + + DB - drop all objects known to be blessed. + DU - drop all objects known to be uncursed. + DC - drop all objects known to be cursed. + DX - drop all objects of unknown B/U/C status. + Da - drop all objects, without asking for confirmation. + Di - examine your inventory before dropping anything. + Du - drop only unpaid objects (when in a shop). + Dm - use a menu to pick which object(s) to drop. + D%u - drop only unpaid food. + + ^D Kick something (usually a door). + + e Eat food. + + E Engrave a message on the floor. Engraving the word + ``Elbereth'' will cause most monsters to not attack you + hand-to-hand (but if you attack, you will rub it out); this + is often useful to give yourself a breather. (This feature + may be compiled out of the game, so your version might not + have it.) + + E- - write in the dust with your fingers. + + f Fire one of the objects placed in your quiver. You may se- + lect ammunition with a previous `Q' command, or let the com- + puter pick something appropriate if autoquiver is true. + + i List your inventory (everything you're carrying). + + I List selected parts of your inventory. + + I* - list all gems in inventory; + Iu - list all unpaid items; + Ix - list all used up items that are on your shopping bill; + I$ - count your money. + + o Open a door. + + O Set options. A menu showing the current option values will + be displayed. You can change most values simply by select- + ing the menu entry for the given option (ie, by typing its + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 12 + + + + letter or clicking upon it, depending on your user inter- + face). For the non-boolean choices, a further menu or + prompt will appear once you've closed this menu. The avail- + able options are listed later in this Guidebook. Options + are usually set before the game rather than with the `O' + command; see the section on options below. + + p Pay your shopping bill. + + P Put on a ring or other accessory (amulet, blindfold). + + ^P Repeat previous message. Subsequent ^P's repeat earlier + messages. The behavior can be varied via the msg_window op- + tion. + + q Quaff (drink) something (potion, water, etc). + + Q Select an object for your quiver. You can then throw this + using the `f' command. (In versions prior to 3.3 this was + the command to quit the game, which has now been moved to + `#quit'.) + + r Read a scroll or spellbook. + + R Remove an accessory (ring, amulet, etc). + + ^R Redraw the screen. + + s Search for secret doors and traps around you. It usually + takes several tries to find something. + + S Save (and suspend) the game. The game will be restored au- + tomatically the next time you play. + + t Throw an object or shoot a projectile. + + T Take off armor. + + ^T Teleport, if you have the ability. + + v Display version number. + + V Display the game history. + + w Wield weapon. + + w- - wield nothing, use your bare hands. + + W Wear armor. + + x Exchange your wielded weapon with the item in your alternate + weapon slot. The latter is used as your secondary weapon + when engaging in two-weapon combat. Note that if one of + these slots is empty, the exchange still takes place. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 13 + + + + X Enter explore (discovery) mode, explained in its own section + later. + + ^X Display your name, role, race, gender, and alignment as well + as the various deities in your game. + + z Zap a wand. To aim at yourself, use `.' for the direction. + + Z Zap (cast) a spell. To cast at yourself, use `.' for the + direction. + + ^Z Suspend the game (UNIX(R) versions with job control only). + + : Look at what is here. + + ; Show what type of thing a visible symbol corresponds to. + + , Pick up some things. May be preceded by `m' to force a se- + lection menu. + + @ Toggle the autopickup option on and off. + + ^ Ask for the type of a trap you found earlier. + + ) Tell what weapon you are wielding. + + [ Tell what armor you are wearing. + + = Tell what rings you are wearing. + + " Tell what amulet you are wearing. + + ( Tell what tools you are using. + + * Tell what equipment you are using; combines the preceding + five type-specific commands into one. + + $ Count your gold pieces. + + + List the spells you know. Using this command, you can also + rearrange the order in which your spells are listed. They + are shown via a menu, and if you select a spell in that + menu, you'll be re-prompted for another spell to swap places + with it, and then have opportunity to make further ex- + changes. + + \ Show what types of objects have been discovered. + + ! Escape to a shell. + + + + __________ + (R)UNIX is a registered trademark of AT&T. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 14 + + + + # Perform an extended command. As you can see, the authors of + NetHack used up all the letters, so this is a way to intro- + duce the less frequently used commands. What extended com- + mands are available depends on what features the game was + compiled with. + + #adjust + Adjust inventory letters (most useful when the fixinv option + is ``on''). + + #chat + Talk to someone. + + #conduct + List which challenges you have adhered to. See the section + below entitled ``Conduct'' for details. + + #dip Dip an object into something. + + #enhance + Advance or check weapons and spell skills. + + #force + Force a lock. + + #invoke + Invoke an object's special powers. + + #jump + Jump to another location. + + #loot + Loot a box or bag on the floor beneath you, or the saddle + from a horse standing next to you. + + #monster + Use a monster's special ability (when polymorphed into mon- + ster form). + + #name + Name an item or type of object. + + #offer + Offer a sacrifice to the gods. + + #pray + Pray to the gods for help. + + #quit + Quit the program without saving your game. + + #ride + Ride (or stop riding) a monster. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 15 + + + + #rub Rub a lamp or a stone. + + #sit Sit down. + + #turn + Turn undead. + + #twoweapon + Toggle two-weapon combat on or off. Note that you must use + suitable weapons for this type of combat, or it will be au- + tomatically turned off. + + #untrap + Untrap something (trap, door, or chest). + + #version + Print compile time options for this version of NetHack. + + #wipe + Wipe off your face. + + #? Help menu: get the list of available extended commands. + + If your keyboard has a meta key (which, when pressed in com- + bination with another key, modifies it by setting the `meta' + [8th, or `high'] bit), you can invoke many extended commands by + meta-ing the first letter of the command. In NT, OS/2, and PC + NetHack, the `Alt' key can be used in this fashion. + + M-? #? (not supported by all platforms) + + M-2 #twoweapon (unless the number_pad option is enabled) + + M-a #adjust + + M-c #chat + + M-d #dip + + M-e #enhance + + M-f #force + + M-i #invoke + + M-j #jump + + M-l #loot + + M-m #monster + + M-n #name + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 16 + + + + M-o #offer + + M-p #pray + + M-q #quit + + M-r #rub + + M-s #sit + + M-t #turn + + M-u #untrap + + M-v #version + + M-w #wipe + + If the number_pad option is on, some additional letter com- + mands are available: + + h Help menu: display one of several help texts available, + like ``?''. + + j Jump to another location. Same as ``#jump'' or ``M-j''. + + k Kick something (usually a door). Same as `^D'. + + l Loot a box or bag on the floor beneath you, or the saddle + from a horse standing next to you. Same as ``#loot'' or + ``M-l''. + + N Name an item or type of object. Same as ``#name'' or ``M- + n''. + + u Untrap a trap, door, or chest. Same as ``#untrap'' or ``M- + u''. + + + 5. Rooms and corridors + + Rooms and corridors in the dungeon are either lit or dark. + Any lit areas within your line of sight will be displayed; dark + areas are only displayed if they are within one space of you. + Walls and corridors remain on the map as you explore them. + + Secret corridors are hidden. You can find them with the `s' + (search) command. + + 5.1. Doorways + + Doorways connect rooms and corridors. Some doorways have no + doors; you can walk right through. Others have doors in them, + which may be open, closed, or locked. To open a closed door, use + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 17 + + + + the `o' (open) command; to close it again, use the `c' (close) + command. + + You can get through a locked door by using a tool to pick + the lock with the `a' (apply) command, or by kicking it open with + the `^D' (kick) command. + + Open doors cannot be entered diagonally; you must approach + them straight on, horizontally or vertically. Doorways without + doors are not restricted in this fashion. + + Doors can be useful for shutting out monsters. Most mon- + sters cannot open doors, although a few don't need to (ex. ghosts + can walk through doors). + + Secret doors are hidden. You can find them with the `s' + (search) command. Once found they are in all ways equivalent to + normal doors. + + 5.2. Traps (`^') + + There are traps throughout the dungeon to snare the unwary + delver. For example, you may suddenly fall into a pit and be + stuck for a few turns trying to climb out. Traps don't appear on + your map until you see one triggered by moving onto it, see some- + thing fall into it, or you discover it with the `s' (search) com- + mand. Monsters can fall prey to traps, too, which can be a very + useful defensive strategy. + + There is a special pre-mapped branch of the dungeon based on + the classic computer game ``Sokoban.'' The goal is to push the + boulders into the pits or holes. With careful foresight, it is + possible to complete all of the levels according to the tradi- + tional rules of Sokoban. Some allowances are permitted in case + the player gets stuck; however, they will lower your luck. + + 5.3. Stairs (`<', `>') + + In general, each level in the dungeon will have a staircase + going up (`<') to the previous level and another going down (`>') + to the next level. There are some exceptions though. For in- + stance, fairly early in the dungeon you will find a level with + two down staircases, one continuing into the dungeon and the oth- + er branching into an area known as the Gnomish Mines. Those + mines eventually hit a dead end, so after exploring them (if you + choose to do so), you'll need to climb back up to the main dun- + geon. + + When you traverse a set of stairs, or trigger a trap which + sends you to another level, the level you're leaving will be de- + activated and stored in a file on disk. If you're moving to a + previously visited level, it will be loaded from its file on disk + and reactivated. If you're moving to a level which has not yet + been visited, it will be created (from scratch for most random + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 18 + + + + levels, from a template for some ``special'' levels, or loaded + from the remains of an earlier game for a ``bones'' level as + briefly described below). Monsters are only active on the cur- + rent level; those on other levels are essentially placed into + stasis. + + Ordinarily when you climb a set of stairs, you will arrive + on the corresponding staircase at your destination. However, + pets (see below) and some other monsters will follow along if + they're close enough when you travel up or down stairs, and occa- + sionally one of these creatures will displace you during the + climb. When that occurs, the pet or other monster will arrive on + the staircase and you will end up nearby. + + 5.4. Ladders (`<', `>') + + Ladders serve the same purpose as staircases, and the two + types of inter-level connections are nearly indistinguishable + during game play. + + 5.5. Shops and shopping + + Occasionally you will run across a room with a shopkeeper + near the door and many items lying on the floor. You can buy + items by picking them up and then using the `p' command. You can + inquire about the price of an item prior to picking it up by us- + ing the ``#chat'' command while standing on it. Using an item + prior to paying for it will incur a charge, and the shopkeeper + won't allow you to leave the shop until you have paid any debt + you owe. + + You can sell items to a shopkeeper by dropping them to the + floor while inside a shop. You will either be offered an amount + of gold and asked whether you're willing to sell, or you'll be + told that the shopkeeper isn't interested (generally, your item + needs to be compatible with the type of merchandise carried by + the shop). + + If you drop something in a shop by accident, the shopkeeper + will usually claim ownership without offering any compensation. + You'll have to buy it back if you want to reclaim it. + + Shopkeepers sometimes run out of money. When that happens, + you'll be offered credit instead of gold when you try to sell + something. Credit can be used to pay for purchases, but it is + only good in the shop where it was obtained; other shopkeepers + won't honor it. (If you happen to find a "credit card" in the + dungeon, don't bother trying to use it in shops; shopkeepers will + not accept it.) + + The `$' command, which reports the amount of gold you are + carrying (in inventory, not inside bags or boxes), will also show + current shop debt or credit, if any. The `Iu' command lists un- + paid items (those which still belong to the shop) if you are + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 19 + + + + carrying any. The `Ix' command shows an inventory-like display + of any unpaid items which have been used up, along with other + shop fees, if any. + + 5.5.1. Shop idiosyncracies + + Several aspects of shop behavior might be unexpected. + + * The price of a given item can vary due to a variety of factors. + + * A shopkeeper treats the spot immediately inside the door as if + it were outside the shop. + + * While the shopkeeper watches you like a hawk, he will generally + ignore any other customers. + + * If a shop is "closed for inventory", it will not open of its + own accord. + + * Shops do not get restocked with new items, regardless of inven- + tory depletion. + + + 6. Monsters + + Monsters you cannot see are not displayed on the screen. + Beware! You may suddenly come upon one in a dark place. Some + magic items can help you locate them before they locate you + (which some monsters can do very well). + + The commands `/' and `;' may be used to obtain information + about those monsters who are displayed on the screen. The com- + mand `C' allows you to assign a name to a monster, which may be + useful to help distinguish one from another when multiple mon- + sters are present. Assigning a name which is just a space will + remove any prior name. + + The extended command ``#chat'' can be used to interact with + an adjacent monster. There is no actual dialog (in other words, + you don't get to choose what you'll say), but chatting with some + monsters such as a shopkeeper or the Oracle of Delphi can produce + useful results. + + 6.1. Fighting + + If you see a monster and you wish to fight it, just attempt + to walk into it. Many monsters you find will mind their own + business unless you attack them. Some of them are very dangerous + when angered. Remember: discretion is the better part of valor. + + If you can't see a monster (if it is invisible, or if you + are blinded), the symbol `I' will be shown when you learn of its + presence. If you attempt to walk into it, you will try to fight + it just like a monster that you can see; of course, if the + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 20 + + + + monster has moved, you will attack empty air. If you guess that + the monster has moved and you don't wish to fight, you can use + the `m' command to move without fighting; likewise, if you don't + remember a monster but want to try fighting anyway, you can use + the `F' command. + + 6.2. Your pet + + You start the game with a little dog (`d'), cat (`f'), or + pony (`u'), which follows you about the dungeon and fights mon- + sters with you. Like you, your pet needs food to survive. It + usually feeds itself on fresh carrion and other meats. If you're + worried about it or want to train it, you can feed it, too, by + throwing it food. A properly trained pet can be very useful un- + der certain circumstances. + + Your pet also gains experience from killing monsters, and + can grow over time, gaining hit points and doing more damage. + Initially, your pet may even be better at killing things than + you, which makes pets useful for low-level characters. + + Your pet will follow you up and down staircases if it is + next to you when you move. Otherwise your pet will be stranded + and may become wild. Similarly, when you trigger certain types + of traps which alter your location (for instance, a trap door + which drops you to a lower dungeon level), any adjacent pet will + accompany you and any non-adjacent pet will be left behind. Your + pet may trigger such traps itself; you will not be carried along + with it even if adjacent at the time. + + 6.3. Steeds + + Some types of creatures in the dungeon can actually be rid- + den if you have the right equipment and skill. Convincing a wild + beast to let you saddle it up is difficult to say the least. + Many a dungeoneer has had to resort to magic and wizardry in or- + der to forge the alliance. Once you do have the beast under your + control however, you can easily climb in and out of the saddle + with the `#ride' command. Lead the beast around the dungeon when + riding, in the same manner as you would move yourself. It is the + beast that you will see displayed on the map. + + Riding skill is managed by the `#enhance' command. See the + section on Weapon proficiency for more information about that. + + 6.4. Bones levels + + You may encounter the shades and corpses of other adventur- + ers (or even former incarnations of yourself!) and their personal + effects. Ghosts are hard to kill, but easy to avoid, since + they're slow and do little damage. You can plunder the deceased + adventurer's possessions; however, they are likely to be cursed. + Beware of whatever killed the former player; it is probably still + lurking around, gloating over its last victory. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 21 + + + + 7. Objects + + When you find something in the dungeon, it is common to want + to pick it up. In NetHack, this is accomplished automatically by + walking over the object (unless you turn off the autopickup op- + tion (see below), or move with the `m' prefix (see above)), or + manually by using the `,' command. + + If you're carrying too many items, NetHack will tell you so + and you won't be able to pick up anything more. Otherwise, it + will add the object(s) to your pack and tell you what you just + picked up. + + As you add items to your inventory, you also add the weight + of that object to your load. The amount that you can carry de- + pends on your strength and your constitution. The stronger you + are, the less the additional load will affect you. There comes a + point, though, when the weight of all of that stuff you are car- + rying around with you through the dungeon will encumber you. + Your reactions will get slower and you'll burn calories faster, + requiring food more frequently to cope with it. Eventually, + you'll be so overloaded that you'll either have to discard some + of what you're carrying or collapse under its weight. + + NetHack will tell you how badly you have loaded yourself. + The symbols `Burdened', `Stressed', `Strained', `Overtaxed' and + `Overloaded' are displayed on the bottom line display to indicate + your condition. + + When you pick up an object, it is assigned an inventory let- + ter. Many commands that operate on objects must ask you to find + out which object you want to use. When NetHack asks you to + choose a particular object you are carrying, you are usually pre- + sented with a list of inventory letters to choose from (see Com- + mands, above). + + Some objects, such as weapons, are easily differentiated. + Others, like scrolls and potions, are given descriptions which + vary according to type. During a game, any two objects with the + same description are the same type. However, the descriptions + will vary from game to game. + + When you use one of these objects, if its effect is obvious, + NetHack will remember what it is for you. If its effect isn't + extremely obvious, you will be asked what you want to call this + type of object so you will recognize it later. You can also use + the ``#name'' command for the same purpose at any time, to name + all objects of a particular type or just an individual object. + When you use ``#name'' on an object which has already been named, + specifying a space as the value will remove the prior name in- + stead of assigning a new one. + + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 22 + + + + 7.1. Curses and Blessings + + Any object that you find may be cursed, even if the object + is otherwise helpful. The most common effect of a curse is being + stuck with (and to) the item. Cursed weapons weld themselves to + your hand when wielded, so you cannot unwield them. Any cursed + item you wear is not removable by ordinary means. In addition, + cursed arms and armor usually, but not always, bear negative en- + chantments that make them less effective in combat. Other cursed + objects may act poorly or detrimentally in other ways. + + Objects can also be blessed. Blessed items usually work + better or more beneficially than normal uncursed items. For ex- + ample, a blessed weapon will do more damage against demons. + + There are magical means of bestowing or removing curses upon + objects, so even if you are stuck with one, you can still have + the curse lifted and the item removed. Priests and Priestesses + have an innate sensitivity to this property in any object, so + they can more easily avoid cursed objects than other character + roles. + + An item with unknown status will be reported in your inven- + tory with no prefix. An item which you know the state of will be + distinguished in your inventory by the presence of the word + ``cursed'', ``uncursed'' or ``blessed'' in the description of the + item. + + 7.2. Weapons (`)') + + Given a chance, most monsters in the Mazes of Menace will + gratuitously try to kill you. You need weapons for self-defense + (killing them first). Without a weapon, you do only 1-2 hit + points of damage (plus bonuses, if any). Monk characters are an + exception; they normally do much more damage with bare hands than + they do with weapons. + + There are wielded weapons, like maces and swords, and thrown + weapons, like arrows and spears. To hit monsters with a weapon, + you must wield it and attack them, or throw it at them. You can + simply elect to throw a spear. To shoot an arrow, you should + first wield a bow, then throw the arrow. Crossbows shoot cross- + bow bolts. Slings hurl rocks and (other) stones (like gems). + + Enchanted weapons have a ``plus'' (or ``to hit enhancement'' + which can be either positive or negative) that adds to your + chance to hit and the damage you do to a monster. The only way + to determine a weapon's enchantment is to have it magically iden- + tified somehow. Most weapons are subject to some type of damage + like rust. Such ``erosion'' damage can be repaired. + + The chance that an attack will successfully hit a monster, + and the amount of damage such a hit will do, depends upon many + factors. Among them are: type of weapon, quality of weapon + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 23 + + + + (enchantment and/or erosion), experience level, strength, dexter- + ity, encumbrance, and proficiency (see below). The monster's ar- + mor class - a general defense rating, not necessarily due to + wearing of armor - is a factor too; also, some monsters are par- + ticularly vulnerable to certain types of weapons. + + Many weapons can be wielded in one hand; some require both + hands. When wielding a two-handed weapon, you can not wear a + shield, and vice versa. When wielding a one-handed weapon, you + can have another weapon ready to use by setting things up with + the `x' command, which exchanges your primary (the one being + wielded) and alternate weapons. And if you have proficiency in + the ``two weapon combat'' skill, you may wield both weapons si- + multaneously as primary and secondary; use the `#twoweapon' ex- + tended command to engage or disengage that. Only some types of + characters (barbarians, for instance) have the necessary skill + available. Even with that skill, using two weapons at once in- + curs a penalty in the chance to hit your target compared to using + just one weapon at a time. + + There might be times when you'd rather not wield any weapon + at all. To accomplish that, wield `-', or else use the `A' com- + mand which allows you to unwield the current weapon in addition + to taking off other worn items. + + Those of you in the audience who are AD&D players, be aware + that each weapon which existed in AD&D does roughly the same dam- + age to monsters in NetHack. Some of the more obscure weapons + (such as the aklys, lucern hammer, and bec-de-corbin) are defined + in an appendix to Unearthed Arcana, an AD&D supplement. + + The commands to use weapons are `w' (wield), `t' (throw), + `f' (fire, an alternative way of throwing), `Q' (quiver), `x' + (exchange), `#twoweapon', and `#enhance' (see below). + + 7.2.1. Throwing and shooting + + You can throw just about anything via the `t' command. It + will prompt for the item to throw; picking `?' will list things + in your inventory which are considered likely to be thrown, or + picking `*' will list your entire inventory. After you've chosen + what to throw, you will be prompted for a direction rather than + for a specific target. The distance something can be thrown de- + pends mainly on the type of object and your strength. Arrows can + be thrown by hand, but can be thrown much farther and will be + more likely to hit when thrown while you are wielding a bow. + + You can simplify the throwing operation by using the `Q' + command to select your preferred ``missile'', then using the `f' + command to throw it. You'll be prompted for a direction as + above, but you don't have to specify which item to throw each + time you use `f'. There is also an option, autoquiver, which has + NetHack choose another item to automatically fill your quiver + when the inventory slot used for `Q' runs out. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 24 + + + + Some characters have the ability to fire a volley of multi- + ple items in a single turn. Knowing how to load several rounds + of ammunition at once -- or hold several missiles in your hand -- + and still hit a target is not an easy task. Rangers are among + those who are adept at this task, as are those with a high level + of proficiency in the relevant weapon skill (in bow skill if + you're wielding one to shoot arrows, in crossbow skill if you're + wielding one to shoot bolts, or in sling skill if you're wielding + one to shoot stones). The number of items that the character has + a chance to fire varies from turn to turn. You can explicitly + limit the number of shots by using a numeric prefix before the + `t' or `f' command. For example, ``2f'' (or ``n2f'' if using + number_pad mode) would ensure that at most 2 arrows are shot even + if you could have fired 3. If you specify a larger number than + would have been shot (``4f'' in this example), you'll just end up + shooting the same number (3, here) as if no limit had been speci- + fied. Once the volley is in motion, all of the items will travel + in the same direction; if the first ones kill a monster, the oth- + ers can still continue beyond that spot. + + 7.2.2. Weapon proficiency + + You will have varying degrees of skill in the weapons avail- + able. Weapon proficiency, or weapon skills, affect how well you + can use particular types of weapons, and you'll be able to im- + prove your skills as you progress through a game, depending on + your role, your experience level, and use of the weapons. + + For the purposes of proficiency, weapons have been divided + up into various groups such as daggers, broadswords, and + polearms. Each role has a limit on what level of proficiency a + character can achieve for each group. For instance, wizards can + become highly skilled in daggers or staves but not in swords or + bows. + + The `#enhance' extended command is used to review current + weapons proficiency (also spell proficiency) and to choose which + skill(s) to improve when you've used one or more skills enough to + become eligible to do so. The skill rankings are ``none'' (some- + times also referred to as ``restricted'', because you won't be + able to advance), ``unskilled'', ``basic'', ``skilled'', and + ``expert''. Restricted skills simply will not appear in the list + shown by `#enhance'. (Divine intervention might unrestrict a + particular skill, in which case it will start at unskilled and be + limited to basic.) Some characters can enhance their barehanded + combat or martial arts skill beyond expert to ``master'' or + ``grand master''. + + Use of a weapon in which you're restricted or unskilled will + incur a modest penalty in the chance to hit a monster and also in + the amount of damage done when you do hit; at basic level, there + is no penalty or bonus; at skilled level, you receive a modest + bonus in the chance to hit and amount of damage done; at expert + level, the bonus is higher. A successful hit has a chance to + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 25 + + + + boost your training towards the next skill level (unless you've + already reached the limit for this skill). Once such training + reaches the threshold for that next level, you'll be told that + you feel more confident in your skills. At that point you can + use `#enhance' to increase one or more skills. Such skills are + not increased automatically because there is a limit to your to- + tal overall skills, so you need to actively choose which skills + to enhance and which to ignore. + + 7.3. Armor (`[') + + Lots of unfriendly things lurk about; you need armor to pro- + tect yourself from their blows. Some types of armor offer better + protection than others. Your armor class is a measure of this + protection. Armor class (AC) is measured as in AD&D, with 10 be- + ing the equivalent of no armor, and lower numbers meaning better + armor. Each suit of armor which exists in AD&D gives the same + protection in NetHack. Here is an (incomplete) list of the armor + classes provided by various suits of armor: + + dragon scale mail 1 + plate mail 3 + crystal plate mail 3 + bronze plate mail 4 + splint mail 4 + banded mail 4 + dwarvish mithril-coat 4 + elven mithril-coat 5 + chain mail 5 + orcish chain mail 6 + scale mail 6 + studded leather armor 7 + ring mail 7 + orcish ring mail 8 + leather armor 8 + leather jacket 9 + no armor 10 + + You can also wear other pieces of armor (ex. helmets, boots, + shields, cloaks) to lower your armor class even further, but you + can only wear one item of each category (one suit of armor, one + cloak, one helmet, one shield, and so on) at a time. + + If a piece of armor is enchanted, its armor protection will + be better (or worse) than normal, and its ``plus'' (or minus) + will subtract from your armor class. For example, a +1 chain + mail would give you better protection than normal chain mail, + lowering your armor class one unit further to 4. When you put on + a piece of armor, you immediately find out the armor class and + any ``plusses'' it provides. Cursed pieces of armor usually have + negative enchantments (minuses) in addition to being unremovable. + + Many types of armor are subject to some kind of damage like + rust. Such damage can be repaired. Some types of armor may + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 26 + + + + inhibit spell casting. + + The commands to use armor are `W' (wear) and `T' (take off). + The `A' command can also be used to take off armor as well as + other worn items. + + 7.4. Food (`%') + + Food is necessary to survive. If you go too long without + eating you will faint, and eventually die of starvation. Some + types of food will spoil, and become unhealthy to eat, if not + protected. Food stored in ice boxes or tins (``cans'') will usu- + ally stay fresh, but ice boxes are heavy, and tins take a while + to open. + + When you kill monsters, they usually leave corpses which are + also ``food.'' Many, but not all, of these are edible; some also + give you special powers when you eat them. A good rule of thumb + is ``you are what you eat.'' + + Some character roles and some monsters are vegetarian. Veg- + etarian monsters will typically never eat animal corpses, while + vegetarian players can, but with some rather unpleasant side-ef- + fects. + + You can name one food item after something you like to eat + with the fruit option. + + The command to eat food is `e'. + + 7.5. Scrolls (`?') + + Scrolls are labeled with various titles, probably chosen by + ancient wizards for their amusement value (ex. ``READ ME,'' or + ``THANX MAUD'' backwards). Scrolls disappear after you read them + (except for blank ones, without magic spells on them). + + One of the most useful of these is the scroll of identify, + which can be used to determine what another object is, whether it + is cursed or blessed, and how many uses it has left. Some ob- + jects of subtle enchantment are difficult to identify without + these. + + A mail daemon may run up and deliver mail to you as a scroll + of mail (on versions compiled with this feature). To use this + feature on versions where NetHack mail delivery is triggered by + electronic mail appearing in your system mailbox, you must let + NetHack know where to look for new mail by setting the ``MAIL'' + environment variable to the file name of your mailbox. You may + also want to set the ``MAILREADER'' environment variable to the + file name of your favorite reader, so NetHack can shell to it + when you read the scroll. On versions of NetHack where mail is + randomly generated internal to the game, these environment vari- + ables are ignored. You can disable the mail daemon by turning + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 27 + + + + off the mail option. + + The command to read a scroll is `r'. + + 7.6. Potions (`!') + + Potions are distinguished by the color of the liquid inside + the flask. They disappear after you quaff them. + + Clear potions are potions of water. Sometimes these are + blessed or cursed, resulting in holy or unholy water. Holy water + is the bane of the undead, so potions of holy water are good + things to throw (`t') at them. It is also sometimes very useful + to dip (``#dip'') an object into a potion. + + The command to drink a potion is `q' (quaff). + + 7.7. Wands (`/') + + Magic wands usually have multiple magical charges. Some + wands are directional--you must give a direction in which to zap + them. You can also zap them at yourself (just give a `.' or `s' + for the direction). Be warned, however, for this is often unwise. + Other wands are nondirectional--they don't require a direction. + The number of charges in a wand is random and decreases by one + whenever you use it. + + When the number of charges left in a wand becomes zero, at- + tempts to use the wand will usually result in nothing happening. + Occasionally, however, it may be possible to squeeze the last few + mana points from an otherwise spent wand, destroying it in the + process. A wand may be recharged by using suitable magic, but + doing so runs the risk of causing it to explode. The chance for + such an explosion starts out very small and increases each time + the wand is recharged. + + In a truly desperate situation, when your back is up against + the wall, you might decide to go for broke and break your wand. + This is not for the faint of heart. Doing so will almost cer- + tainly cause a catastrophic release of magical energies. + + When you have fully identified a particular wand, inventory + display will include additional information in parentheses: the + number of times it has been recharged followed by a colon and + then by its current number of charges. A current charge count of + -1 is a special case indicating that the wand has been cancelled. + + The command to use a wand is `z' (zap). To break one, use + the `a' (apply) command. + + 7.8. Rings (`=') + + Rings are very useful items, since they are relatively per- + manent magic, unlike the usually fleeting effects of potions, + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 28 + + + + scrolls, and wands. + + Putting on a ring activates its magic. You can wear only + two rings, one on each ring finger. + + Most rings also cause you to grow hungry more rapidly, the + rate varying with the type of ring. + + The commands to use rings are `P' (put on) and `R' (remove). + + 7.9. Spellbooks (`+') + + Spellbooks are tomes of mighty magic. When studied with the + `r' (read) command, they transfer to the reader the knowledge of + a spell (and therefore eventually become unreadable) -- unless + the attempt backfires. Reading a cursed spellbook or one with + mystic runes beyond your ken can be harmful to your health! + + A spell (even when learned) can also backfire when you cast + it. If you attempt to cast a spell well above your experience + level, or if you have little skill with the appropriate spell + type, or cast it at a time when your luck is particularly bad, + you can end up wasting both the energy and the time required in + casting. + + Casting a spell calls forth magical energies and focuses + them with your naked mind. Some of the magical energy released + comes from within you, and casting several spells in a row may + tire you. Casting of spells also requires practice. With prac- + tice, your skill in each category of spell casting will improve. + Over time, however, your memory of each spell will dim, and you + will need to relearn it. + + Some spells are directional--you must give a direction in + which to cast them. You can also cast them at yourself (just + give a `.' or `s' for the direction). Be warned, however, for + this is often unwise. Other spells are nondirectional--they + don't require a direction. + + Just as weapons are divided into groups in which a character + can become proficient (to varying degrees), spells are similarly + grouped. Successfully casting a spell exercises the skill group; + sufficient skill may increase the potency of the spell and reduce + the risk of spell failure. Skill slots are shared with weapons + skills. (See also the section on ``Weapon proficiency''.) + + Casting a spell also requires flexible movement, and wearing + various types of armor may interfere with that. + + The command to read a spellbook is the same as for scrolls, + `r' (read). The `+' command lists your current spells, their + levels, categories, and chances for failure. The `Z' (cast) com- + mand casts a spell. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 29 + + + + 7.10. Tools (`(') + + Tools are miscellaneous objects with various purposes. Some + tools have a limited number of uses, akin to wand charges. For + example, lamps burn out after a while. Other tools are contain- + ers, which objects can be placed into or taken out of. + + The command to use tools is `a' (apply). + + 7.10.1. Containers + + You may encounter bags, boxes, and chests in your travels. + A tool of this sort can be opened with the ``#loot'' extended + command when you are standing on top of it (that is, on the same + floor spot), or with the `a' (apply) command when you are carry- + ing it. However, chests are often locked, and are in any case + unwieldy objects. You must set one down before unlocking it by + using a key or lock-picking tool with the `a' (apply) command, by + kicking it with the `^D' command, or by using a weapon to force + the lock with the ``#force'' extended command. + + Some chests are trapped, causing nasty things to happen when + you unlock or open them. You can check for and try to deactivate + traps with the ``#untrap'' extended command. + + 7.11. Amulets (`"') + + Amulets are very similar to rings, and often more powerful. + Like rings, amulets have various magical properties, some benefi- + cial, some harmful, which are activated by putting them on. + + Only one amulet may be worn at a time, around your neck. + + The commands to use amulets are the same as for rings, `P' + (put on) and `R' (remove). + + 7.12. Gems (`*') + + Some gems are valuable, and can be sold for a lot of gold. + They are also a far more efficient way of carrying your riches. + Valuable gems increase your score if you bring them with you when + you exit. + + Other small rocks are also categorized as gems, but they are + much less valuable. All rocks, however, can be used as projec- + tile weapons (if you have a sling). In the most desperate of + cases, you can still throw them by hand. + + 7.13. Large rocks (``') + + Statues and boulders are not particularly useful, and are + generally heavy. It is rumored that some statues are not what + they seem. + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 30 + + + + Very large humanoids (giants and their ilk) have been known + to use boulders as weapons. + + 7.14. Gold (`$') + + Gold adds to your score, and you can buy things in shops + with it. There are a number of monsters in the dungeon that may + be influenced by the amount of gold you are carrying (shopkeepers + aside). + + + 8. Conduct + + As if winning NetHack were not difficult enough, certain + players seek to challenge themselves by imposing restrictions on + the way they play the game. The game automatically tracks some + of these challenges, which can be checked at any time with the + #conduct command or at the end of the game. When you perform an + action which breaks a challenge, it will no longer be listed. + This gives players extra ``bragging rights'' for winning the game + with these challenges. Note that it is perfectly acceptable to + win the game without resorting to these restrictions and that it + is unusual for players to adhere to challenges the first time + they win the game. + + Several of the challenges are related to eating behavior. + The most difficult of these is the foodless challenge. Although + creatures can survive long periods of time without food, there is + a physiological need for water; thus there is no restriction on + drinking beverages, even if they provide some minor food bene- + fits. Calling upon your god for help with starvation does not + violate any food challenges either. + + A strict vegan diet is one which avoids any food derived + from animals. The primary source of nutrition is fruits and veg- + etables. The corpses and tins of blobs (`b'), jellies (`j'), and + fungi (`F') are also considered to be vegetable matter. Certain + human food is prepared without animal products; namely, lembas + wafers, cram rations, food rations (gunyoki), K-rations, and C- + rations. Metal or another normally indigestible material eaten + while polymorphed into a creature that can digest it is also con- + sidered vegan food. Note however that eating such items still + counts against foodless conduct. + + Vegetarians do not eat animals; however, they are less se- + lective about eating animal byproducts than vegans. In addition + to the vegan items listed above, they may eat any kind of pudding + (`P') other than the black puddings, eggs and food made from eggs + (fortune cookies and pancakes), food made with milk (cream pies + and candy bars), and lumps of royal jelly. Monks are expected to + observe a vegetarian diet. + + Eating any kind of meat violates the vegetarian, vegan, and + foodless conducts. This includes tripe rations, the corpses or + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 31 + + + + tins of any monsters not mentioned above, and the various other + chunks of meat found in the dungeon. Swallowing and digesting a + monster while polymorphed is treated as if you ate the creature's + corpse. Eating leather, dragon hide, or bone items while poly- + morphed into a creature that can digest it, or eating monster + brains while polymorphed into a mind flayer, is considered eating + an animal, although wax is only an animal byproduct. + + Regardless of conduct, there will be some items which are + indigestible, and others which are hazardous to eat. Using a + swallow-and-digest attack against a monster is equivalent to eat- + ing the monster's corpse. Please note that the term ``vegan'' is + used here only in the context of diet. You are still free to + choose not to use or wear items derived from animals (e.g. + leather, dragon hide, bone, horns, coral), but the game will not + keep track of this for you. Also note that ``milky'' potions may + be a translucent white, but they do not contain milk, so they are + compatible with a vegan diet. Slime molds or player-defined + ``fruits'', although they could be anything from ``cherries'' to + ``pork chops'', are also assumed to be vegan. + + An atheist is one who rejects religion. This means that you + cannot #pray, #offer sacrifices to any god, #turn undead, or + #chat with a priest. Particularly selective readers may argue + that playing Monk or Priest characters should violate this con- + duct; that is a choice left to the player. Offering the Amulet + of Yendor to your god is necessary to win the game and is not + counted against this conduct. You are also not penalized for be- + ing spoken to by an angry god, priest(ess), or other religious + figure; a true atheist would hear the words but attach no special + meaning to them. + + Most players fight with a wielded weapon (or tool intended + to be wielded as a weapon). Another challenge is to win the game + without using such a wielded weapon. You are still permitted to + throw, fire, and kick weapons; use a wand, spell, or other type + of item; or fight with your hands and feet. + + In NetHack, a pacifist refuses to cause the death of any + other monster (i.e. if you would get experience for the death). + This is a particularly difficult challenge, although it is still + possible to gain experience by other means. + + An illiterate character cannot read or write. This includes + reading a scroll, spellbook, fortune cookie message, or t-shirt; + writing a scroll; or making an engraving of anything other than a + single ``x'' (the traditional signature of an illiterate person). + Reading an engraving, or any item that is absolutely necessary to + win the game, is not counted against this conduct. The identity + of scrolls and spellbooks (and knowledge of spells) in your + starting inventory is assumed to be learned from your teachers + prior to the start of the game and isn't counted. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 32 + + + + There are several other challenges tracked by the game. It + is possible to eliminate one or more species of monsters by geno- + cide; playing without this feature is considered a challenge. + When the game offers you an opportunity to genocide monsters, you + may respond with the monster type ``none'' if you want to de- + cline. You can change the form of an item into another item of + the same type (``polypiling'') or the form of your own body into + another creature (``polyself'') by wand, spell, or potion of + polymorph; avoiding these effects are each considered challenges. + Polymorphing monsters, including pets, does not break either of + these challenges. Finally, you may sometimes receive wishes; a + game without an attempt to wish for any items is a challenge, as + is a game without wishing for an artifact (even if the artifact + immediately disappears). When the game offers you an opportunity + to make a wish for an item, you may choose ``nothing'' if you + want to decline. + + + 9. Options + + Due to variations in personal tastes and conceptions of how + NetHack should do things, there are options you can set to change + how NetHack behaves. + + 9.1. Setting the options + + Options may be set in a number of ways. Within the game, + the `O' command allows you to view all options and change most of + them. You can also set options automatically by placing them in + the NETHACKOPTIONS environment variable or in a configuration + file. Some versions of NetHack also have front-end programs that + allow you to set options before starting the game. + + 9.2. Using the NETHACKOPTIONS environment variable + + The NETHACKOPTIONS variable is a comma-separated list of + initial values for the various options. Some can only be turned + on or off. You turn one of these on by adding the name of the + option to the list, and turn it off by typing a `!' or ``no'' be- + fore the name. Others take a character string as a value. You + can set string options by typing the option name, a colon or + equals sign, and then the value of the string. The value is ter- + minated by the next comma or the end of string. + + For example, to set up an environment variable so that ``au- + toquiver'' is on, ``autopickup'' is off, the name is set to + ``Blue Meanie'', and the fruit is set to ``papaya'', you would + enter the command + + % setenv NETHACKOPTIONS "autoquiver,\!autopickup,name:Blue Meanie,fruit:papaya" + + in csh (note the need to escape the ! since it's special to the + shell), or + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 33 + + + + $ NETHACKOPTIONS="autoquiver,!autopickup,name:Blue Meanie,fruit:papaya" + $ export NETHACKOPTIONS + + in sh or ksh. + + 9.3. Using a configuration file + + Any line in the configuration file starting with `#' is + treated as a comment. Any line in the configuration file start- + ing with ``OPTIONS='' may be filled out with options in the same + syntax as in NETHACKOPTIONS. Any line starting with ``DUN- + GEON='', ``EFFECTS='', ``MONSTERS='', ``OBJECTS='', ``TRAPS='', + or ``BOULDER='' is taken as defining the corresponding dungeon, + effects, monsters, objects traps or boulder option in a different + syntax, a sequence of decimal numbers giving the character posi- + tion in the current font to be used in displaying each entry. A + zero in any entry in such a sequence leaves the display of that + entry unchanged; this feature is not available using the option + syntax. Such a sequence can be continued to multiple lines by + putting a `\' at the end of each line to be continued. + + If your copy of the game included the compile time AUTOPICK- + UP_EXCEPTIONS option, then any line starting with ``AUTOPICK- + UP_EXCEPTION='' is taken as defining an exception to the pick- + up_types option. There is a section of this Guidebook that dis- + cusses that. + + The default name of the configuration file varies on differ- + ent operating systems, but NETHACKOPTIONS can also be set to the + full name of a file you want to use (possibly preceded by an + `@'). + + 9.4. Customization options + + Here are explanations of what the various options do. Char- + acter strings that are too long may be truncated. Some of the + options listed may be inactive in your dungeon. + + align + Your starting alignment (align:lawful, align:neutral, or + align:chaotic). You may specify just the first letter. The + default is to randomly pick an appropriate alignment. Cannot + be set with the `O' command. + + autodig + Automatically dig if you are wielding a digging tool and moving + into a place that can be dug (default false). + + autopickup + Automatically pick up things onto which you move (default on). + See pickup_types to refine the behavior. + + autoquiver + This option controls what happens when you attempt the `f' + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 34 + + + + (fire) command with an empty quiver. When true, the computer + will fill your quiver with some suitable weapon. Note that it + will not take into account the blessed/cursed status, enchant- + ment, damage, or quality of the weapon; you are free to manual- + ly fill your quiver with the `Q' command instead. If no weapon + is found or the option is false, the `t' (throw) command is ex- + ecuted instead. (default false) + + boulder + Set the character used to display boulders (default is rock + class symbol). + + catname + Name your starting cat (ex. ``catname:Morris''). Cannot be set + with the `O' command. + + character + Pick your type of character (ex. ``character:Monk''); synonym + for ``role''. See ``name'' for an alternate method of specify- + ing your role. Normally only the first letter of the value is + examined; the string ``random'' is an exception. + + checkpoint + Save game state after each level change, for possible recovery + after program crash (default on). + + checkspace + Check free disk space before writing files to disk (default + on). You may have to turn this off if you have more than 2 GB + free space on the partition used for your save and level files. + Only applies when MFLOPPY was defined during compilation. + + cmdassist + Have the game provide some additional command assistance for + new players if it detects some anticipated mistakes (default + on). + + confirm + Have user confirm attacks on pets, shopkeepers, and other + peaceable creatures (default on). + + DECgraphics + Use a predefined selection of characters from the DEC VT- + xxx/DEC Rainbow/ANSI line-drawing character set to display the + dungeon/effects/traps instead of having to define a full graph- + ics set yourself (default off). This option also sets up prop- + er handling of graphics characters for such terminals, so you + should specify it when appropriate even if you override the se- + lections with your own graphics strings. + + disclose + Controls options for disclosing various information when the + game ends (defaults to all possibilities being disclosed). The + possibilities are: + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 35 + + + + i - disclose your inventory. + a - disclose your attributes. + v - summarize monsters that have been vanquished. + g - list monster species that have been genocided. + c - display your conduct. + + Each disclosure possibility can optionally be preceded by a + prefix which let you refine how it behaves. Here are the valid + prefixes: + + y - prompt you and default to yes on the prompt. + n - prompt you and default to no on the prompt. + + - disclose it without prompting. + - - do not disclose it and do not prompt. + + (ex. ``disclose:yi na +v -g -c'') The example sets inventory to + prompt and default to yes, attributes to prompt and default to + no, vanquished to disclose without prompting, genocided to not + disclose and not to prompt, conduct to not disclose and not to + prompt. Note that the vanquished monsters list includes all + monsters killed by traps and each other as well as by you. + + dogname + Name your starting dog (ex. ``dogname:Fang''). Cannot be set + with the `O' command. + + dungeon + Set the graphics symbols for displaying the dungeon (default + `` |--------||.-|++##.##<><>_|\\#{}.}..## #}''). The dungeon + option should be followed by a string of 1-41 characters to be + used instead of the default map-drawing characters. The dun- + geon map will use the characters you specify instead of the de- + fault symbols, and default symbols for any you do not specify. + Remember that you may need to escape some of these characters + on a command line if they are special to your shell. + + Note that NetHack escape-processes this option string in con- + ventional C fashion. This means that `\' is a prefix to take + the following character literally. Thus `\' needs to be repre- + sented as `\\'. The special escape form `\m' switches on the + meta bit in the following character, and the `^' prefix causes + the following character to be treated as a control character. + + The order of the symbols is: solid rock, vertical wall, hori- + zontal wall, upper left corner, upper right corner, lower left + corner, lower right corner, cross wall, upward T wall, downward + T wall, leftward T wall, rightward T wall, no door, vertical + open door, horizontal open door, vertical closed door, horizon- + tal closed door, iron bars, tree, floor of a room, dark corri- + dor, lit corridor, stairs up, stairs down, ladder up, ladder + down, altar, grave, throne, kitchen sink, fountain, pool or + moat, ice, lava, vertical lowered drawbridge, horizontal low- + ered drawbridge, vertical raised drawbridge, horizontal raised + drawbridge, air, cloud, under water. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 36 + + + + You might want to use `+' for the corners and T walls for a + more aesthetic, boxier display. Note that in the next release, + new symbols may be added, or the present ones rearranged. + + Cannot be set with the `O' command. + + effects + Set the graphics symbols for displaying special effects (de- + fault ``|-\\/*!)(0#@*/-\\||\\-//-\\| |\\-/''). The effects op- + tion should be followed by a string of 1-29 characters to be + used instead of the default special-effects characters. This + string is subjected to the same processing as the dungeon op- + tion. + + The order of the symbols is: vertical beam, horizontal beam, + left slant, right slant, digging beam, camera flash beam, left + boomerang, right boomerang, four glyphs giving the sequence for + magic resistance displays, the eight surrounding glyphs for + swallowed display, nine glyphs for explosions. An explosion + consists of three rows (top, middle, and bottom) of three char- + acters. The explosion is centered in the center of this 3 by 3 + array. + + Note that in the next release, new symbols may be added, or the + present ones rearranged. + + Cannot be set with the `O' command. + + extmenu + Changes the extended commands interface to pop-up a menu of + available commands. It is keystroke compatible with the tradi- + tional interface except that it does not require that you hit + Enter. It is implemented only by the tty port (default off), + when the game has been compiled to support tty graphics. + + female + An obsolete synonym for ``gender:female''. Cannot be set with + the `O' command. + + fixinv + An object's inventory letter sticks to it when it's dropped + (default on). If this is off, dropping an object shifts all + the remaining inventory letters. + + fruit + Name a fruit after something you enjoy eating (ex. ``fruit:man- + go'') (default ``slime mold''). Basically a nostalgic whimsy + that NetHack uses from time to time. You should set this to + something you find more appetizing than slime mold. Apples, + oranges, pears, bananas, and melons already exist in NetHack, + so don't use those. + + gender + Your starting gender (gender:male or gender:female). You may + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 37 + + + + specify just the first letter. Although you can still denote + your gender using the ``male'' and ``female'' options, the + ``gender'' option will take precedence. The default is to ran- + domly pick an appropriate gender. Cannot be set with the `O' + command. + + help + If more information is available for an object looked at with + the `/' command, ask if you want to see it (default on). Turn- + ing help off makes just looking at things faster, since you + aren't interrupted with the ``More info?'' prompt, but it also + means that you might miss some interesting and/or important in- + formation. + + horsename + Name your starting horse (ex. ``horsename:Trigger''). Cannot + be set with the `O' command. + + IBMgraphics + Use a predefined selection of IBM extended ASCII characters to + display the dungeon/effects/traps instead of having to define a + full graphics set yourself (default off). This option also + sets up proper handling of graphics characters for such termi- + nals, so you should specify it when appropriate even if you + override the selections with your own graphics strings. + + ignintr + Ignore interrupt signals, including breaks (default off). + + legacy + Display an introductory message when starting the game (default + on). + + lit_corridor + Show corridor squares seen by night vision or a light source + held by your character as lit (default off). + + lootabc + Use the old `a', `b', and `c' keyboard shortcuts when looting, + rather than the mnemonics `o', `i', and `b' (default off). + + mail + Enable mail delivery during the game (default on). + + male + An obsolete synonym for ``gender:male''. Cannot be set with + the `O' command. + + menustyle + Controls the interface used when you need to choose various ob- + jects (in response to the Drop command, for instance). The + value specified should be the first letter of one of the fol- + lowing: traditional, combination, partial, or full. Tradi- + tional was the only interface available for earlier versions; + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 38 + + + + it consists of a prompt for object class characters, followed + by an object-by-object prompt for all items matching the se- + lected object class(es). Combination starts with a prompt for + object class(es) of interest, but then displays a menu of + matching objects rather than prompting one-by-one. Partial + skips the object class filtering and immediately displays a + menu of all objects. Full displays a menu of object classes + rather than a character prompt, and then a menu of matching ob- + jects for selection. + + menu_deselect_all + Menu character accelerator to deselect all items in a menu. + Implemented by the Amiga, Gem, X11 and tty ports. Default '-'. + + menu_deselect_page + Menu character accelerator to deselect all items on this page + of a menu. Implemented by the Amiga, Gem and tty ports. De- + fault '\'. + + menu_first_page + Menu character accelerator to jump to the first page in a menu. + Implemented by the Amiga, Gem and tty ports. Default '^'. + + menu_headings + Controls how the headings in a menu are highlighted. Values + are 'bold', 'inverse', or 'underline'. Not all ports can actu- + ally display all three types. + + menu_invert_all + Menu character accelerator to invert all items in a menu. Im- + plemented by the Amiga, Gem, X11 and tty ports. Default '@'. + + menu_invert_page + Menu character accelerator to invert all items on this page of + a menu. Implemented by the Amiga, Gem and tty ports. Default + '~'. + + menu_last_page + Menu character accelerator to jump to the last page in a menu. + Implemented by the Amiga, Gem and tty ports. Default '|'. + + menu_next_page + Menu character accelerator to goto the next menu page. Imple- + mented by the Amiga, Gem and tty ports. Default '>'. + + menu_previous_page + Menu character accelerator to goto the previous menu page. Im- + plemented by the Amiga, Gem and tty ports. Default '<'. + + menu_search + Menu character accelerator to search for a menu item. Imple- + mented by the Amiga, Gem and X11 ports. Default ':'. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 39 + + + + menu_select_all + Menu character accelerator to select all items in a menu. Im- + plemented by the Amiga, Gem, X11 and tty ports. Default '.'. + + menu_select_page + Menu character accelerator to select all items on this page of + a menu. Implemented by the Amiga, Gem and tty ports. Default + ','. + + monsters + Set the characters used to display monster classes (default + ``abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU- + VWXYZ@ '&;:~]''). This string is subjected to the same pro- + cessing as the dungeon option. The order of the symbols is ant + or other insect, blob, cockatrice, dog or other canine, eye or + sphere, feline, gremlin, humanoid, imp or minor demon, jelly, + kobold, leprechaun, mimic, nymph, orc, piercer, quadruped, ro- + dent, arachnid or centipede, trapper or lurker above, horse or + unicorn, vortex, worm, xan or other mythical/fantastic insect, + light, zruty, angelic being, bat or bird, centaur, dragon, ele- + mental, fungus or mold, gnome, giant humanoid, invisible mon- + ster, jabberwock, Keystone Kop, lich, mummy, naga, ogre, pud- + ding or ooze, quantum mechanic, rust monster, snake, troll, um- + ber hulk, vampire, wraith, xorn, apelike creature, zombie, hu- + man, ghost, golem, demon, sea monster, lizard, long worm tail, + and mimic. Cannot be set with the `O' command. + + msghistory + The number of top line messages to save (and recall with ^P) + (default 20). Cannot be set with the `O' command. + + msg_window + Allows you to change the way recalled messages are displayed. + (It is currently implemented for tty only.) The possible val- + ues are: + + s - single message (default, this was the behavior before 3.4.0). + c - combination, two messages as `single', then as `full'. + f - full window, oldest message first. + r - full window, newest message first. + + For backward compatibility, no value needs to be specified + (which defaults to `full'), or it can be negated (which de- + faults to `single'). + + name + Set your character's name (defaults to your user name). You + can also set your character's role by appending a dash and one + or more letters of the role (that is, by suffixing one of -A -B + -C -H -K -M -P -Ra -Ro -S -T -V -W). If -@ is used for the + role, then a random one will be automatically chosen. Cannot + be set with the `O' command. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 40 + + + + news + Read the NetHack news file, if present (default on). Since the + news is shown at the beginning of the game, there's no point in + setting this with the `O' command. + + null + Send padding nulls to the terminal (default off). + + number_pad + Use the number keys to move instead of [yuhjklbn] (default 0 or + off). (number_pad:2 invokes the old DOS behavior where `5' + means `g', meta-`5' means `G', and meta-`0' means `I'.) + + objects + Set the characters used to display object classes (default + ``])[="(%!?+/$*`0_.''). This string is subjected to the same + processing as the dungeon option. The order of the symbols is + illegal-object (should never be seen), weapon, armor, ring, + amulet, tool, food, potion, scroll, spellbook, wand, gold, gem + or rock, boulder or statue, iron ball, chain, and venom. Can- + not be set with the `O' command. + + packorder + Specify the order to list object types in (default + ``")[%?+!=/(*`0_''). The value of this option should be a + string containing the symbols for the various object types. + Any omitted types are filled in at the end from the previous + order. + + perm_invent + If true, always display your current inventory in a window. + This only makes sense for windowing system interfaces that im- + plement this feature. + + pettype + Specify the type of your initial pet, if you are playing a + character class that uses multiple types of pets; or choose to + have no initial pet at all. Possible values are ``cat'', + ``dog'' and ``none''. Cannot be set with the `O' command. + + pickup_burden + When you pick up an item that would exceed this encumbrance + level (Unburdened, Burdened, streSsed, straiNed, overTaxed, or + overLoaded), you will be asked if you want to continue. (De- + fault `S'). + + pickup_types + Specify the object types to be picked up when autopickup is on. + Default is all types. If your copy of the game has the experi- + mental compile time option AUTOPICKUP_EXCEPTIONS included, you + may be able to use autopickup_exception configuration file + lines to further refine autopickup behavior. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 41 + + + + prayconfirm + Prompt for confirmation before praying (default on). + + pushweapon + Using the `w' (wield) command when already wielding something + pushes the old item into your alternate weapon slot (default + off). + + race + Selects your race (for example, ``race:human''). Default is + random. Cannot be set with the `O' command. + + rest_on_space + Make the space bar a synonym for the `.' (rest) command (de- + fault off). + + role + Pick your type of character (ex. ``role:Samurai''); synonym for + ``character''. See ``name'' for an alternate method of speci- + fying your role. Normally only the first letter of the value + is examined; `r' is an exception with ``Rogue'', ``Ranger'', + and ``random'' values. + + runmode + Controls the amount of screen updating for the map window when + engaged in multi-turn movement (running via shift+direction or + control+direction and so forth, or via the travel command or + mouse click). The possible values are: + + teleport - update the map after movement has finished; + run - update the map after every seven or so steps; + walk - update the map after each step; + crawl - like walk, but pause briefly after each step. + + This option only affects the game's screen display, not the ac- + tual results of moving. The default is `run'; versions prior + to 3.4.1 used `teleport' only. Whether or not the effect is + noticeable will depend upon the window port used or on the type + of terminal. + + safe_pet + Prevent you from (knowingly) attacking your pets (default on). + + scores + Control what parts of the score list you are shown at the end + (ex. ``scores:5 top scores/4 around my score/own scores''). + Only the first letter of each category (`t', `a', or `o') is + necessary. + + showexp + Show your accumulated experience points on bottom line (default + off). + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 42 + + + + showrace + Display yourself as the glyph for your race, rather than the + glyph for your role (default off). Note that this setting af- + fects only the appearance of the display, not the way the game + treats you. + + showscore + Show your approximate accumulated score on bottom line (default + off). + + silent + Suppress terminal beeps (default on). + + sortpack + Sort the pack contents by type when displaying inventory (de- + fault on). + + sound + Enable messages about what your character hears (default on). + Note that this has nothing to do with your computer's audio ca- + pabilities. This option is only partly under player control. + The game toggles it off and on during and after sleep, for ex- + ample. + + sparkle + Display a sparkly effect when a monster (including yourself) is + hit by an attack to which it is resistant (default on). + + standout + Boldface monsters and ``--More--'' (default off). + + suppress_alert + This option may be set to a NetHack version level to suppress + alert notification messages about feature changes for that and + prior versions (ex. ``suppress_alert:3.3.1''). + + time + Show the elapsed game time in turns on bottom line (default + off). + + timed_delay + When pausing momentarily for display effect, such as with ex- + plosions and moving objects, use a timer rather than sending + extra characters to the screen. (Applies to ``tty'' interface + only; ``X11'' interface always uses a timer based delay. The + default is on if configured into the program.) + + tombstone + Draw a tombstone graphic upon your death (default on). + + toptenwin + Put the ending display in a NetHack window instead of on stdout + (default off). Setting this option makes the score list visi- + ble when a windowing version of NetHack is started without a + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 43 + + + + parent window, but it no longer leaves the score list around + after game end on a terminal or emulating window. + + traps + Set the graphics symbols for displaying traps (default + ``^^^^^^^^^^^^^^^^^"^^^^''). The traps option should be fol- + lowed by a string of 1-22 characters to be used instead of the + default traps characters. This string is subjected to the same + processing as the dungeon option. + + The order of the symbols is: arrow trap, dart trap, falling + rock trap, squeaky board, bear trap, land mine, rolling boulder + trap, sleeping gas trap, rust trap, fire trap, pit, spiked pit, + hole, trap door, teleportation trap, level teleporter, magic + portal, web, statue trap, magic trap, anti-magic field, poly- + morph trap. + + Cannot be set with the `O' command. + + travel + Allow the travel command (default on). Turning this option off + will prevent the game from attempting unintended moves if you + make inadvertent mouse clicks on the map window. + + verbose + Provide more commentary during the game (default on). + + windowtype + Select which windowing system to use, such as ``tty'' or + ``X11'' (default depends on version). Cannot be set with the + `O' command. + + 9.5. Window Port Customization options + + Here are explanations of the various options that are used + to customize and change the characteristics of the windowtype + that you have chosen. Character strings that are too long may be + truncated. Not all window ports will adjust for all settings + listed here. You can safely add any of these options to your + config file, and if the window port is capable of adjusting to + suit your preferences, it will attempt to do so. If it can't it + will silently ignore it. You can find out if an option is sup- + ported by the window port that you are currently using by check- + ing to see if it shows up in the Options list. Some options are + dynamic and can be specified during the game with the `O' com- + mand. + + align_message + Where to align or place the message window (top, bottom, left, + or right) + + align_status + Where to align or place the status window (top, bottom, left, + or right). + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 44 + + + + ascii_map + NetHack should display an ascii character map if it can. + + color + NetHack should display color if it can for different monsters, + objects, and dungeon features + + eight_bit_tty + NetHack should pass eight-bit character values (for example, + specified with the traps option) straight through to your ter- + minal (default off). + + font_map + NetHack should use a font by the chosen name for the map win- + dow. + + font_menu + NetHack should use a font by the chosen name for menu windows. + + font_message + NetHack should use a font by the chosen name for the message + window. + + font_status + NetHack should use a font by the chosen name for the status + window. + + font_text + NetHack should use a font by the chosen name for text windows. + + font_size_map + NetHack should use this size font for the map window. + + font_size_menu + NetHack should use this size font for menu windows. + + font_size_message + NetHack should use this size font for the message window. + + font_size_status + NetHack should use this size font for the status window. + + font_size_text + NetHack should use this size font for text windows. + + fullscreen + NetHack should try and display on the entire screen rather than + in a window. + + hilite_pet + Visually distinguish pets from similar animals (default off). + The behavior of this option depends on the type of windowing + you use. In text windowing, text highlighting or inverse video + is often used; with tiles, generally displays a heart symbol + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 45 + + + + near pets. + + large_font + NetHack should use a large font. + + map_mode + NetHack should display the map in the manner specified. + + mouse_support + Allow use of the mouse for input and travel. + + player_selection + NetHack should pop up dialog boxes, or use prompts for charac- + ter selection. + + popup_dialog + NetHack should pop up dialog boxes for input. + + preload_tiles + NetHack should preload tiles into memory. For example, in the + protected mode MSDOS version, control whether tiles get pre- + loaded into RAM at the start of the game. Doing so enhances + performance of the tile graphics, but uses more memory. (de- + fault on). Cannot be set with the `O' command. + + scroll_amount + NetHack should scroll the display by this number of cells when + the hero reaches the scroll_margin. + + scroll_margin + NetHack should scroll the display when the hero or cursor is + this number of cells away from the edge of the window. + + softkeyboard + Display an onscreen keyboard. Handhelds are most likely to + support this option. + + splash_screen + NetHack should display an opening splash screen when it starts + up (default yes). + + tiled_map + NetHack should display a tiled map if it can. + + tile_file + Specify the name of an alternative tile file to override the + default. + + tile_height + Specify the preferred height of each tile in a tile capable + port. + + tile_width + Specify the preferred width of each tile in a tile capable port + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 46 + + + + use_inverse + NetHack should display inverse when the game specifies it. + + vary_msgcount + NetHack should display this number of messages at a time in the + message window. + + windowcolors + NetHack should display windows with the specified fore- + ground/background colors if it can. + + wraptext + NetHack port should wrap long lines of text if they don't fit + in the visible area of the window. + + 9.6. Platform-specific Customization options + + Here are explanations of options that are used by specific + platforms or ports to customize and change the port behavior. + + altkeyhandler + Select an alternate keystroke handler dll to load (Win32 tty + NetHack only). The name of the handler is specified without + the .dll extension and without any path information. Cannot be + set with the `O' command. + + altmeta + (default on, AMIGA NetHack only). + + BIOS + Use BIOS calls to update the screen display quickly and to read + the keyboard (allowing the use of arrow keys to move) on ma- + chines with an IBM PC compatible BIOS ROM (default off, OS/2, + PC, and ST NetHack only). + + flush + (default off, AMIGA NetHack only). + + MACgraphics + (default on, Mac NetHack only). + + page_wait + (default on, Mac NetHack only). + + rawio + Force raw (non-cbreak) mode for faster output and more bullet- + proof input (MS-DOS sometimes treats `^P' as a printer toggle + without it) (default off, OS/2, PC, and ST NetHack only). + Note: DEC Rainbows hang if this is turned on. Cannot be set + with the `O' command. + + soundcard + (default on, PC NetHack only). Cannot be set with the `O' com- + mand. + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 47 + + + + subkeyvalue + (Win32 tty NetHack only). May be used to alter the value of + keystrokes that the operating system returns to NetHack to help + compensate for international keyboard issues. OPTIONS=subkey- + value:171/92 will return 92 to NetHack, if 171 was originally + going to be returned. You can use multiple subkeyvalue state- + ments in the config file if needed. Cannot be set with the `O' + command. + + video + Set the video mode used (PC NetHack only). Values are `autode- + tect', `default', or `vga'. Setting `vga' (or `autodetect' + with vga hardware present) will cause the game to display + tiles. Cannot be set with the `O' command. + + videocolors + Set the color palette for PC systems using NO_TERMS (default + 4-2-6-1-5-3-15-12-10-14-9-13-11, (PC NetHack only). The order + of colors is red, green, brown, blue, magenta, cyan, + bright.white, bright.red, bright.green, yellow, bright.blue, + bright.magenta, and bright.cyan. Cannot be set with the `O' + command. + + videoshades + Set the intensity level of the three gray scales available (de- + fault dark normal light, PC NetHack only). If the game display + is difficult to read, try adjusting these scales; if this does + not correct the problem, try !color. Cannot be set with the + `O' command. + + 9.7. Configuring autopickup exceptions + + There is an experimental compile time option called AU- + TOPICKUP_EXCEPTIONS. If your copy of the game was built with + that option defined, you can further refine the behavior of the + autopickup option beyond what is available through the pick- + up_types option. + + By placing autopickup_exception lines in your configuration + file, you can define patterns to be checked when the game is + about to autopickup something. + + autopickup_exception + Sets an exception to the pickup_types option. The autopick- + up_exception option should be followed by a string of 1-80 + characters to be used as a pattern to match against the singu- + lar form of the description of an object at your location. + + You may use the following special characters in a pattern: + + *--- matches 0 or more characters. + ?--- matches any single character. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 48 + + + + In addition, some characters are treated specially if they + occur as the first character in the string pattern, specifically: + + < - always pickup an object that matches the pattern that follows. + > - never pickup an object that matches the pattern that follows. + + Can be set with the `O' command, but the setting is not pre- + served across saves and restores. + + Here's a couple of examples of autopickup_exceptions: + + autopickup_exception="<*arrow" + autopickup_exception=">*corpse" + autopickup_exception=">* cursed*" + + The first example above will result in autopickup of any type of + arrow. The second example results in the exclusion of any corpse + from autopickup. The last example results in the exclusion of + items known to be cursed from autopickup. A `never pickup' rule + takes precedence over an `always pickup' rule if both match. + + 9.8. Configuring User Sounds + + Some platforms allow you to define sound files to be played + when a message that matches a user-defined pattern is delivered + to the message window. At this time the Qt port and the win32tty + and win32gui ports support the use of user sounds. + + The following config file entries are relevant to mapping + user sounds to messages: + + SOUNDDIR + The directory that houses the sound files to be played. + + SOUND + An entry that maps a sound file to a user-specified message + pattern. Each SOUND entry is broken down into the following + parts: + + MESG - message window mapping (the only one supported in 3.4). + pattern - the pattern to match. + sound file - the sound file to play. + volume - the volume to be set while playing the sound file. + + The exact format for the pattern depends on whether the + platform is built to use ``regular expressions'' or NetHack's own + internal pattern matching facility. The ``regular expressions'' + matching can be much more sophisticated than the internal NetHack + pattern matching, but requires 3rd party libraries on some plat- + forms. There are plenty of references available elsewhere for + explaining ``regular expressions''. You can verify which pattern + matching is used by your port with the #version command. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 49 + + + + NetHack's internal pattern matching routine uses the follow- + ing special characters in its pattern matching: + + *--- matches 0 or more characters. + ?--- matches any single character. + + Here's an example of a sound mapping using NetHack's inter- + nal pattern matching facility: + + SOUND=MESG "*chime of a cash register*" "gong.wav" 50 + + specifies that any message with "chime of a cash register" con- + tained in it will trigger the playing of "gong.wav". You can + have multiple SOUND entries in your config file. + + 9.9. Configuring NetHack for Play by the Blind + + NetHack can be set up to use only standard ASCII characters + for making maps of the dungeons. This makes the MS-DOS versions + of NetHack completely accessible to the blind who use speech + and/or Braille access technologies. Players will require a good + working knowledge of their screen-reader's review features, and + will have to know how to navigate horizontally and vertically + character by character. They will also find the search capabili- + ties of their screen-readers to be quite valuable. Be certain to + examine this Guidebook before playing so you have an idea what + the screen layout is like. You'll also need to be able to locate + the PC cursor. It is always where your character is located. + Merely searching for an @-sign will not always find your charac- + ter since there are other humanoids represented by the same sign. + Your screen-reader should also have a function which gives you + the row and column of your review cursor and the PC cursor. + These co-ordinates are often useful in giving players a better + sense of the overall location of items on the screen. + + While it is not difficult for experienced users to edit the + defaults.nh file to accomplish this, novices may find this task + somewhat daunting. Included in all official distributions of + NetHack is a file called NHAccess.nh. Replacing defaults.nh with + this file will cause the game to run in a manner accessible to + the blind. After you have gained some experience with the game + and with editing files, you may want to alter settings to better + suit your preferences. Instructions on how to do this are includ- + ed in the NHAccess.nh file itself. The most crucial settings to + make the game accessible are: + + IBMgraphics + Disable IBMgraphics by commenting out this option. + + menustyle:traditional + This will assist in the interface to speech synthesizers. + + number_pad + A lot of speech access programs use the number-pad to review + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 50 + + + + the screen. If this is the case, disable the number_pad option + and use the traditional Rogue-like commands. + + Character graphics + Comment out all character graphics sets found near the bottom + of the defaults.nh file. Most of these replace NetHack's de- + fault representation of the dungeon using standard ASCII char- + acters with fancier characters from extended character sets, + and these fancier characters can annoy screen-readers. + + 10. Scoring + + NetHack maintains a list of the top scores or scorers on + your machine, depending on how it is set up. In the latter case, + each account on the machine can post only one non-winning score + on this list. If you score higher than someone else on this + list, or better your previous score, you will be inserted in the + proper place under your current name. How many scores are kept + can also be set up when NetHack is compiled. + + Your score is chiefly based upon how much experience you + gained, how much loot you accumulated, how deep you explored, and + how the game ended. If you quit the game, you escape with all of + your gold intact. If, however, you get killed in the Mazes of + Menace, the guild will only hear about 90% of your gold when your + corpse is discovered (adventurers have been known to collect + finder's fees). So, consider whether you want to take one last + hit at that monster and possibly live, or quit and stop with + whatever you have. If you quit, you keep all your gold, but if + you swing and live, you might find more. + + If you just want to see what the current top players/games + list is, you can type nethack -s all on most versions. + + + 11. Explore mode + + NetHack is an intricate and difficult game. Novices might + falter in fear, aware of their ignorance of the means to survive. + Well, fear not. Your dungeon may come equipped with an ``ex- + plore'' or ``discovery'' mode that enables you to keep old save + files and cheat death, at the paltry cost of not getting on the + high score list. + + There are two ways of enabling explore mode. One is to + start the game with the -X switch. The other is to issue the `X' + command while already playing the game. The other benefits of + explore mode are left for the trepid reader to discover. + + + 12. Credits + + The original hack game was modeled on the Berkeley UNIX + rogue game. Large portions of this paper were shamelessly + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 51 + + + + cribbed from A Guide to the Dungeons of Doom, by Michael C. Toy + and Kenneth C. R. C. Arnold. Small portions were adapted from + Further Exploration of the Dungeons of Doom, by Ken Arromdee. + + NetHack is the product of literally dozens of people's work. + Main events in the course of the game development are described + below: + + + Jay Fenlason wrote the original Hack, with help from Kenny + Woodland, Mike Thome and Jon Payne. + + Andries Brouwer did a major re-write, transforming Hack into + a very different game, and published (at least) three versions + (1.0.1, 1.0.2, and 1.0.3) for UNIX machines to the Usenet. + + Don G. Kneller ported Hack 1.0.3 to Microsoft C and MS-DOS, + producing PC HACK 1.01e, added support for DEC Rainbow graphics + in version 1.03g, and went on to produce at least four more ver- + sions (3.0, 3.2, 3.51, and 3.6). + + R. Black ported PC HACK 3.51 to Lattice C and the Atari + 520/1040ST, producing ST Hack 1.03. + + Mike Stephenson merged these various versions back together, + incorporating many of the added features, and produced NetHack + 1.4. He then coordinated a cast of thousands in enhancing and + debugging NetHack 1.4 and released NetHack versions 2.2 and 2.3. + + Later, Mike coordinated a major rewrite of the game, heading + a team which included Ken Arromdee, Jean-Christophe Collet, Steve + Creps, Eric Hendrickson, Izchak Miller, John Rupley, Mike Threep- + oint, and Janet Walz, to produce NetHack 3.0c. + + NetHack 3.0 was ported to the Atari by Eric R. Smith, to + OS/2 by Timo Hakulinen, and to VMS by David Gentzel. The three + of them and Kevin Darcy later joined the main development team to + produce subsequent revisions of 3.0. + + Olaf Seibert ported NetHack 2.3 and 3.0 to the Amiga. Norm + Meluch, Stephen Spackman and Pierre Martineau designed overlay + code for PC NetHack 3.0. Johnny Lee ported NetHack 3.0 to the + Macintosh. Along with various other Dungeoneers, they continued + to enhance the PC, Macintosh, and Amiga ports through the later + revisions of 3.0. + + Headed by Mike Stephenson and coordinated by Izchak Miller + and Janet Walz, the development team which now included Ken Ar- + romdee, David Cohrs, Jean-Christophe Collet, Kevin Darcy, Matt + Day, Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric + Raymond, and Eric Smith undertook a radical revision of 3.0. + They re-structured the game's design, and re-wrote major parts of + the code. They added multiple dungeons, a new display, special + individual character quests, a new endgame and many other new + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 52 + + + + features, and produced NetHack 3.1. + + Ken Lorber, Gregg Wonderly and Greg Olson, with help from + Richard Addison, Mike Passaretti, and Olaf Seibert, developed + NetHack 3.1 for the Amiga. + + Norm Meluch and Kevin Smolkowski, with help from Carl Sche- + lin, Stephen Spackman, Steve VanDevender, and Paul Winner, ported + NetHack 3.1 to the PC. + + Jon W{tte and Hao-yang Wang, with help from Ross Brown, Mike + Engber, David Hairston, Michael Hamel, Jonathan Handler, Johnny + Lee, Tim Lennan, Rob Menke, and Andy Swanson, developed NetHack + 3.1 for the Macintosh, porting it for MPW. Building on their de- + velopment, Barton House added a Think C port. + + Timo Hakulinen ported NetHack 3.1 to OS/2. Eric Smith port- + ed NetHack 3.1 to the Atari. Pat Rankin, with help from Joshua + Delahunty, was responsible for the VMS version of NetHack 3.1. + Michael Allison ported NetHack 3.1 to Windows NT. + + Dean Luick, with help from David Cohrs, developed NetHack + 3.1 for X11. Warwick Allison wrote a tiled version of NetHack + for the Atari; he later contributed the tiles to the DevTeam and + tile support was then added to other platforms. + + The 3.2 development team, comprised of Michael Allison, Ken + Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + Timo Hakulinen, Steve Linhart, Dean Luick, Pat Rankin, Eric + Smith, Mike Stephenson, Janet Walz, and Paul Winner, released + version 3.2 in April of 1996. + + Version 3.2 marked the tenth anniversary of the formation of + the development team. In a testament to their dedication to the + game, all thirteen members of the original development team re- + mained on the team at the start of work on that release. During + the interval between the release of 3.1.3 and 3.2, one of the + founding members of the development team, Dr. Izchak Miller, was + diagnosed with cancer and passed away. That release of the game + was dedicated to him by the development and porting teams. + + During the lifespan of NetHack 3.1 and 3.2, several enthusi- + asts of the game added their own modifications to the game and + made these ``variants'' publicly available: + + Tom Proudfoot and Yuval Oren created NetHack++, which was + quickly renamed NetHack--. Working independently, Stephen White + wrote NetHack Plus. Tom Proudfoot later merged NetHack Plus and + his own NetHack-- to produce SLASH. Larry Stewart-Zerba and War- + wick Allison improved the spell casting system with the Wizard + Patch. Warwick Allison also ported NetHack to use the Qt inter- + face. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 53 + + + + Warren Cheung combined SLASH with the Wizard Patch to pro- + duce Slash'em, and with the help of Kevin Hugo, added more fea- + tures. Kevin later joined the DevTeam and incorporated the best + of these ideas in NetHack 3.3. + + The final update to 3.2 was the bug fix release 3.2.3, which + was released simultaneously with 3.3.0 in December 1999 just in + time for the Year 2000. + + The 3.3 development team, consisting of Michael Allison, Ken + Arromdee, David Cohrs, Jessie Collet, Steve Creps, Kevin Darcy, + Timo Hakulinen, Kevin Hugo, Steve Linhart, Ken Lorber, Dean + Luick, Pat Rankin, Eric Smith, Mike Stephenson, Janet Walz, and + Paul Winner, released 3.3.0 in December 1999 and 3.3.1 in August + of 2000. + + Version 3.3 offered many firsts. It was the first version to + separate race and profession. The Elf class was removed in pref- + erence to an elf race, and the races of dwarves, gnomes, and orcs + made their first appearance in the game alongside the familiar + human race. Monk and Ranger roles joined Archeologists, Barbar- + ians, Cavemen, Healers, Knights, Priests, Rogues, Samurai, + Tourists, Valkyries and of course, Wizards. It was also the + first version to allow you to ride a steed, and was the first + version to have a publicly available web-site listing all the + bugs that had been discovered. Despite that constantly growing + bug list, 3.3 proved stable enough to last for more than a year + and a half. + + The 3.4 development team initially consisted of Michael Al- + lison, Ken Arromdee, David Cohrs, Jessie Collet, Kevin Hugo, Ken + Lorber, Dean Luick, Pat Rankin, Mike Stephenson, Janet Walz, and + Paul Winner, with Warwick Allison joining just before the re- + lease of NetHack 3.4.0 in March 2002. + + As with version 3.3, various people contributed to the game + as a whole as well as supporting ports on the different platforms + that NetHack runs on: + + Pat Rankin maintained 3.4 for VMS. + + Michael Allison maintained NetHack 3.4 for the MS-DOS plat- + form. Paul Winner and Yitzhak Sapir provided encouragement. + + Dean Luick, Mark Modrall, and Kevin Hugo maintained and en- + hanced the Macintosh port of 3.4. + + Michael Allison, David Cohrs, Alex Kompel, Dion Nicolaas, + and Yitzhak Sapir maintained and enhanced 3.4 for the Microsoft + Windows platform. Alex Kompel contributed a new graphical inter- + face for the Windows port. Alex Kompel also contributed a Win- + dows CE port for 3.4.1. + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 54 + + + + Ron Van Iwaarden maintained 3.4 for OS/2. + + Janne Salmijarvi and Teemu Suikki maintained and enhanced + the Amiga port of 3.4 after Janne Salmijarvi resurrected it for + 3.3.1. + + Christian ``Marvin'' Bressler maintained 3.4 for the Atari + after he resurrected it for 3.3.1. + + There is a NetHack web site maintained by Ken Lorber at + http://www.nethack.org/. + + - - - - - - - - - - + + From time to time, some depraved individual out there in + netland sends a particularly intriguing modification to help out + with the game. The Gods of the Dungeon sometimes make note of + the names of the worst of these miscreants in this, the list of + Dungeoneers: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NetHack 3.4 December 2, 2003 + + + + + + NetHack Guidebook 55 + + + + Adam Aronow Izchak Miller Mike Stephenson + Alex Kompel J. Ali Harlow Norm Meluch + Andreas Dorn Janet Walz Olaf Seibert + Andy Church Janne Salmijarvi Pasi Kallinen + Andy Swanson Jean-Christophe Collet Pat Rankin + Ari Huttunen Jochen Erwied Paul Winner + Barton House John Kallen Pierre Martineau + Benson I. Margulies John Rupley Ralf Brown + Bill Dyer John S. Bien Ray Chason + Boudewijn Waijers Johnny Lee Richard Addison + Bruce Cox Jon W{tte Richard Beigel + Bruce Holloway Jonathan Handler Richard P. Hughey + Bruce Mewborne Joshua Delahunty Rob Menke + Carl Schelin Keizo Yamamoto Robin Johnson + Chris Russo Ken Arnold Roderick Schertler + David Cohrs Ken Arromdee Roland McGrath + David Damerell Ken Lorber Ron Van Iwaarden + David Gentzel Ken Washikita Ronnen Miller + David Hairston Kevin Darcy Ross Brown + Dean Luick Kevin Hugo Sascha Wostmann + Del Lamb Kevin Sitze Scott Bigham + Deron Meranda Kevin Smolkowski Scott R. Turner + Dion Nicolaas Kevin Sweet Stephen Spackman + Dylan O'Donnell Lars Huttar Stephen White + Eric Backus Malcolm Ryan Steve Creps + Eric Hendrickson Mark Gooderum Steve Linhart + Eric R. Smith Mark Modrall Steve VanDevender + Eric S. Raymond Marvin Bressler Teemu Suikki + Erik Andersen Matthew Day Tim Lennan + Frederick Roeber Merlyn LeRoy Timo Hakulinen + Gil Neiger Michael Allison Tom Almy + Greg Laskin Michael Feir Tom West + Greg Olson Michael Hamel Warren Cheung + Gregg Wonderly Michael Sokolov Warwick Allison + Hao-yang Wang Mike Engber Yitzhak Sapir + Helge Hafting Mike Gallop + Irina Rempt-Drijfhout Mike Passaretti + + Brand and product names are trademarks or registered trademarks + of their respective holders. + + + + + + + + + + + + + + + + + NetHack 3.4 December 2, 2003 + + + diff --git a/doc/dgn_comp.6 b/doc/dgn_comp.6 new file mode 100644 index 0000000..530942e --- /dev/null +++ b/doc/dgn_comp.6 @@ -0,0 +1,402 @@ +.TH DGN_COMP 6 "12 Dec 1995" +.UC 4 +.SH NAME +dgn_comp \- NetHack dungeon compiler +.SH SYNOPSIS +.B dgn_comp +[ +.I file +] +.PP +If no arguments are given, it reads standard input. +.SH DESCRIPTION +.PP +.I Dgn_comp +is a dungeon compiler for NetHack version 3.2 and higher. It +takes a description file as an argument and produces a dungeon "script" +that is to be loaded by NetHack at runtime. +.PP +The purpose of this tool is to provide NetHack administrators and +implementors with a convenient way to create a custom dungeon for the +game, without having to recompile the entire world. +.SH GRAMMAR +.PP +DUNGEON: +.B name +.B bonesmarker +( +.B base +, +.B rand +) [ +.B %age +] +.PP +where +.B name +is the dungeon name, +.B bonesmarker +is a letter for marking bones files, ( +.B base +, +.B rand +) is the number of levels, and +.B %age +is its percentage chance of being generated (if absent, 100% chance). + +DESCRIPTION: +.B tag +.PP +where +.B tag +is currently one of +.BR HELLISH , +.BR MAZELIKE , +or +.BR ROGUELIKE . + +ALIGNMENT | LEVALIGN: [ +.B lawful +| +.B neutral +| +.B chaotic +| +.B unaligned +] +.PP +gives the alignment of the dungeon/level (default is unaligned). + +ENTRY: +.B level +.PP +the dungeon entry point. The dungeon connection attaches at this +level of the given dungeon. +If the value of +.B level +is negative, the entry level is calculated from the bottom of the +dungeon, with -1 being the last level. +If this line is not present in a dungeon description, the entry level +defaults to 1. + +PROTOFILE: +.B name +.PP +the prototypical name for dungeon level files in this dungeon. +For example, the PROTOFILE name for the dungeon +.I Vlad's Tower +is +.IR tower . + +LEVEL: +.B name +.B bonesmarker +@ ( +.B base +, +.B rand +) [ +.B %age +] +.PP +where +.B name +is the level name, +.B bonesmarker +is a letter for marking bones files, ( +.B base +, +.B rand +) is the location and +.B %age +is the generation percentage, as above. + +RNDLEVEL: +.B name +.B bonesmarker +@ ( +.B base +, +.B rand +) +[ +.B %age +] +.B rndlevs +.PP +where +.B name +is the level name, +.B bonesmarker +is a letter for marking bones files, ( +.B base +, +.B rand +) is the location, +.B %age +is the generation percentage, as above, and +.B rndlevs +is the number of similar levels available to choose from. + +CHAINLEVEL: +.B name +.B bonesmarker +.B prev_name ++ ( +.B base +, +.B rand +) [ +.B %age +] +.PP +where +.B name +is the level name, +.B bonesmarker +is a letter for marking bones files, +.B prev_name +is the name of a level defined previously, ( +.B base +, +.B rand +) is the +.I offset +from the level being chained from, and +.B %age +is the generation percentage. + +RNDCHAINLEVEL: +.B name +.B bonesmarker +.B prev_name ++ ( +.B base +, +.B rand +) [ +.B %age +] +.B rndlevs +.PP +where +.B name +is the level name, +.B bonesmarker +is a letter for marking bones files, +.B prev_name +is the name of a level defined previously, ( +.B base +, +.B rand +) is the +.I offset +from the level being chained from, +.B %age +is the generation percentage, and +.B rndlevs +is the number of similar levels available to choose from. + +LEVELDESC: +.B type +.PP +where +.B type +is the level type, (see DESCRIPTION, above). The +.B type +is used to override any pre-set value used to describe the entire dungeon, +for this level only. + +BRANCH: +.B name +@ ( +.B base +, +.B rand +) [ +.B stair +| +.B no_up +| +.B no_down +| +.B portal +] [ +.B up +| +.B down +] +.PP +where +.B name +is the name of the dungeon to branch to, and ( +.B base +, +.B rand +) is the location of the branch. +The last two optional arguments are +the branch type and branch direction. +The type of a branch can be a two-way stair connection, +a one-way stair connection, or a magic portal. +A one-way stair is described by the types +.B no_up +and +.B no_down +which specify which stair direction is missing. +The default branch type is +.BR stair . +The direction for a stair can be either up or down; direction is not +applicable to portals. The default direction is +.BR down . + +CHAINBRANCH: +.B name +.B prev_name ++ ( +.B base +, +.B rand +) [ +.B stair +| +.B no_up +| +.B no_down +| +.B portal +] [ +.B up +| +.B down +] +.PP +where +.B name +is the name of the dungeon to branch to, +.B prev_name +is the name of a previously defined +.B level +and ( +.B base +, +.B rand +) is the +.I offset +from the level being chained from. +The optional branch type and direction are the same as described above. +.SH GENERIC RULES +.PP +Each dungeon must have a unique +.B bonesmarker , +and each special level must have a +.B bonesmarker +unique within its dungeon (letters may be reused in different dungeons). +If the +.B bonesmarker +has the special value "none", no bones files will be created for that +level or dungeon. +.PP +The value +.B base +may be in the range of 1 to +.B MAXLEVEL +(as defined in +.I global.h +). +.PP +The value +.B rand +may be in the range of -1 to +.BR MAXLEVEL . +.PP +If +.B rand +is -1 it will be replaced with the value (num_dunlevs(dungeon) - base) +during the load process (ie. from here to the end of the dungeon). +.PP +If +.B rand +is 0 the level is located absolutely at +.BR base . +.PP +Branches don't have a probability. Dungeons do. If a dungeon fails +to be generated during load, all its levels and branches are skipped. +.PP +No level or branch may be chained from a level with a percentage generation +probability. This is to prevent non-resolution during the load. +In addition, no branch may be made from a dungeon with a percentage +generation probability for the same reason. +.PP +As a general rule using the dungeon compiler: +.PP +If a dungeon has a +.B protofile +name associated with it +.RI ( eg. +.BR tower ) +that file will be used. +.PP +If a special level is present, it will override the above rule and +the appropriate file will be loaded. +.PP +If neither of the above are present, the standard generator will +take over and make a "normal" level. +.PP +A level alignment, if present, will override +the alignment of the dungeon that it exists within. +.SH EXAMPLE +.PP +Here is the current syntax of the dungeon compiler's "language": + +.LP +.nf +.ta +8n +8n +8n +# +# The dungeon description file for the "standard" original +# 3.0 NetHack. +# +DUNGEON: "The Dungeons of Doom" "D" (25, 5) +LEVEL: "rogue" "none" @ (15, 4) +LEVEL: "oracle" "none" @ (5, 7) +LEVEL: "bigroom" "B" @ (12, 3) 15 +LEVEL: "medusa" "none" @ (20, 5) +CHAINLEVEL: "castle" "medusa" + (1, 4) +CHAINBRANCH: "Hell" "castle" + (0, 0) no_down +BRANCH: "The Astral Plane" @ (1, 0) no_down up + +DUNGEON: "Hell" "H" (25, 5) +DESCRIPTION: mazelike +DESCRIPTION: hellish +BRANCH: "Vlad's Tower" @ (13, 5) up +LEVEL: "wizard" "none" @ (15, 10) +LEVEL: "fakewiz" "A" @ (5, 5) +LEVEL: "fakewiz" "B" @ (10, 5) +LEVEL: "fakewiz" "C" @ (15, 5) +LEVEL: "fakewiz" "D" @ (20, 5) +LEVEL: "fakewiz" "E" @ (25, 5) + +DUNGEON: "Vlad's Tower" "T" (3, 0) +PROTOFILE: "tower" +DESCRIPTION: mazelike +ENTRY: -1 + +DUNGEON: "The Astral Plane" "A" (1, 0) +DESCRIPTION: mazelike +PROTOFILE: "endgame" +.fi +.PP +.I NOTES: +.br +Lines beginning with '#' are considered comments. +.br +A special level must be explicitly aligned. The alignment of the dungeon +it is in only applies to non-special levels within that dungeon. +.SH AUTHOR +.PP +M. Stephenson (from the level compiler by Jean-Christophe Collet). +.SH "SEE ALSO" +.PP +lev_comp(6), nethack(6) +.SH BUGS +.PP +Probably infinite. diff --git a/doc/dgn_comp.txt b/doc/dgn_comp.txt new file mode 100644 index 0000000..907a4f0 --- /dev/null +++ b/doc/dgn_comp.txt @@ -0,0 +1,330 @@ + + + +DGN_COMP(6) 1995 DGN_COMP(6) + + + +NAME + dgn_comp - NetHack dungeon compiler + +SYNOPSIS + dgn_comp [ file ] + + If no arguments are given, it reads standard input. + +DESCRIPTION + Dgn_comp is a dungeon compiler for NetHack version 3.2 and + higher. It takes a description file as an argument and pro- + duces a dungeon "script" that is to be loaded by NetHack at + runtime. + + The purpose of this tool is to provide NetHack administra- + tors and implementors with a convenient way to create a cus- + tom dungeon for the game, without having to recompile the + entire world. + +GRAMMAR + DUNGEON: name bonesmarker ( base , rand ) [ %age ] + + where name is the dungeon name, bonesmarker is a letter for + marking bones files, ( base , rand ) is the number of lev- + els, and %age is its percentage chance of being generated + (if absent, 100% chance). + + DESCRIPTION: tag + + where tag is currently one of HELLISH, MAZELIKE, or ROGUE- + LIKE. + + ALIGNMENT | LEVALIGN: [ lawful | neutral | chaotic | + unaligned ] + + gives the alignment of the dungeon/level (default is + unaligned). + + ENTRY: level + + the dungeon entry point. The dungeon connection attaches at + this level of the given dungeon. If the value of level is + negative, the entry level is calculated from the bottom of + the dungeon, with -1 being the last level. If this line is + not present in a dungeon description, the entry level + defaults to 1. + + PROTOFILE: name + + the prototypical name for dungeon level files in this + dungeon. For example, the PROTOFILE name for the dungeon + Vlad's Tower is tower. + + + +Dec Last change: 12 1 + + + + + + +DGN_COMP(6) 1995 DGN_COMP(6) + + + + LEVEL: name bonesmarker @ ( base , rand ) [ %age ] + + where name is the level name, bonesmarker is a letter for + marking bones files, ( base , rand ) is the location and + %age is the generation percentage, as above. + + RNDLEVEL: name bonesmarker @ ( base , rand ) [ %age ] + rndlevs + + where name is the level name, bonesmarker is a letter for + marking bones files, ( base , rand ) is the location, %age + is the generation percentage, as above, and rndlevs is the + number of similar levels available to choose from. + + CHAINLEVEL: name bonesmarker prev_name + ( base , rand ) [ + %age ] + + where name is the level name, bonesmarker is a letter for + marking bones files, prev_name is the name of a level + defined previously, ( base , rand ) is the offset from the + level being chained from, and %age is the generation percen- + tage. + + RNDCHAINLEVEL: name bonesmarker prev_name + ( base , rand ) + [ %age ] rndlevs + + where name is the level name, bonesmarker is a letter for + marking bones files, prev_name is the name of a level + defined previously, ( base , rand ) is the offset from the + level being chained from, %age is the generation percentage, + and rndlevs is the number of similar levels available to + choose from. + + LEVELDESC: type + + where type is the level type, (see DESCRIPTION, above). The + type is used to override any pre-set value used to describe + the entire dungeon, for this level only. + + BRANCH: name @ ( base , rand ) [ stair | no_up | no_down | + portal ] [ up | down ] + + where name is the name of the dungeon to branch to, and ( + base , rand ) is the location of the branch. The last two + optional arguments are the branch type and branch direction. + The type of a branch can be a two-way stair connection, a + one-way stair connection, or a magic portal. A one-way + stair is described by the types no_up and no_down which + specify which stair direction is missing. The default + branch type is stair. The direction for a stair can be + either up or down; direction is not applicable to portals. + The default direction is down. + + + +Dec Last change: 12 2 + + + + + + +DGN_COMP(6) 1995 DGN_COMP(6) + + + + CHAINBRANCH: name prev_name + ( base , rand ) [ stair | + no_up | no_down | portal ] [ up | down ] + + where name is the name of the dungeon to branch to, + prev_name is the name of a previously defined level and ( + base , rand ) is the offset from the level being chained + from. The optional branch type and direction are the same + as described above. + +GENERIC RULES + Each dungeon must have a unique bonesmarker , and each spe- + cial level must have a bonesmarker unique within its dungeon + (letters may be reused in different dungeons). If the + bonesmarker has the special value "none", no bones files + will be created for that level or dungeon. + + The value base may be in the range of 1 to MAXLEVEL (as + defined in global.h ). + + The value rand may be in the range of -1 to MAXLEVEL. + + If rand is -1 it will be replaced with the value + (num_dunlevs(dungeon) - base) during the load process (ie. + from here to the end of the dungeon). + + If rand is 0 the level is located absolutely at base. + + Branches don't have a probability. Dungeons do. If a + dungeon fails to be generated during load, all its levels + and branches are skipped. + + No level or branch may be chained from a level with a per- + centage generation probability. This is to prevent non- + resolution during the load. In addition, no branch may be + made from a dungeon with a percentage generation probability + for the same reason. + + As a general rule using the dungeon compiler: + + If a dungeon has a protofile name associated with it (eg. + tower) that file will be used. + + If a special level is present, it will override the above + rule and the appropriate file will be loaded. + + If neither of the above are present, the standard generator + will take over and make a "normal" level. + + A level alignment, if present, will override the alignment + of the dungeon that it exists within. + + + + + +Dec Last change: 12 3 + + + + + + +DGN_COMP(6) 1995 DGN_COMP(6) + + + +EXAMPLE + Here is the current syntax of the dungeon compiler's + "language": + + + # + # The dungeon description file for the "standard" original + # 3.0 NetHack. + # + DUNGEON: "The Dungeons of Doom" "D" (25, 5) + LEVEL: "rogue" "none" @ (15, 4) + LEVEL: "oracle" "none" @ (5, 7) + LEVEL: "bigroom" "B" @ (12, 3) 15 + LEVEL: "medusa" "none" @ (20, 5) + CHAINLEVEL: "castle" "medusa" + (1, 4) + CHAINBRANCH: "Hell" "castle" + (0, 0) no_down + BRANCH: "The Astral Plane" @ (1, 0) no_down up + + DUNGEON: "Hell" "H" (25, 5) + DESCRIPTION: mazelike + DESCRIPTION: hellish + BRANCH: "Vlad's Tower" @ (13, 5) up + LEVEL: "wizard" "none" @ (15, 10) + LEVEL: "fakewiz" "A" @ (5, 5) + LEVEL: "fakewiz" "B" @ (10, 5) + LEVEL: "fakewiz" "C" @ (15, 5) + LEVEL: "fakewiz" "D" @ (20, 5) + LEVEL: "fakewiz" "E" @ (25, 5) + + DUNGEON: "Vlad's Tower" "T" (3, 0) + PROTOFILE: "tower" + DESCRIPTION: mazelike + ENTRY: -1 + + DUNGEON: "The Astral Plane" "A" (1, 0) + DESCRIPTION: mazelike + PROTOFILE: "endgame" + + NOTES: + Lines beginning with '#' are considered comments. + A special level must be explicitly aligned. The alignment + of the dungeon it is in only applies to non-special levels + within that dungeon. + +AUTHOR + M. Stephenson (from the level compiler by Jean-Christophe + Collet). + +SEE ALSO + lev_comp(6), nethack(6) + + + + + +Dec Last change: 12 4 + + + + + + +DGN_COMP(6) 1995 DGN_COMP(6) + + + +BUGS + Probably infinite. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Dec Last change: 12 5 + + + diff --git a/doc/dlb.6 b/doc/dlb.6 new file mode 100644 index 0000000..eb018b2 --- /dev/null +++ b/doc/dlb.6 @@ -0,0 +1,83 @@ +.TH DLB 6 "28 Oct 1993" +.UC 4 +.SH NAME +dlb \- NetHack data librarian +.SH SYNOPSIS +.B dlb +{ +.B xct +} +[ +.B vfIC +] +arguments... +[ +.B files... +] +.SH DESCRIPTION +.PP +.I Dlb +is a file archiving tool in the spirit (and tradition) of tar for +NetHack version 3.1 and higher. It is used to maintain the +archive files from which NetHack reads special level files and other +read-only information. Note that like tar the command and option +specifiers are specified as a continuous string and are followed +by any arguments required in the same order as the option specifiers. +.PP +This facility is optional and may be excluded during NetHack +configuration. +.SH COMMANDS +The +.B x +command causes +.I dlb +to extract the contents of the archive into the current directory. +.PP +The +.B c +command causes +.I dlb +to create a new archive from files in the current directory. +.PP +The +.B t +command lists the files in the archive. +.SH OPTIONS AND ARGUMENTS +.DT +.ta \w'f archive\ \ \ 'u +v verbose output +.br +.sp 1 +f archive specify the archive. Default if f not specified is +LIBFILE (usually the nhdat file in the playground). +.br +.sp 1 +I lfile specify the file containing the list of files to +put in to or extract from the archive if no files are listed +on the command line. Default for archive creation if no files +are listed is LIBLISTFILE. +.br +.sp 1 +C dir change directory. Changes directory before trying to +read any files (including the archive and the lfile). +.br +.SH EXAMPLES +Create the default archive from the default file list: +.br + dlb c +.sp 1 +List the contents of the archive 'foo': +.br + dlb tf foo +.SH AUTHOR +.PP +Kenneth Lorber +.SH "SEE ALSO" +.PP +nethack(6), tar(1) +.SH BUGS +.PP +Not a good tar emulation; - does not mean stdin or stdout. +Should include an optional compression facility. +Not all read-only files for NetHack can be read out of an archive; +examining the source is the only way to know which files can be. diff --git a/doc/dlb.txt b/doc/dlb.txt new file mode 100644 index 0000000..afc09c2 --- /dev/null +++ b/doc/dlb.txt @@ -0,0 +1,132 @@ + + + +DLB(6) 1993 DLB(6) + + + +NAME + dlb - NetHack data librarian + +SYNOPSIS + dlb { xct } [ vfIC ] arguments... [ files... ] + +DESCRIPTION + Dlb is a file archiving tool in the spirit (and tradition) + of tar for NetHack version 3.1 and higher. It is used to + maintain the archive files from which NetHack reads special + level files and other read-only information. Note that like + tar the command and option specifiers are specified as a + continuous string and are followed by any arguments required + in the same order as the option specifiers. + + This facility is optional and may be excluded during NetHack + configuration. + +COMMANDS + The x command causes dlb to extract the contents of the + archive into the current directory. + + The c command causes dlb to create a new archive from files + in the current directory. + + The t command lists the files in the archive. + +OPTIONS AND ARGUMENTS + v verbose output + + f archive specify the archive. Default if f not specified + is LIBFILE (usually the nhdat file in the playground). + + I lfile specify the file containing the list of files to + put in to or extract from the archive if no files are listed + on the command line. Default for archive creation if no + files are listed is LIBLISTFILE. + + C dir change directory. Changes directory before try- + ing to read any files (including the archive and the lfile). + +EXAMPLES + Create the default archive from the default file list: + dlb c + + List the contents of the archive 'foo': + dlb tf foo + +AUTHOR + Kenneth Lorber + + + + + +Oct Last change: 28 1 + + + + + + +DLB(6) 1993 DLB(6) + + + +SEE ALSO + nethack(6), tar(1) + +BUGS + Not a good tar emulation; - does not mean stdin or stdout. + Should include an optional compression facility. Not all + read-only files for NetHack can be read out of an archive; + examining the source is the only way to know which files can + be. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Oct Last change: 28 2 + + + diff --git a/doc/fixes22.0 b/doc/fixes22.0 new file mode 100644 index 0000000..decc0b9 --- /dev/null +++ b/doc/fixes22.0 @@ -0,0 +1,353 @@ + NetHack Fixes List Revision 2.2 + +Fixes and Modified Features +--------------------------- +Guidebook.mn New file "Guide to the Mazes of Menace". By Eric Raymond. +Guidebook A file for preparation using the "mn" macros supplied with + the 2.11 news release, as well as an ascii version of the + same. + +NetHack.cnf Sample configuration file for the PC. (creps@silver) + +Makefiles Corrected problem in which the linking was done on build and +(unix/xenix) on install. (Contributed by Janet Walz - walz@mimsy) + +Makefile.att Added a makefile for the AT&T Unix PC using shared libraries. + (Contributed by ahby@umn-cs) + +Makefile.pc Streamlined compilation of main.o, tty.o, and unix.o +Makefile.tcc (Contributed by polder@cs.vu.nl). + +data.base deletion of duplicate lines and spelling fixes. (sweet@scubed) + +invent.c REDO problem with "What do you want to..." text fixed. + down stairway identification fixed. + Alloc "buf" to allow for variable length HI/HE. (tom@uw-warp) + +engrave.c Correction to "feel" code. (mike@genat) + Corrected switch for message determination. (patrickm@hpisof0) + BURN'ed engravings made un-erasable again. (kyrimis@princeton) + +pri.c Added colour highliting functions (sweet@scubed) + +prisym.c changed "symbol.room" to "ROOM_SYM" (one I missed) + (Ralf.Brown@b.gp.cs.cmu.edu) + Changed "dirlet()" to return an int. (maartenj@cs.vu.nl) + +msdos.c Changed "symbol" to "showsyms" (Ralf.Brown@b.gp.cs.cmu.edu) + Fixed up REDO & IBMBIOS stuff. (Kevin Sweet - sweet@scubed) + +do.c Dropping gold asked for only when gold posessed. (walz@mimsy) + Potential unsigned value problem fixed (u.ucreamed) + Added leash dropping code. (maartenj@cs.vu.nl) + Blind modifications for blindfolding. (eric@snark) + Value wrap fixed for u.ucreamed + +fight.c Dog's name now used in hitting avoidence message. (walz@mimsy) + Variable initialization fixed w.r.t. #ifdef / #else. + (Reported by Erwin Unruh - unruh@infbs) + Added giant rats and kobolds back into code. (sweet@scubed) + +spell.c Potential unsigned value problem fixed (u.ulevel). + Typos corrected. (Tom May - tom@uw-warp) + Blind modifications for blindfolding. (eric@snark) + +shk.c "inshop" crash bug corrected (many sources). + extern declaration of carrying() moved to avoid a Turbo-C + type mismatch msg. (Ralf.Brown@b.gp.cs.cmu.edu) + Added new "online()" which executes faster. (tom@uw-warp) + Blind modifications for blindfolding. (eric@snark) + Added item pricing shopkeeper talk. + (Idea from a hacked up 1.0.1 source sent in by michael@stb) + Cleaned up Kops code. (sweet@scubed) + +mhitu.c Argument mismatches fixed. (walz@mimsy) + Scorpion/spider mixup fix. (William LeFebvre - phil@rice.edu) + Blind modifications for blindfolding. (eric@snark) + +potion.c Argument mismatch fixed. (walz@mimsy) + Blind modifications for blindfolding. (eric@snark) + Poison handling made more dependant on poison resistance. + (From an idea by Steve Creps - creps@silver) + +mklev.c Fixed up installation of vamp traps. (sweet@scubed) + +makemon.c Monster creation location bug fixed. (walz@mimsy) + Monster creation crash fixed. (many sources) + Monster posessions bug fixed. (S. Wrammerfors stewr@obelix) + Added giant rats and kobolds back into code. (sweet@scubed) + +hack.c "Elbereth" effectiveness increased under "HARD" option to + be reasonable. (walz@mimsy) + Declaration of "register struct monst *m_at()" fixed. (many) + Typo fixed. (tom@uw-warp) + Fixed scroll of scare monster pickup problems (and giveaway) + (polder@cs.vu.nl) + Documentation modifications for blindfolding. (eric@snark) + +ioctl.c ioctl call for SET changed to function properly under + +unixtty.c Sys V R3 mods. (tom@uw-warp) + +decl.c in_doagain initialized. (many sources) + +wield.c Ability to remove cursed weapons w. w- removed. (many sources) + +options.c Major rewrite of options help. Now uses pager. (mike@genat) + Rewrote GRAPHICS setup. (maartenj@cs.vu.nl) + Allowed reassignment of inventory order #ifdef DGK + (polder@cs.vu.nl) + +pray.c Fixed mk_obj of spellbook under all conditions to make book + if "SPELLS" defined, and scroll otherwise. (unruh@infbs) + Fixed typo in "gods angry" text. (tom@uw-warp) + Fixed blessing code. (Simon Brown - simon@its63b) + Blind modifications for blindfolding. (eric@snark) + +zap.c Potion of invis. breakage message improved. (unruh@infbs) + Added WAN_PROBING to "zapyourself". + Changed "dirlet()" to return an int. (maartenj@cs.vu.nl) + Fixed cancellation code to work properly on wands (spe + set to -1 instead of 0) this means no infinite wands of + wishing. (Ron Wessels - ron@utcsri) + Fixed bug in "buzz()" causing crash when destroying a + trapper from inside with a wand/spell. (mike@genat) + Added fcn to destroy wands with zero charges. (sweet@scubed) + +pcmain.c Added a routine to zero out the fileinfo array in order to + prevent crashes on level change. (ralf@b.gp.cs.cmu.edu) + Added chdir to HACKDIR before looking for .CNF file. + Added call "uptodate(savefile)". (polder@cs.vu.nl) + +pager.c changed "cornline()" to use xputs for HI/HE. (tom@uw-warp) + added choice for dowhatis() to allow letter or cursor object + selection. (polder@cs.vu.nl) + +cmd.c Added ^W (wish) and ^I (ident-all) commands for WIZARD-mode. + (Paul Polderman - polder@cs.vu.nl) + Added "Z" as alternate to "# cast" (Eric Raymond - eric@snark) + +u_init.c Expanded a tab which didn't show in raw mode. + Changed trobj.trotyp to "unsigned short" to avoid >255 + problems. (Maarten Jan Huisjes - maartenj@cs.vu.nl) + Removed wand of wishing from WIZARD's inventory (due to + the above cmd additions). (polder@cs.vu.nl) + Fixed declaration of leash. (simon@its63b) + Beefed up Wizard class. + Added Wakizashi for Samurai. + Added holy water for Priest(ess)es. + Modifications to provide blindfolds. (eric@snark) + +end.c changed inventory identification on death to list form. + (polder@cs.vu.nl) + added hallucination effects to done_in_by() + added posession of amulet flag for scoreboard (sweet@scubed) + +wizard.c corrected "nasties" decl. (maartenj@cs.vu.nl) + Blind modifications for blindfolding. (eric@snark) + +do_wear.c Prot. from shape changers logic fixed. (maartenj@cs.vu.nl) + +lev.c Prot. from shape changers logic fixed. (maartenj@cs.vu.nl) + +mon.c Inserted cast to fix compiler warning. (maartenj@cs.vu.nl) + Nymphs now leave potions of object detection when killed. + Kops now don't leave treasure behind. (sweet@scubed) + +topl.c Changed size of "toplines" to avoid overflow in "parseoptions" + when help is asked for. (probably n/a) (maartenj@cs.vu.nl) + +topten.c Added longer death descriptions, including name of + shopkeeper who killed character. (many sources) + +termcap.c Changed allocation of HI/HO for copying SI/SO to allow room + for null. (maartenj@cs.vu.nl) + Added PCHack 3.61 termcap stuff. + Added colour highliting code. (sweet@scubed) + +version.c Expanded a tab for rawmode io. (maartenj@cs.vu.nl) + +objnam.c Allow the WIZARD to wish for really excessive objects. + (polder@cs.vu.nl) + +makedefs.c Added "freopen" which works (MSC 4.0 drops first couple + of lines). Solves missing #define AMULET... problem. + (Nathan Glasser - nathan@mit-eddie) + +rnd.c Changed around random number generation: + BSD uses "random()". (Paul Eggert - eggert@grand) + SYSV uses "lrand48()". (mike@genat from above) + +eat.c Changed "choke()" code to waste food rather than choke on + it #ifndef HARD. (Allan Pratt - apratt@atari) + Blind modifications for blindfolding. (eric@snark) + +objects.h added blindfold object (tool). (eric@snark) + +you.h changed Blind/BLIND to Blinded/Blinded + added Blindfolded/BLINDFOLDED + redefined Blind in terms of above parameters. (eric@snark) + +apply.c added blindfold code. (eric@snark) + +timeout.c Blind modifications for blindfolding. (eric@snark) + +sit.c Blind modifications for blindfolding. (eric@snark) + +trap.c Blind modifications for blindfolding. (eric@snark) + Level teleportation to hell fixed so that it will not + do so unless character has Fire_resistance. (many sources) + Added polymorph trap. (many sources) + +monmove.c added check on presence of "fobj" before atl() call + to avoid potential segmentation problem with ROCKMOLE. + (Reported by Doug Rudoff - doug@wiley) + +various files Fixed typos. Also converted British English words to + American English for uniformity. (Original list of typos + submitted by Steve Creps - creps@silver) + + +New Features +------------ + 1) New flags in "config.h" (some of these were included in 1.4f): + + COM_COMPL Command line completion by John S. Bien + GRAPHICS Funky screen character support (Eric S. Raymond) + HACKOPTIONS Support DGK-style HACKOPTIONS processing (ESR) + RPH Various hacks by Richard P. Hughey + KJSMODS Various changes made by Kevin Sweet + BVH Additions by Bruce Holloway + + In addition, in an MSDOS enviornment, when GRAPHICS is defined: + + MSDOSCOLOR Colour highlighting of monsters, etc. + + Of the above, I haven't tested HACKOPTIONS and MSDOSCOLOR. If you + find bugs in these, send me the reports. + + 2) New objects: + + blindfold - allows you to avoid the gaze of a Floating Eye and to + use your telepathy on command if you have it. + + mirror - scares monsters if you use it on them (and other uses). + + ring of polymorph - (usually cursed) forces random polymorphs. + + ring of polymorph control - prevents system shock and allows choice of + creature to polymorph into. + + 3) New Files: + + - A new set of documentation, the "Guidebook to the Mazes of Menace" + has been supplied by Eric S. Raymond. The guidebook is written for + nroff using the "mn" macro set supplied with Bnews 2.11 or greater. + Since not everyone has these macros, I have run the guidebook through + nroff, and supplied it in flat ascii format as well. [Moderator's + note: because of past problems, I ran the formatted version + through "col -b" before passing it on to remove ^H's, etc. -br] + + - A copy of "HACK.CNF" which has been renamed "NetHack.cnf" was + supplied by Steve Creps. The file decl.c has been updated to reflect + this change. + + - A new "Makefile" for the AT&T Unix machines has been added. + + - I was hoping to get documentation on "NANSI.SYS" as well, but got + no responses to the mail I sent the author, direct and via Bill + Randle at tekred. As per usual, I will gladly publish any relevant + documentation I get. + + 4) Major game changes: + + - Shop generation has been significantly changed. A new structure + has been introduced which allows shops (except the "general" type) + to have up to three different types of object inside. There is also + a new "distribution pattern" parameter which tells the generation + code how to lay out the shop (this is preliminary to the addition of + two new types of shop, the temple and barracks - more on this later). + + - Shopkeepers will now tell you how much they expect for each object + you pick up. This gives you the ability to haggle with the merchant + in question by dropping and picking up objects until you are more or + less satisfied with the price. I have re-written "getprice()" in + shk.c in an attempt to make sure that you cannot actually sell any + particular object for more than the shopkeeper will charge for it. + + - Another change to shopkeepers has them potentially getting angry if + you stay beside them after not paying your bill. Each they time they + ask you to pay up, there is a chance they will decide they don't like + people who don't pay... + + - A new monster, the hydra, has been added (as you have probably seen + on the net). I haven't had much chance to test out this feature of + the game. Mirrors have also been added, and seem to work quite well. + + - Changes have been made to the object ocurrence chances in objects.h, + so that the relatively rare tools, etc. have at least a 1% chance of + showing up. + + - Throwing and zapping code has been modified so that there is a + chance that said can be done through a doorway. Bolts can still + bounce however... + + - The infamous and dreaded makemon() bug has been eliminated. In + addition to this, "r"ats and "K"obolds have been added back into the + game. "K"ops no longer leave treasure (just what they were carrying, + plus maybe a club or whistle). + + - Two new "super"swords have been added. They are the katana named + "Snickersnee" which is +5 on damage (due to sharpness), and the long + sword "Excalibur" which is +rnd(10) to hit, +5 on damage, and has a + couple of other features I won't go into right now. The only way + for a character to get "Excalibur" is as a gift from someone. You + cannot write the word "Excalibur" on things for some reason... + + - There have been two additions to disallow infinite wand charges. + First of all, wands with less than zero charges will automatically + turn to dust (thanks to Kevin Sweet). Next, a wand of cancellation + will set the number of charges in the wand to -1, which will make it + forever useless, (thanks to Ron Wessels). + + 5) Minor game changes: + + - The fountain code has been tightened slightly so you can no longer + dip objects into a fountain or drink from one while you are floating + in mid-air due to levitation. + + - Teleporting to hell via a teleportation trap will no longer occur + if the character does not have fire resistance. I found this just + too arbitrary a way to die (and so did several other people who com- + plained about it). + + - A new trap, the "polymorph" trap has been added by Richard Hughey. + It's inclusion is dependant on having "KAA" defined. + + - In wizard mode, the wizard player has infinite wishes, and the + ability to instantly identify everything (s)he is carrying. The wizard + player is also no longer limited by the standard multiple / bonus res- + trictions on objects wished for. + + - Random number generation has been changed around to make it (I hope) + more unpredictable. + + - A large number of typos have been fixed, and all of the British + spellings converted to American. I would like to see a shell script + to allow conversion back (or something like that) in the future. + + - I have done a "make depend" for the makefiles to reflect a slight + restructuring in the order of inclusion of header files. + + 6) Future additions: + + - Steve Creps is working on "barracks" and "soldier" code which is + now ready for addition. I have added the "soldier" side into the + game, but haven't really tested it. Steve will be adding the + "barracks" section in and sending me the resulting patches. There + will be a minor (read patch) release as soon as he can get the code + integrated into this release and sent up here to me. + + - There are also several other new room projects in the works which + should be able to be included in that minor release, along with any + bug reports that are made in the interim. diff --git a/doc/fixes30.0 b/doc/fixes30.0 new file mode 100644 index 0000000..737d504 --- /dev/null +++ b/doc/fixes30.0 @@ -0,0 +1,164 @@ +$RCSfile: fixes30.0,v $ $Revision: 1.1.2.2 $ $Date: 2003/05/11 15:10:00 $ + +[This is a partial list supplied by Ken Arromdee long after the fact] + +General Fixes and Modified Features +----------------------------------- +dropping a weapon, then picking it up while blind causes it to become + unidentified but it remains that way after your vision returns +when blind and feeling the floor, the objects then on the floor should + have their characteristics become unknown +zapping a wand of probing at yourself works, however using a stethoscope + on yourself does not +trolls that come back to life don't retain their names +if you look at what is on the ground and there is more than a screenful, + quantities are not printed (i.e. "food rations") +if pickup was off and you moved onto a spot with both engraving and objects, + you got told what the engraving was twice +if a Keystone Kop threw a pie and the pie hit a shopkeeper, the shopkeeper + became angry +"two handed sword" is spelled with and without a hyphen in various places +trying to descend stairs while levitating gives a message where "You're" + is misspelled as "Your" +the 'V' command gives a history that is not quite correct; it wasn't + Ken Arromdee that merged PC Hack and Unix Hack; rather, PC Hack + was derived from Unix Hack by Don Kneller. It was (I think) + Mike Stephenson who merged the many Hack versions. +the uranium wand gets described as "an" uranium wand +potions which have been both called and identified have the called instead + of the identified name printed out +status line at the bottom of the screen is formatted strangely for the + first 10 moves; after 10 moves it becomes normal +if you polymorph yourself into a rockmole, you are unable to dig through + rock +a stethoscope or wand of probing, used on a monster, printed out a format + with spaces in it (such as %-3d instead of %d) causing such messages + to be longer than necessary +the number printed out for damage by stethoscope or wand of probing is + completely incorrect +sometimes your dog would turn into a bat (retaining the same name and + tameness) when saving and restoring,going down and back up + and in bones levels +ghosts on bones levels are replaced by little dogs +if you are polymorphed and drink a potion of healing which raises your + maximum monster hit points, your current hit points only are raised, so + you may have (for instance) 37 out of 36 hit points +when you pick up something from a shop, the message is something like + "For you, kind sir, only 5 for this " on systems where + pointer is not the same size as integer +removing a leash from a dog produces garbage, and also can sometimes + crash the game +when the Wizard is mentioned, you get garbage +if you tame a wild dog, and then try to hit it, the safe-attack routine + says "You stop to avoid hitting .", or sometimes garbage +when you are invisible but can see invisible, drinking at a fountain which + dries up causes your symbol to be erased from the screen. +pushing an enormous rock onto a teleport trap deletes it instead of + teleporting it +sometimes returning to human form from a polymorph would leave you with + only 1 current hit point left +sometimes polymorphing would leave you with an 18/** strength +if you used a stethoscope on a wall and a creature moved into the revealed + door before you did, when creature moved off, the door was replaced + by a wall symbol again, until you walked through it +playing a game with fountain symbols set to one character, and restoring it + using a configuration file having them set to a different character, + leaves the fountain symbols as the old character +problem with food poisoning and blindness where you could end up permanently + blind, not even reversible by healing etc. +you could not wish for a C-ration or K-ration due to lowercase conversion +zapping a wand of death at yourself, and the death touch of a demon + prince, killed you even if you were polymorphed into an undead, + but bouncing death rays didn't +if you threw an object in a shop, it landed on the floor still unpaid + zapping a wand of cancellation at yourself turned all your objects to + -1 instead of +0; -1 was only needed for wands (since cancelling wands + to +0 allowed a sneaky way of getting infinite wishes) +#remove command is incorrectly described as removing a cursed + object, but it actually removes only iron balls +at least some messages given for special abilities when you are polymorphed + printed out the monster type even if you were blind +wishing for a scroll of mail, with MAIL undefined, is permitted but + reading the scroll gave an "impossible" error message; change it to + read: "This seems to be junk mail directed to the finder of the Eye + of Larn." +if you were blind, effects of scrolls, et al, still said things such as + "Your mace glows green for a moment," "Your left ring glows white" +the message "The xan pricks your ___ leg" still mentions the type of + monster (xan) when you are blind +polymorphing could change your gender, but when the game ended you were + still described as a "Cave-man" or "Priest" instead of + "Cave-woman" or "Priestess" +if you got rocks by using a pick-axe on an enormous rock, the rocks + started out blessed +if you escaped from a shop by throwing your iron ball to the exit, the ball + would stay unpaid +any object thrown in a shop stayed unpaid, and you had to + pick the object up again and then drop it for it to become paid +if you were invisible but could see invisible, when you drank from a fountain + that dried up, your symbol got erased from the screen +several times colors were mentioned (i.e. black glows when you pray too much) + but the colors weren't always changed for hallucination +if punished and your chain and 1 other object is on the floor, you + get asked if you want to pick up a specific type of object, + and the chain is given as one of the possible objects +if you polymorph into a "new man", your energy points never change +trolls, when killed, can leave bodies or objects, but they should + always leave bodies so they can regenerate back to life, + whether or not they left objects +demon prince demanding bribe gets randomly relocated instead of being placed + next to you +if you polymorph another tool into a magic marker, the marker always starts + out dried out +hallucination has the problem that if you do something which takes no time, + the monsters still change +if you tried drinking at a fountain while levitating, you got asked if you + wanted to drink, and then you weren't allowed to do it, and + the fountain could still dry up +if you were fire resistant and a magic trap produced a tower of flame, you + also get a second message (a shiver...) +when restoring a game, if there was an error, your saved game still vanishd + for some errors +pets were always referred to as "him" in the leash code +stethoscope in the up or down direction while swallowed referred to "floor" + and "ceiling" instead of to the monster that swallowed you +if you could not save a game, the screen cleared and you were told to + "Continue or Quit", but if you typed C, the game thought you just + gave it a Call command +you could get told "that spellbook was a mimic" even if mimic was on a wall +if you were turning to stone and you polymorphed yourself into a 'c', you + still died from the stoning +if you interrupted the program before it finished loading a saved game, + you were asked if you want your possessions identified +"ctmp" was mistakenly used in MHITU.C when being hit by a rust monster + rusting your helmet but it should have been "!mtmp->mcan" + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +PC: often the same character class, and same player name, will end up + getting 2 scores on the high score list +PC: if your name contained a period and you were playing on a PC, you would + have problems saving games since your save-file name still contained + 2 periods and would thus be illegal; change periods to underscores + when figuring out what the save file should be called +PC: if you polymorphed into a "new man", and changed your name, when you saved + your game the game was still saved under a filename derived from your + old name, and to restore that game you had to specify your old name + +General New Features +-------------------- +when polymorphed into a giant you are now able to pick up and + throw rocks by giving infinite carrying capacity +give the Healer a pair of gloves (the most logical piece of armor + for a physician to start out with) +give the Healer a lot of money to start with (doctors are supposedly + rich, you see) +have the Healer start out with the healing, cure blindness spells + already known +create a "trial mode", similar to Wizard Mode where you get a wand of wishing + with charges and you can't get on the high score list +if you are standing on a trapdoor and you type a '>' symbol, + print "You jump down the hole." and go down to the next level + + diff --git a/doc/fixes31.1 b/doc/fixes31.1 new file mode 100644 index 0000000..402166f --- /dev/null +++ b/doc/fixes31.1 @@ -0,0 +1,110 @@ +Makefile.utl vs. GNU make (*_lex.o needs explicit *_lex.c dependency) +defining MAKE in more makefiles (symptom is "sh: makedefs: not found") +finding test in makefiles (needs shell meta-char, >/dev/null or || exit 1) +splitter hunk sizes with SAS 6.x (wouldn't fit on floppy) + +OpenWindows 2.x documentation (?) +',' and ';' in help files, including Guidebook +Guidebook date (nroff and pre-processed versions) +VMS options syntax (config file description omitted "OPTIONS=" requirement) + +phase of moon for Mac and MSDOS ports -- revert to old, portable phase of + moon code +polymorphing into bees (divide by zero core dump, reported as + floating point error on Sparcs) +monster throwing boulder into pool could free object twice +candle burn-out accessing free'd object while traversing list (Mac core dump) +inappropriate candle merging (lit with unlit) +several levelflags (fountains, etc.) not being handled on special levels +subrooms[] not initialized +coredump on messed DEFAULT_WIN_SYS +deleting lock.0 files on early quit +number_pad/mouse coexistence +jumping onto doors or boulders +tombstone gold neglected gold in containers, although score was correct +initial gold record neglected gold in containers +reeling monsters leaving ghosts (missing newsym) +display getobj prompt for non-REDO +packorder option parsing +inconsisent option parsing with IBM vs. IBMg, etc. +throwing Mjollnir at adjacent hard things causes panic/core dump +throwing at ghosts in walls left objects stuck in walls +throwing unpaid objects in shops (donated to player by shk) +Shopkeeper administration out of order (buying group of used up items) +pricing chains, uball, or other nocharge objects, when they couldn't be sold +knocking uball down a hole by dropping another object caused crash +kicking monsters while levitating (core dump if monster "reels" or killed) +kicking empty space while levitating could give free move (recoil) +panic when nurse is fixing your damage and disappears +core dump 'D'ropping everything with gold but no inventory (null pointer access) +core dump using getpos() "move to feature" response when map shows something + covering furniture (displayed glyph leads to invalid subscript use) +deity gender reference in opening legacy message +makeplural() said "poisoned yas" instead of "poisoned ya" +zapping on entry to water left trails +allow level teleports to be cancelled +excess choking when eating corpses +funny death message (or coredump) when you choke on a tin of spinach + after partially eating something else +Magicbane expulsion confused cutworm() +charging for several overlooked items (mainly magical instruments) +taming a sticking monster (e.g mimic) wouldn't set you free +panic when tinning while standing on stairs with a full pack and object drops +zapping down into ice with wand of digging (core dump) +rust monsters wouldn't ever hurt armor, even non-rustproof armor. conversely, + unrustable things rusted. +mysterious rust damage on damp levels (improper object chain traversal + when any item landed in pool after being thrown or dropped) +very eroded nonrustable/corrodable/flammable objects displayed as "very +0..." +map window wasn't being initialized correctly, causing some 'a'filled screens +using magic marker with full pack leaves you with an item in the '#' slot +spellbook merging caused multiple books to fade away when you re-read + them enough times +Master of Thieves in Tourist quest not created with Bell of Opening, making + game virtually un-winnable +could wish for quest items +If an eel managed to drown you, it would say "Drowned by a drowning" +stop making bones from non-branch portal levels +reading blank scrolls exercised wisdom +inverted use of crystal ball "vision in unclear" feedback message +special room entry messages (shop welcome) given before level change map update +limbless blobs like gelatinous cubes could not pick up +The level compiler missed first level flag if more than one was specified. +The level compiler accumulated level flags when compiling multiple files. +make the 32-bit monster flags fields unsigned long rather than just long +mksobj() created gold a factor of 10 too light +indefinite articles in quest text +Alt-n didn't execute #name on some ports, made Alt-? do #? +correct tense and grammar of various messages +obsolete oracle about using mirror to locate Wizard & Medusa removed +Nurses would zap you with wands, et al, instead of healing you +Shopkeepers residence was sometimes wrong on bones level, esp. in the minetown +Some zaps, esp. by monsters, at the map edge could cause core dumps +panic relmon bug possible when #turn'ing multiple monsters at once +it wasn't possible to get underwater when polymorphed to a swimmer +total digestion wasn't working +lycanthropy could change sex +interrupting dosinkring() allowed free ring identification +killing some hostile humans (like Croesus and priests) lost telepathy, etc. + +tty specific: long input lines and improved interrupt handling +X11: NetHack.ad: "*map*grey" should be "*map*gray" +X11: NetHack.ad: remove excess quotes +HPs and X (SYSV conflict caused by X11 headers) +Handle WM_DELETE_WINDOW protocol in X11 window-port. +X11 popups are now positioned so that the cursor is bottom center. +Both X11 fonts now have a pool symbol that coveres the whole rectangle. +X11_getlin will now allow empty strings to be returned. +X11: implement score in the status window. +X11: make displaying experience optional. +X11: position getline and yn popups center,bottom instead of center,center. +X11: autofocus mode made more reliable at program startup +X11: number of objects removed from containers could wrap negative +X11: allow translation tables +X11: initial window would "sweep" shut and reopen +micros: save and restore progress reports weren't appearing during save + and restore (they would appear immediately afterwards) + also error messages from dots aimed off right edge of screen +Atari: colors were not properly set in medium resolution +Atari: terminal size was set incorrectly after a ^Z +OS/2: HPFS support was incomplete diff --git a/doc/fixes31.2 b/doc/fixes31.2 new file mode 100644 index 0000000..8a790f4 --- /dev/null +++ b/doc/fixes31.2 @@ -0,0 +1,92 @@ +revived barbarians didn't gain levels from potions +pets wouldn't keep their armor or weapons (blessed figurine of Archon) +avoid "rot" v. "burn" damage for organics +identify +/- known rings/armor as "uncursed" +loop through identification as long as additional "charges" are available +successive disease attacks made you less sick, also make nurses cure sickness +NO_MAILREADER non-mail daemon message fix +make fortune cookies, pancakes yellow and lightning white +cavemen were red while cavewomen and all other characters were "domestic" +monsters might not change color when growing up +redisplay cancelled objects in case their color changed (potions, spellbooks) +ask if you want to play heard passtune +add: make ^T intrinsic teleports use magical energy +don't encase the Amulet (or invocation tools) inside cockatrice induced statues +fix eating taking 1 turn less than number necessary, cheating you of nutrition +fix inventory to avoid % in user-supplied names [pline(xprname())] +fix crash when #force destroys unpaid chest in shop +fix crash when movement reaches edge of screen on rogue level +fix crash when two items in a row vanish from magic bag +set bknown when Priests see item, fixing missing checks elsewhere +set alignment of priest's quest artifact to match priest's starting alignment +if the Mitre of Holiness gets stolen by the Wizard, reset int & wis stats +always unlock quest stairs whenever leader acknowledges quest completion +make mysterious force which locks quest stairs block objects as well as player +duplicate shopkeeper names were possible on town level +trying to rename shopkeepers left dangling pointers +credit balance can prevent Kop summoning when player leaves shop suddenly +suppress several shopkeeper messages (and buy|sell) is shk is asleep|paralyzed +using charged objects ((engrave) wands, horns of plenty, etc.) in shops was free +don't double bill for food eaten off shop floor (multi-turn to eat only) +fix to prevent unpaid objects differing only in price from merging (only in + inventory; they're not "unpaid" any more when dropped, so might merge) +missing #ifdef SOUNDS in vault.c +inconsistent MFLOPPY/MICRO use of dotcnt for saving +add: wands of striking break potions +add: directional healing spells +add: pickup_types option, pickup -> autopickup +disclosure option takes optional string value; killed counts and genocide list +give better message when trying to set options which are compiled out +add: hungry pets make begging noises +centaurs and mariliths could wear inappropriate armor +remove Medusa's tail and give her wings +separate creatures that don't breathe from creatures that can breathe water +you can't choke if you don't breathe +Fort Knox eels were placed outside moat +jellies for Juiblex, not Jabberwocks +don't heal while praying, so that gods are more likely to heal you +fix fireballs burning fire resistant players +floating eyes could paralyze monsters indefinitely +fix monster sleep/wakeup induced my musical instruments +players hit by wands of sleep get chance to wake up when attacked by monster +iron balls keep their "very heavy" weight +fix vorpal blade/long worm misses +allow monsters to kill players with vorpal blade and tsurugi +fix objects falling through trapdoors; "shipping destination"'s overloaded use + of obj coordinates could cause bogus screen update attempt (y >= ROWNO) +prevent bad argument to rnl() from prayer_done() when praying in gehennom +display message when boulder is pushed into pit +avoid monsters deciding displaced or invisible characters were off the screen +kicking embedded objects loose would break fobj chain (-> freeobj() panic) +when using the cursor to look at remote objects, if the target object is + embedded in something, mention that fact +dropping gold inside a monster -> freeobj() panic +gold picked up by pets wasn't in mgold and thus didn't show up on probing +fix "stop *opening* the lock" even when you were locking it +succubus stealing ring of adornment caused crash if she already had one +monsters created by monster reading create monster didn't appear for scroll ID +monsters explicitly casting spells of sleep or cold had messages reversed +elf character could get impossible("snow boots not found?") stepping onto ice +accept gray/grey except for spellbooks +keep monsters from teleporting on no-teleport levels +don't allow digging of trapdoors on the three wizard levels +don't allow digging down (including drum of earthquake) to destroy portals +fix digging down from within walls and solid rock; monsters could enter + the resulting trapdoor/pit, but players couldn't +fix digging down while flying; don't fall through +destroy any engraving if a hole is dug down thru it, or ice it's in gets melted +change many hardcoded "floor"s and a few "ground"s to use actual surface type +fix blessed unicorn horn's restore ability feature (strength gain when weak) +underwater: prevent turbulence from placing character inside solid wall +underwater: suppress "you inhale water" message when removing amulet of + magical breathing if you're polymorphed into a swimmer or nonbreather +when using the 'A' command to take off armor from underneath other armor, + account for time needed to take off and put back on outer garment(s) +don't create doors over pits and trap doors when using a wand of locking +fix mapping when you drop objects while following the guard out of a vault + +X11: some status didn't show up after restore +UNIX: fix potential security hole accessing file through NETHACKOPTIONS setuid +VMS: disable installed privileges when assigning i/o channel to terminal, + opening termcap, or accessing user-specified config file +Amiga: invalid showscore and numberpad options inserted by frontend diff --git a/doc/fixes31.3 b/doc/fixes31.3 new file mode 100644 index 0000000..c700d51 --- /dev/null +++ b/doc/fixes31.3 @@ -0,0 +1,22 @@ +avoid divide by zero crash for eggs produced from horn of plenty +prevent monsters from eating the invocation tools or Rider corpses +make artifacts much less likely to be destroyed +fix freezing lava in front a raised drawbridge +fix digging pits in front of raised drawbridge; improve digging next to pools +since random teleports have no --More-- prompt, have them purge REDO data + to avoid repeating previous command before noticing changed context +make magic portals function for monsters +make numerous priest blessings increasingly rare, with an upper limit +fix missing quotation marks in quest speech +prevent monsters like killer bees from opening tins +add: keep track of # player deaths, save game start time in struct u +don't used "uncursed" with gold in containers in ending inventory display + +PC: Fix black/gray/white display problem + +X11: set input text portion of the dialog box so it is the width of the box +X11: fix message window seperator redraw problem (thanks to + Ivan D. Brawley ) +VMS: avoid false "trickery" detection for bonesX#.##;1 levels +VMS: prevent display of really long broadcast messages from clobbering stack +VMS: prevent endless input loop upon remote terminal disconnect diff --git a/doc/fixes32.0 b/doc/fixes32.0 new file mode 100644 index 0000000..d811b7b --- /dev/null +++ b/doc/fixes32.0 @@ -0,0 +1,344 @@ +protect the Amulet and invocation tools from being destroyed when a + disintegrated monster's inventory gets deleted +prevent the Amulet and invocation tools from being buried, similar to box + behavior (Book of the Dead would rot away as paper when buried) +don't let polymorphed players eat any of the invocation tools +pets are no longer highlighted when hallucinating +keep glass gems from shattering in chests +return errors from dgn_comp and lev_comp when called for +fix hallucinated fruit juice message +fix several monsters conveying inappropriate resistances +fix misspellings of "Assassins' Guild" and "Minion of Huhetotl" +set personal name flag for Pelias and Thoth Amon; clear it for Chromatic Dragon +don't say "Picking the lock..." when using a skeleton key +give feedback when applying key to current location but no boxes are present +can't manipulate floor locks (chests) while levitating +don't crash onto sink if permanently levitating due to eating a ring +avoid resuming interrupted lock operation on chest that's been polymorphed +wide-angle disintegration Beams O' Wrath disintegrate non-resistant + shields and armor +don't access zapped wand after it's been destroyed by divine lightning bolt +separate graphics symbols for each trap, differently-colored traps +allow wishes for greased objects, correct wishes for "poisoned + rustproof" objects +damage was calculated incorrectly when hitting with statues and boulders +allow digging to finish when digging statues embedded in undiggable walls +list identified invocation tools as "the item" instead of "a item" +ignore rknown for unerodable objects when determining if it's fully identified +flush output after eating mimic so '$' appears right away +update botl for spell power after ^T or #invoke of "energy boost" artifact +correct hunger check when casting spells +correct various messages +fix deliberately jumping into Stronghold trap doors +make random level changes while escaping with Amulet more equitable +when mysterious force randomly picks a location on the current level, send + player into Wizard's tower if and only if already inside that tower +any level change from one tower level to another preserves occupancy state +mysterious force won't kick in when using portal to go up into Wizard's tower +avoid "bad cursor position" situation when mystery force or cursed gain + level potion causes level change within the Wizard's tower +don't allow the Wizard to be resurrected on the Astral level +only list "likely" objects when prompting for what to #invoke +reset encumbrance and movement rate during successful prayer, in case it + cures lycanthropy +prevent cursed weapon that slips when thrown by monster from embedding in stone +ki-rin is not humanoid +all elves can see invisible +gain intrinisics from eating amulets +lose divine protection by killing co-aligned priests or converting alignment +have quest leader check for absolute alignment as well as for piousness +fix tombstone message when dying from kicking door while levitating +bite, &c. attacks on displaced images said "swings wildly and misses" +calculate score before creating bones, otherwise gold in bags gets overlooked +Unique monsters no longer placed in bones files +for blessed genocide, don't report failure for other classes' quest monsters +could get both compressed and uncompressed explore mode save files +ZEROCOMP's bwrite ignored possibility of write failure +mimics imitating fruit caused "Bad fruit #0" on help commands +fix off by one bug in level teleport trap destination for monsters +if g.cube eats a non-empty container, engulf contents rather than devour them +allow wizard to use blessed genocide of '*' to wipe out all monsters on level +when digging a hole in ice, don't describe it as digging in the "floor" + and unearth any objects buried there even when it refills with water +when digging in a pit trap which ends up filling with water instead of + becoming trap door, remove the trap; likewise for overflowing fountains +can't dig pits under drawbridge portcullis; break bridge if hole would be made +can't dig while caught in a web +don't "swing your pick-axe through thin air" if target location is a web +mark webs as seen when "monster is caught in a web" message is given +whirly monsters pass through webs; some huge monsters tear them +Sting cuts through webs +have shk use plural if called for when referring to player's pick-axe(s) +fix price stated by shk when picking up container holding merged items +fix price stated by shk for #chat when standing on a container +don't adjust food price due to hunger when player is selling, only when buying +don't double bill shop corpses turned into tins +don't make mundane objects known when they're outside the shk's expertise +change to have shks possibly identify items sold for credit just like for cash +when player sells something to broke shk for credit, don't offer more for it + in credit than will be charged for it if player buys the item back +when selling items in shop, don't try to adjust message/prompt based on COLNO +when dying in shop entrance, make sure inventory in bones data is placed all + the way inside the shop, hence owned by the shk +make shk notice when shop door destroyed by wand or spell or digging downward +reset unpaid objects if shk poly'd into jumpy monster teleports out of shop +fix handling for shop objects kicked or thrown from one shop into another + and for shop objects hit by cancellation beam +add potions of oil; lamps can be refilled with them +dipping rusty weapons in potions of oil removes rust +allowing drinking from surrounding water if you are underwater +fix non-merging potions of water due to water damage's incompatible dilution +fix mon-merging scrolls of blank paper due to SCR_SCARE_MONSTER's spe usage +fix D(ropping) subset of wielded darts,&c (worn mask got out of synch) +fix #adjust merging wielded darts,&c into non-wielded ones +allow #adjust when fixinv option disabled +fix getobj's '?' help displaying one item when fixinv option disabled +don't give characters with maxed out luck complete immunity to water damage +don't allow AC -17 or better to provide invulnerability to zap attacks +kicking cockatrices while barefooted will stone you +change to inhibit displacement from operating through solid walls +fix mblinded assignment for monsters hit by potion fumes +give runesword same damage adjustments as broadsword +extra verbosity for attacks caused by Stormbringer +allow ghosts to step on garlic +don't let vampires step on altars +don't let monsters jump into polymorph traps covered by boulders, unless + they can carry such, pass through, or squeeze under +giants polymorphed into something else must drop any boulders being carried +giants in pits become untrapped if a boulder is pushed in +prevent traps from being generated on ladders +don't "detect trigger hidden in soil" for previously detected land mine +exploding and crashing doors wake up nearby monsters +factor rings of increase damage into kicking damage +handle omitted case for ball movement that would leave chain in odd position +returning to stairs on top row of map is valid (fixes rogue quest bug) +avoid giving "sad feeling" message for pet if lifesaving prevents its death +don't rot away Rider's corpse if something is standing on it at revival time +kill any engulfer (including poly'd player) trying to digest a Rider +give Riders non-zero nutritional value so tinning kit doesn't ignore them +save & restore u.usick_cause properly +an eating pet can continue & finish eating while you're off its level +fix object names: "a Dark One corpse", "statue of a Wizard of Yendor" +killer_format: poisoned "by Pestilence", not "by a Pestilence"; ditto Juiblex +killer prefix might be wrong after having been life-saved +fix to avoid "invisible invisible {Priest|Minion of Whoever}" on tombstone +fix bug with cold-resistant monsters attacking jellies (etc.) +fix possible panics when restoring while swallowed or held +when taming, make holder/swallower let go even if it just becomes peaceful +reset area of visibility when hurtling backwards from recoil while levitating +don't let hostile monsters who follow up/down stairs share final ascension +add bodypart(HAIR) to correct some inappropriate messages +display monsters inventory (if any) when mon zapped with wand of probing +display inventory of encased items in statues zapped with wand of probing +display inventory of buried items below, by zapping wand of probing downwards +set dknown bit for all objects hit by probing beam +add ceiling function to alter the ceiling messages appropriately +fix 3.1.2's fix for reseting certain class-specific artifact properties +add selection menus to pickup and some apply functions +pre-menu container interface: don't let "%a" select all objects if no food + is present; make user's " a" become "A" instead +wake up monsters hit by potions thrown by other monsters +suppress vault hint messages if player is in the vault +make lev_comp check full object and monster names ([ring of] "protection" in + objects[] was matching "protection from shape changers" in .des file) +guarantee that stairs down from Juiblex swamp level always get created + [sometimes got impossible("couldn't place lregion type 0")] +prevent a three room level which has the stairs to the mines from also having + a special room [so that those stairs can't end up placed in a shop] +allow quest nemeses and other invocation tool guardians to wield artifacts +Mitre of Holiness is not a weapon +don't give "heat and smoke are gone" message when entering Vlad's tower if + arriving from somewhere other than Gehennom (portal via W's quest arti) +when a wielded cockatrice corpse rots away, set `unweapon' so that + further combat will elicit "bashing with gloved hands" message +fix behaviour of wielded eggs (breaking, stoning, etc) +tiny chance for "queen bee" eggs, rather than always killer bee eggs +change Tourist quest home base to Ankh-Morpork +prevent activated statue traps from creating hidden monsters +handle activated statue being the only object poly'd player is hiding under +prevent reference to unseen underwater object when hiding monster attacks +don't pluralize name when smelling opened tin of unique monster's meat +make tins of unique monster's meat be empty in bones file +don't leave a corpse in bones file if killed by disintegration breath +don't leave a corpse when monsters disintegrate other monsters +any food could be tinned (yielding giant ant meat) when corpse in inventory +destroy all boulders in invocation area when creating stairs down to sanctum +boulders landing on previously seen trapdoors will plug them instead of + falling through or settling on top +boulders on ice which gets melted will fill pool as if dropped +don't let dead or sleeping priests give temple greetings +chatting wakes up sleeping priests +don't exercise wisdom when making prediscovered objects known during init +don't generate any generic giants (mummy/zombie placeholder) on castle level +pets and g.cubes will polymorph if they eat chameleon corpses +slippery ice (temporary fumbling) only lasts until the next move +avoid leash limbo if quest leader ejects you while leashed pet's not adjacent + (ditto other unconventional level changes, like W's quest artifact) +release attached leash if poly'd player eats it +crash fix: handle other forms of monster-induced level change besides quest + ejection (swallower expels onto portal, level teleporter, trap door) +fix magic mapping of previously mapped maze walls dug away out of view +assorted drawbridge fixes (kill credit, auto-pickup, drown survival handling) +passtune becomes fully known once successfully played +wiping out engravings leaves partial letters +wipe random letters of trap engravings ("ad aerarium", "Vlad was here") +eating wolfsbane while in werecritter form rehumanizes in addition to purifying +don't penalize player (shop charges in general; bad luck for mirror) when + a monster breaks something with a wand of striking +when loading bones, keep track of unique monsters to avoid their duplication +don't allow a demon prince to summon two copies of a unique demon lord +enlightenment luck display ("extra", "reduced") did not agree with actual luck +avoid duplicate spellbooks in character's initial inventory (affects priest) +fix pets moving reluctantly onto cursed objects +can't #loot while overtaxed +time passes when items disappear on use of a cursed bag of holding +#offer cannot convert or destroy an unaligned altar +MUSEr's reflecting shield or amulet shouldn't become known when not seen +fix check for wearing shield when monsters wield two-handed weapons +don't restrict MUSE scimitar usage to strong monsters +make dwarves less eager to switch back and forth between weapon and pick-axe +clip swallow display at left & right map borders +prevent recoil [hurtle() while levitating] when caught in a trap +downward zap which freezes water or lava shouldn't bounce back up +Vorpal Blade: don't let damage penalty (very low strength, negatively charged + ring of increase damage) prevent beheaded monster from dying +make sure player polymorphed into jabberwock is vulnerable to beheading +make sure that when "The fiery blade burns the shade!" that it actually does + damage (double-damage for non-silver must do at least 1hp damage) +prevent divide by zero crash when hitting tame shade with non-silver weapon +don't lose alignment when throwing gems to/at peaceful unicorns +don't apply grease to worn armor if it's covered by other armor +fix unnaming monsters via `C ' +fix calling object types via `#name n ' +fix off by one problem when shuffling some descriptions (scroll label "KIRJE" + and "thick" spellbook never used; breathing amulet always "octagonal") +exploding land mines can scatter or destroy objects at the trap location +add rolling boulder traps +try harder to make monster hit point and level gain avoid anomalous losses +reduce odds of undead corpses on castle level and priest quest home level, + to make it harder to lure wraiths to more favorable spot +can't polymorph large piles of rocks into gems +hit point gain from nurse healing throttled substantially +make cursed bells be much less effective as instruments of abuse +fully implement object charges for Bell of Opening +allow '%' as destination on rogue level when specifying position by map feature +fire traps can burn up scrolls and spellbooks on the floor +fix inverted cancellation check for AD_SLOW and AD_DREN damage +bullwhips can be applied to disarm monsters and hero +bullwhips can be applied by hero to haul themself out of a pit +ensure that thrown potions hit their mark if you are swallowed +attempting to engrave on an altar causes altar_wrath +differentiate between a hole and a trapdoor, digging always makes a hole +check the right hit point values when polymorphed and encumbered +improve guard behaviour in vaults when player is blind +prevent dwarves from digging while wielding cursed weapons +displacing a pet mimic unhides it +'(' shows the proper tools as in use +improve shk's handling of pick-axe damage and taming +aging of items in ice boxes left in bones files +fix genocide of '@' while polymorphed +add gender to some unique monsters +disallow digging down while phasing through non-diggable walls +general fixes to various message sequencing problems +prevent shopkeeper names from showing up while you are hallucinating +prevent paralyzed pets from picking up items +jellies for Juiblex, not Jabberwocks (done properly this time) +rust monsters can't eat rustproofed objects +general fixes to inventory merging of items +monster inventory undergoes merging too; potentially affects probing and theft +monsters ignore items they want to pick up that are on 'Elbereth' +bows wielded by monsters now do proper (low) damage +even nymphs may not pick up rider corpses +treat cockatrice corpses in multiple item piles the same as one item piles +"PACKORDER" feedback incorrect on parsing failure +you can no longer choke on liquid +stethoscope on secret doors displays properly when blind +monster-hurled potions no longer produce quaff messages (or djinn) +giant eels now hide with mundetected, not invisibility +eels on the plane of water don't hide and aren't negatively impacted by being + out of water +don't give the big point bonus for eels if player is wearing breathing amulet +fix display bug (newsym after Wait! message) +temple priests now wear their cloaks +Orcus is no longer peaceful (had been made so by bad bribery check) +'uskin' save and restore done properly +don't improperly adjust int & wis for stolen non-worn P quest artifact +don't allow Vorpal Blade to behead a monster when it swallowed you +golems are not living and don't "die" in messages +fix "Rocky solidifies. Now it's Rocky!" +polymorphing into a flesh golem, which gets petrified by turning into a stone + golem, now works when stoned +correct "killed by a died" +allow the Wizard to come back and harass at his next reincarnation time even + if he's been left alive on some other level (fixes "paralysis" cheat) +make monsters subject to "mysterious force" in Gehnnom while climbing stairs + with the Amulet, so that once the Wizard has stolen it, his retreat + when wounded doesn't become an easy way to carry it up +changing attributes immediately checks encumber messages +confused monsters get confused SCR_TELEPORTATION effects +fixed "choked on eating too rich a meal" +kicked objects won't stop at stairs if they don't fall +general fixes to stealing from monster carrying cockatrice corpse +a nymph who polymorphs herself while you're frozen during a multi-turn armor + theft can't complete the theft if transformed into non-stealer monster +consistent corpse probability no matter what killed monster (also removes a + loophole allowing permanent rider death) +MUSE monsters no longer wield weapon same as (not better than) current one +incubi/succubi have hands, not claws +make #jump be ineffective on air and water levels +allow multiple sickness causes; vomiting only cures those involving food +#prayer reward: give books for not-yet-known spells preference to known ones +marker use no longer uses wishing interface, fixing several obscure bugs +archeologists' and rogues' initial sack starts out empty +candelabra "has now" 7 candles fixed. +kicked objects would set dknown when the kick caused an injury, even though + safely kicked objects wouldn't +make cloaks subject to burning +make exposed shirts subject to burning and rotting; greased ones defend + against wrap attacks +all types of fire damage affect worn armor [adds explosions, fire traps, and + zapping yourself to previously handled zap/breath attacks by monsters] +for explosions, destroy carried objects before killing player [affects bones] +replace triggered land mine with pit before doing damage [bones] +black dragon breath no longer referred to as "death" instead of disintegration +don't make ring of gain strength known when gauntlets of power mask its effect +can't have "slippery minor currents" or similar silly nohands body parts +proper support for polymorphed players using wrap attacks +cannibalism reduces luck as well as causing aggravation +picking up an item which will merge works even when all 52 slots in use +moving through diagonal openings checks how much you're carrying, not how much + free space you have left +monsters have same restrictions as players moving through diagonal openings +picking up subset of heavy group works for picking one and gets feedback right +taking subset of heavy merged group out of containers works the same as + picking up subset of heavy merged group from floor +when putting gold into containers, don't count its weight twice, thereby + messing up the status line's encumbrance feedback +fix the option parser's handling of attempting to negate string values +teleporting a monster on a trap with a magic whistle sets the trap off +iron ball dragging no longer confuses autopickup +cumulative temporary intrinsic increments can't spill over into permanent bits +eating food shouldn't give messages for intermediate states +don't make wand of death become known after casting finger of death at yourself +ignore case when checking artifacts against wish- or #name-specified name +ignore confusion when reading scrolls of mail +exploding runes for spellbook read failure doesn't imply that book explodes +divine rescue from strangling destroys worn amulet of strangulation +boulders pushed on level teleporters will level teleport; also, make one random + level teleport function to keep all level teleports consistent + +MSDOS: add fake mail daemon +MSDOS: add VGA tiles to tty port +VMS: switch to lint-free, non-GPL'd termcap support code +X11: map behind popup edges was occasionally not refreshed +X11: allow permanent inventory window +X11: when using tiles, highlight pets with "heart" overlay (should be changed + to honor the `hilite_pet' run-time option) +X11: click-in-message-windows crash fixed +tty: fix panic when freeing endwin +tty: fix behavior when recalled text needs to wrap beyond top line +tty: allow selection from single line "help menu" (getobj's '?' response) +tty: don't format data.base with hardcoded tabs that are ugly on non-tty ports +tty: get rid of extra NHW_MENU space (improperly added when the menu was longer + than the screen) +tty: fix repeated "who are you?" prompting at game startup diff --git a/doc/fixes32.1 b/doc/fixes32.1 new file mode 100644 index 0000000..f30afef --- /dev/null +++ b/doc/fixes32.1 @@ -0,0 +1,133 @@ +General Fixes and Modified Features +----------------------------------- +give invocation message when teleporting onto invocation position +flying players with water breathing may retrieve things from water +remove message inconsistently assuming players can be mindless +monsters wear best armor (not first armor) and may switch armors +god doesn't give "strayed" message when your god is angry but a different god + is the one giving the message +divine wrath can hit engulfer if you are swallowed +plug minor topten and oracle memory leaks +monster throwing must allow for 0 damage (cream pies, non-silver vs. shades) +break drawbridge if wand of striking is zapped down at open bridge or either + up or down at its portcullis +wand of striking zapped at ceiling might cause a rock to drop (like digging) +wand of striking hitting a secret door with expose and break it +zapping {teleportation,cancellation,make invisible,polymorph} down affects + any existing engraving similarly to writing with such wands +monsters may use fire horns and frost horns +Guidebook.mn now formats backslashes correctly when using GNU groff tools +Add missing trident case to weapon type categorization. +Fix weapon proficiency handling for missiles. +accept "armour" spelling again for marker use (when writing was disconnected + from wishing, this got lost) +restrict writing scrolls and books by description +give better feedback for some writing results +whirly/wall-passing monsters should not be immune to falling rocks +relearn spellbook even when spell already known (object amnesia fix) +objects that have been called something but not ID'd are subject to amnesia +ask what to call unknown spellbook which crumbles to dust when mis-read +bullwhip only tries to get you out of a pit when you're in a pit +humanoids, gnomes, and ogres now eat; fungi and jellies don't +centaurs, giants, and various others can respond to #chat +potion of paralysis doesn't inherit prior nomovemsg +#naming a nameable artifact when the object already had a name of the same + length didn't create an artifact +applying unID'd potion of oil is possible even not carrying any other items + eligible to be applied +make potion of oil become known after lighting it via apply +fix remaining inconsistency which allowed diluted water +don't let breaking a wand of digging on castle level produce holes, just pits +make Nazgul's sleep gas actually put victims temporarily to sleep +applying a carried, unlocked, trapped chest will set off the trap +explosion due to #untrap failure on trapped door destroys it +shouldn't hear curse/bless status of unseen scroll being read by monster +give some variation in the amount of time it takes a corpse to rot away +breakable objects hitting the ceiling or the hero's head will now break +undead turning now gives credit to player for destroyed monsters +undead turning now brings dead eggs back to hatchable status +code added to support hatching of all eggs in a merged group of eggs +fix display updating at egg's former location when floor egg hatches +fix learning of egg type for hatched eggs +statue traps created with monster inventory inside them +probing shows contents of everything with contents, not just statues +spells of healing and extra healing don't make target monster angry +use cornuthaum cancellation factor +can't kick underwater objects from land or vice versa +objects falling through holes/trapdoors to random destinations obey arrival + restrictions imposed by special levels +being bare-handed counts as wrong projector when throwing projectiles +reduce the range that Mjollnir can be thrown +keep Medusa from continuing to move after she's been killed by reflection + of her own gaze (fixes relmon panic) +medusa's reflected gaze won't affect it if it has amulet/shield of reflection +eating amulet of strangulation can choke when not satiated; also not gluttony +intelligent pets hold onto one pick-axe and one unicorn horn +keep exploding boulders from land mines from hitting you with "a rocks" +objects carried by migrating monsters have no location +more robust parsing of user-supplied option names; trailing characters matter +don't generate spellbooks inside statues of tiny monsters +treat Medusa level statues as petrified monsters (can't be stone-resistant, + and have inventory) +Medusa doesn't gaze more than once per round +data.base: eliminate duplication of Orcrist/goblin king entry +handle luck conferring artifacts correctly (both inventory and enlightenment) +prevent arming land mines and bear traps in various inappropriate locations +prevent easy shop exit by having shopkeeper disarm and pick up trap objects +oilskin cloaks allow defender to slip away from grabbing attacker +make reading the cursed Book of the Dead riskier +enchanting stat-affecting armor now identifies it +fix crash caused by specifying "pickup_types" without a value in config file + or NETHACKOPTIONS (avoid attempt to use menu prior to interface init) +kicking at empty lit corridor with lit_corridor enabled doesn't redraw as unlit +when starting out with an oil lamp, make pre-discovered potions of oil show up + in the discoveries list so that their varying description is available +being crowned Hand of Elbereth enables minimal longsword proficiency even when + Excalibur isn't bestowed +bare-handed and martial arts weapon skill rankings use names instead of numbers + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +tty: reduce alloc/free activity done for message history +tty: windowtype:unsupported_value pauses between listing allowed value(s) + and proceding under default interface +X11: free allocated memory during pre-exit cleanup +X11: display help when DLB is enabled +X11: fix popup inventory window shown for 'i' response to "what type of + object?" prompt with menustyle={T,C} +DLB: avoid excessive fseek calls (major performance hit for MSDOS) +MFLOPPY: wasn't safe to enter endgame! traps, timers, and other level- + specific data ended up being inherited from level 1 +MSDOS: now can re-enter game after chdir'ing in shell from "!" +MSDOS: fix it so -H allows starting a healer game, rather than usage statement +MSDOS: display cursor during input prompts, not just when in the map +MSDOS: fix several cursor-related glitches when moving the display +MSDOS: prevent the use of F3,F4, and F5 before the map window is ready +MSDOS: make flags.BIOS and flags.rawio the default when VGA tiles are used +TERMINFO: colors were wrong for some systems, such as Linux +Amiga: count substitute tiles properly +MAC: avoid MW 68K struct copy optimization bug (in all developer releases up + to and including DR9) by adjusting our structures so it doesn't + occur +MAC: fix crash when trying to drag scrollbar +MAC: add UPP setup for UserItem FrameItem() +MAC: boost partitions to 2M minimum + + +General New Features +-------------------- +#qualifications command eliminated; subsumed into #enhance +OEXTRA temporary compile-time option +menu support for group accelerators to choose objects by class +lev_comp supports specification of percentage chance for monsters and objects +wielding Sunsword provides protection from light-induced blindness +interactive setting of options via menu (Per Liboriussen, liborius@daimi.aau.dk) + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ +MSDOS: Add support for preloading all tiles in protected mode environments +MSDOS: Add support and initial tty Makefile for yet another compiler (Symantec) +BeOS: preliminary support for new BeBox platform; initially tty only + diff --git a/doc/fixes32.2 b/doc/fixes32.2 new file mode 100644 index 0000000..ad79094 --- /dev/null +++ b/doc/fixes32.2 @@ -0,0 +1,128 @@ +General Fixes and Modified Features +----------------------------------- +make `recover' work with the additional element of version info added in 3.2.1 +floating eyes cannot escape down stairs and ladders +do not use body_part() inappropriately when snatching monster's weapon +fix feedback for a confused long worm attacking itself +avoid converting locked secret door into closed+locked normal door; + doors should be flagged as either locked or closed but not both +kicking a locked secret door mustn't yield an open normal door +fixes for dipping weapons into potions of oil +fix crash triggered when knocking off worn blindfold by applying cursed towel +update screen display for vault guard picking up gold from fake corridor +Chromatic Dragon was missing appropriate resistances for orange (sleep) and + yellow (acid + stoning) dragons +when triggering a chest trap, clear the trap flag immediately [bones fodder] +when a boulder gets pushed into a pit where a monster is trapped, immediately + redisplay the monster after giving messages +rings of hunger dropped in sinks should have an effect even if player is blind +player #offering a cockatrice corpse must pass a touch check +hitting a rust monster with a pick-axe, iron ball, or chain will trigger rust + damage to the weapon; ditto for rust traps when wielding such items +cosmetic martial arts and bare-handed changes to skill arrays in u_init.c +gain or lose weapon skill slots appropriately when polymorph into new person +fix crash occuring when self-hit by uncaught boomerang +when delivering a pline message during level change, don't display areas of + the new level using line-of-sight data from the old one +heavy iron balls may be thrown multiple spaces (presumably they roll), + restoring old ball behavior +get rid of "non-null minvent despite MM_NOINVENT" warning +prevent a negative damage adjustment from boosting a target monster's HP +give "bashing" message when first attacking something with unconventional + wielded items besides just throwing weapons and non-weapon tools +when restoring, reset weapon so that "bashing" message can be given again +hole traps in bones data shouldn't be marked as unseen +when carrying 52 items, don't check shop goods for mergability with invent + because it will give false matches, yielding non-gold in slot '#' +kicking a cockatrice corpse is now as dangerous as kicking a live cockatrice +fix set_apparxy() crash when the Wizard returns via migrating monster list +monster wearing armor is protected from disintegration breath like player +monster wearing amulet of life saving survives disintegration breath +greased helmets will block AD_DRIN attacks +get rid of ancient check for welded weapons, which arbitrarily named or didn't + name the weapon for no particular reason. +levitation: sitting makes you tumble, kicking requires bracing +empty bag of tricks won't cause bag of holding to explode +prevent "force bolt" from hitting false match on *orc* data.base entry +prevent "{wand,scroll,spellbook} of light" from matching "* light" entry +non-confused genocide of player should not kill by "genocidal confusion" +re-word message for monster grasping already-wielded unicorn horn +fix various killer reasons +when pushing boulders, make sure current is always top object at its location +always clear unpaid items off shop bill when shk is teleported away, even + if shk was already angry +don't let broken wand of digging create traps at locations which already + have traps (prevent it from turning a hole into a pit) +don't give messages about unseen objects falling down holes +fix to prevent kicked objects which stop on holes/downstairs from becoming + unattached from any object list +don't let flying creatures set off a rolling boulder trap +fix to prevent seeing into a room by kicking an undiscovered locked secret + door from outside +account for case of monster throwing gem at PC unicorn +schedule repair for shop door smashed by big monster +update status line for energy used when "you fail to cast the spell correctly" +polymorphed character will mimic gold after eating mimic corpse +fix relmon panic when bashing weak undead with wielded potion of holy water +holy and unholy water can trigger transformation in werecritters +call update_inventory() when discovering or undiscovering (call " ") an object +fix goto_level panic: if quest leader seals portal, delete portal trap from + quest home level as well as from main dungeon's branch level [possible + return to quest via 'W' quest artifact] +prefix quest leader and/or nemesis name with "the" when appropriate at + run-time rather than using hard-coded text in quest.txt +fix endgame crash caused by string overflow when formatting priest|minion names +dokick.c compiles when WEAPON_SKILLS is disabled +burning objects go out when kicked +monsters with arms but not legs (salamander, marilith) can't kick +knights can't jump while poly'd into anything without legs +try harder to get names for corpses of unique monsters right +very high dexterity doesn't guarantee a hit for thrown potions and eggs +blinding ammo thrown from inside an engulfer can't blind it +monsters going through endgame portals arrive in same area as the player + instead of ending up at the portal to the next level +treat most of the moat on fakewiz levels as outside the special central + area, so that falling or wading into the water and auto-teleporting + out of it can't strand the character inside the room +monsters created by animating a named figurine will inherit that name +prevent possible player-controlled creation of unique monsters via tossing + statues of such onto statue trap locations +activating a statue trap with wand or pick-axe won't discard statue contents +cursed non-weapons can't slip when thrown, as it was in 3.1.x +don't reveal patron deity of high priests with the 'C' command's prompt +two flags structures now, flags and iflags, the latter not saved with the game +object name prefix buffer wasn't big enough for biggest possible case +negatively enchanted weapons thrown by monsters could do negative damage +prevent xorns from phazing through walls on astral level +live Wizard won't teleport to your level if he's carrying the Amulet +can't use 'C' to give the Wizard a name +fix charging for shop goods broken via striking/force bolt +prevent disintegration breath from destroying Famine and Pestilence + and from triggering impossible("monster with zero hp?") for Death +prevent attached ball and/or chain from becoming part of iron golem formed + during polypiling (ball & chain movement later accessed freed memory, + which either crashed or got "remove_object: obj not on floor" panic) + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +Mac: update `mrecover' +Mac: handle `-@' character name suffix for explicitly requesting random role +msdos: suppress tiles on rogue level when restoring games that were saved there +tty: support group accelerators for PICK_ONE menus +tty: after at a yn() prompt, don't blindly accept the character + used to get back to the prompt as the yn() result [could trigger + impossible("invalid sell response")] +Unix+tty: guard against problems with delay_output,TRUE/FALSE macros +X11: fix group accelerators and support them for PICK_ONE menus +X11: implement tty-style counts for menu selections +X11: proper pop-up placement with old-style window managers (eg X11R6 twm) + + +General New Features +-------------------- + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ + diff --git a/doc/fixes32.3 b/doc/fixes32.3 new file mode 100644 index 0000000..9a33655 --- /dev/null +++ b/doc/fixes32.3 @@ -0,0 +1,30 @@ +General Fixes and Modified Features +----------------------------------- +Y2K fix: use 4 digit year values for the dates in the score file +updated COPYRIGHT_BANNER_A to reflect year of release +prevent "late" pline calls from triggering a crash when the RIP window was + displayed at end of game (observed when bones file rename failure + under Win95 was reported to wizard mode users) +being punished on the Plane of Water doesn't trigger a panic when air bubbles + try to move the ball&chain or you around +avoid rn2(0) divide by 0 for empty inventory when trying to crawl out of water +don't let randomly placed monsters on special levels prevent explicitly + placed monsters who target that location from being created (a web + trap's spider resulted in no quest nemesis) +don't let randomly placed stairs on special levels be covered by explicitly + placed features such as fountains +pager: guard against '%' in output from being treated as a printf formatting + directive (using '/' or ';' to look at food yields "% blah blah") +prayer result of ``escape from solid rock'' isn't inhibited by teleport + restrictions (attempting to fix all troubles got stuck in a loop) +report "file empty?" rather than "version mismatch" when that's the reason + why a data file fails its validation check +drum of earthquake can't destroy the high altars + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +micro (assorted): readmail()--don't show fake mail text when blind; also, + update the "report bugs to" message to specify +msdos: fix missing $(INCL) in dependency in djgpp Makefile +mac: Will only dispatch events if the window system is initialized diff --git a/doc/fixes33.0 b/doc/fixes33.0 new file mode 100644 index 0000000..119de07 --- /dev/null +++ b/doc/fixes33.0 @@ -0,0 +1,372 @@ +General Fixes and Modified Features +----------------------------------- +objects falling down a level don't cause everything at destination to scatter +randomize visible trap glyphs during hallucination +don't match statue entry when looking up statue trap [after trap detection] +do match statue entry when looking up "statue of a " when foo happens + to precede statue in the database; likewise for figurines +initialize random number generator before processing user configuration file + (random role selection always selected tourist) +support "character:X" and "role:X" in NETHACKOPTIONS as well as in config file +allow colon as an alternative to equals sign for `OPTIONS:whatever' and + equals sign as an alternative to colon for `pickup_types=?!="$' +make rndexp (blessed gain level) be safe for 16 bit integer configurations +don't add player's weapon proficiency bonus for weapon attacks by monsters +create quest artifact crystal balls with 5 charges instead of 0 +store ghost names in the same manner as other monster names (fix pet bug) +boost kobold shaman level to 2 (was 1, too low to ever cast a spell) +boost ogre king level to 9 (was 7, same as ogre lord) +throwing quest artifact to quest leader won't cause anger; also, artifact + will be caught and thrown back instead of being explicitly ignored +boost level of fake players in endgame to match their rank titles +don't lose odd hit points (integer division truncation) when splitting HP + for cloned monsters +update status line when cloning yourself halves your hit points +suppress clone's initial inventory for poly'd player just as for monsters +update the documention describing the O command +polyself: immediately update vision when reverting to human from eyeless form +use right pronoun when a mind flayer's attack is blocked by a monster's helmet +tins of lizard meat are never rotten, just like the corresponding corpses +tattered capes should not match ape entry in database +booze should not match ooze entry in database +lowered drawbridge should not match werecritter entry +lengthen option file line length to 4*BUFSZ +make zaps of death at polymorphed players work properly +change way invisibility works, add remembered invis monsters and 'F' command +don't list pick-axe and unicorn horn as likely candidates for charging +give more accurate message when nymph steals multi-turn armor from female char +fix splitting merged group of wielded weapons for menu mode version of #loot +if a buried container rots away, bury rather than destroy any contents +the 'W'ear command now only shows armor you can actually wear at this instant, + instead of all armor you're not currently wearing +wishing for a genocided monster egg gets a dead egg, not a generic egg +"Unfortunately it is still genocided" printed only if monster is in range + (particularly important for lifesaved monster genocided off-level). +message for monster growing into genocided monster only printed if in range +include number of attached candles when formatting candelabrum's name +support attaching already lit candles to candelabrum +range of candlelight varies with number of candles +dropping ring of hunger onto sink won't falsely claim that undestroyed objects + like the Amulet have vanished +winged gargoyle can't wear body armor +self probing and stethoscope display speed with same detail as enlightenment +throwing attacks can trigger reprisals similar to hand-to-hand and zap attacks +'A' now works in dropping like when picking up +make setting bear traps and land mines be a multi-turn occupation +make lava be properly lit on special levels +add orig.female flag to handle E quest monster situation +clean up inconsistent quest text +in initial legacy message, use "goddess" when appropriate +allow FIRSTNEMESIS message to actually be printed +taking a peaceful monster's weapon with applied bullwhip will anger victim +applying an unpaid magic lamp will charge a low lighting fee instead of the + djinni release fee +teleporting a Rider will usually bring it near you instead of sending it away +Riders can open locked doors without a key, just like the Wizard +Riders, angels, and elves won't avoid stepping on Elbereth/scare monster when + deciding where to walk +Riders and angels will ignore the sanctuary effect of temples +mind flayers cannot suck out brains by hitting long worm tails +don't ignore object age when #offering a partially eaten corpse +inability to pick up is not as general as nolimbs (blobs may pick up with + pseudopods and purple worms by swallowing) +wishing for a magic lamp produces an oil lamp, not a no-charges, possibly lit, + magic lamp +blobs may not ooze under doors if their inventory can't be squeezed through +peaceful/tame monsters will not use bullwhips on the player +ghosts were not inheriting player gender in bones files +cannot wish for tins of untinnable (due to insubstantiality) monsters +flying monsters cannot fall down stairs +prevent divine retribution from destroying a wand which is being broken +fix resuming to read a spellbook which has become blank since the prior read + attempt got interrupted +make recharging cancelled wands behave like recharging other cancelled objects +prevent "late" pline calls from triggering a crash when the RIP window was + displayed at end of game (observed when bones file rename failure + under Win95 was reported to wizard mode users) +cannot shatter soft weapons (whips, rubber hoses) +being punished on the Plane of Water doesn't trigger a panic when air bubbles + try to move the ball&chain or you around +seen-invisible monsters are consistently visible but transparent, rather + than looking like normal monsters +kicked object message for hitting another object no longer claims it "stops" +kicked object hits objects (plural) if quan>1 but there is nothing else there +kicking an object which is embedded in a closed door behaves like one in rock +can't kick object out of a known pit, but could when pit hadn't been seen yet +pets, shopkeepers, unique monsters, trolls, and Riders retain + their characteristics when killed and brought back to life +being polymorphed into a black light makes you hallucination resistant +don't attempt to perform panic save if the game is already over +don't leave old game's timers, light sources, and shop data in place if + aborted restore attempt reverts to starting new game [eventual panic] +Magicbane carried by mplayers has a lower enchantment than other artifacts +if pets take longer to untame than to starve, make them go wild anyway +split up erosion to allow both rust and acid (or fire and rot) +rust/fire/corrosion/rot now work in all cases (monster/monster, monster/you) +upon arrival to quest, mark return portal as seen +can't be blinded by light while asleep +can't put boulders or big statues into containers +engulfers which engulf a pile engulf 'several objects' +polyself: use right set of hit points for hunger and strength loss +polyself: likewise when checking for troubles during prayer +polyself: stop mimicking gold immediately if shape change occurs +polyself: change monster type when sex change occurs for succubus or incubus +Y2K fix: use 4 digit year values for the dates in the score file +when changing levels, update the screen to show the new level sooner +when changing levels, a monster might displace you from the stairs upon arrival +petrify polymorphed player who has protected hands but is using a non-hand + attack on a cockatrice +fix bug where barehanded AT_WEAP by polymorphed player on cockatrice worked +prevent multiple purchases of clairvoyance at temple from overflowing the + intrinsic's timed subfield and becoming permanent +when cursed, greased or oilskin cloak might fail to protect against grabbing +when any corpse wielded by a monster rots away, unwield it to avoid "bad + monster weapon restore" +hallucination affects priest and minion names +don't try to make the word "aklys" singular +bullwhip can't yank welded weapon from target +eroded T-shirts now display the eroded shirt text consistently +fix "killed by kicking something weird" when kicking a fountain +disallow fruit names whose prefixes are valid for food (uncursed, numbers, etc.) +properly handle wishing for fruits which start with other prefixes +avoid rn2(0) divide by 0 for empty inventory when trying to crawl out of water +don't let randomly placed monsters on special levels prevent explicitly + placed monsters who target that location from being created (a web + trap's spider resulted in no quest nemesis) +don't let randomly placed stairs on special levels be covered by explicitly + placed features such as fountains +substitute random monsters when special level monsters have been genocided +fix intrinsic blockage by worn items so that wielding a mummy wrapping or + cornuthaum won't have the same special effect as wearing one +magic markers created via polymorphing tools are flagged as being recharged +unseen rust monster eating messages, and make tame rust monsters consistent + with wild ones with regard to rustproofed items +pager: guard against '%' in output from being treated as a printf formatting + directive (using '/' or ';' to look at food yields "% blah blah") +getpos: support shifted movement letters in number_pad as per help text +getpos: properly truncate diagonal movements at map edge +using #name to call an object type something could be used to distinguish + fake amulet of yendor (appeared in discoveries list) from real (didn't) +upon quest completion, leader now IDs quest artifact and also tells player + that Bell of Opening is necessary if character doesn't already have it +remove unwanted quote marks from quest message R 70 +make polymorphed objects be likely to retain magic state: non-magic items + usually yield other non-magic items, magic items yield magic ones +make artifact mirrors unlikely to break when used to hit monsters +make sure that nemeses don't leave corpses if the message says there's no body +fix wizard-mode problem with generating Master of Thieves (was singularizing it) +allow weapon-using monsters who ignore gems to throw darts +make flint stones be the preferred ammo for sling wielding monsters +gaining/losing telepathy via polymorph (i.e. mind flayer) redisplays monsters +prayer result of ``escape from solid rock'' isn't inhibited by teleport + restrictions (attempting to fix all troubles got stuck in a loop) +fix surviving level teleport to a negative destination from somewhere other + than the main dungeon (was corrupting the level maps) +surviving level teleport to a negative destination ("you float down to earth") + escapes the dungeon instead of arriving on level 1 +dying due to level teleport directly to heaven won't leave bones +kicking shades with blessed boots, punching with blessed gloves or when wearing + silver rings, does the appropriate damage to them +add artifacts to ending score and display +prevent used objects like scrolls and potions which immediately cause the + character's death from remaining in final inventory (disclosure+bones) +blessed genocide of '@' will list the player's role during genocide disclosure +moved skill definitions to their own file (skills.h) and embedded them in + the object table. +increased the maximum number of branches supported by dgn_comp. +increased the number of characters permitted in a role name. +the number of bits available for properties are expanded. +water demons should not chat about being freed. +since hallucinating players see monsters constantly change anyway, don't print + message when werecritter changes +artifacts which do fire/cold/electric damage to their targets can destroy + carried objects which are susceptible to that type of damage +some artifacts are now unaligned in order to be more accessible to all types + of characters +wizard mode ^F command reveals floor traps along with the map +pager: '/' was not finding data.base entries for shopkeepers, mimics, or + race/role spit when picking from the screen +small monsters like hobbits and gnome zombies couldn't wear cloaks/wraps +make sure non-erodable objects aren't eroded or erodeproof (could happen by + wishing or object polymorph) +consistently let iron non-weapons rust, etc. +handle more spelling variations ("boots of speed",&c) when granting wishes +fix 3.2.0 change which flags the castle and priest quest levels as graveyards +when stepping on a spot where "there are several objects here" (so many + objects that they aren't automatically shown to the user), report any + dungeon feature such as stairs just like when there are fewer objects +report "file empty?" rather than "version mismatch" when that's the reason + why a data file fails its validation check +to-hit bonuses don't accumulate for monsters with multiple weapon + attacks +skill definitions moved to skills.h +skills are stored in the objects[] table. +intrinsics and extrinsics are now >32 bit +number of roles no longer limited to 26 letters +renamed typename() to obj_typename() +add "You hear a nearby zap" when monster is close +fixed a bug that would print of "a Book of the Dead" instead of "The" +fixed a bug so there is no delay when a rolling boulder trap is + triggered completely out of sight +fixed emergency_disrobe() so it will drop gold +fixed a missing case that occurs (rarely) when praying during a + full moon and your god is very pleased +ask for confirmation before praying; no more accidental Alt-P +more guilt messages when you do something which lowers alignment +mplayers get more suitable equipment for their role +allow spaces before = in the options file +dragon scales/scale mail of all colors now work when worn by monsters (in + 3.2.x, only gray conferred any special benefit) +when shopkeeper takes cash from dead player's corpse, clear `been robbed' + status if there's enough gold to cover the amount so that next + player who loads level as bones data won't start out owing money +merged scrolls of scare monster crumble to dust together, matching the + existing feedback (was destroying one and leaving the rest) +properly disallow wishing for venom and allow wishing for iron balls by class +drum of earthquake can't destroy the high altars +potion of oil can't be ignited while underwater +zapping a wand of digging upwards while underwater won't dislodge a rock + from the ceiling +add "born" field so monster extinction limits the number created, not killed +allow "okonomiyaki", etc. to pluralize properly (Ranma 1/2 is popular) +fix off-by-one bug that disabled the check to see if you tried to name your + fruit after a previously existing corpse or egg +avoid a "glorkum" message if an object conveying a warning is stolen before + the warning message is delivered +flags.made_amulet flag was never being set +make sure proper message is given when tinning cockatrice while a flesh golem +fix punctuation on cancelled cobra's dry rattle message +leash cannot choke monsters that do not breathe +rothes are now brown, harder to confuse with much more powerful grey quadrupeds +defer level change for every schedule_goto() call, not just while monsters + are moving (player's move could cause an engulfer to expel character + onto a level changing trap, then attempt to access stale monster and + possibly trigger relmon panic or crash) +fix obscure worm bug which did not consider the tail tip to be visible. Bug + produced "You miss it" on 3.2 and a blatantly obvious 'I' in prerelease 3.3. +water prayer: treat already blessed potions as `other' rather than as `water' +water prayer: potions being blessed glow light blue rather than amber; + hallucination affects the color seen when changed potions glow +fix Death/Sandman #9 joke (should be 8) and make sure the message can be seen +zapping Death with wand of death multiple times could cause hit points to wrap +when pet attacks monster that attacks back, be sure it's in range (could be a + worm attacked on the tail) + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +micro: -uwizard-{class} counts as -uwizard when allowing debug mode +micro (assorted): readmail()--don't show fake mail text when blind; also, + update the "report bugs to" message to specify devteam@nethack.org +msdos: fix overlay separations in weapon.c +msdos: fix problem breaking compile without REINCARNATION +msdos: fix dependency in djgpp Makefile (wintty.c -> hack.h) +tty: try to use terminfo sgr0 (aka termcap me) attribute to turn off all + text attributes, instead of just using "rmso" (aka "se") attribute. +tty: change name of nethack's termcap.h to be tcap.h +tty: ^P at a long prompt printed an extra newline (and then wrapped oddly) +tty: get repeat to work properly on extended commands +tty/ASCIIGRAPH: rogue level uses PC Rogue colors and symbols +nt: in TTY port, non-English keyboard layouts that depended on AltGr-+ sequence + were getting "Unknown command 'M-\'" for '\','@','$','{','[',']','}'. +tty and X11: avoid crashing trying to display long (>128 char) menu items +X11: avoid setuid install problems by changing uid to real uid while + opening the X11 connection. +unix: compress/uncompress detects failure of the compressor, such as for + filesystem full or missing compressor, and no longer deletes the + valid file. In the uncompress case, such as uncompressing the save + file, a message is generated as well. +dlb: handle situation where lseek(,,SEEK_END) doesn't yield the size of the + contents of a file (specifically, VMS "variable length" record format) +vms: install.com couldn't handle the `copy readonly files' step when DLB + wasn't enabled +mac: added unix tty-ish menu flexability +mac: stoped using OLDROUTINENAMES +mac: added dlb support +mac: Increased the maximum number of menu items, so the inventory + won't get cut off at the bottom. +mac: Changed the behavior of Cmd-Q so it uses the new #quit command. +mac: Will only dispatch events if the window system is initialized. + This fixes a bug that would crash the system if the user had an + invalid option in the NetHack Defaults file. +mac: Added an appropriate message when eating an apple. +mac: Change the askname dialog for the new role patch. +mac: Add a gray background to all dialogs. +mac: Replace some improper calls to InitCursor(). +mac: Remove a whole bunch of unused code. +mac: Added Balloon Help messages. +mac: Pop-up menus display the 3-letter file code instead of a single + letter. +mac: Pop-up menus and text item have a 3-dimensional look. + + +General New Features +-------------------- +incorporate the "wizard patch" +`#quit' command added +`*' command added; displays inventory of all equipment currently in use +add Stone To Flesh spell +wands eventually explode if rechaged too many times +show IDed Amulet of Yendor + invocation tools in own section of discoveries + list; likewise for IDed artifacts +add infravision +add Eyes of the Overworld +add lenses +split players race from role in life +cursed figurines cam spontaneously transform when carried +`in_use' struct obj field is unconditional rather than just #if !NO_SIGNAL +add the secondary weapon slot, e(x)change command, #twoweapon + command, and "pushweapon" option. +add the quiver slot, (Q)uiver command, (f)ire command, and + "autoquiver" option (defaults to false). +add the "pickup_burden" option which controls when the user + is asked for confirmation when picking up an item. +pole-weapons can be applied at a distance, and similarly used by monsters. +'/' command's pick-a-location input (getpos) supports shortcuts to bypass the + "more info?" prompt; ':' for '.'+'y', ',' for '.'+'n', ';' for ','+ESC +monsters can throw cockatrice eggs at players +prayer trouble "stuck in wall" takes boulders into consideration +crysknives can be "fixed" +vampires now #chat back +new monsters: chickatrice,pyrolisk,fox,coyote,winter wolf cub,dingo, + gas spore,flaming sphere,shocking sphere,lynx,panther,raven, + glass piercer,mastodon,woodchuck,centipede,master mind flayer, + pony,horse,warhorse,silver dragon,lichen,storm giant,arch-lich, + dwarf mummy,green slime,disenchanter,monkey,dwarf zombie,ghoul, + paper golem, gold golem,glass golem,prisoner,jellyfish,piranha, + shark +new objects: amulet of unchanging,silver dagger,silver spear, + silver dragon scales/mail,robe,alchemy smock,kicking boots, + kelp frond,eucalyptus leaf,scroll of earth,spell of drain life, + potion of acid,potion of full healing,potion of polymorph, + potion of sleeping,ring of free action,ring of gain constitution, + ring of increase accuracy,ring of slow digestion,grappling hook, + ring of sustain ability,wand of enlightenment,saddle,various gems +add Monk role +the old Elf role is replaced by the Ranger +add Human, Elf, Dwarf, Gnome, and Orc races +add multishot ammunition +add graves, iron bars, trees, and arboreal levels +dwarvish mattocks can be used to dig +add leprechaun, cockatrice, and anthole special rooms +add the Sokoban dungeon +implement talking artifacts +members of the clergy (aligned/high/player priests and monks) are + generated with a robe instead of chain mail. +new tin of meat types +tinning kits and cameras have charges +blessed magic mapping detects secret doors +starting spells are known at start of game +pre-discoveries are listed with an * +voluntary challenges with #conduct +add a funny message when eating tridents and flint stones +allow debug-mode level teleport to the sanctum +some #monster commands now consume energy +trees can be kicked as a possible source of fruit +Wile E. Coyote references when using '/' on a coyote + +Platform- and/or Interface-Specific New Features +------------------------------------------------ +WinNT: implement mail support +WinNT: console mouse support added to TTY port + diff --git a/doc/fixes33.1 b/doc/fixes33.1 new file mode 100644 index 0000000..4b4a1c3 --- /dev/null +++ b/doc/fixes33.1 @@ -0,0 +1,444 @@ +General Fixes and Modified Features +----------------------------------- +discarding a tin without eating should not count towards food conduct +expand 'nethack.cnf' in dat/help to include new names on some platforms +using 'C' to name a steed produces a "pony tail" +stopping reading a spellbook when "too much to comprehend" left in_use set +conduct: eating meat{ball,stick,ring,huge chunk} counts as eating meat +don't select gems--aside from rocks and known glass or flint--via autoquiver +skilled slingers can shoot multiple rocks with one shot, like other archers +orcs shooting orcish arrows from orcish bows get multishot bonus, like elves +have 'Q' offer gems/stones as likely quiver candidates when wielding a sling +'Q' command--don't offer tools as likely quiver candidates +spell hunger effect for wizards of high intelligence was not computed correctly +fix "killed by the [master] mind flayer" bug +redisplay correct trap glyphs when hallucination ends +monsters under Conflict cannot attack other monsters that are already dead +monsters that steal gold from monsters should teleport +fix mummy wrappings worn by monsters to block invisibility +applying a weapon or wieldable tool would sometimes give spurious messages + about two-weapon combat +applying a weapon or wieldable tool might not always end two-weapon combat +receiving a divine gift artifact while wielding two weapons would unrestrict + two-weapon skill instead of the skill for the artifact's type +throwing and kicking while wielding two weapons exercised two-weapon skill +when wielding two weapons, ')' command should show both +giants cannot "easily pick up" boulders on the Sokoban level +W command would let you wear an arbitrary item in your body armor slot if that + was empty & uncovered and you carried extra armor for any filled slot +W command would list entire inventory if you answered '?' to the "what do + you want to wear?" prompt when all unworn armor couldn't be worn +#looting and applying containers with menustyle != traditional would do bad + things if you split a merged stack in quiver or secondary weapon slot +save/restore while mounted or stuck could cause a game crash or other errors +baby gray dragons should not be visible to infravision +dying from a failed saddle attempt should name the monster without using + hallucination +spurious "Bummer, you've hit the ground" when hallucinating and dismounting +constitution of <3 and >18 (possible in 3.3 because the ring of gain + constitution was added) was not handled properly +potion and wand of invisibility (on yourself) should not print message if you + are already invisible, even if you can see invisible +reviving tame monsters ended up tame but not peaceful and would attack you +wishing for "rotproof" item is recognized as synonym for erodeproof +your pair of boots "are" not affected when kicking rust monster should be "is" +use article "a", not "an", with "eucalyptus leaf" +fix crash if reviving troll has been genocided +shouldn't see candles flicker when blind +gas clouds use cloud symbol +unchanging suppresses amulet of change, intrinsic lost by life-saving +missing lucern hammer, silver dagger, silver spear in monster weapons +buckled boots are brown +Scorpius and centipedes are not web-makers +race placeholders are M2_NOPOLY +Monk species/leader/guardians are M1_HERBIVORE +leader/nemesis flags fixed with |= instead of = +freezing spheres won't leave corpses +artifacts should add to ending score even if they are inside a bag +being killed by a gas spore should not be treated as burning (most noticeable + problem was that the death message did not include "killed by") +remove a double period from "Caught himself in his own fireball.." +automatic dog names restricted to dogs +chatting with a monster that teleports after the chat (succubus, bribable + demon) would put an 'I' symbol at the monster's destination +stethoscope/probing should reveal identity of invisible monster, not use "it" +wand of probing zapped at 'I' square with no monster should clear the 'I' +cursed potion of invisibility drunk by monster should reveal 'I' +kicked monster that evades kick by moving to unseen square should not leave + 'I' in original position of monster +closing a door on an invisible monster reveals the 'I' +gas spores are recognized as having passive damage for purposes of pet attack +since iron armor can now corrode, don't call all corroded armor "bronze armor" +properly handle attacking a black pudding with a corrodeable weapon +do not print "You still cannot see" when blind and removing lenses +remove possibility of crashes when unseen monster engulfs items +object shattered by wand should use plural verb when object is plural +don't anger monsters when hitting them with invisibility or helpful unholy water +for initial inventory, don't give out spellbooks in restricted spell skills +for tourists' initial inventory, put darts in quiver rather than wield them +artifact discoveries sometimes showed undiscovered object types (for example, + Snickersnee as "katana" when katana was still known as "samurai sword") +"iron bars" singularization exception should not also catch "candy bars" +if a monster kills a monster by throwing acid, don't credit the kill to you +leave two-weapon combat mode if either weapon is stolen or otherwise unwielded +use worse of (two weapon skill, current weapon skill) when figuring skill + bonuses and penaltys while fighting with two weapons +never give back-stabbing or weapon-shattering bonus when using two weapons +engulfing monster will not engulf your pony while you are riding +arch-lich usually starts with an athame or quarterstaff +do not say that "an" Asmodeus reads a scroll +'?' command--short options help sometimes included garbage output +'?' command--longer options help omitted several recent options +eating an amulet of restful sleep now works properly +getting hit by a potion of sleeping now works properly +sleeping is reported by enlightenment +detect unseen / secret door detection refreshes unseen monster (`I') glyphs +monsters won't pick up objects in water (especially kelp) +unseen check for monsters in explosions +fixed "petrified by an " +silver arrows cost a little more than other arrows +javelin back in its own class +dipping weapons in potion of oil now works properly +freed prisoners become peaceful +monk titles shortened so they aren't cut off +elven Priests get their starting musical instrument +you can now correctly ride centaurs +fixed steed getting teleported (e.g. by Quantum mechanic) +fix stethoscope/probing speed reporting, and slowing attack on player +blessed detect monsters increments (not sets) the timeout, and produces a + message if no monsters are on the level +put "Elbereth" under the sokoban prize so that monsters don't eat it +a weak race can still have a high strength if polymorphed into a strong monster +make dingos non barking canines +suppress zap up/down message for stone to flesh on non-stone levels +fix missing spaces on sokoban level that made level impossible without cheating +use case-insensitive comparison for wishing (needed for Master Key of Thievery) +avoid commas in the player name because they confuse the record file +note Sliming when using probing/stethoscope on yourself +fix inconsistency: reflecting medusa's gaze while invisible didn't work, + reflecting floating eye's gaze did +Medusa should not drink potion of invisibility (the code only checked for wands) +restore confirmation prompt for kicking pets and peaceful monsters +ask for confirmation about kicking steed when kicking while mounted +converting secondary weapon into an artifact (naming, dipping) stops #twoweapon +a fully ID'd object converted into an artifact is no longer fully ID'd +polymorphing an object by dipping in potion while inside a shop will only + anger the shopkeeper if the object is shop merchandize +make {wand,spellbook,potion} of polymorph immune to being polymorphed +turning undead should count as calling on a deity for purposes of conduct +fix "monster trail" problem caused by reading a scroll of magic mapping while + engulfed +don't give Slow_digestion-related message when non-digesting engulfer expels you +vary vampire's chat responses according to time of day, tameness, and player + form +added fish_parts to mbodypart/body_part +fixed do-while loop test criteria in create_mplayers() +fix crash if reviving troll has been completely drained by Stormbringer, et al +a stinking cloud should not kill a monster more than once +player stops riding when nymph steals saddle +don't ask for name for eaten ring of slow digestion if already identified +don't let engulfed lifesaved monster beat you up while supposedly being + totally digested +lev_comp: honor class in OBJECT entries (user's '+',"identify" made scroll) +fix uninitialized buffer/unprintable characters error when eggs hatch +accept "aluminium" as variant spelling for "aluminum" +don't die from lava while praying +correctly display gems for the final score even when blinded +throwing a boomerang from {wielded,secondary,quiver} weapon slot will have + it be restored to that slot if caught upon return +don't allow iron balls to pass through iron bars +fix "What weird role is this? (E)" for names taken from 3.2.x score records +make spell of jumping work properly when restricted in escape spells +save traits of petrified monsters; animated statues are like revived corpses +unmoving monsters seen by infrared are removed from/displayed on the screen + when they leave/enter direct line-of-sight +Sting and Orcrist get their anti-orc bonus against orc player characters +buffer overrun caused by many long names in a single message +polymorph can't indirectly transform scrolls of mail into blank scrolls via + paper golem creation +don't let savebones() name a ghost without checking for sufficient space +don't report "killed by ghost of Foo called Foo" on tombstone or in record +when breaking create monster wands, don't place monsters inside solid rock +don't allow tainted cockatrice corpses to prevent stoning if you eat one +oil isn't seen as dimly glowing if you're blind +properly consider hallucination and blindness when printing sliming messages +don't allow the player to jump through iron bars or walls (the latter only + when wearing the Eyes of the Overworld) +don't allow the player to hurtle through iron bars +work around race condition between breaking a wand of teleportation, + teleport control and autopickup +rust traps should affect scrolls +lev_comp returns error if level cannot be fully written out +blank scrolls/spellbooks don't count as reading material +fix seduction attacks to treat characters polymorphed into golems as neuter +chaotic sacrificing on a chaotic altar may crash if demon creation fails +failed demon summoning might cause monsndx panic +avoid possible crash when casting fireball spell while engulfed or near the + edge of the map +prevent observation of dust clouds in rogue level doorways when blind +cans of grease will no longer rust +skip already dead monsters when scanning the full monster list; avoids + monsndx panic and other potential trouble +skip already dead shopkeepers when checking for tended shops +level teleport high in the air while lifesaved should result in an escape +the "stoned" flag wasn't reset when a monster was lifesaved from turning to + stone, so the next monster you killed would always turn to stone +wooden harp is not a magical object +player characters got left at 10 when "normal" speed was increased to 12 +time it takes a monster to change armor doesn't depend on whether you see it +character can't be totally digested on first turn of being swallowed +level 25 engulfer would trigger divide by 0 crash via evaluating rnd(0) +wielded egg that hatched wasn't cleaning up worn objects and might cause crash +mirror shouldn't show location of unseen monsters +cloth headwear was being reported as leather when fire damaged +modify moveloop so that time (moves) is not relative to the player's speed +fix moveloop to account for player not accumulating enough movement points + to move in a turn -- this fixes the reported "time is wrong when + burdened" problem +monsters should not teleport on levels that disallow teleportation +consider existing poison resistance when printing message while eating +don't allow various spells/effects to turn monsters into genocided species +don't crash on abusing guardian angel (accessing edog) +call useupall() rather than useup() for organic items burned by lava +revive any Rider corpse which gets teleported +wishing for gold should affect conduct +gold detection should detect gold golems +grease should affect the secondary weapon in two-weapon mode +falling drawbridge, eating cockatrice eggs, delayed self-genocide all caused + monsters to be fully named instead of using "it". +change the You_hear message if hero is asleep +various inventory changes did not immediately update when perm_invent was set +avoid crash when multiple, cascading explosions occur +pets are no longer permanently weakened by a brush with starvation +doeat() doesn't leave rotten food half-set-up for resumption +don't allow trying to resume eating a revived rider corpse +shopkeepers, priests and peaceful monsters should get angry when you cast + stinking cloud on them +when crowning a neutral wizard who knows finger of death but isn't carrying + its spellbook, don't drop his weapon (crash likely) +similar greased and non-greased objects would merge together into one stack +monster reading scroll of earth may be allowed an extra attack +change message for failed attempt to mount steed while punished +fix multi-shot throwing for darts and shuriken +update monster multi-shot throwing to match player throwing +prevent inappropriate use of "lungs" in creatures that have none +change several instances of 'pline("The ' to 'pline_The("' +monk characters kick as characters rather than as kicking monsters +fix kicking shades by character polymorphed into kicking monster +fix articles in some Sokoban trap messages and eliminate some + superfluous messages +restoring with damaged subroom shops on non-current level could dereference + stale shk pointer +prevent removal of levitation in sokoban pits from causing you to + "float gently to the ground" +peaceful/tame mindflayer now mindblasts hostile monsters and vice versa (the + check was backwards) +fix suppression of stone-to-flesh on unique monster statues +kill player when drain life induces negative HPs +rumors used as engravings should not refer to fortune cookies +magic-resistant players/monsters unhurt by monsters zapping wands of striking +fix time problem where disrobing took too long +saddle that comes with a knight's initial horse should be known to player +iron golems are sensitive to more ways of getting wet with water +prevent odd contents of initial tourist tins and eggs (the contents were + mostly from the quest level, producing many cave spider eggs) +breaking a wielded wand doesn't leave it wielded +if nymph hits monster on first attack and teleports away, suppress second attack +kicking a mimic should reveal its presence +using 'F' command on a pet with safepet should not produce "thin air" message +polymorphing into slime or fire creature removes Slimed; becoming a new man + resets the Slimed timer +throwing cockatrice corpse barehanded should stone the player +avoid "petrified by petrification" on tombstone +avoid "turning into green slime" on tombstone (KILLED_BY didn't work if Slimed) +since unchanging prevents sliming, make it reset any sliming already present +avoid "You turn into a female succubus" redundancy +player hit by potion of acid should take damage like monster +"You are protected" in enlightenment display should include u.uspellprot +chameleons that change into a non-moving, non-attacking form shouldn't get stuck +fix bug where monsters didn't wield bow (etc.) before shooting arrows (etc.) +medium size is too large for giant bats (it allows leaving plate mails when + killed) +player polymorphed to a ghoul resists sickness just like a ghoul monster +player in werecritter beast form shouldn't polymorph into "human" +player wearing scales of genocided dragon was getting duplicate "you feel + dragon-ish" messages when polymorphing +fix luck timeout for full moon and friday 13th +monsters must wield polearms before using them, just like players +when saving bones data, shopkeepers will claim dropped objects inside shops +pets will now wear objects they pick up +pets will now wield pick-axes when necessary +limbless pets are no longer able to carry objects +monsters cannot consider a mattock for digging if they are wearing a shield +avoid a case where monsters keep switching between pick-axe and weapon +override hallucination when reporting pets that ascended or escaped with player +avoid duplicate pickup() calls when landing after falling through a hole +added squeaky board traps to Lord Surtur's lair entrances +cursed lenses no longer considered a major problem by deity +prevent "seeing an image of someone stalking you" when Blind +disallow potion of polymorph / ring of polymorph control starting combo +disallow starting with blank paper +tools shouldn't charge beyond 127 charges +getting money from a fountain should set the looted flag +pole-weapons won't bash and will advance skill when on steed +blessed genocide of polymorphed unchanging player should kill +picking up nothing should take no time +quiver command should take no time +potions should not be autoquivered as worthless glass +players should not get double-billed when using or altering items +silver dragons should have same resistance as other dragons +golems should be un (reverse-)genocidable +player should get blamed for destroying Minetown fountains by Excalibur dipping +player should not get blamed for others destroying Minetown fountains +digesting ghosts and shades as a purple worm should be nonvegan but vegetarian +eating brains as a mind flayer should be nonvegetarian +eating eggs should be nonvegan but vegetarian +eating tripe, meat sticks, chunks of meat should be nonvegetarian +headstones now implemented through engraving +luck penalty for the remaining forms of "creative NetHacking" in sokoban +don't penalize a turn if player cancels #ride direction +Ranger quest is no longer a rip-off of the old Elf quest +several Hello() messages were inappropriate for various monsters +storm giants should talk +monk leader and guardians should use clerical spells +monks shouldn't start with scrolls of enchant weapon +movement rate when saddled was miscalculated +items under lava shouldn't been seen or picked up +clicking in status line during `/' shouldn't cause getpos error +huge chunk of meat should count as dogfood +"Pardon me" when moving directly into peaceful monster +shouldn't glow amulet and save life of digested monsters +" gets angry!" only when you can see the square +"Never hit with a wielded weapon" conduct should only count + weapons and weptools +lynxes should not have cold attacks +Naming a specific object asks "What do you want to name *this* ___" +"Having fun sitting on the floor" shouldn't over fountain +"ball lightning" changed to "ball of lightning" +"poisoned by a poisoned crude arrow" should be "killed by a poisoned + orcish arrow" +shouldn't see invisible monsters oozing under a door +fix apostrophe for invisible seen-invisible crumbling-to-dust liches +amulet of change when polymorphed into single-gender monster could produce + inconsistent role name for Priest(ess) and Cave(wo)man +prevent Fire Brand from "burning" a water elemental +snatching cockatrice corpse gloveless by applying bullwhip will now stone +inventory description of wielded two-handed weapon uses "weapon in hands" +inventory description of secondary weapon explicitly lists it as non-wielded + to reduce confusion about two weapon combat +Bell of Opening removes attached iron ball when performing opening magic +chatting to a monster who responds with "I'm trapped" reveals the trap +Make tmp_at() work when called in the midst of a previous tmp_at() sequence +Make the messages for attempting to wear lenses over a blindfold more clear +Prevent buffer overflow when reading engravings that are BUFSZ in length +paralyzation message on steed should not say your feet are frozen to the floor +avoid buffer overflows and associated security problems from getenv(), + program name, and user name + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +Mac: legacy message was being truncated +Mac: black background left mess on backspace +Mac: backgrounds set too early on game startup +Mac: tty window positions not remembered after move +Mac: tty window turned B&W when moved to bottom of screen +Mac: tty quit command fixed +Mac: remnants of previous hunger status now cleared +MFLOPPY: add checkspace option to avoid problems with >2GB free space +MSDOS: fix clearlocks() to look for the right file names, + and not LEVELS.* (MFLOPPY only) +MSDOS: remove djgpp stuff from the Microsoft C Makefile +MSDOS: change NetHack.cnf to defaults.nh in NHAccess.nh comments +MSDOS: add missing files to gcc 'make spotless' +NT: WIN32 specific code in tty_nh_poskey() was missing the + necessary code to clear window flags so after hitting ESC + messages that should have displayed did not +Linux: set MAILPATH properly +Linux: don't use control characters on Rogue level with IBM graphics +DEC UNIX: set MAILPATH properly, type lex functions properly, avoid conflict + with curses over naming +Qt: remove intermediate files on 'make spotless' +Qt: modify makefile to allow use with BSD make and FreeBSD +Qt: have player selection dialog come up when name specified +Qt: use default menu accelerators and allow remapping +X11: fix memory leaks is reading from dialogs +X11, tty: avoid crashing when displaying empty menus, as from 'i' with + perm_invent and no inventory +tty: when given the choice of ANSI color (AF) vs standard color (Sf), choose + ANSI since there is some disagreement as to the correct color order + for Sf, but no such disagreement for AF. +tty: add workaround for termcap misfeature in some Linux distributions which + affects DECgraphics display +Amiga: minimal functionality restored +Amiga: recover created empty (and unused) save.info files +Amiga: ^P works properly +Amiga: windowcreating modified for better adaptivity +Amiga: changed from intuition menus to gadtools menus +Amiga: changed default colors in tilemode to those of gfxfile +Amiga: window backfill works +Amiga: playerselection adopted from tty-port +Amiga: linesplitting in msg/inv/menu windows fixed +Amiga: obey user configured pens in nethack.cnf +Atari: tty port rescued from oblivion, Gem windowing added + + +General New Features +-------------------- +gold/glass golems, glass piercers now resist acid +added sharks, piranha, jellyfish, prisoners, and iron bars to special levels +piranha can appear in swamp rooms +hero falls off steed when fumbling or falling down stairs +artifacts speak when applied +engraving "x" is not literacy +demons and vampires engrave in blood +shopkeepers don't like riding customers +can #chat down to steed +own race in Gnomish Mines replaced with random monsters +differentiate between light/gaze-induced blindness and other causes of blindness +yellow dragon scale mail provides acid resistance +polymorphed player digests engulf victims more slowly if Slow_digestion +Conflict now affects steed's desire to keep its rider +undead turning of bones level player corpse causes ghost to reunite with + the corpse +control-x in regular mode displays name, role, race, gender, and your deities. +wizard mode can wish for pools of lava +pythons now have infravision to emulate real pythons heat sense organ +M-2 added as a shortcut for #twoweapon +general file location mechanism +you can choose to #loot the saddle from something now +message changes for silver dragon scale mail glowing silver and pit vipers + falling into pits +support explicit `race=random', `alignment=random', and `gender=random' + in startup options +manes now grow up into lemures +potions of healing and sickness affect Pestilence in the opposite way to + their effect on other monsters +introduction of a new method of warning where you sense the danger level of + monsters on the level by displaying it at the monster's location +introduction of a new method of warning for specific monsters the way Sting + does for Orcs; you sense their presence anywhere on the current level +artifacts can belong to specific races and won't be given as gift when "hated" +Archeologists get a penalty for breaking "historic" statues +hatching eggs in male player's inventory have chance of "Daddy?" +steeds affected by more types of wands zapped down +opening/knock versus steed drops saddle +unwearing your steed's saddle (e.g. stolen, opening) causes dismount +yet another funny message when whipping a horse corpse +yet another funny message when mounting when hallucinating +Bell, Book, and Candelabrum added to final score like artifacts +new keywords coaligned and noncoaligned for altars (and monsters/priests) + in special level descriptions +quest start levels get coaligned altars if their roles have multiple + alignments, and goal levels get noncoaligned altars +ice vortices and freezing spheres are infravisible + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ +X11, tty, Amiga: offer for player selection only choices consistent with those + already made by config file/command line (e.g., only offer roles that + are compatible with specified race) +tty: eight_bit_tty option +Amiga: implement menu_* accelerators and counting +mac: the "record" file is created if it does not exist diff --git a/doc/fixes34.0 b/doc/fixes34.0 new file mode 100644 index 0000000..ea3fb77 --- /dev/null +++ b/doc/fixes34.0 @@ -0,0 +1,595 @@ +General Fixes and Modified Features +----------------------------------- +prevent an extraneous selection prompt when a role with only a single + possible gender, race, or align is specified +be consistent with the use of twice and thrice in end of game reports +use "kill" vs "destroy" more consistently +looting bag of tricks on the floor doesn't then prompt for direction +suppress "the" in "you kill the poor Fido" +iron bars added to the Dark One's prison +shouldn't be able to #loot without hands +level compiler can specify cockatrice nests, leprechaun halls, antholes +fix level compiler to allow specifying golems via '\'' in MONSTER directives +fix bug where excalibur blasted lawful non-Knights +unification of the strings "he"/"him"/"his" +conflict caused vanishing vault guards to be killed with player getting + credit/blame (also dmonsfree warning for double removal from map) +monsters' conflict resistance check was unintentionally being affected by + character's experience level +stone-to-flesh was accessing freed memory, passing bad map coordinates + to newsym that might be harmless but could trigger a crash +prevent spurious "placing steed on map?" impossibles during save/restore +prevent real "placing steed on map?" impossibility [sic] when creating bones +dropping secondary or quivered weapon to lighten load in order to crawl + out of water left the item flagged as still worn +if #adjust combined two or more of main weapon, alternate weapon, and quiver + the resulting stack would be flagged as worn in multiple slots and + eventually trigger "Setworn: mask = ##." impossibility +remove curse operated on secondary weapon even though it wasn't wielded +update conduct immediately when eating corpses (character killed by eating + poisonous corpse as first meal was described as "strict vegan") +fix problem with amulets of change when polymorphed into succubus/incubus +YAFM for pit fiends/pit vipers and pits should require seeing the monster +woodchucks, cockatrices, and vampire bats should eat +specifying a non-numeric value for amount when donating to temple priest or + bribing demon prince produced random result +mastodons can tear through webs +praying on wrong deity's altar cursed holy water but ignored uncursed water +polymorphed player's gaze now works properly as a pyrolisk +fix "You drop the Wizard of Yendor's corpse into Wizard of Yendor's interior." +make sure status line gets updated when turning-into-slime state changes +when eating green slime, don't reset slime countdown if already infected +stop current activity when you noticed you're turning into slime +message given when displacing an unnamed pet into a polymorph trapped referred + to it by its new monster type rather than by what you displaced +player killed by ghoul turns into one in the bones file +slings are not made of wood +for post-amnesia deja vu messages, use "seems" rather than "looks" when blind +avoid encumberance messages during startup attribute adjusting +even a wumpus cannot escape the pits in Sokoban +when a steed dies in a shop, don't charge for the saddle +shopkeeper did not charge for use of an unpaid camera +shopkeeper did not charge for items burned by breaking a wand of fire +shopkeeper should charge when you transmute a potion +shk notices if you use a grappling hook to remove an item from the shop +adjust robbed shopkeeper's feedback when he or she plunders hero's corpse +avoid giving away which monsters are saddled while hallucinating +when polymorphed into a herbivorous monster, you should prefer vegan "corpses" +when polymorphed into a hider, stop hiding after picking up the last object + at a location +throwing a wielded, returning weapon should not disable twoweapon mode +monster should not wield cockatrice corpse without gloves on +sharks have thick skin +better message when killed by drinking a potion of sickness from a sink +telepathically detected monsters will be described by name if they try + to attack praying character +taking cockatrice from or putting it into a container should stone you + if you are unprotected +don't fall into pits (or other traps) twice when dismounting +fix two weapon combat bonus/penalty to avoid "weapon_hit_bonus: bad skill 2" +unicorns were at a disadvantage on a noteleport level +missing a cockatrice when polymorphed into a weapon-using monster but + fighting hand-to-hand would stone the player +eliminate ghoul creation abuse when engraving on a headstone +loss of levitation due to a sink will result in touching a wielded cockatrice + corpse, just like falling down stairs while burdened +falling into a sink when constitution is greater than 20 won't raise hit points +stinking cloud should not affect migrating monsters, causes dmonsfree error +only display message about monster using a weapon against another monster + if you can see the monster +don't count artifact amulets (wizard's quest artifact) twice in final score +prevent pets from picking up Rider corpses +when polymorphed into a centaur, don't keep kicking monsters after they die +when throwing at a monster that you see with infravision, don't say "it" +avoid "the arrow misses the mimic" which left the mimic concealed +#sit while water walking over a submerged object should sit on the water's + surface rather than on that unreachable object +suppress extra "the" when printing the names of certain mplayers +do not try to engulf green slimes (same as for cockatrices) +trying to eat the brains of a green slime is now properly handled for players +monsters touching cockatrices check boots for feet and nothing for tentacles +if being petrified, don't disable messages from further petrify attacks +trap detection would generally not find trapped doors +avoid spurious done eating message after choking and vomiting +attribute distribution for several player types did not add up +monsters shouldn't try to eat green slime as a cure for stoning +lighting of arboreal levels should not be stopped by trees +need to recalculate visible locations immediately when monster blinds player +monsters shouldn't see through walls because player wears Eyes of the Overworld +when pricing glass the same as valuable gems, be sure to use gems of same color +nymph stealing armor from a fainted player should wake the player +ensure status line updates when you stop running when time is shown +repairing a trap in a shop doorway must replace the broken door or wall as well +sleeping steed cannot climb stairs/ladders +can't change levels when mounted on a steed which is carrying the Amulet +more artifacts granted by a deity are rustproof +monster name feedback when using the m movement prefix allowed player to + distinguish between peaceful and hostile monsters while hallucinating +scrolls should not fade when hitting rust monsters, only from rust traps +blank scrolls should not fade even from rust traps +can't eat or #offer food off the floor under circumstances other than + encumbrance where you couldn't have picked it up off the floor first +ensure correct message after passive freeze attack by gelatinous cube +avoid buffer overwrite when several weapons slip from your hands at once +prevent portal placement on Ranger quest from stranding player in left margin +avoid crash when a trouble gets fixed before you finish praying +sensed hidden monsters should fight back when attacked +mindless monsters won't be grateful after unsuccessful #untrap attempts +turning affects your religious conduct, even if your god does not help you +rolling boulder trap's boulder will knock another one that it collides + with into motion in its place +make it harder to abuse detect monster and confusion spells +prevent D[a from producing odd message sequence in (c)ombination mode +avoid messages like "the silver bell" after being drained by mind flayer +after polymorph, actually drop both weapons when a message says this happened +curb unicorn horn creation abuse by limiting the chance of a unicorn + leaving one if it has been revived +accept -p and -r options with nethack -s, as documented +avoid printing "spellbook of" Book of the Dead in list of discoveries +eating non-food items made of leather or other animal parts now violates + vegan/vegetarian conduct +use correct skill when throwing something while in twoweapon mode +secondary weapon can rust when hitting a rustmonster in twoweapon mode +extra healing spell cures monster's blindness +add missing quest message for throwing the quest artifact to the Monk leader +pits, arrow and dart traps, webs, polymorph traps and sleeping gas + traps can affect the steed +allow game restoration while polymorphed and your race is genocided +ensure that crysknives revert to worm teeth, even in containers +do not print gas spore's name if you cannot see a gas spore explosion +cursed two-handed weapons now keep you from changing body armor +trapped pets cannot follow you to another level +no corpse when unchanging hero dies while polymorphed into a G_NOCORPSE monster +A-removing armour under cursed stuff no longer fails silently +grease protects gloves from contact poison on books +items picked up from an abandoned shop sometimes wouldn't merge with other + compatible items in inventory ("no charge" bit wasn't being cleared) +prevent cut-off death message by increasing DTHSZ +check to not control teleports when unconscious should now work properly +if armor the hero is donning is stolen or seduced off, attributes + can be left permanently mis-adjusted +ensure a message is printed in all non-obvious cases where a monster flees +a fleeing monster that is holding you always results in a "get released" message +ensure a monster flees for at least one "turn" +explosion type can now be one of dark, noxious, muddy, wet, magical, + fiery, or frosty +flying (jumping or throwing recoil) over some traps (magic portals, fire traps) + will now trigger the trap +displacement does not work through walls +you can't trip and fall or trip over rocks while riding +reduce the chances of a monkey successfully stealing armor you are wearing +monkeys can't steal cursed items that you're unable to remove or attached + iron ball or items too heavy for them to carry +trapped doors are not always detected after returning to a previous level +trap detection sometimes showed non-trap locations to be traps +eucalyptus was never chosen in random tree fruits due to an off-by-one bug +allow knights to pursue and attack thieving monkeys without alignment penalty +gaining levitation while over on sink causes an immediate fall +quest leader should avoid leaving the quest start level voluntarily +blind Medusa cannot gaze +prevent dipping attached iron ball or embedded dragon scales into a potion + of polymorph from confusing the game about what items are in use +should not be able to cut down trees on Ranger quest start level +arrow traps are not currently intended to shoot poisoned arrows +fall off the horse if you mimic a pile of gold while riding +martial attacks will not remove monsters from traps and will cause + monsters to set off traps they land on while reeling/staggering +prevent topten from using alloc after alloc failure +Nazgul and erinyes are nopoly to ensure their numbers are never exceeded +"player-@" randomly selects a race and "player -@" randomly selects + everything that is not specified +prevent spurious "quest portal already gone" when you use an artifact to + return to the quest after being previously expelled +prevent limbless shopkeepers from "leaping" and "grabbing" your backpack + by changing the messages that you get +prevent panic when riding while punished and falling down the stairs +armor class shouldn't wrap from very negative to very positive +searching should only credit you with finding an undetected monster if + you couldn't sense it or spot it already +monsters should not generally lose invisibility when polymorphing +monster must have eyes or breathe to be affected by potion vapors +stop dungeon file open failure from causing vision-related crash +wishing for {statue,figurine,corpse} of long worm tail yields long worm instead +chatting to an arbitrary shopkeeper (not a petrified one) who was created + via statue animation produced strange results +Yeenoghu's confusion attack is not a touch of death +an eating steed should not be able to go up or down stairs, etc. +you don't feel "great" when recovering with a unicorn horn but Slimed; also, + make the same check for potions that make you feel "great" +avoid panic during player-as-demon demon summoning when no demon is available +change "Ouch! You bump into a door" message when riding +prevent voluntary dismount of steed from passing you through walls in + tight spots +prevent throwing boulders, boxes, and chests and medium-to-large + corpses and statues through iron bars +only living eggs which touch cockatrices get turned to stone +since monsters already refuse to zap empty wands, they shouldn't pick them up +after praying, try to give a spellbook for which the player is not restricted +after #dipping your weapon in hand or quiver into a potion of polymorph, + leave it where it was +message from rust trap states "robe" instead of "cloak" when applicable +gas spore explosions were affecting your human hitpoints even if you were + polyd and consequently you did not rehumanize +prevent "You attack empty water" when attacking a spot on land while + underwater +prevent spurious "But you aren't drowning. You touch bottom." message when + removing an amulet of magical breathing as an amphibious creature +fix message given when a monster tries to disarm your multiple welded daggers + with a bullwhip +camera flash no longer stops at invisible monster +monsters inside a stinking cloud should be blinded, just like the hero is +vault guard shouldn't initiate conversation with you when you're hidden +adult wolves are not small but lynxes are small +turn off vision during a save operation to prevent impossible() from + triggering a crash +rolling boulder trap's boulder susceptible to land mines and teleport traps +polymorphing below level 1 should kill player (needed to fix max-HP abuse) +prevent "obj not free" panic when shopkeeper cannot get to a thrown pick-axe +give feedback if Sokoban prevents polymorphed player from passing through walls +eliminate Wounded_legs enlightenment message when riding since it refers to + the steed's legs, not the hero's +adjust the fumbling pick-axe message to reflect that the steed's + legs got damaged, not the hero's +quaffing a noncursed potion of speed no longer heals the steed's wounded legs +prevent mounting of steed when you have Wounded_legs to prevent abuse; + dismount does an unconditional healing of Wounded_legs during the + Wounded_legs context switch +wounded legs on a steed doesn't count as a prayer trouble +wounded legs on a steed doesn't abuse dexterity +make wounded legs gained by falling off a steed consistent (dexterity loss) +land mines while mounted should hurt the steed +self-genocide while sitting on a throne should not refer to scroll of genocide +eating dogfood or fixing a squeaky board conveys experience but didn't + check for gaining a new level +demon bribes are 4x larger than they should be for co-aligned players +specific monster warning no longer reveals the true monster name when you + use the '/' command while hallucinating +start_corpse_timeout() now takes corpse age into consideration rather than + always assuming a fresh corpse, thus fixing potential icebox abuse +player on an immediate diagonal from a monster reading a scroll of earth + should be affected, just like monsters in similar locations +objects that fall from monster's minvent as a result of monster polymorph + are not polymorphed, consistent with items that remain in minvent +quaffing a potion of gain ability while wearing ring of sustain ability + displayed no message and identified the potion +monsters still with WAITFORU strategy should not follow up/downstairs +messages should reflect the fact that the Eyes of the Overworld mask the + effects of blindness +Amulet of life saving should save you from sickness that will kick in this turn +player should stop waiting when a monster uses a polearm from a distance +avoid stone-to-flesh blood pooling message when zapping ice and not stone +when polymorphed into a silent creature, do not "pronounce" scroll formula +ensure hilite turns off immediately when pet stops being tame +hitting with a polearm counts as hitting with a weapon for conduct +traps detected while blind and levitating were not displayed +when a mind flayer uses its mind attack, it should wake the victim +shapechangers restored from disk would no longer change shape +allow "tame" prefix when using the wizmode C-g command to create new monster(s) +display a more appropriate name for a high priestess when using ;/ commands +change "The water elemental is on fire" to "The water elemental is boiling" +blind, cancelled or nonseen invisible Medusa cannot gaze at other monsters +fix impossible when spinning web on falling rock, rolling boulder and fire traps +rust monsters can only eat items made of rustable material +wands of fire are no longer flammable no matter what material they are +displacing you pet into a trap which kills it affects killer conduct +pets can now be displaced in untended shops +only show lit walls if, like doors, the position next to them is lit too +charge for an unpaid weapon used for engraving +shopkeeper should charge for unpaid balls and used candles in containers +when swallowed you could drop or throw a cockatrice corpse into a + monster's stomach without stoning it despite the guaranteed hit +steed would often not respond to an attack, even if you didn't move that turn +after stepping in a polymorph trap, a monster may pick up the wrong items +breaking an unpaid wand of teleportation wouldn't result in the proper charge +next_shkp() was used inconsistently, potentially triggering an endless loop +chaotic wizards usually get a spellbook when crowned, just like neutral ones +monk quest: fix the two inaccessible chambers on the locate level +rogue quest: fix the four inaccessible chambers on the home level; + link the two inaccessible chambers on the locate level and provide + a means of escaping from them; on the goal level, link most + chambers together, resulting in just four disconnected regions, + and force stairs to be in a different region from the nemesis +angels can fly +under #twoweapon fix it so that only Stormbringer carries out the + blood-thirsty attacks, not both +booby-trapped doors shouldn't make you stagger if you're riding +encumbrance exertion checks should happen each time player moves +mksobj_at: add way to suppress the chance of a new object being an artifact +steed should be the one caught in a bear trap, even if player is polymorphed +use a more appropriate message than "being held" when using < or > while + swallowed or engulfed on stairs +stinking cloud isn't useless and shouldn't be excluded from initial inventory +shopkeeper will not try to buy food you are eating when inventory is full +don't duplicate any gold inside containers when saving bones data +can't tell between acid and holy/unholy water that burns like acid +tame stuck monsters should release you after regaining their senses +engraving Elbereth exercises wisdom, engraving anything else does not +artifact bows get any special attack bonus added to missile to-hit roll +monsters with gaze attacks should not try to blind the hero with potions +players polymorphed into umber hulks should not try to eat boulders in Sokoban +when a monster uses up a partially eaten food item, cleanup was not performed +temple priests shouldn't be created with two robes +give some quest leaders and nemeses equipment appropriate for their class +mis-engraving "X" or "x" shouldn't violate illiterate conduct +Heart of Ahriman now explicitly does double damage +prevent NO_ATTK artifacts from accidentally doing double damage +player polymorphed into monster that loses hp out of water should lose hp too +make sure that all leashed monsters get released when bones data is saved +eating a ring of levitation doesn't confer permanent intrinsic levitation +silver hating monster using a bullwhip shouldn't snatch silver weapons into + its inventory +fracturing one of several boulders at a location should not unblock vision +don't hide stairs, thrones, &c under spider webs when creating levels +rediscovering forgotten object types behaved differently depending upon + whether they had user assigned names at the time of amnesia +taming while engulfed is limited to the engulfer +restore blindness resistance to Archons +if a shk is polymorphed into monster form which has Wizard-style tactics, + don't let him teleport to the stairs if he's inside his shop +when the player digs a hole through a shop's floor, don't let shopkeeper + wander out of that shop while multi-turn digging is in progress +don't protect alternate weapon and quivered objects against being taken + by shk who grabs your pack when you dig a hole through his shop floor +add missing break to POT_WATER case in potionbreath() +keep monster from forgetting its weapon is cursed every other round +multiple shot throwing stops immediately whenever you hurtle backwards +don't panic if being billed for a burning or other timed object +food that makes a monster peaceful should not re-anger it at the same time +abusing a leashed pet could result in a leashed peaceful monster +couldn't unleash steed while mounted +trying and failing to wield an item could leave current weapon flagged as both + "weapon in hand" and "alternate weapon" when `pushweapon' option is set +handle OBJ_CONTAINED case for corpse revival so that trolls can revive + from inside containers +eating one of several merged partly eaten food items should take nutrition + from only one of them +coyote names should not disable printing of "tame" or "peaceful" +Eyes of the Overworld protect from stun effect of Archon's radiance attack +give feedback when putting on or taking off the Eyes of the Overworld causes + blindness state to be toggled +avoid spurious "you can see again" when temporary blindness being overridden + by the Eyes of the Overworld times out +removing blindfold or lenses via 'A(' gives same results as via 'R' +make blindness with just 1 turn remaining be a candicate for repair by + unicorn horn and healing potions/spells +healing potions/spells shouldn't fix being creamed +make pie throwing and venom spitting by the player be consistent with the + effects of those attacks by monsters +offering & tinning corpses on altars should work even while riding +It was possible to faint after eating a fortune cookie and still read + the fortune's text despite being unconscious +when filling a pit containing a vortex, a surviving vortex gets untrapped +teleporting no longer moves the iron ball to under you if that's not necessary; + prevents odd ball movement when crawling out of water +monsters now prefer to wear speed boots over other boots +prevent crash when loading a special level specifying a mimic using m_object +prevent crashes caused by dropping or shipping quivered or secondary weapons +don't trigger spurious encumbrance messages on last turn of a multi-turn meal +prevent food being restored to untouched status if interrupted while eating +troll revival shouldn't increment the troll creation counter +breaking mirrors and your eggs should be bad luck when kicking chests as well + as throwing +vampires should be G_NOCORPSE so you can't wish for them +glass objects should break when thrown, just like when kicked in chests +rocks/gems shouldn't be hard to throw by hand because they are ammo +avoid all cases where splitting an object would result in two objects being + quivered, wielded or otherwise having its owornflag set +allow 'a' prompt when dropping many objects in shop for credit (Wingnut) +monsters who get polymorphed while wearing dragon armor turn into dragons +shape changers can't be killed by system shock when hit by polymorph +Chromatic Dragon has silver scales too (she reflects) +being killed when wishing for an artifact should retain that item in bones data +the drain life spell should not wipe out engravings (especially not using a + function that requires you to be able to reach the floor) +monsters who can cast undirected spells don't need to be in combat with you + to do so +messages consistent for all monster spells +monsters casting spells at your displaced image now set mspec_used +monsters without ranged spells don't print curse messages for ranged spells +going down to floor using > should set Heart of Ahriman invocation timeout +riding a steed into water kills the steed if it cannot swim, with penalties +gaze attacks now stop occupation +proper death message when killed by "plain" high priest +don't conceal the identity of Moloch's high priest +blessed full healing can't recover levels lost when polymorphing into new man +blessed full healing can recover at most half of other lost levels +golden glow when praying will recover lost level if blessed full healing could +gaining a level while polymorphed increases current monst hit points as well + as latent human (or whatever) hit points +pets should not try to go after food that they can't reach +monsters shouldn't use wands of digging in Sokoban +objects dropped in or travelling across lava pools can take damage +monsters that enter lava can take damage +eating an unpaid tin should calculate cost before not after eating +spells shouldn't do negative damage +when reading spellbooks, don't "continue studying" wrong book if original one + gets destroyed after previous reading attempt has been interrupted +correctly handle polymorphed quest leader +swallowing zombies/mummies whole makes you sick, like when eating them normally +impose additional teleport restrictions on the no-teleport Plane of Air +landmines set off by pushed boulders have same effects as stepping on them +secret corridor detected out of vision range is still displayed (prevents bug + where wand of secret door detection found nothing but still identified) +getobj can now see user-specified count when using inventory to make selection +scalpel is stainless steel (i.e. METAL) not regular steel (IRON) +eggs, potions & other breakables may break when they fall down stairs +hurtling via grappling hook does not apply effects of destination location +consider vortexes to be nonliving +dragons have scales, not fur +if player teleports a monster while swallowed on a noteleport level, the + player should not teleport along with the monster +prefixes that can appear in any order when wishing should include +/- and empty +don't allow untrapping of adjacent traps in locations you can't move to +summoning should summon any alignment if summoner's base alignment is A_NONE +when dipping unicorn horn in potion, the potion might change bless status, so + set bknown to FALSE +grammar fixes such as "Eyes of the Overworld resists" and others +score bonus was missing from scrolls of identify and fire +make wands of speed or slow monster known if their effect + on monsters is observed; likewise for speed boots +gold detection "materially poor" message inappropriate if you have hidden_gold() +cannot reflect back an invisible umber hulk or medusa's attack +monsters with M3_WANTSBOOK often couldn't move in the Wizard-level +Vlad should want the Candelabrum +if you float_down on a trap in which you're already trapped, don't retrap +applying whip toward hidden mimic displays mimic name before "Wait!" message +stealing a container didn't multiply cost of stolen contained objects by quan +halve air elemental damage to compensate for side effect of speed system +strengthen Death; weaken Famine, Pestilence, and Demogorgon +pet purple worms get nutrition from engulfing attack +throwing an artifact upwards will trigger artifact hit effects when it falls +being hit by Fire Brand stops the turning-into-slime process +monsters hitting other monsters can split puddings with weapons +be consistent with checking for iron weapons when splitting puddings +prevent corpses of undead creatures just killed by undead turning from being + instantly revived by the same undead turning attack +allow fake player monsters to handle artifacts that don't match alignment/role +chaotic monsters can use Stormbringer; lawful monsters can use Excalibur +No "corridor disappears" message if Vault guard dies off-level +slip while mounting and levitating at will should not cause damage +if you see a monster jump into a trap in a secret corridor, it's not secret +fixed a few places where unblock_point wasn't called but should have been +cloned monsters should have the same name and tameness as the original +you should stop eating (etc.) if a monster attacks you and misses +half physical damage should apply to gas spores +iron bars should affect wall corner wallification +potion of polymorph shouldn't be identified if object being dipped into + it ends up as the same type of object after polymorphing +don't slap against the floor while riding and eating bad food +got rid of "nori" (since it doesn't really translate "kelp frond" accurately) +engraving in fog-covered location on in the Wizard quest said you + engraved in air, not dust +dipping non-weapons into burning potions of oil had no effect +dipping arrows into burning potions resulted in rust damage + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +amiga: random crashes when opening menu window in fontmode eliminated +amiga: proper action taken (cancel) when closing the menu window + with closegadget or escape +amiga: allow #/altmeta combination on foreign keymaps +amiga: prevent plname[] overflow from askname() +amiga: prevent writing outside basewindow (bottom) +amiga: tilemode tombstone corrected on cybergfx screen +amiga: don't clutter levels/ with foo.0 when quitting at playerselection +micro: prevent a guaranteed impossible() if we ever have more than (COLNO - 1) + levels in the game +micro: fix out of bounds memory modification for file opens via PATH +msdos: placeholder tiles accepted by the thin tile builder +tiles: use pixel-accurate grid bug tile for grid bugs +tty: correctly dismiss 1-line menus +tty: clear screen before version incompatibility message so it doesn't just + print the message overwriting previous screen text +tty: pet was not always hilited +tty: don't crash if the news file is present but empty +unix/tty: give user a chance to see any error produced by (de)compression +win32/tty: menus can take advantage of consoles larger than 80x25 +win32/tty: add support for inverse attribute +Gnome: workaround for GTK+ attempts to disallow setgid executables +Qt: honor user preferences in startup dialog +X11: map not displayed in color when using X11 windowtype w/o tiles +X11: viewport scrolling could scroll the the wrong place with resized window +X11: allow extra space added to map widget to be removed if widget shrinks +X11: general solution to the problem that the meaning of font height varies + among different implementations of X11 +X11: make "slow" mode the default since it seems to be very prevalent + + +General New Features +-------------------- +added travel command via '_' or mouse click +config file processing detects multiple use of the same OPTION and + prints a warning when it does +make the player selection prompt more explicit in the information + that it is going to request +remove curse now operates on cursed leashes that are in active use +give feedback when shooting/throwing more than one missile at a time +monsters can now deliberately eat dead lizards to cure confusion +general warning now allows you to attack unseen monsters, as long as you can + see the warning glyph on the screen +wand of fire & fireballs now burn webs +wand of locking / wizard lock zapped down will close and remove trap doors +exploding monsters wake nearby monsters +various mindless, sphere monsters no longer need to breath +sleeping gas no longer affects nonbreathing monsters +vault guard doesn't notice you if you're mimicking gold +good chance of untrapping monsters and pets caught in webs if you are + polymorphed into a spider, and extremely small chance even if not +stamina affects ability to throw heavy things +objects merge in containers +wishing for "nothing" yields no object and preserves wishless conduct +genociding "none" destroys no monsters and preserves genocideless conduct +coyote id naming shows only the true latin name if coyote is cancelled +xorns can "speak" and can smell valuable metal +if you find a trap but there is too much clutter to see it, have the + game display it temporarily until a keypress +rename the Wizard of Balance to Neferet the Green +double the number of messages that apprentices/guards utter, with 5 for + before the quest, and 5 after +wizard mode ^G command can create monster by class, not just by name +wizard mode ^G command takes a count +kicking a sleeping/paralyzed steed now causes special effects +allow overriding of the default boulder symbol via BOULDER option +blessed scroll of detect food provides you with a one time ability to + recognize food that may be harmful to you +wizard mode WIZKIT config file option added to ease adding items to + starting inventory for a debug session +helping a sleeping/frozen monster from a trap might wake/unfreeze monster +if the hero comes upon an obviously trapped monster the trap is considered seen +thrown weapons that hit are now subject to passive damage +locomotion-specific use of words, rather than just using "stagger" +if you come upon a physically trapped, visible monster, you see the trap + too, without searching for it +allow looking and pickup inside monster's stomach or interior when swallowed +add body_part(STOMACH) +pets like tame nymphs, et al, now only steal non-cursed items +monks usually get a spellbook rather than a weapon when crowned +blessed gold detection now detects anything made of gold, not just + coins, including candelabrum and gold rings +new T-shirt messages from Scott Bigham +option to get rid of resistance 'sparkle' (shieldeffect) (Scott Bigham) +option for autodig (Malcolm Ryan) +glowing Sunsword (inspired by Slashem) +msg_window option for ^P in TTY mode (Jay Tilton) +ninjas should get multishot bonus with yumi and ya (Dylan O'Donnell) +put prisoners in the Dark One's dungeon (Dylan O'Donnell) +touchstones; Archeologists start with one +add leather cloak so soldiers don't have elven cloaks +add Tom Friedetzky's BUC-patch with some alterations to the original +add wizard #poly and #levelchange (originally levelgain; Dylan O'Donnell), +add Jason Short's additional lenses use patch +add new Gnomish Mines levels from Kelly Bailey's patch +add Ken Arnold's patch to show unpaid item prices in inventory +jousting by players wielding a lance while riding +Knights start with lance rather than spear +can start game without a pet via pettype:none (Dylan O'Donnell) +allow disclose options to be more finally tuned, including being able + to specify the default response for being prompted +debug mode SPLEVTYPE environment variable to choose specific levels from + when there are random selections +artifacts have individual prices +new window-port preference options added, and some existing options + moved into the window-port preferences section +made each of the end disclosure options customizable to "prompt;default no", + "prompt;default yes", "show it without prompt", and + "don't show it and don't prompt" +add female role level names "Medica ossium", "Magistra", "Chevaliere", "Dame" +more feedback about skill advancement from #enhance command +USER_SOUNDS compilation option to enable use of SOUND and SOUNDDIR variables + in the config file for user-specified sound clips for + user-specified, regex-based message patterns +resistance does not protect inventory from artifacts (cold vs Frost Brand,&c) +phrase the prompts for P and R commands using "put on" and "remove" as the + actions rather than repeating W and T commands' "wear" and "take off" +dipping candles, et al, into burning potions lights them + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ +amiga: screenmode requester +amiga: 16 color font mode +mac: command-key shortcuts in the player selection dialog +vms: default compiler configuration in sys/vms/Makefile.* switched to DEC C +win32: new graphical port contribution by Alex Kompel + diff --git a/doc/fixes34.1 b/doc/fixes34.1 new file mode 100644 index 0000000..9dfc5f7 --- /dev/null +++ b/doc/fixes34.1 @@ -0,0 +1,471 @@ +$RCSfile: fixes34.1,v $ $Revision: 1.331 $ $Date: 2003/02/20 00:19:46 $ + +General Fixes and Modified Features +----------------------------------- +prevent panic() obj_not_free when pushing a boulder over a landmine +there was no feedback when successfully hitting shock resistant monsters + with Mjollnir via hand-to-hand attack +unbought single-bite food eaten in shops was not billed properly +charge for shop contents inside "no charge" containers +add wishing for "nothing" and genociding "none" to the conduct section + of the Guidebook +allow both wishing and genocide to accept either "none" or "nothing" when + the player wants to decline +left word in format string in get_wet() causing "The spellbook fadefades" +two bad wizkit items in a row shouldn't make the user hit space many times +kicking thrones no longer loosens rocks +wall symbol not replaced when digging while blind and levitating +increment FQN_NUMBUF from 2 to 3 to prevent premature reuse of a buffer + that caused a level creation error to be reported as a lock file error +print regular death message when leashed, mounted steed dies of starvation +fix more funny messages, new and old +restore the behavior of bumping into closed doors when moving while impaired +fix iron ball cases that could put the chain in solid rock +discovering a mimic on a closed door location should not unblock the location +don't drop corpse when a monster kills another monster on an inaccessible + location (i.e. behave like xkilled behaves) +half-physical-damage from gas spore explosion should only affect you +Sunsword didn't stop glowing when hero killed a monster wielding it +mimics caught in explosions with messages printed about them are discovered +let lev_comp and dgn_comp accept optional carriage return character prior to + the terminating newline in special level and dungeon description files +Wizard of Yendor will start harassing you after the invocation if you've + managed to get that far without ever killing him +characters polymorphed into centaurs can't wear boots +if an unknown rolling boulder trap kills a monster, you shouldn't be a murderer +touchstone entry in data.base +specific message for engraving headstone with wand of digging +wielded/quivered chained ball should be unwielded when thrown +polymorphing into a form that cannot twoweapon should immediately disable + twoweapon mode; likewise when reverting from a monster form which + can use two weapons to a normal form which can't +taking partial count of merged objects from a container while your pack + was full split the object and did not re-merge +animal_parts are not always appropriate for ravens +prevent panic if tombstone window cannot be created +clarify travel command behavior in the Guidebook +touch_artifact checks needed when snagging w/bullwhip and stealing +cannot trip over submerged objects if you're water walking +wand of striking was not identified if it activated a statue trap +cannot sacrifice while you're swallowed +player polymorphed into an eel cannot drown breathless/amphibious monsters +avoid dmonsfree impossible message due to migrating a dead monster via + mhurtle causing the monster to end up in a hole or other trap +avoid temporary disappearing Burdened message due to updating status line + midway thru in_container +don't credit player's wisdom when makelevel creates random Elbereth engravings +reduce insect/monster creation from monster spells and limit chain summons +avoid "couldn't place lregion type 5" warning when arriving at Plane of Fire +avoid crash due to delayed poly or were change no longer being valid +ensure that Priest's ability to recognize B/U/C is considered in B/U/C menus +can't push boulders through iron bars; traps can't roll such through either; + likewise for objects thrown by monsters +thrown objects susceptible to breaking might do so when they hit iron bars +assorted monsters can pass through iron bars; ditto for polymorphed character +attempting to dig iron bars will wake nearby monsters instead of yielding + "you swing your pick-axe through thin air" +autodig won't accept iron bars as candidate location +allow knight to retaliate for all thefts except those "you gladly hand over..." +randomize starting position on goal level for M, P, and S quests +prevent the Wizard of Yendor from displacing the high priest of Moloch out of + the Sanctum's temple +ATR_BOLD on spell menu header +travel command should restrict its shortest paths to areas of the map the + hero knows about or might reasonably guess +non-altar prayer should limit god action, not maximize it +potions of acid explode, not dilute, so make water_damage behave this way +lookat monster notes if you see monster is trapped +don't crash when angry shopkeeper re-enters the shop and you pick up something +monsters with WAITFORU strategy should act if successfully attacked by + non-damaging attacks (e.g. seduction, rust damage) +don't summon kops if credit covers cost of unpaid goods taken out of shop +update swallowed display immediately if an engulfing monster polymorphs + into another engulfing monster +undo xname FAKE_AMULET_OF_YENDOR AD_DRIN check, the_unique_obj checks this case +axes should chop trees; picks shouldn't +chance to aim grappling hook when skilled or better +level limit of monsters like naga hatchlings should be high enough to grow up +scroll of enchant weapon will become discovered when read in some cases +don't crash when using lookat on a boulder an BOULDER sym is unique +attaching a single candle to fill candelabrum's last slot gave message with + poor grammar: "The candelabrum now has seven candle attached." +vault guards won't ask who you are if you're unconscious or paralyzed +monsters should not repeatedly try to teleport on noteleport levels +crocodiles legs are not designed for kicking open doors, chests, et al. +walls of one of the luckstone locations in minend-3 were diggable +minetn-6 could place downstairs in a cut-off location +corpses in bones files don't retain their role characteristic +boulder was not displayed if blind and discovered with a monster known via + ESP behind it +don't claim that statue comes to life if the monster it turns into is invisible +fix goodpos() so worm segments don't get placed on top of each other (causing + a possible display problem if the worm is cut in two) +fix fountain noises on some special levels (castle, valk home, various mines) +disallow mounting a trapped steed to avoid inappropriate trap effects +#chat with meditating monster will rouse it +suppress redundant message when stoning effect transforms a golem +clear worn bits of any object grabbed by shopkeeper to avoid extract_nobj panic +looting any container on a location should suppress looting nearby monsters +give more specific message when forbidden role attempts to use twoweapon mode +avoid double billing if #loot causes a shop's bag of holding to explode +when polymorphed, player killing a paper or straw golem via fire damage + would kill the golem twice, resulting in an impossible error +usually stop mimicing if you polymorph while using #monster mimic capability +under !GOLDOBJ, gold shouldn't disappear if you try to throw it at yourself +under !GOLDOBJ, remove temp gold from inventory during restore +Staff of Aesculapius did not always cure sliming +correct singularization of fungi, liches, vortices +prevent "remove_object: obj not on floor" panic for iron ball placement if + riding while punished leads to a fall off steed when changing levels +specifying -D (or -X) to enter explore mode while restarting from a save + file was lost in the restore process +fix crash when using lookat on an known invisible monster with monster syms set +prevent getting stuck in infinite loop when using wizard mode #levelchange + command to reduce level while having level-drain resistance +naming an already wielded elven dagger "Sting" activates warning against orcs +naming either of the wielded weapons unintentionally ends two-weapon combat +Various nemesis monsters must resist stoning so their death messages make sense +don't call DEBUG impossible in rn2 when a level 0 monster tries to cast a spell +GOLDOBJ: don't call money2mon with 0 zero when killed by shopkeeper +headstone writing was using the adjective "weird" when engraving with a wand + of digging. +don't report "you were riding" if you die as a result of dismounting +allow #untrapping of chests that are co-located with floor traps and hero +unmap "I" symbols when searching while blind and levitating +monsters that are frozen or sleeping cannot be grateful for untrapping +grammar of blessed-detection eating warning messages when eating 1 of N objects +message for charging for items lost in a cursed magic bag wasn't always shown +dropping gold on an altar printed no message and didn't change gnostic conduct +don't allow cursed daggers thrown by monsters to go thru closed doors +hero polymorphed into an exploding monster should explode when attacking + thin air, just like the monster itself +don't mark holes/trapdoors as seen if you levitate over them while blind +player polymorphed as rust monster would lose gold in inventory by + attempting to eat it, even though the eat failed +no messages were printed when dowaterdemon or dowaternymph failed to create + a monster doe to the G_GONE check +knights should be able to avenge attacks from covetous monsters +eating various rotten food items would not break vegan/vegetarian conduct +unaligned special levels should inherit alignment from the dungeon +Samurai quest was missing several doors +Cancelled while polymorphed and Unchanging should provide feedback +stone to flesh on a statue with contents would lose the contents if a + monster was on the same location as the statue +steed movement would use your speed if walking step by step +kicking a known, unseen monster would sometimes leave behind an extra I symbol +applying a lance against a long worm could cause an impossible +a knight applying a lance did not do a caitiff check +blessed gain level when already at level 30 won't reduce experience points +keep counting spell skill exercise even after expert status is reached +when a fountain dries up or a throne vanishes, make sure it really happens +allow player to name polymorph potion if nothing seems to happen +avoid crash when drinking a potion causes the hero to float up over a fire + trap, for example, which might try to destroy the in-use potion +in some situations, if hero stood still, a hostile dwarf would switch back + and forth between weapon and pick-axe and never move +uncontrolled teleports did not handle leashed pets +minetown fountain warnings shouldn't prevent finding gems/coins in fountain +order of container and objects was different for mazelike and roomfilled levels +minetown guards only enforce town rules inside the town proper +electric damage heals hero polymorphed into flesh golem rather than iron golem +fix bug preventing wishing for bear traps (not beartrap objects) in debug mode +be notified about cessation of hallucinations even if blind and the time +when using '/' to examine multiple map items in succession, don't mislabel + some with "or a splash of venom" after having looked at a '.' item +martial arts kick that knocks a monster into a trap would result in warning + "dmonsfree: 1 removed doesn't match 2 pending" if the trap was fatal +if you can't see or sense a monster when it dies, don't set dknown on corpse +effect of wearing or removing the Eyes of the Overworld took effect on the + next move, but should take effect immediately. +dragon scale mail is magic armor +invoking or applying an artifact must pass a touch_artifact check +document 'D'rop BUCX behavior in the Guidebook +remove levitation boots over a portal, the portal teleport is delayed until + your next command is typed. +armor vs cursed two-handed weapon anomalies: with 'T', couldn't remove armor, + but with 'A', could remove it, and with 'W', could put it on +don't print ape data.base description for other words that end in "ape" +prevent crash after animating a statue via stone_to_flesh by avoiding use + of the obj in newsym() after it was deleted +print "magic spreads" message when eating ring of increase damage, etc. +grammar tid: "The looking glass miss the ." +fix wishing for "looking glass" and " glass" +Archeologists suffer same alignment penalty for statue destruction by + stone_to_flesh as they do by other means of statue destruction +being unable to see a vault guard doesn't prevent him from arriving +in town, secret doors should be called "wall", not "fountain" +in town, watch should not allow trees to be cut down +cancel chat direction cancels the chat +prevent "the mimic looks better" on an unrecognized mimic hit with + healing spell +after forcefighting a concealed lurker, the lurker wouldn't fight back +when polymorphed into a hider, cease hiding during level changes +let mind flayer grow up into master mind flayer; also giant/sewer rat and + cave/large spider +engulfing green slime as a purple worm was causing stoning not sliming +zero entries in DUNGEON, MONSTERS, et al, of config file are now treated + as preserving the default rather than being ignored +enlightenment: don't misreport polymorphed lycanthrope as "in beast form" +remove TIMED_DELAY from the features checked for version compatibility +rolling boulder hitting monster stuck in pit should stop even when mon survives +don't see chest trap gas colors while Blind +adjust fruit name in potion juice messages if it has the form "foo of bar" +wielded camera passes harmlessly through shade +reading spellbooks while confused should allow tearing the book +Breaking wand of digging dug through rock which should be undiggable. +Breaking wand of digging near shop walls wouldn't anger the shopkeeper +Shop walls wouldn't be restored if there were pits in the way. +If there were a hole outside a shop, you could kick stuff out of the door + into the hole without the shopkeeper noticing. +curing hallucination while wielding Grayswandir should print a message +removing unowned pick-axe from container in shop gave inappropriate message +don't let monster end up with more current HP than max HP after life drain +make sure that missing file trickery in wizard mode which is discovered during + level change doesn't try to keep going after discarding current level +contribution by Adam Wozniak adds several const & changes some char* to char[] +fix impossible when hitting/jousting a monster causes it to be killed twice +fix a GOLDOBJ crash/hang in take_gold() that could be triggered by reading a + cursed spellbook, or by sitting on a throne +kicking a tree could produce 0 to 4 killer bees but it should have been 1 to 5 +mounting a steed allowed hero to make moves that would otherwise be disallowed + including mounting diagonally in a shop doorway +monsters lose intrinsic speed when pertrified +if you have converted, the quest leader banishes you instead of asking you + to come back later, and tells you that you won't succeed without Bell +don't state that "you narrowly avoid losing all chance" message if you try + to put on a helm of opposite alignment in the quest after converting +fix enlightenment feedback for bonus or penalty on damage and chance to hit +effects of purple worms consuming special monsters is now more consistent + across eating, digesting and dropped corpses while engulfed +avoid "you finish disrobing" when disarming via the 'A' command +make sure corpses and statues which remember monster attributes don't keep + ones that were conferred by no longer worn items (mainly speed boots) +elevate the trouble priority of any cursed item which is preventing removal + of a ring of levitation +starving pets will eat more aggressively +when a pet starves to death, say so instead of just "Fido dies." +starved pet raised from dead shouldn't immediately starve again +skilled spell of detected treasure wasn't acting like blessed potion of + object detection (from Roderick Schertler) +fix end of game attribute disclosure for levitation negated by sink +kicking a box embedded in a wall will knock it free rather than bust it open +stop running or travelling if the vibrating square message is triggered +show correct gender in ^X display when polymorphed into non-humanoid form +for wizard and explore modes, skip second screen of ^X output when first + screen is cancelled by ESC +for wizard mode, override confusion when using ^F to reveal map +polyself into minotaur causes hard headgear to fall off +with multiple leashes in use, 2nd had 50/50 chance of having unbounded length +GOLDOBJ: coins aren't subject to curses/blesses and don't need identification +can no longer activate a figurine while engulfed +can't use figurines to get too many erinyes or Nazgul +include currently wielded weapon among the list of likely choices for 'w' +likewise for currently quivered ammo among choices for 'Q' +only include unknown gems as likely choices when applying known touchstone +prevent mbodypart() from returning animal parts for lights +removing a ring might relearn what it is after amnesia +sleeping shopkeeper shouldn't talk to digging player +give more specific feedback when dipping unicorn horns into potions +can see self via infravision or ESP or monster detection when invisible +class genocide that killed polymorphed self while `Unchanging' reported + incomplete cause of death and possibly left rest of class in bones +class genocide of @ by human or elf character polymorphed into non-@ gave + "you feel dead inside" message twice +unskilled rider who can't reach items on floor also can't dip into moat or + pool from flying steed +when summoning nasty monsters, use new monster's type to decide if they can + be placed on boulders, et al, not the summoning monster's type +don't display the "intones:" prefix when !soundok since the message suffix + won't be displayed in this case +document "sound" option in Guidebook +destroy traps that are buried by boulders dropped in water +renamed debug commands: light sources -> lightsources, + monpoly_control -> monpolycontrol, poly -> polyself +detect attempt to swap places with big pet through narrow opening +stinking clouds in bones files do not get their ttl set reasonably +stinking clouds in bones files may incorrectly set player_inside +breaking wand of digging on a drawbridge shouldn't dig/hole a pit in the bridge +avoid mimicking gold when the character has the Unchanging attribute +handle polearm wielded prior to mounting the same as one wielded while mounted, + and one still used after dismounting like one wielded while not mounted +non-lawful angels don't chat using lawful messages and shouldn't summon + lawful help +cancelled yellow lights should not explode against other monsters, as well as + not exploding against you +becoming confused, eg from nausia, while reading a spellbook should result + in the usual confusion effects +teleports should not be controlled if you're stunned, confusion should have + some effect on your ability to control level teleports +vault wall repair should remove traps subsequently created at affected spots +don't reveal deity name when a high priest(ess) gives temple entry greeting +for ordinary remove curse, don't uncurse quivered object unless it is suitable + to be used as a quivered weapon (ammo or missile) +salamanders have no legs and cannot ride +all objects carried by a monster who's hit by a polymorph zap are protected + from that zap, not just worn armor which falls off due to shape change +sparkle option for display effects was ignored on explosions +level teleport while on a sleeping steed caused panic and possible crash +breaking wand of digging causing a shopkeeper to fall left unpaid items unpaid +use get_adjacent_loc() rather than getdir() directly for some things where + you want to ensure valid adjacent coordinates are returned +minor experience calculation tweaks +level telporting out of the dungeon while carrying unpaid shop goods would + trigger "not on any bill" warnings during final inventory disclosure +only hard helmets protect against falling piercers +don't crash teleporting out of a monster while engulfed, punished but not + carrying the ball +web breaking should consider steed strength and other characteristics +various missing or inappropriate "killed by" death messages +second attack for two-weapon combat will miss if first knocks target away +jousting effect no longer occurs every time riding character hits with lance +skeletons should be able to wear the armor they're created with +bouncing zaps should not bounce around the edge of closed doors +mimics that are detected but not seen should not display as their mimiced + form when the detection ends +not all cavemen are human, so avoid using human in quest messages +tengu is singular and plural, some rumors were incorrect +don't let leader or nemesis be renamed +non-moving monster are not affected by liquid +'A' command wouldn't remove cursed item from quiver or alternate weapon slot +'A' command behaved differently depending on menustyle when non-weapons were + present in the quiver or alternate weapon inventory slots +most cases of the hero dropping things need to check for dropping on an altar +zapping undiggable trees with wand or spell of dig gave feedback about rock +being able to see invisible shouldn't cause you to not notice when potion + or spell of invisibility wears off +can't successfully bribe a demon who happens to be carrying the Amulet +while over water, killing a monster that had engulfed you does not result + in the usual water effects +removing a ring of levitation while engulfed should not invoke spoteffects +feedback from invoking Orb of Detection was sometimes misleading or wrong +ranger quest artifact (Longbow of Diana) confers reflection when wielded by + monsters like it does for the player +avoid discrepancies in size and associated armor-wearing ability between + wizard gnome player and same player polymorphed into gnomish wizard + by forcing newman() if poly-target matches your_race() +add missing data.base entries for caveman, healer, monk, priest, and samurai +allow "grey spellbook" as alternative spelling of "gray spellbook" +handle attacks by cancelled monsters more consistently +armor worn by monsters might negate some magic attacks like it does for hero +give feedback and discovery when visible monster puts on cloak of invisibility +really add artifacts inside carried containers to final score (3.3.1 fix + displayed them them but didn't include any points for them) +drop alternate weapon to terminate twoweapon combat if the alternate + weapon gets cursed +restore monster creation sanity checks to wizard mode ^G command +prevent recoil from hurtling you through narrow areas that you wouldn't + be able to move through intentionally +grammar in cause of death when killed by slipping while mounting named steed +ensure `m'enu is still an available traditional menu choice for + menu-upon-request even when there is only one class of object present +engraving on headstone will appropriately dull your weapon +certain types of golems should not "catch fire" so adjust the messages +no longer need to manually examine inventory after regaining sight in order + to give a type name to an object picked up while blind +when adding an object to inventory, it is possible for it to becomed both + wielded and quivered if it merges with weapon and autoquiver is enabled +include rocks as likely candidates for quivering if alternate weapon is a sling +Asmodeus fails an is_armed() check, so code in m_initweap() to give him wands + of fire and cold never got called; move the code to m_initinv() +#rub would wield the target tool even when already being worn as eyewear +monks lose their to-hit bonus for bare-handed attacking if wearing a shield +fix case on leading character in "Crunched in the head..." in ball.c +using travel mode to move next to a known trap and then trying to step onto + that trap required an extra step; the first one ended up as a no-op +punished with ball and chain on the same floor square as a trapped chest + when it exploded resulted in panic "remove_object: obj not on floor" +see_monsters() wasn't called when you lost the innate warning intrinsic due + to level loss +xorns sink if the drawbridge they're standing on is raised +applying figurines to an adjacent spot over water does drowning checks +fix sequencing of Magicbane's hit messages +avoid buffer overflow from long or too many -s params +wake up first if trying to crawl out of water while asleep +while waiting, don't try to change into were form when already in were form +steed should remember traps encountered while mounted +killing shopkeeper by throwing unpaid things would result in + "item not on bill" impossible error +choking pet to death with cursed leash incurs various pet-killing penalties +wielding Werebane prevents catching lycanthropy via monster attack (but not + via eating, nor does it cure an existing case) +character inflicted with lycanthropy is vulnerable to Werebane when in + human/elf/&c form as well as when in beast form +shopkeeper could get angry without remembering the customer name +any object held by ghost during recorporealization would cease to exist + including the Amulet of Yendor +harassing monsters will be less likely to teleport to your location while + they're running away from you +randomize warning symbols during hallucination + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +wince: added Windows CE port from Alex Kompel +win32: win32 build no longer defines MICRO +win32: allow error save files to be generated +win32: strip illegal file name characters from plname and replace with '_' +win32: don't let recover build a save file out of level files belonging + to an active NetHack.exe or NetHackw.exe process +win32,winCE: SELF_RECOVER to let NetHack itself recover aborted games +win32gui: make error() work; it was essentially non-operative in 3.4.0 +win32gui: fix alignment of columns in menu windows +win32gui: Window menu File|Save worked during #quit disclosure processing +win32gui: make mswin_yn_function() case insensitive like the tty version +win32gui: In msg window, gray out lines that are old and concatenate msgs +win32gui: --More-- prompt if there are more messages than can fit this turn +win32gui: flicker reduction because clear_nhwindow no longer redraws window +win32gui: A caret bug was fixed +win32gui: fix bug that caused two lines too many to be drawn on each paint +win32gui: last line no longer highlighted +win32gui: reduce the number of popups and support for !popup +win32tty: honour the use_inverse option and default to ATR_BOLD if disabled +win32tty: respond only to mouse clicks not mouse movement +win32tty: allow ^C to abort the game at the "Who are you?" prompt +win32tty: fix truncated score list +win32tty: prevent ALT+CTRL from sending NUL to core with the meta bit set +win32tty: international keyboard handling improved by letting ToAscii() + generate input char based on VK and state of shift and caps lock +Gnome: add support for non-square tiles +Gnome: destroy main game windows correctly +Gnome: Dylan Alex Simon's port of KDE-style worn window +Gnome: Dylan Alex Simon's port of KDE-style hero cursor color +Gnome/Linux: more portable getres*id workaround +X11: restore support for non-square tiles when USE_XPM is defined +X11: getlin dialog got steadily narrower each time it was used +msdos: compiling without NO_TERMS resulted in a link-time error +msdos: reworked Makefile.GCC to get rid of need to duplicate source files +msdos,win32: stop doing chdir when NOCWD_ASSUMPTIONS is defined +tty: remove #define DEBUG that forced debug behavior in production builds +tty: correctly handle an empty TERM environment variable +tty: don't lose messages when ESC has canceled their display +tty: clear topl after pickup_burden prompt +tty: support terms where turning off inverse video turns off color too +tty: object selection at --More-- prompt after '?' didn't work anymore +tty: ext command autocomplete now lets you enter auto-completed characters +non-tty: silently ignore tty msg_window option to allow sharing of config file +unix: install recover command into GAMEDIR by default +vms: prevent error() from indirectly triggering hangup save during forced exit + + +General New Features +-------------------- +lootabc option +runmode option +showrace option +travel option +cmdassist option to provide additional error feedback for some commands +mouse_support wincap option +scroll_amount wincap option to adjust how many cells to scroll at scroll_margin +debug mode: #panic routine to test panic() and panic save file generation +a new PANICLOG optional file to log the reason for panic and impossible messages +added validate_prefix_locations() for early directory prefix validation +fire traps are particularly bad for paper and straw golems +cream pies can be 'a'pplied to cause direct temporary blindness +eating newt corpse or tin of same can boost magical energy (Malcolm Ryan) +applying a eucalyptus leaf produces a whistle effect (Malcolm Ryan) +hobbits can wear elven mithril-coats +eating mimics now has an hallucination effect +prefix pickup command with 'm' to force menu of all objects present +provide feedback which states the correct command when players try to use + 'R' or 'P' for armour, or use 'W' or 'T' for accessories +optional #portdebug wizard mode command to invoke port-specific debug routines diff --git a/doc/fixes34.2 b/doc/fixes34.2 new file mode 100644 index 0000000..a9e0c85 --- /dev/null +++ b/doc/fixes34.2 @@ -0,0 +1,180 @@ +$RCSfile: fixes34.2,v $ $Revision: 1.2.2.130 $ $Date: 2003/08/26 15:13:56 $ + +General Fixes and Modified Features +----------------------------------- +avoid panic when secondary weapon is cursed while generating bones level +don't crash when applying a figurine, candle, or bell that gets used up +grammar bits +two invisible monsters hitting one another should not be visible +if only one monster in a monster-vs-monster fight is visible, show an I symbol + for the other one whether it is an attacker or defender +display "It" and not "The invisible " when an invisible pet eats food. +include a hint about expected input when prompting for musical notes +don't report "program initialization failed" if a panic occurs after the + game is over +include statue contents in end of game inventory disclosure +treat handlessness as a major problem when deciding prayer outcome +perform artifact touch checks when putting on accessories +missing noun in message when horns pierce through your helmet +don't use hcolor() for trapped chest gases when you aren't hallucinating +the age of a potion of oil from a bones file wasn't being handled correctly +putting gold in a container on the shop floor wasn't credited the way + gold already in the container when dropped was credited +avoid integer division rounding error when calculating carrying capacity +don't lock/unlock a door while in a pit, to be consistent with door opening +infravision should not make invisible player "visible" (it doesn't for monsters) +Perseus statue should always be male +charge correctly when breaking multiple objects with the same zap, avoids + a dopay: not to shopkeeper impossible +clean up funny lighting on the healer locate level +allow all tame monsters that eat to consider food thrown to them +the screen display wasn't always up to date after map topology changes +jumping over a sokobon pit would result in the player next to, not in, the pit +don't let arrow, rock or dart traps provide an infinite number of objects +make enhanced ammo harder to break to make lesser number last longer +dropping from height or throwing a normal container may damage contents +some Magicbane messages treated "erinys" as plural +initialize artifacts before processing $WIZKIT +clean up inconsistency between various places quaff is documented +is_damageable was using is_rottable incorrectly +charge for use of an unpaid tinning kit +avoid impossible when water freezes while hero is hiding under water +avoid impossible after eating the object the hero is hiding under +failed attempt to eat floor gold while polymorphed would lose the gold +running that stops for closed doors should stop at mimics mimicking closed doors +allow wishing for magenta potions (ignoring the rank name 'mage') +fix an uninitialized memory access in non-quick dolookup +fix were changing message that wasn't being displayed +immediate encumbrance feedback when removing gauntlets of power +make deliberately flying down the Castle's trap doors consistent with falling +give more explicit feedback for exploding bag of holding +help display for "list of game options" misformats runmode and scroll_amount +pit created by land mine explosion doesn't start out concealed +update map display sooner when pushed boulder triggers land mine explosion +prevent several QBUFSZ sized buffers from overflowing and triggering fatal + errors inside window port prompt routines +make sure that leashed monsters are released prior to shopkeeper inheriting + dead character's inventory +attaching long named candle to long named candelabrum caused buffer overflow +when polymorhed, only hand/weapon attack on disenchanter should result in + damage to weapon, gloves, etc. +killer should say "the" when choking on unique monster's corpse +allow applying polearm on monster you can see via infravision +killer reason shouldn't use "a" or "an" prefix for multiple projectiles + scattered by land mine explosion +killer reason for named missile could end up with garbage instead of the name +make killer reason for various poisioning deaths be more consistent +poison missiles were unintentionally more likely to inflict "deadly poison" + than in pre-3.4.1 releases +provide feedback when going invisible after eating a stalker +killer on tombstone had no prefix for starvation/exhaustion case +ensure proper message ordering for boulder trap messages +clean up data set by join_map that is overlaid by MAPs on special levels +clarify disclose option default in opthelp, and support "all" as old help said +add more calls to update_inventory as the inventory changes +don't charge for items picked up from monster's interior while swallowed +choking while eating non-food always called the food "quick snack" +short swords are not throwing weapons +several sit-in-trap cases were unreachable +curse candelabrum in bones, like other similar artifacts +detecting a trap in a chest while confused should not exercise wisdom +any golem statue hit with stone-to-flesh spell animates as flesh golem +correct invalid startup gender selection +can no longer untrap floor containers during unskilled riding +can no longer easily set land mines and bear traps during unskilled riding +refine cmdassist handling for armor vs accessories +prevent monsters from level teleporting out of the quest into the main dungeon +prevent monsters from level teleporting into the Sanctum prior to invocation +"m," command sequence would let you see all objects at a location even when + they included a cockatrice corpse which hero was unequipped to handle +use correct pronoun for unique monsters +hostile monsters who follow you between levels won't do so if they're fleeing +options for font_size for map, menu, message, status, and text all had the + same description of "the size of the map font" in options.c +when dismounting by choice and unimpaired, try not to land in a known trap +when jousting a pudding into a polymorh trap, it was possible to end up + with two of the new type of monster +don't allow polymorphed player to web over the stairs +geographical shopkeeper updates +stethoscope use should be free the first time it's use per player move +travel command caches last position to make non-mouse less painful +update pit trapped time when polymorphing to or from a monster that passes_walls +show artifact hit message which affect the monster that swallowed the hero +revived pet corpse from bones file should not be loyal to current player +finding a statue trap you are about to dig should stop your occupation +try to keep saddle at the same location as the steed corpse +never display I symbol on the mounted hero/steed location +pit digging is no longer stopped by a sleeping monster next to you +ensure mksobj() always attaches timer to corpse, even if called with init FALSE +only charge for eating one stacked tin, not all at once +add flag to makemon() to allow monster to be created adjacent to the supplied + coordinates if there is already a monster at the target location +stone-to-flesh of spot with multiple statues can animate more than one +use of stethoscope now deliberately impacted when hero is engulfed by whirly + monster but fixed so it can sometimes work on your steed there too +typos fixed in data.base +add looting freehand() check to able_to_loot() to prevent opening container + only to be told that you can't loot anything +Schroedinger's Cat could be placed at wrong location when its box is carried +travel while polymorphed into a grid bug should not move diagonally +refine cmdassist handling for grid bugs +when casting force bolt spell while engulfed go ahead and use the engulfers + name in the hit message rather than "it" +a fog cloud shouldn't pummel you with debris +do not let an attached iron ball drag the hero through a location that the hero + could not move normally +hero's appearance should change immediately after mimicing completes +avoid some uses of "it" in killer messages +avoid "singular of null?" warning for info lookup of obscure user input +there was no check for iron bars in dokick() so it defaulted to "empty space" +if you couldn't see the rat created in a sink for some reason other than + blindness, you would get "Eek there's it in the sink." +digging a pit while stuck in the floor should always free the player +quest guardians can no longer be created via stone-to-flesh on their statue +stone-to-flesh no longer silently ignored by a statue of a unique monster +wishing for quest guardian corpse now gives a generic corpse of the species +prevent quest guardians from other classes from talking to you as if they + were your quest guardian +wake up shopkeeper if a shop transaction is attempted while he's immobilized +statues created from monsters remember more monster attributes + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +Gnome: compilation problems on Redhat 7.2 and 8.0 +unix: Makefile.utl would put OBJDIR objects in the wrong directory +vms: create an empty paniclog file during playground installation +win32tty: add subkeyvalue option to alter key values; Finnish keyboard fix +win32tty: distinguish between black/gray/white (by Quietust) +win32gui: prevent male Valkyrie and other incorrect startup settings +win32gui: allow numeric quantity count on item selection during loot +win32: some code in files.c was incorrectly assuming that a file + descriptor return value of 0 from open() was invalid but it + could be valid on win32gui where stdin, stdout, stderr aren't open; + now it correctly checks for return value < 0 from open() +tiles: high priest tile had a couple bad pixels +tiles: bad pixels in Croesus and Yeenoghu tiles +FreeBSD: incorrect srandom declaration +unix: don't autosave if hangup occurs after game is over +linux: add example use of nroff on recent Linux distros +linux: use random() by default instead of lrand48() +OpenBSD: time() prototype and correct default Mail program +Gnome: compilation problems on Solaris +unix: better error message for .nethackrc access problems +vms: during installation, warn if dlb file creation or non-dlb playground + setup is missing expected data files + + +General New Features +-------------------- +debug mode level teleport menu via '?' + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ +win32tty: keystroke handlers can be dynamically loaded to assist in resolving + internationalization issues +win32tty: add Ray Chason's code for international keyboard handling +Solaris (and other SystemV variants): TIMED_DELAY support +X11: NetHack.ad is now installed and used w/o user intervention + diff --git a/doc/fixes34.3 b/doc/fixes34.3 new file mode 100644 index 0000000..e63f65b --- /dev/null +++ b/doc/fixes34.3 @@ -0,0 +1,146 @@ +$RCSfile: fixes34.3,v $ $Revision: 1.1.2.99 $ $Date: 2003/12/06 14:07:57 $ + +General Fixes and Modified Features +----------------------------------- +monster draining the player using Stormbringer decreased monster's hitpoints +polymorphing to a flaming sphere should cure slime like other flaming monsters +grammar, spelling and other typos +wishing for student corpse yielded a valkyrie one, not an archeologist one +fix typo in bustling town down stairs declaration +you could exceed the limits on nazgul and erinys counts via bones files +fix inconsistency where you can't kick something out of a pit, but you can + escape the pit and still pick it up; items are now assumed to be at + the bottom of pit +room cleanup, eg on Bustling Town, could incorrectly truncate room bounds + for rooms that become L shared due to partial overlap with the MAP +approaching Medusa while having reflection+invisibility+esp would cause her + to turn herself to stone if you happened to be blind at the time +Master Kaen's death message was not appropriate +missing fountain tag in minend-3 +do not pacify shopkeeper when the hero enters a shop if that hero previously + angered the shopkeeper without ever visibly entering the shop +attempting to place migrating monsters onto a monster-saturated level no + longer triggers impossible() +open_levelfile_exclusively() was showing the return value -1 in a panic message, + even though that was the only possible value; show errno instead +it was inappropriate to have a ghost "appear" in desecrated temple when + you were blind and without telepathy +accept wish for "grey spell book" not just "grey spellbook" +do not double credit when putting gold into an unpaid container +manes are nonliving +poles and grappling hook worked thru walls when wearing Eyes of the Overworld +more tweaks to fog cloud behavior +when dismounting by choice and unimpaired, try not to land on a boulder +casting stone-to-flesh on self while wielding a statue caused problems +add tab support to menu strings for control-x minimal_enlightenment() +if the monster that a statue represents is not made of flesh then don't + allow stone_to_flesh to animate it, make a meatball instead +attempting to saddle a cockatrice while wearing gloves shouldn't stone you +kicking a closed drawbridge and dieing should not say "kicking a wall" +cannot get blessed potions from sink, remove unreachable message +couldn't insert gold into a container using full menu style if no other + objects in inventory unless compiling with GOLDOBJ +nagas eat +always have warriors on the Valkyrie quest be female +be more consistent with sounds when dropping into water +surface() returns "bottom" when Underwater +bill for all discarded, opened tins +monsters that cannot pick things up cannot throw things either +eating an amulet of unchanging un-changes you +Vlad won't waste time trying to use wand of digging in his own tower +non-weapon iron objects should rust when dipped in fountains since + iron weapons rust +suppress "turn to flee" message if monster is mfrozen +don't silently interrupt monster's hold on you if Levitation/Flying ends + while over water +you could specifiy '~' with crystal ball and have it try to detect monsters, + but it never revealed anything; show the entire long worm now +allow a crystal ball to detect ghosts-and-shades via space key, and display + the results using detected_mon_to_glyph() so that they show up in + inverse video +allow a crystal ball to detect boulders using the user-defined boulder symbol +allow a crystal ball to detect mimics via ']' +prevent boulder option from accepting a symbol that matches a monster symbol +traveling while standing on a trap would sometime step in the wrong direction +avoid traveling into water/lava, using usual running rules +unchanging iron golem would still rehumanize in a rust trap +fix an impossible rndmonst: bad `mndx' bug +pets should not try to go after objects that they can't reach +cutting a shopkeeper polymorphed in to a long worm would generate strange + messages and could cause a crash +reading a cursed scroll of light in a corridor wouldn't display correctly + if lit_corridor option was disabled +barbarians can become export in short sword skill +samurai are now limited to master in martial arts skill; barbarians and + cavemen are now limited to master in bare-handed combat skill +tweak messages when werefoo summons help +when polymorphed into a quantum mechanic, it was possible to jump into + the water on a no teleport level and instinctively teleport +when polymorphed into a quantum mechanic on a no teleport level and swallowed, + no feedback was given when you teleported the swallower away +allow Conflict-resistant monsters to respond to conflict attacks rather than + sitting there and taking the attacks until they die +prefer herbivorous stone-to-flesh message when hero is a vegitarian +try even harder to avoid incorrect map display while changing levels +no "freaked" message by exploding black light, unless you really are +sleeping monster could respond to attacks by other monsters +sleeping shopkeeper responds to various events without waking +rotting corpses grammar fix +allow successful teleport to more locations on debug mode level teleport menu +trapped monster repeatedly switched between ranged and hand-to-hand weapon +silver items such as wands avoided all the silver checks in hmon_hitmon() +resuming an interrupted 'A' command could cause crash if pending worn item(s) + were stolen or destroyed +resuming interrupted 'A' sometimes ended with "You finished disrobing" twice +when you're asleep you shouldn't "notice" monsters that have become undetected +must be able to reach floor in order to use stethoscope on corpse or statue +fix a few coordinate (y,y) -> (x,y) typos in apply.c, mon.c, and wizard.c +killing a long worm on a drawbridge could produce a panic +prevent "see it drop from your pack" when figurine monster becomes undetected +attempting to drop a subset of a stack of multiple cursed loadstones could + corrupt inventory or cause a crash +"miss" message was missing for thrown or kicked gold not caught by a monster +prevent recursive impossible() and panic() calls from leading to a stack overflow +tainted meat didn't invoke cannibalism +shopkeepers can't act as porters for the Amulet +dismissed monsters can't remove special items from play + + +Platform- and/or Interface-Specific Fixes +----------------------------------------- +win32tty: fix visible CRLF characters during lockfile error message +win32tty: switch to low level console routines +win32tty: refrain from cursor movement until an input is pending (M. Lehotay) +win32tty: distinguish blue, bright blue, cyan, and bright cyan (Nicholas Webb) +win32tty: fix hanging problem when you ctrl-C at "Who are you?" prompt +win32gui: you couldn't specify an alignment in defaults.nh and have it stick +win32gui: allow race/gender/alignment selections beyond those specified in + defaults.nh, while still honoring defaults.nh choices +unix: don't define errno if NHSTDC +unix: save file permissions could be wrong in explore/debug mode +X11: avoid a possible crash when using window manger to close a player + selection window +Gnome: add Quiver menu item, fix outdated Quit menu item +Gnome: key values on unsigned char platform could fail to compare correctly +Gnome: real extended command menu so all extended commands can be entered +Gnome: ignore interrupts to avoid infinite loop in gnome library +tty: avoid crash displaying quit inventory if inventory was already displayed +tty: use "bold" in menu heading if available and requested +tty: differentiate between default unlit and lit corridor symbols +winCE: ensure orphaned lockfile is always deleted on single-user handhelds + + +General New Features +-------------------- +bones file compatibility info is now written into the dat/options file +extend autodig to work downwards via '>' +make attribute that is used to distinguish headings in a menu configurable +add experimental build option AUTOPICKUP_EXCEPTIONS for filtering pickup of + items by pattern matching against their doname() description +include version number in paniclog entries +add a section on "shops and shopping" to the Guidebook + + +Platform- and/or Interface-Specific New Features +------------------------------------------------ + diff --git a/doc/lev_comp.6 b/doc/lev_comp.6 new file mode 100644 index 0000000..c661582 --- /dev/null +++ b/doc/lev_comp.6 @@ -0,0 +1,572 @@ +.TH LEV_COMP 6 "16 May 1996" +.UC 4 +.SH NAME +lev_comp \- NetHack special levels compiler +.SH SYNOPSIS +.B lev_comp +[ +.B \-w +] +[ +.I files +] +.PP +If no arguments are given, it reads standard input. +.SH DESCRIPTION +.PP +.I Lev_comp +is a special level compiler for NetHack version 3.2 and higher. It +takes description files as arguments and produces level files that can +be loaded by NetHack at runtime. +.PP +The purpose of this tool is to provide NetHack administrators and +implementors with a convenient way for adding special levels to the +game, or modifying existing ones, without having to recompile the +entire world. +.PP +The +.B \-w +option causes +.I lev_comp +to perform extra checks on the level and display extra warnings, however +these warnings are sometimes superfluous, so they are not normally displayed. + +.SH GRAMMAR +.PP +.LP +.nf +.ta +8n +8n +8n +8n + +file : /* nothing */ + | levels + ; + +levels : level + | level levels + ; + +level : maze_level + | room_level + ; + +maze_level : maze_def flags lev_init messages regions + ; + +room_level : level_def flags lev_init messages rreg_init rooms corridors_def + ; + +level_def : LEVEL_ID ':' string + ; + +lev_init : /* nothing */ + | LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled + ; + +walled : BOOLEAN + | RANDOM_TYPE + ; + +flags : /* nothing */ + | FLAGS_ID ':' flag_list + ; + +flag_list : FLAG_TYPE ',' flag_list + | FLAG_TYPE + ; + +messages : /* nothing */ + | message messages + ; + +message : MESSAGE_ID ':' STRING + ; + +rreg_init : /* nothing */ + | rreg_init init_rreg + ; + +init_rreg : RANDOM_OBJECTS_ID ':' object_list + | RANDOM_MONSTERS_ID ':' monster_list + ; + +rooms : /* Nothing - dummy room for use with INIT_MAP */ + | roomlist + ; + +roomlist : aroom + | aroom roomlist + ; + +corridors_def : random_corridors + | corridors + ; + +random_corridors: RAND_CORRIDOR_ID + ; + +corridors : /* nothing */ + | corridors corridor + ; + +corridor : CORRIDOR_ID ':' corr_spec ',' corr_spec + | CORRIDOR_ID ':' corr_spec ',' INTEGER + ; + +corr_spec : '(' INTEGER ',' DIRECTION ',' door_pos ')' + ; + +aroom : room_def room_details + | subroom_def room_details + ; + +subroom_def : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill + ; + +room_def : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill + ; + +roomfill : /* nothing */ + | ',' BOOLEAN + ; + +room_pos : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + +subroom_pos : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + +room_align : '(' h_justif ',' v_justif ')' + | RANDOM_TYPE + ; + +room_size : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + +room_details : /* nothing */ + | room_details room_detail + ; + +room_detail : room_name + | room_chance + | room_door + | monster_detail + | object_detail + | trap_detail + | altar_detail + | fountain_detail + | sink_detail + | pool_detail + | gold_detail + | engraving_detail + | stair_detail + ; + +room_name : NAME_ID ':' string + ; + +room_chance : CHANCE_ID ':' INTEGER + ; + +room_door : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos + ; + +secret : BOOLEAN + | RANDOM_TYPE + ; + +door_wall : DIRECTION + | RANDOM_TYPE + ; + +door_pos : INTEGER + | RANDOM_TYPE + ; + +maze_def : MAZE_ID ':' string ',' filling + ; + +filling : CHAR + | RANDOM_TYPE + ; + +regions : aregion + | aregion regions + ; + +aregion : map_definition reg_init map_details + ; + +map_definition : NOMAP_ID + | map_geometry MAP_ID + ; + +map_geometry : GEOMETRY_ID ':' h_justif ',' v_justif + ; + +h_justif : LEFT_OR_RIGHT + | CENTER + ; + +v_justif : TOP_OR_BOT + | CENTER + ; + +reg_init : /* nothing */ + | reg_init init_reg + ; + +init_reg : RANDOM_OBJECTS_ID ':' object_list + | RANDOM_PLACES_ID ':' place_list + | RANDOM_MONSTERS_ID ':' monster_list + ; + +object_list : object + | object ',' object_list + ; + +monster_list : monster + | monster ',' monster_list + ; + +place_list : place + | place ',' place_list + ; + +map_details : /* nothing */ + | map_details map_detail + ; + +map_detail : monster_detail + | object_detail + | door_detail + | trap_detail + | drawbridge_detail + | region_detail + | stair_region + | portal_region + | teleprt_region + | branch_region + | altar_detail + | fountain_detail + | mazewalk_detail + | wallify_detail + | ladder_detail + | stair_detail + | gold_detail + | engraving_detail + | diggable_detail + | passwall_detail + ; + +monster_detail : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate + monster_infos + ; + +monster_infos : /* nothing */ + | monster_infos monster_info + ; + +monster_info : ',' string + | ',' MON_ATTITUDE + | ',' MON_ALERTNESS + | ',' alignment + | ',' MON_APPEARANCE string + ; + +object_detail : OBJECT_ID object_desc + | COBJECT_ID object_desc + ; + +object_desc : chance ':' object_c ',' o_name ',' object_where object_infos + ; + +object_where : coordinate + | CONTAINED + ; + +object_infos : /* nothing */ + | ',' curse_state ',' monster_id ',' enchantment optional_name + | ',' curse_state ',' enchantment optional_name + | ',' monster_id ',' enchantment optional_name + ; + +curse_state : RANDOM_TYPE + | CURSE_TYPE + ; + +monster_id : STRING + ; + +enchantment : RANDOM_TYPE + | INTEGER + ; + +optional_name : /* nothing */ + | ',' NONE + | ',' STRING + ; + +door_detail : DOOR_ID ':' door_state ',' coordinate + ; + +trap_detail : TRAP_ID chance ':' trap_name ',' coordinate + ; + +drawbridge_detail: DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state + ; + +mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION + ; + +wallify_detail : WALLIFY_ID + ; + +ladder_detail : LADDER_ID ':' coordinate ',' UP_OR_DOWN + ; + +stair_detail : STAIR_ID ':' coordinate ',' UP_OR_DOWN + ; + +stair_region : STAIR_ID ':' lev_region ',' lev_region ',' UP_OR_DOWN + ; + +portal_region : PORTAL_ID ':' lev_region ',' lev_region ',' string + ; + +teleprt_region : TELEPRT_ID ':' lev_region ',' lev_region teleprt_detail + ; + +branch_region : BRANCH_ID ':' lev_region ',' lev_region + ; + +teleprt_detail : /* empty */ + | ',' UP_OR_DOWN + ; + +lev_region : region + | LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + ; + +fountain_detail : FOUNTAIN_ID ':' coordinate + ; + +sink_detail : SINK_ID ':' coordinate + ; + +pool_detail : POOL_ID ':' coordinate + ; + +diggable_detail : NON_DIGGABLE_ID ':' region + ; + +passwall_detail : NON_PASSWALL_ID ':' region + ; + +region_detail : REGION_ID ':' region ',' light_state ',' room_type prefilled + ; + +altar_detail : ALTAR_ID ':' coordinate ',' alignment ',' altar_type + ; + +gold_detail : GOLD_ID ':' amount ',' coordinate + ; + +engraving_detail: ENGRAVING_ID ':' coordinate ',' engraving_type ',' string + ; + +monster_c : monster + | RANDOM_TYPE + | m_register + ; + +object_c : object + | RANDOM_TYPE + | o_register + ; + +m_name : string + | RANDOM_TYPE + ; + +o_name : string + | RANDOM_TYPE + ; + +trap_name : string + | RANDOM_TYPE + ; + +room_type : string + | RANDOM_TYPE + ; + +prefilled : /* empty */ + | ',' FILLING + | ',' FILLING ',' BOOLEAN + ; + +coordinate : coord + | p_register + | RANDOM_TYPE + ; + +door_state : DOOR_STATE + | RANDOM_TYPE + ; + +light_state : LIGHT_STATE + | RANDOM_TYPE + ; + +alignment : ALIGNMENT + | a_register + | RANDOM_TYPE + ; + +altar_type : ALTAR_TYPE + | RANDOM_TYPE + ; + +p_register : P_REGISTER '[' INTEGER ']' + ; + +o_register : O_REGISTER '[' INTEGER ']' + ; + +m_register : M_REGISTER '[' INTEGER ']' + ; + +a_register : A_REGISTER '[' INTEGER ']' + ; + +place : coord + ; + +monster : CHAR + ; + +object : CHAR + ; + +string : STRING + ; + +amount : INTEGER + | RANDOM_TYPE + ; + +chance : /* empty */ + | PERCENT + ; + +engraving_type : ENGRAVING_TYPE + | RANDOM_TYPE + ; + +coord : '(' INTEGER ',' INTEGER ')' + ; + +region : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + ; +.fi +.PP +.I NOTE: +.br +Lines beginning with '#' are considered comments. +.PP +The contents of a "MAP" description of a maze is a rectangle showing the exact +level map that should be used for the given part of a maze. +Each character in the map corresponds to a location on the screen. +Different location types are denoted using different ASCII characters. +The following characters are recognized. +To give an idea of how these are used, see the EXAMPLE, below. +The maximum size of a map is normally 76 columns by 21 rows. +.LP +.nf +.ta +8n +8n +8n +\&'-' horizontal wall +\&'|' vertical wall +\&'+' a doorway (state is specified in a DOOR declaration) +\&'A' open air +\&'B' boundary room location (for bounding unwalled irregular regions) +\&'C' cloudy air +\&'I' ice +\&'S' a secret door +\&'H' a secret corridor +\&'{' a fountain +\&'\\' a throne +\&'K' a sink (if SINKS is defined, else a room location) +\&'}' a part of a moat or other deep water +\&'P' a pool +\&'L' lava +\&'W' water (yes, different from a pool) +\&'T' a tree +\&'F' iron bars +\&'#' a corridor +\&'.' a normal room location (unlit unless lit in a REGION declaration) +\&' ' stone +.fi +.SH EXAMPLE +.PP +Here is an example of a description file (a very simple one): +.LP +.nf +.ta +8n +8n +8n +MAZE : "fortress", random +GEOMETRY : center , center +MAP +}}}}}}}}} +}}}|-|}}} +}}|-.-|}} +}|-...-|} +}|.....|} +}|-...-|} +}}|-.-|}} +}}}|-|}}} +}}}}}}}}} +ENDMAP +MONSTER: '@', "Wizard of Yendor", (4,4) +OBJECT: '"', "Amulet of Yendor", (4,4) +# a hell hound flanking the Wiz on a random side +RANDOM_PLACES: (4,3), (4,5), (3,4), (5,4) +MONSTER: 'd', "hell hound", place[0] +# a chest on another random side +OBJECT: '(', "chest", place[1] +# a sack on a random side, with a diamond and maybe a ruby in it +CONTAINER: '(', "sack", place[2] +OBJECT: '*', "diamond", contained +OBJECT[50%]: '*', "ruby", contained +# a random dragon somewhere +MONSTER: 'D', random, random +# 3 out of 4 chance for a random trap in the EAST end +TRAP[75%]: random, (6,4) +# an electric eel below the SOUTH end +MONSTER: ';', "electric eel", (4,8) +# make the walls non-diggable +NON_DIGGABLE: (0,0,8,8) +TELEPORT_REGION: levregion(0,0,79,20), (0,0,8,8) +.fi +.PP +This example will produce a file named "fortress" that can be integrated into +one of the numerous mazes of the game. +.PP +Note especially the final, TELEPORT_REGION specification. This says +that level teleports or other non-stairway arrivals on this level can +land anywhere on the level except the area of the map. This shows the +use of the ``levregion'' prefix allowed in certain region specifications. +Normally, regions apply only to the most recent MAP specification, but +when prefixed with ``levregion'', one can refer to any area of the +level, regardless of the placement of the current MAP in the level. +.SH AUTHOR +.PP +Jean-Christophe Collet, David Cohrs. +.SH "SEE ALSO" +.PP +dgn_comp(6), nethack(6) +.SH BUGS +.PP +Probably infinite. +Most importantly, still needs additional bounds checking. diff --git a/doc/lev_comp.txt b/doc/lev_comp.txt new file mode 100644 index 0000000..4a28fce --- /dev/null +++ b/doc/lev_comp.txt @@ -0,0 +1,726 @@ + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + +NAME + lev_comp - NetHack special levels compiler + +SYNOPSIS + lev_comp [ -w ] [ files ] + + If no arguments are given, it reads standard input. + +DESCRIPTION + Lev_comp is a special level compiler for NetHack version 3.2 + and higher. It takes description files as arguments and + produces level files that can be loaded by NetHack at run- + time. + + The purpose of this tool is to provide NetHack administra- + tors and implementors with a convenient way for adding spe- + cial levels to the game, or modifying existing ones, without + having to recompile the entire world. + + The -w option causes lev_comp to perform extra checks on the + level and display extra warnings, however these warnings are + sometimes superfluous, so they are not normally displayed. + + +GRAMMAR + file : /* nothing */ + | levels + ; + + levels : level + | level levels + ; + + level : maze_level + | room_level + ; + + maze_level : maze_def flags lev_init messages regions + ; + + room_level : level_def flags lev_init messages rreg_init rooms corridors_def + ; + + level_def : LEVEL_ID ':' string + ; + + lev_init : /* nothing */ + | LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled + ; + + walled : BOOLEAN + | RANDOM_TYPE + + + +May Last change: 16 1 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + ; + + flags : /* nothing */ + | FLAGS_ID ':' flag_list + ; + + flag_list : FLAG_TYPE ',' flag_list + | FLAG_TYPE + ; + + messages : /* nothing */ + | message messages + ; + + message : MESSAGE_ID ':' STRING + ; + + rreg_init : /* nothing */ + | rreg_init init_rreg + ; + + init_rreg : RANDOM_OBJECTS_ID ':' object_list + | RANDOM_MONSTERS_ID ':' monster_list + ; + + rooms : /* Nothing - dummy room for use with INIT_MAP */ + | roomlist + ; + + roomlist : aroom + | aroom roomlist + ; + + corridors_def : random_corridors + | corridors + ; + + random_corridors: RAND_CORRIDOR_ID + ; + + corridors : /* nothing */ + | corridors corridor + ; + + corridor : CORRIDOR_ID ':' corr_spec ',' corr_spec + | CORRIDOR_ID ':' corr_spec ',' INTEGER + ; + + corr_spec : '(' INTEGER ',' DIRECTION ',' door_pos ')' + ; + + aroom : room_def room_details + + + +May Last change: 16 2 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + | subroom_def room_details + ; + + subroom_def : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill + ; + + room_def : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill + ; + + roomfill : /* nothing */ + | ',' BOOLEAN + ; + + room_pos : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + + subroom_pos : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + + room_align : '(' h_justif ',' v_justif ')' + | RANDOM_TYPE + ; + + room_size : '(' INTEGER ',' INTEGER ')' + | RANDOM_TYPE + ; + + room_details : /* nothing */ + | room_details room_detail + ; + + room_detail : room_name + | room_chance + | room_door + | monster_detail + | object_detail + | trap_detail + | altar_detail + | fountain_detail + | sink_detail + | pool_detail + | gold_detail + | engraving_detail + | stair_detail + ; + + room_name : NAME_ID ':' string + ; + + room_chance : CHANCE_ID ':' INTEGER + + + +May Last change: 16 3 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + ; + + room_door : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos + ; + + secret : BOOLEAN + | RANDOM_TYPE + ; + + door_wall : DIRECTION + | RANDOM_TYPE + ; + + door_pos : INTEGER + | RANDOM_TYPE + ; + + maze_def : MAZE_ID ':' string ',' filling + ; + + filling : CHAR + | RANDOM_TYPE + ; + + regions : aregion + | aregion regions + ; + + aregion : map_definition reg_init map_details + ; + + map_definition : NOMAP_ID + | map_geometry MAP_ID + ; + + map_geometry : GEOMETRY_ID ':' h_justif ',' v_justif + ; + + h_justif : LEFT_OR_RIGHT + | CENTER + ; + + v_justif : TOP_OR_BOT + | CENTER + ; + + reg_init : /* nothing */ + | reg_init init_reg + ; + + init_reg : RANDOM_OBJECTS_ID ':' object_list + | RANDOM_PLACES_ID ':' place_list + + + +May Last change: 16 4 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + | RANDOM_MONSTERS_ID ':' monster_list + ; + + object_list : object + | object ',' object_list + ; + + monster_list : monster + | monster ',' monster_list + ; + + place_list : place + | place ',' place_list + ; + + map_details : /* nothing */ + | map_details map_detail + ; + + map_detail : monster_detail + | object_detail + | door_detail + | trap_detail + | drawbridge_detail + | region_detail + | stair_region + | portal_region + | teleprt_region + | branch_region + | altar_detail + | fountain_detail + | mazewalk_detail + | wallify_detail + | ladder_detail + | stair_detail + | gold_detail + | engraving_detail + | diggable_detail + | passwall_detail + ; + + monster_detail : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate + monster_infos + ; + + monster_infos : /* nothing */ + | monster_infos monster_info + ; + + monster_info : ',' string + | ',' MON_ATTITUDE + | ',' MON_ALERTNESS + + + +May Last change: 16 5 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + | ',' alignment + | ',' MON_APPEARANCE string + ; + + object_detail : OBJECT_ID object_desc + | COBJECT_ID object_desc + ; + + object_desc : chance ':' object_c ',' o_name ',' object_where object_infos + ; + + object_where : coordinate + | CONTAINED + ; + + object_infos : /* nothing */ + | ',' curse_state ',' monster_id ',' enchantment optional_name + | ',' curse_state ',' enchantment optional_name + | ',' monster_id ',' enchantment optional_name + ; + + curse_state : RANDOM_TYPE + | CURSE_TYPE + ; + + monster_id : STRING + ; + + enchantment : RANDOM_TYPE + | INTEGER + ; + + optional_name : /* nothing */ + | ',' NONE + | ',' STRING + ; + + door_detail : DOOR_ID ':' door_state ',' coordinate + ; + + trap_detail : TRAP_ID chance ':' trap_name ',' coordinate + ; + + drawbridge_detail: DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state + ; + + mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION + ; + + wallify_detail : WALLIFY_ID + ; + + + + +May Last change: 16 6 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + ladder_detail : LADDER_ID ':' coordinate ',' UP_OR_DOWN + ; + + stair_detail : STAIR_ID ':' coordinate ',' UP_OR_DOWN + ; + + stair_region : STAIR_ID ':' lev_region ',' lev_region ',' UP_OR_DOWN + ; + + portal_region : PORTAL_ID ':' lev_region ',' lev_region ',' string + ; + + teleprt_region : TELEPRT_ID ':' lev_region ',' lev_region teleprt_detail + ; + + branch_region : BRANCH_ID ':' lev_region ',' lev_region + ; + + teleprt_detail : /* empty */ + | ',' UP_OR_DOWN + ; + + lev_region : region + | LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + ; + + fountain_detail : FOUNTAIN_ID ':' coordinate + ; + + sink_detail : SINK_ID ':' coordinate + ; + + pool_detail : POOL_ID ':' coordinate + ; + + diggable_detail : NON_DIGGABLE_ID ':' region + ; + + passwall_detail : NON_PASSWALL_ID ':' region + ; + + region_detail : REGION_ID ':' region ',' light_state ',' room_type prefilled + ; + + altar_detail : ALTAR_ID ':' coordinate ',' alignment ',' altar_type + ; + + gold_detail : GOLD_ID ':' amount ',' coordinate + ; + + engraving_detail: ENGRAVING_ID ':' coordinate ',' engraving_type ',' string + ; + + + +May Last change: 16 7 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + monster_c : monster + | RANDOM_TYPE + | m_register + ; + + object_c : object + | RANDOM_TYPE + | o_register + ; + + m_name : string + | RANDOM_TYPE + ; + + o_name : string + | RANDOM_TYPE + ; + + trap_name : string + | RANDOM_TYPE + ; + + room_type : string + | RANDOM_TYPE + ; + + prefilled : /* empty */ + | ',' FILLING + | ',' FILLING ',' BOOLEAN + ; + + coordinate : coord + | p_register + | RANDOM_TYPE + ; + + door_state : DOOR_STATE + | RANDOM_TYPE + ; + + light_state : LIGHT_STATE + | RANDOM_TYPE + ; + + alignment : ALIGNMENT + | a_register + | RANDOM_TYPE + ; + + altar_type : ALTAR_TYPE + | RANDOM_TYPE + ; + + + +May Last change: 16 8 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + p_register : P_REGISTER '[' INTEGER ']' + ; + + o_register : O_REGISTER '[' INTEGER ']' + ; + + m_register : M_REGISTER '[' INTEGER ']' + ; + + a_register : A_REGISTER '[' INTEGER ']' + ; + + place : coord + ; + + monster : CHAR + ; + + object : CHAR + ; + + string : STRING + ; + + amount : INTEGER + | RANDOM_TYPE + ; + + chance : /* empty */ + | PERCENT + ; + + engraving_type : ENGRAVING_TYPE + | RANDOM_TYPE + ; + + coord : '(' INTEGER ',' INTEGER ')' + ; + + region : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + ; + + NOTE: + Lines beginning with '#' are considered comments. + + The contents of a "MAP" description of a maze is a rectangle + showing the exact level map that should be used for the + given part of a maze. Each character in the map corresponds + to a location on the screen. Different location types are + denoted using different ASCII characters. The following + characters are recognized. To give an idea of how these are + used, see the EXAMPLE, below. The maximum size of a map is + + + +May Last change: 16 9 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + normally 76 columns by 21 rows. + + '-' horizontal wall + '|' vertical wall + '+' a doorway (state is specified in a DOOR declaration) + 'A' open air + 'B' boundary room location (for bounding unwalled irregular regions) + 'C' cloudy air + 'I' ice + 'S' a secret door + 'H' a secret corridor + '{' a fountain + '\' a throne + 'K' a sink (if SINKS is defined, else a room location) + '}' a part of a moat or other deep water + 'P' a pool + 'L' lava + 'W' water (yes, different from a pool) + 'T' a tree + 'F' iron bars + '#' a corridor + '.' a normal room location (unlit unless lit in a REGION declaration) + ' ' stone + +EXAMPLE + Here is an example of a description file (a very simple + one): + + MAZE : "fortress", random + GEOMETRY : center , center + MAP + }}}}}}}}} + }}}|-|}}} + }}|-.-|}} + }|-...-|} + }|.....|} + }|-...-|} + }}|-.-|}} + }}}|-|}}} + }}}}}}}}} + ENDMAP + MONSTER: '@', "Wizard of Yendor", (4,4) + OBJECT: '"', "Amulet of Yendor", (4,4) + # a hell hound flanking the Wiz on a random side + RANDOM_PLACES: (4,3), (4,5), (3,4), (5,4) + MONSTER: 'd', "hell hound", place[0] + # a chest on another random side + OBJECT: '(', "chest", place[1] + # a sack on a random side, with a diamond and maybe a ruby in it + CONTAINER: '(', "sack", place[2] + OBJECT: '*', "diamond", contained + OBJECT[50%]: '*', "ruby", contained + + + +May Last change: 16 10 + + + + + + +LEV_COMP(6) 1996 LEV_COMP(6) + + + + # a random dragon somewhere + MONSTER: 'D', random, random + # 3 out of 4 chance for a random trap in the EAST end + TRAP[75%]: random, (6,4) + # an electric eel below the SOUTH end + MONSTER: ';', "electric eel", (4,8) + # make the walls non-diggable + NON_DIGGABLE: (0,0,8,8) + TELEPORT_REGION: levregion(0,0,79,20), (0,0,8,8) + + This example will produce a file named "fortress" that can + be integrated into one of the numerous mazes of the game. + + Note especially the final, TELEPORT_REGION specification. + This says that level teleports or other non-stairway + arrivals on this level can land anywhere on the level except + the area of the map. This shows the use of the ``levre- + gion'' prefix allowed in certain region specifications. + Normally, regions apply only to the most recent MAP specifi- + cation, but when prefixed with ``levregion'', one can refer + to any area of the level, regardless of the placement of the + current MAP in the level. + +AUTHOR + Jean-Christophe Collet, David Cohrs. + +SEE ALSO + dgn_comp(6), nethack(6) + +BUGS + Probably infinite. Most importantly, still needs additional + bounds checking. + + + + + + + + + + + + + + + + + + + + + + + +May Last change: 16 11 + + + diff --git a/doc/nethack.6 b/doc/nethack.6 new file mode 100644 index 0000000..7bc2f94 --- /dev/null +++ b/doc/nethack.6 @@ -0,0 +1,305 @@ +.TH NETHACK 6 "9 August 2002" +.UC 4 +.SH NAME +nethack \- Exploring The Mazes of Menace +.SH SYNOPSIS +.na +.hy 0 +.B nethack +[ +.B \-d +.I directory +] +[ +.B \-n +] +[ +.B \-p +.I profession (role) +] +[ +.B \-r +.I race +] +[ +.B \-[DX] +] +[ +.B \-u +.I playername +] +[ +.B \-dec +] +[ +.B \-ibm +] +.PP +.B nethack +[ +.B \-d +.I directory +] +.B \-s +[ +.B \-v +] +[ +.B \-p +.I profession (role) +] +[ +.B \-r +.I race +] +[ +.I playernames +] +.ad +.hy 14 +.SH DESCRIPTION +.PP +.I NetHack +is a display oriented Dungeons & Dragons(tm) - like game. +The standard tty display and command structure resemble rogue. +.PP +Other, more graphical display options exist if you are using either a PC, +or an X11 interface. +.PP +To get started you really only need to know two commands. The command +.B ? +will give you a list of the available commands (as well as other information) +and the command +.B / +will identify the things you see on the screen. +.PP +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. +Nobody has achieved this yet; anybody who does will probably go down +in history as a hero among heros. +.PP +When the game ends, whether by your dying, quitting, or escaping +from the caves, +.I NetHack +will give you (a fragment of) the list of top scorers. +The scoring is based on many aspects of your behavior, but a rough estimate +is obtained by taking the amount of gold you've found in the cave plus four +times your (real) experience. +Precious stones may be worth a lot of gold when brought to the exit. +There is a 10% penalty for getting yourself killed. +.PP +The environment variable NETHACKOPTIONS can be used to initialize many +run-time options. +The ? command provides a description of these options and syntax. +(The +.B \-dec +and +.B \-ibm +command line options are equivalent to the +.B decgraphics +and +.B ibmgraphics +run-time options described there, +and are provided purely for convenience on systems +supporting multiple types of terminals.) +.PP +Because the option list can be very long (particularly when specifying +graphics characters), options may also be included in a configuration +file. +The default is located in your home directory and +named .nethackrc on Unix systems. On other systems, the default may be +different, usually NetHack.cnf. On DOS or Windows, the name is +defaults.nh, while on the Macintosh or BeOS, it is NetHack Defaults. +The configuration file's location may be specified by setting NETHACKOPTIONS +to a string consisting of an @ character followed by the filename. +.PP +The +.B \-u +.I playername +option supplies the answer to the question "Who are you?". +It overrides any name from the options or configuration file, USER, LOGNAME, +or getlogin(), which will otherwise be tried in order. +If none of these provides a useful name, the player will be asked for one. +Player names (in conjunction with uids) are used to identify save files, +so you can have several saved games under different names. +Conversely, you must use the appropriate player name to restore a saved game. +.PP +A +.I playername +suffix can be used to specify the profession, race, alignment and/or gender +of the character. The full syntax of the playername that includes a +suffix is "name-ppp-rrr-aaa-ggg". "ppp" are at least the first three letters +of the profession (this can also be specified using a separate +.B \-p +.I profession +option). "rrr" are at least the first three letters of the character's +race (this can also be specified using a separate +.B \-r +.I race +option). "aaa" are at last the first three letters of the character's +alignment, and "ggg" are at least the first three letters of the +character's gender. Any of the parts of the suffix may be left out. +.PP +.B \-p +.I profession +can be used to determine the character role. You can specify either the +male or female name for the character role, or the first three characters +of the role as an abbreviation. +.B "\-p \@" +has been retained to explicitly request that a random role be chosen. +It may need to be quoted with a backslash (\\@) if @ +is the "kill" character (see "stty") for the terminal, in order +to prevent the current input line from being cleared. +.PP +Likewise, +.B \-r +.I race +can be used to explicitly request that a race be chosen. +.PP +Leaving out any of these characteristics will result in you being prompted +during the game startup for the information. +.PP +.PP +The +.B \-s +option alone will print out the list of your scores on the current version. +An immediately following +.B \-v +reports on all versions present in the score file. +The +.B \-s +may also be followed by arguments +.B \-p +and +.B \-r +to print the scores of particular roles and races only. +It may also be followed by one or more player names to print the scores of the +players mentioned, by 'all' to print out all scores, or by a number to print +that many top scores. +.PP +The +.B \-n +option suppresses printing of any news from the game administrator. +.PP +The +.B \-D +or +.B \-X +option will start the game in a special non-scoring discovery mode. +.B \-D +will, if the player is the game administrator, start in debugging (wizard) +mode instead. +.PP +The +.B \-d +option, which must be the first argument if it appears, +supplies a directory which is to serve as the playground. +It overrides the value from NETHACKDIR, HACKDIR, +or the directory specified by the game administrator during compilation +(usually /usr/games/lib/nethackdir). +This option is usually only useful to the game administrator. +The playground must contain several auxiliary files such as help files, +the list of top scorers, and a subdirectory +.I save +where games are saved. +.SH AUTHORS +.PP +Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) wrote the +original hack, very much like rogue (but full of bugs). +.PP +Andries Brouwer continuously deformed their sources into an entirely +different game. +.PP +Mike Stephenson has continued the perversion of sources, adding various +warped character classes and sadistic traps with the help of many strange +people who reside in that place between the worlds, the Usenet Zone. +A number of these miscreants are immortalized in the historical +roll of dishonor and various other places. +.PP +The resulting mess is now called NetHack, to denote its +development by the Usenet. Andries Brouwer has made this request for the +distinction, as he may eventually release a new version of his own. +.SH FILES +.PP +All files are in the playground, normally /usr/games/lib/nethackdir. +If DLB was defined during the compile, the data files and special levels +will be inside a larger file, normally nhdat, instead of being separate +files. +.br +.DT +.ta \w'cmdhelp, opthelp, wizhelp\ \ \ 'u +nethack The program itself. +.br +data, oracles, rumors Data files used by NetHack. +.br +options, quest.dat More data files. +.br +help, hh Help data files. +.br +cmdhelp, opthelp, wizhelp More help data files. +.br +*.lev Predefined special levels. +.br +dungeon Control file for special levels. +.br +history A short history of NetHack. +.br +license Rules governing redistribution. +.br +record The list of top scorers. +.br +logfile An extended list of games +.br + played. +.br +xlock.nnn Description of a dungeon level. +.br +perm Lock file for xlock.dd. +.br +bonesDD.nn Descriptions of the ghost and +.br + belongings of a deceased +.br + adventurer. +.br +save A subdirectory containing the +.br + saved games. +.SH ENVIRONMENT +.DT +.ta \w'HACKPAGER or PAGER\ \ \ 'u +USER or LOGNAME Your login name. +.br +HOME Your home directory. +.br +SHELL Your shell. +.br +TERM The type of your terminal. +.br +HACKPAGER or PAGER Replacement for default pager. +.br +MAIL Mailbox file. +.br +MAILREADER Replacement for default reader +.br + (probably /bin/mail or /usr/ucb/mail). +.br +NETHACKDIR Playground. +.br +NETHACKOPTIONS String predefining several NetHack +.br + options. +.br + +In addition, SHOPTYPE is used in debugging (wizard) mode. +.SH "SEE ALSO" +.PP +dgn_comp(6), lev_comp(6), recover(6) +.SH BUGS +.PP +Probably infinite. + + +.PP +Dungeons & Dragons is a Trademark of Wizards of the Coast, Inc. diff --git a/doc/nethack.txt b/doc/nethack.txt new file mode 100644 index 0000000..15a11f9 --- /dev/null +++ b/doc/nethack.txt @@ -0,0 +1,208 @@ +NETHACK(6) NETHACK(6) + + + +NAME + nethack - Exploring The Mazes of Menace + +SYNOPSIS + nethack [ -d directory ] [ -n ] [ -p profession (role) ] [ + -r race ] [ -[DX] ] [ -u playername ] [ -dec ] [ -ibm ] + + nethack [ -d directory ] -s [ -v ] [ -p profession (role) + ] [ -r race ] [ playernames ] + +DESCRIPTION + NetHack is a display oriented Dungeons & Dragons(tm) - + like game. The standard tty display and command structure + resemble rogue. + + Other, more graphical display options exist if you are + using either a PC, or an X11 interface. + + To get started you really only need to know two commands. + The command ? will give you a list of the available com- + mands (as well as other information) and the command / + will identify the things you see on the screen. + + To win the game (as opposed to merely playing to beat + other people's high scores) you must locate the Amulet of + Yendor which is somewhere below the 20th level of the dun- + geon and get it out. Nobody has achieved this yet; any- + body who does will probably go down in history as a hero + among heros. + + When the game ends, whether by your dying, quitting, or + escaping from the caves, NetHack will give you (a fragment + of) the list of top scorers. The scoring is based on many + aspects of your behavior, but a rough estimate is obtained + by taking the amount of gold you've found in the cave plus + four times your (real) experience. Precious stones may be + worth a lot of gold when brought to the exit. There is a + 10% penalty for getting yourself killed. + + The environment variable NETHACKOPTIONS can be used to + initialize many run-time options. The ? command provides + a description of these options and syntax. (The -dec and + -ibm command line options are equivalent to the decgraph- + ics and ibmgraphics run-time options described there, and + are provided purely for convenience on systems supporting + multiple types of terminals.) + + Because the option list can be very long (particularly + when specifying graphics characters), options may also be + included in a configuration file. The default is located + in your home directory and named .nethackrc on Unix sys- + tems. On other systems, the default may be different, + usually NetHack.cnf. On DOS or Windows, the name is + defaults.nh, while on the Macintosh or BeOS, it is NetHack + Defaults. The configuration file's location may be speci- + fied by setting NETHACKOPTIONS to a string consisting of + an @ character followed by the filename. + + The -u playername option supplies the answer to the ques- + tion "Who are you?". It overrides any name from the + options or configuration file, USER, LOGNAME, or getlo- + gin(), which will otherwise be tried in order. If none of + these provides a useful name, the player will be asked for + one. Player names (in conjunction with uids) are used to + identify save files, so you can have several saved games + under different names. Conversely, you must use the + appropriate player name to restore a saved game. + + A playername suffix can be used to specify the profession, + race, alignment and/or gender of the character. The full + syntax of the playername that includes a suffix is "name- + ppp-rrr-aaa-ggg". "ppp" are at least the first three let- + ters of the profession (this can also be specified using a + separate -p profession option). "rrr" are at least the + first three letters of the character's race (this can also + be specified using a separate -r race option). "aaa" are + at last the first three letters of the character's align- + ment, and "ggg" are at least the first three letters of + the character's gender. Any of the parts of the suffix + may be left out. + + -p profession can be used to determine the character role. + You can specify either the male or female name for the + character role, or the first three characters of the role + as an abbreviation. -p @ has been retained to explicitly + request that a random role be chosen. It may need to be + quoted with a backslash (\@) if @ is the "kill" character + (see "stty") for the terminal, in order to prevent the + current input line from being cleared. + + Likewise, -r race can be used to explicitly request that a + race be chosen. + + Leaving out any of these characteristics will result in + you being prompted during the game startup for the infor- + mation. + + + The -s option alone will print out the list of your scores + on the current version. An immediately following -v + reports on all versions present in the score file. The -s + may also be followed by arguments -p and -r to print the + scores of particular roles and races only. It may also be + followed by one or more player names to print the scores + of the players mentioned, by 'all' to print out all + scores, or by a number to print that many top scores. + + The -n option suppresses printing of any news from the + game administrator. + + The -D or -X option will start the game in a special non- + scoring discovery mode. -D will, if the player is the + game administrator, start in debugging (wizard) mode + instead. + + The -d option, which must be the first argument if it + appears, supplies a directory which is to serve as the + playground. It overrides the value from NETHACKDIR, HACK- + DIR, or the directory specified by the game administrator + during compilation (usually /usr/games/lib/nethackdir). + This option is usually only useful to the game administra- + tor. The playground must contain several auxiliary files + such as help files, the list of top scorers, and a subdi- + rectory save where games are saved. + +AUTHORS + Jay Fenlason (+ Kenny Woodland, Mike Thome and Jon Payne) + wrote the original hack, very much like rogue (but full of + bugs). + + Andries Brouwer continuously deformed their sources into + an entirely different game. + + Mike Stephenson has continued the perversion of sources, + adding various warped character classes and sadistic traps + with the help of many strange people who reside in that + place between the worlds, the Usenet Zone. A number of + these miscreants are immortalized in the historical roll + of dishonor and various other places. + + The resulting mess is now called NetHack, to denote its + development by the Usenet. Andries Brouwer has made this + request for the distinction, as he may eventually release + a new version of his own. + +FILES + All files are in the playground, normally + /usr/games/lib/nethackdir. If DLB was defined during the + compile, the data files and special levels will be inside + a larger file, normally nhdat, instead of being separate + files. + nethack The program itself. + data, oracles, rumors Data files used by NetHack. + options, quest.dat More data files. + help, hh Help data files. + cmdhelp, opthelp, wizhelp More help data files. + *.lev Predefined special levels. + dungeon Control file for special lev- + els. + history A short history of NetHack. + license Rules governing redistribu- + tion. + record The list of top scorers. + logfile An extended list of games + played. + xlock.nnn Description of a dungeon + level. + perm Lock file for xlock.dd. + bonesDD.nn Descriptions of the ghost and + belongings of a deceased + adventurer. + save A subdirectory containing the + saved games. + +ENVIRONMENT + USER or LOGNAME Your login name. + HOME Your home directory. + SHELL Your shell. + TERM The type of your terminal. + HACKPAGER or PAGER Replacement for default pager. + MAIL Mailbox file. + MAILREADER Replacement for default reader + (probably /bin/mail or + /usr/ucb/mail). + NETHACKDIR Playground. + NETHACKOPTIONS String predefining several NetHack + options. + + In addition, SHOPTYPE is used in debugging (wizard) mode. + +SEE ALSO + dgn_comp(6), lev_comp(6), recover(6) + +BUGS + Probably infinite. + + + + Dungeons & Dragons is a Trademark of Wizards of the Coast, + Inc. + + + + 9 August 2002 NETHACK(6) diff --git a/doc/recover.6 b/doc/recover.6 new file mode 100644 index 0000000..d813264 --- /dev/null +++ b/doc/recover.6 @@ -0,0 +1,116 @@ +.TH RECOVER 6 "9 January 1993" +.UC 4 +.SH NAME +recover \- recover a NetHack game interrupted by disaster +.SH SYNOPSIS +.B recover +[ +.B \-d +.I directory +] +.I "base1 base2" ... +.SH DESCRIPTION +.PP +Occasionally, a NetHack game will be interrupted by disaster +when the game or the system crashes. +Prior to NetHack v3.1, these games were lost because various information +like the player's inventory was kept only in memory. +Now, all pertinent information can be written out to disk, +so such games can be recovered at the point of the last level change. +.PP +The +.I base +options tell +.I recover +which files to process. +Each base option specifies recovery of a separate game. +.PP +The +.B \-d +option, which must be the first argument if it appears, +supplies a directory which is the NetHack playground. +It overrides the value from NETHACKDIR, HACKDIR, or the directory +specified by the game administrator during compilation +(usually /usr/games/lib/nethackdir). +.PP +For recovery to be possible, +.I nethack +must have been compiled with the INSURANCE option, and the run-time option +.I checkpoint +must also have been on. +NetHack normally writes out files for levels as the player leaves them, +so they will be ready for return visits. +When checkpointing, NetHack also writes out the level entered and +the current game state on every level change. +This naturally slows level changes down somewhat. +.PP +The level file names are of the form base.nn, where nn is an internal +bookkeeping number for the level. +The file base.0 is used for game identity, locking, and, when checkpointing, +for the game state. +Various OSes use different strategies for constructing the base name. +Microcomputers use the character name, possibly truncated and modified +to be a legal filename on that system. +Multi-user systems use the (modified) character name prefixed +by a user number to avoid conflicts, +or "xlock" if the number of concurrent players is being limited. +It may be necessary to look in the playground to find the correct +base name of the interrupted game. +.I recover +will transform these level files into a save file of the same name as +.I nethack +would have used. +.PP +Since +.I recover +must be able to read and delete files from the playground +and create files in the save directory, +it has interesting interactions with game security. +Giving ordinary players access to +.I recover +through setuid or setgid is tantamount to leaving the playground +world-writable, +with respect to both cheating and messing up other players. +For a single-user system, this of course does not change anything, +so some of the microcomputer ports install +.I recover +by default. +.PP +For a multi-user system, +the game administrator may want to arrange for all .0 files in the +playground to be fed to recover when the host machine boots, +and handle game crashes individually. +If the user population is sufficiently trustworthy, +.I recover +can be installed with the same permissions the +.I nethack +executable has. +In either case, +.I recover +is easily compiled from the distribution utility directory. +.SH NOTES +.PP +Like +.I nethack +itself, +.I recover +will overwrite existing savefiles of the same name. +Savefiles created by +.I recover +are uncompressed; +they may be compressed afterwards if desired, +but even a compression-using +.I nethack +will find them in the uncompressed form. +.SH "SEE ALSO" +nethack(6) +.SH BUGS +.PP +.I recover +makes no attempt to find out if a base name specifies a game in progress. +If multiple machines share a playground, this would be impossible to +determine. +.PP +.I recover +should be taught to use the nethack playground locking mechanism to +avoid conflicts. diff --git a/doc/recover.txt b/doc/recover.txt new file mode 100644 index 0000000..80eeadb --- /dev/null +++ b/doc/recover.txt @@ -0,0 +1,132 @@ + + + +RECOVER(6) 1993 RECOVER(6) + + + +NAME + recover - recover a NetHack game interrupted by disaster + +SYNOPSIS + recover [ -d directory ] base1 base2 ... + +DESCRIPTION + Occasionally, a NetHack game will be interrupted by disaster + when the game or the system crashes. Prior to NetHack v3.1, + these games were lost because various information like the + player's inventory was kept only in memory. Now, all per- + tinent information can be written out to disk, so such games + can be recovered at the point of the last level change. + + The base options tell recover which files to process. Each + base option specifies recovery of a separate game. + + The -d option, which must be the first argument if it + appears, supplies a directory which is the NetHack play- + ground. It overrides the value from NETHACKDIR, HACKDIR, or + the directory specified by the game administrator during + compilation (usually /usr/games/lib/nethackdir). + + For recovery to be possible, nethack must have been compiled + with the INSURANCE option, and the run-time option check- + point must also have been on. NetHack normally writes out + files for levels as the player leaves them, so they will be + ready for return visits. When checkpointing, NetHack also + writes out the level entered and the current game state on + every level change. This naturally slows level changes down + somewhat. + + The level file names are of the form base.nn, where nn is an + internal bookkeeping number for the level. The file base.0 + is used for game identity, locking, and, when checkpointing, + for the game state. Various OSes use different strategies + for constructing the base name. Microcomputers use the + character name, possibly truncated and modified to be a + legal filename on that system. Multi-user systems use the + (modified) character name prefixed by a user number to avoid + conflicts, or "xlock" if the number of concurrent players is + being limited. It may be necessary to look in the play- + ground to find the correct base name of the interrupted + game. recover will transform these level files into a save + file of the same name as nethack would have used. + + Since recover must be able to read and delete files from the + playground and create files in the save directory, it has + interesting interactions with game security. Giving ordi- + nary players access to recover through setuid or setgid is + tantamount to leaving the playground world-writable, with + respect to both cheating and messing up other players. For + + + +January Last change: 9 1 + + + + + + +RECOVER(6) 1993 RECOVER(6) + + + + a single-user system, this of course does not change any- + thing, so some of the microcomputer ports install recover by + default. + + For a multi-user system, the game administrator may want to + arrange for all .0 files in the playground to be fed to + recover when the host machine boots, and handle game crashes + individually. If the user population is sufficiently + trustworthy, recover can be installed with the same permis- + sions the nethack executable has. In either case, recover + is easily compiled from the distribution utility directory. + +NOTES + Like nethack itself, recover will overwrite existing save- + files of the same name. Savefiles created by recover are + uncompressed; they may be compressed afterwards if desired, + but even a compression-using nethack will find them in the + uncompressed form. + +SEE ALSO + nethack(6) + +BUGS + recover makes no attempt to find out if a base name speci- + fies a game in progress. If multiple machines share a play- + ground, this would be impossible to determine. + + recover should be taught to use the nethack playground lock- + ing mechanism to avoid conflicts. + + + + + + + + + + + + + + + + + + + + + + + + + + +January Last change: 9 2 + + + diff --git a/doc/tmac.n b/doc/tmac.n new file mode 100644 index 0000000..1c8f3f0 --- /dev/null +++ b/doc/tmac.n @@ -0,0 +1,764 @@ +\" @(#)$Id: tmac.n,v 1.3 2000/02/02 18:49:05 dean Exp $ +.\" The News macro package +.\" +.\" This is the macro package that is used to format news documents. It +.\" was written because many sites do not have one of the -mm or -ms pack- +.\" ages that the documents use. This is NOT compatible with EITHER, but +.\" (I hope) will become the standard for all news documents (man pages +.\" excepted, since everyone seems to have -man.) +.\" +.\" This package was written using only the "NROFF/TROFF Users' Guide", +.\" and therefore if you can run NROFF/TROFF, you can legitimately use +.\" this package. However, because NROFF/TROFF are proprietary programs, +.\" I cannot place this package in the public domain. This should not +.\" matter, because if you legitimately have NROFF/TROFF, you have the +.\" documentation; if not, you can't run off the documentation anyway. +.\" +.\" This package may be circulated freely with the news documentation; it +.\" may not be sold, but is to be distributed with the unformatted news +.\" documents. However, the name of the author and the place at which it +.\" was written (in the author's own time, of course) are not to be +.\" removed from the package regardless of how it is modified or altered. +.\" Further, please do not distribute this package if you make any changes +.\" because I don't want to get bug reports of macros I haven't written; +.\" if you have a goodie you want me to add, send it to me and we'll talk. +.\" (I really do like feedback!) I'd really appreciate your cooperation. +.\" +.\" Author: Matt Bishop +.\" Research Institute for Advanced Computer Science +.\" Mail Stop 230-5 +.\" NASA Ames Research Center +.\" Moffett Field, CA 94035 +.\" +.\" version 1.0 September 28, 1985 mab@riacs.arpa +.\" initial version +.\" version 1.1 October 25, 1985 mab@riacs.arpa +.\" fixed an incredibly obscure footnote bug (that occurred twice in +.\" the news documentation!) which put footnoted words on one page +.\" and the footnote on the next if the word was in the next-to-last +.\" or last line; commented it, and generally cleaned up +.\" Version 1.2 October 27, 1985 mab@riacs.arpa +.\" Added a few more comments and a check to keep footnotes lined up +.\" with the bottom margin. +.\" Version 1.3 February 12, 1986 mab@riacs.arpa +.\" Added an error check to catch unmatched ef's and ed's +.\" Version 1.4 December 29, 1986 mab@riacs.edu +.\" Changed footnote for ux, pd, and vx macros and added a string +.\" for rg ("Registered Trademark") +.\" Version 1.5 January 2, 1989 Matt.Bishop@dartmouth.edu +.\" Minor modifications for nroff compatibility +.\" Version 1.6 March 15, 1989 Matt.Bishop@dartmouth.edu +.\" ..!bear.dartmouth.edu!bishop +.\" Fixed a bug in footnote handling (again, sigh ...) This one +.\" occurred when the the "fo" trap position was reset just beneath +.\" the current line; the footnote overflow trap would kick in and +.\" never be closed. +.\" +.\" +.\" ********** +.\" these preserve and restore various things +.\" they are used to shorten other macros +.de yf \" restore fonts +.ft \\n(f2 \" previous font +.ft \\n(f1 \" current font +.. +.de yi \" restore indents +'in \\n(i2u \" previous indent +'in \\n(i1u \" current indent +.. +.de ys \" restore point sizes +.ps \\n(s2 \" previous point size +.ps \\n(s1 \" current point size +.. +.de yv \" restore vertical spacings +.vs \\n(v2u \" previous vertical spacing +.vs \\n(v1u \" current vertical spacing +.. +.de ya \" restore everything +.yf \" restore fonts +.yi \" restore indents +.ys \" restore point sizes +.yv \" restore vertical spacing +.. +.de zf \" preserve fonts +.nr f1 \\n(.f \" current font +.ft \" switch to previous font +.nr f2 \\n(.f \" previous font +.ft \" back to current font +.. +.de zi \" preserve indents +.nr i1 \\n(.iu \" current indent +'in \" switch to previous indent +.nr i2 \\n(.iu \" previous indent +'in \" back to current indent +.. +.de zs \" preserve point sizes +.nr s1 \\n(.su \" current point size +.ps \" switch to previous point size +.nr s2 \\n(.su \" previous point size +.ps \" back to current point size +.. +.de zv \" preserve vertical spacings +.nr v1 \\n(.vu \" current vertical spacing +.vs \" switch to previous vertical spacing +.nr v2 \\n(.vu \" previous vertical spacing +.vs \" back to current vertical spacing +.. +.de za \" save everything +.zf \" save fonts +.zi \" save indents +.zs \" save point sizes +.zv \" save vertical spacings +.. +.\" ********** +.\" these actually print the header and footer titles +.\" they are defined separately from the "hd" and "fo" macros +.\" to make user redefinition easy +.de pt \" print header title +. \" omit header on first page +.if \\n%>1 \{\ +' sp |\\$1u \" move to proper position +. ft 1 \" change to default font +. ps \\n(ps \" change to default point size +. vs \\n(vs \" change to default spacing +. tl '\\*(h0'\\*(h1'\\*(h2' \" center title +. vs \" restore current vertical spacing +. ps \" restore current point size +. ft \" restore current font +.\} +.. +.de pf \" print footer title +.ft 1 \" change to default font +.ps \\n(ps \" change to default point size +.vs \\n(vs \" change to default spacing +.ie \\n%=1 .tl '\\*(h0'\\*(h1'\\*(h2' \" on first page, print the header here +.el .tl '\\*(f0'\\*(f1'\\*(f2' \" on other pages, print the footer +.vs \" restore current vertical spacing +.ps \" restore current point size +.ft \" restore current font +.. +.\" ********** +.\" these are the top of page (header) and bottom of page (footer) macros +.\" they don't actually print anything, just call the right macros +.de hd \" header -- do top of page processing +.if t .if \\n(cm .tl '\(rn''' \" drop cut mark if needed +.pt \\n(ttu \" print header +.nr fc 0 1 \" init footnote count +.nr fs \\n(.pu-\\n(bmu-1u \" if any footnotes, start print here +.nr fp 0-\\n(bmu \" reset current footer place +.ch fo -\\n(bmu \" reset footer trap +.if \\n(dn .fz \" put leftover footnotes st bottom +.ya \" restore font, etc. +'sp |\\n(tmu \" move to top of body +.ns \" don't allow any more space +.. +.de fo \" footer -- do bottom of page processing +.za \" save font, etc. +.rs \" you want motions here +.nr dn 0 \" clobber diversion size register +.if \\n(fc .fd \" now print the footnotes, if any +'bp \" force out page +.. +.\" ********** +.\" these are the footnote macros +.\" here's an overview: +.\" Footnotes are processed in environment #1, which is initialized +.\" at the bottom of this package. When "fn" is called, nroff/troff +.\" switches to this environment. The body of the footnote is saved +.\" in the diversion "tf" (for "temporary footnote"), so you will +.\" NEVER spring a trap during the first reading of a footnote. When +.\" "ef" ("end footnote") is called, the diversion is closed. If +.\" this is the first footnote on the page (ie, the number register +.\" "fc" is 1), and the footnote height (plus the height of 1 line) +.\" crosses the bottom margin, you get the footnoted word on one +.\" page and the footnote on the other. In this case we just call +.\" "fo" manually (taking case it cannot be re-invoked on the same +.\" page!) If this situation does not occur, we just adjust the +.\" footer trap's position upwards (we'll get to how far in a min- +.\" ute); if this puts the trap above the current line, we reposi- +.\" tion the trap just beneath the current line to be sure of trig- +.\" triggering it once the current line is forced out. +.\" To reposition the footer trap, we proceed as follows. Because +.\" the trap may be sprung in the middle of a line, it is possible +.\" that the footnote will not fit on the page (regardless of where +.\" on the page the footnoted word occurs -- really!) if we move the +.\" trap up by the size of the footnote diversion "tf". So, we +.\" fudge things a little bit -- for the first footnote on each page +.\" we move the footer trap up 1 extra line ("line" being 1v in env- +.\" ironment #0). Unless the point size and vertical spacing are +.\" increased between the first footnote and the footer trap's being +.\" sprung, this will keep the footnotes on the same page as the +.\" footnoted word. But as there may be now as much as 1v of space +.\" between the footnote and the bottom margin, which looks HIDEOUS, +.\" we use the number register "fs" to mark where the footer trap +.\" would REALLY go, and just space to it when it comes time to put +.\" out the footnotes. +.de fd \" dump footnotes +.nr gs 1v \" get a measure of 1 line in env #0 +.ev 1 \" switch to footnote environment +.nr gs +2v \" min of 2 lines of footnotes +. \" if the number register ns > 0, +. \" the last text line may contain a +. \" footnote that is too big to fit; +. \" this checks for such a note and +. \" if so, forces the footnote into +. \" the "fy" diversion that carries +. \" it onto the next text page +.ie (\\n(nsu>0)&(\\n(gsu>=\\n(.tu) 'sp \\n(gsu \" be sure you can get it down +.el .if \\n(fsu>\\n(nlu 'sp \\n(fsu-\\n(nlu \" move to footnote start position +'nf \" don't reprocess footnotes +'in 0 \" don't indent them any more either +.tf \" drop text of footnotes +.rm tf +.if '\\n(.z'fy' .di \" end overflow diversion, if any +.nr fc 0 \" re-init footnote count +.ev \" return to usual environment +.. +.de fn \" start footnote +. \" look for nested footnotes -- ILLEGAL +.ie \\n(if>0 .er "footnote within footnote" +.el .da tf \" append footnote to footnote diversion +.nr if +1 \" increment level of footnoting +.nr fc +1 \" one more footnote on this page +.if \\n(fc=1 .nr fp -1v \" The reason for this "fudge factor" +. \" is that there is no way to force +. \" NROFF/TROFF to invoke a macro at +. \" the end of each line. At times, +. \" the trap boundary will not match up +. \" with the bottom of a line, so the +. \" "fo" trap which is set at 2320 may +. \" not be triggered until 2340 -- and +. \" then the footnote won't fit. This +. \" gives some slack so the footnote is +. \" more likely to fit. *sigh* +.ev 1 \" enter footnote environment +.if \\n(fc=1 .fs \" drop separator if first footnote +.br \" flush out any previous line in footnote +.fi \" process footnote in fill mode +.. +.de ef \" end footnote +.br \" flush out the line in footnote +.ie \\n(if<=0 .er "end footnote has no corresponding begin footnote" +.el \{\ +. nr if -1 \" decrement level of footnoting +. nr fg 2v \" remember this for repositioning fo +. ev \" back to usual environment +. if \\n(if=0 \{\ +. di \" end of footnote proper +. nr fp -\\n(dnu \" "fo" will be moved at least up this far +. nr fs -\\n(dnu \" increase size of footnote +. ch fo \\n(fpu \" reposition "fo" trap (first guess) +. \" the first part of the "ie" clause +. \" is taken in the special case +. \" described above +. ie (\\n(fc=1)&((\\n(nlu+1v+\\n(fgu)>=(\\n(.pu-\\n(bmu)) \{\ +. nr ns \\n(dnu \" suppress footnote separator +. \" since this footnote contains it +. \" keep "fo" from being invoked twice +. ch fo \\n(.pu+1i +. fo \" force the page out AT ONCE +. nr ns 0 \" re-enable footnote separator +. \} +. \" footnote won't fit completely +. \" invoke the footer trap but +. \" don't worry about the footnote +. \" separator (it's already there) +. el .if (\\n(nlu+1v)>=(\\n(.pu+\\n(fpu) \{\ +. \" as before we must reposition the +. \" "fo" trap to prevent "fo" from +. \" being invoked twice +. ch fo \\n(.pu+1i +. fo \" force the page out AT ONCE +. \} +. \} +.\} +.. +.de fs \" drop footnote separator +. \" only if not already dropped +.if \\n(ns=0 \l'1i' +.nr ns 0 \" in case footnotes are over 1 page long +.. +.de fx \" process footnote overflow +.if \\n(fc .di fy \" stuff them in the right place +.. +.de fz \" deposit footnote overflow +.fn \" treat it as a footnote +.nf \" it's already been processed +.in 0 \" and indented +.fy \" "fx" put it here +.ef \" end the footnote +.. +.\" ********** +.\" the ones after here are user-invoked (like "fn" and "ef" above) +.\" title, author, etc. +.de mt \" main title +\& +.sp |\\n(mtu \" space +.ft 3 \" in bold +.ps \\n(ps+2p \" large point size and +.vs \\n(vs+2p \" vertical spacing +.ce 1000 \" center the title +.nr t2 1 \" space it +.. +.de au \" author +.nr t2 0 \" spacing here +.sp 2v \" space +.ft 2 \" in italics +.ps \\n(ps \" usual point size and +.vs \\n(vs \" vertical spacing +.ce 1000 \" center the name(s) +.. +.de ai \" author's institution +.if \\n(t2 .sp 2v \" space after a title +.nr t2 0 \" institution +.ft 2 \" in italics +.ps \\n(ps \" usual point size and +.vs \\n(vs \" vertical spacing +.ce 1000 \" center the name(s) +.. +.de bt \" begin text macro +.nr t2 0 \" hold it here +.nr it +1 \" mark as called +.ce 0 \" end any centering +.sn 3v \" a little bit of space +.. +.\" paragraph +.de si \" start indented section +.nr lo \\n(lm \" remember the current level +.nr lm +1 \" go to the next level +.ie '\\$1'' .nr l\\n(lm \\n(l\\n(lo+5n \" if no arg, indent 5n +.el .nr l\\n(lm \\$1n \" otherwise, indent that much +.. +.de ei \" end indent +.nr lm -1 \" down one level +.if \\n(lm<0 .nr lm 0 \" make sure you don't go too far +.. +.de pg \" plain old paragraph +.if !\\n(it .bt \" end the title and such +.sn \\n(pdu \" inter-paragraph spacing +.ft 1 \" reset a few things (paranoia) +. \" these ONLY if not in footnote +.ie \\n(if=0 \{\ +. ps \\n(ps \" reset point size +. vs \\n(vs \" reset vertical spacing +. ne 1v+\\n(.Vu \" slightly more than 1 line +.\} +.el \{\ +. ps \\n(ps-2p \" reset point size +. vs \\n(vs-2p \" reset vertical spacing +.\} +.in \\n(l\\n(lmu \" stop any indenting +.ce 0 \" stop any centering +.if !'\\$1'L' .if !'\\$1'l' .ti +\\n(piu \" indent the sucker +.. +.de lp \" labelled paragraph +.pg l \" reset paragraph +.if \\n(.$>1 .nr li \\$2n \" if indent given use it +.in +\\n(liu \" indent for paragraph +.ti -\\n(liu \" force first line NOT to indent +.ta +\\n(liu \" for the label +\&\\$1\t\c +.if \\w'\\$1'u>=(\\n(l\\n(lmu+\\n(liu) .br \" don't overwrite +.. +.\" The following two macros (hu & hn) have been modified for ELM usage. +.\" If the macros have text as part of the macro call, the text will be +.\" increased in size by two points. After printing the text, the font +.\" will be returned to normal, otherwise the font will be left bold. +.\" +.\" section +.de hu \" header, unnumbered +. \" format: .hu [text] +.if !\\n(it .bt \" end the title and such +.br \" force out previous line +.b +.ie \\n(hP .ps \\n(hP +.el .ps \\n(ps +.ie \\n(hv .vs \\n(hv +.el .vs \\n(vs +.in \\n(l\\n(lmu \" stop any indenting +.sn \\n(hsu \" inter-section spacing +.ne 3v+\\n(.Vu \" slightly more than 3 lines +.fi \" process the text, too +.if \\n(.$>=1 \{\ +.ps +2 +\\$1 +.\} +.if \\n(.$>=2 \\$2 +.if \\n(.$>=3 \\$3 +.if \\n(.$>=4 \\$4 +.if \\n(.$>=5 \\$5 +.if \\n(.$>=6 \\$6 +.if \\n(.$>=7 \\$7 +.if \\n(.$>=8 \\$8 +.if \\n(.$=9 \\$9 +.if \\n(.$>=1 \{\ +.ps -2 +.br +.ft 1 +.\} +.. +.de hn \" header, numbered +. \" format: .hn [level] [text] +.if !\\n(it .bt \" end the title and such +.br \" force out previous line +.b +.ie \\n(hP .ps \\n(hP +.el .ps \\n(ps +.ie \\n(hv .vs \\n(hv +.el .vs \\n(vs +.in \\n(l\\n(lmu \" stop any indenting +.sn \\n(hsu \" inter-section spacing +.ne 3v+\\n(.Vu \" slightly more than 3 lines +.fi \" process the text, too +.ie !'\\$1'' .nr hn \\$1 +.el .nr hn 1 +.ie \\n(hn>0 .nr hn -1 +.el .nr hn 0 +.ie \\n(hn=0 \{\ +. nr h0 +1 \" add 1 to main section header +. nr h1 0 \" zap remaining section numbers +. nr h2 0 \" zap remaining section numbers +. nr h3 0 \" zap remaining section numbers +.ie \\n(.$>=2 \{\ +.ps +2 +\\n(h0. +.ps -2 +.\} +.el \\n(h0. +.\} +.el .ie \\n(hn=1 \{\ +. nr h1 +1 \" add 1 to the section header +. nr h2 0 \" zap remaining section numbers +. nr h3 0 \" zap remaining section numbers +.ie \\n(.$>=2 \{\ +.ps +2 +\\n(h0.\\n(h1. +.ps -2 +.\} +.el \\n(h0.\\n(h1. +.\} +.el .ie \\n(hn=2 \{\ +. nr h2 +1 \" add 1 to the section header +. nr h3 0 \" zap remaining section numbers +.ie \\n(.$>=2 \{\ +.ps +2 +\\n(h0.\\n(h1.\\n(h2. +.ps -2 +.\} +.el \\n(h0.\\n(h1.\\n(h2. +.\} +.el \{\ +. nr h3 +1 \" add 1 to the section number +.ie \\n(.$>=2 \{\ +.ps +2 +\\n(h0.\\n(h1.\\n(h2.\\n(h3. +.ps -2 +.\} +.el \\n(h0.\\n(h1.\\n(h2.\\n(h3. +.\} +.if \\n(.$>=2 \{\ +.ps +2 +\\$2 +.\} +.if \\n(.$>=3 \\$3 +.if \\n(.$>=4 \\$4 +.if \\n(.$>=5 \\$5 +.if \\n(.$>=6 \\$6 +.if \\n(.$>=7 \\$7 +.if \\n(.$>=8 \\$8 +.if \\n(.$>=9 \\$9 +.if \\n(.$>=2 \{\ +.br +.ft 1 +.ps -2 +.\} +.. +.\" displays (no floats, thank God!) +.de sd \" start display +. \" look for nested displays -- ILLEGAL +.ie \\n(id>0 .er "display within display" +.el \{\ +. ie '\\$1'c' .nr sf 1 \" center the sucker +. el .nr sf 0 \" don't center it +.\} +.sn \\n(pdu \" a little bit of space +.ev 2 \" switch to display environment +.nf \" what you type is what you get +.if \\n(id=0 .di dd \" start saving text +.rs \" don't eat leading space +.nr id +1 \" increment level of display +.. +.de ed \" end display +.br \" flush line +.ie \\n(id<=0 .er "end display has no corresponding begin display" +.el \{\ +. nr id -1 \" decrement level of display +. if \\n(id=0 \{\ +. di \" end diversion +. fi \" resume filling +. in -\\n(piu \" dedent +. ev \" pop environment +. ne \\n(dnu \" be sure you have room +. nf \" don't reprocess display +. rs \" don't eat leading space +. zi \" save indents +. ie \\n(sf .in (\\n(llu-\\n(dlu)/2u \" center on the line length +. el .in +\\n(piu \" indent the sucker +. dd \" drop display +. yi \" restore indents +. \} +.\} +.fi \" resume filling +.sn \\n(pdu \" a little bit of space +.. +.\" ********** +.\" fonts -- if argument(s), apply only to first +.de b \" bold (font 3) +.ie \\n(.$>0 \\&\\$3\\f3\\$1\\fP\\$2 +.el .ft 3 +.. +.de i \" italics (font 2) +.ie \\n(.$>0 \\&\\$3\\f2\\$1\\fP\\$2 +.el .ft 2 +.. +.de r \" roman (font 1) +.ft 1 \" just restore it +.. +.de bi \" bold italics (embolden font 2) +\\&\\$3\c +\\kb\\f2\\$1\\fP\\h'|\\nbu+2u'\\f2\\$1\\fP\\$2 +.. +.\" ********** +.\" point sizes -- if argument(s), apply only to first +.de sm \" reduce point size by 2 +.ie \\n(.$>0 \\&\\$3\\s-2\\$1\\s0\\$2 +.el .ps -2 +.. +.de is \" increase point size by 2 +.ie \\n(.$>0 \\&\\$3\\s+2\\$1\\s0\\$2 +.el .ps +2 +.. +.de nl \" return to normal size +.ps \\n(ps \" just reset the point size +.. +.\" ********** +.\" handy force space/inhibit more space macros +.de sn \" space, then turn on nospace mode +.sp \\$1 \" space +.ns \" ignore any more space requests +.. +.de sr \" force out space +.rs \" turn on spacing mode +.sp \\$1 \" space +.. +.\" ********** +.\" end of text and error macros +.de et \" end of text macro +. \" this: (1) flushes rest of line +. \" (2) trips the footer, taking +. \" care of footnotes +.sp \\n(.pu +. \" check for open displays or footnotes +.if \\n(id>0 .er "unfinished display" +.if \\n(if>0 .er "unfinished footnote" +. \" this one means an -mn bug (*sigh*) +.if !'\\n(.z'' .er "diversion \\n(.z not closed" +.. +.de er \" print error message +. \" flag it as an error +.ds ws "** ERROR ** +. \" if you have it, give the file name +.if !'\\*(.f'' .as ws " file \\*(.f, +. \" put out the line number +.as ws " line \\n(.c +. \" and finally the error message +.tm \\*(ws: \\$1 +.. +.\" ********** +.\" macros in this section are VERY specific to the news documentation +.de pa \" protocol appellation (darn names!) +\\&\\$3\\f2\\$1\\fP\\$2 +.. +.de ng \" news group name +\\&\\$3\\f3\\$1\\fP\\$2 +.. +.de cn \" computer name +\\&\\$3\\f2\\$1\\fP\\$2 +.. +.de hf \" header field +\\&\\$3\\*(lq\\$1\\*(rq\\$2 +.. +.de cf \" contents of field +\\&\\$3\\*(lq\\$1\\*(rq\\$2 +.. +.de qc \" quote control char (command) +\\&\\$3\\f3<\\s-2\\$1\\s0>\\fP\\$2 +.. +.de qp \" quote printing char (command) +\\&\\$3\\f3\\$1\\fP\\$2 +.. +.de op \" option +\\&\\$3\\f3\\$1\\fP\\$2 +.. +.\" ********** +.\" trademarked names +.de pd \" print "PDP-11" +.ie \\n(p1 \\&\\$2\\s-1PDP\\s0-11\\$1 +.el \{\ +. nr p1 +1 \" mark footnote as dropped +\\&\\$2\\s-1PDP\\s0-11\\*(rg\\$1 +. fn \" put out the footnote +\\&\\*(rgPDP-11 is a registered trademark of Digital Equipment Corporation. +. ef \" short and sweet ... +.\} +.. +.de ux \" print "UNIX" +.ie \\n(ux \\&\\$2\\s-1UNIX\\s0\\$1 +.el \{\ +. nr ux +1 \" mark footnote as dropped +\\&\\$2\\s-1UNIX\\s0\\*(rg\\$1 +. fn \" put out the footnote +\\&\\*(rgUNIX is a registered trademark of AT&T. +. ef \" short and sweet ... +.\} +.. +.de vx \" print "VAX" +.ie \\n(vx \\&\\$2\\s-1VAX\\s0\\$1 +.el \{\ +. nr vx +1 \" mark footnote as dropped +\\&\\$2\\s-1VAX\\s0\\*(rg\\$1 +. fn \" put out the footnote +\\&\\*(rgVAX is a trademark of Digital Equipment Corporation. +. ef \" short and sweet ... +.\} +.. +.\" ********** +.\" set up string and number registers +. \" set up for the date +.if \n(mo=1 .ds mo January +.if \n(mo=2 .ds mo February +.if \n(mo=3 .ds mo March +.if \n(mo=4 .ds mo April +.if \n(mo=5 .ds mo May +.if \n(mo=6 .ds mo June +.if \n(mo=7 .ds mo July +.if \n(mo=8 .ds mo August +.if \n(mo=9 .ds mo September +.if \n(mo=10 .ds mo October +.if \n(mo=11 .ds mo November +.if \n(mo=12 .ds mo December +.nr Yr \n(yr+1900 +.ds dy "\*(mo \n(dy, \n(Yr +.if \n(dw=1 .ds dw Sunday +.if \n(dw=2 .ds dw Monday +.if \n(dw=3 .ds dw Tuesday +.if \n(dw=4 .ds dw Wednesday +.if \n(dw=5 .ds dw Thursday +.if \n(dw=6 .ds dw Friday +.if \n(dw=7 .ds dw Saturday +. \" NROFF dependencies +.if n \{\ +. \" string registers +. ds rg (R) +. ds lq "" +. ds rq "" +. ds f1 "\*(dy +. \" number registers +. nr hs 1v \" space before section header +. nr pd 1v \" inter-paragraph spacing +. nr bm 1.0i \" height of bottom margin +.\} +. \" NROFF dependencies +.if t \{\ +. \" string registers +. ds rg \\u\\s-2\\(rg\\s0\\d +. ds lq `` +. ds rq '' +. \" number registers +. nr hs 1v \" space before section header +. nr pd 0.3v \" inter-paragraph spacing +. nr bm 1.0i+1v \" height of bottom margin (wacky laser) +.\} +. \" these are the same for [NT]ROFF +.ds dg \(dg +.ds vr "News Version B2.11 +.ds pv "News macros 1.5 +.ds h1 - % - +.nr bt 0.5i+1v \" bottom of page to footer +.nr cm 0 \" no cut marks +.nr fc 0 1 \" init footnote count +.nr fl 5.5i \" footnote line length +.nr fp 0-\n(bmu \" fo macro trap location +.nr h0 0 \" init section header level 0 +.nr h1 0 \" init section header level 1 +.nr h2 0 \" init section header level 2 +.nr h3 0 \" init section header level 3 +.nr id 0 \" 1 in display +.nr if 0 \" 1 in keep +.nr it 0 \" 1 when beyond title, etc. +.nr li 5n \" indent for labelled paragraph +.nr ll 6.5i \" line length +.nr lm 0 \" left margin +.nr l0 0 \" first indent level +.nr mt 1.5i+1v \" title goes down this far +.nr pi 5n \" regular paragraph indent +.nr po 1.0i \" page offset +.nr ps 10 \" point size +.nr tm 1.0i \" height of top margin +.nr tt 0.5i-0.5v \" top of page to header +.nr p1 0 \" no PDP-TM message yet +.nr ux 0 \" no UNIX-TM message yet +.nr vx 0 \" no VAX-TM message yet +.nr vs 12 \" vertical spacing +.\" set things up +.\" DSINC changes for XROFF +.nr f1 1 +.nr f2 1 +.nr s1 10 +.nr s2 10 +.nr v1 12 +.nr v2 12 +.ps 10 +.vs 12 +.\" DSINC end changes for XROFF +.po \n(pou \" set page offset +.ps \n(ps \" set previous, current +.ps \n(ps \" point sizes +.vs \n(vs \" set previous, current +.vs \n(vs \" vertical spacings +.ll \n(llu \" set line length +.lt \n(llu \" set title line length +.ev 1 \" *** footnote environment +.ps \n(ps-2p \" set previous, current +.ps \n(ps-2p \" point sizes +.vs \n(vs-2p \" set previous, current +.vs \n(vs-2p \" vertical spacings +.ll \n(flu \" set line length +.lt \n(flu \" set title line length +.ev \" *** pop environment +.ev 2 \" *** footnote environment +.ps \n(ps \" set previous, current +.ps \n(ps \" point sizes +.vs \n(vs \" set previous, current +.vs \n(vs \" vertical spacings +.ll \n(llu \" set line length +.lt \n(llu \" set title line length +.ev \" *** pop environment +.\" now set internal registers (for the first header section) +.nr f1 \n(.f \" saved font #1 +.nr f2 \n(.f \" saved font #2 +.nr s1 \n(.s \" saved point size #1 +.nr s2 \n(.s \" saved point size #2 +.nr v1 \n(.v \" saved vertical spacing #1 +.nr v2 \n(.v \" saved vertical spacing #2 +.\" install traps +.wh 0i hd \" position header trap +.wh -\n(bmu fo \" position footer trap +.wh \n(.pu+1i fx \" put footnote overflow trap here +.ch fx -\n(bmu \" move it over fo +.wh -\n(btu pf \" print the bottom margin here +.em et \" at end of file, call et +.\" couple of miscellaneous requests +.bd S 3 3 \" embolden special font chars if B +.hy 2 \" don't hyphenate last lines + diff --git a/doc/window.doc b/doc/window.doc new file mode 100644 index 0000000..cbfc571 --- /dev/null +++ b/doc/window.doc @@ -0,0 +1,805 @@ +Introduction + +This file documents the support for various windowing systems in +NetHack. The support is through a standard interface, separating the +main NetHack code from window-system specific code. The implementation +supports multiple window systems in the same binary. Even if you only +wish to support one window-port on your port, you will need to follow +the instructions in Section IX to get a compilable binary. + +Contents: + I. Window Types and Terminology + II. Interface Specification + III. Global variables + IV. WINCAP preferences support + V. New or respecified common, high level routines + VI. Helper routines + VII. Game startup + VIII. Conventions + IX. Implementation and Multi-window support + +I. Window Types and Terminology + +There are 5 basic window types, used to call create_nhwindow(): + + NHW_MESSAGE (top line) + NHW_STATUS (bottom lines) + NHW_MAP (main dungeon) + NHW_MENU (inventory or other "corner" windows) + NHW_TEXT (help/text, full screen paged window) + +The tty window-port also uses NHW_BASE (the base display) internally. + +NHW_MENU windows can be used for either menu or text display. Their +basic feature is that for the tty-port, if the window is small enough, +it appears in the corner of the tty display instead of overwriting +the whole screen. The first call to add information to the window +will decide if it is going to be used to display a menu or text. +If start_menu() is called, then it will be used as a menu. If +putstr() is called, it will be used as text. Once decided, there +is no turning back. For the tty-port, if the data is too large for +a single screen then the data is paged (with --more--) between pages. +Only NHW_MENU type windows can be used for menus. + +NHW_TEXT windows are used to display a large amount of textual data. +This is the type of window one would use for displaying a help file, +for example. In the tty window-port, windows of type NHW_TEXT can +page using the DEF_PAGER, if DEF_PAGER is defined. There exists an +assumption that the font for text windows is monospaced. The help +files are all formatted accordingly. + +"window" is always of type winid. This is currently implemented as an +integer, but doesn't necessarily have to be done that way. There are +a few fixed window names that are known throughout the code: + + WIN_MESSAGE (top line) + WIN_STATUS (bottom lines) + WIN_MAP (main dungeon) + WIN_INVEN (inventory) + +Other windows are created and destroyed as needed. + +"Port" in this document refers to a CPU/OS/hardware platform (UNIX, MSDOS +TOS, etc.) "window-port" refers to the windowing platform. This is +orthogonal (e.g. UNIX might use either a tty window-port or an X11 +window-port). + + +II. Interface Specification + +All functions below are void unless otherwise noted. + +A. Low-level routines: + +raw_print(str) -- Print directly to a screen, or otherwise guarantee that + the user sees str. raw_print() appends a newline to str. + It need not recognize ASCII control characters. This is + used during startup (before windowing system initialization + -- maybe this means only error startup messages are raw), + for error messages, and maybe other "msg" uses. E.g. + updating status for micros (i.e, "saving"). +raw_print_bold(str) + -- Like raw_print(), but prints in bold/standout (if possible). +curs(window, x, y) + -- Next output to window will start at (x,y), also moves + displayable cursor to (x,y). For backward compatibility, + 1 <= x < cols, 0 <= y < rows, where cols and rows are + the size of window. + -- For variable sized windows, like the status window, the + behavior when curs() is called outside the window's limits + is unspecified. The mac port wraps to 0, with the status + window being 2 lines high and 80 columns wide. + -- Still used by curs_on_u(), status updates, screen locating + (identify, teleport). + -- NHW_MESSAGE, NHW_MENU and NHW_TEXT windows do not + currently support curs in the tty window-port. +putstr(window, attr, str) + -- Print str on the window with the given attribute. Only + printable ASCII characters (040-0126) must be supported. + Multiple putstr()s are output on separate lines. Attributes + can be one of + ATR_NONE (or 0) + ATR_ULINE + ATR_BOLD + ATR_BLINK + ATR_INVERSE + If a window-port does not support all of these, it may map + unsupported attributes to a supported one (e.g. map them + all to ATR_INVERSE). putstr() may compress spaces out of + str, break str, or truncate str, if necessary for the + display. Where putstr() breaks a line, it has to clear + to end-of-line. + -- putstr should be implemented such that if two putstr()s + are done consecutively the user will see the first and + then the second. In the tty port, pline() achieves this + by calling more() or displaying both on the same line. +get_nh_event() -- Does window event processing (e.g. exposure events). + A noop for the tty and X window-ports. +int nhgetch() -- Returns a single character input from the user. + -- In the tty window-port, nhgetch() assumes that tgetch() + will be the routine the OS provides to read a character. + Returned character _must_ be non-zero and it must be + non meta-zero too (zero with the meta-bit set). +int nh_poskey(int *x, int *y, int *mod) + -- Returns a single character input from the user or a + a positioning event (perhaps from a mouse). If the + return value is non-zero, a character was typed, else, + a position in the MAP window is returned in x, y and mod. + mod may be one of + + CLICK_1 /* mouse click type 1 */ + CLICK_2 /* mouse click type 2 */ + + The different click types can map to whatever the + hardware supports. If no mouse is supported, this + routine always returns a non-zero character. + +B. High-level routines: + +print_glyph(window, x, y, glyph) + -- Print the glyph at (x,y) on the given window. Glyphs are + integers at the interface, mapped to whatever the window- + port wants (symbol, font, color, attributes, ...there's + a 1-1 map between glyphs and distinct things on the map). +char yn_function(const char *ques, const char *choices, char default) + -- Print a prompt made up of ques, choices and default. + Read a single character response that is contained in + choices or default. If choices is NULL, all possible + inputs are accepted and returned. This overrides + everything else. The choices are expected to be in + lower case. Entering ESC always maps to 'q', or 'n', + in that order, if present in choices, otherwise it maps + to default. Entering any other quit character (SPACE, + RETURN, NEWLINE) maps to default. + -- If the choices string contains ESC, then anything after + it is an acceptable response, but the ESC and whatever + follows is not included in the prompt. + -- If the choices string contains a '#' then accept a count. + Place this value in the global "yn_number" and return '#'. + -- This uses the top line in the tty window-port, other + ports might use a popup. + -- If choices is NULL, all possible inputs are accepted and + returned, preserving case (upper or lower.) This means that + if the calling function needs an exact match, it must handle + user input correctness itself. +getlin(const char *ques, char *input) + -- Prints ques as a prompt and reads a single line of text, + up to a newline. The string entered is returned without the + newline. ESC is used to cancel, in which case the string + "\033\000" is returned. + -- getlin() must call flush_screen(1) before doing anything. + -- This uses the top line in the tty window-port, other + ports might use a popup. + -- getlin() can assume the input buffer is at least BUFSZ + bytes in size and must truncate inputs to fit, including + the nul character. +int get_ext_cmd(void) + -- Get an extended command in a window-port specific way. + An index into extcmdlist[] is returned on a successful + selection, -1 otherwise. +player_selection() + -- Do a window-port specific player type selection. If + player_selection() offers a Quit option, it is its + responsibility to clean up and terminate the process. + You need to fill in pl_character[0]. +display_file(str, boolean complain) + -- Display the file named str. Complain about missing files + iff complain is TRUE. +update_inventory() + -- Indicate to the window port that the inventory has been + changed. + -- Merely calls display_inventory() for window-ports that + leave the window up, otherwise empty. +doprev_message() + -- Display previous messages. Used by the ^P command. + -- On the tty-port this scrolls WIN_MESSAGE back one line. + +update_positionbar(char *features) + -- Optional, POSITIONBAR must be defined. Provide some + additional information for use in a horizontal + position bar (most useful on clipped displays). + Features is a series of char pairs. The first char + in the pair is a symbol and the second char is the + column where it is currently located. + A '<' is used to mark an upstairs, a '>' + for a downstairs, and an '@' for the current player + location. A zero char marks the end of the list. + + +C. Window Utility Routines + +init_nhwindows(int* argcp, char** argv) + -- Initialize the windows used by NetHack. This can also + create the standard windows listed at the top, but does + not display them. + -- Any commandline arguments relevant to the windowport + should be interpreted, and *argcp and *argv should + be changed to remove those arguments. + -- When the message window is created, the variable + iflags.window_inited needs to be set to TRUE. Otherwise + all plines() will be done via raw_print(). + ** Why not have init_nhwindows() create all of the "standard" + ** windows? Or at least all but WIN_INFO? -dean +exit_nhwindows(str) + -- Exits the window system. This should dismiss all windows, + except the "window" used for raw_print(). str is printed + if possible. +window = create_nhwindow(type) + -- Create a window of type "type." +clear_nhwindow(window) + -- Clear the given window, when appropriate. +display_nhwindow(window, boolean blocking) + -- Display the window on the screen. If there is data + pending for output in that window, it should be sent. + If blocking is TRUE, display_nhwindow() will not + return until the data has been displayed on the screen, + and acknowledged by the user where appropriate. + -- All calls are blocking in the tty window-port. + -- Calling display_nhwindow(WIN_MESSAGE,???) will do a + --more--, if necessary, in the tty window-port. +destroy_nhwindow(window) + -- Destroy will dismiss the window if the window has not + already been dismissed. +start_menu(window) + -- Start using window as a menu. You must call start_menu() + before add_menu(). After calling start_menu() you may not + putstr() to the window. Only windows of type NHW_MENU may + be used for menus. +add_menu(windid window, int glyph, const anything identifier, + char accelerator, char groupacc, + int attr, char *str, boolean preselected) + -- Add a text line str to the given menu window. If identifier + is 0, then the line cannot be selected (e.g. a title). + Otherwise, identifier is the value returned if the line is + selected. Accelerator is a keyboard key that can be used + to select the line. If the accelerator of a selectable + item is 0, the window system is free to select its own + accelerator. It is up to the window-port to make the + accelerator visible to the user (e.g. put "a - " in front + of str). The value attr is the same as in putstr(). + Glyph is an optional glyph to accompany the line. If + window port cannot or does not want to display it, this + is OK. If there is no glyph applicable, then this + value will be NO_GLYPH. + -- All accelerators should be in the range [A-Za-z], + but there are a few exceptions such as the tty player + selection code which uses '*'. + -- It is expected that callers do not mix accelerator + choices. Either all selectable items have an accelerator + or let the window system pick them. Don't do both. + -- Groupacc is a group accelerator. It may be any character + outside of the standard accelerator (see above) or a + number. If 0, the item is unaffected by any group + accelerator. If this accelerator conflicts with + the menu command (or their user defined alises), it loses. + The menu commands and aliases take care not to interfere + with the default object class symbols. + -- If you want this choice to be preselected when the + menu is displayed, set preselected to TRUE. + +end_menu(window, prompt) + -- Stop adding entries to the menu and flushes the window + to the screen (brings to front?). Prompt is a prompt + to give the user. If prompt is NULL, no prompt will + be printed. + ** This probably shouldn't flush the window any more (if + ** it ever did). That should be select_menu's job. -dean +int select_menu(windid window, int how, menu_item **selected) + -- Return the number of items selected; 0 if none were chosen, + -1 when explicitly cancelled. If items were selected, then + selected is filled in with an allocated array of menu_item + structures, one for each selected line. The caller must + free this array when done with it. The "count" field + of selected is a user supplied count. If the user did + not supply a count, then the count field is filled with + -1 (meaning all). A count of zero is equivalent to not + being selected and should not be in the list. If no items + were selected, then selected is NULL'ed out. How is the + mode of the menu. Three valid values are PICK_NONE, + PICK_ONE, and PICK_ANY, meaning: nothing is selectable, + only one thing is selectable, and any number valid items + may selected. If how is PICK_NONE, this function should + never return anything but 0 or -1. + -- You may call select_menu() on a window multiple times -- + the menu is saved until start_menu() or destroy_nhwindow() + is called on the window. + -- Note that NHW_MENU windows need not have select_menu() + called for them. There is no way of knowing whether + select_menu() will be called for the window at + create_nhwindow() time. +char message_menu(char let, int how, const char *mesg) + -- tty-specific hack to allow single line context-sensitive + help to behave compatibly with multi-line help menus. + -- This should only be called when a prompt is active; it + sends `mesg' to the message window. For tty, it forces + a --More-- prompt and enables `let' as a viable keystroke + for dismissing that prompt, so that the original prompt + can be answered from the message line "help menu". + -- Return value is either `let', '\0' (no selection was made), + or '\033' (explicit cancellation was requested). + -- Interfaces which issue prompts and messages to separate + windows typically won't need this functionality, so can + substitute genl_message_menu (windows.c) instead. + +D. Misc. Routines + +make_sound(???) -- To be determined later. THIS IS CURRENTLY UN-IMPLEMENTED. +nhbell() -- Beep at user. [This will exist at least until sounds are + redone, since sounds aren't attributable to windows anyway.] +mark_synch() -- Don't go beyond this point in I/O on any channel until + all channels are caught up to here. Can be an empty call + for the moment +wait_synch() -- Wait until all pending output is complete (*flush*() for + streams goes here). + -- May also deal with exposure events etc. so that the + display is OK when return from wait_synch(). +delay_output() -- Causes a visible delay of 50ms in the output. + Conceptually, this is similar to wait_synch() followed + by a nap(50ms), but allows asynchronous operation. +askname() -- Ask the user for a player name. +cliparound(x, y)-- Make sure that the user is more-or-less centered on the + screen if the playing area is larger than the screen. + -- This function is only defined if CLIPPING is defined. +number_pad(state) + -- Initialize the number pad to the given state. +suspend_nhwindows(str) + -- Prepare the window to be suspended. +resume_nhwindows() + -- Restore the windows after being suspended. + +start_screen() -- Only used on Unix tty ports, but must be declared for + completeness. Sets up the tty to work in full-screen + graphics mode. Look at win/tty/termcap.c for an + example. If your window-port does not need this function + just declare an empty function. +end_screen() -- Only used on Unix tty ports, but must be declared for + completeness. The complement of start_screen(). + +outrip(winid, int) + -- The tombstone code. If you want the traditional code use + genl_outrip for the value and check the #if in rip.c. + +preference_update(preference) + -- The player has just changed one of the wincap preference + settings, and the NetHack core is notifying your window + port of that change. If your window-port is capable of + dynamically adjusting to the change then it should do so. + Your window-port will only be notified of a particular + change if it indicated that it wants to be by setting the + corresponding bit in the wincap mask. + +III. Global variables + +The following global variables are defined in decl.c and must be used by +the window interface to the rest of NetHack. + +char toplines[BUFSZ] Contains the last message printed to the WIN_MESSAGE + window, used by Norep(). +winid WIN_MESSAGE, WIN_MAP, WIN_STATUS, WIN_INVEN + The four standard windows. +char *AE, *AS; Checked in options.c to see if we should switch + to DEC_GRAPHICS. It is #ifdefed VMS and UNIX. +int LI, CO; Set in sys/unix/ioctl.c. + +The following appears to be Unix specific. Other ports using the tty +window-port should also declare this variable in one of your sys/*.c files. + +short ospeed; Set and declared in sys/unix/unixtty.c (don't + know about other sys files). + +The following global variable is defined in options.c. It equates a +list of wincap option names with their associated bit-mask [see +section IV WINCAP preferences support]. The array is zero-terminated. + +struct wc_Opt wc_options[]; + One entry for each available WINCAP option. + Each entry has a wc_name field and a wc_bit + field. + +IV. WINCAP preferences support + +Starting with NetHack 3.4.0, the window interface was enhanced to provide +a common way of setting window port user preferences from the config file, +and from the command line for some settings. + +The wincap preference settings all have their underlying values stored +in iflags fields. The names of the wincap related fields are all pre- +fixed with wc_ or wc2_ to make it easy to identify them. Your window +port can access the fields directly. + +Your window port identifies what options it will react to and support +by setting bits in the window_procs wincap mask and/or wincap2 mask. +See section IX for details of where the wincap masks reside. + +Two things control whether any preference setting appears in the +'O' command options menu during the game: + 1. The option must be marked as being supported by having its + bit set in the window_procs wincap or wincap2 mask. + 2. The option must have its optflag field set to SET_IN_GAME in order + to be able to set the option, or marked DISP_IN_GAME if you just + want to reveal what the option is set to. +Both conditions must be true to be able to see or set the option from +within NetHack. + +The default values for the optflag field for all the options are +hard-coded into the option in options.c. The default value for +the wc_ options can be altered by calling + set_wc_option_mod_status(optmask, status) +The default value for the wc2_ options can be altered by calling + set_wc2_option_mod_status(optmask, status) +In each case, set the option modification status to one of SET_IN_FILE, +DISP_IN_GAME, or SET_IN_GAME. + +The setting of any wincap or wincap2 option is handled by the NetHack +core option processing code. You do not have to provide a parser in +your window port, nor should you set the values for the +iflags.wc_* and iflags.wc2_* fields directly within the port code. +The port code should honor whatever values were put there by the core +when processing options, either in the config file, or by the 'O' command. + +You may be wondering what values your window port will find in the +iflags.wc_* and iflags.wc2_* fields for options that the user has not +specified in his/her config file. Put another way, how does you port code +tell if an option has not been set? The next paragraph explains that. + +If the core does not set an option, it will still be initialized +to its default value. Those default values for the +iflags.wc_* and iflags.wc_* fields are: + + o All boolean fields are initialized to the starting + value specified for that option in the boolopt array in + options.c. The window-port should respect that setting + unless it has a very good reason for not doing so. + o All int fields are initialized to zero. Zero is not a valid + setting for any of the int options, so if your port code + encounters a zero there, it can assume that the preference + option was not specified. In that case, the window-port code + should use a default setting that the port is comfortable with. + It should write the default setting back into the iflags.wc_* + field. That is the only time that your window-port could should + update those fields. + o All "char *" fields will be null pointers. Be sure to check for + that in your window-port code before using such a pointer, or + you'll end up triggering a nasty fault. + +Here are the wincap and wincap2 preference settings that your port can choose +to support: + + wincap + +--------------------+--------------------+--------------------+--------+ + | | | iflags field | data | + | player option | bit in wincap mask | for value | type | + |--------------------+--------------------+--------------------+--------+ + | align_message | WC_ALIGN_MESSAGE | wc_align_message |int | + | align_status | WC_ALIGN_STATUS | wc_align_status |int | + | ascii_map | WC_ASCII_MAP | wc_ascii_map |boolean | + | color | WC_COLOR | wc_color |boolean | + | eight_bit_tty | WC_EIGHT_BIT_IN | wc_eight_bit_input |boolean | + | font_map | WC_FONT_MAP | wc_font_map |char * | + | font_menu | WC_FONT_MENU | wc_font_menu |char * | + | font_message | WC_FONT_MESSAGE | wc_font_message |char * | + | font_status | WC_FONT_STATUS | wc_font_status |char * | + | font_text | WC_FONT_TEXT | wc_font_text |char * | + | font_size_map | WC_FONTSIZ_MAP | wc_fontsiz_map |int | + | font_size_menu | WC_FONTSIZ_MENU | wc_fontsiz_menu |int | + | font_size_message | WC_FONTSIZ_MESSAGE | wc_fontsiz_message |int | + | font_size_status | WC_FONTSIZ_STATUS | wc_fontsiz_status |int | + | font_size_text | WC_FONTSIZ_TEXT | wc_fontsiz_text |int | + | hilite_pet | WC_HILITE_PET | wc_hilite_pet |boolean | + | map_mode | WC_MAP_MODE | wc_map_mode |int | + | player_selection | WC_PLAYER_SELECTION| wc_player_selection|int | + | popup_dialog | WC_POPUP_DIALOG | wc_popup_dialog |boolean | + | preload_tiles | WC_PRELOAD_TILES | wc_preload_tiles |boolean | + | scroll_amount | WC_SCROLL_AMOUNT | wc_scroll_amount |int | + | scroll_margin | WC_SCROLL_MARGIN | wc_scroll_margin |int | + | splash_screen | WC_SPLASH_SCREEN | wc_splash_screen |boolean | + | tiled_map | WC_TILED_MAP | wc_tiled_map |boolean | + | tile_width | WC_TILE_WIDTH | wc_tile_width |int | + | tile_height | WC_TILE_HEIGHT | wc_tile_height |int | + | tile_file | WC_TILE_FILE | wc_tile_file |char * | + | use_inverse | WC_INVERSE | wc_inverse |boolean | + | vary_msgcount | WC_VARY_MSGCOUNT | wc_vary_msgcount |int | + | windowcolors | WC_WINDOWCOLORS | wc_foregrnd_menu |char * | + | | | wc_backgrnd_menu |char * | + | | | wc_foregrnd_message|char * | + | | | wc_backgrnd_message|char * | + | | | wc_foregrnd_status |char * | + | | | wc_backgrnd_status |char * | + | | | wc_foregrnd_text |char * | + | | | wc_backgrnd_text |char * | + | mouse | WC_MOUSE_SUPPORT | wc_mouse_support |boolean | + +--------------------+--------------------+--------------------+--------+ + + wincap2 + +--------------------+--------------------+--------------------+--------+ + | | | iflags field | data | + | player option | bit in wincap mask | for value | type | + |--------------------+--------------------+--------------------+--------+ + | fullscreen | WC2_FULLSCREEN | wc2_fullscreen |boolean | + | softkeyboard | WC2_SOFTKEYBOARD | wc2_softkeyboard |boolean | + | wraptext | WC2_WRAPTEXT | wc2_wraptext |boolean | + +--------------------+--------------------+--------------------+--------+ + +align_message -- where to place message window (top, bottom, left, right) +align_status -- where to place status window (top, bottom, left, right). +ascii_map -- port should display an ascii map if it can. +color -- port should display color if it can. +eight_bit_tty -- port should allow eight bit input. +font_map -- port should use a font by this name for map window. +font_menu -- port should use a font by this name for menu windows. +font_message -- port should use a font by this name for message window. +font_size_map -- port should use this size font for the map window. +font_size_menu -- port should use this size font for menu windows. +font_size_message + -- port should use this size font for the message window. +font_size_status-- port should use this size font for the status window. +font_size_text -- port should use this size font for text windows. +font_status -- port should use a font by this name for status window. +font_text -- port should use a font by this name for text windows. +fullscreen -- port should try to use the whole screen. +hilite_pet -- port should mark pets in some special way on the map. +map_mode -- port should display the map in the manner specified. +player_selection + -- dialog or prompts for choosing character. +popup_dialog -- port should pop up dialog boxes for input. +preload_tiles -- port should preload tiles into memory. +scroll_amount -- scroll this amount when scroll_margin is reached. +scroll_margin -- port should scroll the display when the hero or cursor + is this number of cells away from the edge of the window. +softkeyboard -- handhelds should display an on-screen keyboard if possible. +splash_screen -- port should/should not display an opening splashscreen. +tiled_map -- port should display a tiled map if it can. +tile_width -- port should display tiles with this width or round to closest + if it can. +tile_height -- port should display tiles with this height or round to closest + if it can. +tile_file -- open this alternative tile file. The file name is likely to be + window-port or platform specific. +use_inverse -- port should display inverse when NetHack asks for it. +vary_msgcount -- port should display this number of messages at a time in + the message window. +windowcolors + -- port should use these colors for window foreground/background + colors. Syntax: + menu fore/back message fore/back status fore/back text fore/back +wraptext -- port should wrap long lines of text if they don't fit in + the visible area of the window +mouse_support -- port should enable mouse support if possible + +Whenever one of these settings is adjusted, the port is notified of a change +to the setting by calling the port's preference_update() routine. The port +is only notified if it has indicated that it supports that option by setting +the option's bit in the port's wincap mask. The port can choose to adjust +for the change to an option that it receives notification about, or ignore it. +The former approach is recommended. If you don't want to deal with a +user-initiated setting change, then the port should call +set_wc_option_mod_status(mask, SET_IN_FILE) to make the option invisible to +the user. + +Functions available for the window port to call: + +set_wc_option_mod_status(optmask, status) + -- Adjust the optflag field for a set of wincap options to + specify whether the port wants the option to appear + in the 'O' command options menu, The second parameter, + "status" can be set to SET_IN_FILE, DISP_IN_GAME, + or SET_IN_GAME (SET_IN_FILE implies that the option + is completely hidden during the game). + +set_wc2_option_mod_status(optmask, status) + -- Adjust the optflag field for a set of wincap2 options to + specify whether the port wants the option to appear + in the 'O' command options menu, The second parameter, + "status" can be set to SET_IN_FILE, DISP_IN_GAME, + or SET_IN_GAME (SET_IN_FILE implies that the option + is completely hidden during the game). + +set_option_mod_status(optnam, status) + -- Adjust the optflag field for one of the core options + that is not part of the wincap suite. A port might use + this to override the default initialization setting for + status specified in options.c. Note that you have to + specify the option by name and that you can only set + one option per call unlike set_wc_option_mod_status(). + + +Adding a new wincap option: + +To add a new wincap option, please follow all these steps: + 1. Add the option to the wincap preference settings table above. Since + wincap is full, your option will likely target wincap2 field. + 2. Add the description to the paragraph below the chart. + 3. Add the WC_ or WC2_ to the bit list in include/winprocs.h + (in wincap2 if there is no room in wincap). + 4. Add the wc_ or wc2_ field(s) to the iflags structure in flag.h. + 5. Add the name and value to wc_options[] or wc2_options[] in options.c + 6. Add an appropriate parser to parseoptions() in options.c. + 7. Add code to display current value to get_compopt_value() in options.c. + 8. Document the option in Guidebook.mn and Guidebook.tex. + 9. Add the bit name to the OR'd values in your window port's winprocs struct + wincap mask if your port supports the option. + +V. New or respecified common, high level routines + +These are not part of the interface, but mentioned here for your information. + +char display_inventory(lets, want_reply) + -- Calls a start_menu()/add_menu()/select_menu() sequence. + It returns the item selected, or '\0' if none is selected. + Returns '\033' if the menu was canceled. +raw_printf(str, ...) + -- Like raw_print(), but accepts arguments like printf(). This + routine processes the arguments and then calls raw_print(). + -- The mac version #defines error raw_printf. I think this + is a reasonable thing to do for most ports. +pline(str, ...) + -- Prints a string to WIN_MESSAGE using a printf() interface. + It has the variants You(), Your(), Norep(), and others + in pline.c which all use the same mechanism. pline() + requires the variable "char toplines[]" be defined; Every + putstr() on WIN_MESSAGE must copy str to toplines[] for use + by Norep() and pline(). If the window system is not active + (!iflags.window_inited) pline() uses raw_print(). + +VI. Helper Routines + +These are not part of the interface. They may be called by your +window port routines to perform the desired task, instead of duplicating +the necessary code in each window port. + +mapglyph(int glyph, int *ochar, int *ocolor, unsigned *special, int x, int y) + -- Maps glyph at x,y to NetHack ascii character and color. + If it represents something special such as a pet, that + information is returned as set bits in "special." + Usually called from the window port's print_glyph() + routine. + +VII. Game startup + +The following is the general order in which calls from main() should be made, +as they relate to the window system. The actual code may differ, but the +order of the calls should be the same. + + +choose_windows(DEFAULT_WINDOW_SYS) /* choose a default window system */ +initoptions() /* read the resource file */ +init_nhwindows() /* initialize the window system */ +process_options(argc, argv) /* process command line options or equiv */ +if(save file is present) { + display_gamewindows() /* create & display the game windows */ + dorestore() /* restore old game; pline()s are OK */ +} else { + player_selection() /* select a player type using a window */ + display_gamewindows() /* create & display the game windows */ +} +pline("Hello, welcome..."); + +Choose_windows() is a common routine, and calling it in main() is necessary +to initialize the function pointer table to _something_ so that calls to +raw_print() will not fail. Choose_windows() should be called almost +immediately upon entering main(). Look at unixmain.c for an example. + +Display_gamewindows() is a common routine that displays the three standard +game windows (WIN_MESSAGE, WIN_MAP, and WIN_STATUS). It is normally called +just before the "Hello, welcome" message. + +Process_options() is currently still unique to each port. There may be need +in the future to make it possible to replace this on a per window-port basis. + + +VIII. Conventions + +init_nhwindows() is expected to display a gee-whiz banner window, including +the Copyright message. It is recommended that the COPYRIGHT_BANNER_A, +COPYRIGHT_BANNER_B, and COPYRIGHT_BANNER_C macros from patchlevel.h be used +for constructing the Copyright message. COPYRIGHT_BANNER_A is a +quoted string that has the NetHack copyright declaration, +COPYRIGHT_BANNER_B is a quoted string that states who the copyright +belongs to, and COPYRIGHT_BANNER_C simply says "See License for +details." Be sure to #include "patchlevel.h" to define these macros. +Using the macros will prevent having to update the Copyright information +in each window-port prior to each release. + +Ports (MSDOS, TOS, MAC, etc) _may_ use window-port specific routines in +their port specific files, _AT_THEIR_OWN_RISK_. Since "port" and +"window-port" are orthogonal, you make your "port" code less portable by +using "window-port" specific routines. Every effort should be made to +use window-port interface routines, unless there is something port +specific that is better suited (e.g. msmsg() for MSDOS). + +The tty window-port is contained in win/tty, the X window port is contained +in win/X11. The files in these directories contain _only_ window port code, +and may be replaced completely by other window ports. + + +IX. Implementation and Multi-window support + +NetHack 3.2 and higher support multiple window systems in the same binary. +When writing a new window-port, you need to follow the following guidelines: + +1) Pick a unique prefix to identify your window-port. For example, the tty + window port uses "tty"; the X11 window-port uses "X11". +2) When declaring your interface function, precede the function names with + your unique prefix. E.g: + + void tty_init_nhwindows() + { + /* code for initializing windows in the tty port */ + } + + When calling window functions from within your port code, we suggest + calling the prefixed version to avoid unnecessary overhead. However, + you may safely call the non-prefixed version (e.g. putstr() rather than + tty_putstr()) as long as you #include "hack.h". If you do not + include hack.h and use the non-prefixed names, you will get compile + or link-time errors. + + We also suggest declaring all functions and port-specific data with + this prefix to avoid unexpected overlaps with other window-ports. + The tty and X11 ports do not currently follow this suggestion, but do + use separate non-overlapping convention for naming data and internal + functions. + +3) Declare a structure, "struct window_procs prefix_procs", (with your + prefix instead of "prefix") and fill in names of all of your + interface functions. The first entry in this structure is the name + of your window-port, which should be the prefix. The second entry + is the wincap mask that identifies what window port preference + settings your port will react to and support. The other entries + are the function addresses. + + Assuming that you followed the convention in (2), you can safely copy + the structure definition from an existing window-port and just change + the prefixes. That will guarantee that you get the order of your + initializations correct (not all compilers will catch out-of-order + function pointer declarations). + +4) Add a #define to config.h identifying your window-port in the + "Windowing systems" section. Follow the "prefix_GRAPHICS" convention + for your window-port. + +5) Add your prefix to the list of valid prefixes listed in the "Known + systems are" comment. + +6) Edit makedefs.c and add a string for your windowing system to window_opts + inside an #ifdef prefix_GRAPHICS. + +7) Edit windows.c and add an external reference to your prefix_procs inside + an #ifdef prefix_GRAPHICS. Also add an entry to the win_choices + structure for your window-port of the form: + + #ifdef prefix_GRAPHICS + { &prefix_procs, prefix_init_function }, + #endif + + The init_function is necessary for some compilers and systems to force + correct linking. If your system does not need such massaging, you + may put a null pointer here. + + You should declare prefix_procs and prefix_init_function as extern's + in your win*.h file, and #include that file at the beginning of + windows.c, also inside an #ifdef prefix_GRAPHICS. Some win*.h files + are rather sensitive, and you might have to duplicate your + prefix_procs and prefix_init_function's instead of including win*.h. + The tty port includes wintty.h, the X11 port duplicates the declarations. + +8) If your port uses Makefile.src, add the .c and .o files and an + appropriate comment in the section on "WINSRC" and "WINOBJ". See + Makefile.src for the style to use. If you don't use Makefile.src, + we suggest using a similar convention for the make-equivalent used + on your system. Also add your new source and binaries to WINSRC and + WINOBJ (if you want the NetHack binary to include them, that is). + +9) Look at your port's portmain.c (the file containing main()) and make + sure that all of the calls match the the requirements laid out in + Section VII. + +Now, proceed with compilation and installation as usual. Don't forget +to edit Makefile.src (or its equivalent) and config.h to set the +window-ports you want in your binary, the default window-port to use, +and the .o's needed to build a valid game. + +One caveat. Unfortunately, if you incorrectly specify the +DEFAULT_WINDOW_SYS, NetHack will dump core (or whatever) without +printing any message, because raw_print() cannot function without first +setting the window-port. diff --git a/include/align.h b/include/align.h new file mode 100644 index 0000000..1925f14 --- /dev/null +++ b/include/align.h @@ -0,0 +1,42 @@ +/* SCCS Id: @(#)align.h 3.4 1991/12/29 */ +/* Copyright (c) Mike Stephenson, Izchak Miller 1991. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef ALIGN_H +#define ALIGN_H + +typedef schar aligntyp; /* basic alignment type */ + +typedef struct align { /* alignment & record */ + aligntyp type; + int record; +} align; + +/* bounds for "record" -- respect initial alignments of 10 */ +#define ALIGNLIM (10L + (moves/200L)) + +#define A_NONE (-128) /* the value range of type */ + +#define A_CHAOTIC (-1) +#define A_NEUTRAL 0 +#define A_LAWFUL 1 + +#define A_COALIGNED 1 +#define A_OPALIGNED (-1) + +#define AM_NONE 0 +#define AM_CHAOTIC 1 +#define AM_NEUTRAL 2 +#define AM_LAWFUL 4 + +#define AM_MASK 7 + +#define AM_SPLEV_CO 3 +#define AM_SPLEV_NONCO 7 + +#define Amask2align(x) ((aligntyp) ((!(x)) ? A_NONE \ + : ((x) == AM_LAWFUL) ? A_LAWFUL : ((int)x) - 2)) +#define Align2amask(x) (((x) == A_NONE) ? AM_NONE \ + : ((x) == A_LAWFUL) ? AM_LAWFUL : (x) + 2) + +#endif /* ALIGN_H */ diff --git a/include/amiconf.h b/include/amiconf.h new file mode 100644 index 0000000..6b36c4f --- /dev/null +++ b/include/amiconf.h @@ -0,0 +1,190 @@ +/* SCCS Id: @(#)amiconf.h 3.4 2000/01/12 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1990, 1991, 1992, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef AMICONF_H +#define AMICONF_H + +#undef abs /* avoid using macro form of abs */ +#ifndef __SASC_60 +# undef min /* this gets redefined */ +# undef max /* this gets redefined */ +#endif + +#include /* get time_t defined before use! */ + +#ifdef __SASC_60 /* since SAS can prevent re-inclusion */ +#include /* general things, including builtins */ +#include +#endif + +#ifdef AZTEC_50 +#include +# define AZTEC_C_WORKAROUND /* Bug which turns up in sounds.c. Bummer... */ +# define NO_SIGNAL /* 5.0 signal handling doesn't like SIGINT... */ +#endif + +#ifdef _DCC +#include +# define _SIZE_T +# define DCC30_BUG /* A bitfield bug (from dog.c, others) in DICE 3.0. */ +#endif + +#ifndef __GNUC__ +typedef long off_t; +#endif + +#define MICRO /* must be defined to allow some inclusions */ + +#define NOCWD_ASSUMPTIONS /* Allow paths to be specified for HACKDIR, + LEVELDIR, SAVEDIR, BONESDIR, DATADIR, + SCOREDIR, LOCKDIR, CONFIGDIR, and TROUBLEDIR */ + +/* data librarian defs */ +#ifndef NOCWD_ASSUMPTIONS +# define DLBFILE "NetHack:nhdat" /* main library */ +# define DLBFILE2 "NetHack:nhsdat" /* sound library */ +#else +# define DLBFILE "nhdat" /* main library */ +# define DLBFILE2 "nhsdat" /* sound library */ +#endif + +#define FILENAME_CMP stricmp /* case insensitive */ + +#ifndef __SASC_60 +# define O_BINARY 0 +#endif + +/* Compile in New Intuition look for 2.0 */ +#ifdef IDCMP_CLOSEWINDOW +# ifndef INTUI_NEW_LOOK +# define INTUI_NEW_LOOK 1 +# endif +#endif + +#define MFLOPPY /* You'll probably want this; provides assistance + * for typical personal computer configurations + */ +#define RANDOM + +/* ### amidos.c ### */ + +extern void FDECL(nethack_exit, (int)); + +/* ### amiwbench.c ### */ + +extern void NDECL(ami_wbench_init); +extern void NDECL(ami_wbench_args); +extern int FDECL(ami_wbench_getsave, (int)); +extern void FDECL(ami_wbench_unlink, (char *)); +extern int FDECL(ami_wbench_iconsize, (char *)); +extern void FDECL(ami_wbench_iconwrite, (char *)); +extern int FDECL(ami_wbench_badopt, (const char *)); +extern void NDECL(ami_wbench_cleanup); +extern void FDECL(getlind, (const char *,char *,const char *)); + +/* ### winreq.c ### */ + +extern void amii_setpens(int); + +extern void FDECL(exit, (int)); +extern void NDECL(CleanUp); +extern void FDECL(Abort, (long)); +extern int NDECL(getpid); +extern char *FDECL(CopyFile, (const char *, const char *)); +extern int NDECL(kbhit); +extern int NDECL(WindowGetchar); +extern void FDECL(ami_argset, (int *, char *[])); +extern void FDECL(ami_mkargline, (int *, char **[])); +extern void ami_wininit_data(void); + +#define FromWBench 0 /* A hint for compiler ... */ +/* extern boolean FromWBench; /* how were we run? */ +extern int ami_argc; +extern char **ami_argv; + +#ifndef MICRO_H +#include "micro.h" +#endif + +#ifndef PCCONF_H +#include "pcconf.h" /* remainder of stuff is almost same as the PC */ +#endif + +#define remove(x) unlink(x) + +/* DICE wants rewind() to return void. We want it to return int. */ +#if defined(_DCC) || defined(__GNUC__) +# define rewind(f) fseek(f, 0, 0) +#endif + +#ifdef AZTEC_C +extern FILE *FDECL(freopen, (const char *, const char *, FILE *)); +extern char *FDECL(gets, (char *)); +#endif + +#define msmsg printf + +/* + * If AZTEC_C we can't use the long cpath in vision.c.... + */ +#ifdef AZTEC_C +# undef MACRO_CPATH +#endif + +/* + * (Possibly) configurable Amiga options: + */ + +#define TEXTCOLOR /* Use colored monsters and objects */ +#define HACKFONT /* Use special hack.font */ +#define SHELL /* Have a shell escape command (!) */ +#define MAIL /* Get mail at unexpected occasions */ +#define DEFAULT_ICON "NetHack:default.icon" /* private icon */ +#define AMIFLUSH /* toss typeahead (select flush in .cnf) */ +/* #define OPT_DISPMAP /* enable fast_map option */ + +/* new window system options */ + /* WRONG - AMIGA_INTUITION should go away */ +#ifdef AMII_GRAPHICS +# define AMIGA_INTUITION /* high power graphics interface (amii) */ +#endif + +#define CHANGE_COLOR 1 + +#ifdef TEXTCOLOR +# define DEPTH 6 /* Maximum depth of the screen allowed */ +#else +# define DEPTH 2 /* Four colors...sigh... */ +#endif + +#define AMII_MAXCOLORS (1L< /* for time_t */ +#include /* for lseek() */ + +/* could go in extern.h, under bemain.c (or something..) */ +void regularize(char *); + + +/* instead of including system.h... */ +#include +#include +#include + +#endif /* BECONF_H */ diff --git a/include/bitmfile.h b/include/bitmfile.h new file mode 100644 index 0000000..cef1cf9 --- /dev/null +++ b/include/bitmfile.h @@ -0,0 +1,37 @@ +/****************************\ +* Bitmap mit Farbtabelle als * +* Graphik-Datei speichern * +* Autor: Gabriel Schmidt * +* (c} 1992 by MAXON-Computer * +* -> Header-Datei * +\****************************/ + +#ifndef H_TO_FILE +#define H_TO_FILE + +/* #include */ +#define UWORD unsigned short +#define ULONG unsigned long +#define UBYTE unsigned char + +#define XIMG_MAGIC 0x58494D47 + + +typedef enum { IMG, XIMG } FILE_TYP; + +const char *get_file_ext(FILE_TYP typ); + +struct RGB + { + UWORD r, g, b; + }; + +int bitmap_to_file(FILE_TYP typ, int ww, int wh, + unsigned int pwx, unsigned int pwy, + unsigned int planes, unsigned int colors, + const char *filename, + void (*get_color) (unsigned int colind, struct RGB *rgb) , + void (*get_pixel) (int x, int y, unsigned int *colind) ) ; + +#endif + diff --git a/include/color.h b/include/color.h new file mode 100644 index 0000000..2fe85d1 --- /dev/null +++ b/include/color.h @@ -0,0 +1,52 @@ +/* SCCS Id: @(#)color.h 3.4 1992/02/02 */ +/* Copyright (c) Steve Linhart, Eric Raymond, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef COLOR_H +#define COLOR_H + +/* + * The color scheme used is tailored for an IBM PC. It consists of the + * standard 8 colors, folowed by their bright counterparts. There are + * exceptions, these are listed below. Bright black doesn't mean very + * much, so it is used as the "default" foreground color of the screen. + */ +#define CLR_BLACK 0 +#define CLR_RED 1 +#define CLR_GREEN 2 +#define CLR_BROWN 3 /* on IBM, low-intensity yellow is brown */ +#define CLR_BLUE 4 +#define CLR_MAGENTA 5 +#define CLR_CYAN 6 +#define CLR_GRAY 7 /* low-intensity white */ +#define NO_COLOR 8 +#define CLR_ORANGE 9 +#define CLR_BRIGHT_GREEN 10 +#define CLR_YELLOW 11 +#define CLR_BRIGHT_BLUE 12 +#define CLR_BRIGHT_MAGENTA 13 +#define CLR_BRIGHT_CYAN 14 +#define CLR_WHITE 15 +#define CLR_MAX 16 + +/* The "half-way" point for tty based color systems. This is used in */ +/* the tty color setup code. (IMHO, it should be removed - dean). */ +#define BRIGHT 8 + +/* these can be configured */ +#define HI_OBJ CLR_MAGENTA +#define HI_METAL CLR_CYAN +#define HI_COPPER CLR_YELLOW +#define HI_SILVER CLR_GRAY +#define HI_GOLD CLR_YELLOW +#define HI_LEATHER CLR_BROWN +#define HI_CLOTH CLR_BROWN +#define HI_ORGANIC CLR_BROWN +#define HI_WOOD CLR_BROWN +#define HI_PAPER CLR_WHITE +#define HI_GLASS CLR_BRIGHT_CYAN +#define HI_MINERAL CLR_GRAY +#define DRAGON_SILVER CLR_BRIGHT_CYAN +#define HI_ZAP CLR_BRIGHT_BLUE + +#endif /* COLOR_H */ diff --git a/include/config.h b/include/config.h new file mode 100644 index 0000000..3efbfa2 --- /dev/null +++ b/include/config.h @@ -0,0 +1,358 @@ +/* SCCS Id: @(#)config.h 3.4 2003/12/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef CONFIG_H /* make sure the compiler does not see the typedefs twice */ +#define CONFIG_H + + +/* + * Section 1: Operating and window systems selection. + * Select the version of the OS you are using. + * For "UNIX" select BSD, ULTRIX, SYSV, or HPUX in unixconf.h. + * A "VMS" option is not needed since the VMS C-compilers + * provide it (no need to change sec#1, vmsconf.h handles it). + */ + +#define UNIX /* delete if no fork(), exec() available */ + +/* #define MSDOS */ /* in case it's not auto-detected */ + +/* #define OS2 */ /* define for OS/2 */ + +/* #define TOS */ /* define for Atari ST/TT */ + +/* #define STUPID */ /* avoid some complicated expressions if + your C compiler chokes on them */ +/* #define MINIMAL_TERM */ + /* if a terminal handles highlighting or tabs poorly, + try this define, used in pager.c and termcap.c */ +/* #define ULTRIX_CC20 */ + /* define only if using cc v2.0 on a DECstation */ +/* #define ULTRIX_PROTO */ + /* define for Ultrix 4.0 (or higher) on a DECstation; + * if you get compiler errors, don't define this. */ + /* Hint: if you're not developing code, don't define + ULTRIX_PROTO. */ + +#include "config1.h" /* should auto-detect MSDOS, MAC, AMIGA, and WIN32 */ + + +/* Windowing systems... + * Define all of those you want supported in your binary. + * Some combinations make no sense. See the installation document. + */ +#define TTY_GRAPHICS /* good old tty based graphics */ +/* #define X11_GRAPHICS */ /* X11 interface */ +/* #define QT_GRAPHICS */ /* Qt interface */ +/* #define GNOME_GRAPHICS */ /* Gnome interface */ +/* #define MSWIN_GRAPHICS */ /* Windows NT, CE, Graphics */ + +/* + * Define the default window system. This should be one that is compiled + * into your system (see defines above). Known window systems are: + * + * tty, X11, mac, amii, BeOS, Qt, Gem, Gnome + */ + +/* MAC also means MAC windows */ +#ifdef MAC +# ifndef AUX +# define DEFAULT_WINDOW_SYS "mac" +# endif +#endif + +/* Amiga supports AMII_GRAPHICS and/or TTY_GRAPHICS */ +#ifdef AMIGA +# define AMII_GRAPHICS /* (optional) */ +# define DEFAULT_WINDOW_SYS "amii" /* "amii", "amitile" or "tty" */ +#endif + +/* Atari supports GEM_GRAPHICS and/or TTY_GRAPHICS */ +#ifdef TOS +# define GEM_GRAPHICS /* Atari GEM interface (optional) */ +# define DEFAULT_WINDOW_SYS "Gem" /* "Gem" or "tty" */ +#endif + +#ifdef __BEOS__ +#define BEOS_GRAPHICS /* (optional) */ +#define DEFAULT_WINDOW_SYS "BeOS" /* "tty" */ +#ifndef HACKDIR /* override the default hackdir below */ +# define HACKDIR "/boot/apps/NetHack" +#endif +#endif + +#ifdef QT_GRAPHICS +# define DEFAULT_WC_TILED_MAP /* Default to tiles if users doesn't say wc_ascii_map */ +# define USER_SOUNDS /* Use sounds */ +# ifndef __APPLE__ +# define USER_SOUNDS_REGEX +# endif +# define USE_XPM /* Use XPM format for images (required) */ +# define GRAPHIC_TOMBSTONE /* Use graphical tombstone (rip.ppm) */ +# ifndef DEFAULT_WINDOW_SYS +# define DEFAULT_WINDOW_SYS "Qt" +# endif +#endif + +#ifdef GNOME_GRAPHICS +# define USE_XPM /* Use XPM format for images (required) */ +# define GRAPHIC_TOMBSTONE /* Use graphical tombstone (rip.ppm) */ +# ifndef DEFAULT_WINDOW_SYS +# define DEFAULT_WINDOW_SYS "Gnome" +# endif +#endif + +#ifdef MSWIN_GRAPHICS +# ifdef TTY_GRAPHICS +# undef TTY_GRAPHICS +# endif +# ifndef DEFAULT_WINDOW_SYS +# define DEFAULT_WINDOW_SYS "mswin" +# endif +# define HACKDIR "\\nethack" +#endif + +#ifndef DEFAULT_WINDOW_SYS +# define DEFAULT_WINDOW_SYS "tty" +#endif + +#ifdef X11_GRAPHICS +/* + * There are two ways that X11 tiles may be defined. (1) using a custom + * format loaded by NetHack code, or (2) using the XPM format loaded by + * the free XPM library. The second option allows you to then use other + * programs to generate tiles files. For example, the PBMPlus tools + * would allow: + * xpmtoppm x11tiles_big.xpm + */ +/* # define USE_XPM */ /* Disable if you do not have the XPM library */ +# ifdef USE_XPM +# define GRAPHIC_TOMBSTONE /* Use graphical tombstone (rip.xpm) */ +# endif +#endif + + +/* + * Section 2: Some global parameters and filenames. + * Commenting out WIZARD, LOGFILE, NEWS or PANICLOG removes that + * feature from the game; otherwise set the appropriate wizard + * name. LOGFILE, NEWS and PANICLOG refer to files in the + * playground. + */ + +#ifndef WIZARD /* allow for compile-time or Makefile changes */ +# ifndef KR1ED +# define WIZARD "wizard" /* the person allowed to use the -D option */ +# else +# define WIZARD +# define WIZARD_NAME "wizard" +# endif +#endif + +#define LOGFILE "logfile" /* larger file for debugging purposes */ +#define NEWS "news" /* the file containing the latest hack news */ +#define PANICLOG "paniclog" /* log of panic and impossible events */ + +/* + * If COMPRESS is defined, it should contain the full path name of your + * 'compress' program. Defining INTERNAL_COMP causes NetHack to do + * simpler byte-stream compression internally. Both COMPRESS and + * INTERNAL_COMP create smaller bones/level/save files, but require + * additional code and time. Currently, only UNIX fully implements + * COMPRESS; other ports should be able to uncompress save files a + * la unixmain.c if so inclined. + * If you define COMPRESS, you must also define COMPRESS_EXTENSION + * as the extension your compressor appends to filenames after + * compression. + */ + +#ifdef UNIX +/* path and file name extension for compression program */ +#define COMPRESS "/usr/bin/compress" /* Lempel-Ziv compression */ +#define COMPRESS_EXTENSION ".Z" /* compress's extension */ +/* An example of one alternative you might want to use: */ +/* #define COMPRESS "/usr/local/bin/gzip" */ /* FSF gzip compression */ +/* #define COMPRESS_EXTENSION ".gz" */ /* normal gzip extension */ +#endif + +#ifndef COMPRESS +# define INTERNAL_COMP /* control use of NetHack's compression routines */ +#endif + +/* + * Data librarian. Defining DLB places most of the support files into + * a tar-like file, thus making a neater installation. See *conf.h + * for detailed configuration. + */ +/* #define DLB */ /* not supported on all platforms */ + +/* + * Defining INSURANCE slows down level changes, but allows games that + * died due to program or system crashes to be resumed from the point + * of the last level change, after running a utility program. + */ +#define INSURANCE /* allow crashed game recovery */ + +#ifndef MAC +# define CHDIR /* delete if no chdir() available */ +#endif + +#ifdef CHDIR +/* + * If you define HACKDIR, then this will be the default playground; + * otherwise it will be the current directory. + */ +# ifndef HACKDIR +# define HACKDIR "/usr/games/lib/nethackdir" +# endif + +/* + * Some system administrators are stupid enough to make Hack suid root + * or suid daemon, where daemon has other powers besides that of reading or + * writing Hack files. In such cases one should be careful with chdir's + * since the user might create files in a directory of his choice. + * Of course SECURE is meaningful only if HACKDIR is defined. + */ +/* #define SECURE */ /* do setuid(getuid()) after chdir() */ + +/* + * If it is desirable to limit the number of people that can play Hack + * simultaneously, define HACKDIR, SECURE and MAX_NR_OF_PLAYERS. + * #define MAX_NR_OF_PLAYERS 6 + */ +#endif /* CHDIR */ + + + +/* + * Section 3: Definitions that may vary with system type. + * For example, both schar and uchar should be short ints on + * the AT&T 3B2/3B5/etc. family. + */ + +/* + * Uncomment the following line if your compiler doesn't understand the + * 'void' type (and thus would give all sorts of compile errors without + * this definition). + */ +/* #define NOVOID */ /* define if no "void" data type. */ + +/* + * Uncomment the following line if your compiler falsely claims to be + * a standard C compiler (i.e., defines __STDC__ without cause). + * Examples are Apollo's cc (in some versions) and possibly SCO UNIX's rcc. + */ +/* #define NOTSTDC */ /* define for lying compilers */ + +#include "tradstdc.h" + +/* + * type schar: small signed integers (8 bits suffice) (eg. TOS) + * + * typedef char schar; + * + * will do when you have signed characters; otherwise use + * + * typedef short int schar; + */ +#ifdef AZTEC +# define schar char +#else +typedef signed char schar; +#endif + +/* + * type uchar: small unsigned integers (8 bits suffice - but 7 bits do not) + * + * typedef unsigned char uchar; + * + * will be satisfactory if you have an "unsigned char" type; + * otherwise use + * + * typedef unsigned short int uchar; + */ +#ifndef _AIX32 /* identical typedef in system file causes trouble */ +typedef unsigned char uchar; +#endif + +/* + * Various structures have the option of using bitfields to save space. + * If your C compiler handles bitfields well (e.g., it can initialize structs + * containing bitfields), you can define BITFIELDS. Otherwise, the game will + * allocate a separate character for each bitfield. (The bitfields used never + * have more than 7 bits, and most are only 1 bit.) + */ +#define BITFIELDS /* Good bitfield handling */ + +/* #define STRNCMPI */ /* compiler/library has the strncmpi function */ + +/* + * There are various choices for the NetHack vision system. There is a + * choice of two algorithms with the same behavior. Defining VISION_TABLES + * creates huge (60K) tables at compile time, drastically increasing data + * size, but runs slightly faster than the alternate algorithm. (MSDOS in + * particular cannot tolerate the increase in data size; other systems can + * flip a coin weighted to local conditions.) + * + * If VISION_TABLES is not defined, things will be faster if you can use + * MACRO_CPATH. Some cpps, however, cannot deal with the size of the + * functions that have been macroized. + */ + +/* #define VISION_TABLES */ /* use vision tables generated at compile time */ +#ifndef VISION_TABLES +# ifndef NO_MACRO_CPATH +# define MACRO_CPATH /* use clear_path macros instead of functions */ +# endif +#endif + +/* + * Section 4: THE FUN STUFF!!! + * + * Conditional compilation of special options are controlled here. + * If you define the following flags, you will add not only to the + * complexity of the game but also to the size of the load module. + */ + +/* dungeon features */ +#define SINKS /* Kitchen sinks - Janet Walz */ +/* dungeon levels */ +#define WALLIFIED_MAZE /* Fancy mazes - Jean-Christophe Collet */ +#define REINCARNATION /* Special Rogue-like levels */ +/* monsters & objects */ +#define KOPS /* Keystone Kops by Scott R. Turner */ +#define SEDUCE /* Succubi/incubi seduction, by KAA, suggested by IM */ +#define STEED /* Riding steeds */ +#define TOURIST /* Tourist players with cameras and Hawaiian shirts */ +/* difficulty */ +#define ELBERETH /* Engraving the E-word repels monsters */ +/* I/O */ +#define REDO /* support for redoing last command - DGK */ +#if !defined(MAC) +# define CLIPPING /* allow smaller screens -- ERS */ +#endif + +#ifdef REDO +# define DOAGAIN '\001' /* ^A, the "redo" key used in cmd.c and getline.c */ +#endif + +#define EXP_ON_BOTL /* Show experience on bottom line */ +/* #define SCORE_ON_BOTL */ /* added by Gary Erickson (erickson@ucivax) */ + +/* + * Section 5: EXPERIMENTAL STUFF + * + * Conditional compilation of new or experimental options are controlled here. + * Enable any of these at your own risk -- there are almost certainly + * bugs left here. + */ + +/*#define GOLDOBJ */ /* Gold is kept on obj chains - Helge Hafting */ +/*#define AUTOPICKUP_EXCEPTIONS */ /* exceptions to autopickup */ + +/* End of Section 5 */ + +#include "global.h" /* Define everything else according to choices above */ + +#endif /* CONFIG_H */ diff --git a/include/config1.h b/include/config1.h new file mode 100644 index 0000000..d17225f --- /dev/null +++ b/include/config1.h @@ -0,0 +1,204 @@ +/* SCCS Id: @(#)config1.h 3.4 1999/12/05 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef CONFIG1_H +#define CONFIG1_H + +/* + * MS DOS - compilers + * + * Microsoft C auto-defines MSDOS, + * Borland C auto-defines __MSDOS__, + * DJGPP auto-defines MSDOS. + */ + +/* #define MSDOS */ /* use if not defined by compiler or cases below */ + +#ifdef __MSDOS__ /* for Borland C */ +# ifndef MSDOS +# define MSDOS +# endif +#endif + +#ifdef __TURBOC__ +# define __MSC /* increase Borland C compatibility in libraries */ +#endif + +#ifdef MSDOS +# undef UNIX +#endif + +/* + * Mac Stuff. + */ +#ifdef macintosh /* Auto-defined symbol for MPW compilers (sc and mrc) */ +# define MAC +#endif + +#ifdef THINK_C /* Think C auto-defined symbol */ +# define MAC +# define NEED_VARARGS +#endif + +#ifdef __MWERKS__ /* defined by Metrowerks' Codewarrior compiler */ +# ifndef __BEOS__ /* BeOS */ +# define MAC +# endif +# define NEED_VARARGS +# define USE_STDARG +#endif + +#if defined(MAC) || defined(__BEOS__) +# define DLB +# undef UNIX +#endif + +#ifdef __BEOS__ +# define NEED_VARARGS +#endif + + +/* + * Amiga setup. + */ +#ifdef AZTEC_C /* Manx auto-defines this */ +# ifdef MCH_AMIGA /* Manx auto-defines this for AMIGA */ +# ifndef AMIGA +#define AMIGA /* define for Commodore-Amiga */ +# endif /* (SAS/C auto-defines AMIGA) */ +#define AZTEC_50 /* define for version 5.0 of manx */ +# endif +#endif +#ifdef __SASC_60 +# define NEARDATA __near /* put some data close */ +#else +# ifdef _DCC +# define NEARDATA __near /* put some data close */ +# else +# define NEARDATA +# endif +#endif +#ifdef AMIGA +# define NEED_VARARGS +# undef UNIX +# define DLB +# define HACKDIR "NetHack:" +# define NO_MACRO_CPATH +#endif + +/* + * Atari auto-detection + */ + +#ifdef atarist +# undef UNIX +# ifndef TOS +# define TOS +# endif +#else +# ifdef __MINT__ +# undef UNIX +# ifndef TOS +# define TOS +# endif +# endif +#endif + +/* + * Windows NT Autodetection + */ +#ifdef _WIN32_WCE +#define WIN_CE +# ifndef WIN32 +# define WIN32 +# endif +#endif + +#ifdef WIN32 +# undef UNIX +# undef MSDOS +# define NHSTDC +# define USE_STDARG +# define NEED_VARARGS + +#ifndef WIN_CE +# define STRNCMPI +# define STRCMPI +#endif + +#endif + + +#if defined(__linux__) && defined(__GNUC__) && !defined(_GNU_SOURCE) +/* ensure _GNU_SOURCE is defined before including any system headers */ +# define _GNU_SOURCE +#endif + +#ifdef VMS /* really old compilers need special handling, detected here */ +# undef UNIX +# ifdef __DECC +# ifndef __DECC_VER /* buggy early versions want widened prototypes */ +# define NOTSTDC /* except when typedefs are involved */ +# define USE_VARARGS +# else +# define NHSTDC +# define USE_STDARG +# define POSIX_TYPES +# define _DECC_V4_SOURCE /* avoid some incompatible V5.x changes */ +# endif +# undef __HIDE_FORBIDDEN_NAMES /* need non-ANSI library support functions */ +# else +# ifdef VAXC /* must use CC/DEFINE=ANCIENT_VAXC for vaxc v2.2 or older */ +# ifdef ANCIENT_VAXC /* vaxc v2.2 and earlier [lots of warnings to come] */ +# define KR1ED /* simulate defined() */ +# define USE_VARARGS +# else /* vaxc v2.3,2.4,or 3.x, or decc in vaxc mode */ +# if defined(USE_PROTOTYPES) /* this breaks 2.2 (*forces* use of ANCIENT)*/ +# define __STDC__ 0 /* vaxc is not yet ANSI compliant, but close enough */ +# define signed /* well, almost close enough */ +#include +# define UNWIDENED_PROTOTYPES +# endif +# define USE_STDARG +# endif +# endif /*VAXC*/ +# endif /*__DECC*/ +# ifdef VERYOLD_VMS /* v4.5 or earlier; no longer available for testing */ +# define USE_OLDARGS /* is there, vprintf & vsprintf aren't */ +# ifdef USE_VARARGS +# undef USE_VARARGS +# endif +# ifdef USE_STDARG +# undef USE_STDARG +# endif +# endif +#endif /*VMS*/ + +#ifdef vax +/* just in case someone thinks a DECstation is a vax. It's not, it's a mips */ +# ifdef ULTRIX_PROTO +# undef ULTRIX_PROTO +# endif +# ifdef ULTRIX_CC20 +# undef ULTRIX_CC20 +# endif +#endif + +#ifdef KR1ED /* For compilers which cannot handle defined() */ +#define defined(x) (-x-1 != -1) +/* Because: + * #define FOO => FOO={} => defined( ) => (-1 != - - 1) => 1 + * #define FOO 1 or on command-line -DFOO + * => defined(1) => (-1 != - 1 - 1) => 1 + * if FOO isn't defined, FOO=0. But some compilers default to 0 instead of 1 + * for -DFOO, oh well. + * => defined(0) => (-1 != - 0 - 1) => 0 + * + * But: + * defined("") => (-1 != - "" - 1) + * [which is an unavoidable catastrophe.] + */ +#endif + +#endif /* CONFIG1_H */ diff --git a/include/coord.h b/include/coord.h new file mode 100644 index 0000000..3322820 --- /dev/null +++ b/include/coord.h @@ -0,0 +1,12 @@ +/* SCCS Id: @(#)coord.h 3.4 1990/02/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef COORD_H +#define COORD_H + +typedef struct nhcoord { + xchar x,y; +} coord; + +#endif /* COORD_H */ diff --git a/include/decl.h b/include/decl.h new file mode 100644 index 0000000..76f9533 --- /dev/null +++ b/include/decl.h @@ -0,0 +1,390 @@ +/* SCCS Id: @(#)decl.h 3.4 2001/12/10 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef DECL_H +#define DECL_H + +#define E extern + +E int NDECL((*occupation)); +E int NDECL((*afternmv)); + +E const char *hname; +E int hackpid; +#if defined(UNIX) || defined(VMS) +E int locknum; +#endif +#ifdef DEF_PAGER +E char *catmore; +#endif /* DEF_PAGER */ + +E char SAVEF[]; +#ifdef MICRO +E char SAVEP[]; +#endif + +E NEARDATA int bases[MAXOCLASSES]; + +E NEARDATA int multi; +#if 0 +E NEARDATA int warnlevel; +#endif +E NEARDATA int nroom; +E NEARDATA int nsubroom; +E NEARDATA int occtime; + +#define WARNCOUNT 6 /* number of different warning levels */ +E uchar warnsyms[WARNCOUNT]; + +E int x_maze_max, y_maze_max; +E int otg_temp; + +#ifdef REDO +E NEARDATA int in_doagain; +#endif + +E struct dgn_topology { /* special dungeon levels for speed */ + d_level d_oracle_level; + d_level d_bigroom_level; /* unused */ +#ifdef REINCARNATION + d_level d_rogue_level; +#endif + d_level d_medusa_level; + d_level d_stronghold_level; + d_level d_valley_level; + d_level d_wiz1_level; + d_level d_wiz2_level; + d_level d_wiz3_level; + d_level d_juiblex_level; + d_level d_orcus_level; + d_level d_baalzebub_level; /* unused */ + d_level d_asmodeus_level; /* unused */ + d_level d_portal_level; /* only in goto_level() [do.c] */ + d_level d_sanctum_level; + d_level d_earth_level; + d_level d_water_level; + d_level d_fire_level; + d_level d_air_level; + d_level d_astral_level; + xchar d_tower_dnum; + xchar d_sokoban_dnum; + xchar d_mines_dnum, d_quest_dnum; + d_level d_qstart_level, d_qlocate_level, d_nemesis_level; + d_level d_knox_level; +} dungeon_topology; +/* macros for accesing the dungeon levels by their old names */ +#define oracle_level (dungeon_topology.d_oracle_level) +#define bigroom_level (dungeon_topology.d_bigroom_level) +#ifdef REINCARNATION +#define rogue_level (dungeon_topology.d_rogue_level) +#endif +#define medusa_level (dungeon_topology.d_medusa_level) +#define stronghold_level (dungeon_topology.d_stronghold_level) +#define valley_level (dungeon_topology.d_valley_level) +#define wiz1_level (dungeon_topology.d_wiz1_level) +#define wiz2_level (dungeon_topology.d_wiz2_level) +#define wiz3_level (dungeon_topology.d_wiz3_level) +#define juiblex_level (dungeon_topology.d_juiblex_level) +#define orcus_level (dungeon_topology.d_orcus_level) +#define baalzebub_level (dungeon_topology.d_baalzebub_level) +#define asmodeus_level (dungeon_topology.d_asmodeus_level) +#define portal_level (dungeon_topology.d_portal_level) +#define sanctum_level (dungeon_topology.d_sanctum_level) +#define earth_level (dungeon_topology.d_earth_level) +#define water_level (dungeon_topology.d_water_level) +#define fire_level (dungeon_topology.d_fire_level) +#define air_level (dungeon_topology.d_air_level) +#define astral_level (dungeon_topology.d_astral_level) +#define tower_dnum (dungeon_topology.d_tower_dnum) +#define sokoban_dnum (dungeon_topology.d_sokoban_dnum) +#define mines_dnum (dungeon_topology.d_mines_dnum) +#define quest_dnum (dungeon_topology.d_quest_dnum) +#define qstart_level (dungeon_topology.d_qstart_level) +#define qlocate_level (dungeon_topology.d_qlocate_level) +#define nemesis_level (dungeon_topology.d_nemesis_level) +#define knox_level (dungeon_topology.d_knox_level) + +E NEARDATA stairway dnstair, upstair; /* stairs up and down */ +#define xdnstair (dnstair.sx) +#define ydnstair (dnstair.sy) +#define xupstair (upstair.sx) +#define yupstair (upstair.sy) + +E NEARDATA stairway dnladder, upladder; /* ladders up and down */ +#define xdnladder (dnladder.sx) +#define ydnladder (dnladder.sy) +#define xupladder (upladder.sx) +#define yupladder (upladder.sy) + +E NEARDATA stairway sstairs; + +E NEARDATA dest_area updest, dndest; /* level-change destination areas */ + +E NEARDATA coord inv_pos; +E NEARDATA dungeon dungeons[]; +E NEARDATA s_level *sp_levchn; +#define dunlev_reached(x) (dungeons[(x)->dnum].dunlev_ureached) + +#include "quest.h" +E struct q_score quest_status; + +E NEARDATA char pl_character[PL_CSIZ]; +E NEARDATA char pl_race; /* character's race */ + +E NEARDATA char pl_fruit[PL_FSIZ]; +E NEARDATA int current_fruit; +E NEARDATA struct fruit *ffruit; + +E NEARDATA char tune[6]; + +#define MAXLINFO (MAXDUNGEON * MAXLEVEL) +E struct linfo level_info[MAXLINFO]; + +E NEARDATA struct sinfo { + int gameover; /* self explanatory? */ + int stopprint; /* inhibit further end of game disclosure */ +#if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32) + int done_hup; /* SIGHUP or moral equivalent received + * -- no more screen output */ +#endif + int something_worth_saving; /* in case of panic */ + int panicking; /* `panic' is in progress */ +#if defined(VMS) || defined(WIN32) + int exiting; /* an exit handler is executing */ +#endif + int in_impossible; +#ifdef PANICLOG + int in_paniclog; +#endif +} program_state; + +E boolean restoring; + +E const char quitchars[]; +E const char vowels[]; +E const char ynchars[]; +E const char ynqchars[]; +E const char ynaqchars[]; +E const char ynNaqchars[]; +E NEARDATA long yn_number; + +E const char disclosure_options[]; + +E NEARDATA int smeq[]; +E NEARDATA int doorindex; +E NEARDATA char *save_cm; +#define KILLED_BY_AN 0 +#define KILLED_BY 1 +#define NO_KILLER_PREFIX 2 +E NEARDATA int killer_format; +E const char *killer; +E const char *delayed_killer; +#ifdef GOLDOBJ +E long done_money; +#endif +E char killer_buf[BUFSZ]; +E const char *configfile; +E NEARDATA char plname[PL_NSIZ]; +E NEARDATA char dogname[]; +E NEARDATA char catname[]; +E NEARDATA char horsename[]; +E char preferred_pet; +E const char *occtxt; /* defined when occupation != NULL */ +E const char *nomovemsg; +E const char nul[]; +E char lock[]; + +E const char sdir[], ndir[]; +E const schar xdir[], ydir[], zdir[]; + +E NEARDATA schar tbx, tby; /* set in mthrowu.c */ + +E NEARDATA struct multishot { int n, i; short o; boolean s; } m_shot; + +E NEARDATA struct dig_info { /* apply.c, hack.c */ + int effort; + d_level level; + coord pos; + long lastdigtime; + boolean down, chew, warned, quiet; +} digging; + +E NEARDATA long moves, monstermoves; +E NEARDATA long wailmsg; + +E NEARDATA boolean in_mklev; +E NEARDATA boolean stoned; +E NEARDATA boolean unweapon; +E NEARDATA boolean mrg_to_wielded; +E NEARDATA struct obj *current_wand; + +E NEARDATA boolean in_steed_dismounting; + +E const int shield_static[]; + +#include "spell.h" +E NEARDATA struct spell spl_book[]; /* sized in decl.c */ + +#include "color.h" +#ifdef TEXTCOLOR +E const int zapcolors[]; +#endif + +E const char def_oc_syms[MAXOCLASSES]; /* default class symbols */ +E uchar oc_syms[MAXOCLASSES]; /* current class symbols */ +E const char def_monsyms[MAXMCLASSES]; /* default class symbols */ +E uchar monsyms[MAXMCLASSES]; /* current class symbols */ + +#include "obj.h" +E NEARDATA struct obj *invent, + *uarm, *uarmc, *uarmh, *uarms, *uarmg, *uarmf, +#ifdef TOURIST + *uarmu, /* under-wear, so to speak */ +#endif + *uskin, *uamul, *uleft, *uright, *ublindf, + *uwep, *uswapwep, *uquiver; + +E NEARDATA struct obj *uchain; /* defined only when punished */ +E NEARDATA struct obj *uball; +E NEARDATA struct obj *migrating_objs; +E NEARDATA struct obj *billobjs; +E NEARDATA struct obj zeroobj; /* init'd and defined in decl.c */ + +#include "you.h" +E NEARDATA struct you u; + +#include "onames.h" +#ifndef PM_H /* (pm.h has already been included via youprop.h) */ +#include "pm.h" +#endif + +E NEARDATA struct monst youmonst; /* init'd and defined in decl.c */ +E NEARDATA struct monst *mydogs, *migrating_mons; + +E NEARDATA struct mvitals { + uchar born; + uchar died; + uchar mvflags; +} mvitals[NUMMONS]; + +E NEARDATA struct c_color_names { + const char *const c_black, *const c_amber, *const c_golden, + *const c_light_blue,*const c_red, *const c_green, + *const c_silver, *const c_blue, *const c_purple, + *const c_white; +} c_color_names; +#define NH_BLACK c_color_names.c_black +#define NH_AMBER c_color_names.c_amber +#define NH_GOLDEN c_color_names.c_golden +#define NH_LIGHT_BLUE c_color_names.c_light_blue +#define NH_RED c_color_names.c_red +#define NH_GREEN c_color_names.c_green +#define NH_SILVER c_color_names.c_silver +#define NH_BLUE c_color_names.c_blue +#define NH_PURPLE c_color_names.c_purple +#define NH_WHITE c_color_names.c_white + +/* The names of the colors used for gems, etc. */ +E const char *c_obj_colors[]; + +E struct c_common_strings { + const char *const c_nothing_happens, *const c_thats_enough_tries, + *const c_silly_thing_to, *const c_shudder_for_moment, + *const c_something, *const c_Something, + *const c_You_can_move_again, + *const c_Never_mind, *c_vision_clears, + *const c_the_your[2]; +} c_common_strings; +#define nothing_happens c_common_strings.c_nothing_happens +#define thats_enough_tries c_common_strings.c_thats_enough_tries +#define silly_thing_to c_common_strings.c_silly_thing_to +#define shudder_for_moment c_common_strings.c_shudder_for_moment +#define something c_common_strings.c_something +#define Something c_common_strings.c_Something +#define You_can_move_again c_common_strings.c_You_can_move_again +#define Never_mind c_common_strings.c_Never_mind +#define vision_clears c_common_strings.c_vision_clears +#define the_your c_common_strings.c_the_your + +/* material strings */ +E const char *materialnm[]; + +/* Monster name articles */ +#define ARTICLE_NONE 0 +#define ARTICLE_THE 1 +#define ARTICLE_A 2 +#define ARTICLE_YOUR 3 + +/* Monster name suppress masks */ +#define SUPPRESS_IT 0x01 +#define SUPPRESS_INVISIBLE 0x02 +#define SUPPRESS_HALLUCINATION 0x04 +#define SUPPRESS_SADDLE 0x08 +#define EXACT_NAME 0x0F + +/* Vision */ +E NEARDATA boolean vision_full_recalc; /* TRUE if need vision recalc */ +E NEARDATA char **viz_array; /* could see/in sight row pointers */ + +/* Window system stuff */ +E NEARDATA winid WIN_MESSAGE, WIN_STATUS; +E NEARDATA winid WIN_MAP, WIN_INVEN; +E char toplines[]; +#ifndef TCAP_H +E struct tc_gbl_data { /* also declared in tcap.h */ + char *tc_AS, *tc_AE; /* graphics start and end (tty font swapping) */ + int tc_LI, tc_CO; /* lines and columns */ +} tc_gbl_data; +#define AS tc_gbl_data.tc_AS +#define AE tc_gbl_data.tc_AE +#define LI tc_gbl_data.tc_LI +#define CO tc_gbl_data.tc_CO +#endif + +/* xxxexplain[] is in drawing.c */ +E const char * const monexplain[], invisexplain[], * const objexplain[], * const oclass_names[]; + +/* Some systems want to use full pathnames for some subsets of file names, + * rather than assuming that they're all in the current directory. This + * provides all the subclasses that seem reasonable, and sets up for all + * prefixes being null. Port code can set those that it wants. + */ +#define HACKPREFIX 0 +#define LEVELPREFIX 1 +#define SAVEPREFIX 2 +#define BONESPREFIX 3 +#define DATAPREFIX 4 /* this one must match hardcoded value in dlb.c */ +#define SCOREPREFIX 5 +#define LOCKPREFIX 6 +#define CONFIGPREFIX 7 +#define TROUBLEPREFIX 8 +#define PREFIX_COUNT 9 +/* used in files.c; xxconf.h can override if needed */ +# ifndef FQN_MAX_FILENAME +#define FQN_MAX_FILENAME 512 +# endif + +#if defined(NOCWD_ASSUMPTIONS) || defined(VAR_PLAYGROUND) +/* the bare-bones stuff is unconditional above to simplify coding; for + * ports that actually use prefixes, add some more localized things + */ +#define PREFIXES_IN_USE +#endif + +E char *fqn_prefix[PREFIX_COUNT]; +#ifdef PREFIXES_IN_USE +E char *fqn_prefix_names[PREFIX_COUNT]; +#endif + +#ifdef AUTOPICKUP_EXCEPTIONS +struct autopickup_exception { + char *pattern; + boolean grab; + struct autopickup_exception *next; +}; +#endif /* AUTOPICKUP_EXCEPTIONS */ + +#undef E + +#endif /* DECL_H */ diff --git a/include/def_os2.h b/include/def_os2.h new file mode 100644 index 0000000..7ba40e4 --- /dev/null +++ b/include/def_os2.h @@ -0,0 +1,212 @@ +/* SCCS Id: @(#)def_os2.h 3.4 1993/01/19 */ +/* Copyright (c) Timo Hakulinen, 1990, 1991, 1992, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Only a small portion of all OS/2 defines are needed, so the + * actual include files often need not be used. In fact, + * including the full headers may stall the compile in DOS. + */ + +#ifdef OS2_USESYSHEADERS + +# define INCL_NOPMAPI +# define INCL_DOSFILEMGR +# define INCL_DOS +# define INCL_SUB + +#include + +#else + +typedef char CHAR; +typedef void VOID; + +typedef unsigned char UCHAR; +typedef unsigned short USHORT; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef unsigned char BYTE; + +# ifdef OS2_32BITAPI + +typedef unsigned long SHANDLE; +typedef USHORT HKBD; +typedef USHORT HVIO; + +# define CCHMAXPATHCOMP 256 + +# ifdef OS2_CSET2 +# define API16 _Far16 _Pascal +# define DAT16 +# define API32 _System +# define KbdGetStatus KBD16GETSTATUS +# define KbdSetStatus KBD16SETSTATUS +# define KbdCharIn KBD16CHARIN +# define KbdPeek KBD16PEEK +# define VioGetMode VIO16GETMODE +# define VioSetCurPos VIO16SETCURPOS +# else +# define API16 +# define DAT16 +# define API32 +# endif + +# define DAT + +# else /* OS2_32BITAPI */ + +typedef unsigned short SHANDLE; +typedef SHANDLE HKBD; +typedef SHANDLE HVIO; + +# define CCHMAXPATHCOMP 13 + +# ifdef OS2_MSC +# define API16 pascal far +# define DAT16 +# endif + +# define DAT DAT16 + +# endif /* OS2_32BITAPI */ + +typedef USHORT * DAT16 PUSHORT; +typedef BYTE * DAT16 PBYTE; +typedef ULONG * DAT PULONG; +typedef VOID * DAT PVOID; + +typedef SHANDLE HDIR; +typedef HDIR * DAT PHDIR; + +typedef char * DAT16 PCH; +typedef char * DAT PSZ; + +/* all supported compilers understand this */ + +# pragma pack(2) + +typedef struct { + UCHAR chChar; + UCHAR chScan; + UCHAR fbStatus; + UCHAR bNlsShift; + USHORT fsState; + ULONG time; +} KBDKEYINFO; + +typedef KBDKEYINFO * DAT16 PKBDKEYINFO; + +/* File time and date types */ + +typedef struct { + UINT twosecs : 5; + UINT minutes : 6; + UINT hours : 5; +} FTIME; + +typedef struct { + UINT day : 5; + UINT month : 4; + UINT year : 7; +} FDATE; + +# ifdef OS2_32BITAPI + +typedef struct { + ULONG oNextEntryOffset; + FDATE fdateCreation; + FTIME ftimeCreation; + FDATE fdateLastAccess; + FTIME ftimeLastAccess; + FDATE fdateLastWrite; + FTIME ftimeLastWrite; + ULONG cbFile; + ULONG cbFileAlloc; + ULONG attrFile; + UCHAR cchName; + CHAR achName[CCHMAXPATHCOMP]; +} FILEFINDBUF3; + +# else + +typedef struct { + FDATE fdateCreation; + FTIME ftimeCreation; + FDATE fdateLastAccess; + FTIME ftimeLastAccess; + FDATE fdateLastWrite; + FTIME ftimeLastWrite; + ULONG cbFile; + ULONG cbFileAlloc; + USHORT attrFile; + UCHAR cchName; + CHAR achName[CCHMAXPATHCOMP]; +} FILEFINDBUF; + +typedef FILEFINDBUF * DAT16 PFILEFINDBUF; + +# endif /* OS2_32BITAPI */ + +typedef struct { + ULONG idFileSystem; + ULONG cSectorUnit; + ULONG cUnit; + ULONG cUnitAvail; + USHORT cbSector; +} FSALLOCATE; + +typedef struct { + USHORT cb; + USHORT fsMask; + USHORT chTurnAround; + USHORT fsInterim; + USHORT fsState; +} KBDINFO; + +typedef KBDINFO * DAT16 PKBDINFO; + +typedef struct { + USHORT cb; + UCHAR fbType; + UCHAR color; + USHORT col; + USHORT row; + USHORT hres; + USHORT vres; + UCHAR fmt_ID; + UCHAR attrib; + ULONG buf_addr; + ULONG buf_length; + ULONG full_length; + ULONG partial_length; + PCH ext_data_addr; +} VIOMODEINFO; + +typedef VIOMODEINFO * DAT16 PVIOMODEINFO; + +# pragma pack() + +/* OS2 API functions */ + +USHORT API16 KbdGetStatus(PKBDINFO, HKBD); +USHORT API16 KbdSetStatus(PKBDINFO, HKBD); +USHORT API16 KbdCharIn(PKBDKEYINFO, USHORT, HKBD); +USHORT API16 KbdPeek(PKBDKEYINFO, HKBD); + +USHORT API16 VioGetMode(PVIOMODEINFO, HVIO); +USHORT API16 VioSetCurPos(USHORT, USHORT, HVIO); + +# ifdef OS2_32BITAPI +ULONG API32 DosQueryFSInfo(ULONG, ULONG, PVOID, ULONG); +ULONG API32 DosFindFirst(PSZ, PHDIR, ULONG, PVOID, ULONG, PULONG, ULONG); +ULONG API32 DosFindNext(HDIR, PVOID, ULONG, PULONG); +ULONG API32 DosSetDefaultDisk(ULONG); +# else +USHORT API16 DosQFSInfo(USHORT, USHORT, PBYTE, USHORT); +USHORT API16 DosFindFirst(PSZ, PHDIR, USHORT, PFILEFINDBUF, USHORT, PUSHORT, ULONG); +USHORT API16 DosFindNext(HDIR, PFILEFINDBUF, USHORT, PUSHORT); +USHORT API16 DosSelectDisk(USHORT); +# endif /* OS2_32BITAPI */ + +#endif /* OS2_USESYSHEADERS */ diff --git a/include/dgn_file.h b/include/dgn_file.h new file mode 100644 index 0000000..2fa3214 --- /dev/null +++ b/include/dgn_file.h @@ -0,0 +1,76 @@ +/* SCCS Id: @(#)dgn_file.h 3.4 1993/01/17 */ +/* Copyright (c) 1989 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef DGN_FILE_H +#define DGN_FILE_H + +#ifndef ALIGN_H +#include "align.h" +#endif + +/* + * Structures manipulated by the dungeon loader & compiler + */ + +struct couple { + short base, rand; +}; + +struct tmpdungeon { + char name[24], + protoname[24]; + struct couple lev; + int flags, + chance, + levels, + branches, + entry_lev; /* entry level for this dungeon */ + char boneschar; +}; + +struct tmplevel { + char name[24]; + struct couple lev; + int chance, rndlevs, chain, flags; + char boneschar; +}; + +struct tmpbranch { + char name[24]; /* destination dungeon name */ + struct couple lev; + int chain; /* index into tmplevel array (chained branch)*/ + int type; /* branch type (see below) */ + int up; /* branch is up or down */ +}; + +/* + * Values for type for tmpbranch structure. + */ +#define TBR_STAIR 0 /* connection with both ends having a staircase */ +#define TBR_NO_UP 1 /* connection with no up staircase */ +#define TBR_NO_DOWN 2 /* connection with no down staircase */ +#define TBR_PORTAL 3 /* portal connection */ + +/* + * Flags that map into the dungeon flags bitfields. + */ +#define TOWN 1 /* levels only */ +#define HELLISH 2 +#define MAZELIKE 4 +#define ROGUELIKE 8 + +#define D_ALIGN_NONE 0 +#define D_ALIGN_CHAOTIC (AM_CHAOTIC << 4) +#define D_ALIGN_NEUTRAL (AM_NEUTRAL << 4) +#define D_ALIGN_LAWFUL (AM_LAWFUL << 4) + +#define D_ALIGN_MASK 0x70 + +/* + * Max number of prototype levels and branches. + */ +#define LEV_LIMIT 50 +#define BRANCH_LIMIT 32 + +#endif /* DGN_FILE_H */ diff --git a/include/display.h b/include/display.h new file mode 100644 index 0000000..d457e4e --- /dev/null +++ b/include/display.h @@ -0,0 +1,390 @@ +/* SCCS Id: @(#)display.h 3.4 1999/11/30 */ +/* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ +/* and Dave Cohrs, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#ifndef VISION_H +#include "vision.h" +#endif + +#ifndef MONDATA_H +#include "mondata.h" /* for mindless() */ +#endif + +#ifndef INVISIBLE_OBJECTS +#define vobj_at(x,y) (level.objects[x][y]) +#endif + +/* + * sensemon() + * + * Returns true if the hero can sense the given monster. This includes + * monsters that are hiding or mimicing other monsters. + */ +#define tp_sensemon(mon) ( /* The hero can always sense a monster IF: */\ + (!mindless(mon->data)) && /* 1. the monster has a brain to sense AND */\ + ((Blind && Blind_telepat) || /* 2a. hero is blind and telepathic OR */\ + /* 2b. hero is using a telepathy inducing */\ + /* object and in range */\ + (Unblind_telepat && \ + (distu(mon->mx, mon->my) <= (BOLT_LIM * BOLT_LIM)))) \ +) + +#define sensemon(mon) (tp_sensemon(mon) || Detect_monsters || MATCH_WARN_OF_MON(mon)) + +/* + * mon_warning() is used to warn of any dangerous monsters in your + * vicinity, and a glyph representing the warning level is displayed. + */ + +#define mon_warning(mon) (Warning && !(mon)->mpeaceful && \ + (distu((mon)->mx, (mon)->my) < 100) && \ + (((int) ((mon)->m_lev / 4)) >= flags.warnlevel)) + +/* + * mon_visible() + * + * Returns true if the hero can see the monster. It is assumed that the + * hero can physically see the location of the monster. The function + * vobj_at() returns a pointer to an object that the hero can see there. + * Infravision is not taken into account. + */ +#define mon_visible(mon) ( /* The hero can see the monster */\ + /* IF the monster */\ + (!mon->minvis || See_invisible) && /* 1. is not invisible AND */\ + (!mon->mundetected) && /* 2. not an undetected hider */\ + (!(mon->mburied || u.uburied)) /* 3. neither you or it is buried */\ +) + +/* + * see_with_infrared() + * + * This function is true if the player can see a monster using infravision. + * The caller must check for invisibility (invisible monsters are also + * invisible to infravision), because this is usually called from within + * canseemon() or canspotmon() which already check that. + */ +#define see_with_infrared(mon) (!Blind && Infravision && infravisible(mon->data) && couldsee(mon->mx, mon->my)) + + +/* + * canseemon() + * + * This is the globally used canseemon(). It is not called within the display + * routines. Like mon_visible(), but it checks to see if the hero sees the + * location instead of assuming it. (And also considers worms.) + */ +#define canseemon(mon) ((mon->wormno ? worm_known(mon) : \ + (cansee(mon->mx, mon->my) || see_with_infrared(mon))) \ + && mon_visible(mon)) + + +/* + * canspotmon(mon) + * + * This function checks whether you can either see a monster or sense it by + * telepathy, and is what you usually call for monsters about which nothing is + * known. + */ +#define canspotmon(mon) \ + (canseemon(mon) || sensemon(mon)) + +/* knowninvisible(mon) + * This one checks to see if you know a monster is both there and invisible. + * 1) If you can see the monster and have see invisible, it is assumed the + * monster is transparent, but visible in some manner. (Earlier versions of + * Nethack were really inconsistent on this.) + * 2) If you can't see the monster, but can see its location and you have + * telepathy that works when you can see, you can tell that there is a + * creature in an apparently empty spot. + * Infravision is not relevant; we assume that invisible monsters are also + * invisible to infravision. + */ +#define knowninvisible(mon) \ + (mtmp->minvis && \ + ((cansee(mon->mx, mon->my) && (See_invisible || Detect_monsters)) || \ + (!Blind && (HTelepat & ~INTRINSIC) && \ + distu(mon->mx, mon->my) <= (BOLT_LIM * BOLT_LIM) \ + ) \ + ) \ + ) + +/* + * is_safepet(mon) + * + * A special case check used in attack() and domove(). Placing the + * definition here is convenient. + */ +#define is_safepet(mon) \ + (mon && mon->mtame && canspotmon(mon) && flags.safe_dog \ + && !Confusion && !Hallucination && !Stunned) + + +/* + * canseeself() + * senseself() + * + * This returns true if the hero can see her/himself. + * + * The u.uswallow check assumes that you can see yourself even if you are + * invisible. If not, then we don't need the check. + */ +#define canseeself() (Blind || u.uswallow || (!Invisible && !u.uundetected)) +#define senseself() (canseeself() || Unblind_telepat || Detect_monsters) + +/* + * random_monster() + * random_object() + * random_trap() + * + * Respectively return a random monster, object, or trap number. + */ +#define random_monster() rn2(NUMMONS) +#define random_object() rn1(NUM_OBJECTS-1,1) +#define random_trap() rn1(TRAPNUM-1,1) + +/* + * what_obj() + * what_mon() + * what_trap() + * + * If hallucinating, choose a random object/monster, otherwise, use the one + * given. + */ +#define what_obj(obj) (Hallucination ? random_object() : obj) +#define what_mon(mon) (Hallucination ? random_monster() : mon) +#define what_trap(trp) (Hallucination ? random_trap() : trp) + +/* + * covers_objects() + * covers_traps() + * + * These routines are true if what is really at the given location will + * "cover" any objects or traps that might be there. + */ +#define covers_objects(xx,yy) \ + ((is_pool(xx,yy) && !Underwater) || (levl[xx][yy].typ == LAVAPOOL)) + +#define covers_traps(xx,yy) covers_objects(xx,yy) + + +/* + * tmp_at() control calls. + */ +#define DISP_BEAM (-1) /* Keep all glyphs showing & clean up at end. */ +#define DISP_FLASH (-2) /* Clean up each glyph before displaying new one. */ +#define DISP_ALWAYS (-3) /* Like flash, but still displayed if not visible. */ +#define DISP_CHANGE (-4) /* Change glyph. */ +#define DISP_END (-5) /* Clean up. */ +#define DISP_FREEMEM (-6) /* Free all memory during exit only. */ + + +/* Total number of cmap indices in the sheild_static[] array. */ +#define SHIELD_COUNT 21 + + +/* + * display_self() + * + * Display the hero. It is assumed that all checks necessary to determine + * _if_ the hero can be seen have already been done. + */ +#ifdef STEED +#define maybe_display_usteed (u.usteed && mon_visible(u.usteed)) ? \ + ridden_mon_to_glyph(u.usteed) : +#else +#define maybe_display_usteed /* empty */ +#endif + +#define display_self() \ + show_glyph(u.ux, u.uy, \ + maybe_display_usteed /* else */ \ + youmonst.m_ap_type == M_AP_NOTHING ? \ + hero_glyph : \ + youmonst.m_ap_type == M_AP_FURNITURE ? \ + cmap_to_glyph(youmonst.mappearance) : \ + youmonst.m_ap_type == M_AP_OBJECT ? \ + objnum_to_glyph(youmonst.mappearance) : \ + /* else M_AP_MONSTER */ monnum_to_glyph(youmonst.mappearance)) + +/* + * A glyph is an abstraction that represents a _unique_ monster, object, + * dungeon part, or effect. The uniqueness is important. For example, + * It is not enough to have four (one for each "direction") zap beam glyphs, + * we need a set of four for each beam type. Why go to so much trouble? + * Because it is possible that any given window dependent display driver + * [print_glyph()] can produce something different for each type of glyph. + * That is, a beam of cold and a beam of fire would not only be different + * colors, but would also be represented by different symbols. + * + * Glyphs are grouped for easy accessibility: + * + * monster Represents all the wild (not tame) monsters. Count: NUMMONS. + * + * pet Represents all of the tame monsters. Count: NUMMONS + * + * invisible Invisible monster placeholder. Count: 1 + * + * detect Represents all detected monsters. Count: NUMMONS + * + * corpse One for each monster. Count: NUMMONS + * + * ridden Represents all monsters being ridden. Count: NUMMONS + * + * object One for each object. Count: NUM_OBJECTS + * + * cmap One for each entry in the character map. The character map + * is the dungeon features and other miscellaneous things. + * Count: MAXPCHARS + * + * explosions A set of nine for each of the following seven explosion types: + * dark, noxious, muddy, wet, magical, fiery, frosty. + * The nine positions represent those surrounding the hero. + * Count: MAXEXPCHARS * EXPL_MAX (EXPL_MAX is defined in hack.h) + * + * zap beam A set of four (there are four directions) for each beam type. + * The beam type is shifted over 2 positions and the direction + * is stored in the lower 2 bits. Count: NUM_ZAP << 2 + * + * swallow A set of eight for each monster. The eight positions rep- + * resent those surrounding the hero. The monster number is + * shifted over 3 positions and the swallow position is stored + * in the lower three bits. Count: NUMMONS << 3 + * + * warning A set of six representing the different warning levels. + * + * The following are offsets used to convert to and from a glyph. + */ +#define NUM_ZAP 8 /* number of zap beam types */ + +#define GLYPH_MON_OFF 0 +#define GLYPH_PET_OFF (NUMMONS + GLYPH_MON_OFF) +#define GLYPH_INVIS_OFF (NUMMONS + GLYPH_PET_OFF) +#define GLYPH_DETECT_OFF (1 + GLYPH_INVIS_OFF) +#define GLYPH_BODY_OFF (NUMMONS + GLYPH_DETECT_OFF) +#define GLYPH_RIDDEN_OFF (NUMMONS + GLYPH_BODY_OFF) +#define GLYPH_OBJ_OFF (NUMMONS + GLYPH_RIDDEN_OFF) +#define GLYPH_CMAP_OFF (NUM_OBJECTS + GLYPH_OBJ_OFF) +#define GLYPH_EXPLODE_OFF ((MAXPCHARS - MAXEXPCHARS) + GLYPH_CMAP_OFF) +#define GLYPH_ZAP_OFF ((MAXEXPCHARS * EXPL_MAX) + GLYPH_EXPLODE_OFF) +#define GLYPH_SWALLOW_OFF ((NUM_ZAP << 2) + GLYPH_ZAP_OFF) +#define GLYPH_WARNING_OFF ((NUMMONS << 3) + GLYPH_SWALLOW_OFF) +#define MAX_GLYPH (WARNCOUNT + GLYPH_WARNING_OFF) + +#define NO_GLYPH MAX_GLYPH + +#define GLYPH_INVISIBLE GLYPH_INVIS_OFF + +#define warning_to_glyph(mwarnlev) ((mwarnlev)+GLYPH_WARNING_OFF) +#define mon_to_glyph(mon) ((int) what_mon(monsndx((mon)->data))+GLYPH_MON_OFF) +#define detected_mon_to_glyph(mon) ((int) what_mon(monsndx((mon)->data))+GLYPH_DETECT_OFF) +#define ridden_mon_to_glyph(mon) ((int) what_mon(monsndx((mon)->data))+GLYPH_RIDDEN_OFF) +#define pet_to_glyph(mon) ((int) what_mon(monsndx((mon)->data))+GLYPH_PET_OFF) + +/* This has the unfortunate side effect of needing a global variable */ +/* to store a result. 'otg_temp' is defined and declared in decl.{ch}. */ +#define obj_to_glyph(obj) \ + (Hallucination ? \ + ((otg_temp = random_object()) == CORPSE ? \ + random_monster() + GLYPH_BODY_OFF : \ + otg_temp + GLYPH_OBJ_OFF) : \ + ((obj)->otyp == CORPSE ? \ + (int) (obj)->corpsenm + GLYPH_BODY_OFF : \ + (int) (obj)->otyp + GLYPH_OBJ_OFF)) + +#define cmap_to_glyph(cmap_idx) ((int) (cmap_idx) + GLYPH_CMAP_OFF) +#define explosion_to_glyph(expltype,idx) \ + ((((expltype) * MAXEXPCHARS) + ((idx) - S_explode1)) + GLYPH_EXPLODE_OFF) + +#define trap_to_glyph(trap) \ + cmap_to_glyph(trap_to_defsym(what_trap((trap)->ttyp))) + +/* Not affected by hallucination. Gives a generic body for CORPSE */ +#define objnum_to_glyph(onum) ((int) (onum) + GLYPH_OBJ_OFF) +#define monnum_to_glyph(mnum) ((int) (mnum) + GLYPH_MON_OFF) +#define detected_monnum_to_glyph(mnum) ((int) (mnum) + GLYPH_DETECT_OFF) +#define ridden_monnum_to_glyph(mnum) ((int) (mnum) + GLYPH_RIDDEN_OFF) +#define petnum_to_glyph(mnum) ((int) (mnum) + GLYPH_PET_OFF) + +/* The hero's glyph when seen as a monster. + */ +#define hero_glyph \ + monnum_to_glyph((Upolyd || !iflags.showrace) ? u.umonnum : \ + (flags.female && urace.femalenum != NON_PM) ? urace.femalenum : \ + urace.malenum) + + +/* + * Change the given glyph into it's given type. Note: + * 1) Pets, detected, and ridden monsters are animals and are converted + * to the proper monster number. + * 2) Bodies are all mapped into the generic CORPSE object + * 3) If handed a glyph out of range for the type, these functions + * will return NO_GLYPH (see exception below) + * 4) glyph_to_swallow() does not return a showsyms[] index, but an + * offset from the first swallow symbol. If handed something + * out of range, it will return zero (for lack of anything better + * to return). + */ +#define glyph_to_mon(glyph) \ + (glyph_is_normal_monster(glyph) ? ((glyph)-GLYPH_MON_OFF) : \ + glyph_is_pet(glyph) ? ((glyph)-GLYPH_PET_OFF) : \ + glyph_is_detected_monster(glyph) ? ((glyph)-GLYPH_DETECT_OFF) : \ + glyph_is_ridden_monster(glyph) ? ((glyph)-GLYPH_RIDDEN_OFF) : \ + NO_GLYPH) +#define glyph_to_obj(glyph) \ + (glyph_is_body(glyph) ? CORPSE : \ + glyph_is_normal_object(glyph) ? ((glyph)-GLYPH_OBJ_OFF) : \ + NO_GLYPH) +#define glyph_to_trap(glyph) \ + (glyph_is_trap(glyph) ? \ + ((int) defsym_to_trap((glyph) - GLYPH_CMAP_OFF)) : \ + NO_GLYPH) +#define glyph_to_cmap(glyph) \ + (glyph_is_cmap(glyph) ? ((glyph) - GLYPH_CMAP_OFF) : \ + NO_GLYPH) +#define glyph_to_swallow(glyph) \ + (glyph_is_swallow(glyph) ? (((glyph) - GLYPH_SWALLOW_OFF) & 0x7) : \ + 0) +#define glyph_to_warning(glyph) \ + (glyph_is_warning(glyph) ? ((glyph) - GLYPH_WARNING_OFF) : \ + NO_GLYPH); + +/* + * Return true if the given glyph is what we want. Note that bodies are + * considered objects. + */ +#define glyph_is_monster(glyph) \ + (glyph_is_normal_monster(glyph) \ + || glyph_is_pet(glyph) \ + || glyph_is_ridden_monster(glyph) \ + || glyph_is_detected_monster(glyph)) +#define glyph_is_normal_monster(glyph) \ + ((glyph) >= GLYPH_MON_OFF && (glyph) < (GLYPH_MON_OFF+NUMMONS)) +#define glyph_is_pet(glyph) \ + ((glyph) >= GLYPH_PET_OFF && (glyph) < (GLYPH_PET_OFF+NUMMONS)) +#define glyph_is_body(glyph) \ + ((glyph) >= GLYPH_BODY_OFF && (glyph) < (GLYPH_BODY_OFF+NUMMONS)) +#define glyph_is_ridden_monster(glyph) \ + ((glyph) >= GLYPH_RIDDEN_OFF && (glyph) < (GLYPH_RIDDEN_OFF+NUMMONS)) +#define glyph_is_detected_monster(glyph) \ + ((glyph) >= GLYPH_DETECT_OFF && (glyph) < (GLYPH_DETECT_OFF+NUMMONS)) +#define glyph_is_invisible(glyph) ((glyph) == GLYPH_INVISIBLE) +#define glyph_is_normal_object(glyph) \ + ((glyph) >= GLYPH_OBJ_OFF && (glyph) < (GLYPH_OBJ_OFF+NUM_OBJECTS)) +#define glyph_is_object(glyph) \ + (glyph_is_normal_object(glyph) \ + || glyph_is_body(glyph)) +#define glyph_is_trap(glyph) \ + ((glyph) >= (GLYPH_CMAP_OFF+trap_to_defsym(1)) && \ + (glyph) < (GLYPH_CMAP_OFF+trap_to_defsym(1)+TRAPNUM)) +#define glyph_is_cmap(glyph) \ + ((glyph) >= GLYPH_CMAP_OFF && (glyph) < (GLYPH_CMAP_OFF+MAXPCHARS)) +#define glyph_is_swallow(glyph) \ + ((glyph) >= GLYPH_SWALLOW_OFF && (glyph) < (GLYPH_SWALLOW_OFF+(NUMMONS << 3))) +#define glyph_is_warning(glyph) \ + ((glyph) >= GLYPH_WARNING_OFF && (glyph) < (GLYPH_WARNING_OFF + WARNCOUNT)) +#endif /* DISPLAY_H */ diff --git a/include/dlb.h b/include/dlb.h new file mode 100644 index 0000000..ca45809 --- /dev/null +++ b/include/dlb.h @@ -0,0 +1,141 @@ +/* SCCS Id: @(#)dlb.h 3.4 1997/07/29 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef DLB_H +#define DLB_H +/* definitions for data library */ + +#ifdef DLB + +/* implementations */ +#ifdef MAC +# define DLBRSRC /* use Mac resources */ +#else +# define DLBLIB /* use a set of external files */ +#endif + +#ifdef DLBLIB +/* directory structure in memory */ +typedef struct dlb_directory { + char *fname; /* file name as seen from calling code */ + long foffset; /* offset in lib file to start of this file */ + long fsize; /* file size */ + char handling; /* how to handle the file (compression, etc) */ +} libdir; + +/* information about each open library */ +typedef struct dlb_library { + FILE *fdata; /* opened data file */ + long fmark; /* current file mark */ + libdir *dir; /* directory of library file */ + char *sspace; /* pointer to string space */ + long nentries; /* # of files in directory */ + long rev; /* dlb file revision */ + long strsize; /* dlb file string size */ +} library; + +/* library definitions */ +# ifndef DLBFILE +# define DLBFILE "nhdat" /* name of library */ +# endif +# ifndef FILENAME_CMP +# define FILENAME_CMP strcmp /* case sensitive */ +# endif + +#endif /* DLBLIB */ + + +typedef struct dlb_handle { + FILE *fp; /* pointer to an external file, use if non-null */ +#ifdef DLBLIB + library *lib; /* pointer to library structure */ + long start; /* offset of start of file */ + long size; /* size of file */ + long mark; /* current file marker */ +#endif +#ifdef DLBRSRC + int fd; /* HandleFile file descriptor */ +#endif +} dlb; + +#if defined(ULTRIX_PROTO) && !defined(__STDC__) + /* buggy old Ultrix compiler wants this for the (*dlb_fread_proc) + and (*dlb_fgets_proc) prototypes in struct dlb_procs (dlb.c); + we'll use it in all the declarations for consistency */ +#define DLB_P struct dlb_handle * +#else +#define DLB_P dlb * +#endif + +boolean NDECL(dlb_init); +void NDECL(dlb_cleanup); + +dlb *FDECL(dlb_fopen, (const char *,const char *)); +int FDECL(dlb_fclose, (DLB_P)); +int FDECL(dlb_fread, (char *,int,int,DLB_P)); +int FDECL(dlb_fseek, (DLB_P,long,int)); +char *FDECL(dlb_fgets, (char *,int,DLB_P)); +int FDECL(dlb_fgetc, (DLB_P)); +long FDECL(dlb_ftell, (DLB_P)); + + +/* Resource DLB entry points */ +#ifdef DLBRSRC + boolean rsrc_dlb_init(void); + void rsrc_dlb_cleanup(void); + boolean rsrc_dlb_fopen(dlb *dp, const char *name, const char *mode); + int rsrc_dlb_fclose(dlb *dp); + int rsrc_dlb_fread(char *buf, int size, int quan, dlb *dp); + int rsrc_dlb_fseek(dlb *dp, long pos, int whence); + char *rsrc_dlb_fgets(char *buf, int len, dlb *dp); + int rsrc_dlb_fgetc(dlb *dp); + long rsrc_dlb_ftell(dlb *dp); +#endif + + +#else /* DLB */ + +# define dlb FILE + +# define dlb_init() +# define dlb_cleanup() + +# define dlb_fopen fopen +# define dlb_fclose fclose +# define dlb_fread fread +# define dlb_fseek fseek +# define dlb_fgets fgets +# define dlb_fgetc fgetc +# define dlb_ftell ftell + +#endif /* DLB */ + + +/* various other I/O stuff we don't want to replicate everywhere */ + +#ifndef SEEK_SET +# define SEEK_SET 0 +#endif +#ifndef SEEK_CUR +# define SEEK_CUR 1 +#endif +#ifndef SEEK_END +# define SEEK_END 2 +#endif + +#define RDTMODE "r" +#if (defined(MSDOS) || defined(WIN32) || defined(TOS) || defined(OS2)) && defined(DLB) +#define WRTMODE "w+b" +#else +#define WRTMODE "w+" +#endif +#if (defined(MICRO) && !defined(AMIGA)) || defined(THINK_C) || defined(__MWERKS__) || defined(WIN32) +# define RDBMODE "rb" +# define WRBMODE "w+b" +#else +# define RDBMODE "r" +# define WRBMODE "w+" +#endif + +#endif /* DLB_H */ diff --git a/include/dungeon.h b/include/dungeon.h new file mode 100644 index 0000000..24ba413 --- /dev/null +++ b/include/dungeon.h @@ -0,0 +1,169 @@ +/* SCCS Id: @(#)dungeon.h 3.4 1999/07/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef DUNGEON_H +#define DUNGEON_H + +typedef struct d_flags { /* dungeon/level type flags */ + Bitfield(town, 1); /* is this a town? (levels only) */ + Bitfield(hellish, 1); /* is this part of hell? */ + Bitfield(maze_like, 1); /* is this a maze? */ + Bitfield(rogue_like, 1); /* is this an old-fashioned presentation? */ + Bitfield(align, 3); /* dungeon alignment. */ + Bitfield(unused, 1); /* etc... */ +} d_flags; + +typedef struct d_level { /* basic dungeon level element */ + xchar dnum; /* dungeon number */ + xchar dlevel; /* level number */ +} d_level; + +typedef struct s_level { /* special dungeon level element */ + struct s_level *next; + d_level dlevel; /* dungeon & level numbers */ + char proto[15]; /* name of prototype file (eg. "tower") */ + char boneid; /* character to id level in bones files */ + uchar rndlevs; /* no. of randomly available similar levels */ + d_flags flags; /* type flags */ +} s_level; + +typedef struct stairway { /* basic stairway identifier */ + xchar sx, sy; /* x / y location of the stair */ + d_level tolev; /* where does it go */ + char up; /* what type of stairway (up/down) */ +} stairway; + +/* level region types */ +#define LR_DOWNSTAIR 0 +#define LR_UPSTAIR 1 +#define LR_PORTAL 2 +#define LR_BRANCH 3 +#define LR_TELE 4 +#define LR_UPTELE 5 +#define LR_DOWNTELE 6 + +typedef struct dest_area { /* non-stairway level change indentifier */ + xchar lx, ly; /* "lower" left corner (near [0,0]) */ + xchar hx, hy; /* "upper" right corner (near [COLNO,ROWNO]) */ + xchar nlx, nly; /* outline of invalid area */ + xchar nhx, nhy; /* opposite corner of invalid area */ +} dest_area; + +typedef struct dungeon { /* basic dungeon identifier */ + char dname[24]; /* name of the dungeon (eg. "Hell") */ + char proto[15]; /* name of prototype file (eg. "tower") */ + char boneid; /* character to id dungeon in bones files */ + d_flags flags; /* dungeon flags */ + xchar entry_lev; /* entry level */ + xchar num_dunlevs; /* number of levels in this dungeon */ + xchar dunlev_ureached; /* how deep you have been in this dungeon */ + int ledger_start, /* the starting depth in "real" terms */ + depth_start; /* the starting depth in "logical" terms */ +} dungeon; + +/* + * A branch structure defines the connection between two dungeons. They + * will be ordered by the dungeon number/level number of 'end1'. Ties + * are resolved by 'end2'. 'Type' uses 'end1' arbitrarily as the primary + * point. + */ +typedef struct branch { + struct branch *next; /* next in the branch chain */ + int id; /* branch identifier */ + int type; /* type of branch */ + d_level end1; /* "primary" end point */ + d_level end2; /* other end point */ + boolean end1_up; /* does end1 go up? */ +} branch; + +/* branch types */ +#define BR_STAIR 0 /* "Regular" connection, 2 staircases. */ +#define BR_NO_END1 1 /* "Regular" connection. However, no stair from */ + /* end1 to end2. There is a stair from end2 */ + /* to end1. */ +#define BR_NO_END2 2 /* "Regular" connection. However, no stair from */ + /* end2 to end1. There is a stair from end1 */ + /* to end2. */ +#define BR_PORTAL 3 /* Connection by magic portals (traps) */ + + +/* A particular dungeon contains num_dunlevs d_levels with dlevel 1.. + * num_dunlevs. Ledger_start and depth_start are bases that are added + * to the dlevel of a particular d_level to get the effective ledger_no + * and depth for that d_level. + * + * Ledger_no is a bookkeeping number that gives a unique identifier for a + * particular d_level (for level.?? files, e.g.). + * + * Depth corresponds to the number of floors below the surface. + */ +#define Is_astralevel(x) (on_level(x, &astral_level)) +#define Is_earthlevel(x) (on_level(x, &earth_level)) +#define Is_waterlevel(x) (on_level(x, &water_level)) +#define Is_firelevel(x) (on_level(x, &fire_level)) +#define Is_airlevel(x) (on_level(x, &air_level)) +#define Is_medusa_level(x) (on_level(x, &medusa_level)) +#define Is_oracle_level(x) (on_level(x, &oracle_level)) +#define Is_valley(x) (on_level(x, &valley_level)) +#define Is_juiblex_level(x) (on_level(x, &juiblex_level)) +#define Is_asmo_level(x) (on_level(x, &asmodeus_level)) +#define Is_baal_level(x) (on_level(x, &baalzebub_level)) +#define Is_wiz1_level(x) (on_level(x, &wiz1_level)) +#define Is_wiz2_level(x) (on_level(x, &wiz2_level)) +#define Is_wiz3_level(x) (on_level(x, &wiz3_level)) +#define Is_sanctum(x) (on_level(x, &sanctum_level)) +#define Is_portal_level(x) (on_level(x, &portal_level)) +#define Is_rogue_level(x) (on_level(x, &rogue_level)) +#define Is_stronghold(x) (on_level(x, &stronghold_level)) +#define Is_bigroom(x) (on_level(x, &bigroom_level)) +#define Is_qstart(x) (on_level(x, &qstart_level)) +#define Is_qlocate(x) (on_level(x, &qlocate_level)) +#define Is_nemesis(x) (on_level(x, &nemesis_level)) +#define Is_knox(x) (on_level(x, &knox_level)) + +#define In_sokoban(x) ((x)->dnum == sokoban_dnum) +#define Inhell In_hell(&u.uz) /* now gehennom */ +#define In_endgame(x) ((x)->dnum == astral_level.dnum) + +#define within_bounded_area(X,Y,LX,LY,HX,HY) \ + ((X) >= (LX) && (X) <= (HX) && (Y) >= (LY) && (Y) <= (HY)) + +/* monster and object migration codes */ + +#define MIGR_NOWHERE (-1) /* failure flag for down_gate() */ +#define MIGR_RANDOM 0 +#define MIGR_APPROX_XY 1 /* approximate coordinates */ +#define MIGR_EXACT_XY 2 /* specific coordinates */ +#define MIGR_STAIRS_UP 3 +#define MIGR_STAIRS_DOWN 4 +#define MIGR_LADDER_UP 5 +#define MIGR_LADDER_DOWN 6 +#define MIGR_SSTAIRS 7 /* dungeon branch */ +#define MIGR_PORTAL 8 /* magic portal */ +#define MIGR_NEAR_PLAYER 9 /* mon: followers; obj: trap door */ + +/* level information (saved via ledger number) */ + +struct linfo { + unsigned char flags; +#define VISITED 0x01 /* hero has visited this level */ +#define FORGOTTEN 0x02 /* hero will forget this level when reached */ +#define LFILE_EXISTS 0x04 /* a level file exists for this level */ +/* + * Note: VISITED and LFILE_EXISTS are currently almost always set at the + * same time. However they _mean_ different things. + */ + +#ifdef MFLOPPY +# define FROMPERM 1 /* for ramdisk use */ +# define TOPERM 2 /* for ramdisk use */ +# define ACTIVE 1 +# define SWAPPED 2 + int where; + long time; + long size; +#endif /* MFLOPPY */ +}; + +#endif /* DUNGEON_H */ diff --git a/include/edog.h b/include/edog.h new file mode 100644 index 0000000..eadc5b3 --- /dev/null +++ b/include/edog.h @@ -0,0 +1,33 @@ +/* SCCS Id: @(#)edog.h 3.4 1997/10/23 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef EDOG_H +#define EDOG_H + +/* various types of food, the lower, the better liked. */ + +#define DOGFOOD 0 +#define CADAVER 1 +#define ACCFOOD 2 +#define MANFOOD 3 +#define APPORT 4 +#define POISON 5 +#define UNDEF 6 +#define TABU 7 + +struct edog { + long droptime; /* moment dog dropped object */ + unsigned dropdist; /* dist of drpped obj from @ */ + int apport; /* amount of training */ + long whistletime; /* last time he whistled */ + long hungrytime; /* will get hungry at this time */ + coord ogoal; /* previous goal location */ + int abuse; /* track abuses to this pet */ + int revivals; /* count pet deaths */ + int mhpmax_penalty; /* while starving, points reduced */ + Bitfield(killed_by_u, 1); /* you attempted to kill him */ +}; +#define EDOG(mon) ((struct edog *)&(mon)->mextra[0]) + +#endif /* EDOG_H */ diff --git a/include/emin.h b/include/emin.h new file mode 100644 index 0000000..811bf23 --- /dev/null +++ b/include/emin.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)emin.h 3.4 1997/05/01 */ +/* Copyright (c) David Cohrs, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef EMIN_H +#define EMIN_H + +struct emin { + aligntyp min_align; /* alignment of minion */ +}; + +#define EMIN(mon) ((struct emin *)&(mon)->mextra[0]) + +#endif /* EMIN_H */ diff --git a/include/engrave.h b/include/engrave.h new file mode 100644 index 0000000..02c5d9e --- /dev/null +++ b/include/engrave.h @@ -0,0 +1,27 @@ +/* SCCS Id: @(#)engrave.h 3.4 1991/07/31 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef ENGRAVE_H +#define ENGRAVE_H + +struct engr { + struct engr *nxt_engr; + char *engr_txt; + xchar engr_x, engr_y; + unsigned engr_lth; /* for save & restore; not length of text */ + long engr_time; /* moment engraving was (will be) finished */ + xchar engr_type; +#define DUST 1 +#define ENGRAVE 2 +#define BURN 3 +#define MARK 4 +#define ENGR_BLOOD 5 +#define HEADSTONE 6 +#define N_ENGRAVE 6 +}; + +#define newengr(lth) (struct engr *)alloc((unsigned)(lth) + sizeof(struct engr)) +#define dealloc_engr(engr) free((genericptr_t) (engr)) + +#endif /* ENGRAVE_H */ diff --git a/include/epri.h b/include/epri.h new file mode 100644 index 0000000..56b14ac --- /dev/null +++ b/include/epri.h @@ -0,0 +1,24 @@ +/* SCCS Id: @(#)epri.h 3.4 1997/05/01 */ +/* Copyright (c) Izchak Miller, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef EPRI_H +#define EPRI_H + +struct epri { + aligntyp shralign; /* alignment of priest's shrine */ + /* leave as first field to match emin */ + schar shroom; /* index in rooms */ + coord shrpos; /* position of shrine */ + d_level shrlevel; /* level (& dungeon) of shrine */ +}; + +#define EPRI(mon) ((struct epri *)&(mon)->mextra[0]) + +/* A priest without ispriest is a roaming priest without a shrine, so + * the fields (except shralign, which becomes only the priest alignment) + * are available for reuse. + */ +#define renegade shroom + +#endif /* EPRI_H */ diff --git a/include/eshk.h b/include/eshk.h new file mode 100644 index 0000000..72a30e3 --- /dev/null +++ b/include/eshk.h @@ -0,0 +1,45 @@ +/* SCCS Id: @(#)eshk.h 3.4 1997/05/01 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef ESHK_H +#define ESHK_H + +#define REPAIR_DELAY 5 /* minimum delay between shop damage & repair */ + +#define BILLSZ 200 + +struct bill_x { + unsigned bo_id; + boolean useup; + long price; /* price per unit */ + long bquan; /* amount used up */ +}; + +struct eshk { + long robbed; /* amount stolen by most recent customer */ + long credit; /* amount credited to customer */ + long debit; /* amount of debt for using unpaid items */ + long loan; /* shop-gold picked (part of debit) */ + int shoptype; /* the value of rooms[shoproom].rtype */ + schar shoproom; /* index in rooms; set by inshop() */ + schar unused; /* to force alignment for stupid compilers */ + boolean following; /* following customer since he owes us sth */ + boolean surcharge; /* angry shk inflates prices */ + coord shk; /* usual position shopkeeper */ + coord shd; /* position shop door */ + d_level shoplevel; /* level (& dungeon) of his shop */ + int billct; /* no. of entries of bill[] in use */ + struct bill_x bill[BILLSZ]; + struct bill_x *bill_p; + int visitct; /* nr of visits by most recent customer */ + char customer[PL_NSIZ]; /* most recent customer */ + char shknam[PL_NSIZ]; +}; + +#define ESHK(mon) ((struct eshk *)&(mon)->mextra[0]) + +#define NOTANGRY(mon) ((mon)->mpeaceful) +#define ANGRY(mon) (!NOTANGRY(mon)) + +#endif /* ESHK_H */ diff --git a/include/extern.h b/include/extern.h new file mode 100644 index 0000000..4e2314d --- /dev/null +++ b/include/extern.h @@ -0,0 +1,2387 @@ +/* SCCS Id: @(#)extern.h 3.4 2003/03/10 */ +/* Copyright (c) Steve Creps, 1988. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef EXTERN_H +#define EXTERN_H + +#define E extern + +/* ### alloc.c ### */ + +#if 0 +E long *FDECL(alloc, (unsigned int)); +#endif +E char *FDECL(fmt_ptr, (const genericptr,char *)); + +/* This next pre-processor directive covers almost the entire file, + * interrupted only occasionally to pick up specific functions as needed. */ +#if !defined(MAKEDEFS_C) && !defined(LEV_LEX_C) + +/* ### allmain.c ### */ + +E void NDECL(moveloop); +E void NDECL(stop_occupation); +E void NDECL(display_gamewindows); +E void NDECL(newgame); +E void FDECL(welcome, (BOOLEAN_P)); + +/* ### apply.c ### */ + +E int NDECL(doapply); +E int NDECL(dorub); +E int NDECL(dojump); +E int FDECL(jump, (int)); +E int NDECL(number_leashed); +E void FDECL(o_unleash, (struct obj *)); +E void FDECL(m_unleash, (struct monst *,BOOLEAN_P)); +E void NDECL(unleash_all); +E boolean NDECL(next_to_u); +E struct obj *FDECL(get_mleash, (struct monst *)); +E void FDECL(check_leash, (XCHAR_P,XCHAR_P)); +E boolean FDECL(um_dist, (XCHAR_P,XCHAR_P,XCHAR_P)); +E boolean FDECL(snuff_candle, (struct obj *)); +E boolean FDECL(snuff_lit, (struct obj *)); +E boolean FDECL(catch_lit, (struct obj *)); +E void FDECL(use_unicorn_horn, (struct obj *)); +E boolean FDECL(tinnable, (struct obj *)); +E void NDECL(reset_trapset); +E void FDECL(fig_transform, (genericptr_t, long)); +E int FDECL(unfixable_trouble_count,(BOOLEAN_P)); + +/* ### artifact.c ### */ + +E void NDECL(init_artifacts); +E void FDECL(save_artifacts, (int)); +E void FDECL(restore_artifacts, (int)); +E const char *FDECL(artiname, (int)); +E struct obj *FDECL(mk_artifact, (struct obj *,ALIGNTYP_P)); +E const char *FDECL(artifact_name, (const char *,short *)); +E boolean FDECL(exist_artifact, (int,const char *)); +E void FDECL(artifact_exists, (struct obj *,const char *,BOOLEAN_P)); +E int NDECL(nartifact_exist); +E boolean FDECL(spec_ability, (struct obj *,unsigned long)); +E boolean FDECL(confers_luck, (struct obj *)); +E boolean FDECL(arti_reflects, (struct obj *)); +E boolean FDECL(restrict_name, (struct obj *,const char *)); +E boolean FDECL(defends, (int,struct obj *)); +E boolean FDECL(protects, (int,struct obj *)); +E void FDECL(set_artifact_intrinsic, (struct obj *,BOOLEAN_P,long)); +E int FDECL(touch_artifact, (struct obj *,struct monst *)); +E int FDECL(spec_abon, (struct obj *,struct monst *)); +E int FDECL(spec_dbon, (struct obj *,struct monst *,int)); +E void FDECL(discover_artifact, (XCHAR_P)); +E boolean FDECL(undiscovered_artifact, (XCHAR_P)); +E int FDECL(disp_artifact_discoveries, (winid)); +E boolean FDECL(artifact_hit, (struct monst *,struct monst *, + struct obj *,int *,int)); +E int NDECL(doinvoke); +E void FDECL(arti_speak, (struct obj *)); +E boolean FDECL(artifact_light, (struct obj *)); +E long FDECL(spec_m2, (struct obj *)); +E boolean FDECL(artifact_has_invprop, (struct obj *,UCHAR_P)); +E long FDECL(arti_cost, (struct obj *)); + +/* ### attrib.c ### */ + +E boolean FDECL(adjattrib, (int,int,int)); +E void FDECL(change_luck, (SCHAR_P)); +E int FDECL(stone_luck, (BOOLEAN_P)); +E void NDECL(set_moreluck); +E void FDECL(gainstr, (struct obj *,int)); +E void FDECL(losestr, (int)); +E void NDECL(restore_attrib); +E void FDECL(exercise, (int,BOOLEAN_P)); +E void NDECL(exerchk); +E void NDECL(reset_attribute_clock); +E void FDECL(init_attr, (int)); +E void NDECL(redist_attr); +E void FDECL(adjabil, (int,int)); +E int NDECL(newhp); +E schar FDECL(acurr, (int)); +E schar NDECL(acurrstr); +E void FDECL(adjalign, (int)); + +/* ### ball.c ### */ + +E void NDECL(ballfall); +E void NDECL(placebc); +E void NDECL(unplacebc); +E void FDECL(set_bc, (int)); +E void FDECL(move_bc, (int,int,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E boolean FDECL(drag_ball, (XCHAR_P,XCHAR_P, + int *,xchar *,xchar *,xchar *,xchar *, boolean *,BOOLEAN_P)); +E void FDECL(drop_ball, (XCHAR_P,XCHAR_P)); +E void NDECL(drag_down); + +/* ### bones.c ### */ + +E boolean NDECL(can_make_bones); +E void FDECL(savebones, (struct obj *)); +E int NDECL(getbones); + +/* ### botl.c ### */ + +E int FDECL(xlev_to_rank, (int)); +E int FDECL(title_to_mon, (const char *,int *,int *)); +E void NDECL(max_rank_sz); +#ifdef SCORE_ON_BOTL +E long NDECL(botl_score); +#endif +E int FDECL(describe_level, (char *)); +E const char *FDECL(rank_of, (int,SHORT_P,BOOLEAN_P)); +E void NDECL(bot); + +/* ### cmd.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(doextcmd); +E int NDECL(domonability); +E int NDECL(doprev_message); +E int NDECL(timed_occupation); +E int NDECL(wiz_attributes); +E int NDECL(enter_explore_mode); +# ifdef WIZARD +E int NDECL(wiz_detect); +E int NDECL(wiz_genesis); +E int NDECL(wiz_identify); +E int NDECL(wiz_level_tele); +E int NDECL(wiz_map); +E int NDECL(wiz_where); +E int NDECL(wiz_wish); +# endif /* WIZARD */ +#endif /* USE_TRAMPOLI */ +E void NDECL(reset_occupations); +E void FDECL(set_occupation, (int (*)(void),const char *,int)); +#ifdef REDO +E char NDECL(pgetchar); +E void FDECL(pushch, (CHAR_P)); +E void FDECL(savech, (CHAR_P)); +#endif +#ifdef WIZARD +E void NDECL(add_debug_extended_commands); +#endif /* WIZARD */ +E void FDECL(rhack, (char *)); +E int NDECL(doextlist); +E int NDECL(extcmd_via_menu); +E void FDECL(enlightenment, (int)); +E void FDECL(show_conduct, (int)); +E int FDECL(xytod, (SCHAR_P,SCHAR_P)); +E void FDECL(dtoxy, (coord *,int)); +E int FDECL(movecmd, (CHAR_P)); +E int FDECL(getdir, (const char *)); +E void NDECL(confdir); +E int FDECL(isok, (int,int)); +E int FDECL(get_adjacent_loc, (const char *, const char *, XCHAR_P, XCHAR_P, coord *)); +E const char *FDECL(click_to_cmd, (int,int,int)); +E char NDECL(readchar); +#ifdef WIZARD +E void NDECL(sanity_check); +#endif +E char FDECL(yn_function, (const char *, const char *, CHAR_P)); + +/* ### dbridge.c ### */ + +E boolean FDECL(is_pool, (int,int)); +E boolean FDECL(is_lava, (int,int)); +E boolean FDECL(is_ice, (int,int)); +E int FDECL(is_drawbridge_wall, (int,int)); +E boolean FDECL(is_db_wall, (int,int)); +E boolean FDECL(find_drawbridge, (int *,int*)); +E boolean FDECL(create_drawbridge, (int,int,int,BOOLEAN_P)); +E void FDECL(open_drawbridge, (int,int)); +E void FDECL(close_drawbridge, (int,int)); +E void FDECL(destroy_drawbridge, (int,int)); + +/* ### decl.c ### */ + +E void NDECL(decl_init); + +/* ### detect.c ### */ + +E struct obj *FDECL(o_in, (struct obj*,CHAR_P)); +E struct obj *FDECL(o_material, (struct obj*,unsigned)); +E int FDECL(gold_detect, (struct obj *)); +E int FDECL(food_detect, (struct obj *)); +E int FDECL(object_detect, (struct obj *,int)); +E int FDECL(monster_detect, (struct obj *,int)); +E int FDECL(trap_detect, (struct obj *)); +E const char *FDECL(level_distance, (d_level *)); +E void FDECL(use_crystal_ball, (struct obj *)); +E void NDECL(do_mapping); +E void NDECL(do_vicinity_map); +E void FDECL(cvt_sdoor_to_door, (struct rm *)); +#ifdef USE_TRAMPOLI +E void FDECL(findone, (int,int,genericptr_t)); +E void FDECL(openone, (int,int,genericptr_t)); +#endif +E int NDECL(findit); +E int NDECL(openit); +E void FDECL(find_trap, (struct trap *)); +E int FDECL(dosearch0, (int)); +E int NDECL(dosearch); +E void NDECL(sokoban_detect); + +/* ### dig.c ### */ + +E boolean NDECL(is_digging); +#ifdef USE_TRAMPOLI +E int NDECL(dig); +#endif +E int NDECL(holetime); +E boolean FDECL(dig_check, (struct monst *, BOOLEAN_P, int, int)); +E void FDECL(digactualhole, (int,int,struct monst *,int)); +E boolean FDECL(dighole, (BOOLEAN_P)); +E int FDECL(use_pick_axe, (struct obj *)); +E int FDECL(use_pick_axe2, (struct obj *)); +E boolean FDECL(mdig_tunnel, (struct monst *)); +E void FDECL(watch_dig, (struct monst *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E void NDECL(zap_dig); +E struct obj *FDECL(bury_an_obj, (struct obj *)); +E void FDECL(bury_objs, (int,int)); +E void FDECL(unearth_objs, (int,int)); +E void FDECL(rot_organic, (genericptr_t, long)); +E void FDECL(rot_corpse, (genericptr_t, long)); +#if 0 +E void FDECL(bury_monst, (struct monst *)); +E void NDECL(bury_you); +E void NDECL(unearth_you); +E void NDECL(escape_tomb); +E void FDECL(bury_obj, (struct obj *)); +#endif + +/* ### display.c ### */ + +#ifdef INVISIBLE_OBJECTS +E struct obj * FDECL(vobj_at, (XCHAR_P,XCHAR_P)); +#endif /* INVISIBLE_OBJECTS */ +E void FDECL(magic_map_background, (XCHAR_P,XCHAR_P,int)); +E void FDECL(map_background, (XCHAR_P,XCHAR_P,int)); +E void FDECL(map_trap, (struct trap *,int)); +E void FDECL(map_object, (struct obj *,int)); +E void FDECL(map_invisible, (XCHAR_P,XCHAR_P)); +E void FDECL(unmap_object, (int,int)); +E void FDECL(map_location, (int,int,int)); +E void FDECL(feel_location, (XCHAR_P,XCHAR_P)); +E void FDECL(newsym, (int,int)); +E void FDECL(shieldeff, (XCHAR_P,XCHAR_P)); +E void FDECL(tmp_at, (int,int)); +E void FDECL(swallowed, (int)); +E void FDECL(under_ground, (int)); +E void FDECL(under_water, (int)); +E void NDECL(see_monsters); +E void NDECL(set_mimic_blocking); +E void NDECL(see_objects); +E void NDECL(see_traps); +E void NDECL(curs_on_u); +E int NDECL(doredraw); +E void NDECL(docrt); +E void FDECL(show_glyph, (int,int,int)); +E void NDECL(clear_glyph_buffer); +E void FDECL(row_refresh, (int,int,int)); +E void NDECL(cls); +E void FDECL(flush_screen, (int)); +E int FDECL(back_to_glyph, (XCHAR_P,XCHAR_P)); +E int FDECL(zapdir_to_glyph, (int,int,int)); +E int FDECL(glyph_at, (XCHAR_P,XCHAR_P)); +E void NDECL(set_wall_state); + +/* ### do.c ### */ + +#ifdef USE_TRAMPOLI +E int FDECL(drop, (struct obj *)); +E int NDECL(wipeoff); +#endif +E int NDECL(dodrop); +E boolean FDECL(boulder_hits_pool, (struct obj *,int,int,BOOLEAN_P)); +E boolean FDECL(flooreffects, (struct obj *,int,int,const char *)); +E void FDECL(doaltarobj, (struct obj *)); +E boolean FDECL(canletgo, (struct obj *,const char *)); +E void FDECL(dropx, (struct obj *)); +E void FDECL(dropy, (struct obj *)); +E void FDECL(obj_no_longer_held, (struct obj *)); +E int NDECL(doddrop); +E int NDECL(dodown); +E int NDECL(doup); +#ifdef INSURANCE +E void NDECL(save_currentstate); +#endif +E void FDECL(goto_level, (d_level *,BOOLEAN_P,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(schedule_goto, (d_level *,BOOLEAN_P,BOOLEAN_P,int, + const char *,const char *)); +E void NDECL(deferred_goto); +E boolean FDECL(revive_corpse, (struct obj *)); +E void FDECL(revive_mon, (genericptr_t, long)); +E int NDECL(donull); +E int NDECL(dowipe); +E void FDECL(set_wounded_legs, (long,int)); +E void NDECL(heal_legs); + +/* ### do_name.c ### */ + +E int FDECL(getpos, (coord *,BOOLEAN_P,const char *)); +E struct monst *FDECL(christen_monst, (struct monst *,const char *)); +E int NDECL(do_mname); +E struct obj *FDECL(oname, (struct obj *,const char *)); +E int NDECL(ddocall); +E void FDECL(docall, (struct obj *)); +E const char *NDECL(rndghostname); +E char *FDECL(x_monnam, (struct monst *,int,const char *,int,BOOLEAN_P)); +E char *FDECL(l_monnam, (struct monst *)); +E char *FDECL(mon_nam, (struct monst *)); +E char *FDECL(noit_mon_nam, (struct monst *)); +E char *FDECL(Monnam, (struct monst *)); +E char *FDECL(noit_Monnam, (struct monst *)); +E char *FDECL(m_monnam, (struct monst *)); +E char *FDECL(y_monnam, (struct monst *)); +E char *FDECL(Adjmonnam, (struct monst *,const char *)); +E char *FDECL(Amonnam, (struct monst *)); +E char *FDECL(a_monnam, (struct monst *)); +E char *FDECL(distant_monnam, (struct monst *,int,char *)); +E const char *NDECL(rndmonnam); +E const char *FDECL(hcolor, (const char *)); +E const char *NDECL(rndcolor); +#ifdef REINCARNATION +E const char *NDECL(roguename); +#endif +E struct obj *FDECL(realloc_obj, + (struct obj *, int, genericptr_t, int, const char *)); +E char *FDECL(coyotename, (struct monst *,char *)); + +/* ### do_wear.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(Armor_on); +E int NDECL(Boots_on); +E int NDECL(Gloves_on); +E int NDECL(Helmet_on); +E int FDECL(select_off, (struct obj *)); +E int NDECL(take_off); +#endif +E void FDECL(off_msg, (struct obj *)); +E void NDECL(set_wear); +E boolean FDECL(donning, (struct obj *)); +E void NDECL(cancel_don); +E int NDECL(Armor_off); +E int NDECL(Armor_gone); +E int NDECL(Helmet_off); +E int NDECL(Gloves_off); +E int NDECL(Boots_off); +E int NDECL(Cloak_off); +E int NDECL(Shield_off); +#ifdef TOURIST +E int NDECL(Shirt_off); +#endif +E void NDECL(Amulet_off); +E void FDECL(Ring_on, (struct obj *)); +E void FDECL(Ring_off, (struct obj *)); +E void FDECL(Ring_gone, (struct obj *)); +E void FDECL(Blindf_on, (struct obj *)); +E void FDECL(Blindf_off, (struct obj *)); +E int NDECL(dotakeoff); +E int NDECL(doremring); +E int FDECL(cursed, (struct obj *)); +E int FDECL(armoroff, (struct obj *)); +E int FDECL(canwearobj, (struct obj *, long *, BOOLEAN_P)); +E int NDECL(dowear); +E int NDECL(doputon); +E void NDECL(find_ac); +E void NDECL(glibr); +E struct obj *FDECL(some_armor,(struct monst *)); +E void FDECL(erode_armor, (struct monst *,BOOLEAN_P)); +E struct obj *FDECL(stuck_ring, (struct obj *,int)); +E struct obj *NDECL(unchanger); +E void NDECL(reset_remarm); +E int NDECL(doddoremarm); +E int FDECL(destroy_arm, (struct obj *)); +E void FDECL(adj_abon, (struct obj *,SCHAR_P)); + +/* ### dog.c ### */ + +E void FDECL(initedog, (struct monst *)); +E struct monst *FDECL(make_familiar, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E struct monst *NDECL(makedog); +E void NDECL(update_mlstmv); +E void NDECL(losedogs); +E void FDECL(mon_arrive, (struct monst *,BOOLEAN_P)); +E void FDECL(mon_catchup_elapsed_time, (struct monst *,long)); +E void FDECL(keepdogs, (BOOLEAN_P)); +E void FDECL(migrate_to_level, (struct monst *,XCHAR_P,XCHAR_P,coord *)); +E int FDECL(dogfood, (struct monst *,struct obj *)); +E struct monst *FDECL(tamedog, (struct monst *,struct obj *)); +E void FDECL(abuse_dog, (struct monst *)); +E void FDECL(wary_dog, (struct monst *, BOOLEAN_P)); + +/* ### dogmove.c ### */ + +E int FDECL(dog_nutrition, (struct monst *,struct obj *)); +E int FDECL(dog_eat, (struct monst *,struct obj *,int,int,BOOLEAN_P)); +E int FDECL(dog_move, (struct monst *,int)); +#ifdef USE_TRAMPOLI +E void FDECL(wantdoor, (int,int,genericptr_t)); +#endif + +/* ### dokick.c ### */ + +E boolean FDECL(ghitm, (struct monst *,struct obj *)); +E void FDECL(container_impact_dmg, (struct obj *)); +E int NDECL(dokick); +E boolean FDECL(ship_object, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E void NDECL(obj_delivery); +E schar FDECL(down_gate, (XCHAR_P,XCHAR_P)); +E void FDECL(impact_drop, (struct obj *,XCHAR_P,XCHAR_P,XCHAR_P)); + +/* ### dothrow.c ### */ + +E int NDECL(dothrow); +E int NDECL(dofire); +E void FDECL(hitfloor, (struct obj *)); +E void FDECL(hurtle, (int,int,int,BOOLEAN_P)); +E void FDECL(mhurtle, (struct monst *,int,int,int)); +E void FDECL(throwit, (struct obj *,long,BOOLEAN_P)); +E int FDECL(omon_adj, (struct monst *,struct obj *,BOOLEAN_P)); +E int FDECL(thitmonst, (struct monst *,struct obj *)); +E int FDECL(hero_breaks, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E int FDECL(breaks, (struct obj *,XCHAR_P,XCHAR_P)); +E boolean FDECL(breaktest, (struct obj *)); +E boolean FDECL(walk_path, (coord *, coord *, boolean (*)(genericptr_t,int,int), genericptr_t)); +E boolean FDECL(hurtle_step, (genericptr_t, int, int)); + +/* ### drawing.c ### */ +#endif /* !MAKEDEFS_C && !LEV_LEX_C */ +E int FDECL(def_char_to_objclass, (CHAR_P)); +E int FDECL(def_char_to_monclass, (CHAR_P)); +#if !defined(MAKEDEFS_C) && !defined(LEV_LEX_C) +E void FDECL(assign_graphics, (uchar *,int,int,int)); +E void FDECL(switch_graphics, (int)); +#ifdef REINCARNATION +E void FDECL(assign_rogue_graphics, (BOOLEAN_P)); +#endif + +/* ### dungeon.c ### */ + +E void FDECL(save_dungeon, (int,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(restore_dungeon, (int)); +E void FDECL(insert_branch, (branch *,BOOLEAN_P)); +E void NDECL(init_dungeons); +E s_level *FDECL(find_level, (const char *)); +E s_level *FDECL(Is_special, (d_level *)); +E branch *FDECL(Is_branchlev, (d_level *)); +E xchar FDECL(ledger_no, (d_level *)); +E xchar NDECL(maxledgerno); +E schar FDECL(depth, (d_level *)); +E xchar FDECL(dunlev, (d_level *)); +E xchar FDECL(dunlevs_in_dungeon, (d_level *)); +E xchar FDECL(ledger_to_dnum, (XCHAR_P)); +E xchar FDECL(ledger_to_dlev, (XCHAR_P)); +E xchar FDECL(deepest_lev_reached, (BOOLEAN_P)); +E boolean FDECL(on_level, (d_level *,d_level *)); +E void FDECL(next_level, (BOOLEAN_P)); +E void FDECL(prev_level, (BOOLEAN_P)); +E void FDECL(u_on_newpos, (int,int)); +E void NDECL(u_on_sstairs); +E void NDECL(u_on_upstairs); +E void NDECL(u_on_dnstairs); +E boolean FDECL(On_stairs, (XCHAR_P,XCHAR_P)); +E void FDECL(get_level, (d_level *,int)); +E boolean FDECL(Is_botlevel, (d_level *)); +E boolean FDECL(Can_fall_thru, (d_level *)); +E boolean FDECL(Can_dig_down, (d_level *)); +E boolean FDECL(Can_rise_up, (int,int,d_level *)); +E boolean FDECL(In_quest, (d_level *)); +E boolean FDECL(In_mines, (d_level *)); +E branch *FDECL(dungeon_branch, (const char *)); +E boolean FDECL(at_dgn_entrance, (const char *)); +E boolean FDECL(In_hell, (d_level *)); +E boolean FDECL(In_V_tower, (d_level *)); +E boolean FDECL(On_W_tower_level, (d_level *)); +E boolean FDECL(In_W_tower, (int,int,d_level *)); +E void FDECL(find_hell, (d_level *)); +E void FDECL(goto_hell, (BOOLEAN_P,BOOLEAN_P)); +E void FDECL(assign_level, (d_level *,d_level *)); +E void FDECL(assign_rnd_level, (d_level *,d_level *,int)); +E int FDECL(induced_align, (int)); +E boolean FDECL(Invocation_lev, (d_level *)); +E xchar NDECL(level_difficulty); +E schar FDECL(lev_by_name, (const char *)); +#ifdef WIZARD +E schar FDECL(print_dungeon, (BOOLEAN_P,schar *,xchar *)); +#endif + +/* ### eat.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(eatmdone); +E int NDECL(eatfood); +E int NDECL(opentin); +E int NDECL(unfaint); +#endif +E boolean FDECL(is_edible, (struct obj *)); +E void NDECL(init_uhunger); +E int NDECL(Hear_again); +E void NDECL(reset_eat); +E int NDECL(doeat); +E void NDECL(gethungry); +E void FDECL(morehungry, (int)); +E void FDECL(lesshungry, (int)); +E boolean NDECL(is_fainted); +E void NDECL(reset_faint); +E void NDECL(violated_vegetarian); +#if 0 +E void NDECL(sync_hunger); +#endif +E void FDECL(newuhs, (BOOLEAN_P)); +E struct obj *FDECL(floorfood, (const char *,int)); +E void NDECL(vomit); +E int FDECL(eaten_stat, (int,struct obj *)); +E void FDECL(food_disappears, (struct obj *)); +E void FDECL(food_substitution, (struct obj *,struct obj *)); +E void NDECL(fix_petrification); +E void FDECL(consume_oeaten, (struct obj *,int)); +E boolean FDECL(maybe_finished_meal, (BOOLEAN_P)); + +/* ### end.c ### */ + +E void FDECL(done1, (int)); +E int NDECL(done2); +#ifdef USE_TRAMPOLI +E void FDECL(done_intr, (int)); +#endif +E void FDECL(done_in_by, (struct monst *)); +#endif /* !MAKEDEFS_C && !LEV_LEX_C */ +E void VDECL(panic, (const char *,...)) PRINTF_F(1,2); +#if !defined(MAKEDEFS_C) && !defined(LEV_LEX_C) +E void FDECL(done, (int)); +E void FDECL(container_contents, (struct obj *,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(terminate, (int)); +E int NDECL(num_genocides); + +/* ### engrave.c ### */ + +E char *FDECL(random_engraving, (char *)); +E void FDECL(wipeout_text, (char *,int,unsigned)); +E boolean NDECL(can_reach_floor); +E const char *FDECL(surface, (int,int)); +E const char *FDECL(ceiling, (int,int)); +E struct engr *FDECL(engr_at, (XCHAR_P,XCHAR_P)); +#ifdef ELBERETH +E int FDECL(sengr_at, (const char *,XCHAR_P,XCHAR_P)); +#endif +E void FDECL(u_wipe_engr, (int)); +E void FDECL(wipe_engr_at, (XCHAR_P,XCHAR_P,XCHAR_P)); +E void FDECL(read_engr_at, (int,int)); +E void FDECL(make_engr_at, (int,int,const char *,long,XCHAR_P)); +E void FDECL(del_engr_at, (int,int)); +E int NDECL(freehand); +E int NDECL(doengrave); +E void FDECL(save_engravings, (int,int)); +E void FDECL(rest_engravings, (int)); +E void FDECL(del_engr, (struct engr *)); +E void FDECL(rloc_engr, (struct engr *)); +E void FDECL(make_grave, (int,int,const char *)); + +/* ### exper.c ### */ + +E int FDECL(experience, (struct monst *,int)); +E void FDECL(more_experienced, (int,int)); +E void FDECL(losexp, (const char *)); +E void NDECL(newexplevel); +E void FDECL(pluslvl, (BOOLEAN_P)); +E long FDECL(rndexp, (BOOLEAN_P)); + +/* ### explode.c ### */ + +E void FDECL(explode, (int,int,int,int,CHAR_P,int)); +E long FDECL(scatter, (int, int, int, unsigned int, struct obj *)); +E void FDECL(splatter_burning_oil, (int, int)); + +/* ### extralev.c ### */ + +#ifdef REINCARNATION +E void NDECL(makeroguerooms); +E void FDECL(corr, (int,int)); +E void NDECL(makerogueghost); +#endif + +/* ### files.c ### */ + +E char *FDECL(fname_encode, (const char *, CHAR_P, char *, char *, int)); +E char *FDECL(fname_decode, (CHAR_P, char *, char *, int)); +E const char *FDECL(fqname, (const char *, int, int)); +E FILE *FDECL(fopen_datafile, (const char *,const char *,int)); +E boolean FDECL(uptodate, (int,const char *)); +E void FDECL(store_version, (int)); +#ifdef MFLOPPY +E void NDECL(set_lock_and_bones); +#endif +E void FDECL(set_levelfile_name, (char *,int)); +E int FDECL(create_levelfile, (int,char *)); +E int FDECL(open_levelfile, (int,char *)); +E void FDECL(delete_levelfile, (int)); +E void NDECL(clearlocks); +E int FDECL(create_bonesfile, (d_level*,char **, char *)); +#ifdef MFLOPPY +E void NDECL(cancel_bonesfile); +#endif +E void FDECL(commit_bonesfile, (d_level *)); +E int FDECL(open_bonesfile, (d_level*,char **)); +E int FDECL(delete_bonesfile, (d_level*)); +E void NDECL(compress_bonesfile); +E void NDECL(set_savefile_name); +#ifdef INSURANCE +E void FDECL(save_savefile_name, (int)); +#endif +#if defined(WIZARD) && !defined(MICRO) +E void NDECL(set_error_savefile); +#endif +E int NDECL(create_savefile); +E int NDECL(open_savefile); +E int NDECL(delete_savefile); +E int NDECL(restore_saved_game); +E void FDECL(compress, (const char *)); +E void FDECL(uncompress, (const char *)); +E boolean FDECL(lock_file, (const char *,int,int)); +E void FDECL(unlock_file, (const char *)); +#ifdef USER_SOUNDS +E boolean FDECL(can_read_file, (const char *)); +#endif +E void FDECL(read_config_file, (const char *)); +E void FDECL(check_recordfile, (const char *)); +#if defined(WIZARD) +E void NDECL(read_wizkit); +#endif +E void FDECL(paniclog, (const char *, const char *)); +E int FDECL(validate_prefix_locations, (char *)); +E char** NDECL(get_saved_games); +E void FDECL(free_saved_games, (char**)); +#ifdef SELF_RECOVER +E boolean NDECL(recover_savefile); +#endif +#ifdef HOLD_LOCKFILE_OPEN +E void NDECL(really_close); +#endif + +/* ### fountain.c ### */ + +E void FDECL(floating_above, (const char *)); +E void FDECL(dogushforth, (int)); +# ifdef USE_TRAMPOLI +E void FDECL(gush, (int,int,genericptr_t)); +# endif +E void FDECL(dryup, (XCHAR_P,XCHAR_P, BOOLEAN_P)); +E void NDECL(drinkfountain); +E void FDECL(dipfountain, (struct obj *)); +#ifdef SINKS +E void FDECL(breaksink, (int,int)); +E void NDECL(drinksink); +#endif + +/* ### hack.c ### */ + +E boolean FDECL(revive_nasty, (int,int,const char*)); +E void FDECL(movobj, (struct obj *,XCHAR_P,XCHAR_P)); +E boolean FDECL(may_dig, (XCHAR_P,XCHAR_P)); +E boolean FDECL(may_passwall, (XCHAR_P,XCHAR_P)); +E boolean FDECL(bad_rock, (struct permonst *,XCHAR_P,XCHAR_P)); +E boolean FDECL(invocation_pos, (XCHAR_P,XCHAR_P)); +E boolean FDECL(test_move, (int, int, int, int, int)); +E void NDECL(domove); +E void NDECL(invocation_message); +E void FDECL(spoteffects, (BOOLEAN_P)); +E char *FDECL(in_rooms, (XCHAR_P,XCHAR_P,int)); +E boolean FDECL(in_town, (int,int)); +E void FDECL(check_special_room, (BOOLEAN_P)); +E int NDECL(dopickup); +E void NDECL(lookaround); +E int NDECL(monster_nearby); +E void FDECL(nomul, (int)); +E void FDECL(unmul, (const char *)); +E void FDECL(losehp, (int,const char *,BOOLEAN_P)); +E int NDECL(weight_cap); +E int NDECL(inv_weight); +E int NDECL(near_capacity); +E int FDECL(calc_capacity, (int)); +E int NDECL(max_capacity); +E boolean FDECL(check_capacity, (const char *)); +E int NDECL(inv_cnt); +#ifdef GOLDOBJ +E long FDECL(money_cnt, (struct obj *)); +#endif + +/* ### hacklib.c ### */ + +E boolean FDECL(digit, (CHAR_P)); +E boolean FDECL(letter, (CHAR_P)); +E char FDECL(highc, (CHAR_P)); +E char FDECL(lowc, (CHAR_P)); +E char *FDECL(lcase, (char *)); +E char *FDECL(upstart, (char *)); +E char *FDECL(mungspaces, (char *)); +E char *FDECL(eos, (char *)); +E char *FDECL(strkitten, (char *,CHAR_P)); +E char *FDECL(s_suffix, (const char *)); +E char *FDECL(xcrypt, (const char *,char *)); +E boolean FDECL(onlyspace, (const char *)); +E char *FDECL(tabexpand, (char *)); +E char *FDECL(visctrl, (CHAR_P)); +E const char *FDECL(ordin, (int)); +E char *FDECL(sitoa, (int)); +E int FDECL(sgn, (int)); +E int FDECL(rounddiv, (long,int)); +E int FDECL(dist2, (int,int,int,int)); +E int FDECL(distmin, (int,int,int,int)); +E boolean FDECL(online2, (int,int,int,int)); +E boolean FDECL(pmatch, (const char *,const char *)); +#ifndef STRNCMPI +E int FDECL(strncmpi, (const char *,const char *,int)); +#endif +#ifndef STRSTRI +E char *FDECL(strstri, (const char *,const char *)); +#endif +E boolean FDECL(fuzzymatch, (const char *,const char *,const char *,BOOLEAN_P)); +E void NDECL(setrandom); +E int NDECL(getyear); +#if 0 +E char *FDECL(yymmdd, (time_t)); +#endif +E long FDECL(yyyymmdd, (time_t)); +E int NDECL(phase_of_the_moon); +E boolean NDECL(friday_13th); +E int NDECL(night); +E int NDECL(midnight); + +/* ### invent.c ### */ + +E void FDECL(assigninvlet, (struct obj *)); +E struct obj *FDECL(merge_choice, (struct obj *,struct obj *)); +E int FDECL(merged, (struct obj **,struct obj **)); +#ifdef USE_TRAMPOLI +E int FDECL(ckunpaid, (struct obj *)); +#endif +E void FDECL(addinv_core1, (struct obj *)); +E void FDECL(addinv_core2, (struct obj *)); +E struct obj *FDECL(addinv, (struct obj *)); +E struct obj *FDECL(hold_another_object, + (struct obj *,const char *,const char *,const char *)); +E void FDECL(useupall, (struct obj *)); +E void FDECL(useup, (struct obj *)); +E void FDECL(consume_obj_charge, (struct obj *,BOOLEAN_P)); +E void FDECL(freeinv_core, (struct obj *)); +E void FDECL(freeinv, (struct obj *)); +E void FDECL(delallobj, (int,int)); +E void FDECL(delobj, (struct obj *)); +E struct obj *FDECL(sobj_at, (int,int,int)); +E struct obj *FDECL(carrying, (int)); +E boolean NDECL(have_lizard); +E struct obj *FDECL(o_on, (unsigned int,struct obj *)); +E boolean FDECL(obj_here, (struct obj *,int,int)); +E boolean NDECL(wearing_armor); +E boolean FDECL(is_worn, (struct obj *)); +E struct obj *FDECL(g_at, (int,int)); +E struct obj *FDECL(mkgoldobj, (long)); +E struct obj *FDECL(getobj, (const char *,const char *)); +E int FDECL(ggetobj, (const char *,int (*)(OBJ_P),int,BOOLEAN_P,unsigned *)); +E void FDECL(fully_identify_obj, (struct obj *)); +E int FDECL(identify, (struct obj *)); +E void FDECL(identify_pack, (int)); +E int FDECL(askchain, (struct obj **,const char *,int,int (*)(OBJ_P), + int (*)(OBJ_P),int,const char *)); +E void FDECL(prinv, (const char *,struct obj *,long)); +E char *FDECL(xprname, (struct obj *,const char *,CHAR_P,BOOLEAN_P,long,long)); +E int NDECL(ddoinv); +E char FDECL(display_inventory, (const char *,BOOLEAN_P)); +E int FDECL(display_binventory, (int,int,BOOLEAN_P)); +E struct obj *FDECL(display_cinventory,(struct obj *)); +E struct obj *FDECL(display_minventory,(struct monst *,int,char *)); +E int NDECL(dotypeinv); +E const char *FDECL(dfeature_at, (int,int,char *)); +E int FDECL(look_here, (int,BOOLEAN_P)); +E int NDECL(dolook); +E boolean FDECL(will_feel_cockatrice, (struct obj *,BOOLEAN_P)); +E void FDECL(feel_cockatrice, (struct obj *,BOOLEAN_P)); +E void FDECL(stackobj, (struct obj *)); +E int NDECL(doprgold); +E int NDECL(doprwep); +E int NDECL(doprarm); +E int NDECL(doprring); +E int NDECL(dopramulet); +E int NDECL(doprtool); +E int NDECL(doprinuse); +E void FDECL(useupf, (struct obj *,long)); +E char *FDECL(let_to_name, (CHAR_P,BOOLEAN_P)); +E void NDECL(free_invbuf); +E void NDECL(reassign); +E int NDECL(doorganize); +E int FDECL(count_unpaid, (struct obj *)); +E int FDECL(count_buc, (struct obj *,int)); +E void FDECL(carry_obj_effects, (struct obj *)); +E const char *FDECL(currency, (long)); +E void FDECL(silly_thing, (const char *,struct obj *)); + +/* ### ioctl.c ### */ + +#if defined(UNIX) || defined(__BEOS__) +E void NDECL(getwindowsz); +E void NDECL(getioctls); +E void NDECL(setioctls); +# ifdef SUSPEND +E int NDECL(dosuspend); +# endif /* SUSPEND */ +#endif /* UNIX || __BEOS__ */ + +/* ### light.c ### */ + +E void FDECL(new_light_source, (XCHAR_P, XCHAR_P, int, int, genericptr_t)); +E void FDECL(del_light_source, (int, genericptr_t)); +E void FDECL(do_light_sources, (char **)); +E struct monst *FDECL(find_mid, (unsigned, unsigned)); +E void FDECL(save_light_sources, (int, int, int)); +E void FDECL(restore_light_sources, (int)); +E void FDECL(relink_light_sources, (BOOLEAN_P)); +E void FDECL(obj_move_light_source, (struct obj *, struct obj *)); +E boolean NDECL(any_light_source); +E void FDECL(snuff_light_source, (int, int)); +E boolean FDECL(obj_sheds_light, (struct obj *)); +E boolean FDECL(obj_is_burning, (struct obj *)); +E void FDECL(obj_split_light_source, (struct obj *, struct obj *)); +E void FDECL(obj_merge_light_sources, (struct obj *,struct obj *)); +E int FDECL(candle_light_range, (struct obj *)); +#ifdef WIZARD +E int NDECL(wiz_light_sources); +#endif + +/* ### lock.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(forcelock); +E int NDECL(picklock); +#endif +E boolean FDECL(picking_lock, (int *,int *)); +E boolean FDECL(picking_at, (int,int)); +E void NDECL(reset_pick); +E int FDECL(pick_lock, (struct obj *)); +E int NDECL(doforce); +E boolean FDECL(boxlock, (struct obj *,struct obj *)); +E boolean FDECL(doorlock, (struct obj *,int,int)); +E int NDECL(doopen); +E int NDECL(doclose); + +#ifdef MAC +/* These declarations are here because the main code calls them. */ + +/* ### macfile.c ### */ + +E int FDECL(maccreat, (const char *,long)); +E int FDECL(macopen, (const char *,int,long)); +E int FDECL(macclose, (int)); +E int FDECL(macread, (int,void *,unsigned)); +E int FDECL(macwrite, (int,void *,unsigned)); +E long FDECL(macseek, (int,long,short)); +E int FDECL(macunlink, (const char *)); + +/* ### macsnd.c ### */ + +E void FDECL(mac_speaker, (struct obj *,char *)); + +/* ### macunix.c ### */ + +E void FDECL(regularize, (char *)); +E void NDECL(getlock); + +/* ### macwin.c ### */ + +E void FDECL(lock_mouse_cursor, (Boolean)); +E int NDECL(SanePositions); + +/* ### mttymain.c ### */ + +E void FDECL(getreturn, (char *)); +E void VDECL(msmsg, (const char *,...)); +E void NDECL(gettty); +E void NDECL(setftty); +E void FDECL(settty, (const char *)); +E int NDECL(tgetch); +E void FDECL(cmov, (int x, int y)); +E void FDECL(nocmov, (int x, int y)); + +#endif /* MAC */ + +/* ### mail.c ### */ + +#ifdef MAIL +# ifdef UNIX +E void NDECL(getmailstatus); +# endif +E void NDECL(ckmailstatus); +E void FDECL(readmail, (struct obj *)); +#endif /* MAIL */ + +/* ### makemon.c ### */ + +E boolean FDECL(is_home_elemental, (struct permonst *)); +E struct monst *FDECL(clone_mon, (struct monst *,XCHAR_P,XCHAR_P)); +E struct monst *FDECL(makemon, (struct permonst *,int,int,int)); +E boolean FDECL(create_critters, (int,struct permonst *)); +E struct permonst *NDECL(rndmonst); +E void FDECL(reset_rndmonst, (int)); +E struct permonst *FDECL(mkclass, (CHAR_P,int)); +E int FDECL(adj_lev, (struct permonst *)); +E struct permonst *FDECL(grow_up, (struct monst *,struct monst *)); +E int FDECL(mongets, (struct monst *,int)); +E int FDECL(golemhp, (int)); +E boolean FDECL(peace_minded, (struct permonst *)); +E void FDECL(set_malign, (struct monst *)); +E void FDECL(set_mimic_sym, (struct monst *)); +E int FDECL(mbirth_limit, (int)); +E void FDECL(mimic_hit_msg, (struct monst *, SHORT_P)); +#ifdef GOLDOBJ +E void FDECL(mkmonmoney, (struct monst *, long)); +#endif +E void FDECL(bagotricks, (struct obj *)); +E boolean FDECL(propagate, (int, BOOLEAN_P,BOOLEAN_P)); + +/* ### mapglyph.c ### */ + +E void FDECL(mapglyph, (int, int *, int *, unsigned *, int, int)); + +/* ### mcastu.c ### */ + +E int FDECL(castmu, (struct monst *,struct attack *,BOOLEAN_P,BOOLEAN_P)); +E int FDECL(buzzmu, (struct monst *,struct attack *)); + +/* ### mhitm.c ### */ + +E int FDECL(fightm, (struct monst *)); +E int FDECL(mattackm, (struct monst *,struct monst *)); +E int FDECL(noattacks, (struct permonst *)); +E int FDECL(sleep_monst, (struct monst *,int,int)); +E void FDECL(slept_monst, (struct monst *)); +E long FDECL(attk_protection, (int)); + +/* ### mhitu.c ### */ + +E const char *FDECL(mpoisons_subj, (struct monst *,struct attack *)); +E void NDECL(u_slow_down); +E struct monst *NDECL(cloneu); +E void FDECL(expels, (struct monst *,struct permonst *,BOOLEAN_P)); +E struct attack *FDECL(getmattk, (struct permonst *,int,int *,struct attack *)); +E int FDECL(mattacku, (struct monst *)); +E int FDECL(magic_negation, (struct monst *)); +E int FDECL(gazemu, (struct monst *,struct attack *)); +E void FDECL(mdamageu, (struct monst *,int)); +E int FDECL(could_seduce, (struct monst *,struct monst *,struct attack *)); +#ifdef SEDUCE +E int FDECL(doseduce, (struct monst *)); +#endif + +/* ### minion.c ### */ + +E void FDECL(msummon, (struct monst *)); +E void FDECL(summon_minion, (ALIGNTYP_P,BOOLEAN_P)); +E int FDECL(demon_talk, (struct monst *)); +E long FDECL(bribe, (struct monst *)); +E int FDECL(dprince, (ALIGNTYP_P)); +E int FDECL(dlord, (ALIGNTYP_P)); +E int NDECL(llord); +E int FDECL(ndemon, (ALIGNTYP_P)); +E int NDECL(lminion); + +/* ### mklev.c ### */ + +#ifdef USE_TRAMPOLI +E int FDECL(do_comp, (genericptr_t,genericptr_t)); +#endif +E void NDECL(sort_rooms); +E void FDECL(add_room, (int,int,int,int,BOOLEAN_P,SCHAR_P,BOOLEAN_P)); +E void FDECL(add_subroom, (struct mkroom *,int,int,int,int, + BOOLEAN_P,SCHAR_P,BOOLEAN_P)); +E void NDECL(makecorridors); +E void FDECL(add_door, (int,int,struct mkroom *)); +E void NDECL(mklev); +#ifdef SPECIALIZATION +E void FDECL(topologize, (struct mkroom *,BOOLEAN_P)); +#else +E void FDECL(topologize, (struct mkroom *)); +#endif +E void FDECL(place_branch, (branch *,XCHAR_P,XCHAR_P)); +E boolean FDECL(occupied, (XCHAR_P,XCHAR_P)); +E int FDECL(okdoor, (XCHAR_P,XCHAR_P)); +E void FDECL(dodoor, (int,int,struct mkroom *)); +E void FDECL(mktrap, (int,int,struct mkroom *,coord*)); +E void FDECL(mkstairs, (XCHAR_P,XCHAR_P,CHAR_P,struct mkroom *)); +E void NDECL(mkinvokearea); + +/* ### mkmap.c ### */ + +void FDECL(flood_fill_rm, (int,int,int,BOOLEAN_P,BOOLEAN_P)); +void FDECL(remove_rooms, (int,int,int,int)); + +/* ### mkmaze.c ### */ + +E void FDECL(wallification, (int,int,int,int)); +E void FDECL(walkfrom, (int,int)); +E void FDECL(makemaz, (const char *)); +E void FDECL(mazexy, (coord *)); +E void NDECL(bound_digging); +E void FDECL(mkportal, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E boolean FDECL(bad_location, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E void FDECL(place_lregion, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, + XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, + XCHAR_P,d_level *)); +E void NDECL(movebubbles); +E void NDECL(water_friction); +E void FDECL(save_waterlevel, (int,int)); +E void FDECL(restore_waterlevel, (int)); +E const char *FDECL(waterbody_name, (XCHAR_P,XCHAR_P)); + +/* ### mkobj.c ### */ + +E struct obj *FDECL(mkobj_at, (CHAR_P,int,int,BOOLEAN_P)); +E struct obj *FDECL(mksobj_at, (int,int,int,BOOLEAN_P,BOOLEAN_P)); +E struct obj *FDECL(mkobj, (CHAR_P,BOOLEAN_P)); +E int NDECL(rndmonnum); +E struct obj *FDECL(splitobj, (struct obj *,long)); +E void FDECL(replace_object, (struct obj *,struct obj *)); +E void FDECL(bill_dummy_object, (struct obj *)); +E struct obj *FDECL(mksobj, (int,BOOLEAN_P,BOOLEAN_P)); +E int FDECL(bcsign, (struct obj *)); +E int FDECL(weight, (struct obj *)); +E struct obj *FDECL(mkgold, (long,int,int)); +E struct obj *FDECL(mkcorpstat, + (int,struct monst *,struct permonst *,int,int,BOOLEAN_P)); +E struct obj *FDECL(obj_attach_mid, (struct obj *, unsigned)); +E struct monst *FDECL(get_mtraits, (struct obj *, BOOLEAN_P)); +E struct obj *FDECL(mk_tt_object, (int,int,int)); +E struct obj *FDECL(mk_named_object, + (int,struct permonst *,int,int,const char *)); +E struct obj *FDECL(rnd_treefruit_at, (int, int)); +E void FDECL(start_corpse_timeout, (struct obj *)); +E void FDECL(bless, (struct obj *)); +E void FDECL(unbless, (struct obj *)); +E void FDECL(curse, (struct obj *)); +E void FDECL(uncurse, (struct obj *)); +E void FDECL(blessorcurse, (struct obj *,int)); +E boolean FDECL(is_flammable, (struct obj *)); +E boolean FDECL(is_rottable, (struct obj *)); +E void FDECL(place_object, (struct obj *,int,int)); +E void FDECL(remove_object, (struct obj *)); +E void FDECL(discard_minvent, (struct monst *)); +E void FDECL(obj_extract_self, (struct obj *)); +E void FDECL(extract_nobj, (struct obj *, struct obj **)); +E void FDECL(extract_nexthere, (struct obj *, struct obj **)); +E int FDECL(add_to_minv, (struct monst *, struct obj *)); +E struct obj *FDECL(add_to_container, (struct obj *, struct obj *)); +E void FDECL(add_to_migration, (struct obj *)); +E void FDECL(add_to_buried, (struct obj *)); +E void FDECL(dealloc_obj, (struct obj *)); +E void FDECL(obj_ice_effects, (int, int, BOOLEAN_P)); +E long FDECL(peek_at_iced_corpse_age, (struct obj *)); +#ifdef WIZARD +E void NDECL(obj_sanity_check); +#endif + +/* ### mkroom.c ### */ + +E void FDECL(mkroom, (int)); +E void FDECL(fill_zoo, (struct mkroom *)); +E boolean FDECL(nexttodoor, (int,int)); +E boolean FDECL(has_dnstairs, (struct mkroom *)); +E boolean FDECL(has_upstairs, (struct mkroom *)); +E int FDECL(somex, (struct mkroom *)); +E int FDECL(somey, (struct mkroom *)); +E boolean FDECL(inside_room, (struct mkroom *,XCHAR_P,XCHAR_P)); +E boolean FDECL(somexy, (struct mkroom *,coord *)); +E void FDECL(mkundead, (coord *,BOOLEAN_P,int)); +E struct permonst *NDECL(courtmon); +E void FDECL(save_rooms, (int)); +E void FDECL(rest_rooms, (int)); +E struct mkroom *FDECL(search_special, (SCHAR_P)); + +/* ### mon.c ### */ + +E int FDECL(undead_to_corpse, (int)); +E int FDECL(genus, (int,int)); +E int FDECL(pm_to_cham, (int)); +E int FDECL(minliquid, (struct monst *)); +E int NDECL(movemon); +E int FDECL(meatmetal, (struct monst *)); +E int FDECL(meatobj, (struct monst *)); +E void FDECL(mpickgold, (struct monst *)); +E boolean FDECL(mpickstuff, (struct monst *,const char *)); +E int FDECL(curr_mon_load, (struct monst *)); +E int FDECL(max_mon_load, (struct monst *)); +E boolean FDECL(can_carry, (struct monst *,struct obj *)); +E int FDECL(mfndpos, (struct monst *,coord *,long *,long)); +E boolean FDECL(monnear, (struct monst *,int,int)); +E void NDECL(dmonsfree); +E int FDECL(mcalcmove, (struct monst*)); +E void NDECL(mcalcdistress); +E void FDECL(replmon, (struct monst *,struct monst *)); +E void FDECL(relmon, (struct monst *)); +E struct obj *FDECL(mlifesaver, (struct monst *)); +E boolean FDECL(corpse_chance,(struct monst *,struct monst *,BOOLEAN_P)); +E void FDECL(mondead, (struct monst *)); +E void FDECL(mondied, (struct monst *)); +E void FDECL(mongone, (struct monst *)); +E void FDECL(monstone, (struct monst *)); +E void FDECL(monkilled, (struct monst *,const char *,int)); +E void FDECL(unstuck, (struct monst *)); +E void FDECL(killed, (struct monst *)); +E void FDECL(xkilled, (struct monst *,int)); +E void FDECL(mon_to_stone, (struct monst*)); +E void FDECL(mnexto, (struct monst *)); +E boolean FDECL(mnearto, (struct monst *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E void FDECL(poisontell, (int)); +E void FDECL(poisoned, (const char *,int,const char *,int)); +E void FDECL(m_respond, (struct monst *)); +E void FDECL(setmangry, (struct monst *)); +E void FDECL(wakeup, (struct monst *)); +E void NDECL(wake_nearby); +E void FDECL(wake_nearto, (int,int,int)); +E void FDECL(seemimic, (struct monst *)); +E void NDECL(rescham); +E void NDECL(restartcham); +E void FDECL(restore_cham, (struct monst *)); +E void FDECL(mon_animal_list, (BOOLEAN_P)); +E int FDECL(newcham, (struct monst *,struct permonst *,BOOLEAN_P,BOOLEAN_P)); +E int FDECL(can_be_hatched, (int)); +E int FDECL(egg_type_from_parent, (int,BOOLEAN_P)); +E boolean FDECL(dead_species, (int,BOOLEAN_P)); +E void NDECL(kill_genocided_monsters); +E void FDECL(golemeffects, (struct monst *,int,int)); +E boolean FDECL(angry_guards, (BOOLEAN_P)); +E void NDECL(pacify_guards); + +/* ### mondata.c ### */ + +E void FDECL(set_mon_data, (struct monst *,struct permonst *,int)); +E struct attack *FDECL(attacktype_fordmg, (struct permonst *,int,int)); +E boolean FDECL(attacktype, (struct permonst *,int)); +E boolean FDECL(poly_when_stoned, (struct permonst *)); +E boolean FDECL(resists_drli, (struct monst *)); +E boolean FDECL(resists_magm, (struct monst *)); +E boolean FDECL(resists_blnd, (struct monst *)); +E boolean FDECL(can_blnd, (struct monst *,struct monst *,UCHAR_P,struct obj *)); +E boolean FDECL(ranged_attk, (struct permonst *)); +E boolean FDECL(hates_silver, (struct permonst *)); +E boolean FDECL(passes_bars, (struct permonst *)); +E boolean FDECL(can_track, (struct permonst *)); +E boolean FDECL(breakarm, (struct permonst *)); +E boolean FDECL(sliparm, (struct permonst *)); +E boolean FDECL(sticks, (struct permonst *)); +E int FDECL(num_horns, (struct permonst *)); +/* E boolean FDECL(canseemon, (struct monst *)); */ +E struct attack *FDECL(dmgtype_fromattack, (struct permonst *,int,int)); +E boolean FDECL(dmgtype, (struct permonst *,int)); +E int FDECL(max_passive_dmg, (struct monst *,struct monst *)); +E int FDECL(monsndx, (struct permonst *)); +E int FDECL(name_to_mon, (const char *)); +E int FDECL(gender, (struct monst *)); +E int FDECL(pronoun_gender, (struct monst *)); +E boolean FDECL(levl_follower, (struct monst *)); +E int FDECL(little_to_big, (int)); +E int FDECL(big_to_little, (int)); +E const char *FDECL(locomotion, (const struct permonst *,const char *)); +E const char *FDECL(stagger, (const struct permonst *,const char *)); +E const char *FDECL(on_fire, (struct permonst *,struct attack *)); +E const struct permonst *FDECL(raceptr, (struct monst *)); + +/* ### monmove.c ### */ + +E boolean FDECL(itsstuck, (struct monst *)); +E boolean FDECL(mb_trapped, (struct monst *)); +E void FDECL(mon_regen, (struct monst *,BOOLEAN_P)); +E int FDECL(dochugw, (struct monst *)); +E boolean FDECL(onscary, (int,int,struct monst *)); +E void FDECL(monflee, (struct monst *, int, BOOLEAN_P, BOOLEAN_P)); +E int FDECL(dochug, (struct monst *)); +E int FDECL(m_move, (struct monst *,int)); +E boolean FDECL(closed_door, (int,int)); +E boolean FDECL(accessible, (int,int)); +E void FDECL(set_apparxy, (struct monst *)); +E boolean FDECL(can_ooze, (struct monst *)); + +/* ### monst.c ### */ + +E void NDECL(monst_init); + +/* ### monstr.c ### */ + +E void NDECL(monstr_init); + +/* ### mplayer.c ### */ + +E struct monst *FDECL(mk_mplayer, (struct permonst *,XCHAR_P, + XCHAR_P,BOOLEAN_P)); +E void FDECL(create_mplayers, (int,BOOLEAN_P)); +E void FDECL(mplayer_talk, (struct monst *)); + +#if defined(MICRO) || defined(WIN32) + +/* ### msdos.c,os2.c,tos.c,winnt.c ### */ + +# ifndef WIN32 +E int NDECL(tgetch); +# endif +# ifndef TOS +E char NDECL(switchar); +# endif +# ifndef __GO32__ +E long FDECL(freediskspace, (char *)); +# ifdef MSDOS +E int FDECL(findfirst_file, (char *)); +E int NDECL(findnext_file); +E long FDECL(filesize_nh, (char *)); +# else +E int FDECL(findfirst, (char *)); +E int NDECL(findnext); +E long FDECL(filesize, (char *)); +# endif /* MSDOS */ +E char *NDECL(foundfile_buffer); +# endif /* __GO32__ */ +E void FDECL(chdrive, (char *)); +# ifndef TOS +E void NDECL(disable_ctrlP); +E void NDECL(enable_ctrlP); +# endif +# if defined(MICRO) && !defined(WINNT) +E void NDECL(get_scr_size); +# ifndef TOS +E void FDECL(gotoxy, (int,int)); +# endif +# endif +# ifdef TOS +E int FDECL(_copyfile, (char *,char *)); +E int NDECL(kbhit); +E void NDECL(set_colors); +E void NDECL(restore_colors); +# ifdef SUSPEND +E int NDECL(dosuspend); +# endif +# endif /* TOS */ +# ifdef WIN32 +E char *FDECL(get_username, (int *)); +E void FDECL(nt_regularize, (char *)); +E int NDECL((*nt_kbhit)); +E void FDECL(Delay, (int)); +# endif /* WIN32 */ +#endif /* MICRO || WIN32 */ + +/* ### mthrowu.c ### */ + +E int FDECL(thitu, (int,int,struct obj *,const char *)); +E int FDECL(ohitmon, (struct monst *,struct obj *,int,BOOLEAN_P)); +E void FDECL(thrwmu, (struct monst *)); +E int FDECL(spitmu, (struct monst *,struct attack *)); +E int FDECL(breamu, (struct monst *,struct attack *)); +E boolean FDECL(linedup, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E boolean FDECL(lined_up, (struct monst *)); +E struct obj *FDECL(m_carrying, (struct monst *,int)); +E void FDECL(m_useup, (struct monst *,struct obj *)); +E void FDECL(m_throw, (struct monst *,int,int,int,int,int,struct obj *)); +E boolean FDECL(hits_bars, (struct obj **,int,int,int,int)); + +/* ### muse.c ### */ + +E boolean FDECL(find_defensive, (struct monst *)); +E int FDECL(use_defensive, (struct monst *)); +E int FDECL(rnd_defensive_item, (struct monst *)); +E boolean FDECL(find_offensive, (struct monst *)); +#ifdef USE_TRAMPOLI +E int FDECL(mbhitm, (struct monst *,struct obj *)); +#endif +E int FDECL(use_offensive, (struct monst *)); +E int FDECL(rnd_offensive_item, (struct monst *)); +E boolean FDECL(find_misc, (struct monst *)); +E int FDECL(use_misc, (struct monst *)); +E int FDECL(rnd_misc_item, (struct monst *)); +E boolean FDECL(searches_for_item, (struct monst *,struct obj *)); +E boolean FDECL(mon_reflects, (struct monst *,const char *)); +E boolean FDECL(ureflects, (const char *,const char *)); +E boolean FDECL(munstone, (struct monst *,BOOLEAN_P)); + +/* ### music.c ### */ + +E void NDECL(awaken_soldiers); +E int FDECL(do_play_instrument, (struct obj *)); + +/* ### nhlan.c ### */ +#ifdef LAN_FEATURES +E void NDECL(init_lan_features); +E char *NDECL(lan_username); +# ifdef LAN_MAIL +E boolean NDECL(lan_mail_check); +E void FDECL(lan_mail_read, (struct obj *)); +E void NDECL(lan_mail_init); +E void NDECL(lan_mail_finish); +E void NDECL(lan_mail_terminate); +# endif +#endif + +/* ### nttty.c ### */ + +#ifdef WIN32CON +E void NDECL(get_scr_size); +E int NDECL(nttty_kbhit); +E void NDECL(nttty_open); +E void NDECL(nttty_rubout); +E int NDECL(tgetch); +E int FDECL(ntposkey,(int *, int *, int *)); +E void FDECL(set_output_mode, (int)); +E void NDECL(synch_cursor); +#endif + +/* ### o_init.c ### */ + +E void NDECL(init_objects); +E int NDECL(find_skates); +E void NDECL(oinit); +E void FDECL(savenames, (int,int)); +E void FDECL(restnames, (int)); +E void FDECL(discover_object, (int,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(undiscover_object, (int)); +E int NDECL(dodiscovered); + +/* ### objects.c ### */ + +E void NDECL(objects_init); + +/* ### objnam.c ### */ + +E char *FDECL(obj_typename, (int)); +E char *FDECL(simple_typename, (int)); +E boolean FDECL(obj_is_pname, (struct obj *)); +E char *FDECL(distant_name, (struct obj *,char *(*)(OBJ_P))); +E char *FDECL(fruitname, (BOOLEAN_P)); +E char *FDECL(xname, (struct obj *)); +E char *FDECL(mshot_xname, (struct obj *)); +E boolean FDECL(the_unique_obj, (struct obj *obj)); +E char *FDECL(doname, (struct obj *)); +E boolean FDECL(not_fully_identified, (struct obj *)); +E char *FDECL(corpse_xname, (struct obj *,BOOLEAN_P)); +E char *FDECL(cxname, (struct obj *)); +E char *FDECL(killer_xname, (struct obj *)); +E const char *FDECL(singular, (struct obj *,char *(*)(OBJ_P))); +E char *FDECL(an, (const char *)); +E char *FDECL(An, (const char *)); +E char *FDECL(The, (const char *)); +E char *FDECL(the, (const char *)); +E char *FDECL(aobjnam, (struct obj *,const char *)); +E char *FDECL(Tobjnam, (struct obj *,const char *)); +E char *FDECL(otense, (struct obj *,const char *)); +E char *FDECL(vtense, (const char *,const char *)); +E char *FDECL(Doname2, (struct obj *)); +E char *FDECL(yname, (struct obj *)); +E char *FDECL(Yname2, (struct obj *)); +E char *FDECL(ysimple_name, (struct obj *)); +E char *FDECL(Ysimple_name2, (struct obj *)); +E char *FDECL(makeplural, (const char *)); +E char *FDECL(makesingular, (const char *)); +E struct obj *FDECL(readobjnam, (char *,struct obj *,BOOLEAN_P)); +E int FDECL(rnd_class, (int,int)); +E const char *FDECL(cloak_simple_name, (struct obj *)); +E const char *FDECL(mimic_obj_name, (struct monst *)); + +/* ### options.c ### */ + +E boolean FDECL(match_optname, (const char *,const char *,int,BOOLEAN_P)); +E void NDECL(initoptions); +E void FDECL(parseoptions, (char *,BOOLEAN_P,BOOLEAN_P)); +E int NDECL(doset); +E int NDECL(dotogglepickup); +E void NDECL(option_help); +E void FDECL(next_opt, (winid,const char *)); +E int FDECL(fruitadd, (char *)); +E int FDECL(choose_classes_menu, (const char *,int,BOOLEAN_P,char *,char *)); +E void FDECL(add_menu_cmd_alias, (CHAR_P, CHAR_P)); +E char FDECL(map_menu_cmd, (CHAR_P)); +E void FDECL(assign_warnings, (uchar *)); +E char *FDECL(nh_getenv, (const char *)); +E void FDECL(set_duplicate_opt_detection, (int)); +E void FDECL(set_wc_option_mod_status, (unsigned long, int)); +E void FDECL(set_wc2_option_mod_status, (unsigned long, int)); +E void FDECL(set_option_mod_status, (const char *,int)); +#ifdef AUTOPICKUP_EXCEPTIONS +E int FDECL(add_autopickup_exception, (const char *)); +E void NDECL(free_autopickup_exceptions); +#endif /* AUTOPICKUP_EXCEPTIONS */ + +/* ### pager.c ### */ + +E int NDECL(dowhatis); +E int NDECL(doquickwhatis); +E int NDECL(doidtrap); +E int NDECL(dowhatdoes); +E char *FDECL(dowhatdoes_core,(CHAR_P, char *)); +E int NDECL(dohelp); +E int NDECL(dohistory); + +/* ### pcmain.c ### */ + +#if defined(MICRO) || defined(WIN32) +# ifdef CHDIR +E void FDECL(chdirx, (char *,BOOLEAN_P)); +# endif /* CHDIR */ +#endif /* MICRO || WIN32 */ + +/* ### pcsys.c ### */ + +#if defined(MICRO) || defined(WIN32) +E void NDECL(flushout); +E int NDECL(dosh); +# ifdef MFLOPPY +E void FDECL(eraseall, (const char *,const char *)); +E void FDECL(copybones, (int)); +E void NDECL(playwoRAMdisk); +E int FDECL(saveDiskPrompt, (int)); +E void NDECL(gameDiskPrompt); +# endif +E void FDECL(append_slash, (char *)); +E void FDECL(getreturn, (const char *)); +# ifndef AMIGA +E void VDECL(msmsg, (const char *,...)); +# endif +E FILE *FDECL(fopenp, (const char *,const char *)); +#endif /* MICRO || WIN32 */ + +/* ### pctty.c ### */ + +#if defined(MICRO) || defined(WIN32) +E void NDECL(gettty); +E void FDECL(settty, (const char *)); +E void NDECL(setftty); +E void VDECL(error, (const char *,...)); +#if defined(TIMED_DELAY) && defined(_MSC_VER) +E void FDECL(msleep, (unsigned)); +#endif +#endif /* MICRO || WIN32 */ + +/* ### pcunix.c ### */ + +#if defined(MICRO) +E void FDECL(regularize, (char *)); +#endif /* MICRO */ +#if defined(PC_LOCKING) +E void NDECL(getlock); +#endif + +/* ### pickup.c ### */ + +#ifdef GOLDOBJ +E int FDECL(collect_obj_classes, + (char *,struct obj *,BOOLEAN_P,boolean FDECL((*),(OBJ_P)), int *)); +#else +E int FDECL(collect_obj_classes, + (char *,struct obj *,BOOLEAN_P,BOOLEAN_P,boolean FDECL((*),(OBJ_P)), int *)); +#endif +E void FDECL(add_valid_menu_class, (int)); +E boolean FDECL(allow_all, (struct obj *)); +E boolean FDECL(allow_category, (struct obj *)); +E boolean FDECL(is_worn_by_type, (struct obj *)); +#ifdef USE_TRAMPOLI +E int FDECL(ck_bag, (struct obj *)); +E int FDECL(in_container, (struct obj *)); +E int FDECL(out_container, (struct obj *)); +#endif +E int FDECL(pickup, (int)); +E int FDECL(pickup_object, (struct obj *, long, BOOLEAN_P)); +E int FDECL(query_category, (const char *, struct obj *, int, + menu_item **, int)); +E int FDECL(query_objlist, (const char *, struct obj *, int, + menu_item **, int, boolean (*)(OBJ_P))); +E struct obj *FDECL(pick_obj, (struct obj *)); +E int NDECL(encumber_msg); +E int NDECL(doloot); +E int FDECL(use_container, (struct obj *,int)); +E int FDECL(loot_mon, (struct monst *,int *,boolean *)); +E const char *FDECL(safe_qbuf, (const char *,unsigned, + const char *,const char *,const char *)); +E boolean FDECL(is_autopickup_exception, (struct obj *, BOOLEAN_P)); + +/* ### pline.c ### */ + +E void VDECL(pline, (const char *,...)) PRINTF_F(1,2); +E void VDECL(Norep, (const char *,...)) PRINTF_F(1,2); +E void NDECL(free_youbuf); +E void VDECL(You, (const char *,...)) PRINTF_F(1,2); +E void VDECL(Your, (const char *,...)) PRINTF_F(1,2); +E void VDECL(You_feel, (const char *,...)) PRINTF_F(1,2); +E void VDECL(You_cant, (const char *,...)) PRINTF_F(1,2); +E void VDECL(You_hear, (const char *,...)) PRINTF_F(1,2); +E void VDECL(pline_The, (const char *,...)) PRINTF_F(1,2); +E void VDECL(There, (const char *,...)) PRINTF_F(1,2); +E void VDECL(verbalize, (const char *,...)) PRINTF_F(1,2); +E void VDECL(raw_printf, (const char *,...)) PRINTF_F(1,2); +E void VDECL(impossible, (const char *,...)) PRINTF_F(1,2); +E const char *FDECL(align_str, (ALIGNTYP_P)); +E void FDECL(mstatusline, (struct monst *)); +E void NDECL(ustatusline); +E void NDECL(self_invis_message); + +/* ### polyself.c ### */ + +E void NDECL(set_uasmon); +E void NDECL(change_sex); +E void FDECL(polyself, (BOOLEAN_P)); +E int FDECL(polymon, (int)); +E void NDECL(rehumanize); +E int NDECL(dobreathe); +E int NDECL(dospit); +E int NDECL(doremove); +E int NDECL(dospinweb); +E int NDECL(dosummon); +E int NDECL(dogaze); +E int NDECL(dohide); +E int NDECL(domindblast); +E void FDECL(skinback, (BOOLEAN_P)); +E const char *FDECL(mbodypart, (struct monst *,int)); +E const char *FDECL(body_part, (int)); +E int NDECL(poly_gender); +E void FDECL(ugolemeffects, (int,int)); + +/* ### potion.c ### */ + +E void FDECL(set_itimeout, (long *,long)); +E void FDECL(incr_itimeout, (long *,int)); +E void FDECL(make_confused, (long,BOOLEAN_P)); +E void FDECL(make_stunned, (long,BOOLEAN_P)); +E void FDECL(make_blinded, (long,BOOLEAN_P)); +E void FDECL(make_sick, (long, const char *, BOOLEAN_P,int)); +E void FDECL(make_vomiting, (long,BOOLEAN_P)); +E boolean FDECL(make_hallucinated, (long,BOOLEAN_P,long)); +E int NDECL(dodrink); +E int FDECL(dopotion, (struct obj *)); +E int FDECL(peffects, (struct obj *)); +E void FDECL(healup, (int,int,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(strange_feeling, (struct obj *,const char *)); +E void FDECL(potionhit, (struct monst *,struct obj *,BOOLEAN_P)); +E void FDECL(potionbreathe, (struct obj *)); +E boolean FDECL(get_wet, (struct obj *)); +E int NDECL(dodip); +E void FDECL(djinni_from_bottle, (struct obj *)); +E struct monst *FDECL(split_mon, (struct monst *,struct monst *)); +E const char *NDECL(bottlename); + +/* ### pray.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(prayer_done); +#endif +E int NDECL(dosacrifice); +E boolean FDECL(can_pray, (BOOLEAN_P)); +E int NDECL(dopray); +E const char *NDECL(u_gname); +E int NDECL(doturn); +E const char *NDECL(a_gname); +E const char *FDECL(a_gname_at, (XCHAR_P x,XCHAR_P y)); +E const char *FDECL(align_gname, (ALIGNTYP_P)); +E const char *FDECL(halu_gname, (ALIGNTYP_P)); +E const char *FDECL(align_gtitle, (ALIGNTYP_P)); +E void FDECL(altar_wrath, (int,int)); + + +/* ### priest.c ### */ + +E int FDECL(move_special, (struct monst *,BOOLEAN_P,SCHAR_P,BOOLEAN_P,BOOLEAN_P, + XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E char FDECL(temple_occupied, (char *)); +E int FDECL(pri_move, (struct monst *)); +E void FDECL(priestini, (d_level *,struct mkroom *,int,int,BOOLEAN_P)); +E char *FDECL(priestname, (struct monst *,char *)); +E boolean FDECL(p_coaligned, (struct monst *)); +E struct monst *FDECL(findpriest, (CHAR_P)); +E void FDECL(intemple, (int)); +E void FDECL(priest_talk, (struct monst *)); +E struct monst *FDECL(mk_roamer, (struct permonst *,ALIGNTYP_P, + XCHAR_P,XCHAR_P,BOOLEAN_P)); +E void FDECL(reset_hostility, (struct monst *)); +E boolean FDECL(in_your_sanctuary, (struct monst *,XCHAR_P,XCHAR_P)); +E void FDECL(ghod_hitsu, (struct monst *)); +E void NDECL(angry_priest); +E void NDECL(clearpriests); +E void FDECL(restpriest, (struct monst *,BOOLEAN_P)); + +/* ### quest.c ### */ + +E void NDECL(onquest); +E void NDECL(nemdead); +E void NDECL(artitouch); +E boolean NDECL(ok_to_quest); +E void FDECL(leader_speaks, (struct monst *)); +E void NDECL(nemesis_speaks); +E void FDECL(quest_chat, (struct monst *)); +E void FDECL(quest_talk, (struct monst *)); +E void FDECL(quest_stat_check, (struct monst *)); +E void FDECL(finish_quest, (struct obj *)); + +/* ### questpgr.c ### */ + +E void NDECL(load_qtlist); +E void NDECL(unload_qtlist); +E short FDECL(quest_info, (int)); +E const char *NDECL(ldrname); +E boolean FDECL(is_quest_artifact, (struct obj*)); +E void FDECL(com_pager, (int)); +E void FDECL(qt_pager, (int)); +E struct permonst *NDECL(qt_montype); + +/* ### random.c ### */ + +#if defined(RANDOM) && !defined(__GO32__) /* djgpp has its own random */ +E void FDECL(srandom, (unsigned)); +E char *FDECL(initstate, (unsigned,char *,int)); +E char *FDECL(setstate, (char *)); +E long NDECL(random); +#endif /* RANDOM */ + +/* ### read.c ### */ + +E int NDECL(doread); +E boolean FDECL(is_chargeable, (struct obj *)); +E void FDECL(recharge, (struct obj *,int)); +E void FDECL(forget_objects, (int)); +E void FDECL(forget_levels, (int)); +E void NDECL(forget_traps); +E void FDECL(forget_map, (int)); +E int FDECL(seffects, (struct obj *)); +#ifdef USE_TRAMPOLI +E void FDECL(set_lit, (int,int,genericptr_t)); +#endif +E void FDECL(litroom, (BOOLEAN_P,struct obj *)); +E void FDECL(do_genocide, (int)); +E void FDECL(punish, (struct obj *)); +E void NDECL(unpunish); +E boolean FDECL(cant_create, (int *, BOOLEAN_P)); +#ifdef WIZARD +E boolean NDECL(create_particular); +#endif + +/* ### rect.c ### */ + +E void NDECL(init_rect); +E NhRect *FDECL(get_rect, (NhRect *)); +E NhRect *NDECL(rnd_rect); +E void FDECL(remove_rect, (NhRect *)); +E void FDECL(add_rect, (NhRect *)); +E void FDECL(split_rects, (NhRect *,NhRect *)); + +/* ## region.c ### */ +E void NDECL(clear_regions); +E void NDECL(run_regions); +E boolean FDECL(in_out_region, (XCHAR_P,XCHAR_P)); +E boolean FDECL(m_in_out_region, (struct monst *,XCHAR_P,XCHAR_P)); +E void NDECL(update_player_regions); +E void FDECL(update_monster_region, (struct monst *)); +E NhRegion *FDECL(visible_region_at, (XCHAR_P,XCHAR_P)); +E void FDECL(show_region, (NhRegion*, XCHAR_P, XCHAR_P)); +E void FDECL(save_regions, (int,int)); +E void FDECL(rest_regions, (int,BOOLEAN_P)); +E NhRegion* FDECL(create_gas_cloud, (XCHAR_P, XCHAR_P, int, int)); + +/* ### restore.c ### */ + +E void FDECL(inven_inuse, (BOOLEAN_P)); +E int FDECL(dorecover, (int)); +E void FDECL(trickery, (char *)); +E void FDECL(getlev, (int,int,XCHAR_P,BOOLEAN_P)); +E void NDECL(minit); +E boolean FDECL(lookup_id_mapping, (unsigned, unsigned *)); +#ifdef ZEROCOMP +E int FDECL(mread, (int,genericptr_t,unsigned int)); +#else +E void FDECL(mread, (int,genericptr_t,unsigned int)); +#endif + +/* ### rip.c ### */ + +E void FDECL(genl_outrip, (winid,int)); + +/* ### rnd.c ### */ + +E int FDECL(rn2, (int)); +E int FDECL(rnl, (int)); +E int FDECL(rnd, (int)); +E int FDECL(d, (int,int)); +E int FDECL(rne, (int)); +E int FDECL(rnz, (int)); + +/* ### role.c ### */ + +E boolean FDECL(validrole, (int)); +E boolean FDECL(validrace, (int, int)); +E boolean FDECL(validgend, (int, int, int)); +E boolean FDECL(validalign, (int, int, int)); +E int NDECL(randrole); +E int FDECL(randrace, (int)); +E int FDECL(randgend, (int, int)); +E int FDECL(randalign, (int, int)); +E int FDECL(str2role, (char *)); +E int FDECL(str2race, (char *)); +E int FDECL(str2gend, (char *)); +E int FDECL(str2align, (char *)); +E boolean FDECL(ok_role, (int, int, int, int)); +E int FDECL(pick_role, (int, int, int, int)); +E boolean FDECL(ok_race, (int, int, int, int)); +E int FDECL(pick_race, (int, int, int, int)); +E boolean FDECL(ok_gend, (int, int, int, int)); +E int FDECL(pick_gend, (int, int, int, int)); +E boolean FDECL(ok_align, (int, int, int, int)); +E int FDECL(pick_align, (int, int, int, int)); +E void NDECL(role_init); +E void NDECL(rigid_role_checks); +E void NDECL(plnamesuffix); +E const char *FDECL(Hello, (struct monst *)); +E const char *NDECL(Goodbye); +E char *FDECL(build_plselection_prompt, (char *, int, int, int, int, int)); +E char *FDECL(root_plselection_prompt, (char *, int, int, int, int, int)); + +/* ### rumors.c ### */ + +E char *FDECL(getrumor, (int,char *, BOOLEAN_P)); +E void FDECL(outrumor, (int,int)); +E void FDECL(outoracle, (BOOLEAN_P, BOOLEAN_P)); +E void FDECL(save_oracles, (int,int)); +E void FDECL(restore_oracles, (int)); +E int FDECL(doconsult, (struct monst *)); + +/* ### save.c ### */ + +E int NDECL(dosave); +#if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32) +E void FDECL(hangup, (int)); +#endif +E int NDECL(dosave0); +#ifdef INSURANCE +E void NDECL(savestateinlock); +#endif +#ifdef MFLOPPY +E boolean FDECL(savelev, (int,XCHAR_P,int)); +E boolean FDECL(swapin_file, (int)); +E void NDECL(co_false); +#else +E void FDECL(savelev, (int,XCHAR_P,int)); +#endif +E void FDECL(bufon, (int)); +E void FDECL(bufoff, (int)); +E void FDECL(bflush, (int)); +E void FDECL(bwrite, (int,genericptr_t,unsigned int)); +E void FDECL(bclose, (int)); +E void FDECL(savefruitchn, (int,int)); +E void NDECL(free_dungeons); +E void NDECL(freedynamicdata); + +/* ### shk.c ### */ + +#ifdef GOLDOBJ +E long FDECL(money2mon, (struct monst *, long)); +E void FDECL(money2u, (struct monst *, long)); +#endif +E char *FDECL(shkname, (struct monst *)); +E void FDECL(shkgone, (struct monst *)); +E void FDECL(set_residency, (struct monst *,BOOLEAN_P)); +E void FDECL(replshk, (struct monst *,struct monst *)); +E void FDECL(restshk, (struct monst *,BOOLEAN_P)); +E char FDECL(inside_shop, (XCHAR_P,XCHAR_P)); +E void FDECL(u_left_shop, (char *,BOOLEAN_P)); +E void FDECL(remote_burglary, (XCHAR_P,XCHAR_P)); +E void FDECL(u_entered_shop, (char *)); +E boolean FDECL(same_price, (struct obj *,struct obj *)); +E void NDECL(shopper_financial_report); +E int FDECL(inhishop, (struct monst *)); +E struct monst *FDECL(shop_keeper, (CHAR_P)); +E boolean FDECL(tended_shop, (struct mkroom *)); +E void FDECL(delete_contents, (struct obj *)); +E void FDECL(obfree, (struct obj *,struct obj *)); +E void FDECL(home_shk, (struct monst *,BOOLEAN_P)); +E void FDECL(make_happy_shk, (struct monst *,BOOLEAN_P)); +E void FDECL(hot_pursuit, (struct monst *)); +E void FDECL(make_angry_shk, (struct monst *,XCHAR_P,XCHAR_P)); +E int NDECL(dopay); +E boolean FDECL(paybill, (int)); +E void NDECL(finish_paybill); +E struct obj *FDECL(find_oid, (unsigned)); +E long FDECL(contained_cost, (struct obj *,struct monst *,long,BOOLEAN_P, BOOLEAN_P)); +E long FDECL(contained_gold, (struct obj *)); +E void FDECL(picked_container, (struct obj *)); +E long FDECL(unpaid_cost, (struct obj *)); +E void FDECL(addtobill, (struct obj *,BOOLEAN_P,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(splitbill, (struct obj *,struct obj *)); +E void FDECL(subfrombill, (struct obj *,struct monst *)); +E long FDECL(stolen_value, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(sellobj_state, (int)); +E void FDECL(sellobj, (struct obj *,XCHAR_P,XCHAR_P)); +E int FDECL(doinvbill, (int)); +E struct monst *FDECL(shkcatch, (struct obj *,XCHAR_P,XCHAR_P)); +E void FDECL(add_damage, (XCHAR_P,XCHAR_P,long)); +E int FDECL(repair_damage, (struct monst *,struct damage *,BOOLEAN_P)); +E int FDECL(shk_move, (struct monst *)); +E void FDECL(after_shk_move, (struct monst *)); +E boolean FDECL(is_fshk, (struct monst *)); +E void FDECL(shopdig, (int)); +E void FDECL(pay_for_damage, (const char *,BOOLEAN_P)); +E boolean FDECL(costly_spot, (XCHAR_P,XCHAR_P)); +E struct obj *FDECL(shop_object, (XCHAR_P,XCHAR_P)); +E void FDECL(price_quote, (struct obj *)); +E void FDECL(shk_chat, (struct monst *)); +E void FDECL(check_unpaid_usage, (struct obj *,BOOLEAN_P)); +E void FDECL(check_unpaid, (struct obj *)); +E void FDECL(costly_gold, (XCHAR_P,XCHAR_P,long)); +E boolean FDECL(block_door, (XCHAR_P,XCHAR_P)); +E boolean FDECL(block_entry, (XCHAR_P,XCHAR_P)); +E char *FDECL(shk_your, (char *,struct obj *)); +E char *FDECL(Shk_Your, (char *,struct obj *)); + +/* ### shknam.c ### */ + +E void FDECL(stock_room, (int,struct mkroom *)); +E boolean FDECL(saleable, (struct monst *,struct obj *)); +E int FDECL(get_shop_item, (int)); + +/* ### sit.c ### */ + +E void NDECL(take_gold); +E int NDECL(dosit); +E void NDECL(rndcurse); +E void NDECL(attrcurse); + +/* ### sounds.c ### */ + +E void NDECL(dosounds); +E const char *FDECL(growl_sound, (struct monst *)); +E void FDECL(growl, (struct monst *)); +E void FDECL(yelp, (struct monst *)); +E void FDECL(whimper, (struct monst *)); +E void FDECL(beg, (struct monst *)); +E int NDECL(dotalk); +#ifdef USER_SOUNDS +E int FDECL(add_sound_mapping, (const char *)); +E void FDECL(play_sound_for_message, (const char *)); +#endif + +/* ### sys/msdos/sound.c ### */ + +#ifdef MSDOS +E int FDECL(assign_soundcard, (char *)); +#endif + +/* ### sp_lev.c ### */ + +E boolean FDECL(check_room, (xchar *,xchar *,xchar *,xchar *,BOOLEAN_P)); +E boolean FDECL(create_room, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, + XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +E void FDECL(create_secret_door, (struct mkroom *,XCHAR_P)); +E boolean FDECL(dig_corridor, (coord *,coord *,BOOLEAN_P,SCHAR_P,SCHAR_P)); +E void FDECL(fill_room, (struct mkroom *,BOOLEAN_P)); +E boolean FDECL(load_special, (const char *)); + +/* ### spell.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(learn); +#endif +E int FDECL(study_book, (struct obj *)); +E void FDECL(book_disappears, (struct obj *)); +E void FDECL(book_substitution, (struct obj *,struct obj *)); +E void NDECL(age_spells); +E int NDECL(docast); +E int FDECL(spell_skilltype, (int)); +E int FDECL(spelleffects, (int,BOOLEAN_P)); +E void NDECL(losespells); +E int NDECL(dovspell); +E void FDECL(initialspell, (struct obj *)); + +/* ### steal.c ### */ + +#ifdef USE_TRAMPOLI +E int NDECL(stealarm); +#endif +#ifdef GOLDOBJ +E long FDECL(somegold, (long)); +#else +E long NDECL(somegold); +#endif +E void FDECL(stealgold, (struct monst *)); +E void FDECL(remove_worn_item, (struct obj *,BOOLEAN_P)); +E int FDECL(steal, (struct monst *, char *)); +E int FDECL(mpickobj, (struct monst *,struct obj *)); +E void FDECL(stealamulet, (struct monst *)); +E void FDECL(mdrop_special_objs, (struct monst *)); +E void FDECL(relobj, (struct monst *,int,BOOLEAN_P)); +#ifdef GOLDOBJ +E struct obj *FDECL(findgold, (struct obj *)); +#endif + +/* ### steed.c ### */ + +#ifdef STEED +E void NDECL(rider_cant_reach); +E boolean FDECL(can_saddle, (struct monst *)); +E int FDECL(use_saddle, (struct obj *)); +E boolean FDECL(can_ride, (struct monst *)); +E int NDECL(doride); +E boolean FDECL(mount_steed, (struct monst *, BOOLEAN_P)); +E void NDECL(exercise_steed); +E void NDECL(kick_steed); +E void FDECL(dismount_steed, (int)); +E void FDECL(place_monster, (struct monst *,int,int)); +#endif + +/* ### teleport.c ### */ + +E boolean FDECL(goodpos, (int,int,struct monst *,unsigned)); +E boolean FDECL(enexto, (coord *,XCHAR_P,XCHAR_P,struct permonst *)); +E boolean FDECL(enexto_core, (coord *,XCHAR_P,XCHAR_P,struct permonst *,unsigned)); +E void FDECL(teleds, (int,int,BOOLEAN_P)); +E boolean FDECL(safe_teleds, (BOOLEAN_P)); +E boolean FDECL(teleport_pet, (struct monst *,BOOLEAN_P)); +E void NDECL(tele); +E int NDECL(dotele); +E void NDECL(level_tele); +E void FDECL(domagicportal, (struct trap *)); +E void FDECL(tele_trap, (struct trap *)); +E void FDECL(level_tele_trap, (struct trap *)); +E void FDECL(rloc_to, (struct monst *,int,int)); +E boolean FDECL(rloc, (struct monst *, BOOLEAN_P)); +E boolean FDECL(tele_restrict, (struct monst *)); +E void FDECL(mtele_trap, (struct monst *, struct trap *,int)); +E int FDECL(mlevel_tele_trap, (struct monst *, struct trap *,BOOLEAN_P,int)); +E void FDECL(rloco, (struct obj *)); +E int NDECL(random_teleport_level); +E boolean FDECL(u_teleport_mon, (struct monst *,BOOLEAN_P)); + +/* ### tile.c ### */ +#ifdef USE_TILES +E void FDECL(substitute_tiles, (d_level *)); +#endif + +/* ### timeout.c ### */ + +E void NDECL(burn_away_slime); +E void NDECL(nh_timeout); +E void FDECL(fall_asleep, (int, BOOLEAN_P)); +E void FDECL(attach_egg_hatch_timeout, (struct obj *)); +E void FDECL(attach_fig_transform_timeout, (struct obj *)); +E void FDECL(kill_egg, (struct obj *)); +E void FDECL(hatch_egg, (genericptr_t, long)); +E void FDECL(learn_egg_type, (int)); +E void FDECL(burn_object, (genericptr_t, long)); +E void FDECL(begin_burn, (struct obj *, BOOLEAN_P)); +E void FDECL(end_burn, (struct obj *, BOOLEAN_P)); +E void NDECL(do_storms); +E boolean FDECL(start_timer, (long, SHORT_P, SHORT_P, genericptr_t)); +E long FDECL(stop_timer, (SHORT_P, genericptr_t)); +E void NDECL(run_timers); +E void FDECL(obj_move_timers, (struct obj *, struct obj *)); +E void FDECL(obj_split_timers, (struct obj *, struct obj *)); +E void FDECL(obj_stop_timers, (struct obj *)); +E boolean FDECL(obj_is_local, (struct obj *)); +E void FDECL(save_timers, (int,int,int)); +E void FDECL(restore_timers, (int,int,BOOLEAN_P,long)); +E void FDECL(relink_timers, (BOOLEAN_P)); +#ifdef WIZARD +E int NDECL(wiz_timeout_queue); +E void NDECL(timer_sanity_check); +#endif + +/* ### topten.c ### */ + +E void FDECL(topten, (int)); +E void FDECL(prscore, (int,char **)); +E struct obj *FDECL(tt_oname, (struct obj *)); + +/* ### track.c ### */ + +E void NDECL(initrack); +E void NDECL(settrack); +E coord *FDECL(gettrack, (int,int)); + +/* ### trap.c ### */ + +E boolean FDECL(burnarmor,(struct monst *)); +E boolean FDECL(rust_dmg, (struct obj *,const char *,int,BOOLEAN_P,struct monst *)); +E void FDECL(grease_protect, (struct obj *,const char *,struct monst *)); +E struct trap *FDECL(maketrap, (int,int,int)); +E void FDECL(fall_through, (BOOLEAN_P)); +E struct monst *FDECL(animate_statue, (struct obj *,XCHAR_P,XCHAR_P,int,int *)); +E struct monst *FDECL(activate_statue_trap, + (struct trap *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +E void FDECL(dotrap, (struct trap *, unsigned)); +E void FDECL(seetrap, (struct trap *)); +E int FDECL(mintrap, (struct monst *)); +E void FDECL(instapetrify, (const char *)); +E void FDECL(minstapetrify, (struct monst *,BOOLEAN_P)); +E void FDECL(selftouch, (const char *)); +E void FDECL(mselftouch, (struct monst *,const char *,BOOLEAN_P)); +E void NDECL(float_up); +E void FDECL(fill_pit, (int,int)); +E int FDECL(float_down, (long, long)); +E int FDECL(fire_damage, (struct obj *,BOOLEAN_P,BOOLEAN_P,XCHAR_P,XCHAR_P)); +E void FDECL(water_damage, (struct obj *,BOOLEAN_P,BOOLEAN_P)); +E boolean NDECL(drown); +E void FDECL(drain_en, (int)); +E int NDECL(dountrap); +E int FDECL(untrap, (BOOLEAN_P)); +E boolean FDECL(chest_trap, (struct obj *,int,BOOLEAN_P)); +E void FDECL(deltrap, (struct trap *)); +E boolean FDECL(delfloortrap, (struct trap *)); +E struct trap *FDECL(t_at, (int,int)); +E void FDECL(b_trapped, (const char *,int)); +E boolean NDECL(unconscious); +E boolean NDECL(lava_effects); +E void FDECL(blow_up_landmine, (struct trap *)); +E int FDECL(launch_obj,(SHORT_P,int,int,int,int,int)); + +/* ### u_init.c ### */ + +E void NDECL(u_init); + +/* ### uhitm.c ### */ + +E void FDECL(hurtmarmor,(struct monst *,int)); +E boolean FDECL(attack_checks, (struct monst *,struct obj *)); +E void FDECL(check_caitiff, (struct monst *)); +E schar FDECL(find_roll_to_hit, (struct monst *)); +E boolean FDECL(attack, (struct monst *)); +E boolean FDECL(hmon, (struct monst *,struct obj *,int)); +E int FDECL(damageum, (struct monst *,struct attack *)); +E void FDECL(missum, (struct monst *,struct attack *)); +E int FDECL(passive, (struct monst *,BOOLEAN_P,int,UCHAR_P)); +E void FDECL(passive_obj, (struct monst *,struct obj *,struct attack *)); +E void FDECL(stumble_onto_mimic, (struct monst *)); +E int FDECL(flash_hits_mon, (struct monst *,struct obj *)); + +/* ### unixmain.c ### */ + +#ifdef UNIX +# ifdef PORT_HELP +E void NDECL(port_help); +# endif +#endif /* UNIX */ + + +/* ### unixtty.c ### */ + +#if defined(UNIX) || defined(__BEOS__) +E void NDECL(gettty); +E void FDECL(settty, (const char *)); +E void NDECL(setftty); +E void NDECL(intron); +E void NDECL(introff); +E void VDECL(error, (const char *,...)) PRINTF_F(1,2); +#endif /* UNIX || __BEOS__ */ + +/* ### unixunix.c ### */ + +#ifdef UNIX +E void NDECL(getlock); +E void FDECL(regularize, (char *)); +# if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV) +E void FDECL(msleep, (unsigned)); +# endif +# ifdef SHELL +E int NDECL(dosh); +# endif /* SHELL */ +# if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER) +E int FDECL(child, (int)); +# endif +#endif /* UNIX */ + +/* ### unixres.c ### */ + +#ifdef UNIX +# ifdef GNOME_GRAPHICS +E int FDECL(hide_privileges, (BOOLEAN_P)); +# endif +#endif /* UNIX */ + +/* ### vault.c ### */ + +E boolean FDECL(grddead, (struct monst *)); +E char FDECL(vault_occupied, (char *)); +E void NDECL(invault); +E int FDECL(gd_move, (struct monst *)); +E void NDECL(paygd); +E long NDECL(hidden_gold); +E boolean NDECL(gd_sound); + +/* ### version.c ### */ + +E char *FDECL(version_string, (char *)); +E char *FDECL(getversionstring, (char *)); +E int NDECL(doversion); +E int NDECL(doextversion); +#ifdef MICRO +E boolean FDECL(comp_times, (long)); +#endif +E boolean FDECL(check_version, (struct version_info *, + const char *,BOOLEAN_P)); +E unsigned long FDECL(get_feature_notice_ver, (char *)); +E unsigned long NDECL(get_current_feature_ver); +#ifdef RUNTIME_PORT_ID +E void FDECL(append_port_id, (char *)); +#endif + +/* ### video.c ### */ + +#ifdef MSDOS +E int FDECL(assign_video, (char *)); +# ifdef NO_TERMS +E void NDECL(gr_init); +E void NDECL(gr_finish); +# endif +E void FDECL(tileview,(BOOLEAN_P)); +#endif +#ifdef VIDEOSHADES +E int FDECL(assign_videoshades, (char *)); +E int FDECL(assign_videocolors, (char *)); +#endif + +/* ### vis_tab.c ### */ + +#ifdef VISION_TABLES +E void NDECL(vis_tab_init); +#endif + +/* ### vision.c ### */ + +E void NDECL(vision_init); +E int FDECL(does_block, (int,int,struct rm*)); +E void NDECL(vision_reset); +E void FDECL(vision_recalc, (int)); +E void FDECL(block_point, (int,int)); +E void FDECL(unblock_point, (int,int)); +E boolean FDECL(clear_path, (int,int,int,int)); +E void FDECL(do_clear_area, (int,int,int, + void (*)(int,int,genericptr_t),genericptr_t)); + +#ifdef VMS + +/* ### vmsfiles.c ### */ + +E int FDECL(vms_link, (const char *,const char *)); +E int FDECL(vms_unlink, (const char *)); +E int FDECL(vms_creat, (const char *,unsigned int)); +E int FDECL(vms_open, (const char *,int,unsigned int)); +E boolean FDECL(same_dir, (const char *,const char *)); +E int FDECL(c__translate, (int)); +E char *FDECL(vms_basename, (const char *)); + +/* ### vmsmail.c ### */ + +E unsigned long NDECL(init_broadcast_trapping); +E unsigned long NDECL(enable_broadcast_trapping); +E unsigned long NDECL(disable_broadcast_trapping); +# if 0 +E struct mail_info *NDECL(parse_next_broadcast); +# endif /*0*/ + +/* ### vmsmain.c ### */ + +E int FDECL(main, (int, char **)); +# ifdef CHDIR +E void FDECL(chdirx, (const char *,BOOLEAN_P)); +# endif /* CHDIR */ + +/* ### vmsmisc.c ### */ + +E void NDECL(vms_abort); +E void FDECL(vms_exit, (int)); + +/* ### vmstty.c ### */ + +E int NDECL(vms_getchar); +E void NDECL(gettty); +E void FDECL(settty, (const char *)); +E void FDECL(shuttty, (const char *)); +E void NDECL(setftty); +E void NDECL(intron); +E void NDECL(introff); +E void VDECL(error, (const char *,...)) PRINTF_F(1,2); +#ifdef TIMED_DELAY +E void FDECL(msleep, (unsigned)); +#endif + +/* ### vmsunix.c ### */ + +E void NDECL(getlock); +E void FDECL(regularize, (char *)); +E int NDECL(vms_getuid); +E boolean FDECL(file_is_stmlf, (int)); +E int FDECL(vms_define, (const char *,const char *,int)); +E int FDECL(vms_putenv, (const char *)); +E char *NDECL(verify_termcap); +# if defined(CHDIR) || defined(SHELL) || defined(SECURE) +E void NDECL(privoff); +E void NDECL(privon); +# endif +# ifdef SHELL +E int NDECL(dosh); +# endif +# if defined(SHELL) || defined(MAIL) +E int FDECL(vms_doshell, (const char *,BOOLEAN_P)); +# endif +# ifdef SUSPEND +E int NDECL(dosuspend); +# endif + +#endif /* VMS */ + +/* ### weapon.c ### */ + +E int FDECL(hitval, (struct obj *,struct monst *)); +E int FDECL(dmgval, (struct obj *,struct monst *)); +E struct obj *FDECL(select_rwep, (struct monst *)); +E struct obj *FDECL(select_hwep, (struct monst *)); +E void FDECL(possibly_unwield, (struct monst *,BOOLEAN_P)); +E int FDECL(mon_wield_item, (struct monst *)); +E int NDECL(abon); +E int NDECL(dbon); +E int NDECL(enhance_weapon_skill); +E void FDECL(unrestrict_weapon_skill, (int)); +E void FDECL(use_skill, (int,int)); +E void FDECL(add_weapon_skill, (int)); +E void FDECL(lose_weapon_skill, (int)); +E int FDECL(weapon_type, (struct obj *)); +E int NDECL(uwep_skill_type); +E int FDECL(weapon_hit_bonus, (struct obj *)); +E int FDECL(weapon_dam_bonus, (struct obj *)); +E void FDECL(skill_init, (const struct def_skill *)); + +/* ### were.c ### */ + +E void FDECL(were_change, (struct monst *)); +E void FDECL(new_were, (struct monst *)); +E int FDECL(were_summon, (struct permonst *,BOOLEAN_P,int *,char *)); +E void NDECL(you_were); +E void FDECL(you_unwere, (BOOLEAN_P)); + +/* ### wield.c ### */ + +E void FDECL(setuwep, (struct obj *)); +E void FDECL(setuqwep, (struct obj *)); +E void FDECL(setuswapwep, (struct obj *)); +E int NDECL(dowield); +E int NDECL(doswapweapon); +E int NDECL(dowieldquiver); +E boolean FDECL(wield_tool, (struct obj *,const char *)); +E int NDECL(can_twoweapon); +E void NDECL(drop_uswapwep); +E int NDECL(dotwoweapon); +E void NDECL(uwepgone); +E void NDECL(uswapwepgone); +E void NDECL(uqwepgone); +E void NDECL(untwoweapon); +E void FDECL(erode_obj, (struct obj *,BOOLEAN_P,BOOLEAN_P)); +E int FDECL(chwepon, (struct obj *,int)); +E int FDECL(welded, (struct obj *)); +E void FDECL(weldmsg, (struct obj *)); +E void FDECL(setmnotwielded, (struct monst *,struct obj *)); + +/* ### windows.c ### */ + +E void FDECL(choose_windows, (const char *)); +E char FDECL(genl_message_menu, (CHAR_P,int,const char *)); +E void FDECL(genl_preference_update, (const char *)); + +/* ### wizard.c ### */ + +E void NDECL(amulet); +E int FDECL(mon_has_amulet, (struct monst *)); +E int FDECL(mon_has_special, (struct monst *)); +E int FDECL(tactics, (struct monst *)); +E void NDECL(aggravate); +E void NDECL(clonewiz); +E int NDECL(pick_nasty); +E int FDECL(nasty, (struct monst*)); +E void NDECL(resurrect); +E void NDECL(intervene); +E void NDECL(wizdead); +E void FDECL(cuss, (struct monst *)); + +/* ### worm.c ### */ + +E int NDECL(get_wormno); +E void FDECL(initworm, (struct monst *,int)); +E void FDECL(worm_move, (struct monst *)); +E void FDECL(worm_nomove, (struct monst *)); +E void FDECL(wormgone, (struct monst *)); +E void FDECL(wormhitu, (struct monst *)); +E void FDECL(cutworm, (struct monst *,XCHAR_P,XCHAR_P,struct obj *)); +E void FDECL(see_wsegs, (struct monst *)); +E void FDECL(detect_wsegs, (struct monst *,BOOLEAN_P)); +E void FDECL(save_worm, (int,int)); +E void FDECL(rest_worm, (int)); +E void FDECL(place_wsegs, (struct monst *)); +E void FDECL(remove_worm, (struct monst *)); +E void FDECL(place_worm_tail_randomly, (struct monst *,XCHAR_P,XCHAR_P)); +E int FDECL(count_wsegs, (struct monst *)); +E boolean FDECL(worm_known, (struct monst *)); + +/* ### worn.c ### */ + +E void FDECL(setworn, (struct obj *,long)); +E void FDECL(setnotworn, (struct obj *)); +E void FDECL(mon_set_minvis, (struct monst *)); +E void FDECL(mon_adjust_speed, (struct monst *,int,struct obj *)); +E void FDECL(update_mon_intrinsics, + (struct monst *,struct obj *,BOOLEAN_P,BOOLEAN_P)); +E int FDECL(find_mac, (struct monst *)); +E void FDECL(m_dowear, (struct monst *,BOOLEAN_P)); +E struct obj *FDECL(which_armor, (struct monst *,long)); +E void FDECL(mon_break_armor, (struct monst *,BOOLEAN_P)); +E void FDECL(bypass_obj, (struct obj *)); +E void NDECL(clear_bypasses); +E int FDECL(racial_exception, (struct monst *, struct obj *)); + +/* ### write.c ### */ + +E int FDECL(dowrite, (struct obj *)); + +/* ### zap.c ### */ + +E int FDECL(bhitm, (struct monst *,struct obj *)); +E void FDECL(probe_monster, (struct monst *)); +E boolean FDECL(get_obj_location, (struct obj *,xchar *,xchar *,int)); +E boolean FDECL(get_mon_location, (struct monst *,xchar *,xchar *,int)); +E struct monst *FDECL(get_container_location, (struct obj *obj, int *, int *)); +E struct monst *FDECL(montraits, (struct obj *,coord *)); +E struct monst *FDECL(revive, (struct obj *)); +E int FDECL(unturn_dead, (struct monst *)); +E void FDECL(cancel_item, (struct obj *)); +E boolean FDECL(drain_item, (struct obj *)); +E struct obj *FDECL(poly_obj, (struct obj *, int)); +E boolean FDECL(obj_resists, (struct obj *,int,int)); +E boolean FDECL(obj_shudders, (struct obj *)); +E void FDECL(do_osshock, (struct obj *)); +E int FDECL(bhito, (struct obj *,struct obj *)); +E int FDECL(bhitpile, (struct obj *,int (*)(OBJ_P,OBJ_P),int,int)); +E int FDECL(zappable, (struct obj *)); +E void FDECL(zapnodir, (struct obj *)); +E int NDECL(dozap); +E int FDECL(zapyourself, (struct obj *,BOOLEAN_P)); +E boolean FDECL(cancel_monst, (struct monst *,struct obj *, + BOOLEAN_P,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(weffects, (struct obj *)); +E int NDECL(spell_damage_bonus); +E const char *FDECL(exclam, (int force)); +E void FDECL(hit, (const char *,struct monst *,const char *)); +E void FDECL(miss, (const char *,struct monst *)); +E struct monst *FDECL(bhit, (int,int,int,int,int (*)(MONST_P,OBJ_P), + int (*)(OBJ_P,OBJ_P),struct obj *)); +E struct monst *FDECL(boomhit, (int,int)); +E int FDECL(burn_floor_paper, (int,int,BOOLEAN_P,BOOLEAN_P)); +E void FDECL(buzz, (int,int,XCHAR_P,XCHAR_P,int,int)); +E void FDECL(melt_ice, (XCHAR_P,XCHAR_P)); +E int FDECL(zap_over_floor, (XCHAR_P,XCHAR_P,int,boolean *)); +E void FDECL(fracture_rock, (struct obj *)); +E boolean FDECL(break_statue, (struct obj *)); +E void FDECL(destroy_item, (int,int)); +E int FDECL(destroy_mitem, (struct monst *,int,int)); +E int FDECL(resist, (struct monst *,CHAR_P,int,int)); +E void NDECL(makewish); + +#endif /* !MAKEDEFS_C && !LEV_LEX_C */ + +#undef E + +#endif /* EXTERN_H */ diff --git a/include/flag.h b/include/flag.h new file mode 100644 index 0000000..1807a0b --- /dev/null +++ b/include/flag.h @@ -0,0 +1,313 @@ +/* SCCS Id: @(#)flag.h 3.4 2002/08/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* If you change the flag structure make sure you increment EDITLEVEL in */ +/* patchlevel.h if needed. Changing the instance_flags structure does */ +/* not require incrementing EDITLEVEL. */ + +#ifndef FLAG_H +#define FLAG_H + +/* + * Persistent flags that are saved and restored with the game. + * + */ + +struct flag { +#ifdef AMIFLUSH + boolean altmeta; /* use ALT keys as META */ + boolean amiflush; /* kill typeahead */ +#endif +#ifdef MFLOPPY + boolean asksavedisk; +#endif + boolean autodig; /* MRKR: Automatically dig */ + boolean autoquiver; /* Automatically fill quiver */ + boolean beginner; +#ifdef MAIL + boolean biff; /* enable checking for mail */ +#endif + boolean botl; /* partially redo status line */ + boolean botlx; /* print an entirely new bottom line */ + boolean confirm; /* confirm before hitting tame monsters */ + boolean debug; /* in debugging mode */ +#define wizard flags.debug + boolean end_own; /* list all own scores */ + boolean explore; /* in exploration mode */ +#ifdef OPT_DISPMAP + boolean fast_map; /* use optimized, less flexible map display */ +#endif +#define discover flags.explore + boolean female; + boolean forcefight; + boolean friday13; /* it's Friday the 13th */ + boolean help; /* look in data file for info about stuff */ + boolean ignintr; /* ignore interrupts */ +#ifdef INSURANCE + boolean ins_chkpt; /* checkpoint as appropriate */ +#endif + boolean invlet_constant; /* let objects keep their inventory symbol */ + boolean legacy; /* print game entry "story" */ + boolean lit_corridor; /* show a dark corr as lit if it is in sight */ + boolean made_amulet; + boolean mon_moving; /* monsters' turn to move */ + boolean move; + boolean mv; + boolean bypasses; /* bypass flag is set on at least one fobj */ + boolean nap; /* `timed_delay' option for display effects */ + boolean nopick; /* do not pickup objects (as when running) */ + boolean null; /* OK to send nulls to the terminal */ +#ifdef MAC + boolean page_wait; /* put up a --More-- after a page of messages */ +#endif + boolean perm_invent; /* keep full inventories up until dismissed */ + boolean pickup; /* whether you pickup or move and look */ + + boolean pushweapon; /* When wielding, push old weapon into second slot */ + boolean rest_on_space; /* space means rest */ + boolean safe_dog; /* give complete protection to the dog */ +#ifdef EXP_ON_BOTL + boolean showexp; /* show experience points */ +#endif +#ifdef SCORE_ON_BOTL + boolean showscore; /* show score */ +#endif + boolean silent; /* whether the bell rings or not */ + boolean sortpack; /* sorted inventory */ + boolean soundok; /* ok to tell about sounds heard */ + boolean sparkle; /* show "resisting" special FX (Scott Bigham) */ + boolean standout; /* use standout for --More-- */ + boolean time; /* display elapsed 'time' */ + boolean tombstone; /* print tombstone */ + boolean toptenwin; /* ending list in window instead of stdout */ + boolean verbose; /* max battle info */ + boolean prayconfirm; /* confirm before praying */ + int end_top, end_around; /* describe desired score list */ + unsigned ident; /* social security number for each monster */ + unsigned moonphase; + unsigned long suppress_alert; +#define NEW_MOON 0 +#define FULL_MOON 4 + unsigned no_of_wizards; /* 0, 1 or 2 (wizard and his shadow) */ + boolean travel; /* find way automatically to u.tx,u.ty */ + unsigned run; /* 0: h (etc), 1: H (etc), 2: fh (etc) */ + /* 3: FH, 4: ff+, 5: ff-, 6: FF+, 7: FF- */ + /* 8: travel */ + unsigned long warntype; /* warn_of_mon monster type M2 */ + int warnlevel; + int djinni_count, ghost_count; /* potion effect tuning */ + int pickup_burden; /* maximum burden before prompt */ + char inv_order[MAXOCLASSES]; + char pickup_types[MAXOCLASSES]; +#define NUM_DISCLOSURE_OPTIONS 5 +#define DISCLOSE_PROMPT_DEFAULT_YES 'y' +#define DISCLOSE_PROMPT_DEFAULT_NO 'n' +#define DISCLOSE_YES_WITHOUT_PROMPT '+' +#define DISCLOSE_NO_WITHOUT_PROMPT '-' + char end_disclose[NUM_DISCLOSURE_OPTIONS + 1]; /* disclose various info + upon exit */ + char menu_style; /* User interface style setting */ +#ifdef AMII_GRAPHICS + int numcols; + unsigned short amii_dripens[ 20 ]; /* DrawInfo Pens currently there are 13 in v39 */ + AMII_COLOR_TYPE amii_curmap[ AMII_MAXCOLORS ]; /* colormap */ +#endif + + /* KMH, role patch -- Variables used during startup. + * + * If the user wishes to select a role, race, gender, and/or alignment + * during startup, the choices should be recorded here. This + * might be specified through command-line options, environmental + * variables, a popup dialog box, menus, etc. + * + * These values are each an index into an array. They are not + * characters or letters, because that limits us to 26 roles. + * They are not booleans, because someday someone may need a neuter + * gender. Negative values are used to indicate that the user + * hasn't yet specified that particular value. If you determine + * that the user wants a random choice, then you should set an + * appropriate random value; if you just left the negative value, + * the user would be asked again! + * + * These variables are stored here because the u structure is + * cleared during character initialization, and because the + * flags structure is restored for saved games. Thus, we can + * use the same parameters to build the role entry for both + * new and restored games. + * + * These variables should not be referred to after the character + * is initialized or restored (specifically, after role_init() + * is called). + */ + int initrole; /* starting role (index into roles[]) */ + int initrace; /* starting race (index into races[]) */ + int initgend; /* starting gender (index into genders[]) */ + int initalign; /* starting alignment (index into aligns[]) */ + int randomall; /* randomly assign everything not specified */ + int pantheon; /* deity selection for priest character */ +}; + +/* + * Flags that are set each time the game is started. + * These are not saved with the game. + * + */ + +struct instance_flags { + boolean cbreak; /* in cbreak mode, rogue format */ + boolean DECgraphics; /* use DEC VT-xxx extended character set */ + boolean echo; /* 1 to echo characters */ + boolean IBMgraphics; /* use IBM extended character set */ + unsigned msg_history; /* hint: # of top lines to save */ + boolean num_pad; /* use numbers for movement commands */ + boolean news; /* print news */ + boolean window_inited; /* true if init_nhwindows() completed */ + boolean vision_inited; /* true if vision is ready */ + boolean menu_tab_sep; /* Use tabs to separate option menu fields */ + boolean menu_requested; /* Flag for overloaded use of 'm' prefix + * on some non-move commands */ + uchar num_pad_mode; + int menu_headings; /* ATR for menu headings */ + int purge_monsters; /* # of dead monsters still on fmon list */ + int *opt_booldup; /* for duplication of boolean opts in config file */ + int *opt_compdup; /* for duplication of compound opts in config file */ + uchar bouldersym; /* symbol for boulder display */ + boolean travel1; /* first travel step */ + coord travelcc; /* coordinates for travel_cache */ +#ifdef WIZARD + boolean sanity_check; /* run sanity checks */ + boolean mon_polycontrol; /* debug: control monster polymorphs */ +#endif +#ifdef TTY_GRAPHICS + char prevmsg_window; /* type of old message window to use */ + boolean extmenu; /* extended commands use menu interface */ +#endif +#ifdef MFLOPPY + boolean checkspace; /* check disk space before writing files */ + /* (in iflags to allow restore after moving + * to >2GB partition) */ +#endif +#ifdef MICRO + boolean BIOS; /* use IBM or ST BIOS calls when appropriate */ +#endif +#if defined(MICRO) || defined(WIN32) + boolean rawio; /* whether can use rawio (IOCTL call) */ +#endif +#ifdef MAC_GRAPHICS_ENV + boolean MACgraphics; /* use Macintosh extended character set, as + as defined in the special font HackFont */ + unsigned use_stone; /* use the stone ppats */ +#endif +#if defined(MSDOS) || defined(WIN32) + boolean hassound; /* has a sound card */ + boolean usesound; /* use the sound card */ + boolean usepcspeaker; /* use the pc speaker */ + boolean tile_view; + boolean over_view; + boolean traditional_view; +#endif +#ifdef MSDOS + boolean hasvga; /* has a vga adapter */ + boolean usevga; /* use the vga adapter */ + boolean grmode; /* currently in graphics mode */ +#endif +#ifdef LAN_FEATURES + boolean lan_mail; /* mail is initialized */ + boolean lan_mail_fetched; /* mail is awaiting display */ +#endif +/* + * Window capability support. + */ + boolean wc_color; /* use color graphics */ + boolean wc_hilite_pet; /* hilight pets */ + boolean wc_ascii_map; /* show map using traditional ascii */ + boolean wc_tiled_map; /* show map using tiles */ + boolean wc_preload_tiles; /* preload tiles into memory */ + int wc_tile_width; /* tile width */ + int wc_tile_height; /* tile height */ + char *wc_tile_file; /* name of tile file;overrides default */ + boolean wc_inverse; /* use inverse video for some things */ + int wc_align_status; /* status win at top|bot|right|left */ + int wc_align_message; /* message win at top|bot|right|left */ + int wc_vary_msgcount; /* show more old messages at a time */ + char *wc_foregrnd_menu; /* points to foregrnd color name for menu win */ + char *wc_backgrnd_menu; /* points to backgrnd color name for menu win */ + char *wc_foregrnd_message; /* points to foregrnd color name for msg win */ + char *wc_backgrnd_message; /* points to backgrnd color name for msg win */ + char *wc_foregrnd_status; /* points to foregrnd color name for status win */ + char *wc_backgrnd_status; /* points to backgrnd color name for status win */ + char *wc_foregrnd_text; /* points to foregrnd color name for text win */ + char *wc_backgrnd_text; /* points to backgrnd color name for text win */ + char *wc_font_map; /* points to font name for the map win */ + char *wc_font_message; /* points to font name for message win */ + char *wc_font_status; /* points to font name for status win */ + char *wc_font_menu; /* points to font name for menu win */ + char *wc_font_text; /* points to font name for text win */ + int wc_fontsiz_map; /* font size for the map win */ + int wc_fontsiz_message; /* font size for the message window */ + int wc_fontsiz_status; /* font size for the status window */ + int wc_fontsiz_menu; /* font size for the menu window */ + int wc_fontsiz_text; /* font size for text windows */ + int wc_scroll_amount; /* scroll this amount at scroll_margin */ + int wc_scroll_margin; /* scroll map when this far from + the edge */ + int wc_map_mode; /* specify map viewing options, mostly + for backward compatibility */ + int wc_player_selection; /* method of choosing character */ + boolean wc_splash_screen; /* display an opening splash screen or not */ + boolean wc_popup_dialog; /* put queries in pop up dialogs instead of + in the message window */ + boolean wc_eight_bit_input; /* allow eight bit input */ + boolean wc_mouse_support; /* allow mouse support */ + boolean wc2_fullscreen; /* run fullscreen */ + boolean wc2_softkeyboard; /* use software keyboard */ + boolean wc2_wraptext; /* wrap text */ + + boolean cmdassist; /* provide detailed assistance for some commands */ + boolean obsolete; /* obsolete options can point at this, it isn't used */ + /* Items which belong in flags, but are here to allow save compatibility */ + boolean lootabc; /* use "a/b/c" rather than "o/i/b" when looting */ + boolean showrace; /* show hero glyph by race rather than by role */ + boolean travelcmd; /* allow travel command */ + int runmode; /* update screen display during run moves */ +#ifdef AUTOPICKUP_EXCEPTIONS + struct autopickup_exception *autopickup_exceptions[2]; +#define AP_LEAVE 0 +#define AP_GRAB 1 +#endif +#ifdef WIN32CON +#define MAX_ALTKEYHANDLER 25 + char altkeyhandler[MAX_ALTKEYHANDLER]; +#endif +}; + +/* + * Old deprecated names + */ +#ifdef TTY_GRAPHICS +#define eight_bit_tty wc_eight_bit_input +#endif +#ifdef TEXTCOLOR +#define use_color wc_color +#endif +#define hilite_pet wc_hilite_pet +#define use_inverse wc_inverse +#ifdef MAC_GRAPHICS_ENV +#define large_font obsolete +#endif +#ifdef MAC +#define popup_dialog wc_popup_dialog +#endif +#define preload_tiles wc_preload_tiles + +extern NEARDATA struct flag flags; +extern NEARDATA struct instance_flags iflags; + +/* runmode options */ +#define RUN_TPORT 0 /* don't update display until movement stops */ +#define RUN_LEAP 1 /* update display every 7 steps */ +#define RUN_STEP 2 /* update display every single step */ +#define RUN_CRAWL 3 /* walk w/ extra delay after each update */ + +#endif /* FLAG_H */ diff --git a/include/func_tab.h b/include/func_tab.h new file mode 100644 index 0000000..769801d --- /dev/null +++ b/include/func_tab.h @@ -0,0 +1,23 @@ +/* SCCS Id: @(#)func_tab.h 3.4 1992/04/03 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef FUNC_TAB_H +#define FUNC_TAB_H + +struct func_tab { + char f_char; + boolean can_if_buried; + int NDECL((*f_funct)); + const char *f_text; +}; + +struct ext_func_tab { + const char *ef_txt, *ef_desc; + int NDECL((*ef_funct)); + boolean can_if_buried; +}; + +extern struct ext_func_tab extcmdlist[]; + +#endif /* FUNC_TAB_H */ diff --git a/include/gem_rsc.h b/include/gem_rsc.h new file mode 100644 index 0000000..c499d00 --- /dev/null +++ b/include/gem_rsc.h @@ -0,0 +1,65 @@ +/* resource set indices for GEM_RSC */ + +#define MENU 0 /* menu */ +#define DOABOUT 12 /* STRING in tree MENU */ +#define DOQUIT 30 /* STRING in tree MENU */ + +#define STATUSLINE 1 /* form/dialog */ +#define GRABSTATUS 1 /* BOX in tree STATUSLINE */ + +#define MAPWIN 2 /* form/dialog */ +#define MAPBOX 0 /* BOX in tree MAPWIN */ +#define MAPCURSOR 1 /* IBOX in tree MAPWIN */ + +#define ABOUT 3 /* form/dialog */ +#define FLYABOUT 0 /* BOX in tree ABOUT */ +#define OKABOUT 1 /* BUTTON in tree ABOUT */ +#define NETHACKIMG0 3 /* ICON in tree ABOUT */ + +#define LINES 4 /* form/dialog */ +#define FLYLINES 0 /* BOX in tree LINES */ +#define QLINE 1 /* BUTTON in tree LINES */ +#define LINESLIST 2 /* USERDEF in tree LINES */ + +#define YNCHOICE 5 /* form/dialog */ +#define FLYYNCHOICE 0 /* BOX in tree YNCHOICE */ +#define YNPROMPT 1 /* TEXT in tree YNCHOICE */ +#define SOMECHARS 2 /* BOX in tree YNCHOICE */ +#define YN1 3 /* BUTTON in tree YNCHOICE */ +#define YNN 53 /* BUTTON in tree YNCHOICE */ +#define ANYCHAR 55 /* BOX in tree YNCHOICE */ +#define CHOSENCH 56 /* FBOXTEXT in tree YNCHOICE */ +#define COUNT 58 /* FBOXTEXT in tree YNCHOICE */ +#define YNOK 59 /* BUTTON in tree YNCHOICE */ + +#define LINEGET 6 /* form/dialog */ +#define FLYLINEGET 0 /* BOX in tree LINEGET */ +#define LGPROMPT 1 /* TEXT in tree LINEGET */ +#define LGREPLY 2 /* FBOXTEXT in tree LINEGET */ +#define QLG 3 /* BUTTON in tree LINEGET */ +#define LGOK 4 /* BUTTON in tree LINEGET */ + +#define DIRECTION 7 /* form/dialog */ +#define FLYDIRECTION 0 /* BOX in tree DIRECTION */ +#define DIR1 5 /* BOXTEXT in tree DIRECTION */ +#define DIR9 21 /* BOXTEXT in tree DIRECTION */ +#define DIRDOWN 23 /* BOXTEXT in tree DIRECTION */ +#define DIRUP 25 /* BOXTEXT in tree DIRECTION */ + +#define MSGWIN 8 /* form/dialog */ +#define UPMSG 1 /* BOXCHAR in tree MSGWIN */ +#define GRABMSGWIN 2 /* BOX in tree MSGWIN */ +#define DNMSG 3 /* BOXCHAR in tree MSGWIN */ +#define MSGLINES 4 /* USERDEF in tree MSGWIN */ + +#define NAMEGET 9 /* form/dialog */ +#define FLYNAMEGET 0 /* BOX in tree NAMEGET */ +#define PLNAME 2 /* FBOXTEXT in tree NAMEGET */ +#define NETHACKPICTURE 4 /* BOXTEXT in tree NAMEGET */ + +#define PAGER 10 /* form/dialog */ +#define FLYPAGER 0 /* BOX in tree PAGER */ +#define QPAGER 1 /* BUTTON in tree PAGER */ + +#define NHICON 11 /* form/dialog */ + diff --git a/include/global.h b/include/global.h new file mode 100644 index 0000000..8311246 --- /dev/null +++ b/include/global.h @@ -0,0 +1,345 @@ +/* SCCS Id: @(#)global.h 3.4 2003/08/31 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GLOBAL_H +#define GLOBAL_H + +#include + + +/* #define BETA */ /* if a beta-test copy [MRS] */ + +/* + * Files expected to exist in the playground directory. + */ + +#define RECORD "record" /* file containing list of topscorers */ +#define HELP "help" /* file containing command descriptions */ +#define SHELP "hh" /* abbreviated form of the same */ +#define DEBUGHELP "wizhelp" /* file containing debug mode cmds */ +#define RUMORFILE "rumors" /* file with fortune cookies */ +#define ORACLEFILE "oracles" /* file with oracular information */ +#define DATAFILE "data" /* file giving the meaning of symbols used */ +#define CMDHELPFILE "cmdhelp" /* file telling what commands do */ +#define HISTORY "history" /* file giving nethack's history */ +#define LICENSE "license" /* file with license information */ +#define OPTIONFILE "opthelp" /* file explaining runtime options */ +#define OPTIONS_USED "options" /* compile-time options, for #version */ + +#define LEV_EXT ".lev" /* extension for special level files */ + + +/* Assorted definitions that may depend on selections in config.h. */ + +/* + * for DUMB preprocessor and compiler, e.g., cpp and pcc supplied + * with Microport SysV/AT, which have small symbol tables; + * DUMB if needed is defined in CFLAGS + */ +#ifdef DUMB +#ifdef BITFIELDS +#undef BITFIELDS +#endif +#ifndef STUPID +#define STUPID +#endif +#endif /* DUMB */ + +/* + * type xchar: small integers in the range 0 - 127, usually coordinates + * although they are nonnegative they must not be declared unsigned + * since otherwise comparisons with signed quantities are done incorrectly + */ +typedef schar xchar; +#ifndef SKIP_BOOLEAN +typedef xchar boolean; /* 0 or 1 */ +#endif + +#ifndef TRUE /* defined in some systems' native include files */ +#define TRUE ((boolean)1) +#define FALSE ((boolean)0) +#endif + +#ifndef STRNCMPI +# ifndef __SASC_60 /* SAS/C already shifts to stricmp */ +# define strcmpi(a,b) strncmpi((a),(b),-1) +# endif +#endif + +/* comment out to test effects of each #define -- these will probably + * disappear eventually + */ +#ifdef INTERNAL_COMP +# define RLECOMP /* run-length compression of levl array - JLee */ +# define ZEROCOMP /* zero-run compression of everything - Olaf Seibert */ +#endif + +/* #define SPECIALIZATION */ /* do "specialized" version of new topology */ + + +#ifdef BITFIELDS +#define Bitfield(x,n) unsigned x:n +#else +#define Bitfield(x,n) uchar x +#endif + +#ifdef UNWIDENED_PROTOTYPES +# define CHAR_P char +# define SCHAR_P schar +# define UCHAR_P uchar +# define XCHAR_P xchar +# define SHORT_P short +#ifndef SKIP_BOOLEAN +# define BOOLEAN_P boolean +#endif +# define ALIGNTYP_P aligntyp +#else +# ifdef WIDENED_PROTOTYPES +# define CHAR_P int +# define SCHAR_P int +# define UCHAR_P int +# define XCHAR_P int +# define SHORT_P int +# define BOOLEAN_P int +# define ALIGNTYP_P int +# endif +#endif +#if defined(ULTRIX_PROTO) && !defined(__STDC__) +/* The ultrix 2.0 and 2.1 compilers (on Ultrix 4.0 and 4.2 respectively) can't + * handle "struct obj *" constructs in prototypes. Their bugs are different, + * but both seem to work if we put "void*" in the prototype instead. This + * gives us minimal prototype checking but avoids the compiler bugs. + * + * OBJ_P and MONST_P should _only_ be used for declaring function pointers. + */ +#define OBJ_P void* +#define MONST_P void* +#else +#define OBJ_P struct obj* +#define MONST_P struct monst* +#endif + +#define SIZE(x) (int)(sizeof(x) / sizeof(x[0])) + + +/* A limit for some NetHack int variables. It need not, and for comparable + * scoring should not, depend on the actual limit on integers for a + * particular machine, although it is set to the minimum required maximum + * signed integer for C (2^15 -1). + */ +#define LARGEST_INT 32767 + + +#ifdef REDO +#define Getchar pgetchar +#endif + + +#include "coord.h" +/* + * Automatic inclusions for the subsidiary files. + * Please don't change the order. It does matter. + */ + +#ifdef VMS +#include "vmsconf.h" +#endif + +#ifdef UNIX +#include "unixconf.h" +#endif + +#ifdef OS2 +#include "os2conf.h" +#endif + +#ifdef MSDOS +#include "pcconf.h" +#endif + +#ifdef TOS +#include "tosconf.h" +#endif + +#ifdef AMIGA +#include "amiconf.h" +#endif + +#ifdef MAC +#include "macconf.h" +#endif + +#ifdef __BEOS__ +#include "beconf.h" +#endif + +#ifdef WIN32 +#ifdef WIN_CE +#include "wceconf.h" +#else +#include "ntconf.h" +#endif +#endif + +/* Displayable name of this port; don't redefine if defined in *conf.h */ +#ifndef PORT_ID +# ifdef AMIGA +# define PORT_ID "Amiga" +# endif +# ifdef MAC +# define PORT_ID "Mac" +# endif +# ifdef MSDOS +# ifdef PC9800 +# define PORT_ID "PC-9800" +# else +# define PORT_ID "PC" +# endif +# ifdef DJGPP +# define PORT_SUB_ID "djgpp" +# else +# ifdef OVERLAY +# define PORT_SUB_ID "overlaid" +# else +# define PORT_SUB_ID "non-overlaid" +# endif +# endif +# endif +# ifdef OS2 +# define PORT_ID "OS/2" +# endif +# ifdef TOS +# define PORT_ID "ST" +# endif +# ifdef UNIX +# define PORT_ID "Unix" +# endif +# ifdef VMS +# define PORT_ID "VMS" +# endif +# ifdef WIN32 +# define PORT_ID "Windows" +# ifndef PORT_SUB_ID +# ifdef MSWIN_GRAPHICS +# define PORT_SUB_ID "graphical" +# else +# define PORT_SUB_ID "tty" +# endif +# endif +# endif +#endif + +#if defined(MICRO) +#if !defined(AMIGA) && !defined(TOS) && !defined(OS2_HPFS) +#define SHORT_FILENAMES /* filenames are 8.3 */ +#endif +#endif + +#ifdef VMS +/* vms_exit() (sys/vms/vmsmisc.c) expects the non-VMS EXIT_xxx values below. + * these definitions allow all systems to be treated uniformly, provided + * main() routines do not terminate with return(), whose value is not + * so massaged. + */ +# ifdef EXIT_SUCCESS +# undef EXIT_SUCCESS +# endif +# ifdef EXIT_FAILURE +# undef EXIT_FAILURE +# endif +#endif + +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +#endif + +#if defined(X11_GRAPHICS) || defined(QT_GRAPHICS) || defined(GNOME_GRAPHICS) || defined(MSWIN_GRAPHICS) +# ifndef USE_TILES +# define USE_TILES /* glyph2tile[] will be available */ +# endif +#endif +#if defined(AMII_GRAPHICS) || defined(GEM_GRAPHICS) +# ifndef USE_TILES +# define USE_TILES +# endif +#endif + + +#define Sprintf (void) sprintf +#define Strcat (void) strcat +#define Strcpy (void) strcpy +#ifdef NEED_VARARGS +#define Vprintf (void) vprintf +#define Vfprintf (void) vfprintf +#define Vsprintf (void) vsprintf +#endif + + +/* primitive memory leak debugging; see alloc.c */ +#ifdef MONITOR_HEAP +extern long *FDECL(nhalloc, (unsigned int,const char *,int)); +extern void FDECL(nhfree, (genericptr_t,const char *,int)); +# ifndef __FILE__ +# define __FILE__ "" +# endif +# ifndef __LINE__ +# define __LINE__ 0 +# endif +# define alloc(a) nhalloc(a,__FILE__,(int)__LINE__) +# define free(a) nhfree(a,__FILE__,(int)__LINE__) +#else /* !MONITOR_HEAP */ +extern long *FDECL(alloc, (unsigned int)); /* alloc.c */ +#endif + +/* Used for consistency checks of various data files; declare it here so + that utility programs which include config.h but not hack.h can see it. */ +struct version_info { + unsigned long incarnation; /* actual version number */ + unsigned long feature_set; /* bitmask of config settings */ + unsigned long entity_count; /* # of monsters and objects */ + unsigned long struct_sizes; /* size of key structs */ +}; + + +/* + * Configurable internal parameters. + * + * Please be very careful if you are going to change one of these. Any + * changes in these parameters, unless properly done, can render the + * executable inoperative. + */ + +/* size of terminal screen is (at least) (ROWNO+3) by COLNO */ +#define COLNO 80 +#define ROWNO 21 + +#define MAXNROFROOMS 40 /* max number of rooms per level */ +#define MAX_SUBROOMS 24 /* max # of subrooms in a given room */ +#define DOORMAX 120 /* max number of doors per level */ + +#define BUFSZ 256 /* for getlin buffers */ +#define QBUFSZ 128 /* for building question text */ +#define TBUFSZ 300 /* toplines[] buffer max msg: 3 81char names */ + /* plus longest prefix plus a few extra words */ + +#define PL_NSIZ 32 /* name of player, ghost, shopkeeper */ +#define PL_CSIZ 32 /* sizeof pl_character */ +#define PL_FSIZ 32 /* fruit name */ +#define PL_PSIZ 63 /* player-given names for pets, other + * monsters, objects */ + +#define MAXDUNGEON 16 /* current maximum number of dungeons */ +#define MAXLEVEL 32 /* max number of levels in one dungeon */ +#define MAXSTAIRS 1 /* max # of special stairways in a dungeon */ +#define ALIGNWEIGHT 4 /* generation weight of alignment */ + +#define MAXULEV 30 /* max character experience level */ + +#define MAXMONNO 120 /* extinct monst after this number created */ +#define MHPMAX 500 /* maximum monster hp */ + +#endif /* GLOBAL_H */ diff --git a/include/hack.h b/include/hack.h new file mode 100644 index 0000000..b7b5835 --- /dev/null +++ b/include/hack.h @@ -0,0 +1,341 @@ +/* SCCS Id: @(#)hack.h 3.4 2001/04/12 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef HACK_H +#define HACK_H + +#ifndef CONFIG_H +#include "config.h" +#endif + +/* For debugging beta code. */ +#ifdef BETA +#define Dpline pline +#endif + +#define TELL 1 +#define NOTELL 0 +#define ON 1 +#define OFF 0 +#define BOLT_LIM 8 /* from this distance ranged attacks will be made */ +#define MAX_CARR_CAP 1000 /* so that boulders can be heavier */ +#define DUMMY { 0 } + +/* symbolic names for capacity levels */ +#define UNENCUMBERED 0 +#define SLT_ENCUMBER 1 /* Burdened */ +#define MOD_ENCUMBER 2 /* Stressed */ +#define HVY_ENCUMBER 3 /* Strained */ +#define EXT_ENCUMBER 4 /* Overtaxed */ +#define OVERLOADED 5 /* Overloaded */ + +/* Macros for how a rumor was delivered in outrumor() */ +#define BY_ORACLE 0 +#define BY_COOKIE 1 +#define BY_PAPER 2 +#define BY_OTHER 9 + +#ifdef STEED +/* Macros for why you are no longer riding */ +#define DISMOUNT_GENERIC 0 +#define DISMOUNT_FELL 1 +#define DISMOUNT_THROWN 2 +#define DISMOUNT_POLY 3 +#define DISMOUNT_ENGULFED 4 +#define DISMOUNT_BONES 5 +#define DISMOUNT_BYCHOICE 6 +#endif + +/* Special returns from mapglyph() */ +#define MG_CORPSE 0x01 +#define MG_INVIS 0x02 +#define MG_DETECT 0x04 +#define MG_PET 0x08 +#define MG_RIDDEN 0x10 + +/* sellobj_state() states */ +#define SELL_NORMAL (0) +#define SELL_DELIBERATE (1) +#define SELL_DONTSELL (2) + +/* + * This is the way the game ends. If these are rearranged, the arrays + * in end.c and topten.c will need to be changed. Some parts of the + * code assume that PANIC separates the deaths from the non-deaths. + */ +#define DIED 0 +#define CHOKING 1 +#define POISONING 2 +#define STARVING 3 +#define DROWNING 4 +#define BURNING 5 +#define DISSOLVED 6 +#define CRUSHING 7 +#define STONING 8 +#define TURNED_SLIME 9 +#define GENOCIDED 10 +#define PANICKED 11 +#define TRICKED 12 +#define QUIT 13 +#define ESCAPED 14 +#define ASCENDED 15 + +#include "align.h" +#include "dungeon.h" +#include "monsym.h" +#include "mkroom.h" +#include "objclass.h" +#include "youprop.h" +#include "wintype.h" +#include "decl.h" +#include "timeout.h" + +NEARDATA extern coord bhitpos; /* place where throw or zap hits or stops */ + +/* types of calls to bhit() */ +#define ZAPPED_WAND 0 +#define THROWN_WEAPON 1 +#define KICKED_WEAPON 2 +#define FLASHED_LIGHT 3 +#define INVIS_BEAM 4 + +#define MATCH_WARN_OF_MON(mon) (Warn_of_mon && flags.warntype && \ + (flags.warntype & (mon)->data->mflags2)) + +#include "trap.h" +#include "flag.h" +#include "rm.h" +#include "vision.h" +#include "display.h" +#include "engrave.h" +#include "rect.h" +#include "region.h" + +#ifdef USE_TRAMPOLI /* This doesn't belong here, but we have little choice */ +#undef NDECL +#define NDECL(f) f() +#endif + +#include "extern.h" +#include "winprocs.h" + +#ifdef USE_TRAMPOLI +#include "wintty.h" +#undef WINTTY_H +#include "trampoli.h" +#undef EXTERN_H +#include "extern.h" +#endif /* USE_TRAMPOLI */ + +#define NO_SPELL 0 + +/* flags to control makemon() */ +#define NO_MM_FLAGS 0x00 /* use this rather than plain 0 */ +#define NO_MINVENT 0x01 /* suppress minvent when creating mon */ +#define MM_NOWAIT 0x02 /* don't set STRAT_WAITMASK flags */ +#define MM_EDOG 0x04 /* add edog structure */ +#define MM_EMIN 0x08 /* add emin structure */ +#define MM_ANGRY 0x10 /* monster is created angry */ +#define MM_NONAME 0x20 /* monster is not christened */ +#define MM_NOCOUNTBIRTH 0x40 /* don't increment born counter (for revival) */ +#define MM_IGNOREWATER 0x80 /* ignore water when positioning */ +#define MM_ADJACENTOK 0x100 /* it is acceptable to use adjacent coordinates */ + +/* special mhpmax value when loading bones monster to flag as extinct or genocided */ +#define DEFUNCT_MONSTER (-100) + +/* flags for special ggetobj status returns */ +#define ALL_FINISHED 0x01 /* called routine already finished the job */ + +/* flags to control query_objlist() */ +#define BY_NEXTHERE 0x1 /* follow objlist by nexthere field */ +#define AUTOSELECT_SINGLE 0x2 /* if only 1 object, don't ask */ +#define USE_INVLET 0x4 /* use object's invlet */ +#define INVORDER_SORT 0x8 /* sort objects by packorder */ +#define SIGNAL_NOMENU 0x10 /* return -1 rather than 0 if none allowed */ +#define FEEL_COCKATRICE 0x20 /* engage cockatrice checks and react */ + +/* Flags to control query_category() */ +/* BY_NEXTHERE used by query_category() too, so skip 0x01 */ +#define UNPAID_TYPES 0x02 +#define GOLD_TYPES 0x04 +#define WORN_TYPES 0x08 +#define ALL_TYPES 0x10 +#define BILLED_TYPES 0x20 +#define CHOOSE_ALL 0x40 +#define BUC_BLESSED 0x80 +#define BUC_CURSED 0x100 +#define BUC_UNCURSED 0x200 +#define BUC_UNKNOWN 0x400 +#define BUC_ALLBKNOWN (BUC_BLESSED|BUC_CURSED|BUC_UNCURSED) +#define ALL_TYPES_SELECTED -2 + +/* Flags to control find_mid() */ +#define FM_FMON 0x01 /* search the fmon chain */ +#define FM_MIGRATE 0x02 /* search the migrating monster chain */ +#define FM_MYDOGS 0x04 /* search mydogs */ +#define FM_EVERYWHERE (FM_FMON | FM_MIGRATE | FM_MYDOGS) + +/* Flags to control pick_[race,role,gend,align] routines in role.c */ +#define PICK_RANDOM 0 +#define PICK_RIGID 1 + +/* Flags to control dotrap() in trap.c */ +#define NOWEBMSG 0x01 /* suppress stumble into web message */ +#define FORCEBUNGLE 0x02 /* adjustments appropriate for bungling */ +#define RECURSIVETRAP 0x04 /* trap changed into another type this same turn */ + +/* Flags to control test_move in hack.c */ +#define DO_MOVE 0 /* really doing the move */ +#define TEST_MOVE 1 /* test a normal move (move there next) */ +#define TEST_TRAV 2 /* test a future travel location */ + +/*** some utility macros ***/ +#define yn(query) yn_function(query,ynchars, 'n') +#define ynq(query) yn_function(query,ynqchars, 'q') +#define ynaq(query) yn_function(query,ynaqchars, 'y') +#define nyaq(query) yn_function(query,ynaqchars, 'n') +#define nyNaq(query) yn_function(query,ynNaqchars, 'n') +#define ynNaq(query) yn_function(query,ynNaqchars, 'y') + +/* Macros for scatter */ +#define VIS_EFFECTS 0x01 /* display visual effects */ +#define MAY_HITMON 0x02 /* objects may hit monsters */ +#define MAY_HITYOU 0x04 /* objects may hit you */ +#define MAY_HIT (MAY_HITMON|MAY_HITYOU) +#define MAY_DESTROY 0x08 /* objects may be destroyed at random */ +#define MAY_FRACTURE 0x10 /* boulders & statues may fracture */ + +/* Macros for launching objects */ +#define ROLL 0x01 /* the object is rolling */ +#define FLING 0x02 /* the object is flying thru the air */ +#define LAUNCH_UNSEEN 0x40 /* hero neither caused nor saw it */ +#define LAUNCH_KNOWN 0x80 /* the hero caused this by explicit action */ + +/* Macros for explosion types */ +#define EXPL_DARK 0 +#define EXPL_NOXIOUS 1 +#define EXPL_MUDDY 2 +#define EXPL_WET 3 +#define EXPL_MAGICAL 4 +#define EXPL_FIERY 5 +#define EXPL_FROSTY 6 +#define EXPL_MAX 7 + +/* Macros for messages referring to hands, eyes, feet, etc... */ +#define ARM 0 +#define EYE 1 +#define FACE 2 +#define FINGER 3 +#define FINGERTIP 4 +#define FOOT 5 +#define HAND 6 +#define HANDED 7 +#define HEAD 8 +#define LEG 9 +#define LIGHT_HEADED 10 +#define NECK 11 +#define SPINE 12 +#define TOE 13 +#define HAIR 14 +#define BLOOD 15 +#define LUNG 16 +#define NOSE 17 +#define STOMACH 18 + +/* Flags to control menus */ +#define MENUTYPELEN sizeof("traditional ") +#define MENU_TRADITIONAL 0 +#define MENU_COMBINATION 1 +#define MENU_PARTIAL 2 +#define MENU_FULL 3 + +#define MENU_SELECTED TRUE +#define MENU_UNSELECTED FALSE + +/* + * Option flags + * Each higher number includes the characteristics of the numbers + * below it. + */ +#define SET_IN_FILE 0 /* config file option only */ +#define SET_VIA_PROG 1 /* may be set via extern program, not seen in game */ +#define DISP_IN_GAME 2 /* may be set via extern program, displayed in game */ +#define SET_IN_GAME 3 /* may be set via extern program or set in the game */ + +#define FEATURE_NOTICE_VER(major,minor,patch) (((unsigned long)major << 24) | \ + ((unsigned long)minor << 16) | \ + ((unsigned long)patch << 8) | \ + ((unsigned long)0)) + +#define FEATURE_NOTICE_VER_MAJ (flags.suppress_alert >> 24) +#define FEATURE_NOTICE_VER_MIN (((unsigned long)(0x0000000000FF0000L & flags.suppress_alert)) >> 16) +#define FEATURE_NOTICE_VER_PATCH (((unsigned long)(0x000000000000FF00L & flags.suppress_alert)) >> 8) + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef min +#define min(x,y) ((x) < (y) ? (x) : (y)) +#endif +#define plur(x) (((x) == 1) ? "" : "s") + +#define ARM_BONUS(obj) (objects[(obj)->otyp].a_ac + (obj)->spe \ + - min((int)greatest_erosion(obj),objects[(obj)->otyp].a_ac)) + +#define makeknown(x) discover_object((x),TRUE,TRUE) +#define distu(xx,yy) dist2((int)(xx),(int)(yy),(int)u.ux,(int)u.uy) +#define onlineu(xx,yy) online2((int)(xx),(int)(yy),(int)u.ux,(int)u.uy) + +#define rn1(x,y) (rn2(x)+(y)) + +/* negative armor class is randomly weakened to prevent invulnerability */ +#define AC_VALUE(AC) ((AC) >= 0 ? (AC) : -rnd(-(AC))) + +#if defined(MICRO) && !defined(__DJGPP__) +#define getuid() 1 +#define getlogin() ((char *)0) +#endif /* MICRO */ + +#if defined(OVERLAY)&&(defined(OVL0)||defined(OVL1)||defined(OVL2)||defined(OVL3)||defined(OVLB)) +# define USE_OVLx +# define STATIC_DCL extern +# define STATIC_OVL +# ifdef OVLB +# define STATIC_VAR +# else +# define STATIC_VAR extern +# endif + +#else /* !OVERLAY || (!OVL0 && !OVL1 && !OVL2 && !OVL3 && !OVLB) */ +# define STATIC_DCL static +# define STATIC_OVL static +# define STATIC_VAR static + +/* If not compiling an overlay, compile everything. */ +# define OVL0 /* highest priority */ +# define OVL1 +# define OVL2 +# define OVL3 /* lowest specified priority */ +# define OVLB /* the base overlay segment */ +#endif /* OVERLAY && (OVL0 || OVL1 || OVL2 || OVL3 || OVLB) */ + +/* Macro for a few items that are only static if we're not overlaid.... */ +#if defined(USE_TRAMPOLI) || defined(USE_OVLx) +# define STATIC_PTR +#else +# define STATIC_PTR static +#endif + +/* The function argument to qsort() requires a particular + * calling convention under WINCE which is not the default + * in that environment. + */ +#if defined(WIN_CE) +# define CFDECLSPEC __cdecl +#else +# define CFDECLSPEC +#endif + +#endif /* HACK_H */ diff --git a/include/lev.h b/include/lev.h new file mode 100644 index 0000000..afc845a --- /dev/null +++ b/include/lev.h @@ -0,0 +1,49 @@ +/* SCCS Id: @(#)lev.h 3.4 1994/03/18 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Common include file for save and restore routines */ + +#ifndef LEV_H +#define LEV_H + +#define COUNT_SAVE 0x1 +#define WRITE_SAVE 0x2 +#define FREE_SAVE 0x4 + +/* operations of the various saveXXXchn & co. routines */ +#define perform_bwrite(mode) ((mode) & (COUNT_SAVE|WRITE_SAVE)) +#define release_data(mode) ((mode) & FREE_SAVE) + +/* The following are used in mkmaze.c */ +struct container { + struct container *next; + xchar x, y; + short what; + genericptr_t list; +}; + +#define CONS_OBJ 0 +#define CONS_MON 1 +#define CONS_HERO 2 +#define CONS_TRAP 3 + +struct bubble { + xchar x, y; /* coordinates of the upper left corner */ + schar dx, dy; /* the general direction of the bubble's movement */ + uchar *bm; /* pointer to the bubble bit mask */ + struct bubble *prev, *next; /* need to traverse the list up and down */ + struct container *cons; +}; + +/* used in light.c */ +typedef struct ls_t { + struct ls_t *next; + xchar x, y; /* source's position */ + short range; /* source's current range */ + short flags; + short type; /* type of light source */ + genericptr_t id; /* source's identifier */ +} light_source; + +#endif /* LEV_H */ diff --git a/include/load_img.h b/include/load_img.h new file mode 100644 index 0000000..eb4a27d --- /dev/null +++ b/include/load_img.h @@ -0,0 +1,46 @@ + +/* ------------------------------------------- */ +#define XIMG 0x58494D47 + +/* Header of GEM Image Files */ +typedef struct IMG_HEADER{ + short version; /* Img file format version (1) */ + short length; /* Header length in words (8) */ + short planes; /* Number of bit-planes (1) */ + short pat_len; /* length of Patterns (2) */ + short pix_w; /* Pixel width in 1/1000 mmm (372) */ + short pix_h; /* Pixel height in 1/1000 mmm (372) */ + short img_w; /* Pixels per line (=(x+7)/8 Bytes) */ + short img_h; /* Total number of lines */ + long magic; /* Contains "XIMG" if standard color */ + short paltype; /* palette type (0=RGB (short each)) */ + short *palette; /* palette etc. */ + char *addr; /* Address for the depacked bit-planes */ +} IMG_header; + +/* ------------------------------------------- */ +/* error codes */ +#define ERR_HEADER 1 +#define ERR_ALLOC 2 +#define ERR_FILE 3 +#define ERR_DEPACK 4 +#define ERR_COLOR 5 + +/* saves the current colorpalette with col colors in palette */ +void get_colors(int handle, short *palette, int col); + +/* sets col colors from palette */ +void img_set_colors(int handle,short *palette, int col); + +/* converts MFDB of size from standard to deviceformat (0 if succeded, else error). */ +int convert(MFDB *, long ); + +/* transforms image in VDI-Device format */ +int transform_img(MFDB *); + +/* Loads & depacks IMG (0 if succeded, else error). */ +/* Bitplanes are one after another in address IMG_HEADER.addr. */ +int depack_img(char *, IMG_header *); + +/* Halves IMG in Device-format, dest memory has to be allocated*/ +int half_img(MFDB *,MFDB *); diff --git a/include/mac-carbon.h b/include/mac-carbon.h new file mode 100644 index 0000000..9be633a --- /dev/null +++ b/include/mac-carbon.h @@ -0,0 +1,32 @@ +/* SCCS Id: @(#)mac-carbon.h 3.4 2003/06/01 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 2003. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Compiler prefix file for the Macintosh Carbon port. + * + * IMPORTANT: This file is intended only as a compiler prefix + * file and must NEVER be included by other source (.c or .h) + * files. + * + * Usage for MacOS X Project Builder: + * Project menu -> Edit Active Target '_target_' -> + * target settings dialog -> Settings -> Simple View -> + * GCC Compiler Settings -> + * set "Prefix Header" to include/mac-carbon.h + * + * Usage for Metrowerks CodeWarrior: + * Edit menu -> _target_ Settings -> Language Settings -> + * C/C++ Language -> + * set "Prefix File" to include/mac-carbon.h + */ + +#define MAC +#undef UNIX + +/* May already be defined by CodeWarrior as 0 or 1 */ +#ifdef TARGET_API_MAC_CARBON +# undef TARGET_API_MAC_CARBON +#endif +#define TARGET_API_MAC_CARBON 1 + +#define GCC_WARN diff --git a/include/mac-qt.h b/include/mac-qt.h new file mode 100644 index 0000000..8a5a128 --- /dev/null +++ b/include/mac-qt.h @@ -0,0 +1,33 @@ +/* SCCS Id: @(#)mac-qt.h 3.4 2003/06/01 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 2003. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Compiler prefix file for the Macintosh Qt port. + * + * IMPORTANT: This file is intended only as a compiler prefix + * file and must NEVER be included by other source (.c or .h) + * files. + * + * Usage for MacOS X Project Builder: + * Project menu -> Edit Active Target '_target_' -> + * target settings dialog -> Settings -> Simple View -> + * GCC Compiler Settings -> + * set "Prefix Header" to include/mac-qt.h + * + * Usage for Metrowerks CodeWarrior: + * Edit menu -> _target_ Settings -> Language Settings -> + * C/C++ Language -> + * set "Prefix File" to include/mac-qt.h + */ + +#undef MAC +#define UNIX +#define BSD + +/* May already be defined by CodeWarrior as 0 or 1 */ +#ifdef TARGET_API_MAC_CARBON +# undef TARGET_API_MAC_CARBON +#endif +#define TARGET_API_MAC_CARBON 0 + +#define GCC_WARN diff --git a/include/mac-term.h b/include/mac-term.h new file mode 100644 index 0000000..f87afa8 --- /dev/null +++ b/include/mac-term.h @@ -0,0 +1,34 @@ +/* SCCS Id: @(#)mac-term.h 3.4 2003/06/01 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 2003. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Compiler prefix file for the MacOS X Terminal.app port. + * + * IMPORTANT: This file is intended only as a compiler prefix + * file and must NEVER be included by other source (.c or .h) + * files. + * + * Usage for MacOS X Project Builder: + * Project menu -> Edit Active Target '_target_' -> + * target settings dialog -> Settings -> Simple View -> + * GCC Compiler Settings -> + * set "Prefix Header" to include/mac-term.h + * + * Usage for Metrowerks CodeWarrior: + * Edit menu -> _target_ Settings -> Language Settings -> + * C/C++ Language -> + * set "Prefix File" to include/mac-term.h + */ + +/* Stuff needed for the core of NetHack */ +#undef MAC +#define UNIX +#define BSD +#define __FreeBSD__ /* Darwin is based on FreeBSD */ +#define GCC_WARN + +/* May already be defined by CodeWarrior as 0 or 1 */ +#ifdef TARGET_API_MAC_CARBON +# undef TARGET_API_MAC_CARBON +#endif +#define TARGET_API_MAC_CARBON 0 /* Not Carbon */ diff --git a/include/macconf.h b/include/macconf.h new file mode 100644 index 0000000..6bf58fa --- /dev/null +++ b/include/macconf.h @@ -0,0 +1,119 @@ +/* SCCS Id: @(#)macconf.h 3.4 1999/10/25 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef MAC +# ifndef MACCONF_H +# define MACCONF_H + +/* + * Compiler selection is based on the following symbols: + * + * __SC__ sc, a MPW 68k compiler + * __MRC__ mrc, a MPW PowerPC compiler + * THINK_C Think C compiler + * __MWERKS__ Metrowerks' Codewarrior compiler + * + * We use these early in config.h to define some needed symbols, + * including MAC. + # + # The Metrowerks compiler defines __STDC__ (which sets NHSTC) and uses + # WIDENED_PROTOTYPES (defined if UNWIDENED_PROTOTYPES is undefined and + # NHSTDC is defined). + */ + +#ifndef __powerc +# define MAC68K /* 68K mac (non-powerpc) */ +#endif +#ifndef TARGET_API_MAC_CARBON +# define TARGET_API_MAC_CARBON 0 +#endif + + +#ifndef __MACH__ +#define RANDOM +#endif +#define NO_SIGNAL /* You wouldn't believe our signals ... */ +#define FILENAME 256 +#define NO_TERMS /* For tty port (see wintty.h) */ + +#define TEXTCOLOR /* For Mac TTY interface */ +#define CHANGE_COLOR + +/* Use these two includes instead of system.h. */ +#include +#include + +/* Uncomment this line if your headers don't already define off_t */ +/*typedef long off_t;*/ +#include /* for time_t */ + +/* + * Try and keep the number of files here to an ABSOLUTE minimum ! + * include the relevant files in the relevant .c files instead ! + */ +#if TARGET_API_MAC_CARBON + /* Avoid including -- it has a conflicting expl() */ +# define __FP__ +# include +#else +# include +#endif + +/* + * We could use the PSN under sys 7 here ... + * ...but it wouldn't matter... + */ +#define getpid() 1 +#define getuid() 1 +#define index strchr +#define rindex strrchr + +#define Rand random +extern void error(const char *,...); + +#if !defined(O_WRONLY) +# ifdef __MWERKS__ +# include +# endif +# include +#endif + +/* + * Don't redefine these Unix IO functions when making LevComp or DgnComp for + * MPW. With MPW, we make them into MPW tools, which use unix IO. SPEC_LEV + * and DGN_COMP are defined when compiling for LevComp and DgnComp respectively. + */ +#if !((defined(__SC__) || defined(__MRC__) || defined(__MACH__)) && (defined(SPEC_LEV) || defined(DGN_COMP))) +# define creat maccreat +# define open macopen +# define close macclose +# define read macread +# define write macwrite +# define lseek macseek +#ifdef __MWERKS__ +# define unlink _unlink +#endif +#endif + +#define YY_NEVER_INTERACTIVE 1 + +# define TEXT_TYPE 'TEXT' +# define LEVL_TYPE 'LEVL' +# define BONE_TYPE 'BONE' +# define SAVE_TYPE 'SAVE' +# define PREF_TYPE 'PREF' +# define DATA_TYPE 'DATA' +# define MAC_CREATOR 'nh31' /* Registered with DTS ! */ +# define TEXT_CREATOR 'ttxt' /* Something the user can actually edit */ + +/* + * Define PORT_HELP to be the name of the port-specfic help file. + * This file is included into the resource fork of the application. + */ +#define PORT_HELP "MacHelp" + +#define MAC_GRAPHICS_ENV + +# endif /* ! MACCONF_H */ +#endif /* MAC */ diff --git a/include/macpopup.h b/include/macpopup.h new file mode 100644 index 0000000..bdeb966 --- /dev/null +++ b/include/macpopup.h @@ -0,0 +1,15 @@ +/* SCCS Id: @(#)macpopup.h 3.4 1999/10/25 */ +/* Copyright (c) Nethack Develpment Team, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MACPOPUP_H +# define MACPOPUP_H + +/* ### mmodal.c ### */ + +extern void FlashButton(DialogRef, short); +extern char queued_resp(char *resp); +extern char topl_yn_function(const char *query, const char *resp, char def); +extern int get_line_from_key_queue(char *bufp); + +#endif /* MACPOPUP_H */ diff --git a/include/mactty.h b/include/mactty.h new file mode 100644 index 0000000..279e7c0 --- /dev/null +++ b/include/mactty.h @@ -0,0 +1,347 @@ +/* SCCS Id: @(#)mactty.h 3.4 1993/03/01 */ +/* Copyright (c) Jon W{tte 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This header is the supported external interface for the "tty" window + * package. This package sports care-free handling of "dumb" tty windows + * (preferrably using monospaced fonts) - it does NOT remember the strings + * sent to it; rather, it uses an offscreen bitmap. + * + * For best performance, make sure it is aligned on a 32-pixel boundary + * (or at least a 16-pixel one) in black & white. For 24bit color, + * alignment doesn't matter, and for 8-bit color, alignment to every + * fourth pixel is most efficient. + * + * (c) Copyright 1993 Jon W{tte + */ + +/* + * You should really not poke in the structures used by the tty window. + * However, since it uses the wRefCon of windows (by calling GetWRefCon + * and SetWRefCon) you lose that possibility. If you still want to store + * information about a window, the FIRST location _pointed to_ by the + * wRefCon will be a void * that you can use for whatever reasons. Don't + * take the address of this variable and expect it to stay the same + * across calls to the tty window. + * + * void * my_config_ptr = * ( void * * ) GetWRefCon ( tty_window ) ; + */ + +/* + * The library uses the window's port temporarily through SetPortBits; + * that means you shouldn't do any funky things to the clipping region + * etc. Actually, you shouldn't even resize the window, as that will clip + * new drawing. + * + * Also, if you use this library under Pascal, remember that the string + * passed to add_tty_string() is a "C" style string with NO length byte, + * and a terminating zero byte at the end instead. + */ + +#ifndef _H_tty_public +# define _H_tty_public +#undef red /* undef internal color const strings from decl */ +#undef green +#undef blue +#if !TARGET_API_MAC_CARBON +# include +#endif + +/* + * Error code returned when it's probably our fault, or + * bad parameters. + */ +#define general_failure 1 + +/* + * Base resource id's for window types + */ +#define WIN_BASE_RES 128 +#define WIN_BASE_KIND 128 + +/* + * Commonly used characters + */ +#define CHAR_ENTER ((char)3) +#define CHAR_BELL ((char)7) +#define CHAR_BS ((char)8) +#define CHAR_LF ((char)10) +#define CHAR_CR ((char)13) +#define CHAR_ESC ((char)27) +#define CHAR_BLANK ((char)32) +#define CHAR_DELETE ((char)127) + +extern char game_active; /* flag to window rendering routines not to use ppat */ +/* + * If you want some fancy operations that not a normal TTY device normally + * supports, use EXTENDED_SUPPORT. For frames, area erases and area scrolls, + * plus bitmap graphics - RESOLUTION DEPENDENT, be sure to call + * get_tty_metrics and use those limits. + */ +#define EXTENDED_SUPPORT 0 +/* + * if you print a lot of single characters, accumulating each one in a + * clipping region will take too much time. Instead, define this, which + * will clip in rects. + */ +#define CLIP_RECT_ONLY 1 + +typedef enum tty_attrib { + +/* + * Flags relating to the general functioning of the window. + * These flags are passed at create_tty time, and changing them + * later will clear the screen. + */ + TTY_ATTRIB_FLAGS , +/* + * When using proportional fonts, this will place each character + * separately, ensuring aligned columns (but looking ugly and taking + * time) + */ +# define TA_MOVE_EACH_CHAR 1L +/* + * This means draw each change as it occurs instead of collecting the area + * and draw it all at once at update_tty() - slower, but more reliable. + */ +# define TA_ALWAYS_REFRESH 2L +/* + * When reaching the right end, we either just stop drawing, or wrap to the + * next line. + */ +# define TA_WRAP_AROUND 4L +/* + * Overstrike means that characters are added on top of each other; i e don't + * clear the letter beneath. This is faster, using srcOr under QuickDraw + */ +# define TA_OVERSTRIKE 8L +/* + * We may want the window not to scroll when we reach the end line, + * but stop drawing instead. + */ +# define TA_INHIBIT_VERT_SCROLL 16L + +/* + * Foreground and background colors. These only affect characters + * drawn by subsequent calls; not what's already there (but it does + * affect clears) + * On b/w screens these do nothing. + */ + TTY_ATTRIB_FOREGROUND , + TTY_ATTRIB_BACKGROUND , +# define TA_RGB_TO_TTY(r) ((((long)((r).red>>8)&0xff)<<16)+\ + (((long)((r).green>>8)&0xff)<<8)+((long)((r).blue>>8)&0xff)) + +/* + * Attributes relating to the cursor, and character set mappings + */ + TTY_ATTRIB_CURSOR , +/* + * Blinking cursor is more noticeable when it's idle + */ +# define TA_BLINKING_CURSOR 1L +/* + * When handling input, do we echo characters as they are typed? + */ +# define TA_ECHO_INPUT 2L +/* + * Do we return each character code separately, or map delete etc? Note + * that non-raw input means getchar won't return anything until the user + * has typed a return. + */ +# define TA_RAW_INPUT 4L +/* + * Do we print every character as it is (including BS, NL and CR!) or do + * do we interpret characters such as NL, BS and CR? + */ +# define TA_RAW_OUTPUT 8L +/* + * When getting a NL, do we also move to the left? + */ +# define TA_NL_ADD_CR 16L +/* + * When getting a CR, do we also move down? + */ +# define TA_CR_ADD_NL 32L +/* + * Wait for input or return what we've got? + */ +# define TA_NONBLOCKING_IO 64L + +/* + * Use this macro to cast a function pointer to a tty attribute; this will help + * portability to systems where a function pointer doesn't fit in a long + */ +# define TA_ATTRIB_FUNC(x) ((long)(x)) + +/* + * This symbolic constant is used to check the number of attributes + */ + TTY_NUMBER_ATTRIBUTES + +} tty_attrib ; + +/* + * Character returned by end-of-file condition + */ +# define TTY_EOF -1 + + +/* + * Create the window according to a resource WIND template. + * The window pointer pointed to by window should be NULL to + * allocate the window record dynamically, or point to a + * WindowRecord structure already allocated. + * + * Passing in_color means you have to be sure there's color support; + * on the Mac, this means 32bit QuickDraw present, else it will + * crash. Not passing in_color means everything's rendered in + * black & white. + */ +extern short create_tty ( WindowPtr * window , short resource_id , + Boolean in_color ) ; + +/* + * Use init_tty_name or init_tty_number to initialize a window + * once allocated by create_tty. Size parameters are in characters. + */ + +extern short init_tty_number ( WindowPtr window , short font_number , + short font_size , short x_size , short y_size ) ; + +/* + * Close and deallocate a window and its data + */ +extern short destroy_tty ( WindowPtr window ) ; + +/* + * Change the font and font size used in the window for drawing after + * the calls are made. To change the coordinate system, be sure to call + * force_tty_coordinate_system_recalc() - else it may look strange if + * the new font doesn't match the old one. + */ +extern short set_tty_font_name (winid window_type , char * name ) ; +extern short force_tty_coordinate_system_recalc ( WindowPtr window ) ; + +/* + * Getting some metrics about the tty and its drawing. + */ +extern short get_tty_metrics ( WindowPtr window , short * x_size , + short * y_size , short * x_size_pixels , short * y_size_pixels , + short * font_number , short * font_size , + short * char_width , short * row_height ) ; + +/* + * The basic move cursor function. 0,0 is topleft. + */ +extern short move_tty_cursor ( WindowPtr window , short x_pos , + short y_pos ) ; + +/* + * Flush all changes done to a tty to the screen (see TA_ALWAYS_UPDATE above) + */ +extern short update_tty ( WindowPtr window ) ; + +/* + * Add a character to the tty and update the cursor position + */ +extern short add_tty_char ( WindowPtr window , short character ) ; + +/* + * Add a string of characters to the tty and update the cursor + * position. The string is 0-terminated! + */ +extern short add_tty_string ( WindowPtr window , const char * string ) ; + +/* + * Change or read an attribute of the tty. Note that some attribute changes + * may clear the screen. See the above enum and defines for values. + * Attributes can be both function pointers and special flag values. + */ +extern short get_tty_attrib ( WindowPtr window , tty_attrib attrib , + long * value ) ; +extern short set_tty_attrib ( WindowPtr window , tty_attrib attrib , + long value ) ; + +/* + * Scroll the actual TTY image, in characters, positive means up/left + * scroll_tty ( my_tty , 0 , 1 ) means a linefeed. Is always carried out + * directly, regardless of the wait-update setting. Does updates before + * scrolling. + */ +extern short scroll_tty ( WindowPtr window , short delta_x , + short delta_y ) ; + +/* + * Erase the offscreen bitmap and move the cursor to 0,0. Re-init some window + * values (font etc) Is always carried out directly on-screen, regardless of + * the wait-for-update setting. Clears update area. + */ +extern short clear_tty ( WindowPtr window ) ; + +/* + * Call this routine with a window (always _mt_window) and a time (usually + * from most recent event) to determine if cursor in window should be blinked + */ +extern short blink_cursor ( WindowPtr window , long when ) ; + +/* + * For screen dumps, open the printer port and call this function. Can be used + * for clipboard as well (only for a PICT, though; this library doesn't concern + * itself with characters, just bitmaps) + */ +extern short image_tty ( EventRecord *theEvent, WindowPtr window ) ; + +/* + * For erasing just an area of characters + */ +extern short clear_tty_window ( WindowPtr window , short from_row , + short from_col , short to_row , short to_col ) ; + +/* + * get and set the invalid region of the main window + */ + extern short get_invalid_region (WindowPtr window, Rect *inval_rect); + extern short set_invalid_region (WindowPtr window, Rect *inval_rect); + +/* + * Now in macsnd.c, which seemed like a good place + */ +extern void tty_nhbell (); + +#if EXTENDED_SUPPORT + +/* + * Various versions of delete character/s, insert line/s etc can be handled by + * this general-purpose function. Negative num_ means delete, positive means + * insert, and you can never be sure which of row and col operations come first + * if you specify both... + */ +extern short mangle_tty_rows_columns ( WindowPtr window , + short from_row , short num_rows , short from_col , short num_cols ) ; + +/* + * For framing an area without using grahpics characters. + * Note that the given limits are those used for framing, you should not + * draw in them. frame_fatness should typically be 1-5, and may be clipped + * if it is too large. + */ +extern short frame_tty_window ( WindowPtr window , short from_row , + short from_col , short to_row , short to_col , short frame_fatness ) ; + +/* + * For inverting specific characters after the fact. May look funny in color. + */ +extern short invert_tty_window ( WindowPtr window , short from_row , + short from_col , short to_row , short to_col ) ; + +/* + * For drawing lines on the tty - VERY DEVICE DEPENDENT. Use get_tty_metrics. + */ +extern short draw_tty_line ( WindowPtr window , short from_x , + short from_y , short to_x , short to_y ) ; + +#endif /* EXTENDED_SUPPORT */ + +#endif /* _H_tty_public */ diff --git a/include/macwin.h b/include/macwin.h new file mode 100644 index 0000000..92c1934 --- /dev/null +++ b/include/macwin.h @@ -0,0 +1,246 @@ +/* SCCS Id: @(#)macwin.h 3.4 1996/01/15 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MACWIN_H +# define MACWIN_H +#undef red /* undef internal color const strings from decl */ +#undef green +#undef blue + +#ifndef __MACH__ +#include +#include +#endif + +/* more headers */ +#ifdef THINK_C +#include /* for CtoPStr and PtoCStr */ +#endif + +/* resources */ +#define PLAYER_NAME_RES_ID 1001 + +/* fake some things if we don't have universal headers.. */ +#if 0 /*ndef NewUserItemProc*/ +typedef pascal void (*UserItemProcPtr)(WindowPtr theWindow, short itemNo); +typedef UserItemProcPtr UserItemUPP; +#define NewUserItemProc(p) (UserItemUPP)(p) + +typedef pascal void (*ControlActionProcPtr)(ControlHandle theControl, short partCode); +typedef ControlActionProcPtr ControlActionUPP; +#define NewControlActionProc(p) (ControlActionUPP)(p) + +typedef ModalFilterProcPtr ModalFilterUPP; +#define DisposeRoutineDescriptor(p) +#endif + +/* misc */ +#ifdef __MWERKS__ +# define ResumeProcPtr long /* for call to InitDialogs */ +#endif + +/* working dirs structure */ +typedef struct macdirs { + Str32 dataName; + short dataRefNum; + long dataDirID; + + Str32 saveName; + short saveRefNum; + long saveDirID; + + Str32 levelName; + short levelRefNum; + long levelDirID; +} MacDirs; + +typedef struct macflags { + Bitfield (processes, 1); + Bitfield (color, 1); + Bitfield (folders, 1); + Bitfield (tempMem, 1); + Bitfield (help, 1); + Bitfield (fsSpec, 1); + Bitfield (trueType, 1); + Bitfield (aux, 1); + Bitfield (alias, 1); + Bitfield (standardFile, 1); + Bitfield (hasDebugger, 1); + Bitfield (hasAE, 1); + Bitfield (gotOpen, 1); +} MacFlags; + +extern MacDirs theDirs; /* used in macfile.c */ +extern MacFlags macFlags; + +/* + * Mac windows + */ +#define NUM_MACWINDOWS 15 +#define TEXT_BLOCK 512L + +/* Window constants */ +#define kMapWindow 0 +#define kStatusWindow 1 +#define kMessageWindow 2 +#define kTextWindow 3 +#define kMenuWindow 4 +#define kLastWindowKind kMenuWindow + +/* + * This determines the minimum logical line length in text windows + * That is; even if physical width is less, this is where line breaks + * go at the minimum. 350 is about right for score lines with a + * geneva 10 pt font. + */ +#define MIN_RIGHT 350 + +typedef struct { + anything id; + char accelerator; + char groupAcc; + short line; +} MacMHMenuItem; + +typedef struct NhWindow { + WindowPtr its_window; + + short font_number; + short font_size; + short char_width; + short row_height; + short ascent_height; + + short x_size; + short y_size; + short x_curs; + short y_curs; + + short last_more_lin; /* Used by message window */ + short save_lin; /* Used by message window */ + + short miSize; /* size of menu items arrays */ + short miLen; /* number of menu items in array */ + MacMHMenuItem **menuInfo; /* Used by menus (array handle) */ + char menuChar; /* next menu accelerator to use */ + short **menuSelected; /* list of selected elements from list */ + short miSelLen; /* number of items selected */ + short how; /* menu mode */ + + char drawn; + Handle windowText; + long windowTextLen; + short scrollPos; + ControlHandle scrollBar; +} NhWindow; + +extern Boolean CheckNhWin(WindowPtr mac_win); + + +#define NUM_STAT_ROWS 2 +#define NUM_ROWS 22 +#define NUM_COLS 80 /* We shouldn't use column 0 */ +#define QUEUE_LEN 24 + +extern NhWindow * theWindows; + +extern struct window_procs mac_procs; + +#define NHW_BASE 0 +extern winid BASE_WINDOW, WIN_MAP, WIN_MESSAGE, WIN_INVEN, WIN_STATUS; + + +/* + * External declarations for the window routines. + */ + +#define E extern + +/* ### dprintf.c ### */ + +extern void dprintf (char *, ...); + +/* ### maccurs.c ### */ + +extern Boolean RetrievePosition (short, short *, short *); +extern Boolean RetrieveSize (short, short, short, short *, short *); +extern void SaveWindowPos (WindowPtr); +extern void SaveWindowSize (WindowPtr); +extern Boolean FDECL(RetrieveWinPos, (WindowPtr,short *,short *)); + +/* ### macerrs.c ### */ + +extern void showerror(char *,const char *); +extern Boolean itworked(short); +extern void mustwork(short); +extern void attemptingto(char *); +/* appear to be unused +extern void comment(char *,long); +extern void pushattemptingto(char *); +extern void popattempt(void); +*/ +/* ### macfile.c ### */ + +/* extern char *macgets(int fd, char *ptr, unsigned len); unused */ +extern void FDECL(C2P,(const char *c, unsigned char *p)); +extern void FDECL(P2C,(const unsigned char *p, char *c)); + +/* ### macmenu.c ### */ + +extern void DoMenuEvt (long); +extern void InitMenuRes(void); +extern void AdjustMenus(short); +#define DimMenuBar() AdjustMenus(1) +#define UndimMenuBar() AdjustMenus(0) + +/* ### macmain.c ### */ + +extern void FDECL (process_openfile, (short s_vol, long s_dir, Str255 fNm, OSType ft)); + +/* ### macwin.c ### */ + +extern void AddToKeyQueue(unsigned char, Boolean); +extern unsigned char GetFromKeyQueue (void); +void trans_num_keys (EventRecord *); +extern void NDECL (InitMac); +int FDECL (try_key_queue, (char *)); +void FDECL (enter_topl_mode, (char *)); +void FDECL (leave_topl_mode, (char *)); +void FDECL (topl_set_resp, (char *, char)); +Boolean FDECL (topl_key, (unsigned char, Boolean)); +E void FDECL(HandleEvent, (EventRecord *)); /* used in mmodal.c */ +extern void NDECL(port_help); + +extern Boolean small_screen; + +E void FDECL(mac_init_nhwindows, (int *, char **)); +E void NDECL(mac_askname); +E void NDECL(mac_get_nh_event); +E void FDECL(mac_exit_nhwindows, (const char *)); +E winid FDECL(mac_create_nhwindow, (int)); +E void FDECL(mac_clear_nhwindow, (winid)); +E void FDECL(mac_display_nhwindow, (winid, BOOLEAN_P)); +E void FDECL(mac_destroy_nhwindow, (winid)); +E void FDECL(mac_curs, (winid,int,int)); +E void FDECL(mac_putstr, (winid, int, const char *)); +E void FDECL(mac_start_menu, (winid)); +E void FDECL(mac_add_menu, (winid,int,const anything *, + CHAR_P,CHAR_P,int,const char *, BOOLEAN_P)); +E void FDECL(mac_end_menu, (winid, const char *)); +E int FDECL(mac_select_menu, (winid, int, menu_item **)); +#ifdef CLIPPING +E void FDECL(mac_cliparound, (int, int)); +#endif +E int NDECL(mac_nhgetch); +E int FDECL(mac_nh_poskey, (int *, int *, int *)); +E int NDECL(mac_doprev_message); +E char FDECL(mac_yn_function, (const char *, const char *, CHAR_P)); +E void FDECL(mac_getlin, (const char *,char *)); +E int NDECL(mac_get_ext_cmd); +E void FDECL(mac_number_pad, (int)); +E void NDECL(mac_delay_output); + +#undef E + +#endif /* ! MACWIN_H */ diff --git a/include/mail.h b/include/mail.h new file mode 100644 index 0000000..daa8b30 --- /dev/null +++ b/include/mail.h @@ -0,0 +1,20 @@ +/* SCCS Id: @(#)mail.h 3.4 1991/10/11 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* used by ckmailstatus() to pass information to the mail-daemon in newmail() */ + +#ifndef MAIL_H +#define MAIL_H + +#define MSG_OTHER 0 /* catch-all; none of the below... */ +#define MSG_MAIL 1 /* unimportant, uninteresting mail message */ +#define MSG_CALL 2 /* annoying phone/talk/chat-type interruption */ + +struct mail_info { + int message_typ; /* MSG_foo value */ + const char *display_txt; /* text for daemon to verbalize */ + const char *object_nam; /* text to tag object with */ + const char *response_cmd; /* command to eventually execute */ +}; + +#endif /* MAIL_H */ diff --git a/include/mfndpos.h b/include/mfndpos.h new file mode 100644 index 0000000..01a4bb5 --- /dev/null +++ b/include/mfndpos.h @@ -0,0 +1,29 @@ +/* SCCS Id: @(#)mfndpos.h 3.4 2002/04/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MFNDPOS_H +#define MFNDPOS_H + +#define ALLOW_TRAPS 0x00020000L /* can enter traps */ +#define ALLOW_U 0x00040000L /* can attack you */ +#define ALLOW_M 0x00080000L /* can attack other monsters */ +#define ALLOW_TM 0x00100000L /* can attack tame monsters */ +#define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS) +#define NOTONL 0x00200000L /* avoids direct line to player */ +#define OPENDOOR 0x00400000L /* opens closed doors */ +#define UNLOCKDOOR 0x00800000L /* unlocks locked doors */ +#define BUSTDOOR 0x01000000L /* breaks any doors */ +#define ALLOW_ROCK 0x02000000L /* pushes rocks */ +#define ALLOW_WALL 0x04000000L /* walks thru walls */ +#define ALLOW_DIG 0x08000000L /* digs */ +#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */ +#define ALLOW_SANCT 0x20000000L /* enters temples */ +#define ALLOW_SSM 0x40000000L /* ignores scare monster */ +#ifdef NHSTDC +#define NOGARLIC 0x80000000UL /* hates garlic */ +#else +#define NOGARLIC 0x80000000L /* hates garlic */ +#endif + +#endif /* MFNDPOS_H */ diff --git a/include/micro.h b/include/micro.h new file mode 100644 index 0000000..38cd737 --- /dev/null +++ b/include/micro.h @@ -0,0 +1,21 @@ +/* SCCS Id: @(#)micro.h 3.4 1990/02/22 */ +/* micro.h - function declarations for various microcomputers */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MICRO_H +#define MICRO_H + +extern const char *alllevels, *allbones; +extern char levels[], bones[], permbones[], hackdir[]; + +extern int ramdisk; + +#ifndef C +#define C(c) (0x1f & (c)) +#endif +#ifndef M +#define M(c) (((char)0x80) | (c)) +#endif +#define ABORT C('a') + +#endif /* MICRO_H */ diff --git a/include/mkroom.h b/include/mkroom.h new file mode 100644 index 0000000..b1363bd --- /dev/null +++ b/include/mkroom.h @@ -0,0 +1,103 @@ +/* SCCS Id: @(#)mkroom.h 3.4 1992/11/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MKROOM_H +#define MKROOM_H + +/* mkroom.h - types and structures for room and shop initialization */ + +struct mkroom { + schar lx,hx,ly,hy; /* usually xchar, but hx may be -1 */ + schar rtype; /* type of room (zoo, throne, etc...) */ + schar rlit; /* is the room lit ? */ + schar doorct; /* door count */ + schar fdoor; /* index for the first door of the room */ + schar nsubrooms; /* number of subrooms */ + boolean irregular; /* true if room is non-rectangular */ + struct mkroom *sbrooms[MAX_SUBROOMS]; /* Subrooms pointers */ + struct monst *resident; /* priest/shopkeeper/guard for this room */ +}; + +struct shclass { + const char *name; /* name of the shop type */ + char symb; /* this identifies the shop type */ + int prob; /* the shop type probability in % */ + schar shdist; /* object placement type */ +#define D_SCATTER 0 /* normal placement */ +#define D_SHOP 1 /* shop-like placement */ +#define D_TEMPLE 2 /* temple-like placement */ + struct itp { + int iprob; /* probability of an item type */ + int itype; /* item type: if >=0 a class, if < 0 a specific item */ + } iprobs[5]; + const char * const *shknms; /* list of shopkeeper names for this type */ +}; + +extern NEARDATA struct mkroom rooms[(MAXNROFROOMS+1)*2]; +extern NEARDATA struct mkroom* subrooms; +/* the normal rooms on the current level are described in rooms[0..n] for + * some n= rooms && (x) < rooms + MAXNROFROOMS) +#define IS_ROOM_INDEX(x) ((x) >= 0 && (x) < MAXNROFROOMS) +#define IS_SUBROOM_PTR(x) ((x) >= subrooms && \ + (x) < subrooms + MAXNROFROOMS) +#define IS_SUBROOM_INDEX(x) ((x) > MAXNROFROOMS && (x) < (MAXNROFROOMS*2)) +#define ROOM_INDEX(x) ((x) - rooms) +#define SUBROOM_INDEX(x) ((x) - subrooms) +#define IS_LAST_ROOM_PTR(x) (ROOM_INDEX(x) == nroom) +#define IS_LAST_SUBROOM_PTR(x) (!nsubroom || SUBROOM_INDEX(x) == nsubroom) + +#endif /* MKROOM_H */ diff --git a/include/monattk.h b/include/monattk.h new file mode 100644 index 0000000..02ee881 --- /dev/null +++ b/include/monattk.h @@ -0,0 +1,100 @@ +/* SCCS Id: @(#)monattk.h 3.4 2002/03/24 */ +/* NetHack may be freely redistributed. See license for details. */ +/* Copyright 1988, M. Stephenson */ + +#ifndef MONATTK_H +#define MONATTK_H + +/* Add new attack types below - ordering affects experience (exper.c). + * Attacks > AT_BUTT are worth extra experience. + */ +#define AT_ANY (-1) /* fake attack; dmgtype_fromattack wildcard */ +#define AT_NONE 0 /* passive monster (ex. acid blob) */ +#define AT_CLAW 1 /* claw (punch, hit, etc.) */ +#define AT_BITE 2 /* bite */ +#define AT_KICK 3 /* kick */ +#define AT_BUTT 4 /* head butt (ex. a unicorn) */ +#define AT_TUCH 5 /* touches */ +#define AT_STNG 6 /* sting */ +#define AT_HUGS 7 /* crushing bearhug */ +#define AT_SPIT 10 /* spits substance - ranged */ +#define AT_ENGL 11 /* engulf (swallow or by a cloud) */ +#define AT_BREA 12 /* breath - ranged */ +#define AT_EXPL 13 /* explodes - proximity */ +#define AT_BOOM 14 /* explodes when killed */ +#define AT_GAZE 15 /* gaze - ranged */ +#define AT_TENT 16 /* tentacles */ + +#define AT_WEAP 254 /* uses weapon */ +#define AT_MAGC 255 /* uses magic spell(s) */ + +/* Add new damage types below. + * + * Note that 1-10 correspond to the types of attack used in buzz(). + * Please don't disturb the order unless you rewrite the buzz() code. + */ +#define AD_ANY (-1) /* fake damage; attacktype_fordmg wildcard */ +#define AD_PHYS 0 /* ordinary physical */ +#define AD_MAGM 1 /* magic missiles */ +#define AD_FIRE 2 /* fire damage */ +#define AD_COLD 3 /* frost damage */ +#define AD_SLEE 4 /* sleep ray */ +#define AD_DISN 5 /* disintegration (death ray) */ +#define AD_ELEC 6 /* shock damage */ +#define AD_DRST 7 /* drains str (poison) */ +#define AD_ACID 8 /* acid damage */ +#define AD_SPC1 9 /* for extension of buzz() */ +#define AD_SPC2 10 /* for extension of buzz() */ +#define AD_BLND 11 /* blinds (yellow light) */ +#define AD_STUN 12 /* stuns */ +#define AD_SLOW 13 /* slows */ +#define AD_PLYS 14 /* paralyses */ +#define AD_DRLI 15 /* drains life levels (Vampire) */ +#define AD_DREN 16 /* drains magic energy */ +#define AD_LEGS 17 /* damages legs (xan) */ +#define AD_STON 18 /* petrifies (Medusa, cockatrice) */ +#define AD_STCK 19 /* sticks to you (mimic) */ +#define AD_SGLD 20 /* steals gold (leppie) */ +#define AD_SITM 21 /* steals item (nymphs) */ +#define AD_SEDU 22 /* seduces & steals multiple items */ +#define AD_TLPT 23 /* teleports you (Quantum Mech.) */ +#define AD_RUST 24 /* rusts armour (Rust Monster)*/ +#define AD_CONF 25 /* confuses (Umber Hulk) */ +#define AD_DGST 26 /* digests opponent (trapper, etc.) */ +#define AD_HEAL 27 /* heals opponent's wounds (nurse) */ +#define AD_WRAP 28 /* special "stick" for eels */ +#define AD_WERE 29 /* confers lycanthropy */ +#define AD_DRDX 30 /* drains dexterity (quasit) */ +#define AD_DRCO 31 /* drains constitution */ +#define AD_DRIN 32 /* drains intelligence (mind flayer) */ +#define AD_DISE 33 /* confers diseases */ +#define AD_DCAY 34 /* decays organics (brown Pudding) */ +#define AD_SSEX 35 /* Succubus seduction (extended) */ + /* If no SEDUCE then same as AD_SEDU */ +#define AD_HALU 36 /* causes hallucination */ +#define AD_DETH 37 /* for Death only */ +#define AD_PEST 38 /* for Pestilence only */ +#define AD_FAMN 39 /* for Famine only */ +#define AD_SLIM 40 /* turns you into green slime */ +#define AD_ENCH 41 /* remove enchantment (disenchanter) */ +#define AD_CORR 42 /* corrode armor (black pudding) */ + +#define AD_CLRC 240 /* random clerical spell */ +#define AD_SPEL 241 /* random magic spell */ +#define AD_RBRE 242 /* random breath weapon */ + +#define AD_SAMU 252 /* hits, may steal Amulet (Wizard) */ +#define AD_CURS 253 /* random curse (ex. gremlin) */ + + +/* + * Monster to monster attacks. When a monster attacks another (mattackm), + * any or all of the following can be returned. See mattackm() for more + * details. + */ +#define MM_MISS 0x0 /* aggressor missed */ +#define MM_HIT 0x1 /* aggressor hit defender */ +#define MM_DEF_DIED 0x2 /* defender died */ +#define MM_AGR_DIED 0x4 /* aggressor died */ + +#endif /* MONATTK_H */ diff --git a/include/mondata.h b/include/mondata.h new file mode 100644 index 0000000..7d86d59 --- /dev/null +++ b/include/mondata.h @@ -0,0 +1,193 @@ +/* SCCS Id: @(#)mondata.h 3.4 2003/01/08 */ +/* Copyright (c) 1989 Mike Threepoint */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MONDATA_H +#define MONDATA_H + +#define verysmall(ptr) ((ptr)->msize < MZ_SMALL) +#define bigmonst(ptr) ((ptr)->msize >= MZ_LARGE) + +#define pm_resistance(ptr,typ) (((ptr)->mresists & (typ)) != 0) + +#define resists_fire(mon) (((mon)->mintrinsics & MR_FIRE) != 0) +#define resists_cold(mon) (((mon)->mintrinsics & MR_COLD) != 0) +#define resists_sleep(mon) (((mon)->mintrinsics & MR_SLEEP) != 0) +#define resists_disint(mon) (((mon)->mintrinsics & MR_DISINT) != 0) +#define resists_elec(mon) (((mon)->mintrinsics & MR_ELEC) != 0) +#define resists_poison(mon) (((mon)->mintrinsics & MR_POISON) != 0) +#define resists_acid(mon) (((mon)->mintrinsics & MR_ACID) != 0) +#define resists_ston(mon) (((mon)->mintrinsics & MR_STONE) != 0) + +#define is_lminion(mon) (is_minion((mon)->data) && \ + (mon)->data->maligntyp >= A_COALIGNED && \ + ((mon)->data != &mons[PM_ANGEL] || \ + EPRI(mon)->shralign > 0)) + +#define is_flyer(ptr) (((ptr)->mflags1 & M1_FLY) != 0L) +#define is_floater(ptr) ((ptr)->mlet == S_EYE) +#define is_clinger(ptr) (((ptr)->mflags1 & M1_CLING) != 0L) +#define is_swimmer(ptr) (((ptr)->mflags1 & M1_SWIM) != 0L) +#define breathless(ptr) (((ptr)->mflags1 & M1_BREATHLESS) != 0L) +#define amphibious(ptr) (((ptr)->mflags1 & (M1_AMPHIBIOUS | M1_BREATHLESS)) != 0L) +#define passes_walls(ptr) (((ptr)->mflags1 & M1_WALLWALK) != 0L) +#define amorphous(ptr) (((ptr)->mflags1 & M1_AMORPHOUS) != 0L) +#define noncorporeal(ptr) ((ptr)->mlet == S_GHOST) +#define tunnels(ptr) (((ptr)->mflags1 & M1_TUNNEL) != 0L) +#define needspick(ptr) (((ptr)->mflags1 & M1_NEEDPICK) != 0L) +#define hides_under(ptr) (((ptr)->mflags1 & M1_CONCEAL) != 0L) +#define is_hider(ptr) (((ptr)->mflags1 & M1_HIDE) != 0L) +#define haseyes(ptr) (((ptr)->mflags1 & M1_NOEYES) == 0L) +#define eyecount(ptr) (!haseyes(ptr) ? 0 : \ + ((ptr) == &mons[PM_CYCLOPS] || \ + (ptr) == &mons[PM_FLOATING_EYE]) ? 1 : 2) +#define nohands(ptr) (((ptr)->mflags1 & M1_NOHANDS) != 0L) +#define nolimbs(ptr) (((ptr)->mflags1 & M1_NOLIMBS) == M1_NOLIMBS) +#define notake(ptr) (((ptr)->mflags1 & M1_NOTAKE) != 0L) +#define has_head(ptr) (((ptr)->mflags1 & M1_NOHEAD) == 0L) +#define has_horns(ptr) (num_horns(ptr) > 0) +#define is_whirly(ptr) ((ptr)->mlet == S_VORTEX || \ + (ptr) == &mons[PM_AIR_ELEMENTAL]) +#define flaming(ptr) ((ptr) == &mons[PM_FIRE_VORTEX] || \ + (ptr) == &mons[PM_FLAMING_SPHERE] || \ + (ptr) == &mons[PM_FIRE_ELEMENTAL] || \ + (ptr) == &mons[PM_SALAMANDER]) +#define is_silent(ptr) ((ptr)->msound == MS_SILENT) +#define unsolid(ptr) (((ptr)->mflags1 & M1_UNSOLID) != 0L) +#define mindless(ptr) (((ptr)->mflags1 & M1_MINDLESS) != 0L) +#define humanoid(ptr) (((ptr)->mflags1 & M1_HUMANOID) != 0L) +#define is_animal(ptr) (((ptr)->mflags1 & M1_ANIMAL) != 0L) +#define slithy(ptr) (((ptr)->mflags1 & M1_SLITHY) != 0L) +#define is_wooden(ptr) ((ptr) == &mons[PM_WOOD_GOLEM]) +#define thick_skinned(ptr) (((ptr)->mflags1 & M1_THICK_HIDE) != 0L) +#define lays_eggs(ptr) (((ptr)->mflags1 & M1_OVIPAROUS) != 0L) +#define regenerates(ptr) (((ptr)->mflags1 & M1_REGEN) != 0L) +#define perceives(ptr) (((ptr)->mflags1 & M1_SEE_INVIS) != 0L) +#define can_teleport(ptr) (((ptr)->mflags1 & M1_TPORT) != 0L) +#define control_teleport(ptr) (((ptr)->mflags1 & M1_TPORT_CNTRL) != 0L) +#define telepathic(ptr) ((ptr) == &mons[PM_FLOATING_EYE] || \ + (ptr) == &mons[PM_MIND_FLAYER] || \ + (ptr) == &mons[PM_MASTER_MIND_FLAYER]) +#define is_armed(ptr) attacktype(ptr, AT_WEAP) +#define acidic(ptr) (((ptr)->mflags1 & M1_ACID) != 0L) +#define poisonous(ptr) (((ptr)->mflags1 & M1_POIS) != 0L) +#define carnivorous(ptr) (((ptr)->mflags1 & M1_CARNIVORE) != 0L) +#define herbivorous(ptr) (((ptr)->mflags1 & M1_HERBIVORE) != 0L) +#define metallivorous(ptr) (((ptr)->mflags1 & M1_METALLIVORE) != 0L) +#define polyok(ptr) (((ptr)->mflags2 & M2_NOPOLY) == 0L) +#define is_undead(ptr) (((ptr)->mflags2 & M2_UNDEAD) != 0L) +#define is_were(ptr) (((ptr)->mflags2 & M2_WERE) != 0L) +#define is_elf(ptr) (((ptr)->mflags2 & M2_ELF) != 0L) +#define is_dwarf(ptr) (((ptr)->mflags2 & M2_DWARF) != 0L) +#define is_gnome(ptr) (((ptr)->mflags2 & M2_GNOME) != 0L) +#define is_orc(ptr) (((ptr)->mflags2 & M2_ORC) != 0L) +#define is_human(ptr) (((ptr)->mflags2 & M2_HUMAN) != 0L) +#define your_race(ptr) (((ptr)->mflags2 & urace.selfmask) != 0L) +#define is_bat(ptr) ((ptr) == &mons[PM_BAT] || \ + (ptr) == &mons[PM_GIANT_BAT] || \ + (ptr) == &mons[PM_VAMPIRE_BAT]) +#define is_bird(ptr) ((ptr)->mlet == S_BAT && !is_bat(ptr)) +#define is_giant(ptr) (((ptr)->mflags2 & M2_GIANT) != 0L) +#define is_golem(ptr) ((ptr)->mlet == S_GOLEM) +#define is_domestic(ptr) (((ptr)->mflags2 & M2_DOMESTIC) != 0L) +#define is_demon(ptr) (((ptr)->mflags2 & M2_DEMON) != 0L) +#define is_mercenary(ptr) (((ptr)->mflags2 & M2_MERC) != 0L) +#define is_male(ptr) (((ptr)->mflags2 & M2_MALE) != 0L) +#define is_female(ptr) (((ptr)->mflags2 & M2_FEMALE) != 0L) +#define is_neuter(ptr) (((ptr)->mflags2 & M2_NEUTER) != 0L) +#define is_wanderer(ptr) (((ptr)->mflags2 & M2_WANDER) != 0L) +#define always_hostile(ptr) (((ptr)->mflags2 & M2_HOSTILE) != 0L) +#define always_peaceful(ptr) (((ptr)->mflags2 & M2_PEACEFUL) != 0L) +#define race_hostile(ptr) (((ptr)->mflags2 & urace.hatemask) != 0L) +#define race_peaceful(ptr) (((ptr)->mflags2 & urace.lovemask) != 0L) +#define extra_nasty(ptr) (((ptr)->mflags2 & M2_NASTY) != 0L) +#define strongmonst(ptr) (((ptr)->mflags2 & M2_STRONG) != 0L) +#define can_breathe(ptr) attacktype(ptr, AT_BREA) +#define cantwield(ptr) (nohands(ptr) || verysmall(ptr)) +#define could_twoweap(ptr) ((ptr)->mattk[1].aatyp == AT_WEAP) +#define cantweararm(ptr) (breakarm(ptr) || sliparm(ptr)) +#define throws_rocks(ptr) (((ptr)->mflags2 & M2_ROCKTHROW) != 0L) +#define type_is_pname(ptr) (((ptr)->mflags2 & M2_PNAME) != 0L) +#define is_lord(ptr) (((ptr)->mflags2 & M2_LORD) != 0L) +#define is_prince(ptr) (((ptr)->mflags2 & M2_PRINCE) != 0L) +#define is_ndemon(ptr) (is_demon(ptr) && \ + (((ptr)->mflags2 & (M2_LORD|M2_PRINCE)) == 0L)) +#define is_dlord(ptr) (is_demon(ptr) && is_lord(ptr)) +#define is_dprince(ptr) (is_demon(ptr) && is_prince(ptr)) +#define is_minion(ptr) ((ptr)->mflags2 & M2_MINION) +#define likes_gold(ptr) (((ptr)->mflags2 & M2_GREEDY) != 0L) +#define likes_gems(ptr) (((ptr)->mflags2 & M2_JEWELS) != 0L) +#define likes_objs(ptr) (((ptr)->mflags2 & M2_COLLECT) != 0L || \ + is_armed(ptr)) +#define likes_magic(ptr) (((ptr)->mflags2 & M2_MAGIC) != 0L) +#define webmaker(ptr) ((ptr) == &mons[PM_CAVE_SPIDER] || \ + (ptr) == &mons[PM_GIANT_SPIDER]) +#define is_unicorn(ptr) ((ptr)->mlet == S_UNICORN && likes_gems(ptr)) +#define is_longworm(ptr) (((ptr) == &mons[PM_BABY_LONG_WORM]) || \ + ((ptr) == &mons[PM_LONG_WORM]) || \ + ((ptr) == &mons[PM_LONG_WORM_TAIL])) +#define is_covetous(ptr) ((ptr->mflags3 & M3_COVETOUS)) +#define infravision(ptr) ((ptr->mflags3 & M3_INFRAVISION)) +#define infravisible(ptr) ((ptr->mflags3 & M3_INFRAVISIBLE)) +#define is_mplayer(ptr) (((ptr) >= &mons[PM_ARCHEOLOGIST]) && \ + ((ptr) <= &mons[PM_WIZARD])) +#define is_rider(ptr) ((ptr) == &mons[PM_DEATH] || \ + (ptr) == &mons[PM_FAMINE] || \ + (ptr) == &mons[PM_PESTILENCE]) +#define is_placeholder(ptr) ((ptr) == &mons[PM_ORC] || \ + (ptr) == &mons[PM_GIANT] || \ + (ptr) == &mons[PM_ELF] || \ + (ptr) == &mons[PM_HUMAN]) +/* return TRUE if the monster tends to revive */ +#define is_reviver(ptr) (is_rider(ptr) || (ptr)->mlet == S_TROLL) + +/* this returns the light's range, or 0 if none; if we add more light emitting + monsters, we'll likely have to add a new light range field to mons[] */ +#define emits_light(ptr) (((ptr)->mlet == S_LIGHT || \ + (ptr) == &mons[PM_FLAMING_SPHERE] || \ + (ptr) == &mons[PM_SHOCKING_SPHERE] || \ + (ptr) == &mons[PM_FIRE_VORTEX]) ? 1 : \ + ((ptr) == &mons[PM_FIRE_ELEMENTAL]) ? 1 : 0) +/* [note: the light ranges above were reduced to 1 for performance...] */ +#define likes_lava(ptr) (ptr == &mons[PM_FIRE_ELEMENTAL] || \ + ptr == &mons[PM_SALAMANDER]) +#define pm_invisible(ptr) ((ptr) == &mons[PM_STALKER] || \ + (ptr) == &mons[PM_BLACK_LIGHT]) + +/* could probably add more */ +#define likes_fire(ptr) ((ptr) == &mons[PM_FIRE_VORTEX] || \ + (ptr) == &mons[PM_FLAMING_SPHERE] || \ + likes_lava(ptr)) + +#define touch_petrifies(ptr) ((ptr) == &mons[PM_COCKATRICE] || \ + (ptr) == &mons[PM_CHICKATRICE]) + +#define is_mind_flayer(ptr) ((ptr) == &mons[PM_MIND_FLAYER] || \ + (ptr) == &mons[PM_MASTER_MIND_FLAYER]) + +#define nonliving(ptr) (is_golem(ptr) || is_undead(ptr) || \ + (ptr)->mlet == S_VORTEX || \ + (ptr) == &mons[PM_MANES]) + +/* Used for conduct with corpses, tins, and digestion attacks */ +/* G_NOCORPSE monsters might still be swallowed as a purple worm */ +/* Maybe someday this could be in mflags... */ +#define vegan(ptr) ((ptr)->mlet == S_BLOB || \ + (ptr)->mlet == S_JELLY || \ + (ptr)->mlet == S_FUNGUS || \ + (ptr)->mlet == S_VORTEX || \ + (ptr)->mlet == S_LIGHT || \ + ((ptr)->mlet == S_ELEMENTAL && \ + (ptr) != &mons[PM_STALKER]) || \ + ((ptr)->mlet == S_GOLEM && \ + (ptr) != &mons[PM_FLESH_GOLEM] && \ + (ptr) != &mons[PM_LEATHER_GOLEM]) || \ + noncorporeal(ptr)) +#define vegetarian(ptr) (vegan(ptr) || \ + ((ptr)->mlet == S_PUDDING && \ + (ptr) != &mons[PM_BLACK_PUDDING])) + +#define befriend_with_obj(ptr, obj) ((obj)->oclass == FOOD_CLASS && \ + is_domestic(ptr)) + +#endif /* MONDATA_H */ diff --git a/include/monflag.h b/include/monflag.h new file mode 100644 index 0000000..8ea8c15 --- /dev/null +++ b/include/monflag.h @@ -0,0 +1,201 @@ +/* SCCS Id: @(#)monflag.h 3.4 1996/05/04 */ +/* Copyright (c) 1989 Mike Threepoint */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MONFLAG_H +#define MONFLAG_H + +#define MS_SILENT 0 /* makes no sound */ +#define MS_BARK 1 /* if full moon, may howl */ +#define MS_MEW 2 /* mews or hisses */ +#define MS_ROAR 3 /* roars */ +#define MS_GROWL 4 /* growls */ +#define MS_SQEEK 5 /* squeaks, as a rodent */ +#define MS_SQAWK 6 /* squawks, as a bird */ +#define MS_HISS 7 /* hisses */ +#define MS_BUZZ 8 /* buzzes (killer bee) */ +#define MS_GRUNT 9 /* grunts (or speaks own language) */ +#define MS_NEIGH 10 /* neighs, as an equine */ +#define MS_WAIL 11 /* wails, as a tortured soul */ +#define MS_GURGLE 12 /* gurgles, as liquid or through saliva */ +#define MS_BURBLE 13 /* burbles (jabberwock) */ +#define MS_ANIMAL 13 /* up to here are animal noises */ +#define MS_SHRIEK 15 /* wakes up others */ +#define MS_BONES 16 /* rattles bones (skeleton) */ +#define MS_LAUGH 17 /* grins, smiles, giggles, and laughs */ +#define MS_MUMBLE 18 /* says something or other */ +#define MS_IMITATE 19 /* imitates others (leocrotta) */ +#define MS_ORC MS_GRUNT /* intelligent brutes */ +#define MS_HUMANOID 20 /* generic traveling companion */ +#ifdef KOPS +#define MS_ARREST 21 /* "Stop in the name of the law!" (Kops) */ +#endif +#define MS_SOLDIER 22 /* army and watchmen expressions */ +#define MS_GUARD 23 /* "Please drop that gold and follow me." */ +#define MS_DJINNI 24 /* "Thank you for freeing me!" */ +#define MS_NURSE 25 /* "Take off your shirt, please." */ +#define MS_SEDUCE 26 /* "Hello, sailor." (Nymphs) */ +#define MS_VAMPIRE 27 /* vampiric seduction, Vlad's exclamations */ +#define MS_BRIBE 28 /* asks for money, or berates you */ +#define MS_CUSS 29 /* berates (demons) or intimidates (Wiz) */ +#define MS_RIDER 30 /* astral level special monsters */ +#define MS_LEADER 31 /* your class leader */ +#define MS_NEMESIS 32 /* your nemesis */ +#define MS_GUARDIAN 33 /* your leader's guards */ +#define MS_SELL 34 /* demand payment, complain about shoplifters */ +#define MS_ORACLE 35 /* do a consultation */ +#define MS_PRIEST 36 /* ask for contribution; do cleansing */ +#define MS_SPELL 37 /* spellcaster not matching any of the above */ +#define MS_WERE 38 /* lycanthrope in human form */ +#define MS_BOAST 39 /* giants */ + + +#define MR_FIRE 0x01 /* resists fire */ +#define MR_COLD 0x02 /* resists cold */ +#define MR_SLEEP 0x04 /* resists sleep */ +#define MR_DISINT 0x08 /* resists disintegration */ +#define MR_ELEC 0x10 /* resists electricity */ +#define MR_POISON 0x20 /* resists poison */ +#define MR_ACID 0x40 /* resists acid */ +#define MR_STONE 0x80 /* resists petrification */ +/* other resistances: magic, sickness */ +/* other conveyances: teleport, teleport control, telepathy */ + +/* individual resistances */ +#define MR2_SEE_INVIS 0x0100 /* see invisible */ +#define MR2_LEVITATE 0x0200 /* levitation */ +#define MR2_WATERWALK 0x0400 /* water walking */ +#define MR2_MAGBREATH 0x0800 /* magical breathing */ +#define MR2_DISPLACED 0x1000 /* displaced */ +#define MR2_STRENGTH 0x2000 /* gauntlets of power */ +#define MR2_FUMBLING 0x4000 /* clumsy */ + + +#define M1_FLY 0x00000001L /* can fly or float */ +#define M1_SWIM 0x00000002L /* can traverse water */ +#define M1_AMORPHOUS 0x00000004L /* can flow under doors */ +#define M1_WALLWALK 0x00000008L /* can phase thru rock */ +#define M1_CLING 0x00000010L /* can cling to ceiling */ +#define M1_TUNNEL 0x00000020L /* can tunnel thru rock */ +#define M1_NEEDPICK 0x00000040L /* needs pick to tunnel */ +#define M1_CONCEAL 0x00000080L /* hides under objects */ +#define M1_HIDE 0x00000100L /* mimics, blends in with ceiling */ +#define M1_AMPHIBIOUS 0x00000200L /* can survive underwater */ +#define M1_BREATHLESS 0x00000400L /* doesn't need to breathe */ +#define M1_NOTAKE 0x00000800L /* cannot pick up objects */ +#define M1_NOEYES 0x00001000L /* no eyes to gaze into or blind */ +#define M1_NOHANDS 0x00002000L /* no hands to handle things */ +#define M1_NOLIMBS 0x00006000L /* no arms/legs to kick/wear on */ +#define M1_NOHEAD 0x00008000L /* no head to behead */ +#define M1_MINDLESS 0x00010000L /* has no mind--golem, zombie, mold */ +#define M1_HUMANOID 0x00020000L /* has humanoid head/arms/torso */ +#define M1_ANIMAL 0x00040000L /* has animal body */ +#define M1_SLITHY 0x00080000L /* has serpent body */ +#define M1_UNSOLID 0x00100000L /* has no solid or liquid body */ +#define M1_THICK_HIDE 0x00200000L /* has thick hide or scales */ +#define M1_OVIPAROUS 0x00400000L /* can lay eggs */ +#define M1_REGEN 0x00800000L /* regenerates hit points */ +#define M1_SEE_INVIS 0x01000000L /* can see invisible creatures */ +#define M1_TPORT 0x02000000L /* can teleport */ +#define M1_TPORT_CNTRL 0x04000000L /* controls where it teleports to */ +#define M1_ACID 0x08000000L /* acidic to eat */ +#define M1_POIS 0x10000000L /* poisonous to eat */ +#define M1_CARNIVORE 0x20000000L /* eats corpses */ +#define M1_HERBIVORE 0x40000000L /* eats fruits */ +#define M1_OMNIVORE 0x60000000L /* eats both */ +#ifdef NHSTDC +#define M1_METALLIVORE 0x80000000UL /* eats metal */ +#else +#define M1_METALLIVORE 0x80000000L /* eats metal */ +#endif + +#define M2_NOPOLY 0x00000001L /* players mayn't poly into one */ +#define M2_UNDEAD 0x00000002L /* is walking dead */ +#define M2_WERE 0x00000004L /* is a lycanthrope */ +#define M2_HUMAN 0x00000008L /* is a human */ +#define M2_ELF 0x00000010L /* is an elf */ +#define M2_DWARF 0x00000020L /* is a dwarf */ +#define M2_GNOME 0x00000040L /* is a gnome */ +#define M2_ORC 0x00000080L /* is an orc */ +#define M2_DEMON 0x00000100L /* is a demon */ +#define M2_MERC 0x00000200L /* is a guard or soldier */ +#define M2_LORD 0x00000400L /* is a lord to its kind */ +#define M2_PRINCE 0x00000800L /* is an overlord to its kind */ +#define M2_MINION 0x00001000L /* is a minion of a deity */ +#define M2_GIANT 0x00002000L /* is a giant */ +#define M2_MALE 0x00010000L /* always male */ +#define M2_FEMALE 0x00020000L /* always female */ +#define M2_NEUTER 0x00040000L /* neither male nor female */ +#define M2_PNAME 0x00080000L /* monster name is a proper name */ +#define M2_HOSTILE 0x00100000L /* always starts hostile */ +#define M2_PEACEFUL 0x00200000L /* always starts peaceful */ +#define M2_DOMESTIC 0x00400000L /* can be tamed by feeding */ +#define M2_WANDER 0x00800000L /* wanders randomly */ +#define M2_STALK 0x01000000L /* follows you to other levels */ +#define M2_NASTY 0x02000000L /* extra-nasty monster (more xp) */ +#define M2_STRONG 0x04000000L /* strong (or big) monster */ +#define M2_ROCKTHROW 0x08000000L /* throws boulders */ +#define M2_GREEDY 0x10000000L /* likes gold */ +#define M2_JEWELS 0x20000000L /* likes gems */ +#define M2_COLLECT 0x40000000L /* picks up weapons and food */ +#ifdef NHSTDC +#define M2_MAGIC 0x80000000UL /* picks up magic items */ +#else +#define M2_MAGIC 0x80000000L /* picks up magic items */ +#endif + +#define M3_WANTSAMUL 0x0001 /* would like to steal the amulet */ +#define M3_WANTSBELL 0x0002 /* wants the bell */ +#define M3_WANTSBOOK 0x0004 /* wants the book */ +#define M3_WANTSCAND 0x0008 /* wants the candelabrum */ +#define M3_WANTSARTI 0x0010 /* wants the quest artifact */ +#define M3_WANTSALL 0x001f /* wants any major artifact */ +#define M3_WAITFORU 0x0040 /* waits to see you or get attacked */ +#define M3_CLOSE 0x0080 /* lets you close unless attacked */ + +#define M3_COVETOUS 0x001f /* wants something */ +#define M3_WAITMASK 0x00c0 /* waiting... */ + +/* Infravision is currently implemented for players only */ +#define M3_INFRAVISION 0x0100 /* has infravision */ +#define M3_INFRAVISIBLE 0x0200 /* visible by infravision */ + +#define MZ_TINY 0 /* < 2' */ +#define MZ_SMALL 1 /* 2-4' */ +#define MZ_MEDIUM 2 /* 4-7' */ +#define MZ_HUMAN MZ_MEDIUM /* human-sized */ +#define MZ_LARGE 3 /* 7-12' */ +#define MZ_HUGE 4 /* 12-25' */ +#define MZ_GIGANTIC 7 /* off the scale */ + + +/* Monster races -- must stay within ROLE_RACEMASK */ +/* Eventually this may become its own field */ +#define MH_HUMAN M2_HUMAN +#define MH_ELF M2_ELF +#define MH_DWARF M2_DWARF +#define MH_GNOME M2_GNOME +#define MH_ORC M2_ORC + + +/* for mons[].geno (constant during game) */ +#define G_UNIQ 0x1000 /* generated only once */ +#define G_NOHELL 0x0800 /* not generated in "hell" */ +#define G_HELL 0x0400 /* generated only in "hell" */ +#define G_NOGEN 0x0200 /* generated only specially */ +#define G_SGROUP 0x0080 /* appear in small groups normally */ +#define G_LGROUP 0x0040 /* appear in large groups normally */ +#define G_GENO 0x0020 /* can be genocided */ +#define G_NOCORPSE 0x0010 /* no corpse left ever */ +#define G_FREQ 0x0007 /* creation frequency mask */ + +/* for mvitals[].mvflags (variant during game), along with G_NOCORPSE */ +#define G_KNOWN 0x0004 /* have been encountered */ +#define G_GONE (G_GENOD|G_EXTINCT) +#define G_GENOD 0x0002 /* have been genocided */ +#define G_EXTINCT 0x0001 /* have been extinguished as + population control */ +#define MV_KNOWS_EGG 0x0008 /* player recognizes egg of this + monster type */ + +#endif /* MONFLAG_H */ diff --git a/include/monst.h b/include/monst.h new file mode 100644 index 0000000..af2bb78 --- /dev/null +++ b/include/monst.h @@ -0,0 +1,181 @@ +/* SCCS Id: @(#)monst.h 3.4 1999/01/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MONST_H +#define MONST_H + +/* The weapon_check flag is used two ways: + * 1) When calling mon_wield_item, is 2-6 depending on what is desired. + * 2) Between calls to mon_wield_item, is 0 or 1 depending on whether or not + * the weapon is known by the monster to be cursed (so it shouldn't bother + * trying for another weapon). + * I originally planned to also use 0 if the monster already had its best + * weapon, to avoid the overhead of a call to mon_wield_item, but it turns out + * that there are enough situations which might make a monster change its + * weapon that this is impractical. --KAA + */ +# define NO_WEAPON_WANTED 0 +# define NEED_WEAPON 1 +# define NEED_RANGED_WEAPON 2 +# define NEED_HTH_WEAPON 3 +# define NEED_PICK_AXE 4 +# define NEED_AXE 5 +# define NEED_PICK_OR_AXE 6 + +/* The following flags are used for the second argument to display_minventory + * in invent.c: + * + * MINV_NOLET If set, don't display inventory letters on monster's inventory. + * MINV_ALL If set, display all items in monster's inventory, otherwise + * just display wielded weapons and worn items. + */ +#define MINV_NOLET 0x01 +#define MINV_ALL 0x02 + +#ifndef ALIGN_H +#include "align.h" +#endif + +struct monst { + struct monst *nmon; + struct permonst *data; + unsigned m_id; + short mnum; /* permanent monster index number */ + short movement; /* movement points (derived from permonst definition and added effects */ + uchar m_lev; /* adjusted difficulty level of monster */ + aligntyp malign; /* alignment of this monster, relative to the + player (positive = good to kill) */ + xchar mx, my; + xchar mux, muy; /* where the monster thinks you are */ +#define MTSZ 4 + coord mtrack[MTSZ]; /* monster track */ + int mhp, mhpmax; + unsigned mappearance; /* for undetected mimics and the wiz */ + uchar m_ap_type; /* what mappearance is describing: */ +#define M_AP_NOTHING 0 /* mappearance is unused -- monster appears + as itself */ +#define M_AP_FURNITURE 1 /* stairs, a door, an altar, etc. */ +#define M_AP_OBJECT 2 /* an object */ +#define M_AP_MONSTER 3 /* a monster */ + + schar mtame; /* level of tameness, implies peaceful */ + unsigned short mintrinsics; /* low 8 correspond to mresists */ + int mspec_used; /* monster's special ability attack timeout */ + + Bitfield(female,1); /* is female */ + Bitfield(minvis,1); /* currently invisible */ + Bitfield(invis_blkd,1); /* invisibility blocked */ + Bitfield(perminvis,1); /* intrinsic minvis value */ + Bitfield(cham,3); /* shape-changer */ +/* note: lychanthropes are handled elsewhere */ +#define CHAM_ORDINARY 0 /* not a shapechanger */ +#define CHAM_CHAMELEON 1 /* animal */ +#define CHAM_DOPPELGANGER 2 /* demi-human */ +#define CHAM_SANDESTIN 3 /* demon */ +#define CHAM_MAX_INDX CHAM_SANDESTIN + Bitfield(mundetected,1); /* not seen in present hiding place */ + /* implies one of M1_CONCEAL or M1_HIDE, + * but not mimic (that is, snake, spider, + * trapper, piercer, eel) + */ + + Bitfield(mcan,1); /* has been cancelled */ + Bitfield(mburied,1); /* has been buried */ + Bitfield(mspeed,2); /* current speed */ + Bitfield(permspeed,2); /* intrinsic mspeed value */ + Bitfield(mrevived,1); /* has been revived from the dead */ + Bitfield(mavenge,1); /* did something to deserve retaliation */ + + Bitfield(mflee,1); /* fleeing */ + Bitfield(mfleetim,7); /* timeout for mflee */ + + Bitfield(mcansee,1); /* cansee 1, temp.blinded 0, blind 0 */ + Bitfield(mblinded,7); /* cansee 0, temp.blinded n, blind 0 */ + + Bitfield(mcanmove,1); /* paralysis, similar to mblinded */ + Bitfield(mfrozen,7); + + Bitfield(msleeping,1); /* asleep until woken */ + Bitfield(mstun,1); /* stunned (off balance) */ + Bitfield(mconf,1); /* confused */ + Bitfield(mpeaceful,1); /* does not attack unprovoked */ + Bitfield(mtrapped,1); /* trapped in a pit, web or bear trap */ + Bitfield(mleashed,1); /* monster is on a leash */ + Bitfield(isshk,1); /* is shopkeeper */ + Bitfield(isminion,1); /* is a minion */ + + Bitfield(isgd,1); /* is guard */ + Bitfield(ispriest,1); /* is a priest */ + Bitfield(iswiz,1); /* is the Wizard of Yendor */ + Bitfield(wormno,5); /* at most 31 worms on any level */ +#define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */ + + long mstrategy; /* for monsters with mflag3: current strategy */ +#define STRAT_ARRIVE 0x40000000L /* just arrived on current level */ +#define STRAT_WAITFORU 0x20000000L +#define STRAT_CLOSE 0x10000000L +#define STRAT_WAITMASK 0x30000000L +#define STRAT_HEAL 0x08000000L +#define STRAT_GROUND 0x04000000L +#define STRAT_MONSTR 0x02000000L +#define STRAT_PLAYER 0x01000000L +#define STRAT_NONE 0x00000000L +#define STRAT_STRATMASK 0x0f000000L +#define STRAT_XMASK 0x00ff0000L +#define STRAT_YMASK 0x0000ff00L +#define STRAT_GOAL 0x000000ffL +#define STRAT_GOALX(s) ((xchar)((s & STRAT_XMASK) >> 16)) +#define STRAT_GOALY(s) ((xchar)((s & STRAT_YMASK) >> 8)) + + long mtrapseen; /* bitmap of traps we've been trapped in */ + long mlstmv; /* for catching up with lost time */ +#ifndef GOLDOBJ + long mgold; +#endif + struct obj *minvent; + + struct obj *mw; + long misc_worn_check; + xchar weapon_check; + + uchar mnamelth; /* length of name (following mxlth) */ + short mxlth; /* length of following data */ + /* in order to prevent alignment problems mextra should + be (or follow) a long int */ + int meating; /* monster is eating timeout */ + long mextra[1]; /* monster dependent info */ +}; + +/* + * Note that mextra[] may correspond to any of a number of structures, which + * are indicated by some of the other fields. + * isgd -> struct egd + * ispriest -> struct epri + * isshk -> struct eshk + * isminion -> struct emin + * (struct epri for roaming priests and angels, which is + * compatible with emin for polymorph purposes) + * mtame -> struct edog + * (struct epri for guardian angels, which do not eat + * or do other doggy things) + * Since at most one structure can be indicated in this manner, it is not + * possible to tame any creatures using the other structures (the only + * exception being the guardian angels which are tame on creation). + */ + +#define newmonst(xl) (struct monst *)alloc((unsigned)(xl) + sizeof(struct monst)) +#define dealloc_monst(mon) free((genericptr_t)(mon)) + +/* these are in mspeed */ +#define MSLOW 1 /* slow monster */ +#define MFAST 2 /* speeded monster */ + +#define NAME(mtmp) (((char *)(mtmp)->mextra) + (mtmp)->mxlth) + +#define MON_WEP(mon) ((mon)->mw) +#define MON_NOWEP(mon) ((mon)->mw = (struct obj *)0) + +#define DEADMONSTER(mon) ((mon)->mhp < 1) + +#endif /* MONST_H */ diff --git a/include/monsym.h b/include/monsym.h new file mode 100644 index 0000000..7bf393c --- /dev/null +++ b/include/monsym.h @@ -0,0 +1,147 @@ +/* SCCS Id: @(#)monsym.h 3.4 1992/10/18 */ +/* Monster symbols and creation information rev 1.0 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MONSYM_H +#define MONSYM_H + +/* + * Monster classes. Below, are the corresponding default characters for + * them. Monster class 0 is not used or defined so we can use it as a + * NULL character. + */ +#define S_ANT 1 +#define S_BLOB 2 +#define S_COCKATRICE 3 +#define S_DOG 4 +#define S_EYE 5 +#define S_FELINE 6 +#define S_GREMLIN 7 +#define S_HUMANOID 8 +#define S_IMP 9 +#define S_JELLY 10 +#define S_KOBOLD 11 +#define S_LEPRECHAUN 12 +#define S_MIMIC 13 +#define S_NYMPH 14 +#define S_ORC 15 +#define S_PIERCER 16 +#define S_QUADRUPED 17 +#define S_RODENT 18 +#define S_SPIDER 19 +#define S_TRAPPER 20 +#define S_UNICORN 21 +#define S_VORTEX 22 +#define S_WORM 23 +#define S_XAN 24 +#define S_LIGHT 25 +#define S_ZRUTY 26 +#define S_ANGEL 27 +#define S_BAT 28 +#define S_CENTAUR 29 +#define S_DRAGON 30 +#define S_ELEMENTAL 31 +#define S_FUNGUS 32 +#define S_GNOME 33 +#define S_GIANT 34 +#define S_JABBERWOCK 36 +#define S_KOP 37 +#define S_LICH 38 +#define S_MUMMY 39 +#define S_NAGA 40 +#define S_OGRE 41 +#define S_PUDDING 42 +#define S_QUANTMECH 43 +#define S_RUSTMONST 44 +#define S_SNAKE 45 +#define S_TROLL 46 +#define S_UMBER 47 +#define S_VAMPIRE 48 +#define S_WRAITH 49 +#define S_XORN 50 +#define S_YETI 51 +#define S_ZOMBIE 52 +#define S_HUMAN 53 +#define S_GHOST 54 +#define S_GOLEM 55 +#define S_DEMON 56 +#define S_EEL 57 +#define S_LIZARD 58 + +#define S_WORM_TAIL 59 +#define S_MIMIC_DEF 60 + +#define MAXMCLASSES 61 /* number of monster classes */ + +#if 0 /* moved to decl.h so that makedefs.c won't see them */ +extern const char def_monsyms[MAXMCLASSES]; /* default class symbols */ +extern uchar monsyms[MAXMCLASSES]; /* current class symbols */ +#endif + +/* + * Default characters for monsters. These correspond to the monster classes + * above. + */ +#define DEF_ANT 'a' +#define DEF_BLOB 'b' +#define DEF_COCKATRICE 'c' +#define DEF_DOG 'd' +#define DEF_EYE 'e' +#define DEF_FELINE 'f' +#define DEF_GREMLIN 'g' +#define DEF_HUMANOID 'h' +#define DEF_IMP 'i' +#define DEF_JELLY 'j' +#define DEF_KOBOLD 'k' +#define DEF_LEPRECHAUN 'l' +#define DEF_MIMIC 'm' +#define DEF_NYMPH 'n' +#define DEF_ORC 'o' +#define DEF_PIERCER 'p' +#define DEF_QUADRUPED 'q' +#define DEF_RODENT 'r' +#define DEF_SPIDER 's' +#define DEF_TRAPPER 't' +#define DEF_UNICORN 'u' +#define DEF_VORTEX 'v' +#define DEF_WORM 'w' +#define DEF_XAN 'x' +#define DEF_LIGHT 'y' +#define DEF_ZRUTY 'z' +#define DEF_ANGEL 'A' +#define DEF_BAT 'B' +#define DEF_CENTAUR 'C' +#define DEF_DRAGON 'D' +#define DEF_ELEMENTAL 'E' +#define DEF_FUNGUS 'F' +#define DEF_GNOME 'G' +#define DEF_GIANT 'H' +#define DEF_JABBERWOCK 'J' +#define DEF_KOP 'K' +#define DEF_LICH 'L' +#define DEF_MUMMY 'M' +#define DEF_NAGA 'N' +#define DEF_OGRE 'O' +#define DEF_PUDDING 'P' +#define DEF_QUANTMECH 'Q' +#define DEF_RUSTMONST 'R' +#define DEF_SNAKE 'S' +#define DEF_TROLL 'T' +#define DEF_UMBER 'U' +#define DEF_VAMPIRE 'V' +#define DEF_WRAITH 'W' +#define DEF_XORN 'X' +#define DEF_YETI 'Y' +#define DEF_ZOMBIE 'Z' +#define DEF_HUMAN '@' +#define DEF_GHOST ' ' +#define DEF_GOLEM '\'' +#define DEF_DEMON '&' +#define DEF_EEL ';' +#define DEF_LIZARD ':' + +#define DEF_INVISIBLE 'I' +#define DEF_WORM_TAIL '~' +#define DEF_MIMIC_DEF ']' + +#endif /* MONSYM_H */ diff --git a/include/mttypriv.h b/include/mttypriv.h new file mode 100644 index 0000000..ab148cb --- /dev/null +++ b/include/mttypriv.h @@ -0,0 +1,60 @@ +/* SCCS Id: @(#)mttypriv.h 3.4 1993/03/01 */ +/* Copyright (c) Jon W{tte 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains private structures used to implement the + * tty windows - note that these structures may change between + * minor releases! + */ + +#ifndef _H_tty_private +# define _H_tty_private + +# ifndef _H_tty_public +#include "mactty.h" +# endif + +#if !TARGET_API_MAC_CARBON +# include +# include +# include +#endif + +#define TA_TO_RGB(ta,rgb) (((rgb).red=(((ta)>>16)&0xff)*257),((rgb).green=(((ta)>>8)&0xff)*257),\ + ((rgb).blue=((ta)&0xff)*257)),rgb + +typedef struct tty_record { + WindowPtr its_window ; + + short font_number ; + short font_size ; + short char_width ; + short row_height ; + short ascent_height ; + + short x_size ; + short y_size ; + short x_curs ; + short y_curs ; + + GWorldPtr its_window_world ; + BitMap its_bits ; + GrafPtr offscreen_port ; + GWorldPtr offscreen_world ; +#if CLIP_RECT_ONLY + Rect invalid_rect ; +#else + RgnHandle invalid_part ; +#endif + + long attribute [ TTY_NUMBER_ATTRIBUTES ] ; + long last_cursor ; + + Boolean was_allocated ; + Boolean curs_state ; + Boolean uses_gworld ; +} tty_record ; + + +#endif /* _H_tty_private */ diff --git a/include/nhlan.h b/include/nhlan.h new file mode 100644 index 0000000..1cb9bf1 --- /dev/null +++ b/include/nhlan.h @@ -0,0 +1,48 @@ +/* SCCS Id: @(#)nhlan.h 3.4 1997/04/12 */ +/* Copyright (c) Michael Allison, 1997 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef NHLAN_H +#define NHLAN_H +/* + * Here are the LAN features currently implemented: + * LAN_MAIL Mail facility allowing receipt and + * reading of mail. + * LAN_SHARED_BONES Allows bones files to be stored on a + * network share. (Does NOT imply compatibiliy + * between unlike platforms) + */ + +# ifdef LAN_FEATURES +# ifdef LAN_MAIL +#define MAIL +#ifndef WIN32 +#define MAILCKFREQ 50 +#else +/* + * WIN32 port does the real mail lookups in a separate thread + * and the NetHack core code really just checks a flag, + * so that part of it can be done more often. The throttle + * for how often the mail thread should contact the mail + * system is controlled by MAILTHREADFREQ and is expressed + * in milliseconds. + */ +#define MAILCKFREQ 5 +#define MAILTHREADFREQ 50000 +#endif + +#ifndef MAX_BODY_SIZE +#define MAX_BODY_SIZE 1024 +#endif + +struct lan_mail_struct { + char sender[120]; + char subject[120]; + boolean body_in_ram; /* TRUE means body in memory not file */ + char filename[_MAX_PATH]; + char body[MAX_BODY_SIZE]; +}; +# endif + +# endif /*LAN_FEATURES*/ +#endif /*NHLAN_H*/ diff --git a/include/ntconf.h b/include/ntconf.h new file mode 100644 index 0000000..dea4733 --- /dev/null +++ b/include/ntconf.h @@ -0,0 +1,201 @@ +/* SCCS Id: @(#)ntconf.h 3.4 2002/03/10 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef NTCONF_H +#define NTCONF_H + +/* #define SHELL /* nt use of pcsys routines caused a hang */ + +#define RANDOM /* have Berkeley random(3) */ +#define TEXTCOLOR /* Color text */ + +#define EXEPATH /* Allow .exe location to be used as HACKDIR */ +#define TRADITIONAL_GLYPHMAP /* Store glyph mappings at level change time */ +#ifdef WIN32CON +#define LAN_FEATURES /* Include code for lan-aware features. Untested in 3.4.0*/ +#endif + +#define PC_LOCKING /* Prevent overwrites of aborted or in-progress games */ + /* without first receiving confirmation. */ + +#define HOLD_LOCKFILE_OPEN /* Keep an exclusive lock on the .0 file */ + +#define SELF_RECOVER /* Allow the game itself to recover from an aborted game */ + +#define USER_SOUNDS +/* + * ----------------------------------------------------------------- + * The remaining code shouldn't need modification. + * ----------------------------------------------------------------- + */ +/* #define SHORT_FILENAMES /* All NT filesystems support long names now */ + +#ifdef MICRO +#undef MICRO /* never define this! */ +#endif + +#define NOCWD_ASSUMPTIONS /* Always define this. There are assumptions that + it is defined for WIN32. + Allow paths to be specified for HACKDIR, + LEVELDIR, SAVEDIR, BONESDIR, DATADIR, + SCOREDIR, LOCKDIR, CONFIGDIR, and TROUBLEDIR */ +#define NO_TERMS +#define ASCIIGRAPH + +#ifdef OPTIONS_USED +#undef OPTIONS_USED +#endif +#ifdef MSWIN_GRAPHICS +#define OPTIONS_USED "guioptions" +#else +#define OPTIONS_USED "ttyoptions" +#endif +#define OPTIONS_FILE OPTIONS_USED + +#define PORT_HELP "porthelp" + +#ifdef WIN32CON +#define PORT_DEBUG /* include ability to debug international keyboard issues */ +#endif + +/* Stuff to help the user with some common, yet significant errors */ +#define INTERJECT_PANIC 0 +#define INTERJECTION_TYPES (INTERJECT_PANIC + 1) +extern void FDECL(interject_assistance, (int,int,genericptr_t,genericptr_t)); +extern void FDECL(interject, (int)); + +/* The following is needed for prototypes of certain functions */ +#if defined(_MSC_VER) +#include /* Provides prototypes of exit(), spawn() */ +#endif + +#include /* Provides prototypes of strncmpi(), etc. */ +#ifdef STRNCMPI +#define strncmpi(a,b,c) strnicmp(a,b,c) +#endif + +#include +#include +#ifdef __BORLANDC__ +#undef randomize +#undef random +#endif + +#define PATHLEN BUFSZ /* maximum pathlength */ +#define FILENAME BUFSZ /* maximum filename length (conservative) */ + +#if defined(_MAX_PATH) && defined(_MAX_FNAME) +# if (_MAX_PATH < BUFSZ) && (_MAX_FNAME < BUFSZ) +#undef PATHLEN +#undef FILENAME +#define PATHLEN _MAX_PATH +#define FILENAME _MAX_FNAME +# endif +#endif + + +#define NO_SIGNAL +#define index strchr +#define rindex strrchr +#include +#define USE_STDARG +#ifdef RANDOM +/* Use the high quality random number routines. */ +#define Rand() random() +#else +#define Rand() rand() +#endif + +#define FCMASK 0660 /* file creation mask */ +#define regularize nt_regularize +#define HLOCK "NHPERM" + +#ifndef M +#define M(c) ((char) (0x80 | (c))) +/* #define M(c) ((c) - 128) */ +#endif + +#ifndef C +#define C(c) (0x1f & (c)) +#endif + +#if defined(DLB) +#define FILENAME_CMP stricmp /* case insensitive */ +#endif + +#if 0 +extern char levels[], bones[], permbones[], +#endif /* 0 */ + +/* this was part of the MICRO stuff in the past */ +extern const char *alllevels, *allbones; +extern char hackdir[]; +#define ABORT C('a') +#define getuid() 1 +#define getlogin() ((char *)0) +extern void NDECL(win32_abort); +#ifdef WIN32CON +extern void FDECL(nttty_preference_update, (const char *)); +extern void NDECL(toggle_mouse_support); +extern void FDECL(map_subkeyvalue, (char *)); +extern void NDECL(load_keyboard_handler); +#endif + +#include +#ifndef __BORLANDC__ +#include +#include +#else +int _RTLENTRY _EXPFUNC access (const char _FAR *__path, int __amode); +int _RTLENTRY _EXPFUNC _chdrive(int __drive); +int _RTLENTRYF _EXPFUNC32 chdir( const char _FAR *__path ); +char _FAR * _RTLENTRY _EXPFUNC getcwd( char _FAR *__buf, int __buflen ); +int _RTLENTRY _EXPFUNC write (int __handle, const void _FAR *__buf, unsigned __len); +int _RTLENTRY _EXPFUNC creat (const char _FAR *__path, int __amode); +int _RTLENTRY _EXPFUNC close (int __handle); +int _RTLENTRY _EXPFUNC _close (int __handle); +int _RTLENTRY _EXPFUNC open (const char _FAR *__path, int __access,... /*unsigned mode*/); +long _RTLENTRY _EXPFUNC lseek (int __handle, long __offset, int __fromwhere); +int _RTLENTRY _EXPFUNC read (int __handle, void _FAR *__buf, unsigned __len); +#endif +#include +#undef kbhit /* Use our special NT kbhit */ +#define kbhit (*nt_kbhit) + +#ifdef LAN_FEATURES +#define MAX_LAN_USERNAME 20 +#define LAN_RO_PLAYGROUND /* not implemented in 3.3.0 */ +#define LAN_SHARED_BONES /* not implemented in 3.3.0 */ +#include "nhlan.h" +#endif + +#ifndef alloca +#define ALLOCA_HACK /* used in util/panic.c */ +#endif + +#ifndef REDO +#undef Getchar +#define Getchar nhgetch +#endif + +#ifdef _MSC_VER +#if 0 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4305) /* init, conv from 'const int' to 'char' */ +#endif +#pragma warning(disable:4761) /* integral size mismatch in arg; conv supp*/ +#ifdef YYPREFIX +#pragma warning(disable:4102) /* unreferenced label */ +#endif +#endif + +extern int FDECL(set_win32_option, (const char *, const char *)); +#ifdef WIN32CON +#define LEFTBUTTON FROM_LEFT_1ST_BUTTON_PRESSED +#define RIGHTBUTTON RIGHTMOST_BUTTON_PRESSED +#define MIDBUTTON FROM_LEFT_2ND_BUTTON_PRESSED +#define MOUSEMASK (LEFTBUTTON | RIGHTBUTTON | MIDBUTTON) +#endif /* WIN32CON */ + +#endif /* NTCONF_H */ diff --git a/include/obj.h b/include/obj.h new file mode 100644 index 0000000..516fc35 --- /dev/null +++ b/include/obj.h @@ -0,0 +1,306 @@ +/* SCCS Id: @(#)obj.h 3.4 2002/01/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef OBJ_H +#define OBJ_H + +/* #define obj obj_nh */ /* uncomment for SCO UNIX, which has a conflicting + * typedef for "obj" in */ + +union vptrs { + struct obj *v_nexthere; /* floor location lists */ + struct obj *v_ocontainer; /* point back to container */ + struct monst *v_ocarry; /* point back to carrying monst */ +}; + +struct obj { + struct obj *nobj; + union vptrs v; +#define nexthere v.v_nexthere +#define ocontainer v.v_ocontainer +#define ocarry v.v_ocarry + + struct obj *cobj; /* contents list for containers */ + unsigned o_id; + xchar ox,oy; + short otyp; /* object class number */ + unsigned owt; + long quan; /* number of items */ + + schar spe; /* quality of weapon, armor or ring (+ or -) + number of charges for wand ( >= -1 ) + marks your eggs, spinach tins + royal coffers for a court ( == 2) + tells which fruit a fruit is + special for uball and amulet + historic and gender for statues */ +#define STATUE_HISTORIC 0x01 +#define STATUE_MALE 0x02 +#define STATUE_FEMALE 0x04 + char oclass; /* object class */ + char invlet; /* designation in inventory */ + char oartifact; /* artifact array index */ + + xchar where; /* where the object thinks it is */ +#define OBJ_FREE 0 /* object not attached to anything */ +#define OBJ_FLOOR 1 /* object on floor */ +#define OBJ_CONTAINED 2 /* object in a container */ +#define OBJ_INVENT 3 /* object in the hero's inventory */ +#define OBJ_MINVENT 4 /* object in a monster inventory */ +#define OBJ_MIGRATING 5 /* object sent off to another level */ +#define OBJ_BURIED 6 /* object buried */ +#define OBJ_ONBILL 7 /* object on shk bill */ +#define NOBJ_STATES 8 + xchar timed; /* # of fuses (timers) attached to this obj */ + + Bitfield(cursed,1); + Bitfield(blessed,1); + Bitfield(unpaid,1); /* on some bill */ + Bitfield(no_charge,1); /* if shk shouldn't charge for this */ + Bitfield(known,1); /* exact nature known */ + Bitfield(dknown,1); /* color or text known */ + Bitfield(bknown,1); /* blessing or curse known */ + Bitfield(rknown,1); /* rustproof or not known */ + + Bitfield(oeroded,2); /* rusted/burnt weapon/armor */ + Bitfield(oeroded2,2); /* corroded/rotted weapon/armor */ +#define greatest_erosion(otmp) (int)((otmp)->oeroded > (otmp)->oeroded2 ? (otmp)->oeroded : (otmp)->oeroded2) +#define MAX_ERODE 3 +#define orotten oeroded /* rotten food */ +#define odiluted oeroded /* diluted potions */ +#define norevive oeroded2 + Bitfield(oerodeproof,1); /* erodeproof weapon/armor */ + Bitfield(olocked,1); /* object is locked */ + Bitfield(obroken,1); /* lock has been broken */ + Bitfield(otrapped,1); /* container is trapped */ + /* or accidental tripped rolling boulder trap */ +#define opoisoned otrapped /* object (weapon) is coated with poison */ + + Bitfield(recharged,3); /* number of times it's been recharged */ + Bitfield(lamplit,1); /* a light-source -- can be lit */ +#ifdef INVISIBLE_OBJECTS + Bitfield(oinvis,1); /* invisible */ +#endif + Bitfield(greased,1); /* covered with grease */ + Bitfield(oattached,2); /* obj struct has special attachment */ +#define OATTACHED_NOTHING 0 +#define OATTACHED_MONST 1 /* monst struct in oextra */ +#define OATTACHED_M_ID 2 /* monst id in oextra */ +#define OATTACHED_UNUSED3 3 + + Bitfield(in_use,1); /* for magic items before useup items */ + Bitfield(bypass,1); /* mark this as an object to be skipped by bhito() */ + /* 6 free bits */ + + int corpsenm; /* type of corpse is mons[corpsenm] */ +#define leashmon corpsenm /* gets m_id of attached pet */ +#define spestudied corpsenm /* # of times a spellbook has been studied */ +#define fromsink corpsenm /* a potion from a sink */ + unsigned oeaten; /* nutrition left in food, if partly eaten */ + long age; /* creation date */ + + uchar onamelth; /* length of name (following oxlth) */ + short oxlth; /* length of following data */ + /* in order to prevent alignment problems oextra should + be (or follow) a long int */ + long owornmask; + long oextra[1]; /* used for name of ordinary objects - length + is flexible; amount for tmp gold objects */ +}; + +#define newobj(xl) (struct obj *)alloc((unsigned)(xl) + sizeof(struct obj)) +#define ONAME(otmp) (((char *)(otmp)->oextra) + (otmp)->oxlth) + +/* Weapons and weapon-tools */ +/* KMH -- now based on skill categories. Formerly: + * #define is_sword(otmp) (otmp->oclass == WEAPON_CLASS && \ + * objects[otmp->otyp].oc_wepcat == WEP_SWORD) + * #define is_blade(otmp) (otmp->oclass == WEAPON_CLASS && \ + * (objects[otmp->otyp].oc_wepcat == WEP_BLADE || \ + * objects[otmp->otyp].oc_wepcat == WEP_SWORD)) + * #define is_weptool(o) ((o)->oclass == TOOL_CLASS && \ + * objects[(o)->otyp].oc_weptool) + * #define is_multigen(otyp) (otyp <= SHURIKEN) + * #define is_poisonable(otyp) (otyp <= BEC_DE_CORBIN) + */ +#define is_blade(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= P_DAGGER && \ + objects[otmp->otyp].oc_skill <= P_SABER) +#define is_axe(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == TOOL_CLASS) && \ + objects[otmp->otyp].oc_skill == P_AXE) +#define is_pick(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == TOOL_CLASS) && \ + objects[otmp->otyp].oc_skill == P_PICK_AXE) +#define is_sword(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= P_SHORT_SWORD && \ + objects[otmp->otyp].oc_skill <= P_SABER) +#define is_pole(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == TOOL_CLASS) && \ + (objects[otmp->otyp].oc_skill == P_POLEARMS || \ + objects[otmp->otyp].oc_skill == P_LANCE)) +#define is_spear(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= P_SPEAR && \ + objects[otmp->otyp].oc_skill <= P_JAVELIN) +#define is_launcher(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= P_BOW && \ + objects[otmp->otyp].oc_skill <= P_CROSSBOW) +#define is_ammo(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == GEM_CLASS) && \ + objects[otmp->otyp].oc_skill >= -P_CROSSBOW && \ + objects[otmp->otyp].oc_skill <= -P_BOW) +#define ammo_and_launcher(otmp,ltmp) \ + (is_ammo(otmp) && (ltmp) && \ + objects[(otmp)->otyp].oc_skill == -objects[(ltmp)->otyp].oc_skill) +#define is_missile(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == TOOL_CLASS) && \ + objects[otmp->otyp].oc_skill >= -P_BOOMERANG && \ + objects[otmp->otyp].oc_skill <= -P_DART) +#define is_weptool(o) ((o)->oclass == TOOL_CLASS && \ + objects[(o)->otyp].oc_skill != P_NONE) +#define bimanual(otmp) ((otmp->oclass == WEAPON_CLASS || \ + otmp->oclass == TOOL_CLASS) && \ + objects[otmp->otyp].oc_bimanual) +#define is_multigen(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= -P_SHURIKEN && \ + objects[otmp->otyp].oc_skill <= -P_BOW) +#define is_poisonable(otmp) (otmp->oclass == WEAPON_CLASS && \ + objects[otmp->otyp].oc_skill >= -P_SHURIKEN && \ + objects[otmp->otyp].oc_skill <= -P_BOW) +#define uslinging() (uwep && objects[uwep->otyp].oc_skill == P_SLING) + +/* Armor */ +#define is_shield(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_SHIELD) +#define is_helmet(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_HELM) +#define is_boots(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_BOOTS) +#define is_gloves(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_GLOVES) +#define is_cloak(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_CLOAK) +#define is_shirt(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_SHIRT) +#define is_suit(otmp) (otmp->oclass == ARMOR_CLASS && \ + objects[otmp->otyp].oc_armcat == ARM_SUIT) +#define is_elven_armor(otmp) ((otmp)->otyp == ELVEN_LEATHER_HELM\ + || (otmp)->otyp == ELVEN_MITHRIL_COAT\ + || (otmp)->otyp == ELVEN_CLOAK\ + || (otmp)->otyp == ELVEN_SHIELD\ + || (otmp)->otyp == ELVEN_BOOTS) +#define is_orcish_armor(otmp) ((otmp)->otyp == ORCISH_HELM\ + || (otmp)->otyp == ORCISH_CHAIN_MAIL\ + || (otmp)->otyp == ORCISH_RING_MAIL\ + || (otmp)->otyp == ORCISH_CLOAK\ + || (otmp)->otyp == URUK_HAI_SHIELD\ + || (otmp)->otyp == ORCISH_SHIELD) +#define is_dwarvish_armor(otmp) ((otmp)->otyp == DWARVISH_IRON_HELM\ + || (otmp)->otyp == DWARVISH_MITHRIL_COAT\ + || (otmp)->otyp == DWARVISH_CLOAK\ + || (otmp)->otyp == DWARVISH_ROUNDSHIELD) +#define is_gnomish_armor(otmp) (FALSE) + + +/* Eggs and other food */ +#define MAX_EGG_HATCH_TIME 200 /* longest an egg can remain unhatched */ +#define stale_egg(egg) ((monstermoves - (egg)->age) > (2*MAX_EGG_HATCH_TIME)) +#define ofood(o) ((o)->otyp == CORPSE || (o)->otyp == EGG || (o)->otyp == TIN) +#define polyfodder(obj) (ofood(obj) && \ + pm_to_cham((obj)->corpsenm) != CHAM_ORDINARY) +#define mlevelgain(obj) (ofood(obj) && (obj)->corpsenm == PM_WRAITH) +#define mhealup(obj) (ofood(obj) && (obj)->corpsenm == PM_NURSE) + +/* Containers */ +#define carried(o) ((o)->where == OBJ_INVENT) +#define mcarried(o) ((o)->where == OBJ_MINVENT) +#define Has_contents(o) (/* (Is_container(o) || (o)->otyp == STATUE) && */ \ + (o)->cobj != (struct obj *)0) +#define Is_container(o) ((o)->otyp >= LARGE_BOX && (o)->otyp <= BAG_OF_TRICKS) +#define Is_box(otmp) (otmp->otyp == LARGE_BOX || otmp->otyp == CHEST) +#define Is_mbag(otmp) (otmp->otyp == BAG_OF_HOLDING || \ + otmp->otyp == BAG_OF_TRICKS) + +/* dragon gear */ +#define Is_dragon_scales(obj) ((obj)->otyp >= GRAY_DRAGON_SCALES && \ + (obj)->otyp <= YELLOW_DRAGON_SCALES) +#define Is_dragon_mail(obj) ((obj)->otyp >= GRAY_DRAGON_SCALE_MAIL && \ + (obj)->otyp <= YELLOW_DRAGON_SCALE_MAIL) +#define Is_dragon_armor(obj) (Is_dragon_scales(obj) || Is_dragon_mail(obj)) +#define Dragon_scales_to_pm(obj) &mons[PM_GRAY_DRAGON + (obj)->otyp \ + - GRAY_DRAGON_SCALES] +#define Dragon_mail_to_pm(obj) &mons[PM_GRAY_DRAGON + (obj)->otyp \ + - GRAY_DRAGON_SCALE_MAIL] +#define Dragon_to_scales(pm) (GRAY_DRAGON_SCALES + (pm - mons)) + +/* Elven gear */ +#define is_elven_weapon(otmp) ((otmp)->otyp == ELVEN_ARROW\ + || (otmp)->otyp == ELVEN_SPEAR\ + || (otmp)->otyp == ELVEN_DAGGER\ + || (otmp)->otyp == ELVEN_SHORT_SWORD\ + || (otmp)->otyp == ELVEN_BROADSWORD\ + || (otmp)->otyp == ELVEN_BOW) +#define is_elven_obj(otmp) (is_elven_armor(otmp) || is_elven_weapon(otmp)) + +/* Orcish gear */ +#define is_orcish_obj(otmp) (is_orcish_armor(otmp)\ + || (otmp)->otyp == ORCISH_ARROW\ + || (otmp)->otyp == ORCISH_SPEAR\ + || (otmp)->otyp == ORCISH_DAGGER\ + || (otmp)->otyp == ORCISH_SHORT_SWORD\ + || (otmp)->otyp == ORCISH_BOW) + +/* Dwarvish gear */ +#define is_dwarvish_obj(otmp) (is_dwarvish_armor(otmp)\ + || (otmp)->otyp == DWARVISH_SPEAR\ + || (otmp)->otyp == DWARVISH_SHORT_SWORD\ + || (otmp)->otyp == DWARVISH_MATTOCK) + +/* Gnomish gear */ +#define is_gnomish_obj(otmp) (is_gnomish_armor(otmp)) + +/* Light sources */ +#define Is_candle(otmp) (otmp->otyp == TALLOW_CANDLE || \ + otmp->otyp == WAX_CANDLE) +#define MAX_OIL_IN_FLASK 400 /* maximum amount of oil in a potion of oil */ + +/* MAGIC_LAMP intentionally excluded below */ +/* age field of this is relative age rather than absolute */ +#define age_is_relative(otmp) ((otmp)->otyp == BRASS_LANTERN\ + || (otmp)->otyp == OIL_LAMP\ + || (otmp)->otyp == CANDELABRUM_OF_INVOCATION\ + || (otmp)->otyp == TALLOW_CANDLE\ + || (otmp)->otyp == WAX_CANDLE\ + || (otmp)->otyp == POT_OIL) +/* object can be ignited */ +#define ignitable(otmp) ((otmp)->otyp == BRASS_LANTERN\ + || (otmp)->otyp == OIL_LAMP\ + || (otmp)->otyp == CANDELABRUM_OF_INVOCATION\ + || (otmp)->otyp == TALLOW_CANDLE\ + || (otmp)->otyp == WAX_CANDLE\ + || (otmp)->otyp == POT_OIL) + +/* special stones */ +#define is_graystone(obj) ((obj)->otyp == LUCKSTONE || \ + (obj)->otyp == LOADSTONE || \ + (obj)->otyp == FLINT || \ + (obj)->otyp == TOUCHSTONE) + +/* misc */ +#ifdef KOPS +#define is_flimsy(otmp) (objects[(otmp)->otyp].oc_material <= LEATHER || \ + (otmp)->otyp == RUBBER_HOSE) +#else +#define is_flimsy(otmp) (objects[(otmp)->otyp].oc_material <= LEATHER) +#endif + +/* helpers, simple enough to be macros */ +#define is_plural(o) ((o)->quan > 1 || \ + (o)->oartifact == ART_EYES_OF_THE_OVERWORLD) + +/* Flags for get_obj_location(). */ +#define CONTAINED_TOO 0x1 +#define BURIED_TOO 0x2 + +#endif /* OBJ_H */ diff --git a/include/objclass.h b/include/objclass.h new file mode 100644 index 0000000..0af21a4 --- /dev/null +++ b/include/objclass.h @@ -0,0 +1,186 @@ +/* SCCS Id: @(#)objclass.h 3.4 1996/06/16 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef OBJCLASS_H +#define OBJCLASS_H + +/* definition of a class of objects */ + +struct objclass { + short oc_name_idx; /* index of actual name */ + short oc_descr_idx; /* description when name unknown */ + char * oc_uname; /* called by user */ + Bitfield(oc_name_known,1); + Bitfield(oc_merge,1); /* merge otherwise equal objects */ + Bitfield(oc_uses_known,1); /* obj->known affects full decription */ + /* otherwise, obj->dknown and obj->bknown */ + /* tell all, and obj->known should always */ + /* be set for proper merging behavior */ + Bitfield(oc_pre_discovered,1); /* Already known at start of game; */ + /* won't be listed as a discovery. */ + Bitfield(oc_magic,1); /* inherently magical object */ + Bitfield(oc_charged,1); /* may have +n or (n) charges */ + Bitfield(oc_unique,1); /* special one-of-a-kind object */ + Bitfield(oc_nowish,1); /* cannot wish for this object */ + + Bitfield(oc_big,1); +#define oc_bimanual oc_big /* for weapons & tools used as weapons */ +#define oc_bulky oc_big /* for armor */ + Bitfield(oc_tough,1); /* hard gems/rings */ + + Bitfield(oc_dir,2); +#define NODIR 1 /* for wands/spells: non-directional */ +#define IMMEDIATE 2 /* directional */ +#define RAY 3 /* zap beams */ + +#define PIERCE 1 /* for weapons & tools used as weapons */ +#define SLASH 2 /* (latter includes iron ball & chain) */ +#define WHACK 0 + + /*Bitfield(oc_subtyp,3);*/ /* Now too big for a bitfield... see below */ + + Bitfield(oc_material,5); +#define LIQUID 1 /* currently only for venom */ +#define WAX 2 +#define VEGGY 3 /* foodstuffs */ +#define FLESH 4 /* ditto */ +#define PAPER 5 +#define CLOTH 6 +#define LEATHER 7 +#define WOOD 8 +#define BONE 9 +#define DRAGON_HIDE 10 /* not leather! */ +#define IRON 11 /* Fe - includes steel */ +#define METAL 12 /* Sn, &c. */ +#define COPPER 13 /* Cu - includes brass */ +#define SILVER 14 /* Ag */ +#define GOLD 15 /* Au */ +#define PLATINUM 16 /* Pt */ +#define MITHRIL 17 +#define PLASTIC 18 +#define GLASS 19 +#define GEMSTONE 20 +#define MINERAL 21 + +#define is_organic(otmp) (objects[otmp->otyp].oc_material <= WOOD) +#define is_metallic(otmp) (objects[otmp->otyp].oc_material >= IRON && \ + objects[otmp->otyp].oc_material <= MITHRIL) + +/* primary damage: fire/rust/--- */ +/* is_flammable(otmp), is_rottable(otmp) in mkobj.c */ +#define is_rustprone(otmp) (objects[otmp->otyp].oc_material == IRON) + +/* secondary damage: rot/acid/acid */ +#define is_corrodeable(otmp) (objects[otmp->otyp].oc_material == COPPER || objects[otmp->otyp].oc_material == IRON) + +#define is_damageable(otmp) (is_rustprone(otmp) || is_flammable(otmp) || \ + is_rottable(otmp) || is_corrodeable(otmp)) + + schar oc_subtyp; +#define oc_skill oc_subtyp /* Skills of weapons, spellbooks, tools, gems */ +#define oc_armcat oc_subtyp /* for armor */ +#define ARM_SHIELD 1 /* needed for special wear function */ +#define ARM_HELM 2 +#define ARM_GLOVES 3 +#define ARM_BOOTS 4 +#define ARM_CLOAK 5 +#define ARM_SHIRT 6 +#define ARM_SUIT 0 + + uchar oc_oprop; /* property (invis, &c.) conveyed */ + char oc_class; /* object class */ + schar oc_delay; /* delay when using such an object */ + uchar oc_color; /* color of the object */ + + short oc_prob; /* probability, used in mkobj() */ + unsigned short oc_weight; /* encumbrance (1 cn = 0.1 lb.) */ + short oc_cost; /* base cost in shops */ +/* Check the AD&D rules! The FIRST is small monster damage. */ +/* for weapons, and tools, rocks, and gems useful as weapons */ + schar oc_wsdam, oc_wldam; /* max small/large monster damage */ + schar oc_oc1, oc_oc2; +#define oc_hitbon oc_oc1 /* weapons: "to hit" bonus */ + +#define a_ac oc_oc1 /* armor class, used in ARM_BONUS in do.c */ +#define a_can oc_oc2 /* armor: used in mhitu.c */ +#define oc_level oc_oc2 /* books: spell level */ + + unsigned short oc_nutrition; /* food value */ +}; + +struct objdescr { + const char *oc_name; /* actual name */ + const char *oc_descr; /* description when name unknown */ +}; + +extern NEARDATA struct objclass objects[]; +extern NEARDATA struct objdescr obj_descr[]; + +/* + * All objects have a class. Make sure that all classes have a corresponding + * symbol below. + */ +#define RANDOM_CLASS 0 /* used for generating random objects */ +#define ILLOBJ_CLASS 1 +#define WEAPON_CLASS 2 +#define ARMOR_CLASS 3 +#define RING_CLASS 4 +#define AMULET_CLASS 5 +#define TOOL_CLASS 6 +#define FOOD_CLASS 7 +#define POTION_CLASS 8 +#define SCROLL_CLASS 9 +#define SPBOOK_CLASS 10 /* actually SPELL-book */ +#define WAND_CLASS 11 +#define COIN_CLASS 12 +#define GEM_CLASS 13 +#define ROCK_CLASS 14 +#define BALL_CLASS 15 +#define CHAIN_CLASS 16 +#define VENOM_CLASS 17 +#define MAXOCLASSES 18 + +#define ALLOW_COUNT (MAXOCLASSES+1) /* Can be used in the object class */ +#define ALL_CLASSES (MAXOCLASSES+2) /* input to getobj(). */ +#define ALLOW_NONE (MAXOCLASSES+3) /* */ + +#define BURNING_OIL (MAXOCLASSES+1) /* Can be used as input to explode. */ +#define MON_EXPLODE (MAXOCLASSES+2) /* Exploding monster (e.g. gas spore) */ + +#if 0 /* moved to decl.h so that makedefs.c won't see them */ +extern const char def_oc_syms[MAXOCLASSES]; /* default class symbols */ +extern uchar oc_syms[MAXOCLASSES]; /* current class symbols */ +#endif + +/* Default definitions of all object-symbols (must match classes above). */ + +#define ILLOBJ_SYM ']' /* also used for mimics */ +#define WEAPON_SYM ')' +#define ARMOR_SYM '[' +#define RING_SYM '=' +#define AMULET_SYM '"' +#define TOOL_SYM '(' +#define FOOD_SYM '%' +#define POTION_SYM '!' +#define SCROLL_SYM '?' +#define SPBOOK_SYM '+' +#define WAND_SYM '/' +#define GOLD_SYM '$' +#define GEM_SYM '*' +#define ROCK_SYM '`' +#define BALL_SYM '0' +#define CHAIN_SYM '_' +#define VENOM_SYM '.' + +struct fruit { + char fname[PL_FSIZ]; + int fid; + struct fruit *nextf; +}; +#define newfruit() (struct fruit *)alloc(sizeof(struct fruit)) +#define dealloc_fruit(rind) free((genericptr_t) (rind)) + +#define OBJ_NAME(obj) (obj_descr[(obj).oc_name_idx].oc_name) +#define OBJ_DESCR(obj) (obj_descr[(obj).oc_descr_idx].oc_descr) +#endif /* OBJCLASS_H */ diff --git a/include/os2conf.h b/include/os2conf.h new file mode 100644 index 0000000..b706df1 --- /dev/null +++ b/include/os2conf.h @@ -0,0 +1,107 @@ +/* SCCS Id: @(#)os2conf.h 3.4 1996/10/29 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* Copyright (c) Timo Hakulinen, 1990, 1991, 1992, 1993, 1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef OS2 +#ifndef OS2CONF_H +#define OS2CONF_H + +/* + * Compiler configuration. Compiler may be + * selected either here or in Makefile.os2. + */ + +/* #define OS2_MSC /* Microsoft C 5.1 and 6.0 */ +#define OS2_GCC /* GCC emx 0.8f */ +/* #define OS2_CSET2 /* IBM C Set/2 (courtesy Jeff Urlwin) */ +/* #define OS2_CSET2_VER_1 /* CSet/2 version selection */ +/* #define OS2_CSET2_VER_2 /* - " - */ + +/* + * System configuration. + */ + +#define OS2_USESYSHEADERS /* use compiler's own system headers */ +/* #define OS2_HPFS /* use OS/2 High Performance File System */ + +#if defined(OS2_GCC) || defined(OS2_CSET2) +# define OS2_32BITAPI /* enable for compilation in OS/2 2.0 */ +#endif + +/* + * Other configurable options. Generally no + * reason to touch the defaults, I think. + */ + +/*#define MFLOPPY /* floppy and ramdisk support */ +#define RANDOM /* Berkeley random(3) */ +#define SHELL /* shell escape */ +/* #define TERMLIB /* use termcap file */ +#define ANSI_DEFAULT /* allows NetHack to run without termcap file */ +#define TEXTCOLOR /* allow color */ + +/* + * The remaining code shouldn't need modification. + */ + +#ifdef MSDOS +# undef MSDOS /* MSC autodefines this but we don't want it */ +#endif + +#ifndef MICRO +# define MICRO /* must be defined to allow some inclusions */ +#endif + +#if !defined(TERMLIB) && !defined(ANSI_DEFAULT) +# define ANSI_DEFAULT /* have to have one or the other */ +#endif + +#define PATHLEN 260 /* maximum pathlength (HPFS) */ +#define FILENAME 260 /* maximum filename length (HPFS) */ +#ifndef MICRO_H +#include "micro.h" /* necessary externs for [os_name].c */ +#endif + +#ifndef SYSTEM_H +#include "system.h" +#endif + +#ifndef index +#define index strchr +#endif +#ifndef rindex +#define rindex strrchr +#endif + +#include + +/* the high quality random number routines */ + +#ifdef RANDOM +# define Rand() random() +#else +# define Rand() rand() +#endif + +/* file creation mask */ + +#include +#include + +#define FCMASK (S_IREAD | S_IWRITE) + +#include + +#ifdef __EMX__ +#include +#endif + +#ifndef REDO +# undef Getchar +# define Getchar nhgetch +#endif + +void hangup(int i); +#endif /* OS2CONF_H */ +#endif /* OS2 */ diff --git a/include/patchlevel.h b/include/patchlevel.h new file mode 100644 index 0000000..5704523 --- /dev/null +++ b/include/patchlevel.h @@ -0,0 +1,404 @@ +/* SCCS Id: @(#)patchlevel.h 3.4 2003/12/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* NetHack 3.4.3 */ +#define VERSION_MAJOR 3 +#define VERSION_MINOR 4 +/* + * PATCHLEVEL is updated for each release. + */ +#define PATCHLEVEL 3 +/* + * Incrementing EDITLEVEL can be used to force invalidation of old bones + * and save files. + */ +#define EDITLEVEL 0 + +#define COPYRIGHT_BANNER_A \ +"NetHack, Copyright 1985-2003" + +#define COPYRIGHT_BANNER_B \ +" By Stichting Mathematisch Centrum and M. Stephenson." + +#define COPYRIGHT_BANNER_C \ +" See license for details." + +/* + * If two or more successive releases have compatible data files, define + * this with the version number of the oldest such release so that the + * new release will accept old save and bones files. The format is + * 0xMMmmPPeeL + * 0x = literal prefix "0x", MM = major version, mm = minor version, + * PP = patch level, ee = edit level, L = literal suffix "L", + * with all four numbers specified as two hexadecimal digits. + */ +#define VERSION_COMPATIBILITY 0x03040000L /* 3.4.0-0 */ + + +/*****************************************************************************/ +/* Version 3.4.x */ + +/* Patch 3, December 7, 2003 + * Several dozen general bug fixes including at least one fatal bug + * Correct several inconsistencies + * Handle level completely filled with monsters better + * Performance enhancements for win32tty port on Windows 98 and Me + * win32gui player selection fixes + * X11 player selection fixes, one of which could be fatal + * Eliminated a gold-in-shop-container cheat + * Include bones file version compatibility info in options file + */ + +/* Patch 2, August 30, 2003 + * Fix a fatal bug that caused a crash when applying figurine, candle, or + * bell that gets used up + * Fix a fatal bug that triggered a panic when your secondary weapon was + * cursed during bones file creation + * Several dozen general bug fixes + * Fixed some Gnome compilation problems on Redhat 7.2 and 8.0 + * Fixed a problem in the util Makefile + * Use random() by default under linux instead of lrand48() + * win32 tty adjustments and support for loading alternative key handlers + */ + +/* Patch 1, February 22, 2003 + * Fix a few fatal errors including one for reentering shops, one + * involving land mines and boulders/statues, one for delayed + * polymorph, and one from a chest trap exploding ball and chain + * Fix a buffer overflow that could lead to security problems + * Hundreds of general bug fixes + * Several message and other glitches corrected + * Travel command adjustments and ability to disable travel command + * message recall window extensions (by Christian Cooper) + * win32: some interface improvements + * unix: improved tile support + * gnome: some fixes, and some enhancements by Dylan Alex Simon + * winCE: Windows CE port included (by Alex Kompel) + */ + +/* + * NetHack 3.4.0, March 20, 2002 + * + * Hundreds of general bug fixes including some for sliming, zapping, conduct, + * and several more for riding + * Eliminated a few potentially fatal bugs including one for stone-to-flesh, + * trouble-fixing during prayer, riding down stairs while punished, + * polyd player demon summoning, throwing digging tools into shops, and + * a couple from having the vision system enabled at inappropriate times + * Corrected some incorrect calculations in final scoring + * Enhanced config file processing and alert to duplication of entries + * Player selection prompt enhancements for TTY and X11 + * Objects merge in containers + * Wish for "nothing", and genocide "none" to preserve your conduct + * Changes to Wizard quest + * Added the travel command which works by mouse click or '_' command + * Config file BOULDER option to specify the symbol for displaying boulders + * Incorporate modified versions of several 3.3.1 patches that have been + * in circulation in the NetHack community + * New Gnomish Mines levels (courtesy Kelly Bailey) + * Mac: command-key shortcuts in the player selection dialog + * Amiga: screenmode requester, and several amiga specific bug fixes + * Win32 graphical port contributed by Alex Kompel is now included + */ + +/* Version 3.4 */ + +/*****************************************************************************/ +/* Version 3.3.x */ + +/* Patch 1, August 9, 2000 + * Many, many general fixes, including a number for riding, twoweapon, + * and invisible monsters + * A security fix for a couple of potentially exploitable buffer overflows + * in previous versions + * Redo Ranger quest + * Introduction of differentiation between different causes of blindness + * Overhaul of warning + * Functionality restored to Amiga (courtesy Janne Salmijarvi) and Atari + * (courtesy Christian "Marvin" Bressler) ports + * Mac: multiple interface fixes + * win32: fixed bug that caused messages to stop displaying after escape + * tty: use ANSI color (AF) over standard color (Sf) when given the choice + * several ports: offer for player selection only choices consistent with + * those already made by config file/command line (e.g., only offer roles + * that are compatible with specified race) + */ + +/* + * NetHack 3.3.0, December 10, 1999 + * + * Implement the differentiation of character class or role from the + * character race. + * Removal of the Elf class, in preference to the Elf as a race. + * Introduction of Dwarves, Elves, Gnomes and Orcs as distinct races in + * addition to the Human "norm". + * Addition of the Monk and Ranger classes. + * Integrate some of the features of several branch versions of the game, + * notably NetHack--, NHplus, SLASH, and Slash'em. + * Adopt "the wizard patch" spellcasting system. + * Support for the Qt widget set. + * Y2K fix: use 4 digit year values for the dates in the score file + * updated COPYRIGHT_BANNER_A to reflect year of release. + * Dozens of other bug fixes, and minor improvements. + */ + +/* Version 3.3 */ + +/*****************************************************************************/ +/* Version 3.2.x */ + +/* Patch 3, December 10, 1999 + * Released simultaneously with 3.3.0 for the benefit of + * ports and platforms that were unable to get working + * versions of 3.3.0 ready prior to the year 2000. It + * consisted of just a few bug fixes and offered no new + * functionality changes over 3.2.2. + * + * Y2K fix: use 4 digit year values for the dates in the score file + * updated COPYRIGHT_BANNER_A to reflect year of release + * Fatal Mac bug removed + * DOS Makefile problem removed + * several bugs that could potentially trigger crashes removed + */ + +/* Patch 2, December 10, 1996 + * fix the `recover' utility + * fix priest/minion name overflow which could cause Astral Plane crashes + * avoid crash when hit by own thrown boomerang + * " " " worn blindfold pushed off by applying cursed towel + * handle returning live Wizard correctly in deep dungeon levels + * don't occasionally display unseen areas of new levels during level change + * other minor display fixes + * fix several minor reason for death inconsistencies and shop bugs + * high dexterity doesn't guarantee that thrown eggs & potions will hit + * + * Selected platform- or configuration-specific changes: + * Mac: update `mrecover' + * MSDOS: don't switch into tiles mode when resuming play on rogue level + * tty: support object class characters for 'I' command in menu mode + * Unix: work around several compilation problems + * X11: as tty above, plus implement tty-style count handling in menus; + * better window placement support for old window managers + */ + +/* Patch 1, May 28, 1996 + * eliminate `#qualifications'; fix weapon proficiency handling for missiles + * keep Medusa from continuing to move after she's been killed by reflection + * of her own gaze (fixes relmon panic) + * make monsters a little smarter; assorted eating and chatting changes + * fix object amnesia for spellbooks; fix Nazgul's sleep gas attack + * fix bullwhip usage for case of having recently been in a trap + * egg hatching fixes, oil potion fixes, magic marker fixes + * support object class chars as selection accelerators for some menus + * stricter parsing of run-time options at startup time + * interactive setting of options via menu (courtesy Per Liboriussen) + * + * Selected platform- or configuration-specific changes: + * Amiga: fix panic for tiles display in Gnomish mines + * BeOS: preliminary support for new BeBox platform; initially tty only + * DLB: avoid excessive fseek calls (major performance hit for MSDOS) + * HPUX: workaround for gcc-2.6.3 bug adversely affecting monster generation + * Mac: avoid MW 68K struct copy optimization bug which caused crashes; + * fix dragging of scrollbar; boost partitions to 2MB minimum + * MSDOS: wasn't safe to enter endgame for MFLOPPY configuration; + * fix re-entry into game after "!" (shell escape) + chdir + EXIT; + * F3/F4/F5 display interface swapping improvements; + * add support for preloading all tiles in protected mode environment + * TERMINFO: colors were wrong for some systems, such as Linux + * X11: display help files properly + */ + +/* + * NetHack 3.2.0, April 11, 1996 + * enhancements to the windowing systems including "tiles" or icons to + * visually represent monsters and objects (courtesy Warwick Allison) + * window based menu system introduced for inventory and selection + * moving light sources besides the player + * improved #untrap (courtesy Helge Hafting) + * spellcasting logic changes to balance spellcasting towards magic-using + * classes (courtesy Stephen White) + * many, many bug fixes and abuse eliminations + */ + +/* Version 3.2 */ + +/*****************************************************************************/ +/* Version 3.1.x */ + +/* + * Patch 3, July 12, 1993 + * further revise Mac windowing and extend to Think C (courtesy + * Barton House) + * fix confusing black/gray/white display on some MSDOS hardware + * remove fatal bugs dealing with horns of plenty and VMS bones levels, + * as well as more minor ones + */ + +/* + * Patch 2, June 1, 1993 + * add tty windowing to Mac and Amiga ports and revise native windowing + * allow direct screen I/O for MS-DOS versions instead of going through + * termcap routines (courtesy Michael Allison and Kevin Smolkowski) + * changes for NEC PC-9800 and various termcap.zip fixes by Yamamoto Keizo + * SYSV 386 music driver ported to 386BSD (courtesy Andrew Chernov) and + * SCO UNIX (courtesy Andreas Arens) + * enhanced pickup and disclosure options + * removed fatal bugs dealing with cursed bags of holding, renaming + * shopkeepers, objects falling through trapdoors on deep levels, + * and kicking embedded objects loose, and many more minor ones + */ + +/* + * Patch 1, February 25, 1993 + * add Windows NT console port (courtesy Michael Allison) + * polishing of Amiga, Mac, and X11 windowing + * fixing many small bugs, including the infamous 3.0 nurse relmon bug + */ + +/* + * NetHack 3.1.0, January 25, 1993 + * many, many changes and bugfixes -- some of the highlights include: + * display rewrite using line-of-sight vision + * general window interface, with the ability to use multiple interfaces + * in the same executable + * intelligent monsters + * enhanced dungeon mythology + * branching dungeons with more special levels, quest dungeons, and + * multi-level endgame + * more artifacts and more uses for artifacts + * generalization to multiple shops with damage repair + * X11 interface + * ability to recover crashed games + * full rewrite of Macintosh port + * Amiga splitter + * directory rearrangement (dat, doc, sys, win, util) + */ + +/* Version 3.1 */ + +/*****************************************************************************/ +/* Version 3.0 */ + +/* + * Patch 10, February 5, 1991 + * extend overlay manager to multiple files for easier binary distribution + * allow for more system and compiler variance + * remove more small insects + */ + +/* + * Patch 9, June 26, 1990 + * clear up some confusing documentation + * smooth some more rough edges in various ports + * and fix a couple more bugs + */ + +/* + * Patch 8, June 3, 1990 + * further debug and refine Macintosh port + * refine the overlay manager, rearrange the OVLx breakdown for better + * efficiency, rename the overlay macros, and split off the overlay + * instructions to Install.ovl + * introduce NEARDATA for better Amiga efficiency + * support for more VMS versions (courtesy Joshua Delahunty and Pat Rankin) + * more const fixes + * better support for common graphics (DEC VT and IBM) + * and a number of simple fixes and consistency extensions + */ + +/* + * Patch 7, February 19, 1990 + * refine overlay support to handle portions of .c files through OVLx + * (courtesy above plus Kevin Smolkowski) + * update and extend Amiga port and documentation (courtesy Richard Addison, + * Jochen Erwied, Mark Gooderum, Ken Lorber, Greg Olson, Mike Passaretti, + * and Gregg Wonderly) + * refine and extend Macintosh port and documentation (courtesy Johnny Lee, + * Kevin Sitze, Michael Sokolov, Andy Swanson, Jon Watte, and Tom West) + * refine VMS documentation + * continuing ANSIfication, this time of const usage + * teach '/' about differences within monster classes + * smarter eating code (yet again), death messages, and treatment of + * non-animal monsters, monster unconsciousness, and naming + * extended version command to give compilation options + * and the usual bug fixes and hole plugs + */ + +/* + * Patch 6, November 19, 1989 + * add overlay support for MS-DOS (courtesy Pierre Martineau, Stephen + * Spackman, and Norm Meluch) + * refine Macintosh port + * different door states show as different symbols (courtesy Ari Huttunen) + * smarter drawbridges (courtesy Kevin Darcy) + * add CLIPPING and split INFERNO off HARD + * further refine eating code wrt picking up and resumption + * make first few levels easier, by adding :x monsters and increasing initial + * attribute points and hitting probability + * teach '/' about configurable symbols + */ + +/* + * Patch 5, October 15, 1989 + * add support for Macintosh OS (courtesy Johnny Lee) + * fix annoying dependency loop via new color.h file + * allow interruption while eating -- general handling of partially eaten food + * smarter treatment of iron balls (courtesy Kevin Darcy) + * a handful of other bug fixes + */ + +/* + * Patch 4, September 27, 1989 + * add support for VMS (courtesy David Gentzel) + * move monster-on-floor references into functions and implement the new + * lookup structure for both objects and monsters + * extend the definitions of objects and monsters to provide "living color" + * in the dungeon, instead of a single monster color + * ifdef varargs usage to satisfy ANSI compilers + * standardize on the color 'gray' + * assorted bug fixes + */ + +/* + * Patch 3, September 6, 1989 + * add war hammers and revise object prices + * extend prototypes to ANSI compilers in addition to the previous MSDOS ones + * move object-on-floor references into functions in preparation for planned + * data structures to allow faster access and better colors + * fix some more bugs, and extend the portability of things added in earlier + * patches + */ + +/* + * Patch 2, August 16, 1989 + * add support for OS/2 (courtesy Timo Hakulinen) + * add a better makefile for MicroSoft C (courtesy Paul Gyugyi) + * more accomodation of compilers and preprocessors + * add better screen-size sensing + * expand color use for PCs and introduce it for SVR3 UNIX machines + * extend '/' to multiple identifications + * allow meta key to be used to invoke extended commands + * fix various minor bugs, and do further code cleaning + */ + +/* + * Patch 1, July 31, 1989 + * add support for Atari TOS (courtesy Eric Smith) and Andrew File System + * (courtesy Ralf Brown) + * include the uuencoded version of termcap.arc for the MSDOS versions that + * was included with 2.2 and 2.3 + * make a number of simple changes to accommodate various compilers + * fix a handful of bugs, and do some code cleaning elsewhere + * add more instructions for new environments and things commonly done wrong + */ + +/* + * NetHack 3.0 baseline release, July, 1989 + */ + +/* Version 3.0 */ + +/*****************************************************************************/ + +/*patchlevel.h*/ diff --git a/include/pcconf.h b/include/pcconf.h new file mode 100644 index 0000000..1df429e --- /dev/null +++ b/include/pcconf.h @@ -0,0 +1,338 @@ +/* SCCS Id: @(#)pcconf.h 3.4 1995/10/11 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef PCCONF_H +#define PCCONF_H + +#define MICRO /* always define this! */ + +#ifdef MSDOS /* some of this material is MS-DOS specific */ + +/* + * Automatic Defines: + * + * __GO32__ is defined automatically by the djgpp port of gcc. + * __DJGPP__ is defined automatically by djgpp version 2 and above. + * _MSC_VER is defined automatically by Microsoft C. + * __BORLANDC__ is defined automatically by Borland C. + * __SC__ is defined automatically by Symantec C. + * Note: 3.4.1 was not verified with Symantec C. + */ + +/* + * The following options are somewhat configurable depending on + * your compiler. + */ + +/* + * For pre-V7.0 Microsoft Compilers only, manually define OVERLAY here. + */ + +/*#define OVERLAY */ /* Manual overlay definition (MSC 6.0ax only) */ + +# ifndef __GO32__ +#define MFLOPPY /* Support for floppy drives and ramdisks by dgk */ +# endif + +# define SHELL /* via exec of COMMAND.COM */ + +# ifdef __BORLANDC__ +#define PCMUSIC /* Music option, enable very basic pc speaker music notes */ +# endif + +/* + * Screen control options + * + * You may uncomment: + * ANSI_DEFAULT + * or TERMLIB + * or ANSI_DEFAULT and TERMLIB + * or NO_TERMS + */ + +/* # define TERMLIB */ /* enable use of termcap file /etc/termcap */ + /* or ./termcap for MSDOS (SAC) */ + /* compile and link in Fred Fish's termcap library, */ + /* enclosed in TERMCAP.ARC, to use this */ + +/* # define ANSI_DEFAULT */ /* allows NetHack to run without a ./termcap */ + +# define NO_TERMS /* Allows Nethack to run without ansi.sys by linking */ + /* screen routines into the .exe */ + +# ifdef NO_TERMS /* if NO_TERMS select one screen package below */ +#define SCREEN_BIOS /* Use bios calls for all screen control */ +/* #define SCREEN_DJGPPFAST */ /* Use djgpp fast screen routines */ +# endif + + +/* # define PC9800 */ /* Allows NetHack to run on NEC PC-9800 machines */ + /* Yamamoto Keizo */ + + +/* + * PC video hardware support options (for graphical tile support) + * + * You may uncomment any/all of the options below. + * + */ +# ifndef SUPPRESS_GRAPHICS +# if (defined(SCREEN_BIOS) || defined(SCREEN_DJGPPFAST)) && !defined(PC9800) +# ifdef USE_TILES +#define SCREEN_VGA /* Include VGA graphics routines in the build */ +# endif +# endif +# else +# undef NO_TERMS +# undef SCREEN_BIOS +# undef SCREEN_DJGPPFAST +# undef SCREEN_VGA +# undef TERMLIB +# define ANSI_DEFAULT +# endif + +# define RANDOM /* have Berkeley random(3) */ + +# define MAIL /* Allows for fake mail daemon to deliver mail */ + /* in the MSDOS version. (For AMIGA MAIL see */ + /* amiconf.h). In the future this will be the */ + /* hook for mail reader implementation. */ + +/* The following is needed for prototypes of certain functions */ + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__SC__) +#include /* Provides prototypes of exit(), spawn() */ +#endif + +#if defined(__BORLANDC__) && defined(STRNCMPI) +#include /* Provides prototypes of strncmpi(), etc. */ +#endif + +#if defined(__DJGPP__) +#define _NAIVE_DOS_REGS +#include +#include /* Provides prototypes of strncmpi(), etc. */ +# ifndef M +#define M(c) ((char) (0x80 | (c))) +# endif +#endif + +/* + * On the VMS and unix, this option controls whether a delay is done by + * the clock, or whether it is done by excess output. On the PC, however, + * there is always a clock to use for the delay. The TIMED_DELAY option + * on MSDOS (without the termcap routines) is used to determine whether to + * include the delay routines in the code (and thus, provides a compile time + * method to turn off napping for visual effect). However, it is also used + * in the music code to wait between different notes. So it is needed in that + * case as well. + + * Whereas on the VMS and unix, flags.nap is a run-time option controlling + * whether there is a delay by clock or by excess output, on MSDOS it is + * simply a flag to turn on or off napping for visual effects at run-time. + */ + +#define TIMED_DELAY /* enable the `timed_delay' run-time option */ + +# ifdef PCMUSIC +#define TIMED_DELAY /* need it anyway */ +# endif +#define NOCWD_ASSUMPTIONS /* Allow paths to be specified for HACKDIR, + LEVELDIR, SAVEDIR, BONESDIR, DATADIR, + SCOREDIR, LOCKDIR, CONFIGDIR, and TROUBLEDIR. */ + +#endif /* MSDOS configuration stuff */ + +#define PATHLEN 64 /* maximum pathlength */ +#define FILENAME 80 /* maximum filename length (conservative) */ +#ifndef MICRO_H +#include "micro.h" /* contains necessary externs for [os_name].c */ +#endif + + +/* =================================================== + * The remaining code shouldn't need modification. + */ + +#ifndef SYSTEM_H +#include "system.h" +#endif + +#ifdef __DJGPP__ +#include /* close(), etc. */ +/* lock() in io.h interferes with lock[] in decl.h */ +#define lock djlock +#include +#undef lock +#include /* kbhit() */ +#define PC_LOCKING +#define HOLD_LOCKFILE_OPEN +#define SELF_RECOVER /* NetHack itself can recover games */ +#endif + +# ifdef MSDOS +# ifndef EXEPATH +#define EXEPATH /* HACKDIR is .exe location if not explicitly defined */ +# endif +# endif + +# if defined(_MSC_VER) && defined(MSDOS) +# if (_MSC_VER >= 700) && !defined(FUNCTION_LEVEL_LINKING) +# ifndef MOVERLAY +#define MOVERLAY /* Microsoft's MOVE overlay system (MSC >= 7.0) */ +# endif +# endif +#define PC_LOCKING +# endif + +/* Borland Stuff */ +# if defined(__BORLANDC__) +# if defined(__OVERLAY__) && !defined(VROOMM) +/* __OVERLAY__ is automatically defined by Borland C if overlay option is on */ +#define VROOMM /* Borland's VROOMM overlay system */ +# endif +# if !defined(STKSIZ) +#define STKSIZ 5*1024 /* Use a default of 5K stack for Borland C */ + /* This macro is used in any file that contains */ + /* a main() function. */ +# endif +#define PC_LOCKING +# endif + +#ifdef PC_LOCKING +#define HLOCK "NHPERM" +#endif + +#ifndef index +# define index strchr +#endif +#ifndef rindex +# define rindex strrchr +#endif + +#ifndef AMIGA +#include +#endif + +#ifdef RANDOM +/* Use the high quality random number routines. */ +# define Rand() random() +#else +# define Rand() rand() +#endif + +#ifndef TOS +# define FCMASK 0660 /* file creation mask */ +#endif + +#include + +#ifndef REDO +# undef Getchar +# define Getchar nhgetch +#endif + +#ifdef MSDOS +# define TEXTCOLOR /* */ +# define PORT_HELP "msdoshlp.txt" /* msdos port specific help file */ +#endif + + +/* Sanity check, do not modify these blocks. */ + +/* OVERLAY must be defined with MOVERLAY or VROOMM */ +#if (defined(MOVERLAY) || defined(VROOMM)) +# ifndef OVERLAY +# define OVERLAY +# endif +#endif + +#if defined(FUNCTION_LEVEL_LINKING) +#define OVERLAY +#define OVL0 +#define OVL1 +#define OVL2 +#define OVL3 +#define OVLB +#endif + +#if defined(OVERLAY) && !defined(MOVERLAY) && !defined(VROOMM) && !defined(FUNCTION_LEVEL_LINKING) +#define USE_TRAMPOLI +#endif + +#if defined(MSDOS) && defined(NO_TERMS) +# ifdef TERMLIB +# if defined(_MSC_VER) || defined(__SC__) +# pragma message("Warning -- TERMLIB defined with NO_TERMS in pcconf.h") +# pragma message(" Forcing undef of TERMLIB") +# endif +#undef TERMLIB +# endif +# ifdef ANSI_DEFAULT +# if defined(_MSC_VER) || defined(__SC__) +# pragma message("Warning -- ANSI_DEFAULT defined with NO_TERMS in pcconf.h") +# pragma message(" Forcing undef of ANSI_DEFAULT") +# endif +#undef ANSI_DEFAULT +# endif +/* only one screen package is allowed */ +# if defined(SCREEN_BIOS) && defined(SCREEN_DJGPPFAST) +# if defined(_MSC_VER) || defined(__SC__) +# pragma message("Warning -- More than one screen package defined in pcconf.h") +# endif +# if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__SC__) +# if defined(SCREEN_DJGPPFAST) +# if defined(_MSC_VER) || defined(__SC__) +# pragma message(" Forcing undef of SCREEN_DJGPPFAST") +# endif +#undef SCREEN_DJGPPFAST /* Can't use djgpp fast with other compilers anyway */ +# endif +# else +/* djgpp C compiler */ +# if defined(SCREEN_BIOS) +#undef SCREEN_BIOS +# endif +# endif +# endif +# define ASCIIGRAPH +# ifdef TEXTCOLOR +# define VIDEOSHADES +# endif +/* SCREEN_8514, SCREEN_VESA are only placeholders presently - sub VGA instead */ +# if defined(SCREEN_8514) || defined(SCREEN_VESA) +# undef SCREEN_8514 +# undef SCREEN_VESA +# define SCREEN_VGA +# endif +/* Graphical tile sanity checks */ +# ifdef SCREEN_VGA +# define SIMULATE_CURSOR +# define POSITIONBAR +/* Select appropriate tile file format, and map size */ +# define PLANAR_FILE +# define SMALL_MAP +# endif +#endif /* End of sanity check block */ + +#if defined(MSDOS) && defined(DLB) +#define FILENAME_CMP stricmp /* case insensitive */ +#endif + +#ifdef MSC7_WARN /* define with cl /DMSC7_WARN */ +#pragma warning(disable:4131) +#endif + +#ifdef TIMED_DELAY +# ifdef __DJGPP__ +# define msleep(k) (void) usleep((k)*1000) +# endif +# ifdef __BORLANDC__ +# define msleep(k) delay(k) +# endif +# ifdef __SC__ +# define msleep(k) (void) usleep((long)((k)*1000)) +# endif +#endif + +#endif /* PCCONF_H */ diff --git a/include/permonst.h b/include/permonst.h new file mode 100644 index 0000000..a971f01 --- /dev/null +++ b/include/permonst.h @@ -0,0 +1,81 @@ +/* SCCS Id: @(#)permonst.h 3.4 1999/07/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef PERMONST_H +#define PERMONST_H + +/* This structure covers all attack forms. + * aatyp is the gross attack type (eg. claw, bite, breath, ...) + * adtyp is the damage type (eg. physical, fire, cold, spell, ...) + * damn is the number of hit dice of damage from the attack. + * damd is the number of sides on each die. + * + * Some attacks can do no points of damage. Additionally, some can + * have special effects *and* do damage as well. If damn and damd + * are set, they may have a special meaning. For example, if set + * for a blinding attack, they determine the amount of time blinded. + */ + +struct attack { + uchar aatyp; + uchar adtyp, damn, damd; +}; + +/* Max # of attacks for any given monster. + */ + +#define NATTK 6 + +/* Weight of a human body + */ + +#define WT_HUMAN 1450 + +#ifndef ALIGN_H +#include "align.h" +#endif +#include "monattk.h" +#include "monflag.h" + +struct permonst { + const char *mname; /* full name */ + char mlet; /* symbol */ + schar mlevel, /* base monster level */ + mmove, /* move speed */ + ac, /* (base) armor class */ + mr; /* (base) magic resistance */ + aligntyp maligntyp; /* basic monster alignment */ + unsigned short geno; /* creation/geno mask value */ + struct attack mattk[NATTK]; /* attacks matrix */ + unsigned short cwt, /* weight of corpse */ + cnutrit; /* its nutritional value */ + short pxlth; /* length of extension */ + uchar msound; /* noise it makes (6 bits) */ + uchar msize; /* physical size (3 bits) */ + uchar mresists; /* resistances */ + uchar mconveys; /* conveyed by eating */ + unsigned long mflags1, /* boolean bitflags */ + mflags2; /* more boolean bitflags */ + unsigned short mflags3; /* yet more boolean bitflags */ +# ifdef TEXTCOLOR + uchar mcolor; /* color to use */ +# endif +}; + +extern NEARDATA struct permonst + mons[]; /* the master list of monster types */ + +#define VERY_SLOW 3 +#define SLOW_SPEED 9 +#define NORMAL_SPEED 12 /* movement rates */ +#define FAST_SPEED 15 +#define VERY_FAST 24 + +#define NON_PM PM_PLAYERMON /* "not a monster" */ +#define LOW_PM (NON_PM+1) /* first monster in mons[] */ +#define SPECIAL_PM PM_LONG_WORM_TAIL /* [normal] < ~ < [special] */ + /* mons[SPECIAL_PM] through mons[NUMMONS-1], inclusive, are + never generated randomly and cannot be polymorphed into */ + +#endif /* PERMONST_H */ diff --git a/include/prop.h b/include/prop.h new file mode 100644 index 0000000..f2ce274 --- /dev/null +++ b/include/prop.h @@ -0,0 +1,148 @@ +/* SCCS Id: @(#)prop.h 3.4 1999/07/07 */ +/* Copyright (c) 1989 Mike Threepoint */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef PROP_H +#define PROP_H + +/*** What the properties are ***/ +#define FIRE_RES 1 +#define COLD_RES 2 +#define SLEEP_RES 3 +#define DISINT_RES 4 +#define SHOCK_RES 5 +#define POISON_RES 6 +#define ACID_RES 7 +#define STONE_RES 8 +/* note: for the first eight properties, MR_xxx == (1 << (xxx_RES - 1)) */ +#define ADORNED 9 +#define REGENERATION 10 +#define SEARCHING 11 +#define SEE_INVIS 12 +#define INVIS 13 +#define TELEPORT 14 +#define TELEPORT_CONTROL 15 +#define POLYMORPH 16 +#define POLYMORPH_CONTROL 17 +#define LEVITATION 18 +#define STEALTH 19 +#define AGGRAVATE_MONSTER 20 +#define CONFLICT 21 +#define PROTECTION 22 +#define PROT_FROM_SHAPE_CHANGERS 23 +#define WARNING 24 +#define TELEPAT 25 +#define FAST 26 +#define STUNNED 27 +#define CONFUSION 28 +#define SICK 29 +#define BLINDED 30 +#define SLEEPING 31 +#define WOUNDED_LEGS 32 +#define STONED 33 +#define STRANGLED 34 +#define HALLUC 35 +#define HALLUC_RES 36 +#define FUMBLING 37 +#define JUMPING 38 +#define WWALKING 39 +#define HUNGER 40 +#define GLIB 41 +#define REFLECTING 42 +#define LIFESAVED 43 +#define ANTIMAGIC 44 +#define DISPLACED 45 +#define CLAIRVOYANT 46 +#define VOMITING 47 +#define ENERGY_REGENERATION 48 +#define MAGICAL_BREATHING 49 +#define HALF_SPDAM 50 +#define HALF_PHDAM 51 +#define SICK_RES 52 +#define DRAIN_RES 53 +#define WARN_UNDEAD 54 +#define INVULNERABLE 55 +#define FREE_ACTION 56 +#define SWIMMING 57 +#define SLIMED 58 +#define FIXED_ABIL 59 +#define FLYING 60 +#define UNCHANGING 61 +#define PASSES_WALLS 62 +#define SLOW_DIGESTION 63 +#define INFRAVISION 64 +#define WARN_OF_MON 65 +#define DETECT_MONSTERS 66 +#define LAST_PROP (DETECT_MONSTERS) + +/*** Where the properties come from ***/ +/* Definitions were moved here from obj.h and you.h */ +struct prop { + /*** Properties conveyed by objects ***/ + long extrinsic; + /* Armor */ +# define W_ARM 0x00000001L /* Body armor */ +# define W_ARMC 0x00000002L /* Cloak */ +# define W_ARMH 0x00000004L /* Helmet/hat */ +# define W_ARMS 0x00000008L /* Shield */ +# define W_ARMG 0x00000010L /* Gloves/gauntlets */ +# define W_ARMF 0x00000020L /* Footwear */ +#ifdef TOURIST +# define W_ARMU 0x00000040L /* Undershirt */ +# define W_ARMOR (W_ARM | W_ARMC | W_ARMH | W_ARMS | W_ARMG | W_ARMF | W_ARMU) +#else +# define W_ARMOR (W_ARM | W_ARMC | W_ARMH | W_ARMS | W_ARMG | W_ARMF) +#endif + /* Weapons and artifacts */ +# define W_WEP 0x00000100L /* Wielded weapon */ +# define W_QUIVER 0x00000200L /* Quiver for (f)iring ammo */ +# define W_SWAPWEP 0x00000400L /* Secondary weapon */ +# define W_ART 0x00001000L /* Carrying artifact (not really worn) */ +# define W_ARTI 0x00002000L /* Invoked artifact (not really worn) */ + /* Amulets, rings, tools, and other items */ +# define W_AMUL 0x00010000L /* Amulet */ +# define W_RINGL 0x00020000L /* Left ring */ +# define W_RINGR 0x00040000L /* Right ring */ +# define W_RING (W_RINGL | W_RINGR) +# define W_TOOL 0x00080000L /* Eyewear */ +#ifdef STEED +# define W_SADDLE 0x00100000L /* KMH -- For riding monsters */ +#endif +# define W_BALL 0x00200000L /* Punishment ball */ +# define W_CHAIN 0x00400000L /* Punishment chain */ + + /*** Property is blocked by an object ***/ + long blocked; /* Same assignments as extrinsic */ + + /*** Timeouts, permanent properties, and other flags ***/ + long intrinsic; + /* Timed properties */ +# define TIMEOUT 0x00ffffffL /* Up to 16 million turns */ + /* Permanent properties */ +# define FROMEXPER 0x01000000L /* Gain/lose with experience, for role */ +# define FROMRACE 0x02000000L /* Gain/lose with experience, for race */ +# define FROMOUTSIDE 0x04000000L /* By corpses, prayer, thrones, etc. */ +# define INTRINSIC (FROMOUTSIDE|FROMRACE|FROMEXPER) + /* Control flags */ +# define I_SPECIAL 0x10000000L /* Property is controllable */ +}; + +/*** Definitions for backwards compatibility ***/ +#define LEFT_RING W_RINGL +#define RIGHT_RING W_RINGR +#define LEFT_SIDE LEFT_RING +#define RIGHT_SIDE RIGHT_RING +#define BOTH_SIDES (LEFT_SIDE | RIGHT_SIDE) +#define WORN_ARMOR W_ARM +#define WORN_CLOAK W_ARMC +#define WORN_HELMET W_ARMH +#define WORN_SHIELD W_ARMS +#define WORN_GLOVES W_ARMG +#define WORN_BOOTS W_ARMF +#define WORN_AMUL W_AMUL +#define WORN_BLINDF W_TOOL +#ifdef TOURIST +#define WORN_SHIRT W_ARMU +#endif + +#endif /* PROP_H */ diff --git a/include/qt_clust.h b/include/qt_clust.h new file mode 100644 index 0000000..96fec93 --- /dev/null +++ b/include/qt_clust.h @@ -0,0 +1,29 @@ +/* SCCS Id: @(#)qt_clust.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef clusterizer_H +#define clusterizer_H + +#include + +class Clusterizer { +public: + Clusterizer(int maxclusters); + ~Clusterizer(); + + void add(int x, int y); // 1x1 rectangle (point) + void add(int x, int y, int w, int h); + void add(const QRect& rect); + + void clear(); + int clusters() { return count; } + const QRect& operator[](int i); + +private: + QRect* cluster; + int count; + const int max; +}; + +#endif diff --git a/include/qt_kde0.h b/include/qt_kde0.h new file mode 100644 index 0000000..4d64a42 --- /dev/null +++ b/include/qt_kde0.h @@ -0,0 +1,10 @@ +/* SCCS Id: @(#)qt_kde0.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef QT_DUMMYKDE +#define QT_DUMMYKDE +class KTopLevelWidget : public QMainWindow { + Q_OBJECT +}; +#endif diff --git a/include/qt_win.h b/include/qt_win.h new file mode 100644 index 0000000..2dedce2 --- /dev/null +++ b/include/qt_win.h @@ -0,0 +1,845 @@ +// SCCS Id: @(#)qt_win.h 3.4 1999/11/19 +// Copyright (c) Warwick Allison, 1999. +// NetHack may be freely redistributed. See license for details. +// +// Qt Binding for NetHack 3.4 +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type +// program rather than being event driven (hence the ugly key +// and click buffer rather), but also because this is my first +// major application of Qt. +// + +#ifndef qt_win_h +#define qt_win_h + +#define QT_CLEAN_NAMESPACE + +#include +#include +#include +#include +#include +#if defined(QWS) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if QT_VERSION >= 300 +#include +// Should stop using QTableView +#define QTableView QtTableView +#else +#include +#endif +#include +#include + +#ifdef KDE +#include +#include +#endif + +#include "qt_clust.h" + +class QVBox; +class QMenuBar; +class QRadioButton; +class NhPSListView; + +////////////////////////////////////////////////////////////// +// +// The beautiful, abstracted and well-modelled classes... +// +////////////////////////////////////////////////////////////// + +class NetHackQtGlyphs; + +class NetHackQtLineEdit : public QLineEdit { +public: + NetHackQtLineEdit(); + NetHackQtLineEdit(QWidget* parent, const char* name); + + void fakeEvent(int key, int ascii, int state); +}; + +class NetHackQtSettings : public QDialog { + Q_OBJECT +public: + // Size of window - used to decide default sizes + NetHackQtSettings(int width, int height); + + NetHackQtGlyphs& glyphs(); + const QFont& normalFont(); + const QFont& normalFixedFont(); + const QFont& largeFont(); + + bool ynInMessages(); + +signals: + void fontChanged(); + void tilesChanged(); + +public slots: + void toggleGlyphSize(); + void setGlyphSize(bool); + +private: + QSpinBox tilewidth; + QSpinBox tileheight; + QLabel widthlbl; + QLabel heightlbl; + QCheckBox whichsize; + QSize othersize; + + QComboBox fontsize; + + QFont normal, normalfixed, large; + + NetHackQtGlyphs* theglyphs; + +private slots: + void resizeTiles(); +}; + +class NetHackQtKeyBuffer { +public: + NetHackQtKeyBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int k, int ascii, int state); + void Put(char a); + void Put(const char* str); + int GetKey(); + int GetAscii(); + int GetState(); + + int TopKey() const; + int TopAscii() const; + int TopState() const; + +private: + enum { maxkey=64 }; + int key[maxkey]; + int ascii[maxkey]; + int state[maxkey]; + int in,out; +}; + +class NetHackQtClickBuffer { +public: + NetHackQtClickBuffer(); + + bool Empty() const; + bool Full() const; + + void Put(int x, int y, int mod); + + int NextX() const; + int NextY() const; + int NextMod() const; + + void Get(); + +private: + enum { maxclick=64 }; + struct ClickRec { + int x,y,mod; + } click[maxclick]; + int in,out; +}; + + +class NetHackQtSavedGameSelector : public QDialog { +public: + NetHackQtSavedGameSelector(const char** saved); + + int choose(); +}; + +class NetHackQtPlayerSelector : private QDialog { + Q_OBJECT +public: + enum { R_None=-1, R_Quit=-2, R_Rand=-3 }; + + NetHackQtPlayerSelector(NetHackQtKeyBuffer&); + +protected: + virtual void done(int); + +public slots: + void Quit(); + void Random(); + + void selectName(const QString& n); + void selectRole(); + void selectRace(); + void setupOthers(); + void selectGender(int); + void selectAlignment(int); + +public: + bool Choose(); + +private: + NetHackQtKeyBuffer& keysource; + NhPSListView* role; + NhPSListView* race; + QRadioButton **gender; + QRadioButton **alignment; + bool fully_specified_role; +}; + +class NetHackQtStringRequestor : QDialog { +private: + QLabel prompt; + NetHackQtLineEdit input; + QPushButton* okay; + QPushButton* cancel; + NetHackQtKeyBuffer& keysource; + + virtual void done(int); + +public: + NetHackQtStringRequestor(NetHackQtKeyBuffer&, const char* p,const char* cancelstr="Cancel"); + void SetDefault(const char*); + bool Get(char* buffer, int maxchar=80); + virtual void resizeEvent(QResizeEvent*); +}; + +class NetHackQtExtCmdRequestor : public QDialog { + Q_OBJECT + + NetHackQtKeyBuffer& keysource; + +public: + NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks); + int get(); + +private slots: + void cancel(); + void done(int i); +}; + + +class NetHackQtWindow { +public: + NetHackQtWindow(); + virtual ~NetHackQtWindow(); + + virtual QWidget* Widget() =0; + + virtual void Clear(); + virtual void Display(bool block); + virtual bool Destroy(); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const char* text); + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel); + virtual void EndMenu(const char* prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + virtual void UseRIP(int how); + + int nhid; +}; + +class NetHackQtGlyphs { +public: + NetHackQtGlyphs(); + + int width() const { return size.width(); } + int height() const { return size.height(); } + void toggleSize(); + void setSize(int w, int h); + + void drawGlyph(QPainter&, int glyph, int pixelx, int pixely); + void drawCell(QPainter&, int glyph, int cellx, int celly); + +private: + QImage img; + QPixmap pm,pm1, pm2; + QSize size; + int tiles_per_row; +}; + +class BlackScrollView : public QScrollView { +public: + BlackScrollView() + { + viewport()->setBackgroundColor(black); + } +}; + +class NetHackQtMapWindow : public QWidget, public NetHackQtWindow { + Q_OBJECT +private: + NetHackQtClickBuffer& clicksink; + unsigned short glyph[ROWNO][COLNO]; + unsigned short& Glyph(int x, int y) { return glyph[y][x]; } + QPoint cursor; + BlackScrollView viewport; + QPixmap pet_annotation; + Clusterizer change; + QFont *rogue_font; + QString messages; + QRect messages_rect; + + void Changed(int x,int y); + +signals: + void resized(); + +private slots: + void updateTiles(); + void moveMessages(int x, int y); + +protected: + virtual void paintEvent(QPaintEvent*); + virtual void mousePressEvent(QMouseEvent*); + +public: + NetHackQtMapWindow(NetHackQtClickBuffer& click_sink); + ~NetHackQtMapWindow(); + + virtual QWidget* Widget(); + virtual bool Destroy(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const char* text); + virtual void ClipAround(int x,int y); + virtual void PrintGlyph(int x,int y,int glyph); + + void Scroll(int dx, int dy); + + // For messages + void displayMessages(bool block); + void putMessage(int attr, const char* text); + void clearMessages(); + + void clickCursor(); +}; + +class NetHackQtScrollText; +class NetHackQtMessageWindow : QObject, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMessageWindow(); + ~NetHackQtMessageWindow(); + + virtual QWidget* Widget(); + virtual void Clear(); + virtual void Display(bool block); + virtual void PutStr(int attr, const char* text); + + void Scroll(int dx, int dy); + + void setMap(NetHackQtMapWindow*); + +private: + NetHackQtScrollText* list; + bool changed; + NetHackQtMapWindow* map; + +private slots: + void updateFont(); +}; + +class NetHackQtLabelledIcon : public QWidget { +public: + NetHackQtLabelledIcon(QWidget* parent, const char* label); + NetHackQtLabelledIcon(QWidget* parent, const char* label, const QPixmap& icon); + + enum { NoNum=-99999 }; + void setLabel(const char*, bool lower=TRUE); // a string + void setLabel(const char*, long, const char* tail=""); // a number + void setLabel(const char*, long show_value, long comparative_value, const char* tail=""); + void setIcon(const QPixmap&); + virtual void setFont(const QFont&); + + void highlightWhenChanging(); + void lowIsGood(); + void dissipateHighlight(); + + virtual void show(); + +protected: + void resizeEvent(QResizeEvent*); + +private: + void initHighlight(); + void setAlignments(); + void highlight(const QPalette& highlight); + void unhighlight(); + + bool low_is_good; + int prev_value; + int turn_count; /* last time the value changed */ + QPalette hl_good; + QPalette hl_bad; + + QLabel* label; + QLabel* icon; +}; + +class NetHackQtStatusWindow : QWidget, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtStatusWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual void Display(bool block); + virtual void CursorTo(int x,int y); + virtual void PutStr(int attr, const char* text); + + void fadeHighlighting(); + +protected: + void resizeEvent(QResizeEvent*); + +private slots: + void doUpdate(); + +private: + enum { hilight_time=1 }; + + QPixmap p_str; + QPixmap p_dex; + QPixmap p_con; + QPixmap p_int; + QPixmap p_wis; + QPixmap p_cha; + + QPixmap p_chaotic; + QPixmap p_neutral; + QPixmap p_lawful; + + QPixmap p_satiated; + QPixmap p_hungry; + + QPixmap p_confused; + QPixmap p_sick_fp; + QPixmap p_sick_il; + QPixmap p_blind; + QPixmap p_stunned; + QPixmap p_hallu; + + QPixmap p_encumber[5]; + + NetHackQtLabelledIcon name; + NetHackQtLabelledIcon dlevel; + + NetHackQtLabelledIcon str; + NetHackQtLabelledIcon dex; + NetHackQtLabelledIcon con; + NetHackQtLabelledIcon intel; + NetHackQtLabelledIcon wis; + NetHackQtLabelledIcon cha; + + NetHackQtLabelledIcon gold; + NetHackQtLabelledIcon hp; + NetHackQtLabelledIcon power; + NetHackQtLabelledIcon ac; + NetHackQtLabelledIcon level; + NetHackQtLabelledIcon exp; + NetHackQtLabelledIcon align; + + NetHackQtLabelledIcon time; + NetHackQtLabelledIcon score; + + NetHackQtLabelledIcon hunger; + NetHackQtLabelledIcon confused; + NetHackQtLabelledIcon sick_fp; + NetHackQtLabelledIcon sick_il; + NetHackQtLabelledIcon blind; + NetHackQtLabelledIcon stunned; + NetHackQtLabelledIcon hallu; + NetHackQtLabelledIcon encumber; + + QFrame hline1; + QFrame hline2; + QFrame hline3; + + int cursy; + + bool first_set; + + void nullOut(); + void updateStats(); + void checkTurnEvents(); +}; + +class NetHackQtMenuDialog : public QDialog { + Q_OBJECT +public: + NetHackQtMenuDialog(); + + void Accept(); + void Reject(); + void SetResult(int); + + virtual void done(int); + +protected: + void resizeEvent(QResizeEvent*); + +signals: + void Resized(); +}; + + +class NetHackQtMenuWindow : public QTableView, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtMenuWindow(NetHackQtKeyBuffer&); + ~NetHackQtMenuWindow(); + + virtual QWidget* Widget(); + + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel); + virtual void EndMenu(const char* prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +public slots: + void All(); + void ChooseNone(); + void Invert(); + void Search(); + + void Layout(); + void ToggleSelect(int); + +protected: + virtual void keyPressEvent(QKeyEvent*); + //virtual void mouseDoubleClickEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void mouseReleaseEvent(QMouseEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void focusOutEvent(QFocusEvent*); + virtual void focusInEvent(QFocusEvent*); + + virtual void paintCell(QPainter*, int, int); + virtual int cellWidth(int col); + +private: + struct MenuItem { + MenuItem(); + ~MenuItem(); + + int glyph; + ANY_P identifier; + int attr; + const char* str; + int count; + char ch; + bool selected; + + bool Selectable() const { return identifier.a_void!=0; } + }; + + QArray item; + + int itemcount; + int str_width; + bool str_fixed; + int next_accel; + + NetHackQtKeyBuffer& keysource; + + NetHackQtMenuDialog* dialog; + + QPushButton* ok; + QPushButton* cancel; + QPushButton* all; + QPushButton* none; + QPushButton* invert; + QPushButton* search; + QLabel prompt; + + int how; + + bool has_glyphs; + + int pressed; + bool was_sel; +}; + +class NetHackQtTextListBox; + +class NetHackQtRIP : public QWidget { +private: + static QPixmap* pixmap; + char** line; + int riplines; + +public: + NetHackQtRIP(QWidget* parent); + + void setLines(char** l, int n); + +protected: + virtual void paintEvent(QPaintEvent* event); + QSize sizeHint() const; +}; + + +class NetHackQtTextWindow : public QDialog, public NetHackQtWindow { + Q_OBJECT +public: + NetHackQtTextWindow(NetHackQtKeyBuffer&); + ~NetHackQtTextWindow(); + + virtual QWidget* Widget(); + + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const char* text); + virtual void UseRIP(int how); + +public slots: + void Search(); + +protected: + virtual void done(int); + virtual void keyPressEvent(QKeyEvent*); + +private slots: + void doUpdate(); + +private: + NetHackQtKeyBuffer& keysource; + + bool use_rip; + bool str_fixed; + + QPushButton ok; + QPushButton search; + NetHackQtTextListBox* lines; + + NetHackQtRIP rip; +}; + +class NetHackQtMenuOrTextWindow : public NetHackQtWindow { +private: + NetHackQtWindow* actual; + NetHackQtKeyBuffer& keysource; + +public: + NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer&); + + virtual QWidget* Widget(); + + // Text + virtual void Clear(); + virtual bool Destroy(); + virtual void Display(bool block); + virtual void PutStr(int attr, const char* text); + + // Menu + virtual void StartMenu(); + virtual void AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel); + virtual void EndMenu(const char* prompt); + virtual int SelectMenu(int how, MENU_ITEM_P **menu_list); + +}; + +class NetHackQtDelay : QObject { +private: + int msec; + +public: + NetHackQtDelay(int ms); + void wait(); + virtual void timerEvent(QTimerEvent* timer); +}; + + +class NetHackQtInvUsageWindow : public QWidget { +public: + NetHackQtInvUsageWindow(QWidget* parent); + virtual void paintEvent(QPaintEvent*); +private: + void drawWorn(QPainter& painter, obj*, int x, int y, bool canbe=TRUE); +}; + +// This class is the main widget for NetHack +// +// It is a collection of Message, Map, and Status windows. In the current +// version of nethack there is only one of each, and this class makes this +// assumption, not showing itself until all are inserted. +// +// This class simply knows how to layout such children sensibly. +// +// Since it is only responsible for layout, the class does not +// note the actual class of the windows. +// +#ifndef KDE +#include "qt_kde0.h" +#endif + +class NetHackQtMainWindow : public KTopLevelWidget { + Q_OBJECT +public: + NetHackQtMainWindow(NetHackQtKeyBuffer&); + + void AddMessageWindow(NetHackQtMessageWindow* window); + void AddMapWindow(NetHackQtMapWindow* window); + void AddStatusWindow(NetHackQtStatusWindow* window); + void RemoveWindow(NetHackQtWindow* window); + void updateInventory(); + + void fadeHighlighting(); + +public slots: + void doMenuItem(int); + void doKeys(const QString&); + +protected: + virtual void resizeEvent(QResizeEvent*); + virtual void keyPressEvent(QKeyEvent*); + virtual void keyReleaseEvent(QKeyEvent* event); + virtual void closeEvent(QCloseEvent*); + +private slots: + void layout(); + void raiseMap(); + void zoomMap(); + void raiseMessages(); + void raiseStatus(); + +private: + void ShowIfReady(); + +#ifdef KDE + KMenuBar* menubar; +#else + QMenuBar* menubar; +#endif + NetHackQtMessageWindow* message; + NetHackQtMapWindow* map; + NetHackQtStatusWindow* status; + NetHackQtInvUsageWindow* invusage; + + NetHackQtKeyBuffer& keysink; + QWidgetStack* stack; + int dirkey; + + const char* *macro; +}; + +class NetHackQtYnDialog : QDialog { + Q_OBJECT +private: + const char* question; + const char* choices; + char def; + NetHackQtKeyBuffer& keysource; + +protected: + virtual void keyPressEvent(QKeyEvent*); + virtual void done(int); + +private slots: + void doneItem(int); + +public: + NetHackQtYnDialog(NetHackQtKeyBuffer& keysource,const char*,const char*,char); + + char Exec(); +}; + +#ifdef KDE +#define NetHackQtBindBase KApplication +#elif defined(QWS) +#define NetHackQtBindBase QPEApplication +#else +#define NetHackQtBindBase QApplication +#endif + +class NetHackQtBind : NetHackQtBindBase { +private: + // Single-instance preservation... + NetHackQtBind(int& argc, char** argv); + + static NetHackQtBind* instance; + + static NetHackQtKeyBuffer keybuffer; + static NetHackQtClickBuffer clickbuffer; + + static QWidget* splash; + static NetHackQtMainWindow* main; + +public: + static void qt_init_nhwindows(int* argc, char** argv); + static void qt_player_selection(); + static void qt_askname(); + static void qt_get_nh_event(); + static void qt_exit_nhwindows(const char *); + static void qt_suspend_nhwindows(const char *); + static void qt_resume_nhwindows(); + static winid qt_create_nhwindow(int type); + static void qt_clear_nhwindow(winid wid); + static void qt_display_nhwindow(winid wid, BOOLEAN_P block); + static void qt_destroy_nhwindow(winid wid); + static void qt_curs(winid wid, int x, int y); + static void qt_putstr(winid wid, int attr, const char *text); + static void qt_display_file(const char *filename, BOOLEAN_P must_exist); + static void qt_start_menu(winid wid); + static void qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel); + static void qt_end_menu(winid wid, const char *prompt); + static int qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list); + static void qt_update_inventory(); + static void qt_mark_synch(); + static void qt_wait_synch(); + + static void qt_cliparound(int x, int y); + static void qt_cliparound_window(winid wid, int x, int y); + static void qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph); + static void qt_raw_print(const char *str); + static void qt_raw_print_bold(const char *str); + static int qt_nhgetch(); + static int qt_nh_poskey(int *x, int *y, int *mod); + static void qt_nhbell(); + static int qt_doprev_message(); + static char qt_yn_function(const char *question, const char *choices, CHAR_P def); + static void qt_getlin(const char *prompt, char *line); + static int qt_get_ext_cmd(); + static void qt_number_pad(int); + static void qt_delay_output(); + static void qt_start_screen(); + static void qt_end_screen(); + + static void qt_outrip(winid wid, int how); + static int qt_kbhit(); + +private: + virtual bool notify(QObject *receiver, QEvent *event); +}; + +#endif diff --git a/include/qt_xpms.h b/include/qt_xpms.h new file mode 100644 index 0000000..fcb4b8d --- /dev/null +++ b/include/qt_xpms.h @@ -0,0 +1,1406 @@ +/* XPM */ +static const char *blind_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 5 1", +/* colors */ +" c #000000", +". c None", +"X c #909090", +"o c #606060", +"O c #303030", +/* pixels */ +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"....ooooooooooooooooooooooooooooooooX...", +".... o...", +".... o...", +".... o...", +".... o...", +"......o ..o ......", +"......X O..X O......", +"....... o... o......", +".......o ....o .......", +"........O X.....O X.......", +".........O X.......O X........", +"..........o OX.........o OX.........", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................" +}; +/* XPM */ +static const char *cha_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 14 1", +/* colors */ +" c #F85848", +". c #949E9E", +"X c #F8B090", +"o c #E00028", +"O c #D4D4D4", +"+ c None", +"@ c #B0B0B0", +"# c #F82C24", +"$ c #F89E6C", +"% c #FF0000", +"& c #909090", +"* c #FFFFFF", +"= c #CEAA90", +"- c #DADAB6", +/* pixels */ +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"+++++++++++++++=#%#=+=#%% ++++++++++++++", +"++++++++++++++ %O%%%#%$$%o%=++++++++++++", +"+++++++++++++# +#%%o%%o%%%%% +++++++++++", +"+++++++++++ %%%%%%%%%%%%%%%%o#=+++++++++", +"+++++++++ o%%%%%%%%%%%%%%%%%%%%# +++++++", +"++++++ #%%%%%%o%%%o%%o%%o%o%%%%%o%o +++", +"++=#%%o%%%#= =*+**O*+**O*+- = =%%%%#@+++", +"++++ %=++*+*+**O****O****O*O*O*OO%=+++++", +"+++++.%=OO+*O*OO****+****+*O*+O&%=@+++++", +"++++++=%=*OO+**O**O*O**O*O*OO+$%=+++++++", +"+++++++#% +*OOOO****+****@O+*#%=++++++++", +"++++++++#%#*+**+O+OO+O+OOO*O#o#+++++++++", +"+++++++++o% O**+****O****O*#%%=+++++++++", +"+++++++++ %%#O*O****+****+ %o#++++++++++", +"++++++++++o%% XO*O**O*O**#%%%+++++++++++", +"++++++++++ %%%o%$-**+**$%%%%=+++++++++++", +"+++++++++++o%%$X$%%%%%%#= o#++++++++++++", +"++++++++++@ %%%o#O$$+$$$%%%=++++++++++++", +"++++++++++++#o%%%%%%%%o%%%=@++++++++++++", +"+++++++++++++ %%%%%%%%%%o=++++++++++++++", +"+++++++++++++++= & & @++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++" +}; +/* XPM */ +static const char *chaotic_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 9 1", +/* colors */ +" c #000000", +". c #5C7A7A", +"X c None", +"o c #B0B0B0", +"O c #909090", +"+ c #788C8C", +"@ c #606060", +"# c #FFFFFF", +"$ c #303030", +/* pixels */ +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXX@$ @XXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXX$$+#X$ $XXXXXXXXXX", +"XXXXXXXXXXXXXXXXXX@$#o @XXXXXXXXX", +"XXXXXXXXXXXXXXXXXX$XX OXXXXXXXX", +"XXXXXXXXXXXXXXXXX@ # $@$ $XXXXXXXX", +"XXXXXXXXXXXXXXXXX@.+ $XXXO @XXXXXXX", +"XXXXXXXXXXXXXXXXX O@ XXXXX@ @XXXXXXX", +"XXXXXXXXXXXXXXXXX @O $XXXXX@$ @XXXXXXX", +"XXXXXXXXXXXXXXXXX O+ @XXXXO++ @XXXXXXX", +"XXXXXXXXXXXXXXXXX @+ $@OXO$#$ XXXXXXXX", +"XXXXXXXXXXXXXXXXX O@ $ @$Xo $XXXXXXXX", +"XXXXXXXXXXXXXXXXX +O $X##+ $XXXXXXXXX", +"XXXXXXXXXXXXXXXXX +@ $XXXXXXXXXX", +"XXXXXXXXXXXXXXXXX oO $XXXXXXXXXXX", +"XXXXXXXXO@@@@@ +# $XXXXXXXXXXXX", +"XXXXXXO +o########$ $@XXXXXXXXX", +"XXXXXX +#+.$XXXXXXXX", +"XXXXXX @O @XXXXXXX", +"XXXXXX$ $@ $@@$ @XXXXXXX", +"XXXXXXX@@@@XXXXXX + @XXXX@$ OXXXXXXX", +"XXXXXXXXXXXXXXXX@ # @XXXXXXX@@OXXXXXXXX", +"XXXXXXXXXXXXXXXX@.+ @XXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX$O@ XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX @O XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX #$ @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@ # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@ # OXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@.X XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX ++ XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX @+ XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX O@ @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX +O @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX$ OXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX@@OXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +}; +/* XPM */ +static const char *cns_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 19 1", +/* colors */ +" c #000000", +". c #F85848", +"X c #949E9E", +"o c #F8B090", +"O c #E00028", +"+ c #7C3400", +"@ c None", +"# c #B0B0B0", +"$ c #F82C24", +"% c #F89E6C", +"& c #FF0000", +"* c #B64700", +"= c #909090", +"- c #788C8C", +"; c #606060", +": c #C80050", +"> c #CEAA90", +", c #303030", +"< c #FFB691", +/* pixels */ +"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@.oo.o$ ;@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@>.o.%%O,@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@$oo.o. ,@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@.oo$oo+ =@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@..o&oo$ ,@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@#.o.oo. =@.$%@@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o..oo& O.%ooo@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o.&%o.$oo%O++;@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o.+$%$o.@@@@@@@@@@@", +"@@@@@@@@@@@@@.oo++o%$$ ,@@$.oo@@@@@@@@@@", +"@@@@@@@@@@@@>.oo+Oo$o%.@@$oo..-@@@@@@@@@", +"@@@@@@@@@@@@..o%;.o&%.$..o%O ++>@@@@@@@@", +"@@@@@@@@@@@@>.$O:%o.O::::O* $oooo@@@@@@@", +"@@@@@@@@@@@@::::::$$:OO&OO::oo%.;=@@@@@@", +"@@@@@@@@@@@.::::::::O&&&&&O::++ ,@@@@@@", +"@@@@@@@@@@>:::O&&OO&&&&&&&&:: ;@@@@@", +"@@@@@@@@@@=::O&&&&&O:O&&&&&O: ,=@@@@@@@", +"@@@@@@@@@@:::&&&&&&&&:&&&&&O: ;@@@@@@@@", +"@@@@@@@@@@::O&&&&&&&&:&O&&&O:, ;@@@@@@@@", +"@@@@@@@@@@::O&&&&O&O&OO&O&&O:+ ;@@@@@@@@", +"@@@@@@@@@@::&&&O&&&&&O:&&&&O:, @@@@@@@@", +"@@@@@@@@@@::O&&&&&O&&&:O&O&::+ @@@@@@@@", +"@@@@@@@@@@::O&&O&&&&O&OO&&&:: @@@@@@@@", +"@@@@@@@@@@=::O&&&&O&&&O:&&&:: @@@@@@@@", +"@@@@@@@@@@.:::O&&O&&&&&:&OO:: @@@@@@@@", +"@@@@@@@@@@@:::::&&&&O&O:&&O:, @@@@@@@@", +"@@@@@@@@@@.>:::::O&&&&&:&&::+ ;@@@@@@@@", +"@@@@@@@@@@>.<::::O&&O&O:&&:: @@@@@@@@@", +"@@@@@@@@@@@.o%,:::O&&&O:&O:, @@@@@@@@@", +"@@@@@@@@@@@$o. :::OO&OO&::, ;@@@@@@@@@", +"@@@@@@@@@@@&o%+ ,::O&OO&O:: =@@@@@@@@@", +"@@@@@@@@@@@.oo+ :::OO::: ,@@@@@@@@@@", +"@@@@@@@@@@@..oO +::::: =@@@@@@@@@@", +"@@@@@@@@@@@@.<.+ ,+, ,@@@@@@@@@@@", +"@@@@@@@@@@@@Oo<+ @X, ,@@@@@@@@@@@@", +"@@@@@@@@@@@@.%o$ @@@@@;, ;@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o., =@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" +}; +/* XPM */ +static const char *confused_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO.=.+OO=.+O.OO+O+OO.+OOOOOO", +"OOOOOOOOOOO++=====O=====+=O+==++=O+OOOOO", +"OOOOOOOOOOO+=.=====.=++++===OO==+O=+OOOO", +"OOOOOOOOOOO=+===.+=o==o===+&OoO======OOO", +"OOOOOOOO+O+====OO+=o&&&&Oo==o&oO+==+=.O.", +"OOOO+.+=+O==+&&o=oooOo&o&ooo=&oooO==O=+=", +"OOOOOOOO++O===oo=oo&=&o&&oo=o==&o+==++==", +"OOOOOOOO=o.=O====o&OO&o&oo&o&&oo=======O", +"OOOOOOOo===+=O=O=ooO=ooooOOo=o&O=====OOO", +"OOOOOOOOO+==+=======O=oo====O=o=O===+OOO", +"OOOOOOOOO.=#=X=+====O========O======OOOO", +"OOOOOOO.#Xo++.=#%====O==========OO==+OOO", +"OOOOOO+Xo#+#+.#=.==X====+====O=+=+==+OOO", +"OOOOO.+.+O===##.#=X.====oX##===o+OO.OOOO", +"OOOOO#+####O#O##o.#+==#X#O#+...=OOo=+OOO", +"OOOO++#o+#+X++++#.#O.#+#X.#+X+==+OO=oOOO", +"OOOO#+.+..X+.##X++#++#..+XX#+##+..OOOOOO", +"OOOO##....O+#++#+.++#+X+#+#X..+#+#OOOOOO", +"OOOO++#+.+.#+#O+X#X#XX#.++##.#++.X$OOOOO", +"OOOOO#+#+.+++#++.+++##+X###+X+X##+**OOOO", +"OOOOO#..#OO#+.##o###.+..++.+#X+#+#* @OOO", +"OOOOO+#.#O+#+#O.+++.###+##++###+.#* $OOO", +"OOOOOOXX+#+#+#o..X##++#+..##.#+### *OOO", +"OOOOOOOX#.#X+#+#+#+.#+..+####%XX%% OOO", +"OOOOOOOO.%%X.#+#+#.++#+#+#+.X++=.% *OOO", +"OOOOOOOOO.* *##+#+.O####.+XX%%%%#% $OOO", +"OOOOOOOOOOO. %X.+.#+++XXX=.+++#X $OOO", +"OOOOOOOOOOOO.* %%X..#X%=.####%X* $OOO", +"OOOOOOOOOOOOOO.$ *XX%%%=.#X%###=* OOOO", +"OOOOOOOOOOOOOOOOOO+%%%=%%#.+.#=* @OOOO", +"OOOOOOOOOOOOOOOOOOo=%%%==X##X%* OOOOO", +"OOOOOOOOOOOOOOOOOOO+X%%%%X=%* @OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X *@OOOOOO", +"OOOOOOOOOOOOOOOOOOOO=%%%X* *$$OOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+X%%= .OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOX%%% OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO=%%* $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO=%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO+%%% $OOOOOOOOOOOOO" +}; +/* XPM */ +static const char *dex_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 19 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #F8B090", +"o c #5C7A7A", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #B64700", +"* c #909090", +"= c #606060", +"- c #CEAA90", +"; c #DADAB6", +": c #303030", +"> c #F86800", +", c #FFB691", +"< c #F88C48", +/* pixels */ +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"###############-%-######################", +"##############-%X<-#####################", +"#########-<<-#-%XX+==###################", +"#########%,X< :<,X%@ :##################", +"#########-XX%: @;X%+ *#################", +"##########<,X& :<<%+: :#################", +"######->+#-%%%: <,XX@ #################", +"######%X%@ <,,& @XXX+ :++-#############", +"######-: +XX+ #########", +"#####+%%@@,X%<,XXXXXXX<:@XXX<: =########", +"####$%XX< <,<,XX& ########", +"#####$%<%X@%XXXX<%XX< =#######", +"#######<;X%XXXXX<<,XXXXXX%,XXXXXXXXX%@ #######", +"##########- c #788C8C", +", c #606060", +"< c #406868", +"1 c #C80050", +"2 c #FFFFFF", +"3 c #FFFF00", +"4 c #00B6FF", +"5 c #CEAA90", +"6 c #DADAB6", +"7 c #F86800", +"8 c #FFB691", +"9 c #6C91B6", +"0 c #F88C48", +"q c #0000FF", +/* pixels */ +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$353333335*$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$*33333333#7@3335$$$$$$$$$$$$", +"$$$$$$$$$65333333333@7777#333*$$$$$$$$$$", +"$$$$$$$$$3333333333377777733333===%$$$$$", +"$$$$$$$533333333333#7777777333%=====$$$$", +"$$$$$$ #3333333333o>7777773330======%$$$", +"$$$$5---O#33333o3944077777333*=======$$$", +"$$$$-----O333333>4444.77333330======%$$$", +"$$$ ---O--;3333344444443333333:====5$$$$", +"$$$ O-----733333444444433333333 ==035$$$", +"$$$3--O--O333333>44444>33333333333333$$$", +"$$533---O33333333944493333#333333333356$", +"$$33867733333o33333:o333333o3333333333$$", +"$532+2233333#333333333333oooo3#3333333%$", +"6522222+33333333333333333oooooo33o3333*$", +"$+22+22263333333o3333333ooooooo333333356", +"662222+2533333333333333#ooooooo33333333$", +"$32+22223333o3#33333o333ooooooo3#333333%", +"$33222233333333333#333333ooooo333333333$", +"$33368333333333333330626*oooo#333333o33%", +"%333335== 33oo333333222223#333333333333$", +"$3333=====:ooooo333+22+2263333333.>o333%", +"$5333=====oooooo33322222223333339444935$", +"$*33 ====>ooooooo3362+222633333.44444>3$", +"$%330====:ooooooo333222+23333334444444$$", +"$$333177 =oooXoo#333*626333333;4444444$$", +"$$53##777&3oooo3333333333333#--,444449$$", +"$$$3;77777#3o333333333333333O---94449$$$", +"$$%*@77777#33333333333333337O----O:o3$$$", +"$$$5777777333 333333333333;---O-O73$$$$", +"$$$$#7777730====#:.,33333333------3$$$$$", +"$$$$$577333=====qqqq<0333333#O---35$$$$$", +"$$$$$%53335====qqqqqq.33o333337735$$$$$$", +"$$$$$$$533 ====qqqqqqq3333333333%$$$$$$$", +"$$$$$$$$%33====qqqqqqq333333333%$$$$$$$$", +"$$$$$$$$$$50===qqqqqq,3333333:$$$$$$$$$$", +"$$$$$$$$$%6%5503,qqq<333#335%$$$$$$$$$$$", +"$$$$$$$$$$$$$%$*53,03335o$%%$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$%$$+$$$$$$$$$$$$$$$" +}; +/* XPM */ +static const char *hungry_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 15 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #CEAA90", +"= c #DADAB6", +"- c #303030", +"; c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO========OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO=============OOOOOOOOOOOOO", +"OO;XX;@OOOOO================OOOOOOOO;XOO", +"OO;%-;$OOOO==================OOOOOOO;XOO", +"OO;-%;$OOO========@$#@========OOOOO+;;$O", +"OO;%-;$OO=======- -*======*OOOO.;;$O", +"OO;-%;$O======* @====.$$&=====@OOO.;;$O", +"OO;X%;$O====== -========*@=====*.OO+;;$O", +"OO;;;X$o====* -==========@======$OO;;;$O", +"OO+;;-+o====- =============o====#@O+;;$O", +"OOO;%$O===== @=============&====*$O;;;$O", +"OOO+%OO====@ ==============&=====-OO;;$O", +"OOo;-Oo====$ ==============o&==== OO;;$O", +"OOO+%OO====@ ==============&===== O+;;#O", +"OOO;-Oo====$-==============&&==== O+;;-O", +"OOO;;+O=====$*============&&====* OO;;%+", +"OOO;;$o=====$.============&&====X-OO;;$O", +"OOO;;$O======*.===&======&&=====-$=O;;$O", +"OOO;;$Oo=====.==========&&=====* @O+;;$O", +"OOO;;$OO=======oo=====&&&======$-OOO;;$O", +"OOO;;$OOo=======&o&&&&&&======$ @OOO;;$O", +"OOO;;$OOOO========&=&========* $OOOO;;$O", +"OO+;;$OOOOo=================* -OOOOO#;$O", +"OOO;;$OOOOO=*==============@ -=OOOOO;;$O", +"OOO;;$OOOOOOO+*==========*- $OOOOOOO;;$O", +"OOOX-$OOOOOOOO@X@*====*#- -.OOOOOOOOX-$O", +"OOOOOOOOOOOOOO=*@$- -$.=OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOO=O==O=O=OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *hvy_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOO&&&&&&&&&&&&&&&.OOOOOOOOOOOOO", +"OOOOOOOOOOOo==============X*OOOOOOOOOOOO", +"OOOOOOOOOOoO===X====X=====X**OOOOOOOOOOO", +"OOOOOOOOOO&============X===% $OOOOOOOOOO", +"OOOOOOOOOoo===*%====***%===%* OOOOOOOOOO", +"OOOOOOOOOoO==% %===* %==X* @OOOOOOOOO", +"OOOOOOOOO&===% *==% X==**===% $OOOOOOOOO", +"OOOOOOOOoo===% %==% ===% ===X OOOOOOOOO", +"OOOOOOOOoO==== *== *==== *==X* @OOOOOOOO", +"OOOOOOOO&===== %== %==== %===% $OOOOOOOO", +"OOOOOOOoo===== *== *==== *===%* OOOOOOOO", +"OOOOOOOoO===== %==% ===* ====X* @OOOOOOO", +"OOOOOOO&===X== *==% X==**=====% $OOOOOOO", +"OOOOOOoo===== *==* %=====X OOOOOOO", +"OOOOOOoO=====*%%X===*%*X======%* @OOOOOO", +"OOOOOOo====================X===* $OOOOOO", +"OOOOOOO=%X%XXXX%XXXXXXXXX%X=%X% OOOOOO", +"OOOOOOO.=********************** OOOOOO", +"OOOOOOOOO OOOOOO", +"OOOOOOOOO. @OOOOOO", +"OOOOOOOOOOOoOOoOoOoOoOoOoOOoOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *int_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 12 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #303030", +"* c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOO+#.X.##@#OOOOOOOOOOOOOOOOOO", +"OOOOOOOO+##@X#O++.#+#.##OOOOOOOOOOOOOOOO", +"OOOOOO+#Xo++#X#%#+##o#O#.#+OOOOOOOOOOOOO", +"OOOOO.Xo#+#++##+.XX#..+.+..XOOOOOOOOOOOO", +"OOOO++.+O.+O##+#.X###..OX#.+X+OOOOOOOOOO", +"OOOO#+####O#O##o##+###X#+#+.#..OOOOOOOOO", +"OOO.+#o+#+X++++#.#O+#+#X.#+X++X+OOOOOOOO", +"OOO.+.+..X+.##X++#++#..+XX#+#X+..OOOOOOO", +"OOO##....O+#++#+.++#+X+#+#X..+#+#OOOOOOO", +"OOO++#+.+.#+#O+X#X#XX#.++##.#++.X$OOOOOO", +"OOOO#+#+.+++#++.+++##+X###+X+X##+&&OOOOO", +"OOOO#..#OO#+.##o###.+..++.+#X+#+#& @OOOO", +"OOOO.#.#O+#+#O.+++.###+##++###+.# $OOOO", +"OOOOOXX+#+#+#o..X##++#+..##.#+### &OOOO", +"OOOOOOX#.#X+#+#+#+.#+..+####XX%X% OOOO", +"OOOOOOO.%%X.#+#+#.++#+#+#+.%++*+% &OOOO", +"OOOOOOOO@& &##+#+.O####.+XXX%%%#% $OOOO", +"OOOOOOOOOO. %X.+.#+++XXX*.+++#% $OOOO", +"OOOOOOOOOOO@& %%X..#XXX.####%%& $OOOO", +"OOOOOOOOOOOOO@$ &XX%%%*.#X%###*& OOOOO", +"OOOOOOOOOOOOOOOOO+%%%*%%#.+.#*& @OOOOO", +"OOOOOOOOOOOOOOOOOO*%%%*.X##XX& OOOOOO", +"OOOOOOOOOOOOOOOOOOOX%%%%X*%& @OOOOOO", +"OOOOOOOOOOOOOOOOOOOX%%%%% &@OOOOOOO", +"OOOOOOOOOOOOOOOOOOO*%%%X& &$$OOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO+%%%* .OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO+*%%% OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%& $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+%%& $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOoOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *lawful_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 10 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #606060", +"$ c #FFFFFF", +"% c #303030", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOo$$$$$$oOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO$$o$$o$$$$$OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOo$$$$$$$o$$ooOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO$o$$$o$$$$$$$oOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$$+ .o$$$$$oOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$+%OOOO$o$$$oOOOOOOO", +"OOOOOOOOOOOOOOOOO$$o$X@OOOOo$$$ooOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$%OOOOOo$$$..OOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$@OOOOo$$oo##OOOOOOO", +"OOOOOOOOOOOOOOOO+$$o$$ooOoo$$$o OOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$$$$o$$$$o#%OOOOOOOO", +"OOOOOOOOOOOOOOOO+$$o$$o$$$$$o@%OOOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$$$$$o$o.%OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$o$$oo@#%OOOOOOOOOOO", +"OOOOOOOOoooooo$$$$$$$$$$$% %OOOOOOOOOOOO", +"OOOOOOO$$$$$$$$$$$$o$$o$$$$$$$oOOOOOOOOO", +"OOOOOO$$$$$$$$$o$$$$$$$$$$$$o$$oOOOOOOOO", +"OOOOOO$$o$ooooo##+o$$+##@oo$$$$$oOOOOOOO", +"OOOOOOo$$#% %#$$$+%##%%#ooo$O#OOOOOOO", +"OOOOOOOo@##OOOOO+$$$##OOOO#%%##%@OOOOOOO", +"OOOOOOOOOOOOOOOOo$$$##OOOOOOO##@OOOOOOOO", +"OOOOOOOOOOOOOOOOo$$o##OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$oo OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+$$$o OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$o$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$o%@OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$o OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$oo OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$$o OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$o##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$# @OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO.#@OOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *mod_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOo&&&&&&&&&oXOOOOOOOOOOOOOOO", +"OOOOOOOOOOOO+&=========X%@OOOOOOOOOOOOOO", +"OOOOOOOOOOOOO&=====X====% @OOOOOOOOOOOOO", +"OOOOOOOOOOOOoO==X=======X* OOOOOOOOOOOOO", +"OOOOOOOOOOOO&====*%*%*===* $OOOOOOOOOOOO", +"OOOOOOOOOOO+&===X ===% *OOOOOOOOOOOO", +"OOOOOOOOOOOoO===**=======X* OOOOOOOOOOOO", +"OOOOOOOOOOO&===% %=======X% $OOOOOOOOOOO", +"OOOOOOOOOOO&===% %*%%=====% *OOOOOOOOOOO", +"OOOOOOOOOOoO===* ====X* OOOOOOOOOOO", +"OOOOOOOOOO&=========* X===X% $OOOOOOOOOO", +"OOOOOOOOO+&=========% *====% *OOOOOOOOOO", +"OOOOOOOOOoO===% %=== %====%* OOOOOOOOOO", +"OOOOOOOOO&====* *==X===% $OOOOOOOOO", +"OOOOOOOOO&======*%*%X=======% *OOOOOOOOO", +"OOOOOOOOOo==X===============% OOOOOOOOO", +"OOOOOOOOO=XXXXXXXXXX%X%X%X%%% $OOOOOOOO", +"OOOOOOOOOO=%**************** $OOOOOOOO", +"OOOOOOOOOOO$ $OOOOOOOO", +"OOOOOOOOOOOO* *OOOOOOOOO", +"OOOOOOOOOOOOOoOOoOoOoOoOoOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *neutral_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 14 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #00B6FF", +"= c #303030", +"- c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO.------.OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO-+O&o.-----OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+-&o--------.OOOOOOOOO", +"OOOOOOOOOOOOOOOOOO-oo----------+OOOOOOOO", +"OOOOOOOOOOOOOOOOO+-&--% #-------OOOOOOOO", +"OOOOOOOOOOOOOOOOO-OO-X=OOO.-----+OOOOOOO", +"OOOOOOOOOOOOOOOOO-oO-%#OOOO.-----OOOOOOO", +"OOOOOOOOOOOOOOOOO--O-=OOOOO+---X#OOOOOOO", +"OOOOOOOOOOOOOOOOO-oO-XOOOO+OO--=$OOOOOOO", +"OOOOOOOOOOOOOOOOO-OO--++OO-&--- OOOOOOOO", +"OOOOOOOOOOOOOOOOO-OO-----+oo--%=OOOOOOOO", +"OOOOOOOOOOOOOOOOO--O--+o&&o--%=OOOOOOOOO", +"OOOOOOOOOOOOOOOOO-oo*-------%=OOOOOOOOOO", +"OOOOOOOOOOOOOOOOO-oO------%%=OOOOOOOOOOO", +"OOOOOOOO+.+-+.---O&------= =OOOOOOOOOOOO", +"OOOOOO+-oo&&&&&&&&------------.OOOOOOOOO", +"OOOOOO---------------X-----O&Oo-OOOOOOOO", +"OOOOOO---------%=%---%%=%----OO-.OOOOOOO", +"OOOOOO---== =%---%=%%===----%XOOOOOOO", +"OOOOOOO-#$%OOOOOO-+-%$OOOO%===%=@OOOOOOO", +"OOOOOOOOOOOOOOOO.-&-=%OOOOOOO%%#OOOOOOOO", +"OOOOOOOOOOOOOOOo-O+-%$OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-oO- OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-OO- OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-%%OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-%$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-=$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO--o-%$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+-&- .OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-Oo- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-OO- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-oO- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-OO%%OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-o-%$OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO---%$OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO--% #OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOX$@OOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *ovr_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOoO+=+OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOo=#===+OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo=.OO@X=OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo#OO* #X @OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+=.XX+=#* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO+=O=.=OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOO#.=+OO@ $OOOOOOOOOOOOOO", +"OOOOOOOOOooooooooo&O.#+#XooooOOOOOOOOOOO", +"OOOOOOOOO&OOoOoOoOOOOOOOOOoO%@OOOOOOOOOO", +"OOOOOOOOoO==================X*@OOOOOOOOO", +"OOOOOOOO&===================X% @OOOOOOOO", +"OOOOOOOO&==%*%*%*%*%*%*%*%*==% *OOOOOOOO", +"OOOOOOOoO==%*%%*%%*%%*%*%*%==X* OOOOOOOO", +"OOOOOOO&======================* $OOOOOOO", +"OOOOOO+&=== ===% *OOOOOOO", +"OOOOOOoO======================X* OOOOOOO", +"OOOOOO&=======================X% $OOOOOO", +"OOOOOOo========================% *OOOOOO", +"OOOOOoO===*%X=====%%======%%===X* OOOOOO", +"OOOOO&==% %==% *==== %==* $OOOOO", +"OOOOO&== *==**== **% *=X% %%* ==% *OOOOO", +"OOOOoO==%%==* =* ===% == %=== %=X* OOOOO", +"OOOO&=======% =**===% %X %X==* =X% $OOOO", +"OOOO&======% %= %==== %% ====* ==% *OOOO", +"OOOoO=====% *== *==== %* ====% ==X* OOOO", +"OOO&====XX *===**===% X% X=== *===* $OOO", +"OO+&====X *====* ===% == *=== %===% *OOO", +"OOoO===% %*%*== *** %==% %** ====X* OOO", +"OO&====% ==X *====* %====X% $OO", +"OO&================================% *OO", +"OOo===X============================% OO", +"OO=XXXXXXXXXXXX%XXXX%X%X%XXXXX%X%X%% $O", +"OOO=%****************************** $O", +"OOOO$ $O", +"OOOOO* *OO", +"OOOOOOOOOOoOOoOOoOOoOOoOOoOOoOOoOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *pet_mark_xpm[] = { +/* width height ncolors chars_per_pixel */ +"8 7 2 1", +/* colors */ +". c None", +" c #FF0000", +/* pixels */ +"........", +".. . .", +". ", +". ", +".. .", +"... ..", +".... ..." +}; +/* XPM */ +static const char *pet_mark_small_xpm[] = { +/* width height ncolors chars_per_pixel */ +"5 5 2 1", +/* colors */ +". c None", +"X c #FF0000", +/* pixels */ +".X.X.", +"XXXXX", +".XXX.", +"..X.." +}; +/* XPM */ +static const char *satiated_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 23 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #F8B090", +"o c #5C7A7A", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #914700", +"* c #B64700", +"= c #909090", +"- c #788C8C", +"; c #606060", +": c #406868", +"> c #FFFFFF", +", c #CEAA90", +"< c #DADAB6", +"1 c #303030", +"2 c #FFB691", +"3 c #6C91B6", +"4 c #F88C48", +/* pixels */ +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"################<<<<<<<<################", +"##############<<<<<<<<<<$#$$$###########", +"############<<<<<<<<<<$:31:3:###########", +"###########<<<<<<%42<<#:3:133-##########", +"##########<<<<<%<<;;=o$131:33;##########", +"#########<<<<<<,1 ::31:33;,#########", +"########<<<<<<, =<<<<.13:133;<=########", +"########<<<%2, 1<<<<<<#333:33;<,=#######", +"#######<<<3-=<33;<<@o######", +"#######O<<<<<,#<<<<<<<<.3:<3-;<, =######", +"########<<<<2<<<<<<<<<>#31<33o<11#######", +"########O<<<<44<>O>>>>>#3:<3.;- =#######", +"##########<<<4<<<<><><<$3:<331 ;<#######", +"##########<<<<<<<%2<<<<$3:<33 1#########", +"###########O,<<<<<<<<<<#31<331##########", +"#############.<<<<<<<<<$3:133;##########", +"##############=;=,<<<<,o 1;;=##########", +"###############<=;1 1;=##############", +"#################<# c #B64700", +", c #909090", +"< c #788C8C", +"1 c #606060", +"2 c #406868", +"3 c #FFFFFF", +"4 c #CEAA90", +"5 c #DADAB6", +"6 c #303030", +"7 c #F86800", +"8 c #FFB691", +"9 c #6C91B6", +"0 c #F88C48", +"q c #0000FF", +/* pixels */ +"****************************************", +"*************#333333333#****************", +"***********##33333#333333#**************", +"**********#33333#33333#33*==************", +"*********#33##33-;-3#3333399************", +"********#33#33#3-@ 33333#33=.***********", +"********#3*#33-;;;;;-33333#99***********", +"*******#3*3333-;;;;@ 33#333#9=**********", +"*******#333#33#3-;-33#*##33399**********", +"******#3#3333333-@-#333#9933*9=*********", +"******#333#33#3333333#333*9999=*********", +"******#333333333#3#33333333*999*********", +"******#3#33#33333333#33#3333#9=*********", +"******#333334>&&:&&>::44,3#33#9*********", +"******#33*::&41OOO6:4O 0::4433=*********", +"******#3:>,0:O0O1O+O:OXO,O+2+OOo4<+1104:>:#*********", +"******.&:1OOO,14X2O48:O80,440:,*********", +"******4::>OOO%8-X4O4%O,84+O0X&>=********", +"******.::>,O 99*X+<$,+.o*1O4&0:*********", +"******>:0&4O5qq9#10OO3qq9,+X:1:*********", +"****=>,,::,O4qq9X+O>O-qq9O2X0,>*********", +"******4:>OOOO48882OOOO+4OOO07*4*********", +"******4*,4OO+OXX3O5************", +"*********=0%,OO,>:>>O +1OO4*************", +"**********=%+OO:::1:::6+:7**************", +"***********7&OO:O+O,O1OO+1**************", +"***********40OO,O4:OOO11O<5*************", +"**********=4 +O1O2+O2+O0O***************", +"************72O+1+21-OOO%5**************", +"************0%1OOOO+O+174***************", +"*************%%O,OO1407-=***************", +"**************-$>%0%:74*****************", +"****************54044*=*****************", +"*****************=*=********************" +}; +/* XPM */ +static const char *sick_il_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 23 1", +/* colors */ +" c #F85848", +". c #949E9E", +"X c #F8B090", +"o c #E00028", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #FF0000", +"* c #914700", +"= c #B64700", +"- c #909090", +"; c #606060", +": c #FFFFFF", +"> c #CEAA90", +", c #DADAB6", +"< c #F86800", +"1 c #FFB691", +"2 c #6C91B6", +"3 c #F88C48", +"4 c #0000FF", +/* pixels */ +"########################################", +"#############O:::::::::O################", +"###########OO:::::O::::::O##############", +"##########O:::::O:::::O::#$$############", +"#########O::OO::%&%:O:::::22############", +"########O::O::O:%o :::::O::$.###########", +"########O:#O::%&&&&&%:::::O22###########", +"#######O:#::::%&&&&o ::O:::O2$##########", +"#######O:::O::O:%&%::O#OO:::22##########", +"######O:O:::::::%o%O:::O22::#2$#########", +"######O:::O::O:::::::O:::#2222$#########", +"######O:::::::::O:O::::::::#222#########", +"######O:O::O::::::::O::O::::O2$#########", +"######O:::::>=@@=**=**>>-:O::O2#########", +"######O::#**@3>%* ;=>=3;<@>>::$#########", +"######O:** >=>XXXX1X >>+>%*%*;O#########", +"######O3@*,X%XXXXXXX>X%XX >*=*O#########", +"######.@@3XXXXXXXXXXXXXXX>X>3*-#########", +"######>***>X% >XXXXX3XXXXXX%>*=>########", +"######.***> 22#XXX<%X22#XXX@+;#########", +"######=*3@X>O442OXX==%XX11111O1+%X111XX%<#>#########", +"######.,;XXXXXX1O1X%3XXXXX%+3###########", +"########3=XXXXXX:XXXXXXXXX+<>$##########", +"########>+XXXXXX%-3->XXXX%+<############", +"#########%3XXXXXX>- -%XXX%<%$###########", +"#########$############", +"##########+%XXXXXXXXXXXX%+<#############", +"##########%3XXX>=****3XX%<%#############", +"##########>+XXX**=3-*@3>3+##############", +"###########<%XX >XX%X;%X3+##############", +"###########%3XX>XX++XXXX<%$#############", +"##########$>+XXXXXXXXXXX<###############", +"############<%XXXXXXXXX3+###############", +"###########$%+XXXXXXXX%<>###############", +"#############++XXXXXX%<%$###############", +"#############$%<<3333<%#################", +"#################%3>>$##################", +"#################$#$####################" +}; +/* XPM */ +static const char *slt_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO&&&&&&&X @OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo======X*OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOoO======X**OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO&====X===% $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOoo==%* %==%* OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOoO=% % =X* @OOOOOOOOOOOOO", +"OOOOOOOOOOOOO&==**==% %=% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOoo==%%==* %=X OOOOOOOOOOOOO", +"OOOOOOOOOOOOoO=====* X==X* @OOOOOOOOOOOO", +"OOOOOOOOOOOO&=====* %====% $OOOOOOOOOOOO", +"OOOOOOOOOOOoo==== X=====%* OOOOOOOOOOOO", +"OOOOOOOOOOOo+===* *%*%%==X* @OOOOOOOOOOO", +"OOOOOOOOOOO&==== %===% $OOOOOOOOOOO", +"OOOOOOOOOOO&==============% OOOOOOOOOOO", +"OOOOOOOOOOO==============X% @OOOOOOOOOO", +"OOOOOOOOOOO+%%%%%%%%%%%%%% $OOOOOOOOOO", +"OOOOOOOOOOOOO% $OOOOOOOOOO", +"OOOOOOOOOOOOO@ @OOOOOOOOOO", +"OOOOOOOOOOOOOO@$$$$$$$$$$$$$@OOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static const char *str_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 17 1", +/* colors */ +" c #000000", +". c #F8B090", +"X c #5C7A7A", +"o c #F87A24", +"O c #7C3400", +"+ c None", +"@ c #B0B0B0", +"# c #F89E6C", +"$ c #B64700", +"% c #909090", +"& c #606060", +"* c #CEAA90", +"= c #DADAB6", +"- c #303030", +"; c #F86800", +": c #FFB691", +"> c #F88C48", +/* pixels */ +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"+++++++++++++++++++*>*>#++++++++++++++++", +"++++++++++++++++*#o>..*#o*++++++++++++++", +"+++++++++++++++o#.#>.....o++++++++++++++", +"+++++++++++++++;>;#.o.>..#$X++++++++++++", +"+++++++++++++++o#>.o.>:...o %++++++++++", +"++++++++++++++o##>>#o##>..#O -++++++++++", +"++++++++++++++>#.oo#>..>...O ++++++++++", +"++++++++++++++*o##.>>;o#...o ++++++++++", +"+++++++++++++++*;o#........>- &+++++++++", +"+++++++++++++++++#>>;o......O -+++++++++", +"+++++++++++++++++@+@+o>.....$ +++++++++", +"+++++++++++++++++++++*;.#...>- %++++++++", +"++++++++++++++++++++++;>o....$ &++++++++", +"++++++++++++++++++++++#>>....>- %+++++++", +"+++++++++++++++++++++++;#>....; -+++++++", +"+++++++++++++++++++++++o#>....>O %++++++", +"+++++++++++++++++++++++*>o.....; -++++++", +"+++++++++++++#>**+++++++;#.....>O %+++++", +"+o#+++++++*o;>>>>o#+++++o##.....; -+++++", +"+:#o*++++oo#..*..*>;*+++#>#.....>O %++++", +"+:=#o#+*;>.:==:....#;*++@o.......; &++++", +"+::..>;o#.=::::......o*++;.......>O ++++", +"+.....#o.:.=:.........o#+;........$ ++++", +"+......#o..:...........#o;>.......o &+++", +"+........#..............*>o......:o- +++", +"+..................#o>#...#o.......O +++", +"+...............>o>#.......#>......O &++", +"+..................................o -++", +"+..................................> ++", +"+..................................> ++", +"+.................................#$ &+", +"+................................>$ &+", +"+..#>$o>#..............#>;>>>oOOO- ++", +"+...#O OOOOO$>>>>>>>$OO %++", +"+...o -&&++++", +"+..#O -&&%++++++++++", +"++++++++++++++++++++++++++++++++++++++++" +}; +/* XPM */ +static const char *stunned_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 12 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #303030", +"* c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOO&$OOOO@OOO@@OO@OOOOOOOOOOOOO", +"OOOOOOOOOOO@& $OO@&&$$@ O@$$OOOOOOOOOOOO", +"OOOOOOOOOOOO$$ @@@$ &&OOO@$OOOOOOOOOOOO", +"OOOOOOOOOOOOO@@&$$$$&O$OO$O &@O@OOOOOOOO", +"OOOOOO@@@@@@OO@$$O$&$@@OO& &&$O&OOOOOOO", +"OOOOOO&&&& & $ &&@$ &O@$& &&&$ & $OOOOOO", +"OOOOOO$&OO &&&$ $$ $& $$&$&&&OOOOOOO", +"OOOOOO@@O@$ &+ # &O$$ $$&O@OOOOO", +"OOOOOOOO@X%$ %& %% & && $$@@@@OOOO", +"OOOOOOO+$$@+ &%%%&%& & &@OOO&&OOO", +"OOOOOO.Xo%+ &&%%%%%&& & OO@$&&OOO", +"OOOOO++ $$&&$ && %&%%& &O@&$&OOOO", +"OOOOO####$ X&&& && &%& & &&OOOO", +"OOOO++#.+## $&# %& & & &$ OOOO", +"OOOO#+++.@&%&& &#&%& & $ @OOOOOO", +"OOOO##....#+$#@%#& $%$&@&$$% & X##$@OOOO", +"OOOO.+#+.+@#+#+$&$X#%&%.+& %&#++.$&OOOOO", +"OOOOO#+#+.+++#$$%&++&X+X#&#+&+&##+ &OOOO", +"OOOOO#..#OO#+@%#o##X.@..++.+$&+#+#& @OOO", +"OOOOO+#.#O+#+#O@++@$$##+##++###+.#& $OOO", +"OOOOOOXX+#+#+#o.@%&$++#+..##.#+### &OOO", +"OOOOOOOX#.#X+#+#+##&#+..+####%XX%% OOO", +"OOOOOOOO+%%X.#+#+#.++#+#+#+.X++*.% &OOO", +"OOOOOOOOO@& &##+#+.O####.+XX%%%%#% $OOO", +"OOOOOOOOOOO. %X.+.#+++XXX*.+++#X $OOO", +"OOOOOOOOOOOO@& %%X..#X%#.####%X& $OOO", +"OOOOOOOOOOOOOO@$ &XX%%%*.#X%###*& OOOO", +"OOOOOOOOOOOOOOOOOO+%%%*%%#.+.#*& @OOOO", +"OOOOOOOOOOOOOOOOOOO*%%%**X##X%& OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X*X& @OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X &@OOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%%X& &$$OOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+X%%* @OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOX%%& OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO.X%& $OOOOOOOOOOOOO" +}; +/* XPM */ +static const char *wis_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c None", +"O c #B0B0B0", +"+ c #909090", +"@ c #788C8C", +"# c #606060", +"$ c #406868", +"% c #FFFFFF", +"& c #303030", +"* c #6C91B6", +"= c #0000FF", +/* pixels */ +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooo+#& &#oooooooooooooooooooo", +"oooooooooooo+& #oooooooooooooooooo", +"ooooooooooo+ &====&& &ooooooooooooooooo", +"oooooooooo+ &==& ===%& +ooooooooooooooo", +"ooooooooo+&%=== ===%%o&&oooooooooooooo", +"oooooooo.&%%===& ===%o& #+ooooooooooo", +"oooo&###&&%%*=======$#&ooo#& #+oooooooo", +"ooooo###o+&X$=====& #oo##oooo+######oooo", +"oooooooooooo######@oo##ooooooooooooooooo", +"oooooooooooooOoOoOo##ooooooooooooooooooo", +"ooooooooooooooooo+#+ooo+&#oooooooooooooo", +"ooooooooooooooooooooooo#oooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo" +}; diff --git a/include/qtext.h b/include/qtext.h new file mode 100644 index 0000000..0b149c2 --- /dev/null +++ b/include/qtext.h @@ -0,0 +1,112 @@ +/* SCCS Id: @(#)qtext.h 3.4 1997/02/02 */ +/* Copyright (c) Mike Stephenson 1991. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef QTEXT_H +#define QTEXT_H + +#define N_HDR 16 /* Maximum number of categories */ + /* (i.e., num roles + 1) */ +#define LEN_HDR 3 /* Maximum length of a category name */ + +struct qtmsg { + int msgnum; + char delivery; + long offset, + size; +}; + +#ifdef MAKEDEFS_C /***** MAKEDEFS *****/ + +#define N_MSG 100 /* arbitrary */ + +struct msghdr { + int n_msg; + struct qtmsg qt_msg[N_MSG]; +}; + +struct qthdr { + int n_hdr; + char id[N_HDR][LEN_HDR]; + long offset[N_HDR]; +}; + +/* Error message macros */ +#define CREC_IN_MSG "Control record encountered during message - line %d\n" +#define DUP_MSG "Duplicate message number at line %d\n" +#define END_NOT_IN_MSG "End record encountered before message - line %d\n" +#define TEXT_NOT_IN_MSG "Text encountered outside message - line %d\n" +#define UNREC_CREC "Unrecognized Control record at line %d\n" +#define OUT_OF_HEADERS "Too many message types (line %d)\nAdjust N_HDR in qtext.h and recompile.\n" +#define OUT_OF_MESSAGES "Too many messages in class (line %d)\nAdjust N_MSG in qtext.h and recompile.\n" + + +#else /***** !MAKEDEFS *****/ + +struct qtlists { + struct qtmsg *common, +#if 0 /* UNUSED but available */ + *chrace, +#endif + *chrole; +}; + + +/* + * Quest message defines. Used in quest.c to trigger off "realistic" + * dialogue to the player. + */ +#define QT_FIRSTTIME 1 +#define QT_NEXTTIME 2 +#define QT_OTHERTIME 3 + +#define QT_GUARDTALK 5 /* 5 random things guards say before quest */ +#define QT_GUARDTALK2 10 /* 5 random things guards say after quest */ + +#define QT_FIRSTLEADER 15 +#define QT_NEXTLEADER 16 +#define QT_OTHERLEADER 17 +#define QT_LASTLEADER 18 +#define QT_BADLEVEL 19 +#define QT_BADALIGN 20 +#define QT_ASSIGNQUEST 21 + +#define QT_ENCOURAGE 25 /* 1-10 random encouragement messages */ + +#define QT_FIRSTLOCATE 35 +#define QT_NEXTLOCATE 36 + +#define QT_FIRSTGOAL 40 +#define QT_NEXTGOAL 41 + +#define QT_FIRSTNEMESIS 50 +#define QT_NEXTNEMESIS 51 +#define QT_OTHERNEMESIS 52 +#define QT_NEMWANTSIT 53 /* you somehow got the artifact */ + +#define QT_DISCOURAGE 60 /* 1-10 random maledictive messages */ + +#define QT_GOTIT 70 + +#define QT_KILLEDNEM 80 +#define QT_OFFEREDIT 81 +#define QT_OFFEREDIT2 82 + +#define QT_POSTHANKS 90 +#define QT_HASAMULET 91 + +/* + * Message defines for common text used in maledictions. + */ +#define COMMON_ID "-" /* Common message id value */ + +#define QT_ANGELIC 10 +#define QTN_ANGELIC 10 + +#define QT_DEMONIC 30 +#define QTN_DEMONIC 20 + +#define QT_BANISHED 60 +#endif /***** !MAKEDEFS *****/ + +#endif /* QTEXT_H */ diff --git a/include/qttableview.h b/include/qttableview.h new file mode 100644 index 0000000..f6e5e98 --- /dev/null +++ b/include/qttableview.h @@ -0,0 +1,251 @@ +/********************************************************************** +** $Id: qttableview.h,v 1.2 2002/03/09 03:13:13 jwalz Exp $ +** +** Definition of QtTableView class +** +** Created : 941115 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file contains a class moved out of the Qt GUI Toolkit API. It +** may be used, distributed and modified without limitation. +** +**********************************************************************/ + +#ifndef QTTABLEVIEW_H +#define QTTABLEVIEW_H + +#ifndef QT_H +#include +#endif // QT_H + +#ifndef QT_NO_QTTABLEVIEW + +class QScrollBar; +class QCornerSquare; + + +class QtTableView : public QFrame +{ + Q_OBJECT +public: + virtual void setBackgroundColor( const QColor & ); + virtual void setPalette( const QPalette & ); + void show(); + + void repaint( bool erase=TRUE ); + void repaint( int x, int y, int w, int h, bool erase=TRUE ); + void repaint( const QRect &, bool erase=TRUE ); + +protected: + QtTableView( QWidget *parent=0, const char *name=0, WFlags f=0 ); + ~QtTableView(); + + int numRows() const; + virtual void setNumRows( int ); + int numCols() const; + virtual void setNumCols( int ); + + int topCell() const; + virtual void setTopCell( int row ); + int leftCell() const; + virtual void setLeftCell( int col ); + virtual void setTopLeftCell( int row, int col ); + + int xOffset() const; + virtual void setXOffset( int ); + int yOffset() const; + virtual void setYOffset( int ); + virtual void setOffset( int x, int y, bool updateScrBars = TRUE ); + + virtual int cellWidth( int col ); + virtual int cellHeight( int row ); + int cellWidth() const; + int cellHeight() const; + virtual void setCellWidth( int ); + virtual void setCellHeight( int ); + + virtual int totalWidth(); + virtual int totalHeight(); + + uint tableFlags() const; + bool testTableFlags( uint f ) const; + virtual void setTableFlags( uint f ); + void clearTableFlags( uint f = ~0 ); + + bool autoUpdate() const; + virtual void setAutoUpdate( bool ); + + void updateCell( int row, int column, bool erase=TRUE ); + + QRect cellUpdateRect() const; + QRect viewRect() const; + + int lastRowVisible() const; + int lastColVisible() const; + + bool rowIsVisible( int row ) const; + bool colIsVisible( int col ) const; + + QScrollBar *verticalScrollBar() const; + QScrollBar *horizontalScrollBar() const; + +private slots: + void horSbValue( int ); + void horSbSliding( int ); + void horSbSlidingDone(); + void verSbValue( int ); + void verSbSliding( int ); + void verSbSlidingDone(); + +protected: + virtual void paintCell( QPainter *, int row, int col ) = 0; + virtual void setupPainter( QPainter * ); + + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + + int findRow( int yPos ) const; + int findCol( int xPos ) const; + + bool rowYPos( int row, int *yPos ) const; + bool colXPos( int col, int *xPos ) const; + + int maxXOffset(); + int maxYOffset(); + int maxColOffset(); + int maxRowOffset(); + + int minViewX() const; + int minViewY() const; + int maxViewX() const; + int maxViewY() const; + int viewWidth() const; + int viewHeight() const; + + void scroll( int xPixels, int yPixels ); + void updateScrollBars(); + void updateTableSize(); + +private: + void coverCornerSquare( bool ); + void snapToGrid( bool horizontal, bool vertical ); + virtual void setHorScrollBar( bool on, bool update = TRUE ); + virtual void setVerScrollBar( bool on, bool update = TRUE ); + void updateView(); + int findRawRow( int yPos, int *cellMaxY, int *cellMinY = 0, + bool goOutsideView = FALSE ) const; + int findRawCol( int xPos, int *cellMaxX, int *cellMinX = 0, + bool goOutsideView = FALSE ) const; + int maxColsVisible() const; + + void updateScrollBars( uint ); + void updateFrameSize(); + + void doAutoScrollBars(); + void showOrHideScrollBars(); + + int nRows; + int nCols; + int xOffs, yOffs; + int xCellOffs, yCellOffs; + short xCellDelta, yCellDelta; + short cellH, cellW; + + uint eraseInPaint : 1; + uint verSliding : 1; + uint verSnappingOff : 1; + uint horSliding : 1; + uint horSnappingOff : 1; + uint coveringCornerSquare : 1; + uint sbDirty : 8; + uint inSbUpdate : 1; + + uint tFlags; + QRect cellUpdateR; + + QScrollBar *vScrollBar; + QScrollBar *hScrollBar; + QCornerSquare *cornerSquare; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QtTableView( const QtTableView & ); + QtTableView &operator=( const QtTableView & ); +#endif +}; + + +const uint Tbl_vScrollBar = 0x00000001; +const uint Tbl_hScrollBar = 0x00000002; +const uint Tbl_autoVScrollBar = 0x00000004; +const uint Tbl_autoHScrollBar = 0x00000008; +const uint Tbl_autoScrollBars = 0x0000000C; + +const uint Tbl_clipCellPainting = 0x00000100; +const uint Tbl_cutCellsV = 0x00000200; +const uint Tbl_cutCellsH = 0x00000400; +const uint Tbl_cutCells = 0x00000600; + +const uint Tbl_scrollLastHCell = 0x00000800; +const uint Tbl_scrollLastVCell = 0x00001000; +const uint Tbl_scrollLastCell = 0x00001800; + +const uint Tbl_smoothHScrolling = 0x00002000; +const uint Tbl_smoothVScrolling = 0x00004000; +const uint Tbl_smoothScrolling = 0x00006000; + +const uint Tbl_snapToHGrid = 0x00008000; +const uint Tbl_snapToVGrid = 0x00010000; +const uint Tbl_snapToGrid = 0x00018000; + + +inline int QtTableView::numRows() const +{ return nRows; } + +inline int QtTableView::numCols() const +{ return nCols; } + +inline int QtTableView::topCell() const +{ return yCellOffs; } + +inline int QtTableView::leftCell() const +{ return xCellOffs; } + +inline int QtTableView::xOffset() const +{ return xOffs; } + +inline int QtTableView::yOffset() const +{ return yOffs; } + +inline int QtTableView::cellHeight() const +{ return cellH; } + +inline int QtTableView::cellWidth() const +{ return cellW; } + +inline uint QtTableView::tableFlags() const +{ return tFlags; } + +inline bool QtTableView::testTableFlags( uint f ) const +{ return (tFlags & f) != 0; } + +inline QRect QtTableView::cellUpdateRect() const +{ return cellUpdateR; } + +inline bool QtTableView::autoUpdate() const +{ return isUpdatesEnabled(); } + +inline void QtTableView::repaint( bool erase ) +{ repaint( 0, 0, width(), height(), erase ); } + +inline void QtTableView::repaint( const QRect &r, bool erase ) +{ repaint( r.x(), r.y(), r.width(), r.height(), erase ); } + +inline void QtTableView::updateScrollBars() +{ updateScrollBars( 0 ); } + + +#endif // QT_NO_QTTABLEVIEW + +#endif // QTTABLEVIEW_H diff --git a/include/quest.h b/include/quest.h new file mode 100644 index 0000000..98b9e3a --- /dev/null +++ b/include/quest.h @@ -0,0 +1,41 @@ +/* SCCS Id: @(#)quest.h 3.4 1992/11/15 */ +/* Copyright (c) Mike Stephenson 1991. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef QUEST_H +#define QUEST_H + +struct q_score { /* Quest "scorecard" */ + Bitfield(first_start,1); /* only set the first time */ + Bitfield(met_leader,1); /* has met the leader */ + Bitfield(not_ready,3); /* rejected due to alignment, etc. */ + Bitfield(pissed_off,1); /* got the leader angry */ + Bitfield(got_quest,1); /* got the quest assignment */ + + Bitfield(first_locate,1); /* only set the first time */ + Bitfield(met_intermed,1); /* used if the locate is a person. */ + Bitfield(got_final,1); /* got the final quest assignment */ + + Bitfield(made_goal,3); /* # of times on goal level */ + Bitfield(met_nemesis,1); /* has met the nemesis before */ + Bitfield(killed_nemesis,1); /* set when the nemesis is killed */ + Bitfield(in_battle,1); /* set when nemesis fighting you */ + + Bitfield(cheater,1); /* set if cheating detected */ + Bitfield(touched_artifact,1); /* for a special message */ + Bitfield(offered_artifact,1); /* offered to leader */ + Bitfield(got_thanks,1); /* final message from leader */ + + /* keep track of leader presence/absence even if leader is + polymorphed, raised from dead, etc */ + Bitfield(leader_is_dead,1); + unsigned leader_m_id; +}; + +#define MAX_QUEST_TRIES 7 /* exceed this and you "fail" */ +#define MIN_QUEST_ALIGN 20 /* at least this align.record to start */ + /* note: align 20 matches "pious" as reported by enlightenment (cmd.c) */ +#define MIN_QUEST_LEVEL 14 /* at least this u.ulevel to start */ + /* note: exp.lev. 14 is threshold level for 5th rank (class title, role.c) */ + +#endif /* QUEST_H */ diff --git a/include/rect.h b/include/rect.h new file mode 100644 index 0000000..a7deafb --- /dev/null +++ b/include/rect.h @@ -0,0 +1,13 @@ +/* SCCS Id: @(#)rect.h 3.4 1990/02/22 */ +/* Copyright (c) 1990 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef RECT_H +#define RECT_H + +typedef struct nhrect { + xchar lx, ly; + xchar hx, hy; +} NhRect; + +#endif /* RECT_H */ diff --git a/include/region.h b/include/region.h new file mode 100644 index 0000000..e328956 --- /dev/null +++ b/include/region.h @@ -0,0 +1,67 @@ +/* SCCS Id: @(#)region.h 3.4 2002/10/15 */ +/* Copyright (c) 1996 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef REGION_H +#define REGION_H + +/* generic callback function */ + +typedef boolean FDECL((*callback_proc), (genericptr_t, genericptr_t)); + +/* + * Overload the old player_inside field with two values, coded in such + * a way as to retain compatibility with 3.4.0 save and bones files; + * this relies on the fact that nethack's `boolean' is really stored + * in a `char' (or bigger type) rather than in a single bit. + * + * 3.4.1 save and bones files will be correct. + * 3.4.0 save files restored under 3.4.1 will be correct. + * 3.4.0 bones files used with 3.4.1 will continue to have the minor + * 3.4.0 bug of falsely claiming that the current game's hero is + * responsible for the dead former hero's stinking clouds. + */ +#define REG_HERO_INSIDE 1 +#define REG_NOT_HEROS 2 +#define hero_inside(r) ((unsigned)(r)->player_flags & REG_HERO_INSIDE) +#define heros_fault(r) (!((unsigned)(r)->player_flags & REG_NOT_HEROS)) +#define set_hero_inside(r) ((r)->player_flags |= REG_HERO_INSIDE) +#define clear_hero_inside(r) ((r)->player_flags &= ~REG_HERO_INSIDE) +#define set_heros_fault(r) ((r)->player_flags &= ~REG_NOT_HEROS) +#define clear_heros_fault(r) ((r)->player_flags |= REG_NOT_HEROS) + +typedef struct { + NhRect bounding_box; /* Bounding box of the region */ + NhRect *rects; /* Rectangles composing the region */ + short nrects; /* Number of rectangles */ + boolean attach_2_u; /* Region attached to player ? */ + unsigned int attach_2_m; /* Region attached to monster ? */ + /*struct obj *attach_2_o;*/ /* Region attached to object ? UNUSED YET */ + const char* enter_msg; /* Message when entering */ + const char* leave_msg; /* Message when leaving */ + short ttl; /* Time to live. -1 is forever */ + short expire_f; /* Function to call when region's ttl expire */ + short can_enter_f; /* Function to call to check wether the player + can, or can not, enter the region */ + short enter_f; /* Function to call when the player enters*/ + short can_leave_f; /* Function to call to check wether the player + can, or can not, leave the region */ + short leave_f; /* Function to call when the player leaves */ + short inside_f; /* Function to call every turn if player's + inside */ + boolean player_flags; /* (see above) */ + unsigned int* monsters; /* Monsters currently inside this region */ + short n_monst; /* Number of monsters inside this region */ + short max_monst; /* Maximum number of monsters that can be + listed without having to grow the array */ +#define MONST_INC 5 + + /* Should probably do the same thing about objects */ + + boolean visible; /* Is the region visible ? */ + int glyph; /* Which glyph to use if visible */ + genericptr_t arg; /* Optional user argument (Ex: strength of + force field, damage of a fire zone, ...*/ +} NhRegion; + +#endif /* REGION_H */ diff --git a/include/rm.h b/include/rm.h new file mode 100644 index 0000000..7996cac --- /dev/null +++ b/include/rm.h @@ -0,0 +1,526 @@ +/* SCCS Id: @(#)rm.h 3.4 1999/12/12 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef RM_H +#define RM_H + +/* + * The dungeon presentation graphics code and data structures were rewritten + * and generalized for NetHack's release 2 by Eric S. Raymond (eric@snark) + * building on Don G. Kneller's MS-DOS implementation. See drawing.c for + * the code that permits the user to set the contents of the symbol structure. + * + * The door representation was changed by Ari Huttunen(ahuttune@niksula.hut.fi) + */ + +/* + * TLCORNER TDWALL TRCORNER + * +- -+- -+ + * | | | + * + * TRWALL CROSSWALL TLWALL HWALL + * | | | + * +- -+- -+ --- + * | | | + * + * BLCORNER TUWALL BRCORNER VWALL + * | | | | + * +- -+- -+ | + */ + +/* Level location types */ +#define STONE 0 +#define VWALL 1 +#define HWALL 2 +#define TLCORNER 3 +#define TRCORNER 4 +#define BLCORNER 5 +#define BRCORNER 6 +#define CROSSWALL 7 /* For pretty mazes and special levels */ +#define TUWALL 8 +#define TDWALL 9 +#define TLWALL 10 +#define TRWALL 11 +#define DBWALL 12 +#define TREE 13 /* KMH */ +#define SDOOR 14 +#define SCORR 15 +#define POOL 16 +#define MOAT 17 /* pool that doesn't boil, adjust messages */ +#define WATER 18 +#define DRAWBRIDGE_UP 19 +#define LAVAPOOL 20 +#define IRONBARS 21 /* KMH */ +#define DOOR 22 +#define CORR 23 +#define ROOM 24 +#define STAIRS 25 +#define LADDER 26 +#define FOUNTAIN 27 +#define THRONE 28 +#define SINK 29 +#define GRAVE 30 +#define ALTAR 31 +#define ICE 32 +#define DRAWBRIDGE_DOWN 33 +#define AIR 34 +#define CLOUD 35 + +#define MAX_TYPE 36 +#define INVALID_TYPE 127 + +/* + * Avoid using the level types in inequalities: + * these types are subject to change. + * Instead, use one of the macros below. + */ +#define IS_WALL(typ) ((typ) && (typ) <= DBWALL) +#define IS_STWALL(typ) ((typ) <= DBWALL) /* STONE <= (typ) <= DBWALL */ +#define IS_ROCK(typ) ((typ) < POOL) /* absolutely nonaccessible */ +#define IS_DOOR(typ) ((typ) == DOOR) +#define IS_TREE(typ) ((typ) == TREE || \ + (level.flags.arboreal && (typ) == STONE)) +#define ACCESSIBLE(typ) ((typ) >= DOOR) /* good position */ +#define IS_ROOM(typ) ((typ) >= ROOM) /* ROOM, STAIRS, furniture.. */ +#define ZAP_POS(typ) ((typ) >= POOL) +#define SPACE_POS(typ) ((typ) > DOOR) +#define IS_POOL(typ) ((typ) >= POOL && (typ) <= DRAWBRIDGE_UP) +#define IS_THRONE(typ) ((typ) == THRONE) +#define IS_FOUNTAIN(typ) ((typ) == FOUNTAIN) +#define IS_SINK(typ) ((typ) == SINK) +#define IS_GRAVE(typ) ((typ) == GRAVE) +#define IS_ALTAR(typ) ((typ) == ALTAR) +#define IS_DRAWBRIDGE(typ) ((typ) == DRAWBRIDGE_UP || (typ) == DRAWBRIDGE_DOWN) +#define IS_FURNITURE(typ) ((typ) >= STAIRS && (typ) <= ALTAR) +#define IS_AIR(typ) ((typ) == AIR || (typ) == CLOUD) +#define IS_SOFT(typ) ((typ) == AIR || (typ) == CLOUD || IS_POOL(typ)) + +/* + * The screen symbols may be the default or defined at game startup time. + * See drawing.c for defaults. + * Note: {ibm|dec}_graphics[] arrays (also in drawing.c) must be kept in synch. + */ + +/* begin dungeon characters */ + +#define S_stone 0 +#define S_vwall 1 +#define S_hwall 2 +#define S_tlcorn 3 +#define S_trcorn 4 +#define S_blcorn 5 +#define S_brcorn 6 +#define S_crwall 7 +#define S_tuwall 8 +#define S_tdwall 9 +#define S_tlwall 10 +#define S_trwall 11 +#define S_ndoor 12 +#define S_vodoor 13 +#define S_hodoor 14 +#define S_vcdoor 15 /* closed door, vertical wall */ +#define S_hcdoor 16 /* closed door, horizontal wall */ +#define S_bars 17 /* KMH -- iron bars */ +#define S_tree 18 /* KMH */ +#define S_room 19 +#define S_corr 20 +#define S_litcorr 21 +#define S_upstair 22 +#define S_dnstair 23 +#define S_upladder 24 +#define S_dnladder 25 +#define S_altar 26 +#define S_grave 27 +#define S_throne 28 +#define S_sink 29 +#define S_fountain 30 +#define S_pool 31 +#define S_ice 32 +#define S_lava 33 +#define S_vodbridge 34 +#define S_hodbridge 35 +#define S_vcdbridge 36 /* closed drawbridge, vertical wall */ +#define S_hcdbridge 37 /* closed drawbridge, horizontal wall */ +#define S_air 38 +#define S_cloud 39 +#define S_water 40 + +/* end dungeon characters, begin traps */ + +#define S_arrow_trap 41 +#define S_dart_trap 42 +#define S_falling_rock_trap 43 +#define S_squeaky_board 44 +#define S_bear_trap 45 +#define S_land_mine 46 +#define S_rolling_boulder_trap 47 +#define S_sleeping_gas_trap 48 +#define S_rust_trap 49 +#define S_fire_trap 50 +#define S_pit 51 +#define S_spiked_pit 52 +#define S_hole 53 +#define S_trap_door 54 +#define S_teleportation_trap 55 +#define S_level_teleporter 56 +#define S_magic_portal 57 +#define S_web 58 +#define S_statue_trap 59 +#define S_magic_trap 60 +#define S_anti_magic_trap 61 +#define S_polymorph_trap 62 + +/* end traps, begin special effects */ + +#define S_vbeam 63 /* The 4 zap beam symbols. Do NOT separate. */ +#define S_hbeam 64 /* To change order or add, see function */ +#define S_lslant 65 /* zapdir_to_glyph() in display.c. */ +#define S_rslant 66 +#define S_digbeam 67 /* dig beam symbol */ +#define S_flashbeam 68 /* camera flash symbol */ +#define S_boomleft 69 /* thrown boomerang, open left, e.g ')' */ +#define S_boomright 70 /* thrown boomerand, open right, e.g. '(' */ +#define S_ss1 71 /* 4 magic shield glyphs */ +#define S_ss2 72 +#define S_ss3 73 +#define S_ss4 74 + +/* The 8 swallow symbols. Do NOT separate. To change order or add, see */ +/* the function swallow_to_glyph() in display.c. */ +#define S_sw_tl 75 /* swallow top left [1] */ +#define S_sw_tc 76 /* swallow top center [2] Order: */ +#define S_sw_tr 77 /* swallow top right [3] */ +#define S_sw_ml 78 /* swallow middle left [4] 1 2 3 */ +#define S_sw_mr 79 /* swallow middle right [6] 4 5 6 */ +#define S_sw_bl 80 /* swallow bottom left [7] 7 8 9 */ +#define S_sw_bc 81 /* swallow bottom center [8] */ +#define S_sw_br 82 /* swallow bottom right [9] */ + +#define S_explode1 83 /* explosion top left */ +#define S_explode2 84 /* explosion top center */ +#define S_explode3 85 /* explosion top right Ex. */ +#define S_explode4 86 /* explosion middle left */ +#define S_explode5 87 /* explosion middle center /-\ */ +#define S_explode6 88 /* explosion middle right |@| */ +#define S_explode7 89 /* explosion bottom left \-/ */ +#define S_explode8 90 /* explosion bottom center */ +#define S_explode9 91 /* explosion bottom right */ + +/* end effects */ + +#define MAXPCHARS 92 /* maximum number of mapped characters */ +#define MAXDCHARS 41 /* maximum of mapped dungeon characters */ +#define MAXTCHARS 22 /* maximum of mapped trap characters */ +#define MAXECHARS 29 /* maximum of mapped effects characters */ +#define MAXEXPCHARS 9 /* number of explosion characters */ + +struct symdef { + uchar sym; + const char *explanation; +#ifdef TEXTCOLOR + uchar color; +#endif +}; + +extern const struct symdef defsyms[MAXPCHARS]; /* defaults */ +extern uchar showsyms[MAXPCHARS]; +extern const struct symdef def_warnsyms[WARNCOUNT]; + +/* + * Graphics sets for display symbols + */ +#define ASCII_GRAPHICS 0 /* regular characters: '-', '+', &c */ +#define IBM_GRAPHICS 1 /* PC graphic characters */ +#define DEC_GRAPHICS 2 /* VT100 line drawing characters */ +#define MAC_GRAPHICS 3 /* Macintosh drawing characters */ + +/* + * The 5 possible states of doors + */ + +#define D_NODOOR 0 +#define D_BROKEN 1 +#define D_ISOPEN 2 +#define D_CLOSED 4 +#define D_LOCKED 8 +#define D_TRAPPED 16 + +/* + * Some altars are considered as shrines, so we need a flag. + */ +#define AM_SHRINE 8 + +/* + * Thrones should only be looted once. + */ +#define T_LOOTED 1 + +/* + * Trees have more than one kick result. + */ +#define TREE_LOOTED 1 +#define TREE_SWARM 2 + +/* + * Fountains have limits, and special warnings. + */ +#define F_LOOTED 1 +#define F_WARNED 2 +#define FOUNTAIN_IS_WARNED(x,y) (levl[x][y].looted & F_WARNED) +#define FOUNTAIN_IS_LOOTED(x,y) (levl[x][y].looted & F_LOOTED) +#define SET_FOUNTAIN_WARNED(x,y) levl[x][y].looted |= F_WARNED; +#define SET_FOUNTAIN_LOOTED(x,y) levl[x][y].looted |= F_LOOTED; +#define CLEAR_FOUNTAIN_WARNED(x,y) levl[x][y].looted &= ~F_WARNED; +#define CLEAR_FOUNTAIN_LOOTED(x,y) levl[x][y].looted &= ~F_LOOTED; + +/* + * Doors are even worse :-) The special warning has a side effect + * of instantly trapping the door, and if it was defined as trapped, + * the guards consider that you have already been warned! + */ +#define D_WARNED 16 + +/* + * Sinks have 3 different types of loot that shouldn't be abused + */ +#define S_LPUDDING 1 +#define S_LDWASHER 2 +#define S_LRING 4 + +/* + * The four directions for a DrawBridge. + */ +#define DB_NORTH 0 +#define DB_SOUTH 1 +#define DB_EAST 2 +#define DB_WEST 3 +#define DB_DIR 3 /* mask for direction */ + +/* + * What's under a drawbridge. + */ +#define DB_MOAT 0 +#define DB_LAVA 4 +#define DB_ICE 8 +#define DB_FLOOR 16 +#define DB_UNDER 28 /* mask for underneath */ + +/* + * Wall information. + */ +#define WM_MASK 0x07 /* wall mode (bottom three bits) */ +#define W_NONDIGGABLE 0x08 +#define W_NONPASSWALL 0x10 + +/* + * Ladders (in Vlad's tower) may be up or down. + */ +#define LA_UP 1 +#define LA_DOWN 2 + +/* + * Room areas may be iced pools + */ +#define ICED_POOL 8 +#define ICED_MOAT 16 + +/* + * The structure describing a coordinate position. + * Before adding fields, remember that this will significantly affect + * the size of temporary files and save files. + */ +struct rm { + int glyph; /* what the hero thinks is there */ + schar typ; /* what is really there */ + uchar seenv; /* seen vector */ + Bitfield(flags,5); /* extra information for typ */ + Bitfield(horizontal,1); /* wall/door/etc is horiz. (more typ info) */ + Bitfield(lit,1); /* speed hack for lit rooms */ + Bitfield(waslit,1); /* remember if a location was lit */ + Bitfield(roomno,6); /* room # for special rooms */ + Bitfield(edge,1); /* marks boundaries for special rooms*/ +}; + +/* + * Add wall angle viewing by defining "modes" for each wall type. Each + * mode describes which parts of a wall are finished (seen as as wall) + * and which are unfinished (seen as rock). + * + * We use the bottom 3 bits of the flags field for the mode. This comes + * in conflict with secret doors, but we avoid problems because until + * a secret door becomes discovered, we know what sdoor's bottom three + * bits are. + * + * The following should cover all of the cases. + * + * type mode Examples: R=rock, F=finished + * ----- ---- ---------------------------- + * WALL: 0 none hwall, mode 1 + * 1 left/top (1/2 rock) RRR + * 2 right/bottom (1/2 rock) --- + * FFF + * + * CORNER: 0 none trcorn, mode 2 + * 1 outer (3/4 rock) FFF + * 2 inner (1/4 rock) F+- + * F|R + * + * TWALL: 0 none tlwall, mode 3 + * 1 long edge (1/2 rock) F|F + * 2 bottom left (on a tdwall) -+F + * 3 bottom right (on a tdwall) R|F + * + * CRWALL: 0 none crwall, mode 5 + * 1 top left (1/4 rock) R|F + * 2 top right (1/4 rock) -+- + * 3 bottom left (1/4 rock) F|R + * 4 bottom right (1/4 rock) + * 5 top left & bottom right (1/2 rock) + * 6 bottom left & top right (1/2 rock) + */ + +#define WM_W_LEFT 1 /* vertical or horizontal wall */ +#define WM_W_RIGHT 2 +#define WM_W_TOP WM_W_LEFT +#define WM_W_BOTTOM WM_W_RIGHT + +#define WM_C_OUTER 1 /* corner wall */ +#define WM_C_INNER 2 + +#define WM_T_LONG 1 /* T wall */ +#define WM_T_BL 2 +#define WM_T_BR 3 + +#define WM_X_TL 1 /* cross wall */ +#define WM_X_TR 2 +#define WM_X_BL 3 +#define WM_X_BR 4 +#define WM_X_TLBR 5 +#define WM_X_BLTR 6 + +/* + * Seen vector values. The seen vector is an array of 8 bits, one for each + * octant around a given center x: + * + * 0 1 2 + * 7 x 3 + * 6 5 4 + * + * In the case of walls, a single wall square can be viewed from 8 possible + * directions. If we know the type of wall and the directions from which + * it has been seen, then we can determine what it looks like to the hero. + */ +#define SV0 0x1 +#define SV1 0x2 +#define SV2 0x4 +#define SV3 0x8 +#define SV4 0x10 +#define SV5 0x20 +#define SV6 0x40 +#define SV7 0x80 +#define SVALL 0xFF + + + +#define doormask flags +#define altarmask flags +#define wall_info flags +#define ladder flags +#define drawbridgemask flags +#define looted flags +#define icedpool flags + +#define blessedftn horizontal /* a fountain that grants attribs */ +#define disturbed horizontal /* a grave that has been disturbed */ + +struct damage { + struct damage *next; + long when, cost; + coord place; + schar typ; +}; + +struct levelflags { + uchar nfountains; /* number of fountains on level */ + uchar nsinks; /* number of sinks on the level */ + /* Several flags that give hints about what's on the level */ + Bitfield(has_shop, 1); + Bitfield(has_vault, 1); + Bitfield(has_zoo, 1); + Bitfield(has_court, 1); + Bitfield(has_morgue, 1); + Bitfield(has_beehive, 1); + Bitfield(has_barracks, 1); + Bitfield(has_temple, 1); + + Bitfield(has_swamp, 1); + Bitfield(noteleport,1); + Bitfield(hardfloor,1); + Bitfield(nommap,1); + Bitfield(hero_memory,1); /* hero has memory */ + Bitfield(shortsighted,1); /* monsters are shortsighted */ + Bitfield(graveyard,1); /* has_morgue, but remains set */ + Bitfield(is_maze_lev,1); + + Bitfield(is_cavernous_lev,1); + Bitfield(arboreal, 1); /* Trees replace rock */ +}; + +typedef struct +{ + struct rm locations[COLNO][ROWNO]; +#ifndef MICROPORT_BUG + struct obj *objects[COLNO][ROWNO]; + struct monst *monsters[COLNO][ROWNO]; +#else + struct obj *objects[1][ROWNO]; + char *yuk1[COLNO-1][ROWNO]; + struct monst *monsters[1][ROWNO]; + char *yuk2[COLNO-1][ROWNO]; +#endif + struct obj *objlist; + struct obj *buriedobjlist; + struct monst *monlist; + struct damage *damagelist; + struct levelflags flags; +} +dlevel_t; + +extern dlevel_t level; /* structure describing the current level */ + +/* + * Macros for compatibility with old code. Someday these will go away. + */ +#define levl level.locations +#define fobj level.objlist +#define fmon level.monlist + +/* + * Covert a trap number into the defsym graphics array. + * Convert a defsym number into a trap number. + * Assumes that arrow trap will always be the first trap. + */ +#define trap_to_defsym(t) (S_arrow_trap+(t)-1) +#define defsym_to_trap(d) ((d)-S_arrow_trap+1) + +#define OBJ_AT(x,y) (level.objects[x][y] != (struct obj *)0) +/* + * Macros for encapsulation of level.monsters references. + */ +#define MON_AT(x,y) (level.monsters[x][y] != (struct monst *)0 && \ + !(level.monsters[x][y])->mburied) +#define MON_BURIED_AT(x,y) (level.monsters[x][y] != (struct monst *)0 && \ + (level.monsters[x][y])->mburied) +#ifndef STEED +#define place_monster(m,x,y) ((m)->mx=(x),(m)->my=(y),\ + level.monsters[(m)->mx][(m)->my]=(m)) +#endif +#define place_worm_seg(m,x,y) level.monsters[x][y] = m +#define remove_monster(x,y) level.monsters[x][y] = (struct monst *)0 +#define m_at(x,y) (MON_AT(x,y) ? level.monsters[x][y] : \ + (struct monst *)0) +#define m_buried_at(x,y) (MON_BURIED_AT(x,y) ? level.monsters[x][y] : \ + (struct monst *)0) + +#endif /* RM_H */ diff --git a/include/skills.h b/include/skills.h new file mode 100644 index 0000000..09bc3be --- /dev/null +++ b/include/skills.h @@ -0,0 +1,121 @@ +/* SCCS Id: @(#)skills.h 3.4 1999/10/27 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SKILLS_H +#define SKILLS_H + +/* Much of this code was taken from you.h. It is now + * in a separate file so it can be included in objects.c. + */ + + +/* Code to denote that no skill is applicable */ +#define P_NONE 0 + +/* Weapon Skills -- Stephen White + * Order matters and are used in macros. + * Positive values denote hand-to-hand weapons or launchers. + * Negative values denote ammunition or missiles. + * Update weapon.c if you ammend any skills. + * Also used for oc_subtyp. + */ +#define P_DAGGER 1 +#define P_KNIFE 2 +#define P_AXE 3 +#define P_PICK_AXE 4 +#define P_SHORT_SWORD 5 +#define P_BROAD_SWORD 6 +#define P_LONG_SWORD 7 +#define P_TWO_HANDED_SWORD 8 +#define P_SCIMITAR 9 +#define P_SABER 10 +#define P_CLUB 11 /* Heavy-shafted bludgeon */ +#define P_MACE 12 +#define P_MORNING_STAR 13 /* Spiked bludgeon */ +#define P_FLAIL 14 /* Two pieces hinged or chained together */ +#define P_HAMMER 15 /* Heavy head on the end */ +#define P_QUARTERSTAFF 16 /* Long-shafted bludgeon */ +#define P_POLEARMS 17 +#define P_SPEAR 18 +#define P_JAVELIN 19 +#define P_TRIDENT 20 +#define P_LANCE 21 +#define P_BOW 22 +#define P_SLING 23 +#define P_CROSSBOW 24 +#define P_DART 25 +#define P_SHURIKEN 26 +#define P_BOOMERANG 27 +#define P_WHIP 28 +#define P_UNICORN_HORN 29 /* last weapon */ +#define P_FIRST_WEAPON P_DAGGER +#define P_LAST_WEAPON P_UNICORN_HORN + +/* Spell Skills added by Larry Stewart-Zerba */ +#define P_ATTACK_SPELL 30 +#define P_HEALING_SPELL 31 +#define P_DIVINATION_SPELL 32 +#define P_ENCHANTMENT_SPELL 33 +#define P_CLERIC_SPELL 34 +#define P_ESCAPE_SPELL 35 +#define P_MATTER_SPELL 36 +#define P_FIRST_SPELL P_ATTACK_SPELL +#define P_LAST_SPELL P_MATTER_SPELL + +/* Other types of combat */ +#define P_BARE_HANDED_COMBAT 37 +#define P_MARTIAL_ARTS P_BARE_HANDED_COMBAT /* Role distinguishes */ +#define P_TWO_WEAPON_COMBAT 38 /* Finally implemented */ +#ifdef STEED +#define P_RIDING 39 /* How well you control your steed */ +#define P_LAST_H_TO_H P_RIDING +#else +#define P_LAST_H_TO_H P_TWO_WEAPON_COMBAT +#endif +#define P_FIRST_H_TO_H P_BARE_HANDED_COMBAT + +#define P_NUM_SKILLS (P_LAST_H_TO_H+1) + +/* These roles qualify for a martial arts bonus */ +#define martial_bonus() (Role_if(PM_SAMURAI) || Role_if(PM_MONK)) + + +/* + * These are the standard weapon skill levels. It is important that + * the lowest "valid" skill be be 1. The code calculates the + * previous amount to practice by calling practice_needed_to_advance() + * with the current skill-1. To work out for the UNSKILLED case, + * a value of 0 needed. + */ +#define P_ISRESTRICTED 0 +#define P_UNSKILLED 1 +#define P_BASIC 2 +#define P_SKILLED 3 +#define P_EXPERT 4 +#define P_MASTER 5 /* Unarmed combat/martial arts only */ +#define P_GRAND_MASTER 6 /* Unarmed combat/martial arts only */ + +#define practice_needed_to_advance(level) ((level)*(level)*20) + +/* The hero's skill in various weapons. */ +struct skills { + xchar skill; + xchar max_skill; + unsigned short advance; +}; + +#define P_SKILL(type) (u.weapon_skills[type].skill) +#define P_MAX_SKILL(type) (u.weapon_skills[type].max_skill) +#define P_ADVANCE(type) (u.weapon_skills[type].advance) +#define P_RESTRICTED(type) (u.weapon_skills[type].skill == P_ISRESTRICTED) + +#define P_SKILL_LIMIT 60 /* Max number of skill advancements */ + +/* Initial skill matrix structure; used in u_init.c and weapon.c */ +struct def_skill { + xchar skill; + xchar skmax; +}; + +#endif /* SKILLS_H */ diff --git a/include/sp_lev.h b/include/sp_lev.h new file mode 100644 index 0000000..f549f53 --- /dev/null +++ b/include/sp_lev.h @@ -0,0 +1,246 @@ +/* SCCS Id: @(#)sp_lev.h 3.4 1996/05/08 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SP_LEV_H +#define SP_LEV_H + + /* wall directions */ +#define W_NORTH 1 +#define W_SOUTH 2 +#define W_EAST 4 +#define W_WEST 8 +#define W_ANY (W_NORTH|W_SOUTH|W_EAST|W_WEST) + + /* MAP limits */ +#define MAP_X_LIM 76 +#define MAP_Y_LIM 21 + + /* Per level flags */ +#define NOTELEPORT 1 +#define HARDFLOOR 2 +#define NOMMAP 4 +#define SHORTSIGHTED 8 +#define ARBOREAL 16 + + /* special level types */ +#define SP_LEV_ROOMS 1 +#define SP_LEV_MAZE 2 + +/* + * Structures manipulated by the special levels loader & compiler + */ + +typedef union str_or_len { + char *str; + int len; +} Str_or_Len; + +typedef struct { + boolean init_present, padding; + char fg, bg; + boolean smoothed, joined; + xchar lit, walled; +} lev_init; + +typedef struct { + xchar x, y, mask; +} door; + +typedef struct { + xchar wall, pos, secret, mask; +} room_door; + +typedef struct { + xchar x, y, chance, type; +} trap; + +typedef struct { + Str_or_Len name, appear_as; + short id; + aligntyp align; + xchar x, y, chance, class, appear; + schar peaceful, asleep; +} monster; + +typedef struct { + Str_or_Len name; + int corpsenm; + short id, spe; + xchar x, y, chance, class, containment; + schar curse_state; +} object; + +typedef struct { + xchar x, y; + aligntyp align; + xchar shrine; +} altar; + +typedef struct { + xchar x, y, dir, db_open; +} drawbridge; + +typedef struct { + xchar x, y, dir; +} walk; + +typedef struct { + xchar x1, y1, x2, y2; +} digpos; + +typedef struct { + xchar x, y, up; +} lad; + +typedef struct { + xchar x, y, up; +} stair; + +typedef struct { + xchar x1, y1, x2, y2; + xchar rtype, rlit, rirreg; +} region; + +/* values for rtype are defined in dungeon.h */ +typedef struct { + struct { xchar x1, y1, x2, y2; } inarea; + struct { xchar x1, y1, x2, y2; } delarea; + boolean in_islev, del_islev; + xchar rtype, padding; + Str_or_Len rname; +} lev_region; + +typedef struct { + xchar x, y; + int amount; +} gold; + +typedef struct { + xchar x, y; + Str_or_Len engr; + xchar etype; +} engraving; + +typedef struct { + xchar x, y; +} fountain; + +typedef struct { + xchar x, y; +} sink; + +typedef struct { + xchar x, y; +} pool; + +typedef struct { + char halign, valign; + char xsize, ysize; + char **map; + char nrobjects; + char *robjects; + char nloc; + char *rloc_x; + char *rloc_y; + char nrmonst; + char *rmonst; + char nreg; + region **regions; + char nlreg; + lev_region **lregions; + char ndoor; + door **doors; + char ntrap; + trap **traps; + char nmonster; + monster **monsters; + char nobject; + object **objects; + char ndrawbridge; + drawbridge **drawbridges; + char nwalk; + walk **walks; + char ndig; + digpos **digs; + char npass; + digpos **passs; + char nlad; + lad **lads; + char nstair; + stair **stairs; + char naltar; + altar **altars; + char ngold; + gold **golds; + char nengraving; + engraving **engravings; + char nfountain; + fountain **fountains; +} mazepart; + +typedef struct { + long flags; + lev_init init_lev; + schar filling; + char numpart; + mazepart **parts; +} specialmaze; + +typedef struct _room { + char *name; + char *parent; + xchar x, y, w, h; + xchar xalign, yalign; + xchar rtype, chance, rlit, filled; + char ndoor; + room_door **doors; + char ntrap; + trap **traps; + char nmonster; + monster **monsters; + char nobject; + object **objects; + char naltar; + altar **altars; + char nstair; + stair **stairs; + char ngold; + gold **golds; + char nengraving; + engraving **engravings; + char nfountain; + fountain **fountains; + char nsink; + sink **sinks; + char npool; + pool **pools; + /* These three fields are only used when loading the level... */ + int nsubroom; + struct _room *subrooms[MAX_SUBROOMS]; + struct mkroom *mkr; +} room; + +typedef struct { + struct { + xchar room; + xchar wall; + xchar door; + } src, dest; +} corridor; + +/* used only by lev_comp */ +typedef struct { + long flags; + lev_init init_lev; + char nrobjects; + char *robjects; + char nrmonst; + char *rmonst; + xchar nroom; + room **rooms; + xchar ncorr; + corridor **corrs; +} splev; + +#endif /* SP_LEV_H */ diff --git a/include/spell.h b/include/spell.h new file mode 100644 index 0000000..6b43e21 --- /dev/null +++ b/include/spell.h @@ -0,0 +1,22 @@ +/* SCCS Id: @(#)spell.h 3.4 1995/06/01 */ +/* Copyright 1986, M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SPELL_H +#define SPELL_H + +struct spell { + short sp_id; /* spell id (== object.otyp) */ + xchar sp_lev; /* power level */ + int sp_know; /* knowlege of spell */ +}; + +/* levels of memory destruction with a scroll of amnesia */ +#define ALL_MAP 0x1 +#define ALL_SPELLS 0x2 + +#define decrnknow(spell) spl_book[spell].sp_know-- +#define spellid(spell) spl_book[spell].sp_id +#define spellknow(spell) spl_book[spell].sp_know + +#endif /* SPELL_H */ diff --git a/include/system.h b/include/system.h new file mode 100644 index 0000000..a4efff9 --- /dev/null +++ b/include/system.h @@ -0,0 +1,551 @@ +/* SCCS Id: @(#)system.h 3.4 2001/12/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef SYSTEM_H +#define SYSTEM_H + +#if !defined(__cplusplus) && !defined(__GO32__) + +#define E extern + +/* some old may not define off_t and size_t; if your system is + * one of these, define them by hand below + */ +#if (defined(VMS) && !defined(__GNUC__)) || defined(MAC) +#include +#else +# ifndef AMIGA +#include +# endif +#endif + +#if (defined(MICRO) && !defined(TOS)) || defined(ANCIENT_VAXC) +# if !defined(_SIZE_T) && !defined(__size_t) /* __size_t for CSet/2 */ +# define _SIZE_T +# if !((defined(MSDOS) || defined(OS2)) && defined(_SIZE_T_DEFINED)) /* MSC 5.1 */ +# if !(defined(__GNUC__) && defined(AMIGA)) +typedef unsigned int size_t; +# endif +# endif +# endif +#endif /* MICRO && !TOS */ + +#if defined(__TURBOC__) || defined(MAC) +#include /* time_t is not in */ +#endif +#if defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC)) +/* The Ultrix v3.0 seems to be very wrong. */ +# define time_t long +#endif + +#if defined(ULTRIX) || defined(VMS) +# define off_t long +#endif +#if defined(AZTEC) || defined(THINKC4) || defined(__TURBOC__) +typedef long off_t; +#endif + +#endif /* !__cplusplus && !__GO32__ */ + +/* You may want to change this to fit your system, as this is almost + * impossible to get right automatically. + * This is the type of signal handling functions. + */ +#if !defined(OS2) && (defined(_MSC_VER) || defined(__TURBOC__) || defined(__SC__) || defined(WIN32)) +# define SIG_RET_TYPE void (__cdecl *)(int) +#endif +#ifndef SIG_RET_TYPE +# if defined(NHSTDC) || defined(POSIX_TYPES) || defined(OS2) || defined(__DECC) +# define SIG_RET_TYPE void (*)() +# endif +#endif +#ifndef SIG_RET_TYPE +# if defined(ULTRIX) || defined(SUNOS4) || defined(SVR3) || defined(SVR4) + /* SVR3 is defined automatically by some systems */ +# define SIG_RET_TYPE void (*)() +# endif +#endif +#ifndef SIG_RET_TYPE /* BSD, SIII, SVR2 and earlier, Sun3.5 and earlier */ +# define SIG_RET_TYPE int (*)() +#endif + +#if !defined(__cplusplus) && !defined(__GO32__) + +#if defined(BSD) || defined(ULTRIX) || defined(RANDOM) +# ifdef random +# undef random +# endif +# if !defined(__SC__) && !defined(LINUX) +E long NDECL(random); +# endif +# if (!defined(SUNOS4) && !defined(bsdi) && !defined(__FreeBSD__)) || defined(RANDOM) +E void FDECL(srandom, (unsigned int)); +# else +# if !defined(bsdi) && !defined(__FreeBSD__) +E int FDECL(srandom, (unsigned int)); +# endif +# endif +#else +E long lrand48(); +E void srand48(); +#endif /* BSD || ULTRIX || RANDOM */ + +#if !defined(BSD) || defined(ultrix) + /* real BSD wants all these to return int */ +# ifndef MICRO +E void FDECL(exit, (int)); +# endif /* MICRO */ +/* compensate for some CSet/2 bogosities */ +# if defined(OS2_CSET2) && defined(OS2_CSET2_VER_2) +# define open _open +# define close _close +# define read _read +# define write _write +# define lseek _lseek +# define chdir _chdir +# define getcwd _getcwd +# define setmode _setmode +# endif /* OS2_CSET2 && OS2_CSET2_VER_2 */ +/* If flex thinks that we're not __STDC__ it declares free() to return + int and we die. We must use __STDC__ instead of NHSTDC because + the former is naturally what flex tests for. */ +# if defined(__STDC__) || !defined(FLEX_SCANNER) +# ifndef OS2_CSET2 +# ifndef MONITOR_HEAP +E void FDECL(free, (genericptr_t)); +# endif +# endif +# endif +#if !defined(__SASC_60) && !defined(_DCC) && !defined(__SC__) +# if defined(AMIGA) && !defined(AZTEC_50) && !defined(__GNUC__) +E int FDECL(perror, (const char *)); +# else +# if !(defined(ULTRIX_PROTO) && defined(__GNUC__)) +E void FDECL(perror, (const char *)); +# endif +# endif +#endif +#endif +#ifndef NeXT +#ifdef POSIX_TYPES +E void FDECL(qsort, (genericptr_t,size_t,size_t, + int(*)(const genericptr,const genericptr))); +#else +# if defined(BSD) || defined(ULTRIX) +E int qsort(); +# else +# if !defined(LATTICE) && !defined(AZTEC_50) +E void FDECL(qsort, (genericptr_t,size_t,size_t, + int(*)(const genericptr,const genericptr))); +# endif +# endif +#endif +#endif /* NeXT */ + +#ifndef __SASC_60 +#if !defined(AZTEC_50) && !defined(__GNUC__) +/* may already be defined */ + +# ifdef ULTRIX +# ifdef ULTRIX_PROTO +E int FDECL(lseek, (int,off_t,int)); +# else +E long FDECL(lseek, (int,off_t,int)); +# endif + /* Ultrix 3.0 man page mistakenly says it returns an int. */ +E int FDECL(write, (int,char *,int)); +E int FDECL(link, (const char *, const char*)); +# else +# ifndef bsdi +E long FDECL(lseek, (int,long,int)); +# endif +# if defined(POSIX_TYPES) || defined(__TURBOC__) +# ifndef bsdi +E int FDECL(write, (int, const void *,unsigned)); +# endif +# else +# ifndef __MWERKS__ /* metrowerks defines write via universal headers */ +E int FDECL(write, (int,genericptr_t,unsigned)); +# endif +# endif +# endif /* ULTRIX */ + +# ifdef OS2_CSET2 /* IBM CSet/2 */ +# ifdef OS2_CSET2_VER_1 +E int FDECL(unlink, (char *)); +# else +E int FDECL(unlink, (const char *)); /* prototype is ok in ver >= 2 */ +# endif +# else +# ifndef __SC__ +E int FDECL(unlink, (const char *)); +# endif +# endif + +#endif /* AZTEC_50 && __GNUC__ */ + +#ifdef MAC +#ifndef __CONDITIONALMACROS__ /* universal headers */ +E int FDECL(close, (int)); /* unistd.h */ +E int FDECL(read, (int, char *, int)); /* unistd.h */ +E int FDECL(chdir, (const char *)); /* unistd.h */ +E char *FDECL(getcwd, (char *,int)); /* unistd.h */ +#endif + +E int FDECL(open, (const char *,int)); +#endif + +#if defined(MICRO) +E int FDECL(close, (int)); +#ifndef __EMX__ +E int FDECL(read, (int,genericptr_t,unsigned int)); +#endif +E int FDECL(open, (const char *,int,...)); +E int FDECL(dup2, (int, int)); +E int FDECL(setmode, (int,int)); +E int NDECL(kbhit); +# if !defined(_DCC) +# if defined(__TURBOC__) +E int FDECL(chdir, (const char *)); +# else +# ifndef __EMX__ +E int FDECL(chdir, (char *)); +# endif +# endif +# ifndef __EMX__ +E char *FDECL(getcwd, (char *,int)); +# endif +# endif /* !_DCC */ +#endif + +#ifdef ULTRIX +E int FDECL(close, (int)); +E int FDECL(atoi, (const char *)); +E int FDECL(chdir, (const char *)); +# if !defined(ULTRIX_CC20) && !defined(__GNUC__) +E int FDECL(chmod, (const char *,int)); +E mode_t FDECL(umask, (int)); +# endif +E int FDECL(read, (int,genericptr_t,unsigned)); +/* these aren't quite right, but this saves including lots of system files */ +E int FDECL(stty, (int,genericptr_t)); +E int FDECL(gtty, (int,genericptr_t)); +E int FDECL(ioctl, (int, int, char*)); +E int FDECL(isatty, (int)); /* 1==yes, 0==no, -1==error */ +#include +# if defined(ULTRIX_PROTO) || defined(__GNUC__) +E int NDECL(fork); +# else +E long NDECL(fork); +# endif +#endif /* ULTRIX */ + +#ifdef VMS +# ifndef abs +E int FDECL(abs, (int)); +# endif +E int FDECL(atexit, (void (*)(void))); +E int FDECL(atoi, (const char *)); +E int FDECL(chdir, (const char *)); +E int FDECL(chown, (const char *,unsigned,unsigned)); +# ifdef __DECC_VER +E int FDECL(chmod, (const char *,mode_t)); +E mode_t FDECL(umask, (mode_t)); +# else +E int FDECL(chmod, (const char *,int)); +E int FDECL(umask, (int)); +# endif +/* #include */ +E int FDECL(close, (int)); +E int VDECL(creat, (const char *,unsigned,...)); +E int FDECL(delete, (const char *)); +E int FDECL(fstat, ( /*_ int, stat_t * _*/ )); +E int FDECL(isatty, (int)); /* 1==yes, 0==no, -1==error */ +E long FDECL(lseek, (int,long,int)); +E int VDECL(open, (const char *,int,unsigned,...)); +E int FDECL(read, (int,genericptr_t,unsigned)); +E int FDECL(rename, (const char *,const char *)); +E int FDECL(stat, ( /*_ const char *,stat_t * _*/ )); +E int FDECL(write, (int,const genericptr,unsigned)); +#endif + +#endif /* __SASC_60 */ + +/* both old & new versions of Ultrix want these, but real BSD does not */ +#ifdef ultrix +E void abort(); +E void bcopy(); +# ifdef ULTRIX +E int FDECL(system, (const char *)); +# ifndef _UNISTD_H_ +E int FDECL(execl, (const char *, ...)); +# endif +# endif +#endif +#ifdef MICRO +E void NDECL(abort); +E void FDECL(_exit, (int)); +E int FDECL(system, (const char *)); +#endif +#if defined(HPUX) && !defined(_POSIX_SOURCE) +E long NDECL(fork); +#endif + +#ifdef POSIX_TYPES +/* The POSIX string.h is required to define all the mem* and str* functions */ +#include +#else +#if defined(SYSV) || defined(VMS) || defined(MAC) || defined(SUNOS4) +# if defined(NHSTDC) || (defined(VMS) && !defined(ANCIENT_VAXC)) +# if !defined(_AIX32) && !(defined(SUNOS4) && defined(__STDC__)) + /* Solaris unbundled cc (acc) */ +E int FDECL(memcmp, (const void *,const void *,size_t)); +E void *FDECL(memcpy, (void *, const void *, size_t)); +E void *FDECL(memset, (void *, int, size_t)); +# endif +# else +# ifndef memcmp /* some systems seem to macro these back to b*() */ +E int memcmp(); +# endif +# ifndef memcpy +E char *memcpy(); +# endif +# ifndef memset +E char *memset(); +# endif +# endif +#else +# ifdef HPUX +E int FDECL(memcmp, (char *,char *,int)); +E void *FDECL(memcpy, (char *,char *,int)); +E void *FDECL(memset, (char*,int,int)); +# endif +#endif +#endif /* POSIX_TYPES */ + +#if defined(MICRO) && !defined(LATTICE) +# if defined(TOS) && defined(__GNUC__) +E int FDECL(memcmp, (const void *,const void *,size_t)); +E void *FDECL(memcpy, (void *,const void *,size_t)); +E void *FDECL(memset, (void *,int,size_t)); +# else +# if defined(AZTEC_50) || defined(NHSTDC) || defined(WIN32) +E int FDECL(memcmp, (const void *, const void *, size_t)); +E void *FDECL(memcpy, (void *, const void *, size_t)); +E void *FDECL(memset, (void *, int, size_t)); +# else +E int FDECL(memcmp, (char *,char *,unsigned int)); +E char *FDECL(memcpy, (char *,char *,unsigned int)); +E char *FDECL(memset, (char*,int,int)); +# endif /* AZTEC_50 || NHSTDC */ +# endif /* TOS */ +#endif /* MICRO */ + +#if defined(BSD) && defined(ultrix) /* i.e., old versions of Ultrix */ +E void sleep(); +#endif +#if defined(ULTRIX) || defined(SYSV) +E unsigned sleep(); +#endif +#if defined(HPUX) +E unsigned int FDECL(sleep, (unsigned int)); +#endif +#ifdef VMS +E int FDECL(sleep, (unsigned)); +#endif + +E char *FDECL(getenv, (const char *)); +E char *getlogin(); +#if defined(HPUX) && !defined(_POSIX_SOURCE) +E long NDECL(getuid); +E long NDECL(getgid); +E long NDECL(getpid); +#else +# ifdef POSIX_TYPES +E pid_t NDECL(getpid); +E uid_t NDECL(getuid); +E gid_t NDECL(getgid); +# ifdef VMS +E pid_t NDECL(getppid); +# endif +# else /*!POSIX_TYPES*/ +# ifndef getpid /* Borland C defines getpid() as a macro */ +E int NDECL(getpid); +# endif +# ifdef VMS +E int NDECL(getppid); +E unsigned NDECL(getuid); +E unsigned NDECL(getgid); +# endif +# if defined(ULTRIX) && !defined(_UNISTD_H_) +E unsigned NDECL(getuid); +E unsigned NDECL(getgid); +E int FDECL(setgid, (int)); +E int FDECL(setuid, (int)); +# endif +# endif /*?POSIX_TYPES*/ +#endif /*?(HPUX && !_POSIX_SOURCE)*/ + +/* add more architectures as needed */ +#if defined(HPUX) +#define seteuid(x) setreuid(-1, (x)); +#endif + +/*# string(s).h #*/ +#if !defined(_XtIntrinsic_h) && !defined(POSIX_TYPES) +/* #includes ; so does defining POSIX_TYPES */ + +#if (defined(ULTRIX) || defined(NeXT)) && defined(__GNUC__) +#include +#else +E char *FDECL(strcpy, (char *,const char *)); +E char *FDECL(strncpy, (char *,const char *,size_t)); +E char *FDECL(strcat, (char *,const char *)); +E char *FDECL(strncat, (char *,const char *,size_t)); +E char *FDECL(strpbrk, (const char *,const char *)); + +# if defined(SYSV) || defined(MICRO) || defined(MAC) || defined(VMS) || defined(HPUX) +E char *FDECL(strchr, (const char *,int)); +E char *FDECL(strrchr, (const char *,int)); +# else /* BSD */ +E char *FDECL(index, (const char *,int)); +E char *FDECL(rindex, (const char *,int)); +# endif + +E int FDECL(strcmp, (const char *,const char *)); +E int FDECL(strncmp, (const char *,const char *,size_t)); +# if defined(MICRO) || defined(MAC) || defined(VMS) +E size_t FDECL(strlen, (const char *)); +# else +# ifdef HPUX +E unsigned int FDECL(strlen, (char *)); +# else +# if !(defined(ULTRIX_PROTO) && defined(__GNUC__)) +E int FDECL(strlen, (const char *)); +# endif +# endif /* HPUX */ +# endif /* MICRO */ +#endif /* ULTRIX */ + +#endif /* !_XtIntrinsic_h_ && !POSIX_TYPES */ + +#if defined(ULTRIX) && defined(__GNUC__) +E char *FDECL(index, (const char *,int)); +E char *FDECL(rindex, (const char *,int)); +#endif + +/* Old varieties of BSD have char *sprintf(). + * Newer varieties of BSD have int sprintf() but allow for the old char *. + * Several varieties of SYSV and PC systems also have int sprintf(). + * If your system doesn't agree with this breakdown, you may want to change + * this declaration, especially if your machine treats the types differently. + * If your system defines sprintf, et al, in stdio.h, add to the initial + * #if. + */ +#if defined(ULTRIX) || defined(__DECC) || defined(__SASC_60) || defined(WIN32) +#define SPRINTF_PROTO +#endif +#if (defined(SUNOS4) && defined(__STDC__)) || defined(_AIX32) +#define SPRINTF_PROTO +#endif +#if defined(TOS) || defined(AZTEC_50) || defined(__sgi) || defined(__GNUC__) + /* problem with prototype mismatches */ +#define SPRINTF_PROTO +#endif +#if defined(__MWERKS__) || defined(__SC__) + /* Metrowerks already has a prototype for sprintf() */ +# define SPRINTF_PROTO +#endif + +#ifndef SPRINTF_PROTO +# if defined(POSIX_TYPES) || defined(DGUX) || defined(NeXT) || !defined(BSD) +E int FDECL(sprintf, (char *,const char *,...)); +# else +# define OLD_SPRINTF +E char *sprintf(); +# endif +#endif +#ifdef SPRINTF_PROTO +# undef SPRINTF_PROTO +#endif + +#ifndef __SASC_60 +#ifdef NEED_VARARGS +# if defined(USE_STDARG) || defined(USE_VARARGS) +# if !defined(SVR4) && !defined(apollo) +# if !(defined(ULTRIX_PROTO) && defined(__GNUC__)) +# if !(defined(SUNOS4) && defined(__STDC__)) /* Solaris unbundled cc (acc) */ +E int FDECL(vsprintf, (char *, const char *, va_list)); +E int FDECL(vfprintf, (FILE *, const char *, va_list)); +E int FDECL(vprintf, (const char *, va_list)); +# endif +# endif +# endif +# else +# define vprintf printf +# define vfprintf fprintf +# define vsprintf sprintf +# endif +#endif /* NEED_VARARGS */ +#endif + + +#ifdef MICRO +E int FDECL(tgetent, (const char *,const char *)); +E void FDECL(tputs, (const char *,int,int (*)())); +E int FDECL(tgetnum, (const char *)); +E int FDECL(tgetflag, (const char *)); +E char *FDECL(tgetstr, (const char *,char **)); +E char *FDECL(tgoto, (const char *,int,int)); +#else +# if ! (defined(HPUX) && defined(_POSIX_SOURCE)) +E int FDECL(tgetent, (char *,const char *)); +E void FDECL(tputs, (const char *,int,int (*)())); +# endif +E int FDECL(tgetnum, (const char *)); +E int FDECL(tgetflag, (const char *)); +E char *FDECL(tgetstr, (const char *,char **)); +E char *FDECL(tgoto, (const char *,int,int)); +#endif + +#ifdef ALLOC_C +E genericptr_t FDECL(malloc, (size_t)); +#endif + +/* time functions */ + +# ifndef LATTICE +# if !(defined(ULTRIX_PROTO) && defined(__GNUC__)) +E struct tm *FDECL(localtime, (const time_t *)); +# endif +# endif + +# if defined(ULTRIX) || (defined(BSD) && defined(POSIX_TYPES)) || defined(SYSV) || defined(MICRO) || defined(VMS) || defined(MAC) || (defined(HPUX) && defined(_POSIX_SOURCE)) +E time_t FDECL(time, (time_t *)); +# else +E long FDECL(time, (time_t *)); +# endif /* ULTRIX */ + +#ifdef VMS + /* used in makedefs.c, but missing from gcc-vms's */ +E char *FDECL(ctime, (const time_t *)); +#endif + + +#ifdef MICRO +# ifdef abs +# undef abs +# endif +E int FDECL(abs, (int)); +# ifdef atoi +# undef atoi +# endif +E int FDECL(atoi, (const char *)); +#endif + +#undef E + +#endif /* !__cplusplus && !__GO32__ */ + +#endif /* SYSTEM_H */ diff --git a/include/tcap.h b/include/tcap.h new file mode 100644 index 0000000..09cff27 --- /dev/null +++ b/include/tcap.h @@ -0,0 +1,57 @@ +/* SCCS Id: @(#)tcap.h 3.4 1992/10/21 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* not named termcap.h because it may conflict with a system header */ + +#ifndef TCAP_H +#define TCAP_H + +#ifndef MICRO +# define TERMLIB /* include termcap code */ +#endif + +/* might display need graphics code? */ +#if !defined(AMIGA) && !defined(TOS) && !defined(MAC) +# if defined(TERMLIB) || defined(OS2) || defined(MSDOS) +# define ASCIIGRAPH +# endif +#endif + +#ifndef DECL_H +extern struct tc_gbl_data { /* also declared in decl.h; defined in decl.c */ + char *tc_AS, *tc_AE; /* graphics start and end (tty font swapping) */ + int tc_LI, tc_CO; /* lines and columns */ +} tc_gbl_data; +#define AS tc_gbl_data.tc_AS +#define AE tc_gbl_data.tc_AE +#define LI tc_gbl_data.tc_LI +#define CO tc_gbl_data.tc_CO +#endif + +extern struct tc_lcl_data { /* defined and set up in termcap.c */ + char *tc_CM, *tc_ND, *tc_CD; + char *tc_HI, *tc_HE, *tc_US, *tc_UE; + boolean tc_ul_hack; +} tc_lcl_data; +/* some curses.h declare CM etc. */ +#define nh_CM tc_lcl_data.tc_CM +#define nh_ND tc_lcl_data.tc_ND +#define nh_CD tc_lcl_data.tc_CD +#define nh_HI tc_lcl_data.tc_HI +#define nh_HE tc_lcl_data.tc_HE +#define nh_US tc_lcl_data.tc_US +#define nh_UE tc_lcl_data.tc_UE +#define ul_hack tc_lcl_data.tc_ul_hack + +extern short ospeed; /* set up in termcap.c */ + +#ifdef TEXTCOLOR +# ifdef TOS +extern const char *hilites[CLR_MAX]; +# else +extern NEARDATA char *hilites[CLR_MAX]; +# endif +#endif + +#endif /* TCAP_H */ diff --git a/include/tile2x11.h b/include/tile2x11.h new file mode 100644 index 0000000..3f16285 --- /dev/null +++ b/include/tile2x11.h @@ -0,0 +1,22 @@ +/* SCCS Id: @(#)tile2x11.h 3.4 1995/01/25 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef TILE2X11_H +#define TILE2X11_H + +/* + * Header for the x11 tile map. + */ +typedef struct { + unsigned long version; + unsigned long ncolors; + unsigned long tile_width; + unsigned long tile_height; + unsigned long ntiles; + unsigned long per_row; +} x11_header; + +/* how wide each row in the tile file is, in tiles */ +#define TILES_PER_ROW (40) + +#endif /* TILE2X11_H */ diff --git a/include/timeout.h b/include/timeout.h new file mode 100644 index 0000000..481bf95 --- /dev/null +++ b/include/timeout.h @@ -0,0 +1,44 @@ +/* SCCS Id: @(#)timeout.h 3.4 1999/02/13 */ +/* Copyright 1994, Dean Luick */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef TIMEOUT_H +#define TIMEOUT_H + +/* generic timeout function */ +typedef void FDECL((*timeout_proc), (genericptr_t, long)); + +/* kind of timer */ +#define TIMER_LEVEL 0 /* event specific to level */ +#define TIMER_GLOBAL 1 /* event follows current play */ +#define TIMER_OBJECT 2 /* event follows a object */ +#define TIMER_MONSTER 3 /* event follows a monster */ + +/* save/restore timer ranges */ +#define RANGE_LEVEL 0 /* save/restore timers staying on level */ +#define RANGE_GLOBAL 1 /* save/restore timers following global play */ + +/* + * Timeout functions. Add a define here, then put it in the table + * in timeout.c. "One more level of indirection will fix everything." + */ +#define ROT_ORGANIC 0 /* for buried organics */ +#define ROT_CORPSE 1 +#define REVIVE_MON 2 +#define BURN_OBJECT 3 +#define HATCH_EGG 4 +#define FIG_TRANSFORM 5 +#define NUM_TIME_FUNCS 6 + +/* used in timeout.c */ +typedef struct fe { + struct fe *next; /* next item in chain */ + long timeout; /* when we time out */ + unsigned long tid; /* timer ID */ + short kind; /* kind of use */ + short func_index; /* what to call when we time out */ + genericptr_t arg; /* pointer to timeout argument */ + Bitfield (needs_fixup,1); /* does arg need to be patched? */ +} timer_element; + +#endif /* TIMEOUT_H */ diff --git a/include/tosconf.h b/include/tosconf.h new file mode 100644 index 0000000..090195c --- /dev/null +++ b/include/tosconf.h @@ -0,0 +1,86 @@ +/* SCCS Id: @(#)tosconf.h 3.2 90/02/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef TOS +#ifndef TOSCONF_H +#define TOSCONF_H + +#define MICRO /* must be defined to allow some inclusions */ + +/* + Adjust these options to suit your compiler. The default here is for + GNU C with the MiNT library. +*/ + +/*#define NO_SIGNAL /* library doesn't support signals */ +/*#define NO_FSTAT /* library doesn't have fstat() call */ +#define MINT /* library supports MiNT extensions to TOS */ + +#ifdef __MINT__ +#define MINT +#endif + +#ifdef O_BINARY +#define FCMASK O_BINARY +#else +#define FCMASK 0660 +#define O_BINARY 0 +#endif + +#ifdef UNIXDEBUG +#define remove(x) unlink(x) +#endif + +/* configurable options */ +#define MFLOPPY /* floppy support */ +#define RANDOM /* improved random numbers */ +#define SHELL /* allow spawning of shell */ +#define TERMLIB /* use termcap */ +#define TEXTCOLOR /* allow color */ +#define MAIL /* enable the fake maildemon */ +#ifdef MINT +#define SUSPEND /* allow suspending the game */ +#endif + +#ifndef TERMLIB +#define ANSI_DEFAULT /* use vt52 by default */ +#endif + +#if defined(__GNUC__) || defined(__MINT__) +/* actually, only more recent GNU C libraries have strcmpi + * on the other hand, they're free -- if yours is out of + * date, grab the most recent from atari.archive.umich.edu + */ +#define STRNCMPI +#undef strcmpi +extern int FDECL(strcmpi,(const char *, const char *)); +extern int FDECL(strncmpi,(const char *, const char *, size_t)); +#endif + +#include +#include +/* instead of including system.h from pcconf.h */ +#include +#include +#include +#define SIG_RET_TYPE __Sigfunc +#define SYSTEM_H + +#ifndef MICRO_H +#include "micro.h" +#endif +#ifndef PCCONF_H +#include "pcconf.h" /* remainder of stuff is same as the PC */ +#endif + +#ifdef TEXTCOLOR +extern boolean colors_changed; /* in tos.c */ +#endif + +#ifdef __GNUC__ +#define GCC_BUG /* correct a gcc bug involving double for loops */ +#endif + +#endif /* TOSCONF_H */ +#endif /* TOS */ diff --git a/include/tradstdc.h b/include/tradstdc.h new file mode 100644 index 0000000..0c6076a --- /dev/null +++ b/include/tradstdc.h @@ -0,0 +1,273 @@ +/* SCCS Id: @(#)tradstdc.h 3.4 1993/05/30 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef TRADSTDC_H +#define TRADSTDC_H + +#if defined(DUMB) && !defined(NOVOID) +#define NOVOID +#endif + +#ifdef NOVOID +#define void int +#endif + +/* + * Borland C provides enough ANSI C compatibility in its Borland C++ + * mode to warrant this. But it does not set __STDC__ unless it compiles + * in its ANSI keywords only mode, which prevents use of and + * far pointer use. + */ +#if (defined(__STDC__) || defined(__TURBOC__)) && !defined(NOTSTDC) +#define NHSTDC +#endif + +#if defined(ultrix) && defined(__STDC__) && !defined(__LANGUAGE_C) +/* Ultrix seems to be in a constant state of flux. This check attempts to + * set up ansi compatibility if it wasn't set up correctly by the compiler. + */ +#ifdef mips +#define __mips mips +#endif + +#ifdef LANGUAGE_C +#define __LANGUAGE_C LANGUAGE_C +#endif + +#endif + +/* + * ANSI X3J11 detection. + * Makes substitutes for compatibility with the old C standard. + */ + +/* Decide how to handle variable parameter lists: + * USE_STDARG means use the ANSI facilities (only ANSI compilers + * should do this, and only if the library supports it). + * USE_VARARGS means use the facilities. Again, this should only + * be done if the library supports it. ANSI is *not* required for this. + * Otherwise, the kludgy old methods are used. + * The defaults are USE_STDARG for ANSI compilers, and USE_OLDARGS for + * others. + */ + +/* #define USE_VARARGS */ /* use instead of */ +/* #define USE_OLDARGS */ /* don't use any variable argument facilites */ + +#if defined(apollo) /* Apollos have stdarg(3) but not stdarg.h */ +# define USE_VARARGS +#endif + +#if defined(NHSTDC) || defined(ULTRIX_PROTO) || defined(MAC) +# if !defined(USE_VARARGS) && !defined(USE_OLDARGS) && !defined(USE_STDARG) +# define USE_STDARG +# endif +#endif + +#ifdef NEED_VARARGS /* only define these if necessary */ +#ifdef USE_STDARG +#include +# define VA_DECL(typ1,var1) (typ1 var1, ...) { va_list the_args; +# define VA_DECL2(typ1,var1,typ2,var2) \ + (typ1 var1, typ2 var2, ...) { va_list the_args; +# define VA_INIT(var1,typ1) +# define VA_NEXT(var1,typ1) var1 = va_arg(the_args, typ1) +# define VA_ARGS the_args +# define VA_START(x) va_start(the_args, x) +# define VA_END() va_end(the_args) +# if defined(ULTRIX_PROTO) && !defined(_VA_LIST_) +# define _VA_LIST_ /* prevents multiple def in stdio.h */ +# endif +#else +# ifdef USE_VARARGS +#include +# define VA_DECL(typ1,var1) (va_alist) va_dcl {\ + va_list the_args; typ1 var1; +# define VA_DECL2(typ1,var1,typ2,var2) (va_alist) va_dcl {\ + va_list the_args; typ1 var1; typ2 var2; +# define VA_ARGS the_args +# define VA_START(x) va_start(the_args) +# define VA_INIT(var1,typ1) var1 = va_arg(the_args, typ1) +# define VA_NEXT(var1,typ1) var1 = va_arg(the_args,typ1) +# define VA_END() va_end(the_args) +# else +# define VA_ARGS arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9 +# define VA_DECL(typ1,var1) (var1,VA_ARGS) typ1 var1; \ + char *arg1,*arg2,*arg3,*arg4,*arg5,*arg6,*arg7,*arg8,*arg9; { +# define VA_DECL2(typ1,var1,typ2,var2) (var1,var2,VA_ARGS) \ + typ1 var1; typ2 var2;\ + char *arg1,*arg2,*arg3,*arg4,*arg5,*arg6,*arg7,*arg8,*arg9; { +# define VA_START(x) +# define VA_INIT(var1,typ1) +# define VA_END() +# endif +#endif +#endif /* NEED_VARARGS */ + +#if defined(NHSTDC) || defined(MSDOS) || defined(MAC) || defined(ULTRIX_PROTO) || defined(__BEOS__) + +/* + * Used for robust ANSI parameter forward declarations: + * int VDECL(sprintf, (char *, const char *, ...)); + * + * NDECL() is used for functions with zero arguments; + * FDECL() is used for functions with a fixed number of arguments; + * VDECL() is used for functions with a variable number of arguments. + * Separate macros are needed because ANSI will mix old-style declarations + * with prototypes, except in the case of varargs, and the OVERLAY-specific + * trampoli.* mechanism conflicts with the ANSI <> syntax. + */ + +# define NDECL(f) f(void) /* overridden later if USE_TRAMPOLI set */ + +# define FDECL(f,p) f p + +# if defined(MSDOS) || defined(USE_STDARG) +# define VDECL(f,p) f p +# else +# define VDECL(f,p) f() +# endif + +/* generic pointer, always a macro; genericptr_t is usually a typedef */ +# define genericptr void * + +# if (defined(ULTRIX_PROTO) && !defined(__GNUC__)) || defined(OS2_CSET2) +/* Cover for Ultrix on a DECstation with 2.0 compiler, which coredumps on + * typedef void * genericptr_t; + * extern void a(void(*)(int, genericptr_t)); + * Using the #define is OK for other compiler versions too. + */ +/* And IBM CSet/2. The redeclaration of free hoses the compile. */ +# define genericptr_t genericptr +# else +# if !defined(NHSTDC) && !defined(MAC) +# define const +# define signed +# define volatile +# endif +# endif + +/* + * Suppress `const' if necessary and not handled elsewhere. + * Don't use `#if defined(xxx) && !defined(const)' + * because some compilers choke on `defined(const)'. + * This has been observed with Lattice, MPW, and High C. + */ +# if (defined(ULTRIX_PROTO) && !defined(NHSTDC)) || defined(apollo) + /* the system header files don't use `const' properly */ +# ifndef const +# define const +# endif +# endif + +#else /* NHSTDC */ /* a "traditional" C compiler */ + +# define NDECL(f) f() +# define FDECL(f,p) f() +# define VDECL(f,p) f() + +# if defined(AMIGA) || defined(HPUX) || defined(POSIX_TYPES) || defined(__DECC) || defined(__BORLANDC__) +# define genericptr void * +# endif +# ifndef genericptr +# define genericptr char * +# endif + +/* + * Traditional C compilers don't have "signed", "const", or "volatile". + */ +# define signed +# define const +# define volatile + +#endif /* NHSTDC */ + + +#ifndef genericptr_t +typedef genericptr genericptr_t; /* (void *) or (char *) */ +#endif + + +/* + * According to ANSI, prototypes for old-style declarations must widen the + * arguments to int. However, the MSDOS compilers accept shorter arguments + * (char, short, etc.) in prototypes and do typechecking with them. Therefore + * this mess to allow the better typechecking while also allowing some + * prototypes for the ANSI compilers so people quit trying to fix the + * prototypes to match the standard and thus lose the typechecking. + */ +#if defined(MSDOS) && !defined(__GO32__) +#define UNWIDENED_PROTOTYPES +#endif +#if defined(AMIGA) && !defined(AZTEC_50) +#define UNWIDENED_PROTOTYPES +#endif +#if defined(macintosh) && (defined(__SC__) || defined(__MRC__)) +#define WIDENED_PROTOTYPES +#endif +#if defined(__MWERKS__) && defined(__BEOS__) +#define UNWIDENED_PROTOTYPES +#endif +#if defined(WIN32) +#define UNWIDENED_PROTOTYPES +#endif + +#if defined(ULTRIX_PROTO) && defined(ULTRIX_CC20) +#define UNWIDENED_PROTOTYPES +#endif +#if defined(apollo) +#define UNWIDENED_PROTOTYPES +#endif + +#ifndef UNWIDENED_PROTOTYPES +# if defined(NHSTDC) || defined(ULTRIX_PROTO) || defined(THINK_C) +# define WIDENED_PROTOTYPES +# endif +#endif + +#if 0 +/* The problem below is still the case through 4.0.5F, but the suggested + * compiler flags in the Makefiles suppress the nasty messages, so we don't + * need to be quite so drastic. + */ +#if defined(__sgi) && !defined(__GNUC__) +/* + * As of IRIX 4.0.1, /bin/cc claims to be an ANSI compiler, but it thinks + * it's impossible for a prototype to match an old-style definition with + * unwidened argument types. Thus, we have to turn off all NetHack + * prototypes, and avoid declaring several system functions, since the system + * include files have prototypes and the compiler also complains that + * prototyped and unprototyped declarations don't match. + */ +# undef NDECL +# undef FDECL +# undef VDECL +# define NDECL(f) f() +# define FDECL(f,p) f() +# define VDECL(f,p) f() +#endif +#endif + + + /* MetaWare High-C defaults to unsigned chars */ + /* AIX 3.2 needs this also */ +#if defined(__HC__) || defined(_AIX32) +# undef signed +#endif + + +/* + * Allow gcc2 to check parameters of printf-like calls with -Wformat; + * append this to a prototype declaration (see pline() in extern.h). + */ +#ifdef __GNUC__ +# if __GNUC__ >= 2 +#define PRINTF_F(f,v) __attribute__ ((format (printf, f, v))) +# endif +#endif +#ifndef PRINTF_F +#define PRINTF_F(f,v) +#endif + +#endif /* TRADSTDC_H */ diff --git a/include/trampoli.h b/include/trampoli.h new file mode 100644 index 0000000..fde5ab2 --- /dev/null +++ b/include/trampoli.h @@ -0,0 +1,332 @@ +/* SCCS Id: @(#)trampoli.h 3.4 1995/06/01 */ +/* Copyright (c) 1989, by Norm Meluch and Stephen Spackman */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef TRAMPOLI_H +#define TRAMPOLI_H + +#ifdef USE_TRAMPOLI + +/* ### apply.c ### */ +#define dig() dig_() +#define doapply() doapply_() +#define dojump() dojump_() +#define dorub() dorub_() + + +/* ### artifact.c ### */ +#define doinvoke() doinvoke_() + + +/* ### cmd.c ### */ +#define doextcmd() doextcmd_() +#define doextlist() doextlist_() +#define domonability() domonability_() +#define enter_explore_mode() enter_explore_mode_() +#define doprev_message() doprev_message_() +#define timed_occupation() timed_occupation_() +#define wiz_attributes() wiz_attributes_() +#ifdef WIZARD +#define wiz_detect() wiz_detect_() +#define wiz_genesis() wiz_genesis_() +#define wiz_identify() wiz_identify_() +#define wiz_level_tele() wiz_level_tele_() +#define wiz_map() wiz_map_() +#define wiz_where() wiz_where_() +#define wiz_wish() wiz_wish_() +#endif + +/* ### display.c ### */ +#define doredraw() doredraw_() + +/* ### do.c ### */ +#define doddrop() doddrop_() +#define dodown() dodown_() +#define dodrop() dodrop_() +#define donull() donull_() +#define doup() doup_() +#define dowipe() dowipe_() +#define drop(x) drop_(x) +#define wipeoff() wipeoff_() + + +/* ### do_name.c ### */ +#define ddocall() ddocall_() +#define do_mname() do_mname_() + + +/* ### do_wear.c ### */ +#define Armor_off() Armor_off_() +#define Boots_off() Boots_off_() +#define Gloves_off() Gloves_off_() +#define Helmet_off() Helmet_off_() +#define Armor_on() Armor_on_() +#define Boots_on() Boots_on_() +#define Gloves_on() Gloves_on_() +#define Helmet_on() Helmet_on_() +#define doddoremarm() doddoremarm_() +#define doputon() doputon_() +#define doremring() doremring_() +#define dotakeoff() dotakeoff_() +#define dowear() dowear_() +#define select_off(x) select_off_(x) +#define take_off() take_off_() + + +/* ### dogmove.c ### */ +#define wantdoor(x, y, dummy) wantdoor_(x, y, dummy) + + +/* ### dokick.c ### */ +#define dokick() dokick_() + + +/* ### dothrow.c ### */ +#define dothrow() dothrow_() + + +/* ### eat.c ### */ +#define Hear_again() Hear_again_() +#define eatmdone() eatmdone_() +#define doeat() doeat_() +#define eatfood() eatfood_() +#define opentin() opentin_() +#define unfaint() unfaint_() + + +/* ### end.c ### */ +#define done1(sig) done1_(sig) +#define done2() done2_() +#define done_intr(sig) done_intr_(sig) +#if defined(UNIX) || defined (VMS) || defined(__EMX__) +#define done_hangup(sig) done_hangup_(sig) +#endif + + +/* ### engrave.c ### */ +#define doengrave() doengrave_() + + +/* ### fountain.c ### */ +#define gush(x, y, poolcnt) gush_(x, y, poolcnt) + + +/* ### hack.c ### */ +#define dopickup() dopickup_() +#define identify(x) identify_(x) + + +/* ### invent.c ### */ +#define ckunpaid(x) ckunpaid_(x) +#define ddoinv() ddoinv_() +#define dolook() dolook_() +#define dopramulet() dopramulet_() +#define doprarm() doprarm_() +#define doprgold() doprgold_() +#define doprring() doprring_() +#define doprtool() doprtool_() +#define doprwep() doprwep_() +#define dotypeinv() dotypeinv_() +#define doorganize() doorganize_() + + +/* ### ioctl.c ### */ +#ifdef UNIX +# ifdef SUSPEND +#define dosuspend() dosuspend_() +# endif /* SUSPEND */ +#endif /* UNIX */ + + +/* ### lock.c ### */ +#define doclose() doclose_() +#define doforce() doforce_() +#define doopen() doopen_() +#define forcelock() forcelock_() +#define picklock() picklock_() + + +/* ### mklev.c ### */ +#define do_comp(x, y) comp_(x, y) + + +/* ### mondata.c ### */ +/* See comment in trampoli.c before uncommenting canseemon. */ +/* #define canseemon(x) canseemon_(x) */ + + +/* ### muse.c ### */ +#define mbhitm(x, y) mbhitm_(x, y) + + +/* ### o_init.c ### */ +#define dodiscovered() dodiscovered_() + + +/* ### objnam.c ### */ +#define doname(x) doname_(x) +#define xname(x) xname_(x) + + +/* ### options.c ### */ +#define doset() doset_() +#define dotogglepickup() dotogglepickup_() + + +/* ### pager.c ### */ +#define dohelp() dohelp_() +#define dohistory() dohistory_() +#ifdef UNIX +#define intruph() intruph_() +#endif /* UNIX */ +#define dowhatdoes() dowhatdoes_() +#define dowhatis() dowhatis_() +#define doquickwhatis() doquickwhatis_() + + +/* ### pcsys.c ### */ +#ifdef SHELL +#define dosh() dosh_() +#endif /* SHELL */ + + +/* ### pickup.c ### */ +#define ck_bag(x) ck_bag_(x) +#define doloot() doloot_() +#define in_container(x) in_container_(x) +#define out_container(x) out_container_(x) + + +/* ### potion.c ### */ +#define dodrink() dodrink_() +#define dodip() dodip_() + + +/* ### pray.c ### */ +#define doturn() doturn_() +#define dopray() dopray_() +#define prayer_done() prayer_done_() +#define dosacrifice() dosacrifice_() + + +/* ### read.c ### */ +#define doread() doread_() +#define set_lit(x, y, val) set_lit_(x, y, val) + + +/* ### rip.c ### */ +#define genl_outrip(tmpwin, how) genl_outrip_(tmpwin, how) + + +/* ### save.c ### */ +#define dosave() dosave_() +#if defined(UNIX) || defined (VMS) || defined(__EMX__) +#define hangup(sig) hangup_(sig) +#endif + + +/* ### search.c ### */ +#define doidtrap() doidtrap_() +#define dosearch() dosearch_() +#define findone(zx, zy, num) findone_(zx, zy, num) +#define openone(zx, zy, num) openone_(zx, zy, num) + + +/* ### shk.c ### */ +#define dopay() dopay_() + + +/* ### sit.c ### */ +#define dosit() dosit_() + + +/* ### sounds.c ### */ +#define dotalk() dotalk_() + + +/* ### spell.c ### */ +#define learn() learn_() +#define docast() docast_() +#define dovspell() dovspell_() + + +/* ### steal.c ### */ +#define stealarm() stealarm_() + + +/* ### trap.c ### */ +#define dotele() dotele_() +#define dountrap() dountrap_() +#define float_down() float_down_() + + +/* ### version.c ### */ +#define doversion() doversion_() +#define doextversion() doextversion_() + + +/* ### wield.c ### */ +#define dowield() dowield_() + + +/* ### zap.c ### */ +#define bhitm(x, y) bhitm_(x, y) +#define bhito(x, y) bhito_(x, y) +#define dozap() dozap_() + + +/* ### getline.c ### */ +#define tty_getlin(x,y) tty_getlin_(x,y) +#define tty_get_ext_cmd() tty_get_ext_cmd_() + + +/* ### termcap.c ### */ +#define tty_nhbell() tty_nhbell_() +#define tty_number_pad(x) tty_number_pad_(x) +#define tty_delay_output() tty_delay_output_() +#define tty_start_screen() tty_start_screen_() +#define tty_end_screen() tty_end_screen_() + + +/* ### topl.c ### */ +#define tty_doprev_message() tty_doprev_message_() +#define tty_yn_function(x,y,z) tty_yn_function_(x,y,z) + + +/* ### wintty.c ### */ +#define tty_init_nhwindows(x,y) tty_init_nhwindows_(x,y) +#define tty_player_selection() tty_player_selection_() +#define tty_askname() tty_askname_() +#define tty_get_nh_event() tty_get_nh_event_() +#define tty_exit_nhwindows(x) tty_exit_nhwindows_(x) +#define tty_suspend_nhwindows(x) tty_suspend_nhwindows_(x) +#define tty_resume_nhwindows() tty_resume_nhwindows_() +#define tty_create_nhwindow(x) tty_create_nhwindow_(x) +#define tty_clear_nhwindow(x) tty_clear_nhwindow_(x) +#define tty_display_nhwindow(x,y) tty_display_nhwindow_(x,y) +#define tty_destroy_nhwindow(x) tty_destroy_nhwindow_(x) +#define tty_curs(x,y,z) tty_curs_(x,y,z) +#define tty_putstr(x,y,z) tty_putstr_(x,y,z) +#define tty_display_file(x,y) tty_display_file_(x,y) +#define tty_start_menu(x) tty_start_menu_(x) +#define tty_add_menu(a,b,c,d,e,f,g,h) tty_add_menu_(a,b,c,d,e,f,g,h) +#define tty_end_menu(a,b) tty_end_menu_(a,b) +#define tty_select_menu(a,b,c) tty_select_menu_(a,b,c) +#define tty_update_inventory() tty_update_inventory_() +#define tty_mark_synch() tty_mark_synch_() +#define tty_wait_synch() tty_wait_synch_() +#ifdef CLIPPING +#define tty_cliparound(x,y) tty_cliparound_(x,y) +#endif +#ifdef POSITIONBAR +#define tty_update_positionbar(x) tty_update_positionbar_(x) +#endif +#define tty_print_glyph(a,b,c,d) tty_print_glyph_(a,b,c,d) +#define tty_raw_print(x) tty_raw_print_(x) +#define tty_raw_print_bold(x) tty_raw_print_bold_(x) +#define tty_nhgetch() tty_nhgetch_() +#define tty_nh_poskey(x,y,z) tty_nh_poskey_(x,y,z) + +#endif /* USE_TRAMPOLI */ + +#endif /* TRAMPOLI_H */ diff --git a/include/trap.h b/include/trap.h new file mode 100644 index 0000000..f74f328 --- /dev/null +++ b/include/trap.h @@ -0,0 +1,77 @@ +/* SCCS Id: @(#)trap.h 3.4 2000/08/30 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* note for 3.1.0 and later: no longer manipulated by 'makedefs' */ + +#ifndef TRAP_H +#define TRAP_H + +union vlaunchinfo { + short v_launch_otyp; /* type of object to be triggered */ + coord v_launch2; /* secondary launch point (for boulders) */ +}; + +struct trap { + struct trap *ntrap; + xchar tx,ty; + d_level dst; /* destination for portals */ + coord launch; + Bitfield(ttyp,5); + Bitfield(tseen,1); + Bitfield(once,1); + Bitfield(madeby_u,1); /* So monsters may take offence when you trap + them. Recognizing who made the trap isn't + completely unreasonable, everybody has + their own style. This flag is also needed + when you untrap a monster. It would be too + easy to make a monster peaceful if you could + set a trap for it and then untrap it. */ + union vlaunchinfo vl; +#define launch_otyp vl.v_launch_otyp +#define launch2 vl.v_launch2 +}; + +extern struct trap *ftrap; +#define newtrap() (struct trap *) alloc(sizeof(struct trap)) +#define dealloc_trap(trap) free((genericptr_t) (trap)) + +/* reasons for statue animation */ +#define ANIMATE_NORMAL 0 +#define ANIMATE_SHATTER 1 +#define ANIMATE_SPELL 2 + +/* reasons for animate_statue's failure */ +#define AS_OK 0 /* didn't fail */ +#define AS_NO_MON 1 /* makemon failed */ +#define AS_MON_IS_UNIQUE 2 /* statue monster is unique */ + +/* Note: if adding/removing a trap, adjust trap_engravings[] in mklev.c */ + +/* unconditional traps */ +#define NO_TRAP 0 +#define ARROW_TRAP 1 +#define DART_TRAP 2 +#define ROCKTRAP 3 +#define SQKY_BOARD 4 +#define BEAR_TRAP 5 +#define LANDMINE 6 +#define ROLLING_BOULDER_TRAP 7 +#define SLP_GAS_TRAP 8 +#define RUST_TRAP 9 +#define FIRE_TRAP 10 +#define PIT 11 +#define SPIKED_PIT 12 +#define HOLE 13 +#define TRAPDOOR 14 +#define TELEP_TRAP 15 +#define LEVEL_TELEP 16 +#define MAGIC_PORTAL 17 +#define WEB 18 +#define STATUE_TRAP 19 +#define MAGIC_TRAP 20 +#define ANTI_MAGIC 21 +#define POLY_TRAP 22 +#define TRAPNUM 23 + +#endif /* TRAP_H */ diff --git a/include/unixconf.h b/include/unixconf.h new file mode 100644 index 0000000..fe1b006 --- /dev/null +++ b/include/unixconf.h @@ -0,0 +1,351 @@ +/* SCCS Id: @(#)unixconf.h 3.4 1999/07/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef UNIX +#ifndef UNIXCONF_H +#define UNIXCONF_H + +/* + * Some include files are in a different place under SYSV + * BSD SYSV + * + * + * + * Some routines are called differently + * index strchr + * rindex strrchr + * + */ + +/* define exactly one of the following four choices */ +/* #define BSD 1 */ /* define for 4.n/Free/Open/Net BSD */ + /* also for relatives like SunOS 4.x, DG/UX, and */ + /* older versions of Linux */ +/* #define ULTRIX */ /* define for Ultrix v3.0 or higher (but not lower) */ + /* Use BSD for < v3.0 */ + /* "ULTRIX" not to be confused with "ultrix" */ +#define SYSV /* define for System V, Solaris 2.x, newer versions */ + /* of Linux */ +/* #define HPUX */ /* Hewlett-Packard's Unix, version 6.5 or higher */ + /* use SYSV for < v6.5 */ + + +/* define any of the following that are appropriate */ +#define SVR4 /* use in addition to SYSV for System V Release 4 */ + /* including Solaris 2+ */ +#define NETWORK /* if running on a networked system */ + /* e.g. Suns sharing a playground through NFS */ +/* #define SUNOS4 */ /* SunOS 4.x */ +/* #define LINUX */ /* Another Unix clone */ +/* #define CYGWIN32 */ /* Unix on Win32 -- use with case sensitive defines */ +/* #define GENIX */ /* Yet Another Unix Clone */ +/* #define HISX */ /* Bull Unix for XPS Machines */ +/* #define BOS */ /* Bull Open Software - Unix for DPX/2 Machines */ +/* #define UNIXPC */ /* use in addition to SYSV for AT&T 7300/3B1 */ +/* #define AIX_31 */ /* In AIX 3.1 (IBM RS/6000) use BSD ioctl's to gain + * job control (note that AIX is SYSV otherwise) + * Also define this for AIX 3.2 */ + +#define TERMINFO /* uses terminfo rather than termcap */ + /* Should be defined for most SYSV, SVR4 (including + * Solaris 2+), HPUX, and Linux systems. In + * particular, it should NOT be defined for the UNIXPC + * unless you remove the use of the shared library in + * the Makefile */ +#define TEXTCOLOR /* Use System V r3.2 terminfo color support */ + /* and/or ANSI color support on termcap systems */ + /* and/or X11 color */ +#define POSIX_JOB_CONTROL /* use System V / Solaris 2.x / POSIX job control */ + /* (e.g., VSUSP) */ +#define POSIX_TYPES /* use POSIX types for system calls and termios */ + /* Define for many recent OS releases, including + * those with specific defines (since types are + * changing toward the standard from earlier chaos). + * For example, platforms using the GNU libraries, + * Linux, Solaris 2.x + */ + +/* #define OPENWINBUG */ /* avoid a problem using OpenWindows 3.0 for + X11 on SunOS 4.1.x, x>= 2. Do not define + for other X11 implementations. */ +/* #define PYRAMID_BUG */ /* avoid a bug on the Pyramid */ +/* #define BSD_43_BUG */ /* for real 4.3BSD cc's without schain botch fix */ +/* #define MICROPORT_BUG */ /* problems with large arrays in structs */ +/* #define MICROPORT_286_BUG */ /* changes needed in termcap.c to get it to + run with Microport Sys V/AT version 2.4. + By Jay Maynard */ +/* #define AIXPS_2BUG */ /* avoid a problem with little_to_big() optimization */ + +/* #define RANDOM */ /* if neither random/srandom nor lrand48/srand48 + is available from your system */ + +/* see sys/unix/snd86unx.shr for more information on these */ +/* #define UNIX386MUSIC */ /* play real music through speaker on systems + with music driver installed */ +/* #define VPIX_MUSIC */ /* play real music through speaker on systems + with built-in VPIX support */ + + +/* + * The next two defines are intended mainly for the Andrew File System, + * which does not allow hard links. If NO_FILE_LINKS is defined, lock files + * will be created in LOCKDIR using open() instead of in the playground using + * link(). + * Ralf Brown, 7/26/89 (from v2.3 hack of 10/10/88) + */ + +/* #define NO_FILE_LINKS */ /* if no hard links */ +/* #define LOCKDIR "/usr/games/lib/nethackdir" */ /* where to put locks */ + +/* + * If you want the static parts of your playground on a read-only file + * system, define VAR_PLAYGROUND to be where the variable parts are kept. + */ +/* #define VAR_PLAYGROUND "/var/lib/games/nethack" */ + + +/* + * Define DEF_PAGER as your default pager, e.g. "/bin/cat" or "/usr/ucb/more" + * If defined, it can be overridden by the environment variable PAGER. + * Hack will use its internal pager if DEF_PAGER is not defined. + * (This might be preferable for security reasons.) + * #define DEF_PAGER ".../mydir/mypager" + */ + + + +/* + * Define PORT_HELP to be the name of the port-specfic help file. + * This file is found in HACKDIR. + * Normally, you shouldn't need to change this. + * There is currently no port-specific help for Unix systems. + */ +/* #define PORT_HELP "Unixhelp" */ + +#ifdef TTY_GRAPHICS +/* + * To enable the `timed_delay' option for using a timer rather than extra + * screen output when pausing for display effect. Requires that `msleep' + * function be available (with time argument specified in milliseconds). + * Various output devices can produce wildly varying delays when the + * "extra output" method is used, but not all systems provide access to + * a fine-grained timer. + */ +/* #define TIMED_DELAY */ /* usleep() */ +#endif + +/* + * If you define MAIL, then the player will be notified of new mail + * when it arrives. If you also define DEF_MAILREADER then this will + * be the default mail reader, and can be overridden by the environment + * variable MAILREADER; otherwise an internal pager will be used. + * A stat system call is done on the mailbox every MAILCKFREQ moves. + */ + +#define MAIL /* Deliver mail during the game */ + +/* The Andrew Message System does mail a little differently from normal + * UNIX. Mail is deposited in the user's own directory in ~/Mailbox + * (another directory). MAILBOX is the element that will be added on to + * the user's home directory path to generate the Mailbox path - just in + * case other Andrew sites do it differently from CMU. + * + * dan lovinger + * dl2n+@andrew.cmu.edu (dec 19 1989) + */ + +/* #define AMS */ /* use Andrew message system for mail */ + +/* NO_MAILREADER is for kerberos authenticating filesystems where it is + * essentially impossible to securely exec child processes, like mail + * readers, when the game is running under a special token. + * + * dan + */ + +/* #define NO_MAILREADER */ /* have mail daemon just tell player of mail */ + +#ifdef MAIL +# if defined(BSD) || defined(ULTRIX) +# ifdef AMS +#define AMS_MAILBOX "/Mailbox" +# else +# if defined(__FreeBSD__) || defined(__OpenBSD__) +#define DEF_MAILREADER "/usr/bin/mail" +# else +#define DEF_MAILREADER "/usr/ucb/Mail" +# endif +# endif +#else +# if (defined(SYSV) || defined(DGUX) || defined(HPUX)) && !defined(LINUX) +# if defined(M_XENIX) +#define DEF_MAILREADER "/usr/bin/mail" +# else +# ifdef __sgi +#define DEF_MAILREADER "/usr/sbin/Mail" +# else +#define DEF_MAILREADER "/usr/bin/mailx" +# endif +# endif +# else +#define DEF_MAILREADER "/bin/mail" +# endif +#endif + +#define MAILCKFREQ 50 +#endif /* MAIL */ + + + +#ifdef COMPRESS +/* Some implementations of compress need a 'quiet' option. + * If you've got one of these versions, put -q here. + * You can also include any other strange options your compress needs. + * If you have a normal compress, just leave it commented out. + */ +/* #define COMPRESS_OPTIONS "-q" */ +#endif + +#define FCMASK 0660 /* file creation mask */ + + +/* + * The remainder of the file should not need to be changed. + */ + +#ifdef _AUX_SOURCE +# ifdef AUX /* gcc ? */ +# define _SYSV_SOURCE +# define _BSD_SOURCE +#else +# define AUX +# endif +#endif /* _AUX_SOURCE */ + +#if defined(LINUX) || defined(bsdi) +# ifndef POSIX_TYPES +# define POSIX_TYPES +# endif +# ifndef POSIX_JOB_CONTROL +# define POSIX_JOB_CONTROL +# endif +#endif + +/* + * BSD/ULTRIX systems are normally the only ones that can suspend processes. + * Suspending NetHack processes cleanly should be easy to add to other systems + * that have SIGTSTP in the Berkeley sense. Currently the only such systems + * known to work are HPUX and AIX 3.1; other systems will probably require + * tweaks to unixtty.c and ioctl.c. + * + * POSIX defines a slightly different type of job control, which should be + * equivalent for NetHack's purposes. POSIX_JOB_CONTROL should work on + * various recent SYSV versions (with possibly tweaks to unixtty.c again). + */ +#ifndef POSIX_JOB_CONTROL +# if defined(BSD) || defined(ULTRIX) || defined(HPUX) || defined(AIX_31) +# define BSD_JOB_CONTROL +# else +# if defined(SVR4) +# define POSIX_JOB_CONTROL +# endif +# endif +#endif +#if defined(BSD_JOB_CONTROL) || defined(POSIX_JOB_CONTROL) || defined(AUX) +#define SUSPEND /* let ^Z suspend the game */ +#endif + + +#if defined(BSD) || defined(ULTRIX) +#include +#else +#include +#endif + +#define HLOCK "perm" /* an empty file used for locking purposes */ + +#ifndef REDO +#define Getchar nhgetch +#endif +#define tgetch getchar + +#define SHELL /* do not delete the '!' command */ + +#include "system.h" + +#if defined(POSIX_TYPES) || defined(__GNUC__) +#include +#include +#endif + +#if defined(POSIX_TYPES) || defined(__GNUC__) || defined(BSD) || defined(ULTRIX) +#include +#endif + +#if defined(BSD) || defined(ULTRIX) +# if !defined(DGUX) && !defined(SUNOS4) +#define memcpy(d, s, n) bcopy(s, d, n) +#define memcmp(s1, s2, n) bcmp(s2, s1, n) +# endif +# ifdef SUNOS4 +#include +# endif +#else /* therefore SYSV */ +# ifndef index /* some systems seem to do this for you */ +#define index strchr +# endif +# ifndef rindex +#define rindex strrchr +# endif +#endif + +/* Use the high quality random number routines. */ +#if defined(BSD) || defined(LINUX) || defined(ULTRIX) || defined(CYGWIN32) || defined(RANDOM) || defined(__APPLE__) +#define Rand() random() +#else +#define Rand() lrand48() +#endif + +#ifdef TIMED_DELAY +# if defined(SUNOS4) || defined(LINUX) || (defined(BSD) && !defined(ULTRIX)) +# define msleep(k) usleep((k)*1000) +# endif +# ifdef ULTRIX +# define msleep(k) napms(k) +# endif +#endif + +#ifdef hc /* older versions of the MetaWare High-C compiler define this */ +# ifdef __HC__ +# undef __HC__ +# endif +# define __HC__ hc +# undef hc +#endif + +#if defined(GNOME_GRAPHICS) +#if defined(LINUX) +# include +# if defined(__NR_getresuid) && defined(__NR_getresgid) /* ie., >= v2.1.44 */ +# define GETRES_SUPPORT +# endif +#else +# if defined(BSD) || defined(SVR4) +/* + * [ALI] We assume that SVR4 means we can safely include syscall.h + * (although it's really a BSDism). This is certainly true for Solaris 2.5, + * Solaris 7, Solaris 8 and Compaq Tru64 5.1 + * Later BSD systems will have the getresid system calls. + */ +# include +# if (defined (SYS_getuid) || defined(SYS_getresuid)) && \ + (defined(SYS_getgid) || defined(SYS_getresgid)) +# define GETRES_SUPPORT +# endif +# endif /* BSD || SVR4 */ +#endif /* LINUX */ +#endif /* GNOME_GRAPHICS */ + +#endif /* UNIXCONF_H */ +#endif /* UNIX */ diff --git a/include/vault.h b/include/vault.h new file mode 100644 index 0000000..6e0a162 --- /dev/null +++ b/include/vault.h @@ -0,0 +1,27 @@ +/* SCCS Id: @(#)vault.h 3.4 1997/05/01 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef VAULT_H +#define VAULT_H + +#define FCSIZ (ROWNO+COLNO) +struct fakecorridor { + xchar fx,fy,ftyp; +}; + +struct egd { + int fcbeg, fcend; /* fcend: first unused pos */ + int vroom; /* room number of the vault */ + xchar gdx, gdy; /* goal of guard's walk */ + xchar ogx, ogy; /* guard's last position */ + d_level gdlevel; /* level (& dungeon) guard was created in */ + xchar warncnt; /* number of warnings to follow */ + Bitfield(gddone,1); /* true iff guard has released player */ + Bitfield(unused,7); + struct fakecorridor fakecorr[FCSIZ]; +}; + +#define EGD(mon) ((struct egd *)&(mon)->mextra[0]) + +#endif /* VAULT_H */ diff --git a/include/vision.h b/include/vision.h new file mode 100644 index 0000000..29396eb --- /dev/null +++ b/include/vision.h @@ -0,0 +1,58 @@ +/* SCCS Id: @(#)vision.h 3.4 1995/01/26 */ +/* Copyright (c) Dean Luick, with acknowledgements to Dave Cohrs, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef VISION_H +#define VISION_H + +#if 0 /* (moved to decl.h) */ +extern boolean vision_full_recalc; /* TRUE if need vision recalc */ +extern char **viz_array; /* could see/in sight row pointers */ +extern char *viz_rmin; /* min could see indices */ +extern char *viz_rmax; /* max could see indices */ +#endif +#define COULD_SEE 0x1 /* location could be seen, if it were lit */ +#define IN_SIGHT 0x2 /* location can be seen */ +#define TEMP_LIT 0x4 /* location is temporarily lit */ + +/* + * Light source sources + */ +#define LS_OBJECT 0 +#define LS_MONSTER 1 + +/* + * cansee() - Returns true if the hero can see the location. + * + * couldsee() - Returns true if the hero has a clear line of sight to + * the location. + */ +#define cansee(x,y) (viz_array[y][x] & IN_SIGHT) +#define couldsee(x,y) (viz_array[y][x] & COULD_SEE) +#define templit(x,y) (viz_array[y][x] & TEMP_LIT) + +/* + * The following assume the monster is not blind. + * + * m_cansee() - Returns true if the monster can see the given location. + * + * m_canseeu() - Returns true if the monster could see the hero. Assumes + * that if the hero has a clear line of sight to the monster's + * location and the hero is visible, then monster can see the + * hero. + */ +#define m_cansee(mtmp,x2,y2) clear_path((mtmp)->mx,(mtmp)->my,(x2),(y2)) + +#define m_canseeu(m) ((!Invis || perceives((m)->data)) && \ + !(Underwater || u.uburied || (m)->mburied) ? \ + couldsee((m)->mx,(m)->my) : 0) + +/* + * Circle information + */ +#define MAX_RADIUS 15 /* this is in points from the source */ + +/* Use this macro to get a list of distances of the edges (see vision.c). */ +#define circle_ptr(z) (&circle_data[(int)circle_start[z]]) + +#endif /* VISION_H */ diff --git a/include/vmsconf.h b/include/vmsconf.h new file mode 100644 index 0000000..421d68a --- /dev/null +++ b/include/vmsconf.h @@ -0,0 +1,265 @@ +/* SCCS Id: @(#)vmsconf.h 3.4 2003/05/19 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef VMS +#ifndef VMSCONF_H +#define VMSCONF_H + +/* + * Edit these to choose values appropriate for your site. + * WIZARD is the username allowed to use the debug option of nethack; no harm + * is done by leaving it as a username that doesn't exist at your site. + * HACKDIR can be overridden at run-time with the logical name HACKDIR, as in + * $ define hackdir disk$users:[games.nethack] + * Trailing NULs are present in the default values in order to make some + * extra room for patching longer values into an existing executable. + */ +#define Local_WIZARD "NHWIZARD\0\0\0\0" +#define Local_HACKDIR "DISK$USERS:[GAMES.NETHACK.3_4_X.PLAY]\0\0\0\0\0\0\0\0" + +/* + * This section cleans up the stuff done in config.h so that it + * shouldn't need to be modified. It's conservative so that if + * config.h is actually edited, the changes won't impact us. + */ +#ifdef UNIX +# undef UNIX +#endif +#ifdef HACKDIR +# undef HACKDIR +#endif +#ifdef WIZARD +# undef WIZARD +#endif +#ifdef WIZARD_NAME +# undef WIZARD_NAME +#endif +#define HACKDIR Local_HACKDIR +#ifndef KR1ED +# define WIZARD Local_WIZARD +# define WIZARD_NAME WIZARD +#else +# define WIZARD 1 +# define WIZARD_NAME Local_WIZARD +#endif + +/* filenames require punctuation to avoid redirection via logical names */ +#undef RECORD +#define RECORD "record;1" /* scoreboard file (retains high scores) */ +#undef LOGFILE +#define LOGFILE "logfile;0" /* optional file (records all games) */ + +#define HLOCK "perm;1" /* an empty file used for locking purposes */ + +/* want compression--for level & save files--performed within NetHack itself */ +#ifdef COMPRESS +# undef COMPRESS +#endif +#ifndef INTERNAL_COMP +# define INTERNAL_COMP +#endif + +/* + * If nethack.exe will be installed with privilege so that the playground + * won't need to be left unprotected, define SECURE to suppress a couple + * of file protection fixups (protection of bones files and ownership of + * save files). + */ +/* #define SECURE */ + +/* + * Put the readonly data files into a single container rather than into + * separate files in the playground directory. + */ +#define DLB /* use data librarian code */ + +/* + * You may define TEXTCOLOR if your system has any terminals that recognize + * ANSI color sequences of the form ``[#;#m'', where the first # is + * a number between 40 and 47 represented background color, and the second + * # is a number between 30 and 37 representing the foreground color. + * GIGI terminals and DECterm windows on color VAXstations support these + * color escape sequences, as do some 3rd party terminals and many micro + * computers. + */ +/* #define TEXTCOLOR */ + +/* + * If you define USE_QIO_INPUT, then you'll get raw characters from the + * keyboard, not unlike those of the unix version of Nethack. This will + * allow you to use the Escape key in normal gameplay, and the appropriate + * control characters in Wizard mode. It will work most like the unix version. + * It will also avoid "" being displayed when ^Y is pressed. + * + * Otherwise, the VMS SMG calls will be used. These calls block use of + * the escape key, as well as certain control keys, so gameplay is not + * the same, although the differences are fairly negligible. You must + * then use a VTxxx function key or two s to give an ESC response. + */ +#define USE_QIO_INPUT /* use SYS$QIOW instead of SMG$READ_KEYSTROKE */ + +/* + * Allow the user to decide whether to pause via timer or excess screen + * output for various display effects like explosions and moving objects. + */ +#define TIMED_DELAY /* enable the `timed_delay' run-time option */ + +/* + * If you define MAIL, then NetHack will capture incoming broadcast + * messages such as "New mail from so-and-so" and "Print job completed," + * and then deliver them to the player. For mail and phone broadcasts + * a scroll of mail will be created, which when read will cause NetHack + * to prompt the player for a command to spawn in order to respond. The + * latter capability will not be available if SHELL is disabled below. + * If you undefine MAIL, broadcasts will go straight to the terminal, + * resulting in disruption of the screen display; use to redraw. + */ +#define MAIL /* enable broadcast trapping */ + +/* + * SHELL enables the player to 'escape' into a spawned subprocess via + * the '!' command. Logout or attach back to the parent to resume play. + * If the player attaches back to NetHack, then a subsequent escape will + * re-attach to the existing subprocess. Any such subprocess left over + * at game exit will be deleted by an exit handler. + * SUSPEND enables someone running NetHack in a subprocess to reconnect + * to the parent process with the command; this is not very + * close to Unix job control, but it's better than nothing. + */ +#define SHELL /* do not delete the '!' command */ +#define SUSPEND /* don't delete the ^Z command, such as it is */ + +#define RANDOM /* use sys/share/random.c instead of vaxcrtl rand */ + +#define FCMASK 0660 /* file creation mask */ + + +/* + * The remainder of the file should not need to be changed. + */ + +/* data librarian defs */ +#ifdef DLB +# define DLBFILE "nh-data.dlb" + /* + * Since we can do without case insensitive filename comparison, + * avoid enabling it because that requires compiling and linking + * src/hacklib into util/dlb_main. + */ +/* # define FILENAME_CMP strcmpi */ /* case insensitive */ +#endif + +#if defined(VAXC) && !defined(ANCIENT_VAXC) +# ifdef volatile +# undef volatile +# endif +# ifdef const +# undef const +# endif +#endif + +#ifdef __DECC +# define STRICT_REF_DEF /* used in lev_main.c */ +#endif +#ifdef STRICT_REF_DEF +# define DEFINE_OSPEED +#endif + +#ifndef alloca + /* bison generated foo_yacc.c might try to use alloca() */ +# ifdef __GNUC__ +# define alloca __builtin_alloca +# else +# define ALLOCA_HACK /* used in util/panic.c */ +# endif +#endif + +#ifdef _DECC_V4_SOURCE +/* excludes some necessary typedefs when _DECC_V4_SOURCE is defined */ +#include +# ifndef __PID_T +# define __PID_T +typedef __pid_t pid_t; +# endif +# ifndef __UID_T +# define __UID_T +typedef __uid_t uid_t; +# endif +# ifndef __GID_T +# define __GID_T +typedef __gid_t gid_t; +# endif +# ifndef __MODE_T +# define __MODE_T +typedef __mode_t mode_t; +# endif +#endif /* _DECC_V4_SOURCE */ + +#include +#if 0 /* is missing for old gcc versions; skip it to save time */ +#include +#else /* values needed from missing include file */ +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_RDWR 2 +# define O_CREAT 0x200 +# define O_TRUNC 0x400 +#endif + +#ifndef REDO +# define Getchar nhgetch +#endif +#define tgetch vms_getchar + +#include "system.h" + +#define index strchr +#define rindex strrchr + +/* Use the high quality random number routines. */ +#if defined(RANDOM) +#define Rand() random() +/* VMS V7 adds these entry points to DECC$SHR; stick with the nethack-supplied + code to avoid having to deal with version-specific conditionalized builds */ +#define random nh_random +#define srandom nh_srandom +#define initstate nh_initstate +#define setstate nh_setstate +#else +#define Rand() rand() +#endif + +#ifndef __GNUC__ +# ifndef bcopy +#define bcopy(s,d,n) memcpy((d),(s),(n)) /* vaxcrtl */ +# endif +#endif +#define abort() vms_abort() /* vmsmisc.c */ +#define creat(f,m) vms_creat(f,m) /* vmsfiles.c */ +#define exit(sts) vms_exit(sts) /* vmsmisc.c */ +#define getuid() vms_getuid() /* vmsunix.c */ +#define link(f1,f2) vms_link(f1,f2) /* vmsfiles.c */ +#define open(f,k,m) vms_open(f,k,m) /* vmsfiles.c */ +/* #define unlink(f0) vms_unlink(f0) /* vmsfiles.c */ +#ifdef VERYOLD_VMS +#define unlink(f0) delete(f0) /* vaxcrtl */ +#else +#define unlink(f0) remove(f0) /* vaxcrtl, decc$shr */ +#endif +#define C$$TRANSLATE(n) c__translate(n) /* vmsfiles.c */ + +/* VMS global names are case insensitive... */ +#define An vms_an +#define The vms_the +#define Shk_Your vms_shk_your + +/* avoid global symbol in Alpha/VMS V1.5 STARLET library (link trouble) */ +#define ospeed vms_ospeed + +/* used in several files which don't #include "extern.h" */ +extern void FDECL(vms_exit, (int)); +extern int FDECL(vms_open, (const char *,int,unsigned)); + +#endif /* VMSCONF_H */ +#endif /* VMS */ diff --git a/include/wceconf.h b/include/wceconf.h new file mode 100644 index 0000000..0ecbe53 --- /dev/null +++ b/include/wceconf.h @@ -0,0 +1,354 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* Copyright (c) NetHack PC Development Team 1993, 1994. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WCECONF_H +#define WCECONF_H + +#pragma warning(disable:4142) /* benign redefinition of type */ + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +/* Detect the targe device */ +#if defined(WIN32_PLATFORM_PSPC) +# if _WIN32_WCE >= 300 +# define WIN_CE_POCKETPC +# else +# define WIN_CE_PS2xx +# endif +#elif defined(WIN32_PLATFORM_HPCPRO) +# define WIN_CE_HPCPRO +#elif defined(WIN32_PLATFORM_WFSP) +# define WIN_CE_SMARTPHONE +#else +# error "Unsupported Windows CE platform" +#endif + +/* #define SHELL /* nt use of pcsys routines caused a hang */ + +#define RANDOM /* have Berkeley random(3) */ +#define TEXTCOLOR /* Color text */ + +#define EXEPATH /* Allow .exe location to be used as HACKDIR */ +#define TRADITIONAL_GLYPHMAP /* Store glyph mappings at level change time */ + +#define PC_LOCKING /* Prevent overwrites of aborted or in-progress games */ + /* without first receiving confirmation. */ + +#define SELF_RECOVER /* Allow the game itself to recover from an aborted game */ + +#define NOTSTDC /* no strerror() */ + +#define USER_SOUNDS + +#define AUTOPICKUP_EXCEPTIONS + +/* + * ----------------------------------------------------------------- + * The remaining code shouldn't need modification. + * ----------------------------------------------------------------- + */ +/* #define SHORT_FILENAMES /* All NT filesystems support long names now */ + +#ifdef MICRO +#undef MICRO /* never define this! */ +#endif + +#define NOCWD_ASSUMPTIONS /* Always define this. There are assumptions that + it is defined for WIN32. + Allow paths to be specified for HACKDIR, + LEVELDIR, SAVEDIR, BONESDIR, DATADIR, + SCOREDIR, LOCKDIR, CONFIGDIR, and TROUBLEDIR */ +#define NO_TERMS +#define ASCIIGRAPH + +#ifdef OPTIONS_USED +#undef OPTIONS_USED +#endif +#ifdef MSWIN_GRAPHICS +#define OPTIONS_USED "guioptions" +#else +#define OPTIONS_USED "ttyoptions" +#endif +#define OPTIONS_FILE OPTIONS_USED + +#define PORT_HELP "porthelp" + +#if defined(WIN_CE_POCKETPC) +# define PORT_CE_PLATFORM "Pocket PC" +#elif defined(WIN_CE_PS2xx) +# define PORT_CE_PLATFORM "Palm-size PC 2.11" +#elif defined(WIN_CE_HPCPRO) +# define PORT_CE_PLATFORM "H/PC Pro 2.11" +#elif defined(WIN_CE_SMARTPHONE) +# define PORT_CE_PLATFORM "Smartphone 2002" +#endif + +#if defined(ARM) +# define PORT_CE_CPU "ARM" +#elif defined(PPC) +# define PORT_CE_CPU "PPC" +#elif defined(ALPHA) +# define PORT_CE_CPU "ALPHA" +#elif defined(SH3) +# define PORT_CE_CPU "SH3" +#elif defined(SH4) +# define PORT_CE_CPU "SH4" +#elif defined(MIPS) +# define PORT_CE_CPU "MIPS" +#elif defined(X86) || defined(_X86_) +# define PORT_CE_CPU "X86" +#else +# error Only ARM, PPC, ALPHA, SH3, SH4, MIPS and X86 supported +#endif + +#define RUNTIME_PORT_ID /* trigger run-time port identification since + Makedefs is bootstrapped on a cross-platform. */ + +#include /* Provides prototypes of strncmpi(), etc. */ +#ifdef STRNCMPI +#define strncmpi(a,b,c) _strnicmp(a,b,c) +#endif + +#ifdef STRCMPI +#define strcmpi(a,b) _stricmp(a,b) +#define stricmp(a,b) _stricmp(a,b) +#endif + +#include + +#define PATHLEN BUFSZ /* maximum pathlength */ +#define FILENAME BUFSZ /* maximum filename length (conservative) */ + +#if defined(_MAX_PATH) && defined(_MAX_FNAME) +# if (_MAX_PATH < BUFSZ) && (_MAX_FNAME < BUFSZ) +#undef PATHLEN +#undef FILENAME +#define PATHLEN _MAX_PATH +#define FILENAME _MAX_FNAME +# endif +#endif + + +#define NO_SIGNAL +#define index strchr +#define rindex strrchr +#define USE_STDARG +#ifdef RANDOM +/* Use the high quality random number routines. */ +#define Rand() random() +#else +#define Rand() rand() +#endif + +#define FCMASK 0660 /* file creation mask */ +#define regularize nt_regularize +#define HLOCK "NHPERM" + +#ifndef M +#define M(c) ((char) (0x80 | (c))) +/* #define M(c) ((c) - 128) */ +#endif + +#ifndef C +#define C(c) (0x1f & (c)) +#endif + +#if defined(DLB) +#define FILENAME_CMP _stricmp /* case insensitive */ +#endif + +#if 0 +extern char levels[], bones[], permbones[], +#endif /* 0 */ + +/* this was part of the MICRO stuff in the past */ +extern const char *alllevels, *allbones; +extern char hackdir[]; +#define ABORT C('a') +#define getuid() 1 +#define getlogin() ((char *)0) +extern void NDECL(win32_abort); +#ifdef WIN32CON +extern void FDECL(nttty_preference_update, (const char *)); +extern void NDECL(toggle_mouse_support); +#endif + +#ifndef alloca +#define ALLOCA_HACK /* used in util/panic.c */ +#endif + +#ifndef REDO +#undef Getchar +#define Getchar nhgetch +#endif + +#ifdef _MSC_VER +#if 0 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4305) /* init, conv from 'const int' to 'char' */ +#endif +#pragma warning(disable:4761) /* integral size mismatch in arg; conv supp*/ +#ifdef YYPREFIX +#pragma warning(disable:4102) /* unreferenced label */ +#endif +#endif + +/* UNICODE stuff */ +#define NHSTR_BUFSIZE 255 +#ifdef UNICODE + #define NH_W2A(w, a, cb) ( WideCharToMultiByte( \ + CP_ACP, \ + 0, \ + (w), \ + -1, \ + (a), \ + (cb), \ + NULL, \ + NULL), (a) ) + + #define NH_A2W(a, w, cb) ( MultiByteToWideChar( \ + CP_ACP, \ + 0, \ + (a), \ + -1, \ + (w), \ + (cb)), (w) ) +#else + #define NH_W2A(w, a, cb) (strncpy((a), (w), (cb))) + + #define NH_A2W(a, w, cb) (strncpy((w), (a), (cb))) +#endif + +extern int FDECL(set_win32_option, (const char *, const char *)); + +/* + * 3.4.3 addition - Stuff to help the user with some common, yet significant errors + * Let's make it NOP for now + */ +#define interject_assistance(_1,_2,_3,_4) +#define interject(_1) + +/* Missing definitions */ +extern int mswin_have_input(); +#define kbhit mswin_have_input + +#define getenv(a) ((char*)NULL) + +/* __stdio.h__ */ +#define perror(a) +#define freopen(a, b, c) fopen(a, b) +extern int isatty(int); + +/* __time.h___ */ +#ifndef _TIME_T_DEFINED +typedef __int64 time_t; /* time value */ +#define _TIME_T_DEFINED /* avoid multiple def's of time_t */ +#endif + +#ifndef _TM_DEFINED +struct tm { + int tm_sec; /* seconds after the minute - [0,59] */ + int tm_min; /* minutes after the hour - [0,59] */ + int tm_hour; /* hours since midnight - [0,23] */ + int tm_mday; /* day of the month - [1,31] */ + int tm_mon; /* months since January - [0,11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday - [0,6] */ + int tm_yday; /* days since January 1 - [0,365] */ + int tm_isdst; /* daylight savings time flag - - NOT IMPLEMENTED */ + }; +#define _TM_DEFINED +#endif + +extern struct tm * __cdecl localtime(const time_t *); +extern time_t __cdecl time(time_t *); + +/* __stdio.h__ */ +#ifndef BUFSIZ +#define BUFSIZ 255 +#endif + +#define rewind(stream) (void)fseek( stream, 0L, SEEK_SET ) + +/* __io.h__ */ +typedef long off_t; + +extern int __cdecl close(int); +extern int __cdecl creat(const char *, int); +extern int __cdecl eof(int); +extern long __cdecl lseek(int, long, int); +extern int __cdecl open(const char *, int, ...); +extern int __cdecl read(int, void *, unsigned int); +extern int __cdecl unlink(const char *); +extern int __cdecl write(int, const void *, unsigned int); +extern int __cdecl rename(const char *, const char *); +extern int __cdecl access(const char *, int); + +#ifdef DeleteFile +#undef DeleteFile +#endif +#define DeleteFile(a) unlink(a) + +int chdir( const char *dirname ); +extern char *getcwd( char *buffer, int maxlen ); + +/* __stdlib.h__ */ +#define abort() (void)TerminateProcess(GetCurrentProcess(), 0) +#ifndef strdup +#define strdup _strdup +#endif + +/* sys/stat.h */ +#define S_IWRITE GENERIC_WRITE +#define S_IREAD GENERIC_READ + + +/* CE 2.xx is missing even more stuff */ +#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO) +#define ZeroMemory(p, s) memset((p), 0, (s)) + +extern int __cdecl isupper(int c); +extern int __cdecl isdigit(int c); +extern int __cdecl isspace(int c); +extern int __cdecl isprint(int c); + +extern char* __cdecl _strdup(const char* s); +extern char* __cdecl strrchr( const char *string, int c ); +extern int __cdecl _stricmp(const char* a, const char* b); + +extern FILE * __cdecl fopen(const char* filename, const char *mode); +extern int __cdecl fscanf(FILE *f , const char *format, ...); +extern int __cdecl fprintf(FILE *f , const char *format, ...); +extern int __cdecl vfprintf(FILE* f, const char *format, va_list args); +extern int __cdecl fgetc(FILE * f); +extern char * __cdecl fgets(char *s, int size, FILE *f); +extern int __cdecl printf(const char *format, ...); +extern int __cdecl vprintf(const char *format, va_list args); +extern int __cdecl puts(const char * s); +extern FILE* __cdecl _getstdfilex(int desc); +extern int __cdecl fclose(FILE * f); +extern size_t __cdecl fread(void *p, size_t size, size_t count, FILE *f); +extern size_t __cdecl fwrite(const void *p, size_t size, size_t count, FILE * f); +extern int __cdecl fflush(FILE *f); +extern int __cdecl feof(FILE *f); +extern int __cdecl fseek(FILE *f, long offset, int from); +extern long __cdecl ftell(FILE * f); + +#endif + +/* ARM - the processor; avoids conflict with ARM in hack.h */ +# ifdef ARM +# undef ARM +# endif + +/* leave - Windows CE defines leave as part of exception handling (__leave) + It confilicts with existing sources and since we don't use exceptions it is safe + to undefine it */ +# ifdef leave +# undef leave +# endif + +#endif /* WCECONF_H */ diff --git a/include/winGnome.h b/include/winGnome.h new file mode 100644 index 0000000..b2fad55 --- /dev/null +++ b/include/winGnome.h @@ -0,0 +1,18 @@ +/* SCCS Id: @(#)winGnome.h 3.4. 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* Copyright (C) 1998 by Anthony Taylor */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINGNOME_H +#define WINGNOME_H + +#define E extern + +E struct window_procs Gnome_procs; + +#undef E + +#define NHW_WORN 6 +extern winid WIN_WORN; + +#endif /* WINGNOME_H */ diff --git a/include/winX.h b/include/winX.h new file mode 100644 index 0000000..7ded018 --- /dev/null +++ b/include/winX.h @@ -0,0 +1,411 @@ +/* SCCS Id: @(#)winX.h 3.4 1996/08/18 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Definitions for the X11 window-port. See doc/window.doc for details on + * the window interface. + */ +#ifndef WINX_H +#define WINX_H + +#ifndef E +#define E extern +#endif + +#if defined(BOS) || defined(NHSTDC) +#define DIMENSION_P int +#else +# ifdef WIDENED_PROTOTYPES +#define DIMENSION_P unsigned int +# else +#define DIMENSION_P Dimension +# endif +#endif + +/* + * Generic text buffer. + */ +#define START_SIZE 512 /* starting text buffer size */ +struct text_buffer { + char *text; + int text_size; + int text_last; + int num_lines; +}; + + +/* + * Information specific to a map window. + */ +struct text_map_info_t { + unsigned char text[ROWNO][COLNO]; /* Actual displayed screen. */ +#ifdef TEXTCOLOR + unsigned char colors[ROWNO][COLNO]; /* Color of each character. */ + GC color_gcs[CLR_MAX], /* GC for each color */ + inv_color_gcs[CLR_MAX]; /* GC for each inverse color */ +#define copy_gc color_gcs[NO_COLOR] +#define inv_copy_gc inv_color_gcs[NO_COLOR] +#else + GC copy_gc, /* Drawing GC */ + inv_copy_gc; /* Inverse drawing GC */ +#endif +}; + +struct tile_map_info_t { + unsigned short glyphs[ROWNO][COLNO]; /* Saved glyph numbers. */ + GC white_gc; + GC black_gc; + unsigned long image_width; /* dimensions of tile image */ + unsigned long image_height; +}; + +struct map_info_t { + Dimension viewport_width, /* Saved viewport size, so we can */ + viewport_height; /* clip to cursor on a resize. */ + unsigned char t_start[ROWNO], /* Starting column for new info. */ + t_stop[ROWNO]; /* Ending column for new info. */ + int square_width, /* Saved font/tile information so */ + square_height, /* we can calculate the correct */ + square_ascent, /* placement of changes. */ + square_lbearing; + boolean is_tile; + union { + struct text_map_info_t *text_map; + struct tile_map_info_t *tile_map; + } mtype; +}; + + +/* + * Information specific to a message window. + */ +struct line_element { + struct line_element *next; + char *line; /* char buffer */ + int buf_length; /* length of buffer */ + int str_length; /* length of string in buffer */ +}; + +struct mesg_info_t { + XFontStruct *fs; /* Font for the window. */ + int num_lines; /* line count */ + struct line_element *head; /* head of circular line queue */ + struct line_element *line_here;/* current drawn line position */ + struct line_element *last_pause;/* point to the line after the prev */ + /* bottom of screen */ + struct line_element *last_pause_head;/* pointer to head of previous */ + /* turn */ + GC gc; /* GC for text drawing */ + int char_width, /* Saved font information so we can */ + char_height, /* calculate the correct placement */ + char_ascent, /* of changes. */ + char_lbearing; + Dimension viewport_width, /* Saved viewport size, so we can adjust */ + viewport_height;/* the slider on a resize. */ + Boolean dirty; /* Lines have been added to the window. */ +}; + +/* + * Information specific to a "text" status window. + */ +struct status_info_t { + struct text_buffer text; /* Just a text buffer. */ +}; + +/* + * Information specific to a menu window. First a structure for each + * menu entry, then the structure for each menu window. + */ +typedef struct x11_mi { + struct x11_mi *next; + anything identifier; /* Opaque type to identify this selection */ + long pick_count; /* specific selection count; -1 if none */ + char *str; /* The text of the item. */ + int attr; /* Attribute for the line. */ + boolean selected; /* Been selected? */ + char selector; /* Char used to select this entry. */ + char gselector; /* Group selector. */ +} x11_menu_item; + +struct menu { + x11_menu_item *base; /* Starting pointer for item list. */ + x11_menu_item *last; /* End pointer for item list. */ + const char *query; /* Query string. */ + const char *gacc; /* Group accelerators. */ + int count; /* Number of strings. */ + String *list_pointer;/* String list. */ + Boolean *sensitive; /* Active list. */ + char curr_selector;/* Next keyboard accelerator to assign, */ + /* if 0, then we're out. */ +}; + +struct menu_info_t { + struct menu curr_menu; /* Menu being displayed. */ + struct menu new_menu; /* New menu being built. */ + + XFontStruct *fs; /* Font for the window. */ + long menu_count; /* number entered by user */ + Dimension line_height; /* Total height of a line of text. */ + Dimension internal_height; /* Internal height between widget & border */ + Dimension internal_width; /* Internal width between widget & border */ + short how; /* Menu mode PICK_NONE, PICK_ONE, PICK_ANY */ + boolean valid_widgets; /* TRUE if widgets have been created. */ + boolean is_menu; /* Has been confirmed to being a menu window. */ + boolean is_active; /* TRUE when waiting for user input. */ + boolean is_up; /* TRUE when window is popped-up. */ + boolean cancelled; /* Menu has been explicitly cancelled. */ + boolean counting; /* true when menu_count has a valid value */ +}; + +/* + * Information specific to a text window. + */ +struct text_info_t { + struct text_buffer text; + XFontStruct *fs; /* Font for the text window. */ + int max_width; /* Width of widest line so far. */ + int extra_width, /* Sum of left and right border widths. */ + extra_height; /* Sum of top and bottom border widths. */ + boolean blocked; /* */ + boolean destroy_on_ack; /* Destroy this window when acknowleged. */ +#ifdef GRAPHIC_TOMBSTONE + boolean is_rip; /* This window needs a tombstone. */ +#endif +}; + + +/* + * Basic window structure. + */ +struct xwindow { + int type; /* type of nethack window */ + Widget popup; /* direct parent of widget w or viewport */ + Widget w; /* the widget that does things */ + Dimension pixel_width; /* window size, in pixels */ + Dimension pixel_height; + int prevx, cursx; /* Cursor position, only used by */ + int prevy, cursy; /* map and "plain" status windows.*/ + + union { + struct map_info_t *Map_info; /* map window info */ + struct mesg_info_t *Mesg_info; /* message window info */ + struct status_info_t *Status_info; /* status window info */ + struct menu_info_t *Menu_info; /* menu window info */ + struct text_info_t *Text_info; /* menu window info */ + } Win_info; + boolean keep_window; +}; + +/* Defines to use for the window information union. */ +#define map_information Win_info.Map_info +#define mesg_information Win_info.Mesg_info +#define status_information Win_info.Status_info +#define menu_information Win_info.Menu_info +#define text_information Win_info.Text_info + + +#define MAX_WINDOWS 20 /* max number of open windows */ + +#define NHW_NONE 0 /* Unallocated window type. Must be */ + /* different from any other NHW_* type. */ + +#define NO_CLICK 0 /* No click occured on the map window. Must */ + /* be different than CLICK_1 and CLICK_2. */ + +#define DEFAULT_MESSAGE_WIDTH 60/* width in chars of the message window */ + +#define DISPLAY_FILE_SIZE 35 /* Max number of lines in the default */ + /* file display window. */ + +#define MAX_KEY_STRING 64 /* String size for converting a keypress */ + /* event into a character(s) */ + +#define DEFAULT_LINES_DISPLAYED 12 /* # of lines displayed message window */ +#define MAX_HISTORY 60 /* max history saved on message window */ + + +/* Window variables (winX.c). */ +E struct xwindow window_list[MAX_WINDOWS]; +E XtAppContext app_context; /* context of application */ +E Widget toplevel; /* toplevel widget */ +E Atom wm_delete_window; /* delete window protocol */ +E boolean exit_x_event; /* exit condition for event loop */ +#define EXIT_ON_KEY_PRESS 0 /* valid values for exit_x_event */ +#define EXIT_ON_KEY_OR_BUTTON_PRESS 1 +#define EXIT_ON_EXIT 2 +#define EXIT_ON_SENT_EVENT 3 +E int click_x, click_y, click_button, updated_inventory; + +typedef struct { + Boolean slow; + Boolean autofocus; + Boolean message_line; + Boolean double_tile_size; /* double tile size */ + String tile_file; /* name of file to open for tiles */ + String icon; /* name of desired icon */ + int message_lines; /* number of lines to attempt to show */ + String pet_mark_bitmap; /* X11 bitmap file used to mark pets */ + Pixel pet_mark_color; /* color of pet mark */ +#ifdef GRAPHIC_TOMBSTONE + String tombstone; /* name of XPM file for tombstone */ + int tombtext_x; /* x-coord of center of first tombstone text */ + int tombtext_y; /* y-coord of center of first tombstone text */ + int tombtext_dx; /* x-displacement between tombstone line */ + int tombtext_dy; /* y-displacement between tombstone line */ +#endif +} AppResources; + +E AppResources appResources; +E void (*input_func)(); + +extern struct window_procs X11_procs; + +/* Check for an invalid window id. */ +#define check_winid(window) \ + if ((window) < 0 || (window) >= MAX_WINDOWS) { \ + panic("illegal windid [%d] in %s at line %d", \ + window, __FILE__, __LINE__); \ + } + + +/* ### dialogs.c ### */ +E Widget FDECL(CreateDialog, (Widget, String, XtCallbackProc, XtCallbackProc)); +E void FDECL(SetDialogPrompt,(Widget, String)); +E String FDECL(GetDialogResponse,(Widget)); +E void FDECL(SetDialogResponse,(Widget, String)); +E void FDECL(positionpopup,(Widget,BOOLEAN_P)); + +/* ### winX.c ### */ +E struct xwindow *FDECL(find_widget,(Widget)); +E Boolean FDECL(nhApproxColor,(Screen*, Colormap, char*, XColor*)); +E Dimension FDECL(nhFontHeight,(Widget)); +E char FDECL(key_event_to_char,(XKeyEvent*)); +E void FDECL(msgkey, (Widget, XtPointer, XEvent*)); +E void FDECL(nh_XtPopup, (Widget, int, Widget)); +E void FDECL(nh_XtPopdown, (Widget)); +E void NDECL(win_X11_init); +E void FDECL(nh_keyscroll, (Widget, XEvent*, String*, Cardinal*)); + +/* ### winmesg.c ### */ +E void FDECL(set_message_slider, (struct xwindow*)); +E void FDECL(create_message_window,(struct xwindow*, BOOLEAN_P, Widget)); +E void FDECL(destroy_message_window,(struct xwindow*)); +E void FDECL(display_message_window, (struct xwindow*)); +E void FDECL(append_message,(struct xwindow*, const char*)); +E void FDECL(set_last_pause, (struct xwindow*)); + +/* ### winmap.c ### */ +E void NDECL(post_process_tiles); +E void FDECL(check_cursor_visibility,(struct xwindow*)); +E void FDECL(display_map_window,(struct xwindow*)); +E void FDECL(clear_map_window,(struct xwindow*)); +E void FDECL(map_input, (Widget, XEvent*, String*, Cardinal*)); +E void FDECL(set_map_size,(struct xwindow*, DIMENSION_P, DIMENSION_P)); +E void FDECL(create_map_window,(struct xwindow*, BOOLEAN_P, Widget)); +E void FDECL(destroy_map_window,(struct xwindow*)); +E int FDECL(x_event,(int)); + +/* ### winmenu.c ### */ +E void FDECL(menu_delete, (Widget, XEvent*, String*, Cardinal*)); +E void FDECL(menu_key,(Widget, XEvent*, String*, Cardinal*)); +E void FDECL(create_menu_window,(struct xwindow*)); +E void FDECL(destroy_menu_window,(struct xwindow*)); + +/* ### winmisc.c ### */ +E void FDECL(ps_key,(Widget, XEvent*, String*, Cardinal*)); /* player selection action */ +E void FDECL(race_key,(Widget, XEvent*, String*, Cardinal*)); /* race selection action */ +E void FDECL(gend_key, (Widget,XEvent *,String *,Cardinal *)); /* gender */ +E void FDECL(algn_key, (Widget,XEvent *,String *,Cardinal *)); /* alignment */ +E void FDECL(ec_delete, (Widget, XEvent*, String*, Cardinal*)); +E void FDECL(ec_key,(Widget, XEvent*, String*, Cardinal*)); /* extended command action */ + +/* ### winstatus.c ### */ +E void FDECL(create_status_window,(struct xwindow*, BOOLEAN_P, Widget)); +E void FDECL(destroy_status_window,(struct xwindow*)); +E void FDECL(adjust_status,(struct xwindow*, const char*)); +E void NDECL(null_out_status); +E void NDECL(check_turn_events); + +/* ### wintext.c ### */ +E void FDECL(delete_text, (Widget, XEvent*, String*, Cardinal*)); +E void FDECL(dismiss_text,(Widget, XEvent*, String*, Cardinal*)); +E void FDECL(key_dismiss_text,(Widget, XEvent*, String*, Cardinal*)); +#ifdef GRAPHIC_TOMBSTONE +E void FDECL(rip_dismiss_text,(Widget, XEvent*, String*, Cardinal*)); +#endif +E void FDECL(add_to_text_window,(struct xwindow*, int, const char*)); +E void FDECL(display_text_window,(struct xwindow*, BOOLEAN_P)); +E void FDECL(create_text_window,(struct xwindow*)); +E void FDECL(destroy_text_window,(struct xwindow*)); +E void FDECL(clear_text_window,(struct xwindow*)); +E void FDECL(append_text_buffer,(struct text_buffer*, const char*, BOOLEAN_P)); /* text buffer routines */ +E void FDECL(init_text_buffer,(struct text_buffer*)); +E void FDECL(clear_text_buffer,(struct text_buffer*)); +E void FDECL(free_text_buffer,(struct text_buffer*)); +#ifdef GRAPHIC_TOMBSTONE +E void FDECL(calculate_rip_text, (int)); +#endif + + +/* ### winval.c ### */ +E Widget FDECL(create_value,(Widget, const char*)); +E void FDECL(set_name,(Widget, char*)); +E void FDECL(set_name_width,(Widget, int)); +E int FDECL(get_name_width,(Widget)); +E void FDECL(set_value,(Widget, const char*)); +E void FDECL(set_value_width,(Widget, int)); +E int FDECL(get_value_width,(Widget)); +E void FDECL(hilight_value,(Widget)); +E void FDECL(swap_fg_bg,(Widget)); + +/* external declarations */ +E void FDECL(X11_init_nhwindows, (int *, char **)); +E void NDECL(X11_player_selection); +E void NDECL(X11_askname); +E void NDECL(X11_get_nh_event) ; +E void FDECL(X11_exit_nhwindows, (const char *)); +E void FDECL(X11_suspend_nhwindows, (const char *)); +E void NDECL(X11_resume_nhwindows); +E winid FDECL(X11_create_nhwindow, (int)); +E void FDECL(X11_clear_nhwindow, (winid)); +E void FDECL(X11_display_nhwindow, (winid, BOOLEAN_P)); +E void FDECL(X11_destroy_nhwindow, (winid)); +E void FDECL(X11_curs, (winid,int,int)); +E void FDECL(X11_putstr, (winid, int, const char *)); +E void FDECL(X11_display_file, (const char *, BOOLEAN_P)); +E void FDECL(X11_start_menu, (winid)); +E void FDECL(X11_add_menu, (winid,int,const ANY_P *, + CHAR_P, CHAR_P, int, const char *, BOOLEAN_P)); +E void FDECL(X11_end_menu, (winid, const char *)); +E int FDECL(X11_select_menu, (winid, int, MENU_ITEM_P **)); +E void NDECL(X11_update_inventory); +E void NDECL(X11_mark_synch); +E void NDECL(X11_wait_synch); +#ifdef CLIPPING +E void FDECL(X11_cliparound, (int, int)); +#endif +E void FDECL(X11_print_glyph, (winid,XCHAR_P,XCHAR_P,int)); +E void FDECL(X11_raw_print, (const char *)); +E void FDECL(X11_raw_print_bold, (const char *)); +E int NDECL(X11_nhgetch); +E int FDECL(X11_nh_poskey, (int *, int *, int *)); +E void NDECL(X11_nhbell); +E int NDECL(X11_doprev_message); +E char FDECL(X11_yn_function, (const char *, const char *, CHAR_P)); +E void FDECL(X11_getlin, (const char *,char *)); +E int NDECL(X11_get_ext_cmd); +E void FDECL(X11_number_pad, (int)); +E void NDECL(X11_delay_output); + +/* other defs that really should go away (they're tty specific) */ +E void NDECL(X11_start_screen); +E void NDECL(X11_end_screen); + +#ifdef GRAPHIC_TOMBSTONE +E void FDECL(X11_outrip, (winid,int)); +#else +E void FDECL(genl_outrip, (winid,int)); +#endif + +#endif /* WINX_H */ diff --git a/include/winami.h b/include/winami.h new file mode 100644 index 0000000..ea2c0ec --- /dev/null +++ b/include/winami.h @@ -0,0 +1,126 @@ +/* SCCS Id: @(#)winami.h 3.4 1993/01/17 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1991. */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1992, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINAMI_H +#define WINAMI_H + +#define MAXWINTAGS 5 + +/* + * Information specific to a menu window. First a structure for each + * menu entry, then the structure for each menu window. + */ +typedef struct amii_mi { + struct amii_mi *next; + anything identifier; /* Opaque type to identify this selection */ + long glyph; /* Glyph for menu item */ + long count; /* Object count */ + char selected; /* Been selected? */ + char selector; /* Char used to select this entry. */ + char gselector; /* Group selector */ + char canselect; /* Can user select this entry. */ + char attr; /* Attribute for the line. */ + char *str; /* The text of the item. */ +} amii_menu_item; + +struct amii_menu +{ + amii_menu_item *items; /* Starting pointer for item list. */ + amii_menu_item *last; /* End pointer for item list. */ + const char *query; /* Query string */ + int count; /* Number of strings. */ + char chr; /* Character to assign for accelerator */ +}; + +/* descriptor for Amiga Intuition-based windows. If we decide to cope with + * tty-style windows also, then things will need to change. */ +/* per-window data */ +struct amii_WinDesc { + xchar type; /* type of window */ + struct amii_menu menu; + boolean active; /* true if window is active */ + boolean wasup; /* true if menu/text window was already open */ + short disprows; /* Rows displayed so far (used for paging in message win) */ + xchar offx, offy; /* offset from topleft of display */ + short vwx, vwy, vcx, vcy; /* View cursor location */ + short rows, cols; /* dimensions */ + short curx, cury; /* current cursor position */ + short maxrow, maxcol; /* the maximum size used -- for INVEN wins */ + /* maxcol is also used by WIN_MESSAGE for */ + /* tracking the ^P command */ + char **data; /* window data [row][column] */ + menu_item *mi; /* Menu information */ + char *resp; /* valid menu responses (for NHW_INVEN) */ + char *canresp; /* cancel responses; 1st is the return value */ + char *morestr; /* string to display instead of default */ +/* amiga stuff */ + struct Window *win; /* Intuition window pointer */ +#ifdef INTUI_NEW_LOOK + struct ExtNewWindow *newwin; /* NewWindow alloc'd */ +#else + struct NewWindow *newwin; /* ExtNewWindow alloc'd */ +#endif +#ifdef INTUI_NEW_LOOK + struct TagItem wintags[ MAXWINTAGS ];/* Tag items for this window */ +#else + long wintags[ MAXWINTAGS*2 ]; +#endif + void *hook; /* Hook structure pointer for tiles version */ +#define FLMAP_INGLYPH 1 /* An NHW_MAP window is in glyph mode */ +#define FLMAP_CURSUP 2 /* An NHW_MAP window has the cursor displayed */ +#define FLMAP_SKIP 4 +#define FLMSG_FIRST 1 /* First message in the NHW_MESSAGE window for this turn */ + long wflags; + short cursx, cursy; /* Where the cursor is displayed at */ + short curs_apen, /* Color cursor is displayed in */ + curs_bpen; +}; + +/* descriptor for intuition-based displays -- all the per-display data */ +/* this is a generic thing - think of it as Screen level */ + +struct amii_DisplayDesc { +/* we need this for Screen size (which will vary with display mode) */ + uchar rows, cols; /* width & height of display in text units */ + short xpix, ypix; /* width and height of display in pixels */ + int toplin; /* flag for topl stuff */ + int rawprint; /* number of raw_printed lines since synch */ + winid lastwin; /* last window used for I/O */ +}; + +typedef enum { + WEUNK, WEKEY, WEMOUSE, WEMENU, +} WETYPE; + +typedef struct WEVENT +{ + WETYPE type; + union { + int key; + struct { + int x, y; + int qual; + } mouse; + long menucode; + } un; +} WEVENT; + +#define MAXWIN 20 /* maximum number of windows, cop-out */ + +/* port specific variable declarations */ +extern winid WIN_BASE; +extern winid WIN_OVER; +#define NHW_BASE 6 +#define NHW_OVER 7 /* overview window */ + + +extern struct amii_WinDesc *amii_wins[MAXWIN + 1]; + +extern struct amii_DisplayDesc *amiIDisplay; /* the Amiga Intuition descriptor */ + +extern char morc; /* last character typed to xwaitforspace */ +extern char defmorestr[]; /* default --more-- prompt */ + +#endif /* WINAMI_H */ diff --git a/include/wingem.h b/include/wingem.h new file mode 100644 index 0000000..cef5418 --- /dev/null +++ b/include/wingem.h @@ -0,0 +1,112 @@ +/* SCCS Id: @(#)wingem.h 3.4 1999/12/10 */ +/* Copyright (c) Christian Bressler, 1999 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINGEM_H +#define WINGEM_H + +#define E extern + +/* menu structure */ +typedef struct Gmi{ + struct Gmi *Gmi_next; + int Gmi_glyph; + long Gmi_identifier; + char Gmi_accelerator, Gmi_groupacc; + int Gmi_attr; + char *Gmi_str; + long Gmi_count; + int Gmi_selected; +} Gem_menu_item; + +#define MAXWIN 20 /* maximum number of windows, cop-out */ + +extern struct window_procs Gem_procs; + +/* ### wingem1.c ### */ +#ifdef CLIPPING +E void NDECL(setclipped); +#endif +E void FDECL(docorner, (int, int)); +E void NDECL(end_glyphout); +E void FDECL(g_putch, (int)); +E void NDECL(win_Gem_init); +E int NDECL(mar_gem_init); +E char NDECL(mar_ask_class); +E char * NDECL(mar_ask_name); +E int FDECL(mar_create_window, (int)); +E void FDECL(mar_destroy_nhwindow, (int)); +E void FDECL(mar_print_glyph, (int, int, int, int)); +E void FDECL(mar_print_line, (int, int, int, char *)); +E void FDECL(mar_set_message, (char *, char *, char *)); +E Gem_menu_item *NDECL(mar_hol_inv); +E void FDECL(mar_set_menu_type,(int)); +E void NDECL(mar_reverse_menu); +E void FDECL(mar_set_menu_title, (const char *)); +E void NDECL(mar_set_accelerators); +E void FDECL(mar_add_menu, (winid, Gem_menu_item *)); +E void FDECL(mar_change_menu_2_text, (winid)); +E void FDECL(mar_add_message, (const char *)); +E void NDECL(mar_status_dirty); +E int FDECL(mar_hol_win_type, (int)); +E void NDECL(mar_clear_messagewin); +E void FDECL(mar_set_no_glyph, (int)); +E void NDECL(mar_map_curs_weiter); + +/* external declarations */ +E void FDECL(Gem_init_nhwindows, (int *, char **)); +E void NDECL(Gem_player_selection); +E void NDECL(Gem_askname); +E void NDECL(Gem_get_nh_event) ; +E void FDECL(Gem_exit_nhwindows, (const char *)); +E void FDECL(Gem_suspend_nhwindows, (const char *)); +E void NDECL(Gem_resume_nhwindows); +E winid FDECL(Gem_create_nhwindow, (int)); +E void FDECL(Gem_clear_nhwindow, (winid)); +E void FDECL(Gem_display_nhwindow, (winid, BOOLEAN_P)); +E void FDECL(Gem_dismiss_nhwindow, (winid)); +E void FDECL(Gem_destroy_nhwindow, (winid)); +E void FDECL(Gem_curs, (winid,int,int)); +E void FDECL(Gem_putstr, (winid, int, const char *)); +E void FDECL(Gem_display_file, (const char *, BOOLEAN_P)); +E void FDECL(Gem_start_menu, (winid)); +E void FDECL(Gem_add_menu, (winid,int,const ANY_P *, + CHAR_P,CHAR_P,int,const char *, BOOLEAN_P)); +E void FDECL(Gem_end_menu, (winid, const char *)); +E int FDECL(Gem_select_menu, (winid, int, MENU_ITEM_P **)); +E char FDECL(Gem_message_menu, (CHAR_P,int,const char *)); +E void NDECL(Gem_update_inventory); +E void NDECL(Gem_mark_synch); +E void NDECL(Gem_wait_synch); +#ifdef CLIPPING +E void FDECL(Gem_cliparound, (int, int)); +#endif +#ifdef POSITIONBAR +E void FDECL(Gem_update_positionbar, (char *)); +#endif +E void FDECL(Gem_print_glyph, (winid,XCHAR_P,XCHAR_P,int)); +E void FDECL(Gem_raw_print, (const char *)); +E void FDECL(Gem_raw_print_bold, (const char *)); +E int NDECL(Gem_nhgetch); +E int FDECL(Gem_nh_poskey, (int *, int *, int *)); +E void NDECL(Gem_nhbell); +E int NDECL(Gem_doprev_message); +E char FDECL(Gem_yn_function, (const char *, const char *, CHAR_P)); +E void FDECL(Gem_getlin, (const char *,char *)); +E int NDECL(Gem_get_ext_cmd); +E void FDECL(Gem_number_pad, (int)); +E void NDECL(Gem_delay_output); +#ifdef CHANGE_COLOR +E void FDECL(Gem_change_color,(int color,long rgb,int reverse)); +E char * NDECL(Gem_get_color_string); +#endif + +/* other defs that really should go away (they're tty specific) */ +E void NDECL(Gem_start_screen); +E void NDECL(Gem_end_screen); + +E void FDECL(genl_outrip, (winid,int)); + +#undef E + +#endif /* WINGEM_H */ diff --git a/include/winprocs.h b/include/winprocs.h new file mode 100644 index 0000000..2e1f2e9 --- /dev/null +++ b/include/winprocs.h @@ -0,0 +1,220 @@ +/* SCCS Id: @(#)winprocs.h 3.4 2003/01/08 */ +/* Copyright (c) David Cohrs, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINPROCS_H +#define WINPROCS_H + +struct window_procs { + const char *name; + unsigned long wincap; /* window port capability options supported */ + unsigned long wincap2; /* additional window port capability options supported */ + void FDECL((*win_init_nhwindows), (int *, char **)); + void NDECL((*win_player_selection)); + void NDECL((*win_askname)); + void NDECL((*win_get_nh_event)) ; + void FDECL((*win_exit_nhwindows), (const char *)); + void FDECL((*win_suspend_nhwindows), (const char *)); + void NDECL((*win_resume_nhwindows)); + winid FDECL((*win_create_nhwindow), (int)); + void FDECL((*win_clear_nhwindow), (winid)); + void FDECL((*win_display_nhwindow), (winid, BOOLEAN_P)); + void FDECL((*win_destroy_nhwindow), (winid)); + void FDECL((*win_curs), (winid,int,int)); + void FDECL((*win_putstr), (winid, int, const char *)); + void FDECL((*win_display_file), (const char *, BOOLEAN_P)); + void FDECL((*win_start_menu), (winid)); + void FDECL((*win_add_menu), (winid,int,const ANY_P *, + CHAR_P,CHAR_P,int,const char *, BOOLEAN_P)); + void FDECL((*win_end_menu), (winid, const char *)); + int FDECL((*win_select_menu), (winid, int, MENU_ITEM_P **)); + char FDECL((*win_message_menu), (CHAR_P,int,const char *)); + void NDECL((*win_update_inventory)); + void NDECL((*win_mark_synch)); + void NDECL((*win_wait_synch)); +#ifdef CLIPPING + void FDECL((*win_cliparound), (int, int)); +#endif +#ifdef POSITIONBAR + void FDECL((*win_update_positionbar), (char *)); +#endif + void FDECL((*win_print_glyph), (winid,XCHAR_P,XCHAR_P,int)); + void FDECL((*win_raw_print), (const char *)); + void FDECL((*win_raw_print_bold), (const char *)); + int NDECL((*win_nhgetch)); + int FDECL((*win_nh_poskey), (int *, int *, int *)); + void NDECL((*win_nhbell)); + int NDECL((*win_doprev_message)); + char FDECL((*win_yn_function), (const char *, const char *, CHAR_P)); + void FDECL((*win_getlin), (const char *,char *)); + int NDECL((*win_get_ext_cmd)); + void FDECL((*win_number_pad), (int)); + void NDECL((*win_delay_output)); +#ifdef CHANGE_COLOR + void FDECL((*win_change_color), (int,long,int)); +#ifdef MAC + void FDECL((*win_change_background), (int)); + short FDECL((*win_set_font_name), (winid, char *)); +#endif + char * NDECL((*win_get_color_string)); +#endif + + /* other defs that really should go away (they're tty specific) */ + void NDECL((*win_start_screen)); + void NDECL((*win_end_screen)); + + void FDECL((*win_outrip), (winid,int)); + void FDECL((*win_preference_update), (const char *)); +}; + +extern NEARDATA struct window_procs windowprocs; + +/* + * If you wish to only support one window system and not use procedure + * pointers, add the appropriate #ifdef below. + */ + +#define init_nhwindows (*windowprocs.win_init_nhwindows) +#define player_selection (*windowprocs.win_player_selection) +#define askname (*windowprocs.win_askname) +#define get_nh_event (*windowprocs.win_get_nh_event) +#define exit_nhwindows (*windowprocs.win_exit_nhwindows) +#define suspend_nhwindows (*windowprocs.win_suspend_nhwindows) +#define resume_nhwindows (*windowprocs.win_resume_nhwindows) +#define create_nhwindow (*windowprocs.win_create_nhwindow) +#define clear_nhwindow (*windowprocs.win_clear_nhwindow) +#define display_nhwindow (*windowprocs.win_display_nhwindow) +#define destroy_nhwindow (*windowprocs.win_destroy_nhwindow) +#define curs (*windowprocs.win_curs) +#define putstr (*windowprocs.win_putstr) +#define display_file (*windowprocs.win_display_file) +#define start_menu (*windowprocs.win_start_menu) +#define add_menu (*windowprocs.win_add_menu) +#define end_menu (*windowprocs.win_end_menu) +#define select_menu (*windowprocs.win_select_menu) +#define message_menu (*windowprocs.win_message_menu) +#define update_inventory (*windowprocs.win_update_inventory) +#define mark_synch (*windowprocs.win_mark_synch) +#define wait_synch (*windowprocs.win_wait_synch) +#ifdef CLIPPING +#define cliparound (*windowprocs.win_cliparound) +#endif +#ifdef POSITIONBAR +#define update_positionbar (*windowprocs.win_update_positionbar) +#endif +#define print_glyph (*windowprocs.win_print_glyph) +#define raw_print (*windowprocs.win_raw_print) +#define raw_print_bold (*windowprocs.win_raw_print_bold) +#define nhgetch (*windowprocs.win_nhgetch) +#define nh_poskey (*windowprocs.win_nh_poskey) +#define nhbell (*windowprocs.win_nhbell) +#define nh_doprev_message (*windowprocs.win_doprev_message) +#define getlin (*windowprocs.win_getlin) +#define get_ext_cmd (*windowprocs.win_get_ext_cmd) +#define number_pad (*windowprocs.win_number_pad) +#define delay_output (*windowprocs.win_delay_output) +#ifdef CHANGE_COLOR +#define change_color (*windowprocs.win_change_color) +#ifdef MAC +#define change_background (*windowprocs.win_change_background) +#define set_font_name (*windowprocs.win_set_font_name) +#endif +#define get_color_string (*windowprocs.win_get_color_string) +#endif + +/* 3.4.2: There is a real yn_function() in the core now, which does + * some buffer length validation on the parameters prior to + * invoking the window port routine. yn_function() is in cmd.c + */ +/* #define yn_function (*windowprocs.win_yn_function) */ + +/* other defs that really should go away (they're tty specific) */ +#define start_screen (*windowprocs.win_start_screen) +#define end_screen (*windowprocs.win_end_screen) + +#define outrip (*windowprocs.win_outrip) +#define preference_update (*windowprocs.win_preference_update) + +/* + * WINCAP + * Window port preference capability bits. + * Some day this might be better in its own wincap.h file. + */ +#define WC_COLOR 0x01L /* 01 Port can display things in color */ +#define WC_HILITE_PET 0x02L /* 02 supports hilite pet */ +#define WC_ASCII_MAP 0x04L /* 03 supports an ascii map */ +#define WC_TILED_MAP 0x08L /* 04 supports a tiled map */ +#define WC_PRELOAD_TILES 0x10L /* 05 supports pre-loading tiles */ +#define WC_TILE_WIDTH 0x20L /* 06 prefer this width of tile */ +#define WC_TILE_HEIGHT 0x40L /* 07 prefer this height of tile */ +#define WC_TILE_FILE 0x80L /* 08 alternative tile file name */ +#define WC_INVERSE 0x100L /* 09 Port supports inverse video */ +#define WC_ALIGN_MESSAGE 0x200L /* 10 supports message alignmt top|b|l|r */ +#define WC_ALIGN_STATUS 0x400L /* 11 supports status alignmt top|b|l|r */ +#define WC_VARY_MSGCOUNT 0x800L /* 12 supports varying message window */ +#define WC_FONT_MAP 0x1000L /* 13 supports specification of map win font */ +#define WC_FONT_MESSAGE 0x2000L /* 14 supports specification of msg win font */ +#define WC_FONT_STATUS 0x4000L /* 15 supports specification of sts win font */ +#define WC_FONT_MENU 0x8000L /* 16 supports specification of mnu win font */ +#define WC_FONT_TEXT 0x10000L /* 17 supports specification of txt win font */ +#define WC_FONTSIZ_MAP 0x20000L /* 18 supports specification of map win font */ +#define WC_FONTSIZ_MESSAGE 0x40000L /* 19 supports specification of msg win font */ +#define WC_FONTSIZ_STATUS 0x80000L /* 20 supports specification of sts win font */ +#define WC_FONTSIZ_MENU 0x100000L /* 21 supports specification of mnu win font */ +#define WC_FONTSIZ_TEXT 0x200000L /* 22 supports specification of txt win font */ +#define WC_SCROLL_MARGIN 0x400000L /* 23 supports setting scroll margin for map */ +#define WC_SPLASH_SCREEN 0x800000L /* 24 supports display of splash screen */ +#define WC_POPUP_DIALOG 0x1000000L /* 25 supports queries in pop dialogs */ +#define WC_SCROLL_AMOUNT 0x2000000L /* 26 scroll this amount at scroll margin */ +#define WC_EIGHT_BIT_IN 0x4000000L /* 27 8-bit character input */ +#define WC_PERM_INVENT 0x8000000L /* 28 8-bit character input */ +#define WC_MAP_MODE 0x10000000L /* 29 map_mode option */ +#define WC_WINDOWCOLORS 0x20000000L /* 30 background color for message window */ +#define WC_PLAYER_SELECTION 0x40000000L /* 31 background color for message window */ +#define WC_MOUSE_SUPPORT 0x80000000L /* 32 mouse support */ + /* no free bits */ + +#define WC2_FULLSCREEN 0x01L /* 01 display full screen */ +#define WC2_SOFTKEYBOARD 0x02L /* 02 software keyboard */ +#define WC2_WRAPTEXT 0x04L /* 04 wrap long lines of text */ + /* 29 free bits */ + +#define ALIGN_LEFT 1 +#define ALIGN_RIGHT 2 +#define ALIGN_TOP 3 +#define ALIGN_BOTTOM 4 + +/* player_selection */ +#define VIA_DIALOG 0 +#define VIA_PROMPTS 1 + +/* map_mode settings - deprecated */ +#define MAP_MODE_TILES 0 +#define MAP_MODE_ASCII4x6 1 +#define MAP_MODE_ASCII6x8 2 +#define MAP_MODE_ASCII8x8 3 +#define MAP_MODE_ASCII16x8 4 +#define MAP_MODE_ASCII7x12 5 +#define MAP_MODE_ASCII8x12 6 +#define MAP_MODE_ASCII16x12 7 +#define MAP_MODE_ASCII12x16 8 +#define MAP_MODE_ASCII10x18 9 +#define MAP_MODE_ASCII_FIT_TO_SCREEN 10 +#define MAP_MODE_TILES_FIT_TO_SCREEN 11 + +#if 0 +#define WC_SND_SOUND 0x01L /* 01 Port has some sound capabilities */ +#define WC_SND_SPEAKER 0x02L /* 02 Sound supported via built-in speaker */ +#define WC_SND_STEREO 0x04L /* 03 Stereo sound supported */ +#define WC_SND_RAW 0x08L /* 04 Raw sound supported */ +#define WC_SND_WAVE 0x10L /* 05 Wave support */ +#define WC_SND_MIDI 0x20L /* 06 Midi support */ + /* 26 free bits */ +#endif + +struct wc_Opt { + const char *wc_name; + unsigned long wc_bit; +}; + +#endif /* WINPROCS_H */ diff --git a/include/wintty.h b/include/wintty.h new file mode 100644 index 0000000..fc123d8 --- /dev/null +++ b/include/wintty.h @@ -0,0 +1,255 @@ +/* SCCS Id: @(#)wintty.h 3.4 1996/02/18 */ +/* Copyright (c) David Cohrs, 1991,1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINTTY_H +#define WINTTY_H + +#define E extern + +#ifndef WINDOW_STRUCTS +#define WINDOW_STRUCTS + +/* menu structure */ +typedef struct tty_mi { + struct tty_mi *next; + anything identifier; /* user identifier */ + long count; /* user count */ + char *str; /* description string (including accelerator) */ + int attr; /* string attribute */ + boolean selected; /* TRUE if selected by user */ + char selector; /* keyboard accelerator */ + char gselector; /* group accelerator */ +} tty_menu_item; + +/* descriptor for tty-based windows */ +struct WinDesc { + int flags; /* window flags */ + xchar type; /* type of window */ + boolean active; /* true if window is active */ + uchar offx, offy; /* offset from topleft of display */ + short rows, cols; /* dimensions */ + short curx, cury; /* current cursor position */ + short maxrow, maxcol; /* the maximum size used -- for MENU wins */ + /* maxcol is also used by WIN_MESSAGE for */ + /* tracking the ^P command */ + short *datlen; /* allocation size for *data */ + char **data; /* window data [row][column] */ + char *morestr; /* string to display instead of default */ + tty_menu_item *mlist; /* menu information (MENU) */ + tty_menu_item **plist; /* menu page pointers (MENU) */ + short plist_size; /* size of allocated plist (MENU) */ + short npages; /* number of pages in menu (MENU) */ + short nitems; /* total number of items (MENU) */ + short how; /* menu mode - pick 1 or N (MENU) */ + char menu_ch; /* menu char (MENU) */ +}; + +/* window flags */ +#define WIN_CANCELLED 1 +#define WIN_STOP 1 /* for NHW_MESSAGE; stops output */ + +/* descriptor for tty-based displays -- all the per-display data */ +struct DisplayDesc { + uchar rows, cols; /* width and height of tty display */ + uchar curx, cury; /* current cursor position on the screen */ +#ifdef TEXTCOLOR + int color; /* current color */ +#endif + int attrs; /* attributes in effect */ + int toplin; /* flag for topl stuff */ + int rawprint; /* number of raw_printed lines since synch */ + int inmore; /* non-zero if more() is active */ + int inread; /* non-zero if reading a character */ + int intr; /* non-zero if inread was interrupted */ + winid lastwin; /* last window used for I/O */ + char dismiss_more; /* extra character accepted at --More-- */ +}; + +#endif /* WINDOW_STRUCTS */ + +#define MAXWIN 20 /* maximum number of windows, cop-out */ + +/* tty dependent window types */ +#ifdef NHW_BASE +#undef NHW_BASE +#endif +#define NHW_BASE 6 + +extern struct window_procs tty_procs; + +/* port specific variable declarations */ +extern winid BASE_WINDOW; + +extern struct WinDesc *wins[MAXWIN]; + +extern struct DisplayDesc *ttyDisplay; /* the tty display descriptor */ + +extern char morc; /* last character typed to xwaitforspace */ +extern char defmorestr[]; /* default --more-- prompt */ + +/* port specific external function references */ + +/* ### getline.c ### */ +E void FDECL(xwaitforspace, (const char *)); + +/* ### termcap.c, video.c ### */ + +E void FDECL(tty_startup,(int*, int*)); +#ifndef NO_TERMS +E void NDECL(tty_shutdown); +#endif +#if defined(apollo) +/* Apollos don't widen old-style function definitions properly -- they try to + * be smart and use the prototype, or some such strangeness. So we have to + * define UNWIDENDED_PROTOTYPES (in tradstdc.h), which makes CHAR_P below a + * char. But the tputs termcap call was compiled as if xputc's argument + * actually would be expanded. So here, we have to make an exception. */ +E void FDECL(xputc, (int)); +#else +E void FDECL(xputc, (CHAR_P)); +#endif +E void FDECL(xputs, (const char *)); +#if defined(SCREEN_VGA) || defined(SCREEN_8514) +E void FDECL(xputg, (int, int, unsigned)); +#endif +E void NDECL(cl_end); +E void NDECL(clear_screen); +E void NDECL(home); +E void NDECL(standoutbeg); +E void NDECL(standoutend); +# if 0 +E void NDECL(revbeg); +E void NDECL(boldbeg); +E void NDECL(blinkbeg); +E void NDECL(dimbeg); +E void NDECL(m_end); +# endif +E void NDECL(backsp); +E void NDECL(graph_on); +E void NDECL(graph_off); +E void NDECL(cl_eos); + +/* + * termcap.c (or facsimiles in other ports) is the right place for doing + * strange and arcane things such as outputting escape sequences to select + * a color or whatever. wintty.c should concern itself with WHERE to put + * stuff in a window. + */ +E void FDECL(term_start_attr,(int attr)); +E void FDECL(term_end_attr,(int attr)); +E void NDECL(term_start_raw_bold); +E void NDECL(term_end_raw_bold); + +#ifdef TEXTCOLOR +E void NDECL(term_end_color); +E void FDECL(term_start_color,(int color)); +E int FDECL(has_color,(int color)); +#endif /* TEXTCOLOR */ + + +/* ### topl.c ### */ + +E void FDECL(addtopl, (const char *)); +E void NDECL(more); +E void FDECL(update_topl, (const char *)); +E void FDECL(putsyms, (const char*)); + +/* ### wintty.c ### */ +#ifdef CLIPPING +E void NDECL(setclipped); +#endif +E void FDECL(docorner, (int, int)); +E void NDECL(end_glyphout); +E void FDECL(g_putch, (int)); +E void NDECL(win_tty_init); + +/* external declarations */ +E void FDECL(tty_init_nhwindows, (int *, char **)); +E void NDECL(tty_player_selection); +E void NDECL(tty_askname); +E void NDECL(tty_get_nh_event) ; +E void FDECL(tty_exit_nhwindows, (const char *)); +E void FDECL(tty_suspend_nhwindows, (const char *)); +E void NDECL(tty_resume_nhwindows); +E winid FDECL(tty_create_nhwindow, (int)); +E void FDECL(tty_clear_nhwindow, (winid)); +E void FDECL(tty_display_nhwindow, (winid, BOOLEAN_P)); +E void FDECL(tty_dismiss_nhwindow, (winid)); +E void FDECL(tty_destroy_nhwindow, (winid)); +E void FDECL(tty_curs, (winid,int,int)); +E void FDECL(tty_putstr, (winid, int, const char *)); +E void FDECL(tty_display_file, (const char *, BOOLEAN_P)); +E void FDECL(tty_start_menu, (winid)); +E void FDECL(tty_add_menu, (winid,int,const ANY_P *, + CHAR_P,CHAR_P,int,const char *, BOOLEAN_P)); +E void FDECL(tty_end_menu, (winid, const char *)); +E int FDECL(tty_select_menu, (winid, int, MENU_ITEM_P **)); +E char FDECL(tty_message_menu, (CHAR_P,int,const char *)); +E void NDECL(tty_update_inventory); +E void NDECL(tty_mark_synch); +E void NDECL(tty_wait_synch); +#ifdef CLIPPING +E void FDECL(tty_cliparound, (int, int)); +#endif +#ifdef POSITIONBAR +E void FDECL(tty_update_positionbar, (char *)); +#endif +E void FDECL(tty_print_glyph, (winid,XCHAR_P,XCHAR_P,int)); +E void FDECL(tty_raw_print, (const char *)); +E void FDECL(tty_raw_print_bold, (const char *)); +E int NDECL(tty_nhgetch); +E int FDECL(tty_nh_poskey, (int *, int *, int *)); +E void NDECL(tty_nhbell); +E int NDECL(tty_doprev_message); +E char FDECL(tty_yn_function, (const char *, const char *, CHAR_P)); +E void FDECL(tty_getlin, (const char *,char *)); +E int NDECL(tty_get_ext_cmd); +E void FDECL(tty_number_pad, (int)); +E void NDECL(tty_delay_output); +#ifdef CHANGE_COLOR +E void FDECL(tty_change_color,(int color,long rgb,int reverse)); +#ifdef MAC +E void FDECL(tty_change_background,(int white_or_black)); +E short FDECL(set_tty_font_name, (winid, char *)); +#endif +E char * NDECL(tty_get_color_string); +#endif + +/* other defs that really should go away (they're tty specific) */ +E void NDECL(tty_start_screen); +E void NDECL(tty_end_screen); + +E void FDECL(genl_outrip, (winid,int)); + +#ifdef NO_TERMS +# ifdef MAC +# ifdef putchar +# undef putchar +# undef putc +# endif +# define putchar term_putc +# define fflush term_flush +# define puts term_puts +E int FDECL(term_putc, (int c)); +E int FDECL(term_flush, (void *desc)); +E int FDECL(term_puts, (const char *str)); +# endif /* MAC */ +# if defined(MSDOS) || defined(WIN32CON) +# if defined(SCREEN_BIOS) || defined(SCREEN_DJGPPFAST) || defined(WIN32CON) +# undef putchar +# undef putc +# undef puts +# define putchar(x) xputc(x) /* these are in video.c, nttty.c */ +# define putc(x) xputc(x) +# define puts(x) xputs(x) +# endif/*SCREEN_BIOS || SCREEN_DJGPPFAST || WIN32CON */ +# ifdef POSITIONBAR +E void FDECL(video_update_positionbar, (char *)); +# endif +# endif/*MSDOS*/ +#endif/*NO_TERMS*/ + +#undef E + +#endif /* WINTTY_H */ diff --git a/include/wintype.h b/include/wintype.h new file mode 100644 index 0000000..b7a4aa1 --- /dev/null +++ b/include/wintype.h @@ -0,0 +1,71 @@ +/* SCCS Id: @(#)wintype.h 3.4 1996/02/18 */ +/* Copyright (c) David Cohrs, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINTYPE_H +#define WINTYPE_H + +typedef int winid; /* a window identifier */ + +/* generic parameter - must not be any larger than a pointer */ +typedef union any { + genericptr_t a_void; + struct obj *a_obj; + int a_int; + char a_char; + schar a_schar; + /* add types as needed */ +} anything; +#define ANY_P union any /* avoid typedef in prototypes */ + /* (buggy old Ultrix compiler) */ + +/* menu return list */ +typedef struct mi { + anything item; /* identifier */ + long count; /* count */ +} menu_item; +#define MENU_ITEM_P struct mi + +/* select_menu() "how" argument types */ +#define PICK_NONE 0 /* user picks nothing (display only) */ +#define PICK_ONE 1 /* only pick one */ +#define PICK_ANY 2 /* can pick any amount */ + +/* window types */ +/* any additional port specific types should be defined in win*.h */ +#define NHW_MESSAGE 1 +#define NHW_STATUS 2 +#define NHW_MAP 3 +#define NHW_MENU 4 +#define NHW_TEXT 5 + +/* attribute types for putstr; the same as the ANSI value, for convenience */ +#define ATR_NONE 0 +#define ATR_BOLD 1 +#define ATR_DIM 2 +#define ATR_ULINE 4 +#define ATR_BLINK 5 +#define ATR_INVERSE 7 + +/* nh_poskey() modifier types */ +#define CLICK_1 1 +#define CLICK_2 2 + +/* invalid winid */ +#define WIN_ERR ((winid) -1) + +/* menu window keyboard commands (may be mapped) */ +#define MENU_FIRST_PAGE '^' +#define MENU_LAST_PAGE '|' +#define MENU_NEXT_PAGE '>' +#define MENU_PREVIOUS_PAGE '<' +#define MENU_SELECT_ALL '.' +#define MENU_UNSELECT_ALL '-' +#define MENU_INVERT_ALL '@' +#define MENU_SELECT_PAGE ',' +#define MENU_UNSELECT_PAGE '\\' +#define MENU_INVERT_PAGE '~' +#define MENU_SEARCH ':' + + +#endif /* WINTYPE_H */ diff --git a/include/xwindow.h b/include/xwindow.h new file mode 100644 index 0000000..94c7d3a --- /dev/null +++ b/include/xwindow.h @@ -0,0 +1,95 @@ +/* SCCS Id: @(#)xwindow.h 3.4 1992/03/07 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef _xwindow_h +#define _xwindow_h + +/**************************************************************** + * + * Window widget + * + ****************************************************************/ + +/* Resources: + + Name Class RepType Default Value + ---- ----- ------- ------------- + background Background Pixel XtDefaultBackground + border BorderColor Pixel XtDefaultForeground + borderWidth BorderWidth Dimension 1 + destroyCallback Callback Pointer NULL + height Height Dimension 0 + mappedWhenManaged MappedWhenManaged Boolean True + sensitive Sensitive Boolean True + width Width Dimension 0 + x Position Position 0 + y Position Position 0 + + rows Width Dimension 21 + columns Height Dimension 80 + foreground Color Pixel XtDefaultForeground + + black Color Pixel "black" + red Color Pixel "red" + green Color Pixel "pale green" + brown Color Pixel "brown" + blue Color Pixel "blue" + magenta Color Pixel "magenta" + cyan Color Pixel "light cyan" + gray Color Pixel "gray" + //no color// + orange Color Pixel "orange" + bright_green Color Pixel "green" + yellow Color Pixel "yellow" + bright_blue Color Pixel "royal blue" + bright_magenta Color Pixel "violet" + bright_cyan Color Pixel "cyan" + white Color Pixel "white" + + font Font XFontStruct* XtDefaultFont + exposeCallback Callback Callback NULL + callback Callback Callback NULL + resizeCallback Callback Callback NULL +*/ + +/* define any special resource names here that are not in */ + +#define XtNrows "rows" +#define XtNcolumns "columns" +#define XtNblack "black" +#define XtNred "red" +#define XtNgreen "green" +#define XtNbrown "brown" +#define XtNblue "blue" +#define XtNmagenta "magenta" +#define XtNcyan "cyan" +#define XtNgray "gray" +#define XtNorange "orange" +#define XtNbright_green "bright_green" +#define XtNyellow "yellow" +#define XtNbright_blue "bright_blue" +#define XtNbright_magenta "bright_magenta" +#define XtNbright_cyan "bright_cyan" +#define XtNwhite "white" +#define XtNexposeCallback "exposeCallback" +#define XtNresizeCallback "resizeCallback" + + +extern XFontStruct *WindowFontStruct(/* Widget */); +extern Font WindowFont(/* Widget */); + +#define XtCWindowResource "WindowResource" +#define XtCRows "Rows" +#define XtCColumns "Columns" + +/* declare specific WindowWidget class and instance datatypes */ + +typedef struct _WindowClassRec *WindowWidgetClass; +typedef struct _WindowRec *WindowWidget; + +/* declare the class constant */ + +extern WidgetClass windowWidgetClass; + +#endif /* _xwindow_h */ diff --git a/include/xwindowp.h b/include/xwindowp.h new file mode 100644 index 0000000..bbbe378 --- /dev/null +++ b/include/xwindowp.h @@ -0,0 +1,72 @@ +/* SCCS Id: @(#)xwindowp.h 3.4 1992/03/07 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef _xwindowp_h +#define _xwindowp_h + +#include "xwindow.h" + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +/* include superclass private header file */ +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +/* define unique representation types not found in */ + +#define XtRWindowResource "WindowResource" + +typedef struct { + int empty; +} WindowClassPart; + +typedef struct _WindowClassRec { + CoreClassPart core_class; + WindowClassPart window_class; +} WindowClassRec; + +extern WindowClassRec windowClassRec; + +typedef struct { + /* resources */ + Dimension rows; + Dimension columns; + Pixel foreground; + Pixel black; + Pixel red; + Pixel green; + Pixel brown; + Pixel blue; + Pixel magenta; + Pixel cyan; + Pixel gray; + Pixel orange; + Pixel bright_green; + Pixel yellow; + Pixel bright_blue; + Pixel bright_magenta; + Pixel bright_cyan; + Pixel white; + XFontStruct *font; + XtCallbackList expose_callback; + XtCallbackList input_callback; + XtCallbackList resize_callback; + /* private state */ + /* (none) */ +} WindowPart; + +typedef struct _WindowRec { + CorePart core; + WindowPart window; +} WindowRec; + +#endif /* _xwindowp_h */ diff --git a/include/you.h b/include/you.h new file mode 100644 index 0000000..2ca496d --- /dev/null +++ b/include/you.h @@ -0,0 +1,368 @@ +/* SCCS Id: @(#)you.h 3.4 2000/05/21 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef YOU_H +#define YOU_H + +#include "attrib.h" +#include "monst.h" +#ifndef PROP_H +#include "prop.h" /* (needed here for util/makedefs.c) */ +#endif +#include "skills.h" + +/*** Substructures ***/ + +struct RoleName { + const char *m; /* name when character is male */ + const char *f; /* when female; null if same as male */ +}; + +struct RoleAdvance { + /* "fix" is the fixed amount, "rnd" is the random amount */ + xchar infix, inrnd; /* at character initialization */ + xchar lofix, lornd; /* gained per level < urole.xlev */ + xchar hifix, hirnd; /* gained per level >= urole.xlev */ +}; + +struct u_have { + Bitfield(amulet,1); /* carrying Amulet */ + Bitfield(bell,1); /* carrying Bell */ + Bitfield(book,1); /* carrying Book */ + Bitfield(menorah,1); /* carrying Candelabrum */ + Bitfield(questart,1); /* carrying the Quest Artifact */ + Bitfield(unused,3); +}; + +struct u_event { + Bitfield(minor_oracle,1); /* received at least 1 cheap oracle */ + Bitfield(major_oracle,1); /* " expensive oracle */ + Bitfield(qcalled,1); /* called by Quest leader to do task */ + Bitfield(qexpelled,1); /* expelled from the Quest dungeon */ + Bitfield(qcompleted,1); /* successfully completed Quest task */ + Bitfield(uheard_tune,2); /* 1=know about, 2=heard passtune */ + Bitfield(uopened_dbridge,1); /* opened the drawbridge */ + + Bitfield(invoked,1); /* invoked Gate to the Sanctum level */ + Bitfield(gehennom_entered,1); /* entered Gehennom via Valley */ +#ifdef ELBERETH + Bitfield(uhand_of_elbereth,2); /* became Hand of Elbereth */ +#endif + Bitfield(udemigod,1); /* killed the wiz */ + Bitfield(ascended,1); /* has offered the Amulet */ +}; + +/* KMH, conduct -- + * These are voluntary challenges. Each field denotes the number of + * times a challenge has been violated. + */ +struct u_conduct { /* number of times... */ + long unvegetarian; /* eaten any animal */ + long unvegan; /* ... or any animal byproduct */ + long food; /* ... or any comestible */ + long gnostic; /* used prayer, priest, or altar */ + long weaphit; /* hit a monster with a weapon */ + long killer; /* killed a monster yourself */ + long literate; /* read something (other than BotD) */ + long polypiles; /* polymorphed an object */ + long polyselfs; /* transformed yourself */ + long wishes; /* used a wish */ + long wisharti; /* wished for an artifact */ + /* genocides already listed at end of game */ +}; + +/*** Unified structure containing role information ***/ +struct Role { + /*** Strings that name various things ***/ + struct RoleName name; /* the role's name (from u_init.c) */ + struct RoleName rank[9]; /* names for experience levels (from botl.c) */ + const char *lgod, *ngod, *cgod; /* god names (from pray.c) */ + const char *filecode; /* abbreviation for use in file names */ + const char *homebase; /* quest leader's location (from questpgr.c) */ + const char *intermed; /* quest intermediate goal (from questpgr.c) */ + + /*** Indices of important monsters and objects ***/ + short malenum, /* index (PM_) as a male (botl.c) */ + femalenum, /* ...or as a female (NON_PM == same) */ + petnum, /* PM_ of preferred pet (NON_PM == random) */ + ldrnum, /* PM_ of quest leader (questpgr.c) */ + guardnum, /* PM_ of quest guardians (questpgr.c) */ + neminum, /* PM_ of quest nemesis (questpgr.c) */ + enemy1num, /* specific quest enemies (NON_PM == random) */ + enemy2num; + char enemy1sym, /* quest enemies by class (S_) */ + enemy2sym; + short questarti; /* index (ART_) of quest artifact (questpgr.c) */ + + /*** Bitmasks ***/ + short allow; /* bit mask of allowed variations */ +#define ROLE_RACEMASK 0x0ff8 /* allowable races */ +#define ROLE_GENDMASK 0xf000 /* allowable genders */ +#define ROLE_MALE 0x1000 +#define ROLE_FEMALE 0x2000 +#define ROLE_NEUTER 0x4000 +#define ROLE_ALIGNMASK AM_MASK /* allowable alignments */ +#define ROLE_LAWFUL AM_LAWFUL +#define ROLE_NEUTRAL AM_NEUTRAL +#define ROLE_CHAOTIC AM_CHAOTIC + + /*** Attributes (from attrib.c and exper.c) ***/ + xchar attrbase[A_MAX]; /* lowest initial attributes */ + xchar attrdist[A_MAX]; /* distribution of initial attributes */ + struct RoleAdvance hpadv; /* hit point advancement */ + struct RoleAdvance enadv; /* energy advancement */ + xchar xlev; /* cutoff experience level */ + xchar initrecord; /* initial alignment record */ + + /*** Spell statistics (from spell.c) ***/ + int spelbase; /* base spellcasting penalty */ + int spelheal; /* penalty (-bonus) for healing spells */ + int spelshld; /* penalty for wearing any shield */ + int spelarmr; /* penalty for wearing metal armour */ + int spelstat; /* which stat (A_) is used */ + int spelspec; /* spell (SPE_) the class excels at */ + int spelsbon; /* penalty (-bonus) for that spell */ + + /*** Properties in variable-length arrays ***/ + /* intrinsics (see attrib.c) */ + /* initial inventory (see u_init.c) */ + /* skills (see u_init.c) */ + + /*** Don't forget to add... ***/ + /* quest leader, guardians, nemesis (monst.c) */ + /* quest artifact (artilist.h) */ + /* quest dungeon definition (dat/Xyz.dat) */ + /* quest text (dat/quest.txt) */ + /* dictionary entries (dat/data.bas) */ +}; + +extern const struct Role roles[]; /* table of available roles */ +extern struct Role urole; +#define Role_if(X) (urole.malenum == (X)) +#define Role_switch (urole.malenum) + +/* used during initialization for race, gender, and alignment + as well as for character class */ +#define ROLE_NONE (-1) +#define ROLE_RANDOM (-2) + +/*** Unified structure specifying race information ***/ + +struct Race { + /*** Strings that name various things ***/ + const char *noun; /* noun ("human", "elf") */ + const char *adj; /* adjective ("human", "elven") */ + const char *coll; /* collective ("humanity", "elvenkind") */ + const char *filecode; /* code for filenames */ + struct RoleName individual; /* individual as a noun ("man", "elf") */ + + /*** Indices of important monsters and objects ***/ + short malenum, /* PM_ as a male monster */ + femalenum, /* ...or as a female (NON_PM == same) */ + mummynum, /* PM_ as a mummy */ + zombienum; /* PM_ as a zombie */ + + /*** Bitmasks ***/ + short allow; /* bit mask of allowed variations */ + short selfmask, /* your own race's bit mask */ + lovemask, /* bit mask of always peaceful */ + hatemask; /* bit mask of always hostile */ + + /*** Attributes ***/ + xchar attrmin[A_MAX]; /* minimum allowable attribute */ + xchar attrmax[A_MAX]; /* maximum allowable attribute */ + struct RoleAdvance hpadv; /* hit point advancement */ + struct RoleAdvance enadv; /* energy advancement */ +#if 0 /* DEFERRED */ + int nv_range; /* night vision range */ + int xray_range; /* X-ray vision range */ +#endif + + /*** Properties in variable-length arrays ***/ + /* intrinsics (see attrib.c) */ + + /*** Don't forget to add... ***/ + /* quest leader, guardians, nemesis (monst.c) */ + /* quest dungeon definition (dat/Xyz.dat) */ + /* quest text (dat/quest.txt) */ + /* dictionary entries (dat/data.bas) */ +}; + +extern const struct Race races[]; /* Table of available races */ +extern struct Race urace; +#define Race_if(X) (urace.malenum == (X)) +#define Race_switch (urace.malenum) + +/*** Unified structure specifying gender information ***/ +struct Gender { + const char *adj; /* male/female/neuter */ + const char *he; /* he/she/it */ + const char *him; /* him/her/it */ + const char *his; /* his/her/its */ + const char *filecode; /* file code */ + short allow; /* equivalent ROLE_ mask */ +}; +#define ROLE_GENDERS 2 /* number of permitted player genders */ + /* increment to 3 if you allow neuter roles */ + +extern const struct Gender genders[]; /* table of available genders */ +#define uhe() (genders[flags.female ? 1 : 0].he) +#define uhim() (genders[flags.female ? 1 : 0].him) +#define uhis() (genders[flags.female ? 1 : 0].his) +#define mhe(mtmp) (genders[pronoun_gender(mtmp)].he) +#define mhim(mtmp) (genders[pronoun_gender(mtmp)].him) +#define mhis(mtmp) (genders[pronoun_gender(mtmp)].his) + + +/*** Unified structure specifying alignment information ***/ +struct Align { + const char *noun; /* law/balance/chaos */ + const char *adj; /* lawful/neutral/chaotic */ + const char *filecode; /* file code */ + short allow; /* equivalent ROLE_ mask */ + aligntyp value; /* equivalent A_ value */ +}; +#define ROLE_ALIGNS 3 /* number of permitted player alignments */ + +extern const struct Align aligns[]; /* table of available alignments */ + + +/*** Information about the player ***/ +struct you { + xchar ux, uy; + schar dx, dy, dz; /* direction of move (or zap or ... ) */ + schar di; /* direction of FF */ + xchar tx, ty; /* destination of travel */ + xchar ux0, uy0; /* initial position FF */ + d_level uz, uz0; /* your level on this and the previous turn */ + d_level utolev; /* level monster teleported you to, or uz */ + uchar utotype; /* bitmask of goto_level() flags for utolev */ + boolean umoved; /* changed map location (post-move) */ + int last_str_turn; /* 0: none, 1: half turn, 2: full turn */ + /* +: turn right, -: turn left */ + int ulevel; /* 1 to MAXULEV */ + int ulevelmax; + unsigned utrap; /* trap timeout */ + unsigned utraptype; /* defined if utrap nonzero */ +#define TT_BEARTRAP 0 +#define TT_PIT 1 +#define TT_WEB 2 +#define TT_LAVA 3 +#define TT_INFLOOR 4 + char urooms[5]; /* rooms (roomno + 3) occupied now */ + char urooms0[5]; /* ditto, for previous position */ + char uentered[5]; /* rooms (roomno + 3) entered this turn */ + char ushops[5]; /* shop rooms (roomno + 3) occupied now */ + char ushops0[5]; /* ditto, for previous position */ + char ushops_entered[5]; /* ditto, shops entered this turn */ + char ushops_left[5]; /* ditto, shops exited this turn */ + + int uhunger; /* refd only in eat.c and shk.c */ + unsigned uhs; /* hunger state - see eat.c */ + + struct prop uprops[LAST_PROP+1]; + + unsigned umconf; + char usick_cause[PL_PSIZ+20]; /* sizeof "unicorn horn named "+1 */ + Bitfield(usick_type,2); +#define SICK_VOMITABLE 0x01 +#define SICK_NONVOMITABLE 0x02 +#define SICK_ALL 0x03 + + /* These ranges can never be more than MAX_RANGE (vision.h). */ + int nv_range; /* current night vision range */ + int xray_range; /* current xray vision range */ + + /* + * These variables are valid globally only when punished and blind. + */ +#define BC_BALL 0x01 /* bit mask for ball in 'bc_felt' below */ +#define BC_CHAIN 0x02 /* bit mask for chain in 'bc_felt' below */ + int bglyph; /* glyph under the ball */ + int cglyph; /* glyph under the chain */ + int bc_order; /* ball & chain order [see bc_order() in ball.c] */ + int bc_felt; /* mask for ball/chain being felt */ + + int umonster; /* hero's "real" monster num */ + int umonnum; /* current monster number */ + + int mh, mhmax, mtimedone; /* for polymorph-self */ + struct attribs macurr, /* for monster attribs */ + mamax; /* for monster attribs */ + int ulycn; /* lycanthrope type */ + + unsigned ucreamed; + unsigned uswldtim; /* time you have been swallowed */ + + Bitfield(uswallow,1); /* true if swallowed */ + Bitfield(uinwater,1); /* if you're currently in water (only + underwater possible currently) */ + Bitfield(uundetected,1); /* if you're a hiding monster/piercer */ + Bitfield(mfemale,1); /* saved human value of flags.female */ + Bitfield(uinvulnerable,1); /* you're invulnerable (praying) */ + Bitfield(uburied,1); /* you're buried */ + Bitfield(uedibility,1); /* blessed food detection; sense unsafe food */ + /* 1 free bit! */ + + unsigned udg_cnt; /* how long you have been demigod */ + struct u_event uevent; /* certain events have happened */ + struct u_have uhave; /* you're carrying special objects */ + struct u_conduct uconduct; /* KMH, conduct */ + struct attribs acurr, /* your current attributes (eg. str)*/ + aexe, /* for gain/loss via "exercise" */ + abon, /* your bonus attributes (eg. str) */ + amax, /* your max attributes (eg. str) */ + atemp, /* used for temporary loss/gain */ + atime; /* used for loss/gain countdown */ + align ualign; /* character alignment */ +#define CONVERT 2 +#define A_ORIGINAL 1 +#define A_CURRENT 0 + aligntyp ualignbase[CONVERT]; /* for ualign conversion record */ + schar uluck, moreluck; /* luck and luck bonus */ +#define Luck (u.uluck + u.moreluck) +#define LUCKADD 3 /* added value when carrying luck stone */ +#define LUCKMAX 10 /* on moonlit nights 11 */ +#define LUCKMIN (-10) + schar uhitinc; + schar udaminc; + schar uac; + uchar uspellprot; /* protection by SPE_PROTECTION */ + uchar usptime; /* #moves until uspellprot-- */ + uchar uspmtime; /* #moves between uspellprot-- */ + int uhp,uhpmax; + int uen, uenmax; /* magical energy - M. Stephenson */ + int ugangr; /* if the gods are angry at you */ + int ugifts; /* number of artifacts bestowed */ + int ublessed, ublesscnt; /* blessing/duration from #pray */ +#ifndef GOLDOBJ + long ugold, ugold0; +#else + long umoney0; +#endif + long uexp, urexp; + long ucleansed; /* to record moves when player was cleansed */ + long usleep; /* sleeping; monstermove you last started */ + int uinvault; + struct monst *ustuck; +#ifdef STEED + struct monst *usteed; + long ugallop; + int urideturns; +#endif + int umortality; /* how many times you died */ + int ugrave_arise; /* you die and become something aside from a ghost */ + time_t ubirthday; /* real world time when game began */ + + int weapon_slots; /* unused skill slots */ + int skills_advanced; /* # of advances made so far */ + xchar skill_record[P_SKILL_LIMIT]; /* skill advancements */ + struct skills weapon_skills[P_NUM_SKILLS]; + boolean twoweap; /* KMH -- Using two-weapon combat */ + +}; /* end of `struct you' */ + +#define Upolyd (u.umonnum != u.umonster) + +#endif /* YOU_H */ diff --git a/include/youprop.h b/include/youprop.h new file mode 100644 index 0000000..fb80f63 --- /dev/null +++ b/include/youprop.h @@ -0,0 +1,342 @@ +/* SCCS Id: @(#)youprop.h 3.4 1999/07/02 */ +/* Copyright (c) 1989 Mike Threepoint */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef YOUPROP_H +#define YOUPROP_H + +#include "prop.h" +#include "permonst.h" +#include "mondata.h" +#include "pm.h" + + +/* KMH, intrinsics patch. + * Reorganized and rewritten for >32-bit properties. + * HXxx refers to intrinsic bitfields while in human form. + * EXxx refers to extrinsic bitfields from worn objects. + * BXxx refers to the cause of the property being blocked. + * Xxx refers to any source, including polymorph forms. + */ + + +#define maybe_polyd(if_so,if_not) (Upolyd ? (if_so) : (if_not)) + + +/*** Resistances to troubles ***/ +/* With intrinsics and extrinsics */ +#define HFire_resistance u.uprops[FIRE_RES].intrinsic +#define EFire_resistance u.uprops[FIRE_RES].extrinsic +#define Fire_resistance (HFire_resistance || EFire_resistance || \ + resists_fire(&youmonst)) + +#define HCold_resistance u.uprops[COLD_RES].intrinsic +#define ECold_resistance u.uprops[COLD_RES].extrinsic +#define Cold_resistance (HCold_resistance || ECold_resistance || \ + resists_cold(&youmonst)) + +#define HSleep_resistance u.uprops[SLEEP_RES].intrinsic +#define ESleep_resistance u.uprops[SLEEP_RES].extrinsic +#define Sleep_resistance (HSleep_resistance || ESleep_resistance || \ + resists_sleep(&youmonst)) + +#define HDisint_resistance u.uprops[DISINT_RES].intrinsic +#define EDisint_resistance u.uprops[DISINT_RES].extrinsic +#define Disint_resistance (HDisint_resistance || EDisint_resistance || \ + resists_disint(&youmonst)) + +#define HShock_resistance u.uprops[SHOCK_RES].intrinsic +#define EShock_resistance u.uprops[SHOCK_RES].extrinsic +#define Shock_resistance (HShock_resistance || EShock_resistance || \ + resists_elec(&youmonst)) + +#define HPoison_resistance u.uprops[POISON_RES].intrinsic +#define EPoison_resistance u.uprops[POISON_RES].extrinsic +#define Poison_resistance (HPoison_resistance || EPoison_resistance || \ + resists_poison(&youmonst)) + +#define HDrain_resistance u.uprops[DRAIN_RES].intrinsic +#define EDrain_resistance u.uprops[DRAIN_RES].extrinsic +#define Drain_resistance (HDrain_resistance || EDrain_resistance || \ + resists_drli(&youmonst)) + +/* Intrinsics only */ +#define HSick_resistance u.uprops[SICK_RES].intrinsic +#define Sick_resistance (HSick_resistance || \ + youmonst.data->mlet == S_FUNGUS || \ + youmonst.data == &mons[PM_GHOUL] || \ + defends(AD_DISE,uwep)) +#define Invulnerable u.uprops[INVULNERABLE].intrinsic /* [Tom] */ + +/* Extrinsics only */ +#define EAntimagic u.uprops[ANTIMAGIC].extrinsic +#define Antimagic (EAntimagic || \ + (Upolyd && resists_magm(&youmonst))) + +#define EAcid_resistance u.uprops[ACID_RES].extrinsic +#define Acid_resistance (EAcid_resistance || resists_acid(&youmonst)) + +#define EStone_resistance u.uprops[STONE_RES].extrinsic +#define Stone_resistance (EStone_resistance || resists_ston(&youmonst)) + + +/*** Troubles ***/ +/* Pseudo-property */ +#define Punished (uball) + +/* Those implemented solely as timeouts (we use just intrinsic) */ +#define HStun u.uprops[STUNNED].intrinsic +#define Stunned (HStun || u.umonnum == PM_STALKER || \ + youmonst.data->mlet == S_BAT) + /* Note: birds will also be stunned */ + +#define HConfusion u.uprops[CONFUSION].intrinsic +#define Confusion HConfusion + +#define Blinded u.uprops[BLINDED].intrinsic +#define Blindfolded (ublindf && ublindf->otyp != LENSES) + /* ...means blind because of a cover */ +#define Blind ((Blinded || Blindfolded || !haseyes(youmonst.data)) && \ + !(ublindf && ublindf->oartifact == ART_EYES_OF_THE_OVERWORLD)) + /* ...the Eyes operate even when you really are blind + or don't have any eyes */ + +#define Sick u.uprops[SICK].intrinsic +#define Stoned u.uprops[STONED].intrinsic +#define Strangled u.uprops[STRANGLED].intrinsic +#define Vomiting u.uprops[VOMITING].intrinsic +#define Glib u.uprops[GLIB].intrinsic +#define Slimed u.uprops[SLIMED].intrinsic /* [Tom] */ + +/* Hallucination is solely a timeout; its resistance is extrinsic */ +#define HHallucination u.uprops[HALLUC].intrinsic +#define EHalluc_resistance u.uprops[HALLUC_RES].extrinsic +#define Halluc_resistance (EHalluc_resistance || \ + (Upolyd && dmgtype(youmonst.data, AD_HALU))) +#define Hallucination (HHallucination && !Halluc_resistance) + +/* Timeout, plus a worn mask */ +#define HFumbling u.uprops[FUMBLING].intrinsic +#define EFumbling u.uprops[FUMBLING].extrinsic +#define Fumbling (HFumbling || EFumbling) + +#define HWounded_legs u.uprops[WOUNDED_LEGS].intrinsic +#define EWounded_legs u.uprops[WOUNDED_LEGS].extrinsic +#define Wounded_legs (HWounded_legs || EWounded_legs) + +#define HSleeping u.uprops[SLEEPING].intrinsic +#define ESleeping u.uprops[SLEEPING].extrinsic +#define Sleeping (HSleeping || ESleeping) + +#define HHunger u.uprops[HUNGER].intrinsic +#define EHunger u.uprops[HUNGER].extrinsic +#define Hunger (HHunger || EHunger) + + +/*** Vision and senses ***/ +#define HSee_invisible u.uprops[SEE_INVIS].intrinsic +#define ESee_invisible u.uprops[SEE_INVIS].extrinsic +#define See_invisible (HSee_invisible || ESee_invisible || \ + perceives(youmonst.data)) + +#define HTelepat u.uprops[TELEPAT].intrinsic +#define ETelepat u.uprops[TELEPAT].extrinsic +#define Blind_telepat (HTelepat || ETelepat || \ + telepathic(youmonst.data)) +#define Unblind_telepat (ETelepat) + +#define HWarning u.uprops[WARNING].intrinsic +#define EWarning u.uprops[WARNING].extrinsic +#define Warning (HWarning || EWarning) + +/* Warning for a specific type of monster */ +#define HWarn_of_mon u.uprops[WARN_OF_MON].intrinsic +#define EWarn_of_mon u.uprops[WARN_OF_MON].extrinsic +#define Warn_of_mon (HWarn_of_mon || EWarn_of_mon) + +#define HUndead_warning u.uprops[WARN_UNDEAD].intrinsic +#define Undead_warning (HUndead_warning) + +#define HSearching u.uprops[SEARCHING].intrinsic +#define ESearching u.uprops[SEARCHING].extrinsic +#define Searching (HSearching || ESearching) + +#define HClairvoyant u.uprops[CLAIRVOYANT].intrinsic +#define EClairvoyant u.uprops[CLAIRVOYANT].extrinsic +#define BClairvoyant u.uprops[CLAIRVOYANT].blocked +#define Clairvoyant ((HClairvoyant || EClairvoyant) &&\ + !BClairvoyant) + +#define HInfravision u.uprops[INFRAVISION].intrinsic +#define EInfravision u.uprops[INFRAVISION].extrinsic +#define Infravision (HInfravision || EInfravision || \ + infravision(youmonst.data)) + +#define HDetect_monsters u.uprops[DETECT_MONSTERS].intrinsic +#define EDetect_monsters u.uprops[DETECT_MONSTERS].extrinsic +#define Detect_monsters (HDetect_monsters || EDetect_monsters) + + +/*** Appearance and behavior ***/ +#define Adornment u.uprops[ADORNED].extrinsic + +#define HInvis u.uprops[INVIS].intrinsic +#define EInvis u.uprops[INVIS].extrinsic +#define BInvis u.uprops[INVIS].blocked +#define Invis ((HInvis || EInvis || \ + pm_invisible(youmonst.data)) && !BInvis) +#define Invisible (Invis && !See_invisible) + /* Note: invisibility also hides inventory and steed */ + +#define EDisplaced u.uprops[DISPLACED].extrinsic +#define Displaced EDisplaced + +#define HStealth u.uprops[STEALTH].intrinsic +#define EStealth u.uprops[STEALTH].extrinsic +#define BStealth u.uprops[STEALTH].blocked +#define Stealth ((HStealth || EStealth) && !BStealth) + +#define HAggravate_monster u.uprops[AGGRAVATE_MONSTER].intrinsic +#define EAggravate_monster u.uprops[AGGRAVATE_MONSTER].extrinsic +#define Aggravate_monster (HAggravate_monster || EAggravate_monster) + +#define HConflict u.uprops[CONFLICT].intrinsic +#define EConflict u.uprops[CONFLICT].extrinsic +#define Conflict (HConflict || EConflict) + + +/*** Transportation ***/ +#define HJumping u.uprops[JUMPING].intrinsic +#define EJumping u.uprops[JUMPING].extrinsic +#define Jumping (HJumping || EJumping) + +#define HTeleportation u.uprops[TELEPORT].intrinsic +#define ETeleportation u.uprops[TELEPORT].extrinsic +#define Teleportation (HTeleportation || ETeleportation || \ + can_teleport(youmonst.data)) + +#define HTeleport_control u.uprops[TELEPORT_CONTROL].intrinsic +#define ETeleport_control u.uprops[TELEPORT_CONTROL].extrinsic +#define Teleport_control (HTeleport_control || ETeleport_control || \ + control_teleport(youmonst.data)) + +#define HLevitation u.uprops[LEVITATION].intrinsic +#define ELevitation u.uprops[LEVITATION].extrinsic +#define Levitation (HLevitation || ELevitation || \ + is_floater(youmonst.data)) + /* Can't touch surface, can't go under water; overrides all others */ +#define Lev_at_will (((HLevitation & I_SPECIAL) != 0L || \ + (ELevitation & W_ARTI) != 0L) && \ + (HLevitation & ~(I_SPECIAL|TIMEOUT)) == 0L && \ + (ELevitation & ~W_ARTI) == 0L && \ + !is_floater(youmonst.data)) + +#define EFlying u.uprops[FLYING].extrinsic +#ifdef STEED +# define Flying (EFlying || is_flyer(youmonst.data) || \ + (u.usteed && is_flyer(u.usteed->data))) +#else +# define Flying (EFlying || is_flyer(youmonst.data)) +#endif + /* May touch surface; does not override any others */ + +#define Wwalking (u.uprops[WWALKING].extrinsic && \ + !Is_waterlevel(&u.uz)) + /* Don't get wet, can't go under water; overrides others except levitation */ + /* Wwalking is meaningless on water level */ + +#define HSwimming u.uprops[SWIMMING].intrinsic +#define ESwimming u.uprops[SWIMMING].extrinsic /* [Tom] */ +#ifdef STEED +# define Swimming (HSwimming || ESwimming || \ + is_swimmer(youmonst.data) || \ + (u.usteed && is_swimmer(u.usteed->data))) +#else +# define Swimming (HSwimming || ESwimming || \ + is_swimmer(youmonst.data)) +#endif + /* Get wet, don't go under water unless if amphibious */ + +#define HMagical_breathing u.uprops[MAGICAL_BREATHING].intrinsic +#define EMagical_breathing u.uprops[MAGICAL_BREATHING].extrinsic +#define Amphibious (HMagical_breathing || EMagical_breathing || \ + amphibious(youmonst.data)) + /* Get wet, may go under surface */ + +#define Breathless (HMagical_breathing || EMagical_breathing || \ + breathless(youmonst.data)) + +#define Underwater (u.uinwater) +/* Note that Underwater and u.uinwater are both used in code. + The latter form is for later implementation of other in-water + states, like swimming, wading, etc. */ + +#define HPasses_walls u.uprops[PASSES_WALLS].intrinsic +#define EPasses_walls u.uprops[PASSES_WALLS].extrinsic +#define Passes_walls (HPasses_walls || EPasses_walls || \ + passes_walls(youmonst.data)) + + +/*** Physical attributes ***/ +#define HSlow_digestion u.uprops[SLOW_DIGESTION].intrinsic +#define ESlow_digestion u.uprops[SLOW_DIGESTION].extrinsic +#define Slow_digestion (HSlow_digestion || ESlow_digestion) /* KMH */ + +#define HHalf_spell_damage u.uprops[HALF_SPDAM].intrinsic +#define EHalf_spell_damage u.uprops[HALF_SPDAM].extrinsic +#define Half_spell_damage (HHalf_spell_damage || EHalf_spell_damage) + +#define HHalf_physical_damage u.uprops[HALF_PHDAM].intrinsic +#define EHalf_physical_damage u.uprops[HALF_PHDAM].extrinsic +#define Half_physical_damage (HHalf_physical_damage || EHalf_physical_damage) + +#define HRegeneration u.uprops[REGENERATION].intrinsic +#define ERegeneration u.uprops[REGENERATION].extrinsic +#define Regeneration (HRegeneration || ERegeneration || \ + regenerates(youmonst.data)) + +#define HEnergy_regeneration u.uprops[ENERGY_REGENERATION].intrinsic +#define EEnergy_regeneration u.uprops[ENERGY_REGENERATION].extrinsic +#define Energy_regeneration (HEnergy_regeneration || EEnergy_regeneration) + +#define HProtection u.uprops[PROTECTION].intrinsic +#define EProtection u.uprops[PROTECTION].extrinsic +#define Protection (HProtection || EProtection) + +#define HProtection_from_shape_changers \ + u.uprops[PROT_FROM_SHAPE_CHANGERS].intrinsic +#define EProtection_from_shape_changers \ + u.uprops[PROT_FROM_SHAPE_CHANGERS].extrinsic +#define Protection_from_shape_changers \ + (HProtection_from_shape_changers || \ + EProtection_from_shape_changers) + +#define HPolymorph u.uprops[POLYMORPH].intrinsic +#define EPolymorph u.uprops[POLYMORPH].extrinsic +#define Polymorph (HPolymorph || EPolymorph) + +#define HPolymorph_control u.uprops[POLYMORPH_CONTROL].intrinsic +#define EPolymorph_control u.uprops[POLYMORPH_CONTROL].extrinsic +#define Polymorph_control (HPolymorph_control || EPolymorph_control) + +#define HUnchanging u.uprops[UNCHANGING].intrinsic +#define EUnchanging u.uprops[UNCHANGING].extrinsic +#define Unchanging (HUnchanging || EUnchanging) /* KMH */ + +#define HFast u.uprops[FAST].intrinsic +#define EFast u.uprops[FAST].extrinsic +#define Fast (HFast || EFast) +#define Very_fast ((HFast & ~INTRINSIC) || EFast) + +#define EReflecting u.uprops[REFLECTING].extrinsic +#define Reflecting (EReflecting || \ + (youmonst.data == &mons[PM_SILVER_DRAGON])) + +#define Free_action u.uprops[FREE_ACTION].extrinsic /* [Tom] */ + +#define Fixed_abil u.uprops[FIXED_ABIL].extrinsic /* KMH */ + +#define Lifesaved u.uprops[LIFESAVED].extrinsic + + +#endif /* YOUPROP_H */ diff --git a/nethack.dsw b/nethack.dsw new file mode 100644 index 0000000..ff30f52 --- /dev/null +++ b/nethack.dsw @@ -0,0 +1,212 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "NetHackW"=.\build\NetHackW.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgncomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name dlb_main + End Project Dependency + Begin Project Dependency + Project_Dep_Name levcomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency + Begin Project Dependency + Project_Dep_Name tilemap + End Project Dependency + Begin Project Dependency + Project_Dep_Name tiles + End Project Dependency + Begin Project Dependency + Project_Dep_Name uudecode + End Project Dependency +}}} + +############################################################################### + +Project: "dgncomp"=.\build\dgncomp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgnstuff + End Project Dependency +}}} + +############################################################################### + +Project: "dgnstuff"=.\build\dgnstuff.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "dlb_main"=.\build\dlb_main.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgncomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name levcomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "levcomp"=.\build\levcomp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name levstuff + End Project Dependency +}}} + +############################################################################### + +Project: "levstuff"=.\build\levstuff.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "makedefs"=.\build\makedefs.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "recover"=.\build\recover.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency + Begin Project Dependency + Project_Dep_Name dlb_main + End Project Dependency +}}} + +############################################################################### + +Project: "tile2bmp"=.\build\tile2bmp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tilemap"=.\build\tilemap.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tiles"=.\build\tiles.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name tile2bmp + End Project Dependency +}}} + +############################################################################### + +Project: "uudecode"=.\build\uudecode.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..832b857 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,1419 @@ +# SCCS Id: @(#)Makefile.msc 3.4 $Date: 2003/11/16 04:50:58 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# NetHack 3.4.x Makefile for MS Visual C++ V6.x and above and MS NMAKE +# +# Win32 Compilers Tested: +# - Microsoft 32 bit Visual C++ V4.x +# - Microsoft 32 bit Visual C++ V6.0 SP3, SP4 +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +#============================================================================== +# Do not delete the following 3 lines. +# +TARGETOS=BOTH +APPVER=4.0 +!include + +# Graphical interface +# Set to Y for a graphical version + +#GRAPHICAL = Y + +# Set the gamedir according to your preference. +# If not present prior to compilation it gets created. + +!IF "$(GRAPHICAL)" == "Y" +GAME = NetHackW # Game Name +!ELSE +GAME = NetHack # Game Name +!ENDIF + +GAMEDIR = ..\binary # Game directory + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = ..\include # NetHack include files +DAT = ..\dat # NetHack data files +DOC = ..\doc # NetHack documentation files +UTIL = ..\util # Utility source +SRC = ..\src # Main source +SSYS = ..\sys\share # Shared system files +NTSYS = ..\sys\winnt # NT Win32 specific files +TTY = ..\win\tty # window port files (tty) +WIN32 = ..\win\win32 # window port files (Win32) +WSHR = ..\win\share # Tile support files + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), comment out the upper two macros and uncomment +# the lower two. +# + +DO_YACC = YACC_MSG +DO_LEX = LEX_MSG +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +# - Specify your yacc and lex programs (or work-alikes) here. + +#YACC = bison -y +YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# Leave the next two lines uncommented _ONLY_ if you do NOT want any +# debug capability in the object files, or in the NetHack executable. +# Comment them if you want debug capability. + +#cdebug = +#linkdebug = + +# +# Compiler and Linker flags +# + +PRECOMPHEAD = N # set to Y if you want to use precomp. headers + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +!IF "$(GRAPHICAL)" == "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPHDR = $(WIN32)\mhaskyn.h $(WIN32)\mhdlg.h $(WIN32)\mhfont.h \ + $(WIN32)\mhinput.h $(WIN32)\mhmain.h $(WIN32)\mhmap.h $(WIN32)\mhmenu.h \ + $(WIN32)\mhmsg.h $(WIN32)\mhmsgwnd.h $(WIN32)\mhrip.h $(WIN32)\mhstatus.h \ + $(WIN32)\mhtext.h $(WIN32)\resource.h $(WIN32)\winMS.h +WINDLLS = +WINPFLAG= -DTILES -DMSWIN_GRAPHICS +NHRES = $(O)winhack.res +WINPINC = -I$(WIN32) +!ELSE +WINPORT = $(O)nttty.o +WINPHDR = +WINDLLS = $(GAMEDIR)\nhdefkey.dll $(GAMEDIR)\nh340key.dll $(GAMEDIR)\nhraykey.dll +WINPFLAG= -DWIN32CON +NHRES = $(O)console.res +WINPINC = +!ENDIF + +TILEUTIL16 = $(UTIL)\tile2bmp.exe +TILEBMP16 = $(SRC)\tiles.bmp + +TILEUTIL32 = $(UTIL)\til2bm32.exe +TILEBMP32 = $(SRC)\tiles32.bmp + +SOUND = $(OBJ)\ntsound.o + +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = -DDLB +! ELSE +DLBFLG = +! ENDIF + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) $(cvarsmt) -I$(INCL) -nologo $(cdebug) $(WINPINC) +LFLAGSBASEC = $(linkdebug) /NODEFAULTLIB /INCREMENTAL:NO /RELEASE /NOLOGO -subsystem:console,4.0 $(conlibsmt) +LFLAGSBASEG = $(linkdebug) $(guiflags) $(guilibsmt) comctl32.lib + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASEC) + +#========================================== +# - Game build +#========================================== +LFLAGSBASE = $(linkdebug) /NODEFAULTLIB /INCREMENTAL:NO /RELEASE /NOLOGO -subsystem:console,4.0 $(conlibsmt) +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +NHLFLAGS1 = /NODEFAULTLIB /INCREMENTAL:NO /PDB:"$(GAME).PDB" /RELEASE /NOLOGO +NHLFLAGS2 = /MAP:"$(GAME).MAP" /MACHINE:$(CPU) -IGNORE:505 +!IF ("$(GRAPHICAL)"=="Y") +LFLAGS = $(LFLAGSBASEG) $(NHLFLAGS1) $(NHLFLAGS2) +!ELSE +LFLAGS = $(LFLAGSBASEC) $(NHLFLAGS1) $(NHLFLAGS2) +!ENDIF + +GAMEFILE = $(GAMEDIR)\$(GAME).exe # whole thing + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -Fo$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +#========================================== +# Rules for files in sys\share +#========================================== + +{$(SSYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +#========================================== +# Rules for files in sys\winnt +#========================================== + +{$(NTSYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +{$(NTSYS)}.h{$(INCL)}.h: + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +{$(UTIL)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in win\share +#========================================== + +{$(WSHR)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win\tty +#========================================== + +{$(TTY)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + + +#========================================== +# Rules for files in win\win32 +#========================================== + +{$(WIN32)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -Fo$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)\$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)^\ + +U = $(UTIL)^\ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +TEXT_IO32 = $(O)tilete32.o $(O)tiletx32.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o +GIFREADERS32 = $(O)gifrd32.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)pcmain.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +WINPOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(WINPOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + +!IF "$(GRAPHICAL)" == "Y" +OPTIONS_FILE = $(DAT)\guioptions +!ELSE +OPTIONS_FILE = $(DAT)\ttyoptions +!ENDIF +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h \ + $(INCL)\global.h $(INCL)\coord.h $(INCL)\vmsconf.h \ + $(INCL)\system.h $(INCL)\unixconf.h $(INCL)\os2conf.h \ + $(INCL)\micro.h $(INCL)\pcconf.h $(INCL)\tosconf.h \ + $(INCL)\amiconf.h $(INCL)\macconf.h $(INCL)\beconf.h \ + $(INCL)\ntconf.h $(INCL)\nhlan.h + +HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\align.h \ + $(INCL)\dungeon.h $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\youprop.h $(INCL)\prop.h \ + $(INCL)\permonst.h $(INCL)\monattk.h \ + $(INCL)\monflag.h $(INCL)\mondata.h $(INCL)\pm.h \ + $(INCL)\wintype.h $(INCL)\decl.h $(INCL)\quest.h \ + $(INCL)\spell.h $(INCL)\color.h $(INCL)\obj.h \ + $(INCL)\you.h $(INCL)\attrib.h $(INCL)\monst.h \ + $(INCL)\skills.h $(INCL)\onames.h $(INCL)\timeout.h \ + $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\display.h $(INCL)\engrave.h \ + $(INCL)\rect.h $(INCL)\region.h $(INCL)\winprocs.h \ + $(INCL)\wintty.h $(INCL)\trampoli.h + +LEV_H = $(INCL)\lev.h +DGN_FILE_H = $(INCL)\dgn_file.h +LEV_COMP_H = $(INCL)\lev_comp.h +SP_LEV_H = $(INCL)\sp_lev.h +TILE_H = ..\win\share\tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)\data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +#========================================== +#=============== TARGETS ================== +#========================================== + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME): $(O)obj.tag $(O)utility.tag envchk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: envchk $(GAME) $(O)install.tag + @echo Done. + +$(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) + copy $(DAT)\opthelp $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + if exist $(DOC)\guidebook.txt copy $(DOC)\guidebook.txt $(GAMEDIR)\Guidebook.txt + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt + @if exist $(SRC)\$(GAME).PDB copy $(SRC)\$(GAME).pdb $(GAMEDIR)\$(GAME).pdb + @if exist $(GAMEDIR)\$(GAME).PDB echo NOTE: You may want to remove $(GAMEDIR)\$(GAME).pdb to conserve space + -copy $(NTSYS)\defaults.nh $(GAMEDIR)\defaults.nh + echo install done > $@ + +# copy $(NTSYS)\winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)\bigroom.des $(DAT)\castle.des \ + $(DAT)\endgame.des $(DAT)\gehennom.des $(DAT)\knox.des \ + $(DAT)\medusa.des $(DAT)\oracle.des $(DAT)\tower.des \ + $(DAT)\yendor.des $(DAT)\arch.des $(DAT)\barb.des \ + $(DAT)\caveman.des $(DAT)\healer.des $(DAT)\knight.des \ + $(DAT)\monk.des $(DAT)\priest.des $(DAT)\ranger.des \ + $(DAT)\rogue.des $(DAT)\samurai.des $(DAT)\sokoban.des \ + $(DAT)\tourist.des $(DAT)\valkyrie.des $(DAT)\wizard.des + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) + echo sp_levs done > $(O)sp_lev.tag + +$(O)utility.tag: $(INCL)\date.h $(INCL)\onames.h $(INCL)\pm.h \ + $(SRC)\monstr.c $(SRC)\vis_tab.c \ + $(U)lev_comp.exe $(INCL)\vis_tab.h \ + $(U)dgn_comp.exe + @echo utilities made >$@ + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +!IF "$(GRAPHICAL)"=="Y" +$(NHRES): $(TILEBMP16) $(WIN32)\winhack.rc $(WIN32)\mnsel.bmp \ + $(WIN32)\mnselcnt.bmp $(WIN32)\mnunsel.bmp \ + $(WIN32)\petmark.bmp $(WIN32)\NetHack.ico $(WIN32)\rip.bmp \ + $(WIN32)\splash.bmp + @$(rc) -r -fo$@ -i$(WIN32) -dNDEBUG $(WIN32)\winhack.rc +!ELSE +$(NHRES): $(NTSYS)\console.rc $(NTSYS)\NetHack.ico + @$(rc) -r -fo$@ -i$(NTSYS) -dNDEBUG $(NTSYS)\console.rc +!ENDIF + +#========================================== +# The main target. +#========================================== + +# The section for linking the NetHack image looks a little strange at +# first, especially if you are used to UNIX makes, or NDMAKE. It is +# Microsoft nmake specific, and it gets around the problem of the +# link command line being too long for the linker. An "in-line" linker +# response file is generated temporarily. +# +# It takes advantage of the following features of nmake: +# +# Inline files : +# Specifying the "<<" means to start an inline file. +# Another "<<" at the start of a line closes the +# inline file. +# +# Substitution within Macros: +# $(mymacro:string1=string2) replaces every +# occurrence of string1 with string2 in the +# macro mymacro. Special ascii key codes may be +# used in the substitution text by preceding it +# with ^ as we have done below. Every occurence +# of a in $(ALLOBJ) is replaced by +# <+>. +# +# DO NOT INDENT THE << below! +# + +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag $(WINDLLS) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo Linking.... + $(link) $(LFLAGS) user32.lib winmm.lib -out:$@ @<<$(GAME).lnk + $(ALLOBJ:^ =^ + ) $(NHRES) +<< + @if exist $(O)install.tag del $(O)install.tag + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +$(O)gamedir.tag: + @if not exist $(GAMEDIR)\*.* echo creating directory $(GAMEDIR) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo directory created > $@ + +$(O)nhdefkey.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nhdefkey.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +$(O)nh340key.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nh340key.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +$(O)nhraykey.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nhraykey.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + @$(link) $(LFLAGSU) -out:$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(INCL)\patchlevel.h \ + $(U)makedefs.c + @if not exist $(OBJ)\*.* echo creating directory $(OBJ) + @if not exist $(OBJ)\*.* mkdir $(OBJ) + @$(CC) $(CFLAGSU) -Fo$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe + $(U)makedefs -v + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) -out:$@ $(O)uudecode.o + +$(O)uudecode.o: $(SSYS)\uudecode.c + +$(NTSYS)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(NTSYS) + ..\..\util\uudecode.exe nhico.uu + chdir ..\..\src + +$(WIN32)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(WIN32) + ..\..\util\uudecode.exe ../../sys/winnt/nhico.uu + chdir ..\..\src + +$(WIN32)\mnsel.bmp: $(U)uudecode.exe $(WIN32)\mnsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnsel.uu + chdir ..\..\src + +$(WIN32)\mnselcnt.bmp: $(U)uudecode.exe $(WIN32)\mnselcnt.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnselcnt.uu + chdir ..\..\src + +$(WIN32)\mnunsel.bmp: $(U)uudecode.exe $(WIN32)\mnunsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnunsel.uu + chdir ..\..\src + +$(WIN32)\petmark.bmp: $(U)uudecode.exe $(WIN32)\petmark.uu + chdir $(WIN32) + ..\..\util\uudecode.exe petmark.uu + chdir ..\..\src + +$(WIN32)\rip.bmp: $(U)uudecode.exe $(WIN32)\rip.uu + chdir $(WIN32) + ..\..\util\uudecode.exe rip.uu + chdir ..\..\src + +$(WIN32)\splash.bmp: $(U)uudecode.exe $(WIN32)\splash.uu + chdir $(WIN32) + ..\..\util\uudecode.exe splash.uu + chdir ..\..\src + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=-c -nologo -DWINVER=0x0400 -DWIN32 -D_WIN32 \ + -D_MT -MT -I..\include -nologo -Z7 -Od -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(SPLEVOBJS:^ =^ + ) +<< + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d lev_comp.y + copy $(YTABC) lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\lev_yacc.c $(U)lev_yacc.c >nul + @copy $(SSYS)\lev_comp.h $(INCL)\lev_comp.h >nul + @echo /**/ >>$(U)lev_yacc.c + @echo /**/ >>$(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\lev_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(DGNCOMPOBJS:^ =^ + ) +<< + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h $(U)dgn_yacc.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d dgn_comp.y + copy $(YTABC) dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\dgn_yacc.c $(U)dgn_yacc.c >nul + @copy $(SSYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + @echo /**/ >>$(U)dgn_yacc.c + @echo /**/ >>$(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\dgn_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + @if not exist $(OBJ)\*.* echo creating directory $(OBJ) + @if not exist $(OBJ)\*.* mkdir $(OBJ) + @echo directory created >$@ + +#========================================== +# Notify of any CL environment variables +# in effect since they change the compiler +# options. +#========================================== + +envchk: +! IF "$(CL)"!="" + @echo Warning, the CL Environment variable is defined: + @echo CL=$(CL) +! ENDIF +! IF "$(GRAPHICAL)"=="Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +! ENDIF + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ..\include +#=========================================== + +$(INCL)\win32api.h: $(NTSYS)\win32api.h + copy $(NTSYS)\win32api.h $@ + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)dlb_main.o + $(O)dlb.o + $(O)alloc.o + $(O)panic.o +<< + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)\dlb.h + @$(CC) $(CFLAGS) /Fo$@ $(SRC)\dlb.c + +$(O)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h + @$(CC) $(CFLAGS) /Fo$@ $(UTIL)\dlb_main.c + +$(DAT)\porthelp: $(NTSYS)\porthelp + @copy $(NTSYS)\porthelp $@ >nul + +nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon $(DAT)\porthelp \ + $(DAT)\license $(O)sp_lev.tag + cd $(DAT) + echo data >dlb.lst + echo oracles >>dlb.lst + if exist options echo options >>dlb.lst + if exist ttyoptions echo ttyoptions >>dlb.lst + if exist guioptions echo guioptions >>dlb.lst + if exist porthelp echo porthelp >>dlb.lst + echo quest.dat >>dlb.lst + echo rumors >>dlb.lst + echo help >>dlb.lst + echo hh >>dlb.lst + echo cmdhelp >>dlb.lst + echo history >>dlb.lst + echo opthelp >>dlb.lst + echo wizhelp >>dlb.lst + echo dungeon >>dlb.lst + echo license >>dlb.lst + for %%N in (*.lev) do echo %%N >>dlb.lst + $(U)dlb_main cIf dlb.lst $(SRC)\nhdat + cd $(SRC) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) + $(link) $(LFLAGSU) -out:$@ $(RECOVOBJS) + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)\win32api.h + $(CC) $(CFLAGSU) -Fo$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)\tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + @$(link) $(LFLAGSU) -out:$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGSU) -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletx32.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGS) /DTILETEXT /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGS) /DTILETEXT -Fo$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\gifread.c + +$(O)gifrd32.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\tiletext.c + +$(O)tilete32.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(GIFREADERS:^ =^ + ) + $(TEXT_IO:^ =^ + ) +<< + +$(U)gif2tx32.exe: $(GIFREADERS32) $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(GIFREADERS32:^ =^ + ) + $(TEXT_IO32:^ =^ + ) +<< + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(PPMWRITERS:^ =^ + ) + $(TEXT_IO:^ =^ + ) +<< + +!IF "$(GRAPHICAL)"=="Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + @$(U)tile2bmp $(TILEBMP16) +#$(TILEBMP32): $(TILEUTIL32) $(TILEFILES32) +# @echo Creating 32x32 binary tile files (this may take some time) +# @$(U)til2bm32 $(TILEBMP32) + +!ELSE +$(TILEBMP16): +$(TILEBMP32): +!ENDIF + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)tile2bmp.o + $(TEXT_IO:^ =^ + ) +<< + +$(U)til2bm32.exe: $(O)til2bm32.o $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)til2bm32.o + $(TEXT_IO32:^ =^ + ) +<< + +$(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(CC) $(CFLAGS) -I$(WSHR) /DPACKED_FILE /Fo$@ $(WSHR)\tile2bmp.c + +$(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(CC) $(CFLAGS) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)\tile2bmp.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean +! IF ("$(OBJ)"!="") + -rmdir $(OBJ) /s /Q +! ENDIF + if exist $(INCL)\date.h del $(INCL)\date.h + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(U)*.lnk del $(U)*.lnk + if exist $(U)*.map del $(U)*.map + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\???-fil?.lev del $(DAT)\???-fil?.lev + if exist $(DAT)\???-goal.lev del $(DAT)\???-goal.lev + if exist $(DAT)\???-loca.lev del $(DAT)\???-loca.lev + if exist $(DAT)\???-strt.lev del $(DAT)\???-strt.lev + if exist $(DAT)\air.lev del $(DAT)\air.lev + if exist $(DAT)\asmodeus.lev del $(DAT)\asmodeus.lev + if exist $(DAT)\astral.lev del $(DAT)\astral.lev + if exist $(DAT)\baalz.lev del $(DAT)\baalz.lev + if exist $(DAT)\bigroom.lev del $(DAT)\bigroom.lev + if exist $(DAT)\castle.lev del $(DAT)\castle.lev + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\dungeon.pdf del $(DAT)\dungeon.pdf + if exist $(DAT)\earth.lev del $(DAT)\earth.lev + if exist $(DAT)\fakewiz?.lev del $(DAT)\fakewiz?.lev + if exist $(DAT)\fire.lev del $(DAT)\fire.lev + if exist $(DAT)\juiblex.lev del $(DAT)\juiblex.lev + if exist $(DAT)\knox.lev del $(DAT)\knox.lev + if exist $(DAT)\medusa-?.lev del $(DAT)\medusa-?.lev + if exist $(DAT)\mine*.lev del $(DAT)\mine*.lev + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\ttyoptions del $(DAT)\ttyoptions + if exist $(DAT)\guioptions del $(DAT)\guioptions + if exist $(DAT)\oracle.lev del $(DAT)\oracle.lev + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\orcus.lev del $(DAT)\orcus.lev + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(DAT)\sanctum.lev del $(DAT)\sanctum.lev + if exist $(DAT)\soko?-?.lev del $(DAT)\soko?-?.lev + if exist $(DAT)\tower?.lev del $(DAT)\tower?.lev + if exist $(DAT)\valley.lev del $(DAT)\valley.lev + if exist $(DAT)\water.lev del $(DAT)\water.lev + if exist $(DAT)\wizard?.lev del $(DAT)\wizard?.lev + if exist $(O)sp_lev.tag del $(O)sp_lev.tag + if exist $(SRC)\monstr.c del $(SRC)\monstr.c + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(U)recover.exe del $(U)recover.exe + if exist nhdat. del nhdat. + if exist $(O)obj.tag del $(O)obj.tag + if exist $(O)gamedir.tag del $(O)gamedir.tag + if exist $(O)nh*key.lib del $(O)nh*key.lib + if exist $(O)nh*key.exp del $(O)nh*key.exp + +clean: + if exist $(O)*.o del $(O)*.o + if exist $(O)utility.tag del $(O)utility.tag + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(SRC)\*.lnk del $(SRC)\*.lnk + if exist $(SRC)\*.map del $(SRC)\*.map + if exist $(O)install.tag del $(O)install.tag +! IF ("$(WINPFLAG)"!="") + if exist $(TILEBMP16) del $(TILEBMP16) + if exist $(TILEBMP32) del $(TILEBMP32) +! ENDIF + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)\data: $(O)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(O)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(O)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(O)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nttty.c + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(NTSYS)\nttty.c +$(O)nhkeys.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nhkeys.c + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(NTSYS)\nhkeys.c +$(O)winnt.o: $(HACK_H) $(INCL)\win32api.h $(NTSYS)\winnt.c + @$(CC) $(CFLAGS) -Fo$@ $(NTSYS)\winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)\ntsound.c + @$(CC) $(CFLAGS) -Fo$@ $(NTSYS)\ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)\nhlan.h $(NTSYS)\mapimail.c + @$(CC) $(CFLAGS) -DMAPI_VERBOSE -Fo$@ $(NTSYS)\mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * slashes changed to back-slashes +# * -c (which is included in CFLAGS) substituted with -Fo$@ +# * targets prefixed with $(O) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\atari\tos.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\win32api.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcmain.c +$(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcsys.c +$(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pctty.c +$(O)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcunix.c +$(O)random.o: ..\sys\share\random.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\random.c +$(O)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\ioctl.c +$(O)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\unixtty.c +$(O)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixmain.c +$(O)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixunix.c +$(O)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixres.c +$(O)bemain.o: ..\sys\be\bemain.c $(HACK_H) $(INCL)\dlb.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\be\bemain.c +$(O)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\getline.c +$(O)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\termcap.c +$(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\topl.c +$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\wintty.c +$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\Window.c +$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\dialogs.c +$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h ..\win\X11\nh72icon \ + ..\win\X11\nh56icon ..\win\X11\nh32icon + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winX.c +$(O)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmap.c +$(O)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmenu.c +$(O)winmesg.o: ..\win\X11\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmesg.c +$(O)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmisc.c +$(O)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winstat.c +$(O)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\wintext.c +$(O)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winval.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)gnaskstr.o: ..\win\gnome\gnaskstr.c ..\win\gnome\gnaskstr.h \ + ..\win\gnome\gnmain.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnaskstr.c +$(O)gnbind.o: ..\win\gnome\gnbind.c ..\win\gnome\gnbind.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnaskstr.h ..\win\gnome\gnyesno.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnbind.c +$(O)gnglyph.o: ..\win\gnome\gnglyph.c ..\win\gnome\gnglyph.h $(INCL)\tile2x11.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnglyph.c +$(O)gnmain.o: ..\win\gnome\gnmain.c ..\win\gnome\gnmain.h ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnbind.h ..\win\gnome\gnopts.h $(HACK_H) \ + $(INCL)\date.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmain.c +$(O)gnmap.o: ..\win\gnome\gnmap.c ..\win\gnome\gnmap.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmap.c +$(O)gnmenu.o: ..\win\gnome\gnmenu.c ..\win\gnome\gnmenu.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnbind.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmenu.c +$(O)gnmesg.o: ..\win\gnome\gnmesg.c ..\win\gnome\gnmesg.h ..\win\gnome\gnsignal.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmesg.c +$(O)gnopts.o: ..\win\gnome\gnopts.c ..\win\gnome\gnopts.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnmain.h ..\win\gnome\gnmap.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnopts.c +$(O)gnplayer.o: ..\win\gnome\gnplayer.c ..\win\gnome\gnplayer.h \ + ..\win\gnome\gnmain.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnplayer.c +$(O)gnsignal.o: ..\win\gnome\gnsignal.c ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnmain.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnsignal.c +$(O)gnstatus.o: ..\win\gnome\gnstatus.c ..\win\gnome\gnstatus.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gn_xpms.h \ + ..\win\gnome\gnomeprv.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnstatus.c +$(O)gntext.o: ..\win\gnome\gntext.c ..\win\gnome\gntext.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gn_rip.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gntext.c +$(O)gnworn.o: ..\win\gnome\gnworn.c ..\win\gnome\gnworn.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gnomeprv.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnworn.c +$(O)gnyesno.o: ..\win\gnome\gnyesno.c ..\win\gnome\gnbind.h ..\win\gnome\gnyesno.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnyesno.c +$(O)wingem.o: ..\win\gem\wingem.c $(HACK_H) $(INCL)\func_tab.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\wingem.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\wingem.c +$(O)wingem1.o: ..\win\gem\wingem1.c $(INCL)\gem_rsc.h $(INCL)\load_img.h \ + $(INCL)\gr_rect.h $(INCL)\wintype.h $(INCL)\wingem.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\wingem1.c +$(O)load_img.o: ..\win\gem\load_img.c $(INCL)\load_img.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\load_img.c +$(O)gr_rect.o: ..\win\gem\gr_rect.c $(INCL)\gr_rect.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\gr_rect.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\dlb.h $(INCL)\patchlevel.h $(INCL)\tile2x11.h \ + $(INCL)\qt_win.h $(INCL)\qt_clust.h $(INCL)\qt_kde0.h \ + $(INCL)\qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qt_win.cpp +$(O)qt_clust.o: ..\win\Qt\qt_clust.cpp $(INCL)\qt_clust.h + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qt_clust.cpp +$(O)qttableview.o: ..\win\Qt\qttableview.cpp $(INCL)\qttableview.h + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qttableview.cpp +$(O)monstr.o: monstr.c $(CONFIG_H) +$(O)vis_tab.o: vis_tab.c $(CONFIG_H) $(INCL)\vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)\edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +$(O)attrib.o: attrib.c $(HACK_H) +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)\lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)\edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)\lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)\edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)\eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)\edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)\tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)\lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)\dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) +$(O)light.o: light.c $(HACK_H) $(INCL)\lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)\mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h \ + $(INCL)\epri.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)\edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)\lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)\epri.h $(INCL)\edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)\epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)\qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)\lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)\lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)\eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)\eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)\edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)\lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)\vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h $(INCL)\epri.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)\lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file diff --git a/src/Makefile.bcc b/src/Makefile.bcc new file mode 100644 index 0000000..c734afe --- /dev/null +++ b/src/Makefile.bcc @@ -0,0 +1,1378 @@ +# SCCS Id: @(#)Makefile.bcc 3.4 $Date: 2003/11/16 04:50:56 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# +# IMPORTANT NOTE: This Makefile has not been tested for 3.4.3. +# +# +# NetHack 3.4.x Makefile for Borland C++ V5.5.1 and above and Borland's MAKE +# +# Win32 Compilers Tested: +# - Borland C++ 5.5.1 for Win32 +# +# If you don't have this compiler, you can get it at: +# http://www.borland.com/bcppbuilder/freecompiler/ +# +# This makefile is set up to assume the directories are extracted at the +# root, but this can be changed by modifying the bccroot and related +# variables. +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +# +# -- +# Yitzhak Sapir +#============================================================================== +# Do not delete the following 3 lines. +# +TARGETOS=BOTH +APPVER=4.0 + +bccbin = $(MAKEDIR) +bccroot = $(MAKEDIR)\.. +bccinc = $(bccroot)\include +bcclib = $(bccroot)\lib + +!IFNDEF APPVER +APPVER = 4.0 +!ENDIF + +# Graphical interface +# Set to Y for a graphical version +# Set to anything else (or undefine) for a tty version + +#GRAPHICAL = Y + +# Debug +# Set to Y for Debug support (to produce full map files, listing files, and debug information) +# Set to anything else (or undefine) for a "release" version + +DEBUG = Y + +!IF "$(APPVER)" == "4.0" +MAKE_WINVER = 0x0400 +!ELSEIF "$(APPVER)" == "5.0" +MAKE_WINVER = 0x0500 +!ENDIF + +cc = $(bccbin)\bcc32 +rc = $(bccbin)\brc32 +link = $(bccbin)\ilink32 +implib = $(bccbin)\tlib + +cflags = -c -D_X86_=1 -DWINVER=$(MAKE_WINVER) -q -I$(bccinc) -w-pia -w-rch -w-csu -w-par -w-aus +cdebug = -y -v -O2 +cvarsmt = -DWIN32 -D_WIN32 -D_MT +lflags = +!IF "$(DEBUG)" == "Y" +linkdebug = /v /m /s +cdebug = -v -y -Q +!ELSE +linkdebug = /C /Gn +cdebug = +!ENDIF +startobj = $(bcclib)\c0x32.obj +!IF "$(GRAPHICAL)" == "Y" +verlflags = /Gn /Gz /q -L$(bcclib) /c /Tpe /V$(APPVER) +startobjg = $(bcclib)\c0w32.obj +!ELSE +verlflags = /Gn /Gz /q -L$(bcclib) /c /ap /Tpe /V$(APPVER) +startobjg = $(startobj) +!ENDIF +libsmt = $(bcclib)\cw32mt.lib $(bcclib)\import32.lib + +# +# Set the gamedir according to your preference. +# It must be present prior to compilation. + +!IF "$(GRAPHICAL)" == "Y" +GAME = NetHackW # Game Name +!ELSE +GAME = NetHack # Game Name +!ENDIF +GAMEDIR = ..\binary # Game directory + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = ..\include # NetHack include files +DAT = ..\dat # NetHack data files +DOC = ..\doc # NetHack documentation files +UTIL = ..\util # Utility source +SRC = ..\src # Main source +SSYS = ..\sys\share # Shared system files +NTSYS = ..\sys\winnt # NT Win32 specific files +TTY = ..\win\tty # window port files (tty) +WIN32 = ..\win\win32 # window port files (Win32) +WSHR = ..\win\share # Tile support files + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), uncomment the upper two macros. +# + +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +!IFNDEF DO_YACC +DO_YACC = YACC_MSG +!ENDIF +!IFNDEF DO_LEX +DO_LEX = LEX_MSG +!ENDIF + +# Wilbur Streett's Win32 ports of GNU bison and flex are available at: +# http://www.monmouth.com/~wstreett/lex-yacc/lex-yacc.html +# +# To use them, download the executables and templates (bison.simple, +# bison.hairy) to some directory, and set the environment variables +# BISON_SIMPLE and BISON_HAIRY, and your path to point to this +# directory. +# +# For example, if you placed them in C:\BIN, you should set: +# C:> SET BISON_SIMPLE=C:\BIN\BISON.SIMPLE +# C:> SET BISON_HAIRY=C:\BIN\BISON.HAIRY +# +# Also, make sure your path points to the bison/flex directories. +# +# The following settings are configured for Wilbur Streett's ports. + +# - Specify your yacc and lex programs (or work-alikes) here. + +YACC = bison -y +#YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +#YTABC = y.tab.c +#YTABH = y.tab.h +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lex.yy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# Compiler and Linker flags +# + +PRECOMPHEAD = N # set to Y if you want to use precomp. headers + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +!IF "$(GRAPHICAL)" == "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPHDR = $(WIN32)\mhaskyn.h $(WIN32)\mhdlg.h $(WIN32)\mhfont.h \ + $(WIN32)\mhinput.h $(WIN32)\mhmain.h $(WIN32)\mhmap.h $(WIN32)\mhmenu.h \ + $(WIN32)\mhmsg.h $(WIN32)\mhmsgwnd.h $(WIN32)\mhrip.h $(WIN32)\mhstatus.h \ + $(WIN32)\mhtext.h $(WIN32)\resource.h $(WIN32)\winMS.h +WINDLLS = +WINPFLAG= -DTILES -DMSWIN_GRAPHICS +NHRES = $(O)winhack.res +WINPINC = -I$(WIN32) +!ELSE +WINPORT = $(O)nttty.o +WINPHDR = +WINDLLS = $(GAMEDIR)\nhdefkey.dll $(GAMEDIR)\nh340key.dll $(GAMEDIR)\nhraykey.dll +WINPFLAG= -DWIN32CON +NHRES = $(O)console.res +WINPINC = +!ENDIF + +TILEUTIL16 = $(UTIL)\tile2bmp.exe +TILEBMP16 = $(SRC)\tiles.bmp + +TILEUTIL32 = $(UTIL)\til2bm32.exe +TILEBMP32 = $(SRC)\tiles32.bmp + +SOUND = $(OBJ)\ntsound.o +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = -DDLB +! ELSE +DLBFLG = +! ENDIF + + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) $(cvarsmt) -I$(INCL) $(WINPINC) -q $(cdebug) -v +LFLAGSBASE = $(linkdebug) $(verlflags) -L$(bcclib) -v + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASE) + +#========================================== +# - Game build +#========================================== + +LFLAGSBASE = $(linkdebug) $(conflags) +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +NHLFLAGS1 = /Gn /v /m /s /Gz /q /c +lflags = $(LFLAGSBASE) $(NHLFLAGS1) + +GAMEFILE = $(FDIR)\$(GAME).exe # whole thing + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys\share +#========================================== + +{$(SSYS)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys\winnt +#========================================== + +{$(NTSYS)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(NTSYS)}.h{$(INCL)}.h: + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +{$(UTIL)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGSU) -o$@ $< + +#========================================== +# Rules for files in win\share +#========================================== + +{$(WSHR)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win\tty +#========================================== + +{$(TTY)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in win\win32 +#========================================== + +{$(WIN32)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)\$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)^\ + +U = $(UTIL)^\ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)pcmain.o $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +TILOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(TILOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + + +!IF "$(GRAPHICAL)" == "Y" +OPTIONS_FILE = $(DAT)\guioptions +!ELSE +OPTIONS_FILE = $(DAT)\ttyoptions +!ENDIF +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h \ + $(INCL)\global.h $(INCL)\coord.h $(INCL)\vmsconf.h \ + $(INCL)\system.h $(INCL)\unixconf.h $(INCL)\os2conf.h \ + $(INCL)\micro.h $(INCL)\pcconf.h $(INCL)\tosconf.h \ + $(INCL)\amiconf.h $(INCL)\macconf.h $(INCL)\beconf.h \ + $(INCL)\ntconf.h $(INCL)\nhlan.h + +HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\align.h \ + $(INCL)\dungeon.h $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\youprop.h $(INCL)\prop.h \ + $(INCL)\permonst.h $(INCL)\monattk.h \ + $(INCL)\monflag.h $(INCL)\mondata.h $(INCL)\pm.h \ + $(INCL)\wintype.h $(INCL)\decl.h $(INCL)\quest.h \ + $(INCL)\spell.h $(INCL)\color.h $(INCL)\obj.h \ + $(INCL)\you.h $(INCL)\attrib.h $(INCL)\monst.h \ + $(INCL)\skills.h $(INCL)\onames.h $(INCL)\timeout.h \ + $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\display.h $(INCL)\engrave.h \ + $(INCL)\rect.h $(INCL)\region.h $(INCL)\winprocs.h \ + $(INCL)\wintty.h $(INCL)\trampoli.h + +LEV_H = $(INCL)\lev.h +DGN_FILE_H = $(INCL)\dgn_file.h +LEV_COMP_H = $(INCL)\lev_comp.h +SP_LEV_H = $(INCL)\sp_lev.h +TILE_H = ..\win\share\tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)\data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +#========================================== +#=============== TARGETS ================== +#========================================== + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME): $(O)obj.tag $(O)utility.tag graphicschk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: graphicschk $(GAME) $(O)install.tag + @echo Done. + +$(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) + copy $(DAT)\opthelp $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + if exist $(DOC)\guidebook.txt copy $(DOC)\guidebook.txt $(GAMEDIR)\Guidebook.txt + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt + @if exist $(SRC)\$(GAME).PDB copy $(SRC)\$(GAME).pdb $(GAMEDIR)\$(GAME).pdb + @if exist $(GAMEDIR)\$(GAME).PDB echo NOTE: You may want to remove $(GAMEDIR)\$(GAME).pdb to conserve space + -copy $(NTSYS)\defaults.nh $(GAMEDIR)\defaults.nh + echo install done > $@ + +# copy $(NTSYS)\winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)\bigroom.des $(DAT)\castle.des \ + $(DAT)\endgame.des $(DAT)\gehennom.des $(DAT)\knox.des \ + $(DAT)\medusa.des $(DAT)\oracle.des $(DAT)\tower.des \ + $(DAT)\yendor.des $(DAT)\arch.des $(DAT)\barb.des \ + $(DAT)\caveman.des $(DAT)\healer.des $(DAT)\knight.des \ + $(DAT)\monk.des $(DAT)\priest.des $(DAT)\ranger.des \ + $(DAT)\rogue.des $(DAT)\samurai.des $(DAT)\sokoban.des \ + $(DAT)\tourist.des $(DAT)\valkyrie.des $(DAT)\wizard.des + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) + echo sp_levs done > $(O)sp_lev.tag + +$(O)utility.tag: $(INCL)\date.h $(INCL)\onames.h $(INCL)\pm.h \ + $(SRC)\monstr.c $(SRC)\vis_tab.c \ + $(U)lev_comp.exe $(INCL)\vis_tab.h \ + $(U)dgn_comp.exe $(TILEUTIL16) + @echo utilities made >$@ + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +!IF "$(GRAPHICAL)"=="Y" +$(NHRES): $(TILEBMP16) $(WIN32)\winhack.rc $(WIN32)\mnsel.bmp \ + $(WIN32)\mnselcnt.bmp $(WIN32)\mnunsel.bmp \ + $(WIN32)\petmark.bmp $(WIN32)\NetHack.ico $(WIN32)\rip.bmp \ + $(WIN32)\splash.bmp + @$(rc) -r -fo$@ -i$(WIN32) -i$(bccinc) -dNDEBUG $(WIN32)\winhack.rc +!ELSE +$(NHRES): $(NTSYS)\console.rc $(NTSYS)\NetHack.ico + @$(rc) -r -fo$@ -i$(NTSYS) -i$(bccinc) -dNDEBUG $(NTSYS)\console.rc +!ENDIF + +#========================================== +# The main target. +#========================================== + +$(SRC)\uuid.lib: $(bcclib)\uuid.lib + @copy $(bcclib)\uuid.lib $@ + +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(SRC)\uuid.lib $(O)gamedir.tag $(WINDLLS) + @echo Linking.... + @$(link) $(lflags) $(startobjg) $(ALLOBJ), $@, $(GAME).map,$(libsmt),,$(NHRES) + @if exist $(O)install.tag del $(O)install.tag + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +$(O)gamedir.tag: + @if not exist $(GAMEDIR)\*.* echo creating directory $(GAMEDIR) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo directory created > $@ + +$(GAME)_.ico : $(NTSYS)\$(GAME).ico + @copy $(NTSYS)\$(GAME).ico $@ + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + @if not exist $(O)*.* mkdir $(OBJ) + @echo directory $(OBJ) created >$@ + +#========================================== +# Notify of any CL environment variables +# in effect since they change the compiler +# options. +#========================================== + +graphicschk: +! IF "$(GRAPHICAL)"=="Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +! ENDIF + @echo graphicschk > graphicschk + + +$(GAMEDIR)\nhdefkey.dll : $(O)nhdefkey.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nhdefkey.def + @echo ProcessKeystroke >>nhdefkey.def + @echo NHkbhit >>nhdefkey.def + @echo CheckInput >>nhdefkey.def + @echo SourceWhere >>nhdefkey.def + @echo SourceAuthor >>nhdefkey.def + @echo KeyHandlerName >>nhdefkey.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nhdefkey.o, $@,nhdefkey.map,$(libsmt),nhdefkey.def + +$(GAMEDIR)\nh340key.dll : $(O)nh340key.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nh340key.def + @echo ProcessKeystroke >>nh340key.def + @echo NHkbhit >>nh340key.def + @echo CheckInput >>nh340key.def + @echo SourceWhere >>nh340key.def + @echo SourceAuthor >>nh340key.def + @echo KeyHandlerName >>nh340key.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nh340key.o, $@,nh340key.map,$(libsmt),nh340key.def + +$(GAMEDIR)\nhraykey.dll : $(O)nhraykey.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nhraykey.def + @echo ProcessKeystroke >>nhraykey.def + @echo NHkbhit >>nhraykey.def + @echo CheckInput >>nhraykey.def + @echo SourceWhere >>nhraykey.def + @echo SourceAuthor >>nhraykey.def + @echo KeyHandlerName >>nhraykey.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nhraykey.o, $@,nhraykey.map,$(libsmt),nhraykey.def + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(O)obj.tag $(MAKEOBJS) $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(MAKEOBJS), $@,,$(libsmt) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(INCL)\patchlevel.h \ + $(U)makedefs.c + @$(cc) $(CFLAGSU) -o$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe + $(U)makedefs -v + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) $(startobj) $(O)uudecode.o, $@,,$(libsmt) + +$(O)uudecode.o: $(SSYS)\uudecode.c + +$(NTSYS)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(NTSYS) + ..\..\util\uudecode.exe nhico.uu + chdir ..\..\src + +$(WIN32)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(WIN32) + ..\..\util\uudecode.exe ../../sys/winnt/nhico.uu + chdir ..\..\src + +$(WIN32)\mnsel.bmp: $(U)uudecode.exe $(WIN32)\mnsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnsel.uu + chdir ..\..\src + +$(WIN32)\mnselcnt.bmp: $(U)uudecode.exe $(WIN32)\mnselcnt.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnselcnt.uu + chdir ..\..\src + +$(WIN32)\mnunsel.bmp: $(U)uudecode.exe $(WIN32)\mnunsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnunsel.uu + chdir ..\..\src + +$(WIN32)\petmark.bmp: $(U)uudecode.exe $(WIN32)\petmark.uu + chdir $(WIN32) + ..\..\util\uudecode.exe petmark.uu + chdir ..\..\src + +$(WIN32)\rip.bmp: $(U)uudecode.exe $(WIN32)\rip.uu + chdir $(WIN32) + ..\..\util\uudecode.exe rip.uu + chdir ..\..\src + +$(WIN32)\splash.bmp: $(U)uudecode.exe $(WIN32)\splash.uu + chdir $(WIN32) + ..\..\util\uudecode.exe splash.uu + chdir ..\..\src + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=$(cflags) -DWIN32 -D_WIN32 -D_MT -I..\include $(cdebug) -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(SPLEVOBJS), $@,,$(libsmt) + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + @$(cc) $(LEVCFLAGS) -D__IO_H -o$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + @$(cc) $(LEVCFLAGS) -o$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d lev_comp.y + copy $(YTABC) lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\lev_yacc.c $(U)lev_yacc.c >nul + @copy $(SSYS)\lev_comp.h $(INCL)\lev_comp.h >nul + @echo /**/ >>$(U)lev_yacc.c + @echo /**/ >>$(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\lev_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(DGNCOMPOBJS), $@,,$(libsmt) + + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h $(U)dgn_yacc.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + @$(cc) $(LEVCFLAGS) -D__IO_H -o$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d dgn_comp.y + copy $(YTABC) dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\dgn_yacc.c $(U)dgn_yacc.c >nul + @copy $(SSYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + @echo /**/ >>$(U)dgn_yacc.c + @echo /**/ >>$(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\dgn_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ..\include +#=========================================== + +$(INCL)\win32api.h: $(NTSYS)\win32api.h + copy $(NTSYS)\win32api.h $@ + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o, $@,,$(libsmt) + + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)\dlb.h + @$(cc) $(CFLAGS) -o$@ $(SRC)\dlb.c + +$(O)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h + @$(cc) $(CFLAGS) -o$@ $(UTIL)\dlb_main.c + +$(DAT)\porthelp: $(NTSYS)\porthelp + @copy $(NTSYS)\porthelp $@ >nul + +nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon $(DAT)\porthelp \ + $(DAT)\license $(O)sp_lev.tag + cd $(DAT) + echo data >dlb.lst + echo oracles >>dlb.lst + if exist options echo options >>dlb.lst + if exist ttyoptions echo ttyoptions >>dlb.lst + if exist guioptions echo guioptions >>dlb.lst + if exist porthelp echo porthelp >>dlb.lst + echo quest.dat >>dlb.lst + echo rumors >>dlb.lst + echo help >>dlb.lst + echo hh >>dlb.lst + echo cmdhelp >>dlb.lst + echo history >>dlb.lst + echo opthelp >>dlb.lst + echo wizhelp >>dlb.lst + echo dungeon >>dlb.lst + echo license >>dlb.lst + for %N in (*.lev) do echo %N >>dlb.lst + $(U)dlb_main cIf dlb.lst $(SRC)\nhdat + cd $(SRC) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) $(SRC)\uuid.lib + $(link) $(LFLAGSU) $(startobj) $(RECOVOBJS), $@,,$(libsmt) + + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)\win32api.h + $(cc) $(CFLAGSU) -o$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)\tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(O)tilemap.o, $@,,$(libsmt) + + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + @$(cc) $(CFLAGSU) -o$@ $(WSHR)\tilemap.c + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + @$(cc) $(CFLAGS) /DTILETEXT -o$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(GIFREADERS) $(TEXT_IO), $@,,$(libsmt) + + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(PPMWRITERS) $(TEXT_IO), $@,,$(libsmt) + + +!IF "$(GRAPHICAL)"=="Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + @$(U)tile2bmp $(TILEBMP16) +!ENDIF + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(O)tile2bmp.o $(TEXT_IO), $@,,$(libsmt) + + +$(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(cc) $(CFLAGS) -I$(WSHR) /DPACKED_FILE -o$@ $(WSHR)\tile2bmp.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean +! IF ("$(OBJ)"!="") + -rmdir $(OBJ) /s /Q +! ENDIF + if exist $(INCL)\date.h del $(INCL)\date.h + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(U)*.lnk del $(U)*.lnk + if exist $(U)*.map del $(U)*.map + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\???-fil?.lev del $(DAT)\???-fil?.lev + if exist $(DAT)\???-goal.lev del $(DAT)\???-goal.lev + if exist $(DAT)\???-loca.lev del $(DAT)\???-loca.lev + if exist $(DAT)\???-strt.lev del $(DAT)\???-strt.lev + if exist $(DAT)\air.lev del $(DAT)\air.lev + if exist $(DAT)\asmodeus.lev del $(DAT)\asmodeus.lev + if exist $(DAT)\astral.lev del $(DAT)\astral.lev + if exist $(DAT)\baalz.lev del $(DAT)\baalz.lev + if exist $(DAT)\bigroom.lev del $(DAT)\bigroom.lev + if exist $(DAT)\castle.lev del $(DAT)\castle.lev + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\dungeon.pdf del $(DAT)\dungeon.pdf + if exist $(DAT)\earth.lev del $(DAT)\earth.lev + if exist $(DAT)\fakewiz?.lev del $(DAT)\fakewiz?.lev + if exist $(DAT)\fire.lev del $(DAT)\fire.lev + if exist $(DAT)\juiblex.lev del $(DAT)\juiblex.lev + if exist $(DAT)\knox.lev del $(DAT)\knox.lev + if exist $(DAT)\medusa-?.lev del $(DAT)\medusa-?.lev + if exist $(DAT)\mine*.lev del $(DAT)\mine*.lev + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\ttyoptions del $(DAT)\ttyoptions + if exist $(DAT)\guioptions del $(DAT)\guioptions + if exist $(DAT)\oracle.lev del $(DAT)\oracle.lev + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\orcus.lev del $(DAT)\orcus.lev + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(DAT)\sanctum.lev del $(DAT)\sanctum.lev + if exist $(DAT)\soko?-?.lev del $(DAT)\soko?-?.lev + if exist $(DAT)\tower?.lev del $(DAT)\tower?.lev + if exist $(DAT)\valley.lev del $(DAT)\valley.lev + if exist $(DAT)\water.lev del $(DAT)\water.lev + if exist $(DAT)\wizard?.lev del $(DAT)\wizard?.lev + if exist $(O)sp_lev.tag del $(O)sp_lev.tag + if exist $(SRC)\monstr.c del $(SRC)\monstr.c + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(U)recover.exe del $(U)recover.exe + if exist nhdat. del nhdat. + +clean: + if exist $(O)*.o del $(O)*.o + if exist $(O)utility.tag del $(O)utility.tag + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(SRC)\*.lnk del $(SRC)\*.lnk + if exist $(SRC)\*.map del $(SRC)\*.map + if exist $(TILEBMP16) del $(TILEBMP16) + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)\data: $(O)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(O)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(O)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(O)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nttty.c + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(NTSYS)\nttty.c +$(O)winnt.o: $(HACK_H) $(INCL)\win32api.h $(NTSYS)\winnt.c + @$(cc) $(CFLAGS) -o$@ $(NTSYS)\winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)\ntsound.c + @$(cc) $(CFLAGS) -o$@ $(NTSYS)\ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)\nhlan.h $(NTSYS)\mapimail.c + @$(cc) $(CFLAGS) -DMAPI_VERBOSE -o$@ $(NTSYS)\mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + @$(cc) $(CFLAGS) -o$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * slashes changed to back-slashes +# * -c (which is included in CFLAGS) substituted +# with -o$@ +# * $(CC) changed to $(cc) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\sys\atari\tos.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\win32api.h + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcmain.c +$(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcsys.c +$(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pctty.c +$(O)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcunix.c +$(O)random.o: ..\sys\share\random.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\random.c +$(O)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\sys\share\ioctl.c +$(O)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\unixtty.c +$(O)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h + $(cc) $(CFLAGS) -o$@ ..\sys\unix\unixmain.c +$(O)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\unix\unixunix.c +$(O)bemain.o: ..\sys\be\bemain.c $(HACK_H) $(INCL)\dlb.h + $(cc) $(CFLAGS) -o$@ ..\sys\be\bemain.c +$(O)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\getline.c +$(O)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\termcap.c +$(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\topl.c +$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\wintty.c +$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ..\win\X11\Window.c +$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ..\win\X11\dialogs.c +$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h ..\win\X11\nh72icon \ + ..\win\X11\nh56icon ..\win\X11\nh32icon + $(cc) $(CFLAGS) -o$@ ..\win\X11\winX.c +$(O)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmap.c +$(O)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmenu.c +$(O)winmesg.o: ..\win\X11\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmesg.c +$(O)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmisc.c +$(O)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winstat.c +$(O)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\wintext.c +$(O)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winval.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)gnaskstr.o: ..\win\gnome\gnaskstr.c ..\win\gnome\gnaskstr.h \ + ..\win\gnome\gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnaskstr.c +$(O)gnbind.o: ..\win\gnome\gnbind.c ..\win\gnome\gnbind.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnaskstr.h ..\win\gnome\gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnbind.c +$(O)gnglyph.o: ..\win\gnome\gnglyph.c ..\win\gnome\gnglyph.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnglyph.c +$(O)gnmain.o: ..\win\gnome\gnmain.c ..\win\gnome\gnmain.h ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnbind.h ..\win\gnome\gnopts.h $(HACK_H) \ + $(INCL)\date.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmain.c +$(O)gnmap.o: ..\win\gnome\gnmap.c ..\win\gnome\gnmap.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmap.c +$(O)gnmenu.o: ..\win\gnome\gnmenu.c ..\win\gnome\gnmenu.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnbind.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmenu.c +$(O)gnmesg.o: ..\win\gnome\gnmesg.c ..\win\gnome\gnmesg.h ..\win\gnome\gnsignal.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmesg.c +$(O)gnopts.o: ..\win\gnome\gnopts.c ..\win\gnome\gnopts.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnmain.h ..\win\gnome\gnmap.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnopts.c +$(O)gnplayer.o: ..\win\gnome\gnplayer.c ..\win\gnome\gnplayer.h \ + ..\win\gnome\gnmain.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnplayer.c +$(O)gnsignal.o: ..\win\gnome\gnsignal.c ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnsignal.c +$(O)gnstatus.o: ..\win\gnome\gnstatus.c ..\win\gnome\gnstatus.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gn_xpms.h \ + ..\win\gnome\gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnstatus.c +$(O)gntext.o: ..\win\gnome\gntext.c ..\win\gnome\gntext.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gn_rip.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gntext.c +$(O)gnyesno.o: ..\win\gnome\gnyesno.c ..\win\gnome\gnbind.h ..\win\gnome\gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnyesno.c +$(O)wingem.o: ..\win\gem\wingem.c $(HACK_H) $(INCL)\func_tab.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\wingem.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\wingem.c +$(O)wingem1.o: ..\win\gem\wingem1.c $(INCL)\gem_rsc.h $(INCL)\load_img.h \ + $(INCL)\wintype.h $(INCL)\wingem.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\wingem1.c +$(O)load_img.o: ..\win\gem\load_img.c $(INCL)\load_img.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\load_img.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\dlb.h $(INCL)\patchlevel.h $(INCL)\qt_win.h \ + $(INCL)\qt_clust.h $(INCL)\qt_kde0.h \ + $(INCL)\qt_xpms.h qt_win.moc qt_kde0.moc + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_win.cpp +$(O)qt_clust.o: ..\win\Qt\qt_clust.cpp $(INCL)\qt_clust.h + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_clust.cpp +$(O)monstr.o: $(SRC)\monstr.c $(CONFIG_H) +$(O)vis_tab.o: $(SRC)\vis_tab.c $(CONFIG_H) $(INCL)\vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)\edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +$(O)attrib.o: attrib.c $(HACK_H) $(INCL)\artifact.h +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)\lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)\edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)\lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)\edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)\eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)\edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)\tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)\lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)\dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) $(INCL)\artifact.h +$(O)light.o: light.c $(HACK_H) $(INCL)\lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)\mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) $(INCL)\artifact.h +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)\edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)\lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)\epri.h $(INCL)\edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)\epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)\qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)\lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)\lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)\eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)\eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)\edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)\lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)\vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)\lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file + diff --git a/src/Makefile.gcc b/src/Makefile.gcc new file mode 100644 index 0000000..980c2c2 --- /dev/null +++ b/src/Makefile.gcc @@ -0,0 +1,1352 @@ +# SCCS Id: @(#)Makefile.gcc 3.4 $Date: 2003/11/16 04:50:57 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# NetHack 3.4.x Makefile for MinGW +# +# Win32 Compilers Tested: +# - MinGW 1.0 (gcc version 2.95.3-6) (Console NetHack only) +# - MinGW 2.0 (gcc version 3.2) +# +# If you don't have this compiler, you can get it at: +# http://www.mingw.org/ +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +# +# -- +# Dion Nicolaas +#============================================================================== +# Graphical interface +# Set to Y for a graphical version +# Set to anything else (or undefine) for a tty version + +#GRAPHICAL = Y + +# Debug +# Set to Y for Debug support (to produce debug information) +# Set to anything else (or undefine) for a "release" version +# You can set your debug options below. + +DEBUG = Y + +cc = gcc +rc = windres +link = gcc + +cflags = -mms-bitfields +lflags = +ifeq "$(DEBUG)" "Y" +cdebug = -g +linkdebug = -g +else +cdebug = +linkdebug = +endif + +# +# Set the gamedir according to your preference. +# If not present prior to compilation it gets created. + +ifeq "$(GRAPHICAL)" "Y" +# Game Name +GAME = NetHackW +else +# Game Name +GAME = NetHack +endif +# Game directory +GAMEDIR = ../binary + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +# NetHack include files +INCL = ../include +# NetHack data files +DAT = ../dat +# NetHack documentation files +DOC = ../doc +# Utility source +UTIL = ../util +# Main source +SRC = ../src +# Shared system files +SSYS = ../sys/share +# NT Win32 specific files +NTSYS = ../sys/winnt +# window port files (tty) +TTY = ../win/tty +# window port files (Win32) +WIN32 = ../win/win32 +# Tile support files +WSHR = ../win/share + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), comment out the upper two macros and uncomment +# the lower two. +# + +DO_YACC = YACC_MSG +DO_LEX = LEX_MSG +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +# - Specify your yacc and lex programs (or work-alikes) here. + +#YACC = bison -y +YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)/random.o +#RANDOM = + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +ifeq "$(GRAPHICAL)" "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPFLAG = -DTILES -DMSWIN_GRAPHICS -D_WIN32_IE=0x0400 +NHRES = $(O)winres.o +WINPINC = -I$(WIN32) +WINPHDR = $(WIN32)/mhaskyn.h $(WIN32)/mhdlg.h $(WIN32)/mhfont.h \ + $(WIN32)/mhinput.h $(WIN32)/mhmain.h $(WIN32)/mhmap.h \ + $(WIN32)/mhmenu.h $(WIN32)/mhmsg.h $(WIN32)/mhmsgwnd.h \ + $(WIN32)/mhrip.h $(WIN32)/mhstatus.h \ + $(WIN32)/mhtext.h $(WIN32)/resource.h $(WIN32)/winMS.h +WINPLIBS = -lcomctl32 -lwinmm +else +WINPORT = $(O)nttty.o +WINPFLAG= -DWIN32CON +WINPHDR = +NHRES = $(O)console.o +WINPINC = +WINPLIBS = -lwinmm +endif + +TILEUTIL16 = $(UTIL)/tile2bmp.exe +TILEBMP16 = $(SRC)/tiles.bmp + +TILEUTIL32 = $(UTIL)/til2bm32.exe +TILEBMP32 = $(SRC)/tiles32.bmp + +SOUND = $(OBJ)/ntsound.o + +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +ifeq "$(USE_DLB)" "Y" +DLBFLG = -DDLB +else +DLBFLG = +endif + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) -I$(INCL) $(WINPINC) $(cdebug) +LFLAGSBASEC = $(linkdebug) +LFLAGSBASEG = $(linkdebug) -mwindows + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASEC) + +#========================================== +# - Game build +#========================================== + +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +lflags = $(LFLAGSBASE) +ifeq "$(GRAPHICAL)" "Y" +lflags = $(LFLAGSBASEG) +else +lflags = $(LFLAGSBASEC) +endif + +GAMEFILE = $(GAMEDIR)/$(GAME).exe # whole thing + +ifeq "$(USE_DLB)" "Y" +DLB = nhdat +else +DLB = +endif + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +$(OBJ)/%.o : /%.c + $(cc) $(CFLAGS) -o$@ $< + +$(OBJ)/%.o : $(SRC)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys/share +#========================================== + +$(OBJ)/%.o : $(SSYS)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys/winnt +#========================================== + +$(OBJ)/%.o : $(NTSYS)/%.c + $(cc) $(CFLAGS) -o$@ $< + +$(INCL)/%.h : $(NTSYS)/%.h + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +$(OBJ)/%.o : $(UTIL)/%.c + $(cc) $(CFLAGSU) -o$@ $< + +#========================================== +# Rules for files in win/share +#========================================== + +$(OBJ)/%.o : $(WSHR)/%.c + $(cc) $(CFLAGS) -o$@ $< + +$(INCL)/%.h : $(WSHR)/%.h + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win/tty +#========================================== + +$(OBJ)/%.o : $(TTY)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in win/win32 +#========================================== + +$(OBJ)/%.o : $(WIN32)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)/$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)/ + +U = $(UTIL)/ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)/monsters.txt $(WSHR)/objects.txt $(WSHR)/other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +TEXT_IO32 = $(O)tilete32.o $(O)tiletx32.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o +GIFREADERS32 = $(O)gifrd32.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)pcmain.o $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +WINPOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(WINPOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + +ifeq "$(GRAPHICAL)" "Y" +OPTIONS_FILE = $(DAT)/guioptions +else +OPTIONS_FILE = $(DAT)/ttyoptions +endif + +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)/config.h $(INCL)/config1.h $(INCL)/tradstdc.h \ + $(INCL)/global.h $(INCL)/coord.h $(INCL)/vmsconf.h \ + $(INCL)/system.h $(INCL)/unixconf.h $(INCL)/os2conf.h \ + $(INCL)/micro.h $(INCL)/pcconf.h $(INCL)/tosconf.h \ + $(INCL)/amiconf.h $(INCL)/macconf.h $(INCL)/beconf.h \ + $(INCL)/ntconf.h $(INCL)/nhlan.h + +HACK_H = $(INCL)/hack.h $(CONFIG_H) $(INCL)/align.h \ + $(INCL)/dungeon.h $(INCL)/monsym.h $(INCL)/mkroom.h \ + $(INCL)/objclass.h $(INCL)/youprop.h $(INCL)/prop.h \ + $(INCL)/permonst.h $(INCL)/monattk.h \ + $(INCL)/monflag.h $(INCL)/mondata.h $(INCL)/pm.h \ + $(INCL)/wintype.h $(INCL)/decl.h $(INCL)/quest.h \ + $(INCL)/spell.h $(INCL)/color.h $(INCL)/obj.h \ + $(INCL)/you.h $(INCL)/attrib.h $(INCL)/monst.h \ + $(INCL)/skills.h $(INCL)/onames.h $(INCL)/timeout.h \ + $(INCL)/trap.h $(INCL)/flag.h $(INCL)/rm.h \ + $(INCL)/vision.h $(INCL)/display.h $(INCL)/engrave.h \ + $(INCL)/rect.h $(INCL)/region.h $(INCL)/winprocs.h \ + $(INCL)/wintty.h $(INCL)/trampoli.h + +LEV_H = $(INCL)/lev.h +DGN_FILE_H = $(INCL)/dgn_file.h +LEV_COMP_H = $(INCL)/lev_comp.h +SP_LEV_H = $(INCL)/sp_lev.h +TILE_H = ../win/share/tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)/data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)/$(GAME).exe + + +#========================================== +#=============== TARGETS ================== +#========================================== + +# Since DOS doesn't allow / as path separator, and GCC doesn't allow \ as +# path separator, we must change all pathnames when performing DOS commands. +# This is done by blindly applying $(subst /,\, ...) on every command. +# Where any command contain / for another reason (switch char, or echoing +# comment lines to lev/dungeon files) a little more care is taken. + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME) : $(O)obj.tag $(O)utility.tag graphicschk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: graphicschk $(GAME) $(O)install.tag + @echo Done. + + +$(O)install.tag: $(DAT)/data $(DAT)/rumors $(DAT)/dungeon \ + $(DAT)/oracles $(DAT)/quest.dat $(O)sp_lev.tag $(DLB) +ifeq "$(USE_DLB)" "Y" + $(subst /,\,copy nhdat $(GAMEDIR)) + $(subst /,\,copy $(DAT)/license $(GAMEDIR)) + $(subst /,\,copy $(DAT)/opthelp $(GAMEDIR)) +else + $(subst /,\,copy $(DAT)/*. $(GAMEDIR)) + $(subst /,\,copy $(DAT)/*.dat $(GAMEDIR)) + $(subst /,\,copy $(DAT)/*.lev $(GAMEDIR)) + $(subst /,\,if exist $(GAMEDIR)/makefile del $(GAMEDIR)/makefile) +endif + $(subst /,\,if exist $(DOC)/guidebook.txt copy $(DOC)/guidebook.txt $(GAMEDIR)/Guidebook.txt) + $(subst /,\,if exist $(DOC)/nethack.txt copy $(DOC)/nethack.txt $(GAMEDIR)/NetHack.txt) + $(subst /,\,copy $(NTSYS)/defaults.nh $(GAMEDIR)/defaults.nh) + $(subst /,\,echo install done > $@) + +# copy $(NTSYS)/winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + $(subst /,\,if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR)) + $(subst /,\,if exist $(DOC)/recover.txt copy $(DOC)/recover.txt $(GAMEDIR)/recover.txt) + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)/bigroom.des $(DAT)/castle.des \ + $(DAT)/endgame.des $(DAT)/gehennom.des $(DAT)/knox.des \ + $(DAT)/medusa.des $(DAT)/oracle.des $(DAT)/tower.des \ + $(DAT)/yendor.des $(DAT)/arch.des $(DAT)/barb.des \ + $(DAT)/caveman.des $(DAT)/healer.des $(DAT)/knight.des \ + $(DAT)/monk.des $(DAT)/priest.des $(DAT)/ranger.des \ + $(DAT)/rogue.des $(DAT)/samurai.des $(DAT)/sokoban.des \ + $(DAT)/tourist.des $(DAT)/valkyrie.des $(DAT)/wizard.des + $(subst /,\,$(U)lev_comp $(DAT)/bigroom.des) + $(subst /,\,$(U)lev_comp $(DAT)/castle.des) + $(subst /,\,$(U)lev_comp $(DAT)/endgame.des) + $(subst /,\,$(U)lev_comp $(DAT)/gehennom.des) + $(subst /,\,$(U)lev_comp $(DAT)/knox.des) + $(subst /,\,$(U)lev_comp $(DAT)/mines.des) + $(subst /,\,$(U)lev_comp $(DAT)/medusa.des) + $(subst /,\,$(U)lev_comp $(DAT)/oracle.des) + $(subst /,\,$(U)lev_comp $(DAT)/sokoban.des) + $(subst /,\,$(U)lev_comp $(DAT)/tower.des) + $(subst /,\,$(U)lev_comp $(DAT)/yendor.des) + $(subst /,\,$(U)lev_comp $(DAT)/arch.des) + $(subst /,\,$(U)lev_comp $(DAT)/barb.des) + $(subst /,\,$(U)lev_comp $(DAT)/caveman.des) + $(subst /,\,$(U)lev_comp $(DAT)/healer.des) + $(subst /,\,$(U)lev_comp $(DAT)/knight.des) + $(subst /,\,$(U)lev_comp $(DAT)/monk.des) + $(subst /,\,$(U)lev_comp $(DAT)/priest.des) + $(subst /,\,$(U)lev_comp $(DAT)/ranger.des) + $(subst /,\,$(U)lev_comp $(DAT)/rogue.des) + $(subst /,\,$(U)lev_comp $(DAT)/samurai.des) + $(subst /,\,$(U)lev_comp $(DAT)/tourist.des) + $(subst /,\,$(U)lev_comp $(DAT)/valkyrie.des) + $(subst /,\,$(U)lev_comp $(DAT)/wizard.des) + $(subst /,\,copy *.lev $(DAT)) + $(subst /,\,del *.lev) + $(subst /,\,echo sp_levs done > $(O)sp_lev.tag) + +$(O)utility.tag: $(INCL)/date.h $(INCL)/onames.h $(INCL)/pm.h \ + $(SRC)/monstr.c $(SRC)/vis_tab.c $(U)lev_comp.exe $(INCL)/vis_tab.h \ + $(U)dgn_comp.exe $(TILEUTIL16) + $(subst /,\,@echo utilities made >$@) + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +ifeq "$(GRAPHICAL)" "Y" +$(NHRES): $(TILEBMP16) $(WIN32)/winhack.rc $(WIN32)/mnsel.bmp \ + $(WIN32)/mnselcnt.bmp $(WIN32)/mnunsel.bmp \ + $(WIN32)/petmark.bmp $(WIN32)/NetHack.ico $(WIN32)/rip.bmp \ + $(WIN32)/splash.bmp + @$(rc) -o$@ --include-dir $(WIN32) -i $(WIN32)/winhack.rc +else +$(NHRES): $(NTSYS)/console.rc $(NTSYS)/NetHack.ico + @$(rc) -o$@ --include-dir $(NTSYS) -i $(NTSYS)/console.rc +endif + +#========================================== +# The main target. +#========================================== +$(O)gamedir.tag: + $(subst /,\,@if not exist $(GAMEDIR)/*.* echo creating directory $(GAMEDIR)) + $(subst /,\,@if not exist $(GAMEDIR)/*.* mkdir $(GAMEDIR)) + $(subst /,\,@echo directory created > $@) + +ifeq "$(GRAPHICAL)" "Y" +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag +else +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag \ + $(GAMEDIR)/nhdefkey.dll $(GAMEDIR)/nh340key.dll $(GAMEDIR)/nhraykey.dll +endif + @echo Linking.... + @$(link) $(lflags) -o$@ $(ALLOBJ) $(NHRES) $(WINPLIBS) + $(subst /,\,@if exist $(O)install.tag del $(O)install.tag) + + +$(O)nhdefkey.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nhdefkey.c + +$(GAMEDIR)/nhdefkey.dll : $(O)nhdefkey.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(O)nh340key.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nh340key.c + +$(GAMEDIR)/nh340key.dll : $(O)nh340key.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(O)nhraykey.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nhraykey.c + +$(GAMEDIR)/nhraykey.dll : $(O)nhraykey.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(GAME)_.ico : $(NTSYS)/$(GAME).ico + $(subst /,\,@copy $(NTSYS)/$(GAME).ico $@) + +#========================================== +# Create directory for holding object files +#========================================== + +graphicschk: +ifeq "$(GRAPHICAL)" "Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +endif + $(subst /,\,@echo graphicschk > graphicschk) + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + @$(link) $(LFLAGSU) -o$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)/monattk.h $(INCL)/monflag.h \ + $(INCL)/objclass.h $(INCL)/monsym.h $(INCL)/qtext.h \ + $(INCL)/patchlevel.h $(U)makedefs.c $(O)obj.tag + $(cc) $(CFLAGSU) -o$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)/date.h $(OPTIONS_FILE): $(U)makedefs.exe + $(subst /,\,$(U)makedefs -v) + +#$(OPTIONS_FILE): $(U)makedefs.exe +# $(subst /,\,$(U)makedefs -v) + +$(INCL)/onames.h : $(U)makedefs.exe + $(subst /,\,$(U)makedefs -o) + +$(INCL)/pm.h : $(U)makedefs.exe + $(subst /,\,$(U)makedefs -p) + +#$(INCL)/trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)/monstr.c: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -m) + +$(INCL)/vis_tab.h: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -z) + +$(SRC)/vis_tab.c: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -z) + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) -o$@ $(O)uudecode.o + +$(O)uudecode.o: $(SSYS)/uudecode.c + +$(NTSYS)/NetHack.ico : $(U)uudecode.exe $(NTSYS)/nhico.uu + $(subst /,\,$(U)uudecode.exe $(NTSYS)/nhico.uu) + $(subst /,\,copy NetHack.ico $@) + del NetHack.ico + +$(WIN32)/NetHack.ico : $(NTSYS)/NetHack.ico + $(subst /,\,copy $< $@) + +$(WIN32)/mnsel.bmp: $(U)uudecode.exe $(WIN32)/mnsel.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnsel.uu) + $(subst /,\,copy mnsel.bmp $@) + del mnsel.bmp + +$(WIN32)/mnselcnt.bmp: $(U)uudecode.exe $(WIN32)/mnselcnt.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnselcnt.uu) + $(subst /,\,copy mnselcnt.bmp $@) + del mnselcnt.bmp + +$(WIN32)/mnunsel.bmp: $(U)uudecode.exe $(WIN32)/mnunsel.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnunsel.uu) + $(subst /,\,copy mnunsel.bmp $@) + del mnunsel.bmp + +$(WIN32)/petmark.bmp: $(U)uudecode.exe $(WIN32)/petmark.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/petmark.uu) + $(subst /,\,copy petmark.bmp $@) + del petmark.bmp + +$(WIN32)/rip.bmp: $(U)uudecode.exe $(WIN32)/rip.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/rip.uu) + $(subst /,\,copy rip.bmp $@) + del rip.bmp + +$(WIN32)/splash.bmp: $(U)uudecode.exe $(WIN32)/splash.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/splash.uu) + $(subst /,\,copy splash.bmp $@) + del splash.bmp + + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=$(cflags) -c -DWIN32 -D_WIN32 -I../include $(cdebug) -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(SPLEVOBJS) + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)/lev_comp.h $(U)lev_yacc.c + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)/lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)/lev_comp.h : $(U)lev_comp.y +ifeq "$(DO_YACC)" "YACC_ACT" + $(subst /,\,$(YACC) -d $(U)lev_comp.y) + $(subst /,\,copy $(YTABC) $(U)lev_yacc.c) + $(subst /,\,copy $(YTABH) $(INCL)/lev_comp.h) + $(subst /,\,@del $(YTABC)) + $(subst /,\,@del $(YTABH)) + +else + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)/lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + $(subst /,\,@copy $(SSYS)/lev_yacc.c $(U)lev_yacc.c >nul) + $(subst /,\,@copy $(SSYS)/lev_comp.h $(INCL)/lev_comp.h >nul) + $(subst /,\,echo.>>$(U)lev_yacc.c) + $(subst /,\,echo.>>$(INCL)/lev_comp.h) +endif + +$(U)lev_$(LEX).c: $(U)lev_comp.l +ifeq "$(DO_LEX)" "LEX_ACT" + $(subst /,\,$(LEX) $(FLEXSKEL) $(U)lev_comp.l) + $(subst /,\,copy $(LEXYYC) $@) + $(subst /,\,@del $(LEXYYC)) +else + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + $(subst /,\,@copy $(SSYS)/lev_lex.c $@ >nul) + $(subst /,\,echo.>>$@) +endif + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(DGNCOMPOBJS) + + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)/dgn_comp.h $(U)dgn_yacc.c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)/dgn_comp.h \ + $(U)dgn_$(LEX).c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)/dgn_comp.h : $(U)dgn_comp.y +ifeq "$(DO_YACC)" "YACC_ACT" + $(subst /,\,$(YACC) -d $(U)dgn_comp.y) + $(subst /,\,copy $(YTABC) $(U)dgn_yacc.c) + $(subst /,\,copy $(YTABH) $(INCL)/dgn_comp.h) + $(subst /,\,@del $(YTABC)) + $(subst /,\,@del $(YTABH)) +else + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)/dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + $(subst /,\,@copy $(SSYS)/dgn_yacc.c $(U)dgn_yacc.c >nul) + $(subst /,\,@copy $(SSYS)/dgn_comp.h $(INCL)/dgn_comp.h >nul) + $(subst /,\,echo.>>$(U)dgn_yacc.c) + $(subst /,\,echo.>>$(INCL)/dgn_comp.h) +endif + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +ifeq "$(DO_LEX)" "LEX_ACT" + $(subst /,\,$(LEX) $(FLEXSKEL) $(U)dgn_comp.l) + $(subst /,\,copy $(LEXYYC) $@) + $(subst /,\,@del $(LEXYYC)) +else + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + $(subst /,\,@copy $(SSYS)/dgn_lex.c $@ >nul) + $(subst /,\,echo.>>$@) +endif + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + $(subst /,\,@if not exist $(OBJ)/*.* echo creating directory $(OBJ)) + $(subst /,\,@if not exist $(OBJ)/*.* mkdir $(OBJ)) + $(subst /,\,@echo directory created > $@) + + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ../include +#=========================================== + +$(INCL)/win32api.h: $(NTSYS)/win32api.h + $(subst /,\,copy $(NTSYS)/win32api.h $@) + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o + @$(link) $(LFLAGSU) -o$@ $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o + + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ $(SRC)/dlb.c + +$(O)dlb_main.o: $(UTIL)/dlb_main.c $(INCL)/config.h $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ $(UTIL)/dlb_main.c + +$(DAT)/porthelp: $(NTSYS)/porthelp + $(subst /,\,@copy $(NTSYS)/porthelp $@ >nul) + +nhdat: $(U)dlb_main.exe $(DAT)/data $(DAT)/oracles $(OPTIONS_FILE) \ + $(DAT)/quest.dat $(DAT)/rumors $(DAT)/help $(DAT)/hh $(DAT)/cmdhelp \ + $(DAT)/history $(DAT)/opthelp $(DAT)/wizhelp $(DAT)/dungeon \ + $(DAT)/porthelp $(DAT)/license $(O)sp_lev.tag + $(subst /,\,echo data >$(DAT)/dlb.lst) + $(subst /,\,echo oracles >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/options echo options >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/ttyoptions echo ttyoptions >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/guioptions echo guioptions >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/porthelp echo porthelp >>$(DAT)/dlb.lst) + $(subst /,\,echo quest.dat >>$(DAT)/dlb.lst) + $(subst /,\,echo rumors >>$(DAT)/dlb.lst) + $(subst /,\,echo help >>$(DAT)/dlb.lst) + $(subst /,\,echo hh >>$(DAT)/dlb.lst) + $(subst /,\,echo cmdhelp >>$(DAT)/dlb.lst) + $(subst /,\,echo history >>$(DAT)/dlb.lst) + $(subst /,\,echo opthelp >>$(DAT)/dlb.lst) + $(subst /,\,echo wizhelp >>$(DAT)/dlb.lst) + $(subst /,\,echo dungeon >>$(DAT)/dlb.lst) + $(subst /,\,echo license >>$(DAT)/dlb.lst) + dir /l /b /-p $(subst /,\,$(DAT)/*.lev >>$(DAT)/dlb.lst) + $(subst /,\,$(U)dlb_main CcIf $(DAT) dlb.lst $(SRC)/nhdat) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) + $(link) $(LFLAGSU) -o$@ $(RECOVOBJS) + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)/win32api.h + $(cc) $(CFLAGSU) -o$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)/tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + @$(link) $(LFLAGSU) -o$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGSU) -o$@ $(WSHR)/tilemap.c + +$(O)tiletx32.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGS) -DTILETEXT -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/tilemap.c + +$(O)tiletxt.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGS) -DTILETEXT -o$@ $(WSHR)/tilemap.c + +$(O)gifread.o: $(WSHR)/gifread.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/gifread.c + +$(O)gifrd32.o: $(WSHR)/gifread.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/gifread.c + +$(O)ppmwrite.o: $(WSHR)/ppmwrite.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/ppmwrite.c + +$(O)tiletext.o: $(WSHR)/tiletext.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/tiletext.c + +$(O)tilete32.o: $(WSHR)/tiletext.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(GIFREADERS) $(TEXT_IO) + +$(U)gif2tx32.exe: $(GIFREADERS32) $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(GIFREADERS32) $(TEXT_IO32) + + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(PPMWRITERS) $(TEXT_IO) + + +ifeq "$(GRAPHICAL)" "Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + $(subst /,\,@$(U)tile2bmp $(TILEBMP16)) +#$(TILEBMP32): $(TILEUTIL32) $(TILEFILES32) +# @echo Creating 32x32 binary tile files (this may take some time) +# $(subst /,\,@$(U)til2bm32 $(TILEBMP32)) +else +$(TILEBMP16): +$(TILEBMP32): +endif + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(O)tile2bmp.o $(TEXT_IO) + +$(U)til2bm32.exe: $(O)til2bm32.o $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(O)til2bm32.o $(TEXT_IO32) + +$(O)tile2bmp.o: $(WSHR)/tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)/win32api.h + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/tile2bmp.c + +$(O)til2bm32.o: $(WSHR)/til2bm32.c $(HACK_H) $(TILE_H) $(INCL)/win32api.h + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/til2bm32.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean + $(subst /,\,if exist graphicschk del graphicschk) + $(subst /,\,if exist $(INCL)/date.h del $(INCL)/date.h) + $(subst /,\,if exist $(INCL)/onames.h del $(INCL)/onames.h) + $(subst /,\,if exist $(INCL)/pm.h del $(INCL)/pm.h) + $(subst /,\,if exist $(INCL)/vis_tab.h del $(INCL)/vis_tab.h) + $(subst /,\,if exist $(SRC)/vis_tab.c del $(SRC)/vis_tab.c) + $(subst /,\,if exist $(SRC)/tile.c del $(SRC)/tile.c) + $(subst /,\,if exist $(U)*.lnk del $(U)*.lnk) + $(subst /,\,if exist $(U)*.map del $(U)*.map) + $(subst /,\,if exist $(DAT)/data del $(DAT)/data) + $(subst /,\,if exist $(DAT)/rumors del $(DAT)/rumors) + $(subst /,\,if exist $(DAT)/???-fil?.lev del $(DAT)/???-fil?.lev) + $(subst /,\,if exist $(DAT)/???-goal.lev del $(DAT)/???-goal.lev) + $(subst /,\,if exist $(DAT)/???-loca.lev del $(DAT)/???-loca.lev) + $(subst /,\,if exist $(DAT)/???-strt.lev del $(DAT)/???-strt.lev) + $(subst /,\,if exist $(DAT)/air.lev del $(DAT)/air.lev) + $(subst /,\,if exist $(DAT)/asmodeus.lev del $(DAT)/asmodeus.lev) + $(subst /,\,if exist $(DAT)/astral.lev del $(DAT)/astral.lev) + $(subst /,\,if exist $(DAT)/baalz.lev del $(DAT)/baalz.lev) + $(subst /,\,if exist $(DAT)/bigrm-*.lev del $(DAT)/bigrm-*.lev) + $(subst /,\,if exist $(DAT)/castle.lev del $(DAT)/castle.lev) + $(subst /,\,if exist $(DAT)/data del $(DAT)/data) + $(subst /,\,if exist $(DAT)/dungeon del $(DAT)/dungeon) + $(subst /,\,if exist $(DAT)/dungeon.pdf del $(DAT)/dungeon.pdf) + $(subst /,\,if exist $(DAT)/earth.lev del $(DAT)/earth.lev) + $(subst /,\,if exist $(DAT)/fakewiz?.lev del $(DAT)/fakewiz?.lev) + $(subst /,\,if exist $(DAT)/fire.lev del $(DAT)/fire.lev) + $(subst /,\,if exist $(DAT)/juiblex.lev del $(DAT)/juiblex.lev) + $(subst /,\,if exist $(DAT)/knox.lev del $(DAT)/knox.lev) + $(subst /,\,if exist $(DAT)/medusa-?.lev del $(DAT)/medusa-?.lev) + $(subst /,\,if exist $(DAT)/mine*.lev del $(DAT)/mine*.lev) + $(subst /,\,if exist $(DAT)/options del $(DAT)/options) + $(subst /,\,if exist $(DAT)/ttyoptions del $(DAT)/ttyoptions) + $(subst /,\,if exist $(DAT)/guioptions del $(DAT)/guioptions) + $(subst /,\,if exist $(DAT)/oracle.lev del $(DAT)/oracle.lev) + $(subst /,\,if exist $(DAT)/oracles del $(DAT)/oracles) + $(subst /,\,if exist $(DAT)/orcus.lev del $(DAT)/orcus.lev) + $(subst /,\,if exist $(DAT)/rumors del $(DAT)/rumors) + $(subst /,\,if exist $(DAT)/quest.dat del $(DAT)/quest.dat) + $(subst /,\,if exist $(DAT)/sanctum.lev del $(DAT)/sanctum.lev) + $(subst /,\,if exist $(DAT)/soko?-?.lev del $(DAT)/soko?-?.lev) + $(subst /,\,if exist $(DAT)/tower?.lev del $(DAT)/tower?.lev) + $(subst /,\,if exist $(DAT)/valley.lev del $(DAT)/valley.lev) + $(subst /,\,if exist $(DAT)/water.lev del $(DAT)/water.lev) + $(subst /,\,if exist $(DAT)/wizard?.lev del $(DAT)/wizard?.lev) + $(subst /,\,if exist $(O)sp_lev.tag del $(O)sp_lev.tag) + $(subst /,\,if exist $(SRC)/monstr.c del $(SRC)/monstr.c) + $(subst /,\,if exist $(SRC)/vis_tab.c del $(SRC)/vis_tab.c) + $(subst /,\,if exist $(U)recover.exe del $(U)recover.exe) + $(subst /,\,if exist $(DAT)/dlb.lst del $(DAT)/dlb.lst) + $(subst /,\,if exist nhdat. del nhdat.) + $(subst /,\,if exist $(O)install.tag del $(O)install.tag) + $(subst /,\,if exist $(O)obj.tag del $(O)obj.tag) + $(subst /,\,if exist $(O)gamedir.tag del $(O)gamedir.tag) +ifneq "$(OBJ)" "" + $(subst /,\,rmdir $(OBJ)) /s /Q +endif + +clean: + $(subst /,\,if exist $(O)*.o del $(O)*.o) + $(subst /,\,if exist $(O)utility.tag del $(O)utility.tag) + $(subst /,\,if exist $(U)makedefs.exe del $(U)makedefs.exe) + $(subst /,\,if exist $(U)lev_comp.exe del $(U)lev_comp.exe) + $(subst /,\,if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe) + $(subst /,\,if exist $(SRC)/*.lnk del $(SRC)/*.lnk) + $(subst /,\,if exist $(SRC)/*.map del $(SRC)/*.map) + $(subst /,\,if exist $(TILEBMP16) del $(TILEBMP16)) + $(subst /,\,if exist $(TILEBMP32) del $(TILEBMP32)) + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)/data: $(O)utility.tag $(DATABASE) + $(subst /,\,$(U)makedefs -d) + +$(DAT)/rumors: $(O)utility.tag $(DAT)/rumors.tru $(DAT)/rumors.fal + $(subst /,\,$(U)makedefs -r) + +$(DAT)/quest.dat: $(O)utility.tag $(DAT)/quest.txt + $(subst /,\,$(U)makedefs -q) + +$(DAT)/oracles: $(O)utility.tag $(DAT)/oracles.txt + $(subst /,\,$(U)makedefs -h) + +$(DAT)/dungeon: $(O)utility.tag $(DAT)/dungeon.def + $(subst /,\,$(U)makedefs -e) + $(subst /,\,$(U)dgn_comp $(DAT)/dungeon.pdf) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)/win32api.h $(NTSYS)/nttty.c + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(NTSYS)/nttty.c +$(O)winnt.o: $(HACK_H) $(INCL)/win32api.h $(NTSYS)/winnt.c + $(cc) $(CFLAGS) -o$@ $(NTSYS)/winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)/ntsound.c + $(cc) $(CFLAGS) -o$@ $(NTSYS)/ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)/nhlan.h $(NTSYS)/mapimail.c + $(cc) $(CFLAGS) -DMAPI_VERBOSE -o$@ $(NTSYS)/mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * -c (which is included in CFLAGS) substituted +# with -o$@ +# * targets prefixed with $(O) +# * $(CC) changed to $(cc) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ../sys/atari/tos.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../sys/atari/tos.c +$(O)pcmain.o: ../sys/share/pcmain.c $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/win32api.h + $(cc) $(CFLAGS) -o$@ ../sys/share/pcmain.c +$(O)pcsys.o: ../sys/share/pcsys.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pcsys.c +$(O)pctty.o: ../sys/share/pctty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pctty.c +$(O)pcunix.o: ../sys/share/pcunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pcunix.c +$(O)random.o: ../sys/share/random.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/random.c +$(O)ioctl.o: ../sys/share/ioctl.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../sys/share/ioctl.c +$(O)unixtty.o: ../sys/share/unixtty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/unixtty.c +$(O)unixmain.o: ../sys/unix/unixmain.c $(HACK_H) $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixmain.c +$(O)unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixunix.c +$(O)unixres.o: ../sys/unix/unixres.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixres.c +$(O)bemain.o: ../sys/be/bemain.c $(HACK_H) $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ ../sys/be/bemain.c +$(O)getline.o: ../win/tty/getline.c $(HACK_H) $(INCL)/func_tab.h + $(cc) $(CFLAGS) -o$@ ../win/tty/getline.c +$(O)termcap.o: ../win/tty/termcap.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/termcap.c +$(O)topl.o: ../win/tty/topl.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/topl.c +$(O)wintty.o: ../win/tty/wintty.c $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/patchlevel.h $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/wintty.c +$(O)Window.o: ../win/X11/Window.c $(INCL)/xwindowp.h $(INCL)/xwindow.h \ + $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../win/X11/Window.c +$(O)dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../win/X11/dialogs.c +$(O)winX.o: ../win/X11/winX.c $(HACK_H) $(INCL)/winX.h $(INCL)/dlb.h \ + $(INCL)/patchlevel.h ../win/X11/nh72icon \ + ../win/X11/nh56icon ../win/X11/nh32icon + $(cc) $(CFLAGS) -o$@ ../win/X11/winX.c +$(O)winmap.o: ../win/X11/winmap.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/winX.h $(INCL)/tile2x11.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmap.c +$(O)winmenu.o: ../win/X11/winmenu.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmenu.c +$(O)winmesg.o: ../win/X11/winmesg.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmesg.c +$(O)winmisc.o: ../win/X11/winmisc.c $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmisc.c +$(O)winstat.o: ../win/X11/winstat.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winstat.c +$(O)wintext.o: ../win/X11/wintext.c $(HACK_H) $(INCL)/winX.h $(INCL)/xwindow.h + $(cc) $(CFLAGS) -o$@ ../win/X11/wintext.c +$(O)winval.o: ../win/X11/winval.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winval.c +$(O)tile.o: tile.c $(HACK_H) +$(O)gnaskstr.o: ../win/gnome/gnaskstr.c ../win/gnome/gnaskstr.h \ + ../win/gnome/gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnaskstr.c +$(O)gnbind.o: ../win/gnome/gnbind.c ../win/gnome/gnbind.h ../win/gnome/gnmain.h \ + ../win/gnome/gnaskstr.h ../win/gnome/gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnbind.c +$(O)gnglyph.o: ../win/gnome/gnglyph.c ../win/gnome/gnglyph.h $(INCL)/tile2x11.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnglyph.c +$(O)gnmain.o: ../win/gnome/gnmain.c ../win/gnome/gnmain.h ../win/gnome/gnsignal.h \ + ../win/gnome/gnbind.h ../win/gnome/gnopts.h $(HACK_H) \ + $(INCL)/date.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmain.c +$(O)gnmap.o: ../win/gnome/gnmap.c ../win/gnome/gnmap.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmap.c +$(O)gnmenu.o: ../win/gnome/gnmenu.c ../win/gnome/gnmenu.h ../win/gnome/gnmain.h \ + ../win/gnome/gnbind.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmenu.c +$(O)gnmesg.o: ../win/gnome/gnmesg.c ../win/gnome/gnmesg.h ../win/gnome/gnsignal.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmesg.c +$(O)gnopts.o: ../win/gnome/gnopts.c ../win/gnome/gnopts.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnmain.h ../win/gnome/gnmap.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnopts.c +$(O)gnplayer.o: ../win/gnome/gnplayer.c ../win/gnome/gnplayer.h \ + ../win/gnome/gnmain.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnplayer.c +$(O)gnsignal.o: ../win/gnome/gnsignal.c ../win/gnome/gnsignal.h \ + ../win/gnome/gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnsignal.c +$(O)gnstatus.o: ../win/gnome/gnstatus.c ../win/gnome/gnstatus.h \ + ../win/gnome/gnsignal.h ../win/gnome/gn_xpms.h \ + ../win/gnome/gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnstatus.c +$(O)gntext.o: ../win/gnome/gntext.c ../win/gnome/gntext.h ../win/gnome/gnmain.h \ + ../win/gnome/gn_rip.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gntext.c +$(O)gnworn.o: ../win/gnome/gnworn.c ../win/gnome/gnworn.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h ../win/gnome/gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnworn.c +$(O)gnyesno.o: ../win/gnome/gnyesno.c ../win/gnome/gnbind.h ../win/gnome/gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnyesno.c +$(O)wingem.o: ../win/gem/wingem.c $(HACK_H) $(INCL)/func_tab.h $(INCL)/dlb.h \ + $(INCL)/patchlevel.h $(INCL)/wingem.h + $(cc) $(CFLAGS) -o$@ ../win/gem/wingem.c +$(O)wingem1.o: ../win/gem/wingem1.c $(INCL)/gem_rsc.h $(INCL)/load_img.h \ + $(INCL)/gr_rect.h $(INCL)/wintype.h $(INCL)/wingem.h + $(cc) $(CFLAGS) -o$@ ../win/gem/wingem1.c +$(O)load_img.o: ../win/gem/load_img.c $(INCL)/load_img.h + $(cc) $(CFLAGS) -o$@ ../win/gem/load_img.c +$(O)gr_rect.o: ../win/gem/gr_rect.c $(INCL)/gr_rect.h + $(cc) $(CFLAGS) -o$@ ../win/gem/gr_rect.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/dlb.h $(INCL)/patchlevel.h $(INCL)/tile2x11.h \ + $(INCL)/qt_win.h $(INCL)/qt_clust.h $(INCL)/qt_kde0.h \ + $(INCL)/qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_win.cpp +$(O)qt_clust.o: ../win/Qt/qt_clust.cpp $(INCL)/qt_clust.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_clust.cpp +$(O)qttableview.o: ../win/Qt/qttableview.cpp $(INCL)/qttableview.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qttableview.cpp +$(O)monstr.o: monstr.c $(CONFIG_H) +$(O)vis_tab.o: vis_tab.c $(CONFIG_H) $(INCL)/vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)/edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)/artifact.h $(INCL)/artilist.h +$(O)attrib.o: attrib.c $(HACK_H) +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)/lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)/func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)/artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)/edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)/dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)/lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)/edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)/eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)/edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)/tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)/dgn_file.h $(INCL)/dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)/eshk.h $(INCL)/dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)/lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)/dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) +$(O)light.o: light.c $(HACK_H) $(INCL)/lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)/mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)/epri.h $(INCL)/emin.h \ + $(INCL)/edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)/emin.h $(INCL)/epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)/sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)/sp_lev.h $(INCL)/lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)/eshk.h $(INCL)/epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/artifact.h \ + $(INCL)/epri.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)/permonst.h $(INCL)/align.h \ + $(INCL)/monattk.h $(INCL)/monflag.h $(INCL)/monsym.h \ + $(INCL)/dungeon.h $(INCL)/eshk.h $(INCL)/vault.h \ + $(INCL)/epri.h $(INCL)/color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)/edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)/lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)/obj.h $(INCL)/objclass.h \ + $(INCL)/prop.h $(INCL)/skills.h $(INCL)/color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)/objclass.h $(INCL)/flag.h \ + $(HACK_H) $(INCL)/tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)/dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)/epri.h $(INCL)/edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)/epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/eshk.h \ + $(INCL)/epri.h $(INCL)/emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)/qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)/dlb.h $(INCL)/qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)/lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)/lev.h $(INCL)/tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)/lev.h $(INCL)/dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)/lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)/eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)/eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)/artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)/edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)/dlb.h $(INCL)/sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)/lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)/dlb.h $(INCL)/patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)/vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)/date.h $(INCL)/patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)/vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)/wingem.h $(INCL)/winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)/qtext.h $(INCL)/epri.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)/lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file diff --git a/src/allmain.c b/src/allmain.c new file mode 100644 index 0000000..d624a2f --- /dev/null +++ b/src/allmain.c @@ -0,0 +1,633 @@ +/* SCCS Id: @(#)allmain.c 3.4 2003/04/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* various code that was replicated in *main.c */ + +#include "hack.h" + +#ifndef NO_SIGNAL +#include +#endif + +#ifdef POSITIONBAR +STATIC_DCL void NDECL(do_positionbar); +#endif + +#ifdef OVL0 + +void +moveloop() +{ +#if defined(MICRO) || defined(WIN32) + char ch; + int abort_lev; +#endif + int moveamt = 0, wtcap = 0, change = 0; + boolean didmove = FALSE, monscanmove = FALSE; + + flags.moonphase = phase_of_the_moon(); + if(flags.moonphase == FULL_MOON) { + You("are lucky! Full moon tonight."); + change_luck(1); + } else if(flags.moonphase == NEW_MOON) { + pline("Be careful! New moon tonight."); + } + flags.friday13 = friday_13th(); + if (flags.friday13) { + pline("Watch out! Bad things can happen on Friday the 13th."); + change_luck(-1); + } + + initrack(); + + + /* Note: these initializers don't do anything except guarantee that + we're linked properly. + */ + decl_init(); + monst_init(); + monstr_init(); /* monster strengths */ + objects_init(); + +#ifdef WIZARD + if (wizard) add_debug_extended_commands(); +#endif + + (void) encumber_msg(); /* in case they auto-picked up something */ + + u.uz0.dlevel = u.uz.dlevel; + youmonst.movement = NORMAL_SPEED; /* give the hero some movement points */ + + for(;;) { + get_nh_event(); +#ifdef POSITIONBAR + do_positionbar(); +#endif + + didmove = flags.move; + if(didmove) { + /* actual time passed */ + youmonst.movement -= NORMAL_SPEED; + + do { /* hero can't move this turn loop */ + wtcap = encumber_msg(); + + flags.mon_moving = TRUE; + do { + monscanmove = movemon(); + if (youmonst.movement > NORMAL_SPEED) + break; /* it's now your turn */ + } while (monscanmove); + flags.mon_moving = FALSE; + + if (!monscanmove && youmonst.movement < NORMAL_SPEED) { + /* both you and the monsters are out of steam this round */ + /* set up for a new turn */ + struct monst *mtmp; + mcalcdistress(); /* adjust monsters' trap, blind, etc */ + + /* reallocate movement rations to monsters */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + mtmp->movement += mcalcmove(mtmp); + + if(!rn2(u.uevent.udemigod ? 25 : + (depth(&u.uz) > depth(&stronghold_level)) ? 50 : 70)) + (void) makemon((struct permonst *)0, 0, 0, NO_MM_FLAGS); + + /* calculate how much time passed. */ +#ifdef STEED + if (u.usteed && u.umoved) { + /* your speed doesn't augment steed's speed */ + moveamt = mcalcmove(u.usteed); + } else +#endif + { + moveamt = youmonst.data->mmove; + + if (Very_fast) { /* speed boots or potion */ + /* average movement is 1.67 times normal */ + moveamt += NORMAL_SPEED / 2; + if (rn2(3) == 0) moveamt += NORMAL_SPEED / 2; + } else if (Fast) { + /* average movement is 1.33 times normal */ + if (rn2(3) != 0) moveamt += NORMAL_SPEED / 2; + } + } + + switch (wtcap) { + case UNENCUMBERED: break; + case SLT_ENCUMBER: moveamt -= (moveamt / 4); break; + case MOD_ENCUMBER: moveamt -= (moveamt / 2); break; + case HVY_ENCUMBER: moveamt -= ((moveamt * 3) / 4); break; + case EXT_ENCUMBER: moveamt -= ((moveamt * 7) / 8); break; + default: break; + } + + youmonst.movement += moveamt; + if (youmonst.movement < 0) youmonst.movement = 0; + settrack(); + + monstermoves++; + moves++; + + /********************************/ + /* once-per-turn things go here */ + /********************************/ + + if (flags.bypasses) clear_bypasses(); + if(Glib) glibr(); + nh_timeout(); + run_regions(); + + if (u.ublesscnt) u.ublesscnt--; + if(flags.time && !flags.run) + flags.botl = 1; + + /* One possible result of prayer is healing. Whether or + * not you get healed depends on your current hit points. + * If you are allowed to regenerate during the prayer, the + * end-of-prayer calculation messes up on this. + * Another possible result is rehumanization, which requires + * that encumbrance and movement rate be recalculated. + */ + if (u.uinvulnerable) { + /* for the moment at least, you're in tiptop shape */ + wtcap = UNENCUMBERED; + } else if (Upolyd && youmonst.data->mlet == S_EEL && !is_pool(u.ux,u.uy) && !Is_waterlevel(&u.uz)) { + if (u.mh > 1) { + u.mh--; + flags.botl = 1; + } else if (u.mh < 1) + rehumanize(); + } else if (Upolyd && u.mh < u.mhmax) { + if (u.mh < 1) + rehumanize(); + else if (Regeneration || + (wtcap < MOD_ENCUMBER && !(moves%20))) { + flags.botl = 1; + u.mh++; + } + } else if (u.uhp < u.uhpmax && + (wtcap < MOD_ENCUMBER || !u.umoved || Regeneration)) { + if (u.ulevel > 9 && !(moves % 3)) { + int heal, Con = (int) ACURR(A_CON); + + if (Con <= 12) { + heal = 1; + } else { + heal = rnd(Con); + if (heal > u.ulevel-9) heal = u.ulevel-9; + } + flags.botl = 1; + u.uhp += heal; + if(u.uhp > u.uhpmax) + u.uhp = u.uhpmax; + } else if (Regeneration || + (u.ulevel <= 9 && + !(moves % ((MAXULEV+12) / (u.ulevel+2) + 1)))) { + flags.botl = 1; + u.uhp++; + } + } + + /* moving around while encumbered is hard work */ + if (wtcap > MOD_ENCUMBER && u.umoved) { + if(!(wtcap < EXT_ENCUMBER ? moves%30 : moves%10)) { + if (Upolyd && u.mh > 1) { + u.mh--; + } else if (!Upolyd && u.uhp > 1) { + u.uhp--; + } else { + You("pass out from exertion!"); + exercise(A_CON, FALSE); + fall_asleep(-10, FALSE); + } + } + } + + if ((u.uen < u.uenmax) && + ((wtcap < MOD_ENCUMBER && + (!(moves%((MAXULEV + 8 - u.ulevel) * + (Role_if(PM_WIZARD) ? 3 : 4) / 6)))) + || Energy_regeneration)) { + u.uen += rn1((int)(ACURR(A_WIS) + ACURR(A_INT)) / 15 + 1,1); + if (u.uen > u.uenmax) u.uen = u.uenmax; + flags.botl = 1; + } + + if(!u.uinvulnerable) { + if(Teleportation && !rn2(85)) { + xchar old_ux = u.ux, old_uy = u.uy; + tele(); + if (u.ux != old_ux || u.uy != old_uy) { + if (!next_to_u()) { + check_leash(old_ux, old_uy); + } +#ifdef REDO + /* clear doagain keystrokes */ + pushch(0); + savech(0); +#endif + } + } + /* delayed change may not be valid anymore */ + if ((change == 1 && !Polymorph) || + (change == 2 && u.ulycn == NON_PM)) + change = 0; + if(Polymorph && !rn2(100)) + change = 1; + else if (u.ulycn >= LOW_PM && !Upolyd && + !rn2(80 - (20 * night()))) + change = 2; + if (change && !Unchanging) { + if (multi >= 0) { + if (occupation) + stop_occupation(); + else + nomul(0); + if (change == 1) polyself(FALSE); + else you_were(); + change = 0; + } + } + } + + if(Searching && multi >= 0) (void) dosearch0(1); + dosounds(); + do_storms(); + gethungry(); + age_spells(); + exerchk(); + invault(); + if (u.uhave.amulet) amulet(); + if (!rn2(40+(int)(ACURR(A_DEX)*3))) + u_wipe_engr(rnd(3)); + if (u.uevent.udemigod && !u.uinvulnerable) { + if (u.udg_cnt) u.udg_cnt--; + if (!u.udg_cnt) { + intervene(); + u.udg_cnt = rn1(200, 50); + } + } + restore_attrib(); + /* underwater and waterlevel vision are done here */ + if (Is_waterlevel(&u.uz)) + movebubbles(); + else if (Underwater) + under_water(0); + /* vision while buried done here */ + else if (u.uburied) under_ground(0); + + /* when immobile, count is in turns */ + if(multi < 0) { + if (++multi == 0) { /* finished yet? */ + unmul((char *)0); + /* if unmul caused a level change, take it now */ + if (u.utotype) deferred_goto(); + } + } + } + } while (youmonst.movement= 0 && occupation) { +#if defined(MICRO) || defined(WIN32) + abort_lev = 0; + if (kbhit()) { + if ((ch = Getchar()) == ABORT) + abort_lev++; +# ifdef REDO + else + pushch(ch); +# endif /* REDO */ + } + if (!abort_lev && (*occupation)() == 0) +#else + if ((*occupation)() == 0) +#endif + occupation = 0; + if( +#if defined(MICRO) || defined(WIN32) + abort_lev || +#endif + monster_nearby()) { + stop_occupation(); + reset_eat(); + } +#if defined(MICRO) || defined(WIN32) + if (!(++occtime % 7)) + display_nhwindow(WIN_MAP, FALSE); +#endif + continue; + } + + if ((u.uhave.amulet || Clairvoyant) && + !In_endgame(&u.uz) && !BClairvoyant && + !(moves % 15) && !rn2(2)) + do_vicinity_map(); + + if(u.utrap && u.utraptype == TT_LAVA) { + if(!is_lava(u.ux,u.uy)) + u.utrap = 0; + else if (!u.uinvulnerable) { + u.utrap -= 1<<8; + if(u.utrap < 1<<8) { + killer_format = KILLED_BY; + killer = "molten lava"; + You("sink below the surface and die."); + done(DISSOLVED); + } else if(didmove && !u.umoved) { + Norep("You sink deeper into the lava."); + u.utrap += rnd(4); + } + } + } + +#ifdef WIZARD + if (iflags.sanity_check) + sanity_check(); +#endif + +#ifdef CLIPPING + /* just before rhack */ + cliparound(u.ux, u.uy); +#endif + + u.umoved = FALSE; + + if (multi > 0) { + lookaround(); + if (!multi) { + /* lookaround may clear multi */ + flags.move = 0; + if (flags.time) flags.botl = 1; + continue; + } + if (flags.mv) { + if(multi < COLNO && !--multi) + flags.travel = iflags.travel1 = flags.mv = flags.run = 0; + domove(); + } else { + --multi; + rhack(save_cm); + } + } else if (multi == 0) { +#ifdef MAIL + ckmailstatus(); +#endif + rhack((char *)0); + } + if (u.utotype) /* change dungeon level */ + deferred_goto(); /* after rhack() */ + /* !flags.move here: multiple movement command stopped */ + else if (flags.time && (!flags.move || !flags.mv)) + flags.botl = 1; + + if (vision_full_recalc) vision_recalc(0); /* vision! */ + /* when running in non-tport mode, this gets done through domove() */ + if ((!flags.run || iflags.runmode == RUN_TPORT) && + (multi && (!flags.travel ? !(multi % 7) : !(moves % 7L)))) { + if (flags.time && flags.run) flags.botl = 1; + display_nhwindow(WIN_MAP, FALSE); + } + } +} + +#endif /* OVL0 */ +#ifdef OVL1 + +void +stop_occupation() +{ + if(occupation) { + if (!maybe_finished_meal(TRUE)) + You("stop %s.", occtxt); + occupation = 0; + flags.botl = 1; /* in case u.uhs changed */ +/* fainting stops your occupation, there's no reason to sync. + sync_hunger(); +*/ +#ifdef REDO + nomul(0); + pushch(0); +#endif + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +display_gamewindows() +{ + WIN_MESSAGE = create_nhwindow(NHW_MESSAGE); + WIN_STATUS = create_nhwindow(NHW_STATUS); + WIN_MAP = create_nhwindow(NHW_MAP); + WIN_INVEN = create_nhwindow(NHW_MENU); + +#ifdef MAC + /* + * This _is_ the right place for this - maybe we will + * have to split display_gamewindows into create_gamewindows + * and show_gamewindows to get rid of this ifdef... + */ + if ( ! strcmp ( windowprocs . name , "mac" ) ) { + SanePositions ( ) ; + } +#endif + + /* + * The mac port is not DEPENDENT on the order of these + * displays, but it looks a lot better this way... + */ + display_nhwindow(WIN_STATUS, FALSE); + display_nhwindow(WIN_MESSAGE, FALSE); + clear_glyph_buffer(); + display_nhwindow(WIN_MAP, FALSE); +} + +void +newgame() +{ + int i; + +#ifdef MFLOPPY + gameDiskPrompt(); +#endif + + flags.ident = 1; + + for (i = 0; i < NUMMONS; i++) + mvitals[i].mvflags = mons[i].geno & G_NOCORPSE; + + init_objects(); /* must be before u_init() */ + + flags.pantheon = -1; /* role_init() will reset this */ + role_init(); /* must be before init_dungeons(), u_init(), + * and init_artifacts() */ + + init_dungeons(); /* must be before u_init() to avoid rndmonst() + * creating odd monsters for any tins and eggs + * in hero's initial inventory */ + init_artifacts(); /* before u_init() in case $WIZKIT specifies + * any artifacts */ + u_init(); + +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif +#ifdef NEWS + if(iflags.news) display_file(NEWS, FALSE); +#endif + load_qtlist(); /* load up the quest text info */ +/* quest_init();*/ /* Now part of role_init() */ + + mklev(); + u_on_upstairs(); + vision_reset(); /* set up internals for level (after mklev) */ + check_special_room(FALSE); + + flags.botlx = 1; + + /* Move the monster from under you or else + * makedog() will fail when it calls makemon(). + * - ucsfcgl!kneller + */ + if(MON_AT(u.ux, u.uy)) mnexto(m_at(u.ux, u.uy)); + (void) makedog(); + docrt(); + + if (flags.legacy) { + flush_screen(1); + com_pager(1); + } + +#ifdef INSURANCE + save_currentstate(); +#endif + program_state.something_worth_saving++; /* useful data now exists */ + + /* Success! */ + welcome(TRUE); + return; +} + +/* show "welcome [back] to nethack" message at program startup */ +void +welcome(new_game) +boolean new_game; /* false => restoring an old game */ +{ + char buf[BUFSZ]; + boolean currentgend = Upolyd ? u.mfemale : flags.female; + + /* + * The "welcome back" message always describes your innate form + * even when polymorphed or wearing a helm of opposite alignment. + * Alignment is shown unconditionally for new games; for restores + * it's only shown if it has changed from its original value. + * Sex is shown for new games except when it is redundant; for + * restores it's only shown if different from its original value. + */ + *buf = '\0'; + if (new_game || u.ualignbase[A_ORIGINAL] != u.ualignbase[A_CURRENT]) + Sprintf(eos(buf), " %s", align_str(u.ualignbase[A_ORIGINAL])); + if (!urole.name.f && + (new_game ? (urole.allow & ROLE_GENDMASK) == (ROLE_MALE|ROLE_FEMALE) : + currentgend != flags.initgend)) + Sprintf(eos(buf), " %s", genders[currentgend].adj); + + pline(new_game ? "%s %s, welcome to NetHack! You are a%s %s %s." + : "%s %s, the%s %s %s, welcome back to NetHack!", + Hello((struct monst *) 0), plname, buf, urace.adj, + (currentgend && urole.name.f) ? urole.name.f : urole.name.m); +} + +#ifdef POSITIONBAR +STATIC_DCL void +do_positionbar() +{ + static char pbar[COLNO]; + char *p; + + p = pbar; + /* up stairway */ + if (upstair.sx && + (glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) == + S_upstair || + glyph_to_cmap(level.locations[upstair.sx][upstair.sy].glyph) == + S_upladder)) { + *p++ = '<'; + *p++ = upstair.sx; + } + if (sstairs.sx && + (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) == + S_upstair || + glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) == + S_upladder)) { + *p++ = '<'; + *p++ = sstairs.sx; + } + + /* down stairway */ + if (dnstair.sx && + (glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) == + S_dnstair || + glyph_to_cmap(level.locations[dnstair.sx][dnstair.sy].glyph) == + S_dnladder)) { + *p++ = '>'; + *p++ = dnstair.sx; + } + if (sstairs.sx && + (glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) == + S_dnstair || + glyph_to_cmap(level.locations[sstairs.sx][sstairs.sy].glyph) == + S_dnladder)) { + *p++ = '>'; + *p++ = sstairs.sx; + } + + /* hero location */ + if (u.ux) { + *p++ = '@'; + *p++ = u.ux; + } + /* fence post */ + *p = 0; + + update_positionbar(pbar); +} +#endif + +#endif /* OVLB */ + +/*allmain.c*/ diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..e545eea --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,144 @@ +/* SCCS Id: @(#)alloc.c 3.4 1995/10/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* to get the malloc() prototype from system.h */ +#define ALLOC_C /* comment line for pre-compiled headers */ +/* since this file is also used in auxiliary programs, don't include all the + * function declarations for all of nethack + */ +#define EXTERN_H /* comment line for pre-compiled headers */ +#include "config.h" + +#if defined(MONITOR_HEAP) || defined(WIZARD) +char *FDECL(fmt_ptr, (const genericptr,char *)); +#endif + +#ifdef MONITOR_HEAP +#undef alloc +#undef free +extern void FDECL(free,(genericptr_t)); +static void NDECL(heapmon_init); + +static FILE *heaplog = 0; +static boolean tried_heaplog = FALSE; +#endif + +long *FDECL(alloc,(unsigned int)); +extern void VDECL(panic, (const char *,...)) PRINTF_F(1,2); + + +long * +alloc(lth) +register unsigned int lth; +{ +#ifdef LINT +/* + * a ridiculous definition, suppressing + * "possible pointer alignment problem" for (long *) malloc() + * from lint + */ + long dummy = ftell(stderr); + + if(lth) dummy = 0; /* make sure arg is used */ + return(&dummy); +#else + register genericptr_t ptr; + + ptr = malloc(lth); +#ifndef MONITOR_HEAP + if (!ptr) panic("Memory allocation failure; cannot get %u bytes", lth); +#endif + return((long *) ptr); +#endif +} + + +#if defined(MONITOR_HEAP) || defined(WIZARD) + +# if defined(MICRO) || defined(WIN32) +/* we actually want to know which systems have an ANSI run-time library + * to know which support the new %p format for printing pointers. + * due to the presence of things like gcc, NHSTDC is not a good test. + * so we assume microcomputers have all converted to ANSI and bigger + * computers which may have older libraries give reasonable results with + * the cast. + */ +# define MONITOR_PTR_FMT +# endif + +# ifdef MONITOR_PTR_FMT +# define PTR_FMT "%p" +# define PTR_TYP genericptr_t +# else +# define PTR_FMT "%06lx" +# define PTR_TYP unsigned long +# endif + +/* format a pointer for display purposes; caller supplies the result buffer */ +char * +fmt_ptr(ptr, buf) +const genericptr ptr; +char *buf; +{ + Sprintf(buf, PTR_FMT, (PTR_TYP)ptr); + return buf; +} + +#endif + +#ifdef MONITOR_HEAP + +/* If ${NH_HEAPLOG} is defined and we can create a file by that name, + then we'll log the allocation and release information to that file. */ +static void +heapmon_init() +{ + char *logname = getenv("NH_HEAPLOG"); + + if (logname && *logname) + heaplog = fopen(logname, "w"); + tried_heaplog = TRUE; +} + +long * +nhalloc(lth, file, line) +unsigned int lth; +const char *file; +int line; +{ + long *ptr = alloc(lth); + char ptr_address[20]; + + if (!tried_heaplog) heapmon_init(); + if (heaplog) + (void) fprintf(heaplog, "+%5u %s %4d %s\n", lth, + fmt_ptr((genericptr_t)ptr, ptr_address), + line, file); + /* potential panic in alloc() was deferred til here */ + if (!ptr) panic("Cannot get %u bytes, line %d of %s", + lth, line, file); + + return ptr; +} + +void +nhfree(ptr, file, line) +genericptr_t ptr; +const char *file; +int line; +{ + char ptr_address[20]; + + if (!tried_heaplog) heapmon_init(); + if (heaplog) + (void) fprintf(heaplog, "- %s %4d %s\n", + fmt_ptr((genericptr_t)ptr, ptr_address), + line, file); + + free(ptr); +} + +#endif /* MONITOR_HEAP */ + +/*alloc.c*/ diff --git a/src/apply.c b/src/apply.c new file mode 100644 index 0000000..f45e196 --- /dev/null +++ b/src/apply.c @@ -0,0 +1,3057 @@ +/* SCCS Id: @(#)apply.c 3.4 2003/11/18 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "edog.h" + +#ifdef OVLB + +static const char tools[] = { TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, 0 }; +static const char tools_too[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS, + WEAPON_CLASS, WAND_CLASS, GEM_CLASS, 0 }; + +#ifdef TOURIST +STATIC_DCL int FDECL(use_camera, (struct obj *)); +#endif +STATIC_DCL int FDECL(use_towel, (struct obj *)); +STATIC_DCL boolean FDECL(its_dead, (int,int,int *)); +STATIC_DCL int FDECL(use_stethoscope, (struct obj *)); +STATIC_DCL void FDECL(use_whistle, (struct obj *)); +STATIC_DCL void FDECL(use_magic_whistle, (struct obj *)); +STATIC_DCL void FDECL(use_leash, (struct obj *)); +STATIC_DCL int FDECL(use_mirror, (struct obj *)); +STATIC_DCL void FDECL(use_bell, (struct obj **)); +STATIC_DCL void FDECL(use_candelabrum, (struct obj *)); +STATIC_DCL void FDECL(use_candle, (struct obj **)); +STATIC_DCL void FDECL(use_lamp, (struct obj *)); +STATIC_DCL void FDECL(light_cocktail, (struct obj *)); +STATIC_DCL void FDECL(use_tinning_kit, (struct obj *)); +STATIC_DCL void FDECL(use_figurine, (struct obj **)); +STATIC_DCL void FDECL(use_grease, (struct obj *)); +STATIC_DCL void FDECL(use_trap, (struct obj *)); +STATIC_DCL void FDECL(use_stone, (struct obj *)); +STATIC_PTR int NDECL(set_trap); /* occupation callback */ +STATIC_DCL int FDECL(use_whip, (struct obj *)); +STATIC_DCL int FDECL(use_pole, (struct obj *)); +STATIC_DCL int FDECL(use_cream_pie, (struct obj *)); +STATIC_DCL int FDECL(use_grapple, (struct obj *)); +STATIC_DCL int FDECL(do_break_wand, (struct obj *)); +STATIC_DCL boolean FDECL(figurine_location_checks, + (struct obj *, coord *, BOOLEAN_P)); +STATIC_DCL boolean NDECL(uhave_graystone); +STATIC_DCL void FDECL(add_class, (char *, CHAR_P)); + +#ifdef AMIGA +void FDECL( amii_speaker, ( struct obj *, char *, int ) ); +#endif + +static const char no_elbow_room[] = "don't have enough elbow-room to maneuver."; + +#ifdef TOURIST +STATIC_OVL int +use_camera(obj) + struct obj *obj; +{ + register struct monst *mtmp; + + if(Underwater) { + pline("Using your camera underwater would void the warranty."); + return(0); + } + if(!getdir((char *)0)) return(0); + + if (obj->spe <= 0) { + pline(nothing_happens); + return (1); + } + consume_obj_charge(obj, TRUE); + + if (obj->cursed && !rn2(2)) { + (void) zapyourself(obj, TRUE); + } else if (u.uswallow) { + You("take a picture of %s %s.", s_suffix(mon_nam(u.ustuck)), + mbodypart(u.ustuck, STOMACH)); + } else if (u.dz) { + You("take a picture of the %s.", + (u.dz > 0) ? surface(u.ux,u.uy) : ceiling(u.ux,u.uy)); + } else if (!u.dx && !u.dy) { + (void) zapyourself(obj, TRUE); + } else if ((mtmp = bhit(u.dx, u.dy, COLNO, FLASHED_LIGHT, + (int FDECL((*),(MONST_P,OBJ_P)))0, + (int FDECL((*),(OBJ_P,OBJ_P)))0, + obj)) != 0) { + obj->ox = u.ux, obj->oy = u.uy; + (void) flash_hits_mon(mtmp, obj); + } + return 1; +} +#endif + +STATIC_OVL int +use_towel(obj) + struct obj *obj; +{ + if(!freehand()) { + You("have no free %s!", body_part(HAND)); + return 0; + } else if (obj->owornmask) { + You("cannot use it while you're wearing it!"); + return 0; + } else if (obj->cursed) { + long old; + switch (rn2(3)) { + case 2: + old = Glib; + Glib += rn1(10, 3); + Your("%s %s!", makeplural(body_part(HAND)), + (old ? "are filthier than ever" : "get slimy")); + return 1; + case 1: + if (!ublindf) { + old = u.ucreamed; + u.ucreamed += rn1(10, 3); + pline("Yecch! Your %s %s gunk on it!", body_part(FACE), + (old ? "has more" : "now has")); + make_blinded(Blinded + (long)u.ucreamed - old, TRUE); + } else { + const char *what = (ublindf->otyp == LENSES) ? + "lenses" : "blindfold"; + if (ublindf->cursed) { + You("push your %s %s.", what, + rn2(2) ? "cock-eyed" : "crooked"); + } else { + struct obj *saved_ublindf = ublindf; + You("push your %s off.", what); + Blindf_off(ublindf); + dropx(saved_ublindf); + } + } + return 1; + case 0: + break; + } + } + + if (Glib) { + Glib = 0; + You("wipe off your %s.", makeplural(body_part(HAND))); + return 1; + } else if(u.ucreamed) { + Blinded -= u.ucreamed; + u.ucreamed = 0; + + if (!Blinded) { + pline("You've got the glop off."); + Blinded = 1; + make_blinded(0L,TRUE); + } else { + Your("%s feels clean now.", body_part(FACE)); + } + return 1; + } + + Your("%s and %s are already clean.", + body_part(FACE), makeplural(body_part(HAND))); + + return 0; +} + +/* maybe give a stethoscope message based on floor objects */ +STATIC_OVL boolean +its_dead(rx, ry, resp) +int rx, ry, *resp; +{ + struct obj *otmp; + struct trap *ttmp; + + if (!can_reach_floor()) return FALSE; + + /* additional stethoscope messages from jyoung@apanix.apana.org.au */ + if (Hallucination && sobj_at(CORPSE, rx, ry)) { + /* (a corpse doesn't retain the monster's sex, + so we're forced to use generic pronoun here) */ + You_hear("a voice say, \"It's dead, Jim.\""); + *resp = 1; + return TRUE; + } else if (Role_if(PM_HEALER) && ((otmp = sobj_at(CORPSE, rx, ry)) != 0 || + (otmp = sobj_at(STATUE, rx, ry)) != 0)) { + /* possibly should check uppermost {corpse,statue} in the pile + if both types are present, but it's not worth the effort */ + if (vobj_at(rx, ry)->otyp == STATUE) otmp = vobj_at(rx, ry); + if (otmp->otyp == CORPSE) { + You("determine that %s unfortunate being is dead.", + (rx == u.ux && ry == u.uy) ? "this" : "that"); + } else { + ttmp = t_at(rx, ry); + pline("%s appears to be in %s health for a statue.", + The(mons[otmp->corpsenm].mname), + (ttmp && ttmp->ttyp == STATUE_TRAP) ? + "extraordinary" : "excellent"); + } + return TRUE; + } + return FALSE; +} + +static const char hollow_str[] = "a hollow sound. This must be a secret %s!"; + +/* Strictly speaking it makes no sense for usage of a stethoscope to + not take any time; however, unless it did, the stethoscope would be + almost useless. As a compromise, one use per turn is free, another + uses up the turn; this makes curse status have a tangible effect. */ +STATIC_OVL int +use_stethoscope(obj) + register struct obj *obj; +{ + static long last_used_move = -1; + static short last_used_movement = 0; + struct monst *mtmp; + struct rm *lev; + int rx, ry, res; + boolean interference = (u.uswallow && is_whirly(u.ustuck->data) && + !rn2(Role_if(PM_HEALER) ? 10 : 3)); + + if (nohands(youmonst.data)) { /* should also check for no ears and/or deaf */ + You("have no hands!"); /* not `body_part(HAND)' */ + return 0; + } else if (!freehand()) { + You("have no free %s.", body_part(HAND)); + return 0; + } + if (!getdir((char *)0)) return 0; + + res = (moves == last_used_move) && + (youmonst.movement == last_used_movement); + last_used_move = moves; + last_used_movement = youmonst.movement; + +#ifdef STEED + if (u.usteed && u.dz > 0) { + if (interference) { + pline("%s interferes.", Monnam(u.ustuck)); + mstatusline(u.ustuck); + } else + mstatusline(u.usteed); + return res; + } else +#endif + if (u.uswallow && (u.dx || u.dy || u.dz)) { + mstatusline(u.ustuck); + return res; + } else if (u.uswallow && interference) { + pline("%s interferes.", Monnam(u.ustuck)); + mstatusline(u.ustuck); + return res; + } else if (u.dz) { + if (Underwater) + You_hear("faint splashing."); + else if (u.dz < 0 || !can_reach_floor()) + You_cant("reach the %s.", + (u.dz > 0) ? surface(u.ux,u.uy) : ceiling(u.ux,u.uy)); + else if (its_dead(u.ux, u.uy, &res)) + ; /* message already given */ + else if (Is_stronghold(&u.uz)) + You_hear("the crackling of hellfire."); + else + pline_The("%s seems healthy enough.", surface(u.ux,u.uy)); + return res; + } else if (obj->cursed && !rn2(2)) { + You_hear("your heart beat."); + return res; + } + if (Stunned || (Confusion && !rn2(5))) confdir(); + if (!u.dx && !u.dy) { + ustatusline(); + return res; + } + rx = u.ux + u.dx; ry = u.uy + u.dy; + if (!isok(rx,ry)) { + You_hear("a faint typing noise."); + return 0; + } + if ((mtmp = m_at(rx,ry)) != 0) { + mstatusline(mtmp); + if (mtmp->mundetected) { + mtmp->mundetected = 0; + if (cansee(rx,ry)) newsym(mtmp->mx,mtmp->my); + } + if (!canspotmon(mtmp)) + map_invisible(rx,ry); + return res; + } + if (glyph_is_invisible(levl[rx][ry].glyph)) { + unmap_object(rx, ry); + newsym(rx, ry); + pline_The("invisible monster must have moved."); + } + lev = &levl[rx][ry]; + switch(lev->typ) { + case SDOOR: + You_hear(hollow_str, "door"); + cvt_sdoor_to_door(lev); /* ->typ = DOOR */ + if (Blind) feel_location(rx,ry); + else newsym(rx,ry); + return res; + case SCORR: + You_hear(hollow_str, "passage"); + lev->typ = CORR; + unblock_point(rx,ry); + if (Blind) feel_location(rx,ry); + else newsym(rx,ry); + return res; + } + + if (!its_dead(rx, ry, &res)) + You("hear nothing special."); /* not You_hear() */ + return res; +} + +static const char whistle_str[] = "produce a %s whistling sound."; + +STATIC_OVL void +use_whistle(obj) +struct obj *obj; +{ + You(whistle_str, obj->cursed ? "shrill" : "high"); + wake_nearby(); +} + +STATIC_OVL void +use_magic_whistle(obj) +struct obj *obj; +{ + register struct monst *mtmp, *nextmon; + + if(obj->cursed && !rn2(2)) { + You("produce a high-pitched humming noise."); + wake_nearby(); + } else { + int pet_cnt = 0; + You(whistle_str, Hallucination ? "normal" : "strange"); + for(mtmp = fmon; mtmp; mtmp = nextmon) { + nextmon = mtmp->nmon; /* trap might kill mon */ + if (DEADMONSTER(mtmp)) continue; + if (mtmp->mtame) { + if (mtmp->mtrapped) { + /* no longer in previous trap (affects mintrap) */ + mtmp->mtrapped = 0; + fill_pit(mtmp->mx, mtmp->my); + } + mnexto(mtmp); + if (canspotmon(mtmp)) ++pet_cnt; + if (mintrap(mtmp) == 2) change_luck(-1); + } + } + if (pet_cnt > 0) makeknown(obj->otyp); + } +} + +boolean +um_dist(x,y,n) +register xchar x, y, n; +{ + return((boolean)(abs(u.ux - x) > n || abs(u.uy - y) > n)); +} + +int +number_leashed() +{ + register int i = 0; + register struct obj *obj; + + for(obj = invent; obj; obj = obj->nobj) + if(obj->otyp == LEASH && obj->leashmon != 0) i++; + return(i); +} + +void +o_unleash(otmp) /* otmp is about to be destroyed or stolen */ +register struct obj *otmp; +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(mtmp->m_id == (unsigned)otmp->leashmon) + mtmp->mleashed = 0; + otmp->leashmon = 0; +} + +void +m_unleash(mtmp, feedback) /* mtmp is about to die, or become untame */ +register struct monst *mtmp; +boolean feedback; +{ + register struct obj *otmp; + + if (feedback) { + if (canseemon(mtmp)) + pline("%s pulls free of %s leash!", Monnam(mtmp), mhis(mtmp)); + else + Your("leash falls slack."); + } + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == LEASH && + otmp->leashmon == (int)mtmp->m_id) + otmp->leashmon = 0; + mtmp->mleashed = 0; +} + +void +unleash_all() /* player is about to die (for bones) */ +{ + register struct obj *otmp; + register struct monst *mtmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == LEASH) otmp->leashmon = 0; + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + mtmp->mleashed = 0; +} + +#define MAXLEASHED 2 + +/* ARGSUSED */ +STATIC_OVL void +use_leash(obj) +struct obj *obj; +{ + coord cc; + register struct monst *mtmp; + int spotmon; + + if(!obj->leashmon && number_leashed() >= MAXLEASHED) { + You("cannot leash any more pets."); + return; + } + + if(!get_adjacent_loc((char *)0, (char *)0, u.ux, u.uy, &cc)) return; + + if((cc.x == u.ux) && (cc.y == u.uy)) { +#ifdef STEED + if (u.usteed && u.dz > 0) { + mtmp = u.usteed; + spotmon = 1; + goto got_target; + } +#endif + pline("Leash yourself? Very funny..."); + return; + } + + if(!(mtmp = m_at(cc.x, cc.y))) { + There("is no creature there."); + return; + } + + spotmon = canspotmon(mtmp); +#ifdef STEED + got_target: +#endif + + if(!mtmp->mtame) { + if(!spotmon) + There("is no creature there."); + else + pline("%s %s leashed!", Monnam(mtmp), (!obj->leashmon) ? + "cannot be" : "is not"); + return; + } + if(!obj->leashmon) { + if(mtmp->mleashed) { + pline("This %s is already leashed.", + spotmon ? l_monnam(mtmp) : "monster"); + return; + } + You("slip the leash around %s%s.", + spotmon ? "your " : "", l_monnam(mtmp)); + mtmp->mleashed = 1; + obj->leashmon = (int)mtmp->m_id; + mtmp->msleeping = 0; + return; + } + if(obj->leashmon != (int)mtmp->m_id) { + pline("This leash is not attached to that creature."); + return; + } else { + if(obj->cursed) { + pline_The("leash would not come off!"); + obj->bknown = TRUE; + return; + } + mtmp->mleashed = 0; + obj->leashmon = 0; + You("remove the leash from %s%s.", + spotmon ? "your " : "", l_monnam(mtmp)); + } + return; +} + +struct obj * +get_mleash(mtmp) /* assuming mtmp->mleashed has been checked */ +register struct monst *mtmp; +{ + register struct obj *otmp; + + otmp = invent; + while(otmp) { + if(otmp->otyp == LEASH && otmp->leashmon == (int)mtmp->m_id) + return(otmp); + otmp = otmp->nobj; + } + return((struct obj *)0); +} + +#endif /* OVLB */ +#ifdef OVL1 + +boolean +next_to_u() +{ + register struct monst *mtmp; + register struct obj *otmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if(mtmp->mleashed) { + if (distu(mtmp->mx,mtmp->my) > 2) mnexto(mtmp); + if (distu(mtmp->mx,mtmp->my) > 2) { + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == LEASH && + otmp->leashmon == (int)mtmp->m_id) { + if(otmp->cursed) return(FALSE); + You_feel("%s leash go slack.", + (number_leashed() > 1) ? "a" : "the"); + mtmp->mleashed = 0; + otmp->leashmon = 0; + } + } + } + } +#ifdef STEED + /* no pack mules for the Amulet */ + if (u.usteed && mon_has_amulet(u.usteed)) return FALSE; +#endif + return(TRUE); +} + +#endif /* OVL1 */ +#ifdef OVL0 + +void +check_leash(x, y) +register xchar x, y; +{ + register struct obj *otmp; + register struct monst *mtmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->otyp != LEASH || otmp->leashmon == 0) continue; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((int)mtmp->m_id == otmp->leashmon) break; + } + if (!mtmp) { + impossible("leash in use isn't attached to anything?"); + otmp->leashmon = 0; + continue; + } + if (dist2(u.ux,u.uy,mtmp->mx,mtmp->my) > + dist2(x,y,mtmp->mx,mtmp->my)) { + if (!um_dist(mtmp->mx, mtmp->my, 3)) { + ; /* still close enough */ + } else if (otmp->cursed && !breathless(mtmp->data)) { + if (um_dist(mtmp->mx, mtmp->my, 5) || + (mtmp->mhp -= rnd(2)) <= 0) { + long save_pacifism = u.uconduct.killer; + + Your("leash chokes %s to death!", mon_nam(mtmp)); + /* hero might not have intended to kill pet, but + that's the result of his actions; gain experience, + lose pacifism, take alignment and luck hit, make + corpse less likely to remain tame after revival */ + xkilled(mtmp, 0); /* no "you kill it" message */ + /* life-saving doesn't ordinarily reset this */ + if (mtmp->mhp > 0) u.uconduct.killer = save_pacifism; + } else { + pline("%s chokes on the leash!", Monnam(mtmp)); + /* tameness eventually drops to 1 here (never 0) */ + if (mtmp->mtame && rn2(mtmp->mtame)) mtmp->mtame--; + } + } else { + if (um_dist(mtmp->mx, mtmp->my, 5)) { + pline("%s leash snaps loose!", s_suffix(Monnam(mtmp))); + m_unleash(mtmp, FALSE); + } else { + You("pull on the leash."); + if (mtmp->data->msound != MS_SILENT) + switch (rn2(3)) { + case 0: growl(mtmp); break; + case 1: yelp(mtmp); break; + default: whimper(mtmp); break; + } + } + } + } + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +#define WEAK 3 /* from eat.c */ + +static const char look_str[] = "look %s."; + +STATIC_OVL int +use_mirror(obj) +struct obj *obj; +{ + register struct monst *mtmp; + register char mlet; + boolean vis; + + if(!getdir((char *)0)) return 0; + if(obj->cursed && !rn2(2)) { + if (!Blind) + pline_The("mirror fogs up and doesn't reflect!"); + return 1; + } + if(!u.dx && !u.dy && !u.dz) { + if(!Blind && !Invisible) { + if (u.umonnum == PM_FLOATING_EYE) { + if (!Free_action) { + pline(Hallucination ? + "Yow! The mirror stares back!" : + "Yikes! You've frozen yourself!"); + nomul(-rnd((MAXULEV+6) - u.ulevel)); + } else You("stiffen momentarily under your gaze."); + } else if (youmonst.data->mlet == S_VAMPIRE) + You("don't have a reflection."); + else if (u.umonnum == PM_UMBER_HULK) { + pline("Huh? That doesn't look like you!"); + make_confused(HConfusion + d(3,4),FALSE); + } else if (Hallucination) + You(look_str, hcolor((char *)0)); + else if (Sick) + You(look_str, "peaked"); + else if (u.uhs >= WEAK) + You(look_str, "undernourished"); + else You("look as %s as ever.", + ACURR(A_CHA) > 14 ? + (poly_gender()==1 ? "beautiful" : "handsome") : + "ugly"); + } else { + You_cant("see your %s %s.", + ACURR(A_CHA) > 14 ? + (poly_gender()==1 ? "beautiful" : "handsome") : + "ugly", + body_part(FACE)); + } + return 1; + } + if(u.uswallow) { + if (!Blind) You("reflect %s %s.", s_suffix(mon_nam(u.ustuck)), + mbodypart(u.ustuck, STOMACH)); + return 1; + } + if(Underwater) { + You(Hallucination ? + "give the fish a chance to fix their makeup." : + "reflect the murky water."); + return 1; + } + if(u.dz) { + if (!Blind) + You("reflect the %s.", + (u.dz > 0) ? surface(u.ux,u.uy) : ceiling(u.ux,u.uy)); + return 1; + } + mtmp = bhit(u.dx, u.dy, COLNO, INVIS_BEAM, + (int FDECL((*),(MONST_P,OBJ_P)))0, + (int FDECL((*),(OBJ_P,OBJ_P)))0, + obj); + if (!mtmp || !haseyes(mtmp->data)) + return 1; + + vis = canseemon(mtmp); + mlet = mtmp->data->mlet; + if (mtmp->msleeping) { + if (vis) + pline ("%s is too tired to look at your mirror.", + Monnam(mtmp)); + } else if (!mtmp->mcansee) { + if (vis) + pline("%s can't see anything right now.", Monnam(mtmp)); + /* some monsters do special things */ + } else if (mlet == S_VAMPIRE || mlet == S_GHOST) { + if (vis) + pline ("%s doesn't have a reflection.", Monnam(mtmp)); + } else if(!mtmp->mcan && !mtmp->minvis && + mtmp->data == &mons[PM_MEDUSA]) { + if (mon_reflects(mtmp, "The gaze is reflected away by %s %s!")) + return 1; + if (vis) + pline("%s is turned to stone!", Monnam(mtmp)); + stoned = TRUE; + killed(mtmp); + } else if(!mtmp->mcan && !mtmp->minvis && + mtmp->data == &mons[PM_FLOATING_EYE]) { + int tmp = d((int)mtmp->m_lev, (int)mtmp->data->mattk[0].damd); + if (!rn2(4)) tmp = 120; + if (vis) + pline("%s is frozen by its reflection.", Monnam(mtmp)); + else You_hear("%s stop moving.",something); + mtmp->mcanmove = 0; + if ( (int) mtmp->mfrozen + tmp > 127) + mtmp->mfrozen = 127; + else mtmp->mfrozen += tmp; + } else if(!mtmp->mcan && !mtmp->minvis && + mtmp->data == &mons[PM_UMBER_HULK]) { + if (vis) + pline ("%s confuses itself!", Monnam(mtmp)); + mtmp->mconf = 1; + } else if(!mtmp->mcan && !mtmp->minvis && (mlet == S_NYMPH + || mtmp->data==&mons[PM_SUCCUBUS])) { + if (vis) { + pline ("%s admires herself in your mirror.", Monnam(mtmp)); + pline ("She takes it!"); + } else pline ("It steals your mirror!"); + setnotworn(obj); /* in case mirror was wielded */ + freeinv(obj); + (void) mpickobj(mtmp,obj); + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + } else if (!is_unicorn(mtmp->data) && !humanoid(mtmp->data) && + (!mtmp->minvis || perceives(mtmp->data)) && rn2(5)) { + if (vis) + pline("%s is frightened by its reflection.", Monnam(mtmp)); + monflee(mtmp, d(2,4), FALSE, FALSE); + } else if (!Blind) { + if (mtmp->minvis && !See_invisible) + ; + else if ((mtmp->minvis && !perceives(mtmp->data)) + || !haseyes(mtmp->data)) + pline("%s doesn't seem to notice its reflection.", + Monnam(mtmp)); + else + pline("%s ignores %s reflection.", + Monnam(mtmp), mhis(mtmp)); + } + return 1; +} + +STATIC_OVL void +use_bell(optr) +struct obj **optr; +{ + register struct obj *obj = *optr; + struct monst *mtmp; + boolean wakem = FALSE, learno = FALSE, + ordinary = (obj->otyp != BELL_OF_OPENING || !obj->spe), + invoking = (obj->otyp == BELL_OF_OPENING && + invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)); + + You("ring %s.", the(xname(obj))); + + if (Underwater || (u.uswallow && ordinary)) { +#ifdef AMIGA + amii_speaker( obj, "AhDhGqEqDhEhAqDqFhGw", AMII_MUFFLED_VOLUME ); +#endif + pline("But the sound is muffled."); + + } else if (invoking && ordinary) { + /* needs to be recharged... */ + pline("But it makes no sound."); + learno = TRUE; /* help player figure out why */ + + } else if (ordinary) { +#ifdef AMIGA + amii_speaker( obj, "ahdhgqeqdhehaqdqfhgw", AMII_MUFFLED_VOLUME ); +#endif + if (obj->cursed && !rn2(4) && + /* note: once any of them are gone, we stop all of them */ + !(mvitals[PM_WOOD_NYMPH].mvflags & G_GONE) && + !(mvitals[PM_WATER_NYMPH].mvflags & G_GONE) && + !(mvitals[PM_MOUNTAIN_NYMPH].mvflags & G_GONE) && + (mtmp = makemon(mkclass(S_NYMPH, 0), + u.ux, u.uy, NO_MINVENT)) != 0) { + You("summon %s!", a_monnam(mtmp)); + if (!obj_resists(obj, 93, 100)) { + pline("%s shattered!", Tobjnam(obj, "have")); + useup(obj); + *optr = 0; + } else switch (rn2(3)) { + default: + break; + case 1: + mon_adjust_speed(mtmp, 2, (struct obj *)0); + break; + case 2: /* no explanation; it just happens... */ + nomovemsg = ""; + nomul(-rnd(2)); + break; + } + } + wakem = TRUE; + + } else { + /* charged Bell of Opening */ + consume_obj_charge(obj, TRUE); + + if (u.uswallow) { + if (!obj->cursed) + (void) openit(); + else + pline(nothing_happens); + + } else if (obj->cursed) { + coord mm; + + mm.x = u.ux; + mm.y = u.uy; + mkundead(&mm, FALSE, NO_MINVENT); + wakem = TRUE; + + } else if (invoking) { + pline("%s an unsettling shrill sound...", + Tobjnam(obj, "issue")); +#ifdef AMIGA + amii_speaker( obj, "aefeaefeaefeaefeaefe", AMII_LOUDER_VOLUME ); +#endif + obj->age = moves; + learno = TRUE; + wakem = TRUE; + + } else if (obj->blessed) { + int res = 0; + +#ifdef AMIGA + amii_speaker( obj, "ahahahDhEhCw", AMII_SOFT_VOLUME ); +#endif + if (uchain) { + unpunish(); + res = 1; + } + res += openit(); + switch (res) { + case 0: pline(nothing_happens); break; + case 1: pline("%s opens...", Something); + learno = TRUE; break; + default: pline("Things open around you..."); + learno = TRUE; break; + } + + } else { /* uncursed */ +#ifdef AMIGA + amii_speaker( obj, "AeFeaeFeAefegw", AMII_OKAY_VOLUME ); +#endif + if (findit() != 0) learno = TRUE; + else pline(nothing_happens); + } + + } /* charged BofO */ + + if (learno) { + makeknown(BELL_OF_OPENING); + obj->known = 1; + } + if (wakem) wake_nearby(); +} + +STATIC_OVL void +use_candelabrum(obj) +register struct obj *obj; +{ + const char *s = (obj->spe != 1) ? "candles" : "candle"; + + if(Underwater) { + You("cannot make fire under water."); + return; + } + if(obj->lamplit) { + You("snuff the %s.", s); + end_burn(obj, TRUE); + return; + } + if(obj->spe <= 0) { + pline("This %s has no %s.", xname(obj), s); + return; + } + if(u.uswallow || obj->cursed) { + if (!Blind) + pline_The("%s %s for a moment, then %s.", + s, vtense(s, "flicker"), vtense(s, "die")); + return; + } + if(obj->spe < 7) { + There("%s only %d %s in %s.", + vtense(s, "are"), obj->spe, s, the(xname(obj))); + if (!Blind) + pline("%s lit. %s dimly.", + obj->spe == 1 ? "It is" : "They are", + Tobjnam(obj, "shine")); + } else { + pline("%s's %s burn%s", The(xname(obj)), s, + (Blind ? "." : " brightly!")); + } + if (!invocation_pos(u.ux, u.uy)) { + pline_The("%s %s being rapidly consumed!", s, vtense(s, "are")); + obj->age /= 2; + } else { + if(obj->spe == 7) { + if (Blind) + pline("%s a strange warmth!", Tobjnam(obj, "radiate")); + else + pline("%s with a strange light!", Tobjnam(obj, "glow")); + } + obj->known = 1; + } + begin_burn(obj, FALSE); +} + +STATIC_OVL void +use_candle(optr) +struct obj **optr; +{ + register struct obj *obj = *optr; + register struct obj *otmp; + const char *s = (obj->quan != 1) ? "candles" : "candle"; + char qbuf[QBUFSZ]; + + if(u.uswallow) { + You(no_elbow_room); + return; + } + if(Underwater) { + pline("Sorry, fire and water don't mix."); + return; + } + + otmp = carrying(CANDELABRUM_OF_INVOCATION); + if(!otmp || otmp->spe == 7) { + use_lamp(obj); + return; + } + + Sprintf(qbuf, "Attach %s", the(xname(obj))); + Sprintf(eos(qbuf), " to %s?", + safe_qbuf(qbuf, sizeof(" to ?"), the(xname(otmp)), + the(simple_typename(otmp->otyp)), "it")); + if(yn(qbuf) == 'n') { + if (!obj->lamplit) + You("try to light %s...", the(xname(obj))); + use_lamp(obj); + return; + } else { + if ((long)otmp->spe + obj->quan > 7L) + obj = splitobj(obj, 7L - (long)otmp->spe); + else *optr = 0; + You("attach %ld%s %s to %s.", + obj->quan, !otmp->spe ? "" : " more", + s, the(xname(otmp))); + if (!otmp->spe || otmp->age > obj->age) + otmp->age = obj->age; + otmp->spe += (int)obj->quan; + if (otmp->lamplit && !obj->lamplit) + pline_The("new %s magically %s!", s, vtense(s, "ignite")); + else if (!otmp->lamplit && obj->lamplit) + pline("%s out.", (obj->quan > 1L) ? "They go" : "It goes"); + if (obj->unpaid) + verbalize("You %s %s, you bought %s!", + otmp->lamplit ? "burn" : "use", + (obj->quan > 1L) ? "them" : "it", + (obj->quan > 1L) ? "them" : "it"); + if (obj->quan < 7L && otmp->spe == 7) + pline("%s now has seven%s candles attached.", + The(xname(otmp)), otmp->lamplit ? " lit" : ""); + /* candelabrum's light range might increase */ + if (otmp->lamplit) obj_merge_light_sources(otmp, otmp); + /* candles are no longer a separate light source */ + if (obj->lamplit) end_burn(obj, TRUE); + /* candles are now gone */ + useupall(obj); + } +} + +boolean +snuff_candle(otmp) /* call in drop, throw, and put in box, etc. */ +register struct obj *otmp; +{ + register boolean candle = Is_candle(otmp); + + if ((candle || otmp->otyp == CANDELABRUM_OF_INVOCATION) && + otmp->lamplit) { + char buf[BUFSZ]; + xchar x, y; + register boolean many = candle ? otmp->quan > 1L : otmp->spe > 1; + + (void) get_obj_location(otmp, &x, &y, 0); + if (otmp->where == OBJ_MINVENT ? cansee(x,y) : !Blind) + pline("%s %scandle%s flame%s extinguished.", + Shk_Your(buf, otmp), + (candle ? "" : "candelabrum's "), + (many ? "s'" : "'s"), (many ? "s are" : " is")); + end_burn(otmp, TRUE); + return(TRUE); + } + return(FALSE); +} + +/* called when lit lamp is hit by water or put into a container or + you've been swallowed by a monster; obj might be in transit while + being thrown or dropped so don't assume that its location is valid */ +boolean +snuff_lit(obj) +struct obj *obj; +{ + xchar x, y; + + if (obj->lamplit) { + if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || + obj->otyp == BRASS_LANTERN || obj->otyp == POT_OIL) { + (void) get_obj_location(obj, &x, &y, 0); + if (obj->where == OBJ_MINVENT ? cansee(x,y) : !Blind) + pline("%s %s out!", Yname2(obj), otense(obj, "go")); + end_burn(obj, TRUE); + return TRUE; + } + if (snuff_candle(obj)) return TRUE; + } + return FALSE; +} + +/* Called when potentially lightable object is affected by fire_damage(). + Return TRUE if object was lit and FALSE otherwise --ALI */ +boolean +catch_lit(obj) +struct obj *obj; +{ + xchar x, y; + + if (!obj->lamplit && (obj->otyp == MAGIC_LAMP || ignitable(obj))) { + if ((obj->otyp == MAGIC_LAMP || + obj->otyp == CANDELABRUM_OF_INVOCATION) && + obj->spe == 0) + return FALSE; + else if (obj->otyp != MAGIC_LAMP && obj->age == 0) + return FALSE; + if (!get_obj_location(obj, &x, &y, 0)) + return FALSE; + if (obj->otyp == CANDELABRUM_OF_INVOCATION && obj->cursed) + return FALSE; + if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || + obj->otyp == BRASS_LANTERN) && obj->cursed && !rn2(2)) + return FALSE; + if (obj->where == OBJ_MINVENT ? cansee(x,y) : !Blind) + pline("%s %s light!", Yname2(obj), otense(obj, "catch")); + if (obj->otyp == POT_OIL) makeknown(obj->otyp); + if (obj->unpaid && costly_spot(u.ux, u.uy) && (obj->where == OBJ_INVENT)) { + /* if it catches while you have it, then it's your tough luck */ + check_unpaid(obj); + verbalize("That's in addition to the cost of %s %s, of course.", + Yname2(obj), obj->quan == 1 ? "itself" : "themselves"); + bill_dummy_object(obj); + } + begin_burn(obj, FALSE); + return TRUE; + } + return FALSE; +} + +STATIC_OVL void +use_lamp(obj) +struct obj *obj; +{ + char buf[BUFSZ]; + + if(Underwater) { + pline("This is not a diving lamp."); + return; + } + if(obj->lamplit) { + if(obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || + obj->otyp == BRASS_LANTERN) + pline("%s lamp is now off.", Shk_Your(buf, obj)); + else + You("snuff out %s.", yname(obj)); + end_burn(obj, TRUE); + return; + } + /* magic lamps with an spe == 0 (wished for) cannot be lit */ + if ((!Is_candle(obj) && obj->age == 0) + || (obj->otyp == MAGIC_LAMP && obj->spe == 0)) { + if (obj->otyp == BRASS_LANTERN) + Your("lamp has run out of power."); + else pline("This %s has no oil.", xname(obj)); + return; + } + if (obj->cursed && !rn2(2)) { + pline("%s for a moment, then %s.", + Tobjnam(obj, "flicker"), otense(obj, "die")); + } else { + if(obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || + obj->otyp == BRASS_LANTERN) { + check_unpaid(obj); + pline("%s lamp is now on.", Shk_Your(buf, obj)); + } else { /* candle(s) */ + pline("%s flame%s %s%s", + s_suffix(Yname2(obj)), + plur(obj->quan), otense(obj, "burn"), + Blind ? "." : " brightly!"); + if (obj->unpaid && costly_spot(u.ux, u.uy) && + obj->age == 20L * (long)objects[obj->otyp].oc_cost) { + const char *ithem = obj->quan > 1L ? "them" : "it"; + verbalize("You burn %s, you bought %s!", ithem, ithem); + bill_dummy_object(obj); + } + } + begin_burn(obj, FALSE); + } +} + +STATIC_OVL void +light_cocktail(obj) + struct obj *obj; /* obj is a potion of oil */ +{ + char buf[BUFSZ]; + + if (u.uswallow) { + You(no_elbow_room); + return; + } + + if (obj->lamplit) { + You("snuff the lit potion."); + end_burn(obj, TRUE); + /* + * Free & add to re-merge potion. This will average the + * age of the potions. Not exactly the best solution, + * but its easy. + */ + freeinv(obj); + (void) addinv(obj); + return; + } else if (Underwater) { + There("is not enough oxygen to sustain a fire."); + return; + } + + You("light %s potion.%s", shk_your(buf, obj), + Blind ? "" : " It gives off a dim light."); + if (obj->unpaid && costly_spot(u.ux, u.uy)) { + /* Normally, we shouldn't both partially and fully charge + * for an item, but (Yendorian Fuel) Taxes are inevitable... + */ + check_unpaid(obj); + verbalize("That's in addition to the cost of the potion, of course."); + bill_dummy_object(obj); + } + makeknown(obj->otyp); + + if (obj->quan > 1L) { + obj = splitobj(obj, 1L); + begin_burn(obj, FALSE); /* burn before free to get position */ + obj_extract_self(obj); /* free from inv */ + + /* shouldn't merge */ + obj = hold_another_object(obj, "You drop %s!", + doname(obj), (const char *)0); + } else + begin_burn(obj, FALSE); +} + +static NEARDATA const char cuddly[] = { TOOL_CLASS, GEM_CLASS, 0 }; + +int +dorub() +{ + struct obj *obj = getobj(cuddly, "rub"); + + if (obj && obj->oclass == GEM_CLASS) { + if (is_graystone(obj)) { + use_stone(obj); + return 1; + } else { + pline("Sorry, I don't know how to use that."); + return 0; + } + } + + if (!obj || !wield_tool(obj, "rub")) return 0; + + /* now uwep is obj */ + if (uwep->otyp == MAGIC_LAMP) { + if (uwep->spe > 0 && !rn2(3)) { + check_unpaid_usage(uwep, TRUE); /* unusual item use */ + djinni_from_bottle(uwep); + makeknown(MAGIC_LAMP); + uwep->otyp = OIL_LAMP; + uwep->spe = 0; /* for safety */ + uwep->age = rn1(500,1000); + if (uwep->lamplit) begin_burn(uwep, TRUE); + update_inventory(); + } else if (rn2(2) && !Blind) + You("see a puff of smoke."); + else pline(nothing_happens); + } else if (obj->otyp == BRASS_LANTERN) { + /* message from Adventure */ + pline("Rubbing the electric lamp is not particularly rewarding."); + pline("Anyway, nothing exciting happens."); + } else pline(nothing_happens); + return 1; +} + +int +dojump() +{ + /* Physical jump */ + return jump(0); +} + +int +jump(magic) +int magic; /* 0=Physical, otherwise skill level */ +{ + coord cc; + + if (!magic && (nolimbs(youmonst.data) || slithy(youmonst.data))) { + /* normally (nolimbs || slithy) implies !Jumping, + but that isn't necessarily the case for knights */ + You_cant("jump; you have no legs!"); + return 0; + } else if (!magic && !Jumping) { + You_cant("jump very far."); + return 0; + } else if (u.uswallow) { + if (magic) { + You("bounce around a little."); + return 1; + } + pline("You've got to be kidding!"); + return 0; + } else if (u.uinwater) { + if (magic) { + You("swish around a little."); + return 1; + } + pline("This calls for swimming, not jumping!"); + return 0; + } else if (u.ustuck) { + if (u.ustuck->mtame && !Conflict && !u.ustuck->mconf) { + You("pull free from %s.", mon_nam(u.ustuck)); + u.ustuck = 0; + return 1; + } + if (magic) { + You("writhe a little in the grasp of %s!", mon_nam(u.ustuck)); + return 1; + } + You("cannot escape from %s!", mon_nam(u.ustuck)); + return 0; + } else if (Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) { + if (magic) { + You("flail around a little."); + return 1; + } + You("don't have enough traction to jump."); + return 0; + } else if (!magic && near_capacity() > UNENCUMBERED) { + You("are carrying too much to jump!"); + return 0; + } else if (!magic && (u.uhunger <= 100 || ACURR(A_STR) < 6)) { + You("lack the strength to jump!"); + return 0; + } else if (Wounded_legs) { + long wl = (Wounded_legs & BOTH_SIDES); + const char *bp = body_part(LEG); + + if (wl == BOTH_SIDES) bp = makeplural(bp); +#ifdef STEED + if (u.usteed) + pline("%s is in no shape for jumping.", Monnam(u.usteed)); + else +#endif + Your("%s%s %s in no shape for jumping.", + (wl == LEFT_SIDE) ? "left " : + (wl == RIGHT_SIDE) ? "right " : "", + bp, (wl == BOTH_SIDES) ? "are" : "is"); + return 0; + } +#ifdef STEED + else if (u.usteed && u.utrap) { + pline("%s is stuck in a trap.", Monnam(u.usteed)); + return (0); + } +#endif + + pline("Where do you want to jump?"); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the desired position") < 0) + return 0; /* user pressed ESC */ + if (!magic && !(HJumping & ~INTRINSIC) && !EJumping && + distu(cc.x, cc.y) != 5) { + /* The Knight jumping restriction still applies when riding a + * horse. After all, what shape is the knight piece in chess? + */ + pline("Illegal move!"); + return 0; + } else if (distu(cc.x, cc.y) > (magic ? 6+magic*3 : 9)) { + pline("Too far!"); + return 0; + } else if (!cansee(cc.x, cc.y)) { + You("cannot see where to land!"); + return 0; + } else if (!isok(cc.x, cc.y)) { + You("cannot jump there!"); + return 0; + } else { + coord uc; + int range, temp; + + if(u.utrap) + switch(u.utraptype) { + case TT_BEARTRAP: { + register long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; + You("rip yourself free of the bear trap! Ouch!"); + losehp(rnd(10), "jumping out of a bear trap", KILLED_BY); + set_wounded_legs(side, rn1(1000,500)); + break; + } + case TT_PIT: + You("leap from the pit!"); + break; + case TT_WEB: + You("tear the web apart as you pull yourself free!"); + deltrap(t_at(u.ux,u.uy)); + break; + case TT_LAVA: + You("pull yourself above the lava!"); + u.utrap = 0; + return 1; + case TT_INFLOOR: + You("strain your %s, but you're still stuck in the floor.", + makeplural(body_part(LEG))); + set_wounded_legs(LEFT_SIDE, rn1(10, 11)); + set_wounded_legs(RIGHT_SIDE, rn1(10, 11)); + return 1; + } + + /* + * Check the path from uc to cc, calling hurtle_step at each + * location. The final position actually reached will be + * in cc. + */ + uc.x = u.ux; + uc.y = u.uy; + /* calculate max(abs(dx), abs(dy)) as the range */ + range = cc.x - uc.x; + if (range < 0) range = -range; + temp = cc.y - uc.y; + if (temp < 0) temp = -temp; + if (range < temp) + range = temp; + (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range); + + /* A little Sokoban guilt... */ + if (In_sokoban(&u.uz)) + change_luck(-1); + + teleds(cc.x, cc.y, TRUE); + nomul(-1); + nomovemsg = ""; + morehungry(rnd(25)); + return 1; + } +} + +boolean +tinnable(corpse) +struct obj *corpse; +{ + if (corpse->oeaten) return 0; + if (!mons[corpse->corpsenm].cnutrit) return 0; + return 1; +} + +STATIC_OVL void +use_tinning_kit(obj) +register struct obj *obj; +{ + register struct obj *corpse, *can; + + /* This takes only 1 move. If this is to be changed to take many + * moves, we've got to deal with decaying corpses... + */ + if (obj->spe <= 0) { + You("seem to be out of tins."); + return; + } + if (!(corpse = floorfood("tin", 2))) return; + if (corpse->oeaten) { + You("cannot tin %s which is partly eaten.",something); + return; + } + if (touch_petrifies(&mons[corpse->corpsenm]) + && !Stone_resistance && !uarmg) { + char kbuf[BUFSZ]; + + if (poly_when_stoned(youmonst.data)) + You("tin %s without wearing gloves.", + an(mons[corpse->corpsenm].mname)); + else { + pline("Tinning %s without wearing gloves is a fatal mistake...", + an(mons[corpse->corpsenm].mname)); + Sprintf(kbuf, "trying to tin %s without gloves", + an(mons[corpse->corpsenm].mname)); + } + instapetrify(kbuf); + } + if (is_rider(&mons[corpse->corpsenm])) { + (void) revive_corpse(corpse); + verbalize("Yes... But War does not preserve its enemies..."); + return; + } + if (mons[corpse->corpsenm].cnutrit == 0) { + pline("That's too insubstantial to tin."); + return; + } + consume_obj_charge(obj, TRUE); + + if ((can = mksobj(TIN, FALSE, FALSE)) != 0) { + static const char you_buy_it[] = "You tin it, you bought it!"; + + can->corpsenm = corpse->corpsenm; + can->cursed = obj->cursed; + can->blessed = obj->blessed; + can->owt = weight(can); + can->known = 1; + can->spe = -1; /* Mark tinned tins. No spinach allowed... */ + if (carried(corpse)) { + if (corpse->unpaid) + verbalize(you_buy_it); + useup(corpse); + } else { + if (costly_spot(corpse->ox, corpse->oy) && !corpse->no_charge) + verbalize(you_buy_it); + useupf(corpse, 1L); + } + can = hold_another_object(can, "You make, but cannot pick up, %s.", + doname(can), (const char *)0); + } else impossible("Tinning failed."); +} + +void +use_unicorn_horn(obj) +struct obj *obj; +{ +#define PROP_COUNT 6 /* number of properties we're dealing with */ +#define ATTR_COUNT (A_MAX*3) /* number of attribute points we might fix */ + int idx, val, val_limit, + trouble_count, unfixable_trbl, did_prop, did_attr; + int trouble_list[PROP_COUNT + ATTR_COUNT]; + + if (obj && obj->cursed) { + long lcount = (long) rnd(100); + + switch (rn2(6)) { + case 0: make_sick(Sick ? Sick/3L + 1L : (long)rn1(ACURR(A_CON),20), + xname(obj), TRUE, SICK_NONVOMITABLE); + break; + case 1: make_blinded(Blinded + lcount, TRUE); + break; + case 2: if (!Confusion) + You("suddenly feel %s.", + Hallucination ? "trippy" : "confused"); + make_confused(HConfusion + lcount, TRUE); + break; + case 3: make_stunned(HStun + lcount, TRUE); + break; + case 4: (void) adjattrib(rn2(A_MAX), -1, FALSE); + break; + case 5: (void) make_hallucinated(HHallucination + lcount, TRUE, 0L); + break; + } + return; + } + +/* + * Entries in the trouble list use a very simple encoding scheme. + */ +#define prop2trbl(X) ((X) + A_MAX) +#define attr2trbl(Y) (Y) +#define prop_trouble(X) trouble_list[trouble_count++] = prop2trbl(X) +#define attr_trouble(Y) trouble_list[trouble_count++] = attr2trbl(Y) + + trouble_count = unfixable_trbl = did_prop = did_attr = 0; + + /* collect property troubles */ + if (Sick) prop_trouble(SICK); + if (Blinded > (long)u.ucreamed) prop_trouble(BLINDED); + if (HHallucination) prop_trouble(HALLUC); + if (Vomiting) prop_trouble(VOMITING); + if (HConfusion) prop_trouble(CONFUSION); + if (HStun) prop_trouble(STUNNED); + + unfixable_trbl = unfixable_trouble_count(TRUE); + + /* collect attribute troubles */ + for (idx = 0; idx < A_MAX; idx++) { + val_limit = AMAX(idx); + /* don't recover strength lost from hunger */ + if (idx == A_STR && u.uhs >= WEAK) val_limit--; + /* don't recover more than 3 points worth of any attribute */ + if (val_limit > ABASE(idx) + 3) val_limit = ABASE(idx) + 3; + + for (val = ABASE(idx); val < val_limit; val++) + attr_trouble(idx); + /* keep track of unfixed trouble, for message adjustment below */ + unfixable_trbl += (AMAX(idx) - val_limit); + } + + if (trouble_count == 0) { + pline(nothing_happens); + return; + } else if (trouble_count > 1) { /* shuffle */ + int i, j, k; + + for (i = trouble_count - 1; i > 0; i--) + if ((j = rn2(i + 1)) != i) { + k = trouble_list[j]; + trouble_list[j] = trouble_list[i]; + trouble_list[i] = k; + } + } + + /* + * Chances for number of troubles to be fixed + * 0 1 2 3 4 5 6 7 + * blessed: 22.7% 22.7% 19.5% 15.4% 10.7% 5.7% 2.6% 0.8% + * uncursed: 35.4% 35.4% 22.9% 6.3% 0 0 0 0 + */ + val_limit = rn2( d(2, (obj && obj->blessed) ? 4 : 2) ); + if (val_limit > trouble_count) val_limit = trouble_count; + + /* fix [some of] the troubles */ + for (val = 0; val < val_limit; val++) { + idx = trouble_list[val]; + + switch (idx) { + case prop2trbl(SICK): + make_sick(0L, (char *) 0, TRUE, SICK_ALL); + did_prop++; + break; + case prop2trbl(BLINDED): + make_blinded((long)u.ucreamed, TRUE); + did_prop++; + break; + case prop2trbl(HALLUC): + (void) make_hallucinated(0L, TRUE, 0L); + did_prop++; + break; + case prop2trbl(VOMITING): + make_vomiting(0L, TRUE); + did_prop++; + break; + case prop2trbl(CONFUSION): + make_confused(0L, TRUE); + did_prop++; + break; + case prop2trbl(STUNNED): + make_stunned(0L, TRUE); + did_prop++; + break; + default: + if (idx >= 0 && idx < A_MAX) { + ABASE(idx) += 1; + did_attr++; + } else + panic("use_unicorn_horn: bad trouble? (%d)", idx); + break; + } + } + + if (did_attr) + pline("This makes you feel %s!", + (did_prop + did_attr) == (trouble_count + unfixable_trbl) ? + "great" : "better"); + else if (!did_prop) + pline("Nothing seems to happen."); + + flags.botl = (did_attr || did_prop); +#undef PROP_COUNT +#undef ATTR_COUNT +#undef prop2trbl +#undef attr2trbl +#undef prop_trouble +#undef attr_trouble +} + +/* + * Timer callback routine: turn figurine into monster + */ +void +fig_transform(arg, timeout) +genericptr_t arg; +long timeout; +{ + struct obj *figurine = (struct obj *)arg; + struct monst *mtmp; + coord cc; + boolean cansee_spot, silent, okay_spot; + boolean redraw = FALSE; + char monnambuf[BUFSZ], carriedby[BUFSZ]; + + if (!figurine) { +#ifdef DEBUG + pline("null figurine in fig_transform()"); +#endif + return; + } + silent = (timeout != monstermoves); /* happened while away */ + okay_spot = get_obj_location(figurine, &cc.x, &cc.y, 0); + if (figurine->where == OBJ_INVENT || + figurine->where == OBJ_MINVENT) + okay_spot = enexto(&cc, cc.x, cc.y, + &mons[figurine->corpsenm]); + if (!okay_spot || + !figurine_location_checks(figurine,&cc, TRUE)) { + /* reset the timer to try again later */ + (void) start_timer((long)rnd(5000), TIMER_OBJECT, + FIG_TRANSFORM, (genericptr_t)figurine); + return; + } + + cansee_spot = cansee(cc.x, cc.y); + mtmp = make_familiar(figurine, cc.x, cc.y, TRUE); + if (mtmp) { + Sprintf(monnambuf, "%s",an(m_monnam(mtmp))); + switch (figurine->where) { + case OBJ_INVENT: + if (Blind) + You_feel("%s %s from your pack!", something, + locomotion(mtmp->data,"drop")); + else + You("see %s %s out of your pack!", + monnambuf, + locomotion(mtmp->data,"drop")); + break; + + case OBJ_FLOOR: + if (cansee_spot && !silent) { + You("suddenly see a figurine transform into %s!", + monnambuf); + redraw = TRUE; /* update figurine's map location */ + } + break; + + case OBJ_MINVENT: + if (cansee_spot && !silent) { + struct monst *mon; + mon = figurine->ocarry; + /* figurine carring monster might be invisible */ + if (canseemon(figurine->ocarry)) { + Sprintf(carriedby, "%s pack", + s_suffix(a_monnam(mon))); + } + else if (is_pool(mon->mx, mon->my)) + Strcpy(carriedby, "empty water"); + else + Strcpy(carriedby, "thin air"); + You("see %s %s out of %s!", monnambuf, + locomotion(mtmp->data, "drop"), carriedby); + } + break; +#if 0 + case OBJ_MIGRATING: + break; +#endif + + default: + impossible("figurine came to life where? (%d)", + (int)figurine->where); + break; + } + } + /* free figurine now */ + obj_extract_self(figurine); + obfree(figurine, (struct obj *)0); + if (redraw) newsym(cc.x, cc.y); +} + +STATIC_OVL boolean +figurine_location_checks(obj, cc, quietly) +struct obj *obj; +coord *cc; +boolean quietly; +{ + xchar x,y; + + if (carried(obj) && u.uswallow) { + if (!quietly) + You("don't have enough room in here."); + return FALSE; + } + x = cc->x; y = cc->y; + if (!isok(x,y)) { + if (!quietly) + You("cannot put the figurine there."); + return FALSE; + } + if (IS_ROCK(levl[x][y].typ) && + !(passes_walls(&mons[obj->corpsenm]) && may_passwall(x,y))) { + if (!quietly) + You("cannot place a figurine in %s!", + IS_TREE(levl[x][y].typ) ? "a tree" : "solid rock"); + return FALSE; + } + if (sobj_at(BOULDER,x,y) && !passes_walls(&mons[obj->corpsenm]) + && !throws_rocks(&mons[obj->corpsenm])) { + if (!quietly) + You("cannot fit the figurine on the boulder."); + return FALSE; + } + return TRUE; +} + +STATIC_OVL void +use_figurine(optr) +struct obj **optr; +{ + register struct obj *obj = *optr; + xchar x, y; + coord cc; + + if (u.uswallow) { + /* can't activate a figurine while swallowed */ + if (!figurine_location_checks(obj, (coord *)0, FALSE)) + return; + } + if(!getdir((char *)0)) { + flags.move = multi = 0; + return; + } + x = u.ux + u.dx; y = u.uy + u.dy; + cc.x = x; cc.y = y; + /* Passing FALSE arg here will result in messages displayed */ + if (!figurine_location_checks(obj, &cc, FALSE)) return; + You("%s and it transforms.", + (u.dx||u.dy) ? "set the figurine beside you" : + (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || + is_pool(cc.x, cc.y)) ? + "release the figurine" : + (u.dz < 0 ? + "toss the figurine into the air" : + "set the figurine on the ground")); + (void) make_familiar(obj, cc.x, cc.y, FALSE); + (void) stop_timer(FIG_TRANSFORM, (genericptr_t)obj); + useup(obj); + *optr = 0; +} + +static NEARDATA const char lubricables[] = { ALL_CLASSES, ALLOW_NONE, 0 }; +static NEARDATA const char need_to_remove_outer_armor[] = + "need to remove your %s to grease your %s."; + +STATIC_OVL void +use_grease(obj) +struct obj *obj; +{ + struct obj *otmp; + char buf[BUFSZ]; + + if (Glib) { + pline("%s from your %s.", Tobjnam(obj, "slip"), + makeplural(body_part(FINGER))); + dropx(obj); + return; + } + + if (obj->spe > 0) { + if ((obj->cursed || Fumbling) && !rn2(2)) { + consume_obj_charge(obj, TRUE); + + pline("%s from your %s.", Tobjnam(obj, "slip"), + makeplural(body_part(FINGER))); + dropx(obj); + return; + } + otmp = getobj(lubricables, "grease"); + if (!otmp) return; + if ((otmp->owornmask & WORN_ARMOR) && uarmc) { + Strcpy(buf, xname(uarmc)); + You(need_to_remove_outer_armor, buf, xname(otmp)); + return; + } +#ifdef TOURIST + if ((otmp->owornmask & WORN_SHIRT) && (uarmc || uarm)) { + Strcpy(buf, uarmc ? xname(uarmc) : ""); + if (uarmc && uarm) Strcat(buf, " and "); + Strcat(buf, uarm ? xname(uarm) : ""); + You(need_to_remove_outer_armor, buf, xname(otmp)); + return; + } +#endif + consume_obj_charge(obj, TRUE); + + if (otmp != &zeroobj) { + You("cover %s with a thick layer of grease.", + yname(otmp)); + otmp->greased = 1; + if (obj->cursed && !nohands(youmonst.data)) { + incr_itimeout(&Glib, rnd(15)); + pline("Some of the grease gets all over your %s.", + makeplural(body_part(HAND))); + } + } else { + Glib += rnd(15); + You("coat your %s with grease.", + makeplural(body_part(FINGER))); + } + } else { + if (obj->known) + pline("%s empty.", Tobjnam(obj, "are")); + else + pline("%s to be empty.", Tobjnam(obj, "seem")); + } + update_inventory(); +} + +static struct trapinfo { + struct obj *tobj; + xchar tx, ty; + int time_needed; + boolean force_bungle; +} trapinfo; + +void +reset_trapset() +{ + trapinfo.tobj = 0; + trapinfo.force_bungle = 0; +} + +/* touchstones - by Ken Arnold */ +STATIC_OVL void +use_stone(tstone) +struct obj *tstone; +{ + struct obj *obj; + boolean do_scratch; + const char *streak_color, *choices; + char stonebuf[QBUFSZ]; + static const char scritch[] = "\"scritch, scritch\""; + static const char allowall[3] = { COIN_CLASS, ALL_CLASSES, 0 }; + static const char justgems[3] = { ALLOW_NONE, GEM_CLASS, 0 }; +#ifndef GOLDOBJ + struct obj goldobj; +#endif + + /* in case it was acquired while blinded */ + if (!Blind) tstone->dknown = 1; + /* when the touchstone is fully known, don't bother listing extra + junk as likely candidates for rubbing */ + choices = (tstone->otyp == TOUCHSTONE && tstone->dknown && + objects[TOUCHSTONE].oc_name_known) ? justgems : allowall; + Sprintf(stonebuf, "rub on the stone%s", plur(tstone->quan)); + if ((obj = getobj(choices, stonebuf)) == 0) + return; +#ifndef GOLDOBJ + if (obj->oclass == COIN_CLASS) { + u.ugold += obj->quan; /* keep botl up to date */ + goldobj = *obj; + dealloc_obj(obj); + obj = &goldobj; + } +#endif + + if (obj == tstone && obj->quan == 1) { + You_cant("rub %s on itself.", the(xname(obj))); + return; + } + + if (tstone->otyp == TOUCHSTONE && tstone->cursed && + obj->oclass == GEM_CLASS && !is_graystone(obj) && + !obj_resists(obj, 80, 100)) { + if (Blind) + pline("You feel something shatter."); + else if (Hallucination) + pline("Oh, wow, look at the pretty shards."); + else + pline("A sharp crack shatters %s%s.", + (obj->quan > 1) ? "one of " : "", the(xname(obj))); +#ifndef GOLDOBJ + /* assert(obj != &goldobj); */ +#endif + useup(obj); + return; + } + + if (Blind) { + pline(scritch); + return; + } else if (Hallucination) { + pline("Oh wow, man: Fractals!"); + return; + } + + do_scratch = FALSE; + streak_color = 0; + + switch (obj->oclass) { + case GEM_CLASS: /* these have class-specific handling below */ + case RING_CLASS: + if (tstone->otyp != TOUCHSTONE) { + do_scratch = TRUE; + } else if (obj->oclass == GEM_CLASS && (tstone->blessed || + (!tstone->cursed && + (Role_if(PM_ARCHEOLOGIST) || Race_if(PM_GNOME))))) { + makeknown(TOUCHSTONE); + makeknown(obj->otyp); + prinv((char *)0, obj, 0L); + return; + } else { + /* either a ring or the touchstone was not effective */ + if (objects[obj->otyp].oc_material == GLASS) { + do_scratch = TRUE; + break; + } + } + streak_color = c_obj_colors[objects[obj->otyp].oc_color]; + break; /* gem or ring */ + + default: + switch (objects[obj->otyp].oc_material) { + case CLOTH: + pline("%s a little more polished now.", Tobjnam(tstone, "look")); + return; + case LIQUID: + if (!obj->known) /* note: not "whetstone" */ + You("must think this is a wetstone, do you?"); + else + pline("%s a little wetter now.", Tobjnam(tstone, "are")); + return; + case WAX: + streak_color = "waxy"; + break; /* okay even if not touchstone */ + case WOOD: + streak_color = "wooden"; + break; /* okay even if not touchstone */ + case GOLD: + do_scratch = TRUE; /* scratching and streaks */ + streak_color = "golden"; + break; + case SILVER: + do_scratch = TRUE; /* scratching and streaks */ + streak_color = "silvery"; + break; + default: + /* Objects passing the is_flimsy() test will not + scratch a stone. They will leave streaks on + non-touchstones and touchstones alike. */ + if (is_flimsy(obj)) + streak_color = c_obj_colors[objects[obj->otyp].oc_color]; + else + do_scratch = (tstone->otyp != TOUCHSTONE); + break; + } + break; /* default oclass */ + } + + Sprintf(stonebuf, "stone%s", plur(tstone->quan)); + if (do_scratch) + pline("You make %s%sscratch marks on the %s.", + streak_color ? streak_color : (const char *)"", + streak_color ? " " : "", stonebuf); + else if (streak_color) + pline("You see %s streaks on the %s.", streak_color, stonebuf); + else + pline(scritch); + return; +} + +/* Place a landmine/bear trap. Helge Hafting */ +STATIC_OVL void +use_trap(otmp) +struct obj *otmp; +{ + int ttyp, tmp; + const char *what = (char *)0; + char buf[BUFSZ]; + const char *occutext = "setting the trap"; + + if (nohands(youmonst.data)) + what = "without hands"; + else if (Stunned) + what = "while stunned"; + else if (u.uswallow) + what = is_animal(u.ustuck->data) ? "while swallowed" : + "while engulfed"; + else if (Underwater) + what = "underwater"; + else if (Levitation) + what = "while levitating"; + else if (is_pool(u.ux, u.uy)) + what = "in water"; + else if (is_lava(u.ux, u.uy)) + what = "in lava"; + else if (On_stairs(u.ux, u.uy)) + what = (u.ux == xdnladder || u.ux == xupladder) ? + "on the ladder" : "on the stairs"; + else if (IS_FURNITURE(levl[u.ux][u.uy].typ) || + IS_ROCK(levl[u.ux][u.uy].typ) || + closed_door(u.ux, u.uy) || t_at(u.ux, u.uy)) + what = "here"; + if (what) { + You_cant("set a trap %s!",what); + reset_trapset(); + return; + } + ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP; + if (otmp == trapinfo.tobj && + u.ux == trapinfo.tx && u.uy == trapinfo.ty) { + You("resume setting %s %s.", + shk_your(buf, otmp), + defsyms[trap_to_defsym(what_trap(ttyp))].explanation); + set_occupation(set_trap, occutext, 0); + return; + } + trapinfo.tobj = otmp; + trapinfo.tx = u.ux, trapinfo.ty = u.uy; + tmp = ACURR(A_DEX); + trapinfo.time_needed = (tmp > 17) ? 2 : (tmp > 12) ? 3 : + (tmp > 7) ? 4 : 5; + if (Blind) trapinfo.time_needed *= 2; + tmp = ACURR(A_STR); + if (ttyp == BEAR_TRAP && tmp < 18) + trapinfo.time_needed += (tmp > 12) ? 1 : (tmp > 7) ? 2 : 4; + /*[fumbling and/or confusion and/or cursed object check(s) + should be incorporated here instead of in set_trap]*/ +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { + boolean chance; + + if (Fumbling || otmp->cursed) chance = (rnl(10) > 3); + else chance = (rnl(10) > 5); + You("aren't very skilled at reaching from %s.", + mon_nam(u.usteed)); + Sprintf(buf, "Continue your attempt to set %s?", + the(defsyms[trap_to_defsym(what_trap(ttyp))].explanation)); + if(yn(buf) == 'y') { + if (chance) { + switch(ttyp) { + case LANDMINE: /* set it off */ + trapinfo.time_needed = 0; + trapinfo.force_bungle = TRUE; + break; + case BEAR_TRAP: /* drop it without arming it */ + reset_trapset(); + You("drop %s!", + the(defsyms[trap_to_defsym(what_trap(ttyp))].explanation)); + dropx(otmp); + return; + } + } + } else { + reset_trapset(); + return; + } + } +#endif + You("begin setting %s %s.", + shk_your(buf, otmp), + defsyms[trap_to_defsym(what_trap(ttyp))].explanation); + set_occupation(set_trap, occutext, 0); + return; +} + +STATIC_PTR +int +set_trap() +{ + struct obj *otmp = trapinfo.tobj; + struct trap *ttmp; + int ttyp; + + if (!otmp || !carried(otmp) || + u.ux != trapinfo.tx || u.uy != trapinfo.ty) { + /* ?? */ + reset_trapset(); + return 0; + } + + if (--trapinfo.time_needed > 0) return 1; /* still busy */ + + ttyp = (otmp->otyp == LAND_MINE) ? LANDMINE : BEAR_TRAP; + ttmp = maketrap(u.ux, u.uy, ttyp); + if (ttmp) { + ttmp->tseen = 1; + ttmp->madeby_u = 1; + newsym(u.ux, u.uy); /* if our hero happens to be invisible */ + if (*in_rooms(u.ux,u.uy,SHOPBASE)) { + add_damage(u.ux, u.uy, 0L); /* schedule removal */ + } + if (!trapinfo.force_bungle) + You("finish arming %s.", + the(defsyms[trap_to_defsym(what_trap(ttyp))].explanation)); + if (((otmp->cursed || Fumbling) && (rnl(10) > 5)) || trapinfo.force_bungle) + dotrap(ttmp, + (unsigned)(trapinfo.force_bungle ? FORCEBUNGLE : 0)); + } else { + /* this shouldn't happen */ + Your("trap setting attempt fails."); + } + useup(otmp); + reset_trapset(); + return 0; +} + +STATIC_OVL int +use_whip(obj) +struct obj *obj; +{ + char buf[BUFSZ]; + struct monst *mtmp; + struct obj *otmp; + int rx, ry, proficient, res = 0; + const char *msg_slipsfree = "The bullwhip slips free."; + const char *msg_snap = "Snap!"; + + if (obj != uwep) { + if (!wield_tool(obj, "lash")) return 0; + else res = 1; + } + if (!getdir((char *)0)) return res; + + if (Stunned || (Confusion && !rn2(5))) confdir(); + rx = u.ux + u.dx; + ry = u.uy + u.dy; + mtmp = m_at(rx, ry); + + /* fake some proficiency checks */ + proficient = 0; + if (Role_if(PM_ARCHEOLOGIST)) ++proficient; + if (ACURR(A_DEX) < 6) proficient--; + else if (ACURR(A_DEX) >= 14) proficient += (ACURR(A_DEX) - 14); + if (Fumbling) --proficient; + if (proficient > 3) proficient = 3; + if (proficient < 0) proficient = 0; + + if (u.uswallow && attack(u.ustuck)) { + There("is not enough room to flick your bullwhip."); + + } else if (Underwater) { + There("is too much resistance to flick your bullwhip."); + + } else if (u.dz < 0) { + You("flick a bug off of the %s.",ceiling(u.ux,u.uy)); + + } else if ((!u.dx && !u.dy) || (u.dz > 0)) { + int dam; + +#ifdef STEED + /* Sometimes you hit your steed by mistake */ + if (u.usteed && !rn2(proficient + 2)) { + You("whip %s!", mon_nam(u.usteed)); + kick_steed(); + return 1; + } +#endif + if (Levitation +#ifdef STEED + || u.usteed +#endif + ) { + /* Have a shot at snaring something on the floor */ + otmp = level.objects[u.ux][u.uy]; + if (otmp && otmp->otyp == CORPSE && otmp->corpsenm == PM_HORSE) { + pline("Why beat a dead horse?"); + return 1; + } + if (otmp && proficient) { + You("wrap your bullwhip around %s on the %s.", + an(singular(otmp, xname)), surface(u.ux, u.uy)); + if (rnl(6) || pickup_object(otmp, 1L, TRUE) < 1) + pline(msg_slipsfree); + return 1; + } + } + dam = rnd(2) + dbon() + obj->spe; + if (dam <= 0) dam = 1; + You("hit your %s with your bullwhip.", body_part(FOOT)); + Sprintf(buf, "killed %sself with %s bullwhip", uhim(), uhis()); + losehp(dam, buf, NO_KILLER_PREFIX); + flags.botl = 1; + return 1; + + } else if ((Fumbling || Glib) && !rn2(5)) { + pline_The("bullwhip slips out of your %s.", body_part(HAND)); + dropx(obj); + + } else if (u.utrap && u.utraptype == TT_PIT) { + /* + * Assumptions: + * + * if you're in a pit + * - you are attempting to get out of the pit + * - or, if you are applying it towards a small + * monster then it is assumed that you are + * trying to hit it. + * else if the monster is wielding a weapon + * - you are attempting to disarm a monster + * else + * - you are attempting to hit the monster + * + * if you're confused (and thus off the mark) + * - you only end up hitting. + * + */ + const char *wrapped_what = (char *)0; + + if (mtmp) { + if (bigmonst(mtmp->data)) { + wrapped_what = strcpy(buf, mon_nam(mtmp)); + } else if (proficient) { + if (attack(mtmp)) return 1; + else pline(msg_snap); + } + } + if (!wrapped_what) { + if (IS_FURNITURE(levl[rx][ry].typ)) + wrapped_what = something; + else if (sobj_at(BOULDER, rx, ry)) + wrapped_what = "a boulder"; + } + if (wrapped_what) { + coord cc; + + cc.x = rx; cc.y = ry; + You("wrap your bullwhip around %s.", wrapped_what); + if (proficient && rn2(proficient + 2)) { + if (!mtmp || enexto(&cc, rx, ry, youmonst.data)) { + You("yank yourself out of the pit!"); + teleds(cc.x, cc.y, TRUE); + u.utrap = 0; + vision_full_recalc = 1; + } + } else { + pline(msg_slipsfree); + } + if (mtmp) wakeup(mtmp); + } else pline(msg_snap); + + } else if (mtmp) { + if (!canspotmon(mtmp) && + !glyph_is_invisible(levl[rx][ry].glyph)) { + pline("A monster is there that you couldn't see."); + map_invisible(rx, ry); + } + otmp = MON_WEP(mtmp); /* can be null */ + if (otmp) { + char onambuf[BUFSZ]; + const char *mon_hand; + boolean gotit = proficient && (!Fumbling || !rn2(10)); + + Strcpy(onambuf, cxname(otmp)); + if (gotit) { + mon_hand = mbodypart(mtmp, HAND); + if (bimanual(otmp)) mon_hand = makeplural(mon_hand); + } else + mon_hand = 0; /* lint suppression */ + + You("wrap your bullwhip around %s %s.", + s_suffix(mon_nam(mtmp)), onambuf); + if (gotit && otmp->cursed) { + pline("%s welded to %s %s%c", + (otmp->quan == 1L) ? "It is" : "They are", + mhis(mtmp), mon_hand, + !otmp->bknown ? '!' : '.'); + otmp->bknown = 1; + gotit = FALSE; /* can't pull it free */ + } + if (gotit) { + obj_extract_self(otmp); + possibly_unwield(mtmp, FALSE); + setmnotwielded(mtmp,otmp); + + switch (rn2(proficient + 1)) { + case 2: + /* to floor near you */ + You("yank %s %s to the %s!", s_suffix(mon_nam(mtmp)), + onambuf, surface(u.ux, u.uy)); + place_object(otmp, u.ux, u.uy); + stackobj(otmp); + break; + case 3: + /* right to you */ +#if 0 + if (!rn2(25)) { + /* proficient with whip, but maybe not + so proficient at catching weapons */ + int hitu, hitvalu; + + hitvalu = 8 + otmp->spe; + hitu = thitu(hitvalu, + dmgval(otmp, &youmonst), + otmp, (char *)0); + if (hitu) { + pline_The("%s hits you as you try to snatch it!", + the(onambuf)); + } + place_object(otmp, u.ux, u.uy); + stackobj(otmp); + break; + } +#endif /* 0 */ + /* right into your inventory */ + You("snatch %s %s!", s_suffix(mon_nam(mtmp)), onambuf); + if (otmp->otyp == CORPSE && + touch_petrifies(&mons[otmp->corpsenm]) && + !uarmg && !Stone_resistance && + !(poly_when_stoned(youmonst.data) && + polymon(PM_STONE_GOLEM))) { + char kbuf[BUFSZ]; + + Sprintf(kbuf, "%s corpse", + an(mons[otmp->corpsenm].mname)); + pline("Snatching %s is a fatal mistake.", kbuf); + instapetrify(kbuf); + } + otmp = hold_another_object(otmp, "You drop %s!", + doname(otmp), (const char *)0); + break; + default: + /* to floor beneath mon */ + You("yank %s from %s %s!", the(onambuf), + s_suffix(mon_nam(mtmp)), mon_hand); + obj_no_longer_held(otmp); + place_object(otmp, mtmp->mx, mtmp->my); + stackobj(otmp); + break; + } + } else { + pline(msg_slipsfree); + } + wakeup(mtmp); + } else { + if (mtmp->m_ap_type && + !Protection_from_shape_changers && !sensemon(mtmp)) + stumble_onto_mimic(mtmp); + else You("flick your bullwhip towards %s.", mon_nam(mtmp)); + if (proficient) { + if (attack(mtmp)) return 1; + else pline(msg_snap); + } + } + + } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) { + /* it must be air -- water checked above */ + You("snap your whip through thin air."); + + } else { + pline(msg_snap); + + } + return 1; +} + + +static const char + not_enough_room[] = "There's not enough room here to use that.", + where_to_hit[] = "Where do you want to hit?", + cant_see_spot[] = "won't hit anything if you can't see that spot.", + cant_reach[] = "can't reach that spot from here."; + +/* Distance attacks by pole-weapons */ +STATIC_OVL int +use_pole (obj) + struct obj *obj; +{ + int res = 0, typ, max_range = 4, min_range = 4; + coord cc; + struct monst *mtmp; + + + /* Are you allowed to use the pole? */ + if (u.uswallow) { + pline(not_enough_room); + return (0); + } + if (obj != uwep) { + if (!wield_tool(obj, "swing")) return(0); + else res = 1; + } + /* assert(obj == uwep); */ + + /* Prompt for a location */ + pline(where_to_hit); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the spot to hit") < 0) + return 0; /* user pressed ESC */ + + /* Calculate range */ + typ = uwep_skill_type(); + if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) max_range = 4; + else if (P_SKILL(typ) == P_SKILLED) max_range = 5; + else max_range = 8; + if (distu(cc.x, cc.y) > max_range) { + pline("Too far!"); + return (res); + } else if (distu(cc.x, cc.y) < min_range) { + pline("Too close!"); + return (res); + } else if (!cansee(cc.x, cc.y) && + ((mtmp = m_at(cc.x, cc.y)) == (struct monst *)0 || + !canseemon(mtmp))) { + You(cant_see_spot); + return (res); + } else if (!couldsee(cc.x, cc.y)) { /* Eyes of the Overworld */ + You(cant_reach); + return res; + } + + /* Attack the monster there */ + if ((mtmp = m_at(cc.x, cc.y)) != (struct monst *)0) { + int oldhp = mtmp->mhp; + + bhitpos = cc; + check_caitiff(mtmp); + (void) thitmonst(mtmp, uwep); + /* check the monster's HP because thitmonst() doesn't return + * an indication of whether it hit. Not perfect (what if it's a + * non-silver weapon on a shade?) + */ + if (mtmp->mhp < oldhp) + u.uconduct.weaphit++; + } else + /* Now you know that nothing is there... */ + pline(nothing_happens); + return (1); +} + +STATIC_OVL int +use_cream_pie(obj) +struct obj *obj; +{ + boolean wasblind = Blind; + boolean wascreamed = u.ucreamed; + boolean several = FALSE; + + if (obj->quan > 1L) { + several = TRUE; + obj = splitobj(obj, 1L); + } + if (Hallucination) + You("give yourself a facial."); + else + pline("You immerse your %s in %s%s.", body_part(FACE), + several ? "one of " : "", + several ? makeplural(the(xname(obj))) : the(xname(obj))); + if(can_blnd((struct monst*)0, &youmonst, AT_WEAP, obj)) { + int blindinc = rnd(25); + u.ucreamed += blindinc; + make_blinded(Blinded + (long)blindinc, FALSE); + if (!Blind || (Blind && wasblind)) + pline("There's %ssticky goop all over your %s.", + wascreamed ? "more " : "", + body_part(FACE)); + else /* Blind && !wasblind */ + You_cant("see through all the sticky goop on your %s.", + body_part(FACE)); + } + if (obj->unpaid) { + verbalize("You used it, you bought it!"); + bill_dummy_object(obj); + } + obj_extract_self(obj); + delobj(obj); + return(0); +} + +STATIC_OVL int +use_grapple (obj) + struct obj *obj; +{ + int res = 0, typ, max_range = 4, tohit; + coord cc; + struct monst *mtmp; + struct obj *otmp; + + /* Are you allowed to use the hook? */ + if (u.uswallow) { + pline(not_enough_room); + return (0); + } + if (obj != uwep) { + if (!wield_tool(obj, "cast")) return(0); + else res = 1; + } + /* assert(obj == uwep); */ + + /* Prompt for a location */ + pline(where_to_hit); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the spot to hit") < 0) + return 0; /* user pressed ESC */ + + /* Calculate range */ + typ = uwep_skill_type(); + if (typ == P_NONE || P_SKILL(typ) <= P_BASIC) max_range = 4; + else if (P_SKILL(typ) == P_SKILLED) max_range = 5; + else max_range = 8; + if (distu(cc.x, cc.y) > max_range) { + pline("Too far!"); + return (res); + } else if (!cansee(cc.x, cc.y)) { + You(cant_see_spot); + return (res); + } else if (!couldsee(cc.x, cc.y)) { /* Eyes of the Overworld */ + You(cant_reach); + return res; + } + + /* What do you want to hit? */ + tohit = rn2(5); + if (typ != P_NONE && P_SKILL(typ) >= P_SKILLED) { + winid tmpwin = create_nhwindow(NHW_MENU); + anything any; + char buf[BUFSZ]; + menu_item *selected; + + any.a_void = 0; /* set all bits to zero */ + any.a_int = 1; /* use index+1 (cant use 0) as identifier */ + start_menu(tmpwin); + any.a_int++; + Sprintf(buf, "an object on the %s", surface(cc.x, cc.y)); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + any.a_int++; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + "a monster", MENU_UNSELECTED); + any.a_int++; + Sprintf(buf, "the %s", surface(cc.x, cc.y)); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + end_menu(tmpwin, "Aim for what?"); + tohit = rn2(4); + if (select_menu(tmpwin, PICK_ONE, &selected) > 0 && + rn2(P_SKILL(typ) > P_SKILLED ? 20 : 2)) + tohit = selected[0].item.a_int - 1; + free((genericptr_t)selected); + destroy_nhwindow(tmpwin); + } + + /* What did you hit? */ + switch (tohit) { + case 0: /* Trap */ + /* FIXME -- untrap needs to deal with non-adjacent traps */ + break; + case 1: /* Object */ + if ((otmp = level.objects[cc.x][cc.y]) != 0) { + You("snag an object from the %s!", surface(cc.x, cc.y)); + (void) pickup_object(otmp, 1L, FALSE); + /* If pickup fails, leave it alone */ + newsym(cc.x, cc.y); + return (1); + } + break; + case 2: /* Monster */ + if ((mtmp = m_at(cc.x, cc.y)) == (struct monst *)0) break; + if (verysmall(mtmp->data) && !rn2(4) && + enexto(&cc, u.ux, u.uy, (struct permonst *)0)) { + You("pull in %s!", mon_nam(mtmp)); + mtmp->mundetected = 0; + rloc_to(mtmp, cc.x, cc.y); + return (1); + } else if ((!bigmonst(mtmp->data) && !strongmonst(mtmp->data)) || + rn2(4)) { + (void) thitmonst(mtmp, uwep); + return (1); + } + /* FALL THROUGH */ + case 3: /* Surface */ + if (IS_AIR(levl[cc.x][cc.y].typ) || is_pool(cc.x, cc.y)) + pline_The("hook slices through the %s.", surface(cc.x, cc.y)); + else { + You("are yanked toward the %s!", surface(cc.x, cc.y)); + hurtle(sgn(cc.x-u.ux), sgn(cc.y-u.uy), 1, FALSE); + spoteffects(TRUE); + } + return (1); + default: /* Yourself (oops!) */ + if (P_SKILL(typ) <= P_BASIC) { + You("hook yourself!"); + losehp(rn1(10,10), "a grappling hook", KILLED_BY); + return (1); + } + break; + } + pline(nothing_happens); + return (1); +} + + +#define BY_OBJECT ((struct monst *)0) + +/* return 1 if the wand is broken, hence some time elapsed */ +STATIC_OVL int +do_break_wand(obj) + struct obj *obj; +{ + static const char nothing_else_happens[] = "But nothing else happens..."; + register int i, x, y; + register struct monst *mon; + int dmg, damage; + boolean affects_objects; + boolean shop_damage = FALSE; + int expltype = EXPL_MAGICAL; + char confirm[QBUFSZ], the_wand[BUFSZ], buf[BUFSZ]; + + Strcpy(the_wand, yname(obj)); + Sprintf(confirm, "Are you really sure you want to break %s?", + safe_qbuf("", sizeof("Are you really sure you want to break ?"), + the_wand, ysimple_name(obj), "the wand")); + if (yn(confirm) == 'n' ) return 0; + + if (nohands(youmonst.data)) { + You_cant("break %s without hands!", the_wand); + return 0; + } else if (ACURR(A_STR) < 10) { + You("don't have the strength to break %s!", the_wand); + return 0; + } + pline("Raising %s high above your %s, you break it in two!", + the_wand, body_part(HEAD)); + + /* [ALI] Do this first so that wand is removed from bill. Otherwise, + * the freeinv() below also hides it from setpaid() which causes problems. + */ + if (obj->unpaid) { + check_unpaid(obj); /* Extra charge for use */ + bill_dummy_object(obj); + } + + current_wand = obj; /* destroy_item might reset this */ + freeinv(obj); /* hide it from destroy_item instead... */ + setnotworn(obj); /* so we need to do this ourselves */ + + if (obj->spe <= 0) { + pline(nothing_else_happens); + goto discard_broken_wand; + } + obj->ox = u.ux; + obj->oy = u.uy; + dmg = obj->spe * 4; + affects_objects = FALSE; + + switch (obj->otyp) { + case WAN_WISHING: + case WAN_NOTHING: + case WAN_LOCKING: + case WAN_PROBING: + case WAN_ENLIGHTENMENT: + case WAN_OPENING: + case WAN_SECRET_DOOR_DETECTION: + pline(nothing_else_happens); + goto discard_broken_wand; + case WAN_DEATH: + case WAN_LIGHTNING: + dmg *= 4; + goto wanexpl; + case WAN_FIRE: + expltype = EXPL_FIERY; + case WAN_COLD: + if (expltype == EXPL_MAGICAL) expltype = EXPL_FROSTY; + dmg *= 2; + case WAN_MAGIC_MISSILE: + wanexpl: + explode(u.ux, u.uy, + (obj->otyp - WAN_MAGIC_MISSILE), dmg, WAND_CLASS, expltype); + makeknown(obj->otyp); /* explode described the effect */ + goto discard_broken_wand; + case WAN_STRIKING: + /* we want this before the explosion instead of at the very end */ + pline("A wall of force smashes down around you!"); + dmg = d(1 + obj->spe,6); /* normally 2d12 */ + case WAN_CANCELLATION: + case WAN_POLYMORPH: + case WAN_TELEPORTATION: + case WAN_UNDEAD_TURNING: + affects_objects = TRUE; + break; + default: + break; + } + + /* magical explosion and its visual effect occur before specific effects */ + explode(obj->ox, obj->oy, 0, rnd(dmg), WAND_CLASS, EXPL_MAGICAL); + + /* this makes it hit us last, so that we can see the action first */ + for (i = 0; i <= 8; i++) { + bhitpos.x = x = obj->ox + xdir[i]; + bhitpos.y = y = obj->oy + ydir[i]; + if (!isok(x,y)) continue; + + if (obj->otyp == WAN_DIGGING) { + if(dig_check(BY_OBJECT, FALSE, x, y)) { + if (IS_WALL(levl[x][y].typ) || IS_DOOR(levl[x][y].typ)) { + /* normally, pits and holes don't anger guards, but they + * do if it's a wall or door that's being dug */ + watch_dig((struct monst *)0, x, y, TRUE); + if (*in_rooms(x,y,SHOPBASE)) shop_damage = TRUE; + } + digactualhole(x, y, BY_OBJECT, + (rn2(obj->spe) < 3 || !Can_dig_down(&u.uz)) ? + PIT : HOLE); + } + continue; + } else if(obj->otyp == WAN_CREATE_MONSTER) { + /* u.ux,u.uy creates it near you--x,y might create it in rock */ + (void) makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS); + continue; + } else { + if (x == u.ux && y == u.uy) { + /* teleport objects first to avoid race with tele control and + autopickup. Other wand/object effects handled after + possible wand damage is assessed */ + if (obj->otyp == WAN_TELEPORTATION && + affects_objects && level.objects[x][y]) { + (void) bhitpile(obj, bhito, x, y); + if (flags.botl) bot(); /* potion effects */ + } + damage = zapyourself(obj, FALSE); + if (damage) { + Sprintf(buf, "killed %sself by breaking a wand", uhim()); + losehp(damage, buf, NO_KILLER_PREFIX); + } + if (flags.botl) bot(); /* blindness */ + } else if ((mon = m_at(x, y)) != 0) { + (void) bhitm(mon, obj); + /* if (flags.botl) bot(); */ + } + if (affects_objects && level.objects[x][y]) { + (void) bhitpile(obj, bhito, x, y); + if (flags.botl) bot(); /* potion effects */ + } + } + } + + /* Note: if player fell thru, this call is a no-op. + Damage is handled in digactualhole in that case */ + if (shop_damage) pay_for_damage("dig into", FALSE); + + if (obj->otyp == WAN_LIGHT) + litroom(TRUE, obj); /* only needs to be done once */ + + discard_broken_wand: + obj = current_wand; /* [see dozap() and destroy_item()] */ + current_wand = 0; + if (obj) + delobj(obj); + nomul(0); + return 1; +} + +STATIC_OVL boolean +uhave_graystone() +{ + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) + if(is_graystone(otmp)) + return TRUE; + return FALSE; +} + +STATIC_OVL void +add_class(cl, class) +char *cl; +char class; +{ + char tmp[2]; + tmp[0] = class; + tmp[1] = '\0'; + Strcat(cl, tmp); +} + +int +doapply() +{ + struct obj *obj; + register int res = 1; + char class_list[MAXOCLASSES+2]; + + if(check_capacity((char *)0)) return (0); + + if (carrying(POT_OIL) || uhave_graystone()) + Strcpy(class_list, tools_too); + else + Strcpy(class_list, tools); + if (carrying(CREAM_PIE) || carrying(EUCALYPTUS_LEAF)) + add_class(class_list, FOOD_CLASS); + + obj = getobj(class_list, "use or apply"); + if(!obj) return 0; + + if (obj->oartifact && !touch_artifact(obj, &youmonst)) + return 1; /* evading your grasp costs a turn; just be + grateful that you don't drop it as well */ + + if (obj->oclass == WAND_CLASS) + return do_break_wand(obj); + + switch(obj->otyp){ + case BLINDFOLD: + case LENSES: + if (obj == ublindf) { + if (!cursed(obj)) Blindf_off(obj); + } else if (!ublindf) + Blindf_on(obj); + else You("are already %s.", + ublindf->otyp == TOWEL ? "covered by a towel" : + ublindf->otyp == BLINDFOLD ? "wearing a blindfold" : + "wearing lenses"); + break; + case CREAM_PIE: + res = use_cream_pie(obj); + break; + case BULLWHIP: + res = use_whip(obj); + break; + case GRAPPLING_HOOK: + res = use_grapple(obj); + break; + case LARGE_BOX: + case CHEST: + case ICE_BOX: + case SACK: + case BAG_OF_HOLDING: + case OILSKIN_SACK: + res = use_container(obj, 1); + break; + case BAG_OF_TRICKS: + bagotricks(obj); + break; + case CAN_OF_GREASE: + use_grease(obj); + break; + case LOCK_PICK: +#ifdef TOURIST + case CREDIT_CARD: +#endif + case SKELETON_KEY: + (void) pick_lock(obj); + break; + case PICK_AXE: + case DWARVISH_MATTOCK: + res = use_pick_axe(obj); + break; + case TINNING_KIT: + use_tinning_kit(obj); + break; + case LEASH: + use_leash(obj); + break; +#ifdef STEED + case SADDLE: + res = use_saddle(obj); + break; +#endif + case MAGIC_WHISTLE: + use_magic_whistle(obj); + break; + case TIN_WHISTLE: + use_whistle(obj); + break; + case EUCALYPTUS_LEAF: + /* MRKR: Every Australian knows that a gum leaf makes an */ + /* excellent whistle, especially if your pet is a */ + /* tame kangaroo named Skippy. */ + if (obj->blessed) { + use_magic_whistle(obj); + /* sometimes the blessing will be worn off */ + if (!rn2(49)) { + if (!Blind) { + char buf[BUFSZ]; + + pline("%s %s %s.", Shk_Your(buf, obj), + aobjnam(obj, "glow"), hcolor("brown")); + obj->bknown = 1; + } + unbless(obj); + } + } else { + use_whistle(obj); + } + break; + case STETHOSCOPE: + res = use_stethoscope(obj); + break; + case MIRROR: + res = use_mirror(obj); + break; + case BELL: + case BELL_OF_OPENING: + use_bell(&obj); + break; + case CANDELABRUM_OF_INVOCATION: + use_candelabrum(obj); + break; + case WAX_CANDLE: + case TALLOW_CANDLE: + use_candle(&obj); + break; + case OIL_LAMP: + case MAGIC_LAMP: + case BRASS_LANTERN: + use_lamp(obj); + break; + case POT_OIL: + light_cocktail(obj); + break; +#ifdef TOURIST + case EXPENSIVE_CAMERA: + res = use_camera(obj); + break; +#endif + case TOWEL: + res = use_towel(obj); + break; + case CRYSTAL_BALL: + use_crystal_ball(obj); + break; + case MAGIC_MARKER: + res = dowrite(obj); + break; + case TIN_OPENER: + if(!carrying(TIN)) { + You("have no tin to open."); + goto xit; + } + You("cannot open a tin without eating or discarding its contents."); + if(flags.verbose) + pline("In order to eat, use the 'e' command."); + if(obj != uwep) + pline("Opening the tin will be much easier if you wield the tin opener."); + goto xit; + + case FIGURINE: + use_figurine(&obj); + break; + case UNICORN_HORN: + use_unicorn_horn(obj); + break; + case WOODEN_FLUTE: + case MAGIC_FLUTE: + case TOOLED_HORN: + case FROST_HORN: + case FIRE_HORN: + case WOODEN_HARP: + case MAGIC_HARP: + case BUGLE: + case LEATHER_DRUM: + case DRUM_OF_EARTHQUAKE: + res = do_play_instrument(obj); + break; + case HORN_OF_PLENTY: /* not a musical instrument */ + if (obj->spe > 0) { + struct obj *otmp; + const char *what; + + consume_obj_charge(obj, TRUE); + if (!rn2(13)) { + otmp = mkobj(POTION_CLASS, FALSE); + if (objects[otmp->otyp].oc_magic) do { + otmp->otyp = rnd_class(POT_BOOZE, POT_WATER); + } while (otmp->otyp == POT_SICKNESS); + what = "A potion"; + } else { + otmp = mkobj(FOOD_CLASS, FALSE); + if (otmp->otyp == FOOD_RATION && !rn2(7)) + otmp->otyp = LUMP_OF_ROYAL_JELLY; + what = "Some food"; + } + pline("%s spills out.", what); + otmp->blessed = obj->blessed; + otmp->cursed = obj->cursed; + otmp->owt = weight(otmp); + otmp = hold_another_object(otmp, u.uswallow ? + "Oops! %s out of your reach!" : + (Is_airlevel(&u.uz) || + Is_waterlevel(&u.uz) || + levl[u.ux][u.uy].typ < IRONBARS || + levl[u.ux][u.uy].typ >= ICE) ? + "Oops! %s away from you!" : + "Oops! %s to the floor!", + The(aobjnam(otmp, "slip")), + (const char *)0); + makeknown(HORN_OF_PLENTY); + } else + pline(nothing_happens); + break; + case LAND_MINE: + case BEARTRAP: + use_trap(obj); + break; + case FLINT: + case LUCKSTONE: + case LOADSTONE: + case TOUCHSTONE: + use_stone(obj); + break; + default: + /* Pole-weapons can strike at a distance */ + if (is_pole(obj)) { + res = use_pole(obj); + break; + } else if (is_pick(obj) || is_axe(obj)) { + res = use_pick_axe(obj); + break; + } + pline("Sorry, I don't know how to use that."); + xit: + nomul(0); + return 0; + } + if (res && obj && obj->oartifact) arti_speak(obj); + nomul(0); + return res; +} + +/* Keep track of unfixable troubles for purposes of messages saying you feel + * great. + */ +int +unfixable_trouble_count(is_horn) + boolean is_horn; +{ + int unfixable_trbl = 0; + + if (Stoned) unfixable_trbl++; + if (Strangled) unfixable_trbl++; + if (Wounded_legs +#ifdef STEED + && !u.usteed +#endif + ) unfixable_trbl++; + if (Slimed) unfixable_trbl++; + /* lycanthropy is not desirable, but it doesn't actually make you feel + bad */ + + /* we'll assume that intrinsic stunning from being a bat/stalker + doesn't make you feel bad */ + if (!is_horn) { + if (Confusion) unfixable_trbl++; + if (Sick) unfixable_trbl++; + if (HHallucination) unfixable_trbl++; + if (Vomiting) unfixable_trbl++; + if (HStun) unfixable_trbl++; + } + return unfixable_trbl; +} + +#endif /* OVLB */ + +/*apply.c*/ diff --git a/src/artifact.c b/src/artifact.c new file mode 100644 index 0000000..ef27bd5 --- /dev/null +++ b/src/artifact.c @@ -0,0 +1,1461 @@ +/* SCCS Id: @(#)artifact.c 3.4 2003/08/11 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "artifact.h" +#ifdef OVLB +#include "artilist.h" +#else +STATIC_DCL struct artifact artilist[]; +#endif +/* + * Note: both artilist[] and artiexist[] have a dummy element #0, + * so loops over them should normally start at #1. The primary + * exception is the save & restore code, which doesn't care about + * the contents, just the total size. + */ + +extern boolean notonhead; /* for long worms */ + +#define get_artifact(o) \ + (((o)&&(o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0) + +STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *)); +STATIC_DCL int FDECL(arti_invoke, (struct obj*)); +STATIC_DCL boolean FDECL(Mb_hit, (struct monst *magr,struct monst *mdef, + struct obj *,int *,int,BOOLEAN_P,char *)); + +/* The amount added to the victim's total hit points to insure that the + victim will be killed even after damage bonus/penalty adjustments. + Most such penalties are small, and 200 is plenty; the exception is + half physical damage. 3.3.1 and previous versions tried to use a very + large number to account for this case; now, we just compute the fatal + damage by adding it to 2 times the total hit points instead of 1 time. + Note: this will still break if they have more than about half the number + of hit points that will fit in a 15 bit integer. */ +#define FATAL_DAMAGE_MODIFIER 200 + +#ifndef OVLB +STATIC_DCL int spec_dbon_applies; +STATIC_DCL xchar artidisco[NROFARTIFACTS]; +#else /* OVLB */ +/* coordinate effects from spec_dbon() with messages in artifact_hit() */ +STATIC_OVL int spec_dbon_applies = 0; + +/* flags including which artifacts have already been created */ +static boolean artiexist[1+NROFARTIFACTS+1]; +/* and a discovery list for them (no dummy first entry here) */ +STATIC_OVL xchar artidisco[NROFARTIFACTS]; + +STATIC_DCL void NDECL(hack_artifacts); +STATIC_DCL boolean FDECL(attacks, (int,struct obj *)); + +/* handle some special cases; must be called after u_init() */ +STATIC_OVL void +hack_artifacts() +{ + struct artifact *art; + int alignmnt = aligns[flags.initalign].value; + + /* Fix up the alignments of "gift" artifacts */ + for (art = artilist+1; art->otyp; art++) + if (art->role == Role_switch && art->alignment != A_NONE) + art->alignment = alignmnt; + + /* Excalibur can be used by any lawful character, not just knights */ + if (!Role_if(PM_KNIGHT)) + artilist[ART_EXCALIBUR].role = NON_PM; + + /* Fix up the quest artifact */ + if (urole.questarti) { + artilist[urole.questarti].alignment = alignmnt; + artilist[urole.questarti].role = Role_switch; + } + return; +} + +/* zero out the artifact existence list */ +void +init_artifacts() +{ + (void) memset((genericptr_t) artiexist, 0, sizeof artiexist); + (void) memset((genericptr_t) artidisco, 0, sizeof artidisco); + hack_artifacts(); +} + +void +save_artifacts(fd) +int fd; +{ + bwrite(fd, (genericptr_t) artiexist, sizeof artiexist); + bwrite(fd, (genericptr_t) artidisco, sizeof artidisco); +} + +void +restore_artifacts(fd) +int fd; +{ + mread(fd, (genericptr_t) artiexist, sizeof artiexist); + mread(fd, (genericptr_t) artidisco, sizeof artidisco); + hack_artifacts(); /* redo non-saved special cases */ +} + +const char * +artiname(artinum) +int artinum; +{ + if (artinum <= 0 || artinum > NROFARTIFACTS) return(""); + return(artilist[artinum].name); +} + +/* + Make an artifact. If a specific alignment is specified, then an object of + the appropriate alignment is created from scratch, or 0 is returned if + none is available. (If at least one aligned artifact has already been + given, then unaligned ones also become eligible for this.) + If no alignment is given, then 'otmp' is converted + into an artifact of matching type, or returned as-is if that's not possible. + For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);'' + for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''. + */ +struct obj * +mk_artifact(otmp, alignment) +struct obj *otmp; /* existing object; ignored if alignment specified */ +aligntyp alignment; /* target alignment, or A_NONE */ +{ + const struct artifact *a; + int n, m; + boolean by_align = (alignment != A_NONE); + short o_typ = (by_align || !otmp) ? 0 : otmp->otyp; + boolean unique = !by_align && otmp && objects[o_typ].oc_unique; + short eligible[NROFARTIFACTS]; + + /* gather eligible artifacts */ + for (n = 0, a = artilist+1, m = 1; a->otyp; a++, m++) + if ((!by_align ? a->otyp == o_typ : + (a->alignment == alignment || + (a->alignment == A_NONE && u.ugifts > 0))) && + (!(a->spfx & SPFX_NOGEN) || unique) && !artiexist[m]) { + if (by_align && a->race != NON_PM && race_hostile(&mons[a->race])) + continue; /* skip enemies' equipment */ + else if (by_align && Role_if(a->role)) + goto make_artif; /* 'a' points to the desired one */ + else + eligible[n++] = m; + } + + if (n) { /* found at least one candidate */ + m = eligible[rn2(n)]; /* [0..n-1] */ + a = &artilist[m]; + + /* make an appropriate object if necessary, then christen it */ +make_artif: if (by_align) otmp = mksobj((int)a->otyp, TRUE, FALSE); + otmp = oname(otmp, a->name); + otmp->oartifact = m; + artiexist[m] = TRUE; + } else { + /* nothing appropriate could be found; return the original object */ + if (by_align) otmp = 0; /* (there was no original object) */ + } + return otmp; +} + +/* + * Returns the full name (with articles and correct capitalization) of an + * artifact named "name" if one exists, or NULL, it not. + * The given name must be rather close to the real name for it to match. + * The object type of the artifact is returned in otyp if the return value + * is non-NULL. + */ +const char* +artifact_name(name, otyp) +const char *name; +short *otyp; +{ + register const struct artifact *a; + register const char *aname; + + if(!strncmpi(name, "the ", 4)) name += 4; + + for (a = artilist+1; a->otyp; a++) { + aname = a->name; + if(!strncmpi(aname, "the ", 4)) aname += 4; + if(!strcmpi(name, aname)) { + *otyp = a->otyp; + return a->name; + } + } + + return (char *)0; +} + +boolean +exist_artifact(otyp, name) +register int otyp; +register const char *name; +{ + register const struct artifact *a; + register boolean *arex; + + if (otyp && *name) + for (a = artilist+1,arex = artiexist+1; a->otyp; a++,arex++) + if ((int) a->otyp == otyp && !strcmp(a->name, name)) + return *arex; + return FALSE; +} + +void +artifact_exists(otmp, name, mod) +register struct obj *otmp; +register const char *name; +register boolean mod; +{ + register const struct artifact *a; + + if (otmp && *name) + for (a = artilist+1; a->otyp; a++) + if (a->otyp == otmp->otyp && !strcmp(a->name, name)) { + register int m = a - artilist; + otmp->oartifact = (char)(mod ? m : 0); + otmp->age = 0; + if(otmp->otyp == RIN_INCREASE_DAMAGE) + otmp->spe = 0; + artiexist[m] = mod; + break; + } + return; +} + +int +nartifact_exist() +{ + int a = 0; + int n = SIZE(artiexist); + + while(n > 1) + if(artiexist[--n]) a++; + + return a; +} +#endif /* OVLB */ +#ifdef OVL0 + +boolean +spec_ability(otmp, abil) +struct obj *otmp; +unsigned long abil; +{ + const struct artifact *arti = get_artifact(otmp); + + return((boolean)(arti && (arti->spfx & abil))); +} + +/* used so that callers don't need to known about SPFX_ codes */ +boolean +confers_luck(obj) +struct obj *obj; +{ + /* might as well check for this too */ + if (obj->otyp == LUCKSTONE) return TRUE; + + return (obj->oartifact && spec_ability(obj, SPFX_LUCK)); +} + +/* used to check whether a monster is getting reflection from an artifact */ +boolean +arti_reflects(obj) +struct obj *obj; +{ + const struct artifact *arti = get_artifact(obj); + + if (arti) { + /* while being worn */ + if ((obj->owornmask & ~W_ART) && (arti->spfx & SPFX_REFLECT)) + return TRUE; + /* just being carried */ + if (arti->cspfx & SPFX_REFLECT) return TRUE; + } + return FALSE; +} + +#endif /* OVL0 */ +#ifdef OVLB + +boolean +restrict_name(otmp, name) /* returns 1 if name is restricted for otmp->otyp */ +register struct obj *otmp; +register const char *name; +{ + register const struct artifact *a; + register const char *aname; + + if (!*name) return FALSE; + if (!strncmpi(name, "the ", 4)) name += 4; + + /* Since almost every artifact is SPFX_RESTR, it doesn't cost + us much to do the string comparison before the spfx check. + Bug fix: don't name multiple elven daggers "Sting". + */ + for (a = artilist+1; a->otyp; a++) { + if (a->otyp != otmp->otyp) continue; + aname = a->name; + if (!strncmpi(aname, "the ", 4)) aname += 4; + if (!strcmp(aname, name)) + return ((boolean)((a->spfx & (SPFX_NOGEN|SPFX_RESTR)) != 0 || + otmp->quan > 1L)); + } + + return FALSE; +} + +STATIC_OVL boolean +attacks(adtyp, otmp) +register int adtyp; +register struct obj *otmp; +{ + register const struct artifact *weap; + + if ((weap = get_artifact(otmp)) != 0) + return((boolean)(weap->attk.adtyp == adtyp)); + return FALSE; +} + +boolean +defends(adtyp, otmp) +register int adtyp; +register struct obj *otmp; +{ + register const struct artifact *weap; + + if ((weap = get_artifact(otmp)) != 0) + return((boolean)(weap->defn.adtyp == adtyp)); + return FALSE; +} + +/* used for monsters */ +boolean +protects(adtyp, otmp) +int adtyp; +struct obj *otmp; +{ + register const struct artifact *weap; + + if ((weap = get_artifact(otmp)) != 0) + return (boolean)(weap->cary.adtyp == adtyp); + return FALSE; +} + +/* + * a potential artifact has just been worn/wielded/picked-up or + * unworn/unwielded/dropped. Pickup/drop only set/reset the W_ART mask. + */ +void +set_artifact_intrinsic(otmp,on,wp_mask) +register struct obj *otmp; +boolean on; +long wp_mask; +{ + long *mask = 0; + register const struct artifact *oart = get_artifact(otmp); + uchar dtyp; + long spfx; + + if (!oart) return; + + /* effects from the defn field */ + dtyp = (wp_mask != W_ART) ? oart->defn.adtyp : oart->cary.adtyp; + + if (dtyp == AD_FIRE) + mask = &EFire_resistance; + else if (dtyp == AD_COLD) + mask = &ECold_resistance; + else if (dtyp == AD_ELEC) + mask = &EShock_resistance; + else if (dtyp == AD_MAGM) + mask = &EAntimagic; + else if (dtyp == AD_DISN) + mask = &EDisint_resistance; + else if (dtyp == AD_DRST) + mask = &EPoison_resistance; + + if (mask && wp_mask == W_ART && !on) { + /* find out if some other artifact also confers this intrinsic */ + /* if so, leave the mask alone */ + register struct obj* obj; + for(obj = invent; obj; obj = obj->nobj) + if(obj != otmp && obj->oartifact) { + register const struct artifact *art = get_artifact(obj); + if(art->cary.adtyp == dtyp) { + mask = (long *) 0; + break; + } + } + } + if (mask) { + if (on) *mask |= wp_mask; + else *mask &= ~wp_mask; + } + + /* intrinsics from the spfx field; there could be more than one */ + spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx; + if(spfx && wp_mask == W_ART && !on) { + /* don't change any spfx also conferred by other artifacts */ + register struct obj* obj; + for(obj = invent; obj; obj = obj->nobj) + if(obj != otmp && obj->oartifact) { + register const struct artifact *art = get_artifact(obj); + spfx &= ~art->cspfx; + } + } + + if (spfx & SPFX_SEARCH) { + if(on) ESearching |= wp_mask; + else ESearching &= ~wp_mask; + } + if (spfx & SPFX_HALRES) { + /* make_hallucinated must (re)set the mask itself to get + * the display right */ + /* restoring needed because this is the only artifact intrinsic + * that can print a message--need to guard against being printed + * when restoring a game + */ + (void) make_hallucinated((long)!on, restoring ? FALSE : TRUE, wp_mask); + } + if (spfx & SPFX_ESP) { + if(on) ETelepat |= wp_mask; + else ETelepat &= ~wp_mask; + see_monsters(); + } + if (spfx & SPFX_STLTH) { + if (on) EStealth |= wp_mask; + else EStealth &= ~wp_mask; + } + if (spfx & SPFX_REGEN) { + if (on) ERegeneration |= wp_mask; + else ERegeneration &= ~wp_mask; + } + if (spfx & SPFX_TCTRL) { + if (on) ETeleport_control |= wp_mask; + else ETeleport_control &= ~wp_mask; + } + if (spfx & SPFX_WARN) { + if (spec_m2(otmp)) { + if (on) { + EWarn_of_mon |= wp_mask; + flags.warntype |= spec_m2(otmp); + } else { + EWarn_of_mon &= ~wp_mask; + flags.warntype &= ~spec_m2(otmp); + } + see_monsters(); + } else { + if (on) EWarning |= wp_mask; + else EWarning &= ~wp_mask; + } + } + if (spfx & SPFX_EREGEN) { + if (on) EEnergy_regeneration |= wp_mask; + else EEnergy_regeneration &= ~wp_mask; + } + if (spfx & SPFX_HSPDAM) { + if (on) EHalf_spell_damage |= wp_mask; + else EHalf_spell_damage &= ~wp_mask; + } + if (spfx & SPFX_HPHDAM) { + if (on) EHalf_physical_damage |= wp_mask; + else EHalf_physical_damage &= ~wp_mask; + } + if (spfx & SPFX_XRAY) { + /* this assumes that no one else is using xray_range */ + if (on) u.xray_range = 3; + else u.xray_range = -1; + vision_full_recalc = 1; + } + if ((spfx & SPFX_REFLECT) && (wp_mask & W_WEP)) { + if (on) EReflecting |= wp_mask; + else EReflecting &= ~wp_mask; + } + + if(wp_mask == W_ART && !on && oart->inv_prop) { + /* might have to turn off invoked power too */ + if (oart->inv_prop <= LAST_PROP && + (u.uprops[oart->inv_prop].extrinsic & W_ARTI)) + (void) arti_invoke(otmp); + } +} + +/* + * creature (usually player) tries to touch (pick up or wield) an artifact obj. + * Returns 0 if the object refuses to be touched. + * This routine does not change any object chains. + * Ignores such things as gauntlets, assuming the artifact is not + * fooled by such trappings. + */ +int +touch_artifact(obj,mon) + struct obj *obj; + struct monst *mon; +{ + register const struct artifact *oart = get_artifact(obj); + boolean badclass, badalign, self_willed, yours; + + if(!oart) return 1; + + yours = (mon == &youmonst); + /* all quest artifacts are self-willed; it this ever changes, `badclass' + will have to be extended to explicitly include quest artifacts */ + self_willed = ((oart->spfx & SPFX_INTEL) != 0); + if (yours) { + badclass = self_willed && + ((oart->role != NON_PM && !Role_if(oart->role)) || + (oart->race != NON_PM && !Race_if(oart->race))); + badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE && + (oart->alignment != u.ualign.type || u.ualign.record < 0); + } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) { + badclass = self_willed && + oart->role != NON_PM && oart != &artilist[ART_EXCALIBUR]; + badalign = (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE && + (oart->alignment != sgn(mon->data->maligntyp)); + } else { /* an M3_WANTSxxx monster or a fake player */ + /* special monsters trying to take the Amulet, invocation tools or + quest item can touch anything except for `spec_applies' artifacts */ + badclass = badalign = FALSE; + } + /* weapons which attack specific categories of monsters are + bad for them even if their alignments happen to match */ + if (!badalign && (oart->spfx & SPFX_DBONUS) != 0) { + struct artifact tmp; + + tmp = *oart; + tmp.spfx &= SPFX_DBONUS; + badalign = !!spec_applies(&tmp, mon); + } + + if (((badclass || badalign) && self_willed) || + (badalign && (!yours || !rn2(4)))) { + int dmg; + char buf[BUFSZ]; + + if (!yours) return 0; + You("are blasted by %s power!", s_suffix(the(xname(obj)))); + dmg = d((Antimagic ? 2 : 4), (self_willed ? 10 : 4)); + Sprintf(buf, "touching %s", oart->name); + losehp(dmg, buf, KILLED_BY); + exercise(A_WIS, FALSE); + } + + /* can pick it up unless you're totally non-synch'd with the artifact */ + if (badclass && badalign && self_willed) { + if (yours) pline("%s your grasp!", Tobjnam(obj, "evade")); + return 0; + } + + return 1; +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* decide whether an artifact's special attacks apply against mtmp */ +STATIC_OVL int +spec_applies(weap, mtmp) +register const struct artifact *weap; +struct monst *mtmp; +{ + struct permonst *ptr; + boolean yours; + + if(!(weap->spfx & (SPFX_DBONUS | SPFX_ATTK))) + return(weap->attk.adtyp == AD_PHYS); + + yours = (mtmp == &youmonst); + ptr = mtmp->data; + + if (weap->spfx & SPFX_DMONS) { + return (ptr == &mons[(int)weap->mtype]); + } else if (weap->spfx & SPFX_DCLAS) { + return (weap->mtype == (unsigned long)ptr->mlet); + } else if (weap->spfx & SPFX_DFLAG1) { + return ((ptr->mflags1 & weap->mtype) != 0L); + } else if (weap->spfx & SPFX_DFLAG2) { + return ((ptr->mflags2 & weap->mtype) || (yours && + ((!Upolyd && (urace.selfmask & weap->mtype)) || + ((weap->mtype & M2_WERE) && u.ulycn >= LOW_PM)))); + } else if (weap->spfx & SPFX_DALIGN) { + return yours ? (u.ualign.type != weap->alignment) : + (ptr->maligntyp == A_NONE || + sgn(ptr->maligntyp) != weap->alignment); + } else if (weap->spfx & SPFX_ATTK) { + struct obj *defending_weapon = (yours ? uwep : MON_WEP(mtmp)); + + if (defending_weapon && defending_weapon->oartifact && + defends((int)weap->attk.adtyp, defending_weapon)) + return FALSE; + switch(weap->attk.adtyp) { + case AD_FIRE: + return !(yours ? Fire_resistance : resists_fire(mtmp)); + case AD_COLD: + return !(yours ? Cold_resistance : resists_cold(mtmp)); + case AD_ELEC: + return !(yours ? Shock_resistance : resists_elec(mtmp)); + case AD_MAGM: + case AD_STUN: + return !(yours ? Antimagic : (rn2(100) < ptr->mr)); + case AD_DRST: + return !(yours ? Poison_resistance : resists_poison(mtmp)); + case AD_DRLI: + return !(yours ? Drain_resistance : resists_drli(mtmp)); + case AD_STON: + return !(yours ? Stone_resistance : resists_ston(mtmp)); + default: impossible("Weird weapon special attack."); + } + } + return(0); +} + +/* return the M2 flags of monster that an artifact's special attacks apply against */ +long +spec_m2(otmp) +struct obj *otmp; +{ + register const struct artifact *artifact = get_artifact(otmp); + if (artifact) + return artifact->mtype; + return 0L; +} + +/* special attack bonus */ +int +spec_abon(otmp, mon) +struct obj *otmp; +struct monst *mon; +{ + register const struct artifact *weap = get_artifact(otmp); + + /* no need for an extra check for `NO_ATTK' because this will + always return 0 for any artifact which has that attribute */ + + if (weap && weap->attk.damn && spec_applies(weap, mon)) + return rnd((int)weap->attk.damn); + return 0; +} + +/* special damage bonus */ +int +spec_dbon(otmp, mon, tmp) +struct obj *otmp; +struct monst *mon; +int tmp; +{ + register const struct artifact *weap = get_artifact(otmp); + + if (!weap || (weap->attk.adtyp == AD_PHYS && /* check for `NO_ATTK' */ + weap->attk.damn == 0 && weap->attk.damd == 0)) + spec_dbon_applies = FALSE; + else + spec_dbon_applies = spec_applies(weap, mon); + + if (spec_dbon_applies) + return weap->attk.damd ? rnd((int)weap->attk.damd) : max(tmp,1); + return 0; +} + +/* add identified artifact to discoveries list */ +void +discover_artifact(m) +xchar m; +{ + int i; + + /* look for this artifact in the discoveries list; + if we hit an empty slot then it's not present, so add it */ + for (i = 0; i < NROFARTIFACTS; i++) + if (artidisco[i] == 0 || artidisco[i] == m) { + artidisco[i] = m; + return; + } + /* there is one slot per artifact, so we should never reach the + end without either finding the artifact or an empty slot... */ + impossible("couldn't discover artifact (%d)", (int)m); +} + +/* used to decide whether an artifact has been fully identified */ +boolean +undiscovered_artifact(m) +xchar m; +{ + int i; + + /* look for this artifact in the discoveries list; + if we hit an empty slot then it's undiscovered */ + for (i = 0; i < NROFARTIFACTS; i++) + if (artidisco[i] == m) + return FALSE; + else if (artidisco[i] == 0) + break; + return TRUE; +} + +/* display a list of discovered artifacts; return their count */ +int +disp_artifact_discoveries(tmpwin) +winid tmpwin; /* supplied by dodiscover() */ +{ + int i, m, otyp; + char buf[BUFSZ]; + + for (i = 0; i < NROFARTIFACTS; i++) { + if (artidisco[i] == 0) break; /* empty slot implies end of list */ + if (i == 0) putstr(tmpwin, iflags.menu_headings, "Artifacts"); + m = artidisco[i]; + otyp = artilist[m].otyp; + Sprintf(buf, " %s [%s %s]", artiname(m), + align_str(artilist[m].alignment), simple_typename(otyp)); + putstr(tmpwin, 0, buf); + } + return i; +} + +#endif /* OVL1 */ + +#ifdef OVLB + + + /* + * Magicbane's intrinsic magic is incompatible with normal + * enchantment magic. Thus, its effects have a negative + * dependence on spe. Against low mr victims, it typically + * does "double athame" damage, 2d4. Occasionally, it will + * cast unbalancing magic which effectively averages out to + * 4d4 damage (3d4 against high mr victims), for spe = 0. + * + * Prior to 3.4.1, the cancel (aka purge) effect always + * included the scare effect too; now it's one or the other. + * Likewise, the stun effect won't be combined with either + * of those two; it will be chosen separately or possibly + * used as a fallback when scare or cancel fails. + * + * [Historical note: a change to artifact_hit() for 3.4.0 + * unintentionally made all of Magicbane's special effects + * be blocked if the defender successfully saved against a + * stun attack. As of 3.4.1, those effects can occur but + * will be slightly less likely than they were in 3.3.x.] + */ +#define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */ +static const char * const mb_verb[2][4] = { + { "probe", "stun", "scare", "cancel" }, + { "prod", "amaze", "tickle", "purge" }, +}; +#define MB_INDEX_PROBE 0 +#define MB_INDEX_STUN 1 +#define MB_INDEX_SCARE 2 +#define MB_INDEX_CANCEL 3 + +/* called when someone is being hit by Magicbane */ +STATIC_OVL boolean +Mb_hit(magr, mdef, mb, dmgptr, dieroll, vis, hittee) +struct monst *magr, *mdef; /* attacker and defender */ +struct obj *mb; /* Magicbane */ +int *dmgptr; /* extra damage target will suffer */ +int dieroll; /* d20 that has already scored a hit */ +boolean vis; /* whether the action can be seen */ +char *hittee; /* target's name: "you" or mon_nam(mdef) */ +{ + struct permonst *old_uasmon; + const char *verb; + boolean youattack = (magr == &youmonst), + youdefend = (mdef == &youmonst), + resisted = FALSE, do_stun, do_confuse, result; + int attack_indx, scare_dieroll = MB_MAX_DIEROLL / 2; + + result = FALSE; /* no message given yet */ + /* the most severe effects are less likely at higher enchantment */ + if (mb->spe >= 3) + scare_dieroll /= (1 << (mb->spe / 3)); + /* if target successfully resisted the artifact damage bonus, + reduce overall likelihood of the assorted special effects */ + if (!spec_dbon_applies) dieroll += 1; + + /* might stun even when attempting a more severe effect, but + in that case it will only happen if the other effect fails; + extra damage will apply regardless; 3.4.1: sometimes might + just probe even when it hasn't been enchanted */ + do_stun = (max(mb->spe,0) < rn2(spec_dbon_applies ? 11 : 7)); + + /* the special effects also boost physical damage; increments are + generally cumulative, but since the stun effect is based on a + different criterium its damage might not be included; the base + damage is either 1d4 (athame) or 2d4 (athame+spec_dbon) depending + on target's resistance check against AD_STUN (handled by caller) + [note that a successful save against AD_STUN doesn't actually + prevent the target from ending up stunned] */ + attack_indx = MB_INDEX_PROBE; + *dmgptr += rnd(4); /* (2..3)d4 */ + if (do_stun) { + attack_indx = MB_INDEX_STUN; + *dmgptr += rnd(4); /* (3..4)d4 */ + } + if (dieroll <= scare_dieroll) { + attack_indx = MB_INDEX_SCARE; + *dmgptr += rnd(4); /* (3..5)d4 */ + } + if (dieroll <= (scare_dieroll / 2)) { + attack_indx = MB_INDEX_CANCEL; + *dmgptr += rnd(4); /* (4..6)d4 */ + } + + /* give the hit message prior to inflicting the effects */ + verb = mb_verb[!!Hallucination][attack_indx]; + if (youattack || youdefend || vis) { + result = TRUE; + pline_The("magic-absorbing blade %s %s!", + vtense((const char *)0, verb), hittee); + /* assume probing has some sort of noticeable feedback + even if it is being done by one monster to another */ + if (attack_indx == MB_INDEX_PROBE && !canspotmon(mdef)) + map_invisible(mdef->mx, mdef->my); + } + + /* now perform special effects */ + switch (attack_indx) { + case MB_INDEX_CANCEL: + old_uasmon = youmonst.data; + /* No mdef->mcan check: even a cancelled monster can be polymorphed + * into a golem, and the "cancel" effect acts as if some magical + * energy remains in spellcasting defenders to be absorbed later. + */ + if (!cancel_monst(mdef, mb, youattack, FALSE, FALSE)) { + resisted = TRUE; + } else { + do_stun = FALSE; + if (youdefend) { + if (youmonst.data != old_uasmon) + *dmgptr = 0; /* rehumanized, so no more damage */ + if (u.uenmax > 0) { + You("lose magical energy!"); + u.uenmax--; + if (u.uen > 0) u.uen--; + flags.botl = 1; + } + } else { + if (mdef->data == &mons[PM_CLAY_GOLEM]) + mdef->mhp = 1; /* cancelled clay golems will die */ + if (youattack && attacktype(mdef->data, AT_MAGC)) { + You("absorb magical energy!"); + u.uenmax++; + u.uen++; + flags.botl = 1; + } + } + } + break; + + case MB_INDEX_SCARE: + if (youdefend) { + if (Antimagic) { + resisted = TRUE; + } else { + nomul(-3); + nomovemsg = ""; + if (magr && magr == u.ustuck && sticks(youmonst.data)) { + u.ustuck = (struct monst *)0; + You("release %s!", mon_nam(magr)); + } + } + } else { + if (rn2(2) && resist(mdef, WEAPON_CLASS, 0, NOTELL)) + resisted = TRUE; + else + monflee(mdef, 3, FALSE, (mdef->mhp > *dmgptr)); + } + if (!resisted) do_stun = FALSE; + break; + + case MB_INDEX_STUN: + do_stun = TRUE; /* (this is redundant...) */ + break; + + case MB_INDEX_PROBE: + if (youattack && (mb->spe == 0 || !rn2(3 * abs(mb->spe)))) { + pline_The("%s is insightful.", verb); + /* pre-damage status */ + probe_monster(mdef); + } + break; + } + /* stun if that was selected and a worse effect didn't occur */ + if (do_stun) { + if (youdefend) + make_stunned((HStun + 3), FALSE); + else + mdef->mstun = 1; + /* avoid extra stun message below if we used mb_verb["stun"] above */ + if (attack_indx == MB_INDEX_STUN) do_stun = FALSE; + } + /* lastly, all this magic can be confusing... */ + do_confuse = !rn2(12); + if (do_confuse) { + if (youdefend) + make_confused(HConfusion + 4, FALSE); + else + mdef->mconf = 1; + } + + if (youattack || youdefend || vis) { + (void) upstart(hittee); /* capitalize */ + if (resisted) { + pline("%s %s!", hittee, vtense(hittee, "resist")); + shieldeff(youdefend ? u.ux : mdef->mx, + youdefend ? u.uy : mdef->my); + } + if ((do_stun || do_confuse) && flags.verbose) { + char buf[BUFSZ]; + + buf[0] = '\0'; + if (do_stun) Strcat(buf, "stunned"); + if (do_stun && do_confuse) Strcat(buf, " and "); + if (do_confuse) Strcat(buf, "confused"); + pline("%s %s %s%c", hittee, vtense(hittee, "are"), + buf, (do_stun && do_confuse) ? '!' : '.'); + } + } + + return result; +} + +/* Function used when someone attacks someone else with an artifact + * weapon. Only adds the special (artifact) damage, and returns a 1 if it + * did something special (in which case the caller won't print the normal + * hit message). This should be called once upon every artifact attack; + * dmgval() no longer takes artifact bonuses into account. Possible + * extension: change the killer so that when an orc kills you with + * Stormbringer it's "killed by Stormbringer" instead of "killed by an orc". + */ +boolean +artifact_hit(magr, mdef, otmp, dmgptr, dieroll) +struct monst *magr, *mdef; +struct obj *otmp; +int *dmgptr; +int dieroll; /* needed for Magicbane and vorpal blades */ +{ + boolean youattack = (magr == &youmonst); + boolean youdefend = (mdef == &youmonst); + boolean vis = (!youattack && magr && cansee(magr->mx, magr->my)) + || (!youdefend && cansee(mdef->mx, mdef->my)) + || (youattack && u.uswallow && mdef == u.ustuck && !Blind); + boolean realizes_damage; + const char *wepdesc; + static const char you[] = "you"; + char hittee[BUFSZ]; + + Strcpy(hittee, youdefend ? you : mon_nam(mdef)); + + /* The following takes care of most of the damage, but not all-- + * the exception being for level draining, which is specially + * handled. Messages are done in this function, however. + */ + *dmgptr += spec_dbon(otmp, mdef, *dmgptr); + + if (youattack && youdefend) { + impossible("attacking yourself with weapon?"); + return FALSE; + } + + realizes_damage = (youdefend || vis || + /* feel the effect even if not seen */ + (youattack && mdef == u.ustuck)); + + /* the four basic attacks: fire, cold, shock and missiles */ + if (attacks(AD_FIRE, otmp)) { + if (realizes_damage) + pline_The("fiery blade %s %s%c", + !spec_dbon_applies ? "hits" : + (mdef->data == &mons[PM_WATER_ELEMENTAL]) ? + "vaporizes part of" : "burns", + hittee, !spec_dbon_applies ? '.' : '!'); + if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_FIRE); + if (!rn2(4)) (void) destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE); + if (!rn2(7)) (void) destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE); + if (youdefend && Slimed) burn_away_slime(); + return realizes_damage; + } + if (attacks(AD_COLD, otmp)) { + if (realizes_damage) + pline_The("ice-cold blade %s %s%c", + !spec_dbon_applies ? "hits" : "freezes", + hittee, !spec_dbon_applies ? '.' : '!'); + if (!rn2(4)) (void) destroy_mitem(mdef, POTION_CLASS, AD_COLD); + return realizes_damage; + } + if (attacks(AD_ELEC, otmp)) { + if (realizes_damage) + pline_The("massive hammer hits%s %s%c", + !spec_dbon_applies ? "" : "! Lightning strikes", + hittee, !spec_dbon_applies ? '.' : '!'); + if (!rn2(5)) (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC); + if (!rn2(5)) (void) destroy_mitem(mdef, WAND_CLASS, AD_ELEC); + return realizes_damage; + } + if (attacks(AD_MAGM, otmp)) { + if (realizes_damage) + pline_The("imaginary widget hits%s %s%c", + !spec_dbon_applies ? "" : + "! A hail of magic missiles strikes", + hittee, !spec_dbon_applies ? '.' : '!'); + return realizes_damage; + } + + if (attacks(AD_STUN, otmp) && dieroll <= MB_MAX_DIEROLL) { + /* Magicbane's special attacks (possibly modifies hittee[]) */ + return Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee); + } + + if (!spec_dbon_applies) { + /* since damage bonus didn't apply, nothing more to do; + no further attacks have side-effects on inventory */ + return FALSE; + } + + /* We really want "on a natural 20" but Nethack does it in */ + /* reverse from AD&D. */ + if (spec_ability(otmp, SPFX_BEHEAD)) { + if (otmp->oartifact == ART_TSURUGI_OF_MURAMASA && dieroll == 1) { + wepdesc = "The razor-sharp blade"; + /* not really beheading, but so close, why add another SPFX */ + if (youattack && u.uswallow && mdef == u.ustuck) { + You("slice %s wide open!", mon_nam(mdef)); + *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; + return TRUE; + } + if (!youdefend) { + /* allow normal cutworm() call to add extra damage */ + if(notonhead) + return FALSE; + + if (bigmonst(mdef->data)) { + if (youattack) + You("slice deeply into %s!", + mon_nam(mdef)); + else if (vis) + pline("%s cuts deeply into %s!", + Monnam(magr), hittee); + *dmgptr *= 2; + return TRUE; + } + *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; + pline("%s cuts %s in half!", wepdesc, mon_nam(mdef)); + otmp->dknown = TRUE; + return TRUE; + } else { + if (bigmonst(youmonst.data)) { + pline("%s cuts deeply into you!", + magr ? Monnam(magr) : wepdesc); + *dmgptr *= 2; + return TRUE; + } + + /* Players with negative AC's take less damage instead + * of just not getting hit. We must add a large enough + * value to the damage so that this reduction in + * damage does not prevent death. + */ + *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + FATAL_DAMAGE_MODIFIER; + pline("%s cuts you in half!", wepdesc); + otmp->dknown = TRUE; + return TRUE; + } + } else if (otmp->oartifact == ART_VORPAL_BLADE && + (dieroll == 1 || mdef->data == &mons[PM_JABBERWOCK])) { + static const char * const behead_msg[2] = { + "%s beheads %s!", + "%s decapitates %s!" + }; + + if (youattack && u.uswallow && mdef == u.ustuck) + return FALSE; + wepdesc = artilist[ART_VORPAL_BLADE].name; + if (!youdefend) { + if (!has_head(mdef->data) || notonhead || u.uswallow) { + if (youattack) + pline("Somehow, you miss %s wildly.", + mon_nam(mdef)); + else if (vis) + pline("Somehow, %s misses wildly.", + mon_nam(magr)); + *dmgptr = 0; + return ((boolean)(youattack || vis)); + } + if (noncorporeal(mdef->data) || amorphous(mdef->data)) { + pline("%s slices through %s %s.", wepdesc, + s_suffix(mon_nam(mdef)), + mbodypart(mdef,NECK)); + return TRUE; + } + *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; + pline(behead_msg[rn2(SIZE(behead_msg))], + wepdesc, mon_nam(mdef)); + otmp->dknown = TRUE; + return TRUE; + } else { + if (!has_head(youmonst.data)) { + pline("Somehow, %s misses you wildly.", + magr ? mon_nam(magr) : wepdesc); + *dmgptr = 0; + return TRUE; + } + if (noncorporeal(youmonst.data) || amorphous(youmonst.data)) { + pline("%s slices through your %s.", + wepdesc, body_part(NECK)); + return TRUE; + } + *dmgptr = 2 * (Upolyd ? u.mh : u.uhp) + + FATAL_DAMAGE_MODIFIER; + pline(behead_msg[rn2(SIZE(behead_msg))], + wepdesc, "you"); + otmp->dknown = TRUE; + /* Should amulets fall off? */ + return TRUE; + } + } + } + if (spec_ability(otmp, SPFX_DRLI)) { + if (!youdefend) { + if (vis) { + if(otmp->oartifact == ART_STORMBRINGER) + pline_The("%s blade draws the life from %s!", + hcolor(NH_BLACK), + mon_nam(mdef)); + else + pline("%s draws the life from %s!", + The(distant_name(otmp, xname)), + mon_nam(mdef)); + } + if (mdef->m_lev == 0) { + *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER; + } else { + int drain = rnd(8); + *dmgptr += drain; + mdef->mhpmax -= drain; + mdef->m_lev--; + drain /= 2; + if (drain) healup(drain, 0, FALSE, FALSE); + } + return vis; + } else { /* youdefend */ + int oldhpmax = u.uhpmax; + + if (Blind) + You_feel("an %s drain your life!", + otmp->oartifact == ART_STORMBRINGER ? + "unholy blade" : "object"); + else if (otmp->oartifact == ART_STORMBRINGER) + pline_The("%s blade drains your life!", + hcolor(NH_BLACK)); + else + pline("%s drains your life!", + The(distant_name(otmp, xname))); + losexp("life drainage"); + if (magr && magr->mhp < magr->mhpmax) { + magr->mhp += (oldhpmax - u.uhpmax)/2; + if (magr->mhp > magr->mhpmax) magr->mhp = magr->mhpmax; + } + return TRUE; + } + } + return FALSE; +} + +static NEARDATA const char recharge_type[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; +static NEARDATA const char invoke_types[] = { ALL_CLASSES, 0 }; + /* #invoke: an "ugly check" filters out most objects */ + +int +doinvoke() +{ + register struct obj *obj; + + obj = getobj(invoke_types, "invoke"); + if (!obj) return 0; + if (obj->oartifact && !touch_artifact(obj, &youmonst)) return 1; + return arti_invoke(obj); +} + +STATIC_OVL int +arti_invoke(obj) + register struct obj *obj; +{ + register const struct artifact *oart = get_artifact(obj); + + if(!oart || !oart->inv_prop) { + if(obj->otyp == CRYSTAL_BALL) + use_crystal_ball(obj); + else + pline(nothing_happens); + return 1; + } + + if(oart->inv_prop > LAST_PROP) { + /* It's a special power, not "just" a property */ + if(obj->age > monstermoves) { + /* the artifact is tired :-) */ + You_feel("that %s %s ignoring you.", + the(xname(obj)), otense(obj, "are")); + /* and just got more so; patience is essential... */ + obj->age += (long) d(3,10); + return 1; + } + obj->age = monstermoves + rnz(100); + + switch(oart->inv_prop) { + case TAMING: { + struct obj pseudo; + + pseudo = zeroobj; /* neither cursed nor blessed */ + pseudo.otyp = SCR_TAMING; + (void) seffects(&pseudo); + break; + } + case HEALING: { + int healamt = (u.uhpmax + 1 - u.uhp) / 2; + long creamed = (long)u.ucreamed; + + if (Upolyd) healamt = (u.mhmax + 1 - u.mh) / 2; + if (healamt || Sick || Slimed || Blinded > creamed) + You_feel("better."); + else + goto nothing_special; + if (healamt > 0) { + if (Upolyd) u.mh += healamt; + else u.uhp += healamt; + } + if(Sick) make_sick(0L,(char *)0,FALSE,SICK_ALL); + if(Slimed) Slimed = 0L; + if (Blinded > creamed) make_blinded(creamed, FALSE); + flags.botl = 1; + break; + } + case ENERGY_BOOST: { + int epboost = (u.uenmax + 1 - u.uen) / 2; + if (epboost > 120) epboost = 120; /* arbitrary */ + else if (epboost < 12) epboost = u.uenmax - u.uen; + if(epboost) { + You_feel("re-energized."); + u.uen += epboost; + flags.botl = 1; + } else + goto nothing_special; + break; + } + case UNTRAP: { + if(!untrap(TRUE)) { + obj->age = 0; /* don't charge for changing their mind */ + return 0; + } + break; + } + case CHARGE_OBJ: { + struct obj *otmp = getobj(recharge_type, "charge"); + boolean b_effect; + + if (!otmp) { + obj->age = 0; + return 0; + } + b_effect = obj->blessed && + (Role_switch == oart->role || !oart->role); + recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0); + update_inventory(); + break; + } + case LEV_TELE: + level_tele(); + break; + case CREATE_PORTAL: { + int i, num_ok_dungeons, last_ok_dungeon = 0; + d_level newlev; + extern int n_dgns; /* from dungeon.c */ + winid tmpwin = create_nhwindow(NHW_MENU); + anything any; + + any.a_void = 0; /* set all bits to zero */ + start_menu(tmpwin); + /* use index+1 (cant use 0) as identifier */ + for (i = num_ok_dungeons = 0; i < n_dgns; i++) { + if (!dungeons[i].dunlev_ureached) continue; + any.a_int = i+1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + dungeons[i].dname, MENU_UNSELECTED); + num_ok_dungeons++; + last_ok_dungeon = i; + } + end_menu(tmpwin, "Open a portal to which dungeon?"); + if (num_ok_dungeons > 1) { + /* more than one entry; display menu for choices */ + menu_item *selected; + int n; + + n = select_menu(tmpwin, PICK_ONE, &selected); + if (n <= 0) { + destroy_nhwindow(tmpwin); + goto nothing_special; + } + i = selected[0].item.a_int - 1; + free((genericptr_t)selected); + } else + i = last_ok_dungeon; /* also first & only OK dungeon */ + destroy_nhwindow(tmpwin); + + /* + * i is now index into dungeon structure for the new dungeon. + * Find the closest level in the given dungeon, open + * a use-once portal to that dungeon and go there. + * The closest level is either the entry or dunlev_ureached. + */ + newlev.dnum = i; + if(dungeons[i].depth_start >= depth(&u.uz)) + newlev.dlevel = dungeons[i].entry_lev; + else + newlev.dlevel = dungeons[i].dunlev_ureached; + if(u.uhave.amulet || In_endgame(&u.uz) || In_endgame(&newlev) || + newlev.dnum == u.uz.dnum) { + You_feel("very disoriented for a moment."); + } else { + if(!Blind) You("are surrounded by a shimmering sphere!"); + else You_feel("weightless for a moment."); + goto_level(&newlev, FALSE, FALSE, FALSE); + } + break; + } + case ENLIGHTENING: + enlightenment(0); + break; + case CREATE_AMMO: { + struct obj *otmp = mksobj(ARROW, TRUE, FALSE); + + if (!otmp) goto nothing_special; + otmp->blessed = obj->blessed; + otmp->cursed = obj->cursed; + otmp->bknown = obj->bknown; + if (obj->blessed) { + if (otmp->spe < 0) otmp->spe = 0; + otmp->quan += rnd(10); + } else if (obj->cursed) { + if (otmp->spe > 0) otmp->spe = 0; + } else + otmp->quan += rnd(5); + otmp->owt = weight(otmp); + otmp = hold_another_object(otmp, "Suddenly %s out.", + aobjnam(otmp, "fall"), (const char *)0); + break; + } + } + } else { + long eprop = (u.uprops[oart->inv_prop].extrinsic ^= W_ARTI), + iprop = u.uprops[oart->inv_prop].intrinsic; + boolean on = (eprop & W_ARTI) != 0; /* true if invoked prop just set */ + + if(on && obj->age > monstermoves) { + /* the artifact is tired :-) */ + u.uprops[oart->inv_prop].extrinsic ^= W_ARTI; + You_feel("that %s %s ignoring you.", + the(xname(obj)), otense(obj, "are")); + /* can't just keep repeatedly trying */ + obj->age += (long) d(3,10); + return 1; + } else if(!on) { + /* when turning off property, determine downtime */ + /* arbitrary for now until we can tune this -dlc */ + obj->age = monstermoves + rnz(100); + } + + if ((eprop & ~W_ARTI) || iprop) { +nothing_special: + /* you had the property from some other source too */ + if (carried(obj)) + You_feel("a surge of power, but nothing seems to happen."); + return 1; + } + switch(oart->inv_prop) { + case CONFLICT: + if(on) You_feel("like a rabble-rouser."); + else You_feel("the tension decrease around you."); + break; + case LEVITATION: + if(on) { + float_up(); + spoteffects(FALSE); + } else (void) float_down(I_SPECIAL|TIMEOUT, W_ARTI); + break; + case INVIS: + if (BInvis || Blind) goto nothing_special; + newsym(u.ux, u.uy); + if (on) + Your("body takes on a %s transparency...", + Hallucination ? "normal" : "strange"); + else + Your("body seems to unfade..."); + break; + } + } + + return 1; +} + + +/* WAC return TRUE if artifact is always lit */ +boolean +artifact_light(obj) + struct obj *obj; +{ + return (get_artifact(obj) && obj->oartifact == ART_SUNSWORD); +} + +/* KMH -- Talking artifacts are finally implemented */ +void +arti_speak(obj) + struct obj *obj; +{ + register const struct artifact *oart = get_artifact(obj); + const char *line; + char buf[BUFSZ]; + + + /* Is this a speaking artifact? */ + if (!oart || !(oart->spfx & SPFX_SPEAK)) + return; + + line = getrumor(bcsign(obj), buf, TRUE); + if (!*line) + line = "NetHack rumors file closed for renovation."; + pline("%s:", Tobjnam(obj, "whisper")); + verbalize("%s", line); + return; +} + +boolean +artifact_has_invprop(otmp, inv_prop) +struct obj *otmp; +uchar inv_prop; +{ + const struct artifact *arti = get_artifact(otmp); + + return((boolean)(arti && (arti->inv_prop == inv_prop))); +} + +/* Return the price sold to the hero of a given artifact or unique item */ +long +arti_cost(otmp) +struct obj *otmp; +{ + if (!otmp->oartifact) + return ((long)objects[otmp->otyp].oc_cost); + else if (artilist[(int) otmp->oartifact].cost) + return (artilist[(int) otmp->oartifact].cost); + else + return (100L * (long)objects[otmp->otyp].oc_cost); +} + +#endif /* OVLB */ + +/*artifact.c*/ diff --git a/src/attrib.c b/src/attrib.c new file mode 100644 index 0000000..e4fab6f --- /dev/null +++ b/src/attrib.c @@ -0,0 +1,731 @@ +/* SCCS Id: @(#)attrib.c 3.4 2002/10/07 */ +/* Copyright 1988, 1989, 1990, 1992, M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* attribute modification routines. */ + +#include "hack.h" + +/* #define DEBUG */ /* uncomment for debugging info */ + +#ifdef OVLB + + /* part of the output on gain or loss of attribute */ +static +const char * const plusattr[] = { + "strong", "smart", "wise", "agile", "tough", "charismatic" +}, + * const minusattr[] = { + "weak", "stupid", "foolish", "clumsy", "fragile", "repulsive" +}; + + +static +const struct innate { + schar ulevel; + long *ability; + const char *gainstr, *losestr; +} arc_abil[] = { { 1, &(HStealth), "", "" }, + { 1, &(HFast), "", "" }, + { 10, &(HSearching), "perceptive", "" }, + { 0, 0, 0, 0 } }, + + bar_abil[] = { { 1, &(HPoison_resistance), "", "" }, + { 7, &(HFast), "quick", "slow" }, + { 15, &(HStealth), "stealthy", "" }, + { 0, 0, 0, 0 } }, + + cav_abil[] = { { 7, &(HFast), "quick", "slow" }, + { 15, &(HWarning), "sensitive", "" }, + { 0, 0, 0, 0 } }, + + hea_abil[] = { { 1, &(HPoison_resistance), "", "" }, + { 15, &(HWarning), "sensitive", "" }, + { 0, 0, 0, 0 } }, + + kni_abil[] = { { 7, &(HFast), "quick", "slow" }, + { 0, 0, 0, 0 } }, + + mon_abil[] = { { 1, &(HFast), "", "" }, + { 1, &(HSleep_resistance), "", "" }, + { 1, &(HSee_invisible), "", "" }, + { 3, &(HPoison_resistance), "healthy", "" }, + { 5, &(HStealth), "stealthy", "" }, + { 7, &(HWarning), "sensitive", "" }, + { 9, &(HSearching), "perceptive", "unaware" }, + { 11, &(HFire_resistance), "cool", "warmer" }, + { 13, &(HCold_resistance), "warm", "cooler" }, + { 15, &(HShock_resistance), "insulated", "conductive" }, + { 17, &(HTeleport_control), "controlled","uncontrolled" }, + { 0, 0, 0, 0 } }, + + pri_abil[] = { { 15, &(HWarning), "sensitive", "" }, + { 20, &(HFire_resistance), "cool", "warmer" }, + { 0, 0, 0, 0 } }, + + ran_abil[] = { { 1, &(HSearching), "", "" }, + { 7, &(HStealth), "stealthy", "" }, + { 15, &(HSee_invisible), "", "" }, + { 0, 0, 0, 0 } }, + + rog_abil[] = { { 1, &(HStealth), "", "" }, + { 10, &(HSearching), "perceptive", "" }, + { 0, 0, 0, 0 } }, + + sam_abil[] = { { 1, &(HFast), "", "" }, + { 15, &(HStealth), "stealthy", "" }, + { 0, 0, 0, 0 } }, + + tou_abil[] = { { 10, &(HSearching), "perceptive", "" }, + { 20, &(HPoison_resistance), "hardy", "" }, + { 0, 0, 0, 0 } }, + + val_abil[] = { { 1, &(HCold_resistance), "", "" }, + { 1, &(HStealth), "", "" }, + { 7, &(HFast), "quick", "slow" }, + { 0, 0, 0, 0 } }, + + wiz_abil[] = { { 15, &(HWarning), "sensitive", "" }, + { 17, &(HTeleport_control), "controlled","uncontrolled" }, + { 0, 0, 0, 0 } }, + + /* Intrinsics conferred by race */ + elf_abil[] = { { 4, &(HSleep_resistance), "awake", "tired" }, + { 0, 0, 0, 0 } }, + + orc_abil[] = { { 1, &(HPoison_resistance), "", "" }, + { 0, 0, 0, 0 } }; + +static long next_check = 600L; /* arbitrary first setting */ +STATIC_DCL void NDECL(exerper); +STATIC_DCL void FDECL(postadjabil, (long *)); + +/* adjust an attribute; return TRUE if change is made, FALSE otherwise */ +boolean +adjattrib(ndx, incr, msgflg) + int ndx, incr; + int msgflg; /* positive => no message, zero => message, and */ +{ /* negative => conditional (msg if change made) */ + if (Fixed_abil || !incr) return FALSE; + + if ((ndx == A_INT || ndx == A_WIS) + && uarmh && uarmh->otyp == DUNCE_CAP) { + if (msgflg == 0) + Your("cap constricts briefly, then relaxes again."); + return FALSE; + } + + if (incr > 0) { + if ((AMAX(ndx) >= ATTRMAX(ndx)) && (ACURR(ndx) >= AMAX(ndx))) { + if (msgflg == 0 && flags.verbose) + pline("You're already as %s as you can get.", + plusattr[ndx]); + ABASE(ndx) = AMAX(ndx) = ATTRMAX(ndx); /* just in case */ + return FALSE; + } + + ABASE(ndx) += incr; + if(ABASE(ndx) > AMAX(ndx)) { + incr = ABASE(ndx) - AMAX(ndx); + AMAX(ndx) += incr; + if(AMAX(ndx) > ATTRMAX(ndx)) + AMAX(ndx) = ATTRMAX(ndx); + ABASE(ndx) = AMAX(ndx); + } + } else { + if (ABASE(ndx) <= ATTRMIN(ndx)) { + if (msgflg == 0 && flags.verbose) + pline("You're already as %s as you can get.", + minusattr[ndx]); + ABASE(ndx) = ATTRMIN(ndx); /* just in case */ + return FALSE; + } + + ABASE(ndx) += incr; + if(ABASE(ndx) < ATTRMIN(ndx)) { + incr = ABASE(ndx) - ATTRMIN(ndx); + ABASE(ndx) = ATTRMIN(ndx); + AMAX(ndx) += incr; + if(AMAX(ndx) < ATTRMIN(ndx)) + AMAX(ndx) = ATTRMIN(ndx); + } + } + if (msgflg <= 0) + You_feel("%s%s!", + (incr > 1 || incr < -1) ? "very ": "", + (incr > 0) ? plusattr[ndx] : minusattr[ndx]); + flags.botl = 1; + if (moves > 1 && (ndx == A_STR || ndx == A_CON)) + (void)encumber_msg(); + return TRUE; +} + +void +gainstr(otmp, incr) + register struct obj *otmp; + register int incr; +{ + int num = 1; + + if(incr) num = incr; + else { + if(ABASE(A_STR) < 18) num = (rn2(4) ? 1 : rnd(6) ); + else if (ABASE(A_STR) < STR18(85)) num = rnd(10); + } + (void) adjattrib(A_STR, (otmp && otmp->cursed) ? -num : num, TRUE); +} + +void +losestr(num) /* may kill you; cause may be poison or monster like 'a' */ + register int num; +{ + int ustr = ABASE(A_STR) - num; + + while(ustr < 3) { + ++ustr; + --num; + if (Upolyd) { + u.mh -= 6; + u.mhmax -= 6; + } else { + u.uhp -= 6; + u.uhpmax -= 6; + } + } + (void) adjattrib(A_STR, -num, TRUE); +} + +void +change_luck(n) + register schar n; +{ + u.uluck += n; + if (u.uluck < 0 && u.uluck < LUCKMIN) u.uluck = LUCKMIN; + if (u.uluck > 0 && u.uluck > LUCKMAX) u.uluck = LUCKMAX; +} + +int +stone_luck(parameter) +boolean parameter; /* So I can't think up of a good name. So sue me. --KAA */ +{ + register struct obj *otmp; + register long bonchance = 0; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (confers_luck(otmp)) { + if (otmp->cursed) bonchance -= otmp->quan; + else if (otmp->blessed) bonchance += otmp->quan; + else if (parameter) bonchance += otmp->quan; + } + + return sgn((int)bonchance); +} + +/* there has just been an inventory change affecting a luck-granting item */ +void +set_moreluck() +{ + int luckbon = stone_luck(TRUE); + + if (!luckbon && !carrying(LUCKSTONE)) u.moreluck = 0; + else if (luckbon >= 0) u.moreluck = LUCKADD; + else u.moreluck = -LUCKADD; +} + +#endif /* OVLB */ +#ifdef OVL1 + +void +restore_attrib() +{ + int i; + + for(i = 0; i < A_MAX; i++) { /* all temporary losses/gains */ + + if(ATEMP(i) && ATIME(i)) { + if(!(--(ATIME(i)))) { /* countdown for change */ + ATEMP(i) += ATEMP(i) > 0 ? -1 : 1; + + if(ATEMP(i)) /* reset timer */ + ATIME(i) = 100 / ACURR(A_CON); + } + } + } + (void)encumber_msg(); +} + +#endif /* OVL1 */ +#ifdef OVLB + +#define AVAL 50 /* tune value for exercise gains */ + +void +exercise(i, inc_or_dec) +int i; +boolean inc_or_dec; +{ +#ifdef DEBUG + pline("Exercise:"); +#endif + if (i == A_INT || i == A_CHA) return; /* can't exercise these */ + + /* no physical exercise while polymorphed; the body's temporary */ + if (Upolyd && i != A_WIS) return; + + if(abs(AEXE(i)) < AVAL) { + /* + * Law of diminishing returns (Part I): + * + * Gain is harder at higher attribute values. + * 79% at "3" --> 0% at "18" + * Loss is even at all levels (50%). + * + * Note: *YES* ACURR is the right one to use. + */ + AEXE(i) += (inc_or_dec) ? (rn2(19) > ACURR(i)) : -rn2(2); +#ifdef DEBUG + pline("%s, %s AEXE = %d", + (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" : + (i == A_DEX) ? "Dex" : "Con", + (inc_or_dec) ? "inc" : "dec", AEXE(i)); +#endif + } + if (moves > 0 && (i == A_STR || i == A_CON)) (void)encumber_msg(); +} + +/* hunger values - from eat.c */ +#define SATIATED 0 +#define NOT_HUNGRY 1 +#define HUNGRY 2 +#define WEAK 3 +#define FAINTING 4 +#define FAINTED 5 +#define STARVED 6 + +STATIC_OVL void +exerper() +{ + if(!(moves % 10)) { + /* Hunger Checks */ + + int hs = (u.uhunger > 1000) ? SATIATED : + (u.uhunger > 150) ? NOT_HUNGRY : + (u.uhunger > 50) ? HUNGRY : + (u.uhunger > 0) ? WEAK : FAINTING; + +#ifdef DEBUG + pline("exerper: Hunger checks"); +#endif + switch (hs) { + case SATIATED: exercise(A_DEX, FALSE); + if (Role_if(PM_MONK)) + exercise(A_WIS, FALSE); + break; + case NOT_HUNGRY: exercise(A_CON, TRUE); break; + case WEAK: exercise(A_STR, FALSE); + if (Role_if(PM_MONK)) /* fasting */ + exercise(A_WIS, TRUE); + break; + case FAINTING: + case FAINTED: exercise(A_CON, FALSE); break; + } + + /* Encumberance Checks */ +#ifdef DEBUG + pline("exerper: Encumber checks"); +#endif + switch (near_capacity()) { + case MOD_ENCUMBER: exercise(A_STR, TRUE); break; + case HVY_ENCUMBER: exercise(A_STR, TRUE); + exercise(A_DEX, FALSE); break; + case EXT_ENCUMBER: exercise(A_DEX, FALSE); + exercise(A_CON, FALSE); break; + } + + } + + /* status checks */ + if(!(moves % 5)) { +#ifdef DEBUG + pline("exerper: Status checks"); +#endif + if ((HClairvoyant & (INTRINSIC|TIMEOUT)) && + !BClairvoyant) exercise(A_WIS, TRUE); + if (HRegeneration) exercise(A_STR, TRUE); + + if(Sick || Vomiting) exercise(A_CON, FALSE); + if(Confusion || Hallucination) exercise(A_WIS, FALSE); + if((Wounded_legs +#ifdef STEED + && !u.usteed +#endif + ) || Fumbling || HStun) exercise(A_DEX, FALSE); + } +} + +void +exerchk() +{ + int i, mod_val; + + /* Check out the periodic accumulations */ + exerper(); + +#ifdef DEBUG + if(moves >= next_check) + pline("exerchk: ready to test. multi = %d.", multi); +#endif + /* Are we ready for a test? */ + if(moves >= next_check && !multi) { +#ifdef DEBUG + pline("exerchk: testing."); +#endif + /* + * Law of diminishing returns (Part II): + * + * The effects of "exercise" and "abuse" wear + * off over time. Even if you *don't* get an + * increase/decrease, you lose some of the + * accumulated effects. + */ + for(i = 0; i < A_MAX; AEXE(i++) /= 2) { + + if(ABASE(i) >= 18 || !AEXE(i)) continue; + if(i == A_INT || i == A_CHA) continue;/* can't exercise these */ + +#ifdef DEBUG + pline("exerchk: testing %s (%d).", + (i == A_STR) ? "Str" : (i == A_WIS) ? "Wis" : + (i == A_DEX) ? "Dex" : "Con", AEXE(i)); +#endif + /* + * Law of diminishing returns (Part III): + * + * You don't *always* gain by exercising. + * [MRS 92/10/28 - Treat Wisdom specially for balance.] + */ + if(rn2(AVAL) > ((i != A_WIS) ? abs(AEXE(i)*2/3) : abs(AEXE(i)))) + continue; + mod_val = sgn(AEXE(i)); + +#ifdef DEBUG + pline("exerchk: changing %d.", i); +#endif + if(adjattrib(i, mod_val, -1)) { +#ifdef DEBUG + pline("exerchk: changed %d.", i); +#endif + /* if you actually changed an attrib - zero accumulation */ + AEXE(i) = 0; + /* then print an explanation */ + switch(i) { + case A_STR: You((mod_val >0) ? + "must have been exercising." : + "must have been abusing your body."); + break; + case A_WIS: You((mod_val >0) ? + "must have been very observant." : + "haven't been paying attention."); + break; + case A_DEX: You((mod_val >0) ? + "must have been working on your reflexes." : + "haven't been working on reflexes lately."); + break; + case A_CON: You((mod_val >0) ? + "must be leading a healthy life-style." : + "haven't been watching your health."); + break; + } + } + } + next_check += rn1(200,800); +#ifdef DEBUG + pline("exerchk: next check at %ld.", next_check); +#endif + } +} + +/* next_check will otherwise have its initial 600L after a game restore */ +void +reset_attribute_clock() +{ + if (moves > 600L) next_check = moves + rn1(50,800); +} + + +void +init_attr(np) + register int np; +{ + register int i, x, tryct; + + + for(i = 0; i < A_MAX; i++) { + ABASE(i) = AMAX(i) = urole.attrbase[i]; + ATEMP(i) = ATIME(i) = 0; + np -= urole.attrbase[i]; + } + + tryct = 0; + while(np > 0 && tryct < 100) { + + x = rn2(100); + for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ; + if(i >= A_MAX) continue; /* impossible */ + + if(ABASE(i) >= ATTRMAX(i)) { + + tryct++; + continue; + } + tryct = 0; + ABASE(i)++; + AMAX(i)++; + np--; + } + + tryct = 0; + while(np < 0 && tryct < 100) { /* for redistribution */ + + x = rn2(100); + for (i = 0; (i < A_MAX) && ((x -= urole.attrdist[i]) > 0); i++) ; + if(i >= A_MAX) continue; /* impossible */ + + if(ABASE(i) <= ATTRMIN(i)) { + + tryct++; + continue; + } + tryct = 0; + ABASE(i)--; + AMAX(i)--; + np++; + } +} + +void +redist_attr() +{ + register int i, tmp; + + for(i = 0; i < A_MAX; i++) { + if (i==A_INT || i==A_WIS) continue; + /* Polymorphing doesn't change your mind */ + tmp = AMAX(i); + AMAX(i) += (rn2(5)-2); + if (AMAX(i) > ATTRMAX(i)) AMAX(i) = ATTRMAX(i); + if (AMAX(i) < ATTRMIN(i)) AMAX(i) = ATTRMIN(i); + ABASE(i) = ABASE(i) * AMAX(i) / tmp; + /* ABASE(i) > ATTRMAX(i) is impossible */ + if (ABASE(i) < ATTRMIN(i)) ABASE(i) = ATTRMIN(i); + } + (void)encumber_msg(); +} + +STATIC_OVL +void +postadjabil(ability) +long *ability; +{ + if (!ability) return; + if (ability == &(HWarning) || ability == &(HSee_invisible)) + see_monsters(); +} + +void +adjabil(oldlevel,newlevel) +int oldlevel, newlevel; +{ + register const struct innate *abil, *rabil; + long mask = FROMEXPER; + + + switch (Role_switch) { + case PM_ARCHEOLOGIST: abil = arc_abil; break; + case PM_BARBARIAN: abil = bar_abil; break; + case PM_CAVEMAN: abil = cav_abil; break; + case PM_HEALER: abil = hea_abil; break; + case PM_KNIGHT: abil = kni_abil; break; + case PM_MONK: abil = mon_abil; break; + case PM_PRIEST: abil = pri_abil; break; + case PM_RANGER: abil = ran_abil; break; + case PM_ROGUE: abil = rog_abil; break; + case PM_SAMURAI: abil = sam_abil; break; +#ifdef TOURIST + case PM_TOURIST: abil = tou_abil; break; +#endif + case PM_VALKYRIE: abil = val_abil; break; + case PM_WIZARD: abil = wiz_abil; break; + default: abil = 0; break; + } + + switch (Race_switch) { + case PM_ELF: rabil = elf_abil; break; + case PM_ORC: rabil = orc_abil; break; + case PM_HUMAN: + case PM_DWARF: + case PM_GNOME: + default: rabil = 0; break; + } + + while (abil || rabil) { + long prevabil; + /* Have we finished with the intrinsics list? */ + if (!abil || !abil->ability) { + /* Try the race intrinsics */ + if (!rabil || !rabil->ability) break; + abil = rabil; + rabil = 0; + mask = FROMRACE; + } + prevabil = *(abil->ability); + if(oldlevel < abil->ulevel && newlevel >= abil->ulevel) { + /* Abilities gained at level 1 can never be lost + * via level loss, only via means that remove _any_ + * sort of ability. A "gain" of such an ability from + * an outside source is devoid of meaning, so we set + * FROMOUTSIDE to avoid such gains. + */ + if (abil->ulevel == 1) + *(abil->ability) |= (mask|FROMOUTSIDE); + else + *(abil->ability) |= mask; + if(!(*(abil->ability) & INTRINSIC & ~mask)) { + if(*(abil->gainstr)) + You_feel("%s!", abil->gainstr); + } + } else if (oldlevel >= abil->ulevel && newlevel < abil->ulevel) { + *(abil->ability) &= ~mask; + if(!(*(abil->ability) & INTRINSIC)) { + if(*(abil->losestr)) + You_feel("%s!", abil->losestr); + else if(*(abil->gainstr)) + You_feel("less %s!", abil->gainstr); + } + } + if (prevabil != *(abil->ability)) /* it changed */ + postadjabil(abil->ability); + abil++; + } + + if (oldlevel > 0) { + if (newlevel > oldlevel) + add_weapon_skill(newlevel - oldlevel); + else + lose_weapon_skill(oldlevel - newlevel); + } +} + + +int +newhp() +{ + int hp, conplus; + + + if (u.ulevel == 0) { + /* Initialize hit points */ + hp = urole.hpadv.infix + urace.hpadv.infix; + if (urole.hpadv.inrnd > 0) hp += rnd(urole.hpadv.inrnd); + if (urace.hpadv.inrnd > 0) hp += rnd(urace.hpadv.inrnd); + + /* Initialize alignment stuff */ + u.ualign.type = aligns[flags.initalign].value; + u.ualign.record = urole.initrecord; + + return hp; + } else { + if (u.ulevel < urole.xlev) { + hp = urole.hpadv.lofix + urace.hpadv.lofix; + if (urole.hpadv.lornd > 0) hp += rnd(urole.hpadv.lornd); + if (urace.hpadv.lornd > 0) hp += rnd(urace.hpadv.lornd); + } else { + hp = urole.hpadv.hifix + urace.hpadv.hifix; + if (urole.hpadv.hirnd > 0) hp += rnd(urole.hpadv.hirnd); + if (urace.hpadv.hirnd > 0) hp += rnd(urace.hpadv.hirnd); + } + } + + if (ACURR(A_CON) <= 3) conplus = -2; + else if (ACURR(A_CON) <= 6) conplus = -1; + else if (ACURR(A_CON) <= 14) conplus = 0; + else if (ACURR(A_CON) <= 16) conplus = 1; + else if (ACURR(A_CON) == 17) conplus = 2; + else if (ACURR(A_CON) == 18) conplus = 3; + else conplus = 4; + + hp += conplus; + return((hp <= 0) ? 1 : hp); +} + +#endif /* OVLB */ +#ifdef OVL0 + +schar +acurr(x) +int x; +{ + register int tmp = (u.abon.a[x] + u.atemp.a[x] + u.acurr.a[x]); + + if (x == A_STR) { + if (uarmg && uarmg->otyp == GAUNTLETS_OF_POWER) return(125); +#ifdef WIN32_BUG + else return(x=((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp)); +#else + else return((schar)((tmp >= 125) ? 125 : (tmp <= 3) ? 3 : tmp)); +#endif + } else if (x == A_CHA) { + if (tmp < 18 && (youmonst.data->mlet == S_NYMPH || + u.umonnum==PM_SUCCUBUS || u.umonnum == PM_INCUBUS)) + return 18; + } else if (x == A_INT || x == A_WIS) { + /* yes, this may raise int/wis if player is sufficiently + * stupid. there are lower levels of cognition than "dunce". + */ + if (uarmh && uarmh->otyp == DUNCE_CAP) return(6); + } +#ifdef WIN32_BUG + return(x=((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp)); +#else + return((schar)((tmp >= 25) ? 25 : (tmp <= 3) ? 3 : tmp)); +#endif +} + +/* condense clumsy ACURR(A_STR) value into value that fits into game formulas + */ +schar +acurrstr() +{ + register int str = ACURR(A_STR); + + if (str <= 18) return((schar)str); + if (str <= 121) return((schar)(19 + str / 50)); /* map to 19-21 */ + else return((schar)(str - 100)); +} + +#endif /* OVL0 */ +#ifdef OVL2 + +/* avoid possible problems with alignment overflow, and provide a centralized + * location for any future alignment limits + */ +void +adjalign(n) +register int n; +{ + register int newalign = u.ualign.record + n; + + if(n < 0) { + if(newalign < u.ualign.record) + u.ualign.record = newalign; + } else + if(newalign > u.ualign.record) { + u.ualign.record = newalign; + if(u.ualign.record > ALIGNLIM) + u.ualign.record = ALIGNLIM; + } +} + +#endif /* OVL2 */ + +/*attrib.c*/ diff --git a/src/ball.c b/src/ball.c new file mode 100644 index 0000000..b2a05e0 --- /dev/null +++ b/src/ball.c @@ -0,0 +1,790 @@ +/* SCCS Id: @(#)ball.c 3.4 2003/02/03 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Ball & Chain =============================================================*/ + +#include "hack.h" + +STATIC_DCL int NDECL(bc_order); +STATIC_DCL void NDECL(litter); + +void +ballfall() +{ + boolean gets_hit; + + gets_hit = (((uball->ox != u.ux) || (uball->oy != u.uy)) && + ((uwep == uball)? FALSE : (boolean)rn2(5))); + if (carried(uball)) { + pline("Startled, you drop the iron ball."); + if (uwep == uball) + setuwep((struct obj *)0); + if (uswapwep == uball) + setuswapwep((struct obj *)0); + if (uquiver == uball) + setuqwep((struct obj *)0);; + if (uwep != uball) + freeinv(uball); + } + if(gets_hit){ + int dmg = rn1(7,25); + pline_The("iron ball falls on your %s.", + body_part(HEAD)); + if (uarmh) { + if(is_metallic(uarmh)) { + pline("Fortunately, you are wearing a hard helmet."); + dmg = 3; + } else if (flags.verbose) + Your("%s does not protect you.", xname(uarmh)); + } + losehp(dmg, "crunched in the head by an iron ball", + NO_KILLER_PREFIX); + } +} + +/* + * To make this work, we have to mess with the hero's mind. The rules for + * ball&chain are: + * + * 1. If the hero can see them, fine. + * 2. If the hero can't see either, it isn't seen. + * 3. If either is felt it is seen. + * 4. If either is felt and moved, it disappears. + * + * If the hero can see, then when a move is done, the ball and chain are + * first picked up, the positions under them are corrected, then they + * are moved after the hero moves. Not too bad. + * + * If the hero is blind, then she can "feel" the ball and/or chain at any + * time. However, when the hero moves, the felt ball and/or chain become + * unfelt and whatever was felt "under" the ball&chain appears. Pretty + * nifty, but it requires that the ball&chain "remember" what was under + * them --- i.e. they pick-up glyphs when they are felt and drop them when + * moved (and felt). When swallowed, the ball&chain are pulled completely + * off of the dungeon, but are still on the object chain. They are placed + * under the hero when she is expelled. + */ + +/* + * from you.h + * int u.bglyph glyph under the ball + * int u.cglyph glyph under the chain + * int u.bc_felt mask for ball/chain being felt + * #define BC_BALL 0x01 bit mask in u.bc_felt for ball + * #define BC_CHAIN 0x02 bit mask in u.bc_felt for chain + * int u.bc_order ball & chain order + * + * u.bc_felt is also manipulated in display.c and read.c, the others only + * in this file. None of these variables are valid unless the player is + * Blind. + */ + +/* values for u.bc_order */ +#define BCPOS_DIFFER 0 /* ball & chain at different positions */ +#define BCPOS_CHAIN 1 /* chain on top of ball */ +#define BCPOS_BALL 2 /* ball on top of chain */ + + + +/* + * Place the ball & chain under the hero. Make sure that the ball & chain + * variables are set (actually only needed when blind, but what the heck). + * It is assumed that when this is called, the ball and chain are NOT + * attached to the object list. + * + * Should not be called while swallowed. + */ +void +placebc() +{ + if (!uchain || !uball) { + impossible("Where are your ball and chain?"); + return; + } + + (void) flooreffects(uchain, u.ux, u.uy, ""); /* chain might rust */ + + if (carried(uball)) /* the ball is carried */ + u.bc_order = BCPOS_DIFFER; + else { + /* ball might rust -- already checked when carried */ + (void) flooreffects(uball, u.ux, u.uy, ""); + place_object(uball, u.ux, u.uy); + u.bc_order = BCPOS_CHAIN; + } + + place_object(uchain, u.ux, u.uy); + + u.bglyph = u.cglyph = levl[u.ux][u.uy].glyph; /* pick up glyph */ + + newsym(u.ux,u.uy); +} + +void +unplacebc() +{ + if (u.uswallow) return; /* ball&chain not placed while swallowed */ + + if (!carried(uball)) { + obj_extract_self(uball); + if (Blind && (u.bc_felt & BC_BALL)) /* drop glyph */ + levl[uball->ox][uball->oy].glyph = u.bglyph; + + newsym(uball->ox,uball->oy); + } + obj_extract_self(uchain); + if (Blind && (u.bc_felt & BC_CHAIN)) /* drop glyph */ + levl[uchain->ox][uchain->oy].glyph = u.cglyph; + + newsym(uchain->ox,uchain->oy); + u.bc_felt = 0; /* feel nothing */ +} + + +/* + * Return the stacking of the hero's ball & chain. This assumes that the + * hero is being punished. + */ +STATIC_OVL int +bc_order() +{ + struct obj *obj; + + if (uchain->ox != uball->ox || uchain->oy != uball->oy || carried(uball) + || u.uswallow) + return BCPOS_DIFFER; + + for (obj = level.objects[uball->ox][uball->oy]; obj; obj = obj->nexthere) { + if (obj == uchain) return BCPOS_CHAIN; + if (obj == uball) return BCPOS_BALL; + } + impossible("bc_order: ball&chain not in same location!"); + return BCPOS_DIFFER; +} + +/* + * set_bc() + * + * The hero is either about to go blind or already blind and just punished. + * Set up the ball and chain variables so that the ball and chain are "felt". + */ +void +set_bc(already_blind) +int already_blind; +{ + int ball_on_floor = !carried(uball); + + u.bc_order = bc_order(); /* get the order */ + u.bc_felt = ball_on_floor ? BC_BALL|BC_CHAIN : BC_CHAIN; /* felt */ + + if (already_blind || u.uswallow) { + u.cglyph = u.bglyph = levl[u.ux][u.uy].glyph; + return; + } + + /* + * Since we can still see, remove the ball&chain and get the glyph that + * would be beneath them. Then put the ball&chain back. This is pretty + * disgusting, but it will work. + */ + remove_object(uchain); + if (ball_on_floor) remove_object(uball); + + newsym(uchain->ox, uchain->oy); + u.cglyph = levl[uchain->ox][uchain->oy].glyph; + + if (u.bc_order == BCPOS_DIFFER) { /* different locations */ + place_object(uchain, uchain->ox, uchain->oy); + newsym(uchain->ox, uchain->oy); + if (ball_on_floor) { + newsym(uball->ox, uball->oy); /* see under ball */ + u.bglyph = levl[uball->ox][uball->oy].glyph; + place_object(uball, uball->ox, uball->oy); + newsym(uball->ox, uball->oy); /* restore ball */ + } + } else { + u.bglyph = u.cglyph; + if (u.bc_order == BCPOS_CHAIN) { + place_object(uball, uball->ox, uball->oy); + place_object(uchain, uchain->ox, uchain->oy); + } else { + place_object(uchain, uchain->ox, uchain->oy); + place_object(uball, uball->ox, uball->oy); + } + newsym(uball->ox, uball->oy); + } +} + + +/* + * move_bc() + * + * Move the ball and chain. This is called twice for every move. The first + * time to pick up the ball and chain before the move, the second time to + * place the ball and chain after the move. If the ball is carried, this + * function should never have BC_BALL as part of its control. + * + * Should not be called while swallowed. + */ +void +move_bc(before, control, ballx, bally, chainx, chainy) +int before, control; +xchar ballx, bally, chainx, chainy; /* only matter !before */ +{ + if (Blind) { + /* + * The hero is blind. Time to work hard. The ball and chain that + * are attached to the hero are very special. The hero knows that + * they are attached, so when they move, the hero knows that they + * aren't at the last position remembered. This is complicated + * by the fact that the hero can "feel" the surrounding locations + * at any time, hence, making one or both of them show up again. + * So, we have to keep track of which is felt at any one time and + * act accordingly. + */ + if (!before) { + if ((control & BC_CHAIN) && (control & BC_BALL)) { + /* + * Both ball and chain moved. If felt, drop glyph. + */ + if (u.bc_felt & BC_BALL) + levl[uball->ox][uball->oy].glyph = u.bglyph; + if (u.bc_felt & BC_CHAIN) + levl[uchain->ox][uchain->oy].glyph = u.cglyph; + u.bc_felt = 0; + + /* Pick up glyph at new location. */ + u.bglyph = levl[ballx][bally].glyph; + u.cglyph = levl[chainx][chainy].glyph; + + movobj(uball,ballx,bally); + movobj(uchain,chainx,chainy); + } else if (control & BC_BALL) { + if (u.bc_felt & BC_BALL) { + if (u.bc_order == BCPOS_DIFFER) { /* ball by itself */ + levl[uball->ox][uball->oy].glyph = u.bglyph; + } else if (u.bc_order == BCPOS_BALL) { + if (u.bc_felt & BC_CHAIN) { /* know chain is there */ + map_object(uchain, 0); + } else { + levl[uball->ox][uball->oy].glyph = u.bglyph; + } + } + u.bc_felt &= ~BC_BALL; /* no longer feel the ball */ + } + + /* Pick up glyph at new position. */ + u.bglyph = (ballx != chainx || bally != chainy) ? + levl[ballx][bally].glyph : u.cglyph; + + movobj(uball,ballx,bally); + } else if (control & BC_CHAIN) { + if (u.bc_felt & BC_CHAIN) { + if (u.bc_order == BCPOS_DIFFER) { + levl[uchain->ox][uchain->oy].glyph = u.cglyph; + } else if (u.bc_order == BCPOS_CHAIN) { + if (u.bc_felt & BC_BALL) { + map_object(uball, 0); + } else { + levl[uchain->ox][uchain->oy].glyph = u.cglyph; + } + } + u.bc_felt &= ~BC_CHAIN; + } + /* Pick up glyph at new position. */ + u.cglyph = (ballx != chainx || bally != chainy) ? + levl[chainx][chainy].glyph : u.bglyph; + + movobj(uchain,chainx,chainy); + } + + u.bc_order = bc_order(); /* reset the order */ + } + + } else { + /* + * The hero is not blind. To make this work correctly, we need to + * pick up the ball and chain before the hero moves, then put them + * in their new positions after the hero moves. + */ + if (before) { + if (!control) { + /* + * Neither ball nor chain is moving, so remember which was + * on top until !before. Use the variable u.bc_order + * since it is only valid when blind. + */ + u.bc_order = bc_order(); + } + + remove_object(uchain); + newsym(uchain->ox, uchain->oy); + if (!carried(uball)) { + remove_object(uball); + newsym(uball->ox, uball->oy); + } + } else { + int on_floor = !carried(uball); + + if ((control & BC_CHAIN) || + (!control && u.bc_order == BCPOS_CHAIN)) { + /* If the chain moved or nothing moved & chain on top. */ + if (on_floor) place_object(uball, ballx, bally); + place_object(uchain, chainx, chainy); /* chain on top */ + } else { + place_object(uchain, chainx, chainy); + if (on_floor) place_object(uball, ballx, bally); + /* ball on top */ + } + newsym(chainx, chainy); + if (on_floor) newsym(ballx, bally); + } + } +} + +/* return TRUE if the caller needs to place the ball and chain down again + * + * Should not be called while swallowed. Should be called before movement, + * because we might want to move the ball or chain to the hero's old position. + * + * It is called if we are moving. It is also called if we are teleporting + * *if* the ball doesn't move and we thus must drag the chain. It is not + * called for ordinary teleportation. + * + * allow_drag is only used in the ugly special case where teleporting must + * drag the chain, while an identical-looking movement must drag both the ball + * and chain. + */ +boolean +drag_ball(x, y, bc_control, ballx, bally, chainx, chainy, cause_delay, + allow_drag) +xchar x, y; +int *bc_control; +xchar *ballx, *bally, *chainx, *chainy; +boolean *cause_delay; +boolean allow_drag; +{ + struct trap *t = (struct trap *)0; + boolean already_in_rock; + + *ballx = uball->ox; + *bally = uball->oy; + *chainx = uchain->ox; + *chainy = uchain->oy; + *bc_control = 0; + *cause_delay = FALSE; + + if (dist2(x, y, uchain->ox, uchain->oy) <= 2) { /* nothing moved */ + move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy); + return TRUE; + } + + /* only need to move the chain? */ + if (carried(uball) || distmin(x, y, uball->ox, uball->oy) <= 2) { + xchar oldchainx = uchain->ox, oldchainy = uchain->oy; + *bc_control = BC_CHAIN; + move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy); + if (carried(uball)) { + /* move chain only if necessary */ + if (distmin(x, y, uchain->ox, uchain->oy) > 1) { + *chainx = u.ux; + *chainy = u.uy; + } + return TRUE; + } +#define CHAIN_IN_MIDDLE(chx, chy) \ +(distmin(x, y, chx, chy) <= 1 && distmin(chx, chy, uball->ox, uball->oy) <= 1) +#define IS_CHAIN_ROCK(x,y) \ +(IS_ROCK(levl[x][y].typ) || (IS_DOOR(levl[x][y].typ) && \ + (levl[x][y].doormask & (D_CLOSED|D_LOCKED)))) +/* Don't ever move the chain into solid rock. If we have to, then instead + * undo the move_bc() and jump to the drag ball code. Note that this also + * means the "cannot carry and drag" message will not appear, since unless we + * moved at least two squares there is no possibility of the chain position + * being in solid rock. + */ +#define SKIP_TO_DRAG { *chainx = oldchainx; *chainy = oldchainy; \ + move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); \ + goto drag; } + if (IS_CHAIN_ROCK(u.ux, u.uy) || IS_CHAIN_ROCK(*chainx, *chainy) + || IS_CHAIN_ROCK(uball->ox, uball->oy)) + already_in_rock = TRUE; + else + already_in_rock = FALSE; + + switch(dist2(x, y, uball->ox, uball->oy)) { + /* two spaces diagonal from ball, move chain inbetween */ + case 8: + *chainx = (uball->ox + x)/2; + *chainy = (uball->oy + y)/2; + if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock) + SKIP_TO_DRAG; + break; + + /* player is distance 2/1 from ball; move chain to one of the + * two spaces between + * @ + * __ + * 0 + */ + case 5: { + xchar tempx, tempy, tempx2, tempy2; + + /* find position closest to current position of chain */ + /* no effect if current position is already OK */ + if (abs(x - uball->ox) == 1) { + tempx = x; + tempx2 = uball->ox; + tempy = tempy2 = (uball->oy + y)/2; + } else { + tempx = tempx2 = (uball->ox + x)/2; + tempy = y; + tempy2 = uball->oy; + } + if (IS_CHAIN_ROCK(tempx, tempy) && + !IS_CHAIN_ROCK(tempx2, tempy2) && + !already_in_rock) { + if (allow_drag) { + /* Avoid pathological case *if* not teleporting: + * 0 0_ + * _X move northeast -----> X@ + * @ + */ + if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 && + dist2(x, y, tempx, tempy) == 1) + SKIP_TO_DRAG; + /* Avoid pathological case *if* not teleporting: + * 0 0 + * _X move east -----> X_ + * @ @ + */ + if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 && + dist2(x, y, tempx, tempy) == 2) + SKIP_TO_DRAG; + } + *chainx = tempx2; + *chainy = tempy2; + } else if (!IS_CHAIN_ROCK(tempx, tempy) && + IS_CHAIN_ROCK(tempx2, tempy2) && + !already_in_rock) { + if (allow_drag) { + if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 5 && + dist2(x, y, tempx2, tempy2) == 1) + SKIP_TO_DRAG; + if (dist2(u.ux, u.uy, uball->ox, uball->oy) == 4 && + dist2(x, y, tempx2, tempy2) == 2) + SKIP_TO_DRAG; + } + *chainx = tempx; + *chainy = tempy; + } else if (IS_CHAIN_ROCK(tempx, tempy) && + IS_CHAIN_ROCK(tempx2, tempy2) && + !already_in_rock) { + SKIP_TO_DRAG; + } else if (dist2(tempx, tempy, uchain->ox, uchain->oy) < + dist2(tempx2, tempy2, uchain->ox, uchain->oy) || + ((dist2(tempx, tempy, uchain->ox, uchain->oy) == + dist2(tempx2, tempy2, uchain->ox, uchain->oy)) && rn2(2))) { + *chainx = tempx; + *chainy = tempy; + } else { + *chainx = tempx2; + *chainy = tempy2; + } + break; + } + + /* ball is two spaces horizontal or vertical from player; move*/ + /* chain inbetween *unless* current chain position is OK */ + case 4: + if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy)) + break; + *chainx = (x + uball->ox)/2; + *chainy = (y + uball->oy)/2; + if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock) + SKIP_TO_DRAG; + break; + + /* ball is one space diagonal from player. Check for the + * following special case: + * @ + * _ moving southwest becomes @_ + * 0 0 + * (This will also catch teleporting that happens to resemble + * this case, but oh well.) Otherwise fall through. + */ + case 2: + if (dist2(x, y, uball->ox, uball->oy) == 2 && + dist2(x, y, uchain->ox, uchain->oy) == 4) { + if (uchain->oy == y) + *chainx = uball->ox; + else + *chainy = uball->oy; + if (IS_CHAIN_ROCK(*chainx, *chainy) && !already_in_rock) + SKIP_TO_DRAG; + break; + } + /* fall through */ + case 1: + case 0: + /* do nothing if possible */ + if (CHAIN_IN_MIDDLE(uchain->ox, uchain->oy)) + break; + /* otherwise try to drag chain to player's old position */ + if (CHAIN_IN_MIDDLE(u.ux, u.uy)) { + *chainx = u.ux; + *chainy = u.uy; + break; + } + /* otherwise use player's new position (they must have + teleported, for this to happen) */ + *chainx = x; + *chainy = y; + break; + + default: impossible("bad chain movement"); + break; + } +#undef SKIP_TO_DRAG +#undef IS_CHAIN_ROCK +#undef CHAIN_IN_MIDDLE + return TRUE; + } + +drag: + + if (near_capacity() > SLT_ENCUMBER && dist2(x, y, u.ux, u.uy) <= 2) { + You("cannot %sdrag the heavy iron ball.", + invent ? "carry all that and also " : ""); + nomul(0); + return FALSE; + } + + if ((is_pool(uchain->ox, uchain->oy) && + /* water not mere continuation of previous water */ + (levl[uchain->ox][uchain->oy].typ == POOL || + !is_pool(uball->ox, uball->oy) || + levl[uball->ox][uball->oy].typ == POOL)) + || ((t = t_at(uchain->ox, uchain->oy)) && + (t->ttyp == PIT || + t->ttyp == SPIKED_PIT || + t->ttyp == HOLE || + t->ttyp == TRAPDOOR)) ) { + + if (Levitation) { + You_feel("a tug from the iron ball."); + if (t) t->tseen = 1; + } else { + struct monst *victim; + + You("are jerked back by the iron ball!"); + if ((victim = m_at(uchain->ox, uchain->oy)) != 0) { + int tmp; + + tmp = -2 + Luck + find_mac(victim); + tmp += omon_adj(victim, uball, TRUE); + if (tmp >= rnd(20)) + (void) hmon(victim,uball,1); + else + miss(xname(uball), victim); + + } /* now check again in case mon died */ + if (!m_at(uchain->ox, uchain->oy)) { + u.ux = uchain->ox; + u.uy = uchain->oy; + newsym(u.ux0, u.uy0); + } + nomul(0); + + *bc_control = BC_BALL; + move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy); + *ballx = uchain->ox; + *bally = uchain->oy; + move_bc(0, *bc_control, *ballx, *bally, *chainx, *chainy); + spoteffects(TRUE); + return FALSE; + } + } + + *bc_control = BC_BALL|BC_CHAIN; + + move_bc(1, *bc_control, *ballx, *bally, *chainx, *chainy); + if (dist2(x, y, u.ux, u.uy) > 2) { + /* Awful case: we're still in range of the ball, so we thought we + * could only move the chain, but it turned out that the target + * square for the chain was rock, so we had to drag it instead. + * But we can't drag it either, because we teleported and are more + * than one square from our old position. Revert to the teleport + * behavior. + */ + *ballx = *chainx = x; + *bally = *chainy = y; + } else { + *ballx = uchain->ox; + *bally = uchain->oy; + *chainx = u.ux; + *chainy = u.uy; + } + *cause_delay = TRUE; + return TRUE; +} + +/* + * drop_ball() + * + * The punished hero drops or throws her iron ball. If the hero is + * blind, we must reset the order and glyph. Check for side effects. + * This routine expects the ball to be already placed. + * + * Should not be called while swallowed. + */ +void +drop_ball(x, y) +xchar x, y; +{ + if (Blind) { + u.bc_order = bc_order(); /* get the order */ + /* pick up glyph */ + u.bglyph = (u.bc_order) ? u.cglyph : levl[x][y].glyph; + } + + if (x != u.ux || y != u.uy) { + struct trap *t; + const char *pullmsg = "The ball pulls you out of the %s!"; + + if (u.utrap && u.utraptype != TT_INFLOOR) { + switch(u.utraptype) { + case TT_PIT: + pline(pullmsg, "pit"); + break; + case TT_WEB: + pline(pullmsg, "web"); + pline_The("web is destroyed!"); + deltrap(t_at(u.ux,u.uy)); + break; + case TT_LAVA: + pline(pullmsg, "lava"); + break; + case TT_BEARTRAP: { + register long side = rn2(3) ? LEFT_SIDE : RIGHT_SIDE; + pline(pullmsg, "bear trap"); + set_wounded_legs(side, rn1(1000, 500)); +#ifdef STEED + if (!u.usteed) +#endif + { + Your("%s %s is severely damaged.", + (side == LEFT_SIDE) ? "left" : "right", + body_part(LEG)); + losehp(2, "leg damage from being pulled out of a bear trap", + KILLED_BY); + } + break; + } + } + u.utrap = 0; + fill_pit(u.ux, u.uy); + } + + u.ux0 = u.ux; + u.uy0 = u.uy; + if (!Levitation && !MON_AT(x, y) && !u.utrap && + (is_pool(x, y) || + ((t = t_at(x, y)) && + (t->ttyp == PIT || t->ttyp == SPIKED_PIT || + t->ttyp == TRAPDOOR || t->ttyp == HOLE)))) { + u.ux = x; + u.uy = y; + } else { + u.ux = x - u.dx; + u.uy = y - u.dy; + } + vision_full_recalc = 1; /* hero has moved, recalculate vision later */ + + if (Blind) { + /* drop glyph under the chain */ + if (u.bc_felt & BC_CHAIN) + levl[uchain->ox][uchain->oy].glyph = u.cglyph; + u.bc_felt = 0; /* feel nothing */ + /* pick up new glyph */ + u.cglyph = (u.bc_order) ? u.bglyph : levl[u.ux][u.uy].glyph; + } + movobj(uchain,u.ux,u.uy); /* has a newsym */ + if (Blind) { + u.bc_order = bc_order(); + } + newsym(u.ux0,u.uy0); /* clean up old position */ + if (u.ux0 != u.ux || u.uy0 != u.uy) { + spoteffects(TRUE); + if (In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + } + } +} + + +STATIC_OVL void +litter() +{ + struct obj *otmp = invent, *nextobj; + int capacity = weight_cap(); + + while (otmp) { + nextobj = otmp->nobj; + if ((otmp != uball) && (rnd(capacity) <= (int)otmp->owt)) { + if (canletgo(otmp, "")) { + Your("%s you down the stairs.", + aobjnam(otmp, "follow")); + dropx(otmp); + } + } + otmp = nextobj; + } +} + +void +drag_down() +{ + boolean forward; + uchar dragchance = 3; + + /* + * Assume that the ball falls forward if: + * + * a) the character is wielding it, or + * b) the character has both hands available to hold it (i.e. is + * not wielding any weapon), or + * c) (perhaps) it falls forward out of his non-weapon hand + */ + + forward = carried(uball) && (uwep == uball || !uwep || !rn2(3)); + + if (carried(uball)) + You("lose your grip on the iron ball."); + + if (forward) { + if(rn2(6)) { + pline_The("iron ball drags you downstairs!"); + losehp(rnd(6), "dragged downstairs by an iron ball", + NO_KILLER_PREFIX); + litter(); + } + } else { + if(rn2(2)) { + pline_The("iron ball smacks into you!"); + losehp(rnd(20), "iron ball collision", KILLED_BY_AN); + exercise(A_STR, FALSE); + dragchance -= 2; + } + if( (int) dragchance >= rnd(6)) { + pline_The("iron ball drags you downstairs!"); + losehp(rnd(3), "dragged downstairs by an iron ball", + NO_KILLER_PREFIX); + exercise(A_STR, FALSE); + litter(); + } + } +} + +/*ball.c*/ diff --git a/src/bones.c b/src/bones.c new file mode 100644 index 0000000..585200a --- /dev/null +++ b/src/bones.c @@ -0,0 +1,474 @@ +/* SCCS Id: @(#)bones.c 3.4 2003/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" + +extern char bones[]; /* from files.c */ +#ifdef MFLOPPY +extern long bytes_counted; +#endif + +STATIC_DCL boolean FDECL(no_bones_level, (d_level *)); +STATIC_DCL void FDECL(goodfruit, (int)); +STATIC_DCL void FDECL(resetobjs,(struct obj *,BOOLEAN_P)); +STATIC_DCL void FDECL(drop_upon_death, (struct monst *, struct obj *)); + +STATIC_OVL boolean +no_bones_level(lev) +d_level *lev; +{ + extern d_level save_dlevel; /* in do.c */ + s_level *sptr; + + if (ledger_no(&save_dlevel)) assign_level(lev, &save_dlevel); + + return (boolean)(((sptr = Is_special(lev)) != 0 && !sptr->boneid) + || !dungeons[lev->dnum].boneid + /* no bones on the last or multiway branch levels */ + /* in any dungeon (level 1 isn't multiway). */ + || Is_botlevel(lev) || (Is_branchlev(lev) && lev->dlevel > 1) + /* no bones in the invocation level */ + || (In_hell(lev) && lev->dlevel == dunlevs_in_dungeon(lev) - 1) + ); +} + +/* Call this function for each fruit object saved in the bones level: it marks + * that particular type of fruit as existing (the marker is that that type's + * ID is positive instead of negative). This way, when we later save the + * chain of fruit types, we know to only save the types that exist. + */ +STATIC_OVL void +goodfruit(id) +int id; +{ + register struct fruit *f; + + for(f=ffruit; f; f=f->nextf) { + if(f->fid == -id) { + f->fid = id; + return; + } + } +} + +STATIC_OVL void +resetobjs(ochain,restore) +struct obj *ochain; +boolean restore; +{ + struct obj *otmp; + + for (otmp = ochain; otmp; otmp = otmp->nobj) { + if (otmp->cobj) + resetobjs(otmp->cobj,restore); + + if (((otmp->otyp != CORPSE || otmp->corpsenm < SPECIAL_PM) + && otmp->otyp != STATUE) + && (!otmp->oartifact || + (restore && (exist_artifact(otmp->otyp, ONAME(otmp)) + || is_quest_artifact(otmp))))) { + otmp->oartifact = 0; + otmp->onamelth = 0; + *ONAME(otmp) = '\0'; + } else if (otmp->oartifact && restore) + artifact_exists(otmp,ONAME(otmp),TRUE); + if (!restore) { + /* do not zero out o_ids for ghost levels anymore */ + + if(objects[otmp->otyp].oc_uses_known) otmp->known = 0; + otmp->dknown = otmp->bknown = 0; + otmp->rknown = 0; + otmp->invlet = 0; + otmp->no_charge = 0; + + if (otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe); +#ifdef MAIL + else if (otmp->otyp == SCR_MAIL) otmp->spe = 1; +#endif + else if (otmp->otyp == EGG) otmp->spe = 0; + else if (otmp->otyp == TIN) { + /* make tins of unique monster's meat be empty */ + if (otmp->corpsenm >= LOW_PM && + (mons[otmp->corpsenm].geno & G_UNIQ)) + otmp->corpsenm = NON_PM; + } else if (otmp->otyp == AMULET_OF_YENDOR) { + /* no longer the real Amulet */ + otmp->otyp = FAKE_AMULET_OF_YENDOR; + curse(otmp); + } else if (otmp->otyp == CANDELABRUM_OF_INVOCATION) { + if (otmp->lamplit) + end_burn(otmp, TRUE); + otmp->otyp = WAX_CANDLE; + otmp->age = 50L; /* assume used */ + if (otmp->spe > 0) + otmp->quan = (long)otmp->spe; + otmp->spe = 0; + otmp->owt = weight(otmp); + curse(otmp); + } else if (otmp->otyp == BELL_OF_OPENING) { + otmp->otyp = BELL; + curse(otmp); + } else if (otmp->otyp == SPE_BOOK_OF_THE_DEAD) { + otmp->otyp = SPE_BLANK_PAPER; + curse(otmp); + } + } + } +} + +STATIC_OVL void +drop_upon_death(mtmp, cont) +struct monst *mtmp; +struct obj *cont; +{ + struct obj *otmp; + + uswapwep = 0; /* ensure curse() won't cause swapwep to drop twice */ + while ((otmp = invent) != 0) { + obj_extract_self(otmp); + obj_no_longer_held(otmp); + + otmp->owornmask = 0; + /* lamps don't go out when dropped */ + if ((cont || artifact_light(otmp)) && obj_is_burning(otmp)) + end_burn(otmp, TRUE); /* smother in statue */ + + if(otmp->otyp == SLIME_MOLD) goodfruit(otmp->spe); + + if(rn2(5)) curse(otmp); + if (mtmp) + (void) add_to_minv(mtmp, otmp); + else if (cont) + (void) add_to_container(cont, otmp); + else + place_object(otmp, u.ux, u.uy); + } +#ifndef GOLDOBJ + if(u.ugold) { + long ugold = u.ugold; + if (mtmp) mtmp->mgold = ugold; + else if (cont) (void) add_to_container(cont, mkgoldobj(ugold)); + else (void)mkgold(ugold, u.ux, u.uy); + u.ugold = ugold; /* undo mkgoldobj()'s removal */ + } +#endif + if (cont) cont->owt = weight(cont); +} + +/* check whether bones are feasible */ +boolean +can_make_bones() +{ + register struct trap *ttmp; + + if (ledger_no(&u.uz) <= 0 || ledger_no(&u.uz) > maxledgerno()) + return FALSE; + if (no_bones_level(&u.uz)) + return FALSE; /* no bones for specific levels */ + if (u.uswallow) { + return FALSE; /* no bones when swallowed */ + } + if (!Is_branchlev(&u.uz)) { + /* no bones on non-branches with portals */ + for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + if (ttmp->ttyp == MAGIC_PORTAL) return FALSE; + } + + if(depth(&u.uz) <= 0 || /* bulletproofing for endgame */ + (!rn2(1 + (depth(&u.uz)>>2)) /* fewer ghosts on low levels */ +#ifdef WIZARD + && !wizard +#endif + )) return FALSE; + /* don't let multiple restarts generate multiple copies of objects + * in bones files */ + if (discover) return FALSE; + return TRUE; +} + +/* save bones and possessions of a deceased adventurer */ +void +savebones(corpse) +struct obj *corpse; +{ + int fd, x, y; + struct trap *ttmp; + struct monst *mtmp; + struct permonst *mptr; + struct fruit *f; + char c, *bonesid; + char whynot[BUFSZ]; + + /* caller has already checked `can_make_bones()' */ + + clear_bypasses(); + fd = open_bonesfile(&u.uz, &bonesid); + if (fd >= 0) { + (void) close(fd); + compress_bonesfile(); +#ifdef WIZARD + if (wizard) { + if (yn("Bones file already exists. Replace it?") == 'y') { + if (delete_bonesfile(&u.uz)) goto make_bones; + else pline("Cannot unlink old bones."); + } + } +#endif + return; + } + +#ifdef WIZARD + make_bones: +#endif + unleash_all(); + /* in case these characters are not in their home bases */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + mptr = mtmp->data; + if (mtmp->iswiz || mptr == &mons[PM_MEDUSA] || + mptr->msound == MS_NEMESIS || mptr->msound == MS_LEADER || + mptr == &mons[PM_VLAD_THE_IMPALER]) + mongone(mtmp); + } +#ifdef STEED + if (u.usteed) dismount_steed(DISMOUNT_BONES); +#endif + dmonsfree(); /* discard dead or gone monsters */ + + /* mark all fruits as nonexistent; when we come to them we'll mark + * them as existing (using goodfruit()) + */ + for(f=ffruit; f; f=f->nextf) f->fid = -f->fid; + + /* check iron balls separately--maybe they're not carrying it */ + if (uball) uball->owornmask = uchain->owornmask = 0; + + /* dispose of your possessions, usually cursed */ + if (u.ugrave_arise == (NON_PM - 1)) { + struct obj *otmp; + + /* embed your possessions in your statue */ + otmp = mk_named_object(STATUE, &mons[u.umonnum], + u.ux, u.uy, plname); + + drop_upon_death((struct monst *)0, otmp); + if (!otmp) return; /* couldn't make statue */ + mtmp = (struct monst *)0; + } else if (u.ugrave_arise < LOW_PM) { + /* drop everything */ + drop_upon_death((struct monst *)0, (struct obj *)0); + /* trick makemon() into allowing monster creation + * on your location + */ + in_mklev = TRUE; + mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, MM_NONAME); + in_mklev = FALSE; + if (!mtmp) return; + mtmp = christen_monst(mtmp, plname); + if (corpse) + (void) obj_attach_mid(corpse, mtmp->m_id); + } else { + /* give your possessions to the monster you become */ + in_mklev = TRUE; + mtmp = makemon(&mons[u.ugrave_arise], u.ux, u.uy, NO_MM_FLAGS); + in_mklev = FALSE; + if (!mtmp) { + drop_upon_death((struct monst *)0, (struct obj *)0); + return; + } + mtmp = christen_monst(mtmp, plname); + newsym(u.ux, u.uy); + Your("body rises from the dead as %s...", + an(mons[u.ugrave_arise].mname)); + display_nhwindow(WIN_MESSAGE, FALSE); + drop_upon_death(mtmp, (struct obj *)0); + m_dowear(mtmp, TRUE); + } + if (mtmp) { + mtmp->m_lev = (u.ulevel ? u.ulevel : 1); + mtmp->mhp = mtmp->mhpmax = u.uhpmax; + mtmp->female = flags.female; + mtmp->msleeping = 1; + } + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + resetobjs(mtmp->minvent,FALSE); + /* do not zero out m_ids for bones levels any more */ + mtmp->mlstmv = 0L; + if(mtmp->mtame) mtmp->mtame = mtmp->mpeaceful = 0; + } + for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + ttmp->madeby_u = 0; + ttmp->tseen = (ttmp->ttyp == HOLE); + } + resetobjs(fobj,FALSE); + resetobjs(level.buriedobjlist, FALSE); + + /* Hero is no longer on the map. */ + u.ux = u.uy = 0; + + /* Clear all memory from the level. */ + for(x=0; x freediskspace(bones)) { /* not enough room */ +# ifdef WIZARD + if (wizard) + pline("Insufficient space to create bones file."); +# endif + (void) close(fd); + cancel_bonesfile(); + return; + } + co_false(); /* make sure stuff before savelev() gets written */ + } +#endif /* MFLOPPY */ + + store_version(fd); + bwrite(fd, (genericptr_t) &c, sizeof c); + bwrite(fd, (genericptr_t) bonesid, (unsigned) c); /* DD.nnn */ + savefruitchn(fd, WRITE_SAVE | FREE_SAVE); + update_mlstmv(); /* update monsters for eventual restoration */ + savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE); + bclose(fd); + commit_bonesfile(&u.uz); + compress_bonesfile(); +} + +int +getbones() +{ + register int fd; + register int ok; + char c, *bonesid, oldbonesid[10]; + + if(discover) /* save bones files for real games */ + return(0); + + /* wizard check added by GAN 02/05/87 */ + if(rn2(3) /* only once in three times do we find bones */ +#ifdef WIZARD + && !wizard +#endif + ) return(0); + if(no_bones_level(&u.uz)) return(0); + fd = open_bonesfile(&u.uz, &bonesid); + if (fd < 0) return(0); + + if ((ok = uptodate(fd, bones)) == 0) { +#ifdef WIZARD + if (!wizard) +#endif + pline("Discarding unuseable bones; no need to panic..."); + } else { +#ifdef WIZARD + if(wizard) { + if(yn("Get bones?") == 'n') { + (void) close(fd); + compress_bonesfile(); + return(0); + } + } +#endif + mread(fd, (genericptr_t) &c, sizeof c); /* length incl. '\0' */ + mread(fd, (genericptr_t) oldbonesid, (unsigned) c); /* DD.nnn */ + if (strcmp(bonesid, oldbonesid) != 0) { + char errbuf[BUFSZ]; + + Sprintf(errbuf, "This is bones level '%s', not '%s'!", + oldbonesid, bonesid); +#ifdef WIZARD + if (wizard) { + pline("%s", errbuf); + ok = FALSE; /* won't die of trickery */ + } +#endif + trickery(errbuf); + } else { + register struct monst *mtmp; + + getlev(fd, 0, 0, TRUE); + + /* Note that getlev() now keeps tabs on unique + * monsters such as demon lords, and tracks the + * birth counts of all species just as makemon() + * does. If a bones monster is extinct or has been + * subject to genocide, their mhpmax will be + * set to the magic DEFUNCT_MONSTER cookie value. + */ + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (mtmp->mhpmax == DEFUNCT_MONSTER) { +#if defined(DEBUG) && defined(WIZARD) + if (wizard) + pline("Removing defunct monster %s from bones.", + mtmp->data->mname); +#endif + mongone(mtmp); + } else + /* to correctly reset named artifacts on the level */ + resetobjs(mtmp->minvent,TRUE); + } + resetobjs(fobj,TRUE); + resetobjs(level.buriedobjlist,TRUE); + } + } + (void) close(fd); + +#ifdef WIZARD + if(wizard) { + if(yn("Unlink bones?") == 'n') { + compress_bonesfile(); + return(ok); + } + } +#endif + if (!delete_bonesfile(&u.uz)) { + /* When N games try to simultaneously restore the same + * bones file, N-1 of them will fail to delete it + * (the first N-1 under AmigaDOS, the last N-1 under UNIX). + * So no point in a mysterious message for a normal event + * -- just generate a new level for those N-1 games. + */ + /* pline("Cannot unlink bones."); */ + return(0); + } + return(ok); +} + +/*bones.c*/ diff --git a/src/botl.c b/src/botl.c new file mode 100644 index 0000000..6534ad7 --- /dev/null +++ b/src/botl.c @@ -0,0 +1,309 @@ +/* SCCS Id: @(#)botl.c 3.4 1996/07/15 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVL0 +extern const char *hu_stat[]; /* defined in eat.c */ + +const char * const enc_stat[] = { + "", + "Burdened", + "Stressed", + "Strained", + "Overtaxed", + "Overloaded" +}; + +STATIC_DCL void NDECL(bot1); +STATIC_DCL void NDECL(bot2); +#endif /* OVL0 */ + +/* MAXCO must hold longest uncompressed status line, and must be larger + * than COLNO + * + * longest practical second status line at the moment is + * Astral Plane $:12345 HP:700(700) Pw:111(111) AC:-127 Xp:30/123456789 + * T:123456 Satiated Conf FoodPois Ill Blind Stun Hallu Overloaded + * -- or somewhat over 130 characters + */ +#if COLNO <= 140 +#define MAXCO 160 +#else +#define MAXCO (COLNO+20) +#endif + +#ifndef OVLB +STATIC_DCL int mrank_sz; +#else /* OVLB */ +STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ +#endif /* OVLB */ + +STATIC_DCL const char *NDECL(rank); + +#ifdef OVL1 + +/* convert experience level (1..30) to rank index (0..8) */ +int +xlev_to_rank(xlev) +int xlev; +{ + return (xlev <= 2) ? 0 : (xlev <= 30) ? ((xlev + 2) / 4) : 8; +} + +#if 0 /* not currently needed */ +/* convert rank index (0..8) to experience level (1..30) */ +int +rank_to_xlev(rank) +int rank; +{ + return (rank <= 0) ? 1 : (rank <= 8) ? ((rank * 4) - 2) : 30; +} +#endif + +const char * +rank_of(lev, monnum, female) + int lev; + short monnum; + boolean female; +{ + register struct Role *role; + register int i; + + + /* Find the role */ + for (role = (struct Role *) roles; role->name.m; role++) + if (monnum == role->malenum || monnum == role->femalenum) + break; + if (!role->name.m) + role = &urole; + + /* Find the rank */ + for (i = xlev_to_rank((int)lev); i >= 0; i--) { + if (female && role->rank[i].f) return (role->rank[i].f); + if (role->rank[i].m) return (role->rank[i].m); + } + + /* Try the role name, instead */ + if (female && role->name.f) return (role->name.f); + else if (role->name.m) return (role->name.m); + return ("Player"); +} + + +STATIC_OVL const char * +rank() +{ + return(rank_of(u.ulevel, Role_switch, flags.female)); +} + +int +title_to_mon(str, rank_indx, title_length) +const char *str; +int *rank_indx, *title_length; +{ + register int i, j; + + + /* Loop through each of the roles */ + for (i = 0; roles[i].name.m; i++) + for (j = 0; j < 9; j++) { + if (roles[i].rank[j].m && !strncmpi(str, + roles[i].rank[j].m, strlen(roles[i].rank[j].m))) { + if (rank_indx) *rank_indx = j; + if (title_length) *title_length = strlen(roles[i].rank[j].m); + return roles[i].malenum; + } + if (roles[i].rank[j].f && !strncmpi(str, + roles[i].rank[j].f, strlen(roles[i].rank[j].f))) { + if (rank_indx) *rank_indx = j; + if (title_length) *title_length = strlen(roles[i].rank[j].f); + return ((roles[i].femalenum != NON_PM) ? + roles[i].femalenum : roles[i].malenum); + } + } + return NON_PM; +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +max_rank_sz() +{ + register int i, r, maxr = 0; + for (i = 0; i < 9; i++) { + if (urole.rank[i].m && (r = strlen(urole.rank[i].m)) > maxr) maxr = r; + if (urole.rank[i].f && (r = strlen(urole.rank[i].f)) > maxr) maxr = r; + } + mrank_sz = maxr; + return; +} + +#endif /* OVLB */ +#ifdef OVL0 + +#ifdef SCORE_ON_BOTL +long +botl_score() +{ + int deepest = deepest_lev_reached(FALSE); +#ifndef GOLDOBJ + long ugold = u.ugold + hidden_gold(); + + if ((ugold -= u.ugold0) < 0L) ugold = 0L; + return ugold + u.urexp + (long)(50 * (deepest - 1)) +#else + long umoney = money_cnt(invent) + hidden_gold(); + + if ((umoney -= u.umoney0) < 0L) umoney = 0L; + return umoney + u.urexp + (long)(50 * (deepest - 1)) +#endif + + (long)(deepest > 30 ? 10000 : + deepest > 20 ? 1000*(deepest - 20) : 0); +} +#endif + +STATIC_OVL void +bot1() +{ + char newbot1[MAXCO]; + register char *nb; + register int i,j; + + Strcpy(newbot1, plname); + if('a' <= newbot1[0] && newbot1[0] <= 'z') newbot1[0] += 'A'-'a'; + newbot1[10] = 0; + Sprintf(nb = eos(newbot1)," the "); + + if (Upolyd) { + char mbot[BUFSZ]; + int k = 0; + + Strcpy(mbot, mons[u.umonnum].mname); + while(mbot[k] != 0) { + if ((k == 0 || (k > 0 && mbot[k-1] == ' ')) && + 'a' <= mbot[k] && mbot[k] <= 'z') + mbot[k] += 'A' - 'a'; + k++; + } + Sprintf(nb = eos(nb), mbot); + } else + Sprintf(nb = eos(nb), rank()); + + Sprintf(nb = eos(nb)," "); + i = mrank_sz + 15; + j = (nb + 2) - newbot1; /* aka strlen(newbot1) but less computation */ + if((i - j) > 0) + Sprintf(nb = eos(nb),"%*s", i-j, " "); /* pad with spaces */ + if (ACURR(A_STR) > 18) { + if (ACURR(A_STR) > STR18(100)) + Sprintf(nb = eos(nb),"St:%2d ",ACURR(A_STR)-100); + else if (ACURR(A_STR) < STR18(100)) + Sprintf(nb = eos(nb), "St:18/%02d ",ACURR(A_STR)-18); + else + Sprintf(nb = eos(nb),"St:18/** "); + } else + Sprintf(nb = eos(nb), "St:%-1d ",ACURR(A_STR)); + Sprintf(nb = eos(nb), + "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), ACURR(A_CHA)); + Sprintf(nb = eos(nb), (u.ualign.type == A_CHAOTIC) ? " Chaotic" : + (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful"); +#ifdef SCORE_ON_BOTL + if (flags.showscore) + Sprintf(nb = eos(nb), " S:%ld", botl_score()); +#endif + curs(WIN_STATUS, 1, 0); + putstr(WIN_STATUS, 0, newbot1); +} + +/* provide the name of the current level for display by various ports */ +int +describe_level(buf) +char *buf; +{ + int ret = 1; + + /* TODO: Add in dungeon name */ + if (Is_knox(&u.uz)) + Sprintf(buf, "%s ", dungeons[u.uz.dnum].dname); + else if (In_quest(&u.uz)) + Sprintf(buf, "Home %d ", dunlev(&u.uz)); + else if (In_endgame(&u.uz)) + Sprintf(buf, + Is_astralevel(&u.uz) ? "Astral Plane " : "End Game "); + else { + /* ports with more room may expand this one */ + Sprintf(buf, "Dlvl:%-2d ", depth(&u.uz)); + ret = 0; + } + return ret; +} + +STATIC_OVL void +bot2() +{ + char newbot2[MAXCO]; + register char *nb; + int hp, hpmax; + int cap = near_capacity(); + + hp = Upolyd ? u.mh : u.uhp; + hpmax = Upolyd ? u.mhmax : u.uhpmax; + + if(hp < 0) hp = 0; + (void) describe_level(newbot2); + Sprintf(nb = eos(newbot2), + "%c:%-2ld HP:%d(%d) Pw:%d(%d) AC:%-2d", oc_syms[COIN_CLASS], +#ifndef GOLDOBJ + u.ugold, +#else + money_cnt(invent), +#endif + hp, hpmax, u.uen, u.uenmax, u.uac); + + if (Upolyd) + Sprintf(nb = eos(nb), " HD:%d", mons[u.umonnum].mlevel); +#ifdef EXP_ON_BOTL + else if(flags.showexp) + Sprintf(nb = eos(nb), " Xp:%u/%-1ld", u.ulevel,u.uexp); +#endif + else + Sprintf(nb = eos(nb), " Exp:%u", u.ulevel); + + if(flags.time) + Sprintf(nb = eos(nb), " T:%ld", moves); + if(strcmp(hu_stat[u.uhs], " ")) { + Sprintf(nb = eos(nb), " "); + Strcat(newbot2, hu_stat[u.uhs]); + } + if(Confusion) Sprintf(nb = eos(nb), " Conf"); + if(Sick) { + if (u.usick_type & SICK_VOMITABLE) + Sprintf(nb = eos(nb), " FoodPois"); + if (u.usick_type & SICK_NONVOMITABLE) + Sprintf(nb = eos(nb), " Ill"); + } + if(Blind) Sprintf(nb = eos(nb), " Blind"); + if(Stunned) Sprintf(nb = eos(nb), " Stun"); + if(Hallucination) Sprintf(nb = eos(nb), " Hallu"); + if(Slimed) Sprintf(nb = eos(nb), " Slime"); + if(cap > UNENCUMBERED) + Sprintf(nb = eos(nb), " %s", enc_stat[cap]); + curs(WIN_STATUS, 1, 1); + putstr(WIN_STATUS, 0, newbot2); +} + +void +bot() +{ + bot1(); + bot2(); + flags.botl = flags.botlx = 0; +} + +#endif /* OVL0 */ + +/*botl.c*/ diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 0000000..b12c0e5 --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,2555 @@ +/* SCCS Id: @(#)cmd.c 3.4 2003/02/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "func_tab.h" +/* #define DEBUG */ /* uncomment for debugging */ + +/* + * Some systems may have getchar() return EOF for various reasons, and + * we should not quit before seeing at least NR_OF_EOFS consecutive EOFs. + */ +#if defined(SYSV) || defined(DGUX) || defined(HPUX) +#define NR_OF_EOFS 20 +#endif + +#define CMD_TRAVEL (char)0x90 + +#ifdef DEBUG +/* + * only one "wiz_debug_cmd" routine should be available (in whatever + * module you are trying to debug) or things are going to get rather + * hard to link :-) + */ +extern int NDECL(wiz_debug_cmd); +#endif + +#ifdef DUMB /* stuff commented out in extern.h, but needed here */ +extern int NDECL(doapply); /**/ +extern int NDECL(dorub); /**/ +extern int NDECL(dojump); /**/ +extern int NDECL(doextlist); /**/ +extern int NDECL(dodrop); /**/ +extern int NDECL(doddrop); /**/ +extern int NDECL(dodown); /**/ +extern int NDECL(doup); /**/ +extern int NDECL(donull); /**/ +extern int NDECL(dowipe); /**/ +extern int NDECL(do_mname); /**/ +extern int NDECL(ddocall); /**/ +extern int NDECL(dotakeoff); /**/ +extern int NDECL(doremring); /**/ +extern int NDECL(dowear); /**/ +extern int NDECL(doputon); /**/ +extern int NDECL(doddoremarm); /**/ +extern int NDECL(dokick); /**/ +extern int NDECL(dofire); /**/ +extern int NDECL(dothrow); /**/ +extern int NDECL(doeat); /**/ +extern int NDECL(done2); /**/ +extern int NDECL(doengrave); /**/ +extern int NDECL(dopickup); /**/ +extern int NDECL(ddoinv); /**/ +extern int NDECL(dotypeinv); /**/ +extern int NDECL(dolook); /**/ +extern int NDECL(doprgold); /**/ +extern int NDECL(doprwep); /**/ +extern int NDECL(doprarm); /**/ +extern int NDECL(doprring); /**/ +extern int NDECL(dopramulet); /**/ +extern int NDECL(doprtool); /**/ +extern int NDECL(dosuspend); /**/ +extern int NDECL(doforce); /**/ +extern int NDECL(doopen); /**/ +extern int NDECL(doclose); /**/ +extern int NDECL(dosh); /**/ +extern int NDECL(dodiscovered); /**/ +extern int NDECL(doset); /**/ +extern int NDECL(dotogglepickup); /**/ +extern int NDECL(dowhatis); /**/ +extern int NDECL(doquickwhatis); /**/ +extern int NDECL(dowhatdoes); /**/ +extern int NDECL(dohelp); /**/ +extern int NDECL(dohistory); /**/ +extern int NDECL(doloot); /**/ +extern int NDECL(dodrink); /**/ +extern int NDECL(dodip); /**/ +extern int NDECL(dosacrifice); /**/ +extern int NDECL(dopray); /**/ +extern int NDECL(doturn); /**/ +extern int NDECL(doredraw); /**/ +extern int NDECL(doread); /**/ +extern int NDECL(dosave); /**/ +extern int NDECL(dosearch); /**/ +extern int NDECL(doidtrap); /**/ +extern int NDECL(dopay); /**/ +extern int NDECL(dosit); /**/ +extern int NDECL(dotalk); /**/ +extern int NDECL(docast); /**/ +extern int NDECL(dovspell); /**/ +extern int NDECL(dotele); /**/ +extern int NDECL(dountrap); /**/ +extern int NDECL(doversion); /**/ +extern int NDECL(doextversion); /**/ +extern int NDECL(doswapweapon); /**/ +extern int NDECL(dowield); /**/ +extern int NDECL(dowieldquiver); /**/ +extern int NDECL(dozap); /**/ +extern int NDECL(doorganize); /**/ +#endif /* DUMB */ + +#ifdef OVL1 +static int NDECL((*timed_occ_fn)); +#endif /* OVL1 */ + +STATIC_PTR int NDECL(doprev_message); +STATIC_PTR int NDECL(timed_occupation); +STATIC_PTR int NDECL(doextcmd); +STATIC_PTR int NDECL(domonability); +STATIC_PTR int NDECL(dotravel); +# ifdef WIZARD +STATIC_PTR int NDECL(wiz_wish); +STATIC_PTR int NDECL(wiz_identify); +STATIC_PTR int NDECL(wiz_map); +STATIC_PTR int NDECL(wiz_genesis); +STATIC_PTR int NDECL(wiz_where); +STATIC_PTR int NDECL(wiz_detect); +STATIC_PTR int NDECL(wiz_panic); +STATIC_PTR int NDECL(wiz_polyself); +STATIC_PTR int NDECL(wiz_level_tele); +STATIC_PTR int NDECL(wiz_level_change); +STATIC_PTR int NDECL(wiz_show_seenv); +STATIC_PTR int NDECL(wiz_show_vision); +STATIC_PTR int NDECL(wiz_mon_polycontrol); +STATIC_PTR int NDECL(wiz_show_wmodes); +#if defined(__BORLANDC__) && !defined(_WIN32) +extern void FDECL(show_borlandc_stats, (winid)); +#endif +#ifdef DEBUG_MIGRATING_MONS +STATIC_PTR int NDECL(wiz_migrate_mons); +#endif +STATIC_DCL void FDECL(count_obj, (struct obj *, long *, long *, BOOLEAN_P, BOOLEAN_P)); +STATIC_DCL void FDECL(obj_chain, (winid, const char *, struct obj *, long *, long *)); +STATIC_DCL void FDECL(mon_invent_chain, (winid, const char *, struct monst *, long *, long *)); +STATIC_DCL void FDECL(mon_chain, (winid, const char *, struct monst *, long *, long *)); +STATIC_DCL void FDECL(contained, (winid, const char *, long *, long *)); +STATIC_PTR int NDECL(wiz_show_stats); +# ifdef PORT_DEBUG +STATIC_DCL int NDECL(wiz_port_debug); +# endif +# endif +STATIC_PTR int NDECL(enter_explore_mode); +STATIC_PTR int NDECL(doattributes); +STATIC_PTR int NDECL(doconduct); /**/ +STATIC_PTR boolean NDECL(minimal_enlightenment); + +#ifdef OVLB +STATIC_DCL void FDECL(enlght_line, (const char *,const char *,const char *)); +STATIC_DCL char *FDECL(enlght_combatinc, (const char *,int,int,char *)); +#ifdef UNIX +static void NDECL(end_of_input); +#endif +#endif /* OVLB */ + +static const char* readchar_queue=""; + +STATIC_DCL char *NDECL(parse); +STATIC_DCL boolean FDECL(help_dir, (CHAR_P,const char *)); + +#ifdef OVL1 + +STATIC_PTR int +doprev_message() +{ + return nh_doprev_message(); +} + +/* Count down by decrementing multi */ +STATIC_PTR int +timed_occupation() +{ + (*timed_occ_fn)(); + if (multi > 0) + multi--; + return multi > 0; +} + +/* If you have moved since initially setting some occupations, they + * now shouldn't be able to restart. + * + * The basic rule is that if you are carrying it, you can continue + * since it is with you. If you are acting on something at a distance, + * your orientation to it must have changed when you moved. + * + * The exception to this is taking off items, since they can be taken + * off in a number of ways in the intervening time, screwing up ordering. + * + * Currently: Take off all armor. + * Picking Locks / Forcing Chests. + * Setting traps. + */ +void +reset_occupations() +{ + reset_remarm(); + reset_pick(); + reset_trapset(); +} + +/* If a time is given, use it to timeout this function, otherwise the + * function times out by its own means. + */ +void +set_occupation(fn, txt, xtime) +int NDECL((*fn)); +const char *txt; +int xtime; +{ + if (xtime) { + occupation = timed_occupation; + timed_occ_fn = fn; + } else + occupation = fn; + occtxt = txt; + occtime = 0; + return; +} + +#ifdef REDO + +static char NDECL(popch); + +/* Provide a means to redo the last command. The flag `in_doagain' is set + * to true while redoing the command. This flag is tested in commands that + * require additional input (like `throw' which requires a thing and a + * direction), and the input prompt is not shown. Also, while in_doagain is + * TRUE, no keystrokes can be saved into the saveq. + */ +#define BSIZE 20 +static char pushq[BSIZE], saveq[BSIZE]; +static NEARDATA int phead, ptail, shead, stail; + +static char +popch() { + /* If occupied, return '\0', letting tgetch know a character should + * be read from the keyboard. If the character read is not the + * ABORT character (as checked in pcmain.c), that character will be + * pushed back on the pushq. + */ + if (occupation) return '\0'; + if (in_doagain) return(char)((shead != stail) ? saveq[stail++] : '\0'); + else return(char)((phead != ptail) ? pushq[ptail++] : '\0'); +} + +char +pgetchar() { /* curtesy of aeb@cwi.nl */ + register int ch; + + if(!(ch = popch())) + ch = nhgetch(); + return((char)ch); +} + +/* A ch == 0 resets the pushq */ +void +pushch(ch) +char ch; +{ + if (!ch) + phead = ptail = 0; + if (phead < BSIZE) + pushq[phead++] = ch; + return; +} + +/* A ch == 0 resets the saveq. Only save keystrokes when not + * replaying a previous command. + */ +void +savech(ch) +char ch; +{ + if (!in_doagain) { + if (!ch) + phead = ptail = shead = stail = 0; + else if (shead < BSIZE) + saveq[shead++] = ch; + } + return; +} +#endif /* REDO */ + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_PTR int +doextcmd() /* here after # - now read a full-word command */ +{ + int idx, retval; + + /* keep repeating until we don't run help or quit */ + do { + idx = get_ext_cmd(); + if (idx < 0) return 0; /* quit */ + + retval = (*extcmdlist[idx].ef_funct)(); + } while (extcmdlist[idx].ef_funct == doextlist); + + return retval; +} + +int +doextlist() /* here after #? - now list all full-word commands */ +{ + register const struct ext_func_tab *efp; + char buf[BUFSZ]; + winid datawin; + + datawin = create_nhwindow(NHW_TEXT); + putstr(datawin, 0, ""); + putstr(datawin, 0, " Extended Commands List"); + putstr(datawin, 0, ""); + putstr(datawin, 0, " Press '#', then type:"); + putstr(datawin, 0, ""); + + for(efp = extcmdlist; efp->ef_txt; efp++) { + Sprintf(buf, " %-15s - %s.", efp->ef_txt, efp->ef_desc); + putstr(datawin, 0, buf); + } + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); + return 0; +} + +#ifdef TTY_GRAPHICS +#define MAX_EXT_CMD 40 /* Change if we ever have > 40 ext cmds */ +/* + * This is currently used only by the tty port and is + * controlled via runtime option 'extmenu' + */ +int +extcmd_via_menu() /* here after # - now show pick-list of possible commands */ +{ + const struct ext_func_tab *efp; + menu_item *pick_list = (menu_item *)0; + winid win; + anything any; + const struct ext_func_tab *choices[MAX_EXT_CMD]; + char buf[BUFSZ]; + char cbuf[QBUFSZ], prompt[QBUFSZ], fmtstr[20]; + int i, n, nchoices, acount; + int ret, biggest; + int accelerator, prevaccelerator; + int matchlevel = 0; + + ret = 0; + cbuf[0] = '\0'; + biggest = 0; + while (!ret) { + i = n = 0; + accelerator = 0; + any.a_void = 0; + /* populate choices */ + for(efp = extcmdlist; efp->ef_txt; efp++) { + if (!matchlevel || !strncmp(efp->ef_txt, cbuf, matchlevel)) { + choices[i++] = efp; + if ((int)strlen(efp->ef_desc) > biggest) { + biggest = strlen(efp->ef_desc); + Sprintf(fmtstr,"%%-%ds", biggest + 15); + } +#ifdef DEBUG + if (i >= MAX_EXT_CMD - 2) { + impossible("Exceeded %d extended commands in doextcmd() menu", + MAX_EXT_CMD - 2); + return 0; + } +#endif + } + } + choices[i] = (struct ext_func_tab *)0; + nchoices = i; + /* if we're down to one, we have our selection so get out of here */ + if (nchoices == 1) { + for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++) + if (!strncmpi(extcmdlist[i].ef_txt, cbuf, matchlevel)) { + ret = i; + break; + } + break; + } + + /* otherwise... */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + prevaccelerator = 0; + acount = 0; + for(i = 0; choices[i]; ++i) { + accelerator = choices[i]->ef_txt[matchlevel]; + if (accelerator != prevaccelerator || nchoices < (ROWNO - 3)) { + if (acount) { + /* flush the extended commands for that letter already in buf */ + Sprintf(buf, fmtstr, prompt); + any.a_char = prevaccelerator; + add_menu(win, NO_GLYPH, &any, any.a_char, 0, + ATR_NONE, buf, FALSE); + acount = 0; + } + } + prevaccelerator = accelerator; + if (!acount || nchoices < (ROWNO - 3)) { + Sprintf(prompt, "%s [%s]", choices[i]->ef_txt, + choices[i]->ef_desc); + } else if (acount == 1) { + Sprintf(prompt, "%s or %s", choices[i-1]->ef_txt, + choices[i]->ef_txt); + } else { + Strcat(prompt," or "); + Strcat(prompt, choices[i]->ef_txt); + } + ++acount; + } + if (acount) { + /* flush buf */ + Sprintf(buf, fmtstr, prompt); + any.a_char = prevaccelerator; + add_menu(win, NO_GLYPH, &any, any.a_char, 0, ATR_NONE, buf, FALSE); + } + Sprintf(prompt, "Extended Command: %s", cbuf); + end_menu(win, prompt); + n = select_menu(win, PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n==1) { + if (matchlevel > (QBUFSZ - 2)) { + free((genericptr_t)pick_list); +#ifdef DEBUG + impossible("Too many characters (%d) entered in extcmd_via_menu()", + matchlevel); +#endif + ret = -1; + } else { + cbuf[matchlevel++] = pick_list[0].item.a_char; + cbuf[matchlevel] = '\0'; + free((genericptr_t)pick_list); + } + } else { + if (matchlevel) { + ret = 0; + matchlevel = 0; + } else + ret = -1; + } + } + return ret; +} +#endif + +/* #monster command - use special monster ability while polymorphed */ +STATIC_PTR int +domonability() +{ + if (can_breathe(youmonst.data)) return dobreathe(); + else if (attacktype(youmonst.data, AT_SPIT)) return dospit(); + else if (youmonst.data->mlet == S_NYMPH) return doremove(); + else if (attacktype(youmonst.data, AT_GAZE)) return dogaze(); + else if (is_were(youmonst.data)) return dosummon(); + else if (webmaker(youmonst.data)) return dospinweb(); + else if (is_hider(youmonst.data)) return dohide(); + else if (is_mind_flayer(youmonst.data)) return domindblast(); + else if (u.umonnum == PM_GREMLIN) { + if(IS_FOUNTAIN(levl[u.ux][u.uy].typ)) { + if (split_mon(&youmonst, (struct monst *)0)) + dryup(u.ux, u.uy, TRUE); + } else There("is no fountain here."); + } else if (is_unicorn(youmonst.data)) { + use_unicorn_horn((struct obj *)0); + return 1; + } else if (youmonst.data->msound == MS_SHRIEK) { + You("shriek."); + if(u.uburied) + pline("Unfortunately sound does not carry well through rock."); + else aggravate(); + } else if (Upolyd) + pline("Any special ability you may have is purely reflexive."); + else You("don't have a special ability in your normal form!"); + return 0; +} + +STATIC_PTR int +enter_explore_mode() +{ + if(!discover && !wizard) { + pline("Beware! From explore mode there will be no return to normal game."); + if (yn("Do you want to enter explore mode?") == 'y') { + clear_nhwindow(WIN_MESSAGE); + You("are now in non-scoring explore mode."); + discover = TRUE; + } + else { + clear_nhwindow(WIN_MESSAGE); + pline("Resuming normal game."); + } + } + return 0; +} + +#ifdef WIZARD + +/* ^W command - wish for something */ +STATIC_PTR int +wiz_wish() /* Unlimited wishes for debug mode by Paul Polderman */ +{ + if (wizard) { + boolean save_verbose = flags.verbose; + + flags.verbose = FALSE; + makewish(); + flags.verbose = save_verbose; + (void) encumber_msg(); + } else + pline("Unavailable command '^W'."); + return 0; +} + +/* ^I command - identify hero's inventory */ +STATIC_PTR int +wiz_identify() +{ + if (wizard) identify_pack(0); + else pline("Unavailable command '^I'."); + return 0; +} + +/* ^F command - reveal the level map and any traps on it */ +STATIC_PTR int +wiz_map() +{ + if (wizard) { + struct trap *t; + long save_Hconf = HConfusion, + save_Hhallu = HHallucination; + + HConfusion = HHallucination = 0L; + for (t = ftrap; t != 0; t = t->ntrap) { + t->tseen = 1; + map_trap(t, TRUE); + } + do_mapping(); + HConfusion = save_Hconf; + HHallucination = save_Hhallu; + } else + pline("Unavailable command '^F'."); + return 0; +} + +/* ^G command - generate monster(s); a count prefix will be honored */ +STATIC_PTR int +wiz_genesis() +{ + if (wizard) (void) create_particular(); + else pline("Unavailable command '^G'."); + return 0; +} + +/* ^O command - display dungeon layout */ +STATIC_PTR int +wiz_where() +{ + if (wizard) (void) print_dungeon(FALSE, (schar *)0, (xchar *)0); + else pline("Unavailable command '^O'."); + return 0; +} + +/* ^E command - detect unseen (secret doors, traps, hidden monsters) */ +STATIC_PTR int +wiz_detect() +{ + if(wizard) (void) findit(); + else pline("Unavailable command '^E'."); + return 0; +} + +/* ^V command - level teleport */ +STATIC_PTR int +wiz_level_tele() +{ + if (wizard) level_tele(); + else pline("Unavailable command '^V'."); + return 0; +} + +/* #monpolycontrol command - choose new form for shapechangers, polymorphees */ +STATIC_PTR int +wiz_mon_polycontrol() +{ + iflags.mon_polycontrol = !iflags.mon_polycontrol; + pline("Monster polymorph control is %s.", + iflags.mon_polycontrol ? "on" : "off"); + return 0; +} + +/* #levelchange command - adjust hero's experience level */ +STATIC_PTR int +wiz_level_change() +{ + char buf[BUFSZ]; + int newlevel; + int ret; + + getlin("To what experience level do you want to be set?", buf); + (void)mungspaces(buf); + if (buf[0] == '\033' || buf[0] == '\0') ret = 0; + else ret = sscanf(buf, "%d", &newlevel); + + if (ret != 1) { + pline(Never_mind); + return 0; + } + if (newlevel == u.ulevel) { + You("are already that experienced."); + } else if (newlevel < u.ulevel) { + if (u.ulevel == 1) { + You("are already as inexperienced as you can get."); + return 0; + } + if (newlevel < 1) newlevel = 1; + while (u.ulevel > newlevel) + losexp("#levelchange"); + } else { + if (u.ulevel >= MAXULEV) { + You("are already as experienced as you can get."); + return 0; + } + if (newlevel > MAXULEV) newlevel = MAXULEV; + while (u.ulevel < newlevel) + pluslvl(FALSE); + } + u.ulevelmax = u.ulevel; + return 0; +} + +/* #panic command - test program's panic handling */ +STATIC_PTR int +wiz_panic() +{ + if (yn("Do you want to call panic() and end your game?") == 'y') + panic("crash test."); + return 0; +} + +/* #polyself command - change hero's form */ +STATIC_PTR int +wiz_polyself() +{ + polyself(TRUE); + return 0; +} + +/* #seenv command */ +STATIC_PTR int +wiz_show_seenv() +{ + winid win; + int x, y, v, startx, stopx, curx; + char row[COLNO+1]; + + win = create_nhwindow(NHW_TEXT); + /* + * Each seenv description takes up 2 characters, so center + * the seenv display around the hero. + */ + startx = max(1, u.ux-(COLNO/4)); + stopx = min(startx+(COLNO/2), COLNO); + /* can't have a line exactly 80 chars long */ + if (stopx - startx == COLNO/2) startx++; + + for (y = 0; y < ROWNO; y++) { + for (x = startx, curx = 0; x < stopx; x++, curx += 2) { + if (x == u.ux && y == u.uy) { + row[curx] = row[curx+1] = '@'; + } else { + v = levl[x][y].seenv & 0xff; + if (v == 0) + row[curx] = row[curx+1] = ' '; + else + Sprintf(&row[curx], "%02x", v); + } + } + /* remove trailing spaces */ + for (x = curx-1; x >= 0; x--) + if (row[x] != ' ') break; + row[x+1] = '\0'; + + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +/* #vision command */ +STATIC_PTR int +wiz_show_vision() +{ + winid win; + int x, y, v; + char row[COLNO+1]; + + win = create_nhwindow(NHW_TEXT); + Sprintf(row, "Flags: 0x%x could see, 0x%x in sight, 0x%x temp lit", + COULD_SEE, IN_SIGHT, TEMP_LIT); + putstr(win, 0, row); + putstr(win, 0, ""); + for (y = 0; y < ROWNO; y++) { + for (x = 1; x < COLNO; x++) { + if (x == u.ux && y == u.uy) + row[x] = '@'; + else { + v = viz_array[y][x]; /* data access should be hidden */ + if (v == 0) + row[x] = ' '; + else + row[x] = '0' + viz_array[y][x]; + } + } + /* remove trailing spaces */ + for (x = COLNO-1; x >= 1; x--) + if (row[x] != ' ') break; + row[x+1] = '\0'; + + putstr(win, 0, &row[1]); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +/* #wmode command */ +STATIC_PTR int +wiz_show_wmodes() +{ + winid win; + int x,y; + char row[COLNO+1]; + struct rm *lev; + + win = create_nhwindow(NHW_TEXT); + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + lev = &levl[x][y]; + if (x == u.ux && y == u.uy) + row[x] = '@'; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + row[x] = '0' + (lev->wall_info & WM_MASK); + else if (lev->typ == CORR) + row[x] = '#'; + else if (IS_ROOM(lev->typ) || IS_DOOR(lev->typ)) + row[x] = '.'; + else + row[x] = 'x'; + } + row[COLNO] = '\0'; + putstr(win, 0, row); + } + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} + +#endif /* WIZARD */ + + +/* -enlightenment and conduct- */ +static winid en_win; +static const char + You_[] = "You ", + are[] = "are ", were[] = "were ", + have[] = "have ", had[] = "had ", + can[] = "can ", could[] = "could "; +static const char + have_been[] = "have been ", + have_never[] = "have never ", never[] = "never "; + +#define enl_msg(prefix,present,past,suffix) \ + enlght_line(prefix, final ? past : present, suffix) +#define you_are(attr) enl_msg(You_,are,were,attr) +#define you_have(attr) enl_msg(You_,have,had,attr) +#define you_can(attr) enl_msg(You_,can,could,attr) +#define you_have_been(goodthing) enl_msg(You_,have_been,were,goodthing) +#define you_have_never(badthing) enl_msg(You_,have_never,never,badthing) +#define you_have_X(something) enl_msg(You_,have,(const char *)"",something) + +static void +enlght_line(start, middle, end) +const char *start, *middle, *end; +{ + char buf[BUFSZ]; + + Sprintf(buf, "%s%s%s.", start, middle, end); + putstr(en_win, 0, buf); +} + +/* format increased damage or chance to hit */ +static char * +enlght_combatinc(inctyp, incamt, final, outbuf) +const char *inctyp; +int incamt, final; +char *outbuf; +{ + char numbuf[24]; + const char *modif, *bonus; + + if (final +#ifdef WIZARD + || wizard +#endif + ) { + Sprintf(numbuf, "%s%d", + (incamt > 0) ? "+" : "", incamt); + modif = (const char *) numbuf; + } else { + int absamt = abs(incamt); + + if (absamt <= 3) modif = "small"; + else if (absamt <= 6) modif = "moderate"; + else if (absamt <= 12) modif = "large"; + else modif = "huge"; + } + bonus = (incamt > 0) ? "bonus" : "penalty"; + /* "bonus to hit" vs "damage bonus" */ + if (!strcmp(inctyp, "damage")) { + const char *ctmp = inctyp; + inctyp = bonus; + bonus = ctmp; + } + Sprintf(outbuf, "%s %s %s", an(modif), bonus, inctyp); + return outbuf; +} + +void +enlightenment(final) +int final; /* 0 => still in progress; 1 => over, survived; 2 => dead */ +{ + int ltmp; + char buf[BUFSZ]; + + en_win = create_nhwindow(NHW_MENU); + putstr(en_win, 0, final ? "Final Attributes:" : "Current Attributes:"); + putstr(en_win, 0, ""); + +#ifdef ELBERETH + if (u.uevent.uhand_of_elbereth) { + static const char * const hofe_titles[3] = { + "the Hand of Elbereth", + "the Envoy of Balance", + "the Glory of Arioch" + }; + you_are(hofe_titles[u.uevent.uhand_of_elbereth - 1]); + } +#endif + + /* note: piousness 20 matches MIN_QUEST_ALIGN (quest.h) */ + if (u.ualign.record >= 20) you_are("piously aligned"); + else if (u.ualign.record > 13) you_are("devoutly aligned"); + else if (u.ualign.record > 8) you_are("fervently aligned"); + else if (u.ualign.record > 3) you_are("stridently aligned"); + else if (u.ualign.record == 3) you_are("aligned"); + else if (u.ualign.record > 0) you_are("haltingly aligned"); + else if (u.ualign.record == 0) you_are("nominally aligned"); + else if (u.ualign.record >= -3) you_have("strayed"); + else if (u.ualign.record >= -8) you_have("sinned"); + else you_have("transgressed"); +#ifdef WIZARD + if (wizard) { + Sprintf(buf, " %d", u.ualign.record); + enl_msg("Your alignment ", "is", "was", buf); + } +#endif + + /*** Resistances to troubles ***/ + if (Fire_resistance) you_are("fire resistant"); + if (Cold_resistance) you_are("cold resistant"); + if (Sleep_resistance) you_are("sleep resistant"); + if (Disint_resistance) you_are("disintegration-resistant"); + if (Shock_resistance) you_are("shock resistant"); + if (Poison_resistance) you_are("poison resistant"); + if (Drain_resistance) you_are("level-drain resistant"); + if (Sick_resistance) you_are("immune to sickness"); + if (Antimagic) you_are("magic-protected"); + if (Acid_resistance) you_are("acid resistant"); + if (Stone_resistance) + you_are("petrification resistant"); + if (Invulnerable) you_are("invulnerable"); + if (u.uedibility) you_can("recognize detrimental food"); + + /*** Troubles ***/ + if (Halluc_resistance) + enl_msg("You resist", "", "ed", " hallucinations"); + if (final) { + if (Hallucination) you_are("hallucinating"); + if (Stunned) you_are("stunned"); + if (Confusion) you_are("confused"); + if (Blinded) you_are("blinded"); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) + you_are("sick from food poisoning"); + if (u.usick_type & SICK_NONVOMITABLE) + you_are("sick from illness"); + } + } + if (Stoned) you_are("turning to stone"); + if (Slimed) you_are("turning into slime"); + if (Strangled) you_are((u.uburied) ? "buried" : "being strangled"); + if (Glib) { + Sprintf(buf, "slippery %s", makeplural(body_part(FINGER))); + you_have(buf); + } + if (Fumbling) enl_msg("You fumble", "", "d", ""); + if (Wounded_legs +#ifdef STEED + && !u.usteed +#endif + ) { + Sprintf(buf, "wounded %s", makeplural(body_part(LEG))); + you_have(buf); + } +#if defined(WIZARD) && defined(STEED) + if (Wounded_legs && u.usteed && wizard) { + Strcpy(buf, x_monnam(u.usteed, ARTICLE_YOUR, (char *)0, + SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION, FALSE)); + *buf = highc(*buf); + enl_msg(buf, " has", " had", " wounded legs"); + } +#endif + if (Sleeping) enl_msg("You ", "fall", "fell", " asleep"); + if (Hunger) enl_msg("You hunger", "", "ed", " rapidly"); + + /*** Vision and senses ***/ + if (See_invisible) enl_msg(You_, "see", "saw", " invisible"); + if (Blind_telepat) you_are("telepathic"); + if (Warning) you_are("warned"); + if (Warn_of_mon && flags.warntype) { + Sprintf(buf, "aware of the presence of %s", + (flags.warntype & M2_ORC) ? "orcs" : + (flags.warntype & M2_DEMON) ? "demons" : + something); + you_are(buf); + } + if (Undead_warning) you_are("warned of undead"); + if (Searching) you_have("automatic searching"); + if (Clairvoyant) you_are("clairvoyant"); + if (Infravision) you_have("infravision"); + if (Detect_monsters) you_are("sensing the presence of monsters"); + if (u.umconf) you_are("going to confuse monsters"); + + /*** Appearance and behavior ***/ + if (Adornment) { + int adorn = 0; + + if(uleft && uleft->otyp == RIN_ADORNMENT) adorn += uleft->spe; + if(uright && uright->otyp == RIN_ADORNMENT) adorn += uright->spe; + if (adorn < 0) + you_are("poorly adorned"); + else + you_are("adorned"); + } + if (Invisible) you_are("invisible"); + else if (Invis) you_are("invisible to others"); + /* ordinarily "visible" is redundant; this is a special case for + the situation when invisibility would be an expected attribute */ + else if ((HInvis || EInvis || pm_invisible(youmonst.data)) && BInvis) + you_are("visible"); + if (Displaced) you_are("displaced"); + if (Stealth) you_are("stealthy"); + if (Aggravate_monster) enl_msg("You aggravate", "", "d", " monsters"); + if (Conflict) enl_msg("You cause", "", "d", " conflict"); + + /*** Transportation ***/ + if (Jumping) you_can("jump"); + if (Teleportation) you_can("teleport"); + if (Teleport_control) you_have("teleport control"); + if (Lev_at_will) you_are("levitating, at will"); + else if (Levitation) you_are("levitating"); /* without control */ + else if (Flying) you_can("fly"); + if (Wwalking) you_can("walk on water"); + if (Swimming) you_can("swim"); + if (Breathless) you_can("survive without air"); + else if (Amphibious) you_can("breathe water"); + if (Passes_walls) you_can("walk through walls"); +#ifdef STEED + /* If you die while dismounting, u.usteed is still set. Since several + * places in the done() sequence depend on u.usteed, just detect this + * special case. */ + if (u.usteed && (final < 2 || strcmp(killer, "riding accident"))) { + Sprintf(buf, "riding %s", y_monnam(u.usteed)); + you_are(buf); + } +#endif + if (u.uswallow) { + Sprintf(buf, "swallowed by %s", a_monnam(u.ustuck)); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%u)", u.uswldtim); +#endif + you_are(buf); + } else if (u.ustuck) { + Sprintf(buf, "%s %s", + (Upolyd && sticks(youmonst.data)) ? "holding" : "held by", + a_monnam(u.ustuck)); + you_are(buf); + } + + /*** Physical attributes ***/ + if (u.uhitinc) + you_have(enlght_combatinc("to hit", u.uhitinc, final, buf)); + if (u.udaminc) + you_have(enlght_combatinc("damage", u.udaminc, final, buf)); + if (Slow_digestion) you_have("slower digestion"); + if (Regeneration) enl_msg("You regenerate", "", "d", ""); + if (u.uspellprot || Protection) { + int prot = 0; + + if(uleft && uleft->otyp == RIN_PROTECTION) prot += uleft->spe; + if(uright && uright->otyp == RIN_PROTECTION) prot += uright->spe; + if (HProtection & INTRINSIC) prot += u.ublessed; + prot += u.uspellprot; + + if (prot < 0) + you_are("ineffectively protected"); + else + you_are("protected"); + } + if (Protection_from_shape_changers) + you_are("protected from shape changers"); + if (Polymorph) you_are("polymorphing"); + if (Polymorph_control) you_have("polymorph control"); + if (u.ulycn >= LOW_PM) { + Strcpy(buf, an(mons[u.ulycn].mname)); + you_are(buf); + } + if (Upolyd) { + if (u.umonnum == u.ulycn) Strcpy(buf, "in beast form"); + else Sprintf(buf, "polymorphed into %s", an(youmonst.data->mname)); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.mtimedone); +#endif + you_are(buf); + } + if (Unchanging) you_can("not change from your current form"); + if (Fast) you_are(Very_fast ? "very fast" : "fast"); + if (Reflecting) you_have("reflection"); + if (Free_action) you_have("free action"); + if (Fixed_abil) you_have("fixed abilities"); + if (Lifesaved) + enl_msg("Your life ", "will be", "would have been", " saved"); + if (u.twoweap) you_are("wielding two weapons at once"); + + /*** Miscellany ***/ + if (Luck) { + ltmp = abs((int)Luck); + Sprintf(buf, "%s%slucky", + ltmp >= 10 ? "extremely " : ltmp >= 5 ? "very " : "", + Luck < 0 ? "un" : ""); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", Luck); +#endif + you_are(buf); + } +#ifdef WIZARD + else if (wizard) enl_msg("Your luck ", "is", "was", " zero"); +#endif + if (u.moreluck > 0) you_have("extra luck"); + else if (u.moreluck < 0) you_have("reduced luck"); + if (carrying(LUCKSTONE) || stone_luck(TRUE)) { + ltmp = stone_luck(FALSE); + if (ltmp <= 0) + enl_msg("Bad luck ", "does", "did", " not time out for you"); + if (ltmp >= 0) + enl_msg("Good luck ", "does", "did", " not time out for you"); + } + + if (u.ugangr) { + Sprintf(buf, " %sangry with you", + u.ugangr > 6 ? "extremely " : u.ugangr > 3 ? "very " : ""); +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.ugangr); +#endif + enl_msg(u_gname(), " is", " was", buf); + } else + /* + * We need to suppress this when the game is over, because death + * can change the value calculated by can_pray(), potentially + * resulting in a false claim that you could have prayed safely. + */ + if (!final) { +#if 0 + /* "can [not] safely pray" vs "could [not] have safely prayed" */ + Sprintf(buf, "%s%ssafely pray%s", can_pray(FALSE) ? "" : "not ", + final ? "have " : "", final ? "ed" : ""); +#else + Sprintf(buf, "%ssafely pray", can_pray(FALSE) ? "" : "not "); +#endif +#ifdef WIZARD + if (wizard) Sprintf(eos(buf), " (%d)", u.ublesscnt); +#endif + you_can(buf); + } + + { + const char *p; + + buf[0] = '\0'; + if (final < 2) { /* still in progress, or quit/escaped/ascended */ + p = "survived after being killed "; + switch (u.umortality) { + case 0: p = !final ? (char *)0 : "survived"; break; + case 1: Strcpy(buf, "once"); break; + case 2: Strcpy(buf, "twice"); break; + case 3: Strcpy(buf, "thrice"); break; + default: Sprintf(buf, "%d times", u.umortality); + break; + } + } else { /* game ended in character's death */ + p = "are dead"; + switch (u.umortality) { + case 0: impossible("dead without dying?"); + case 1: break; /* just "are dead" */ + default: Sprintf(buf, " (%d%s time!)", u.umortality, + ordin(u.umortality)); + break; + } + } + if (p) enl_msg(You_, "have been killed ", p, buf); + } + + display_nhwindow(en_win, TRUE); + destroy_nhwindow(en_win); + return; +} + +/* + * Courtesy function for non-debug, non-explorer mode players + * to help refresh them about who/what they are. + * Returns FALSE if menu cancelled (dismissed with ESC), TRUE otherwise. + */ +STATIC_OVL boolean +minimal_enlightenment() +{ + winid tmpwin; + menu_item *selected; + anything any; + int genidx, n; + char buf[BUFSZ], buf2[BUFSZ]; + static const char untabbed_fmtstr[] = "%-15s: %-12s"; + static const char untabbed_deity_fmtstr[] = "%-17s%s"; + static const char tabbed_fmtstr[] = "%s:\t%-12s"; + static const char tabbed_deity_fmtstr[] = "%s\t%s"; + static const char *fmtstr; + static const char *deity_fmtstr; + + fmtstr = iflags.menu_tab_sep ? tabbed_fmtstr : untabbed_fmtstr; + deity_fmtstr = iflags.menu_tab_sep ? + tabbed_deity_fmtstr : untabbed_deity_fmtstr; + any.a_void = 0; + buf[0] = buf2[0] = '\0'; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Starting", FALSE); + + /* Starting name, race, role, gender */ + Sprintf(buf, fmtstr, "name", plname); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "race", urace.noun); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "role", + (flags.initgend && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + Sprintf(buf, fmtstr, "gender", genders[flags.initgend].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Starting alignment */ + Sprintf(buf, fmtstr, "alignment", align_str(u.ualignbase[A_ORIGINAL])); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Current name, race, role, gender */ + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Current", FALSE); + Sprintf(buf, fmtstr, "race", Upolyd ? youmonst.data->mname : urace.noun); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + if (Upolyd) { + Sprintf(buf, fmtstr, "role (base)", + (u.mfemale && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } else { + Sprintf(buf, fmtstr, "role", + (flags.female && urole.name.f) ? urole.name.f : urole.name.m); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } + /* don't want poly_gender() here; it forces `2' for non-humanoids */ + genidx = is_neuter(youmonst.data) ? 2 : flags.female; + Sprintf(buf, fmtstr, "gender", genders[genidx].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + if (Upolyd && (int)u.mfemale != genidx) { + Sprintf(buf, fmtstr, "gender (base)", genders[u.mfemale].adj); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + } + + /* Current alignment */ + Sprintf(buf, fmtstr, "alignment", align_str(u.ualign.type)); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + /* Deity list */ + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", FALSE); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, "Deities", FALSE); + Sprintf(buf2, deity_fmtstr, align_gname(A_CHAOTIC), + (u.ualignbase[A_ORIGINAL] == u.ualign.type + && u.ualign.type == A_CHAOTIC) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_CHAOTIC) ? " (s)" : + (u.ualign.type == A_CHAOTIC) ? " (c)" : ""); + Sprintf(buf, fmtstr, "Chaotic", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + Sprintf(buf2, deity_fmtstr, align_gname(A_NEUTRAL), + (u.ualignbase[A_ORIGINAL] == u.ualign.type + && u.ualign.type == A_NEUTRAL) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_NEUTRAL) ? " (s)" : + (u.ualign.type == A_NEUTRAL) ? " (c)" : ""); + Sprintf(buf, fmtstr, "Neutral", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + Sprintf(buf2, deity_fmtstr, align_gname(A_LAWFUL), + (u.ualignbase[A_ORIGINAL] == u.ualign.type && + u.ualign.type == A_LAWFUL) ? " (s,c)" : + (u.ualignbase[A_ORIGINAL] == A_LAWFUL) ? " (s)" : + (u.ualign.type == A_LAWFUL) ? " (c)" : ""); + Sprintf(buf, fmtstr, "Lawful", buf2); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, FALSE); + + end_menu(tmpwin, "Base Attributes"); + n = select_menu(tmpwin, PICK_NONE, &selected); + destroy_nhwindow(tmpwin); + return (n != -1); +} + +STATIC_PTR int +doattributes() +{ + if (!minimal_enlightenment()) + return 0; + if (wizard || discover) + enlightenment(0); + return 0; +} + +/* KMH, #conduct + * (shares enlightenment's tense handling) + */ +STATIC_PTR int +doconduct() +{ + show_conduct(0); + return 0; +} + +void +show_conduct(final) +int final; +{ + char buf[BUFSZ]; + int ngenocided; + + /* Create the conduct window */ + en_win = create_nhwindow(NHW_MENU); + putstr(en_win, 0, "Voluntary challenges:"); + putstr(en_win, 0, ""); + + if (!u.uconduct.food) + enl_msg(You_, "have gone", "went", " without food"); + /* But beverages are okay */ + else if (!u.uconduct.unvegan) + you_have_X("followed a strict vegan diet"); + else if (!u.uconduct.unvegetarian) + you_have_been("vegetarian"); + + if (!u.uconduct.gnostic) + you_have_been("an atheist"); + + if (!u.uconduct.weaphit) + you_have_never("hit with a wielded weapon"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "used a wielded weapon %ld time%s", + u.uconduct.weaphit, plur(u.uconduct.weaphit)); + you_have_X(buf); + } +#endif + if (!u.uconduct.killer) + you_have_been("a pacifist"); + + if (!u.uconduct.literate) + you_have_been("illiterate"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "read items or engraved %ld time%s", + u.uconduct.literate, plur(u.uconduct.literate)); + you_have_X(buf); + } +#endif + + ngenocided = num_genocides(); + if (ngenocided == 0) { + you_have_never("genocided any monsters"); + } else { + Sprintf(buf, "genocided %d type%s of monster%s", + ngenocided, plur(ngenocided), plur(ngenocided)); + you_have_X(buf); + } + + if (!u.uconduct.polypiles) + you_have_never("polymorphed an object"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "polymorphed %ld item%s", + u.uconduct.polypiles, plur(u.uconduct.polypiles)); + you_have_X(buf); + } +#endif + + if (!u.uconduct.polyselfs) + you_have_never("changed form"); +#ifdef WIZARD + else if (wizard) { + Sprintf(buf, "changed form %ld time%s", + u.uconduct.polyselfs, plur(u.uconduct.polyselfs)); + you_have_X(buf); + } +#endif + + if (!u.uconduct.wishes) + you_have_X("used no wishes"); + else { + Sprintf(buf, "used %ld wish%s", + u.uconduct.wishes, (u.uconduct.wishes > 1L) ? "es" : ""); + you_have_X(buf); + + if (!u.uconduct.wisharti) + enl_msg(You_, "have not wished", "did not wish", + " for any artifacts"); + } + + /* Pop up the window and wait for a key */ + display_nhwindow(en_win, TRUE); + destroy_nhwindow(en_win); +} + +#endif /* OVLB */ +#ifdef OVL1 + +#ifndef M +# ifndef NHSTDC +# define M(c) (0x80 | (c)) +# else +# define M(c) ((c) - 128) +# endif /* NHSTDC */ +#endif +#ifndef C +#define C(c) (0x1f & (c)) +#endif + +static const struct func_tab cmdlist[] = { + {C('d'), FALSE, dokick}, /* "D" is for door!...? Msg is in dokick.c */ +#ifdef WIZARD + {C('e'), TRUE, wiz_detect}, + {C('f'), TRUE, wiz_map}, + {C('g'), TRUE, wiz_genesis}, + {C('i'), TRUE, wiz_identify}, +#endif + {C('l'), TRUE, doredraw}, /* if number_pad is set */ +#ifdef WIZARD + {C('o'), TRUE, wiz_where}, +#endif + {C('p'), TRUE, doprev_message}, + {C('r'), TRUE, doredraw}, + {C('t'), TRUE, dotele}, +#ifdef WIZARD + {C('v'), TRUE, wiz_level_tele}, + {C('w'), TRUE, wiz_wish}, +#endif + {C('x'), TRUE, doattributes}, +#ifdef SUSPEND + {C('z'), TRUE, dosuspend}, +#endif + {'a', FALSE, doapply}, + {'A', FALSE, doddoremarm}, + {M('a'), TRUE, doorganize}, +/* 'b', 'B' : go sw */ + {'c', FALSE, doclose}, + {'C', TRUE, do_mname}, + {M('c'), TRUE, dotalk}, + {'d', FALSE, dodrop}, + {'D', FALSE, doddrop}, + {M('d'), FALSE, dodip}, + {'e', FALSE, doeat}, + {'E', FALSE, doengrave}, + {M('e'), TRUE, enhance_weapon_skill}, + {'f', FALSE, dofire}, +/* 'F' : fight (one time) */ + {M('f'), FALSE, doforce}, +/* 'g', 'G' : multiple go */ +/* 'h', 'H' : go west */ + {'h', TRUE, dohelp}, /* if number_pad is set */ + {'i', TRUE, ddoinv}, + {'I', TRUE, dotypeinv}, /* Robert Viduya */ + {M('i'), TRUE, doinvoke}, +/* 'j', 'J', 'k', 'K', 'l', 'L', 'm', 'M', 'n', 'N' : move commands */ + {'j', FALSE, dojump}, /* if number_pad is on */ + {M('j'), FALSE, dojump}, + {'k', FALSE, dokick}, /* if number_pad is on */ + {'l', FALSE, doloot}, /* if number_pad is on */ + {M('l'), FALSE, doloot}, +/* 'n' prefixes a count if number_pad is on */ + {M('m'), TRUE, domonability}, + {'N', TRUE, ddocall}, /* if number_pad is on */ + {M('n'), TRUE, ddocall}, + {M('N'), TRUE, ddocall}, + {'o', FALSE, doopen}, + {'O', TRUE, doset}, + {M('o'), FALSE, dosacrifice}, + {'p', FALSE, dopay}, + {'P', FALSE, doputon}, + {M('p'), TRUE, dopray}, + {'q', FALSE, dodrink}, + {'Q', FALSE, dowieldquiver}, + {M('q'), TRUE, done2}, + {'r', FALSE, doread}, + {'R', FALSE, doremring}, + {M('r'), FALSE, dorub}, + {'s', TRUE, dosearch, "searching"}, + {'S', TRUE, dosave}, + {M('s'), FALSE, dosit}, + {'t', FALSE, dothrow}, + {'T', FALSE, dotakeoff}, + {M('t'), TRUE, doturn}, +/* 'u', 'U' : go ne */ + {'u', FALSE, dountrap}, /* if number_pad is on */ + {M('u'), FALSE, dountrap}, + {'v', TRUE, doversion}, + {'V', TRUE, dohistory}, + {M('v'), TRUE, doextversion}, + {'w', FALSE, dowield}, + {'W', FALSE, dowear}, + {M('w'), FALSE, dowipe}, + {'x', FALSE, doswapweapon}, + {'X', TRUE, enter_explore_mode}, +/* 'y', 'Y' : go nw */ + {'z', FALSE, dozap}, + {'Z', TRUE, docast}, + {'<', FALSE, doup}, + {'>', FALSE, dodown}, + {'/', TRUE, dowhatis}, + {'&', TRUE, dowhatdoes}, + {'?', TRUE, dohelp}, + {M('?'), TRUE, doextlist}, +#ifdef SHELL + {'!', TRUE, dosh}, +#endif + {'.', TRUE, donull, "waiting"}, + {' ', TRUE, donull, "waiting"}, + {',', FALSE, dopickup}, + {':', TRUE, dolook}, + {';', TRUE, doquickwhatis}, + {'^', TRUE, doidtrap}, + {'\\', TRUE, dodiscovered}, /* Robert Viduya */ + {'@', TRUE, dotogglepickup}, + {M('2'), FALSE, dotwoweapon}, + {WEAPON_SYM, TRUE, doprwep}, + {ARMOR_SYM, TRUE, doprarm}, + {RING_SYM, TRUE, doprring}, + {AMULET_SYM, TRUE, dopramulet}, + {TOOL_SYM, TRUE, doprtool}, + {'*', TRUE, doprinuse}, /* inventory of all equipment in use */ + {GOLD_SYM, TRUE, doprgold}, + {SPBOOK_SYM, TRUE, dovspell}, /* Mike Stephenson */ + {'#', TRUE, doextcmd}, + {'_', TRUE, dotravel}, + {0,0,0,0} +}; + +struct ext_func_tab extcmdlist[] = { + {"adjust", "adjust inventory letters", doorganize, TRUE}, + {"chat", "talk to someone", dotalk, TRUE}, /* converse? */ + {"conduct", "list which challenges you have adhered to", doconduct, TRUE}, + {"dip", "dip an object into something", dodip, FALSE}, + {"enhance", "advance or check weapons skills", enhance_weapon_skill, + TRUE}, + {"force", "force a lock", doforce, FALSE}, + {"invoke", "invoke an object's powers", doinvoke, TRUE}, + {"jump", "jump to a location", dojump, FALSE}, + {"loot", "loot a box on the floor", doloot, FALSE}, + {"monster", "use a monster's special ability", domonability, TRUE}, + {"name", "name an item or type of object", ddocall, TRUE}, + {"offer", "offer a sacrifice to the gods", dosacrifice, FALSE}, + {"pray", "pray to the gods for help", dopray, TRUE}, + {"quit", "exit without saving current game", done2, TRUE}, +#ifdef STEED + {"ride", "ride (or stop riding) a monster", doride, FALSE}, +#endif + {"rub", "rub a lamp or a stone", dorub, FALSE}, + {"sit", "sit down", dosit, FALSE}, + {"turn", "turn undead", doturn, TRUE}, + {"twoweapon", "toggle two-weapon combat", dotwoweapon, FALSE}, + {"untrap", "untrap something", dountrap, FALSE}, + {"version", "list compile time options for this version of NetHack", + doextversion, TRUE}, + {"wipe", "wipe off your face", dowipe, FALSE}, + {"?", "get this list of extended commands", doextlist, TRUE}, +#if defined(WIZARD) + /* + * There must be a blank entry here for every entry in the table + * below. + */ + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, +#ifdef DEBUG_MIGRATING_MONS + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, +#ifdef PORT_DEBUG + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, + {(char *)0, (char *)0, donull, TRUE}, +#ifdef DEBUG + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE}, +#endif + {(char *)0, (char *)0, donull, TRUE} /* sentinel */ +}; + +#if defined(WIZARD) +static const struct ext_func_tab debug_extcmdlist[] = { + {"levelchange", "change experience level", wiz_level_change, TRUE}, + {"lightsources", "show mobile light sources", wiz_light_sources, TRUE}, +#ifdef DEBUG_MIGRATING_MONS + {"migratemons", "migrate n random monsters", wiz_migrate_mons, TRUE}, +#endif + {"monpolycontrol", "control monster polymorphs", wiz_mon_polycontrol, TRUE}, + {"panic", "test panic routine (fatal to game)", wiz_panic, TRUE}, + {"polyself", "polymorph self", wiz_polyself, TRUE}, +#ifdef PORT_DEBUG + {"portdebug", "wizard port debug command", wiz_port_debug, TRUE}, +#endif + {"seenv", "show seen vectors", wiz_show_seenv, TRUE}, + {"stats", "show memory statistics", wiz_show_stats, TRUE}, + {"timeout", "look at timeout queue", wiz_timeout_queue, TRUE}, + {"vision", "show vision array", wiz_show_vision, TRUE}, +#ifdef DEBUG + {"wizdebug", "wizard debug command", wiz_debug_cmd, TRUE}, +#endif + {"wmode", "show wall modes", wiz_show_wmodes, TRUE}, + {(char *)0, (char *)0, donull, TRUE} +}; + +/* + * Insert debug commands into the extended command list. This function + * assumes that the last entry will be the help entry. + * + * You must add entries in ext_func_tab every time you add one to the + * debug_extcmdlist(). + */ +void +add_debug_extended_commands() +{ + int i, j, k, n; + + /* count the # of help entries */ + for (n = 0; extcmdlist[n].ef_txt[0] != '?'; n++) + ; + + for (i = 0; debug_extcmdlist[i].ef_txt; i++) { + for (j = 0; j < n; j++) + if (strcmp(debug_extcmdlist[i].ef_txt, extcmdlist[j].ef_txt) < 0) break; + + /* insert i'th debug entry into extcmdlist[j], pushing down */ + for (k = n; k >= j; --k) + extcmdlist[k+1] = extcmdlist[k]; + extcmdlist[j] = debug_extcmdlist[i]; + n++; /* now an extra entry */ + } +} + + +static const char template[] = "%-18s %4ld %6ld"; +static const char count_str[] = " count bytes"; +static const char separator[] = "------------------ ----- ------"; + +STATIC_OVL void +count_obj(chain, total_count, total_size, top, recurse) + struct obj *chain; + long *total_count; + long *total_size; + boolean top; + boolean recurse; +{ + long count, size; + struct obj *obj; + + for (count = size = 0, obj = chain; obj; obj = obj->nobj) { + if (top) { + count++; + size += sizeof(struct obj) + obj->oxlth + obj->onamelth; + } + if (recurse && obj->cobj) + count_obj(obj->cobj, total_count, total_size, TRUE, TRUE); + } + *total_count += count; + *total_size += size; +} + +STATIC_OVL void +obj_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct obj *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + + count_obj(chain, &count, &size, TRUE, FALSE); + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +mon_invent_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct monst *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + for (mon = chain; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, TRUE, FALSE); + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +contained(win, src, total_count, total_size) + winid win; + const char *src; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count = 0, size = 0; + struct monst *mon; + + count_obj(invent, &count, &size, FALSE, TRUE); + count_obj(fobj, &count, &size, FALSE, TRUE); + count_obj(level.buriedobjlist, &count, &size, FALSE, TRUE); + count_obj(migrating_objs, &count, &size, FALSE, TRUE); + /* DEADMONSTER check not required in this loop since they have no inventory */ + for (mon = fmon; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + for (mon = migrating_mons; mon; mon = mon->nmon) + count_obj(mon->minvent, &count, &size, FALSE, TRUE); + + *total_count += count; *total_size += size; + + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +STATIC_OVL void +mon_chain(win, src, chain, total_count, total_size) + winid win; + const char *src; + struct monst *chain; + long *total_count; + long *total_size; +{ + char buf[BUFSZ]; + long count, size; + struct monst *mon; + + for (count = size = 0, mon = chain; mon; mon = mon->nmon) { + count++; + size += sizeof(struct monst) + mon->mxlth + mon->mnamelth; + } + *total_count += count; + *total_size += size; + Sprintf(buf, template, src, count, size); + putstr(win, 0, buf); +} + +/* + * Display memory usage of all monsters and objects on the level. + */ +static int +wiz_show_stats() +{ + char buf[BUFSZ]; + winid win; + long total_obj_size = 0, total_obj_count = 0; + long total_mon_size = 0, total_mon_count = 0; + + win = create_nhwindow(NHW_TEXT); + putstr(win, 0, "Current memory statistics:"); + putstr(win, 0, ""); + Sprintf(buf, "Objects, size %d", (int) sizeof(struct obj)); + putstr(win, 0, buf); + putstr(win, 0, ""); + putstr(win, 0, count_str); + + obj_chain(win, "invent", invent, &total_obj_count, &total_obj_size); + obj_chain(win, "fobj", fobj, &total_obj_count, &total_obj_size); + obj_chain(win, "buried", level.buriedobjlist, + &total_obj_count, &total_obj_size); + obj_chain(win, "migrating obj", migrating_objs, + &total_obj_count, &total_obj_size); + mon_invent_chain(win, "minvent", fmon, + &total_obj_count,&total_obj_size); + mon_invent_chain(win, "migrating minvent", migrating_mons, + &total_obj_count, &total_obj_size); + + contained(win, "contained", + &total_obj_count, &total_obj_size); + + putstr(win, 0, separator); + Sprintf(buf, template, "Total", total_obj_count, total_obj_size); + putstr(win, 0, buf); + + putstr(win, 0, ""); + putstr(win, 0, ""); + Sprintf(buf, "Monsters, size %d", (int) sizeof(struct monst)); + putstr(win, 0, buf); + putstr(win, 0, ""); + + mon_chain(win, "fmon", fmon, + &total_mon_count, &total_mon_size); + mon_chain(win, "migrating", migrating_mons, + &total_mon_count, &total_mon_size); + + putstr(win, 0, separator); + Sprintf(buf, template, "Total", total_mon_count, total_mon_size); + putstr(win, 0, buf); + +#if defined(__BORLANDC__) && !defined(_WIN32) + show_borlandc_stats(win); +#endif + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return 0; +} + +void +sanity_check() +{ + obj_sanity_check(); + timer_sanity_check(); +} + +#ifdef DEBUG_MIGRATING_MONS +static int +wiz_migrate_mons() +{ + int mcount = 0; + char inbuf[BUFSZ]; + struct permonst *ptr; + struct monst *mtmp; + d_level tolevel; + getlin("How many random monsters to migrate? [0]", inbuf); + if (*inbuf == '\033') return 0; + mcount = atoi(inbuf); + if (mcount < 0 || mcount > (COLNO * ROWNO) || Is_botlevel(&u.uz)) + return 0; + while (mcount > 0) { + if (Is_stronghold(&u.uz)) + assign_level(&tolevel, &valley_level); + else + get_level(&tolevel, depth(&u.uz) + 1); + ptr = rndmonst(); + mtmp = makemon(ptr, 0, 0, NO_MM_FLAGS); + if (mtmp) migrate_to_level(mtmp, ledger_no(&tolevel), + MIGR_RANDOM, (coord *)0); + mcount--; + } + return 0; +} +#endif + +#endif /* WIZARD */ + +#define unctrl(c) ((c) <= C('z') ? (0x60 | (c)) : (c)) +#define unmeta(c) (0x7f & (c)) + + +void +rhack(cmd) +register char *cmd; +{ + boolean do_walk, do_rush, prefix_seen, bad_command, + firsttime = (cmd == 0); + + iflags.menu_requested = FALSE; + if (firsttime) { + flags.nopick = 0; + cmd = parse(); + } + if (*cmd == '\033') { + flags.move = FALSE; + return; + } +#ifdef REDO + if (*cmd == DOAGAIN && !in_doagain && saveq[0]) { + in_doagain = TRUE; + stail = 0; + rhack((char *)0); /* read and execute command */ + in_doagain = FALSE; + return; + } + /* Special case of *cmd == ' ' handled better below */ + if(!*cmd || *cmd == (char)0377) +#else + if(!*cmd || *cmd == (char)0377 || (!flags.rest_on_space && *cmd == ' ')) +#endif + { + nhbell(); + flags.move = FALSE; + return; /* probably we just had an interrupt */ + } + if (iflags.num_pad && iflags.num_pad_mode == 1) { + /* This handles very old inconsistent DOS/Windows behaviour + * in a new way: earlier, the keyboard handler mapped these, + * which caused counts to be strange when entered from the + * number pad. Now do not map them until here. + */ + switch (*cmd) { + case '5': *cmd = 'g'; break; + case M('5'): *cmd = 'G'; break; + case M('0'): *cmd = 'I'; break; + } + } + /* handle most movement commands */ + do_walk = do_rush = prefix_seen = FALSE; + flags.travel = iflags.travel1 = 0; + switch (*cmd) { + case 'g': if (movecmd(cmd[1])) { + flags.run = 2; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '5': if (!iflags.num_pad) break; /* else FALLTHRU */ + case 'G': if (movecmd(lowc(cmd[1]))) { + flags.run = 3; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '-': if (!iflags.num_pad) break; /* else FALLTHRU */ + /* Effects of movement commands and invisible monsters: + * m: always move onto space (even if 'I' remembered) + * F: always attack space (even if 'I' not remembered) + * normal movement: attack if 'I', move otherwise + */ + case 'F': if (movecmd(cmd[1])) { + flags.forcefight = 1; + do_walk = TRUE; + } else + prefix_seen = TRUE; + break; + case 'm': if (movecmd(cmd[1]) || u.dz) { + flags.run = 0; + flags.nopick = 1; + if (!u.dz) do_walk = TRUE; + else cmd[0] = cmd[1]; /* "m<" or "m>" */ + } else + prefix_seen = TRUE; + break; + case 'M': if (movecmd(lowc(cmd[1]))) { + flags.run = 1; + flags.nopick = 1; + do_rush = TRUE; + } else + prefix_seen = TRUE; + break; + case '0': if (!iflags.num_pad) break; + (void)ddoinv(); /* a convenience borrowed from the PC */ + flags.move = FALSE; + multi = 0; + return; + case CMD_TRAVEL: + if (iflags.travelcmd) { + flags.travel = 1; + iflags.travel1 = 1; + flags.run = 8; + flags.nopick = 1; + do_rush = TRUE; + break; + } + /*FALLTHRU*/ + default: if (movecmd(*cmd)) { /* ordinary movement */ + flags.run = 0; /* only matters here if it was 8 */ + do_walk = TRUE; + } else if (movecmd(iflags.num_pad ? + unmeta(*cmd) : lowc(*cmd))) { + flags.run = 1; + do_rush = TRUE; + } else if (movecmd(unctrl(*cmd))) { + flags.run = 3; + do_rush = TRUE; + } + break; + } + + /* some special prefix handling */ + /* overload 'm' prefix for ',' to mean "request a menu" */ + if (prefix_seen && cmd[1] == ',') { + iflags.menu_requested = TRUE; + ++cmd; + } + + if (do_walk) { + if (multi) flags.mv = TRUE; + domove(); + flags.forcefight = 0; + return; + } else if (do_rush) { + if (firsttime) { + if (!multi) multi = max(COLNO,ROWNO); + u.last_str_turn = 0; + } + flags.mv = TRUE; + domove(); + return; + } else if (prefix_seen && cmd[1] == '\033') { /* */ + /* don't report "unknown command" for change of heart... */ + bad_command = FALSE; + } else if (*cmd == ' ' && !flags.rest_on_space) { + bad_command = TRUE; /* skip cmdlist[] loop */ + + /* handle all other commands */ + } else { + register const struct func_tab *tlist; + int res, NDECL((*func)); + + for (tlist = cmdlist; tlist->f_char; tlist++) { + if ((*cmd & 0xff) != (tlist->f_char & 0xff)) continue; + + if (u.uburied && !tlist->can_if_buried) { + You_cant("do that while you are buried!"); + res = 0; + } else { + /* we discard 'const' because some compilers seem to have + trouble with the pointer passed to set_occupation() */ + func = ((struct func_tab *)tlist)->f_funct; + if (tlist->f_text && !occupation && multi) + set_occupation(func, tlist->f_text, multi); + res = (*func)(); /* perform the command */ + } + if (!res) { + flags.move = FALSE; + multi = 0; + } + return; + } + /* if we reach here, cmd wasn't found in cmdlist[] */ + bad_command = TRUE; + } + + if (bad_command) { + char expcmd[10]; + register char *cp = expcmd; + + while (*cmd && (int)(cp - expcmd) < (int)(sizeof expcmd - 3)) { + if (*cmd >= 040 && *cmd < 0177) { + *cp++ = *cmd++; + } else if (*cmd & 0200) { + *cp++ = 'M'; + *cp++ = '-'; + *cp++ = *cmd++ &= ~0200; + } else { + *cp++ = '^'; + *cp++ = *cmd++ ^ 0100; + } + } + *cp = '\0'; + if (!prefix_seen || !iflags.cmdassist || + !help_dir(0, "Invalid direction key!")) + Norep("Unknown command '%s'.", expcmd); + } + /* didn't move */ + flags.move = FALSE; + multi = 0; + return; +} + +int +xytod(x, y) /* convert an x,y pair into a direction code */ +schar x, y; +{ + register int dd; + + for(dd = 0; dd < 8; dd++) + if(x == xdir[dd] && y == ydir[dd]) return dd; + + return -1; +} + +void +dtoxy(cc,dd) /* convert a direction code into an x,y pair */ +coord *cc; +register int dd; +{ + cc->x = xdir[dd]; + cc->y = ydir[dd]; + return; +} + +int +movecmd(sym) /* also sets u.dz, but returns false for <> */ +char sym; +{ + register const char *dp; + register const char *sdp; + if(iflags.num_pad) sdp = ndir; else sdp = sdir; /* DICE workaround */ + + u.dz = 0; + if(!(dp = index(sdp, sym))) return 0; + u.dx = xdir[dp-sdp]; + u.dy = ydir[dp-sdp]; + u.dz = zdir[dp-sdp]; + if (u.dx && u.dy && u.umonnum == PM_GRID_BUG) { + u.dx = u.dy = 0; + return 0; + } + return !u.dz; +} + +/* + * uses getdir() but unlike getdir() it specifically + * produces coordinates using the direction from getdir() + * and verifies that those coordinates are ok. + * + * If the call to getdir() returns 0, Never_mind is displayed. + * If the resulting coordinates are not okay, emsg is displayed. + * + * Returns non-zero if coordinates in cc are valid. + */ +int get_adjacent_loc(prompt,emsg,x,y,cc) +const char *prompt, *emsg; +xchar x,y; +coord *cc; +{ + xchar new_x, new_y; + if (!getdir(prompt)) { + pline(Never_mind); + return 0; + } + new_x = x + u.dx; + new_y = y + u.dy; + if (cc && isok(new_x,new_y)) { + cc->x = new_x; + cc->y = new_y; + } else { + if (emsg) pline(emsg); + return 0; + } + return 1; +} + +int +getdir(s) +const char *s; +{ + char dirsym; + +#ifdef REDO + if(in_doagain || *readchar_queue) + dirsym = readchar(); + else +#endif + dirsym = yn_function ((s && *s != '^') ? s : "In what direction?", + (char *)0, '\0'); +#ifdef REDO + savech(dirsym); +#endif + if(dirsym == '.' || dirsym == 's') + u.dx = u.dy = u.dz = 0; + else if(!movecmd(dirsym) && !u.dz) { + boolean did_help = FALSE; + if(!index(quitchars, dirsym)) { + if (iflags.cmdassist) { + did_help = help_dir((s && *s == '^') ? dirsym : 0, + "Invalid direction key!"); + } + if (!did_help) pline("What a strange direction!"); + } + return 0; + } + if(!u.dz && (Stunned || (Confusion && !rn2(5)))) confdir(); + return 1; +} + +STATIC_OVL boolean +help_dir(sym, msg) +char sym; +const char *msg; +{ + char ctrl; + winid win; + static const char wiz_only_list[] = "EFGIOVW"; + char buf[BUFSZ], buf2[BUFSZ], *expl; + + win = create_nhwindow(NHW_TEXT); + if (!win) return FALSE; + if (msg) { + Sprintf(buf, "cmdassist: %s", msg); + putstr(win, 0, buf); + putstr(win, 0, ""); + } + if (letter(sym)) { + sym = highc(sym); + ctrl = (sym - 'A') + 1; + if ((expl = dowhatdoes_core(ctrl, buf2)) + && (!index(wiz_only_list, sym) +#ifdef WIZARD + || wizard +#endif + )) { + Sprintf(buf, "Are you trying to use ^%c%s?", sym, + index(wiz_only_list, sym) ? "" : + " as specified in the Guidebook"); + putstr(win, 0, buf); + putstr(win, 0, ""); + putstr(win, 0, expl); + putstr(win, 0, ""); + putstr(win, 0, "To use that command, you press"); + Sprintf(buf, + "the key, and the <%c> key at the same time.", sym); + putstr(win, 0, buf); + putstr(win, 0, ""); + } + } + if (iflags.num_pad && u.umonnum == PM_GRID_BUG) { + putstr(win, 0, "Valid direction keys in your current form (with number_pad on) are:"); + putstr(win, 0, " 8 "); + putstr(win, 0, " | "); + putstr(win, 0, " 4- . -6"); + putstr(win, 0, " | "); + putstr(win, 0, " 2 "); + } else if (u.umonnum == PM_GRID_BUG) { + putstr(win, 0, "Valid direction keys in your current form are:"); + putstr(win, 0, " k "); + putstr(win, 0, " | "); + putstr(win, 0, " h- . -l"); + putstr(win, 0, " | "); + putstr(win, 0, " j "); + } else if (iflags.num_pad) { + putstr(win, 0, "Valid direction keys (with number_pad on) are:"); + putstr(win, 0, " 7 8 9"); + putstr(win, 0, " \\ | / "); + putstr(win, 0, " 4- . -6"); + putstr(win, 0, " / | \\ "); + putstr(win, 0, " 1 2 3"); + } else { + putstr(win, 0, "Valid direction keys are:"); + putstr(win, 0, " y k u"); + putstr(win, 0, " \\ | / "); + putstr(win, 0, " h- . -l"); + putstr(win, 0, " / | \\ "); + putstr(win, 0, " b j n"); + }; + putstr(win, 0, ""); + putstr(win, 0, " < up"); + putstr(win, 0, " > down"); + putstr(win, 0, " . direct at yourself"); + putstr(win, 0, ""); + putstr(win, 0, "(Suppress this message with !cmdassist in config file.)"); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + return TRUE; +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +confdir() +{ + register int x = (u.umonnum == PM_GRID_BUG) ? 2*rn2(4) : rn2(8); + u.dx = xdir[x]; + u.dy = ydir[x]; + return; +} + +#endif /* OVLB */ +#ifdef OVL0 + +int +isok(x,y) +register int x, y; +{ + /* x corresponds to curx, so x==1 is the first column. Ach. %% */ + return x >= 1 && x <= COLNO-1 && y >= 0 && y <= ROWNO-1; +} + +static NEARDATA int last_multi; + +/* + * convert a MAP window position into a movecmd + */ +const char * +click_to_cmd(x, y, mod) + int x, y, mod; +{ + int dir; + static char cmd[4]; + cmd[1]=0; + + x -= u.ux; + y -= u.uy; + + if (iflags.travelcmd) { + if (abs(x) <= 1 && abs(y) <= 1 ) { + x = sgn(x), y = sgn(y); + } else { + u.tx = u.ux+x; + u.ty = u.uy+y; + cmd[0] = CMD_TRAVEL; + return cmd; + } + + if(x == 0 && y == 0) { + /* here */ + if(IS_FOUNTAIN(levl[u.ux][u.uy].typ) || IS_SINK(levl[u.ux][u.uy].typ)) { + cmd[0]=mod == CLICK_1 ? 'q' : M('d'); + return cmd; + } else if(IS_THRONE(levl[u.ux][u.uy].typ)) { + cmd[0]=M('s'); + return cmd; + } else if((u.ux == xupstair && u.uy == yupstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && sstairs.up) + || (u.ux == xupladder && u.uy == yupladder)) { + return "<"; + } else if((u.ux == xdnstair && u.uy == ydnstair) + || (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up) + || (u.ux == xdnladder && u.uy == ydnladder)) { + return ">"; + } else if(OBJ_AT(u.ux, u.uy)) { + cmd[0] = Is_container(level.objects[u.ux][u.uy]) ? M('l') : ','; + return cmd; + } else { + return "."; /* just rest */ + } + } + + /* directional commands */ + + dir = xytod(x, y); + + if (!m_at(u.ux+x, u.uy+y) && !test_move(u.ux, u.uy, x, y, TEST_MOVE)) { + cmd[1] = (iflags.num_pad ? ndir[dir] : sdir[dir]); + cmd[2] = 0; + if (IS_DOOR(levl[u.ux+x][u.uy+y].typ)) { + /* slight assistance to the player: choose kick/open for them */ + if (levl[u.ux+x][u.uy+y].doormask & D_LOCKED) { + cmd[0] = C('d'); + return cmd; + } + if (levl[u.ux+x][u.uy+y].doormask & D_CLOSED) { + cmd[0] = 'o'; + return cmd; + } + } + if (levl[u.ux+x][u.uy+y].typ <= SCORR) { + cmd[0] = 's'; + cmd[1] = 0; + return cmd; + } + } + } else { + /* convert without using floating point, allowing sloppy clicking */ + if(x > 2*abs(y)) + x = 1, y = 0; + else if(y > 2*abs(x)) + x = 0, y = 1; + else if(x < -2*abs(y)) + x = -1, y = 0; + else if(y < -2*abs(x)) + x = 0, y = -1; + else + x = sgn(x), y = sgn(y); + + if(x == 0 && y == 0) /* map click on player to "rest" command */ + return "."; + + dir = xytod(x, y); + } + + /* move, attack, etc. */ + cmd[1] = 0; + if(mod == CLICK_1) { + cmd[0] = (iflags.num_pad ? ndir[dir] : sdir[dir]); + } else { + cmd[0] = (iflags.num_pad ? M(ndir[dir]) : + (sdir[dir] - 'a' + 'A')); /* run command */ + } + + return cmd; +} + +STATIC_OVL char * +parse() +{ +#ifdef LINT /* static char in_line[COLNO]; */ + char in_line[COLNO]; +#else + static char in_line[COLNO]; +#endif + register int foo; + boolean prezero = FALSE; + + multi = 0; + flags.move = 1; + flush_screen(1); /* Flush screen buffer. Put the cursor on the hero. */ + + if (!iflags.num_pad || (foo = readchar()) == 'n') + for (;;) { + foo = readchar(); + if (foo >= '0' && foo <= '9') { + multi = 10 * multi + foo - '0'; + if (multi < 0 || multi >= LARGEST_INT) multi = LARGEST_INT; + if (multi > 9) { + clear_nhwindow(WIN_MESSAGE); + Sprintf(in_line, "Count: %d", multi); + pline(in_line); + mark_synch(); + } + last_multi = multi; + if (!multi && foo == '0') prezero = TRUE; + } else break; /* not a digit */ + } + + if (foo == '\033') { /* esc cancels count (TH) */ + clear_nhwindow(WIN_MESSAGE); + multi = last_multi = 0; +# ifdef REDO + } else if (foo == DOAGAIN || in_doagain) { + multi = last_multi; + } else { + last_multi = multi; + savech(0); /* reset input queue */ + savech((char)foo); +# endif + } + + if (multi) { + multi--; + save_cm = in_line; + } else { + save_cm = (char *)0; + } + in_line[0] = foo; + in_line[1] = '\0'; + if (foo == 'g' || foo == 'G' || foo == 'm' || foo == 'M' || + foo == 'F' || (iflags.num_pad && (foo == '5' || foo == '-'))) { + foo = readchar(); +#ifdef REDO + savech((char)foo); +#endif + in_line[1] = foo; + in_line[2] = 0; + } + clear_nhwindow(WIN_MESSAGE); + if (prezero) in_line[0] = '\033'; + return(in_line); +} + +#endif /* OVL0 */ +#ifdef OVLB + +#ifdef UNIX +static +void +end_of_input() +{ +#ifndef NOSAVEONHANGUP + if (!program_state.done_hup++ && program_state.something_worth_saving) + (void) dosave0(); +#endif + exit_nhwindows((char *)0); + clearlocks(); + terminate(EXIT_SUCCESS); +} +#endif + +#endif /* OVLB */ +#ifdef OVL0 + +char +readchar() +{ + register int sym; + int x = u.ux, y = u.uy, mod = 0; + + if ( *readchar_queue ) + sym = *readchar_queue++; + else +#ifdef REDO + sym = in_doagain ? Getchar() : nh_poskey(&x, &y, &mod); +#else + sym = Getchar(); +#endif + +#ifdef UNIX +# ifdef NR_OF_EOFS + if (sym == EOF) { + register int cnt = NR_OF_EOFS; + /* + * Some SYSV systems seem to return EOFs for various reasons + * (?like when one hits break or for interrupted systemcalls?), + * and we must see several before we quit. + */ + do { + clearerr(stdin); /* omit if clearerr is undefined */ + sym = Getchar(); + } while (--cnt && sym == EOF); + } +# endif /* NR_OF_EOFS */ + if (sym == EOF) + end_of_input(); +#endif /* UNIX */ + + if(sym == 0) { + /* click event */ + readchar_queue = click_to_cmd(x, y, mod); + sym = *readchar_queue++; + } + return((char) sym); +} + +STATIC_PTR int +dotravel() +{ + /* Keyboard travel command */ + static char cmd[2]; + coord cc; + + if (!iflags.travelcmd) return 0; + cmd[1]=0; + cc.x = iflags.travelcc.x; + cc.y = iflags.travelcc.y; + if (cc.x == -1 && cc.y == -1) { + /* No cached destination, start attempt from current position */ + cc.x = u.ux; + cc.y = u.uy; + } + pline("Where do you want to travel to?"); + if (getpos(&cc, TRUE, "the desired destination") < 0) { + /* user pressed ESC */ + return 0; + } + iflags.travelcc.x = u.tx = cc.x; + iflags.travelcc.y = u.ty = cc.y; + cmd[0] = CMD_TRAVEL; + readchar_queue = cmd; + return 0; +} + +#ifdef PORT_DEBUG +# ifdef WIN32CON +extern void NDECL(win32con_debug_keystrokes); +extern void NDECL(win32con_handler_info); +# endif + +int +wiz_port_debug() +{ + int n, k; + winid win; + anything any; + int item = 'a'; + int num_menu_selections; + struct menu_selection_struct { + char *menutext; + void NDECL((*fn)); + } menu_selections[] = { +#ifdef WIN32CON + {"test win32 keystrokes", win32con_debug_keystrokes}, + {"show keystroke handler information", win32con_handler_info}, +#endif + {(char *)0, (void NDECL((*)))0} /* array terminator */ + }; + + num_menu_selections = SIZE(menu_selections) - 1; + if (num_menu_selections > 0) { + menu_item *pick_list; + win = create_nhwindow(NHW_MENU); + start_menu(win); + for (k=0; k < num_menu_selections; ++k) { + any.a_int = k+1; + add_menu(win, NO_GLYPH, &any, item++, 0, ATR_NONE, + menu_selections[k].menutext, MENU_UNSELECTED); + } + end_menu(win, "Which port debugging feature?"); + n = select_menu(win, PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n > 0) { + n = pick_list[0].item.a_int - 1; + free((genericptr_t) pick_list); + /* execute the function */ + (*menu_selections[n].fn)(); + } + } else + pline("No port-specific debug capability defined."); + return 0; +} +# endif /*PORT_DEBUG*/ + +#endif /* OVL0 */ +#ifdef OVLB +/* + * Parameter validator for generic yes/no function to prevent + * the core from sending too long a prompt string to the + * window port causing a buffer overflow there. + */ +char +yn_function(query,resp, def) +const char *query,*resp; +char def; +{ + char qbuf[QBUFSZ]; + unsigned truncspot, reduction = sizeof(" [N] ?") + 1; + + if (resp) reduction += strlen(resp) + sizeof(" () "); + if (strlen(query) < (QBUFSZ - reduction)) + return (*windowprocs.win_yn_function)(query, resp, def); + paniclog("Query truncated: ", query); + reduction += sizeof("..."); + truncspot = QBUFSZ - reduction; + (void) strncpy(qbuf, query, (int)truncspot); + qbuf[truncspot] = '\0'; + Strcat(qbuf,"..."); + return (*windowprocs.win_yn_function)(qbuf, resp, def); +} +#endif + +/*cmd.c*/ diff --git a/src/dbridge.c b/src/dbridge.c new file mode 100644 index 0000000..df04bc8 --- /dev/null +++ b/src/dbridge.c @@ -0,0 +1,942 @@ +/* SCCS Id: @(#)dbridge.c 3.4 2003/02/08 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the drawbridge manipulation (create, open, close, + * destroy). + * + * Added comprehensive monster-handling, and the "entity" structure to + * deal with players as well. - 11/89 + */ + +#include "hack.h" + +#ifdef OVLB +STATIC_DCL void FDECL(get_wall_for_db, (int *, int *)); +STATIC_DCL struct entity *FDECL(e_at, (int, int)); +STATIC_DCL void FDECL(m_to_e, (struct monst *, int, int, struct entity *)); +STATIC_DCL void FDECL(u_to_e, (struct entity *)); +STATIC_DCL void FDECL(set_entity, (int, int, struct entity *)); +STATIC_DCL const char *FDECL(e_nam, (struct entity *)); +#ifdef D_DEBUG +static const char *FDECL(Enam, (struct entity *)); /* unused */ +#endif +STATIC_DCL const char *FDECL(E_phrase, (struct entity *, const char *)); +STATIC_DCL boolean FDECL(e_survives_at, (struct entity *, int, int)); +STATIC_DCL void FDECL(e_died, (struct entity *, int, int)); +STATIC_DCL boolean FDECL(automiss, (struct entity *)); +STATIC_DCL boolean FDECL(e_missed, (struct entity *, BOOLEAN_P)); +STATIC_DCL boolean FDECL(e_jumps, (struct entity *)); +STATIC_DCL void FDECL(do_entity, (struct entity *)); +#endif /* OVLB */ + +#ifdef OVL0 + +boolean +is_pool(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == POOL || ltyp == MOAT || ltyp == WATER) return TRUE; + if (ltyp == DRAWBRIDGE_UP && + (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT) return TRUE; + return FALSE; +} + +boolean +is_lava(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == LAVAPOOL + || (ltyp == DRAWBRIDGE_UP + && (levl[x][y].drawbridgemask & DB_UNDER) == DB_LAVA)) return TRUE; + return FALSE; +} + +boolean +is_ice(x,y) +int x,y; +{ + schar ltyp; + + if (!isok(x,y)) return FALSE; + ltyp = levl[x][y].typ; + if (ltyp == ICE + || (ltyp == DRAWBRIDGE_UP + && (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE)) return TRUE; + return FALSE; +} + +#endif /* OVL0 */ + +#ifdef OVL1 + +/* + * We want to know whether a wall (or a door) is the portcullis (passageway) + * of an eventual drawbridge. + * + * Return value: the direction of the drawbridge. + */ + +int +is_drawbridge_wall(x,y) +int x,y; +{ + struct rm *lev; + + lev = &levl[x][y]; + if (lev->typ != DOOR && lev->typ != DBWALL) + return (-1); + + if (IS_DRAWBRIDGE(levl[x+1][y].typ) && + (levl[x+1][y].drawbridgemask & DB_DIR) == DB_WEST) + return (DB_WEST); + if (IS_DRAWBRIDGE(levl[x-1][y].typ) && + (levl[x-1][y].drawbridgemask & DB_DIR) == DB_EAST) + return (DB_EAST); + if (IS_DRAWBRIDGE(levl[x][y-1].typ) && + (levl[x][y-1].drawbridgemask & DB_DIR) == DB_SOUTH) + return (DB_SOUTH); + if (IS_DRAWBRIDGE(levl[x][y+1].typ) && + (levl[x][y+1].drawbridgemask & DB_DIR) == DB_NORTH) + return (DB_NORTH); + + return (-1); +} + +/* + * Use is_db_wall where you want to verify that a + * drawbridge "wall" is UP in the location x, y + * (instead of UP or DOWN, as with is_drawbridge_wall). + */ +boolean +is_db_wall(x,y) +int x,y; +{ + return((boolean)( levl[x][y].typ == DBWALL )); +} + + +/* + * Return true with x,y pointing to the drawbridge if x,y initially indicate + * a drawbridge or drawbridge wall. + */ +boolean +find_drawbridge(x,y) +int *x,*y; +{ + int dir; + + if (IS_DRAWBRIDGE(levl[*x][*y].typ)) + return TRUE; + dir = is_drawbridge_wall(*x,*y); + if (dir >= 0) { + switch(dir) { + case DB_NORTH: (*y)++; break; + case DB_SOUTH: (*y)--; break; + case DB_EAST: (*x)--; break; + case DB_WEST: (*x)++; break; + } + return TRUE; + } + return FALSE; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* + * Find the drawbridge wall associated with a drawbridge. + */ +STATIC_OVL void +get_wall_for_db(x,y) +int *x,*y; +{ + switch (levl[*x][*y].drawbridgemask & DB_DIR) { + case DB_NORTH: (*y)--; break; + case DB_SOUTH: (*y)++; break; + case DB_EAST: (*x)++; break; + case DB_WEST: (*x)--; break; + } +} + +/* + * Creation of a drawbridge at pos x,y. + * dir is the direction. + * flag must be put to TRUE if we want the drawbridge to be opened. + */ + +boolean +create_drawbridge(x,y,dir,flag) +int x,y,dir; +boolean flag; +{ + int x2,y2; + boolean horiz; + boolean lava = levl[x][y].typ == LAVAPOOL; /* assume initialized map */ + + x2 = x; y2 = y; + switch(dir) { + case DB_NORTH: + horiz = TRUE; + y2--; + break; + case DB_SOUTH: + horiz = TRUE; + y2++; + break; + case DB_EAST: + horiz = FALSE; + x2++; + break; + default: + impossible("bad direction in create_drawbridge"); + /* fall through */ + case DB_WEST: + horiz = FALSE; + x2--; + break; + } + if (!IS_WALL(levl[x2][y2].typ)) + return(FALSE); + if (flag) { /* We want the bridge open */ + levl[x][y].typ = DRAWBRIDGE_DOWN; + levl[x2][y2].typ = DOOR; + levl[x2][y2].doormask = D_NODOOR; + } else { + levl[x][y].typ = DRAWBRIDGE_UP; + levl[x2][y2].typ = DBWALL; + /* Drawbridges are non-diggable. */ + levl[x2][y2].wall_info = W_NONDIGGABLE; + } + levl[x][y].horizontal = !horiz; + levl[x2][y2].horizontal = horiz; + levl[x][y].drawbridgemask = dir; + if(lava) levl[x][y].drawbridgemask |= DB_LAVA; + return(TRUE); +} + +struct entity { + struct monst *emon; /* youmonst for the player */ + struct permonst *edata; /* must be non-zero for record to be valid */ + int ex, ey; +}; + +#define ENTITIES 2 + +static NEARDATA struct entity occupants[ENTITIES]; + +STATIC_OVL +struct entity * +e_at(x, y) +int x, y; +{ + int entitycnt; + + for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) + if ((occupants[entitycnt].edata) && + (occupants[entitycnt].ex == x) && + (occupants[entitycnt].ey == y)) + break; +#ifdef D_DEBUG + pline("entitycnt = %d", entitycnt); + wait_synch(); +#endif + return((entitycnt == ENTITIES)? + (struct entity *)0 : &(occupants[entitycnt])); +} + +STATIC_OVL void +m_to_e(mtmp, x, y, etmp) +struct monst *mtmp; +int x, y; +struct entity *etmp; +{ + etmp->emon = mtmp; + if (mtmp) { + etmp->ex = x; + etmp->ey = y; + if (mtmp->wormno && (x != mtmp->mx || y != mtmp->my)) + etmp->edata = &mons[PM_LONG_WORM_TAIL]; + else + etmp->edata = mtmp->data; + } else + etmp->edata = (struct permonst *)0; +} + +STATIC_OVL void +u_to_e(etmp) +struct entity *etmp; +{ + etmp->emon = &youmonst; + etmp->ex = u.ux; + etmp->ey = u.uy; + etmp->edata = youmonst.data; +} + +STATIC_OVL void +set_entity(x, y, etmp) +int x, y; +struct entity *etmp; +{ + if ((x == u.ux) && (y == u.uy)) + u_to_e(etmp); + else if (MON_AT(x, y)) + m_to_e(m_at(x, y), x, y, etmp); + else + etmp->edata = (struct permonst *)0; +} + +#define is_u(etmp) (etmp->emon == &youmonst) +#define e_canseemon(etmp) (is_u(etmp) ? (boolean)TRUE : canseemon(etmp->emon)) + +/* + * e_strg is a utility routine which is not actually in use anywhere, since + * the specialized routines below suffice for all current purposes. + */ + +/* #define e_strg(etmp, func) (is_u(etmp)? (char *)0 : func(etmp->emon)) */ + +STATIC_OVL const char * +e_nam(etmp) +struct entity *etmp; +{ + return(is_u(etmp)? "you" : mon_nam(etmp->emon)); +} + +#ifdef D_DEBUG +/* + * Enam is another unused utility routine: E_phrase is preferable. + */ + +static const char * +Enam(etmp) +struct entity *etmp; +{ + return(is_u(etmp)? "You" : Monnam(etmp->emon)); +} +#endif /* D_DEBUG */ + +/* + * Generates capitalized entity name, makes 2nd -> 3rd person conversion on + * verb, where necessary. + */ + +STATIC_OVL const char * +E_phrase(etmp, verb) +struct entity *etmp; +const char *verb; +{ + static char wholebuf[80]; + + Strcpy(wholebuf, is_u(etmp) ? "You" : Monnam(etmp->emon)); + if (!*verb) return(wholebuf); + Strcat(wholebuf, " "); + if (is_u(etmp)) + Strcat(wholebuf, verb); + else + Strcat(wholebuf, vtense((char *)0, verb)); + return(wholebuf); +} + +/* + * Simple-minded "can it be here?" routine + */ + +STATIC_OVL boolean +e_survives_at(etmp, x, y) +struct entity *etmp; +int x, y; +{ + if (noncorporeal(etmp->edata)) + return(TRUE); + if (is_pool(x, y)) + return (boolean)((is_u(etmp) && + (Wwalking || Amphibious || Swimming || + Flying || Levitation)) || + is_swimmer(etmp->edata) || is_flyer(etmp->edata) || + is_floater(etmp->edata)); + /* must force call to lava_effects in e_died if is_u */ + if (is_lava(x, y)) + return (boolean)((is_u(etmp) && (Levitation || Flying)) || + likes_lava(etmp->edata) || is_flyer(etmp->edata)); + if (is_db_wall(x, y)) + return((boolean)(is_u(etmp) ? Passes_walls : + passes_walls(etmp->edata))); + return(TRUE); +} + +STATIC_OVL void +e_died(etmp, dest, how) +struct entity *etmp; +int dest, how; +{ + if (is_u(etmp)) { + if (how == DROWNING) { + killer = 0; /* drown() sets its own killer */ + (void) drown(); + } else if (how == BURNING) { + killer = 0; /* lava_effects() sets its own killer */ + (void) lava_effects(); + } else { + coord xy; + + /* use more specific killer if specified */ + if (!killer) { + killer_format = KILLED_BY_AN; + killer = "falling drawbridge"; + } + done(how); + /* So, you didn't die */ + if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { + if (enexto(&xy, etmp->ex, etmp->ey, etmp->edata)) { + pline("A %s force teleports you away...", + Hallucination ? "normal" : "strange"); + teleds(xy.x, xy.y, FALSE); + } + /* otherwise on top of the drawbridge is the + * only viable spot in the dungeon, so stay there + */ + } + } + /* we might have crawled out of the moat to survive */ + etmp->ex = u.ux, etmp->ey = u.uy; + } else { + int entitycnt; + + killer = 0; + /* fake "digested to death" damage-type suppresses corpse */ +#define mk_message(dest) ((dest & 1) ? "" : (char *)0) +#define mk_corpse(dest) ((dest & 2) ? AD_DGST : AD_PHYS) + /* if monsters are moving, one of them caused the destruction */ + if (flags.mon_moving) + monkilled(etmp->emon, mk_message(dest), mk_corpse(dest)); + else /* you caused it */ + xkilled(etmp->emon, dest); + etmp->edata = (struct permonst *)0; + + /* dead long worm handling */ + for (entitycnt = 0; entitycnt < ENTITIES; entitycnt++) { + if (etmp != &(occupants[entitycnt]) && + etmp->emon == occupants[entitycnt].emon) + occupants[entitycnt].edata = (struct permonst *)0; + } +#undef mk_message +#undef mk_corpse + } +} + + +/* + * These are never directly affected by a bridge or portcullis. + */ + +STATIC_OVL boolean +automiss(etmp) +struct entity *etmp; +{ + return (boolean)((is_u(etmp) ? Passes_walls : + passes_walls(etmp->edata)) || noncorporeal(etmp->edata)); +} + +/* + * Does falling drawbridge or portcullis miss etmp? + */ + +STATIC_OVL boolean +e_missed(etmp, chunks) +struct entity *etmp; +boolean chunks; +{ + int misses; + +#ifdef D_DEBUG + if (chunks) + pline("Do chunks miss?"); +#endif + if (automiss(etmp)) + return(TRUE); + + if (is_flyer(etmp->edata) && + (is_u(etmp)? !Sleeping : + (etmp->emon->mcanmove && !etmp->emon->msleeping))) + /* flying requires mobility */ + misses = 5; /* out of 8 */ + else if (is_floater(etmp->edata) || + (is_u(etmp) && Levitation)) /* doesn't require mobility */ + misses = 3; + else if (chunks && is_pool(etmp->ex, etmp->ey)) + misses = 2; /* sitting ducks */ + else + misses = 0; + + if (is_db_wall(etmp->ex, etmp->ey)) + misses -= 3; /* less airspace */ + +#ifdef D_DEBUG + pline("Miss chance = %d (out of 8)", misses); +#endif + + return((boolean)((misses >= rnd(8))? TRUE : FALSE)); +} + +/* + * Can etmp jump from death? + */ + +STATIC_OVL boolean +e_jumps(etmp) +struct entity *etmp; +{ + int tmp = 4; /* out of 10 */ + + if (is_u(etmp)? (Sleeping || Fumbling) : + (!etmp->emon->mcanmove || etmp->emon->msleeping || + !etmp->edata->mmove || etmp->emon->wormno)) + return(FALSE); + + if (is_u(etmp)? Confusion : etmp->emon->mconf) + tmp -= 2; + + if (is_u(etmp)? Stunned : etmp->emon->mstun) + tmp -= 3; + + if (is_db_wall(etmp->ex, etmp->ey)) + tmp -= 2; /* less room to maneuver */ + +#ifdef D_DEBUG + pline("%s to jump (%d chances in 10)", E_phrase(etmp, "try"), tmp); +#endif + return((boolean)((tmp >= rnd(10))? TRUE : FALSE)); +} + +STATIC_OVL void +do_entity(etmp) +struct entity *etmp; +{ + int newx, newy, at_portcullis, oldx, oldy; + boolean must_jump = FALSE, relocates = FALSE, e_inview; + struct rm *crm; + + if (!etmp->edata) + return; + + e_inview = e_canseemon(etmp); + oldx = etmp->ex; + oldy = etmp->ey; + at_portcullis = is_db_wall(oldx, oldy); + crm = &levl[oldx][oldy]; + + if (automiss(etmp) && e_survives_at(etmp, oldx, oldy)) { + if (e_inview && (at_portcullis || IS_DRAWBRIDGE(crm->typ))) + pline_The("%s passes through %s!", + at_portcullis ? "portcullis" : "drawbridge", + e_nam(etmp)); + if (is_u(etmp)) spoteffects(FALSE); + return; + } + if (e_missed(etmp, FALSE)) { + if (at_portcullis) + pline_The("portcullis misses %s!", + e_nam(etmp)); +#ifdef D_DEBUG + else + pline_The("drawbridge misses %s!", + e_nam(etmp)); +#endif + if (e_survives_at(etmp, oldx, oldy)) + return; + else { +#ifdef D_DEBUG + pline("Mon can't survive here"); +#endif + if (at_portcullis) + must_jump = TRUE; + else + relocates = TRUE; /* just ride drawbridge in */ + } + } else { + if (crm->typ == DRAWBRIDGE_DOWN) { + pline("%s crushed underneath the drawbridge.", + E_phrase(etmp, "are")); /* no jump */ + e_died(etmp, e_inview? 3 : 2, CRUSHING);/* no corpse */ + return; /* Note: Beyond this point, we know we're */ + } /* not at an opened drawbridge, since all */ + must_jump = TRUE; /* *missable* creatures survive on the */ + } /* square, and all the unmissed ones die. */ + if (must_jump) { + if (at_portcullis) { + if (e_jumps(etmp)) { + relocates = TRUE; +#ifdef D_DEBUG + pline("Jump succeeds!"); +#endif + } else { + if (e_inview) + pline("%s crushed by the falling portcullis!", + E_phrase(etmp, "are")); + else if (flags.soundok) + You_hear("a crushing sound."); + e_died(etmp, e_inview? 3 : 2, CRUSHING); + /* no corpse */ + return; + } + } else { /* tries to jump off bridge to original square */ + relocates = !e_jumps(etmp); +#ifdef D_DEBUG + pline("Jump %s!", (relocates)? "fails" : "succeeds"); +#endif + } + } + +/* + * Here's where we try to do relocation. Assumes that etmp is not arriving + * at the portcullis square while the drawbridge is falling, since this square + * would be inaccessible (i.e. etmp started on drawbridge square) or + * unnecessary (i.e. etmp started here) in such a situation. + */ +#ifdef D_DEBUG + pline("Doing relocation."); +#endif + newx = oldx; + newy = oldy; + (void)find_drawbridge(&newx, &newy); + if ((newx == oldx) && (newy == oldy)) + get_wall_for_db(&newx, &newy); +#ifdef D_DEBUG + pline("Checking new square for occupancy."); +#endif + if (relocates && (e_at(newx, newy))) { + +/* + * Standoff problem: one or both entities must die, and/or both switch + * places. Avoid infinite recursion by checking first whether the other + * entity is staying put. Clean up if we happen to move/die in recursion. + */ + struct entity *other; + + other = e_at(newx, newy); +#ifdef D_DEBUG + pline("New square is occupied by %s", e_nam(other)); +#endif + if (e_survives_at(other, newx, newy) && automiss(other)) { + relocates = FALSE; /* "other" won't budge */ +#ifdef D_DEBUG + pline("%s suicide.", E_phrase(etmp, "commit")); +#endif + } else { + +#ifdef D_DEBUG + pline("Handling %s", e_nam(other)); +#endif + while ((e_at(newx, newy) != 0) && + (e_at(newx, newy) != etmp)) + do_entity(other); +#ifdef D_DEBUG + pline("Checking existence of %s", e_nam(etmp)); + wait_synch(); +#endif + if (e_at(oldx, oldy) != etmp) { +#ifdef D_DEBUG + pline("%s moved or died in recursion somewhere", + E_phrase(etmp, "have")); + wait_synch(); +#endif + return; + } + } + } + if (relocates && !e_at(newx, newy)) {/* if e_at() entity = worm tail */ +#ifdef D_DEBUG + pline("Moving %s", e_nam(etmp)); +#endif + if (!is_u(etmp)) { + remove_monster(etmp->ex, etmp->ey); + place_monster(etmp->emon, newx, newy); + update_monster_region(etmp->emon); + } else { + u.ux = newx; + u.uy = newy; + } + etmp->ex = newx; + etmp->ey = newy; + e_inview = e_canseemon(etmp); + } +#ifdef D_DEBUG + pline("Final disposition of %s", e_nam(etmp)); + wait_synch(); +#endif + if (is_db_wall(etmp->ex, etmp->ey)) { +#ifdef D_DEBUG + pline("%s in portcullis chamber", E_phrase(etmp, "are")); + wait_synch(); +#endif + if (e_inview) { + if (is_u(etmp)) { + You("tumble towards the closed portcullis!"); + if (automiss(etmp)) + You("pass through it!"); + else + pline_The("drawbridge closes in..."); + } else + pline("%s behind the drawbridge.", + E_phrase(etmp, "disappear")); + } + if (!e_survives_at(etmp, etmp->ex, etmp->ey)) { + killer_format = KILLED_BY_AN; + killer = "closing drawbridge"; + e_died(etmp, 0, CRUSHING); /* no message */ + return; + } +#ifdef D_DEBUG + pline("%s in here", E_phrase(etmp, "survive")); +#endif + } else { +#ifdef D_DEBUG + pline("%s on drawbridge square", E_phrase(etmp, "are")); +#endif + if (is_pool(etmp->ex, etmp->ey) && !e_inview) + if (flags.soundok) + You_hear("a splash."); + if (e_survives_at(etmp, etmp->ex, etmp->ey)) { + if (e_inview && !is_flyer(etmp->edata) && + !is_floater(etmp->edata)) + pline("%s from the bridge.", + E_phrase(etmp, "fall")); + return; + } +#ifdef D_DEBUG + pline("%s cannot survive on the drawbridge square",Enam(etmp)); +#endif + if (is_pool(etmp->ex, etmp->ey) || is_lava(etmp->ex, etmp->ey)) + if (e_inview && !is_u(etmp)) { + /* drown() will supply msgs if nec. */ + boolean lava = is_lava(etmp->ex, etmp->ey); + + if (Hallucination) + pline("%s the %s and disappears.", + E_phrase(etmp, "drink"), + lava ? "lava" : "moat"); + else + pline("%s into the %s.", + E_phrase(etmp, "fall"), + lava ? "lava" : "moat"); + } + killer_format = NO_KILLER_PREFIX; + killer = "fell from a drawbridge"; + e_died(etmp, e_inview ? 3 : 2, /* CRUSHING is arbitrary */ + (is_pool(etmp->ex, etmp->ey)) ? DROWNING : + (is_lava(etmp->ex, etmp->ey)) ? BURNING : + CRUSHING); /*no corpse*/ + return; + } +} + +/* + * Close the drawbridge located at x,y + */ + +void +close_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + + lev1 = &levl[x][y]; + if (lev1->typ != DRAWBRIDGE_DOWN) return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + if (cansee(x,y) || cansee(x2,y2)) + You("see a drawbridge %s up!", + (((u.ux == x || u.uy == y) && !Underwater) || + distu(x2,y2) < distu(x,y)) ? "coming" : "going"); + lev1->typ = DRAWBRIDGE_UP; + lev2 = &levl[x2][y2]; + lev2->typ = DBWALL; + switch (lev1->drawbridgemask & DB_DIR) { + case DB_NORTH: + case DB_SOUTH: + lev2->horizontal = TRUE; + break; + case DB_WEST: + case DB_EAST: + lev2->horizontal = FALSE; + break; + } + lev2->wall_info = W_NONDIGGABLE; + set_entity(x, y, &(occupants[0])); + set_entity(x2, y2, &(occupants[1])); + do_entity(&(occupants[0])); /* Do set_entity after first */ + set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tail */ + do_entity(&(occupants[1])); + if(OBJ_AT(x,y) && flags.soundok) + You_hear("smashing and crushing."); + (void) revive_nasty(x,y,(char *)0); + (void) revive_nasty(x2,y2,(char *)0); + delallobj(x, y); + delallobj(x2, y2); + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x, y); + newsym(x2, y2); + block_point(x2,y2); /* vision */ +} + +/* + * Open the drawbridge located at x,y + */ + +void +open_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + + lev1 = &levl[x][y]; + if (lev1->typ != DRAWBRIDGE_UP) return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + if (cansee(x,y) || cansee(x2,y2)) + You("see a drawbridge %s down!", + (distu(x2,y2) < distu(x,y)) ? "going" : "coming"); + lev1->typ = DRAWBRIDGE_DOWN; + lev2 = &levl[x2][y2]; + lev2->typ = DOOR; + lev2->doormask = D_NODOOR; + set_entity(x, y, &(occupants[0])); + set_entity(x2, y2, &(occupants[1])); + do_entity(&(occupants[0])); /* do set_entity after first */ + set_entity(x2, y2, &(occupants[1])); /* do_entity for worm tails */ + do_entity(&(occupants[1])); + (void) revive_nasty(x,y,(char *)0); + delallobj(x, y); + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x, y); + newsym(x2, y2); + unblock_point(x2,y2); /* vision */ + if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE; +} + +/* + * Let's destroy the drawbridge located at x,y + */ + +void +destroy_drawbridge(x,y) +int x,y; +{ + register struct rm *lev1, *lev2; + struct trap *t; + int x2, y2; + boolean e_inview; + struct entity *etmp1 = &(occupants[0]), *etmp2 = &(occupants[1]); + + lev1 = &levl[x][y]; + if (!IS_DRAWBRIDGE(lev1->typ)) + return; + x2 = x; y2 = y; + get_wall_for_db(&x2,&y2); + lev2 = &levl[x2][y2]; + if ((lev1->drawbridgemask & DB_UNDER) == DB_MOAT || + (lev1->drawbridgemask & DB_UNDER) == DB_LAVA) { + struct obj *otmp; + boolean lava = (lev1->drawbridgemask & DB_UNDER) == DB_LAVA; + if (lev1->typ == DRAWBRIDGE_UP) { + if (cansee(x2,y2)) + pline_The("portcullis of the drawbridge falls into the %s!", + lava ? "lava" : "moat"); + else if (flags.soundok) + You_hear("a loud *SPLASH*!"); + } else { + if (cansee(x,y)) + pline_The("drawbridge collapses into the %s!", + lava ? "lava" : "moat"); + else if (flags.soundok) + You_hear("a loud *SPLASH*!"); + } + lev1->typ = lava ? LAVAPOOL : MOAT; + lev1->drawbridgemask = 0; + if ((otmp = sobj_at(BOULDER,x,y)) != 0) { + obj_extract_self(otmp); + (void) flooreffects(otmp,x,y,"fall"); + } + } else { + if (cansee(x,y)) + pline_The("drawbridge disintegrates!"); + else + You_hear("a loud *CRASH*!"); + lev1->typ = + ((lev1->drawbridgemask & DB_ICE) ? ICE : ROOM); + lev1->icedpool = + ((lev1->drawbridgemask & DB_ICE) ? ICED_MOAT : 0); + } + wake_nearto(x, y, 500); + lev2->typ = DOOR; + lev2->doormask = D_NODOOR; + if ((t = t_at(x, y)) != 0) deltrap(t); + if ((t = t_at(x2, y2)) != 0) deltrap(t); + newsym(x,y); + newsym(x2,y2); + if (!does_block(x2,y2,lev2)) unblock_point(x2,y2); /* vision */ + if (Is_stronghold(&u.uz)) u.uevent.uopened_dbridge = TRUE; + + set_entity(x2, y2, etmp2); /* currently only automissers can be here */ + if (etmp2->edata) { + e_inview = e_canseemon(etmp2); + if (!automiss(etmp2)) { + if (e_inview) + pline("%s blown apart by flying debris.", + E_phrase(etmp2, "are")); + killer_format = KILLED_BY_AN; + killer = "exploding drawbridge"; + e_died(etmp2, e_inview? 3 : 2, CRUSHING); /*no corpse*/ + } /* nothing which is vulnerable can survive this */ + } + set_entity(x, y, etmp1); + if (etmp1->edata) { + e_inview = e_canseemon(etmp1); + if (e_missed(etmp1, TRUE)) { +#ifdef D_DEBUG + pline("%s spared!", E_phrase(etmp1, "are")); +#endif + } else { + if (e_inview) { + if (!is_u(etmp1) && Hallucination) + pline("%s into some heavy metal!", + E_phrase(etmp1, "get")); + else + pline("%s hit by a huge chunk of metal!", + E_phrase(etmp1, "are")); + } else { + if (flags.soundok && !is_u(etmp1) && !is_pool(x,y)) + You_hear("a crushing sound."); +#ifdef D_DEBUG + else + pline("%s from shrapnel", + E_phrase(etmp1, "die")); +#endif + } + killer_format = KILLED_BY_AN; + killer = "collapsing drawbridge"; + e_died(etmp1, e_inview? 3 : 2, CRUSHING); /*no corpse*/ + if(lev1->typ == MOAT) do_entity(etmp1); + } + } +} + +#endif /* OVLB */ + +/*dbridge.c*/ diff --git a/src/decl.c b/src/decl.c new file mode 100644 index 0000000..4dd460e --- /dev/null +++ b/src/decl.c @@ -0,0 +1,281 @@ +/* SCCS Id: @(#)decl.c 3.2 2001/12/10 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +int NDECL((*afternmv)); +int NDECL((*occupation)); + +/* from xxxmain.c */ +const char *hname = 0; /* name of the game (argv[0] of main) */ +int hackpid = 0; /* current process id */ +#if defined(UNIX) || defined(VMS) +int locknum = 0; /* max num of simultaneous users */ +#endif +#ifdef DEF_PAGER +char *catmore = 0; /* default pager */ +#endif + +NEARDATA int bases[MAXOCLASSES] = DUMMY; + +NEARDATA int multi = 0; +#if 0 +NEARDATA int warnlevel = 0; /* used by movemon and dochugw */ +#endif +NEARDATA int nroom = 0; +NEARDATA int nsubroom = 0; +NEARDATA int occtime = 0; + +int x_maze_max, y_maze_max; /* initialized in main, used in mkmaze.c */ +int otg_temp; /* used by object_to_glyph() [otg] */ + +#ifdef REDO +NEARDATA int in_doagain = 0; +#endif + +/* + * The following structure will be initialized at startup time with + * the level numbers of some "important" things in the game. + */ +struct dgn_topology dungeon_topology = {DUMMY}; + +#include "quest.h" +struct q_score quest_status = DUMMY; + +NEARDATA int smeq[MAXNROFROOMS+1] = DUMMY; +NEARDATA int doorindex = 0; + +NEARDATA char *save_cm = 0; +NEARDATA int killer_format = 0; +const char *killer = 0; +const char *delayed_killer = 0; +#ifdef GOLDOBJ +NEARDATA long done_money = 0; +#endif +char killer_buf[BUFSZ] = DUMMY; +const char *nomovemsg = 0; +const char nul[40] = DUMMY; /* contains zeros */ +NEARDATA char plname[PL_NSIZ] = DUMMY; /* player name */ +NEARDATA char pl_character[PL_CSIZ] = DUMMY; +NEARDATA char pl_race = '\0'; + +NEARDATA char pl_fruit[PL_FSIZ] = DUMMY; +NEARDATA int current_fruit = 0; +NEARDATA struct fruit *ffruit = (struct fruit *)0; + +NEARDATA char tune[6] = DUMMY; + +const char *occtxt = DUMMY; +const char quitchars[] = " \r\n\033"; +const char vowels[] = "aeiouAEIOU"; +const char ynchars[] = "yn"; +const char ynqchars[] = "ynq"; +const char ynaqchars[] = "ynaq"; +const char ynNaqchars[] = "yn#aq"; +NEARDATA long yn_number = 0L; + +const char disclosure_options[] = "iavgc"; + +#if defined(MICRO) || defined(WIN32) +char hackdir[PATHLEN]; /* where rumors, help, record are */ +# ifdef MICRO +char levels[PATHLEN]; /* where levels are */ +# endif +#endif /* MICRO || WIN32 */ + + +#ifdef MFLOPPY +char permbones[PATHLEN]; /* where permanent copy of bones go */ +int ramdisk = FALSE; /* whether to copy bones to levels or not */ +int saveprompt = TRUE; +const char *alllevels = "levels.*"; +const char *allbones = "bones*.*"; +#endif + +struct linfo level_info[MAXLINFO]; + +NEARDATA struct sinfo program_state; + +/* 'rogue'-like direction commands (cmd.c) */ +const char sdir[] = "hykulnjb><"; +const char ndir[] = "47896321><"; /* number pad mode */ +const schar xdir[10] = { -1,-1, 0, 1, 1, 1, 0,-1, 0, 0 }; +const schar ydir[10] = { 0,-1,-1,-1, 0, 1, 1, 1, 0, 0 }; +const schar zdir[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 1,-1 }; + +NEARDATA schar tbx = 0, tby = 0; /* mthrowu: target */ + +/* for xname handling of multiple shot missile volleys: + number of shots, index of current one, validity check, shoot vs throw */ +NEARDATA struct multishot m_shot = { 0, 0, STRANGE_OBJECT, FALSE }; + +NEARDATA struct dig_info digging; + +NEARDATA dungeon dungeons[MAXDUNGEON]; /* ini'ed by init_dungeon() */ +NEARDATA s_level *sp_levchn; +NEARDATA stairway upstair = { 0, 0 }, dnstair = { 0, 0 }; +NEARDATA stairway upladder = { 0, 0 }, dnladder = { 0, 0 }; +NEARDATA stairway sstairs = { 0, 0 }; +NEARDATA dest_area updest = { 0, 0, 0, 0, 0, 0, 0, 0 }; +NEARDATA dest_area dndest = { 0, 0, 0, 0, 0, 0, 0, 0 }; +NEARDATA coord inv_pos = { 0, 0 }; + +NEARDATA boolean in_mklev = FALSE; +NEARDATA boolean stoned = FALSE; /* done to monsters hit by 'c' */ +NEARDATA boolean unweapon = FALSE; +NEARDATA boolean mrg_to_wielded = FALSE; + /* weapon picked is merged with wielded one */ +NEARDATA struct obj *current_wand = 0; /* wand currently zapped/applied */ + +NEARDATA boolean in_steed_dismounting = FALSE; + +NEARDATA coord bhitpos = DUMMY; +NEARDATA coord doors[DOORMAX] = {DUMMY}; + +NEARDATA struct mkroom rooms[(MAXNROFROOMS+1)*2] = {DUMMY}; +NEARDATA struct mkroom* subrooms = &rooms[MAXNROFROOMS+1]; +struct mkroom *upstairs_room, *dnstairs_room, *sstairs_room; + +dlevel_t level; /* level map */ +struct trap *ftrap = (struct trap *)0; +NEARDATA struct monst youmonst = DUMMY; +NEARDATA struct flag flags = DUMMY; +NEARDATA struct instance_flags iflags = DUMMY; +NEARDATA struct you u = DUMMY; + +NEARDATA struct obj *invent = (struct obj *)0, + *uwep = (struct obj *)0, *uarm = (struct obj *)0, + *uswapwep = (struct obj *)0, + *uquiver = (struct obj *)0, /* quiver */ +#ifdef TOURIST + *uarmu = (struct obj *)0, /* under-wear, so to speak */ +#endif + *uskin = (struct obj *)0, /* dragon armor, if a dragon */ + *uarmc = (struct obj *)0, *uarmh = (struct obj *)0, + *uarms = (struct obj *)0, *uarmg = (struct obj *)0, + *uarmf = (struct obj *)0, *uamul = (struct obj *)0, + *uright = (struct obj *)0, + *uleft = (struct obj *)0, + *ublindf = (struct obj *)0, + *uchain = (struct obj *)0, + *uball = (struct obj *)0; + +#ifdef TEXTCOLOR +/* + * This must be the same order as used for buzz() in zap.c. + */ +const int zapcolors[NUM_ZAP] = { + HI_ZAP, /* 0 - missile */ + CLR_ORANGE, /* 1 - fire */ + CLR_WHITE, /* 2 - frost */ + HI_ZAP, /* 3 - sleep */ + CLR_BLACK, /* 4 - death */ + CLR_WHITE, /* 5 - lightning */ + CLR_YELLOW, /* 6 - poison gas */ + CLR_GREEN, /* 7 - acid */ +}; +#endif /* text color */ + +const int shield_static[SHIELD_COUNT] = { + S_ss1, S_ss2, S_ss3, S_ss2, S_ss1, S_ss2, S_ss4, /* 7 per row */ + S_ss1, S_ss2, S_ss3, S_ss2, S_ss1, S_ss2, S_ss4, + S_ss1, S_ss2, S_ss3, S_ss2, S_ss1, S_ss2, S_ss4, +}; + +NEARDATA struct spell spl_book[MAXSPELL + 1] = {DUMMY}; + +NEARDATA long moves = 1L, monstermoves = 1L; + /* These diverge when player is Fast */ +NEARDATA long wailmsg = 0L; + +/* objects that are moving to another dungeon level */ +NEARDATA struct obj *migrating_objs = (struct obj *)0; +/* objects not yet paid for */ +NEARDATA struct obj *billobjs = (struct obj *)0; + +/* used to zero all elements of a struct obj */ +NEARDATA struct obj zeroobj = DUMMY; + +/* originally from dog.c */ +NEARDATA char dogname[PL_PSIZ] = DUMMY; +NEARDATA char catname[PL_PSIZ] = DUMMY; +NEARDATA char horsename[PL_PSIZ] = DUMMY; +char preferred_pet; /* '\0', 'c', 'd', 'n' (none) */ +/* monsters that went down/up together with @ */ +NEARDATA struct monst *mydogs = (struct monst *)0; +/* monsters that are moving to another dungeon level */ +NEARDATA struct monst *migrating_mons = (struct monst *)0; + +NEARDATA struct mvitals mvitals[NUMMONS]; + +NEARDATA struct c_color_names c_color_names = { + "black", "amber", "golden", + "light blue", "red", "green", + "silver", "blue", "purple", + "white" +}; + +const char *c_obj_colors[] = { + "black", /* CLR_BLACK */ + "red", /* CLR_RED */ + "green", /* CLR_GREEN */ + "brown", /* CLR_BROWN */ + "blue", /* CLR_BLUE */ + "magenta", /* CLR_MAGENTA */ + "cyan", /* CLR_CYAN */ + "gray", /* CLR_GRAY */ + "transparent", /* no_color */ + "orange", /* CLR_ORANGE */ + "bright green", /* CLR_BRIGHT_GREEN */ + "yellow", /* CLR_YELLOW */ + "bright blue", /* CLR_BRIGHT_BLUE */ + "bright magenta", /* CLR_BRIGHT_MAGENTA */ + "bright cyan", /* CLR_BRIGHT_CYAN */ + "white", /* CLR_WHITE */ +}; + +struct c_common_strings c_common_strings = { + "Nothing happens.", "That's enough tries!", + "That is a silly thing to %s.", "shudder for a moment.", + "something", "Something", "You can move again.", "Never mind.", + "vision quickly clears.", {"the", "your"} +}; + +/* NOTE: the order of these words exactly corresponds to the + order of oc_material values #define'd in objclass.h. */ +const char *materialnm[] = { + "mysterious", "liquid", "wax", "organic", "flesh", + "paper", "cloth", "leather", "wooden", "bone", "dragonhide", + "iron", "metal", "copper", "silver", "gold", "platinum", "mithril", + "plastic", "glass", "gemstone", "stone" +}; + +/* Vision */ +NEARDATA boolean vision_full_recalc = 0; +NEARDATA char **viz_array = 0;/* used in cansee() and couldsee() macros */ + +/* Global windowing data, defined here for multi-window-system support */ +NEARDATA winid WIN_MESSAGE = WIN_ERR, WIN_STATUS = WIN_ERR; +NEARDATA winid WIN_MAP = WIN_ERR, WIN_INVEN = WIN_ERR; +char toplines[TBUFSZ]; +/* Windowing stuff that's really tty oriented, but present for all ports */ +struct tc_gbl_data tc_gbl_data = { 0,0, 0,0 }; /* AS,AE, LI,CO */ + +char *fqn_prefix[PREFIX_COUNT] = { (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0 }; + +#ifdef PREFIXES_IN_USE +char *fqn_prefix_names[PREFIX_COUNT] = { "hackdir", "leveldir", "savedir", + "bonesdir", "datadir", "scoredir", + "lockdir", "configdir", "troubledir" }; +#endif + +/* dummy routine used to force linkage */ +void +decl_init() +{ + return; +} + +/*decl.c*/ diff --git a/src/detect.c b/src/detect.c new file mode 100644 index 0000000..5da025f --- /dev/null +++ b/src/detect.c @@ -0,0 +1,1299 @@ +/* SCCS Id: @(#)detect.c 3.4 2003/08/13 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Detection routines, including crystal ball, magic mapping, and search + * command. + */ + +#include "hack.h" +#include "artifact.h" + +extern boolean known; /* from read.c */ + +STATIC_DCL void FDECL(do_dknown_of, (struct obj *)); +STATIC_DCL boolean FDECL(check_map_spot, (int,int,CHAR_P,unsigned)); +STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P,unsigned)); +STATIC_DCL void FDECL(sense_trap, (struct trap *,XCHAR_P,XCHAR_P,int)); +STATIC_DCL void FDECL(show_map_spot, (int,int)); +STATIC_PTR void FDECL(findone,(int,int,genericptr_t)); +STATIC_PTR void FDECL(openone,(int,int,genericptr_t)); + +/* Recursively search obj for an object in class oclass and return 1st found */ +struct obj * +o_in(obj, oclass) +struct obj* obj; +char oclass; +{ + register struct obj* otmp; + struct obj *temp; + + if (obj->oclass == oclass) return obj; + + if (Has_contents(obj)) { + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) + if (otmp->oclass == oclass) return otmp; + else if (Has_contents(otmp) && (temp = o_in(otmp, oclass))) + return temp; + } + return (struct obj *) 0; +} + +/* Recursively search obj for an object made of specified material and return 1st found */ +struct obj * +o_material(obj, material) +struct obj* obj; +unsigned material; +{ + register struct obj* otmp; + struct obj *temp; + + if (objects[obj->otyp].oc_material == material) return obj; + + if (Has_contents(obj)) { + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) + if (objects[otmp->otyp].oc_material == material) return otmp; + else if (Has_contents(otmp) && (temp = o_material(otmp, material))) + return temp; + } + return (struct obj *) 0; +} + +STATIC_OVL void +do_dknown_of(obj) +struct obj *obj; +{ + struct obj *otmp; + + obj->dknown = 1; + if (Has_contents(obj)) { + for(otmp = obj->cobj; otmp; otmp = otmp->nobj) + do_dknown_of(otmp); + } +} + +/* Check whether the location has an outdated object displayed on it. */ +STATIC_OVL boolean +check_map_spot(x, y, oclass, material) +int x, y; +register char oclass; +unsigned material; +{ + register int glyph; + register struct obj *otmp; + register struct monst *mtmp; + + glyph = glyph_at(x,y); + if (glyph_is_object(glyph)) { + /* there's some object shown here */ + if (oclass == ALL_CLASSES) { + return((boolean)( !(level.objects[x][y] || /* stale if nothing here */ + ((mtmp = m_at(x,y)) != 0 && + ( +#ifndef GOLDOBJ + mtmp->mgold || +#endif + mtmp->minvent))))); + } else { + if (material && objects[glyph_to_obj(glyph)].oc_material == material) { + /* the object shown here is of interest because material matches */ + for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (o_material(otmp, GOLD)) return FALSE; + /* didn't find it; perhaps a monster is carrying it */ + if ((mtmp = m_at(x,y)) != 0) { + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (o_material(otmp, GOLD)) return FALSE; + } + /* detection indicates removal of this object from the map */ + return TRUE; + } + if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) { + /* the object shown here is of interest because its class matches */ + for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (o_in(otmp, oclass)) return FALSE; + /* didn't find it; perhaps a monster is carrying it */ +#ifndef GOLDOBJ + if ((mtmp = m_at(x,y)) != 0) { + if (oclass == COIN_CLASS && mtmp->mgold) + return FALSE; + else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (o_in(otmp, oclass)) return FALSE; + } +#else + if ((mtmp = m_at(x,y)) != 0) { + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (o_in(otmp, oclass)) return FALSE; + } +#endif + /* detection indicates removal of this object from the map */ + return TRUE; + } + } + } + return FALSE; +} + +/* + When doing detection, remove stale data from the map display (corpses + rotted away, objects carried away by monsters, etc) so that it won't + reappear after the detection has completed. Return true if noticeable + change occurs. + */ +STATIC_OVL boolean +clear_stale_map(oclass, material) +register char oclass; +unsigned material; +{ + register int zx, zy; + register boolean change_made = FALSE; + + for (zx = 1; zx < COLNO; zx++) + for (zy = 0; zy < ROWNO; zy++) + if (check_map_spot(zx, zy, oclass,material)) { + unmap_object(zx, zy); + change_made = TRUE; + } + + return change_made; +} + +/* look for gold, on the floor or in monsters' possession */ +int +gold_detect(sobj) +register struct obj *sobj; +{ + register struct obj *obj; + register struct monst *mtmp; + int uw = u.uinwater; + struct obj *temp; + boolean stale; + + known = stale = clear_stale_map(COIN_CLASS, + (unsigned)(sobj->blessed ? GOLD : 0)); + + /* look for gold carried by monsters (might be in a container) */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */ +#ifndef GOLDOBJ + if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#else + if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#endif + known = TRUE; + goto outgoldmap; /* skip further searching */ + } else for (obj = mtmp->minvent; obj; obj = obj->nobj) + if (sobj->blessed && o_material(obj, GOLD)) { + known = TRUE; + goto outgoldmap; + } else if (o_in(obj, COIN_CLASS)) { + known = TRUE; + goto outgoldmap; /* skip further searching */ + } + } + + /* look for gold objects */ + for (obj = fobj; obj; obj = obj->nobj) { + if (sobj->blessed && o_material(obj, GOLD)) { + known = TRUE; + if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; + } else if (o_in(obj, COIN_CLASS)) { + known = TRUE; + if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap; + } + } + + if (!known) { + /* no gold found on floor or monster's inventory. + adjust message if you have gold in your inventory */ + if (sobj) { + char buf[BUFSZ]; + if (youmonst.data == &mons[PM_GOLD_GOLEM]) { + Sprintf(buf, "You feel like a million %s!", + currency(2L)); + } else if (hidden_gold() || +#ifndef GOLDOBJ + u.ugold) +#else + money_cnt(invent)) +#endif + Strcpy(buf, + "You feel worried about your future financial situation."); + else + Strcpy(buf, "You feel materially poor."); + strange_feeling(sobj, buf); + } + return(1); + } + /* only under me - no separate display required */ + if (stale) docrt(); + You("notice some gold between your %s.", makeplural(body_part(FOOT))); + return(0); + +outgoldmap: + cls(); + + u.uinwater = 0; + /* Discover gold locations. */ + for (obj = fobj; obj; obj = obj->nobj) { + if (sobj->blessed && (temp = o_material(obj, GOLD))) { + if (temp != obj) { + temp->ox = obj->ox; + temp->oy = obj->oy; + } + map_object(temp,1); + } else if ((temp = o_in(obj, COIN_CLASS))) { + if (temp != obj) { + temp->ox = obj->ox; + temp->oy = obj->oy; + } + map_object(temp,1); + } + } + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; /* probably overkill here */ +#ifndef GOLDOBJ + if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#else + if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { +#endif + struct obj gold; + + gold.otyp = GOLD_PIECE; + gold.ox = mtmp->mx; + gold.oy = mtmp->my; + map_object(&gold,1); + } else for (obj = mtmp->minvent; obj; obj = obj->nobj) + if (sobj->blessed && (temp = o_material(obj, GOLD))) { + temp->ox = mtmp->mx; + temp->oy = mtmp->my; + map_object(temp,1); + break; + } else if ((temp = o_in(obj, COIN_CLASS))) { + temp->ox = mtmp->mx; + temp->oy = mtmp->my; + map_object(temp,1); + break; + } + } + + newsym(u.ux,u.uy); + You_feel("very greedy, and sense gold!"); + exercise(A_WIS, TRUE); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + u.uinwater = uw; + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + return(0); +} + +/* returns 1 if nothing was detected */ +/* returns 0 if something was detected */ +int +food_detect(sobj) +register struct obj *sobj; +{ + register struct obj *obj; + register struct monst *mtmp; + register int ct = 0, ctu = 0; + boolean confused = (Confusion || (sobj && sobj->cursed)), stale; + char oclass = confused ? POTION_CLASS : FOOD_CLASS; + const char *what = confused ? something : "food"; + int uw = u.uinwater; + + stale = clear_stale_map(oclass, 0); + + for (obj = fobj; obj; obj = obj->nobj) + if (o_in(obj, oclass)) { + if (obj->ox == u.ux && obj->oy == u.uy) ctu++; + else ct++; + } + for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) { + /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */ + for (obj = mtmp->minvent; obj; obj = obj->nobj) + if (o_in(obj, oclass)) { + ct++; + break; + } + } + + if (!ct && !ctu) { + known = stale && !confused; + if (stale) { + docrt(); + You("sense a lack of %s nearby.", what); + if (sobj && sobj->blessed) { + if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE)); + u.uedibility = 1; + } + } else if (sobj) { + char buf[BUFSZ]; + Sprintf(buf, "Your %s twitches%s.", body_part(NOSE), + (sobj->blessed && !u.uedibility) ? " then starts to tingle" : ""); + if (sobj->blessed && !u.uedibility) { + boolean savebeginner = flags.beginner; /* prevent non-delivery of */ + flags.beginner = FALSE; /* message */ + strange_feeling(sobj, buf); + flags.beginner = savebeginner; + u.uedibility = 1; + } else + strange_feeling(sobj, buf); + } + return !stale; + } else if (!ct) { + known = TRUE; + You("%s %s nearby.", sobj ? "smell" : "sense", what); + if (sobj && sobj->blessed) { + if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE)); + u.uedibility = 1; + } + } else { + struct obj *temp; + known = TRUE; + cls(); + u.uinwater = 0; + for (obj = fobj; obj; obj = obj->nobj) + if ((temp = o_in(obj, oclass)) != 0) { + if (temp != obj) { + temp->ox = obj->ox; + temp->oy = obj->oy; + } + map_object(temp,1); + } + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */ + for (obj = mtmp->minvent; obj; obj = obj->nobj) + if ((temp = o_in(obj, oclass)) != 0) { + temp->ox = mtmp->mx; + temp->oy = mtmp->my; + map_object(temp,1); + break; /* skip rest of this monster's inventory */ + } + newsym(u.ux,u.uy); + if (sobj) { + if (sobj->blessed) { + Your("%s %s to tingle and you smell %s.", body_part(NOSE), + u.uedibility ? "continues" : "starts", what); + u.uedibility = 1; + } else + Your("%s tingles and you smell %s.", body_part(NOSE), what); + } + else You("sense %s.", what); + display_nhwindow(WIN_MAP, TRUE); + exercise(A_WIS, TRUE); + docrt(); + u.uinwater = uw; + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + } + return(0); +} + +/* + * Used for scrolls, potions, spells, and crystal balls. Returns: + * + * 1 - nothing was detected + * 0 - something was detected + */ +int +object_detect(detector, class) +struct obj *detector; /* object doing the detecting */ +int class; /* an object class, 0 for all */ +{ + register int x, y; + char stuff[BUFSZ]; + int is_cursed = (detector && detector->cursed); + int do_dknown = (detector && (detector->oclass == POTION_CLASS || + detector->oclass == SPBOOK_CLASS) && + detector->blessed); + int ct = 0, ctu = 0; + register struct obj *obj, *otmp = (struct obj *)0; + register struct monst *mtmp; + int uw = u.uinwater; + int sym, boulder = 0; + + if (class < 0 || class >= MAXOCLASSES) { + impossible("object_detect: illegal class %d", class); + class = 0; + } + + /* Special boulder symbol check - does the class symbol happen + * to match iflags.bouldersym which is a user-defined? + * If so, that means we aren't sure what they really wanted to + * detect. Rather than trump anything, show both possibilities. + * We can exclude checking the buried obj chain for boulders below. + */ + sym = class ? def_oc_syms[class] : 0; + if (sym && iflags.bouldersym && sym == iflags.bouldersym) + boulder = ROCK_CLASS; + + if (Hallucination || (Confusion && class == SCROLL_CLASS)) + Strcpy(stuff, something); + else + Strcpy(stuff, class ? oclass_names[class] : "objects"); + if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones"); + + if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj); + + for (obj = fobj; obj; obj = obj->nobj) { + if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) { + if (obj->ox == u.ux && obj->oy == u.uy) ctu++; + else ct++; + } + if (do_dknown) do_dknown_of(obj); + } + + for (obj = level.buriedobjlist; obj; obj = obj->nobj) { + if (!class || o_in(obj, class)) { + if (obj->ox == u.ux && obj->oy == u.uy) ctu++; + else ct++; + } + if (do_dknown) do_dknown_of(obj); + } + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + for (obj = mtmp->minvent; obj; obj = obj->nobj) { + if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) ct++; + if (do_dknown) do_dknown_of(obj); + } + if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT && + (!class || class == objects[mtmp->mappearance].oc_class)) || +#ifndef GOLDOBJ + (mtmp->mgold && (!class || class == COIN_CLASS))) { +#else + (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) { +#endif + ct++; + break; + } + } + + if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) { + if (!ctu) { + if (detector) + strange_feeling(detector, "You feel a lack of something."); + return 1; + } + + You("sense %s nearby.", stuff); + return 0; + } + + cls(); + + u.uinwater = 0; +/* + * Map all buried objects first. + */ + for (obj = level.buriedobjlist; obj; obj = obj->nobj) + if (!class || (otmp = o_in(obj, class))) { + if (class) { + if (otmp != obj) { + otmp->ox = obj->ox; + otmp->oy = obj->oy; + } + map_object(otmp, 1); + } else + map_object(obj, 1); + } + /* + * If we are mapping all objects, map only the top object of a pile or + * the first object in a monster's inventory. Otherwise, go looking + * for a matching object class and display the first one encountered + * at each location. + * + * Objects on the floor override buried objects. + */ + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + for (obj = level.objects[x][y]; obj; obj = obj->nexthere) + if ((!class && !boulder) || + (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) { + if (class || boulder) { + if (otmp != obj) { + otmp->ox = obj->ox; + otmp->oy = obj->oy; + } + map_object(otmp, 1); + } else + map_object(obj, 1); + break; + } + + /* Objects in the monster's inventory override floor objects. */ + for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + for (obj = mtmp->minvent; obj; obj = obj->nobj) + if ((!class && !boulder) || + (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) { + if (!class && !boulder) otmp = obj; + otmp->ox = mtmp->mx; /* at monster location */ + otmp->oy = mtmp->my; + map_object(otmp, 1); + break; + } + /* Allow a mimic to override the detected objects it is carrying. */ + if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT && + (!class || class == objects[mtmp->mappearance].oc_class)) { + struct obj temp; + + temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */ + temp.ox = mtmp->mx; + temp.oy = mtmp->my; + temp.corpsenm = PM_TENGU; /* if mimicing a corpse */ + map_object(&temp, 1); +#ifndef GOLDOBJ + } else if (mtmp->mgold && (!class || class == COIN_CLASS)) { +#else + } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) { +#endif + struct obj gold; + + gold.otyp = GOLD_PIECE; + gold.ox = mtmp->mx; + gold.oy = mtmp->my; + map_object(&gold, 1); + } + } + + newsym(u.ux,u.uy); + You("detect the %s of %s.", ct ? "presence" : "absence", stuff); + display_nhwindow(WIN_MAP, TRUE); + /* + * What are we going to do when the hero does an object detect while blind + * and the detected object covers a known pool? + */ + docrt(); /* this will correctly reset vision */ + + u.uinwater = uw; + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + return 0; +} + +/* + * Used by: crystal balls, potions, fountains + * + * Returns 1 if nothing was detected. + * Returns 0 if something was detected. + */ +int +monster_detect(otmp, mclass) +register struct obj *otmp; /* detecting object (if any) */ +int mclass; /* monster class, 0 for all */ +{ + register struct monst *mtmp; + int mcnt = 0; + + + /* Note: This used to just check fmon for a non-zero value + * but in versions since 3.3.0 fmon can test TRUE due to the + * presence of dmons, so we have to find at least one + * with positive hit-points to know for sure. + */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp)) { + mcnt++; + break; + } + + if (!mcnt) { + if (otmp) + strange_feeling(otmp, Hallucination ? + "You get the heebie jeebies." : + "You feel threatened."); + return 1; + } else { + boolean woken = FALSE; + + cls(); + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if (!mclass || mtmp->data->mlet == mclass || + (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL)) + if (mtmp->mx > 0) { + if (mclass && def_monsyms[mclass] == ' ') + show_glyph(mtmp->mx,mtmp->my, + detected_mon_to_glyph(mtmp)); + else + show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp)); + /* don't be stingy - display entire worm */ + if (mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp,0); + } + if (otmp && otmp->cursed && + (mtmp->msleeping || !mtmp->mcanmove)) { + mtmp->msleeping = mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + woken = TRUE; + } + } + display_self(); + You("sense the presence of monsters."); + if (woken) + pline("Monsters sense the presence of you."); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + } + return 0; +} + +STATIC_OVL void +sense_trap(trap, x, y, src_cursed) +struct trap *trap; +xchar x, y; +int src_cursed; +{ + if (Hallucination || src_cursed) { + struct obj obj; /* fake object */ + if (trap) { + obj.ox = trap->tx; + obj.oy = trap->ty; + } else { + obj.ox = x; + obj.oy = y; + } + obj.otyp = (src_cursed) ? GOLD_PIECE : random_object(); + obj.corpsenm = random_monster(); /* if otyp == CORPSE */ + map_object(&obj,1); + } else if (trap) { + map_trap(trap,1); + trap->tseen = 1; + } else { + struct trap temp_trap; /* fake trap */ + temp_trap.tx = x; + temp_trap.ty = y; + temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */ + map_trap(&temp_trap,1); + } + +} + +/* the detections are pulled out so they can */ +/* also be used in the crystal ball routine */ +/* returns 1 if nothing was detected */ +/* returns 0 if something was detected */ +int +trap_detect(sobj) +register struct obj *sobj; +/* sobj is null if crystal ball, *scroll if gold detection scroll */ +{ + register struct trap *ttmp; + register struct obj *obj; + register int door; + int uw = u.uinwater; + boolean found = FALSE; + coord cc; + + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + if (ttmp->tx != u.ux || ttmp->ty != u.uy) + goto outtrapmap; + else found = TRUE; + } + for (obj = fobj; obj; obj = obj->nobj) { + if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) { + if (obj->ox != u.ux || obj->oy != u.uy) + goto outtrapmap; + else found = TRUE; + } + } + for (door = 0; door < doorindex; door++) { + cc = doors[door]; + if (levl[cc.x][cc.y].doormask & D_TRAPPED) { + if (cc.x != u.ux || cc.y != u.uy) + goto outtrapmap; + else found = TRUE; + } + } + if (!found) { + char buf[42]; + Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE))); + strange_feeling(sobj,buf); + return(1); + } + /* traps exist, but only under me - no separate display required */ + Your("%s itch.", makeplural(body_part(TOE))); + return(0); +outtrapmap: + cls(); + + u.uinwater = 0; + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + sense_trap(ttmp, 0, 0, sobj && sobj->cursed); + + for (obj = fobj; obj; obj = obj->nobj) + if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) + sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed); + + for (door = 0; door < doorindex; door++) { + cc = doors[door]; + if (levl[cc.x][cc.y].doormask & D_TRAPPED) + sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed); + } + + newsym(u.ux,u.uy); + You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped"); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + u.uinwater = uw; + if (Underwater) under_water(2); + if (u.uburied) under_ground(2); + return(0); +} + +const char * +level_distance(where) +d_level *where; +{ + register schar ll = depth(&u.uz) - depth(where); + register boolean indun = (u.uz.dnum == where->dnum); + + if (ll < 0) { + if (ll < (-8 - rn2(3))) + if (!indun) return "far away"; + else return "far below"; + else if (ll < -1) + if (!indun) return "away below you"; + else return "below you"; + else + if (!indun) return "in the distance"; + else return "just below"; + } else if (ll > 0) { + if (ll > (8 + rn2(3))) + if (!indun) return "far away"; + else return "far above"; + else if (ll > 1) + if (!indun) return "away above you"; + else return "above you"; + else + if (!indun) return "in the distance"; + else return "just above"; + } else + if (!indun) return "in the distance"; + else return "near you"; +} + +static const struct { + const char *what; + d_level *where; +} level_detects[] = { + { "Delphi", &oracle_level }, + { "Medusa's lair", &medusa_level }, + { "a castle", &stronghold_level }, + { "the Wizard of Yendor's tower", &wiz1_level }, +}; + +void +use_crystal_ball(obj) +struct obj *obj; +{ + char ch; + int oops; + + if (Blind) { + pline("Too bad you can't see %s.", the(xname(obj))); + return; + } + oops = (rnd(20) > ACURR(A_INT) || obj->cursed); + if (oops && (obj->spe > 0)) { + switch (rnd(obj->oartifact ? 4 : 5)) { + case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are")); + break; + case 2 : pline("%s you!", Tobjnam(obj, "confuse")); + make_confused(HConfusion + rnd(100),FALSE); + break; + case 3 : if (!resists_blnd(&youmonst)) { + pline("%s your vision!", Tobjnam(obj, "damage")); + make_blinded(Blinded + rnd(100),FALSE); + if (!Blind) Your(vision_clears); + } else { + pline("%s your vision.", Tobjnam(obj, "assault")); + You("are unaffected!"); + } + break; + case 4 : pline("%s your mind!", Tobjnam(obj, "zap")); + (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L); + break; + case 5 : pline("%s!", Tobjnam(obj, "explode")); + useup(obj); + obj = 0; /* it's gone */ + losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN); + break; + } + if (obj) consume_obj_charge(obj, TRUE); + return; + } + + if (Hallucination) { + if (!obj->spe) { + pline("All you see is funky %s haze.", hcolor((char *)0)); + } else { + switch(rnd(6)) { + case 1 : You("grok some groovy globs of incandescent lava."); + break; + case 2 : pline("Whoa! Psychedelic colors, %s!", + poly_gender() == 1 ? "babe" : "dude"); + break; + case 3 : pline_The("crystal pulses with sinister %s light!", + hcolor((char *)0)); + break; + case 4 : You("see goldfish swimming above fluorescent rocks."); + break; + case 5 : You("see tiny snowflakes spinning around a miniature farmhouse."); + break; + default: pline("Oh wow... like a kaleidoscope!"); + break; + } + consume_obj_charge(obj, TRUE); + } + return; + } + + /* read a single character */ + if (flags.verbose) You("may look for an object or monster symbol."); + ch = yn_function("What do you look for?", (char *)0, '\0'); + /* Don't filter out ' ' here; it has a use */ + if ((ch != def_monsyms[S_GHOST]) && index(quitchars,ch)) { + if (flags.verbose) pline(Never_mind); + return; + } + You("peer into %s...", the(xname(obj))); + nomul(-rnd(10)); + nomovemsg = ""; + if (obj->spe <= 0) + pline_The("vision is unclear."); + else { + int class; + int ret = 0; + + makeknown(CRYSTAL_BALL); + consume_obj_charge(obj, TRUE); + + /* special case: accept ']' as synonym for mimic + * we have to do this before the def_char_to_objclass check + */ + if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC; + + if ((class = def_char_to_objclass(ch)) != MAXOCLASSES) + ret = object_detect((struct obj *)0, class); + else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES) + ret = monster_detect((struct obj *)0, class); + else if (iflags.bouldersym && (ch == iflags.bouldersym)) + ret = object_detect((struct obj *)0, ROCK_CLASS); + else switch(ch) { + case '^': + ret = trap_detect((struct obj *)0); + break; + default: + { + int i = rn2(SIZE(level_detects)); + You("see %s, %s.", + level_detects[i].what, + level_distance(level_detects[i].where)); + } + ret = 0; + break; + } + + if (ret) { + if (!rn2(100)) /* make them nervous */ + You("see the Wizard of Yendor gazing out at you."); + else pline_The("vision is unclear."); + } + } + return; +} + +STATIC_OVL void +show_map_spot(x, y) +register int x, y; +{ + register struct rm *lev; + + if (Confusion && rn2(7)) return; + lev = &levl[x][y]; + + lev->seenv = SVALL; + + /* Secret corridors are found, but not secret doors. */ + if (lev->typ == SCORR) { + lev->typ = CORR; + unblock_point(x,y); + } + + /* if we don't remember an object or trap there, map it */ + if (lev->typ == ROOM ? + (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) && + glyph_to_cmap(lev->glyph) != ROOM) : + (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) { + if (level.flags.hero_memory) { + magic_map_background(x,y,0); + newsym(x,y); /* show it, if not blocked */ + } else { + magic_map_background(x,y,1); /* display it */ + } + } +} + +void +do_mapping() +{ + register int zx, zy; + int uw = u.uinwater; + + u.uinwater = 0; + for (zx = 1; zx < COLNO; zx++) + for (zy = 0; zy < ROWNO; zy++) + show_map_spot(zx, zy); + exercise(A_WIS, TRUE); + u.uinwater = uw; + if (!level.flags.hero_memory || Underwater) { + flush_screen(1); /* flush temp screen */ + display_nhwindow(WIN_MAP, TRUE); /* wait */ + docrt(); + } +} + +void +do_vicinity_map() +{ + register int zx, zy; + int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5), + hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6), + lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */ + hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10); + + for (zx = lo_x; zx < hi_x; zx++) + for (zy = lo_y; zy < hi_y; zy++) + show_map_spot(zx, zy); + + if (!level.flags.hero_memory || Underwater) { + flush_screen(1); /* flush temp screen */ + display_nhwindow(WIN_MAP, TRUE); /* wait */ + docrt(); + } +} + +/* convert a secret door into a normal door */ +void +cvt_sdoor_to_door(lev) +struct rm *lev; +{ + int newmask = lev->doormask & ~WM_MASK; + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) + /* rogue didn't have doors, only doorways */ + newmask = D_NODOOR; + else +#endif + /* newly exposed door is closed */ + if (!(newmask & D_LOCKED)) newmask |= D_CLOSED; + + lev->typ = DOOR; + lev->doormask = newmask; +} + + +STATIC_PTR void +findone(zx,zy,num) +int zx,zy; +genericptr_t num; +{ + register struct trap *ttmp; + register struct monst *mtmp; + + if(levl[zx][zy].typ == SDOOR) { + cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ + magic_map_background(zx, zy, 0); + newsym(zx, zy); + (*(int*)num)++; + } else if(levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; + unblock_point(zx,zy); + magic_map_background(zx, zy, 0); + newsym(zx, zy); + (*(int*)num)++; + } else if ((ttmp = t_at(zx, zy)) != 0) { + if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) { + ttmp->tseen = 1; + newsym(zx,zy); + (*(int*)num)++; + } + } else if ((mtmp = m_at(zx, zy)) != 0) { + if(mtmp->m_ap_type) { + seemimic(mtmp); + (*(int*)num)++; + } + if (mtmp->mundetected && + (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) { + mtmp->mundetected = 0; + newsym(zx, zy); + (*(int*)num)++; + } + if (!canspotmon(mtmp) && + !glyph_is_invisible(levl[zx][zy].glyph)) + map_invisible(zx, zy); + } else if (glyph_is_invisible(levl[zx][zy].glyph)) { + unmap_object(zx, zy); + newsym(zx, zy); + (*(int*)num)++; + } +} + +STATIC_PTR void +openone(zx,zy,num) +int zx,zy; +genericptr_t num; +{ + register struct trap *ttmp; + register struct obj *otmp; + + if(OBJ_AT(zx, zy)) { + for(otmp = level.objects[zx][zy]; + otmp; otmp = otmp->nexthere) { + if(Is_box(otmp) && otmp->olocked) { + otmp->olocked = 0; + (*(int*)num)++; + } + } + /* let it fall to the next cases. could be on trap. */ + } + if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR && + (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) { + if(levl[zx][zy].typ == SDOOR) + cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ + if(levl[zx][zy].doormask & D_TRAPPED) { + if(distu(zx, zy) < 3) b_trapped("door", 0); + else Norep("You %s an explosion!", + cansee(zx, zy) ? "see" : + (flags.soundok ? "hear" : + "feel the shock of")); + wake_nearto(zx, zy, 11*11); + levl[zx][zy].doormask = D_NODOOR; + } else + levl[zx][zy].doormask = D_ISOPEN; + unblock_point(zx, zy); + newsym(zx, zy); + (*(int*)num)++; + } else if(levl[zx][zy].typ == SCORR) { + levl[zx][zy].typ = CORR; + unblock_point(zx, zy); + newsym(zx, zy); + (*(int*)num)++; + } else if ((ttmp = t_at(zx, zy)) != 0) { + if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) { + ttmp->tseen = 1; + newsym(zx,zy); + (*(int*)num)++; + } + } else if (find_drawbridge(&zx, &zy)) { + /* make sure it isn't an open drawbridge */ + open_drawbridge(zx, zy); + (*(int*)num)++; + } +} + +int +findit() /* returns number of things found */ +{ + int num = 0; + + if(u.uswallow) return(0); + do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num); + return(num); +} + +int +openit() /* returns number of things found and opened */ +{ + int num = 0; + + if(u.uswallow) { + if (is_animal(u.ustuck->data)) { + if (Blind) pline("Its mouth opens!"); + else pline("%s opens its mouth!", Monnam(u.ustuck)); + } + expels(u.ustuck, u.ustuck->data, TRUE); + return(-1); + } + + do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num); + return(num); +} + +void +find_trap(trap) +struct trap *trap; +{ + int tt = what_trap(trap->ttyp); + boolean cleared = FALSE; + + trap->tseen = 1; + exercise(A_WIS, TRUE); + if (Blind) + feel_location(trap->tx, trap->ty); + else + newsym(trap->tx, trap->ty); + + if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) { + /* There's too much clutter to see your find otherwise */ + cls(); + map_trap(trap, 1); + display_self(); + cleared = TRUE; + } + + You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation)); + + if (cleared) { + display_nhwindow(WIN_MAP, TRUE); /* wait */ + docrt(); + } +} + +int +dosearch0(aflag) +register int aflag; +{ +#ifdef GCC_BUG +/* some versions of gcc seriously muck up nested loops. if you get strange + crashes while searching in a version compiled with gcc, try putting + #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the + makefile). + */ + volatile xchar x, y; +#else + register xchar x, y; +#endif + register struct trap *trap; + register struct monst *mtmp; + + if(u.uswallow) { + if (!aflag) + pline("What are you looking for? The exit?"); + } else { + int fund = (uwep && uwep->oartifact && + spec_ability(uwep, SPFX_SEARCH)) ? + uwep->spe : 0; + if (ublindf && ublindf->otyp == LENSES && !Blind) + fund += 2; /* JDS: lenses help searching */ + if (fund > 5) fund = 5; + for(x = u.ux-1; x < u.ux+2; x++) + for(y = u.uy-1; y < u.uy+2; y++) { + if(!isok(x,y)) continue; + if(x != u.ux || y != u.uy) { + if (Blind && !aflag) feel_location(x,y); + if(levl[x][y].typ == SDOOR) { + if(rnl(7-fund)) continue; + cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */ + exercise(A_WIS, TRUE); + nomul(0); + if (Blind && !aflag) + feel_location(x,y); /* make sure it shows up */ + else + newsym(x,y); + } else if(levl[x][y].typ == SCORR) { + if(rnl(7-fund)) continue; + levl[x][y].typ = CORR; + unblock_point(x,y); /* vision */ + exercise(A_WIS, TRUE); + nomul(0); + newsym(x,y); + } else { + /* Be careful not to find anything in an SCORR or SDOOR */ + if((mtmp = m_at(x, y)) && !aflag) { + if(mtmp->m_ap_type) { + seemimic(mtmp); + find: exercise(A_WIS, TRUE); + if (!canspotmon(mtmp)) { + if (glyph_is_invisible(levl[x][y].glyph)) { + /* found invisible monster in a square + * which already has an 'I' in it. + * Logically, this should still take + * time and lead to a return(1), but if + * we did that the player would keep + * finding the same monster every turn. + */ + continue; + } else { + You_feel("an unseen monster!"); + map_invisible(x, y); + } + } else if (!sensemon(mtmp)) + You("find %s.", a_monnam(mtmp)); + return(1); + } + if(!canspotmon(mtmp)) { + if (mtmp->mundetected && + (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) + mtmp->mundetected = 0; + newsym(x,y); + goto find; + } + } + + /* see if an invisible monster has moved--if Blind, + * feel_location() already did it + */ + if (!aflag && !mtmp && !Blind && + glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x,y); + newsym(x,y); + } + + if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) { + nomul(0); + + if (trap->ttyp == STATUE_TRAP) { + if (activate_statue_trap(trap, x, y, FALSE)) + exercise(A_WIS, TRUE); + return(1); + } else { + find_trap(trap); + } + } + } + } + } + } + return(1); +} + +int +dosearch() +{ + return(dosearch0(0)); +} + +/* Pre-map the sokoban levels */ +void +sokoban_detect() +{ + register int x, y; + register struct trap *ttmp; + register struct obj *obj; + + /* Map the background and boulders */ + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) { + levl[x][y].seenv = SVALL; + levl[x][y].waslit = TRUE; + map_background(x, y, 1); + for (obj = level.objects[x][y]; obj; obj = obj->nexthere) + if (obj->otyp == BOULDER) + map_object(obj, 1); + } + + /* Map the traps */ + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + ttmp->tseen = 1; + map_trap(ttmp, 1); + } +} + + +/*detect.c*/ diff --git a/src/dig.c b/src/dig.c new file mode 100644 index 0000000..4c40a59 --- /dev/null +++ b/src/dig.c @@ -0,0 +1,1571 @@ +/* SCCS Id: @(#)dig.c 3.4 2003/03/23 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "edog.h" +/* #define DEBUG */ /* turn on for diagnostics */ + +#ifdef OVLB + +static NEARDATA boolean did_dig_msg; + +STATIC_DCL boolean NDECL(rm_waslit); +STATIC_DCL void FDECL(mkcavepos, (XCHAR_P,XCHAR_P,int,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL void FDECL(mkcavearea, (BOOLEAN_P)); +STATIC_DCL int FDECL(dig_typ, (struct obj *,XCHAR_P,XCHAR_P)); +STATIC_DCL int NDECL(dig); +STATIC_DCL schar FDECL(fillholetyp, (int, int)); +STATIC_DCL void NDECL(dig_up_grave); + +/* Indices returned by dig_typ() */ +#define DIGTYP_UNDIGGABLE 0 +#define DIGTYP_ROCK 1 +#define DIGTYP_STATUE 2 +#define DIGTYP_BOULDER 3 +#define DIGTYP_DOOR 4 +#define DIGTYP_TREE 5 + + +STATIC_OVL boolean +rm_waslit() +{ + register xchar x, y; + + if(levl[u.ux][u.uy].typ == ROOM && levl[u.ux][u.uy].waslit) + return(TRUE); + for(x = u.ux-2; x < u.ux+3; x++) + for(y = u.uy-1; y < u.uy+2; y++) + if(isok(x,y) && levl[x][y].waslit) return(TRUE); + return(FALSE); +} + +/* Change level topology. Messes with vision tables and ignores things like + * boulders in the name of a nice effect. Vision will get fixed up again + * immediately after the effect is complete. + */ +STATIC_OVL void +mkcavepos(x, y, dist, waslit, rockit) + xchar x,y; + int dist; + boolean waslit, rockit; +{ + register struct rm *lev; + + if(!isok(x,y)) return; + lev = &levl[x][y]; + + if(rockit) { + register struct monst *mtmp; + + if(IS_ROCK(lev->typ)) return; + if(t_at(x, y)) return; /* don't cover the portal */ + if ((mtmp = m_at(x, y)) != 0) /* make sure crucial monsters survive */ + if(!passes_walls(mtmp->data)) (void) rloc(mtmp, FALSE); + } else if(lev->typ == ROOM) return; + + unblock_point(x,y); /* make sure vision knows this location is open */ + + /* fake out saved state */ + lev->seenv = 0; + lev->doormask = 0; + if(dist < 3) lev->lit = (rockit ? FALSE : TRUE); + if(waslit) lev->waslit = (rockit ? FALSE : TRUE); + lev->horizontal = FALSE; + viz_array[y][x] = (dist < 3 ) ? + (IN_SIGHT|COULD_SEE) : /* short-circuit vision recalc */ + COULD_SEE; + lev->typ = (rockit ? STONE : ROOM); + if(dist >= 3) + impossible("mkcavepos called with dist %d", dist); + if(Blind) + feel_location(x, y); + else newsym(x,y); +} + +STATIC_OVL void +mkcavearea(rockit) +register boolean rockit; +{ + int dist; + xchar xmin = u.ux, xmax = u.ux; + xchar ymin = u.uy, ymax = u.uy; + register xchar i; + register boolean waslit = rm_waslit(); + + if(rockit) pline("Crash! The ceiling collapses around you!"); + else pline("A mysterious force %s cave around you!", + (levl[u.ux][u.uy].typ == CORR) ? "creates a" : "extends the"); + display_nhwindow(WIN_MESSAGE, TRUE); + + for(dist = 1; dist <= 2; dist++) { + xmin--; xmax++; + + /* top and bottom */ + if(dist < 2) { /* the area is wider that it is high */ + ymin--; ymax++; + for(i = xmin+1; i < xmax; i++) { + mkcavepos(i, ymin, dist, waslit, rockit); + mkcavepos(i, ymax, dist, waslit, rockit); + } + } + + /* left and right */ + for(i = ymin; i <= ymax; i++) { + mkcavepos(xmin, i, dist, waslit, rockit); + mkcavepos(xmax, i, dist, waslit, rockit); + } + + flush_screen(1); /* make sure the new glyphs shows up */ + delay_output(); + } + + if(!rockit && levl[u.ux][u.uy].typ == CORR) { + levl[u.ux][u.uy].typ = ROOM; + if(waslit) levl[u.ux][u.uy].waslit = TRUE; + newsym(u.ux, u.uy); /* in case player is invisible */ + } + + vision_full_recalc = 1; /* everything changed */ +} + +/* When digging into location , what are you actually digging into? */ +STATIC_OVL int +dig_typ(otmp, x, y) +struct obj *otmp; +xchar x, y; +{ + boolean ispick = is_pick(otmp); + + return (ispick && sobj_at(STATUE, x, y) ? DIGTYP_STATUE : + ispick && sobj_at(BOULDER, x, y) ? DIGTYP_BOULDER : + closed_door(x, y) ? DIGTYP_DOOR : + IS_TREE(levl[x][y].typ) ? + (ispick ? DIGTYP_UNDIGGABLE : DIGTYP_TREE) : + ispick && IS_ROCK(levl[x][y].typ) && + (!level.flags.arboreal || IS_WALL(levl[x][y].typ)) ? + DIGTYP_ROCK : DIGTYP_UNDIGGABLE); +} + +boolean +is_digging() +{ + if (occupation == dig) { + return TRUE; + } + return FALSE; +} + +#define BY_YOU (&youmonst) +#define BY_OBJECT ((struct monst *)0) + +boolean +dig_check(madeby, verbose, x, y) + struct monst *madeby; + boolean verbose; + int x, y; +{ + struct trap *ttmp = t_at(x, y); + const char *verb = (madeby == BY_YOU && uwep && is_axe(uwep)) ? "chop" : "dig in"; + + if (On_stairs(x, y)) { + if (x == xdnladder || x == xupladder) { + if(verbose) pline_The("ladder resists your effort."); + } else if(verbose) pline_The("stairs are too hard to %s.", verb); + return(FALSE); + } else if (IS_THRONE(levl[x][y].typ) && madeby != BY_OBJECT) { + if(verbose) pline_The("throne is too hard to break apart."); + return(FALSE); + } else if (IS_ALTAR(levl[x][y].typ) && (madeby != BY_OBJECT || + Is_astralevel(&u.uz) || Is_sanctum(&u.uz))) { + if(verbose) pline_The("altar is too hard to break apart."); + return(FALSE); + } else if (Is_airlevel(&u.uz)) { + if(verbose) You("cannot %s thin air.", verb); + return(FALSE); + } else if (Is_waterlevel(&u.uz)) { + if(verbose) pline_The("water splashes and subsides."); + return(FALSE); + } else if ((IS_ROCK(levl[x][y].typ) && levl[x][y].typ != SDOOR && + (levl[x][y].wall_info & W_NONDIGGABLE) != 0) + || (ttmp && + (ttmp->ttyp == MAGIC_PORTAL || !Can_dig_down(&u.uz)))) { + if(verbose) pline_The("%s here is too hard to %s.", + surface(x,y), verb); + return(FALSE); + } else if (sobj_at(BOULDER, x, y)) { + if(verbose) There("isn't enough room to %s here.", verb); + return(FALSE); + } else if (madeby == BY_OBJECT && + /* the block against existing traps is mainly to + prevent broken wands from turning holes into pits */ + (ttmp || is_pool(x,y) || is_lava(x,y))) { + /* digging by player handles pools separately */ + return FALSE; + } + return(TRUE); +} + +STATIC_OVL int +dig() +{ + register struct rm *lev; + register xchar dpx = digging.pos.x, dpy = digging.pos.y; + register boolean ispick = uwep && is_pick(uwep); + const char *verb = + (!uwep || is_pick(uwep)) ? "dig into" : "chop through"; + + lev = &levl[dpx][dpy]; + /* perhaps a nymph stole your pick-axe while you were busy digging */ + /* or perhaps you teleported away */ + if (u.uswallow || !uwep || (!ispick && !is_axe(uwep)) || + !on_level(&digging.level, &u.uz) || + ((digging.down ? (dpx != u.ux || dpy != u.uy) + : (distu(dpx,dpy) > 2)))) + return(0); + + if (digging.down) { + if(!dig_check(BY_YOU, TRUE, u.ux, u.uy)) return(0); + } else { /* !digging.down */ + if (IS_TREE(lev->typ) && !may_dig(dpx,dpy) && + dig_typ(uwep, dpx, dpy) == DIGTYP_TREE) { + pline("This tree seems to be petrified."); + return(0); + } + if (IS_ROCK(lev->typ) && !may_dig(dpx,dpy) && + dig_typ(uwep, dpx, dpy) == DIGTYP_ROCK) { + pline("This wall is too hard to %s.", verb); + return(0); + } + } + if(Fumbling && !rn2(3)) { + switch(rn2(3)) { + case 0: + if(!welded(uwep)) { + You("fumble and drop your %s.", xname(uwep)); + dropx(uwep); + } else { +#ifdef STEED + if (u.usteed) + Your("%s %s and %s %s!", + xname(uwep), + otense(uwep, "bounce"), otense(uwep, "hit"), + mon_nam(u.usteed)); + else +#endif + pline("Ouch! Your %s %s and %s you!", + xname(uwep), + otense(uwep, "bounce"), otense(uwep, "hit")); + set_wounded_legs(RIGHT_SIDE, 5 + rnd(5)); + } + break; + case 1: + pline("Bang! You hit with the broad side of %s!", + the(xname(uwep))); + break; + default: Your("swing misses its mark."); + break; + } + return(0); + } + + digging.effort += 10 + rn2(5) + abon() + + uwep->spe - greatest_erosion(uwep) + u.udaminc; + if (Race_if(PM_DWARF)) + digging.effort *= 2; + if (digging.down) { + register struct trap *ttmp; + + if (digging.effort > 250) { + (void) dighole(FALSE); + (void) memset((genericptr_t)&digging, 0, sizeof digging); + return(0); /* done with digging */ + } + + if (digging.effort <= 50 || + ((ttmp = t_at(dpx,dpy)) != 0 && + (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT || + ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE))) + return(1); + + if (IS_ALTAR(lev->typ)) { + altar_wrath(dpx, dpy); + angry_priest(); + } + + if (dighole(TRUE)) { /* make pit at */ + digging.level.dnum = 0; + digging.level.dlevel = -1; + } + return(0); + } + + if (digging.effort > 100) { + register const char *digtxt, *dmgtxt = (const char*) 0; + register struct obj *obj; + register boolean shopedge = *in_rooms(dpx, dpy, SHOPBASE); + + if ((obj = sobj_at(STATUE, dpx, dpy)) != 0) { + if (break_statue(obj)) + digtxt = "The statue shatters."; + else + /* it was a statue trap; break_statue() + * printed a message and updated the screen + */ + digtxt = (char *)0; + } else if ((obj = sobj_at(BOULDER, dpx, dpy)) != 0) { + struct obj *bobj; + + fracture_rock(obj); + if ((bobj = sobj_at(BOULDER, dpx, dpy)) != 0) { + /* another boulder here, restack it to the top */ + obj_extract_self(bobj); + place_object(bobj, dpx, dpy); + } + digtxt = "The boulder falls apart."; + } else if (lev->typ == STONE || lev->typ == SCORR || + IS_TREE(lev->typ)) { + if(Is_earthlevel(&u.uz)) { + if(uwep->blessed && !rn2(3)) { + mkcavearea(FALSE); + goto cleanup; + } else if((uwep->cursed && !rn2(4)) || + (!uwep->blessed && !rn2(6))) { + mkcavearea(TRUE); + goto cleanup; + } + } + if (IS_TREE(lev->typ)) { + digtxt = "You cut down the tree."; + lev->typ = ROOM; + if (!rn2(5)) (void) rnd_treefruit_at(dpx, dpy); + } else { + digtxt = "You succeed in cutting away some rock."; + lev->typ = CORR; + } + } else if(IS_WALL(lev->typ)) { + if(shopedge) { + add_damage(dpx, dpy, 10L * ACURRSTR); + dmgtxt = "damage"; + } + if (level.flags.is_maze_lev) { + lev->typ = ROOM; + } else if (level.flags.is_cavernous_lev && + !in_town(dpx, dpy)) { + lev->typ = CORR; + } else { + lev->typ = DOOR; + lev->doormask = D_NODOOR; + } + digtxt = "You make an opening in the wall."; + } else if(lev->typ == SDOOR) { + cvt_sdoor_to_door(lev); /* ->typ = DOOR */ + digtxt = "You break through a secret door!"; + if(!(lev->doormask & D_TRAPPED)) + lev->doormask = D_BROKEN; + } else if(closed_door(dpx, dpy)) { + digtxt = "You break through the door."; + if(shopedge) { + add_damage(dpx, dpy, 400L); + dmgtxt = "break"; + } + if(!(lev->doormask & D_TRAPPED)) + lev->doormask = D_BROKEN; + } else return(0); /* statue or boulder got taken */ + + if(!does_block(dpx,dpy,&levl[dpx][dpy])) + unblock_point(dpx,dpy); /* vision: can see through */ + if(Blind) + feel_location(dpx, dpy); + else + newsym(dpx, dpy); + if(digtxt && !digging.quiet) pline(digtxt); /* after newsym */ + if(dmgtxt) + pay_for_damage(dmgtxt, FALSE); + + if(Is_earthlevel(&u.uz) && !rn2(3)) { + register struct monst *mtmp; + + switch(rn2(2)) { + case 0: + mtmp = makemon(&mons[PM_EARTH_ELEMENTAL], + dpx, dpy, NO_MM_FLAGS); + break; + default: + mtmp = makemon(&mons[PM_XORN], + dpx, dpy, NO_MM_FLAGS); + break; + } + if(mtmp) pline_The("debris from your digging comes to life!"); + } + if(IS_DOOR(lev->typ) && (lev->doormask & D_TRAPPED)) { + lev->doormask = D_NODOOR; + b_trapped("door", 0); + newsym(dpx, dpy); + } +cleanup: + digging.lastdigtime = moves; + digging.quiet = FALSE; + digging.level.dnum = 0; + digging.level.dlevel = -1; + return(0); + } else { /* not enough effort has been spent yet */ + static const char *const d_target[6] = { + "", "rock", "statue", "boulder", "door", "tree" + }; + int dig_target = dig_typ(uwep, dpx, dpy); + + if (IS_WALL(lev->typ) || dig_target == DIGTYP_DOOR) { + if(*in_rooms(dpx, dpy, SHOPBASE)) { + pline("This %s seems too hard to %s.", + IS_DOOR(lev->typ) ? "door" : "wall", verb); + return(0); + } + } else if (!IS_ROCK(lev->typ) && dig_target == DIGTYP_ROCK) + return(0); /* statue or boulder got taken */ + if(!did_dig_msg) { + You("hit the %s with all your might.", + d_target[dig_target]); + did_dig_msg = TRUE; + } + } + return(1); +} + +/* When will hole be finished? Very rough indication used by shopkeeper. */ +int +holetime() +{ + if(occupation != dig || !*u.ushops) return(-1); + return ((250 - digging.effort) / 20); +} + +/* Return typ of liquid to fill a hole with, or ROOM, if no liquid nearby */ +STATIC_OVL +schar +fillholetyp(x,y) +int x, y; +{ + register int x1, y1; + int lo_x = max(1,x-1), hi_x = min(x+1,COLNO-1), + lo_y = max(0,y-1), hi_y = min(y+1,ROWNO-1); + int pool_cnt = 0, moat_cnt = 0, lava_cnt = 0; + + for (x1 = lo_x; x1 <= hi_x; x1++) + for (y1 = lo_y; y1 <= hi_y; y1++) + if (levl[x1][y1].typ == POOL) + pool_cnt++; + else if (levl[x1][y1].typ == MOAT || + (levl[x1][y1].typ == DRAWBRIDGE_UP && + (levl[x1][y1].drawbridgemask & DB_UNDER) == DB_MOAT)) + moat_cnt++; + else if (levl[x1][y1].typ == LAVAPOOL || + (levl[x1][y1].typ == DRAWBRIDGE_UP && + (levl[x1][y1].drawbridgemask & DB_UNDER) == DB_LAVA)) + lava_cnt++; + pool_cnt /= 3; /* not as much liquid as the others */ + + if (lava_cnt > moat_cnt + pool_cnt && rn2(lava_cnt + 1)) + return LAVAPOOL; + else if (moat_cnt > 0 && rn2(moat_cnt + 1)) + return MOAT; + else if (pool_cnt > 0 && rn2(pool_cnt + 1)) + return POOL; + else + return ROOM; +} + +void +digactualhole(x, y, madeby, ttyp) +register int x, y; +struct monst *madeby; +int ttyp; +{ + struct obj *oldobjs, *newobjs; + register struct trap *ttmp; + char surface_type[BUFSZ]; + struct rm *lev = &levl[x][y]; + boolean shopdoor; + struct monst *mtmp = m_at(x, y); /* may be madeby */ + boolean madeby_u = (madeby == BY_YOU); + boolean madeby_obj = (madeby == BY_OBJECT); + boolean at_u = (x == u.ux) && (y == u.uy); + boolean wont_fall = Levitation || Flying; + + if (u.utrap && u.utraptype == TT_INFLOOR) u.utrap = 0; + + /* these furniture checks were in dighole(), but wand + breaking bypasses that routine and calls us directly */ + if (IS_FOUNTAIN(lev->typ)) { + dogushforth(FALSE); + SET_FOUNTAIN_WARNED(x,y); /* force dryup */ + dryup(x, y, madeby_u); + return; +#ifdef SINKS + } else if (IS_SINK(lev->typ)) { + breaksink(x, y); + return; +#endif + } else if (lev->typ == DRAWBRIDGE_DOWN || + (is_drawbridge_wall(x, y) >= 0)) { + int bx = x, by = y; + /* if under the portcullis, the bridge is adjacent */ + (void) find_drawbridge(&bx, &by); + destroy_drawbridge(bx, by); + return; + } + + if (ttyp != PIT && !Can_dig_down(&u.uz)) { + impossible("digactualhole: can't dig %s on this level.", + defsyms[trap_to_defsym(ttyp)].explanation); + ttyp = PIT; + } + + /* maketrap() might change it, also, in this situation, + surface() returns an inappropriate string for a grave */ + if (IS_GRAVE(lev->typ)) + Strcpy(surface_type, "grave"); + else + Strcpy(surface_type, surface(x,y)); + shopdoor = IS_DOOR(lev->typ) && *in_rooms(x, y, SHOPBASE); + oldobjs = level.objects[x][y]; + ttmp = maketrap(x, y, ttyp); + if (!ttmp) return; + newobjs = level.objects[x][y]; + ttmp->tseen = (madeby_u || cansee(x,y)); + ttmp->madeby_u = madeby_u; + newsym(ttmp->tx,ttmp->ty); + + if (ttyp == PIT) { + + if(madeby_u) { + You("dig a pit in the %s.", surface_type); + if (shopdoor) pay_for_damage("ruin", FALSE); + } else if (!madeby_obj && canseemon(madeby)) + pline("%s digs a pit in the %s.", Monnam(madeby), surface_type); + else if (cansee(x, y) && flags.verbose) + pline("A pit appears in the %s.", surface_type); + + if(at_u) { + if (!wont_fall) { + if (!Passes_walls) + u.utrap = rn1(4,2); + u.utraptype = TT_PIT; + vision_full_recalc = 1; /* vision limits change */ + } else + u.utrap = 0; + if (oldobjs != newobjs) /* something unearthed */ + (void) pickup(1); /* detects pit */ + } else if(mtmp) { + if(is_flyer(mtmp->data) || is_floater(mtmp->data)) { + if(canseemon(mtmp)) + pline("%s %s over the pit.", Monnam(mtmp), + (is_flyer(mtmp->data)) ? + "flies" : "floats"); + } else if(mtmp != madeby) + (void) mintrap(mtmp); + } + } else { /* was TRAPDOOR now a HOLE*/ + + if(madeby_u) + You("dig a hole through the %s.", surface_type); + else if(!madeby_obj && canseemon(madeby)) + pline("%s digs a hole through the %s.", + Monnam(madeby), surface_type); + else if(cansee(x, y) && flags.verbose) + pline("A hole appears in the %s.", surface_type); + + if (at_u) { + if (!u.ustuck && !wont_fall && !next_to_u()) { + You("are jerked back by your pet!"); + wont_fall = TRUE; + } + + /* Floor objects get a chance of falling down. The case where + * the hero does NOT fall down is treated here. The case + * where the hero does fall down is treated in goto_level(). + */ + if (u.ustuck || wont_fall) { + if (newobjs) + impact_drop((struct obj *)0, x, y, 0); + if (oldobjs != newobjs) + (void) pickup(1); + if (shopdoor && madeby_u) pay_for_damage("ruin", FALSE); + + } else { + d_level newlevel; + + if (*u.ushops && madeby_u) + shopdig(1); /* shk might snatch pack */ + /* handle earlier damage, eg breaking wand of digging */ + else if (!madeby_u) pay_for_damage("dig into", TRUE); + + You("fall through..."); + /* Earlier checks must ensure that the destination + * level exists and is in the present dungeon. + */ + newlevel.dnum = u.uz.dnum; + newlevel.dlevel = u.uz.dlevel + 1; + goto_level(&newlevel, FALSE, TRUE, FALSE); + /* messages for arriving in special rooms */ + spoteffects(FALSE); + } + } else { + if (shopdoor && madeby_u) pay_for_damage("ruin", FALSE); + if (newobjs) + impact_drop((struct obj *)0, x, y, 0); + if (mtmp) { + /*[don't we need special sokoban handling here?]*/ + if (is_flyer(mtmp->data) || is_floater(mtmp->data) || + mtmp->data == &mons[PM_WUMPUS] || + (mtmp->wormno && count_wsegs(mtmp) > 5) || + mtmp->data->msize >= MZ_HUGE) return; + if (mtmp == u.ustuck) /* probably a vortex */ + return; /* temporary? kludge */ + + if (teleport_pet(mtmp, FALSE)) { + d_level tolevel; + + if (Is_stronghold(&u.uz)) { + assign_level(&tolevel, &valley_level); + } else if (Is_botlevel(&u.uz)) { + if (canseemon(mtmp)) + pline("%s avoids the trap.", Monnam(mtmp)); + return; + } else { + get_level(&tolevel, depth(&u.uz) + 1); + } + if (mtmp->isshk) make_angry_shk(mtmp, 0, 0); + migrate_to_level(mtmp, ledger_no(&tolevel), + MIGR_RANDOM, (coord *)0); + } + } + } + } +} + +/* return TRUE if digging succeeded, FALSE otherwise */ +boolean +dighole(pit_only) +boolean pit_only; +{ + register struct trap *ttmp = t_at(u.ux, u.uy); + struct rm *lev = &levl[u.ux][u.uy]; + struct obj *boulder_here; + schar typ; + boolean nohole = !Can_dig_down(&u.uz); + + if ((ttmp && (ttmp->ttyp == MAGIC_PORTAL || nohole)) || + (IS_ROCK(lev->typ) && lev->typ != SDOOR && + (lev->wall_info & W_NONDIGGABLE) != 0)) { + pline_The("%s here is too hard to dig in.", surface(u.ux,u.uy)); + + } else if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) { + pline_The("%s sloshes furiously for a moment, then subsides.", + is_lava(u.ux, u.uy) ? "lava" : "water"); + wake_nearby(); /* splashing */ + + } else if (lev->typ == DRAWBRIDGE_DOWN || + (is_drawbridge_wall(u.ux, u.uy) >= 0)) { + /* drawbridge_down is the platform crossing the moat when the + bridge is extended; drawbridge_wall is the open "doorway" or + closed "door" where the portcullis/mechanism is located */ + if (pit_only) { + pline_The("drawbridge seems too hard to dig through."); + return FALSE; + } else { + int x = u.ux, y = u.uy; + /* if under the portcullis, the bridge is adjacent */ + (void) find_drawbridge(&x, &y); + destroy_drawbridge(x, y); + return TRUE; + } + + } else if ((boulder_here = sobj_at(BOULDER, u.ux, u.uy)) != 0) { + if (ttmp && (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT) && + rn2(2)) { + pline_The("boulder settles into the pit."); + ttmp->ttyp = PIT; /* crush spikes */ + } else { + /* + * digging makes a hole, but the boulder immediately + * fills it. Final outcome: no hole, no boulder. + */ + pline("KADOOM! The boulder falls in!"); + (void) delfloortrap(ttmp); + } + delobj(boulder_here); + return TRUE; + + } else if (IS_GRAVE(lev->typ)) { + digactualhole(u.ux, u.uy, BY_YOU, PIT); + dig_up_grave(); + return TRUE; + } else if (lev->typ == DRAWBRIDGE_UP) { + /* must be floor or ice, other cases handled above */ + /* dig "pit" and let fluid flow in (if possible) */ + typ = fillholetyp(u.ux,u.uy); + + if (typ == ROOM) { + /* + * We can't dig a hole here since that will destroy + * the drawbridge. The following is a cop-out. --dlc + */ + pline_The("%s here is too hard to dig in.", + surface(u.ux, u.uy)); + return FALSE; + } + + lev->drawbridgemask &= ~DB_UNDER; + lev->drawbridgemask |= (typ == LAVAPOOL) ? DB_LAVA : DB_MOAT; + + liquid_flow: + if (ttmp) (void) delfloortrap(ttmp); + /* if any objects were frozen here, they're released now */ + unearth_objs(u.ux, u.uy); + + pline("As you dig, the hole fills with %s!", + typ == LAVAPOOL ? "lava" : "water"); + if (!Levitation && !Flying) { + if (typ == LAVAPOOL) + (void) lava_effects(); + else if (!Wwalking) + (void) drown(); + } + return TRUE; + + /* the following two are here for the wand of digging */ + } else if (IS_THRONE(lev->typ)) { + pline_The("throne is too hard to break apart."); + + } else if (IS_ALTAR(lev->typ)) { + pline_The("altar is too hard to break apart."); + + } else { + typ = fillholetyp(u.ux,u.uy); + + if (typ != ROOM) { + lev->typ = typ; + goto liquid_flow; + } + + /* finally we get to make a hole */ + if (nohole || pit_only) + digactualhole(u.ux, u.uy, BY_YOU, PIT); + else + digactualhole(u.ux, u.uy, BY_YOU, HOLE); + + return TRUE; + } + + return FALSE; +} + +STATIC_OVL void +dig_up_grave() +{ + struct obj *otmp; + + /* Grave-robbing is frowned upon... */ + exercise(A_WIS, FALSE); + if (Role_if(PM_ARCHEOLOGIST)) { + adjalign(-sgn(u.ualign.type)*3); + You_feel("like a despicable grave-robber!"); + } else if (Role_if(PM_SAMURAI)) { + adjalign(-sgn(u.ualign.type)); + You("disturb the honorable dead!"); + } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) { + adjalign(-sgn(u.ualign.type)); + You("have violated the sanctity of this grave!"); + } + + switch (rn2(5)) { + case 0: + case 1: + You("unearth a corpse."); + if (!!(otmp = mk_tt_object(CORPSE, u.ux, u.uy))) + otmp->age -= 100; /* this is an *OLD* corpse */; + break; + case 2: + if (!Blind) pline(Hallucination ? "Dude! The living dead!" : + "The grave's owner is very upset!"); + (void) makemon(mkclass(S_ZOMBIE,0), u.ux, u.uy, NO_MM_FLAGS); + break; + case 3: + if (!Blind) pline(Hallucination ? "I want my mummy!" : + "You've disturbed a tomb!"); + (void) makemon(mkclass(S_MUMMY,0), u.ux, u.uy, NO_MM_FLAGS); + break; + default: + /* No corpse */ + pline_The("grave seems unused. Strange...."); + break; + } + levl[u.ux][u.uy].typ = ROOM; + del_engr_at(u.ux, u.uy); + newsym(u.ux,u.uy); + return; +} + +int +use_pick_axe(obj) +struct obj *obj; +{ + boolean ispick; + char dirsyms[12]; + char qbuf[QBUFSZ]; + register char *dsp = dirsyms; + register int rx, ry; + int res = 0; + register const char *sdp, *verb; + + if(iflags.num_pad) sdp = ndir; else sdp = sdir; /* DICE workaround */ + + /* Check tool */ + if (obj != uwep) { + if (!wield_tool(obj, "swing")) return 0; + else res = 1; + } + ispick = is_pick(obj); + verb = ispick ? "dig" : "chop"; + + if (u.utrap && u.utraptype == TT_WEB) { + pline("%s you can't %s while entangled in a web.", + /* res==0 => no prior message; + res==1 => just got "You now wield a pick-axe." message */ + !res ? "Unfortunately," : "But", verb); + return res; + } + + while(*sdp) { + (void) movecmd(*sdp); /* sets u.dx and u.dy and u.dz */ + rx = u.ux + u.dx; + ry = u.uy + u.dy; + /* Include down even with axe, so we have at least one direction */ + if (u.dz > 0 || + (u.dz == 0 && isok(rx, ry) && + dig_typ(obj, rx, ry) != DIGTYP_UNDIGGABLE)) + *dsp++ = *sdp; + sdp++; + } + *dsp = 0; + Sprintf(qbuf, "In what direction do you want to %s? [%s]", verb, dirsyms); + if(!getdir(qbuf)) + return(res); + + return(use_pick_axe2(obj)); +} + +/* MRKR: use_pick_axe() is split in two to allow autodig to bypass */ +/* the "In what direction do you want to dig?" query. */ +/* use_pick_axe2() uses the existing u.dx, u.dy and u.dz */ + +int +use_pick_axe2(obj) +struct obj *obj; +{ + register int rx, ry; + register struct rm *lev; + int dig_target; + boolean ispick = is_pick(obj); + const char *verbing = ispick ? "digging" : "chopping"; + + if (u.uswallow && attack(u.ustuck)) { + ; /* return(1) */ + } else if (Underwater) { + pline("Turbulence torpedoes your %s attempts.", verbing); + } else if(u.dz < 0) { + if(Levitation) + You("don't have enough leverage."); + else + You_cant("reach the %s.",ceiling(u.ux,u.uy)); + } else if(!u.dx && !u.dy && !u.dz) { + char buf[BUFSZ]; + int dam; + + dam = rnd(2) + dbon() + obj->spe; + if (dam <= 0) dam = 1; + You("hit yourself with %s.", yname(uwep)); + Sprintf(buf, "%s own %s", uhis(), + OBJ_NAME(objects[obj->otyp])); + losehp(dam, buf, KILLED_BY); + flags.botl=1; + return(1); + } else if(u.dz == 0) { + if(Stunned || (Confusion && !rn2(5))) confdir(); + rx = u.ux + u.dx; + ry = u.uy + u.dy; + if(!isok(rx, ry)) { + pline("Clash!"); + return(1); + } + lev = &levl[rx][ry]; + if(MON_AT(rx, ry) && attack(m_at(rx, ry))) + return(1); + dig_target = dig_typ(obj, rx, ry); + if (dig_target == DIGTYP_UNDIGGABLE) { + /* ACCESSIBLE or POOL */ + struct trap *trap = t_at(rx, ry); + + if (trap && trap->ttyp == WEB) { + if (!trap->tseen) { + seetrap(trap); + There("is a spider web there!"); + } + Your("%s entangled in the web.", + aobjnam(obj, "become")); + /* you ought to be able to let go; tough luck */ + /* (maybe `move_into_trap()' would be better) */ + nomul(-d(2,2)); + nomovemsg = "You pull free."; + } else if (lev->typ == IRONBARS) { + pline("Clang!"); + wake_nearby(); + } else if (IS_TREE(lev->typ)) + You("need an axe to cut down a tree."); + else if (IS_ROCK(lev->typ)) + You("need a pick to dig rock."); + else if (!ispick && (sobj_at(STATUE, rx, ry) || + sobj_at(BOULDER, rx, ry))) { + boolean vibrate = !rn2(3); + pline("Sparks fly as you whack the %s.%s", + sobj_at(STATUE, rx, ry) ? "statue" : "boulder", + vibrate ? " The axe-handle vibrates violently!" : ""); + if (vibrate) losehp(2, "axing a hard object", KILLED_BY); + } + else + You("swing your %s through thin air.", + aobjnam(obj, (char *)0)); + } else { + static const char * const d_action[6] = { + "swinging", + "digging", + "chipping the statue", + "hitting the boulder", + "chopping at the door", + "cutting the tree" + }; + did_dig_msg = FALSE; + digging.quiet = FALSE; + if (digging.pos.x != rx || digging.pos.y != ry || + !on_level(&digging.level, &u.uz) || digging.down) { + if (flags.autodig && + dig_target == DIGTYP_ROCK && !digging.down && + digging.pos.x == u.ux && + digging.pos.y == u.uy && + (moves <= digging.lastdigtime+2 && + moves >= digging.lastdigtime)) { + /* avoid messages if repeated autodigging */ + did_dig_msg = TRUE; + digging.quiet = TRUE; + } + digging.down = digging.chew = FALSE; + digging.warned = FALSE; + digging.pos.x = rx; + digging.pos.y = ry; + assign_level(&digging.level, &u.uz); + digging.effort = 0; + if (!digging.quiet) + You("start %s.", d_action[dig_target]); + } else { + You("%s %s.", digging.chew ? "begin" : "continue", + d_action[dig_target]); + digging.chew = FALSE; + } + set_occupation(dig, verbing, 0); + } + } else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) { + /* it must be air -- water checked above */ + You("swing your %s through thin air.", aobjnam(obj, (char *)0)); + } else if (!can_reach_floor()) { + You_cant("reach the %s.", surface(u.ux,u.uy)); + } else if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) { + /* Monsters which swim also happen not to be able to dig */ + You("cannot stay under%s long enough.", + is_pool(u.ux, u.uy) ? "water" : " the lava"); + } else if (!ispick) { + Your("%s merely scratches the %s.", + aobjnam(obj, (char *)0), surface(u.ux,u.uy)); + u_wipe_engr(3); + } else { + if (digging.pos.x != u.ux || digging.pos.y != u.uy || + !on_level(&digging.level, &u.uz) || !digging.down) { + digging.chew = FALSE; + digging.down = TRUE; + digging.warned = FALSE; + digging.pos.x = u.ux; + digging.pos.y = u.uy; + assign_level(&digging.level, &u.uz); + digging.effort = 0; + You("start %s downward.", verbing); + if (*u.ushops) shopdig(0); + } else + You("continue %s downward.", verbing); + did_dig_msg = FALSE; + set_occupation(dig, verbing, 0); + } + return(1); +} + +/* + * Town Watchmen frown on damage to the town walls, trees or fountains. + * It's OK to dig holes in the ground, however. + * If mtmp is assumed to be a watchman, a watchman is found if mtmp == 0 + * zap == TRUE if wand/spell of digging, FALSE otherwise (chewing) + */ +void +watch_dig(mtmp, x, y, zap) + struct monst *mtmp; + xchar x, y; + boolean zap; +{ + struct rm *lev = &levl[x][y]; + + if (in_town(x, y) && + (closed_door(x, y) || lev->typ == SDOOR || + IS_WALL(lev->typ) || IS_FOUNTAIN(lev->typ) || IS_TREE(lev->typ))) { + if (!mtmp) { + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + mtmp->mcansee && m_canseeu(mtmp) && + couldsee(mtmp->mx, mtmp->my) && mtmp->mpeaceful) + break; + } + } + + if (mtmp) { + if(zap || digging.warned) { + verbalize("Halt, vandal! You're under arrest!"); + (void) angry_guards(!(flags.soundok)); + } else { + const char *str; + + if (IS_DOOR(lev->typ)) + str = "door"; + else if (IS_TREE(lev->typ)) + str = "tree"; + else if (IS_ROCK(lev->typ)) + str = "wall"; + else + str = "fountain"; + verbalize("Hey, stop damaging that %s!", str); + digging.warned = TRUE; + } + if (is_digging()) + stop_occupation(); + } + } +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* Return TRUE if monster died, FALSE otherwise. Called from m_move(). */ +boolean +mdig_tunnel(mtmp) +register struct monst *mtmp; +{ + register struct rm *here; + int pile = rnd(12); + + here = &levl[mtmp->mx][mtmp->my]; + if (here->typ == SDOOR) + cvt_sdoor_to_door(here); /* ->typ = DOOR */ + + /* Eats away door if present & closed or locked */ + if (closed_door(mtmp->mx, mtmp->my)) { + if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) + add_damage(mtmp->mx, mtmp->my, 0L); + unblock_point(mtmp->mx, mtmp->my); /* vision */ + if (here->doormask & D_TRAPPED) { + here->doormask = D_NODOOR; + if (mb_trapped(mtmp)) { /* mtmp is killed */ + newsym(mtmp->mx, mtmp->my); + return TRUE; + } + } else { + if (!rn2(3) && flags.verbose) /* not too often.. */ + You_feel("an unexpected draft."); + here->doormask = D_BROKEN; + } + newsym(mtmp->mx, mtmp->my); + return FALSE; + } else if (!IS_ROCK(here->typ) && !IS_TREE(here->typ)) /* no dig */ + return FALSE; + + /* Only rock, trees, and walls fall through to this point. */ + if ((here->wall_info & W_NONDIGGABLE) != 0) { + impossible("mdig_tunnel: %s at (%d,%d) is undiggable", + (IS_WALL(here->typ) ? "wall" : "stone"), + (int) mtmp->mx, (int) mtmp->my); + return FALSE; /* still alive */ + } + + if (IS_WALL(here->typ)) { + /* KMH -- Okay on arboreal levels (room walls are still stone) */ + if (flags.soundok && flags.verbose && !rn2(5)) + You_hear("crashing rock."); + if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) + add_damage(mtmp->mx, mtmp->my, 0L); + if (level.flags.is_maze_lev) { + here->typ = ROOM; + } else if (level.flags.is_cavernous_lev && + !in_town(mtmp->mx, mtmp->my)) { + here->typ = CORR; + } else { + here->typ = DOOR; + here->doormask = D_NODOOR; + } + } else if (IS_TREE(here->typ)) { + here->typ = ROOM; + if (pile && pile < 5) + (void) rnd_treefruit_at(mtmp->mx, mtmp->my); + } else { + here->typ = CORR; + if (pile && pile < 5) + (void) mksobj_at((pile == 1) ? BOULDER : ROCK, + mtmp->mx, mtmp->my, TRUE, FALSE); + } + newsym(mtmp->mx, mtmp->my); + if (!sobj_at(BOULDER, mtmp->mx, mtmp->my)) + unblock_point(mtmp->mx, mtmp->my); /* vision */ + + return FALSE; +} + +#endif /* OVL0 */ +#ifdef OVL3 + +/* digging via wand zap or spell cast */ +void +zap_dig() +{ + struct rm *room; + struct monst *mtmp; + struct obj *otmp; + int zx, zy, digdepth; + boolean shopdoor, shopwall, maze_dig; + /* + * Original effect (approximately): + * from CORR: dig until we pierce a wall + * from ROOM: pierce wall and dig until we reach + * an ACCESSIBLE place. + * Currently: dig for digdepth positions; + * also down on request of Lennart Augustsson. + */ + + if (u.uswallow) { + mtmp = u.ustuck; + + if (!is_whirly(mtmp->data)) { + if (is_animal(mtmp->data)) + You("pierce %s %s wall!", + s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH)); + mtmp->mhp = 1; /* almost dead */ + expels(mtmp, mtmp->data, !is_animal(mtmp->data)); + } + return; + } /* swallowed */ + + if (u.dz) { + if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !Underwater) { + if (u.dz < 0 || On_stairs(u.ux, u.uy)) { + if (On_stairs(u.ux, u.uy)) + pline_The("beam bounces off the %s and hits the %s.", + (u.ux == xdnladder || u.ux == xupladder) ? + "ladder" : "stairs", ceiling(u.ux, u.uy)); + You("loosen a rock from the %s.", ceiling(u.ux, u.uy)); + pline("It falls on your %s!", body_part(HEAD)); + losehp(rnd((uarmh && is_metallic(uarmh)) ? 2 : 6), + "falling rock", KILLED_BY_AN); + otmp = mksobj_at(ROCK, u.ux, u.uy, FALSE, FALSE); + if (otmp) { + (void)xname(otmp); /* set dknown, maybe bknown */ + stackobj(otmp); + } + newsym(u.ux, u.uy); + } else { + watch_dig((struct monst *)0, u.ux, u.uy, TRUE); + (void) dighole(FALSE); + } + } + return; + } /* up or down */ + + /* normal case: digging across the level */ + shopdoor = shopwall = FALSE; + maze_dig = level.flags.is_maze_lev && !Is_earthlevel(&u.uz); + zx = u.ux + u.dx; + zy = u.uy + u.dy; + digdepth = rn1(18, 8); + tmp_at(DISP_BEAM, cmap_to_glyph(S_digbeam)); + while (--digdepth >= 0) { + if (!isok(zx,zy)) break; + room = &levl[zx][zy]; + tmp_at(zx,zy); + delay_output(); /* wait a little bit */ + if (closed_door(zx, zy) || room->typ == SDOOR) { + if (*in_rooms(zx,zy,SHOPBASE)) { + add_damage(zx, zy, 400L); + shopdoor = TRUE; + } + if (room->typ == SDOOR) + room->typ = DOOR; + else if (cansee(zx, zy)) + pline_The("door is razed!"); + watch_dig((struct monst *)0, zx, zy, TRUE); + room->doormask = D_NODOOR; + unblock_point(zx,zy); /* vision */ + digdepth -= 2; + if (maze_dig) break; + } else if (maze_dig) { + if (IS_WALL(room->typ)) { + if (!(room->wall_info & W_NONDIGGABLE)) { + if (*in_rooms(zx,zy,SHOPBASE)) { + add_damage(zx, zy, 200L); + shopwall = TRUE; + } + room->typ = ROOM; + unblock_point(zx,zy); /* vision */ + } else if (!Blind) + pline_The("wall glows then fades."); + break; + } else if (IS_TREE(room->typ)) { /* check trees before stone */ + if (!(room->wall_info & W_NONDIGGABLE)) { + room->typ = ROOM; + unblock_point(zx,zy); /* vision */ + } else if (!Blind) + pline_The("tree shudders but is unharmed."); + break; + } else if (room->typ == STONE || room->typ == SCORR) { + if (!(room->wall_info & W_NONDIGGABLE)) { + room->typ = CORR; + unblock_point(zx,zy); /* vision */ + } else if (!Blind) + pline_The("rock glows then fades."); + break; + } + } else if (IS_ROCK(room->typ)) { + if (!may_dig(zx,zy)) break; + if (IS_WALL(room->typ) || room->typ == SDOOR) { + if (*in_rooms(zx,zy,SHOPBASE)) { + add_damage(zx, zy, 200L); + shopwall = TRUE; + } + watch_dig((struct monst *)0, zx, zy, TRUE); + if (level.flags.is_cavernous_lev && !in_town(zx, zy)) { + room->typ = CORR; + } else { + room->typ = DOOR; + room->doormask = D_NODOOR; + } + digdepth -= 2; + } else if (IS_TREE(room->typ)) { + room->typ = ROOM; + digdepth -= 2; + } else { /* IS_ROCK but not IS_WALL or SDOOR */ + room->typ = CORR; + digdepth--; + } + unblock_point(zx,zy); /* vision */ + } + zx += u.dx; + zy += u.dy; + } /* while */ + tmp_at(DISP_END,0); /* closing call */ + if (shopdoor || shopwall) + pay_for_damage(shopdoor ? "destroy" : "dig into", FALSE); + return; +} + +/* move objects from fobj/nexthere lists to buriedobjlist, keeping position */ +/* information */ +struct obj * +bury_an_obj(otmp) + struct obj *otmp; +{ + struct obj *otmp2; + boolean under_ice; + +#ifdef DEBUG + pline("bury_an_obj: %s", xname(otmp)); +#endif + if (otmp == uball) + unpunish(); + /* after unpunish(), or might get deallocated chain */ + otmp2 = otmp->nexthere; + /* + * obj_resists(,0,0) prevents Rider corpses from being buried. + * It also prevents The Amulet and invocation tools from being + * buried. Since they can't be confined to bags and statues, + * it makes sense that they can't be buried either, even though + * the real reason there (direct accessibility when carried) is + * completely different. + */ + if (otmp == uchain || obj_resists(otmp, 0, 0)) + return(otmp2); + + if (otmp->otyp == LEASH && otmp->leashmon != 0) + o_unleash(otmp); + + if (otmp->lamplit && otmp->otyp != POT_OIL) + end_burn(otmp, TRUE); + + obj_extract_self(otmp); + + under_ice = is_ice(otmp->ox, otmp->oy); + if (otmp->otyp == ROCK && !under_ice) { + /* merges into burying material */ + obfree(otmp, (struct obj *)0); + return(otmp2); + } + /* + * Start a rot on organic material. Not corpses -- they + * are already handled. + */ + if (otmp->otyp == CORPSE) { + ; /* should cancel timer if under_ice */ + } else if ((under_ice ? otmp->oclass == POTION_CLASS : is_organic(otmp)) + && !obj_resists(otmp, 5, 95)) { + (void) start_timer((under_ice ? 0L : 250L) + (long)rnd(250), + TIMER_OBJECT, ROT_ORGANIC, (genericptr_t)otmp); + } + add_to_buried(otmp); + return(otmp2); +} + +void +bury_objs(x, y) +int x, y; +{ + struct obj *otmp, *otmp2; + +#ifdef DEBUG + if(level.objects[x][y] != (struct obj *)0) + pline("bury_objs: at %d, %d", x, y); +#endif + for (otmp = level.objects[x][y]; otmp; otmp = otmp2) + otmp2 = bury_an_obj(otmp); + + /* don't expect any engravings here, but just in case */ + del_engr_at(x, y); + newsym(x, y); +} + +/* move objects from buriedobjlist to fobj/nexthere lists */ +void +unearth_objs(x, y) +int x, y; +{ + struct obj *otmp, *otmp2; + +#ifdef DEBUG + pline("unearth_objs: at %d, %d", x, y); +#endif + for (otmp = level.buriedobjlist; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + if (otmp->ox == x && otmp->oy == y) { + obj_extract_self(otmp); + if (otmp->timed) + (void) stop_timer(ROT_ORGANIC, (genericptr_t)otmp); + place_object(otmp, x, y); + stackobj(otmp); + } + } + del_engr_at(x, y); + newsym(x, y); +} + +/* + * The organic material has rotted away while buried. As an expansion, + * we could add add partial damage. A damage count is kept in the object + * and every time we are called we increment the count and reschedule another + * timeout. Eventually the object rots away. + * + * This is used by buried objects other than corpses. When a container rots + * away, any contents become newly buried objects. + */ +/* ARGSUSED */ +void +rot_organic(arg, timeout) +genericptr_t arg; +long timeout; /* unused */ +{ + struct obj *obj = (struct obj *) arg; + + while (Has_contents(obj)) { + /* We don't need to place contained object on the floor + first, but we do need to update its map coordinates. */ + obj->cobj->ox = obj->ox, obj->cobj->oy = obj->oy; + /* Everything which can be held in a container can also be + buried, so bury_an_obj's use of obj_extract_self insures + that Has_contents(obj) will eventually become false. */ + (void)bury_an_obj(obj->cobj); + } + obj_extract_self(obj); + obfree(obj, (struct obj *) 0); +} + +/* + * Called when a corpse has rotted completely away. + */ +void +rot_corpse(arg, timeout) +genericptr_t arg; +long timeout; /* unused */ +{ + xchar x = 0, y = 0; + struct obj *obj = (struct obj *) arg; + boolean on_floor = obj->where == OBJ_FLOOR, + in_invent = obj->where == OBJ_INVENT; + + if (on_floor) { + x = obj->ox; + y = obj->oy; + } else if (in_invent) { + if (flags.verbose) { + char *cname = corpse_xname(obj, FALSE); + Your("%s%s %s away%c", + obj == uwep ? "wielded " : nul, cname, + otense(obj, "rot"), obj == uwep ? '!' : '.'); + } + if (obj == uwep) { + uwepgone(); /* now bare handed */ + stop_occupation(); + } else if (obj == uswapwep) { + uswapwepgone(); + stop_occupation(); + } else if (obj == uquiver) { + uqwepgone(); + stop_occupation(); + } + } else if (obj->where == OBJ_MINVENT && obj->owornmask) { + if (obj == MON_WEP(obj->ocarry)) { + setmnotwielded(obj->ocarry,obj); + MON_NOWEP(obj->ocarry); + } + } + rot_organic(arg, timeout); + if (on_floor) newsym(x, y); + else if (in_invent) update_inventory(); +} + +#if 0 +void +bury_monst(mtmp) +struct monst *mtmp; +{ +#ifdef DEBUG + pline("bury_monst: %s", mon_nam(mtmp)); +#endif + if(canseemon(mtmp)) { + if(is_flyer(mtmp->data) || is_floater(mtmp->data)) { + pline_The("%s opens up, but %s is not swallowed!", + surface(mtmp->mx, mtmp->my), mon_nam(mtmp)); + return; + } else + pline_The("%s opens up and swallows %s!", + surface(mtmp->mx, mtmp->my), mon_nam(mtmp)); + } + + mtmp->mburied = TRUE; + wakeup(mtmp); /* at least give it a chance :-) */ + newsym(mtmp->mx, mtmp->my); +} + +void +bury_you() +{ +#ifdef DEBUG + pline("bury_you"); +#endif + if (!Levitation && !Flying) { + if(u.uswallow) + You_feel("a sensation like falling into a trap!"); + else + pline_The("%s opens beneath you and you fall in!", + surface(u.ux, u.uy)); + + u.uburied = TRUE; + if(!Strangled && !Breathless) Strangled = 6; + under_ground(1); + } +} + +void +unearth_you() +{ +#ifdef DEBUG + pline("unearth_you"); +#endif + u.uburied = FALSE; + under_ground(0); + if(!uamul || uamul->otyp != AMULET_OF_STRANGULATION) + Strangled = 0; + vision_recalc(0); +} + +void +escape_tomb() +{ +#ifdef DEBUG + pline("escape_tomb"); +#endif + if ((Teleportation || can_teleport(youmonst.data)) && + (Teleport_control || rn2(3) < Luck+2)) { + You("attempt a teleport spell."); + (void) dotele(); /* calls unearth_you() */ + } else if(u.uburied) { /* still buried after 'port attempt */ + boolean good; + + if(amorphous(youmonst.data) || Passes_walls || + noncorporeal(youmonst.data) || unsolid(youmonst.data) || + (tunnels(youmonst.data) && !needspick(youmonst.data))) { + + You("%s up through the %s.", + (tunnels(youmonst.data) && !needspick(youmonst.data)) ? + "try to tunnel" : (amorphous(youmonst.data)) ? + "ooze" : "phase", surface(u.ux, u.uy)); + + if(tunnels(youmonst.data) && !needspick(youmonst.data)) + good = dighole(TRUE); + else good = TRUE; + if(good) unearth_you(); + } + } +} + +void +bury_obj(otmp) +struct obj *otmp; +{ + +#ifdef DEBUG + pline("bury_obj"); +#endif + if(cansee(otmp->ox, otmp->oy)) + pline_The("objects on the %s tumble into a hole!", + surface(otmp->ox, otmp->oy)); + + bury_objs(otmp->ox, otmp->oy); +} +#endif + +#ifdef DEBUG +int +wiz_debug_cmd() /* in this case, bury everything at your loc and around */ +{ + int x, y; + + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) + if (isok(x,y)) bury_objs(x,y); + return 0; +} + +#endif /* DEBUG */ +#endif /* OVL3 */ + +/*dig.c*/ diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..2c9e9ae --- /dev/null +++ b/src/display.c @@ -0,0 +1,2196 @@ +/* SCCS Id: @(#)display.c 3.4 2003/02/19 */ +/* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ +/* and Dave Cohrs, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * THE NEW DISPLAY CODE + * + * The old display code has been broken up into three parts: vision, display, + * and drawing. Vision decides what locations can and cannot be physically + * seen by the hero. Display decides _what_ is displayed at a given location. + * Drawing decides _how_ to draw a monster, fountain, sword, etc. + * + * The display system uses information from the vision system to decide + * what to draw at a given location. The routines for the vision system + * can be found in vision.c and vision.h. The routines for display can + * be found in this file (display.c) and display.h. The drawing routines + * are part of the window port. See doc/window.doc for the drawing + * interface. + * + * The display system deals with an abstraction called a glyph. Anything + * that could possibly be displayed has a unique glyph identifier. + * + * What is seen on the screen is a combination of what the hero remembers + * and what the hero currently sees. Objects and dungeon features (walls + * doors, etc) are remembered when out of sight. Monsters and temporary + * effects are not remembered. Each location on the level has an + * associated glyph. This is the hero's _memory_ of what he or she has + * seen there before. + * + * Display rules: + * + * If the location is in sight, display in order: + * visible (or sensed) monsters + * visible objects + * known traps + * background + * + * If the location is out of sight, display in order: + * sensed monsters (telepathy) + * memory + * + * + * + * Here is a list of the major routines in this file to be used externally: + * + * newsym + * + * Possibly update the screen location (x,y). This is the workhorse routine. + * It is always correct --- where correct means following the in-sight/out- + * of-sight rules. **Most of the code should use this routine.** This + * routine updates the map and displays monsters. + * + * + * map_background + * map_object + * map_trap + * map_invisible + * unmap_object + * + * If you absolutely must override the in-sight/out-of-sight rules, there + * are two possibilities. First, you can mess with vision to force the + * location in sight then use newsym(), or you can use the map_* routines. + * The first has not been tried [no need] and the second is used in the + * detect routines --- detect object, magic mapping, etc. The map_* + * routines *change* what the hero remembers. All changes made by these + * routines will be sticky --- they will survive screen redraws. Do *not* + * use these for things that only temporarily change the screen. These + * routines are also used directly by newsym(). unmap_object is used to + * clear a remembered object when/if detection reveals it isn't there. + * + * + * show_glyph + * + * This is direct (no processing in between) buffered access to the screen. + * Temporary screen effects are run through this and its companion, + * flush_screen(). There is yet a lower level routine, print_glyph(), + * but this is unbuffered and graphic dependent (i.e. it must be surrounded + * by graphic set-up and tear-down routines). Do not use print_glyph(). + * + * + * see_monsters + * see_objects + * see_traps + * + * These are only used when something affects all of the monsters or + * objects or traps. For objects and traps, the only thing is hallucination. + * For monsters, there are hallucination and changing from/to blindness, etc. + * + * + * tmp_at + * + * This is a useful interface for displaying temporary items on the screen. + * Its interface is different than previously, so look at it carefully. + * + * + * + * Parts of the rm structure that are used: + * + * typ - What is really there. + * glyph - What the hero remembers. This will never be a monster. + * Monsters "float" above this. + * lit - True if the position is lit. An optimization for + * lit/unlit rooms. + * waslit - True if the position was *remembered* as lit. + * seenv - A vector of bits representing the directions from which the + * hero has seen this position. The vector's primary use is + * determining how walls are seen. E.g. a wall sometimes looks + * like stone on one side, but is seen as a wall from the other. + * Other uses are for unmapping detected objects and felt + * locations, where we need to know if the hero has ever + * seen the location. + * flags - Additional information for the typ field. Different for + * each typ. + * horizontal - Indicates whether the wall or door is horizontal or + * vertical. + */ +#include "hack.h" +#include "region.h" + +STATIC_DCL void FDECL(display_monster,(XCHAR_P,XCHAR_P,struct monst *,int,XCHAR_P)); +STATIC_DCL int FDECL(swallow_to_glyph, (int, int)); +STATIC_DCL void FDECL(display_warning,(struct monst *)); + +STATIC_DCL int FDECL(check_pos, (int, int, int)); +#ifdef WA_VERBOSE +STATIC_DCL boolean FDECL(more_than_one, (int, int, int, int, int)); +#endif +STATIC_DCL int FDECL(set_twall, (int,int, int,int, int,int, int,int)); +STATIC_DCL int FDECL(set_wall, (int, int, int)); +STATIC_DCL int FDECL(set_corn, (int,int, int,int, int,int, int,int)); +STATIC_DCL int FDECL(set_crosswall, (int, int)); +STATIC_DCL void FDECL(set_seenv, (struct rm *, int, int, int, int)); +STATIC_DCL void FDECL(t_warn, (struct rm *)); +STATIC_DCL int FDECL(wall_angle, (struct rm *)); + +#ifdef INVISIBLE_OBJECTS +/* + * vobj_at() + * + * Returns a pointer to an object if the hero can see an object at the + * given location. This takes care of invisible objects. NOTE, this + * assumes that the hero is not blind and on top of the object pile. + * It does NOT take into account that the location is out of sight, or, + * say, one can see blessed, etc. + */ +struct obj * +vobj_at(x,y) + xchar x,y; +{ + register struct obj *obj = level.objects[x][y]; + + while (obj) { + if (!obj->oinvis || See_invisible) return obj; + obj = obj->nexthere; + } + return ((struct obj *) 0); +} +#endif /* else vobj_at() is defined in display.h */ + +/* + * magic_map_background() + * + * This function is similar to map_background (see below) except we pay + * attention to and correct unexplored, lit ROOM and CORR spots. + */ +void +magic_map_background(x, y, show) + xchar x,y; + int show; +{ + int glyph = back_to_glyph(x,y); /* assumes hero can see x,y */ + struct rm *lev = &levl[x][y]; + + /* + * Correct for out of sight lit corridors and rooms that the hero + * doesn't remember as lit. + */ + if (!cansee(x,y) && !lev->waslit) { + /* Floor spaces are dark if unlit. Corridors are dark if unlit. */ + if (lev->typ == ROOM && glyph == cmap_to_glyph(S_room)) + glyph = cmap_to_glyph(S_stone); + else if (lev->typ == CORR && glyph == cmap_to_glyph(S_litcorr)) + glyph = cmap_to_glyph(S_corr); + } + if (level.flags.hero_memory) + lev->glyph = glyph; + if (show) show_glyph(x,y, glyph); +} + +/* + * The routines map_background(), map_object(), and map_trap() could just + * as easily be: + * + * map_glyph(x,y,glyph,show) + * + * Which is called with the xx_to_glyph() in the call. Then I can get + * rid of 3 routines that don't do very much anyway. And then stop + * having to create fake objects and traps. However, I am reluctant to + * make this change. + */ +/* FIXME: some of these use xchars for x and y, and some use ints. Make + * this consistent. + */ + +/* + * map_background() + * + * Make the real background part of our map. This routine assumes that + * the hero can physically see the location. Update the screen if directed. + */ +void +map_background(x, y, show) + register xchar x,y; + register int show; +{ + register int glyph = back_to_glyph(x,y); + + if (level.flags.hero_memory) + levl[x][y].glyph = glyph; + if (show) show_glyph(x,y, glyph); +} + +/* + * map_trap() + * + * Map the trap and print it out if directed. This routine assumes that the + * hero can physically see the location. + */ +void +map_trap(trap, show) + register struct trap *trap; + register int show; +{ + register int x = trap->tx, y = trap->ty; + register int glyph = trap_to_glyph(trap); + + if (level.flags.hero_memory) + levl[x][y].glyph = glyph; + if (show) show_glyph(x, y, glyph); +} + +/* + * map_object() + * + * Map the given object. This routine assumes that the hero can physically + * see the location of the object. Update the screen if directed. + */ +void +map_object(obj, show) + register struct obj *obj; + register int show; +{ + register int x = obj->ox, y = obj->oy; + register int glyph = obj_to_glyph(obj); + + if (level.flags.hero_memory) + levl[x][y].glyph = glyph; + if (show) show_glyph(x, y, glyph); +} + +/* + * map_invisible() + * + * Make the hero remember that a square contains an invisible monster. + * This is a special case in that the square will continue to be displayed + * this way even when the hero is close enough to see it. To get rid of + * this and display the square's actual contents, use unmap_object() followed + * by newsym() if necessary. + */ +void +map_invisible(x, y) +register xchar x, y; +{ + if (x != u.ux || y != u.uy) { /* don't display I at hero's location */ + if (level.flags.hero_memory) + levl[x][y].glyph = GLYPH_INVISIBLE; + show_glyph(x, y, GLYPH_INVISIBLE); + } +} + +/* + * unmap_object() + * + * Remove something from the map when the hero realizes it's not there any + * more. Replace it with background or known trap, but not with any other + * If this is used for detection, a full screen update is imminent anyway; + * if this is used to get rid of an invisible monster notation, we might have + * to call newsym(). + */ +void +unmap_object(x, y) + register int x, y; +{ + register struct trap *trap; + + if (!level.flags.hero_memory) return; + + if ((trap = t_at(x,y)) != 0 && trap->tseen && !covers_traps(x,y)) + map_trap(trap, 0); + else if (levl[x][y].seenv) { + struct rm *lev = &levl[x][y]; + + map_background(x, y, 0); + + /* turn remembered dark room squares dark */ + if (!lev->waslit && lev->glyph == cmap_to_glyph(S_room) && + lev->typ == ROOM) + lev->glyph = cmap_to_glyph(S_stone); + } else + levl[x][y].glyph = cmap_to_glyph(S_stone); /* default val */ +} + + +/* + * map_location() + * + * Make whatever at this location show up. This is only for non-living + * things. This will not handle feeling invisible objects correctly. + * + * Internal to display.c, this is a #define for speed. + */ +#define _map_location(x,y,show) \ +{ \ + register struct obj *obj; \ + register struct trap *trap; \ + \ + if ((obj = vobj_at(x,y)) && !covers_objects(x,y)) \ + map_object(obj,show); \ + else if ((trap = t_at(x,y)) && trap->tseen && !covers_traps(x,y)) \ + map_trap(trap,show); \ + else \ + map_background(x,y,show); \ +} + +void +map_location(x,y,show) + int x, y, show; +{ + _map_location(x,y,show); +} + +#define DETECTED 2 +#define PHYSICALLY_SEEN 1 +#define is_worm_tail(mon) ((mon) && ((x != (mon)->mx) || (y != (mon)->my))) + +/* + * display_monster() + * + * Note that this is *not* a map_XXXX() function! Monsters sort of float + * above everything. + * + * Yuck. Display body parts by recognizing that the display position is + * not the same as the monster position. Currently the only body part is + * a worm tail. + * + */ +STATIC_OVL void +display_monster(x, y, mon, sightflags, worm_tail) + register xchar x, y; /* display position */ + register struct monst *mon; /* monster to display */ + int sightflags; /* 1 if the monster is physically seen */ + /* 2 if detected using Detect_monsters */ + register xchar worm_tail; /* mon is actually a worm tail */ +{ + register boolean mon_mimic = (mon->m_ap_type != M_AP_NOTHING); + register int sensed = mon_mimic && + (Protection_from_shape_changers || sensemon(mon)); + /* + * We must do the mimic check first. If the mimic is mimicing something, + * and the location is in sight, we have to change the hero's memory + * so that when the position is out of sight, the hero remembers what + * the mimic was mimicing. + */ + + if (mon_mimic && (sightflags == PHYSICALLY_SEEN)) { + switch (mon->m_ap_type) { + default: + impossible("display_monster: bad m_ap_type value [ = %d ]", + (int) mon->m_ap_type); + case M_AP_NOTHING: + show_glyph(x, y, mon_to_glyph(mon)); + break; + + case M_AP_FURNITURE: { + /* + * This is a poor man's version of map_background(). I can't + * use map_background() because we are overriding what is in + * the 'typ' field. Maybe have map_background()'s parameters + * be (x,y,glyph) instead of just (x,y). + * + * mappearance is currently set to an S_ index value in + * makemon.c. + */ + register int glyph = cmap_to_glyph(mon->mappearance); + levl[x][y].glyph = glyph; + if (!sensed) show_glyph(x,y, glyph); + break; + } + + case M_AP_OBJECT: { + struct obj obj; /* Make a fake object to send */ + /* to map_object(). */ + obj.ox = x; + obj.oy = y; + obj.otyp = mon->mappearance; + obj.corpsenm = PM_TENGU; /* if mimicing a corpse */ + map_object(&obj,!sensed); + break; + } + + case M_AP_MONSTER: + show_glyph(x,y, monnum_to_glyph(what_mon((int)mon->mappearance))); + break; + } + + } + + /* If the mimic is unsucessfully mimicing something, display the monster */ + if (!mon_mimic || sensed) { + int num; + + /* [ALI] Only use detected glyphs when monster wouldn't be + * visible by any other means. + */ + if (sightflags == DETECTED) { + if (worm_tail) + num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); + else + num = detected_mon_to_glyph(mon); + } else if (mon->mtame && !Hallucination) { + if (worm_tail) + num = petnum_to_glyph(PM_LONG_WORM_TAIL); + else + num = pet_to_glyph(mon); + } else { + if (worm_tail) + num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); + else + num = mon_to_glyph(mon); + } + show_glyph(x,y,num); + } +} + +/* + * display_warning() + * + * This is also *not* a map_XXXX() function! Monster warnings float + * above everything just like monsters do, but only if the monster + * is not showing. + * + * Do not call for worm tails. + */ +STATIC_OVL void +display_warning(mon) + register struct monst *mon; +{ + int x = mon->mx, y = mon->my; + int wl = (int) (mon->m_lev / 4); + int glyph; + + if (mon_warning(mon)) { + if (wl > WARNCOUNT - 1) wl = WARNCOUNT - 1; + /* 3.4.1: this really ought to be rn2(WARNCOUNT), but value "0" + isn't handled correctly by the what_is routine so avoid it */ + if (Hallucination) wl = rn1(WARNCOUNT-1,1); + glyph = warning_to_glyph(wl); + } else if (MATCH_WARN_OF_MON(mon)) { + glyph = mon_to_glyph(mon); + } else { + impossible("display_warning did not match warning type?"); + return; + } + show_glyph(x, y, glyph); +} + +/* + * feel_location() + * + * Feel the given location. This assumes that the hero is blind and that + * the given position is either the hero's or one of the eight squares + * adjacent to the hero (except for a boulder push). + * If an invisible monster has gone away, that will be discovered. If an + * invisible monster has appeared, this will _not_ be discovered since + * searching only finds one monster per turn so we must check that separately. + */ +void +feel_location(x, y) + xchar x, y; +{ + struct rm *lev = &(levl[x][y]); + struct obj *boulder; + register struct monst *mon; + + /* If the hero's memory of an invisible monster is accurate, we want to keep + * him from detecting the same monster over and over again on each turn. + * We must return (so we don't erase the monster). (We must also, in the + * search function, be sure to skip over previously detected 'I's.) + */ + if (glyph_is_invisible(levl[x][y].glyph) && m_at(x,y)) return; + + /* The hero can't feel non pool locations while under water. */ + if (Underwater && !Is_waterlevel(&u.uz) && ! is_pool(x,y)) + return; + + /* Set the seen vector as if the hero had seen it. It doesn't matter */ + /* if the hero is levitating or not. */ + set_seenv(lev, u.ux, u.uy, x, y); + + if (Levitation && !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) { + /* + * Levitation Rules. It is assumed that the hero can feel the state + * of the walls around herself and can tell if she is in a corridor, + * room, or doorway. Boulders are felt because they are large enough. + * Anything else is unknown because the hero can't reach the ground. + * This makes things difficult. + * + * Check (and display) in order: + * + * + Stone, walls, and closed doors. + * + Boulders. [see a boulder before a doorway] + * + Doors. + * + Room/water positions + * + Everything else (hallways!) + */ + if (IS_ROCK(lev->typ) || (IS_DOOR(lev->typ) && + (lev->doormask & (D_LOCKED | D_CLOSED)))) { + map_background(x, y, 1); + } else if ((boulder = sobj_at(BOULDER,x,y)) != 0) { + map_object(boulder, 1); + } else if (IS_DOOR(lev->typ)) { + map_background(x, y, 1); + } else if (IS_ROOM(lev->typ) || IS_POOL(lev->typ)) { + /* + * An open room or water location. Normally we wouldn't touch + * this, but we have to get rid of remembered boulder symbols. + * This will only occur in rare occations when the hero goes + * blind and doesn't find a boulder where expected (something + * came along and picked it up). We know that there is not a + * boulder at this location. Show fountains, pools, etc. + * underneath if already seen. Otherwise, show the appropriate + * floor symbol. + * + * Similarly, if the hero digs a hole in a wall or feels a location + * that used to contain an unseen monster. In these cases, + * there's no reason to assume anything was underneath, so + * just show the appropriate floor symbol. If something was + * embedded in the wall, the glyph will probably already + * reflect that. Don't change the symbol in this case. + * + * This isn't quite correct. If the boulder was on top of some + * other objects they should be seen once the boulder is removed. + * However, we have no way of knowing that what is there now + * was there then. So we let the hero have a lapse of memory. + * We could also just display what is currently on the top of the + * object stack (if anything). + */ + if (lev->glyph == objnum_to_glyph(BOULDER)) { + if (lev->typ != ROOM && lev->seenv) { + map_background(x, y, 1); + } else { + lev->glyph = lev->waslit ? cmap_to_glyph(S_room) : + cmap_to_glyph(S_stone); + show_glyph(x,y,lev->glyph); + } + } else if ((lev->glyph >= cmap_to_glyph(S_stone) && + lev->glyph < cmap_to_glyph(S_room)) || + glyph_is_invisible(levl[x][y].glyph)) { + lev->glyph = lev->waslit ? cmap_to_glyph(S_room) : + cmap_to_glyph(S_stone); + show_glyph(x,y,lev->glyph); + } + } else { + /* We feel it (I think hallways are the only things left). */ + map_background(x, y, 1); + /* Corridors are never felt as lit (unless remembered that way) */ + /* (lit_corridor only). */ + if (lev->typ == CORR && + lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit) + show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr)); + } + } else { + _map_location(x, y, 1); + + if (Punished) { + /* + * A ball or chain is only felt if it is first on the object + * location list. Otherwise, we need to clear the felt bit --- + * something has been dropped on the ball/chain. If the bit is + * not cleared, then when the ball/chain is moved it will drop + * the wrong glyph. + */ + if (uchain->ox == x && uchain->oy == y) { + if (level.objects[x][y] == uchain) + u.bc_felt |= BC_CHAIN; + else + u.bc_felt &= ~BC_CHAIN; /* do not feel the chain */ + } + if (!carried(uball) && uball->ox == x && uball->oy == y) { + if (level.objects[x][y] == uball) + u.bc_felt |= BC_BALL; + else + u.bc_felt &= ~BC_BALL; /* do not feel the ball */ + } + } + + /* Floor spaces are dark if unlit. Corridors are dark if unlit. */ + if (lev->typ == ROOM && + lev->glyph == cmap_to_glyph(S_room) && !lev->waslit) + show_glyph(x,y, lev->glyph = cmap_to_glyph(S_stone)); + else if (lev->typ == CORR && + lev->glyph == cmap_to_glyph(S_litcorr) && !lev->waslit) + show_glyph(x,y, lev->glyph = cmap_to_glyph(S_corr)); + } + /* draw monster on top if we can sense it */ + if ((x != u.ux || y != u.uy) && (mon = m_at(x,y)) && sensemon(mon)) + display_monster(x, y, mon, + (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)) ? PHYSICALLY_SEEN : DETECTED, + is_worm_tail(mon)); +} + +/* + * newsym() + * + * Possibly put a new glyph at the given location. + */ +void +newsym(x,y) + register int x,y; +{ + register struct monst *mon; + register struct rm *lev = &(levl[x][y]); + register int see_it; + register xchar worm_tail; + + if (in_mklev) return; + + /* only permit updating the hero when swallowed */ + if (u.uswallow) { + if (x == u.ux && y == u.uy) display_self(); + return; + } + if (Underwater && !Is_waterlevel(&u.uz)) { + /* don't do anything unless (x,y) is an adjacent underwater position */ + int dx, dy; + if (!is_pool(x,y)) return; + dx = x - u.ux; if (dx < 0) dx = -dx; + dy = y - u.uy; if (dy < 0) dy = -dy; + if (dx > 1 || dy > 1) return; + } + + /* Can physically see the location. */ + if (cansee(x,y)) { + NhRegion* reg = visible_region_at(x,y); + /* + * Don't use templit here: E.g. + * + * lev->waslit = !!(lev->lit || templit(x,y)); + * + * Otherwise we have the "light pool" problem, where non-permanently + * lit areas just out of sight stay remembered as lit. They should + * re-darken. + * + * Perhaps ALL areas should revert to their "unlit" look when + * out of sight. + */ + lev->waslit = (lev->lit!=0); /* remember lit condition */ + + if (reg != NULL && ACCESSIBLE(lev->typ)) { + show_region(reg,x,y); + return; + } + if (x == u.ux && y == u.uy) { + if (senseself()) { + _map_location(x,y,0); /* map *under* self */ + display_self(); + } else + /* we can see what is there */ + _map_location(x,y,1); + } + else { + mon = m_at(x,y); + worm_tail = is_worm_tail(mon); + see_it = mon && (worm_tail + ? (!mon->minvis || See_invisible) + : (mon_visible(mon)) || tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)); + if (mon && (see_it || (!worm_tail && Detect_monsters))) { + if (mon->mtrapped) { + struct trap *trap = t_at(x, y); + int tt = trap ? trap->ttyp : NO_TRAP; + + /* if monster is in a physical trap, you see the trap too */ + if (tt == BEAR_TRAP || tt == PIT || + tt == SPIKED_PIT ||tt == WEB) { + trap->tseen = TRUE; + } + } + _map_location(x,y,0); /* map under the monster */ + /* also gets rid of any invisibility glyph */ + display_monster(x, y, mon, see_it ? PHYSICALLY_SEEN : DETECTED, worm_tail); + } + else if (mon && mon_warning(mon) && !is_worm_tail(mon)) + display_warning(mon); + else if (glyph_is_invisible(levl[x][y].glyph)) + map_invisible(x, y); + else + _map_location(x,y,1); /* map the location */ + } + } + + /* Can't see the location. */ + else { + if (x == u.ux && y == u.uy) { + feel_location(u.ux, u.uy); /* forces an update */ + + if (senseself()) display_self(); + } + else if ((mon = m_at(x,y)) + && ((see_it = (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon) + || (see_with_infrared(mon) && mon_visible(mon)))) + || Detect_monsters) + && !is_worm_tail(mon)) { + /* Monsters are printed every time. */ + /* This also gets rid of any invisibility glyph */ + display_monster(x, y, mon, see_it ? 0 : DETECTED, 0); + } + else if ((mon = m_at(x,y)) && mon_warning(mon) && + !is_worm_tail(mon)) { + display_warning(mon); + } + + /* + * If the location is remembered as being both dark (waslit is false) + * and lit (glyph is a lit room or lit corridor) then it was either: + * + * (1) A dark location that the hero could see through night + * vision. + * + * (2) Darkened while out of the hero's sight. This can happen + * when cursed scroll of light is read. + * + * In either case, we have to manually correct the hero's memory to + * match waslit. Deciding when to change waslit is non-trivial. + * + * Note: If flags.lit_corridor is set, then corridors act like room + * squares. That is, they light up if in night vision range. + * If flags.lit_corridor is not set, then corridors will + * remain dark unless lit by a light spell and may darken + * again, as discussed above. + * + * These checks and changes must be here and not in back_to_glyph(). + * They are dependent on the position being out of sight. + */ + else if (!lev->waslit) { + if (lev->glyph == cmap_to_glyph(S_litcorr) && lev->typ == CORR) + show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr)); + else if (lev->glyph == cmap_to_glyph(S_room) && lev->typ == ROOM) + show_glyph(x, y, lev->glyph = cmap_to_glyph(S_stone)); + else + goto show_mem; + } else { +show_mem: + show_glyph(x, y, lev->glyph); + } + } +} + +#undef is_worm_tail + +/* + * shieldeff() + * + * Put magic shield pyrotechnics at the given location. This *could* be + * pulled into a platform dependent routine for fancier graphics if desired. + */ +void +shieldeff(x,y) + xchar x,y; +{ + register int i; + + if (!flags.sparkle) return; + if (cansee(x,y)) { /* Don't see anything if can't see the location */ + for (i = 0; i < SHIELD_COUNT; i++) { + show_glyph(x, y, cmap_to_glyph(shield_static[i])); + flush_screen(1); /* make sure the glyph shows up */ + delay_output(); + } + newsym(x,y); /* restore the old information */ + } +} + + +/* + * tmp_at() + * + * Temporarily place glyphs on the screen. Do not call delay_output(). It + * is up to the caller to decide if it wants to wait [presently, everyone + * but explode() wants to delay]. + * + * Call: + * (DISP_BEAM, glyph) open, initialize glyph + * (DISP_FLASH, glyph) open, initialize glyph + * (DISP_ALWAYS, glyph) open, initialize glyph + * (DISP_CHANGE, glyph) change glyph + * (DISP_END, 0) close & clean up (second argument doesn't + * matter) + * (DISP_FREEMEM, 0) only used to prevent memory leak during + * exit) + * (x, y) display the glyph at the location + * + * DISP_BEAM - Display the given glyph at each location, but do not erase + * any until the close call. + * DISP_FLASH - Display the given glyph at each location, but erase the + * previous location's glyph. + * DISP_ALWAYS- Like DISP_FLASH, but vision is not taken into account. + */ + +static struct tmp_glyph { + coord saved[COLNO]; /* previously updated positions */ + int sidx; /* index of next unused slot in saved[] */ + int style; /* either DISP_BEAM or DISP_FLASH or DISP_ALWAYS */ + int glyph; /* glyph to use when printing */ + struct tmp_glyph *prev; +} tgfirst; + +void +tmp_at(x, y) + int x, y; +{ + static struct tmp_glyph *tglyph = (struct tmp_glyph *)0; + struct tmp_glyph *tmp; + + switch (x) { + case DISP_BEAM: + case DISP_FLASH: + case DISP_ALWAYS: + if (!tglyph) + tmp = &tgfirst; + else /* nested effect; we need dynamic memory */ + tmp = (struct tmp_glyph *)alloc(sizeof (struct tmp_glyph)); + tmp->prev = tglyph; + tglyph = tmp; + tglyph->sidx = 0; + tglyph->style = x; + tglyph->glyph = y; + flush_screen(0); /* flush buffered glyphs */ + return; + + case DISP_FREEMEM: /* in case game ends with tmp_at() in progress */ + while (tglyph) { + tmp = tglyph->prev; + if (tglyph != &tgfirst) free((genericptr_t)tglyph); + tglyph = tmp; + } + return; + + default: + break; + } + + if (!tglyph) panic("tmp_at: tglyph not initialized"); + + switch (x) { + case DISP_CHANGE: + tglyph->glyph = y; + break; + + case DISP_END: + if (tglyph->style == DISP_BEAM) { + register int i; + + /* Erase (reset) from source to end */ + for (i = 0; i < tglyph->sidx; i++) + newsym(tglyph->saved[i].x, tglyph->saved[i].y); + } else { /* DISP_FLASH or DISP_ALWAYS */ + if (tglyph->sidx) /* been called at least once */ + newsym(tglyph->saved[0].x, tglyph->saved[0].y); + } + /* tglyph->sidx = 0; -- about to be freed, so not necessary */ + tmp = tglyph->prev; + if (tglyph != &tgfirst) free((genericptr_t)tglyph); + tglyph = tmp; + break; + + default: /* do it */ + if (tglyph->style == DISP_BEAM) { + if (!cansee(x,y)) break; + /* save pos for later erasing */ + tglyph->saved[tglyph->sidx].x = x; + tglyph->saved[tglyph->sidx].y = y; + tglyph->sidx += 1; + } else { /* DISP_FLASH/ALWAYS */ + if (tglyph->sidx) { /* not first call, so reset previous pos */ + newsym(tglyph->saved[0].x, tglyph->saved[0].y); + tglyph->sidx = 0; /* display is presently up to date */ + } + if (!cansee(x,y) && tglyph->style != DISP_ALWAYS) break; + tglyph->saved[0].x = x; + tglyph->saved[0].y = y; + tglyph->sidx = 1; + } + + show_glyph(x, y, tglyph->glyph); /* show it */ + flush_screen(0); /* make sure it shows up */ + break; + } /* end case */ +} + + +/* + * swallowed() + * + * The hero is swallowed. Show a special graphics sequence for this. This + * bypasses all of the display routines and messes with buffered screen + * directly. This method works because both vision and display check for + * being swallowed. + */ +void +swallowed(first) + int first; +{ + static xchar lastx, lasty; /* last swallowed position */ + int swallower, left_ok, rght_ok; + + if (first) + cls(); + else { + register int x, y; + + /* Clear old location */ + for (y = lasty-1; y <= lasty+1; y++) + for (x = lastx-1; x <= lastx+1; x++) + if (isok(x,y)) show_glyph(x,y,cmap_to_glyph(S_stone)); + } + + swallower = monsndx(u.ustuck->data); + /* assume isok(u.ux,u.uy) */ + left_ok = isok(u.ux-1,u.uy); + rght_ok = isok(u.ux+1,u.uy); + /* + * Display the hero surrounded by the monster's stomach. + */ + if(isok(u.ux, u.uy-1)) { + if (left_ok) + show_glyph(u.ux-1, u.uy-1, swallow_to_glyph(swallower, S_sw_tl)); + show_glyph(u.ux , u.uy-1, swallow_to_glyph(swallower, S_sw_tc)); + if (rght_ok) + show_glyph(u.ux+1, u.uy-1, swallow_to_glyph(swallower, S_sw_tr)); + } + + if (left_ok) + show_glyph(u.ux-1, u.uy , swallow_to_glyph(swallower, S_sw_ml)); + display_self(); + if (rght_ok) + show_glyph(u.ux+1, u.uy , swallow_to_glyph(swallower, S_sw_mr)); + + if(isok(u.ux, u.uy+1)) { + if (left_ok) + show_glyph(u.ux-1, u.uy+1, swallow_to_glyph(swallower, S_sw_bl)); + show_glyph(u.ux , u.uy+1, swallow_to_glyph(swallower, S_sw_bc)); + if (rght_ok) + show_glyph(u.ux+1, u.uy+1, swallow_to_glyph(swallower, S_sw_br)); + } + + /* Update the swallowed position. */ + lastx = u.ux; + lasty = u.uy; +} + +/* + * under_water() + * + * Similar to swallowed() in operation. Shows hero when underwater + * except when in water level. Special routines exist for that. + */ +void +under_water(mode) + int mode; +{ + static xchar lastx, lasty; + static boolean dela; + register int x, y; + + /* swallowing has a higher precedence than under water */ + if (Is_waterlevel(&u.uz) || u.uswallow) return; + + /* full update */ + if (mode == 1 || dela) { + cls(); + dela = FALSE; + } + /* delayed full update */ + else if (mode == 2) { + dela = TRUE; + return; + } + /* limited update */ + else { + for (y = lasty-1; y <= lasty+1; y++) + for (x = lastx-1; x <= lastx+1; x++) + if (isok(x,y)) + show_glyph(x,y,cmap_to_glyph(S_stone)); + } + for (x = u.ux-1; x <= u.ux+1; x++) + for (y = u.uy-1; y <= u.uy+1; y++) + if (isok(x,y) && is_pool(x,y)) { + if (Blind && !(x == u.ux && y == u.uy)) + show_glyph(x,y,cmap_to_glyph(S_stone)); + else + newsym(x,y); + } + lastx = u.ux; + lasty = u.uy; +} + +/* + * under_ground() + * + * Very restricted display. You can only see yourself. + */ +void +under_ground(mode) + int mode; +{ + static boolean dela; + + /* swallowing has a higher precedence than under ground */ + if (u.uswallow) return; + + /* full update */ + if (mode == 1 || dela) { + cls(); + dela = FALSE; + } + /* delayed full update */ + else if (mode == 2) { + dela = TRUE; + return; + } + /* limited update */ + else + newsym(u.ux,u.uy); +} + + +/* ========================================================================= */ + +/* + * Loop through all of the monsters and update them. Called when: + * + going blind & telepathic + * + regaining sight & telepathic + * + getting and losing infravision + * + hallucinating + * + doing a full screen redraw + * + see invisible times out or a ring of see invisible is taken off + * + when a potion of see invisible is quaffed or a ring of see + * invisible is put on + * + gaining telepathy when blind [givit() in eat.c, pleased() in pray.c] + * + losing telepathy while blind [xkilled() in mon.c, attrcurse() in + * sit.c] + */ +void +see_monsters() +{ + register struct monst *mon; + + for (mon = fmon; mon; mon = mon->nmon) { + if (DEADMONSTER(mon)) continue; + newsym(mon->mx,mon->my); + if (mon->wormno) see_wsegs(mon); + } +#ifdef STEED + /* when mounted, hero's location gets caught by monster loop */ + if (!u.usteed) +#endif + newsym(u.ux, u.uy); +} + +/* + * Block/unblock light depending on what a mimic is mimicing and if it's + * invisible or not. Should be called only when the state of See_invisible + * changes. + */ +void +set_mimic_blocking() +{ + register struct monst *mon; + + for (mon = fmon; mon; mon = mon->nmon) { + if (DEADMONSTER(mon)) continue; + if (mon->minvis && + ((mon->m_ap_type == M_AP_FURNITURE && + (mon->mappearance == S_vcdoor || mon->mappearance == S_hcdoor)) || + (mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER))) { + if(See_invisible) + block_point(mon->mx, mon->my); + else + unblock_point(mon->mx, mon->my); + } + } +} + +/* + * Loop through all of the object *locations* and update them. Called when + * + hallucinating. + */ +void +see_objects() +{ + register struct obj *obj; + for(obj = fobj; obj; obj = obj->nobj) + if (vobj_at(obj->ox,obj->oy) == obj) newsym(obj->ox, obj->oy); +} + +/* + * Update hallucinated traps. + */ +void +see_traps() +{ + struct trap *trap; + int glyph; + + for (trap = ftrap; trap; trap = trap->ntrap) { + glyph = glyph_at(trap->tx, trap->ty); + if (glyph_is_trap(glyph)) + newsym(trap->tx, trap->ty); + } +} + +/* + * Put the cursor on the hero. Flush all accumulated glyphs before doing it. + */ +void +curs_on_u() +{ + flush_screen(1); /* Flush waiting glyphs & put cursor on hero */ +} + +int +doredraw() +{ + docrt(); + return 0; +} + +void +docrt() +{ + register int x,y; + register struct rm *lev; + + if (!u.ux) return; /* display isn't ready yet */ + + if (u.uswallow) { + swallowed(1); + return; + } + if (Underwater && !Is_waterlevel(&u.uz)) { + under_water(1); + return; + } + if (u.uburied) { + under_ground(1); + return; + } + + /* shut down vision */ + vision_recalc(2); + + /* + * This routine assumes that cls() does the following: + * + fills the physical screen with the symbol for rock + * + clears the glyph buffer + */ + cls(); + + /* display memory */ + for (x = 1; x < COLNO; x++) { + lev = &levl[x][0]; + for (y = 0; y < ROWNO; y++, lev++) + if (lev->glyph != cmap_to_glyph(S_stone)) + show_glyph(x,y,lev->glyph); + } + + /* see what is to be seen */ + vision_recalc(0); + + /* overlay with monsters */ + see_monsters(); + + flags.botlx = 1; /* force a redraw of the bottom line */ +} + + +/* ========================================================================= */ +/* Glyph Buffering (3rd screen) ============================================ */ + +typedef struct { + xchar new; /* perhaps move this bit into the rm strucure. */ + int glyph; +} gbuf_entry; + +static gbuf_entry gbuf[ROWNO][COLNO]; +static char gbuf_start[ROWNO]; +static char gbuf_stop[ROWNO]; + +/* + * Store the glyph in the 3rd screen for later flushing. + */ +void +show_glyph(x,y,glyph) + int x, y, glyph; +{ + /* + * Check for bad positions and glyphs. + */ + if (!isok(x, y)) { + const char *text; + int offset; + + /* column 0 is invalid, but it's often used as a flag, so ignore it */ + if (x == 0) return; + + /* + * This assumes an ordering of the offsets. See display.h for + * the definition. + */ + + if (glyph >= GLYPH_WARNING_OFF) { /* a warning */ + text = "warning"; offset = glyph - GLYPH_WARNING_OFF; + } else if (glyph >= GLYPH_SWALLOW_OFF) { /* swallow border */ + text = "swallow border"; offset = glyph - GLYPH_SWALLOW_OFF; + } else if (glyph >= GLYPH_ZAP_OFF) { /* zap beam */ + text = "zap beam"; offset = glyph - GLYPH_ZAP_OFF; + } else if (glyph >= GLYPH_EXPLODE_OFF) { /* explosion */ + text = "explosion"; offset = glyph - GLYPH_EXPLODE_OFF; + } else if (glyph >= GLYPH_CMAP_OFF) { /* cmap */ + text = "cmap_index"; offset = glyph - GLYPH_CMAP_OFF; + } else if (glyph >= GLYPH_OBJ_OFF) { /* object */ + text = "object"; offset = glyph - GLYPH_OBJ_OFF; + } else if (glyph >= GLYPH_RIDDEN_OFF) { /* ridden mon */ + text = "ridden mon"; offset = glyph - GLYPH_RIDDEN_OFF; + } else if (glyph >= GLYPH_BODY_OFF) { /* a corpse */ + text = "corpse"; offset = glyph - GLYPH_BODY_OFF; + } else if (glyph >= GLYPH_DETECT_OFF) { /* detected mon */ + text = "detected mon"; offset = glyph - GLYPH_DETECT_OFF; + } else if (glyph >= GLYPH_INVIS_OFF) { /* invisible mon */ + text = "invisible mon"; offset = glyph - GLYPH_INVIS_OFF; + } else if (glyph >= GLYPH_PET_OFF) { /* a pet */ + text = "pet"; offset = glyph - GLYPH_PET_OFF; + } else { /* a monster */ + text = "monster"; offset = glyph; + } + + impossible("show_glyph: bad pos %d %d with glyph %d [%s %d].", + x, y, glyph, text, offset); + return; + } + + if (glyph >= MAX_GLYPH) { + impossible("show_glyph: bad glyph %d [max %d] at (%d,%d).", + glyph, MAX_GLYPH, x, y); + return; + } + + if (gbuf[y][x].glyph != glyph) { + gbuf[y][x].glyph = glyph; + gbuf[y][x].new = 1; + if (gbuf_start[y] > x) gbuf_start[y] = x; + if (gbuf_stop[y] < x) gbuf_stop[y] = x; + } +} + + +/* + * Reset the changed glyph borders so that none of the 3rd screen has + * changed. + */ +#define reset_glyph_bbox() \ + { \ + int i; \ + \ + for (i = 0; i < ROWNO; i++) { \ + gbuf_start[i] = COLNO-1; \ + gbuf_stop[i] = 0; \ + } \ + } + + +static gbuf_entry nul_gbuf = { 0, cmap_to_glyph(S_stone) }; +/* + * Turn the 3rd screen into stone. + */ +void +clear_glyph_buffer() +{ + register int x, y; + register gbuf_entry *gptr; + + for (y = 0; y < ROWNO; y++) { + gptr = &gbuf[y][0]; + for (x = COLNO; x; x--) { + *gptr++ = nul_gbuf; + } + } + reset_glyph_bbox(); +} + +/* + * Assumes that the indicated positions are filled with S_stone glyphs. + */ +void +row_refresh(start,stop,y) + int start,stop,y; +{ + register int x; + + for (x = start; x <= stop; x++) + if (gbuf[y][x].glyph != cmap_to_glyph(S_stone)) + print_glyph(WIN_MAP,x,y,gbuf[y][x].glyph); +} + +void +cls() +{ + display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ + flags.botlx = 1; /* force update of botl window */ + clear_nhwindow(WIN_MAP); /* clear physical screen */ + + clear_glyph_buffer(); /* this is sort of an extra effort, but OK */ +} + +/* + * Synch the third screen with the display. + */ +void +flush_screen(cursor_on_u) + int cursor_on_u; +{ + /* Prevent infinite loops on errors: + * flush_screen->print_glyph->impossible->pline->flush_screen + */ + static boolean flushing = 0; + static boolean delay_flushing = 0; + register int x,y; + + if (cursor_on_u == -1) delay_flushing = !delay_flushing; + if (delay_flushing) return; + if (flushing) return; /* if already flushing then return */ + flushing = 1; + + for (y = 0; y < ROWNO; y++) { + register gbuf_entry *gptr = &gbuf[y][x = gbuf_start[y]]; + for (; x <= gbuf_stop[y]; gptr++, x++) + if (gptr->new) { + print_glyph(WIN_MAP,x,y,gptr->glyph); + gptr->new = 0; + } + } + + if (cursor_on_u) curs(WIN_MAP, u.ux,u.uy); /* move cursor to the hero */ + display_nhwindow(WIN_MAP, FALSE); + reset_glyph_bbox(); + flushing = 0; + if(flags.botl || flags.botlx) bot(); +} + +/* ========================================================================= */ + +/* + * back_to_glyph() + * + * Use the information in the rm structure at the given position to create + * a glyph of a background. + * + * I had to add a field in the rm structure (horizontal) so that we knew + * if open doors and secret doors were horizontal or vertical. Previously, + * the screen symbol had the horizontal/vertical information set at + * level generation time. + * + * I used the 'ladder' field (really doormask) for deciding if stairwells + * were up or down. I didn't want to check the upstairs and dnstairs + * variables. + */ +int +back_to_glyph(x,y) + xchar x,y; +{ + int idx; + struct rm *ptr = &(levl[x][y]); + + switch (ptr->typ) { + case SCORR: + case STONE: + idx = level.flags.arboreal ? S_tree : S_stone; + break; + case ROOM: idx = S_room; break; + case CORR: + idx = (ptr->waslit || flags.lit_corridor) ? S_litcorr : S_corr; + break; + case HWALL: + case VWALL: + case TLCORNER: + case TRCORNER: + case BLCORNER: + case BRCORNER: + case CROSSWALL: + case TUWALL: + case TDWALL: + case TLWALL: + case TRWALL: + case SDOOR: + idx = ptr->seenv ? wall_angle(ptr) : S_stone; + break; + case DOOR: + if (ptr->doormask) { + if (ptr->doormask & D_BROKEN) + idx = S_ndoor; + else if (ptr->doormask & D_ISOPEN) + idx = (ptr->horizontal) ? S_hodoor : S_vodoor; + else /* else is closed */ + idx = (ptr->horizontal) ? S_hcdoor : S_vcdoor; + } else + idx = S_ndoor; + break; + case IRONBARS: idx = S_bars; break; + case TREE: idx = S_tree; break; + case POOL: + case MOAT: idx = S_pool; break; + case STAIRS: + idx = (ptr->ladder & LA_DOWN) ? S_dnstair : S_upstair; + break; + case LADDER: + idx = (ptr->ladder & LA_DOWN) ? S_dnladder : S_upladder; + break; + case FOUNTAIN: idx = S_fountain; break; + case SINK: idx = S_sink; break; + case ALTAR: idx = S_altar; break; + case GRAVE: idx = S_grave; break; + case THRONE: idx = S_throne; break; + case LAVAPOOL: idx = S_lava; break; + case ICE: idx = S_ice; break; + case AIR: idx = S_air; break; + case CLOUD: idx = S_cloud; break; + case WATER: idx = S_water; break; + case DBWALL: + idx = (ptr->horizontal) ? S_hcdbridge : S_vcdbridge; + break; + case DRAWBRIDGE_UP: + switch(ptr->drawbridgemask & DB_UNDER) { + case DB_MOAT: idx = S_pool; break; + case DB_LAVA: idx = S_lava; break; + case DB_ICE: idx = S_ice; break; + case DB_FLOOR: idx = S_room; break; + default: + impossible("Strange db-under: %d", + ptr->drawbridgemask & DB_UNDER); + idx = S_room; /* something is better than nothing */ + break; + } + break; + case DRAWBRIDGE_DOWN: + idx = (ptr->horizontal) ? S_hodbridge : S_vodbridge; + break; + default: + impossible("back_to_glyph: unknown level type [ = %d ]",ptr->typ); + idx = S_room; + break; + } + + return cmap_to_glyph(idx); +} + + +/* + * swallow_to_glyph() + * + * Convert a monster number and a swallow location into the correct glyph. + * If you don't want a patchwork monster while hallucinating, decide on + * a random monster in swallowed() and don't use what_mon() here. + */ +STATIC_OVL int +swallow_to_glyph(mnum, loc) + int mnum; + int loc; +{ + if (loc < S_sw_tl || S_sw_br < loc) { + impossible("swallow_to_glyph: bad swallow location"); + loc = S_sw_br; + } + return ((int) (what_mon(mnum)<<3) | (loc - S_sw_tl)) + GLYPH_SWALLOW_OFF; +} + + + +/* + * zapdir_to_glyph() + * + * Change the given zap direction and beam type into a glyph. Each beam + * type has four glyphs, one for each of the symbols below. The order of + * the zap symbols [0-3] as defined in rm.h are: + * + * | S_vbeam ( 0, 1) or ( 0,-1) + * - S_hbeam ( 1, 0) or (-1, 0) + * \ S_lslant ( 1, 1) or (-1,-1) + * / S_rslant (-1, 1) or ( 1,-1) + */ +int +zapdir_to_glyph(dx, dy, beam_type) + register int dx, dy; + int beam_type; +{ + if (beam_type >= NUM_ZAP) { + impossible("zapdir_to_glyph: illegal beam type"); + beam_type = 0; + } + dx = (dx == dy) ? 2 : (dx && dy) ? 3 : dx ? 1 : 0; + + return ((int) ((beam_type << 2) | dx)) + GLYPH_ZAP_OFF; +} + + +/* + * Utility routine for dowhatis() used to find out the glyph displayed at + * the location. This isn't necessarily the same as the glyph in the levl + * structure, so we must check the "third screen". + */ +int +glyph_at(x, y) + xchar x,y; +{ + if(x < 0 || y < 0 || x >= COLNO || y >= ROWNO) + return cmap_to_glyph(S_room); /* XXX */ + return gbuf[y][x].glyph; +} + + +/* ------------------------------------------------------------------------- */ +/* Wall Angle -------------------------------------------------------------- */ + +/*#define WA_VERBOSE*/ /* give (x,y) locations for all "bad" spots */ + +#ifdef WA_VERBOSE + +static const char *FDECL(type_to_name, (int)); +static void FDECL(error4, (int,int,int,int,int,int)); + +static int bad_count[MAX_TYPE]; /* count of positions flagged as bad */ +static const char *type_names[MAX_TYPE] = { + "STONE", "VWALL", "HWALL", "TLCORNER", + "TRCORNER", "BLCORNER", "BRCORNER", "CROSSWALL", + "TUWALL", "TDWALL", "TLWALL", "TRWALL", + "DBWALL", "SDOOR", "SCORR", "POOL", + "MOAT", "WATER", "DRAWBRIDGE_UP","LAVAPOOL", + "DOOR", "CORR", "ROOM", "STAIRS", + "LADDER", "FOUNTAIN", "THRONE", "SINK", + "ALTAR", "ICE", "DRAWBRIDGE_DOWN","AIR", + "CLOUD" +}; + + +static const char * +type_to_name(type) + int type; +{ + return (type < 0 || type > MAX_TYPE) ? "unknown" : type_names[type]; +} + +static void +error4(x, y, a, b, c, dd) + int x, y, a, b, c, dd; +{ + pline("set_wall_state: %s @ (%d,%d) %s%s%s%s", + type_to_name(levl[x][y].typ), x, y, + a ? "1":"", b ? "2":"", c ? "3":"", dd ? "4":""); + bad_count[levl[x][y].typ]++; +} +#endif /* WA_VERBOSE */ + +/* + * Return 'which' if position is implies an unfinshed exterior. Return + * zero otherwise. Unfinished implies outer area is rock or a corridor. + * + * Things that are ambigious: lava + */ +STATIC_OVL int +check_pos(x, y, which) + int x, y, which; +{ + int type; + if (!isok(x,y)) return which; + type = levl[x][y].typ; + if (IS_ROCK(type) || type == CORR || type == SCORR) return which; + return 0; +} + +/* Return TRUE if more than one is non-zero. */ +/*ARGSUSED*/ +#ifdef WA_VERBOSE +STATIC_OVL boolean +more_than_one(x, y, a, b, c) + int x, y, a, b, c; +{ + if ((a && (b|c)) || (b && (a|c)) || (c && (a|b))) { + error4(x,y,a,b,c,0); + return TRUE; + } + return FALSE; +} +#else +#define more_than_one(x, y, a, b, c) (((a) && ((b)|(c))) || ((b) && ((a)|(c))) || ((c) && ((a)|(b)))) +#endif + +/* Return the wall mode for a T wall. */ +STATIC_OVL int +set_twall(x0,y0, x1,y1, x2,y2, x3,y3) +int x0,y0, x1,y1, x2,y2, x3,y3; +{ + int wmode, is_1, is_2, is_3; + + is_1 = check_pos(x1, y1, WM_T_LONG); + is_2 = check_pos(x2, y2, WM_T_BL); + is_3 = check_pos(x3, y3, WM_T_BR); + if (more_than_one(x0, y0, is_1, is_2, is_3)) { + wmode = 0; + } else { + wmode = is_1 + is_2 + is_3; + } + return wmode; +} + +/* Return wall mode for a horizontal or vertical wall. */ +STATIC_OVL int +set_wall(x, y, horiz) + int x, y, horiz; +{ + int wmode, is_1, is_2; + + if (horiz) { + is_1 = check_pos(x,y-1, WM_W_TOP); + is_2 = check_pos(x,y+1, WM_W_BOTTOM); + } else { + is_1 = check_pos(x-1,y, WM_W_LEFT); + is_2 = check_pos(x+1,y, WM_W_RIGHT); + } + if (more_than_one(x, y, is_1, is_2, 0)) { + wmode = 0; + } else { + wmode = is_1 + is_2; + } + return wmode; +} + + +/* Return a wall mode for a corner wall. (x4,y4) is the "inner" position. */ +STATIC_OVL int +set_corn(x1,y1, x2,y2, x3,y3, x4,y4) + int x1, y1, x2, y2, x3, y3, x4, y4; +{ + int wmode, is_1, is_2, is_3, is_4; + + is_1 = check_pos(x1, y1, 1); + is_2 = check_pos(x2, y2, 1); + is_3 = check_pos(x3, y3, 1); + is_4 = check_pos(x4, y4, 1); /* inner location */ + + /* + * All 4 should not be true. So if the inner location is rock, + * use it. If all of the outer 3 are true, use outer. We currently + * can't cover the case where only part of the outer is rock, so + * we just say that all the walls are finished (if not overridden + * by the inner section). + */ + if (is_4) { + wmode = WM_C_INNER; + } else if (is_1 && is_2 && is_3) + wmode = WM_C_OUTER; + else + wmode = 0; /* finished walls on all sides */ + + return wmode; +} + +/* Return mode for a crosswall. */ +STATIC_OVL int +set_crosswall(x, y) + int x, y; +{ + int wmode, is_1, is_2, is_3, is_4; + + is_1 = check_pos(x-1, y-1, 1); + is_2 = check_pos(x+1, y-1, 1); + is_3 = check_pos(x+1, y+1, 1); + is_4 = check_pos(x-1, y+1, 1); + + wmode = is_1+is_2+is_3+is_4; + if (wmode > 1) { + if (is_1 && is_3 && (is_2+is_4 == 0)) { + wmode = WM_X_TLBR; + } else if (is_2 && is_4 && (is_1+is_3 == 0)) { + wmode = WM_X_BLTR; + } else { +#ifdef WA_VERBOSE + error4(x,y,is_1,is_2,is_3,is_4); +#endif + wmode = 0; + } + } else if (is_1) + wmode = WM_X_TL; + else if (is_2) + wmode = WM_X_TR; + else if (is_3) + wmode = WM_X_BR; + else if (is_4) + wmode = WM_X_BL; + + return wmode; +} + +/* Called from mklev. Scan the level and set the wall modes. */ +void +set_wall_state() +{ + int x, y; + int wmode; + struct rm *lev; + +#ifdef WA_VERBOSE + for (x = 0; x < MAX_TYPE; x++) bad_count[x] = 0; +#endif + + for (x = 0; x < COLNO; x++) + for (lev = &levl[x][0], y = 0; y < ROWNO; y++, lev++) { + switch (lev->typ) { + case SDOOR: + wmode = set_wall(x, y, (int) lev->horizontal); + break; + case VWALL: + wmode = set_wall(x, y, 0); + break; + case HWALL: + wmode = set_wall(x, y, 1); + break; + case TDWALL: + wmode = set_twall(x,y, x,y-1, x-1,y+1, x+1,y+1); + break; + case TUWALL: + wmode = set_twall(x,y, x,y+1, x+1,y-1, x-1,y-1); + break; + case TLWALL: + wmode = set_twall(x,y, x+1,y, x-1,y-1, x-1,y+1); + break; + case TRWALL: + wmode = set_twall(x,y, x-1,y, x+1,y+1, x+1,y-1); + break; + case TLCORNER: + wmode = set_corn(x-1,y-1, x,y-1, x-1,y, x+1,y+1); + break; + case TRCORNER: + wmode = set_corn(x,y-1, x+1,y-1, x+1,y, x-1,y+1); + break; + case BLCORNER: + wmode = set_corn(x,y+1, x-1,y+1, x-1,y, x+1,y-1); + break; + case BRCORNER: + wmode = set_corn(x+1,y, x+1,y+1, x,y+1, x-1,y-1); + break; + case CROSSWALL: + wmode = set_crosswall(x, y); + break; + + default: + wmode = -1; /* don't set wall info */ + break; + } + + if (wmode >= 0) + lev->wall_info = (lev->wall_info & ~WM_MASK) | wmode; + } + +#ifdef WA_VERBOSE + /* check if any bad positions found */ + for (x = y = 0; x < MAX_TYPE; x++) + if (bad_count[x]) { + if (y == 0) { + y = 1; /* only print once */ + pline("set_wall_type: wall mode problems with: "); + } + pline("%s %d;", type_names[x], bad_count[x]); + } +#endif /* WA_VERBOSE */ +} + +/* ------------------------------------------------------------------------- */ +/* This matrix is used here and in vision.c. */ +unsigned char seenv_matrix[3][3] = { {SV2, SV1, SV0}, + {SV3, SVALL, SV7}, + {SV4, SV5, SV6} }; + +#define sign(z) ((z) < 0 ? -1 : ((z) > 0 ? 1 : 0)) + +/* Set the seen vector of lev as if seen from (x0,y0) to (x,y). */ +STATIC_OVL void +set_seenv(lev, x0, y0, x, y) + struct rm *lev; + int x0, y0, x, y; /* from, to */ +{ + int dx = x-x0, dy = y0-y; + lev->seenv |= seenv_matrix[sign(dy)+1][sign(dx)+1]; +} + +/* ------------------------------------------------------------------------- */ + +/* T wall types, one for each row in wall_matrix[][]. */ +#define T_d 0 +#define T_l 1 +#define T_u 2 +#define T_r 3 + +/* + * These are the column names of wall_matrix[][]. They are the "results" + * of a tdwall pattern match. All T walls are rotated so they become + * a tdwall. Then we do a single pattern match, but return the + * correct result for the original wall by using different rows for + * each of the wall types. + */ +#define T_stone 0 +#define T_tlcorn 1 +#define T_trcorn 2 +#define T_hwall 3 +#define T_tdwall 4 + +static const int wall_matrix[4][5] = { + { S_stone, S_tlcorn, S_trcorn, S_hwall, S_tdwall }, /* tdwall */ + { S_stone, S_trcorn, S_brcorn, S_vwall, S_tlwall }, /* tlwall */ + { S_stone, S_brcorn, S_blcorn, S_hwall, S_tuwall }, /* tuwall */ + { S_stone, S_blcorn, S_tlcorn, S_vwall, S_trwall }, /* trwall */ +}; + + +/* Cross wall types, one for each "solid" quarter. Rows of cross_matrix[][]. */ +#define C_bl 0 +#define C_tl 1 +#define C_tr 2 +#define C_br 3 + +/* + * These are the column names for cross_matrix[][]. They express results + * in C_br (bottom right) terms. All crosswalls with a single solid + * quarter are rotated so the solid section is at the bottom right. + * We pattern match on that, but return the correct result depending + * on which row we'ere looking at. + */ +#define C_trcorn 0 +#define C_brcorn 1 +#define C_blcorn 2 +#define C_tlwall 3 +#define C_tuwall 4 +#define C_crwall 5 + +static const int cross_matrix[4][6] = { + { S_brcorn, S_blcorn, S_tlcorn, S_tuwall, S_trwall, S_crwall }, + { S_blcorn, S_tlcorn, S_trcorn, S_trwall, S_tdwall, S_crwall }, + { S_tlcorn, S_trcorn, S_brcorn, S_tdwall, S_tlwall, S_crwall }, + { S_trcorn, S_brcorn, S_blcorn, S_tlwall, S_tuwall, S_crwall }, +}; + + +/* Print out a T wall warning and all interesting info. */ +STATIC_OVL void +t_warn(lev) + struct rm *lev; +{ + static const char warn_str[] = "wall_angle: %s: case %d: seenv = 0x%x"; + const char *wname; + + if (lev->typ == TUWALL) wname = "tuwall"; + else if (lev->typ == TLWALL) wname = "tlwall"; + else if (lev->typ == TRWALL) wname = "trwall"; + else if (lev->typ == TDWALL) wname = "tdwall"; + else wname = "unknown"; + impossible(warn_str, wname, lev->wall_info & WM_MASK, + (unsigned int) lev->seenv); +} + + +/* + * Return the correct graphics character index using wall type, wall mode, + * and the seen vector. It is expected that seenv is non zero. + * + * All T-wall vectors are rotated to be TDWALL. All single crosswall + * blocks are rotated to bottom right. All double crosswall are rotated + * to W_X_BLTR. All results are converted back. + * + * The only way to understand this is to take out pen and paper and + * draw diagrams. See rm.h for more details on the wall modes and + * seen vector (SV). + */ +STATIC_OVL int +wall_angle(lev) + struct rm *lev; +{ + register unsigned int seenv = lev->seenv & 0xff; + const int *row; + int col, idx; + +#define only(sv, bits) (((sv) & (bits)) && ! ((sv) & ~(bits))) + switch (lev->typ) { + case TUWALL: + row = wall_matrix[T_u]; + seenv = (seenv >> 4 | seenv << 4) & 0xff;/* rotate to tdwall */ + goto do_twall; + case TLWALL: + row = wall_matrix[T_l]; + seenv = (seenv >> 2 | seenv << 6) & 0xff;/* rotate to tdwall */ + goto do_twall; + case TRWALL: + row = wall_matrix[T_r]; + seenv = (seenv >> 6 | seenv << 2) & 0xff;/* rotate to tdwall */ + goto do_twall; + case TDWALL: + row = wall_matrix[T_d]; +do_twall: + switch (lev->wall_info & WM_MASK) { + case 0: + if (seenv == SV4) { + col = T_tlcorn; + } else if (seenv == SV6) { + col = T_trcorn; + } else if (seenv & (SV3|SV5|SV7) || + ((seenv & SV4) && (seenv & SV6))) { + col = T_tdwall; + } else if (seenv & (SV0|SV1|SV2)) { + col = (seenv & (SV4|SV6) ? T_tdwall : T_hwall); + } else { + t_warn(lev); + col = T_stone; + } + break; + case WM_T_LONG: + if (seenv & (SV3|SV4) && !(seenv & (SV5|SV6|SV7))) { + col = T_tlcorn; + } else if (seenv&(SV6|SV7) && !(seenv&(SV3|SV4|SV5))) { + col = T_trcorn; + } else if ((seenv & SV5) || + ((seenv & (SV3|SV4)) && (seenv & (SV6|SV7)))) { + col = T_tdwall; + } else { + /* only SV0|SV1|SV2 */ + if (! only(seenv, SV0|SV1|SV2) ) + t_warn(lev); + col = T_stone; + } + break; + case WM_T_BL: +#if 0 /* older method, fixed */ + if (only(seenv, SV4|SV5)) { + col = T_tlcorn; + } else if ((seenv & (SV0|SV1|SV2)) && + only(seenv, SV0|SV1|SV2|SV6|SV7)) { + col = T_hwall; + } else if (seenv & SV3 || + ((seenv & (SV0|SV1|SV2)) && (seenv & (SV4|SV5)))) { + col = T_tdwall; + } else { + if (seenv != SV6) + t_warn(lev); + col = T_stone; + } +#endif /* 0 */ + if (only(seenv, SV4|SV5)) + col = T_tlcorn; + else if ((seenv & (SV0|SV1|SV2|SV7)) && + !(seenv & (SV3|SV4|SV5))) + col = T_hwall; + else if (only(seenv, SV6)) + col = T_stone; + else + col = T_tdwall; + break; + case WM_T_BR: +#if 0 /* older method, fixed */ + if (only(seenv, SV5|SV6)) { + col = T_trcorn; + } else if ((seenv & (SV0|SV1|SV2)) && + only(seenv, SV0|SV1|SV2|SV3|SV4)) { + col = T_hwall; + } else if (seenv & SV7 || + ((seenv & (SV0|SV1|SV2)) && (seenv & (SV5|SV6)))) { + col = T_tdwall; + } else { + if (seenv != SV4) + t_warn(lev); + col = T_stone; + } +#endif /* 0 */ + if (only(seenv, SV5|SV6)) + col = T_trcorn; + else if ((seenv & (SV0|SV1|SV2|SV3)) && + !(seenv & (SV5|SV6|SV7))) + col = T_hwall; + else if (only(seenv, SV4)) + col = T_stone; + else + col = T_tdwall; + + break; + default: + impossible("wall_angle: unknown T wall mode %d", + lev->wall_info & WM_MASK); + col = T_stone; + break; + } + idx = row[col]; + break; + + case SDOOR: + if (lev->horizontal) goto horiz; + /* fall through */ + case VWALL: + switch (lev->wall_info & WM_MASK) { + case 0: idx = seenv ? S_vwall : S_stone; break; + case 1: idx = seenv & (SV1|SV2|SV3|SV4|SV5) ? S_vwall : + S_stone; + break; + case 2: idx = seenv & (SV0|SV1|SV5|SV6|SV7) ? S_vwall : + S_stone; + break; + default: + impossible("wall_angle: unknown vwall mode %d", + lev->wall_info & WM_MASK); + idx = S_stone; + break; + } + break; + + case HWALL: +horiz: + switch (lev->wall_info & WM_MASK) { + case 0: idx = seenv ? S_hwall : S_stone; break; + case 1: idx = seenv & (SV3|SV4|SV5|SV6|SV7) ? S_hwall : + S_stone; + break; + case 2: idx = seenv & (SV0|SV1|SV2|SV3|SV7) ? S_hwall : + S_stone; + break; + default: + impossible("wall_angle: unknown hwall mode %d", + lev->wall_info & WM_MASK); + idx = S_stone; + break; + } + break; + +#define set_corner(idx, lev, which, outer, inner, name) \ + switch ((lev)->wall_info & WM_MASK) { \ + case 0: idx = which; break; \ + case WM_C_OUTER: idx = seenv & (outer) ? which : S_stone; break; \ + case WM_C_INNER: idx = seenv & ~(inner) ? which : S_stone; break; \ + default: \ + impossible("wall_angle: unknown %s mode %d", name, \ + (lev)->wall_info & WM_MASK); \ + idx = S_stone; \ + break; \ + } + + case TLCORNER: + set_corner(idx, lev, S_tlcorn, (SV3|SV4|SV5), SV4, "tlcorn"); + break; + case TRCORNER: + set_corner(idx, lev, S_trcorn, (SV5|SV6|SV7), SV6, "trcorn"); + break; + case BLCORNER: + set_corner(idx, lev, S_blcorn, (SV1|SV2|SV3), SV2, "blcorn"); + break; + case BRCORNER: + set_corner(idx, lev, S_brcorn, (SV7|SV0|SV1), SV0, "brcorn"); + break; + + + case CROSSWALL: + switch (lev->wall_info & WM_MASK) { + case 0: + if (seenv == SV0) + idx = S_brcorn; + else if (seenv == SV2) + idx = S_blcorn; + else if (seenv == SV4) + idx = S_tlcorn; + else if (seenv == SV6) + idx = S_trcorn; + else if (!(seenv & ~(SV0|SV1|SV2)) && + (seenv & SV1 || seenv == (SV0|SV2))) + idx = S_tuwall; + else if (!(seenv & ~(SV2|SV3|SV4)) && + (seenv & SV3 || seenv == (SV2|SV4))) + idx = S_trwall; + else if (!(seenv & ~(SV4|SV5|SV6)) && + (seenv & SV5 || seenv == (SV4|SV6))) + idx = S_tdwall; + else if (!(seenv & ~(SV0|SV6|SV7)) && + (seenv & SV7 || seenv == (SV0|SV6))) + idx = S_tlwall; + else + idx = S_crwall; + break; + + case WM_X_TL: + row = cross_matrix[C_tl]; + seenv = (seenv >> 4 | seenv << 4) & 0xff; + goto do_crwall; + case WM_X_TR: + row = cross_matrix[C_tr]; + seenv = (seenv >> 6 | seenv << 2) & 0xff; + goto do_crwall; + case WM_X_BL: + row = cross_matrix[C_bl]; + seenv = (seenv >> 2 | seenv << 6) & 0xff; + goto do_crwall; + case WM_X_BR: + row = cross_matrix[C_br]; +do_crwall: + if (seenv == SV4) + idx = S_stone; + else { + seenv = seenv & ~SV4; /* strip SV4 */ + if (seenv == SV0) { + col = C_brcorn; + } else if (seenv & (SV2|SV3)) { + if (seenv & (SV5|SV6|SV7)) + col = C_crwall; + else if (seenv & (SV0|SV1)) + col = C_tuwall; + else + col = C_blcorn; + } else if (seenv & (SV5|SV6)) { + if (seenv & (SV1|SV2|SV3)) + col = C_crwall; + else if (seenv & (SV0|SV7)) + col = C_tlwall; + else + col = C_trcorn; + } else if (seenv & SV1) { + col = seenv & SV7 ? C_crwall : C_tuwall; + } else if (seenv & SV7) { + col = seenv & SV1 ? C_crwall : C_tlwall; + } else { + impossible( + "wall_angle: bottom of crwall check"); + col = C_crwall; + } + + idx = row[col]; + } + break; + + case WM_X_TLBR: + if ( only(seenv, SV1|SV2|SV3) ) + idx = S_blcorn; + else if ( only(seenv, SV5|SV6|SV7) ) + idx = S_trcorn; + else if ( only(seenv, SV0|SV4) ) + idx = S_stone; + else + idx = S_crwall; + break; + + case WM_X_BLTR: + if ( only(seenv, SV0|SV1|SV7) ) + idx = S_brcorn; + else if ( only(seenv, SV3|SV4|SV5) ) + idx = S_tlcorn; + else if ( only(seenv, SV2|SV6) ) + idx = S_stone; + else + idx = S_crwall; + break; + + default: + impossible("wall_angle: unknown crosswall mode"); + idx = S_stone; + break; + } + break; + + default: + impossible("wall_angle: unexpected wall type %d", lev->typ); + idx = S_stone; + } + return idx; +} + +/*display.c*/ diff --git a/src/dlb.c b/src/dlb.c new file mode 100644 index 0000000..5d42326 --- /dev/null +++ b/src/dlb.c @@ -0,0 +1,544 @@ +/* SCCS Id: @(#)dlb.c 3.4 1997/07/29 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "config.h" +#include "dlb.h" + +#ifdef __DJGPP__ +#include +#endif + +#define DATAPREFIX 4 + +#ifdef DLB +/* + * Data librarian. Present a STDIO-like interface to NetHack while + * multiplexing on one or more "data libraries". If a file is not found + * in a given library, look for it outside the libraries. + */ + +typedef struct dlb_procs { + boolean NDECL((*dlb_init_proc)); + void NDECL((*dlb_cleanup_proc)); + boolean FDECL((*dlb_fopen_proc), (DLB_P,const char *,const char *)); + int FDECL((*dlb_fclose_proc), (DLB_P)); + int FDECL((*dlb_fread_proc), (char *,int,int,DLB_P)); + int FDECL((*dlb_fseek_proc), (DLB_P,long,int)); + char *FDECL((*dlb_fgets_proc), (char *,int,DLB_P)); + int FDECL((*dlb_fgetc_proc), (DLB_P)); + long FDECL((*dlb_ftell_proc), (DLB_P)); +} dlb_procs_t; + +/* without extern.h via hack.h, these haven't been declared for us */ +extern FILE *FDECL(fopen_datafile, (const char *,const char *,int)); + +#ifdef DLBLIB +/* + * Library Implementation: + * + * When initialized, we open all library files and read in their tables + * of contents. The library files stay open all the time. When + * a open is requested, the libraries' directories are searched. If + * successful, we return a descriptor that contains the library, file + * size, and current file mark. This descriptor is used for all + * successive calls. + * + * The ability to open more than one library is supported but used + * only in the Amiga port (the second library holds the sound files). + * For Unix, the idea would be to split the NetHack library + * into text and binary parts, where the text version could be shared. + */ + +#define MAX_LIBS 4 +static library dlb_libs[MAX_LIBS]; + +static boolean FDECL(readlibdir,(library *lp)); +static boolean FDECL(find_file,(const char *name, library **lib, long *startp, + long *sizep)); +static boolean NDECL(lib_dlb_init); +static void NDECL(lib_dlb_cleanup); +static boolean FDECL(lib_dlb_fopen,(dlb *, const char *, const char *)); +static int FDECL(lib_dlb_fclose,(dlb *)); +static int FDECL(lib_dlb_fread,(char *, int, int, dlb *)); +static int FDECL(lib_dlb_fseek,(dlb *, long, int)); +static char *FDECL(lib_dlb_fgets,(char *, int, dlb *)); +static int FDECL(lib_dlb_fgetc,(dlb *)); +static long FDECL(lib_dlb_ftell,(dlb *)); + +/* not static because shared with dlb_main.c */ +boolean FDECL(open_library,(const char *lib_name, library *lp)); +void FDECL(close_library,(library *lp)); + +/* without extern.h via hack.h, these haven't been declared for us */ +extern char *FDECL(eos, (char *)); + + + +/* + * Read the directory out of the library. Return 1 if successful, + * 0 if it failed. + * + * NOTE: An improvement of the file structure should be the file + * size as part of the directory entry or perhaps in place of the + * offset -- the offset can be calculated by a running tally of + * the sizes. + * + * Library file structure: + * + * HEADER: + * %3ld library FORMAT revision (currently rev 1) + * %1c space + * %8ld # of files in archive (includes 1 for directory) + * %1c space + * %8ld size of allocation for string space for directory names + * %1c space + * %8ld library offset - sanity check - lseek target for start of first file + * %1c space + * %8ld size - sanity check - byte size of complete archive file + * + * followed by one DIRECTORY entry for each file in the archive, including + * the directory itself: + * %1c handling information (compression, etc.) Always ' ' in rev 1. + * %s file name + * %1c space + * %8ld offset in archive file of start of this file + * %c newline + * + * followed by the contents of the files + */ +#define DLB_MIN_VERS 1 /* min library version readable by this code */ +#define DLB_MAX_VERS 1 /* max library version readable by this code */ + +/* + * Read the directory from the library file. This will allocate and + * fill in our globals. The file pointer is reset back to position + * zero. If any part fails, leave nothing that needs to be deallocated. + * + * Return TRUE on success, FALSE on failure. + */ +static boolean +readlibdir(lp) + library *lp; /* library pointer to fill in */ +{ + int i; + char *sp; + long liboffset, totalsize; + + if (fscanf(lp->fdata, "%ld %ld %ld %ld %ld\n", + &lp->rev,&lp->nentries,&lp->strsize,&liboffset,&totalsize) != 5) + return FALSE; + if (lp->rev > DLB_MAX_VERS || lp->rev < DLB_MIN_VERS) return FALSE; + + lp->dir = (libdir *) alloc(lp->nentries * sizeof(libdir)); + lp->sspace = (char *) alloc(lp->strsize); + + /* read in each directory entry */ + for (i = 0, sp = lp->sspace; i < lp->nentries; i++) { + lp->dir[i].fname = sp; + if (fscanf(lp->fdata, "%c%s %ld\n", + &lp->dir[i].handling, sp, &lp->dir[i].foffset) != 3) { + free((genericptr_t) lp->dir); + free((genericptr_t) lp->sspace); + lp->dir = (libdir *) 0; + lp->sspace = (char *) 0; + return FALSE; + } + sp = eos(sp) + 1; + } + + /* calculate file sizes using offset information */ + for (i = 0; i < lp->nentries; i++) { + if (i == lp->nentries - 1) + lp->dir[i].fsize = totalsize - lp->dir[i].foffset; + else + lp->dir[i].fsize = lp->dir[i+1].foffset - lp->dir[i].foffset; + } + + (void) fseek(lp->fdata, 0L, SEEK_SET); /* reset back to zero */ + lp->fmark = 0; + + return TRUE; +} + +/* + * Look for the file in our directory structure. Return 1 if successful, + * 0 if not found. Fill in the size and starting position. + */ +static boolean +find_file(name, lib, startp, sizep) + const char *name; + library **lib; + long *startp, *sizep; +{ + int i, j; + library *lp; + + for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) { + lp = &dlb_libs[i]; + for (j = 0; j < lp->nentries; j++) { + if (FILENAME_CMP(name, lp->dir[j].fname) == 0) { + *lib = lp; + *startp = lp->dir[j].foffset; + *sizep = lp->dir[j].fsize; + return TRUE; + } + } + } + *lib = (library *) 0; + *startp = *sizep = 0; + return FALSE; +} + +/* + * Open the library of the given name and fill in the given library + * structure. Return TRUE if successful, FALSE otherwise. + */ +boolean +open_library(lib_name, lp) + const char *lib_name; + library *lp; +{ + boolean status = FALSE; + + lp->fdata = fopen_datafile(lib_name, RDBMODE, DATAPREFIX); + if (lp->fdata) { + if (readlibdir(lp)) { + status = TRUE; + } else { + (void) fclose(lp->fdata); + lp->fdata = (FILE *) 0; + } + } + return status; +} + +void +close_library(lp) + library *lp; +{ + (void) fclose(lp->fdata); + free((genericptr_t) lp->dir); + free((genericptr_t) lp->sspace); + + (void) memset((char *)lp, 0, sizeof(library)); +} + +/* + * Open the library file once using stdio. Keep it open, but + * keep track of the file position. + */ +static boolean +lib_dlb_init() +{ + /* zero out array */ + (void) memset((char *)&dlb_libs[0], 0, sizeof(dlb_libs)); + + /* To open more than one library, add open library calls here. */ + if (!open_library(DLBFILE, &dlb_libs[0])) return FALSE; +#ifdef DLBFILE2 + if (!open_library(DLBFILE2, &dlb_libs[1])) { + close_library(&dlb_libs[0]); + return FALSE; + } +#endif + return TRUE; +} + +static void +lib_dlb_cleanup() +{ + int i; + + /* close the data file(s) */ + for (i = 0; i < MAX_LIBS && dlb_libs[i].fdata; i++) + close_library(&dlb_libs[i]); +} + +static boolean +lib_dlb_fopen(dp, name, mode) + dlb *dp; + const char *name, *mode; +{ + long start, size; + library *lp; + + /* look up file in directory */ + if (find_file(name, &lp, &start, &size)) { + dp->lib = lp; + dp->start = start; + dp->size = size; + dp->mark = 0; + return TRUE; + } + + return FALSE; /* failed */ +} + +static int +lib_dlb_fclose(dp) + dlb *dp; +{ + /* nothing needs to be done */ + return 0; +} + +static int +lib_dlb_fread(buf, size, quan, dp) + char *buf; + int size, quan; + dlb *dp; +{ + long pos, nread, nbytes; + + /* make sure we don't read into the next file */ + if ((dp->size - dp->mark) < (size * quan)) + quan = (dp->size - dp->mark) / size; + if (quan == 0) return 0; + + pos = dp->start + dp->mark; + if (dp->lib->fmark != pos) { + fseek(dp->lib->fdata, pos, SEEK_SET); /* check for error??? */ + dp->lib->fmark = pos; + } + + nread = fread(buf, size, quan, dp->lib->fdata); + nbytes = nread * size; + dp->mark += nbytes; + dp->lib->fmark += nbytes; + + return nread; +} + +static int +lib_dlb_fseek(dp, pos, whence) + dlb *dp; + long pos; + int whence; +{ + long curpos; + + switch (whence) { + case SEEK_CUR: curpos = dp->mark + pos; break; + case SEEK_END: curpos = dp->size - pos; break; + default: /* set */ curpos = pos; break; + } + if (curpos < 0) curpos = 0; + if (curpos > dp->size) curpos = dp->size; + + dp->mark = curpos; + return 0; +} + +static char * +lib_dlb_fgets(buf, len, dp) + char *buf; + int len; + dlb *dp; +{ + int i; + char *bp, c = 0; + + if (len <= 0) return buf; /* sanity check */ + + /* return NULL on EOF */ + if (dp->mark >= dp->size) return (char *) 0; + + len--; /* save room for null */ + for (i = 0, bp = buf; + i < len && dp->mark < dp->size && c != '\n'; i++, bp++) { + if (dlb_fread(bp, 1, 1, dp) <= 0) break; /* EOF or error */ + c = *bp; + } + *bp = '\0'; + +#if defined(MSDOS) || defined(WIN32) + if ((bp = index(buf, '\r')) != 0) { + *bp++ = '\n'; + *bp = '\0'; + } +#endif + + return buf; +} + +static int +lib_dlb_fgetc(dp) + dlb *dp; +{ + char c; + + if (lib_dlb_fread(&c, 1, 1, dp) != 1) return EOF; + return (int) c; +} + + +static long +lib_dlb_ftell(dp) + dlb *dp; +{ + return dp->mark; +} + +const dlb_procs_t lib_dlb_procs = { + lib_dlb_init, + lib_dlb_cleanup, + lib_dlb_fopen, + lib_dlb_fclose, + lib_dlb_fread, + lib_dlb_fseek, + lib_dlb_fgets, + lib_dlb_fgetc, + lib_dlb_ftell +}; + +#endif /* DLBLIB */ + +#ifdef DLBRSRC +const dlb_procs_t rsrc_dlb_procs = { + rsrc_dlb_init, + rsrc_dlb_cleanup, + rsrc_dlb_fopen, + rsrc_dlb_fclose, + rsrc_dlb_fread, + rsrc_dlb_fseek, + rsrc_dlb_fgets, + rsrc_dlb_fgetc, + rsrc_dlb_ftell +}; +#endif + +/* Global wrapper functions ------------------------------------------------ */ + +#define do_dlb_init (*dlb_procs->dlb_init_proc) +#define do_dlb_cleanup (*dlb_procs->dlb_cleanup_proc) +#define do_dlb_fopen (*dlb_procs->dlb_fopen_proc) +#define do_dlb_fclose (*dlb_procs->dlb_fclose_proc) +#define do_dlb_fread (*dlb_procs->dlb_fread_proc) +#define do_dlb_fseek (*dlb_procs->dlb_fseek_proc) +#define do_dlb_fgets (*dlb_procs->dlb_fgets_proc) +#define do_dlb_fgetc (*dlb_procs->dlb_fgetc_proc) +#define do_dlb_ftell (*dlb_procs->dlb_ftell_proc) + +static const dlb_procs_t *dlb_procs; +static boolean dlb_initialized = FALSE; + +boolean +dlb_init() +{ + if (!dlb_initialized) { +#ifdef DLBLIB + dlb_procs = &lib_dlb_procs; +#endif +#ifdef DLBRSRC + dlb_procs = &rsrc_dlb_procs; +#endif + + if (dlb_procs) + dlb_initialized = do_dlb_init(); + } + + return dlb_initialized; +} + +void +dlb_cleanup() +{ + if (dlb_initialized) { + do_dlb_cleanup(); + dlb_initialized = FALSE; + } +} + +dlb * +dlb_fopen(name, mode) + const char *name, *mode; +{ + FILE *fp; + dlb *dp; + + if (!dlb_initialized) return (dlb *) 0; + + dp = (dlb *) alloc(sizeof(dlb)); + if (do_dlb_fopen(dp, name, mode)) + dp->fp = (FILE *) 0; + else if ((fp = fopen_datafile(name, mode, DATAPREFIX)) != 0) + dp->fp = fp; + else { + /* can't find anything */ + free((genericptr_t) dp); + dp = (dlb *) 0; + } + + return dp; +} + +int +dlb_fclose(dp) + dlb *dp; +{ + int ret = 0; + + if (dlb_initialized) { + if (dp->fp) ret = fclose(dp->fp); + else ret = do_dlb_fclose(dp); + + free((genericptr_t) dp); + } + return ret; +} + +int +dlb_fread(buf, size, quan, dp) + char *buf; + int size, quan; + dlb *dp; +{ + if (!dlb_initialized || size <= 0 || quan <= 0) return 0; + if (dp->fp) return (int) fread(buf, size, quan, dp->fp); + return do_dlb_fread(buf, size, quan, dp); +} + +int +dlb_fseek(dp, pos, whence) + dlb *dp; + long pos; + int whence; +{ + if (!dlb_initialized) return EOF; + if (dp->fp) return fseek(dp->fp, pos, whence); + return do_dlb_fseek(dp, pos, whence); +} + +char * +dlb_fgets(buf, len, dp) + char *buf; + int len; + dlb *dp; +{ + if (!dlb_initialized) return (char *) 0; + if (dp->fp) return fgets(buf, len, dp->fp); + return do_dlb_fgets(buf, len, dp); +} + +int +dlb_fgetc(dp) + dlb *dp; +{ + if (!dlb_initialized) return EOF; + if (dp->fp) return fgetc(dp->fp); + return do_dlb_fgetc(dp); +} + +long +dlb_ftell(dp) + dlb *dp; +{ + if (!dlb_initialized) return 0; + if (dp->fp) return ftell(dp->fp); + return do_dlb_ftell(dp); +} + +#endif /* DLB */ + +/*dlb.c*/ diff --git a/src/do.c b/src/do.c new file mode 100644 index 0000000..858777f --- /dev/null +++ b/src/do.c @@ -0,0 +1,1687 @@ +/* SCCS Id: @(#)do.c 3.4 2003/12/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Contains code for 'd', 'D' (drop), '>', '<' (up, down) */ + +#include "hack.h" +#include "lev.h" + +#ifdef SINKS +# ifdef OVLB +STATIC_DCL void FDECL(trycall, (struct obj *)); +# endif /* OVLB */ +STATIC_DCL void FDECL(dosinkring, (struct obj *)); +#endif /* SINKS */ + +STATIC_PTR int FDECL(drop, (struct obj *)); +STATIC_PTR int NDECL(wipeoff); + +#ifdef OVL0 +STATIC_DCL int FDECL(menu_drop, (int)); +#endif +#ifdef OVL2 +STATIC_DCL int NDECL(currentlevel_rewrite); +STATIC_DCL void NDECL(final_level); +/* static boolean FDECL(badspot, (XCHAR_P,XCHAR_P)); */ +#endif + +#ifdef OVLB + +static NEARDATA const char drop_types[] = + { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, 0 }; + +/* 'd' command: drop one inventory item */ +int +dodrop() +{ +#ifndef GOLDOBJ + int result, i = (invent || u.ugold) ? 0 : (SIZE(drop_types) - 1); +#else + int result, i = (invent) ? 0 : (SIZE(drop_types) - 1); +#endif + + if (*u.ushops) sellobj_state(SELL_DELIBERATE); + result = drop(getobj(&drop_types[i], "drop")); + if (*u.ushops) sellobj_state(SELL_NORMAL); + reset_occupations(); + + return result; +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* Called when a boulder is dropped, thrown, or pushed. If it ends up + * in a pool, it either fills the pool up or sinks away. In either case, + * it's gone for good... If the destination is not a pool, returns FALSE. + */ +boolean +boulder_hits_pool(otmp, rx, ry, pushing) +struct obj *otmp; +register int rx, ry; +boolean pushing; +{ + if (!otmp || otmp->otyp != BOULDER) + impossible("Not a boulder?"); + else if (!Is_waterlevel(&u.uz) && (is_pool(rx,ry) || is_lava(rx,ry))) { + boolean lava = is_lava(rx,ry), fills_up; + const char *what = waterbody_name(rx,ry); + schar ltyp = levl[rx][ry].typ; + int chance = rn2(10); /* water: 90%; lava: 10% */ + fills_up = lava ? chance == 0 : chance != 0; + + if (fills_up) { + struct trap *ttmp = t_at(rx, ry); + + if (ltyp == DRAWBRIDGE_UP) { + levl[rx][ry].drawbridgemask &= ~DB_UNDER; /* clear lava */ + levl[rx][ry].drawbridgemask |= DB_FLOOR; + } else + levl[rx][ry].typ = ROOM; + + if (ttmp) (void) delfloortrap(ttmp); + bury_objs(rx, ry); + + newsym(rx,ry); + if (pushing) { + You("push %s into the %s.", the(xname(otmp)), what); + if (flags.verbose && !Blind) + pline("Now you can cross it!"); + /* no splashing in this case */ + } + } + if (!fills_up || !pushing) { /* splashing occurs */ + if (!u.uinwater) { + if (pushing ? !Blind : cansee(rx,ry)) { + There("is a large splash as %s %s the %s.", + the(xname(otmp)), fills_up? "fills":"falls into", + what); + } else if (flags.soundok) + You_hear("a%s splash.", lava ? " sizzling" : ""); + wake_nearto(rx, ry, 40); + } + + if (fills_up && u.uinwater && distu(rx,ry) == 0) { + u.uinwater = 0; + docrt(); + vision_full_recalc = 1; + You("find yourself on dry land again!"); + } else if (lava && distu(rx,ry) <= 2) { + You("are hit by molten lava%c", + Fire_resistance ? '.' : '!'); + burn_away_slime(); + losehp(d((Fire_resistance ? 1 : 3), 6), + "molten lava", KILLED_BY); + } else if (!fills_up && flags.verbose && + (pushing ? !Blind : cansee(rx,ry))) + pline("It sinks without a trace!"); + } + + /* boulder is now gone */ + if (pushing) delobj(otmp); + else obfree(otmp, (struct obj *)0); + return TRUE; + } + return FALSE; +} + +/* Used for objects which sometimes do special things when dropped; must be + * called with the object not in any chain. Returns TRUE if the object goes + * away. + */ +boolean +flooreffects(obj,x,y,verb) +struct obj *obj; +int x,y; +const char *verb; +{ + struct trap *t; + struct monst *mtmp; + + if (obj->where != OBJ_FREE) + panic("flooreffects: obj not free"); + + /* make sure things like water_damage() have no pointers to follow */ + obj->nobj = obj->nexthere = (struct obj *)0; + + if (obj->otyp == BOULDER && boulder_hits_pool(obj, x, y, FALSE)) + return TRUE; + else if (obj->otyp == BOULDER && (t = t_at(x,y)) != 0 && + (t->ttyp==PIT || t->ttyp==SPIKED_PIT + || t->ttyp==TRAPDOOR || t->ttyp==HOLE)) { + if (((mtmp = m_at(x, y)) && mtmp->mtrapped) || + (u.utrap && u.ux == x && u.uy == y)) { + if (*verb) + pline_The("boulder %s into the pit%s.", + vtense((const char *)0, verb), + (mtmp) ? "" : " with you"); + if (mtmp) { + if (!passes_walls(mtmp->data) && + !throws_rocks(mtmp->data)) { + if (hmon(mtmp, obj, TRUE) && !is_whirly(mtmp->data)) + return FALSE; /* still alive */ + } + mtmp->mtrapped = 0; + } else { + if (!Passes_walls && !throws_rocks(youmonst.data)) { + losehp(rnd(15), "squished under a boulder", + NO_KILLER_PREFIX); + return FALSE; /* player remains trapped */ + } else u.utrap = 0; + } + } + if (*verb) { + if (Blind) { + if ((x == u.ux) && (y == u.uy)) + You_hear("a CRASH! beneath you."); + else + You_hear("the boulder %s.", verb); + } else if (cansee(x, y)) { + pline_The("boulder %s%s.", + t->tseen ? "" : "triggers and ", + t->ttyp == TRAPDOOR ? "plugs a trap door" : + t->ttyp == HOLE ? "plugs a hole" : + "fills a pit"); + } + } + deltrap(t); + obfree(obj, (struct obj *)0); + bury_objs(x, y); + newsym(x,y); + return TRUE; + } else if (is_lava(x, y)) { + return fire_damage(obj, FALSE, FALSE, x, y); + } else if (is_pool(x, y)) { + /* Reasonably bulky objects (arbitrary) splash when dropped. + * If you're floating above the water even small things make noise. + * Stuff dropped near fountains always misses */ + if ((Blind || (Levitation || Flying)) && flags.soundok && + ((x == u.ux) && (y == u.uy))) { + if (!Underwater) { + if (weight(obj) > 9) { + pline("Splash!"); + } else if (Levitation || Flying) { + pline("Plop!"); + } + } + map_background(x, y, 0); + newsym(x, y); + } + water_damage(obj, FALSE, FALSE); + } else if (u.ux == x && u.uy == y && + (!u.utrap || u.utraptype != TT_PIT) && + (t = t_at(x,y)) != 0 && t->tseen && + (t->ttyp==PIT || t->ttyp==SPIKED_PIT)) { + /* you escaped a pit and are standing on the precipice */ + if (Blind && flags.soundok) + You_hear("%s %s downwards.", + The(xname(obj)), otense(obj, "tumble")); + else + pline("%s %s into %s pit.", + The(xname(obj)), otense(obj, "tumble"), + the_your[t->madeby_u]); + } + return FALSE; +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +doaltarobj(obj) /* obj is an object dropped on an altar */ + register struct obj *obj; +{ + if (Blind) + return; + + /* KMH, conduct */ + u.uconduct.gnostic++; + + if ((obj->blessed || obj->cursed) && obj->oclass != COIN_CLASS) { + There("is %s flash as %s %s the altar.", + an(hcolor(obj->blessed ? NH_AMBER : NH_BLACK)), + doname(obj), otense(obj, "hit")); + if (!Hallucination) obj->bknown = 1; + } else { + pline("%s %s on the altar.", Doname2(obj), + otense(obj, "land")); + obj->bknown = 1; + } +} + +#ifdef SINKS +STATIC_OVL +void +trycall(obj) +register struct obj *obj; +{ + if(!objects[obj->otyp].oc_name_known && + !objects[obj->otyp].oc_uname) + docall(obj); +} + +STATIC_OVL +void +dosinkring(obj) /* obj is a ring being dropped over a kitchen sink */ +register struct obj *obj; +{ + register struct obj *otmp,*otmp2; + register boolean ideed = TRUE; + + You("drop %s down the drain.", doname(obj)); + obj->in_use = TRUE; /* block free identification via interrupt */ + switch(obj->otyp) { /* effects that can be noticed without eyes */ + case RIN_SEARCHING: + You("thought your %s got lost in the sink, but there it is!", + xname(obj)); + goto giveback; + case RIN_SLOW_DIGESTION: + pline_The("ring is regurgitated!"); +giveback: + obj->in_use = FALSE; + dropx(obj); + trycall(obj); + return; + case RIN_LEVITATION: + pline_The("sink quivers upward for a moment."); + break; + case RIN_POISON_RESISTANCE: + You("smell rotten %s.", makeplural(fruitname(FALSE))); + break; + case RIN_AGGRAVATE_MONSTER: + pline("Several flies buzz angrily around the sink."); + break; + case RIN_SHOCK_RESISTANCE: + pline("Static electricity surrounds the sink."); + break; + case RIN_CONFLICT: + You_hear("loud noises coming from the drain."); + break; + case RIN_SUSTAIN_ABILITY: /* KMH */ + pline_The("water flow seems fixed."); + break; + case RIN_GAIN_STRENGTH: + pline_The("water flow seems %ser now.", + (obj->spe<0) ? "weak" : "strong"); + break; + case RIN_GAIN_CONSTITUTION: + pline_The("water flow seems %ser now.", + (obj->spe<0) ? "less" : "great"); + break; + case RIN_INCREASE_ACCURACY: /* KMH */ + pline_The("water flow %s the drain.", + (obj->spe<0) ? "misses" : "hits"); + break; + case RIN_INCREASE_DAMAGE: + pline_The("water's force seems %ser now.", + (obj->spe<0) ? "small" : "great"); + break; + case RIN_HUNGER: + ideed = FALSE; + for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if (otmp != uball && otmp != uchain && + !obj_resists(otmp, 1, 99)) { + if (!Blind) { + pline("Suddenly, %s %s from the sink!", + doname(otmp), otense(otmp, "vanish")); + ideed = TRUE; + } + delobj(otmp); + } + } + break; + case MEAT_RING: + /* Not the same as aggravate monster; besides, it's obvious. */ + pline("Several flies buzz around the sink."); + break; + default: + ideed = FALSE; + break; + } + if(!Blind && !ideed && obj->otyp != RIN_HUNGER) { + ideed = TRUE; + switch(obj->otyp) { /* effects that need eyes */ + case RIN_ADORNMENT: + pline_The("faucets flash brightly for a moment."); + break; + case RIN_REGENERATION: + pline_The("sink looks as good as new."); + break; + case RIN_INVISIBILITY: + You("don't see anything happen to the sink."); + break; + case RIN_FREE_ACTION: + You("see the ring slide right down the drain!"); + break; + case RIN_SEE_INVISIBLE: + You("see some air in the sink."); + break; + case RIN_STEALTH: + pline_The("sink seems to blend into the floor for a moment."); + break; + case RIN_FIRE_RESISTANCE: + pline_The("hot water faucet flashes brightly for a moment."); + break; + case RIN_COLD_RESISTANCE: + pline_The("cold water faucet flashes brightly for a moment."); + break; + case RIN_PROTECTION_FROM_SHAPE_CHAN: + pline_The("sink looks nothing like a fountain."); + break; + case RIN_PROTECTION: + pline_The("sink glows %s for a moment.", + hcolor((obj->spe<0) ? NH_BLACK : NH_SILVER)); + break; + case RIN_WARNING: + pline_The("sink glows %s for a moment.", hcolor(NH_WHITE)); + break; + case RIN_TELEPORTATION: + pline_The("sink momentarily vanishes."); + break; + case RIN_TELEPORT_CONTROL: + pline_The("sink looks like it is being beamed aboard somewhere."); + break; + case RIN_POLYMORPH: + pline_The("sink momentarily looks like a fountain."); + break; + case RIN_POLYMORPH_CONTROL: + pline_The("sink momentarily looks like a regularly erupting geyser."); + break; + } + } + if(ideed) + trycall(obj); + else + You_hear("the ring bouncing down the drainpipe."); + if (!rn2(20)) { + pline_The("sink backs up, leaving %s.", doname(obj)); + obj->in_use = FALSE; + dropx(obj); + } else + useup(obj); +} +#endif + +#endif /* OVLB */ +#ifdef OVL0 + +/* some common tests when trying to drop or throw items */ +boolean +canletgo(obj,word) +register struct obj *obj; +register const char *word; +{ + if(obj->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)){ + if (*word) + Norep("You cannot %s %s you are wearing.",word, + something); + return(FALSE); + } + if (obj->otyp == LOADSTONE && obj->cursed) { + /* getobj() kludge sets corpsenm to user's specified count + when refusing to split a stack of cursed loadstones */ + if (*word) { + /* getobj() ignores a count for throwing since that is + implicitly forced to be 1; replicate its kludge... */ + if (!strcmp(word, "throw") && obj->quan > 1L) + obj->corpsenm = 1; + pline("For some reason, you cannot %s%s the stone%s!", + word, obj->corpsenm ? " any of" : "", + plur(obj->quan)); + } + obj->corpsenm = 0; /* reset */ + obj->bknown = 1; + return(FALSE); + } + if (obj->otyp == LEASH && obj->leashmon != 0) { + if (*word) + pline_The("leash is tied around your %s.", + body_part(HAND)); + return(FALSE); + } +#ifdef STEED + if (obj->owornmask & W_SADDLE) { + if (*word) + You("cannot %s %s you are sitting on.", word, + something); + return (FALSE); + } +#endif + return(TRUE); +} + +STATIC_PTR +int +drop(obj) +register struct obj *obj; +{ + if(!obj) return(0); + if(!canletgo(obj,"drop")) + return(0); + if(obj == uwep) { + if(welded(uwep)) { + weldmsg(obj); + return(0); + } + setuwep((struct obj *)0); + } + if(obj == uquiver) { + setuqwep((struct obj *)0); + } + if (obj == uswapwep) { + setuswapwep((struct obj *)0); + } + + if (u.uswallow) { + /* barrier between you and the floor */ + if(flags.verbose) + { + char buf[BUFSZ]; + + /* doname can call s_suffix, reusing its buffer */ + Strcpy(buf, s_suffix(mon_nam(u.ustuck))); + You("drop %s into %s %s.", doname(obj), buf, + mbodypart(u.ustuck, STOMACH)); + } + } else { +#ifdef SINKS + if((obj->oclass == RING_CLASS || obj->otyp == MEAT_RING) && + IS_SINK(levl[u.ux][u.uy].typ)) { + dosinkring(obj); + return(1); + } +#endif + if (!can_reach_floor()) { + if(flags.verbose) You("drop %s.", doname(obj)); +#ifndef GOLDOBJ + if (obj->oclass != COIN_CLASS || obj == invent) freeinv(obj); +#else + /* Ensure update when we drop gold objects */ + if (obj->oclass == COIN_CLASS) flags.botl = 1; + freeinv(obj); +#endif + hitfloor(obj); + return(1); + } + if (!IS_ALTAR(levl[u.ux][u.uy].typ) && flags.verbose) + You("drop %s.", doname(obj)); + } + dropx(obj); + return(1); +} + +/* Called in several places - may produce output */ +/* eg ship_object() and dropy() -> sellobj() both produce output */ +void +dropx(obj) +register struct obj *obj; +{ +#ifndef GOLDOBJ + if (obj->oclass != COIN_CLASS || obj == invent) freeinv(obj); +#else + /* Ensure update when we drop gold objects */ + if (obj->oclass == COIN_CLASS) flags.botl = 1; + freeinv(obj); +#endif + if (!u.uswallow) { + if (ship_object(obj, u.ux, u.uy, FALSE)) return; + if (IS_ALTAR(levl[u.ux][u.uy].typ)) + doaltarobj(obj); /* set bknown */ + } + dropy(obj); +} + +void +dropy(obj) +register struct obj *obj; +{ + if (obj == uwep) setuwep((struct obj *)0); + if (obj == uquiver) setuqwep((struct obj *)0); + if (obj == uswapwep) setuswapwep((struct obj *)0); + + if (!u.uswallow && flooreffects(obj,u.ux,u.uy,"drop")) return; + /* uswallow check done by GAN 01/29/87 */ + if(u.uswallow) { + boolean could_petrify = FALSE; + boolean could_poly = FALSE; + boolean could_slime = FALSE; + boolean could_grow = FALSE; + boolean could_heal = FALSE; + + if (obj != uball) { /* mon doesn't pick up ball */ + if (obj->otyp == CORPSE) { + could_petrify = touch_petrifies(&mons[obj->corpsenm]); + could_poly = polyfodder(obj); + could_slime = (obj->corpsenm == PM_GREEN_SLIME); + could_grow = (obj->corpsenm == PM_WRAITH); + could_heal = (obj->corpsenm == PM_NURSE); + } + (void) mpickobj(u.ustuck,obj); + if (is_animal(u.ustuck->data)) { + if (could_poly || could_slime) { + (void) newcham(u.ustuck, + could_poly ? (struct permonst *)0 : + &mons[PM_GREEN_SLIME], + FALSE, could_slime); + delobj(obj); /* corpse is digested */ + } else if (could_petrify) { + minstapetrify(u.ustuck, TRUE); + /* Don't leave a cockatrice corpse in a statue */ + if (!u.uswallow) delobj(obj); + } else if (could_grow) { + (void) grow_up(u.ustuck, (struct monst *)0); + delobj(obj); /* corpse is digested */ + } else if (could_heal) { + u.ustuck->mhp = u.ustuck->mhpmax; + delobj(obj); /* corpse is digested */ + } + } + } + } else { + place_object(obj, u.ux, u.uy); + if (obj == uball) + drop_ball(u.ux,u.uy); + else + sellobj(obj, u.ux, u.uy); + stackobj(obj); + if(Blind && Levitation) + map_object(obj, 0); + newsym(u.ux,u.uy); /* remap location under self */ + } +} + +/* things that must change when not held; recurse into containers. + Called for both player and monsters */ +void +obj_no_longer_held(obj) +struct obj *obj; +{ + if (!obj) { + return; + } else if ((Is_container(obj) || obj->otyp == STATUE) && obj->cobj) { + struct obj *contents; + for(contents=obj->cobj; contents; contents=contents->nobj) + obj_no_longer_held(contents); + } + switch(obj->otyp) { + case CRYSKNIFE: + /* KMH -- Fixed crysknives have only 10% chance of reverting */ + /* only changes when not held by player or monster */ + if (!obj->oerodeproof || !rn2(10)) { + obj->otyp = WORM_TOOTH; + obj->oerodeproof = 0; + } + break; + } +} + +/* 'D' command: drop several things */ +int +doddrop() +{ + int result = 0; + + add_valid_menu_class(0); /* clear any classes already there */ + if (*u.ushops) sellobj_state(SELL_DELIBERATE); + if (flags.menu_style != MENU_TRADITIONAL || + (result = ggetobj("drop", drop, 0, FALSE, (unsigned *)0)) < -1) + result = menu_drop(result); + if (*u.ushops) sellobj_state(SELL_NORMAL); + reset_occupations(); + + return result; +} + +/* Drop things from the hero's inventory, using a menu. */ +STATIC_OVL int +menu_drop(retry) +int retry; +{ + int n, i, n_dropped = 0; + long cnt; + struct obj *otmp, *otmp2; +#ifndef GOLDOBJ + struct obj *u_gold = 0; +#endif + menu_item *pick_list; + boolean all_categories = TRUE; + boolean drop_everything = FALSE; + +#ifndef GOLDOBJ + if (u.ugold) { + /* Hack: gold is not in the inventory, so make a gold object + and put it at the head of the inventory list. */ + u_gold = mkgoldobj(u.ugold); /* removes from u.ugold */ + u_gold->in_use = TRUE; + u.ugold = u_gold->quan; /* put the gold back */ + assigninvlet(u_gold); /* might end up as NOINVSYM */ + u_gold->nobj = invent; + invent = u_gold; + } +#endif + if (retry) { + all_categories = (retry == -2); + } else if (flags.menu_style == MENU_FULL) { + all_categories = FALSE; + n = query_category("Drop what type of items?", + invent, + UNPAID_TYPES | ALL_TYPES | CHOOSE_ALL | + BUC_BLESSED | BUC_CURSED | BUC_UNCURSED | BUC_UNKNOWN, + &pick_list, PICK_ANY); + if (!n) goto drop_done; + for (i = 0; i < n; i++) { + if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) + all_categories = TRUE; + else if (pick_list[i].item.a_int == 'A') + drop_everything = TRUE; + else + add_valid_menu_class(pick_list[i].item.a_int); + } + free((genericptr_t) pick_list); + } else if (flags.menu_style == MENU_COMBINATION) { + unsigned ggoresults = 0; + all_categories = FALSE; + /* Gather valid classes via traditional NetHack method */ + i = ggetobj("drop", drop, 0, TRUE, &ggoresults); + if (i == -2) all_categories = TRUE; + if (ggoresults & ALL_FINISHED) { + n_dropped = i; + goto drop_done; + } + } + + if (drop_everything) { + for(otmp = invent; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + n_dropped += drop(otmp); + } + } else { + /* should coordinate with perm invent, maybe not show worn items */ + n = query_objlist("What would you like to drop?", invent, + USE_INVLET|INVORDER_SORT, &pick_list, + PICK_ANY, all_categories ? allow_all : allow_category); + if (n > 0) { + for (i = 0; i < n; i++) { + otmp = pick_list[i].item.a_obj; + cnt = pick_list[i].count; + if (cnt < otmp->quan) { + if (welded(otmp)) { + ; /* don't split */ + } else if (otmp->otyp == LOADSTONE && otmp->cursed) { + /* same kludge as getobj(), for canletgo()'s use */ + otmp->corpsenm = (int) cnt; /* don't split */ + } else { +#ifndef GOLDOBJ + if (otmp->oclass == COIN_CLASS) + (void) splitobj(otmp, otmp->quan - cnt); + else +#endif + otmp = splitobj(otmp, cnt); + } + } + n_dropped += drop(otmp); + } + free((genericptr_t) pick_list); + } + } + + drop_done: +#ifndef GOLDOBJ + if (u_gold && invent && invent->oclass == COIN_CLASS) { + /* didn't drop [all of] it */ + u_gold = invent; + invent = u_gold->nobj; + u_gold->in_use = FALSE; + dealloc_obj(u_gold); + update_inventory(); + } +#endif + return n_dropped; +} + +#endif /* OVL0 */ +#ifdef OVL2 + +/* on a ladder, used in goto_level */ +static NEARDATA boolean at_ladder = FALSE; + +int +dodown() +{ + struct trap *trap = 0; + boolean stairs_down = ((u.ux == xdnstair && u.uy == ydnstair) || + (u.ux == sstairs.sx && u.uy == sstairs.sy && !sstairs.up)), + ladder_down = (u.ux == xdnladder && u.uy == ydnladder); + +#ifdef STEED + if (u.usteed && !u.usteed->mcanmove) { + pline("%s won't move!", Monnam(u.usteed)); + return(0); + } else if (u.usteed && u.usteed->meating) { + pline("%s is still eating.", Monnam(u.usteed)); + return(0); + } else +#endif + if (Levitation) { + if ((HLevitation & I_SPECIAL) || (ELevitation & W_ARTI)) { + /* end controlled levitation */ + if (ELevitation & W_ARTI) { + struct obj *obj; + + for(obj = invent; obj; obj = obj->nobj) { + if (obj->oartifact && + artifact_has_invprop(obj,LEVITATION)) { + if (obj->age < monstermoves) + obj->age = monstermoves + rnz(100); + else + obj->age += rnz(100); + } + } + } + if (float_down(I_SPECIAL|TIMEOUT, W_ARTI)) + return (1); /* came down, so moved */ + } + floating_above(stairs_down ? "stairs" : ladder_down ? + "ladder" : surface(u.ux, u.uy)); + return (0); /* didn't move */ + } + if (!stairs_down && !ladder_down) { + if (!(trap = t_at(u.ux,u.uy)) || + (trap->ttyp != TRAPDOOR && trap->ttyp != HOLE) + || !Can_fall_thru(&u.uz) || !trap->tseen) { + + if (flags.autodig && !flags.nopick && + uwep && is_pick(uwep)) { + return use_pick_axe2(uwep); + } else { + You_cant("go down here."); + return(0); + } + } + } + if(u.ustuck) { + You("are %s, and cannot go down.", + !u.uswallow ? "being held" : is_animal(u.ustuck->data) ? + "swallowed" : "engulfed"); + return(1); + } + if (on_level(&valley_level, &u.uz) && !u.uevent.gehennom_entered) { + You("are standing at the gate to Gehennom."); + pline("Unspeakable cruelty and harm lurk down there."); + if (yn("Are you sure you want to enter?") != 'y') + return(0); + else pline("So be it."); + u.uevent.gehennom_entered = 1; /* don't ask again */ + } + + if(!next_to_u()) { + You("are held back by your pet!"); + return(0); + } + + if (trap) + You("%s %s.", locomotion(youmonst.data, "jump"), + trap->ttyp == HOLE ? "down the hole" : "through the trap door"); + + if (trap && Is_stronghold(&u.uz)) { + goto_hell(FALSE, TRUE); + } else { + at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER); + next_level(!trap); + at_ladder = FALSE; + } + return(1); +} + +int +doup() +{ + if( (u.ux != xupstair || u.uy != yupstair) + && (!xupladder || u.ux != xupladder || u.uy != yupladder) + && (!sstairs.sx || u.ux != sstairs.sx || u.uy != sstairs.sy + || !sstairs.up) + ) { + You_cant("go up here."); + return(0); + } +#ifdef STEED + if (u.usteed && !u.usteed->mcanmove) { + pline("%s won't move!", Monnam(u.usteed)); + return(0); + } else if (u.usteed && u.usteed->meating) { + pline("%s is still eating.", Monnam(u.usteed)); + return(0); + } else +#endif + if(u.ustuck) { + You("are %s, and cannot go up.", + !u.uswallow ? "being held" : is_animal(u.ustuck->data) ? + "swallowed" : "engulfed"); + return(1); + } + if(near_capacity() > SLT_ENCUMBER) { + /* No levitation check; inv_weight() already allows for it */ + Your("load is too heavy to climb the %s.", + levl[u.ux][u.uy].typ == STAIRS ? "stairs" : "ladder"); + return(1); + } + if(ledger_no(&u.uz) == 1) { + if (yn("Beware, there will be no return! Still climb?") != 'y') + return(0); + } + if(!next_to_u()) { + You("are held back by your pet!"); + return(0); + } + at_ladder = (boolean) (levl[u.ux][u.uy].typ == LADDER); + prev_level(TRUE); + at_ladder = FALSE; + return(1); +} + +d_level save_dlevel = {0, 0}; + +/* check that we can write out the current level */ +STATIC_OVL int +currentlevel_rewrite() +{ + register int fd; + char whynot[BUFSZ]; + + /* since level change might be a bit slow, flush any buffered screen + * output (like "you fall through a trap door") */ + mark_synch(); + + fd = create_levelfile(ledger_no(&u.uz), whynot); + if (fd < 0) { + /* + * This is not quite impossible: e.g., we may have + * exceeded our quota. If that is the case then we + * cannot leave this level, and cannot save either. + * Another possibility is that the directory was not + * writable. + */ + pline("%s", whynot); + return -1; + } + +#ifdef MFLOPPY + if (!savelev(fd, ledger_no(&u.uz), COUNT_SAVE)) { + (void) close(fd); + delete_levelfile(ledger_no(&u.uz)); + pline("NetHack is out of disk space for making levels!"); + You("can save, quit, or continue playing."); + return -1; + } +#endif + return fd; +} + +#ifdef INSURANCE +void +save_currentstate() +{ + int fd; + + if (flags.ins_chkpt) { + /* write out just-attained level, with pets and everything */ + fd = currentlevel_rewrite(); + if(fd < 0) return; + bufon(fd); + savelev(fd,ledger_no(&u.uz), WRITE_SAVE); + bclose(fd); + } + + /* write out non-level state */ + savestateinlock(); +} +#endif + +/* +static boolean +badspot(x, y) +register xchar x, y; +{ + return((levl[x][y].typ != ROOM && levl[x][y].typ != AIR && + levl[x][y].typ != CORR) || MON_AT(x, y)); +} +*/ + +void +goto_level(newlevel, at_stairs, falling, portal) +d_level *newlevel; +boolean at_stairs, falling, portal; +{ + int fd, l_idx; + xchar new_ledger; + boolean cant_go_back, + up = (depth(newlevel) < depth(&u.uz)), + newdungeon = (u.uz.dnum != newlevel->dnum), + was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz), + familiar = FALSE; + boolean new = FALSE; /* made a new level? */ + struct monst *mtmp; + char whynot[BUFSZ]; + + if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel)) + newlevel->dlevel = dunlevs_in_dungeon(newlevel); + if (newdungeon && In_endgame(newlevel)) { /* 1st Endgame Level !!! */ + if (u.uhave.amulet) + assign_level(newlevel, &earth_level); + else return; + } + new_ledger = ledger_no(newlevel); + if (new_ledger <= 0) + done(ESCAPED); /* in fact < 0 is impossible */ + + /* If you have the amulet and are trying to get out of Gehennom, going + * up a set of stairs sometimes does some very strange things! + * Biased against law and towards chaos, but not nearly as strongly + * as it used to be (prior to 3.2.0). + * Odds: old new + * "up" L N C "up" L N C + * +1 75.0 75.0 75.0 +1 75.0 75.0 75.0 + * 0 0.0 12.5 25.0 0 6.25 8.33 12.5 + * -1 8.33 4.17 0.0 -1 6.25 8.33 12.5 + * -2 8.33 4.17 0.0 -2 6.25 8.33 0.0 + * -3 8.33 4.17 0.0 -3 6.25 0.0 0.0 + */ + if (Inhell && up && u.uhave.amulet && !newdungeon && !portal && + (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)-3)) { + if (!rn2(4)) { + int odds = 3 + (int)u.ualign.type, /* 2..4 */ + diff = odds <= 1 ? 0 : rn2(odds); /* paranoia */ + + if (diff != 0) { + assign_rnd_level(newlevel, &u.uz, diff); + /* if inside the tower, stay inside */ + if (was_in_W_tower && + !On_W_tower_level(newlevel)) diff = 0; + } + if (diff == 0) + assign_level(newlevel, &u.uz); + + new_ledger = ledger_no(newlevel); + + pline("A mysterious force momentarily surrounds you..."); + if (on_level(newlevel, &u.uz)) { + (void) safe_teleds(FALSE); + (void) next_to_u(); + return; + } else + at_stairs = at_ladder = FALSE; + } + } + + /* Prevent the player from going past the first quest level unless + * (s)he has been given the go-ahead by the leader. + */ + if (on_level(&u.uz, &qstart_level) && !newdungeon && !ok_to_quest()) { + pline("A mysterious force prevents you from descending."); + return; + } + + if (on_level(newlevel, &u.uz)) return; /* this can happen */ + + fd = currentlevel_rewrite(); + if (fd < 0) return; + + if (falling) /* assuming this is only trap door or hole */ + impact_drop((struct obj *)0, u.ux, u.uy, newlevel->dlevel); + + check_special_room(TRUE); /* probably was a trap door */ + if (Punished) unplacebc(); + u.utrap = 0; /* needed in level_tele */ + fill_pit(u.ux, u.uy); + u.ustuck = 0; /* idem */ + u.uinwater = 0; + u.uundetected = 0; /* not hidden, even if means are available */ + keepdogs(FALSE); + if (u.uswallow) /* idem */ + u.uswldtim = u.uswallow = 0; + /* + * We no longer see anything on the level. Make sure that this + * follows u.uswallow set to null since uswallow overrides all + * normal vision. + */ + vision_recalc(2); + + /* + * Save the level we're leaving. If we're entering the endgame, + * we can get rid of all existing levels because they cannot be + * reached any more. We still need to use savelev()'s cleanup + * for the level being left, to recover dynamic memory in use and + * to avoid dangling timers and light sources. + */ + cant_go_back = (newdungeon && In_endgame(newlevel)); + if (!cant_go_back) { + update_mlstmv(); /* current monsters are becoming inactive */ + bufon(fd); /* use buffered output */ + } + savelev(fd, ledger_no(&u.uz), + cant_go_back ? FREE_SAVE : (WRITE_SAVE | FREE_SAVE)); + bclose(fd); + if (cant_go_back) { + /* discard unreachable levels; keep #0 */ + for (l_idx = maxledgerno(); l_idx > 0; --l_idx) + delete_levelfile(l_idx); + } + +#ifdef REINCARNATION + if (Is_rogue_level(newlevel) || Is_rogue_level(&u.uz)) + assign_rogue_graphics(Is_rogue_level(newlevel)); +#endif +#ifdef USE_TILES + substitute_tiles(newlevel); +#endif + assign_level(&u.uz0, &u.uz); + assign_level(&u.uz, newlevel); + assign_level(&u.utolev, newlevel); + u.utotype = 0; + if (dunlev_reached(&u.uz) < dunlev(&u.uz)) + dunlev_reached(&u.uz) = dunlev(&u.uz); + reset_rndmonst(NON_PM); /* u.uz change affects monster generation */ + + /* set default level change destination areas */ + /* the special level code may override these */ + (void) memset((genericptr_t) &updest, 0, sizeof updest); + (void) memset((genericptr_t) &dndest, 0, sizeof dndest); + + if (!(level_info[new_ledger].flags & LFILE_EXISTS)) { + /* entering this level for first time; make it now */ + if (level_info[new_ledger].flags & (FORGOTTEN|VISITED)) { + impossible("goto_level: returning to discarded level?"); + level_info[new_ledger].flags &= ~(FORGOTTEN|VISITED); + } + mklev(); + new = TRUE; /* made the level */ + } else { + /* returning to previously visited level; reload it */ + fd = open_levelfile(new_ledger, whynot); + if (fd < 0) { + pline("%s", whynot); + pline("Probably someone removed it."); + killer = whynot; + done(TRICKED); + /* we'll reach here if running in wizard mode */ + error("Cannot continue this game."); + } + minit(); /* ZEROCOMP */ + getlev(fd, hackpid, new_ledger, FALSE); + (void) close(fd); + } + /* do this prior to level-change pline messages */ + vision_reset(); /* clear old level's line-of-sight */ + vision_full_recalc = 0; /* don't let that reenable vision yet */ + flush_screen(-1); /* ensure all map flushes are postponed */ + + if (portal && !In_endgame(&u.uz)) { + /* find the portal on the new level */ + register struct trap *ttrap; + + for (ttrap = ftrap; ttrap; ttrap = ttrap->ntrap) + if (ttrap->ttyp == MAGIC_PORTAL) break; + + if (!ttrap) panic("goto_level: no corresponding portal!"); + seetrap(ttrap); + u_on_newpos(ttrap->tx, ttrap->ty); + } else if (at_stairs && !In_endgame(&u.uz)) { + if (up) { + if (at_ladder) { + u_on_newpos(xdnladder, ydnladder); + } else { + if (newdungeon) { + if (Is_stronghold(&u.uz)) { + register xchar x, y; + + do { + x = (COLNO - 2 - rnd(5)); + y = rn1(ROWNO - 4, 3); + } while(occupied(x, y) || + IS_WALL(levl[x][y].typ)); + u_on_newpos(x, y); + } else u_on_sstairs(); + } else u_on_dnstairs(); + } + /* Remove bug which crashes with levitation/punishment KAA */ + if (Punished && !Levitation) { + pline("With great effort you climb the %s.", + at_ladder ? "ladder" : "stairs"); + } else if (at_ladder) + You("climb up the ladder."); + } else { /* down */ + if (at_ladder) { + u_on_newpos(xupladder, yupladder); + } else { + if (newdungeon) u_on_sstairs(); + else u_on_upstairs(); + } + if (u.dz && Flying) + You("fly down along the %s.", + at_ladder ? "ladder" : "stairs"); + else if (u.dz && + (near_capacity() > UNENCUMBERED || Punished || Fumbling)) { + You("fall down the %s.", at_ladder ? "ladder" : "stairs"); + if (Punished) { + drag_down(); + if (carried(uball)) { + if (uwep == uball) + setuwep((struct obj *)0); + if (uswapwep == uball) + setuswapwep((struct obj *)0); + if (uquiver == uball) + setuqwep((struct obj *)0); + freeinv(uball); + } + } +#ifdef STEED + /* falling off steed has its own losehp() call */ + if (u.usteed) + dismount_steed(DISMOUNT_FELL); + else +#endif + losehp(rnd(3), "falling downstairs", KILLED_BY); + selftouch("Falling, you"); + } else if (u.dz && at_ladder) + You("climb down the ladder."); + } + } else { /* trap door or level_tele or In_endgame */ + if (was_in_W_tower && On_W_tower_level(&u.uz)) + /* Stay inside the Wizard's tower when feasible. */ + /* Note: up vs down doesn't really matter in this case. */ + place_lregion(dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy, + 0,0, 0,0, LR_DOWNTELE, (d_level *) 0); + else if (up) + place_lregion(updest.lx, updest.ly, + updest.hx, updest.hy, + updest.nlx, updest.nly, + updest.nhx, updest.nhy, + LR_UPTELE, (d_level *) 0); + else + place_lregion(dndest.lx, dndest.ly, + dndest.hx, dndest.hy, + dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy, + LR_DOWNTELE, (d_level *) 0); + if (falling) { + if (Punished) ballfall(); + selftouch("Falling, you"); + } + } + + if (Punished) placebc(); + obj_delivery(); /* before killing geno'd monsters' eggs */ + losedogs(); + kill_genocided_monsters(); /* for those wiped out while in limbo */ + /* + * Expire all timers that have gone off while away. Must be + * after migrating monsters and objects are delivered + * (losedogs and obj_delivery). + */ + run_timers(); + + initrack(); + + if ((mtmp = m_at(u.ux, u.uy)) != 0 +#ifdef STEED + && mtmp != u.usteed +#endif + ) { + /* There's a monster at your target destination; it might be one + which accompanied you--see mon_arrive(dogmove.c)--or perhaps + it was already here. Randomly move you to an adjacent spot + or else the monster to any nearby location. Prior to 3.3.0 + the latter was done unconditionally. */ + coord cc; + + if (!rn2(2) && + enexto(&cc, u.ux, u.uy, youmonst.data) && + distu(cc.x, cc.y) <= 2) + u_on_newpos(cc.x, cc.y); /*[maybe give message here?]*/ + else + mnexto(mtmp); + + if ((mtmp = m_at(u.ux, u.uy)) != 0) { + impossible("mnexto failed (do.c)?"); + (void) rloc(mtmp, FALSE); + } + } + + /* initial movement of bubbles just before vision_recalc */ + if (Is_waterlevel(&u.uz)) + movebubbles(); + + if (level_info[new_ledger].flags & FORGOTTEN) { + forget_map(ALL_MAP); /* forget the map */ + forget_traps(); /* forget all traps too */ + familiar = TRUE; + level_info[new_ledger].flags &= ~FORGOTTEN; + } + + /* Reset the screen. */ + vision_reset(); /* reset the blockages */ + docrt(); /* does a full vision recalc */ + flush_screen(-1); + + /* + * Move all plines beyond the screen reset. + */ + + /* give room entrance message, if any */ + check_special_room(FALSE); + + /* Check whether we just entered Gehennom. */ + if (!In_hell(&u.uz0) && Inhell) { + if (Is_valley(&u.uz)) { + You("arrive at the Valley of the Dead..."); + pline_The("odor of burnt flesh and decay pervades the air."); +#ifdef MICRO + display_nhwindow(WIN_MESSAGE, FALSE); +#endif + You_hear("groans and moans everywhere."); + } else pline("It is hot here. You smell smoke..."); + } + + if (familiar) { + static const char * const fam_msgs[4] = { + "You have a sense of deja vu.", + "You feel like you've been here before.", + "This place %s familiar...", + 0 /* no message */ + }; + static const char * const halu_fam_msgs[4] = { + "Whoa! Everything %s different.", + "You are surrounded by twisty little passages, all alike.", + "Gee, this %s like uncle Conan's place...", + 0 /* no message */ + }; + const char *mesg; + char buf[BUFSZ]; + int which = rn2(4); + + if (Hallucination) + mesg = halu_fam_msgs[which]; + else + mesg = fam_msgs[which]; + if (mesg && index(mesg, '%')) { + Sprintf(buf, mesg, !Blind ? "looks" : "seems"); + mesg = buf; + } + if (mesg) pline(mesg); + } + +#ifdef REINCARNATION + if (new && Is_rogue_level(&u.uz)) + You("enter what seems to be an older, more primitive world."); +#endif + /* Final confrontation */ + if (In_endgame(&u.uz) && newdungeon && u.uhave.amulet) + resurrect(); + if (newdungeon && In_V_tower(&u.uz) && In_hell(&u.uz0)) + pline_The("heat and smoke are gone."); + + /* the message from your quest leader */ + if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") && + !(u.uevent.qexpelled || u.uevent.qcompleted || quest_status.leader_is_dead)) { + + if (u.uevent.qcalled) { + com_pager(Role_if(PM_ROGUE) ? 4 : 3); + } else { + com_pager(2); + u.uevent.qcalled = TRUE; + } + } + + /* once Croesus is dead, his alarm doesn't work any more */ + if (Is_knox(&u.uz) && (new || !mvitals[PM_CROESUS].died)) { + You("penetrated a high security area!"); + pline("An alarm sounds!"); + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && mtmp->msleeping) mtmp->msleeping = 0; + } + + if (on_level(&u.uz, &astral_level)) + final_level(); + else + onquest(); + assign_level(&u.uz0, &u.uz); /* reset u.uz0 */ + +#ifdef INSURANCE + save_currentstate(); +#endif + + /* assume this will always return TRUE when changing level */ + (void) in_out_region(u.ux, u.uy); + (void) pickup(1); +} + +STATIC_OVL void +final_level() +{ + struct monst *mtmp; + struct obj *otmp; + coord mm; + int i; + + /* reset monster hostility relative to player */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp)) reset_hostility(mtmp); + + /* create some player-monsters */ + create_mplayers(rn1(4, 3), TRUE); + + /* create a guardian angel next to player, if worthy */ + if (Conflict) { + pline( + "A voice booms: \"Thy desire for conflict shall be fulfilled!\""); + for (i = rnd(4); i > 0; --i) { + mm.x = u.ux; + mm.y = u.uy; + if (enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) + (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type, + mm.x, mm.y, FALSE); + } + + } else if (u.ualign.record > 8) { /* fervent */ + pline("A voice whispers: \"Thou hast been worthy of me!\""); + mm.x = u.ux; + mm.y = u.uy; + if (enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) { + if ((mtmp = mk_roamer(&mons[PM_ANGEL], u.ualign.type, + mm.x, mm.y, TRUE)) != 0) { + if (!Blind) + pline("An angel appears near you."); + else + You_feel("the presence of a friendly angel near you."); + /* guardian angel -- the one case mtame doesn't + * imply an edog structure, so we don't want to + * call tamedog(). + */ + mtmp->mtame = 10; + /* make him strong enough vs. endgame foes */ + mtmp->m_lev = rn1(8,15); + mtmp->mhp = mtmp->mhpmax = + d((int)mtmp->m_lev,10) + 30 + rnd(30); + if ((otmp = select_hwep(mtmp)) == 0) { + otmp = mksobj(SILVER_SABER, FALSE, FALSE); + if (mpickobj(mtmp, otmp)) + panic("merged weapon?"); + } + bless(otmp); + if (otmp->spe < 4) otmp->spe += rnd(4); + if ((otmp = which_armor(mtmp, W_ARMS)) == 0 || + otmp->otyp != SHIELD_OF_REFLECTION) { + (void) mongets(mtmp, AMULET_OF_REFLECTION); + m_dowear(mtmp, TRUE); + } + } + } + } +} + +static char *dfr_pre_msg = 0, /* pline() before level change */ + *dfr_post_msg = 0; /* pline() after level change */ + +/* change levels at the end of this turn, after monsters finish moving */ +void +schedule_goto(tolev, at_stairs, falling, portal_flag, pre_msg, post_msg) +d_level *tolev; +boolean at_stairs, falling; +int portal_flag; +const char *pre_msg, *post_msg; +{ + int typmask = 0100; /* non-zero triggers `deferred_goto' */ + + /* destination flags (`goto_level' args) */ + if (at_stairs) typmask |= 1; + if (falling) typmask |= 2; + if (portal_flag) typmask |= 4; + if (portal_flag < 0) typmask |= 0200; /* flag for portal removal */ + u.utotype = typmask; + /* destination level */ + assign_level(&u.utolev, tolev); + + if (pre_msg) + dfr_pre_msg = strcpy((char *)alloc(strlen(pre_msg) + 1), pre_msg); + if (post_msg) + dfr_post_msg = strcpy((char *)alloc(strlen(post_msg)+1), post_msg); +} + +/* handle something like portal ejection */ +void +deferred_goto() +{ + if (!on_level(&u.uz, &u.utolev)) { + d_level dest; + int typmask = u.utotype; /* save it; goto_level zeroes u.utotype */ + + assign_level(&dest, &u.utolev); + if (dfr_pre_msg) pline(dfr_pre_msg); + goto_level(&dest, !!(typmask&1), !!(typmask&2), !!(typmask&4)); + if (typmask & 0200) { /* remove portal */ + struct trap *t = t_at(u.ux, u.uy); + + if (t) { + deltrap(t); + newsym(u.ux, u.uy); + } + } + if (dfr_post_msg) pline(dfr_post_msg); + } + u.utotype = 0; /* our caller keys off of this */ + if (dfr_pre_msg) + free((genericptr_t)dfr_pre_msg), dfr_pre_msg = 0; + if (dfr_post_msg) + free((genericptr_t)dfr_post_msg), dfr_post_msg = 0; +} + +#endif /* OVL2 */ +#ifdef OVL3 + +/* + * Return TRUE if we created a monster for the corpse. If successful, the + * corpse is gone. + */ +boolean +revive_corpse(corpse) +struct obj *corpse; +{ + struct monst *mtmp, *mcarry; + boolean is_uwep, chewed; + xchar where; + char *cname, cname_buf[BUFSZ]; + struct obj *container = (struct obj *)0; + int container_where = 0; + + where = corpse->where; + is_uwep = corpse == uwep; + cname = eos(strcpy(cname_buf, "bite-covered ")); + Strcpy(cname, corpse_xname(corpse, TRUE)); + mcarry = (where == OBJ_MINVENT) ? corpse->ocarry : 0; + + if (where == OBJ_CONTAINED) { + struct monst *mtmp2 = (struct monst *)0; + container = corpse->ocontainer; + mtmp2 = get_container_location(container, &container_where, (int *)0); + /* container_where is the outermost container's location even if nested */ + if (container_where == OBJ_MINVENT && mtmp2) mcarry = mtmp2; + } + mtmp = revive(corpse); /* corpse is gone if successful */ + + if (mtmp) { + chewed = (mtmp->mhp < mtmp->mhpmax); + if (chewed) cname = cname_buf; /* include "bite-covered" prefix */ + switch (where) { + case OBJ_INVENT: + if (is_uwep) + pline_The("%s writhes out of your grasp!", cname); + else + You_feel("squirming in your backpack!"); + break; + + case OBJ_FLOOR: + if (cansee(mtmp->mx, mtmp->my)) + pline("%s rises from the dead!", chewed ? + Adjmonnam(mtmp, "bite-covered") : Monnam(mtmp)); + break; + + case OBJ_MINVENT: /* probably a nymph's */ + if (cansee(mtmp->mx, mtmp->my)) { + if (canseemon(mcarry)) + pline("Startled, %s drops %s as it revives!", + mon_nam(mcarry), an(cname)); + else + pline("%s suddenly appears!", chewed ? + Adjmonnam(mtmp, "bite-covered") : Monnam(mtmp)); + } + break; + case OBJ_CONTAINED: + if (container_where == OBJ_MINVENT && cansee(mtmp->mx, mtmp->my) && + mcarry && canseemon(mcarry) && container) { + char sackname[BUFSZ]; + Sprintf(sackname, "%s %s", s_suffix(mon_nam(mcarry)), + xname(container)); + pline("%s writhes out of %s!", Amonnam(mtmp), sackname); + } else if (container_where == OBJ_INVENT && container) { + char sackname[BUFSZ]; + Strcpy(sackname, an(xname(container))); + pline("%s %s out of %s in your pack!", + Blind ? Something : Amonnam(mtmp), + locomotion(mtmp->data,"writhes"), + sackname); + } else if (container_where == OBJ_FLOOR && container && + cansee(mtmp->mx, mtmp->my)) { + char sackname[BUFSZ]; + Strcpy(sackname, an(xname(container))); + pline("%s escapes from %s!", Amonnam(mtmp), sackname); + } + break; + default: + /* we should be able to handle the other cases... */ + impossible("revive_corpse: lost corpse @ %d", where); + break; + } + return TRUE; + } + return FALSE; +} + +/* Revive the corpse via a timeout. */ +/*ARGSUSED*/ +void +revive_mon(arg, timeout) +genericptr_t arg; +long timeout; +{ + struct obj *body = (struct obj *) arg; + + /* if we succeed, the corpse is gone, otherwise, rot it away */ + if (!revive_corpse(body)) { + if (is_rider(&mons[body->corpsenm])) + You_feel("less hassled."); + (void) start_timer(250L - (monstermoves-body->age), + TIMER_OBJECT, ROT_CORPSE, arg); + } +} + +int +donull() +{ + return(1); /* Do nothing, but let other things happen */ +} + +#endif /* OVL3 */ +#ifdef OVLB + +STATIC_PTR int +wipeoff() +{ + if(u.ucreamed < 4) u.ucreamed = 0; + else u.ucreamed -= 4; + if (Blinded < 4) Blinded = 0; + else Blinded -= 4; + if (!Blinded) { + pline("You've got the glop off."); + u.ucreamed = 0; + Blinded = 1; + make_blinded(0L,TRUE); + return(0); + } else if (!u.ucreamed) { + Your("%s feels clean now.", body_part(FACE)); + return(0); + } + return(1); /* still busy */ +} + +int +dowipe() +{ + if(u.ucreamed) { + static NEARDATA char buf[39]; + + Sprintf(buf, "wiping off your %s", body_part(FACE)); + set_occupation(wipeoff, buf, 0); + /* Not totally correct; what if they change back after now + * but before they're finished wiping? + */ + return(1); + } + Your("%s is already clean.", body_part(FACE)); + return(1); +} + +void +set_wounded_legs(side, timex) +register long side; +register int timex; +{ + /* KMH -- STEED + * If you are riding, your steed gets the wounded legs instead. + * You still call this function, but don't lose hp. + * Caller is also responsible for adjusting messages. + */ + + if(!Wounded_legs) { + ATEMP(A_DEX)--; + flags.botl = 1; + } + + if(!Wounded_legs || (HWounded_legs & TIMEOUT)) + HWounded_legs = timex; + EWounded_legs = side; + (void)encumber_msg(); +} + +void +heal_legs() +{ + if(Wounded_legs) { + if (ATEMP(A_DEX) < 0) { + ATEMP(A_DEX)++; + flags.botl = 1; + } + +#ifdef STEED + if (!u.usteed) +#endif + { + /* KMH, intrinsics patch */ + if((EWounded_legs & BOTH_SIDES) == BOTH_SIDES) { + Your("%s feel somewhat better.", + makeplural(body_part(LEG))); + } else { + Your("%s feels somewhat better.", + body_part(LEG)); + } + } + HWounded_legs = EWounded_legs = 0; + } + (void)encumber_msg(); +} + +#endif /* OVLB */ + +/*do.c*/ diff --git a/src/do_name.c b/src/do_name.c new file mode 100644 index 0000000..47b3f87 --- /dev/null +++ b/src/do_name.c @@ -0,0 +1,1044 @@ +/* SCCS Id: @(#)do_name.c 3.4 2003/01/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVLB + +STATIC_DCL void FDECL(do_oname, (struct obj *)); +static void FDECL(getpos_help, (BOOLEAN_P,const char *)); + +extern const char what_is_an_unknown_object[]; /* from pager.c */ + +/* the response for '?' help request in getpos() */ +static void +getpos_help(force, goal) +boolean force; +const char *goal; +{ + char sbuf[BUFSZ]; + boolean doing_what_is; + winid tmpwin = create_nhwindow(NHW_MENU); + + Sprintf(sbuf, "Use [%s] to move the cursor to %s.", + iflags.num_pad ? "2468" : "hjkl", goal); + putstr(tmpwin, 0, sbuf); + putstr(tmpwin, 0, "Use [HJKL] to move the cursor 8 units at a time."); + putstr(tmpwin, 0, "Or enter a background symbol (ex. <)."); + /* disgusting hack; the alternate selection characters work for any + getpos call, but they only matter for dowhatis (and doquickwhatis) */ + doing_what_is = (goal == what_is_an_unknown_object); + Sprintf(sbuf, "Type a .%s when you are at the right place.", + doing_what_is ? " or , or ; or :" : ""); + putstr(tmpwin, 0, sbuf); + if (!force) + putstr(tmpwin, 0, "Type Space or Escape when you're done."); + putstr(tmpwin, 0, ""); + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); +} + +int +getpos(cc, force, goal) +coord *cc; +boolean force; +const char *goal; +{ + int result = 0; + int cx, cy, i, c; + int sidx, tx, ty; + boolean msg_given = TRUE; /* clear message window by default */ + static const char pick_chars[] = ".,;:"; + const char *cp; + const char *sdp; + if(iflags.num_pad) sdp = ndir; else sdp = sdir; /* DICE workaround */ + + if (flags.verbose) { + pline("(For instructions type a ?)"); + msg_given = TRUE; + } + cx = cc->x; + cy = cc->y; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP, cx,cy); + flush_screen(0); +#ifdef MAC + lock_mouse_cursor(TRUE); +#endif + for (;;) { + c = nh_poskey(&tx, &ty, &sidx); + if (c == '\033') { + cx = cy = -10; + msg_given = TRUE; /* force clear */ + result = -1; + break; + } + if(c == 0) { + if (!isok(tx, ty)) continue; + /* a mouse click event, just assign and return */ + cx = tx; + cy = ty; + break; + } + if ((cp = index(pick_chars, c)) != 0) { + /* '.' => 0, ',' => 1, ';' => 2, ':' => 3 */ + result = cp - pick_chars; + break; + } + for (i = 0; i < 8; i++) { + int dx, dy; + + if (sdp[i] == c) { + /* a normal movement letter or digit */ + dx = xdir[i]; + dy = ydir[i]; + } else if (sdir[i] == lowc((char)c)) { + /* a shifted movement letter */ + dx = 8 * xdir[i]; + dy = 8 * ydir[i]; + } else + continue; + + /* truncate at map edge; diagonal moves complicate this... */ + if (cx + dx < 1) { + dy -= sgn(dy) * (1 - (cx + dx)); + dx = 1 - cx; /* so that (cx+dx == 1) */ + } else if (cx + dx > COLNO-1) { + dy += sgn(dy) * ((COLNO-1) - (cx + dx)); + dx = (COLNO-1) - cx; + } + if (cy + dy < 0) { + dx -= sgn(dx) * (0 - (cy + dy)); + dy = 0 - cy; /* so that (cy+dy == 0) */ + } else if (cy + dy > ROWNO-1) { + dx += sgn(dx) * ((ROWNO-1) - (cy + dy)); + dy = (ROWNO-1) - cy; + } + cx += dx; + cy += dy; + goto nxtc; + } + + if(c == '?'){ + getpos_help(force, goal); + } else { + if (!index(quitchars, c)) { + char matching[MAXPCHARS]; + int pass, lo_x, lo_y, hi_x, hi_y, k = 0; + (void)memset((genericptr_t)matching, 0, sizeof matching); + for (sidx = 1; sidx < MAXPCHARS; sidx++) + if (c == defsyms[sidx].sym || c == (int)showsyms[sidx]) + matching[sidx] = (char) ++k; + if (k) { + for (pass = 0; pass <= 1; pass++) { + /* pass 0: just past current pos to lower right; + pass 1: upper left corner to current pos */ + lo_y = (pass == 0) ? cy : 0; + hi_y = (pass == 0) ? ROWNO - 1 : cy; + for (ty = lo_y; ty <= hi_y; ty++) { + lo_x = (pass == 0 && ty == lo_y) ? cx + 1 : 1; + hi_x = (pass == 1 && ty == hi_y) ? cx : COLNO - 1; + for (tx = lo_x; tx <= hi_x; tx++) { + k = levl[tx][ty].glyph; + if (glyph_is_cmap(k) && + matching[glyph_to_cmap(k)]) { + cx = tx, cy = ty; + if (msg_given) { + clear_nhwindow(WIN_MESSAGE); + msg_given = FALSE; + } + goto nxtc; + } + } /* column */ + } /* row */ + } /* pass */ + pline("Can't find dungeon feature '%c'.", c); + msg_given = TRUE; + goto nxtc; + } else { + pline("Unknown direction: '%s' (%s).", + visctrl((char)c), + !force ? "aborted" : + iflags.num_pad ? "use 2468 or ." : "use hjkl or ."); + msg_given = TRUE; + } /* k => matching */ + } /* !quitchars */ + if (force) goto nxtc; + pline("Done."); + msg_given = FALSE; /* suppress clear */ + cx = -1; + cy = 0; + result = 0; /* not -1 */ + break; + } + nxtc: ; +#ifdef CLIPPING + cliparound(cx, cy); +#endif + curs(WIN_MAP,cx,cy); + flush_screen(0); + } +#ifdef MAC + lock_mouse_cursor(FALSE); +#endif + if (msg_given) clear_nhwindow(WIN_MESSAGE); + cc->x = cx; + cc->y = cy; + return result; +} + +struct monst * +christen_monst(mtmp, name) +struct monst *mtmp; +const char *name; +{ + int lth; + struct monst *mtmp2; + char buf[PL_PSIZ]; + + /* dogname & catname are PL_PSIZ arrays; object names have same limit */ + lth = *name ? (int)(strlen(name) + 1) : 0; + if(lth > PL_PSIZ){ + lth = PL_PSIZ; + name = strncpy(buf, name, PL_PSIZ - 1); + buf[PL_PSIZ - 1] = '\0'; + } + if (lth == mtmp->mnamelth) { + /* don't need to allocate a new monst struct */ + if (lth) Strcpy(NAME(mtmp), name); + return mtmp; + } + mtmp2 = newmonst(mtmp->mxlth + lth); + *mtmp2 = *mtmp; + (void) memcpy((genericptr_t)mtmp2->mextra, + (genericptr_t)mtmp->mextra, mtmp->mxlth); + mtmp2->mnamelth = lth; + if (lth) Strcpy(NAME(mtmp2), name); + replmon(mtmp,mtmp2); + return(mtmp2); +} + +int +do_mname() +{ + char buf[BUFSZ]; + coord cc; + register int cx,cy; + register struct monst *mtmp; + char qbuf[QBUFSZ]; + + if (Hallucination) { + You("would never recognize it anyway."); + return 0; + } + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, FALSE, "the monster you want to name") < 0 || + (cx = cc.x) < 0) + return 0; + cy = cc.y; + + if (cx == u.ux && cy == u.uy) { +#ifdef STEED + if (u.usteed && canspotmon(u.usteed)) + mtmp = u.usteed; + else { +#endif + pline("This %s creature is called %s and cannot be renamed.", + ACURR(A_CHA) > 14 ? + (flags.female ? "beautiful" : "handsome") : + "ugly", + plname); + return(0); +#ifdef STEED + } +#endif + } else + mtmp = m_at(cx, cy); + + if (!mtmp || (!sensemon(mtmp) && + (!(cansee(cx,cy) || see_with_infrared(mtmp)) || mtmp->mundetected + || mtmp->m_ap_type == M_AP_FURNITURE + || mtmp->m_ap_type == M_AP_OBJECT + || (mtmp->minvis && !See_invisible)))) { + pline("I see no monster there."); + return(0); + } + /* special case similar to the one in lookat() */ + (void) distant_monnam(mtmp, ARTICLE_THE, buf); + Sprintf(qbuf, "What do you want to call %s?", buf); + getlin(qbuf,buf); + if(!*buf || *buf == '\033') return(0); + /* strip leading and trailing spaces; unnames monster if all spaces */ + (void)mungspaces(buf); + + if (mtmp->data->geno & G_UNIQ) + pline("%s doesn't like being called names!", Monnam(mtmp)); + else + (void) christen_monst(mtmp, buf); + return(0); +} + +/* + * This routine changes the address of obj. Be careful not to call it + * when there might be pointers around in unknown places. For now: only + * when obj is in the inventory. + */ +STATIC_OVL +void +do_oname(obj) +register struct obj *obj; +{ + char buf[BUFSZ], qbuf[QBUFSZ]; + const char *aname; + short objtyp; + + Sprintf(qbuf, "What do you want to name %s %s?", + is_plural(obj) ? "these" : "this", xname(obj)); + getlin(qbuf, buf); + if(!*buf || *buf == '\033') return; + /* strip leading and trailing spaces; unnames item if all spaces */ + (void)mungspaces(buf); + + /* relax restrictions over proper capitalization for artifacts */ + if ((aname = artifact_name(buf, &objtyp)) != 0 && objtyp == obj->otyp) + Strcpy(buf, aname); + + if (obj->oartifact) { + pline_The("artifact seems to resist the attempt."); + return; + } else if (restrict_name(obj, buf) || exist_artifact(obj->otyp, buf)) { + int n = rn2((int)strlen(buf)); + register char c1, c2; + + c1 = lowc(buf[n]); + do c2 = 'a' + rn2('z'-'a'); while (c1 == c2); + buf[n] = (buf[n] == c1) ? c2 : highc(c2); /* keep same case */ + pline("While engraving your %s slips.", body_part(HAND)); + display_nhwindow(WIN_MESSAGE, FALSE); + You("engrave: \"%s\".",buf); + } + obj = oname(obj, buf); +} + +/* + * Allocate a new and possibly larger storage space for an obj. + */ +struct obj * +realloc_obj(obj, oextra_size, oextra_src, oname_size, name) +struct obj *obj; +int oextra_size; /* storage to allocate for oextra */ +genericptr_t oextra_src; +int oname_size; /* size of name string + 1 (null terminator) */ +const char *name; +{ + struct obj *otmp; + + otmp = newobj(oextra_size + oname_size); + *otmp = *obj; /* the cobj pointer is copied to otmp */ + if (oextra_size) { + if (oextra_src) + (void) memcpy((genericptr_t)otmp->oextra, oextra_src, + oextra_size); + } else { + otmp->oattached = OATTACHED_NOTHING; + } + otmp->oxlth = oextra_size; + + otmp->onamelth = oname_size; + otmp->timed = 0; /* not timed, yet */ + otmp->lamplit = 0; /* ditto */ + /* __GNUC__ note: if the assignment of otmp->onamelth immediately + precedes this `if' statement, a gcc bug will miscompile the + test on vax (`insv' instruction used to store bitfield does + not set condition codes, but optimizer behaves as if it did). + gcc-2.7.2.1 finally fixed this. */ + if (oname_size) { + if (name) + Strcpy(ONAME(otmp), name); + } + + if (obj->owornmask) { + boolean save_twoweap = u.twoweap; + /* unwearing the old instance will clear dual-wield mode + if this object is either of the two weapons */ + setworn((struct obj *)0, obj->owornmask); + setworn(otmp, otmp->owornmask); + u.twoweap = save_twoweap; + } + + /* replace obj with otmp */ + replace_object(obj, otmp); + + /* fix ocontainer pointers */ + if (Has_contents(obj)) { + struct obj *inside; + + for(inside = obj->cobj; inside; inside = inside->nobj) + inside->ocontainer = otmp; + } + + /* move timers and light sources from obj to otmp */ + if (obj->timed) obj_move_timers(obj, otmp); + if (obj->lamplit) obj_move_light_source(obj, otmp); + + /* objects possibly being manipulated by multi-turn occupations + which have been interrupted but might be subsequently resumed */ + if (obj->oclass == FOOD_CLASS) + food_substitution(obj, otmp); /* eat food or open tin */ + else if (obj->oclass == SPBOOK_CLASS) + book_substitution(obj, otmp); /* read spellbook */ + + /* obfree(obj, otmp); now unnecessary: no pointers on bill */ + dealloc_obj(obj); /* let us hope nobody else saved a pointer */ + return otmp; +} + +struct obj * +oname(obj, name) +struct obj *obj; +const char *name; +{ + int lth; + char buf[PL_PSIZ]; + + lth = *name ? (int)(strlen(name) + 1) : 0; + if (lth > PL_PSIZ) { + lth = PL_PSIZ; + name = strncpy(buf, name, PL_PSIZ - 1); + buf[PL_PSIZ - 1] = '\0'; + } + /* If named artifact exists in the game, do not create another. + * Also trying to create an artifact shouldn't de-artifact + * it (e.g. Excalibur from prayer). In this case the object + * will retain its current name. */ + if (obj->oartifact || (lth && exist_artifact(obj->otyp, name))) + return obj; + + if (lth == obj->onamelth) { + /* no need to replace entire object */ + if (lth) Strcpy(ONAME(obj), name); + } else { + obj = realloc_obj(obj, obj->oxlth, + (genericptr_t)obj->oextra, lth, name); + } + if (lth) artifact_exists(obj, name, TRUE); + if (obj->oartifact) { + /* can't dual-wield with artifact as secondary weapon */ + if (obj == uswapwep) untwoweapon(); + /* activate warning if you've just named your weapon "Sting" */ + if (obj == uwep) set_artifact_intrinsic(obj, TRUE, W_WEP); + } + if (carried(obj)) update_inventory(); + return obj; +} + +static NEARDATA const char callable[] = { + SCROLL_CLASS, POTION_CLASS, WAND_CLASS, RING_CLASS, AMULET_CLASS, + GEM_CLASS, SPBOOK_CLASS, ARMOR_CLASS, TOOL_CLASS, 0 }; + +int +ddocall() +{ + register struct obj *obj; +#ifdef REDO + char ch; +#endif + char allowall[2]; + + switch( +#ifdef REDO + ch = +#endif + ynq("Name an individual object?")) { + case 'q': + break; + case 'y': +#ifdef REDO + savech(ch); +#endif + allowall[0] = ALL_CLASSES; allowall[1] = '\0'; + obj = getobj(allowall, "name"); + if(obj) do_oname(obj); + break; + default : +#ifdef REDO + savech(ch); +#endif + obj = getobj(callable, "call"); + if (obj) { + /* behave as if examining it in inventory; + this might set dknown if it was picked up + while blind and the hero can now see */ + (void) xname(obj); + + if (!obj->dknown) { + You("would never recognize another one."); + return 0; + } + docall(obj); + } + break; + } + return 0; +} + +void +docall(obj) +register struct obj *obj; +{ + char buf[BUFSZ], qbuf[QBUFSZ]; + struct obj otemp; + register char **str1; + + if (!obj->dknown) return; /* probably blind */ + otemp = *obj; + otemp.quan = 1L; + otemp.onamelth = 0; + otemp.oxlth = 0; + if (objects[otemp.otyp].oc_class == POTION_CLASS && otemp.fromsink) + /* kludge, meaning it's sink water */ + Sprintf(qbuf,"Call a stream of %s fluid:", + OBJ_DESCR(objects[otemp.otyp])); + else + Sprintf(qbuf, "Call %s:", an(xname(&otemp))); + getlin(qbuf, buf); + if(!*buf || *buf == '\033') + return; + + /* clear old name */ + str1 = &(objects[obj->otyp].oc_uname); + if(*str1) free((genericptr_t)*str1); + + /* strip leading and trailing spaces; uncalls item if all spaces */ + (void)mungspaces(buf); + if (!*buf) { + if (*str1) { /* had name, so possibly remove from disco[] */ + /* strip name first, for the update_inventory() call + from undiscover_object() */ + *str1 = (char *)0; + undiscover_object(obj->otyp); + } + } else { + *str1 = strcpy((char *) alloc((unsigned)strlen(buf)+1), buf); + discover_object(obj->otyp, FALSE, TRUE); /* possibly add to disco[] */ + } +} + +#endif /*OVLB*/ +#ifdef OVL0 + +static const char * const ghostnames[] = { + /* these names should have length < PL_NSIZ */ + /* Capitalize the names for aesthetics -dgk */ + "Adri", "Andries", "Andreas", "Bert", "David", "Dirk", "Emile", + "Frans", "Fred", "Greg", "Hether", "Jay", "John", "Jon", "Karnov", + "Kay", "Kenny", "Kevin", "Maud", "Michiel", "Mike", "Peter", "Robert", + "Ron", "Tom", "Wilmar", "Nick Danger", "Phoenix", "Jiro", "Mizue", + "Stephan", "Lance Braccus", "Shadowhawk" +}; + +/* ghost names formerly set by x_monnam(), now by makemon() instead */ +const char * +rndghostname() +{ + return rn2(7) ? ghostnames[rn2(SIZE(ghostnames))] : (const char *)plname; +} + +/* Monster naming functions: + * x_monnam is the generic monster-naming function. + * seen unseen detected named + * mon_nam: the newt it the invisible orc Fido + * noit_mon_nam:the newt (as if detected) the invisible orc Fido + * l_monnam: newt it invisible orc dog called fido + * Monnam: The newt It The invisible orc Fido + * noit_Monnam: The newt (as if detected) The invisible orc Fido + * Adjmonnam: The poor newt It The poor invisible orc The poor Fido + * Amonnam: A newt It An invisible orc Fido + * a_monnam: a newt it an invisible orc Fido + * m_monnam: newt xan orc Fido + * y_monnam: your newt your xan your invisible orc Fido + */ + +/* Bug: if the monster is a priest or shopkeeper, not every one of these + * options works, since those are special cases. + */ +char * +x_monnam(mtmp, article, adjective, suppress, called) +register struct monst *mtmp; +int article; +/* ARTICLE_NONE, ARTICLE_THE, ARTICLE_A: obvious + * ARTICLE_YOUR: "your" on pets, "the" on everything else + * + * If the monster would be referred to as "it" or if the monster has a name + * _and_ there is no adjective, "invisible", "saddled", etc., override this + * and always use no article. + */ +const char *adjective; +int suppress; +/* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE. + * EXACT_NAME: combination of all the above + */ +boolean called; +{ +#ifdef LINT /* static char buf[BUFSZ]; */ + char buf[BUFSZ]; +#else + static char buf[BUFSZ]; +#endif + struct permonst *mdat = mtmp->data; + boolean do_hallu, do_invis, do_it, do_saddle; + boolean name_at_start, has_adjectives; + char *bp; + + if (program_state.gameover) + suppress |= SUPPRESS_HALLUCINATION; + if (article == ARTICLE_YOUR && !mtmp->mtame) + article = ARTICLE_THE; + + do_hallu = Hallucination && !(suppress & SUPPRESS_HALLUCINATION); + do_invis = mtmp->minvis && !(suppress & SUPPRESS_INVISIBLE); + do_it = !canspotmon(mtmp) && + article != ARTICLE_YOUR && + !program_state.gameover && +#ifdef STEED + mtmp != u.usteed && +#endif + !(u.uswallow && mtmp == u.ustuck) && + !(suppress & SUPPRESS_IT); + do_saddle = !(suppress & SUPPRESS_SADDLE); + + buf[0] = 0; + + /* unseen monsters, etc. Use "it" */ + if (do_it) { + Strcpy(buf, "it"); + return buf; + } + + /* priests and minions: don't even use this function */ + if (mtmp->ispriest || mtmp->isminion) { + char priestnambuf[BUFSZ]; + char *name; + long save_prop = EHalluc_resistance; + unsigned save_invis = mtmp->minvis; + + /* when true name is wanted, explicitly block Hallucination */ + if (!do_hallu) EHalluc_resistance = 1L; + if (!do_invis) mtmp->minvis = 0; + name = priestname(mtmp, priestnambuf); + EHalluc_resistance = save_prop; + mtmp->minvis = save_invis; + if (article == ARTICLE_NONE && !strncmp(name, "the ", 4)) + name += 4; + return strcpy(buf, name); + } + + /* Shopkeepers: use shopkeeper name. For normal shopkeepers, just + * "Asidonhopo"; for unusual ones, "Asidonhopo the invisible + * shopkeeper" or "Asidonhopo the blue dragon". If hallucinating, + * none of this applies. + */ + if (mtmp->isshk && !do_hallu) { + if (adjective && article == ARTICLE_THE) { + /* pathological case: "the angry Asidonhopo the blue dragon" + sounds silly */ + Strcpy(buf, "the "); + Strcat(strcat(buf, adjective), " "); + Strcat(buf, shkname(mtmp)); + return buf; + } + Strcat(buf, shkname(mtmp)); + if (mdat == &mons[PM_SHOPKEEPER] && !do_invis) + return buf; + Strcat(buf, " the "); + if (do_invis) + Strcat(buf, "invisible "); + Strcat(buf, mdat->mname); + return buf; + } + + /* Put the adjectives in the buffer */ + if (adjective) + Strcat(strcat(buf, adjective), " "); + if (do_invis) + Strcat(buf, "invisible "); +#ifdef STEED + if (do_saddle && (mtmp->misc_worn_check & W_SADDLE) && + !Blind && !Hallucination) + Strcat(buf, "saddled "); +#endif + if (buf[0] != 0) + has_adjectives = TRUE; + else + has_adjectives = FALSE; + + /* Put the actual monster name or type into the buffer now */ + /* Be sure to remember whether the buffer starts with a name */ + if (do_hallu) { + Strcat(buf, rndmonnam()); + name_at_start = FALSE; + } else if (mtmp->mnamelth) { + char *name = NAME(mtmp); + + if (mdat == &mons[PM_GHOST]) { + Sprintf(eos(buf), "%s ghost", s_suffix(name)); + name_at_start = TRUE; + } else if (called) { + Sprintf(eos(buf), "%s called %s", mdat->mname, name); + name_at_start = (boolean)type_is_pname(mdat); + } else if (is_mplayer(mdat) && (bp = strstri(name, " the ")) != 0) { + /* the */ + char pbuf[BUFSZ]; + + Strcpy(pbuf, name); + pbuf[bp - name + 5] = '\0'; /* adjectives right after " the " */ + if (has_adjectives) + Strcat(pbuf, buf); + Strcat(pbuf, bp + 5); /* append the rest of the name */ + Strcpy(buf, pbuf); + article = ARTICLE_NONE; + name_at_start = TRUE; + } else { + Strcat(buf, name); + name_at_start = TRUE; + } + } else if (is_mplayer(mdat) && !In_endgame(&u.uz)) { + char pbuf[BUFSZ]; + Strcpy(pbuf, rank_of((int)mtmp->m_lev, + monsndx(mdat), + (boolean)mtmp->female)); + Strcat(buf, lcase(pbuf)); + name_at_start = FALSE; + } else { + Strcat(buf, mdat->mname); + name_at_start = (boolean)type_is_pname(mdat); + } + + if (name_at_start && (article == ARTICLE_YOUR || !has_adjectives)) { + if (mdat == &mons[PM_WIZARD_OF_YENDOR]) + article = ARTICLE_THE; + else + article = ARTICLE_NONE; + } else if ((mdat->geno & G_UNIQ) && article == ARTICLE_A) { + article = ARTICLE_THE; + } + + { + char buf2[BUFSZ]; + + switch(article) { + case ARTICLE_YOUR: + Strcpy(buf2, "your "); + Strcat(buf2, buf); + Strcpy(buf, buf2); + return buf; + case ARTICLE_THE: + Strcpy(buf2, "the "); + Strcat(buf2, buf); + Strcpy(buf, buf2); + return buf; + case ARTICLE_A: + return(an(buf)); + case ARTICLE_NONE: + default: + return buf; + } + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +char * +l_monnam(mtmp) +register struct monst *mtmp; +{ + return(x_monnam(mtmp, ARTICLE_NONE, (char *)0, + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, TRUE)); +} + +#endif /* OVLB */ +#ifdef OVL0 + +char * +mon_nam(mtmp) +register struct monst *mtmp; +{ + return(x_monnam(mtmp, ARTICLE_THE, (char *)0, + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, FALSE)); +} + +/* print the name as if mon_nam() was called, but assume that the player + * can always see the monster--used for probing and for monsters aggravating + * the player with a cursed potion of invisibility + */ +char * +noit_mon_nam(mtmp) +register struct monst *mtmp; +{ + return(x_monnam(mtmp, ARTICLE_THE, (char *)0, + mtmp->mnamelth ? (SUPPRESS_SADDLE|SUPPRESS_IT) : + SUPPRESS_IT, FALSE)); +} + +char * +Monnam(mtmp) +register struct monst *mtmp; +{ + register char *bp = mon_nam(mtmp); + + *bp = highc(*bp); + return(bp); +} + +char * +noit_Monnam(mtmp) +register struct monst *mtmp; +{ + register char *bp = noit_mon_nam(mtmp); + + *bp = highc(*bp); + return(bp); +} + +/* monster's own name */ +char * +m_monnam(mtmp) +struct monst *mtmp; +{ + return x_monnam(mtmp, ARTICLE_NONE, (char *)0, EXACT_NAME, FALSE); +} + +/* pet name: "your little dog" */ +char * +y_monnam(mtmp) +struct monst *mtmp; +{ + int prefix, suppression_flag; + + prefix = mtmp->mtame ? ARTICLE_YOUR : ARTICLE_THE; + suppression_flag = (mtmp->mnamelth +#ifdef STEED + /* "saddled" is redundant when mounted */ + || mtmp == u.usteed +#endif + ) ? SUPPRESS_SADDLE : 0; + + return x_monnam(mtmp, prefix, (char *)0, suppression_flag, FALSE); +} + +#endif /* OVL0 */ +#ifdef OVLB + +char * +Adjmonnam(mtmp, adj) +register struct monst *mtmp; +register const char *adj; +{ + register char *bp = x_monnam(mtmp, ARTICLE_THE, adj, + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, FALSE); + + *bp = highc(*bp); + return(bp); +} + +char * +a_monnam(mtmp) +register struct monst *mtmp; +{ + return x_monnam(mtmp, ARTICLE_A, (char *)0, + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, FALSE); +} + +char * +Amonnam(mtmp) +register struct monst *mtmp; +{ + register char *bp = a_monnam(mtmp); + + *bp = highc(*bp); + return(bp); +} + +/* used for monster ID by the '/', ';', and 'C' commands to block remote + identification of the endgame altars via their attending priests */ +char * +distant_monnam(mon, article, outbuf) +struct monst *mon; +int article; /* only ARTICLE_NONE and ARTICLE_THE are handled here */ +char *outbuf; +{ + /* high priest(ess)'s identity is concealed on the Astral Plane, + unless you're adjacent (overridden for hallucination which does + its own obfuscation) */ + if (mon->data == &mons[PM_HIGH_PRIEST] && !Hallucination && + Is_astralevel(&u.uz) && distu(mon->mx, mon->my) > 2) { + Strcpy(outbuf, article == ARTICLE_THE ? "the " : ""); + Strcat(outbuf, mon->female ? "high priestess" : "high priest"); + } else { + Strcpy(outbuf, x_monnam(mon, article, (char *)0, 0, TRUE)); + } + return outbuf; +} + +static const char * const bogusmons[] = { + "jumbo shrimp", "giant pigmy", "gnu", "killer penguin", + "giant cockroach", "giant slug", "maggot", "pterodactyl", + "tyrannosaurus rex", "basilisk", "beholder", "nightmare", + "efreeti", "marid", "rot grub", "bookworm", "master lichen", + "shadow", "hologram", "jester", "attorney", "sleazoid", + "killer tomato", "amazon", "robot", "battlemech", + "rhinovirus", "harpy", "lion-dog", "rat-ant", "Y2K bug", + /* misc. */ + "grue", "Christmas-tree monster", "luck sucker", "paskald", + "brogmoid", "dornbeast", /* Quendor (Zork, &c.) */ + "Ancient Multi-Hued Dragon", "Evil Iggy", + /* Moria */ + "emu", "kestrel", "xeroc", "venus flytrap", + /* Rogue */ + "creeping coins", /* Wizardry */ + "hydra", "siren", /* Greek legend */ + "killer bunny", /* Monty Python */ + "rodent of unusual size", /* The Princess Bride */ + "Smokey the bear", /* "Only you can prevent forest fires!" */ + "Luggage", /* Discworld */ + "Ent", /* Lord of the Rings */ + "tangle tree", "nickelpede", "wiggle", /* Xanth */ + "white rabbit", "snark", /* Lewis Carroll */ + "pushmi-pullyu", /* Dr. Doolittle */ + "smurf", /* The Smurfs */ + "tribble", "Klingon", "Borg", /* Star Trek */ + "Ewok", /* Star Wars */ + "Totoro", /* Tonari no Totoro */ + "ohmu", /* Nausicaa */ + "youma", /* Sailor Moon */ + "nyaasu", /* Pokemon (Meowth) */ + "Godzilla", "King Kong", /* monster movies */ + "earthquake beast", /* old L of SH */ + "Invid", /* Robotech */ + "Terminator", /* The Terminator */ + "boomer", /* Bubblegum Crisis */ + "Dalek", /* Dr. Who ("Exterminate!") */ + "microscopic space fleet", "Ravenous Bugblatter Beast of Traal", + /* HGttG */ + "teenage mutant ninja turtle", /* TMNT */ + "samurai rabbit", /* Usagi Yojimbo */ + "aardvark", /* Cerebus */ + "Audrey II", /* Little Shop of Horrors */ + "witch doctor", "one-eyed one-horned flying purple people eater", + /* 50's rock 'n' roll */ + "Barney the dinosaur", /* saccharine kiddy TV */ + "Morgoth", /* Angband */ + "Vorlon", /* Babylon 5 */ + "questing beast", /* King Arthur */ + "Predator", /* Movie */ + "mother-in-law" /* common pest */ +}; + + +/* Return a random monster name, for hallucination. + * KNOWN BUG: May be a proper name (Godzilla, Barney), may not + * (the Terminator, a Dalek). There's no elegant way to deal + * with this without radically modifying the calling functions. + */ +const char * +rndmonnam() +{ + int name; + + do { + name = rn1(SPECIAL_PM + SIZE(bogusmons) - LOW_PM, LOW_PM); + } while (name < SPECIAL_PM && + (type_is_pname(&mons[name]) || (mons[name].geno & G_NOGEN))); + + if (name >= SPECIAL_PM) return bogusmons[name - SPECIAL_PM]; + return mons[name].mname; +} + +#ifdef REINCARNATION +const char * +roguename() /* Name of a Rogue player */ +{ + char *i, *opts; + + if ((opts = nh_getenv("ROGUEOPTS")) != 0) { + for (i = opts; *i; i++) + if (!strncmp("name=",i,5)) { + char *j; + if ((j = index(i+5,',')) != 0) + *j = (char)0; + return i+5; + } + } + return rn2(3) ? (rn2(2) ? "Michael Toy" : "Kenneth Arnold") + : "Glenn Wichman"; +} +#endif /* REINCARNATION */ +#endif /* OVLB */ + +#ifdef OVL2 + +static NEARDATA const char * const hcolors[] = { + "ultraviolet", "infrared", "bluish-orange", + "reddish-green", "dark white", "light black", "sky blue-pink", + "salty", "sweet", "sour", "bitter", + "striped", "spiral", "swirly", "plaid", "checkered", "argyle", + "paisley", "blotchy", "guernsey-spotted", "polka-dotted", + "square", "round", "triangular", + "cabernet", "sangria", "fuchsia", "wisteria", + "lemon-lime", "strawberry-banana", "peppermint", + "romantic", "incandescent" +}; + +const char * +hcolor(colorpref) +const char *colorpref; +{ + return (Hallucination || !colorpref) ? + hcolors[rn2(SIZE(hcolors))] : colorpref; +} + +/* return a random real color unless hallucinating */ +const char * +rndcolor() +{ + int k = rn2(CLR_MAX); + return Hallucination ? hcolor((char *)0) : (k == NO_COLOR) ? + "colorless" : c_obj_colors[k]; +} + +/* Aliases for road-runner nemesis + */ +static const char * const coynames[] = { + "Carnivorous Vulgaris","Road-Runnerus Digestus", + "Eatibus Anythingus" ,"Famishus-Famishus", + "Eatibus Almost Anythingus","Eatius Birdius", + "Famishius Fantasticus","Eternalii Famishiis", + "Famishus Vulgarus","Famishius Vulgaris Ingeniusi", + "Eatius-Slobbius","Hardheadipus Oedipus", + "Carnivorous Slobbius","Hard-Headipus Ravenus", + "Evereadii Eatibus","Apetitius Giganticus", + "Hungrii Flea-Bagius","Overconfidentii Vulgaris", + "Caninus Nervous Rex","Grotesques Appetitus", + "Nemesis Riduclii","Canis latrans" +}; + +char * +coyotename(mtmp, buf) +struct monst *mtmp; +char *buf; +{ + if (mtmp && buf) { + Sprintf(buf, "%s - %s", + x_monnam(mtmp, ARTICLE_NONE, (char *)0, 0, TRUE), + mtmp->mcan ? coynames[SIZE(coynames)-1] : coynames[rn2(SIZE(coynames)-1)]); + } + return buf; +} +#endif /* OVL2 */ + +/*do_name.c*/ diff --git a/src/do_wear.c b/src/do_wear.c new file mode 100644 index 0000000..c197c1b --- /dev/null +++ b/src/do_wear.c @@ -0,0 +1,2185 @@ +/* SCCS Id: @(#)do_wear.c 3.4 2003/11/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifndef OVLB + +STATIC_DCL long takeoff_mask, taking_off; + +#else /* OVLB */ + +STATIC_OVL NEARDATA long takeoff_mask = 0L; +static NEARDATA long taking_off = 0L; + +static NEARDATA int todelay; +static boolean cancelled_don = FALSE; + +static NEARDATA const char see_yourself[] = "see yourself"; +static NEARDATA const char unknown_type[] = "Unknown type of %s (%d)"; +static NEARDATA const char c_armor[] = "armor", + c_suit[] = "suit", +#ifdef TOURIST + c_shirt[] = "shirt", +#endif + c_cloak[] = "cloak", + c_gloves[] = "gloves", + c_boots[] = "boots", + c_helmet[] = "helmet", + c_shield[] = "shield", + c_weapon[] = "weapon", + c_sword[] = "sword", + c_axe[] = "axe", + c_that_[] = "that"; + +static NEARDATA const long takeoff_order[] = { WORN_BLINDF, W_WEP, + WORN_SHIELD, WORN_GLOVES, LEFT_RING, RIGHT_RING, WORN_CLOAK, + WORN_HELMET, WORN_AMUL, WORN_ARMOR, +#ifdef TOURIST + WORN_SHIRT, +#endif + WORN_BOOTS, W_SWAPWEP, W_QUIVER, 0L }; + +STATIC_DCL void FDECL(on_msg, (struct obj *)); +STATIC_PTR int NDECL(Armor_on); +STATIC_PTR int NDECL(Boots_on); +STATIC_DCL int NDECL(Cloak_on); +STATIC_PTR int NDECL(Helmet_on); +STATIC_PTR int NDECL(Gloves_on); +STATIC_PTR int NDECL(Shield_on); +#ifdef TOURIST +STATIC_PTR int NDECL(Shirt_on); +#endif +STATIC_DCL void NDECL(Amulet_on); +STATIC_DCL void FDECL(Ring_off_or_gone, (struct obj *, BOOLEAN_P)); +STATIC_PTR int FDECL(select_off, (struct obj *)); +STATIC_DCL struct obj *NDECL(do_takeoff); +STATIC_PTR int NDECL(take_off); +STATIC_DCL int FDECL(menu_remarm, (int)); +STATIC_DCL void FDECL(already_wearing, (const char*)); +STATIC_DCL void FDECL(already_wearing2, (const char*, const char*)); + +void +off_msg(otmp) +register struct obj *otmp; +{ + if(flags.verbose) + You("were wearing %s.", doname(otmp)); +} + +/* for items that involve no delay */ +STATIC_OVL void +on_msg(otmp) +register struct obj *otmp; +{ + if (flags.verbose) { + char how[BUFSZ]; + + how[0] = '\0'; + if (otmp->otyp == TOWEL) + Sprintf(how, " around your %s", body_part(HEAD)); + You("are now wearing %s%s.", + obj_is_pname(otmp) ? the(xname(otmp)) : an(xname(otmp)), + how); + } +} + +/* + * The Type_on() functions should be called *after* setworn(). + * The Type_off() functions call setworn() themselves. + */ + +STATIC_PTR +int +Boots_on() +{ + long oldprop = + u.uprops[objects[uarmf->otyp].oc_oprop].extrinsic & ~WORN_BOOTS; + + switch(uarmf->otyp) { + case LOW_BOOTS: + case IRON_SHOES: + case HIGH_BOOTS: + case JUMPING_BOOTS: + case KICKING_BOOTS: + break; + case WATER_WALKING_BOOTS: + if (u.uinwater) spoteffects(TRUE); + break; + case SPEED_BOOTS: + /* Speed boots are still better than intrinsic speed, */ + /* though not better than potion speed */ + if (!oldprop && !(HFast & TIMEOUT)) { + makeknown(uarmf->otyp); + You_feel("yourself speed up%s.", + (oldprop || HFast) ? " a bit more" : ""); + } + break; + case ELVEN_BOOTS: + if (!oldprop && !HStealth && !BStealth) { + makeknown(uarmf->otyp); + You("walk very quietly."); + } + break; + case FUMBLE_BOOTS: + if (!oldprop && !(HFumbling & ~TIMEOUT)) + incr_itimeout(&HFumbling, rnd(20)); + break; + case LEVITATION_BOOTS: + if (!oldprop && !HLevitation) { + makeknown(uarmf->otyp); + float_up(); + spoteffects(FALSE); + } + break; + default: impossible(unknown_type, c_boots, uarmf->otyp); + } + return 0; +} + +int +Boots_off() +{ + int otyp = uarmf->otyp; + long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_BOOTS; + + takeoff_mask &= ~W_ARMF; + /* For levitation, float_down() returns if Levitation, so we + * must do a setworn() _before_ the levitation case. + */ + setworn((struct obj *)0, W_ARMF); + switch (otyp) { + case SPEED_BOOTS: + if (!Very_fast && !cancelled_don) { + makeknown(otyp); + You_feel("yourself slow down%s.", + Fast ? " a bit" : ""); + } + break; + case WATER_WALKING_BOOTS: + if (is_pool(u.ux,u.uy) && !Levitation && !Flying && + !is_clinger(youmonst.data) && !cancelled_don) { + makeknown(otyp); + /* make boots known in case you survive the drowning */ + spoteffects(TRUE); + } + break; + case ELVEN_BOOTS: + if (!oldprop && !HStealth && !BStealth && !cancelled_don) { + makeknown(otyp); + You("sure are noisy."); + } + break; + case FUMBLE_BOOTS: + if (!oldprop && !(HFumbling & ~TIMEOUT)) + HFumbling = EFumbling = 0; + break; + case LEVITATION_BOOTS: + if (!oldprop && !HLevitation && !cancelled_don) { + (void) float_down(0L, 0L); + makeknown(otyp); + } + break; + case LOW_BOOTS: + case IRON_SHOES: + case HIGH_BOOTS: + case JUMPING_BOOTS: + case KICKING_BOOTS: + break; + default: impossible(unknown_type, c_boots, otyp); + } + cancelled_don = FALSE; + return 0; +} + +STATIC_OVL int +Cloak_on() +{ + long oldprop = + u.uprops[objects[uarmc->otyp].oc_oprop].extrinsic & ~WORN_CLOAK; + + switch(uarmc->otyp) { + case ELVEN_CLOAK: + case CLOAK_OF_PROTECTION: + case CLOAK_OF_DISPLACEMENT: + makeknown(uarmc->otyp); + break; + case ORCISH_CLOAK: + case DWARVISH_CLOAK: + case CLOAK_OF_MAGIC_RESISTANCE: + case ROBE: + case LEATHER_CLOAK: + break; + case MUMMY_WRAPPING: + /* Note: it's already being worn, so we have to cheat here. */ + if ((HInvis || EInvis || pm_invisible(youmonst.data)) && !Blind) { + newsym(u.ux,u.uy); + You("can %s!", + See_invisible ? "no longer see through yourself" + : see_yourself); + } + break; + case CLOAK_OF_INVISIBILITY: + /* since cloak of invisibility was worn, we know mummy wrapping + wasn't, so no need to check `oldprop' against blocked */ + if (!oldprop && !HInvis && !Blind) { + makeknown(uarmc->otyp); + newsym(u.ux,u.uy); + pline("Suddenly you can%s yourself.", + See_invisible ? " see through" : "not see"); + } + break; + case OILSKIN_CLOAK: + pline("%s very tightly.", Tobjnam(uarmc, "fit")); + break; + /* Alchemy smock gives poison _and_ acid resistance */ + case ALCHEMY_SMOCK: + EAcid_resistance |= WORN_CLOAK; + break; + default: impossible(unknown_type, c_cloak, uarmc->otyp); + } + return 0; +} + +int +Cloak_off() +{ + int otyp = uarmc->otyp; + long oldprop = u.uprops[objects[otyp].oc_oprop].extrinsic & ~WORN_CLOAK; + + takeoff_mask &= ~W_ARMC; + /* For mummy wrapping, taking it off first resets `Invisible'. */ + setworn((struct obj *)0, W_ARMC); + switch (otyp) { + case ELVEN_CLOAK: + case ORCISH_CLOAK: + case DWARVISH_CLOAK: + case CLOAK_OF_PROTECTION: + case CLOAK_OF_MAGIC_RESISTANCE: + case CLOAK_OF_DISPLACEMENT: + case OILSKIN_CLOAK: + case ROBE: + case LEATHER_CLOAK: + break; + case MUMMY_WRAPPING: + if (Invis && !Blind) { + newsym(u.ux,u.uy); + You("can %s.", + See_invisible ? "see through yourself" + : "no longer see yourself"); + } + break; + case CLOAK_OF_INVISIBILITY: + if (!oldprop && !HInvis && !Blind) { + makeknown(CLOAK_OF_INVISIBILITY); + newsym(u.ux,u.uy); + pline("Suddenly you can %s.", + See_invisible ? "no longer see through yourself" + : see_yourself); + } + break; + /* Alchemy smock gives poison _and_ acid resistance */ + case ALCHEMY_SMOCK: + EAcid_resistance &= ~WORN_CLOAK; + break; + default: impossible(unknown_type, c_cloak, otyp); + } + return 0; +} + +STATIC_PTR +int +Helmet_on() +{ + switch(uarmh->otyp) { + case FEDORA: + case HELMET: + case DENTED_POT: + case ELVEN_LEATHER_HELM: + case DWARVISH_IRON_HELM: + case ORCISH_HELM: + case HELM_OF_TELEPATHY: + break; + case HELM_OF_BRILLIANCE: + adj_abon(uarmh, uarmh->spe); + break; + case CORNUTHAUM: + /* people think marked wizards know what they're talking + * about, but it takes trained arrogance to pull it off, + * and the actual enchantment of the hat is irrelevant. + */ + ABON(A_CHA) += (Role_if(PM_WIZARD) ? 1 : -1); + flags.botl = 1; + makeknown(uarmh->otyp); + break; + case HELM_OF_OPPOSITE_ALIGNMENT: + if (u.ualign.type == A_NEUTRAL) + u.ualign.type = rn2(2) ? A_CHAOTIC : A_LAWFUL; + else u.ualign.type = -(u.ualign.type); + u.ublessed = 0; /* lose your god's protection */ + /* makeknown(uarmh->otyp); -- moved below, after xname() */ + /*FALLTHRU*/ + case DUNCE_CAP: + if (!uarmh->cursed) { + if (Blind) + pline("%s for a moment.", Tobjnam(uarmh, "vibrate")); + else + pline("%s %s for a moment.", + Tobjnam(uarmh, "glow"), hcolor(NH_BLACK)); + curse(uarmh); + } + flags.botl = 1; /* reveal new alignment or INT & WIS */ + if (Hallucination) { + pline("My brain hurts!"); /* Monty Python's Flying Circus */ + } else if (uarmh->otyp == DUNCE_CAP) { + You_feel("%s.", /* track INT change; ignore WIS */ + ACURR(A_INT) <= (ABASE(A_INT) + ABON(A_INT) + ATEMP(A_INT)) ? + "like sitting in a corner" : "giddy"); + } else { + Your("mind oscillates briefly."); + makeknown(HELM_OF_OPPOSITE_ALIGNMENT); + } + break; + default: impossible(unknown_type, c_helmet, uarmh->otyp); + } + return 0; +} + +int +Helmet_off() +{ + takeoff_mask &= ~W_ARMH; + + switch(uarmh->otyp) { + case FEDORA: + case HELMET: + case DENTED_POT: + case ELVEN_LEATHER_HELM: + case DWARVISH_IRON_HELM: + case ORCISH_HELM: + break; + case DUNCE_CAP: + flags.botl = 1; + break; + case CORNUTHAUM: + if (!cancelled_don) { + ABON(A_CHA) += (Role_if(PM_WIZARD) ? -1 : 1); + flags.botl = 1; + } + break; + case HELM_OF_TELEPATHY: + /* need to update ability before calling see_monsters() */ + setworn((struct obj *)0, W_ARMH); + see_monsters(); + return 0; + case HELM_OF_BRILLIANCE: + if (!cancelled_don) adj_abon(uarmh, -uarmh->spe); + break; + case HELM_OF_OPPOSITE_ALIGNMENT: + u.ualign.type = u.ualignbase[A_CURRENT]; + u.ublessed = 0; /* lose the other god's protection */ + flags.botl = 1; + break; + default: impossible(unknown_type, c_helmet, uarmh->otyp); + } + setworn((struct obj *)0, W_ARMH); + cancelled_don = FALSE; + return 0; +} + +STATIC_PTR +int +Gloves_on() +{ + long oldprop = + u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES; + + switch(uarmg->otyp) { + case LEATHER_GLOVES: + break; + case GAUNTLETS_OF_FUMBLING: + if (!oldprop && !(HFumbling & ~TIMEOUT)) + incr_itimeout(&HFumbling, rnd(20)); + break; + case GAUNTLETS_OF_POWER: + makeknown(uarmg->otyp); + flags.botl = 1; /* taken care of in attrib.c */ + break; + case GAUNTLETS_OF_DEXTERITY: + adj_abon(uarmg, uarmg->spe); + break; + default: impossible(unknown_type, c_gloves, uarmg->otyp); + } + return 0; +} + +int +Gloves_off() +{ + long oldprop = + u.uprops[objects[uarmg->otyp].oc_oprop].extrinsic & ~WORN_GLOVES; + + takeoff_mask &= ~W_ARMG; + + switch(uarmg->otyp) { + case LEATHER_GLOVES: + break; + case GAUNTLETS_OF_FUMBLING: + if (!oldprop && !(HFumbling & ~TIMEOUT)) + HFumbling = EFumbling = 0; + break; + case GAUNTLETS_OF_POWER: + makeknown(uarmg->otyp); + flags.botl = 1; /* taken care of in attrib.c */ + break; + case GAUNTLETS_OF_DEXTERITY: + if (!cancelled_don) adj_abon(uarmg, -uarmg->spe); + break; + default: impossible(unknown_type, c_gloves, uarmg->otyp); + } + setworn((struct obj *)0, W_ARMG); + cancelled_don = FALSE; + (void) encumber_msg(); /* immediate feedback for GoP */ + + /* Prevent wielding cockatrice when not wearing gloves */ + if (uwep && uwep->otyp == CORPSE && + touch_petrifies(&mons[uwep->corpsenm])) { + char kbuf[BUFSZ]; + + You("wield the %s in your bare %s.", + corpse_xname(uwep, TRUE), makeplural(body_part(HAND))); + Strcpy(kbuf, an(corpse_xname(uwep, TRUE))); + instapetrify(kbuf); + uwepgone(); /* life-saved still doesn't allow touching cockatrice */ + } + + /* KMH -- ...or your secondary weapon when you're wielding it */ + if (u.twoweap && uswapwep && uswapwep->otyp == CORPSE && + touch_petrifies(&mons[uswapwep->corpsenm])) { + char kbuf[BUFSZ]; + + You("wield the %s in your bare %s.", + corpse_xname(uswapwep, TRUE), body_part(HAND)); + + Strcpy(kbuf, an(corpse_xname(uswapwep, TRUE))); + instapetrify(kbuf); + uswapwepgone(); /* lifesaved still doesn't allow touching cockatrice */ + } + + return 0; +} + +STATIC_OVL int +Shield_on() +{ +/* + switch (uarms->otyp) { + case SMALL_SHIELD: + case ELVEN_SHIELD: + case URUK_HAI_SHIELD: + case ORCISH_SHIELD: + case DWARVISH_ROUNDSHIELD: + case LARGE_SHIELD: + case SHIELD_OF_REFLECTION: + break; + default: impossible(unknown_type, c_shield, uarms->otyp); + } +*/ + return 0; +} + +int +Shield_off() +{ + takeoff_mask &= ~W_ARMS; +/* + switch (uarms->otyp) { + case SMALL_SHIELD: + case ELVEN_SHIELD: + case URUK_HAI_SHIELD: + case ORCISH_SHIELD: + case DWARVISH_ROUNDSHIELD: + case LARGE_SHIELD: + case SHIELD_OF_REFLECTION: + break; + default: impossible(unknown_type, c_shield, uarms->otyp); + } +*/ + setworn((struct obj *)0, W_ARMS); + return 0; +} + +#ifdef TOURIST +STATIC_OVL int +Shirt_on() +{ +/* + switch (uarmu->otyp) { + case HAWAIIAN_SHIRT: + case T_SHIRT: + break; + default: impossible(unknown_type, c_shirt, uarmu->otyp); + } +*/ + return 0; +} + +int +Shirt_off() +{ + takeoff_mask &= ~W_ARMU; +/* + switch (uarmu->otyp) { + case HAWAIIAN_SHIRT: + case T_SHIRT: + break; + default: impossible(unknown_type, c_shirt, uarmu->otyp); + } +*/ + setworn((struct obj *)0, W_ARMU); + return 0; +} +#endif /*TOURIST*/ + +/* This must be done in worn.c, because one of the possible intrinsics conferred + * is fire resistance, and we have to immediately set HFire_resistance in worn.c + * since worn.c will check it before returning. + */ +STATIC_PTR +int +Armor_on() +{ + return 0; +} + +int +Armor_off() +{ + takeoff_mask &= ~W_ARM; + setworn((struct obj *)0, W_ARM); + cancelled_don = FALSE; + return 0; +} + +/* The gone functions differ from the off functions in that if you die from + * taking it off and have life saving, you still die. + */ +int +Armor_gone() +{ + takeoff_mask &= ~W_ARM; + setnotworn(uarm); + cancelled_don = FALSE; + return 0; +} + +STATIC_OVL void +Amulet_on() +{ + switch(uamul->otyp) { + case AMULET_OF_ESP: + case AMULET_OF_LIFE_SAVING: + case AMULET_VERSUS_POISON: + case AMULET_OF_REFLECTION: + case AMULET_OF_MAGICAL_BREATHING: + case FAKE_AMULET_OF_YENDOR: + break; + case AMULET_OF_UNCHANGING: + if (Slimed) { + Slimed = 0; + flags.botl = 1; + } + break; + case AMULET_OF_CHANGE: + { + int orig_sex = poly_gender(); + + if (Unchanging) break; + change_sex(); + /* Don't use same message as polymorph */ + if (orig_sex != poly_gender()) { + makeknown(AMULET_OF_CHANGE); + You("are suddenly very %s!", flags.female ? "feminine" + : "masculine"); + flags.botl = 1; + } else + /* already polymorphed into single-gender monster; only + changed the character's base sex */ + You("don't feel like yourself."); + pline_The("amulet disintegrates!"); + if (orig_sex == poly_gender() && uamul->dknown && + !objects[AMULET_OF_CHANGE].oc_name_known && + !objects[AMULET_OF_CHANGE].oc_uname) + docall(uamul); + useup(uamul); + break; + } + case AMULET_OF_STRANGULATION: + makeknown(AMULET_OF_STRANGULATION); + pline("It constricts your throat!"); + Strangled = 6; + break; + case AMULET_OF_RESTFUL_SLEEP: + HSleeping = rnd(100); + break; + case AMULET_OF_YENDOR: + break; + } +} + +void +Amulet_off() +{ + takeoff_mask &= ~W_AMUL; + + switch(uamul->otyp) { + case AMULET_OF_ESP: + /* need to update ability before calling see_monsters() */ + setworn((struct obj *)0, W_AMUL); + see_monsters(); + return; + case AMULET_OF_LIFE_SAVING: + case AMULET_VERSUS_POISON: + case AMULET_OF_REFLECTION: + case AMULET_OF_CHANGE: + case AMULET_OF_UNCHANGING: + case FAKE_AMULET_OF_YENDOR: + break; + case AMULET_OF_MAGICAL_BREATHING: + if (Underwater) { + /* HMagical_breathing must be set off + before calling drown() */ + setworn((struct obj *)0, W_AMUL); + if (!breathless(youmonst.data) && !amphibious(youmonst.data) + && !Swimming) { + You("suddenly inhale an unhealthy amount of water!"); + (void) drown(); + } + return; + } + break; + case AMULET_OF_STRANGULATION: + if (Strangled) { + You("can breathe more easily!"); + Strangled = 0; + } + break; + case AMULET_OF_RESTFUL_SLEEP: + setworn((struct obj *)0, W_AMUL); + if (!ESleeping) + HSleeping = 0; + return; + case AMULET_OF_YENDOR: + break; + } + setworn((struct obj *)0, W_AMUL); + return; +} + +void +Ring_on(obj) +register struct obj *obj; +{ + long oldprop = u.uprops[objects[obj->otyp].oc_oprop].extrinsic; + int old_attrib, which; + + if (obj == uwep) setuwep((struct obj *) 0); + if (obj == uswapwep) setuswapwep((struct obj *) 0); + if (obj == uquiver) setuqwep((struct obj *) 0); + + /* only mask out W_RING when we don't have both + left and right rings of the same type */ + if ((oldprop & W_RING) != W_RING) oldprop &= ~W_RING; + + switch(obj->otyp){ + case RIN_TELEPORTATION: + case RIN_REGENERATION: + case RIN_SEARCHING: + case RIN_STEALTH: + case RIN_HUNGER: + case RIN_AGGRAVATE_MONSTER: + case RIN_POISON_RESISTANCE: + case RIN_FIRE_RESISTANCE: + case RIN_COLD_RESISTANCE: + case RIN_SHOCK_RESISTANCE: + case RIN_CONFLICT: + case RIN_TELEPORT_CONTROL: + case RIN_POLYMORPH: + case RIN_POLYMORPH_CONTROL: + case RIN_FREE_ACTION: + case RIN_SLOW_DIGESTION: + case RIN_SUSTAIN_ABILITY: + case MEAT_RING: + break; + case RIN_WARNING: + see_monsters(); + break; + case RIN_SEE_INVISIBLE: + /* can now see invisible monsters */ + set_mimic_blocking(); /* do special mimic handling */ + see_monsters(); +#ifdef INVISIBLE_OBJECTS + see_objects(); +#endif + + if (Invis && !oldprop && !HSee_invisible && + !perceives(youmonst.data) && !Blind) { + newsym(u.ux,u.uy); + pline("Suddenly you are transparent, but there!"); + makeknown(RIN_SEE_INVISIBLE); + } + break; + case RIN_INVISIBILITY: + if (!oldprop && !HInvis && !BInvis && !Blind) { + makeknown(RIN_INVISIBILITY); + newsym(u.ux,u.uy); + self_invis_message(); + } + break; + case RIN_LEVITATION: + if (!oldprop && !HLevitation) { + float_up(); + makeknown(RIN_LEVITATION); + spoteffects(FALSE); /* for sinks */ + } + break; + case RIN_GAIN_STRENGTH: + which = A_STR; + goto adjust_attrib; + case RIN_GAIN_CONSTITUTION: + which = A_CON; + goto adjust_attrib; + case RIN_ADORNMENT: + which = A_CHA; + adjust_attrib: + old_attrib = ACURR(which); + ABON(which) += obj->spe; + if (ACURR(which) != old_attrib || + (objects[obj->otyp].oc_name_known && + old_attrib != 25 && old_attrib != 3)) { + flags.botl = 1; + makeknown(obj->otyp); + obj->known = 1; + update_inventory(); + } + break; + case RIN_INCREASE_ACCURACY: /* KMH */ + u.uhitinc += obj->spe; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc += obj->spe; + break; + case RIN_PROTECTION_FROM_SHAPE_CHAN: + rescham(); + break; + case RIN_PROTECTION: + if (obj->spe || objects[RIN_PROTECTION].oc_name_known) { + flags.botl = 1; + makeknown(RIN_PROTECTION); + obj->known = 1; + update_inventory(); + } + break; + } +} + +STATIC_OVL void +Ring_off_or_gone(obj,gone) +register struct obj *obj; +boolean gone; +{ + long mask = (obj->owornmask & W_RING); + int old_attrib, which; + + takeoff_mask &= ~mask; + if(!(u.uprops[objects[obj->otyp].oc_oprop].extrinsic & mask)) + impossible("Strange... I didn't know you had that ring."); + if(gone) setnotworn(obj); + else setworn((struct obj *)0, obj->owornmask); + + switch(obj->otyp) { + case RIN_TELEPORTATION: + case RIN_REGENERATION: + case RIN_SEARCHING: + case RIN_STEALTH: + case RIN_HUNGER: + case RIN_AGGRAVATE_MONSTER: + case RIN_POISON_RESISTANCE: + case RIN_FIRE_RESISTANCE: + case RIN_COLD_RESISTANCE: + case RIN_SHOCK_RESISTANCE: + case RIN_CONFLICT: + case RIN_TELEPORT_CONTROL: + case RIN_POLYMORPH: + case RIN_POLYMORPH_CONTROL: + case RIN_FREE_ACTION: + case RIN_SLOW_DIGESTION: + case RIN_SUSTAIN_ABILITY: + case MEAT_RING: + break; + case RIN_WARNING: + see_monsters(); + break; + case RIN_SEE_INVISIBLE: + /* Make invisible monsters go away */ + if (!See_invisible) { + set_mimic_blocking(); /* do special mimic handling */ + see_monsters(); +#ifdef INVISIBLE_OBJECTS + see_objects(); +#endif + } + + if (Invisible && !Blind) { + newsym(u.ux,u.uy); + pline("Suddenly you cannot see yourself."); + makeknown(RIN_SEE_INVISIBLE); + } + break; + case RIN_INVISIBILITY: + if (!Invis && !BInvis && !Blind) { + newsym(u.ux,u.uy); + Your("body seems to unfade%s.", + See_invisible ? " completely" : ".."); + makeknown(RIN_INVISIBILITY); + } + break; + case RIN_LEVITATION: + (void) float_down(0L, 0L); + if (!Levitation) makeknown(RIN_LEVITATION); + break; + case RIN_GAIN_STRENGTH: + which = A_STR; + goto adjust_attrib; + case RIN_GAIN_CONSTITUTION: + which = A_CON; + goto adjust_attrib; + case RIN_ADORNMENT: + which = A_CHA; + adjust_attrib: + old_attrib = ACURR(which); + ABON(which) -= obj->spe; + if (ACURR(which) != old_attrib) { + flags.botl = 1; + makeknown(obj->otyp); + obj->known = 1; + update_inventory(); + } + break; + case RIN_INCREASE_ACCURACY: /* KMH */ + u.uhitinc -= obj->spe; + break; + case RIN_INCREASE_DAMAGE: + u.udaminc -= obj->spe; + break; + case RIN_PROTECTION: + /* might have forgotten it due to amnesia */ + if (obj->spe) { + flags.botl = 1; + makeknown(RIN_PROTECTION); + obj->known = 1; + update_inventory(); + } + case RIN_PROTECTION_FROM_SHAPE_CHAN: + /* If you're no longer protected, let the chameleons + * change shape again -dgk + */ + restartcham(); + break; + } +} + +void +Ring_off(obj) +struct obj *obj; +{ + Ring_off_or_gone(obj,FALSE); +} + +void +Ring_gone(obj) +struct obj *obj; +{ + Ring_off_or_gone(obj,TRUE); +} + +void +Blindf_on(otmp) +register struct obj *otmp; +{ + boolean already_blind = Blind, changed = FALSE; + + if (otmp == uwep) + setuwep((struct obj *) 0); + if (otmp == uswapwep) + setuswapwep((struct obj *) 0); + if (otmp == uquiver) + setuqwep((struct obj *) 0); + setworn(otmp, W_TOOL); + on_msg(otmp); + + if (Blind && !already_blind) { + changed = TRUE; + if (flags.verbose) You_cant("see any more."); + /* set ball&chain variables before the hero goes blind */ + if (Punished) set_bc(0); + } else if (already_blind && !Blind) { + changed = TRUE; + /* "You are now wearing the Eyes of the Overworld." */ + You("can see!"); + } + if (changed) { + /* blindness has just been toggled */ + if (Blind_telepat || Infravision) see_monsters(); + vision_full_recalc = 1; /* recalc vision limits */ + flags.botl = 1; + } +} + +void +Blindf_off(otmp) +register struct obj *otmp; +{ + boolean was_blind = Blind, changed = FALSE; + + takeoff_mask &= ~W_TOOL; + setworn((struct obj *)0, otmp->owornmask); + off_msg(otmp); + + if (Blind) { + if (was_blind) { + /* "still cannot see" makes no sense when removing lenses + since they can't have been the cause of your blindness */ + if (otmp->otyp != LENSES) + You("still cannot see."); + } else { + changed = TRUE; /* !was_blind */ + /* "You were wearing the Eyes of the Overworld." */ + You_cant("see anything now!"); + /* set ball&chain variables before the hero goes blind */ + if (Punished) set_bc(0); + } + } else if (was_blind) { + changed = TRUE; /* !Blind */ + You("can see again."); + } + if (changed) { + /* blindness has just been toggled */ + if (Blind_telepat || Infravision) see_monsters(); + vision_full_recalc = 1; /* recalc vision limits */ + flags.botl = 1; + } +} + +/* called in main to set intrinsics of worn start-up items */ +void +set_wear() +{ +#ifdef TOURIST + if (uarmu) (void) Shirt_on(); +#endif + if (uarm) (void) Armor_on(); + if (uarmc) (void) Cloak_on(); + if (uarmf) (void) Boots_on(); + if (uarmg) (void) Gloves_on(); + if (uarmh) (void) Helmet_on(); + if (uarms) (void) Shield_on(); +} + +/* check whether the target object is currently being put on (or taken off) */ +boolean +donning(otmp) /* also checks for doffing */ +register struct obj *otmp; +{ + /* long what = (occupation == take_off) ? taking_off : 0L; */ + long what = taking_off; /* if nonzero, occupation is implied */ + boolean result = FALSE; + + if (otmp == uarm) + result = (afternmv == Armor_on || afternmv == Armor_off || + what == WORN_ARMOR); +#ifdef TOURIST + else if (otmp == uarmu) + result = (afternmv == Shirt_on || afternmv == Shirt_off || + what == WORN_SHIRT); +#endif + else if (otmp == uarmc) + result = (afternmv == Cloak_on || afternmv == Cloak_off || + what == WORN_CLOAK); + else if (otmp == uarmf) + result = (afternmv == Boots_on || afternmv == Boots_off || + what == WORN_BOOTS); + else if (otmp == uarmh) + result = (afternmv == Helmet_on || afternmv == Helmet_off || + what == WORN_HELMET); + else if (otmp == uarmg) + result = (afternmv == Gloves_on || afternmv == Gloves_off || + what == WORN_GLOVES); + else if (otmp == uarms) + result = (afternmv == Shield_on || afternmv == Shield_off || + what == WORN_SHIELD); + + return result; +} + +void +cancel_don() +{ + /* the piece of armor we were donning/doffing has vanished, so stop + * wasting time on it (and don't dereference it when donning would + * otherwise finish) + */ + cancelled_don = (afternmv == Boots_on || afternmv == Helmet_on || + afternmv == Gloves_on || afternmv == Armor_on); + afternmv = 0; + nomovemsg = (char *)0; + multi = 0; + todelay = 0; + taking_off = 0L; +} + +static NEARDATA const char clothes[] = {ARMOR_CLASS, 0}; +static NEARDATA const char accessories[] = {RING_CLASS, AMULET_CLASS, TOOL_CLASS, FOOD_CLASS, 0}; + +/* the 'T' command */ +int +dotakeoff() +{ + register struct obj *otmp = (struct obj *)0; + int armorpieces = 0; + +#define MOREARM(x) if (x) { armorpieces++; otmp = x; } + MOREARM(uarmh); + MOREARM(uarms); + MOREARM(uarmg); + MOREARM(uarmf); + if (uarmc) { + armorpieces++; + otmp = uarmc; + } else if (uarm) { + armorpieces++; + otmp = uarm; +#ifdef TOURIST + } else if (uarmu) { + armorpieces++; + otmp = uarmu; +#endif + } + if (!armorpieces) { + /* assert( GRAY_DRAGON_SCALES > YELLOW_DRAGON_SCALE_MAIL ); */ + if (uskin) + pline_The("%s merged with your skin!", + uskin->otyp >= GRAY_DRAGON_SCALES ? + "dragon scales are" : "dragon scale mail is"); + else + pline("Not wearing any armor.%s", (iflags.cmdassist && + (uleft || uright || uamul || ublindf)) ? + " Use 'R' command to remove accessories." : ""); + return 0; + } + if (armorpieces > 1) + otmp = getobj(clothes, "take off"); + if (otmp == 0) return(0); + if (!(otmp->owornmask & W_ARMOR)) { + You("are not wearing that."); + return(0); + } + /* note: the `uskin' case shouldn't be able to happen here; dragons + can't wear any armor so will end up with `armorpieces == 0' above */ + if (otmp == uskin || ((otmp == uarm) && uarmc) +#ifdef TOURIST + || ((otmp == uarmu) && (uarmc || uarm)) +#endif + ) { + You_cant("take that off."); + return 0; + } + + reset_remarm(); /* clear takeoff_mask and taking_off */ + (void) select_off(otmp); + if (!takeoff_mask) return 0; + reset_remarm(); /* armoroff() doesn't use takeoff_mask */ + + (void) armoroff(otmp); + return(1); +} + +/* the 'R' command */ +int +doremring() +{ + register struct obj *otmp = 0; + int Accessories = 0; + +#define MOREACC(x) if (x) { Accessories++; otmp = x; } + MOREACC(uleft); + MOREACC(uright); + MOREACC(uamul); + MOREACC(ublindf); + + if(!Accessories) { + pline("Not wearing any accessories.%s", (iflags.cmdassist && + (uarm || uarmc || +#ifdef TOURIST + uarmu || +#endif + uarms || uarmh || uarmg || uarmf)) ? + " Use 'T' command to take off armor." : ""); + return(0); + } + if (Accessories != 1) otmp = getobj(accessories, "remove"); + if(!otmp) return(0); + if(!(otmp->owornmask & (W_RING | W_AMUL | W_TOOL))) { + You("are not wearing that."); + return(0); + } + + reset_remarm(); /* clear takeoff_mask and taking_off */ + (void) select_off(otmp); + if (!takeoff_mask) return 0; + reset_remarm(); /* not used by Ring_/Amulet_/Blindf_off() */ + + if (otmp == uright || otmp == uleft) { + /* Sometimes we want to give the off_msg before removing and + * sometimes after; for instance, "you were wearing a moonstone + * ring (on right hand)" is desired but "you were wearing a + * square amulet (being worn)" is not because of the redundant + * "being worn". + */ + off_msg(otmp); + Ring_off(otmp); + } else if (otmp == uamul) { + Amulet_off(); + off_msg(otmp); + } else if (otmp == ublindf) { + Blindf_off(otmp); /* does its own off_msg */ + } else { + impossible("removing strange accessory?"); + } + return(1); +} + +/* Check if something worn is cursed _and_ unremovable. */ +int +cursed(otmp) +register struct obj *otmp; +{ + /* Curses, like chickens, come home to roost. */ + if((otmp == uwep) ? welded(otmp) : (int)otmp->cursed) { + You("can't. %s cursed.", + (is_boots(otmp) || is_gloves(otmp) || otmp->quan > 1L) + ? "They are" : "It is"); + otmp->bknown = TRUE; + return(1); + } + return(0); +} + +int +armoroff(otmp) +register struct obj *otmp; +{ + register int delay = -objects[otmp->otyp].oc_delay; + + if(cursed(otmp)) return(0); + if(delay) { + nomul(delay); + if (is_helmet(otmp)) { + nomovemsg = "You finish taking off your helmet."; + afternmv = Helmet_off; + } + else if (is_gloves(otmp)) { + nomovemsg = "You finish taking off your gloves."; + afternmv = Gloves_off; + } + else if (is_boots(otmp)) { + nomovemsg = "You finish taking off your boots."; + afternmv = Boots_off; + } + else { + nomovemsg = "You finish taking off your suit."; + afternmv = Armor_off; + } + } else { + /* Be warned! We want off_msg after removing the item to + * avoid "You were wearing ____ (being worn)." However, an + * item which grants fire resistance might cause some trouble + * if removed in Hell and lifesaving puts it back on; in this + * case the message will be printed at the wrong time (after + * the messages saying you died and were lifesaved). Luckily, + * no cloak, shield, or fast-removable armor grants fire + * resistance, so we can safely do the off_msg afterwards. + * Rings do grant fire resistance, but for rings we want the + * off_msg before removal anyway so there's no problem. Take + * care in adding armors granting fire resistance; this code + * might need modification. + * 3.2 (actually 3.1 even): this comment is obsolete since + * fire resistance is not needed for Gehennom. + */ + if(is_cloak(otmp)) + (void) Cloak_off(); + else if(is_shield(otmp)) + (void) Shield_off(); + else setworn((struct obj *)0, otmp->owornmask & W_ARMOR); + off_msg(otmp); + } + takeoff_mask = taking_off = 0L; + return(1); +} + +STATIC_OVL void +already_wearing(cc) +const char *cc; +{ + You("are already wearing %s%c", cc, (cc == c_that_) ? '!' : '.'); +} + +STATIC_OVL void +already_wearing2(cc1, cc2) +const char *cc1, *cc2; +{ + You_cant("wear %s because you're wearing %s there already.", cc1, cc2); +} + +/* + * canwearobj checks to see whether the player can wear a piece of armor + * + * inputs: otmp (the piece of armor) + * noisy (if TRUE give error messages, otherwise be quiet about it) + * output: mask (otmp's armor type) + */ +int +canwearobj(otmp,mask,noisy) +struct obj *otmp; +long *mask; +boolean noisy; +{ + int err = 0; + const char *which; + + which = is_cloak(otmp) ? c_cloak : +#ifdef TOURIST + is_shirt(otmp) ? c_shirt : +#endif + is_suit(otmp) ? c_suit : 0; + if (which && cantweararm(youmonst.data) && + /* same exception for cloaks as used in m_dowear() */ + (which != c_cloak || youmonst.data->msize != MZ_SMALL) && + (racial_exception(&youmonst, otmp) < 1)) { + if (noisy) pline_The("%s will not fit on your body.", which); + return 0; + } else if (otmp->owornmask & W_ARMOR) { + if (noisy) already_wearing(c_that_); + return 0; + } + + if (welded(uwep) && bimanual(uwep) && + (is_suit(otmp) +#ifdef TOURIST + || is_shirt(otmp) +#endif + )) { + if (noisy) + You("cannot do that while holding your %s.", + is_sword(uwep) ? c_sword : c_weapon); + return 0; + } + + if (is_helmet(otmp)) { + if (uarmh) { + if (noisy) already_wearing(an(c_helmet)); + err++; + } else if (Upolyd && has_horns(youmonst.data) && !is_flimsy(otmp)) { + /* (flimsy exception matches polyself handling) */ + if (noisy) + pline_The("%s won't fit over your horn%s.", + c_helmet, plur(num_horns(youmonst.data))); + err++; + } else + *mask = W_ARMH; + } else if (is_shield(otmp)) { + if (uarms) { + if (noisy) already_wearing(an(c_shield)); + err++; + } else if (uwep && bimanual(uwep)) { + if (noisy) + You("cannot wear a shield while wielding a two-handed %s.", + is_sword(uwep) ? c_sword : + (uwep->otyp == BATTLE_AXE) ? c_axe : c_weapon); + err++; + } else if (u.twoweap) { + if (noisy) + You("cannot wear a shield while wielding two weapons."); + err++; + } else + *mask = W_ARMS; + } else if (is_boots(otmp)) { + if (uarmf) { + if (noisy) already_wearing(c_boots); + err++; + } else if (Upolyd && slithy(youmonst.data)) { + if (noisy) You("have no feet..."); /* not body_part(FOOT) */ + err++; + } else if (Upolyd && youmonst.data->mlet == S_CENTAUR) { + /* break_armor() pushes boots off for centaurs, + so don't let dowear() put them back on... */ + if (noisy) pline("You have too many hooves to wear %s.", + c_boots); /* makeplural(body_part(FOOT)) yields + "rear hooves" which sounds odd */ + err++; + } else if (u.utrap && (u.utraptype == TT_BEARTRAP || + u.utraptype == TT_INFLOOR)) { + if (u.utraptype == TT_BEARTRAP) { + if (noisy) Your("%s is trapped!", body_part(FOOT)); + } else { + if (noisy) Your("%s are stuck in the %s!", + makeplural(body_part(FOOT)), + surface(u.ux, u.uy)); + } + err++; + } else + *mask = W_ARMF; + } else if (is_gloves(otmp)) { + if (uarmg) { + if (noisy) already_wearing(c_gloves); + err++; + } else if (welded(uwep)) { + if (noisy) You("cannot wear gloves over your %s.", + is_sword(uwep) ? c_sword : c_weapon); + err++; + } else + *mask = W_ARMG; +#ifdef TOURIST + } else if (is_shirt(otmp)) { + if (uarm || uarmc || uarmu) { + if (uarmu) { + if (noisy) already_wearing(an(c_shirt)); + } else { + if (noisy) You_cant("wear that over your %s.", + (uarm && !uarmc) ? c_armor : cloak_simple_name(uarmc)); + } + err++; + } else + *mask = W_ARMU; +#endif + } else if (is_cloak(otmp)) { + if (uarmc) { + if (noisy) already_wearing(an(cloak_simple_name(uarmc))); + err++; + } else + *mask = W_ARMC; + } else if (is_suit(otmp)) { + if (uarmc) { + if (noisy) You("cannot wear armor over a %s.", cloak_simple_name(uarmc)); + err++; + } else if (uarm) { + if (noisy) already_wearing("some armor"); + err++; + } else + *mask = W_ARM; + } else { + /* getobj can't do this after setting its allow_all flag; that + happens if you have armor for slots that are covered up or + extra armor for slots that are filled */ + if (noisy) silly_thing("wear", otmp); + err++; + } +/* Unnecessary since now only weapons and special items like pick-axes get + * welded to your hand, not armor + if (welded(otmp)) { + if (!err++) { + if (noisy) weldmsg(otmp); + } + } + */ + return !err; +} + +/* the 'W' command */ +int +dowear() +{ + struct obj *otmp; + int delay; + long mask = 0; + + /* cantweararm checks for suits of armor */ + /* verysmall or nohands checks for shields, gloves, etc... */ + if ((verysmall(youmonst.data) || nohands(youmonst.data))) { + pline("Don't even bother."); + return(0); + } + + otmp = getobj(clothes, "wear"); + if(!otmp) return(0); + + if (!canwearobj(otmp,&mask,TRUE)) return(0); + + if (otmp->oartifact && !touch_artifact(otmp, &youmonst)) + return 1; /* costs a turn even though it didn't get worn */ + + if (otmp->otyp == HELM_OF_OPPOSITE_ALIGNMENT && + qstart_level.dnum == u.uz.dnum) { /* in quest */ + if (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL]) + You("narrowly avoid losing all chance at your goal."); + else /* converted */ + You("are suddenly overcome with shame and change your mind."); + u.ublessed = 0; /* lose your god's protection */ + makeknown(otmp->otyp); + flags.botl = 1; + return 1; + } + + otmp->known = TRUE; + if(otmp == uwep) + setuwep((struct obj *)0); + if (otmp == uswapwep) + setuswapwep((struct obj *) 0); + if (otmp == uquiver) + setuqwep((struct obj *) 0); + setworn(otmp, mask); + delay = -objects[otmp->otyp].oc_delay; + if(delay){ + nomul(delay); + if(is_boots(otmp)) afternmv = Boots_on; + if(is_helmet(otmp)) afternmv = Helmet_on; + if(is_gloves(otmp)) afternmv = Gloves_on; + if(otmp == uarm) afternmv = Armor_on; + nomovemsg = "You finish your dressing maneuver."; + } else { + if(is_cloak(otmp)) (void) Cloak_on(); + if (is_shield(otmp)) (void) Shield_on(); +#ifdef TOURIST + if (is_shirt(otmp)) (void) Shirt_on(); +#endif + on_msg(otmp); + } + takeoff_mask = taking_off = 0L; + return(1); +} + +int +doputon() +{ + register struct obj *otmp; + long mask = 0L; + + if(uleft && uright && uamul && ublindf) { + Your("%s%s are full, and you're already wearing an amulet and %s.", + humanoid(youmonst.data) ? "ring-" : "", + makeplural(body_part(FINGER)), + ublindf->otyp==LENSES ? "some lenses" : "a blindfold"); + return(0); + } + otmp = getobj(accessories, "put on"); + if(!otmp) return(0); + if(otmp->owornmask & (W_RING | W_AMUL | W_TOOL)) { + already_wearing(c_that_); + return(0); + } + if(welded(otmp)) { + weldmsg(otmp); + return(0); + } + if(otmp == uwep) + setuwep((struct obj *)0); + if(otmp == uswapwep) + setuswapwep((struct obj *) 0); + if(otmp == uquiver) + setuqwep((struct obj *) 0); + if(otmp->oclass == RING_CLASS || otmp->otyp == MEAT_RING) { + if(nolimbs(youmonst.data)) { + You("cannot make the ring stick to your body."); + return(0); + } + if(uleft && uright){ + There("are no more %s%s to fill.", + humanoid(youmonst.data) ? "ring-" : "", + makeplural(body_part(FINGER))); + return(0); + } + if(uleft) mask = RIGHT_RING; + else if(uright) mask = LEFT_RING; + else do { + char qbuf[QBUFSZ]; + char answer; + + Sprintf(qbuf, "Which %s%s, Right or Left?", + humanoid(youmonst.data) ? "ring-" : "", + body_part(FINGER)); + if(!(answer = yn_function(qbuf, "rl", '\0'))) + return(0); + switch(answer){ + case 'l': + case 'L': + mask = LEFT_RING; + break; + case 'r': + case 'R': + mask = RIGHT_RING; + break; + } + } while(!mask); + if (uarmg && uarmg->cursed) { + uarmg->bknown = TRUE; + You("cannot remove your gloves to put on the ring."); + return(0); + } + if (welded(uwep) && bimanual(uwep)) { + /* welded will set bknown */ + You("cannot free your weapon hands to put on the ring."); + return(0); + } + if (welded(uwep) && mask==RIGHT_RING) { + /* welded will set bknown */ + You("cannot free your weapon hand to put on the ring."); + return(0); + } + if (otmp->oartifact && !touch_artifact(otmp, &youmonst)) + return 1; /* costs a turn even though it didn't get worn */ + setworn(otmp, mask); + Ring_on(otmp); + } else if (otmp->oclass == AMULET_CLASS) { + if(uamul) { + already_wearing("an amulet"); + return(0); + } + if (otmp->oartifact && !touch_artifact(otmp, &youmonst)) + return 1; + setworn(otmp, W_AMUL); + if (otmp->otyp == AMULET_OF_CHANGE) { + Amulet_on(); + /* Don't do a prinv() since the amulet is now gone */ + return(1); + } + Amulet_on(); + } else { /* it's a blindfold, towel, or lenses */ + if (ublindf) { + if (ublindf->otyp == TOWEL) + Your("%s is already covered by a towel.", + body_part(FACE)); + else if (ublindf->otyp == BLINDFOLD) { + if (otmp->otyp == LENSES) + already_wearing2("lenses", "a blindfold"); + else + already_wearing("a blindfold"); + } else if (ublindf->otyp == LENSES) { + if (otmp->otyp == BLINDFOLD) + already_wearing2("a blindfold", "some lenses"); + else + already_wearing("some lenses"); + } else + already_wearing(something); /* ??? */ + return(0); + } + if (otmp->otyp != BLINDFOLD && otmp->otyp != TOWEL && otmp->otyp != LENSES) { + You_cant("wear that!"); + return(0); + } + if (otmp->oartifact && !touch_artifact(otmp, &youmonst)) + return 1; + Blindf_on(otmp); + return(1); + } + if (is_worn(otmp)) + prinv((char *)0, otmp, 0L); + return(1); +} + +#endif /* OVLB */ + +#ifdef OVL0 + +void +find_ac() +{ + int uac = mons[u.umonnum].ac; + + if(uarm) uac -= ARM_BONUS(uarm); + if(uarmc) uac -= ARM_BONUS(uarmc); + if(uarmh) uac -= ARM_BONUS(uarmh); + if(uarmf) uac -= ARM_BONUS(uarmf); + if(uarms) uac -= ARM_BONUS(uarms); + if(uarmg) uac -= ARM_BONUS(uarmg); +#ifdef TOURIST + if(uarmu) uac -= ARM_BONUS(uarmu); +#endif + if(uleft && uleft->otyp == RIN_PROTECTION) uac -= uleft->spe; + if(uright && uright->otyp == RIN_PROTECTION) uac -= uright->spe; + if (HProtection & INTRINSIC) uac -= u.ublessed; + uac -= u.uspellprot; + if (uac < -128) uac = -128; /* u.uac is an schar */ + if(uac != u.uac){ + u.uac = uac; + flags.botl = 1; + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +glibr() +{ + register struct obj *otmp; + int xfl = 0; + boolean leftfall, rightfall; + const char *otherwep = 0; + + leftfall = (uleft && !uleft->cursed && + (!uwep || !welded(uwep) || !bimanual(uwep))); + rightfall = (uright && !uright->cursed && (!welded(uwep))); + if (!uarmg && (leftfall || rightfall) && !nolimbs(youmonst.data)) { + /* changed so cursed rings don't fall off, GAN 10/30/86 */ + Your("%s off your %s.", + (leftfall && rightfall) ? "rings slip" : "ring slips", + (leftfall && rightfall) ? makeplural(body_part(FINGER)) : + body_part(FINGER)); + xfl++; + if (leftfall) { + otmp = uleft; + Ring_off(uleft); + dropx(otmp); + } + if (rightfall) { + otmp = uright; + Ring_off(uright); + dropx(otmp); + } + } + + otmp = uswapwep; + if (u.twoweap && otmp) { + otherwep = is_sword(otmp) ? c_sword : + makesingular(oclass_names[(int)otmp->oclass]); + Your("%s %sslips from your %s.", + otherwep, + xfl ? "also " : "", + makeplural(body_part(HAND))); + setuswapwep((struct obj *)0); + xfl++; + if (otmp->otyp != LOADSTONE || !otmp->cursed) + dropx(otmp); + } + otmp = uwep; + if (otmp && !welded(otmp)) { + const char *thiswep; + + /* nice wording if both weapons are the same type */ + thiswep = is_sword(otmp) ? c_sword : + makesingular(oclass_names[(int)otmp->oclass]); + if (otherwep && strcmp(thiswep, otherwep)) otherwep = 0; + + /* changed so cursed weapons don't fall, GAN 10/30/86 */ + Your("%s%s %sslips from your %s.", + otherwep ? "other " : "", thiswep, + xfl ? "also " : "", + makeplural(body_part(HAND))); + setuwep((struct obj *)0); + if (otmp->otyp != LOADSTONE || !otmp->cursed) + dropx(otmp); + } +} + +struct obj * +some_armor(victim) +struct monst *victim; +{ + register struct obj *otmph, *otmp; + + otmph = (victim == &youmonst) ? uarmc : which_armor(victim, W_ARMC); + if (!otmph) + otmph = (victim == &youmonst) ? uarm : which_armor(victim, W_ARM); +#ifdef TOURIST + if (!otmph) + otmph = (victim == &youmonst) ? uarmu : which_armor(victim, W_ARMU); +#endif + + otmp = (victim == &youmonst) ? uarmh : which_armor(victim, W_ARMH); + if(otmp && (!otmph || !rn2(4))) otmph = otmp; + otmp = (victim == &youmonst) ? uarmg : which_armor(victim, W_ARMG); + if(otmp && (!otmph || !rn2(4))) otmph = otmp; + otmp = (victim == &youmonst) ? uarmf : which_armor(victim, W_ARMF); + if(otmp && (!otmph || !rn2(4))) otmph = otmp; + otmp = (victim == &youmonst) ? uarms : which_armor(victim, W_ARMS); + if(otmp && (!otmph || !rn2(4))) otmph = otmp; + return(otmph); +} + +/* erode some arbitrary armor worn by the victim */ +void +erode_armor(victim, acid_dmg) +struct monst *victim; +boolean acid_dmg; +{ + struct obj *otmph = some_armor(victim); + + if (otmph && (otmph != uarmf)) { + erode_obj(otmph, acid_dmg, FALSE); + if (carried(otmph)) update_inventory(); + } +} + +/* used for praying to check and fix levitation trouble */ +struct obj * +stuck_ring(ring, otyp) +struct obj *ring; +int otyp; +{ + if (ring != uleft && ring != uright) { + impossible("stuck_ring: neither left nor right?"); + return (struct obj *)0; + } + + if (ring && ring->otyp == otyp) { + /* reasons ring can't be removed match those checked by select_off(); + limbless case has extra checks because ordinarily it's temporary */ + if (nolimbs(youmonst.data) && + uamul && uamul->otyp == AMULET_OF_UNCHANGING && uamul->cursed) + return uamul; + if (welded(uwep) && (ring == uright || bimanual(uwep))) return uwep; + if (uarmg && uarmg->cursed) return uarmg; + if (ring->cursed) return ring; + } + /* either no ring or not right type or nothing prevents its removal */ + return (struct obj *)0; +} + +/* also for praying; find worn item that confers "Unchanging" attribute */ +struct obj * +unchanger() +{ + if (uamul && uamul->otyp == AMULET_OF_UNCHANGING) return uamul; + return 0; +} + +/* occupation callback for 'A' */ +STATIC_PTR +int +select_off(otmp) +register struct obj *otmp; +{ + struct obj *why; + char buf[BUFSZ]; + + if (!otmp) return 0; + *buf = '\0'; /* lint suppresion */ + + /* special ring checks */ + if (otmp == uright || otmp == uleft) { + if (nolimbs(youmonst.data)) { + pline_The("ring is stuck."); + return 0; + } + why = 0; /* the item which prevents ring removal */ + if (welded(uwep) && (otmp == uright || bimanual(uwep))) { + Sprintf(buf, "free a weapon %s", body_part(HAND)); + why = uwep; + } else if (uarmg && uarmg->cursed) { + Sprintf(buf, "take off your %s", c_gloves); + why = uarmg; + } + if (why) { + You("cannot %s to remove the ring.", buf); + why->bknown = TRUE; + return 0; + } + } + /* special glove checks */ + if (otmp == uarmg) { + if (welded(uwep)) { + You("are unable to take off your %s while wielding that %s.", + c_gloves, is_sword(uwep) ? c_sword : c_weapon); + uwep->bknown = TRUE; + return 0; + } else if (Glib) { + You_cant("take off the slippery %s with your slippery %s.", + c_gloves, makeplural(body_part(FINGER))); + return 0; + } + } + /* special boot checks */ + if (otmp == uarmf) { + if (u.utrap && u.utraptype == TT_BEARTRAP) { + pline_The("bear trap prevents you from pulling your %s out.", + body_part(FOOT)); + return 0; + } else if (u.utrap && u.utraptype == TT_INFLOOR) { + You("are stuck in the %s, and cannot pull your %s out.", + surface(u.ux, u.uy), makeplural(body_part(FOOT))); + return 0; + } + } + /* special suit and shirt checks */ + if (otmp == uarm +#ifdef TOURIST + || otmp == uarmu +#endif + ) { + why = 0; /* the item which prevents disrobing */ + if (uarmc && uarmc->cursed) { + Sprintf(buf, "remove your %s", cloak_simple_name(uarmc)); + why = uarmc; +#ifdef TOURIST + } else if (otmp == uarmu && uarm && uarm->cursed) { + Sprintf(buf, "remove your %s", c_suit); + why = uarm; +#endif + } else if (welded(uwep) && bimanual(uwep)) { + Sprintf(buf, "release your %s", + is_sword(uwep) ? c_sword : + (uwep->otyp == BATTLE_AXE) ? c_axe : c_weapon); + why = uwep; + } + if (why) { + You("cannot %s to take off %s.", buf, the(xname(otmp))); + why->bknown = TRUE; + return 0; + } + } + /* basic curse check */ + if (otmp == uquiver || (otmp == uswapwep && !u.twoweap)) { + ; /* some items can be removed even when cursed */ + } else { + /* otherwise, this is fundamental */ + if (cursed(otmp)) return 0; + } + + if(otmp == uarm) takeoff_mask |= WORN_ARMOR; + else if(otmp == uarmc) takeoff_mask |= WORN_CLOAK; + else if(otmp == uarmf) takeoff_mask |= WORN_BOOTS; + else if(otmp == uarmg) takeoff_mask |= WORN_GLOVES; + else if(otmp == uarmh) takeoff_mask |= WORN_HELMET; + else if(otmp == uarms) takeoff_mask |= WORN_SHIELD; +#ifdef TOURIST + else if(otmp == uarmu) takeoff_mask |= WORN_SHIRT; +#endif + else if(otmp == uleft) takeoff_mask |= LEFT_RING; + else if(otmp == uright) takeoff_mask |= RIGHT_RING; + else if(otmp == uamul) takeoff_mask |= WORN_AMUL; + else if(otmp == ublindf) takeoff_mask |= WORN_BLINDF; + else if(otmp == uwep) takeoff_mask |= W_WEP; + else if(otmp == uswapwep) takeoff_mask |= W_SWAPWEP; + else if(otmp == uquiver) takeoff_mask |= W_QUIVER; + + else impossible("select_off: %s???", doname(otmp)); + + return(0); +} + +STATIC_OVL struct obj * +do_takeoff() +{ + register struct obj *otmp = (struct obj *)0; + + if (taking_off == W_WEP) { + if(!cursed(uwep)) { + setuwep((struct obj *) 0); + You("are empty %s.", body_part(HANDED)); + u.twoweap = FALSE; + } + } else if (taking_off == W_SWAPWEP) { + setuswapwep((struct obj *) 0); + You("no longer have a second weapon readied."); + u.twoweap = FALSE; + } else if (taking_off == W_QUIVER) { + setuqwep((struct obj *) 0); + You("no longer have ammunition readied."); + } else if (taking_off == WORN_ARMOR) { + otmp = uarm; + if(!cursed(otmp)) (void) Armor_off(); + } else if (taking_off == WORN_CLOAK) { + otmp = uarmc; + if(!cursed(otmp)) (void) Cloak_off(); + } else if (taking_off == WORN_BOOTS) { + otmp = uarmf; + if(!cursed(otmp)) (void) Boots_off(); + } else if (taking_off == WORN_GLOVES) { + otmp = uarmg; + if(!cursed(otmp)) (void) Gloves_off(); + } else if (taking_off == WORN_HELMET) { + otmp = uarmh; + if(!cursed(otmp)) (void) Helmet_off(); + } else if (taking_off == WORN_SHIELD) { + otmp = uarms; + if(!cursed(otmp)) (void) Shield_off(); +#ifdef TOURIST + } else if (taking_off == WORN_SHIRT) { + otmp = uarmu; + if (!cursed(otmp)) (void) Shirt_off(); +#endif + } else if (taking_off == WORN_AMUL) { + otmp = uamul; + if(!cursed(otmp)) Amulet_off(); + } else if (taking_off == LEFT_RING) { + otmp = uleft; + if(!cursed(otmp)) Ring_off(uleft); + } else if (taking_off == RIGHT_RING) { + otmp = uright; + if(!cursed(otmp)) Ring_off(uright); + } else if (taking_off == WORN_BLINDF) { + if (!cursed(ublindf)) Blindf_off(ublindf); + } else impossible("do_takeoff: taking off %lx", taking_off); + + return(otmp); +} + +static const char *disrobing = ""; + +STATIC_PTR +int +take_off() +{ + register int i; + register struct obj *otmp; + + if (taking_off) { + if (todelay > 0) { + todelay--; + return(1); /* still busy */ + } else { + if ((otmp = do_takeoff())) off_msg(otmp); + } + takeoff_mask &= ~taking_off; + taking_off = 0L; + } + + for(i = 0; takeoff_order[i]; i++) + if(takeoff_mask & takeoff_order[i]) { + taking_off = takeoff_order[i]; + break; + } + + otmp = (struct obj *) 0; + todelay = 0; + + if (taking_off == 0L) { + You("finish %s.", disrobing); + return 0; + } else if (taking_off == W_WEP) { + todelay = 1; + } else if (taking_off == W_SWAPWEP) { + todelay = 1; + } else if (taking_off == W_QUIVER) { + todelay = 1; + } else if (taking_off == WORN_ARMOR) { + otmp = uarm; + /* If a cloak is being worn, add the time to take it off and put + * it back on again. Kludge alert! since that time is 0 for all + * known cloaks, add 1 so that it actually matters... + */ + if (uarmc) todelay += 2 * objects[uarmc->otyp].oc_delay + 1; + } else if (taking_off == WORN_CLOAK) { + otmp = uarmc; + } else if (taking_off == WORN_BOOTS) { + otmp = uarmf; + } else if (taking_off == WORN_GLOVES) { + otmp = uarmg; + } else if (taking_off == WORN_HELMET) { + otmp = uarmh; + } else if (taking_off == WORN_SHIELD) { + otmp = uarms; +#ifdef TOURIST + } else if (taking_off == WORN_SHIRT) { + otmp = uarmu; + /* add the time to take off and put back on armor and/or cloak */ + if (uarm) todelay += 2 * objects[uarm->otyp].oc_delay; + if (uarmc) todelay += 2 * objects[uarmc->otyp].oc_delay + 1; +#endif + } else if (taking_off == WORN_AMUL) { + todelay = 1; + } else if (taking_off == LEFT_RING) { + todelay = 1; + } else if (taking_off == RIGHT_RING) { + todelay = 1; + } else if (taking_off == WORN_BLINDF) { + todelay = 2; + } else { + impossible("take_off: taking off %lx", taking_off); + return 0; /* force done */ + } + + if (otmp) todelay += objects[otmp->otyp].oc_delay; + + /* Since setting the occupation now starts the counter next move, that + * would always produce a delay 1 too big per item unless we subtract + * 1 here to account for it. + */ + if (todelay > 0) todelay--; + + set_occupation(take_off, disrobing, 0); + return(1); /* get busy */ +} + +/* clear saved context to avoid inappropriate resumption of interrupted 'A' */ +void +reset_remarm() +{ + taking_off = takeoff_mask = 0L; + disrobing = nul; +} + +/* the 'A' command -- remove multiple worn items */ +int +doddoremarm() +{ + int result = 0; + + if (taking_off || takeoff_mask) { + You("continue %s.", disrobing); + set_occupation(take_off, disrobing, 0); + return 0; + } else if (!uwep && !uswapwep && !uquiver && !uamul && !ublindf && + !uleft && !uright && !wearing_armor()) { + You("are not wearing anything."); + return 0; + } + + add_valid_menu_class(0); /* reset */ + if (flags.menu_style != MENU_TRADITIONAL || + (result = ggetobj("take off", select_off, 0, FALSE, (unsigned *)0)) < -1) + result = menu_remarm(result); + + if (takeoff_mask) { + /* default activity for armor and/or accessories, + possibly combined with weapons */ + disrobing = "disrobing"; + /* specific activity when handling weapons only */ + if (!(takeoff_mask & ~(W_WEP|W_SWAPWEP|W_QUIVER))) + disrobing = "disarming"; + (void) take_off(); + } + /* The time to perform the command is already completely accounted for + * in take_off(); if we return 1, that would add an extra turn to each + * disrobe. + */ + return 0; +} + +STATIC_OVL int +menu_remarm(retry) +int retry; +{ + int n, i = 0; + menu_item *pick_list; + boolean all_worn_categories = TRUE; + + if (retry) { + all_worn_categories = (retry == -2); + } else if (flags.menu_style == MENU_FULL) { + all_worn_categories = FALSE; + n = query_category("What type of things do you want to take off?", + invent, WORN_TYPES|ALL_TYPES, &pick_list, PICK_ANY); + if (!n) return 0; + for (i = 0; i < n; i++) { + if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) + all_worn_categories = TRUE; + else + add_valid_menu_class(pick_list[i].item.a_int); + } + free((genericptr_t) pick_list); + } else if (flags.menu_style == MENU_COMBINATION) { + all_worn_categories = FALSE; + if (ggetobj("take off", select_off, 0, TRUE, (unsigned *)0) == -2) + all_worn_categories = TRUE; + } + + n = query_objlist("What do you want to take off?", invent, + SIGNAL_NOMENU|USE_INVLET|INVORDER_SORT, + &pick_list, PICK_ANY, + all_worn_categories ? is_worn : is_worn_by_type); + if (n > 0) { + for (i = 0; i < n; i++) + (void) select_off(pick_list[i].item.a_obj); + free((genericptr_t) pick_list); + } else if (n < 0 && flags.menu_style != MENU_COMBINATION) { + There("is nothing else you can remove or unwield."); + } + return 0; +} + +/* hit by destroy armor scroll/black dragon breath/monster spell */ +int +destroy_arm(atmp) +register struct obj *atmp; +{ + register struct obj *otmp; +#define DESTROY_ARM(o) ((otmp = (o)) != 0 && \ + (!atmp || atmp == otmp) && \ + (!obj_resists(otmp, 0, 90))) + + if (DESTROY_ARM(uarmc)) { + if (donning(otmp)) cancel_don(); + Your("%s crumbles and turns to dust!", + cloak_simple_name(uarmc)); + (void) Cloak_off(); + useup(otmp); + } else if (DESTROY_ARM(uarm)) { + if (donning(otmp)) cancel_don(); + Your("armor turns to dust and falls to the %s!", + surface(u.ux,u.uy)); + (void) Armor_gone(); + useup(otmp); +#ifdef TOURIST + } else if (DESTROY_ARM(uarmu)) { + if (donning(otmp)) cancel_don(); + Your("shirt crumbles into tiny threads and falls apart!"); + (void) Shirt_off(); + useup(otmp); +#endif + } else if (DESTROY_ARM(uarmh)) { + if (donning(otmp)) cancel_don(); + Your("helmet turns to dust and is blown away!"); + (void) Helmet_off(); + useup(otmp); + } else if (DESTROY_ARM(uarmg)) { + if (donning(otmp)) cancel_don(); + Your("gloves vanish!"); + (void) Gloves_off(); + useup(otmp); + selftouch("You"); + } else if (DESTROY_ARM(uarmf)) { + if (donning(otmp)) cancel_don(); + Your("boots disintegrate!"); + (void) Boots_off(); + useup(otmp); + } else if (DESTROY_ARM(uarms)) { + if (donning(otmp)) cancel_don(); + Your("shield crumbles away!"); + (void) Shield_off(); + useup(otmp); + } else { + return 0; /* could not destroy anything */ + } + +#undef DESTROY_ARM + stop_occupation(); + return(1); +} + +void +adj_abon(otmp, delta) +register struct obj *otmp; +register schar delta; +{ + if (uarmg && uarmg == otmp && otmp->otyp == GAUNTLETS_OF_DEXTERITY) { + if (delta) { + makeknown(uarmg->otyp); + ABON(A_DEX) += (delta); + } + flags.botl = 1; + } + if (uarmh && uarmh == otmp && otmp->otyp == HELM_OF_BRILLIANCE) { + if (delta) { + makeknown(uarmh->otyp); + ABON(A_INT) += (delta); + ABON(A_WIS) += (delta); + } + flags.botl = 1; + } +} + +#endif /* OVLB */ + +/*do_wear.c*/ diff --git a/src/dog.c b/src/dog.c new file mode 100644 index 0000000..1081665 --- /dev/null +++ b/src/dog.c @@ -0,0 +1,939 @@ +/* SCCS Id: @(#)dog.c 3.4 2002/09/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "edog.h" + +#ifdef OVLB + +STATIC_DCL int NDECL(pet_type); + +void +initedog(mtmp) +register struct monst *mtmp; +{ + mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5; + mtmp->mpeaceful = 1; + mtmp->mavenge = 0; + set_malign(mtmp); /* recalc alignment now that it's tamed */ + mtmp->mleashed = 0; + mtmp->meating = 0; + EDOG(mtmp)->droptime = 0; + EDOG(mtmp)->dropdist = 10000; + EDOG(mtmp)->apport = 10; + EDOG(mtmp)->whistletime = 0; + EDOG(mtmp)->hungrytime = 1000 + monstermoves; + EDOG(mtmp)->ogoal.x = -1; /* force error if used before set */ + EDOG(mtmp)->ogoal.y = -1; + EDOG(mtmp)->abuse = 0; + EDOG(mtmp)->revivals = 0; + EDOG(mtmp)->mhpmax_penalty = 0; + EDOG(mtmp)->killed_by_u = 0; +} + +STATIC_OVL int +pet_type() +{ + if (urole.petnum != NON_PM) + return (urole.petnum); + else if (preferred_pet == 'c') + return (PM_KITTEN); + else if (preferred_pet == 'd') + return (PM_LITTLE_DOG); + else + return (rn2(2) ? PM_KITTEN : PM_LITTLE_DOG); +} + +struct monst * +make_familiar(otmp,x,y,quietly) +register struct obj *otmp; +xchar x, y; +boolean quietly; +{ + struct permonst *pm; + struct monst *mtmp = 0; + int chance, trycnt = 100; + + do { + if (otmp) { /* figurine; otherwise spell */ + int mndx = otmp->corpsenm; + pm = &mons[mndx]; + /* activating a figurine provides one way to exceed the + maximum number of the target critter created--unless + it has a special limit (erinys, Nazgul) */ + if ((mvitals[mndx].mvflags & G_EXTINCT) && + mbirth_limit(mndx) != MAXMONNO) { + if (!quietly) + /* have just been given "You + the figurine and it transforms." message */ + pline("... into a pile of dust."); + break; /* mtmp is null */ + } + } else if (!rn2(3)) { + pm = &mons[pet_type()]; + } else { + pm = rndmonst(); + if (!pm) { + if (!quietly) + There("seems to be nothing available for a familiar."); + break; + } + } + + mtmp = makemon(pm, x, y, MM_EDOG|MM_IGNOREWATER); + if (otmp && !mtmp) { /* monster was genocided or square occupied */ + if (!quietly) + pline_The("figurine writhes and then shatters into pieces!"); + break; + } + } while (!mtmp && --trycnt > 0); + + if (!mtmp) return (struct monst *)0; + + if (is_pool(mtmp->mx, mtmp->my) && minliquid(mtmp)) + return (struct monst *)0; + + initedog(mtmp); + mtmp->msleeping = 0; + if (otmp) { /* figurine; resulting monster might not become a pet */ + chance = rn2(10); /* 0==tame, 1==peaceful, 2==hostile */ + if (chance > 2) chance = otmp->blessed ? 0 : !otmp->cursed ? 1 : 2; + /* 0,1,2: b=80%,10,10; nc=10%,80,10; c=10%,10,80 */ + if (chance > 0) { + mtmp->mtame = 0; /* not tame after all */ + if (chance == 2) { /* hostile (cursed figurine) */ + if (!quietly) + You("get a bad feeling about this."); + mtmp->mpeaceful = 0; + set_malign(mtmp); + } + } + /* if figurine has been named, give same name to the monster */ + if (otmp->onamelth) + mtmp = christen_monst(mtmp, ONAME(otmp)); + } + set_malign(mtmp); /* more alignment changes */ + newsym(mtmp->mx, mtmp->my); + + /* must wield weapon immediately since pets will otherwise drop it */ + if (mtmp->mtame && attacktype(mtmp->data, AT_WEAP)) { + mtmp->weapon_check = NEED_HTH_WEAPON; + (void) mon_wield_item(mtmp); + } + return mtmp; +} + +struct monst * +makedog() +{ + register struct monst *mtmp; +#ifdef STEED + register struct obj *otmp; +#endif + const char *petname; + int pettype; + static int petname_used = 0; + + if (preferred_pet == 'n') return((struct monst *) 0); + + pettype = pet_type(); + if (pettype == PM_LITTLE_DOG) + petname = dogname; + else if (pettype == PM_PONY) + petname = horsename; + else + petname = catname; + + /* default pet names */ + if (!*petname && pettype == PM_LITTLE_DOG) { + /* All of these names were for dogs. */ + if(Role_if(PM_CAVEMAN)) petname = "Slasher"; /* The Warrior */ + if(Role_if(PM_SAMURAI)) petname = "Hachi"; /* Shibuya Station */ + if(Role_if(PM_BARBARIAN)) petname = "Idefix"; /* Obelix */ + if(Role_if(PM_RANGER)) petname = "Sirius"; /* Orion's dog */ + } + + mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG); + + if(!mtmp) return((struct monst *) 0); /* pets were genocided */ + +#ifdef STEED + /* Horses already wear a saddle */ + if (pettype == PM_PONY && !!(otmp = mksobj(SADDLE, TRUE, FALSE))) { + if (mpickobj(mtmp, otmp)) + panic("merged saddle?"); + mtmp->misc_worn_check |= W_SADDLE; + otmp->dknown = otmp->bknown = otmp->rknown = 1; + otmp->owornmask = W_SADDLE; + otmp->leashmon = mtmp->m_id; + update_mon_intrinsics(mtmp, otmp, TRUE, TRUE); + } +#endif + + if (!petname_used++ && *petname) + mtmp = christen_monst(mtmp, petname); + + initedog(mtmp); + return(mtmp); +} + +/* record `last move time' for all monsters prior to level save so that + mon_arrive() can catch up for lost time when they're restored later */ +void +update_mlstmv() +{ + struct monst *mon; + + /* monst->mlstmv used to be updated every time `monst' actually moved, + but that is no longer the case so we just do a blanket assignment */ + for (mon = fmon; mon; mon = mon->nmon) + if (!DEADMONSTER(mon)) mon->mlstmv = monstermoves; +} + +void +losedogs() +{ + register struct monst *mtmp, *mtmp0 = 0, *mtmp2; + + while ((mtmp = mydogs) != 0) { + mydogs = mtmp->nmon; + mon_arrive(mtmp, TRUE); + } + + for(mtmp = migrating_mons; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) { + if(mtmp == migrating_mons) + migrating_mons = mtmp->nmon; + else + mtmp0->nmon = mtmp->nmon; + mon_arrive(mtmp, FALSE); + } else + mtmp0 = mtmp; + } +} + +/* called from resurrect() in addition to losedogs() */ +void +mon_arrive(mtmp, with_you) +struct monst *mtmp; +boolean with_you; +{ + struct trap *t; + xchar xlocale, ylocale, xyloc, xyflags, wander; + int num_segs; + + mtmp->nmon = fmon; + fmon = mtmp; + if (mtmp->isshk) + set_residency(mtmp, FALSE); + + num_segs = mtmp->wormno; + /* baby long worms have no tail so don't use is_longworm() */ + if ((mtmp->data == &mons[PM_LONG_WORM]) && +#ifdef DCC30_BUG + (mtmp->wormno = get_wormno(), mtmp->wormno != 0)) +#else + (mtmp->wormno = get_wormno()) != 0) +#endif + { + initworm(mtmp, num_segs); + /* tail segs are not yet initialized or displayed */ + } else mtmp->wormno = 0; + + /* some monsters might need to do something special upon arrival + _after_ the current level has been fully set up; see dochug() */ + mtmp->mstrategy |= STRAT_ARRIVE; + + /* make sure mnexto(rloc_to(set_apparxy())) doesn't use stale data */ + mtmp->mux = u.ux, mtmp->muy = u.uy; + xyloc = mtmp->mtrack[0].x; + xyflags = mtmp->mtrack[0].y; + xlocale = mtmp->mtrack[1].x; + ylocale = mtmp->mtrack[1].y; + mtmp->mtrack[0].x = mtmp->mtrack[0].y = 0; + mtmp->mtrack[1].x = mtmp->mtrack[1].y = 0; + +#ifdef STEED + if (mtmp == u.usteed) + return; /* don't place steed on the map */ +#endif + if (with_you) { + /* When a monster accompanies you, sometimes it will arrive + at your intended destination and you'll end up next to + that spot. This code doesn't control the final outcome; + goto_level(do.c) decides who ends up at your target spot + when there is a monster there too. */ + if (!MON_AT(u.ux, u.uy) && + !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2)) + rloc_to(mtmp, u.ux, u.uy); + else + mnexto(mtmp); + return; + } + /* + * The monster arrived on this level independently of the player. + * Its coordinate fields were overloaded for use as flags that + * specify its final destination. + */ + + if (mtmp->mlstmv < monstermoves - 1L) { + /* heal monster for time spent in limbo */ + long nmv = monstermoves - 1L - mtmp->mlstmv; + + mon_catchup_elapsed_time(mtmp, nmv); + mtmp->mlstmv = monstermoves - 1L; + + /* let monster move a bit on new level (see placement code below) */ + wander = (xchar) min(nmv, 8); + } else + wander = 0; + + switch (xyloc) { + case MIGR_APPROX_XY: /* {x,y}locale set above */ + break; + case MIGR_EXACT_XY: wander = 0; + break; + case MIGR_NEAR_PLAYER: xlocale = u.ux, ylocale = u.uy; + break; + case MIGR_STAIRS_UP: xlocale = xupstair, ylocale = yupstair; + break; + case MIGR_STAIRS_DOWN: xlocale = xdnstair, ylocale = ydnstair; + break; + case MIGR_LADDER_UP: xlocale = xupladder, ylocale = yupladder; + break; + case MIGR_LADDER_DOWN: xlocale = xdnladder, ylocale = ydnladder; + break; + case MIGR_SSTAIRS: xlocale = sstairs.sx, ylocale = sstairs.sy; + break; + case MIGR_PORTAL: + if (In_endgame(&u.uz)) { + /* there is no arrival portal for endgame levels */ + /* BUG[?]: for simplicity, this code relies on the fact + that we know that the current endgame levels always + build upwards and never have any exclusion subregion + inside their TELEPORT_REGION settings. */ + xlocale = rn1(updest.hx - updest.lx + 1, updest.lx); + ylocale = rn1(updest.hy - updest.ly + 1, updest.ly); + break; + } + /* find the arrival portal */ + for (t = ftrap; t; t = t->ntrap) + if (t->ttyp == MAGIC_PORTAL) break; + if (t) { + xlocale = t->tx, ylocale = t->ty; + break; + } else { + impossible("mon_arrive: no corresponding portal?"); + } /*FALLTHRU*/ + default: + case MIGR_RANDOM: xlocale = ylocale = 0; + break; + } + + if (xlocale && wander) { + /* monster moved a bit; pick a nearby location */ + /* mnearto() deals w/stone, et al */ + char *r = in_rooms(xlocale, ylocale, 0); + if (r && *r) { + coord c; + /* somexy() handles irregular rooms */ + if (somexy(&rooms[*r - ROOMOFFSET], &c)) + xlocale = c.x, ylocale = c.y; + else + xlocale = ylocale = 0; + } else { /* not in a room */ + int i, j; + i = max(1, xlocale - wander); + j = min(COLNO-1, xlocale + wander); + xlocale = rn1(j - i, i); + i = max(0, ylocale - wander); + j = min(ROWNO-1, ylocale + wander); + ylocale = rn1(j - i, i); + } + } /* moved a bit */ + + mtmp->mx = 0; /*(already is 0)*/ + mtmp->my = xyflags; + if (xlocale) + (void) mnearto(mtmp, xlocale, ylocale, FALSE); + else { + if (!rloc(mtmp,TRUE)) { + /* + * Failed to place migrating monster, + * probably because the level is full. + * Dump the monster's cargo and leave the monster dead. + */ + struct obj *obj, *corpse; + while ((obj = mtmp->minvent) != 0) { + obj_extract_self(obj); + obj_no_longer_held(obj); + if (obj->owornmask & W_WEP) + setmnotwielded(mtmp,obj); + obj->owornmask = 0L; + if (xlocale && ylocale) + place_object(obj, xlocale, ylocale); + else { + rloco(obj); + get_obj_location(obj, &xlocale, &ylocale, 0); + } + } + corpse = mkcorpstat(CORPSE, (struct monst *)0, mtmp->data, + xlocale, ylocale, FALSE); +#ifndef GOLDOBJ + if (mtmp->mgold) { + if (xlocale == 0 && ylocale == 0 && corpse) { + (void) get_obj_location(corpse, &xlocale, &ylocale, 0); + (void) mkgold(mtmp->mgold, xlocale, ylocale); + } + mtmp->mgold = 0L; + } +#endif + mongone(mtmp); + } + } +} + +/* heal monster for time spent elsewhere */ +void +mon_catchup_elapsed_time(mtmp, nmv) +struct monst *mtmp; +long nmv; /* number of moves */ +{ + int imv = 0; /* avoid zillions of casts and lint warnings */ + +#if defined(DEBUG) || defined(BETA) + if (nmv < 0L) { /* crash likely... */ + panic("catchup from future time?"); + /*NOTREACHED*/ + return; + } else if (nmv == 0L) { /* safe, but should'nt happen */ + impossible("catchup from now?"); + } else +#endif + if (nmv >= LARGEST_INT) /* paranoia */ + imv = LARGEST_INT - 1; + else + imv = (int)nmv; + + /* might stop being afraid, blind or frozen */ + /* set to 1 and allow final decrement in movemon() */ + if (mtmp->mblinded) { + if (imv >= (int) mtmp->mblinded) mtmp->mblinded = 1; + else mtmp->mblinded -= imv; + } + if (mtmp->mfrozen) { + if (imv >= (int) mtmp->mfrozen) mtmp->mfrozen = 1; + else mtmp->mfrozen -= imv; + } + if (mtmp->mfleetim) { + if (imv >= (int) mtmp->mfleetim) mtmp->mfleetim = 1; + else mtmp->mfleetim -= imv; + } + + /* might recover from temporary trouble */ + if (mtmp->mtrapped && rn2(imv + 1) > 40/2) mtmp->mtrapped = 0; + if (mtmp->mconf && rn2(imv + 1) > 50/2) mtmp->mconf = 0; + if (mtmp->mstun && rn2(imv + 1) > 10/2) mtmp->mstun = 0; + + /* might finish eating or be able to use special ability again */ + if (imv > mtmp->meating) mtmp->meating = 0; + else mtmp->meating -= imv; + if (imv > mtmp->mspec_used) mtmp->mspec_used = 0; + else mtmp->mspec_used -= imv; + + /* reduce tameness for every 150 moves you are separated */ + if (mtmp->mtame) { + int wilder = (imv + 75) / 150; + if (mtmp->mtame > wilder) mtmp->mtame -= wilder; /* less tame */ + else if (mtmp->mtame > rn2(wilder)) mtmp->mtame = 0; /* untame */ + else mtmp->mtame = mtmp->mpeaceful = 0; /* hostile! */ + } + /* check to see if it would have died as a pet; if so, go wild instead + * of dying the next time we call dog_move() + */ + if (mtmp->mtame && !mtmp->isminion && + (carnivorous(mtmp->data) || herbivorous(mtmp->data))) { + struct edog *edog = EDOG(mtmp); + + if ((monstermoves > edog->hungrytime + 500 && mtmp->mhp < 3) || + (monstermoves > edog->hungrytime + 750)) + mtmp->mtame = mtmp->mpeaceful = 0; + } + + if (!mtmp->mtame && mtmp->mleashed) { + /* leashed monsters should always be with hero, consequently + never losing any time to be accounted for later */ + impossible("catching up for leashed monster?"); + m_unleash(mtmp, FALSE); + } + + /* recover lost hit points */ + if (!regenerates(mtmp->data)) imv /= 20; + if (mtmp->mhp + imv >= mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + else mtmp->mhp += imv; +} + +#endif /* OVLB */ +#ifdef OVL2 + +/* called when you move to another level */ +void +keepdogs(pets_only) +boolean pets_only; /* true for ascension or final escape */ +{ + register struct monst *mtmp, *mtmp2; + register struct obj *obj; + int num_segs; + boolean stay_behind; + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp)) continue; + if (pets_only && !mtmp->mtame) continue; + if (((monnear(mtmp, u.ux, u.uy) && levl_follower(mtmp)) || +#ifdef STEED + (mtmp == u.usteed) || +#endif + /* the wiz will level t-port from anywhere to chase + the amulet; if you don't have it, will chase you + only if in range. -3. */ + (u.uhave.amulet && mtmp->iswiz)) + && ((!mtmp->msleeping && mtmp->mcanmove) +#ifdef STEED + /* eg if level teleport or new trap, steed has no control + to avoid following */ + || (mtmp == u.usteed) +#endif + ) + /* monster won't follow if it hasn't noticed you yet */ + && !(mtmp->mstrategy & STRAT_WAITFORU)) { + stay_behind = FALSE; + if (mtmp->mtame && mtmp->meating) { + if (canseemon(mtmp)) + pline("%s is still eating.", Monnam(mtmp)); + stay_behind = TRUE; + } else if (mon_has_amulet(mtmp)) { + if (canseemon(mtmp)) + pline("%s seems very disoriented for a moment.", + Monnam(mtmp)); + stay_behind = TRUE; + } else if (mtmp->mtame && mtmp->mtrapped) { + if (canseemon(mtmp)) + pline("%s is still trapped.", Monnam(mtmp)); + stay_behind = TRUE; + } +#ifdef STEED + if (mtmp == u.usteed) stay_behind = FALSE; +#endif + if (stay_behind) { + if (mtmp->mleashed) { + pline("%s leash suddenly comes loose.", + humanoid(mtmp->data) + ? (mtmp->female ? "Her" : "His") + : "Its"); + m_unleash(mtmp, FALSE); + } + continue; + } + if (mtmp->isshk) + set_residency(mtmp, TRUE); + + if (mtmp->wormno) { + register int cnt; + /* NOTE: worm is truncated to # segs = max wormno size */ + cnt = count_wsegs(mtmp); + num_segs = min(cnt, MAX_NUM_WORMS - 1); + wormgone(mtmp); + } else num_segs = 0; + + /* set minvent's obj->no_charge to 0 */ + for(obj = mtmp->minvent; obj; obj = obj->nobj) { + if (Has_contents(obj)) + picked_container(obj); /* does the right thing */ + obj->no_charge = 0; + } + + relmon(mtmp); + newsym(mtmp->mx,mtmp->my); + mtmp->mx = mtmp->my = 0; /* avoid mnexto()/MON_AT() problem */ + mtmp->wormno = num_segs; + mtmp->mlstmv = monstermoves; + mtmp->nmon = mydogs; + mydogs = mtmp; + } else if (mtmp->iswiz) { + /* we want to be able to find him when his next resurrection + chance comes up, but have him resume his present location + if player returns to this level before that time */ + migrate_to_level(mtmp, ledger_no(&u.uz), + MIGR_EXACT_XY, (coord *)0); + } else if (mtmp->mleashed) { + /* this can happen if your quest leader ejects you from the + "home" level while a leashed pet isn't next to you */ + pline("%s leash goes slack.", s_suffix(Monnam(mtmp))); + m_unleash(mtmp, FALSE); + } + } +} + +#endif /* OVL2 */ +#ifdef OVLB + +void +migrate_to_level(mtmp, tolev, xyloc, cc) + register struct monst *mtmp; + xchar tolev; /* destination level */ + xchar xyloc; /* MIGR_xxx destination xy location: */ + coord *cc; /* optional destination coordinates */ +{ + register struct obj *obj; + d_level new_lev; + xchar xyflags; + int num_segs = 0; /* count of worm segments */ + + if (mtmp->isshk) + set_residency(mtmp, TRUE); + + if (mtmp->wormno) { + register int cnt; + /* **** NOTE: worm is truncated to # segs = max wormno size **** */ + cnt = count_wsegs(mtmp); + num_segs = min(cnt, MAX_NUM_WORMS - 1); + wormgone(mtmp); + } + + /* set minvent's obj->no_charge to 0 */ + for(obj = mtmp->minvent; obj; obj = obj->nobj) { + if (Has_contents(obj)) + picked_container(obj); /* does the right thing */ + obj->no_charge = 0; + } + + if (mtmp->mleashed) { + mtmp->mtame--; + m_unleash(mtmp, TRUE); + } + relmon(mtmp); + mtmp->nmon = migrating_mons; + migrating_mons = mtmp; + newsym(mtmp->mx,mtmp->my); + + new_lev.dnum = ledger_to_dnum((xchar)tolev); + new_lev.dlevel = ledger_to_dlev((xchar)tolev); + /* overload mtmp->[mx,my], mtmp->[mux,muy], and mtmp->mtrack[] as */ + /* destination codes (setup flag bits before altering mx or my) */ + xyflags = (depth(&new_lev) < depth(&u.uz)); /* 1 => up */ + if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2; + mtmp->wormno = num_segs; + mtmp->mlstmv = monstermoves; + mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx; + mtmp->mtrack[1].y = cc ? cc->y : mtmp->my; + mtmp->mtrack[0].x = xyloc; + mtmp->mtrack[0].y = xyflags; + mtmp->mux = new_lev.dnum; + mtmp->muy = new_lev.dlevel; + mtmp->mx = mtmp->my = 0; /* this implies migration */ +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* return quality of food; the lower the better */ +/* fungi will eat even tainted food */ +int +dogfood(mon,obj) +struct monst *mon; +register struct obj *obj; +{ + boolean carni = carnivorous(mon->data); + boolean herbi = herbivorous(mon->data); + struct permonst *fptr = &mons[obj->corpsenm]; + boolean starving; + + if (is_quest_artifact(obj) || obj_resists(obj, 0, 95)) + return (obj->cursed ? TABU : APPORT); + + switch(obj->oclass) { + case FOOD_CLASS: + if (obj->otyp == CORPSE && + ((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) + || is_rider(fptr))) + return TABU; + + /* Ghouls only eat old corpses... yum! */ + if (mon->data == &mons[PM_GHOUL]) + return (obj->otyp == CORPSE && + peek_at_iced_corpse_age(obj) + 50L <= monstermoves) ? + DOGFOOD : TABU; + + if (!carni && !herbi) + return (obj->cursed ? UNDEF : APPORT); + + /* a starving pet will eat almost anything */ + starving = (mon->mtame && !mon->isminion && + EDOG(mon)->mhpmax_penalty); + + switch (obj->otyp) { + case TRIPE_RATION: + case MEATBALL: + case MEAT_RING: + case MEAT_STICK: + case HUGE_CHUNK_OF_MEAT: + return (carni ? DOGFOOD : MANFOOD); + case EGG: + if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon)) + return POISON; + return (carni ? CADAVER : MANFOOD); + case CORPSE: + if ((peek_at_iced_corpse_age(obj) + 50L <= monstermoves + && obj->corpsenm != PM_LIZARD + && obj->corpsenm != PM_LICHEN + && mon->data->mlet != S_FUNGUS) || + (acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) || + (poisonous(&mons[obj->corpsenm]) && + !resists_poison(mon))) + return POISON; + else if (vegan(fptr)) + return (herbi ? CADAVER : MANFOOD); + else return (carni ? CADAVER : MANFOOD); + case CLOVE_OF_GARLIC: + return (is_undead(mon->data) ? TABU : + ((herbi || starving) ? ACCFOOD : MANFOOD)); + case TIN: + return (metallivorous(mon->data) ? ACCFOOD : MANFOOD); + case APPLE: + case CARROT: + return (herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD); + case BANANA: + return ((mon->data->mlet == S_YETI) ? DOGFOOD : + ((herbi || starving) ? ACCFOOD : MANFOOD)); + default: + if (starving) return ACCFOOD; + return (obj->otyp > SLIME_MOLD ? + (carni ? ACCFOOD : MANFOOD) : + (herbi ? ACCFOOD : MANFOOD)); + } + default: + if (obj->otyp == AMULET_OF_STRANGULATION || + obj->otyp == RIN_SLOW_DIGESTION) + return TABU; + if (hates_silver(mon->data) && + objects[obj->otyp].oc_material == SILVER) + return(TABU); + if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj)) + return(ACCFOOD); + if (metallivorous(mon->data) && is_metallic(obj) && (is_rustprone(obj) || mon->data != &mons[PM_RUST_MONSTER])) { + /* Non-rustproofed ferrous based metals are preferred. */ + return((is_rustprone(obj) && !obj->oerodeproof) ? DOGFOOD : + ACCFOOD); + } + if(!obj->cursed && obj->oclass != BALL_CLASS && + obj->oclass != CHAIN_CLASS) + return(APPORT); + /* fall into next case */ + case ROCK_CLASS: + return(UNDEF); + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +struct monst * +tamedog(mtmp, obj) +register struct monst *mtmp; +register struct obj *obj; +{ + register struct monst *mtmp2; + + /* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */ + if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA] + || (mtmp->data->mflags3 & M3_WANTSARTI)) + return((struct monst *)0); + + /* worst case, at least it'll be peaceful. */ + mtmp->mpeaceful = 1; + set_malign(mtmp); + if(flags.moonphase == FULL_MOON && night() && rn2(6) && obj + && mtmp->data->mlet == S_DOG) + return((struct monst *)0); + + /* If we cannot tame it, at least it's no longer afraid. */ + mtmp->mflee = 0; + mtmp->mfleetim = 0; + + /* make grabber let go now, whether it becomes tame or not */ + if (mtmp == u.ustuck) { + if (u.uswallow) + expels(mtmp, mtmp->data, TRUE); + else if (!(Upolyd && sticks(youmonst.data))) + unstuck(mtmp); + } + + /* feeding it treats makes it tamer */ + if (mtmp->mtame && obj) { + int tasty; + + if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating && + ((tasty = dogfood(mtmp, obj)) == DOGFOOD || + (tasty <= ACCFOOD && EDOG(mtmp)->hungrytime <= monstermoves))) { + /* pet will "catch" and eat this thrown food */ + if (canseemon(mtmp)) { + boolean big_corpse = (obj->otyp == CORPSE && + obj->corpsenm >= LOW_PM && + mons[obj->corpsenm].msize > mtmp->data->msize); + pline("%s catches %s%s", + Monnam(mtmp), the(xname(obj)), + !big_corpse ? "." : ", or vice versa!"); + } else if (cansee(mtmp->mx,mtmp->my)) + pline("%s.", Tobjnam(obj, "stop")); + /* dog_eat expects a floor object */ + place_object(obj, mtmp->mx, mtmp->my); + (void) dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE); + /* eating might have killed it, but that doesn't matter here; + a non-null result suppresses "miss" message for thrown + food and also implies that the object has been deleted */ + return mtmp; + } else + return (struct monst *)0; + } + + if (mtmp->mtame || !mtmp->mcanmove || + /* monsters with conflicting structures cannot be tamed */ + mtmp->isshk || mtmp->isgd || mtmp->ispriest || mtmp->isminion || + is_covetous(mtmp->data) || is_human(mtmp->data) || + (is_demon(mtmp->data) && !is_demon(youmonst.data)) || + (obj && dogfood(mtmp, obj) >= MANFOOD)) return (struct monst *)0; + + if (mtmp->m_id == quest_status.leader_m_id) + return((struct monst *)0); + + /* make a new monster which has the pet extension */ + mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth); + *mtmp2 = *mtmp; + mtmp2->mxlth = sizeof(struct edog); + if (mtmp->mnamelth) Strcpy(NAME(mtmp2), NAME(mtmp)); + initedog(mtmp2); + replmon(mtmp, mtmp2); + /* `mtmp' is now obsolete */ + + if (obj) { /* thrown food */ + /* defer eating until the edog extension has been set up */ + place_object(obj, mtmp2->mx, mtmp2->my); /* put on floor */ + /* devour the food (might grow into larger, genocided monster) */ + if (dog_eat(mtmp2, obj, mtmp2->mx, mtmp2->my, TRUE) == 2) + return mtmp2; /* oops, it died... */ + /* `obj' is now obsolete */ + } + + newsym(mtmp2->mx, mtmp2->my); + if (attacktype(mtmp2->data, AT_WEAP)) { + mtmp2->weapon_check = NEED_HTH_WEAPON; + (void) mon_wield_item(mtmp2); + } + return(mtmp2); +} + +/* + * Called during pet revival or pet life-saving. + * If you killed the pet, it revives wild. + * If you abused the pet a lot while alive, it revives wild. + * If you abused the pet at all while alive, it revives untame. + * If the pet wasn't abused and was very tame, it might revive tame. + */ +void +wary_dog(mtmp, was_dead) +struct monst *mtmp; +boolean was_dead; +{ + struct edog *edog; + boolean quietly = was_dead; + + mtmp->meating = 0; + if (!mtmp->mtame) return; + edog = !mtmp->isminion ? EDOG(mtmp) : 0; + + /* if monster was starving when it died, undo that now */ + if (edog && edog->mhpmax_penalty) { + mtmp->mhpmax += edog->mhpmax_penalty; + mtmp->mhp += edog->mhpmax_penalty; /* heal it */ + edog->mhpmax_penalty = 0; + } + + if (edog && (edog->killed_by_u == 1 || edog->abuse > 2)) { + mtmp->mpeaceful = mtmp->mtame = 0; + if (edog->abuse >= 0 && edog->abuse < 10) + if (!rn2(edog->abuse + 1)) mtmp->mpeaceful = 1; + if(!quietly && cansee(mtmp->mx, mtmp->my)) { + if (haseyes(youmonst.data)) { + if (haseyes(mtmp->data)) + pline("%s %s to look you in the %s.", + Monnam(mtmp), + mtmp->mpeaceful ? "seems unable" : + "refuses", + body_part(EYE)); + else + pline("%s avoids your gaze.", + Monnam(mtmp)); + } + } + } else { + /* chance it goes wild anyway - Pet Semetary */ + if (!rn2(mtmp->mtame)) { + mtmp->mpeaceful = mtmp->mtame = 0; + } + } + if (!mtmp->mtame) { + newsym(mtmp->mx, mtmp->my); + /* a life-saved monster might be leashed; + don't leave it that way if it's no longer tame */ + if (mtmp->mleashed) m_unleash(mtmp, TRUE); + } + + /* if its still a pet, start a clean pet-slate now */ + if (edog && mtmp->mtame) { + edog->revivals++; + edog->killed_by_u = 0; + edog->abuse = 0; + edog->ogoal.x = edog->ogoal.y = -1; + if (was_dead || edog->hungrytime < monstermoves + 500L) + edog->hungrytime = monstermoves + 500L; + if (was_dead) { + edog->droptime = 0L; + edog->dropdist = 10000; + edog->whistletime = 0L; + edog->apport = 5; + } /* else lifesaved, so retain current values */ + } +} + +void +abuse_dog(mtmp) +struct monst *mtmp; +{ + if (!mtmp->mtame) return; + + if (Aggravate_monster || Conflict) mtmp->mtame /=2; + else mtmp->mtame--; + + if (mtmp->mtame && !mtmp->isminion) + EDOG(mtmp)->abuse++; + + if (!mtmp->mtame && mtmp->mleashed) + m_unleash(mtmp, TRUE); + + /* don't make a sound if pet is in the middle of leaving the level */ + /* newsym isn't necessary in this case either */ + if (mtmp->mx != 0) { + if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp); + else growl(mtmp); /* give them a moment's worry */ + + if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); + } +} + +#endif /* OVLB */ + +/*dog.c*/ diff --git a/src/dogmove.c b/src/dogmove.c new file mode 100644 index 0000000..f2e00c1 --- /dev/null +++ b/src/dogmove.c @@ -0,0 +1,867 @@ +/* SCCS Id: @(#)dogmove.c 3.4 2002/09/10 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#include "mfndpos.h" +#include "edog.h" + +extern boolean notonhead; + +#ifdef OVL0 + +STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *)); +STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int)); +STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int)); + +STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *)); +STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P, + XCHAR_P,XCHAR_P)); +STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P)); + +STATIC_OVL struct obj * +DROPPABLES(mon) +register struct monst *mon; +{ + register struct obj *obj; + struct obj *wep = MON_WEP(mon); + boolean item1 = FALSE, item2 = FALSE; + + if (is_animal(mon->data) || mindless(mon->data)) + item1 = item2 = TRUE; + if (!tunnels(mon->data) || !needspick(mon->data)) + item1 = TRUE; + for(obj = mon->minvent; obj; obj = obj->nobj) { + if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK + || !which_armor(mon, W_ARMS))) { + item1 = TRUE; + continue; + } + if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) { + item2 = TRUE; + continue; + } + if (!obj->owornmask && obj != wep) return obj; + } + return (struct obj *)0; +} + +static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 }; + +#endif /* OVL0 */ + +STATIC_OVL boolean FDECL(cursed_object_at, (int, int)); + +STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */ + +STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t)); + +#ifdef OVLB +STATIC_OVL boolean +cursed_object_at(x, y) +int x, y; +{ + struct obj *otmp; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (otmp->cursed) return TRUE; + return FALSE; +} + +int +dog_nutrition(mtmp, obj) +struct monst *mtmp; +struct obj *obj; +{ + int nutrit; + + /* + * It is arbitrary that the pet takes the same length of time to eat + * as a human, but gets more nutritional value. + */ + if (obj->oclass == FOOD_CLASS) { + if(obj->otyp == CORPSE) { + mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6); + nutrit = mons[obj->corpsenm].cnutrit; + } else { + mtmp->meating = objects[obj->otyp].oc_delay; + nutrit = objects[obj->otyp].oc_nutrition; + } + switch(mtmp->data->msize) { + case MZ_TINY: nutrit *= 8; break; + case MZ_SMALL: nutrit *= 6; break; + default: + case MZ_MEDIUM: nutrit *= 5; break; + case MZ_LARGE: nutrit *= 4; break; + case MZ_HUGE: nutrit *= 3; break; + case MZ_GIGANTIC: nutrit *= 2; break; + } + if(obj->oeaten) { + mtmp->meating = eaten_stat(mtmp->meating, obj); + nutrit = eaten_stat(nutrit, obj); + } + } else if (obj->oclass == COIN_CLASS) { + mtmp->meating = (int)(obj->quan/2000) + 1; + if (mtmp->meating < 0) mtmp->meating = 1; + nutrit = (int)(obj->quan/20); + if (nutrit < 0) nutrit = 0; + } else { + /* Unusual pet such as gelatinous cube eating odd stuff. + * meating made consistent with wild monsters in mon.c. + * nutrit made consistent with polymorphed player nutrit in + * eat.c. (This also applies to pets eating gold.) + */ + mtmp->meating = obj->owt/20 + 1; + nutrit = 5*objects[obj->otyp].oc_nutrition; + } + return nutrit; +} + +/* returns 2 if pet dies, otherwise 1 */ +int +dog_eat(mtmp, obj, x, y, devour) +register struct monst *mtmp; +register struct obj * obj; +int x, y; +boolean devour; +{ + register struct edog *edog = EDOG(mtmp); + boolean poly = FALSE, grow = FALSE, heal = FALSE; + int nutrit; + + if(edog->hungrytime < monstermoves) + edog->hungrytime = monstermoves; + nutrit = dog_nutrition(mtmp, obj); + poly = polyfodder(obj); + grow = mlevelgain(obj); + heal = mhealup(obj); + if (devour) { + if (mtmp->meating > 1) mtmp->meating /= 2; + if (nutrit > 1) nutrit = (nutrit * 3) / 4; + } + edog->hungrytime += nutrit; + mtmp->mconf = 0; + if (edog->mhpmax_penalty) { + /* no longer starving */ + mtmp->mhpmax += edog->mhpmax_penalty; + edog->mhpmax_penalty = 0; + } + if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2; + if (mtmp->mtame < 20) mtmp->mtame++; + if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */ + newsym(x, y); + newsym(mtmp->mx, mtmp->my); + } + if (is_pool(x, y) && !Underwater) { + /* Don't print obj */ + /* TODO: Reveal presence of sea monster (especially sharks) */ + } else + /* hack: observe the action if either new or old location is in view */ + /* However, invisible monsters should still be "it" even though out of + sight locations should not. */ + if (cansee(x, y) || cansee(mtmp->mx, mtmp->my)) + pline("%s %s %s.", mon_visible(mtmp) ? noit_Monnam(mtmp) : "It", + devour ? "devours" : "eats", + (obj->oclass == FOOD_CLASS) ? + singular(obj, doname) : doname(obj)); + /* It's a reward if it's DOGFOOD and the player dropped/threw it. */ + /* We know the player had it if invlet is set -dlc */ + if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet) +#ifdef LINT + edog->apport = 0; +#else + edog->apport += (int)(200L/ + ((long)edog->dropdist + monstermoves - edog->droptime)); +#endif + if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) { + /* The object's rustproofing is gone now */ + obj->oerodeproof = 0; + mtmp->mstun = 1; + if (canseemon(mtmp) && flags.verbose) { + pline("%s spits %s out in disgust!", + Monnam(mtmp), distant_name(obj,doname)); + } + } else if (obj == uball) { + unpunish(); + delobj(obj); + } else if (obj == uchain) + unpunish(); + else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) { + obj->quan--; + obj->owt = weight(obj); + } else + delobj(obj); + + if (poly) { + (void) newcham(mtmp, (struct permonst *)0, FALSE, + cansee(mtmp->mx, mtmp->my)); + } + /* limit "instant" growth to prevent potential abuse */ + if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) { + if (!grow_up(mtmp, (struct monst *)0)) return 2; + } + if (heal) mtmp->mhp = mtmp->mhpmax; + return 1; +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* hunger effects -- returns TRUE on starvation */ +STATIC_OVL boolean +dog_hunger(mtmp, edog) +register struct monst *mtmp; +register struct edog *edog; +{ + if (monstermoves > edog->hungrytime + 500) { + if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) { + edog->hungrytime = monstermoves + 500; + /* but not too high; it might polymorph */ + } else if (!edog->mhpmax_penalty) { + /* starving pets are limited in healing */ + int newmhpmax = mtmp->mhpmax / 3; + mtmp->mconf = 1; + edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax; + mtmp->mhpmax = newmhpmax; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + if (mtmp->mhp < 1) goto dog_died; + if (cansee(mtmp->mx, mtmp->my)) + pline("%s is confused from hunger.", Monnam(mtmp)); + else if (couldsee(mtmp->mx, mtmp->my)) + beg(mtmp); + else + You_feel("worried about %s.", y_monnam(mtmp)); + stop_occupation(); + } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) { + dog_died: + if (mtmp->mleashed +#ifdef STEED + && mtmp != u.usteed +#endif + ) + Your("leash goes slack."); + else if (cansee(mtmp->mx, mtmp->my)) + pline("%s starves.", Monnam(mtmp)); + else + You_feel("%s for a moment.", + Hallucination ? "bummed" : "sad"); + mondied(mtmp); + return(TRUE); + } + } + return(FALSE); +} + +/* do something with object (drop, pick up, eat) at current position + * returns 1 if object eaten (since that counts as dog's move), 2 if died + */ +STATIC_OVL int +dog_invent(mtmp, edog, udist) +register struct monst *mtmp; +register struct edog *edog; +int udist; +{ + register int omx, omy; + struct obj *obj; + + if (mtmp->msleeping || !mtmp->mcanmove) return(0); + + omx = mtmp->mx; + omy = mtmp->my; + + /* if we are carrying sth then we drop it (perhaps near @) */ + /* Note: if apport == 1 then our behaviour is independent of udist */ + /* Use udist+1 so steed won't cause divide by zero */ +#ifndef GOLDOBJ + if(DROPPABLES(mtmp) || mtmp->mgold) { +#else + if(DROPPABLES(mtmp)) { +#endif + if (!rn2(udist+1) || !rn2(edog->apport)) + if(rn2(10) < edog->apport){ + relobj(mtmp, (int)mtmp->minvis, TRUE); + if(edog->apport > 1) edog->apport--; + edog->dropdist = udist; /* hpscdi!jon */ + edog->droptime = monstermoves; + } + } else { + if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass) +#ifdef MAIL + && obj->otyp != SCR_MAIL +#endif + ){ + int edible = dogfood(mtmp, obj); + + if ((edible <= CADAVER || + /* starving pet is more aggressive about eating */ + (edog->mhpmax_penalty && edible == ACCFOOD)) && + could_reach_item(mtmp, obj->ox, obj->oy)) + return dog_eat(mtmp, obj, omx, omy, FALSE); + + if(can_carry(mtmp, obj) && !obj->cursed && + could_reach_item(mtmp, obj->ox, obj->oy)) { + if(rn2(20) < edog->apport+3) { + if (rn2(udist) || !rn2(edog->apport)) { + if (cansee(omx, omy) && flags.verbose) + pline("%s picks up %s.", Monnam(mtmp), + distant_name(obj, doname)); + obj_extract_self(obj); + newsym(omx,omy); + (void) mpickobj(mtmp,obj); + if (attacktype(mtmp->data, AT_WEAP) && + mtmp->weapon_check == NEED_WEAPON) { + mtmp->weapon_check = NEED_HTH_WEAPON; + (void) mon_wield_item(mtmp); + } + m_dowear(mtmp, FALSE); + } + } + } + } + } + return 0; +} + +/* set dog's goal -- gtyp, gx, gy + * returns -1/0/1 (dog's desire to approach player) or -2 (abort move) + */ +STATIC_OVL int +dog_goal(mtmp, edog, after, udist, whappr) +register struct monst *mtmp; +struct edog *edog; +int after, udist, whappr; +{ + register int omx, omy; + boolean in_masters_sight, dog_has_minvent; + register struct obj *obj; + xchar otyp; + int appr; + +#ifdef STEED + /* Steeds don't move on their own will */ + if (mtmp == u.usteed) + return (-2); +#endif + + omx = mtmp->mx; + omy = mtmp->my; + + in_masters_sight = couldsee(omx, omy); + dog_has_minvent = (DROPPABLES(mtmp) != 0); + + if (!edog || mtmp->mleashed) { /* he's not going anywhere... */ + gtyp = APPORT; + gx = u.ux; + gy = u.uy; + } else { +#define DDIST(x,y) (dist2(x,y,omx,omy)) +#define SQSRCHRADIUS 5 + int min_x, max_x, min_y, max_y; + register int nx, ny; + + gtyp = UNDEF; /* no goal as yet */ + gx = gy = 0; /* suppress 'used before set' message */ + + if ((min_x = omx - SQSRCHRADIUS) < 1) min_x = 1; + if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1; + if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0; + if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1; + + /* nearby food is the first choice, then other objects */ + for (obj = fobj; obj; obj = obj->nobj) { + nx = obj->ox; + ny = obj->oy; + if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) { + otyp = dogfood(mtmp, obj); + /* skip inferior goals */ + if (otyp > gtyp || otyp == UNDEF) + continue; + /* avoid cursed items unless starving */ + if (cursed_object_at(nx, ny) && + !(edog->mhpmax_penalty && otyp < MANFOOD)) + continue; + /* skip completely unreacheable goals */ + if (!could_reach_item(mtmp, nx, ny) || + !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny)) + continue; + if (otyp < MANFOOD) { + if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) { + gx = nx; + gy = ny; + gtyp = otyp; + } + } else if(gtyp == UNDEF && in_masters_sight && + !dog_has_minvent && + (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) && + (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) && + edog->apport > rn2(8) && + can_carry(mtmp,obj)) { + gx = nx; + gy = ny; + gtyp = APPORT; + } + } + } + } + + /* follow player if appropriate */ + if (gtyp == UNDEF || + (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) { + gx = u.ux; + gy = u.uy; + if (after && udist <= 4 && gx == u.ux && gy == u.uy) + return(-2); + appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; + if (udist > 1) { + if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || + whappr || + (dog_has_minvent && rn2(edog->apport))) + appr = 1; + } + /* if you have dog food it'll follow you more closely */ + if (appr == 0) { + obj = invent; + while (obj) { + if(dogfood(mtmp, obj) == DOGFOOD) { + appr = 1; + break; + } + obj = obj->nobj; + } + } + } else + appr = 1; /* gtyp != UNDEF */ + if(mtmp->mconf) + appr = 0; + +#define FARAWAY (COLNO + 2) /* position outside screen */ + if (gx == u.ux && gy == u.uy && !in_masters_sight) { + register coord *cp; + + cp = gettrack(omx,omy); + if (cp) { + gx = cp->x; + gy = cp->y; + if(edog) edog->ogoal.x = 0; + } else { + /* assume master hasn't moved far, and reuse previous goal */ + if(edog && edog->ogoal.x && + ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) { + gx = edog->ogoal.x; + gy = edog->ogoal.y; + edog->ogoal.x = 0; + } else { + int fardist = FARAWAY * FARAWAY; + gx = gy = FARAWAY; /* random */ + do_clear_area(omx, omy, 9, wantdoor, + (genericptr_t)&fardist); + + /* here gx == FARAWAY e.g. when dog is in a vault */ + if (gx == FARAWAY || (gx == omx && gy == omy)) { + gx = u.ux; + gy = u.uy; + } else if(edog) { + edog->ogoal.x = gx; + edog->ogoal.y = gy; + } + } + } + } else if(edog) { + edog->ogoal.x = 0; + } + return appr; +} + +/* return 0 (no move), 1 (move) or 2 (dead) */ +int +dog_move(mtmp, after) +register struct monst *mtmp; +register int after; /* this is extra fast monster movement */ +{ + int omx, omy; /* original mtmp position */ + int appr, whappr, udist; + int i, j, k; + register struct edog *edog = EDOG(mtmp); + struct obj *obj = (struct obj *) 0; + xchar otyp; + boolean has_edog, cursemsg[9], do_eat = FALSE; + xchar nix, niy; /* position mtmp is (considering) moving to */ + register int nx, ny; /* temporary coordinates */ + xchar cnt, uncursedcnt, chcnt; + int chi = -1, nidist, ndist; + coord poss[9]; + long info[9], allowflags; +#define GDIST(x,y) (dist2(x,y,gx,gy)) + + /* + * Tame Angels have isminion set and an ispriest structure instead of + * an edog structure. Fortunately, guardian Angels need not worry + * about mundane things like eating and fetching objects, and can + * spend all their energy defending the player. (They are the only + * monsters with other structures that can be tame.) + */ + has_edog = !mtmp->isminion; + + omx = mtmp->mx; + omy = mtmp->my; + if (has_edog && dog_hunger(mtmp, edog)) return(2); /* starved */ + + udist = distu(omx,omy); +#ifdef STEED + /* Let steeds eat and maybe throw rider during Conflict */ + if (mtmp == u.usteed) { + if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { + dismount_steed(DISMOUNT_THROWN); + return (1); + } + udist = 1; + } else +#endif + /* maybe we tamed him while being swallowed --jgm */ + if (!udist) return(0); + + nix = omx; /* set before newdogpos */ + niy = omy; + cursemsg[0] = FALSE; /* lint suppression */ + info[0] = 0; /* ditto */ + + if (has_edog) { + j = dog_invent(mtmp, edog, udist); + if (j == 2) return 2; /* died */ + else if (j == 1) goto newdogpos; /* eating something */ + + whappr = (monstermoves - edog->whistletime < 5); + } else + whappr = 0; + + appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0, + after, udist, whappr); + if (appr == -2) return(0); + + allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT; + if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK | ALLOW_WALL); + if (passes_bars(mtmp->data)) allowflags |= ALLOW_BARS; + if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK; + if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { + allowflags |= ALLOW_U; + if (!has_edog) { + coord mm; + /* Guardian angel refuses to be conflicted; rather, + * it disappears, angrily, and sends in some nasties + */ + if (canspotmon(mtmp)) { + pline("%s rebukes you, saying:", Monnam(mtmp)); + verbalize("Since you desire conflict, have some more!"); + } + mongone(mtmp); + i = rnd(4); + while(i--) { + mm.x = u.ux; + mm.y = u.uy; + if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) + (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type, + mm.x, mm.y, FALSE); + } + return(2); + + } + } + if (!Conflict && !mtmp->mconf && + mtmp == u.ustuck && !sticks(youmonst.data)) { + unstuck(mtmp); /* swallowed case handled above */ + You("get released!"); + } + if (!nohands(mtmp->data) && !verysmall(mtmp->data)) { + allowflags |= OPENDOOR; + if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR; + } + if (is_giant(mtmp->data)) allowflags |= BUSTDOOR; + if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG; + cnt = mfndpos(mtmp, poss, info, allowflags); + + /* Normally dogs don't step on cursed items, but if they have no + * other choice they will. This requires checking ahead of time + * to see how many uncursed item squares are around. + */ + uncursedcnt = 0; + for (i = 0; i < cnt; i++) { + nx = poss[i].x; ny = poss[i].y; + if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue; + if (cursed_object_at(nx, ny)) continue; + uncursedcnt++; + } + + chcnt = 0; + chi = -1; + nidist = GDIST(nix,niy); + + for (i = 0; i < cnt; i++) { + nx = poss[i].x; + ny = poss[i].y; + cursemsg[i] = FALSE; + + /* if leashed, we drag him along. */ + if (mtmp->mleashed && distu(nx, ny) > 4) continue; + + /* if a guardian, try to stay close by choice */ + if (!has_edog && + (j = distu(nx, ny)) > 16 && j >= udist) continue; + + if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { + int mstatus; + register struct monst *mtmp2 = m_at(nx,ny); + + if ((int)mtmp2->m_lev >= (int)mtmp->m_lev+2 || + (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) && + mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee + && (perceives(mtmp->data) || !mtmp2->minvis)) || + (mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) || + (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) || + ((mtmp->mhp*4 < mtmp->mhpmax + || mtmp2->data->msound == MS_GUARDIAN + || mtmp2->data->msound == MS_LEADER) && + mtmp2->mpeaceful && !Conflict) || + (touch_petrifies(mtmp2->data) && + !resists_ston(mtmp))) + continue; + + if (after) return(0); /* hit only once each move */ + + notonhead = 0; + mstatus = mattackm(mtmp, mtmp2); + + /* aggressor (pet) died */ + if (mstatus & MM_AGR_DIED) return 2; + + if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && + rn2(4) && mtmp2->mlstmv != monstermoves && + !onscary(mtmp->mx, mtmp->my, mtmp2) && + /* monnear check needed: long worms hit on tail */ + monnear(mtmp2, mtmp->mx, mtmp->my)) { + mstatus = mattackm(mtmp2, mtmp); /* return attack */ + if (mstatus & MM_DEF_DIED) return 2; + } + + return 0; + } + + { /* Dog avoids harmful traps, but perhaps it has to pass one + * in order to follow player. (Non-harmful traps do not + * have ALLOW_TRAPS in info[].) The dog only avoids the + * trap if you've seen it, unlike enemies who avoid traps + * if they've seen some trap of that type sometime in the + * past. (Neither behavior is really realistic.) + */ + struct trap *trap; + + if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) { + if (mtmp->mleashed) { + if (flags.soundok) whimper(mtmp); + } else + /* 1/40 chance of stepping on it anyway, in case + * it has to pass one to follow the player... + */ + if (trap->tseen && rn2(40)) continue; + } + } + + /* dog eschews cursed objects, but likes dog food */ + /* (minion isn't interested; `cursemsg' stays FALSE) */ + if (has_edog) + for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) { + if (obj->cursed) cursemsg[i] = TRUE; + else if ((otyp = dogfood(mtmp, obj)) < MANFOOD && + (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) { + /* Note: our dog likes the food so much that he + * might eat it even when it conceals a cursed object */ + nix = nx; + niy = ny; + chi = i; + do_eat = TRUE; + cursemsg[i] = FALSE; /* not reluctant */ + goto newdogpos; + } + } + /* didn't find something to eat; if we saw a cursed item and + aren't being forced to walk on it, usually keep looking */ + if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 && + rn2(13 * uncursedcnt)) continue; + + /* lessen the chance of backtracking to previous position(s) */ + k = has_edog ? uncursedcnt : cnt; + for (j = 0; j < MTSZ && j < k - 1; j++) + if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) + if (rn2(MTSZ * (k - j))) goto nxti; + + j = ((ndist = GDIST(nx,ny)) - nidist) * appr; + if ((j == 0 && !rn2(++chcnt)) || j < 0 || + (j > 0 && !whappr && + ((omx == nix && omy == niy && !rn2(3)) + || !rn2(12)) + )) { + nix = nx; + niy = ny; + nidist = ndist; + if(j < 0) chcnt = 0; + chi = i; + } + nxti: ; + } +newdogpos: + if (nix != omx || niy != omy) { + struct obj *mw_tmp; + + if (info[chi] & ALLOW_U) { + if (mtmp->mleashed) { /* play it safe */ + pline("%s breaks loose of %s leash!", + Monnam(mtmp), mhis(mtmp)); + m_unleash(mtmp, FALSE); + } + (void) mattacku(mtmp); + return(0); + } + if (!m_in_out_region(mtmp, nix, niy)) + return 1; + if (((IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy)) || + closed_door(nix, niy)) && + mtmp->weapon_check != NO_WEAPON_WANTED && + tunnels(mtmp->data) && needspick(mtmp->data)) { + if (closed_door(nix, niy)) { + if (!(mw_tmp = MON_WEP(mtmp)) || + !is_pick(mw_tmp) || !is_axe(mw_tmp)) + mtmp->weapon_check = NEED_PICK_OR_AXE; + } else if (IS_TREE(levl[nix][niy].typ)) { + if (!(mw_tmp = MON_WEP(mtmp)) || !is_axe(mw_tmp)) + mtmp->weapon_check = NEED_AXE; + } else if (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp)) { + mtmp->weapon_check = NEED_PICK_AXE; + } + if (mtmp->weapon_check >= NEED_PICK_AXE && + mon_wield_item(mtmp)) + return 0; + } + /* insert a worm_move() if worms ever begin to eat things */ + remove_monster(omx, omy); + place_monster(mtmp, nix, niy); + if (cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy))) + pline("%s moves only reluctantly.", Monnam(mtmp)); + for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; + /* We have to know if the pet's gonna do a combined eat and + * move before moving it, but it can't eat until after being + * moved. Thus the do_eat flag. + */ + if (do_eat) { + if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2; + } + } else if (mtmp->mleashed && distu(omx, omy) > 4) { + /* an incredible kludge, but the only way to keep pooch near + * after it spends time eating or in a trap, etc. + */ + coord cc; + + nx = sgn(omx - u.ux); + ny = sgn(omy - u.uy); + cc.x = u.ux + nx; + cc.y = u.uy + ny; + if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; + + i = xytod(nx, ny); + for (j = (i + 7)%8; j < (i + 1)%8; j++) { + dtoxy(&cc, j); + if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; + } + for (j = (i + 6)%8; j < (i + 2)%8; j++) { + dtoxy(&cc, j); + if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; + } + cc.x = mtmp->mx; + cc.y = mtmp->my; +dognext: + if (!m_in_out_region(mtmp, nix, niy)) + return 1; + remove_monster(mtmp->mx, mtmp->my); + place_monster(mtmp, cc.x, cc.y); + newsym(cc.x,cc.y); + set_apparxy(mtmp); + } + return(1); +} + +/* check if a monster could pick up objects from a location */ +STATIC_OVL boolean +could_reach_item(mon, nx, ny) +struct monst *mon; +xchar nx, ny; +{ + if ((!is_pool(nx,ny) || is_swimmer(mon->data)) && + (!is_lava(nx,ny) || likes_lava(mon->data)) && + (!sobj_at(BOULDER,nx,ny) || throws_rocks(mon->data))) + return TRUE; + return FALSE; +} + +/* Hack to prevent a dog from being endlessly stuck near an object that + * it can't reach, such as caught in a teleport scroll niche. It recursively + * checks to see if the squares in between are good. The checking could be a + * little smarter; a full check would probably be useful in m_move() too. + * Since the maximum food distance is 5, this should never be more than 5 calls + * deep. + */ +STATIC_OVL boolean +can_reach_location(mon, mx, my, fx, fy) +struct monst *mon; +xchar mx, my, fx, fy; +{ + int i, j; + int dist; + + if (mx == fx && my == fy) return TRUE; + if (!isok(mx, my)) return FALSE; /* should not happen */ + + dist = dist2(mx, my, fx, fy); + for(i=mx-1; i<=mx+1; i++) { + for(j=my-1; j<=my+1; j++) { + if (!isok(i, j)) + continue; + if (dist2(i, j, fx, fy) >= dist) + continue; + if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) && + (!may_dig(i,j) || !tunnels(mon->data))) + continue; + if (IS_DOOR(levl[i][j].typ) && + (levl[i][j].doormask & (D_CLOSED | D_LOCKED))) + continue; + if (!could_reach_item(mon, i, j)) + continue; + if (can_reach_location(mon, i, j, fx, fy)) + return TRUE; + } + } + return FALSE; +} + +#endif /* OVL0 */ +#ifdef OVLB + +/*ARGSUSED*/ /* do_clear_area client */ +STATIC_PTR void +wantdoor(x, y, distance) +int x, y; +genericptr_t distance; +{ + int ndist; + + if (*(int*)distance > (ndist = distu(x, y))) { + gx = x; + gy = y; + *(int*)distance = ndist; + } +} + +#endif /* OVLB */ + +/*dogmove.c*/ diff --git a/src/dokick.c b/src/dokick.c new file mode 100644 index 0000000..f390414 --- /dev/null +++ b/src/dokick.c @@ -0,0 +1,1491 @@ +/* SCCS Id: @(#)dokick.c 3.4 2003/12/04 */ +/* Copyright (c) Izchak Miller, Mike Stephenson, Steve Linhart, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "eshk.h" + +#define is_bigfoot(x) ((x) == &mons[PM_SASQUATCH]) +#define martial() (martial_bonus() || is_bigfoot(youmonst.data) || \ + (uarmf && uarmf->otyp == KICKING_BOOTS)) + +static NEARDATA struct rm *maploc; +static NEARDATA const char *gate_str; + +extern boolean notonhead; /* for long worms */ + +STATIC_DCL void FDECL(kickdmg, (struct monst *, BOOLEAN_P)); +STATIC_DCL void FDECL(kick_monster, (XCHAR_P, XCHAR_P)); +STATIC_DCL int FDECL(kick_object, (XCHAR_P, XCHAR_P)); +STATIC_DCL char *FDECL(kickstr, (char *)); +STATIC_DCL void FDECL(otransit_msg, (struct obj *, BOOLEAN_P, long)); +STATIC_DCL void FDECL(drop_to, (coord *,SCHAR_P)); + +static NEARDATA struct obj *kickobj; + +static const char kick_passes_thru[] = "kick passes harmlessly through"; + +STATIC_OVL void +kickdmg(mon, clumsy) +register struct monst *mon; +register boolean clumsy; +{ + register int mdx, mdy; + register int dmg = ( ACURRSTR + ACURR(A_DEX) + ACURR(A_CON) )/ 15; + int kick_skill = P_NONE; + int blessed_foot_damage = 0; + boolean trapkilled = FALSE; + + if (uarmf && uarmf->otyp == KICKING_BOOTS) + dmg += 5; + + /* excessive wt affects dex, so it affects dmg */ + if (clumsy) dmg /= 2; + + /* kicking a dragon or an elephant will not harm it */ + if (thick_skinned(mon->data)) dmg = 0; + + /* attacking a shade is useless */ + if (mon->data == &mons[PM_SHADE]) + dmg = 0; + + if ((is_undead(mon->data) || is_demon(mon->data)) && uarmf && + uarmf->blessed) + blessed_foot_damage = 1; + + if (mon->data == &mons[PM_SHADE] && !blessed_foot_damage) { + pline_The("%s.", kick_passes_thru); + /* doesn't exercise skill or abuse alignment or frighten pet, + and shades have no passive counterattack */ + return; + } + + if(mon->m_ap_type) seemimic(mon); + + check_caitiff(mon); + + /* squeeze some guilt feelings... */ + if(mon->mtame) { + abuse_dog(mon); + if (mon->mtame) + monflee(mon, (dmg ? rnd(dmg) : 1), FALSE, FALSE); + else + mon->mflee = 0; + } + + if (dmg > 0) { + /* convert potential damage to actual damage */ + dmg = rnd(dmg); + if (martial()) { + if (dmg > 1) kick_skill = P_MARTIAL_ARTS; + dmg += rn2(ACURR(A_DEX)/2 + 1); + } + /* a good kick exercises your dex */ + exercise(A_DEX, TRUE); + } + if (blessed_foot_damage) dmg += rnd(4); + if (uarmf) dmg += uarmf->spe; + dmg += u.udaminc; /* add ring(s) of increase damage */ + if (dmg > 0) + mon->mhp -= dmg; + if (mon->mhp > 0 && martial() && !bigmonst(mon->data) && !rn2(3) && + mon->mcanmove && mon != u.ustuck && !mon->mtrapped) { + /* see if the monster has a place to move into */ + mdx = mon->mx + u.dx; + mdy = mon->my + u.dy; + if(goodpos(mdx, mdy, mon, 0)) { + pline("%s reels from the blow.", Monnam(mon)); + if (m_in_out_region(mon, mdx, mdy)) { + remove_monster(mon->mx, mon->my); + newsym(mon->mx, mon->my); + place_monster(mon, mdx, mdy); + newsym(mon->mx, mon->my); + set_apparxy(mon); + if (mintrap(mon) == 2) trapkilled = TRUE; + } + } + } + + (void) passive(mon, TRUE, mon->mhp > 0, AT_KICK); + if (mon->mhp <= 0 && !trapkilled) killed(mon); + + /* may bring up a dialog, so put this after all messages */ + if (kick_skill != P_NONE) /* exercise proficiency */ + use_skill(kick_skill, 1); +} + +STATIC_OVL void +kick_monster(x, y) +register xchar x, y; +{ + register boolean clumsy = FALSE; + register struct monst *mon = m_at(x, y); + register int i, j; + + bhitpos.x = x; + bhitpos.y = y; + if (attack_checks(mon, (struct obj *)0)) return; + setmangry(mon); + + /* Kick attacks by kicking monsters are normal attacks, not special. + * This is almost always worthless, since you can either take one turn + * and do all your kicks, or else take one turn and attack the monster + * normally, getting all your attacks _including_ all your kicks. + * If you have >1 kick attack, you get all of them. + */ + if (Upolyd && attacktype(youmonst.data, AT_KICK)) { + struct attack *uattk; + int sum; + schar tmp = find_roll_to_hit(mon); + + for (i = 0; i < NATTK; i++) { + /* first of two kicks might have provoked counterattack + that has incapacitated the hero (ie, floating eye) */ + if (multi < 0) break; + + uattk = &youmonst.data->mattk[i]; + /* we only care about kicking attacks here */ + if (uattk->aatyp != AT_KICK) continue; + + if (mon->data == &mons[PM_SHADE] && + (!uarmf || !uarmf->blessed)) { + /* doesn't matter whether it would have hit or missed, + and shades have no passive counterattack */ + Your("%s %s.", kick_passes_thru, mon_nam(mon)); + break; /* skip any additional kicks */ + } else if (tmp > rnd(20)) { + You("kick %s.", mon_nam(mon)); + sum = damageum(mon, uattk); + (void)passive(mon, (boolean)(sum > 0), (sum != 2), AT_KICK); + if (sum == 2) + break; /* Defender died */ + } else { + missum(mon, uattk); + (void)passive(mon, 0, 1, AT_KICK); + } + } + return; + } + + if(Levitation && !rn2(3) && verysmall(mon->data) && + !is_flyer(mon->data)) { + pline("Floating in the air, you miss wildly!"); + exercise(A_DEX, FALSE); + (void) passive(mon, FALSE, 1, AT_KICK); + return; + } + + i = -inv_weight(); + j = weight_cap(); + + if(i < (j*3)/10) { + if(!rn2((i < j/10) ? 2 : (i < j/5) ? 3 : 4)) { + if(martial() && !rn2(2)) goto doit; + Your("clumsy kick does no damage."); + (void) passive(mon, FALSE, 1, AT_KICK); + return; + } + if(i < j/10) clumsy = TRUE; + else if(!rn2((i < j/5) ? 2 : 3)) clumsy = TRUE; + } + + if(Fumbling) clumsy = TRUE; + + else if(uarm && objects[uarm->otyp].oc_bulky && ACURR(A_DEX) < rnd(25)) + clumsy = TRUE; +doit: + You("kick %s.", mon_nam(mon)); + if(!rn2(clumsy ? 3 : 4) && (clumsy || !bigmonst(mon->data)) && + mon->mcansee && !mon->mtrapped && !thick_skinned(mon->data) && + mon->data->mlet != S_EEL && haseyes(mon->data) && mon->mcanmove && + !mon->mstun && !mon->mconf && !mon->msleeping && + mon->data->mmove >= 12) { + if(!nohands(mon->data) && !rn2(martial() ? 5 : 3)) { + pline("%s blocks your %skick.", Monnam(mon), + clumsy ? "clumsy " : ""); + (void) passive(mon, FALSE, 1, AT_KICK); + return; + } else { + mnexto(mon); + if(mon->mx != x || mon->my != y) { + if(glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x, y); + newsym(x, y); + } + pline("%s %s, %s evading your %skick.", Monnam(mon), + (can_teleport(mon->data) ? "teleports" : + is_floater(mon->data) ? "floats" : + is_flyer(mon->data) ? "swoops" : + (nolimbs(mon->data) || slithy(mon->data)) ? + "slides" : "jumps"), + clumsy ? "easily" : "nimbly", + clumsy ? "clumsy " : ""); + (void) passive(mon, FALSE, 1, AT_KICK); + return; + } + } + } + kickdmg(mon, clumsy); +} + +/* + * Return TRUE if caught (the gold taken care of), FALSE otherwise. + * The gold object is *not* attached to the fobj chain! + */ +boolean +ghitm(mtmp, gold) +register struct monst *mtmp; +register struct obj *gold; +{ + boolean msg_given = FALSE; + + if(!likes_gold(mtmp->data) && !mtmp->isshk && !mtmp->ispriest + && !is_mercenary(mtmp->data)) { + wakeup(mtmp); + } else if (!mtmp->mcanmove) { + /* too light to do real damage */ + if (canseemon(mtmp)) { + pline_The("%s harmlessly %s %s.", xname(gold), + otense(gold, "hit"), mon_nam(mtmp)); + msg_given = TRUE; + } + } else { +#ifdef GOLDOBJ + long value = gold->quan * objects[gold->otyp].oc_cost; +#endif + mtmp->msleeping = 0; + mtmp->meating = 0; + if(!rn2(4)) setmangry(mtmp); /* not always pleasing */ + + /* greedy monsters catch gold */ + if (cansee(mtmp->mx, mtmp->my)) + pline("%s catches the gold.", Monnam(mtmp)); +#ifndef GOLDOBJ + mtmp->mgold += gold->quan; +#endif + if (mtmp->isshk) { + long robbed = ESHK(mtmp)->robbed; + + if (robbed) { +#ifndef GOLDOBJ + robbed -= gold->quan; +#else + robbed -= value; +#endif + if (robbed < 0) robbed = 0; + pline_The("amount %scovers %s recent losses.", + !robbed ? "" : "partially ", + mhis(mtmp)); + ESHK(mtmp)->robbed = robbed; + if(!robbed) + make_happy_shk(mtmp, FALSE); + } else { + if(mtmp->mpeaceful) { +#ifndef GOLDOBJ + ESHK(mtmp)->credit += gold->quan; +#else + ESHK(mtmp)->credit += value; +#endif + You("have %ld %s in credit.", + ESHK(mtmp)->credit, + currency(ESHK(mtmp)->credit)); + } else verbalize("Thanks, scum!"); + } + } else if (mtmp->ispriest) { + if (mtmp->mpeaceful) + verbalize("Thank you for your contribution."); + else verbalize("Thanks, scum!"); + } else if (is_mercenary(mtmp->data)) { + long goldreqd = 0L; + + if (rn2(3)) { + if (mtmp->data == &mons[PM_SOLDIER]) + goldreqd = 100L; + else if (mtmp->data == &mons[PM_SERGEANT]) + goldreqd = 250L; + else if (mtmp->data == &mons[PM_LIEUTENANT]) + goldreqd = 500L; + else if (mtmp->data == &mons[PM_CAPTAIN]) + goldreqd = 750L; + + if (goldreqd) { +#ifndef GOLDOBJ + if (gold->quan > goldreqd + + (u.ugold + u.ulevel*rn2(5))/ACURR(A_CHA)) +#else + if (value > goldreqd + + (money_cnt(invent) + u.ulevel*rn2(5))/ACURR(A_CHA)) +#endif + mtmp->mpeaceful = TRUE; + } + } + if (mtmp->mpeaceful) + verbalize("That should do. Now beat it!"); + else verbalize("That's not enough, coward!"); + } + +#ifndef GOLDOBJ + dealloc_obj(gold); +#else + add_to_minv(mtmp, gold); +#endif + return TRUE; + } + + if (!msg_given) miss(xname(gold), mtmp); + return FALSE; +} + +/* container is kicked, dropped, thrown or otherwise impacted by player. + * Assumes container is on floor. Checks contents for possible damage. */ +void +container_impact_dmg(obj) +struct obj *obj; +{ + struct monst *shkp; + struct obj *otmp, *otmp2; + long loss = 0L; + boolean costly, insider; + xchar x = obj->ox, y = obj->oy; + + /* only consider normal containers */ + if (!Is_container(obj) || Is_mbag(obj)) return; + + costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) && + costly_spot(x, y)); + insider = (*u.ushops && inside_shop(u.ux, u.uy) && + *in_rooms(x, y, SHOPBASE) == *u.ushops); + + for (otmp = obj->cobj; otmp; otmp = otmp2) { + const char *result = (char *)0; + + otmp2 = otmp->nobj; + if (objects[otmp->otyp].oc_material == GLASS && + otmp->oclass != GEM_CLASS && !obj_resists(otmp, 33, 100)) { + result = "shatter"; + } else if (otmp->otyp == EGG && !rn2(3)) { + result = "cracking"; + } + if (result) { + if (otmp->otyp == MIRROR) change_luck(-2); + + /* eggs laid by you. penalty is -1 per egg, max 5, + * but it's always exactly 1 that breaks */ + if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) + change_luck(-1); + You_hear("a muffled %s.", result); + if (costly) + loss += stolen_value(otmp, x, y, + (boolean)shkp->mpeaceful, TRUE); + if (otmp->quan > 1L) + useup(otmp); + else { + obj_extract_self(otmp); + obfree(otmp, (struct obj *) 0); + } + } + } + if (costly && loss) { + if (!insider) { + You("caused %ld %s worth of damage!", loss, currency(loss)); + make_angry_shk(shkp, x, y); + } else { + You("owe %s %ld %s for objects destroyed.", + mon_nam(shkp), loss, currency(loss)); + } + } +} + +STATIC_OVL int +kick_object(x, y) +xchar x, y; +{ + int range; + register struct monst *mon, *shkp; + struct trap *trap; + char bhitroom; + boolean costly, isgold, slide = FALSE; + + /* if a pile, the "top" object gets kicked */ + kickobj = level.objects[x][y]; + + /* kickobj should always be set due to conditions of call */ + if(!kickobj || kickobj->otyp == BOULDER + || kickobj == uball || kickobj == uchain) + return(0); + + if ((trap = t_at(x,y)) != 0 && + (((trap->ttyp == PIT || + trap->ttyp == SPIKED_PIT) && !Passes_walls) || + trap->ttyp == WEB)) { + if (!trap->tseen) find_trap(trap); + You_cant("kick %s that's in a %s!", something, + Hallucination ? "tizzy" : + (trap->ttyp == WEB) ? "web" : "pit"); + return 1; + } + + if(Fumbling && !rn2(3)) { + Your("clumsy kick missed."); + return(1); + } + + if(kickobj->otyp == CORPSE && touch_petrifies(&mons[kickobj->corpsenm]) + && !Stone_resistance && !uarmf) { + char kbuf[BUFSZ]; + + You("kick the %s with your bare %s.", + corpse_xname(kickobj, TRUE), makeplural(body_part(FOOT))); + if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { + You("turn to stone..."); + killer_format = KILLED_BY; + /* KMH -- otmp should be kickobj */ + Sprintf(kbuf, "kicking %s without boots", + an(corpse_xname(kickobj, TRUE))); + killer = kbuf; + done(STONING); + } + } + + /* range < 2 means the object will not move. */ + /* maybe dexterity should also figure here. */ + range = (int)((ACURRSTR)/2 - kickobj->owt/40); + + if(martial()) range += rnd(3); + + if (is_pool(x, y)) { + /* you're in the water too; significantly reduce range */ + range = range / 3 + 1; /* {1,2}=>1, {3,4,5}=>2, {6,7,8}=>3 */ + } else { + if (is_ice(x, y)) range += rnd(3), slide = TRUE; + if (kickobj->greased) range += rnd(3), slide = TRUE; + } + + /* Mjollnir is magically too heavy to kick */ + if(kickobj->oartifact == ART_MJOLLNIR) range = 1; + + /* see if the object has a place to move into */ + if(!ZAP_POS(levl[x+u.dx][y+u.dy].typ) || closed_door(x+u.dx, y+u.dy)) + range = 1; + + costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) && + costly_spot(x, y)); + isgold = (kickobj->oclass == COIN_CLASS); + + if (IS_ROCK(levl[x][y].typ) || closed_door(x, y)) { + if ((!martial() && rn2(20) > ACURR(A_DEX)) || + IS_ROCK(levl[u.ux][u.uy].typ) || closed_door(u.ux, u.uy)) { + if (Blind) + pline("It doesn't come loose."); + else + pline("%s %sn't come loose.", + The(distant_name(kickobj, xname)), + otense(kickobj, "do")); + return (!rn2(3) || martial()); + } + if (Blind) + pline("It comes loose."); + else + pline("%s %s loose.", + The(distant_name(kickobj, xname)), + otense(kickobj, "come")); + obj_extract_self(kickobj); + newsym(x, y); + if (costly && (!costly_spot(u.ux, u.uy) || + !index(u.urooms, *in_rooms(x, y, SHOPBASE)))) + addtobill(kickobj, FALSE, FALSE, FALSE); + if (!flooreffects(kickobj, u.ux, u.uy, "fall")) { + place_object(kickobj, u.ux, u.uy); + stackobj(kickobj); + newsym(u.ux, u.uy); + } + return 1; + } + + /* a box gets a chance of breaking open here */ + if(Is_box(kickobj)) { + boolean otrp = kickobj->otrapped; + + if(range < 2) pline("THUD!"); + + container_impact_dmg(kickobj); + + if (kickobj->olocked) { + if (!rn2(5) || (martial() && !rn2(2))) { + You("break open the lock!"); + kickobj->olocked = 0; + kickobj->obroken = 1; + if (otrp) (void) chest_trap(kickobj, LEG, FALSE); + return(1); + } + } else { + if (!rn2(3) || (martial() && !rn2(2))) { + pline_The("lid slams open, then falls shut."); + if (otrp) (void) chest_trap(kickobj, LEG, FALSE); + return(1); + } + } + if(range < 2) return(1); + /* else let it fall through to the next cases... */ + } + + /* fragile objects should not be kicked */ + if (hero_breaks(kickobj, kickobj->ox, kickobj->oy, FALSE)) return 1; + + /* too heavy to move. range is calculated as potential distance from + * player, so range == 2 means the object may move up to one square + * from its current position + */ + if(range < 2 || (isgold && kickobj->quan > 300L)) { + if(!Is_box(kickobj)) pline("Thump!"); + return(!rn2(3) || martial()); + } + + if (kickobj->quan > 1L && !isgold) kickobj = splitobj(kickobj, 1L); + + if (slide && !Blind) + pline("Whee! %s %s across the %s.", Doname2(kickobj), + otense(kickobj, "slide"), surface(x,y)); + + obj_extract_self(kickobj); + (void) snuff_candle(kickobj); + newsym(x, y); + mon = bhit(u.dx, u.dy, range, KICKED_WEAPON, + (int FDECL((*),(MONST_P,OBJ_P)))0, + (int FDECL((*),(OBJ_P,OBJ_P)))0, + kickobj); + + if(mon) { + if (mon->isshk && + kickobj->where == OBJ_MINVENT && kickobj->ocarry == mon) + return 1; /* alert shk caught it */ + notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y); + if (isgold ? ghitm(mon, kickobj) : /* caught? */ + thitmonst(mon, kickobj)) /* hit && used up? */ + return(1); + } + + /* the object might have fallen down a hole */ + if (kickobj->where == OBJ_MIGRATING) { + if (costly) { + if(isgold) + costly_gold(x, y, kickobj->quan); + else (void)stolen_value(kickobj, x, y, + (boolean)shkp->mpeaceful, FALSE); + } + return 1; + } + + bhitroom = *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE); + if (costly && (!costly_spot(bhitpos.x, bhitpos.y) || + *in_rooms(x, y, SHOPBASE) != bhitroom)) { + if(isgold) + costly_gold(x, y, kickobj->quan); + else (void)stolen_value(kickobj, x, y, + (boolean)shkp->mpeaceful, FALSE); + } + + if(flooreffects(kickobj,bhitpos.x,bhitpos.y,"fall")) return(1); + place_object(kickobj, bhitpos.x, bhitpos.y); + stackobj(kickobj); + newsym(kickobj->ox, kickobj->oy); + return(1); +} + +STATIC_OVL char * +kickstr(buf) +char *buf; +{ + const char *what; + + if (kickobj) what = distant_name(kickobj,doname); + else if (IS_DOOR(maploc->typ)) what = "a door"; + else if (IS_TREE(maploc->typ)) what = "a tree"; + else if (IS_STWALL(maploc->typ)) what = "a wall"; + else if (IS_ROCK(maploc->typ)) what = "a rock"; + else if (IS_THRONE(maploc->typ)) what = "a throne"; + else if (IS_FOUNTAIN(maploc->typ)) what = "a fountain"; + else if (IS_GRAVE(maploc->typ)) what = "a headstone"; +#ifdef SINKS + else if (IS_SINK(maploc->typ)) what = "a sink"; +#endif + else if (IS_ALTAR(maploc->typ)) what = "an altar"; + else if (IS_DRAWBRIDGE(maploc->typ)) what = "a drawbridge"; + else if (maploc->typ == STAIRS) what = "the stairs"; + else if (maploc->typ == LADDER) what = "a ladder"; + else if (maploc->typ == IRONBARS) what = "an iron bar"; + else what = "something weird"; + return strcat(strcpy(buf, "kicking "), what); +} + +int +dokick() +{ + int x, y; + int avrg_attrib; + register struct monst *mtmp; + boolean no_kick = FALSE; + char buf[BUFSZ]; + + if (nolimbs(youmonst.data) || slithy(youmonst.data)) { + You("have no legs to kick with."); + no_kick = TRUE; + } else if (verysmall(youmonst.data)) { + You("are too small to do any kicking."); + no_kick = TRUE; +#ifdef STEED + } else if (u.usteed) { + if (yn_function("Kick your steed?", ynchars, 'y') == 'y') { + You("kick %s.", mon_nam(u.usteed)); + kick_steed(); + return 1; + } else { + return 0; + } +#endif + } else if (Wounded_legs) { + /* note: jump() has similar code */ + long wl = (EWounded_legs & BOTH_SIDES); + const char *bp = body_part(LEG); + + if (wl == BOTH_SIDES) bp = makeplural(bp); + Your("%s%s %s in no shape for kicking.", + (wl == LEFT_SIDE) ? "left " : + (wl == RIGHT_SIDE) ? "right " : "", + bp, (wl == BOTH_SIDES) ? "are" : "is"); + no_kick = TRUE; + } else if (near_capacity() > SLT_ENCUMBER) { + Your("load is too heavy to balance yourself for a kick."); + no_kick = TRUE; + } else if (youmonst.data->mlet == S_LIZARD) { + Your("legs cannot kick effectively."); + no_kick = TRUE; + } else if (u.uinwater && !rn2(2)) { + Your("slow motion kick doesn't hit anything."); + no_kick = TRUE; + } else if (u.utrap) { + switch (u.utraptype) { + case TT_PIT: + pline("There's not enough room to kick down here."); + break; + case TT_WEB: + case TT_BEARTRAP: + You_cant("move your %s!", body_part(LEG)); + break; + default: + break; + } + no_kick = TRUE; + } + + if (no_kick) { + /* ignore direction typed before player notices kick failed */ + display_nhwindow(WIN_MESSAGE, TRUE); /* --More-- */ + return 0; + } + + if(!getdir((char *)0)) return(0); + if(!u.dx && !u.dy) return(0); + + x = u.ux + u.dx; + y = u.uy + u.dy; + + /* KMH -- Kicking boots always succeed */ + if (uarmf && uarmf->otyp == KICKING_BOOTS) + avrg_attrib = 99; + else + avrg_attrib = (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3; + + if(u.uswallow) { + switch(rn2(3)) { + case 0: You_cant("move your %s!", body_part(LEG)); + break; + case 1: if (is_animal(u.ustuck->data)) { + pline("%s burps loudly.", Monnam(u.ustuck)); + break; + } + default: Your("feeble kick has no effect."); break; + } + return(1); + } + if (Levitation) { + int xx, yy; + + xx = u.ux - u.dx; + yy = u.uy - u.dy; + /* doors can be opened while levitating, so they must be + * reachable for bracing purposes + * Possible extension: allow bracing against stuff on the side? + */ + if (isok(xx,yy) && !IS_ROCK(levl[xx][yy].typ) && + !IS_DOOR(levl[xx][yy].typ) && + (!Is_airlevel(&u.uz) || !OBJ_AT(xx,yy))) { + You("have nothing to brace yourself against."); + return(0); + } + } + + wake_nearby(); + u_wipe_engr(2); + + maploc = &levl[x][y]; + + /* The next five tests should stay in */ + /* their present order: monsters, pools, */ + /* objects, non-doors, doors. */ + + if(MON_AT(x, y)) { + struct permonst *mdat; + + mtmp = m_at(x, y); + mdat = mtmp->data; + if (!mtmp->mpeaceful || !canspotmon(mtmp)) + flags.forcefight = TRUE; /* attack even if invisible */ + kick_monster(x, y); + flags.forcefight = FALSE; + /* see comment in attack_checks() */ + if (!DEADMONSTER(mtmp) && + !canspotmon(mtmp) && + /* check x and y; a monster that evades your kick by + jumping to an unseen square doesn't leave an I behind */ + mtmp->mx == x && mtmp->my == y && + !glyph_is_invisible(levl[x][y].glyph) && + !(u.uswallow && mtmp == u.ustuck)) + map_invisible(x, y); + if((Is_airlevel(&u.uz) || Levitation) && flags.move) { + int range; + + range = ((int)youmonst.data->cwt + (weight_cap() + inv_weight())); + if (range < 1) range = 1; /* divide by zero avoidance */ + range = (3*(int)mdat->cwt) / range; + + if(range < 1) range = 1; + hurtle(-u.dx, -u.dy, range, TRUE); + } + return(1); + } + if (glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x, y); + newsym(x, y); + } + if (is_pool(x, y) ^ !!u.uinwater) { + /* objects normally can't be removed from water by kicking */ + You("splash some water around."); + return 1; + } + + kickobj = (struct obj *)0; + if (OBJ_AT(x, y) && + (!Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) + || sobj_at(BOULDER,x,y))) { + if(kick_object(x, y)) { + if(Is_airlevel(&u.uz)) + hurtle(-u.dx, -u.dy, 1, TRUE); /* assume it's light */ + return(1); + } + goto ouch; + } + + if(!IS_DOOR(maploc->typ)) { + if(maploc->typ == SDOOR) { + if(!Levitation && rn2(30) < avrg_attrib) { + cvt_sdoor_to_door(maploc); /* ->typ = DOOR */ + pline("Crash! %s a secret door!", + /* don't "kick open" when it's locked + unless it also happens to be trapped */ + (maploc->doormask & (D_LOCKED|D_TRAPPED)) == D_LOCKED ? + "Your kick uncovers" : "You kick open"); + exercise(A_DEX, TRUE); + if(maploc->doormask & D_TRAPPED) { + maploc->doormask = D_NODOOR; + b_trapped("door", FOOT); + } else if (maploc->doormask != D_NODOOR && + !(maploc->doormask & D_LOCKED)) + maploc->doormask = D_ISOPEN; + if (Blind) + feel_location(x,y); /* we know it's gone */ + else + newsym(x,y); + if (maploc->doormask == D_ISOPEN || + maploc->doormask == D_NODOOR) + unblock_point(x,y); /* vision */ + return(1); + } else goto ouch; + } + if(maploc->typ == SCORR) { + if(!Levitation && rn2(30) < avrg_attrib) { + pline("Crash! You kick open a secret passage!"); + exercise(A_DEX, TRUE); + maploc->typ = CORR; + if (Blind) + feel_location(x,y); /* we know it's gone */ + else + newsym(x,y); + unblock_point(x,y); /* vision */ + return(1); + } else goto ouch; + } + if(IS_THRONE(maploc->typ)) { + register int i; + if(Levitation) goto dumb; + if((Luck < 0 || maploc->doormask) && !rn2(3)) { + maploc->typ = ROOM; + maploc->doormask = 0; /* don't leave loose ends.. */ + (void) mkgold((long)rnd(200), x, y); + if (Blind) + pline("CRASH! You destroy it."); + else { + pline("CRASH! You destroy the throne."); + newsym(x, y); + } + exercise(A_DEX, TRUE); + return(1); + } else if(Luck > 0 && !rn2(3) && !maploc->looted) { + (void) mkgold((long) rn1(201, 300), x, y); + i = Luck + 1; + if(i > 6) i = 6; + while(i--) + (void) mksobj_at(rnd_class(DILITHIUM_CRYSTAL, + LUCKSTONE-1), x, y, FALSE, TRUE); + if (Blind) + You("kick %s loose!", something); + else { + You("kick loose some ornamental coins and gems!"); + newsym(x, y); + } + /* prevent endless milking */ + maploc->looted = T_LOOTED; + return(1); + } else if (!rn2(4)) { + if(dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)) { + fall_through(FALSE); + return(1); + } else goto ouch; + } + goto ouch; + } + if(IS_ALTAR(maploc->typ)) { + if(Levitation) goto dumb; + You("kick %s.",(Blind ? something : "the altar")); + if(!rn2(3)) goto ouch; + altar_wrath(x, y); + exercise(A_DEX, TRUE); + return(1); + } + if(IS_FOUNTAIN(maploc->typ)) { + if(Levitation) goto dumb; + You("kick %s.",(Blind ? something : "the fountain")); + if(!rn2(3)) goto ouch; + /* make metal boots rust */ + if(uarmf && rn2(3)) + if (!rust_dmg(uarmf, "metal boots", 1, FALSE, &youmonst)) { + Your("boots get wet."); + /* could cause short-lived fumbling here */ + } + exercise(A_DEX, TRUE); + return(1); + } + if(IS_GRAVE(maploc->typ) || maploc->typ == IRONBARS) + goto ouch; + if(IS_TREE(maploc->typ)) { + struct obj *treefruit; + /* nothing, fruit or trouble? 75:23.5:1.5% */ + if (rn2(3)) { + if ( !rn2(6) && !(mvitals[PM_KILLER_BEE].mvflags & G_GONE) ) + You_hear("a low buzzing."); /* a warning */ + goto ouch; + } + if (rn2(15) && !(maploc->looted & TREE_LOOTED) && + (treefruit = rnd_treefruit_at(x, y))) { + long nfruit = 8L-rnl(7), nfall; + short frtype = treefruit->otyp; + treefruit->quan = nfruit; + if (is_plural(treefruit)) + pline("Some %s fall from the tree!", xname(treefruit)); + else + pline("%s falls from the tree!", An(xname(treefruit))); + nfall = scatter(x,y,2,MAY_HIT,treefruit); + if (nfall != nfruit) { + /* scatter left some in the tree, but treefruit + * may not refer to the correct object */ + treefruit = mksobj(frtype, TRUE, FALSE); + treefruit->quan = nfruit-nfall; + pline("%ld %s got caught in the branches.", + nfruit-nfall, xname(treefruit)); + dealloc_obj(treefruit); + } + exercise(A_DEX, TRUE); + exercise(A_WIS, TRUE); /* discovered a new food source! */ + newsym(x, y); + maploc->looted |= TREE_LOOTED; + return(1); + } else if (!(maploc->looted & TREE_SWARM)) { + int cnt = rnl(4) + 2; + int made = 0; + coord mm; + mm.x = x; mm.y = y; + while (cnt--) { + if (enexto(&mm, mm.x, mm.y, &mons[PM_KILLER_BEE]) + && makemon(&mons[PM_KILLER_BEE], + mm.x, mm.y, MM_ANGRY)) + made++; + } + if ( made ) + pline("You've attracted the tree's former occupants!"); + else + You("smell stale honey."); + maploc->looted |= TREE_SWARM; + return(1); + } + goto ouch; + } +#ifdef SINKS + if(IS_SINK(maploc->typ)) { + int gend = poly_gender(); + short washerndx = (gend == 1 || (gend == 2 && rn2(2))) ? + PM_INCUBUS : PM_SUCCUBUS; + + if(Levitation) goto dumb; + if(rn2(5)) { + if(flags.soundok) + pline("Klunk! The pipes vibrate noisily."); + else pline("Klunk!"); + exercise(A_DEX, TRUE); + return(1); + } else if(!(maploc->looted & S_LPUDDING) && !rn2(3) && + !(mvitals[PM_BLACK_PUDDING].mvflags & G_GONE)) { + if (Blind) + You_hear("a gushing sound."); + else + pline("A %s ooze gushes up from the drain!", + hcolor(NH_BLACK)); + (void) makemon(&mons[PM_BLACK_PUDDING], + x, y, NO_MM_FLAGS); + exercise(A_DEX, TRUE); + newsym(x,y); + maploc->looted |= S_LPUDDING; + return(1); + } else if(!(maploc->looted & S_LDWASHER) && !rn2(3) && + !(mvitals[washerndx].mvflags & G_GONE)) { + /* can't resist... */ + pline("%s returns!", (Blind ? Something : + "The dish washer")); + if (makemon(&mons[washerndx], x, y, NO_MM_FLAGS)) + newsym(x,y); + maploc->looted |= S_LDWASHER; + exercise(A_DEX, TRUE); + return(1); + } else if(!rn2(3)) { + pline("Flupp! %s.", (Blind ? + "You hear a sloshing sound" : + "Muddy waste pops up from the drain")); + if(!(maploc->looted & S_LRING)) { /* once per sink */ + if (!Blind) + You("see a ring shining in its midst."); + (void) mkobj_at(RING_CLASS, x, y, TRUE); + newsym(x, y); + exercise(A_DEX, TRUE); + exercise(A_WIS, TRUE); /* a discovery! */ + maploc->looted |= S_LRING; + } + return(1); + } + goto ouch; + } +#endif + if (maploc->typ == STAIRS || maploc->typ == LADDER || + IS_STWALL(maploc->typ)) { + if(!IS_STWALL(maploc->typ) && maploc->ladder == LA_DOWN) + goto dumb; +ouch: + pline("Ouch! That hurts!"); + exercise(A_DEX, FALSE); + exercise(A_STR, FALSE); + if (Blind) feel_location(x,y); /* we know we hit it */ + if (is_drawbridge_wall(x,y) >= 0) { + pline_The("drawbridge is unaffected."); + /* update maploc to refer to the drawbridge */ + (void) find_drawbridge(&x,&y); + maploc = &levl[x][y]; + } + if(!rn2(3)) set_wounded_legs(RIGHT_SIDE, 5 + rnd(5)); + losehp(rnd(ACURR(A_CON) > 15 ? 3 : 5), kickstr(buf), + KILLED_BY); + if(Is_airlevel(&u.uz) || Levitation) + hurtle(-u.dx, -u.dy, rn1(2,4), TRUE); /* assume it's heavy */ + return(1); + } + goto dumb; + } + + if(maploc->doormask == D_ISOPEN || + maploc->doormask == D_BROKEN || + maploc->doormask == D_NODOOR) { +dumb: + exercise(A_DEX, FALSE); + if (martial() || ACURR(A_DEX) >= 16 || rn2(3)) { + You("kick at empty space."); + if (Blind) feel_location(x,y); + } else { + pline("Dumb move! You strain a muscle."); + exercise(A_STR, FALSE); + set_wounded_legs(RIGHT_SIDE, 5 + rnd(5)); + } + if ((Is_airlevel(&u.uz) || Levitation) && rn2(2)) { + hurtle(-u.dx, -u.dy, 1, TRUE); + return 1; /* you moved, so use up a turn */ + } + return(0); + } + + /* not enough leverage to kick open doors while levitating */ + if(Levitation) goto ouch; + + exercise(A_DEX, TRUE); + /* door is known to be CLOSED or LOCKED */ + if(rnl(35) < avrg_attrib + (!martial() ? 0 : ACURR(A_DEX))) { + boolean shopdoor = *in_rooms(x, y, SHOPBASE) ? TRUE : FALSE; + /* break the door */ + if(maploc->doormask & D_TRAPPED) { + if (flags.verbose) You("kick the door."); + exercise(A_STR, FALSE); + maploc->doormask = D_NODOOR; + b_trapped("door", FOOT); + } else if(ACURR(A_STR) > 18 && !rn2(5) && !shopdoor) { + pline("As you kick the door, it shatters to pieces!"); + exercise(A_STR, TRUE); + maploc->doormask = D_NODOOR; + } else { + pline("As you kick the door, it crashes open!"); + exercise(A_STR, TRUE); + maploc->doormask = D_BROKEN; + } + if (Blind) + feel_location(x,y); /* we know we broke it */ + else + newsym(x,y); + unblock_point(x,y); /* vision */ + if (shopdoor) { + add_damage(x, y, 400L); + pay_for_damage("break", FALSE); + } + if (in_town(x, y)) + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mpeaceful) { + if (canspotmon(mtmp)) + pline("%s yells:", Amonnam(mtmp)); + else + You_hear("someone yell:"); + verbalize("Halt, thief! You're under arrest!"); + (void) angry_guards(FALSE); + break; + } + } + } else { + if (Blind) feel_location(x,y); /* we know we hit it */ + exercise(A_STR, TRUE); + pline("WHAMMM!!!"); + if (in_town(x, y)) + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + mtmp->mpeaceful && couldsee(mtmp->mx, mtmp->my)) { + if (canspotmon(mtmp)) + pline("%s yells:", Amonnam(mtmp)); + else + You_hear("someone yell:"); + if(levl[x][y].looted & D_WARNED) { + verbalize("Halt, vandal! You're under arrest!"); + (void) angry_guards(FALSE); + } else { + verbalize("Hey, stop damaging that door!"); + levl[x][y].looted |= D_WARNED; + } + break; + } + } + } + return(1); +} + +STATIC_OVL void +drop_to(cc, loc) +coord *cc; +schar loc; +{ + /* cover all the MIGR_xxx choices generated by down_gate() */ + switch (loc) { + case MIGR_RANDOM: /* trap door or hole */ + if (Is_stronghold(&u.uz)) { + cc->x = valley_level.dnum; + cc->y = valley_level.dlevel; + break; + } else if (In_endgame(&u.uz) || Is_botlevel(&u.uz)) { + cc->y = cc->x = 0; + break; + } /* else fall to the next cases */ + case MIGR_STAIRS_UP: + case MIGR_LADDER_UP: + cc->x = u.uz.dnum; + cc->y = u.uz.dlevel + 1; + break; + case MIGR_SSTAIRS: + cc->x = sstairs.tolev.dnum; + cc->y = sstairs.tolev.dlevel; + break; + default: + case MIGR_NOWHERE: + /* y==0 means "nowhere", in which case x doesn't matter */ + cc->y = cc->x = 0; + break; + } +} + +void +impact_drop(missile, x, y, dlev) +struct obj *missile; +xchar x, y, dlev; +{ + schar toloc; + register struct obj *obj, *obj2; + register struct monst *shkp; + long oct, dct, price, debit, robbed; + boolean angry, costly, isrock; + coord cc; + + if(!OBJ_AT(x, y)) return; + + toloc = down_gate(x, y); + drop_to(&cc, toloc); + if (!cc.y) return; + + if (dlev) { + /* send objects next to player falling through trap door. + * checked in obj_delivery(). + */ + toloc = MIGR_NEAR_PLAYER; + cc.y = dlev; + } + + costly = costly_spot(x, y); + price = debit = robbed = 0L; + angry = FALSE; + shkp = (struct monst *) 0; + /* if 'costly', we must keep a record of ESHK(shkp) before + * it undergoes changes through the calls to stolen_value. + * the angry bit must be reset, if needed, in this fn, since + * stolen_value is called under the 'silent' flag to avoid + * unsavory pline repetitions. + */ + if(costly) { + if ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0) { + debit = ESHK(shkp)->debit; + robbed = ESHK(shkp)->robbed; + angry = !shkp->mpeaceful; + } + } + + isrock = (missile && missile->otyp == ROCK); + oct = dct = 0L; + for(obj = level.objects[x][y]; obj; obj = obj2) { + obj2 = obj->nexthere; + if(obj == missile) continue; + /* number of objects in the pile */ + oct += obj->quan; + if(obj == uball || obj == uchain) continue; + /* boulders can fall too, but rarely & never due to rocks */ + if((isrock && obj->otyp == BOULDER) || + rn2(obj->otyp == BOULDER ? 30 : 3)) continue; + obj_extract_self(obj); + + if(costly) { + price += stolen_value(obj, x, y, + (costly_spot(u.ux, u.uy) && + index(u.urooms, *in_rooms(x, y, SHOPBASE))), + TRUE); + /* set obj->no_charge to 0 */ + if (Has_contents(obj)) + picked_container(obj); /* does the right thing */ + if (obj->oclass != COIN_CLASS) + obj->no_charge = 0; + } + + add_to_migration(obj); + obj->ox = cc.x; + obj->oy = cc.y; + obj->owornmask = (long)toloc; + + /* number of fallen objects */ + dct += obj->quan; + } + + if (dct && cansee(x,y)) { /* at least one object fell */ + const char *what = (dct == 1L ? "object falls" : "objects fall"); + + if (missile) + pline("From the impact, %sother %s.", + dct == oct ? "the " : dct == 1L ? "an" : "", what); + else if (oct == dct) + pline("%s adjacent %s %s.", + dct == 1L ? "The" : "All the", what, gate_str); + else + pline("%s adjacent %s %s.", + dct == 1L ? "One of the" : "Some of the", + dct == 1L ? "objects falls" : what, gate_str); + } + + if(costly && shkp && price) { + if(ESHK(shkp)->robbed > robbed) { + You("removed %ld %s worth of goods!", price, currency(price)); + if(cansee(shkp->mx, shkp->my)) { + if(ESHK(shkp)->customer[0] == 0) + (void) strncpy(ESHK(shkp)->customer, + plname, PL_NSIZ); + if(angry) + pline("%s is infuriated!", Monnam(shkp)); + else pline("\"%s, you are a thief!\"", plname); + } else You_hear("a scream, \"Thief!\""); + hot_pursuit(shkp); + (void) angry_guards(FALSE); + return; + } + if(ESHK(shkp)->debit > debit) { + long amt = (ESHK(shkp)->debit - debit); + You("owe %s %ld %s for goods lost.", + Monnam(shkp), + amt, currency(amt)); + } + } + +} + +/* NOTE: ship_object assumes otmp was FREED from fobj or invent. + * is the point of drop. otmp is _not_ an resident: + * otmp is either a kicked, dropped, or thrown object. + */ +boolean +ship_object(otmp, x, y, shop_floor_obj) +xchar x, y; +struct obj *otmp; +boolean shop_floor_obj; +{ + schar toloc; + xchar ox, oy; + coord cc; + struct obj *obj; + struct trap *t; + boolean nodrop, unpaid, container, impact = FALSE; + long n = 0L; + + if (!otmp) return(FALSE); + if ((toloc = down_gate(x, y)) == MIGR_NOWHERE) return(FALSE); + drop_to(&cc, toloc); + if (!cc.y) return(FALSE); + + /* objects other than attached iron ball always fall down ladder, + but have a chance of staying otherwise */ + nodrop = (otmp == uball) || (otmp == uchain) || + (toloc != MIGR_LADDER_UP && rn2(3)); + + container = Has_contents(otmp); + unpaid = (otmp->unpaid || (container && count_unpaid(otmp->cobj))); + + if(OBJ_AT(x, y)) { + for(obj = level.objects[x][y]; obj; obj = obj->nexthere) + if(obj != otmp) n += obj->quan; + if(n) impact = TRUE; + } + /* boulders never fall through trap doors, but they might knock + other things down before plugging the hole */ + if (otmp->otyp == BOULDER && + ((t = t_at(x, y)) != 0) && + (t->ttyp == TRAPDOOR || t->ttyp == HOLE)) { + if (impact) impact_drop(otmp, x, y, 0); + return FALSE; /* let caller finish the drop */ + } + + if (cansee(x, y)) + otransit_msg(otmp, nodrop, n); + + if (nodrop) { + if (impact) impact_drop(otmp, x, y, 0); + return(FALSE); + } + + if(unpaid || shop_floor_obj) { + if(unpaid) { + subfrombill(otmp, shop_keeper(*u.ushops)); + (void)stolen_value(otmp, u.ux, u.uy, TRUE, FALSE); + } else { + ox = otmp->ox; + oy = otmp->oy; + (void)stolen_value(otmp, ox, oy, + (costly_spot(u.ux, u.uy) && + index(u.urooms, *in_rooms(ox, oy, SHOPBASE))), + FALSE); + } + /* set otmp->no_charge to 0 */ + if(container) + picked_container(otmp); /* happens to do the right thing */ + if(otmp->oclass != COIN_CLASS) + otmp->no_charge = 0; + } + + if (otmp == uwep) setuwep((struct obj *)0); + if (otmp == uquiver) setuqwep((struct obj *)0); + if (otmp == uswapwep) setuswapwep((struct obj *)0); + + /* some things break rather than ship */ + if (breaktest(otmp)) { + const char *result; + + if (objects[otmp->otyp].oc_material == GLASS +#ifdef TOURIST + || otmp->otyp == EXPENSIVE_CAMERA +#endif + ) { + if (otmp->otyp == MIRROR) + change_luck(-2); + result = "crash"; + } else { + /* penalty for breaking eggs laid by you */ + if (otmp->otyp == EGG && otmp->spe && otmp->corpsenm >= LOW_PM) + change_luck((schar) -min(otmp->quan, 5L)); + result = "splat"; + } + You_hear("a muffled %s.",result); + obj_extract_self(otmp); + obfree(otmp, (struct obj *) 0); + return TRUE; + } + + add_to_migration(otmp); + otmp->ox = cc.x; + otmp->oy = cc.y; + otmp->owornmask = (long)toloc; + /* boulder from rolling boulder trap, no longer part of the trap */ + if (otmp->otyp == BOULDER) otmp->otrapped = 0; + + if(impact) { + /* the objs impacted may be in a shop other than + * the one in which the hero is located. another + * check for a shk is made in impact_drop. it is, e.g., + * possible to kick/throw an object belonging to one + * shop into another shop through a gap in the wall, + * and cause objects belonging to the other shop to + * fall down a trap door--thereby getting two shopkeepers + * angry at the hero in one shot. + */ + impact_drop(otmp, x, y, 0); + newsym(x,y); + } + return(TRUE); +} + +void +obj_delivery() +{ + register struct obj *otmp, *otmp2; + register int nx, ny; + long where; + + for (otmp = migrating_objs; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + if (otmp->ox != u.uz.dnum || otmp->oy != u.uz.dlevel) continue; + + obj_extract_self(otmp); + where = otmp->owornmask; /* destination code */ + otmp->owornmask = 0L; + + switch ((int)where) { + case MIGR_STAIRS_UP: nx = xupstair, ny = yupstair; + break; + case MIGR_LADDER_UP: nx = xupladder, ny = yupladder; + break; + case MIGR_SSTAIRS: nx = sstairs.sx, ny = sstairs.sy; + break; + case MIGR_NEAR_PLAYER: nx = u.ux, ny = u.uy; + break; + default: + case MIGR_RANDOM: nx = ny = 0; + break; + } + if (nx > 0) { + place_object(otmp, nx, ny); + stackobj(otmp); + (void)scatter(nx, ny, rnd(2), 0, otmp); + } else { /* random location */ + /* set dummy coordinates because there's no + current position for rloco() to update */ + otmp->ox = otmp->oy = 0; + rloco(otmp); + } + } +} + +STATIC_OVL void +otransit_msg(otmp, nodrop, num) +register struct obj *otmp; +register boolean nodrop; +long num; +{ + char obuf[BUFSZ]; + + Sprintf(obuf, "%s%s", + (otmp->otyp == CORPSE && + type_is_pname(&mons[otmp->corpsenm])) ? "" : "The ", + xname(otmp)); + + if(num) { /* means: other objects are impacted */ + Sprintf(eos(obuf), " %s %s object%s", + otense(otmp, "hit"), + num == 1L ? "another" : "other", + num > 1L ? "s" : ""); + if(nodrop) + Sprintf(eos(obuf), "."); + else + Sprintf(eos(obuf), " and %s %s.", + otense(otmp, "fall"), gate_str); + pline("%s", obuf); + } else if(!nodrop) + pline("%s %s %s.", obuf, otense(otmp, "fall"), gate_str); +} + +/* migration destination for objects which fall down to next level */ +schar +down_gate(x, y) +xchar x, y; +{ + struct trap *ttmp; + + gate_str = 0; + /* this matches the player restriction in goto_level() */ + if (on_level(&u.uz, &qstart_level) && !ok_to_quest()) + return MIGR_NOWHERE; + + if ((xdnstair == x && ydnstair == y) || + (sstairs.sx == x && sstairs.sy == y && !sstairs.up)) { + gate_str = "down the stairs"; + return (xdnstair == x && ydnstair == y) ? + MIGR_STAIRS_UP : MIGR_SSTAIRS; + } + if (xdnladder == x && ydnladder == y) { + gate_str = "down the ladder"; + return MIGR_LADDER_UP; + } + + if (((ttmp = t_at(x, y)) != 0 && ttmp->tseen) && + (ttmp->ttyp == TRAPDOOR || ttmp->ttyp == HOLE)) { + gate_str = (ttmp->ttyp == TRAPDOOR) ? + "through the trap door" : "through the hole"; + return MIGR_RANDOM; + } + return MIGR_NOWHERE; +} + +/*dokick.c*/ diff --git a/src/dothrow.c b/src/dothrow.c new file mode 100644 index 0000000..2c4389a --- /dev/null +++ b/src/dothrow.c @@ -0,0 +1,1772 @@ +/* SCCS Id: @(#)dothrow.c 3.4 2003/12/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Contains code for 't' (throw) */ + +#include "hack.h" +#include "edog.h" + +STATIC_DCL int FDECL(throw_obj, (struct obj *,int)); +STATIC_DCL void NDECL(autoquiver); +STATIC_DCL int FDECL(gem_accept, (struct monst *, struct obj *)); +STATIC_DCL void FDECL(tmiss, (struct obj *, struct monst *)); +STATIC_DCL int FDECL(throw_gold, (struct obj *)); +STATIC_DCL void FDECL(check_shop_obj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P)); +STATIC_DCL void FDECL(breakobj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL void FDECL(breakmsg, (struct obj *,BOOLEAN_P)); +STATIC_DCL boolean FDECL(toss_up,(struct obj *, BOOLEAN_P)); +STATIC_DCL boolean FDECL(throwing_weapon, (struct obj *)); +STATIC_DCL void FDECL(sho_obj_return_to_u, (struct obj *obj)); +STATIC_DCL boolean FDECL(mhurtle_step, (genericptr_t,int,int)); + + +static NEARDATA const char toss_objs[] = + { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, WEAPON_CLASS, 0 }; +/* different default choices when wielding a sling (gold must be included) */ +static NEARDATA const char bullets[] = + { ALLOW_COUNT, COIN_CLASS, ALL_CLASSES, GEM_CLASS, 0 }; + +struct obj *thrownobj = 0; /* tracks an object until it lands */ + +extern boolean notonhead; /* for long worms */ + + +/* Throw the selected object, asking for direction */ +STATIC_OVL int +throw_obj(obj, shotlimit) +struct obj *obj; +int shotlimit; +{ + struct obj *otmp; + int multishot = 1; + schar skill; + long wep_mask; + boolean twoweap; + + /* ask "in what direction?" */ +#ifndef GOLDOBJ + if (!getdir((char *)0)) { + if (obj->oclass == COIN_CLASS) { + u.ugold += obj->quan; + flags.botl = 1; + dealloc_obj(obj); + } + return(0); + } + + if(obj->oclass == COIN_CLASS) return(throw_gold(obj)); +#else + if (!getdir((char *)0)) { + /* obj might need to be merged back into the singular gold object */ + freeinv(obj); + addinv(obj); + return(0); + } + + /* + Throwing money is usually for getting rid of it when + a leprechaun approaches, or for bribing an oncoming + angry monster. So throw the whole object. + + If the money is in quiver, throw one coin at a time, + possibly using a sling. + */ + if(obj->oclass == COIN_CLASS && obj != uquiver) return(throw_gold(obj)); +#endif + + if(!canletgo(obj,"throw")) + return(0); + if (obj->oartifact == ART_MJOLLNIR && obj != uwep) { + pline("%s must be wielded before it can be thrown.", + The(xname(obj))); + return(0); + } + if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) < STR19(25)) + || (obj->otyp == BOULDER && !throws_rocks(youmonst.data))) { + pline("It's too heavy."); + return(1); + } + if(!u.dx && !u.dy && !u.dz) { + You("cannot throw an object at yourself."); + return(0); + } + u_wipe_engr(2); + if (!uarmg && !Stone_resistance && (obj->otyp == CORPSE && + touch_petrifies(&mons[obj->corpsenm]))) { + You("throw the %s corpse with your bare %s.", + mons[obj->corpsenm].mname, body_part(HAND)); + Sprintf(killer_buf, "%s corpse", an(mons[obj->corpsenm].mname)); + instapetrify(killer_buf); + } + if (welded(obj)) { + weldmsg(obj); + return 1; + } + + /* Multishot calculations + */ + skill = objects[obj->otyp].oc_skill; + if ((ammo_and_launcher(obj, uwep) || skill == P_DAGGER || + skill == -P_DART || skill == -P_SHURIKEN) && + !(Confusion || Stunned)) { + /* Bonus if the player is proficient in this weapon... */ + switch (P_SKILL(weapon_type(obj))) { + default: break; /* No bonus */ + case P_SKILLED: multishot++; break; + case P_EXPERT: multishot += 2; break; + } + /* ...or is using a special weapon for their role... */ + switch (Role_switch) { + case PM_RANGER: + multishot++; + break; + case PM_ROGUE: + if (skill == P_DAGGER) multishot++; + break; + case PM_SAMURAI: + if (obj->otyp == YA && uwep && uwep->otyp == YUMI) multishot++; + break; + default: + break; /* No bonus */ + } + /* ...or using their race's special bow */ + switch (Race_switch) { + case PM_ELF: + if (obj->otyp == ELVEN_ARROW && uwep && + uwep->otyp == ELVEN_BOW) multishot++; + break; + case PM_ORC: + if (obj->otyp == ORCISH_ARROW && uwep && + uwep->otyp == ORCISH_BOW) multishot++; + break; + default: + break; /* No bonus */ + } + } + + if ((long)multishot > obj->quan) multishot = (int)obj->quan; + multishot = rnd(multishot); + if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit; + + m_shot.s = ammo_and_launcher(obj,uwep) ? TRUE : FALSE; + /* give a message if shooting more than one, or if player + attempted to specify a count */ + if (multishot > 1 || shotlimit > 0) { + /* "You shoot N arrows." or "You throw N daggers." */ + You("%s %d %s.", + m_shot.s ? "shoot" : "throw", + multishot, /* (might be 1 if player gave shotlimit) */ + (multishot == 1) ? singular(obj, xname) : xname(obj)); + } + + wep_mask = obj->owornmask; + m_shot.o = obj->otyp; + m_shot.n = multishot; + for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) { + twoweap = u.twoweap; + /* split this object off from its slot if necessary */ + if (obj->quan > 1L) { + otmp = splitobj(obj, 1L); + } else { + otmp = obj; + if (otmp->owornmask) + remove_worn_item(otmp, FALSE); + } + freeinv(otmp); + throwit(otmp, wep_mask, twoweap); + } + m_shot.n = m_shot.i = 0; + m_shot.o = STRANGE_OBJECT; + m_shot.s = FALSE; + + return 1; +} + + +int +dothrow() +{ + register struct obj *obj; + int shotlimit; + + /* + * Since some characters shoot multiple missiles at one time, + * allow user to specify a count prefix for 'f' or 't' to limit + * number of items thrown (to avoid possibly hitting something + * behind target after killing it, or perhaps to conserve ammo). + * + * Prior to 3.3.0, command ``3t'' meant ``t(shoot) t(shoot) t(shoot)'' + * and took 3 turns. Now it means ``t(shoot at most 3 missiles)''. + */ + /* kludge to work around parse()'s pre-decrement of `multi' */ + shotlimit = (multi || save_cm) ? multi + 1 : 0; + multi = 0; /* reset; it's been used up */ + + if (notake(youmonst.data)) { + You("are physically incapable of throwing anything."); + return 0; + } + + if(check_capacity((char *)0)) return(0); + obj = getobj(uslinging() ? bullets : toss_objs, "throw"); + /* it is also possible to throw food */ + /* (or jewels, or iron balls... ) */ + + if (!obj) return(0); + return throw_obj(obj, shotlimit); +} + + +/* KMH -- Automatically fill quiver */ +/* Suggested by Jeffrey Bay */ +static void +autoquiver() +{ + struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0, *altammo = 0; + + if (uquiver) + return; + + /* Scan through the inventory */ + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->owornmask || otmp->oartifact || !otmp->dknown) { + ; /* Skip it */ + } else if (otmp->otyp == ROCK || + /* seen rocks or known flint or known glass */ + (objects[otmp->otyp].oc_name_known && + otmp->otyp == FLINT) || + (objects[otmp->otyp].oc_name_known && + otmp->oclass == GEM_CLASS && + objects[otmp->otyp].oc_material == GLASS)) { + if (uslinging()) + oammo = otmp; + else if (ammo_and_launcher(otmp, uswapwep)) + altammo = otmp; + else if (!omisc) + omisc = otmp; + } else if (otmp->oclass == GEM_CLASS) { + ; /* skip non-rock gems--they're ammo but + player has to select them explicitly */ + } else if (is_ammo(otmp)) { + if (ammo_and_launcher(otmp, uwep)) + /* Ammo matched with launcher (bow and arrow, crossbow and bolt) */ + oammo = otmp; + else if (ammo_and_launcher(otmp, uswapwep)) + altammo = otmp; + else + /* Mismatched ammo (no better than an ordinary weapon) */ + omisc = otmp; + } else if (is_missile(otmp)) { + /* Missile (dart, shuriken, etc.) */ + omissile = otmp; + } else if (otmp->oclass == WEAPON_CLASS && throwing_weapon(otmp)) { + /* Ordinary weapon */ + if (objects[otmp->otyp].oc_skill == P_DAGGER + && !omissile) + omissile = otmp; + else + omisc = otmp; + } + } + + /* Pick the best choice */ + if (oammo) + setuqwep(oammo); + else if (omissile) + setuqwep(omissile); + else if (altammo) + setuqwep(altammo); + else if (omisc) + setuqwep(omisc); + + return; +} + + +/* Throw from the quiver */ +int +dofire() +{ + int shotlimit; + + if (notake(youmonst.data)) { + You("are physically incapable of doing that."); + return 0; + } + + if(check_capacity((char *)0)) return(0); + if (!uquiver) { + if (!flags.autoquiver) { + /* Don't automatically fill the quiver */ + You("have no ammunition readied!"); + return(dothrow()); + } + autoquiver(); + if (!uquiver) { + You("have nothing appropriate for your quiver!"); + return(dothrow()); + } else { + You("fill your quiver:"); + prinv((char *)0, uquiver, 0L); + } + } + + /* + * Since some characters shoot multiple missiles at one time, + * allow user to specify a count prefix for 'f' or 't' to limit + * number of items thrown (to avoid possibly hitting something + * behind target after killing it, or perhaps to conserve ammo). + * + * The number specified can never increase the number of missiles. + * Using ``5f'' when the shooting skill (plus RNG) dictates launch + * of 3 projectiles will result in 3 being shot, not 5. + */ + /* kludge to work around parse()'s pre-decrement of `multi' */ + shotlimit = (multi || save_cm) ? multi + 1 : 0; + multi = 0; /* reset; it's been used up */ + + return throw_obj(uquiver, shotlimit); +} + + +/* + * Object hits floor at hero's feet. Called from drop() and throwit(). + */ +void +hitfloor(obj) +register struct obj *obj; +{ + if (IS_SOFT(levl[u.ux][u.uy].typ) || u.uinwater) { + dropy(obj); + return; + } + if (IS_ALTAR(levl[u.ux][u.uy].typ)) + doaltarobj(obj); + else + pline("%s hit%s the %s.", Doname2(obj), + (obj->quan == 1L) ? "s" : "", surface(u.ux,u.uy)); + + if (hero_breaks(obj, u.ux, u.uy, TRUE)) return; + if (ship_object(obj, u.ux, u.uy, FALSE)) return; + dropy(obj); + if (!u.uswallow) container_impact_dmg(obj); +} + +/* + * Walk a path from src_cc to dest_cc, calling a proc for each location + * except the starting one. If the proc returns FALSE, stop walking + * and return FALSE. If stopped early, dest_cc will be the location + * before the failed callback. + */ +boolean +walk_path(src_cc, dest_cc, check_proc, arg) + coord *src_cc; + coord *dest_cc; + boolean FDECL((*check_proc), (genericptr_t, int, int)); + genericptr_t arg; +{ + int x, y, dx, dy, x_change, y_change, err, i, prev_x, prev_y; + boolean keep_going = TRUE; + + /* Use Bresenham's Line Algorithm to walk from src to dest */ + dx = dest_cc->x - src_cc->x; + dy = dest_cc->y - src_cc->y; + prev_x = x = src_cc->x; + prev_y = y = src_cc->y; + + if (dx < 0) { + x_change = -1; + dx = -dx; + } else + x_change = 1; + if (dy < 0) { + y_change = -1; + dy = -dy; + } else + y_change = 1; + + i = err = 0; + if (dx < dy) { + while (i++ < dy) { + prev_x = x; + prev_y = y; + y += y_change; + err += dx; + if (err >= dy) { + x += x_change; + err -= dy; + } + /* check for early exit condition */ + if (!(keep_going = (*check_proc)(arg, x, y))) + break; + } + } else { + while (i++ < dx) { + prev_x = x; + prev_y = y; + x += x_change; + err += dy; + if (err >= dx) { + y += y_change; + err -= dx; + } + /* check for early exit condition */ + if (!(keep_going = (*check_proc)(arg, x, y))) + break; + } + } + + if (keep_going) + return TRUE; /* successful */ + + dest_cc->x = prev_x; + dest_cc->y = prev_y; + return FALSE; +} + +/* + * Single step for the hero flying through the air from jumping, flying, + * etc. Called from hurtle() and jump() via walk_path(). We expect the + * argument to be a pointer to an integer -- the range -- which is + * used in the calculation of points off if we hit something. + * + * Bumping into monsters won't cause damage but will wake them and make + * them angry. Auto-pickup isn't done, since you don't have control over + * your movements at the time. + * + * Possible additions/changes: + * o really attack monster if we hit one + * o set stunned if we hit a wall or door + * o reset nomul when we stop + * o creepy feeling if pass through monster (if ever implemented...) + * o bounce off walls + * o let jumps go over boulders + */ +boolean +hurtle_step(arg, x, y) + genericptr_t arg; + int x, y; +{ + int ox, oy, *range = (int *)arg; + struct obj *obj; + struct monst *mon; + boolean may_pass = TRUE; + struct trap *ttmp; + + if (!isok(x,y)) { + You_feel("the spirits holding you back."); + return FALSE; + } else if (!in_out_region(x, y)) { + return FALSE; + } else if (*range == 0) { + return FALSE; /* previous step wants to stop now */ + } + + if (!Passes_walls || !(may_pass = may_passwall(x, y))) { + if (IS_ROCK(levl[x][y].typ) || closed_door(x,y)) { + const char *s; + + pline("Ouch!"); + if (IS_TREE(levl[x][y].typ)) + s = "bumping into a tree"; + else if (IS_ROCK(levl[x][y].typ)) + s = "bumping into a wall"; + else + s = "bumping into a door"; + losehp(rnd(2+*range), s, KILLED_BY); + return FALSE; + } + if (levl[x][y].typ == IRONBARS) { + You("crash into some iron bars. Ouch!"); + losehp(rnd(2+*range), "crashing into iron bars", KILLED_BY); + return FALSE; + } + if ((obj = sobj_at(BOULDER,x,y)) != 0) { + You("bump into a %s. Ouch!", xname(obj)); + losehp(rnd(2+*range), "bumping into a boulder", KILLED_BY); + return FALSE; + } + if (!may_pass) { + /* did we hit a no-dig non-wall position? */ + You("smack into something!"); + losehp(rnd(2+*range), "touching the edge of the universe", KILLED_BY); + return FALSE; + } + if ((u.ux - x) && (u.uy - y) && + bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) { + boolean too_much = (invent && (inv_weight() + weight_cap() > 600)); + /* Move at a diagonal. */ + if (bigmonst(youmonst.data) || too_much) { + You("%sget forcefully wedged into a crevice.", + too_much ? "and all your belongings " : ""); + losehp(rnd(2+*range), "wedging into a narrow crevice", KILLED_BY); + return FALSE; + } + } + } + + if ((mon = m_at(x, y)) != 0) { + You("bump into %s.", a_monnam(mon)); + wakeup(mon); + return FALSE; + } + if ((u.ux - x) && (u.uy - y) && + bad_rock(youmonst.data,u.ux,y) && bad_rock(youmonst.data,x,u.uy)) { + /* Move at a diagonal. */ + if (In_sokoban(&u.uz)) { + You("come to an abrupt halt!"); + return FALSE; + } + } + + ox = u.ux; + oy = u.uy; + u.ux = x; + u.uy = y; + newsym(ox, oy); /* update old position */ + vision_recalc(1); /* update for new position */ + flush_screen(1); + /* FIXME: + * Each trap should really trigger on the recoil if + * it would trigger during normal movement. However, + * not all the possible side-effects of this are + * tested [as of 3.4.0] so we trigger those that + * we have tested, and offer a message for the + * ones that we have not yet tested. + */ + if ((ttmp = t_at(x, y)) != 0) { + if (ttmp->ttyp == MAGIC_PORTAL) { + dotrap(ttmp,0); + return FALSE; + } else if (ttmp->ttyp == FIRE_TRAP) { + dotrap(ttmp,0); + } else if ((ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT || + ttmp->ttyp == HOLE || ttmp->ttyp == TRAPDOOR) && + In_sokoban(&u.uz)) { + /* Air currents overcome the recoil */ + dotrap(ttmp,0); + *range = 0; + return TRUE; + } else { + if (ttmp->tseen) + You("pass right over %s %s.", + (ttmp->ttyp == ARROW_TRAP) ? "an" : "a", + defsyms[trap_to_defsym(ttmp->ttyp)].explanation); + } + } + if (--*range < 0) /* make sure our range never goes negative */ + *range = 0; + if (*range != 0) + delay_output(); + return TRUE; +} + +STATIC_OVL boolean +mhurtle_step(arg, x, y) + genericptr_t arg; + int x, y; +{ + struct monst *mon = (struct monst *)arg; + + /* TODO: Treat walls, doors, iron bars, pools, lava, etc. specially + * rather than just stopping before. + */ + if (goodpos(x, y, mon, 0) && m_in_out_region(mon, x, y)) { + remove_monster(mon->mx, mon->my); + newsym(mon->mx, mon->my); + place_monster(mon, x, y); + newsym(mon->mx, mon->my); + set_apparxy(mon); + (void) mintrap(mon); + return TRUE; + } + return FALSE; +} + +/* + * The player moves through the air for a few squares as a result of + * throwing or kicking something. + * + * dx and dy should be the direction of the hurtle, not of the original + * kick or throw and be only. + */ +void +hurtle(dx, dy, range, verbose) + int dx, dy, range; + boolean verbose; +{ + coord uc, cc; + + /* The chain is stretched vertically, so you shouldn't be able to move + * very far diagonally. The premise that you should be able to move one + * spot leads to calculations that allow you to only move one spot away + * from the ball, if you are levitating over the ball, or one spot + * towards the ball, if you are at the end of the chain. Rather than + * bother with all of that, assume that there is no slack in the chain + * for diagonal movement, give the player a message and return. + */ + if(Punished && !carried(uball)) { + You_feel("a tug from the iron ball."); + nomul(0); + return; + } else if (u.utrap) { + You("are anchored by the %s.", + u.utraptype == TT_WEB ? "web" : u.utraptype == TT_LAVA ? "lava" : + u.utraptype == TT_INFLOOR ? surface(u.ux,u.uy) : "trap"); + nomul(0); + return; + } + + /* make sure dx and dy are [-1,0,1] */ + dx = sgn(dx); + dy = sgn(dy); + + if(!range || (!dx && !dy) || u.ustuck) return; /* paranoia */ + + nomul(-range); + if (verbose) + You("%s in the opposite direction.", range > 1 ? "hurtle" : "float"); + /* if we're in the midst of shooting multiple projectiles, stop */ + if (m_shot.i < m_shot.n) { + /* last message before hurtling was "you shoot N arrows" */ + You("stop %sing after the first %s.", + m_shot.s ? "shoot" : "throw", m_shot.s ? "shot" : "toss"); + m_shot.n = m_shot.i; /* make current shot be the last */ + } + if (In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + uc.x = u.ux; + uc.y = u.uy; + /* this setting of cc is only correct if dx and dy are [-1,0,1] only */ + cc.x = u.ux + (dx * range); + cc.y = u.uy + (dy * range); + (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range); +} + +/* Move a monster through the air for a few squares. + */ +void +mhurtle(mon, dx, dy, range) + struct monst *mon; + int dx, dy, range; +{ + coord mc, cc; + + /* At the very least, debilitate the monster */ + mon->movement = 0; + mon->mstun = 1; + + /* Is the monster stuck or too heavy to push? + * (very large monsters have too much inertia, even floaters and flyers) + */ + if (mon->data->msize >= MZ_HUGE || mon == u.ustuck || mon->mtrapped) + return; + + /* Make sure dx and dy are [-1,0,1] */ + dx = sgn(dx); + dy = sgn(dy); + if(!range || (!dx && !dy)) return; /* paranoia */ + + /* Send the monster along the path */ + mc.x = mon->mx; + mc.y = mon->my; + cc.x = mon->mx + (dx * range); + cc.y = mon->my + (dy * range); + (void) walk_path(&mc, &cc, mhurtle_step, (genericptr_t)mon); + return; +} + +STATIC_OVL void +check_shop_obj(obj, x, y, broken) +register struct obj *obj; +register xchar x, y; +register boolean broken; +{ + struct monst *shkp = shop_keeper(*u.ushops); + + if(!shkp) return; + + if(broken) { + if (obj->unpaid) { + (void)stolen_value(obj, u.ux, u.uy, + (boolean)shkp->mpeaceful, FALSE); + subfrombill(obj, shkp); + } + obj->no_charge = 1; + return; + } + + if (!costly_spot(x, y) || *in_rooms(x, y, SHOPBASE) != *u.ushops) { + /* thrown out of a shop or into a different shop */ + if (obj->unpaid) { + (void)stolen_value(obj, u.ux, u.uy, + (boolean)shkp->mpeaceful, FALSE); + subfrombill(obj, shkp); + } + } else { + if (costly_spot(u.ux, u.uy) && costly_spot(x, y)) { + if(obj->unpaid) subfrombill(obj, shkp); + else if(!(x == shkp->mx && y == shkp->my)) + sellobj(obj, x, y); + } + } +} + +/* + * Hero tosses an object upwards with appropriate consequences. + * + * Returns FALSE if the object is gone. + */ +STATIC_OVL boolean +toss_up(obj, hitsroof) +struct obj *obj; +boolean hitsroof; +{ + const char *almost; + /* note: obj->quan == 1 */ + + if (hitsroof) { + if (breaktest(obj)) { + pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy)); + breakmsg(obj, !Blind); + breakobj(obj, u.ux, u.uy, TRUE, TRUE); + return FALSE; + } + almost = ""; + } else { + almost = " almost"; + } + pline("%s%s hits the %s, then falls back on top of your %s.", + Doname2(obj), almost, ceiling(u.ux,u.uy), body_part(HEAD)); + + /* object now hits you */ + + if (obj->oclass == POTION_CLASS) { + potionhit(&youmonst, obj, TRUE); + } else if (breaktest(obj)) { + int otyp = obj->otyp, ocorpsenm = obj->corpsenm; + int blindinc; + + /* need to check for blindness result prior to destroying obj */ + blindinc = (otyp == CREAM_PIE || otyp == BLINDING_VENOM) && + /* AT_WEAP is ok here even if attack type was AT_SPIT */ + can_blnd(&youmonst, &youmonst, AT_WEAP, obj) ? rnd(25) : 0; + + breakmsg(obj, !Blind); + breakobj(obj, u.ux, u.uy, TRUE, TRUE); + obj = 0; /* it's now gone */ + switch (otyp) { + case EGG: + if (touch_petrifies(&mons[ocorpsenm]) && + !uarmh && !Stone_resistance && + !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) + goto petrify; + case CREAM_PIE: + case BLINDING_VENOM: + pline("You've got it all over your %s!", body_part(FACE)); + if (blindinc) { + if (otyp == BLINDING_VENOM && !Blind) + pline("It blinds you!"); + u.ucreamed += blindinc; + make_blinded(Blinded + (long)blindinc, FALSE); + if (!Blind) Your(vision_clears); + } + break; + default: + break; + } + return FALSE; + } else { /* neither potion nor other breaking object */ + boolean less_damage = uarmh && is_metallic(uarmh), artimsg = FALSE; + int dmg = dmgval(obj, &youmonst); + + if (obj->oartifact) + /* need a fake die roll here; rn1(18,2) avoids 1 and 20 */ + artimsg = artifact_hit((struct monst *)0, &youmonst, + obj, &dmg, rn1(18,2)); + + if (!dmg) { /* probably wasn't a weapon; base damage on weight */ + dmg = (int) obj->owt / 100; + if (dmg < 1) dmg = 1; + else if (dmg > 6) dmg = 6; + if (youmonst.data == &mons[PM_SHADE] && + objects[obj->otyp].oc_material != SILVER) + dmg = 0; + } + if (dmg > 1 && less_damage) dmg = 1; + if (dmg > 0) dmg += u.udaminc; + if (dmg < 0) dmg = 0; /* beware negative rings of increase damage */ + if (Half_physical_damage) dmg = (dmg + 1) / 2; + + if (uarmh) { + if (less_damage && dmg < (Upolyd ? u.mh : u.uhp)) { + if (!artimsg) + pline("Fortunately, you are wearing a hard helmet."); + } else if (flags.verbose && + !(obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm]))) + Your("%s does not protect you.", xname(uarmh)); + } else if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) { + if (!Stone_resistance && + !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { + petrify: + killer_format = KILLED_BY; + killer = "elementary physics"; /* "what goes up..." */ + You("turn to stone."); + if (obj) dropy(obj); /* bypass most of hitfloor() */ + done(STONING); + return obj ? TRUE : FALSE; + } + } + hitfloor(obj); + losehp(dmg, "falling object", KILLED_BY_AN); + } + return TRUE; +} + +/* return true for weapon meant to be thrown; excludes ammo */ +STATIC_OVL boolean +throwing_weapon(obj) +struct obj *obj; +{ + return (is_missile(obj) || is_spear(obj) || + /* daggers and knife (excludes scalpel) */ + (is_blade(obj) && !is_sword(obj) && + (objects[obj->otyp].oc_dir & PIERCE)) || + /* special cases [might want to add AXE] */ + obj->otyp == WAR_HAMMER || obj->otyp == AKLYS); +} + +/* the currently thrown object is returning to you (not for boomerangs) */ +STATIC_OVL void +sho_obj_return_to_u(obj) +struct obj *obj; +{ + /* might already be our location (bounced off a wall) */ + if (bhitpos.x != u.ux || bhitpos.y != u.uy) { + int x = bhitpos.x - u.dx, y = bhitpos.y - u.dy; + + tmp_at(DISP_FLASH, obj_to_glyph(obj)); + while(x != u.ux || y != u.uy) { + tmp_at(x, y); + delay_output(); + x -= u.dx; y -= u.dy; + } + tmp_at(DISP_END, 0); + } +} + +void +throwit(obj, wep_mask, twoweap) +register struct obj *obj; +long wep_mask; /* used to re-equip returning boomerang */ +boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */ +{ + register struct monst *mon; + register int range, urange; + boolean impaired = (Confusion || Stunned || Blind || + Hallucination || Fumbling); + + if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) { + boolean slipok = TRUE; + if (ammo_and_launcher(obj, uwep)) + pline("%s!", Tobjnam(obj, "misfire")); + else { + /* only slip if it's greased or meant to be thrown */ + if (obj->greased || throwing_weapon(obj)) + /* BUG: this message is grammatically incorrect if obj has + a plural name; greased gloves or boots for instance. */ + pline("%s as you throw it!", Tobjnam(obj, "slip")); + else slipok = FALSE; + } + if (slipok) { + u.dx = rn2(3)-1; + u.dy = rn2(3)-1; + if (!u.dx && !u.dy) u.dz = 1; + impaired = TRUE; + } + } + + if ((u.dx || u.dy || (u.dz < 1)) && + calc_capacity((int)obj->owt) > SLT_ENCUMBER && + (Upolyd ? (u.mh < 5 && u.mh != u.mhmax) + : (u.uhp < 10 && u.uhp != u.uhpmax)) && + obj->owt > (unsigned)((Upolyd ? u.mh : u.uhp) * 2) && + !Is_airlevel(&u.uz)) { + You("have so little stamina, %s drops from your grasp.", + the(xname(obj))); + exercise(A_CON, FALSE); + u.dx = u.dy = 0; + u.dz = 1; + } + + thrownobj = obj; + + if(u.uswallow) { + mon = u.ustuck; + bhitpos.x = mon->mx; + bhitpos.y = mon->my; + } else if(u.dz) { + if (u.dz < 0 && Role_if(PM_VALKYRIE) && + obj->oartifact == ART_MJOLLNIR && !impaired) { + pline("%s the %s and returns to your hand!", + Tobjnam(obj, "hit"), ceiling(u.ux,u.uy)); + obj = addinv(obj); + (void) encumber_msg(); + setuwep(obj); + u.twoweap = twoweap; + } else if (u.dz < 0 && !Is_airlevel(&u.uz) && + !Underwater && !Is_waterlevel(&u.uz)) { + (void) toss_up(obj, rn2(5)); + } else { + hitfloor(obj); + } + thrownobj = (struct obj*)0; + return; + + } else if(obj->otyp == BOOMERANG && !Underwater) { + if(Is_airlevel(&u.uz) || Levitation) + hurtle(-u.dx, -u.dy, 1, TRUE); + mon = boomhit(u.dx, u.dy); + if(mon == &youmonst) { /* the thing was caught */ + exercise(A_DEX, TRUE); + obj = addinv(obj); + (void) encumber_msg(); + if (wep_mask && !(obj->owornmask & wep_mask)) { + setworn(obj, wep_mask); + u.twoweap = twoweap; + } + thrownobj = (struct obj*)0; + return; + } + } else { + urange = (int)(ACURRSTR)/2; + /* balls are easy to throw or at least roll */ + /* also, this insures the maximum range of a ball is greater + * than 1, so the effects from throwing attached balls are + * actually possible + */ + if (obj->otyp == HEAVY_IRON_BALL) + range = urange - (int)(obj->owt/100); + else + range = urange - (int)(obj->owt/40); + if (obj == uball) { + if (u.ustuck) range = 1; + else if (range >= 5) range = 5; + } + if (range < 1) range = 1; + + if (is_ammo(obj)) { + if (ammo_and_launcher(obj, uwep)) + range++; + else if (obj->oclass != GEM_CLASS) + range /= 2; + } + + if (Is_airlevel(&u.uz) || Levitation) { + /* action, reaction... */ + urange -= range; + if(urange < 1) urange = 1; + range -= urange; + if(range < 1) range = 1; + } + + if (obj->otyp == BOULDER) + range = 20; /* you must be giant */ + else if (obj->oartifact == ART_MJOLLNIR) + range = (range + 1) / 2; /* it's heavy */ + else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR) + range = 1; + + if (Underwater) range = 1; + + mon = bhit(u.dx, u.dy, range, THROWN_WEAPON, + (int FDECL((*),(MONST_P,OBJ_P)))0, + (int FDECL((*),(OBJ_P,OBJ_P)))0, + obj); + + /* have to do this after bhit() so u.ux & u.uy are correct */ + if(Is_airlevel(&u.uz) || Levitation) + hurtle(-u.dx, -u.dy, urange, TRUE); + } + + if (mon) { + boolean obj_gone; + + if (mon->isshk && + obj->where == OBJ_MINVENT && obj->ocarry == mon) { + thrownobj = (struct obj*)0; + return; /* alert shk caught it */ + } + (void) snuff_candle(obj); + notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my); + obj_gone = thitmonst(mon, obj); + /* Monster may have been tamed; this frees old mon */ + mon = m_at(bhitpos.x, bhitpos.y); + + /* [perhaps this should be moved into thitmonst or hmon] */ + if (mon && mon->isshk && + (!inside_shop(u.ux, u.uy) || + !index(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) + hot_pursuit(mon); + + if (obj_gone) return; + } + + if (u.uswallow) { + /* ball is not picked up by monster */ + if (obj != uball) (void) mpickobj(u.ustuck,obj); + } else { + /* the code following might become part of dropy() */ + if (obj->oartifact == ART_MJOLLNIR && + Role_if(PM_VALKYRIE) && rn2(100)) { + /* we must be wearing Gauntlets of Power to get here */ + sho_obj_return_to_u(obj); /* display its flight */ + + if (!impaired && rn2(100)) { + pline("%s to your hand!", Tobjnam(obj, "return")); + obj = addinv(obj); + (void) encumber_msg(); + setuwep(obj); + u.twoweap = twoweap; + if(cansee(bhitpos.x, bhitpos.y)) + newsym(bhitpos.x,bhitpos.y); + } else { + int dmg = rn2(2); + if (!dmg) { + pline(Blind ? "%s lands %s your %s." : + "%s back to you, landing %s your %s.", + Blind ? Something : Tobjnam(obj, "return"), + Levitation ? "beneath" : "at", + makeplural(body_part(FOOT))); + } else { + dmg += rnd(3); + pline(Blind ? "%s your %s!" : + "%s back toward you, hitting your %s!", + Tobjnam(obj, Blind ? "hit" : "fly"), + body_part(ARM)); + (void) artifact_hit((struct monst *)0, + &youmonst, obj, &dmg, 0); + losehp(dmg, xname(obj), + obj_is_pname(obj) ? KILLED_BY : KILLED_BY_AN); + } + if (ship_object(obj, u.ux, u.uy, FALSE)) { + thrownobj = (struct obj*)0; + return; + } + dropy(obj); + } + thrownobj = (struct obj*)0; + return; + } + + if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) && + breaktest(obj)) { + tmp_at(DISP_FLASH, obj_to_glyph(obj)); + tmp_at(bhitpos.x, bhitpos.y); + delay_output(); + tmp_at(DISP_END, 0); + breakmsg(obj, cansee(bhitpos.x, bhitpos.y)); + breakobj(obj, bhitpos.x, bhitpos.y, TRUE, TRUE); + return; + } + if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return; + obj_no_longer_held(obj); + if (mon && mon->isshk && is_pick(obj)) { + if (cansee(bhitpos.x, bhitpos.y)) + pline("%s snatches up %s.", + Monnam(mon), the(xname(obj))); + if(*u.ushops) + check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE); + (void) mpickobj(mon, obj); /* may merge and free obj */ + thrownobj = (struct obj*)0; + return; + } + (void) snuff_candle(obj); + if (!mon && ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) { + thrownobj = (struct obj*)0; + return; + } + thrownobj = (struct obj*)0; + place_object(obj, bhitpos.x, bhitpos.y); + if(*u.ushops && obj != uball) + check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE); + + stackobj(obj); + if (obj == uball) + drop_ball(bhitpos.x, bhitpos.y); + if (cansee(bhitpos.x, bhitpos.y)) + newsym(bhitpos.x,bhitpos.y); + if (obj_sheds_light(obj)) + vision_full_recalc = 1; + if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ)) + container_impact_dmg(obj); + } +} + +/* an object may hit a monster; various factors adjust the chance of hitting */ +int +omon_adj(mon, obj, mon_notices) +struct monst *mon; +struct obj *obj; +boolean mon_notices; +{ + int tmp = 0; + + /* size of target affects the chance of hitting */ + tmp += (mon->data->msize - MZ_MEDIUM); /* -2..+5 */ + /* sleeping target is more likely to be hit */ + if (mon->msleeping) { + tmp += 2; + if (mon_notices) mon->msleeping = 0; + } + /* ditto for immobilized target */ + if (!mon->mcanmove || !mon->data->mmove) { + tmp += 4; + if (mon_notices && mon->data->mmove && !rn2(10)) { + mon->mcanmove = 1; + mon->mfrozen = 0; + } + } + /* some objects are more likely to hit than others */ + switch (obj->otyp) { + case HEAVY_IRON_BALL: + if (obj != uball) tmp += 2; + break; + case BOULDER: + tmp += 6; + break; + default: + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || + obj->oclass == GEM_CLASS) + tmp += hitval(obj, mon); + break; + } + return tmp; +} + +/* thrown object misses target monster */ +STATIC_OVL void +tmiss(obj, mon) +struct obj *obj; +struct monst *mon; +{ + const char *missile = mshot_xname(obj); + + /* If the target can't be seen or doesn't look like a valid target, + avoid "the arrow misses it," or worse, "the arrows misses the mimic." + An attentive player will still notice that this is different from + an arrow just landing short of any target (no message in that case), + so will realize that there is a valid target here anyway. */ + if (!canseemon(mon) || (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER)) + pline("%s %s.", The(missile), otense(obj, "miss")); + else + miss(missile, mon); + if (!rn2(3)) wakeup(mon); + return; +} + +#define quest_arti_hits_leader(obj,mon) \ + (obj->oartifact && is_quest_artifact(obj) && (mon->data->msound == MS_LEADER)) + +/* + * Object thrown by player arrives at monster's location. + * Return 1 if obj has disappeared or otherwise been taken care of, + * 0 if caller must take care of it. + */ +int +thitmonst(mon, obj) +register struct monst *mon; +register struct obj *obj; +{ + register int tmp; /* Base chance to hit */ + register int disttmp; /* distance modifier */ + int otyp = obj->otyp; + boolean guaranteed_hit = (u.uswallow && mon == u.ustuck); + + /* Differences from melee weapons: + * + * Dex still gives a bonus, but strength does not. + * Polymorphed players lacking attacks may still throw. + * There's a base -1 to hit. + * No bonuses for fleeing or stunned targets (they don't dodge + * melee blows as readily, but dodging arrows is hard anyway). + * Not affected by traps, etc. + * Certain items which don't in themselves do damage ignore tmp. + * Distance and monster size affect chance to hit. + */ + tmp = -1 + Luck + find_mac(mon) + u.uhitinc + + maybe_polyd(youmonst.data->mlevel, u.ulevel); + if (ACURR(A_DEX) < 4) tmp -= 3; + else if (ACURR(A_DEX) < 6) tmp -= 2; + else if (ACURR(A_DEX) < 8) tmp -= 1; + else if (ACURR(A_DEX) >= 14) tmp += (ACURR(A_DEX) - 14); + + /* Modify to-hit depending on distance; but keep it sane. + * Polearms get a distance penalty even when wielded; it's + * hard to hit at a distance. + */ + disttmp = 3 - distmin(u.ux, u.uy, mon->mx, mon->my); + if(disttmp < -4) disttmp = -4; + tmp += disttmp; + + /* gloves are a hinderance to proper use of bows */ + if (uarmg && uwep && objects[uwep->otyp].oc_skill == P_BOW) { + switch (uarmg->otyp) { + case GAUNTLETS_OF_POWER: /* metal */ + tmp -= 2; + break; + case GAUNTLETS_OF_FUMBLING: + tmp -= 3; + break; + case LEATHER_GLOVES: + case GAUNTLETS_OF_DEXTERITY: + break; + default: + impossible("Unknown type of gloves (%d)", uarmg->otyp); + break; + } + } + + tmp += omon_adj(mon, obj, TRUE); + if (is_orc(mon->data) && maybe_polyd(is_elf(youmonst.data), + Race_if(PM_ELF))) + tmp++; + if (guaranteed_hit) { + tmp += 1000; /* Guaranteed hit */ + } + + if (obj->oclass == GEM_CLASS && is_unicorn(mon->data)) { + if (mon->mtame) { + pline("%s catches and drops %s.", Monnam(mon), the(xname(obj))); + return 0; + } else { + pline("%s catches %s.", Monnam(mon), the(xname(obj))); + return gem_accept(mon, obj); + } + } + + /* don't make game unwinnable if naive player throws artifact + at leader.... */ + if (quest_arti_hits_leader(obj, mon)) { + /* not wakeup(), which angers non-tame monsters */ + mon->msleeping = 0; + mon->mstrategy &= ~STRAT_WAITMASK; + + if (mon->mcanmove) { + pline("%s catches %s.", Monnam(mon), the(xname(obj))); + if (mon->mpeaceful) { + boolean next2u = monnear(mon, u.ux, u.uy); + + finish_quest(obj); /* acknowledge quest completion */ + pline("%s %s %s back to you.", Monnam(mon), + (next2u ? "hands" : "tosses"), the(xname(obj))); + if (!next2u) sho_obj_return_to_u(obj); + obj = addinv(obj); /* back into your inventory */ + (void) encumber_msg(); + } else { + /* angry leader caught it and isn't returning it */ + (void) mpickobj(mon, obj); + } + return 1; /* caller doesn't need to place it */ + } + return(0); + } + + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || + obj->oclass == GEM_CLASS) { + if (is_ammo(obj)) { + if (!ammo_and_launcher(obj, uwep)) { + tmp -= 4; + } else { + tmp += uwep->spe - greatest_erosion(uwep); + tmp += weapon_hit_bonus(uwep); + if (uwep->oartifact) tmp += spec_abon(uwep, mon); + /* + * Elves and Samurais are highly trained w/bows, + * especially their own special types of bow. + * Polymorphing won't make you a bow expert. + */ + if ((Race_if(PM_ELF) || Role_if(PM_SAMURAI)) && + (!Upolyd || your_race(youmonst.data)) && + objects[uwep->otyp].oc_skill == P_BOW) { + tmp++; + if (Race_if(PM_ELF) && uwep->otyp == ELVEN_BOW) + tmp++; + else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI) + tmp++; + } + } + } else { + if (otyp == BOOMERANG) /* arbitrary */ + tmp += 4; + else if (throwing_weapon(obj)) /* meant to be thrown */ + tmp += 2; + else /* not meant to be thrown */ + tmp -= 2; + /* we know we're dealing with a weapon or weptool handled + by WEAPON_SKILLS once ammo objects have been excluded */ + tmp += weapon_hit_bonus(obj); + } + + if (tmp >= rnd(20)) { + if (hmon(mon,obj,1)) { /* mon still alive */ + cutworm(mon, bhitpos.x, bhitpos.y, obj); + } + exercise(A_DEX, TRUE); + /* projectiles other than magic stones + sometimes disappear when thrown */ + if (objects[otyp].oc_skill < P_NONE && + objects[otyp].oc_skill > -P_BOOMERANG && + !objects[otyp].oc_magic) { + /* we were breaking 2/3 of everything unconditionally. + * we still don't want anything to survive unconditionally, + * but we need ammo to stay around longer on average. + */ + int broken, chance; + chance = 3 + greatest_erosion(obj) - obj->spe; + if (chance > 1) + broken = rn2(chance); + else + broken = !rn2(4); + if (obj->blessed && !rnl(4)) + broken = 0; + + if (broken) { + if (*u.ushops) + check_shop_obj(obj, bhitpos.x,bhitpos.y, TRUE); + obfree(obj, (struct obj *)0); + return 1; + } + } + passive_obj(mon, obj, (struct attack *)0); + } else { + tmiss(obj, mon); + } + + } else if (otyp == HEAVY_IRON_BALL) { + exercise(A_STR, TRUE); + if (tmp >= rnd(20)) { + int was_swallowed = guaranteed_hit; + + exercise(A_DEX, TRUE); + if (!hmon(mon,obj,1)) { /* mon killed */ + if (was_swallowed && !u.uswallow && obj == uball) + return 1; /* already did placebc() */ + } + } else { + tmiss(obj, mon); + } + + } else if (otyp == BOULDER) { + exercise(A_STR, TRUE); + if (tmp >= rnd(20)) { + exercise(A_DEX, TRUE); + (void) hmon(mon,obj,1); + } else { + tmiss(obj, mon); + } + + } else if ((otyp == EGG || otyp == CREAM_PIE || + otyp == BLINDING_VENOM || otyp == ACID_VENOM) && + (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { + (void) hmon(mon, obj, 1); + return 1; /* hmon used it up */ + + } else if (obj->oclass == POTION_CLASS && + (guaranteed_hit || ACURR(A_DEX) > rnd(25))) { + potionhit(mon, obj, TRUE); + return 1; + + } else if (befriend_with_obj(mon->data, obj) || + (mon->mtame && dogfood(mon, obj) <= ACCFOOD)) { + if (tamedog(mon, obj)) + return 1; /* obj is gone */ + else { + /* not tmiss(), which angers non-tame monsters */ + miss(xname(obj), mon); + mon->msleeping = 0; + mon->mstrategy &= ~STRAT_WAITMASK; + } + } else if (guaranteed_hit) { + /* this assumes that guaranteed_hit is due to swallowing */ + wakeup(mon); + if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) { + if (is_animal(u.ustuck->data)) { + minstapetrify(u.ustuck, TRUE); + /* Don't leave a cockatrice corpse available in a statue */ + if (!u.uswallow) { + delobj(obj); + return 1; + } + } + } + pline("%s into %s %s.", + Tobjnam(obj, "vanish"), s_suffix(mon_nam(mon)), + is_animal(u.ustuck->data) ? "entrails" : "currents"); + } else { + tmiss(obj, mon); + } + + return 0; +} + +STATIC_OVL int +gem_accept(mon, obj) +register struct monst *mon; +register struct obj *obj; +{ + char buf[BUFSZ]; + boolean is_buddy = sgn(mon->data->maligntyp) == sgn(u.ualign.type); + boolean is_gem = objects[obj->otyp].oc_material == GEMSTONE; + int ret = 0; + static NEARDATA const char nogood[] = " is not interested in your junk."; + static NEARDATA const char acceptgift[] = " accepts your gift."; + static NEARDATA const char maybeluck[] = " hesitatingly"; + static NEARDATA const char noluck[] = " graciously"; + static NEARDATA const char addluck[] = " gratefully"; + + Strcpy(buf,Monnam(mon)); + mon->mpeaceful = 1; + mon->mavenge = 0; + + /* object properly identified */ + if(obj->dknown && objects[obj->otyp].oc_name_known) { + if(is_gem) { + if(is_buddy) { + Strcat(buf,addluck); + change_luck(5); + } else { + Strcat(buf,maybeluck); + change_luck(rn2(7)-3); + } + } else { + Strcat(buf,nogood); + goto nopick; + } + /* making guesses */ + } else if(obj->onamelth || objects[obj->otyp].oc_uname) { + if(is_gem) { + if(is_buddy) { + Strcat(buf,addluck); + change_luck(2); + } else { + Strcat(buf,maybeluck); + change_luck(rn2(3)-1); + } + } else { + Strcat(buf,nogood); + goto nopick; + } + /* value completely unknown to @ */ + } else { + if(is_gem) { + if(is_buddy) { + Strcat(buf,addluck); + change_luck(1); + } else { + Strcat(buf,maybeluck); + change_luck(rn2(3)-1); + } + } else { + Strcat(buf,noluck); + } + } + Strcat(buf,acceptgift); + if(*u.ushops) check_shop_obj(obj, mon->mx, mon->my, TRUE); + (void) mpickobj(mon, obj); /* may merge and free obj */ + ret = 1; + +nopick: + if(!Blind) pline("%s", buf); + if (!tele_restrict(mon)) (void) rloc(mon, FALSE); + return(ret); +} + +/* + * Comments about the restructuring of the old breaks() routine. + * + * There are now three distinct phases to object breaking: + * breaktest() - which makes the check/decision about whether the + * object is going to break. + * breakmsg() - which outputs a message about the breakage, + * appropriate for that particular object. Should + * only be called after a positve breaktest(). + * on the object and, if it going to be called, + * it must be called before calling breakobj(). + * Calling breakmsg() is optional. + * breakobj() - which actually does the breakage and the side-effects + * of breaking that particular object. This should + * only be called after a positive breaktest() on the + * object. + * + * Each of the above routines is currently static to this source module. + * There are two routines callable from outside this source module which + * perform the routines above in the correct sequence. + * + * hero_breaks() - called when an object is to be broken as a result + * of something that the hero has done. (throwing it, + * kicking it, etc.) + * breaks() - called when an object is to be broken for some + * reason other than the hero doing something to it. + */ + +/* + * The hero causes breakage of an object (throwing, dropping it, etc.) + * Return 0 if the object didn't break, 1 if the object broke. + */ +int +hero_breaks(obj, x, y, from_invent) +struct obj *obj; +xchar x, y; /* object location (ox, oy may not be right) */ +boolean from_invent; /* thrown or dropped by player; maybe on shop bill */ +{ + boolean in_view = !Blind; + if (!breaktest(obj)) return 0; + breakmsg(obj, in_view); + breakobj(obj, x, y, TRUE, from_invent); + return 1; +} + +/* + * The object is going to break for a reason other than the hero doing + * something to it. + * Return 0 if the object doesn't break, 1 if the object broke. + */ +int +breaks(obj, x, y) +struct obj *obj; +xchar x, y; /* object location (ox, oy may not be right) */ +{ + boolean in_view = Blind ? FALSE : cansee(x, y); + + if (!breaktest(obj)) return 0; + breakmsg(obj, in_view); + breakobj(obj, x, y, FALSE, FALSE); + return 1; +} + +/* + * Unconditionally break an object. Assumes all resistance checks + * and break messages have been delivered prior to getting here. + */ +STATIC_OVL void +breakobj(obj, x, y, hero_caused, from_invent) +struct obj *obj; +xchar x, y; /* object location (ox, oy may not be right) */ +boolean hero_caused; /* is this the hero's fault? */ +boolean from_invent; +{ + switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { + case MIRROR: + if (hero_caused) + change_luck(-2); + break; + case POT_WATER: /* really, all potions */ + if (obj->otyp == POT_OIL && obj->lamplit) { + splatter_burning_oil(x,y); + } else if (distu(x,y) <= 2) { + if (!breathless(youmonst.data) || haseyes(youmonst.data)) { + if (obj->otyp != POT_WATER) { + if (!breathless(youmonst.data)) + /* [what about "familiar odor" when known?] */ + You("smell a peculiar odor..."); + else { + int numeyes = eyecount(youmonst.data); + Your("%s water%s.", + (numeyes == 1) ? body_part(EYE) : + makeplural(body_part(EYE)), + (numeyes == 1) ? "s" : ""); + } + } + potionbreathe(obj); + } + } + /* monster breathing isn't handled... [yet?] */ + break; + case EGG: + /* breaking your own eggs is bad luck */ + if (hero_caused && obj->spe && obj->corpsenm >= LOW_PM) + change_luck((schar) -min(obj->quan, 5L)); + break; + } + if (hero_caused) { + if (from_invent) { + if (*u.ushops) + check_shop_obj(obj, x, y, TRUE); + } else if (!obj->no_charge && costly_spot(x, y)) { + /* it is assumed that the obj is a floor-object */ + char *o_shop = in_rooms(x, y, SHOPBASE); + struct monst *shkp = shop_keeper(*o_shop); + + if (shkp) { /* (implies *o_shop != '\0') */ + static NEARDATA long lastmovetime = 0L; + static NEARDATA boolean peaceful_shk = FALSE; + /* We want to base shk actions on her peacefulness + at start of this turn, so that "simultaneous" + multiple breakage isn't drastically worse than + single breakage. (ought to be done via ESHK) */ + if (moves != lastmovetime) + peaceful_shk = shkp->mpeaceful; + if (stolen_value(obj, x, y, peaceful_shk, FALSE) > 0L && + (*o_shop != u.ushops[0] || !inside_shop(u.ux, u.uy)) && + moves != lastmovetime) make_angry_shk(shkp, x, y); + lastmovetime = moves; + } + } + } + delobj(obj); +} + +/* + * Check to see if obj is going to break, but don't actually break it. + * Return 0 if the object isn't going to break, 1 if it is. + */ +boolean +breaktest(obj) +struct obj *obj; +{ + if (obj_resists(obj, 1, 99)) return 0; + if (objects[obj->otyp].oc_material == GLASS && !obj->oartifact && + obj->oclass != GEM_CLASS) + return 1; + switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { +#ifdef TOURIST + case EXPENSIVE_CAMERA: +#endif + case POT_WATER: /* really, all potions */ + case EGG: + case CREAM_PIE: + case MELON: + case ACID_VENOM: + case BLINDING_VENOM: + return 1; + default: + return 0; + } +} + +STATIC_OVL void +breakmsg(obj, in_view) +struct obj *obj; +boolean in_view; +{ + const char *to_pieces; + + to_pieces = ""; + switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) { + default: /* glass or crystal wand */ + if (obj->oclass != WAND_CLASS) + impossible("breaking odd object?"); + case CRYSTAL_PLATE_MAIL: + case LENSES: + case MIRROR: + case CRYSTAL_BALL: +#ifdef TOURIST + case EXPENSIVE_CAMERA: +#endif + to_pieces = " into a thousand pieces"; + /*FALLTHRU*/ + case POT_WATER: /* really, all potions */ + if (!in_view) + You_hear("%s shatter!", something); + else + pline("%s shatter%s%s!", Doname2(obj), + (obj->quan==1) ? "s" : "", to_pieces); + break; + case EGG: + case MELON: + pline("Splat!"); + break; + case CREAM_PIE: + if (in_view) pline("What a mess!"); + break; + case ACID_VENOM: + case BLINDING_VENOM: + pline("Splash!"); + break; + } +} + +STATIC_OVL int +throw_gold(obj) +struct obj *obj; +{ + int range, odx, ody; +#ifndef GOLDOBJ + long zorks = obj->quan; +#endif + register struct monst *mon; + + if(!u.dx && !u.dy && !u.dz) { +#ifndef GOLDOBJ + u.ugold += obj->quan; + flags.botl = 1; + dealloc_obj(obj); +#endif + You("cannot throw gold at yourself."); + return(0); + } +#ifdef GOLDOBJ + freeinv(obj); +#endif + if(u.uswallow) { + pline(is_animal(u.ustuck->data) ? + "%s in the %s's entrails." : "%s into %s.", +#ifndef GOLDOBJ + "The gold disappears", mon_nam(u.ustuck)); + u.ustuck->mgold += zorks; + dealloc_obj(obj); +#else + "The money disappears", mon_nam(u.ustuck)); + add_to_minv(u.ustuck, obj); +#endif + return(1); + } + + if(u.dz) { + if (u.dz < 0 && !Is_airlevel(&u.uz) && + !Underwater && !Is_waterlevel(&u.uz)) { + pline_The("gold hits the %s, then falls back on top of your %s.", + ceiling(u.ux,u.uy), body_part(HEAD)); + /* some self damage? */ + if(uarmh) pline("Fortunately, you are wearing a helmet!"); + } + bhitpos.x = u.ux; + bhitpos.y = u.uy; + } else { + /* consistent with range for normal objects */ + range = (int)((ACURRSTR)/2 - obj->owt/40); + + /* see if the gold has a place to move into */ + odx = u.ux + u.dx; + ody = u.uy + u.dy; + if(!ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) { + bhitpos.x = u.ux; + bhitpos.y = u.uy; + } else { + mon = bhit(u.dx, u.dy, range, THROWN_WEAPON, + (int FDECL((*),(MONST_P,OBJ_P)))0, + (int FDECL((*),(OBJ_P,OBJ_P)))0, + obj); + if(mon) { + if (ghitm(mon, obj)) /* was it caught? */ + return 1; + } else { + if(ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) + return 1; + } + } + } + + if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return(1); + if(u.dz > 0) + pline_The("gold hits the %s.", surface(bhitpos.x,bhitpos.y)); + place_object(obj,bhitpos.x,bhitpos.y); + if(*u.ushops) sellobj(obj, bhitpos.x, bhitpos.y); + stackobj(obj); + newsym(bhitpos.x,bhitpos.y); + return(1); +} + +/*dothrow.c*/ diff --git a/src/drawing.c b/src/drawing.c new file mode 100644 index 0000000..da39614 --- /dev/null +++ b/src/drawing.c @@ -0,0 +1,901 @@ +/* SCCS Id: @(#)drawing.c 3.4 1999/12/02 */ +/* Copyright (c) NetHack Development Team 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "tcap.h" + +/* Relevent header information in rm.h and objclass.h. */ + +#ifdef C +#undef C +#endif + +#ifdef TEXTCOLOR +#define C(n) n +#else +#define C(n) +#endif + +#define g_FILLER(symbol) 0 + +uchar oc_syms[MAXOCLASSES] = DUMMY; /* the current object display symbols */ +uchar showsyms[MAXPCHARS] = DUMMY; /* the current feature display symbols */ +uchar monsyms[MAXMCLASSES] = DUMMY; /* the current monster display symbols */ +uchar warnsyms[WARNCOUNT] = DUMMY; /* the current warning display symbols */ + +/* Default object class symbols. See objclass.h. */ +const char def_oc_syms[MAXOCLASSES] = { +/* 0*/ '\0', /* placeholder for the "random class" */ + ILLOBJ_SYM, + WEAPON_SYM, + ARMOR_SYM, + RING_SYM, +/* 5*/ AMULET_SYM, + TOOL_SYM, + FOOD_SYM, + POTION_SYM, + SCROLL_SYM, +/*10*/ SPBOOK_SYM, + WAND_SYM, + GOLD_SYM, + GEM_SYM, + ROCK_SYM, +/*15*/ BALL_SYM, + CHAIN_SYM, + VENOM_SYM +}; + +const char invisexplain[] = "remembered, unseen, creature"; + +/* Object descriptions. Used in do_look(). */ +const char * const objexplain[] = { /* these match def_oc_syms, above */ +/* 0*/ 0, + "strange object", + "weapon", + "suit or piece of armor", + "ring", +/* 5*/ "amulet", + "useful item (pick-axe, key, lamp...)", + "piece of food", + "potion", + "scroll", +/*10*/ "spellbook", + "wand", + "pile of coins", + "gem or rock", + "boulder or statue", +/*15*/ "iron ball", + "iron chain", + "splash of venom" +}; + +/* Object class names. Used in object_detect(). */ +const char * const oclass_names[] = { +/* 0*/ 0, + "illegal objects", + "weapons", + "armor", + "rings", +/* 5*/ "amulets", + "tools", + "food", + "potions", + "scrolls", +/*10*/ "spellbooks", + "wands", + "coins", + "rocks", + "large stones", +/*15*/ "iron balls", + "chains", + "venoms" +}; + +/* Default monster class symbols. See monsym.h. */ +const char def_monsyms[MAXMCLASSES] = { + '\0', /* holder */ + DEF_ANT, + DEF_BLOB, + DEF_COCKATRICE, + DEF_DOG, + DEF_EYE, + DEF_FELINE, + DEF_GREMLIN, + DEF_HUMANOID, + DEF_IMP, + DEF_JELLY, /* 10 */ + DEF_KOBOLD, + DEF_LEPRECHAUN, + DEF_MIMIC, + DEF_NYMPH, + DEF_ORC, + DEF_PIERCER, + DEF_QUADRUPED, + DEF_RODENT, + DEF_SPIDER, + DEF_TRAPPER, /* 20 */ + DEF_UNICORN, + DEF_VORTEX, + DEF_WORM, + DEF_XAN, + DEF_LIGHT, + DEF_ZRUTY, + DEF_ANGEL, + DEF_BAT, + DEF_CENTAUR, + DEF_DRAGON, /* 30 */ + DEF_ELEMENTAL, + DEF_FUNGUS, + DEF_GNOME, + DEF_GIANT, + '\0', + DEF_JABBERWOCK, + DEF_KOP, + DEF_LICH, + DEF_MUMMY, + DEF_NAGA, /* 40 */ + DEF_OGRE, + DEF_PUDDING, + DEF_QUANTMECH, + DEF_RUSTMONST, + DEF_SNAKE, + DEF_TROLL, + DEF_UMBER, + DEF_VAMPIRE, + DEF_WRAITH, + DEF_XORN, /* 50 */ + DEF_YETI, + DEF_ZOMBIE, + DEF_HUMAN, + DEF_GHOST, + DEF_GOLEM, + DEF_DEMON, + DEF_EEL, + DEF_LIZARD, + DEF_WORM_TAIL, + DEF_MIMIC_DEF, /* 60 */ +}; + +/* The explanations below are also used when the user gives a string + * for blessed genocide, so no text should wholly contain any later + * text. They should also always contain obvious names (eg. cat/feline). + */ +const char * const monexplain[MAXMCLASSES] = { + 0, + "ant or other insect", "blob", "cockatrice", + "dog or other canine", "eye or sphere", "cat or other feline", + "gremlin", "humanoid", "imp or minor demon", + "jelly", "kobold", "leprechaun", + "mimic", "nymph", "orc", + "piercer", "quadruped", "rodent", + "arachnid or centipede", "trapper or lurker above", "unicorn or horse", + "vortex", "worm", "xan or other mythical/fantastic insect", + "light", "zruty", + + "angelic being", "bat or bird", "centaur", + "dragon", "elemental", "fungus or mold", + "gnome", "giant humanoid", 0, + "jabberwock", "Keystone Kop", "lich", + "mummy", "naga", "ogre", + "pudding or ooze", "quantum mechanic", "rust monster or disenchanter", + "snake", "troll", "umber hulk", + "vampire", "wraith", "xorn", + "apelike creature", "zombie", + + "human or elf", "ghost", "golem", + "major demon", "sea monster", "lizard", + "long worm tail", "mimic" +}; + +const struct symdef def_warnsyms[WARNCOUNT] = { + {'0', "unknown creature causing you worry", C(CLR_WHITE)}, /* white warning */ + {'1', "unknown creature causing you concern", C(CLR_RED)}, /* pink warning */ + {'2', "unknown creature causing you anxiety", C(CLR_RED)}, /* red warning */ + {'3', "unknown creature causing you disquiet", C(CLR_RED)}, /* ruby warning */ + {'4', "unknown creature causing you alarm", + C(CLR_MAGENTA)}, /* purple warning */ + {'5', "unknown creature causing you dread", + C(CLR_BRIGHT_MAGENTA)} /* black warning */ +}; + +/* + * Default screen symbols with explanations and colors. + * Note: {ibm|dec|mac}_graphics[] arrays also depend on this symbol order. + */ +const struct symdef defsyms[MAXPCHARS] = { +/* 0*/ {' ', "dark part of a room",C(NO_COLOR)}, /* stone */ + {'|', "wall", C(CLR_GRAY)}, /* vwall */ + {'-', "wall", C(CLR_GRAY)}, /* hwall */ + {'-', "wall", C(CLR_GRAY)}, /* tlcorn */ + {'-', "wall", C(CLR_GRAY)}, /* trcorn */ + {'-', "wall", C(CLR_GRAY)}, /* blcorn */ + {'-', "wall", C(CLR_GRAY)}, /* brcorn */ + {'-', "wall", C(CLR_GRAY)}, /* crwall */ + {'-', "wall", C(CLR_GRAY)}, /* tuwall */ + {'-', "wall", C(CLR_GRAY)}, /* tdwall */ +/*10*/ {'|', "wall", C(CLR_GRAY)}, /* tlwall */ + {'|', "wall", C(CLR_GRAY)}, /* trwall */ + {'.', "doorway", C(CLR_GRAY)}, /* ndoor */ + {'-', "open door", C(CLR_BROWN)}, /* vodoor */ + {'|', "open door", C(CLR_BROWN)}, /* hodoor */ + {'+', "closed door", C(CLR_BROWN)}, /* vcdoor */ + {'+', "closed door", C(CLR_BROWN)}, /* hcdoor */ + {'#', "iron bars", C(HI_METAL)}, /* bars */ + {'#', "tree", C(CLR_GREEN)}, /* tree */ + {'.', "floor of a room",C(CLR_GRAY)}, /* room */ +/*20*/ {'#', "corridor", C(CLR_GRAY)}, /* dark corr */ + {'#', "lit corridor", C(CLR_GRAY)}, /* lit corr (see mapglyph.c) */ + {'<', "staircase up", C(CLR_GRAY)}, /* upstair */ + {'>', "staircase down", C(CLR_GRAY)}, /* dnstair */ + {'<', "ladder up", C(CLR_BROWN)}, /* upladder */ + {'>', "ladder down", C(CLR_BROWN)}, /* dnladder */ + {'_', "altar", C(CLR_GRAY)}, /* altar */ + {'|', "grave", C(CLR_GRAY)}, /* grave */ + {'\\', "opulent throne",C(HI_GOLD)}, /* throne */ +#ifdef SINKS + {'#', "sink", C(CLR_GRAY)}, /* sink */ +#else + {'#', "", C(CLR_GRAY)}, /* sink */ +#endif +/*30*/ {'{', "fountain", C(CLR_BLUE)}, /* fountain */ + {'}', "water", C(CLR_BLUE)}, /* pool */ + {'.', "ice", C(CLR_CYAN)}, /* ice */ + {'}', "molten lava", C(CLR_RED)}, /* lava */ + {'.', "lowered drawbridge",C(CLR_BROWN)}, /* vodbridge */ + {'.', "lowered drawbridge",C(CLR_BROWN)}, /* hodbridge */ + {'#', "raised drawbridge",C(CLR_BROWN)},/* vcdbridge */ + {'#', "raised drawbridge",C(CLR_BROWN)},/* hcdbridge */ + {' ', "air", C(CLR_CYAN)}, /* open air */ + {'#', "cloud", C(CLR_GRAY)}, /* [part of] a cloud */ +/*40*/ {'}', "water", C(CLR_BLUE)}, /* under water */ + {'^', "arrow trap", C(HI_METAL)}, /* trap */ + {'^', "dart trap", C(HI_METAL)}, /* trap */ + {'^', "falling rock trap",C(CLR_GRAY)}, /* trap */ + {'^', "squeaky board", C(CLR_BROWN)}, /* trap */ + {'^', "bear trap", C(HI_METAL)}, /* trap */ + {'^', "land mine", C(CLR_RED)}, /* trap */ + {'^', "rolling boulder trap", C(CLR_GRAY)}, /* trap */ + {'^', "sleeping gas trap",C(HI_ZAP)}, /* trap */ + {'^', "rust trap", C(CLR_BLUE)}, /* trap */ +/*50*/ {'^', "fire trap", C(CLR_ORANGE)}, /* trap */ + {'^', "pit", C(CLR_BLACK)}, /* trap */ + {'^', "spiked pit", C(CLR_BLACK)}, /* trap */ + {'^', "hole", C(CLR_BROWN)}, /* trap */ + {'^', "trap door", C(CLR_BROWN)}, /* trap */ + {'^', "teleportation trap", C(CLR_MAGENTA)}, /* trap */ + {'^', "level teleporter", C(CLR_MAGENTA)}, /* trap */ + {'^', "magic portal", C(CLR_BRIGHT_MAGENTA)}, /* trap */ + {'"', "web", C(CLR_GRAY)}, /* web */ + {'^', "statue trap", C(CLR_GRAY)}, /* trap */ +/*60*/ {'^', "magic trap", C(HI_ZAP)}, /* trap */ + {'^', "anti-magic field", C(HI_ZAP)}, /* trap */ + {'^', "polymorph trap", C(CLR_BRIGHT_GREEN)}, /* trap */ + {'|', "wall", C(CLR_GRAY)}, /* vbeam */ + {'-', "wall", C(CLR_GRAY)}, /* hbeam */ + {'\\',"wall", C(CLR_GRAY)}, /* lslant */ + {'/', "wall", C(CLR_GRAY)}, /* rslant */ + {'*', "", C(CLR_WHITE)}, /* dig beam */ + {'!', "", C(CLR_WHITE)}, /* camera flash beam */ + {')', "", C(HI_WOOD)}, /* boomerang open left */ +/*70*/ {'(', "", C(HI_WOOD)}, /* boomerang open right */ + {'0', "", C(HI_ZAP)}, /* 4 magic shield symbols */ + {'#', "", C(HI_ZAP)}, + {'@', "", C(HI_ZAP)}, + {'*', "", C(HI_ZAP)}, + {'/', "", C(CLR_GREEN)}, /* swallow top left */ + {'-', "", C(CLR_GREEN)}, /* swallow top center */ + {'\\', "", C(CLR_GREEN)}, /* swallow top right */ + {'|', "", C(CLR_GREEN)}, /* swallow middle left */ + {'|', "", C(CLR_GREEN)}, /* swallow middle right */ +/*80*/ {'\\', "", C(CLR_GREEN)}, /* swallow bottom left */ + {'-', "", C(CLR_GREEN)}, /* swallow bottom center*/ + {'/', "", C(CLR_GREEN)}, /* swallow bottom right */ + {'/', "", C(CLR_ORANGE)}, /* explosion top left */ + {'-', "", C(CLR_ORANGE)}, /* explosion top center */ + {'\\', "", C(CLR_ORANGE)}, /* explosion top right */ + {'|', "", C(CLR_ORANGE)}, /* explosion middle left */ + {' ', "", C(CLR_ORANGE)}, /* explosion middle center*/ + {'|', "", C(CLR_ORANGE)}, /* explosion middle right */ + {'\\', "", C(CLR_ORANGE)}, /* explosion bottom left */ +/*90*/ {'-', "", C(CLR_ORANGE)}, /* explosion bottom center*/ + {'/', "", C(CLR_ORANGE)}, /* explosion bottom right */ +/* + * Note: Additions to this array should be reflected in the + * {ibm,dec,mac}_graphics[] arrays below. + */ +}; + +#undef C + +#ifdef ASCIIGRAPH + +#ifdef PC9800 +void NDECL((*ibmgraphics_mode_callback)) = 0; /* set in tty_start_screen() */ +#endif /* PC9800 */ + +static uchar ibm_graphics[MAXPCHARS] = { +/* 0*/ g_FILLER(S_stone), + 0xb3, /* S_vwall: meta-3, vertical rule */ + 0xc4, /* S_hwall: meta-D, horizontal rule */ + 0xda, /* S_tlcorn: meta-Z, top left corner */ + 0xbf, /* S_trcorn: meta-?, top right corner */ + 0xc0, /* S_blcorn: meta-@, bottom left */ + 0xd9, /* S_brcorn: meta-Y, bottom right */ + 0xc5, /* S_crwall: meta-E, cross */ + 0xc1, /* S_tuwall: meta-A, T up */ + 0xc2, /* S_tdwall: meta-B, T down */ +/*10*/ 0xb4, /* S_tlwall: meta-4, T left */ + 0xc3, /* S_trwall: meta-C, T right */ + 0xfa, /* S_ndoor: meta-z, centered dot */ + 0xfe, /* S_vodoor: meta-~, small centered square */ + 0xfe, /* S_hodoor: meta-~, small centered square */ + g_FILLER(S_vcdoor), + g_FILLER(S_hcdoor), + 240, /* S_bars: equivalence symbol */ + 241, /* S_tree: plus or minus symbol */ + 0xfa, /* S_room: meta-z, centered dot */ +/*20*/ 0xb0, /* S_corr: meta-0, light shading */ + 0xb1, /* S_litcorr: meta-1, medium shading */ + g_FILLER(S_upstair), + g_FILLER(S_dnstair), + g_FILLER(S_upladder), + g_FILLER(S_dnladder), + g_FILLER(S_altar), + g_FILLER(S_grave), + g_FILLER(S_throne), + g_FILLER(S_sink), +/*30*/ 0xf4, /* S_fountain: meta-t, integral top half */ + 0xf7, /* S_pool: meta-w, approx. equals */ + 0xfa, /* S_ice: meta-z, centered dot */ + 0xf7, /* S_lava: meta-w, approx. equals */ + 0xfa, /* S_vodbridge: meta-z, centered dot */ + 0xfa, /* S_hodbridge: meta-z, centered dot */ + g_FILLER(S_vcdbridge), + g_FILLER(S_hcdbridge), + g_FILLER(S_air), + g_FILLER(S_cloud), +/*40*/ 0xf7, /* S_water: meta-w, approx. equals */ + g_FILLER(S_arrow_trap), + g_FILLER(S_dart_trap), + g_FILLER(S_falling_rock_trap), + g_FILLER(S_squeaky_board), + g_FILLER(S_bear_trap), + g_FILLER(S_land_mine), + g_FILLER(S_rolling_boulder_trap), + g_FILLER(S_sleeping_gas_trap), + g_FILLER(S_rust_trap), +/*50*/ g_FILLER(S_fire_trap), + g_FILLER(S_pit), + g_FILLER(S_spiked_pit), + g_FILLER(S_hole), + g_FILLER(S_trap_door), + g_FILLER(S_teleportation_trap), + g_FILLER(S_level_teleporter), + g_FILLER(S_magic_portal), + g_FILLER(S_web), + g_FILLER(S_statue_trap), +/*60*/ g_FILLER(S_magic_trap), + g_FILLER(S_anti_magic_trap), + g_FILLER(S_polymorph_trap), + 0xb3, /* S_vbeam: meta-3, vertical rule */ + 0xc4, /* S_hbeam: meta-D, horizontal rule */ + g_FILLER(S_lslant), + g_FILLER(S_rslant), + g_FILLER(S_digbeam), + g_FILLER(S_flashbeam), + g_FILLER(S_boomleft), +/*70*/ g_FILLER(S_boomright), + g_FILLER(S_ss1), + g_FILLER(S_ss2), + g_FILLER(S_ss3), + g_FILLER(S_ss4), + g_FILLER(S_sw_tl), + g_FILLER(S_sw_tc), + g_FILLER(S_sw_tr), + 0xb3, /* S_sw_ml: meta-3, vertical rule */ + 0xb3, /* S_sw_mr: meta-3, vertical rule */ +/*80*/ g_FILLER(S_sw_bl), + g_FILLER(S_sw_bc), + g_FILLER(S_sw_br), + g_FILLER(S_explode1), + g_FILLER(S_explode2), + g_FILLER(S_explode3), + 0xb3, /* S_explode4: meta-3, vertical rule */ + g_FILLER(S_explode5), + 0xb3, /* S_explode6: meta-3, vertical rule */ + g_FILLER(S_explode7), +/*90*/ g_FILLER(S_explode8), + g_FILLER(S_explode9) +}; +#endif /* ASCIIGRAPH */ + +#ifdef TERMLIB +void NDECL((*decgraphics_mode_callback)) = 0; /* set in tty_start_screen() */ + +static uchar dec_graphics[MAXPCHARS] = { +/* 0*/ g_FILLER(S_stone), + 0xf8, /* S_vwall: meta-x, vertical rule */ + 0xf1, /* S_hwall: meta-q, horizontal rule */ + 0xec, /* S_tlcorn: meta-l, top left corner */ + 0xeb, /* S_trcorn: meta-k, top right corner */ + 0xed, /* S_blcorn: meta-m, bottom left */ + 0xea, /* S_brcorn: meta-j, bottom right */ + 0xee, /* S_crwall: meta-n, cross */ + 0xf6, /* S_tuwall: meta-v, T up */ + 0xf7, /* S_tdwall: meta-w, T down */ +/*10*/ 0xf5, /* S_tlwall: meta-u, T left */ + 0xf4, /* S_trwall: meta-t, T right */ + 0xfe, /* S_ndoor: meta-~, centered dot */ + 0xe1, /* S_vodoor: meta-a, solid block */ + 0xe1, /* S_hodoor: meta-a, solid block */ + g_FILLER(S_vcdoor), + g_FILLER(S_hcdoor), + 0xfb, /* S_bars: meta-{, small pi */ + 0xe7, /* S_tree: meta-g, plus-or-minus */ + 0xfe, /* S_room: meta-~, centered dot */ +/*20*/ g_FILLER(S_corr), + g_FILLER(S_litcorr), + g_FILLER(S_upstair), + g_FILLER(S_dnstair), + 0xf9, /* S_upladder: meta-y, greater-than-or-equals */ + 0xfa, /* S_dnladder: meta-z, less-than-or-equals */ + g_FILLER(S_altar), /* 0xc3, \E)3: meta-C, dagger */ + g_FILLER(S_grave), + g_FILLER(S_throne), + g_FILLER(S_sink), +/*30*/ g_FILLER(S_fountain), /* 0xdb, \E)3: meta-[, integral top half */ + 0xe0, /* S_pool: meta-\, diamond */ + 0xfe, /* S_ice: meta-~, centered dot */ + 0xe0, /* S_lava: meta-\, diamond */ + 0xfe, /* S_vodbridge: meta-~, centered dot */ + 0xfe, /* S_hodbridge: meta-~, centered dot */ + g_FILLER(S_vcdbridge), + g_FILLER(S_hcdbridge), + g_FILLER(S_air), + g_FILLER(S_cloud), +/*40*/ 0xe0, /* S_water: meta-\, diamond */ + g_FILLER(S_arrow_trap), + g_FILLER(S_dart_trap), + g_FILLER(S_falling_rock_trap), + g_FILLER(S_squeaky_board), + g_FILLER(S_bear_trap), + g_FILLER(S_land_mine), + g_FILLER(S_rolling_boulder_trap), + g_FILLER(S_sleeping_gas_trap), + g_FILLER(S_rust_trap), +/*50*/ g_FILLER(S_fire_trap), + g_FILLER(S_pit), + g_FILLER(S_spiked_pit), + g_FILLER(S_hole), + g_FILLER(S_trap_door), + g_FILLER(S_teleportation_trap), + g_FILLER(S_level_teleporter), + g_FILLER(S_magic_portal), + g_FILLER(S_web), /* 0xbd, \E)3: meta-=, int'l currency */ + g_FILLER(S_statue_trap), +/*60*/ g_FILLER(S_magic_trap), + g_FILLER(S_anti_magic_trap), + g_FILLER(S_polymorph_trap), + 0xf8, /* S_vbeam: meta-x, vertical rule */ + 0xf1, /* S_hbeam: meta-q, horizontal rule */ + g_FILLER(S_lslant), + g_FILLER(S_rslant), + g_FILLER(S_digbeam), + g_FILLER(S_flashbeam), + g_FILLER(S_boomleft), +/*70*/ g_FILLER(S_boomright), + g_FILLER(S_ss1), + g_FILLER(S_ss2), + g_FILLER(S_ss3), + g_FILLER(S_ss4), + g_FILLER(S_sw_tl), + 0xef, /* S_sw_tc: meta-o, high horizontal line */ + g_FILLER(S_sw_tr), + 0xf8, /* S_sw_ml: meta-x, vertical rule */ + 0xf8, /* S_sw_mr: meta-x, vertical rule */ +/*80*/ g_FILLER(S_sw_bl), + 0xf3, /* S_sw_bc: meta-s, low horizontal line */ + g_FILLER(S_sw_br), + g_FILLER(S_explode1), + 0xef, /* S_explode2: meta-o, high horizontal line */ + g_FILLER(S_explode3), + 0xf8, /* S_explode4: meta-x, vertical rule */ + g_FILLER(S_explode5), + 0xf8, /* S_explode6: meta-x, vertical rule */ + g_FILLER(S_explode7), +/*90*/ 0xf3, /* S_explode8: meta-s, low horizontal line */ + g_FILLER(S_explode9) +}; +#endif /* TERMLIB */ + +#ifdef MAC_GRAPHICS_ENV +static uchar mac_graphics[MAXPCHARS] = { +/* 0*/ g_FILLER(S_stone), + 0xba, /* S_vwall */ + 0xcd, /* S_hwall */ + 0xc9, /* S_tlcorn */ + 0xbb, /* S_trcorn */ + 0xc8, /* S_blcorn */ + 0xbc, /* S_brcorn */ + 0xce, /* S_crwall */ + 0xca, /* S_tuwall */ + 0xcb, /* S_tdwall */ +/*10*/ 0xb9, /* S_tlwall */ + 0xcc, /* S_trwall */ + 0xb0, /* S_ndoor */ + 0xee, /* S_vodoor */ + 0xee, /* S_hodoor */ + 0xef, /* S_vcdoor */ + 0xef, /* S_hcdoor */ + 0xf0, /* S_bars: equivalency symbol */ + 0xf1, /* S_tree: plus-or-minus */ + g_FILLER(S_Room), +/*20*/ 0xB0, /* S_corr */ + g_FILLER(S_litcorr), + g_FILLER(S_upstair), + g_FILLER(S_dnstair), + g_FILLER(S_upladder), + g_FILLER(S_dnladder), + g_FILLER(S_altar), + 0xef, /* S_grave: same as open door */ + g_FILLER(S_throne), + g_FILLER(S_sink), +/*30*/ g_FILLER(S_fountain), + 0xe0, /* S_pool */ + g_FILLER(S_ice), + g_FILLER(S_lava), + g_FILLER(S_vodbridge), + g_FILLER(S_hodbridge), + g_FILLER(S_vcdbridge), + g_FILLER(S_hcdbridge), + g_FILLER(S_air), + g_FILLER(S_cloud), +/*40*/ g_FILLER(S_water), + g_FILLER(S_arrow_trap), + g_FILLER(S_dart_trap), + g_FILLER(S_falling_rock_trap), + g_FILLER(S_squeaky_board), + g_FILLER(S_bear_trap), + g_FILLER(S_land_mine), + g_FILLER(S_rolling_boulder_trap), + g_FILLER(S_sleeping_gas_trap), + g_FILLER(S_rust_trap), +/*50*/ g_FILLER(S_fire_trap), + g_FILLER(S_pit), + g_FILLER(S_spiked_pit), + g_FILLER(S_hole), + g_FILLER(S_trap_door), + g_FILLER(S_teleportation_trap), + g_FILLER(S_level_teleporter), + g_FILLER(S_magic_portal), + g_FILLER(S_web), + g_FILLER(S_statue_trap), +/*60*/ g_FILLER(S_magic_trap), + g_FILLER(S_anti_magic_trap), + g_FILLER(S_polymorph_trap), + g_FILLER(S_vbeam), + g_FILLER(S_hbeam), + g_FILLER(S_lslant), + g_FILLER(S_rslant), + g_FILLER(S_digbeam), + g_FILLER(S_flashbeam), + g_FILLER(S_boomleft), +/*70*/ g_FILLER(S_boomright), + g_FILLER(S_ss1), + g_FILLER(S_ss2), + g_FILLER(S_ss3), + g_FILLER(S_ss4), + g_FILLER(S_sw_tl), + g_FILLER(S_sw_tc), + g_FILLER(S_sw_tr), + g_FILLER(S_sw_ml), + g_FILLER(S_sw_mr), +/*80*/ g_FILLER(S_sw_bl), + g_FILLER(S_sw_bc), + g_FILLER(S_sw_br), + g_FILLER(S_explode1), + g_FILLER(S_explode2), + g_FILLER(S_explode3), + g_FILLER(S_explode4), + g_FILLER(S_explode5), + g_FILLER(S_explode6), + g_FILLER(S_explode7), +/*90*/ g_FILLER(S_explode8), + g_FILLER(S_explode9) +}; +#endif /* MAC_GRAPHICS_ENV */ + +#ifdef PC9800 +void NDECL((*ascgraphics_mode_callback)) = 0; /* set in tty_start_screen() */ +#endif + +/* + * Convert the given character to an object class. If the character is not + * recognized, then MAXOCLASSES is returned. Used in detect.c invent.c, + * options.c, pickup.c, sp_lev.c, and lev_main.c. + */ +int +def_char_to_objclass(ch) + char ch; +{ + int i; + for (i = 1; i < MAXOCLASSES; i++) + if (ch == def_oc_syms[i]) break; + return i; +} + +/* + * Convert a character into a monster class. This returns the _first_ + * match made. If there are are no matches, return MAXMCLASSES. + */ +int +def_char_to_monclass(ch) + char ch; +{ + int i; + for (i = 1; i < MAXMCLASSES; i++) + if (def_monsyms[i] == ch) break; + return i; +} + +void +assign_graphics(graph_chars, glth, maxlen, offset) +register uchar *graph_chars; +int glth, maxlen, offset; +{ + register int i; + + for (i = 0; i < maxlen; i++) + showsyms[i+offset] = (((i < glth) && graph_chars[i]) ? + graph_chars[i] : defsyms[i+offset].sym); +} + +void +switch_graphics(gr_set_flag) +int gr_set_flag; +{ + switch (gr_set_flag) { + default: + case ASCII_GRAPHICS: + assign_graphics((uchar *)0, 0, MAXPCHARS, 0); +#ifdef PC9800 + if (ascgraphics_mode_callback) (*ascgraphics_mode_callback)(); +#endif + break; +#ifdef ASCIIGRAPH + case IBM_GRAPHICS: +/* + * Use the nice IBM Extended ASCII line-drawing characters (codepage 437). + * + * OS/2 defaults to a multilingual character set (codepage 850, corresponding + * to the ISO 8859 character set. We should probably do a VioSetCp() call to + * set the codepage to 437. + */ + iflags.IBMgraphics = TRUE; + iflags.DECgraphics = FALSE; + assign_graphics(ibm_graphics, SIZE(ibm_graphics), MAXPCHARS, 0); +#ifdef PC9800 + if (ibmgraphics_mode_callback) (*ibmgraphics_mode_callback)(); +#endif + break; +#endif /* ASCIIGRAPH */ +#ifdef TERMLIB + case DEC_GRAPHICS: +/* + * Use the VT100 line drawing character set. + */ + iflags.DECgraphics = TRUE; + iflags.IBMgraphics = FALSE; + assign_graphics(dec_graphics, SIZE(dec_graphics), MAXPCHARS, 0); + if (decgraphics_mode_callback) (*decgraphics_mode_callback)(); + break; +#endif /* TERMLIB */ +#ifdef MAC_GRAPHICS_ENV + case MAC_GRAPHICS: + assign_graphics(mac_graphics, SIZE(mac_graphics), MAXPCHARS, 0); + break; +#endif + } + return; +} + + +#ifdef REINCARNATION + +/* + * saved display symbols for objects & features. + */ +static uchar save_oc_syms[MAXOCLASSES] = DUMMY; +static uchar save_showsyms[MAXPCHARS] = DUMMY; +static uchar save_monsyms[MAXPCHARS] = DUMMY; + +static const uchar r_oc_syms[MAXOCLASSES] = { +/* 0*/ '\0', + ILLOBJ_SYM, + WEAPON_SYM, + ']', /* armor */ + RING_SYM, +/* 5*/ ',', /* amulet */ + TOOL_SYM, + ':', /* food */ + POTION_SYM, + SCROLL_SYM, +/*10*/ SPBOOK_SYM, + WAND_SYM, + GEM_SYM, /* gold -- yes it's the same as gems */ + GEM_SYM, + ROCK_SYM, +/*15*/ BALL_SYM, + CHAIN_SYM, + VENOM_SYM +}; + +# ifdef ASCIIGRAPH +/* Rogue level graphics. Under IBM graphics mode, use the symbols that were + * used for Rogue on the IBM PC. Unfortunately, this can't be completely + * done because some of these are control characters--armor and rings under + * DOS, and a whole bunch of them under Linux. Use the TTY Rogue characters + * for those cases. + */ +static const uchar IBM_r_oc_syms[MAXOCLASSES] = { /* a la EPYX Rogue */ +/* 0*/ '\0', + ILLOBJ_SYM, +# if defined(MSDOS) || defined(OS2) || ( defined(WIN32) && !defined(MSWIN_GRAPHICS) ) + 0x18, /* weapon: up arrow */ +/* 0x0a, */ ARMOR_SYM, /* armor: Vert rect with o */ +/* 0x09, */ RING_SYM, /* ring: circle with arrow */ +/* 5*/ 0x0c, /* amulet: "female" symbol */ + TOOL_SYM, + 0x05, /* food: club (as in cards) */ + 0xad, /* potion: upside down '!' */ + 0x0e, /* scroll: musical note */ +/*10*/ SPBOOK_SYM, + 0xe7, /* wand: greek tau */ + 0x0f, /* gold: yes it's the same as gems */ + 0x0f, /* gems: fancy '*' */ +# else + ')', /* weapon */ + ARMOR_SYM, /* armor */ + RING_SYM, /* ring */ +/* 5*/ ',', /* amulet */ + TOOL_SYM, + ':', /* food */ + 0xad, /* potion: upside down '!' */ + SCROLL_SYM, /* scroll */ +/*10*/ SPBOOK_SYM, + 0xe7, /* wand: greek tau */ + GEM_SYM, /* gold: yes it's the same as gems */ + GEM_SYM, /* gems */ +# endif + ROCK_SYM, +/*15*/ BALL_SYM, + CHAIN_SYM, + VENOM_SYM +}; +# endif /* ASCIIGRAPH */ + +void +assign_rogue_graphics(is_rlevel) +boolean is_rlevel; +{ + /* Adjust graphics display characters on Rogue levels */ + + if (is_rlevel) { + register int i; + + (void) memcpy((genericptr_t)save_showsyms, + (genericptr_t)showsyms, sizeof showsyms); + (void) memcpy((genericptr_t)save_oc_syms, + (genericptr_t)oc_syms, sizeof oc_syms); + (void) memcpy((genericptr_t)save_monsyms, + (genericptr_t)monsyms, sizeof monsyms); + + /* Use a loop: char != uchar on some machines. */ + for (i = 0; i < MAXMCLASSES; i++) + monsyms[i] = def_monsyms[i]; +# if defined(ASCIIGRAPH) && !defined(MSWIN_GRAPHICS) + if (iflags.IBMgraphics +# if defined(USE_TILES) && defined(MSDOS) + && !iflags.grmode +# endif + ) + monsyms[S_HUMAN] = 0x01; /* smiley face */ +# endif + for (i = 0; i < MAXPCHARS; i++) + showsyms[i] = defsyms[i].sym; + +/* + * Some day if these rogue showsyms get much more extensive than this, + * we may want to create r_showsyms, and IBM_r_showsyms arrays to hold + * all of this info and to simply initialize it via a for() loop like r_oc_syms. + */ + +# ifdef ASCIIGRAPH + if (!iflags.IBMgraphics +# if defined(USE_TILES) && defined(MSDOS) + || iflags.grmode +# endif + ) { +# endif + showsyms[S_vodoor] = showsyms[S_hodoor] = showsyms[S_ndoor] = '+'; + showsyms[S_upstair] = showsyms[S_dnstair] = '%'; +# ifdef ASCIIGRAPH + } else { + /* a la EPYX Rogue */ + showsyms[S_vwall] = 0xba; /* all walls now use */ + showsyms[S_hwall] = 0xcd; /* double line graphics */ + showsyms[S_tlcorn] = 0xc9; + showsyms[S_trcorn] = 0xbb; + showsyms[S_blcorn] = 0xc8; + showsyms[S_brcorn] = 0xbc; + showsyms[S_crwall] = 0xce; + showsyms[S_tuwall] = 0xca; + showsyms[S_tdwall] = 0xcb; + showsyms[S_tlwall] = 0xb9; + showsyms[S_trwall] = 0xcc; + showsyms[S_ndoor] = 0xce; + showsyms[S_vodoor] = 0xce; + showsyms[S_hodoor] = 0xce; + showsyms[S_room] = 0xfa; /* centered dot */ + showsyms[S_corr] = 0xb1; + showsyms[S_litcorr] = 0xb2; + showsyms[S_upstair] = 0xf0; /* Greek Xi */ + showsyms[S_dnstair] = 0xf0; +#ifndef MSWIN_GRAPHICS + showsyms[S_arrow_trap] = 0x04; /* diamond (cards) */ + showsyms[S_dart_trap] = 0x04; + showsyms[S_falling_rock_trap] = 0x04; + showsyms[S_squeaky_board] = 0x04; + showsyms[S_bear_trap] = 0x04; + showsyms[S_land_mine] = 0x04; + showsyms[S_rolling_boulder_trap] = 0x04; + showsyms[S_sleeping_gas_trap] = 0x04; + showsyms[S_rust_trap] = 0x04; + showsyms[S_fire_trap] = 0x04; + showsyms[S_pit] = 0x04; + showsyms[S_spiked_pit] = 0x04; + showsyms[S_hole] = 0x04; + showsyms[S_trap_door] = 0x04; + showsyms[S_teleportation_trap] = 0x04; + showsyms[S_level_teleporter] = 0x04; + showsyms[S_magic_portal] = 0x04; + showsyms[S_web] = 0x04; + showsyms[S_statue_trap] = 0x04; + showsyms[S_magic_trap] = 0x04; + showsyms[S_anti_magic_trap] = 0x04; + showsyms[S_polymorph_trap] = 0x04; +#endif + } +#endif /* ASCIIGRAPH */ + + for (i = 0; i < MAXOCLASSES; i++) { +#ifdef ASCIIGRAPH + if (iflags.IBMgraphics +# if defined(USE_TILES) && defined(MSDOS) + && !iflags.grmode +# endif + ) + oc_syms[i] = IBM_r_oc_syms[i]; + else +#endif /* ASCIIGRAPH */ + oc_syms[i] = r_oc_syms[i]; + } +#if defined(MSDOS) && defined(USE_TILES) + if (iflags.grmode) tileview(FALSE); +#endif + } else { + (void) memcpy((genericptr_t)showsyms, + (genericptr_t)save_showsyms, sizeof showsyms); + (void) memcpy((genericptr_t)oc_syms, + (genericptr_t)save_oc_syms, sizeof oc_syms); + (void) memcpy((genericptr_t)monsyms, + (genericptr_t)save_monsyms, sizeof monsyms); +#if defined(MSDOS) && defined(USE_TILES) + if (iflags.grmode) tileview(TRUE); +#endif + } +} +#endif /* REINCARNATION */ + +/*drawing.c*/ diff --git a/src/dungeon.c b/src/dungeon.c new file mode 100644 index 0000000..b87b8bb --- /dev/null +++ b/src/dungeon.c @@ -0,0 +1,1744 @@ +/* SCCS Id: @(#)dungeon.c 3.4 1999/10/30 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dgn_file.h" +#include "dlb.h" + +#ifdef OVL1 + +#define DUNGEON_FILE "dungeon" + +#define X_START "x-strt" +#define X_LOCATE "x-loca" +#define X_GOAL "x-goal" + +struct proto_dungeon { + struct tmpdungeon tmpdungeon[MAXDUNGEON]; + struct tmplevel tmplevel[LEV_LIMIT]; + s_level *final_lev[LEV_LIMIT]; /* corresponding level pointers */ + struct tmpbranch tmpbranch[BRANCH_LIMIT]; + + int start; /* starting index of current dungeon sp levels */ + int n_levs; /* number of tmplevel entries */ + int n_brs; /* number of tmpbranch entries */ +}; + +int n_dgns; /* number of dungeons (used here, */ + /* and mklev.c) */ +static branch *branches = (branch *) 0; /* dungeon branch list */ + +struct lchoice { + int idx; + schar lev[MAXLINFO]; + schar playerlev[MAXLINFO]; + xchar dgn[MAXLINFO]; + char menuletter; +}; + +static void FDECL(Fread, (genericptr_t, int, int, dlb *)); +STATIC_DCL xchar FDECL(dname_to_dnum, (const char *)); +STATIC_DCL int FDECL(find_branch, (const char *, struct proto_dungeon *)); +STATIC_DCL xchar FDECL(parent_dnum, (const char *, struct proto_dungeon *)); +STATIC_DCL int FDECL(level_range, (XCHAR_P,int,int,int,struct proto_dungeon *,int *)); +STATIC_DCL xchar FDECL(parent_dlevel, (const char *, struct proto_dungeon *)); +STATIC_DCL int FDECL(correct_branch_type, (struct tmpbranch *)); +STATIC_DCL branch *FDECL(add_branch, (int, int, struct proto_dungeon *)); +STATIC_DCL void FDECL(add_level, (s_level *)); +STATIC_DCL void FDECL(init_level, (int,int,struct proto_dungeon *)); +STATIC_DCL int FDECL(possible_places, (int, boolean *, struct proto_dungeon *)); +STATIC_DCL xchar FDECL(pick_level, (boolean *, int)); +STATIC_DCL boolean FDECL(place_level, (int, struct proto_dungeon *)); +#ifdef WIZARD +STATIC_DCL const char *FDECL(br_string, (int)); +STATIC_DCL void FDECL(print_branch, (winid, int, int, int, BOOLEAN_P, struct lchoice *)); +#endif + +#ifdef DEBUG +#define DD dungeons[i] +STATIC_DCL void NDECL(dumpit); + +STATIC_OVL void +dumpit() +{ + int i; + s_level *x; + branch *br; + + for(i = 0; i < n_dgns; i++) { + fprintf(stderr, "\n#%d \"%s\" (%s):\n", i, + DD.dname, DD.proto); + fprintf(stderr, " num_dunlevs %d, dunlev_ureached %d\n", + DD.num_dunlevs, DD.dunlev_ureached); + fprintf(stderr, " depth_start %d, ledger_start %d\n", + DD.depth_start, DD.ledger_start); + fprintf(stderr, " flags:%s%s%s\n", + DD.flags.rogue_like ? " rogue_like" : "", + DD.flags.maze_like ? " maze_like" : "", + DD.flags.hellish ? " hellish" : ""); + getchar(); + } + fprintf(stderr,"\nSpecial levels:\n"); + for(x = sp_levchn; x; x = x->next) { + fprintf(stderr, "%s (%d): ", x->proto, x->rndlevs); + fprintf(stderr, "on %d, %d; ", x->dlevel.dnum, x->dlevel.dlevel); + fprintf(stderr, "flags:%s%s%s%s\n", + x->flags.rogue_like ? " rogue_like" : "", + x->flags.maze_like ? " maze_like" : "", + x->flags.hellish ? " hellish" : "", + x->flags.town ? " town" : ""); + getchar(); + } + fprintf(stderr,"\nBranches:\n"); + for (br = branches; br; br = br->next) { + fprintf(stderr, "%d: %s, end1 %d %d, end2 %d %d, %s\n", + br->id, + br->type == BR_STAIR ? "stair" : + br->type == BR_NO_END1 ? "no end1" : + br->type == BR_NO_END2 ? "no end2" : + br->type == BR_PORTAL ? "portal" : + "unknown", + br->end1.dnum, br->end1.dlevel, + br->end2.dnum, br->end2.dlevel, + br->end1_up ? "end1 up" : "end1 down"); + } + getchar(); + fprintf(stderr,"\nDone\n"); + getchar(); +} +#endif + +/* Save the dungeon structures. */ +void +save_dungeon(fd, perform_write, free_data) + int fd; + boolean perform_write, free_data; +{ + branch *curr, *next; + int count; + + if (perform_write) { + bwrite(fd, (genericptr_t) &n_dgns, sizeof n_dgns); + bwrite(fd, (genericptr_t) dungeons, sizeof(dungeon) * (unsigned)n_dgns); + bwrite(fd, (genericptr_t) &dungeon_topology, sizeof dungeon_topology); + bwrite(fd, (genericptr_t) tune, sizeof tune); + + for (count = 0, curr = branches; curr; curr = curr->next) + count++; + bwrite(fd, (genericptr_t) &count, sizeof(count)); + + for (curr = branches; curr; curr = curr->next) + bwrite(fd, (genericptr_t) curr, sizeof (branch)); + + count = maxledgerno(); + bwrite(fd, (genericptr_t) &count, sizeof count); + bwrite(fd, (genericptr_t) level_info, + (unsigned)count * sizeof (struct linfo)); + bwrite(fd, (genericptr_t) &inv_pos, sizeof inv_pos); + } + + if (free_data) { + for (curr = branches; curr; curr = next) { + next = curr->next; + free((genericptr_t) curr); + } + branches = 0; + } +} + +/* Restore the dungeon structures. */ +void +restore_dungeon(fd) + int fd; +{ + branch *curr, *last; + int count, i; + + mread(fd, (genericptr_t) &n_dgns, sizeof(n_dgns)); + mread(fd, (genericptr_t) dungeons, sizeof(dungeon) * (unsigned)n_dgns); + mread(fd, (genericptr_t) &dungeon_topology, sizeof dungeon_topology); + mread(fd, (genericptr_t) tune, sizeof tune); + + last = branches = (branch *) 0; + + mread(fd, (genericptr_t) &count, sizeof(count)); + for (i = 0; i < count; i++) { + curr = (branch *) alloc(sizeof(branch)); + mread(fd, (genericptr_t) curr, sizeof(branch)); + curr->next = (branch *) 0; + if (last) + last->next = curr; + else + branches = curr; + last = curr; + } + + mread(fd, (genericptr_t) &count, sizeof(count)); + if (count >= MAXLINFO) + panic("level information count larger (%d) than allocated size", count); + mread(fd, (genericptr_t) level_info, (unsigned)count*sizeof(struct linfo)); + mread(fd, (genericptr_t) &inv_pos, sizeof inv_pos); +} + +static void +Fread(ptr, size, nitems, stream) + genericptr_t ptr; + int size, nitems; + dlb *stream; +{ + int cnt; + + if((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) { + panic( + "Premature EOF on dungeon description file!\r\nExpected %d bytes - got %d.", + (size * nitems), (size * cnt)); + terminate(EXIT_FAILURE); + } +} + +STATIC_OVL xchar +dname_to_dnum(s) +const char *s; +{ + xchar i; + + for (i = 0; i < n_dgns; i++) + if (!strcmp(dungeons[i].dname, s)) return i; + + panic("Couldn't resolve dungeon number for name \"%s\".", s); + /*NOT REACHED*/ + return (xchar)0; +} + +s_level * +find_level(s) + const char *s; +{ + s_level *curr; + for(curr = sp_levchn; curr; curr = curr->next) + if (!strcmpi(s, curr->proto)) break; + return curr; +} + +/* Find the branch that links the named dungeon. */ +STATIC_OVL int +find_branch(s, pd) + const char *s; /* dungeon name */ + struct proto_dungeon *pd; +{ + int i; + + if (pd) { + for (i = 0; i < pd->n_brs; i++) + if (!strcmp(pd->tmpbranch[i].name, s)) break; + if (i == pd->n_brs) panic("find_branch: can't find %s", s); + } else { + /* support for level tport by name */ + branch *br; + const char *dnam; + + for (br = branches; br; br = br->next) { + dnam = dungeons[br->end2.dnum].dname; + if (!strcmpi(dnam, s) || + (!strncmpi(dnam, "The ", 4) && !strcmpi(dnam + 4, s))) + break; + } + i = br ? ((ledger_no(&br->end1) << 8) | ledger_no(&br->end2)) : -1; + } + return i; +} + + +/* + * Find the "parent" by searching the prototype branch list for the branch + * listing, then figuring out to which dungeon it belongs. + */ +STATIC_OVL xchar +parent_dnum(s, pd) +const char *s; /* dungeon name */ +struct proto_dungeon *pd; +{ + int i; + xchar pdnum; + + i = find_branch(s, pd); + /* + * Got branch, now find parent dungeon. Stop if we have reached + * "this" dungeon (if we haven't found it by now it is an error). + */ + for (pdnum = 0; strcmp(pd->tmpdungeon[pdnum].name, s); pdnum++) + if ((i -= pd->tmpdungeon[pdnum].branches) < 0) + return(pdnum); + + panic("parent_dnum: couldn't resolve branch."); + /*NOT REACHED*/ + return (xchar)0; +} + +/* + * Return a starting point and number of successive positions a level + * or dungeon entrance can occupy. + * + * Note: This follows the acouple (instead of the rcouple) rules for a + * negative random component (rand < 0). These rules are found + * in dgn_comp.y. The acouple [absolute couple] section says that + * a negative random component means from the (adjusted) base to the + * end of the dungeon. + */ +STATIC_OVL int +level_range(dgn, base, rand, chain, pd, adjusted_base) + xchar dgn; + int base, rand, chain; + struct proto_dungeon *pd; + int *adjusted_base; +{ + int lmax = dungeons[dgn].num_dunlevs; + + if (chain >= 0) { /* relative to a special level */ + s_level *levtmp = pd->final_lev[chain]; + if (!levtmp) panic("level_range: empty chain level!"); + + base += levtmp->dlevel.dlevel; + } else { /* absolute in the dungeon */ + /* from end of dungeon */ + if (base < 0) base = (lmax + base + 1); + } + + if (base < 1 || base > lmax) + panic("level_range: base value out of range"); + + *adjusted_base = base; + + if (rand == -1) { /* from base to end of dungeon */ + return (lmax - base + 1); + } else if (rand) { + /* make sure we don't run off the end of the dungeon */ + return (((base + rand - 1) > lmax) ? lmax-base+1 : rand); + } /* else only one choice */ + return 1; +} + +STATIC_OVL xchar +parent_dlevel(s, pd) + const char *s; + struct proto_dungeon *pd; +{ + int i, j, num, base, dnum = parent_dnum(s, pd); + branch *curr; + + + i = find_branch(s, pd); + num = level_range(dnum, pd->tmpbranch[i].lev.base, + pd->tmpbranch[i].lev.rand, + pd->tmpbranch[i].chain, + pd, &base); + + /* KMH -- Try our best to find a level without an existing branch */ + i = j = rn2(num); + do { + if (++i >= num) i = 0; + for (curr = branches; curr; curr = curr->next) + if ((curr->end1.dnum == dnum && curr->end1.dlevel == base+i) || + (curr->end2.dnum == dnum && curr->end2.dlevel == base+i)) + break; + } while (curr && i != j); + return (base + i); +} + +/* Convert from the temporary branch type to the dungeon branch type. */ +STATIC_OVL int +correct_branch_type(tbr) + struct tmpbranch *tbr; +{ + switch (tbr->type) { + case TBR_STAIR: return BR_STAIR; + case TBR_NO_UP: return tbr->up ? BR_NO_END1 : BR_NO_END2; + case TBR_NO_DOWN: return tbr->up ? BR_NO_END2 : BR_NO_END1; + case TBR_PORTAL: return BR_PORTAL; + } + impossible("correct_branch_type: unknown branch type"); + return BR_STAIR; +} + +/* + * Add the given branch to the branch list. The branch list is ordered + * by end1 dungeon and level followed by end2 dungeon and level. If + * extract_first is true, then the branch is already part of the list + * but needs to be repositioned. + */ +void +insert_branch(new_branch, extract_first) + branch *new_branch; + boolean extract_first; +{ + branch *curr, *prev; + long new_val, curr_val, prev_val; + + if (extract_first) { + for (prev = 0, curr = branches; curr; prev = curr, curr = curr->next) + if (curr == new_branch) break; + + if (!curr) panic("insert_branch: not found"); + if (prev) + prev->next = curr->next; + else + branches = curr->next; + } + new_branch->next = (branch *) 0; + +/* Convert the branch into a unique number so we can sort them. */ +#define branch_val(bp) \ + ((((long)(bp)->end1.dnum * (MAXLEVEL+1) + \ + (long)(bp)->end1.dlevel) * (MAXDUNGEON+1) * (MAXLEVEL+1)) + \ + ((long)(bp)->end2.dnum * (MAXLEVEL+1) + (long)(bp)->end2.dlevel)) + + /* + * Insert the new branch into the correct place in the branch list. + */ + prev = (branch *) 0; + prev_val = -1; + new_val = branch_val(new_branch); + for (curr = branches; curr; + prev_val = curr_val, prev = curr, curr = curr->next) { + curr_val = branch_val(curr); + if (prev_val < new_val && new_val <= curr_val) break; + } + if (prev) { + new_branch->next = curr; + prev->next = new_branch; + } else { + new_branch->next = branches; + branches = new_branch; + } +} + +/* Add a dungeon branch to the branch list. */ +STATIC_OVL branch * +add_branch(dgn, child_entry_level, pd) + int dgn; + int child_entry_level; + struct proto_dungeon *pd; +{ + static int branch_id = 0; + int branch_num; + branch *new_branch; + + branch_num = find_branch(dungeons[dgn].dname,pd); + new_branch = (branch *) alloc(sizeof(branch)); + new_branch->next = (branch *) 0; + new_branch->id = branch_id++; + new_branch->type = correct_branch_type(&pd->tmpbranch[branch_num]); + new_branch->end1.dnum = parent_dnum(dungeons[dgn].dname, pd); + new_branch->end1.dlevel = parent_dlevel(dungeons[dgn].dname, pd); + new_branch->end2.dnum = dgn; + new_branch->end2.dlevel = child_entry_level; + new_branch->end1_up = pd->tmpbranch[branch_num].up ? TRUE : FALSE; + + insert_branch(new_branch, FALSE); + return new_branch; +} + +/* + * Add new level to special level chain. Insert it in level order with the + * other levels in this dungeon. This assumes that we are never given a + * level that has a dungeon number less than the dungeon number of the + * last entry. + */ +STATIC_OVL void +add_level(new_lev) + s_level *new_lev; +{ + s_level *prev, *curr; + + prev = (s_level *) 0; + for (curr = sp_levchn; curr; curr = curr->next) { + if (curr->dlevel.dnum == new_lev->dlevel.dnum && + curr->dlevel.dlevel > new_lev->dlevel.dlevel) + break; + prev = curr; + } + if (!prev) { + new_lev->next = sp_levchn; + sp_levchn = new_lev; + } else { + new_lev->next = curr; + prev->next = new_lev; + } +} + +STATIC_OVL void +init_level(dgn, proto_index, pd) + int dgn, proto_index; + struct proto_dungeon *pd; +{ + s_level *new_level; + struct tmplevel *tlevel = &pd->tmplevel[proto_index]; + + pd->final_lev[proto_index] = (s_level *) 0; /* no "real" level */ +#ifdef WIZARD + if (!wizard) +#endif + if (tlevel->chance <= rn2(100)) return; + + pd->final_lev[proto_index] = new_level = + (s_level *) alloc(sizeof(s_level)); + /* load new level with data */ + Strcpy(new_level->proto, tlevel->name); + new_level->boneid = tlevel->boneschar; + new_level->dlevel.dnum = dgn; + new_level->dlevel.dlevel = 0; /* for now */ + + new_level->flags.town = !!(tlevel->flags & TOWN); + new_level->flags.hellish = !!(tlevel->flags & HELLISH); + new_level->flags.maze_like = !!(tlevel->flags & MAZELIKE); + new_level->flags.rogue_like = !!(tlevel->flags & ROGUELIKE); + new_level->flags.align = ((tlevel->flags & D_ALIGN_MASK) >> 4); + if (!new_level->flags.align) + new_level->flags.align = + ((pd->tmpdungeon[dgn].flags & D_ALIGN_MASK) >> 4); + + new_level->rndlevs = tlevel->rndlevs; + new_level->next = (s_level *) 0; +} + +STATIC_OVL int +possible_places(idx, map, pd) + int idx; /* prototype index */ + boolean *map; /* array MAXLEVEL+1 in length */ + struct proto_dungeon *pd; +{ + int i, start, count; + s_level *lev = pd->final_lev[idx]; + + /* init level possibilities */ + for (i = 0; i <= MAXLEVEL; i++) map[i] = FALSE; + + /* get base and range and set those entried to true */ + count = level_range(lev->dlevel.dnum, pd->tmplevel[idx].lev.base, + pd->tmplevel[idx].lev.rand, + pd->tmplevel[idx].chain, + pd, &start); + for (i = start; i < start+count; i++) + map[i] = TRUE; + + /* mark off already placed levels */ + for (i = pd->start; i < idx; i++) { + if (pd->final_lev[i] && map[pd->final_lev[i]->dlevel.dlevel]) { + map[pd->final_lev[i]->dlevel.dlevel] = FALSE; + --count; + } + } + + return count; +} + +/* Pick the nth TRUE entry in the given boolean array. */ +STATIC_OVL xchar +pick_level(map, nth) + boolean *map; /* an array MAXLEVEL+1 in size */ + int nth; +{ + int i; + for (i = 1; i <= MAXLEVEL; i++) + if (map[i] && !nth--) return (xchar) i; + panic("pick_level: ran out of valid levels"); + return 0; +} + +#ifdef DDEBUG +static void FDECL(indent,(int)); + +static void +indent(d) +int d; +{ + while (d-- > 0) fputs(" ", stderr); +} +#endif + +/* + * Place a level. First, find the possible places on a dungeon map + * template. Next pick one. Then try to place the next level. If + * sucessful, we're done. Otherwise, try another (and another) until + * all possible places have been tried. If all possible places have + * been exausted, return false. + */ +STATIC_OVL boolean +place_level(proto_index, pd) + int proto_index; + struct proto_dungeon *pd; +{ + boolean map[MAXLEVEL+1]; /* valid levels are 1..MAXLEVEL inclusive */ + s_level *lev; + int npossible; +#ifdef DDEBUG + int i; +#endif + + if (proto_index == pd->n_levs) return TRUE; /* at end of proto levels */ + + lev = pd->final_lev[proto_index]; + + /* No level created for this prototype, goto next. */ + if (!lev) return place_level(proto_index+1, pd); + + npossible = possible_places(proto_index, map, pd); + + for (; npossible; --npossible) { + lev->dlevel.dlevel = pick_level(map, rn2(npossible)); +#ifdef DDEBUG + indent(proto_index-pd->start); + fprintf(stderr,"%s: trying %d [ ", lev->proto, lev->dlevel.dlevel); + for (i = 1; i <= MAXLEVEL; i++) + if (map[i]) fprintf(stderr,"%d ", i); + fprintf(stderr,"]\n"); +#endif + if (place_level(proto_index+1, pd)) return TRUE; + map[lev->dlevel.dlevel] = FALSE; /* this choice didn't work */ + } +#ifdef DDEBUG + indent(proto_index-pd->start); + fprintf(stderr,"%s: failed\n", lev->proto); +#endif + return FALSE; +} + + +struct level_map { + const char *lev_name; + d_level *lev_spec; +} level_map[] = { + { "air", &air_level }, + { "asmodeus", &asmodeus_level }, + { "astral", &astral_level }, + { "baalz", &baalzebub_level }, + { "bigroom", &bigroom_level }, + { "castle", &stronghold_level }, + { "earth", &earth_level }, + { "fakewiz1", &portal_level }, + { "fire", &fire_level }, + { "juiblex", &juiblex_level }, + { "knox", &knox_level }, + { "medusa", &medusa_level }, + { "oracle", &oracle_level }, + { "orcus", &orcus_level }, +#ifdef REINCARNATION + { "rogue", &rogue_level }, +#endif + { "sanctum", &sanctum_level }, + { "valley", &valley_level }, + { "water", &water_level }, + { "wizard1", &wiz1_level }, + { "wizard2", &wiz2_level }, + { "wizard3", &wiz3_level }, + { X_START, &qstart_level }, + { X_LOCATE, &qlocate_level }, + { X_GOAL, &nemesis_level }, + { "", (d_level *)0 } +}; + +void +init_dungeons() /* initialize the "dungeon" structs */ +{ + dlb *dgn_file; + register int i, cl = 0, cb = 0; + register s_level *x; + struct proto_dungeon pd; + struct level_map *lev_map; + struct version_info vers_info; + + pd.n_levs = pd.n_brs = 0; + + dgn_file = dlb_fopen(DUNGEON_FILE, RDBMODE); + if (!dgn_file) { + char tbuf[BUFSZ]; + Sprintf(tbuf, "Cannot open dungeon description - \"%s", + DUNGEON_FILE); +#ifdef DLBRSRC /* using a resource from the executable */ + Strcat(tbuf, "\" resource!"); +#else /* using a file or DLB file */ +# if defined(DLB) + Strcat(tbuf, "\" from "); +# ifdef PREFIXES_IN_USE + Strcat(tbuf, "\n\""); + if (fqn_prefix[DATAPREFIX]) Strcat(tbuf, fqn_prefix[DATAPREFIX]); +# else + Strcat(tbuf, "\""); +# endif + Strcat(tbuf, DLBFILE); +# endif + Strcat(tbuf, "\" file!"); +#endif +#ifdef WIN32 + interject_assistance(1, INTERJECT_PANIC, (genericptr_t)tbuf, + (genericptr_t)fqn_prefix[DATAPREFIX]); +#endif + panic(tbuf); + } + + /* validate the data's version against the program's version */ + Fread((genericptr_t) &vers_info, sizeof vers_info, 1, dgn_file); + /* we'd better clear the screen now, since when error messages come from + * check_version() they will be printed using pline(), which doesn't + * mix with the raw messages that might be already on the screen + */ + if (iflags.window_inited) clear_nhwindow(WIN_MAP); + if (!check_version(&vers_info, DUNGEON_FILE, TRUE)) + panic("Dungeon description not valid."); + + /* + * Read in each dungeon and transfer the results to the internal + * dungeon arrays. + */ + sp_levchn = (s_level *) 0; + Fread((genericptr_t)&n_dgns, sizeof(int), 1, dgn_file); + if (n_dgns >= MAXDUNGEON) + panic("init_dungeons: too many dungeons"); + + for (i = 0; i < n_dgns; i++) { + Fread((genericptr_t)&pd.tmpdungeon[i], + sizeof(struct tmpdungeon), 1, dgn_file); +#ifdef WIZARD + if(!wizard) +#endif + if(pd.tmpdungeon[i].chance && (pd.tmpdungeon[i].chance <= rn2(100))) { + int j; + + /* skip over any levels or branches */ + for(j = 0; j < pd.tmpdungeon[i].levels; j++) + Fread((genericptr_t)&pd.tmplevel[cl], sizeof(struct tmplevel), + 1, dgn_file); + + for(j = 0; j < pd.tmpdungeon[i].branches; j++) + Fread((genericptr_t)&pd.tmpbranch[cb], + sizeof(struct tmpbranch), 1, dgn_file); + n_dgns--; i--; + continue; + } + + Strcpy(dungeons[i].dname, pd.tmpdungeon[i].name); + Strcpy(dungeons[i].proto, pd.tmpdungeon[i].protoname); + dungeons[i].boneid = pd.tmpdungeon[i].boneschar; + + if(pd.tmpdungeon[i].lev.rand) + dungeons[i].num_dunlevs = (xchar)rn1(pd.tmpdungeon[i].lev.rand, + pd.tmpdungeon[i].lev.base); + else dungeons[i].num_dunlevs = (xchar)pd.tmpdungeon[i].lev.base; + + if(!i) { + dungeons[i].ledger_start = 0; + dungeons[i].depth_start = 1; + dungeons[i].dunlev_ureached = 1; + } else { + dungeons[i].ledger_start = dungeons[i-1].ledger_start + + dungeons[i-1].num_dunlevs; + dungeons[i].dunlev_ureached = 0; + } + + dungeons[i].flags.hellish = !!(pd.tmpdungeon[i].flags & HELLISH); + dungeons[i].flags.maze_like = !!(pd.tmpdungeon[i].flags & MAZELIKE); + dungeons[i].flags.rogue_like = !!(pd.tmpdungeon[i].flags & ROGUELIKE); + dungeons[i].flags.align = ((pd.tmpdungeon[i].flags & D_ALIGN_MASK) >> 4); + /* + * Set the entry level for this dungeon. The pd.tmpdungeon entry + * value means: + * < 0 from bottom (-1 == bottom level) + * 0 default (top) + * > 0 actual level (1 = top) + * + * Note that the entry_lev field in the dungeon structure is + * redundant. It is used only here and in print_dungeon(). + */ + if (pd.tmpdungeon[i].entry_lev < 0) { + dungeons[i].entry_lev = dungeons[i].num_dunlevs + + pd.tmpdungeon[i].entry_lev + 1; + if (dungeons[i].entry_lev <= 0) dungeons[i].entry_lev = 1; + } else if (pd.tmpdungeon[i].entry_lev > 0) { + dungeons[i].entry_lev = pd.tmpdungeon[i].entry_lev; + if (dungeons[i].entry_lev > dungeons[i].num_dunlevs) + dungeons[i].entry_lev = dungeons[i].num_dunlevs; + } else { /* default */ + dungeons[i].entry_lev = 1; /* defaults to top level */ + } + + if (i) { /* set depth */ + branch *br; + schar from_depth; + boolean from_up; + + br = add_branch(i, dungeons[i].entry_lev, &pd); + + /* Get the depth of the connecting end. */ + if (br->end1.dnum == i) { + from_depth = depth(&br->end2); + from_up = !br->end1_up; + } else { + from_depth = depth(&br->end1); + from_up = br->end1_up; + } + + /* + * Calculate the depth of the top of the dungeon via + * its branch. First, the depth of the entry point: + * + * depth of branch from "parent" dungeon + * + -1 or 1 depending on a up or down stair or + * 0 if portal + * + * Followed by the depth of the top of the dungeon: + * + * - (entry depth - 1) + * + * We'll say that portals stay on the same depth. + */ + dungeons[i].depth_start = from_depth + + (br->type == BR_PORTAL ? 0 : + (from_up ? -1 : 1)) + - (dungeons[i].entry_lev - 1); + } + + /* this is redundant - it should have been flagged by dgn_comp */ + if(dungeons[i].num_dunlevs > MAXLEVEL) + dungeons[i].num_dunlevs = MAXLEVEL; + + pd.start = pd.n_levs; /* save starting point */ + pd.n_levs += pd.tmpdungeon[i].levels; + if (pd.n_levs > LEV_LIMIT) + panic("init_dungeon: too many special levels"); + /* + * Read in the prototype special levels. Don't add generated + * special levels until they are all placed. + */ + for(; cl < pd.n_levs; cl++) { + Fread((genericptr_t)&pd.tmplevel[cl], + sizeof(struct tmplevel), 1, dgn_file); + init_level(i, cl, &pd); + } + /* + * Recursively place the generated levels for this dungeon. This + * routine will attempt all possible combinations before giving + * up. + */ + if (!place_level(pd.start, &pd)) + panic("init_dungeon: couldn't place levels"); +#ifdef DDEBUG + fprintf(stderr, "--- end of dungeon %d ---\n", i); + fflush(stderr); + getchar(); +#endif + for (; pd.start < pd.n_levs; pd.start++) + if (pd.final_lev[pd.start]) add_level(pd.final_lev[pd.start]); + + + pd.n_brs += pd.tmpdungeon[i].branches; + if (pd.n_brs > BRANCH_LIMIT) + panic("init_dungeon: too many branches"); + for(; cb < pd.n_brs; cb++) + Fread((genericptr_t)&pd.tmpbranch[cb], + sizeof(struct tmpbranch), 1, dgn_file); + } + (void) dlb_fclose(dgn_file); + + for (i = 0; i < 5; i++) tune[i] = 'A' + rn2(7); + tune[5] = 0; + + /* + * Find most of the special levels and dungeons so we can access their + * locations quickly. + */ + for (lev_map = level_map; lev_map->lev_name[0]; lev_map++) { + x = find_level(lev_map->lev_name); + if (x) { + assign_level(lev_map->lev_spec, &x->dlevel); + if (!strncmp(lev_map->lev_name, "x-", 2)) { + /* This is where the name substitution on the + * levels of the quest dungeon occur. + */ + Sprintf(x->proto, "%s%s", urole.filecode, &lev_map->lev_name[1]); + } else if (lev_map->lev_spec == &knox_level) { + branch *br; + /* + * Kludge to allow floating Knox entrance. We + * specify a floating entrance by the fact that + * its entrance (end1) has a bogus dnum, namely + * n_dgns. + */ + for (br = branches; br; br = br->next) + if (on_level(&br->end2, &knox_level)) break; + + if (br) br->end1.dnum = n_dgns; + /* adjust the branch's position on the list */ + insert_branch(br, TRUE); + } + } + } +/* + * I hate hardwiring these names. :-( + */ + quest_dnum = dname_to_dnum("The Quest"); + sokoban_dnum = dname_to_dnum("Sokoban"); + mines_dnum = dname_to_dnum("The Gnomish Mines"); + tower_dnum = dname_to_dnum("Vlad's Tower"); + + /* one special fixup for dummy surface level */ + if ((x = find_level("dummy")) != 0) { + i = x->dlevel.dnum; + /* the code above puts earth one level above dungeon level #1, + making the dummy level overlay level 1; but the whole reason + for having the dummy level is to make earth have depth -1 + instead of 0, so adjust the start point to shift endgame up */ + if (dunlevs_in_dungeon(&x->dlevel) > 1 - dungeons[i].depth_start) + dungeons[i].depth_start -= 1; + /* TO DO: strip "dummy" out all the way here, + so that it's hidden from feedback. */ + } + +#ifdef DEBUG + dumpit(); +#endif +} + +xchar +dunlev(lev) /* return the level number for lev in *this* dungeon */ +d_level *lev; +{ + return(lev->dlevel); +} + +xchar +dunlevs_in_dungeon(lev) /* return the lowest level number for *this* dungeon*/ +d_level *lev; +{ + return(dungeons[lev->dnum].num_dunlevs); +} + +xchar +deepest_lev_reached(noquest) /* return the lowest level explored in the game*/ +boolean noquest; +{ + /* this function is used for three purposes: to provide a factor + * of difficulty in monster generation; to provide a factor of + * difficulty in experience calculations (botl.c and end.c); and + * to insert the deepest level reached in the game in the topten + * display. the 'noquest' arg switch is required for the latter. + * + * from the player's point of view, going into the Quest is _not_ + * going deeper into the dungeon -- it is going back "home", where + * the dungeon starts at level 1. given the setup in dungeon.def, + * the depth of the Quest (thought of as starting at level 1) is + * never lower than the level of entry into the Quest, so we exclude + * the Quest from the topten "deepest level reached" display + * calculation. _However_ the Quest is a difficult dungeon, so we + * include it in the factor of difficulty calculations. + */ + register int i; + d_level tmp; + register schar ret = 0; + + for(i = 0; i < n_dgns; i++) { + if((tmp.dlevel = dungeons[i].dunlev_ureached) == 0) continue; + if(!strcmp(dungeons[i].dname, "The Quest") && noquest) continue; + + tmp.dnum = i; + if(depth(&tmp) > ret) ret = depth(&tmp); + } + return((xchar) ret); +} + +/* return a bookkeeping level number for purpose of comparisons and + * save/restore */ +xchar +ledger_no(lev) +d_level *lev; +{ + return((xchar)(lev->dlevel + dungeons[lev->dnum].ledger_start)); +} + +/* + * The last level in the bookkeeping list of level is the bottom of the last + * dungeon in the dungeons[] array. + * + * Maxledgerno() -- which is the max number of levels in the bookkeeping + * list, should not be confused with dunlevs_in_dungeon(lev) -- which + * returns the max number of levels in lev's dungeon, and both should + * not be confused with deepest_lev_reached() -- which returns the lowest + * depth visited by the player. + */ +xchar +maxledgerno() +{ + return (xchar) (dungeons[n_dgns-1].ledger_start + + dungeons[n_dgns-1].num_dunlevs); +} + +/* return the dungeon that this ledgerno exists in */ +xchar +ledger_to_dnum(ledgerno) +xchar ledgerno; +{ + register int i; + + /* find i such that (i->base + 1) <= ledgerno <= (i->base + i->count) */ + for (i = 0; i < n_dgns; i++) + if (dungeons[i].ledger_start < ledgerno && + ledgerno <= dungeons[i].ledger_start + dungeons[i].num_dunlevs) + return (xchar)i; + + panic("level number out of range [ledger_to_dnum(%d)]", (int)ledgerno); + /*NOT REACHED*/ + return (xchar)0; +} + +/* return the level of the dungeon this ledgerno exists in */ +xchar +ledger_to_dlev(ledgerno) +xchar ledgerno; +{ + return((xchar)(ledgerno - dungeons[ledger_to_dnum(ledgerno)].ledger_start)); +} + +#endif /* OVL1 */ +#ifdef OVL0 + +/* returns the depth of a level, in floors below the surface */ +/* (note levels in different dungeons can have the same depth). */ +schar +depth(lev) +d_level *lev; +{ + return((schar)( dungeons[lev->dnum].depth_start + lev->dlevel - 1)); +} + +boolean +on_level(lev1, lev2) /* are "lev1" and "lev2" actually the same? */ +d_level *lev1, *lev2; +{ + return((boolean)((lev1->dnum == lev2->dnum) && (lev1->dlevel == lev2->dlevel))); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +/* is this level referenced in the special level chain? */ +s_level * +Is_special(lev) +d_level *lev; +{ + s_level *levtmp; + + for (levtmp = sp_levchn; levtmp; levtmp = levtmp->next) + if (on_level(lev, &levtmp->dlevel)) return(levtmp); + + return((s_level *)0); +} + +/* + * Is this a multi-dungeon branch level? If so, return a pointer to the + * branch. Otherwise, return null. + */ +branch * +Is_branchlev(lev) + d_level *lev; +{ + branch *curr; + + for (curr = branches; curr; curr = curr->next) { + if (on_level(lev, &curr->end1) || on_level(lev, &curr->end2)) + return curr; + } + return (branch *) 0; +} + +/* goto the next level (or appropriate dungeon) */ +void +next_level(at_stairs) +boolean at_stairs; +{ + if (at_stairs && u.ux == sstairs.sx && u.uy == sstairs.sy) { + /* Taking a down dungeon branch. */ + goto_level(&sstairs.tolev, at_stairs, FALSE, FALSE); + } else { + /* Going down a stairs or jump in a trap door. */ + d_level newlevel; + + newlevel.dnum = u.uz.dnum; + newlevel.dlevel = u.uz.dlevel + 1; + goto_level(&newlevel, at_stairs, !at_stairs, FALSE); + } +} + +/* goto the previous level (or appropriate dungeon) */ +void +prev_level(at_stairs) +boolean at_stairs; +{ + if (at_stairs && u.ux == sstairs.sx && u.uy == sstairs.sy) { + /* Taking an up dungeon branch. */ + /* KMH -- Upwards branches are okay if not level 1 */ + /* (Just make sure it doesn't go above depth 1) */ + if(!u.uz.dnum && u.uz.dlevel == 1 && !u.uhave.amulet) done(ESCAPED); + else goto_level(&sstairs.tolev, at_stairs, FALSE, FALSE); + } else { + /* Going up a stairs or rising through the ceiling. */ + d_level newlevel; + newlevel.dnum = u.uz.dnum; + newlevel.dlevel = u.uz.dlevel - 1; + goto_level(&newlevel, at_stairs, FALSE, FALSE); + } +} + +void +u_on_newpos(x, y) +int x, y; +{ + u.ux = x; + u.uy = y; +#ifdef CLIPPING + cliparound(u.ux, u.uy); +#endif +#ifdef STEED + /* ridden steed always shares hero's location */ + if (u.usteed) u.usteed->mx = u.ux, u.usteed->my = u.uy; +#endif +} + +void +u_on_sstairs() { /* place you on the special staircase */ + + if (sstairs.sx) { + u_on_newpos(sstairs.sx, sstairs.sy); + } else { + /* code stolen from goto_level */ + int trycnt = 0; + xchar x, y; +#ifdef DEBUG + pline("u_on_sstairs: picking random spot"); +#endif +#define badspot(x,y) ((levl[x][y].typ != ROOM && levl[x][y].typ != CORR) || MON_AT(x, y)) + do { + x = rnd(COLNO-1); + y = rn2(ROWNO); + if (!badspot(x, y)) { + u_on_newpos(x, y); + return; + } + } while (++trycnt <= 500); + panic("u_on_sstairs: could not relocate player!"); +#undef badspot + } +} + +void +u_on_upstairs() /* place you on upstairs (or special equivalent) */ +{ + if (xupstair) { + u_on_newpos(xupstair, yupstair); + } else + u_on_sstairs(); +} + +void +u_on_dnstairs() /* place you on dnstairs (or special equivalent) */ +{ + if (xdnstair) { + u_on_newpos(xdnstair, ydnstair); + } else + u_on_sstairs(); +} + +boolean +On_stairs(x, y) +xchar x, y; +{ + return((boolean)((x == xupstair && y == yupstair) || + (x == xdnstair && y == ydnstair) || + (x == xdnladder && y == ydnladder) || + (x == xupladder && y == yupladder) || + (x == sstairs.sx && y == sstairs.sy))); +} + +boolean +Is_botlevel(lev) +d_level *lev; +{ + return((boolean)(lev->dlevel == dungeons[lev->dnum].num_dunlevs)); +} + +boolean +Can_dig_down(lev) +d_level *lev; +{ + return((boolean)(!level.flags.hardfloor + && !Is_botlevel(lev) && !Invocation_lev(lev))); +} + +/* + * Like Can_dig_down (above), but also allows falling through on the + * stronghold level. Normally, the bottom level of a dungeon resists + * both digging and falling. + */ +boolean +Can_fall_thru(lev) +d_level *lev; +{ + return((boolean)(Can_dig_down(lev) || Is_stronghold(lev))); +} + +/* + * True if one can rise up a level (e.g. cursed gain level). + * This happens on intermediate dungeon levels or on any top dungeon + * level that has a stairwell style branch to the next higher dungeon. + * Checks for amulets and such must be done elsewhere. + */ +boolean +Can_rise_up(x, y, lev) +int x, y; +d_level *lev; +{ + /* can't rise up from inside the top of the Wizard's tower */ + /* KMH -- or in sokoban */ + if (In_endgame(lev) || In_sokoban(lev) || + (Is_wiz1_level(lev) && In_W_tower(x, y, lev))) + return FALSE; + return (boolean)(lev->dlevel > 1 || + (dungeons[lev->dnum].entry_lev == 1 && ledger_no(lev) != 1 && + sstairs.sx && sstairs.up)); +} + +/* + * It is expected that the second argument of get_level is a depth value, + * either supplied by the user (teleport control) or randomly generated. + * But more than one level can be at the same depth. If the target level + * is "above" the present depth location, get_level must trace "up" from + * the player's location (through the ancestors dungeons) the dungeon + * within which the target level is located. With only one exception + * which does not pass through this routine (see level_tele), teleporting + * "down" is confined to the current dungeon. At present, level teleport + * in dungeons that build up is confined within them. + */ +void +get_level(newlevel, levnum) +d_level *newlevel; +int levnum; +{ + branch *br; + xchar dgn = u.uz.dnum; + + if (levnum <= 0) { + /* can only currently happen in endgame */ + levnum = u.uz.dlevel; + } else if (levnum > dungeons[dgn].depth_start + + dungeons[dgn].num_dunlevs - 1) { + /* beyond end of dungeon, jump to last level */ + levnum = dungeons[dgn].num_dunlevs; + } else { + /* The desired level is in this dungeon or a "higher" one. */ + + /* + * Branch up the tree until we reach a dungeon that contains the + * levnum. + */ + if (levnum < dungeons[dgn].depth_start) { + + do { + /* + * Find the parent dungeon of this dungeon. + * + * This assumes that end2 is always the "child" and it is + * unique. + */ + for (br = branches; br; br = br->next) + if (br->end2.dnum == dgn) break; + if (!br) + panic("get_level: can't find parent dungeon"); + + dgn = br->end1.dnum; + } while (levnum < dungeons[dgn].depth_start); + } + + /* We're within the same dungeon; calculate the level. */ + levnum = levnum - dungeons[dgn].depth_start + 1; + } + + newlevel->dnum = dgn; + newlevel->dlevel = levnum; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +boolean +In_quest(lev) /* are you in the quest dungeon? */ +d_level *lev; +{ + return((boolean)(lev->dnum == quest_dnum)); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +boolean +In_mines(lev) /* are you in the mines dungeon? */ +d_level *lev; +{ + return((boolean)(lev->dnum == mines_dnum)); +} + +/* + * Return the branch for the given dungeon. + * + * This function assumes: + * + This is not called with "Dungeons of Doom". + * + There is only _one_ branch to a given dungeon. + * + Field end2 is the "child" dungeon. + */ +branch * +dungeon_branch(s) + const char *s; +{ + branch *br; + xchar dnum; + + dnum = dname_to_dnum(s); + + /* Find the branch that connects to dungeon i's branch. */ + for (br = branches; br; br = br->next) + if (br->end2.dnum == dnum) break; + + if (!br) panic("dgn_entrance: can't find entrance to %s", s); + + return br; +} + +/* + * This returns true if the hero is on the same level as the entrance to + * the named dungeon. + * + * Called from do.c and mklev.c. + * + * Assumes that end1 is always the "parent". + */ +boolean +at_dgn_entrance(s) + const char *s; +{ + branch *br; + + br = dungeon_branch(s); + return((boolean)(on_level(&u.uz, &br->end1) ? TRUE : FALSE)); +} + +boolean +In_V_tower(lev) /* is `lev' part of Vlad's tower? */ +d_level *lev; +{ + return((boolean)(lev->dnum == tower_dnum)); +} + +boolean +On_W_tower_level(lev) /* is `lev' a level containing the Wizard's tower? */ +d_level *lev; +{ + return (boolean)(Is_wiz1_level(lev) || + Is_wiz2_level(lev) || + Is_wiz3_level(lev)); +} + +boolean +In_W_tower(x, y, lev) /* is of `lev' inside the Wizard's tower? */ +int x, y; +d_level *lev; +{ + if (!On_W_tower_level(lev)) return FALSE; + /* + * Both of the exclusion regions for arriving via level teleport + * (from above or below) define the tower's boundary. + * assert( updest.nIJ == dndest.nIJ for I={l|h},J={x|y} ); + */ + if (dndest.nlx > 0) + return (boolean)within_bounded_area(x, y, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy); + else + impossible("No boundary for Wizard's Tower?"); + return FALSE; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +boolean +In_hell(lev) /* are you in one of the Hell levels? */ +d_level *lev; +{ + return((boolean)(dungeons[lev->dnum].flags.hellish)); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +void +find_hell(lev) /* sets *lev to be the gateway to Gehennom... */ +d_level *lev; +{ + lev->dnum = valley_level.dnum; + lev->dlevel = 1; +} + +void +goto_hell(at_stairs, falling) /* go directly to hell... */ +boolean at_stairs, falling; +{ + d_level lev; + + find_hell(&lev); + goto_level(&lev, at_stairs, falling, FALSE); +} + +void +assign_level(dest, src) /* equivalent to dest = source */ +d_level *dest, *src; +{ + dest->dnum = src->dnum; + dest->dlevel = src->dlevel; +} + +void +assign_rnd_level(dest, src, range) /* dest = src + rn1(range) */ +d_level *dest, *src; +int range; +{ + dest->dnum = src->dnum; + dest->dlevel = src->dlevel + ((range > 0) ? rnd(range) : -rnd(-range)) ; + + if(dest->dlevel > dunlevs_in_dungeon(dest)) + dest->dlevel = dunlevs_in_dungeon(dest); + else if(dest->dlevel < 1) + dest->dlevel = 1; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +int +induced_align(pct) +int pct; +{ + s_level *lev = Is_special(&u.uz); + aligntyp al; + + if (lev && lev->flags.align) + if(rn2(100) < pct) return(lev->flags.align); + + if(dungeons[u.uz.dnum].flags.align) + if(rn2(100) < pct) return(dungeons[u.uz.dnum].flags.align); + + al = rn2(3) - 1; + return(Align2amask(al)); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +boolean +Invocation_lev(lev) +d_level *lev; +{ + return((boolean)(In_hell(lev) && + lev->dlevel == (dungeons[lev->dnum].num_dunlevs - 1))); +} + +/* use instead of depth() wherever a degree of difficulty is made + * dependent on the location in the dungeon (eg. monster creation). + */ +xchar +level_difficulty() +{ + if (In_endgame(&u.uz)) + return((xchar)(depth(&sanctum_level) + u.ulevel/2)); + else + if (u.uhave.amulet) + return(deepest_lev_reached(FALSE)); + else + return((xchar) depth(&u.uz)); +} + +/* Take one word and try to match it to a level. + * Recognized levels are as shown by print_dungeon(). + */ +schar +lev_by_name(nam) +const char *nam; +{ + schar lev = 0; + s_level *slev; + d_level dlev; + const char *p; + int idx, idxtoo; + char buf[BUFSZ]; + + /* allow strings like "the oracle level" to find "oracle" */ + if (!strncmpi(nam, "the ", 4)) nam += 4; + if ((p = strstri(nam, " level")) != 0 && p == eos((char*)nam) - 6) { + nam = strcpy(buf, nam); + *(eos(buf) - 6) = '\0'; + } + /* hell is the old name, and wouldn't match; gehennom would match its + branch, yielding the castle level instead of the valley of the dead */ + if (!strcmpi(nam, "gehennom") || !strcmpi(nam, "hell")) { + if (In_V_tower(&u.uz)) nam = " to Vlad's tower"; /* branch to... */ + else nam = "valley"; + } + + if ((slev = find_level(nam)) != 0) { + dlev = slev->dlevel; + idx = ledger_no(&dlev); + if ((dlev.dnum == u.uz.dnum || + /* within same branch, or else main dungeon <-> gehennom */ + (u.uz.dnum == valley_level.dnum && + dlev.dnum == medusa_level.dnum) || + (u.uz.dnum == medusa_level.dnum && + dlev.dnum == valley_level.dnum)) && + ( /* either wizard mode or else seen and not forgotten */ +#ifdef WIZARD + wizard || +#endif + (level_info[idx].flags & (FORGOTTEN|VISITED)) == VISITED)) { + lev = depth(&slev->dlevel); + } + } else { /* not a specific level; try branch names */ + idx = find_branch(nam, (struct proto_dungeon *)0); + /* " to Xyzzy" */ + if (idx < 0 && (p = strstri(nam, " to ")) != 0) + idx = find_branch(p + 4, (struct proto_dungeon *)0); + + if (idx >= 0) { + idxtoo = (idx >> 8) & 0x00FF; + idx &= 0x00FF; + if ( /* either wizard mode, or else _both_ sides of branch seen */ +#ifdef WIZARD + wizard || +#endif + ((level_info[idx].flags & (FORGOTTEN|VISITED)) == VISITED && + (level_info[idxtoo].flags & (FORGOTTEN|VISITED)) == VISITED)) { + if (ledger_to_dnum(idxtoo) == u.uz.dnum) idx = idxtoo; + dlev.dnum = ledger_to_dnum(idx); + dlev.dlevel = ledger_to_dlev(idx); + lev = depth(&dlev); + } + } + } + return lev; +} + +#ifdef WIZARD + +/* Convert a branch type to a string usable by print_dungeon(). */ +STATIC_OVL const char * +br_string(type) + int type; +{ + switch (type) { + case BR_PORTAL: return "Portal"; + case BR_NO_END1: return "Connection"; + case BR_NO_END2: return "One way stair"; + case BR_STAIR: return "Stair"; + } + return " (unknown)"; +} + +/* Print all child branches between the lower and upper bounds. */ +STATIC_OVL void +print_branch(win, dnum, lower_bound, upper_bound, bymenu, lchoices) + winid win; + int dnum; + int lower_bound; + int upper_bound; + boolean bymenu; + struct lchoice *lchoices; +{ + branch *br; + char buf[BUFSZ]; + anything any; + + /* This assumes that end1 is the "parent". */ + for (br = branches; br; br = br->next) { + if (br->end1.dnum == dnum && lower_bound < br->end1.dlevel && + br->end1.dlevel <= upper_bound) { + Sprintf(buf," %s to %s: %d", + br_string(br->type), + dungeons[br->end2.dnum].dname, + depth(&br->end1)); + if (bymenu) { + lchoices->lev[lchoices->idx] = br->end1.dlevel; + lchoices->dgn[lchoices->idx] = br->end1.dnum; + lchoices->playerlev[lchoices->idx] = depth(&br->end1); + any.a_void = 0; + any.a_int = lchoices->idx + 1; + add_menu(win, NO_GLYPH, &any, lchoices->menuletter, + 0, ATR_NONE, buf, MENU_UNSELECTED); + if (lchoices->menuletter == 'z') lchoices->menuletter = 'A'; + else lchoices->menuletter++; + lchoices->idx++; + } else + putstr(win, 0, buf); + } + } +} + +/* Print available dungeon information. */ +schar +print_dungeon(bymenu, rlev, rdgn) +boolean bymenu; +schar *rlev; +xchar *rdgn; +{ + int i, last_level, nlev; + char buf[BUFSZ]; + boolean first; + s_level *slev; + dungeon *dptr; + branch *br; + anything any; + struct lchoice lchoices; + + winid win = create_nhwindow(NHW_MENU); + if (bymenu) { + start_menu(win); + lchoices.idx = 0; + lchoices.menuletter = 'a'; + } + + for (i = 0, dptr = dungeons; i < n_dgns; i++, dptr++) { + nlev = dptr->num_dunlevs; + if (nlev > 1) + Sprintf(buf, "%s: levels %d to %d", dptr->dname, dptr->depth_start, + dptr->depth_start + nlev - 1); + else + Sprintf(buf, "%s: level %d", dptr->dname, dptr->depth_start); + + /* Most entrances are uninteresting. */ + if (dptr->entry_lev != 1) { + if (dptr->entry_lev == nlev) + Strcat(buf, ", entrance from below"); + else + Sprintf(eos(buf), ", entrance on %d", + dptr->depth_start + dptr->entry_lev - 1); + } + if (bymenu) { + any.a_void = 0; + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, buf, MENU_UNSELECTED); + } else + putstr(win, 0, buf); + + /* + * Circle through the special levels to find levels that are in + * this dungeon. + */ + for (slev = sp_levchn, last_level = 0; slev; slev = slev->next) { + if (slev->dlevel.dnum != i) continue; + + /* print any branches before this level */ + print_branch(win, i, last_level, slev->dlevel.dlevel, bymenu, &lchoices); + + Sprintf(buf, " %s: %d", slev->proto, depth(&slev->dlevel)); + if (Is_stronghold(&slev->dlevel)) + Sprintf(eos(buf), " (tune %s)", tune); + if (bymenu) { + /* If other floating branches are added, this will need to change */ + if (i != knox_level.dnum) { + lchoices.lev[lchoices.idx] = slev->dlevel.dlevel; + lchoices.dgn[lchoices.idx] = i; + } else { + lchoices.lev[lchoices.idx] = depth(&slev->dlevel); + lchoices.dgn[lchoices.idx] = 0; + } + lchoices.playerlev[lchoices.idx] = depth(&slev->dlevel); + any.a_void = 0; + any.a_int = lchoices.idx + 1; + add_menu(win, NO_GLYPH, &any, lchoices.menuletter, + 0, ATR_NONE, buf, MENU_UNSELECTED); + if (lchoices.menuletter == 'z') lchoices.menuletter = 'A'; + else lchoices.menuletter++; + lchoices.idx++; + } else + putstr(win, 0, buf); + + last_level = slev->dlevel.dlevel; + } + /* print branches after the last special level */ + print_branch(win, i, last_level, MAXLEVEL, bymenu, &lchoices); + } + + /* Print out floating branches (if any). */ + for (first = TRUE, br = branches; br; br = br->next) { + if (br->end1.dnum == n_dgns) { + if (first) { + if (!bymenu) { + putstr(win, 0, ""); + putstr(win, 0, "Floating branches"); + } + first = FALSE; + } + Sprintf(buf, " %s to %s", + br_string(br->type), dungeons[br->end2.dnum].dname); + if (!bymenu) + putstr(win, 0, buf); + } + } + if (bymenu) { + int n; + menu_item *selected; + int idx; + + end_menu(win, "Level teleport to where:"); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n > 0) { + idx = selected[0].item.a_int - 1; + free((genericptr_t)selected); + if (rlev && rdgn) { + *rlev = lchoices.lev[idx]; + *rdgn = lchoices.dgn[idx]; + return lchoices.playerlev[idx]; + } + } + return 0; + } + + /* I hate searching for the invocation pos while debugging. -dean */ + if (Invocation_lev(&u.uz)) { + putstr(win, 0, ""); + Sprintf(buf, "Invocation position @ (%d,%d), hero @ (%d,%d)", + inv_pos.x, inv_pos.y, u.ux, u.uy); + putstr(win, 0, buf); + } + /* + * The following is based on the assumption that the inter-level portals + * created by the level compiler (not the dungeon compiler) only exist + * one per level (currently true, of course). + */ + else if (Is_earthlevel(&u.uz) || Is_waterlevel(&u.uz) + || Is_firelevel(&u.uz) || Is_airlevel(&u.uz)) { + struct trap *trap; + for (trap = ftrap; trap; trap = trap->ntrap) + if (trap->ttyp == MAGIC_PORTAL) break; + + putstr(win, 0, ""); + if (trap) + Sprintf(buf, "Portal @ (%d,%d), hero @ (%d,%d)", + trap->tx, trap->ty, u.ux, u.uy); + else + Sprintf(buf, "No portal found."); + putstr(win, 0, buf); + } + + display_nhwindow(win, TRUE); + destroy_nhwindow(win); + return 0; +} +#endif /* WIZARD */ + +#endif /* OVL1 */ + +/*dungeon.c*/ diff --git a/src/eat.c b/src/eat.c new file mode 100644 index 0000000..12e8d9b --- /dev/null +++ b/src/eat.c @@ -0,0 +1,2586 @@ +/* SCCS Id: @(#)eat.c 3.4 2003/02/13 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +/* #define DEBUG */ /* uncomment to enable new eat code debugging */ + +#ifdef DEBUG +# ifdef WIZARD +#define debugpline if (wizard) pline +# else +#define debugpline pline +# endif +#endif + +STATIC_PTR int NDECL(eatmdone); +STATIC_PTR int NDECL(eatfood); +STATIC_PTR void FDECL(costly_tin, (const char*)); +STATIC_PTR int NDECL(opentin); +STATIC_PTR int NDECL(unfaint); + +#ifdef OVLB +STATIC_DCL const char *FDECL(food_xname, (struct obj *,BOOLEAN_P)); +STATIC_DCL void FDECL(choke, (struct obj *)); +STATIC_DCL void NDECL(recalc_wt); +STATIC_DCL struct obj *FDECL(touchfood, (struct obj *)); +STATIC_DCL void NDECL(do_reset_eat); +STATIC_DCL void FDECL(done_eating, (BOOLEAN_P)); +STATIC_DCL void FDECL(cprefx, (int)); +STATIC_DCL int FDECL(intrinsic_possible, (int,struct permonst *)); +STATIC_DCL void FDECL(givit, (int,struct permonst *)); +STATIC_DCL void FDECL(cpostfx, (int)); +STATIC_DCL void FDECL(start_tin, (struct obj *)); +STATIC_DCL int FDECL(eatcorpse, (struct obj *)); +STATIC_DCL void FDECL(start_eating, (struct obj *)); +STATIC_DCL void FDECL(fprefx, (struct obj *)); +STATIC_DCL void FDECL(accessory_has_effect, (struct obj *)); +STATIC_DCL void FDECL(fpostfx, (struct obj *)); +STATIC_DCL int NDECL(bite); +STATIC_DCL int FDECL(edibility_prompts, (struct obj *)); +STATIC_DCL int FDECL(rottenfood, (struct obj *)); +STATIC_DCL void NDECL(eatspecial); +STATIC_DCL void FDECL(eataccessory, (struct obj *)); +STATIC_DCL const char *FDECL(foodword, (struct obj *)); +STATIC_DCL boolean FDECL(maybe_cannibal, (int,BOOLEAN_P)); + +char msgbuf[BUFSZ]; + +#endif /* OVLB */ + +/* hunger texts used on bottom line (each 8 chars long) */ +#define SATIATED 0 +#define NOT_HUNGRY 1 +#define HUNGRY 2 +#define WEAK 3 +#define FAINTING 4 +#define FAINTED 5 +#define STARVED 6 + +/* also used to see if you're allowed to eat cats and dogs */ +#define CANNIBAL_ALLOWED() (Role_if(PM_CAVEMAN) || Race_if(PM_ORC)) + +#ifndef OVLB + +STATIC_DCL NEARDATA const char comestibles[]; +STATIC_DCL NEARDATA const char allobj[]; +STATIC_DCL boolean force_save_hs; + +#else + +STATIC_OVL NEARDATA const char comestibles[] = { FOOD_CLASS, 0 }; + +/* Gold must come first for getobj(). */ +STATIC_OVL NEARDATA const char allobj[] = { + COIN_CLASS, WEAPON_CLASS, ARMOR_CLASS, POTION_CLASS, SCROLL_CLASS, + WAND_CLASS, RING_CLASS, AMULET_CLASS, FOOD_CLASS, TOOL_CLASS, + GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, SPBOOK_CLASS, 0 }; + +STATIC_OVL boolean force_save_hs = FALSE; + +const char *hu_stat[] = { + "Satiated", + " ", + "Hungry ", + "Weak ", + "Fainting", + "Fainted ", + "Starved " +}; + +#endif /* OVLB */ +#ifdef OVL1 + +/* + * Decide whether a particular object can be eaten by the possibly + * polymorphed character. Not used for monster checks. + */ +boolean +is_edible(obj) +register struct obj *obj; +{ + /* protect invocation tools but not Rider corpses (handled elsewhere)*/ + /* if (obj->oclass != FOOD_CLASS && obj_resists(obj, 0, 0)) */ + if (objects[obj->otyp].oc_unique) + return FALSE; + /* above also prevents the Amulet from being eaten, so we must never + allow fake amulets to be eaten either [which is already the case] */ + + if (metallivorous(youmonst.data) && is_metallic(obj) && + (youmonst.data != &mons[PM_RUST_MONSTER] || is_rustprone(obj))) + return TRUE; + if (u.umonnum == PM_GELATINOUS_CUBE && is_organic(obj) && + /* [g.cubes can eat containers and retain all contents + as engulfed items, but poly'd player can't do that] */ + !Has_contents(obj)) + return TRUE; + + /* return((boolean)(!!index(comestibles, obj->oclass))); */ + return (boolean)(obj->oclass == FOOD_CLASS); +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +init_uhunger() +{ + u.uhunger = 900; + u.uhs = NOT_HUNGRY; +} + +static const struct { const char *txt; int nut; } tintxts[] = { + {"deep fried", 60}, + {"pickled", 40}, + {"soup made from", 20}, + {"pureed", 500}, +#define ROTTEN_TIN 4 + {"rotten", -50}, +#define HOMEMADE_TIN 5 + {"homemade", 50}, + {"stir fried", 80}, + {"candied", 100}, + {"boiled", 50}, + {"dried", 55}, + {"szechuan", 70}, +#define FRENCH_FRIED_TIN 11 + {"french fried", 40}, + {"sauteed", 95}, + {"broiled", 80}, + {"smoked", 50}, + {"", 0} +}; +#define TTSZ SIZE(tintxts) + +static NEARDATA struct { + struct obj *tin; + int usedtime, reqtime; +} tin; + +static NEARDATA struct { + struct obj *piece; /* the thing being eaten, or last thing that + * was partially eaten, unless that thing was + * a tin, which uses the tin structure above, + * in which case this should be 0 */ + /* doeat() initializes these when piece is valid */ + int usedtime, /* turns spent eating */ + reqtime; /* turns required to eat */ + int nmod; /* coded nutrition per turn */ + Bitfield(canchoke,1); /* was satiated at beginning */ + + /* start_eating() initializes these */ + Bitfield(fullwarn,1); /* have warned about being full */ + Bitfield(eating,1); /* victual currently being eaten */ + Bitfield(doreset,1); /* stop eating at end of turn */ +} victual; + +static char *eatmbuf = 0; /* set by cpostfx() */ + +STATIC_PTR +int +eatmdone() /* called after mimicing is over */ +{ + /* release `eatmbuf' */ + if (eatmbuf) { + if (nomovemsg == eatmbuf) nomovemsg = 0; + free((genericptr_t)eatmbuf), eatmbuf = 0; + } + /* update display */ + if (youmonst.m_ap_type) { + youmonst.m_ap_type = M_AP_NOTHING; + newsym(u.ux,u.uy); + } + return 0; +} + +/* ``[the(] singular(food, xname) [)]'' with awareness of unique monsters */ +STATIC_OVL const char * +food_xname(food, the_pfx) +struct obj *food; +boolean the_pfx; +{ + const char *result; + int mnum = food->corpsenm; + + if (food->otyp == CORPSE && (mons[mnum].geno & G_UNIQ)) { + /* grab xname()'s modifiable return buffer for our own use */ + char *bufp = xname(food); + Sprintf(bufp, "%s%s corpse", + (the_pfx && !type_is_pname(&mons[mnum])) ? "the " : "", + s_suffix(mons[mnum].mname)); + result = bufp; + } else { + /* the ordinary case */ + result = singular(food, xname); + if (the_pfx) result = the(result); + } + return result; +} + +/* Created by GAN 01/28/87 + * Amended by AKP 09/22/87: if not hard, don't choke, just vomit. + * Amended by 3. 06/12/89: if not hard, sometimes choke anyway, to keep risk. + * 11/10/89: if hard, rarely vomit anyway, for slim chance. + */ +STATIC_OVL void +choke(food) /* To a full belly all food is bad. (It.) */ + register struct obj *food; +{ + /* only happens if you were satiated */ + if (u.uhs != SATIATED) { + if (!food || food->otyp != AMULET_OF_STRANGULATION) + return; + } else if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL) { + adjalign(-1); /* gluttony is unchivalrous */ + You_feel("like a glutton!"); + } + + exercise(A_CON, FALSE); + + if (Breathless || (!Strangled && !rn2(20))) { + /* choking by eating AoS doesn't involve stuffing yourself */ + if (food && food->otyp == AMULET_OF_STRANGULATION) { + You("choke, but recover your composure."); + return; + } + You("stuff yourself and then vomit voluminously."); + morehungry(1000); /* you just got *very* sick! */ + nomovemsg = 0; + vomit(); + } else { + killer_format = KILLED_BY_AN; + /* + * Note all "killer"s below read "Choked on %s" on the + * high score list & tombstone. So plan accordingly. + */ + if(food) { + You("choke over your %s.", foodword(food)); + if (food->oclass == COIN_CLASS) { + killer = "a very rich meal"; + } else { + killer = food_xname(food, FALSE); + if (food->otyp == CORPSE && + (mons[food->corpsenm].geno & G_UNIQ)) { + if (!type_is_pname(&mons[food->corpsenm])) + killer = the(killer); + killer_format = KILLED_BY; + } + } + } else { + You("choke over it."); + killer = "quick snack"; + } + You("die..."); + done(CHOKING); + } +} + +/* modify object wt. depending on time spent consuming it */ +STATIC_OVL void +recalc_wt() +{ + struct obj *piece = victual.piece; + +#ifdef DEBUG + debugpline("Old weight = %d", piece->owt); + debugpline("Used time = %d, Req'd time = %d", + victual.usedtime, victual.reqtime); +#endif + piece->owt = weight(piece); +#ifdef DEBUG + debugpline("New weight = %d", piece->owt); +#endif +} + +void +reset_eat() /* called when eating interrupted by an event */ +{ + /* we only set a flag here - the actual reset process is done after + * the round is spent eating. + */ + if(victual.eating && !victual.doreset) { +#ifdef DEBUG + debugpline("reset_eat..."); +#endif + victual.doreset = TRUE; + } + return; +} + +STATIC_OVL struct obj * +touchfood(otmp) +register struct obj *otmp; +{ + if (otmp->quan > 1L) { + if(!carried(otmp)) + (void) splitobj(otmp, otmp->quan - 1L); + else + otmp = splitobj(otmp, 1L); +#ifdef DEBUG + debugpline("split object,"); +#endif + } + + if (!otmp->oeaten) { + if(((!carried(otmp) && costly_spot(otmp->ox, otmp->oy) && + !otmp->no_charge) + || otmp->unpaid)) { + /* create a dummy duplicate to put on bill */ + verbalize("You bit it, you bought it!"); + bill_dummy_object(otmp); + } + otmp->oeaten = (otmp->otyp == CORPSE ? + mons[otmp->corpsenm].cnutrit : + objects[otmp->otyp].oc_nutrition); + } + + if (carried(otmp)) { + freeinv(otmp); + if (inv_cnt() >= 52) { + sellobj_state(SELL_DONTSELL); + dropy(otmp); + sellobj_state(SELL_NORMAL); + } else { + otmp->oxlth++; /* hack to prevent merge */ + otmp = addinv(otmp); + otmp->oxlth--; + } + } + return(otmp); +} + +/* When food decays, in the middle of your meal, we don't want to dereference + * any dangling pointers, so set it to null (which should still trigger + * do_reset_eat() at the beginning of eatfood()) and check for null pointers + * in do_reset_eat(). + */ +void +food_disappears(obj) +register struct obj *obj; +{ + if (obj == victual.piece) victual.piece = (struct obj *)0; + if (obj->timed) obj_stop_timers(obj); +} + +/* renaming an object usually results in it having a different address; + so the sequence start eating/opening, get interrupted, name the food, + resume eating/opening would restart from scratch */ +void +food_substitution(old_obj, new_obj) +struct obj *old_obj, *new_obj; +{ + if (old_obj == victual.piece) victual.piece = new_obj; + if (old_obj == tin.tin) tin.tin = new_obj; +} + +STATIC_OVL void +do_reset_eat() +{ +#ifdef DEBUG + debugpline("do_reset_eat..."); +#endif + if (victual.piece) { + victual.piece = touchfood(victual.piece); + recalc_wt(); + } + victual.fullwarn = victual.eating = victual.doreset = FALSE; + /* Do not set canchoke to FALSE; if we continue eating the same object + * we need to know if canchoke was set when they started eating it the + * previous time. And if we don't continue eating the same object + * canchoke always gets recalculated anyway. + */ + stop_occupation(); + newuhs(FALSE); +} + +STATIC_PTR +int +eatfood() /* called each move during eating process */ +{ + if(!victual.piece || + (!carried(victual.piece) && !obj_here(victual.piece, u.ux, u.uy))) { + /* maybe it was stolen? */ + do_reset_eat(); + return(0); + } + if(!victual.eating) return(0); + + if(++victual.usedtime <= victual.reqtime) { + if(bite()) return(0); + return(1); /* still busy */ + } else { /* done */ + done_eating(TRUE); + return(0); + } +} + +STATIC_OVL void +done_eating(message) +boolean message; +{ + victual.piece->in_use = TRUE; + occupation = 0; /* do this early, so newuhs() knows we're done */ + newuhs(FALSE); + if (nomovemsg) { + if (message) pline(nomovemsg); + nomovemsg = 0; + } else if (message) + You("finish eating %s.", food_xname(victual.piece, TRUE)); + + if(victual.piece->otyp == CORPSE) + cpostfx(victual.piece->corpsenm); + else + fpostfx(victual.piece); + + if (carried(victual.piece)) useup(victual.piece); + else useupf(victual.piece, 1L); + victual.piece = (struct obj *) 0; + victual.fullwarn = victual.eating = victual.doreset = FALSE; +} + +STATIC_OVL boolean +maybe_cannibal(pm, allowmsg) +int pm; +boolean allowmsg; +{ + if (!CANNIBAL_ALLOWED() && your_race(&mons[pm])) { + if (allowmsg) { + if (Upolyd) + You("have a bad feeling deep inside."); + You("cannibal! You will regret this!"); + } + HAggravate_monster |= FROMOUTSIDE; + change_luck(-rn1(4,2)); /* -5..-2 */ + return TRUE; + } + return FALSE; +} + +STATIC_OVL void +cprefx(pm) +register int pm; +{ + (void) maybe_cannibal(pm,TRUE); + if (touch_petrifies(&mons[pm]) || pm == PM_MEDUSA) { + if (!Stone_resistance && + !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { + Sprintf(killer_buf, "tasting %s meat", mons[pm].mname); + killer_format = KILLED_BY; + killer = killer_buf; + You("turn to stone."); + done(STONING); + if (victual.piece) + victual.eating = FALSE; + return; /* lifesaved */ + } + } + + switch(pm) { + case PM_LITTLE_DOG: + case PM_DOG: + case PM_LARGE_DOG: + case PM_KITTEN: + case PM_HOUSECAT: + case PM_LARGE_CAT: + if (!CANNIBAL_ALLOWED()) { + You_feel("that eating the %s was a bad idea.", mons[pm].mname); + HAggravate_monster |= FROMOUTSIDE; + } + break; + case PM_LIZARD: + if (Stoned) fix_petrification(); + break; + case PM_DEATH: + case PM_PESTILENCE: + case PM_FAMINE: + { char buf[BUFSZ]; + pline("Eating that is instantly fatal."); + Sprintf(buf, "unwisely ate the body of %s", + mons[pm].mname); + killer = buf; + killer_format = NO_KILLER_PREFIX; + done(DIED); + /* It so happens that since we know these monsters */ + /* cannot appear in tins, victual.piece will always */ + /* be what we want, which is not generally true. */ + if (revive_corpse(victual.piece)) + victual.piece = (struct obj *)0; + return; + } + case PM_GREEN_SLIME: + if (!Slimed && !Unchanging && !flaming(youmonst.data) && + youmonst.data != &mons[PM_GREEN_SLIME]) { + You("don't feel very well."); + Slimed = 10L; + flags.botl = 1; + } + /* Fall through */ + default: + if (acidic(&mons[pm]) && Stoned) + fix_petrification(); + break; + } +} + +void +fix_petrification() +{ + Stoned = 0; + delayed_killer = 0; + if (Hallucination) + pline("What a pity - you just ruined a future piece of %sart!", + ACURR(A_CHA) > 15 ? "fine " : ""); + else + You_feel("limber!"); +} + +/* + * If you add an intrinsic that can be gotten by eating a monster, add it + * to intrinsic_possible() and givit(). (It must already be in prop.h to + * be an intrinsic property.) + * It would be very easy to make the intrinsics not try to give you one + * that you already had by checking to see if you have it in + * intrinsic_possible() instead of givit(). + */ + +/* intrinsic_possible() returns TRUE iff a monster can give an intrinsic. */ +STATIC_OVL int +intrinsic_possible(type, ptr) +int type; +register struct permonst *ptr; +{ + switch (type) { + case FIRE_RES: +#ifdef DEBUG + if (ptr->mconveys & MR_FIRE) { + debugpline("can get fire resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_FIRE); +#endif + case SLEEP_RES: +#ifdef DEBUG + if (ptr->mconveys & MR_SLEEP) { + debugpline("can get sleep resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_SLEEP); +#endif + case COLD_RES: +#ifdef DEBUG + if (ptr->mconveys & MR_COLD) { + debugpline("can get cold resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_COLD); +#endif + case DISINT_RES: +#ifdef DEBUG + if (ptr->mconveys & MR_DISINT) { + debugpline("can get disintegration resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_DISINT); +#endif + case SHOCK_RES: /* shock (electricity) resistance */ +#ifdef DEBUG + if (ptr->mconveys & MR_ELEC) { + debugpline("can get shock resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_ELEC); +#endif + case POISON_RES: +#ifdef DEBUG + if (ptr->mconveys & MR_POISON) { + debugpline("can get poison resistance"); + return(TRUE); + } else return(FALSE); +#else + return(ptr->mconveys & MR_POISON); +#endif + case TELEPORT: +#ifdef DEBUG + if (can_teleport(ptr)) { + debugpline("can get teleport"); + return(TRUE); + } else return(FALSE); +#else + return(can_teleport(ptr)); +#endif + case TELEPORT_CONTROL: +#ifdef DEBUG + if (control_teleport(ptr)) { + debugpline("can get teleport control"); + return(TRUE); + } else return(FALSE); +#else + return(control_teleport(ptr)); +#endif + case TELEPAT: +#ifdef DEBUG + if (telepathic(ptr)) { + debugpline("can get telepathy"); + return(TRUE); + } else return(FALSE); +#else + return(telepathic(ptr)); +#endif + default: + return(FALSE); + } + /*NOTREACHED*/ +} + +/* givit() tries to give you an intrinsic based on the monster's level + * and what type of intrinsic it is trying to give you. + */ +STATIC_OVL void +givit(type, ptr) +int type; +register struct permonst *ptr; +{ + register int chance; + +#ifdef DEBUG + debugpline("Attempting to give intrinsic %d", type); +#endif + /* some intrinsics are easier to get than others */ + switch (type) { + case POISON_RES: + if ((ptr == &mons[PM_KILLER_BEE] || + ptr == &mons[PM_SCORPION]) && !rn2(4)) + chance = 1; + else + chance = 15; + break; + case TELEPORT: + chance = 10; + break; + case TELEPORT_CONTROL: + chance = 12; + break; + case TELEPAT: + chance = 1; + break; + default: + chance = 15; + break; + } + + if (ptr->mlevel <= rn2(chance)) + return; /* failed die roll */ + + switch (type) { + case FIRE_RES: +#ifdef DEBUG + debugpline("Trying to give fire resistance"); +#endif + if(!(HFire_resistance & FROMOUTSIDE)) { + You(Hallucination ? "be chillin'." : + "feel a momentary chill."); + HFire_resistance |= FROMOUTSIDE; + } + break; + case SLEEP_RES: +#ifdef DEBUG + debugpline("Trying to give sleep resistance"); +#endif + if(!(HSleep_resistance & FROMOUTSIDE)) { + You_feel("wide awake."); + HSleep_resistance |= FROMOUTSIDE; + } + break; + case COLD_RES: +#ifdef DEBUG + debugpline("Trying to give cold resistance"); +#endif + if(!(HCold_resistance & FROMOUTSIDE)) { + You_feel("full of hot air."); + HCold_resistance |= FROMOUTSIDE; + } + break; + case DISINT_RES: +#ifdef DEBUG + debugpline("Trying to give disintegration resistance"); +#endif + if(!(HDisint_resistance & FROMOUTSIDE)) { + You_feel(Hallucination ? + "totally together, man." : + "very firm."); + HDisint_resistance |= FROMOUTSIDE; + } + break; + case SHOCK_RES: /* shock (electricity) resistance */ +#ifdef DEBUG + debugpline("Trying to give shock resistance"); +#endif + if(!(HShock_resistance & FROMOUTSIDE)) { + if (Hallucination) + You_feel("grounded in reality."); + else + Your("health currently feels amplified!"); + HShock_resistance |= FROMOUTSIDE; + } + break; + case POISON_RES: +#ifdef DEBUG + debugpline("Trying to give poison resistance"); +#endif + if(!(HPoison_resistance & FROMOUTSIDE)) { + You_feel(Poison_resistance ? + "especially healthy." : "healthy."); + HPoison_resistance |= FROMOUTSIDE; + } + break; + case TELEPORT: +#ifdef DEBUG + debugpline("Trying to give teleport"); +#endif + if(!(HTeleportation & FROMOUTSIDE)) { + You_feel(Hallucination ? "diffuse." : + "very jumpy."); + HTeleportation |= FROMOUTSIDE; + } + break; + case TELEPORT_CONTROL: +#ifdef DEBUG + debugpline("Trying to give teleport control"); +#endif + if(!(HTeleport_control & FROMOUTSIDE)) { + You_feel(Hallucination ? + "centered in your personal space." : + "in control of yourself."); + HTeleport_control |= FROMOUTSIDE; + } + break; + case TELEPAT: +#ifdef DEBUG + debugpline("Trying to give telepathy"); +#endif + if(!(HTelepat & FROMOUTSIDE)) { + You_feel(Hallucination ? + "in touch with the cosmos." : + "a strange mental acuity."); + HTelepat |= FROMOUTSIDE; + /* If blind, make sure monsters show up. */ + if (Blind) see_monsters(); + } + break; + default: +#ifdef DEBUG + debugpline("Tried to give an impossible intrinsic"); +#endif + break; + } +} + +STATIC_OVL void +cpostfx(pm) /* called after completely consuming a corpse */ +register int pm; +{ + register int tmp = 0; + boolean catch_lycanthropy = FALSE; + + /* in case `afternmv' didn't get called for previously mimicking + gold, clean up now to avoid `eatmbuf' memory leak */ + if (eatmbuf) (void)eatmdone(); + + switch(pm) { + case PM_NEWT: + /* MRKR: "eye of newt" may give small magical energy boost */ + if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) { + int old_uen = u.uen; + u.uen += rnd(3); + if (u.uen > u.uenmax) { + if (!rn2(3)) u.uenmax++; + u.uen = u.uenmax; + } + if (old_uen != u.uen) { + You_feel("a mild buzz."); + flags.botl = 1; + } + } + break; + case PM_WRAITH: + pluslvl(FALSE); + break; + case PM_HUMAN_WERERAT: + catch_lycanthropy = TRUE; + u.ulycn = PM_WERERAT; + break; + case PM_HUMAN_WEREJACKAL: + catch_lycanthropy = TRUE; + u.ulycn = PM_WEREJACKAL; + break; + case PM_HUMAN_WEREWOLF: + catch_lycanthropy = TRUE; + u.ulycn = PM_WEREWOLF; + break; + case PM_NURSE: + if (Upolyd) u.mh = u.mhmax; + else u.uhp = u.uhpmax; + flags.botl = 1; + break; + case PM_STALKER: + if(!Invis) { + set_itimeout(&HInvis, (long)rn1(100, 50)); + if (!Blind && !BInvis) self_invis_message(); + } else { + if (!(HInvis & INTRINSIC)) You_feel("hidden!"); + HInvis |= FROMOUTSIDE; + HSee_invisible |= FROMOUTSIDE; + } + newsym(u.ux, u.uy); + /* fall into next case */ + case PM_YELLOW_LIGHT: + /* fall into next case */ + case PM_GIANT_BAT: + make_stunned(HStun + 30,FALSE); + /* fall into next case */ + case PM_BAT: + make_stunned(HStun + 30,FALSE); + break; + case PM_GIANT_MIMIC: + tmp += 10; + /* fall into next case */ + case PM_LARGE_MIMIC: + tmp += 20; + /* fall into next case */ + case PM_SMALL_MIMIC: + tmp += 20; + if (youmonst.data->mlet != S_MIMIC && !Unchanging) { + char buf[BUFSZ]; + You_cant("resist the temptation to mimic %s.", + Hallucination ? "an orange" : "a pile of gold"); +#ifdef STEED + /* A pile of gold can't ride. */ + if (u.usteed) dismount_steed(DISMOUNT_FELL); +#endif + nomul(-tmp); + Sprintf(buf, Hallucination ? + "You suddenly dread being peeled and mimic %s again!" : + "You now prefer mimicking %s again.", + an(Upolyd ? youmonst.data->mname : urace.noun)); + eatmbuf = strcpy((char *) alloc(strlen(buf) + 1), buf); + nomovemsg = eatmbuf; + afternmv = eatmdone; + /* ??? what if this was set before? */ + youmonst.m_ap_type = M_AP_OBJECT; + youmonst.mappearance = Hallucination ? ORANGE : GOLD_PIECE; + newsym(u.ux,u.uy); + curs_on_u(); + /* make gold symbol show up now */ + display_nhwindow(WIN_MAP, TRUE); + } + break; + case PM_QUANTUM_MECHANIC: + Your("velocity suddenly seems very uncertain!"); + if (HFast & INTRINSIC) { + HFast &= ~INTRINSIC; + You("seem slower."); + } else { + HFast |= FROMOUTSIDE; + You("seem faster."); + } + break; + case PM_LIZARD: + if (HStun > 2) make_stunned(2L,FALSE); + if (HConfusion > 2) make_confused(2L,FALSE); + break; + case PM_CHAMELEON: + case PM_DOPPELGANGER: + /* case PM_SANDESTIN: */ + if (!Unchanging) { + You_feel("a change coming over you."); + polyself(FALSE); + } + break; + case PM_MIND_FLAYER: + case PM_MASTER_MIND_FLAYER: + if (ABASE(A_INT) < ATTRMAX(A_INT)) { + if (!rn2(2)) { + pline("Yum! That was real brain food!"); + (void) adjattrib(A_INT, 1, FALSE); + break; /* don't give them telepathy, too */ + } + } + else { + pline("For some reason, that tasted bland."); + } + /* fall through to default case */ + default: { + register struct permonst *ptr = &mons[pm]; + int i, count; + + if (dmgtype(ptr, AD_STUN) || dmgtype(ptr, AD_HALU) || + pm == PM_VIOLET_FUNGUS) { + pline ("Oh wow! Great stuff!"); + make_hallucinated(HHallucination + 200,FALSE,0L); + } + if(is_giant(ptr)) gainstr((struct obj *)0, 0); + + /* Check the monster for all of the intrinsics. If this + * monster can give more than one, pick one to try to give + * from among all it can give. + * + * If a monster can give 4 intrinsics then you have + * a 1/1 * 1/2 * 2/3 * 3/4 = 1/4 chance of getting the first, + * a 1/2 * 2/3 * 3/4 = 1/4 chance of getting the second, + * a 1/3 * 3/4 = 1/4 chance of getting the third, + * and a 1/4 chance of getting the fourth. + * + * And now a proof by induction: + * it works for 1 intrinsic (1 in 1 of getting it) + * for 2 you have a 1 in 2 chance of getting the second, + * otherwise you keep the first + * for 3 you have a 1 in 3 chance of getting the third, + * otherwise you keep the first or the second + * for n+1 you have a 1 in n+1 chance of getting the (n+1)st, + * otherwise you keep the previous one. + * Elliott Kleinrock, October 5, 1990 + */ + + count = 0; /* number of possible intrinsics */ + tmp = 0; /* which one we will try to give */ + for (i = 1; i <= LAST_PROP; i++) { + if (intrinsic_possible(i, ptr)) { + count++; + /* a 1 in count chance of replacing the old + * one with this one, and a count-1 in count + * chance of keeping the old one. (note + * that 1 in 1 and 0 in 1 are what we want + * for the first one + */ + if (!rn2(count)) { +#ifdef DEBUG + debugpline("Intrinsic %d replacing %d", + i, tmp); +#endif + tmp = i; + } + } + } + + /* if any found try to give them one */ + if (count) givit(tmp, ptr); + } + break; + } + + if (catch_lycanthropy && defends(AD_WERE, uwep)) { + if (!touch_artifact(uwep, &youmonst)) { + dropx(uwep); + uwepgone(); + } + } + + return; +} + +void +violated_vegetarian() +{ + u.uconduct.unvegetarian++; + if (Role_if(PM_MONK)) { + You_feel("guilty."); + adjalign(-1); + } + return; +} + +/* common code to check and possibly charge for 1 context.tin.tin, + * will split() context.tin.tin if necessary */ +STATIC_PTR +void +costly_tin(verb) + const char* verb; /* if 0, the verb is "open" */ +{ + if(((!carried(tin.tin) && + costly_spot(tin.tin->ox, tin.tin->oy) && + !tin.tin->no_charge) + || tin.tin->unpaid)) { + verbalize("You %s it, you bought it!", verb ? verb : "open"); + if(tin.tin->quan > 1L) tin.tin = splitobj(tin.tin, 1L); + bill_dummy_object(tin.tin); + } +} + +STATIC_PTR +int +opentin() /* called during each move whilst opening a tin */ +{ + register int r; + const char *what; + int which; + + if(!carried(tin.tin) && !obj_here(tin.tin, u.ux, u.uy)) + /* perhaps it was stolen? */ + return(0); /* %% probably we should use tinoid */ + if(tin.usedtime++ >= 50) { + You("give up your attempt to open the tin."); + return(0); + } + if(tin.usedtime < tin.reqtime) + return(1); /* still busy */ + if(tin.tin->otrapped || + (tin.tin->cursed && tin.tin->spe != -1 && !rn2(8))) { + b_trapped("tin", 0); + costly_tin("destroyed"); + goto use_me; + } + You("succeed in opening the tin."); + if(tin.tin->spe != 1) { + if (tin.tin->corpsenm == NON_PM) { + pline("It turns out to be empty."); + tin.tin->dknown = tin.tin->known = TRUE; + costly_tin((const char*)0); + goto use_me; + } + r = tin.tin->cursed ? ROTTEN_TIN : /* always rotten if cursed */ + (tin.tin->spe == -1) ? HOMEMADE_TIN : /* player made it */ + rn2(TTSZ-1); /* else take your pick */ + if (r == ROTTEN_TIN && (tin.tin->corpsenm == PM_LIZARD || + tin.tin->corpsenm == PM_LICHEN)) + r = HOMEMADE_TIN; /* lizards don't rot */ + else if (tin.tin->spe == -1 && !tin.tin->blessed && !rn2(7)) + r = ROTTEN_TIN; /* some homemade tins go bad */ + which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */ + if (Hallucination) { + what = rndmonnam(); + } else { + what = mons[tin.tin->corpsenm].mname; + if (mons[tin.tin->corpsenm].geno & G_UNIQ) + which = type_is_pname(&mons[tin.tin->corpsenm]) ? 1 : 2; + } + if (which == 0) what = makeplural(what); + pline("It smells like %s%s.", (which == 2) ? "the " : "", what); + if (yn("Eat it?") == 'n') { + if (!Hallucination) tin.tin->dknown = tin.tin->known = TRUE; + if (flags.verbose) You("discard the open tin."); + costly_tin((const char*)0); + goto use_me; + } + /* in case stop_occupation() was called on previous meal */ + victual.piece = (struct obj *)0; + victual.fullwarn = victual.eating = victual.doreset = FALSE; + + You("consume %s %s.", tintxts[r].txt, + mons[tin.tin->corpsenm].mname); + + /* KMH, conduct */ + u.uconduct.food++; + if (!vegan(&mons[tin.tin->corpsenm])) + u.uconduct.unvegan++; + if (!vegetarian(&mons[tin.tin->corpsenm])) + violated_vegetarian(); + + tin.tin->dknown = tin.tin->known = TRUE; + cprefx(tin.tin->corpsenm); cpostfx(tin.tin->corpsenm); + + /* charge for one at pre-eating cost */ + costly_tin((const char*)0); + + /* check for vomiting added by GAN 01/16/87 */ + if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE); + else lesshungry(tintxts[r].nut); + + if(r == 0 || r == FRENCH_FRIED_TIN) { + /* Assume !Glib, because you can't open tins when Glib. */ + incr_itimeout(&Glib, rnd(15)); + pline("Eating deep fried food made your %s very slippery.", + makeplural(body_part(FINGER))); + } + } else { + if (tin.tin->cursed) + pline("It contains some decaying%s%s substance.", + Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN)); + else + pline("It contains spinach."); + + if (yn("Eat it?") == 'n') { + if (!Hallucination && !tin.tin->cursed) + tin.tin->dknown = tin.tin->known = TRUE; + if (flags.verbose) + You("discard the open tin."); + costly_tin((const char*)0); + goto use_me; + } + + tin.tin->dknown = tin.tin->known = TRUE; + costly_tin((const char*)0); + + if (!tin.tin->cursed) + pline("This makes you feel like %s!", + Hallucination ? "Swee'pea" : "Popeye"); + lesshungry(600); + gainstr(tin.tin, 0); + u.uconduct.food++; + } +use_me: + if (carried(tin.tin)) useup(tin.tin); + else useupf(tin.tin, 1L); + tin.tin = (struct obj *) 0; + return(0); +} + +STATIC_OVL void +start_tin(otmp) /* called when starting to open a tin */ + register struct obj *otmp; +{ + register int tmp; + + if (metallivorous(youmonst.data)) { + You("bite right into the metal tin..."); + tmp = 1; + } else if (nolimbs(youmonst.data)) { + You("cannot handle the tin properly to open it."); + return; + } else if (otmp->blessed) { + pline_The("tin opens like magic!"); + tmp = 1; + } else if(uwep) { + switch(uwep->otyp) { + case TIN_OPENER: + tmp = 1; + break; + case DAGGER: + case SILVER_DAGGER: + case ELVEN_DAGGER: + case ORCISH_DAGGER: + case ATHAME: + case CRYSKNIFE: + tmp = 3; + break; + case PICK_AXE: + case AXE: + tmp = 6; + break; + default: + goto no_opener; + } + pline("Using your %s you try to open the tin.", + aobjnam(uwep, (char *)0)); + } else { +no_opener: + pline("It is not so easy to open this tin."); + if(Glib) { + pline_The("tin slips from your %s.", + makeplural(body_part(FINGER))); + if(otmp->quan > 1L) { + otmp = splitobj(otmp, 1L); + } + if (carried(otmp)) dropx(otmp); + else stackobj(otmp); + return; + } + tmp = rn1(1 + 500/((int)(ACURR(A_DEX) + ACURRSTR)), 10); + } + tin.reqtime = tmp; + tin.usedtime = 0; + tin.tin = otmp; + set_occupation(opentin, "opening the tin", 0); + return; +} + +int +Hear_again() /* called when waking up after fainting */ +{ + flags.soundok = 1; + return 0; +} + +/* called on the "first bite" of rotten food */ +STATIC_OVL int +rottenfood(obj) +struct obj *obj; +{ + pline("Blecch! Rotten %s!", foodword(obj)); + if(!rn2(4)) { + if (Hallucination) You_feel("rather trippy."); + else You_feel("rather %s.", body_part(LIGHT_HEADED)); + make_confused(HConfusion + d(2,4),FALSE); + } else if(!rn2(4) && !Blind) { + pline("Everything suddenly goes dark."); + make_blinded((long)d(2,10),FALSE); + if (!Blind) Your(vision_clears); + } else if(!rn2(3)) { + const char *what, *where; + if (!Blind) + what = "goes", where = "dark"; + else if (Levitation || Is_airlevel(&u.uz) || + Is_waterlevel(&u.uz)) + what = "you lose control of", where = "yourself"; + else + what = "you slap against the", where = +#ifdef STEED + (u.usteed) ? "saddle" : +#endif + surface(u.ux,u.uy); + pline_The("world spins and %s %s.", what, where); + flags.soundok = 0; + nomul(-rnd(10)); + nomovemsg = "You are conscious again."; + afternmv = Hear_again; + return(1); + } + return(0); +} + +STATIC_OVL int +eatcorpse(otmp) /* called when a corpse is selected as food */ + register struct obj *otmp; +{ + int tp = 0, mnum = otmp->corpsenm; + long rotted = 0L; + boolean uniq = !!(mons[mnum].geno & G_UNIQ); + int retcode = 0; + boolean stoneable = (touch_petrifies(&mons[mnum]) && !Stone_resistance && + !poly_when_stoned(youmonst.data)); + + /* KMH, conduct */ + if (!vegan(&mons[mnum])) u.uconduct.unvegan++; + if (!vegetarian(&mons[mnum])) violated_vegetarian(); + + if (mnum != PM_LIZARD && mnum != PM_LICHEN) { + long age = peek_at_iced_corpse_age(otmp); + + rotted = (monstermoves - age)/(10L + rn2(20)); + if (otmp->cursed) rotted += 2L; + else if (otmp->blessed) rotted -= 2L; + } + + if (mnum != PM_ACID_BLOB && !stoneable && rotted > 5L) { + boolean cannibal = maybe_cannibal(mnum, FALSE); + pline("Ulch - that %s was tainted%s!", + mons[mnum].mlet == S_FUNGUS ? "fungoid vegetation" : + !vegetarian(&mons[mnum]) ? "meat" : "protoplasm", + cannibal ? " cannibal" : ""); + if (Sick_resistance) { + pline("It doesn't seem at all sickening, though..."); + } else { + char buf[BUFSZ]; + long sick_time; + + sick_time = (long) rn1(10, 10); + /* make sure new ill doesn't result in improvement */ + if (Sick && (sick_time > Sick)) + sick_time = (Sick > 1L) ? Sick - 1L : 1L; + if (!uniq) + Sprintf(buf, "rotted %s", corpse_xname(otmp,TRUE)); + else + Sprintf(buf, "%s%s rotted corpse", + !type_is_pname(&mons[mnum]) ? "the " : "", + s_suffix(mons[mnum].mname)); + make_sick(sick_time, buf, TRUE, SICK_VOMITABLE); + } + if (carried(otmp)) useup(otmp); + else useupf(otmp, 1L); + return(2); + } else if (acidic(&mons[mnum]) && !Acid_resistance) { + tp++; + You("have a very bad case of stomach acid."); /* not body_part() */ + losehp(rnd(15), "acidic corpse", KILLED_BY_AN); + } else if (poisonous(&mons[mnum]) && rn2(5)) { + tp++; + pline("Ecch - that must have been poisonous!"); + if(!Poison_resistance) { + losestr(rnd(4)); + losehp(rnd(15), "poisonous corpse", KILLED_BY_AN); + } else You("seem unaffected by the poison."); + /* now any corpse left too long will make you mildly ill */ + } else if ((rotted > 5L || (rotted > 3L && rn2(5))) + && !Sick_resistance) { + tp++; + You_feel("%ssick.", (Sick) ? "very " : ""); + losehp(rnd(8), "cadaver", KILLED_BY_AN); + } + + /* delay is weight dependent */ + victual.reqtime = 3 + (mons[mnum].cwt >> 6); + + if (!tp && mnum != PM_LIZARD && mnum != PM_LICHEN && + (otmp->orotten || !rn2(7))) { + if (rottenfood(otmp)) { + otmp->orotten = TRUE; + (void)touchfood(otmp); + retcode = 1; + } + + if (!mons[otmp->corpsenm].cnutrit) { + /* no nutrution: rots away, no message if you passed out */ + if (!retcode) pline_The("corpse rots away completely."); + if (carried(otmp)) useup(otmp); + else useupf(otmp, 1L); + retcode = 2; + } + + if (!retcode) consume_oeaten(otmp, 2); /* oeaten >>= 2 */ + } else { + pline("%s%s %s!", + !uniq ? "This " : !type_is_pname(&mons[mnum]) ? "The " : "", + food_xname(otmp, FALSE), + (vegan(&mons[mnum]) ? + (!carnivorous(youmonst.data) && herbivorous(youmonst.data)) : + (carnivorous(youmonst.data) && !herbivorous(youmonst.data))) + ? "is delicious" : "tastes terrible"); + } + + return(retcode); +} + +STATIC_OVL void +start_eating(otmp) /* called as you start to eat */ + register struct obj *otmp; +{ +#ifdef DEBUG + debugpline("start_eating: %lx (victual = %lx)", otmp, victual.piece); + debugpline("reqtime = %d", victual.reqtime); + debugpline("(original reqtime = %d)", objects[otmp->otyp].oc_delay); + debugpline("nmod = %d", victual.nmod); + debugpline("oeaten = %d", otmp->oeaten); +#endif + victual.fullwarn = victual.doreset = FALSE; + victual.eating = TRUE; + + if (otmp->otyp == CORPSE) { + cprefx(victual.piece->corpsenm); + if (!victual.piece || !victual.eating) { + /* rider revived, or died and lifesaved */ + return; + } + } + + if (bite()) return; + + if (++victual.usedtime >= victual.reqtime) { + /* print "finish eating" message if they just resumed -dlc */ + done_eating(victual.reqtime > 1 ? TRUE : FALSE); + return; + } + + Sprintf(msgbuf, "eating %s", food_xname(otmp, TRUE)); + set_occupation(eatfood, msgbuf, 0); +} + + +/* + * called on "first bite" of (non-corpse) food. + * used for non-rotten non-tin non-corpse food + */ +STATIC_OVL void +fprefx(otmp) +struct obj *otmp; +{ + switch(otmp->otyp) { + case FOOD_RATION: + if(u.uhunger <= 200) + pline(Hallucination ? "Oh wow, like, superior, man!" : + "That food really hit the spot!"); + else if(u.uhunger <= 700) pline("That satiated your %s!", + body_part(STOMACH)); + break; + case TRIPE_RATION: + if (carnivorous(youmonst.data) && !humanoid(youmonst.data)) + pline("That tripe ration was surprisingly good!"); + else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC))) + pline(Hallucination ? "Tastes great! Less filling!" : + "Mmm, tripe... not bad!"); + else { + pline("Yak - dog food!"); + more_experienced(1,0); + newexplevel(); + /* not cannibalism, but we use similar criteria + for deciding whether to be sickened by this meal */ + if (rn2(2) && !CANNIBAL_ALLOWED()) + make_vomiting((long)rn1(victual.reqtime, 14), FALSE); + } + break; + case MEATBALL: + case MEAT_STICK: + case HUGE_CHUNK_OF_MEAT: + case MEAT_RING: + goto give_feedback; + /* break; */ + case CLOVE_OF_GARLIC: + if (is_undead(youmonst.data)) { + make_vomiting((long)rn1(victual.reqtime, 5), FALSE); + break; + } + /* Fall through otherwise */ + default: + if (otmp->otyp==SLIME_MOLD && !otmp->cursed + && otmp->spe == current_fruit) + pline("My, that was a %s %s!", + Hallucination ? "primo" : "yummy", + singular(otmp, xname)); + else +#ifdef UNIX + if (otmp->otyp == APPLE || otmp->otyp == PEAR) { + if (!Hallucination) pline("Core dumped."); + else { +/* This is based on an old Usenet joke, a fake a.out manual page */ + int x = rnd(100); + if (x <= 75) + pline("Segmentation fault -- core dumped."); + else if (x <= 99) + pline("Bus error -- core dumped."); + else pline("Yo' mama -- core dumped."); + } + } else +#endif +#ifdef MAC /* KMH -- Why should Unix have all the fun? */ + if (otmp->otyp == APPLE) { + pline("Delicious! Must be a Macintosh!"); + } else +#endif + if (otmp->otyp == EGG && stale_egg(otmp)) { + pline("Ugh. Rotten egg."); /* perhaps others like it */ + make_vomiting(Vomiting+d(10,4), TRUE); + } else + give_feedback: + pline("This %s is %s", singular(otmp, xname), + otmp->cursed ? (Hallucination ? "grody!" : "terrible!") : + (otmp->otyp == CRAM_RATION + || otmp->otyp == K_RATION + || otmp->otyp == C_RATION) + ? "bland." : + Hallucination ? "gnarly!" : "delicious!"); + break; + } +} + +STATIC_OVL void +accessory_has_effect(otmp) +struct obj *otmp; +{ + pline("Magic spreads through your body as you digest the %s.", + otmp->oclass == RING_CLASS ? "ring" : "amulet"); +} + +STATIC_OVL void +eataccessory(otmp) +struct obj *otmp; +{ + int typ = otmp->otyp; + long oldprop; + + /* Note: rings are not so common that this is unbalancing. */ + /* (How often do you even _find_ 3 rings of polymorph in a game?) */ + oldprop = u.uprops[objects[typ].oc_oprop].intrinsic; + if (otmp == uleft || otmp == uright) { + Ring_gone(otmp); + if (u.uhp <= 0) return; /* died from sink fall */ + } + otmp->known = otmp->dknown = 1; /* by taste */ + if (!rn2(otmp->oclass == RING_CLASS ? 3 : 5)) { + switch (otmp->otyp) { + default: + if (!objects[typ].oc_oprop) break; /* should never happen */ + + if (!(u.uprops[objects[typ].oc_oprop].intrinsic & FROMOUTSIDE)) + accessory_has_effect(otmp); + + u.uprops[objects[typ].oc_oprop].intrinsic |= FROMOUTSIDE; + + switch (typ) { + case RIN_SEE_INVISIBLE: + set_mimic_blocking(); + see_monsters(); + if (Invis && !oldprop && !ESee_invisible && + !perceives(youmonst.data) && !Blind) { + newsym(u.ux,u.uy); + pline("Suddenly you can see yourself."); + makeknown(typ); + } + break; + case RIN_INVISIBILITY: + if (!oldprop && !EInvis && !BInvis && + !See_invisible && !Blind) { + newsym(u.ux,u.uy); + Your("body takes on a %s transparency...", + Hallucination ? "normal" : "strange"); + makeknown(typ); + } + break; + case RIN_PROTECTION_FROM_SHAPE_CHAN: + rescham(); + break; + case RIN_LEVITATION: + /* undo the `.intrinsic |= FROMOUTSIDE' done above */ + u.uprops[LEVITATION].intrinsic = oldprop; + if (!Levitation) { + float_up(); + incr_itimeout(&HLevitation, d(10,20)); + makeknown(typ); + } + break; + } + break; + case RIN_ADORNMENT: + accessory_has_effect(otmp); + if (adjattrib(A_CHA, otmp->spe, -1)) + makeknown(typ); + break; + case RIN_GAIN_STRENGTH: + accessory_has_effect(otmp); + if (adjattrib(A_STR, otmp->spe, -1)) + makeknown(typ); + break; + case RIN_GAIN_CONSTITUTION: + accessory_has_effect(otmp); + if (adjattrib(A_CON, otmp->spe, -1)) + makeknown(typ); + break; + case RIN_INCREASE_ACCURACY: + accessory_has_effect(otmp); + u.uhitinc += otmp->spe; + break; + case RIN_INCREASE_DAMAGE: + accessory_has_effect(otmp); + u.udaminc += otmp->spe; + break; + case RIN_PROTECTION: + accessory_has_effect(otmp); + HProtection |= FROMOUTSIDE; + u.ublessed += otmp->spe; + flags.botl = 1; + break; + case RIN_FREE_ACTION: + /* Give sleep resistance instead */ + if (!(HSleep_resistance & FROMOUTSIDE)) + accessory_has_effect(otmp); + if (!Sleep_resistance) + You_feel("wide awake."); + HSleep_resistance |= FROMOUTSIDE; + break; + case AMULET_OF_CHANGE: + accessory_has_effect(otmp); + makeknown(typ); + change_sex(); + You("are suddenly very %s!", + flags.female ? "feminine" : "masculine"); + flags.botl = 1; + break; + case AMULET_OF_UNCHANGING: + /* un-change: it's a pun */ + if (!Unchanging && Upolyd) { + accessory_has_effect(otmp); + makeknown(typ); + rehumanize(); + } + break; + case AMULET_OF_STRANGULATION: /* bad idea! */ + /* no message--this gives no permanent effect */ + choke(otmp); + break; + case AMULET_OF_RESTFUL_SLEEP: /* another bad idea! */ + if (!(HSleeping & FROMOUTSIDE)) + accessory_has_effect(otmp); + HSleeping = FROMOUTSIDE | rnd(100); + break; + case RIN_SUSTAIN_ABILITY: + case AMULET_OF_LIFE_SAVING: + case AMULET_OF_REFLECTION: /* nice try */ + /* can't eat Amulet of Yendor or fakes, + * and no oc_prop even if you could -3. + */ + break; + } + } +} + +STATIC_OVL void +eatspecial() /* called after eating non-food */ +{ + register struct obj *otmp = victual.piece; + + /* lesshungry wants an occupation to handle choke messages correctly */ + set_occupation(eatfood, "eating non-food", 0); + lesshungry(victual.nmod); + occupation = 0; + victual.piece = (struct obj *)0; + victual.eating = 0; + if (otmp->oclass == COIN_CLASS) { +#ifdef GOLDOBJ + if (carried(otmp)) + useupall(otmp); +#else + if (otmp->where == OBJ_FREE) + dealloc_obj(otmp); +#endif + else + useupf(otmp, otmp->quan); + return; + } + if (otmp->oclass == POTION_CLASS) { + otmp->quan++; /* dopotion() does a useup() */ + (void)dopotion(otmp); + } + if (otmp->oclass == RING_CLASS || otmp->oclass == AMULET_CLASS) + eataccessory(otmp); + else if (otmp->otyp == LEASH && otmp->leashmon) + o_unleash(otmp); + + /* KMH -- idea by "Tommy the Terrorist" */ + if ((otmp->otyp == TRIDENT) && !otmp->cursed) + { + pline(Hallucination ? "Four out of five dentists agree." : + "That was pure chewing satisfaction!"); + exercise(A_WIS, TRUE); + } + if ((otmp->otyp == FLINT) && !otmp->cursed) + { + pline("Yabba-dabba delicious!"); + exercise(A_CON, TRUE); + } + + if (otmp == uwep && otmp->quan == 1L) uwepgone(); + if (otmp == uquiver && otmp->quan == 1L) uqwepgone(); + if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone(); + + if (otmp == uball) unpunish(); + if (otmp == uchain) unpunish(); /* but no useup() */ + else if (carried(otmp)) useup(otmp); + else useupf(otmp, 1L); +} + +/* NOTE: the order of these words exactly corresponds to the + order of oc_material values #define'd in objclass.h. */ +static const char *foodwords[] = { + "meal", "liquid", "wax", "food", "meat", + "paper", "cloth", "leather", "wood", "bone", "scale", + "metal", "metal", "metal", "silver", "gold", "platinum", "mithril", + "plastic", "glass", "rich food", "stone" +}; + +STATIC_OVL const char * +foodword(otmp) +register struct obj *otmp; +{ + if (otmp->oclass == FOOD_CLASS) return "food"; + if (otmp->oclass == GEM_CLASS && + objects[otmp->otyp].oc_material == GLASS && + otmp->dknown) + makeknown(otmp->otyp); + return foodwords[objects[otmp->otyp].oc_material]; +} + +STATIC_OVL void +fpostfx(otmp) /* called after consuming (non-corpse) food */ +register struct obj *otmp; +{ + switch(otmp->otyp) { + case SPRIG_OF_WOLFSBANE: + if (u.ulycn >= LOW_PM || is_were(youmonst.data)) + you_unwere(TRUE); + break; + case CARROT: + make_blinded((long)u.ucreamed,TRUE); + break; + case FORTUNE_COOKIE: + outrumor(bcsign(otmp), BY_COOKIE); + if (!Blind) u.uconduct.literate++; + break; + case LUMP_OF_ROYAL_JELLY: + /* This stuff seems to be VERY healthy! */ + gainstr(otmp, 1); + if (Upolyd) { + u.mh += otmp->cursed ? -rnd(20) : rnd(20); + if (u.mh > u.mhmax) { + if (!rn2(17)) u.mhmax++; + u.mh = u.mhmax; + } else if (u.mh <= 0) { + rehumanize(); + } + } else { + u.uhp += otmp->cursed ? -rnd(20) : rnd(20); + if (u.uhp > u.uhpmax) { + if(!rn2(17)) u.uhpmax++; + u.uhp = u.uhpmax; + } else if (u.uhp <= 0) { + killer_format = KILLED_BY_AN; + killer = "rotten lump of royal jelly"; + done(POISONING); + } + } + if(!otmp->cursed) heal_legs(); + break; + case EGG: + if (touch_petrifies(&mons[otmp->corpsenm])) { + if (!Stone_resistance && + !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { + if (!Stoned) Stoned = 5; + killer_format = KILLED_BY_AN; + Sprintf(killer_buf, "%s egg", mons[otmp->corpsenm].mname); + delayed_killer = killer_buf; + } + } + break; + case EUCALYPTUS_LEAF: + if (Sick && !otmp->cursed) + make_sick(0L, (char *)0, TRUE, SICK_ALL); + if (Vomiting && !otmp->cursed) + make_vomiting(0L, TRUE); + break; + } + return; +} + +/* + * return 0 if the food was not dangerous. + * return 1 if the food was dangerous and you chose to stop. + * return 2 if the food was dangerous and you chose to eat it anyway. + */ +STATIC_OVL int +edibility_prompts(otmp) +struct obj *otmp; +{ + /* blessed food detection granted you a one-use + ability to detect food that is unfit for consumption + or dangerous and avoid it. */ + + char buf[BUFSZ], foodsmell[BUFSZ], + it_or_they[QBUFSZ], eat_it_anyway[QBUFSZ]; + boolean cadaver = (otmp->otyp == CORPSE), + stoneorslime = FALSE; + int material = objects[otmp->otyp].oc_material, + mnum = otmp->corpsenm; + long rotted = 0L; + + Strcpy(foodsmell, Tobjnam(otmp, "smell")); + Strcpy(it_or_they, (otmp->quan == 1L) ? "it" : "they"); + Sprintf(eat_it_anyway, "Eat %s anyway?", + (otmp->quan == 1L) ? "it" : "one"); + + if (cadaver || otmp->otyp == EGG || otmp->otyp == TIN) { + /* These checks must match those in eatcorpse() */ + stoneorslime = (touch_petrifies(&mons[mnum]) && + !Stone_resistance && + !poly_when_stoned(youmonst.data)); + + if (mnum == PM_GREEN_SLIME) + stoneorslime = (!Unchanging && !flaming(youmonst.data) && + youmonst.data != &mons[PM_GREEN_SLIME]); + + if (cadaver && mnum != PM_LIZARD && mnum != PM_LICHEN) { + long age = peek_at_iced_corpse_age(otmp); + /* worst case rather than random + in this calculation to force prompt */ + rotted = (monstermoves - age)/(10L + 0 /* was rn2(20) */); + if (otmp->cursed) rotted += 2L; + else if (otmp->blessed) rotted -= 2L; + } + } + + /* + * These problems with food should be checked in + * order from most detrimental to least detrimental. + */ + + if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && !Sick_resistance) { + /* Tainted meat */ + Sprintf(buf, "%s like %s could be tainted! %s", + foodsmell, it_or_they, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (stoneorslime) { + Sprintf(buf, "%s like %s could be something very dangerous! %s", + foodsmell, it_or_they, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (otmp->orotten || (cadaver && rotted > 3L)) { + /* Rotten */ + Sprintf(buf, "%s like %s could be rotten! %s", + foodsmell, it_or_they, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (cadaver && poisonous(&mons[mnum]) && !Poison_resistance) { + /* poisonous */ + Sprintf(buf, "%s like %s might be poisonous! %s", + foodsmell, it_or_they, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (cadaver && !vegetarian(&mons[mnum]) && + !u.uconduct.unvegetarian && Role_if(PM_MONK)) { + Sprintf(buf, "%s unhealthy. %s", + foodsmell, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (cadaver && acidic(&mons[mnum]) && !Acid_resistance) { + Sprintf(buf, "%s rather acidic. %s", + foodsmell, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (Upolyd && u.umonnum == PM_RUST_MONSTER && + is_metallic(otmp) && otmp->oerodeproof) { + Sprintf(buf, "%s disgusting to you right now. %s", + foodsmell, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + + /* + * Breaks conduct, but otherwise safe. + */ + + if (!u.uconduct.unvegan && + ((material == LEATHER || material == BONE || + material == DRAGON_HIDE || material == WAX) || + (cadaver && !vegan(&mons[mnum])))) { + Sprintf(buf, "%s foul and unfamiliar to you. %s", + foodsmell, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + if (!u.uconduct.unvegetarian && + ((material == LEATHER || material == BONE || + material == DRAGON_HIDE) || + (cadaver && !vegetarian(&mons[mnum])))) { + Sprintf(buf, "%s unfamiliar to you. %s", + foodsmell, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + + if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && Sick_resistance) { + /* Tainted meat with Sick_resistance */ + Sprintf(buf, "%s like %s could be tainted! %s", + foodsmell, it_or_they, eat_it_anyway); + if (yn_function(buf,ynchars,'n')=='n') return 1; + else return 2; + } + return 0; +} + +int +doeat() /* generic "eat" command funtion (see cmd.c) */ +{ + register struct obj *otmp; + int basenutrit; /* nutrition of full item */ + boolean dont_start = FALSE; + + if (Strangled) { + pline("If you can't breathe air, how can you consume solids?"); + return 0; + } + if (!(otmp = floorfood("eat", 0))) return 0; + if (check_capacity((char *)0)) return 0; + + if (u.uedibility) { + int res = edibility_prompts(otmp); + if (res) { + Your("%s stops tingling and your sense of smell returns to normal.", + body_part(NOSE)); + u.uedibility = 0; + if (res == 1) return 0; + } + } + + /* We have to make non-foods take 1 move to eat, unless we want to + * do ridiculous amounts of coding to deal with partly eaten plate + * mails, players who polymorph back to human in the middle of their + * metallic meal, etc.... + */ + if (!is_edible(otmp)) { + You("cannot eat that!"); + return 0; + } else if ((otmp->owornmask & (W_ARMOR|W_TOOL|W_AMUL +#ifdef STEED + |W_SADDLE +#endif + )) != 0) { + /* let them eat rings */ + You_cant("eat %s you're wearing.", something); + return 0; + } + if (is_metallic(otmp) && + u.umonnum == PM_RUST_MONSTER && otmp->oerodeproof) { + otmp->rknown = TRUE; + if (otmp->quan > 1L) { + if(!carried(otmp)) + (void) splitobj(otmp, otmp->quan - 1L); + else + otmp = splitobj(otmp, 1L); + } + pline("Ulch - That %s was rustproofed!", xname(otmp)); + /* The regurgitated object's rustproofing is gone now */ + otmp->oerodeproof = 0; + make_stunned(HStun + rn2(10), TRUE); + You("spit %s out onto the %s.", the(xname(otmp)), + surface(u.ux, u.uy)); + if (carried(otmp)) { + freeinv(otmp); + dropy(otmp); + } + stackobj(otmp); + return 1; + } + /* KMH -- Slow digestion is... indigestible */ + if (otmp->otyp == RIN_SLOW_DIGESTION) { + pline("This ring is indigestible!"); + (void) rottenfood(otmp); + if (otmp->dknown && !objects[otmp->otyp].oc_name_known + && !objects[otmp->otyp].oc_uname) + docall(otmp); + return (1); + } + if (otmp->oclass != FOOD_CLASS) { + int material; + victual.reqtime = 1; + victual.piece = otmp; + /* Don't split it, we don't need to if it's 1 move */ + victual.usedtime = 0; + victual.canchoke = (u.uhs == SATIATED); + /* Note: gold weighs 1 pt. for each 1000 pieces (see */ + /* pickup.c) so gold and non-gold is consistent. */ + if (otmp->oclass == COIN_CLASS) + basenutrit = ((otmp->quan > 200000L) ? 2000 + : (int)(otmp->quan/100L)); + else if(otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) + basenutrit = weight(otmp); + /* oc_nutrition is usually weight anyway */ + else basenutrit = objects[otmp->otyp].oc_nutrition; + victual.nmod = basenutrit; + victual.eating = TRUE; /* needed for lesshungry() */ + + material = objects[otmp->otyp].oc_material; + if (material == LEATHER || + material == BONE || material == DRAGON_HIDE) { + u.uconduct.unvegan++; + violated_vegetarian(); + } else if (material == WAX) + u.uconduct.unvegan++; + u.uconduct.food++; + + if (otmp->cursed) + (void) rottenfood(otmp); + + if (otmp->oclass == WEAPON_CLASS && otmp->opoisoned) { + pline("Ecch - that must have been poisonous!"); + if(!Poison_resistance) { + losestr(rnd(4)); + losehp(rnd(15), xname(otmp), KILLED_BY_AN); + } else + You("seem unaffected by the poison."); + } else if (!otmp->cursed) + pline("This %s is delicious!", + otmp->oclass == COIN_CLASS ? foodword(otmp) : + singular(otmp, xname)); + + eatspecial(); + return 1; + } + + if(otmp == victual.piece) { + /* If they weren't able to choke, they don't suddenly become able to + * choke just because they were interrupted. On the other hand, if + * they were able to choke before, if they lost food it's possible + * they shouldn't be able to choke now. + */ + if (u.uhs != SATIATED) victual.canchoke = FALSE; + victual.piece = touchfood(otmp); + You("resume your meal."); + start_eating(victual.piece); + return(1); + } + + /* nothing in progress - so try to find something. */ + /* tins are a special case */ + /* tins must also check conduct separately in case they're discarded */ + if(otmp->otyp == TIN) { + start_tin(otmp); + return(1); + } + + /* KMH, conduct */ + u.uconduct.food++; + + victual.piece = otmp = touchfood(otmp); + victual.usedtime = 0; + + /* Now we need to calculate delay and nutritional info. + * The base nutrition calculated here and in eatcorpse() accounts + * for normal vs. rotten food. The reqtime and nutrit values are + * then adjusted in accordance with the amount of food left. + */ + if(otmp->otyp == CORPSE) { + int tmp = eatcorpse(otmp); + if (tmp == 2) { + /* used up */ + victual.piece = (struct obj *)0; + return(1); + } else if (tmp) + dont_start = TRUE; + /* if not used up, eatcorpse sets up reqtime and may modify + * oeaten */ + } else { + /* No checks for WAX, LEATHER, BONE, DRAGON_HIDE. These are + * all handled in the != FOOD_CLASS case, above */ + switch (objects[otmp->otyp].oc_material) { + case FLESH: + u.uconduct.unvegan++; + if (otmp->otyp != EGG) { + violated_vegetarian(); + } + break; + + default: + if (otmp->otyp == PANCAKE || + otmp->otyp == FORTUNE_COOKIE || /* eggs */ + otmp->otyp == CREAM_PIE || + otmp->otyp == CANDY_BAR || /* milk */ + otmp->otyp == LUMP_OF_ROYAL_JELLY) + u.uconduct.unvegan++; + break; + } + + victual.reqtime = objects[otmp->otyp].oc_delay; + if (otmp->otyp != FORTUNE_COOKIE && + (otmp->cursed || + (((monstermoves - otmp->age) > (int) otmp->blessed ? 50:30) && + (otmp->orotten || !rn2(7))))) { + + if (rottenfood(otmp)) { + otmp->orotten = TRUE; + dont_start = TRUE; + } + consume_oeaten(otmp, 1); /* oeaten >>= 1 */ + } else fprefx(otmp); + } + + /* re-calc the nutrition */ + if (otmp->otyp == CORPSE) basenutrit = mons[otmp->corpsenm].cnutrit; + else basenutrit = objects[otmp->otyp].oc_nutrition; + +#ifdef DEBUG + debugpline("before rounddiv: victual.reqtime == %d", victual.reqtime); + debugpline("oeaten == %d, basenutrit == %d", otmp->oeaten, basenutrit); +#endif + victual.reqtime = (basenutrit == 0 ? 0 : + rounddiv(victual.reqtime * (long)otmp->oeaten, basenutrit)); +#ifdef DEBUG + debugpline("after rounddiv: victual.reqtime == %d", victual.reqtime); +#endif + /* calculate the modulo value (nutrit. units per round eating) + * note: this isn't exact - you actually lose a little nutrition + * due to this method. + * TODO: add in a "remainder" value to be given at the end of the + * meal. + */ + if (victual.reqtime == 0 || otmp->oeaten == 0) + /* possible if most has been eaten before */ + victual.nmod = 0; + else if ((int)otmp->oeaten >= victual.reqtime) + victual.nmod = -((int)otmp->oeaten / victual.reqtime); + else + victual.nmod = victual.reqtime % otmp->oeaten; + victual.canchoke = (u.uhs == SATIATED); + + if (!dont_start) start_eating(otmp); + return(1); +} + +/* Take a single bite from a piece of food, checking for choking and + * modifying usedtime. Returns 1 if they choked and survived, 0 otherwise. + */ +STATIC_OVL int +bite() +{ + if(victual.canchoke && u.uhunger >= 2000) { + choke(victual.piece); + return 1; + } + if (victual.doreset) { + do_reset_eat(); + return 0; + } + force_save_hs = TRUE; + if(victual.nmod < 0) { + lesshungry(-victual.nmod); + consume_oeaten(victual.piece, victual.nmod); /* -= -nmod */ + } else if(victual.nmod > 0 && (victual.usedtime % victual.nmod)) { + lesshungry(1); + consume_oeaten(victual.piece, -1); /* -= 1 */ + } + force_save_hs = FALSE; + recalc_wt(); + return 0; +} + +#endif /* OVLB */ +#ifdef OVL0 + +void +gethungry() /* as time goes by - called by moveloop() and domove() */ +{ + if (u.uinvulnerable) return; /* you don't feel hungrier */ + + if ((!u.usleep || !rn2(10)) /* slow metabolic rate while asleep */ + && (carnivorous(youmonst.data) || herbivorous(youmonst.data)) + && !Slow_digestion) + u.uhunger--; /* ordinary food consumption */ + + if (moves % 2) { /* odd turns */ + /* Regeneration uses up food, unless due to an artifact */ + if (HRegeneration || ((ERegeneration & (~W_ART)) && + (ERegeneration != W_WEP || !uwep->oartifact))) + u.uhunger--; + if (near_capacity() > SLT_ENCUMBER) u.uhunger--; + } else { /* even turns */ + if (Hunger) u.uhunger--; + /* Conflict uses up food too */ + if (HConflict || (EConflict & (~W_ARTI))) u.uhunger--; + /* +0 charged rings don't do anything, so don't affect hunger */ + /* Slow digestion still uses ring hunger */ + switch ((int)(moves % 20)) { /* note: use even cases only */ + case 4: if (uleft && + (uleft->spe || !objects[uleft->otyp].oc_charged)) + u.uhunger--; + break; + case 8: if (uamul) u.uhunger--; + break; + case 12: if (uright && + (uright->spe || !objects[uright->otyp].oc_charged)) + u.uhunger--; + break; + case 16: if (u.uhave.amulet) u.uhunger--; + break; + default: break; + } + } + newuhs(TRUE); +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +morehungry(num) /* called after vomiting and after performing feats of magic */ +register int num; +{ + u.uhunger -= num; + newuhs(TRUE); +} + + +void +lesshungry(num) /* called after eating (and after drinking fruit juice) */ +register int num; +{ + /* See comments in newuhs() for discussion on force_save_hs */ + boolean iseating = (occupation == eatfood) || force_save_hs; +#ifdef DEBUG + debugpline("lesshungry(%d)", num); +#endif + u.uhunger += num; + if(u.uhunger >= 2000) { + if (!iseating || victual.canchoke) { + if (iseating) { + choke(victual.piece); + reset_eat(); + } else + choke(occupation == opentin ? tin.tin : (struct obj *)0); + /* no reset_eat() */ + } + } else { + /* Have lesshungry() report when you're nearly full so all eating + * warns when you're about to choke. + */ + if (u.uhunger >= 1500) { + if (!victual.eating || (victual.eating && !victual.fullwarn)) { + pline("You're having a hard time getting all of it down."); + nomovemsg = "You're finally finished."; + if (!victual.eating) + multi = -2; + else { + victual.fullwarn = TRUE; + if (victual.canchoke && victual.reqtime > 1) { + /* a one-gulp food will not survive a stop */ + if (yn_function("Stop eating?",ynchars,'y')=='y') { + reset_eat(); + nomovemsg = (char *)0; + } + } + } + } + } + } + newuhs(FALSE); +} + +STATIC_PTR +int +unfaint() +{ + (void) Hear_again(); + if(u.uhs > FAINTING) + u.uhs = FAINTING; + stop_occupation(); + flags.botl = 1; + return 0; +} + +#endif /* OVLB */ +#ifdef OVL0 + +boolean +is_fainted() +{ + return((boolean)(u.uhs == FAINTED)); +} + +void +reset_faint() /* call when a faint must be prematurely terminated */ +{ + if(is_fainted()) nomul(0); +} + +#if 0 +void +sync_hunger() +{ + + if(is_fainted()) { + + flags.soundok = 0; + nomul(-10+(u.uhunger/10)); + nomovemsg = "You regain consciousness."; + afternmv = unfaint; + } +} +#endif + +void +newuhs(incr) /* compute and comment on your (new?) hunger status */ +boolean incr; +{ + unsigned newhs; + static unsigned save_hs; + static boolean saved_hs = FALSE; + int h = u.uhunger; + + newhs = (h > 1000) ? SATIATED : + (h > 150) ? NOT_HUNGRY : + (h > 50) ? HUNGRY : + (h > 0) ? WEAK : FAINTING; + + /* While you're eating, you may pass from WEAK to HUNGRY to NOT_HUNGRY. + * This should not produce the message "you only feel hungry now"; + * that message should only appear if HUNGRY is an endpoint. Therefore + * we check to see if we're in the middle of eating. If so, we save + * the first hunger status, and at the end of eating we decide what + * message to print based on the _entire_ meal, not on each little bit. + */ + /* It is normally possible to check if you are in the middle of a meal + * by checking occupation == eatfood, but there is one special case: + * start_eating() can call bite() for your first bite before it + * sets the occupation. + * Anyone who wants to get that case to work _without_ an ugly static + * force_save_hs variable, feel free. + */ + /* Note: If you become a certain hunger status in the middle of the + * meal, and still have that same status at the end of the meal, + * this will incorrectly print the associated message at the end of + * the meal instead of the middle. Such a case is currently + * impossible, but could become possible if a message for SATIATED + * were added or if HUNGRY and WEAK were separated by a big enough + * gap to fit two bites. + */ + if (occupation == eatfood || force_save_hs) { + if (!saved_hs) { + save_hs = u.uhs; + saved_hs = TRUE; + } + u.uhs = newhs; + return; + } else { + if (saved_hs) { + u.uhs = save_hs; + saved_hs = FALSE; + } + } + + if(newhs == FAINTING) { + if(is_fainted()) newhs = FAINTED; + if(u.uhs <= WEAK || rn2(20-u.uhunger/10) >= 19) { + if(!is_fainted() && multi >= 0 /* %% */) { + /* stop what you're doing, then faint */ + stop_occupation(); + You("faint from lack of food."); + flags.soundok = 0; + nomul(-10+(u.uhunger/10)); + nomovemsg = "You regain consciousness."; + afternmv = unfaint; + newhs = FAINTED; + } + } else + if(u.uhunger < -(int)(200 + 20*ACURR(A_CON))) { + u.uhs = STARVED; + flags.botl = 1; + bot(); + You("die from starvation."); + killer_format = KILLED_BY; + killer = "starvation"; + done(STARVING); + /* if we return, we lifesaved, and that calls newuhs */ + return; + } + } + + if(newhs != u.uhs) { + if(newhs >= WEAK && u.uhs < WEAK) + losestr(1); /* this may kill you -- see below */ + else if(newhs < WEAK && u.uhs >= WEAK) + losestr(-1); + switch(newhs){ + case HUNGRY: + if (Hallucination) { + You((!incr) ? + "now have a lesser case of the munchies." : + "are getting the munchies."); + } else + You((!incr) ? "only feel hungry now." : + (u.uhunger < 145) ? "feel hungry." : + "are beginning to feel hungry."); + if (incr && occupation && + (occupation != eatfood && occupation != opentin)) + stop_occupation(); + break; + case WEAK: + if (Hallucination) + pline((!incr) ? + "You still have the munchies." : + "The munchies are interfering with your motor capabilities."); + else if (incr && + (Role_if(PM_WIZARD) || Race_if(PM_ELF) || + Role_if(PM_VALKYRIE))) + pline("%s needs food, badly!", + (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? + urole.name.m : "Elf"); + else + You((!incr) ? "feel weak now." : + (u.uhunger < 45) ? "feel weak." : + "are beginning to feel weak."); + if (incr && occupation && + (occupation != eatfood && occupation != opentin)) + stop_occupation(); + break; + } + u.uhs = newhs; + flags.botl = 1; + bot(); + if ((Upolyd ? u.mh : u.uhp) < 1) { + You("die from hunger and exhaustion."); + killer_format = KILLED_BY; + killer = "exhaustion"; + done(STARVING); + return; + } + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +/* Returns an object representing food. Object may be either on floor or + * in inventory. + */ +struct obj * +floorfood(verb,corpsecheck) /* get food from floor or pack */ + const char *verb; + int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */ +{ + register struct obj *otmp; + char qbuf[QBUFSZ]; + char c; + boolean feeding = (!strcmp(verb, "eat")); + + /* if we can't touch floor objects then use invent food only */ + if (!can_reach_floor() || +#ifdef STEED + (feeding && u.usteed) || /* can't eat off floor while riding */ +#endif + ((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) && + (Wwalking || is_clinger(youmonst.data) || + (Flying && !Breathless)))) + goto skipfloor; + + if (feeding && metallivorous(youmonst.data)) { + struct obj *gold; + struct trap *ttmp = t_at(u.ux, u.uy); + + if (ttmp && ttmp->tseen && ttmp->ttyp == BEAR_TRAP) { + /* If not already stuck in the trap, perhaps there should + be a chance to becoming trapped? Probably not, because + then the trap would just get eaten on the _next_ turn... */ + Sprintf(qbuf, "There is a bear trap here (%s); eat it?", + (u.utrap && u.utraptype == TT_BEARTRAP) ? + "holding you" : "armed"); + if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') { + u.utrap = u.utraptype = 0; + deltrap(ttmp); + return mksobj(BEARTRAP, TRUE, FALSE); + } else if (c == 'q') { + return (struct obj *)0; + } + } + + if (youmonst.data != &mons[PM_RUST_MONSTER] && + (gold = g_at(u.ux, u.uy)) != 0) { + if (gold->quan == 1L) + Sprintf(qbuf, "There is 1 gold piece here; eat it?"); + else + Sprintf(qbuf, "There are %ld gold pieces here; eat them?", + gold->quan); + if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') { + return gold; + } else if (c == 'q') { + return (struct obj *)0; + } + } + } + + /* Is there some food (probably a heavy corpse) here on the ground? */ + for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { + if(corpsecheck ? + (otmp->otyp==CORPSE && (corpsecheck == 1 || tinnable(otmp))) : + feeding ? (otmp->oclass != COIN_CLASS && is_edible(otmp)) : + otmp->oclass==FOOD_CLASS) { + Sprintf(qbuf, "There %s %s here; %s %s?", + otense(otmp, "are"), + doname(otmp), verb, + (otmp->quan == 1L) ? "it" : "one"); + if((c = yn_function(qbuf,ynqchars,'n')) == 'y') + return(otmp); + else if(c == 'q') + return((struct obj *) 0); + } + } + + skipfloor: + /* We cannot use ALL_CLASSES since that causes getobj() to skip its + * "ugly checks" and we need to check for inedible items. + */ + otmp = getobj(feeding ? (const char *)allobj : + (const char *)comestibles, verb); + if (corpsecheck && otmp) + if (otmp->otyp != CORPSE || (corpsecheck == 2 && !tinnable(otmp))) { + You_cant("%s that!", verb); + return (struct obj *)0; + } + return otmp; +} + +/* Side effects of vomiting */ +/* added nomul (MRS) - it makes sense, you're too busy being sick! */ +void +vomit() /* A good idea from David Neves */ +{ + make_sick(0L, (char *) 0, TRUE, SICK_VOMITABLE); + nomul(-2); +} + +int +eaten_stat(base, obj) +register int base; +register struct obj *obj; +{ + long uneaten_amt, full_amount; + + uneaten_amt = (long)obj->oeaten; + full_amount = (obj->otyp == CORPSE) ? (long)mons[obj->corpsenm].cnutrit + : (long)objects[obj->otyp].oc_nutrition; + if (uneaten_amt > full_amount) { + impossible( + "partly eaten food (%ld) more nutritious than untouched food (%ld)", + uneaten_amt, full_amount); + uneaten_amt = full_amount; + } + + base = (int)(full_amount ? (long)base * uneaten_amt / full_amount : 0L); + return (base < 1) ? 1 : base; +} + +/* reduce obj's oeaten field, making sure it never hits or passes 0 */ +void +consume_oeaten(obj, amt) +struct obj *obj; +int amt; +{ + /* + * This is a hack to try to squelch several long standing mystery + * food bugs. A better solution would be to rewrite the entire + * victual handling mechanism from scratch using a less complex + * model. Alternatively, this routine could call done_eating() + * or food_disappears() but its callers would need revisions to + * cope with victual.piece unexpectedly going away. + * + * Multi-turn eating operates by setting the food's oeaten field + * to its full nutritional value and then running a counter which + * independently keeps track of whether there is any food left. + * The oeaten field can reach exactly zero on the last turn, and + * the object isn't removed from inventory until the next turn + * when the "you finish eating" message gets delivered, so the + * food would be restored to the status of untouched during that + * interval. This resulted in unexpected encumbrance messages + * at the end of a meal (if near enough to a threshold) and would + * yield full food if there was an interruption on the critical + * turn. Also, there have been reports over the years of food + * becoming massively heavy or producing unlimited satiation; + * this would occur if reducing oeaten via subtraction attempted + * to drop it below 0 since its unsigned type would produce a + * huge positive value instead. So far, no one has figured out + * _why_ that inappropriate subtraction might sometimes happen. + */ + + if (amt > 0) { + /* bit shift to divide the remaining amount of food */ + obj->oeaten >>= amt; + } else { + /* simple decrement; value is negative so we actually add it */ + if ((int) obj->oeaten > -amt) + obj->oeaten += amt; + else + obj->oeaten = 0; + } + + if (obj->oeaten == 0) { + if (obj == victual.piece) /* always true unless wishing... */ + victual.reqtime = victual.usedtime; /* no bites left */ + obj->oeaten = 1; /* smallest possible positive value */ + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* called when eatfood occupation has been interrupted, + or in the case of theft, is about to be interrupted */ +boolean +maybe_finished_meal(stopping) +boolean stopping; +{ + /* in case consume_oeaten() has decided that the food is all gone */ + if (occupation == eatfood && victual.usedtime >= victual.reqtime) { + if (stopping) occupation = 0; /* for do_reset_eat */ + (void) eatfood(); /* calls done_eating() to use up victual.piece */ + return TRUE; + } + return FALSE; +} + +#endif /* OVL1 */ + +/*eat.c*/ diff --git a/src/end.c b/src/end.c new file mode 100644 index 0000000..58d47e0 --- /dev/null +++ b/src/end.c @@ -0,0 +1,1106 @@ +/* SCCS Id: @(#)end.c 3.4 2003/03/10 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#define NEED_VARARGS /* comment line for pre-compiled headers */ + +#include "hack.h" +#include "eshk.h" +#ifndef NO_SIGNAL +#include +#endif +#include "dlb.h" + + /* these probably ought to be generated by makedefs, like LAST_GEM */ +#define FIRST_GEM DILITHIUM_CRYSTAL +#define FIRST_AMULET AMULET_OF_ESP +#define LAST_AMULET AMULET_OF_YENDOR + +struct valuable_data { long count; int typ; }; + +static struct valuable_data + gems[LAST_GEM+1 - FIRST_GEM + 1], /* 1 extra for glass */ + amulets[LAST_AMULET+1 - FIRST_AMULET]; + +static struct val_list { struct valuable_data *list; int size; } valuables[] = { + { gems, sizeof gems / sizeof *gems }, + { amulets, sizeof amulets / sizeof *amulets }, + { 0, 0 } +}; + +#ifndef NO_SIGNAL +STATIC_PTR void FDECL(done_intr, (int)); +# if defined(UNIX) || defined(VMS) || defined (__EMX__) +static void FDECL(done_hangup, (int)); +# endif +#endif +STATIC_DCL void FDECL(disclose,(int,BOOLEAN_P)); +STATIC_DCL void FDECL(get_valuables, (struct obj *)); +STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *,int)); +STATIC_DCL void FDECL(artifact_score, (struct obj *,BOOLEAN_P,winid)); +STATIC_DCL void FDECL(savelife, (int)); +STATIC_DCL void FDECL(list_vanquished, (CHAR_P,BOOLEAN_P)); +STATIC_DCL void FDECL(list_genocided, (CHAR_P,BOOLEAN_P)); +STATIC_DCL boolean FDECL(should_query_disclose_option, (int,char *)); + +#if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2) +extern void FDECL(nethack_exit,(int)); +#else +#define nethack_exit exit +#endif + +#define done_stopprint program_state.stopprint + +#ifdef AMIGA +# define NH_abort() Abort(0) +#else +# ifdef SYSV +# define NH_abort() (void) abort() +# else +# ifdef WIN32 +# define NH_abort() win32_abort() +# else +# define NH_abort() abort() +# endif +# endif +#endif + +/* + * The order of these needs to match the macros in hack.h. + */ +static NEARDATA const char *deaths[] = { /* the array of death */ + "died", "choked", "poisoned", "starvation", "drowning", + "burning", "dissolving under the heat and pressure", + "crushed", "turned to stone", "turned into slime", + "genocided", "panic", "trickery", + "quit", "escaped", "ascended" +}; + +static NEARDATA const char *ends[] = { /* "when you..." */ + "died", "choked", "were poisoned", "starved", "drowned", + "burned", "dissolved in the lava", + "were crushed", "turned to stone", "turned into slime", + "were genocided", "panicked", "were tricked", + "quit", "escaped", "ascended" +}; + +extern const char * const killed_by_prefix[]; /* from topten.c */ + +/*ARGSUSED*/ +void +done1(sig_unused) /* called as signal() handler, so sent at least one arg */ +int sig_unused; +{ +#ifndef NO_SIGNAL + (void) signal(SIGINT,SIG_IGN); +#endif + if(flags.ignintr) { +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif + clear_nhwindow(WIN_MESSAGE); + curs_on_u(); + wait_synch(); + if(multi > 0) nomul(0); + } else { + (void)done2(); + } +} + + +/* "#quit" command or keyboard interrupt */ +int +done2() +{ + if(yn("Really quit?") == 'n') { +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif + clear_nhwindow(WIN_MESSAGE); + curs_on_u(); + wait_synch(); + if(multi > 0) nomul(0); + if(multi == 0) { + u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */ + u.usleep = 0; + } + return 0; + } +#if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE)) + if(wizard) { + int c; +# ifdef VMS + const char *tmp = "Enter debugger?"; +# else +# ifdef LATTICE + const char *tmp = "Create SnapShot?"; +# else + const char *tmp = "Dump core?"; +# endif +# endif + if ((c = ynq(tmp)) == 'y') { + (void) signal(SIGINT, (SIG_RET_TYPE) done1); + exit_nhwindows((char *)0); + NH_abort(); + } else if (c == 'q') done_stopprint++; + } +#endif +#ifndef LINT + done(QUIT); +#endif + return 0; +} + +#ifndef NO_SIGNAL +/*ARGSUSED*/ +STATIC_PTR void +done_intr(sig_unused) /* called as signal() handler, so sent at least one arg */ +int sig_unused; +{ + done_stopprint++; + (void) signal(SIGINT, SIG_IGN); +# if defined(UNIX) || defined(VMS) + (void) signal(SIGQUIT, SIG_IGN); +# endif + return; +} + +# if defined(UNIX) || defined(VMS) || defined(__EMX__) +static void +done_hangup(sig) /* signal() handler */ +int sig; +{ + program_state.done_hup++; + (void)signal(SIGHUP, SIG_IGN); + done_intr(sig); + return; +} +# endif +#endif /* NO_SIGNAL */ + +void +done_in_by(mtmp) +register struct monst *mtmp; +{ + char buf[BUFSZ]; + boolean distorted = (boolean)(Hallucination && canspotmon(mtmp)); + + You("die..."); + mark_synch(); /* flush buffered screen output */ + buf[0] = '\0'; + killer_format = KILLED_BY_AN; + /* "killed by the high priest of Crom" is okay, "killed by the high + priest" alone isn't */ + if ((mtmp->data->geno & G_UNIQ) != 0 && !(mtmp->data == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) { + if (!type_is_pname(mtmp->data)) + Strcat(buf, "the "); + killer_format = KILLED_BY; + } + /* _the_ ghost of Dudley */ + if (mtmp->data == &mons[PM_GHOST] && mtmp->mnamelth) { + Strcat(buf, "the "); + killer_format = KILLED_BY; + } + if (mtmp->minvis) + Strcat(buf, "invisible "); + if (distorted) + Strcat(buf, "hallucinogen-distorted "); + + if(mtmp->data == &mons[PM_GHOST]) { + Strcat(buf, "ghost"); + if (mtmp->mnamelth) Sprintf(eos(buf), " of %s", NAME(mtmp)); + } else if(mtmp->isshk) { + Sprintf(eos(buf), "%s %s, the shopkeeper", + (mtmp->female ? "Ms." : "Mr."), shkname(mtmp)); + killer_format = KILLED_BY; + } else if (mtmp->ispriest || mtmp->isminion) { + /* m_monnam() suppresses "the" prefix plus "invisible", and + it overrides the effect of Hallucination on priestname() */ + killer = m_monnam(mtmp); + Strcat(buf, killer); + } else { + Strcat(buf, mtmp->data->mname); + if (mtmp->mnamelth) + Sprintf(eos(buf), " called %s", NAME(mtmp)); + } + + if (multi) Strcat(buf, ", while helpless"); + killer = buf; + if (mtmp->data->mlet == S_WRAITH) + u.ugrave_arise = PM_WRAITH; + else if (mtmp->data->mlet == S_MUMMY && urace.mummynum != NON_PM) + u.ugrave_arise = urace.mummynum; + else if (mtmp->data->mlet == S_VAMPIRE && Race_if(PM_HUMAN)) + u.ugrave_arise = PM_VAMPIRE; + else if (mtmp->data == &mons[PM_GHOUL]) + u.ugrave_arise = PM_GHOUL; + if (u.ugrave_arise >= LOW_PM && + (mvitals[u.ugrave_arise].mvflags & G_GENOD)) + u.ugrave_arise = NON_PM; + if (touch_petrifies(mtmp->data)) + done(STONING); + else + done(DIED); + return; +} + +#if defined(WIN32) +#define NOTIFY_NETHACK_BUGS +#endif + +/*VARARGS1*/ +void +panic VA_DECL(const char *, str) + VA_START(str); + VA_INIT(str, char *); + + if (program_state.panicking++) + NH_abort(); /* avoid loops - this should never happen*/ + + if (iflags.window_inited) { + raw_print("\r\nOops..."); + wait_synch(); /* make sure all pending output gets flushed */ + exit_nhwindows((char *)0); + iflags.window_inited = 0; /* they're gone; force raw_print()ing */ + } + + raw_print(program_state.gameover ? + "Postgame wrapup disrupted." : + !program_state.something_worth_saving ? + "Program initialization has failed." : + "Suddenly, the dungeon collapses."); +#if defined(WIZARD) && !defined(MICRO) +# if defined(NOTIFY_NETHACK_BUGS) + if (!wizard) + raw_printf("Report the following error to \"%s\".", + "nethack-bugs@nethack.org"); + else if (program_state.something_worth_saving) + raw_print("\nError save file being written.\n"); +# else + if (!wizard) + raw_printf("Report error to \"%s\"%s.", +# ifdef WIZARD_NAME /*(KR1ED)*/ + WIZARD_NAME, +# else + WIZARD, +# endif + !program_state.something_worth_saving ? "" : + " and it may be possible to rebuild."); +# endif + if (program_state.something_worth_saving) { + set_error_savefile(); + (void) dosave0(); + } +#endif + { + char buf[BUFSZ]; + Vsprintf(buf,str,VA_ARGS); + raw_print(buf); + paniclog("panic", buf); + } +#ifdef WIN32 + interject(INTERJECT_PANIC); +#endif +#if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)) + if (wizard) + NH_abort(); /* generate core dump */ +#endif + VA_END(); + done(PANICKED); +} + +STATIC_OVL boolean +should_query_disclose_option(category, defquery) +int category; +char *defquery; +{ + int idx; + char *dop = index(disclosure_options, category); + + if (dop && defquery) { + idx = dop - disclosure_options; + if (idx < 0 || idx > (NUM_DISCLOSURE_OPTIONS - 1)) { + impossible( + "should_query_disclose_option: bad disclosure index %d %c", + idx, category); + *defquery = DISCLOSE_PROMPT_DEFAULT_YES; + return TRUE; + } + if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) { + *defquery = 'y'; + return FALSE; + } else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) { + *defquery = 'n'; + return FALSE; + } else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) { + *defquery = 'y'; + return TRUE; + } else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_NO) { + *defquery = 'n'; + return TRUE; + } + } + if (defquery) + impossible("should_query_disclose_option: bad category %c", category); + else + impossible("should_query_disclose_option: null defquery"); + return TRUE; +} + +STATIC_OVL void +disclose(how,taken) +int how; +boolean taken; +{ + char c = 0, defquery; + char qbuf[QBUFSZ]; + boolean ask; + + if (invent) { + if(taken) + Sprintf(qbuf,"Do you want to see what you had when you %s?", + (how == QUIT) ? "quit" : "died"); + else + Strcpy(qbuf,"Do you want your possessions identified?"); + + ask = should_query_disclose_option('i', &defquery); + if (!done_stopprint) { + c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery; + if (c == 'y') { + struct obj *obj; + + for (obj = invent; obj; obj = obj->nobj) { + makeknown(obj->otyp); + obj->known = obj->bknown = obj->dknown = obj->rknown = 1; + } + (void) display_inventory((char *)0, TRUE); + container_contents(invent, TRUE, TRUE); + } + if (c == 'q') done_stopprint++; + } + } + + ask = should_query_disclose_option('a', &defquery); + if (!done_stopprint) { + c = ask ? yn_function("Do you want to see your attributes?", + ynqchars, defquery) : defquery; + if (c == 'y') + enlightenment(how >= PANICKED ? 1 : 2); /* final */ + if (c == 'q') done_stopprint++; + } + + ask = should_query_disclose_option('v', &defquery); + if (!done_stopprint) + list_vanquished(defquery, ask); + + ask = should_query_disclose_option('g', &defquery); + if (!done_stopprint) + list_genocided(defquery, ask); + + ask = should_query_disclose_option('c', &defquery); + if (!done_stopprint) { + c = ask ? yn_function("Do you want to see your conduct?", + ynqchars, defquery) : defquery; + if (c == 'y') + show_conduct(how >= PANICKED ? 1 : 2); + if (c == 'q') done_stopprint++; + } +} + +/* try to get the player back in a viable state after being killed */ +STATIC_OVL void +savelife(how) +int how; +{ + u.uswldtim = 0; + u.uhp = u.uhpmax; + if (u.uhunger < 500) { + u.uhunger = 500; + newuhs(FALSE); + } + /* cure impending doom of sickness hero won't have time to fix */ + if ((Sick & TIMEOUT) == 1) { + u.usick_type = 0; + Sick = 0; + } + if (how == CHOKING) init_uhunger(); + nomovemsg = "You survived that attempt on your life."; + flags.move = 0; + if(multi > 0) multi = 0; else multi = -1; + if(u.utrap && u.utraptype == TT_LAVA) u.utrap = 0; + flags.botl = 1; + u.ugrave_arise = NON_PM; + HUnchanging = 0L; + curs_on_u(); +} + +/* + * Get valuables from the given list. Revised code: the list always remains + * intact. + */ +STATIC_OVL void +get_valuables(list) +struct obj *list; /* inventory or container contents */ +{ + register struct obj *obj; + register int i; + + /* find amulets and gems, ignoring all artifacts */ + for (obj = list; obj; obj = obj->nobj) + if (Has_contents(obj)) { + get_valuables(obj->cobj); + } else if (obj->oartifact) { + continue; + } else if (obj->oclass == AMULET_CLASS) { + i = obj->otyp - FIRST_AMULET; + if (!amulets[i].count) { + amulets[i].count = obj->quan; + amulets[i].typ = obj->otyp; + } else amulets[i].count += obj->quan; /* always adds one */ + } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) { + i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM; + if (!gems[i].count) { + gems[i].count = obj->quan; + gems[i].typ = obj->otyp; + } else gems[i].count += obj->quan; + } + return; +} + +/* + * Sort collected valuables, most frequent to least. We could just + * as easily use qsort, but we don't care about efficiency here. + */ +STATIC_OVL void +sort_valuables(list, size) +struct valuable_data list[]; +int size; /* max value is less than 20 */ +{ + register int i, j; + struct valuable_data ltmp; + + /* move greater quantities to the front of the list */ + for (i = 1; i < size; i++) { + if (list[i].count == 0) continue; /* empty slot */ + ltmp = list[i]; /* structure copy */ + for (j = i; j > 0; --j) + if (list[j-1].count >= ltmp.count) break; + else { + list[j] = list[j-1]; + } + list[j] = ltmp; + } + return; +} + +/* called twice; first to calculate total, then to list relevant items */ +STATIC_OVL void +artifact_score(list, counting, endwin) +struct obj *list; +boolean counting; /* true => add up points; false => display them */ +winid endwin; +{ + char pbuf[BUFSZ]; + struct obj *otmp; + long value, points; + short dummy; /* object type returned by artifact_name() */ + + for (otmp = list; otmp; otmp = otmp->nobj) { + if (otmp->oartifact || + otmp->otyp == BELL_OF_OPENING || + otmp->otyp == SPE_BOOK_OF_THE_DEAD || + otmp->otyp == CANDELABRUM_OF_INVOCATION) { + value = arti_cost(otmp); /* zorkmid value */ + points = value * 5 / 2; /* score value */ + if (counting) { + u.urexp += points; + } else { + makeknown(otmp->otyp); + otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; + /* assumes artifacts don't have quan > 1 */ + Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)", + the_unique_obj(otmp) ? "The " : "", + otmp->oartifact ? artifact_name(xname(otmp), &dummy) : + OBJ_NAME(objects[otmp->otyp]), + value, currency(value), points); + putstr(endwin, 0, pbuf); + } + } + if (Has_contents(otmp)) + artifact_score(otmp->cobj, counting, endwin); + } +} + +/* Be careful not to call panic from here! */ +void +done(how) +int how; +{ + boolean taken; + char kilbuf[BUFSZ], pbuf[BUFSZ]; + winid endwin = WIN_ERR; + boolean bones_ok, have_windows = iflags.window_inited; + struct obj *corpse = (struct obj *)0; + long umoney; + + if (how == TRICKED) { + if (killer) { + paniclog("trickery", killer); + killer = 0; + } +#ifdef WIZARD + if (wizard) { + You("are a very tricky wizard, it seems."); + return; + } +#endif + } + + /* kilbuf: used to copy killer in case it comes from something like + * xname(), which would otherwise get overwritten when we call + * xname() when listing possessions + * pbuf: holds Sprintf'd output for raw_print and putstr + */ + if (how == ASCENDED || (!killer && how == GENOCIDED)) + killer_format = NO_KILLER_PREFIX; + /* Avoid killed by "a" burning or "a" starvation */ + if (!killer && (how == STARVING || how == BURNING)) + killer_format = KILLED_BY; + Strcpy(kilbuf, (!killer || how >= PANICKED ? deaths[how] : killer)); + killer = kilbuf; + + if (how < PANICKED) u.umortality++; + if (Lifesaved && (how <= GENOCIDED)) { + pline("But wait..."); + makeknown(AMULET_OF_LIFE_SAVING); + Your("medallion %s!", + !Blind ? "begins to glow" : "feels warm"); + if (how == CHOKING) You("vomit ..."); + You_feel("much better!"); + pline_The("medallion crumbles to dust!"); + if (uamul) useup(uamul); + + (void) adjattrib(A_CON, -1, TRUE); + if(u.uhpmax <= 0) u.uhpmax = 10; /* arbitrary */ + savelife(how); + if (how == GENOCIDED) + pline("Unfortunately you are still genocided..."); + else { + killer = 0; + killer_format = 0; + return; + } + } + if (( +#ifdef WIZARD + wizard || +#endif + discover) && (how <= GENOCIDED)) { + if(yn("Die?") == 'y') goto die; + pline("OK, so you don't %s.", + (how == CHOKING) ? "choke" : "die"); + if(u.uhpmax <= 0) u.uhpmax = u.ulevel * 8; /* arbitrary */ + savelife(how); + killer = 0; + killer_format = 0; + return; + } + + /* + * The game is now over... + */ + +die: + program_state.gameover = 1; + /* in case of a subsequent panic(), there's no point trying to save */ + program_state.something_worth_saving = 0; + /* render vision subsystem inoperative */ + iflags.vision_inited = 0; + /* might have been killed while using a disposable item, so make sure + it's gone prior to inventory disclosure and creation of bones data */ + inven_inuse(TRUE); + + /* Sometimes you die on the first move. Life's not fair. + * On those rare occasions you get hosed immediately, go out + * smiling... :-) -3. + */ + if (moves <= 1 && how < PANICKED) /* You die... --More-- */ + pline("Do not pass go. Do not collect 200 %s.", currency(200L)); + + if (have_windows) wait_synch(); /* flush screen output */ +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done_intr); +# if defined(UNIX) || defined(VMS) || defined (__EMX__) + (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr); + (void) signal(SIGHUP, (SIG_RET_TYPE) done_hangup); +# endif +#endif /* NO_SIGNAL */ + + bones_ok = (how < GENOCIDED) && can_make_bones(); + + if (how == TURNED_SLIME) + u.ugrave_arise = PM_GREEN_SLIME; + + if (bones_ok && u.ugrave_arise < LOW_PM) { + /* corpse gets burnt up too */ + if (how == BURNING) + u.ugrave_arise = (NON_PM - 2); /* leave no corpse */ + else if (how == STONING) + u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */ + else if (u.ugrave_arise == NON_PM && + !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) { + int mnum = u.umonnum; + + if (!Upolyd) { + /* Base corpse on race when not poly'd since original + * u.umonnum is based on role, and all role monsters + * are human. + */ + mnum = (flags.female && urace.femalenum != NON_PM) ? + urace.femalenum : urace.malenum; + } + corpse = mk_named_object(CORPSE, &mons[mnum], + u.ux, u.uy, plname); + Sprintf(pbuf, "%s, %s%s", plname, + killer_format == NO_KILLER_PREFIX ? "" : + killed_by_prefix[how], + killer_format == KILLED_BY_AN ? an(killer) : killer); + make_grave(u.ux, u.uy, pbuf); + } + } + + if (how == QUIT) { + killer_format = NO_KILLER_PREFIX; + if (u.uhp < 1) { + how = DIED; + u.umortality++; /* skipped above when how==QUIT */ + /* note that killer is pointing at kilbuf */ + Strcpy(kilbuf, "quit while already on Charon's boat"); + } + } + if (how == ESCAPED || how == PANICKED) + killer_format = NO_KILLER_PREFIX; + + if (how != PANICKED) { + /* these affect score and/or bones, but avoid them during panic */ + taken = paybill((how == ESCAPED) ? -1 : (how != QUIT)); + paygd(); + clearpriests(); + } else taken = FALSE; /* lint; assert( !bones_ok ); */ + + clearlocks(); + + if (have_windows) display_nhwindow(WIN_MESSAGE, FALSE); + + if (strcmp(flags.end_disclose, "none") && how != PANICKED) + disclose(how, taken); + /* finish_paybill should be called after disclosure but before bones */ + if (bones_ok && taken) finish_paybill(); + + /* calculate score, before creating bones [container gold] */ + { + long tmp; + int deepest = deepest_lev_reached(FALSE); + +#ifndef GOLDOBJ + umoney = u.ugold; + tmp = u.ugold0; +#else + umoney = money_cnt(invent); + tmp = u.umoney0; +#endif + umoney += hidden_gold(); /* accumulate gold from containers */ + tmp = umoney - tmp; /* net gain */ + + if (tmp < 0L) + tmp = 0L; + if (how < PANICKED) + tmp -= tmp / 10L; + u.urexp += tmp; + u.urexp += 50L * (long)(deepest - 1); + if (deepest > 20) + u.urexp += 1000L * (long)((deepest > 30) ? 10 : deepest - 20); + if (how == ASCENDED) u.urexp *= 2L; + } + + if (bones_ok) { +#ifdef WIZARD + if (!wizard || yn("Save bones?") == 'y') +#endif + savebones(corpse); + /* corpse may be invalid pointer now so + ensure that it isn't used again */ + corpse = (struct obj *)0; + } + + /* update gold for the rip output, which can't use hidden_gold() + (containers will be gone by then if bones just got saved...) */ +#ifndef GOLDOBJ + u.ugold = umoney; +#else + done_money = umoney; +#endif + + /* clean up unneeded windows */ + if (have_windows) { + wait_synch(); + display_nhwindow(WIN_MESSAGE, TRUE); + destroy_nhwindow(WIN_MAP); + destroy_nhwindow(WIN_STATUS); + destroy_nhwindow(WIN_MESSAGE); + WIN_MESSAGE = WIN_STATUS = WIN_MAP = WIN_ERR; + + if(!done_stopprint || flags.tombstone) + endwin = create_nhwindow(NHW_TEXT); + + if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR) + outrip(endwin, how); + } else + done_stopprint = 1; /* just avoid any more output */ + +/* changing kilbuf really changes killer. we do it this way because + killer is declared a (const char *) +*/ + if (u.uhave.amulet) Strcat(kilbuf, " (with the Amulet)"); + else if (how == ESCAPED) { + if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */ + Strcat(kilbuf, " (in celestial disgrace)"); + else if (carrying(FAKE_AMULET_OF_YENDOR)) + Strcat(kilbuf, " (with a fake Amulet)"); + /* don't bother counting to see whether it should be plural */ + } + + if (!done_stopprint) { + Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname, + how != ASCENDED ? + (const char *) ((flags.female && urole.name.f) ? + urole.name.f : urole.name.m) : + (const char *) (flags.female ? "Demigoddess" : "Demigod")); + putstr(endwin, 0, pbuf); + putstr(endwin, 0, ""); + } + + if (how == ESCAPED || how == ASCENDED) { + register struct monst *mtmp; + register struct obj *otmp; + register struct val_list *val; + register int i; + + for (val = valuables; val->list; val++) + for (i = 0; i < val->size; i++) { + val->list[i].count = 0L; + } + get_valuables(invent); + + /* add points for collected valuables */ + for (val = valuables; val->list; val++) + for (i = 0; i < val->size; i++) + if (val->list[i].count != 0L) + u.urexp += val->list[i].count + * (long)objects[val->list[i].typ].oc_cost; + + /* count the points for artifacts */ + artifact_score(invent, TRUE, endwin); + + keepdogs(TRUE); + viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */ + mtmp = mydogs; + if (!done_stopprint) Strcpy(pbuf, "You"); + if (mtmp) { + while (mtmp) { + if (!done_stopprint) + Sprintf(eos(pbuf), " and %s", mon_nam(mtmp)); + if (mtmp->mtame) + u.urexp += mtmp->mhp; + mtmp = mtmp->nmon; + } + if (!done_stopprint) putstr(endwin, 0, pbuf); + pbuf[0] = '\0'; + } else { + if (!done_stopprint) Strcat(pbuf, " "); + } + if (!done_stopprint) { + Sprintf(eos(pbuf), "%s with %ld point%s,", + how==ASCENDED ? "went to your reward" : + "escaped from the dungeon", + u.urexp, plur(u.urexp)); + putstr(endwin, 0, pbuf); + } + + if (!done_stopprint) + artifact_score(invent, FALSE, endwin); /* list artifacts */ + + /* list valuables here */ + for (val = valuables; val->list; val++) { + sort_valuables(val->list, val->size); + for (i = 0; i < val->size && !done_stopprint; i++) { + int typ = val->list[i].typ; + long count = val->list[i].count; + + if (count == 0L) continue; + if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) { + otmp = mksobj(typ, FALSE, FALSE); + makeknown(otmp->otyp); + otmp->known = 1; /* for fake amulets */ + otmp->dknown = 1; /* seen it (blindness fix) */ + otmp->onamelth = 0; + otmp->quan = count; + Sprintf(pbuf, "%8ld %s (worth %ld %s),", + count, xname(otmp), + count * (long)objects[typ].oc_cost, currency(2L)); + obfree(otmp, (struct obj *)0); + } else { + Sprintf(pbuf, + "%8ld worthless piece%s of colored glass,", + count, plur(count)); + } + putstr(endwin, 0, pbuf); + } + } + + } else if (!done_stopprint) { + /* did not escape or ascend */ + if (u.uz.dnum == 0 && u.uz.dlevel <= 0) { + /* level teleported out of the dungeon; `how' is DIED, + due to falling or to "arriving at heaven prematurely" */ + Sprintf(pbuf, "You %s beyond the confines of the dungeon", + (u.uz.dlevel < 0) ? "passed away" : ends[how]); + } else { + /* more conventional demise */ + const char *where = dungeons[u.uz.dnum].dname; + + if (Is_astralevel(&u.uz)) where = "The Astral Plane"; + Sprintf(pbuf, "You %s in %s", ends[how], where); + if (!In_endgame(&u.uz) && !Is_knox(&u.uz)) + Sprintf(eos(pbuf), " on dungeon level %d", + In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz)); + } + + Sprintf(eos(pbuf), " with %ld point%s,", + u.urexp, plur(u.urexp)); + putstr(endwin, 0, pbuf); + } + + if (!done_stopprint) { + Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", + umoney, plur(umoney), moves, plur(moves)); + putstr(endwin, 0, pbuf); + } + if (!done_stopprint) { + Sprintf(pbuf, + "You were level %d with a maximum of %d hit point%s when you %s.", + u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]); + putstr(endwin, 0, pbuf); + putstr(endwin, 0, ""); + } + if (!done_stopprint) + display_nhwindow(endwin, TRUE); + if (endwin != WIN_ERR) + destroy_nhwindow(endwin); + + /* "So when I die, the first thing I will see in Heaven is a + * score list?" */ + if (flags.toptenwin) { + topten(how); + if (have_windows) + exit_nhwindows((char *)0); + } else { + if (have_windows) + exit_nhwindows((char *)0); + topten(how); + } + + if(done_stopprint) { raw_print(""); raw_print(""); } + terminate(EXIT_SUCCESS); +} + + +void +container_contents(list, identified, all_containers) +struct obj *list; +boolean identified, all_containers; +{ + register struct obj *box, *obj; + char buf[BUFSZ]; + + for (box = list; box; box = box->nobj) { + if (Is_container(box) || box->otyp == STATUE) { + if (box->otyp == BAG_OF_TRICKS) { + continue; /* wrong type of container */ + } else if (box->cobj) { + winid tmpwin = create_nhwindow(NHW_MENU); + Sprintf(buf, "Contents of %s:", the(xname(box))); + putstr(tmpwin, 0, buf); + putstr(tmpwin, 0, ""); + for (obj = box->cobj; obj; obj = obj->nobj) { + if (identified) { + makeknown(obj->otyp); + obj->known = obj->bknown = + obj->dknown = obj->rknown = 1; + } + putstr(tmpwin, 0, doname(obj)); + } + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + if (all_containers) + container_contents(box->cobj, identified, TRUE); + } else { + pline("%s empty.", Tobjnam(box, "are")); + display_nhwindow(WIN_MESSAGE, FALSE); + } + } + if (!all_containers) + break; + } +} + + +/* should be called with either EXIT_SUCCESS or EXIT_FAILURE */ +void +terminate(status) +int status; +{ +#ifdef MAC + getreturn("to exit"); +#endif + /* don't bother to try to release memory if we're in panic mode, to + avoid trouble in case that happens to be due to memory problems */ + if (!program_state.panicking) { + freedynamicdata(); + dlb_cleanup(); + } + + nethack_exit(status); +} + +STATIC_OVL void +list_vanquished(defquery, ask) +char defquery; +boolean ask; +{ + register int i, lev; + int ntypes = 0, max_lev = 0, nkilled; + long total_killed = 0L; + char c; + winid klwin; + char buf[BUFSZ]; + + /* get totals first */ + for (i = LOW_PM; i < NUMMONS; i++) { + if (mvitals[i].died) ntypes++; + total_killed += (long)mvitals[i].died; + if (mons[i].mlevel > max_lev) max_lev = mons[i].mlevel; + } + + /* vanquished creatures list; + * includes all dead monsters, not just those killed by the player + */ + if (ntypes != 0) { + c = ask ? yn_function("Do you want an account of creatures vanquished?", + ynqchars, defquery) : defquery; + if (c == 'q') done_stopprint++; + if (c == 'y') { + klwin = create_nhwindow(NHW_MENU); + putstr(klwin, 0, "Vanquished creatures:"); + putstr(klwin, 0, ""); + + /* countdown by monster "toughness" */ + for (lev = max_lev; lev >= 0; lev--) + for (i = LOW_PM; i < NUMMONS; i++) + if (mons[i].mlevel == lev && (nkilled = mvitals[i].died) > 0) { + if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) { + Sprintf(buf, "%s%s", + !type_is_pname(&mons[i]) ? "The " : "", + mons[i].mname); + if (nkilled > 1) { + switch (nkilled) { + case 2: Sprintf(eos(buf)," (twice)"); break; + case 3: Sprintf(eos(buf)," (thrice)"); break; + default: Sprintf(eos(buf)," (%d time%s)", + nkilled, plur(nkilled)); + break; + } + } + } else { + /* trolls or undead might have come back, + but we don't keep track of that */ + if (nkilled == 1) + Strcpy(buf, an(mons[i].mname)); + else + Sprintf(buf, "%d %s", + nkilled, makeplural(mons[i].mname)); + } + putstr(klwin, 0, buf); + } + /* + * if (Hallucination) + * putstr(klwin, 0, "and a partridge in a pear tree"); + */ + if (ntypes > 1) { + putstr(klwin, 0, ""); + Sprintf(buf, "%ld creatures vanquished.", total_killed); + putstr(klwin, 0, buf); + } + display_nhwindow(klwin, TRUE); + destroy_nhwindow(klwin); + } + } +} + +/* number of monster species which have been genocided */ +int +num_genocides() +{ + int i, n = 0; + + for (i = LOW_PM; i < NUMMONS; ++i) + if (mvitals[i].mvflags & G_GENOD) ++n; + + return n; +} + +STATIC_OVL void +list_genocided(defquery, ask) +char defquery; +boolean ask; +{ + register int i; + int ngenocided; + char c; + winid klwin; + char buf[BUFSZ]; + + ngenocided = num_genocides(); + + /* genocided species list */ + if (ngenocided != 0) { + c = ask ? yn_function("Do you want a list of species genocided?", + ynqchars, defquery) : defquery; + if (c == 'q') done_stopprint++; + if (c == 'y') { + klwin = create_nhwindow(NHW_MENU); + putstr(klwin, 0, "Genocided species:"); + putstr(klwin, 0, ""); + + for (i = LOW_PM; i < NUMMONS; i++) + if (mvitals[i].mvflags & G_GENOD) { + if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) + Sprintf(buf, "%s%s", + !type_is_pname(&mons[i]) ? "" : "the ", + mons[i].mname); + else + Strcpy(buf, makeplural(mons[i].mname)); + putstr(klwin, 0, buf); + } + + putstr(klwin, 0, ""); + Sprintf(buf, "%d species genocided.", ngenocided); + putstr(klwin, 0, buf); + + display_nhwindow(klwin, TRUE); + destroy_nhwindow(klwin); + } + } +} + +/*end.c*/ diff --git a/src/engrave.c b/src/engrave.c new file mode 100644 index 0000000..fe09b4f --- /dev/null +++ b/src/engrave.c @@ -0,0 +1,1259 @@ +/* SCCS Id: @(#)engrave.c 3.4 2001/11/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" +#include + +STATIC_VAR NEARDATA struct engr *head_engr; + +#ifdef OVLB +/* random engravings */ +static const char *random_mesg[] = { + "Elbereth", + /* trap engravings */ + "Vlad was here", "ad aerarium", + /* take-offs and other famous engravings */ + "Owlbreath", "Galadriel", + "Kilroy was here", + "A.S. ->", "<- A.S.", /* Journey to the Center of the Earth */ + "You won't get it up the steps", /* Adventure */ + "Lasciate ogni speranza o voi ch'entrate.", /* Inferno */ + "Well Come", /* Prisoner */ + "We apologize for the inconvenience.", /* So Long... */ + "See you next Wednesday", /* Thriller */ + "notary sojak", /* Smokey Stover */ + "For a good time call 8?7-5309", + "Please don't feed the animals.", /* Various zoos around the world */ + "Madam, in Eden, I'm Adam.", /* A palindrome */ + "Two thumbs up!", /* Siskel & Ebert */ + "Hello, World!", /* The First C Program */ +#ifdef MAIL + "You've got mail!", /* AOL */ +#endif + "As if!", /* Clueless */ +}; + +char * +random_engraving(outbuf) +char *outbuf; +{ + const char *rumor; + + /* a random engraving may come from the "rumors" file, + or from the list above */ + if (!rn2(4) || !(rumor = getrumor(0, outbuf, TRUE)) || !*rumor) + Strcpy(outbuf, random_mesg[rn2(SIZE(random_mesg))]); + + wipeout_text(outbuf, (int)(strlen(outbuf) / 4), 0); + return outbuf; +} + +/* Partial rubouts for engraving characters. -3. */ +static const struct { + char wipefrom; + const char * wipeto; +} rubouts[] = { + {'A', "^"}, {'B', "Pb["}, {'C', "("}, {'D', "|)["}, + {'E', "|FL[_"}, {'F', "|-"}, {'G', "C("}, {'H', "|-"}, + {'I', "|"}, {'K', "|<"}, {'L', "|_"}, {'M', "|"}, + {'N', "|\\"}, {'O', "C("}, {'P', "F"}, {'Q', "C("}, + {'R', "PF"}, {'T', "|"}, {'U', "J"}, {'V', "/\\"}, + {'W', "V/\\"}, {'Z', "/"}, + {'b', "|"}, {'d', "c|"}, {'e', "c"}, {'g', "c"}, + {'h', "n"}, {'j', "i"}, {'k', "|"}, {'l', "|"}, + {'m', "nr"}, {'n', "r"}, {'o', "c"}, {'q', "c"}, + {'w', "v"}, {'y', "v"}, + {':', "."}, {';', ","}, + {'0', "C("}, {'1', "|"}, {'6', "o"}, {'7', "/"}, + {'8', "3o"} +}; + +void +wipeout_text(engr, cnt, seed) +char *engr; +int cnt; +unsigned seed; /* for semi-controlled randomization */ +{ + char *s; + int i, j, nxt, use_rubout, lth = (int)strlen(engr); + + if (lth && cnt > 0) { + while (cnt--) { + /* pick next character */ + if (!seed) { + /* random */ + nxt = rn2(lth); + use_rubout = rn2(4); + } else { + /* predictable; caller can reproduce the same sequence by + supplying the same arguments later, or a pseudo-random + sequence by varying any of them */ + nxt = seed % lth; + seed *= 31, seed %= (BUFSZ-1); + use_rubout = seed & 3; + } + s = &engr[nxt]; + if (*s == ' ') continue; + + /* rub out unreadable & small punctuation marks */ + if (index("?.,'`-|_", *s)) { + *s = ' '; + continue; + } + + if (!use_rubout) + i = SIZE(rubouts); + else + for (i = 0; i < SIZE(rubouts); i++) + if (*s == rubouts[i].wipefrom) { + /* + * Pick one of the substitutes at random. + */ + if (!seed) + j = rn2(strlen(rubouts[i].wipeto)); + else { + seed *= 31, seed %= (BUFSZ-1); + j = seed % (strlen(rubouts[i].wipeto)); + } + *s = rubouts[i].wipeto[j]; + break; + } + + /* didn't pick rubout; use '?' for unreadable character */ + if (i == SIZE(rubouts)) *s = '?'; + } + } + + /* trim trailing spaces */ + while (lth && engr[lth-1] == ' ') engr[--lth] = 0; +} + +boolean +can_reach_floor() +{ + return (boolean)(!u.uswallow && +#ifdef STEED + /* Restricted/unskilled riders can't reach the floor */ + !(u.usteed && P_SKILL(P_RIDING) < P_BASIC) && +#endif + (!Levitation || + Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))); +} +#endif /* OVLB */ +#ifdef OVL0 + +const char * +surface(x, y) +register int x, y; +{ + register struct rm *lev = &levl[x][y]; + + if ((x == u.ux) && (y == u.uy) && u.uswallow && + is_animal(u.ustuck->data)) + return "maw"; + else if (IS_AIR(lev->typ) && Is_airlevel(&u.uz)) + return "air"; + else if (is_pool(x,y)) + return (Underwater && !Is_waterlevel(&u.uz)) ? "bottom" : "water"; + else if (is_ice(x,y)) + return "ice"; + else if (is_lava(x,y)) + return "lava"; + else if (lev->typ == DRAWBRIDGE_DOWN) + return "bridge"; + else if(IS_ALTAR(levl[x][y].typ)) + return "altar"; + else if(IS_GRAVE(levl[x][y].typ)) + return "headstone"; + else if(IS_FOUNTAIN(levl[x][y].typ)) + return "fountain"; + else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz)) || + IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR) + return "floor"; + else + return "ground"; +} + +const char * +ceiling(x, y) +register int x, y; +{ + register struct rm *lev = &levl[x][y]; + const char *what; + + /* other room types will no longer exist when we're interested -- + * see check_special_room() + */ + if (*in_rooms(x,y,VAULT)) + what = "vault's ceiling"; + else if (*in_rooms(x,y,TEMPLE)) + what = "temple's ceiling"; + else if (*in_rooms(x,y,SHOPBASE)) + what = "shop's ceiling"; + else if (IS_AIR(lev->typ)) + what = "sky"; + else if (Underwater) + what = "water's surface"; + else if ((IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz)) || + IS_WALL(lev->typ) || IS_DOOR(lev->typ) || lev->typ == SDOOR) + what = "ceiling"; + else + what = "rock above"; + + return what; +} + +struct engr * +engr_at(x, y) +xchar x, y; +{ + register struct engr *ep = head_engr; + + while(ep) { + if(x == ep->engr_x && y == ep->engr_y) + return(ep); + ep = ep->nxt_engr; + } + return((struct engr *) 0); +} + +#ifdef ELBERETH +/* Decide whether a particular string is engraved at a specified + * location; a case-insensitive substring match used. + * Ignore headstones, in case the player names herself "Elbereth". + */ +int +sengr_at(s, x, y) + const char *s; + xchar x, y; +{ + register struct engr *ep = engr_at(x,y); + + return (ep && ep->engr_type != HEADSTONE && + ep->engr_time <= moves && strstri(ep->engr_txt, s) != 0); +} +#endif /* ELBERETH */ + +#endif /* OVL0 */ +#ifdef OVL2 + +void +u_wipe_engr(cnt) +register int cnt; +{ + if (can_reach_floor()) + wipe_engr_at(u.ux, u.uy, cnt); +} + +#endif /* OVL2 */ +#ifdef OVL1 + +void +wipe_engr_at(x,y,cnt) +register xchar x,y,cnt; +{ + register struct engr *ep = engr_at(x,y); + + /* Headstones are indelible */ + if(ep && ep->engr_type != HEADSTONE){ + if(ep->engr_type != BURN || is_ice(x,y)) { + if(ep->engr_type != DUST && ep->engr_type != ENGR_BLOOD) { + cnt = rn2(1 + 50/(cnt+1)) ? 0 : 1; + } + wipeout_text(ep->engr_txt, (int)cnt, 0); + while(ep->engr_txt[0] == ' ') + ep->engr_txt++; + if(!ep->engr_txt[0]) del_engr(ep); + } + } +} + +#endif /* OVL1 */ +#ifdef OVL2 + +void +read_engr_at(x,y) +register int x,y; +{ + register struct engr *ep = engr_at(x,y); + register int sensed = 0; + char buf[BUFSZ]; + + /* Sensing an engraving does not require sight, + * nor does it necessarily imply comprehension (literacy). + */ + if(ep && ep->engr_txt[0]) { + switch(ep->engr_type) { + case DUST: + if(!Blind) { + sensed = 1; + pline("%s is written here in the %s.", Something, + is_ice(x,y) ? "frost" : "dust"); + } + break; + case ENGRAVE: + case HEADSTONE: + if (!Blind || can_reach_floor()) { + sensed = 1; + pline("%s is engraved here on the %s.", + Something, + surface(x,y)); + } + break; + case BURN: + if (!Blind || can_reach_floor()) { + sensed = 1; + pline("Some text has been %s into the %s here.", + is_ice(x,y) ? "melted" : "burned", + surface(x,y)); + } + break; + case MARK: + if(!Blind) { + sensed = 1; + pline("There's some graffiti on the %s here.", + surface(x,y)); + } + break; + case ENGR_BLOOD: + /* "It's a message! Scrawled in blood!" + * "What's it say?" + * "It says... `See you next Wednesday.'" -- Thriller + */ + if(!Blind) { + sensed = 1; + You("see a message scrawled in blood here."); + } + break; + default: + impossible("%s is written in a very strange way.", + Something); + sensed = 1; + } + if (sensed) { + char *et; + unsigned maxelen = BUFSZ - sizeof("You feel the words: \"\". "); + if (strlen(ep->engr_txt) > maxelen) { + (void) strncpy(buf, ep->engr_txt, (int)maxelen); + buf[maxelen] = '\0'; + et = buf; + } else + et = ep->engr_txt; + You("%s: \"%s\".", + (Blind) ? "feel the words" : "read", et); + if(flags.run > 1) nomul(0); + } + } +} + +#endif /* OVL2 */ +#ifdef OVLB + +void +make_engr_at(x,y,s,e_time,e_type) +register int x,y; +register const char *s; +register long e_time; +register xchar e_type; +{ + register struct engr *ep; + + if ((ep = engr_at(x,y)) != 0) + del_engr(ep); + ep = newengr(strlen(s) + 1); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_x = x; + ep->engr_y = y; + ep->engr_txt = (char *)(ep + 1); + Strcpy(ep->engr_txt, s); + /* engraving Elbereth shows wisdom */ + if (!in_mklev && !strcmp(s, "Elbereth")) exercise(A_WIS, TRUE); + ep->engr_time = e_time; + ep->engr_type = e_type > 0 ? e_type : rnd(N_ENGRAVE-1); + ep->engr_lth = strlen(s) + 1; +} + +/* delete any engraving at location */ +void +del_engr_at(x, y) +int x, y; +{ + register struct engr *ep = engr_at(x, y); + + if (ep) del_engr(ep); +} + +/* + * freehand - returns true if player has a free hand + */ +int +freehand() +{ + return(!uwep || !welded(uwep) || + (!bimanual(uwep) && (!uarms || !uarms->cursed))); +/* if ((uwep && bimanual(uwep)) || + (uwep && uarms)) + return(0); + else + return(1);*/ +} + +static NEARDATA const char styluses[] = + { ALL_CLASSES, ALLOW_NONE, TOOL_CLASS, WEAPON_CLASS, WAND_CLASS, + GEM_CLASS, RING_CLASS, 0 }; + +/* Mohs' Hardness Scale: + * 1 - Talc 6 - Orthoclase + * 2 - Gypsum 7 - Quartz + * 3 - Calcite 8 - Topaz + * 4 - Fluorite 9 - Corundum + * 5 - Apatite 10 - Diamond + * + * Since granite is a igneous rock hardness ~ 7, anything >= 8 should + * probably be able to scratch the rock. + * Devaluation of less hard gems is not easily possible because obj struct + * does not contain individual oc_cost currently. 7/91 + * + * steel - 5-8.5 (usu. weapon) + * diamond - 10 * jade - 5-6 (nephrite) + * ruby - 9 (corundum) * turquoise - 5-6 + * sapphire - 9 (corundum) * opal - 5-6 + * topaz - 8 * glass - ~5.5 + * emerald - 7.5-8 (beryl) * dilithium - 4-5?? + * aquamarine - 7.5-8 (beryl) * iron - 4-5 + * garnet - 7.25 (var. 6.5-8) * fluorite - 4 + * agate - 7 (quartz) * brass - 3-4 + * amethyst - 7 (quartz) * gold - 2.5-3 + * jasper - 7 (quartz) * silver - 2.5-3 + * onyx - 7 (quartz) * copper - 2.5-3 + * moonstone - 6 (orthoclase) * amber - 2-2.5 + */ + +/* return 1 if action took 1 (or more) moves, 0 if error or aborted */ +int +doengrave() +{ + boolean dengr = FALSE; /* TRUE if we wipe out the current engraving */ + boolean doblind = FALSE;/* TRUE if engraving blinds the player */ + boolean doknown = FALSE;/* TRUE if we identify the stylus */ + boolean eow = FALSE; /* TRUE if we are overwriting oep */ + boolean jello = FALSE; /* TRUE if we are engraving in slime */ + boolean ptext = TRUE; /* TRUE if we must prompt for engrave text */ + boolean teleengr =FALSE;/* TRUE if we move the old engraving */ + boolean zapwand = FALSE;/* TRUE if we remove a wand charge */ + xchar type = DUST; /* Type of engraving made */ + char buf[BUFSZ]; /* Buffer for final/poly engraving text */ + char ebuf[BUFSZ]; /* Buffer for initial engraving text */ + char qbuf[QBUFSZ]; /* Buffer for query text */ + char post_engr_text[BUFSZ]; /* Text displayed after engraving prompt */ + const char *everb; /* Present tense of engraving type */ + const char *eloc; /* Where the engraving is (ie dust/floor/...) */ + char *sp; /* Place holder for space count of engr text */ + int len; /* # of nonspace chars of new engraving text */ + int maxelen; /* Max allowable length of engraving text */ + struct engr *oep = engr_at(u.ux,u.uy); + /* The current engraving */ + struct obj *otmp; /* Object selected with which to engrave */ + char *writer; + + multi = 0; /* moves consumed */ + nomovemsg = (char *)0; /* occupation end message */ + + buf[0] = (char)0; + ebuf[0] = (char)0; + post_engr_text[0] = (char)0; + maxelen = BUFSZ - 1; + if (is_demon(youmonst.data) || youmonst.data->mlet == S_VAMPIRE) + type = ENGR_BLOOD; + + /* Can the adventurer engrave at all? */ + + if(u.uswallow) { + if (is_animal(u.ustuck->data)) { + pline("What would you write? \"Jonah was here\"?"); + return(0); + } else if (is_whirly(u.ustuck->data)) { + You_cant("reach the %s.", surface(u.ux,u.uy)); + return(0); + } else + jello = TRUE; + } else if (is_lava(u.ux, u.uy)) { + You_cant("write on the lava!"); + return(0); + } else if (is_pool(u.ux,u.uy) || IS_FOUNTAIN(levl[u.ux][u.uy].typ)) { + You_cant("write on the water!"); + return(0); + } + if(Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)/* in bubble */) { + You_cant("write in thin air!"); + return(0); + } + if (cantwield(youmonst.data)) { + You_cant("even hold anything!"); + return(0); + } + if (check_capacity((char *)0)) return (0); + + /* One may write with finger, or weapon, or wand, or..., or... + * Edited by GAN 10/20/86 so as not to change weapon wielded. + */ + + otmp = getobj(styluses, "write with"); + if(!otmp) return(0); /* otmp == zeroobj if fingers */ + + if (otmp == &zeroobj) writer = makeplural(body_part(FINGER)); + else writer = xname(otmp); + + /* There's no reason you should be able to write with a wand + * while both your hands are tied up. + */ + if (!freehand() && otmp != uwep && !otmp->owornmask) { + You("have no free %s to write with!", body_part(HAND)); + return(0); + } + + if (jello) { + You("tickle %s with your %s.", mon_nam(u.ustuck), writer); + Your("message dissolves..."); + return(0); + } + if (otmp->oclass != WAND_CLASS && !can_reach_floor()) { + You_cant("reach the %s!", surface(u.ux,u.uy)); + return(0); + } + if (IS_ALTAR(levl[u.ux][u.uy].typ)) { + You("make a motion towards the altar with your %s.", writer); + altar_wrath(u.ux, u.uy); + return(0); + } + if (IS_GRAVE(levl[u.ux][u.uy].typ)) { + if (otmp == &zeroobj) { /* using only finger */ + You("would only make a small smudge on the %s.", + surface(u.ux, u.uy)); + return(0); + } else if (!levl[u.ux][u.uy].disturbed) { + You("disturb the undead!"); + levl[u.ux][u.uy].disturbed = 1; + (void) makemon(&mons[PM_GHOUL], u.ux, u.uy, NO_MM_FLAGS); + exercise(A_WIS, FALSE); + return(1); + } + } + + /* SPFX for items */ + + switch (otmp->oclass) { + default: + case AMULET_CLASS: + case CHAIN_CLASS: + case POTION_CLASS: + case COIN_CLASS: + break; + + case RING_CLASS: + /* "diamond" rings and others should work */ + case GEM_CLASS: + /* diamonds & other hard gems should work */ + if (objects[otmp->otyp].oc_tough) { + type = ENGRAVE; + break; + } + break; + + case ARMOR_CLASS: + if (is_boots(otmp)) { + type = DUST; + break; + } + /* fall through */ + /* Objects too large to engrave with */ + case BALL_CLASS: + case ROCK_CLASS: + You_cant("engrave with such a large object!"); + ptext = FALSE; + break; + + /* Objects too silly to engrave with */ + case FOOD_CLASS: + case SCROLL_CLASS: + case SPBOOK_CLASS: + Your("%s would get %s.", xname(otmp), + is_ice(u.ux,u.uy) ? "all frosty" : "too dirty"); + ptext = FALSE; + break; + + case RANDOM_CLASS: /* This should mean fingers */ + break; + + /* The charge is removed from the wand before prompting for + * the engraving text, because all kinds of setup decisions + * and pre-engraving messages are based upon knowing what type + * of engraving the wand is going to do. Also, the player + * will have potentially seen "You wrest .." message, and + * therefore will know they are using a charge. + */ + case WAND_CLASS: + if (zappable(otmp)) { + check_unpaid(otmp); + zapwand = TRUE; + if (Levitation) ptext = FALSE; + + switch (otmp->otyp) { + /* DUST wands */ + default: + break; + + /* NODIR wands */ + case WAN_LIGHT: + case WAN_SECRET_DOOR_DETECTION: + case WAN_CREATE_MONSTER: + case WAN_WISHING: + case WAN_ENLIGHTENMENT: + zapnodir(otmp); + break; + + /* IMMEDIATE wands */ + /* If wand is "IMMEDIATE", remember to affect the + * previous engraving even if turning to dust. + */ + case WAN_STRIKING: + Strcpy(post_engr_text, + "The wand unsuccessfully fights your attempt to write!" + ); + break; + case WAN_SLOW_MONSTER: + if (!Blind) { + Sprintf(post_engr_text, + "The bugs on the %s slow down!", + surface(u.ux, u.uy)); + } + break; + case WAN_SPEED_MONSTER: + if (!Blind) { + Sprintf(post_engr_text, + "The bugs on the %s speed up!", + surface(u.ux, u.uy)); + } + break; + case WAN_POLYMORPH: + if(oep) { + if (!Blind) { + type = (xchar)0; /* random */ + (void) random_engraving(buf); + } + dengr = TRUE; + } + break; + case WAN_NOTHING: + case WAN_UNDEAD_TURNING: + case WAN_OPENING: + case WAN_LOCKING: + case WAN_PROBING: + break; + + /* RAY wands */ + case WAN_MAGIC_MISSILE: + ptext = TRUE; + if (!Blind) { + Sprintf(post_engr_text, + "The %s is riddled by bullet holes!", + surface(u.ux, u.uy)); + } + break; + + /* can't tell sleep from death - Eric Backus */ + case WAN_SLEEP: + case WAN_DEATH: + if (!Blind) { + Sprintf(post_engr_text, + "The bugs on the %s stop moving!", + surface(u.ux, u.uy)); + } + break; + + case WAN_COLD: + if (!Blind) + Strcpy(post_engr_text, + "A few ice cubes drop from the wand."); + if(!oep || (oep->engr_type != BURN)) + break; + case WAN_CANCELLATION: + case WAN_MAKE_INVISIBLE: + if (oep && oep->engr_type != HEADSTONE) { + if (!Blind) + pline_The("engraving on the %s vanishes!", + surface(u.ux,u.uy)); + dengr = TRUE; + } + break; + case WAN_TELEPORTATION: + if (oep && oep->engr_type != HEADSTONE) { + if (!Blind) + pline_The("engraving on the %s vanishes!", + surface(u.ux,u.uy)); + teleengr = TRUE; + } + break; + + /* type = ENGRAVE wands */ + case WAN_DIGGING: + ptext = TRUE; + type = ENGRAVE; + if(!objects[otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of digging!", + xname(otmp)); + doknown = TRUE; + } + if (!Blind) + Strcpy(post_engr_text, + IS_GRAVE(levl[u.ux][u.uy].typ) ? + "Chips fly out from the headstone." : + is_ice(u.ux,u.uy) ? + "Ice chips fly up from the ice surface!" : + "Gravel flies up from the floor."); + else + Strcpy(post_engr_text, "You hear drilling!"); + break; + + /* type = BURN wands */ + case WAN_FIRE: + ptext = TRUE; + type = BURN; + if(!objects[otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of fire!", xname(otmp)); + doknown = TRUE; + } + Strcpy(post_engr_text, + Blind ? "You feel the wand heat up." : + "Flames fly from the wand."); + break; + case WAN_LIGHTNING: + ptext = TRUE; + type = BURN; + if(!objects[otmp->otyp].oc_name_known) { + if (flags.verbose) + pline("This %s is a wand of lightning!", + xname(otmp)); + doknown = TRUE; + } + if (!Blind) { + Strcpy(post_engr_text, + "Lightning arcs from the wand."); + doblind = TRUE; + } else + Strcpy(post_engr_text, "You hear crackling!"); + break; + + /* type = MARK wands */ + /* type = ENGR_BLOOD wands */ + } + } else /* end if zappable */ + if (!can_reach_floor()) { + You_cant("reach the %s!", surface(u.ux,u.uy)); + return(0); + } + break; + + case WEAPON_CLASS: + if (is_blade(otmp)) { + if ((int)otmp->spe > -3) + type = ENGRAVE; + else + Your("%s too dull for engraving.", aobjnam(otmp,"are")); + } + break; + + case TOOL_CLASS: + if(otmp == ublindf) { + pline( + "That is a bit difficult to engrave with, don't you think?"); + return(0); + } + switch (otmp->otyp) { + case MAGIC_MARKER: + if (otmp->spe <= 0) + Your("marker has dried out."); + else + type = MARK; + break; + case TOWEL: + /* Can't really engrave with a towel */ + ptext = FALSE; + if (oep) + if ((oep->engr_type == DUST ) || + (oep->engr_type == ENGR_BLOOD) || + (oep->engr_type == MARK )) { + if (!Blind) + You("wipe out the message here."); + else + Your("%s %s %s.", xname(otmp), + otense(otmp, "get"), + is_ice(u.ux,u.uy) ? + "frosty" : "dusty"); + dengr = TRUE; + } else + Your("%s can't wipe out this engraving.", + xname(otmp)); + else + Your("%s %s %s.", xname(otmp), otense(otmp, "get"), + is_ice(u.ux,u.uy) ? "frosty" : "dusty"); + break; + default: + break; + } + break; + + case VENOM_CLASS: +#ifdef WIZARD + if (wizard) { + pline("Writing a poison pen letter??"); + break; + } +#endif + case ILLOBJ_CLASS: + impossible("You're engraving with an illegal object!"); + break; + } + + if (IS_GRAVE(levl[u.ux][u.uy].typ)) { + if (type == ENGRAVE || type == 0) + type = HEADSTONE; + else { + /* ensures the "cannot wipe out" case */ + type = DUST; + dengr = FALSE; + teleengr = FALSE; + buf[0] = (char)0; + } + } + + /* End of implement setup */ + + /* Identify stylus */ + if (doknown) { + makeknown(otmp->otyp); + more_experienced(0,10); + } + + if (teleengr) { + rloc_engr(oep); + oep = (struct engr *)0; + } + + if (dengr) { + del_engr(oep); + oep = (struct engr *)0; + } + + /* Something has changed the engraving here */ + if (*buf) { + make_engr_at(u.ux, u.uy, buf, moves, type); + pline_The("engraving now reads: \"%s\".", buf); + ptext = FALSE; + } + + if (zapwand && (otmp->spe < 0)) { + pline("%s %sturns to dust.", + The(xname(otmp)), Blind ? "" : "glows violently, then "); + if (!IS_GRAVE(levl[u.ux][u.uy].typ)) + You("are not going to get anywhere trying to write in the %s with your dust.", + is_ice(u.ux,u.uy) ? "frost" : "dust"); + useup(otmp); + ptext = FALSE; + } + + if (!ptext) { /* Early exit for some implements. */ + if (otmp->oclass == WAND_CLASS && !can_reach_floor()) + You_cant("reach the %s!", surface(u.ux,u.uy)); + return(1); + } + + /* Special effects should have deleted the current engraving (if + * possible) by now. + */ + + if (oep) { + register char c = 'n'; + + /* Give player the choice to add to engraving. */ + + if (type == HEADSTONE) { + /* no choice, only append */ + c = 'y'; + } else if ( (type == oep->engr_type) && (!Blind || + (oep->engr_type == BURN) || (oep->engr_type == ENGRAVE)) ) { + c = yn_function("Do you want to add to the current engraving?", + ynqchars, 'y'); + if (c == 'q') { + pline(Never_mind); + return(0); + } + } + + if (c == 'n' || Blind) { + + if( (oep->engr_type == DUST) || (oep->engr_type == ENGR_BLOOD) || + (oep->engr_type == MARK) ) { + if (!Blind) { + You("wipe out the message that was %s here.", + ((oep->engr_type == DUST) ? "written in the dust" : + ((oep->engr_type == ENGR_BLOOD) ? "scrawled in blood" : + "written"))); + del_engr(oep); + oep = (struct engr *)0; + } else + /* Don't delete engr until after we *know* we're engraving */ + eow = TRUE; + } else + if ( (type == DUST) || (type == MARK) || (type == ENGR_BLOOD) ) { + You( + "cannot wipe out the message that is %s the %s here.", + oep->engr_type == BURN ? + (is_ice(u.ux,u.uy) ? "melted into" : "burned into") : + "engraved in", surface(u.ux,u.uy)); + return(1); + } else + if ( (type != oep->engr_type) || (c == 'n') ) { + if (!Blind || can_reach_floor()) + You("will overwrite the current message."); + eow = TRUE; + } + } + } + + eloc = surface(u.ux,u.uy); + switch(type){ + default: + everb = (oep && !eow ? "add to the weird writing on" : + "write strangely on"); + break; + case DUST: + everb = (oep && !eow ? "add to the writing in" : + "write in"); + eloc = is_ice(u.ux,u.uy) ? "frost" : "dust"; + break; + case HEADSTONE: + everb = (oep && !eow ? "add to the epitaph on" : + "engrave on"); + break; + case ENGRAVE: + everb = (oep && !eow ? "add to the engraving in" : + "engrave in"); + break; + case BURN: + everb = (oep && !eow ? + ( is_ice(u.ux,u.uy) ? "add to the text melted into" : + "add to the text burned into") : + ( is_ice(u.ux,u.uy) ? "melt into" : "burn into")); + break; + case MARK: + everb = (oep && !eow ? "add to the graffiti on" : + "scribble on"); + break; + case ENGR_BLOOD: + everb = (oep && !eow ? "add to the scrawl on" : + "scrawl on"); + break; + } + + /* Tell adventurer what is going on */ + if (otmp != &zeroobj) + You("%s the %s with %s.", everb, eloc, doname(otmp)); + else + You("%s the %s with your %s.", everb, eloc, + makeplural(body_part(FINGER))); + + /* Prompt for engraving! */ + Sprintf(qbuf,"What do you want to %s the %s here?", everb, eloc); + getlin(qbuf, ebuf); + + /* Count the actual # of chars engraved not including spaces */ + len = strlen(ebuf); + for (sp = ebuf; *sp; sp++) if (isspace(*sp)) len -= 1; + + if (len == 0 || index(ebuf, '\033')) { + if (zapwand) { + if (!Blind) + pline("%s, then %s.", + Tobjnam(otmp, "glow"), otense(otmp, "fade")); + return(1); + } else { + pline(Never_mind); + return(0); + } + } + + /* A single `x' is the traditional signature of an illiterate person */ + if (len != 1 || (!index(ebuf, 'x') && !index(ebuf, 'X'))) + u.uconduct.literate++; + + /* Mix up engraving if surface or state of mind is unsound. + Note: this won't add or remove any spaces. */ + for (sp = ebuf; *sp; sp++) { + if (isspace(*sp)) continue; + if (((type == DUST || type == ENGR_BLOOD) && !rn2(25)) || + (Blind && !rn2(11)) || (Confusion && !rn2(7)) || + (Stunned && !rn2(4)) || (Hallucination && !rn2(2))) + *sp = ' ' + rnd(96 - 2); /* ASCII '!' thru '~' + (excludes ' ' and DEL) */ + } + + /* Previous engraving is overwritten */ + if (eow) { + del_engr(oep); + oep = (struct engr *)0; + } + + /* Figure out how long it took to engrave, and if player has + * engraved too much. + */ + switch(type){ + default: + multi = -(len/10); + if (multi) nomovemsg = "You finish your weird engraving."; + break; + case DUST: + multi = -(len/10); + if (multi) nomovemsg = "You finish writing in the dust."; + break; + case HEADSTONE: + case ENGRAVE: + multi = -(len/10); + if ((otmp->oclass == WEAPON_CLASS) && + ((otmp->otyp != ATHAME) || otmp->cursed)) { + multi = -len; + maxelen = ((otmp->spe + 3) * 2) + 1; + /* -2 = 3, -1 = 5, 0 = 7, +1 = 9, +2 = 11 + * Note: this does not allow a +0 anything (except + * an athame) to engrave "Elbereth" all at once. + * However, you could now engrave "Elb", then + * "ere", then "th". + */ + Your("%s dull.", aobjnam(otmp, "get")); + if (otmp->unpaid) { + struct monst *shkp = shop_keeper(*u.ushops); + if (shkp) { + You("damage it, you pay for it!"); + bill_dummy_object(otmp); + } + } + if (len > maxelen) { + multi = -maxelen; + otmp->spe = -3; + } else if (len > 1) + otmp->spe -= len >> 1; + else otmp->spe -= 1; /* Prevent infinite engraving */ + } else + if ( (otmp->oclass == RING_CLASS) || + (otmp->oclass == GEM_CLASS) ) + multi = -len; + if (multi) nomovemsg = "You finish engraving."; + break; + case BURN: + multi = -(len/10); + if (multi) + nomovemsg = is_ice(u.ux,u.uy) ? + "You finish melting your message into the ice.": + "You finish burning your message into the floor."; + break; + case MARK: + multi = -(len/10); + if ((otmp->oclass == TOOL_CLASS) && + (otmp->otyp == MAGIC_MARKER)) { + maxelen = (otmp->spe) * 2; /* one charge / 2 letters */ + if (len > maxelen) { + Your("marker dries out."); + otmp->spe = 0; + multi = -(maxelen/10); + } else + if (len > 1) otmp->spe -= len >> 1; + else otmp->spe -= 1; /* Prevent infinite grafitti */ + } + if (multi) nomovemsg = "You finish defacing the dungeon."; + break; + case ENGR_BLOOD: + multi = -(len/10); + if (multi) nomovemsg = "You finish scrawling."; + break; + } + + /* Chop engraving down to size if necessary */ + if (len > maxelen) { + for (sp = ebuf; (maxelen && *sp); sp++) + if (!isspace(*sp)) maxelen--; + if (!maxelen && *sp) { + *sp = (char)0; + if (multi) nomovemsg = "You cannot write any more."; + You("only are able to write \"%s\"", ebuf); + } + } + + /* Add to existing engraving */ + if (oep) Strcpy(buf, oep->engr_txt); + + (void) strncat(buf, ebuf, (BUFSZ - (int)strlen(buf) - 1)); + + make_engr_at(u.ux, u.uy, buf, (moves - multi), type); + + if (post_engr_text[0]) pline(post_engr_text); + + if (doblind && !resists_blnd(&youmonst)) { + You("are blinded by the flash!"); + make_blinded((long)rnd(50),FALSE); + if (!Blind) Your(vision_clears); + } + + return(1); +} + +void +save_engravings(fd, mode) +int fd, mode; +{ + register struct engr *ep = head_engr; + register struct engr *ep2; + unsigned no_more_engr = 0; + + while (ep) { + ep2 = ep->nxt_engr; + if (ep->engr_lth && ep->engr_txt[0] && perform_bwrite(mode)) { + bwrite(fd, (genericptr_t)&(ep->engr_lth), sizeof(ep->engr_lth)); + bwrite(fd, (genericptr_t)ep, sizeof(struct engr) + ep->engr_lth); + } + if (release_data(mode)) + dealloc_engr(ep); + ep = ep2; + } + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t)&no_more_engr, sizeof no_more_engr); + if (release_data(mode)) + head_engr = 0; +} + +void +rest_engravings(fd) +int fd; +{ + register struct engr *ep; + unsigned lth; + + head_engr = 0; + while(1) { + mread(fd, (genericptr_t) <h, sizeof(unsigned)); + if(lth == 0) return; + ep = newengr(lth); + mread(fd, (genericptr_t) ep, sizeof(struct engr) + lth); + ep->nxt_engr = head_engr; + head_engr = ep; + ep->engr_txt = (char *) (ep + 1); /* Andreas Bormann */ + /* mark as finished for bones levels -- no problem for + * normal levels as the player must have finished engraving + * to be able to move again */ + ep->engr_time = moves; + } +} + +void +del_engr(ep) +register struct engr *ep; +{ + if (ep == head_engr) { + head_engr = ep->nxt_engr; + } else { + register struct engr *ept; + + for (ept = head_engr; ept; ept = ept->nxt_engr) + if (ept->nxt_engr == ep) { + ept->nxt_engr = ep->nxt_engr; + break; + } + if (!ept) { + impossible("Error in del_engr?"); + return; + } + } + dealloc_engr(ep); +} + +/* randomly relocate an engraving */ +void +rloc_engr(ep) +struct engr *ep; +{ + int tx, ty, tryct = 200; + + do { + if (--tryct < 0) return; + tx = rn1(COLNO-3,2); + ty = rn2(ROWNO); + } while (engr_at(tx, ty) || + !goodpos(tx, ty, (struct monst *)0, 0)); + + ep->engr_x = tx; + ep->engr_y = ty; +} + + +/* Epitaphs for random headstones */ +static const char *epitaphs[] = { + "Rest in peace", + "R.I.P.", + "Rest In Pieces", + "Note -- there are NO valuable items in this grave", + "1994-1995. The Longest-Lived Hacker Ever", + "The Grave of the Unknown Hacker", + "We weren't sure who this was, but we buried him here anyway", + "Sparky -- he was a very good dog", + "Beware of Electric Third Rail", + "Made in Taiwan", + "Og friend. Og good dude. Og died. Og now food", + "Beetlejuice Beetlejuice Beetlejuice", + "Look out below!", + "Please don't dig me up. I'm perfectly happy down here. -- Resident", + "Postman, please note forwarding address: Gehennom, Asmodeus's Fortress, fifth lemure on the left", + "Mary had a little lamb/Its fleece was white as snow/When Mary was in trouble/The lamb was first to go", + "Be careful, or this could happen to you!", + "Soon you'll join this fellow in hell! -- the Wizard of Yendor", + "Caution! This grave contains toxic waste", + "Sum quod eris", + "Here lies an Atheist, all dressed up and no place to go", + "Here lies Ezekiel, age 102. The good die young.", + "Here lies my wife: Here let her lie! Now she's at rest and so am I.", + "Here lies Johnny Yeast. Pardon me for not rising.", + "He always lied while on the earth and now he's lying in it", + "I made an ash of myself", + "Soon ripe. Soon rotten. Soon gone. But not forgotten.", + "Here lies the body of Jonathan Blake. Stepped on the gas instead of the brake.", + "Go away!" +}; + +/* Create a headstone at the given location. + * The caller is responsible for newsym(x, y). + */ +void +make_grave(x, y, str) +int x, y; +const char *str; +{ + /* Can we put a grave here? */ + if ((levl[x][y].typ != ROOM && levl[x][y].typ != GRAVE) || t_at(x,y)) return; + + /* Make the grave */ + levl[x][y].typ = GRAVE; + + /* Engrave the headstone */ + if (!str) str = epitaphs[rn2(SIZE(epitaphs))]; + del_engr_at(x, y); + make_engr_at(x, y, str, 0L, HEADSTONE); + return; +} + + +#endif /* OVLB */ + +/*engrave.c*/ diff --git a/src/exper.c b/src/exper.c new file mode 100644 index 0000000..649f1c6 --- /dev/null +++ b/src/exper.c @@ -0,0 +1,248 @@ +/* SCCS Id: @(#)exper.c 3.4 2002/11/20 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL long FDECL(newuexp, (int)); +STATIC_DCL int FDECL(enermod, (int)); + +STATIC_OVL long +newuexp(lev) +int lev; +{ + if (lev < 10) return (10L * (1L << lev)); + if (lev < 20) return (10000L * (1L << (lev - 10))); + return (10000000L * ((long)(lev - 19))); +} + +STATIC_OVL int +enermod(en) +int en; +{ + switch (Role_switch) { + case PM_PRIEST: + case PM_WIZARD: + return(2 * en); + case PM_HEALER: + case PM_KNIGHT: + return((3 * en) / 2); + case PM_BARBARIAN: + case PM_VALKYRIE: + return((3 * en) / 4); + default: + return (en); + } +} + +int +experience(mtmp, nk) /* return # of exp points for mtmp after nk killed */ + register struct monst *mtmp; + register int nk; +#if defined(macintosh) && (defined(__SC__) || defined(__MRC__)) +# pragma unused(nk) +#endif +{ + register struct permonst *ptr = mtmp->data; + int i, tmp, tmp2; + + tmp = 1 + mtmp->m_lev * mtmp->m_lev; + +/* For higher ac values, give extra experience */ + if ((i = find_mac(mtmp)) < 3) tmp += (7 - i) * ((i < 0) ? 2 : 1); + +/* For very fast monsters, give extra experience */ + if (ptr->mmove > NORMAL_SPEED) + tmp += (ptr->mmove > (3*NORMAL_SPEED/2)) ? 5 : 3; + +/* For each "special" attack type give extra experience */ + for(i = 0; i < NATTK; i++) { + + tmp2 = ptr->mattk[i].aatyp; + if(tmp2 > AT_BUTT) { + + if(tmp2 == AT_WEAP) tmp += 5; + else if(tmp2 == AT_MAGC) tmp += 10; + else tmp += 3; + } + } + +/* For each "special" damage type give extra experience */ + for(i = 0; i < NATTK; i++) { + tmp2 = ptr->mattk[i].adtyp; + if(tmp2 > AD_PHYS && tmp2 < AD_BLND) tmp += 2*mtmp->m_lev; + else if((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || + (tmp2 == AD_SLIM)) tmp += 50; + else if(tmp != AD_PHYS) tmp += mtmp->m_lev; + /* extra heavy damage bonus */ + if((int)(ptr->mattk[i].damd * ptr->mattk[i].damn) > 23) + tmp += mtmp->m_lev; + if (tmp2 == AD_WRAP && ptr->mlet == S_EEL && !Amphibious) + tmp += 1000; + } + +/* For certain "extra nasty" monsters, give even more */ + if (extra_nasty(ptr)) tmp += (7 * mtmp->m_lev); + +/* For higher level monsters, an additional bonus is given */ + if(mtmp->m_lev > 8) tmp += 50; + +#ifdef MAIL + /* Mail daemons put up no fight. */ + if(mtmp->data == &mons[PM_MAIL_DAEMON]) tmp = 1; +#endif + + return(tmp); +} + +void +more_experienced(exp, rexp) + register int exp, rexp; +{ + u.uexp += exp; + u.urexp += 4*exp + rexp; + if(exp +#ifdef SCORE_ON_BOTL + || flags.showscore +#endif + ) flags.botl = 1; + if (u.urexp >= (Role_if(PM_WIZARD) ? 1000 : 2000)) + flags.beginner = 0; +} + +void +losexp(drainer) /* e.g., hit by drain life attack */ +const char *drainer; /* cause of death, if drain should be fatal */ +{ + register int num; + +#ifdef WIZARD + /* override life-drain resistance when handling an explicit + wizard mode request to reduce level; never fatal though */ + if (drainer && !strcmp(drainer, "#levelchange")) + drainer = 0; + else +#endif + if (resists_drli(&youmonst)) return; + + if (u.ulevel > 1) { + pline("%s level %d.", Goodbye(), u.ulevel--); + /* remove intrinsic abilities */ + adjabil(u.ulevel + 1, u.ulevel); + reset_rndmonst(NON_PM); /* new monster selection */ + } else { + if (drainer) { + killer_format = KILLED_BY; + killer = drainer; + done(DIED); + } + /* no drainer or lifesaved */ + u.uexp = 0; + } + num = newhp(); + u.uhpmax -= num; + if (u.uhpmax < 1) u.uhpmax = 1; + u.uhp -= num; + if (u.uhp < 1) u.uhp = 1; + else if (u.uhp > u.uhpmax) u.uhp = u.uhpmax; + + if (u.ulevel < urole.xlev) + num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.lornd + urace.enadv.lornd, + urole.enadv.lofix + urace.enadv.lofix); + else + num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.hirnd + urace.enadv.hirnd, + urole.enadv.hifix + urace.enadv.hifix); + num = enermod(num); /* M. Stephenson */ + u.uenmax -= num; + if (u.uenmax < 0) u.uenmax = 0; + u.uen -= num; + if (u.uen < 0) u.uen = 0; + else if (u.uen > u.uenmax) u.uen = u.uenmax; + + if (u.uexp > 0) + u.uexp = newuexp(u.ulevel) - 1; + flags.botl = 1; +} + +/* + * Make experience gaining similar to AD&D(tm), whereby you can at most go + * up by one level at a time, extra expr possibly helping you along. + * After all, how much real experience does one get shooting a wand of death + * at a dragon created with a wand of polymorph?? + */ +void +newexplevel() +{ + if (u.ulevel < MAXULEV && u.uexp >= newuexp(u.ulevel)) + pluslvl(TRUE); +} + +void +pluslvl(incr) +boolean incr; /* true iff via incremental experience growth */ +{ /* (false for potion of gain level) */ + register int num; + + if (!incr) You_feel("more experienced."); + num = newhp(); + u.uhpmax += num; + u.uhp += num; + if (Upolyd) { + num = rnd(8); + u.mhmax += num; + u.mh += num; + } + if (u.ulevel < urole.xlev) + num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.lornd + urace.enadv.lornd, + urole.enadv.lofix + urace.enadv.lofix); + else + num = rn1((int)ACURR(A_WIS)/2 + urole.enadv.hirnd + urace.enadv.hirnd, + urole.enadv.hifix + urace.enadv.hifix); + num = enermod(num); /* M. Stephenson */ + u.uenmax += num; + u.uen += num; + if (u.ulevel < MAXULEV) { + if (incr) { + long tmp = newuexp(u.ulevel + 1); + if (u.uexp >= tmp) u.uexp = tmp - 1; + } else { + u.uexp = newuexp(u.ulevel); + } + ++u.ulevel; + if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel; + pline("Welcome to experience level %d.", u.ulevel); + adjabil(u.ulevel - 1, u.ulevel); /* give new intrinsics */ + reset_rndmonst(NON_PM); /* new monster selection */ + } + flags.botl = 1; +} + +/* compute a random amount of experience points suitable for the hero's + experience level: base number of points needed to reach the current + level plus a random portion of what it takes to get to the next level */ +long +rndexp(gaining) +boolean gaining; /* gaining XP via potion vs setting XP for polyself */ +{ + long minexp, maxexp, diff, factor, result; + + minexp = (u.ulevel == 1) ? 0L : newuexp(u.ulevel - 1); + maxexp = newuexp(u.ulevel); + diff = maxexp - minexp, factor = 1L; + /* make sure that `diff' is an argument which rn2() can handle */ + while (diff >= (long)LARGEST_INT) + diff /= 2L, factor *= 2L; + result = minexp + factor * (long)rn2((int)diff); + /* 3.4.1: if already at level 30, add to current experience + points rather than to threshold needed to reach the current + level; otherwise blessed potions of gain level can result + in lowering the experience points instead of raising them */ + if (u.ulevel == MAXULEV && gaining) { + result += (u.uexp - minexp); + /* avoid wrapping (over 400 blessed potions needed for that...) */ + if (result < u.uexp) result = u.uexp; + } + return result; +} + +/*exper.c*/ diff --git a/src/explode.c b/src/explode.c new file mode 100644 index 0000000..8671a2c --- /dev/null +++ b/src/explode.c @@ -0,0 +1,580 @@ +/* SCCS Id: @(#)explode.c 3.4 2002/11/10 */ +/* Copyright (C) 1990 by Ken Arromdee */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVL0 + +/* Note: Arrays are column first, while the screen is row first */ +static int expl[3][3] = { + { S_explode1, S_explode4, S_explode7 }, + { S_explode2, S_explode5, S_explode8 }, + { S_explode3, S_explode6, S_explode9 } +}; + +/* Note: I had to choose one of three possible kinds of "type" when writing + * this function: a wand type (like in zap.c), an adtyp, or an object type. + * Wand types get complex because they must be converted to adtyps for + * determining such things as fire resistance. Adtyps get complex in that + * they don't supply enough information--was it a player or a monster that + * did it, and with a wand, spell, or breath weapon? Object types share both + * these disadvantages.... + */ +void +explode(x, y, type, dam, olet, expltype) +int x, y; +int type; /* the same as in zap.c */ +int dam; +char olet; +int expltype; +{ + int i, j, k, damu = dam; + boolean starting = 1; + boolean visible, any_shield; + int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */ + const char *str; + int idamres, idamnonres; + struct monst *mtmp; + uchar adtyp; + int explmask[3][3]; + /* 0=normal explosion, 1=do shieldeff, 2=do nothing */ + boolean shopdamage = FALSE; + boolean generic = FALSE; + + if (olet == WAND_CLASS) /* retributive strike */ + switch (Role_switch) { + case PM_PRIEST: + case PM_MONK: + case PM_WIZARD: damu /= 5; + break; + case PM_HEALER: + case PM_KNIGHT: damu /= 2; + break; + default: break; + } + + if (olet == MON_EXPLODE) { + str = killer; + killer = 0; /* set again later as needed */ + adtyp = AD_PHYS; + } else + switch (abs(type) % 10) { + case 0: str = "magical blast"; + adtyp = AD_MAGM; + break; + case 1: str = olet == BURNING_OIL ? "burning oil" : + olet == SCROLL_CLASS ? "tower of flame" : + "fireball"; + adtyp = AD_FIRE; + break; + case 2: str = "ball of cold"; + adtyp = AD_COLD; + break; + case 4: str = (olet == WAND_CLASS) ? "death field" : + "disintegration field"; + adtyp = AD_DISN; + break; + case 5: str = "ball of lightning"; + adtyp = AD_ELEC; + break; + case 6: str = "poison gas cloud"; + adtyp = AD_DRST; + break; + case 7: str = "splash of acid"; + adtyp = AD_ACID; + break; + default: impossible("explosion base type %d?", type); return; + } + + any_shield = visible = FALSE; + for (i=0; i<3; i++) for (j=0; j<3; j++) { + if (!isok(i+x-1, j+y-1)) { + explmask[i][j] = 2; + continue; + } else + explmask[i][j] = 0; + + if (i+x-1 == u.ux && j+y-1 == u.uy) { + switch(adtyp) { + case AD_PHYS: + explmask[i][j] = 0; + break; + case AD_MAGM: + explmask[i][j] = !!Antimagic; + break; + case AD_FIRE: + explmask[i][j] = !!Fire_resistance; + break; + case AD_COLD: + explmask[i][j] = !!Cold_resistance; + break; + case AD_DISN: + explmask[i][j] = (olet == WAND_CLASS) ? + !!(nonliving(youmonst.data) || is_demon(youmonst.data)) : + !!Disint_resistance; + break; + case AD_ELEC: + explmask[i][j] = !!Shock_resistance; + break; + case AD_DRST: + explmask[i][j] = !!Poison_resistance; + break; + case AD_ACID: + explmask[i][j] = !!Acid_resistance; + break; + default: + impossible("explosion type %d?", adtyp); + break; + } + } + /* can be both you and mtmp if you're swallowed */ + mtmp = m_at(i+x-1, j+y-1); +#ifdef STEED + if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy) + mtmp = u.usteed; +#endif + if (mtmp) { + if (mtmp->mhp < 1) explmask[i][j] = 2; + else switch(adtyp) { + case AD_PHYS: + break; + case AD_MAGM: + explmask[i][j] |= resists_magm(mtmp); + break; + case AD_FIRE: + explmask[i][j] |= resists_fire(mtmp); + break; + case AD_COLD: + explmask[i][j] |= resists_cold(mtmp); + break; + case AD_DISN: + explmask[i][j] |= (olet == WAND_CLASS) ? + (nonliving(mtmp->data) || is_demon(mtmp->data)) : + resists_disint(mtmp); + break; + case AD_ELEC: + explmask[i][j] |= resists_elec(mtmp); + break; + case AD_DRST: + explmask[i][j] |= resists_poison(mtmp); + break; + case AD_ACID: + explmask[i][j] |= resists_acid(mtmp); + break; + default: + impossible("explosion type %d?", adtyp); + break; + } + } + if (mtmp && cansee(i+x-1,j+y-1) && !canspotmon(mtmp)) + map_invisible(i+x-1, j+y-1); + else if (!mtmp && glyph_is_invisible(levl[i+x-1][j+y-1].glyph)) { + unmap_object(i+x-1, j+y-1); + newsym(i+x-1, j+y-1); + } + if (cansee(i+x-1, j+y-1)) visible = TRUE; + if (explmask[i][j] == 1) any_shield = TRUE; + } + + if (visible) { + /* Start the explosion */ + for (i=0; i<3; i++) for (j=0; j<3; j++) { + if (explmask[i][j] == 2) continue; + tmp_at(starting ? DISP_BEAM : DISP_CHANGE, + explosion_to_glyph(expltype,expl[i][j])); + tmp_at(i+x-1, j+y-1); + starting = 0; + } + curs_on_u(); /* will flush screen and output */ + + if (any_shield && flags.sparkle) { /* simulate shield effect */ + for (k = 0; k < SHIELD_COUNT; k++) { + for (i=0; i<3; i++) for (j=0; j<3; j++) { + if (explmask[i][j] == 1) + /* + * Bypass tmp_at() and send the shield glyphs + * directly to the buffered screen. tmp_at() + * will clean up the location for us later. + */ + show_glyph(i+x-1, j+y-1, + cmap_to_glyph(shield_static[k])); + } + curs_on_u(); /* will flush screen and output */ + delay_output(); + } + + /* Cover last shield glyph with blast symbol. */ + for (i=0; i<3; i++) for (j=0; j<3; j++) { + if (explmask[i][j] == 1) + show_glyph(i+x-1,j+y-1, + explosion_to_glyph(expltype, expl[i][j])); + } + + } else { /* delay a little bit. */ + delay_output(); + delay_output(); + } + + tmp_at(DISP_END, 0); /* clear the explosion */ + } else { + if (olet == MON_EXPLODE) { + str = "explosion"; + generic = TRUE; + } + if (flags.soundok) You_hear("a blast."); + } + + if (dam) + for (i=0; i<3; i++) for (j=0; j<3; j++) { + if (explmask[i][j] == 2) continue; + if (i+x-1 == u.ux && j+y-1 == u.uy) + uhurt = (explmask[i][j] == 1) ? 1 : 2; + idamres = idamnonres = 0; + if (type >= 0) + (void)zap_over_floor((xchar)(i+x-1), (xchar)(j+y-1), + type, &shopdamage); + + mtmp = m_at(i+x-1, j+y-1); +#ifdef STEED + if (!mtmp && i+x-1 == u.ux && j+y-1 == u.uy) + mtmp = u.usteed; +#endif + if (!mtmp) continue; + if (u.uswallow && mtmp == u.ustuck) { + if (is_animal(u.ustuck->data)) + pline("%s gets %s!", + Monnam(u.ustuck), + (adtyp == AD_FIRE) ? "heartburn" : + (adtyp == AD_COLD) ? "chilly" : + (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? + "irradiated by pure energy" : "perforated") : + (adtyp == AD_ELEC) ? "shocked" : + (adtyp == AD_DRST) ? "poisoned" : + (adtyp == AD_ACID) ? "an upset stomach" : + "fried"); + else + pline("%s gets slightly %s!", + Monnam(u.ustuck), + (adtyp == AD_FIRE) ? "toasted" : + (adtyp == AD_COLD) ? "chilly" : + (adtyp == AD_DISN) ? ((olet == WAND_CLASS) ? + "overwhelmed by pure energy" : "perforated") : + (adtyp == AD_ELEC) ? "shocked" : + (adtyp == AD_DRST) ? "intoxicated" : + (adtyp == AD_ACID) ? "burned" : + "fried"); + } else if (cansee(i+x-1, j+y-1)) { + if(mtmp->m_ap_type) seemimic(mtmp); + pline("%s is caught in the %s!", Monnam(mtmp), str); + } + + idamres += destroy_mitem(mtmp, SCROLL_CLASS, (int) adtyp); + idamres += destroy_mitem(mtmp, SPBOOK_CLASS, (int) adtyp); + idamnonres += destroy_mitem(mtmp, POTION_CLASS, (int) adtyp); + idamnonres += destroy_mitem(mtmp, WAND_CLASS, (int) adtyp); + idamnonres += destroy_mitem(mtmp, RING_CLASS, (int) adtyp); + + if (explmask[i][j] == 1) { + golemeffects(mtmp, (int) adtyp, dam + idamres); + mtmp->mhp -= idamnonres; + } else { + /* call resist with 0 and do damage manually so 1) we can + * get out the message before doing the damage, and 2) we can + * call mondied, not killed, if it's not your blast + */ + int mdam = dam; + + if (resist(mtmp, olet, 0, FALSE)) { + if (cansee(i+x-1,j+y-1)) + pline("%s resists the %s!", Monnam(mtmp), str); + mdam = dam/2; + } + if (mtmp == u.ustuck) + mdam *= 2; + if (resists_cold(mtmp) && adtyp == AD_FIRE) + mdam *= 2; + else if (resists_fire(mtmp) && adtyp == AD_COLD) + mdam *= 2; + mtmp->mhp -= mdam; + mtmp->mhp -= (idamres + idamnonres); + } + if (mtmp->mhp <= 0) { + /* KMH -- Don't blame the player for pets killing gas spores */ + if (!flags.mon_moving) killed(mtmp); + else monkilled(mtmp, "", (int)adtyp); + } else if (!flags.mon_moving) setmangry(mtmp); + } + + /* Do your injury last */ + if (uhurt) { + if ((type >= 0 || adtyp == AD_PHYS) && /* gas spores */ + flags.verbose && olet != SCROLL_CLASS) + You("are caught in the %s!", str); + /* do property damage first, in case we end up leaving bones */ + if (adtyp == AD_FIRE) burn_away_slime(); + if (Invulnerable) { + damu = 0; + You("are unharmed!"); + } else if (Half_physical_damage && adtyp == AD_PHYS) + damu = (damu+1) / 2; + if (adtyp == AD_FIRE) (void) burnarmor(&youmonst); + destroy_item(SCROLL_CLASS, (int) adtyp); + destroy_item(SPBOOK_CLASS, (int) adtyp); + destroy_item(POTION_CLASS, (int) adtyp); + destroy_item(RING_CLASS, (int) adtyp); + destroy_item(WAND_CLASS, (int) adtyp); + + ugolemeffects((int) adtyp, damu); + if (uhurt == 2) { + if (Upolyd) + u.mh -= damu; + else + u.uhp -= damu; + flags.botl = 1; + } + + if (u.uhp <= 0 || (Upolyd && u.mh <= 0)) { + if (Upolyd) { + rehumanize(); + } else { + if (olet == MON_EXPLODE) { + /* killer handled by caller */ + if (str != killer_buf && !generic) + Strcpy(killer_buf, str); + killer_format = KILLED_BY_AN; + } else if (type >= 0 && olet != SCROLL_CLASS) { + killer_format = NO_KILLER_PREFIX; + Sprintf(killer_buf, "caught %sself in %s own %s", + uhim(), uhis(), str); + } else if (!strncmpi(str,"tower of flame", 8) || + !strncmpi(str,"fireball", 8)) { + killer_format = KILLED_BY_AN; + Strcpy(killer_buf, str); + } else { + killer_format = KILLED_BY; + Strcpy(killer_buf, str); + } + killer = killer_buf; + /* Known BUG: BURNING suppresses corpse in bones data, + but done does not handle killer reason correctly */ + done((adtyp == AD_FIRE) ? BURNING : DIED); + } + } + exercise(A_STR, FALSE); + } + + if (shopdamage) { + pay_for_damage(adtyp == AD_FIRE ? "burn away" : + adtyp == AD_COLD ? "shatter" : + adtyp == AD_DISN ? "disintegrate" : "destroy", + FALSE); + } + + /* explosions are noisy */ + i = dam * dam; + if (i < 50) i = 50; /* in case random damage is very small */ + wake_nearto(x, y, i); +} +#endif /* OVL0 */ +#ifdef OVL1 + +struct scatter_chain { + struct scatter_chain *next; /* pointer to next scatter item */ + struct obj *obj; /* pointer to the object */ + xchar ox; /* location of */ + xchar oy; /* item */ + schar dx; /* direction of */ + schar dy; /* travel */ + int range; /* range of object */ + boolean stopped; /* flag for in-motion/stopped */ +}; + +/* + * scflags: + * VIS_EFFECTS Add visual effects to display + * MAY_HITMON Objects may hit monsters + * MAY_HITYOU Objects may hit hero + * MAY_HIT Objects may hit you or monsters + * MAY_DESTROY Objects may be destroyed at random + * MAY_FRACTURE Stone objects can be fractured (statues, boulders) + */ + +/* returns number of scattered objects */ +long +scatter(sx,sy,blastforce,scflags, obj) +int sx,sy; /* location of objects to scatter */ +int blastforce; /* force behind the scattering */ +unsigned int scflags; +struct obj *obj; /* only scatter this obj */ +{ + register struct obj *otmp; + register int tmp; + int farthest = 0; + uchar typ; + long qtmp; + boolean used_up; + boolean individual_object = obj ? TRUE : FALSE; + struct monst *mtmp; + struct scatter_chain *stmp, *stmp2 = 0; + struct scatter_chain *schain = (struct scatter_chain *)0; + long total = 0L; + + while ((otmp = individual_object ? obj : level.objects[sx][sy]) != 0) { + if (otmp->quan > 1L) { + qtmp = otmp->quan - 1; + if (qtmp > LARGEST_INT) qtmp = LARGEST_INT; + qtmp = (long)rnd((int)qtmp); + otmp = splitobj(otmp, qtmp); + } else { + obj = (struct obj *)0; /* all used */ + } + obj_extract_self(otmp); + used_up = FALSE; + + /* 9 in 10 chance of fracturing boulders or statues */ + if ((scflags & MAY_FRACTURE) + && ((otmp->otyp == BOULDER) || (otmp->otyp == STATUE)) + && rn2(10)) { + if (otmp->otyp == BOULDER) { + pline("%s apart.", Tobjnam(otmp, "break")); + fracture_rock(otmp); + place_object(otmp, sx, sy); + if ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { + /* another boulder here, restack it to the top */ + obj_extract_self(otmp); + place_object(otmp, sx, sy); + } + } else { + struct trap *trap; + + if ((trap = t_at(sx,sy)) && trap->ttyp == STATUE_TRAP) + deltrap(trap); + pline("%s.", Tobjnam(otmp, "crumble")); + (void) break_statue(otmp); + place_object(otmp, sx, sy); /* put fragments on floor */ + } + used_up = TRUE; + + /* 1 in 10 chance of destruction of obj; glass, egg destruction */ + } else if ((scflags & MAY_DESTROY) && (!rn2(10) + || (objects[otmp->otyp].oc_material == GLASS + || otmp->otyp == EGG))) { + if (breaks(otmp, (xchar)sx, (xchar)sy)) used_up = TRUE; + } + + if (!used_up) { + stmp = (struct scatter_chain *) + alloc(sizeof(struct scatter_chain)); + stmp->next = (struct scatter_chain *)0; + stmp->obj = otmp; + stmp->ox = sx; + stmp->oy = sy; + tmp = rn2(8); /* get the direction */ + stmp->dx = xdir[tmp]; + stmp->dy = ydir[tmp]; + tmp = blastforce - (otmp->owt/40); + if (tmp < 1) tmp = 1; + stmp->range = rnd(tmp); /* anywhere up to that determ. by wt */ + if (farthest < stmp->range) farthest = stmp->range; + stmp->stopped = FALSE; + if (!schain) + schain = stmp; + else + stmp2->next = stmp; + stmp2 = stmp; + } + } + + while (farthest-- > 0) { + for (stmp = schain; stmp; stmp = stmp->next) { + if ((stmp->range-- > 0) && (!stmp->stopped)) { + bhitpos.x = stmp->ox + stmp->dx; + bhitpos.y = stmp->oy + stmp->dy; + typ = levl[bhitpos.x][bhitpos.y].typ; + if(!isok(bhitpos.x, bhitpos.y)) { + bhitpos.x -= stmp->dx; + bhitpos.y -= stmp->dy; + stmp->stopped = TRUE; + } else if(!ZAP_POS(typ) || + closed_door(bhitpos.x, bhitpos.y)) { + bhitpos.x -= stmp->dx; + bhitpos.y -= stmp->dy; + stmp->stopped = TRUE; + } else if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { + if (scflags & MAY_HITMON) { + stmp->range--; + if (ohitmon(mtmp, stmp->obj, 1, FALSE)) { + stmp->obj = (struct obj *)0; + stmp->stopped = TRUE; + } + } + } else if (bhitpos.x==u.ux && bhitpos.y==u.uy) { + if (scflags & MAY_HITYOU) { + int hitvalu, hitu; + + if (multi) nomul(0); + hitvalu = 8 + stmp->obj->spe; + if (bigmonst(youmonst.data)) hitvalu++; + hitu = thitu(hitvalu, + dmgval(stmp->obj, &youmonst), + stmp->obj, (char *)0); + if (hitu) { + stmp->range -= 3; + stop_occupation(); + } + } + } else { + if (scflags & VIS_EFFECTS) { + /* tmp_at(bhitpos.x, bhitpos.y); */ + /* delay_output(); */ + } + } + stmp->ox = bhitpos.x; + stmp->oy = bhitpos.y; + } + } + } + for (stmp = schain; stmp; stmp = stmp2) { + int x,y; + + stmp2 = stmp->next; + x = stmp->ox; y = stmp->oy; + if (stmp->obj) { + if ( x!=sx || y!=sy ) + total += stmp->obj->quan; + place_object(stmp->obj, x, y); + stackobj(stmp->obj); + } + free((genericptr_t)stmp); + newsym(x,y); + } + + return total; +} + + +/* + * Splatter burning oil from x,y to the surrounding area. + * + * This routine should really take a how and direction parameters. + * The how is how it was caused, e.g. kicked verses thrown. The + * direction is which way to spread the flaming oil. Different + * "how"s would give different dispersal patterns. For example, + * kicking a burning flask will splatter differently from a thrown + * flask hitting the ground. + * + * For now, just perform a "regular" explosion. + */ +void +splatter_burning_oil(x, y) + int x, y; +{ +/* ZT_SPELL(ZT_FIRE) = ZT_SPELL(AD_FIRE-1) = 10+(2-1) = 11 */ +#define ZT_SPELL_O_FIRE 11 /* value kludge, see zap.c */ + explode(x, y, ZT_SPELL_O_FIRE, d(4,4), BURNING_OIL, EXPL_FIERY); +} + +#endif /* OVL1 */ + +/*explode.c*/ diff --git a/src/extralev.c b/src/extralev.c new file mode 100644 index 0000000..8312402 --- /dev/null +++ b/src/extralev.c @@ -0,0 +1,346 @@ +/* SCCS Id: @(#)extralev.c 3.4 2001/09/06 */ +/* Copyright 1988, 1989 by Ken Arromdee */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Support code for "rogue"-style level. + */ + +#include "hack.h" + +#ifdef REINCARNATION + +struct rogueroom { + xchar rlx, rly; + xchar dx, dy; + boolean real; + uchar doortable; + int nroom; /* Only meaningful for "real" rooms */ +}; +#define UP 1 +#define DOWN 2 +#define LEFT 4 +#define RIGHT 8 + +static NEARDATA struct rogueroom r[3][3]; +STATIC_DCL void FDECL(roguejoin,(int,int,int,int,int)); +STATIC_DCL void FDECL(roguecorr,(int,int,int)); +STATIC_DCL void FDECL(miniwalk,(int,int)); + +STATIC_OVL +void +roguejoin(x1,y1,x2,y2, horiz) +int x1,y1,x2,y2; +int horiz; +{ + register int x,y,middle; +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + if (horiz) { + middle = x1 + rn2(x2-x1+1); + for(x=MIN(x1,middle); x<=MAX(x1,middle); x++) + corr(x, y1); + for(y=MIN(y1,y2); y<=MAX(y1,y2); y++) + corr(middle,y); + for(x=MIN(middle,x2); x<=MAX(middle,x2); x++) + corr(x, y2); + } else { + middle = y1 + rn2(y2-y1+1); + for(y=MIN(y1,middle); y<=MAX(y1,middle); y++) + corr(x1, y); + for(x=MIN(x1,x2); x<=MAX(x1,x2); x++) + corr(x, middle); + for(y=MIN(middle,y2); y<=MAX(middle,y2); y++) + corr(x2,y); + } +} + +STATIC_OVL +void +roguecorr(x, y, dir) +int x,y,dir; +{ + register int fromx, fromy, tox, toy; + + if (dir==DOWN) { + r[x][y].doortable &= ~DOWN; + if (!r[x][y].real) { + fromx = r[x][y].rlx; fromy = r[x][y].rly; + fromx += 1 + 26*x; fromy += 7*y; + } else { + fromx = r[x][y].rlx + rn2(r[x][y].dx); + fromy = r[x][y].rly + r[x][y].dy; + fromx += 1 + 26*x; fromy += 7*y; + if (!IS_WALL(levl[fromx][fromy].typ)) + impossible("down: no wall at %d,%d?",fromx, + fromy); + dodoor(fromx, fromy, &rooms[r[x][y].nroom]); + levl[fromx][fromy].doormask = D_NODOOR; + fromy++; + } + if(y >= 2) { + impossible("down door from %d,%d going nowhere?",x,y); + return; + } + y++; + r[x][y].doortable &= ~UP; + if (!r[x][y].real) { + tox = r[x][y].rlx; toy = r[x][y].rly; + tox += 1 + 26*x; toy += 7*y; + } else { + tox = r[x][y].rlx + rn2(r[x][y].dx); + toy = r[x][y].rly - 1; + tox += 1 + 26*x; toy += 7*y; + if (!IS_WALL(levl[tox][toy].typ)) + impossible("up: no wall at %d,%d?",tox,toy); + dodoor(tox, toy, &rooms[r[x][y].nroom]); + levl[tox][toy].doormask = D_NODOOR; + toy--; + } + roguejoin(fromx, fromy, tox, toy, FALSE); + return; + } else if (dir == RIGHT) { + r[x][y].doortable &= ~RIGHT; + if (!r[x][y].real) { + fromx = r[x][y].rlx; fromy = r[x][y].rly; + fromx += 1 + 26*x; fromy += 7*y; + } else { + fromx = r[x][y].rlx + r[x][y].dx; + fromy = r[x][y].rly + rn2(r[x][y].dy); + fromx += 1 + 26*x; fromy += 7*y; + if (!IS_WALL(levl[fromx][fromy].typ)) + impossible("down: no wall at %d,%d?",fromx, + fromy); + dodoor(fromx, fromy, &rooms[r[x][y].nroom]); + levl[fromx][fromy].doormask = D_NODOOR; + fromx++; + } + if(x >= 2) { + impossible("right door from %d,%d going nowhere?",x,y); + return; + } + x++; + r[x][y].doortable &= ~LEFT; + if (!r[x][y].real) { + tox = r[x][y].rlx; toy = r[x][y].rly; + tox += 1 + 26*x; toy += 7*y; + } else { + tox = r[x][y].rlx - 1; + toy = r[x][y].rly + rn2(r[x][y].dy); + tox += 1 + 26*x; toy += 7*y; + if (!IS_WALL(levl[tox][toy].typ)) + impossible("left: no wall at %d,%d?",tox,toy); + dodoor(tox, toy, &rooms[r[x][y].nroom]); + levl[tox][toy].doormask = D_NODOOR; + tox--; + } + roguejoin(fromx, fromy, tox, toy, TRUE); + return; + } else impossible("corridor in direction %d?",dir); +} + +/* Modified walkfrom() from mkmaze.c */ +STATIC_OVL +void +miniwalk(x, y) +int x,y; +{ + register int q, dir; + int dirs[4]; + + while(1) { + q = 0; +#define doorhere (r[x][y].doortable) + if (x>0 && (!(doorhere & LEFT)) && + (!r[x-1][y].doortable || !rn2(10))) + dirs[q++] = 0; + if (x<2 && (!(doorhere & RIGHT)) && + (!r[x+1][y].doortable || !rn2(10))) + dirs[q++] = 1; + if (y>0 && (!(doorhere & UP)) && + (!r[x][y-1].doortable || !rn2(10))) + dirs[q++] = 2; + if (y<2 && (!(doorhere & DOWN)) && + (!r[x][y+1].doortable || !rn2(10))) + dirs[q++] = 3; + /* Rogue levels aren't just 3 by 3 mazes; they have some extra + * connections, thus that 1/10 chance + */ + if (!q) return; + dir = dirs[rn2(q)]; + switch(dir) { /* Move in direction */ + case 0: doorhere |= LEFT; + x--; + doorhere |= RIGHT; + break; + case 1: doorhere |= RIGHT; + x++; + doorhere |= LEFT; + break; + case 2: doorhere |= UP; + y--; + doorhere |= DOWN; + break; + case 3: doorhere |= DOWN; + y++; + doorhere |= UP; + break; + } + miniwalk(x,y); + } +} + +void +makeroguerooms() { + register int x,y; + /* Rogue levels are structured 3 by 3, with each section containing + * a room or an intersection. The minimum width is 2 each way. + * One difference between these and "real" Rogue levels: real Rogue + * uses 24 rows and NetHack only 23. So we cheat a bit by making the + * second row of rooms not as deep. + * + * Each normal space has 6/7 rows and 25 columns in which a room may + * actually be placed. Walls go from rows 0-5/6 and columns 0-24. + * Not counting walls, the room may go in + * rows 1-5 and columns 1-23 (numbering starting at 0). A room + * coordinate of this type may be converted to a level coordinate + * by adding 1+28*x to the column, and 7*y to the row. (The 1 + * is because column 0 isn't used [we only use 1-78]). + * Room height may be 2-4 (2-5 on last row), length 2-23 (not + * counting walls) + */ +#define here r[x][y] + + nroom = 0; + for(y=0; y<3; y++) for(x=0; x<3; x++) { + /* Note: we want to insure at least 1 room. So, if the + * first 8 are all dummies, force the last to be a room. + */ + if (!rn2(5) && (nroom || (x<2 && y<2))) { + /* Arbitrary: dummy rooms may only go where real + * ones do. + */ + here.real = FALSE; + here.rlx = rn1(22, 2); + here.rly = rn1((y==2)?4:3, 2); + } else { + here.real = TRUE; + here.dx = rn1(22, 2); /* 2-23 long, plus walls */ + here.dy = rn1((y==2)?4:3, 2); /* 2-5 high, plus walls */ + + /* boundaries of room floor */ + here.rlx = rnd(23 - here.dx + 1); + here.rly = rnd(((y==2) ? 5 : 4)- here.dy + 1); + nroom++; + } + here.doortable = 0; + } + miniwalk(rn2(3), rn2(3)); + nroom = 0; + for(y=0; y<3; y++) for(x=0; x<3; x++) { + if (here.real) { /* Make a room */ + int lowx, lowy, hix, hiy; + + r[x][y].nroom = nroom; + smeq[nroom] = nroom; + + lowx = 1 + 26*x + here.rlx; + lowy = 7*y + here.rly; + hix = 1 + 26*x + here.rlx + here.dx - 1; + hiy = 7*y + here.rly + here.dy - 1; + /* Strictly speaking, it should be lit only if above + * level 10, but since Rogue rooms are only + * encountered below level 10, use !rn2(7). + */ + add_room(lowx, lowy, hix, hiy, + (boolean) !rn2(7), OROOM, FALSE); + } + } + + /* Now, add connecting corridors. */ + for(y=0; y<3; y++) for(x=0; x<3; x++) { + if (here.doortable & DOWN) + roguecorr(x, y, DOWN); + if (here.doortable & RIGHT) + roguecorr(x, y, RIGHT); + if (here.doortable & LEFT) + impossible ("left end of %d, %d never connected?",x,y); + if (here.doortable & UP) + impossible ("up end of %d, %d never connected?",x,y); + } +} + +void +corr(x,y) +int x, y; +{ + if (rn2(50)) { + levl[x][y].typ = CORR; + } else { + levl[x][y].typ = SCORR; + } +} + +void +makerogueghost() +{ + register struct monst *ghost; + struct obj *ghostobj; + struct mkroom *croom; + int x,y; + + if (!nroom) return; /* Should never happen */ + croom = &rooms[rn2(nroom)]; + x = somex(croom); y = somey(croom); + if (!(ghost = makemon(&mons[PM_GHOST], x, y, NO_MM_FLAGS))) + return; + ghost->msleeping = 1; + ghost = christen_monst(ghost, roguename()); + + if (rn2(4)) { + ghostobj = mksobj_at(FOOD_RATION, x, y, FALSE, FALSE); + ghostobj->quan = (long) rnd(7); + ghostobj->owt = weight(ghostobj); + } + if (rn2(2)) { + ghostobj = mksobj_at(MACE, x, y, FALSE, FALSE); + ghostobj->spe = rnd(3); + if (rn2(4)) curse(ghostobj); + } else { + ghostobj = mksobj_at(TWO_HANDED_SWORD, x, y, FALSE, FALSE); + ghostobj->spe = rnd(5) - 2; + if (rn2(4)) curse(ghostobj); + } + ghostobj = mksobj_at(BOW, x, y, FALSE, FALSE); + ghostobj->spe = 1; + if (rn2(4)) curse(ghostobj); + + ghostobj = mksobj_at(ARROW, x, y, FALSE, FALSE); + ghostobj->spe = 0; + ghostobj->quan = (long) rn1(10,25); + ghostobj->owt = weight(ghostobj); + if (rn2(4)) curse(ghostobj); + + if (rn2(2)) { + ghostobj = mksobj_at(RING_MAIL, x, y, FALSE, FALSE); + ghostobj->spe = rn2(3); + if (!rn2(3)) ghostobj->oerodeproof = TRUE; + if (rn2(4)) curse(ghostobj); + } else { + ghostobj = mksobj_at(PLATE_MAIL, x, y, FALSE, FALSE); + ghostobj->spe = rnd(5) - 2; + if (!rn2(3)) ghostobj->oerodeproof = TRUE; + if (rn2(4)) curse(ghostobj); + } + if (rn2(2)) { + ghostobj = mksobj_at(FAKE_AMULET_OF_YENDOR, x, y, TRUE, FALSE); + ghostobj->known = TRUE; + } +} +#endif /* REINCARNATION */ + +/*extralev.c*/ diff --git a/src/files.c b/src/files.c new file mode 100644 index 0000000..023546b --- /dev/null +++ b/src/files.c @@ -0,0 +1,2424 @@ +/* SCCS Id: @(#)files.c 3.4 2003/11/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" + +#ifdef TTY_GRAPHICS +#include "wintty.h" /* more() */ +#endif + +#include + +#if !defined(MAC) && !defined(O_WRONLY) && !defined(AZTEC_C) +#include +#endif + +#include +#ifdef _MSC_VER /* MSC 6.0 defines errno quite differently */ +# if (_MSC_VER >= 600) +# define SKIP_ERRNO +# endif +#else +# ifdef NHSTDC +# define SKIP_ERRNO +# endif +#endif +#ifndef SKIP_ERRNO +# ifdef _DCC +const +# endif +extern int errno; +#endif + +#if defined(UNIX) && defined(QT_GRAPHICS) +#include +#endif + +#if defined(UNIX) || defined(VMS) +#include +#endif + +#if defined(MSDOS) || defined(OS2) || defined(TOS) || defined(WIN32) +# ifndef GNUDOS +#include +# else +#include +# endif +#endif +#ifndef O_BINARY /* used for micros, no-op for others */ +# define O_BINARY 0 +#endif + +#ifdef PREFIXES_IN_USE +#define FQN_NUMBUF 4 +static char fqn_filename_buffer[FQN_NUMBUF][FQN_MAX_FILENAME]; +#endif + +#if !defined(MFLOPPY) && !defined(VMS) && !defined(WIN32) +char bones[] = "bonesnn.xxx"; +char lock[PL_NSIZ+14] = "1lock"; /* long enough for uid+name+.99 */ +#else +# if defined(MFLOPPY) +char bones[FILENAME]; /* pathname of bones files */ +char lock[FILENAME]; /* pathname of level files */ +# endif +# if defined(VMS) +char bones[] = "bonesnn.xxx;1"; +char lock[PL_NSIZ+17] = "1lock"; /* long enough for _uid+name+.99;1 */ +# endif +# if defined(WIN32) +char bones[] = "bonesnn.xxx"; +char lock[PL_NSIZ+25]; /* long enough for username+-+name+.99 */ +# endif +#endif + +#if defined(UNIX) || defined(__BEOS__) +#define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */ +#else +# ifdef VMS +#define SAVESIZE (PL_NSIZ + 22) /* [.save]player.e;1 */ +# else +# if defined(WIN32) +#define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */ +# else +#define SAVESIZE FILENAME /* from macconf.h or pcconf.h */ +# endif +# endif +#endif + +char SAVEF[SAVESIZE]; /* holds relative path of save file from playground */ +#ifdef MICRO +char SAVEP[SAVESIZE]; /* holds path of directory for save file */ +#endif + +#ifdef HOLD_LOCKFILE_OPEN +struct level_ftrack { +int init; +int fd; /* file descriptor for level file */ +int oflag; /* open flags */ +boolean nethack_thinks_it_is_open; /* Does NetHack think it's open? */ +} lftrack; +# if defined(WIN32) +#include +# endif +#endif /*HOLD_LOCKFILE_OPEN*/ + +#ifdef WIZARD +#define WIZKIT_MAX 128 +static char wizkit[WIZKIT_MAX]; +STATIC_DCL FILE *NDECL(fopen_wizkit_file); +#endif + +#ifdef AMIGA +extern char PATH[]; /* see sys/amiga/amidos.c */ +extern char bbs_id[]; +static int lockptr; +# ifdef __SASC_60 +#include +# endif + +#include +extern void FDECL(amii_set_text_font, ( char *, int )); +#endif + +#if defined(WIN32) || defined(MSDOS) +static int lockptr; +# ifdef MSDOS +#define Delay(a) msleep(a) +# endif +#define Close close +#ifndef WIN_CE +#define DeleteFile unlink +#endif +#endif + +#ifdef MAC +# define unlink macunlink +#endif + +#ifdef USER_SOUNDS +extern char *sounddir; +#endif + +extern int n_dgns; /* from dungeon.c */ + +STATIC_DCL char *FDECL(set_bonesfile_name, (char *,d_level*)); +STATIC_DCL char *NDECL(set_bonestemp_name); +#ifdef COMPRESS +STATIC_DCL void FDECL(redirect, (const char *,const char *,FILE *,BOOLEAN_P)); +STATIC_DCL void FDECL(docompress_file, (const char *,BOOLEAN_P)); +#endif +STATIC_DCL char *FDECL(make_lockname, (const char *,char *)); +STATIC_DCL FILE *FDECL(fopen_config_file, (const char *)); +STATIC_DCL int FDECL(get_uchars, (FILE *,char *,char *,uchar *,BOOLEAN_P,int,const char *)); +int FDECL(parse_config_line, (FILE *,char *,char *,char *)); +#ifdef NOCWD_ASSUMPTIONS +STATIC_DCL void FDECL(adjust_prefix, (char *, int)); +#endif +#ifdef SELF_RECOVER +STATIC_DCL boolean FDECL(copy_bytes, (int, int)); +#endif +#ifdef HOLD_LOCKFILE_OPEN +STATIC_DCL int FDECL(open_levelfile_exclusively, (const char *, int, int)); +#endif + +/* + * fname_encode() + * + * Args: + * legal zero-terminated list of acceptable file name characters + * quotechar lead-in character used to quote illegal characters as hex digits + * s string to encode + * callerbuf buffer to house result + * bufsz size of callerbuf + * + * Notes: + * The hex digits 0-9 and A-F are always part of the legal set due to + * their use in the encoding scheme, even if not explicitly included in 'legal'. + * + * Sample: + * The following call: + * (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + * '%', "This is a % test!", buf, 512); + * results in this encoding: + * "This%20is%20a%20%25%20test%21" + */ +char * +fname_encode(legal, quotechar, s, callerbuf, bufsz) +const char *legal; +char quotechar; +char *s, *callerbuf; +int bufsz; +{ + char *sp, *op; + int cnt = 0; + static char hexdigits[] = "0123456789ABCDEF"; + + sp = s; + op = callerbuf; + *op = '\0'; + + while (*sp) { + /* Do we have room for one more character or encoding? */ + if ((bufsz - cnt) <= 4) return callerbuf; + + if (*sp == quotechar) { + (void)sprintf(op, "%c%02X", quotechar, *sp); + op += 3; + cnt += 3; + } else if ((index(legal, *sp) != 0) || (index(hexdigits, *sp) != 0)) { + *op++ = *sp; + *op = '\0'; + cnt++; + } else { + (void)sprintf(op,"%c%02X", quotechar, *sp); + op += 3; + cnt += 3; + } + sp++; + } + return callerbuf; +} + +/* + * fname_decode() + * + * Args: + * quotechar lead-in character used to quote illegal characters as hex digits + * s string to decode + * callerbuf buffer to house result + * bufsz size of callerbuf + */ +char * +fname_decode(quotechar, s, callerbuf, bufsz) +char quotechar; +char *s, *callerbuf; +int bufsz; +{ + char *sp, *op; + int k,calc,cnt = 0; + static char hexdigits[] = "0123456789ABCDEF"; + + sp = s; + op = callerbuf; + *op = '\0'; + calc = 0; + + while (*sp) { + /* Do we have room for one more character? */ + if ((bufsz - cnt) <= 2) return callerbuf; + if (*sp == quotechar) { + sp++; + for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break; + if (k >= 16) return callerbuf; /* impossible, so bail */ + calc = k << 4; + sp++; + for (k=0; k < 16; ++k) if (*sp == hexdigits[k]) break; + if (k >= 16) return callerbuf; /* impossible, so bail */ + calc += k; + sp++; + *op++ = calc; + *op = '\0'; + } else { + *op++ = *sp++; + *op = '\0'; + } + cnt++; + } + return callerbuf; +} + +#ifndef PREFIXES_IN_USE +/*ARGSUSED*/ +#endif +const char * +fqname(basename, whichprefix, buffnum) +const char *basename; +int whichprefix, buffnum; +{ +#ifndef PREFIXES_IN_USE + return basename; +#else + if (!basename || whichprefix < 0 || whichprefix >= PREFIX_COUNT) + return basename; + if (!fqn_prefix[whichprefix]) + return basename; + if (buffnum < 0 || buffnum >= FQN_NUMBUF) { + impossible("Invalid fqn_filename_buffer specified: %d", + buffnum); + buffnum = 0; + } + if (strlen(fqn_prefix[whichprefix]) + strlen(basename) >= + FQN_MAX_FILENAME) { + impossible("fqname too long: %s + %s", fqn_prefix[whichprefix], + basename); + return basename; /* XXX */ + } + Strcpy(fqn_filename_buffer[buffnum], fqn_prefix[whichprefix]); + return strcat(fqn_filename_buffer[buffnum], basename); +#endif +} + +/* reasonbuf must be at least BUFSZ, supplied by caller */ +/*ARGSUSED*/ +int +validate_prefix_locations(reasonbuf) +char *reasonbuf; +{ +#if defined(NOCWD_ASSUMPTIONS) + FILE *fp; + const char *filename; + int prefcnt, failcount = 0; + char panicbuf1[BUFSZ], panicbuf2[BUFSZ], *details; + + if (reasonbuf) reasonbuf[0] = '\0'; + for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) { + /* don't test writing to configdir or datadir; they're readonly */ + if (prefcnt == CONFIGPREFIX || prefcnt == DATAPREFIX) continue; + filename = fqname("validate", prefcnt, 3); + if ((fp = fopen(filename, "w"))) { + fclose(fp); + (void) unlink(filename); + } else { + if (reasonbuf) { + if (failcount) Strcat(reasonbuf,", "); + Strcat(reasonbuf, fqn_prefix_names[prefcnt]); + } + /* the paniclog entry gets the value of errno as well */ + Sprintf(panicbuf1,"Invalid %s", fqn_prefix_names[prefcnt]); +#if defined (NHSTDC) && !defined(NOTSTDC) + if (!(details = strerror(errno))) +#endif + details = ""; + Sprintf(panicbuf2,"\"%s\", (%d) %s", + fqn_prefix[prefcnt], errno, details); + paniclog(panicbuf1, panicbuf2); + failcount++; + } + } + if (failcount) + return 0; + else +#endif + return 1; +} + +/* fopen a file, with OS-dependent bells and whistles */ +/* NOTE: a simpler version of this routine also exists in util/dlb_main.c */ +FILE * +fopen_datafile(filename, mode, prefix) +const char *filename, *mode; +int prefix; +{ + FILE *fp; + + filename = fqname(filename, prefix, prefix == TROUBLEPREFIX ? 3 : 0); +#ifdef VMS /* essential to have punctuation, to avoid logical names */ + { + char tmp[BUFSIZ]; + + if (!index(filename, '.') && !index(filename, ';')) + filename = strcat(strcpy(tmp, filename), ";0"); + fp = fopen(filename, mode, "mbc=16"); + } +#else + fp = fopen(filename, mode); +#endif + return fp; +} + +/* ---------- BEGIN LEVEL FILE HANDLING ----------- */ + +#ifdef MFLOPPY +/* Set names for bones[] and lock[] */ +void +set_lock_and_bones() +{ + if (!ramdisk) { + Strcpy(levels, permbones); + Strcpy(bones, permbones); + } + append_slash(permbones); + append_slash(levels); +#ifdef AMIGA + strncat(levels, bbs_id, PATHLEN); +#endif + append_slash(bones); + Strcat(bones, "bonesnn.*"); + Strcpy(lock, levels); +#ifndef AMIGA + Strcat(lock, alllevels); +#endif + return; +} +#endif /* MFLOPPY */ + + +/* Construct a file name for a level-type file, which is of the form + * something.level (with any old level stripped off). + * This assumes there is space on the end of 'file' to append + * a two digit number. This is true for 'level' + * but be careful if you use it for other things -dgk + */ +void +set_levelfile_name(file, lev) +char *file; +int lev; +{ + char *tf; + + tf = rindex(file, '.'); + if (!tf) tf = eos(file); + Sprintf(tf, ".%d", lev); +#ifdef VMS + Strcat(tf, ";1"); +#endif + return; +} + +int +create_levelfile(lev, errbuf) +int lev; +char errbuf[]; +{ + int fd; + const char *fq_lock; + + if (errbuf) *errbuf = '\0'; + set_levelfile_name(lock, lev); + fq_lock = fqname(lock, LEVELPREFIX, 0); + +#if defined(MICRO) || defined(WIN32) + /* Use O_TRUNC to force the file to be shortened if it already + * exists and is currently longer. + */ +# ifdef HOLD_LOCKFILE_OPEN + if (lev == 0) + fd = open_levelfile_exclusively(fq_lock, lev, + O_WRONLY |O_CREAT | O_TRUNC | O_BINARY); + else +# endif + fd = open(fq_lock, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK); +#else +# ifdef MAC + fd = maccreat(fq_lock, LEVL_TYPE); +# else + fd = creat(fq_lock, FCMASK); +# endif +#endif /* MICRO || WIN32 */ + + if (fd >= 0) + level_info[lev].flags |= LFILE_EXISTS; + else if (errbuf) /* failure explanation */ + Sprintf(errbuf, + "Cannot create file \"%s\" for level %d (errno %d).", + lock, lev, errno); + + return fd; +} + + +int +open_levelfile(lev, errbuf) +int lev; +char errbuf[]; +{ + int fd; + const char *fq_lock; + + if (errbuf) *errbuf = '\0'; + set_levelfile_name(lock, lev); + fq_lock = fqname(lock, LEVELPREFIX, 0); +#ifdef MFLOPPY + /* If not currently accessible, swap it in. */ + if (level_info[lev].where != ACTIVE) + swapin_file(lev); +#endif +#ifdef MAC + fd = macopen(fq_lock, O_RDONLY | O_BINARY, LEVL_TYPE); +#else +# ifdef HOLD_LOCKFILE_OPEN + if (lev == 0) + fd = open_levelfile_exclusively(fq_lock, lev, O_RDONLY | O_BINARY ); + else +# endif + fd = open(fq_lock, O_RDONLY | O_BINARY, 0); +#endif + + /* for failure, return an explanation that our caller can use; + settle for `lock' instead of `fq_lock' because the latter + might end up being too big for nethack's BUFSZ */ + if (fd < 0 && errbuf) + Sprintf(errbuf, + "Cannot open file \"%s\" for level %d (errno %d).", + lock, lev, errno); + + return fd; +} + + +void +delete_levelfile(lev) +int lev; +{ + /* + * Level 0 might be created by port specific code that doesn't + * call create_levfile(), so always assume that it exists. + */ + if (lev == 0 || (level_info[lev].flags & LFILE_EXISTS)) { + set_levelfile_name(lock, lev); +#ifdef HOLD_LOCKFILE_OPEN + if (lev == 0) really_close(); +#endif + (void) unlink(fqname(lock, LEVELPREFIX, 0)); + level_info[lev].flags &= ~LFILE_EXISTS; + } +} + + +void +clearlocks() +{ +#if !defined(PC_LOCKING) && defined(MFLOPPY) && !defined(AMIGA) + eraseall(levels, alllevels); + if (ramdisk) + eraseall(permbones, alllevels); +#else + register int x; + +# if defined(UNIX) || defined(VMS) + (void) signal(SIGHUP, SIG_IGN); +# endif + /* can't access maxledgerno() before dungeons are created -dlc */ + for (x = (n_dgns ? maxledgerno() : 0); x >= 0; x--) + delete_levelfile(x); /* not all levels need be present */ +#endif +} + +#ifdef HOLD_LOCKFILE_OPEN +STATIC_OVL int +open_levelfile_exclusively(name, lev, oflag) +const char *name; +int lev, oflag; +{ + int reslt, fd; + if (!lftrack.init) { + lftrack.init = 1; + lftrack.fd = -1; + } + if (lftrack.fd >= 0) { + /* check for compatible access */ + if (lftrack.oflag == oflag) { + fd = lftrack.fd; + reslt = lseek(fd, 0L, SEEK_SET); + if (reslt == -1L) + panic("open_levelfile_exclusively: lseek failed %d", errno); + lftrack.nethack_thinks_it_is_open = TRUE; + } else { + really_close(); + fd = sopen(name, oflag,SH_DENYRW, FCMASK); + lftrack.fd = fd; + lftrack.oflag = oflag; + lftrack.nethack_thinks_it_is_open = TRUE; + } + } else { + fd = sopen(name, oflag,SH_DENYRW, FCMASK); + lftrack.fd = fd; + lftrack.oflag = oflag; + if (fd >= 0) + lftrack.nethack_thinks_it_is_open = TRUE; + } + return fd; +} + +void +really_close() +{ + int fd = lftrack.fd; + lftrack.nethack_thinks_it_is_open = FALSE; + lftrack.fd = -1; + lftrack.oflag = 0; + (void)_close(fd); + return; +} + +int +close(fd) +int fd; +{ + if (lftrack.fd == fd) { + really_close(); /* close it, but reopen it to hold it */ + fd = open_levelfile(0, (char *)0); + lftrack.nethack_thinks_it_is_open = FALSE; + return 0; + } + return _close(fd); +} +#endif + +/* ---------- END LEVEL FILE HANDLING ----------- */ + + +/* ---------- BEGIN BONES FILE HANDLING ----------- */ + +/* set up "file" to be file name for retrieving bones, and return a + * bonesid to be read/written in the bones file. + */ +STATIC_OVL char * +set_bonesfile_name(file, lev) +char *file; +d_level *lev; +{ + s_level *sptr; + char *dptr; + + Sprintf(file, "bon%c%s", dungeons[lev->dnum].boneid, + In_quest(lev) ? urole.filecode : "0"); + dptr = eos(file); + if ((sptr = Is_special(lev)) != 0) + Sprintf(dptr, ".%c", sptr->boneid); + else + Sprintf(dptr, ".%d", lev->dlevel); +#ifdef VMS + Strcat(dptr, ";1"); +#endif + return(dptr-2); +} + +/* set up temporary file name for writing bones, to avoid another game's + * trying to read from an uncompleted bones file. we want an uncontentious + * name, so use one in the namespace reserved for this game's level files. + * (we are not reading or writing level files while writing bones files, so + * the same array may be used instead of copying.) + */ +STATIC_OVL char * +set_bonestemp_name() +{ + char *tf; + + tf = rindex(lock, '.'); + if (!tf) tf = eos(lock); + Sprintf(tf, ".bn"); +#ifdef VMS + Strcat(tf, ";1"); +#endif + return lock; +} + +int +create_bonesfile(lev, bonesid, errbuf) +d_level *lev; +char **bonesid; +char errbuf[]; +{ + const char *file; + int fd; + + if (errbuf) *errbuf = '\0'; + *bonesid = set_bonesfile_name(bones, lev); + file = set_bonestemp_name(); + file = fqname(file, BONESPREFIX, 0); + +#if defined(MICRO) || defined(WIN32) + /* Use O_TRUNC to force the file to be shortened if it already + * exists and is currently longer. + */ + fd = open(file, O_WRONLY |O_CREAT | O_TRUNC | O_BINARY, FCMASK); +#else +# ifdef MAC + fd = maccreat(file, BONE_TYPE); +# else + fd = creat(file, FCMASK); +# endif +#endif + if (fd < 0 && errbuf) /* failure explanation */ + Sprintf(errbuf, + "Cannot create bones \"%s\", id %s (errno %d).", + lock, *bonesid, errno); + +# if defined(VMS) && !defined(SECURE) + /* + Re-protect bones file with world:read+write+execute+delete access. + umask() doesn't seem very reliable; also, vaxcrtl won't let us set + delete access without write access, which is what's really wanted. + Can't simply create it with the desired protection because creat + ANDs the mask with the user's default protection, which usually + denies some or all access to world. + */ + (void) chmod(file, FCMASK | 007); /* allow other users full access */ +# endif /* VMS && !SECURE */ + + return fd; +} + +#ifdef MFLOPPY +/* remove partial bonesfile in process of creation */ +void +cancel_bonesfile() +{ + const char *tempname; + + tempname = set_bonestemp_name(); + tempname = fqname(tempname, BONESPREFIX, 0); + (void) unlink(tempname); +} +#endif /* MFLOPPY */ + +/* move completed bones file to proper name */ +void +commit_bonesfile(lev) +d_level *lev; +{ + const char *fq_bones, *tempname; + int ret; + + (void) set_bonesfile_name(bones, lev); + fq_bones = fqname(bones, BONESPREFIX, 0); + tempname = set_bonestemp_name(); + tempname = fqname(tempname, BONESPREFIX, 1); + +#if (defined(SYSV) && !defined(SVR4)) || defined(GENIX) + /* old SYSVs don't have rename. Some SVR3's may, but since they + * also have link/unlink, it doesn't matter. :-) + */ + (void) unlink(fq_bones); + ret = link(tempname, fq_bones); + ret += unlink(tempname); +#else + ret = rename(tempname, fq_bones); +#endif +#ifdef WIZARD + if (wizard && ret != 0) + pline("couldn't rename %s to %s.", tempname, fq_bones); +#endif +} + + +int +open_bonesfile(lev, bonesid) +d_level *lev; +char **bonesid; +{ + const char *fq_bones; + int fd; + + *bonesid = set_bonesfile_name(bones, lev); + fq_bones = fqname(bones, BONESPREFIX, 0); + uncompress(fq_bones); /* no effect if nonexistent */ +#ifdef MAC + fd = macopen(fq_bones, O_RDONLY | O_BINARY, BONE_TYPE); +#else + fd = open(fq_bones, O_RDONLY | O_BINARY, 0); +#endif + return fd; +} + + +int +delete_bonesfile(lev) +d_level *lev; +{ + (void) set_bonesfile_name(bones, lev); + return !(unlink(fqname(bones, BONESPREFIX, 0)) < 0); +} + + +/* assume we're compressing the recently read or created bonesfile, so the + * file name is already set properly */ +void +compress_bonesfile() +{ + compress(fqname(bones, BONESPREFIX, 0)); +} + +/* ---------- END BONES FILE HANDLING ----------- */ + + +/* ---------- BEGIN SAVE FILE HANDLING ----------- */ + +/* set savefile name in OS-dependent manner from pre-existing plname, + * avoiding troublesome characters */ +void +set_savefile_name() +{ +#if defined(WIN32) + char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; +#endif +#ifdef VMS + Sprintf(SAVEF, "[.save]%d%s", getuid(), plname); + regularize(SAVEF+7); + Strcat(SAVEF, ";1"); +#else +# if defined(MICRO) + Strcpy(SAVEF, SAVEP); +# ifdef AMIGA + strncat(SAVEF, bbs_id, PATHLEN); +# endif + { + int i = strlen(SAVEP); +# ifdef AMIGA + /* plname has to share space with SAVEP and ".sav" */ + (void)strncat(SAVEF, plname, FILENAME - i - 4); +# else + (void)strncat(SAVEF, plname, 8); +# endif + regularize(SAVEF+i); + } + Strcat(SAVEF, ".sav"); +# else +# if defined(WIN32) + /* Obtain the name of the logged on user and incorporate + * it into the name. */ + Sprintf(fnamebuf, "%s-%s", get_username(0), plname); + (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", + '%', fnamebuf, encodedfnamebuf, BUFSZ); + Sprintf(SAVEF, "%s.NetHack-saved-game", encodedfnamebuf); +# else + Sprintf(SAVEF, "save/%d%s", (int)getuid(), plname); + regularize(SAVEF+5); /* avoid . or / in name */ +# endif /* WIN32 */ +# endif /* MICRO */ +#endif /* VMS */ +} + +#ifdef INSURANCE +void +save_savefile_name(fd) +int fd; +{ + (void) write(fd, (genericptr_t) SAVEF, sizeof(SAVEF)); +} +#endif + + +#if defined(WIZARD) && !defined(MICRO) +/* change pre-existing savefile name to indicate an error savefile */ +void +set_error_savefile() +{ +# ifdef VMS + { + char *semi_colon = rindex(SAVEF, ';'); + if (semi_colon) *semi_colon = '\0'; + } + Strcat(SAVEF, ".e;1"); +# else +# ifdef MAC + Strcat(SAVEF, "-e"); +# else + Strcat(SAVEF, ".e"); +# endif +# endif +} +#endif + + +/* create save file, overwriting one if it already exists */ +int +create_savefile() +{ + const char *fq_save; + int fd; + + fq_save = fqname(SAVEF, SAVEPREFIX, 0); +#if defined(MICRO) || defined(WIN32) + fd = open(fq_save, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); +#else +# ifdef MAC + fd = maccreat(fq_save, SAVE_TYPE); +# else + fd = creat(fq_save, FCMASK); +# endif +# if defined(VMS) && !defined(SECURE) + /* + Make sure the save file is owned by the current process. That's + the default for non-privileged users, but for priv'd users the + file will be owned by the directory's owner instead of the user. + */ +# ifdef getuid /*(see vmsunix.c)*/ +# undef getuid +# endif + (void) chown(fq_save, getuid(), getgid()); +# endif /* VMS && !SECURE */ +#endif /* MICRO */ + + return fd; +} + + +/* open savefile for reading */ +int +open_savefile() +{ + const char *fq_save; + int fd; + + fq_save = fqname(SAVEF, SAVEPREFIX, 0); +#ifdef MAC + fd = macopen(fq_save, O_RDONLY | O_BINARY, SAVE_TYPE); +#else + fd = open(fq_save, O_RDONLY | O_BINARY, 0); +#endif + return fd; +} + + +/* delete savefile */ +int +delete_savefile() +{ + (void) unlink(fqname(SAVEF, SAVEPREFIX, 0)); + return 0; /* for restore_saved_game() (ex-xxxmain.c) test */ +} + + +/* try to open up a save file and prepare to restore it */ +int +restore_saved_game() +{ + const char *fq_save; + int fd; + + set_savefile_name(); +#ifdef MFLOPPY + if (!saveDiskPrompt(1)) + return -1; +#endif /* MFLOPPY */ + fq_save = fqname(SAVEF, SAVEPREFIX, 0); + + uncompress(fq_save); + if ((fd = open_savefile()) < 0) return fd; + + if (!uptodate(fd, fq_save)) { + (void) close(fd), fd = -1; + (void) delete_savefile(); + } + return fd; +} + +#if defined(UNIX) && defined(QT_GRAPHICS) +/*ARGSUSED*/ +static char* +plname_from_file(filename) +const char* filename; +{ +#ifdef STORE_PLNAME_IN_FILE + int fd; + char* result = 0; + + Strcpy(SAVEF,filename); +#ifdef COMPRESS_EXTENSION + SAVEF[strlen(SAVEF)-strlen(COMPRESS_EXTENSION)] = '\0'; +#endif + uncompress(SAVEF); + if ((fd = open_savefile()) >= 0) { + if (uptodate(fd, filename)) { + char tplname[PL_NSIZ]; + mread(fd, (genericptr_t) tplname, PL_NSIZ); + result = strdup(tplname); + } + (void) close(fd); + } + compress(SAVEF); + + return result; +#else +# if defined(UNIX) && defined(QT_GRAPHICS) + /* Name not stored in save file, so we have to extract it from + the filename, which loses information + (eg. "/", "_", and "." characters are lost. */ + int k; + int uid; + char name[64]; /* more than PL_NSIZ */ +#ifdef COMPRESS_EXTENSION +#define EXTSTR COMPRESS_EXTENSION +#else +#define EXTSTR "" +#endif + if ( sscanf( filename, "%*[^/]/%d%63[^.]" EXTSTR, &uid, name ) == 2 ) { +#undef EXTSTR + /* "_" most likely means " ", which certainly looks nicer */ + for (k=0; name[k]; k++) + if ( name[k]=='_' ) + name[k]=' '; + return strdup(name); + } else +# endif + { + return 0; + } +#endif +} +#endif /* defined(UNIX) && defined(QT_GRAPHICS) */ + +char** +get_saved_games() +{ +#if defined(UNIX) && defined(QT_GRAPHICS) + int myuid=getuid(); + struct dirent **namelist; + int n = scandir("save", &namelist, 0, alphasort);; + if ( n > 0 ) { + int i,j=0; + char** result = (char**)alloc((n+1)*sizeof(char*)); /* at most */ + for (i=0; id_name, "%d%63s", &uid, name ) == 2 ) { + if ( uid == myuid ) { + char filename[BUFSZ]; + char* r; + Sprintf(filename,"save/%d%s",uid,name); + r = plname_from_file(filename); + if ( r ) + result[j++] = r; + } + } + } + result[j++] = 0; + return result; + } else +#endif + { + return 0; + } +} + +void +free_saved_games(saved) +char** saved; +{ + if ( saved ) { + int i=0; + while (saved[i]) free((genericptr_t)saved[i++]); + free((genericptr_t)saved); + } +} + + +/* ---------- END SAVE FILE HANDLING ----------- */ + + +/* ---------- BEGIN FILE COMPRESSION HANDLING ----------- */ + +#ifdef COMPRESS + +STATIC_OVL void +redirect(filename, mode, stream, uncomp) +const char *filename, *mode; +FILE *stream; +boolean uncomp; +{ + if (freopen(filename, mode, stream) == (FILE *)0) { + (void) fprintf(stderr, "freopen of %s for %scompress failed\n", + filename, uncomp ? "un" : ""); + terminate(EXIT_FAILURE); + } +} + +/* + * using system() is simpler, but opens up security holes and causes + * problems on at least Interactive UNIX 3.0.1 (SVR3.2), where any + * setuid is renounced by /bin/sh, so the files cannot be accessed. + * + * cf. child() in unixunix.c. + */ +STATIC_OVL void +docompress_file(filename, uncomp) +const char *filename; +boolean uncomp; +{ + char cfn[80]; + FILE *cf; + const char *args[10]; +# ifdef COMPRESS_OPTIONS + char opts[80]; +# endif + int i = 0; + int f; +# ifdef TTY_GRAPHICS + boolean istty = !strncmpi(windowprocs.name, "tty", 3); +# endif + + Strcpy(cfn, filename); +# ifdef COMPRESS_EXTENSION + Strcat(cfn, COMPRESS_EXTENSION); +# endif + /* when compressing, we know the file exists */ + if (uncomp) { + if ((cf = fopen(cfn, RDBMODE)) == (FILE *)0) + return; + (void) fclose(cf); + } + + args[0] = COMPRESS; + if (uncomp) args[++i] = "-d"; /* uncompress */ +# ifdef COMPRESS_OPTIONS + { + /* we can't guarantee there's only one additional option, sigh */ + char *opt; + boolean inword = FALSE; + + Strcpy(opts, COMPRESS_OPTIONS); + opt = opts; + while (*opt) { + if ((*opt == ' ') || (*opt == '\t')) { + if (inword) { + *opt = '\0'; + inword = FALSE; + } + } else if (!inword) { + args[++i] = opt; + inword = TRUE; + } + opt++; + } + } +# endif + args[++i] = (char *)0; + +# ifdef TTY_GRAPHICS + /* If we don't do this and we are right after a y/n question *and* + * there is an error message from the compression, the 'y' or 'n' can + * end up being displayed after the error message. + */ + if (istty) + mark_synch(); +# endif + f = fork(); + if (f == 0) { /* child */ +# ifdef TTY_GRAPHICS + /* any error messages from the compression must come out after + * the first line, because the more() to let the user read + * them will have to clear the first line. This should be + * invisible if there are no error messages. + */ + if (istty) + raw_print(""); +# endif + /* run compressor without privileges, in case other programs + * have surprises along the line of gzip once taking filenames + * in GZIP. + */ + /* assume all compressors will compress stdin to stdout + * without explicit filenames. this is true of at least + * compress and gzip, those mentioned in config.h. + */ + if (uncomp) { + redirect(cfn, RDBMODE, stdin, uncomp); + redirect(filename, WRBMODE, stdout, uncomp); + } else { + redirect(filename, RDBMODE, stdin, uncomp); + redirect(cfn, WRBMODE, stdout, uncomp); + } + (void) setgid(getgid()); + (void) setuid(getuid()); + (void) execv(args[0], (char *const *) args); + perror((char *)0); + (void) fprintf(stderr, "Exec to %scompress %s failed.\n", + uncomp ? "un" : "", filename); + terminate(EXIT_FAILURE); + } else if (f == -1) { + perror((char *)0); + pline("Fork to %scompress %s failed.", + uncomp ? "un" : "", filename); + return; + } + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); + (void) wait((int *)&i); + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +# ifdef WIZARD + if (wizard) (void) signal(SIGQUIT, SIG_DFL); +# endif + if (i == 0) { + /* (un)compress succeeded: remove file left behind */ + if (uncomp) + (void) unlink(cfn); + else + (void) unlink(filename); + } else { + /* (un)compress failed; remove the new, bad file */ + if (uncomp) { + raw_printf("Unable to uncompress %s", filename); + (void) unlink(filename); + } else { + /* no message needed for compress case; life will go on */ + (void) unlink(cfn); + } +#ifdef TTY_GRAPHICS + /* Give them a chance to read any error messages from the + * compression--these would go to stdout or stderr and would get + * overwritten only in tty mode. It's still ugly, since the + * messages are being written on top of the screen, but at least + * the user can read them. + */ + if (istty) + { + clear_nhwindow(WIN_MESSAGE); + more(); + /* No way to know if this is feasible */ + /* doredraw(); */ + } +#endif + } +} +#endif /* COMPRESS */ + +/* compress file */ +void +compress(filename) +const char *filename; +{ +#ifndef COMPRESS +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(filename) +#endif +#else + docompress_file(filename, FALSE); +#endif +} + + +/* uncompress file if it exists */ +void +uncompress(filename) +const char *filename; +{ +#ifndef COMPRESS +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(filename) +#endif +#else + docompress_file(filename, TRUE); +#endif +} + +/* ---------- END FILE COMPRESSION HANDLING ----------- */ + + +/* ---------- BEGIN FILE LOCKING HANDLING ----------- */ + +static int nesting = 0; + +#ifdef NO_FILE_LINKS /* implies UNIX */ +static int lockfd; /* for lock_file() to pass to unlock_file() */ +#endif + +#define HUP if (!program_state.done_hup) + +STATIC_OVL char * +make_lockname(filename, lockname) +const char *filename; +char *lockname; +{ +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(filename,lockname) + return (char*)0; +#else +# if defined(UNIX) || defined(VMS) || defined(AMIGA) || defined(WIN32) || defined(MSDOS) +# ifdef NO_FILE_LINKS + Strcpy(lockname, LOCKDIR); + Strcat(lockname, "/"); + Strcat(lockname, filename); +# else + Strcpy(lockname, filename); +# endif +# ifdef VMS + { + char *semi_colon = rindex(lockname, ';'); + if (semi_colon) *semi_colon = '\0'; + } + Strcat(lockname, ".lock;1"); +# else + Strcat(lockname, "_lock"); +# endif + return lockname; +# else + lockname[0] = '\0'; + return (char*)0; +# endif /* UNIX || VMS || AMIGA || WIN32 || MSDOS */ +#endif +} + + +/* lock a file */ +boolean +lock_file(filename, whichprefix, retryct) +const char *filename; +int whichprefix; +int retryct; +{ +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(filename, retryct) +#endif + char locknambuf[BUFSZ]; + const char *lockname; + + nesting++; + if (nesting > 1) { + impossible("TRIED TO NEST LOCKS"); + return TRUE; + } + + lockname = make_lockname(filename, locknambuf); + filename = fqname(filename, whichprefix, 0); +#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */ + lockname = fqname(lockname, LOCKPREFIX, 2); +#endif + +#if defined(UNIX) || defined(VMS) +# ifdef NO_FILE_LINKS + while ((lockfd = open(lockname, O_RDWR|O_CREAT|O_EXCL, 0666)) == -1) { +# else + while (link(filename, lockname) == -1) { +# endif + register int errnosv = errno; + + switch (errnosv) { /* George Barbanis */ + case EEXIST: + if (retryct--) { + HUP raw_printf( + "Waiting for access to %s. (%d retries left).", + filename, retryct); +# if defined(SYSV) || defined(ULTRIX) || defined(VMS) + (void) +# endif + sleep(1); + } else { + HUP (void) raw_print("I give up. Sorry."); + HUP raw_printf("Perhaps there is an old %s around?", + lockname); + nesting--; + return FALSE; + } + + break; + case ENOENT: + HUP raw_printf("Can't find file %s to lock!", filename); + nesting--; + return FALSE; + case EACCES: + HUP raw_printf("No write permission to lock %s!", filename); + nesting--; + return FALSE; +# ifdef VMS /* c__translate(vmsfiles.c) */ + case EPERM: + /* could be misleading, but usually right */ + HUP raw_printf("Can't lock %s due to directory protection.", + filename); + nesting--; + return FALSE; +# endif + default: + HUP perror(lockname); + HUP raw_printf( + "Cannot lock %s for unknown reason (%d).", + filename, errnosv); + nesting--; + return FALSE; + } + + } +#endif /* UNIX || VMS */ + +#if defined(AMIGA) || defined(WIN32) || defined(MSDOS) +# ifdef AMIGA +#define OPENFAILURE(fd) (!fd) + lockptr = 0; +# else +#define OPENFAILURE(fd) (fd < 0) + lockptr = -1; +# endif + while (--retryct && OPENFAILURE(lockptr)) { +# if defined(WIN32) && !defined(WIN_CE) + lockptr = sopen(lockname, O_RDWR|O_CREAT, SH_DENYRW, S_IWRITE); +# else + (void)DeleteFile(lockname); /* in case dead process was here first */ +# ifdef AMIGA + lockptr = Open(lockname,MODE_NEWFILE); +# else + lockptr = open(lockname, O_RDWR|O_CREAT|O_EXCL, S_IWRITE); +# endif +# endif + if (OPENFAILURE(lockptr)) { + raw_printf("Waiting for access to %s. (%d retries left).", + filename, retryct); + Delay(50); + } + } + if (!retryct) { + raw_printf("I give up. Sorry."); + nesting--; + return FALSE; + } +#endif /* AMIGA || WIN32 || MSDOS */ + return TRUE; +} + + +#ifdef VMS /* for unlock_file, use the unlink() routine in vmsunix.c */ +# ifdef unlink +# undef unlink +# endif +# define unlink(foo) vms_unlink(foo) +#endif + +/* unlock file, which must be currently locked by lock_file */ +void +unlock_file(filename) +const char *filename; +#if defined(macintosh) && (defined(__SC__) || defined(__MRC__)) +# pragma unused(filename) +#endif +{ + char locknambuf[BUFSZ]; + const char *lockname; + + if (nesting == 1) { + lockname = make_lockname(filename, locknambuf); +#ifndef NO_FILE_LINKS /* LOCKDIR should be subsumed by LOCKPREFIX */ + lockname = fqname(lockname, LOCKPREFIX, 2); +#endif + +#if defined(UNIX) || defined(VMS) + if (unlink(lockname) < 0) + HUP raw_printf("Can't unlink %s.", lockname); +# ifdef NO_FILE_LINKS + (void) close(lockfd); +# endif + +#endif /* UNIX || VMS */ + +#if defined(AMIGA) || defined(WIN32) || defined(MSDOS) + if (lockptr) Close(lockptr); + DeleteFile(lockname); + lockptr = 0; +#endif /* AMIGA || WIN32 || MSDOS */ + } + + nesting--; +} + +/* ---------- END FILE LOCKING HANDLING ----------- */ + + +/* ---------- BEGIN CONFIG FILE HANDLING ----------- */ + +const char *configfile = +#ifdef UNIX + ".nethackrc"; +#else +# if defined(MAC) || defined(__BEOS__) + "NetHack Defaults"; +# else +# if defined(MSDOS) || defined(WIN32) + "defaults.nh"; +# else + "NetHack.cnf"; +# endif +# endif +#endif + + +#ifdef MSDOS +/* conflict with speed-dial under windows + * for XXX.cnf file so support of NetHack.cnf + * is for backward compatibility only. + * Preferred name (and first tried) is now defaults.nh but + * the game will try the old name if there + * is no defaults.nh. + */ +const char *backward_compat_configfile = "nethack.cnf"; +#endif + +#ifndef MFLOPPY +#define fopenp fopen +#endif + +STATIC_OVL FILE * +fopen_config_file(filename) +const char *filename; +{ + FILE *fp; +#if defined(UNIX) || defined(VMS) + char tmp_config[BUFSZ]; + char *envp; +#endif + + /* "filename" is an environment variable, so it should hang around */ + /* if set, it is expected to be a full path name (if relevant) */ + if (filename) { +#ifdef UNIX + if (access(filename, 4) == -1) { + /* 4 is R_OK on newer systems */ + /* nasty sneaky attempt to read file through + * NetHack's setuid permissions -- this is the only + * place a file name may be wholly under the player's + * control + */ + raw_printf("Access to %s denied (%d).", + filename, errno); + wait_synch(); + /* fall through to standard names */ + } else +#endif + if ((fp = fopenp(filename, "r")) != (FILE *)0) { + configfile = filename; + return(fp); +#if defined(UNIX) || defined(VMS) + } else { + /* access() above probably caught most problems for UNIX */ + raw_printf("Couldn't open requested config file %s (%d).", + filename, errno); + wait_synch(); + /* fall through to standard names */ +#endif + } + } + +#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) + if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) + != (FILE *)0) + return(fp); +# ifdef MSDOS + else if ((fp = fopenp(fqname(backward_compat_configfile, + CONFIGPREFIX, 0), "r")) != (FILE *)0) + return(fp); +# endif +#else + /* constructed full path names don't need fqname() */ +# ifdef VMS + if ((fp = fopenp(fqname("nethackini", CONFIGPREFIX, 0), "r")) + != (FILE *)0) { + configfile = "nethackini"; + return(fp); + } + if ((fp = fopenp("sys$login:nethack.ini", "r")) != (FILE *)0) { + configfile = "nethack.ini"; + return(fp); + } + + envp = nh_getenv("HOME"); + if (!envp) + Strcpy(tmp_config, "NetHack.cnf"); + else + Sprintf(tmp_config, "%s%s", envp, "NetHack.cnf"); + if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) + return(fp); +# else /* should be only UNIX left */ + envp = nh_getenv("HOME"); + if (!envp) + Strcpy(tmp_config, configfile); + else + Sprintf(tmp_config, "%s/%s", envp, configfile); + if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) + return(fp); +# if defined(__APPLE__) + /* try an alternative */ + if (envp) { + Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults"); + if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) + return(fp); + Sprintf(tmp_config, "%s/%s", envp, "Library/Preferences/NetHack Defaults.txt"); + if ((fp = fopenp(tmp_config, "r")) != (FILE *)0) + return(fp); + } +# endif + if (errno != ENOENT) { + char *details; + + /* e.g., problems when setuid NetHack can't search home + * directory restricted to user */ + +#if defined (NHSTDC) && !defined(NOTSTDC) + if ((details = strerror(errno)) == 0) +#endif + details = ""; + raw_printf("Couldn't open default config file %s %s(%d).", + tmp_config, details, errno); + wait_synch(); + } +# endif +#endif + return (FILE *)0; + +} + + +/* + * Retrieve a list of integers from a file into a uchar array. + * + * NOTE: zeros are inserted unless modlist is TRUE, in which case the list + * location is unchanged. Callers must handle zeros if modlist is FALSE. + */ +STATIC_OVL int +get_uchars(fp, buf, bufp, list, modlist, size, name) + FILE *fp; /* input file pointer */ + char *buf; /* read buffer, must be of size BUFSZ */ + char *bufp; /* current pointer */ + uchar *list; /* return list */ + boolean modlist; /* TRUE: list is being modified in place */ + int size; /* return list size */ + const char *name; /* name of option for error message */ +{ + unsigned int num = 0; + int count = 0; + boolean havenum = FALSE; + + while (1) { + switch(*bufp) { + case ' ': case '\0': + case '\t': case '\n': + if (havenum) { + /* if modifying in place, don't insert zeros */ + if (num || !modlist) list[count] = num; + count++; + num = 0; + havenum = FALSE; + } + if (count == size || !*bufp) return count; + bufp++; + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + havenum = TRUE; + num = num*10 + (*bufp-'0'); + bufp++; + break; + + case '\\': + if (fp == (FILE *)0) + goto gi_error; + do { + if (!fgets(buf, BUFSZ, fp)) goto gi_error; + } while (buf[0] == '#'); + bufp = buf; + break; + + default: +gi_error: + raw_printf("Syntax error in %s", name); + wait_synch(); + return count; + } + } + /*NOTREACHED*/ +} + +#ifdef NOCWD_ASSUMPTIONS +STATIC_OVL void +adjust_prefix(bufp, prefixid) +char *bufp; +int prefixid; +{ + char *ptr; + + if (!bufp) return; + /* Backward compatibility, ignore trailing ;n */ + if ((ptr = index(bufp, ';')) != 0) *ptr = '\0'; + if (strlen(bufp) > 0) { + fqn_prefix[prefixid] = (char *)alloc(strlen(bufp)+2); + Strcpy(fqn_prefix[prefixid], bufp); + append_slash(fqn_prefix[prefixid]); + } +} +#endif + +#define match_varname(INP,NAM,LEN) match_optname(INP, NAM, LEN, TRUE) + +/*ARGSUSED*/ +int +parse_config_line(fp, buf, tmp_ramdisk, tmp_levels) +FILE *fp; +char *buf; +char *tmp_ramdisk; +char *tmp_levels; +{ +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(tmp_ramdisk,tmp_levels) +#endif + char *bufp, *altp; + uchar translate[MAXPCHARS]; + int len; + + if (*buf == '#') + return 1; + + /* remove trailing whitespace */ + bufp = eos(buf); + while (--bufp > buf && isspace(*bufp)) + continue; + + if (bufp <= buf) + return 1; /* skip all-blank lines */ + else + *(bufp + 1) = '\0'; /* terminate line */ + + /* find the '=' or ':' */ + bufp = index(buf, '='); + altp = index(buf, ':'); + if (!bufp || (altp && altp < bufp)) bufp = altp; + if (!bufp) return 0; + + /* skip whitespace between '=' and value */ + do { ++bufp; } while (isspace(*bufp)); + + /* Go through possible variables */ + /* some of these (at least LEVELS and SAVE) should now set the + * appropriate fqn_prefix[] rather than specialized variables + */ + if (match_varname(buf, "OPTIONS", 4)) { + parseoptions(bufp, TRUE, TRUE); + if (plname[0]) /* If a name was given */ + plnamesuffix(); /* set the character class */ +#ifdef AUTOPICKUP_EXCEPTIONS + } else if (match_varname(buf, "AUTOPICKUP_EXCEPTION", 5)) { + add_autopickup_exception(bufp); +#endif +#ifdef NOCWD_ASSUMPTIONS + } else if (match_varname(buf, "HACKDIR", 4)) { + adjust_prefix(bufp, HACKPREFIX); + } else if (match_varname(buf, "LEVELDIR", 4) || + match_varname(buf, "LEVELS", 4)) { + adjust_prefix(bufp, LEVELPREFIX); + } else if (match_varname(buf, "SAVEDIR", 4)) { + adjust_prefix(bufp, SAVEPREFIX); + } else if (match_varname(buf, "BONESDIR", 5)) { + adjust_prefix(bufp, BONESPREFIX); + } else if (match_varname(buf, "DATADIR", 4)) { + adjust_prefix(bufp, DATAPREFIX); + } else if (match_varname(buf, "SCOREDIR", 4)) { + adjust_prefix(bufp, SCOREPREFIX); + } else if (match_varname(buf, "LOCKDIR", 4)) { + adjust_prefix(bufp, LOCKPREFIX); + } else if (match_varname(buf, "CONFIGDIR", 4)) { + adjust_prefix(bufp, CONFIGPREFIX); + } else if (match_varname(buf, "TROUBLEDIR", 4)) { + adjust_prefix(bufp, TROUBLEPREFIX); +#else /*NOCWD_ASSUMPTIONS*/ +# ifdef MICRO + } else if (match_varname(buf, "HACKDIR", 4)) { + (void) strncpy(hackdir, bufp, PATHLEN-1); +# ifdef MFLOPPY + } else if (match_varname(buf, "RAMDISK", 3)) { + /* The following ifdef is NOT in the wrong + * place. For now, we accept and silently + * ignore RAMDISK */ +# ifndef AMIGA + (void) strncpy(tmp_ramdisk, bufp, PATHLEN-1); +# endif +# endif + } else if (match_varname(buf, "LEVELS", 4)) { + (void) strncpy(tmp_levels, bufp, PATHLEN-1); + + } else if (match_varname(buf, "SAVE", 4)) { +# ifdef MFLOPPY + extern int saveprompt; +# endif + char *ptr; + if ((ptr = index(bufp, ';')) != 0) { + *ptr = '\0'; +# ifdef MFLOPPY + if (*(ptr+1) == 'n' || *(ptr+1) == 'N') { + saveprompt = FALSE; + } +# endif + } +# ifdef MFLOPPY + else + saveprompt = flags.asksavedisk; +# endif + + (void) strncpy(SAVEP, bufp, SAVESIZE-1); + append_slash(SAVEP); +# endif /* MICRO */ +#endif /*NOCWD_ASSUMPTIONS*/ + + } else if (match_varname(buf, "NAME", 4)) { + (void) strncpy(plname, bufp, PL_NSIZ-1); + plnamesuffix(); + } else if (match_varname(buf, "ROLE", 4) || + match_varname(buf, "CHARACTER", 4)) { + if ((len = str2role(bufp)) >= 0) + flags.initrole = len; + } else if (match_varname(buf, "DOGNAME", 3)) { + (void) strncpy(dogname, bufp, PL_PSIZ-1); + } else if (match_varname(buf, "CATNAME", 3)) { + (void) strncpy(catname, bufp, PL_PSIZ-1); + + } else if (match_varname(buf, "BOULDER", 3)) { + (void) get_uchars(fp, buf, bufp, &iflags.bouldersym, TRUE, + 1, "BOULDER"); + } else if (match_varname(buf, "GRAPHICS", 4)) { + len = get_uchars(fp, buf, bufp, translate, FALSE, + MAXPCHARS, "GRAPHICS"); + assign_graphics(translate, len, MAXPCHARS, 0); + } else if (match_varname(buf, "DUNGEON", 4)) { + len = get_uchars(fp, buf, bufp, translate, FALSE, + MAXDCHARS, "DUNGEON"); + assign_graphics(translate, len, MAXDCHARS, 0); + } else if (match_varname(buf, "TRAPS", 4)) { + len = get_uchars(fp, buf, bufp, translate, FALSE, + MAXTCHARS, "TRAPS"); + assign_graphics(translate, len, MAXTCHARS, MAXDCHARS); + } else if (match_varname(buf, "EFFECTS", 4)) { + len = get_uchars(fp, buf, bufp, translate, FALSE, + MAXECHARS, "EFFECTS"); + assign_graphics(translate, len, MAXECHARS, MAXDCHARS+MAXTCHARS); + + } else if (match_varname(buf, "OBJECTS", 3)) { + /* oc_syms[0] is the RANDOM object, unused */ + (void) get_uchars(fp, buf, bufp, &(oc_syms[1]), TRUE, + MAXOCLASSES-1, "OBJECTS"); + } else if (match_varname(buf, "MONSTERS", 3)) { + /* monsyms[0] is unused */ + (void) get_uchars(fp, buf, bufp, &(monsyms[1]), TRUE, + MAXMCLASSES-1, "MONSTERS"); + } else if (match_varname(buf, "WARNINGS", 5)) { + (void) get_uchars(fp, buf, bufp, translate, FALSE, + WARNCOUNT, "WARNINGS"); + assign_warnings(translate); +#ifdef WIZARD + } else if (match_varname(buf, "WIZKIT", 6)) { + (void) strncpy(wizkit, bufp, WIZKIT_MAX-1); +#endif +#ifdef AMIGA + } else if (match_varname(buf, "FONT", 4)) { + char *t; + + if( t = strchr( buf+5, ':' ) ) + { + *t = 0; + amii_set_text_font( buf+5, atoi( t + 1 ) ); + *t = ':'; + } + } else if (match_varname(buf, "PATH", 4)) { + (void) strncpy(PATH, bufp, PATHLEN-1); + } else if (match_varname(buf, "DEPTH", 5)) { + extern int amii_numcolors; + int val = atoi( bufp ); + amii_numcolors = 1L << min( DEPTH, val ); + } else if (match_varname(buf, "DRIPENS", 7)) { + int i, val; + char *t; + for (i = 0, t = strtok(bufp, ",/"); t != (char *)0; + i < 20 && (t = strtok((char*)0, ",/")), ++i) { + sscanf(t, "%d", &val ); + flags.amii_dripens[i] = val; + } + } else if (match_varname(buf, "SCREENMODE", 10 )) { + extern long amii_scrnmode; + if (!stricmp(bufp,"req")) + amii_scrnmode = 0xffffffff; /* Requester */ + else if( sscanf(bufp, "%x", &amii_scrnmode) != 1 ) + amii_scrnmode = 0; + } else if (match_varname(buf, "MSGPENS", 7)) { + extern int amii_msgAPen, amii_msgBPen; + char *t = strtok(bufp, ",/"); + if( t ) + { + sscanf(t, "%d", &amii_msgAPen); + if( t = strtok((char*)0, ",/") ) + sscanf(t, "%d", &amii_msgBPen); + } + } else if (match_varname(buf, "TEXTPENS", 8)) { + extern int amii_textAPen, amii_textBPen; + char *t = strtok(bufp, ",/"); + if( t ) + { + sscanf(t, "%d", &amii_textAPen); + if( t = strtok((char*)0, ",/") ) + sscanf(t, "%d", &amii_textBPen); + } + } else if (match_varname(buf, "MENUPENS", 8)) { + extern int amii_menuAPen, amii_menuBPen; + char *t = strtok(bufp, ",/"); + if( t ) + { + sscanf(t, "%d", &amii_menuAPen); + if( t = strtok((char*)0, ",/") ) + sscanf(t, "%d", &amii_menuBPen); + } + } else if (match_varname(buf, "STATUSPENS", 10)) { + extern int amii_statAPen, amii_statBPen; + char *t = strtok(bufp, ",/"); + if( t ) + { + sscanf(t, "%d", &amii_statAPen); + if( t = strtok((char*)0, ",/") ) + sscanf(t, "%d", &amii_statBPen); + } + } else if (match_varname(buf, "OTHERPENS", 9)) { + extern int amii_otherAPen, amii_otherBPen; + char *t = strtok(bufp, ",/"); + if( t ) + { + sscanf(t, "%d", &amii_otherAPen); + if( t = strtok((char*)0, ",/") ) + sscanf(t, "%d", &amii_otherBPen); + } + } else if (match_varname(buf, "PENS", 4)) { + extern unsigned short amii_init_map[ AMII_MAXCOLORS ]; + int i; + char *t; + + for (i = 0, t = strtok(bufp, ",/"); + i < AMII_MAXCOLORS && t != (char *)0; + t = strtok((char *)0, ",/"), ++i) + { + sscanf(t, "%hx", &amii_init_map[i]); + } + amii_setpens( amii_numcolors = i ); + } else if (match_varname(buf, "FGPENS", 6)) { + extern int foreg[ AMII_MAXCOLORS ]; + int i; + char *t; + + for (i = 0, t = strtok(bufp, ",/"); + i < AMII_MAXCOLORS && t != (char *)0; + t = strtok((char *)0, ",/"), ++i) + { + sscanf(t, "%d", &foreg[i]); + } + } else if (match_varname(buf, "BGPENS", 6)) { + extern int backg[ AMII_MAXCOLORS ]; + int i; + char *t; + + for (i = 0, t = strtok(bufp, ",/"); + i < AMII_MAXCOLORS && t != (char *)0; + t = strtok((char *)0, ",/"), ++i) + { + sscanf(t, "%d", &backg[i]); + } +#endif +#ifdef USER_SOUNDS + } else if (match_varname(buf, "SOUNDDIR", 8)) { + sounddir = (char *)strdup(bufp); + } else if (match_varname(buf, "SOUND", 5)) { + add_sound_mapping(bufp); +#endif +#ifdef QT_GRAPHICS + /* These should move to wc_ options */ + } else if (match_varname(buf, "QT_TILEWIDTH", 12)) { + extern char *qt_tilewidth; + if (qt_tilewidth == NULL) + qt_tilewidth=(char *)strdup(bufp); + } else if (match_varname(buf, "QT_TILEHEIGHT", 13)) { + extern char *qt_tileheight; + if (qt_tileheight == NULL) + qt_tileheight=(char *)strdup(bufp); + } else if (match_varname(buf, "QT_FONTSIZE", 11)) { + extern char *qt_fontsize; + if (qt_fontsize == NULL) + qt_fontsize=(char *)strdup(bufp); + } else if (match_varname(buf, "QT_COMPACT", 10)) { + extern int qt_compact_mode; + qt_compact_mode = atoi(bufp); +#endif + } else + return 0; + return 1; +} + +#ifdef USER_SOUNDS +boolean +can_read_file(filename) +const char *filename; +{ + return (access(filename, 4) == 0); +} +#endif /* USER_SOUNDS */ + +void +read_config_file(filename) +const char *filename; +{ +#define tmp_levels (char *)0 +#define tmp_ramdisk (char *)0 + +#if defined(MICRO) || defined(WIN32) +#undef tmp_levels + char tmp_levels[PATHLEN]; +# ifdef MFLOPPY +# ifndef AMIGA +#undef tmp_ramdisk + char tmp_ramdisk[PATHLEN]; +# endif +# endif +#endif + char buf[4*BUFSZ]; + FILE *fp; + + if (!(fp = fopen_config_file(filename))) return; + +#if defined(MICRO) || defined(WIN32) +# ifdef MFLOPPY +# ifndef AMIGA + tmp_ramdisk[0] = 0; +# endif +# endif + tmp_levels[0] = 0; +#endif + /* begin detection of duplicate configfile options */ + set_duplicate_opt_detection(1); + + while (fgets(buf, 4*BUFSZ, fp)) { + if (!parse_config_line(fp, buf, tmp_ramdisk, tmp_levels)) { + raw_printf("Bad option line: \"%.50s\"", buf); + wait_synch(); + } + } + (void) fclose(fp); + + /* turn off detection of duplicate configfile options */ + set_duplicate_opt_detection(0); + +#if defined(MICRO) && !defined(NOCWD_ASSUMPTIONS) + /* should be superseded by fqn_prefix[] */ +# ifdef MFLOPPY + Strcpy(permbones, tmp_levels); +# ifndef AMIGA + if (tmp_ramdisk[0]) { + Strcpy(levels, tmp_ramdisk); + if (strcmp(permbones, levels)) /* if not identical */ + ramdisk = TRUE; + } else +# endif /* AMIGA */ + Strcpy(levels, tmp_levels); + + Strcpy(bones, levels); +# endif /* MFLOPPY */ +#endif /* MICRO */ + return; +} + +#ifdef WIZARD +STATIC_OVL FILE * +fopen_wizkit_file() +{ + FILE *fp; +#if defined(VMS) || defined(UNIX) + char tmp_wizkit[BUFSZ]; +#endif + char *envp; + + envp = nh_getenv("WIZKIT"); + if (envp && *envp) (void) strncpy(wizkit, envp, WIZKIT_MAX - 1); + if (!wizkit[0]) return (FILE *)0; + +#ifdef UNIX + if (access(wizkit, 4) == -1) { + /* 4 is R_OK on newer systems */ + /* nasty sneaky attempt to read file through + * NetHack's setuid permissions -- this is a + * place a file name may be wholly under the player's + * control + */ + raw_printf("Access to %s denied (%d).", + wizkit, errno); + wait_synch(); + /* fall through to standard names */ + } else +#endif + if ((fp = fopenp(wizkit, "r")) != (FILE *)0) { + return(fp); +#if defined(UNIX) || defined(VMS) + } else { + /* access() above probably caught most problems for UNIX */ + raw_printf("Couldn't open requested config file %s (%d).", + wizkit, errno); + wait_synch(); +#endif + } + +#if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32) + if ((fp = fopenp(fqname(wizkit, CONFIGPREFIX, 0), "r")) + != (FILE *)0) + return(fp); +#else +# ifdef VMS + envp = nh_getenv("HOME"); + if (envp) + Sprintf(tmp_wizkit, "%s%s", envp, wizkit); + else + Sprintf(tmp_wizkit, "%s%s", "sys$login:", wizkit); + if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0) + return(fp); +# else /* should be only UNIX left */ + envp = nh_getenv("HOME"); + if (envp) + Sprintf(tmp_wizkit, "%s/%s", envp, wizkit); + else Strcpy(tmp_wizkit, wizkit); + if ((fp = fopenp(tmp_wizkit, "r")) != (FILE *)0) + return(fp); + else if (errno != ENOENT) { + /* e.g., problems when setuid NetHack can't search home + * directory restricted to user */ + raw_printf("Couldn't open default wizkit file %s (%d).", + tmp_wizkit, errno); + wait_synch(); + } +# endif +#endif + return (FILE *)0; +} + +void +read_wizkit() +{ + FILE *fp; + char *ep, buf[BUFSZ]; + struct obj *otmp; + boolean bad_items = FALSE, skip = FALSE; + + if (!wizard || !(fp = fopen_wizkit_file())) return; + + while (fgets(buf, (int)(sizeof buf), fp)) { + ep = index(buf, '\n'); + if (skip) { /* in case previous line was too long */ + if (ep) skip = FALSE; /* found newline; next line is normal */ + } else { + if (!ep) skip = TRUE; /* newline missing; discard next fgets */ + else *ep = '\0'; /* remove newline */ + + if (buf[0]) { + otmp = readobjnam(buf, (struct obj *)0, FALSE); + if (otmp) { + if (otmp != &zeroobj) + otmp = addinv(otmp); + } else { + /* .60 limits output line width to 79 chars */ + raw_printf("Bad wizkit item: \"%.60s\"", buf); + bad_items = TRUE; + } + } + } + } + if (bad_items) + wait_synch(); + (void) fclose(fp); + return; +} + +#endif /*WIZARD*/ + +/* ---------- END CONFIG FILE HANDLING ----------- */ + +/* ---------- BEGIN SCOREBOARD CREATION ----------- */ + +/* verify that we can write to the scoreboard file; if not, try to create one */ +void +check_recordfile(dir) +const char *dir; +{ +#if (defined(macintosh) && (defined(__SC__) || defined(__MRC__))) || defined(__MWERKS__) +# pragma unused(dir) +#endif + const char *fq_record; + int fd; + +#if defined(UNIX) || defined(VMS) + fq_record = fqname(RECORD, SCOREPREFIX, 0); + fd = open(fq_record, O_RDWR, 0); + if (fd >= 0) { +# ifdef VMS /* must be stream-lf to use UPDATE_RECORD_IN_PLACE */ + if (!file_is_stmlf(fd)) { + raw_printf( + "Warning: scoreboard file %s is not in stream_lf format", + fq_record); + wait_synch(); + } +# endif + (void) close(fd); /* RECORD is accessible */ + } else if ((fd = open(fq_record, O_CREAT|O_RDWR, FCMASK)) >= 0) { + (void) close(fd); /* RECORD newly created */ +# if defined(VMS) && !defined(SECURE) + /* Re-protect RECORD with world:read+write+execute+delete access. */ + (void) chmod(fq_record, FCMASK | 007); +# endif /* VMS && !SECURE */ + } else { + raw_printf("Warning: cannot write scoreboard file %s", fq_record); + wait_synch(); + } +#endif /* !UNIX && !VMS */ +#if defined(MICRO) || defined(WIN32) + char tmp[PATHLEN]; + +# ifdef OS2_CODEVIEW /* explicit path on opening for OS/2 */ + /* how does this work when there isn't an explicit path or fopenp + * for later access to the file via fopen_datafile? ? */ + (void) strncpy(tmp, dir, PATHLEN - 1); + tmp[PATHLEN-1] = '\0'; + if ((strlen(tmp) + 1 + strlen(RECORD)) < (PATHLEN - 1)) { + append_slash(tmp); + Strcat(tmp, RECORD); + } + fq_record = tmp; +# else + Strcpy(tmp, RECORD); + fq_record = fqname(RECORD, SCOREPREFIX, 0); +# endif + + if ((fd = open(fq_record, O_RDWR)) < 0) { + /* try to create empty record */ +# if defined(AZTEC_C) || defined(_DCC) || (defined(__GNUC__) && defined(__AMIGA__)) + /* Aztec doesn't use the third argument */ + /* DICE doesn't like it */ + if ((fd = open(fq_record, O_CREAT|O_RDWR)) < 0) { +# else + if ((fd = open(fq_record, O_CREAT|O_RDWR, S_IREAD|S_IWRITE)) < 0) { +# endif + raw_printf("Warning: cannot write record %s", tmp); + wait_synch(); + } else + (void) close(fd); + } else /* open succeeded */ + (void) close(fd); +#else /* MICRO || WIN32*/ + +# ifdef MAC + /* Create the "record" file, if necessary */ + fq_record = fqname(RECORD, SCOREPREFIX, 0); + fd = macopen (fq_record, O_RDWR | O_CREAT, TEXT_TYPE); + if (fd != -1) macclose (fd); +# endif /* MAC */ + +#endif /* MICRO || WIN32*/ +} + +/* ---------- END SCOREBOARD CREATION ----------- */ + +/* ---------- BEGIN PANIC/IMPOSSIBLE LOG ----------- */ + +/*ARGSUSED*/ +void +paniclog(type, reason) +const char *type; /* panic, impossible, trickery */ +const char *reason; /* explanation */ +{ +#ifdef PANICLOG + FILE *lfile; + char buf[BUFSZ]; + + if (!program_state.in_paniclog) { + program_state.in_paniclog = 1; + lfile = fopen_datafile(PANICLOG, "a", TROUBLEPREFIX); + if (lfile) { + (void) fprintf(lfile, "%s %08ld: %s %s\n", + version_string(buf), yyyymmdd((time_t)0L), + type, reason); + (void) fclose(lfile); + } + program_state.in_paniclog = 0; + } +#endif /* PANICLOG */ + return; +} + +/* ---------- END PANIC/IMPOSSIBLE LOG ----------- */ + +#ifdef SELF_RECOVER + +/* ---------- BEGIN INTERNAL RECOVER ----------- */ +boolean +recover_savefile() +{ + int gfd, lfd, sfd; + int lev, savelev, hpid; + xchar levc; + struct version_info version_data; + int processed[256]; + char savename[SAVESIZE], errbuf[BUFSZ]; + + for (lev = 0; lev < 256; lev++) + processed[lev] = 0; + + /* level 0 file contains: + * pid of creating process (ignored here) + * level number for current level of save file + * name of save file nethack would have created + * and game state + */ + gfd = open_levelfile(0, errbuf); + if (gfd < 0) { + raw_printf("%s\n", errbuf); + return FALSE; + } + if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { + raw_printf( +"\nCheckpoint data incompletely written or subsequently clobbered. Recovery impossible."); + (void)close(gfd); + return FALSE; + } + if (read(gfd, (genericptr_t) &savelev, sizeof(savelev)) + != sizeof(savelev)) { + raw_printf("\nCheckpointing was not in effect for %s -- recovery impossible.\n", + lock); + (void)close(gfd); + return FALSE; + } + if ((read(gfd, (genericptr_t) savename, sizeof savename) + != sizeof savename) || + (read(gfd, (genericptr_t) &version_data, sizeof version_data) + != sizeof version_data)) { + raw_printf("\nError reading %s -- can't recover.\n", lock); + (void)close(gfd); + return FALSE; + } + + /* save file should contain: + * version info + * current level (including pets) + * (non-level-based) game state + * other levels + */ + set_savefile_name(); + sfd = create_savefile(); + if (sfd < 0) { + raw_printf("\nCannot recover savefile %s.\n", SAVEF); + (void)close(gfd); + return FALSE; + } + + lfd = open_levelfile(savelev, errbuf); + if (lfd < 0) { + raw_printf("\n%s\n", errbuf); + (void)close(gfd); + (void)close(sfd); + delete_savefile(); + return FALSE; + } + + if (write(sfd, (genericptr_t) &version_data, sizeof version_data) + != sizeof version_data) { + raw_printf("\nError writing %s; recovery failed.", SAVEF); + (void)close(gfd); + (void)close(sfd); + delete_savefile(); + return FALSE; + } + + if (!copy_bytes(lfd, sfd)) { + (void) close(lfd); + (void) close(sfd); + delete_savefile(); + return FALSE; + } + (void)close(lfd); + processed[savelev] = 1; + + if (!copy_bytes(gfd, sfd)) { + (void) close(lfd); + (void) close(sfd); + delete_savefile(); + return FALSE; + } + (void)close(gfd); + processed[0] = 1; + + for (lev = 1; lev < 256; lev++) { + /* level numbers are kept in xchars in save.c, so the + * maximum level number (for the endlevel) must be < 256 + */ + if (lev != savelev) { + lfd = open_levelfile(lev, (char *)0); + if (lfd >= 0) { + /* any or all of these may not exist */ + levc = (xchar) lev; + write(sfd, (genericptr_t) &levc, sizeof(levc)); + if (!copy_bytes(lfd, sfd)) { + (void) close(lfd); + (void) close(sfd); + delete_savefile(); + return FALSE; + } + (void)close(lfd); + processed[lev] = 1; + } + } + } + (void)close(sfd); + +#ifdef HOLD_LOCKFILE_OPEN + really_close(); +#endif + /* + * We have a successful savefile! + * Only now do we erase the level files. + */ + for (lev = 0; lev < 256; lev++) { + if (processed[lev]) { + const char *fq_lock; + set_levelfile_name(lock, lev); + fq_lock = fqname(lock, LEVELPREFIX, 3); + (void) unlink(fq_lock); + } + } + return TRUE; +} + +boolean +copy_bytes(ifd, ofd) +int ifd, ofd; +{ + char buf[BUFSIZ]; + int nfrom, nto; + + do { + nfrom = read(ifd, buf, BUFSIZ); + nto = write(ofd, buf, nfrom); + if (nto != nfrom) return FALSE; + } while (nfrom == BUFSIZ); + return TRUE; +} + +/* ---------- END INTERNAL RECOVER ----------- */ +#endif /*SELF_RECOVER*/ + +/*files.c*/ diff --git a/src/fountain.c b/src/fountain.c new file mode 100644 index 0000000..a1b4249 --- /dev/null +++ b/src/fountain.c @@ -0,0 +1,602 @@ +/* SCCS Id: @(#)fountain.c 3.4 2003/03/23 */ +/* Copyright Scott R. Turner, srt@ucla, 10/27/86 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Code for drinking from fountains. */ + +#include "hack.h" + +STATIC_DCL void NDECL(dowatersnakes); +STATIC_DCL void NDECL(dowaterdemon); +STATIC_DCL void NDECL(dowaternymph); +STATIC_PTR void FDECL(gush, (int,int,genericptr_t)); +STATIC_DCL void NDECL(dofindgem); + +void +floating_above(what) +const char *what; +{ + You("are floating high above the %s.", what); +} + +STATIC_OVL void +dowatersnakes() /* Fountain of snakes! */ +{ + register int num = rn1(5,2); + struct monst *mtmp; + + if (!(mvitals[PM_WATER_MOCCASIN].mvflags & G_GONE)) { + if (!Blind) + pline("An endless stream of %s pours forth!", + Hallucination ? makeplural(rndmonnam()) : "snakes"); + else + You_hear("%s hissing!", something); + while(num-- > 0) + if((mtmp = makemon(&mons[PM_WATER_MOCCASIN], + u.ux, u.uy, NO_MM_FLAGS)) && t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } else + pline_The("fountain bubbles furiously for a moment, then calms."); +} + +STATIC_OVL +void +dowaterdemon() /* Water demon */ +{ + register struct monst *mtmp; + + if(!(mvitals[PM_WATER_DEMON].mvflags & G_GONE)) { + if((mtmp = makemon(&mons[PM_WATER_DEMON],u.ux,u.uy, NO_MM_FLAGS))) { + if (!Blind) + You("unleash %s!", a_monnam(mtmp)); + else + You_feel("the presence of evil."); + + /* Give those on low levels a (slightly) better chance of survival */ + if (rnd(100) > (80 + level_difficulty())) { + pline("Grateful for %s release, %s grants you a wish!", + mhis(mtmp), mhe(mtmp)); + makewish(); + mongone(mtmp); + } else if (t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } + } else + pline_The("fountain bubbles furiously for a moment, then calms."); +} + +STATIC_OVL void +dowaternymph() /* Water Nymph */ +{ + register struct monst *mtmp; + + if(!(mvitals[PM_WATER_NYMPH].mvflags & G_GONE) && + (mtmp = makemon(&mons[PM_WATER_NYMPH],u.ux,u.uy, NO_MM_FLAGS))) { + if (!Blind) + You("attract %s!", a_monnam(mtmp)); + else + You_hear("a seductive voice."); + mtmp->msleeping = 0; + if (t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } else + if (!Blind) + pline("A large bubble rises to the surface and pops."); + else + You_hear("a loud pop."); +} + +void +dogushforth(drinking) /* Gushing forth along LOS from (u.ux, u.uy) */ +int drinking; +{ + int madepool = 0; + + do_clear_area(u.ux, u.uy, 7, gush, (genericptr_t)&madepool); + if (!madepool) { + if (drinking) + Your("thirst is quenched."); + else + pline("Water sprays all over you."); + } +} + +STATIC_PTR void +gush(x, y, poolcnt) +int x, y; +genericptr_t poolcnt; +{ + register struct monst *mtmp; + register struct trap *ttmp; + + if (((x+y)%2) || (x == u.ux && y == u.uy) || + (rn2(1 + distmin(u.ux, u.uy, x, y))) || + (levl[x][y].typ != ROOM) || + (sobj_at(BOULDER, x, y)) || nexttodoor(x, y)) + return; + + if ((ttmp = t_at(x, y)) != 0 && !delfloortrap(ttmp)) + return; + + if (!((*(int *)poolcnt)++)) + pline("Water gushes forth from the overflowing fountain!"); + + /* Put a pool at x, y */ + levl[x][y].typ = POOL; + /* No kelp! */ + del_engr_at(x, y); + water_damage(level.objects[x][y], FALSE, TRUE); + + if ((mtmp = m_at(x, y)) != 0) + (void) minliquid(mtmp); + else + newsym(x,y); +} + +STATIC_OVL void +dofindgem() /* Find a gem in the sparkling waters. */ +{ + if (!Blind) You("spot a gem in the sparkling waters!"); + else You_feel("a gem here!"); + (void) mksobj_at(rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE-1), + u.ux, u.uy, FALSE, FALSE); + SET_FOUNTAIN_LOOTED(u.ux,u.uy); + newsym(u.ux, u.uy); + exercise(A_WIS, TRUE); /* a discovery! */ +} + +void +dryup(x, y, isyou) +xchar x, y; +boolean isyou; +{ + if (IS_FOUNTAIN(levl[x][y].typ) && + (!rn2(3) || FOUNTAIN_IS_WARNED(x,y))) { + if(isyou && in_town(x, y) && !FOUNTAIN_IS_WARNED(x,y)) { + struct monst *mtmp; + SET_FOUNTAIN_WARNED(x,y); + /* Warn about future fountain use. */ + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mpeaceful) { + pline("%s yells:", Amonnam(mtmp)); + verbalize("Hey, stop using that fountain!"); + break; + } + } + /* You can see or hear this effect */ + if(!mtmp) pline_The("flow reduces to a trickle."); + return; + } +#ifdef WIZARD + if (isyou && wizard) { + if (yn("Dry up fountain?") == 'n') + return; + } +#endif + /* replace the fountain with ordinary floor */ + levl[x][y].typ = ROOM; + levl[x][y].looted = 0; + levl[x][y].blessedftn = 0; + if (cansee(x,y)) pline_The("fountain dries up!"); + /* The location is seen if the hero/monster is invisible */ + /* or felt if the hero is blind. */ + newsym(x, y); + level.flags.nfountains--; + if(isyou && in_town(x, y)) + (void) angry_guards(FALSE); + } +} + +void +drinkfountain() +{ + /* What happens when you drink from a fountain? */ + register boolean mgkftn = (levl[u.ux][u.uy].blessedftn == 1); + register int fate = rnd(30); + + if (Levitation) { + floating_above("fountain"); + return; + } + + if (mgkftn && u.uluck >= 0 && fate >= 10) { + int i, ii, littleluck = (u.uluck < 4); + + pline("Wow! This makes you feel great!"); + /* blessed restore ability */ + for (ii = 0; ii < A_MAX; ii++) + if (ABASE(ii) < AMAX(ii)) { + ABASE(ii) = AMAX(ii); + flags.botl = 1; + } + /* gain ability, blessed if "natural" luck is high */ + i = rn2(A_MAX); /* start at a random attribute */ + for (ii = 0; ii < A_MAX; ii++) { + if (adjattrib(i, 1, littleluck ? -1 : 0) && littleluck) + break; + if (++i >= A_MAX) i = 0; + } + display_nhwindow(WIN_MESSAGE, FALSE); + pline("A wisp of vapor escapes the fountain..."); + exercise(A_WIS, TRUE); + levl[u.ux][u.uy].blessedftn = 0; + return; + } + + if (fate < 10) { + pline_The("cool draught refreshes you."); + u.uhunger += rnd(10); /* don't choke on water */ + newuhs(FALSE); + if(mgkftn) return; + } else { + switch (fate) { + + case 19: /* Self-knowledge */ + + You_feel("self-knowledgeable..."); + display_nhwindow(WIN_MESSAGE, FALSE); + enlightenment(0); + exercise(A_WIS, TRUE); + pline_The("feeling subsides."); + break; + + case 20: /* Foul water */ + + pline_The("water is foul! You gag and vomit."); + morehungry(rn1(20, 11)); + vomit(); + break; + + case 21: /* Poisonous */ + + pline_The("water is contaminated!"); + if (Poison_resistance) { + pline( + "Perhaps it is runoff from the nearby %s farm.", + fruitname(FALSE)); + losehp(rnd(4),"unrefrigerated sip of juice", + KILLED_BY_AN); + break; + } + losestr(rn1(4,3)); + losehp(rnd(10),"contaminated water", KILLED_BY); + exercise(A_CON, FALSE); + break; + + case 22: /* Fountain of snakes! */ + + dowatersnakes(); + break; + + case 23: /* Water demon */ + dowaterdemon(); + break; + + case 24: /* Curse an item */ { + register struct obj *obj; + + pline("This water's no good!"); + morehungry(rn1(20, 11)); + exercise(A_CON, FALSE); + for(obj = invent; obj ; obj = obj->nobj) + if (!rn2(5)) curse(obj); + break; + } + + case 25: /* See invisible */ + + if (Blind) { + if (Invisible) { + You("feel transparent."); + } else { + You("feel very self-conscious."); + pline("Then it passes."); + } + } else { + You("see an image of someone stalking you."); + pline("But it disappears."); + } + HSee_invisible |= FROMOUTSIDE; + newsym(u.ux,u.uy); + exercise(A_WIS, TRUE); + break; + + case 26: /* See Monsters */ + + (void) monster_detect((struct obj *)0, 0); + exercise(A_WIS, TRUE); + break; + + case 27: /* Find a gem in the sparkling waters. */ + + if (!FOUNTAIN_IS_LOOTED(u.ux,u.uy)) { + dofindgem(); + break; + } + + case 28: /* Water Nymph */ + + dowaternymph(); + break; + + case 29: /* Scare */ { + register struct monst *mtmp; + + pline("This water gives you bad breath!"); + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(!DEADMONSTER(mtmp)) + monflee(mtmp, 0, FALSE, FALSE); + } + break; + + case 30: /* Gushing forth in this room */ + + dogushforth(TRUE); + break; + + default: + + pline("This tepid water is tasteless."); + break; + } + } + dryup(u.ux, u.uy, TRUE); +} + +void +dipfountain(obj) +register struct obj *obj; +{ + if (Levitation) { + floating_above("fountain"); + return; + } + + /* Don't grant Excalibur when there's more than one object. */ + /* (quantity could be > 1 if merged daggers got polymorphed) */ + if (obj->otyp == LONG_SWORD && obj->quan == 1L + && u.ulevel >= 5 && !rn2(6) + && !obj->oartifact + && !exist_artifact(LONG_SWORD, artiname(ART_EXCALIBUR))) { + + if (u.ualign.type != A_LAWFUL) { + /* Ha! Trying to cheat her. */ + pline("A freezing mist rises from the water and envelopes the sword."); + pline_The("fountain disappears!"); + curse(obj); + if (obj->spe > -6 && !rn2(3)) obj->spe--; + obj->oerodeproof = FALSE; + exercise(A_WIS, FALSE); + } else { + /* The lady of the lake acts! - Eric Backus */ + /* Be *REAL* nice */ + pline("From the murky depths, a hand reaches up to bless the sword."); + pline("As the hand retreats, the fountain disappears!"); + obj = oname(obj, artiname(ART_EXCALIBUR)); + discover_artifact(ART_EXCALIBUR); + bless(obj); + obj->oeroded = obj->oeroded2 = 0; + obj->oerodeproof = TRUE; + exercise(A_WIS, TRUE); + } + update_inventory(); + levl[u.ux][u.uy].typ = ROOM; + levl[u.ux][u.uy].looted = 0; + newsym(u.ux, u.uy); + level.flags.nfountains--; + if(in_town(u.ux, u.uy)) + (void) angry_guards(FALSE); + return; + } else if (get_wet(obj) && !rn2(2)) + return; + + /* Acid and water don't mix */ + if (obj->otyp == POT_ACID) { + useup(obj); + return; + } + + switch (rnd(30)) { + case 16: /* Curse the item */ + curse(obj); + break; + case 17: + case 18: + case 19: + case 20: /* Uncurse the item */ + if(obj->cursed) { + if (!Blind) + pline_The("water glows for a moment."); + uncurse(obj); + } else { + pline("A feeling of loss comes over you."); + } + break; + case 21: /* Water Demon */ + dowaterdemon(); + break; + case 22: /* Water Nymph */ + dowaternymph(); + break; + case 23: /* an Endless Stream of Snakes */ + dowatersnakes(); + break; + case 24: /* Find a gem */ + if (!FOUNTAIN_IS_LOOTED(u.ux,u.uy)) { + dofindgem(); + break; + } + case 25: /* Water gushes forth */ + dogushforth(FALSE); + break; + case 26: /* Strange feeling */ + pline("A strange tingling runs up your %s.", + body_part(ARM)); + break; + case 27: /* Strange feeling */ + You_feel("a sudden chill."); + break; + case 28: /* Strange feeling */ + pline("An urge to take a bath overwhelms you."); +#ifndef GOLDOBJ + if (u.ugold > 10) { + u.ugold -= somegold() / 10; + You("lost some of your gold in the fountain!"); + CLEAR_FOUNTAIN_LOOTED(u.ux,u.uy); + exercise(A_WIS, FALSE); + } +#else + { + long money = money_cnt(invent); + struct obj *otmp; + if (money > 10) { + /* Amount to loose. Might get rounded up as fountains don't pay change... */ + money = somegold(money) / 10; + for (otmp = invent; otmp && money > 0; otmp = otmp->nobj) if (otmp->oclass == COIN_CLASS) { + int denomination = objects[otmp->otyp].oc_cost; + long coin_loss = (money + denomination - 1) / denomination; + coin_loss = min(coin_loss, otmp->quan); + otmp->quan -= coin_loss; + money -= coin_loss * denomination; + if (!otmp->quan) delobj(otmp); + } + You("lost some of your money in the fountain!"); + CLEAR_FOUNTAIN_LOOTED(u.ux,u.uy); + exercise(A_WIS, FALSE); + } + } +#endif + break; + case 29: /* You see coins */ + + /* We make fountains have more coins the closer you are to the + * surface. After all, there will have been more people going + * by. Just like a shopping mall! Chris Woodbury */ + + if (FOUNTAIN_IS_LOOTED(u.ux,u.uy)) break; + SET_FOUNTAIN_LOOTED(u.ux,u.uy); + (void) mkgold((long) + (rnd((dunlevs_in_dungeon(&u.uz)-dunlev(&u.uz)+1)*2)+5), + u.ux, u.uy); + if (!Blind) + pline("Far below you, you see coins glistening in the water."); + exercise(A_WIS, TRUE); + newsym(u.ux,u.uy); + break; + } + update_inventory(); + dryup(u.ux, u.uy, TRUE); +} + +#ifdef SINKS +void +breaksink(x,y) +int x, y; +{ + if(cansee(x,y) || (x == u.ux && y == u.uy)) + pline_The("pipes break! Water spurts out!"); + level.flags.nsinks--; + levl[x][y].doormask = 0; + levl[x][y].typ = FOUNTAIN; + level.flags.nfountains++; + newsym(x,y); +} + +void +drinksink() +{ + struct obj *otmp; + struct monst *mtmp; + + if (Levitation) { + floating_above("sink"); + return; + } + switch(rn2(20)) { + case 0: You("take a sip of very cold water."); + break; + case 1: You("take a sip of very warm water."); + break; + case 2: You("take a sip of scalding hot water."); + if (Fire_resistance) + pline("It seems quite tasty."); + else losehp(rnd(6), "sipping boiling water", KILLED_BY); + break; + case 3: if (mvitals[PM_SEWER_RAT].mvflags & G_GONE) + pline_The("sink seems quite dirty."); + else { + mtmp = makemon(&mons[PM_SEWER_RAT], + u.ux, u.uy, NO_MM_FLAGS); + if (mtmp) pline("Eek! There's %s in the sink!", + (Blind || !canspotmon(mtmp)) ? + "something squirmy" : + a_monnam(mtmp)); + } + break; + case 4: do { + otmp = mkobj(POTION_CLASS,FALSE); + if (otmp->otyp == POT_WATER) { + obfree(otmp, (struct obj *)0); + otmp = (struct obj *) 0; + } + } while(!otmp); + otmp->cursed = otmp->blessed = 0; + pline("Some %s liquid flows from the faucet.", + Blind ? "odd" : + hcolor(OBJ_DESCR(objects[otmp->otyp]))); + otmp->dknown = !(Blind || Hallucination); + otmp->quan++; /* Avoid panic upon useup() */ + otmp->fromsink = 1; /* kludge for docall() */ + (void) dopotion(otmp); + obfree(otmp, (struct obj *)0); + break; + case 5: if (!(levl[u.ux][u.uy].looted & S_LRING)) { + You("find a ring in the sink!"); + (void) mkobj_at(RING_CLASS, u.ux, u.uy, TRUE); + levl[u.ux][u.uy].looted |= S_LRING; + exercise(A_WIS, TRUE); + newsym(u.ux,u.uy); + } else pline("Some dirty water backs up in the drain."); + break; + case 6: breaksink(u.ux,u.uy); + break; + case 7: pline_The("water moves as though of its own will!"); + if ((mvitals[PM_WATER_ELEMENTAL].mvflags & G_GONE) + || !makemon(&mons[PM_WATER_ELEMENTAL], + u.ux, u.uy, NO_MM_FLAGS)) + pline("But it quiets down."); + break; + case 8: pline("Yuk, this water tastes awful."); + more_experienced(1,0); + newexplevel(); + break; + case 9: pline("Gaggg... this tastes like sewage! You vomit."); + morehungry(rn1(30-ACURR(A_CON), 11)); + vomit(); + break; + case 10: pline("This water contains toxic wastes!"); + if (!Unchanging) { + You("undergo a freakish metamorphosis!"); + polyself(FALSE); + } + break; + /* more odd messages --JJB */ + case 11: You_hear("clanking from the pipes..."); + break; + case 12: You_hear("snatches of song from among the sewers..."); + break; + case 19: if (Hallucination) { + pline("From the murky drain, a hand reaches up... --oops--"); + break; + } + default: You("take a sip of %s water.", + rn2(3) ? (rn2(2) ? "cold" : "warm") : "hot"); + } +} +#endif /* SINKS */ + +/*fountain.c*/ diff --git a/src/hack.c b/src/hack.c new file mode 100644 index 0000000..0e89c3b --- /dev/null +++ b/src/hack.c @@ -0,0 +1,2303 @@ +/* SCCS Id: @(#)hack.c 3.4 2003/04/30 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVL1 +STATIC_DCL void NDECL(maybe_wail); +#endif /*OVL1*/ +STATIC_DCL int NDECL(moverock); +STATIC_DCL int FDECL(still_chewing,(XCHAR_P,XCHAR_P)); +#ifdef SINKS +STATIC_DCL void NDECL(dosinkfall); +#endif +STATIC_DCL boolean FDECL(findtravelpath, (BOOLEAN_P)); +STATIC_DCL boolean FDECL(monstinroom, (struct permonst *,int)); + +STATIC_DCL void FDECL(move_update, (BOOLEAN_P)); + +#define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE) + +#ifdef OVL2 + +boolean +revive_nasty(x, y, msg) +int x,y; +const char *msg; +{ + register struct obj *otmp, *otmp2; + struct monst *mtmp; + coord cc; + boolean revived = FALSE; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if (otmp->otyp == CORPSE && + (is_rider(&mons[otmp->corpsenm]) || + otmp->corpsenm == PM_WIZARD_OF_YENDOR)) { + /* move any living monster already at that location */ + if((mtmp = m_at(x,y)) && enexto(&cc, x, y, mtmp->data)) + rloc_to(mtmp, cc.x, cc.y); + if(msg) Norep("%s", msg); + revived = revive_corpse(otmp); + } + } + + /* this location might not be safe, if not, move revived monster */ + if (revived) { + mtmp = m_at(x,y); + if (mtmp && !goodpos(x, y, mtmp, 0) && + enexto(&cc, x, y, mtmp->data)) { + rloc_to(mtmp, cc.x, cc.y); + } + /* else impossible? */ + } + + return (revived); +} + +STATIC_OVL int +moverock() +{ + register xchar rx, ry, sx, sy; + register struct obj *otmp; + register struct trap *ttmp; + register struct monst *mtmp; + + sx = u.ux + u.dx, sy = u.uy + u.dy; /* boulder starting position */ + while ((otmp = sobj_at(BOULDER, sx, sy)) != 0) { + /* make sure that this boulder is visible as the top object */ + if (otmp != level.objects[sx][sy]) movobj(otmp, sx, sy); + + rx = u.ux + 2 * u.dx; /* boulder destination position */ + ry = u.uy + 2 * u.dy; + nomul(0); + if (Levitation || Is_airlevel(&u.uz)) { + if (Blind) feel_location(sx, sy); + You("don't have enough leverage to push %s.", the(xname(otmp))); + /* Give them a chance to climb over it? */ + return -1; + } + if (verysmall(youmonst.data) +#ifdef STEED + && !u.usteed +#endif + ) { + if (Blind) feel_location(sx, sy); + pline("You're too small to push that %s.", xname(otmp)); + goto cannot_push; + } + if (isok(rx,ry) && !IS_ROCK(levl[rx][ry].typ) && + levl[rx][ry].typ != IRONBARS && + (!IS_DOOR(levl[rx][ry].typ) || !(u.dx && u.dy) || ( +#ifdef REINCARNATION + !Is_rogue_level(&u.uz) && +#endif + (levl[rx][ry].doormask & ~D_BROKEN) == D_NODOOR)) && + !sobj_at(BOULDER, rx, ry)) { + ttmp = t_at(rx, ry); + mtmp = m_at(rx, ry); + + /* KMH -- Sokoban doesn't let you push boulders diagonally */ + if (In_sokoban(&u.uz) && u.dx && u.dy) { + if (Blind) feel_location(sx,sy); + pline("%s won't roll diagonally on this %s.", + The(xname(otmp)), surface(sx, sy)); + goto cannot_push; + } + + if (revive_nasty(rx, ry, "You sense movement on the other side.")) + return (-1); + + if (mtmp && !noncorporeal(mtmp->data) && + (!mtmp->mtrapped || + !(ttmp && ((ttmp->ttyp == PIT) || + (ttmp->ttyp == SPIKED_PIT))))) { + if (Blind) feel_location(sx, sy); + if (canspotmon(mtmp)) + pline("There's %s on the other side.", a_monnam(mtmp)); + else { + You_hear("a monster behind %s.", the(xname(otmp))); + map_invisible(rx, ry); + } + if (flags.verbose) + pline("Perhaps that's why %s cannot move it.", +#ifdef STEED + u.usteed ? y_monnam(u.usteed) : +#endif + "you"); + goto cannot_push; + } + + if (ttmp) + switch(ttmp->ttyp) { + case LANDMINE: + if (rn2(10)) { + obj_extract_self(otmp); + place_object(otmp, rx, ry); + unblock_point(sx, sy); + newsym(sx, sy); + pline("KAABLAMM!!! %s %s land mine.", + Tobjnam(otmp, "trigger"), + ttmp->madeby_u ? "your" : "a"); + blow_up_landmine(ttmp); + /* if the boulder remains, it should fill the pit */ + fill_pit(u.ux, u.uy); + if (cansee(rx,ry)) newsym(rx,ry); + continue; + } + break; + case SPIKED_PIT: + case PIT: + obj_extract_self(otmp); + /* vision kludge to get messages right; + the pit will temporarily be seen even + if this is one among multiple boulders */ + if (!Blind) viz_array[ry][rx] |= IN_SIGHT; + if (!flooreffects(otmp, rx, ry, "fall")) { + place_object(otmp, rx, ry); + } + if (mtmp && !Blind) newsym(rx, ry); + continue; + case HOLE: + case TRAPDOOR: + if (Blind) + pline("Kerplunk! You no longer feel %s.", + the(xname(otmp))); + else + pline("%s%s and %s a %s in the %s!", + Tobjnam(otmp, + (ttmp->ttyp == TRAPDOOR) ? "trigger" : "fall"), + (ttmp->ttyp == TRAPDOOR) ? nul : " into", + otense(otmp, "plug"), + (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole", + surface(rx, ry)); + deltrap(ttmp); + delobj(otmp); + bury_objs(rx, ry); + if (cansee(rx,ry)) newsym(rx,ry); + continue; + case LEVEL_TELEP: + case TELEP_TRAP: +#ifdef STEED + if (u.usteed) + pline("%s pushes %s and suddenly it disappears!", + upstart(y_monnam(u.usteed)), the(xname(otmp))); + else +#endif + You("push %s and suddenly it disappears!", + the(xname(otmp))); + if (ttmp->ttyp == TELEP_TRAP) + rloco(otmp); + else { + int newlev = random_teleport_level(); + d_level dest; + + if (newlev == depth(&u.uz) || In_endgame(&u.uz)) + continue; + obj_extract_self(otmp); + add_to_migration(otmp); + get_level(&dest, newlev); + otmp->ox = dest.dnum; + otmp->oy = dest.dlevel; + otmp->owornmask = (long)MIGR_RANDOM; + } + seetrap(ttmp); + continue; + } + if (closed_door(rx, ry)) + goto nopushmsg; + if (boulder_hits_pool(otmp, rx, ry, TRUE)) + continue; + /* + * Re-link at top of fobj chain so that pile order is preserved + * when level is restored. + */ + if (otmp != fobj) { + remove_object(otmp); + place_object(otmp, otmp->ox, otmp->oy); + } + + { +#ifdef LINT /* static long lastmovetime; */ + long lastmovetime; + lastmovetime = 0; +#else + /* note: reset to zero after save/restore cycle */ + static NEARDATA long lastmovetime; +#endif +#ifdef STEED + if (!u.usteed) { +#endif + if (moves > lastmovetime+2 || moves < lastmovetime) + pline("With %s effort you move %s.", + throws_rocks(youmonst.data) ? "little" : "great", + the(xname(otmp))); + exercise(A_STR, TRUE); +#ifdef STEED + } else + pline("%s moves %s.", + upstart(y_monnam(u.usteed)), the(xname(otmp))); +#endif + lastmovetime = moves; + } + + /* Move the boulder *after* the message. */ + if (glyph_is_invisible(levl[rx][ry].glyph)) + unmap_object(rx, ry); + movobj(otmp, rx, ry); /* does newsym(rx,ry) */ + if (Blind) { + feel_location(rx,ry); + feel_location(sx, sy); + } else { + newsym(sx, sy); + } + } else { + nopushmsg: +#ifdef STEED + if (u.usteed) + pline("%s tries to move %s, but cannot.", + upstart(y_monnam(u.usteed)), the(xname(otmp))); + else +#endif + You("try to move %s, but in vain.", the(xname(otmp))); + if (Blind) feel_location(sx, sy); + cannot_push: + if (throws_rocks(youmonst.data)) { +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { + You("aren't skilled enough to %s %s from %s.", + (flags.pickup && !In_sokoban(&u.uz)) + ? "pick up" : "push aside", + the(xname(otmp)), y_monnam(u.usteed)); + } else +#endif + { + pline("However, you can easily %s.", + (flags.pickup && !In_sokoban(&u.uz)) + ? "pick it up" : "push it aside"); + if (In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + break; + } + break; + } + + if ( +#ifdef STEED + !u.usteed && +#endif + (((!invent || inv_weight() <= -850) && + (!u.dx || !u.dy || (IS_ROCK(levl[u.ux][sy].typ) + && IS_ROCK(levl[sx][u.uy].typ)))) + || verysmall(youmonst.data))) { + pline("However, you can squeeze yourself into a small opening."); + if (In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + break; + } else + return (-1); + } + } + return (0); +} + +/* + * still_chewing() + * + * Chew on a wall, door, or boulder. Returns TRUE if still eating, FALSE + * when done. + */ +STATIC_OVL int +still_chewing(x,y) + xchar x, y; +{ + struct rm *lev = &levl[x][y]; + struct obj *boulder = sobj_at(BOULDER,x,y); + const char *digtxt = (char *)0, *dmgtxt = (char *)0; + + if (digging.down) /* not continuing previous dig (w/ pick-axe) */ + (void) memset((genericptr_t)&digging, 0, sizeof digging); + + if (!boulder && IS_ROCK(lev->typ) && !may_dig(x,y)) { + You("hurt your teeth on the %s.", + IS_TREE(lev->typ) ? "tree" : "hard stone"); + nomul(0); + return 1; + } else if (digging.pos.x != x || digging.pos.y != y || + !on_level(&digging.level, &u.uz)) { + digging.down = FALSE; + digging.chew = TRUE; + digging.warned = FALSE; + digging.pos.x = x; + digging.pos.y = y; + assign_level(&digging.level, &u.uz); + /* solid rock takes more work & time to dig through */ + digging.effort = + (IS_ROCK(lev->typ) && !IS_TREE(lev->typ) ? 30 : 60) + u.udaminc; + You("start chewing %s %s.", + (boulder || IS_TREE(lev->typ)) ? "on a" : "a hole in the", + boulder ? "boulder" : + IS_TREE(lev->typ) ? "tree" : IS_ROCK(lev->typ) ? "rock" : "door"); + watch_dig((struct monst *)0, x, y, FALSE); + return 1; + } else if ((digging.effort += (30 + u.udaminc)) <= 100) { + if (flags.verbose) + You("%s chewing on the %s.", + digging.chew ? "continue" : "begin", + boulder ? "boulder" : + IS_TREE(lev->typ) ? "tree" : + IS_ROCK(lev->typ) ? "rock" : "door"); + digging.chew = TRUE; + watch_dig((struct monst *)0, x, y, FALSE); + return 1; + } + + /* Okay, you've chewed through something */ + u.uconduct.food++; + u.uhunger += rnd(20); + + if (boulder) { + delobj(boulder); /* boulder goes bye-bye */ + You("eat the boulder."); /* yum */ + + /* + * The location could still block because of + * 1. More than one boulder + * 2. Boulder stuck in a wall/stone/door. + * + * [perhaps use does_block() below (from vision.c)] + */ + if (IS_ROCK(lev->typ) || closed_door(x,y) || sobj_at(BOULDER,x,y)) { + block_point(x,y); /* delobj will unblock the point */ + /* reset dig state */ + (void) memset((genericptr_t)&digging, 0, sizeof digging); + return 1; + } + + } else if (IS_WALL(lev->typ)) { + if (*in_rooms(x, y, SHOPBASE)) { + add_damage(x, y, 10L * ACURRSTR); + dmgtxt = "damage"; + } + digtxt = "chew a hole in the wall."; + if (level.flags.is_maze_lev) { + lev->typ = ROOM; + } else if (level.flags.is_cavernous_lev && !in_town(x, y)) { + lev->typ = CORR; + } else { + lev->typ = DOOR; + lev->doormask = D_NODOOR; + } + } else if (IS_TREE(lev->typ)) { + digtxt = "chew through the tree."; + lev->typ = ROOM; + } else if (lev->typ == SDOOR) { + if (lev->doormask & D_TRAPPED) { + lev->doormask = D_NODOOR; + b_trapped("secret door", 0); + } else { + digtxt = "chew through the secret door."; + lev->doormask = D_BROKEN; + } + lev->typ = DOOR; + + } else if (IS_DOOR(lev->typ)) { + if (*in_rooms(x, y, SHOPBASE)) { + add_damage(x, y, 400L); + dmgtxt = "break"; + } + if (lev->doormask & D_TRAPPED) { + lev->doormask = D_NODOOR; + b_trapped("door", 0); + } else { + digtxt = "chew through the door."; + lev->doormask = D_BROKEN; + } + + } else { /* STONE or SCORR */ + digtxt = "chew a passage through the rock."; + lev->typ = CORR; + } + + unblock_point(x, y); /* vision */ + newsym(x, y); + if (digtxt) You(digtxt); /* after newsym */ + if (dmgtxt) pay_for_damage(dmgtxt, FALSE); + (void) memset((genericptr_t)&digging, 0, sizeof digging); + return 0; +} + +#endif /* OVL2 */ +#ifdef OVLB + +void +movobj(obj, ox, oy) +register struct obj *obj; +register xchar ox, oy; +{ + /* optimize by leaving on the fobj chain? */ + remove_object(obj); + newsym(obj->ox, obj->oy); + place_object(obj, ox, oy); + newsym(ox, oy); +} + +#ifdef SINKS +static NEARDATA const char fell_on_sink[] = "fell onto a sink"; + +STATIC_OVL void +dosinkfall() +{ + register struct obj *obj; + + if (is_floater(youmonst.data) || (HLevitation & FROMOUTSIDE)) { + You("wobble unsteadily for a moment."); + } else { + long save_ELev = ELevitation, save_HLev = HLevitation; + + /* fake removal of levitation in advance so that final + disclosure will be right in case this turns out to + be fatal; fortunately the fact that rings and boots + are really still worn has no effect on bones data */ + ELevitation = HLevitation = 0L; + You("crash to the floor!"); + losehp(rn1(8, 25 - (int)ACURR(A_CON)), + fell_on_sink, NO_KILLER_PREFIX); + exercise(A_DEX, FALSE); + selftouch("Falling, you"); + for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) + if (obj->oclass == WEAPON_CLASS || is_weptool(obj)) { + You("fell on %s.", doname(obj)); + losehp(rnd(3), fell_on_sink, NO_KILLER_PREFIX); + exercise(A_CON, FALSE); + } + ELevitation = save_ELev; + HLevitation = save_HLev; + } + + ELevitation &= ~W_ARTI; + HLevitation &= ~(I_SPECIAL|TIMEOUT); + HLevitation++; + if(uleft && uleft->otyp == RIN_LEVITATION) { + obj = uleft; + Ring_off(obj); + off_msg(obj); + } + if(uright && uright->otyp == RIN_LEVITATION) { + obj = uright; + Ring_off(obj); + off_msg(obj); + } + if(uarmf && uarmf->otyp == LEVITATION_BOOTS) { + obj = uarmf; + (void)Boots_off(); + off_msg(obj); + } + HLevitation--; +} +#endif + +boolean +may_dig(x,y) +register xchar x,y; +/* intended to be called only on ROCKs */ +{ + return (boolean)(!(IS_STWALL(levl[x][y].typ) && + (levl[x][y].wall_info & W_NONDIGGABLE))); +} + +boolean +may_passwall(x,y) +register xchar x,y; +{ + return (boolean)(!(IS_STWALL(levl[x][y].typ) && + (levl[x][y].wall_info & W_NONPASSWALL))); +} + +#endif /* OVLB */ +#ifdef OVL1 + +boolean +bad_rock(mdat,x,y) +struct permonst *mdat; +register xchar x,y; +{ + return((boolean) ((In_sokoban(&u.uz) && sobj_at(BOULDER,x,y)) || + (IS_ROCK(levl[x][y].typ) + && (!tunnels(mdat) || needspick(mdat) || !may_dig(x,y)) + && !(passes_walls(mdat) && may_passwall(x,y))))); +} + +boolean +invocation_pos(x, y) +xchar x, y; +{ + return((boolean)(Invocation_lev(&u.uz) && x == inv_pos.x && y == inv_pos.y)); +} + +#endif /* OVL1 */ +#ifdef OVL3 + +/* return TRUE if (dx,dy) is an OK place to move + * mode is one of DO_MOVE, TEST_MOVE or TEST_TRAV + */ +boolean +test_move(ux, uy, dx, dy, mode) +int ux, uy, dx, dy; +int mode; +{ + int x = ux+dx; + int y = uy+dy; + register struct rm *tmpr = &levl[x][y]; + register struct rm *ust; + + /* + * Check for physical obstacles. First, the place we are going. + */ + if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) { + if (Blind && mode == DO_MOVE) feel_location(x,y); + if (Passes_walls && may_passwall(x,y)) { + ; /* do nothing */ + } else if (tmpr->typ == IRONBARS) { + if (!(Passes_walls || passes_bars(youmonst.data))) + return FALSE; + } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) { + /* Eat the rock. */ + if (mode == DO_MOVE && still_chewing(x,y)) return FALSE; + } else if (flags.autodig && !flags.run && !flags.nopick && + uwep && is_pick(uwep)) { + /* MRKR: Automatic digging when wielding the appropriate tool */ + if (mode == DO_MOVE) + (void) use_pick_axe2(uwep); + return FALSE; + } else { + if (mode == DO_MOVE) { + if (Is_stronghold(&u.uz) && is_db_wall(x,y)) + pline_The("drawbridge is up!"); + if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz)) + pline_The("Sokoban walls resist your ability."); + } + return FALSE; + } + } else if (IS_DOOR(tmpr->typ)) { + if (closed_door(x,y)) { + if (Blind && mode == DO_MOVE) feel_location(x,y); + if (Passes_walls) + ; /* do nothing */ + else if (can_ooze(&youmonst)) { + if (mode == DO_MOVE) You("ooze under the door."); + } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) { + /* Eat the door. */ + if (mode == DO_MOVE && still_chewing(x,y)) return FALSE; + } else { + if (mode == DO_MOVE) { + if (amorphous(youmonst.data)) + You("try to ooze under the door, but can't squeeze your possessions through."); + else if (x == ux || y == uy) { + if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) { +#ifdef STEED + if (u.usteed) { + You_cant("lead %s through that closed door.", + y_monnam(u.usteed)); + } else +#endif + { + pline("Ouch! You bump into a door."); + exercise(A_DEX, FALSE); + } + } else pline("That door is closed."); + } + } else if (mode == TEST_TRAV) goto testdiag; + return FALSE; + } + } else { + testdiag: + if (dx && dy && !Passes_walls + && ((tmpr->doormask & ~D_BROKEN) +#ifdef REINCARNATION + || Is_rogue_level(&u.uz) +#endif + || block_door(x,y))) { + /* Diagonal moves into a door are not allowed. */ + if (Blind && mode == DO_MOVE) + feel_location(x,y); + return FALSE; + } + } + } + if (dx && dy + && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) { + /* Move at a diagonal. */ + if (In_sokoban(&u.uz)) { + if (mode == DO_MOVE) + You("cannot pass that way."); + return FALSE; + } + if (bigmonst(youmonst.data)) { + if (mode == DO_MOVE) + Your("body is too large to fit through."); + return FALSE; + } + if (invent && (inv_weight() + weight_cap() > 600)) { + if (mode == DO_MOVE) + You("are carrying too much to get through."); + return FALSE; + } + } + /* Pick travel path that does not require crossing a trap. + * Avoid water and lava using the usual running rules. + * (but not u.ux/u.uy because findtravelpath walks toward u.ux/u.uy) */ + if (flags.run == 8 && mode != DO_MOVE && (x != u.ux || y != u.uy)) { + struct trap* t = t_at(x, y); + + if ((t && t->tseen) || + (!Levitation && !Flying && + !is_clinger(youmonst.data) && + (is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) + return FALSE; + } + + ust = &levl[ux][uy]; + + /* Now see if other things block our way . . */ + if (dx && dy && !Passes_walls + && (IS_DOOR(ust->typ) && ((ust->doormask & ~D_BROKEN) +#ifdef REINCARNATION + || Is_rogue_level(&u.uz) +#endif + || block_entry(x, y)) + )) { + /* Can't move at a diagonal out of a doorway with door. */ + return FALSE; + } + + if (sobj_at(BOULDER,x,y) && (In_sokoban(&u.uz) || !Passes_walls)) { + if (!(Blind || Hallucination) && (flags.run >= 2) && mode != TEST_TRAV) + return FALSE; + if (mode == DO_MOVE) { + /* tunneling monsters will chew before pushing */ + if (tunnels(youmonst.data) && !needspick(youmonst.data) && + !In_sokoban(&u.uz)) { + if (still_chewing(x,y)) return FALSE; + } else + if (moverock() < 0) return FALSE; + } else if (mode == TEST_TRAV) { + struct obj* obj; + + /* don't pick two boulders in a row, unless there's a way thru */ + if (sobj_at(BOULDER,ux,uy) && !In_sokoban(&u.uz)) { + if (!Passes_walls && + !(tunnels(youmonst.data) && !needspick(youmonst.data)) && + !carrying(PICK_AXE) && !carrying(DWARVISH_MATTOCK) && + !((obj = carrying(WAN_DIGGING)) && + !objects[obj->otyp].oc_name_known)) + return FALSE; + } + } + /* assume you'll be able to push it when you get there... */ + } + + /* OK, it is a legal place to move. */ + return TRUE; +} + +/* + * Find a path from the destination (u.tx,u.ty) back to (u.ux,u.uy). + * A shortest path is returned. If guess is TRUE, consider various + * inaccessible locations as valid intermediate path points. + * Returns TRUE if a path was found. + */ +static boolean +findtravelpath(guess) +boolean guess; +{ + /* if travel to adjacent, reachable location, use normal movement rules */ + if (!guess && iflags.travel1 && distmin(u.ux, u.uy, u.tx, u.ty) == 1) { + flags.run = 0; + if (test_move(u.ux, u.uy, u.tx-u.ux, u.ty-u.uy, TEST_MOVE)) { + u.dx = u.tx-u.ux; + u.dy = u.ty-u.uy; + nomul(0); + iflags.travelcc.x = iflags.travelcc.y = -1; + return TRUE; + } + flags.run = 8; + } + if (u.tx != u.ux || u.ty != u.uy) { + xchar travel[COLNO][ROWNO]; + xchar travelstepx[2][COLNO*ROWNO]; + xchar travelstepy[2][COLNO*ROWNO]; + xchar tx, ty, ux, uy; + int n = 1; /* max offset in travelsteps */ + int set = 0; /* two sets current and previous */ + int radius = 1; /* search radius */ + int i; + + /* If guessing, first find an "obvious" goal location. The obvious + * goal is the position the player knows of, or might figure out + * (couldsee) that is closest to the target on a straight path. + */ + if (guess) { + tx = u.ux; ty = u.uy; ux = u.tx; uy = u.ty; + } else { + tx = u.tx; ty = u.ty; ux = u.ux; uy = u.uy; + } + + noguess: + (void) memset((genericptr_t)travel, 0, sizeof(travel)); + travelstepx[0][0] = tx; + travelstepy[0][0] = ty; + + while (n != 0) { + int nn = 0; + + for (i = 0; i < n; i++) { + int dir; + int x = travelstepx[set][i]; + int y = travelstepy[set][i]; + static int ordered[] = { 0, 2, 4, 6, 1, 3, 5, 7 }; + /* no diagonal movement for grid bugs */ + int dirmax = u.umonnum == PM_GRID_BUG ? 4 : 8; + + for (dir = 0; dir < dirmax; ++dir) { + int nx = x+xdir[ordered[dir]]; + int ny = y+ydir[ordered[dir]]; + + if (!isok(nx, ny)) continue; + if ((!Passes_walls && !can_ooze(&youmonst) && + closed_door(x, y)) || sobj_at(BOULDER, x, y)) { + /* closed doors and boulders usually + * cause a delay, so prefer another path */ + if (travel[x][y] > radius-3) { + travelstepx[1-set][nn] = x; + travelstepy[1-set][nn] = y; + /* don't change travel matrix! */ + nn++; + continue; + } + } + if (test_move(x, y, nx-x, ny-y, TEST_TRAV) && + (levl[nx][ny].seenv || (!Blind && couldsee(nx, ny)))) { + if (nx == ux && ny == uy) { + if (!guess) { + u.dx = x-ux; + u.dy = y-uy; + if (x == u.tx && y == u.ty) { + nomul(0); + /* reset run so domove run checks work */ + flags.run = 8; + iflags.travelcc.x = iflags.travelcc.y = -1; + } + return TRUE; + } + } else if (!travel[nx][ny]) { + travelstepx[1-set][nn] = nx; + travelstepy[1-set][nn] = ny; + travel[nx][ny] = radius; + nn++; + } + } + } + } + + n = nn; + set = 1-set; + radius++; + } + + /* if guessing, find best location in travel matrix and go there */ + if (guess) { + int px = tx, py = ty; /* pick location */ + int dist, nxtdist, d2, nd2; + + dist = distmin(ux, uy, tx, ty); + d2 = dist2(ux, uy, tx, ty); + for (tx = 1; tx < COLNO; ++tx) + for (ty = 0; ty < ROWNO; ++ty) + if (travel[tx][ty]) { + nxtdist = distmin(ux, uy, tx, ty); + if (nxtdist == dist && couldsee(tx, ty)) { + nd2 = dist2(ux, uy, tx, ty); + if (nd2 < d2) { + /* prefer non-zigzag path */ + px = tx; py = ty; + d2 = nd2; + } + } else if (nxtdist < dist && couldsee(tx, ty)) { + px = tx; py = ty; + dist = nxtdist; + d2 = dist2(ux, uy, tx, ty); + } + } + + if (px == u.ux && py == u.uy) { + /* no guesses, just go in the general direction */ + u.dx = sgn(u.tx - u.ux); + u.dy = sgn(u.ty - u.uy); + if (test_move(u.ux, u.uy, u.dx, u.dy, TEST_MOVE)) + return TRUE; + goto found; + } + tx = px; + ty = py; + ux = u.ux; + uy = u.uy; + set = 0; + n = radius = 1; + guess = FALSE; + goto noguess; + } + return FALSE; + } + +found: + u.dx = 0; + u.dy = 0; + nomul(0); + return FALSE; +} + +void +domove() +{ + register struct monst *mtmp; + register struct rm *tmpr; + register xchar x,y; + struct trap *trap; + int wtcap; + boolean on_ice; + xchar chainx, chainy, ballx, bally; /* ball&chain new positions */ + int bc_control; /* control for ball&chain */ + boolean cause_delay = FALSE; /* dragging ball will skip a move */ + const char *predicament; + + u_wipe_engr(rnd(5)); + + if (flags.travel) { + if (!findtravelpath(FALSE)) + (void) findtravelpath(TRUE); + iflags.travel1 = 0; + } + + if(((wtcap = near_capacity()) >= OVERLOADED + || (wtcap > SLT_ENCUMBER && + (Upolyd ? (u.mh < 5 && u.mh != u.mhmax) + : (u.uhp < 10 && u.uhp != u.uhpmax)))) + && !Is_airlevel(&u.uz)) { + if(wtcap < OVERLOADED) { + You("don't have enough stamina to move."); + exercise(A_CON, FALSE); + } else + You("collapse under your load."); + nomul(0); + return; + } + if(u.uswallow) { + u.dx = u.dy = 0; + u.ux = x = u.ustuck->mx; + u.uy = y = u.ustuck->my; + mtmp = u.ustuck; + } else { + if (Is_airlevel(&u.uz) && rn2(4) && + !Levitation && !Flying) { + switch(rn2(3)) { + case 0: + You("tumble in place."); + exercise(A_DEX, FALSE); + break; + case 1: + You_cant("control your movements very well."); break; + case 2: + pline("It's hard to walk in thin air."); + exercise(A_DEX, TRUE); + break; + } + return; + } + + /* check slippery ice */ + on_ice = !Levitation && is_ice(u.ux, u.uy); + if (on_ice) { + static int skates = 0; + if (!skates) skates = find_skates(); + if ((uarmf && uarmf->otyp == skates) + || resists_cold(&youmonst) || Flying + || is_floater(youmonst.data) || is_clinger(youmonst.data) + || is_whirly(youmonst.data)) + on_ice = FALSE; + else if (!rn2(Cold_resistance ? 3 : 2)) { + HFumbling |= FROMOUTSIDE; + HFumbling &= ~TIMEOUT; + HFumbling += 1; /* slip on next move */ + } + } + if (!on_ice && (HFumbling & FROMOUTSIDE)) + HFumbling &= ~FROMOUTSIDE; + + x = u.ux + u.dx; + y = u.uy + u.dy; + if(Stunned || (Confusion && !rn2(5))) { + register int tries = 0; + + do { + if(tries++ > 50) { + nomul(0); + return; + } + confdir(); + x = u.ux + u.dx; + y = u.uy + u.dy; + } while(!isok(x, y) || bad_rock(youmonst.data, x, y)); + } + /* turbulence might alter your actual destination */ + if (u.uinwater) { + water_friction(); + if (!u.dx && !u.dy) { + nomul(0); + return; + } + x = u.ux + u.dx; + y = u.uy + u.dy; + } + if(!isok(x, y)) { + nomul(0); + return; + } + if (((trap = t_at(x, y)) && trap->tseen) || + (Blind && !Levitation && !Flying && + !is_clinger(youmonst.data) && + (is_pool(x, y) || is_lava(x, y)) && levl[x][y].seenv)) { + if(flags.run >= 2) { + nomul(0); + flags.move = 0; + return; + } else + nomul(0); + } + + if (u.ustuck && (x != u.ustuck->mx || y != u.ustuck->my)) { + if (distu(u.ustuck->mx, u.ustuck->my) > 2) { + /* perhaps it fled (or was teleported or ... ) */ + u.ustuck = 0; + } else if (sticks(youmonst.data)) { + /* When polymorphed into a sticking monster, + * u.ustuck means it's stuck to you, not you to it. + */ + You("release %s.", mon_nam(u.ustuck)); + u.ustuck = 0; + } else { + /* If holder is asleep or paralyzed: + * 37.5% chance of getting away, + * 12.5% chance of waking/releasing it; + * otherwise: + * 7.5% chance of getting away. + * [strength ought to be a factor] + * If holder is tame and there is no conflict, + * guaranteed escape. + */ + switch (rn2(!u.ustuck->mcanmove ? 8 : 40)) { + case 0: case 1: case 2: + pull_free: + You("pull free from %s.", mon_nam(u.ustuck)); + u.ustuck = 0; + break; + case 3: + if (!u.ustuck->mcanmove) { + /* it's free to move on next turn */ + u.ustuck->mfrozen = 1; + u.ustuck->msleeping = 0; + } + /*FALLTHRU*/ + default: + if (u.ustuck->mtame && + !Conflict && !u.ustuck->mconf) + goto pull_free; + You("cannot escape from %s!", mon_nam(u.ustuck)); + nomul(0); + return; + } + } + } + + mtmp = m_at(x,y); + if (mtmp) { + /* Don't attack if you're running, and can see it */ + /* We should never get here if forcefight */ + if (flags.run && + ((!Blind && mon_visible(mtmp) && + ((mtmp->m_ap_type != M_AP_FURNITURE && + mtmp->m_ap_type != M_AP_OBJECT) || + Protection_from_shape_changers)) || + sensemon(mtmp))) { + nomul(0); + flags.move = 0; + return; + } + } + } + + u.ux0 = u.ux; + u.uy0 = u.uy; + bhitpos.x = x; + bhitpos.y = y; + tmpr = &levl[x][y]; + + /* attack monster */ + if(mtmp) { + nomul(0); + /* only attack if we know it's there */ + /* or if we used the 'F' command to fight blindly */ + /* or if it hides_under, in which case we call attack() to print + * the Wait! message. + * This is different from ceiling hiders, who aren't handled in + * attack(). + */ + + /* If they used a 'm' command, trying to move onto a monster + * prints the below message and wastes a turn. The exception is + * if the monster is unseen and the player doesn't remember an + * invisible monster--then, we fall through to attack() and + * attack_check(), which still wastes a turn, but prints a + * different message and makes the player remember the monster. */ + if(flags.nopick && + (canspotmon(mtmp) || glyph_is_invisible(levl[x][y].glyph))){ + if(mtmp->m_ap_type && !Protection_from_shape_changers + && !sensemon(mtmp)) + stumble_onto_mimic(mtmp); + else if (mtmp->mpeaceful && !Hallucination) + pline("Pardon me, %s.", m_monnam(mtmp)); + else + You("move right into %s.", mon_nam(mtmp)); + return; + } + if(flags.forcefight || !mtmp->mundetected || sensemon(mtmp) || + ((hides_under(mtmp->data) || mtmp->data->mlet == S_EEL) && + !is_safepet(mtmp))){ + gethungry(); + if(wtcap >= HVY_ENCUMBER && moves%3) { + if (Upolyd && u.mh > 1) { + u.mh--; + } else if (!Upolyd && u.uhp > 1) { + u.uhp--; + } else { + You("pass out from exertion!"); + exercise(A_CON, FALSE); + fall_asleep(-10, FALSE); + } + } + if(multi < 0) return; /* we just fainted */ + + /* try to attack; note that it might evade */ + /* also, we don't attack tame when _safepet_ */ + if(attack(mtmp)) return; + } + } + + /* specifying 'F' with no monster wastes a turn */ + if (flags.forcefight || + /* remembered an 'I' && didn't use a move command */ + (glyph_is_invisible(levl[x][y].glyph) && !flags.nopick)) { + boolean expl = (Upolyd && attacktype(youmonst.data, AT_EXPL)); + char buf[BUFSZ]; + Sprintf(buf,"a vacant spot on the %s", surface(x,y)); + You("%s %s.", + expl ? "explode at" : "attack", + !Underwater ? "thin air" : + is_pool(x,y) ? "empty water" : buf); + unmap_object(x, y); /* known empty -- remove 'I' if present */ + newsym(x, y); + nomul(0); + if (expl) { + u.mh = -1; /* dead in the current form */ + rehumanize(); + } + return; + } + if (glyph_is_invisible(levl[x][y].glyph)) { + unmap_object(x, y); + newsym(x, y); + } + /* not attacking an animal, so we try to move */ +#ifdef STEED + if (u.usteed && !u.usteed->mcanmove && (u.dx || u.dy)) { + pline("%s won't move!", upstart(y_monnam(u.usteed))); + nomul(0); + return; + } else +#endif + if(!youmonst.data->mmove) { + You("are rooted %s.", + Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) ? + "in place" : "to the ground"); + nomul(0); + return; + } + if(u.utrap) { + if(u.utraptype == TT_PIT) { + if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) { + Your("%s gets stuck in a crevice.", body_part(LEG)); + display_nhwindow(WIN_MESSAGE, FALSE); + clear_nhwindow(WIN_MESSAGE); + You("free your %s.", body_part(LEG)); + } else if (!(--u.utrap)) { + You("%s to the edge of the pit.", + (In_sokoban(&u.uz) && Levitation) ? + "struggle against the air currents and float" : +#ifdef STEED + u.usteed ? "ride" : +#endif + "crawl"); + fill_pit(u.ux, u.uy); + vision_full_recalc = 1; /* vision limits change */ + } else if (flags.verbose) { +#ifdef STEED + if (u.usteed) + Norep("%s is still in a pit.", + upstart(y_monnam(u.usteed))); + else +#endif + Norep( (Hallucination && !rn2(5)) ? + "You've fallen, and you can't get up." : + "You are still in a pit." ); + } + } else if (u.utraptype == TT_LAVA) { + if(flags.verbose) { + predicament = "stuck in the lava"; +#ifdef STEED + if (u.usteed) + Norep("%s is %s.", upstart(y_monnam(u.usteed)), + predicament); + else +#endif + Norep("You are %s.", predicament); + } + if(!is_lava(x,y)) { + u.utrap--; + if((u.utrap & 0xff) == 0) { +#ifdef STEED + if (u.usteed) + You("lead %s to the edge of the lava.", + y_monnam(u.usteed)); + else +#endif + You("pull yourself to the edge of the lava."); + u.utrap = 0; + } + } + u.umoved = TRUE; + } else if (u.utraptype == TT_WEB) { + if(uwep && uwep->oartifact == ART_STING) { + u.utrap = 0; + pline("Sting cuts through the web!"); + return; + } + if(--u.utrap) { + if(flags.verbose) { + predicament = "stuck to the web"; +#ifdef STEED + if (u.usteed) + Norep("%s is %s.", upstart(y_monnam(u.usteed)), + predicament); + else +#endif + Norep("You are %s.", predicament); + } + } else { +#ifdef STEED + if (u.usteed) + pline("%s breaks out of the web.", + upstart(y_monnam(u.usteed))); + else +#endif + You("disentangle yourself."); + } + } else if (u.utraptype == TT_INFLOOR) { + if(--u.utrap) { + if(flags.verbose) { + predicament = "stuck in the"; +#ifdef STEED + if (u.usteed) + Norep("%s is %s %s.", + upstart(y_monnam(u.usteed)), + predicament, surface(u.ux, u.uy)); + else +#endif + Norep("You are %s %s.", predicament, + surface(u.ux, u.uy)); + } + } else { +#ifdef STEED + if (u.usteed) + pline("%s finally wiggles free.", + upstart(y_monnam(u.usteed))); + else +#endif + You("finally wiggle free."); + } + } else { + if(flags.verbose) { + predicament = "caught in a bear trap"; +#ifdef STEED + if (u.usteed) + Norep("%s is %s.", upstart(y_monnam(u.usteed)), + predicament); + else +#endif + Norep("You are %s.", predicament); + } + if((u.dx && u.dy) || !rn2(5)) u.utrap--; + } + return; + } + + if (!test_move(u.ux, u.uy, x-u.ux, y-u.uy, DO_MOVE)) { + flags.move = 0; + nomul(0); + return; + } + + /* Move ball and chain. */ + if (Punished) + if (!drag_ball(x,y, &bc_control, &ballx, &bally, &chainx, &chainy, + &cause_delay, TRUE)) + return; + + /* Check regions entering/leaving */ + if (!in_out_region(x,y)) + return; + + /* now move the hero */ + mtmp = m_at(x, y); + u.ux += u.dx; + u.uy += u.dy; +#ifdef STEED + /* Move your steed, too */ + if (u.usteed) { + u.usteed->mx = u.ux; + u.usteed->my = u.uy; + exercise_steed(); + } +#endif + + /* + * If safepet at destination then move the pet to the hero's + * previous location using the same conditions as in attack(). + * there are special extenuating circumstances: + * (1) if the pet dies then your god angers, + * (2) if the pet gets trapped then your god may disapprove, + * (3) if the pet was already trapped and you attempt to free it + * not only do you encounter the trap but you may frighten your + * pet causing it to go wild! moral: don't abuse this privilege. + * + * Ceiling-hiding pets are skipped by this section of code, to + * be caught by the normal falling-monster code. + */ + if (is_safepet(mtmp) && !(is_hider(mtmp->data) && mtmp->mundetected)) { + /* if trapped, there's a chance the pet goes wild */ + if (mtmp->mtrapped) { + if (!rn2(mtmp->mtame)) { + mtmp->mtame = mtmp->mpeaceful = mtmp->msleeping = 0; + if (mtmp->mleashed) m_unleash(mtmp, TRUE); + growl(mtmp); + } else { + yelp(mtmp); + } + } + mtmp->mundetected = 0; + if (mtmp->m_ap_type) seemimic(mtmp); + else if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my); + + if (mtmp->mtrapped && + (trap = t_at(mtmp->mx, mtmp->my)) != 0 && + (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT) && + sobj_at(BOULDER, trap->tx, trap->ty)) { + /* can't swap places with pet pinned in a pit by a boulder */ + u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ + } else if (u.ux0 != x && u.uy0 != y && + bad_rock(mtmp->data, x, u.uy0) && + bad_rock(mtmp->data, u.ux0, y) && + (bigmonst(mtmp->data) || (curr_mon_load(mtmp) > 600))) { + /* can't swap places when pet won't fit thru the opening */ + u.ux = u.ux0, u.uy = u.uy0; /* didn't move after all */ + You("stop. %s won't fit through.", upstart(y_monnam(mtmp))); + } else { + char pnambuf[BUFSZ]; + + /* save its current description in case of polymorph */ + Strcpy(pnambuf, y_monnam(mtmp)); + mtmp->mtrapped = 0; + remove_monster(x, y); + place_monster(mtmp, u.ux0, u.uy0); + + /* check for displacing it into pools and traps */ + switch (minliquid(mtmp) ? 2 : mintrap(mtmp)) { + case 0: + You("%s %s.", mtmp->mtame ? "displaced" : "frightened", + pnambuf); + break; + case 1: /* trapped */ + case 3: /* changed levels */ + /* there's already been a trap message, reinforce it */ + abuse_dog(mtmp); + adjalign(-3); + break; + case 2: + /* it may have drowned or died. that's no way to + * treat a pet! your god gets angry. + */ + if (rn2(4)) { + You_feel("guilty about losing your pet like this."); + u.ugangr++; + adjalign(-15); + } + + /* you killed your pet by direct action. + * minliquid and mintrap don't know to do this + */ + u.uconduct.killer++; + break; + default: + pline("that's strange, unknown mintrap result!"); + break; + } + } + } + + reset_occupations(); + if (flags.run) { + if ( flags.run < 8 ) + if (IS_DOOR(tmpr->typ) || IS_ROCK(tmpr->typ) || + IS_FURNITURE(tmpr->typ)) + nomul(0); + } + + if (hides_under(youmonst.data)) + u.uundetected = OBJ_AT(u.ux, u.uy); + else if (youmonst.data->mlet == S_EEL) + u.uundetected = is_pool(u.ux, u.uy) && !Is_waterlevel(&u.uz); + else if (u.dx || u.dy) + u.uundetected = 0; + + /* + * Mimics (or whatever) become noticeable if they move and are + * imitating something that doesn't move. We could extend this + * to non-moving monsters... + */ + if ((u.dx || u.dy) && (youmonst.m_ap_type == M_AP_OBJECT + || youmonst.m_ap_type == M_AP_FURNITURE)) + youmonst.m_ap_type = M_AP_NOTHING; + + check_leash(u.ux0,u.uy0); + + if(u.ux0 != u.ux || u.uy0 != u.uy) { + u.umoved = TRUE; + /* Clean old position -- vision_recalc() will print our new one. */ + newsym(u.ux0,u.uy0); + /* Since the hero has moved, adjust what can be seen/unseen. */ + vision_recalc(1); /* Do the work now in the recover time. */ + invocation_message(); + } + + if (Punished) /* put back ball and chain */ + move_bc(0,bc_control,ballx,bally,chainx,chainy); + + spoteffects(TRUE); + + /* delay next move because of ball dragging */ + /* must come after we finished picking up, in spoteffects() */ + if (cause_delay) { + nomul(-2); + nomovemsg = ""; + } + + if (flags.run && iflags.runmode != RUN_TPORT) { + /* display every step or every 7th step depending upon mode */ + if (iflags.runmode != RUN_LEAP || !(moves % 7L)) { + if (flags.time) flags.botl = 1; + curs_on_u(); + delay_output(); + if (iflags.runmode == RUN_CRAWL) { + delay_output(); + delay_output(); + delay_output(); + delay_output(); + } + } + } +} + +void +invocation_message() +{ + /* a special clue-msg when on the Invocation position */ + if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { + char buf[BUFSZ]; + struct obj *otmp = carrying(CANDELABRUM_OF_INVOCATION); + + nomul(0); /* stop running or travelling */ +#ifdef STEED + if (u.usteed) Sprintf(buf, "beneath %s", y_monnam(u.usteed)); + else +#endif + if (Levitation || Flying) Strcpy(buf, "beneath you"); + else Sprintf(buf, "under your %s", makeplural(body_part(FOOT))); + + You_feel("a strange vibration %s.", buf); + if (otmp && otmp->spe == 7 && otmp->lamplit) + pline("%s %s!", The(xname(otmp)), + Blind ? "throbs palpably" : "glows with a strange light"); + } +} + +#endif /* OVL3 */ +#ifdef OVL2 + +void +spoteffects(pick) +boolean pick; +{ + register struct monst *mtmp; + + if(u.uinwater) { + int was_underwater; + + if (!is_pool(u.ux,u.uy)) { + if (Is_waterlevel(&u.uz)) + You("pop into an air bubble."); + else if (is_lava(u.ux, u.uy)) + You("leave the water..."); /* oops! */ + else + You("are on solid %s again.", + is_ice(u.ux, u.uy) ? "ice" : "land"); + } + else if (Is_waterlevel(&u.uz)) + goto stillinwater; + else if (Levitation) + You("pop out of the water like a cork!"); + else if (Flying) + You("fly out of the water."); + else if (Wwalking) + You("slowly rise above the surface."); + else + goto stillinwater; + was_underwater = Underwater && !Is_waterlevel(&u.uz); + u.uinwater = 0; /* leave the water */ + if (was_underwater) { /* restore vision */ + docrt(); + vision_full_recalc = 1; + } + } +stillinwater:; + if (!Levitation && !u.ustuck && !Flying) { + /* limit recursive calls through teleds() */ + if (is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) { +#ifdef STEED + if (u.usteed && !is_flyer(u.usteed->data) && + !is_floater(u.usteed->data) && + !is_clinger(u.usteed->data)) { + dismount_steed(Underwater ? + DISMOUNT_FELL : DISMOUNT_GENERIC); + /* dismount_steed() -> float_down() -> pickup() */ + if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz)) + pick = FALSE; + } else +#endif + if (is_lava(u.ux, u.uy)) { + if (lava_effects()) return; + } else if (!Wwalking && drown()) + return; + } + } + check_special_room(FALSE); +#ifdef SINKS + if(IS_SINK(levl[u.ux][u.uy].typ) && Levitation) + dosinkfall(); +#endif + if (!in_steed_dismounting) { /* if dismounting, we'll check again later */ + struct trap *trap = t_at(u.ux, u.uy); + boolean pit; + pit = (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)); + if (trap && pit) + dotrap(trap, 0); /* fall into pit */ + if (pick) (void) pickup(1); + if (trap && !pit) + dotrap(trap, 0); /* fall into arrow trap, etc. */ + } + if((mtmp = m_at(u.ux, u.uy)) && !u.uswallow) { + mtmp->mundetected = mtmp->msleeping = 0; + switch(mtmp->data->mlet) { + case S_PIERCER: + pline("%s suddenly drops from the %s!", + Amonnam(mtmp), ceiling(u.ux,u.uy)); + if(mtmp->mtame) /* jumps to greet you, not attack */ + ; + else if(uarmh && is_metallic(uarmh)) + pline("Its blow glances off your helmet."); + else if (u.uac + 3 <= rnd(20)) + You("are almost hit by %s!", + x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE)); + else { + int dmg; + You("are hit by %s!", + x_monnam(mtmp, ARTICLE_A, "falling", 0, TRUE)); + dmg = d(4,6); + if(Half_physical_damage) dmg = (dmg+1) / 2; + mdamageu(mtmp, dmg); + } + break; + default: /* monster surprises you. */ + if(mtmp->mtame) + pline("%s jumps near you from the %s.", + Amonnam(mtmp), ceiling(u.ux,u.uy)); + else if(mtmp->mpeaceful) { + You("surprise %s!", + Blind && !sensemon(mtmp) ? + something : a_monnam(mtmp)); + mtmp->mpeaceful = 0; + } else + pline("%s attacks you by surprise!", + Amonnam(mtmp)); + break; + } + mnexto(mtmp); /* have to move the monster */ + } +} + +STATIC_OVL boolean +monstinroom(mdat,roomno) +struct permonst *mdat; +int roomno; +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(!DEADMONSTER(mtmp) && mtmp->data == mdat && + index(in_rooms(mtmp->mx, mtmp->my, 0), roomno + ROOMOFFSET)) + return(TRUE); + return(FALSE); +} + +char * +in_rooms(x, y, typewanted) +register xchar x, y; +register int typewanted; +{ + static char buf[5]; + char rno, *ptr = &buf[4]; + int typefound, min_x, min_y, max_x, max_y_offset, step; + register struct rm *lev; + +#define goodtype(rno) (!typewanted || \ + ((typefound = rooms[rno - ROOMOFFSET].rtype) == typewanted) || \ + ((typewanted == SHOPBASE) && (typefound > SHOPBASE))) \ + + switch (rno = levl[x][y].roomno) { + case NO_ROOM: + return(ptr); + case SHARED: + step = 2; + break; + case SHARED_PLUS: + step = 1; + break; + default: /* i.e. a regular room # */ + if (goodtype(rno)) + *(--ptr) = rno; + return(ptr); + } + + min_x = x - 1; + max_x = x + 1; + if (x < 1) + min_x += step; + else + if (x >= COLNO) + max_x -= step; + + min_y = y - 1; + max_y_offset = 2; + if (min_y < 0) { + min_y += step; + max_y_offset -= step; + } else + if ((min_y + max_y_offset) >= ROWNO) + max_y_offset -= step; + + for (x = min_x; x <= max_x; x += step) { + lev = &levl[x][min_y]; + y = 0; + if (((rno = lev[y].roomno) >= ROOMOFFSET) && + !index(ptr, rno) && goodtype(rno)) + *(--ptr) = rno; + y += step; + if (y > max_y_offset) + continue; + if (((rno = lev[y].roomno) >= ROOMOFFSET) && + !index(ptr, rno) && goodtype(rno)) + *(--ptr) = rno; + y += step; + if (y > max_y_offset) + continue; + if (((rno = lev[y].roomno) >= ROOMOFFSET) && + !index(ptr, rno) && goodtype(rno)) + *(--ptr) = rno; + } + return(ptr); +} + +/* is (x,y) in a town? */ +boolean +in_town(x, y) +register int x, y; +{ + s_level *slev = Is_special(&u.uz); + register struct mkroom *sroom; + boolean has_subrooms = FALSE; + + if (!slev || !slev->flags.town) return FALSE; + + /* + * See if (x,y) is in a room with subrooms, if so, assume it's the + * town. If there are no subrooms, the whole level is in town. + */ + for (sroom = &rooms[0]; sroom->hx > 0; sroom++) { + if (sroom->nsubrooms > 0) { + has_subrooms = TRUE; + if (inside_room(sroom, x, y)) return TRUE; + } + } + + return !has_subrooms; +} + +STATIC_OVL void +move_update(newlev) +register boolean newlev; +{ + char *ptr1, *ptr2, *ptr3, *ptr4; + + Strcpy(u.urooms0, u.urooms); + Strcpy(u.ushops0, u.ushops); + if (newlev) { + u.urooms[0] = '\0'; + u.uentered[0] = '\0'; + u.ushops[0] = '\0'; + u.ushops_entered[0] = '\0'; + Strcpy(u.ushops_left, u.ushops0); + return; + } + Strcpy(u.urooms, in_rooms(u.ux, u.uy, 0)); + + for (ptr1 = &u.urooms[0], + ptr2 = &u.uentered[0], + ptr3 = &u.ushops[0], + ptr4 = &u.ushops_entered[0]; + *ptr1; ptr1++) { + if (!index(u.urooms0, *ptr1)) + *(ptr2++) = *ptr1; + if (IS_SHOP(*ptr1 - ROOMOFFSET)) { + *(ptr3++) = *ptr1; + if (!index(u.ushops0, *ptr1)) + *(ptr4++) = *ptr1; + } + } + *ptr2 = '\0'; + *ptr3 = '\0'; + *ptr4 = '\0'; + + /* filter u.ushops0 -> u.ushops_left */ + for (ptr1 = &u.ushops0[0], ptr2 = &u.ushops_left[0]; *ptr1; ptr1++) + if (!index(u.ushops, *ptr1)) + *(ptr2++) = *ptr1; + *ptr2 = '\0'; +} + +void +check_special_room(newlev) +register boolean newlev; +{ + register struct monst *mtmp; + char *ptr; + + move_update(newlev); + + if (*u.ushops0) + u_left_shop(u.ushops_left, newlev); + + if (!*u.uentered && !*u.ushops_entered) /* implied by newlev */ + return; /* no entrance messages necessary */ + + /* Did we just enter a shop? */ + if (*u.ushops_entered) + u_entered_shop(u.ushops_entered); + + for (ptr = &u.uentered[0]; *ptr; ptr++) { + register int roomno = *ptr - ROOMOFFSET, rt = rooms[roomno].rtype; + + /* Did we just enter some other special room? */ + /* vault.c insists that a vault remain a VAULT, + * and temples should remain TEMPLEs, + * but everything else gives a message only the first time */ + switch (rt) { + case ZOO: + pline("Welcome to David's treasure zoo!"); + break; + case SWAMP: + pline("It %s rather %s down here.", + Blind ? "feels" : "looks", + Blind ? "humid" : "muddy"); + break; + case COURT: + You("enter an opulent throne room!"); + break; + case LEPREHALL: + You("enter a leprechaun hall!"); + break; + case MORGUE: + if(midnight()) { + const char *run = locomotion(youmonst.data, "Run"); + pline("%s away! %s away!", run, run); + } else + You("have an uncanny feeling..."); + break; + case BEEHIVE: + You("enter a giant beehive!"); + break; + case COCKNEST: + You("enter a disgusting nest!"); + break; + case ANTHOLE: + You("enter an anthole!"); + break; + case BARRACKS: + if(monstinroom(&mons[PM_SOLDIER], roomno) || + monstinroom(&mons[PM_SERGEANT], roomno) || + monstinroom(&mons[PM_LIEUTENANT], roomno) || + monstinroom(&mons[PM_CAPTAIN], roomno)) + You("enter a military barracks!"); + else + You("enter an abandoned barracks."); + break; + case DELPHI: + if(monstinroom(&mons[PM_ORACLE], roomno)) + verbalize("%s, %s, welcome to Delphi!", + Hello((struct monst *) 0), plname); + break; + case TEMPLE: + intemple(roomno + ROOMOFFSET); + /* fall through */ + default: + rt = 0; + } + + if (rt != 0) { + rooms[roomno].rtype = OROOM; + if (!search_special(rt)) { + /* No more room of that type */ + switch(rt) { + case COURT: + level.flags.has_court = 0; + break; + case SWAMP: + level.flags.has_swamp = 0; + break; + case MORGUE: + level.flags.has_morgue = 0; + break; + case ZOO: + level.flags.has_zoo = 0; + break; + case BARRACKS: + level.flags.has_barracks = 0; + break; + case TEMPLE: + level.flags.has_temple = 0; + break; + case BEEHIVE: + level.flags.has_beehive = 0; + break; + } + } + if (rt == COURT || rt == SWAMP || rt == MORGUE || rt == ZOO) + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && !Stealth && !rn2(3)) mtmp->msleeping = 0; + } + } + + return; +} + +#endif /* OVL2 */ +#ifdef OVLB + +int +dopickup() +{ + int count; + struct trap *traphere = t_at(u.ux, u.uy); + /* awful kludge to work around parse()'s pre-decrement */ + count = (multi || (save_cm && *save_cm == ',')) ? multi + 1 : 0; + multi = 0; /* always reset */ + /* uswallow case added by GAN 01/29/87 */ + if(u.uswallow) { + if (!u.ustuck->minvent) { + if (is_animal(u.ustuck->data)) { + You("pick up %s tongue.", s_suffix(mon_nam(u.ustuck))); + pline("But it's kind of slimy, so you drop it."); + } else + You("don't %s anything in here to pick up.", + Blind ? "feel" : "see"); + return(1); + } else { + int tmpcount = -count; + return loot_mon(u.ustuck, &tmpcount, (boolean *)0); + } + } + if(is_pool(u.ux, u.uy)) { + if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data) + || (Flying && !Breathless)) { + You("cannot dive into the water to pick things up."); + return(0); + } else if (!Underwater) { + You_cant("even see the bottom, let alone pick up %s.", + something); + return(0); + } + } + if (is_lava(u.ux, u.uy)) { + if (Wwalking || is_floater(youmonst.data) || is_clinger(youmonst.data) + || (Flying && !Breathless)) { + You_cant("reach the bottom to pick things up."); + return(0); + } else if (!likes_lava(youmonst.data)) { + You("would burn to a crisp trying to pick things up."); + return(0); + } + } + if(!OBJ_AT(u.ux, u.uy)) { + There("is nothing here to pick up."); + return(0); + } + if (!can_reach_floor()) { +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) + You("aren't skilled enough to reach from %s.", + y_monnam(u.usteed)); + else +#endif + You("cannot reach the %s.", surface(u.ux,u.uy)); + return(0); + } + + if (traphere && traphere->tseen) { + /* Allow pickup from holes and trap doors that you escaped from + * because that stuff is teetering on the edge just like you, but + * not pits, because there is an elevation discrepancy with stuff + * in pits. + */ + if ((traphere->ttyp == PIT || traphere->ttyp == SPIKED_PIT) && + (!u.utrap || (u.utrap && u.utraptype != TT_PIT))) { + You("cannot reach the bottom of the pit."); + return(0); + } + } + + return (pickup(-count)); +} + +#endif /* OVLB */ +#ifdef OVL2 + +/* stop running if we see something interesting */ +/* turn around a corner if that is the only way we can proceed */ +/* do not turn left or right twice */ +void +lookaround() +{ + register int x, y, i, x0 = 0, y0 = 0, m0 = 1, i0 = 9; + register int corrct = 0, noturn = 0; + register struct monst *mtmp; + register struct trap *trap; + + /* Grid bugs stop if trying to move diagonal, even if blind. Maybe */ + /* they polymorphed while in the middle of a long move. */ + if (u.umonnum == PM_GRID_BUG && u.dx && u.dy) { + nomul(0); + return; + } + + if(Blind || flags.run == 0) return; + for(x = u.ux-1; x <= u.ux+1; x++) for(y = u.uy-1; y <= u.uy+1; y++) { + if(!isok(x,y)) continue; + + if(u.umonnum == PM_GRID_BUG && x != u.ux && y != u.uy) continue; + + if(x == u.ux && y == u.uy) continue; + + if((mtmp = m_at(x,y)) && + mtmp->m_ap_type != M_AP_FURNITURE && + mtmp->m_ap_type != M_AP_OBJECT && + (!mtmp->minvis || See_invisible) && !mtmp->mundetected) { + if((flags.run != 1 && !mtmp->mtame) + || (x == u.ux+u.dx && y == u.uy+u.dy)) + goto stop; + } + + if (levl[x][y].typ == STONE) continue; + if (x == u.ux-u.dx && y == u.uy-u.dy) continue; + + if (IS_ROCK(levl[x][y].typ) || (levl[x][y].typ == ROOM) || + IS_AIR(levl[x][y].typ)) + continue; + else if (closed_door(x,y) || + (mtmp && mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor))) { + if(x != u.ux && y != u.uy) continue; + if(flags.run != 1) goto stop; + goto bcorr; + } else if (levl[x][y].typ == CORR) { +bcorr: + if(levl[u.ux][u.uy].typ != ROOM) { + if(flags.run == 1 || flags.run == 3 || flags.run == 8) { + i = dist2(x,y,u.ux+u.dx,u.uy+u.dy); + if(i > 2) continue; + if(corrct == 1 && dist2(x,y,x0,y0) != 1) + noturn = 1; + if(i < i0) { + i0 = i; + x0 = x; + y0 = y; + m0 = mtmp ? 1 : 0; + } + } + corrct++; + } + continue; + } else if ((trap = t_at(x,y)) && trap->tseen) { + if(flags.run == 1) goto bcorr; /* if you must */ + if(x == u.ux+u.dx && y == u.uy+u.dy) goto stop; + continue; + } else if (is_pool(x,y) || is_lava(x,y)) { + /* water and lava only stop you if directly in front, and stop + * you even if you are running + */ + if(!Levitation && !Flying && !is_clinger(youmonst.data) && + x == u.ux+u.dx && y == u.uy+u.dy) + /* No Wwalking check; otherwise they'd be able + * to test boots by trying to SHIFT-direction + * into a pool and seeing if the game allowed it + */ + goto stop; + continue; + } else { /* e.g. objects or trap or stairs */ + if(flags.run == 1) goto bcorr; + if(flags.run == 8) continue; + if(mtmp) continue; /* d */ + if(((x == u.ux - u.dx) && (y != u.uy + u.dy)) || + ((y == u.uy - u.dy) && (x != u.ux + u.dx))) + continue; + } +stop: + nomul(0); + return; + } /* end for loops */ + + if(corrct > 1 && flags.run == 2) goto stop; + if((flags.run == 1 || flags.run == 3 || flags.run == 8) && + !noturn && !m0 && i0 && (corrct == 1 || (corrct == 2 && i0 == 1))) + { + /* make sure that we do not turn too far */ + if(i0 == 2) { + if(u.dx == y0-u.uy && u.dy == u.ux-x0) + i = 2; /* straight turn right */ + else + i = -2; /* straight turn left */ + } else if(u.dx && u.dy) { + if((u.dx == u.dy && y0 == u.uy) || (u.dx != u.dy && y0 != u.uy)) + i = -1; /* half turn left */ + else + i = 1; /* half turn right */ + } else { + if((x0-u.ux == y0-u.uy && !u.dy) || (x0-u.ux != y0-u.uy && u.dy)) + i = 1; /* half turn right */ + else + i = -1; /* half turn left */ + } + + i += u.last_str_turn; + if(i <= 2 && i >= -2) { + u.last_str_turn = i; + u.dx = x0-u.ux; + u.dy = y0-u.uy; + } + } +} + +/* something like lookaround, but we are not running */ +/* react only to monsters that might hit us */ +int +monster_nearby() +{ + register int x,y; + register struct monst *mtmp; + + /* Also see the similar check in dochugw() in monmove.c */ + for(x = u.ux-1; x <= u.ux+1; x++) + for(y = u.uy-1; y <= u.uy+1; y++) { + if(!isok(x,y)) continue; + if(x == u.ux && y == u.uy) continue; + if((mtmp = m_at(x,y)) && + mtmp->m_ap_type != M_AP_FURNITURE && + mtmp->m_ap_type != M_AP_OBJECT && + (!mtmp->mpeaceful || Hallucination) && + (!is_hider(mtmp->data) || !mtmp->mundetected) && + !noattacks(mtmp->data) && + mtmp->mcanmove && !mtmp->msleeping && /* aplvax!jcn */ + !onscary(u.ux, u.uy, mtmp) && + canspotmon(mtmp)) + return(1); + } + return(0); +} + +void +nomul(nval) + register int nval; +{ + if(multi < nval) return; /* This is a bug fix by ab@unido */ + u.uinvulnerable = FALSE; /* Kludge to avoid ctrl-C bug -dlc */ + u.usleep = 0; + multi = nval; + flags.travel = iflags.travel1 = flags.mv = flags.run = 0; +} + +/* called when a non-movement, multi-turn action has completed */ +void +unmul(msg_override) +const char *msg_override; +{ + multi = 0; /* caller will usually have done this already */ + if (msg_override) nomovemsg = msg_override; + else if (!nomovemsg) nomovemsg = You_can_move_again; + if (*nomovemsg) pline(nomovemsg); + nomovemsg = 0; + u.usleep = 0; + if (afternmv) (*afternmv)(); + afternmv = 0; +} + +#endif /* OVL2 */ +#ifdef OVL1 + +STATIC_OVL void +maybe_wail() +{ + static short powers[] = { TELEPORT, SEE_INVIS, POISON_RES, COLD_RES, + SHOCK_RES, FIRE_RES, SLEEP_RES, DISINT_RES, + TELEPORT_CONTROL, STEALTH, FAST, INVIS }; + + if (moves <= wailmsg + 50) return; + + wailmsg = moves; + if (Role_if(PM_WIZARD) || Race_if(PM_ELF) || Role_if(PM_VALKYRIE)) { + const char *who; + int i, powercnt; + + who = (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ? + urole.name.m : "Elf"; + if (u.uhp == 1) { + pline("%s is about to die.", who); + } else { + for (i = 0, powercnt = 0; i < SIZE(powers); ++i) + if (u.uprops[powers[i]].intrinsic & INTRINSIC) ++powercnt; + + pline(powercnt >= 4 ? "%s, all your powers will be lost..." + : "%s, your life force is running out.", who); + } + } else { + You_hear(u.uhp == 1 ? "the wailing of the Banshee..." + : "the howling of the CwnAnnwn..."); + } +} + +void +losehp(n, knam, k_format) +register int n; +register const char *knam; +boolean k_format; +{ + if (Upolyd) { + u.mh -= n; + if (u.mhmax < u.mh) u.mhmax = u.mh; + flags.botl = 1; + if (u.mh < 1) + rehumanize(); + else if (n > 0 && u.mh*10 < u.mhmax && Unchanging) + maybe_wail(); + return; + } + + u.uhp -= n; + if(u.uhp > u.uhpmax) + u.uhpmax = u.uhp; /* perhaps n was negative */ + flags.botl = 1; + if(u.uhp < 1) { + killer_format = k_format; + killer = knam; /* the thing that killed you */ + You("die..."); + done(DIED); + } else if (n > 0 && u.uhp*10 < u.uhpmax) { + maybe_wail(); + } +} + +int +weight_cap() +{ + register long carrcap; + + carrcap = 25*(ACURRSTR + ACURR(A_CON)) + 50; + if (Upolyd) { + /* consistent with can_carry() in mon.c */ + if (youmonst.data->mlet == S_NYMPH) + carrcap = MAX_CARR_CAP; + else if (!youmonst.data->cwt) + carrcap = (carrcap * (long)youmonst.data->msize) / MZ_HUMAN; + else if (!strongmonst(youmonst.data) + || (strongmonst(youmonst.data) && (youmonst.data->cwt > WT_HUMAN))) + carrcap = (carrcap * (long)youmonst.data->cwt / WT_HUMAN); + } + + if (Levitation || Is_airlevel(&u.uz) /* pugh@cornell */ +#ifdef STEED + || (u.usteed && strongmonst(u.usteed->data)) +#endif + ) + carrcap = MAX_CARR_CAP; + else { + if(carrcap > MAX_CARR_CAP) carrcap = MAX_CARR_CAP; + if (!Flying) { + if(EWounded_legs & LEFT_SIDE) carrcap -= 100; + if(EWounded_legs & RIGHT_SIDE) carrcap -= 100; + } + if (carrcap < 0) carrcap = 0; + } + return((int) carrcap); +} + +static int wc; /* current weight_cap(); valid after call to inv_weight() */ + +/* returns how far beyond the normal capacity the player is currently. */ +/* inv_weight() is negative if the player is below normal capacity. */ +int +inv_weight() +{ + register struct obj *otmp = invent; + register int wt = 0; + +#ifndef GOLDOBJ + /* when putting stuff into containers, gold is inserted at the head + of invent for easier manipulation by askchain & co, but it's also + retained in u.ugold in order to keep the status line accurate; we + mustn't add its weight in twice under that circumstance */ + wt = (otmp && otmp->oclass == COIN_CLASS) ? 0 : + (int)((u.ugold + 50L) / 100L); +#endif + while (otmp) { +#ifndef GOLDOBJ + if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data)) +#else + if (otmp->oclass == COIN_CLASS) + wt += (int)(((long)otmp->quan + 50L) / 100L); + else if (otmp->otyp != BOULDER || !throws_rocks(youmonst.data)) +#endif + wt += otmp->owt; + otmp = otmp->nobj; + } + wc = weight_cap(); + return (wt - wc); +} + +/* + * Returns 0 if below normal capacity, or the number of "capacity units" + * over the normal capacity the player is loaded. Max is 5. + */ +int +calc_capacity(xtra_wt) +int xtra_wt; +{ + int cap, wt = inv_weight() + xtra_wt; + + if (wt <= 0) return UNENCUMBERED; + if (wc <= 1) return OVERLOADED; + cap = (wt*2 / wc) + 1; + return min(cap, OVERLOADED); +} + +int +near_capacity() +{ + return calc_capacity(0); +} + +int +max_capacity() +{ + int wt = inv_weight(); + + return (wt - (2 * wc)); +} + +boolean +check_capacity(str) +const char *str; +{ + if(near_capacity() >= EXT_ENCUMBER) { + if(str) + pline(str); + else + You_cant("do that while carrying so much stuff."); + return 1; + } + return 0; +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +inv_cnt() +{ + register struct obj *otmp = invent; + register int ct = 0; + + while(otmp){ + ct++; + otmp = otmp->nobj; + } + return(ct); +} + +#ifdef GOLDOBJ +/* Counts the money in an object chain. */ +/* Intended use is for your or some monsters inventory, */ +/* now that u.gold/m.gold is gone.*/ +/* Counting money in a container might be possible too. */ +long +money_cnt(otmp) +struct obj *otmp; +{ + while(otmp) { + /* Must change when silver & copper is implemented: */ + if (otmp->oclass == COIN_CLASS) return otmp->quan; + otmp = otmp->nobj; + } + return 0; +} +#endif +#endif /* OVLB */ + +/*hack.c*/ diff --git a/src/hacklib.c b/src/hacklib.c new file mode 100644 index 0000000..0d08270 --- /dev/null +++ b/src/hacklib.c @@ -0,0 +1,615 @@ +/* SCCS Id: @(#)hacklib.c 3.4 2002/12/13 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* Copyright (c) Robert Patrick Rankin, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* We could include only config.h, except for the overlay definitions... */ +#include "hack.h" +/*= + Assorted 'small' utility routines. They're virtually independent of +NetHack, except that rounddiv may call panic(). + + return type routine name argument type(s) + boolean digit (char) + boolean letter (char) + char highc (char) + char lowc (char) + char * lcase (char *) + char * upstart (char *) + char * mungspaces (char *) + char * eos (char *) + char * strkitten (char *,char) + char * s_suffix (const char *) + char * xcrypt (const char *, char *) + boolean onlyspace (const char *) + char * tabexpand (char *) + char * visctrl (char) + const char * ordin (int) + char * sitoa (int) + int sgn (int) + int rounddiv (long, int) + int distmin (int, int, int, int) + int dist2 (int, int, int, int) + boolean online2 (int, int) + boolean pmatch (const char *, const char *) + int strncmpi (const char *, const char *, int) + char * strstri (const char *, const char *) + boolean fuzzymatch (const char *,const char *,const char *,boolean) + void setrandom (void) + int getyear (void) + char * yymmdd (time_t) + long yyyymmdd (time_t) + int phase_of_the_moon (void) + boolean friday_13th (void) + int night (void) + int midnight (void) +=*/ +#ifdef LINT +# define Static /* pacify lint */ +#else +# define Static static +#endif + +#ifdef OVLB +boolean +digit(c) /* is 'c' a digit? */ + char c; +{ + return((boolean)('0' <= c && c <= '9')); +} + +boolean +letter(c) /* is 'c' a letter? note: '@' classed as letter */ + char c; +{ + return((boolean)(('@' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))); +} +#endif /* OVLB */ + +#ifdef OVL1 +char +highc(c) /* force 'c' into uppercase */ + char c; +{ + return((char)(('a' <= c && c <= 'z') ? (c & ~040) : c)); +} + +char +lowc(c) /* force 'c' into lowercase */ + char c; +{ + return((char)(('A' <= c && c <= 'Z') ? (c | 040) : c)); +} +#endif /* OVL1 */ + +#ifdef OVLB +char * +lcase(s) /* convert a string into all lowercase */ + char *s; +{ + register char *p; + + for (p = s; *p; p++) + if ('A' <= *p && *p <= 'Z') *p |= 040; + return s; +} + +char * +upstart(s) /* convert first character of a string to uppercase */ + char *s; +{ + if (s) *s = highc(*s); + return s; +} + +/* remove excess whitespace from a string buffer (in place) */ +char * +mungspaces(bp) +char *bp; +{ + register char c, *p, *p2; + boolean was_space = TRUE; + + for (p = p2 = bp; (c = *p) != '\0'; p++) { + if (c == '\t') c = ' '; + if (c != ' ' || !was_space) *p2++ = c; + was_space = (c == ' '); + } + if (was_space && p2 > bp) p2--; + *p2 = '\0'; + return bp; +} + +#endif /* OVLB */ + +#ifdef OVL0 +char * +eos(s) /* return the end of a string (pointing at '\0') */ + register char *s; +{ + while (*s) s++; /* s += strlen(s); */ + return s; +} + +/* strcat(s, {c,'\0'}); */ +char * +strkitten(s, c) /* append a character to a string (in place) */ + char *s; + char c; +{ + char *p = eos(s); + + *p++ = c; + *p = '\0'; + return s; +} + +char * +s_suffix(s) /* return a name converted to possessive */ + const char *s; +{ + Static char buf[BUFSZ]; + + Strcpy(buf, s); + if(!strcmpi(buf, "it")) + Strcat(buf, "s"); + else if(*(eos(buf)-1) == 's') + Strcat(buf, "'"); + else + Strcat(buf, "'s"); + return buf; +} + +char * +xcrypt(str, buf) /* trivial text encryption routine (see makedefs) */ +const char *str; +char *buf; +{ + register const char *p; + register char *q; + register int bitmask; + + for (bitmask = 1, p = str, q = buf; *p; q++) { + *q = *p++; + if (*q & (32|64)) *q ^= bitmask; + if ((bitmask <<= 1) >= 32) bitmask = 1; + } + *q = '\0'; + return buf; +} +#endif /* OVL0 */ + +#ifdef OVL2 +boolean +onlyspace(s) /* is a string entirely whitespace? */ + const char *s; +{ + for (; *s; s++) + if (*s != ' ' && *s != '\t') return FALSE; + return TRUE; +} +#endif /* OVL2 */ + +#ifdef OVLB +char * +tabexpand(sbuf) /* expand tabs into proper number of spaces */ + char *sbuf; +{ + char buf[BUFSZ]; + register char *bp, *s = sbuf; + register int idx; + + if (!*s) return sbuf; + + /* warning: no bounds checking performed */ + for (bp = buf, idx = 0; *s; s++) + if (*s == '\t') { + do *bp++ = ' '; while (++idx % 8); + } else { + *bp++ = *s; + idx++; + } + *bp = 0; + return strcpy(sbuf, buf); +} + +char * +visctrl(c) /* make a displayable string from a character */ + char c; +{ + Static char ccc[3]; + + c &= 0177; + + ccc[2] = '\0'; + if (c < 040) { + ccc[0] = '^'; + ccc[1] = c | 0100; /* letter */ + } else if (c == 0177) { + ccc[0] = '^'; + ccc[1] = c & ~0100; /* '?' */ + } else { + ccc[0] = c; /* printable character */ + ccc[1] = '\0'; + } + return ccc; +} +#endif /* OVLB */ + +#ifdef OVL2 +const char * +ordin(n) /* return the ordinal suffix of a number */ + int n; /* note: should be non-negative */ +{ + register int dd = n % 10; + + return (dd == 0 || dd > 3 || (n % 100) / 10 == 1) ? "th" : + (dd == 1) ? "st" : (dd == 2) ? "nd" : "rd"; +} +#endif /* OVL2 */ + +#ifdef OVL1 +char * +sitoa(n) /* make a signed digit string from a number */ + int n; +{ + Static char buf[13]; + + Sprintf(buf, (n < 0) ? "%d" : "+%d", n); + return buf; +} + +int +sgn(n) /* return the sign of a number: -1, 0, or 1 */ + int n; +{ + return (n < 0) ? -1 : (n != 0); +} +#endif /* OVL1 */ + +#ifdef OVLB +int +rounddiv(x, y) /* calculate x/y, rounding as appropriate */ + long x; + int y; +{ + int r, m; + int divsgn = 1; + + if (y == 0) + panic("division by zero in rounddiv"); + else if (y < 0) { + divsgn = -divsgn; y = -y; + } + if (x < 0) { + divsgn = -divsgn; x = -x; + } + r = x / y; + m = x % y; + if (2*m >= y) r++; + + return divsgn * r; +} +#endif /* OVLB */ + +#ifdef OVL0 +int +distmin(x0, y0, x1, y1) /* distance between two points, in moves */ + int x0, y0, x1, y1; +{ + register int dx = x0 - x1, dy = y0 - y1; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + /* The minimum number of moves to get from (x0,y0) to (x1,y1) is the + : larger of the [absolute value of the] two deltas. + */ + return (dx < dy) ? dy : dx; +} + +int +dist2(x0, y0, x1, y1) /* square of euclidean distance between pair of pts */ + int x0, y0, x1, y1; +{ + register int dx = x0 - x1, dy = y0 - y1; + return dx * dx + dy * dy; +} + +boolean +online2(x0, y0, x1, y1) /* are two points lined up (on a straight line)? */ + int x0, y0, x1, y1; +{ + int dx = x0 - x1, dy = y0 - y1; + /* If either delta is zero then they're on an orthogonal line, + * else if the deltas are equal (signs ignored) they're on a diagonal. + */ + return((boolean)(!dy || !dx || (dy == dx) || (dy + dx == 0))); /* (dy == -dx) */ +} + +#endif /* OVL0 */ +#ifdef OVLB + +boolean +pmatch(patrn, strng) /* match a string against a pattern */ + const char *patrn, *strng; +{ + char s, p; + /* + : Simple pattern matcher: '*' matches 0 or more characters, '?' matches + : any single character. Returns TRUE if 'strng' matches 'patrn'. + */ +pmatch_top: + s = *strng++; p = *patrn++; /* get next chars and pre-advance */ + if (!p) /* end of pattern */ + return((boolean)(s == '\0')); /* matches iff end of string too */ + else if (p == '*') /* wildcard reached */ + return((boolean)((!*patrn || pmatch(patrn, strng-1)) ? TRUE : + s ? pmatch(patrn-1, strng) : FALSE)); + else if (p != s && (p != '?' || !s)) /* check single character */ + return FALSE; /* doesn't match */ + else /* return pmatch(patrn, strng); */ + goto pmatch_top; /* optimize tail recursion */ +} +#endif /* OVLB */ + +#ifdef OVL2 +#ifndef STRNCMPI +int +strncmpi(s1, s2, n) /* case insensitive counted string comparison */ + register const char *s1, *s2; + register int n; /*(should probably be size_t, which is usually unsigned)*/ +{ /*{ aka strncasecmp }*/ + register char t1, t2; + + while (n--) { + if (!*s2) return (*s1 != 0); /* s1 >= s2 */ + else if (!*s1) return -1; /* s1 < s2 */ + t1 = lowc(*s1++); + t2 = lowc(*s2++); + if (t1 != t2) return (t1 > t2) ? 1 : -1; + } + return 0; /* s1 == s2 */ +} +#endif /* STRNCMPI */ +#endif /* OVL2 */ + +#ifdef OVLB +#ifndef STRSTRI + +char * +strstri(str, sub) /* case insensitive substring search */ + const char *str; + const char *sub; +{ + register const char *s1, *s2; + register int i, k; +# define TABSIZ 0x20 /* 0x40 would be case-sensitive */ + char tstr[TABSIZ], tsub[TABSIZ]; /* nibble count tables */ +# if 0 + assert( (TABSIZ & ~(TABSIZ-1)) == TABSIZ ); /* must be exact power of 2 */ + assert( &lowc != 0 ); /* can't be unsafe macro */ +# endif + + /* special case: empty substring */ + if (!*sub) return (char *) str; + + /* do some useful work while determining relative lengths */ + for (i = 0; i < TABSIZ; i++) tstr[i] = tsub[i] = 0; /* init */ + for (k = 0, s1 = str; *s1; k++) tstr[*s1++ & (TABSIZ-1)]++; + for ( s2 = sub; *s2; --k) tsub[*s2++ & (TABSIZ-1)]++; + + /* evaluate the info we've collected */ + if (k < 0) return (char *) 0; /* sub longer than str, so can't match */ + for (i = 0; i < TABSIZ; i++) /* does sub have more 'x's than str? */ + if (tsub[i] > tstr[i]) return (char *) 0; /* match not possible */ + + /* now actually compare the substring repeatedly to parts of the string */ + for (i = 0; i <= k; i++) { + s1 = &str[i]; + s2 = sub; + while (lowc(*s1++) == lowc(*s2++)) + if (!*s2) return (char *) &str[i]; /* full match */ + } + return (char *) 0; /* not found */ +} +#endif /* STRSTRI */ + +/* compare two strings for equality, ignoring the presence of specified + characters (typically whitespace) and possibly ignoring case */ +boolean +fuzzymatch(s1, s2, ignore_chars, caseblind) + const char *s1, *s2; + const char *ignore_chars; + boolean caseblind; +{ + register char c1, c2; + + do { + while ((c1 = *s1++) != '\0' && index(ignore_chars, c1) != 0) continue; + while ((c2 = *s2++) != '\0' && index(ignore_chars, c2) != 0) continue; + if (!c1 || !c2) break; /* stop when end of either string is reached */ + + if (caseblind) { + c1 = lowc(c1); + c2 = lowc(c2); + } + } while (c1 == c2); + + /* match occurs only when the end of both strings has been reached */ + return (boolean)(!c1 && !c2); +} + +#endif /* OVLB */ +#ifdef OVL2 + +/* + * Time routines + * + * The time is used for: + * - seed for rand() + * - year on tombstone and yyyymmdd in record file + * - phase of the moon (various monsters react to NEW_MOON or FULL_MOON) + * - night and midnight (the undead are dangerous at midnight) + * - determination of what files are "very old" + */ + +#if defined(AMIGA) && !defined(AZTEC_C) && !defined(__SASC_60) && !defined(_DCC) && !defined(__GNUC__) +extern struct tm *FDECL(localtime,(time_t *)); +#endif +static struct tm *NDECL(getlt); + +void +setrandom() +{ + /* the types are different enough here that sweeping the different + * routine names into one via #defines is even more confusing + */ +#ifdef RANDOM /* srandom() from sys/share/random.c */ + srandom((unsigned int) time((time_t *)0)); +#else +# if defined(__APPLE__) || defined(BSD) || defined(LINUX) || defined(ULTRIX) || defined(CYGWIN32) /* system srandom() */ +# if defined(BSD) && !defined(POSIX_TYPES) +# if defined(SUNOS4) + (void) +# endif + srandom((int) time((long *)0)); +# else + srandom((int) time((time_t *)0)); +# endif +# else +# ifdef UNIX /* system srand48() */ + srand48((long) time((time_t *)0)); +# else /* poor quality system routine */ + srand((int) time((time_t *)0)); +# endif +# endif +#endif +} + +static struct tm * +getlt() +{ + time_t date; + +#if defined(BSD) && !defined(POSIX_TYPES) + (void) time((long *)(&date)); +#else + (void) time(&date); +#endif +#if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || (defined(BSD) && !defined(POSIX_TYPES)) + return(localtime((long *)(&date))); +#else + return(localtime(&date)); +#endif +} + +int +getyear() +{ + return(1900 + getlt()->tm_year); +} + +#if 0 +/* This routine is no longer used since in 2000 it will yield "100mmdd". */ +char * +yymmdd(date) +time_t date; +{ + Static char datestr[10]; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else +#if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || defined(BSD) + lt = localtime((long *)(&date)); +#else + lt = localtime(&date); +#endif + + Sprintf(datestr, "%02d%02d%02d", + lt->tm_year, lt->tm_mon + 1, lt->tm_mday); + return(datestr); +} +#endif + +long +yyyymmdd(date) +time_t date; +{ + long datenum; + struct tm *lt; + + if (date == 0) + lt = getlt(); + else +#if (defined(ULTRIX) && !(defined(ULTRIX_PROTO) || defined(NHSTDC))) || (defined(BSD) && !defined(POSIX_TYPES)) + lt = localtime((long *)(&date)); +#else + lt = localtime(&date); +#endif + + /* just in case somebody's localtime supplies (year % 100) + rather than the expected (year - 1900) */ + if (lt->tm_year < 70) + datenum = (long)lt->tm_year + 2000L; + else + datenum = (long)lt->tm_year + 1900L; + /* yyyy --> yyyymm */ + datenum = datenum * 100L + (long)(lt->tm_mon + 1); + /* yyyymm --> yyyymmdd */ + datenum = datenum * 100L + (long)lt->tm_mday; + return datenum; +} + +/* + * moon period = 29.53058 days ~= 30, year = 365.2422 days + * days moon phase advances on first day of year compared to preceding year + * = 365.2422 - 12*29.53058 ~= 11 + * years in Metonic cycle (time until same phases fall on the same days of + * the month) = 18.6 ~= 19 + * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30 + * (29 as initial condition) + * current phase in days = first day phase + days elapsed in year + * 6 moons ~= 177 days + * 177 ~= 8 reported phases * 22 + * + 11/22 for rounding + */ +int +phase_of_the_moon() /* 0-7, with 0: new, 4: full */ +{ + register struct tm *lt = getlt(); + register int epact, diy, goldn; + + diy = lt->tm_yday; + goldn = (lt->tm_year % 19) + 1; + epact = (11 * goldn + 18) % 30; + if ((epact == 25 && goldn > 11) || epact == 24) + epact++; + + return( (((((diy + epact) * 6) + 11) % 177) / 22) & 7 ); +} + +boolean +friday_13th() +{ + register struct tm *lt = getlt(); + + return((boolean)(lt->tm_wday == 5 /* friday */ && lt->tm_mday == 13)); +} + +int +night() +{ + register int hour = getlt()->tm_hour; + + return(hour < 6 || hour > 21); +} + +int +midnight() +{ + return(getlt()->tm_hour == 0); +} +#endif /* OVL2 */ + +/*hacklib.c*/ diff --git a/src/invent.c b/src/invent.c new file mode 100644 index 0000000..b9a3683 --- /dev/null +++ b/src/invent.c @@ -0,0 +1,2934 @@ +/* SCCS Id: @(#)invent.c 3.4 2003/12/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#define NOINVSYM '#' +#define CONTAINED_SYM '>' /* designator for inside a container */ + +#ifdef OVL1 +STATIC_DCL void NDECL(reorder_invent); +STATIC_DCL boolean FDECL(mergable,(struct obj *,struct obj *)); +STATIC_DCL void FDECL(invdisp_nothing, (const char *,const char *)); +STATIC_DCL boolean FDECL(worn_wield_only, (struct obj *)); +STATIC_DCL boolean FDECL(only_here, (struct obj *)); +#endif /* OVL1 */ +STATIC_DCL void FDECL(compactify,(char *)); +STATIC_DCL boolean FDECL(taking_off, (const char *)); +STATIC_DCL boolean FDECL(putting_on, (const char *)); +STATIC_PTR int FDECL(ckunpaid,(struct obj *)); +STATIC_PTR int FDECL(ckvalidcat,(struct obj *)); +static char FDECL(display_pickinv, (const char *,BOOLEAN_P, long *)); +#ifdef OVLB +STATIC_DCL boolean FDECL(this_type_only, (struct obj *)); +STATIC_DCL void NDECL(dounpaid); +STATIC_DCL struct obj *FDECL(find_unpaid,(struct obj *,struct obj **)); +STATIC_DCL void FDECL(menu_identify, (int)); +STATIC_DCL boolean FDECL(tool_in_use, (struct obj *)); +#endif /* OVLB */ +STATIC_DCL char FDECL(obj_to_let,(struct obj *)); + +#ifdef OVLB + +static int lastinvnr = 51; /* 0 ... 51 (never saved&restored) */ + +#ifdef WIZARD +/* wizards can wish for venom, which will become an invisible inventory + * item without this. putting it in inv_order would mean venom would + * suddenly become a choice for all the inventory-class commands, which + * would probably cause mass confusion. the test for inventory venom + * is only WIZARD and not wizard because the wizard can leave venom lying + * around on a bones level for normal players to find. + */ +static char venom_inv[] = { VENOM_CLASS, 0 }; /* (constant) */ +#endif + +void +assigninvlet(otmp) +register struct obj *otmp; +{ + boolean inuse[52]; + register int i; + register struct obj *obj; + +#ifdef GOLDOBJ + /* There is only one of these in inventory... */ + if (otmp->oclass == COIN_CLASS) { + otmp->invlet = GOLD_SYM; + return; + } +#endif + + for(i = 0; i < 52; i++) inuse[i] = FALSE; + for(obj = invent; obj; obj = obj->nobj) if(obj != otmp) { + i = obj->invlet; + if('a' <= i && i <= 'z') inuse[i - 'a'] = TRUE; else + if('A' <= i && i <= 'Z') inuse[i - 'A' + 26] = TRUE; + if(i == otmp->invlet) otmp->invlet = 0; + } + if((i = otmp->invlet) && + (('a' <= i && i <= 'z') || ('A' <= i && i <= 'Z'))) + return; + for(i = lastinvnr+1; i != lastinvnr; i++) { + if(i == 52) { i = -1; continue; } + if(!inuse[i]) break; + } + otmp->invlet = (inuse[i] ? NOINVSYM : + (i < 26) ? ('a'+i) : ('A'+i-26)); + lastinvnr = i; +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* note: assumes ASCII; toggling a bit puts lowercase in front of uppercase */ +#define inv_rank(o) ((o)->invlet ^ 040) + +/* sort the inventory; used by addinv() and doorganize() */ +STATIC_OVL void +reorder_invent() +{ + struct obj *otmp, *prev, *next; + boolean need_more_sorting; + + do { + /* + * We expect at most one item to be out of order, so this + * isn't nearly as inefficient as it may first appear. + */ + need_more_sorting = FALSE; + for (otmp = invent, prev = 0; otmp; ) { + next = otmp->nobj; + if (next && inv_rank(next) < inv_rank(otmp)) { + need_more_sorting = TRUE; + if (prev) prev->nobj = next; + else invent = next; + otmp->nobj = next->nobj; + next->nobj = otmp; + prev = next; + } else { + prev = otmp; + otmp = next; + } + } + } while (need_more_sorting); +} + +#undef inv_rank + +/* scan a list of objects to see whether another object will merge with + one of them; used in pickup.c when all 52 inventory slots are in use, + to figure out whether another object could still be picked up */ +struct obj * +merge_choice(objlist, obj) +struct obj *objlist, *obj; +{ + struct monst *shkp; + int save_nocharge; + + if (obj->otyp == SCR_SCARE_MONSTER) /* punt on these */ + return (struct obj *)0; + /* if this is an item on the shop floor, the attributes it will + have when carried are different from what they are now; prevent + that from eliciting an incorrect result from mergable() */ + save_nocharge = obj->no_charge; + if (objlist == invent && obj->where == OBJ_FLOOR && + (shkp = shop_keeper(inside_shop(obj->ox, obj->oy))) != 0) { + if (obj->no_charge) obj->no_charge = 0; + /* A billable object won't have its `unpaid' bit set, so would + erroneously seem to be a candidate to merge with a similar + ordinary object. That's no good, because once it's really + picked up, it won't merge after all. It might merge with + another unpaid object, but we can't check that here (depends + too much upon shk's bill) and if it doesn't merge it would + end up in the '#' overflow inventory slot, so reject it now. */ + else if (inhishop(shkp)) return (struct obj *)0; + } + while (objlist) { + if (mergable(objlist, obj)) break; + objlist = objlist->nobj; + } + obj->no_charge = save_nocharge; + return objlist; +} + +/* merge obj with otmp and delete obj if types agree */ +int +merged(potmp, pobj) +struct obj **potmp, **pobj; +{ + register struct obj *otmp = *potmp, *obj = *pobj; + + if(mergable(otmp, obj)) { + /* Approximate age: we do it this way because if we were to + * do it "accurately" (merge only when ages are identical) + * we'd wind up never merging any corpses. + * otmp->age = otmp->age*(1-proportion) + obj->age*proportion; + * + * Don't do the age manipulation if lit. We would need + * to stop the burn on both items, then merge the age, + * then restart the burn. + */ + if (!obj->lamplit) + otmp->age = ((otmp->age*otmp->quan) + (obj->age*obj->quan)) + / (otmp->quan + obj->quan); + + otmp->quan += obj->quan; +#ifdef GOLDOBJ + /* temporary special case for gold objects!!!! */ +#endif + if (otmp->oclass == COIN_CLASS) otmp->owt = weight(otmp); + else otmp->owt += obj->owt; + if(!otmp->onamelth && obj->onamelth) + otmp = *potmp = oname(otmp, ONAME(obj)); + obj_extract_self(obj); + + /* really should merge the timeouts */ + if (obj->lamplit) obj_merge_light_sources(obj, otmp); + if (obj->timed) obj_stop_timers(obj); /* follows lights */ + + /* fixup for `#adjust' merging wielded darts, daggers, &c */ + if (obj->owornmask && carried(otmp)) { + long wmask = otmp->owornmask | obj->owornmask; + + /* Both the items might be worn in competing slots; + merger preference (regardless of which is which): + primary weapon + alternate weapon -> primary weapon; + primary weapon + quiver -> primary weapon; + alternate weapon + quiver -> alternate weapon. + (Prior to 3.3.0, it was not possible for the two + stacks to be worn in different slots and `obj' + didn't need to be unworn when merging.) */ + if (wmask & W_WEP) wmask = W_WEP; + else if (wmask & W_SWAPWEP) wmask = W_SWAPWEP; + else if (wmask & W_QUIVER) wmask = W_QUIVER; + else { + impossible("merging strangely worn items (%lx)", wmask); + wmask = otmp->owornmask; + } + if ((otmp->owornmask & ~wmask) != 0L) setnotworn(otmp); + setworn(otmp, wmask); + setnotworn(obj); + } +#if 0 + /* (this should not be necessary, since items + already in a monster's inventory don't ever get + merged into other objects [only vice versa]) */ + else if (obj->owornmask && mcarried(otmp)) { + if (obj == MON_WEP(otmp->ocarry)) { + MON_WEP(otmp->ocarry) = otmp; + otmp->owornmask = W_WEP; + } + } +#endif /*0*/ + + obfree(obj,otmp); /* free(obj), bill->otmp */ + return(1); + } + return 0; +} + +/* +Adjust hero intrinsics as if this object was being added to the hero's +inventory. Called _before_ the object has been added to the hero's +inventory. + +This is called when adding objects to the hero's inventory normally (via +addinv) or when an object in the hero's inventory has been polymorphed +in-place. + +It may be valid to merge this code with with addinv_core2(). +*/ +void +addinv_core1(obj) +struct obj *obj; +{ + if (obj->oclass == COIN_CLASS) { +#ifndef GOLDOBJ + u.ugold += obj->quan; +#else + flags.botl = 1; +#endif + } else if (obj->otyp == AMULET_OF_YENDOR) { + if (u.uhave.amulet) impossible("already have amulet?"); + u.uhave.amulet = 1; + } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) { + if (u.uhave.menorah) impossible("already have candelabrum?"); + u.uhave.menorah = 1; + } else if (obj->otyp == BELL_OF_OPENING) { + if (u.uhave.bell) impossible("already have silver bell?"); + u.uhave.bell = 1; + } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + if (u.uhave.book) impossible("already have the book?"); + u.uhave.book = 1; + } else if (obj->oartifact) { + if (is_quest_artifact(obj)) { + if (u.uhave.questart) + impossible("already have quest artifact?"); + u.uhave.questart = 1; + artitouch(); + } + set_artifact_intrinsic(obj, 1, W_ART); + } +} + +/* +Adjust hero intrinsics as if this object was being added to the hero's +inventory. Called _after_ the object has been added to the hero's +inventory. + +This is called when adding objects to the hero's inventory normally (via +addinv) or when an object in the hero's inventory has been polymorphed +in-place. +*/ +void +addinv_core2(obj) +struct obj *obj; +{ + if (confers_luck(obj)) { + /* new luckstone must be in inventory by this point + * for correct calculation */ + set_moreluck(); + } +} + +/* +Add obj to the hero's inventory. Make sure the object is "free". +Adjust hero attributes as necessary. +*/ +struct obj * +addinv(obj) +struct obj *obj; +{ + struct obj *otmp, *prev; + + if (obj->where != OBJ_FREE) + panic("addinv: obj not free"); + obj->no_charge = 0; /* not meaningful for invent */ + + addinv_core1(obj); +#ifndef GOLDOBJ + /* if handed gold, we're done */ + if (obj->oclass == COIN_CLASS) + return obj; +#endif + + /* merge if possible; find end of chain in the process */ + for (prev = 0, otmp = invent; otmp; prev = otmp, otmp = otmp->nobj) + if (merged(&otmp, &obj)) { + obj = otmp; + goto added; + } + /* didn't merge, so insert into chain */ + if (flags.invlet_constant || !prev) { + if (flags.invlet_constant) assigninvlet(obj); + obj->nobj = invent; /* insert at beginning */ + invent = obj; + if (flags.invlet_constant) reorder_invent(); + } else { + prev->nobj = obj; /* insert at end */ + obj->nobj = 0; + } + obj->where = OBJ_INVENT; + +added: + addinv_core2(obj); + carry_obj_effects(obj); /* carrying affects the obj */ + update_inventory(); + return(obj); +} + +/* + * Some objects are affected by being carried. + * Make those adjustments here. Called _after_ the object + * has been added to the hero's or monster's inventory, + * and after hero's intrinsics have been updated. + */ +void +carry_obj_effects(obj) +struct obj *obj; +{ + /* Cursed figurines can spontaneously transform + when carried. */ + if (obj->otyp == FIGURINE) { + if (obj->cursed + && obj->corpsenm != NON_PM + && !dead_species(obj->corpsenm,TRUE)) { + attach_fig_transform_timeout(obj); + } + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* Add an item to the inventory unless we're fumbling or it refuses to be + * held (via touch_artifact), and give a message. + * If there aren't any free inventory slots, we'll drop it instead. + * If both success and failure messages are NULL, then we're just doing the + * fumbling/slot-limit checking for a silent grab. In any case, + * touch_artifact will print its own messages if they are warranted. + */ +struct obj * +hold_another_object(obj, drop_fmt, drop_arg, hold_msg) +struct obj *obj; +const char *drop_fmt, *drop_arg, *hold_msg; +{ + char buf[BUFSZ]; + + if (!Blind) obj->dknown = 1; /* maximize mergibility */ + if (obj->oartifact) { + /* place_object may change these */ + boolean crysknife = (obj->otyp == CRYSKNIFE); + int oerode = obj->oerodeproof; + boolean wasUpolyd = Upolyd; + + /* in case touching this object turns out to be fatal */ + place_object(obj, u.ux, u.uy); + + if (!touch_artifact(obj, &youmonst)) { + obj_extract_self(obj); /* remove it from the floor */ + dropy(obj); /* now put it back again :-) */ + return obj; + } else if (wasUpolyd && !Upolyd) { + /* loose your grip if you revert your form */ + if (drop_fmt) pline(drop_fmt, drop_arg); + obj_extract_self(obj); + dropy(obj); + return obj; + } + obj_extract_self(obj); + if (crysknife) { + obj->otyp = CRYSKNIFE; + obj->oerodeproof = oerode; + } + } + if (Fumbling) { + if (drop_fmt) pline(drop_fmt, drop_arg); + dropy(obj); + } else { + long oquan = obj->quan; + int prev_encumbr = near_capacity(); /* before addinv() */ + + /* encumbrance only matters if it would now become worse + than max( current_value, stressed ) */ + if (prev_encumbr < MOD_ENCUMBER) prev_encumbr = MOD_ENCUMBER; + /* addinv() may redraw the entire inventory, overwriting + drop_arg when it comes from something like doname() */ + if (drop_arg) drop_arg = strcpy(buf, drop_arg); + + obj = addinv(obj); + if (inv_cnt() > 52 + || ((obj->otyp != LOADSTONE || !obj->cursed) + && near_capacity() > prev_encumbr)) { + if (drop_fmt) pline(drop_fmt, drop_arg); + /* undo any merge which took place */ + if (obj->quan > oquan) obj = splitobj(obj, oquan); + dropx(obj); + } else { + if (flags.autoquiver && !uquiver && !obj->owornmask && + (is_missile(obj) || + ammo_and_launcher(obj, uwep) || + ammo_and_launcher(obj, uswapwep))) + setuqwep(obj); + if (hold_msg || drop_fmt) prinv(hold_msg, obj, oquan); + } + } + return obj; +} + +/* useup() all of an item regardless of its quantity */ +void +useupall(obj) +struct obj *obj; +{ + setnotworn(obj); + freeinv(obj); + obfree(obj, (struct obj *)0); /* deletes contents also */ +} + +void +useup(obj) +register struct obj *obj; +{ + /* Note: This works correctly for containers because they */ + /* (containers) don't merge. */ + if (obj->quan > 1L) { + obj->in_use = FALSE; /* no longer in use */ + obj->quan--; + obj->owt = weight(obj); + update_inventory(); + } else { + useupall(obj); + } +} + +/* use one charge from an item and possibly incur shop debt for it */ +void +consume_obj_charge(obj, maybe_unpaid) +struct obj *obj; +boolean maybe_unpaid; /* false if caller handles shop billing */ +{ + if (maybe_unpaid) check_unpaid(obj); + obj->spe -= 1; + if (obj->known) update_inventory(); +} + +#endif /* OVLB */ +#ifdef OVL3 + +/* +Adjust hero's attributes as if this object was being removed from the +hero's inventory. This should only be called from freeinv() and +where we are polymorphing an object already in the hero's inventory. + +Should think of a better name... +*/ +void +freeinv_core(obj) +struct obj *obj; +{ + if (obj->oclass == COIN_CLASS) { +#ifndef GOLDOBJ + u.ugold -= obj->quan; + obj->in_use = FALSE; +#endif + flags.botl = 1; + return; + } else if (obj->otyp == AMULET_OF_YENDOR) { + if (!u.uhave.amulet) impossible("don't have amulet?"); + u.uhave.amulet = 0; + } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) { + if (!u.uhave.menorah) impossible("don't have candelabrum?"); + u.uhave.menorah = 0; + } else if (obj->otyp == BELL_OF_OPENING) { + if (!u.uhave.bell) impossible("don't have silver bell?"); + u.uhave.bell = 0; + } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + if (!u.uhave.book) impossible("don't have the book?"); + u.uhave.book = 0; + } else if (obj->oartifact) { + if (is_quest_artifact(obj)) { + if (!u.uhave.questart) + impossible("don't have quest artifact?"); + u.uhave.questart = 0; + } + set_artifact_intrinsic(obj, 0, W_ART); + } + + if (obj->otyp == LOADSTONE) { + curse(obj); + } else if (confers_luck(obj)) { + set_moreluck(); + flags.botl = 1; + } else if (obj->otyp == FIGURINE && obj->timed) { + (void) stop_timer(FIG_TRANSFORM, (genericptr_t) obj); + } +} + +/* remove an object from the hero's inventory */ +void +freeinv(obj) +register struct obj *obj; +{ + extract_nobj(obj, &invent); + freeinv_core(obj); + update_inventory(); +} + +void +delallobj(x, y) +int x, y; +{ + struct obj *otmp, *otmp2; + + for (otmp = level.objects[x][y]; otmp; otmp = otmp2) { + if (otmp == uball) + unpunish(); + /* after unpunish(), or might get deallocated chain */ + otmp2 = otmp->nexthere; + if (otmp == uchain) + continue; + delobj(otmp); + } +} + +#endif /* OVL3 */ +#ifdef OVL2 + +/* destroy object in fobj chain (if unpaid, it remains on the bill) */ +void +delobj(obj) +register struct obj *obj; +{ + boolean update_map; + + if (obj->otyp == AMULET_OF_YENDOR || + obj->otyp == CANDELABRUM_OF_INVOCATION || + obj->otyp == BELL_OF_OPENING || + obj->otyp == SPE_BOOK_OF_THE_DEAD) { + /* player might be doing something stupid, but we + * can't guarantee that. assume special artifacts + * are indestructible via drawbridges, and exploding + * chests, and golem creation, and ... + */ + return; + } + update_map = (obj->where == OBJ_FLOOR); + obj_extract_self(obj); + if (update_map) newsym(obj->ox, obj->oy); + obfree(obj, (struct obj *) 0); /* frees contents also */ +} + +#endif /* OVL2 */ +#ifdef OVL0 + +struct obj * +sobj_at(n,x,y) +register int n, x, y; +{ + register struct obj *otmp; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if(otmp->otyp == n) + return(otmp); + return((struct obj *)0); +} + +#endif /* OVL0 */ +#ifdef OVLB + +struct obj * +carrying(type) +register int type; +{ + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == type) + return(otmp); + return((struct obj *) 0); +} + +const char * +currency(amount) +long amount; +{ + if (amount == 1L) return "zorkmid"; + else return "zorkmids"; +} + +boolean +have_lizard() +{ + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == CORPSE && otmp->corpsenm == PM_LIZARD) + return(TRUE); + return(FALSE); +} + +struct obj * +o_on(id, objchn) +unsigned int id; +register struct obj *objchn; +{ + struct obj *temp; + + while(objchn) { + if(objchn->o_id == id) return(objchn); + if (Has_contents(objchn) && (temp = o_on(id,objchn->cobj))) + return temp; + objchn = objchn->nobj; + } + return((struct obj *) 0); +} + +boolean +obj_here(obj, x, y) +register struct obj *obj; +int x, y; +{ + register struct obj *otmp; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if(obj == otmp) return(TRUE); + return(FALSE); +} + +#endif /* OVLB */ +#ifdef OVL2 + +struct obj * +g_at(x,y) +register int x, y; +{ + register struct obj *obj = level.objects[x][y]; + while(obj) { + if (obj->oclass == COIN_CLASS) return obj; + obj = obj->nexthere; + } + return((struct obj *)0); +} + +#endif /* OVL2 */ +#ifdef OVLB +#ifndef GOLDOBJ +/* Make a gold object from the hero's gold. */ +struct obj * +mkgoldobj(q) +register long q; +{ + register struct obj *otmp; + + otmp = mksobj(GOLD_PIECE, FALSE, FALSE); + u.ugold -= q; + otmp->quan = q; + otmp->owt = weight(otmp); + flags.botl = 1; + return(otmp); +} +#endif +#endif /* OVLB */ +#ifdef OVL1 + +STATIC_OVL void +compactify(buf) +register char *buf; +/* compact a string of inventory letters by dashing runs of letters */ +{ + register int i1 = 1, i2 = 1; + register char ilet, ilet1, ilet2; + + ilet2 = buf[0]; + ilet1 = buf[1]; + buf[++i2] = buf[++i1]; + ilet = buf[i1]; + while(ilet) { + if(ilet == ilet1+1) { + if(ilet1 == ilet2+1) + buf[i2 - 1] = ilet1 = '-'; + else if(ilet2 == '-') { + buf[i2 - 1] = ++ilet1; + buf[i2] = buf[++i1]; + ilet = buf[i1]; + continue; + } + } + ilet2 = ilet1; + ilet1 = ilet; + buf[++i2] = buf[++i1]; + ilet = buf[i1]; + } +} + +/* match the prompt for either 'T' or 'R' command */ +STATIC_OVL boolean +taking_off(action) +const char *action; +{ + return !strcmp(action, "take off") || !strcmp(action, "remove"); +} + +/* match the prompt for either 'W' or 'P' command */ +STATIC_OVL boolean +putting_on(action) +const char *action; +{ + return !strcmp(action, "wear") || !strcmp(action, "put on"); +} + +/* + * getobj returns: + * struct obj *xxx: object to do something with. + * (struct obj *) 0 error return: no object. + * &zeroobj explicitly no object (as in w-). +#ifdef GOLDOBJ +!!!! test if gold can be used in unusual ways (eaten etc.) +!!!! may be able to remove "usegold" +#endif + */ +struct obj * +getobj(let,word) +register const char *let,*word; +{ + register struct obj *otmp; + register char ilet; + char buf[BUFSZ], qbuf[QBUFSZ]; + char lets[BUFSZ], altlets[BUFSZ], *ap; + register int foo = 0; + register char *bp = buf; + xchar allowcnt = 0; /* 0, 1 or 2 */ +#ifndef GOLDOBJ + boolean allowgold = FALSE; /* can't use gold because they don't have any */ +#endif + boolean usegold = FALSE; /* can't use gold because its illegal */ + boolean allowall = FALSE; + boolean allownone = FALSE; + boolean useboulder = FALSE; + xchar foox = 0; + long cnt; + boolean prezero = FALSE; + long dummymask; + + if(*let == ALLOW_COUNT) let++, allowcnt = 1; +#ifndef GOLDOBJ + if(*let == COIN_CLASS) let++, + usegold = TRUE, allowgold = (u.ugold ? TRUE : FALSE); +#else + if(*let == COIN_CLASS) let++, usegold = TRUE; +#endif + + /* Equivalent of an "ugly check" for gold */ + if (usegold && !strcmp(word, "eat") && + (!metallivorous(youmonst.data) + || youmonst.data == &mons[PM_RUST_MONSTER])) +#ifndef GOLDOBJ + usegold = allowgold = FALSE; +#else + usegold = FALSE; +#endif + + if(*let == ALL_CLASSES) let++, allowall = TRUE; + if(*let == ALLOW_NONE) let++, allownone = TRUE; + /* "ugly check" for reading fortune cookies, part 1 */ + /* The normal 'ugly check' keeps the object on the inventory list. + * We don't want to do that for shirts/cookies, so the check for + * them is handled a bit differently (and also requires that we set + * allowall in the caller) + */ + if(allowall && !strcmp(word, "read")) allowall = FALSE; + + /* another ugly check: show boulders (not statues) */ + if(*let == WEAPON_CLASS && + !strcmp(word, "throw") && throws_rocks(youmonst.data)) + useboulder = TRUE; + + if(allownone) *bp++ = '-'; +#ifndef GOLDOBJ + if(allowgold) *bp++ = def_oc_syms[COIN_CLASS]; +#endif + if(bp > buf && bp[-1] == '-') *bp++ = ' '; + ap = altlets; + + ilet = 'a'; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (!flags.invlet_constant) +#ifdef GOLDOBJ + if (otmp->invlet != GOLD_SYM) /* don't reassign this */ +#endif + otmp->invlet = ilet; /* reassign() */ + if (!*let || index(let, otmp->oclass) +#ifdef GOLDOBJ + || (usegold && otmp->invlet == GOLD_SYM) +#endif + || (useboulder && otmp->otyp == BOULDER) + ) { + register int otyp = otmp->otyp; + bp[foo++] = otmp->invlet; + + /* ugly check: remove inappropriate things */ + if ((taking_off(word) && + (!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)) + || (otmp==uarm && uarmc) +#ifdef TOURIST + || (otmp==uarmu && (uarm || uarmc)) +#endif + )) + || (putting_on(word) && + (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))) + /* already worn */ +#if 0 /* 3.4.1 -- include currently wielded weapon among the choices */ + || (!strcmp(word, "wield") && + (otmp->owornmask & W_WEP)) +#endif + || (!strcmp(word, "ready") && + (otmp == uwep || (otmp == uswapwep && u.twoweap))) + ) { + foo--; + foox++; + } + + /* Second ugly check; unlike the first it won't trigger an + * "else" in "you don't have anything else to ___". + */ + else if ((putting_on(word) && + ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) || + (otmp->oclass == TOOL_CLASS && + otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES))) + || (!strcmp(word, "wield") && + (otmp->oclass == TOOL_CLASS && !is_weptool(otmp))) + || (!strcmp(word, "eat") && !is_edible(otmp)) + || (!strcmp(word, "sacrifice") && + (otyp != CORPSE && + otyp != AMULET_OF_YENDOR && otyp != FAKE_AMULET_OF_YENDOR)) + || (!strcmp(word, "write with") && + (otmp->oclass == TOOL_CLASS && + otyp != MAGIC_MARKER && otyp != TOWEL)) + || (!strcmp(word, "tin") && + (otyp != CORPSE || !tinnable(otmp))) + || (!strcmp(word, "rub") && + ((otmp->oclass == TOOL_CLASS && + otyp != OIL_LAMP && otyp != MAGIC_LAMP && + otyp != BRASS_LANTERN) || + (otmp->oclass == GEM_CLASS && !is_graystone(otmp)))) + || (!strncmp(word, "rub on the stone", 16) && + *let == GEM_CLASS && /* using known touchstone */ + otmp->dknown && objects[otyp].oc_name_known) + || ((!strcmp(word, "use or apply") || + !strcmp(word, "untrap with")) && + /* Picks, axes, pole-weapons, bullwhips */ + ((otmp->oclass == WEAPON_CLASS && !is_pick(otmp) && + !is_axe(otmp) && !is_pole(otmp) && otyp != BULLWHIP) || + (otmp->oclass == POTION_CLASS && + /* only applicable potion is oil, and it will only + be offered as a choice when already discovered */ + (otyp != POT_OIL || !otmp->dknown || + !objects[POT_OIL].oc_name_known)) || + (otmp->oclass == FOOD_CLASS && + otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF) || + (otmp->oclass == GEM_CLASS && !is_graystone(otmp)))) + || (!strcmp(word, "invoke") && + (!otmp->oartifact && !objects[otyp].oc_unique && + (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) && + otyp != CRYSTAL_BALL && /* #invoke synonym for apply */ + /* note: presenting the possibility of invoking non-artifact + mirrors and/or lamps is a simply a cruel deception... */ + otyp != MIRROR && otyp != MAGIC_LAMP && + (otyp != OIL_LAMP || /* don't list known oil lamp */ + (otmp->dknown && objects[OIL_LAMP].oc_name_known)))) + || (!strcmp(word, "untrap with") && + (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE)) + || (!strcmp(word, "charge") && !is_chargeable(otmp)) + ) + foo--; + /* ugly check for unworn armor that can't be worn */ + else if (putting_on(word) && *let == ARMOR_CLASS && + !canwearobj(otmp, &dummymask, FALSE)) { + foo--; + allowall = TRUE; + *ap++ = otmp->invlet; + } + } else { + + /* "ugly check" for reading fortune cookies, part 2 */ + if ((!strcmp(word, "read") && + (otmp->otyp == FORTUNE_COOKIE +#ifdef TOURIST + || otmp->otyp == T_SHIRT +#endif + ))) + allowall = TRUE; + } + + if(ilet == 'z') ilet = 'A'; else ilet++; + } + bp[foo] = 0; + if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0; + Strcpy(lets, bp); /* necessary since we destroy buf */ + if(foo > 5) /* compactify string */ + compactify(bp); + *ap = '\0'; + +#ifndef GOLDOBJ + if(!foo && !allowall && !allowgold && !allownone) { +#else + if(!foo && !allowall && !allownone) { +#endif + You("don't have anything %sto %s.", + foox ? "else " : "", word); + return((struct obj *)0); + } + for(;;) { + cnt = 0; + if (allowcnt == 2) allowcnt = 1; /* abort previous count */ + if(!buf[0]) { + Sprintf(qbuf, "What do you want to %s? [*]", word); + } else { + Sprintf(qbuf, "What do you want to %s? [%s or ?*]", + word, buf); + } +#ifdef REDO + if (in_doagain) + ilet = readchar(); + else +#endif + ilet = yn_function(qbuf, (char *)0, '\0'); + if(ilet == '0') prezero = TRUE; + while(digit(ilet) && allowcnt) { +#ifdef REDO + if (ilet != '?' && ilet != '*') savech(ilet); +#endif + cnt = 10*cnt + (ilet - '0'); + allowcnt = 2; /* signal presence of cnt */ + ilet = readchar(); + } + if(digit(ilet)) { + pline("No count allowed with this command."); + continue; + } + if(index(quitchars,ilet)) { + if(flags.verbose) + pline(Never_mind); + return((struct obj *)0); + } + if(ilet == '-') { + return(allownone ? &zeroobj : (struct obj *) 0); + } + if(ilet == def_oc_syms[COIN_CLASS]) { + if (!usegold) { + if (!strncmp(word, "rub on ", 7)) { + /* the dangers of building sentences... */ + You("cannot rub gold%s.", word + 3); + } else { + You("cannot %s gold.", word); + } + return(struct obj *)0; +#ifndef GOLDOBJ + } else if (!allowgold) { + You("are not carrying any gold."); + return(struct obj *)0; +#endif + } + if(cnt == 0 && prezero) return((struct obj *)0); + /* Historic note: early Nethack had a bug which was + * first reported for Larn, where trying to drop 2^32-n + * gold pieces was allowed, and did interesting things + * to your money supply. The LRS is the tax bureau + * from Larn. + */ + if(cnt < 0) { + pline_The("LRS would be very interested to know you have that much."); + return(struct obj *)0; + } + +#ifndef GOLDOBJ + if(!(allowcnt == 2 && cnt < u.ugold)) + cnt = u.ugold; + return(mkgoldobj(cnt)); +#endif + } + if(ilet == '?' || ilet == '*') { + char *allowed_choices = (ilet == '?') ? lets : (char *)0; + long ctmp = 0; + + if (ilet == '?' && !*lets && *altlets) + allowed_choices = altlets; + ilet = display_pickinv(allowed_choices, TRUE, + allowcnt ? &ctmp : (long *)0); + if(!ilet) continue; + if (allowcnt && ctmp >= 0) { + cnt = ctmp; + if (!cnt) prezero = TRUE; + allowcnt = 2; + } + if(ilet == '\033') { + if(flags.verbose) + pline(Never_mind); + return((struct obj *)0); + } + /* they typed a letter (not a space) at the prompt */ + } + if(allowcnt == 2 && !strcmp(word,"throw")) { + /* permit counts for throwing gold, but don't accept + * counts for other things since the throw code will + * split off a single item anyway */ +#ifdef GOLDOBJ + if (ilet != def_oc_syms[COIN_CLASS]) +#endif + allowcnt = 1; + if(cnt == 0 && prezero) return((struct obj *)0); + if(cnt > 1) { + You("can only throw one item at a time."); + continue; + } + } +#ifdef GOLDOBJ + flags.botl = 1; /* May have changed the amount of money */ +#endif +#ifdef REDO + savech(ilet); +#endif + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->invlet == ilet) break; + if(!otmp) { + You("don't have that object."); +#ifdef REDO + if (in_doagain) return((struct obj *) 0); +#endif + continue; + } else if (cnt < 0 || otmp->quan < cnt) { + You("don't have that many! You have only %ld.", + otmp->quan); +#ifdef REDO + if (in_doagain) return((struct obj *) 0); +#endif + continue; + } + break; + } + if(!allowall && let && !index(let,otmp->oclass) +#ifdef GOLDOBJ + && !(usegold && otmp->oclass == COIN_CLASS) +#endif + ) { + silly_thing(word, otmp); + return((struct obj *)0); + } + if(allowcnt == 2) { /* cnt given */ + if(cnt == 0) return (struct obj *)0; + if(cnt != otmp->quan) { + /* don't split a stack of cursed loadstones */ + if (otmp->otyp == LOADSTONE && otmp->cursed) + /* kludge for canletgo()'s can't-drop-this message */ + otmp->corpsenm = (int) cnt; + else + otmp = splitobj(otmp, cnt); + } + } + return(otmp); +} + +void +silly_thing(word, otmp) +const char *word; +struct obj *otmp; +{ + const char *s1, *s2, *s3, *what; + int ocls = otmp->oclass, otyp = otmp->otyp; + + s1 = s2 = s3 = 0; + /* check for attempted use of accessory commands ('P','R') on armor + and for corresponding armor commands ('W','T') on accessories */ + if (ocls == ARMOR_CLASS) { + if (!strcmp(word, "put on")) + s1 = "W", s2 = "wear", s3 = ""; + else if (!strcmp(word, "remove")) + s1 = "T", s2 = "take", s3 = " off"; + } else if ((ocls == RING_CLASS || otyp == MEAT_RING) || + ocls == AMULET_CLASS || + (otyp == BLINDFOLD || otyp == TOWEL || otyp == LENSES)) { + if (!strcmp(word, "wear")) + s1 = "P", s2 = "put", s3 = " on"; + else if (!strcmp(word, "take off")) + s1 = "R", s2 = "remove", s3 = ""; + } + if (s1) { + what = "that"; + /* quantity for armor and accessory objects is always 1, + but some things should be referred to as plural */ + if (otyp == LENSES || is_gloves(otmp) || is_boots(otmp)) + what = "those"; + pline("Use the '%s' command to %s %s%s.", s1, s2, what, s3); + } else { + pline(silly_thing_to, word); + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_PTR int +ckvalidcat(otmp) +register struct obj *otmp; +{ + /* use allow_category() from pickup.c */ + return((int)allow_category(otmp)); +} + +STATIC_PTR int +ckunpaid(otmp) +register struct obj *otmp; +{ + return((int)(otmp->unpaid)); +} + +boolean +wearing_armor() +{ + return((boolean)(uarm || uarmc || uarmf || uarmg || uarmh || uarms +#ifdef TOURIST + || uarmu +#endif + )); +} + +boolean +is_worn(otmp) +register struct obj *otmp; +{ + return((boolean)(!!(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL | +#ifdef STEED + W_SADDLE | +#endif + W_WEP | W_SWAPWEP | W_QUIVER)))); +} + +static NEARDATA const char removeables[] = + { ARMOR_CLASS, WEAPON_CLASS, RING_CLASS, AMULET_CLASS, TOOL_CLASS, 0 }; + +/* interactive version of getobj - used for Drop, Identify and */ +/* Takeoff (A). Return the number of times fn was called successfully */ +/* If combo is TRUE, we just use this to get a category list */ +int +ggetobj(word, fn, mx, combo, resultflags) +const char *word; +int FDECL((*fn),(OBJ_P)), mx; +boolean combo; /* combination menu flag */ +unsigned *resultflags; +{ + int FDECL((*ckfn),(OBJ_P)) = (int FDECL((*),(OBJ_P))) 0; + boolean FDECL((*filter),(OBJ_P)) = (boolean FDECL((*),(OBJ_P))) 0; + boolean takeoff, ident, allflag, m_seen; + int itemcount; +#ifndef GOLDOBJ + int oletct, iletct, allowgold, unpaid, oc_of_sym; +#else + int oletct, iletct, unpaid, oc_of_sym; +#endif + char sym, *ip, olets[MAXOCLASSES+5], ilets[MAXOCLASSES+5]; + char extra_removeables[3+1]; /* uwep,uswapwep,uquiver */ + char buf[BUFSZ], qbuf[QBUFSZ]; + + if (resultflags) *resultflags = 0; +#ifndef GOLDOBJ + allowgold = (u.ugold && !strcmp(word, "drop")) ? 1 : 0; +#endif + takeoff = ident = allflag = m_seen = FALSE; +#ifndef GOLDOBJ + if(!invent && !allowgold){ +#else + if(!invent){ +#endif + You("have nothing to %s.", word); + return(0); + } + add_valid_menu_class(0); /* reset */ + if (taking_off(word)) { + takeoff = TRUE; + filter = is_worn; + } else if (!strcmp(word, "identify")) { + ident = TRUE; + filter = not_fully_identified; + } + + iletct = collect_obj_classes(ilets, invent, + FALSE, +#ifndef GOLDOBJ + (allowgold != 0), +#endif + filter, &itemcount); + unpaid = count_unpaid(invent); + + if (ident && !iletct) { + return -1; /* no further identifications */ + } else if (!takeoff && (unpaid || invent)) { + ilets[iletct++] = ' '; + if (unpaid) ilets[iletct++] = 'u'; + if (count_buc(invent, BUC_BLESSED)) ilets[iletct++] = 'B'; + if (count_buc(invent, BUC_UNCURSED)) ilets[iletct++] = 'U'; + if (count_buc(invent, BUC_CURSED)) ilets[iletct++] = 'C'; + if (count_buc(invent, BUC_UNKNOWN)) ilets[iletct++] = 'X'; + if (invent) ilets[iletct++] = 'a'; + } else if (takeoff && invent) { + ilets[iletct++] = ' '; + } + ilets[iletct++] = 'i'; + if (!combo) + ilets[iletct++] = 'm'; /* allow menu presentation on request */ + ilets[iletct] = '\0'; + + for (;;) { + Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]", + word, ilets); + getlin(qbuf, buf); + if (buf[0] == '\033') return(0); + if (index(buf, 'i')) { + if (display_inventory((char *)0, TRUE) == '\033') return 0; + } else + break; + } + + extra_removeables[0] = '\0'; + if (takeoff) { + /* arbitrary types of items can be placed in the weapon slots + [any duplicate entries in extra_removeables[] won't matter] */ + if (uwep) (void)strkitten(extra_removeables, uwep->oclass); + if (uswapwep) (void)strkitten(extra_removeables, uswapwep->oclass); + if (uquiver) (void)strkitten(extra_removeables, uquiver->oclass); + } + + ip = buf; + olets[oletct = 0] = '\0'; + while ((sym = *ip++) != '\0') { + if (sym == ' ') continue; + oc_of_sym = def_char_to_objclass(sym); + if (takeoff && oc_of_sym != MAXOCLASSES) { + if (index(extra_removeables, oc_of_sym)) { + ; /* skip rest of takeoff checks */ + } else if (!index(removeables, oc_of_sym)) { + pline("Not applicable."); + return 0; + } else if (oc_of_sym == ARMOR_CLASS && !wearing_armor()) { + You("are not wearing any armor."); + return 0; + } else if (oc_of_sym == WEAPON_CLASS && + !uwep && !uswapwep && !uquiver) { + You("are not wielding anything."); + return 0; + } else if (oc_of_sym == RING_CLASS && !uright && !uleft) { + You("are not wearing rings."); + return 0; + } else if (oc_of_sym == AMULET_CLASS && !uamul) { + You("are not wearing an amulet."); + return 0; + } else if (oc_of_sym == TOOL_CLASS && !ublindf) { + You("are not wearing a blindfold."); + return 0; + } + } + + if (oc_of_sym == COIN_CLASS && !combo) { +#ifndef GOLDOBJ + if (allowgold == 1) + (*fn)(mkgoldobj(u.ugold)); + else if (!u.ugold) + You("have no gold."); + allowgold = 2; +#else + flags.botl = 1; +#endif + } else if (sym == 'a') { + allflag = TRUE; + } else if (sym == 'A') { + /* same as the default */ ; + } else if (sym == 'u') { + add_valid_menu_class('u'); + ckfn = ckunpaid; + } else if (sym == 'B') { + add_valid_menu_class('B'); + ckfn = ckvalidcat; + } else if (sym == 'U') { + add_valid_menu_class('U'); + ckfn = ckvalidcat; + } else if (sym == 'C') { + add_valid_menu_class('C'); + ckfn = ckvalidcat; + } else if (sym == 'X') { + add_valid_menu_class('X'); + ckfn = ckvalidcat; + } else if (sym == 'm') { + m_seen = TRUE; + } else if (oc_of_sym == MAXOCLASSES) { + You("don't have any %c's.", sym); + } else if (oc_of_sym != VENOM_CLASS) { /* suppress venom */ + if (!index(olets, oc_of_sym)) { + add_valid_menu_class(oc_of_sym); + olets[oletct++] = oc_of_sym; + olets[oletct] = 0; + } + } + } + + if (m_seen) + return (allflag || (!oletct && ckfn != ckunpaid)) ? -2 : -3; + else if (flags.menu_style != MENU_TRADITIONAL && combo && !allflag) + return 0; +#ifndef GOLDOBJ + else if (allowgold == 2 && !oletct) + return 1; /* you dropped gold (or at least tried to) */ + else { +#else + else /*!!!! if (allowgold == 2 && !oletct) + !!!! return 1; you dropped gold (or at least tried to) + !!!! test gold dropping + else*/ { +#endif + int cnt = askchain(&invent, olets, allflag, fn, ckfn, mx, word); + /* + * askchain() has already finished the job in this case + * so set a special flag to convey that back to the caller + * so that it won't continue processing. + * Fix for bug C331-1 reported by Irina Rempt-Drijfhout. + */ + if (combo && allflag && resultflags) + *resultflags |= ALL_FINISHED; + return cnt; + } +} + +/* + * Walk through the chain starting at objchn and ask for all objects + * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull) + * whether the action in question (i.e., fn) has to be performed. + * If allflag then no questions are asked. Max gives the max nr of + * objects to be treated. Return the number of objects treated. + */ +int +askchain(objchn, olets, allflag, fn, ckfn, mx, word) +struct obj **objchn; +register int allflag, mx; +register const char *olets, *word; /* olets is an Obj Class char array */ +register int FDECL((*fn),(OBJ_P)), FDECL((*ckfn),(OBJ_P)); +{ + struct obj *otmp, *otmp2, *otmpo; + register char sym, ilet; + register int cnt = 0, dud = 0, tmp; + boolean takeoff, nodot, ident, ininv; + char qbuf[QBUFSZ]; + + takeoff = taking_off(word); + ident = !strcmp(word, "identify"); + nodot = (!strcmp(word, "nodot") || !strcmp(word, "drop") || + ident || takeoff); + ininv = (*objchn == invent); + /* Changed so the askchain is interrogated in the order specified. + * For example, if a person specifies =/ then first all rings will be + * asked about followed by all wands -dgk + */ +nextclass: + ilet = 'a'-1; + if (*objchn && (*objchn)->oclass == COIN_CLASS) + ilet--; /* extra iteration */ + for (otmp = *objchn; otmp; otmp = otmp2) { + if(ilet == 'z') ilet = 'A'; else ilet++; + otmp2 = otmp->nobj; + if (olets && *olets && otmp->oclass != *olets) continue; + if (takeoff && !is_worn(otmp)) continue; + if (ident && !not_fully_identified(otmp)) continue; + if (ckfn && !(*ckfn)(otmp)) continue; + if (!allflag) { + Strcpy(qbuf, !ininv ? doname(otmp) : + xprname(otmp, (char *)0, ilet, !nodot, 0L, 0L)); + Strcat(qbuf, "?"); + sym = (takeoff || ident || otmp->quan < 2L) ? + nyaq(qbuf) : nyNaq(qbuf); + } + else sym = 'y'; + + otmpo = otmp; + if (sym == '#') { + /* Number was entered; split the object unless it corresponds + to 'none' or 'all'. 2 special cases: cursed loadstones and + welded weapons (eg, multiple daggers) will remain as merged + unit; done to avoid splitting an object that won't be + droppable (even if we're picking up rather than dropping). + */ + if (!yn_number) + sym = 'n'; + else { + sym = 'y'; + if (yn_number < otmp->quan && !welded(otmp) && + (!otmp->cursed || otmp->otyp != LOADSTONE)) { + otmp = splitobj(otmp, yn_number); + } + } + } + switch(sym){ + case 'a': + allflag = 1; + case 'y': + tmp = (*fn)(otmp); + if(tmp < 0) { + if (otmp != otmpo) { + /* split occurred, merge again */ + (void) merged(&otmpo, &otmp); + } + goto ret; + } + cnt += tmp; + if(--mx == 0) goto ret; + case 'n': + if(nodot) dud++; + default: + break; + case 'q': + /* special case for seffects() */ + if (ident) cnt = -1; + goto ret; + } + } + if (olets && *olets && *++olets) + goto nextclass; + if(!takeoff && (dud || cnt)) pline("That was all."); + else if(!dud && !cnt) pline("No applicable objects."); +ret: + return(cnt); +} + + +/* + * Object identification routines: + */ + +/* make an object actually be identified; no display updating */ +void +fully_identify_obj(otmp) +struct obj *otmp; +{ + makeknown(otmp->otyp); + if (otmp->oartifact) discover_artifact((xchar)otmp->oartifact); + otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; + if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) + learn_egg_type(otmp->corpsenm); +} + +/* ggetobj callback routine; identify an object and give immediate feedback */ +int +identify(otmp) +struct obj *otmp; +{ + fully_identify_obj(otmp); + prinv((char *)0, otmp, 0L); + return 1; +} + +/* menu of unidentified objects; select and identify up to id_limit of them */ +STATIC_OVL void +menu_identify(id_limit) +int id_limit; +{ + menu_item *pick_list; + int n, i, first = 1; + char buf[BUFSZ]; + /* assumptions: id_limit > 0 and at least one unID'd item is present */ + + while (id_limit) { + Sprintf(buf, "What would you like to identify %s?", + first ? "first" : "next"); + n = query_objlist(buf, invent, SIGNAL_NOMENU|USE_INVLET|INVORDER_SORT, + &pick_list, PICK_ANY, not_fully_identified); + + if (n > 0) { + if (n > id_limit) n = id_limit; + for (i = 0; i < n; i++, id_limit--) + (void) identify(pick_list[i].item.a_obj); + free((genericptr_t) pick_list); + mark_synch(); /* Before we loop to pop open another menu */ + } else { + if (n < 0) pline("That was all."); + id_limit = 0; /* Stop now */ + } + first = 0; + } +} + +/* dialog with user to identify a given number of items; 0 means all */ +void +identify_pack(id_limit) +int id_limit; +{ + struct obj *obj, *the_obj; + int n, unid_cnt; + + unid_cnt = 0; + the_obj = 0; /* if unid_cnt ends up 1, this will be it */ + for (obj = invent; obj; obj = obj->nobj) + if (not_fully_identified(obj)) ++unid_cnt, the_obj = obj; + + if (!unid_cnt) { + You("have already identified all of your possessions."); + } else if (!id_limit) { + /* identify everything */ + if (unid_cnt == 1) { + (void) identify(the_obj); + } else { + + /* TODO: use fully_identify_obj and cornline/menu/whatever here */ + for (obj = invent; obj; obj = obj->nobj) + if (not_fully_identified(obj)) (void) identify(obj); + + } + } else { + /* identify up to `id_limit' items */ + n = 0; + if (flags.menu_style == MENU_TRADITIONAL) + do { + n = ggetobj("identify", identify, id_limit, FALSE, (unsigned *)0); + if (n < 0) break; /* quit or no eligible items */ + } while ((id_limit -= n) > 0); + if (n == 0 || n < -1) + menu_identify(id_limit); + } + update_inventory(); +} + +#endif /* OVLB */ +#ifdef OVL2 + +STATIC_OVL char +obj_to_let(obj) /* should of course only be called for things in invent */ +register struct obj *obj; +{ +#ifndef GOLDOBJ + if (obj->oclass == COIN_CLASS) + return GOLD_SYM; +#endif + if (!flags.invlet_constant) { + obj->invlet = NOINVSYM; + reassign(); + } + return obj->invlet; +} + +/* + * Print the indicated quantity of the given object. If quan == 0L then use + * the current quantity. + */ +void +prinv(prefix, obj, quan) +const char *prefix; +register struct obj *obj; +long quan; +{ + if (!prefix) prefix = ""; + pline("%s%s%s", + prefix, *prefix ? " " : "", + xprname(obj, (char *)0, obj_to_let(obj), TRUE, 0L, quan)); +} + +#endif /* OVL2 */ +#ifdef OVL1 + +char * +xprname(obj, txt, let, dot, cost, quan) +struct obj *obj; +const char *txt; /* text to print instead of obj */ +char let; /* inventory letter */ +boolean dot; /* append period; (dot && cost => Iu) */ +long cost; /* cost (for inventory of unpaid or expended items) */ +long quan; /* if non-0, print this quantity, not obj->quan */ +{ +#ifdef LINT /* handle static char li[BUFSZ]; */ + char li[BUFSZ]; +#else + static char li[BUFSZ]; +#endif + boolean use_invlet = flags.invlet_constant && let != CONTAINED_SYM; + long savequan = 0; + + if (quan && obj) { + savequan = obj->quan; + obj->quan = quan; + } + + /* + * If let is: + * * Then obj == null and we are printing a total amount. + * > Then the object is contained and doesn't have an inventory letter. + */ + if (cost != 0 || let == '*') { + /* if dot is true, we're doing Iu, otherwise Ix */ + Sprintf(li, "%c - %-45s %6ld %s", + (dot && use_invlet ? obj->invlet : let), + (txt ? txt : doname(obj)), cost, currency(cost)); +#ifndef GOLDOBJ + } else if (obj && obj->oclass == COIN_CLASS) { + Sprintf(li, "%ld gold piece%s%s", obj->quan, plur(obj->quan), + (dot ? "." : "")); +#endif + } else { + /* ordinary inventory display or pickup message */ + Sprintf(li, "%c - %s%s", + (use_invlet ? obj->invlet : let), + (txt ? txt : doname(obj)), (dot ? "." : "")); + } + if (savequan) obj->quan = savequan; + + return li; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* the 'i' command */ +int +ddoinv() +{ + (void) display_inventory((char *)0, FALSE); + return 0; +} + +/* + * find_unpaid() + * + * Scan the given list of objects. If last_found is NULL, return the first + * unpaid object found. If last_found is not NULL, then skip over unpaid + * objects until last_found is reached, then set last_found to NULL so the + * next unpaid object is returned. This routine recursively follows + * containers. + */ +STATIC_OVL struct obj * +find_unpaid(list, last_found) + struct obj *list, **last_found; +{ + struct obj *obj; + + while (list) { + if (list->unpaid) { + if (*last_found) { + /* still looking for previous unpaid object */ + if (list == *last_found) + *last_found = (struct obj *) 0; + } else + return (*last_found = list); + } + if (Has_contents(list)) { + if ((obj = find_unpaid(list->cobj, last_found)) != 0) + return obj; + } + list = list->nobj; + } + return (struct obj *) 0; +} + +/* + * Internal function used by display_inventory and getobj that can display + * inventory and return a count as well as a letter. If out_cnt is not null, + * any count returned from the menu selection is placed here. + */ +static char +display_pickinv(lets, want_reply, out_cnt) +register const char *lets; +boolean want_reply; +long* out_cnt; +{ + struct obj *otmp; + char ilet, ret; + char *invlet = flags.inv_order; + int n, classcount; + winid win; /* windows being used */ + static winid local_win = WIN_ERR; /* window for partial menus */ + anything any; + menu_item *selected; + + /* overriden by global flag */ + if (flags.perm_invent) { + win = (lets && *lets) ? local_win : WIN_INVEN; + /* create the first time used */ + if (win == WIN_ERR) + win = local_win = create_nhwindow(NHW_MENU); + } else + win = WIN_INVEN; + + /* + Exit early if no inventory -- but keep going if we are doing + a permanent inventory update. We need to keep going so the + permanent inventory window updates itself to remove the last + item(s) dropped. One down side: the addition of the exception + for permanent inventory window updates _can_ pop the window + up when it's not displayed -- even if it's empty -- because we + don't know at this level if its up or not. This may not be + an issue if empty checks are done before hand and the call + to here is short circuited away. + */ + if (!invent && !(flags.perm_invent && !lets && !want_reply)) { +#ifndef GOLDOBJ + pline("Not carrying anything%s.", u.ugold ? " except gold" : ""); +#else + pline("Not carrying anything."); +#endif + return 0; + } + + /* oxymoron? temporarily assign permanent inventory letters */ + if (!flags.invlet_constant) reassign(); + + if (lets && strlen(lets) == 1) { + /* when only one item of interest, use pline instead of menus; + we actually use a fake message-line menu in order to allow + the user to perform selection at the --More-- prompt for tty */ + ret = '\0'; + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (otmp->invlet == lets[0]) { + ret = message_menu(lets[0], + want_reply ? PICK_ONE : PICK_NONE, + xprname(otmp, (char *)0, lets[0], TRUE, 0L, 0L)); + if (out_cnt) *out_cnt = -1L; /* select all */ + break; + } + } + return ret; + } + + start_menu(win); +nextclass: + classcount = 0; + any.a_void = 0; /* set all bits to zero */ + for(otmp = invent; otmp; otmp = otmp->nobj) { + ilet = otmp->invlet; + if(!lets || !*lets || index(lets, ilet)) { + if (!flags.sortpack || otmp->oclass == *invlet) { + if (flags.sortpack && !classcount) { + any.a_void = 0; /* zero */ + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + let_to_name(*invlet, FALSE), MENU_UNSELECTED); + classcount++; + } + any.a_char = ilet; + add_menu(win, obj_to_glyph(otmp), + &any, ilet, 0, ATR_NONE, doname(otmp), + MENU_UNSELECTED); + } + } + } + if (flags.sortpack) { + if (*++invlet) goto nextclass; +#ifdef WIZARD + if (--invlet != venom_inv) { + invlet = venom_inv; + goto nextclass; + } +#endif + } + end_menu(win, (char *) 0); + + n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected); + if (n > 0) { + ret = selected[0].item.a_char; + if (out_cnt) *out_cnt = selected[0].count; + free((genericptr_t)selected); + } else + ret = !n ? '\0' : '\033'; /* cancelled */ + + return ret; +} + +/* + * If lets == NULL or "", list all objects in the inventory. Otherwise, + * list all objects with object classes that match the order in lets. + * + * Returns the letter identifier of a selected item, or 0 if nothing + * was selected. + */ +char +display_inventory(lets, want_reply) +register const char *lets; +boolean want_reply; +{ + return display_pickinv(lets, want_reply, (long *)0); +} + +/* + * Returns the number of unpaid items within the given list. This includes + * contained objects. + */ +int +count_unpaid(list) + struct obj *list; +{ + int count = 0; + + while (list) { + if (list->unpaid) count++; + if (Has_contents(list)) + count += count_unpaid(list->cobj); + list = list->nobj; + } + return count; +} + +/* + * Returns the number of items with b/u/c/unknown within the given list. + * This does NOT include contained objects. + */ +int +count_buc(list, type) + struct obj *list; + int type; +{ + int count = 0; + + while (list) { + if (Role_if(PM_PRIEST)) list->bknown = TRUE; + switch(type) { + case BUC_BLESSED: + if (list->oclass != COIN_CLASS && list->bknown && list->blessed) + count++; + break; + case BUC_CURSED: + if (list->oclass != COIN_CLASS && list->bknown && list->cursed) + count++; + break; + case BUC_UNCURSED: + if (list->oclass != COIN_CLASS && + list->bknown && !list->blessed && !list->cursed) + count++; + break; + case BUC_UNKNOWN: + if (list->oclass != COIN_CLASS && !list->bknown) + count++; + break; + default: + impossible("need count of curse status %d?", type); + return 0; + } + list = list->nobj; + } + return count; +} + +STATIC_OVL void +dounpaid() +{ + winid win; + struct obj *otmp, *marker; + register char ilet; + char *invlet = flags.inv_order; + int classcount, count, num_so_far; + int save_unpaid = 0; /* lint init */ + long cost, totcost; + + count = count_unpaid(invent); + + if (count == 1) { + marker = (struct obj *) 0; + otmp = find_unpaid(invent, &marker); + + /* see if the unpaid item is in the top level inventory */ + for (marker = invent; marker; marker = marker->nobj) + if (marker == otmp) break; + + pline("%s", xprname(otmp, distant_name(otmp, doname), + marker ? otmp->invlet : CONTAINED_SYM, + TRUE, unpaid_cost(otmp), 0L)); + return; + } + + win = create_nhwindow(NHW_MENU); + cost = totcost = 0; + num_so_far = 0; /* count of # printed so far */ + if (!flags.invlet_constant) reassign(); + + do { + classcount = 0; + for (otmp = invent; otmp; otmp = otmp->nobj) { + ilet = otmp->invlet; + if (otmp->unpaid) { + if (!flags.sortpack || otmp->oclass == *invlet) { + if (flags.sortpack && !classcount) { + putstr(win, 0, let_to_name(*invlet, TRUE)); + classcount++; + } + + totcost += cost = unpaid_cost(otmp); + /* suppress "(unpaid)" suffix */ + save_unpaid = otmp->unpaid; + otmp->unpaid = 0; + putstr(win, 0, xprname(otmp, distant_name(otmp, doname), + ilet, TRUE, cost, 0L)); + otmp->unpaid = save_unpaid; + num_so_far++; + } + } + } + } while (flags.sortpack && (*++invlet)); + + if (count > num_so_far) { + /* something unpaid is contained */ + if (flags.sortpack) + putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE)); + /* + * Search through the container objects in the inventory for + * unpaid items. The top level inventory items have already + * been listed. + */ + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (Has_contents(otmp)) { + marker = (struct obj *) 0; /* haven't found any */ + while (find_unpaid(otmp->cobj, &marker)) { + totcost += cost = unpaid_cost(marker); + save_unpaid = marker->unpaid; + marker->unpaid = 0; /* suppress "(unpaid)" suffix */ + putstr(win, 0, + xprname(marker, distant_name(marker, doname), + CONTAINED_SYM, TRUE, cost, 0L)); + marker->unpaid = save_unpaid; + } + } + } + } + + putstr(win, 0, ""); + putstr(win, 0, xprname((struct obj *)0, "Total:", '*', FALSE, totcost, 0L)); + display_nhwindow(win, FALSE); + destroy_nhwindow(win); +} + + +/* query objlist callback: return TRUE if obj type matches "this_type" */ +static int this_type; + +STATIC_OVL boolean +this_type_only(obj) + struct obj *obj; +{ + return (obj->oclass == this_type); +} + +/* the 'I' command */ +int +dotypeinv() +{ + char c = '\0'; + int n, i = 0; + char *extra_types, types[BUFSZ]; + int class_count, oclass, unpaid_count, itemcount; + boolean billx = *u.ushops && doinvbill(0); + menu_item *pick_list; + boolean traditional = TRUE; + const char *prompt = "What type of object do you want an inventory of?"; + +#ifndef GOLDOBJ + if (!invent && !u.ugold && !billx) { +#else + if (!invent && !billx) { +#endif + You("aren't carrying anything."); + return 0; + } + unpaid_count = count_unpaid(invent); + if (flags.menu_style != MENU_TRADITIONAL) { + if (flags.menu_style == MENU_FULL || + flags.menu_style == MENU_PARTIAL) { + traditional = FALSE; + i = UNPAID_TYPES; + if (billx) i |= BILLED_TYPES; + n = query_category(prompt, invent, i, &pick_list, PICK_ONE); + if (!n) return 0; + this_type = c = pick_list[0].item.a_int; + free((genericptr_t) pick_list); + } + } + if (traditional) { + /* collect a list of classes of objects carried, for use as a prompt */ + types[0] = 0; + class_count = collect_obj_classes(types, invent, + FALSE, +#ifndef GOLDOBJ + (u.ugold != 0), +#endif + (boolean FDECL((*),(OBJ_P))) 0, &itemcount); + if (unpaid_count) { + Strcat(types, "u"); + class_count++; + } + if (billx) { + Strcat(types, "x"); + class_count++; + } + /* add everything not already included; user won't see these */ + extra_types = eos(types); + *extra_types++ = '\033'; + if (!unpaid_count) *extra_types++ = 'u'; + if (!billx) *extra_types++ = 'x'; + *extra_types = '\0'; /* for index() */ + for (i = 0; i < MAXOCLASSES; i++) + if (!index(types, def_oc_syms[i])) { + *extra_types++ = def_oc_syms[i]; + *extra_types = '\0'; + } + + if(class_count > 1) { + c = yn_function(prompt, types, '\0'); +#ifdef REDO + savech(c); +#endif + if(c == '\0') { + clear_nhwindow(WIN_MESSAGE); + return 0; + } + } else { + /* only one thing to itemize */ + if (unpaid_count) + c = 'u'; + else if (billx) + c = 'x'; + else + c = types[0]; + } + } + if (c == 'x') { + if (billx) + (void) doinvbill(1); + else + pline("No used-up objects on your shopping bill."); + return 0; + } + if (c == 'u') { + if (unpaid_count) + dounpaid(); + else + You("are not carrying any unpaid objects."); + return 0; + } + if (traditional) { + oclass = def_char_to_objclass(c); /* change to object class */ + if (oclass == COIN_CLASS) { + return doprgold(); + } else if (index(types, c) > index(types, '\033')) { + You("have no such objects."); + return 0; + } + this_type = oclass; + } + if (query_objlist((char *) 0, invent, + (flags.invlet_constant ? USE_INVLET : 0)|INVORDER_SORT, + &pick_list, PICK_NONE, this_type_only) > 0) + free((genericptr_t)pick_list); + return 0; +} + +/* return a string describing the dungeon feature at if there + is one worth mentioning at that location; otherwise null */ +const char * +dfeature_at(x, y, buf) +int x, y; +char *buf; +{ + struct rm *lev = &levl[x][y]; + int ltyp = lev->typ, cmap = -1; + const char *dfeature = 0; + static char altbuf[BUFSZ]; + + if (IS_DOOR(ltyp)) { + switch (lev->doormask) { + case D_NODOOR: cmap = S_ndoor; break; /* "doorway" */ + case D_ISOPEN: cmap = S_vodoor; break; /* "open door" */ + case D_BROKEN: dfeature = "broken door"; break; + default: cmap = S_vcdoor; break; /* "closed door" */ + } + /* override door description for open drawbridge */ + if (is_drawbridge_wall(x, y) >= 0) + dfeature = "open drawbridge portcullis", cmap = -1; + } else if (IS_FOUNTAIN(ltyp)) + cmap = S_fountain; /* "fountain" */ + else if (IS_THRONE(ltyp)) + cmap = S_throne; /* "opulent throne" */ + else if (is_lava(x,y)) + cmap = S_lava; /* "molten lava" */ + else if (is_ice(x,y)) + cmap = S_ice; /* "ice" */ + else if (is_pool(x,y)) + dfeature = "pool of water"; +#ifdef SINKS + else if (IS_SINK(ltyp)) + cmap = S_sink; /* "sink" */ +#endif + else if (IS_ALTAR(ltyp)) { + Sprintf(altbuf, "altar to %s (%s)", a_gname(), + align_str(Amask2align(lev->altarmask & ~AM_SHRINE))); + dfeature = altbuf; + } else if ((x == xupstair && y == yupstair) || + (x == sstairs.sx && y == sstairs.sy && sstairs.up)) + cmap = S_upstair; /* "staircase up" */ + else if ((x == xdnstair && y == ydnstair) || + (x == sstairs.sx && y == sstairs.sy && !sstairs.up)) + cmap = S_dnstair; /* "staircase down" */ + else if (x == xupladder && y == yupladder) + cmap = S_upladder; /* "ladder up" */ + else if (x == xdnladder && y == ydnladder) + cmap = S_dnladder; /* "ladder down" */ + else if (ltyp == DRAWBRIDGE_DOWN) + cmap = S_vodbridge; /* "lowered drawbridge" */ + else if (ltyp == DBWALL) + cmap = S_vcdbridge; /* "raised drawbridge" */ + else if (IS_GRAVE(ltyp)) + cmap = S_grave; /* "grave" */ + else if (ltyp == TREE) + cmap = S_tree; /* "tree" */ + else if (ltyp == IRONBARS) + dfeature = "set of iron bars"; + + if (cmap >= 0) dfeature = defsyms[cmap].explanation; + if (dfeature) Strcpy(buf, dfeature); + return dfeature; +} + +/* look at what is here; if there are many objects (5 or more), + don't show them unless obj_cnt is 0 */ +int +look_here(obj_cnt, picked_some) +int obj_cnt; /* obj_cnt > 0 implies that autopickup is in progess */ +boolean picked_some; +{ + struct obj *otmp; + struct trap *trap; + const char *verb = Blind ? "feel" : "see"; + const char *dfeature = (char *)0; + char fbuf[BUFSZ], fbuf2[BUFSZ]; + winid tmpwin; + boolean skip_objects = (obj_cnt >= 5), felt_cockatrice = FALSE; + + if (u.uswallow && u.ustuck) { + struct monst *mtmp = u.ustuck; + Sprintf(fbuf, "Contents of %s %s", + s_suffix(mon_nam(mtmp)), mbodypart(mtmp, STOMACH)); + /* Skip "Contents of " by using fbuf index 12 */ + You("%s to %s what is lying in %s.", + Blind ? "try" : "look around", verb, &fbuf[12]); + otmp = mtmp->minvent; + if (otmp) { + for ( ; otmp; otmp = otmp->nobj) { + /* If swallower is an animal, it should have become stone but... */ + if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE); + } + if (Blind) Strcpy(fbuf, "You feel"); + Strcat(fbuf,":"); + (void) display_minventory(mtmp, MINV_ALL, fbuf); + } else { + You("%s no objects here.", verb); + } + return(!!Blind); + } + if (!skip_objects && (trap = t_at(u.ux,u.uy)) && trap->tseen) + There("is %s here.", + an(defsyms[trap_to_defsym(trap->ttyp)].explanation)); + + otmp = level.objects[u.ux][u.uy]; + dfeature = dfeature_at(u.ux, u.uy, fbuf2); + if (dfeature && !strcmp(dfeature, "pool of water") && Underwater) + dfeature = 0; + + if (Blind) { + boolean drift = Is_airlevel(&u.uz) || Is_waterlevel(&u.uz); + if (dfeature && !strncmp(dfeature, "altar ", 6)) { + /* don't say "altar" twice, dfeature has more info */ + You("try to feel what is here."); + } else { + You("try to feel what is %s%s.", + drift ? "floating here" : "lying here on the ", + drift ? "" : surface(u.ux, u.uy)); + } + if (dfeature && !drift && !strcmp(dfeature, surface(u.ux,u.uy))) + dfeature = 0; /* ice already identifed */ + if (!can_reach_floor()) { + pline("But you can't reach it!"); + return(0); + } + } + + if (dfeature) + Sprintf(fbuf, "There is %s here.", an(dfeature)); + + if (!otmp || is_lava(u.ux,u.uy) || (is_pool(u.ux,u.uy) && !Underwater)) { + if (dfeature) pline(fbuf); + read_engr_at(u.ux, u.uy); /* Eric Backus */ + if (!skip_objects && (Blind || !dfeature)) + You("%s no objects here.", verb); + return(!!Blind); + } + /* we know there is something here */ + + if (skip_objects) { + if (dfeature) pline(fbuf); + read_engr_at(u.ux, u.uy); /* Eric Backus */ + There("are %s%s objects here.", + (obj_cnt <= 10) ? "several" : "many", + picked_some ? " more" : ""); + } else if (!otmp->nexthere) { + /* only one object */ + if (dfeature) pline(fbuf); + read_engr_at(u.ux, u.uy); /* Eric Backus */ +#ifdef INVISIBLE_OBJECTS + if (otmp->oinvis && !See_invisible) verb = "feel"; +#endif + You("%s here %s.", verb, doname(otmp)); + if (otmp->otyp == CORPSE) feel_cockatrice(otmp, FALSE); + } else { + display_nhwindow(WIN_MESSAGE, FALSE); + tmpwin = create_nhwindow(NHW_MENU); + if(dfeature) { + putstr(tmpwin, 0, fbuf); + putstr(tmpwin, 0, ""); + } + putstr(tmpwin, 0, Blind ? "Things that you feel here:" : + "Things that are here:"); + for ( ; otmp; otmp = otmp->nexthere) { + if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) { + char buf[BUFSZ]; + felt_cockatrice = TRUE; + Strcpy(buf, doname(otmp)); + Strcat(buf, "..."); + putstr(tmpwin, 0, buf); + break; + } + putstr(tmpwin, 0, doname(otmp)); + } + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + if (felt_cockatrice) feel_cockatrice(otmp, FALSE); + read_engr_at(u.ux, u.uy); /* Eric Backus */ + } + return(!!Blind); +} + +/* explicilty look at what is here, including all objects */ +int +dolook() +{ + return look_here(0, FALSE); +} + +boolean +will_feel_cockatrice(otmp, force_touch) +struct obj *otmp; +boolean force_touch; +{ + if ((Blind || force_touch) && !uarmg && !Stone_resistance && + (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]))) + return TRUE; + return FALSE; +} + +void +feel_cockatrice(otmp, force_touch) +struct obj *otmp; +boolean force_touch; +{ + char kbuf[BUFSZ]; + + if (will_feel_cockatrice(otmp, force_touch)) { + if(poly_when_stoned(youmonst.data)) + You("touched the %s corpse with your bare %s.", + mons[otmp->corpsenm].mname, makeplural(body_part(HAND))); + else + pline("Touching the %s corpse is a fatal mistake...", + mons[otmp->corpsenm].mname); + Sprintf(kbuf, "%s corpse", an(mons[otmp->corpsenm].mname)); + instapetrify(kbuf); + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +void +stackobj(obj) +struct obj *obj; +{ + struct obj *otmp; + + for(otmp = level.objects[obj->ox][obj->oy]; otmp; otmp = otmp->nexthere) + if(otmp != obj && merged(&obj,&otmp)) + break; + return; +} + +STATIC_OVL boolean +mergable(otmp, obj) /* returns TRUE if obj & otmp can be merged */ + register struct obj *otmp, *obj; +{ + if (obj->otyp != otmp->otyp) return FALSE; +#ifdef GOLDOBJ + /* coins of the same kind will always merge */ + if (obj->oclass == COIN_CLASS) return TRUE; +#endif + if (obj->unpaid != otmp->unpaid || + obj->spe != otmp->spe || obj->dknown != otmp->dknown || + (obj->bknown != otmp->bknown && !Role_if(PM_PRIEST)) || + obj->cursed != otmp->cursed || obj->blessed != otmp->blessed || + obj->no_charge != otmp->no_charge || + obj->obroken != otmp->obroken || + obj->otrapped != otmp->otrapped || + obj->lamplit != otmp->lamplit || +#ifdef INVISIBLE_OBJECTS + obj->oinvis != otmp->oinvis || +#endif + obj->greased != otmp->greased || + obj->oeroded != otmp->oeroded || + obj->oeroded2 != otmp->oeroded2 || + obj->bypass != otmp->bypass) + return(FALSE); + + if ((obj->oclass==WEAPON_CLASS || obj->oclass==ARMOR_CLASS) && + (obj->oerodeproof!=otmp->oerodeproof || obj->rknown!=otmp->rknown)) + return FALSE; + + if (obj->oclass == FOOD_CLASS && (obj->oeaten != otmp->oeaten || + obj->orotten != otmp->orotten)) + return(FALSE); + + if (obj->otyp == CORPSE || obj->otyp == EGG || obj->otyp == TIN) { + if (obj->corpsenm != otmp->corpsenm) + return FALSE; + } + + /* hatching eggs don't merge; ditto for revivable corpses */ + if ((obj->otyp == EGG && (obj->timed || otmp->timed)) || + (obj->otyp == CORPSE && otmp->corpsenm >= LOW_PM && + is_reviver(&mons[otmp->corpsenm]))) + return FALSE; + + /* allow candle merging only if their ages are close */ + /* see begin_burn() for a reference for the magic "25" */ + if (Is_candle(obj) && obj->age/25 != otmp->age/25) + return(FALSE); + + /* burning potions of oil never merge */ + if (obj->otyp == POT_OIL && obj->lamplit) + return FALSE; + + /* don't merge surcharged item with base-cost item */ + if (obj->unpaid && !same_price(obj, otmp)) + return FALSE; + + /* if they have names, make sure they're the same */ + if ( (obj->onamelth != otmp->onamelth && + ((obj->onamelth && otmp->onamelth) || obj->otyp == CORPSE) + ) || + (obj->onamelth && otmp->onamelth && + strncmp(ONAME(obj), ONAME(otmp), (int)obj->onamelth))) + return FALSE; + + /* for the moment, any additional information is incompatible */ + if (obj->oxlth || otmp->oxlth) return FALSE; + + if(obj->oartifact != otmp->oartifact) return FALSE; + + if(obj->known == otmp->known || + !objects[otmp->otyp].oc_uses_known) { + return((boolean)(objects[obj->otyp].oc_merge)); + } else return(FALSE); +} + +int +doprgold() +{ + /* the messages used to refer to "carrying gold", but that didn't + take containers into account */ +#ifndef GOLDOBJ + if(!u.ugold) + Your("wallet is empty."); + else + Your("wallet contains %ld gold piece%s.", u.ugold, plur(u.ugold)); +#else + long umoney = money_cnt(invent); + if(!umoney) + Your("wallet is empty."); + else + Your("wallet contains %ld %s.", umoney, currency(umoney)); +#endif + shopper_financial_report(); + return 0; +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +doprwep() +{ + if (!uwep) { + You("are empty %s.", body_part(HANDED)); + } else { + prinv((char *)0, uwep, 0L); + if (u.twoweap) prinv((char *)0, uswapwep, 0L); + } + return 0; +} + +int +doprarm() +{ + if(!wearing_armor()) + You("are not wearing any armor."); + else { +#ifdef TOURIST + char lets[8]; +#else + char lets[7]; +#endif + register int ct = 0; + +#ifdef TOURIST + if(uarmu) lets[ct++] = obj_to_let(uarmu); +#endif + if(uarm) lets[ct++] = obj_to_let(uarm); + if(uarmc) lets[ct++] = obj_to_let(uarmc); + if(uarmh) lets[ct++] = obj_to_let(uarmh); + if(uarms) lets[ct++] = obj_to_let(uarms); + if(uarmg) lets[ct++] = obj_to_let(uarmg); + if(uarmf) lets[ct++] = obj_to_let(uarmf); + lets[ct] = 0; + (void) display_inventory(lets, FALSE); + } + return 0; +} + +int +doprring() +{ + if(!uleft && !uright) + You("are not wearing any rings."); + else { + char lets[3]; + register int ct = 0; + + if(uleft) lets[ct++] = obj_to_let(uleft); + if(uright) lets[ct++] = obj_to_let(uright); + lets[ct] = 0; + (void) display_inventory(lets, FALSE); + } + return 0; +} + +int +dopramulet() +{ + if (!uamul) + You("are not wearing an amulet."); + else + prinv((char *)0, uamul, 0L); + return 0; +} + +STATIC_OVL boolean +tool_in_use(obj) +struct obj *obj; +{ + if ((obj->owornmask & (W_TOOL +#ifdef STEED + | W_SADDLE +#endif + )) != 0L) return TRUE; + if (obj->oclass != TOOL_CLASS) return FALSE; + return (boolean)(obj == uwep || obj->lamplit || + (obj->otyp == LEASH && obj->leashmon)); +} + +int +doprtool() +{ + struct obj *otmp; + int ct = 0; + char lets[52+1]; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (tool_in_use(otmp)) + lets[ct++] = obj_to_let(otmp); + lets[ct] = '\0'; + if (!ct) You("are not using any tools."); + else (void) display_inventory(lets, FALSE); + return 0; +} + +/* '*' command; combines the ')' + '[' + '=' + '"' + '(' commands; + show inventory of all currently wielded, worn, or used objects */ +int +doprinuse() +{ + struct obj *otmp; + int ct = 0; + char lets[52+1]; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (is_worn(otmp) || tool_in_use(otmp)) + lets[ct++] = obj_to_let(otmp); + lets[ct] = '\0'; + if (!ct) You("are not wearing or wielding anything."); + else (void) display_inventory(lets, FALSE); + return 0; +} + +/* + * uses up an object that's on the floor, charging for it as necessary + */ +void +useupf(obj, numused) +register struct obj *obj; +long numused; +{ + register struct obj *otmp; + boolean at_u = (obj->ox == u.ux && obj->oy == u.uy); + + /* burn_floor_paper() keeps an object pointer that it tries to + * useupf() multiple times, so obj must survive if plural */ + if (obj->quan > numused) + otmp = splitobj(obj, numused); + else + otmp = obj; + if(costly_spot(otmp->ox, otmp->oy)) { + if(index(u.urooms, *in_rooms(otmp->ox, otmp->oy, 0))) + addtobill(otmp, FALSE, FALSE, FALSE); + else (void)stolen_value(otmp, otmp->ox, otmp->oy, FALSE, FALSE); + } + delobj(otmp); + if (at_u && u.uundetected && hides_under(youmonst.data)) + u.uundetected = OBJ_AT(u.ux, u.uy); +} + +#endif /* OVLB */ + + +#ifdef OVL1 + +/* + * Conversion from a class to a string for printing. + * This must match the object class order. + */ +STATIC_VAR NEARDATA const char *names[] = { 0, + "Illegal objects", "Weapons", "Armor", "Rings", "Amulets", + "Tools", "Comestibles", "Potions", "Scrolls", "Spellbooks", + "Wands", "Coins", "Gems", "Boulders/Statues", "Iron balls", + "Chains", "Venoms" +}; + +static NEARDATA const char oth_symbols[] = { + CONTAINED_SYM, + '\0' +}; + +static NEARDATA const char *oth_names[] = { + "Bagged/Boxed items" +}; + +static NEARDATA char *invbuf = (char *)0; +static NEARDATA unsigned invbufsiz = 0; + +char * +let_to_name(let,unpaid) +char let; +boolean unpaid; +{ + const char *class_name; + const char *pos; + int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0; + unsigned len; + + if (oclass) + class_name = names[oclass]; + else if ((pos = index(oth_symbols, let)) != 0) + class_name = oth_names[pos - oth_symbols]; + else + class_name = names[0]; + + len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof ""); + if (len > invbufsiz) { + if (invbuf) free((genericptr_t)invbuf); + invbufsiz = len + 10; /* add slop to reduce incremental realloc */ + invbuf = (char *) alloc(invbufsiz); + } + if (unpaid) + Strcat(strcpy(invbuf, "Unpaid "), class_name); + else + Strcpy(invbuf, class_name); + return invbuf; +} + +void +free_invbuf() +{ + if (invbuf) free((genericptr_t)invbuf), invbuf = (char *)0; + invbufsiz = 0; +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +reassign() +{ + register int i; + register struct obj *obj; + + for(obj = invent, i = 0; obj; obj = obj->nobj, i++) + obj->invlet = (i < 26) ? ('a'+i) : ('A'+i-26); + lastinvnr = i; +} + +#endif /* OVLB */ +#ifdef OVL1 + +int +doorganize() /* inventory organizer by Del Lamb */ +{ + struct obj *obj, *otmp; + register int ix, cur; + register char let; + char alphabet[52+1], buf[52+1]; + char qbuf[QBUFSZ]; + char allowall[2]; + const char *adj_type; + + if (!flags.invlet_constant) reassign(); + /* get a pointer to the object the user wants to organize */ + allowall[0] = ALL_CLASSES; allowall[1] = '\0'; + if (!(obj = getobj(allowall,"adjust"))) return(0); + + /* initialize the list with all upper and lower case letters */ + for (let = 'a', ix = 0; let <= 'z';) alphabet[ix++] = let++; + for (let = 'A', ix = 26; let <= 'Z';) alphabet[ix++] = let++; + alphabet[52] = 0; + + /* blank out all the letters currently in use in the inventory */ + /* except those that will be merged with the selected object */ + for (otmp = invent; otmp; otmp = otmp->nobj) + if (otmp != obj && !mergable(otmp,obj)) { + if (otmp->invlet <= 'Z') + alphabet[(otmp->invlet) - 'A' + 26] = ' '; + else alphabet[(otmp->invlet) - 'a'] = ' '; + } + + /* compact the list by removing all the blanks */ + for (ix = cur = 0; ix <= 52; ix++) + if (alphabet[ix] != ' ') buf[cur++] = alphabet[ix]; + + /* and by dashing runs of letters */ + if(cur > 5) compactify(buf); + + /* get new letter to use as inventory letter */ + for (;;) { + Sprintf(qbuf, "Adjust letter to what [%s]?",buf); + let = yn_function(qbuf, (char *)0, '\0'); + if(index(quitchars,let)) { + pline(Never_mind); + return(0); + } + if (let == '@' || !letter(let)) + pline("Select an inventory slot letter."); + else + break; + } + + /* change the inventory and print the resulting item */ + adj_type = "Moving:"; + + /* + * don't use freeinv/addinv to avoid double-touching artifacts, + * dousing lamps, losing luck, cursing loadstone, etc. + */ + extract_nobj(obj, &invent); + + for (otmp = invent; otmp;) + if (merged(&otmp,&obj)) { + adj_type = "Merging:"; + obj = otmp; + otmp = otmp->nobj; + extract_nobj(obj, &invent); + } else { + if (otmp->invlet == let) { + adj_type = "Swapping:"; + otmp->invlet = obj->invlet; + } + otmp = otmp->nobj; + } + + /* inline addinv (assuming flags.invlet_constant and !merged) */ + obj->invlet = let; + obj->nobj = invent; /* insert at beginning */ + obj->where = OBJ_INVENT; + invent = obj; + reorder_invent(); + + prinv(adj_type, obj, 0L); + update_inventory(); + return(0); +} + +/* common to display_minventory and display_cinventory */ +STATIC_OVL void +invdisp_nothing(hdr, txt) +const char *hdr, *txt; +{ + winid win; + anything any; + menu_item *selected; + + any.a_void = 0; + win = create_nhwindow(NHW_MENU); + start_menu(win); + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, hdr, MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, txt, MENU_UNSELECTED); + end_menu(win, (char *)0); + if (select_menu(win, PICK_NONE, &selected) > 0) + free((genericptr_t)selected); + destroy_nhwindow(win); + return; +} + +/* query_objlist callback: return things that could possibly be worn/wielded */ +STATIC_OVL boolean +worn_wield_only(obj) +struct obj *obj; +{ + return (obj->oclass == WEAPON_CLASS + || obj->oclass == ARMOR_CLASS + || obj->oclass == AMULET_CLASS + || obj->oclass == RING_CLASS + || obj->oclass == TOOL_CLASS); +} + +/* + * Display a monster's inventory. + * Returns a pointer to the object from the monster's inventory selected + * or NULL if nothing was selected. + * + * By default, only worn and wielded items are displayed. The caller + * can pick one. Modifier flags are: + * + * MINV_NOLET - nothing selectable + * MINV_ALL - display all inventory + */ +struct obj * +display_minventory(mon, dflags, title) +register struct monst *mon; +int dflags; +char *title; +{ + struct obj *ret; +#ifndef GOLDOBJ + struct obj m_gold; +#endif + char tmp[QBUFSZ]; + int n; + menu_item *selected = 0; +#ifndef GOLDOBJ + int do_all = (dflags & MINV_ALL) != 0, + do_gold = (do_all && mon->mgold); +#else + int do_all = (dflags & MINV_ALL) != 0; +#endif + + Sprintf(tmp,"%s %s:", s_suffix(noit_Monnam(mon)), + do_all ? "possessions" : "armament"); + +#ifndef GOLDOBJ + if (do_all ? (mon->minvent || mon->mgold) +#else + if (do_all ? (mon->minvent != 0) +#endif + : (mon->misc_worn_check || MON_WEP(mon))) { + /* Fool the 'weapon in hand' routine into + * displaying 'weapon in claw', etc. properly. + */ + youmonst.data = mon->data; + +#ifndef GOLDOBJ + if (do_gold) { + /* + * Make temporary gold object and insert at the head of + * the mon's inventory. We can get away with using a + * stack variable object because monsters don't carry + * gold in their inventory, so it won't merge. + */ + m_gold = zeroobj; + m_gold.otyp = GOLD_PIECE; m_gold.oclass = COIN_CLASS; + m_gold.quan = mon->mgold; m_gold.dknown = 1; + m_gold.where = OBJ_FREE; + /* we had better not merge and free this object... */ + if (add_to_minv(mon, &m_gold)) + panic("display_minventory: static object freed."); + } + +#endif + n = query_objlist(title ? title : tmp, mon->minvent, INVORDER_SORT, &selected, + (dflags & MINV_NOLET) ? PICK_NONE : PICK_ONE, + do_all ? allow_all : worn_wield_only); + +#ifndef GOLDOBJ + if (do_gold) obj_extract_self(&m_gold); +#endif + + set_uasmon(); + } else { + invdisp_nothing(title ? title : tmp, "(none)"); + n = 0; + } + + if (n > 0) { + ret = selected[0].item.a_obj; + free((genericptr_t)selected); +#ifndef GOLDOBJ + /* + * Unfortunately, we can't return a pointer to our temporary + * gold object. We'll have to work out a scheme where this + * can happen. Maybe even put gold in the inventory list... + */ + if (ret == &m_gold) ret = (struct obj *) 0; +#endif + } else + ret = (struct obj *) 0; + return ret; +} + +/* + * Display the contents of a container in inventory style. + * Currently, this is only used for statues, via wand of probing. + */ +struct obj * +display_cinventory(obj) +register struct obj *obj; +{ + struct obj *ret; + char tmp[QBUFSZ]; + int n; + menu_item *selected = 0; + + Sprintf(tmp,"Contents of %s:", doname(obj)); + + if (obj->cobj) { + n = query_objlist(tmp, obj->cobj, INVORDER_SORT, &selected, + PICK_NONE, allow_all); + } else { + invdisp_nothing(tmp, "(empty)"); + n = 0; + } + if (n > 0) { + ret = selected[0].item.a_obj; + free((genericptr_t)selected); + } else + ret = (struct obj *) 0; + return ret; +} + +/* query objlist callback: return TRUE if obj is at given location */ +static coord only; + +STATIC_OVL boolean +only_here(obj) + struct obj *obj; +{ + return (obj->ox == only.x && obj->oy == only.y); +} + +/* + * Display a list of buried items in inventory style. Return a non-zero + * value if there were items at that spot. + * + * Currently, this is only used with a wand of probing zapped downwards. + */ +int +display_binventory(x, y, as_if_seen) +int x, y; +boolean as_if_seen; +{ + struct obj *obj; + menu_item *selected = 0; + int n; + + /* count # of objects here */ + for (n = 0, obj = level.buriedobjlist; obj; obj = obj->nobj) + if (obj->ox == x && obj->oy == y) { + if (as_if_seen) obj->dknown = 1; + n++; + } + + if (n) { + only.x = x; + only.y = y; + if (query_objlist("Things that are buried here:", + level.buriedobjlist, INVORDER_SORT, + &selected, PICK_NONE, only_here) > 0) + free((genericptr_t)selected); + only.x = only.y = 0; + } + return n; +} + +#endif /* OVL1 */ + +/*invent.c*/ diff --git a/src/light.c b/src/light.c new file mode 100644 index 0000000..d0029e7 --- /dev/null +++ b/src/light.c @@ -0,0 +1,626 @@ +/* SCCS Id: @(#)light.c 3.4 1997/04/10 */ +/* Copyright (c) Dean Luick, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" /* for checking save modes */ + +/* + * Mobile light sources. + * + * This implementation minimizes memory at the expense of extra + * recalculations. + * + * Light sources are "things" that have a physical position and range. + * They have a type, which gives us information about them. Currently + * they are only attached to objects and monsters. Note well: the + * polymorphed-player handling assumes that both youmonst.m_id and + * youmonst.mx will always remain 0. + * + * Light sources, like timers, either follow game play (RANGE_GLOBAL) or + * stay on a level (RANGE_LEVEL). Light sources are unique by their + * (type, id) pair. For light sources attached to objects, this id + * is a pointer to the object. + * + * The major working function is do_light_sources(). It is called + * when the vision system is recreating its "could see" array. Here + * we add a flag (TEMP_LIT) to the array for all locations that are lit + * via a light source. The bad part of this is that we have to + * re-calculate the LOS of each light source every time the vision + * system runs. Even if the light sources and any topology (vision blocking + * positions) have not changed. The good part is that no extra memory + * is used, plus we don't have to figure out how far the sources have moved, + * or if the topology has changed. + * + * The structure of the save/restore mechanism is amazingly similar to + * the timer save/restore. This is because they both have the same + * principals of having pointers into objects that must be recalculated + * across saves and restores. + */ + +#ifdef OVL3 + +/* flags */ +#define LSF_SHOW 0x1 /* display the light source */ +#define LSF_NEEDS_FIXUP 0x2 /* need oid fixup */ + +static light_source *light_base = 0; + +STATIC_DCL void FDECL(write_ls, (int, light_source *)); +STATIC_DCL int FDECL(maybe_write_ls, (int, int, BOOLEAN_P)); + +/* imported from vision.c, for small circles */ +extern char circle_data[]; +extern char circle_start[]; + + +/* Create a new light source. */ +void +new_light_source(x, y, range, type, id) + xchar x, y; + int range, type; + genericptr_t id; +{ + light_source *ls; + + if (range > MAX_RADIUS || range < 1) { + impossible("new_light_source: illegal range %d", range); + return; + } + + ls = (light_source *) alloc(sizeof(light_source)); + + ls->next = light_base; + ls->x = x; + ls->y = y; + ls->range = range; + ls->type = type; + ls->id = id; + ls->flags = 0; + light_base = ls; + + vision_full_recalc = 1; /* make the source show up */ +} + +/* + * Delete a light source. This assumes only one light source is attached + * to an object at a time. + */ +void +del_light_source(type, id) + int type; + genericptr_t id; +{ + light_source *curr, *prev; + genericptr_t tmp_id; + + /* need to be prepared for dealing a with light source which + has only been partially restored during a level change + (in particular: chameleon vs prot. from shape changers) */ + switch (type) { + case LS_OBJECT: tmp_id = (genericptr_t)(((struct obj *)id)->o_id); + break; + case LS_MONSTER: tmp_id = (genericptr_t)(((struct monst *)id)->m_id); + break; + default: tmp_id = 0; + break; + } + + for (prev = 0, curr = light_base; curr; prev = curr, curr = curr->next) { + if (curr->type != type) continue; + if (curr->id == ((curr->flags & LSF_NEEDS_FIXUP) ? tmp_id : id)) { + if (prev) + prev->next = curr->next; + else + light_base = curr->next; + + free((genericptr_t)curr); + vision_full_recalc = 1; + return; + } + } + impossible("del_light_source: not found type=%d, id=0x%lx", type, (long)id); +} + +/* Mark locations that are temporarily lit via mobile light sources. */ +void +do_light_sources(cs_rows) + char **cs_rows; +{ + int x, y, min_x, max_x, max_y, offset; + char *limits; + short at_hero_range = 0; + light_source *ls; + char *row; + + for (ls = light_base; ls; ls = ls->next) { + ls->flags &= ~LSF_SHOW; + + /* + * Check for moved light sources. It may be possible to + * save some effort if an object has not moved, but not in + * the current setup -- we need to recalculate for every + * vision recalc. + */ + if (ls->type == LS_OBJECT) { + if (get_obj_location((struct obj *) ls->id, &ls->x, &ls->y, 0)) + ls->flags |= LSF_SHOW; + } else if (ls->type == LS_MONSTER) { + if (get_mon_location((struct monst *) ls->id, &ls->x, &ls->y, 0)) + ls->flags |= LSF_SHOW; + } + + /* minor optimization: don't bother with duplicate light sources */ + /* at hero */ + if (ls->x == u.ux && ls->y == u.uy) { + if (at_hero_range >= ls->range) + ls->flags &= ~LSF_SHOW; + else + at_hero_range = ls->range; + } + + if (ls->flags & LSF_SHOW) { + /* + * Walk the points in the circle and see if they are + * visible from the center. If so, mark'em. + * + * Kevin's tests indicated that doing this brute-force + * method is faster for radius <= 3 (or so). + */ + limits = circle_ptr(ls->range); + if ((max_y = (ls->y + ls->range)) >= ROWNO) max_y = ROWNO-1; + if ((y = (ls->y - ls->range)) < 0) y = 0; + for (; y <= max_y; y++) { + row = cs_rows[y]; + offset = limits[abs(y - ls->y)]; + if ((min_x = (ls->x - offset)) < 0) min_x = 0; + if ((max_x = (ls->x + offset)) >= COLNO) max_x = COLNO-1; + + if (ls->x == u.ux && ls->y == u.uy) { + /* + * If the light source is located at the hero, then + * we can use the COULD_SEE bits already calcualted + * by the vision system. More importantly than + * this optimization, is that it allows the vision + * system to correct problems with clear_path(). + * The function clear_path() is a simple LOS + * path checker that doesn't go out of its way + * make things look "correct". The vision system + * does this. + */ + for (x = min_x; x <= max_x; x++) + if (row[x] & COULD_SEE) + row[x] |= TEMP_LIT; + } else { + for (x = min_x; x <= max_x; x++) + if ((ls->x == x && ls->y == y) + || clear_path((int)ls->x, (int) ls->y, x, y)) + row[x] |= TEMP_LIT; + } + } + } + } +} + +/* (mon->mx == 0) implies migrating */ +#define mon_is_local(mon) ((mon)->mx > 0) + +struct monst * +find_mid(nid, fmflags) +unsigned nid; +unsigned fmflags; +{ + struct monst *mtmp; + + if (!nid) + return &youmonst; + if (fmflags & FM_FMON) + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && mtmp->m_id == nid) return mtmp; + if (fmflags & FM_MIGRATE) + for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == nid) return mtmp; + if (fmflags & FM_MYDOGS) + for (mtmp = mydogs; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == nid) return mtmp; + return (struct monst *) 0; +} + +/* Save all light sources of the given range. */ +void +save_light_sources(fd, mode, range) + int fd, mode, range; +{ + int count, actual, is_global; + light_source **prev, *curr; + + if (perform_bwrite(mode)) { + count = maybe_write_ls(fd, range, FALSE); + bwrite(fd, (genericptr_t) &count, sizeof count); + actual = maybe_write_ls(fd, range, TRUE); + if (actual != count) + panic("counted %d light sources, wrote %d! [range=%d]", + count, actual, range); + } + + if (release_data(mode)) { + for (prev = &light_base; (curr = *prev) != 0; ) { + if (!curr->id) { + impossible("save_light_sources: no id! [range=%d]", range); + is_global = 0; + } else + switch (curr->type) { + case LS_OBJECT: + is_global = !obj_is_local((struct obj *)curr->id); + break; + case LS_MONSTER: + is_global = !mon_is_local((struct monst *)curr->id); + break; + default: + is_global = 0; + impossible("save_light_sources: bad type (%d) [range=%d]", + curr->type, range); + break; + } + /* if global and not doing local, or vice versa, remove it */ + if (is_global ^ (range == RANGE_LEVEL)) { + *prev = curr->next; + free((genericptr_t)curr); + } else { + prev = &(*prev)->next; + } + } + } +} + +/* + * Pull in the structures from disk, but don't recalculate the object + * pointers. + */ +void +restore_light_sources(fd) + int fd; +{ + int count; + light_source *ls; + + /* restore elements */ + mread(fd, (genericptr_t) &count, sizeof count); + + while (count-- > 0) { + ls = (light_source *) alloc(sizeof(light_source)); + mread(fd, (genericptr_t) ls, sizeof(light_source)); + ls->next = light_base; + light_base = ls; + } +} + +/* Relink all lights that are so marked. */ +void +relink_light_sources(ghostly) + boolean ghostly; +{ + char which; + unsigned nid; + light_source *ls; + + for (ls = light_base; ls; ls = ls->next) { + if (ls->flags & LSF_NEEDS_FIXUP) { + if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { + if (ghostly) { + if (!lookup_id_mapping((unsigned)ls->id, &nid)) + impossible("relink_light_sources: no id mapping"); + } else + nid = (unsigned) ls->id; + if (ls->type == LS_OBJECT) { + which = 'o'; + ls->id = (genericptr_t) find_oid(nid); + } else { + which = 'm'; + ls->id = (genericptr_t) find_mid(nid, FM_EVERYWHERE); + } + if (!ls->id) + impossible("relink_light_sources: cant find %c_id %d", + which, nid); + } else + impossible("relink_light_sources: bad type (%d)", ls->type); + + ls->flags &= ~LSF_NEEDS_FIXUP; + } + } +} + +/* + * Part of the light source save routine. Count up the number of light + * sources that would be written. If write_it is true, actually write + * the light source out. + */ +STATIC_OVL int +maybe_write_ls(fd, range, write_it) + int fd, range; + boolean write_it; +{ + int count = 0, is_global; + light_source *ls; + + for (ls = light_base; ls; ls = ls->next) { + if (!ls->id) { + impossible("maybe_write_ls: no id! [range=%d]", range); + continue; + } + switch (ls->type) { + case LS_OBJECT: + is_global = !obj_is_local((struct obj *)ls->id); + break; + case LS_MONSTER: + is_global = !mon_is_local((struct monst *)ls->id); + break; + default: + is_global = 0; + impossible("maybe_write_ls: bad type (%d) [range=%d]", + ls->type, range); + break; + } + /* if global and not doing local, or vice versa, count it */ + if (is_global ^ (range == RANGE_LEVEL)) { + count++; + if (write_it) write_ls(fd, ls); + } + } + + return count; +} + +/* Write a light source structure to disk. */ +STATIC_OVL void +write_ls(fd, ls) + int fd; + light_source *ls; +{ + genericptr_t arg_save; + struct obj *otmp; + struct monst *mtmp; + + if (ls->type == LS_OBJECT || ls->type == LS_MONSTER) { + if (ls->flags & LSF_NEEDS_FIXUP) + bwrite(fd, (genericptr_t)ls, sizeof(light_source)); + else { + /* replace object pointer with id for write, then put back */ + arg_save = ls->id; + if (ls->type == LS_OBJECT) { + otmp = (struct obj *)ls->id; + ls->id = (genericptr_t)otmp->o_id; +#ifdef DEBUG + if (find_oid((unsigned)ls->id) != otmp) + panic("write_ls: can't find obj #%u!", (unsigned)ls->id); +#endif + } else { /* ls->type == LS_MONSTER */ + mtmp = (struct monst *)ls->id; + ls->id = (genericptr_t)mtmp->m_id; +#ifdef DEBUG + if (find_mid((unsigned)ls->id, FM_EVERYWHERE) != mtmp) + panic("write_ls: can't find mon #%u!", (unsigned)ls->id); +#endif + } + ls->flags |= LSF_NEEDS_FIXUP; + bwrite(fd, (genericptr_t)ls, sizeof(light_source)); + ls->id = arg_save; + ls->flags &= ~LSF_NEEDS_FIXUP; + } + } else { + impossible("write_ls: bad type (%d)", ls->type); + } +} + +/* Change light source's ID from src to dest. */ +void +obj_move_light_source(src, dest) + struct obj *src, *dest; +{ + light_source *ls; + + for (ls = light_base; ls; ls = ls->next) + if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src) + ls->id = (genericptr_t) dest; + src->lamplit = 0; + dest->lamplit = 1; +} + +/* return true if there exist any light sources */ +boolean +any_light_source() +{ + return light_base != (light_source *) 0; +} + +/* + * Snuff an object light source if at (x,y). This currently works + * only for burning light sources. + */ +void +snuff_light_source(x, y) + int x, y; +{ + light_source *ls; + struct obj *obj; + + for (ls = light_base; ls; ls = ls->next) + /* + Is this position check valid??? Can I assume that the positions + will always be correct because the objects would have been + updated with the last vision update? [Is that recent enough???] + */ + if (ls->type == LS_OBJECT && ls->x == x && ls->y == y) { + obj = (struct obj *) ls->id; + if (obj_is_burning(obj)) { + /* The only way to snuff Sunsword is to unwield it. Darkness + * scrolls won't affect it. (If we got here because it was + * dropped or thrown inside a monster, this won't matter anyway + * because it will go out when dropped.) + */ + if (artifact_light(obj)) continue; + end_burn(obj, obj->otyp != MAGIC_LAMP); + /* + * The current ls element has just been removed (and + * ls->next is now invalid). Return assuming that there + * is only one light source attached to each object. + */ + return; + } + } +} + +/* Return TRUE if object sheds any light at all. */ +boolean +obj_sheds_light(obj) + struct obj *obj; +{ + /* so far, only burning objects shed light */ + return obj_is_burning(obj); +} + +/* Return TRUE if sheds light AND will be snuffed by end_burn(). */ +boolean +obj_is_burning(obj) + struct obj *obj; +{ + return (obj->lamplit && + (obj->otyp == MAGIC_LAMP || ignitable(obj) || artifact_light(obj))); +} + +/* copy the light source(s) attachted to src, and attach it/them to dest */ +void +obj_split_light_source(src, dest) + struct obj *src, *dest; +{ + light_source *ls, *new_ls; + + for (ls = light_base; ls; ls = ls->next) + if (ls->type == LS_OBJECT && ls->id == (genericptr_t) src) { + /* + * Insert the new source at beginning of list. This will + * never interfere us walking down the list - we are already + * past the insertion point. + */ + new_ls = (light_source *) alloc(sizeof(light_source)); + *new_ls = *ls; + if (Is_candle(src)) { + /* split candles may emit less light than original group */ + ls->range = candle_light_range(src); + new_ls->range = candle_light_range(dest); + vision_full_recalc = 1; /* in case range changed */ + } + new_ls->id = (genericptr_t) dest; + new_ls->next = light_base; + light_base = new_ls; + dest->lamplit = 1; /* now an active light source */ + } +} + +/* light source `src' has been folded into light source `dest'; + used for merging lit candles and adding candle(s) to lit candelabrum */ +void +obj_merge_light_sources(src, dest) +struct obj *src, *dest; +{ + light_source *ls; + + /* src == dest implies adding to candelabrum */ + if (src != dest) end_burn(src, TRUE); /* extinguish candles */ + + for (ls = light_base; ls; ls = ls->next) + if (ls->type == LS_OBJECT && ls->id == (genericptr_t) dest) { + ls->range = candle_light_range(dest); + vision_full_recalc = 1; /* in case range changed */ + break; + } +} + +/* Candlelight is proportional to the number of candles; + minimum range is 2 rather than 1 for playability. */ +int +candle_light_range(obj) +struct obj *obj; +{ + int radius; + + if (obj->otyp == CANDELABRUM_OF_INVOCATION) { + /* + * The special candelabrum emits more light than the + * corresponding number of candles would. + * 1..3 candles, range 2 (minimum range); + * 4..6 candles, range 3 (normal lamp range); + * 7 candles, range 4 (bright). + */ + radius = (obj->spe < 4) ? 2 : (obj->spe < 7) ? 3 : 4; + } else if (Is_candle(obj)) { + /* + * Range is incremented by powers of 7 so that it will take + * wizard mode quantities of candles to get more light than + * from a lamp, without imposing an arbitrary limit. + * 1..6 candles, range 2; + * 7..48 candles, range 3; + * 49..342 candles, range 4; &c. + */ + long n = obj->quan; + + radius = 1; /* always incremented at least once */ + do { + radius++; + n /= 7L; + } while (n > 0L); + } else { + /* we're only called for lit candelabrum or candles */ + /* impossible("candlelight for %d?", obj->otyp); */ + radius = 3; /* lamp's value */ + } + return radius; +} + +#ifdef WIZARD +extern char *FDECL(fmt_ptr, (const genericptr, char *)); /* from alloc.c */ + +int +wiz_light_sources() +{ + winid win; + char buf[BUFSZ], arg_address[20]; + light_source *ls; + + win = create_nhwindow(NHW_MENU); /* corner text window */ + if (win == WIN_ERR) return 0; + + Sprintf(buf, "Mobile light sources: hero @ (%2d,%2d)", u.ux, u.uy); + putstr(win, 0, buf); + putstr(win, 0, ""); + + if (light_base) { + putstr(win, 0, "location range flags type id"); + putstr(win, 0, "-------- ----- ------ ---- -------"); + for (ls = light_base; ls; ls = ls->next) { + Sprintf(buf, " %2d,%2d %2d 0x%04x %s %s", + ls->x, ls->y, ls->range, ls->flags, + (ls->type == LS_OBJECT ? "obj" : + ls->type == LS_MONSTER ? + (mon_is_local((struct monst *)ls->id) ? "mon" : + ((struct monst *)ls->id == &youmonst) ? "you" : + "") : /* migrating monster */ + "???"), + fmt_ptr(ls->id, arg_address)); + putstr(win, 0, buf); + } + } else + putstr(win, 0, ""); + + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + + return 0; +} + +#endif /* WIZARD */ + +#endif /* OVL3 */ + +/*light.c*/ diff --git a/src/lock.c b/src/lock.c new file mode 100644 index 0000000..4d5d333 --- /dev/null +++ b/src/lock.c @@ -0,0 +1,921 @@ +/* SCCS Id: @(#)lock.c 3.4 2000/02/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_PTR int NDECL(picklock); +STATIC_PTR int NDECL(forcelock); + +/* at most one of `door' and `box' should be non-null at any given time */ +STATIC_VAR NEARDATA struct xlock_s { + struct rm *door; + struct obj *box; + int picktyp, chance, usedtime; +} xlock; + +#ifdef OVLB + +STATIC_DCL const char *NDECL(lock_action); +STATIC_DCL boolean FDECL(obstructed,(int,int)); +STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); + +boolean +picking_lock(x, y) + int *x, *y; +{ + if (occupation == picklock) { + *x = u.ux + u.dx; + *y = u.uy + u.dy; + return TRUE; + } else { + *x = *y = 0; + return FALSE; + } +} + +boolean +picking_at(x, y) +int x, y; +{ + return (boolean)(occupation == picklock && xlock.door == &levl[x][y]); +} + +/* produce an occupation string appropriate for the current activity */ +STATIC_OVL const char * +lock_action() +{ + /* "unlocking"+2 == "locking" */ + static const char *actions[] = { + /* [0] */ "unlocking the door", + /* [1] */ "unlocking the chest", + /* [2] */ "unlocking the box", + /* [3] */ "picking the lock" + }; + + /* if the target is currently unlocked, we're trying to lock it now */ + if (xlock.door && !(xlock.door->doormask & D_LOCKED)) + return actions[0]+2; /* "locking the door" */ + else if (xlock.box && !xlock.box->olocked) + return xlock.box->otyp == CHEST ? actions[1]+2 : actions[2]+2; + /* otherwise we're trying to unlock it */ + else if (xlock.picktyp == LOCK_PICK) + return actions[3]; /* "picking the lock" */ +#ifdef TOURIST + else if (xlock.picktyp == CREDIT_CARD) + return actions[3]; /* same as lock_pick */ +#endif + else if (xlock.door) + return actions[0]; /* "unlocking the door" */ + else + return xlock.box->otyp == CHEST ? actions[1] : actions[2]; +} + +STATIC_PTR +int +picklock() /* try to open/close a lock */ +{ + + if (xlock.box) { + if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) { + return((xlock.usedtime = 0)); /* you or it moved */ + } + } else { /* door */ + if(xlock.door != &(levl[u.ux+u.dx][u.uy+u.dy])) { + return((xlock.usedtime = 0)); /* you moved */ + } + switch (xlock.door->doormask) { + case D_NODOOR: + pline("This doorway has no door."); + return((xlock.usedtime = 0)); + case D_ISOPEN: + You("cannot lock an open door."); + return((xlock.usedtime = 0)); + case D_BROKEN: + pline("This door is broken."); + return((xlock.usedtime = 0)); + } + } + + if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) { + You("give up your attempt at %s.", lock_action()); + exercise(A_DEX, TRUE); /* even if you don't succeed */ + return((xlock.usedtime = 0)); + } + + if(rn2(100) >= xlock.chance) return(1); /* still busy */ + + You("succeed in %s.", lock_action()); + if (xlock.door) { + if(xlock.door->doormask & D_TRAPPED) { + b_trapped("door", FINGER); + xlock.door->doormask = D_NODOOR; + unblock_point(u.ux+u.dx, u.uy+u.dy); + if (*in_rooms(u.ux+u.dx, u.uy+u.dy, SHOPBASE)) + add_damage(u.ux+u.dx, u.uy+u.dy, 0L); + newsym(u.ux+u.dx, u.uy+u.dy); + } else if (xlock.door->doormask & D_LOCKED) + xlock.door->doormask = D_CLOSED; + else xlock.door->doormask = D_LOCKED; + } else { + xlock.box->olocked = !xlock.box->olocked; + if(xlock.box->otrapped) + (void) chest_trap(xlock.box, FINGER, FALSE); + } + exercise(A_DEX, TRUE); + return((xlock.usedtime = 0)); +} + +STATIC_PTR +int +forcelock() /* try to force a locked chest */ +{ + + register struct obj *otmp; + + if((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy)) + return((xlock.usedtime = 0)); /* you or it moved */ + + if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) { + You("give up your attempt to force the lock."); + if(xlock.usedtime >= 50) /* you made the effort */ + exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); + return((xlock.usedtime = 0)); + } + + if(xlock.picktyp) { /* blade */ + + if(rn2(1000-(int)uwep->spe) > (992-greatest_erosion(uwep)*10) && + !uwep->cursed && !obj_resists(uwep, 0, 99)) { + /* for a +0 weapon, probability that it survives an unsuccessful + * attempt to force the lock is (.992)^50 = .67 + */ + pline("%sour %s broke!", + (uwep->quan > 1L) ? "One of y" : "Y", xname(uwep)); + useup(uwep); + You("give up your attempt to force the lock."); + exercise(A_DEX, TRUE); + return((xlock.usedtime = 0)); + } + } else /* blunt */ + wake_nearby(); /* due to hammering on the container */ + + if(rn2(100) >= xlock.chance) return(1); /* still busy */ + + You("succeed in forcing the lock."); + xlock.box->olocked = 0; + xlock.box->obroken = 1; + if(!xlock.picktyp && !rn2(3)) { + struct monst *shkp; + boolean costly; + long loss = 0L; + + costly = (*u.ushops && costly_spot(u.ux, u.uy)); + shkp = costly ? shop_keeper(*u.ushops) : 0; + + pline("In fact, you've totally destroyed %s.", + the(xname(xlock.box))); + + /* Put the contents on ground at the hero's feet. */ + while ((otmp = xlock.box->cobj) != 0) { + obj_extract_self(otmp); + if(!rn2(3) || otmp->oclass == POTION_CLASS) { + chest_shatter_msg(otmp); + if (costly) + loss += stolen_value(otmp, u.ux, u.uy, + (boolean)shkp->mpeaceful, TRUE); + if (otmp->quan == 1L) { + obfree(otmp, (struct obj *) 0); + continue; + } + useup(otmp); + } + if (xlock.box->otyp == ICE_BOX && otmp->otyp == CORPSE) { + otmp->age = monstermoves - otmp->age; /* actual age */ + start_corpse_timeout(otmp); + } + place_object(otmp, u.ux, u.uy); + stackobj(otmp); + } + + if (costly) + loss += stolen_value(xlock.box, u.ux, u.uy, + (boolean)shkp->mpeaceful, TRUE); + if(loss) You("owe %ld %s for objects destroyed.", loss, currency(loss)); + delobj(xlock.box); + } + exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE); + return((xlock.usedtime = 0)); +} + +#endif /* OVLB */ +#ifdef OVL0 + +void +reset_pick() +{ + xlock.usedtime = xlock.chance = xlock.picktyp = 0; + xlock.door = 0; + xlock.box = 0; +} + +#endif /* OVL0 */ +#ifdef OVLB + +int +pick_lock(pick) /* pick a lock with a given object */ + register struct obj *pick; +{ + int picktyp, c, ch; + coord cc; + struct rm *door; + struct obj *otmp; + char qbuf[QBUFSZ]; + + picktyp = pick->otyp; + + /* check whether we're resuming an interrupted previous attempt */ + if (xlock.usedtime && picktyp == xlock.picktyp) { + static char no_longer[] = "Unfortunately, you can no longer %s %s."; + + if (nohands(youmonst.data)) { + const char *what = (picktyp == LOCK_PICK) ? "pick" : "key"; +#ifdef TOURIST + if (picktyp == CREDIT_CARD) what = "card"; +#endif + pline(no_longer, "hold the", what); + reset_pick(); + return 0; + } else if (xlock.box && !can_reach_floor()) { + pline(no_longer, "reach the", "lock"); + reset_pick(); + return 0; + } else { + const char *action = lock_action(); + You("resume your attempt at %s.", action); + set_occupation(picklock, action, 0); + return(1); + } + } + + if(nohands(youmonst.data)) { + You_cant("hold %s -- you have no hands!", doname(pick)); + return(0); + } + + if((picktyp != LOCK_PICK && +#ifdef TOURIST + picktyp != CREDIT_CARD && +#endif + picktyp != SKELETON_KEY)) { + impossible("picking lock with object %d?", picktyp); + return(0); + } + ch = 0; /* lint suppression */ + + if(!get_adjacent_loc((char *)0, "Invalid location!", u.ux, u.uy, &cc)) return 0; + if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */ + const char *verb; + boolean it; + int count; + + if (u.dz < 0) { + There("isn't any sort of lock up %s.", + Levitation ? "here" : "there"); + return 0; + } else if (is_lava(u.ux, u.uy)) { + pline("Doing that would probably melt your %s.", + xname(pick)); + return 0; + } else if (is_pool(u.ux, u.uy) && !Underwater) { + pline_The("water has no lock."); + return 0; + } + + count = 0; + c = 'n'; /* in case there are no boxes here */ + for(otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere) + if (Is_box(otmp)) { + ++count; + if (!can_reach_floor()) { + You_cant("reach %s from up here.", the(xname(otmp))); + return 0; + } + it = 0; + if (otmp->obroken) verb = "fix"; + else if (!otmp->olocked) verb = "lock", it = 1; + else if (picktyp != LOCK_PICK) verb = "unlock", it = 1; + else verb = "pick"; + Sprintf(qbuf, "There is %s here, %s %s?", + safe_qbuf("", sizeof("There is here, unlock its lock?"), + doname(otmp), an(simple_typename(otmp->otyp)), "a box"), + verb, it ? "it" : "its lock"); + + c = ynq(qbuf); + if(c == 'q') return(0); + if(c == 'n') continue; + + if (otmp->obroken) { + You_cant("fix its broken lock with %s.", doname(pick)); + return 0; + } +#ifdef TOURIST + else if (picktyp == CREDIT_CARD && !otmp->olocked) { + /* credit cards are only good for unlocking */ + You_cant("do that with %s.", doname(pick)); + return 0; + } +#endif + switch(picktyp) { +#ifdef TOURIST + case CREDIT_CARD: + ch = ACURR(A_DEX) + 20*Role_if(PM_ROGUE); + break; +#endif + case LOCK_PICK: + ch = 4*ACURR(A_DEX) + 25*Role_if(PM_ROGUE); + break; + case SKELETON_KEY: + ch = 75 + ACURR(A_DEX); + break; + default: ch = 0; + } + if(otmp->cursed) ch /= 2; + + xlock.picktyp = picktyp; + xlock.box = otmp; + xlock.door = 0; + break; + } + if (c != 'y') { + if (!count) + There("doesn't seem to be any sort of lock here."); + return(0); /* decided against all boxes */ + } + } else { /* pick the lock in a door */ + struct monst *mtmp; + + if (u.utrap && u.utraptype == TT_PIT) { + You_cant("reach over the edge of the pit."); + return(0); + } + + door = &levl[cc.x][cc.y]; + if ((mtmp = m_at(cc.x, cc.y)) && canseemon(mtmp) + && mtmp->m_ap_type != M_AP_FURNITURE + && mtmp->m_ap_type != M_AP_OBJECT) { +#ifdef TOURIST + if (picktyp == CREDIT_CARD && + (mtmp->isshk || mtmp->data == &mons[PM_ORACLE])) + verbalize("No checks, no credit, no problem."); + else +#endif + pline("I don't think %s would appreciate that.", mon_nam(mtmp)); + return(0); + } + if(!IS_DOOR(door->typ)) { + if (is_drawbridge_wall(cc.x,cc.y) >= 0) + You("%s no lock on the drawbridge.", + Blind ? "feel" : "see"); + else + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + switch (door->doormask) { + case D_NODOOR: + pline("This doorway has no door."); + return(0); + case D_ISOPEN: + You("cannot lock an open door."); + return(0); + case D_BROKEN: + pline("This door is broken."); + return(0); + default: +#ifdef TOURIST + /* credit cards are only good for unlocking */ + if(picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { + You_cant("lock a door with a credit card."); + return(0); + } +#endif + + Sprintf(qbuf,"%sock it?", + (door->doormask & D_LOCKED) ? "Unl" : "L" ); + + c = yn(qbuf); + if(c == 'n') return(0); + + switch(picktyp) { +#ifdef TOURIST + case CREDIT_CARD: + ch = 2*ACURR(A_DEX) + 20*Role_if(PM_ROGUE); + break; +#endif + case LOCK_PICK: + ch = 3*ACURR(A_DEX) + 30*Role_if(PM_ROGUE); + break; + case SKELETON_KEY: + ch = 70 + ACURR(A_DEX); + break; + default: ch = 0; + } + xlock.door = door; + xlock.box = 0; + } + } + flags.move = 0; + xlock.chance = ch; + xlock.picktyp = picktyp; + xlock.usedtime = 0; + set_occupation(picklock, lock_action(), 0); + return(1); +} + +int +doforce() /* try to force a chest with your weapon */ +{ + register struct obj *otmp; + register int c, picktyp; + char qbuf[QBUFSZ]; + + if(!uwep || /* proper type test */ + (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep) && + uwep->oclass != ROCK_CLASS) || + (objects[uwep->otyp].oc_skill < P_DAGGER) || + (objects[uwep->otyp].oc_skill > P_LANCE) || + uwep->otyp == FLAIL || uwep->otyp == AKLYS +#ifdef KOPS + || uwep->otyp == RUBBER_HOSE +#endif + ) { + You_cant("force anything without a %sweapon.", + (uwep) ? "proper " : ""); + return(0); + } + + picktyp = is_blade(uwep); + if(xlock.usedtime && xlock.box && picktyp == xlock.picktyp) { + You("resume your attempt to force the lock."); + set_occupation(forcelock, "forcing the lock", 0); + return(1); + } + + /* A lock is made only for the honest man, the thief will break it. */ + xlock.box = (struct obj *)0; + for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) + if(Is_box(otmp)) { + if (otmp->obroken || !otmp->olocked) { + There("is %s here, but its lock is already %s.", + doname(otmp), otmp->obroken ? "broken" : "unlocked"); + continue; + } + Sprintf(qbuf,"There is %s here, force its lock?", + safe_qbuf("", sizeof("There is here, force its lock?"), + doname(otmp), an(simple_typename(otmp->otyp)), + "a box")); + + c = ynq(qbuf); + if(c == 'q') return(0); + if(c == 'n') continue; + + if(picktyp) + You("force your %s into a crack and pry.", xname(uwep)); + else + You("start bashing it with your %s.", xname(uwep)); + xlock.box = otmp; + xlock.chance = objects[uwep->otyp].oc_wldam * 2; + xlock.picktyp = picktyp; + xlock.usedtime = 0; + break; + } + + if(xlock.box) set_occupation(forcelock, "forcing the lock", 0); + else You("decide not to force the issue."); + return(1); +} + +int +doopen() /* try to open a door */ +{ + coord cc; + register struct rm *door; + struct monst *mtmp; + + if (nohands(youmonst.data)) { + You_cant("open anything -- you have no hands!"); + return 0; + } + + if (u.utrap && u.utraptype == TT_PIT) { + You_cant("reach over the edge of the pit."); + return 0; + } + + if(!get_adjacent_loc((char *)0, (char *)0, u.ux, u.uy, &cc)) return(0); + + if((cc.x == u.ux) && (cc.y == u.uy)) return(0); + + if ((mtmp = m_at(cc.x,cc.y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + door = &levl[cc.x][cc.y]; + + if(!IS_DOOR(door->typ)) { + if (is_db_wall(cc.x,cc.y)) { + There("is no obvious way to open the drawbridge."); + return(0); + } + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + + if (!(door->doormask & D_CLOSED)) { + const char *mesg; + + switch (door->doormask) { + case D_BROKEN: mesg = " is broken"; break; + case D_NODOOR: mesg = "way has no door"; break; + case D_ISOPEN: mesg = " is already open"; break; + default: mesg = " is locked"; break; + } + pline("This door%s.", mesg); + if (Blind) feel_location(cc.x,cc.y); + return(0); + } + + if(verysmall(youmonst.data)) { + pline("You're too small to pull the door open."); + return(0); + } + + /* door is known to be CLOSED */ + if (rnl(20) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { + pline_The("door opens."); + if(door->doormask & D_TRAPPED) { + b_trapped("door", FINGER); + door->doormask = D_NODOOR; + if (*in_rooms(cc.x, cc.y, SHOPBASE)) add_damage(cc.x, cc.y, 0L); + } else + door->doormask = D_ISOPEN; + if (Blind) + feel_location(cc.x,cc.y); /* the hero knows she opened it */ + else + newsym(cc.x,cc.y); + unblock_point(cc.x,cc.y); /* vision: new see through there */ + } else { + exercise(A_STR, TRUE); + pline_The("door resists!"); + } + + return(1); +} + +STATIC_OVL +boolean +obstructed(x,y) +register int x, y; +{ + register struct monst *mtmp = m_at(x, y); + + if(mtmp && mtmp->m_ap_type != M_AP_FURNITURE) { + if (mtmp->m_ap_type == M_AP_OBJECT) goto objhere; + pline("%s stands in the way!", !canspotmon(mtmp) ? + "Some creature" : Monnam(mtmp)); + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + return(TRUE); + } + if (OBJ_AT(x, y)) { +objhere: pline("%s's in the way.", Something); + return(TRUE); + } + return(FALSE); +} + +int +doclose() /* try to close a door */ +{ + register int x, y; + register struct rm *door; + struct monst *mtmp; + + if (nohands(youmonst.data)) { + You_cant("close anything -- you have no hands!"); + return 0; + } + + if (u.utrap && u.utraptype == TT_PIT) { + You_cant("reach over the edge of the pit."); + return 0; + } + + if(!getdir((char *)0)) return(0); + + x = u.ux + u.dx; + y = u.uy + u.dy; + if((x == u.ux) && (y == u.uy)) { + You("are in the way!"); + return(1); + } + + if ((mtmp = m_at(x,y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + door = &levl[x][y]; + + if(!IS_DOOR(door->typ)) { + if (door->typ == DRAWBRIDGE_DOWN) + There("is no obvious way to close the drawbridge."); + else + You("%s no door there.", + Blind ? "feel" : "see"); + return(0); + } + + if(door->doormask == D_NODOOR) { + pline("This doorway has no door."); + return(0); + } + + if(obstructed(x, y)) return(0); + + if(door->doormask == D_BROKEN) { + pline("This door is broken."); + return(0); + } + + if(door->doormask & (D_CLOSED | D_LOCKED)) { + pline("This door is already closed."); + return(0); + } + + if(door->doormask == D_ISOPEN) { + if(verysmall(youmonst.data) +#ifdef STEED + && !u.usteed +#endif + ) { + pline("You're too small to push the door closed."); + return(0); + } + if ( +#ifdef STEED + u.usteed || +#endif + rn2(25) < (ACURRSTR+ACURR(A_DEX)+ACURR(A_CON))/3) { + pline_The("door closes."); + door->doormask = D_CLOSED; + if (Blind) + feel_location(x,y); /* the hero knows she closed it */ + else + newsym(x,y); + block_point(x,y); /* vision: no longer see there */ + } + else { + exercise(A_STR, TRUE); + pline_The("door resists!"); + } + } + + return(1); +} + +boolean /* box obj was hit with spell effect otmp */ +boxlock(obj, otmp) /* returns true if something happened */ +register struct obj *obj, *otmp; /* obj *is* a box */ +{ + register boolean res = 0; + + switch(otmp->otyp) { + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + if (!obj->olocked) { /* lock it; fix if broken */ + pline("Klunk!"); + obj->olocked = 1; + obj->obroken = 0; + res = 1; + } /* else already closed and locked */ + break; + case WAN_OPENING: + case SPE_KNOCK: + if (obj->olocked) { /* unlock; couldn't be broken */ + pline("Klick!"); + obj->olocked = 0; + res = 1; + } else /* silently fix if broken */ + obj->obroken = 0; + break; + case WAN_POLYMORPH: + case SPE_POLYMORPH: + /* maybe start unlocking chest, get interrupted, then zap it; + we must avoid any attempt to resume unlocking it */ + if (xlock.box == obj) + reset_pick(); + break; + } + return res; +} + +boolean /* Door/secret door was hit with spell effect otmp */ +doorlock(otmp,x,y) /* returns true if something happened */ +struct obj *otmp; +int x, y; +{ + register struct rm *door = &levl[x][y]; + boolean res = TRUE; + int loudness = 0; + const char *msg = (const char *)0; + const char *dustcloud = "A cloud of dust"; + const char *quickly_dissipates = "quickly dissipates"; + + if (door->typ == SDOOR) { + switch (otmp->otyp) { + case WAN_OPENING: + case SPE_KNOCK: + case WAN_STRIKING: + case SPE_FORCE_BOLT: + door->typ = DOOR; + door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); + newsym(x,y); + if (cansee(x,y)) pline("A door appears in the wall!"); + if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) + return TRUE; + break; /* striking: continue door handling below */ + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + default: + return FALSE; + } + } + + switch(otmp->otyp) { + case WAN_LOCKING: + case SPE_WIZARD_LOCK: +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + boolean vis = cansee(x,y); + /* Can't have real locking in Rogue, so just hide doorway */ + if (vis) pline("%s springs up in the older, more primitive doorway.", + dustcloud); + else + You_hear("a swoosh."); + if (obstructed(x,y)) { + if (vis) pline_The("cloud %s.",quickly_dissipates); + return FALSE; + } + block_point(x, y); + door->typ = SDOOR; + if (vis) pline_The("doorway vanishes!"); + newsym(x,y); + return TRUE; + } +#endif + if (obstructed(x,y)) return FALSE; + /* Don't allow doors to close over traps. This is for pits */ + /* & trap doors, but is it ever OK for anything else? */ + if (t_at(x,y)) { + /* maketrap() clears doormask, so it should be NODOOR */ + pline( + "%s springs up in the doorway, but %s.", + dustcloud, quickly_dissipates); + return FALSE; + } + + switch (door->doormask & ~D_TRAPPED) { + case D_CLOSED: + msg = "The door locks!"; + break; + case D_ISOPEN: + msg = "The door swings shut, and locks!"; + break; + case D_BROKEN: + msg = "The broken door reassembles and locks!"; + break; + case D_NODOOR: + msg = + "A cloud of dust springs up and assembles itself into a door!"; + break; + default: + res = FALSE; + break; + } + block_point(x, y); + door->doormask = D_LOCKED | (door->doormask & D_TRAPPED); + newsym(x,y); + break; + case WAN_OPENING: + case SPE_KNOCK: + if (door->doormask & D_LOCKED) { + msg = "The door unlocks!"; + door->doormask = D_CLOSED | (door->doormask & D_TRAPPED); + } else res = FALSE; + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + if (door->doormask & (D_LOCKED | D_CLOSED)) { + if (door->doormask & D_TRAPPED) { + if (MON_AT(x, y)) + (void) mb_trapped(m_at(x,y)); + else if (flags.verbose) { + if (cansee(x,y)) + pline("KABOOM!! You see a door explode."); + else if (flags.soundok) + You_hear("a distant explosion."); + } + door->doormask = D_NODOOR; + unblock_point(x,y); + newsym(x,y); + loudness = 40; + break; + } + door->doormask = D_BROKEN; + if (flags.verbose) { + if (cansee(x,y)) + pline_The("door crashes open!"); + else if (flags.soundok) + You_hear("a crashing sound."); + } + unblock_point(x,y); + newsym(x,y); + /* force vision recalc before printing more messages */ + if (vision_full_recalc) vision_recalc(0); + loudness = 20; + } else res = FALSE; + break; + default: impossible("magic (%d) attempted on door.", otmp->otyp); + break; + } + if (msg && cansee(x,y)) pline(msg); + if (loudness > 0) { + /* door was destroyed */ + wake_nearto(x, y, loudness); + if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); + } + + if (res && picking_at(x, y)) { + /* maybe unseen monster zaps door you're unlocking */ + stop_occupation(); + reset_pick(); + } + return res; +} + +STATIC_OVL void +chest_shatter_msg(otmp) +struct obj *otmp; +{ + const char *disposition; + const char *thing; + long save_Blinded; + + if (otmp->oclass == POTION_CLASS) { + You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename())); + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(otmp); + return; + } + /* We have functions for distant and singular names, but not one */ + /* which does _both_... */ + save_Blinded = Blinded; + Blinded = 1; + thing = singular(otmp, xname); + Blinded = save_Blinded; + switch (objects[otmp->otyp].oc_material) { + case PAPER: disposition = "is torn to shreds"; + break; + case WAX: disposition = "is crushed"; + break; + case VEGGY: disposition = "is pulped"; + break; + case FLESH: disposition = "is mashed"; + break; + case GLASS: disposition = "shatters"; + break; + case WOOD: disposition = "splinters to fragments"; + break; + default: disposition = "is destroyed"; + break; + } + pline("%s %s!", An(thing), disposition); +} + +#endif /* OVLB */ + +/*lock.c*/ diff --git a/src/mail.c b/src/mail.c new file mode 100644 index 0000000..99d7637 --- /dev/null +++ b/src/mail.c @@ -0,0 +1,625 @@ +/* SCCS Id: @(#)mail.c 3.4 2002/01/13 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef MAIL +#include "mail.h" + +/* + * Notify user when new mail has arrived. Idea by Merlyn Leroy. + * + * The mail daemon can move with less than usual restraint. It can: + * - move diagonally from a door + * - use secret and closed doors + * - run through a monster ("Gangway!", etc.) + * - run over pools & traps + * + * Possible extensions: + * - Open the file MAIL and do fstat instead of stat for efficiency. + * (But sh uses stat, so this cannot be too bad.) + * - Examine the mail and produce a scroll of mail named "From somebody". + * - Invoke MAILREADER in such a way that only this single letter is read. + * - Do something to the text when the scroll is enchanted or cancelled. + * - Make the daemon always appear at a stairwell, and have it find a + * path to the hero. + * + * Note by Olaf Seibert: On the Amiga, we usually don't get mail. So we go + * through most of the effects at 'random' moments. + * Note by Paul Winner: The MSDOS port also 'fakes' the mail daemon at + * random intervals. + */ + +STATIC_DCL boolean FDECL(md_start,(coord *)); +STATIC_DCL boolean FDECL(md_stop,(coord *, coord *)); +STATIC_DCL boolean FDECL(md_rush,(struct monst *,int,int)); +STATIC_DCL void FDECL(newmail, (struct mail_info *)); + +extern char *viz_rmin, *viz_rmax; /* line-of-sight limits (vision.c) */ + +#ifdef OVL0 + +# if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) +int mustgetmail = -1; +# endif + +#endif /* OVL0 */ +#ifdef OVLB + +# ifdef UNIX +#include +#include +/* DON'T trust all Unices to declare getpwuid() in */ +# if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) +# if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) +/* DO trust all SVR4 to typedef uid_t in (probably to a long) */ +# if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) +extern struct passwd *FDECL(getpwuid,(uid_t)); +# else +extern struct passwd *FDECL(getpwuid,(int)); +# endif +# endif +# endif +static struct stat omstat,nmstat; +static char *mailbox = (char *)0; +static long laststattime; + +# if !defined(MAILPATH) && defined(AMS) /* Just a placeholder for AMS */ +# define MAILPATH "/dev/null" +# endif +# if !defined(MAILPATH) && (defined(LINUX) || defined(__osf__)) +# define MAILPATH "/var/spool/mail/" +# endif +# if !defined(MAILPATH) && defined(__FreeBSD__) +# define MAILPATH "/var/mail/" +# endif +# if !defined(MAILPATH) && (defined(BSD) || defined(ULTRIX)) +# define MAILPATH "/usr/spool/mail/" +# endif +# if !defined(MAILPATH) && (defined(SYSV) || defined(HPUX)) +# define MAILPATH "/usr/mail/" +# endif + +void +getmailstatus() +{ + if(!mailbox && !(mailbox = nh_getenv("MAIL"))) { +# ifdef MAILPATH +# ifdef AMS + struct passwd ppasswd; + + (void) memcpy(&ppasswd, getpwuid(getuid()), sizeof(struct passwd)); + if (ppasswd.pw_dir) { + mailbox = (char *) alloc((unsigned) strlen(ppasswd.pw_dir)+sizeof(AMS_MAILBOX)); + Strcpy(mailbox, ppasswd.pw_dir); + Strcat(mailbox, AMS_MAILBOX); + } else + return; +# else + const char *pw_name = getpwuid(getuid())->pw_name; + mailbox = (char *) alloc(sizeof(MAILPATH)+strlen(pw_name)); + Strcpy(mailbox, MAILPATH); + Strcat(mailbox, pw_name); +# endif /* AMS */ +# else + return; +# endif + } + if(stat(mailbox, &omstat)){ +# ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=\"%s\".", mailbox); + mailbox = 0; +# else + omstat.st_mtime = 0; +# endif + } +} +# endif /* UNIX */ + +#endif /* OVLB */ +#ifdef OVL0 + +/* + * Pick coordinates for a starting position for the mail daemon. Called + * from newmail() and newphone(). + */ +STATIC_OVL boolean +md_start(startp) + coord *startp; +{ + coord testcc; /* scratch coordinates */ + int row; /* current row we are checking */ + int lax; /* if TRUE, pick a position in sight. */ + int dd; /* distance to current point */ + int max_distance; /* max distance found so far */ + + /* + * If blind and not telepathic, then it doesn't matter what we pick --- + * the hero is not going to see it anyway. So pick a nearby position. + */ + if (Blind && !Blind_telepat) { + if (!enexto(startp, u.ux, u.uy, (struct permonst *) 0)) + return FALSE; /* no good posiitons */ + return TRUE; + } + + /* + * Arrive at an up or down stairwell if it is in line of sight from the + * hero. + */ + if (couldsee(upstair.sx, upstair.sy)) { + startp->x = upstair.sx; + startp->y = upstair.sy; + return TRUE; + } + if (couldsee(dnstair.sx, dnstair.sy)) { + startp->x = dnstair.sx; + startp->y = dnstair.sy; + return TRUE; + } + + /* + * Try to pick a location out of sight next to the farthest position away + * from the hero. If this fails, try again, just picking the farthest + * position that could be seen. What we really ought to be doing is + * finding a path from a stairwell... + * + * The arrays viz_rmin[] and viz_rmax[] are set even when blind. These + * are the LOS limits for each row. + */ + lax = 0; /* be picky */ + max_distance = -1; +retry: + for (row = 0; row < ROWNO; row++) { + if (viz_rmin[row] < viz_rmax[row]) { + /* There are valid positions on this row. */ + dd = distu(viz_rmin[row],row); + if (dd > max_distance) { + if (lax) { + max_distance = dd; + startp->y = row; + startp->x = viz_rmin[row]; + + } else if (enexto(&testcc, (xchar)viz_rmin[row], row, + (struct permonst *) 0) && + !cansee(testcc.x, testcc.y) && + couldsee(testcc.x, testcc.y)) { + max_distance = dd; + *startp = testcc; + } + } + dd = distu(viz_rmax[row],row); + if (dd > max_distance) { + if (lax) { + max_distance = dd; + startp->y = row; + startp->x = viz_rmax[row]; + + } else if (enexto(&testcc, (xchar)viz_rmax[row], row, + (struct permonst *) 0) && + !cansee(testcc.x,testcc.y) && + couldsee(testcc.x, testcc.y)) { + + max_distance = dd; + *startp = testcc; + } + } + } + } + + if (max_distance < 0) { + if (!lax) { + lax = 1; /* just find a position */ + goto retry; + } + return FALSE; + } + + return TRUE; +} + +/* + * Try to choose a stopping point as near as possible to the starting + * position while still adjacent to the hero. If all else fails, try + * enexto(). Use enexto() as a last resort because enexto() chooses + * its point randomly, which is not what we want. + */ +STATIC_OVL boolean +md_stop(stopp, startp) + coord *stopp; /* stopping position (we fill it in) */ + coord *startp; /* starting positon (read only) */ +{ + int x, y, distance, min_distance = -1; + + for (x = u.ux-1; x <= u.ux+1; x++) + for (y = u.uy-1; y <= u.uy+1; y++) { + if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; + + if (ACCESSIBLE(levl[x][y].typ) && !MON_AT(x,y)) { + distance = dist2(x,y,startp->x,startp->y); + if (min_distance < 0 || distance < min_distance || + (distance == min_distance && rn2(2))) { + stopp->x = x; + stopp->y = y; + min_distance = distance; + } + } + } + + /* If we didn't find a good spot, try enexto(). */ + if (min_distance < 0 && + !enexto(stopp, u.ux, u.uy, &mons[PM_MAIL_DAEMON])) + return FALSE; + + return TRUE; +} + +/* Let the mail daemon have a larger vocabulary. */ +static NEARDATA const char *mail_text[] = { + "Gangway!", + "Look out!", + "Pardon me!" +}; +#define md_exclamations() (mail_text[rn2(3)]) + +/* + * Make the mail daemon run through the dungeon. The daemon will run over + * any monsters that are in its path, but will replace them later. Return + * FALSE if the md gets stuck in a position where there is a monster. Return + * TRUE otherwise. + */ +STATIC_OVL boolean +md_rush(md,tx,ty) + struct monst *md; + register int tx, ty; /* destination of mail daemon */ +{ + struct monst *mon; /* displaced monster */ + register int dx, dy; /* direction counters */ + int fx = md->mx, fy = md->my; /* current location */ + int nfx = fx, nfy = fy, /* new location */ + d1, d2; /* shortest distances */ + + /* + * It is possible that the monster at (fx,fy) is not the md when: + * the md rushed the hero and failed, and is now starting back. + */ + if (m_at(fx, fy) == md) { + remove_monster(fx, fy); /* pick up from orig position */ + newsym(fx, fy); + } + + /* + * At the beginning and exit of this loop, md is not placed in the + * dungeon. + */ + while (1) { + /* Find a good location next to (fx,fy) closest to (tx,ty). */ + d1 = dist2(fx,fy,tx,ty); + for (dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) + if ((dx || dy) && isok(fx+dx,fy+dy) && + !IS_STWALL(levl[fx+dx][fy+dy].typ)) { + d2 = dist2(fx+dx,fy+dy,tx,ty); + if (d2 < d1) { + d1 = d2; + nfx = fx+dx; + nfy = fy+dy; + } + } + + /* Break if the md couldn't find a new position. */ + if (nfx == fx && nfy == fy) break; + + fx = nfx; /* this is our new position */ + fy = nfy; + + /* Break if the md reaches its destination. */ + if (fx == tx && fy == ty) break; + + if ((mon = m_at(fx,fy)) != 0) /* save monster at this position */ + verbalize(md_exclamations()); + else if (fx == u.ux && fy == u.uy) + verbalize("Excuse me."); + + place_monster(md,fx,fy); /* put md down */ + newsym(fx,fy); /* see it */ + flush_screen(0); /* make sure md shows up */ + delay_output(); /* wait a little bit */ + + /* Remove md from the dungeon. Restore original mon, if necessary. */ + if (mon) { + if ((mon->mx != fx) || (mon->my != fy)) + place_worm_seg(mon, fx, fy); + else + place_monster(mon, fx, fy); + } else + remove_monster(fx, fy); + newsym(fx,fy); + } + + /* + * Check for a monster at our stopping position (this is possible, but + * very unlikely). If one exists, then have the md leave in disgust. + */ + if ((mon = m_at(fx, fy)) != 0) { + place_monster(md, fx, fy); /* display md with text below */ + newsym(fx, fy); + verbalize("This place's too crowded. I'm outta here."); + + if ((mon->mx != fx) || (mon->my != fy)) /* put mon back */ + place_worm_seg(mon, fx, fy); + else + place_monster(mon, fx, fy); + + newsym(fx, fy); + return FALSE; + } + + place_monster(md, fx, fy); /* place at final spot */ + newsym(fx, fy); + flush_screen(0); + delay_output(); /* wait a little bit */ + + return TRUE; +} + +/* Deliver a scroll of mail. */ +/*ARGSUSED*/ +STATIC_OVL void +newmail(info) +struct mail_info *info; +{ + struct monst *md; + coord start, stop; + boolean message_seen = FALSE; + + /* Try to find good starting and stopping places. */ + if (!md_start(&start) || !md_stop(&stop,&start)) goto give_up; + + /* Make the daemon. Have it rush towards the hero. */ + if (!(md = makemon(&mons[PM_MAIL_DAEMON], start.x, start.y, NO_MM_FLAGS))) + goto give_up; + if (!md_rush(md, stop.x, stop.y)) goto go_back; + + message_seen = TRUE; + verbalize("%s, %s! %s.", Hello(md), plname, info->display_txt); + + if (info->message_typ) { + struct obj *obj = mksobj(SCR_MAIL, FALSE, FALSE); + if (distu(md->mx,md->my) > 2) + verbalize("Catch!"); + display_nhwindow(WIN_MESSAGE, FALSE); + if (info->object_nam) { + obj = oname(obj, info->object_nam); + if (info->response_cmd) { /*(hide extension of the obj name)*/ + int namelth = info->response_cmd - info->object_nam - 1; + if ( namelth <= 0 || namelth >= (int) obj->onamelth ) + impossible("mail delivery screwed up"); + else + *(ONAME(obj) + namelth) = '\0'; + /* Note: renaming object will discard the hidden command. */ + } + } + obj = hold_another_object(obj, "Oops!", + (const char *)0, (const char *)0); + } + + /* zip back to starting location */ +go_back: + (void) md_rush(md, start.x, start.y); + mongone(md); + /* deliver some classes of messages even if no daemon ever shows up */ +give_up: + if (!message_seen && info->message_typ == MSG_OTHER) + pline("Hark! \"%s.\"", info->display_txt); +} + +# if !defined(UNIX) && !defined(VMS) && !defined(LAN_MAIL) + +void +ckmailstatus() +{ + if (u.uswallow || !flags.biff) return; + if (mustgetmail < 0) { +#if defined(AMIGA) || defined(MSDOS) || defined(TOS) + mustgetmail=(moves<2000)?(100+rn2(2000)):(2000+rn2(3000)); +#endif + return; + } + if (--mustgetmail <= 0) { + static struct mail_info + deliver = {MSG_MAIL,"I have some mail for you",0,0}; + newmail(&deliver); + mustgetmail = -1; + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ + static char *junk[] = { + "Please disregard previous letter.", + "Welcome to NetHack.", +#ifdef AMIGA + "Only Amiga makes it possible.", + "CATS have all the answers.", +#endif + "Report bugs to .", + "Invitation: Visit the NetHack web site at http://www.nethack.org!" + }; + + if (Blind) { + pline("Unfortunately you cannot see what it says."); + } else + pline("It reads: \"%s\"", junk[rn2(SIZE(junk))]); + +} + +# endif /* !UNIX && !VMS && !LAN_MAIL */ + +# ifdef UNIX + +void +ckmailstatus() +{ + if(!mailbox || u.uswallow || !flags.biff +# ifdef MAILCKFREQ + || moves < laststattime + MAILCKFREQ +# endif + ) + return; + + laststattime = moves; + if(stat(mailbox, &nmstat)){ +# ifdef PERMANENT_MAILBOX + pline("Cannot get status of MAIL=\"%s\" anymore.", mailbox); + mailbox = 0; +# else + nmstat.st_mtime = 0; +# endif + } else if(nmstat.st_mtime > omstat.st_mtime) { + if (nmstat.st_size) { + static struct mail_info deliver = { +# ifndef NO_MAILREADER + MSG_MAIL, "I have some mail for you", +# else + /* suppress creation and delivery of scroll of mail */ + MSG_OTHER, "You have some mail in the outside world", +# endif + 0, 0 + }; + newmail(&deliver); + } + getmailstatus(); /* might be too late ... */ + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ +# ifdef DEF_MAILREADER /* This implies that UNIX is defined */ + register const char *mr = 0; + + display_nhwindow(WIN_MESSAGE, FALSE); + if(!(mr = nh_getenv("MAILREADER"))) + mr = DEF_MAILREADER; + + if(child(1)){ + (void) execl(mr, mr, (char *)0); + terminate(EXIT_FAILURE); + } +# else +# ifndef AMS /* AMS mailboxes are directories */ + display_file(mailbox, TRUE); +# endif /* AMS */ +# endif /* DEF_MAILREADER */ + + /* get new stat; not entirely correct: there is a small time + window where we do not see new mail */ + getmailstatus(); +} + +# endif /* UNIX */ + +# ifdef VMS + +extern NDECL(struct mail_info *parse_next_broadcast); + +volatile int broadcasts = 0; + +void +ckmailstatus() +{ + struct mail_info *brdcst; + + if (u.uswallow || !flags.biff) return; + + while (broadcasts > 0) { /* process all trapped broadcasts [until] */ + broadcasts--; + if ((brdcst = parse_next_broadcast()) != 0) { + newmail(brdcst); + break; /* only handle one real message at a time */ + } + } +} + +void +readmail(otmp) +struct obj *otmp; +{ +# ifdef SHELL /* can't access mail reader without spawning subprocess */ + const char *txt, *cmd; + char *p, buf[BUFSZ], qbuf[BUFSZ]; + int len; + + /* there should be a command hidden beyond the object name */ + txt = otmp->onamelth ? ONAME(otmp) : ""; + len = strlen(txt); + cmd = (len + 1 < otmp->onamelth) ? txt + len + 1 : (char *) 0; + if (!cmd || !*cmd) cmd = "SPAWN"; + + Sprintf(qbuf, "System command (%s)", cmd); + getlin(qbuf, buf); + if (*buf != '\033') { + for (p = eos(buf); p > buf; *p = '\0') + if (*--p != ' ') break; /* strip trailing spaces */ + if (*buf) cmd = buf; /* use user entered command */ + if (!strcmpi(cmd, "SPAWN") || !strcmp(cmd, "!")) + cmd = (char *) 0; /* interactive escape */ + + vms_doshell(cmd, TRUE); + (void) sleep(1); + } +# endif /* SHELL */ +} + +# endif /* VMS */ + +# ifdef LAN_MAIL + +void +ckmailstatus() +{ + static int laststattime = 0; + + if(u.uswallow || !flags.biff +# ifdef MAILCKFREQ + || moves < laststattime + MAILCKFREQ +# endif + ) + return; + + laststattime = moves; + if (lan_mail_check()) { + static struct mail_info deliver = { +# ifndef NO_MAILREADER + MSG_MAIL, "I have some mail for you", +# else + /* suppress creation and delivery of scroll of mail */ + MSG_OTHER, "You have some mail in the outside world", +# endif + 0, 0 + }; + newmail(&deliver); + } +} + +/*ARGSUSED*/ +void +readmail(otmp) +struct obj *otmp; +{ + lan_mail_read(otmp); +} + +# endif /* LAN_MAIL */ + +#endif /* OVL0 */ + +#endif /* MAIL */ + +/*mail.c*/ diff --git a/src/makemon.c b/src/makemon.c new file mode 100644 index 0000000..89098dd --- /dev/null +++ b/src/makemon.c @@ -0,0 +1,1800 @@ +/* SCCS Id: @(#)makemon.c 3.4 2003/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "epri.h" +#include "emin.h" +#include "edog.h" +#ifdef REINCARNATION +#include +#endif + +STATIC_VAR NEARDATA struct monst zeromonst; + +/* this assumes that a human quest leader or nemesis is an archetype + of the corresponding role; that isn't so for some roles (tourist + for instance) but is for the priests and monks we use it for... */ +#define quest_mon_represents_role(mptr,role_pm) \ + (mptr->mlet == S_HUMAN && Role_if(role_pm) && \ + (mptr->msound == MS_LEADER || mptr->msound == MS_NEMESIS)) + +#ifdef OVL0 +STATIC_DCL boolean FDECL(uncommon, (int)); +STATIC_DCL int FDECL(align_shift, (struct permonst *)); +#endif /* OVL0 */ +STATIC_DCL boolean FDECL(wrong_elem_type, (struct permonst *)); +STATIC_DCL void FDECL(m_initgrp,(struct monst *,int,int,int)); +STATIC_DCL void FDECL(m_initthrow,(struct monst *,int,int)); +STATIC_DCL void FDECL(m_initweap,(struct monst *)); +#ifdef OVL1 +STATIC_DCL void FDECL(m_initinv,(struct monst *)); +#endif /* OVL1 */ + +extern const int monstr[]; + +#define m_initsgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 3) +#define m_initlgrp(mtmp, x, y) m_initgrp(mtmp, x, y, 10) +#define toostrong(monindx, lev) (monstr[monindx] > lev) +#define tooweak(monindx, lev) (monstr[monindx] < lev) + +#ifdef OVLB +boolean +is_home_elemental(ptr) +register struct permonst *ptr; +{ + if (ptr->mlet == S_ELEMENTAL) + switch (monsndx(ptr)) { + case PM_AIR_ELEMENTAL: return Is_airlevel(&u.uz); + case PM_FIRE_ELEMENTAL: return Is_firelevel(&u.uz); + case PM_EARTH_ELEMENTAL: return Is_earthlevel(&u.uz); + case PM_WATER_ELEMENTAL: return Is_waterlevel(&u.uz); + } + return FALSE; +} + +/* + * Return true if the given monster cannot exist on this elemental level. + */ +STATIC_OVL boolean +wrong_elem_type(ptr) + register struct permonst *ptr; +{ + if (ptr->mlet == S_ELEMENTAL) { + return((boolean)(!is_home_elemental(ptr))); + } else if (Is_earthlevel(&u.uz)) { + /* no restrictions? */ + } else if (Is_waterlevel(&u.uz)) { + /* just monsters that can swim */ + if(!is_swimmer(ptr)) return TRUE; + } else if (Is_firelevel(&u.uz)) { + if (!pm_resistance(ptr,MR_FIRE)) return TRUE; + } else if (Is_airlevel(&u.uz)) { + if(!(is_flyer(ptr) && ptr->mlet != S_TRAPPER) && !is_floater(ptr) + && !amorphous(ptr) && !noncorporeal(ptr) && !is_whirly(ptr)) + return TRUE; + } + return FALSE; +} + +STATIC_OVL void +m_initgrp(mtmp, x, y, n) /* make a group just like mtmp */ +register struct monst *mtmp; +register int x, y, n; +{ + coord mm; + register int cnt = rnd(n); + struct monst *mon; +#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) + /* There is an unresolved problem with several people finding that + * the game hangs eating CPU; if interrupted and restored, the level + * will be filled with monsters. Of those reports giving system type, + * there were two DG/UX and two HP-UX, all using gcc as the compiler. + * hcroft@hpopb1.cern.ch, using gcc 2.6.3 on HP-UX, says that the + * problem went away for him and another reporter-to-newsgroup + * after adding this debugging code. This has almost got to be a + * compiler bug, but until somebody tracks it down and gets it fixed, + * might as well go with the "but it went away when I tried to find + * it" code. + */ + int cnttmp,cntdiv; + + cnttmp = cnt; +# ifdef DEBUG + pline("init group call x=%d,y=%d,n=%d,cnt=%d.", x, y, n, cnt); +# endif + cntdiv = ((u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1); +#endif + /* Tuning: cut down on swarming at low character levels [mrs] */ + cnt /= (u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1; +#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) + if (cnt != (cnttmp/cntdiv)) { + pline("cnt=%d using %d, cnttmp=%d, cntdiv=%d", cnt, + (u.ulevel < 3) ? 4 : (u.ulevel < 5) ? 2 : 1, + cnttmp, cntdiv); + } +#endif + if(!cnt) cnt++; +#if defined(__GNUC__) && (defined(HPUX) || defined(DGUX)) + if (cnt < 0) cnt = 1; + if (cnt > 10) cnt = 10; +#endif + + mm.x = x; + mm.y = y; + while(cnt--) { + if (peace_minded(mtmp->data)) continue; + /* Don't create groups of peaceful monsters since they'll get + * in our way. If the monster has a percentage chance so some + * are peaceful and some are not, the result will just be a + * smaller group. + */ + if (enexto(&mm, mm.x, mm.y, mtmp->data)) { + mon = makemon(mtmp->data, mm.x, mm.y, NO_MM_FLAGS); + mon->mpeaceful = FALSE; + mon->mavenge = 0; + set_malign(mon); + /* Undo the second peace_minded() check in makemon(); if the + * monster turned out to be peaceful the first time we + * didn't create it at all; we don't want a second check. + */ + } + } +} + +STATIC_OVL +void +m_initthrow(mtmp,otyp,oquan) +struct monst *mtmp; +int otyp,oquan; +{ + register struct obj *otmp; + + otmp = mksobj(otyp, TRUE, FALSE); + otmp->quan = (long) rn1(oquan, 3); + otmp->owt = weight(otmp); + if (otyp == ORCISH_ARROW) otmp->opoisoned = TRUE; + (void) mpickobj(mtmp, otmp); +} + +#endif /* OVLB */ +#ifdef OVL2 + +STATIC_OVL void +m_initweap(mtmp) +register struct monst *mtmp; +{ + register struct permonst *ptr = mtmp->data; + register int mm = monsndx(ptr); + struct obj *otmp; + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) return; +#endif +/* + * first a few special cases: + * + * giants get a boulder to throw sometimes. + * ettins get clubs + * kobolds get darts to throw + * centaurs get some sort of bow & arrows or bolts + * soldiers get all sorts of things. + * kops get clubs & cream pies. + */ + switch (ptr->mlet) { + case S_GIANT: + if (rn2(2)) (void)mongets(mtmp, (mm != PM_ETTIN) ? + BOULDER : CLUB); + break; + case S_HUMAN: + if(is_mercenary(ptr)) { + int w1 = 0, w2 = 0; + switch (mm) { + + case PM_WATCHMAN: + case PM_SOLDIER: + if (!rn2(3)) { + w1 = rn1(BEC_DE_CORBIN - PARTISAN + 1, PARTISAN); + w2 = rn2(2) ? DAGGER : KNIFE; + } else w1 = rn2(2) ? SPEAR : SHORT_SWORD; + break; + case PM_SERGEANT: + w1 = rn2(2) ? FLAIL : MACE; + break; + case PM_LIEUTENANT: + w1 = rn2(2) ? BROADSWORD : LONG_SWORD; + break; + case PM_CAPTAIN: + case PM_WATCH_CAPTAIN: + w1 = rn2(2) ? LONG_SWORD : SILVER_SABER; + break; + default: + if (!rn2(4)) w1 = DAGGER; + if (!rn2(7)) w2 = SPEAR; + break; + } + if (w1) (void)mongets(mtmp, w1); + if (!w2 && w1 != DAGGER && !rn2(4)) w2 = KNIFE; + if (w2) (void)mongets(mtmp, w2); + } else if (is_elf(ptr)) { + if (rn2(2)) + (void) mongets(mtmp, + rn2(2) ? ELVEN_MITHRIL_COAT : ELVEN_CLOAK); + if (rn2(2)) (void)mongets(mtmp, ELVEN_LEATHER_HELM); + else if (!rn2(4)) (void)mongets(mtmp, ELVEN_BOOTS); + if (rn2(2)) (void)mongets(mtmp, ELVEN_DAGGER); + switch (rn2(3)) { + case 0: + if (!rn2(4)) (void)mongets(mtmp, ELVEN_SHIELD); + if (rn2(3)) (void)mongets(mtmp, ELVEN_SHORT_SWORD); + (void)mongets(mtmp, ELVEN_BOW); + m_initthrow(mtmp, ELVEN_ARROW, 12); + break; + case 1: + (void)mongets(mtmp, ELVEN_BROADSWORD); + if (rn2(2)) (void)mongets(mtmp, ELVEN_SHIELD); + break; + case 2: + if (rn2(2)) { + (void)mongets(mtmp, ELVEN_SPEAR); + (void)mongets(mtmp, ELVEN_SHIELD); + } + break; + } + if (mm == PM_ELVENKING) { + if (rn2(3) || (in_mklev && Is_earthlevel(&u.uz))) + (void)mongets(mtmp, PICK_AXE); + if (!rn2(50)) (void)mongets(mtmp, CRYSTAL_BALL); + } + } else if (ptr->msound == MS_PRIEST || + quest_mon_represents_role(ptr,PM_PRIEST)) { + otmp = mksobj(MACE, FALSE, FALSE); + if(otmp) { + otmp->spe = rnd(3); + if(!rn2(2)) curse(otmp); + (void) mpickobj(mtmp, otmp); + } + } + break; + + case S_ANGEL: + { + int spe2; + + /* create minion stuff; can't use mongets */ + otmp = mksobj(LONG_SWORD, FALSE, FALSE); + + /* maybe make it special */ + if (!rn2(20) || is_lord(ptr)) + otmp = oname(otmp, artiname( + rn2(2) ? ART_DEMONBANE : ART_SUNSWORD)); + bless(otmp); + otmp->oerodeproof = TRUE; + spe2 = rn2(4); + otmp->spe = max(otmp->spe, spe2); + (void) mpickobj(mtmp, otmp); + + otmp = mksobj(!rn2(4) || is_lord(ptr) ? + SHIELD_OF_REFLECTION : LARGE_SHIELD, + FALSE, FALSE); + otmp->cursed = FALSE; + otmp->oerodeproof = TRUE; + otmp->spe = 0; + (void) mpickobj(mtmp, otmp); + } + break; + + case S_HUMANOID: + if (mm == PM_HOBBIT) { + switch (rn2(3)) { + case 0: + (void)mongets(mtmp, DAGGER); + break; + case 1: + (void)mongets(mtmp, ELVEN_DAGGER); + break; + case 2: + (void)mongets(mtmp, SLING); + break; + } + if (!rn2(10)) (void)mongets(mtmp, ELVEN_MITHRIL_COAT); + if (!rn2(10)) (void)mongets(mtmp, DWARVISH_CLOAK); + } else if (is_dwarf(ptr)) { + if (rn2(7)) (void)mongets(mtmp, DWARVISH_CLOAK); + if (rn2(7)) (void)mongets(mtmp, IRON_SHOES); + if (!rn2(4)) { + (void)mongets(mtmp, DWARVISH_SHORT_SWORD); + /* note: you can't use a mattock with a shield */ + if (rn2(2)) (void)mongets(mtmp, DWARVISH_MATTOCK); + else { + (void)mongets(mtmp, AXE); + (void)mongets(mtmp, DWARVISH_ROUNDSHIELD); + } + (void)mongets(mtmp, DWARVISH_IRON_HELM); + if (!rn2(3)) + (void)mongets(mtmp, DWARVISH_MITHRIL_COAT); + } else { + (void)mongets(mtmp, !rn2(3) ? PICK_AXE : DAGGER); + } + } + break; +# ifdef KOPS + case S_KOP: /* create Keystone Kops with cream pies to + * throw. As suggested by KAA. [MRS] + */ + if (!rn2(4)) m_initthrow(mtmp, CREAM_PIE, 2); + if (!rn2(3)) (void)mongets(mtmp,(rn2(2)) ? CLUB : RUBBER_HOSE); + break; +# endif + case S_ORC: + if(rn2(2)) (void)mongets(mtmp, ORCISH_HELM); + switch (mm != PM_ORC_CAPTAIN ? mm : + rn2(2) ? PM_MORDOR_ORC : PM_URUK_HAI) { + case PM_MORDOR_ORC: + if(!rn2(3)) (void)mongets(mtmp, SCIMITAR); + if(!rn2(3)) (void)mongets(mtmp, ORCISH_SHIELD); + if(!rn2(3)) (void)mongets(mtmp, KNIFE); + if(!rn2(3)) (void)mongets(mtmp, ORCISH_CHAIN_MAIL); + break; + case PM_URUK_HAI: + if(!rn2(3)) (void)mongets(mtmp, ORCISH_CLOAK); + if(!rn2(3)) (void)mongets(mtmp, ORCISH_SHORT_SWORD); + if(!rn2(3)) (void)mongets(mtmp, IRON_SHOES); + if(!rn2(3)) { + (void)mongets(mtmp, ORCISH_BOW); + m_initthrow(mtmp, ORCISH_ARROW, 12); + } + if(!rn2(3)) (void)mongets(mtmp, URUK_HAI_SHIELD); + break; + default: + if (mm != PM_ORC_SHAMAN && rn2(2)) + (void)mongets(mtmp, (mm == PM_GOBLIN || rn2(2) == 0) + ? ORCISH_DAGGER : SCIMITAR); + } + break; + case S_OGRE: + if (!rn2(mm == PM_OGRE_KING ? 3 : mm == PM_OGRE_LORD ? 6 : 12)) + (void) mongets(mtmp, BATTLE_AXE); + else + (void) mongets(mtmp, CLUB); + break; + case S_TROLL: + if (!rn2(2)) switch (rn2(4)) { + case 0: (void)mongets(mtmp, RANSEUR); break; + case 1: (void)mongets(mtmp, PARTISAN); break; + case 2: (void)mongets(mtmp, GLAIVE); break; + case 3: (void)mongets(mtmp, SPETUM); break; + } + break; + case S_KOBOLD: + if (!rn2(4)) m_initthrow(mtmp, DART, 12); + break; + + case S_CENTAUR: + if (rn2(2)) { + if(ptr == &mons[PM_FOREST_CENTAUR]) { + (void)mongets(mtmp, BOW); + m_initthrow(mtmp, ARROW, 12); + } else { + (void)mongets(mtmp, CROSSBOW); + m_initthrow(mtmp, CROSSBOW_BOLT, 12); + } + } + break; + case S_WRAITH: + (void)mongets(mtmp, KNIFE); + (void)mongets(mtmp, LONG_SWORD); + break; + case S_ZOMBIE: + if (!rn2(4)) (void)mongets(mtmp, LEATHER_ARMOR); + if (!rn2(4)) + (void)mongets(mtmp, (rn2(3) ? KNIFE : SHORT_SWORD)); + break; + case S_LIZARD: + if (mm == PM_SALAMANDER) + (void)mongets(mtmp, (rn2(7) ? SPEAR : rn2(3) ? + TRIDENT : STILETTO)); + break; + case S_DEMON: + switch (mm) { + case PM_BALROG: + (void)mongets(mtmp, BULLWHIP); + (void)mongets(mtmp, BROADSWORD); + break; + case PM_ORCUS: + (void)mongets(mtmp, WAN_DEATH); /* the Wand of Orcus */ + break; + case PM_HORNED_DEVIL: + (void)mongets(mtmp, rn2(4) ? TRIDENT : BULLWHIP); + break; + case PM_DISPATER: + (void)mongets(mtmp, WAN_STRIKING); + break; + case PM_YEENOGHU: + (void)mongets(mtmp, FLAIL); + break; + } + /* prevent djinnis and mail daemons from leaving objects when + * they vanish + */ + if (!is_demon(ptr)) break; + /* fall thru */ +/* + * Now the general case, Some chance of getting some type + * of weapon for "normal" monsters. Certain special types + * of monsters will get a bonus chance or different selections. + */ + default: + { + int bias; + + bias = is_lord(ptr) + is_prince(ptr) * 2 + extra_nasty(ptr); + switch(rnd(14 - (2 * bias))) { + case 1: + if(strongmonst(ptr)) (void) mongets(mtmp, BATTLE_AXE); + else m_initthrow(mtmp, DART, 12); + break; + case 2: + if(strongmonst(ptr)) + (void) mongets(mtmp, TWO_HANDED_SWORD); + else { + (void) mongets(mtmp, CROSSBOW); + m_initthrow(mtmp, CROSSBOW_BOLT, 12); + } + break; + case 3: + (void) mongets(mtmp, BOW); + m_initthrow(mtmp, ARROW, 12); + break; + case 4: + if(strongmonst(ptr)) (void) mongets(mtmp, LONG_SWORD); + else m_initthrow(mtmp, DAGGER, 3); + break; + case 5: + if(strongmonst(ptr)) + (void) mongets(mtmp, LUCERN_HAMMER); + else (void) mongets(mtmp, AKLYS); + break; + default: + break; + } + } + break; + } + if ((int) mtmp->m_lev > rn2(75)) + (void) mongets(mtmp, rnd_offensive_item(mtmp)); +} + +#endif /* OVL2 */ +#ifdef OVL1 + +#ifdef GOLDOBJ +/* + * Makes up money for monster's inventory. + * This will change with silver & copper coins + */ +void +mkmonmoney(mtmp, amount) +struct monst *mtmp; +long amount; +{ + struct obj *gold = mksobj(GOLD_PIECE, FALSE, FALSE); + gold->quan = amount; + add_to_minv(mtmp, gold); +} +#endif + +STATIC_OVL void +m_initinv(mtmp) +register struct monst *mtmp; +{ + register int cnt; + register struct obj *otmp; + register struct permonst *ptr = mtmp->data; +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) return; +#endif +/* + * Soldiers get armour & rations - armour approximates their ac. + * Nymphs may get mirror or potion of object detection. + */ + switch(ptr->mlet) { + + case S_HUMAN: + if(is_mercenary(ptr)) { + register int mac; + + switch(monsndx(ptr)) { + case PM_GUARD: mac = -1; break; + case PM_SOLDIER: mac = 3; break; + case PM_SERGEANT: mac = 0; break; + case PM_LIEUTENANT: mac = -2; break; + case PM_CAPTAIN: mac = -3; break; + case PM_WATCHMAN: mac = 3; break; + case PM_WATCH_CAPTAIN: mac = -2; break; + default: impossible("odd mercenary %d?", monsndx(ptr)); + mac = 0; + break; + } + + if (mac < -1 && rn2(5)) + mac += 7 + mongets(mtmp, (rn2(5)) ? + PLATE_MAIL : CRYSTAL_PLATE_MAIL); + else if (mac < 3 && rn2(5)) + mac += 6 + mongets(mtmp, (rn2(3)) ? + SPLINT_MAIL : BANDED_MAIL); + else if (rn2(5)) + mac += 3 + mongets(mtmp, (rn2(3)) ? + RING_MAIL : STUDDED_LEATHER_ARMOR); + else + mac += 2 + mongets(mtmp, LEATHER_ARMOR); + + if (mac < 10 && rn2(3)) + mac += 1 + mongets(mtmp, HELMET); + else if (mac < 10 && rn2(2)) + mac += 1 + mongets(mtmp, DENTED_POT); + if (mac < 10 && rn2(3)) + mac += 1 + mongets(mtmp, SMALL_SHIELD); + else if (mac < 10 && rn2(2)) + mac += 2 + mongets(mtmp, LARGE_SHIELD); + if (mac < 10 && rn2(3)) + mac += 1 + mongets(mtmp, LOW_BOOTS); + else if (mac < 10 && rn2(2)) + mac += 2 + mongets(mtmp, HIGH_BOOTS); + if (mac < 10 && rn2(3)) + mac += 1 + mongets(mtmp, LEATHER_GLOVES); + else if (mac < 10 && rn2(2)) + mac += 1 + mongets(mtmp, LEATHER_CLOAK); + + if(ptr != &mons[PM_GUARD] && + ptr != &mons[PM_WATCHMAN] && + ptr != &mons[PM_WATCH_CAPTAIN]) { + if (!rn2(3)) (void) mongets(mtmp, K_RATION); + if (!rn2(2)) (void) mongets(mtmp, C_RATION); + if (ptr != &mons[PM_SOLDIER] && !rn2(3)) + (void) mongets(mtmp, BUGLE); + } else + if (ptr == &mons[PM_WATCHMAN] && rn2(3)) + (void) mongets(mtmp, TIN_WHISTLE); + } else if (ptr == &mons[PM_SHOPKEEPER]) { + (void) mongets(mtmp,SKELETON_KEY); + switch (rn2(4)) { + /* MAJOR fall through ... */ + case 0: (void) mongets(mtmp, WAN_MAGIC_MISSILE); + case 1: (void) mongets(mtmp, POT_EXTRA_HEALING); + case 2: (void) mongets(mtmp, POT_HEALING); + case 3: (void) mongets(mtmp, WAN_STRIKING); + } + } else if (ptr->msound == MS_PRIEST || + quest_mon_represents_role(ptr,PM_PRIEST)) { + (void) mongets(mtmp, rn2(7) ? ROBE : + rn2(3) ? CLOAK_OF_PROTECTION : + CLOAK_OF_MAGIC_RESISTANCE); + (void) mongets(mtmp, SMALL_SHIELD); +#ifndef GOLDOBJ + mtmp->mgold = (long)rn1(10,20); +#else + mkmonmoney(mtmp,(long)rn1(10,20)); +#endif + } else if (quest_mon_represents_role(ptr,PM_MONK)) { + (void) mongets(mtmp, rn2(11) ? ROBE : + CLOAK_OF_MAGIC_RESISTANCE); + } + break; + case S_NYMPH: + if(!rn2(2)) (void) mongets(mtmp, MIRROR); + if(!rn2(2)) (void) mongets(mtmp, POT_OBJECT_DETECTION); + break; + case S_GIANT: + if (ptr == &mons[PM_MINOTAUR]) { + if (!rn2(3) || (in_mklev && Is_earthlevel(&u.uz))) + (void) mongets(mtmp, WAN_DIGGING); + } else if (is_giant(ptr)) { + for (cnt = rn2((int)(mtmp->m_lev / 2)); cnt; cnt--) { + otmp = mksobj(rnd_class(DILITHIUM_CRYSTAL,LUCKSTONE-1), + FALSE, FALSE); + otmp->quan = (long) rn1(2, 3); + otmp->owt = weight(otmp); + (void) mpickobj(mtmp, otmp); + } + } + break; + case S_WRAITH: + if (ptr == &mons[PM_NAZGUL]) { + otmp = mksobj(RIN_INVISIBILITY, FALSE, FALSE); + curse(otmp); + (void) mpickobj(mtmp, otmp); + } + break; + case S_LICH: + if (ptr == &mons[PM_MASTER_LICH] && !rn2(13)) + (void)mongets(mtmp, (rn2(7) ? ATHAME : WAN_NOTHING)); + else if (ptr == &mons[PM_ARCH_LICH] && !rn2(3)) { + otmp = mksobj(rn2(3) ? ATHAME : QUARTERSTAFF, + TRUE, rn2(13) ? FALSE : TRUE); + if (otmp->spe < 2) otmp->spe = rnd(3); + if (!rn2(4)) otmp->oerodeproof = 1; + (void) mpickobj(mtmp, otmp); + } + break; + case S_MUMMY: + if (rn2(7)) (void)mongets(mtmp, MUMMY_WRAPPING); + break; + case S_QUANTMECH: + if (!rn2(20)) { + otmp = mksobj(LARGE_BOX, FALSE, FALSE); + otmp->spe = 1; /* flag for special box */ + otmp->owt = weight(otmp); + (void) mpickobj(mtmp, otmp); + } + break; + case S_LEPRECHAUN: +#ifndef GOLDOBJ + mtmp->mgold = (long) d(level_difficulty(), 30); +#else + mkmonmoney(mtmp, (long) d(level_difficulty(), 30)); +#endif + break; + case S_DEMON: + /* moved here from m_initweap() because these don't + have AT_WEAP so m_initweap() is not called for them */ + if (ptr == &mons[PM_ICE_DEVIL] && !rn2(4)) { + (void)mongets(mtmp, SPEAR); + } else if (ptr == &mons[PM_ASMODEUS]) { + (void)mongets(mtmp, WAN_COLD); + (void)mongets(mtmp, WAN_FIRE); + } + break; + default: + break; + } + + /* ordinary soldiers rarely have access to magic (or gold :-) */ + if (ptr == &mons[PM_SOLDIER] && rn2(13)) return; + + if ((int) mtmp->m_lev > rn2(50)) + (void) mongets(mtmp, rnd_defensive_item(mtmp)); + if ((int) mtmp->m_lev > rn2(100)) + (void) mongets(mtmp, rnd_misc_item(mtmp)); +#ifndef GOLDOBJ + if (likes_gold(ptr) && !mtmp->mgold && !rn2(5)) + mtmp->mgold = + (long) d(level_difficulty(), mtmp->minvent ? 5 : 10); +#else + if (likes_gold(ptr) && !findgold(mtmp->minvent) && !rn2(5)) + mkmonmoney(mtmp, (long) d(level_difficulty(), mtmp->minvent ? 5 : 10)); +#endif +} + +/* Note: for long worms, always call cutworm (cutworm calls clone_mon) */ +struct monst * +clone_mon(mon, x, y) +struct monst *mon; +xchar x, y; /* clone's preferred location or 0 (near mon) */ +{ + coord mm; + struct monst *m2; + + /* may be too weak or have been extinguished for population control */ + if (mon->mhp <= 1 || (mvitals[monsndx(mon->data)].mvflags & G_EXTINCT)) + return (struct monst *)0; + + if (x == 0) { + mm.x = mon->mx; + mm.y = mon->my; + if (!enexto(&mm, mm.x, mm.y, mon->data) || MON_AT(mm.x, mm.y)) + return (struct monst *)0; + } else if (!isok(x, y)) { + return (struct monst *)0; /* paranoia */ + } else { + mm.x = x; + mm.y = y; + if (MON_AT(mm.x, mm.y)) { + if (!enexto(&mm, mm.x, mm.y, mon->data) || MON_AT(mm.x, mm.y)) + return (struct monst *)0; + } + } + m2 = newmonst(0); + *m2 = *mon; /* copy condition of old monster */ + m2->nmon = fmon; + fmon = m2; + m2->m_id = flags.ident++; + if (!m2->m_id) m2->m_id = flags.ident++; /* ident overflowed */ + m2->mx = mm.x; + m2->my = mm.y; + + m2->minvent = (struct obj *) 0; /* objects don't clone */ + m2->mleashed = FALSE; +#ifndef GOLDOBJ + m2->mgold = 0L; +#endif + /* Max HP the same, but current HP halved for both. The caller + * might want to override this by halving the max HP also. + * When current HP is odd, the original keeps the extra point. + */ + m2->mhpmax = mon->mhpmax; + m2->mhp = mon->mhp / 2; + mon->mhp -= m2->mhp; + + /* since shopkeepers and guards will only be cloned if they've been + * polymorphed away from their original forms, the clone doesn't have + * room for the extra information. we also don't want two shopkeepers + * around for the same shop. + */ + if (mon->isshk) m2->isshk = FALSE; + if (mon->isgd) m2->isgd = FALSE; + if (mon->ispriest) m2->ispriest = FALSE; + m2->mxlth = 0; + place_monster(m2, m2->mx, m2->my); + if (emits_light(m2->data)) + new_light_source(m2->mx, m2->my, emits_light(m2->data), + LS_MONSTER, (genericptr_t)m2); + if (m2->mnamelth) { + m2->mnamelth = 0; /* or it won't get allocated */ + m2 = christen_monst(m2, NAME(mon)); + } else if (mon->isshk) { + m2 = christen_monst(m2, shkname(mon)); + } + + /* not all clones caused by player are tame or peaceful */ + if (!flags.mon_moving) { + if (mon->mtame) + m2->mtame = rn2(max(2 + u.uluck, 2)) ? mon->mtame : 0; + else if (mon->mpeaceful) + m2->mpeaceful = rn2(max(2 + u.uluck, 2)) ? 1 : 0; + } + + newsym(m2->mx,m2->my); /* display the new monster */ + if (m2->mtame) { + struct monst *m3; + + if (mon->isminion) { + m3 = newmonst(sizeof(struct epri) + mon->mnamelth); + *m3 = *m2; + m3->mxlth = sizeof(struct epri); + if (m2->mnamelth) Strcpy(NAME(m3), NAME(m2)); + *(EPRI(m3)) = *(EPRI(mon)); + replmon(m2, m3); + m2 = m3; + } else { + /* because m2 is a copy of mon it is tame but not init'ed. + * however, tamedog will not re-tame a tame dog, so m2 + * must be made non-tame to get initialized properly. + */ + m2->mtame = 0; + if ((m3 = tamedog(m2, (struct obj *)0)) != 0) { + m2 = m3; + *(EDOG(m2)) = *(EDOG(mon)); + } + } + } + set_malign(m2); + + return m2; +} + +/* + * Propagate a species + * + * Once a certain number of monsters are created, don't create any more + * at random (i.e. make them extinct). The previous (3.2) behavior was + * to do this when a certain number had _died_, which didn't make + * much sense. + * + * Returns FALSE propagation unsuccessful + * TRUE propagation successful + */ +boolean +propagate(mndx, tally, ghostly) +int mndx; +boolean tally; +boolean ghostly; +{ + boolean result; + uchar lim = mbirth_limit(mndx); + boolean gone = (mvitals[mndx].mvflags & G_GONE); /* genocided or extinct */ + + result = (((int) mvitals[mndx].born < lim) && !gone) ? TRUE : FALSE; + + /* if it's unique, don't ever make it again */ + if (mons[mndx].geno & G_UNIQ) mvitals[mndx].mvflags |= G_EXTINCT; + + if (mvitals[mndx].born < 255 && tally && (!ghostly || (ghostly && result))) + mvitals[mndx].born++; + if ((int) mvitals[mndx].born >= lim && !(mons[mndx].geno & G_NOGEN) && + !(mvitals[mndx].mvflags & G_EXTINCT)) { +#if defined(DEBUG) && defined(WIZARD) + if (wizard) pline("Automatically extinguished %s.", + makeplural(mons[mndx].mname)); +#endif + mvitals[mndx].mvflags |= G_EXTINCT; + reset_rndmonst(mndx); + } + return result; +} + +/* + * called with [x,y] = coordinates; + * [0,0] means anyplace + * [u.ux,u.uy] means: near player (if !in_mklev) + * + * In case we make a monster group, only return the one at [x,y]. + */ +struct monst * +makemon(ptr, x, y, mmflags) +register struct permonst *ptr; +register int x, y; +register int mmflags; +{ + register struct monst *mtmp; + int mndx, mcham, ct, mitem, xlth; + boolean anymon = (!ptr); + boolean byyou = (x == u.ux && y == u.uy); + boolean allow_minvent = ((mmflags & NO_MINVENT) == 0); + boolean countbirth = ((mmflags & MM_NOCOUNTBIRTH) == 0); + unsigned gpflags = (mmflags & MM_IGNOREWATER) ? MM_IGNOREWATER : 0; + + /* if caller wants random location, do it here */ + if(x == 0 && y == 0) { + int tryct = 0; /* careful with bigrooms */ + struct monst fakemon; + + fakemon.data = ptr; /* set up for goodpos */ + do { + x = rn1(COLNO-3,2); + y = rn2(ROWNO); + } while(!goodpos(x, y, ptr ? &fakemon : (struct monst *)0, gpflags) || + (!in_mklev && tryct++ < 50 && cansee(x, y))); + } else if (byyou && !in_mklev) { + coord bypos; + + if(enexto_core(&bypos, u.ux, u.uy, ptr, gpflags)) { + x = bypos.x; + y = bypos.y; + } else + return((struct monst *)0); + } + + /* Does monster already exist at the position? */ + if(MON_AT(x, y)) { + if ((mmflags & MM_ADJACENTOK) != 0) { + coord bypos; + if(enexto_core(&bypos, x, y, ptr, gpflags)) { + x = bypos.x; + y = bypos.y; + } else + return((struct monst *) 0); + } else + return((struct monst *) 0); + } + + if(ptr){ + mndx = monsndx(ptr); + /* if you are to make a specific monster and it has + already been genocided, return */ + if (mvitals[mndx].mvflags & G_GENOD) return((struct monst *) 0); +#if defined(WIZARD) && defined(DEBUG) + if (wizard && (mvitals[mndx].mvflags & G_EXTINCT)) + pline("Explicitly creating extinct monster %s.", + mons[mndx].mname); +#endif + } else { + /* make a random (common) monster that can survive here. + * (the special levels ask for random monsters at specific + * positions, causing mass drowning on the medusa level, + * for instance.) + */ + int tryct = 0; /* maybe there are no good choices */ + struct monst fakemon; + do { + if(!(ptr = rndmonst())) { +#ifdef DEBUG + pline("Warning: no monster."); +#endif + return((struct monst *) 0); /* no more monsters! */ + } + fakemon.data = ptr; /* set up for goodpos */ + } while(!goodpos(x, y, &fakemon, gpflags) && tryct++ < 50); + mndx = monsndx(ptr); + } + (void) propagate(mndx, countbirth, FALSE); + xlth = ptr->pxlth; + if (mmflags & MM_EDOG) xlth += sizeof(struct edog); + else if (mmflags & MM_EMIN) xlth += sizeof(struct emin); + mtmp = newmonst(xlth); + *mtmp = zeromonst; /* clear all entries in structure */ + (void)memset((genericptr_t)mtmp->mextra, 0, xlth); + mtmp->nmon = fmon; + fmon = mtmp; + mtmp->m_id = flags.ident++; + if (!mtmp->m_id) mtmp->m_id = flags.ident++; /* ident overflowed */ + set_mon_data(mtmp, ptr, 0); + if (mtmp->data->msound == MS_LEADER) + quest_status.leader_m_id = mtmp->m_id; + mtmp->mxlth = xlth; + mtmp->mnum = mndx; + + mtmp->m_lev = adj_lev(ptr); + if (is_golem(ptr)) { + mtmp->mhpmax = mtmp->mhp = golemhp(mndx); + } else if (is_rider(ptr)) { + /* We want low HP, but a high mlevel so they can attack well */ + mtmp->mhpmax = mtmp->mhp = d(10,8); + } else if (ptr->mlevel > 49) { + /* "special" fixed hp monster + * the hit points are encoded in the mlevel in a somewhat strange + * way to fit in the 50..127 positive range of a signed character + * above the 1..49 that indicate "normal" monster levels */ + mtmp->mhpmax = mtmp->mhp = 2*(ptr->mlevel - 6); + mtmp->m_lev = mtmp->mhp / 4; /* approximation */ + } else if (ptr->mlet == S_DRAGON && mndx >= PM_GRAY_DRAGON) { + /* adult dragons */ + mtmp->mhpmax = mtmp->mhp = (int) (In_endgame(&u.uz) ? + (8 * mtmp->m_lev) : (4 * mtmp->m_lev + d((int)mtmp->m_lev, 4))); + } else if (!mtmp->m_lev) { + mtmp->mhpmax = mtmp->mhp = rnd(4); + } else { + mtmp->mhpmax = mtmp->mhp = d((int)mtmp->m_lev, 8); + if (is_home_elemental(ptr)) + mtmp->mhpmax = (mtmp->mhp *= 3); + } + + if (is_female(ptr)) mtmp->female = TRUE; + else if (is_male(ptr)) mtmp->female = FALSE; + else mtmp->female = rn2(2); /* ignored for neuters */ + + if (In_sokoban(&u.uz) && !mindless(ptr)) /* know about traps here */ + mtmp->mtrapseen = (1L << (PIT - 1)) | (1L << (HOLE - 1)); + if (ptr->msound == MS_LEADER) /* leader knows about portal */ + mtmp->mtrapseen |= (1L << (MAGIC_PORTAL-1)); + + place_monster(mtmp, x, y); + mtmp->mcansee = mtmp->mcanmove = TRUE; + mtmp->mpeaceful = (mmflags & MM_ANGRY) ? FALSE : peace_minded(ptr); + + switch(ptr->mlet) { + case S_MIMIC: + set_mimic_sym(mtmp); + break; + case S_SPIDER: + case S_SNAKE: + if(in_mklev) + if(x && y) + (void) mkobj_at(0, x, y, TRUE); + if(hides_under(ptr) && OBJ_AT(x, y)) + mtmp->mundetected = TRUE; + break; + case S_LIGHT: + case S_ELEMENTAL: + if (mndx == PM_STALKER || mndx == PM_BLACK_LIGHT) { + mtmp->perminvis = TRUE; + mtmp->minvis = TRUE; + } + break; + case S_EEL: + if (is_pool(x, y)) + mtmp->mundetected = TRUE; + break; + case S_LEPRECHAUN: + mtmp->msleeping = 1; + break; + case S_JABBERWOCK: + case S_NYMPH: + if (rn2(5) && !u.uhave.amulet) mtmp->msleeping = 1; + break; + case S_ORC: + if (Race_if(PM_ELF)) mtmp->mpeaceful = FALSE; + break; + case S_UNICORN: + if (is_unicorn(ptr) && + sgn(u.ualign.type) == sgn(ptr->maligntyp)) + mtmp->mpeaceful = TRUE; + break; + case S_BAT: + if (Inhell && is_bat(ptr)) + mon_adjust_speed(mtmp, 2, (struct obj *)0); + break; + } + if ((ct = emits_light(mtmp->data)) > 0) + new_light_source(mtmp->mx, mtmp->my, ct, + LS_MONSTER, (genericptr_t)mtmp); + mitem = 0; /* extra inventory item for this monster */ + + if ((mcham = pm_to_cham(mndx)) != CHAM_ORDINARY) { + /* If you're protected with a ring, don't create + * any shape-changing chameleons -dgk + */ + if (Protection_from_shape_changers) + mtmp->cham = CHAM_ORDINARY; + else { + mtmp->cham = mcham; + (void) newcham(mtmp, rndmonst(), FALSE, FALSE); + } + } else if (mndx == PM_WIZARD_OF_YENDOR) { + mtmp->iswiz = TRUE; + flags.no_of_wizards++; + if (flags.no_of_wizards == 1 && Is_earthlevel(&u.uz)) + mitem = SPE_DIG; + } else if (mndx == PM_DJINNI) { + flags.djinni_count++; + } else if (mndx == PM_GHOST) { + flags.ghost_count++; + if (!(mmflags & MM_NONAME)) + mtmp = christen_monst(mtmp, rndghostname()); + } else if (mndx == PM_VLAD_THE_IMPALER) { + mitem = CANDELABRUM_OF_INVOCATION; + } else if (mndx == PM_CROESUS) { + mitem = TWO_HANDED_SWORD; + } else if (ptr->msound == MS_NEMESIS) { + mitem = BELL_OF_OPENING; + } else if (mndx == PM_PESTILENCE) { + mitem = POT_SICKNESS; + } + if (mitem && allow_minvent) (void) mongets(mtmp, mitem); + + if(in_mklev) { + if(((is_ndemon(ptr)) || + (mndx == PM_WUMPUS) || + (mndx == PM_LONG_WORM) || + (mndx == PM_GIANT_EEL)) && !u.uhave.amulet && rn2(5)) + mtmp->msleeping = TRUE; + } else { + if(byyou) { + newsym(mtmp->mx,mtmp->my); + set_apparxy(mtmp); + } + } + if(is_dprince(ptr) && ptr->msound == MS_BRIBE) { + mtmp->mpeaceful = mtmp->minvis = mtmp->perminvis = 1; + mtmp->mavenge = 0; + if (uwep && uwep->oartifact == ART_EXCALIBUR) + mtmp->mpeaceful = mtmp->mtame = FALSE; + } +#ifndef DCC30_BUG + if (mndx == PM_LONG_WORM && (mtmp->wormno = get_wormno()) != 0) +#else + /* DICE 3.0 doesn't like assigning and comparing mtmp->wormno in the + * same expression. + */ + if (mndx == PM_LONG_WORM && + (mtmp->wormno = get_wormno(), mtmp->wormno != 0)) +#endif + { + /* we can now create worms with tails - 11/91 */ + initworm(mtmp, rn2(5)); + if (count_wsegs(mtmp)) place_worm_tail_randomly(mtmp, x, y); + } + set_malign(mtmp); /* having finished peaceful changes */ + if(anymon) { + if ((ptr->geno & G_SGROUP) && rn2(2)) { + m_initsgrp(mtmp, mtmp->mx, mtmp->my); + } else if (ptr->geno & G_LGROUP) { + if(rn2(3)) m_initlgrp(mtmp, mtmp->mx, mtmp->my); + else m_initsgrp(mtmp, mtmp->mx, mtmp->my); + } + } + + if (allow_minvent) { + if(is_armed(ptr)) + m_initweap(mtmp); /* equip with weapons / armor */ + m_initinv(mtmp); /* add on a few special items incl. more armor */ + m_dowear(mtmp, TRUE); + } else { + /* no initial inventory is allowed */ + if (mtmp->minvent) discard_minvent(mtmp); + mtmp->minvent = (struct obj *)0; /* caller expects this */ + } + if ((ptr->mflags3 & M3_WAITMASK) && !(mmflags & MM_NOWAIT)) { + if (ptr->mflags3 & M3_WAITFORU) + mtmp->mstrategy |= STRAT_WAITFORU; + if (ptr->mflags3 & M3_CLOSE) + mtmp->mstrategy |= STRAT_CLOSE; + } + + if (!in_mklev) + newsym(mtmp->mx,mtmp->my); /* make sure the mon shows up */ + + return(mtmp); +} + +int +mbirth_limit(mndx) +int mndx; +{ + /* assert(MAXMONNO < 255); */ + return (mndx == PM_NAZGUL ? 9 : mndx == PM_ERINYS ? 3 : MAXMONNO); +} + +/* used for wand/scroll/spell of create monster */ +/* returns TRUE iff you know monsters have been created */ +boolean +create_critters(cnt, mptr) +int cnt; +struct permonst *mptr; /* usually null; used for confused reading */ +{ + coord c; + int x, y; + struct monst *mon; + boolean known = FALSE; +#ifdef WIZARD + boolean ask = wizard; +#endif + + while (cnt--) { +#ifdef WIZARD + if (ask) { + if (create_particular()) { + known = TRUE; + continue; + } + else ask = FALSE; /* ESC will shut off prompting */ + } +#endif + x = u.ux, y = u.uy; + /* if in water, try to encourage an aquatic monster + by finding and then specifying another wet location */ + if (!mptr && u.uinwater && enexto(&c, x, y, &mons[PM_GIANT_EEL])) + x = c.x, y = c.y; + + mon = makemon(mptr, x, y, NO_MM_FLAGS); + if (mon && canspotmon(mon)) known = TRUE; + } + return known; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +STATIC_OVL boolean +uncommon(mndx) +int mndx; +{ + if (mons[mndx].geno & (G_NOGEN | G_UNIQ)) return TRUE; + if (mvitals[mndx].mvflags & G_GONE) return TRUE; + if (Inhell) + return(mons[mndx].maligntyp > A_NEUTRAL); + else + return((mons[mndx].geno & G_HELL) != 0); +} + +/* + * shift the probability of a monster's generation by + * comparing the dungeon alignment and monster alignment. + * return an integer in the range of 0-5. + */ +STATIC_OVL int +align_shift(ptr) +register struct permonst *ptr; +{ + static NEARDATA long oldmoves = 0L; /* != 1, starting value of moves */ + static NEARDATA s_level *lev; + register int alshift; + + if(oldmoves != moves) { + lev = Is_special(&u.uz); + oldmoves = moves; + } + switch((lev) ? lev->flags.align : dungeons[u.uz.dnum].flags.align) { + default: /* just in case */ + case AM_NONE: alshift = 0; + break; + case AM_LAWFUL: alshift = (ptr->maligntyp+20)/(2*ALIGNWEIGHT); + break; + case AM_NEUTRAL: alshift = (20 - abs(ptr->maligntyp))/ALIGNWEIGHT; + break; + case AM_CHAOTIC: alshift = (-(ptr->maligntyp-20))/(2*ALIGNWEIGHT); + break; + } + return alshift; +} + +static NEARDATA struct { + int choice_count; + char mchoices[SPECIAL_PM]; /* value range is 0..127 */ +} rndmonst_state = { -1, {0} }; + +/* select a random monster type */ +struct permonst * +rndmonst() +{ + register struct permonst *ptr; + register int mndx, ct; + + if (u.uz.dnum == quest_dnum && rn2(7) && (ptr = qt_montype()) != 0) + return ptr; + + if (rndmonst_state.choice_count < 0) { /* need to recalculate */ + int zlevel, minmlev, maxmlev; + boolean elemlevel; +#ifdef REINCARNATION + boolean upper; +#endif + + rndmonst_state.choice_count = 0; + /* look for first common monster */ + for (mndx = LOW_PM; mndx < SPECIAL_PM; mndx++) { + if (!uncommon(mndx)) break; + rndmonst_state.mchoices[mndx] = 0; + } + if (mndx == SPECIAL_PM) { + /* evidently they've all been exterminated */ +#ifdef DEBUG + pline("rndmonst: no common mons!"); +#endif + return (struct permonst *)0; + } /* else `mndx' now ready for use below */ + zlevel = level_difficulty(); + /* determine the level of the weakest monster to make. */ + minmlev = zlevel / 6; + /* determine the level of the strongest monster to make. */ + maxmlev = (zlevel + u.ulevel) / 2; +#ifdef REINCARNATION + upper = Is_rogue_level(&u.uz); +#endif + elemlevel = In_endgame(&u.uz) && !Is_astralevel(&u.uz); + +/* + * Find out how many monsters exist in the range we have selected. + */ + /* (`mndx' initialized above) */ + for ( ; mndx < SPECIAL_PM; mndx++) { + ptr = &mons[mndx]; + rndmonst_state.mchoices[mndx] = 0; + if (tooweak(mndx, minmlev) || toostrong(mndx, maxmlev)) + continue; +#ifdef REINCARNATION + if (upper && !isupper(def_monsyms[(int)(ptr->mlet)])) continue; +#endif + if (elemlevel && wrong_elem_type(ptr)) continue; + if (uncommon(mndx)) continue; + if (Inhell && (ptr->geno & G_NOHELL)) continue; + ct = (int)(ptr->geno & G_FREQ) + align_shift(ptr); + if (ct < 0 || ct > 127) + panic("rndmonst: bad count [#%d: %d]", mndx, ct); + rndmonst_state.choice_count += ct; + rndmonst_state.mchoices[mndx] = (char)ct; + } +/* + * Possible modification: if choice_count is "too low", + * expand minmlev..maxmlev range and try again. + */ + } /* choice_count+mchoices[] recalc */ + + if (rndmonst_state.choice_count <= 0) { + /* maybe no common mons left, or all are too weak or too strong */ +#ifdef DEBUG + Norep("rndmonst: choice_count=%d", rndmonst_state.choice_count); +#endif + return (struct permonst *)0; + } + +/* + * Now, select a monster at random. + */ + ct = rnd(rndmonst_state.choice_count); + for (mndx = LOW_PM; mndx < SPECIAL_PM; mndx++) + if ((ct -= (int)rndmonst_state.mchoices[mndx]) <= 0) break; + + if (mndx == SPECIAL_PM || uncommon(mndx)) { /* shouldn't happen */ + impossible("rndmonst: bad `mndx' [#%d]", mndx); + return (struct permonst *)0; + } + return &mons[mndx]; +} + +/* called when you change level (experience or dungeon depth) or when + monster species can no longer be created (genocide or extinction) */ +void +reset_rndmonst(mndx) +int mndx; /* particular species that can no longer be created */ +{ + /* cached selection info is out of date */ + if (mndx == NON_PM) { + rndmonst_state.choice_count = -1; /* full recalc needed */ + } else if (mndx < SPECIAL_PM) { + rndmonst_state.choice_count -= rndmonst_state.mchoices[mndx]; + rndmonst_state.mchoices[mndx] = 0; + } /* note: safe to ignore extinction of unique monsters */ +} + +#endif /* OVL0 */ +#ifdef OVL1 + +/* The routine below is used to make one of the multiple types + * of a given monster class. The second parameter specifies a + * special casing bit mask to allow the normal genesis + * masks to be deactivated. Returns 0 if no monsters + * in that class can be made. + */ + +struct permonst * +mkclass(class,spc) +char class; +int spc; +{ + register int first, last, num = 0; + int maxmlev, mask = (G_NOGEN | G_UNIQ) & ~spc; + + maxmlev = level_difficulty() >> 1; + if(class < 1 || class >= MAXMCLASSES) { + impossible("mkclass called with bad class!"); + return((struct permonst *) 0); + } +/* Assumption #1: monsters of a given class are contiguous in the + * mons[] array. + */ + for (first = LOW_PM; first < SPECIAL_PM; first++) + if (mons[first].mlet == class) break; + if (first == SPECIAL_PM) return (struct permonst *) 0; + + for (last = first; + last < SPECIAL_PM && mons[last].mlet == class; last++) + if (!(mvitals[last].mvflags & G_GONE) && !(mons[last].geno & mask) + && !is_placeholder(&mons[last])) { + /* consider it */ + if(num && toostrong(last, maxmlev) && + monstr[last] != monstr[last-1] && rn2(2)) break; + num += mons[last].geno & G_FREQ; + } + + if(!num) return((struct permonst *) 0); + +/* Assumption #2: monsters of a given class are presented in ascending + * order of strength. + */ + for(num = rnd(num); num > 0; first++) + if (!(mvitals[first].mvflags & G_GONE) && !(mons[first].geno & mask) + && !is_placeholder(&mons[first])) { + /* skew towards lower value monsters at lower exp. levels */ + num -= mons[first].geno & G_FREQ; + if (num && adj_lev(&mons[first]) > (u.ulevel*2)) { + /* but not when multiple monsters are same level */ + if (mons[first].mlevel != mons[first+1].mlevel) + num--; + } + } + first--; /* correct an off-by-one error */ + + return(&mons[first]); +} + +int +adj_lev(ptr) /* adjust strength of monsters based on u.uz and u.ulevel */ +register struct permonst *ptr; +{ + int tmp, tmp2; + + if (ptr == &mons[PM_WIZARD_OF_YENDOR]) { + /* does not depend on other strengths, but does get stronger + * every time he is killed + */ + tmp = ptr->mlevel + mvitals[PM_WIZARD_OF_YENDOR].died; + if (tmp > 49) tmp = 49; + return tmp; + } + + if((tmp = ptr->mlevel) > 49) return(50); /* "special" demons/devils */ + tmp2 = (level_difficulty() - tmp); + if(tmp2 < 0) tmp--; /* if mlevel > u.uz decrement tmp */ + else tmp += (tmp2 / 5); /* else increment 1 per five diff */ + + tmp2 = (u.ulevel - ptr->mlevel); /* adjust vs. the player */ + if(tmp2 > 0) tmp += (tmp2 / 4); /* level as well */ + + tmp2 = (3 * ((int) ptr->mlevel))/ 2; /* crude upper limit */ + if (tmp2 > 49) tmp2 = 49; /* hard upper limit */ + return((tmp > tmp2) ? tmp2 : (tmp > 0 ? tmp : 0)); /* 0 lower limit */ +} + +#endif /* OVL1 */ +#ifdef OVLB + +struct permonst * +grow_up(mtmp, victim) /* `mtmp' might "grow up" into a bigger version */ +struct monst *mtmp, *victim; +{ + int oldtype, newtype, max_increase, cur_increase, + lev_limit, hp_threshold; + struct permonst *ptr = mtmp->data; + + /* monster died after killing enemy but before calling this function */ + /* currently possible if killing a gas spore */ + if (mtmp->mhp <= 0) + return ((struct permonst *)0); + + /* note: none of the monsters with special hit point calculations + have both little and big forms */ + oldtype = monsndx(ptr); + newtype = little_to_big(oldtype); + if (newtype == PM_PRIEST && mtmp->female) newtype = PM_PRIESTESS; + + /* growth limits differ depending on method of advancement */ + if (victim) { /* killed a monster */ + /* + * The HP threshold is the maximum number of hit points for the + * current level; once exceeded, a level will be gained. + * Possible bug: if somehow the hit points are already higher + * than that, monster will gain a level without any increase in HP. + */ + hp_threshold = mtmp->m_lev * 8; /* normal limit */ + if (!mtmp->m_lev) + hp_threshold = 4; + else if (is_golem(ptr)) /* strange creatures */ + hp_threshold = ((mtmp->mhpmax / 10) + 1) * 10 - 1; + else if (is_home_elemental(ptr)) + hp_threshold *= 3; + lev_limit = 3 * (int)ptr->mlevel / 2; /* same as adj_lev() */ + /* If they can grow up, be sure the level is high enough for that */ + if (oldtype != newtype && mons[newtype].mlevel > lev_limit) + lev_limit = (int)mons[newtype].mlevel; + /* number of hit points to gain; unlike for the player, we put + the limit at the bottom of the next level rather than the top */ + max_increase = rnd((int)victim->m_lev + 1); + if (mtmp->mhpmax + max_increase > hp_threshold + 1) + max_increase = max((hp_threshold + 1) - mtmp->mhpmax, 0); + cur_increase = (max_increase > 1) ? rn2(max_increase) : 0; + } else { + /* a gain level potion or wraith corpse; always go up a level + unless already at maximum (49 is hard upper limit except + for demon lords, who start at 50 and can't go any higher) */ + max_increase = cur_increase = rnd(8); + hp_threshold = 0; /* smaller than `mhpmax + max_increase' */ + lev_limit = 50; /* recalc below */ + } + + mtmp->mhpmax += max_increase; + mtmp->mhp += cur_increase; + if (mtmp->mhpmax <= hp_threshold) + return ptr; /* doesn't gain a level */ + + if (is_mplayer(ptr)) lev_limit = 30; /* same as player */ + else if (lev_limit < 5) lev_limit = 5; /* arbitrary */ + else if (lev_limit > 49) lev_limit = (ptr->mlevel > 49 ? 50 : 49); + + if ((int)++mtmp->m_lev >= mons[newtype].mlevel && newtype != oldtype) { + ptr = &mons[newtype]; + if (mvitals[newtype].mvflags & G_GENOD) { /* allow G_EXTINCT */ + if (sensemon(mtmp)) + pline("As %s grows up into %s, %s %s!", mon_nam(mtmp), + an(ptr->mname), mhe(mtmp), + nonliving(ptr) ? "expires" : "dies"); + set_mon_data(mtmp, ptr, -1); /* keep mvitals[] accurate */ + mondied(mtmp); + return (struct permonst *)0; + } + set_mon_data(mtmp, ptr, 1); /* preserve intrinsics */ + newsym(mtmp->mx, mtmp->my); /* color may change */ + lev_limit = (int)mtmp->m_lev; /* never undo increment */ + } + /* sanity checks */ + if ((int)mtmp->m_lev > lev_limit) { + mtmp->m_lev--; /* undo increment */ + /* HP might have been allowed to grow when it shouldn't */ + if (mtmp->mhpmax == hp_threshold + 1) mtmp->mhpmax--; + } + if (mtmp->mhpmax > 50*8) mtmp->mhpmax = 50*8; /* absolute limit */ + if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; + + return ptr; +} + +#endif /* OVLB */ +#ifdef OVL1 + +int +mongets(mtmp, otyp) +register struct monst *mtmp; +register int otyp; +{ + register struct obj *otmp; + int spe; + + if (!otyp) return 0; + otmp = mksobj(otyp, TRUE, FALSE); + if (otmp) { + if (mtmp->data->mlet == S_DEMON) { + /* demons never get blessed objects */ + if (otmp->blessed) curse(otmp); + } else if(is_lminion(mtmp)) { + /* lawful minions don't get cursed, bad, or rusting objects */ + otmp->cursed = FALSE; + if(otmp->spe < 0) otmp->spe = 0; + otmp->oerodeproof = TRUE; + } else if(is_mplayer(mtmp->data) && is_sword(otmp)) { + otmp->spe = (3 + rn2(4)); + } + + if(otmp->otyp == CANDELABRUM_OF_INVOCATION) { + otmp->spe = 0; + otmp->age = 0L; + otmp->lamplit = FALSE; + otmp->blessed = otmp->cursed = FALSE; + } else if (otmp->otyp == BELL_OF_OPENING) { + otmp->blessed = otmp->cursed = FALSE; + } else if (otmp->otyp == SPE_BOOK_OF_THE_DEAD) { + otmp->blessed = FALSE; + otmp->cursed = TRUE; + } + + /* leaders don't tolerate inferior quality battle gear */ + if (is_prince(mtmp->data)) { + if (otmp->oclass == WEAPON_CLASS && otmp->spe < 1) + otmp->spe = 1; + else if (otmp->oclass == ARMOR_CLASS && otmp->spe < 0) + otmp->spe = 0; + } + + spe = otmp->spe; + (void) mpickobj(mtmp, otmp); /* might free otmp */ + return(spe); + } else return(0); +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +golemhp(type) +int type; +{ + switch(type) { + case PM_STRAW_GOLEM: return 20; + case PM_PAPER_GOLEM: return 20; + case PM_ROPE_GOLEM: return 30; + case PM_LEATHER_GOLEM: return 40; + case PM_GOLD_GOLEM: return 40; + case PM_WOOD_GOLEM: return 50; + case PM_FLESH_GOLEM: return 40; + case PM_CLAY_GOLEM: return 50; + case PM_STONE_GOLEM: return 60; + case PM_GLASS_GOLEM: return 60; + case PM_IRON_GOLEM: return 80; + default: return 0; + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* + * Alignment vs. yours determines monster's attitude to you. + * ( some "animal" types are co-aligned, but also hungry ) + */ +boolean +peace_minded(ptr) +register struct permonst *ptr; +{ + aligntyp mal = ptr->maligntyp, ual = u.ualign.type; + + if (always_peaceful(ptr)) return TRUE; + if (always_hostile(ptr)) return FALSE; + if (ptr->msound == MS_LEADER || ptr->msound == MS_GUARDIAN) + return TRUE; + if (ptr->msound == MS_NEMESIS) return FALSE; + + if (race_peaceful(ptr)) return TRUE; + if (race_hostile(ptr)) return FALSE; + + /* the monster is hostile if its alignment is different from the + * player's */ + if (sgn(mal) != sgn(ual)) return FALSE; + + /* Negative monster hostile to player with Amulet. */ + if (mal < A_NEUTRAL && u.uhave.amulet) return FALSE; + + /* minions are hostile to players that have strayed at all */ + if (is_minion(ptr)) return((boolean)(u.ualign.record >= 0)); + + /* Last case: a chance of a co-aligned monster being + * hostile. This chance is greater if the player has strayed + * (u.ualign.record negative) or the monster is not strongly aligned. + */ + return((boolean)(!!rn2(16 + (u.ualign.record < -15 ? -15 : u.ualign.record)) && + !!rn2(2 + abs(mal)))); +} + +/* Set malign to have the proper effect on player alignment if monster is + * killed. Negative numbers mean it's bad to kill this monster; positive + * numbers mean it's good. Since there are more hostile monsters than + * peaceful monsters, the penalty for killing a peaceful monster should be + * greater than the bonus for killing a hostile monster to maintain balance. + * Rules: + * it's bad to kill peaceful monsters, potentially worse to kill always- + * peaceful monsters + * it's never bad to kill a hostile monster, although it may not be good + */ +void +set_malign(mtmp) +struct monst *mtmp; +{ + schar mal = mtmp->data->maligntyp; + boolean coaligned; + + if (mtmp->ispriest || mtmp->isminion) { + /* some monsters have individual alignments; check them */ + if (mtmp->ispriest) + mal = EPRI(mtmp)->shralign; + else if (mtmp->isminion) + mal = EMIN(mtmp)->min_align; + /* unless alignment is none, set mal to -5,0,5 */ + /* (see align.h for valid aligntyp values) */ + if(mal != A_NONE) + mal *= 5; + } + + coaligned = (sgn(mal) == sgn(u.ualign.type)); + if (mtmp->data->msound == MS_LEADER) { + mtmp->malign = -20; + } else if (mal == A_NONE) { + if (mtmp->mpeaceful) + mtmp->malign = 0; + else + mtmp->malign = 20; /* really hostile */ + } else if (always_peaceful(mtmp->data)) { + int absmal = abs(mal); + if (mtmp->mpeaceful) + mtmp->malign = -3*max(5,absmal); + else + mtmp->malign = 3*max(5,absmal); /* renegade */ + } else if (always_hostile(mtmp->data)) { + int absmal = abs(mal); + if (coaligned) + mtmp->malign = 0; + else + mtmp->malign = max(5,absmal); + } else if (coaligned) { + int absmal = abs(mal); + if (mtmp->mpeaceful) + mtmp->malign = -3*max(3,absmal); + else /* renegade */ + mtmp->malign = max(3,absmal); + } else /* not coaligned and therefore hostile */ + mtmp->malign = abs(mal); +} + +#endif /* OVL1 */ +#ifdef OVLB + +static NEARDATA char syms[] = { + MAXOCLASSES, MAXOCLASSES+1, RING_CLASS, WAND_CLASS, WEAPON_CLASS, + FOOD_CLASS, COIN_CLASS, SCROLL_CLASS, POTION_CLASS, ARMOR_CLASS, + AMULET_CLASS, TOOL_CLASS, ROCK_CLASS, GEM_CLASS, SPBOOK_CLASS, + S_MIMIC_DEF, S_MIMIC_DEF, S_MIMIC_DEF, +}; + +void +set_mimic_sym(mtmp) /* KAA, modified by ERS */ +register struct monst *mtmp; +{ + int typ, roomno, rt; + unsigned appear, ap_type; + int s_sym; + struct obj *otmp; + int mx, my; + + if (!mtmp) return; + mx = mtmp->mx; my = mtmp->my; + typ = levl[mx][my].typ; + /* only valid for INSIDE of room */ + roomno = levl[mx][my].roomno - ROOMOFFSET; + if (roomno >= 0) + rt = rooms[roomno].rtype; +#ifdef SPECIALIZATION + else if (IS_ROOM(typ)) + rt = OROOM, roomno = 0; +#endif + else rt = 0; /* roomno < 0 case for GCC_WARN */ + + if (OBJ_AT(mx, my)) { + ap_type = M_AP_OBJECT; + appear = level.objects[mx][my]->otyp; + } else if (IS_DOOR(typ) || IS_WALL(typ) || + typ == SDOOR || typ == SCORR) { + ap_type = M_AP_FURNITURE; + /* + * If there is a wall to the left that connects to this + * location, then the mimic mimics a horizontal closed door. + * This does not allow doors to be in corners of rooms. + */ + if (mx != 0 && + (levl[mx-1][my].typ == HWALL || + levl[mx-1][my].typ == TLCORNER || + levl[mx-1][my].typ == TRWALL || + levl[mx-1][my].typ == BLCORNER || + levl[mx-1][my].typ == TDWALL || + levl[mx-1][my].typ == CROSSWALL|| + levl[mx-1][my].typ == TUWALL )) + appear = S_hcdoor; + else + appear = S_vcdoor; + + if(!mtmp->minvis || See_invisible) + block_point(mx,my); /* vision */ + } else if (level.flags.is_maze_lev && rn2(2)) { + ap_type = M_AP_OBJECT; + appear = STATUE; + } else if (roomno < 0) { + ap_type = M_AP_OBJECT; + appear = BOULDER; + if(!mtmp->minvis || See_invisible) + block_point(mx,my); /* vision */ + } else if (rt == ZOO || rt == VAULT) { + ap_type = M_AP_OBJECT; + appear = GOLD_PIECE; + } else if (rt == DELPHI) { + if (rn2(2)) { + ap_type = M_AP_OBJECT; + appear = STATUE; + } else { + ap_type = M_AP_FURNITURE; + appear = S_fountain; + } + } else if (rt == TEMPLE) { + ap_type = M_AP_FURNITURE; + appear = S_altar; + /* + * We won't bother with beehives, morgues, barracks, throne rooms + * since they shouldn't contain too many mimics anyway... + */ + } else if (rt >= SHOPBASE) { + s_sym = get_shop_item(rt - SHOPBASE); + if (s_sym < 0) { + ap_type = M_AP_OBJECT; + appear = -s_sym; + } else { + if (s_sym == RANDOM_CLASS) + s_sym = syms[rn2((int)sizeof(syms)-2) + 2]; + goto assign_sym; + } + } else { + s_sym = syms[rn2((int)sizeof(syms))]; +assign_sym: + if (s_sym >= MAXOCLASSES) { + ap_type = M_AP_FURNITURE; + appear = s_sym == MAXOCLASSES ? S_upstair : S_dnstair; + } else if (s_sym == COIN_CLASS) { + ap_type = M_AP_OBJECT; + appear = GOLD_PIECE; + } else { + ap_type = M_AP_OBJECT; + if (s_sym == S_MIMIC_DEF) { + appear = STRANGE_OBJECT; + } else { + otmp = mkobj( (char) s_sym, FALSE ); + appear = otmp->otyp; + /* make sure container contents are free'ed */ + obfree(otmp, (struct obj *) 0); + } + } + } + mtmp->m_ap_type = ap_type; + mtmp->mappearance = appear; +} + +/* release a monster from a bag of tricks */ +void +bagotricks(bag) +struct obj *bag; +{ + if (!bag || bag->otyp != BAG_OF_TRICKS) { + impossible("bad bag o' tricks"); + } else if (bag->spe < 1) { + pline(nothing_happens); + } else { + boolean gotone = FALSE; + int cnt = 1; + + consume_obj_charge(bag, TRUE); + + if (!rn2(23)) cnt += rn1(7, 1); + while (cnt-- > 0) { + if (makemon((struct permonst *)0, u.ux, u.uy, NO_MM_FLAGS)) + gotone = TRUE; + } + if (gotone) makeknown(BAG_OF_TRICKS); + } +} + +#endif /* OVLB */ + +/*makemon.c*/ diff --git a/src/mapglyph.c b/src/mapglyph.c new file mode 100644 index 0000000..92fbd17 --- /dev/null +++ b/src/mapglyph.c @@ -0,0 +1,241 @@ +/* SCCS Id: @(#)mapglyph.c 3.4 2003/01/08 */ +/* Copyright (c) David Cohrs, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#if defined(TTY_GRAPHICS) +#include "wintty.h" /* for prototype of has_color() only */ +#endif +#include "color.h" +#define HI_DOMESTIC CLR_WHITE /* monst.c */ + +int explcolors[] = { + CLR_BLACK, /* dark */ + CLR_GREEN, /* noxious */ + CLR_BROWN, /* muddy */ + CLR_BLUE, /* wet */ + CLR_MAGENTA, /* magical */ + CLR_ORANGE, /* fiery */ + CLR_WHITE, /* frosty */ +}; + +#if !defined(TTY_GRAPHICS) +#define has_color(n) TRUE +#endif + +#ifdef TEXTCOLOR +#define zap_color(n) color = iflags.use_color ? zapcolors[n] : NO_COLOR +#define cmap_color(n) color = iflags.use_color ? defsyms[n].color : NO_COLOR +#define obj_color(n) color = iflags.use_color ? objects[n].oc_color : NO_COLOR +#define mon_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define invis_color(n) color = NO_COLOR +#define pet_color(n) color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define warn_color(n) color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR +#define explode_color(n) color = iflags.use_color ? explcolors[n] : NO_COLOR +# if defined(REINCARNATION) && defined(ASCIIGRAPH) +# define ROGUE_COLOR +# endif + +#else /* no text color */ + +#define zap_color(n) +#define cmap_color(n) +#define obj_color(n) +#define mon_color(n) +#define invis_color(n) +#define pet_color(c) +#define warn_color(n) +#define explode_color(n) +#endif + +#ifdef ROGUE_COLOR +# if defined(USE_TILES) && defined(MSDOS) +#define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && !iflags.grmode && \ + Is_rogue_level(&u.uz)) +# else +#define HAS_ROGUE_IBM_GRAPHICS (iflags.IBMgraphics && Is_rogue_level(&u.uz)) +# endif +#endif + +/*ARGSUSED*/ +void +mapglyph(glyph, ochar, ocolor, ospecial, x, y) +int glyph, *ocolor, x, y; +int *ochar; +unsigned *ospecial; +{ + register int offset; +#if defined(TEXTCOLOR) || defined(ROGUE_COLOR) + int color = NO_COLOR; +#endif + uchar ch; + unsigned special = 0; + + /* + * Map the glyph back to a character and color. + * + * Warning: For speed, this makes an assumption on the order of + * offsets. The order is set in display.h. + */ + if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ + ch = warnsyms[offset]; +# ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS) + color = NO_COLOR; + else +# endif + warn_color(offset); + } else if ((offset = (glyph - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */ + /* see swallow_to_glyph() in display.c */ + ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) + color = NO_COLOR; + else +#endif + mon_color(offset >> 3); + } else if ((offset = (glyph - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */ + /* see zapdir_to_glyph() in display.c */ + ch = showsyms[S_vbeam + (offset & 0x3)]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) + color = NO_COLOR; + else +#endif + zap_color((offset >> 2)); + } else if ((offset = (glyph - GLYPH_EXPLODE_OFF)) >= 0) { /* explosion */ + ch = showsyms[(offset % MAXEXPCHARS) + S_explode1]; + explode_color(offset / MAXEXPCHARS); + } else if ((offset = (glyph - GLYPH_CMAP_OFF)) >= 0) { /* cmap */ + ch = showsyms[offset]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) { + if (offset >= S_vwall && offset <= S_hcdoor) + color = CLR_BROWN; + else if (offset >= S_arrow_trap && offset <= S_polymorph_trap) + color = CLR_MAGENTA; + else if (offset == S_corr || offset == S_litcorr) + color = CLR_GRAY; + else if (offset >= S_room && offset <= S_water) + color = CLR_GREEN; + else + color = NO_COLOR; + } else +#endif +#ifdef TEXTCOLOR + /* provide a visible difference if normal and lit corridor + * use the same symbol */ + if (iflags.use_color && + offset == S_litcorr && ch == showsyms[S_corr]) + color = CLR_WHITE; + else +#endif + cmap_color(offset); + } else if ((offset = (glyph - GLYPH_OBJ_OFF)) >= 0) { /* object */ + if (offset == BOULDER && iflags.bouldersym) ch = iflags.bouldersym; + else ch = oc_syms[(int)objects[offset].oc_class]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) { + switch(objects[offset].oc_class) { + case COIN_CLASS: color = CLR_YELLOW; break; + case FOOD_CLASS: color = CLR_RED; break; + default: color = CLR_BRIGHT_BLUE; break; + } + } else +#endif + obj_color(offset); + } else if ((offset = (glyph - GLYPH_RIDDEN_OFF)) >= 0) { /* mon ridden */ + ch = monsyms[(int)mons[offset].mlet]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS) + /* This currently implies that the hero is here -- monsters */ + /* don't ride (yet...). Should we set it to yellow like in */ + /* the monster case below? There is no equivalent in rogue. */ + color = NO_COLOR; /* no need to check iflags.use_color */ + else +#endif + mon_color(offset); + special |= MG_RIDDEN; + } else if ((offset = (glyph - GLYPH_BODY_OFF)) >= 0) { /* a corpse */ + ch = oc_syms[(int)objects[CORPSE].oc_class]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) + color = CLR_RED; + else +#endif + mon_color(offset); + special |= MG_CORPSE; + } else if ((offset = (glyph - GLYPH_DETECT_OFF)) >= 0) { /* mon detect */ + ch = monsyms[(int)mons[offset].mlet]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS) + color = NO_COLOR; /* no need to check iflags.use_color */ + else +#endif + mon_color(offset); + /* Disabled for now; anyone want to get reverse video to work? */ + /* is_reverse = TRUE; */ + special |= MG_DETECT; + } else if ((offset = (glyph - GLYPH_INVIS_OFF)) >= 0) { /* invisible */ + ch = DEF_INVISIBLE; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS) + color = NO_COLOR; /* no need to check iflags.use_color */ + else +#endif + invis_color(offset); + special |= MG_INVIS; + } else if ((offset = (glyph - GLYPH_PET_OFF)) >= 0) { /* a pet */ + ch = monsyms[(int)mons[offset].mlet]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS) + color = NO_COLOR; /* no need to check iflags.use_color */ + else +#endif + pet_color(offset); + special |= MG_PET; + } else { /* a monster */ + ch = monsyms[(int)mons[glyph].mlet]; +#ifdef ROGUE_COLOR + if (HAS_ROGUE_IBM_GRAPHICS && iflags.use_color) { + if (x == u.ux && y == u.uy) + /* actually player should be yellow-on-gray if in a corridor */ + color = CLR_YELLOW; + else + color = NO_COLOR; + } else +#endif + { + mon_color(glyph); + /* special case the hero for `showrace' option */ +#ifdef TEXTCOLOR + if (iflags.use_color && x == u.ux && y == u.uy && + iflags.showrace && !Upolyd) + color = HI_DOMESTIC; +#endif + } + } + +#ifdef TEXTCOLOR + /* Turn off color if no color defined, or rogue level w/o PC graphics. */ +# ifdef REINCARNATION +# ifdef ASCIIGRAPH + if (!has_color(color) || (Is_rogue_level(&u.uz) && !HAS_ROGUE_IBM_GRAPHICS)) +# else + if (!has_color(color) || Is_rogue_level(&u.uz)) +# endif +# else + if (!has_color(color)) +# endif + color = NO_COLOR; +#endif + + *ochar = (int)ch; + *ospecial = special; +#ifdef TEXTCOLOR + *ocolor = color; +#endif + return; +} + +/*mapglyph.c*/ diff --git a/src/mcastu.c b/src/mcastu.c new file mode 100644 index 0000000..93d3b8d --- /dev/null +++ b/src/mcastu.c @@ -0,0 +1,788 @@ +/* SCCS Id: @(#)mcastu.c 3.4 2003/01/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* monster mage spells */ +#define MGC_PSI_BOLT 0 +#define MGC_CURE_SELF 1 +#define MGC_HASTE_SELF 2 +#define MGC_STUN_YOU 3 +#define MGC_DISAPPEAR 4 +#define MGC_WEAKEN_YOU 5 +#define MGC_DESTRY_ARMR 6 +#define MGC_CURSE_ITEMS 7 +#define MGC_AGGRAVATION 8 +#define MGC_SUMMON_MONS 9 +#define MGC_CLONE_WIZ 10 +#define MGC_DEATH_TOUCH 11 + +/* monster cleric spells */ +#define CLC_OPEN_WOUNDS 0 +#define CLC_CURE_SELF 1 +#define CLC_CONFUSE_YOU 2 +#define CLC_PARALYZE 3 +#define CLC_BLIND_YOU 4 +#define CLC_INSECTS 5 +#define CLC_CURSE_ITEMS 6 +#define CLC_LIGHTNING 7 +#define CLC_FIRE_PILLAR 8 +#define CLC_GEYSER 9 + +STATIC_DCL void FDECL(cursetxt,(struct monst *,BOOLEAN_P)); +STATIC_DCL int FDECL(choose_magic_spell, (int)); +STATIC_DCL int FDECL(choose_clerical_spell, (int)); +STATIC_DCL void FDECL(cast_wizard_spell,(struct monst *, int,int)); +STATIC_DCL void FDECL(cast_cleric_spell,(struct monst *, int,int)); +STATIC_DCL boolean FDECL(is_undirected_spell,(unsigned int,int)); +STATIC_DCL boolean FDECL(spell_would_be_useless,(struct monst *,unsigned int,int)); + +#ifdef OVL0 + +extern const char * const flash_types[]; /* from zap.c */ + +/* feedback when frustrated monster couldn't cast a spell */ +STATIC_OVL +void +cursetxt(mtmp, undirected) +struct monst *mtmp; +boolean undirected; +{ + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { + const char *point_msg; /* spellcasting monsters are impolite */ + + if (undirected) + point_msg = "all around, then curses"; + else if ((Invis && !perceives(mtmp->data) && + (mtmp->mux != u.ux || mtmp->muy != u.uy)) || + (youmonst.m_ap_type == M_AP_OBJECT && + youmonst.mappearance == STRANGE_OBJECT) || + u.uundetected) + point_msg = "and curses in your general direction"; + else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + point_msg = "and curses at your displaced image"; + else + point_msg = "at you, then curses"; + + pline("%s points %s.", Monnam(mtmp), point_msg); + } else if ((!(moves % 4) || !rn2(4))) { + if (flags.soundok) Norep("You hear a mumbled curse."); + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +/* convert a level based random selection into a specific mage spell; + inappropriate choices will be screened out by spell_would_be_useless() */ +STATIC_OVL int +choose_magic_spell(spellval) +int spellval; +{ + switch (spellval) { + case 22: + case 21: + case 20: + return MGC_DEATH_TOUCH; + case 19: + case 18: + return MGC_CLONE_WIZ; + case 17: + case 16: + case 15: + return MGC_SUMMON_MONS; + case 14: + case 13: + return MGC_AGGRAVATION; + case 12: + case 11: + case 10: + return MGC_CURSE_ITEMS; + case 9: + case 8: + return MGC_DESTRY_ARMR; + case 7: + case 6: + return MGC_WEAKEN_YOU; + case 5: + case 4: + return MGC_DISAPPEAR; + case 3: + return MGC_STUN_YOU; + case 2: + return MGC_HASTE_SELF; + case 1: + return MGC_CURE_SELF; + case 0: + default: + return MGC_PSI_BOLT; + } +} + +/* convert a level based random selection into a specific cleric spell */ +STATIC_OVL int +choose_clerical_spell(spellnum) +int spellnum; +{ + switch (spellnum) { + case 13: + return CLC_GEYSER; + case 12: + return CLC_FIRE_PILLAR; + case 11: + return CLC_LIGHTNING; + case 10: + case 9: + return CLC_CURSE_ITEMS; + case 8: + return CLC_INSECTS; + case 7: + case 6: + return CLC_BLIND_YOU; + case 5: + case 4: + return CLC_PARALYZE; + case 3: + case 2: + return CLC_CONFUSE_YOU; + case 1: + return CLC_CURE_SELF; + case 0: + default: + return CLC_OPEN_WOUNDS; + } +} + +/* return values: + * 1: successful spell + * 0: unsuccessful spell + */ +int +castmu(mtmp, mattk, thinks_it_foundyou, foundyou) + register struct monst *mtmp; + register struct attack *mattk; + boolean thinks_it_foundyou; + boolean foundyou; +{ + int dmg, ml = mtmp->m_lev; + int ret; + int spellnum = 0; + + /* Three cases: + * -- monster is attacking you. Search for a useful spell. + * -- monster thinks it's attacking you. Search for a useful spell, + * without checking for undirected. If the spell found is directed, + * it fails with cursetxt() and loss of mspec_used. + * -- monster isn't trying to attack. Select a spell once. Don't keep + * searching; if that spell is not useful (or if it's directed), + * return and do something else. + * Since most spells are directed, this means that a monster that isn't + * attacking casts spells only a small portion of the time that an + * attacking monster does. + */ + if ((mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) && ml) { + int cnt = 40; + + do { + spellnum = rn2(ml); + if (mattk->adtyp == AD_SPEL) + spellnum = choose_magic_spell(spellnum); + else + spellnum = choose_clerical_spell(spellnum); + /* not trying to attack? don't allow directed spells */ + if (!thinks_it_foundyou) { + if (!is_undirected_spell(mattk->adtyp, spellnum) || + spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) { + if (foundyou) + impossible("spellcasting monster found you and doesn't know it?"); + return 0; + } + break; + } + } while(--cnt > 0 && + spell_would_be_useless(mtmp, mattk->adtyp, spellnum)); + if (cnt == 0) return 0; + } + + /* monster unable to cast spells? */ + if(mtmp->mcan || mtmp->mspec_used || !ml) { + cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum)); + return(0); + } + + if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) { + mtmp->mspec_used = 10 - mtmp->m_lev; + if (mtmp->mspec_used < 2) mtmp->mspec_used = 2; + } + + /* monster can cast spells, but is casting a directed spell at the + wrong place? If so, give a message, and return. Do this *after* + penalizing mspec_used. */ + if (!foundyou && thinks_it_foundyou && + !is_undirected_spell(mattk->adtyp, spellnum)) { + pline("%s casts a spell at %s!", + canseemon(mtmp) ? Monnam(mtmp) : "Something", + levl[mtmp->mux][mtmp->muy].typ == WATER + ? "empty water" : "thin air"); + return(0); + } + + nomul(0); + if(rn2(ml*10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */ + if (canseemon(mtmp) && flags.soundok) + pline_The("air crackles around %s.", mon_nam(mtmp)); + return(0); + } + if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) { + pline("%s casts a spell%s!", + canspotmon(mtmp) ? Monnam(mtmp) : "Something", + is_undirected_spell(mattk->adtyp, spellnum) ? "" : + (Invisible && !perceives(mtmp->data) && + (mtmp->mux != u.ux || mtmp->muy != u.uy)) ? + " at a spot near you" : + (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) ? + " at your displaced image" : + " at you"); + } + +/* + * As these are spells, the damage is related to the level + * of the monster casting the spell. + */ + if (!foundyou) { + dmg = 0; + if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) { + impossible( + "%s casting non-hand-to-hand version of hand-to-hand spell %d?", + Monnam(mtmp), mattk->adtyp); + return(0); + } + } else if (mattk->damd) + dmg = d((int)((ml/2) + mattk->damn), (int)mattk->damd); + else dmg = d((int)((ml/2) + 1), 6); + if (Half_spell_damage) dmg = (dmg+1) / 2; + + ret = 1; + + switch (mattk->adtyp) { + + case AD_FIRE: + pline("You're enveloped in flames."); + if(Fire_resistance) { + shieldeff(u.ux, u.uy); + pline("But you resist the effects."); + dmg = 0; + } + burn_away_slime(); + break; + case AD_COLD: + pline("You're covered in frost."); + if(Cold_resistance) { + shieldeff(u.ux, u.uy); + pline("But you resist the effects."); + dmg = 0; + } + break; + case AD_MAGM: + You("are hit by a shower of missiles!"); + if(Antimagic) { + shieldeff(u.ux, u.uy); + pline_The("missiles bounce off!"); + dmg = 0; + } else dmg = d((int)mtmp->m_lev/2 + 1,6); + break; + case AD_SPEL: /* wizard spell */ + case AD_CLRC: /* clerical spell */ + { + if (mattk->adtyp == AD_SPEL) + cast_wizard_spell(mtmp, dmg, spellnum); + else + cast_cleric_spell(mtmp, dmg, spellnum); + dmg = 0; /* done by the spell casting functions */ + break; + } + } + if(dmg) mdamageu(mtmp, dmg); + return(ret); +} + +/* monster wizard and cleric spellcasting functions */ +/* + If dmg is zero, then the monster is not casting at you. + If the monster is intentionally not casting at you, we have previously + called spell_would_be_useless() and spellnum should always be a valid + undirected spell. + If you modify either of these, be sure to change is_undirected_spell() + and spell_would_be_useless(). + */ +STATIC_OVL +void +cast_wizard_spell(mtmp, dmg, spellnum) +struct monst *mtmp; +int dmg; +int spellnum; +{ + if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) { + impossible("cast directed wizard spell (%d) with dmg=0?", spellnum); + return; + } + + switch (spellnum) { + case MGC_DEATH_TOUCH: + pline("Oh no, %s's using the touch of death!", mhe(mtmp)); + if (nonliving(youmonst.data) || is_demon(youmonst.data)) { + You("seem no deader than before."); + } else if (!Antimagic && rn2(mtmp->m_lev) > 12) { + if (Hallucination) { + You("have an out of body experience."); + } else { + killer_format = KILLED_BY_AN; + killer = "touch of death"; + done(DIED); + } + } else { + if (Antimagic) shieldeff(u.ux, u.uy); + pline("Lucky for you, it didn't work!"); + } + dmg = 0; + break; + case MGC_CLONE_WIZ: + if (mtmp->iswiz && flags.no_of_wizards == 1) { + pline("Double Trouble..."); + clonewiz(); + dmg = 0; + } else + impossible("bad wizard cloning?"); + break; + case MGC_SUMMON_MONS: + { + int count; + + count = nasty(mtmp); /* summon something nasty */ + if (mtmp->iswiz) + verbalize("Destroy the thief, my pet%s!", plur(count)); + else { + const char *mappear = + (count == 1) ? "A monster appears" : "Monsters appear"; + + /* messages not quite right if plural monsters created but + only a single monster is seen */ + if (Invisible && !perceives(mtmp->data) && + (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s around a spot near you!", mappear); + else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s around your displaced image!", mappear); + else + pline("%s from nowhere!", mappear); + } + dmg = 0; + break; + } + case MGC_AGGRAVATION: + You_feel("that monsters are aware of your presence."); + aggravate(); + dmg = 0; + break; + case MGC_CURSE_ITEMS: + You_feel("as if you need some help."); + rndcurse(); + dmg = 0; + break; + case MGC_DESTRY_ARMR: + if (Antimagic) { + shieldeff(u.ux, u.uy); + pline("A field of force surrounds you!"); + } else if (!destroy_arm(some_armor(&youmonst))) { + Your("skin itches."); + } + dmg = 0; + break; + case MGC_WEAKEN_YOU: /* drain strength */ + if (Antimagic) { + shieldeff(u.ux, u.uy); + You_feel("momentarily weakened."); + } else { + You("suddenly feel weaker!"); + dmg = mtmp->m_lev - 6; + if (Half_spell_damage) dmg = (dmg + 1) / 2; + losestr(rnd(dmg)); + if (u.uhp < 1) + done_in_by(mtmp); + } + dmg = 0; + break; + case MGC_DISAPPEAR: /* makes self invisible */ + if (!mtmp->minvis && !mtmp->invis_blkd) { + if (canseemon(mtmp)) + pline("%s suddenly %s!", Monnam(mtmp), + !See_invisible ? "disappears" : "becomes transparent"); + mon_set_minvis(mtmp); + dmg = 0; + } else + impossible("no reason for monster to cast disappear spell?"); + break; + case MGC_STUN_YOU: + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + if (!Stunned) + You_feel("momentarily disoriented."); + make_stunned(1L, FALSE); + } else { + You(Stunned ? "struggle to keep your balance." : "reel..."); + dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4); + if (Half_spell_damage) dmg = (dmg + 1) / 2; + make_stunned(HStun + dmg, FALSE); + } + dmg = 0; + break; + case MGC_HASTE_SELF: + mon_adjust_speed(mtmp, 1, (struct obj *)0); + dmg = 0; + break; + case MGC_CURE_SELF: + if (mtmp->mhp < mtmp->mhpmax) { + if (canseemon(mtmp)) + pline("%s looks better.", Monnam(mtmp)); + /* note: player healing does 6d4; this used to do 1d8 */ + if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + dmg = 0; + } + break; + case MGC_PSI_BOLT: + /* prior to 3.4.0 Antimagic was setting the damage to 1--this + made the spell virtually harmless to players with magic res. */ + if (Antimagic) { + shieldeff(u.ux, u.uy); + dmg = (dmg + 1) / 2; + } + if (dmg <= 5) + You("get a slight %sache.", body_part(HEAD)); + else if (dmg <= 10) + Your("brain is on fire!"); + else if (dmg <= 20) + Your("%s suddenly aches painfully!", body_part(HEAD)); + else + Your("%s suddenly aches very painfully!", body_part(HEAD)); + break; + default: + impossible("mcastu: invalid magic spell (%d)", spellnum); + dmg = 0; + break; + } + + if (dmg) mdamageu(mtmp, dmg); +} + +STATIC_OVL +void +cast_cleric_spell(mtmp, dmg, spellnum) +struct monst *mtmp; +int dmg; +int spellnum; +{ + if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) { + impossible("cast directed cleric spell (%d) with dmg=0?", spellnum); + return; + } + + switch (spellnum) { + case CLC_GEYSER: + /* this is physical damage, not magical damage */ + pline("A sudden geyser slams into you from nowhere!"); + dmg = d(8, 6); + if (Half_physical_damage) dmg = (dmg + 1) / 2; + break; + case CLC_FIRE_PILLAR: + pline("A pillar of fire strikes all around you!"); + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + dmg = 0; + } else + dmg = d(8, 6); + if (Half_spell_damage) dmg = (dmg + 1) / 2; + burn_away_slime(); + (void) burnarmor(&youmonst); + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + (void) burn_floor_paper(u.ux, u.uy, TRUE, FALSE); + break; + case CLC_LIGHTNING: + { + boolean reflects; + + pline("A bolt of lightning strikes down at you from above!"); + reflects = ureflects("It bounces off your %s%s.", ""); + if (reflects || Shock_resistance) { + shieldeff(u.ux, u.uy); + dmg = 0; + if (reflects) + break; + } else + dmg = d(8, 6); + if (Half_spell_damage) dmg = (dmg + 1) / 2; + destroy_item(WAND_CLASS, AD_ELEC); + destroy_item(RING_CLASS, AD_ELEC); + break; + } + case CLC_CURSE_ITEMS: + You_feel("as if you need some help."); + rndcurse(); + dmg = 0; + break; + case CLC_INSECTS: + { + /* Try for insects, and if there are none + left, go for (sticks to) snakes. -3. */ + struct permonst *pm = mkclass(S_ANT,0); + struct monst *mtmp2 = (struct monst *)0; + char let = (pm ? S_ANT : S_SNAKE); + boolean success; + int i; + coord bypos; + int quan; + + quan = (mtmp->m_lev < 2) ? 1 : rnd((int)mtmp->m_lev / 2); + if (quan < 3) quan = 3; + success = pm ? TRUE : FALSE; + for (i = 0; i <= quan; i++) { + if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data)) + break; + if ((pm = mkclass(let,0)) != 0 && + (mtmp2 = makemon(pm, bypos.x, bypos.y, NO_MM_FLAGS)) != 0) { + success = TRUE; + mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0; + set_malign(mtmp2); + } + } + /* Not quite right: + * -- message doesn't always make sense for unseen caster (particularly + * the first message) + * -- message assumes plural monsters summoned (non-plural should be + * very rare, unlike in nasty()) + * -- message assumes plural monsters seen + */ + if (!success) + pline("%s casts at a clump of sticks, but nothing happens.", + Monnam(mtmp)); + else if (let == S_SNAKE) + pline("%s transforms a clump of sticks into snakes!", + Monnam(mtmp)); + else if (Invisible && !perceives(mtmp->data) && + (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s summons insects around a spot near you!", + Monnam(mtmp)); + else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy)) + pline("%s summons insects around your displaced image!", + Monnam(mtmp)); + else + pline("%s summons insects!", Monnam(mtmp)); + dmg = 0; + break; + } + case CLC_BLIND_YOU: + /* note: resists_blnd() doesn't apply here */ + if (!Blinded) { + int num_eyes = eyecount(youmonst.data); + pline("Scales cover your %s!", + (num_eyes == 1) ? + body_part(EYE) : makeplural(body_part(EYE))); + make_blinded(Half_spell_damage ? 100L : 200L, FALSE); + if (!Blind) Your(vision_clears); + dmg = 0; + } else + impossible("no reason for monster to cast blindness spell?"); + break; + case CLC_PARALYZE: + if (Antimagic || Free_action) { + shieldeff(u.ux, u.uy); + if (multi >= 0) + You("stiffen briefly."); + nomul(-1); + } else { + if (multi >= 0) + You("are frozen in place!"); + dmg = 4 + (int)mtmp->m_lev; + if (Half_spell_damage) dmg = (dmg + 1) / 2; + nomul(-dmg); + } + dmg = 0; + break; + case CLC_CONFUSE_YOU: + if (Antimagic) { + shieldeff(u.ux, u.uy); + You_feel("momentarily dizzy."); + } else { + boolean oldprop = !!Confusion; + + dmg = (int)mtmp->m_lev; + if (Half_spell_damage) dmg = (dmg + 1) / 2; + make_confused(HConfusion + dmg, TRUE); + if (Hallucination) + You_feel("%s!", oldprop ? "trippier" : "trippy"); + else + You_feel("%sconfused!", oldprop ? "more " : ""); + } + dmg = 0; + break; + case CLC_CURE_SELF: + if (mtmp->mhp < mtmp->mhpmax) { + if (canseemon(mtmp)) + pline("%s looks better.", Monnam(mtmp)); + /* note: player healing does 6d4; this used to do 1d8 */ + if ((mtmp->mhp += d(3,6)) > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + dmg = 0; + } + break; + case CLC_OPEN_WOUNDS: + if (Antimagic) { + shieldeff(u.ux, u.uy); + dmg = (dmg + 1) / 2; + } + if (dmg <= 5) + Your("skin itches badly for a moment."); + else if (dmg <= 10) + pline("Wounds appear on your body!"); + else if (dmg <= 20) + pline("Severe wounds appear on your body!"); + else + Your("body is covered with painful wounds!"); + break; + default: + impossible("mcastu: invalid clerical spell (%d)", spellnum); + dmg = 0; + break; + } + + if (dmg) mdamageu(mtmp, dmg); +} + +STATIC_DCL +boolean +is_undirected_spell(adtyp, spellnum) +unsigned int adtyp; +int spellnum; +{ + if (adtyp == AD_SPEL) { + switch (spellnum) { + case MGC_CLONE_WIZ: + case MGC_SUMMON_MONS: + case MGC_AGGRAVATION: + case MGC_DISAPPEAR: + case MGC_HASTE_SELF: + case MGC_CURE_SELF: + return TRUE; + default: + break; + } + } else if (adtyp == AD_CLRC) { + switch (spellnum) { + case CLC_INSECTS: + case CLC_CURE_SELF: + return TRUE; + default: + break; + } + } + return FALSE; +} + +/* Some spells are useless under some circumstances. */ +STATIC_DCL +boolean +spell_would_be_useless(mtmp, adtyp, spellnum) +struct monst *mtmp; +unsigned int adtyp; +int spellnum; +{ + /* Some spells don't require the player to really be there and can be cast + * by the monster when you're invisible, yet still shouldn't be cast when + * the monster doesn't even think you're there. + * This check isn't quite right because it always uses your real position. + * We really want something like "if the monster could see mux, muy". + */ + boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my); + + if (adtyp == AD_SPEL) { + /* aggravate monsters, etc. won't be cast by peaceful monsters */ + if (mtmp->mpeaceful && (spellnum == MGC_AGGRAVATION || + spellnum == MGC_SUMMON_MONS || spellnum == MGC_CLONE_WIZ)) + return TRUE; + /* haste self when already fast */ + if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF) + return TRUE; + /* invisibility when already invisible */ + if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR) + return TRUE; + /* peaceful monster won't cast invisibility if you can't see invisible, + same as when monsters drink potions of invisibility. This doesn't + really make a lot of sense, but lets the player avoid hitting + peaceful monsters by mistake */ + if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR) + return TRUE; + /* healing when already healed */ + if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF) + return TRUE; + /* don't summon monsters if it doesn't think you're around */ + if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS || + (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ))) + return TRUE; + if ((!mtmp->iswiz || flags.no_of_wizards > 1) + && spellnum == MGC_CLONE_WIZ) + return TRUE; + } else if (adtyp == AD_CLRC) { + /* summon insects/sticks to snakes won't be cast by peaceful monsters */ + if (mtmp->mpeaceful && spellnum == CLC_INSECTS) + return TRUE; + /* healing when already healed */ + if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF) + return TRUE; + /* don't summon insects if it doesn't think you're around */ + if (!mcouldseeu && spellnum == CLC_INSECTS) + return TRUE; + /* blindness spell on blinded player */ + if (Blinded && spellnum == CLC_BLIND_YOU) + return TRUE; + } + return FALSE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* convert 1..10 to 0..9; add 10 for second group (spell casting) */ +#define ad_to_typ(k) (10 + (int)k - 1) + +int +buzzmu(mtmp, mattk) /* monster uses spell (ranged) */ + register struct monst *mtmp; + register struct attack *mattk; +{ + /* don't print constant stream of curse messages for 'normal' + spellcasting monsters at range */ + if (mattk->adtyp > AD_SPC2) + return(0); + + if (mtmp->mcan) { + cursetxt(mtmp, FALSE); + return(0); + } + if(lined_up(mtmp) && rn2(3)) { + nomul(0); + if(mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */ + if(canseemon(mtmp)) + pline("%s zaps you with a %s!", Monnam(mtmp), + flash_types[ad_to_typ(mattk->adtyp)]); + buzz(-ad_to_typ(mattk->adtyp), (int)mattk->damn, + mtmp->mx, mtmp->my, sgn(tbx), sgn(tby)); + } else impossible("Monster spell %d cast", mattk->adtyp-1); + } + return(1); +} + +#endif /* OVL0 */ + +/*mcastu.c*/ diff --git a/src/mhitm.c b/src/mhitm.c new file mode 100644 index 0000000..912f1d2 --- /dev/null +++ b/src/mhitm.c @@ -0,0 +1,1460 @@ +/* SCCS Id: @(#)mhitm.c 3.4 2003/01/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "artifact.h" +#include "edog.h" + +extern boolean notonhead; + +#ifdef OVLB + +static NEARDATA boolean vis, far_noise; +static NEARDATA long noisetime; +static NEARDATA struct obj *otmp; + +static const char brief_feeling[] = + "have a %s feeling for a moment, then it passes."; + +STATIC_DCL char *FDECL(mon_nam_too, (char *,struct monst *,struct monst *)); +STATIC_DCL void FDECL(mrustm, (struct monst *, struct monst *, struct obj *)); +STATIC_DCL int FDECL(hitmm, (struct monst *,struct monst *,struct attack *)); +STATIC_DCL int FDECL(gazemm, (struct monst *,struct monst *,struct attack *)); +STATIC_DCL int FDECL(gulpmm, (struct monst *,struct monst *,struct attack *)); +STATIC_DCL int FDECL(explmm, (struct monst *,struct monst *,struct attack *)); +STATIC_DCL int FDECL(mdamagem, (struct monst *,struct monst *,struct attack *)); +STATIC_DCL void FDECL(mswingsm, (struct monst *, struct monst *, struct obj *)); +STATIC_DCL void FDECL(noises,(struct monst *,struct attack *)); +STATIC_DCL void FDECL(missmm,(struct monst *,struct monst *,struct attack *)); +STATIC_DCL int FDECL(passivemm, (struct monst *, struct monst *, BOOLEAN_P, int)); + +/* Needed for the special case of monsters wielding vorpal blades (rare). + * If we use this a lot it should probably be a parameter to mdamagem() + * instead of a global variable. + */ +static int dieroll; + +/* returns mon_nam(mon) relative to other_mon; normal name unless they're + the same, in which case the reference is to {him|her|it} self */ +STATIC_OVL char * +mon_nam_too(outbuf, mon, other_mon) +char *outbuf; +struct monst *mon, *other_mon; +{ + Strcpy(outbuf, mon_nam(mon)); + if (mon == other_mon) + switch (pronoun_gender(mon)) { + case 0: Strcpy(outbuf, "himself"); break; + case 1: Strcpy(outbuf, "herself"); break; + default: Strcpy(outbuf, "itself"); break; + } + return outbuf; +} + +STATIC_OVL void +noises(magr, mattk) + register struct monst *magr; + register struct attack *mattk; +{ + boolean farq = (distu(magr->mx, magr->my) > 15); + + if(flags.soundok && (farq != far_noise || moves-noisetime > 10)) { + far_noise = farq; + noisetime = moves; + You_hear("%s%s.", + (mattk->aatyp == AT_EXPL) ? "an explosion" : "some noises", + farq ? " in the distance" : ""); + } +} + +STATIC_OVL +void +missmm(magr, mdef, mattk) + register struct monst *magr, *mdef; + struct attack *mattk; +{ + const char *fmt; + char buf[BUFSZ], mdef_name[BUFSZ]; + + if (vis) { + if (!canspotmon(magr)) + map_invisible(magr->mx, magr->my); + if (!canspotmon(mdef)) + map_invisible(mdef->mx, mdef->my); + if (mdef->m_ap_type) seemimic(mdef); + if (magr->m_ap_type) seemimic(magr); + fmt = (could_seduce(magr,mdef,mattk) && !magr->mcan) ? + "%s pretends to be friendly to" : "%s misses"; + Sprintf(buf, fmt, Monnam(magr)); + pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr)); + } else noises(magr, mattk); +} + +/* + * fightm() -- fight some other monster + * + * Returns: + * 0 - Monster did nothing. + * 1 - If the monster made an attack. The monster might have died. + * + * There is an exception to the above. If mtmp has the hero swallowed, + * then we report that the monster did nothing so it will continue to + * digest the hero. + */ +int +fightm(mtmp) /* have monsters fight each other */ + register struct monst *mtmp; +{ + register struct monst *mon, *nmon; + int result, has_u_swallowed; +#ifdef LINT + nmon = 0; +#endif + /* perhaps the monster will resist Conflict */ + if(resist(mtmp, RING_CLASS, 0, 0)) + return(0); + + if(u.ustuck == mtmp) { + /* perhaps we're holding it... */ + if(itsstuck(mtmp)) + return(0); + } + has_u_swallowed = (u.uswallow && (mtmp == u.ustuck)); + + for(mon = fmon; mon; mon = nmon) { + nmon = mon->nmon; + if(nmon == mtmp) nmon = mtmp->nmon; + /* Be careful to ignore monsters that are already dead, since we + * might be calling this before we've cleaned them up. This can + * happen if the monster attacked a cockatrice bare-handedly, for + * instance. + */ + if(mon != mtmp && !DEADMONSTER(mon)) { + if(monnear(mtmp,mon->mx,mon->my)) { + if(!u.uswallow && (mtmp == u.ustuck)) { + if(!rn2(4)) { + pline("%s releases you!", Monnam(mtmp)); + u.ustuck = 0; + } else + break; + } + + /* mtmp can be killed */ + bhitpos.x = mon->mx; + bhitpos.y = mon->my; + notonhead = 0; + result = mattackm(mtmp,mon); + + if (result & MM_AGR_DIED) return 1; /* mtmp died */ + /* + * If mtmp has the hero swallowed, lie and say there + * was no attack (this allows mtmp to digest the hero). + */ + if (has_u_swallowed) return 0; + + /* Allow attacked monsters a chance to hit back. Primarily + * to allow monsters that resist conflict to respond. + */ + if ((result & MM_HIT) && !(result & MM_DEF_DIED) && + rn2(4) && mon->movement >= NORMAL_SPEED) { + mon->movement -= NORMAL_SPEED; + notonhead = 0; + (void) mattackm(mon, mtmp); /* return attack */ + } + + return ((result & MM_HIT) ? 1 : 0); + } + } + } + return 0; +} + +/* + * mattackm() -- a monster attacks another monster. + * + * This function returns a result bitfield: + * + * --------- aggressor died + * / ------- defender died + * / / ----- defender was hit + * / / / + * x x x + * + * 0x4 MM_AGR_DIED + * 0x2 MM_DEF_DIED + * 0x1 MM_HIT + * 0x0 MM_MISS + * + * Each successive attack has a lower probability of hitting. Some rely on the + * success of previous attacks. ** this doen't seem to be implemented -dl ** + * + * In the case of exploding monsters, the monster dies as well. + */ +int +mattackm(magr, mdef) + register struct monst *magr,*mdef; +{ + int i, /* loop counter */ + tmp, /* amour class difference */ + strike, /* hit this attack */ + attk, /* attack attempted this time */ + struck = 0, /* hit at least once */ + res[NATTK]; /* results of all attacks */ + struct attack *mattk, alt_attk; + struct permonst *pa, *pd; + + if (!magr || !mdef) return(MM_MISS); /* mike@genat */ + if (!magr->mcanmove || magr->msleeping) return(MM_MISS); + pa = magr->data; pd = mdef->data; + + /* Grid bugs cannot attack at an angle. */ + if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx + && magr->my != mdef->my) + return(MM_MISS); + + /* Calculate the armour class differential. */ + tmp = find_mac(mdef) + magr->m_lev; + if (mdef->mconf || !mdef->mcanmove || mdef->msleeping) { + tmp += 4; + mdef->msleeping = 0; + } + + /* undetect monsters become un-hidden if they are attacked */ + if (mdef->mundetected) { + mdef->mundetected = 0; + newsym(mdef->mx, mdef->my); + if(canseemon(mdef) && !sensemon(mdef)) { + if (u.usleep) You("dream of %s.", + (mdef->data->geno & G_UNIQ) ? + a_monnam(mdef) : makeplural(m_monnam(mdef))); + else pline("Suddenly, you notice %s.", a_monnam(mdef)); + } + } + + /* Elves hate orcs. */ + if (is_elf(pa) && is_orc(pd)) tmp++; + + + /* Set up the visibility of action */ + vis = (cansee(magr->mx,magr->my) && cansee(mdef->mx,mdef->my) && (canspotmon(magr) || canspotmon(mdef))); + + /* Set flag indicating monster has moved this turn. Necessary since a + * monster might get an attack out of sequence (i.e. before its move) in + * some cases, in which case this still counts as its move for the round + * and it shouldn't move again. + */ + magr->mlstmv = monstermoves; + + /* Now perform all attacks for the monster. */ + for (i = 0; i < NATTK; i++) { + res[i] = MM_MISS; + mattk = getmattk(pa, i, res, &alt_attk); + otmp = (struct obj *)0; + attk = 1; + switch (mattk->aatyp) { + case AT_WEAP: /* "hand to hand" attacks */ + if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) { + magr->weapon_check = NEED_HTH_WEAPON; + if (mon_wield_item(magr) != 0) return 0; + } + possibly_unwield(magr, FALSE); + otmp = MON_WEP(magr); + + if (otmp) { + if (vis) mswingsm(magr, mdef, otmp); + tmp += hitval(otmp, mdef); + } + /* fall through */ + case AT_CLAW: + case AT_KICK: + case AT_BITE: + case AT_STNG: + case AT_TUCH: + case AT_BUTT: + case AT_TENT: + /* Nymph that teleported away on first attack? */ + if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) + return MM_MISS; + /* Monsters won't attack cockatrices physically if they + * have a weapon instead. This instinct doesn't work for + * players, or under conflict or confusion. + */ + if (!magr->mconf && !Conflict && otmp && + mattk->aatyp != AT_WEAP && touch_petrifies(mdef->data)) { + strike = 0; + break; + } + dieroll = rnd(20 + i); + strike = (tmp > dieroll); + /* KMH -- don't accumulate to-hit bonuses */ + if (otmp) + tmp -= hitval(otmp, mdef); + if (strike) { + res[i] = hitmm(magr, mdef, mattk); + if((mdef->data == &mons[PM_BLACK_PUDDING] || mdef->data == &mons[PM_BROWN_PUDDING]) + && otmp && objects[otmp->otyp].oc_material == IRON + && mdef->mhp > 1 && !mdef->mcan) + { + if (clone_mon(mdef, 0, 0)) { + if (vis) { + char buf[BUFSZ]; + + Strcpy(buf, Monnam(mdef)); + pline("%s divides as %s hits it!", buf, mon_nam(magr)); + } + } + } + } else + missmm(magr, mdef, mattk); + break; + + case AT_HUGS: /* automatic if prev two attacks succeed */ + strike = (i >= 2 && res[i-1] == MM_HIT && res[i-2] == MM_HIT); + if (strike) + res[i] = hitmm(magr, mdef, mattk); + + break; + + case AT_GAZE: + strike = 0; /* will not wake up a sleeper */ + res[i] = gazemm(magr, mdef, mattk); + break; + + case AT_EXPL: + res[i] = explmm(magr, mdef, mattk); + if (res[i] == MM_MISS) { /* cancelled--no attack */ + strike = 0; + attk = 0; + } else + strike = 1; /* automatic hit */ + break; + + case AT_ENGL: +#ifdef STEED + if (u.usteed && (mdef == u.usteed)) { + strike = 0; + break; + } +#endif + /* Engulfing attacks are directed at the hero if + * possible. -dlc + */ + if (u.uswallow && magr == u.ustuck) + strike = 0; + else { + if ((strike = (tmp > rnd(20+i)))) + res[i] = gulpmm(magr, mdef, mattk); + else + missmm(magr, mdef, mattk); + } + break; + + default: /* no attack */ + strike = 0; + attk = 0; + break; + } + + if (attk && !(res[i] & MM_AGR_DIED)) + res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED); + + if (res[i] & MM_DEF_DIED) return res[i]; + + /* + * Wake up the defender. NOTE: this must follow the check + * to see if the defender died. We don't want to modify + * unallocated monsters! + */ + if (strike) mdef->msleeping = 0; + + if (res[i] & MM_AGR_DIED) return res[i]; + /* return if aggressor can no longer attack */ + if (!magr->mcanmove || magr->msleeping) return res[i]; + if (res[i] & MM_HIT) struck = 1; /* at least one hit */ + } + + return(struck ? MM_HIT : MM_MISS); +} + +/* Returns the result of mdamagem(). */ +STATIC_OVL int +hitmm(magr, mdef, mattk) + register struct monst *magr,*mdef; + struct attack *mattk; +{ + if(vis){ + int compat; + char buf[BUFSZ], mdef_name[BUFSZ]; + + if (!canspotmon(magr)) + map_invisible(magr->mx, magr->my); + if (!canspotmon(mdef)) + map_invisible(mdef->mx, mdef->my); + if(mdef->m_ap_type) seemimic(mdef); + if(magr->m_ap_type) seemimic(magr); + if((compat = could_seduce(magr,mdef,mattk)) && !magr->mcan) { + Sprintf(buf, "%s %s", Monnam(magr), + mdef->mcansee ? "smiles at" : "talks to"); + pline("%s %s %s.", buf, mon_nam(mdef), + compat == 2 ? + "engagingly" : "seductively"); + } else { + char magr_name[BUFSZ]; + + Strcpy(magr_name, Monnam(magr)); + switch (mattk->aatyp) { + case AT_BITE: + Sprintf(buf,"%s bites", magr_name); + break; + case AT_STNG: + Sprintf(buf,"%s stings", magr_name); + break; + case AT_BUTT: + Sprintf(buf,"%s butts", magr_name); + break; + case AT_TUCH: + Sprintf(buf,"%s touches", magr_name); + break; + case AT_TENT: + Sprintf(buf, "%s tentacles suck", + s_suffix(magr_name)); + break; + case AT_HUGS: + if (magr != u.ustuck) { + Sprintf(buf,"%s squeezes", magr_name); + break; + } + default: + Sprintf(buf,"%s hits", magr_name); + } + pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr)); + } + } else noises(magr, mattk); + return(mdamagem(magr, mdef, mattk)); +} + +/* Returns the same values as mdamagem(). */ +STATIC_OVL int +gazemm(magr, mdef, mattk) + register struct monst *magr, *mdef; + struct attack *mattk; +{ + char buf[BUFSZ]; + + if(vis) { + Sprintf(buf,"%s gazes at", Monnam(magr)); + pline("%s %s...", buf, mon_nam(mdef)); + } + + if (magr->mcan || !magr->mcansee || + (magr->minvis && !perceives(mdef->data)) || + !mdef->mcansee || mdef->msleeping) { + if(vis) pline("but nothing happens."); + return(MM_MISS); + } + /* call mon_reflects 2x, first test, then, if visible, print message */ + if (magr->data == &mons[PM_MEDUSA] && mon_reflects(mdef, (char *)0)) { + if (canseemon(mdef)) + (void) mon_reflects(mdef, + "The gaze is reflected away by %s %s."); + if (mdef->mcansee) { + if (mon_reflects(magr, (char *)0)) { + if (canseemon(magr)) + (void) mon_reflects(magr, + "The gaze is reflected away by %s %s."); + return (MM_MISS); + } + if (mdef->minvis && !perceives(magr->data)) { + if (canseemon(magr)) { + pline("%s doesn't seem to notice that %s gaze was reflected.", + Monnam(magr), mhis(magr)); + } + return (MM_MISS); + } + if (canseemon(magr)) + pline("%s is turned to stone!", Monnam(magr)); + monstone(magr); + if (magr->mhp > 0) return (MM_MISS); + return (MM_AGR_DIED); + } + } + + return(mdamagem(magr, mdef, mattk)); +} + +/* Returns the same values as mattackm(). */ +STATIC_OVL int +gulpmm(magr, mdef, mattk) + register struct monst *magr, *mdef; + register struct attack *mattk; +{ + xchar ax, ay, dx, dy; + int status; + char buf[BUFSZ]; + struct obj *obj; + + if (mdef->data->msize >= MZ_HUGE) return MM_MISS; + + if (vis) { + Sprintf(buf,"%s swallows", Monnam(magr)); + pline("%s %s.", buf, mon_nam(mdef)); + } + for (obj = mdef->minvent; obj; obj = obj->nobj) + (void) snuff_lit(obj); + + /* + * All of this maniuplation is needed to keep the display correct. + * There is a flush at the next pline(). + */ + ax = magr->mx; + ay = magr->my; + dx = mdef->mx; + dy = mdef->my; + /* + * Leave the defender in the monster chain at it's current position, + * but don't leave it on the screen. Move the agressor to the def- + * ender's position. + */ + remove_monster(ax, ay); + place_monster(magr, dx, dy); + newsym(ax,ay); /* erase old position */ + newsym(dx,dy); /* update new position */ + + status = mdamagem(magr, mdef, mattk); + + if ((status & MM_AGR_DIED) && (status & MM_DEF_DIED)) { + ; /* both died -- do nothing */ + } + else if (status & MM_DEF_DIED) { /* defender died */ + /* + * Note: remove_monster() was called in relmon(), wiping out + * magr from level.monsters[mdef->mx][mdef->my]. We need to + * put it back and display it. -kd + */ + place_monster(magr, dx, dy); + newsym(dx, dy); + } + else if (status & MM_AGR_DIED) { /* agressor died */ + place_monster(mdef, dx, dy); + newsym(dx, dy); + } + else { /* both alive, put them back */ + if (cansee(dx, dy)) + pline("%s is regurgitated!", Monnam(mdef)); + + place_monster(magr, ax, ay); + place_monster(mdef, dx, dy); + newsym(ax, ay); + newsym(dx, dy); + } + + return status; +} + +STATIC_OVL int +explmm(magr, mdef, mattk) + register struct monst *magr, *mdef; + register struct attack *mattk; +{ + int result; + + if (magr->mcan) + return MM_MISS; + + if(cansee(magr->mx, magr->my)) + pline("%s explodes!", Monnam(magr)); + else noises(magr, mattk); + + result = mdamagem(magr, mdef, mattk); + + /* Kill off agressor if it didn't die. */ + if (!(result & MM_AGR_DIED)) { + mondead(magr); + if (magr->mhp > 0) return result; /* life saved */ + result |= MM_AGR_DIED; + } + if (magr->mtame) /* give this one even if it was visible */ + You(brief_feeling, "melancholy"); + + return result; +} + +/* + * See comment at top of mattackm(), for return values. + */ +STATIC_OVL int +mdamagem(magr, mdef, mattk) + register struct monst *magr, *mdef; + register struct attack *mattk; +{ + struct obj *obj; + char buf[BUFSZ]; + struct permonst *pa = magr->data, *pd = mdef->data; + int armpro, num, tmp = d((int)mattk->damn, (int)mattk->damd); + boolean cancelled; + + if (touch_petrifies(pd) && !resists_ston(magr)) { + long protector = attk_protection((int)mattk->aatyp), + wornitems = magr->misc_worn_check; + + /* wielded weapon gives same protection as gloves here */ + if (otmp != 0) wornitems |= W_ARMG; + + if (protector == 0L || + (protector != ~0L && (wornitems & protector) != protector)) { + if (poly_when_stoned(pa)) { + mon_to_stone(magr); + return MM_HIT; /* no damage during the polymorph */ + } + if (vis) pline("%s turns to stone!", Monnam(magr)); + monstone(magr); + if (magr->mhp > 0) return 0; + else if (magr->mtame && !vis) + You(brief_feeling, "peculiarly sad"); + return MM_AGR_DIED; + } + } + + /* cancellation factor is the same as when attacking the hero */ + armpro = magic_negation(mdef); + cancelled = magr->mcan || !((rn2(3) >= armpro) || !rn2(50)); + + switch(mattk->adtyp) { + case AD_DGST: + /* eating a Rider or its corpse is fatal */ + if (is_rider(mdef->data)) { + if (vis) + pline("%s %s!", Monnam(magr), + mdef->data == &mons[PM_FAMINE] ? + "belches feebly, shrivels up and dies" : + mdef->data == &mons[PM_PESTILENCE] ? + "coughs spasmodically and collapses" : + "vomits violently and drops dead"); + mondied(magr); + if (magr->mhp > 0) return 0; /* lifesaved */ + else if (magr->mtame && !vis) + You(brief_feeling, "queasy"); + return MM_AGR_DIED; + } + if(flags.verbose && flags.soundok) verbalize("Burrrrp!"); + tmp = mdef->mhp; + /* Use up amulet of life saving */ + if (!!(obj = mlifesaver(mdef))) m_useup(mdef, obj); + + /* Is a corpse for nutrition possible? It may kill magr */ + if (!corpse_chance(mdef, magr, TRUE) || magr->mhp < 1) + break; + + /* Pets get nutrition from swallowing monster whole. + * No nutrition from G_NOCORPSE monster, eg, undead. + * DGST monsters don't die from undead corpses + */ + num = monsndx(mdef->data); + if (magr->mtame && !magr->isminion && + !(mvitals[num].mvflags & G_NOCORPSE)) { + struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE); + int nutrit; + + virtualcorpse->corpsenm = num; + virtualcorpse->owt = weight(virtualcorpse); + nutrit = dog_nutrition(magr, virtualcorpse); + dealloc_obj(virtualcorpse); + + /* only 50% nutrition, 25% of normal eating time */ + if (magr->meating > 1) magr->meating = (magr->meating+3)/4; + if (nutrit > 1) nutrit /= 2; + EDOG(magr)->hungrytime += nutrit; + } + break; + case AD_STUN: + if (magr->mcan) break; + if (canseemon(mdef)) + pline("%s %s for a moment.", Monnam(mdef), + makeplural(stagger(mdef->data, "stagger"))); + mdef->mstun = 1; + goto physical; + case AD_LEGS: + if (magr->mcan) { + tmp = 0; + break; + } + goto physical; + case AD_WERE: + case AD_HEAL: + case AD_PHYS: + physical: + if (mattk->aatyp == AT_KICK && thick_skinned(pd)) { + tmp = 0; + } else if(mattk->aatyp == AT_WEAP) { + if(otmp) { + if (otmp->otyp == CORPSE && + touch_petrifies(&mons[otmp->corpsenm])) + goto do_stone; + tmp += dmgval(otmp, mdef); + if (otmp->oartifact) { + (void)artifact_hit(magr,mdef, otmp, &tmp, dieroll); + if (mdef->mhp <= 0) + return (MM_DEF_DIED | + (grow_up(magr,mdef) ? 0 : MM_AGR_DIED)); + } + if (tmp) + mrustm(magr, mdef, otmp); + } + } else if (magr->data == &mons[PM_PURPLE_WORM] && + mdef->data == &mons[PM_SHRIEKER]) { + /* hack to enhance mm_aggression(); we don't want purple + worm's bite attack to kill a shrieker because then it + won't swallow the corpse; but if the target survives, + the subsequent engulf attack should accomplish that */ + if (tmp >= mdef->mhp) tmp = mdef->mhp - 1; + } + break; + case AD_FIRE: + if (cancelled) { + tmp = 0; + break; + } + if (vis) + pline("%s is %s!", Monnam(mdef), + on_fire(mdef->data, mattk)); + if (pd == &mons[PM_STRAW_GOLEM] || + pd == &mons[PM_PAPER_GOLEM]) { + if (vis) pline("%s burns completely!", Monnam(mdef)); + mondied(mdef); + if (mdef->mhp > 0) return 0; + else if (mdef->mtame && !vis) + pline("May %s roast in peace.", mon_nam(mdef)); + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + } + tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE); + tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE); + if (resists_fire(mdef)) { + if (vis) + pline_The("fire doesn't seem to burn %s!", + mon_nam(mdef)); + shieldeff(mdef->mx, mdef->my); + golemeffects(mdef, AD_FIRE, tmp); + tmp = 0; + } + /* only potions damage resistant players in destroy_item */ + tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE); + break; + case AD_COLD: + if (cancelled) { + tmp = 0; + break; + } + if (vis) pline("%s is covered in frost!", Monnam(mdef)); + if (resists_cold(mdef)) { + if (vis) + pline_The("frost doesn't seem to chill %s!", + mon_nam(mdef)); + shieldeff(mdef->mx, mdef->my); + golemeffects(mdef, AD_COLD, tmp); + tmp = 0; + } + tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD); + break; + case AD_ELEC: + if (cancelled) { + tmp = 0; + break; + } + if (vis) pline("%s gets zapped!", Monnam(mdef)); + tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC); + if (resists_elec(mdef)) { + if (vis) pline_The("zap doesn't shock %s!", mon_nam(mdef)); + shieldeff(mdef->mx, mdef->my); + golemeffects(mdef, AD_ELEC, tmp); + tmp = 0; + } + /* only rings damage resistant players in destroy_item */ + tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC); + break; + case AD_ACID: + if (magr->mcan) { + tmp = 0; + break; + } + if (resists_acid(mdef)) { + if (vis) + pline("%s is covered in acid, but it seems harmless.", + Monnam(mdef)); + tmp = 0; + } else if (vis) { + pline("%s is covered in acid!", Monnam(mdef)); + pline("It burns %s!", mon_nam(mdef)); + } + if (!rn2(30)) erode_armor(mdef, TRUE); + if (!rn2(6)) erode_obj(MON_WEP(mdef), TRUE, TRUE); + break; + case AD_RUST: + if (magr->mcan) break; + if (pd == &mons[PM_IRON_GOLEM]) { + if (vis) pline("%s falls to pieces!", Monnam(mdef)); + mondied(mdef); + if (mdef->mhp > 0) return 0; + else if (mdef->mtame && !vis) + pline("May %s rust in peace.", mon_nam(mdef)); + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + } + hurtmarmor(mdef, AD_RUST); + mdef->mstrategy &= ~STRAT_WAITFORU; + tmp = 0; + break; + case AD_CORR: + if (magr->mcan) break; + hurtmarmor(mdef, AD_CORR); + mdef->mstrategy &= ~STRAT_WAITFORU; + tmp = 0; + break; + case AD_DCAY: + if (magr->mcan) break; + if (pd == &mons[PM_WOOD_GOLEM] || + pd == &mons[PM_LEATHER_GOLEM]) { + if (vis) pline("%s falls to pieces!", Monnam(mdef)); + mondied(mdef); + if (mdef->mhp > 0) return 0; + else if (mdef->mtame && !vis) + pline("May %s rot in peace.", mon_nam(mdef)); + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + } + hurtmarmor(mdef, AD_DCAY); + mdef->mstrategy &= ~STRAT_WAITFORU; + tmp = 0; + break; + case AD_STON: + if (magr->mcan) break; + do_stone: + /* may die from the acid if it eats a stone-curing corpse */ + if (munstone(mdef, FALSE)) goto post_stone; + if (poly_when_stoned(pd)) { + mon_to_stone(mdef); + tmp = 0; + break; + } + if (!resists_ston(mdef)) { + if (vis) pline("%s turns to stone!", Monnam(mdef)); + monstone(mdef); + post_stone: if (mdef->mhp > 0) return 0; + else if (mdef->mtame && !vis) + You(brief_feeling, "peculiarly sad"); + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + } + tmp = (mattk->adtyp == AD_STON ? 0 : 1); + break; + case AD_TLPT: + if (!cancelled && tmp < mdef->mhp && !tele_restrict(mdef)) { + char mdef_Monnam[BUFSZ]; + /* save the name before monster teleports, otherwise + we'll get "it" in the suddenly disappears message */ + if (vis) Strcpy(mdef_Monnam, Monnam(mdef)); + mdef->mstrategy &= ~STRAT_WAITFORU; + (void) rloc(mdef, FALSE); + if (vis && !canspotmon(mdef) +#ifdef STEED + && mdef != u.usteed +#endif + ) + pline("%s suddenly disappears!", mdef_Monnam); + } + break; + case AD_SLEE: + if (!cancelled && !mdef->msleeping && + sleep_monst(mdef, rnd(10), -1)) { + if (vis) { + Strcpy(buf, Monnam(mdef)); + pline("%s is put to sleep by %s.", buf, mon_nam(magr)); + } + mdef->mstrategy &= ~STRAT_WAITFORU; + slept_monst(mdef); + } + break; + case AD_PLYS: + if(!cancelled && mdef->mcanmove) { + if (vis) { + Strcpy(buf, Monnam(mdef)); + pline("%s is frozen by %s.", buf, mon_nam(magr)); + } + mdef->mcanmove = 0; + mdef->mfrozen = rnd(10); + mdef->mstrategy &= ~STRAT_WAITFORU; + } + break; + case AD_SLOW: + if (!cancelled && mdef->mspeed != MSLOW) { + unsigned int oldspeed = mdef->mspeed; + + mon_adjust_speed(mdef, -1, (struct obj *)0); + mdef->mstrategy &= ~STRAT_WAITFORU; + if (mdef->mspeed != oldspeed && vis) + pline("%s slows down.", Monnam(mdef)); + } + break; + case AD_CONF: + /* Since confusing another monster doesn't have a real time + * limit, setting spec_used would not really be right (though + * we still should check for it). + */ + if (!magr->mcan && !mdef->mconf && !magr->mspec_used) { + if (vis) pline("%s looks confused.", Monnam(mdef)); + mdef->mconf = 1; + mdef->mstrategy &= ~STRAT_WAITFORU; + } + break; + case AD_BLND: + if (can_blnd(magr, mdef, mattk->aatyp, (struct obj*)0)) { + register unsigned rnd_tmp; + + if (vis && mdef->mcansee) + pline("%s is blinded.", Monnam(mdef)); + rnd_tmp = d((int)mattk->damn, (int)mattk->damd); + if ((rnd_tmp += mdef->mblinded) > 127) rnd_tmp = 127; + mdef->mblinded = rnd_tmp; + mdef->mcansee = 0; + mdef->mstrategy &= ~STRAT_WAITFORU; + } + tmp = 0; + break; + case AD_HALU: + if (!magr->mcan && haseyes(pd) && mdef->mcansee) { + if (vis) pline("%s looks %sconfused.", + Monnam(mdef), mdef->mconf ? "more " : ""); + mdef->mconf = 1; + mdef->mstrategy &= ~STRAT_WAITFORU; + } + tmp = 0; + break; + case AD_CURS: + if (!night() && (pa == &mons[PM_GREMLIN])) break; + if (!magr->mcan && !rn2(10)) { + mdef->mcan = 1; /* cancelled regardless of lifesave */ + mdef->mstrategy &= ~STRAT_WAITFORU; + if (is_were(pd) && pd->mlet != S_HUMAN) + were_change(mdef); + if (pd == &mons[PM_CLAY_GOLEM]) { + if (vis) { + pline("Some writing vanishes from %s head!", + s_suffix(mon_nam(mdef))); + pline("%s is destroyed!", Monnam(mdef)); + } + mondied(mdef); + if (mdef->mhp > 0) return 0; + else if (mdef->mtame && !vis) + You(brief_feeling, "strangely sad"); + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + } + if (flags.soundok) { + if (!vis) You_hear("laughter."); + else pline("%s chuckles.", Monnam(magr)); + } + } + break; + case AD_SGLD: + tmp = 0; +#ifndef GOLDOBJ + if (magr->mcan || !mdef->mgold) break; + /* technically incorrect; no check for stealing gold from + * between mdef's feet... + */ + magr->mgold += mdef->mgold; + mdef->mgold = 0; +#else + if (magr->mcan) break; + /* technically incorrect; no check for stealing gold from + * between mdef's feet... + */ + { + struct obj *gold = findgold(mdef->minvent); + if (!gold) break; + obj_extract_self(gold); + add_to_minv(magr, gold); + } +#endif + mdef->mstrategy &= ~STRAT_WAITFORU; + if (vis) { + Strcpy(buf, Monnam(magr)); + pline("%s steals some gold from %s.", buf, mon_nam(mdef)); + } + if (!tele_restrict(magr)) { + (void) rloc(magr, FALSE); + if (vis && !canspotmon(magr)) + pline("%s suddenly disappears!", buf); + } + break; + case AD_DRLI: + if (!cancelled && !rn2(3) && !resists_drli(mdef)) { + tmp = d(2,6); + if (vis) + pline("%s suddenly seems weaker!", Monnam(mdef)); + mdef->mhpmax -= tmp; + if (mdef->m_lev == 0) + tmp = mdef->mhp; + else mdef->m_lev--; + /* Automatic kill if drained past level 0 */ + } + break; +#ifdef SEDUCE + case AD_SSEX: +#endif + case AD_SITM: /* for now these are the same */ + case AD_SEDU: + if (magr->mcan) break; + /* find an object to steal, non-cursed if magr is tame */ + for (obj = mdef->minvent; obj; obj = obj->nobj) + if (!magr->mtame || !obj->cursed) + break; + + if (obj) { + char onambuf[BUFSZ], mdefnambuf[BUFSZ]; + + /* make a special x_monnam() call that never omits + the saddle, and save it for later messages */ + Strcpy(mdefnambuf, x_monnam(mdef, ARTICLE_THE, (char *)0, 0, FALSE)); + + otmp = obj; +#ifdef STEED + if (u.usteed == mdef && + otmp == which_armor(mdef, W_SADDLE)) + /* "You can no longer ride ." */ + dismount_steed(DISMOUNT_POLY); +#endif + obj_extract_self(otmp); + if (otmp->owornmask) { + mdef->misc_worn_check &= ~otmp->owornmask; + if (otmp->owornmask & W_WEP) + setmnotwielded(mdef,otmp); + otmp->owornmask = 0L; + update_mon_intrinsics(mdef, otmp, FALSE, FALSE); + } + /* add_to_minv() might free otmp [if it merges] */ + if (vis) + Strcpy(onambuf, doname(otmp)); + (void) add_to_minv(magr, otmp); + if (vis) { + Strcpy(buf, Monnam(magr)); + pline("%s steals %s from %s!", buf, + onambuf, mdefnambuf); + } + possibly_unwield(mdef, FALSE); + mdef->mstrategy &= ~STRAT_WAITFORU; + mselftouch(mdef, (const char *)0, FALSE); + if (mdef->mhp <= 0) + return (MM_DEF_DIED | (grow_up(magr,mdef) ? + 0 : MM_AGR_DIED)); + if (magr->data->mlet == S_NYMPH && + !tele_restrict(magr)) { + (void) rloc(magr, FALSE); + if (vis && !canspotmon(magr)) + pline("%s suddenly disappears!", buf); + } + } + tmp = 0; + break; + case AD_DRST: + case AD_DRDX: + case AD_DRCO: + if (!cancelled && !rn2(8)) { + if (vis) + pline("%s %s was poisoned!", s_suffix(Monnam(magr)), + mpoisons_subj(magr, mattk)); + if (resists_poison(mdef)) { + if (vis) + pline_The("poison doesn't seem to affect %s.", + mon_nam(mdef)); + } else { + if (rn2(10)) tmp += rn1(10,6); + else { + if (vis) pline_The("poison was deadly..."); + tmp = mdef->mhp; + } + } + } + break; + case AD_DRIN: + if (notonhead || !has_head(pd)) { + if (vis) pline("%s doesn't seem harmed.", Monnam(mdef)); + /* Not clear what to do for green slimes */ + tmp = 0; + break; + } + if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) { + if (vis) { + Strcpy(buf, s_suffix(Monnam(mdef))); + pline("%s helmet blocks %s attack to %s head.", + buf, s_suffix(mon_nam(magr)), + mhis(mdef)); + } + break; + } + if (vis) pline("%s brain is eaten!", s_suffix(Monnam(mdef))); + if (mindless(pd)) { + if (vis) pline("%s doesn't notice.", Monnam(mdef)); + break; + } + tmp += rnd(10); /* fakery, since monsters lack INT scores */ + if (magr->mtame && !magr->isminion) { + EDOG(magr)->hungrytime += rnd(60); + magr->mconf = 0; + } + if (tmp >= mdef->mhp && vis) + pline("%s last thought fades away...", + s_suffix(Monnam(mdef))); + break; + case AD_SLIM: + if (cancelled) break; /* physical damage only */ + if (!rn2(4) && !flaming(mdef->data) && + mdef->data != &mons[PM_GREEN_SLIME]) { + (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis); + mdef->mstrategy &= ~STRAT_WAITFORU; + tmp = 0; + } + break; + case AD_STCK: + if (cancelled) tmp = 0; + break; + case AD_WRAP: /* monsters cannot grab one another, it's too hard */ + if (magr->mcan) tmp = 0; + break; + case AD_ENCH: + /* there's no msomearmor() function, so just do damage */ + /* if (cancelled) break; */ + break; + default: tmp = 0; + break; + } + if(!tmp) return(MM_MISS); + + if((mdef->mhp -= tmp) < 1) { + if (m_at(mdef->mx, mdef->my) == magr) { /* see gulpmm() */ + remove_monster(mdef->mx, mdef->my); + mdef->mhp = 1; /* otherwise place_monster will complain */ + place_monster(mdef, mdef->mx, mdef->my); + mdef->mhp = 0; + } + monkilled(mdef, "", (int)mattk->adtyp); + if (mdef->mhp > 0) return 0; /* mdef lifesaved */ + + if (mattk->adtyp == AD_DGST) { + /* various checks similar to dog_eat and meatobj. + * after monkilled() to provide better message ordering */ + if (mdef->cham != CHAM_ORDINARY) { + (void) newcham(magr, (struct permonst *)0, FALSE, TRUE); + } else if (mdef->data == &mons[PM_GREEN_SLIME]) { + (void) newcham(magr, &mons[PM_GREEN_SLIME], FALSE, TRUE); + } else if (mdef->data == &mons[PM_WRAITH]) { + (void) grow_up(magr, (struct monst *)0); + /* don't grow up twice */ + return (MM_DEF_DIED | (magr->mhp > 0 ? 0 : MM_AGR_DIED)); + } else if (mdef->data == &mons[PM_NURSE]) { + magr->mhp = magr->mhpmax; + } + } + + return (MM_DEF_DIED | (grow_up(magr,mdef) ? 0 : MM_AGR_DIED)); + } + return(MM_HIT); +} + +#endif /* OVLB */ + + +#ifdef OVL0 + +int +noattacks(ptr) /* returns 1 if monster doesn't attack */ + struct permonst *ptr; +{ + int i; + + for(i = 0; i < NATTK; i++) + if(ptr->mattk[i].aatyp) return(0); + + return(1); +} + +/* `mon' is hit by a sleep attack; return 1 if it's affected, 0 otherwise */ +int +sleep_monst(mon, amt, how) +struct monst *mon; +int amt, how; +{ + if (resists_sleep(mon) || + (how >= 0 && resist(mon, (char)how, 0, NOTELL))) { + shieldeff(mon->mx, mon->my); + } else if (mon->mcanmove) { + amt += (int) mon->mfrozen; + if (amt > 0) { /* sleep for N turns */ + mon->mcanmove = 0; + mon->mfrozen = min(amt, 127); + } else { /* sleep until awakened */ + mon->msleeping = 1; + } + return 1; + } + return 0; +} + +/* sleeping grabber releases, engulfer doesn't; don't use for paralysis! */ +void +slept_monst(mon) +struct monst *mon; +{ + if ((mon->msleeping || !mon->mcanmove) && mon == u.ustuck && + !sticks(youmonst.data) && !u.uswallow) { + pline("%s grip relaxes.", s_suffix(Monnam(mon))); + unstuck(mon); + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +STATIC_OVL void +mrustm(magr, mdef, obj) +register struct monst *magr, *mdef; +register struct obj *obj; +{ + boolean is_acid; + + if (!magr || !mdef || !obj) return; /* just in case */ + + if (dmgtype(mdef->data, AD_CORR)) + is_acid = TRUE; + else if (dmgtype(mdef->data, AD_RUST)) + is_acid = FALSE; + else + return; + + if (!mdef->mcan && + (is_acid ? is_corrodeable(obj) : is_rustprone(obj)) && + (is_acid ? obj->oeroded2 : obj->oeroded) < MAX_ERODE) { + if (obj->greased || obj->oerodeproof || (obj->blessed && rn2(3))) { + if (cansee(mdef->mx, mdef->my) && flags.verbose) + pline("%s weapon is not affected.", + s_suffix(Monnam(magr))); + if (obj->greased && !rn2(2)) obj->greased = 0; + } else { + if (cansee(mdef->mx, mdef->my)) { + pline("%s %s%s!", s_suffix(Monnam(magr)), + aobjnam(obj, (is_acid ? "corrode" : "rust")), + (is_acid ? obj->oeroded2 : obj->oeroded) + ? " further" : ""); + } + if (is_acid) obj->oeroded2++; + else obj->oeroded++; + } + } +} + +STATIC_OVL void +mswingsm(magr, mdef, otemp) +register struct monst *magr, *mdef; +register struct obj *otemp; +{ + char buf[BUFSZ]; + if (!flags.verbose || Blind || !mon_visible(magr)) return; + Strcpy(buf, mon_nam(mdef)); + pline("%s %s %s %s at %s.", Monnam(magr), + (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings", + mhis(magr), singular(otemp, xname), buf); +} + +/* + * Passive responses by defenders. Does not replicate responses already + * handled above. Returns same values as mattackm. + */ +STATIC_OVL int +passivemm(magr,mdef,mhit,mdead) +register struct monst *magr, *mdef; +boolean mhit; +int mdead; +{ + register struct permonst *mddat = mdef->data; + register struct permonst *madat = magr->data; + char buf[BUFSZ]; + int i, tmp; + + for(i = 0; ; i++) { + if(i >= NATTK) return (mdead | mhit); /* no passive attacks */ + if(mddat->mattk[i].aatyp == AT_NONE) break; + } + if (mddat->mattk[i].damn) + tmp = d((int)mddat->mattk[i].damn, + (int)mddat->mattk[i].damd); + else if(mddat->mattk[i].damd) + tmp = d((int)mddat->mlevel+1, (int)mddat->mattk[i].damd); + else + tmp = 0; + + /* These affect the enemy even if defender killed */ + switch(mddat->mattk[i].adtyp) { + case AD_ACID: + if (mhit && !rn2(2)) { + Strcpy(buf, Monnam(magr)); + if(canseemon(magr)) + pline("%s is splashed by %s acid!", + buf, s_suffix(mon_nam(mdef))); + if (resists_acid(magr)) { + if(canseemon(magr)) + pline("%s is not affected.", Monnam(magr)); + tmp = 0; + } + } else tmp = 0; + goto assess_dmg; + case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ + if (mhit && !mdef->mcan && otmp) { + (void) drain_item(otmp); + /* No message */ + } + break; + default: + break; + } + if (mdead || mdef->mcan) return (mdead|mhit); + + /* These affect the enemy only if defender is still alive */ + if (rn2(3)) switch(mddat->mattk[i].adtyp) { + case AD_PLYS: /* Floating eye */ + if (tmp > 127) tmp = 127; + if (mddat == &mons[PM_FLOATING_EYE]) { + if (!rn2(4)) tmp = 127; + if (magr->mcansee && haseyes(madat) && mdef->mcansee && + (perceives(madat) || !mdef->minvis)) { + Sprintf(buf, "%s gaze is reflected by %%s %%s.", + s_suffix(mon_nam(mdef))); + if (mon_reflects(magr, + canseemon(magr) ? buf : (char *)0)) + return(mdead|mhit); + Strcpy(buf, Monnam(magr)); + if(canseemon(magr)) + pline("%s is frozen by %s gaze!", + buf, s_suffix(mon_nam(mdef))); + magr->mcanmove = 0; + magr->mfrozen = tmp; + return (mdead|mhit); + } + } else { /* gelatinous cube */ + Strcpy(buf, Monnam(magr)); + if(canseemon(magr)) + pline("%s is frozen by %s.", buf, mon_nam(mdef)); + magr->mcanmove = 0; + magr->mfrozen = tmp; + return (mdead|mhit); + } + return 1; + case AD_COLD: + if (resists_cold(magr)) { + if (canseemon(magr)) { + pline("%s is mildly chilly.", Monnam(magr)); + golemeffects(magr, AD_COLD, tmp); + } + tmp = 0; + break; + } + if(canseemon(magr)) + pline("%s is suddenly very cold!", Monnam(magr)); + mdef->mhp += tmp / 2; + if (mdef->mhpmax < mdef->mhp) mdef->mhpmax = mdef->mhp; + if (mdef->mhpmax > ((int) (mdef->m_lev+1) * 8)) + (void)split_mon(mdef, magr); + break; + case AD_STUN: + if (!magr->mstun) { + magr->mstun = 1; + if (canseemon(magr)) + pline("%s %s...", Monnam(magr), + makeplural(stagger(magr->data, "stagger"))); + } + tmp = 0; + break; + case AD_FIRE: + if (resists_fire(magr)) { + if (canseemon(magr)) { + pline("%s is mildly warmed.", Monnam(magr)); + golemeffects(magr, AD_FIRE, tmp); + } + tmp = 0; + break; + } + if(canseemon(magr)) + pline("%s is suddenly very hot!", Monnam(magr)); + break; + case AD_ELEC: + if (resists_elec(magr)) { + if (canseemon(magr)) { + pline("%s is mildly tingled.", Monnam(magr)); + golemeffects(magr, AD_ELEC, tmp); + } + tmp = 0; + break; + } + if(canseemon(magr)) + pline("%s is jolted with electricity!", Monnam(magr)); + break; + default: tmp = 0; + break; + } + else tmp = 0; + + assess_dmg: + if((magr->mhp -= tmp) <= 0) { + monkilled(magr, "", (int)mddat->mattk[i].adtyp); + return (mdead | mhit | MM_AGR_DIED); + } + return (mdead | mhit); +} + +/* "aggressive defense"; what type of armor prevents specified attack + from touching its target? */ +long +attk_protection(aatyp) +int aatyp; +{ + long w_mask = 0L; + + switch (aatyp) { + case AT_NONE: + case AT_SPIT: + case AT_EXPL: + case AT_BOOM: + case AT_GAZE: + case AT_BREA: + case AT_MAGC: + w_mask = ~0L; /* special case; no defense needed */ + break; + case AT_CLAW: + case AT_TUCH: + case AT_WEAP: + w_mask = W_ARMG; /* caller needs to check for weapon */ + break; + case AT_KICK: + w_mask = W_ARMF; + break; + case AT_BUTT: + w_mask = W_ARMH; + break; + case AT_HUGS: + w_mask = (W_ARMC|W_ARMG); /* attacker needs both to be protected */ + break; + case AT_BITE: + case AT_STNG: + case AT_ENGL: + case AT_TENT: + default: + w_mask = 0L; /* no defense available */ + break; + } + return w_mask; +} + +#endif /* OVLB */ + +/*mhitm.c*/ + diff --git a/src/mhitu.c b/src/mhitu.c new file mode 100644 index 0000000..c0711fc --- /dev/null +++ b/src/mhitu.c @@ -0,0 +1,2633 @@ +/* SCCS Id: @(#)mhitu.c 3.4 2003/11/26 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "artifact.h" + +STATIC_VAR NEARDATA struct obj *otmp; + +STATIC_DCL void FDECL(urustm, (struct monst *, struct obj *)); +# ifdef OVL1 +STATIC_DCL boolean FDECL(u_slip_free, (struct monst *,struct attack *)); +STATIC_DCL int FDECL(passiveum, (struct permonst *,struct monst *,struct attack *)); +# endif /* OVL1 */ + +#ifdef OVLB +# ifdef SEDUCE +STATIC_DCL void FDECL(mayberem, (struct obj *, const char *)); +# endif +#endif /* OVLB */ + +STATIC_DCL boolean FDECL(diseasemu, (struct permonst *)); +STATIC_DCL int FDECL(hitmu, (struct monst *,struct attack *)); +STATIC_DCL int FDECL(gulpmu, (struct monst *,struct attack *)); +STATIC_DCL int FDECL(explmu, (struct monst *,struct attack *,BOOLEAN_P)); +STATIC_DCL void FDECL(missmu,(struct monst *,BOOLEAN_P,struct attack *)); +STATIC_DCL void FDECL(mswings,(struct monst *,struct obj *)); +STATIC_DCL void FDECL(wildmiss, (struct monst *,struct attack *)); + +STATIC_DCL void FDECL(hurtarmor,(int)); +STATIC_DCL void FDECL(hitmsg,(struct monst *,struct attack *)); + +/* See comment in mhitm.c. If we use this a lot it probably should be */ +/* changed to a parameter to mhitu. */ +static int dieroll; + +#ifdef OVL1 + + +STATIC_OVL void +hitmsg(mtmp, mattk) +register struct monst *mtmp; +register struct attack *mattk; +{ + int compat; + + /* Note: if opposite gender, "seductively" */ + /* If same gender, "engagingly" for nymph, normal msg for others */ + if((compat = could_seduce(mtmp, &youmonst, mattk)) + && !mtmp->mcan && !mtmp->mspec_used) { + pline("%s %s you %s.", Monnam(mtmp), + Blind ? "talks to" : "smiles at", + compat == 2 ? "engagingly" : "seductively"); + } else + switch (mattk->aatyp) { + case AT_BITE: + pline("%s bites!", Monnam(mtmp)); + break; + case AT_KICK: + pline("%s kicks%c", Monnam(mtmp), + thick_skinned(youmonst.data) ? '.' : '!'); + break; + case AT_STNG: + pline("%s stings!", Monnam(mtmp)); + break; + case AT_BUTT: + pline("%s butts!", Monnam(mtmp)); + break; + case AT_TUCH: + pline("%s touches you!", Monnam(mtmp)); + break; + case AT_TENT: + pline("%s tentacles suck you!", + s_suffix(Monnam(mtmp))); + break; + case AT_EXPL: + case AT_BOOM: + pline("%s explodes!", Monnam(mtmp)); + break; + default: + pline("%s hits!", Monnam(mtmp)); + } +} + +STATIC_OVL void +missmu(mtmp, nearmiss, mattk) /* monster missed you */ +register struct monst *mtmp; +register boolean nearmiss; +register struct attack *mattk; +{ + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + + if(could_seduce(mtmp, &youmonst, mattk) && !mtmp->mcan) + pline("%s pretends to be friendly.", Monnam(mtmp)); + else { + if (!flags.verbose || !nearmiss) + pline("%s misses.", Monnam(mtmp)); + else + pline("%s just misses!", Monnam(mtmp)); + } + stop_occupation(); +} + +STATIC_OVL void +mswings(mtmp, otemp) /* monster swings obj */ +register struct monst *mtmp; +register struct obj *otemp; +{ + if (!flags.verbose || Blind || !mon_visible(mtmp)) + return; + pline("%s %s %s %s.", Monnam(mtmp), + (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings", + mhis(mtmp), singular(otemp, xname)); +} + +/* return how a poison attack was delivered */ +const char * +mpoisons_subj(mtmp, mattk) +struct monst *mtmp; +struct attack *mattk; +{ + if (mattk->aatyp == AT_WEAP) { + struct obj *mwep = (mtmp == &youmonst) ? uwep : MON_WEP(mtmp); + /* "Foo's attack was poisoned." is pretty lame, but at least + it's better than "sting" when not a stinging attack... */ + return (!mwep || !mwep->opoisoned) ? "attack" : "weapon"; + } else { + return (mattk->aatyp == AT_TUCH) ? "contact" : + (mattk->aatyp == AT_GAZE) ? "gaze" : + (mattk->aatyp == AT_BITE) ? "bite" : "sting"; + } +} + +/* called when your intrinsic speed is taken away */ +void +u_slow_down() +{ + HFast = 0L; + if (!Fast) + You("slow down."); + else /* speed boots */ + Your("quickness feels less natural."); + exercise(A_DEX, FALSE); +} + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_OVL void +wildmiss(mtmp, mattk) /* monster attacked your displaced image */ + register struct monst *mtmp; + register struct attack *mattk; +{ + int compat; + + /* no map_invisible() -- no way to tell where _this_ is coming from */ + + if (!flags.verbose) return; + if (!cansee(mtmp->mx, mtmp->my)) return; + /* maybe it's attacking an image around the corner? */ + + compat = (mattk->adtyp == AD_SEDU || mattk->adtyp == AD_SSEX) && + could_seduce(mtmp, &youmonst, (struct attack *)0); + + if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) { + const char *swings = + mattk->aatyp == AT_BITE ? "snaps" : + mattk->aatyp == AT_KICK ? "kicks" : + (mattk->aatyp == AT_STNG || + mattk->aatyp == AT_BUTT || + nolimbs(mtmp->data)) ? "lunges" : "swings"; + + if (compat) + pline("%s tries to touch you and misses!", Monnam(mtmp)); + else + switch(rn2(3)) { + case 0: pline("%s %s wildly and misses!", Monnam(mtmp), + swings); + break; + case 1: pline("%s attacks a spot beside you.", Monnam(mtmp)); + break; + case 2: pline("%s strikes at %s!", Monnam(mtmp), + levl[mtmp->mux][mtmp->muy].typ == WATER + ? "empty water" : "thin air"); + break; + default:pline("%s %s wildly!", Monnam(mtmp), swings); + break; + } + + } else if (Displaced) { + if (compat) + pline("%s smiles %s at your %sdisplaced image...", + Monnam(mtmp), + compat == 2 ? "engagingly" : "seductively", + Invis ? "invisible " : ""); + else + pline("%s strikes at your %sdisplaced image and misses you!", + /* Note: if you're both invisible and displaced, + * only monsters which see invisible will attack your + * displaced image, since the displaced image is also + * invisible. + */ + Monnam(mtmp), + Invis ? "invisible " : ""); + + } else if (Underwater) { + /* monsters may miss especially on water level where + bubbles shake the player here and there */ + if (compat) + pline("%s reaches towards your distorted image.",Monnam(mtmp)); + else + pline("%s is fooled by water reflections and misses!",Monnam(mtmp)); + + } else impossible("%s attacks you without knowing your location?", + Monnam(mtmp)); +} + +void +expels(mtmp, mdat, message) +register struct monst *mtmp; +register struct permonst *mdat; /* if mtmp is polymorphed, mdat != mtmp->data */ +boolean message; +{ + if (message) { + if (is_animal(mdat)) + You("get regurgitated!"); + else { + char blast[40]; + register int i; + + blast[0] = '\0'; + for(i = 0; i < NATTK; i++) + if(mdat->mattk[i].aatyp == AT_ENGL) + break; + if (mdat->mattk[i].aatyp != AT_ENGL) + impossible("Swallower has no engulfing attack?"); + else { + if (is_whirly(mdat)) { + switch (mdat->mattk[i].adtyp) { + case AD_ELEC: + Strcpy(blast, + " in a shower of sparks"); + break; + case AD_COLD: + Strcpy(blast, + " in a blast of frost"); + break; + } + } else + Strcpy(blast, " with a squelch"); + You("get expelled from %s%s!", + mon_nam(mtmp), blast); + } + } + } + unstuck(mtmp); /* ball&chain returned in unstuck() */ + mnexto(mtmp); + newsym(u.ux,u.uy); + spoteffects(TRUE); + /* to cover for a case where mtmp is not in a next square */ + if(um_dist(mtmp->mx,mtmp->my,1)) + pline("Brrooaa... You land hard at some distance."); +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* select a monster's next attack, possibly substituting for its usual one */ +struct attack * +getmattk(mptr, indx, prev_result, alt_attk_buf) +struct permonst *mptr; +int indx, prev_result[]; +struct attack *alt_attk_buf; +{ + struct attack *attk = &mptr->mattk[indx]; + + /* prevent a monster with two consecutive disease or hunger attacks + from hitting with both of them on the same turn; if the first has + already hit, switch to a stun attack for the second */ + if (indx > 0 && prev_result[indx - 1] > 0 && + (attk->adtyp == AD_DISE || + attk->adtyp == AD_PEST || + attk->adtyp == AD_FAMN) && + attk->adtyp == mptr->mattk[indx - 1].adtyp) { + *alt_attk_buf = *attk; + attk = alt_attk_buf; + attk->adtyp = AD_STUN; + } + return attk; +} + +/* + * mattacku: monster attacks you + * returns 1 if monster dies (e.g. "yellow light"), 0 otherwise + * Note: if you're displaced or invisible the monster might attack the + * wrong position... + * Assumption: it's attacking you or an empty square; if there's another + * monster which it attacks by mistake, the caller had better + * take care of it... + */ +int +mattacku(mtmp) + register struct monst *mtmp; +{ + struct attack *mattk, alt_attk; + int i, j, tmp, sum[NATTK]; + struct permonst *mdat = mtmp->data; + boolean ranged = (distu(mtmp->mx, mtmp->my) > 3); + /* Is it near you? Affects your actions */ + boolean range2 = !monnear(mtmp, mtmp->mux, mtmp->muy); + /* Does it think it's near you? Affects its actions */ + boolean foundyou = (mtmp->mux==u.ux && mtmp->muy==u.uy); + /* Is it attacking you or your image? */ + boolean youseeit = canseemon(mtmp); + /* Might be attacking your image around the corner, or + * invisible, or you might be blind.... + */ + + if(!ranged) nomul(0); + if(mtmp->mhp <= 0 || (Underwater && !is_swimmer(mtmp->data))) + return(0); + + /* If swallowed, can only be affected by u.ustuck */ + if(u.uswallow) { + if(mtmp != u.ustuck) + return(0); + u.ustuck->mux = u.ux; + u.ustuck->muy = u.uy; + range2 = 0; + foundyou = 1; + if(u.uinvulnerable) return (0); /* stomachs can't hurt you! */ + } + +#ifdef STEED + else if (u.usteed) { + if (mtmp == u.usteed) + /* Your steed won't attack you */ + return (0); + /* Orcs like to steal and eat horses and the like */ + if (!rn2(is_orc(mtmp->data) ? 2 : 4) && + distu(mtmp->mx, mtmp->my) <= 2) { + /* Attack your steed instead */ + i = mattackm(mtmp, u.usteed); + if ((i & MM_AGR_DIED)) + return (1); + if (i & MM_DEF_DIED || u.umoved) + return (0); + /* Let your steed retaliate */ + return (!!(mattackm(u.usteed, mtmp) & MM_DEF_DIED)); + } + } +#endif + + if (u.uundetected && !range2 && foundyou && !u.uswallow) { + u.uundetected = 0; + if (is_hider(youmonst.data)) { + coord cc; /* maybe we need a unexto() function? */ + struct obj *obj; + + You("fall from the %s!", ceiling(u.ux,u.uy)); + if (enexto(&cc, u.ux, u.uy, youmonst.data)) { + remove_monster(mtmp->mx, mtmp->my); + newsym(mtmp->mx,mtmp->my); + place_monster(mtmp, u.ux, u.uy); + if(mtmp->wormno) worm_move(mtmp); + teleds(cc.x, cc.y, TRUE); + set_apparxy(mtmp); + newsym(u.ux,u.uy); + } else { + pline("%s is killed by a falling %s (you)!", + Monnam(mtmp), youmonst.data->mname); + killed(mtmp); + newsym(u.ux,u.uy); + if (mtmp->mhp > 0) return 0; + else return 1; + } + if (youmonst.data->mlet != S_PIERCER) + return(0); /* trappers don't attack */ + + obj = which_armor(mtmp, WORN_HELMET); + if (obj && is_metallic(obj)) { + Your("blow glances off %s helmet.", + s_suffix(mon_nam(mtmp))); + } else { + if (3 + find_mac(mtmp) <= rnd(20)) { + pline("%s is hit by a falling piercer (you)!", + Monnam(mtmp)); + if ((mtmp->mhp -= d(3,6)) < 1) + killed(mtmp); + } else + pline("%s is almost hit by a falling piercer (you)!", + Monnam(mtmp)); + } + } else { + if (!youseeit) + pline("It tries to move where you are hiding."); + else { + /* Ugly kludge for eggs. The message is phrased so as + * to be directed at the monster, not the player, + * which makes "laid by you" wrong. For the + * parallelism to work, we can't rephrase it, so we + * zap the "laid by you" momentarily instead. + */ + struct obj *obj = level.objects[u.ux][u.uy]; + + if (obj || + (youmonst.data->mlet == S_EEL && is_pool(u.ux, u.uy))) { + int save_spe = 0; /* suppress warning */ + if (obj) { + save_spe = obj->spe; + if (obj->otyp == EGG) obj->spe = 0; + } + if (youmonst.data->mlet == S_EEL) + pline("Wait, %s! There's a hidden %s named %s there!", + m_monnam(mtmp), youmonst.data->mname, plname); + else + pline("Wait, %s! There's a %s named %s hiding under %s!", + m_monnam(mtmp), youmonst.data->mname, plname, + doname(level.objects[u.ux][u.uy])); + if (obj) obj->spe = save_spe; + } else + impossible("hiding under nothing?"); + } + newsym(u.ux,u.uy); + } + return(0); + } + if (youmonst.data->mlet == S_MIMIC && youmonst.m_ap_type && + !range2 && foundyou && !u.uswallow) { + if (!youseeit) pline("It gets stuck on you."); + else pline("Wait, %s! That's a %s named %s!", + m_monnam(mtmp), youmonst.data->mname, plname); + u.ustuck = mtmp; + youmonst.m_ap_type = M_AP_NOTHING; + youmonst.mappearance = 0; + newsym(u.ux,u.uy); + return(0); + } + + /* player might be mimicking an object */ + if (youmonst.m_ap_type == M_AP_OBJECT && !range2 && foundyou && !u.uswallow) { + if (!youseeit) + pline("%s %s!", Something, + (likes_gold(mtmp->data) && youmonst.mappearance == GOLD_PIECE) ? + "tries to pick you up" : "disturbs you"); + else pline("Wait, %s! That %s is really %s named %s!", + m_monnam(mtmp), + mimic_obj_name(&youmonst), + an(mons[u.umonnum].mname), + plname); + if (multi < 0) { /* this should always be the case */ + char buf[BUFSZ]; + Sprintf(buf, "You appear to be %s again.", + Upolyd ? (const char *) an(youmonst.data->mname) : + (const char *) "yourself"); + unmul(buf); /* immediately stop mimicking */ + } + return 0; + } + +/* Work out the armor class differential */ + tmp = AC_VALUE(u.uac) + 10; /* tmp ~= 0 - 20 */ + tmp += mtmp->m_lev; + if(multi < 0) tmp += 4; + if((Invis && !perceives(mdat)) || !mtmp->mcansee) + tmp -= 2; + if(mtmp->mtrapped) tmp -= 2; + if(tmp <= 0) tmp = 1; + + /* make eels visible the moment they hit/miss us */ + if(mdat->mlet == S_EEL && mtmp->minvis && cansee(mtmp->mx,mtmp->my)) { + mtmp->minvis = 0; + newsym(mtmp->mx,mtmp->my); + } + +/* Special demon handling code */ + if(!mtmp->cham && is_demon(mdat) && !range2 + && mtmp->data != &mons[PM_BALROG] + && mtmp->data != &mons[PM_SUCCUBUS] + && mtmp->data != &mons[PM_INCUBUS]) + if(!mtmp->mcan && !rn2(13)) msummon(mtmp); + +/* Special lycanthrope handling code */ + if(!mtmp->cham && is_were(mdat) && !range2) { + + if(is_human(mdat)) { + if(!rn2(5 - (night() * 2)) && !mtmp->mcan) new_were(mtmp); + } else if(!rn2(30) && !mtmp->mcan) new_were(mtmp); + mdat = mtmp->data; + + if(!rn2(10) && !mtmp->mcan) { + int numseen, numhelp; + char buf[BUFSZ], genericwere[BUFSZ]; + + Strcpy(genericwere, "creature"); + numhelp = were_summon(mdat, FALSE, &numseen, genericwere); + if (youseeit) { + pline("%s summons help!", Monnam(mtmp)); + if (numhelp > 0) { + if (numseen == 0) + You_feel("hemmed in."); + } else pline("But none comes."); + } else { + const char *from_nowhere; + + if (flags.soundok) { + pline("%s %s!", Something, + makeplural(growl_sound(mtmp))); + from_nowhere = ""; + } else from_nowhere = " from nowhere"; + if (numhelp > 0) { + if (numseen < 1) You_feel("hemmed in."); + else { + if (numseen == 1) + Sprintf(buf, "%s appears", + an(genericwere)); + else + Sprintf(buf, "%s appear", + makeplural(genericwere)); + pline("%s%s!", upstart(buf), from_nowhere); + } + } /* else no help came; but you didn't know it tried */ + } + } + } + + if(u.uinvulnerable) { + /* monsters won't attack you */ + if(mtmp == u.ustuck) + pline("%s loosens its grip slightly.", Monnam(mtmp)); + else if(!range2) { + if (youseeit || sensemon(mtmp)) + pline("%s starts to attack you, but pulls back.", + Monnam(mtmp)); + else + You_feel("%s move nearby.", something); + } + return (0); + } + + /* Unlike defensive stuff, don't let them use item _and_ attack. */ + if(find_offensive(mtmp)) { + int foo = use_offensive(mtmp); + + if (foo != 0) return(foo==1); + } + + for(i = 0; i < NATTK; i++) { + + sum[i] = 0; + mattk = getmattk(mdat, i, sum, &alt_attk); + if (u.uswallow && (mattk->aatyp != AT_ENGL)) + continue; + switch(mattk->aatyp) { + case AT_CLAW: /* "hand to hand" attacks */ + case AT_KICK: + case AT_BITE: + case AT_STNG: + case AT_TUCH: + case AT_BUTT: + case AT_TENT: + if(!range2 && (!MON_WEP(mtmp) || mtmp->mconf || Conflict || + !touch_petrifies(youmonst.data))) { + if (foundyou) { + if(tmp > (j = rnd(20+i))) { + if (mattk->aatyp != AT_KICK || + !thick_skinned(youmonst.data)) + sum[i] = hitmu(mtmp, mattk); + } else + missmu(mtmp, (tmp == j), mattk); + } else + wildmiss(mtmp, mattk); + } + break; + + case AT_HUGS: /* automatic if prev two attacks succeed */ + /* Note: if displaced, prev attacks never succeeded */ + if((!range2 && i>=2 && sum[i-1] && sum[i-2]) + || mtmp == u.ustuck) + sum[i]= hitmu(mtmp, mattk); + break; + + case AT_GAZE: /* can affect you either ranged or not */ + /* Medusa gaze already operated through m_respond in + * dochug(); don't gaze more than once per round. + */ + if (mdat != &mons[PM_MEDUSA]) + sum[i] = gazemu(mtmp, mattk); + break; + + case AT_EXPL: /* automatic hit if next to, and aimed at you */ + if(!range2) sum[i] = explmu(mtmp, mattk, foundyou); + break; + + case AT_ENGL: + if (!range2) { + if(foundyou) { + if(u.uswallow || tmp > (j = rnd(20+i))) { + /* Force swallowing monster to be + * displayed even when player is + * moving away */ + flush_screen(1); + sum[i] = gulpmu(mtmp, mattk); + } else { + missmu(mtmp, (tmp == j), mattk); + } + } else if (is_animal(mtmp->data)) { + pline("%s gulps some air!", Monnam(mtmp)); + } else { + if (youseeit) + pline("%s lunges forward and recoils!", + Monnam(mtmp)); + else + You_hear("a %s nearby.", + is_whirly(mtmp->data) ? + "rushing noise" : "splat"); + } + } + break; + case AT_BREA: + if(range2) sum[i] = breamu(mtmp, mattk); + /* Note: breamu takes care of displacement */ + break; + case AT_SPIT: + if(range2) sum[i] = spitmu(mtmp, mattk); + /* Note: spitmu takes care of displacement */ + break; + case AT_WEAP: + if(range2) { +#ifdef REINCARNATION + if (!Is_rogue_level(&u.uz)) +#endif + thrwmu(mtmp); + } else { + int hittmp = 0; + + /* Rare but not impossible. Normally the monster + * wields when 2 spaces away, but it can be + * teleported or whatever.... + */ + if (mtmp->weapon_check == NEED_WEAPON || + !MON_WEP(mtmp)) { + mtmp->weapon_check = NEED_HTH_WEAPON; + /* mon_wield_item resets weapon_check as + * appropriate */ + if (mon_wield_item(mtmp) != 0) break; + } + if (foundyou) { + otmp = MON_WEP(mtmp); + if(otmp) { + hittmp = hitval(otmp, &youmonst); + tmp += hittmp; + mswings(mtmp, otmp); + } + if(tmp > (j = dieroll = rnd(20+i))) + sum[i] = hitmu(mtmp, mattk); + else + missmu(mtmp, (tmp == j), mattk); + /* KMH -- Don't accumulate to-hit bonuses */ + if (otmp) + tmp -= hittmp; + } else + wildmiss(mtmp, mattk); + } + break; + case AT_MAGC: + if (range2) + sum[i] = buzzmu(mtmp, mattk); + else { + if (foundyou) + sum[i] = castmu(mtmp, mattk, TRUE, TRUE); + else + sum[i] = castmu(mtmp, mattk, TRUE, FALSE); + } + break; + + default: /* no attack */ + break; + } + if(flags.botl) bot(); + /* give player a chance of waking up before dying -kaa */ + if(sum[i] == 1) { /* successful attack */ + if (u.usleep && u.usleep < monstermoves && !rn2(10)) { + multi = -1; + nomovemsg = "The combat suddenly awakens you."; + } + } + if(sum[i] == 2) return 1; /* attacker dead */ + if(sum[i] == 3) break; /* attacker teleported, no more attacks */ + /* sum[i] == 0: unsuccessful attack */ + } + return(0); +} + +#endif /* OVL0 */ +#ifdef OVLB + +/* + * helper function for some compilers that have trouble with hitmu + */ + +STATIC_OVL void +hurtarmor(attk) +int attk; +{ + int hurt; + + switch(attk) { + /* 0 is burning, which we should never be called with */ + case AD_RUST: hurt = 1; break; + case AD_CORR: hurt = 3; break; + default: hurt = 2; break; + } + + /* What the following code does: it keeps looping until it + * finds a target for the rust monster. + * Head, feet, etc... not covered by metal, or covered by + * rusty metal, are not targets. However, your body always + * is, no matter what covers it. + */ + while (1) { + switch(rn2(5)) { + case 0: + if (!uarmh || !rust_dmg(uarmh, xname(uarmh), hurt, FALSE, &youmonst)) + continue; + break; + case 1: + if (uarmc) { + (void)rust_dmg(uarmc, xname(uarmc), hurt, TRUE, &youmonst); + break; + } + /* Note the difference between break and continue; + * break means it was hit and didn't rust; continue + * means it wasn't a target and though it didn't rust + * something else did. + */ + if (uarm) + (void)rust_dmg(uarm, xname(uarm), hurt, TRUE, &youmonst); +#ifdef TOURIST + else if (uarmu) + (void)rust_dmg(uarmu, xname(uarmu), hurt, TRUE, &youmonst); +#endif + break; + case 2: + if (!uarms || !rust_dmg(uarms, xname(uarms), hurt, FALSE, &youmonst)) + continue; + break; + case 3: + if (!uarmg || !rust_dmg(uarmg, xname(uarmg), hurt, FALSE, &youmonst)) + continue; + break; + case 4: + if (!uarmf || !rust_dmg(uarmf, xname(uarmf), hurt, FALSE, &youmonst)) + continue; + break; + } + break; /* Out of while loop */ + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +STATIC_OVL boolean +diseasemu(mdat) +struct permonst *mdat; +{ + if (Sick_resistance) { + You_feel("a slight illness."); + return FALSE; + } else { + make_sick(Sick ? Sick/3L + 1L : (long)rn1(ACURR(A_CON), 20), + mdat->mname, TRUE, SICK_NONVOMITABLE); + return TRUE; + } +} + +/* check whether slippery clothing protects from hug or wrap attack */ +STATIC_OVL boolean +u_slip_free(mtmp, mattk) +struct monst *mtmp; +struct attack *mattk; +{ + struct obj *obj = (uarmc ? uarmc : uarm); + +#ifdef TOURIST + if (!obj) obj = uarmu; +#endif + if (mattk->adtyp == AD_DRIN) obj = uarmh; + + /* if your cloak/armor is greased, monster slips off; this + protection might fail (33% chance) when the armor is cursed */ + if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) && + (!obj->cursed || rn2(3))) { + pline("%s %s your %s %s!", + Monnam(mtmp), + (mattk->adtyp == AD_WRAP) ? + "slips off of" : "grabs you, but cannot hold onto", + obj->greased ? "greased" : "slippery", + /* avoid "slippery slippery cloak" + for undiscovered oilskin cloak */ + (obj->greased || objects[obj->otyp].oc_name_known) ? + xname(obj) : cloak_simple_name(obj)); + + if (obj->greased && !rn2(2)) { + pline_The("grease wears off."); + obj->greased = 0; + update_inventory(); + } + return TRUE; + } + return FALSE; +} + +/* armor that sufficiently covers the body might be able to block magic */ +int +magic_negation(mon) +struct monst *mon; +{ + struct obj *armor; + int armpro = 0; + + armor = (mon == &youmonst) ? uarm : which_armor(mon, W_ARM); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + armor = (mon == &youmonst) ? uarmc : which_armor(mon, W_ARMC); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + armor = (mon == &youmonst) ? uarmh : which_armor(mon, W_ARMH); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + + /* armor types for shirt, gloves, shoes, and shield don't currently + provide any magic cancellation but we might as well be complete */ +#ifdef TOURIST + armor = (mon == &youmonst) ? uarmu : which_armor(mon, W_ARMU); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; +#endif + armor = (mon == &youmonst) ? uarmg : which_armor(mon, W_ARMG); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + armor = (mon == &youmonst) ? uarmf : which_armor(mon, W_ARMF); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + armor = (mon == &youmonst) ? uarms : which_armor(mon, W_ARMS); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; + +#ifdef STEED + /* this one is really a stretch... */ + armor = (mon == &youmonst) ? 0 : which_armor(mon, W_SADDLE); + if (armor && armpro < objects[armor->otyp].a_can) + armpro = objects[armor->otyp].a_can; +#endif + + return armpro; +} + +/* + * hitmu: monster hits you + * returns 2 if monster dies (e.g. "yellow light"), 1 otherwise + * 3 if the monster lives but teleported/paralyzed, so it can't keep + * attacking you + */ +STATIC_OVL int +hitmu(mtmp, mattk) + register struct monst *mtmp; + register struct attack *mattk; +{ + register struct permonst *mdat = mtmp->data; + register int uncancelled, ptmp; + int dmg, armpro, permdmg; + char buf[BUFSZ]; + struct permonst *olduasmon = youmonst.data; + int res; + + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + +/* If the monster is undetected & hits you, you should know where + * the attack came from. + */ + if(mtmp->mundetected && (hides_under(mdat) || mdat->mlet == S_EEL)) { + mtmp->mundetected = 0; + if (!(Blind ? Blind_telepat : Unblind_telepat)) { + struct obj *obj; + const char *what; + + if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0) { + if (Blind && !obj->dknown) + what = something; + else if (is_pool(mtmp->mx, mtmp->my) && !Underwater) + what = "the water"; + else + what = doname(obj); + + pline("%s was hidden under %s!", Amonnam(mtmp), what); + } + newsym(mtmp->mx, mtmp->my); + } + } + +/* First determine the base damage done */ + dmg = d((int)mattk->damn, (int)mattk->damd); + if(is_undead(mdat) && midnight()) + dmg += d((int)mattk->damn, (int)mattk->damd); /* extra damage */ + +/* Next a cancellation factor */ +/* Use uncancelled when the cancellation factor takes into account certain + * armor's special magic protection. Otherwise just use !mtmp->mcan. + */ + armpro = magic_negation(&youmonst); + uncancelled = !mtmp->mcan && ((rn2(3) >= armpro) || !rn2(50)); + + permdmg = 0; +/* Now, adjust damages via resistances or specific attacks */ + switch(mattk->adtyp) { + case AD_PHYS: + if (mattk->aatyp == AT_HUGS && !sticks(youmonst.data)) { + if(!u.ustuck && rn2(2)) { + if (u_slip_free(mtmp, mattk)) { + dmg = 0; + } else { + u.ustuck = mtmp; + pline("%s grabs you!", Monnam(mtmp)); + } + } else if(u.ustuck == mtmp) { + exercise(A_STR, FALSE); + You("are being %s.", + (mtmp->data == &mons[PM_ROPE_GOLEM]) + ? "choked" : "crushed"); + } + } else { /* hand to hand weapon */ + if(mattk->aatyp == AT_WEAP && otmp) { + if (otmp->otyp == CORPSE + && touch_petrifies(&mons[otmp->corpsenm])) { + dmg = 1; + pline("%s hits you with the %s corpse.", + Monnam(mtmp), mons[otmp->corpsenm].mname); + if (!Stoned) + goto do_stone; + } + dmg += dmgval(otmp, &youmonst); + if (dmg <= 0) dmg = 1; + if (!(otmp->oartifact && + artifact_hit(mtmp, &youmonst, otmp, &dmg,dieroll))) + hitmsg(mtmp, mattk); + if (!dmg) break; + if (u.mh > 1 && u.mh > ((u.uac>0) ? dmg : dmg+u.uac) && + objects[otmp->otyp].oc_material == IRON && + (u.umonnum==PM_BLACK_PUDDING + || u.umonnum==PM_BROWN_PUDDING)) { + /* This redundancy necessary because you have to + * take the damage _before_ being cloned. + */ + if (u.uac < 0) dmg += u.uac; + if (dmg < 1) dmg = 1; + if (dmg > 1) exercise(A_STR, FALSE); + u.mh -= dmg; + flags.botl = 1; + dmg = 0; + if(cloneu()) + You("divide as %s hits you!",mon_nam(mtmp)); + } + urustm(mtmp, otmp); + } else if (mattk->aatyp != AT_TUCH || dmg != 0 || + mtmp != u.ustuck) + hitmsg(mtmp, mattk); + } + break; + case AD_DISE: + hitmsg(mtmp, mattk); + if (!diseasemu(mdat)) dmg = 0; + break; + case AD_FIRE: + hitmsg(mtmp, mattk); + if (uncancelled) { + pline("You're %s!", on_fire(youmonst.data, mattk)); + if (youmonst.data == &mons[PM_STRAW_GOLEM] || + youmonst.data == &mons[PM_PAPER_GOLEM]) { + You("roast!"); + /* KMH -- this is okay with unchanging */ + rehumanize(); + break; + } else if (Fire_resistance) { + pline_The("fire doesn't feel hot!"); + dmg = 0; + } + if((int) mtmp->m_lev > rn2(20)) + destroy_item(SCROLL_CLASS, AD_FIRE); + if((int) mtmp->m_lev > rn2(20)) + destroy_item(POTION_CLASS, AD_FIRE); + if((int) mtmp->m_lev > rn2(25)) + destroy_item(SPBOOK_CLASS, AD_FIRE); + burn_away_slime(); + } else dmg = 0; + break; + case AD_COLD: + hitmsg(mtmp, mattk); + if (uncancelled) { + pline("You're covered in frost!"); + if (Cold_resistance) { + pline_The("frost doesn't seem cold!"); + dmg = 0; + } + if((int) mtmp->m_lev > rn2(20)) + destroy_item(POTION_CLASS, AD_COLD); + } else dmg = 0; + break; + case AD_ELEC: + hitmsg(mtmp, mattk); + if (uncancelled) { + You("get zapped!"); + if (Shock_resistance) { + pline_The("zap doesn't shock you!"); + dmg = 0; + } + if((int) mtmp->m_lev > rn2(20)) + destroy_item(WAND_CLASS, AD_ELEC); + if((int) mtmp->m_lev > rn2(20)) + destroy_item(RING_CLASS, AD_ELEC); + } else dmg = 0; + break; + case AD_SLEE: + hitmsg(mtmp, mattk); + if (uncancelled && multi >= 0 && !rn2(5)) { + if (Sleep_resistance) break; + fall_asleep(-rnd(10), TRUE); + if (Blind) You("are put to sleep!"); + else You("are put to sleep by %s!", mon_nam(mtmp)); + } + break; + case AD_BLND: + if (can_blnd(mtmp, &youmonst, mattk->aatyp, (struct obj*)0)) { + if (!Blind) pline("%s blinds you!", Monnam(mtmp)); + make_blinded(Blinded+(long)dmg,FALSE); + if (!Blind) Your(vision_clears); + } + dmg = 0; + break; + case AD_DRST: + ptmp = A_STR; + goto dopois; + case AD_DRDX: + ptmp = A_DEX; + goto dopois; + case AD_DRCO: + ptmp = A_CON; +dopois: + hitmsg(mtmp, mattk); + if (uncancelled && !rn2(8)) { + Sprintf(buf, "%s %s", + s_suffix(Monnam(mtmp)), mpoisons_subj(mtmp, mattk)); + poisoned(buf, ptmp, mdat->mname, 30); + } + break; + case AD_DRIN: + hitmsg(mtmp, mattk); + if (defends(AD_DRIN, uwep) || !has_head(youmonst.data)) { + You("don't seem harmed."); + /* Not clear what to do for green slimes */ + break; + } + if (u_slip_free(mtmp,mattk)) break; + + if (uarmh && rn2(8)) { + /* not body_part(HEAD) */ + Your("helmet blocks the attack to your head."); + break; + } + if (Half_physical_damage) dmg = (dmg+1) / 2; + mdamageu(mtmp, dmg); + + if (!uarmh || uarmh->otyp != DUNCE_CAP) { + Your("brain is eaten!"); + /* No such thing as mindless players... */ + if (ABASE(A_INT) <= ATTRMIN(A_INT)) { + int lifesaved = 0; + struct obj *wore_amulet = uamul; + + while(1) { + /* avoid looping on "die(y/n)?" */ + if (lifesaved && (discover || wizard)) { + if (wore_amulet && !uamul) { + /* used up AMULET_OF_LIFE_SAVING; still + subject to dying from brainlessness */ + wore_amulet = 0; + } else { + /* explicitly chose not to die; + arbitrarily boost intelligence */ + ABASE(A_INT) = ATTRMIN(A_INT) + 2; + You_feel("like a scarecrow."); + break; + } + } + + if (lifesaved) + pline("Unfortunately your brain is still gone."); + else + Your("last thought fades away."); + killer = "brainlessness"; + killer_format = KILLED_BY; + done(DIED); + lifesaved++; + } + } + } + /* adjattrib gives dunce cap message when appropriate */ + (void) adjattrib(A_INT, -rnd(2), FALSE); + forget_levels(25); /* lose memory of 25% of levels */ + forget_objects(25); /* lose memory of 25% of objects */ + exercise(A_WIS, FALSE); + break; + case AD_PLYS: + hitmsg(mtmp, mattk); + if (uncancelled && multi >= 0 && !rn2(3)) { + if (Free_action) { + You("momentarily stiffen."); + } else { + if (Blind) You("are frozen!"); + else You("are frozen by %s!", mon_nam(mtmp)); + nomovemsg = 0; /* default: "you can move again" */ + nomul(-rnd(10)); + exercise(A_DEX, FALSE); + } + } + break; + case AD_DRLI: + hitmsg(mtmp, mattk); + if (uncancelled && !rn2(3) && !Drain_resistance) { + losexp("life drainage"); + } + break; + case AD_LEGS: + { register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE; + const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left"; + + /* This case is too obvious to ignore, but Nethack is not in + * general very good at considering height--most short monsters + * still _can_ attack you when you're flying or mounted. + * [FIXME: why can't a flying attacker overcome this?] + */ + if ( +#ifdef STEED + u.usteed || +#endif + Levitation || Flying) { + pline("%s tries to reach your %s %s!", Monnam(mtmp), + sidestr, body_part(LEG)); + dmg = 0; + } else if (mtmp->mcan) { + pline("%s nuzzles against your %s %s!", Monnam(mtmp), + sidestr, body_part(LEG)); + dmg = 0; + } else { + if (uarmf) { + if (rn2(2) && (uarmf->otyp == LOW_BOOTS || + uarmf->otyp == IRON_SHOES)) + pline("%s pricks the exposed part of your %s %s!", + Monnam(mtmp), sidestr, body_part(LEG)); + else if (!rn2(5)) + pline("%s pricks through your %s boot!", + Monnam(mtmp), sidestr); + else { + pline("%s scratches your %s boot!", Monnam(mtmp), + sidestr); + dmg = 0; + break; + } + } else pline("%s pricks your %s %s!", Monnam(mtmp), + sidestr, body_part(LEG)); + set_wounded_legs(side, rnd(60-ACURR(A_DEX))); + exercise(A_STR, FALSE); + exercise(A_DEX, FALSE); + } + break; + } + case AD_STON: /* cockatrice */ + hitmsg(mtmp, mattk); + if(!rn2(3)) { + if (mtmp->mcan) { + if (flags.soundok) + You_hear("a cough from %s!", mon_nam(mtmp)); + } else { + if (flags.soundok) + You_hear("%s hissing!", s_suffix(mon_nam(mtmp))); + if(!rn2(10) || + (flags.moonphase == NEW_MOON && !have_lizard())) { + do_stone: + if (!Stoned && !Stone_resistance + && !(poly_when_stoned(youmonst.data) && + polymon(PM_STONE_GOLEM))) { + Stoned = 5; + delayed_killer = mtmp->data->mname; + if (mtmp->data->geno & G_UNIQ) { + if (!type_is_pname(mtmp->data)) { + static char kbuf[BUFSZ]; + + /* "the" buffer may be reallocated */ + Strcpy(kbuf, the(delayed_killer)); + delayed_killer = kbuf; + } + killer_format = KILLED_BY; + } else killer_format = KILLED_BY_AN; + return(1); + /* You("turn to stone..."); */ + /* done_in_by(mtmp); */ + } + } + } + } + break; + case AD_STCK: + hitmsg(mtmp, mattk); + if (uncancelled && !u.ustuck && !sticks(youmonst.data)) + u.ustuck = mtmp; + break; + case AD_WRAP: + if ((!mtmp->mcan || u.ustuck == mtmp) && !sticks(youmonst.data)) { + if (!u.ustuck && !rn2(10)) { + if (u_slip_free(mtmp, mattk)) { + dmg = 0; + } else { + pline("%s swings itself around you!", + Monnam(mtmp)); + u.ustuck = mtmp; + } + } else if(u.ustuck == mtmp) { + if (is_pool(mtmp->mx,mtmp->my) && !Swimming + && !Amphibious) { + boolean moat = + (levl[mtmp->mx][mtmp->my].typ != POOL) && + (levl[mtmp->mx][mtmp->my].typ != WATER) && + !Is_medusa_level(&u.uz) && + !Is_waterlevel(&u.uz); + + pline("%s drowns you...", Monnam(mtmp)); + killer_format = KILLED_BY_AN; + Sprintf(buf, "%s by %s", + moat ? "moat" : "pool of water", + an(mtmp->data->mname)); + killer = buf; + done(DROWNING); + } else if(mattk->aatyp == AT_HUGS) + You("are being crushed."); + } else { + dmg = 0; + if(flags.verbose) + pline("%s brushes against your %s.", Monnam(mtmp), + body_part(LEG)); + } + } else dmg = 0; + break; + case AD_WERE: + hitmsg(mtmp, mattk); + if (uncancelled && !rn2(4) && u.ulycn == NON_PM && + !Protection_from_shape_changers && + !defends(AD_WERE,uwep)) { + You_feel("feverish."); + exercise(A_CON, FALSE); + u.ulycn = monsndx(mdat); + } + break; + case AD_SGLD: + hitmsg(mtmp, mattk); + if (youmonst.data->mlet == mdat->mlet) break; + if(!mtmp->mcan) stealgold(mtmp); + break; + + case AD_SITM: /* for now these are the same */ + case AD_SEDU: + if (is_animal(mtmp->data)) { + hitmsg(mtmp, mattk); + if (mtmp->mcan) break; + /* Continue below */ + } else if (dmgtype(youmonst.data, AD_SEDU) +#ifdef SEDUCE + || dmgtype(youmonst.data, AD_SSEX) +#endif + ) { + pline("%s %s.", Monnam(mtmp), mtmp->minvent ? + "brags about the goods some dungeon explorer provided" : + "makes some remarks about how difficult theft is lately"); + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + return 3; + } else if (mtmp->mcan) { + if (!Blind) { + pline("%s tries to %s you, but you seem %s.", + Adjmonnam(mtmp, "plain"), + flags.female ? "charm" : "seduce", + flags.female ? "unaffected" : "uninterested"); + } + if(rn2(3)) { + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + return 3; + } + break; + } + buf[0] = '\0'; + switch (steal(mtmp, buf)) { + case -1: + return 2; + case 0: + break; + default: + if (!is_animal(mtmp->data) && !tele_restrict(mtmp)) + (void) rloc(mtmp, FALSE); + if (is_animal(mtmp->data) && *buf) { + if (canseemon(mtmp)) + pline("%s tries to %s away with %s.", + Monnam(mtmp), + locomotion(mtmp->data, "run"), + buf); + } + monflee(mtmp, 0, FALSE, FALSE); + return 3; + } + break; +#ifdef SEDUCE + case AD_SSEX: + if(could_seduce(mtmp, &youmonst, mattk) == 1 + && !mtmp->mcan) + if (doseduce(mtmp)) + return 3; + break; +#endif + case AD_SAMU: + hitmsg(mtmp, mattk); + /* when the Wiz hits, 1/20 steals the amulet */ + if (u.uhave.amulet || + u.uhave.bell || u.uhave.book || u.uhave.menorah + || u.uhave.questart) /* carrying the Quest Artifact */ + if (!rn2(20)) stealamulet(mtmp); + break; + + case AD_TLPT: + hitmsg(mtmp, mattk); + if (uncancelled) { + if(flags.verbose) + Your("position suddenly seems very uncertain!"); + tele(); + } + break; + case AD_RUST: + hitmsg(mtmp, mattk); + if (mtmp->mcan) break; + if (u.umonnum == PM_IRON_GOLEM) { + You("rust!"); + /* KMH -- this is okay with unchanging */ + rehumanize(); + break; + } + hurtarmor(AD_RUST); + break; + case AD_CORR: + hitmsg(mtmp, mattk); + if (mtmp->mcan) break; + hurtarmor(AD_CORR); + break; + case AD_DCAY: + hitmsg(mtmp, mattk); + if (mtmp->mcan) break; + if (u.umonnum == PM_WOOD_GOLEM || + u.umonnum == PM_LEATHER_GOLEM) { + You("rot!"); + /* KMH -- this is okay with unchanging */ + rehumanize(); + break; + } + hurtarmor(AD_DCAY); + break; + case AD_HEAL: + /* a cancelled nurse is just an ordinary monster */ + if (mtmp->mcan) { + hitmsg(mtmp, mattk); + break; + } + if(!uwep +#ifdef TOURIST + && !uarmu +#endif + && !uarm && !uarmh && !uarms && !uarmg && !uarmc && !uarmf) { + boolean goaway = FALSE; + pline("%s hits! (I hope you don't mind.)", Monnam(mtmp)); + if (Upolyd) { + u.mh += rnd(7); + if (!rn2(7)) { + /* no upper limit necessary; effect is temporary */ + u.mhmax++; + if (!rn2(13)) goaway = TRUE; + } + if (u.mh > u.mhmax) u.mh = u.mhmax; + } else { + u.uhp += rnd(7); + if (!rn2(7)) { + /* hard upper limit via nurse care: 25 * ulevel */ + if (u.uhpmax < 5 * u.ulevel + d(2 * u.ulevel, 10)) + u.uhpmax++; + if (!rn2(13)) goaway = TRUE; + } + if (u.uhp > u.uhpmax) u.uhp = u.uhpmax; + } + if (!rn2(3)) exercise(A_STR, TRUE); + if (!rn2(3)) exercise(A_CON, TRUE); + if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL); + flags.botl = 1; + if (goaway) { + mongone(mtmp); + return 2; + } else if (!rn2(33)) { + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + monflee(mtmp, d(3, 6), TRUE, FALSE); + return 3; + } + dmg = 0; + } else { + if (Role_if(PM_HEALER)) { + if (flags.soundok && !(moves % 5)) + verbalize("Doc, I can't help you unless you cooperate."); + dmg = 0; + } else hitmsg(mtmp, mattk); + } + break; + case AD_CURS: + hitmsg(mtmp, mattk); + if(!night() && mdat == &mons[PM_GREMLIN]) break; + if(!mtmp->mcan && !rn2(10)) { + if (flags.soundok) { + if (Blind) You_hear("laughter."); + else pline("%s chuckles.", Monnam(mtmp)); + } + if (u.umonnum == PM_CLAY_GOLEM) { + pline("Some writing vanishes from your head!"); + /* KMH -- this is okay with unchanging */ + rehumanize(); + break; + } + attrcurse(); + } + break; + case AD_STUN: + hitmsg(mtmp, mattk); + if(!mtmp->mcan && !rn2(4)) { + make_stunned(HStun + dmg, TRUE); + dmg /= 2; + } + break; + case AD_ACID: + hitmsg(mtmp, mattk); + if (!mtmp->mcan && !rn2(3)) + if (Acid_resistance) { + pline("You're covered in acid, but it seems harmless."); + dmg = 0; + } else { + pline("You're covered in acid! It burns!"); + exercise(A_STR, FALSE); + } + else dmg = 0; + break; + case AD_SLOW: + hitmsg(mtmp, mattk); + if (uncancelled && HFast && + !defends(AD_SLOW, uwep) && !rn2(4)) + u_slow_down(); + break; + case AD_DREN: + hitmsg(mtmp, mattk); + if (uncancelled && !rn2(4)) + drain_en(dmg); + dmg = 0; + break; + case AD_CONF: + hitmsg(mtmp, mattk); + if(!mtmp->mcan && !rn2(4) && !mtmp->mspec_used) { + mtmp->mspec_used = mtmp->mspec_used + (dmg + rn2(6)); + if(Confusion) + You("are getting even more confused."); + else You("are getting confused."); + make_confused(HConfusion + dmg, FALSE); + } + dmg = 0; + break; + case AD_DETH: + pline("%s reaches out with its deadly touch.", Monnam(mtmp)); + if (is_undead(youmonst.data)) { + /* Still does normal damage */ + pline("Was that the touch of death?"); + break; + } + switch (rn2(20)) { + case 19: case 18: case 17: + if (!Antimagic) { + killer_format = KILLED_BY_AN; + killer = "touch of death"; + done(DIED); + dmg = 0; + break; + } /* else FALLTHRU */ + default: /* case 16: ... case 5: */ + You_feel("your life force draining away..."); + permdmg = 1; /* actual damage done below */ + break; + case 4: case 3: case 2: case 1: case 0: + if (Antimagic) shieldeff(u.ux, u.uy); + pline("Lucky for you, it didn't work!"); + dmg = 0; + break; + } + break; + case AD_PEST: + pline("%s reaches out, and you feel fever and chills.", + Monnam(mtmp)); + (void) diseasemu(mdat); /* plus the normal damage */ + break; + case AD_FAMN: + pline("%s reaches out, and your body shrivels.", + Monnam(mtmp)); + exercise(A_CON, FALSE); + if (!is_fainted()) morehungry(rn1(40,40)); + /* plus the normal damage */ + break; + case AD_SLIM: + hitmsg(mtmp, mattk); + if (!uncancelled) break; + if (flaming(youmonst.data)) { + pline_The("slime burns away!"); + dmg = 0; + } else if (Unchanging || + youmonst.data == &mons[PM_GREEN_SLIME]) { + You("are unaffected."); + dmg = 0; + } else if (!Slimed) { + You("don't feel very well."); + Slimed = 10L; + flags.botl = 1; + killer_format = KILLED_BY_AN; + delayed_killer = mtmp->data->mname; + } else + pline("Yuck!"); + break; + case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ + hitmsg(mtmp, mattk); + /* uncancelled is sufficient enough; please + don't make this attack less frequent */ + if (uncancelled) { + struct obj *obj = some_armor(&youmonst); + + if (drain_item(obj)) { + Your("%s less effective.", aobjnam(obj, "seem")); + } + } + break; + default: dmg = 0; + break; + } + if(u.uhp < 1) done_in_by(mtmp); + +/* Negative armor class reduces damage done instead of fully protecting + * against hits. + */ + if (dmg && u.uac < 0) { + dmg -= rnd(-u.uac); + if (dmg < 1) dmg = 1; + } + + if(dmg) { + if (Half_physical_damage + /* Mitre of Holiness */ + || (Role_if(PM_PRIEST) && uarmh && is_quest_artifact(uarmh) && + (is_undead(mtmp->data) || is_demon(mtmp->data)))) + dmg = (dmg+1) / 2; + + if (permdmg) { /* Death's life force drain */ + int lowerlimit, *hpmax_p; + /* + * Apply some of the damage to permanent hit points: + * polymorphed 100% against poly'd hpmax + * hpmax > 25*lvl 100% against normal hpmax + * hpmax > 10*lvl 50..100% + * hpmax > 5*lvl 25..75% + * otherwise 0..50% + * Never reduces hpmax below 1 hit point per level. + */ + permdmg = rn2(dmg / 2 + 1); + if (Upolyd || u.uhpmax > 25 * u.ulevel) permdmg = dmg; + else if (u.uhpmax > 10 * u.ulevel) permdmg += dmg / 2; + else if (u.uhpmax > 5 * u.ulevel) permdmg += dmg / 4; + + if (Upolyd) { + hpmax_p = &u.mhmax; + /* [can't use youmonst.m_lev] */ + lowerlimit = min((int)youmonst.data->mlevel, u.ulevel); + } else { + hpmax_p = &u.uhpmax; + lowerlimit = u.ulevel; + } + if (*hpmax_p - permdmg > lowerlimit) + *hpmax_p -= permdmg; + else if (*hpmax_p > lowerlimit) + *hpmax_p = lowerlimit; + else /* unlikely... */ + ; /* already at or below minimum threshold; do nothing */ + flags.botl = 1; + } + + mdamageu(mtmp, dmg); + } + + if (dmg) + res = passiveum(olduasmon, mtmp, mattk); + else + res = 1; + stop_occupation(); + return res; +} + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_OVL int +gulpmu(mtmp, mattk) /* monster swallows you, or damage if u.uswallow */ + register struct monst *mtmp; + register struct attack *mattk; +{ + struct trap *t = t_at(u.ux, u.uy); + int tmp = d((int)mattk->damn, (int)mattk->damd); + int tim_tmp; + register struct obj *otmp2; + int i; + + if (!u.uswallow) { /* swallows you */ + if (youmonst.data->msize >= MZ_HUGE) return(0); + if ((t && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT))) && + sobj_at(BOULDER, u.ux, u.uy)) + return(0); + + if (Punished) unplacebc(); /* ball&chain go away */ + remove_monster(mtmp->mx, mtmp->my); + mtmp->mtrapped = 0; /* no longer on old trap */ + place_monster(mtmp, u.ux, u.uy); + u.ustuck = mtmp; + newsym(mtmp->mx,mtmp->my); +#ifdef STEED + if (is_animal(mtmp->data) && u.usteed) { + char buf[BUFSZ]; + /* Too many quirks presently if hero and steed + * are swallowed. Pretend purple worms don't + * like horses for now :-) + */ + Strcpy(buf, mon_nam(u.usteed)); + pline ("%s lunges forward and plucks you off %s!", + Monnam(mtmp), buf); + dismount_steed(DISMOUNT_ENGULFED); + } else +#endif + pline("%s engulfs you!", Monnam(mtmp)); + stop_occupation(); + reset_occupations(); /* behave as if you had moved */ + + if (u.utrap) { + You("are released from the %s!", + u.utraptype==TT_WEB ? "web" : "trap"); + u.utrap = 0; + } + + i = number_leashed(); + if (i > 0) { + const char *s = (i > 1) ? "leashes" : "leash"; + pline_The("%s %s loose.", s, vtense(s, "snap")); + unleash_all(); + } + + if (touch_petrifies(youmonst.data) && !resists_ston(mtmp)) { + minstapetrify(mtmp, TRUE); + if (mtmp->mhp > 0) return 0; + else return 2; + } + + display_nhwindow(WIN_MESSAGE, FALSE); + vision_recalc(2); /* hero can't see anything */ + u.uswallow = 1; + /* u.uswldtim always set > 1 */ + tim_tmp = 25 - (int)mtmp->m_lev; + if (tim_tmp > 0) tim_tmp = rnd(tim_tmp) / 2; + else if (tim_tmp < 0) tim_tmp = -(rnd(-tim_tmp) / 2); + tim_tmp += -u.uac + 10; + u.uswldtim = (unsigned)((tim_tmp < 2) ? 2 : tim_tmp); + swallowed(1); + for (otmp2 = invent; otmp2; otmp2 = otmp2->nobj) + (void) snuff_lit(otmp2); + } + + if (mtmp != u.ustuck) return(0); + if (u.uswldtim > 0) u.uswldtim -= 1; + + switch(mattk->adtyp) { + + case AD_DGST: + if (Slow_digestion) { + /* Messages are handled below */ + u.uswldtim = 0; + tmp = 0; + } else if (u.uswldtim == 0) { + pline("%s totally digests you!", Monnam(mtmp)); + tmp = u.uhp; + if (Half_physical_damage) tmp *= 2; /* sorry */ + } else { + pline("%s%s digests you!", Monnam(mtmp), + (u.uswldtim == 2) ? " thoroughly" : + (u.uswldtim == 1) ? " utterly" : ""); + exercise(A_STR, FALSE); + } + break; + case AD_PHYS: + if (mtmp->data == &mons[PM_FOG_CLOUD]) { + You("are laden with moisture and %s", + flaming(youmonst.data) ? "are smoldering out!" : + Breathless ? "find it mildly uncomfortable." : + amphibious(youmonst.data) ? "feel comforted." : + "can barely breathe!"); + /* NB: Amphibious includes Breathless */ + if (Amphibious && !flaming(youmonst.data)) tmp = 0; + } else { + You("are pummeled with debris!"); + exercise(A_STR, FALSE); + } + break; + case AD_ACID: + if (Acid_resistance) { + You("are covered with a seemingly harmless goo."); + tmp = 0; + } else { + if (Hallucination) pline("Ouch! You've been slimed!"); + else You("are covered in slime! It burns!"); + exercise(A_STR, FALSE); + } + break; + case AD_BLND: + if (can_blnd(mtmp, &youmonst, mattk->aatyp, (struct obj*)0)) { + if(!Blind) { + You_cant("see in here!"); + make_blinded((long)tmp,FALSE); + if (!Blind) Your(vision_clears); + } else + /* keep him blind until disgorged */ + make_blinded(Blinded+1,FALSE); + } + tmp = 0; + break; + case AD_ELEC: + if(!mtmp->mcan && rn2(2)) { + pline_The("air around you crackles with electricity."); + if (Shock_resistance) { + shieldeff(u.ux, u.uy); + You("seem unhurt."); + ugolemeffects(AD_ELEC,tmp); + tmp = 0; + } + } else tmp = 0; + break; + case AD_COLD: + if(!mtmp->mcan && rn2(2)) { + if (Cold_resistance) { + shieldeff(u.ux, u.uy); + You_feel("mildly chilly."); + ugolemeffects(AD_COLD,tmp); + tmp = 0; + } else You("are freezing to death!"); + } else tmp = 0; + break; + case AD_FIRE: + if(!mtmp->mcan && rn2(2)) { + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + You_feel("mildly hot."); + ugolemeffects(AD_FIRE,tmp); + tmp = 0; + } else You("are burning to a crisp!"); + burn_away_slime(); + } else tmp = 0; + break; + case AD_DISE: + if (!diseasemu(mtmp->data)) tmp = 0; + break; + default: + tmp = 0; + break; + } + + if (Half_physical_damage) tmp = (tmp+1) / 2; + + mdamageu(mtmp, tmp); + if (tmp) stop_occupation(); + + if (touch_petrifies(youmonst.data) && !resists_ston(mtmp)) { + pline("%s very hurriedly %s you!", Monnam(mtmp), + is_animal(mtmp->data)? "regurgitates" : "expels"); + expels(mtmp, mtmp->data, FALSE); + } else if (!u.uswldtim || youmonst.data->msize >= MZ_HUGE) { + You("get %s!", is_animal(mtmp->data)? "regurgitated" : "expelled"); + if (flags.verbose && (is_animal(mtmp->data) || + (dmgtype(mtmp->data, AD_DGST) && Slow_digestion))) + pline("Obviously %s doesn't like your taste.", mon_nam(mtmp)); + expels(mtmp, mtmp->data, FALSE); + } + return(1); +} + +STATIC_OVL int +explmu(mtmp, mattk, ufound) /* monster explodes in your face */ +register struct monst *mtmp; +register struct attack *mattk; +boolean ufound; +{ + if (mtmp->mcan) return(0); + + if (!ufound) + pline("%s explodes at a spot in %s!", + canseemon(mtmp) ? Monnam(mtmp) : "It", + levl[mtmp->mux][mtmp->muy].typ == WATER + ? "empty water" : "thin air"); + else { + register int tmp = d((int)mattk->damn, (int)mattk->damd); + register boolean not_affected = defends((int)mattk->adtyp, uwep); + + hitmsg(mtmp, mattk); + + switch (mattk->adtyp) { + case AD_COLD: + not_affected |= Cold_resistance; + goto common; + case AD_FIRE: + not_affected |= Fire_resistance; + goto common; + case AD_ELEC: + not_affected |= Shock_resistance; +common: + + if (!not_affected) { + if (ACURR(A_DEX) > rnd(20)) { + You("duck some of the blast."); + tmp = (tmp+1) / 2; + } else { + if (flags.verbose) You("get blasted!"); + } + if (mattk->adtyp == AD_FIRE) burn_away_slime(); + if (Half_physical_damage) tmp = (tmp+1) / 2; + mdamageu(mtmp, tmp); + } + break; + + case AD_BLND: + not_affected = resists_blnd(&youmonst); + if (!not_affected) { + /* sometimes you're affected even if it's invisible */ + if (mon_visible(mtmp) || (rnd(tmp /= 2) > u.ulevel)) { + You("are blinded by a blast of light!"); + make_blinded((long)tmp, FALSE); + if (!Blind) Your(vision_clears); + } else if (flags.verbose) + You("get the impression it was not terribly bright."); + } + break; + + case AD_HALU: + not_affected |= Blind || + (u.umonnum == PM_BLACK_LIGHT || + u.umonnum == PM_VIOLET_FUNGUS || + dmgtype(youmonst.data, AD_STUN)); + if (!not_affected) { + boolean chg; + if (!Hallucination) + You("are caught in a blast of kaleidoscopic light!"); + chg = make_hallucinated(HHallucination + (long)tmp,FALSE,0L); + You("%s.", chg ? "are freaked out" : "seem unaffected"); + } + break; + + default: + break; + } + if (not_affected) { + You("seem unaffected by it."); + ugolemeffects((int)mattk->adtyp, tmp); + } + } + mondead(mtmp); + wake_nearto(mtmp->mx, mtmp->my, 7*7); + if (mtmp->mhp > 0) return(0); + return(2); /* it dies */ +} + +int +gazemu(mtmp, mattk) /* monster gazes at you */ + register struct monst *mtmp; + register struct attack *mattk; +{ + switch(mattk->adtyp) { + case AD_STON: + if (mtmp->mcan || !mtmp->mcansee) { + if (!canseemon(mtmp)) break; /* silently */ + pline("%s %s.", Monnam(mtmp), + (mtmp->data == &mons[PM_MEDUSA] && mtmp->mcan) ? + "doesn't look all that ugly" : + "gazes ineffectually"); + break; + } + if (Reflecting && couldsee(mtmp->mx, mtmp->my) && + mtmp->data == &mons[PM_MEDUSA]) { + /* hero has line of sight to Medusa and she's not blind */ + boolean useeit = canseemon(mtmp); + + if (useeit) + (void) ureflects("%s gaze is reflected by your %s.", + s_suffix(Monnam(mtmp))); + if (mon_reflects(mtmp, !useeit ? (char *)0 : + "The gaze is reflected away by %s %s!")) + break; + if (!m_canseeu(mtmp)) { /* probably you're invisible */ + if (useeit) + pline( + "%s doesn't seem to notice that %s gaze was reflected.", + Monnam(mtmp), mhis(mtmp)); + break; + } + if (useeit) + pline("%s is turned to stone!", Monnam(mtmp)); + stoned = TRUE; + killed(mtmp); + + if (mtmp->mhp > 0) break; + return 2; + } + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my) && + !Stone_resistance) { + You("meet %s gaze.", s_suffix(mon_nam(mtmp))); + stop_occupation(); + if(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + break; + You("turn to stone..."); + killer_format = KILLED_BY; + killer = mtmp->data->mname; + done(STONING); + } + break; + case AD_CONF: + if(!mtmp->mcan && canseemon(mtmp) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { + int conf = d(3,4); + + mtmp->mspec_used = mtmp->mspec_used + (conf + rn2(6)); + if(!Confusion) + pline("%s gaze confuses you!", + s_suffix(Monnam(mtmp))); + else + You("are getting more and more confused."); + make_confused(HConfusion + conf, FALSE); + stop_occupation(); + } + break; + case AD_STUN: + if(!mtmp->mcan && canseemon(mtmp) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { + int stun = d(2,6); + + mtmp->mspec_used = mtmp->mspec_used + (stun + rn2(6)); + pline("%s stares piercingly at you!", Monnam(mtmp)); + make_stunned(HStun + stun, TRUE); + stop_occupation(); + } + break; + case AD_BLND: + if (!mtmp->mcan && canseemon(mtmp) && !resists_blnd(&youmonst) + && distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) { + int blnd = d((int)mattk->damn, (int)mattk->damd); + + You("are blinded by %s radiance!", + s_suffix(mon_nam(mtmp))); + make_blinded((long)blnd,FALSE); + stop_occupation(); + /* not blind at this point implies you're wearing + the Eyes of the Overworld; make them block this + particular stun attack too */ + if (!Blind) Your(vision_clears); + else make_stunned((long)d(1,3),TRUE); + } + break; + case AD_FIRE: + if (!mtmp->mcan && canseemon(mtmp) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mcansee && !mtmp->mspec_used && rn2(5)) { + int dmg = d(2,6); + + pline("%s attacks you with a fiery gaze!", Monnam(mtmp)); + stop_occupation(); + if (Fire_resistance) { + pline_The("fire doesn't feel hot!"); + dmg = 0; + } + burn_away_slime(); + if ((int) mtmp->m_lev > rn2(20)) + destroy_item(SCROLL_CLASS, AD_FIRE); + if ((int) mtmp->m_lev > rn2(20)) + destroy_item(POTION_CLASS, AD_FIRE); + if ((int) mtmp->m_lev > rn2(25)) + destroy_item(SPBOOK_CLASS, AD_FIRE); + if (dmg) mdamageu(mtmp, dmg); + } + break; +#ifdef PM_BEHOLDER /* work in progress */ + case AD_SLEE: + if(!mtmp->mcan && canseemon(mtmp) && + couldsee(mtmp->mx, mtmp->my) && mtmp->mcansee && + multi >= 0 && !rn2(5) && !Sleep_resistance) { + + fall_asleep(-rnd(10), TRUE); + pline("%s gaze makes you very sleepy...", + s_suffix(Monnam(mtmp))); + } + break; + case AD_SLOW: + if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee && + (HFast & (INTRINSIC|TIMEOUT)) && + !defends(AD_SLOW, uwep) && !rn2(4)) + + u_slow_down(); + stop_occupation(); + break; +#endif + default: impossible("Gaze attack %d?", mattk->adtyp); + break; + } + return(0); +} + +#endif /* OVLB */ +#ifdef OVL1 + +void +mdamageu(mtmp, n) /* mtmp hits you for n points damage */ +register struct monst *mtmp; +register int n; +{ + flags.botl = 1; + if (Upolyd) { + u.mh -= n; + if (u.mh < 1) rehumanize(); + } else { + u.uhp -= n; + if(u.uhp < 1) done_in_by(mtmp); + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +STATIC_OVL void +urustm(mon, obj) +register struct monst *mon; +register struct obj *obj; +{ + boolean vis; + boolean is_acid; + + if (!mon || !obj) return; /* just in case */ + if (dmgtype(youmonst.data, AD_CORR)) + is_acid = TRUE; + else if (dmgtype(youmonst.data, AD_RUST)) + is_acid = FALSE; + else + return; + + vis = cansee(mon->mx, mon->my); + + if ((is_acid ? is_corrodeable(obj) : is_rustprone(obj)) && + (is_acid ? obj->oeroded2 : obj->oeroded) < MAX_ERODE) { + if (obj->greased || obj->oerodeproof || (obj->blessed && rn2(3))) { + if (vis) + pline("Somehow, %s weapon is not affected.", + s_suffix(mon_nam(mon))); + if (obj->greased && !rn2(2)) obj->greased = 0; + } else { + if (vis) + pline("%s %s%s!", + s_suffix(Monnam(mon)), + aobjnam(obj, (is_acid ? "corrode" : "rust")), + (is_acid ? obj->oeroded2 : obj->oeroded) + ? " further" : ""); + if (is_acid) obj->oeroded2++; + else obj->oeroded++; + } + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +int +could_seduce(magr,mdef,mattk) +struct monst *magr, *mdef; +struct attack *mattk; +/* returns 0 if seduction impossible, + * 1 if fine, + * 2 if wrong gender for nymph */ +{ + register struct permonst *pagr; + boolean agrinvis, defperc; + xchar genagr, gendef; + + if (is_animal(magr->data)) return (0); + if(magr == &youmonst) { + pagr = youmonst.data; + agrinvis = (Invis != 0); + genagr = poly_gender(); + } else { + pagr = magr->data; + agrinvis = magr->minvis; + genagr = gender(magr); + } + if(mdef == &youmonst) { + defperc = (See_invisible != 0); + gendef = poly_gender(); + } else { + defperc = perceives(mdef->data); + gendef = gender(mdef); + } + + if(agrinvis && !defperc +#ifdef SEDUCE + && mattk && mattk->adtyp != AD_SSEX +#endif + ) + return 0; + + if(pagr->mlet != S_NYMPH + && ((pagr != &mons[PM_INCUBUS] && pagr != &mons[PM_SUCCUBUS]) +#ifdef SEDUCE + || (mattk && mattk->adtyp != AD_SSEX) +#endif + )) + return 0; + + if(genagr == 1 - gendef) + return 1; + else + return (pagr->mlet == S_NYMPH) ? 2 : 0; +} + +#endif /* OVL1 */ +#ifdef OVLB + +#ifdef SEDUCE +/* Returns 1 if monster teleported */ +int +doseduce(mon) +register struct monst *mon; +{ + register struct obj *ring, *nring; + boolean fem = (mon->data == &mons[PM_SUCCUBUS]); /* otherwise incubus */ + char qbuf[QBUFSZ]; + + if (mon->mcan || mon->mspec_used) { + pline("%s acts as though %s has got a %sheadache.", + Monnam(mon), mhe(mon), + mon->mcan ? "severe " : ""); + return 0; + } + + if (unconscious()) { + pline("%s seems dismayed at your lack of response.", + Monnam(mon)); + return 0; + } + + if (Blind) pline("It caresses you..."); + else You_feel("very attracted to %s.", mon_nam(mon)); + + for(ring = invent; ring; ring = nring) { + nring = ring->nobj; + if (ring->otyp != RIN_ADORNMENT) continue; + if (fem) { + if (rn2(20) < ACURR(A_CHA)) { + Sprintf(qbuf, "\"That %s looks pretty. May I have it?\"", + safe_qbuf("",sizeof("\"That looks pretty. May I have it?\""), + xname(ring), simple_typename(ring->otyp), "ring")); + makeknown(RIN_ADORNMENT); + if (yn(qbuf) == 'n') continue; + } else pline("%s decides she'd like your %s, and takes it.", + Blind ? "She" : Monnam(mon), xname(ring)); + makeknown(RIN_ADORNMENT); + if (ring==uleft || ring==uright) Ring_gone(ring); + if (ring==uwep) setuwep((struct obj *)0); + if (ring==uswapwep) setuswapwep((struct obj *)0); + if (ring==uquiver) setuqwep((struct obj *)0); + freeinv(ring); + (void) mpickobj(mon,ring); + } else { + char buf[BUFSZ]; + + if (uleft && uright && uleft->otyp == RIN_ADORNMENT + && uright->otyp==RIN_ADORNMENT) + break; + if (ring==uleft || ring==uright) continue; + if (rn2(20) < ACURR(A_CHA)) { + Sprintf(qbuf,"\"That %s looks pretty. Would you wear it for me?\"", + safe_qbuf("", + sizeof("\"That looks pretty. Would you wear it for me?\""), + xname(ring), simple_typename(ring->otyp), "ring")); + makeknown(RIN_ADORNMENT); + if (yn(qbuf) == 'n') continue; + } else { + pline("%s decides you'd look prettier wearing your %s,", + Blind ? "He" : Monnam(mon), xname(ring)); + pline("and puts it on your finger."); + } + makeknown(RIN_ADORNMENT); + if (!uright) { + pline("%s puts %s on your right %s.", + Blind ? "He" : Monnam(mon), the(xname(ring)), body_part(HAND)); + setworn(ring, RIGHT_RING); + } else if (!uleft) { + pline("%s puts %s on your left %s.", + Blind ? "He" : Monnam(mon), the(xname(ring)), body_part(HAND)); + setworn(ring, LEFT_RING); + } else if (uright && uright->otyp != RIN_ADORNMENT) { + Strcpy(buf, xname(uright)); + pline("%s replaces your %s with your %s.", + Blind ? "He" : Monnam(mon), buf, xname(ring)); + Ring_gone(uright); + setworn(ring, RIGHT_RING); + } else if (uleft && uleft->otyp != RIN_ADORNMENT) { + Strcpy(buf, xname(uleft)); + pline("%s replaces your %s with your %s.", + Blind ? "He" : Monnam(mon), buf, xname(ring)); + Ring_gone(uleft); + setworn(ring, LEFT_RING); + } else impossible("ring replacement"); + Ring_on(ring); + prinv((char *)0, ring, 0L); + } + } + + if (!uarmc && !uarmf && !uarmg && !uarms && !uarmh +#ifdef TOURIST + && !uarmu +#endif + ) + pline("%s murmurs sweet nothings into your ear.", + Blind ? (fem ? "She" : "He") : Monnam(mon)); + else + pline("%s murmurs in your ear, while helping you undress.", + Blind ? (fem ? "She" : "He") : Monnam(mon)); + mayberem(uarmc, cloak_simple_name(uarmc)); + if(!uarmc) + mayberem(uarm, "suit"); + mayberem(uarmf, "boots"); + if(!uwep || !welded(uwep)) + mayberem(uarmg, "gloves"); + mayberem(uarms, "shield"); + mayberem(uarmh, "helmet"); +#ifdef TOURIST + if(!uarmc && !uarm) + mayberem(uarmu, "shirt"); +#endif + + if (uarm || uarmc) { + verbalize("You're such a %s; I wish...", + flags.female ? "sweet lady" : "nice guy"); + if (!tele_restrict(mon)) (void) rloc(mon, FALSE); + return 1; + } + if (u.ualign.type == A_CHAOTIC) + adjalign(1); + + /* by this point you have discovered mon's identity, blind or not... */ + pline("Time stands still while you and %s lie in each other's arms...", + noit_mon_nam(mon)); + if (rn2(35) > ACURR(A_CHA) + ACURR(A_INT)) { + /* Don't bother with mspec_used here... it didn't get tired! */ + pline("%s seems to have enjoyed it more than you...", + noit_Monnam(mon)); + switch (rn2(5)) { + case 0: You_feel("drained of energy."); + u.uen = 0; + u.uenmax -= rnd(Half_physical_damage ? 5 : 10); + exercise(A_CON, FALSE); + if (u.uenmax < 0) u.uenmax = 0; + break; + case 1: You("are down in the dumps."); + (void) adjattrib(A_CON, -1, TRUE); + exercise(A_CON, FALSE); + flags.botl = 1; + break; + case 2: Your("senses are dulled."); + (void) adjattrib(A_WIS, -1, TRUE); + exercise(A_WIS, FALSE); + flags.botl = 1; + break; + case 3: + if (!resists_drli(&youmonst)) { + You_feel("out of shape."); + losexp("overexertion"); + } else { + You("have a curious feeling..."); + } + break; + case 4: { + int tmp; + You_feel("exhausted."); + exercise(A_STR, FALSE); + tmp = rn1(10, 6); + if(Half_physical_damage) tmp = (tmp+1) / 2; + losehp(tmp, "exhaustion", KILLED_BY); + break; + } + } + } else { + mon->mspec_used = rnd(100); /* monster is worn out */ + You("seem to have enjoyed it more than %s...", + noit_mon_nam(mon)); + switch (rn2(5)) { + case 0: You_feel("raised to your full potential."); + exercise(A_CON, TRUE); + u.uen = (u.uenmax += rnd(5)); + break; + case 1: You_feel("good enough to do it again."); + (void) adjattrib(A_CON, 1, TRUE); + exercise(A_CON, TRUE); + flags.botl = 1; + break; + case 2: You("will always remember %s...", noit_mon_nam(mon)); + (void) adjattrib(A_WIS, 1, TRUE); + exercise(A_WIS, TRUE); + flags.botl = 1; + break; + case 3: pline("That was a very educational experience."); + pluslvl(FALSE); + exercise(A_WIS, TRUE); + break; + case 4: You_feel("restored to health!"); + u.uhp = u.uhpmax; + if (Upolyd) u.mh = u.mhmax; + exercise(A_STR, TRUE); + flags.botl = 1; + break; + } + } + + if (mon->mtame) /* don't charge */ ; + else if (rn2(20) < ACURR(A_CHA)) { + pline("%s demands that you pay %s, but you refuse...", + noit_Monnam(mon), + Blind ? (fem ? "her" : "him") : mhim(mon)); + } else if (u.umonnum == PM_LEPRECHAUN) + pline("%s tries to take your money, but fails...", + noit_Monnam(mon)); + else { +#ifndef GOLDOBJ + long cost; + + if (u.ugold > (long)LARGEST_INT - 10L) + cost = (long) rnd(LARGEST_INT) + 500L; + else + cost = (long) rnd((int)u.ugold + 10) + 500L; + if (mon->mpeaceful) { + cost /= 5L; + if (!cost) cost = 1L; + } + if (cost > u.ugold) cost = u.ugold; + if (!cost) verbalize("It's on the house!"); + else { + pline("%s takes %ld %s for services rendered!", + noit_Monnam(mon), cost, currency(cost)); + u.ugold -= cost; + mon->mgold += cost; + flags.botl = 1; + } +#else + long cost; + long umoney = money_cnt(invent); + + if (umoney > (long)LARGEST_INT - 10L) + cost = (long) rnd(LARGEST_INT) + 500L; + else + cost = (long) rnd((int)umoney + 10) + 500L; + if (mon->mpeaceful) { + cost /= 5L; + if (!cost) cost = 1L; + } + if (cost > umoney) cost = umoney; + if (!cost) verbalize("It's on the house!"); + else { + pline("%s takes %ld %s for services rendered!", + noit_Monnam(mon), cost, currency(cost)); + money2mon(mon, cost); + flags.botl = 1; + } +#endif + } + if (!rn2(25)) mon->mcan = 1; /* monster is worn out */ + if (!tele_restrict(mon)) (void) rloc(mon, FALSE); + return 1; +} + +STATIC_OVL void +mayberem(obj, str) +register struct obj *obj; +const char *str; +{ + char qbuf[QBUFSZ]; + + if (!obj || !obj->owornmask) return; + + if (rn2(20) < ACURR(A_CHA)) { + Sprintf(qbuf,"\"Shall I remove your %s, %s?\"", + str, + (!rn2(2) ? "lover" : !rn2(2) ? "dear" : "sweetheart")); + if (yn(qbuf) == 'n') return; + } else { + char hairbuf[BUFSZ]; + + Sprintf(hairbuf, "let me run my fingers through your %s", + body_part(HAIR)); + verbalize("Take off your %s; %s.", str, + (obj == uarm) ? "let's get a little closer" : + (obj == uarmc || obj == uarms) ? "it's in the way" : + (obj == uarmf) ? "let me rub your feet" : + (obj == uarmg) ? "they're too clumsy" : +#ifdef TOURIST + (obj == uarmu) ? "let me massage you" : +#endif + /* obj == uarmh */ + hairbuf); + } + remove_worn_item(obj, TRUE); +} +#endif /* SEDUCE */ + +#endif /* OVLB */ + +#ifdef OVL1 + +STATIC_OVL int +passiveum(olduasmon,mtmp,mattk) +struct permonst *olduasmon; +register struct monst *mtmp; +register struct attack *mattk; +{ + int i, tmp; + + for (i = 0; ; i++) { + if (i >= NATTK) return 1; + if (olduasmon->mattk[i].aatyp == AT_NONE || + olduasmon->mattk[i].aatyp == AT_BOOM) break; + } + if (olduasmon->mattk[i].damn) + tmp = d((int)olduasmon->mattk[i].damn, + (int)olduasmon->mattk[i].damd); + else if(olduasmon->mattk[i].damd) + tmp = d((int)olduasmon->mlevel+1, (int)olduasmon->mattk[i].damd); + else + tmp = 0; + + /* These affect the enemy even if you were "killed" (rehumanized) */ + switch(olduasmon->mattk[i].adtyp) { + case AD_ACID: + if (!rn2(2)) { + pline("%s is splashed by your acid!", Monnam(mtmp)); + if (resists_acid(mtmp)) { + pline("%s is not affected.", Monnam(mtmp)); + tmp = 0; + } + } else tmp = 0; + if (!rn2(30)) erode_armor(mtmp, TRUE); + if (!rn2(6)) erode_obj(MON_WEP(mtmp), TRUE, TRUE); + goto assess_dmg; + case AD_STON: /* cockatrice */ + { + long protector = attk_protection((int)mattk->aatyp), + wornitems = mtmp->misc_worn_check; + + /* wielded weapon gives same protection as gloves here */ + if (MON_WEP(mtmp) != 0) wornitems |= W_ARMG; + + if (!resists_ston(mtmp) && (protector == 0L || + (protector != ~0L && + (wornitems & protector) != protector))) { + if (poly_when_stoned(mtmp->data)) { + mon_to_stone(mtmp); + return (1); + } + pline("%s turns to stone!", Monnam(mtmp)); + stoned = 1; + xkilled(mtmp, 0); + if (mtmp->mhp > 0) return 1; + return 2; + } + return 1; + } + case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ + if (otmp) { + (void) drain_item(otmp); + /* No message */ + } + return (1); + default: + break; + } + if (!Upolyd) return 1; + + /* These affect the enemy only if you are still a monster */ + if (rn2(3)) switch(youmonst.data->mattk[i].adtyp) { + case AD_PHYS: + if (youmonst.data->mattk[i].aatyp == AT_BOOM) { + You("explode!"); + /* KMH, balance patch -- this is okay with unchanging */ + rehumanize(); + goto assess_dmg; + } + break; + case AD_PLYS: /* Floating eye */ + if (tmp > 127) tmp = 127; + if (u.umonnum == PM_FLOATING_EYE) { + if (!rn2(4)) tmp = 127; + if (mtmp->mcansee && haseyes(mtmp->data) && rn2(3) && + (perceives(mtmp->data) || !Invis)) { + if (Blind) + pline("As a blind %s, you cannot defend yourself.", + youmonst.data->mname); + else { + if (mon_reflects(mtmp, + "Your gaze is reflected by %s %s.")) + return 1; + pline("%s is frozen by your gaze!", Monnam(mtmp)); + mtmp->mcanmove = 0; + mtmp->mfrozen = tmp; + return 3; + } + } + } else { /* gelatinous cube */ + pline("%s is frozen by you.", Monnam(mtmp)); + mtmp->mcanmove = 0; + mtmp->mfrozen = tmp; + return 3; + } + return 1; + case AD_COLD: /* Brown mold or blue jelly */ + if (resists_cold(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + pline("%s is mildly chilly.", Monnam(mtmp)); + golemeffects(mtmp, AD_COLD, tmp); + tmp = 0; + break; + } + pline("%s is suddenly very cold!", Monnam(mtmp)); + u.mh += tmp / 2; + if (u.mhmax < u.mh) u.mhmax = u.mh; + if (u.mhmax > ((youmonst.data->mlevel+1) * 8)) + (void)split_mon(&youmonst, mtmp); + break; + case AD_STUN: /* Yellow mold */ + if (!mtmp->mstun) { + mtmp->mstun = 1; + pline("%s %s.", Monnam(mtmp), + makeplural(stagger(mtmp->data, "stagger"))); + } + tmp = 0; + break; + case AD_FIRE: /* Red mold */ + if (resists_fire(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + pline("%s is mildly warm.", Monnam(mtmp)); + golemeffects(mtmp, AD_FIRE, tmp); + tmp = 0; + break; + } + pline("%s is suddenly very hot!", Monnam(mtmp)); + break; + case AD_ELEC: + if (resists_elec(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + pline("%s is slightly tingled.", Monnam(mtmp)); + golemeffects(mtmp, AD_ELEC, tmp); + tmp = 0; + break; + } + pline("%s is jolted with your electricity!", Monnam(mtmp)); + break; + default: tmp = 0; + break; + } + else tmp = 0; + + assess_dmg: + if((mtmp->mhp -= tmp) <= 0) { + pline("%s dies!", Monnam(mtmp)); + xkilled(mtmp,0); + if (mtmp->mhp > 0) return 1; + return 2; + } + return 1; +} + +#endif /* OVL1 */ +#ifdef OVLB + +#include "edog.h" +struct monst * +cloneu() +{ + register struct monst *mon; + int mndx = monsndx(youmonst.data); + + if (u.mh <= 1) return(struct monst *)0; + if (mvitals[mndx].mvflags & G_EXTINCT) return(struct monst *)0; + mon = makemon(youmonst.data, u.ux, u.uy, NO_MINVENT|MM_EDOG); + mon = christen_monst(mon, plname); + initedog(mon); + mon->m_lev = youmonst.data->mlevel; + mon->mhpmax = u.mhmax; + mon->mhp = u.mh / 2; + u.mh -= mon->mhp; + flags.botl = 1; + return(mon); +} + +#endif /* OVLB */ + +/*mhitu.c*/ diff --git a/src/minion.c b/src/minion.c new file mode 100644 index 0000000..af47256 --- /dev/null +++ b/src/minion.c @@ -0,0 +1,322 @@ +/* SCCS Id: @(#)minion.c 3.4 2003/01/09 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "emin.h" +#include "epri.h" + +void +msummon(mon) /* mon summons a monster */ +struct monst *mon; +{ + register struct permonst *ptr; + register int dtype = NON_PM, cnt = 0; + aligntyp atyp; + struct monst *mtmp; + + if (mon) { + ptr = mon->data; + atyp = (ptr->maligntyp==A_NONE) ? A_NONE : sgn(ptr->maligntyp); + if (mon->ispriest || mon->data == &mons[PM_ALIGNED_PRIEST] + || mon->data == &mons[PM_ANGEL]) + atyp = EPRI(mon)->shralign; + } else { + ptr = &mons[PM_WIZARD_OF_YENDOR]; + atyp = (ptr->maligntyp==A_NONE) ? A_NONE : sgn(ptr->maligntyp); + } + + if (is_dprince(ptr) || (ptr == &mons[PM_WIZARD_OF_YENDOR])) { + dtype = (!rn2(20)) ? dprince(atyp) : + (!rn2(4)) ? dlord(atyp) : ndemon(atyp); + cnt = (!rn2(4) && is_ndemon(&mons[dtype])) ? 2 : 1; + } else if (is_dlord(ptr)) { + dtype = (!rn2(50)) ? dprince(atyp) : + (!rn2(20)) ? dlord(atyp) : ndemon(atyp); + cnt = (!rn2(4) && is_ndemon(&mons[dtype])) ? 2 : 1; + } else if (is_ndemon(ptr)) { + dtype = (!rn2(20)) ? dlord(atyp) : + (!rn2(6)) ? ndemon(atyp) : monsndx(ptr); + cnt = 1; + } else if (is_lminion(mon)) { + dtype = (is_lord(ptr) && !rn2(20)) ? llord() : + (is_lord(ptr) || !rn2(6)) ? lminion() : monsndx(ptr); + cnt = (!rn2(4) && !is_lord(&mons[dtype])) ? 2 : 1; + } else if (ptr == &mons[PM_ANGEL]) { + /* non-lawful angels can also summon */ + if (!rn2(6)) { + switch (atyp) { /* see summon_minion */ + case A_NEUTRAL: + dtype = PM_AIR_ELEMENTAL + rn2(4); + break; + case A_CHAOTIC: + case A_NONE: + dtype = ndemon(atyp); + break; + } + } else { + dtype = PM_ANGEL; + } + cnt = (!rn2(4) && !is_lord(&mons[dtype])) ? 2 : 1; + } + + if (dtype == NON_PM) return; + + /* sanity checks */ + if (cnt > 1 && (mons[dtype].geno & G_UNIQ)) cnt = 1; + /* + * If this daemon is unique and being re-summoned (the only way we + * could get this far with an extinct dtype), try another. + */ + if (mvitals[dtype].mvflags & G_GONE) { + dtype = ndemon(atyp); + if (dtype == NON_PM) return; + } + + while (cnt > 0) { + mtmp = makemon(&mons[dtype], u.ux, u.uy, NO_MM_FLAGS); + if (mtmp && (dtype == PM_ANGEL)) { + /* alignment should match the summoner */ + EPRI(mtmp)->shralign = atyp; + } + cnt--; + } +} + +void +summon_minion(alignment, talk) +aligntyp alignment; +boolean talk; +{ + register struct monst *mon; + int mnum; + + switch ((int)alignment) { + case A_LAWFUL: + mnum = lminion(); + break; + case A_NEUTRAL: + mnum = PM_AIR_ELEMENTAL + rn2(4); + break; + case A_CHAOTIC: + case A_NONE: + mnum = ndemon(alignment); + break; + default: + impossible("unaligned player?"); + mnum = ndemon(A_NONE); + break; + } + if (mnum == NON_PM) { + mon = 0; + } else if (mons[mnum].pxlth == 0) { + struct permonst *pm = &mons[mnum]; + mon = makemon(pm, u.ux, u.uy, MM_EMIN); + if (mon) { + mon->isminion = TRUE; + EMIN(mon)->min_align = alignment; + } + } else if (mnum == PM_ANGEL) { + mon = makemon(&mons[mnum], u.ux, u.uy, NO_MM_FLAGS); + if (mon) { + mon->isminion = TRUE; + EPRI(mon)->shralign = alignment; /* always A_LAWFUL here */ + } + } else + mon = makemon(&mons[mnum], u.ux, u.uy, NO_MM_FLAGS); + if (mon) { + if (talk) { + pline_The("voice of %s booms:", align_gname(alignment)); + verbalize("Thou shalt pay for thy indiscretion!"); + if (!Blind) + pline("%s appears before you.", Amonnam(mon)); + } + mon->mpeaceful = FALSE; + /* don't call set_malign(); player was naughty */ + } +} + +#define Athome (Inhell && !mtmp->cham) + +int +demon_talk(mtmp) /* returns 1 if it won't attack. */ +register struct monst *mtmp; +{ + long cash, demand, offer; + + if (uwep && uwep->oartifact == ART_EXCALIBUR) { + pline("%s looks very angry.", Amonnam(mtmp)); + mtmp->mpeaceful = mtmp->mtame = 0; + set_malign(mtmp); + newsym(mtmp->mx, mtmp->my); + return 0; + } + + /* Slight advantage given. */ + if (is_dprince(mtmp->data) && mtmp->minvis) { + mtmp->minvis = mtmp->perminvis = 0; + if (!Blind) pline("%s appears before you.", Amonnam(mtmp)); + newsym(mtmp->mx,mtmp->my); + } + if (youmonst.data->mlet == S_DEMON) { /* Won't blackmail their own. */ + pline("%s says, \"Good hunting, %s.\"", + Amonnam(mtmp), flags.female ? "Sister" : "Brother"); + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + return(1); + } +#ifndef GOLDOBJ + cash = u.ugold; +#else + cash = money_cnt(invent); +#endif + demand = (cash * (rnd(80) + 20 * Athome)) / + (100 * (1 + (sgn(u.ualign.type) == sgn(mtmp->data->maligntyp)))); + + if (!demand) { /* you have no gold */ + mtmp->mpeaceful = 0; + set_malign(mtmp); + return 0; + } else { + /* make sure that the demand is unmeetable if the monster + has the Amulet, preventing monster from being satisified + and removed from the game (along with said Amulet...) */ + if (mon_has_amulet(mtmp)) + demand = cash + (long)rn1(1000,40); + + pline("%s demands %ld %s for safe passage.", + Amonnam(mtmp), demand, currency(demand)); + + if ((offer = bribe(mtmp)) >= demand) { + pline("%s vanishes, laughing about cowardly mortals.", + Amonnam(mtmp)); + } else if (offer > 0L && (long)rnd(40) > (demand - offer)) { + pline("%s scowls at you menacingly, then vanishes.", + Amonnam(mtmp)); + } else { + pline("%s gets angry...", Amonnam(mtmp)); + mtmp->mpeaceful = 0; + set_malign(mtmp); + return 0; + } + } + mongone(mtmp); + return(1); +} + +long +bribe(mtmp) +struct monst *mtmp; +{ + char buf[BUFSZ]; + long offer; +#ifdef GOLDOBJ + long umoney = money_cnt(invent); +#endif + + getlin("How much will you offer?", buf); + if (sscanf(buf, "%ld", &offer) != 1) offer = 0L; + + /*Michael Paddon -- fix for negative offer to monster*/ + /*JAR880815 - */ + if (offer < 0L) { + You("try to shortchange %s, but fumble.", + mon_nam(mtmp)); + return 0L; + } else if (offer == 0L) { + You("refuse."); + return 0L; +#ifndef GOLDOBJ + } else if (offer >= u.ugold) { + You("give %s all your gold.", mon_nam(mtmp)); + offer = u.ugold; + } else { + You("give %s %ld %s.", mon_nam(mtmp), offer, currency(offer)); + } + u.ugold -= offer; + mtmp->mgold += offer; +#else + } else if (offer >= umoney) { + You("give %s all your gold.", mon_nam(mtmp)); + offer = umoney; + } else { + You("give %s %ld %s.", mon_nam(mtmp), offer, currency(offer)); + } + (void) money2mon(mtmp, offer); +#endif + flags.botl = 1; + return(offer); +} + +int +dprince(atyp) +aligntyp atyp; +{ + int tryct, pm; + + for (tryct = 0; tryct < 20; tryct++) { + pm = rn1(PM_DEMOGORGON + 1 - PM_ORCUS, PM_ORCUS); + if (!(mvitals[pm].mvflags & G_GONE) && + (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp))) + return(pm); + } + return(dlord(atyp)); /* approximate */ +} + +int +dlord(atyp) +aligntyp atyp; +{ + int tryct, pm; + + for (tryct = 0; tryct < 20; tryct++) { + pm = rn1(PM_YEENOGHU + 1 - PM_JUIBLEX, PM_JUIBLEX); + if (!(mvitals[pm].mvflags & G_GONE) && + (atyp == A_NONE || sgn(mons[pm].maligntyp) == sgn(atyp))) + return(pm); + } + return(ndemon(atyp)); /* approximate */ +} + +/* create lawful (good) lord */ +int +llord() +{ + if (!(mvitals[PM_ARCHON].mvflags & G_GONE)) + return(PM_ARCHON); + + return(lminion()); /* approximate */ +} + +int +lminion() +{ + int tryct; + struct permonst *ptr; + + for (tryct = 0; tryct < 20; tryct++) { + ptr = mkclass(S_ANGEL,0); + if (ptr && !is_lord(ptr)) + return(monsndx(ptr)); + } + + return NON_PM; +} + +int +ndemon(atyp) +aligntyp atyp; +{ + int tryct; + struct permonst *ptr; + + for (tryct = 0; tryct < 20; tryct++) { + ptr = mkclass(S_DEMON, 0); + if (ptr && is_ndemon(ptr) && + (atyp == A_NONE || sgn(ptr->maligntyp) == sgn(atyp))) + return(monsndx(ptr)); + } + + return NON_PM; +} + +/*minion.c*/ diff --git a/src/mklev.c b/src/mklev.c new file mode 100644 index 0000000..c1fb7cc --- /dev/null +++ b/src/mklev.c @@ -0,0 +1,1592 @@ +/* SCCS Id: @(#)mklev.c 3.4 2001/11/29 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +/* #define DEBUG */ /* uncomment to enable code debugging */ + +#ifdef DEBUG +# ifdef WIZARD +#define debugpline if (wizard) pline +# else +#define debugpline pline +# endif +#endif + +/* for UNIX, Rand #def'd to (long)lrand48() or (long)random() */ +/* croom->lx etc are schar (width <= int), so % arith ensures that */ +/* conversion of result to int is reasonable */ + + +STATIC_DCL void FDECL(mkfount,(int,struct mkroom *)); +#ifdef SINKS +STATIC_DCL void FDECL(mksink,(struct mkroom *)); +#endif +STATIC_DCL void FDECL(mkaltar,(struct mkroom *)); +STATIC_DCL void FDECL(mkgrave,(struct mkroom *)); +STATIC_DCL void NDECL(makevtele); +STATIC_DCL void NDECL(clear_level_structures); +STATIC_DCL void NDECL(makelevel); +STATIC_DCL void NDECL(mineralize); +STATIC_DCL boolean FDECL(bydoor,(XCHAR_P,XCHAR_P)); +STATIC_DCL struct mkroom *FDECL(find_branch_room, (coord *)); +STATIC_DCL struct mkroom *FDECL(pos_to_room, (XCHAR_P, XCHAR_P)); +STATIC_DCL boolean FDECL(place_niche,(struct mkroom *,int*,int*,int*)); +STATIC_DCL void FDECL(makeniche,(int)); +STATIC_DCL void NDECL(make_niches); + +STATIC_PTR int FDECL( CFDECLSPEC do_comp,(const genericptr,const genericptr)); + +STATIC_DCL void FDECL(dosdoor,(XCHAR_P,XCHAR_P,struct mkroom *,int)); +STATIC_DCL void FDECL(join,(int,int,BOOLEAN_P)); +STATIC_DCL void FDECL(do_room_or_subroom, (struct mkroom *,int,int,int,int, + BOOLEAN_P,SCHAR_P,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL void NDECL(makerooms); +STATIC_DCL void FDECL(finddpos,(coord *,XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P)); +STATIC_DCL void FDECL(mkinvpos, (XCHAR_P,XCHAR_P,int)); +STATIC_DCL void FDECL(mk_knox_portal, (XCHAR_P,XCHAR_P)); + +#define create_vault() create_room(-1, -1, 2, 2, -1, -1, VAULT, TRUE) +#define init_vault() vault_x = -1 +#define do_vault() (vault_x != -1) +static xchar vault_x, vault_y; +boolean goldseen; +static boolean made_branch; /* used only during level creation */ + +/* Args must be (const genericptr) so that qsort will always be happy. */ + +STATIC_PTR int CFDECLSPEC +do_comp(vx,vy) +const genericptr vx; +const genericptr vy; +{ +#ifdef LINT +/* lint complains about possible pointer alignment problems, but we know + that vx and vy are always properly aligned. Hence, the following + bogus definition: +*/ + return (vx == vy) ? 0 : -1; +#else + register const struct mkroom *x, *y; + + x = (const struct mkroom *)vx; + y = (const struct mkroom *)vy; + if(x->lx < y->lx) return(-1); + return(x->lx > y->lx); +#endif /* LINT */ +} + +STATIC_OVL void +finddpos(cc, xl,yl,xh,yh) +coord *cc; +xchar xl,yl,xh,yh; +{ + register xchar x, y; + + x = (xl == xh) ? xl : (xl + rn2(xh-xl+1)); + y = (yl == yh) ? yl : (yl + rn2(yh-yl+1)); + if(okdoor(x, y)) + goto gotit; + + for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++) + if(okdoor(x, y)) + goto gotit; + + for(x = xl; x <= xh; x++) for(y = yl; y <= yh; y++) + if(IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) + goto gotit; + /* cannot find something reasonable -- strange */ + x = xl; + y = yh; +gotit: + cc->x = x; + cc->y = y; + return; +} + +void +sort_rooms() +{ +#if defined(SYSV) || defined(DGUX) + qsort((genericptr_t) rooms, (unsigned)nroom, sizeof(struct mkroom), do_comp); +#else + qsort((genericptr_t) rooms, nroom, sizeof(struct mkroom), do_comp); +#endif +} + +STATIC_OVL void +do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, rtype, special, is_room) + register struct mkroom *croom; + int lowx, lowy; + register int hix, hiy; + boolean lit; + schar rtype; + boolean special; + boolean is_room; +{ + register int x, y; + struct rm *lev; + + /* locations might bump level edges in wall-less rooms */ + /* add/subtract 1 to allow for edge locations */ + if(!lowx) lowx++; + if(!lowy) lowy++; + if(hix >= COLNO-1) hix = COLNO-2; + if(hiy >= ROWNO-1) hiy = ROWNO-2; + + if(lit) { + for(x = lowx-1; x <= hix+1; x++) { + lev = &levl[x][max(lowy-1,0)]; + for(y = lowy-1; y <= hiy+1; y++) + lev++->lit = 1; + } + croom->rlit = 1; + } else + croom->rlit = 0; + + croom->lx = lowx; + croom->hx = hix; + croom->ly = lowy; + croom->hy = hiy; + croom->rtype = rtype; + croom->doorct = 0; + /* if we're not making a vault, doorindex will still be 0 + * if we are, we'll have problems adding niches to the previous room + * unless fdoor is at least doorindex + */ + croom->fdoor = doorindex; + croom->irregular = FALSE; + + croom->nsubrooms = 0; + croom->sbrooms[0] = (struct mkroom *) 0; + if (!special) { + for(x = lowx-1; x <= hix+1; x++) + for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) { + levl[x][y].typ = HWALL; + levl[x][y].horizontal = 1; /* For open/secret doors. */ + } + for(x = lowx-1; x <= hix+1; x += (hix-lowx+2)) + for(y = lowy; y <= hiy; y++) { + levl[x][y].typ = VWALL; + levl[x][y].horizontal = 0; /* For open/secret doors. */ + } + for(x = lowx; x <= hix; x++) { + lev = &levl[x][lowy]; + for(y = lowy; y <= hiy; y++) + lev++->typ = ROOM; + } + if (is_room) { + levl[lowx-1][lowy-1].typ = TLCORNER; + levl[hix+1][lowy-1].typ = TRCORNER; + levl[lowx-1][hiy+1].typ = BLCORNER; + levl[hix+1][hiy+1].typ = BRCORNER; + } else { /* a subroom */ + wallification(lowx-1, lowy-1, hix+1, hiy+1); + } + } +} + + +void +add_room(lowx, lowy, hix, hiy, lit, rtype, special) +register int lowx, lowy, hix, hiy; +boolean lit; +schar rtype; +boolean special; +{ + register struct mkroom *croom; + + croom = &rooms[nroom]; + do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, + rtype, special, (boolean) TRUE); + croom++; + croom->hx = -1; + nroom++; +} + +void +add_subroom(proom, lowx, lowy, hix, hiy, lit, rtype, special) +struct mkroom *proom; +register int lowx, lowy, hix, hiy; +boolean lit; +schar rtype; +boolean special; +{ + register struct mkroom *croom; + + croom = &subrooms[nsubroom]; + do_room_or_subroom(croom, lowx, lowy, hix, hiy, lit, + rtype, special, (boolean) FALSE); + proom->sbrooms[proom->nsubrooms++] = croom; + croom++; + croom->hx = -1; + nsubroom++; +} + +STATIC_OVL void +makerooms() +{ + boolean tried_vault = FALSE; + + /* make rooms until satisfied */ + /* rnd_rect() will returns 0 if no more rects are available... */ + while(nroom < MAXNROFROOMS && rnd_rect()) { + if(nroom >= (MAXNROFROOMS/6) && rn2(2) && !tried_vault) { + tried_vault = TRUE; + if (create_vault()) { + vault_x = rooms[nroom].lx; + vault_y = rooms[nroom].ly; + rooms[nroom].hx = -1; + } + } else + if (!create_room(-1, -1, -1, -1, -1, -1, OROOM, -1)) + return; + } + return; +} + +STATIC_OVL void +join(a,b,nxcor) +register int a, b; +boolean nxcor; +{ + coord cc,tt, org, dest; + register xchar tx, ty, xx, yy; + register struct mkroom *croom, *troom; + register int dx, dy; + + croom = &rooms[a]; + troom = &rooms[b]; + + /* find positions cc and tt for doors in croom and troom + and direction for a corridor between them */ + + if(troom->hx < 0 || croom->hx < 0 || doorindex >= DOORMAX) return; + if(troom->lx > croom->hx) { + dx = 1; + dy = 0; + xx = croom->hx+1; + tx = troom->lx-1; + finddpos(&cc, xx, croom->ly, xx, croom->hy); + finddpos(&tt, tx, troom->ly, tx, troom->hy); + } else if(troom->hy < croom->ly) { + dy = -1; + dx = 0; + yy = croom->ly-1; + finddpos(&cc, croom->lx, yy, croom->hx, yy); + ty = troom->hy+1; + finddpos(&tt, troom->lx, ty, troom->hx, ty); + } else if(troom->hx < croom->lx) { + dx = -1; + dy = 0; + xx = croom->lx-1; + tx = troom->hx+1; + finddpos(&cc, xx, croom->ly, xx, croom->hy); + finddpos(&tt, tx, troom->ly, tx, troom->hy); + } else { + dy = 1; + dx = 0; + yy = croom->hy+1; + ty = troom->ly-1; + finddpos(&cc, croom->lx, yy, croom->hx, yy); + finddpos(&tt, troom->lx, ty, troom->hx, ty); + } + xx = cc.x; + yy = cc.y; + tx = tt.x - dx; + ty = tt.y - dy; + if(nxcor && levl[xx+dx][yy+dy].typ) + return; + if (okdoor(xx,yy) || !nxcor) + dodoor(xx,yy,croom); + + org.x = xx+dx; org.y = yy+dy; + dest.x = tx; dest.y = ty; + + if (!dig_corridor(&org, &dest, nxcor, + level.flags.arboreal ? ROOM : CORR, STONE)) + return; + + /* we succeeded in digging the corridor */ + if (okdoor(tt.x, tt.y) || !nxcor) + dodoor(tt.x, tt.y, troom); + + if(smeq[a] < smeq[b]) + smeq[b] = smeq[a]; + else + smeq[a] = smeq[b]; +} + +void +makecorridors() +{ + int a, b, i; + boolean any = TRUE; + + for(a = 0; a < nroom-1; a++) { + join(a, a+1, FALSE); + if(!rn2(50)) break; /* allow some randomness */ + } + for(a = 0; a < nroom-2; a++) + if(smeq[a] != smeq[a+2]) + join(a, a+2, FALSE); + for(a = 0; any && a < nroom; a++) { + any = FALSE; + for(b = 0; b < nroom; b++) + if(smeq[a] != smeq[b]) { + join(a, b, FALSE); + any = TRUE; + } + } + if(nroom > 2) + for(i = rn2(nroom) + 4; i; i--) { + a = rn2(nroom); + b = rn2(nroom-2); + if(b >= a) b += 2; + join(a, b, TRUE); + } +} + +void +add_door(x,y,aroom) +register int x, y; +register struct mkroom *aroom; +{ + register struct mkroom *broom; + register int tmp; + + aroom->doorct++; + broom = aroom+1; + if(broom->hx < 0) + tmp = doorindex; + else + for(tmp = doorindex; tmp > broom->fdoor; tmp--) + doors[tmp] = doors[tmp-1]; + doorindex++; + doors[tmp].x = x; + doors[tmp].y = y; + for( ; broom->hx >= 0; broom++) broom->fdoor++; +} + +STATIC_OVL void +dosdoor(x,y,aroom,type) +register xchar x, y; +register struct mkroom *aroom; +register int type; +{ + boolean shdoor = ((*in_rooms(x, y, SHOPBASE))? TRUE : FALSE); + + if(!IS_WALL(levl[x][y].typ)) /* avoid SDOORs on already made doors */ + type = DOOR; + levl[x][y].typ = type; + if(type == DOOR) { + if(!rn2(3)) { /* is it a locked door, closed, or a doorway? */ + if(!rn2(5)) + levl[x][y].doormask = D_ISOPEN; + else if(!rn2(6)) + levl[x][y].doormask = D_LOCKED; + else + levl[x][y].doormask = D_CLOSED; + + if (levl[x][y].doormask != D_ISOPEN && !shdoor && + level_difficulty() >= 5 && !rn2(25)) + levl[x][y].doormask |= D_TRAPPED; + } else +#ifdef STUPID + if (shdoor) + levl[x][y].doormask = D_ISOPEN; + else + levl[x][y].doormask = D_NODOOR; +#else + levl[x][y].doormask = (shdoor ? D_ISOPEN : D_NODOOR); +#endif + if(levl[x][y].doormask & D_TRAPPED) { + struct monst *mtmp; + + if (level_difficulty() >= 9 && !rn2(5) && + !((mvitals[PM_SMALL_MIMIC].mvflags & G_GONE) && + (mvitals[PM_LARGE_MIMIC].mvflags & G_GONE) && + (mvitals[PM_GIANT_MIMIC].mvflags & G_GONE))) { + /* make a mimic instead */ + levl[x][y].doormask = D_NODOOR; + mtmp = makemon(mkclass(S_MIMIC,0), x, y, NO_MM_FLAGS); + if (mtmp) + set_mimic_sym(mtmp); + } + } + /* newsym(x,y); */ + } else { /* SDOOR */ + if(shdoor || !rn2(5)) levl[x][y].doormask = D_LOCKED; + else levl[x][y].doormask = D_CLOSED; + + if(!shdoor && level_difficulty() >= 4 && !rn2(20)) + levl[x][y].doormask |= D_TRAPPED; + } + + add_door(x,y,aroom); +} + +STATIC_OVL boolean +place_niche(aroom,dy,xx,yy) +register struct mkroom *aroom; +int *dy, *xx, *yy; +{ + coord dd; + + if(rn2(2)) { + *dy = 1; + finddpos(&dd, aroom->lx, aroom->hy+1, aroom->hx, aroom->hy+1); + } else { + *dy = -1; + finddpos(&dd, aroom->lx, aroom->ly-1, aroom->hx, aroom->ly-1); + } + *xx = dd.x; + *yy = dd.y; + return((boolean)((isok(*xx,*yy+*dy) && levl[*xx][*yy+*dy].typ == STONE) + && (isok(*xx,*yy-*dy) && !IS_POOL(levl[*xx][*yy-*dy].typ) + && !IS_FURNITURE(levl[*xx][*yy-*dy].typ)))); +} + +/* there should be one of these per trap, in the same order as trap.h */ +static NEARDATA const char *trap_engravings[TRAPNUM] = { + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, (char *)0, (char *)0, (char *)0, + /* 14..16: trap door, teleport, level-teleport */ + "Vlad was here", "ad aerarium", "ad aerarium", + (char *)0, (char *)0, (char *)0, (char *)0, (char *)0, + (char *)0, +}; + +STATIC_OVL void +makeniche(trap_type) +int trap_type; +{ + register struct mkroom *aroom; + register struct rm *rm; + register int vct = 8; + int dy, xx, yy; + register struct trap *ttmp; + + if(doorindex < DOORMAX) + while(vct--) { + aroom = &rooms[rn2(nroom)]; + if(aroom->rtype != OROOM) continue; /* not an ordinary room */ + if(aroom->doorct == 1 && rn2(5)) continue; + if(!place_niche(aroom,&dy,&xx,&yy)) continue; + + rm = &levl[xx][yy+dy]; + if(trap_type || !rn2(4)) { + + rm->typ = SCORR; + if(trap_type) { + if((trap_type == HOLE || trap_type == TRAPDOOR) + && !Can_fall_thru(&u.uz)) + trap_type = ROCKTRAP; + ttmp = maketrap(xx, yy+dy, trap_type); + if (ttmp) { + if (trap_type != ROCKTRAP) ttmp->once = 1; + if (trap_engravings[trap_type]) { + make_engr_at(xx, yy-dy, + trap_engravings[trap_type], 0L, DUST); + wipe_engr_at(xx, yy-dy, 5); /* age it a little */ + } + } + } + dosdoor(xx, yy, aroom, SDOOR); + } else { + rm->typ = CORR; + if(rn2(7)) + dosdoor(xx, yy, aroom, rn2(5) ? SDOOR : DOOR); + else { + if (!level.flags.noteleport) + (void) mksobj_at(SCR_TELEPORTATION, + xx, yy+dy, TRUE, FALSE); + if (!rn2(3)) (void) mkobj_at(0, xx, yy+dy, TRUE); + } + } + return; + } +} + +STATIC_OVL void +make_niches() +{ + register int ct = rnd((nroom>>1) + 1), dep = depth(&u.uz); + + boolean ltptr = (!level.flags.noteleport && dep > 15), + vamp = (dep > 5 && dep < 25); + + while(ct--) { + if (ltptr && !rn2(6)) { + ltptr = FALSE; + makeniche(LEVEL_TELEP); + } else if (vamp && !rn2(6)) { + vamp = FALSE; + makeniche(TRAPDOOR); + } else makeniche(NO_TRAP); + } +} + +STATIC_OVL void +makevtele() +{ + makeniche(TELEP_TRAP); +} + +/* clear out various globals that keep information on the current level. + * some of this is only necessary for some types of levels (maze, normal, + * special) but it's easier to put it all in one place than make sure + * each type initializes what it needs to separately. + */ +STATIC_OVL void +clear_level_structures() +{ + static struct rm zerorm = { cmap_to_glyph(S_stone), + 0, 0, 0, 0, 0, 0, 0, 0 }; + register int x,y; + register struct rm *lev; + + for(x=0; xproto); + return; + } else if (dungeons[u.uz.dnum].proto[0]) { + makemaz(""); + return; + } else if (In_mines(&u.uz)) { + makemaz("minefill"); + return; + } else if (In_quest(&u.uz)) { + char fillname[9]; + s_level *loc_lev; + + Sprintf(fillname, "%s-loca", urole.filecode); + loc_lev = find_level(fillname); + + Sprintf(fillname, "%s-fil", urole.filecode); + Strcat(fillname, + (u.uz.dlevel < loc_lev->dlevel.dlevel) ? "a" : "b"); + makemaz(fillname); + return; + } else if(In_hell(&u.uz) || + (rn2(5) && u.uz.dnum == medusa_level.dnum + && depth(&u.uz) > depth(&medusa_level))) { + makemaz(""); + return; + } + } + + /* otherwise, fall through - it's a "regular" level. */ + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + makeroguerooms(); + makerogueghost(); + } else +#endif + makerooms(); + sort_rooms(); + + /* construct stairs (up and down in different rooms if possible) */ + croom = &rooms[rn2(nroom)]; + if (!Is_botlevel(&u.uz)) + mkstairs(somex(croom), somey(croom), 0, croom); /* down */ + if (nroom > 1) { + troom = croom; + croom = &rooms[rn2(nroom-1)]; + if (croom == troom) croom++; + } + + if (u.uz.dlevel != 1) { + xchar sx, sy; + do { + sx = somex(croom); + sy = somey(croom); + } while(occupied(sx, sy)); + mkstairs(sx, sy, 1, croom); /* up */ + } + + branchp = Is_branchlev(&u.uz); /* possible dungeon branch */ + room_threshold = branchp ? 4 : 3; /* minimum number of rooms needed + to allow a random special room */ +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) goto skip0; +#endif + makecorridors(); + make_niches(); + + /* make a secret treasure vault, not connected to the rest */ + if(do_vault()) { + xchar w,h; +#ifdef DEBUG + debugpline("trying to make a vault..."); +#endif + w = 1; + h = 1; + if (check_room(&vault_x, &w, &vault_y, &h, TRUE)) { + fill_vault: + add_room(vault_x, vault_y, vault_x+w, + vault_y+h, TRUE, VAULT, FALSE); + level.flags.has_vault = 1; + ++room_threshold; + fill_room(&rooms[nroom - 1], FALSE); + mk_knox_portal(vault_x+w, vault_y+h); + if(!level.flags.noteleport && !rn2(3)) makevtele(); + } else if(rnd_rect() && create_vault()) { + vault_x = rooms[nroom].lx; + vault_y = rooms[nroom].ly; + if (check_room(&vault_x, &w, &vault_y, &h, TRUE)) + goto fill_vault; + else + rooms[nroom].hx = -1; + } + } + + { + register int u_depth = depth(&u.uz); + +#ifdef WIZARD + if(wizard && nh_getenv("SHOPTYPE")) mkroom(SHOPBASE); else +#endif + if (u_depth > 1 && + u_depth < depth(&medusa_level) && + nroom >= room_threshold && + rn2(u_depth) < 3) mkroom(SHOPBASE); + else if (u_depth > 4 && !rn2(6)) mkroom(COURT); + else if (u_depth > 5 && !rn2(8) && + !(mvitals[PM_LEPRECHAUN].mvflags & G_GONE)) mkroom(LEPREHALL); + else if (u_depth > 6 && !rn2(7)) mkroom(ZOO); + else if (u_depth > 8 && !rn2(5)) mkroom(TEMPLE); + else if (u_depth > 9 && !rn2(5) && + !(mvitals[PM_KILLER_BEE].mvflags & G_GONE)) mkroom(BEEHIVE); + else if (u_depth > 11 && !rn2(6)) mkroom(MORGUE); + else if (u_depth > 12 && !rn2(8)) mkroom(ANTHOLE); + else if (u_depth > 14 && !rn2(4) && + !(mvitals[PM_SOLDIER].mvflags & G_GONE)) mkroom(BARRACKS); + else if (u_depth > 15 && !rn2(6)) mkroom(SWAMP); + else if (u_depth > 16 && !rn2(8) && + !(mvitals[PM_COCKATRICE].mvflags & G_GONE)) mkroom(COCKNEST); + } + +#ifdef REINCARNATION +skip0: +#endif + /* Place multi-dungeon branch. */ + place_branch(branchp, 0, 0); + + /* for each room: put things inside */ + for(croom = rooms; croom->hx > 0; croom++) { + if(croom->rtype != OROOM) continue; + + /* put a sleeping monster inside */ + /* Note: monster may be on the stairs. This cannot be + avoided: maybe the player fell through a trap door + while a monster was on the stairs. Conclusion: + we have to check for monsters on the stairs anyway. */ + + if(u.uhave.amulet || !rn2(3)) { + x = somex(croom); y = somey(croom); + tmonst = makemon((struct permonst *) 0, x,y,NO_MM_FLAGS); + if (tmonst && tmonst->data == &mons[PM_GIANT_SPIDER] && + !occupied(x, y)) + (void) maketrap(x, y, WEB); + } + /* put traps and mimics inside */ + goldseen = FALSE; + x = 8 - (level_difficulty()/6); + if (x <= 1) x = 2; + while (!rn2(x)) + mktrap(0,0,croom,(coord*)0); + if (!goldseen && !rn2(3)) + (void) mkgold(0L, somex(croom), somey(croom)); +#ifdef REINCARNATION + if(Is_rogue_level(&u.uz)) goto skip_nonrogue; +#endif + if(!rn2(10)) mkfount(0,croom); +#ifdef SINKS + if(!rn2(60)) mksink(croom); +#endif + if(!rn2(60)) mkaltar(croom); + x = 80 - (depth(&u.uz) * 2); + if (x < 2) x = 2; + if(!rn2(x)) mkgrave(croom); + + /* put statues inside */ + if(!rn2(20)) + (void) mkcorpstat(STATUE, (struct monst *)0, + (struct permonst *)0, + somex(croom), somey(croom), TRUE); + /* put box/chest inside; + * 40% chance for at least 1 box, regardless of number + * of rooms; about 5 - 7.5% for 2 boxes, least likely + * when few rooms; chance for 3 or more is neglible. + */ + if(!rn2(nroom * 5 / 2)) + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + somex(croom), somey(croom), TRUE, FALSE); + + /* maybe make some graffiti */ + if(!rn2(27 + 3 * abs(depth(&u.uz)))) { + char buf[BUFSZ]; + const char *mesg = random_engraving(buf); + if (mesg) { + do { + x = somex(croom); y = somey(croom); + } while(levl[x][y].typ != ROOM && !rn2(40)); + if (!(IS_POOL(levl[x][y].typ) || + IS_FURNITURE(levl[x][y].typ))) + make_engr_at(x, y, mesg, 0L, MARK); + } + } + +#ifdef REINCARNATION + skip_nonrogue: +#endif + if(!rn2(3)) { + (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + tryct = 0; + while(!rn2(5)) { + if(++tryct > 100) { + impossible("tryct overflow4"); + break; + } + (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + } + } + } +} + +/* + * Place deposits of minerals (gold and misc gems) in the stone + * surrounding the rooms on the map. + * Also place kelp in water. + */ +STATIC_OVL void +mineralize() +{ + s_level *sp; + struct obj *otmp; + int goldprob, gemprob, x, y, cnt; + + + /* Place kelp, except on the plane of water */ + if (In_endgame(&u.uz)) return; + for (x = 2; x < (COLNO - 2); x++) + for (y = 1; y < (ROWNO - 1); y++) + if ((levl[x][y].typ == POOL && !rn2(10)) || + (levl[x][y].typ == MOAT && !rn2(30))) + (void) mksobj_at(KELP_FROND, x, y, TRUE, FALSE); + + /* determine if it is even allowed; + almost all special levels are excluded */ + if (In_hell(&u.uz) || In_V_tower(&u.uz) || +#ifdef REINCARNATION + Is_rogue_level(&u.uz) || +#endif + level.flags.arboreal || + ((sp = Is_special(&u.uz)) != 0 && !Is_oracle_level(&u.uz) + && (!In_mines(&u.uz) || sp->flags.town) + )) return; + + /* basic level-related probabilities */ + goldprob = 20 + depth(&u.uz) / 3; + gemprob = goldprob / 4; + + /* mines have ***MORE*** goodies - otherwise why mine? */ + if (In_mines(&u.uz)) { + goldprob *= 2; + gemprob *= 3; + } else if (In_quest(&u.uz)) { + goldprob /= 4; + gemprob /= 6; + } + + /* + * Seed rock areas with gold and/or gems. + * We use fairly low level object handling to avoid unnecessary + * overhead from placing things in the floor chain prior to burial. + */ + for (x = 2; x < (COLNO - 2); x++) + for (y = 1; y < (ROWNO - 1); y++) + if (levl[x][y+1].typ != STONE) { /* spot not eligible */ + y += 2; /* next two spots aren't eligible either */ + } else if (levl[x][y].typ != STONE) { /* this spot not eligible */ + y += 1; /* next spot isn't eligible either */ + } else if (!(levl[x][y].wall_info & W_NONDIGGABLE) && + levl[x][y-1].typ == STONE && + levl[x+1][y-1].typ == STONE && levl[x-1][y-1].typ == STONE && + levl[x+1][y].typ == STONE && levl[x-1][y].typ == STONE && + levl[x+1][y+1].typ == STONE && levl[x-1][y+1].typ == STONE) { + if (rn2(1000) < goldprob) { + if ((otmp = mksobj(GOLD_PIECE, FALSE, FALSE)) != 0) { + otmp->ox = x, otmp->oy = y; + otmp->quan = 1L + rnd(goldprob * 3); + otmp->owt = weight(otmp); + if (!rn2(3)) add_to_buried(otmp); + else place_object(otmp, x, y); + } + } + if (rn2(1000) < gemprob) { + for (cnt = rnd(2 + dunlev(&u.uz) / 3); cnt > 0; cnt--) + if ((otmp = mkobj(GEM_CLASS, FALSE)) != 0) { + if (otmp->otyp == ROCK) { + dealloc_obj(otmp); /* discard it */ + } else { + otmp->ox = x, otmp->oy = y; + if (!rn2(3)) add_to_buried(otmp); + else place_object(otmp, x, y); + } + } + } + } +} + +void +mklev() +{ + struct mkroom *croom; + + if(getbones()) return; + in_mklev = TRUE; + makelevel(); + bound_digging(); + mineralize(); + in_mklev = FALSE; + /* has_morgue gets cleared once morgue is entered; graveyard stays + set (graveyard might already be set even when has_morgue is clear + [see fixup_special()], so don't update it unconditionally) */ + if (level.flags.has_morgue) + level.flags.graveyard = 1; + if (!level.flags.is_maze_lev) { + for (croom = &rooms[0]; croom != &rooms[nroom]; croom++) +#ifdef SPECIALIZATION + topologize(croom, FALSE); +#else + topologize(croom); +#endif + } + set_wall_state(); +} + +void +#ifdef SPECIALIZATION +topologize(croom, do_ordinary) +register struct mkroom *croom; +boolean do_ordinary; +#else +topologize(croom) +register struct mkroom *croom; +#endif +{ + register int x, y, roomno = (croom - rooms) + ROOMOFFSET; + register int lowx = croom->lx, lowy = croom->ly; + register int hix = croom->hx, hiy = croom->hy; +#ifdef SPECIALIZATION + register schar rtype = croom->rtype; +#endif + register int subindex, nsubrooms = croom->nsubrooms; + + /* skip the room if already done; i.e. a shop handled out of order */ + /* also skip if this is non-rectangular (it _must_ be done already) */ + if ((int) levl[lowx][lowy].roomno == roomno || croom->irregular) + return; +#ifdef SPECIALIZATION +# ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) + do_ordinary = TRUE; /* vision routine helper */ +# endif + if ((rtype != OROOM) || do_ordinary) +#endif + { + /* do innards first */ + for(x = lowx; x <= hix; x++) + for(y = lowy; y <= hiy; y++) +#ifdef SPECIALIZATION + if (rtype == OROOM) + levl[x][y].roomno = NO_ROOM; + else +#endif + levl[x][y].roomno = roomno; + /* top and bottom edges */ + for(x = lowx-1; x <= hix+1; x++) + for(y = lowy-1; y <= hiy+1; y += (hiy-lowy+2)) { + levl[x][y].edge = 1; + if (levl[x][y].roomno) + levl[x][y].roomno = SHARED; + else + levl[x][y].roomno = roomno; + } + /* sides */ + for(x = lowx-1; x <= hix+1; x += (hix-lowx+2)) + for(y = lowy; y <= hiy; y++) { + levl[x][y].edge = 1; + if (levl[x][y].roomno) + levl[x][y].roomno = SHARED; + else + levl[x][y].roomno = roomno; + } + } + /* subrooms */ + for (subindex = 0; subindex < nsubrooms; subindex++) +#ifdef SPECIALIZATION + topologize(croom->sbrooms[subindex], (rtype != OROOM)); +#else + topologize(croom->sbrooms[subindex]); +#endif +} + +/* Find an unused room for a branch location. */ +STATIC_OVL struct mkroom * +find_branch_room(mp) + coord *mp; +{ + struct mkroom *croom = 0; + + if (nroom == 0) { + mazexy(mp); /* already verifies location */ + } else { + /* not perfect - there may be only one stairway */ + if(nroom > 2) { + int tryct = 0; + + do + croom = &rooms[rn2(nroom)]; + while((croom == dnstairs_room || croom == upstairs_room || + croom->rtype != OROOM) && (++tryct < 100)); + } else + croom = &rooms[rn2(nroom)]; + + do { + if (!somexy(croom, mp)) + impossible("Can't place branch!"); + } while(occupied(mp->x, mp->y) || + (levl[mp->x][mp->y].typ != CORR && levl[mp->x][mp->y].typ != ROOM)); + } + return croom; +} + +/* Find the room for (x,y). Return null if not in a room. */ +STATIC_OVL struct mkroom * +pos_to_room(x, y) + xchar x, y; +{ + int i; + struct mkroom *curr; + + for (curr = rooms, i = 0; i < nroom; curr++, i++) + if (inside_room(curr, x, y)) return curr;; + return (struct mkroom *) 0; +} + + +/* If given a branch, randomly place a special stair or portal. */ +void +place_branch(br, x, y) +branch *br; /* branch to place */ +xchar x, y; /* location */ +{ + coord m; + d_level *dest; + boolean make_stairs; + struct mkroom *br_room; + + /* + * Return immediately if there is no branch to make or we have + * already made one. This routine can be called twice when + * a special level is loaded that specifies an SSTAIR location + * as a favored spot for a branch. + */ + if (!br || made_branch) return; + + if (!x) { /* find random coordinates for branch */ + br_room = find_branch_room(&m); + x = m.x; + y = m.y; + } else { + br_room = pos_to_room(x, y); + } + + if (on_level(&br->end1, &u.uz)) { + /* we're on end1 */ + make_stairs = br->type != BR_NO_END1; + dest = &br->end2; + } else { + /* we're on end2 */ + make_stairs = br->type != BR_NO_END2; + dest = &br->end1; + } + + if (br->type == BR_PORTAL) { + mkportal(x, y, dest->dnum, dest->dlevel); + } else if (make_stairs) { + sstairs.sx = x; + sstairs.sy = y; + sstairs.up = (char) on_level(&br->end1, &u.uz) ? + br->end1_up : !br->end1_up; + assign_level(&sstairs.tolev, dest); + sstairs_room = br_room; + + levl[x][y].ladder = sstairs.up ? LA_UP : LA_DOWN; + levl[x][y].typ = STAIRS; + } + /* + * Set made_branch to TRUE even if we didn't make a stairwell (i.e. + * make_stairs is false) since there is currently only one branch + * per level, if we failed once, we're going to fail again on the + * next call. + */ + made_branch = TRUE; +} + +STATIC_OVL boolean +bydoor(x, y) +register xchar x, y; +{ + register int typ; + + if (isok(x+1, y)) { + typ = levl[x+1][y].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x-1, y)) { + typ = levl[x-1][y].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x, y+1)) { + typ = levl[x][y+1].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + if (isok(x, y-1)) { + typ = levl[x][y-1].typ; + if (IS_DOOR(typ) || typ == SDOOR) return TRUE; + } + return FALSE; +} + +/* see whether it is allowable to create a door at [x,y] */ +int +okdoor(x,y) +register xchar x, y; +{ + register boolean near_door = bydoor(x, y); + + return((levl[x][y].typ == HWALL || levl[x][y].typ == VWALL) && + doorindex < DOORMAX && !near_door); +} + +void +dodoor(x,y,aroom) +register int x, y; +register struct mkroom *aroom; +{ + if(doorindex >= DOORMAX) { + impossible("DOORMAX exceeded?"); + return; + } + + dosdoor(x,y,aroom,rn2(8) ? DOOR : SDOOR); +} + +boolean +occupied(x, y) +register xchar x, y; +{ + return((boolean)(t_at(x, y) + || IS_FURNITURE(levl[x][y].typ) + || is_lava(x,y) + || is_pool(x,y) + || invocation_pos(x,y) + )); +} + +/* make a trap somewhere (in croom if mazeflag = 0 && !tm) */ +/* if tm != null, make trap at that location */ +void +mktrap(num, mazeflag, croom, tm) +register int num, mazeflag; +register struct mkroom *croom; +coord *tm; +{ + register int kind; + coord m; + + /* no traps in pools */ + if (tm && is_pool(tm->x,tm->y)) return; + + if (num > 0 && num < TRAPNUM) { + kind = num; +#ifdef REINCARNATION + } else if (Is_rogue_level(&u.uz)) { + switch (rn2(7)) { + default: kind = BEAR_TRAP; break; /* 0 */ + case 1: kind = ARROW_TRAP; break; + case 2: kind = DART_TRAP; break; + case 3: kind = TRAPDOOR; break; + case 4: kind = PIT; break; + case 5: kind = SLP_GAS_TRAP; break; + case 6: kind = RUST_TRAP; break; + } +#endif + } else if (Inhell && !rn2(5)) { + /* bias the frequency of fire traps in Gehennom */ + kind = FIRE_TRAP; + } else { + unsigned lvl = level_difficulty(); + + do { + kind = rnd(TRAPNUM-1); + /* reject "too hard" traps */ + switch (kind) { + case MAGIC_PORTAL: + kind = NO_TRAP; break; + case ROLLING_BOULDER_TRAP: + case SLP_GAS_TRAP: + if (lvl < 2) kind = NO_TRAP; break; + case LEVEL_TELEP: + if (lvl < 5 || level.flags.noteleport) + kind = NO_TRAP; break; + case SPIKED_PIT: + if (lvl < 5) kind = NO_TRAP; break; + case LANDMINE: + if (lvl < 6) kind = NO_TRAP; break; + case WEB: + if (lvl < 7) kind = NO_TRAP; break; + case STATUE_TRAP: + case POLY_TRAP: + if (lvl < 8) kind = NO_TRAP; break; + case FIRE_TRAP: + if (!Inhell) kind = NO_TRAP; break; + case TELEP_TRAP: + if (level.flags.noteleport) kind = NO_TRAP; break; + case HOLE: + /* make these much less often than other traps */ + if (rn2(7)) kind = NO_TRAP; break; + } + } while (kind == NO_TRAP); + } + + if ((kind == TRAPDOOR || kind == HOLE) && !Can_fall_thru(&u.uz)) + kind = ROCKTRAP; + + if (tm) + m = *tm; + else { + register int tryct = 0; + boolean avoid_boulder = (kind == PIT || kind == SPIKED_PIT || + kind == TRAPDOOR || kind == HOLE); + + do { + if (++tryct > 200) + return; + if (mazeflag) + mazexy(&m); + else if (!somexy(croom,&m)) + return; + } while (occupied(m.x, m.y) || + (avoid_boulder && sobj_at(BOULDER, m.x, m.y))); + } + + (void) maketrap(m.x, m.y, kind); + if (kind == WEB) (void) makemon(&mons[PM_GIANT_SPIDER], + m.x, m.y, NO_MM_FLAGS); +} + +void +mkstairs(x, y, up, croom) +xchar x, y; +char up; +struct mkroom *croom; +{ + if (!x) { + impossible("mkstairs: bogus stair attempt at <%d,%d>", x, y); + return; + } + + /* + * We can't make a regular stair off an end of the dungeon. This + * attempt can happen when a special level is placed at an end and + * has an up or down stair specified in its description file. + */ + if ((dunlev(&u.uz) == 1 && up) || + (dunlev(&u.uz) == dunlevs_in_dungeon(&u.uz) && !up)) + return; + + if(up) { + xupstair = x; + yupstair = y; + upstairs_room = croom; + } else { + xdnstair = x; + ydnstair = y; + dnstairs_room = croom; + } + + levl[x][y].typ = STAIRS; + levl[x][y].ladder = up ? LA_UP : LA_DOWN; +} + +STATIC_OVL +void +mkfount(mazeflag,croom) +register int mazeflag; +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + + do { + if(++tryct > 200) return; + if(mazeflag) + mazexy(&m); + else + if (!somexy(croom, &m)) + return; + } while(occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a fountain at m.x, m.y */ + levl[m.x][m.y].typ = FOUNTAIN; + /* Is it a "blessed" fountain? (affects drinking from fountain) */ + if(!rn2(7)) levl[m.x][m.y].blessedftn = 1; + + level.flags.nfountains++; +} + +#ifdef SINKS +STATIC_OVL void +mksink(croom) +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while(occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a sink at m.x, m.y */ + levl[m.x][m.y].typ = SINK; + + level.flags.nsinks++; +} +#endif /* SINKS */ + + +STATIC_OVL void +mkaltar(croom) +register struct mkroom *croom; +{ + coord m; + register int tryct = 0; + aligntyp al; + + if (croom->rtype != OROOM) return; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while (occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put an altar at m.x, m.y */ + levl[m.x][m.y].typ = ALTAR; + + /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */ + al = rn2((int)A_LAWFUL+2) - 1; + levl[m.x][m.y].altarmask = Align2amask( al ); +} + +static void +mkgrave(croom) +struct mkroom *croom; +{ + coord m; + register int tryct = 0; + register struct obj *otmp; + boolean dobell = !rn2(10); + + + if(croom->rtype != OROOM) return; + + do { + if(++tryct > 200) return; + if (!somexy(croom, &m)) + return; + } while (occupied(m.x, m.y) || bydoor(m.x, m.y)); + + /* Put a grave at m.x, m.y */ + make_grave(m.x, m.y, dobell ? "Saved by the bell!" : (char *) 0); + + /* Possibly fill it with objects */ + if (!rn2(3)) (void) mkgold(0L, m.x, m.y); + for (tryct = rn2(5); tryct; tryct--) { + otmp = mkobj(RANDOM_CLASS, TRUE); + if (!otmp) return; + curse(otmp); + otmp->ox = m.x; + otmp->oy = m.y; + add_to_buried(otmp); + } + + /* Leave a bell, in case we accidentally buried someone alive */ + if (dobell) (void) mksobj_at(BELL, m.x, m.y, TRUE, FALSE); + return; +} + + +/* maze levels have slightly different constraints from normal levels */ +#define x_maze_min 2 +#define y_maze_min 2 +/* + * Major level transmutation: add a set of stairs (to the Sanctum) after + * an earthquake that leaves behind a a new topology, centered at inv_pos. + * Assumes there are no rooms within the invocation area and that inv_pos + * is not too close to the edge of the map. Also assume the hero can see, + * which is guaranteed for normal play due to the fact that sight is needed + * to read the Book of the Dead. + */ +void +mkinvokearea() +{ + int dist; + xchar xmin = inv_pos.x, xmax = inv_pos.x; + xchar ymin = inv_pos.y, ymax = inv_pos.y; + register xchar i; + + pline_The("floor shakes violently under you!"); + pline_The("walls around you begin to bend and crumble!"); + display_nhwindow(WIN_MESSAGE, TRUE); + + mkinvpos(xmin, ymin, 0); /* middle, before placing stairs */ + + for(dist = 1; dist < 7; dist++) { + xmin--; xmax++; + + /* top and bottom */ + if(dist != 3) { /* the area is wider that it is high */ + ymin--; ymax++; + for(i = xmin+1; i < xmax; i++) { + mkinvpos(i, ymin, dist); + mkinvpos(i, ymax, dist); + } + } + + /* left and right */ + for(i = ymin; i <= ymax; i++) { + mkinvpos(xmin, i, dist); + mkinvpos(xmax, i, dist); + } + + flush_screen(1); /* make sure the new glyphs shows up */ + delay_output(); + } + + You("are standing at the top of a stairwell leading down!"); + mkstairs(u.ux, u.uy, 0, (struct mkroom *)0); /* down */ + newsym(u.ux, u.uy); + vision_full_recalc = 1; /* everything changed */ +} + +/* Change level topology. Boulders in the vicinity are eliminated. + * Temporarily overrides vision in the name of a nice effect. + */ +STATIC_OVL void +mkinvpos(x,y,dist) +xchar x,y; +int dist; +{ + struct trap *ttmp; + struct obj *otmp; + boolean make_rocks; + register struct rm *lev = &levl[x][y]; + + /* clip at existing map borders if necessary */ + if (!within_bounded_area(x, y, x_maze_min + 1, y_maze_min + 1, + x_maze_max - 1, y_maze_max - 1)) { + /* only outermost 2 columns and/or rows may be truncated due to edge */ + if (dist < (7 - 2)) + panic("mkinvpos: <%d,%d> (%d) off map edge!", x, y, dist); + return; + } + + /* clear traps */ + if ((ttmp = t_at(x,y)) != 0) deltrap(ttmp); + + /* clear boulders; leave some rocks for non-{moat|trap} locations */ + make_rocks = (dist != 1 && dist != 4 && dist != 5) ? TRUE : FALSE; + while ((otmp = sobj_at(BOULDER, x, y)) != 0) { + if (make_rocks) { + fracture_rock(otmp); + make_rocks = FALSE; /* don't bother with more rocks */ + } else { + obj_extract_self(otmp); + obfree(otmp, (struct obj *)0); + } + } + unblock_point(x,y); /* make sure vision knows this location is open */ + + /* fake out saved state */ + lev->seenv = 0; + lev->doormask = 0; + if(dist < 6) lev->lit = TRUE; + lev->waslit = TRUE; + lev->horizontal = FALSE; + viz_array[y][x] = (dist < 6 ) ? + (IN_SIGHT|COULD_SEE) : /* short-circuit vision recalc */ + COULD_SEE; + + switch(dist) { + case 1: /* fire traps */ + if (is_pool(x,y)) break; + lev->typ = ROOM; + ttmp = maketrap(x, y, FIRE_TRAP); + if (ttmp) ttmp->tseen = TRUE; + break; + case 0: /* lit room locations */ + case 2: + case 3: + case 6: /* unlit room locations */ + lev->typ = ROOM; + break; + case 4: /* pools (aka a wide moat) */ + case 5: + lev->typ = MOAT; + /* No kelp! */ + break; + default: + impossible("mkinvpos called with dist %d", dist); + break; + } + + /* display new value of position; could have a monster/object on it */ + newsym(x,y); +} + +/* + * The portal to Ludios is special. The entrance can only occur within a + * vault in the main dungeon at a depth greater than 10. The Ludios branch + * structure reflects this by having a bogus "source" dungeon: the value + * of n_dgns (thus, Is_branchlev() will never find it). + * + * Ludios will remain isolated until the branch is corrected by this function. + */ +STATIC_OVL void +mk_knox_portal(x, y) +xchar x, y; +{ + extern int n_dgns; /* from dungeon.c */ + d_level *source; + branch *br; + schar u_depth; + + br = dungeon_branch("Fort Ludios"); + if (on_level(&knox_level, &br->end1)) { + source = &br->end2; + } else { + /* disallow Knox branch on a level with one branch already */ + if(Is_branchlev(&u.uz)) + return; + source = &br->end1; + } + + /* Already set or 2/3 chance of deferring until a later level. */ + if (source->dnum < n_dgns || (rn2(3) +#ifdef WIZARD + && !wizard +#endif + )) return; + + if (! (u.uz.dnum == oracle_level.dnum /* in main dungeon */ + && !at_dgn_entrance("The Quest") /* but not Quest's entry */ + && (u_depth = depth(&u.uz)) > 10 /* beneath 10 */ + && u_depth < depth(&medusa_level))) /* and above Medusa */ + return; + + /* Adjust source to be current level and re-insert branch. */ + *source = u.uz; + insert_branch(br, TRUE); + +#ifdef DEBUG + pline("Made knox portal."); +#endif + place_branch(br, x, y); +} + +/*mklev.c*/ diff --git a/src/mkmap.c b/src/mkmap.c new file mode 100644 index 0000000..ed24ac9 --- /dev/null +++ b/src/mkmap.c @@ -0,0 +1,479 @@ +/* SCCS Id: @(#)mkmap.c 3.4 1996/05/23 */ +/* Copyright (c) J. C. Collet, M. Stephenson and D. Cohrs, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "sp_lev.h" + +#define HEIGHT (ROWNO - 1) +#define WIDTH (COLNO - 2) + +STATIC_DCL void FDECL(init_map,(SCHAR_P)); +STATIC_DCL void FDECL(init_fill,(SCHAR_P,SCHAR_P)); +STATIC_DCL schar FDECL(get_map,(int,int,SCHAR_P)); +STATIC_DCL void FDECL(pass_one,(SCHAR_P,SCHAR_P)); +STATIC_DCL void FDECL(pass_two,(SCHAR_P,SCHAR_P)); +STATIC_DCL void FDECL(pass_three,(SCHAR_P,SCHAR_P)); +STATIC_DCL void NDECL(wallify_map); +STATIC_DCL void FDECL(join_map,(SCHAR_P,SCHAR_P)); +STATIC_DCL void FDECL(finish_map,(SCHAR_P,SCHAR_P,XCHAR_P,XCHAR_P)); +STATIC_DCL void FDECL(remove_room,(unsigned)); +void FDECL(mkmap, (lev_init *)); + +char *new_locations; +int min_rx, max_rx, min_ry, max_ry; /* rectangle bounds for regions */ +static int n_loc_filled; + +STATIC_OVL void +init_map(bg_typ) + schar bg_typ; +{ + register int i,j; + + for(i=1; i WIDTH || row >= HEIGHT) + return bg_typ; + return levl[col][row].typ; +} + +static int dirs[16] = { + -1, -1 /**/, -1, 0 /**/, -1, 1 /**/, + 0, -1 /**/, 0, 1 /**/, + 1, -1 /**/, 1, 0 /**/, 1, 1}; + +STATIC_OVL void +pass_one(bg_typ, fg_typ) + schar bg_typ, fg_typ; +{ + register int i,j; + short count, dr; + + for(i=2; i<=WIDTH; i++) + for(j=1; j 0 && + (anyroom ? IS_ROOM(levl[sx][sy].typ) : levl[sx][sy].typ == fg_typ) && + (int) levl[sx][sy].roomno != rmno) + sx--; + sx++; /* compensate for extra decrement */ + + /* assume sx,sy is valid */ + if(sx < min_rx) min_rx = sx; + if(sy < min_ry) min_ry = sy; + + for(i=sx; i<=WIDTH && levl[i][sy].typ == fg_typ; i++) { + levl[i][sy].roomno = rmno; + levl[i][sy].lit = lit; + if(anyroom) { + /* add walls to room as well */ + register int ii,jj; + for(ii= (i == sx ? i-1 : i); ii <= i+1; ii++) + for(jj = sy-1; jj <= sy+1; jj++) + if(isok(ii,jj) && + (IS_WALL(levl[ii][jj].typ) || + IS_DOOR(levl[ii][jj].typ))) { + levl[ii][jj].edge = 1; + if(lit) levl[ii][jj].lit = lit; + if ((int) levl[ii][jj].roomno != rmno) + levl[ii][jj].roomno = SHARED; + } + } + n_loc_filled++; + } + nx = i; + + if(isok(sx,sy-1)) { + for(i=sx; isx || isok(i-1,sy-1)) && + levl[i-1][sy-1].typ == fg_typ) { + if ((int) levl[i-1][sy-1].roomno != rmno) + flood_fill_rm(i-1,sy-1,rmno,lit,anyroom); + } + if((isx || isok(i-1,sy+1)) && + levl[i-1][sy+1].typ == fg_typ) { + if ((int) levl[i-1][sy+1].roomno != rmno) + flood_fill_rm(i-1,sy+1,rmno,lit,anyroom); + } + if((i max_rx) max_rx = nx - 1; /* nx is just past valid region */ + if(sy > max_ry) max_ry = sy; +} + +/* + * If we have drawn a map without walls, this allows us to + * auto-magically wallify it. Taken from lev_main.c. + */ +STATIC_OVL void +wallify_map() +{ + + int x, y, xx, yy; + + for(x = 1; x < COLNO; x++) + for(y = 0; y < ROWNO; y++) + if(levl[x][y].typ == STONE) { + for(yy = y - 1; yy <= y+1; yy++) + for(xx = x - 1; xx <= x+1; xx++) + if(isok(xx,yy) && levl[xx][yy].typ == ROOM) { + if(yy != y) levl[x][y].typ = HWALL; + else levl[x][y].typ = VWALL; + } + } +} + +STATIC_OVL void +join_map(bg_typ, fg_typ) + schar bg_typ, fg_typ; +{ + register struct mkroom *croom, *croom2; + + register int i, j; + int sx, sy; + coord sm, em; + + /* first, use flood filling to find all of the regions that need joining */ + for(i=2; i<=WIDTH; i++) + for(j=1; j 3) { + add_room(min_rx, min_ry, max_rx, max_ry, + FALSE, OROOM, TRUE); + rooms[nroom-1].irregular = TRUE; + if(nroom >= (MAXNROFROOMS*2)) + goto joinm; + } else { + /* + * it's a tiny hole; erase it from the map to avoid + * having the player end up here with no way out. + */ + for(sx = min_rx; sx<=max_rx; sx++) + for(sy = min_ry; sy<=max_ry; sy++) + if ((int) levl[sx][sy].roomno == + nroom + ROOMOFFSET) { + levl[sx][sy].typ = bg_typ; + levl[sx][sy].roomno = NO_ROOM; + } + } + } + } + +joinm: + /* + * Ok, now we can actually join the regions with fg_typ's. + * The rooms are already sorted due to the previous loop, + * so don't call sort_rooms(), which can screw up the roomno's + * validity in the levl structure. + */ + for(croom = &rooms[0], croom2 = croom + 1; croom2 < &rooms[nroom]; ) { + /* pick random starting and end locations for "corridor" */ + if(!somexy(croom, &sm) || !somexy(croom2, &em)) { + /* ack! -- the level is going to be busted */ + /* arbitrarily pick centers of both rooms and hope for the best */ + impossible("No start/end room loc in join_map."); + sm.x = croom->lx + ((croom->hx - croom->lx) / 2); + sm.y = croom->ly + ((croom->hy - croom->ly) / 2); + em.x = croom2->lx + ((croom2->hx - croom2->lx) / 2); + em.y = croom2->ly + ((croom2->hy - croom2->ly) / 2); + } + + (void) dig_corridor(&sm, &em, FALSE, fg_typ, bg_typ); + + /* choose next region to join */ + /* only increment croom if croom and croom2 are non-overlapping */ + if(croom2->lx > croom->hx || + ((croom2->ly > croom->hy || croom2->hy < croom->ly) && rn2(3))) { + croom = croom2; + } + croom2++; /* always increment the next room */ + } +} + +STATIC_OVL void +finish_map(fg_typ, bg_typ, lit, walled) + schar fg_typ, bg_typ; + boolean lit, walled; +{ + int i, j; + + if(walled) wallify_map(); + + if(lit) { + for(i=1; i= 0; --i) { + croom = &rooms[i]; + if (croom->hx < lx || croom->lx >= hx || + croom->hy < ly || croom->ly >= hy) continue; /* no overlap */ + + if (croom->lx < lx || croom->hx >= hx || + croom->ly < ly || croom->hy >= hy) { /* partial overlap */ + /* TODO: ensure remaining parts of room are still joined */ + + if (!croom->irregular) impossible("regular room in joined map"); + } else { + /* total overlap, remove the room */ + remove_room((unsigned)i); + } + } +} + +/* + * Remove roomno from the rooms array, decrementing nroom. Also updates + * all level roomno values of affected higher numbered rooms. Assumes + * level structure contents corresponding to roomno have already been reset. + * Currently handles only the removal of rooms that have no subrooms. + */ +STATIC_OVL void +remove_room(roomno) + unsigned roomno; +{ + struct mkroom *croom = &rooms[roomno]; + struct mkroom *maxroom = &rooms[--nroom]; + int i, j; + unsigned oroomno; + + if (croom != maxroom) { + /* since the order in the array only matters for making corridors, + * copy the last room over the one being removed on the assumption + * that corridors have already been dug. */ + (void) memcpy((genericptr_t)croom, (genericptr_t)maxroom, + sizeof(struct mkroom)); + + /* since maxroom moved, update affected level roomno values */ + oroomno = nroom + ROOMOFFSET; + roomno += ROOMOFFSET; + for (i = croom->lx; i <= croom->hx; ++i) + for (j = croom->ly; j <= croom->hy; ++j) { + if (levl[i][j].roomno == oroomno) + levl[i][j].roomno = roomno; + } + } + + maxroom->hx = -1; /* just like add_room */ +} + +#define N_P1_ITER 1 /* tune map generation via this value */ +#define N_P2_ITER 1 /* tune map generation via this value */ +#define N_P3_ITER 2 /* tune map smoothing via this value */ + +void +mkmap(init_lev) + lev_init *init_lev; +{ + schar bg_typ = init_lev->bg, + fg_typ = init_lev->fg; + boolean smooth = init_lev->smoothed, + join = init_lev->joined; + xchar lit = init_lev->lit, + walled = init_lev->walled; + int i; + + if(lit < 0) + lit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? 1 : 0; + + new_locations = (char *)alloc((WIDTH+1) * HEIGHT); + + init_map(bg_typ); + init_fill(bg_typ, fg_typ); + + for(i = 0; i < N_P1_ITER; i++) + pass_one(bg_typ, fg_typ); + + for(i = 0; i < N_P2_ITER; i++) + pass_two(bg_typ, fg_typ); + + if(smooth) + for(i = 0; i < N_P3_ITER; i++) + pass_three(bg_typ, fg_typ); + + if(join) + join_map(bg_typ, fg_typ); + + finish_map(fg_typ, bg_typ, (boolean)lit, (boolean)walled); + /* a walled, joined level is cavernous, not mazelike -dlc */ + if (walled && join) { + level.flags.is_maze_lev = FALSE; + level.flags.is_cavernous_lev = TRUE; + } + free(new_locations); +} + +/*mkmap.c*/ diff --git a/src/mkmaze.c b/src/mkmaze.c new file mode 100644 index 0000000..12aaee6 --- /dev/null +++ b/src/mkmaze.c @@ -0,0 +1,1415 @@ +/* SCCS Id: @(#)mkmaze.c 3.4 2002/04/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "sp_lev.h" +#include "lev.h" /* save & restore info */ + +/* from sp_lev.c, for fixup_special() */ +extern char *lev_message; +extern lev_region *lregions; +extern int num_lregions; + +STATIC_DCL boolean FDECL(iswall,(int,int)); +STATIC_DCL boolean FDECL(iswall_or_stone,(int,int)); +STATIC_DCL boolean FDECL(is_solid,(int,int)); +STATIC_DCL int FDECL(extend_spine, (int [3][3], int, int, int)); +STATIC_DCL boolean FDECL(okay,(int,int,int)); +STATIC_DCL void FDECL(maze0xy,(coord *)); +STATIC_DCL boolean FDECL(put_lregion_here,(XCHAR_P,XCHAR_P,XCHAR_P, + XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,BOOLEAN_P,d_level *)); +STATIC_DCL void NDECL(fixup_special); +STATIC_DCL void FDECL(move, (int *,int *,int)); +STATIC_DCL void NDECL(setup_waterlevel); +STATIC_DCL void NDECL(unsetup_waterlevel); + + +STATIC_OVL boolean +iswall(x,y) +int x,y; +{ + register int type; + + if (!isok(x,y)) return FALSE; + type = levl[x][y].typ; + return (IS_WALL(type) || IS_DOOR(type) || + type == SDOOR || type == IRONBARS); +} + +STATIC_OVL boolean +iswall_or_stone(x,y) + int x,y; +{ + register int type; + + /* out of bounds = stone */ + if (!isok(x,y)) return TRUE; + + type = levl[x][y].typ; + return (type == STONE || IS_WALL(type) || IS_DOOR(type) || + type == SDOOR || type == IRONBARS); +} + +/* return TRUE if out of bounds, wall or rock */ +STATIC_OVL boolean +is_solid(x,y) + int x, y; +{ + return (!isok(x,y) || IS_STWALL(levl[x][y].typ)); +} + + +/* + * Return 1 (not TRUE - we're doing bit vectors here) if we want to extend + * a wall spine in the (dx,dy) direction. Return 0 otherwise. + * + * To extend a wall spine in that direction, first there must be a wall there. + * Then, extend a spine unless the current position is surrounded by walls + * in the direction given by (dx,dy). E.g. if 'x' is our location, 'W' + * a wall, '.' a room, 'a' anything (we don't care), and our direction is + * (0,1) - South or down - then: + * + * a a a + * W x W This would not extend a spine from x down + * W W W (a corridor of walls is formed). + * + * a a a + * W x W This would extend a spine from x down. + * . W W + */ +STATIC_OVL int +extend_spine(locale, wall_there, dx, dy) + int locale[3][3]; + int wall_there, dx, dy; +{ + int spine, nx, ny; + + nx = 1 + dx; + ny = 1 + dy; + + if (wall_there) { /* wall in that direction */ + if (dx) { + if (locale[ 1][0] && locale[ 1][2] && /* EW are wall/stone */ + locale[nx][0] && locale[nx][2]) { /* diag are wall/stone */ + spine = 0; + } else { + spine = 1; + } + } else { /* dy */ + if (locale[0][ 1] && locale[2][ 1] && /* NS are wall/stone */ + locale[0][ny] && locale[2][ny]) { /* diag are wall/stone */ + spine = 0; + } else { + spine = 1; + } + } + } else { + spine = 0; + } + + return spine; +} + + +/* + * Wall cleanup. This function has two purposes: (1) remove walls that + * are totally surrounded by stone - they are redundant. (2) correct + * the types so that they extend and connect to each other. + */ +void +wallification(x1, y1, x2, y2) +int x1, y1, x2, y2; +{ + uchar type; + register int x,y; + struct rm *lev; + int bits; + int locale[3][3]; /* rock or wall status surrounding positions */ + /* + * Value 0 represents a free-standing wall. It could be anything, + * so even though this table says VWALL, we actually leave whatever + * typ was there alone. + */ + static xchar spine_array[16] = { + VWALL, HWALL, HWALL, HWALL, + VWALL, TRCORNER, TLCORNER, TDWALL, + VWALL, BRCORNER, BLCORNER, TUWALL, + VWALL, TLWALL, TRWALL, CROSSWALL + }; + + /* sanity check on incoming variables */ + if (x1<0 || x2>=COLNO || x1>x2 || y1<0 || y2>=ROWNO || y1>y2) + panic("wallification: bad bounds (%d,%d) to (%d,%d)",x1,y1,x2,y2); + + /* Step 1: change walls surrounded by rock to rock. */ + for(x = x1; x <= x2; x++) + for(y = y1; y <= y2; y++) { + lev = &levl[x][y]; + type = lev->typ; + if (IS_WALL(type) && type != DBWALL) { + if (is_solid(x-1,y-1) && + is_solid(x-1,y ) && + is_solid(x-1,y+1) && + is_solid(x, y-1) && + is_solid(x, y+1) && + is_solid(x+1,y-1) && + is_solid(x+1,y ) && + is_solid(x+1,y+1)) + lev->typ = STONE; + } + } + + /* + * Step 2: set the correct wall type. We can't combine steps + * 1 and 2 into a single sweep because we depend on knowing if + * the surrounding positions are stone. + */ + for(x = x1; x <= x2; x++) + for(y = y1; y <= y2; y++) { + lev = &levl[x][y]; + type = lev->typ; + if ( !(IS_WALL(type) && type != DBWALL)) continue; + + /* set the locations TRUE if rock or wall or out of bounds */ + locale[0][0] = iswall_or_stone(x-1,y-1); + locale[1][0] = iswall_or_stone( x,y-1); + locale[2][0] = iswall_or_stone(x+1,y-1); + + locale[0][1] = iswall_or_stone(x-1, y); + locale[2][1] = iswall_or_stone(x+1, y); + + locale[0][2] = iswall_or_stone(x-1,y+1); + locale[1][2] = iswall_or_stone( x,y+1); + locale[2][2] = iswall_or_stone(x+1,y+1); + + /* determine if wall should extend to each direction NSEW */ + bits = (extend_spine(locale, iswall(x,y-1), 0, -1) << 3) + | (extend_spine(locale, iswall(x,y+1), 0, 1) << 2) + | (extend_spine(locale, iswall(x+1,y), 1, 0) << 1) + | extend_spine(locale, iswall(x-1,y), -1, 0); + + /* don't change typ if wall is free-standing */ + if (bits) lev->typ = spine_array[bits]; + } +} + +STATIC_OVL boolean +okay(x,y,dir) +int x,y; +register int dir; +{ + move(&x,&y,dir); + move(&x,&y,dir); + if(x<3 || y<3 || x>x_maze_max || y>y_maze_max || levl[x][y].typ != 0) + return(FALSE); + return(TRUE); +} + +STATIC_OVL void +maze0xy(cc) /* find random starting point for maze generation */ + coord *cc; +{ + cc->x = 3 + 2*rn2((x_maze_max>>1) - 1); + cc->y = 3 + 2*rn2((y_maze_max>>1) - 1); + return; +} + +/* + * Bad if: + * pos is occupied OR + * pos is inside restricted region (lx,ly,hx,hy) OR + * NOT (pos is corridor and a maze level OR pos is a room OR pos is air) + */ +boolean +bad_location(x, y, lx, ly, hx, hy) + xchar x, y; + xchar lx, ly, hx, hy; +{ + return((boolean)(occupied(x, y) || + within_bounded_area(x,y, lx,ly, hx,hy) || + !((levl[x][y].typ == CORR && level.flags.is_maze_lev) || + levl[x][y].typ == ROOM || levl[x][y].typ == AIR))); +} + +/* pick a location in area (lx, ly, hx, hy) but not in (nlx, nly, nhx, nhy) */ +/* and place something (based on rtype) in that region */ +void +place_lregion(lx, ly, hx, hy, nlx, nly, nhx, nhy, rtype, lev) + xchar lx, ly, hx, hy; + xchar nlx, nly, nhx, nhy; + xchar rtype; + d_level *lev; +{ + int trycnt; + boolean oneshot; + xchar x, y; + + if(!lx) { /* default to whole level */ + /* + * if there are rooms and this a branch, let place_branch choose + * the branch location (to avoid putting branches in corridors). + */ + if(rtype == LR_BRANCH && nroom) { + place_branch(Is_branchlev(&u.uz), 0, 0); + return; + } + + lx = 1; hx = COLNO-1; + ly = 1; hy = ROWNO-1; + } + + /* first a probabilistic approach */ + + oneshot = (lx == hx && ly == hy); + for (trycnt = 0; trycnt < 200; trycnt++) { + x = rn1((hx - lx) + 1, lx); + y = rn1((hy - ly) + 1, ly); + if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)) + return; + } + + /* then a deterministic one */ + + oneshot = TRUE; + for (x = lx; x <= hx; x++) + for (y = ly; y <= hy; y++) + if (put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev)) + return; + + impossible("Couldn't place lregion type %d!", rtype); +} + +STATIC_OVL boolean +put_lregion_here(x,y,nlx,nly,nhx,nhy,rtype,oneshot,lev) +xchar x, y; +xchar nlx, nly, nhx, nhy; +xchar rtype; +boolean oneshot; +d_level *lev; +{ + if (bad_location(x, y, nlx, nly, nhx, nhy)) { + if (!oneshot) { + return FALSE; /* caller should try again */ + } else { + /* Must make do with the only location possible; + avoid failure due to a misplaced trap. + It might still fail if there's a dungeon feature here. */ + struct trap *t = t_at(x,y); + + if (t && t->ttyp != MAGIC_PORTAL) deltrap(t); + if (bad_location(x, y, nlx, nly, nhx, nhy)) return FALSE; + } + } + switch (rtype) { + case LR_TELE: + case LR_UPTELE: + case LR_DOWNTELE: + /* "something" means the player in this case */ + if(MON_AT(x, y)) { + /* move the monster if no choice, or just try again */ + if(oneshot) (void) rloc(m_at(x,y), FALSE); + else return(FALSE); + } + u_on_newpos(x, y); + break; + case LR_PORTAL: + mkportal(x, y, lev->dnum, lev->dlevel); + break; + case LR_DOWNSTAIR: + case LR_UPSTAIR: + mkstairs(x, y, (char)rtype, (struct mkroom *)0); + break; + case LR_BRANCH: + place_branch(Is_branchlev(&u.uz), x, y); + break; + } + return(TRUE); +} + +static boolean was_waterlevel; /* ugh... this shouldn't be needed */ + +/* this is special stuff that the level compiler cannot (yet) handle */ +STATIC_OVL void +fixup_special() +{ + register lev_region *r = lregions; + struct d_level lev; + register int x, y; + struct mkroom *croom; + boolean added_branch = FALSE; + + if (was_waterlevel) { + was_waterlevel = FALSE; + u.uinwater = 0; + unsetup_waterlevel(); + } else if (Is_waterlevel(&u.uz)) { + level.flags.hero_memory = 0; + was_waterlevel = TRUE; + /* water level is an odd beast - it has to be set up + before calling place_lregions etc. */ + setup_waterlevel(); + } + for(x = 0; x < num_lregions; x++, r++) { + switch(r->rtype) { + case LR_BRANCH: + added_branch = TRUE; + goto place_it; + + case LR_PORTAL: + if(*r->rname.str >= '0' && *r->rname.str <= '9') { + /* "chutes and ladders" */ + lev = u.uz; + lev.dlevel = atoi(r->rname.str); + } else { + s_level *sp = find_level(r->rname.str); + lev = sp->dlevel; + } + /* fall into... */ + + case LR_UPSTAIR: + case LR_DOWNSTAIR: + place_it: + place_lregion(r->inarea.x1, r->inarea.y1, + r->inarea.x2, r->inarea.y2, + r->delarea.x1, r->delarea.y1, + r->delarea.x2, r->delarea.y2, + r->rtype, &lev); + break; + + case LR_TELE: + case LR_UPTELE: + case LR_DOWNTELE: + /* save the region outlines for goto_level() */ + if(r->rtype == LR_TELE || r->rtype == LR_UPTELE) { + updest.lx = r->inarea.x1; updest.ly = r->inarea.y1; + updest.hx = r->inarea.x2; updest.hy = r->inarea.y2; + updest.nlx = r->delarea.x1; updest.nly = r->delarea.y1; + updest.nhx = r->delarea.x2; updest.nhy = r->delarea.y2; + } + if(r->rtype == LR_TELE || r->rtype == LR_DOWNTELE) { + dndest.lx = r->inarea.x1; dndest.ly = r->inarea.y1; + dndest.hx = r->inarea.x2; dndest.hy = r->inarea.y2; + dndest.nlx = r->delarea.x1; dndest.nly = r->delarea.y1; + dndest.nhx = r->delarea.x2; dndest.nhy = r->delarea.y2; + } + /* place_lregion gets called from goto_level() */ + break; + } + + if (r->rname.str) free((genericptr_t) r->rname.str), r->rname.str = 0; + } + + /* place dungeon branch if not placed above */ + if (!added_branch && Is_branchlev(&u.uz)) { + place_lregion(0,0,0,0,0,0,0,0,LR_BRANCH,(d_level *)0); + } + + /* KMH -- Sokoban levels */ + if(In_sokoban(&u.uz)) + sokoban_detect(); + + /* Still need to add some stuff to level file */ + if (Is_medusa_level(&u.uz)) { + struct obj *otmp; + int tryct; + + croom = &rooms[0]; /* only one room on the medusa level */ + for (tryct = rnd(4); tryct; tryct--) { + x = somex(croom); y = somey(croom); + if (goodpos(x, y, (struct monst *)0, 0)) { + otmp = mk_tt_object(STATUE, x, y); + while (otmp && (poly_when_stoned(&mons[otmp->corpsenm]) || + pm_resistance(&mons[otmp->corpsenm],MR_STONE))) { + otmp->corpsenm = rndmonnum(); + otmp->owt = weight(otmp); + } + } + } + + if (rn2(2)) + otmp = mk_tt_object(STATUE, somex(croom), somey(croom)); + else /* Medusa statues don't contain books */ + otmp = mkcorpstat(STATUE, (struct monst *)0, (struct permonst *)0, + somex(croom), somey(croom), FALSE); + if (otmp) { + while (pm_resistance(&mons[otmp->corpsenm],MR_STONE) + || poly_when_stoned(&mons[otmp->corpsenm])) { + otmp->corpsenm = rndmonnum(); + otmp->owt = weight(otmp); + } + } + } else if(Is_wiz1_level(&u.uz)) { + croom = search_special(MORGUE); + + create_secret_door(croom, W_SOUTH|W_EAST|W_WEST); + } else if(Is_knox(&u.uz)) { + /* using an unfilled morgue for rm id */ + croom = search_special(MORGUE); + /* avoid inappropriate morgue-related messages */ + level.flags.graveyard = level.flags.has_morgue = 0; + croom->rtype = OROOM; /* perhaps it should be set to VAULT? */ + /* stock the main vault */ + for(x = croom->lx; x <= croom->hx; x++) + for(y = croom->ly; y <= croom->hy; y++) { + (void) mkgold((long) rn1(300, 600), x, y); + if (!rn2(3) && !is_pool(x,y)) + (void)maketrap(x, y, rn2(3) ? LANDMINE : SPIKED_PIT); + } + } else if (Role_if(PM_PRIEST) && In_quest(&u.uz)) { + /* less chance for undead corpses (lured from lower morgues) */ + level.flags.graveyard = 1; + } else if (Is_stronghold(&u.uz)) { + level.flags.graveyard = 1; + } else if(Is_sanctum(&u.uz)) { + croom = search_special(TEMPLE); + + create_secret_door(croom, W_ANY); + } else if(on_level(&u.uz, &orcus_level)) { + register struct monst *mtmp, *mtmp2; + + /* it's a ghost town, get rid of shopkeepers */ + for(mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if(mtmp->isshk) mongone(mtmp); + } + } + + if(lev_message) { + char *str, *nl; + for(str = lev_message; (nl = index(str, '\n')) != 0; str = nl+1) { + *nl = '\0'; + pline("%s", str); + } + if(*str) + pline("%s", str); + free((genericptr_t)lev_message); + lev_message = 0; + } + + if (lregions) + free((genericptr_t) lregions), lregions = 0; + num_lregions = 0; +} + +void +makemaz(s) +register const char *s; +{ + int x,y; + char protofile[20]; + s_level *sp = Is_special(&u.uz); + coord mm; + + if(*s) { + if(sp && sp->rndlevs) Sprintf(protofile, "%s-%d", s, + rnd((int) sp->rndlevs)); + else Strcpy(protofile, s); + } else if(*(dungeons[u.uz.dnum].proto)) { + if(dunlevs_in_dungeon(&u.uz) > 1) { + if(sp && sp->rndlevs) + Sprintf(protofile, "%s%d-%d", dungeons[u.uz.dnum].proto, + dunlev(&u.uz), + rnd((int) sp->rndlevs)); + else Sprintf(protofile, "%s%d", dungeons[u.uz.dnum].proto, + dunlev(&u.uz)); + } else if(sp && sp->rndlevs) { + Sprintf(protofile, "%s-%d", dungeons[u.uz.dnum].proto, + rnd((int) sp->rndlevs)); + } else Strcpy(protofile, dungeons[u.uz.dnum].proto); + + } else Strcpy(protofile, ""); + +#ifdef WIZARD + /* SPLEVTYPE format is "level-choice,level-choice"... */ + if (wizard && *protofile && sp && sp->rndlevs) { + char *ep = getenv("SPLEVTYPE"); /* not nh_getenv */ + if (ep) { + /* rindex always succeeds due to code in prior block */ + int len = (rindex(protofile, '-') - protofile) + 1; + + while (ep && *ep) { + if (!strncmp(ep, protofile, len)) { + int pick = atoi(ep + len); + /* use choice only if valid */ + if (pick > 0 && pick <= (int) sp->rndlevs) + Sprintf(protofile + len, "%d", pick); + break; + } else { + ep = index(ep, ','); + if (ep) ++ep; + } + } + } + } +#endif + + if(*protofile) { + Strcat(protofile, LEV_EXT); + if(load_special(protofile)) { + fixup_special(); + /* some levels can end up with monsters + on dead mon list, including light source monsters */ + dmonsfree(); + return; /* no mazification right now */ + } + impossible("Couldn't load \"%s\" - making a maze.", protofile); + } + + level.flags.is_maze_lev = TRUE; + +#ifndef WALLIFIED_MAZE + for(x = 2; x < x_maze_max; x++) + for(y = 2; y < y_maze_max; y++) + levl[x][y].typ = STONE; +#else + for(x = 2; x <= x_maze_max; x++) + for(y = 2; y <= y_maze_max; y++) + levl[x][y].typ = ((x % 2) && (y % 2)) ? STONE : HWALL; +#endif + + maze0xy(&mm); + walkfrom((int) mm.x, (int) mm.y); + /* put a boulder at the maze center */ + (void) mksobj_at(BOULDER, (int) mm.x, (int) mm.y, TRUE, FALSE); + +#ifdef WALLIFIED_MAZE + wallification(2, 2, x_maze_max, y_maze_max); +#endif + mazexy(&mm); + mkstairs(mm.x, mm.y, 1, (struct mkroom *)0); /* up */ + if (!Invocation_lev(&u.uz)) { + mazexy(&mm); + mkstairs(mm.x, mm.y, 0, (struct mkroom *)0); /* down */ + } else { /* choose "vibrating square" location */ +#define x_maze_min 2 +#define y_maze_min 2 + /* + * Pick a position where the stairs down to Moloch's Sanctum + * level will ultimately be created. At that time, an area + * will be altered: walls removed, moat and traps generated, + * boulders destroyed. The position picked here must ensure + * that that invocation area won't extend off the map. + * + * We actually allow up to 2 squares around the usual edge of + * the area to get truncated; see mkinvokearea(mklev.c). + */ +#define INVPOS_X_MARGIN (6 - 2) +#define INVPOS_Y_MARGIN (5 - 2) +#define INVPOS_DISTANCE 11 + int x_range = x_maze_max - x_maze_min - 2*INVPOS_X_MARGIN - 1, + y_range = y_maze_max - y_maze_min - 2*INVPOS_Y_MARGIN - 1; + +#ifdef DEBUG + if (x_range <= INVPOS_X_MARGIN || y_range <= INVPOS_Y_MARGIN || + (x_range * y_range) <= (INVPOS_DISTANCE * INVPOS_DISTANCE)) + panic("inv_pos: maze is too small! (%d x %d)", + x_maze_max, y_maze_max); +#endif + inv_pos.x = inv_pos.y = 0; /*{occupied() => invocation_pos()}*/ + do { + x = rn1(x_range, x_maze_min + INVPOS_X_MARGIN + 1); + y = rn1(y_range, y_maze_min + INVPOS_Y_MARGIN + 1); + /* we don't want it to be too near the stairs, nor + to be on a spot that's already in use (wall|trap) */ + } while (x == xupstair || y == yupstair || /*(direct line)*/ + abs(x - xupstair) == abs(y - yupstair) || + distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE || + !SPACE_POS(levl[x][y].typ) || occupied(x, y)); + inv_pos.x = x; + inv_pos.y = y; +#undef INVPOS_X_MARGIN +#undef INVPOS_Y_MARGIN +#undef INVPOS_DISTANCE +#undef x_maze_min +#undef y_maze_min + } + + /* place branch stair or portal */ + place_branch(Is_branchlev(&u.uz), 0, 0); + + for(x = rn1(8,11); x; x--) { + mazexy(&mm); + (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE); + } + for(x = rn1(10,2); x; x--) { + mazexy(&mm); + (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE); + } + for (x = rn2(3); x; x--) { + mazexy(&mm); + (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rn1(5,7); x; x--) { + mazexy(&mm); + (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rn1(6,7); x; x--) { + mazexy(&mm); + (void) mkgold(0L,mm.x,mm.y); + } + for(x = rn1(6,7); x; x--) + mktrap(0,1,(struct mkroom *) 0, (coord*) 0); +} + +#ifdef MICRO +/* Make the mazewalk iterative by faking a stack. This is needed to + * ensure the mazewalk is successful in the limited stack space of + * the program. This iterative version uses the minimum amount of stack + * that is totally safe. + */ +void +walkfrom(x,y) +int x,y; +{ +#define CELLS (ROWNO * COLNO) / 4 /* a maze cell is 4 squares */ + char mazex[CELLS + 1], mazey[CELLS + 1]; /* char's are OK */ + int q, a, dir, pos; + int dirs[4]; + + pos = 1; + mazex[pos] = (char) x; + mazey[pos] = (char) y; + while (pos) { + x = (int) mazex[pos]; + y = (int) mazey[pos]; + if(!IS_DOOR(levl[x][y].typ)) { + /* might still be on edge of MAP, so don't overwrite */ +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + q = 0; + for (a = 0; a < 4; a++) + if(okay(x, y, a)) dirs[q++]= a; + if (!q) + pos--; + else { + dir = dirs[rn2(q)]; + move(&x, &y, dir); +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + move(&x, &y, dir); + pos++; + if (pos > CELLS) + panic("Overflow in walkfrom"); + mazex[pos] = (char) x; + mazey[pos] = (char) y; + } + } +} +#else + +void +walkfrom(x,y) +int x,y; +{ + register int q,a,dir; + int dirs[4]; + + if(!IS_DOOR(levl[x][y].typ)) { + /* might still be on edge of MAP, so don't overwrite */ +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + + while(1) { + q = 0; + for(a = 0; a < 4; a++) + if(okay(x,y,a)) dirs[q++]= a; + if(!q) return; + dir = dirs[rn2(q)]; + move(&x,&y,dir); +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + move(&x,&y,dir); + walkfrom(x,y); + } +} +#endif /* MICRO */ + +STATIC_OVL void +move(x,y,dir) +register int *x, *y; +register int dir; +{ + switch(dir){ + case 0: --(*y); break; + case 1: (*x)++; break; + case 2: (*y)++; break; + case 3: --(*x); break; + default: panic("move: bad direction"); + } +} + +void +mazexy(cc) /* find random point in generated corridors, + so we don't create items in moats, bunkers, or walls */ + coord *cc; +{ + int cpt=0; + + do { + cc->x = 3 + 2*rn2((x_maze_max>>1) - 1); + cc->y = 3 + 2*rn2((y_maze_max>>1) - 1); + cpt++; + } while (cpt < 100 && levl[cc->x][cc->y].typ != +#ifdef WALLIFIED_MAZE + ROOM +#else + CORR +#endif + ); + if (cpt >= 100) { + register int x, y; + /* last try */ + for (x = 0; x < (x_maze_max>>1) - 1; x++) + for (y = 0; y < (y_maze_max>>1) - 1; y++) { + cc->x = 3 + 2 * x; + cc->y = 3 + 2 * y; + if (levl[cc->x][cc->y].typ == +#ifdef WALLIFIED_MAZE + ROOM +#else + CORR +#endif + ) return; + } + panic("mazexy: can't find a place!"); + } + return; +} + +void +bound_digging() +/* put a non-diggable boundary around the initial portion of a level map. + * assumes that no level will initially put things beyond the isok() range. + * + * we can't bound unconditionally on the last line with something in it, + * because that something might be a niche which was already reachable, + * so the boundary would be breached + * + * we can't bound unconditionally on one beyond the last line, because + * that provides a window of abuse for WALLIFIED_MAZE special levels + */ +{ + register int x,y; + register unsigned typ; + register struct rm *lev; + boolean found, nonwall; + int xmin,xmax,ymin,ymax; + + if(Is_earthlevel(&u.uz)) return; /* everything diggable here */ + + found = nonwall = FALSE; + for(xmin=0; !found; xmin++) { + lev = &levl[xmin][0]; + for(y=0; y<=ROWNO-1; y++, lev++) { + typ = lev->typ; + if(typ != STONE) { + found = TRUE; + if(!IS_WALL(typ)) nonwall = TRUE; + } + } + } + xmin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1; + if (xmin < 0) xmin = 0; + + found = nonwall = FALSE; + for(xmax=COLNO-1; !found; xmax--) { + lev = &levl[xmax][0]; + for(y=0; y<=ROWNO-1; y++, lev++) { + typ = lev->typ; + if(typ != STONE) { + found = TRUE; + if(!IS_WALL(typ)) nonwall = TRUE; + } + } + } + xmax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1; + if (xmax >= COLNO) xmax = COLNO-1; + + found = nonwall = FALSE; + for(ymin=0; !found; ymin++) { + lev = &levl[xmin][ymin]; + for(x=xmin; x<=xmax; x++, lev += ROWNO) { + typ = lev->typ; + if(typ != STONE) { + found = TRUE; + if(!IS_WALL(typ)) nonwall = TRUE; + } + } + } + ymin -= (nonwall || !level.flags.is_maze_lev) ? 2 : 1; + + found = nonwall = FALSE; + for(ymax=ROWNO-1; !found; ymax--) { + lev = &levl[xmin][ymax]; + for(x=xmin; x<=xmax; x++, lev += ROWNO) { + typ = lev->typ; + if(typ != STONE) { + found = TRUE; + if(!IS_WALL(typ)) nonwall = TRUE; + } + } + } + ymax += (nonwall || !level.flags.is_maze_lev) ? 2 : 1; + + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + if (y <= ymin || y >= ymax || x <= xmin || x >= xmax) { +#ifdef DCC30_BUG + lev = &levl[x][y]; + lev->wall_info |= W_NONDIGGABLE; +#else + levl[x][y].wall_info |= W_NONDIGGABLE; +#endif + } +} + +void +mkportal(x, y, todnum, todlevel) +register xchar x, y, todnum, todlevel; +{ + /* a portal "trap" must be matched by a */ + /* portal in the destination dungeon/dlevel */ + register struct trap *ttmp = maketrap(x, y, MAGIC_PORTAL); + + if (!ttmp) { + impossible("portal on top of portal??"); + return; + } +#ifdef DEBUG + pline("mkportal: at (%d,%d), to %s, level %d", + x, y, dungeons[todnum].dname, todlevel); +#endif + ttmp->dst.dnum = todnum; + ttmp->dst.dlevel = todlevel; + return; +} + +/* + * Special waterlevel stuff in endgame (TH). + * + * Some of these functions would probably logically belong to some + * other source files, but they are all so nicely encapsulated here. + */ + +/* to ease the work of debuggers at this stage */ +#define register + +#define CONS_OBJ 0 +#define CONS_MON 1 +#define CONS_HERO 2 +#define CONS_TRAP 3 + +static struct bubble *bbubbles, *ebubbles; + +static struct trap *wportal; +static int xmin, ymin, xmax, ymax; /* level boundaries */ +/* bubble movement boundaries */ +#define bxmin (xmin + 1) +#define bymin (ymin + 1) +#define bxmax (xmax - 1) +#define bymax (ymax - 1) + +STATIC_DCL void NDECL(set_wportal); +STATIC_DCL void FDECL(mk_bubble, (int,int,int)); +STATIC_DCL void FDECL(mv_bubble, (struct bubble *,int,int,BOOLEAN_P)); + +void +movebubbles() +{ + static boolean up; + register struct bubble *b; + register int x, y, i, j; + struct trap *btrap; + static const struct rm water_pos = + { cmap_to_glyph(S_water), WATER, 0, 0, 0, 0, 0, 0, 0 }; + + /* set up the portal the first time bubbles are moved */ + if (!wportal) set_wportal(); + + vision_recalc(2); + /* keep attached ball&chain separate from bubble objects */ + if (Punished) unplacebc(); + + /* + * Pick up everything inside of a bubble then fill all bubble + * locations. + */ + + for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) { + if (b->cons) panic("movebubbles: cons != null"); + for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++) + for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++) + if (b->bm[j + 2] & (1 << i)) { + if (!isok(x,y)) { + impossible("movebubbles: bad pos (%d,%d)", x,y); + continue; + } + + /* pick up objects, monsters, hero, and traps */ + if (OBJ_AT(x,y)) { + struct obj *olist = (struct obj *) 0, *otmp; + struct container *cons = (struct container *) + alloc(sizeof(struct container)); + + while ((otmp = level.objects[x][y]) != 0) { + remove_object(otmp); + otmp->ox = otmp->oy = 0; + otmp->nexthere = olist; + olist = otmp; + } + + cons->x = x; + cons->y = y; + cons->what = CONS_OBJ; + cons->list = (genericptr_t) olist; + cons->next = b->cons; + b->cons = cons; + } + if (MON_AT(x,y)) { + struct monst *mon = m_at(x,y); + struct container *cons = (struct container *) + alloc(sizeof(struct container)); + + cons->x = x; + cons->y = y; + cons->what = CONS_MON; + cons->list = (genericptr_t) mon; + + cons->next = b->cons; + b->cons = cons; + + if(mon->wormno) + remove_worm(mon); + else + remove_monster(x, y); + + newsym(x,y); /* clean up old position */ + mon->mx = mon->my = 0; + } + if (!u.uswallow && x == u.ux && y == u.uy) { + struct container *cons = (struct container *) + alloc(sizeof(struct container)); + + cons->x = x; + cons->y = y; + cons->what = CONS_HERO; + cons->list = (genericptr_t) 0; + + cons->next = b->cons; + b->cons = cons; + } + if ((btrap = t_at(x,y)) != 0) { + struct container *cons = (struct container *) + alloc(sizeof(struct container)); + + cons->x = x; + cons->y = y; + cons->what = CONS_TRAP; + cons->list = (genericptr_t) btrap; + + cons->next = b->cons; + b->cons = cons; + } + + levl[x][y] = water_pos; + block_point(x,y); + } + } + + /* + * Every second time traverse down. This is because otherwise + * all the junk that changes owners when bubbles overlap + * would eventually end up in the last bubble in the chain. + */ + + up = !up; + for (b = up ? bbubbles : ebubbles; b; b = up ? b->next : b->prev) { + register int rx = rn2(3), ry = rn2(3); + + mv_bubble(b,b->dx + 1 - (!b->dx ? rx : (rx ? 1 : 0)), + b->dy + 1 - (!b->dy ? ry : (ry ? 1 : 0)), + FALSE); + } + + /* put attached ball&chain back */ + if (Punished) placebc(); + vision_full_recalc = 1; +} + +/* when moving in water, possibly (1 in 3) alter the intended destination */ +void +water_friction() +{ + register int x, y, dx, dy; + register boolean eff = FALSE; + + if (Swimming && rn2(4)) + return; /* natural swimmers have advantage */ + + if (u.dx && !rn2(!u.dy ? 3 : 6)) { /* 1/3 chance or half that */ + /* cancel delta x and choose an arbitrary delta y value */ + x = u.ux; + do { + dy = rn2(3) - 1; /* -1, 0, 1 */ + y = u.uy + dy; + } while (dy && (!isok(x,y) || !is_pool(x,y))); + u.dx = 0; + u.dy = dy; + eff = TRUE; + } else if (u.dy && !rn2(!u.dx ? 3 : 5)) { /* 1/3 or 1/5*(5/6) */ + /* cancel delta y and choose an arbitrary delta x value */ + y = u.uy; + do { + dx = rn2(3) - 1; /* -1 .. 1 */ + x = u.ux + dx; + } while (dx && (!isok(x,y) || !is_pool(x,y))); + u.dy = 0; + u.dx = dx; + eff = TRUE; + } + if (eff) pline("Water turbulence affects your movements."); +} + +void +save_waterlevel(fd, mode) +int fd, mode; +{ + register struct bubble *b; + + if (!Is_waterlevel(&u.uz)) return; + + if (perform_bwrite(mode)) { + int n = 0; + for (b = bbubbles; b; b = b->next) ++n; + bwrite(fd, (genericptr_t)&n, sizeof (int)); + bwrite(fd, (genericptr_t)&xmin, sizeof (int)); + bwrite(fd, (genericptr_t)&ymin, sizeof (int)); + bwrite(fd, (genericptr_t)&xmax, sizeof (int)); + bwrite(fd, (genericptr_t)&ymax, sizeof (int)); + for (b = bbubbles; b; b = b->next) + bwrite(fd, (genericptr_t)b, sizeof (struct bubble)); + } + if (release_data(mode)) + unsetup_waterlevel(); +} + +void +restore_waterlevel(fd) +register int fd; +{ + register struct bubble *b = (struct bubble *)0, *btmp; + register int i; + int n; + + if (!Is_waterlevel(&u.uz)) return; + + set_wportal(); + mread(fd,(genericptr_t)&n,sizeof(int)); + mread(fd,(genericptr_t)&xmin,sizeof(int)); + mread(fd,(genericptr_t)&ymin,sizeof(int)); + mread(fd,(genericptr_t)&xmax,sizeof(int)); + mread(fd,(genericptr_t)&ymax,sizeof(int)); + for (i = 0; i < n; i++) { + btmp = b; + b = (struct bubble *)alloc(sizeof(struct bubble)); + mread(fd,(genericptr_t)b,sizeof(struct bubble)); + if (bbubbles) { + btmp->next = b; + b->prev = btmp; + } else { + bbubbles = b; + b->prev = (struct bubble *)0; + } + mv_bubble(b,0,0,TRUE); + } + ebubbles = b; + b->next = (struct bubble *)0; + was_waterlevel = TRUE; +} + +const char *waterbody_name(x, y) +xchar x,y; +{ + register struct rm *lev; + schar ltyp; + + if (!isok(x,y)) + return "drink"; /* should never happen */ + lev = &levl[x][y]; + ltyp = lev->typ; + + if (is_lava(x,y)) + return "lava"; + else if (ltyp == ICE || + (ltyp == DRAWBRIDGE_UP && + (levl[x][y].drawbridgemask & DB_UNDER) == DB_ICE)) + return "ice"; + else if (((ltyp != POOL) && (ltyp != WATER) && + !Is_medusa_level(&u.uz) && !Is_waterlevel(&u.uz) && !Is_juiblex_level(&u.uz)) || + (ltyp == DRAWBRIDGE_UP && (levl[x][y].drawbridgemask & DB_UNDER) == DB_MOAT)) + return "moat"; + else if ((ltyp != POOL) && (ltyp != WATER) && Is_juiblex_level(&u.uz)) + return "swamp"; + else if (ltyp == POOL) + return "pool of water"; + else return "water"; +} + +STATIC_OVL void +set_wportal() +{ + /* there better be only one magic portal on water level... */ + for (wportal = ftrap; wportal; wportal = wportal->ntrap) + if (wportal->ttyp == MAGIC_PORTAL) return; + impossible("set_wportal(): no portal!"); +} + +STATIC_OVL void +setup_waterlevel() +{ + register int x, y; + register int xskip, yskip; + register int water_glyph = cmap_to_glyph(S_water); + + /* ouch, hardcoded... */ + + xmin = 3; + ymin = 1; + xmax = 78; + ymax = 20; + + /* set hero's memory to water */ + + for (x = xmin; x <= xmax; x++) + for (y = ymin; y <= ymax; y++) + levl[x][y].glyph = water_glyph; + + /* make bubbles */ + + xskip = 10 + rn2(10); + yskip = 4 + rn2(4); + for (x = bxmin; x <= bxmax; x += xskip) + for (y = bymin; y <= bymax; y += yskip) + mk_bubble(x,y,rn2(7)); +} + +STATIC_OVL void +unsetup_waterlevel() +{ + register struct bubble *b, *bb; + + /* free bubbles */ + + for (b = bbubbles; b; b = bb) { + bb = b->next; + free((genericptr_t)b); + } + bbubbles = ebubbles = (struct bubble *)0; +} + +STATIC_OVL void +mk_bubble(x,y,n) +register int x, y, n; +{ + /* + * These bit masks make visually pleasing bubbles on a normal aspect + * 25x80 terminal, which naturally results in them being mathematically + * anything but symmetric. For this reason they cannot be computed + * in situ, either. The first two elements tell the dimensions of + * the bubble's bounding box. + */ + static uchar + bm2[] = {2,1,0x3}, + bm3[] = {3,2,0x7,0x7}, + bm4[] = {4,3,0x6,0xf,0x6}, + bm5[] = {5,3,0xe,0x1f,0xe}, + bm6[] = {6,4,0x1e,0x3f,0x3f,0x1e}, + bm7[] = {7,4,0x3e,0x7f,0x7f,0x3e}, + bm8[] = {8,4,0x7e,0xff,0xff,0x7e}, + *bmask[] = {bm2,bm3,bm4,bm5,bm6,bm7,bm8}; + + register struct bubble *b; + + if (x >= bxmax || y >= bymax) return; + if (n >= SIZE(bmask)) { + impossible("n too large (mk_bubble)"); + n = SIZE(bmask) - 1; + } + b = (struct bubble *)alloc(sizeof(struct bubble)); + if ((x + (int) bmask[n][0] - 1) > bxmax) x = bxmax - bmask[n][0] + 1; + if ((y + (int) bmask[n][1] - 1) > bymax) y = bymax - bmask[n][1] + 1; + b->x = x; + b->y = y; + b->dx = 1 - rn2(3); + b->dy = 1 - rn2(3); + b->bm = bmask[n]; + b->cons = 0; + if (!bbubbles) bbubbles = b; + if (ebubbles) { + ebubbles->next = b; + b->prev = ebubbles; + } + else + b->prev = (struct bubble *)0; + b->next = (struct bubble *)0; + ebubbles = b; + mv_bubble(b,0,0,TRUE); +} + +/* + * The player, the portal and all other objects and monsters + * float along with their associated bubbles. Bubbles may overlap + * freely, and the contents may get associated with other bubbles in + * the process. Bubbles are "sticky", meaning that if the player is + * in the immediate neighborhood of one, he/she may get sucked inside. + * This property also makes leaving a bubble slightly difficult. + */ +STATIC_OVL void +mv_bubble(b,dx,dy,ini) +register struct bubble *b; +register int dx, dy; +register boolean ini; +{ + register int x, y, i, j, colli = 0; + struct container *cons, *ctemp; + + /* move bubble */ + if (dx < -1 || dx > 1 || dy < -1 || dy > 1) { + /* pline("mv_bubble: dx = %d, dy = %d", dx, dy); */ + dx = sgn(dx); + dy = sgn(dy); + } + + /* + * collision with level borders? + * 1 = horizontal border, 2 = vertical, 3 = corner + */ + if (b->x <= bxmin) colli |= 2; + if (b->y <= bymin) colli |= 1; + if ((int) (b->x + b->bm[0] - 1) >= bxmax) colli |= 2; + if ((int) (b->y + b->bm[1] - 1) >= bymax) colli |= 1; + + if (b->x < bxmin) { + pline("bubble xmin: x = %d, xmin = %d", b->x, bxmin); + b->x = bxmin; + } + if (b->y < bymin) { + pline("bubble ymin: y = %d, ymin = %d", b->y, bymin); + b->y = bymin; + } + if ((int) (b->x + b->bm[0] - 1) > bxmax) { + pline("bubble xmax: x = %d, xmax = %d", + b->x + b->bm[0] - 1, bxmax); + b->x = bxmax - b->bm[0] + 1; + } + if ((int) (b->y + b->bm[1] - 1) > bymax) { + pline("bubble ymax: y = %d, ymax = %d", + b->y + b->bm[1] - 1, bymax); + b->y = bymax - b->bm[1] + 1; + } + + /* bounce if we're trying to move off the border */ + if (b->x == bxmin && dx < 0) dx = -dx; + if (b->x + b->bm[0] - 1 == bxmax && dx > 0) dx = -dx; + if (b->y == bymin && dy < 0) dy = -dy; + if (b->y + b->bm[1] - 1 == bymax && dy > 0) dy = -dy; + + b->x += dx; + b->y += dy; + + /* void positions inside bubble */ + + for (i = 0, x = b->x; i < (int) b->bm[0]; i++, x++) + for (j = 0, y = b->y; j < (int) b->bm[1]; j++, y++) + if (b->bm[j + 2] & (1 << i)) { + levl[x][y].typ = AIR; + levl[x][y].lit = 1; + unblock_point(x,y); + } + + /* replace contents of bubble */ + for (cons = b->cons; cons; cons = ctemp) { + ctemp = cons->next; + cons->x += dx; + cons->y += dy; + + switch(cons->what) { + case CONS_OBJ: { + struct obj *olist, *otmp; + + for (olist=(struct obj *)cons->list; olist; olist=otmp) { + otmp = olist->nexthere; + place_object(olist, cons->x, cons->y); + } + break; + } + + case CONS_MON: { + struct monst *mon = (struct monst *) cons->list; + (void) mnearto(mon, cons->x, cons->y, TRUE); + break; + } + + case CONS_HERO: { + int ux0 = u.ux, uy0 = u.uy; + + /* change u.ux0 and u.uy0? */ + u.ux = cons->x; + u.uy = cons->y; + newsym(ux0, uy0); /* clean up old position */ + + if (MON_AT(cons->x, cons->y)) { + mnexto(m_at(cons->x,cons->y)); + } + break; + } + + case CONS_TRAP: { + struct trap *btrap = (struct trap *) cons->list; + btrap->tx = cons->x; + btrap->ty = cons->y; + break; + } + + default: + impossible("mv_bubble: unknown bubble contents"); + break; + } + free((genericptr_t)cons); + } + b->cons = 0; + + /* boing? */ + + switch (colli) { + case 1: b->dy = -b->dy; break; + case 3: b->dy = -b->dy; /* fall through */ + case 2: b->dx = -b->dx; break; + default: + /* sometimes alter direction for fun anyway + (higher probability for stationary bubbles) */ + if (!ini && ((b->dx || b->dy) ? !rn2(20) : !rn2(5))) { + b->dx = 1 - rn2(3); + b->dy = 1 - rn2(3); + } + } +} + +/*mkmaze.c*/ diff --git a/src/mkobj.c b/src/mkobj.c new file mode 100644 index 0000000..3db4c0b --- /dev/null +++ b/src/mkobj.c @@ -0,0 +1,1639 @@ +/* SCCS Id: @(#)mkobj.c 3.4 2002/10/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "prop.h" + +STATIC_DCL void FDECL(mkbox_cnts,(struct obj *)); +STATIC_DCL void FDECL(obj_timer_checks,(struct obj *, XCHAR_P, XCHAR_P, int)); +#ifdef OVL1 +STATIC_DCL void FDECL(container_weight, (struct obj *)); +STATIC_DCL struct obj *FDECL(save_mtraits, (struct obj *, struct monst *)); +#ifdef WIZARD +STATIC_DCL const char *FDECL(where_name, (int)); +STATIC_DCL void FDECL(check_contained, (struct obj *,const char *)); +#endif +#endif /* OVL1 */ + +extern struct obj *thrownobj; /* defined in dothrow.c */ + +/*#define DEBUG_EFFECTS*/ /* show some messages for debugging */ + +struct icp { + int iprob; /* probability of an item type */ + char iclass; /* item class */ +}; + +#ifdef OVL1 + +const struct icp mkobjprobs[] = { +{10, WEAPON_CLASS}, +{10, ARMOR_CLASS}, +{20, FOOD_CLASS}, +{ 8, TOOL_CLASS}, +{ 8, GEM_CLASS}, +{16, POTION_CLASS}, +{16, SCROLL_CLASS}, +{ 4, SPBOOK_CLASS}, +{ 4, WAND_CLASS}, +{ 3, RING_CLASS}, +{ 1, AMULET_CLASS} +}; + +const struct icp boxiprobs[] = { +{18, GEM_CLASS}, +{15, FOOD_CLASS}, +{18, POTION_CLASS}, +{18, SCROLL_CLASS}, +{12, SPBOOK_CLASS}, +{ 7, COIN_CLASS}, +{ 6, WAND_CLASS}, +{ 5, RING_CLASS}, +{ 1, AMULET_CLASS} +}; + +#ifdef REINCARNATION +const struct icp rogueprobs[] = { +{12, WEAPON_CLASS}, +{12, ARMOR_CLASS}, +{22, FOOD_CLASS}, +{22, POTION_CLASS}, +{22, SCROLL_CLASS}, +{ 5, WAND_CLASS}, +{ 5, RING_CLASS} +}; +#endif + +const struct icp hellprobs[] = { +{20, WEAPON_CLASS}, +{20, ARMOR_CLASS}, +{16, FOOD_CLASS}, +{12, TOOL_CLASS}, +{10, GEM_CLASS}, +{ 1, POTION_CLASS}, +{ 1, SCROLL_CLASS}, +{ 8, WAND_CLASS}, +{ 8, RING_CLASS}, +{ 4, AMULET_CLASS} +}; + +struct obj * +mkobj_at(let, x, y, artif) +char let; +int x, y; +boolean artif; +{ + struct obj *otmp; + + otmp = mkobj(let, artif); + place_object(otmp, x, y); + return(otmp); +} + +struct obj * +mksobj_at(otyp, x, y, init, artif) +int otyp, x, y; +boolean init, artif; +{ + struct obj *otmp; + + otmp = mksobj(otyp, init, artif); + place_object(otmp, x, y); + return(otmp); +} + +struct obj * +mkobj(oclass, artif) +char oclass; +boolean artif; +{ + int tprob, i, prob = rnd(1000); + + if(oclass == RANDOM_CLASS) { + const struct icp *iprobs = +#ifdef REINCARNATION + (Is_rogue_level(&u.uz)) ? + (const struct icp *)rogueprobs : +#endif + Inhell ? (const struct icp *)hellprobs : + (const struct icp *)mkobjprobs; + + for(tprob = rnd(100); + (tprob -= iprobs->iprob) > 0; + iprobs++); + oclass = iprobs->iclass; + } + + i = bases[(int)oclass]; + while((prob -= objects[i].oc_prob) > 0) i++; + + if(objects[i].oc_class != oclass || !OBJ_NAME(objects[i])) + panic("probtype error, oclass=%d i=%d", (int) oclass, i); + + return(mksobj(i, TRUE, artif)); +} + +STATIC_OVL void +mkbox_cnts(box) +struct obj *box; +{ + register int n; + register struct obj *otmp; + + box->cobj = (struct obj *) 0; + + switch (box->otyp) { + case ICE_BOX: n = 20; break; + case CHEST: n = 5; break; + case LARGE_BOX: n = 3; break; + case SACK: + case OILSKIN_SACK: + /* initial inventory: sack starts out empty */ + if (moves <= 1 && !in_mklev) { n = 0; break; } + /*else FALLTHRU*/ + case BAG_OF_HOLDING: n = 1; break; + default: n = 0; break; + } + + for (n = rn2(n+1); n > 0; n--) { + if (box->otyp == ICE_BOX) { + if (!(otmp = mksobj(CORPSE, TRUE, TRUE))) continue; + /* Note: setting age to 0 is correct. Age has a different + * from usual meaning for objects stored in ice boxes. -KAA + */ + otmp->age = 0L; + if (otmp->timed) { + (void) stop_timer(ROT_CORPSE, (genericptr_t)otmp); + (void) stop_timer(REVIVE_MON, (genericptr_t)otmp); + } + } else { + register int tprob; + const struct icp *iprobs = boxiprobs; + + for (tprob = rnd(100); (tprob -= iprobs->iprob) > 0; iprobs++) + ; + if (!(otmp = mkobj(iprobs->iclass, TRUE))) continue; + + /* handle a couple of special cases */ + if (otmp->oclass == COIN_CLASS) { + /* 2.5 x level's usual amount; weight adjusted below */ + otmp->quan = (long)(rnd(level_difficulty()+2) * rnd(75)); + otmp->owt = weight(otmp); + } else while (otmp->otyp == ROCK) { + otmp->otyp = rnd_class(DILITHIUM_CRYSTAL, LOADSTONE); + if (otmp->quan > 2L) otmp->quan = 1L; + otmp->owt = weight(otmp); + } + if (box->otyp == BAG_OF_HOLDING) { + if (Is_mbag(otmp)) { + otmp->otyp = SACK; + otmp->spe = 0; + otmp->owt = weight(otmp); + } else while (otmp->otyp == WAN_CANCELLATION) + otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING); + } + } + (void) add_to_container(box, otmp); + } +} + +int +rndmonnum() /* select a random, common monster type */ +{ + register struct permonst *ptr; + register int i; + + /* Plan A: get a level-appropriate common monster */ + ptr = rndmonst(); + if (ptr) return(monsndx(ptr)); + + /* Plan B: get any common monster */ + do { + i = rn1(SPECIAL_PM - LOW_PM, LOW_PM); + ptr = &mons[i]; + } while((ptr->geno & G_NOGEN) || (!Inhell && (ptr->geno & G_HELL))); + + return(i); +} + +/* + * Split obj so that it gets size gets reduced by num. The quantity num is + * put in the object structure delivered by this call. The returned object + * has its wornmask cleared and is positioned just following the original + * in the nobj chain (and nexthere chain when on the floor). + */ +struct obj * +splitobj(obj, num) +struct obj *obj; +long num; +{ + struct obj *otmp; + + if (obj->cobj || num <= 0L || obj->quan <= num) + panic("splitobj"); /* can't split containers */ + otmp = newobj(obj->oxlth + obj->onamelth); + *otmp = *obj; /* copies whole structure */ + otmp->o_id = flags.ident++; + if (!otmp->o_id) otmp->o_id = flags.ident++; /* ident overflowed */ + otmp->timed = 0; /* not timed, yet */ + otmp->lamplit = 0; /* ditto */ + otmp->owornmask = 0L; /* new object isn't worn */ + obj->quan -= num; + obj->owt = weight(obj); + otmp->quan = num; + otmp->owt = weight(otmp); /* -= obj->owt ? */ + obj->nobj = otmp; + /* Only set nexthere when on the floor, nexthere is also used */ + /* as a back pointer to the container object when contained. */ + if (obj->where == OBJ_FLOOR) + obj->nexthere = otmp; + if (obj->oxlth) + (void)memcpy((genericptr_t)otmp->oextra, (genericptr_t)obj->oextra, + obj->oxlth); + if (obj->onamelth) + (void)strncpy(ONAME(otmp), ONAME(obj), (int)obj->onamelth); + if (obj->unpaid) splitbill(obj,otmp); + if (obj->timed) obj_split_timers(obj, otmp); + if (obj_sheds_light(obj)) obj_split_light_source(obj, otmp); + return otmp; +} + +/* + * Insert otmp right after obj in whatever chain(s) it is on. Then extract + * obj from the chain(s). This function does a literal swap. It is up to + * the caller to provide a valid context for the swap. When done, obj will + * still exist, but not on any chain. + * + * Note: Don't use use obj_extract_self() -- we are doing an in-place swap, + * not actually moving something. + */ +void +replace_object(obj, otmp) +struct obj *obj; +struct obj *otmp; +{ + otmp->where = obj->where; + switch (obj->where) { + case OBJ_FREE: + /* do nothing */ + break; + case OBJ_INVENT: + otmp->nobj = obj->nobj; + obj->nobj = otmp; + extract_nobj(obj, &invent); + break; + case OBJ_CONTAINED: + otmp->nobj = obj->nobj; + otmp->ocontainer = obj->ocontainer; + obj->nobj = otmp; + extract_nobj(obj, &obj->ocontainer->cobj); + break; + case OBJ_MINVENT: + otmp->nobj = obj->nobj; + otmp->ocarry = obj->ocarry; + obj->nobj = otmp; + extract_nobj(obj, &obj->ocarry->minvent); + break; + case OBJ_FLOOR: + otmp->nobj = obj->nobj; + otmp->nexthere = obj->nexthere; + otmp->ox = obj->ox; + otmp->oy = obj->oy; + obj->nobj = otmp; + obj->nexthere = otmp; + extract_nobj(obj, &fobj); + extract_nexthere(obj, &level.objects[obj->ox][obj->oy]); + break; + default: + panic("replace_object: obj position"); + break; + } +} + +/* + * Create a dummy duplicate to put on shop bill. The duplicate exists + * only in the billobjs chain. This function is used when a shop object + * is being altered, and a copy of the original is needed for billing + * purposes. For example, when eating, where an interruption will yield + * an object which is different from what it started out as; the "I x" + * command needs to display the original object. + * + * The caller is responsible for checking otmp->unpaid and + * costly_spot(u.ux, u.uy). This function will make otmp no charge. + * + * Note that check_unpaid_usage() should be used instead for partial + * usage of an object. + */ +void +bill_dummy_object(otmp) +register struct obj *otmp; +{ + register struct obj *dummy; + + if (otmp->unpaid) + subfrombill(otmp, shop_keeper(*u.ushops)); + dummy = newobj(otmp->oxlth + otmp->onamelth); + *dummy = *otmp; + dummy->where = OBJ_FREE; + dummy->o_id = flags.ident++; + if (!dummy->o_id) dummy->o_id = flags.ident++; /* ident overflowed */ + dummy->timed = 0; + if (otmp->oxlth) + (void)memcpy((genericptr_t)dummy->oextra, + (genericptr_t)otmp->oextra, otmp->oxlth); + if (otmp->onamelth) + (void)strncpy(ONAME(dummy), ONAME(otmp), (int)otmp->onamelth); + if (Is_candle(dummy)) dummy->lamplit = 0; + addtobill(dummy, FALSE, TRUE, TRUE); + otmp->no_charge = 1; + otmp->unpaid = 0; + return; +} + +#endif /* OVL1 */ +#ifdef OVLB + +static const char dknowns[] = { + WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS, + GEM_CLASS, SPBOOK_CLASS, WEAPON_CLASS, TOOL_CLASS, 0 +}; + +struct obj * +mksobj(otyp, init, artif) +int otyp; +boolean init; +boolean artif; +{ + int mndx, tryct; + struct obj *otmp; + char let = objects[otyp].oc_class; + + otmp = newobj(0); + *otmp = zeroobj; + otmp->age = monstermoves; + otmp->o_id = flags.ident++; + if (!otmp->o_id) otmp->o_id = flags.ident++; /* ident overflowed */ + otmp->quan = 1L; + otmp->oclass = let; + otmp->otyp = otyp; + otmp->where = OBJ_FREE; + otmp->dknown = index(dknowns, let) ? 0 : 1; + if ((otmp->otyp >= ELVEN_SHIELD && otmp->otyp <= ORCISH_SHIELD) || + otmp->otyp == SHIELD_OF_REFLECTION) + otmp->dknown = 0; + if (!objects[otmp->otyp].oc_uses_known) + otmp->known = 1; +#ifdef INVISIBLE_OBJECTS + otmp->oinvis = !rn2(1250); +#endif + if (init) switch (let) { + case WEAPON_CLASS: + otmp->quan = is_multigen(otmp) ? (long) rn1(6,6) : 1L; + if(!rn2(11)) { + otmp->spe = rne(3); + otmp->blessed = rn2(2); + } else if(!rn2(10)) { + curse(otmp); + otmp->spe = -rne(3); + } else blessorcurse(otmp, 10); + if (is_poisonable(otmp) && !rn2(100)) + otmp->opoisoned = 1; + + if (artif && !rn2(20)) + otmp = mk_artifact(otmp, (aligntyp)A_NONE); + break; + case FOOD_CLASS: + otmp->oeaten = 0; + switch(otmp->otyp) { + case CORPSE: + /* possibly overridden by mkcorpstat() */ + tryct = 50; + do otmp->corpsenm = undead_to_corpse(rndmonnum()); + while ((mvitals[otmp->corpsenm].mvflags & G_NOCORPSE) && (--tryct > 0)); + if (tryct == 0) { + /* perhaps rndmonnum() only wants to make G_NOCORPSE monsters on + this level; let's create an adventurer's corpse instead, then */ + otmp->corpsenm = PM_HUMAN; + } + /* timer set below */ + break; + case EGG: + otmp->corpsenm = NON_PM; /* generic egg */ + if (!rn2(3)) for (tryct = 200; tryct > 0; --tryct) { + mndx = can_be_hatched(rndmonnum()); + if (mndx != NON_PM && !dead_species(mndx, TRUE)) { + otmp->corpsenm = mndx; /* typed egg */ + attach_egg_hatch_timeout(otmp); + break; + } + } + break; + case TIN: + otmp->corpsenm = NON_PM; /* empty (so far) */ + if (!rn2(6)) + otmp->spe = 1; /* spinach */ + else for (tryct = 200; tryct > 0; --tryct) { + mndx = undead_to_corpse(rndmonnum()); + if (mons[mndx].cnutrit && + !(mvitals[mndx].mvflags & G_NOCORPSE)) { + otmp->corpsenm = mndx; + break; + } + } + blessorcurse(otmp, 10); + break; + case SLIME_MOLD: + otmp->spe = current_fruit; + break; + case KELP_FROND: + otmp->quan = (long) rnd(2); + break; + } + if (otmp->otyp == CORPSE || otmp->otyp == MEAT_RING || + otmp->otyp == KELP_FROND) break; + /* fall into next case */ + + case GEM_CLASS: + if (otmp->otyp == LOADSTONE) curse(otmp); + else if (otmp->otyp == ROCK) otmp->quan = (long) rn1(6,6); + else if (otmp->otyp != LUCKSTONE && !rn2(6)) otmp->quan = 2L; + else otmp->quan = 1L; + break; + case TOOL_CLASS: + switch(otmp->otyp) { + case TALLOW_CANDLE: + case WAX_CANDLE: otmp->spe = 1; + otmp->age = 20L * /* 400 or 200 */ + (long)objects[otmp->otyp].oc_cost; + otmp->lamplit = 0; + otmp->quan = 1L + + (long)(rn2(2) ? rn2(7) : 0); + blessorcurse(otmp, 5); + break; + case BRASS_LANTERN: + case OIL_LAMP: otmp->spe = 1; + otmp->age = (long) rn1(500,1000); + otmp->lamplit = 0; + blessorcurse(otmp, 5); + break; + case MAGIC_LAMP: otmp->spe = 1; + otmp->lamplit = 0; + blessorcurse(otmp, 2); + break; + case CHEST: + case LARGE_BOX: otmp->olocked = !!(rn2(5)); + otmp->otrapped = !(rn2(10)); + case ICE_BOX: + case SACK: + case OILSKIN_SACK: + case BAG_OF_HOLDING: mkbox_cnts(otmp); + break; +#ifdef TOURIST + case EXPENSIVE_CAMERA: +#endif + case TINNING_KIT: + case MAGIC_MARKER: otmp->spe = rn1(70,30); + break; + case CAN_OF_GREASE: otmp->spe = rnd(25); + blessorcurse(otmp, 10); + break; + case CRYSTAL_BALL: otmp->spe = rnd(5); + blessorcurse(otmp, 2); + break; + case HORN_OF_PLENTY: + case BAG_OF_TRICKS: otmp->spe = rnd(20); + break; + case FIGURINE: { int tryct2 = 0; + do + otmp->corpsenm = rndmonnum(); + while(is_human(&mons[otmp->corpsenm]) + && tryct2++ < 30); + blessorcurse(otmp, 4); + break; + } + case BELL_OF_OPENING: otmp->spe = 3; + break; + case MAGIC_FLUTE: + case MAGIC_HARP: + case FROST_HORN: + case FIRE_HORN: + case DRUM_OF_EARTHQUAKE: + otmp->spe = rn1(5,4); + break; + } + break; + case AMULET_CLASS: + if (otmp->otyp == AMULET_OF_YENDOR) flags.made_amulet = TRUE; + if(rn2(10) && (otmp->otyp == AMULET_OF_STRANGULATION || + otmp->otyp == AMULET_OF_CHANGE || + otmp->otyp == AMULET_OF_RESTFUL_SLEEP)) { + curse(otmp); + } else blessorcurse(otmp, 10); + case VENOM_CLASS: + case CHAIN_CLASS: + case BALL_CLASS: + break; + case POTION_CLASS: + if (otmp->otyp == POT_OIL) + otmp->age = MAX_OIL_IN_FLASK; /* amount of oil */ + /* fall through */ + case SCROLL_CLASS: +#ifdef MAIL + if (otmp->otyp != SCR_MAIL) +#endif + blessorcurse(otmp, 4); + break; + case SPBOOK_CLASS: + blessorcurse(otmp, 17); + break; + case ARMOR_CLASS: + if(rn2(10) && (otmp->otyp == FUMBLE_BOOTS || + otmp->otyp == LEVITATION_BOOTS || + otmp->otyp == HELM_OF_OPPOSITE_ALIGNMENT || + otmp->otyp == GAUNTLETS_OF_FUMBLING || + !rn2(11))) { + curse(otmp); + otmp->spe = -rne(3); + } else if(!rn2(10)) { + otmp->blessed = rn2(2); + otmp->spe = rne(3); + } else blessorcurse(otmp, 10); + if (artif && !rn2(40)) + otmp = mk_artifact(otmp, (aligntyp)A_NONE); + /* simulate lacquered armor for samurai */ + if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL && + (moves <= 1 || In_quest(&u.uz))) { +#ifdef UNIXPC + /* optimizer bitfield bug */ + otmp->oerodeproof = 1; + otmp->rknown = 1; +#else + otmp->oerodeproof = otmp->rknown = 1; +#endif + } + break; + case WAND_CLASS: + if(otmp->otyp == WAN_WISHING) otmp->spe = rnd(3); else + otmp->spe = rn1(5, + (objects[otmp->otyp].oc_dir == NODIR) ? 11 : 4); + blessorcurse(otmp, 17); + otmp->recharged = 0; /* used to control recharging */ + break; + case RING_CLASS: + if(objects[otmp->otyp].oc_charged) { + blessorcurse(otmp, 3); + if(rn2(10)) { + if(rn2(10) && bcsign(otmp)) + otmp->spe = bcsign(otmp) * rne(3); + else otmp->spe = rn2(2) ? rne(3) : -rne(3); + } + /* make useless +0 rings much less common */ + if (otmp->spe == 0) otmp->spe = rn2(4) - rn2(3); + /* negative rings are usually cursed */ + if (otmp->spe < 0 && rn2(5)) curse(otmp); + } else if(rn2(10) && (otmp->otyp == RIN_TELEPORTATION || + otmp->otyp == RIN_POLYMORPH || + otmp->otyp == RIN_AGGRAVATE_MONSTER || + otmp->otyp == RIN_HUNGER || !rn2(9))) { + curse(otmp); + } + break; + case ROCK_CLASS: + switch (otmp->otyp) { + case STATUE: + /* possibly overridden by mkcorpstat() */ + otmp->corpsenm = rndmonnum(); + if (!verysmall(&mons[otmp->corpsenm]) && + rn2(level_difficulty()/2 + 10) > 10) + (void) add_to_container(otmp, + mkobj(SPBOOK_CLASS,FALSE)); + } + break; + case COIN_CLASS: + break; /* do nothing */ + default: + impossible("impossible mkobj %d, sym '%c'.", otmp->otyp, + objects[otmp->otyp].oc_class); + return (struct obj *)0; + } + + /* Some things must get done (timers) even if init = 0 */ + switch (otmp->otyp) { + case CORPSE: + start_corpse_timeout(otmp); + break; + } + + /* unique objects may have an associated artifact entry */ + if (objects[otyp].oc_unique && !otmp->oartifact) + otmp = mk_artifact(otmp, (aligntyp)A_NONE); + otmp->owt = weight(otmp); + return(otmp); +} + +/* + * Start a corpse decay or revive timer. + * This takes the age of the corpse into consideration as of 3.4.0. + */ +void +start_corpse_timeout(body) + struct obj *body; +{ + long when; /* rot away when this old */ + long corpse_age; /* age of corpse */ + int rot_adjust; + short action; + +#define TAINT_AGE (50L) /* age when corpses go bad */ +#define TROLL_REVIVE_CHANCE 37 /* 1/37 chance for 50 turns ~ 75% chance */ +#define ROT_AGE (250L) /* age when corpses rot away */ + + /* lizards and lichen don't rot or revive */ + if (body->corpsenm == PM_LIZARD || body->corpsenm == PM_LICHEN) return; + + action = ROT_CORPSE; /* default action: rot away */ + rot_adjust = in_mklev ? 25 : 10; /* give some variation */ + corpse_age = monstermoves - body->age; + if (corpse_age > ROT_AGE) + when = rot_adjust; + else + when = ROT_AGE - corpse_age; + when += (long)(rnz(rot_adjust) - rot_adjust); + + if (is_rider(&mons[body->corpsenm])) { + /* + * Riders always revive. They have a 1/3 chance per turn + * of reviving after 12 turns. Always revive by 500. + */ + action = REVIVE_MON; + for (when = 12L; when < 500L; when++) + if (!rn2(3)) break; + + } else if (mons[body->corpsenm].mlet == S_TROLL && !body->norevive) { + long age; + for (age = 2; age <= TAINT_AGE; age++) + if (!rn2(TROLL_REVIVE_CHANCE)) { /* troll revives */ + action = REVIVE_MON; + when = age; + break; + } + } + + if (body->norevive) body->norevive = 0; + (void) start_timer(when, TIMER_OBJECT, action, (genericptr_t)body); +} + +void +bless(otmp) +register struct obj *otmp; +{ +#ifdef GOLDOBJ + if (otmp->oclass == COIN_CLASS) return; +#endif + otmp->cursed = 0; + otmp->blessed = 1; + if (carried(otmp) && confers_luck(otmp)) + set_moreluck(); + else if (otmp->otyp == BAG_OF_HOLDING) + otmp->owt = weight(otmp); + else if (otmp->otyp == FIGURINE && otmp->timed) + (void) stop_timer(FIG_TRANSFORM, (genericptr_t) otmp); + return; +} + +void +unbless(otmp) +register struct obj *otmp; +{ + otmp->blessed = 0; + if (carried(otmp) && confers_luck(otmp)) + set_moreluck(); + else if (otmp->otyp == BAG_OF_HOLDING) + otmp->owt = weight(otmp); +} + +void +curse(otmp) +register struct obj *otmp; +{ +#ifdef GOLDOBJ + if (otmp->oclass == COIN_CLASS) return; +#endif + otmp->blessed = 0; + otmp->cursed = 1; + /* welded two-handed weapon interferes with some armor removal */ + if (otmp == uwep && bimanual(uwep)) reset_remarm(); + /* rules at top of wield.c state that twoweapon cannot be done + with cursed alternate weapon */ + if (otmp == uswapwep && u.twoweap) + drop_uswapwep(); + /* some cursed items need immediate updating */ + if (carried(otmp) && confers_luck(otmp)) + set_moreluck(); + else if (otmp->otyp == BAG_OF_HOLDING) + otmp->owt = weight(otmp); + else if (otmp->otyp == FIGURINE) { + if (otmp->corpsenm != NON_PM + && !dead_species(otmp->corpsenm,TRUE) + && (carried(otmp) || mcarried(otmp))) + attach_fig_transform_timeout(otmp); + } + return; +} + +void +uncurse(otmp) +register struct obj *otmp; +{ + otmp->cursed = 0; + if (carried(otmp) && confers_luck(otmp)) + set_moreluck(); + else if (otmp->otyp == BAG_OF_HOLDING) + otmp->owt = weight(otmp); + else if (otmp->otyp == FIGURINE && otmp->timed) + (void) stop_timer(FIG_TRANSFORM, (genericptr_t) otmp); + return; +} + +#endif /* OVLB */ +#ifdef OVL1 + +void +blessorcurse(otmp, chance) +register struct obj *otmp; +register int chance; +{ + if(otmp->blessed || otmp->cursed) return; + + if(!rn2(chance)) { + if(!rn2(2)) { + curse(otmp); + } else { + bless(otmp); + } + } + return; +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +bcsign(otmp) +register struct obj *otmp; +{ + return(!!otmp->blessed - !!otmp->cursed); +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* + * Calculate the weight of the given object. This will recursively follow + * and calculate the weight of any containers. + * + * Note: It is possible to end up with an incorrect weight if some part + * of the code messes with a contained object and doesn't update the + * container's weight. + */ +int +weight(obj) +register struct obj *obj; +{ + int wt = objects[obj->otyp].oc_weight; + + if (obj->otyp == LARGE_BOX && obj->spe == 1) /* Schroedinger's Cat */ + wt += mons[PM_HOUSECAT].cwt; + if (Is_container(obj) || obj->otyp == STATUE) { + struct obj *contents; + register int cwt = 0; + + if (obj->otyp == STATUE && obj->corpsenm >= LOW_PM) + wt = (int)obj->quan * + ((int)mons[obj->corpsenm].cwt * 3 / 2); + + for(contents=obj->cobj; contents; contents=contents->nobj) + cwt += weight(contents); + /* + * The weight of bags of holding is calculated as the weight + * of the bag plus the weight of the bag's contents modified + * as follows: + * + * Bag status Weight of contents + * ---------- ------------------ + * cursed 2x + * blessed x/4 + 1 + * otherwise x/2 + 1 + * + * The macro DELTA_CWT in pickup.c also implements these + * weight equations. + * + * Note: The above checks are performed in the given order. + * this means that if an object is both blessed and + * cursed (not supposed to happen), it will be treated + * as cursed. + */ + if (obj->otyp == BAG_OF_HOLDING) + cwt = obj->cursed ? (cwt * 2) : + (1 + (cwt / (obj->blessed ? 4 : 2))); + + return wt + cwt; + } + if (obj->otyp == CORPSE && obj->corpsenm >= LOW_PM) { + long long_wt = obj->quan * (long) mons[obj->corpsenm].cwt; + + wt = (long_wt > LARGEST_INT) ? LARGEST_INT : (int)long_wt; + if (obj->oeaten) wt = eaten_stat(wt, obj); + return wt; + } else if (obj->oclass == FOOD_CLASS && obj->oeaten) { + return eaten_stat((int)obj->quan * wt, obj); + } else if (obj->oclass == COIN_CLASS) + return (int)((obj->quan + 50L) / 100L); + else if (obj->otyp == HEAVY_IRON_BALL && obj->owt != 0) + return((int)(obj->owt)); /* kludge for "very" heavy iron ball */ + return(wt ? wt*(int)obj->quan : ((int)obj->quan + 1)>>1); +} + +static int treefruits[] = {APPLE,ORANGE,PEAR,BANANA,EUCALYPTUS_LEAF}; + +struct obj * +rnd_treefruit_at(x,y) +int x, y; +{ + return mksobj_at(treefruits[rn2(SIZE(treefruits))], x, y, TRUE, FALSE); +} +#endif /* OVL0 */ +#ifdef OVLB + +struct obj * +mkgold(amount, x, y) +long amount; +int x, y; +{ + register struct obj *gold = g_at(x,y); + + if (amount <= 0L) + amount = (long)(1 + rnd(level_difficulty()+2) * rnd(30)); + if (gold) { + gold->quan += amount; + } else { + gold = mksobj_at(GOLD_PIECE, x, y, TRUE, FALSE); + gold->quan = amount; + } + gold->owt = weight(gold); + return (gold); +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* return TRUE if the corpse has special timing */ +#define special_corpse(num) (((num) == PM_LIZARD) \ + || ((num) == PM_LICHEN) \ + || (is_rider(&mons[num])) \ + || (mons[num].mlet == S_TROLL)) + +/* + * OEXTRA note: Passing mtmp causes mtraits to be saved + * even if ptr passed as well, but ptr is always used for + * the corpse type (corpsenm). That allows the corpse type + * to be different from the original monster, + * i.e. vampire -> human corpse + * yet still allow restoration of the original monster upon + * resurrection. + */ +struct obj * +mkcorpstat(objtype, mtmp, ptr, x, y, init) +int objtype; /* CORPSE or STATUE */ +struct monst *mtmp; +struct permonst *ptr; +int x, y; +boolean init; +{ + register struct obj *otmp; + + if (objtype != CORPSE && objtype != STATUE) + impossible("making corpstat type %d", objtype); + if (x == 0 && y == 0) { /* special case - random placement */ + otmp = mksobj(objtype, init, FALSE); + if (otmp) rloco(otmp); + } else + otmp = mksobj_at(objtype, x, y, init, FALSE); + if (otmp) { + if (mtmp) { + struct obj *otmp2; + + if (!ptr) ptr = mtmp->data; + /* save_mtraits frees original data pointed to by otmp */ + otmp2 = save_mtraits(otmp, mtmp); + if (otmp2) otmp = otmp2; + } + /* use the corpse or statue produced by mksobj() as-is + unless `ptr' is non-null */ + if (ptr) { + int old_corpsenm = otmp->corpsenm; + + otmp->corpsenm = monsndx(ptr); + otmp->owt = weight(otmp); + if (otmp->otyp == CORPSE && + (special_corpse(old_corpsenm) || + special_corpse(otmp->corpsenm))) { + obj_stop_timers(otmp); + start_corpse_timeout(otmp); + } + } + } + return(otmp); +} + +/* + * Attach a monster id to an object, to provide + * a lasting association between the two. + */ +struct obj * +obj_attach_mid(obj, mid) +struct obj *obj; +unsigned mid; +{ + struct obj *otmp; + int lth, namelth; + + if (!mid || !obj) return (struct obj *)0; + lth = sizeof(mid); + namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0; + if (namelth) + otmp = realloc_obj(obj, lth, (genericptr_t) &mid, namelth, ONAME(obj)); + else { + otmp = obj; + otmp->oxlth = sizeof(mid); + (void) memcpy((genericptr_t)otmp->oextra, (genericptr_t)&mid, + sizeof(mid)); + } + if (otmp && otmp->oxlth) otmp->oattached = OATTACHED_M_ID; /* mark it */ + return otmp; +} + +static struct obj * +save_mtraits(obj, mtmp) +struct obj *obj; +struct monst *mtmp; +{ + struct obj *otmp; + int lth, namelth; + + lth = sizeof(struct monst) + mtmp->mxlth + mtmp->mnamelth; + namelth = obj->onamelth ? strlen(ONAME(obj)) + 1 : 0; + otmp = realloc_obj(obj, lth, (genericptr_t) mtmp, namelth, ONAME(obj)); + if (otmp && otmp->oxlth) { + struct monst *mtmp2 = (struct monst *)otmp->oextra; + if (mtmp->data) mtmp2->mnum = monsndx(mtmp->data); + /* invalidate pointers */ + /* m_id is needed to know if this is a revived quest leader */ + /* but m_id must be cleared when loading bones */ + mtmp2->nmon = (struct monst *)0; + mtmp2->data = (struct permonst *)0; + mtmp2->minvent = (struct obj *)0; + otmp->oattached = OATTACHED_MONST; /* mark it */ + } + return otmp; +} + +/* returns a pointer to a new monst structure based on + * the one contained within the obj. + */ +struct monst * +get_mtraits(obj, copyof) +struct obj *obj; +boolean copyof; +{ + struct monst *mtmp = (struct monst *)0; + struct monst *mnew = (struct monst *)0; + + if (obj->oxlth && obj->oattached == OATTACHED_MONST) + mtmp = (struct monst *)obj->oextra; + if (mtmp) { + if (copyof) { + int lth = mtmp->mxlth + mtmp->mnamelth; + mnew = newmonst(lth); + lth += sizeof(struct monst); + (void) memcpy((genericptr_t)mnew, + (genericptr_t)mtmp, lth); + } else { + /* Never insert this returned pointer into mon chains! */ + mnew = mtmp; + } + } + return mnew; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* make an object named after someone listed in the scoreboard file */ +struct obj * +mk_tt_object(objtype, x, y) +int objtype; /* CORPSE or STATUE */ +register int x, y; +{ + register struct obj *otmp, *otmp2; + boolean initialize_it; + + /* player statues never contain books */ + initialize_it = (objtype != STATUE); + if ((otmp = mksobj_at(objtype, x, y, initialize_it, FALSE)) != 0) { + /* tt_oname will return null if the scoreboard is empty */ + if ((otmp2 = tt_oname(otmp)) != 0) otmp = otmp2; + } + return(otmp); +} + +/* make a new corpse or statue, uninitialized if a statue (i.e. no books) */ +struct obj * +mk_named_object(objtype, ptr, x, y, nm) +int objtype; /* CORPSE or STATUE */ +struct permonst *ptr; +int x, y; +const char *nm; +{ + struct obj *otmp; + + otmp = mkcorpstat(objtype, (struct monst *)0, ptr, + x, y, (boolean)(objtype != STATUE)); + if (nm) + otmp = oname(otmp, nm); + return(otmp); +} + +boolean +is_flammable(otmp) +register struct obj *otmp; +{ + int otyp = otmp->otyp; + int omat = objects[otyp].oc_material; + + if (objects[otyp].oc_oprop == FIRE_RES || otyp == WAN_FIRE) + return FALSE; + + return((boolean)((omat <= WOOD && omat != LIQUID) || omat == PLASTIC)); +} + +boolean +is_rottable(otmp) +register struct obj *otmp; +{ + int otyp = otmp->otyp; + + return((boolean)(objects[otyp].oc_material <= WOOD && + objects[otyp].oc_material != LIQUID)); +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* + * These routines maintain the single-linked lists headed in level.objects[][] + * and threaded through the nexthere fields in the object-instance structure. + */ + +/* put the object at the given location */ +void +place_object(otmp, x, y) +register struct obj *otmp; +int x, y; +{ + register struct obj *otmp2 = level.objects[x][y]; + + if (otmp->where != OBJ_FREE) + panic("place_object: obj not free"); + + obj_no_longer_held(otmp); + if (otmp->otyp == BOULDER) block_point(x,y); /* vision */ + + /* obj goes under boulders */ + if (otmp2 && (otmp2->otyp == BOULDER)) { + otmp->nexthere = otmp2->nexthere; + otmp2->nexthere = otmp; + } else { + otmp->nexthere = otmp2; + level.objects[x][y] = otmp; + } + + /* set the new object's location */ + otmp->ox = x; + otmp->oy = y; + + otmp->where = OBJ_FLOOR; + + /* add to floor chain */ + otmp->nobj = fobj; + fobj = otmp; + if (otmp->timed) obj_timer_checks(otmp, x, y, 0); +} + +#define ON_ICE(a) ((a)->recharged) +#define ROT_ICE_ADJUSTMENT 2 /* rotting on ice takes 2 times as long */ + +/* If ice was affecting any objects correct that now + * Also used for starting ice effects too. [zap.c] + */ +void +obj_ice_effects(x, y, do_buried) +int x, y; +boolean do_buried; +{ + struct obj *otmp; + + for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) { + if (otmp->timed) obj_timer_checks(otmp, x, y, 0); + } + if (do_buried) { + for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj) { + if (otmp->ox == x && otmp->oy == y) { + if (otmp->timed) obj_timer_checks(otmp, x, y, 0); + } + } + } +} + +/* + * Returns an obj->age for a corpse object on ice, that would be the + * actual obj->age if the corpse had just been lifted from the ice. + * This is useful when just using obj->age in a check or calculation because + * rot timers pertaining to the object don't have to be stopped and + * restarted etc. + */ +long +peek_at_iced_corpse_age(otmp) +struct obj *otmp; +{ + long age, retval = otmp->age; + + if (otmp->otyp == CORPSE && ON_ICE(otmp)) { + /* Adjust the age; must be same as obj_timer_checks() for off ice*/ + age = monstermoves - otmp->age; + retval = otmp->age + (age / ROT_ICE_ADJUSTMENT); +#ifdef DEBUG_EFFECTS + pline_The("%s age has ice modifications:otmp->age = %ld, returning %ld.", + s_suffix(doname(otmp)),otmp->age, retval); + pline("Effective age of corpse: %ld.", + monstermoves - retval); +#endif + } + return retval; +} + +STATIC_OVL void +obj_timer_checks(otmp, x, y, force) +struct obj *otmp; +xchar x, y; +int force; /* 0 = no force so do checks, <0 = force off, >0 force on */ +{ + long tleft = 0L; + short action = ROT_CORPSE; + boolean restart_timer = FALSE; + boolean on_floor = (otmp->where == OBJ_FLOOR); + boolean buried = (otmp->where == OBJ_BURIED); + + /* Check for corpses just placed on or in ice */ + if (otmp->otyp == CORPSE && (on_floor || buried) && is_ice(x,y)) { + tleft = stop_timer(action, (genericptr_t)otmp); + if (tleft == 0L) { + action = REVIVE_MON; + tleft = stop_timer(action, (genericptr_t)otmp); + } + if (tleft != 0L) { + long age; + + tleft = tleft - monstermoves; + /* mark the corpse as being on ice */ + ON_ICE(otmp) = 1; +#ifdef DEBUG_EFFECTS + pline("%s is now on ice at %d,%d.", The(xname(otmp)),x,y); +#endif + /* Adjust the time remaining */ + tleft *= ROT_ICE_ADJUSTMENT; + restart_timer = TRUE; + /* Adjust the age; must be same as in obj_ice_age() */ + age = monstermoves - otmp->age; + otmp->age = monstermoves - (age * ROT_ICE_ADJUSTMENT); + } + } + /* Check for corpses coming off ice */ + else if ((force < 0) || + (otmp->otyp == CORPSE && ON_ICE(otmp) && + ((on_floor && !is_ice(x,y)) || !on_floor))) { + tleft = stop_timer(action, (genericptr_t)otmp); + if (tleft == 0L) { + action = REVIVE_MON; + tleft = stop_timer(action, (genericptr_t)otmp); + } + if (tleft != 0L) { + long age; + + tleft = tleft - monstermoves; + ON_ICE(otmp) = 0; +#ifdef DEBUG_EFFECTS + pline("%s is no longer on ice at %d,%d.", The(xname(otmp)),x,y); +#endif + /* Adjust the remaining time */ + tleft /= ROT_ICE_ADJUSTMENT; + restart_timer = TRUE; + /* Adjust the age */ + age = monstermoves - otmp->age; + otmp->age = otmp->age + (age / ROT_ICE_ADJUSTMENT); + } + } + /* now re-start the timer with the appropriate modifications */ + if (restart_timer) + (void) start_timer(tleft, TIMER_OBJECT, action, (genericptr_t)otmp); +} + +#undef ON_ICE +#undef ROT_ICE_ADJUSTMENT + +void +remove_object(otmp) +register struct obj *otmp; +{ + xchar x = otmp->ox; + xchar y = otmp->oy; + + if (otmp->where != OBJ_FLOOR) + panic("remove_object: obj not on floor"); + if (otmp->otyp == BOULDER) unblock_point(x,y); /* vision */ + extract_nexthere(otmp, &level.objects[x][y]); + extract_nobj(otmp, &fobj); + if (otmp->timed) obj_timer_checks(otmp,x,y,0); +} + +/* throw away all of a monster's inventory */ +void +discard_minvent(mtmp) +struct monst *mtmp; +{ + struct obj *otmp; + + while ((otmp = mtmp->minvent) != 0) { + obj_extract_self(otmp); + obfree(otmp, (struct obj *)0); /* dealloc_obj() isn't sufficient */ + } +} + +/* + * Free obj from whatever list it is on in preperation of deleting it or + * moving it elsewhere. This will perform all high-level consequences + * involved with removing the item. E.g. if the object is in the hero's + * inventory and confers heat resistance, the hero will lose it. + * + * Object positions: + * OBJ_FREE not on any list + * OBJ_FLOOR fobj, level.locations[][] chains (use remove_object) + * OBJ_CONTAINED cobj chain of container object + * OBJ_INVENT hero's invent chain (use freeinv) + * OBJ_MINVENT monster's invent chain + * OBJ_MIGRATING migrating chain + * OBJ_BURIED level.buriedobjs chain + * OBJ_ONBILL on billobjs chain + */ +void +obj_extract_self(obj) + struct obj *obj; +{ + switch (obj->where) { + case OBJ_FREE: + break; + case OBJ_FLOOR: + remove_object(obj); + break; + case OBJ_CONTAINED: + extract_nobj(obj, &obj->ocontainer->cobj); + container_weight(obj->ocontainer); + break; + case OBJ_INVENT: + freeinv(obj); + break; + case OBJ_MINVENT: + extract_nobj(obj, &obj->ocarry->minvent); + break; + case OBJ_MIGRATING: + extract_nobj(obj, &migrating_objs); + break; + case OBJ_BURIED: + extract_nobj(obj, &level.buriedobjlist); + break; + case OBJ_ONBILL: + extract_nobj(obj, &billobjs); + break; + default: + panic("obj_extract_self"); + break; + } +} + + +/* Extract the given object from the chain, following nobj chain. */ +void +extract_nobj(obj, head_ptr) + struct obj *obj, **head_ptr; +{ + struct obj *curr, *prev; + + curr = *head_ptr; + for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nobj) { + if (curr == obj) { + if (prev) + prev->nobj = curr->nobj; + else + *head_ptr = curr->nobj; + break; + } + } + if (!curr) panic("extract_nobj: object lost"); + obj->where = OBJ_FREE; +} + + +/* + * Extract the given object from the chain, following nexthere chain. + * + * This does not set obj->where, this function is expected to be called + * in tandem with extract_nobj, which does set it. + */ +void +extract_nexthere(obj, head_ptr) + struct obj *obj, **head_ptr; +{ + struct obj *curr, *prev; + + curr = *head_ptr; + for (prev = (struct obj *) 0; curr; prev = curr, curr = curr->nexthere) { + if (curr == obj) { + if (prev) + prev->nexthere = curr->nexthere; + else + *head_ptr = curr->nexthere; + break; + } + } + if (!curr) panic("extract_nexthere: object lost"); +} + + +/* + * Add obj to mon's inventory. If obj is able to merge with something already + * in the inventory, then the passed obj is deleted and 1 is returned. + * Otherwise 0 is returned. + */ +int +add_to_minv(mon, obj) + struct monst *mon; + struct obj *obj; +{ + struct obj *otmp; + + if (obj->where != OBJ_FREE) + panic("add_to_minv: obj not free"); + + /* merge if possible */ + for (otmp = mon->minvent; otmp; otmp = otmp->nobj) + if (merged(&otmp, &obj)) + return 1; /* obj merged and then free'd */ + /* else insert; don't bother forcing it to end of chain */ + obj->where = OBJ_MINVENT; + obj->ocarry = mon; + obj->nobj = mon->minvent; + mon->minvent = obj; + return 0; /* obj on mon's inventory chain */ +} + +/* + * Add obj to container, make sure obj is "free". Returns (merged) obj. + * The input obj may be deleted in the process. + */ +struct obj * +add_to_container(container, obj) + struct obj *container, *obj; +{ + struct obj *otmp; + + if (obj->where != OBJ_FREE) + panic("add_to_container: obj not free"); + if (container->where != OBJ_INVENT && container->where != OBJ_MINVENT) + obj_no_longer_held(obj); + + /* merge if possible */ + for (otmp = container->cobj; otmp; otmp = otmp->nobj) + if (merged(&otmp, &obj)) return (otmp); + + obj->where = OBJ_CONTAINED; + obj->ocontainer = container; + obj->nobj = container->cobj; + container->cobj = obj; + return (obj); +} + +void +add_to_migration(obj) + struct obj *obj; +{ + if (obj->where != OBJ_FREE) + panic("add_to_migration: obj not free"); + + obj->where = OBJ_MIGRATING; + obj->nobj = migrating_objs; + migrating_objs = obj; +} + +void +add_to_buried(obj) + struct obj *obj; +{ + if (obj->where != OBJ_FREE) + panic("add_to_buried: obj not free"); + + obj->where = OBJ_BURIED; + obj->nobj = level.buriedobjlist; + level.buriedobjlist = obj; +} + +/* Recalculate the weight of this container and all of _its_ containers. */ +STATIC_OVL void +container_weight(container) + struct obj *container; +{ + container->owt = weight(container); + if (container->where == OBJ_CONTAINED) + container_weight(container->ocontainer); +/* + else if (container->where == OBJ_INVENT) + recalculate load delay here ??? +*/ +} + +/* + * Deallocate the object. _All_ objects should be run through here for + * them to be deallocated. + */ +void +dealloc_obj(obj) + struct obj *obj; +{ + if (obj->where != OBJ_FREE) + panic("dealloc_obj: obj not free"); + + /* free up any timers attached to the object */ + if (obj->timed) + obj_stop_timers(obj); + + /* + * Free up any light sources attached to the object. + * + * We may want to just call del_light_source() without any + * checks (requires a code change there). Otherwise this + * list must track all objects that can have a light source + * attached to it (and also requires lamplit to be set). + */ + if (obj_sheds_light(obj)) + del_light_source(LS_OBJECT, (genericptr_t) obj); + + if (obj == thrownobj) thrownobj = (struct obj*)0; + + free((genericptr_t) obj); +} + +#ifdef WIZARD +/* Check all object lists for consistency. */ +void +obj_sanity_check() +{ + int x, y; + struct obj *obj; + struct monst *mon; + const char *mesg; + char obj_address[20], mon_address[20]; /* room for formatted pointers */ + + mesg = "fobj sanity"; + for (obj = fobj; obj; obj = obj->nobj) { + if (obj->where != OBJ_FLOOR) { + pline("%s obj %s %s@(%d,%d): %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), + obj->ox, obj->oy, doname(obj)); + } + check_contained(obj, mesg); + } + + mesg = "location sanity"; + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + for (obj = level.objects[x][y]; obj; obj = obj->nexthere) + if (obj->where != OBJ_FLOOR) { + pline("%s obj %s %s@(%d,%d): %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), + obj->ox, obj->oy, doname(obj)); + } + + mesg = "invent sanity"; + for (obj = invent; obj; obj = obj->nobj) { + if (obj->where != OBJ_INVENT) { + pline("%s obj %s %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), doname(obj)); + } + check_contained(obj, mesg); + } + + mesg = "migrating sanity"; + for (obj = migrating_objs; obj; obj = obj->nobj) { + if (obj->where != OBJ_MIGRATING) { + pline("%s obj %s %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), doname(obj)); + } + check_contained(obj, mesg); + } + + mesg = "buried sanity"; + for (obj = level.buriedobjlist; obj; obj = obj->nobj) { + if (obj->where != OBJ_BURIED) { + pline("%s obj %s %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), doname(obj)); + } + check_contained(obj, mesg); + } + + mesg = "bill sanity"; + for (obj = billobjs; obj; obj = obj->nobj) { + if (obj->where != OBJ_ONBILL) { + pline("%s obj %s %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), doname(obj)); + } + /* shouldn't be a full container on the bill */ + if (obj->cobj) { + pline("%s obj %s contains %s! %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + something, doname(obj)); + } + } + + mesg = "minvent sanity"; + for (mon = fmon; mon; mon = mon->nmon) + for (obj = mon->minvent; obj; obj = obj->nobj) { + if (obj->where != OBJ_MINVENT) { + pline("%s obj %s %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + where_name(obj->where), doname(obj)); + } + if (obj->ocarry != mon) { + pline("%s obj %s (%s) not held by mon %s (%s)\n", mesg, + fmt_ptr((genericptr_t)obj, obj_address), + doname(obj), + fmt_ptr((genericptr_t)mon, mon_address), + mon_nam(mon)); + } + check_contained(obj, mesg); + } +} + +/* This must stay consistent with the defines in obj.h. */ +static const char *obj_state_names[NOBJ_STATES] = { + "free", "floor", "contained", "invent", + "minvent", "migrating", "buried", "onbill" +}; + +STATIC_OVL const char * +where_name(where) + int where; +{ + return (where<0 || where>=NOBJ_STATES) ? "unknown" : obj_state_names[where]; +} + +/* obj sanity check: check objs contained by container */ +STATIC_OVL void +check_contained(container, mesg) + struct obj *container; + const char *mesg; +{ + struct obj *obj; + char obj1_address[20], obj2_address[20]; + + for (obj = container->cobj; obj; obj = obj->nobj) { + if (obj->where != OBJ_CONTAINED) + pline("contained %s obj %s: %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj1_address), + where_name(obj->where)); + else if (obj->ocontainer != container) + pline("%s obj %s not in container %s\n", mesg, + fmt_ptr((genericptr_t)obj, obj1_address), + fmt_ptr((genericptr_t)container, obj2_address)); + } +} +#endif /* WIZARD */ + +#endif /* OVL1 */ + +/*mkobj.c*/ diff --git a/src/mkroom.c b/src/mkroom.c new file mode 100644 index 0000000..b6e708c --- /dev/null +++ b/src/mkroom.c @@ -0,0 +1,781 @@ +/* SCCS Id: @(#)mkroom.c 3.4 2001/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Entry points: + * mkroom() -- make and stock a room of a given type + * nexttodoor() -- return TRUE if adjacent to a door + * has_dnstairs() -- return TRUE if given room has a down staircase + * has_upstairs() -- return TRUE if given room has an up staircase + * courtmon() -- generate a court monster + * save_rooms() -- save rooms into file fd + * rest_rooms() -- restore rooms from file fd + */ + +#include "hack.h" + +#ifdef OVLB +STATIC_DCL boolean FDECL(isbig, (struct mkroom *)); +STATIC_DCL struct mkroom * FDECL(pick_room,(BOOLEAN_P)); +STATIC_DCL void NDECL(mkshop), FDECL(mkzoo,(int)), NDECL(mkswamp); +STATIC_DCL void NDECL(mktemple); +STATIC_DCL coord * FDECL(shrine_pos, (int)); +STATIC_DCL struct permonst * NDECL(morguemon); +STATIC_DCL struct permonst * NDECL(antholemon); +STATIC_DCL struct permonst * NDECL(squadmon); +STATIC_DCL void FDECL(save_room, (int,struct mkroom *)); +STATIC_DCL void FDECL(rest_room, (int,struct mkroom *)); +#endif /* OVLB */ + +#define sq(x) ((x)*(x)) + +extern const struct shclass shtypes[]; /* defined in shknam.c */ + +#ifdef OVLB + +STATIC_OVL boolean +isbig(sroom) +register struct mkroom *sroom; +{ + register int area = (sroom->hx - sroom->lx + 1) + * (sroom->hy - sroom->ly + 1); + return((boolean)( area > 20 )); +} + +void +mkroom(roomtype) +/* make and stock a room of a given type */ +int roomtype; +{ + if (roomtype >= SHOPBASE) + mkshop(); /* someday, we should be able to specify shop type */ + else switch(roomtype) { + case COURT: mkzoo(COURT); break; + case ZOO: mkzoo(ZOO); break; + case BEEHIVE: mkzoo(BEEHIVE); break; + case MORGUE: mkzoo(MORGUE); break; + case BARRACKS: mkzoo(BARRACKS); break; + case SWAMP: mkswamp(); break; + case TEMPLE: mktemple(); break; + case LEPREHALL: mkzoo(LEPREHALL); break; + case COCKNEST: mkzoo(COCKNEST); break; + case ANTHOLE: mkzoo(ANTHOLE); break; + default: impossible("Tried to make a room of type %d.", roomtype); + } +} + +STATIC_OVL void +mkshop() +{ + register struct mkroom *sroom; + int i = -1; +#ifdef WIZARD + char *ep = (char *)0; /* (init == lint suppression) */ + + /* first determine shoptype */ + if(wizard){ +#ifndef MAC + ep = nh_getenv("SHOPTYPE"); + if(ep){ + if(*ep == 'z' || *ep == 'Z'){ + mkzoo(ZOO); + return; + } + if(*ep == 'm' || *ep == 'M'){ + mkzoo(MORGUE); + return; + } + if(*ep == 'b' || *ep == 'B'){ + mkzoo(BEEHIVE); + return; + } + if(*ep == 't' || *ep == 'T' || *ep == '\\'){ + mkzoo(COURT); + return; + } + if(*ep == 's' || *ep == 'S'){ + mkzoo(BARRACKS); + return; + } + if(*ep == 'a' || *ep == 'A'){ + mkzoo(ANTHOLE); + return; + } + if(*ep == 'c' || *ep == 'C'){ + mkzoo(COCKNEST); + return; + } + if(*ep == 'l' || *ep == 'L'){ + mkzoo(LEPREHALL); + return; + } + if(*ep == '_'){ + mktemple(); + return; + } + if(*ep == '}'){ + mkswamp(); + return; + } + for(i=0; shtypes[i].name; i++) + if(*ep == def_oc_syms[(int)shtypes[i].symb]) + goto gottype; + if(*ep == 'g' || *ep == 'G') + i = 0; + else + i = -1; + } +#endif + } +#ifndef MAC +gottype: +#endif +#endif + for(sroom = &rooms[0]; ; sroom++){ + if(sroom->hx < 0) return; + if(sroom - rooms >= nroom) { + pline("rooms not closed by -1?"); + return; + } + if(sroom->rtype != OROOM) continue; + if(has_dnstairs(sroom) || has_upstairs(sroom)) + continue; + if( +#ifdef WIZARD + (wizard && ep && sroom->doorct != 0) || +#endif + sroom->doorct == 1) break; + } + if (!sroom->rlit) { + int x, y; + + for(x = sroom->lx - 1; x <= sroom->hx + 1; x++) + for(y = sroom->ly - 1; y <= sroom->hy + 1; y++) + levl[x][y].lit = 1; + sroom->rlit = 1; + } + + if(i < 0) { /* shoptype not yet determined */ + register int j; + + /* pick a shop type at random */ + for (j = rnd(100), i = 0; (j -= shtypes[i].prob) > 0; i++) + continue; + + /* big rooms cannot be wand or book shops, + * - so make them general stores + */ + if(isbig(sroom) && (shtypes[i].symb == WAND_CLASS + || shtypes[i].symb == SPBOOK_CLASS)) i = 0; + } + sroom->rtype = SHOPBASE + i; + + /* set room bits before stocking the shop */ +#ifdef SPECIALIZATION + topologize(sroom, FALSE); /* doesn't matter - this is a special room */ +#else + topologize(sroom); +#endif + + /* stock the room with a shopkeeper and artifacts */ + stock_room(i, sroom); +} + +STATIC_OVL struct mkroom * +pick_room(strict) +register boolean strict; +/* pick an unused room, preferably with only one door */ +{ + register struct mkroom *sroom; + register int i = nroom; + + for(sroom = &rooms[rn2(nroom)]; i--; sroom++) { + if(sroom == &rooms[nroom]) + sroom = &rooms[0]; + if(sroom->hx < 0) + return (struct mkroom *)0; + if(sroom->rtype != OROOM) continue; + if(!strict) { + if(has_upstairs(sroom) || (has_dnstairs(sroom) && rn2(3))) + continue; + } else if(has_upstairs(sroom) || has_dnstairs(sroom)) + continue; + if(sroom->doorct == 1 || !rn2(5) +#ifdef WIZARD + || wizard +#endif + ) + return sroom; + } + return (struct mkroom *)0; +} + +STATIC_OVL void +mkzoo(type) +int type; +{ + register struct mkroom *sroom; + + if ((sroom = pick_room(FALSE)) != 0) { + sroom->rtype = type; + fill_zoo(sroom); + } +} + +void +fill_zoo(sroom) +struct mkroom *sroom; +{ + struct monst *mon; + register int sx,sy,i; + int sh, tx, ty, goldlim, type = sroom->rtype; + int rmno = (sroom - rooms) + ROOMOFFSET; + coord mm; + +#ifdef GCC_WARN + tx = ty = goldlim = 0; +#endif + + sh = sroom->fdoor; + switch(type) { + case COURT: + if(level.flags.is_maze_lev) { + for(tx = sroom->lx; tx <= sroom->hx; tx++) + for(ty = sroom->ly; ty <= sroom->hy; ty++) + if(IS_THRONE(levl[tx][ty].typ)) + goto throne_placed; + } + i = 100; + do { /* don't place throne on top of stairs */ + (void) somexy(sroom, &mm); + tx = mm.x; ty = mm.y; + } while (occupied((xchar)tx, (xchar)ty) && --i > 0); + throne_placed: + /* TODO: try to ensure the enthroned monster is an M2_PRINCE */ + break; + case BEEHIVE: + tx = sroom->lx + (sroom->hx - sroom->lx + 1)/2; + ty = sroom->ly + (sroom->hy - sroom->ly + 1)/2; + if(sroom->irregular) { + /* center might not be valid, so put queen elsewhere */ + if ((int) levl[tx][ty].roomno != rmno || + levl[tx][ty].edge) { + (void) somexy(sroom, &mm); + tx = mm.x; ty = mm.y; + } + } + break; + case ZOO: + case LEPREHALL: + goldlim = 500 * level_difficulty(); + break; + } + for(sx = sroom->lx; sx <= sroom->hx; sx++) + for(sy = sroom->ly; sy <= sroom->hy; sy++) { + if(sroom->irregular) { + if ((int) levl[sx][sy].roomno != rmno || + levl[sx][sy].edge || + (sroom->doorct && + distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1)) + continue; + } else if(!SPACE_POS(levl[sx][sy].typ) || + (sroom->doorct && + ((sx == sroom->lx && doors[sh].x == sx-1) || + (sx == sroom->hx && doors[sh].x == sx+1) || + (sy == sroom->ly && doors[sh].y == sy-1) || + (sy == sroom->hy && doors[sh].y == sy+1)))) + continue; + /* don't place monster on explicitly placed throne */ + if(type == COURT && IS_THRONE(levl[sx][sy].typ)) + continue; + mon = makemon( + (type == COURT) ? courtmon() : + (type == BARRACKS) ? squadmon() : + (type == MORGUE) ? morguemon() : + (type == BEEHIVE) ? + (sx == tx && sy == ty ? &mons[PM_QUEEN_BEE] : + &mons[PM_KILLER_BEE]) : + (type == LEPREHALL) ? &mons[PM_LEPRECHAUN] : + (type == COCKNEST) ? &mons[PM_COCKATRICE] : + (type == ANTHOLE) ? antholemon() : + (struct permonst *) 0, + sx, sy, NO_MM_FLAGS); + if(mon) { + mon->msleeping = 1; + if (type==COURT && mon->mpeaceful) { + mon->mpeaceful = 0; + set_malign(mon); + } + } + switch(type) { + case ZOO: + case LEPREHALL: + if(sroom->doorct) + { + int distval = dist2(sx,sy,doors[sh].x,doors[sh].y); + i = sq(distval); + } + else + i = goldlim; + if(i >= goldlim) i = 5*level_difficulty(); + goldlim -= i; + (void) mkgold((long) rn1(i, 10), sx, sy); + break; + case MORGUE: + if(!rn2(5)) + (void) mk_tt_object(CORPSE, sx, sy); + if(!rn2(10)) /* lots of treasure buried with dead */ + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + sx, sy, TRUE, FALSE); + if (!rn2(5)) + make_grave(sx, sy, (char *)0); + break; + case BEEHIVE: + if(!rn2(3)) + (void) mksobj_at(LUMP_OF_ROYAL_JELLY, + sx, sy, TRUE, FALSE); + break; + case BARRACKS: + if(!rn2(20)) /* the payroll and some loot */ + (void) mksobj_at((rn2(3)) ? LARGE_BOX : CHEST, + sx, sy, TRUE, FALSE); + break; + case COCKNEST: + if(!rn2(3)) { + struct obj *sobj = mk_tt_object(STATUE, sx, sy); + + if (sobj) { + for (i = rn2(5); i; i--) + (void) add_to_container(sobj, + mkobj(RANDOM_CLASS, FALSE)); + sobj->owt = weight(sobj); + } + } + break; + case ANTHOLE: + if(!rn2(3)) + (void) mkobj_at(FOOD_CLASS, sx, sy, FALSE); + break; + } + } + switch (type) { + case COURT: + { + struct obj *chest; + levl[tx][ty].typ = THRONE; + (void) somexy(sroom, &mm); + (void) mkgold((long) rn1(50 * level_difficulty(),10), mm.x, mm.y); + /* the royal coffers */ + chest = mksobj_at(CHEST, mm.x, mm.y, TRUE, FALSE); + chest->spe = 2; /* so it can be found later */ + level.flags.has_court = 1; + break; + } + case BARRACKS: + level.flags.has_barracks = 1; + break; + case ZOO: + level.flags.has_zoo = 1; + break; + case MORGUE: + level.flags.has_morgue = 1; + break; + case SWAMP: + level.flags.has_swamp = 1; + break; + case BEEHIVE: + level.flags.has_beehive = 1; + break; + } +} + +/* make a swarm of undead around mm */ +void +mkundead(mm, revive_corpses, mm_flags) +coord *mm; +boolean revive_corpses; +int mm_flags; +{ + int cnt = (level_difficulty() + 1)/10 + rnd(5); + struct permonst *mdat; + struct obj *otmp; + coord cc; + + while (cnt--) { + mdat = morguemon(); + if (enexto(&cc, mm->x, mm->y, mdat) && + (!revive_corpses || + !(otmp = sobj_at(CORPSE, cc.x, cc.y)) || + !revive(otmp))) + (void) makemon(mdat, cc.x, cc.y, mm_flags); + } + level.flags.graveyard = TRUE; /* reduced chance for undead corpse */ +} + +STATIC_OVL struct permonst * +morguemon() +{ + register int i = rn2(100), hd = rn2(level_difficulty()); + + if(hd > 10 && i < 10) + return((Inhell || In_endgame(&u.uz)) ? mkclass(S_DEMON,0) : + &mons[ndemon(A_NONE)]); + if(hd > 8 && i > 85) + return(mkclass(S_VAMPIRE,0)); + + return((i < 20) ? &mons[PM_GHOST] + : (i < 40) ? &mons[PM_WRAITH] : mkclass(S_ZOMBIE,0)); +} + +STATIC_OVL struct permonst * +antholemon() +{ + int mtyp; + + /* Same monsters within a level, different ones between levels */ + switch ((level_difficulty() + ((long)u.ubirthday)) % 3) { + default: mtyp = PM_GIANT_ANT; break; + case 0: mtyp = PM_SOLDIER_ANT; break; + case 1: mtyp = PM_FIRE_ANT; break; + } + return ((mvitals[mtyp].mvflags & G_GONE) ? + (struct permonst *)0 : &mons[mtyp]); +} + +STATIC_OVL void +mkswamp() /* Michiel Huisjes & Fred de Wilde */ +{ + register struct mkroom *sroom; + register int sx,sy,i,eelct = 0; + + for(i=0; i<5; i++) { /* turn up to 5 rooms swampy */ + sroom = &rooms[rn2(nroom)]; + if(sroom->hx < 0 || sroom->rtype != OROOM || + has_upstairs(sroom) || has_dnstairs(sroom)) + continue; + + /* satisfied; make a swamp */ + sroom->rtype = SWAMP; + for(sx = sroom->lx; sx <= sroom->hx; sx++) + for(sy = sroom->ly; sy <= sroom->hy; sy++) + if(!OBJ_AT(sx, sy) && + !MON_AT(sx, sy) && !t_at(sx,sy) && !nexttodoor(sx,sy)) { + if((sx+sy)%2) { + levl[sx][sy].typ = POOL; + if(!eelct || !rn2(4)) { + /* mkclass() won't do, as we might get kraken */ + (void) makemon(rn2(5) ? &mons[PM_GIANT_EEL] + : rn2(2) ? &mons[PM_PIRANHA] + : &mons[PM_ELECTRIC_EEL], + sx, sy, NO_MM_FLAGS); + eelct++; + } + } else + if(!rn2(4)) /* swamps tend to be moldy */ + (void) makemon(mkclass(S_FUNGUS,0), + sx, sy, NO_MM_FLAGS); + } + level.flags.has_swamp = 1; + } +} + +STATIC_OVL coord * +shrine_pos(roomno) +int roomno; +{ + static coord buf; + struct mkroom *troom = &rooms[roomno - ROOMOFFSET]; + + buf.x = troom->lx + ((troom->hx - troom->lx) / 2); + buf.y = troom->ly + ((troom->hy - troom->ly) / 2); + return(&buf); +} + +STATIC_OVL void +mktemple() +{ + register struct mkroom *sroom; + coord *shrine_spot; + register struct rm *lev; + + if(!(sroom = pick_room(TRUE))) return; + + /* set up Priest and shrine */ + sroom->rtype = TEMPLE; + /* + * In temples, shrines are blessed altars + * located in the center of the room + */ + shrine_spot = shrine_pos((sroom - rooms) + ROOMOFFSET); + lev = &levl[shrine_spot->x][shrine_spot->y]; + lev->typ = ALTAR; + lev->altarmask = induced_align(80); + priestini(&u.uz, sroom, shrine_spot->x, shrine_spot->y, FALSE); + lev->altarmask |= AM_SHRINE; + level.flags.has_temple = 1; +} + +boolean +nexttodoor(sx,sy) +register int sx, sy; +{ + register int dx, dy; + register struct rm *lev; + for(dx = -1; dx <= 1; dx++) for(dy = -1; dy <= 1; dy++) { + if(!isok(sx+dx, sy+dy)) continue; + if(IS_DOOR((lev = &levl[sx+dx][sy+dy])->typ) || + lev->typ == SDOOR) + return(TRUE); + } + return(FALSE); +} + +boolean +has_dnstairs(sroom) +register struct mkroom *sroom; +{ + if (sroom == dnstairs_room) + return TRUE; + if (sstairs.sx && !sstairs.up) + return((boolean)(sroom == sstairs_room)); + return FALSE; +} + +boolean +has_upstairs(sroom) +register struct mkroom *sroom; +{ + if (sroom == upstairs_room) + return TRUE; + if (sstairs.sx && sstairs.up) + return((boolean)(sroom == sstairs_room)); + return FALSE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +int +somex(croom) +register struct mkroom *croom; +{ + return rn2(croom->hx-croom->lx+1) + croom->lx; +} + +int +somey(croom) +register struct mkroom *croom; +{ + return rn2(croom->hy-croom->ly+1) + croom->ly; +} + +boolean +inside_room(croom, x, y) +struct mkroom *croom; +xchar x, y; +{ + return((boolean)(x >= croom->lx-1 && x <= croom->hx+1 && + y >= croom->ly-1 && y <= croom->hy+1)); +} + +boolean +somexy(croom, c) +struct mkroom *croom; +coord *c; +{ + int try_cnt = 0; + int i; + + if (croom->irregular) { + i = (croom - rooms) + ROOMOFFSET; + + while(try_cnt++ < 100) { + c->x = somex(croom); + c->y = somey(croom); + if (!levl[c->x][c->y].edge && + (int) levl[c->x][c->y].roomno == i) + return TRUE; + } + /* try harder; exhaustively search until one is found */ + for(c->x = croom->lx; c->x <= croom->hx; c->x++) + for(c->y = croom->ly; c->y <= croom->hy; c->y++) + if (!levl[c->x][c->y].edge && + (int) levl[c->x][c->y].roomno == i) + return TRUE; + return FALSE; + } + + if (!croom->nsubrooms) { + c->x = somex(croom); + c->y = somey(croom); + return TRUE; + } + + /* Check that coords doesn't fall into a subroom or into a wall */ + + while(try_cnt++ < 100) { + c->x = somex(croom); + c->y = somey(croom); + if (IS_WALL(levl[c->x][c->y].typ)) + continue; + for(i=0 ; insubrooms;i++) + if(inside_room(croom->sbrooms[i], c->x, c->y)) + goto you_lose; + break; +you_lose: ; + } + if (try_cnt >= 100) + return FALSE; + return TRUE; +} + +/* + * Search for a special room given its type (zoo, court, etc...) + * Special values : + * - ANY_SHOP + * - ANY_TYPE + */ + +struct mkroom * +search_special(type) +schar type; +{ + register struct mkroom *croom; + + for(croom = &rooms[0]; croom->hx >= 0; croom++) + if((type == ANY_TYPE && croom->rtype != OROOM) || + (type == ANY_SHOP && croom->rtype >= SHOPBASE) || + croom->rtype == type) + return croom; + for(croom = &subrooms[0]; croom->hx >= 0; croom++) + if((type == ANY_TYPE && croom->rtype != OROOM) || + (type == ANY_SHOP && croom->rtype >= SHOPBASE) || + croom->rtype == type) + return croom; + return (struct mkroom *) 0; +} + +#endif /* OVL0 */ +#ifdef OVLB + +struct permonst * +courtmon() +{ + int i = rn2(60) + rn2(3*level_difficulty()); + if (i > 100) return(mkclass(S_DRAGON,0)); + else if (i > 95) return(mkclass(S_GIANT,0)); + else if (i > 85) return(mkclass(S_TROLL,0)); + else if (i > 75) return(mkclass(S_CENTAUR,0)); + else if (i > 60) return(mkclass(S_ORC,0)); + else if (i > 45) return(&mons[PM_BUGBEAR]); + else if (i > 30) return(&mons[PM_HOBGOBLIN]); + else if (i > 15) return(mkclass(S_GNOME,0)); + else return(mkclass(S_KOBOLD,0)); +} + +#define NSTYPES (PM_CAPTAIN - PM_SOLDIER + 1) + +static struct { + unsigned pm; + unsigned prob; +} squadprob[NSTYPES] = { + {PM_SOLDIER, 80}, {PM_SERGEANT, 15}, {PM_LIEUTENANT, 4}, {PM_CAPTAIN, 1} +}; + +STATIC_OVL struct permonst * +squadmon() /* return soldier types. */ +{ + int sel_prob, i, cpro, mndx; + + sel_prob = rnd(80+level_difficulty()); + + cpro = 0; + for (i = 0; i < NSTYPES; i++) { + cpro += squadprob[i].prob; + if (cpro > sel_prob) { + mndx = squadprob[i].pm; + goto gotone; + } + } + mndx = squadprob[rn2(NSTYPES)].pm; +gotone: + if (!(mvitals[mndx].mvflags & G_GONE)) return(&mons[mndx]); + else return((struct permonst *) 0); +} + +/* + * save_room : A recursive function that saves a room and its subrooms + * (if any). + */ + +STATIC_OVL void +save_room(fd, r) +int fd; +struct mkroom *r; +{ + short i; + /* + * Well, I really should write only useful information instead + * of writing the whole structure. That is I should not write + * the subrooms pointers, but who cares ? + */ + bwrite(fd, (genericptr_t) r, sizeof(struct mkroom)); + for(i=0; insubrooms; i++) + save_room(fd, r->sbrooms[i]); +} + +/* + * save_rooms : Save all the rooms on disk! + */ + +void +save_rooms(fd) +int fd; +{ + short i; + + /* First, write the number of rooms */ + bwrite(fd, (genericptr_t) &nroom, sizeof(nroom)); + for(i=0; insubrooms; i++) { + r->sbrooms[i] = &subrooms[nsubroom]; + rest_room(fd, &subrooms[nsubroom]); + subrooms[nsubroom++].resident = (struct monst *)0; + } +} + +/* + * rest_rooms : That's for restoring rooms. Read the rooms structure from + * the disk. + */ + +void +rest_rooms(fd) +int fd; +{ + short i; + + mread(fd, (genericptr_t) &nroom, sizeof(nroom)); + nsubroom = 0; + for(i = 0; i + +STATIC_DCL boolean FDECL(restrap,(struct monst *)); +STATIC_DCL long FDECL(mm_aggression, (struct monst *,struct monst *)); +#ifdef OVL2 +STATIC_DCL int NDECL(pick_animal); +STATIC_DCL int FDECL(select_newcham_form, (struct monst *)); +STATIC_DCL void FDECL(kill_eggs, (struct obj *)); +#endif + +#ifdef REINCARNATION +#define LEVEL_SPECIFIC_NOCORPSE(mdat) \ + (Is_rogue_level(&u.uz) || \ + (level.flags.graveyard && is_undead(mdat) && rn2(3))) +#else +#define LEVEL_SPECIFIC_NOCORPSE(mdat) \ + (level.flags.graveyard && is_undead(mdat) && rn2(3)) +#endif + + +#if 0 +/* part of the original warning code which was replaced in 3.3.1 */ +#ifdef OVL1 +#define warnDelay 10 +long lastwarntime; +int lastwarnlev; + +const char *warnings[] = { + "white", "pink", "red", "ruby", "purple", "black" +}; + +STATIC_DCL void NDECL(warn_effects); +#endif /* OVL1 */ +#endif /* 0 */ + +#ifndef OVLB +STATIC_VAR short cham_to_pm[]; +#else +STATIC_DCL struct obj *FDECL(make_corpse,(struct monst *)); +STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *)); +STATIC_DCL void FDECL(lifesaved_monster, (struct monst *)); + +/* convert the monster index of an undead to its living counterpart */ +int +undead_to_corpse(mndx) +int mndx; +{ + switch (mndx) { + case PM_KOBOLD_ZOMBIE: + case PM_KOBOLD_MUMMY: mndx = PM_KOBOLD; break; + case PM_DWARF_ZOMBIE: + case PM_DWARF_MUMMY: mndx = PM_DWARF; break; + case PM_GNOME_ZOMBIE: + case PM_GNOME_MUMMY: mndx = PM_GNOME; break; + case PM_ORC_ZOMBIE: + case PM_ORC_MUMMY: mndx = PM_ORC; break; + case PM_ELF_ZOMBIE: + case PM_ELF_MUMMY: mndx = PM_ELF; break; + case PM_VAMPIRE: + case PM_VAMPIRE_LORD: +#if 0 /* DEFERRED */ + case PM_VAMPIRE_MAGE: +#endif + case PM_HUMAN_ZOMBIE: + case PM_HUMAN_MUMMY: mndx = PM_HUMAN; break; + case PM_GIANT_ZOMBIE: + case PM_GIANT_MUMMY: mndx = PM_GIANT; break; + case PM_ETTIN_ZOMBIE: + case PM_ETTIN_MUMMY: mndx = PM_ETTIN; break; + default: break; + } + return mndx; +} + +/* Convert the monster index of some monsters (such as quest guardians) + * to their generic species type. + * + * Return associated character class monster, rather than species + * if mode is 1. + */ +int +genus(mndx, mode) +int mndx, mode; +{ + switch (mndx) { +/* Quest guardians */ + case PM_STUDENT: mndx = mode ? PM_ARCHEOLOGIST : PM_HUMAN; break; + case PM_CHIEFTAIN: mndx = mode ? PM_BARBARIAN : PM_HUMAN; break; + case PM_NEANDERTHAL: mndx = mode ? PM_CAVEMAN : PM_HUMAN; break; + case PM_ATTENDANT: mndx = mode ? PM_HEALER : PM_HUMAN; break; + case PM_PAGE: mndx = mode ? PM_KNIGHT : PM_HUMAN; break; + case PM_ABBOT: mndx = mode ? PM_MONK : PM_HUMAN; break; + case PM_ACOLYTE: mndx = mode ? PM_PRIEST : PM_HUMAN; break; + case PM_HUNTER: mndx = mode ? PM_RANGER : PM_HUMAN; break; + case PM_THUG: mndx = mode ? PM_ROGUE : PM_HUMAN; break; + case PM_ROSHI: mndx = mode ? PM_SAMURAI : PM_HUMAN; break; +#ifdef TOURIST + case PM_GUIDE: mndx = mode ? PM_TOURIST : PM_HUMAN; break; +#endif + case PM_APPRENTICE: mndx = mode ? PM_WIZARD : PM_HUMAN; break; + case PM_WARRIOR: mndx = mode ? PM_VALKYRIE : PM_HUMAN; break; + default: + if (mndx >= LOW_PM && mndx < NUMMONS) { + struct permonst *ptr = &mons[mndx]; + if (is_human(ptr)) mndx = PM_HUMAN; + else if (is_elf(ptr)) mndx = PM_ELF; + else if (is_dwarf(ptr)) mndx = PM_DWARF; + else if (is_gnome(ptr)) mndx = PM_GNOME; + else if (is_orc(ptr)) mndx = PM_ORC; + } + break; + } + return mndx; +} + +/* convert monster index to chameleon index */ +int +pm_to_cham(mndx) +int mndx; +{ + int mcham; + + switch (mndx) { + case PM_CHAMELEON: mcham = CHAM_CHAMELEON; break; + case PM_DOPPELGANGER: mcham = CHAM_DOPPELGANGER; break; + case PM_SANDESTIN: mcham = CHAM_SANDESTIN; break; + default: mcham = CHAM_ORDINARY; break; + } + return mcham; +} + +/* convert chameleon index to monster index */ +STATIC_VAR short cham_to_pm[] = { + NON_PM, /* placeholder for CHAM_ORDINARY */ + PM_CHAMELEON, + PM_DOPPELGANGER, + PM_SANDESTIN, +}; + +/* for deciding whether corpse or statue will carry along full monster data */ +#define KEEPTRAITS(mon) ((mon)->isshk || (mon)->mtame || \ + ((mon)->data->geno & G_UNIQ) || \ + is_reviver((mon)->data) || \ + /* normally leader the will be unique, */ \ + /* but he might have been polymorphed */ \ + (mon)->m_id == quest_status.leader_m_id || \ + /* special cancellation handling for these */ \ + (dmgtype((mon)->data, AD_SEDU) || \ + dmgtype((mon)->data, AD_SSEX))) + +/* Creates a monster corpse, a "special" corpse, or nothing if it doesn't + * leave corpses. Monsters which leave "special" corpses should have + * G_NOCORPSE set in order to prevent wishing for one, finding tins of one, + * etc.... + */ +STATIC_OVL struct obj * +make_corpse(mtmp) +register struct monst *mtmp; +{ + register struct permonst *mdat = mtmp->data; + int num; + struct obj *obj = (struct obj *)0; + int x = mtmp->mx, y = mtmp->my; + int mndx = monsndx(mdat); + + switch(mndx) { + case PM_GRAY_DRAGON: + case PM_SILVER_DRAGON: +#if 0 /* DEFERRED */ + case PM_SHIMMERING_DRAGON: +#endif + case PM_RED_DRAGON: + case PM_ORANGE_DRAGON: + case PM_WHITE_DRAGON: + case PM_BLACK_DRAGON: + case PM_BLUE_DRAGON: + case PM_GREEN_DRAGON: + case PM_YELLOW_DRAGON: + /* Make dragon scales. This assumes that the order of the */ + /* dragons is the same as the order of the scales. */ + if (!rn2(mtmp->mrevived ? 20 : 3)) { + num = GRAY_DRAGON_SCALES + monsndx(mdat) - PM_GRAY_DRAGON; + obj = mksobj_at(num, x, y, FALSE, FALSE); + obj->spe = 0; + obj->cursed = obj->blessed = FALSE; + } + goto default_1; + + case PM_WHITE_UNICORN: + case PM_GRAY_UNICORN: + case PM_BLACK_UNICORN: + if (mtmp->mrevived && rn2(20)) { + if (canseemon(mtmp)) + pline("%s recently regrown horn crumbles to dust.", + s_suffix(Monnam(mtmp))); + } else + (void) mksobj_at(UNICORN_HORN, x, y, TRUE, FALSE); + goto default_1; + case PM_LONG_WORM: + (void) mksobj_at(WORM_TOOTH, x, y, TRUE, FALSE); + goto default_1; + case PM_VAMPIRE: + case PM_VAMPIRE_LORD: + /* include mtmp in the mkcorpstat() call */ + num = undead_to_corpse(mndx); + obj = mkcorpstat(CORPSE, mtmp, &mons[num], x, y, TRUE); + obj->age -= 100; /* this is an *OLD* corpse */ + break; + case PM_KOBOLD_MUMMY: + case PM_DWARF_MUMMY: + case PM_GNOME_MUMMY: + case PM_ORC_MUMMY: + case PM_ELF_MUMMY: + case PM_HUMAN_MUMMY: + case PM_GIANT_MUMMY: + case PM_ETTIN_MUMMY: + case PM_KOBOLD_ZOMBIE: + case PM_DWARF_ZOMBIE: + case PM_GNOME_ZOMBIE: + case PM_ORC_ZOMBIE: + case PM_ELF_ZOMBIE: + case PM_HUMAN_ZOMBIE: + case PM_GIANT_ZOMBIE: + case PM_ETTIN_ZOMBIE: + num = undead_to_corpse(mndx); + obj = mkcorpstat(CORPSE, mtmp, &mons[num], x, y, TRUE); + obj->age -= 100; /* this is an *OLD* corpse */ + break; + case PM_IRON_GOLEM: + num = d(2,6); + while (num--) + obj = mksobj_at(IRON_CHAIN, x, y, TRUE, FALSE); + mtmp->mnamelth = 0; + break; + case PM_GLASS_GOLEM: + num = d(2,4); /* very low chance of creating all glass gems */ + while (num--) + obj = mksobj_at((LAST_GEM + rnd(9)), x, y, TRUE, FALSE); + mtmp->mnamelth = 0; + break; + case PM_CLAY_GOLEM: + obj = mksobj_at(ROCK, x, y, FALSE, FALSE); + obj->quan = (long)(rn2(20) + 50); + obj->owt = weight(obj); + mtmp->mnamelth = 0; + break; + case PM_STONE_GOLEM: + obj = mkcorpstat(STATUE, (struct monst *)0, + mdat, x, y, FALSE); + break; + case PM_WOOD_GOLEM: + num = d(2,4); + while(num--) { + obj = mksobj_at(QUARTERSTAFF, x, y, TRUE, FALSE); + } + mtmp->mnamelth = 0; + break; + case PM_LEATHER_GOLEM: + num = d(2,4); + while(num--) + obj = mksobj_at(LEATHER_ARMOR, x, y, TRUE, FALSE); + mtmp->mnamelth = 0; + break; + case PM_GOLD_GOLEM: + /* Good luck gives more coins */ + obj = mkgold((long)(200 - rnl(101)), x, y); + mtmp->mnamelth = 0; + break; + case PM_PAPER_GOLEM: + num = rnd(4); + while (num--) + obj = mksobj_at(SCR_BLANK_PAPER, x, y, TRUE, FALSE); + mtmp->mnamelth = 0; + break; + default_1: + default: + if (mvitals[mndx].mvflags & G_NOCORPSE) + return (struct obj *)0; + else /* preserve the unique traits of some creatures */ + obj = mkcorpstat(CORPSE, KEEPTRAITS(mtmp) ? mtmp : 0, + mdat, x, y, TRUE); + break; + } + /* All special cases should precede the G_NOCORPSE check */ + + /* if polymorph or undead turning has killed this monster, + prevent the same attack beam from hitting its corpse */ + if (flags.bypasses) bypass_obj(obj); + + if (mtmp->mnamelth) + obj = oname(obj, NAME(mtmp)); + + /* Avoid "It was hidden under a green mold corpse!" + * during Blind combat. An unseen monster referred to as "it" + * could be killed and leave a corpse. If a hider then hid + * underneath it, you could be told the corpse type of a + * monster that you never knew was there without this. + * The code in hitmu() substitutes the word "something" + * if the corpses obj->dknown is 0. + */ + if (Blind && !sensemon(mtmp)) obj->dknown = 0; + +#ifdef INVISIBLE_OBJECTS + /* Invisible monster ==> invisible corpse */ + obj->oinvis = mtmp->minvis; +#endif + + stackobj(obj); + newsym(x, y); + return obj; +} + +#endif /* OVLB */ +#ifdef OVL1 + +#if 0 +/* part of the original warning code which was replaced in 3.3.1 */ +STATIC_OVL void +warn_effects() +{ + if (warnlevel == 100) { + if(!Blind && uwep && + (warnlevel > lastwarnlev || moves > lastwarntime + warnDelay)) { + Your("%s %s!", aobjnam(uwep, "glow"), + hcolor(NH_LIGHT_BLUE)); + lastwarnlev = warnlevel; + lastwarntime = moves; + } + warnlevel = 0; + return; + } + + if (warnlevel >= SIZE(warnings)) + warnlevel = SIZE(warnings)-1; + if (!Blind && + (warnlevel > lastwarnlev || moves > lastwarntime + warnDelay)) { + const char *which, *what, *how; + long rings = (EWarning & (LEFT_RING|RIGHT_RING)); + + if (rings) { + what = Hallucination ? "mood ring" : "ring"; + how = "glows"; /* singular verb */ + if (rings == LEFT_RING) { + which = "left "; + } else if (rings == RIGHT_RING) { + which = "right "; + } else { /* both */ + which = ""; + what = (const char *) makeplural(what); + how = "glow"; /* plural verb */ + } + Your("%s%s %s %s!", which, what, how, hcolor(warnings[warnlevel])); + } else { + if (Hallucination) + Your("spider-sense is tingling..."); + else + You_feel("apprehensive as you sense a %s flash.", + warnings[warnlevel]); + } + + lastwarntime = moves; + lastwarnlev = warnlevel; + } +} +#endif /* 0 */ + +/* check mtmp and water/lava for compatibility, 0 (survived), 1 (died) */ +int +minliquid(mtmp) +register struct monst *mtmp; +{ + boolean inpool, inlava, infountain; + + inpool = is_pool(mtmp->mx,mtmp->my) && + !is_flyer(mtmp->data) && !is_floater(mtmp->data); + inlava = is_lava(mtmp->mx,mtmp->my) && + !is_flyer(mtmp->data) && !is_floater(mtmp->data); + infountain = IS_FOUNTAIN(levl[mtmp->mx][mtmp->my].typ); + +#ifdef STEED + /* Flying and levitation keeps our steed out of the liquid */ + /* (but not water-walking or swimming) */ + if (mtmp == u.usteed && (Flying || Levitation)) + return (0); +#endif + + /* Gremlin multiplying won't go on forever since the hit points + * keep going down, and when it gets to 1 hit point the clone + * function will fail. + */ + if (mtmp->data == &mons[PM_GREMLIN] && (inpool || infountain) && rn2(3)) { + if (split_mon(mtmp, (struct monst *)0)) + dryup(mtmp->mx, mtmp->my, FALSE); + if (inpool) water_damage(mtmp->minvent, FALSE, FALSE); + return (0); + } else if (mtmp->data == &mons[PM_IRON_GOLEM] && inpool && !rn2(5)) { + int dam = d(2,6); + if (cansee(mtmp->mx,mtmp->my)) + pline("%s rusts.", Monnam(mtmp)); + mtmp->mhp -= dam; + if (mtmp->mhpmax > dam) mtmp->mhpmax -= dam; + if (mtmp->mhp < 1) { + mondead(mtmp); + if (mtmp->mhp < 1) return (1); + } + water_damage(mtmp->minvent, FALSE, FALSE); + return (0); + } + + if (inlava) { + /* + * Lava effects much as water effects. Lava likers are able to + * protect their stuff. Fire resistant monsters can only protect + * themselves --ALI + */ + if (!is_clinger(mtmp->data) && !likes_lava(mtmp->data)) { + if (!resists_fire(mtmp)) { + if (cansee(mtmp->mx,mtmp->my)) + pline("%s %s.", Monnam(mtmp), + mtmp->data == &mons[PM_WATER_ELEMENTAL] ? + "boils away" : "burns to a crisp"); + mondead(mtmp); + } + else { + if (--mtmp->mhp < 1) { + if (cansee(mtmp->mx,mtmp->my)) + pline("%s surrenders to the fire.", Monnam(mtmp)); + mondead(mtmp); + } + else if (cansee(mtmp->mx,mtmp->my)) + pline("%s burns slightly.", Monnam(mtmp)); + } + if (mtmp->mhp > 0) { + (void) fire_damage(mtmp->minvent, FALSE, FALSE, + mtmp->mx, mtmp->my); + (void) rloc(mtmp, FALSE); + return 0; + } + return (1); + } + } else if (inpool) { + /* Most monsters drown in pools. flooreffects() will take care of + * water damage to dead monsters' inventory, but survivors need to + * be handled here. Swimmers are able to protect their stuff... + */ + if (!is_clinger(mtmp->data) + && !is_swimmer(mtmp->data) && !amphibious(mtmp->data)) { + if (cansee(mtmp->mx,mtmp->my)) { + pline("%s drowns.", Monnam(mtmp)); + } + if (u.ustuck && u.uswallow && u.ustuck == mtmp) { + /* This can happen after a purple worm plucks you off a + flying steed while you are over water. */ + pline("%s sinks as water rushes in and flushes you out.", + Monnam(mtmp)); + } + mondead(mtmp); + if (mtmp->mhp > 0) { + (void) rloc(mtmp, FALSE); + water_damage(mtmp->minvent, FALSE, FALSE); + return 0; + } + return (1); + } + } else { + /* but eels have a difficult time outside */ + if (mtmp->data->mlet == S_EEL && !Is_waterlevel(&u.uz)) { + if(mtmp->mhp > 1) mtmp->mhp--; + monflee(mtmp, 2, FALSE, FALSE); + } + } + return (0); +} + + +int +mcalcmove(mon) +struct monst *mon; +{ + int mmove = mon->data->mmove; + + /* Note: MSLOW's `+ 1' prevents slowed speed 1 getting reduced to 0; + * MFAST's `+ 2' prevents hasted speed 1 from becoming a no-op; + * both adjustments have negligible effect on higher speeds. + */ + if (mon->mspeed == MSLOW) + mmove = (2 * mmove + 1) / 3; + else if (mon->mspeed == MFAST) + mmove = (4 * mmove + 2) / 3; + +#ifdef STEED + if (mon == u.usteed) { + if (u.ugallop && flags.mv) { + /* average movement is 1.50 times normal */ + mmove = ((rn2(2) ? 4 : 5) * mmove) / 3; + } + } +#endif + + return mmove; +} + +/* actions that happen once per ``turn'', regardless of each + individual monster's metabolism; some of these might need to + be reclassified to occur more in proportion with movement rate */ +void +mcalcdistress() +{ + struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + + /* must check non-moving monsters once/turn in case + * they managed to end up in liquid */ + if (mtmp->data->mmove == 0) { + if (vision_full_recalc) vision_recalc(0); + if (minliquid(mtmp)) continue; + } + + /* regenerate hit points */ + mon_regen(mtmp, FALSE); + + /* possibly polymorph shapechangers and lycanthropes */ + if (mtmp->cham && !rn2(6)) + (void) newcham(mtmp, (struct permonst *)0, FALSE, FALSE); + were_change(mtmp); + + /* gradually time out temporary problems */ + if (mtmp->mblinded && !--mtmp->mblinded) + mtmp->mcansee = 1; + if (mtmp->mfrozen && !--mtmp->mfrozen) + mtmp->mcanmove = 1; + if (mtmp->mfleetim && !--mtmp->mfleetim) + mtmp->mflee = 0; + + /* FIXME: mtmp->mlstmv ought to be updated here */ + } +} + +int +movemon() +{ + register struct monst *mtmp, *nmtmp; + register boolean somebody_can_move = FALSE; +#if 0 + /* part of the original warning code which was replaced in 3.3.1 */ + warnlevel = 0; +#endif + + /* + Some of you may remember the former assertion here that + because of deaths and other actions, a simple one-pass + algorithm wasn't possible for movemon. Deaths are no longer + removed to the separate list fdmon; they are simply left in + the chain with hit points <= 0, to be cleaned up at the end + of the pass. + + The only other actions which cause monsters to be removed from + the chain are level migrations and losedogs(). I believe losedogs() + is a cleanup routine not associated with monster movements, and + monsters can only affect level migrations on themselves, not others + (hence the fetching of nmon before moving the monster). Currently, + monsters can jump into traps, read cursed scrolls of teleportation, + and drink cursed potions of raise level to change levels. These are + all reflexive at this point. Should one monster be able to level + teleport another, this scheme would have problems. + */ + + for(mtmp = fmon; mtmp; mtmp = nmtmp) { + nmtmp = mtmp->nmon; + + /* Find a monster that we have not treated yet. */ + if(DEADMONSTER(mtmp)) + continue; + if(mtmp->movement < NORMAL_SPEED) + continue; + + mtmp->movement -= NORMAL_SPEED; + if (mtmp->movement >= NORMAL_SPEED) + somebody_can_move = TRUE; + + if (vision_full_recalc) vision_recalc(0); /* vision! */ + + if (minliquid(mtmp)) continue; + + if (is_hider(mtmp->data)) { + /* unwatched mimics and piercers may hide again [MRS] */ + if(restrap(mtmp)) continue; + if(mtmp->m_ap_type == M_AP_FURNITURE || + mtmp->m_ap_type == M_AP_OBJECT) + continue; + if(mtmp->mundetected) continue; + } + + /* continue if the monster died fighting */ + if (Conflict && !mtmp->iswiz && mtmp->mcansee) { + /* Note: + * Conflict does not take effect in the first round. + * Therefore, A monster when stepping into the area will + * get to swing at you. + * + * The call to fightm() must be _last_. The monster might + * have died if it returns 1. + */ + if (couldsee(mtmp->mx,mtmp->my) && + (distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) && + fightm(mtmp)) + continue; /* mon might have died */ + } + if(dochugw(mtmp)) /* otherwise just move the monster */ + continue; + } +#if 0 + /* part of the original warning code which was replaced in 3.3.1 */ + if(warnlevel > 0) + warn_effects(); +#endif + + if (any_light_source()) + vision_full_recalc = 1; /* in case a mon moved with a light source */ + dmonsfree(); /* remove all dead monsters */ + + /* a monster may have levteleported player -dlc */ + if (u.utotype) { + deferred_goto(); + /* changed levels, so these monsters are dormant */ + somebody_can_move = FALSE; + } + + return somebody_can_move; +} + +#endif /* OVL1 */ +#ifdef OVLB + +#define mstoning(obj) (ofood(obj) && \ + (touch_petrifies(&mons[(obj)->corpsenm]) || \ + (obj)->corpsenm == PM_MEDUSA)) + +/* + * Maybe eat a metallic object (not just gold). + * Return value: 0 => nothing happened, 1 => monster ate something, + * 2 => monster died (it must have grown into a genocided form, but + * that can't happen at present because nothing which eats objects + * has young and old forms). + */ +int +meatmetal(mtmp) + register struct monst *mtmp; +{ + register struct obj *otmp; + struct permonst *ptr; + int poly, grow, heal, mstone; + + /* If a pet, eating is handled separately, in dog.c */ + if (mtmp->mtame) return 0; + + /* Eats topmost metal object if it is there */ + for (otmp = level.objects[mtmp->mx][mtmp->my]; + otmp; otmp = otmp->nexthere) { + if (mtmp->data == &mons[PM_RUST_MONSTER] && !is_rustprone(otmp)) + continue; + if (is_metallic(otmp) && !obj_resists(otmp, 5, 95) && + touch_artifact(otmp,mtmp)) { + if (mtmp->data == &mons[PM_RUST_MONSTER] && otmp->oerodeproof) { + if (canseemon(mtmp) && flags.verbose) { + pline("%s eats %s!", + Monnam(mtmp), + distant_name(otmp,doname)); + } + /* The object's rustproofing is gone now */ + otmp->oerodeproof = 0; + mtmp->mstun = 1; + if (canseemon(mtmp) && flags.verbose) { + pline("%s spits %s out in disgust!", + Monnam(mtmp), distant_name(otmp,doname)); + } + /* KMH -- Don't eat indigestible/choking objects */ + } else if (otmp->otyp != AMULET_OF_STRANGULATION && + otmp->otyp != RIN_SLOW_DIGESTION) { + if (cansee(mtmp->mx,mtmp->my) && flags.verbose) + pline("%s eats %s!", Monnam(mtmp), + distant_name(otmp,doname)); + else if (flags.soundok && flags.verbose) + You_hear("a crunching sound."); + mtmp->meating = otmp->owt/2 + 1; + /* Heal up to the object's weight in hp */ + if (mtmp->mhp < mtmp->mhpmax) { + mtmp->mhp += objects[otmp->otyp].oc_weight; + if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; + } + if(otmp == uball) { + unpunish(); + delobj(otmp); + } else if (otmp == uchain) { + unpunish(); /* frees uchain */ + } else { + poly = polyfodder(otmp); + grow = mlevelgain(otmp); + heal = mhealup(otmp); + mstone = mstoning(otmp); + delobj(otmp); + ptr = mtmp->data; + if (poly) { + if (newcham(mtmp, (struct permonst *)0, + FALSE, FALSE)) + ptr = mtmp->data; + } else if (grow) { + ptr = grow_up(mtmp, (struct monst *)0); + } else if (mstone) { + if (poly_when_stoned(ptr)) { + mon_to_stone(mtmp); + ptr = mtmp->data; + } else if (!resists_ston(mtmp)) { + if (canseemon(mtmp)) + pline("%s turns to stone!", Monnam(mtmp)); + monstone(mtmp); + ptr = (struct permonst *)0; + } + } else if (heal) { + mtmp->mhp = mtmp->mhpmax; + } + if (!ptr) return 2; /* it died */ + } + /* Left behind a pile? */ + if (rnd(25) < 3) + (void)mksobj_at(ROCK, mtmp->mx, mtmp->my, TRUE, FALSE); + newsym(mtmp->mx, mtmp->my); + return 1; + } + } + } + return 0; +} + +int +meatobj(mtmp) /* for gelatinous cubes */ + register struct monst *mtmp; +{ + register struct obj *otmp, *otmp2; + struct permonst *ptr; + int poly, grow, heal, count = 0, ecount = 0; + char buf[BUFSZ]; + + buf[0] = '\0'; + /* If a pet, eating is handled separately, in dog.c */ + if (mtmp->mtame) return 0; + + /* Eats organic objects, including cloth and wood, if there */ + /* Engulfs others, except huge rocks and metal attached to player */ + for (otmp = level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if (is_organic(otmp) && !obj_resists(otmp, 5, 95) && + touch_artifact(otmp,mtmp)) { + if (otmp->otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]) && + !resists_ston(mtmp)) + continue; + if (otmp->otyp == AMULET_OF_STRANGULATION || + otmp->otyp == RIN_SLOW_DIGESTION) + continue; + ++count; + if (cansee(mtmp->mx,mtmp->my) && flags.verbose) + pline("%s eats %s!", Monnam(mtmp), + distant_name(otmp, doname)); + else if (flags.soundok && flags.verbose) + You_hear("a slurping sound."); + /* Heal up to the object's weight in hp */ + if (mtmp->mhp < mtmp->mhpmax) { + mtmp->mhp += objects[otmp->otyp].oc_weight; + if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = mtmp->mhpmax; + } + if (Has_contents(otmp)) { + register struct obj *otmp3; + /* contents of eaten containers become engulfed; this + is arbitrary, but otherwise g.cubes are too powerful */ + while ((otmp3 = otmp->cobj) != 0) { + obj_extract_self(otmp3); + if (otmp->otyp == ICE_BOX && otmp3->otyp == CORPSE) { + otmp3->age = monstermoves - otmp3->age; + start_corpse_timeout(otmp3); + } + (void) mpickobj(mtmp, otmp3); + } + } + poly = polyfodder(otmp); + grow = mlevelgain(otmp); + heal = mhealup(otmp); + delobj(otmp); /* munch */ + ptr = mtmp->data; + if (poly) { + if (newcham(mtmp, (struct permonst *)0, FALSE, FALSE)) + ptr = mtmp->data; + } else if (grow) { + ptr = grow_up(mtmp, (struct monst *)0); + } else if (heal) { + mtmp->mhp = mtmp->mhpmax; + } + /* in case it polymorphed or died */ + if (ptr != &mons[PM_GELATINOUS_CUBE]) + return !ptr ? 2 : 1; + } else if (otmp->oclass != ROCK_CLASS && + otmp != uball && otmp != uchain) { + ++ecount; + if (ecount == 1) { + Sprintf(buf, "%s engulfs %s.", Monnam(mtmp), + distant_name(otmp,doname)); + } else if (ecount == 2) + Sprintf(buf, "%s engulfs several objects.", Monnam(mtmp)); + obj_extract_self(otmp); + (void) mpickobj(mtmp, otmp); /* slurp */ + } + /* Engulf & devour is instant, so don't set meating */ + if (mtmp->minvis) newsym(mtmp->mx, mtmp->my); + } + if (ecount > 0) { + if (cansee(mtmp->mx, mtmp->my) && flags.verbose && buf[0]) + pline("%s", buf); + else if (flags.soundok && flags.verbose) + You_hear("%s slurping sound%s.", + ecount == 1 ? "a" : "several", + ecount == 1 ? "" : "s"); + } + return ((count > 0) || (ecount > 0)) ? 1 : 0; +} + +void +mpickgold(mtmp) + register struct monst *mtmp; +{ + register struct obj *gold; + int mat_idx; + + if ((gold = g_at(mtmp->mx, mtmp->my)) != 0) { + mat_idx = objects[gold->otyp].oc_material; +#ifndef GOLDOBJ + mtmp->mgold += gold->quan; + delobj(gold); +#else + obj_extract_self(gold); + add_to_minv(mtmp, gold); +#endif + if (cansee(mtmp->mx, mtmp->my) ) { + if (flags.verbose && !mtmp->isgd) + pline("%s picks up some %s.", Monnam(mtmp), + mat_idx == GOLD ? "gold" : "money"); + newsym(mtmp->mx, mtmp->my); + } + } +} +#endif /* OVLB */ +#ifdef OVL2 + +boolean +mpickstuff(mtmp, str) + register struct monst *mtmp; + register const char *str; +{ + register struct obj *otmp, *otmp2; + +/* prevent shopkeepers from leaving the door of their shop */ + if(mtmp->isshk && inhishop(mtmp)) return FALSE; + + for(otmp = level.objects[mtmp->mx][mtmp->my]; otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; +/* Nymphs take everything. Most monsters don't pick up corpses. */ + if (!str ? searches_for_item(mtmp,otmp) : + !!(index(str, otmp->oclass))) { + if (otmp->otyp == CORPSE && mtmp->data->mlet != S_NYMPH && + /* let a handful of corpse types thru to can_carry() */ + !touch_petrifies(&mons[otmp->corpsenm]) && + otmp->corpsenm != PM_LIZARD && + !acidic(&mons[otmp->corpsenm])) continue; + if (!touch_artifact(otmp,mtmp)) continue; + if (!can_carry(mtmp,otmp)) continue; + if (is_pool(mtmp->mx,mtmp->my)) continue; +#ifdef INVISIBLE_OBJECTS + if (otmp->oinvis && !perceives(mtmp->data)) continue; +#endif + if (cansee(mtmp->mx,mtmp->my) && flags.verbose) + pline("%s picks up %s.", Monnam(mtmp), + (distu(mtmp->mx, mtmp->my) <= 5) ? + doname(otmp) : distant_name(otmp, doname)); + obj_extract_self(otmp); + /* unblock point after extract, before pickup */ + if (otmp->otyp == BOULDER) + unblock_point(otmp->ox,otmp->oy); /* vision */ + (void) mpickobj(mtmp, otmp); /* may merge and free otmp */ + m_dowear(mtmp, FALSE); + newsym(mtmp->mx, mtmp->my); + return TRUE; /* pick only one object */ + } + } + return FALSE; +} + +#endif /* OVL2 */ +#ifdef OVL0 + +int +curr_mon_load(mtmp) +register struct monst *mtmp; +{ + register int curload = 0; + register struct obj *obj; + + for(obj = mtmp->minvent; obj; obj = obj->nobj) { + if(obj->otyp != BOULDER || !throws_rocks(mtmp->data)) + curload += obj->owt; + } + + return curload; +} + +int +max_mon_load(mtmp) +register struct monst *mtmp; +{ + register long maxload; + + /* Base monster carrying capacity is equal to human maximum + * carrying capacity, or half human maximum if not strong. + * (for a polymorphed player, the value used would be the + * non-polymorphed carrying capacity instead of max/half max). + * This is then modified by the ratio between the monster weights + * and human weights. Corpseless monsters are given a capacity + * proportional to their size instead of weight. + */ + if (!mtmp->data->cwt) + maxload = (MAX_CARR_CAP * (long)mtmp->data->msize) / MZ_HUMAN; + else if (!strongmonst(mtmp->data) + || (strongmonst(mtmp->data) && (mtmp->data->cwt > WT_HUMAN))) + maxload = (MAX_CARR_CAP * (long)mtmp->data->cwt) / WT_HUMAN; + else maxload = MAX_CARR_CAP; /*strong monsters w/cwt <= WT_HUMAN*/ + + if (!strongmonst(mtmp->data)) maxload /= 2; + + if (maxload < 1) maxload = 1; + + return (int) maxload; +} + +/* for restricting monsters' object-pickup */ +boolean +can_carry(mtmp,otmp) +struct monst *mtmp; +struct obj *otmp; +{ + int otyp = otmp->otyp, newload = otmp->owt; + struct permonst *mdat = mtmp->data; + + if (notake(mdat)) return FALSE; /* can't carry anything */ + + if (otyp == CORPSE && touch_petrifies(&mons[otmp->corpsenm]) && + !(mtmp->misc_worn_check & W_ARMG) && !resists_ston(mtmp)) + return FALSE; + if (otyp == CORPSE && is_rider(&mons[otmp->corpsenm])) + return FALSE; + if (objects[otyp].oc_material == SILVER && hates_silver(mdat) && + (otyp != BELL_OF_OPENING || !is_covetous(mdat))) + return FALSE; + +#ifdef STEED + /* Steeds don't pick up stuff (to avoid shop abuse) */ + if (mtmp == u.usteed) return (FALSE); +#endif + if (mtmp->isshk) return(TRUE); /* no limit */ + if (mtmp->mpeaceful && !mtmp->mtame) return(FALSE); + /* otherwise players might find themselves obligated to violate + * their alignment if the monster takes something they need + */ + + /* special--boulder throwers carry unlimited amounts of boulders */ + if (throws_rocks(mdat) && otyp == BOULDER) + return(TRUE); + + /* nymphs deal in stolen merchandise, but not boulders or statues */ + if (mdat->mlet == S_NYMPH) + return (boolean)(otmp->oclass != ROCK_CLASS); + + if (curr_mon_load(mtmp) + newload > max_mon_load(mtmp)) return FALSE; + + return(TRUE); +} + +/* return number of acceptable neighbour positions */ +int +mfndpos(mon, poss, info, flag) + register struct monst *mon; + coord *poss; /* coord poss[9] */ + long *info; /* long info[9] */ + long flag; +{ + struct permonst *mdat = mon->data; + register xchar x,y,nx,ny; + register int cnt = 0; + register uchar ntyp; + uchar nowtyp; + boolean wantpool,poolok,lavaok,nodiag; + boolean rockok = FALSE, treeok = FALSE, thrudoor; + int maxx, maxy; + + x = mon->mx; + y = mon->my; + nowtyp = levl[x][y].typ; + + nodiag = (mdat == &mons[PM_GRID_BUG]); + wantpool = mdat->mlet == S_EEL; + poolok = is_flyer(mdat) || is_clinger(mdat) || + (is_swimmer(mdat) && !wantpool); + lavaok = is_flyer(mdat) || is_clinger(mdat) || likes_lava(mdat); + thrudoor = ((flag & (ALLOW_WALL|BUSTDOOR)) != 0L); + if (flag & ALLOW_DIG) { + struct obj *mw_tmp; + + /* need to be specific about what can currently be dug */ + if (!needspick(mdat)) { + rockok = treeok = TRUE; + } else if ((mw_tmp = MON_WEP(mon)) && mw_tmp->cursed && + mon->weapon_check == NO_WEAPON_WANTED) { + rockok = is_pick(mw_tmp); + treeok = is_axe(mw_tmp); + } else { + rockok = (m_carrying(mon, PICK_AXE) || + (m_carrying(mon, DWARVISH_MATTOCK) && + !which_armor(mon, W_ARMS))); + treeok = (m_carrying(mon, AXE) || + (m_carrying(mon, BATTLE_AXE) && + !which_armor(mon, W_ARMS))); + } + thrudoor |= rockok || treeok; + } + +nexttry: /* eels prefer the water, but if there is no water nearby, + they will crawl over land */ + if(mon->mconf) { + flag |= ALLOW_ALL; + flag &= ~NOTONL; + } + if(!mon->mcansee) + flag |= ALLOW_SSM; + maxx = min(x+1,COLNO-1); + maxy = min(y+1,ROWNO-1); + for(nx = max(1,x-1); nx <= maxx; nx++) + for(ny = max(0,y-1); ny <= maxy; ny++) { + if(nx == x && ny == y) continue; + if(IS_ROCK(ntyp = levl[nx][ny].typ) && + !((flag & ALLOW_WALL) && may_passwall(nx,ny)) && + !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx,ny))) continue; + /* KMH -- Added iron bars */ + if (ntyp == IRONBARS && !(flag & ALLOW_BARS)) continue; + if(IS_DOOR(ntyp) && !amorphous(mdat) && + ((levl[nx][ny].doormask & D_CLOSED && !(flag & OPENDOOR)) || + (levl[nx][ny].doormask & D_LOCKED && !(flag & UNLOCKDOOR))) && + !thrudoor) continue; + if(nx != x && ny != y && (nodiag || +#ifdef REINCARNATION + ((IS_DOOR(nowtyp) && + ((levl[x][y].doormask & ~D_BROKEN) || Is_rogue_level(&u.uz))) || + (IS_DOOR(ntyp) && + ((levl[nx][ny].doormask & ~D_BROKEN) || Is_rogue_level(&u.uz)))) +#else + ((IS_DOOR(nowtyp) && (levl[x][y].doormask & ~D_BROKEN)) || + (IS_DOOR(ntyp) && (levl[nx][ny].doormask & ~D_BROKEN))) +#endif + )) + continue; + if((is_pool(nx,ny) == wantpool || poolok) && + (lavaok || !is_lava(nx,ny))) { + int dispx, dispy; + boolean monseeu = (mon->mcansee && (!Invis || perceives(mdat))); + boolean checkobj = OBJ_AT(nx,ny); + + /* Displacement also displaces the Elbereth/scare monster, + * as long as you are visible. + */ + if(Displaced && monseeu && (mon->mux==nx) && (mon->muy==ny)) { + dispx = u.ux; + dispy = u.uy; + } else { + dispx = nx; + dispy = ny; + } + + info[cnt] = 0; + if ((checkobj || Displaced) && onscary(dispx, dispy, mon)) { + if(!(flag & ALLOW_SSM)) continue; + info[cnt] |= ALLOW_SSM; + } + if((nx == u.ux && ny == u.uy) || + (nx == mon->mux && ny == mon->muy)) { + if (nx == u.ux && ny == u.uy) { + /* If it's right next to you, it found you, + * displaced or no. We must set mux and muy + * right now, so when we return we can tell + * that the ALLOW_U means to attack _you_ and + * not the image. + */ + mon->mux = u.ux; + mon->muy = u.uy; + } + if(!(flag & ALLOW_U)) continue; + info[cnt] |= ALLOW_U; + } else { + if(MON_AT(nx, ny)) { + struct monst *mtmp2 = m_at(nx, ny); + long mmflag = flag | mm_aggression(mon, mtmp2); + + if (!(mmflag & ALLOW_M)) continue; + info[cnt] |= ALLOW_M; + if (mtmp2->mtame) { + if (!(mmflag & ALLOW_TM)) continue; + info[cnt] |= ALLOW_TM; + } + } + /* Note: ALLOW_SANCT only prevents movement, not */ + /* attack, into a temple. */ + if(level.flags.has_temple && + *in_rooms(nx, ny, TEMPLE) && + !*in_rooms(x, y, TEMPLE) && + in_your_sanctuary((struct monst *)0, nx, ny)) { + if(!(flag & ALLOW_SANCT)) continue; + info[cnt] |= ALLOW_SANCT; + } + } + if(checkobj && sobj_at(CLOVE_OF_GARLIC, nx, ny)) { + if(flag & NOGARLIC) continue; + info[cnt] |= NOGARLIC; + } + if(checkobj && sobj_at(BOULDER, nx, ny)) { + if(!(flag & ALLOW_ROCK)) continue; + info[cnt] |= ALLOW_ROCK; + } + if (monseeu && onlineu(nx,ny)) { + if(flag & NOTONL) continue; + info[cnt] |= NOTONL; + } + if (nx != x && ny != y && bad_rock(mdat, x, ny) + && bad_rock(mdat, nx, y) + && (bigmonst(mdat) || (curr_mon_load(mon) > 600))) + continue; + /* The monster avoids a particular type of trap if it's familiar + * with the trap type. Pets get ALLOW_TRAPS and checking is + * done in dogmove.c. In either case, "harmless" traps are + * neither avoided nor marked in info[]. + */ + { register struct trap *ttmp = t_at(nx, ny); + if(ttmp) { + if(ttmp->ttyp >= TRAPNUM || ttmp->ttyp == 0) { +impossible("A monster looked at a very strange trap of type %d.", ttmp->ttyp); + continue; + } + if ((ttmp->ttyp != RUST_TRAP + || mdat == &mons[PM_IRON_GOLEM]) + && ttmp->ttyp != STATUE_TRAP + && ((ttmp->ttyp != PIT + && ttmp->ttyp != SPIKED_PIT + && ttmp->ttyp != TRAPDOOR + && ttmp->ttyp != HOLE) + || (!is_flyer(mdat) + && !is_floater(mdat) + && !is_clinger(mdat)) + || In_sokoban(&u.uz)) + && (ttmp->ttyp != SLP_GAS_TRAP || + !resists_sleep(mon)) + && (ttmp->ttyp != BEAR_TRAP || + (mdat->msize > MZ_SMALL && + !amorphous(mdat) && !is_flyer(mdat))) + && (ttmp->ttyp != FIRE_TRAP || + !resists_fire(mon)) + && (ttmp->ttyp != SQKY_BOARD || !is_flyer(mdat)) + && (ttmp->ttyp != WEB || (!amorphous(mdat) && + !webmaker(mdat))) + ) { + if (!(flag & ALLOW_TRAPS)) { + if (mon->mtrapseen & (1L << (ttmp->ttyp - 1))) + continue; + } + info[cnt] |= ALLOW_TRAPS; + } + } + } + poss[cnt].x = nx; + poss[cnt].y = ny; + cnt++; + } + } + if(!cnt && wantpool && !is_pool(x,y)) { + wantpool = FALSE; + goto nexttry; + } + return(cnt); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +/* Monster against monster special attacks; for the specified monster + combinations, this allows one monster to attack another adjacent one + in the absence of Conflict. There is no provision for targetting + other monsters; just hand to hand fighting when they happen to be + next to each other. */ +STATIC_OVL long +mm_aggression(magr, mdef) +struct monst *magr, /* monster that is currently deciding where to move */ + *mdef; /* another monster which is next to it */ +{ + /* supposedly purple worms are attracted to shrieking because they + like to eat shriekers, so attack the latter when feasible */ + if (magr->data == &mons[PM_PURPLE_WORM] && + mdef->data == &mons[PM_SHRIEKER]) + return ALLOW_M|ALLOW_TM; + /* Various other combinations such as dog vs cat, cat vs rat, and + elf vs orc have been suggested. For the time being we don't + support those. */ + return 0L; +} + +boolean +monnear(mon, x, y) +register struct monst *mon; +register int x,y; +/* Is the square close enough for the monster to move or attack into? */ +{ + register int distance = dist2(mon->mx, mon->my, x, y); + if (distance==2 && mon->data==&mons[PM_GRID_BUG]) return 0; + return((boolean)(distance < 3)); +} + +/* really free dead monsters */ +void +dmonsfree() +{ + struct monst **mtmp; + int count = 0; + + for (mtmp = &fmon; *mtmp;) { + if ((*mtmp)->mhp <= 0) { + struct monst *freetmp = *mtmp; + *mtmp = (*mtmp)->nmon; + dealloc_monst(freetmp); + count++; + } else + mtmp = &(*mtmp)->nmon; + } + + if (count != iflags.purge_monsters) + impossible("dmonsfree: %d removed doesn't match %d pending", + count, iflags.purge_monsters); + iflags.purge_monsters = 0; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* called when monster is moved to larger structure */ +void +replmon(mtmp, mtmp2) +register struct monst *mtmp, *mtmp2; +{ + struct obj *otmp; + + /* transfer the monster's inventory */ + for (otmp = mtmp2->minvent; otmp; otmp = otmp->nobj) { +#ifdef DEBUG + if (otmp->where != OBJ_MINVENT || otmp->ocarry != mtmp) + panic("replmon: minvent inconsistency"); +#endif + otmp->ocarry = mtmp2; + } + mtmp->minvent = 0; + + /* remove the old monster from the map and from `fmon' list */ + relmon(mtmp); + + /* finish adding its replacement */ +#ifdef STEED + if (mtmp == u.usteed) ; else /* don't place steed onto the map */ +#endif + place_monster(mtmp2, mtmp2->mx, mtmp2->my); + if (mtmp2->wormno) /* update level.monsters[wseg->wx][wseg->wy] */ + place_wsegs(mtmp2); /* locations to mtmp2 not mtmp. */ + if (emits_light(mtmp2->data)) { + /* since this is so rare, we don't have any `mon_move_light_source' */ + new_light_source(mtmp2->mx, mtmp2->my, + emits_light(mtmp2->data), + LS_MONSTER, (genericptr_t)mtmp2); + /* here we rely on the fact that `mtmp' hasn't actually been deleted */ + del_light_source(LS_MONSTER, (genericptr_t)mtmp); + } + mtmp2->nmon = fmon; + fmon = mtmp2; + if (u.ustuck == mtmp) u.ustuck = mtmp2; +#ifdef STEED + if (u.usteed == mtmp) u.usteed = mtmp2; +#endif + if (mtmp2->isshk) replshk(mtmp,mtmp2); + + /* discard the old monster */ + dealloc_monst(mtmp); +} + +/* release mon from display and monster list */ +void +relmon(mon) +register struct monst *mon; +{ + register struct monst *mtmp; + + if (fmon == (struct monst *)0) panic ("relmon: no fmon available."); + + remove_monster(mon->mx, mon->my); + + if(mon == fmon) fmon = fmon->nmon; + else { + for(mtmp = fmon; mtmp && mtmp->nmon != mon; mtmp = mtmp->nmon) ; + if(mtmp) mtmp->nmon = mon->nmon; + else panic("relmon: mon not in list."); + } +} + +/* remove effects of mtmp from other data structures */ +STATIC_OVL void +m_detach(mtmp, mptr) +struct monst *mtmp; +struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */ +{ + if (mtmp->mleashed) m_unleash(mtmp, FALSE); + /* to prevent an infinite relobj-flooreffects-hmon-killed loop */ + mtmp->mtrapped = 0; + mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */ + relobj(mtmp, 0, FALSE); + remove_monster(mtmp->mx, mtmp->my); + if (emits_light(mptr)) + del_light_source(LS_MONSTER, (genericptr_t)mtmp); + newsym(mtmp->mx,mtmp->my); + unstuck(mtmp); + fill_pit(mtmp->mx, mtmp->my); + + if(mtmp->isshk) shkgone(mtmp); + if(mtmp->wormno) wormgone(mtmp); + iflags.purge_monsters++; +} + +/* find the worn amulet of life saving which will save a monster */ +struct obj * +mlifesaver(mon) +struct monst *mon; +{ + if (!nonliving(mon->data)) { + struct obj *otmp = which_armor(mon, W_AMUL); + + if (otmp && otmp->otyp == AMULET_OF_LIFE_SAVING) + return otmp; + } + return (struct obj *)0; +} + +STATIC_OVL void +lifesaved_monster(mtmp) +struct monst *mtmp; +{ + struct obj *lifesave = mlifesaver(mtmp); + + if (lifesave) { + /* not canseemon; amulets are on the head, so you don't want */ + /* to show this for a long worm with only a tail visible. */ + /* Nor do you check invisibility, because glowing and disinte- */ + /* grating amulets are always visible. */ + if (cansee(mtmp->mx, mtmp->my)) { + pline("But wait..."); + pline("%s medallion begins to glow!", + s_suffix(Monnam(mtmp))); + makeknown(AMULET_OF_LIFE_SAVING); + if (attacktype(mtmp->data, AT_EXPL) + || attacktype(mtmp->data, AT_BOOM)) + pline("%s reconstitutes!", Monnam(mtmp)); + else + pline("%s looks much better!", Monnam(mtmp)); + pline_The("medallion crumbles to dust!"); + } + m_useup(mtmp, lifesave); + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + if (mtmp->mtame && !mtmp->isminion) { + wary_dog(mtmp, FALSE); + } + if (mtmp->mhpmax <= 0) mtmp->mhpmax = 10; + mtmp->mhp = mtmp->mhpmax; + if (mvitals[monsndx(mtmp->data)].mvflags & G_GENOD) { + if (cansee(mtmp->mx, mtmp->my)) + pline("Unfortunately %s is still genocided...", + mon_nam(mtmp)); + } else + return; + } + mtmp->mhp = 0; +} + +void +mondead(mtmp) +register struct monst *mtmp; +{ + struct permonst *mptr; + int tmp; + + if(mtmp->isgd) { + /* if we're going to abort the death, it *must* be before + * the m_detach or there will be relmon problems later */ + if(!grddead(mtmp)) return; + } + lifesaved_monster(mtmp); + if (mtmp->mhp > 0) return; + +#ifdef STEED + /* Player is thrown from his steed when it dies */ + if (mtmp == u.usteed) + dismount_steed(DISMOUNT_GENERIC); +#endif + + mptr = mtmp->data; /* save this for m_detach() */ + /* restore chameleon, lycanthropes to true form at death */ + if (mtmp->cham) + set_mon_data(mtmp, &mons[cham_to_pm[mtmp->cham]], -1); + else if (mtmp->data == &mons[PM_WEREJACKAL]) + set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1); + else if (mtmp->data == &mons[PM_WEREWOLF]) + set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF], -1); + else if (mtmp->data == &mons[PM_WERERAT]) + set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1); + + /* if MAXMONNO monsters of a given type have died, and it + * can be done, extinguish that monster. + * + * mvitals[].died does double duty as total number of dead monsters + * and as experience factor for the player killing more monsters. + * this means that a dragon dying by other means reduces the + * experience the player gets for killing a dragon directly; this + * is probably not too bad, since the player likely finagled the + * first dead dragon via ring of conflict or pets, and extinguishing + * based on only player kills probably opens more avenues of abuse + * for rings of conflict and such. + */ + tmp = monsndx(mtmp->data); + if (mvitals[tmp].died < 255) mvitals[tmp].died++; + + /* if it's a (possibly polymorphed) quest leader, mark him as dead */ + if (mtmp->m_id == quest_status.leader_m_id) + quest_status.leader_is_dead = TRUE; +#ifdef MAIL + /* if the mail daemon dies, no more mail delivery. -3. */ + if (tmp == PM_MAIL_DAEMON) mvitals[tmp].mvflags |= G_GENOD; +#endif + +#ifdef KOPS + if (mtmp->data->mlet == S_KOP) { + /* Dead Kops may come back. */ + switch(rnd(5)) { + case 1: /* returns near the stairs */ + (void) makemon(mtmp->data,xdnstair,ydnstair,NO_MM_FLAGS); + break; + case 2: /* randomly */ + (void) makemon(mtmp->data,0,0,NO_MM_FLAGS); + break; + default: + break; + } + } +#endif + if(mtmp->iswiz) wizdead(); + if(mtmp->data->msound == MS_NEMESIS) nemdead(); + if(glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) + unmap_object(mtmp->mx, mtmp->my); + m_detach(mtmp, mptr); +} + +/* TRUE if corpse might be dropped, magr may die if mon was swallowed */ +boolean +corpse_chance(mon, magr, was_swallowed) +struct monst *mon; +struct monst *magr; /* killer, if swallowed */ +boolean was_swallowed; /* digestion */ +{ + struct permonst *mdat = mon->data; + int i, tmp; + + if (mdat == &mons[PM_VLAD_THE_IMPALER] || mdat->mlet == S_LICH) { + if (cansee(mon->mx, mon->my) && !was_swallowed) + pline("%s body crumbles into dust.", s_suffix(Monnam(mon))); + return FALSE; + } + + /* Gas spores always explode upon death */ + for(i = 0; i < NATTK; i++) { + if (mdat->mattk[i].aatyp == AT_BOOM) { + if (mdat->mattk[i].damn) + tmp = d((int)mdat->mattk[i].damn, + (int)mdat->mattk[i].damd); + else if(mdat->mattk[i].damd) + tmp = d((int)mdat->mlevel+1, (int)mdat->mattk[i].damd); + else tmp = 0; + if (was_swallowed && magr) { + if (magr == &youmonst) { + There("is an explosion in your %s!", + body_part(STOMACH)); + Sprintf(killer_buf, "%s explosion", + s_suffix(mdat->mname)); + if (Half_physical_damage) tmp = (tmp+1) / 2; + losehp(tmp, killer_buf, KILLED_BY_AN); + } else { + if (flags.soundok) You_hear("an explosion."); + magr->mhp -= tmp; + if (magr->mhp < 1) mondied(magr); + if (magr->mhp < 1) { /* maybe lifesaved */ + if (canspotmon(magr)) + pline("%s rips open!", Monnam(magr)); + } else if (canseemon(magr)) + pline("%s seems to have indigestion.", + Monnam(magr)); + } + + return FALSE; + } + + Sprintf(killer_buf, "%s explosion", s_suffix(mdat->mname)); + killer = killer_buf; + killer_format = KILLED_BY_AN; + explode(mon->mx, mon->my, -1, tmp, MON_EXPLODE, EXPL_NOXIOUS); + return (FALSE); + } + } + + /* must duplicate this below check in xkilled() since it results in + * creating no objects as well as no corpse + */ + if (LEVEL_SPECIFIC_NOCORPSE(mdat)) + return FALSE; + + if (bigmonst(mdat) || mdat == &mons[PM_LIZARD] + || is_golem(mdat) + || is_mplayer(mdat) + || is_rider(mdat)) + return TRUE; + return (boolean) (!rn2((int) + (2 + ((int)(mdat->geno & G_FREQ)<2) + verysmall(mdat)))); +} + +/* drop (perhaps) a cadaver and remove monster */ +void +mondied(mdef) +register struct monst *mdef; +{ + mondead(mdef); + if (mdef->mhp > 0) return; /* lifesaved */ + + if (corpse_chance(mdef, (struct monst *)0, FALSE) && + (accessible(mdef->mx, mdef->my) || is_pool(mdef->mx, mdef->my))) + (void) make_corpse(mdef); +} + +/* monster disappears, not dies */ +void +mongone(mdef) +register struct monst *mdef; +{ + mdef->mhp = 0; /* can skip some inventory bookkeeping */ +#ifdef STEED + /* Player is thrown from his steed when it disappears */ + if (mdef == u.usteed) + dismount_steed(DISMOUNT_GENERIC); +#endif + + /* drop special items like the Amulet so that a dismissed Kop or nurse + can't remove them from the game */ + mdrop_special_objs(mdef); + /* release rest of monster's inventory--it is removed from game */ + discard_minvent(mdef); +#ifndef GOLDOBJ + mdef->mgold = 0L; +#endif + m_detach(mdef, mdef->data); +} + +/* drop a statue or rock and remove monster */ +void +monstone(mdef) +register struct monst *mdef; +{ + struct obj *otmp, *obj, *oldminvent; + xchar x = mdef->mx, y = mdef->my; + boolean wasinside = FALSE; + + /* we have to make the statue before calling mondead, to be able to + * put inventory in it, and we have to check for lifesaving before + * making the statue.... + */ + lifesaved_monster(mdef); + if (mdef->mhp > 0) return; + + mdef->mtrapped = 0; /* (see m_detach) */ + + if ((int)mdef->data->msize > MZ_TINY || + !rn2(2 + ((int) (mdef->data->geno & G_FREQ) > 2))) { + oldminvent = 0; + /* some objects may end up outside the statue */ + while ((obj = mdef->minvent) != 0) { + obj_extract_self(obj); + if (obj->owornmask) + update_mon_intrinsics(mdef, obj, FALSE, TRUE); + obj_no_longer_held(obj); + if (obj->owornmask & W_WEP) + setmnotwielded(mdef,obj); + obj->owornmask = 0L; + if (obj->otyp == BOULDER || +#if 0 /* monsters don't carry statues */ + (obj->otyp == STATUE && mons[obj->corpsenm].msize >= mdef->data->msize) || +#endif + obj_resists(obj, 0, 0)) { + if (flooreffects(obj, x, y, "fall")) continue; + place_object(obj, x, y); + } else { + if (obj->lamplit) end_burn(obj, TRUE); + obj->nobj = oldminvent; + oldminvent = obj; + } + } + /* defer statue creation until after inventory removal + so that saved monster traits won't retain any stale + item-conferred attributes */ + otmp = mkcorpstat(STATUE, KEEPTRAITS(mdef) ? mdef : 0, + mdef->data, x, y, FALSE); + if (mdef->mnamelth) otmp = oname(otmp, NAME(mdef)); + while ((obj = oldminvent) != 0) { + oldminvent = obj->nobj; + (void) add_to_container(otmp, obj); + } +#ifndef GOLDOBJ + if (mdef->mgold) { + struct obj *au; + au = mksobj(GOLD_PIECE, FALSE, FALSE); + au->quan = mdef->mgold; + au->owt = weight(au); + (void) add_to_container(otmp, au); + mdef->mgold = 0; + } +#endif + /* Archeologists should not break unique statues */ + if (mdef->data->geno & G_UNIQ) + otmp->spe = 1; + otmp->owt = weight(otmp); + } else + otmp = mksobj_at(ROCK, x, y, TRUE, FALSE); + + stackobj(otmp); + /* mondead() already does this, but we must do it before the newsym */ + if(glyph_is_invisible(levl[x][y].glyph)) + unmap_object(x, y); + if (cansee(x, y)) newsym(x,y); + /* We don't currently trap the hero in the statue in this case but we could */ + if (u.uswallow && u.ustuck == mdef) wasinside = TRUE; + mondead(mdef); + if (wasinside) { + if (is_animal(mdef->data)) + You("%s through an opening in the new %s.", + locomotion(youmonst.data, "jump"), + xname(otmp)); + } +} + +/* another monster has killed the monster mdef */ +void +monkilled(mdef, fltxt, how) +register struct monst *mdef; +const char *fltxt; +int how; +{ + boolean be_sad = FALSE; /* true if unseen pet is killed */ + + if ((mdef->wormno ? worm_known(mdef) : cansee(mdef->mx, mdef->my)) + && fltxt) + pline("%s is %s%s%s!", Monnam(mdef), + nonliving(mdef->data) ? "destroyed" : "killed", + *fltxt ? " by the " : "", + fltxt + ); + else + be_sad = (mdef->mtame != 0); + + /* no corpses if digested or disintegrated */ + if(how == AD_DGST || how == -AD_RBRE) + mondead(mdef); + else + mondied(mdef); + + if (be_sad && mdef->mhp <= 0) + You("have a sad feeling for a moment, then it passes."); +} + +void +unstuck(mtmp) +register struct monst *mtmp; +{ + if(u.ustuck == mtmp) { + if(u.uswallow){ + u.ux = mtmp->mx; + u.uy = mtmp->my; + u.uswallow = 0; + u.uswldtim = 0; + if (Punished) placebc(); + vision_full_recalc = 1; + docrt(); + } + u.ustuck = 0; + } +} + +void +killed(mtmp) +register struct monst *mtmp; +{ + xkilled(mtmp, 1); +} + +/* the player has killed the monster mtmp */ +void +xkilled(mtmp, dest) + register struct monst *mtmp; +/* + * Dest=1, normal; dest=0, don't print message; dest=2, don't drop corpse + * either; dest=3, message but no corpse + */ + int dest; +{ + register int tmp, x = mtmp->mx, y = mtmp->my; + register struct permonst *mdat; + int mndx; + register struct obj *otmp; + register struct trap *t; + boolean redisp = FALSE; + boolean wasinside = u.uswallow && (u.ustuck == mtmp); + + + /* KMH, conduct */ + u.uconduct.killer++; + + if (dest & 1) { + const char *verb = nonliving(mtmp->data) ? "destroy" : "kill"; + + if (!wasinside && !canspotmon(mtmp)) + You("%s it!", verb); + else { + You("%s %s!", verb, + !mtmp->mtame ? mon_nam(mtmp) : + x_monnam(mtmp, + mtmp->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, + FALSE)); + } + } + + if (mtmp->mtrapped && (t = t_at(x, y)) != 0 && + (t->ttyp == PIT || t->ttyp == SPIKED_PIT) && + sobj_at(BOULDER, x, y)) + dest |= 2; /* + * Prevent corpses/treasure being created "on top" + * of the boulder that is about to fall in. This is + * out of order, but cannot be helped unless this + * whole routine is rearranged. + */ + + /* your pet knows who just killed it...watch out */ + if (mtmp->mtame && !mtmp->isminion) EDOG(mtmp)->killed_by_u = 1; + + /* dispose of monster and make cadaver */ + if(stoned) monstone(mtmp); + else mondead(mtmp); + + if (mtmp->mhp > 0) { /* monster lifesaved */ + /* Cannot put the non-visible lifesaving message in + * lifesaved_monster() since the message appears only when you + * kill it (as opposed to visible lifesaving which always + * appears). + */ + stoned = FALSE; + if (!cansee(x,y)) pline("Maybe not..."); + return; + } + + mdat = mtmp->data; /* note: mondead can change mtmp->data */ + mndx = monsndx(mdat); + + if (stoned) { + stoned = FALSE; + goto cleanup; + } + + if((dest & 2) || LEVEL_SPECIFIC_NOCORPSE(mdat)) + goto cleanup; + +#ifdef MAIL + if(mdat == &mons[PM_MAIL_DAEMON]) { + stackobj(mksobj_at(SCR_MAIL, x, y, FALSE, FALSE)); + redisp = TRUE; + } +#endif + if((!accessible(x, y) && !is_pool(x, y)) || + (x == u.ux && y == u.uy)) { + /* might be mimic in wall or corpse in lava or on player's spot */ + redisp = TRUE; + if(wasinside) spoteffects(TRUE); + } else if(x != u.ux || y != u.uy) { + /* might be here after swallowed */ + if (!rn2(6) && !(mvitals[mndx].mvflags & G_NOCORPSE) +#ifdef KOPS + && mdat->mlet != S_KOP +#endif + ) { + int typ; + + otmp = mkobj_at(RANDOM_CLASS, x, y, TRUE); + /* Don't create large objects from small monsters */ + typ = otmp->otyp; + if (mdat->msize < MZ_HUMAN && typ != FOOD_RATION + && typ != LEASH + && typ != FIGURINE + && (otmp->owt > 3 || + objects[typ].oc_big /*oc_bimanual/oc_bulky*/ || + is_spear(otmp) || is_pole(otmp) || + typ == MORNING_STAR)) { + delobj(otmp); + } else redisp = TRUE; + } + /* Whether or not it always makes a corpse is, in theory, + * different from whether or not the corpse is "special"; + * if we want both, we have to specify it explicitly. + */ + if (corpse_chance(mtmp, (struct monst *)0, FALSE)) + (void) make_corpse(mtmp); + } + if(redisp) newsym(x,y); +cleanup: + /* punish bad behaviour */ + if(is_human(mdat) && (!always_hostile(mdat) && mtmp->malign <= 0) && + (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD) && + u.ualign.type != A_CHAOTIC) { + HTelepat &= ~INTRINSIC; + change_luck(-2); + You("murderer!"); + if (Blind && !Blind_telepat) + see_monsters(); /* Can't sense monsters any more. */ + } + if((mtmp->mpeaceful && !rn2(2)) || mtmp->mtame) change_luck(-1); + if (is_unicorn(mdat) && + sgn(u.ualign.type) == sgn(mdat->maligntyp)) { + change_luck(-5); + You_feel("guilty..."); + } + + /* give experience points */ + tmp = experience(mtmp, (int)mvitals[mndx].died + 1); + more_experienced(tmp, 0); + newexplevel(); /* will decide if you go up */ + + /* adjust alignment points */ + if (mtmp->m_id == quest_status.leader_m_id) { /* REAL BAD! */ + adjalign(-(u.ualign.record+(int)ALIGNLIM/2)); + pline("That was %sa bad idea...", + u.uevent.qcompleted ? "probably " : ""); + } else if (mdat->msound == MS_NEMESIS) /* Real good! */ + adjalign((int)(ALIGNLIM/4)); + else if (mdat->msound == MS_GUARDIAN) { /* Bad */ + adjalign(-(int)(ALIGNLIM/8)); + if (!Hallucination) pline("That was probably a bad idea..."); + else pline("Whoopsie-daisy!"); + }else if (mtmp->ispriest) { + adjalign((p_coaligned(mtmp)) ? -2 : 2); + /* cancel divine protection for killing your priest */ + if (p_coaligned(mtmp)) u.ublessed = 0; + if (mdat->maligntyp == A_NONE) + adjalign((int)(ALIGNLIM / 4)); /* BIG bonus */ + } else if (mtmp->mtame) { + adjalign(-15); /* bad!! */ + /* your god is mighty displeased... */ + if (!Hallucination) You_hear("the rumble of distant thunder..."); + else You_hear("the studio audience applaud!"); + } else if (mtmp->mpeaceful) + adjalign(-5); + + /* malign was already adjusted for u.ualign.type and randomization */ + adjalign(mtmp->malign); +} + +/* changes the monster into a stone monster of the same type */ +/* this should only be called when poly_when_stoned() is true */ +void +mon_to_stone(mtmp) + register struct monst *mtmp; +{ + if(mtmp->data->mlet == S_GOLEM) { + /* it's a golem, and not a stone golem */ + if(canseemon(mtmp)) + pline("%s solidifies...", Monnam(mtmp)); + if (newcham(mtmp, &mons[PM_STONE_GOLEM], FALSE, FALSE)) { + if(canseemon(mtmp)) + pline("Now it's %s.", an(mtmp->data->mname)); + } else { + if(canseemon(mtmp)) + pline("... and returns to normal."); + } + } else + impossible("Can't polystone %s!", a_monnam(mtmp)); +} + +void +mnexto(mtmp) /* Make monster mtmp next to you (if possible) */ + struct monst *mtmp; +{ + coord mm; + +#ifdef STEED + if (mtmp == u.usteed) { + /* Keep your steed in sync with you instead */ + mtmp->mx = u.ux; + mtmp->my = u.uy; + return; + } +#endif + + if(!enexto(&mm, u.ux, u.uy, mtmp->data)) return; + rloc_to(mtmp, mm.x, mm.y); + return; +} + +/* mnearto() + * Put monster near (or at) location if possible. + * Returns: + * 1 - if a monster was moved from x, y to put mtmp at x, y. + * 0 - in most cases. + */ +boolean +mnearto(mtmp,x,y,move_other) +register struct monst *mtmp; +xchar x, y; +boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */ +{ + struct monst *othermon = (struct monst *)0; + xchar newx, newy; + coord mm; + + if ((mtmp->mx == x) && (mtmp->my == y)) return(FALSE); + + if (move_other && (othermon = m_at(x, y))) { + if (othermon->wormno) + remove_worm(othermon); + else + remove_monster(x, y); + } + + newx = x; + newy = y; + + if (!goodpos(newx, newy, mtmp, 0)) { + /* actually we have real problems if enexto ever fails. + * migrating_mons that need to be placed will cause + * no end of trouble. + */ + if (!enexto(&mm, newx, newy, mtmp->data)) return(FALSE); + newx = mm.x; newy = mm.y; + } + + rloc_to(mtmp, newx, newy); + + if (move_other && othermon) { + othermon->mx = othermon->my = 0; + (void) mnearto(othermon, x, y, FALSE); + if ((othermon->mx != x) || (othermon->my != y)) + return(TRUE); + } + + return(FALSE); +} + + +static const char *poiseff[] = { + + " feel weaker", "r brain is on fire", + "r judgement is impaired", "r muscles won't obey you", + " feel very sick", " break out in hives" +}; + +void +poisontell(typ) + + int typ; +{ + pline("You%s.", poiseff[typ]); +} + +void +poisoned(string, typ, pname, fatal) +const char *string, *pname; +int typ, fatal; +{ + int i, plural, kprefix = KILLED_BY_AN; + boolean thrown_weapon = (fatal < 0); + + if (thrown_weapon) fatal = -fatal; + if(strcmp(string, "blast") && !thrown_weapon) { + /* 'blast' has already given a 'poison gas' message */ + /* so have "poison arrow", "poison dart", etc... */ + plural = (string[strlen(string) - 1] == 's')? 1 : 0; + /* avoid "The" Orcus's sting was poisoned... */ + pline("%s%s %s poisoned!", isupper(*string) ? "" : "The ", + string, plural ? "were" : "was"); + } + + if(Poison_resistance) { + if(!strcmp(string, "blast")) shieldeff(u.ux, u.uy); + pline_The("poison doesn't seem to affect you."); + return; + } + /* suppress killer prefix if it already has one */ + if ((i = name_to_mon(pname)) >= LOW_PM && mons[i].geno & G_UNIQ) { + kprefix = KILLED_BY; + if (!type_is_pname(&mons[i])) pname = the(pname); + } else if (!strncmpi(pname, "the ", 4) || + !strncmpi(pname, "an ", 3) || + !strncmpi(pname, "a ", 2)) { + /*[ does this need a plural check too? ]*/ + kprefix = KILLED_BY; + } + i = rn2(fatal + 20*thrown_weapon); + if(i == 0 && typ != A_CHA) { + u.uhp = -1; + pline_The("poison was deadly..."); + } else if(i <= 5) { + /* Check that a stat change was made */ + if (adjattrib(typ, thrown_weapon ? -1 : -rn1(3,3), 1)) + pline("You%s!", poiseff[typ]); + } else { + i = thrown_weapon ? rnd(6) : rn1(10,6); + if(Half_physical_damage) i = (i+1) / 2; + losehp(i, pname, kprefix); + } + if(u.uhp < 1) { + killer_format = kprefix; + killer = pname; + /* "Poisoned by a poisoned ___" is redundant */ + done(strstri(pname, "poison") ? DIED : POISONING); + } + (void) encumber_msg(); +} + +/* monster responds to player action; not the same as a passive attack */ +/* assumes reason for response has been tested, and response _must_ be made */ +void +m_respond(mtmp) +register struct monst *mtmp; +{ + if(mtmp->data->msound == MS_SHRIEK) { + if(flags.soundok) { + pline("%s shrieks.", Monnam(mtmp)); + stop_occupation(); + } + if (!rn2(10)) { + if (!rn2(13)) + (void) makemon(&mons[PM_PURPLE_WORM], 0, 0, NO_MM_FLAGS); + else + (void) makemon((struct permonst *)0, 0, 0, NO_MM_FLAGS); + + } + aggravate(); + } + if(mtmp->data == &mons[PM_MEDUSA]) { + register int i; + for(i = 0; i < NATTK; i++) + if(mtmp->data->mattk[i].aatyp == AT_GAZE) { + (void) gazemu(mtmp, &mtmp->data->mattk[i]); + break; + } + } +} + +#endif /* OVLB */ +#ifdef OVL2 + +void +setmangry(mtmp) +register struct monst *mtmp; +{ + mtmp->mstrategy &= ~STRAT_WAITMASK; + if(!mtmp->mpeaceful) return; + if(mtmp->mtame) return; + mtmp->mpeaceful = 0; + if(mtmp->ispriest) { + if(p_coaligned(mtmp)) adjalign(-5); /* very bad */ + else adjalign(2); + } else + adjalign(-1); /* attacking peaceful monsters is bad */ + if (couldsee(mtmp->mx, mtmp->my)) { + if (humanoid(mtmp->data) || mtmp->isshk || mtmp->isgd) + pline("%s gets angry!", Monnam(mtmp)); + else if (flags.verbose && flags.soundok) growl(mtmp); + } + + /* attacking your own quest leader will anger his or her guardians */ + if (!flags.mon_moving && /* should always be the case here */ + mtmp->data == &mons[quest_info(MS_LEADER)]) { + struct monst *mon; + struct permonst *q_guardian = &mons[quest_info(MS_GUARDIAN)]; + int got_mad = 0; + + /* guardians will sense this attack even if they can't see it */ + for (mon = fmon; mon; mon = mon->nmon) + if (!DEADMONSTER(mon) && mon->data == q_guardian && mon->mpeaceful) { + mon->mpeaceful = 0; + if (canseemon(mon)) ++got_mad; + } + if (got_mad && !Hallucination) + pline_The("%s appear%s to be angry too...", + got_mad == 1 ? q_guardian->mname : + makeplural(q_guardian->mname), + got_mad == 1 ? "s" : ""); + } +} + +void +wakeup(mtmp) +register struct monst *mtmp; +{ + mtmp->msleeping = 0; + mtmp->meating = 0; /* assume there's no salvagable food left */ + setmangry(mtmp); + if(mtmp->m_ap_type) seemimic(mtmp); + else if (flags.forcefight && !flags.mon_moving && mtmp->mundetected) { + mtmp->mundetected = 0; + newsym(mtmp->mx, mtmp->my); + } +} + +/* Wake up nearby monsters. */ +void +wake_nearby() +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (!DEADMONSTER(mtmp) && distu(mtmp->mx,mtmp->my) < u.ulevel*20) { + mtmp->msleeping = 0; + if (mtmp->mtame && !mtmp->isminion) + EDOG(mtmp)->whistletime = moves; + } + } +} + +/* Wake up monsters near some particular location. */ +void +wake_nearto(x, y, distance) +register int x, y, distance; +{ + register struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (!DEADMONSTER(mtmp) && mtmp->msleeping && (distance == 0 || + dist2(mtmp->mx, mtmp->my, x, y) < distance)) + mtmp->msleeping = 0; + } +} + +/* NOTE: we must check for mimicry before calling this routine */ +void +seemimic(mtmp) +register struct monst *mtmp; +{ + unsigned old_app = mtmp->mappearance; + uchar old_ap_type = mtmp->m_ap_type; + + mtmp->m_ap_type = M_AP_NOTHING; + mtmp->mappearance = 0; + + /* + * Discovered mimics don't block light. + */ + if (((old_ap_type == M_AP_FURNITURE && + (old_app == S_hcdoor || old_app == S_vcdoor)) || + (old_ap_type == M_AP_OBJECT && old_app == BOULDER)) && + !does_block(mtmp->mx, mtmp->my, &levl[mtmp->mx][mtmp->my])) + unblock_point(mtmp->mx, mtmp->my); + + newsym(mtmp->mx,mtmp->my); +} + +/* force all chameleons to become normal */ +void +rescham() +{ + register struct monst *mtmp; + int mcham; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + mcham = (int) mtmp->cham; + if (mcham) { + mtmp->cham = CHAM_ORDINARY; + (void) newcham(mtmp, &mons[cham_to_pm[mcham]], + FALSE, FALSE); + } + if(is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN) + new_were(mtmp); + if(mtmp->m_ap_type && cansee(mtmp->mx, mtmp->my)) { + seemimic(mtmp); + /* we pretend that the mimic doesn't */ + /* know that it has been unmasked. */ + mtmp->msleeping = 1; + } + } +} + +/* Let the chameleons change again -dgk */ +void +restartcham() +{ + register struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + mtmp->cham = pm_to_cham(monsndx(mtmp->data)); + if (mtmp->data->mlet == S_MIMIC && mtmp->msleeping && + cansee(mtmp->mx, mtmp->my)) { + set_mimic_sym(mtmp); + newsym(mtmp->mx,mtmp->my); + } + } +} + +/* called when restoring a monster from a saved level; protection + against shape-changing might be different now than it was at the + time the level was saved. */ +void +restore_cham(mon) +struct monst *mon; +{ + int mcham; + + if (Protection_from_shape_changers) { + mcham = (int) mon->cham; + if (mcham) { + mon->cham = CHAM_ORDINARY; + (void) newcham(mon, &mons[cham_to_pm[mcham]], FALSE, FALSE); + } else if (is_were(mon->data) && !is_human(mon->data)) { + new_were(mon); + } + } else if (mon->cham == CHAM_ORDINARY) { + mon->cham = pm_to_cham(monsndx(mon->data)); + } +} + +/* unwatched hiders may hide again; if so, a 1 is returned. */ +STATIC_OVL boolean +restrap(mtmp) +register struct monst *mtmp; +{ + if(mtmp->cham || mtmp->mcan || mtmp->m_ap_type || + cansee(mtmp->mx, mtmp->my) || rn2(3) || (mtmp == u.ustuck) || + (sensemon(mtmp) && distu(mtmp->mx, mtmp->my) <= 2)) + return(FALSE); + + if(mtmp->data->mlet == S_MIMIC) { + set_mimic_sym(mtmp); + return(TRUE); + } else + if(levl[mtmp->mx][mtmp->my].typ == ROOM) { + mtmp->mundetected = 1; + return(TRUE); + } + + return(FALSE); +} + +short *animal_list = 0; /* list of PM values for animal monsters */ +int animal_list_count; + +void +mon_animal_list(construct) +boolean construct; +{ + if (construct) { + short animal_temp[SPECIAL_PM]; + int i, n; + + /* if (animal_list) impossible("animal_list already exists"); */ + + for (n = 0, i = LOW_PM; i < SPECIAL_PM; i++) + if (is_animal(&mons[i])) animal_temp[n++] = i; + /* if (n == 0) animal_temp[n++] = NON_PM; */ + + animal_list = (short *)alloc(n * sizeof *animal_list); + (void) memcpy((genericptr_t)animal_list, + (genericptr_t)animal_temp, + n * sizeof *animal_list); + animal_list_count = n; + } else { /* release */ + if (animal_list) free((genericptr_t)animal_list), animal_list = 0; + animal_list_count = 0; + } +} + +STATIC_OVL int +pick_animal() +{ + if (!animal_list) mon_animal_list(TRUE); + + return animal_list[rn2(animal_list_count)]; +} + +STATIC_OVL int +select_newcham_form(mon) +struct monst *mon; +{ + int mndx = NON_PM; + + switch (mon->cham) { + case CHAM_SANDESTIN: + if (rn2(7)) mndx = pick_nasty(); + break; + case CHAM_DOPPELGANGER: + if (!rn2(7)) mndx = pick_nasty(); + else if (rn2(3)) mndx = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1, + PM_ARCHEOLOGIST); + break; + case CHAM_CHAMELEON: + if (!rn2(3)) mndx = pick_animal(); + break; + case CHAM_ORDINARY: + { + struct obj *m_armr = which_armor(mon, W_ARM); + + if (m_armr && Is_dragon_scales(m_armr)) + mndx = Dragon_scales_to_pm(m_armr) - mons; + else if (m_armr && Is_dragon_mail(m_armr)) + mndx = Dragon_mail_to_pm(m_armr) - mons; + } + break; + } +#ifdef WIZARD + /* For debugging only: allow control of polymorphed monster; not saved */ + if (wizard && iflags.mon_polycontrol) { + char pprompt[BUFSZ], buf[BUFSZ]; + int tries = 0; + do { + Sprintf(pprompt, + "Change %s into what kind of monster? [type the name]", + mon_nam(mon)); + getlin(pprompt,buf); + mndx = name_to_mon(buf); + if (mndx < LOW_PM) + You("cannot polymorph %s into that.", mon_nam(mon)); + else break; + } while(++tries < 5); + if (tries==5) pline(thats_enough_tries); + } +#endif /*WIZARD*/ + if (mndx == NON_PM) mndx = rn1(SPECIAL_PM - LOW_PM, LOW_PM); + return mndx; +} + +/* make a chameleon look like a new monster; returns 1 if it actually changed */ +int +newcham(mtmp, mdat, polyspot, msg) +struct monst *mtmp; +struct permonst *mdat; +boolean polyspot; /* change is the result of wand or spell of polymorph */ +boolean msg; /* "The oldmon turns into a newmon!" */ +{ + int mhp, hpn, hpd; + int mndx, tryct; + struct permonst *olddata = mtmp->data; + char oldname[BUFSZ]; + + if (msg) { + /* like Monnam() but never mention saddle */ + Strcpy(oldname, x_monnam(mtmp, ARTICLE_THE, (char *)0, + SUPPRESS_SADDLE, FALSE)); + oldname[0] = highc(oldname[0]); + } + + /* mdat = 0 -> caller wants a random monster shape */ + tryct = 0; + if (mdat == 0) { + while (++tryct <= 100) { + mndx = select_newcham_form(mtmp); + mdat = &mons[mndx]; + if ((mvitals[mndx].mvflags & G_GENOD) != 0 || + is_placeholder(mdat)) continue; + /* polyok rules out all M2_PNAME and M2_WERE's; + select_newcham_form might deliberately pick a player + character type, so we can't arbitrarily rule out all + human forms any more */ + if (is_mplayer(mdat) || (!is_human(mdat) && polyok(mdat))) + break; + } + if (tryct > 100) return 0; /* Should never happen */ + } else if (mvitals[monsndx(mdat)].mvflags & G_GENOD) + return(0); /* passed in mdat is genocided */ + + if(is_male(mdat)) { + if(mtmp->female) mtmp->female = FALSE; + } else if (is_female(mdat)) { + if(!mtmp->female) mtmp->female = TRUE; + } else if (!is_neuter(mdat)) { + if(!rn2(10)) mtmp->female = !mtmp->female; + } + + if (In_endgame(&u.uz) && is_mplayer(olddata)) { + /* mplayers start out as "Foo the Bar", but some of the + * titles are inappropriate when polymorphed, particularly + * into the opposite sex. players don't use ranks when + * polymorphed, so dropping the rank for mplayers seems + * reasonable. + */ + char *p = index(NAME(mtmp), ' '); + if (p) { + *p = '\0'; + mtmp->mnamelth = p - NAME(mtmp) + 1; + } + } + + if(mdat == mtmp->data) return(0); /* still the same monster */ + + if(mtmp->wormno) { /* throw tail away */ + wormgone(mtmp); + place_monster(mtmp, mtmp->mx, mtmp->my); + } + + hpn = mtmp->mhp; + hpd = (mtmp->m_lev < 50) ? ((int)mtmp->m_lev)*8 : mdat->mlevel; + if(!hpd) hpd = 4; + + mtmp->m_lev = adj_lev(mdat); /* new monster level */ + + mhp = (mtmp->m_lev < 50) ? ((int)mtmp->m_lev)*8 : mdat->mlevel; + if(!mhp) mhp = 4; + + /* new hp: same fraction of max as before */ +#ifndef LINT + mtmp->mhp = (int)(((long)hpn*(long)mhp)/(long)hpd); +#endif + if(mtmp->mhp < 0) mtmp->mhp = hpn; /* overflow */ +/* Unlikely but not impossible; a 1HD creature with 1HP that changes into a + 0HD creature will require this statement */ + if (!mtmp->mhp) mtmp->mhp = 1; + +/* and the same for maximum hit points */ + hpn = mtmp->mhpmax; +#ifndef LINT + mtmp->mhpmax = (int)(((long)hpn*(long)mhp)/(long)hpd); +#endif + if(mtmp->mhpmax < 0) mtmp->mhpmax = hpn; /* overflow */ + if (!mtmp->mhpmax) mtmp->mhpmax = 1; + + /* take on the new form... */ + set_mon_data(mtmp, mdat, 0); + + if (emits_light(olddata) != emits_light(mtmp->data)) { + /* used to give light, now doesn't, or vice versa, + or light's range has changed */ + if (emits_light(olddata)) + del_light_source(LS_MONSTER, (genericptr_t)mtmp); + if (emits_light(mtmp->data)) + new_light_source(mtmp->mx, mtmp->my, emits_light(mtmp->data), + LS_MONSTER, (genericptr_t)mtmp); + } + if (!mtmp->perminvis || pm_invisible(olddata)) + mtmp->perminvis = pm_invisible(mdat); + mtmp->minvis = mtmp->invis_blkd ? 0 : mtmp->perminvis; + if (!(hides_under(mdat) && OBJ_AT(mtmp->mx, mtmp->my)) && + !(mdat->mlet == S_EEL && is_pool(mtmp->mx, mtmp->my))) + mtmp->mundetected = 0; + if (u.ustuck == mtmp) { + if(u.uswallow) { + if(!attacktype(mdat,AT_ENGL)) { + /* Does mdat care? */ + if (!noncorporeal(mdat) && !amorphous(mdat) && + !is_whirly(mdat) && + (mdat != &mons[PM_YELLOW_LIGHT])) { + You("break out of %s%s!", mon_nam(mtmp), + (is_animal(mdat)? + "'s stomach" : "")); + mtmp->mhp = 1; /* almost dead */ + } + expels(mtmp, olddata, FALSE); + } else { + /* update swallow glyphs for new monster */ + swallowed(0); + } + } else if (!sticks(mdat) && !sticks(youmonst.data)) + unstuck(mtmp); + } + +#ifndef DCC30_BUG + if (mdat == &mons[PM_LONG_WORM] && (mtmp->wormno = get_wormno()) != 0) { +#else + /* DICE 3.0 doesn't like assigning and comparing mtmp->wormno in the + * same expression. + */ + if (mdat == &mons[PM_LONG_WORM] && + (mtmp->wormno = get_wormno(), mtmp->wormno != 0)) { +#endif + /* we can now create worms with tails - 11/91 */ + initworm(mtmp, rn2(5)); + if (count_wsegs(mtmp)) + place_worm_tail_randomly(mtmp, mtmp->mx, mtmp->my); + } + + newsym(mtmp->mx,mtmp->my); + + if (msg) { + uchar save_mnamelth = mtmp->mnamelth; + mtmp->mnamelth = 0; + pline("%s turns into %s!", oldname, + mdat == &mons[PM_GREEN_SLIME] ? "slime" : + x_monnam(mtmp, ARTICLE_A, (char*)0, SUPPRESS_SADDLE, FALSE)); + mtmp->mnamelth = save_mnamelth; + } + + possibly_unwield(mtmp, polyspot); /* might lose use of weapon */ + mon_break_armor(mtmp, polyspot); + if (!(mtmp->misc_worn_check & W_ARMG)) + mselftouch(mtmp, "No longer petrify-resistant, ", + !flags.mon_moving); + m_dowear(mtmp, FALSE); + + /* This ought to re-test can_carry() on each item in the inventory + * rather than just checking ex-giants & boulders, but that'd be + * pretty expensive to perform. If implemented, then perhaps + * minvent should be sorted in order to drop heaviest items first. + */ + /* former giants can't continue carrying boulders */ + if (mtmp->minvent && !throws_rocks(mdat)) { + register struct obj *otmp, *otmp2; + + for (otmp = mtmp->minvent; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + if (otmp->otyp == BOULDER) { + /* this keeps otmp from being polymorphed in the + same zap that the monster that held it is polymorphed */ + if (polyspot) bypass_obj(otmp); + obj_extract_self(otmp); + /* probably ought to give some "drop" message here */ + if (flooreffects(otmp, mtmp->mx, mtmp->my, "")) continue; + place_object(otmp, mtmp->mx, mtmp->my); + } + } + } + + return(1); +} + +/* sometimes an egg will be special */ +#define BREEDER_EGG (!rn2(77)) + +/* + * Determine if the given monster number can be hatched from an egg. + * Return the monster number to use as the egg's corpsenm. Return + * NON_PM if the given monster can't be hatched. + */ +int +can_be_hatched(mnum) +int mnum; +{ + /* ranger quest nemesis has the oviparous bit set, making it + be possible to wish for eggs of that unique monster; turn + such into ordinary eggs rather than forbidding them outright */ + if (mnum == PM_SCORPIUS) mnum = PM_SCORPION; + + mnum = little_to_big(mnum); + /* + * Queen bees lay killer bee eggs (usually), but killer bees don't + * grow into queen bees. Ditto for [winged-]gargoyles. + */ + if (mnum == PM_KILLER_BEE || mnum == PM_GARGOYLE || + (lays_eggs(&mons[mnum]) && (BREEDER_EGG || + (mnum != PM_QUEEN_BEE && mnum != PM_WINGED_GARGOYLE)))) + return mnum; + return NON_PM; +} + +/* type of egg laid by #sit; usually matches parent */ +int +egg_type_from_parent(mnum, force_ordinary) +int mnum; /* parent monster; caller must handle lays_eggs() check */ +boolean force_ordinary; +{ + if (force_ordinary || !BREEDER_EGG) { + if (mnum == PM_QUEEN_BEE) mnum = PM_KILLER_BEE; + else if (mnum == PM_WINGED_GARGOYLE) mnum = PM_GARGOYLE; + } + return mnum; +} + +/* decide whether an egg of the indicated monster type is viable; */ +/* also used to determine whether an egg or tin can be created... */ +boolean +dead_species(m_idx, egg) +int m_idx; +boolean egg; +{ + /* + * For monsters with both baby and adult forms, genociding either + * form kills all eggs of that monster. Monsters with more than + * two forms (small->large->giant mimics) are more or less ignored; + * fortunately, none of them have eggs. Species extinction due to + * overpopulation does not kill eggs. + */ + return (boolean) + (m_idx >= LOW_PM && + ((mvitals[m_idx].mvflags & G_GENOD) != 0 || + (egg && + (mvitals[big_to_little(m_idx)].mvflags & G_GENOD) != 0))); +} + +/* kill off any eggs of genocided monsters */ +STATIC_OVL void +kill_eggs(obj_list) +struct obj *obj_list; +{ + struct obj *otmp; + + for (otmp = obj_list; otmp; otmp = otmp->nobj) + if (otmp->otyp == EGG) { + if (dead_species(otmp->corpsenm, TRUE)) { + /* + * It seems we could also just catch this when + * it attempted to hatch, so we wouldn't have to + * search all of the objlists.. or stop all + * hatch timers based on a corpsenm. + */ + kill_egg(otmp); + } +#if 0 /* not used */ + } else if (otmp->otyp == TIN) { + if (dead_species(otmp->corpsenm, FALSE)) + otmp->corpsenm = NON_PM; /* empty tin */ + } else if (otmp->otyp == CORPSE) { + if (dead_species(otmp->corpsenm, FALSE)) + ; /* not yet implemented... */ +#endif + } else if (Has_contents(otmp)) { + kill_eggs(otmp->cobj); + } +} + +/* kill all members of genocided species */ +void +kill_genocided_monsters() +{ + struct monst *mtmp, *mtmp2; + boolean kill_cham[CHAM_MAX_INDX+1]; + int mndx; + + kill_cham[CHAM_ORDINARY] = FALSE; /* (this is mndx==0) */ + for (mndx = 1; mndx <= CHAM_MAX_INDX; mndx++) + kill_cham[mndx] = (mvitals[cham_to_pm[mndx]].mvflags & G_GENOD) != 0; + /* + * Called during genocide, and again upon level change. The latter + * catches up with any migrating monsters as they finally arrive at + * their intended destinations, so possessions get deposited there. + * + * Chameleon handling: + * 1) if chameleons have been genocided, destroy them + * regardless of current form; + * 2) otherwise, force every chameleon which is imitating + * any genocided species to take on a new form. + */ + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp)) continue; + mndx = monsndx(mtmp->data); + if ((mvitals[mndx].mvflags & G_GENOD) || kill_cham[mtmp->cham]) { + if (mtmp->cham && !kill_cham[mtmp->cham]) + (void) newcham(mtmp, (struct permonst *)0, FALSE, FALSE); + else + mondead(mtmp); + } + if (mtmp->minvent) kill_eggs(mtmp->minvent); + } + + kill_eggs(invent); + kill_eggs(fobj); + kill_eggs(level.buriedobjlist); +} + +#endif /* OVL2 */ +#ifdef OVLB + +void +golemeffects(mon, damtype, dam) +register struct monst *mon; +int damtype, dam; +{ + int heal = 0, slow = 0; + + if (mon->data == &mons[PM_FLESH_GOLEM]) { + if (damtype == AD_ELEC) heal = dam / 6; + else if (damtype == AD_FIRE || damtype == AD_COLD) slow = 1; + } else if (mon->data == &mons[PM_IRON_GOLEM]) { + if (damtype == AD_ELEC) slow = 1; + else if (damtype == AD_FIRE) heal = dam; + } else { + return; + } + if (slow) { + if (mon->mspeed != MSLOW) + mon_adjust_speed(mon, -1, (struct obj *)0); + } + if (heal) { + if (mon->mhp < mon->mhpmax) { + mon->mhp += dam; + if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (cansee(mon->mx, mon->my)) + pline("%s seems healthier.", Monnam(mon)); + } + } +} + +boolean +angry_guards(silent) +register boolean silent; +{ + register struct monst *mtmp; + register int ct = 0, nct = 0, sct = 0, slct = 0; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) + && mtmp->mpeaceful) { + ct++; + if(cansee(mtmp->mx, mtmp->my) && mtmp->mcanmove) { + if (distu(mtmp->mx, mtmp->my) == 2) nct++; + else sct++; + } + if (mtmp->msleeping || mtmp->mfrozen) { + slct++; + mtmp->msleeping = mtmp->mfrozen = 0; + } + mtmp->mpeaceful = 0; + } + } + if(ct) { + if(!silent) { /* do we want pline msgs? */ + if(slct) pline_The("guard%s wake%s up!", + slct > 1 ? "s" : "", slct == 1 ? "s" : ""); + if(nct || sct) { + if(nct) pline_The("guard%s get%s angry!", + nct == 1 ? "" : "s", nct == 1 ? "s" : ""); + else if(!Blind) + You("see %sangry guard%s approaching!", + sct == 1 ? "an " : "", sct > 1 ? "s" : ""); + } else if(flags.soundok) + You_hear("the shrill sound of a guard's whistle."); + } + return(TRUE); + } + return(FALSE); +} + +void +pacify_guards() +{ + register struct monst *mtmp; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if (mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) + mtmp->mpeaceful = 1; + } +} + +void +mimic_hit_msg(mtmp, otyp) +struct monst *mtmp; +short otyp; +{ + short ap = mtmp->mappearance; + + switch(mtmp->m_ap_type) { + case M_AP_NOTHING: + case M_AP_FURNITURE: + case M_AP_MONSTER: + break; + case M_AP_OBJECT: + if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) { + pline("%s seems a more vivid %s than before.", + The(simple_typename(ap)), + c_obj_colors[objects[ap].oc_color]); + } + break; + } +} +#endif /* OVLB */ + +/*mon.c*/ diff --git a/src/mondata.c b/src/mondata.c new file mode 100644 index 0000000..c66be4f --- /dev/null +++ b/src/mondata.c @@ -0,0 +1,757 @@ +/* SCCS Id: @(#)mondata.c 3.4 2003/06/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "eshk.h" +#include "epri.h" + +/* These routines provide basic data for any type of monster. */ + +#ifdef OVLB + +void +set_mon_data(mon, ptr, flag) +struct monst *mon; +struct permonst *ptr; +int flag; +{ + mon->data = ptr; + if (flag == -1) return; /* "don't care" */ + + if (flag == 1) + mon->mintrinsics |= (ptr->mresists & 0x00FF); + else + mon->mintrinsics = (ptr->mresists & 0x00FF); + return; +} + +#endif /* OVLB */ +#ifdef OVL0 + +struct attack * +attacktype_fordmg(ptr, atyp, dtyp) +struct permonst *ptr; +int atyp, dtyp; +{ + struct attack *a; + + for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++) + if (a->aatyp == atyp && (dtyp == AD_ANY || a->adtyp == dtyp)) + return a; + + return (struct attack *)0; +} + +boolean +attacktype(ptr, atyp) +struct permonst *ptr; +int atyp; +{ + return attacktype_fordmg(ptr, atyp, AD_ANY) ? TRUE : FALSE; +} + +#endif /* OVL0 */ +#ifdef OVLB + +boolean +poly_when_stoned(ptr) + struct permonst *ptr; +{ + return((boolean)(is_golem(ptr) && ptr != &mons[PM_STONE_GOLEM] && + !(mvitals[PM_STONE_GOLEM].mvflags & G_GENOD))); + /* allow G_EXTINCT */ +} + +boolean +resists_drli(mon) /* returns TRUE if monster is drain-life resistant */ +struct monst *mon; +{ + struct permonst *ptr = mon->data; + struct obj *wep = ((mon == &youmonst) ? uwep : MON_WEP(mon)); + + return (boolean)(is_undead(ptr) || is_demon(ptr) || is_were(ptr) || + ptr == &mons[PM_DEATH] || + (wep && wep->oartifact && defends(AD_DRLI, wep))); +} + +boolean +resists_magm(mon) /* TRUE if monster is magic-missile resistant */ +struct monst *mon; +{ + struct permonst *ptr = mon->data; + struct obj *o; + + /* as of 3.2.0: gray dragons, Angels, Oracle, Yeenoghu */ + if (dmgtype(ptr, AD_MAGM) || ptr == &mons[PM_BABY_GRAY_DRAGON] || + dmgtype(ptr, AD_RBRE)) /* Chromatic Dragon */ + return TRUE; + /* check for magic resistance granted by wielded weapon */ + o = (mon == &youmonst) ? uwep : MON_WEP(mon); + if (o && o->oartifact && defends(AD_MAGM, o)) + return TRUE; + /* check for magic resistance granted by worn or carried items */ + o = (mon == &youmonst) ? invent : mon->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask && objects[o->otyp].oc_oprop == ANTIMAGIC) || + (o->oartifact && protects(AD_MAGM, o))) + return TRUE; + return FALSE; +} + +/* TRUE iff monster is resistant to light-induced blindness */ +boolean +resists_blnd(mon) +struct monst *mon; +{ + struct permonst *ptr = mon->data; + boolean is_you = (mon == &youmonst); + struct obj *o; + + if (is_you ? (Blind || u.usleep) : + (mon->mblinded || !mon->mcansee || !haseyes(ptr) || + /* BUG: temporary sleep sets mfrozen, but since + paralysis does too, we can't check it */ + mon->msleeping)) + return TRUE; + /* yellow light, Archon; !dust vortex, !cobra, !raven */ + if (dmgtype_fromattack(ptr, AD_BLND, AT_EXPL) || + dmgtype_fromattack(ptr, AD_BLND, AT_GAZE)) + return TRUE; + o = is_you ? uwep : MON_WEP(mon); + if (o && o->oartifact && defends(AD_BLND, o)) + return TRUE; + o = is_you ? invent : mon->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask && objects[o->otyp].oc_oprop == BLINDED) || + (o->oartifact && protects(AD_BLND, o))) + return TRUE; + return FALSE; +} + +/* TRUE iff monster can be blinded by the given attack */ +/* Note: may return TRUE when mdef is blind (e.g. new cream-pie attack) */ +boolean +can_blnd(magr, mdef, aatyp, obj) +struct monst *magr; /* NULL == no specific aggressor */ +struct monst *mdef; +uchar aatyp; +struct obj *obj; /* aatyp == AT_WEAP, AT_SPIT */ +{ + boolean is_you = (mdef == &youmonst); + boolean check_visor = FALSE; + struct obj *o; + const char *s; + + /* no eyes protect against all attacks for now */ + if (!haseyes(mdef->data)) + return FALSE; + + switch(aatyp) { + case AT_EXPL: case AT_BOOM: case AT_GAZE: case AT_MAGC: + case AT_BREA: /* assumed to be lightning */ + /* light-based attacks may be cancelled or resisted */ + if (magr && magr->mcan) + return FALSE; + return !resists_blnd(mdef); + + case AT_WEAP: case AT_SPIT: case AT_NONE: + /* an object is used (thrown/spit/other) */ + if (obj && (obj->otyp == CREAM_PIE)) { + if (is_you && Blindfolded) + return FALSE; + } else if (obj && (obj->otyp == BLINDING_VENOM)) { + /* all ublindf, including LENSES, protect, cream-pies too */ + if (is_you && (ublindf || u.ucreamed)) + return FALSE; + check_visor = TRUE; + } else if (obj && (obj->otyp == POT_BLINDNESS)) { + return TRUE; /* no defense */ + } else + return FALSE; /* other objects cannot cause blindness yet */ + if ((magr == &youmonst) && u.uswallow) + return FALSE; /* can't affect eyes while inside monster */ + break; + + case AT_ENGL: + if (is_you && (Blindfolded || u.usleep || u.ucreamed)) + return FALSE; + if (!is_you && mdef->msleeping) + return FALSE; + break; + + case AT_CLAW: + /* e.g. raven: all ublindf, including LENSES, protect */ + if (is_you && ublindf) + return FALSE; + if ((magr == &youmonst) && u.uswallow) + return FALSE; /* can't affect eyes while inside monster */ + check_visor = TRUE; + break; + + case AT_TUCH: case AT_STNG: + /* some physical, blind-inducing attacks can be cancelled */ + if (magr && magr->mcan) + return FALSE; + break; + + default: + break; + } + + /* check if wearing a visor (only checked if visor might help) */ + if (check_visor) { + o = (mdef == &youmonst) ? invent : mdef->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask & W_ARMH) && + (s = OBJ_DESCR(objects[o->otyp])) != (char *)0 && + !strcmp(s, "visored helmet")) + return FALSE; + } + + return TRUE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +boolean +ranged_attk(ptr) /* returns TRUE if monster can attack at range */ +struct permonst *ptr; +{ + register int i, atyp; + long atk_mask = (1L << AT_BREA) | (1L << AT_SPIT) | (1L << AT_GAZE); + + /* was: (attacktype(ptr, AT_BREA) || attacktype(ptr, AT_WEAP) || + attacktype(ptr, AT_SPIT) || attacktype(ptr, AT_GAZE) || + attacktype(ptr, AT_MAGC)); + but that's too slow -dlc + */ + for (i = 0; i < NATTK; i++) { + atyp = ptr->mattk[i].aatyp; + if (atyp >= AT_WEAP) return TRUE; + /* assert(atyp < 32); */ + if ((atk_mask & (1L << atyp)) != 0L) return TRUE; + } + + return FALSE; +} + +boolean +hates_silver(ptr) +register struct permonst *ptr; +/* returns TRUE if monster is especially affected by silver weapons */ +{ + return((boolean)(is_were(ptr) || ptr->mlet==S_VAMPIRE || is_demon(ptr) || + ptr == &mons[PM_SHADE] || + (ptr->mlet==S_IMP && ptr != &mons[PM_TENGU]))); +} + +/* true iff the type of monster pass through iron bars */ +boolean +passes_bars(mptr) +struct permonst *mptr; +{ + return (boolean) (passes_walls(mptr) || amorphous(mptr) || + is_whirly(mptr) || verysmall(mptr) || + (slithy(mptr) && !bigmonst(mptr))); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +boolean +can_track(ptr) /* returns TRUE if monster can track well */ + register struct permonst *ptr; +{ + if (uwep && uwep->oartifact == ART_EXCALIBUR) + return TRUE; + else + return((boolean)haseyes(ptr)); +} + +#endif /* OVL1 */ +#ifdef OVLB + +boolean +sliparm(ptr) /* creature will slide out of armor */ + register struct permonst *ptr; +{ + return((boolean)(is_whirly(ptr) || ptr->msize <= MZ_SMALL || + noncorporeal(ptr))); +} + +boolean +breakarm(ptr) /* creature will break out of armor */ + register struct permonst *ptr; +{ + return ((bigmonst(ptr) || (ptr->msize > MZ_SMALL && !humanoid(ptr)) || + /* special cases of humanoids that cannot wear body armor */ + ptr == &mons[PM_MARILITH] || ptr == &mons[PM_WINGED_GARGOYLE]) + && !sliparm(ptr)); +} +#endif /* OVLB */ +#ifdef OVL1 + +boolean +sticks(ptr) /* creature sticks other creatures it hits */ + register struct permonst *ptr; +{ + return((boolean)(dmgtype(ptr,AD_STCK) || dmgtype(ptr,AD_WRAP) || + attacktype(ptr,AT_HUGS))); +} + +/* number of horns this type of monster has on its head */ +int +num_horns(ptr) +struct permonst *ptr; +{ + switch (monsndx(ptr)) { + case PM_HORNED_DEVIL: /* ? "more than one" */ + case PM_MINOTAUR: + case PM_ASMODEUS: + case PM_BALROG: + return 2; + case PM_WHITE_UNICORN: + case PM_GRAY_UNICORN: + case PM_BLACK_UNICORN: + case PM_KI_RIN: + return 1; + default: + break; + } + return 0; +} + +struct attack * +dmgtype_fromattack(ptr, dtyp, atyp) +struct permonst *ptr; +int dtyp, atyp; +{ + struct attack *a; + + for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++) + if (a->adtyp == dtyp && (atyp == AT_ANY || a->aatyp == atyp)) + return a; + + return (struct attack *)0; +} + +boolean +dmgtype(ptr, dtyp) +struct permonst *ptr; +int dtyp; +{ + return dmgtype_fromattack(ptr, dtyp, AT_ANY) ? TRUE : FALSE; +} + +/* returns the maximum damage a defender can do to the attacker via + * a passive defense */ +int +max_passive_dmg(mdef, magr) + register struct monst *mdef, *magr; +{ + int i, dmg = 0; + uchar adtyp; + + for(i = 0; i < NATTK; i++) + if(mdef->data->mattk[i].aatyp == AT_NONE || + mdef->data->mattk[i].aatyp == AT_BOOM) { + adtyp = mdef->data->mattk[i].adtyp; + if ((adtyp == AD_ACID && !resists_acid(magr)) || + (adtyp == AD_COLD && !resists_cold(magr)) || + (adtyp == AD_FIRE && !resists_fire(magr)) || + (adtyp == AD_ELEC && !resists_elec(magr)) || + adtyp == AD_PHYS) { + dmg = mdef->data->mattk[i].damn; + if(!dmg) dmg = mdef->data->mlevel+1; + dmg *= mdef->data->mattk[i].damd; + } else dmg = 0; + + return dmg; + } + return 0; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +int +monsndx(ptr) /* return an index into the mons array */ + struct permonst *ptr; +{ + register int i; + + i = (int)(ptr - &mons[0]); + if (i < LOW_PM || i >= NUMMONS) { + /* ought to switch this to use `fmt_ptr' */ + panic("monsndx - could not index monster (%lx)", + (unsigned long)ptr); + return NON_PM; /* will not get here */ + } + + return(i); +} + +#endif /* OVL0 */ +#ifdef OVL1 + + +int +name_to_mon(in_str) +const char *in_str; +{ + /* Be careful. We must check the entire string in case it was + * something such as "ettin zombie corpse". The calling routine + * doesn't know about the "corpse" until the monster name has + * already been taken off the front, so we have to be able to + * read the name with extraneous stuff such as "corpse" stuck on + * the end. + * This causes a problem for names which prefix other names such + * as "ettin" on "ettin zombie". In this case we want the _longest_ + * name which exists. + * This also permits plurals created by adding suffixes such as 's' + * or 'es'. Other plurals must still be handled explicitly. + */ + register int i; + register int mntmp = NON_PM; + register char *s, *str, *term; + char buf[BUFSZ]; + int len, slen; + + str = strcpy(buf, in_str); + + if (!strncmp(str, "a ", 2)) str += 2; + else if (!strncmp(str, "an ", 3)) str += 3; + + slen = strlen(str); + term = str + slen; + + if ((s = strstri(str, "vortices")) != 0) + Strcpy(s+4, "ex"); + /* be careful with "ies"; "priest", "zombies" */ + else if (slen > 3 && !strcmpi(term-3, "ies") && + (slen < 7 || strcmpi(term-7, "zombies"))) + Strcpy(term-3, "y"); + /* luckily no monster names end in fe or ve with ves plurals */ + else if (slen > 3 && !strcmpi(term-3, "ves")) + Strcpy(term-3, "f"); + + slen = strlen(str); /* length possibly needs recomputing */ + + { + static const struct alt_spl { const char* name; short pm_val; } + names[] = { + /* Alternate spellings */ + { "grey dragon", PM_GRAY_DRAGON }, + { "baby grey dragon", PM_BABY_GRAY_DRAGON }, + { "grey unicorn", PM_GRAY_UNICORN }, + { "grey ooze", PM_GRAY_OOZE }, + { "gray-elf", PM_GREY_ELF }, + /* Hyphenated names */ + { "ki rin", PM_KI_RIN }, + { "uruk hai", PM_URUK_HAI }, + { "orc captain", PM_ORC_CAPTAIN }, + { "woodland elf", PM_WOODLAND_ELF }, + { "green elf", PM_GREEN_ELF }, + { "grey elf", PM_GREY_ELF }, + { "gray elf", PM_GREY_ELF }, + { "elf lord", PM_ELF_LORD }, +#if 0 /* OBSOLETE */ + { "high elf", PM_HIGH_ELF }, +#endif + { "olog hai", PM_OLOG_HAI }, + { "arch lich", PM_ARCH_LICH }, + /* Some irregular plurals */ + { "incubi", PM_INCUBUS }, + { "succubi", PM_SUCCUBUS }, + { "violet fungi", PM_VIOLET_FUNGUS }, + { "homunculi", PM_HOMUNCULUS }, + { "baluchitheria", PM_BALUCHITHERIUM }, + { "lurkers above", PM_LURKER_ABOVE }, + { "cavemen", PM_CAVEMAN }, + { "cavewomen", PM_CAVEWOMAN }, + { "djinn", PM_DJINNI }, + { "mumakil", PM_MUMAK }, + { "erinyes", PM_ERINYS }, + /* falsely caught by -ves check above */ + { "master of thief", PM_MASTER_OF_THIEVES }, + /* end of list */ + { 0, 0 } + }; + register const struct alt_spl *namep; + + for (namep = names; namep->name; namep++) + if (!strncmpi(str, namep->name, (int)strlen(namep->name))) + return namep->pm_val; + } + + for (len = 0, i = LOW_PM; i < NUMMONS; i++) { + register int m_i_len = strlen(mons[i].mname); + if (m_i_len > len && !strncmpi(mons[i].mname, str, m_i_len)) { + if (m_i_len == slen) return i; /* exact match */ + else if (slen > m_i_len && + (str[m_i_len] == ' ' || + !strcmpi(&str[m_i_len], "s") || + !strncmpi(&str[m_i_len], "s ", 2) || + !strcmpi(&str[m_i_len], "'") || + !strncmpi(&str[m_i_len], "' ", 2) || + !strcmpi(&str[m_i_len], "'s") || + !strncmpi(&str[m_i_len], "'s ", 3) || + !strcmpi(&str[m_i_len], "es") || + !strncmpi(&str[m_i_len], "es ", 3))) { + mntmp = i; + len = m_i_len; + } + } + } + if (mntmp == NON_PM) mntmp = title_to_mon(str, (int *)0, (int *)0); + return mntmp; +} + +#endif /* OVL1 */ +#ifdef OVL2 + +/* returns 3 values (0=male, 1=female, 2=none) */ +int +gender(mtmp) +register struct monst *mtmp; +{ + if (is_neuter(mtmp->data)) return 2; + return mtmp->female; +} + +/* Like gender(), but lower animals and such are still "it". */ +/* This is the one we want to use when printing messages. */ +int +pronoun_gender(mtmp) +register struct monst *mtmp; +{ + if (is_neuter(mtmp->data) || !canspotmon(mtmp)) return 2; + return (humanoid(mtmp->data) || (mtmp->data->geno & G_UNIQ) || + type_is_pname(mtmp->data)) ? (int)mtmp->female : 2; +} + +#endif /* OVL2 */ +#ifdef OVLB + +/* used for nearby monsters when you go to another level */ +boolean +levl_follower(mtmp) +struct monst *mtmp; +{ + /* monsters with the Amulet--even pets--won't follow across levels */ + if (mon_has_amulet(mtmp)) return FALSE; + + /* some monsters will follow even while intending to flee from you */ + if (mtmp->mtame || mtmp->iswiz || is_fshk(mtmp)) return TRUE; + + /* stalking types follow, but won't when fleeing unless you hold + the Amulet */ + return (boolean)((mtmp->data->mflags2 & M2_STALK) && + (!mtmp->mflee || u.uhave.amulet)); +} + +static const short grownups[][2] = { + {PM_CHICKATRICE, PM_COCKATRICE}, + {PM_LITTLE_DOG, PM_DOG}, {PM_DOG, PM_LARGE_DOG}, + {PM_HELL_HOUND_PUP, PM_HELL_HOUND}, + {PM_WINTER_WOLF_CUB, PM_WINTER_WOLF}, + {PM_KITTEN, PM_HOUSECAT}, {PM_HOUSECAT, PM_LARGE_CAT}, + {PM_PONY, PM_HORSE}, {PM_HORSE, PM_WARHORSE}, + {PM_KOBOLD, PM_LARGE_KOBOLD}, {PM_LARGE_KOBOLD, PM_KOBOLD_LORD}, + {PM_GNOME, PM_GNOME_LORD}, {PM_GNOME_LORD, PM_GNOME_KING}, + {PM_DWARF, PM_DWARF_LORD}, {PM_DWARF_LORD, PM_DWARF_KING}, + {PM_MIND_FLAYER, PM_MASTER_MIND_FLAYER}, + {PM_ORC, PM_ORC_CAPTAIN}, {PM_HILL_ORC, PM_ORC_CAPTAIN}, + {PM_MORDOR_ORC, PM_ORC_CAPTAIN}, {PM_URUK_HAI, PM_ORC_CAPTAIN}, + {PM_SEWER_RAT, PM_GIANT_RAT}, + {PM_CAVE_SPIDER, PM_GIANT_SPIDER}, + {PM_OGRE, PM_OGRE_LORD}, {PM_OGRE_LORD, PM_OGRE_KING}, + {PM_ELF, PM_ELF_LORD}, {PM_WOODLAND_ELF, PM_ELF_LORD}, + {PM_GREEN_ELF, PM_ELF_LORD}, {PM_GREY_ELF, PM_ELF_LORD}, + {PM_ELF_LORD, PM_ELVENKING}, + {PM_LICH, PM_DEMILICH}, {PM_DEMILICH, PM_MASTER_LICH}, + {PM_MASTER_LICH, PM_ARCH_LICH}, + {PM_VAMPIRE, PM_VAMPIRE_LORD}, {PM_BAT, PM_GIANT_BAT}, + {PM_BABY_GRAY_DRAGON, PM_GRAY_DRAGON}, + {PM_BABY_SILVER_DRAGON, PM_SILVER_DRAGON}, +#if 0 /* DEFERRED */ + {PM_BABY_SHIMMERING_DRAGON, PM_SHIMMERING_DRAGON}, +#endif + {PM_BABY_RED_DRAGON, PM_RED_DRAGON}, + {PM_BABY_WHITE_DRAGON, PM_WHITE_DRAGON}, + {PM_BABY_ORANGE_DRAGON, PM_ORANGE_DRAGON}, + {PM_BABY_BLACK_DRAGON, PM_BLACK_DRAGON}, + {PM_BABY_BLUE_DRAGON, PM_BLUE_DRAGON}, + {PM_BABY_GREEN_DRAGON, PM_GREEN_DRAGON}, + {PM_BABY_YELLOW_DRAGON, PM_YELLOW_DRAGON}, + {PM_RED_NAGA_HATCHLING, PM_RED_NAGA}, + {PM_BLACK_NAGA_HATCHLING, PM_BLACK_NAGA}, + {PM_GOLDEN_NAGA_HATCHLING, PM_GOLDEN_NAGA}, + {PM_GUARDIAN_NAGA_HATCHLING, PM_GUARDIAN_NAGA}, + {PM_SMALL_MIMIC, PM_LARGE_MIMIC}, {PM_LARGE_MIMIC, PM_GIANT_MIMIC}, + {PM_BABY_LONG_WORM, PM_LONG_WORM}, + {PM_BABY_PURPLE_WORM, PM_PURPLE_WORM}, + {PM_BABY_CROCODILE, PM_CROCODILE}, + {PM_SOLDIER, PM_SERGEANT}, + {PM_SERGEANT, PM_LIEUTENANT}, + {PM_LIEUTENANT, PM_CAPTAIN}, + {PM_WATCHMAN, PM_WATCH_CAPTAIN}, + {PM_ALIGNED_PRIEST, PM_HIGH_PRIEST}, + {PM_STUDENT, PM_ARCHEOLOGIST}, + {PM_ATTENDANT, PM_HEALER}, + {PM_PAGE, PM_KNIGHT}, + {PM_ACOLYTE, PM_PRIEST}, + {PM_APPRENTICE, PM_WIZARD}, + {PM_MANES,PM_LEMURE}, +#ifdef KOPS + {PM_KEYSTONE_KOP, PM_KOP_SERGEANT}, + {PM_KOP_SERGEANT, PM_KOP_LIEUTENANT}, + {PM_KOP_LIEUTENANT, PM_KOP_KAPTAIN}, +#endif + {NON_PM,NON_PM} +}; + +int +little_to_big(montype) +int montype; +{ +#ifndef AIXPS2_BUG + register int i; + + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][0]) return grownups[i][1]; + return montype; +#else +/* AIX PS/2 C-compiler 1.1.1 optimizer does not like the above for loop, + * and causes segmentation faults at runtime. (The problem does not + * occur if -O is not used.) + * lehtonen@cs.Helsinki.FI (Tapio Lehtonen) 28031990 + */ + int i; + int monvalue; + + monvalue = montype; + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][0]) monvalue = grownups[i][1]; + + return monvalue; +#endif +} + +int +big_to_little(montype) +int montype; +{ + register int i; + + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][1]) return grownups[i][0]; + return montype; +} + +/* + * Return the permonst ptr for the race of the monster. + * Returns correct pointer for non-polymorphed and polymorphed + * player. It does not return a pointer to player role character. + */ +const struct permonst * +raceptr(mtmp) +struct monst *mtmp; +{ + if (mtmp == &youmonst && !Upolyd) return(&mons[urace.malenum]); + else return(mtmp->data); +} + +static const char *levitate[4] = { "float", "Float", "wobble", "Wobble" }; +static const char *flys[4] = { "fly", "Fly", "flutter", "Flutter" }; +static const char *flyl[4] = { "fly", "Fly", "stagger", "Stagger" }; +static const char *slither[4] = { "slither", "Slither", "falter", "Falter" }; +static const char *ooze[4] = { "ooze", "Ooze", "tremble", "Tremble" }; +static const char *immobile[4] = { "wiggle", "Wiggle", "pulsate", "Pulsate" }; +static const char *crawl[4] = { "crawl", "Crawl", "falter", "Falter" }; + +const char * +locomotion(ptr, def) +const struct permonst *ptr; +const char *def; +{ + int capitalize = (*def == highc(*def)); + + return ( + is_floater(ptr) ? levitate[capitalize] : + (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] : + (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[capitalize] : + slithy(ptr) ? slither[capitalize] : + amorphous(ptr) ? ooze[capitalize] : + !ptr->mmove ? immobile[capitalize] : + nolimbs(ptr) ? crawl[capitalize] : + def + ); + +} + +const char * +stagger(ptr, def) +const struct permonst *ptr; +const char *def; +{ + int capitalize = 2 + (*def == highc(*def)); + + return ( + is_floater(ptr) ? levitate[capitalize] : + (is_flyer(ptr) && ptr->msize <= MZ_SMALL) ? flys[capitalize] : + (is_flyer(ptr) && ptr->msize > MZ_SMALL) ? flyl[capitalize] : + slithy(ptr) ? slither[capitalize] : + amorphous(ptr) ? ooze[capitalize] : + !ptr->mmove ? immobile[capitalize] : + nolimbs(ptr) ? crawl[capitalize] : + def + ); + +} + +/* return a phrase describing the effect of fire attack on a type of monster */ +const char * +on_fire(mptr, mattk) +struct permonst *mptr; +struct attack *mattk; +{ + const char *what; + + switch (monsndx(mptr)) { + case PM_FLAMING_SPHERE: + case PM_FIRE_VORTEX: + case PM_FIRE_ELEMENTAL: + case PM_SALAMANDER: + what = "already on fire"; + break; + case PM_WATER_ELEMENTAL: + case PM_FOG_CLOUD: + case PM_STEAM_VORTEX: + what = "boiling"; + break; + case PM_ICE_VORTEX: + case PM_GLASS_GOLEM: + what = "melting"; + break; + case PM_STONE_GOLEM: + case PM_CLAY_GOLEM: + case PM_GOLD_GOLEM: + case PM_AIR_ELEMENTAL: + case PM_EARTH_ELEMENTAL: + case PM_DUST_VORTEX: + case PM_ENERGY_VORTEX: + what = "heating up"; + break; + default: + what = (mattk->aatyp == AT_HUGS) ? "being roasted" : "on fire"; + break; + } + return what; +} + +#endif /* OVLB */ + +/*mondata.c*/ diff --git a/src/monmove.c b/src/monmove.c new file mode 100644 index 0000000..309fd32 --- /dev/null +++ b/src/monmove.c @@ -0,0 +1,1369 @@ +/* SCCS Id: @(#)monmove.c 3.4 2002/04/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "mfndpos.h" +#include "artifact.h" +#include "epri.h" + +extern boolean notonhead; + +#ifdef OVL0 + +STATIC_DCL int FDECL(disturb,(struct monst *)); +STATIC_DCL void FDECL(distfleeck,(struct monst *,int *,int *,int *)); +STATIC_DCL int FDECL(m_arrival, (struct monst *)); +STATIC_DCL void FDECL(watch_on_duty,(struct monst *)); + +#endif /* OVL0 */ +#ifdef OVLB + +boolean /* TRUE : mtmp died */ +mb_trapped(mtmp) +register struct monst *mtmp; +{ + if (flags.verbose) { + if (cansee(mtmp->mx, mtmp->my)) + pline("KABOOM!! You see a door explode."); + else if (flags.soundok) + You_hear("a distant explosion."); + } + wake_nearto(mtmp->mx, mtmp->my, 7*7); + mtmp->mstun = 1; + mtmp->mhp -= rnd(15); + if(mtmp->mhp <= 0) { + mondied(mtmp); + if (mtmp->mhp > 0) /* lifesaved */ + return(FALSE); + else + return(TRUE); + } + return(FALSE); +} + +#endif /* OVLB */ +#ifdef OVL0 + +STATIC_OVL void +watch_on_duty(mtmp) +register struct monst *mtmp; +{ + int x, y; + + if(mtmp->mpeaceful && in_town(u.ux+u.dx, u.uy+u.dy) && + mtmp->mcansee && m_canseeu(mtmp) && !rn2(3)) { + + if(picking_lock(&x, &y) && IS_DOOR(levl[x][y].typ) && + (levl[x][y].doormask & D_LOCKED)) { + + if(couldsee(mtmp->mx, mtmp->my)) { + + pline("%s yells:", Amonnam(mtmp)); + if(levl[x][y].looted & D_WARNED) { + verbalize("Halt, thief! You're under arrest!"); + (void) angry_guards(!(flags.soundok)); + } else { + verbalize("Hey, stop picking that lock!"); + levl[x][y].looted |= D_WARNED; + } + stop_occupation(); + } + } else if (is_digging()) { + /* chewing, wand/spell of digging are checked elsewhere */ + watch_dig(mtmp, digging.pos.x, digging.pos.y, FALSE); + } + } +} + +#endif /* OVL0 */ +#ifdef OVL1 + +int +dochugw(mtmp) + register struct monst *mtmp; +{ + register int x = mtmp->mx, y = mtmp->my; + boolean already_saw_mon = !occupation ? 0 : canspotmon(mtmp); + int rd = dochug(mtmp); +#if 0 + /* part of the original warning code which was replaced in 3.3.1 */ + int dd; + + if(Warning && !rd && !mtmp->mpeaceful && + (dd = distu(mtmp->mx,mtmp->my)) < distu(x,y) && + dd < 100 && !canseemon(mtmp)) { + /* Note: this assumes we only want to warn against the monster to + * which the weapon does extra damage, as there is no "monster + * which the weapon warns against" field. + */ + if (spec_ability(uwep, SPFX_WARN) && spec_dbon(uwep, mtmp, 1)) + warnlevel = 100; + else if ((int) (mtmp->m_lev / 4) > warnlevel) + warnlevel = (mtmp->m_lev / 4); + } +#endif /* 0 */ + + /* a similar check is in monster_nearby() in hack.c */ + /* check whether hero notices monster and stops current activity */ + if (occupation && !rd && !Confusion && + (!mtmp->mpeaceful || Hallucination) && + /* it's close enough to be a threat */ + distu(mtmp->mx,mtmp->my) <= (BOLT_LIM+1)*(BOLT_LIM+1) && + /* and either couldn't see it before, or it was too far away */ + (!already_saw_mon || !couldsee(x,y) || + distu(x,y) > (BOLT_LIM+1)*(BOLT_LIM+1)) && + /* can see it now, or sense it and would normally see it */ + (canseemon(mtmp) || + (sensemon(mtmp) && couldsee(mtmp->mx,mtmp->my))) && + mtmp->mcanmove && + !noattacks(mtmp->data) && !onscary(u.ux, u.uy, mtmp)) + stop_occupation(); + + return(rd); +} + +#endif /* OVL1 */ +#ifdef OVL2 + +boolean +onscary(x, y, mtmp) +int x, y; +struct monst *mtmp; +{ + if (mtmp->isshk || mtmp->isgd || mtmp->iswiz || !mtmp->mcansee || + mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN || + is_lminion(mtmp) || mtmp->data == &mons[PM_ANGEL] || + is_rider(mtmp->data) || mtmp->data == &mons[PM_MINOTAUR]) + return(FALSE); + + return (boolean)(sobj_at(SCR_SCARE_MONSTER, x, y) +#ifdef ELBERETH + || sengr_at("Elbereth", x, y) +#endif + || (mtmp->data->mlet == S_VAMPIRE + && IS_ALTAR(levl[x][y].typ))); +} + +#endif /* OVL2 */ +#ifdef OVL0 + +/* regenerate lost hit points */ +void +mon_regen(mon, digest_meal) +struct monst *mon; +boolean digest_meal; +{ + if (mon->mhp < mon->mhpmax && + (moves % 20 == 0 || regenerates(mon->data))) mon->mhp++; + if (mon->mspec_used) mon->mspec_used--; + if (digest_meal) { + if (mon->meating) mon->meating--; + } +} + +/* + * Possibly awaken the given monster. Return a 1 if the monster has been + * jolted awake. + */ +STATIC_OVL int +disturb(mtmp) + register struct monst *mtmp; +{ + /* + * + Ettins are hard to surprise. + * + Nymphs, jabberwocks, and leprechauns do not easily wake up. + * + * Wake up if: + * in direct LOS AND + * within 10 squares AND + * not stealthy or (mon is an ettin and 9/10) AND + * (mon is not a nymph, jabberwock, or leprechaun) or 1/50 AND + * Aggravate or mon is (dog or human) or + * (1/7 and mon is not mimicing furniture or object) + */ + if(couldsee(mtmp->mx,mtmp->my) && + distu(mtmp->mx,mtmp->my) <= 100 && + (!Stealth || (mtmp->data == &mons[PM_ETTIN] && rn2(10))) && + (!(mtmp->data->mlet == S_NYMPH + || mtmp->data == &mons[PM_JABBERWOCK] +#if 0 /* DEFERRED */ + || mtmp->data == &mons[PM_VORPAL_JABBERWOCK] +#endif + || mtmp->data->mlet == S_LEPRECHAUN) || !rn2(50)) && + (Aggravate_monster + || (mtmp->data->mlet == S_DOG || + mtmp->data->mlet == S_HUMAN) + || (!rn2(7) && mtmp->m_ap_type != M_AP_FURNITURE && + mtmp->m_ap_type != M_AP_OBJECT) )) { + mtmp->msleeping = 0; + return(1); + } + return(0); +} + +/* monster begins fleeing for the specified time, 0 means untimed flee + * if first, only adds fleetime if monster isn't already fleeing + * if fleemsg, prints a message about new flight, otherwise, caller should */ +void +monflee(mtmp, fleetime, first, fleemsg) +struct monst *mtmp; +int fleetime; +boolean first; +boolean fleemsg; +{ + if (u.ustuck == mtmp) { + if (u.uswallow) + expels(mtmp, mtmp->data, TRUE); + else if (!sticks(youmonst.data)) { + unstuck(mtmp); /* monster lets go when fleeing */ + You("get released!"); + } + } + + if (!first || !mtmp->mflee) { + /* don't lose untimed scare */ + if (!fleetime) + mtmp->mfleetim = 0; + else if (!mtmp->mflee || mtmp->mfleetim) { + fleetime += mtmp->mfleetim; + /* ensure monster flees long enough to visibly stop fighting */ + if (fleetime == 1) fleetime++; + mtmp->mfleetim = min(fleetime, 127); + } + if (!mtmp->mflee && fleemsg && canseemon(mtmp) && !mtmp->mfrozen) + pline("%s turns to flee!", (Monnam(mtmp))); + mtmp->mflee = 1; + } +} + +STATIC_OVL void +distfleeck(mtmp,inrange,nearby,scared) +register struct monst *mtmp; +int *inrange, *nearby, *scared; +{ + int seescaryx, seescaryy; + + *inrange = (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= + (BOLT_LIM * BOLT_LIM)); + *nearby = *inrange && monnear(mtmp, mtmp->mux, mtmp->muy); + + /* Note: if your image is displaced, the monster sees the Elbereth + * at your displaced position, thus never attacking your displaced + * position, but possibly attacking you by accident. If you are + * invisible, it sees the Elbereth at your real position, thus never + * running into you by accident but possibly attacking the spot + * where it guesses you are. + */ + if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) { + seescaryx = mtmp->mux; + seescaryy = mtmp->muy; + } else { + seescaryx = u.ux; + seescaryy = u.uy; + } + *scared = (*nearby && (onscary(seescaryx, seescaryy, mtmp) || + (!mtmp->mpeaceful && + in_your_sanctuary(mtmp, 0, 0)))); + + if(*scared) { + if (rn2(7)) + monflee(mtmp, rnd(10), TRUE, TRUE); + else + monflee(mtmp, rnd(100), TRUE, TRUE); + } + +} + +/* perform a special one-time action for a monster; returns -1 if nothing + special happened, 0 if monster uses up its turn, 1 if monster is killed */ +STATIC_OVL int +m_arrival(mon) +struct monst *mon; +{ + mon->mstrategy &= ~STRAT_ARRIVE; /* always reset */ + + return -1; +} + +/* returns 1 if monster died moving, 0 otherwise */ +/* The whole dochugw/m_move/distfleeck/mfndpos section is serious spaghetti + * code. --KAA + */ +int +dochug(mtmp) +register struct monst *mtmp; +{ + register struct permonst *mdat; + register int tmp=0; + int inrange, nearby, scared; +#ifdef GOLDOBJ + struct obj *ygold = 0, *lepgold = 0; +#endif + +/* Pre-movement adjustments */ + + mdat = mtmp->data; + + if (mtmp->mstrategy & STRAT_ARRIVE) { + int res = m_arrival(mtmp); + if (res >= 0) return res; + } + + /* check for waitmask status change */ + if ((mtmp->mstrategy & STRAT_WAITFORU) && + (m_canseeu(mtmp) || mtmp->mhp < mtmp->mhpmax)) + mtmp->mstrategy &= ~STRAT_WAITFORU; + + /* update quest status flags */ + quest_stat_check(mtmp); + + if (!mtmp->mcanmove || (mtmp->mstrategy & STRAT_WAITMASK)) { + if (Hallucination) newsym(mtmp->mx,mtmp->my); + if (mtmp->mcanmove && (mtmp->mstrategy & STRAT_CLOSE) && + !mtmp->msleeping && monnear(mtmp, u.ux, u.uy)) + quest_talk(mtmp); /* give the leaders a chance to speak */ + return(0); /* other frozen monsters can't do anything */ + } + + /* there is a chance we will wake it */ + if (mtmp->msleeping && !disturb(mtmp)) { + if (Hallucination) newsym(mtmp->mx,mtmp->my); + return(0); + } + + /* not frozen or sleeping: wipe out texts written in the dust */ + wipe_engr_at(mtmp->mx, mtmp->my, 1); + + /* confused monsters get unconfused with small probability */ + if (mtmp->mconf && !rn2(50)) mtmp->mconf = 0; + + /* stunned monsters get un-stunned with larger probability */ + if (mtmp->mstun && !rn2(10)) mtmp->mstun = 0; + + /* some monsters teleport */ + if (mtmp->mflee && !rn2(40) && can_teleport(mdat) && !mtmp->iswiz && + !level.flags.noteleport) { + (void) rloc(mtmp, FALSE); + return(0); + } + if (mdat->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1)) + m_respond(mtmp); + if (mdat == &mons[PM_MEDUSA] && couldsee(mtmp->mx, mtmp->my)) + m_respond(mtmp); + if (mtmp->mhp <= 0) return(1); /* m_respond gaze can kill medusa */ + + /* fleeing monsters might regain courage */ + if (mtmp->mflee && !mtmp->mfleetim + && mtmp->mhp == mtmp->mhpmax && !rn2(25)) mtmp->mflee = 0; + + set_apparxy(mtmp); + /* Must be done after you move and before the monster does. The + * set_apparxy() call in m_move() doesn't suffice since the variables + * inrange, etc. all depend on stuff set by set_apparxy(). + */ + + /* Monsters that want to acquire things */ + /* may teleport, so do it before inrange is set */ + if(is_covetous(mdat)) (void) tactics(mtmp); + + /* check distance and scariness of attacks */ + distfleeck(mtmp,&inrange,&nearby,&scared); + + if(find_defensive(mtmp)) { + if (use_defensive(mtmp) != 0) + return 1; + } else if(find_misc(mtmp)) { + if (use_misc(mtmp) != 0) + return 1; + } + + /* Demonic Blackmail! */ + if(nearby && mdat->msound == MS_BRIBE && + mtmp->mpeaceful && !mtmp->mtame && !u.uswallow) { + if (mtmp->mux != u.ux || mtmp->muy != u.uy) { + pline("%s whispers at thin air.", + cansee(mtmp->mux, mtmp->muy) ? Monnam(mtmp) : "It"); + + if (is_demon(youmonst.data)) { + /* "Good hunting, brother" */ + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + } else { + mtmp->minvis = mtmp->perminvis = 0; + /* Why? For the same reason in real demon talk */ + pline("%s gets angry!", Amonnam(mtmp)); + mtmp->mpeaceful = 0; + /* since no way is an image going to pay it off */ + } + } else if(demon_talk(mtmp)) return(1); /* you paid it off */ + } + + /* the watch will look around and see if you are up to no good :-) */ + if (mdat == &mons[PM_WATCHMAN] || mdat == &mons[PM_WATCH_CAPTAIN]) + watch_on_duty(mtmp); + + else if (is_mind_flayer(mdat) && !rn2(20)) { + struct monst *m2, *nmon = (struct monst *)0; + + if (canseemon(mtmp)) + pline("%s concentrates.", Monnam(mtmp)); + if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM) { + You("sense a faint wave of psychic energy."); + goto toofar; + } + pline("A wave of psychic energy pours over you!"); + if (mtmp->mpeaceful && + (!Conflict || resist(mtmp, RING_CLASS, 0, 0))) + pline("It feels quite soothing."); + else { + register boolean m_sen = sensemon(mtmp); + + if (m_sen || (Blind_telepat && rn2(2)) || !rn2(10)) { + int dmg; + pline("It locks on to your %s!", + m_sen ? "telepathy" : + Blind_telepat ? "latent telepathy" : "mind"); + dmg = rnd(15); + if (Half_spell_damage) dmg = (dmg+1) / 2; + losehp(dmg, "psychic blast", KILLED_BY_AN); + } + } + for(m2=fmon; m2; m2 = nmon) { + nmon = m2->nmon; + if (DEADMONSTER(m2)) continue; + if (m2->mpeaceful == mtmp->mpeaceful) continue; + if (mindless(m2->data)) continue; + if (m2 == mtmp) continue; + if ((telepathic(m2->data) && + (rn2(2) || m2->mblinded)) || !rn2(10)) { + if (cansee(m2->mx, m2->my)) + pline("It locks on to %s.", mon_nam(m2)); + m2->mhp -= rnd(15); + if (m2->mhp <= 0) + monkilled(m2, "", AD_DRIN); + else + m2->msleeping = 0; + } + } + } +toofar: + + /* If monster is nearby you, and has to wield a weapon, do so. This + * costs the monster a move, of course. + */ + if((!mtmp->mpeaceful || Conflict) && inrange && + dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 8 + && attacktype(mdat, AT_WEAP)) { + struct obj *mw_tmp; + + /* The scared check is necessary. Otherwise a monster that is + * one square near the player but fleeing into a wall would keep + * switching between pick-axe and weapon. If monster is stuck + * in a trap, prefer ranged weapon (wielding is done in thrwmu). + * This may cost the monster an attack, but keeps the monster + * from switching back and forth if carrying both. + */ + mw_tmp = MON_WEP(mtmp); + if (!(scared && mw_tmp && is_pick(mw_tmp)) && + mtmp->weapon_check == NEED_WEAPON && + !(mtmp->mtrapped && !nearby && select_rwep(mtmp))) { + mtmp->weapon_check = NEED_HTH_WEAPON; + if (mon_wield_item(mtmp) != 0) return(0); + } + } + +/* Now the actual movement phase */ + +#ifndef GOLDOBJ + if(!nearby || mtmp->mflee || scared || + mtmp->mconf || mtmp->mstun || (mtmp->minvis && !rn2(3)) || + (mdat->mlet == S_LEPRECHAUN && !u.ugold && (mtmp->mgold || rn2(2))) || +#else + if (mdat->mlet == S_LEPRECHAUN) { + ygold = findgold(invent); + lepgold = findgold(mtmp->minvent); + } + + if(!nearby || mtmp->mflee || scared || + mtmp->mconf || mtmp->mstun || (mtmp->minvis && !rn2(3)) || + (mdat->mlet == S_LEPRECHAUN && !ygold && (lepgold || rn2(2))) || +#endif + (is_wanderer(mdat) && !rn2(4)) || (Conflict && !mtmp->iswiz) || + (!mtmp->mcansee && !rn2(4)) || mtmp->mpeaceful) { + /* Possibly cast an undirected spell if not attacking you */ + /* note that most of the time castmu() will pick a directed + spell and do nothing, so the monster moves normally */ + /* arbitrary distance restriction to keep monster far away + from you from having cast dozens of sticks-to-snakes + or similar spells by the time you reach it */ + if (dist2(mtmp->mx, mtmp->my, u.ux, u.uy) <= 49 && !mtmp->mspec_used) { + struct attack *a; + + for (a = &mdat->mattk[0]; a < &mdat->mattk[NATTK]; a++) { + if (a->aatyp == AT_MAGC && (a->adtyp == AD_SPEL || a->adtyp == AD_CLRC)) { + if (castmu(mtmp, a, FALSE, FALSE)) { + tmp = 3; + break; + } + } + } + } + + tmp = m_move(mtmp, 0); + distfleeck(mtmp,&inrange,&nearby,&scared); /* recalc */ + + switch (tmp) { + case 0: /* no movement, but it can still attack you */ + case 3: /* absolutely no movement */ + /* for pets, case 0 and 3 are equivalent */ + /* vault guard might have vanished */ + if (mtmp->isgd && (mtmp->mhp < 1 || + (mtmp->mx == 0 && mtmp->my == 0))) + return 1; /* behave as if it died */ + /* During hallucination, monster appearance should + * still change - even if it doesn't move. + */ + if(Hallucination) newsym(mtmp->mx,mtmp->my); + break; + case 1: /* monster moved */ + /* Maybe it stepped on a trap and fell asleep... */ + if (mtmp->msleeping || !mtmp->mcanmove) return(0); + if(!nearby && + (ranged_attk(mdat) || find_offensive(mtmp))) + break; + else if(u.uswallow && mtmp == u.ustuck) { + /* a monster that's digesting you can move at the + * same time -dlc + */ + return(mattacku(mtmp)); + } else + return(0); + /*NOTREACHED*/ + break; + case 2: /* monster died */ + return(1); + } + } + +/* Now, attack the player if possible - one attack set per monst */ + + if (!mtmp->mpeaceful || + (Conflict && !resist(mtmp, RING_CLASS, 0, 0))) { + if(inrange && !noattacks(mdat) && u.uhp > 0 && !scared && tmp != 3) + if(mattacku(mtmp)) return(1); /* monster died (e.g. exploded) */ + + if(mtmp->wormno) wormhitu(mtmp); + } + /* special speeches for quest monsters */ + if (!mtmp->msleeping && mtmp->mcanmove && nearby) + quest_talk(mtmp); + /* extra emotional attack for vile monsters */ + if (inrange && mtmp->data->msound == MS_CUSS && !mtmp->mpeaceful && + couldsee(mtmp->mx, mtmp->my) && !mtmp->minvis && !rn2(5)) + cuss(mtmp); + + return(tmp == 2); +} + +static NEARDATA const char practical[] = { WEAPON_CLASS, ARMOR_CLASS, GEM_CLASS, FOOD_CLASS, 0 }; +static NEARDATA const char magical[] = { + AMULET_CLASS, POTION_CLASS, SCROLL_CLASS, WAND_CLASS, RING_CLASS, + SPBOOK_CLASS, 0 }; +static NEARDATA const char indigestion[] = { BALL_CLASS, ROCK_CLASS, 0 }; +static NEARDATA const char boulder_class[] = { ROCK_CLASS, 0 }; +static NEARDATA const char gem_class[] = { GEM_CLASS, 0 }; + +boolean +itsstuck(mtmp) +register struct monst *mtmp; +{ + if (sticks(youmonst.data) && mtmp==u.ustuck && !u.uswallow) { + pline("%s cannot escape from you!", Monnam(mtmp)); + return(TRUE); + } + return(FALSE); +} + +/* Return values: + * 0: did not move, but can still attack and do other stuff. + * 1: moved, possibly can attack. + * 2: monster died. + * 3: did not move, and can't do anything else either. + */ +int +m_move(mtmp, after) +register struct monst *mtmp; +register int after; +{ + register int appr; + xchar gx,gy,nix,niy,chcnt; + int chi; /* could be schar except for stupid Sun-2 compiler */ + boolean likegold=0, likegems=0, likeobjs=0, likemagic=0, conceals=0; + boolean likerock=0, can_tunnel=0; + boolean can_open=0, can_unlock=0, doorbuster=0; + boolean uses_items=0, setlikes=0; + boolean avoid=FALSE; + struct permonst *ptr; + struct monst *mtoo; + schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */ + long info[9]; + long flag; + int omx = mtmp->mx, omy = mtmp->my; + struct obj *mw_tmp; + + if(mtmp->mtrapped) { + int i = mintrap(mtmp); + if(i >= 2) { newsym(mtmp->mx,mtmp->my); return(2); }/* it died */ + if(i == 1) return(0); /* still in trap, so didn't move */ + } + ptr = mtmp->data; /* mintrap() can change mtmp->data -dlc */ + + if (mtmp->meating) { + mtmp->meating--; + return 3; /* still eating */ + } + if (hides_under(ptr) && OBJ_AT(mtmp->mx, mtmp->my) && rn2(10)) + return 0; /* do not leave hiding place */ + + set_apparxy(mtmp); + /* where does mtmp think you are? */ + /* Not necessary if m_move called from this file, but necessary in + * other calls of m_move (ex. leprechauns dodging) + */ +#ifdef REINCARNATION + if (!Is_rogue_level(&u.uz)) +#endif + can_tunnel = tunnels(ptr); + can_open = !(nohands(ptr) || verysmall(ptr)); + can_unlock = ((can_open && m_carrying(mtmp, SKELETON_KEY)) || + mtmp->iswiz || is_rider(ptr)); + doorbuster = is_giant(ptr); + if(mtmp->wormno) goto not_special; + /* my dog gets special treatment */ + if(mtmp->mtame) { + mmoved = dog_move(mtmp, after); + goto postmov; + } + + /* likewise for shopkeeper */ + if(mtmp->isshk) { + mmoved = shk_move(mtmp); + if(mmoved == -2) return(2); + if(mmoved >= 0) goto postmov; + mmoved = 0; /* follow player outside shop */ + } + + /* and for the guard */ + if(mtmp->isgd) { + mmoved = gd_move(mtmp); + if(mmoved == -2) return(2); + if(mmoved >= 0) goto postmov; + mmoved = 0; + } + + /* and the acquisitive monsters get special treatment */ + if(is_covetous(ptr)) { + xchar tx = STRAT_GOALX(mtmp->mstrategy), + ty = STRAT_GOALY(mtmp->mstrategy); + struct monst *intruder = m_at(tx, ty); + /* + * if there's a monster on the object or in possesion of it, + * attack it. + */ + if((dist2(mtmp->mx, mtmp->my, tx, ty) < 2) && + intruder && (intruder != mtmp)) { + + notonhead = (intruder->mx != tx || intruder->my != ty); + if(mattackm(mtmp, intruder) == 2) return(2); + mmoved = 1; + } else mmoved = 0; + goto postmov; + } + + /* and for the priest */ + if(mtmp->ispriest) { + mmoved = pri_move(mtmp); + if(mmoved == -2) return(2); + if(mmoved >= 0) goto postmov; + mmoved = 0; + } + +#ifdef MAIL + if(ptr == &mons[PM_MAIL_DAEMON]) { + if(flags.soundok && canseemon(mtmp)) + verbalize("I'm late!"); + mongone(mtmp); + return(2); + } +#endif + + /* teleport if that lies in our nature */ + if(ptr == &mons[PM_TENGU] && !rn2(5) && !mtmp->mcan && + !tele_restrict(mtmp)) { + if(mtmp->mhp < 7 || mtmp->mpeaceful || rn2(2)) + (void) rloc(mtmp, FALSE); + else + mnexto(mtmp); + mmoved = 1; + goto postmov; + } +not_special: + if(u.uswallow && !mtmp->mflee && u.ustuck != mtmp) return(1); + omx = mtmp->mx; + omy = mtmp->my; + gx = mtmp->mux; + gy = mtmp->muy; + appr = mtmp->mflee ? -1 : 1; + if (mtmp->mconf || (u.uswallow && mtmp == u.ustuck)) + appr = 0; + else { +#ifdef GOLDOBJ + struct obj *lepgold, *ygold; +#endif + boolean should_see = (couldsee(omx, omy) && + (levl[gx][gy].lit || + !levl[omx][omy].lit) && + (dist2(omx, omy, gx, gy) <= 36)); + + if (!mtmp->mcansee || + (should_see && Invis && !perceives(ptr) && rn2(11)) || + (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == STRANGE_OBJECT) || u.uundetected || + (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == GOLD_PIECE && !likes_gold(ptr)) || + (mtmp->mpeaceful && !mtmp->isshk) || /* allow shks to follow */ + ((monsndx(ptr) == PM_STALKER || ptr->mlet == S_BAT || + ptr->mlet == S_LIGHT) && !rn2(3))) + appr = 0; + + if(monsndx(ptr) == PM_LEPRECHAUN && (appr == 1) && +#ifndef GOLDOBJ + (mtmp->mgold > u.ugold)) +#else + ( (lepgold = findgold(mtmp->minvent)) && + (lepgold->quan > ((ygold = findgold(invent)) ? ygold->quan : 0L)) )) +#endif + appr = -1; + + if (!should_see && can_track(ptr)) { + register coord *cp; + + cp = gettrack(omx,omy); + if (cp) { + gx = cp->x; + gy = cp->y; + } + } + } + + if ((!mtmp->mpeaceful || !rn2(10)) +#ifdef REINCARNATION + && (!Is_rogue_level(&u.uz)) +#endif + ) { + boolean in_line = lined_up(mtmp) && + (distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= + (throws_rocks(youmonst.data) ? 20 : ACURRSTR/2+1) + ); + + if (appr != 1 || !in_line) { + /* Monsters in combat won't pick stuff up, avoiding the + * situation where you toss arrows at it and it has nothing + * better to do than pick the arrows up. + */ + register int pctload = (curr_mon_load(mtmp) * 100) / + max_mon_load(mtmp); + + /* look for gold or jewels nearby */ + likegold = (likes_gold(ptr) && pctload < 95); + likegems = (likes_gems(ptr) && pctload < 85); + uses_items = (!mindless(ptr) && !is_animal(ptr) + && pctload < 75); + likeobjs = (likes_objs(ptr) && pctload < 75); + likemagic = (likes_magic(ptr) && pctload < 85); + likerock = (throws_rocks(ptr) && pctload < 50 && !In_sokoban(&u.uz)); + conceals = hides_under(ptr); + setlikes = TRUE; + } + } + +#define SQSRCHRADIUS 5 + + { register int minr = SQSRCHRADIUS; /* not too far away */ + register struct obj *otmp; + register int xx, yy; + int oomx, oomy, lmx, lmy; + + /* cut down the search radius if it thinks character is closer. */ + if(distmin(mtmp->mux, mtmp->muy, omx, omy) < SQSRCHRADIUS && + !mtmp->mpeaceful) minr--; + /* guards shouldn't get too distracted */ + if(!mtmp->mpeaceful && is_mercenary(ptr)) minr = 1; + + if((likegold || likegems || likeobjs || likemagic || likerock || conceals) + && (!*in_rooms(omx, omy, SHOPBASE) || (!rn2(25) && !mtmp->isshk))) { + look_for_obj: + oomx = min(COLNO-1, omx+minr); + oomy = min(ROWNO-1, omy+minr); + lmx = max(1, omx-minr); + lmy = max(0, omy-minr); + for(otmp = fobj; otmp; otmp = otmp->nobj) { + /* monsters may pick rocks up, but won't go out of their way + to grab them; this might hamper sling wielders, but it cuts + down on move overhead by filtering out most common item */ + if (otmp->otyp == ROCK) continue; + xx = otmp->ox; + yy = otmp->oy; + /* Nymphs take everything. Most other creatures should not + * pick up corpses except as a special case like in + * searches_for_item(). We need to do this check in + * mpickstuff() as well. + */ + if(xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) { + /* don't get stuck circling around an object that's underneath + an immobile or hidden monster; paralysis victims excluded */ + if ((mtoo = m_at(xx,yy)) != 0 && + (mtoo->msleeping || mtoo->mundetected || + (mtoo->mappearance && !mtoo->iswiz) || + !mtoo->data->mmove)) continue; + + if(((likegold && otmp->oclass == COIN_CLASS) || + (likeobjs && index(practical, otmp->oclass) && + (otmp->otyp != CORPSE || (ptr->mlet == S_NYMPH + && !is_rider(&mons[otmp->corpsenm])))) || + (likemagic && index(magical, otmp->oclass)) || + (uses_items && searches_for_item(mtmp, otmp)) || + (likerock && otmp->otyp == BOULDER) || + (likegems && otmp->oclass == GEM_CLASS && + objects[otmp->otyp].oc_material != MINERAL) || + (conceals && !cansee(otmp->ox,otmp->oy)) || + (ptr == &mons[PM_GELATINOUS_CUBE] && + !index(indigestion, otmp->oclass) && + !(otmp->otyp == CORPSE && + touch_petrifies(&mons[otmp->corpsenm]))) + ) && touch_artifact(otmp,mtmp)) { + if(can_carry(mtmp,otmp) && + (throws_rocks(ptr) || + !sobj_at(BOULDER,xx,yy)) && + (!is_unicorn(ptr) || + objects[otmp->otyp].oc_material == GEMSTONE) && + /* Don't get stuck circling an Elbereth */ + !(onscary(xx, yy, mtmp))) { + minr = distmin(omx,omy,xx,yy); + oomx = min(COLNO-1, omx+minr); + oomy = min(ROWNO-1, omy+minr); + lmx = max(1, omx-minr); + lmy = max(0, omy-minr); + gx = otmp->ox; + gy = otmp->oy; + if (gx == omx && gy == omy) { + mmoved = 3; /* actually unnecessary */ + goto postmov; + } + } + } + } + } + } else if(likegold) { + /* don't try to pick up anything else, but use the same loop */ + uses_items = 0; + likegems = likeobjs = likemagic = likerock = conceals = 0; + goto look_for_obj; + } + + if(minr < SQSRCHRADIUS && appr == -1) { + if(distmin(omx,omy,mtmp->mux,mtmp->muy) <= 3) { + gx = mtmp->mux; + gy = mtmp->muy; + } else + appr = 1; + } + } + + /* don't tunnel if hostile and close enough to prefer a weapon */ + if (can_tunnel && needspick(ptr) && + ((!mtmp->mpeaceful || Conflict) && + dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 8)) + can_tunnel = FALSE; + + nix = omx; + niy = omy; + flag = 0L; + if (mtmp->mpeaceful && (!Conflict || resist(mtmp, RING_CLASS, 0, 0))) + flag |= (ALLOW_SANCT | ALLOW_SSM); + else flag |= ALLOW_U; + if (is_minion(ptr) || is_rider(ptr)) flag |= ALLOW_SANCT; + /* unicorn may not be able to avoid hero on a noteleport level */ + if (is_unicorn(ptr) && !level.flags.noteleport) flag |= NOTONL; + if (passes_walls(ptr)) flag |= (ALLOW_WALL | ALLOW_ROCK); + if (passes_bars(ptr)) flag |= ALLOW_BARS; + if (can_tunnel) flag |= ALLOW_DIG; + if (is_human(ptr) || ptr == &mons[PM_MINOTAUR]) flag |= ALLOW_SSM; + if (is_undead(ptr) && ptr->mlet != S_GHOST) flag |= NOGARLIC; + if (throws_rocks(ptr)) flag |= ALLOW_ROCK; + if (can_open) flag |= OPENDOOR; + if (can_unlock) flag |= UNLOCKDOOR; + if (doorbuster) flag |= BUSTDOOR; + { + register int i, j, nx, ny, nearer; + int jcnt, cnt; + int ndist, nidist; + register coord *mtrk; + coord poss[9]; + + cnt = mfndpos(mtmp, poss, info, flag); + chcnt = 0; + jcnt = min(MTSZ, cnt-1); + chi = -1; + nidist = dist2(nix,niy,gx,gy); + /* allow monsters be shortsighted on some levels for balance */ + if(!mtmp->mpeaceful && level.flags.shortsighted && + nidist > (couldsee(nix,niy) ? 144 : 36) && appr == 1) appr = 0; + if (is_unicorn(ptr) && level.flags.noteleport) { + /* on noteleport levels, perhaps we cannot avoid hero */ + for(i = 0; i < cnt; i++) + if(!(info[i] & NOTONL)) avoid=TRUE; + } + + for(i=0; i < cnt; i++) { + if (avoid && (info[i] & NOTONL)) continue; + nx = poss[i].x; + ny = poss[i].y; + + if (appr != 0) { + mtrk = &mtmp->mtrack[0]; + for(j=0; j < jcnt; mtrk++, j++) + if(nx == mtrk->x && ny == mtrk->y) + if(rn2(4*(cnt-j))) + goto nxti; + } + + nearer = ((ndist = dist2(nx,ny,gx,gy)) < nidist); + + if((appr == 1 && nearer) || (appr == -1 && !nearer) || + (!appr && !rn2(++chcnt)) || !mmoved) { + nix = nx; + niy = ny; + nidist = ndist; + chi = i; + mmoved = 1; + } + nxti: ; + } + } + + if(mmoved) { + register int j; + + if (mmoved==1 && (u.ux != nix || u.uy != niy) && itsstuck(mtmp)) + return(3); + + if (((IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy)) || + closed_door(nix, niy)) && + mmoved==1 && can_tunnel && needspick(ptr)) { + if (closed_door(nix, niy)) { + if (!(mw_tmp = MON_WEP(mtmp)) || + !is_pick(mw_tmp) || !is_axe(mw_tmp)) + mtmp->weapon_check = NEED_PICK_OR_AXE; + } else if (IS_TREE(levl[nix][niy].typ)) { + if (!(mw_tmp = MON_WEP(mtmp)) || !is_axe(mw_tmp)) + mtmp->weapon_check = NEED_AXE; + } else if (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp)) { + mtmp->weapon_check = NEED_PICK_AXE; + } + if (mtmp->weapon_check >= NEED_PICK_AXE && mon_wield_item(mtmp)) + return(3); + } + /* If ALLOW_U is set, either it's trying to attack you, or it + * thinks it is. In either case, attack this spot in preference to + * all others. + */ + /* Actually, this whole section of code doesn't work as you'd expect. + * Most attacks are handled in dochug(). It calls distfleeck(), which + * among other things sets nearby if the monster is near you--and if + * nearby is set, we never call m_move unless it is a special case + * (confused, stun, etc.) The effect is that this ALLOW_U (and + * mfndpos) has no effect for normal attacks, though it lets a confused + * monster attack you by accident. + */ + if(info[chi] & ALLOW_U) { + nix = mtmp->mux; + niy = mtmp->muy; + } + if (nix == u.ux && niy == u.uy) { + mtmp->mux = u.ux; + mtmp->muy = u.uy; + return(0); + } + /* The monster may attack another based on 1 of 2 conditions: + * 1 - It may be confused. + * 2 - It may mistake the monster for your (displaced) image. + * Pets get taken care of above and shouldn't reach this code. + * Conflict gets handled even farther away (movemon()). + */ + if((info[chi] & ALLOW_M) || + (nix == mtmp->mux && niy == mtmp->muy)) { + struct monst *mtmp2; + int mstatus; + mtmp2 = m_at(nix,niy); + + notonhead = mtmp2 && (nix != mtmp2->mx || niy != mtmp2->my); + /* note: mstatus returns 0 if mtmp2 is nonexistent */ + mstatus = mattackm(mtmp, mtmp2); + + if (mstatus & MM_AGR_DIED) /* aggressor died */ + return 2; + + if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && + rn2(4) && mtmp2->movement >= NORMAL_SPEED) { + mtmp2->movement -= NORMAL_SPEED; + notonhead = 0; + mstatus = mattackm(mtmp2, mtmp); /* return attack */ + if (mstatus & MM_DEF_DIED) + return 2; + } + return 3; + } + + if (!m_in_out_region(mtmp,nix,niy)) + return 3; + remove_monster(omx, omy); + place_monster(mtmp, nix, niy); + for(j = MTSZ-1; j > 0; j--) + mtmp->mtrack[j] = mtmp->mtrack[j-1]; + mtmp->mtrack[0].x = omx; + mtmp->mtrack[0].y = omy; + /* Place a segment at the old position. */ + if (mtmp->wormno) worm_move(mtmp); + } else { + if(is_unicorn(ptr) && rn2(2) && !tele_restrict(mtmp)) { + (void) rloc(mtmp, FALSE); + return(1); + } + if(mtmp->wormno) worm_nomove(mtmp); + } +postmov: + if(mmoved == 1 || mmoved == 3) { + boolean canseeit = cansee(mtmp->mx, mtmp->my); + + if(mmoved == 1) { + newsym(omx,omy); /* update the old position */ + if (mintrap(mtmp) >= 2) { + if(mtmp->mx) newsym(mtmp->mx,mtmp->my); + return(2); /* it died */ + } + ptr = mtmp->data; + + /* open a door, or crash through it, if you can */ + if(IS_DOOR(levl[mtmp->mx][mtmp->my].typ) + && !passes_walls(ptr) /* doesn't need to open doors */ + && !can_tunnel /* taken care of below */ + ) { + struct rm *here = &levl[mtmp->mx][mtmp->my]; + boolean btrapped = (here->doormask & D_TRAPPED); + + if(here->doormask & (D_LOCKED|D_CLOSED) && amorphous(ptr)) { + if (flags.verbose && canseemon(mtmp)) + pline("%s %s under the door.", Monnam(mtmp), + (ptr == &mons[PM_FOG_CLOUD] || + ptr == &mons[PM_YELLOW_LIGHT]) + ? "flows" : "oozes"); + } else if(here->doormask & D_LOCKED && can_unlock) { + if(btrapped) { + here->doormask = D_NODOOR; + newsym(mtmp->mx, mtmp->my); + unblock_point(mtmp->mx,mtmp->my); /* vision */ + if(mb_trapped(mtmp)) return(2); + } else { + if (flags.verbose) { + if (canseeit) + You("see a door unlock and open."); + else if (flags.soundok) + You_hear("a door unlock and open."); + } + here->doormask = D_ISOPEN; + /* newsym(mtmp->mx, mtmp->my); */ + unblock_point(mtmp->mx,mtmp->my); /* vision */ + } + } else if (here->doormask == D_CLOSED && can_open) { + if(btrapped) { + here->doormask = D_NODOOR; + newsym(mtmp->mx, mtmp->my); + unblock_point(mtmp->mx,mtmp->my); /* vision */ + if(mb_trapped(mtmp)) return(2); + } else { + if (flags.verbose) { + if (canseeit) + You("see a door open."); + else if (flags.soundok) + You_hear("a door open."); + } + here->doormask = D_ISOPEN; + /* newsym(mtmp->mx, mtmp->my); */ /* done below */ + unblock_point(mtmp->mx,mtmp->my); /* vision */ + } + } else if (here->doormask & (D_LOCKED|D_CLOSED)) { + /* mfndpos guarantees this must be a doorbuster */ + if(btrapped) { + here->doormask = D_NODOOR; + newsym(mtmp->mx, mtmp->my); + unblock_point(mtmp->mx,mtmp->my); /* vision */ + if(mb_trapped(mtmp)) return(2); + } else { + if (flags.verbose) { + if (canseeit) + You("see a door crash open."); + else if (flags.soundok) + You_hear("a door crash open."); + } + if (here->doormask & D_LOCKED && !rn2(2)) + here->doormask = D_NODOOR; + else here->doormask = D_BROKEN; + /* newsym(mtmp->mx, mtmp->my); */ /* done below */ + unblock_point(mtmp->mx,mtmp->my); /* vision */ + } + /* if it's a shop door, schedule repair */ + if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) + add_damage(mtmp->mx, mtmp->my, 0L); + } + } else if (levl[mtmp->mx][mtmp->my].typ == IRONBARS) { + if (flags.verbose && canseemon(mtmp)) + Norep("%s %s %s the iron bars.", Monnam(mtmp), + /* pluralization fakes verb conjugation */ + makeplural(locomotion(ptr, "pass")), + passes_walls(ptr) ? "through" : "between"); + } + + /* possibly dig */ + if (can_tunnel && mdig_tunnel(mtmp)) + return(2); /* mon died (position already updated) */ + + /* set also in domove(), hack.c */ + if (u.uswallow && mtmp == u.ustuck && + (mtmp->mx != omx || mtmp->my != omy)) { + /* If the monster moved, then update */ + u.ux0 = u.ux; + u.uy0 = u.uy; + u.ux = mtmp->mx; + u.uy = mtmp->my; + swallowed(0); + } else + newsym(mtmp->mx,mtmp->my); + } + if(OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) { + /* recompute the likes tests, in case we polymorphed + * or if the "likegold" case got taken above */ + if (setlikes) { + register int pctload = (curr_mon_load(mtmp) * 100) / + max_mon_load(mtmp); + + /* look for gold or jewels nearby */ + likegold = (likes_gold(ptr) && pctload < 95); + likegems = (likes_gems(ptr) && pctload < 85); + uses_items = (!mindless(ptr) && !is_animal(ptr) + && pctload < 75); + likeobjs = (likes_objs(ptr) && pctload < 75); + likemagic = (likes_magic(ptr) && pctload < 85); + likerock = (throws_rocks(ptr) && pctload < 50 && + !In_sokoban(&u.uz)); + conceals = hides_under(ptr); + } + + /* Maybe a rock mole just ate some metal object */ + if (metallivorous(ptr)) { + if (meatmetal(mtmp) == 2) return 2; /* it died */ + } + + if(g_at(mtmp->mx,mtmp->my) && likegold) mpickgold(mtmp); + + /* Maybe a cube ate just about anything */ + if (ptr == &mons[PM_GELATINOUS_CUBE]) { + if (meatobj(mtmp) == 2) return 2; /* it died */ + } + + if(!*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || !rn2(25)) { + boolean picked = FALSE; + + if(likeobjs) picked |= mpickstuff(mtmp, practical); + if(likemagic) picked |= mpickstuff(mtmp, magical); + if(likerock) picked |= mpickstuff(mtmp, boulder_class); + if(likegems) picked |= mpickstuff(mtmp, gem_class); + if(uses_items) picked |= mpickstuff(mtmp, (char *)0); + if(picked) mmoved = 3; + } + + if(mtmp->minvis) { + newsym(mtmp->mx, mtmp->my); + if (mtmp->wormno) see_wsegs(mtmp); + } + } + + if(hides_under(ptr) || ptr->mlet == S_EEL) { + /* Always set--or reset--mundetected if it's already hidden + (just in case the object it was hiding under went away); + usually set mundetected unless monster can't move. */ + if (mtmp->mundetected || + (mtmp->mcanmove && !mtmp->msleeping && rn2(5))) + mtmp->mundetected = (ptr->mlet != S_EEL) ? + OBJ_AT(mtmp->mx, mtmp->my) : + (is_pool(mtmp->mx, mtmp->my) && !Is_waterlevel(&u.uz)); + newsym(mtmp->mx, mtmp->my); + } + if (mtmp->isshk) { + after_shk_move(mtmp); + } + } + return(mmoved); +} + +#endif /* OVL0 */ +#ifdef OVL2 + +boolean +closed_door(x, y) +register int x, y; +{ + return((boolean)(IS_DOOR(levl[x][y].typ) && + (levl[x][y].doormask & (D_LOCKED | D_CLOSED)))); +} + +boolean +accessible(x, y) +register int x, y; +{ + return((boolean)(ACCESSIBLE(levl[x][y].typ) && !closed_door(x, y))); +} + +#endif /* OVL2 */ +#ifdef OVL0 + +/* decide where the monster thinks you are standing */ +void +set_apparxy(mtmp) +register struct monst *mtmp; +{ + boolean notseen, gotu; + register int disp, mx = mtmp->mux, my = mtmp->muy; +#ifdef GOLDOBJ + long umoney = money_cnt(invent); +#endif + + /* + * do cheapest and/or most likely tests first + */ + + /* pet knows your smell; grabber still has hold of you */ + if (mtmp->mtame || mtmp == u.ustuck) goto found_you; + + /* monsters which know where you are don't suddenly forget, + if you haven't moved away */ + if (mx == u.ux && my == u.uy) goto found_you; + + notseen = (!mtmp->mcansee || (Invis && !perceives(mtmp->data))); + /* add cases as required. eg. Displacement ... */ + if (notseen || Underwater) { + /* Xorns can smell valuable metal like gold, treat as seen */ + if ((mtmp->data == &mons[PM_XORN]) && +#ifndef GOLDOBJ + u.ugold +#else + umoney +#endif + && !Underwater) + disp = 0; + else + disp = 1; + } else if (Displaced) { + disp = couldsee(mx, my) ? 2 : 1; + } else disp = 0; + if (!disp) goto found_you; + + /* without something like the following, invis. and displ. + are too powerful */ + gotu = notseen ? !rn2(3) : Displaced ? !rn2(4) : FALSE; + +#if 0 /* this never worked as intended & isn't needed anyway */ + /* If invis but not displaced, staying around gets you 'discovered' */ + gotu |= (!Displaced && u.dx == 0 && u.dy == 0); +#endif + + if (!gotu) { + register int try_cnt = 0; + do { + if (++try_cnt > 200) goto found_you; /* punt */ + mx = u.ux - disp + rn2(2*disp+1); + my = u.uy - disp + rn2(2*disp+1); + } while (!isok(mx,my) + || (disp != 2 && mx == mtmp->mx && my == mtmp->my) + || ((mx != u.ux || my != u.uy) && + !passes_walls(mtmp->data) && + (!ACCESSIBLE(levl[mx][my].typ) || + (closed_door(mx, my) && !can_ooze(mtmp)))) + || !couldsee(mx, my)); + } else { +found_you: + mx = u.ux; + my = u.uy; + } + + mtmp->mux = mx; + mtmp->muy = my; +} + +boolean +can_ooze(mtmp) +struct monst *mtmp; +{ + struct obj *chain, *obj; + + if (!amorphous(mtmp->data)) return FALSE; + if (mtmp == &youmonst) { +#ifndef GOLDOBJ + if (u.ugold > 100L) return FALSE; +#endif + chain = invent; + } else { +#ifndef GOLDOBJ + if (mtmp->mgold > 100L) return FALSE; +#endif + chain = mtmp->minvent; + } + for (obj = chain; obj; obj = obj->nobj) { + int typ = obj->otyp; + +#ifdef GOLDOBJ + if (typ == COIN_CLASS && obj->quan > 100L) return FALSE; +#endif + if (obj->oclass != GEM_CLASS && + !(typ >= ARROW && typ <= BOOMERANG) && + !(typ >= DAGGER && typ <= CRYSKNIFE) && + typ != SLING && + !is_cloak(obj) && typ != FEDORA && + !is_gloves(obj) && typ != LEATHER_JACKET && +#ifdef TOURIST + typ != CREDIT_CARD && !is_shirt(obj) && +#endif + !(typ == CORPSE && verysmall(&mons[obj->corpsenm])) && + typ != FORTUNE_COOKIE && typ != CANDY_BAR && + typ != PANCAKE && typ != LEMBAS_WAFER && + typ != LUMP_OF_ROYAL_JELLY && + obj->oclass != AMULET_CLASS && + obj->oclass != RING_CLASS && +#ifdef WIZARD + obj->oclass != VENOM_CLASS && +#endif + typ != SACK && typ != BAG_OF_HOLDING && + typ != BAG_OF_TRICKS && !Is_candle(obj) && + typ != OILSKIN_SACK && typ != LEASH && + typ != STETHOSCOPE && typ != BLINDFOLD && typ != TOWEL && + typ != TIN_WHISTLE && typ != MAGIC_WHISTLE && + typ != MAGIC_MARKER && typ != TIN_OPENER && + typ != SKELETON_KEY && typ != LOCK_PICK + ) return FALSE; + if (Is_container(obj) && obj->cobj) return FALSE; + + } + return TRUE; +} + +#endif /* OVL0 */ + +/*monmove.c*/ diff --git a/src/monst.c b/src/monst.c new file mode 100644 index 0000000..dc7c267 --- /dev/null +++ b/src/monst.c @@ -0,0 +1,3477 @@ +/* SCCS Id: @(#)monst.c 3.4 2000/07/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "config.h" +#include "permonst.h" +#include "monsym.h" +#include "dungeon.h" /* prerequisite for eshk,vault,epri */ +#include "eshk.h" +#include "vault.h" +#include "epri.h" + +#define NO_ATTK {0,0,0,0} + +#define WT_ELF 800 +#define WT_DRAGON 4500 + +#ifdef C +#undef C +#endif +#ifdef TEXTCOLOR +#include "color.h" +#define C(color) color +#define HI_DOMESTIC CLR_WHITE /* use for player + friendlies */ +#define HI_LORD CLR_MAGENTA +#else +#define C(color) +#endif + +void NDECL(monst_init); +/* + * Entry Format: (from permonst.h) + * + * name, symbol (S_* defines), + * difficulty level, move rate, armor class, magic resistance, + * alignment, creation/geno flags (G_* defines), + * 6 * attack structs ( type , damage-type, # dice, # sides ), + * weight (WT_* defines), nutritional value, extension length, + * sounds made (MS_* defines), physical size (MZ_* defines), + * resistances, resistances conferred (both MR_* defines), + * 3 * flag bitmaps (M1_*, M2_*, and M3_* defines respectively) + * symbol color (C(x) macro) + */ +#define MON(nam,sym,lvl,gen,atk,siz,mr1,mr2,flg1,flg2,flg3,col) \ + {nam,sym,lvl,gen,atk,siz,mr1,mr2,flg1,flg2,flg3,C(col)} +/* LVL() and SIZ() collect several fields to cut down on # of args for MON() */ +#define LVL(lvl,mov,ac,mr,aln) lvl,mov,ac,mr,aln +#define SIZ(wt,nut,pxl,snd,siz) wt,nut,pxl,snd,siz +/* ATTK() and A() are to avoid braces and commas within args to MON() */ +#define ATTK(at,ad,n,d) {at,ad,n,d} +#define A(a1,a2,a3,a4,a5,a6) {a1,a2,a3,a4,a5,a6} + + +/* + * Rule #1: monsters of a given class are contiguous in the + * mons[] array. + * + * Rule #2: monsters of a given class are presented in ascending + * order of strength. + * + * Rule #3: monster frequency is included in the geno mask; + * the frequency can be from 0 to 7. 0's will also + * be skipped during generation. + * + * Rule #4: monster subclasses (e.g. giants) should be kept + * together, unless it violates Rule 2. NOGEN monsters + * won't violate Rule 2. + * + * Guidelines for color assignment: + * + * * Use the same color for all `growth stages' of a monster (ex. + * little dog/big dog, baby naga/full-grown naga. + * + * * Use colors given in names wherever possible. If the class has `real' + * members with strong color associations, use those. + * + * * Favor `cool' colors for cold-resistent monsters, `warm' ones for + * fire-resistent ones. + * + * * Try to reserve purple (magenta) for powerful `ruler' monsters (queen + * bee, kobold lord, &c.). + * + * * Subject to all these constraints, try to use color to make as many + * distinctions as the / command (that is, within a monster letter + * distinct names should map to distinct colors). + * + * The aim in assigning colors is to be consistent enough so a player can + * become `intuitive' about them, deducing some or all of these rules + * unconsciously. Use your common sense. + */ + +#ifndef SPLITMON_2 +NEARDATA struct permonst mons[] = { +/* + * ants + */ + MON("giant ant", S_ANT, + LVL(2, 18, 3, 0, 0), (G_GENO|G_SGROUP|3), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_BROWN), + MON("killer bee", S_ANT, + LVL(1, 18, -1, 0, 0), (G_GENO|G_LGROUP|2), + A(ATTK(AT_STNG, AD_DRST, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1, 5, 0, MS_BUZZ, MZ_TINY), MR_POISON, MR_POISON, + M1_ANIMAL|M1_FLY|M1_NOHANDS|M1_POIS, + M2_HOSTILE|M2_FEMALE, 0, CLR_YELLOW), + MON("soldier ant", S_ANT, + LVL(3, 18, 3, 0, 0), (G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_STNG, AD_DRST, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(20, 5, 0, MS_SILENT, MZ_TINY), MR_POISON, MR_POISON, + M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_POIS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_BLUE), + MON("fire ant", S_ANT, + LVL(3, 18, 3, 10, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_BITE, AD_FIRE, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 10, 0, MS_SILENT, MZ_TINY), MR_FIRE, MR_FIRE, + M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_CARNIVORE, + M2_HOSTILE, M3_INFRAVISIBLE, CLR_RED), + MON("giant beetle", S_ANT, + LVL(5, 6, 4, 0, 0), (G_GENO|3), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_LARGE), MR_POISON, MR_POISON, + M1_ANIMAL|M1_NOHANDS|M1_POIS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_BLACK), + MON("queen bee", S_ANT, + LVL(9, 24, -4, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_STNG, AD_DRST, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1, 5, 0, MS_BUZZ, MZ_TINY), MR_POISON, MR_POISON, + M1_ANIMAL|M1_FLY|M1_NOHANDS|M1_OVIPAROUS|M1_POIS, + M2_HOSTILE|M2_FEMALE|M2_PRINCE, 0, HI_LORD), +/* + * blobs + */ + MON("acid blob", S_BLOB, + LVL(1, 3, 8, 0, 0), (G_GENO|2), + A(ATTK(AT_NONE, AD_ACID, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 10, 0, MS_SILENT, MZ_TINY), + MR_SLEEP|MR_POISON|MR_ACID|MR_STONE, MR_STONE, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_ACID, + M2_WANDER|M2_NEUTER, 0, CLR_GREEN), + MON("quivering blob", S_BLOB, + LVL(5, 1, 8, 0, 0), (G_GENO|2), + A(ATTK(AT_TUCH, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 100, 0, MS_SILENT, MZ_SMALL), + MR_SLEEP|MR_POISON, MR_POISON, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_WANDER|M2_HOSTILE|M2_NEUTER, 0, CLR_WHITE), + MON("gelatinous cube", S_BLOB, + LVL(6, 6, 8, 0, 0), (G_GENO|2), + A(ATTK(AT_TUCH, AD_PLYS, 2, 4), ATTK(AT_NONE, AD_PLYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 150, 0, MS_SILENT, MZ_LARGE), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_ACID|MR_STONE, + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_OMNIVORE|M1_ACID, + M2_WANDER|M2_HOSTILE|M2_NEUTER, 0, CLR_CYAN), +/* + * cockatrice + */ + MON("chickatrice", S_COCKATRICE, + LVL(4, 4, 8, 30, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), ATTK(AT_TUCH, AD_STON, 0, 0), + ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_HISS, MZ_TINY), + MR_POISON|MR_STONE, MR_POISON|MR_STONE, + M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE, M2_HOSTILE, + M3_INFRAVISIBLE, CLR_BROWN), + MON("cockatrice", S_COCKATRICE, + LVL(5, 6, 6, 30, 0), (G_GENO|5), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), ATTK(AT_TUCH, AD_STON, 0, 0), + ATTK(AT_NONE, AD_STON, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_HISS, MZ_SMALL), + MR_POISON|MR_STONE, MR_POISON|MR_STONE, + M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE|M1_OVIPAROUS, M2_HOSTILE, + M3_INFRAVISIBLE, CLR_YELLOW), + MON("pyrolisk", S_COCKATRICE, + LVL(6, 6, 6, 30, 0), (G_GENO|1), + A(ATTK(AT_GAZE, AD_FIRE, 2, 6), NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_HISS, MZ_SMALL), + MR_POISON|MR_FIRE, MR_POISON|MR_FIRE, + M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE|M1_OVIPAROUS, M2_HOSTILE, + M3_INFRAVISIBLE, CLR_RED), +/* + * dogs & other canines + */ + MON("jackal", S_DOG, + LVL(0, 12, 7, 0, 0), (G_GENO|G_SGROUP|3), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 250, 0, MS_BARK, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("fox", S_DOG, + LVL(0, 15, 7, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 250, 0, MS_BARK, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_RED), + MON("coyote", S_DOG, + LVL(1, 12, 7, 0, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 250, 0, MS_BARK, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("werejackal", S_DOG, + LVL(2, 12, 7, 10, -7), (G_NOGEN|G_NOCORPSE), + A(ATTK(AT_BITE, AD_WERE, 1, 4), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 250, 0, MS_BARK, MZ_SMALL), MR_POISON, 0, + M1_NOHANDS|M1_POIS|M1_REGEN|M1_CARNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN), + MON("little dog", S_DOG, + LVL(2, 18, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(150, 150, 0, MS_BARK, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_DOMESTIC, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("dog", S_DOG, + LVL(4, 16, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1 ,6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 200, 0, MS_BARK, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_DOMESTIC, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("large dog", S_DOG, + LVL(6, 15, 4, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 250, 0, MS_BARK, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_STRONG|M2_DOMESTIC, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("dingo", S_DOG, + LVL(4, 16, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1 ,6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 200, 0, MS_BARK, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_YELLOW), + MON("wolf", S_DOG, + LVL(5, 12, 4, 0, 0), (G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 250, 0, MS_BARK, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("werewolf", S_DOG, + LVL(5, 12, 4, 20, -7), (G_NOGEN|G_NOCORPSE), + A(ATTK(AT_BITE, AD_WERE, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 250, 0, MS_BARK, MZ_MEDIUM), MR_POISON, 0, + M1_NOHANDS|M1_POIS|M1_REGEN|M1_CARNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN), + MON("warg", S_DOG, + LVL(7, 12, 4, 0, -5), (G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(850, 350, 0, MS_BARK, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("winter wolf cub", S_DOG, + LVL(5, 12, 4, 0, -5), (G_NOHELL|G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 8), ATTK(AT_BREA, AD_COLD, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(250, 200, 0, MS_BARK, MZ_SMALL), MR_COLD, MR_COLD, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, 0, CLR_CYAN), + MON("winter wolf", S_DOG, + LVL(7, 12, 4, 20, 0), (G_NOHELL|G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_BREA, AD_COLD, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(700, 300, 0, MS_BARK, MZ_LARGE), MR_COLD, MR_COLD, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG, 0, CLR_CYAN), + MON("hell hound pup", S_DOG, + LVL(7, 12, 4, 20, -5), (G_HELL|G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_BREA, AD_FIRE, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 200, 0, MS_BARK, MZ_SMALL), MR_FIRE, MR_FIRE, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_RED), + MON("hell hound", S_DOG, + LVL(12, 14, 2, 20, 0), (G_HELL|G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_BREA, AD_FIRE, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_BARK, MZ_MEDIUM), MR_FIRE, MR_FIRE, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE|M2_STRONG, + M3_INFRAVISIBLE, CLR_RED), +#ifdef CHARON + MON("Cerberus", S_DOG, + LVL(12, 10, 2, 20, -7), (G_HELL|G_UNIQ|1), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_BITE, AD_PHYS, 3, 6), + ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 350, 0, MS_BARK, MZ_LARGE), MR_FIRE, MR_FIRE, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_NOPOLY|M2_HOSTILE|M2_STRONG|M2_PNAME|M2_MALE, M3_INFRAVISIBLE, + CLR_RED), +#endif +/* + * eyes + */ + MON("gas spore", S_EYE, + LVL(1, 3, 10, 0, 0), (G_NOCORPSE|G_GENO|1), + A(ATTK(AT_BOOM, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_HOSTILE|M2_NEUTER, 0, CLR_GRAY), + MON("floating eye", S_EYE, + LVL(2, 1, 9, 10, 0), (G_GENO|5), + A(ATTK(AT_NONE, AD_PLYS, 0,70), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_FLY|M1_AMPHIBIOUS|M1_NOLIMBS|M1_NOHEAD|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_BLUE), + MON("freezing sphere", S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE|G_NOHELL|G_GENO|2), + A(ATTK(AT_EXPL, AD_COLD, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_COLD, MR_COLD, + M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_WHITE), + MON("flaming sphere", S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE|G_GENO|2), + A(ATTK(AT_EXPL, AD_FIRE, 4, 6), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_FIRE, MR_FIRE, + M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_RED), + MON("shocking sphere", S_EYE, + LVL(6, 13, 4, 0, 0), (G_NOCORPSE|G_GENO|2), + A(ATTK(AT_EXPL, AD_ELEC, 4, 6), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_ELEC, MR_ELEC, + M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, HI_ZAP), +#if 0 /* not yet implemented */ + MON("beholder", S_EYE, + LVL(6, 3, 4, 0, -10), (G_GENO|2), + A(ATTK(AT_GAZE, AD_SLOW, 0, 0), ATTK(AT_GAZE, AD_SLEE, 2,25), + ATTK(AT_GAZE, AD_DISN, 0, 0), ATTK(AT_GAZE, AD_STON, 0, 0), + ATTK(AT_GAZE, AD_CNCL, 2, 4), ATTK(AT_BITE, AD_PHYS, 2, 4)), + SIZ(10, 10, 0, MS_SILENT, MZ_SMALL), MR_COLD, 0, + M1_FLY|M1_BREATHLESS|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_NOPOLY|M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_BROWN), +#endif +/* + * felines + */ + MON("kitten", S_FELINE, + LVL(2, 18, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(150, 150, 0, MS_MEW, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_WANDER|M2_DOMESTIC, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("housecat", S_FELINE, + LVL(4, 16, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 200, 0, MS_MEW, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_DOMESTIC, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("jaguar", S_FELINE, + LVL(4, 15, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("lynx", S_FELINE, + LVL(5, 15, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_GROWL, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE,M2_HOSTILE, M3_INFRAVISIBLE, + CLR_CYAN), + MON("panther", S_FELINE, + LVL(5, 15, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + ATTK(AT_BITE, AD_PHYS, 1, 10), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE,M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BLACK), + MON("large cat", S_FELINE, + LVL(6, 15, 4, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(250, 250, 0, MS_MEW, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_STRONG|M2_DOMESTIC, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("tiger", S_FELINE, + LVL(6, 12, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + ATTK(AT_BITE, AD_PHYS, 1,10), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_YELLOW), +/* + * gremlins and gargoyles + */ + MON("gremlin", S_GREMLIN, + LVL(5, 12, 2, 25, -9), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + ATTK(AT_BITE, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_CURS, 0, 0), + NO_ATTK, NO_ATTK), + SIZ(100, 20, 0, MS_LAUGH, MZ_SMALL), MR_POISON, MR_POISON, + M1_SWIM|M1_HUMANOID|M1_POIS, M2_STALK, M3_INFRAVISIBLE, CLR_GREEN), + MON("gargoyle", S_GREMLIN, + LVL(6, 10, -4, 0, -9), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 2, 6), + ATTK(AT_BITE, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 200, 0, MS_GRUNT, MZ_HUMAN), MR_STONE, MR_STONE, + M1_HUMANOID|M1_THICK_HIDE|M1_BREATHLESS, + M2_HOSTILE|M2_STRONG, 0, CLR_BROWN), + MON("winged gargoyle", S_GREMLIN, + LVL(9, 15, -2, 0, -12), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 6), + ATTK(AT_BITE, AD_PHYS, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 300, 0, MS_GRUNT, MZ_HUMAN), MR_STONE, MR_STONE, + M1_FLY|M1_HUMANOID|M1_THICK_HIDE|M1_BREATHLESS|M1_OVIPAROUS, + M2_LORD|M2_HOSTILE|M2_STRONG|M2_MAGIC, 0, HI_LORD), +/* + * humanoids + */ + MON("hobbit", S_HUMANOID, + LVL(1, 9, 10, 0, 6), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 200, 0, MS_HUMANOID, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_GREEN), + MON("dwarf", S_HUMANOID, + LVL(2, 6, 10, 10, 4), (G_GENO|3), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 300, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_DWARF|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("bugbear", S_HUMANOID, + LVL(3, 9, 5, 0, -6), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1250, 250, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("dwarf lord", S_HUMANOID, + LVL(4, 6, 10, 10, 5), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 300, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE, + M2_DWARF|M2_STRONG|M2_LORD|M2_MALE|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + MON("dwarf king", S_HUMANOID, + LVL(6, 6, 10, 20, 6), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 300, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE, + M2_DWARF|M2_STRONG|M2_PRINCE|M2_MALE|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("mind flayer", S_HUMANOID, + LVL(9, 12, 5, 90, -8), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_TENT, AD_DRIN, 2, 1), + ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), + NO_ATTK, NO_ATTK), + SIZ(1450, 400, 0, MS_HISS, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_FLY|M1_SEE_INVIS|M1_OMNIVORE, + M2_HOSTILE|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_MAGENTA), + MON("master mind flayer", S_HUMANOID, + LVL(13, 12, 0, 90, -8), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TENT, AD_DRIN, 2, 1), + ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1), + ATTK(AT_TENT, AD_DRIN, 2, 1), ATTK(AT_TENT, AD_DRIN, 2, 1)), + SIZ(1450, 400, 0, MS_HISS, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_FLY|M1_SEE_INVIS|M1_OMNIVORE, + M2_HOSTILE|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_MAGENTA), +/* + * imps & other minor demons/devils + */ + MON("manes", S_IMP, + LVL(1, 3, 7, 0, -7), (G_GENO|G_LGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), + ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 100, 0, MS_SILENT, MZ_SMALL), MR_SLEEP|MR_POISON, 0, + M1_POIS, M2_HOSTILE|M2_STALK, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("homunculus", S_IMP, + LVL(2, 12, 6, 10, -7), (G_GENO|2), + A(ATTK(AT_BITE, AD_SLEE, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(60, 100, 0, MS_SILENT, MZ_TINY), + MR_SLEEP|MR_POISON, MR_SLEEP|MR_POISON, + M1_FLY|M1_POIS, M2_STALK, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GREEN), + MON("imp", S_IMP, + LVL(3, 12, 2, 20, -7), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(20, 10, 0, MS_CUSS, MZ_TINY), 0, 0, + M1_REGEN, M2_WANDER|M2_STALK, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("lemure", S_IMP, + LVL(3, 3, 7, 0, -7), (G_HELL|G_GENO|G_LGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(150, 100, 0, MS_SILENT, MZ_MEDIUM), + MR_SLEEP|MR_POISON, MR_SLEEP, M1_POIS|M1_REGEN, + M2_HOSTILE|M2_WANDER|M2_STALK|M2_NEUTER, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("quasit", S_IMP, + LVL(3, 15, 2, 20, -7), (G_GENO|2), + A(ATTK(AT_CLAW, AD_DRDX, 1, 2), ATTK(AT_CLAW, AD_DRDX, 1, 2), + ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 200, 0, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, + M1_REGEN, M2_STALK, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + MON("tengu", S_IMP, + LVL(6, 13, 5, 30, 7), (G_GENO|3), + A(ATTK(AT_BITE, AD_PHYS, 1, 7), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 200, 0, MS_SQAWK, MZ_SMALL), MR_POISON, MR_POISON, + M1_TPORT|M1_TPORT_CNTRL, M2_STALK, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_CYAN), +/* + * jellies + */ + MON("blue jelly", S_JELLY, + LVL(4, 0, 8, 10, 0), (G_GENO|2), + A(ATTK(AT_NONE, AD_COLD, 0, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 20, 0, MS_SILENT, MZ_MEDIUM), + MR_COLD|MR_POISON, MR_COLD|MR_POISON, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS + |M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_BLUE), + MON("spotted jelly", S_JELLY, + LVL(5, 0, 8, 10, 0), (G_GENO|1), + A(ATTK(AT_NONE, AD_ACID, 0, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 20, 0, MS_SILENT, MZ_MEDIUM), MR_ACID|MR_STONE, 0, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_ACID|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_GREEN), + MON("ochre jelly", S_JELLY, + LVL(6, 3, 8, 20, 0), (G_GENO|2), + A(ATTK(AT_ENGL, AD_ACID, 3, 6), ATTK(AT_NONE, AD_ACID, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 20, 0, MS_SILENT, MZ_MEDIUM), MR_ACID|MR_STONE, 0, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_ACID|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN), +/* + * kobolds + */ + MON("kobold", S_KOBOLD, + LVL(0, 6, 10, 0, -2), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 100, 0, MS_ORC, MZ_SMALL), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_OMNIVORE, M2_HOSTILE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("large kobold", S_KOBOLD, + LVL(1, 6, 10, 0, -3), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(450, 150, 0, MS_ORC, MZ_SMALL), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_OMNIVORE, M2_HOSTILE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("kobold lord", S_KOBOLD, + LVL(2, 6, 10, 0, -4), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 200, 0, MS_ORC, MZ_SMALL), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_OMNIVORE, + M2_HOSTILE|M2_LORD|M2_MALE|M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, + HI_LORD), + MON("kobold shaman", S_KOBOLD, + LVL(2, 6, 6, 10, -4), (G_GENO|1), + A(ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(450, 150, 0, MS_ORC, MZ_SMALL), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_OMNIVORE, M2_HOSTILE|M2_MAGIC, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_ZAP), +/* + * leprechauns + */ + MON("leprechaun", S_LEPRECHAUN, + LVL(5, 15, 8, 20, 0), (G_GENO|4), + A(ATTK(AT_CLAW, AD_SGLD, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(60, 30, 0, MS_LAUGH, MZ_TINY), 0, 0, + M1_HUMANOID|M1_TPORT, M2_HOSTILE|M2_GREEDY, M3_INFRAVISIBLE, CLR_GREEN), +/* + * mimics + */ + MON("small mimic", S_MIMIC, + LVL(7, 3, 7, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 200, 0, MS_SILENT, MZ_MEDIUM), MR_ACID, 0, + M1_BREATHLESS|M1_AMORPHOUS|M1_HIDE|M1_ANIMAL|M1_NOEYES| + M1_NOHEAD|M1_NOLIMBS|M1_THICK_HIDE|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_BROWN), + MON("large mimic", S_MIMIC, + LVL(8, 3, 7, 10, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_STCK, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 400, 0, MS_SILENT, MZ_LARGE), MR_ACID, 0, + M1_CLING|M1_BREATHLESS|M1_AMORPHOUS|M1_HIDE|M1_ANIMAL|M1_NOEYES| + M1_NOHEAD|M1_NOLIMBS|M1_THICK_HIDE|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG, 0, CLR_RED), + MON("giant mimic", S_MIMIC, + LVL(9, 3, 7, 20, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_STCK, 3, 6), ATTK(AT_CLAW, AD_STCK, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 500, 0, MS_SILENT, MZ_LARGE), MR_ACID, 0, + M1_CLING|M1_BREATHLESS|M1_AMORPHOUS|M1_HIDE|M1_ANIMAL|M1_NOEYES| + M1_NOHEAD|M1_NOLIMBS|M1_THICK_HIDE|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG, 0, HI_LORD), +/* + * nymphs + */ + MON("wood nymph", S_NYMPH, + LVL(3, 12, 9, 20, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_SEDUCE, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_TPORT, M2_HOSTILE|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE, + CLR_GREEN), + MON("water nymph", S_NYMPH, + LVL(3, 12, 9, 20, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_SEDUCE, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_TPORT|M1_SWIM, + M2_HOSTILE|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE, CLR_BLUE), + MON("mountain nymph", S_NYMPH, + LVL(3, 12, 9, 20, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_CLAW, AD_SEDU, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_SEDUCE, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_TPORT, M2_HOSTILE|M2_FEMALE|M2_COLLECT, + M3_INFRAVISIBLE, CLR_BROWN), +/* + * orcs + */ + MON("goblin", S_ORC, + LVL(0, 6, 10, 0, -3), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 100, 0, MS_ORC, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_ORC|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), + MON("hobgoblin", S_ORC, + LVL(1, 9, 10, 0, -4), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 200, 0, MS_ORC, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_ORC|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + /* plain "orc" for zombie corpses only; not created at random + */ + MON("orc", S_ORC, + LVL(1, 9, 10, 0, -3), (G_GENO|G_NOGEN|G_LGROUP), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(850, 150, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("hill orc", S_ORC, + LVL(2, 9, 10, 0, -4), (G_GENO|G_LGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 200, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_YELLOW), + MON("Mordor orc", S_ORC, + LVL(3, 5, 10, 0, -5), (G_GENO|G_LGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 200, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + MON("Uruk-hai", S_ORC, + LVL(3, 7, 10, 0, -4), (G_GENO|G_LGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1300, 300, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLACK), + MON("orc shaman", S_ORC, + LVL(3, 9, 5, 10, -5), (G_GENO|1), + A(ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 300, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_MAGIC, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_ZAP), + MON("orc-captain", S_ORC, + LVL(5, 5, 10, 0, -5), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1350, 350, 0, MS_ORC, MZ_HUMAN), 0, 0, M1_HUMANOID|M1_OMNIVORE, + M2_ORC|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), +/* + * piercers + */ + MON("rock piercer", S_PIERCER, + LVL(3, 1, 3, 0, 0), (G_GENO|4), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 200, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_CLING|M1_HIDE|M1_ANIMAL|M1_NOEYES|M1_NOLIMBS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_GRAY), + MON("iron piercer", S_PIERCER, + LVL(5, 1, 0, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 300, 0, MS_SILENT, MZ_MEDIUM), 0, 0, + M1_CLING|M1_HIDE|M1_ANIMAL|M1_NOEYES|M1_NOLIMBS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_CYAN), + MON("glass piercer", S_PIERCER, + LVL(7, 1, 0, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 4, 6), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 300, 0, MS_SILENT, MZ_MEDIUM), MR_ACID, 0, + M1_CLING|M1_HIDE|M1_ANIMAL|M1_NOEYES|M1_NOLIMBS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_WHITE), +/* + * quadrupeds + */ + MON("rothe", S_QUADRUPED, + LVL(2, 9, 7, 0, 0), (G_GENO|G_SGROUP|4), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 1, 3), + ATTK(AT_BITE, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 100, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("mumak", S_QUADRUPED, + LVL(5, 9, 0, 0, -2), (G_GENO|1), + A(ATTK(AT_BUTT, AD_PHYS, 4,12), ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2500, 500, 0, MS_ROAR, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_THICK_HIDE|M1_NOHANDS|M1_HERBIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_GRAY), + MON("leocrotta", S_QUADRUPED, + LVL(6, 18, 4, 10, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_BITE, AD_PHYS, 2, 6), + ATTK(AT_CLAW, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 500, 0, MS_IMITATE, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE, M2_HOSTILE|M2_STRONG, + M3_INFRAVISIBLE, CLR_RED), + MON("wumpus", S_QUADRUPED, + LVL(8, 3, 2, 10, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2500, 500, 0, MS_BURBLE, MZ_LARGE), 0, 0, + M1_CLING|M1_ANIMAL|M1_NOHANDS|M1_OMNIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_CYAN), + MON("titanothere", S_QUADRUPED, + LVL(12, 12, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2650, 650, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_THICK_HIDE|M1_NOHANDS|M1_HERBIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_GRAY), + MON("baluchitherium", S_QUADRUPED, + LVL(14, 12, 5, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 5, 4), ATTK(AT_CLAW, AD_PHYS, 5, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(3800, 800, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_THICK_HIDE|M1_NOHANDS|M1_HERBIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_GRAY), + MON("mastodon", S_QUADRUPED, + LVL(20, 12, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BUTT, AD_PHYS, 4, 8), ATTK(AT_BUTT, AD_PHYS, 4, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(3800, 800, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_THICK_HIDE|M1_NOHANDS|M1_HERBIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_BLACK), +/* + * rodents + */ + MON("sewer rat", S_RODENT, + LVL(0, 12, 7, 0, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(20, 12, 0, MS_SQEEK, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("giant rat", S_RODENT, + LVL(1, 10, 7, 0, 0), (G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_SQEEK, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("rabid rat", S_RODENT, + LVL(2, 12, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DRCO, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 5, 0, MS_SQEEK, MZ_TINY), MR_POISON, 0, + M1_ANIMAL|M1_NOHANDS|M1_POIS|M1_CARNIVORE, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_BROWN), + MON("wererat", S_RODENT, + LVL(2, 12, 6, 10, -7), (G_NOGEN|G_NOCORPSE), + A(ATTK(AT_BITE, AD_WERE, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(40, 30, 0, MS_SQEEK, MZ_TINY), MR_POISON, 0, + M1_NOHANDS|M1_POIS|M1_REGEN|M1_CARNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN), + MON("rock mole", S_RODENT, + LVL(3, 3, 0, 20, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_TUNNEL|M1_ANIMAL|M1_NOHANDS|M1_METALLIVORE, + M2_HOSTILE|M2_GREEDY|M2_JEWELS|M2_COLLECT, M3_INFRAVISIBLE, CLR_GRAY), + MON("woodchuck", S_RODENT, + LVL(3, 3, 0, 20, 0), (G_NOGEN|G_GENO), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_TUNNEL/*LOGGING*/|M1_ANIMAL|M1_NOHANDS|M1_SWIM|M1_HERBIVORE, + /* In reality, they tunnel instead of cutting lumber. Oh, well. */ + M2_WANDER|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BROWN), +/* + * spiders & scorpions (keep webmaker() in sync if new critters are added) + */ + MON("cave spider", S_SPIDER, + LVL(1, 12, 3, 0, 0), (G_GENO|G_SGROUP|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 50, 0, MS_SILENT, MZ_TINY), MR_POISON, MR_POISON, + M1_CONCEAL|M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_GRAY), + MON("centipede", S_SPIDER, + LVL(2, 4, 3, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DRST, 1, 3), NO_ATTK, NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 50, 0, MS_SILENT, MZ_TINY), MR_POISON, MR_POISON, + M1_CONCEAL|M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_YELLOW), + MON("giant spider", S_SPIDER, + LVL(5, 15, 4, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 100, 0, MS_SILENT, MZ_LARGE), MR_POISON, MR_POISON, + M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_POIS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG, 0, CLR_MAGENTA), + MON("scorpion", S_SPIDER, + LVL(5, 15, 3, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 2), + ATTK(AT_STNG, AD_DRST, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 100, 0, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, + M1_CONCEAL|M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_POIS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_RED), +/* + * trappers, lurkers, &c + */ + MON("lurker above", S_TRAPPER, + LVL(10, 3, 3, 0, 0), (G_GENO|2), + A(ATTK(AT_ENGL, AD_DGST, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 350, 0, MS_SILENT, MZ_HUGE), 0, 0, + M1_HIDE|M1_FLY|M1_ANIMAL|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_CARNIVORE, + M2_HOSTILE|M2_STALK|M2_STRONG, 0, CLR_GRAY), + MON("trapper", S_TRAPPER, + LVL(12, 3, 3, 0, 0), (G_GENO|2), + A(ATTK(AT_ENGL, AD_DGST, 1,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 350, 0, MS_SILENT, MZ_HUGE), 0, 0, + M1_HIDE|M1_ANIMAL|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_CARNIVORE, + M2_HOSTILE|M2_STALK|M2_STRONG, 0, CLR_GREEN), +/* + * unicorns and horses + */ + MON("white unicorn", S_UNICORN, + LVL(4, 24, 2, 70, 7), (G_GENO|2), + A(ATTK(AT_BUTT, AD_PHYS, 1,12), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1300, 300, 0, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, + M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_JEWELS, + M3_INFRAVISIBLE, CLR_WHITE), + MON("gray unicorn", S_UNICORN, + LVL(4, 24, 2, 70, 0), (G_GENO|1), + A(ATTK(AT_BUTT, AD_PHYS, 1,12), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1300, 300, 0, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, + M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_JEWELS, + M3_INFRAVISIBLE, CLR_GRAY), + MON("black unicorn", S_UNICORN, + LVL(4, 24, 2, 70, -7), (G_GENO|1), + A(ATTK(AT_BUTT, AD_PHYS, 1,12), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1300, 300, 0, MS_NEIGH, MZ_LARGE), MR_POISON, MR_POISON, + M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_JEWELS, + M3_INFRAVISIBLE, CLR_BLACK), + MON("pony", S_UNICORN, + LVL(3, 16, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_KICK, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1300, 250, 0, MS_NEIGH, MZ_MEDIUM), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_DOMESTIC, + M3_INFRAVISIBLE, CLR_BROWN), + MON("horse", S_UNICORN, + LVL(5, 20, 5, 0, 0), (G_GENO|2), + A(ATTK(AT_KICK, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 300, 0, MS_NEIGH, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_DOMESTIC, + M3_INFRAVISIBLE, CLR_BROWN), + MON("warhorse", S_UNICORN, + LVL(7, 24, 4, 0, 0), (G_GENO|2), + A(ATTK(AT_KICK, AD_PHYS, 1, 10), ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1800, 350, 0, MS_NEIGH, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_HERBIVORE, M2_WANDER|M2_STRONG|M2_DOMESTIC, + M3_INFRAVISIBLE, CLR_BROWN), +/* + * vortices + */ + MON("fog cloud", S_VORTEX, + LVL(3, 1, 0, 0, 0), (G_GENO|G_NOCORPSE|2), + A(ATTK(AT_ENGL, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS| + M1_AMORPHOUS|M1_UNSOLID, + M2_HOSTILE|M2_NEUTER, 0, CLR_GRAY), + MON("dust vortex", S_VORTEX, + LVL(4, 20, 2, 30, 0), (G_GENO|G_NOCORPSE|2), + A(ATTK(AT_ENGL, AD_BLND, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN), + MON("ice vortex", S_VORTEX, + LVL(5, 20, 2, 30, 0), (G_NOHELL|G_GENO|G_NOCORPSE|1), + A(ATTK(AT_ENGL, AD_COLD, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), + MR_COLD|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_CYAN), + MON("energy vortex", S_VORTEX, + LVL(6, 20, 2, 30, 0), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_ENGL, AD_ELEC, 1, 6), ATTK(AT_ENGL, AD_DREN, 0, 0), + ATTK(AT_NONE, AD_ELEC, 0, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), + MR_ELEC|MR_SLEEP|MR_DISINT|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS| + M1_UNSOLID, + M2_HOSTILE|M2_NEUTER, 0, HI_ZAP), + MON("steam vortex", S_VORTEX, + LVL(7, 22, 2, 30, 0), (G_HELL|G_GENO|G_NOCORPSE|2), + A(ATTK(AT_ENGL, AD_FIRE, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), + MR_FIRE|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS| + M1_UNSOLID, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_BLUE), + MON("fire vortex", S_VORTEX, + LVL(8, 22, 2, 30, 0), (G_HELL|G_GENO|G_NOCORPSE|1), + A(ATTK(AT_ENGL, AD_FIRE, 1,10), ATTK(AT_NONE, AD_FIRE, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), + MR_FIRE|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS| + M1_UNSOLID, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_YELLOW), +/* + * worms + */ + MON("baby long worm", S_WORM, + LVL(8, 3, 5, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 250, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_SLITHY|M1_NOLIMBS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_BROWN), + MON("baby purple worm", S_WORM, + LVL(8, 3, 5, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 250, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_SLITHY|M1_NOLIMBS|M1_CARNIVORE, M2_HOSTILE, + 0, CLR_MAGENTA), + MON("long worm", S_WORM, + LVL(8, 3, 5, 10, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_SILENT, MZ_GIGANTIC), 0, 0, + M1_ANIMAL|M1_SLITHY|M1_NOLIMBS|M1_OVIPAROUS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE|M2_STRONG|M2_NASTY, 0, CLR_BROWN), + MON("purple worm", S_WORM, + LVL(15, 9, 6, 20, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 2, 8), ATTK(AT_ENGL, AD_DGST, 1,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2700, 700, 0, MS_SILENT, MZ_GIGANTIC), 0, 0, + M1_ANIMAL|M1_SLITHY|M1_NOLIMBS|M1_OVIPAROUS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY, 0, CLR_MAGENTA), +/* + * xan, &c + */ + MON("grid bug", S_XAN, + LVL(0, 12, 9, 0, 0), (G_GENO|G_SGROUP|G_NOCORPSE|3), + A(ATTK(AT_BITE, AD_ELEC, 1, 1), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(15, 10, 0, MS_BUZZ, MZ_TINY), MR_ELEC|MR_POISON, 0, + M1_ANIMAL, M2_HOSTILE, M3_INFRAVISIBLE, CLR_MAGENTA), + MON("xan", S_XAN, + LVL(7, 18, -4, 0, 0), (G_GENO|3), + A(ATTK(AT_STNG, AD_LEGS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 300, 0, MS_BUZZ, MZ_TINY), MR_POISON, MR_POISON, + M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_POIS, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_RED), +/* + * lights + */ + MON("yellow light", S_LIGHT, + LVL(3, 15, 0, 0, 0), (G_NOCORPSE|G_GENO|4), + A(ATTK(AT_EXPL, AD_BLND, 10,20), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_SMALL), + MR_FIRE|MR_COLD|MR_ELEC|MR_DISINT|MR_SLEEP|MR_POISON|MR_ACID| + MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_UNSOLID|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_YELLOW), + MON("black light", S_LIGHT, + LVL(5, 15, 0, 0, 0), (G_NOCORPSE|G_GENO|2), + A(ATTK(AT_EXPL, AD_HALU, 10,12), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_SMALL), + MR_FIRE|MR_COLD|MR_ELEC|MR_DISINT|MR_SLEEP|MR_POISON|MR_ACID| + MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_UNSOLID|M1_SEE_INVIS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_BLACK), +/* + * zruty + */ + MON("zruty", S_ZRUTY, + LVL(9, 8, 3, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), + ATTK(AT_BITE, AD_PHYS, 3, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 600, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_BROWN), +/* + * Angels and other lawful minions + */ + MON("couatl", S_ANGEL, + LVL(8, 10, 5, 30, 7), (G_NOHELL|G_SGROUP|G_NOCORPSE|1), + A(ATTK(AT_BITE, AD_DRST, 2, 4), ATTK(AT_BITE, AD_PHYS, 1, 3), + ATTK(AT_HUGS, AD_WRAP, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 400, 0, MS_HISS, MZ_LARGE), MR_POISON, 0, + M1_FLY|M1_POIS, + M2_MINION|M2_STALK|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_GREEN), + MON("Aleax", S_ANGEL, + LVL(10, 8, 0, 30, 7), (G_NOHELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + ATTK(AT_KICK, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_IMITATE, MZ_HUMAN), + MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_HUMANOID|M1_SEE_INVIS, + M2_MINION|M2_STALK|M2_NASTY|M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_YELLOW), + MON("Angel", S_ANGEL, + LVL(14, 10, -4, 55, 12), (G_NOHELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_MAGC, AD_MAGM, 2, 6), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, sizeof(struct epri), MS_CUSS, MZ_HUMAN), + MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_HUMANOID|M1_SEE_INVIS, + M2_NOPOLY|M2_MINION|M2_STALK|M2_STRONG|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_WHITE), + MON("ki-rin", S_ANGEL, + LVL(16, 18, -5, 90, 15), (G_NOHELL|G_NOCORPSE|1), + A(ATTK(AT_KICK, AD_PHYS, 2, 4), ATTK(AT_KICK, AD_PHYS, 2, 4), + ATTK(AT_BUTT, AD_PHYS, 3, 6), ATTK(AT_MAGC, AD_SPEL, 2, 6), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEIGH, MZ_LARGE), 0, 0, + M1_FLY|M1_SEE_INVIS, + M2_NOPOLY|M2_MINION|M2_STALK|M2_STRONG|M2_NASTY|M2_LORD, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_GOLD), + MON("Archon", S_ANGEL, + LVL(19, 16, -6, 80, 15), (G_NOHELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + ATTK(AT_GAZE, AD_BLND, 2, 6), ATTK(AT_CLAW, AD_PHYS, 1, 8), + ATTK(AT_MAGC, AD_SPEL, 4, 6), NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_CUSS, MZ_LARGE), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_HUMANOID|M1_SEE_INVIS|M1_REGEN, + M2_NOPOLY|M2_MINION|M2_STALK|M2_STRONG|M2_NASTY|M2_LORD| + M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), +/* + * Bats + */ + MON("bat", S_BAT, + LVL(0, 22, 8, 0, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(20, 20, 0, MS_SQEEK, MZ_TINY), 0, 0, + M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_WANDER, M3_INFRAVISIBLE, + CLR_BROWN), + MON("giant bat", S_BAT, + LVL(2, 22, 7, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_SQEEK, MZ_SMALL), 0, 0, + M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_WANDER|M2_HOSTILE, M3_INFRAVISIBLE, CLR_RED), + MON("raven", S_BAT, + LVL(4, 20, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_BLND, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(40, 20, 0, MS_SQAWK, MZ_SMALL), 0, 0, + M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_WANDER|M2_HOSTILE, M3_INFRAVISIBLE, CLR_BLACK), + MON("vampire bat", S_BAT, + LVL(5, 20, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRST, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 20, 0, MS_SQEEK, MZ_SMALL), MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_ANIMAL|M1_NOHANDS|M1_POIS|M1_REGEN|M1_OMNIVORE, + M2_HOSTILE, M3_INFRAVISIBLE, CLR_BLACK), +/* + * Centaurs + */ + MON("plains centaur", S_CENTAUR, + LVL(4, 18, 4, 0, 0), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2500, 500, 0, MS_HUMANOID, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_STRONG|M2_GREEDY|M2_COLLECT, + M3_INFRAVISIBLE, CLR_BROWN), + MON("forest centaur", S_CENTAUR, + LVL(5, 18, 3, 10, -1), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2550, 600, 0, MS_HUMANOID, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_STRONG|M2_GREEDY|M2_COLLECT, + M3_INFRAVISIBLE, CLR_GREEN), + MON("mountain centaur", S_CENTAUR, + LVL(6, 20, 2, 10, -3), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1,10), ATTK(AT_KICK, AD_PHYS, 1, 6), + ATTK(AT_KICK, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2550, 500, 0, MS_HUMANOID, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_STRONG|M2_GREEDY|M2_COLLECT, + M3_INFRAVISIBLE, CLR_CYAN), +/* + * Dragons + */ + /* The order of the dragons is VERY IMPORTANT. Quite a few + * pieces of code depend on gray being first and yellow being last. + * The code also depends on the *order* being the same as that for + * dragon scale mail and dragon scales in objects.c. Baby dragons + * cannot confer intrinsics, to avoid polyself/egg abuse. + * + * As reptiles, dragons are cold-blooded and thus aren't seen + * with infravision. Red dragons are the exception. + */ + MON("baby gray dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), 0, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_GRAY), + MON("baby silver dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), 0, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, DRAGON_SILVER), +#if 0 /* DEFERRED */ + MON("baby shimmering dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), 0, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_CYAN), +#endif + MON("baby red dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_FIRE, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, M3_INFRAVISIBLE, CLR_RED), + MON("baby white dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_COLD, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_WHITE), + MON("baby orange dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_SLEEP, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_ORANGE), + MON("baby black dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_DISINT, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_BLACK), + MON("baby blue dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_ELEC, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_BLUE), + MON("baby green dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_POISON, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE|M1_POIS, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_GREEN), + MON("baby yellow dragon", S_DRAGON, + LVL(12, 9, 2, 10, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_ROAR, MZ_HUGE), MR_ACID|MR_STONE, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE|M1_ACID, + M2_HOSTILE|M2_STRONG|M2_GREEDY|M2_JEWELS, 0, CLR_YELLOW), + MON("gray dragon", S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO|1), + A(ATTK(AT_BREA, AD_MAGM, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), 0, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, 0, CLR_GRAY), + MON("silver dragon", S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO|1), + A(ATTK(AT_BREA, AD_COLD, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_COLD, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, + 0, DRAGON_SILVER), +#if 0 /* DEFERRED */ + MON("shimmering dragon", S_DRAGON, + LVL(15, 9, -1, 20, 4), (G_GENO|1), + A(ATTK(AT_BREA, AD_MAGM, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), 0, 0, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, + 0, CLR_CYAN), +#endif + MON("red dragon", S_DRAGON, + LVL(15, 9, -1, 20, -4), (G_GENO|1), + A(ATTK(AT_BREA, AD_FIRE, 6, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_FIRE, MR_FIRE, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, + M3_INFRAVISIBLE, CLR_RED), + MON("white dragon", S_DRAGON, + LVL(15, 9, -1, 20, -5), (G_GENO|1), + A(ATTK(AT_BREA, AD_COLD, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_COLD, MR_COLD, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, 0, CLR_WHITE), + MON("orange dragon", S_DRAGON, + LVL(15, 9, -1, 20, 5), (G_GENO|1), + A(ATTK(AT_BREA, AD_SLEE, 4,25), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_SLEEP, MR_SLEEP, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, + 0, CLR_ORANGE), + MON("black dragon", S_DRAGON, + LVL(15, 9, -1, 20, -6), (G_GENO|1), + A(ATTK(AT_BREA, AD_DISN, 4,10), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_DISINT, MR_DISINT, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, 0, CLR_BLACK), + MON("blue dragon", S_DRAGON, + LVL(15, 9, -1, 20, -7), (G_GENO|1), + A(ATTK(AT_BREA, AD_ELEC, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_ELEC, MR_ELEC, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, 0, CLR_BLUE), + MON("green dragon", S_DRAGON, + LVL(15, 9, -1, 20, 6), (G_GENO|1), + A(ATTK(AT_BREA, AD_DRST, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), MR_POISON, MR_POISON, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE|M1_POIS, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, 0, CLR_GREEN), + MON("yellow dragon", S_DRAGON, + LVL(15, 9, -1, 20, 7), (G_GENO|1), + A(ATTK(AT_BREA, AD_ACID, 4, 6), ATTK(AT_BITE, AD_PHYS, 3, 8), + ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_DRAGON, 1500, 0, MS_ROAR, MZ_GIGANTIC), + MR_ACID|MR_STONE, MR_STONE, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_SEE_INVIS|M1_OVIPAROUS| + M1_CARNIVORE|M1_ACID, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_GREEDY|M2_JEWELS|M2_MAGIC, + 0, CLR_YELLOW), +/* + * Elementals + */ + MON("stalker", S_ELEMENTAL, + LVL(8, 12, 3, 0, 0), (G_GENO|3), + A(ATTK(AT_CLAW, AD_PHYS, 4, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 400, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_FLY|M1_SEE_INVIS, + M2_WANDER|M2_STALK|M2_HOSTILE|M2_STRONG, M3_INFRAVISION, CLR_WHITE), + MON("air elemental", S_ELEMENTAL, + LVL(8, 36, 2, 30, 0), (G_NOCORPSE|1), + A(ATTK(AT_ENGL, AD_PHYS, 1, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), MR_POISON|MR_STONE, 0, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_UNSOLID|M1_FLY, + M2_STRONG|M2_NEUTER, 0, CLR_CYAN), + MON("fire elemental", S_ELEMENTAL, + LVL(8, 12, 2, 30, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_FIRE, 3, 6), ATTK(AT_NONE, AD_FIRE, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUGE), MR_FIRE|MR_POISON|MR_STONE, 0, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_UNSOLID|M1_FLY|M1_NOTAKE, + M2_STRONG|M2_NEUTER, M3_INFRAVISIBLE, CLR_YELLOW), + MON("earth elemental", S_ELEMENTAL, + LVL(8, 6, 2, 30, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2500, 0, 0, MS_SILENT, MZ_HUGE), + MR_FIRE|MR_COLD|MR_POISON|MR_STONE, 0, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_BREATHLESS| + M1_WALLWALK|M1_THICK_HIDE, + M2_STRONG|M2_NEUTER, 0, CLR_BROWN), + MON("water elemental", S_ELEMENTAL, + LVL(8, 6, 2, 30, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 5, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2500, 0, 0, MS_SILENT, MZ_HUGE), MR_POISON|MR_STONE, 0, + M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_AMPHIBIOUS|M1_SWIM, + M2_STRONG|M2_NEUTER, 0, CLR_BLUE), +/* + * Fungi + */ + MON("lichen", S_FUNGUS, + LVL(0, 1, 9, 0, 0), (G_GENO|4), + A(ATTK(AT_TUCH, AD_STCK, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(20, 200, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_BRIGHT_GREEN), + MON("brown mold", S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO|1), + A(ATTK(AT_NONE, AD_COLD, 0, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 30, 0, MS_SILENT, MZ_SMALL), + MR_COLD|MR_POISON, MR_COLD|MR_POISON, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN), + MON("yellow mold", S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO|2), + A(ATTK(AT_NONE, AD_STUN, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 30, 0, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_POIS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_YELLOW), + MON("green mold", S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO|1), + A(ATTK(AT_NONE, AD_ACID, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 30, 0, MS_SILENT, MZ_SMALL), MR_ACID|MR_STONE, MR_STONE, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_ACID|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_GREEN), + MON("red mold", S_FUNGUS, + LVL(1, 0, 9, 0, 0), (G_GENO|1), + A(ATTK(AT_NONE, AD_FIRE, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 30, 0, MS_SILENT, MZ_SMALL), + MR_FIRE|MR_POISON, MR_FIRE|MR_POISON, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, M3_INFRAVISIBLE, CLR_RED), + MON("shrieker", S_FUNGUS, + LVL(3, 1, 7, 0, 0), (G_GENO|1), + A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 100, 0, MS_SHRIEK, MZ_SMALL), MR_POISON, MR_POISON, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_MAGENTA), + MON("violet fungus", S_FUNGUS, + LVL(3, 1, 7, 0, 0), (G_GENO|2), + A(ATTK(AT_TUCH, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_STCK, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 100, 0, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, + M1_BREATHLESS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD|M1_MINDLESS|M1_NOTAKE, + M2_HOSTILE|M2_NEUTER, 0, CLR_MAGENTA), +/* + * Gnomes + */ + MON("gnome", S_GNOME, + LVL(1, 6, 10, 4, 0), (G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(650, 100, 0, MS_ORC, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_NOPOLY|M2_GNOME|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("gnome lord", S_GNOME, + LVL(3, 8, 10, 4, 0), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(700, 120, 0, MS_ORC, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_GNOME|M2_LORD|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + MON("gnomish wizard", S_GNOME, + LVL(3, 10, 4, 10, 0), (G_GENO|1), + A(ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(700, 120, 0, MS_ORC, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_GNOME|M2_MAGIC, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_ZAP), + MON("gnome king", S_GNOME, + LVL(5, 10, 10, 20, 0), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(750, 150, 0, MS_ORC, MZ_SMALL), 0, 0, + M1_HUMANOID|M1_OMNIVORE, M2_GNOME|M2_PRINCE|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), +#ifdef SPLITMON_1 +}; +#endif +#endif /* !SPLITMON_2 */ + +/* horrible kludge alert: + * This is a compiler-specific kludge to allow the compilation of monst.o in + * two pieces, by defining first SPLITMON_1 and then SPLITMON_2. The + * resulting assembler files (monst1.s and monst2.s) are then run through + * sed to change local symbols, concatenated together, and assembled to + * produce monst.o. THIS ONLY WORKS WITH THE ATARI GCC, and should only + * be done if you don't have enough memory to compile monst.o the "normal" + * way. --ERS + */ + +#ifndef SPLITMON_1 +#ifdef SPLITMON_2 +struct permonst _mons2[] = { +#endif +/* + * giant Humanoids + */ + MON("giant", S_GIANT, + LVL(6, 6, 0, 0, 2), (G_GENO|G_NOGEN|1), + A(ATTK(AT_WEAP, AD_PHYS, 2,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 750, 0, MS_BOAST, MZ_HUGE), 0, 0, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("stone giant", S_GIANT, + LVL(6, 6, 0, 0, 2), (G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 2,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 750, 0, MS_BOAST, MZ_HUGE), 0, 0, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), + MON("hill giant", S_GIANT, + LVL(8, 10, 6, 0, -2), (G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2200, 700, 0, MS_BOAST, MZ_HUGE), 0, 0, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_CYAN), + MON("fire giant", S_GIANT, + LVL(9, 12, 4, 5, 2), (G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 2,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 750, 0, MS_BOAST, MZ_HUGE), MR_FIRE, MR_FIRE, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_YELLOW), + MON("frost giant", S_GIANT, + LVL(10, 12, 3, 10, -3), (G_NOHELL|G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 2,12), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 750, 0, MS_BOAST, MZ_HUGE), MR_COLD, MR_COLD, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_WHITE), + MON("storm giant", S_GIANT, + LVL(16, 12, 3, 10, -3), (G_GENO|G_SGROUP|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 12), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 750, 0, MS_BOAST, MZ_HUGE), MR_ELEC, MR_ELEC, + M1_HUMANOID|M1_CARNIVORE, + M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_JEWELS, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + MON("ettin", S_GIANT, + LVL(10, 12, 3, 0, 0), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_WEAP, AD_PHYS, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 500, 0, MS_GRUNT, MZ_HUGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("titan", S_GIANT, + LVL(16, 18, -3, 70, 9), (1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2300, 900, 0, MS_SPELL, MZ_HUGE), 0, 0, + M1_FLY|M1_HUMANOID|M1_OMNIVORE, + M2_STRONG|M2_ROCKTHROW|M2_NASTY|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_MAGENTA), + MON("minotaur", S_GIANT, + LVL(15, 15, 6, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_CLAW, AD_PHYS, 3,10), ATTK(AT_CLAW, AD_PHYS, 3,10), + ATTK(AT_BUTT, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 700, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_BROWN), +/* 'I' is a visual marker for all invisible monsters and must be unused */ +/* + * Jabberwock + */ + MON("jabberwock", S_JABBERWOCK, + LVL(15, 12, -2, 50, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2,10), ATTK(AT_BITE, AD_PHYS, 2,10), + ATTK(AT_CLAW, AD_PHYS, 2,10), ATTK(AT_CLAW, AD_PHYS, 2,10), + NO_ATTK, NO_ATTK), + SIZ(1300, 600, 0, MS_BURBLE, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_FLY|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_COLLECT, M3_INFRAVISIBLE, CLR_ORANGE), +#if 0 /* DEFERRED */ + MON("vorpal jabberwock", S_JABBERWOCK, + LVL(20, 12, -2, 50, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 3, 10), ATTK(AT_BITE, AD_PHYS, 3, 10), + ATTK(AT_CLAW, AD_PHYS, 3, 10), ATTK(AT_CLAW, AD_PHYS, 3, 10), + NO_ATTK, NO_ATTK), + SIZ(1300, 600, 0, MS_BURBLE, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_FLY|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY|M2_COLLECT, M3_INFRAVISIBLE, HI_LORD), +#endif +#ifdef KOPS +/* + * Kops + */ + MON("Keystone Kop", S_KOP, + LVL(1, 6, 10, 10, 9), (G_GENO|G_LGROUP|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID, + M2_HUMAN|M2_WANDER|M2_HOSTILE|M2_MALE|M2_COLLECT, M3_INFRAVISIBLE, + CLR_BLUE), + MON("Kop Sergeant", S_KOP, + LVL(2, 8, 10, 10, 10), (G_GENO|G_SGROUP|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID, + M2_HUMAN|M2_WANDER|M2_HOSTILE|M2_STRONG|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE, CLR_BLUE), + MON("Kop Lieutenant", S_KOP, + LVL(3, 10, 10, 20, 11), (G_GENO|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID, + M2_HUMAN|M2_WANDER|M2_HOSTILE|M2_STRONG|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE, CLR_CYAN), + MON("Kop Kaptain", S_KOP, + LVL(4, 12, 10, 20, 12), (G_GENO|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_ARREST, MZ_HUMAN), 0, 0, M1_HUMANOID, + M2_HUMAN|M2_WANDER|M2_HOSTILE|M2_STRONG|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE, HI_LORD), +#endif +/* + * Liches + */ + MON("lich", S_LICH, + LVL(11, 6, 0, 30, -9), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_TUCH, AD_COLD, 1,10), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 100, 0, MS_MUMBLE, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, MR_COLD, + M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_HOSTILE|M2_MAGIC, M3_INFRAVISION, CLR_BROWN), + MON("demilich", S_LICH, + LVL(14, 9, -2, 60, -12), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_TUCH, AD_COLD, 3, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 100, 0, MS_MUMBLE, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, MR_COLD, + M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_HOSTILE|M2_MAGIC, M3_INFRAVISION, CLR_RED), + MON("master lich", S_LICH, + LVL(17, 9, -4, 90, -15), (G_HELL|G_GENO|G_NOCORPSE|1), + A(ATTK(AT_TUCH, AD_COLD, 3, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 100, 0, MS_MUMBLE, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_SLEEP|MR_POISON, MR_FIRE|MR_COLD, + M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_HOSTILE|M2_MAGIC, M3_WANTSBOOK|M3_INFRAVISION, HI_LORD), + MON("arch-lich", S_LICH, + LVL(25, 9, -6, 90, -15), (G_HELL|G_GENO|G_NOCORPSE|1), + A(ATTK(AT_TUCH, AD_COLD, 5, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 100, 0, MS_MUMBLE, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_SLEEP|MR_ELEC|MR_POISON, MR_FIRE|MR_COLD, + M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_HOSTILE|M2_MAGIC, M3_WANTSBOOK|M3_INFRAVISION, HI_LORD), +/* + * Mummies + */ + MON("kobold mummy", S_MUMMY, + LVL(3, 8, 6, 20, -2), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 50, 0, MS_SILENT, MZ_SMALL), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE, M3_INFRAVISION, CLR_BROWN), + MON("gnome mummy", S_MUMMY, + LVL(4, 10, 6, 20, -3), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(650, 50, 0, MS_SILENT, MZ_SMALL), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_GNOME, M3_INFRAVISION, CLR_RED), + MON("orc mummy", S_MUMMY, + LVL(5, 10, 5, 20, -4), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(850, 75, 0, MS_SILENT, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_ORC|M2_GREEDY|M2_JEWELS, M3_INFRAVISION, + CLR_GRAY), + MON("dwarf mummy", S_MUMMY, + LVL(5, 10, 5, 20, -4), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 150, 0, MS_SILENT, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_DWARF|M2_GREEDY|M2_JEWELS, M3_INFRAVISION, + CLR_RED), + MON("elf mummy", S_MUMMY, + LVL(6, 12, 4, 30, -5), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 175, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_ELF, M3_INFRAVISION, CLR_GREEN), + MON("human mummy", S_MUMMY, + LVL(6, 12, 4, 30, -5), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE, M3_INFRAVISION, CLR_GRAY), + MON("ettin mummy", S_MUMMY, + LVL(7, 12, 4, 30, -6), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 250, 0, MS_SILENT, MZ_HUGE), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_STRONG, M3_INFRAVISION, CLR_BLUE), + MON("giant mummy", S_MUMMY, + LVL(8, 14, 3, 30, -7), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2050, 375, 0, MS_SILENT, MZ_HUGE), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_HOSTILE|M2_GIANT|M2_STRONG|M2_JEWELS, M3_INFRAVISION, + CLR_CYAN), +/* + * Nagas + */ + MON("red naga hatchling", S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 100, 0, MS_MUMBLE, MZ_LARGE), + MR_FIRE|MR_POISON, MR_FIRE|MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_NOTAKE|M1_OMNIVORE, M2_STRONG, + M3_INFRAVISIBLE, CLR_RED), + MON("black naga hatchling", S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 100, 0, MS_MUMBLE, MZ_LARGE), + MR_POISON|MR_ACID|MR_STONE, MR_POISON|MR_STONE, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_ACID|M1_NOTAKE|M1_CARNIVORE, + M2_STRONG, 0, CLR_BLACK), + MON("golden naga hatchling", S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 100, 0, MS_MUMBLE, MZ_LARGE), MR_POISON, MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_NOTAKE|M1_OMNIVORE, + M2_STRONG, 0, HI_GOLD), + MON("guardian naga hatchling", S_NAGA, + LVL(3, 10, 6, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 100, 0, MS_MUMBLE, MZ_LARGE), MR_POISON, MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_NOTAKE|M1_OMNIVORE, + M2_STRONG, 0, CLR_GREEN), + MON("red naga", S_NAGA, + LVL(6, 12, 4, 0, -4), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_BREA, AD_FIRE, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2600, 400, 0, MS_MUMBLE, MZ_HUGE), + MR_FIRE|MR_POISON, MR_FIRE|MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_OVIPAROUS|M1_NOTAKE|M1_OMNIVORE, + M2_STRONG, M3_INFRAVISIBLE, CLR_RED), + MON("black naga", S_NAGA, + LVL(8, 14, 2, 10, 4), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_SPIT, AD_ACID, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2600, 400, 0, MS_MUMBLE, MZ_HUGE), + MR_POISON|MR_ACID|MR_STONE, MR_POISON|MR_STONE, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_OVIPAROUS|M1_ACID|M1_NOTAKE| + M1_CARNIVORE, + M2_STRONG, 0, CLR_BLACK), + MON("golden naga", S_NAGA, + LVL(10, 14, 2, 70, 5), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), ATTK(AT_MAGC, AD_SPEL, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2600, 400, 0, MS_MUMBLE, MZ_HUGE), MR_POISON, MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_OVIPAROUS|M1_NOTAKE|M1_OMNIVORE, + M2_STRONG, 0, HI_GOLD), + MON("guardian naga", S_NAGA, + LVL(12, 16, 0, 50, 7), (G_GENO|1), + A(ATTK(AT_BITE, AD_PLYS, 1, 6), ATTK(AT_SPIT, AD_DRST, 1, 6), + ATTK(AT_HUGS, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2600, 400, 0, MS_MUMBLE, MZ_HUGE), MR_POISON, MR_POISON, + M1_NOLIMBS|M1_SLITHY|M1_THICK_HIDE|M1_OVIPAROUS|M1_POIS|M1_NOTAKE| + M1_OMNIVORE, + M2_STRONG, 0, CLR_GREEN), +/* + * Ogres + */ + MON("ogre", S_OGRE, + LVL(5, 10, 5, 0, -3), (G_SGROUP|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1600, 500, 0, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID|M1_CARNIVORE, + M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BROWN), + MON("ogre lord", S_OGRE, + LVL(7, 12, 3, 30, -5), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 700, 0, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID|M1_CARNIVORE, + M2_STRONG|M2_LORD|M2_MALE|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("ogre king", S_OGRE, + LVL(9, 14, 4, 60, -7), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 3, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 750, 0, MS_GRUNT, MZ_LARGE), 0, 0, M1_HUMANOID|M1_CARNIVORE, + M2_STRONG|M2_PRINCE|M2_MALE|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), +/* + * Puddings + */ + MON("gray ooze", S_PUDDING, + LVL(3, 1, 8, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_RUST, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 250, 0, MS_SILENT, MZ_MEDIUM), + MR_FIRE|MR_COLD|MR_POISON|MR_ACID|MR_STONE, MR_FIRE|MR_COLD|MR_POISON, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_OMNIVORE|M1_ACID, + M2_HOSTILE|M2_NEUTER, 0, CLR_GRAY), + MON("brown pudding", S_PUDDING, + LVL(5, 3, 8, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DCAY, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 250, 0, MS_SILENT, MZ_MEDIUM), + MR_COLD|MR_ELEC|MR_POISON|MR_ACID|MR_STONE, MR_COLD|MR_ELEC|MR_POISON, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_OMNIVORE|M1_ACID, + M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN), + MON("black pudding", S_PUDDING, + LVL(10, 6, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_CORR, 3, 8), ATTK(AT_NONE, AD_CORR, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 250, 0, MS_SILENT, MZ_LARGE), + MR_COLD|MR_ELEC|MR_POISON|MR_ACID|MR_STONE, MR_COLD|MR_ELEC|MR_POISON, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_OMNIVORE|M1_ACID, + M2_HOSTILE|M2_NEUTER, 0, CLR_BLACK), + MON("green slime", S_PUDDING, + LVL(6, 6, 6, 0, 0), (G_HELL|G_GENO|1), + A(ATTK(AT_TUCH, AD_SLIM, 1, 4), ATTK(AT_NONE, AD_SLIM, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 150, 0, MS_SILENT, MZ_LARGE), + MR_COLD|MR_ELEC|MR_POISON|MR_ACID|MR_STONE, 0, + M1_BREATHLESS|M1_AMORPHOUS|M1_NOEYES|M1_NOLIMBS|M1_NOHEAD| + M1_MINDLESS|M1_OMNIVORE|M1_ACID|M1_POIS, + M2_HOSTILE|M2_NEUTER, 0, CLR_GREEN), +/* + * Quantum mechanics + */ + MON("quantum mechanic", S_QUANTMECH, + LVL(7, 12, 3, 10, 0), (G_GENO|3), + A(ATTK(AT_CLAW, AD_TLPT, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 20, 0, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE|M1_POIS|M1_TPORT, M2_HOSTILE, M3_INFRAVISIBLE, + CLR_CYAN), +/* + * Rust monster or disenchanter + */ + MON("rust monster", S_RUSTMONST, + LVL(5, 18, 2, 0, 0), (G_GENO|2), + A(ATTK(AT_TUCH, AD_RUST, 0, 0), ATTK(AT_TUCH, AD_RUST, 0, 0), + ATTK(AT_NONE, AD_RUST, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 250, 0, MS_SILENT, MZ_MEDIUM), 0, 0, + M1_SWIM|M1_ANIMAL|M1_NOHANDS|M1_METALLIVORE, M2_HOSTILE, + M3_INFRAVISIBLE, CLR_BROWN), + MON("disenchanter", S_RUSTMONST, + LVL(12, 12, -10, 0, -3), (G_HELL|G_GENO|2), + A(ATTK(AT_CLAW, AD_ENCH, 4, 4), ATTK(AT_NONE, AD_ENCH, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(750, 200, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_CARNIVORE, M2_HOSTILE, + M3_INFRAVISIBLE, CLR_BLUE), +/* + * Snakes + */ + MON("garter snake", S_SNAKE, + LVL(1, 8, 8, 0, 0), (G_LGROUP|G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(50, 60, 0, MS_HISS, MZ_TINY), 0, 0, + M1_SWIM|M1_CONCEAL|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY|M1_OVIPAROUS| + M1_CARNIVORE|M1_NOTAKE, + 0, 0, CLR_GREEN), + MON("snake", S_SNAKE, + LVL(4, 15, 3, 0, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 80, 0, MS_HISS, MZ_SMALL), MR_POISON, MR_POISON, + M1_SWIM|M1_CONCEAL|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY|M1_POIS| + M1_OVIPAROUS|M1_CARNIVORE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_BROWN), + MON("water moccasin", S_SNAKE, + LVL(4, 15, 3, 0, 0), (G_GENO|G_NOGEN|G_LGROUP), + A(ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(150, 80, 0, MS_HISS, MZ_SMALL), MR_POISON, MR_POISON, + M1_SWIM|M1_CONCEAL|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY|M1_POIS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, 0, CLR_RED), + MON("pit viper", S_SNAKE, + LVL(6, 15, 2, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DRST, 1, 4), ATTK(AT_BITE, AD_DRST, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 60, 0, MS_HISS, MZ_MEDIUM), + MR_POISON, MR_POISON, + M1_SWIM|M1_CONCEAL|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY|M1_POIS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, M3_INFRAVISION, CLR_BLUE), + MON("python", S_SNAKE, + LVL(6, 3, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), ATTK(AT_TUCH, AD_PHYS, 0, 0), + ATTK(AT_HUGS, AD_WRAP, 1, 4), ATTK(AT_HUGS, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK), + SIZ(250, 100, 0, MS_HISS, MZ_LARGE), 0, 0, + M1_SWIM|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE|M2_STRONG, M3_INFRAVISION, CLR_MAGENTA), + MON("cobra", S_SNAKE, + LVL(6, 18, 2, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_DRST, 2, 4), ATTK(AT_SPIT, AD_BLND, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(250, 100, 0, MS_HISS, MZ_MEDIUM), + MR_POISON, MR_POISON, + M1_SWIM|M1_CONCEAL|M1_NOLIMBS|M1_ANIMAL|M1_SLITHY|M1_POIS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, 0, CLR_BLUE), +/* + * Trolls + */ + MON("troll", S_TROLL, + LVL(7, 12, 4, 0, -3), (G_GENO|2), + A(ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 4, 2), + ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 350, 0, MS_GRUNT, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_REGEN|M1_CARNIVORE, + M2_STRONG|M2_STALK|M2_HOSTILE, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_BROWN), + MON("ice troll", S_TROLL, + LVL(9, 10, 2, 20, -3), (G_NOHELL|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_COLD, 2, 6), + ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1000, 300, 0, MS_GRUNT, MZ_LARGE), MR_COLD, MR_COLD, + M1_HUMANOID|M1_REGEN|M1_CARNIVORE, + M2_STRONG|M2_STALK|M2_HOSTILE, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_WHITE), + MON("rock troll", S_TROLL, + LVL(9, 12, 0, 0, -3), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8), + ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 300, 0, MS_GRUNT, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_REGEN|M1_CARNIVORE, + M2_STRONG|M2_STALK|M2_HOSTILE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_CYAN), + MON("water troll", S_TROLL, + LVL(11, 14, 4, 40, -3), (G_NOGEN|G_GENO), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 350, 0, MS_GRUNT, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_REGEN|M1_CARNIVORE|M1_SWIM, + M2_STRONG|M2_STALK|M2_HOSTILE, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_BLUE), + MON("Olog-hai", S_TROLL, + LVL(13, 12, -4, 0, -7), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 2, 8), + ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 400, 0, MS_GRUNT, MZ_LARGE), 0, 0, + M1_HUMANOID|M1_REGEN|M1_CARNIVORE, + M2_STRONG|M2_STALK|M2_HOSTILE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), +/* + * Umber hulk + */ + MON("umber hulk", S_UMBER, + LVL(9, 6, 2, 25, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_CLAW, AD_PHYS, 3, 4), + ATTK(AT_BITE, AD_PHYS, 2, 5), ATTK(AT_GAZE, AD_CONF, 0, 0), + NO_ATTK, NO_ATTK), + SIZ(1200, 500, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_TUNNEL|M1_CARNIVORE, M2_STRONG, M3_INFRAVISIBLE, CLR_BROWN), +/* + * Vampires + */ + MON("vampire", S_VAMPIRE, + LVL(10, 12, 1, 25, -8), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_BITE, AD_DRLI, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE, + CLR_RED), + MON("vampire lord", S_VAMPIRE, + LVL(12, 14, 0, 50, -9), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_LORD|M2_MALE, + M3_INFRAVISIBLE, CLR_BLUE), +#if 0 /* DEFERRED */ + MON("vampire mage", S_VAMPIRE, + LVL(20, 14, -4, 50, -9), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_DRLI, 2, 8), ATTK(AT_BITE, AD_DRLI, 1, 8), + ATTK(AT_MAGC, AD_SPEL, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_LORD|M2_MALE| + M2_MAGIC, M3_INFRAVISIBLE, HI_ZAP), +#endif + MON("Vlad the Impaler", S_VAMPIRE, + LVL(14, 18, -3, 80, -10), (G_NOGEN|G_NOCORPSE|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 10), ATTK(AT_BITE, AD_DRLI, 1, 10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_VAMPIRE, MZ_HUMAN), MR_SLEEP|MR_POISON, 0, + M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_POIS|M1_REGEN, + M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG| + M2_NASTY|M2_PRINCE|M2_MALE, + M3_WAITFORU|M3_WANTSCAND|M3_INFRAVISIBLE, HI_LORD), +/* + * Wraiths + */ + MON("barrow wight", S_WRAITH, + LVL(3, 12, 5, 5, -3), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_DRLI, 0, 0), ATTK(AT_MAGC, AD_SPEL, 0, 0), + ATTK(AT_CLAW, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1200, 0, 0, MS_SPELL, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_HUMANOID, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_COLLECT, 0, CLR_GRAY), + MON("wraith", S_WRAITH, + LVL(6, 12, 4, 15, -6), (G_GENO|2), + A(ATTK(AT_TUCH, AD_DRLI, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_BREATHLESS|M1_FLY|M1_HUMANOID|M1_UNSOLID, + M2_UNDEAD|M2_STALK|M2_HOSTILE, 0, CLR_BLACK), + MON("Nazgul", S_WRAITH, + LVL(13, 12, 0, 25, -17), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_DRLI, 1, 4), ATTK(AT_BREA, AD_SLEE, 2,25), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 0, 0, MS_SPELL, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, 0, M1_BREATHLESS|M1_HUMANOID, + M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_STRONG|M2_HOSTILE|M2_MALE|M2_COLLECT, + 0, HI_LORD), +/* + * Xorn + */ + MON("xorn", S_XORN, + LVL(8, 9,-2, 20, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), + ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_BITE, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK), + SIZ(1200, 700, 0, MS_ROAR, MZ_MEDIUM), + MR_FIRE|MR_COLD|MR_STONE, MR_STONE, + M1_BREATHLESS|M1_WALLWALK|M1_THICK_HIDE|M1_METALLIVORE, + M2_HOSTILE|M2_STRONG, 0, CLR_BROWN), +/* + * Apelike beasts + */ + MON("monkey", S_YETI, + LVL(2, 12, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_SITM, 0, 0), ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 50, 0, MS_GROWL, MZ_SMALL), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, 0, M3_INFRAVISIBLE, CLR_GRAY), + MON("ape", S_YETI, + LVL(4, 12, 6, 0, 0), (G_GENO|G_SGROUP|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), + ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1100, 500, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, M2_STRONG, M3_INFRAVISIBLE, + CLR_BROWN), + MON("owlbear", S_YETI, + LVL(5, 12, 5, 0, 0), (G_GENO|3), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + ATTK(AT_HUGS, AD_PHYS, 2, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 700, 0, MS_ROAR, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, + M2_HOSTILE|M2_STRONG|M2_NASTY, M3_INFRAVISIBLE, CLR_BROWN), + MON("yeti", S_YETI, + LVL(5, 15, 6, 0, 0), (G_GENO|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + ATTK(AT_BITE, AD_PHYS, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1600, 700, 0, MS_GROWL, MZ_LARGE), MR_COLD, MR_COLD, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, M2_HOSTILE|M2_STRONG, + M3_INFRAVISIBLE, CLR_WHITE), + MON("carnivorous ape", S_YETI, + LVL(6, 12, 6, 0, 0), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_HUGS, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1250, 550, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_CARNIVORE, M2_HOSTILE|M2_STRONG, + M3_INFRAVISIBLE, CLR_BLACK), + MON("sasquatch", S_YETI, + LVL(7, 15, 6, 0, 2), (G_GENO|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + ATTK(AT_KICK, AD_PHYS, 1, 8), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1550, 750, 0, MS_GROWL, MZ_LARGE), 0, 0, + M1_ANIMAL|M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, M2_STRONG, + M3_INFRAVISIBLE, CLR_GRAY), +/* + * Zombies + */ + MON("kobold zombie", S_ZOMBIE, + LVL(0, 6, 10, 0, -2), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 50, 0, MS_SILENT, MZ_SMALL), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_STALK|M2_HOSTILE, M3_INFRAVISION, CLR_BROWN), + MON("gnome zombie", S_ZOMBIE, + LVL(1, 6, 10, 0, -2), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 5), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(650, 50, 0, MS_SILENT, MZ_SMALL), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_GNOME, M3_INFRAVISION, CLR_BROWN), + MON("orc zombie", S_ZOMBIE, + LVL(2, 6, 9, 0, -3), (G_GENO|G_SGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(850, 75, 0, MS_SILENT, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_ORC, M3_INFRAVISION, CLR_GRAY), + MON("dwarf zombie", S_ZOMBIE, + LVL(2, 6, 9, 0, -3), (G_GENO|G_SGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 150, 0, MS_SILENT, MZ_HUMAN), MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_DWARF, M3_INFRAVISION, CLR_RED), + MON("elf zombie", S_ZOMBIE, + LVL(3, 6, 9, 0, -3), (G_GENO|G_SGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 7), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 175, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_ELF, M3_INFRAVISION, CLR_GREEN), + MON("human zombie", S_ZOMBIE, + LVL(4, 6, 8, 0, -3), (G_GENO|G_SGROUP|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 200, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_UNDEAD|M2_STALK|M2_HOSTILE, M3_INFRAVISION, HI_DOMESTIC), + MON("ettin zombie", S_ZOMBIE, + LVL(6, 8, 6, 0, -4), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1,10), ATTK(AT_CLAW, AD_PHYS, 1,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1700, 250, 0, MS_SILENT, MZ_HUGE), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_STRONG, M3_INFRAVISION, CLR_BLUE), + MON("giant zombie", S_ZOMBIE, + LVL(8, 8, 6, 0, -4), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2050, 375, 0, MS_SILENT, MZ_HUGE), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_UNDEAD|M2_STALK|M2_HOSTILE|M2_GIANT|M2_STRONG, M3_INFRAVISION, + CLR_CYAN), + MON("ghoul", S_ZOMBIE, + LVL(3, 6, 10, 0, -2), (G_GENO|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PLYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 50, 0, MS_SILENT, MZ_SMALL), + MR_COLD|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_POIS, + M2_UNDEAD|M2_WANDER|M2_HOSTILE, M3_INFRAVISION, CLR_BLACK), + MON("skeleton", S_ZOMBIE, + LVL(12, 8, 4, 0, 0), (G_NOCORPSE|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(300, 5, 0, MS_BONES, MZ_HUMAN), + MR_COLD|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_UNDEAD|M2_WANDER|M2_HOSTILE|M2_STRONG|M2_COLLECT|M2_NASTY, + M3_INFRAVISION, CLR_WHITE), +/* + * golems + */ + MON("straw golem", S_GOLEM, + LVL(3, 12, 10, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 2), ATTK(AT_CLAW, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_HOSTILE|M2_NEUTER, 0, CLR_YELLOW), + MON("paper golem", S_GOLEM, + LVL(3, 12, 10, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(400, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_HOSTILE|M2_NEUTER, 0, HI_PAPER), + MON("rope golem", S_GOLEM, + LVL(4, 9, 8, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_HUGS, AD_PHYS, 6, 1), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(450, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_HOSTILE|M2_NEUTER, 0, CLR_BROWN), + MON("gold golem", S_GOLEM, + LVL(5, 9, 6, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 3), ATTK(AT_CLAW, AD_PHYS, 2, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(450, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON|MR_ACID, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_HOSTILE|M2_NEUTER, 0, HI_GOLD), + MON("leather golem", S_GOLEM, + LVL(6, 6, 6, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 6), ATTK(AT_CLAW, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(800, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_HOSTILE|M2_NEUTER, 0, HI_LEATHER), + MON("wood golem", S_GOLEM, + LVL(7, 3, 4, 0, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(900, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_HOSTILE|M2_NEUTER, 0, HI_WOOD), + MON("flesh golem", S_GOLEM, + LVL(9, 8, 9, 30, 0), (1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1400, 600, 0, MS_SILENT, MZ_LARGE), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID, + M2_HOSTILE|M2_STRONG, 0, CLR_RED), + MON("clay golem", S_GOLEM, + LVL(11, 7, 7, 40, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 3,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1550, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_HOSTILE|M2_STRONG, 0, CLR_BROWN), + MON("stone golem", S_GOLEM, + LVL(14, 6, 5, 50, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 3, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1900, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_HOSTILE|M2_STRONG, 0, CLR_GRAY), + MON("glass golem", S_GOLEM, + LVL(16, 6, 1, 50, 0), (G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 2, 8), ATTK(AT_CLAW, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1800, 0, 0, MS_SILENT, MZ_LARGE), MR_SLEEP|MR_POISON|MR_ACID, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE, + M2_HOSTILE|M2_STRONG, 0, CLR_CYAN), + MON("iron golem", S_GOLEM, + LVL(18, 6, 3, 60, 0), (G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 4,10), ATTK(AT_BREA, AD_DRST, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2000, 0, 0, MS_SILENT, MZ_LARGE), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_BREATHLESS|M1_MINDLESS|M1_HUMANOID|M1_THICK_HIDE|M1_POIS, + M2_HOSTILE|M2_STRONG|M2_COLLECT, 0, HI_METAL), +/* + * humans, including elves and were-critters + */ + MON("human", S_HUMAN, + LVL(0, 12, 10, 0, 0), G_NOGEN, /* for corpses */ + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("wererat", S_HUMAN, + LVL(2, 12, 10, 10, -7), (1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_WERE, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_REGEN|M1_OMNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE|M2_HUMAN|M2_COLLECT, M3_INFRAVISIBLE, + CLR_BROWN), + MON("werejackal", S_HUMAN, + LVL(2, 12, 10, 10, -7), (1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_WERE, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_REGEN|M1_OMNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE|M2_HUMAN|M2_COLLECT, M3_INFRAVISIBLE, + CLR_RED), + MON("werewolf", S_HUMAN, + LVL(5, 12, 10, 20, -7), (1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_WERE, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_REGEN|M1_OMNIVORE, + M2_NOPOLY|M2_WERE|M2_HOSTILE|M2_HUMAN|M2_COLLECT, + M3_INFRAVISIBLE, CLR_ORANGE), + MON("elf", S_HUMAN, + LVL(10, 12, 10, 2, -3), G_NOGEN, /* for corpses */ + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_NOPOLY|M2_ELF|M2_STRONG|M2_COLLECT, M3_INFRAVISION|M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("Woodland-elf", S_HUMAN, + LVL(4, 12, 10, 10, -5), (G_GENO|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_ELF|M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GREEN), + MON("Green-elf", S_HUMAN, + LVL(5, 12, 10, 10, -6), (G_GENO|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_ELF|M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BRIGHT_GREEN), + MON("Grey-elf", S_HUMAN, + LVL(6, 12, 10, 10, -7), (G_GENO|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_ELF|M2_COLLECT, M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), + MON("elf-lord", S_HUMAN, + LVL(8, 12, 10, 20, -9), (G_GENO|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_ELF|M2_STRONG|M2_LORD|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BRIGHT_BLUE), + MON("Elvenking", S_HUMAN, + LVL(9, 12, 10, 25, -10), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_HUMANOID, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS, + M2_ELF|M2_STRONG|M2_PRINCE|M2_MALE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("doppelganger", S_HUMAN, + LVL(9, 12, 5, 20, 0), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 12), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_IMITATE, MZ_HUMAN), MR_SLEEP, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("nurse", S_HUMAN, + LVL(11, 6, 0, 0, 0), (G_GENO|3), + A(ATTK(AT_CLAW, AD_HEAL, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NURSE, MZ_HUMAN), MR_POISON, MR_POISON, + M1_HUMANOID|M1_OMNIVORE, M2_NOPOLY|M2_HUMAN|M2_HOSTILE, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("shopkeeper", S_HUMAN, + LVL(12, 18, 0, 50, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, sizeof(struct eshk), MS_SELL, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("guard", S_HUMAN, + LVL(12, 12, 10, 40, 10), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 4,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, sizeof(struct egd), MS_GUARD, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_PEACEFUL|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_BLUE), + MON("prisoner", S_HUMAN, + LVL(12, 12, 10, 0, 0), G_NOGEN, /* for special levels */ + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_DJINNI, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE|M3_CLOSE, HI_DOMESTIC), + MON("Oracle", S_HUMAN, + LVL(12, 0, 0, 50, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_NONE, AD_MAGM, 0, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_ORACLE, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_FEMALE, M3_INFRAVISIBLE, HI_ZAP), + MON("aligned priest", S_HUMAN, + LVL(12, 12, 10, 50, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 4,10), ATTK(AT_KICK, AD_PHYS, 1, 4), + ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, sizeof(struct epri), MS_PRIEST, MZ_HUMAN), + MR_ELEC, 0, M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_LORD|M2_PEACEFUL|M2_COLLECT, M3_INFRAVISIBLE, + CLR_WHITE), + MON("high priest", S_HUMAN, + LVL(25, 15, 7, 70, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4,10), ATTK(AT_KICK, AD_PHYS, 2, 8), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, sizeof(struct epri), MS_PRIEST, MZ_HUMAN), + MR_FIRE|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MINION|M2_PRINCE|M2_NASTY|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE, CLR_WHITE), + MON("soldier", S_HUMAN, + LVL(6, 10, 10, 0, -2), (G_SGROUP|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_HOSTILE|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_GRAY), + MON("sergeant", S_HUMAN, + LVL(8, 10, 10, 5, -3), (G_SGROUP|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_HOSTILE|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_RED), + MON("lieutenant", S_HUMAN, + LVL(10, 10, 10, 15, -4), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_HOSTILE|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_GREEN), + MON("captain", S_HUMAN, + LVL(12, 10, 10, 15, -5), (G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 4, 4), ATTK(AT_WEAP, AD_PHYS, 4, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_HOSTILE|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_BLUE), + /* Keep these separate - some of the mkroom code assumes that + * all the soldiers are contiguous. + */ + MON("watchman", S_HUMAN, + LVL(6, 10, 10, 0, -2), (G_SGROUP|G_NOGEN|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_PEACEFUL|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_GRAY), + MON("watch captain", S_HUMAN, + LVL(10, 10, 10, 15, -4), (G_NOGEN|G_GENO|1), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_WEAP, AD_PHYS, 3, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SOLDIER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_MERC|M2_STALK|M2_PEACEFUL|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE, CLR_GREEN), + /* Unique humans not tied to quests. + */ + MON("Medusa", S_HUMAN, + LVL(20, 12, 2, 50, -15), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 1, 8), + ATTK(AT_GAZE, AD_STON, 0, 0), ATTK(AT_BITE, AD_DRST, 1, 6), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HISS, MZ_LARGE), + MR_POISON|MR_STONE, MR_POISON|MR_STONE, + M1_FLY|M1_SWIM|M1_AMPHIBIOUS|M1_HUMANOID|M1_POIS|M1_OMNIVORE, + M2_NOPOLY|M2_HOSTILE|M2_STRONG|M2_PNAME|M2_FEMALE, + M3_WAITFORU|M3_INFRAVISIBLE, CLR_BRIGHT_GREEN), + MON("Wizard of Yendor", S_HUMAN, + LVL(30, 12, -8, 100, A_NONE), (G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_SAMU, 2,12), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_CUSS, MZ_HUMAN), + MR_FIRE|MR_POISON, MR_FIRE|MR_POISON, + M1_FLY|M1_BREATHLESS|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT| + M1_TPORT_CNTRL|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_NASTY| + M2_PRINCE|M2_MALE|M2_MAGIC, + M3_COVETOUS|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), + MON("Croesus", S_HUMAN, + LVL(20, 15, 0, 40, 15), (G_UNIQ|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 4,10), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARD, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_PNAME| + M2_PRINCE|M2_MALE|M2_GREEDY|M2_JEWELS|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE, HI_LORD), +#ifdef CHARON + MON("Charon", S_HUMAN, + LVL(76, 18, -5, 120, 0), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_TUCH, AD_PLYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_FERRY, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_POISON|MR_STONE, 0, + M1_BREATHLESS|M1_SEE_INVIS|M1_HUMANOID, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_PNAME|M2_MALE|M2_GREEDY|M2_COLLECT, + M3_INFRAVISIBLE, CLR_WHITE), +#endif +/* + * ghosts + */ + MON("ghost", S_GHOST, + LVL(10, 3, -5, 50, -5), (G_NOCORPSE|G_NOGEN), + A(ATTK(AT_TUCH, AD_PHYS, 1, 1), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 0, 0, MS_SILENT, MZ_HUMAN), + MR_COLD|MR_DISINT|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_WALLWALK|M1_HUMANOID|M1_UNSOLID, + M2_NOPOLY|M2_UNDEAD|M2_STALK|M2_HOSTILE, M3_INFRAVISION, CLR_GRAY), + MON("shade", S_GHOST, + LVL(12, 10, 10, 0, 0), (G_NOCORPSE|G_NOGEN), + A(ATTK(AT_TUCH, AD_PLYS, 2, 6), ATTK(AT_TUCH, AD_SLOW, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 0, 0, MS_WAIL, MZ_HUMAN), + MR_COLD|MR_DISINT|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_BREATHLESS|M1_WALLWALK|M1_HUMANOID|M1_UNSOLID|M1_SEE_INVIS, + M2_NOPOLY|M2_UNDEAD|M2_WANDER|M2_STALK|M2_HOSTILE|M2_NASTY, + M3_INFRAVISION, CLR_BLACK), +/* + * (major) demons + */ + MON("water demon", S_DEMON, + LVL(8, 12,-4, 30, -7), (G_NOCORPSE|G_NOGEN), + A(ATTK(AT_WEAP, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), + ATTK(AT_BITE, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_DJINNI, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_POIS|M1_SWIM, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_BLUE), + /* standard demons & devils + */ + MON("horned devil", S_DEMON, + LVL(6, 9, -5, 50, 11), (G_HELL|G_NOCORPSE|2), + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 2, 3), ATTK(AT_STNG, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_POIS|M1_THICK_HIDE, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_BROWN), +#ifdef SEDUCE +# define SEDUCTION_ATTACKS \ + A(ATTK(AT_BITE, AD_SSEX, 0, 0), ATTK(AT_CLAW, AD_PHYS, 1, 3), \ + ATTK(AT_CLAW, AD_PHYS, 1, 3), NO_ATTK, NO_ATTK, NO_ATTK) +#else +# define SEDUCTION_ATTACKS \ + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), \ + ATTK(AT_BITE, AD_DRLI, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK) +#endif + MON("succubus", S_DEMON, + LVL(6, 12, 0, 70, -9), (G_NOCORPSE|1), + SEDUCTION_ATTACKS, + SIZ(WT_HUMAN, 400, 0, MS_SEDUCE, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_FLY|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_FEMALE, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), + MON("incubus", S_DEMON, + LVL(6, 12, 0, 70, -9), (G_NOCORPSE|1), + SEDUCTION_ATTACKS, + SIZ(WT_HUMAN, 400, 0, MS_SEDUCE, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_FLY|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_MALE, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), +#undef SEDUCTION_ATTACKS + /* Used by AD&D for a type of demon, originally one of the Furies */ + /* and spelled this way */ + MON("erinys", S_DEMON, + LVL(7, 12, 2, 30, 10), (G_HELL|G_NOCORPSE|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_FEMALE| + M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("barbed devil", S_DEMON, + LVL(8, 12, 0, 35, 8), (G_HELL|G_NOCORPSE|G_SGROUP|2), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + ATTK(AT_STNG, AD_PHYS, 3, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_POIS|M1_THICK_HIDE, M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("marilith", S_DEMON, + LVL(7, 12, -6, 80, -12), (G_HELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_WEAP, AD_PHYS, 2, 4), + ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4)), + SIZ(WT_HUMAN, 400, 0, MS_CUSS, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_SLITHY|M1_SEE_INVIS|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_FEMALE|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("vrock", S_DEMON, + LVL(8, 12, 0, 50, -9), (G_HELL|G_NOCORPSE|G_SGROUP|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_CLAW, AD_PHYS, 1, 8), + ATTK(AT_BITE, AD_PHYS, 1, 6), NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_POIS, M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("hezrou", S_DEMON, + LVL(9, 6, -2, 55, -10), (G_HELL|G_NOCORPSE|G_SGROUP|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 3), ATTK(AT_CLAW, AD_PHYS, 1, 3), + ATTK(AT_BITE, AD_PHYS, 4, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_POIS, M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("bone devil", S_DEMON, + LVL(9, 15, -1, 40, -9), (G_HELL|G_NOCORPSE|G_SGROUP|2), + A(ATTK(AT_WEAP, AD_PHYS, 3, 4), ATTK(AT_STNG, AD_DRST, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_POIS, M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), + MON("ice devil", S_DEMON, + LVL(11, 6, -4, 55, -12), (G_HELL|G_NOCORPSE|2), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_STNG, AD_COLD, 3, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), + MR_FIRE|MR_COLD|MR_POISON, 0, M1_SEE_INVIS|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_WHITE), + MON("nalfeshnee", S_DEMON, + LVL(11, 9, -1, 65, -11), (G_HELL|G_NOCORPSE|1), + A(ATTK(AT_CLAW, AD_PHYS, 1, 4), ATTK(AT_CLAW, AD_PHYS, 1, 4), + ATTK(AT_BITE, AD_PHYS, 2, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SPELL, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_HUMANOID|M1_POIS, M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("pit fiend", S_DEMON, + LVL(13, 6, -3, 65, -13), (G_HELL|G_NOCORPSE|2), + A(ATTK(AT_WEAP, AD_PHYS, 4, 2), ATTK(AT_WEAP, AD_PHYS, 4, 2), + ATTK(AT_HUGS, AD_PHYS, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GROWL, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_SEE_INVIS|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + MON("balrog", S_DEMON, + LVL(16, 5, -2, 75, -14), (G_HELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_DEMON|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_RED), + /* Named demon lords & princes plus Arch-Devils. + * (their order matters; see minion.c) + */ + MON("Juiblex", S_DEMON, + LVL(50, 3, -7, 65, -15), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_ENGL, AD_DISE, 4,10), ATTK(AT_SPIT, AD_ACID, 3, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 0, 0, MS_GURGLE, MZ_LARGE), + MR_FIRE|MR_POISON|MR_ACID|MR_STONE, 0, + M1_AMPHIBIOUS|M1_AMORPHOUS|M1_NOHEAD|M1_FLY|M1_SEE_INVIS|M1_ACID| + M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY|M2_LORD| + M2_MALE, + M3_WAITFORU|M3_WANTSAMUL|M3_INFRAVISION, CLR_BRIGHT_GREEN), + MON("Yeenoghu", S_DEMON, + LVL(56, 18, -5, 80, -15), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_WEAP, AD_CONF, 2, 8), + ATTK(AT_CLAW, AD_PLYS, 1, 6), ATTK(AT_MAGC, AD_MAGM, 2, 6), + NO_ATTK, NO_ATTK), + SIZ(900, 500, 0, MS_ORC, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY|M2_LORD| + M2_MALE|M2_COLLECT, + M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Orcus", S_DEMON, + LVL(66, 9, -6, 85, -20), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 4), + ATTK(AT_CLAW, AD_PHYS, 3, 4), ATTK(AT_MAGC, AD_SPEL, 8, 6), + ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK), + SIZ(1500, 500, 0, MS_ORC, MZ_HUGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY|M2_PRINCE| + M2_MALE|M2_COLLECT, + M3_WAITFORU|M3_WANTSBOOK|M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, + HI_LORD), + MON("Geryon", S_DEMON, + LVL(72, 3, -3, 75, 15), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 3, 6), ATTK(AT_CLAW, AD_PHYS, 3, 6), + ATTK(AT_STNG, AD_DRST, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_BRIBE, MZ_HUGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS|M1_SLITHY, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY| + M2_PRINCE|M2_MALE, + M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Dispater", S_DEMON, + LVL(78, 15, -2, 80, 15), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4, 6), ATTK(AT_MAGC, AD_SPEL, 6, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_BRIBE, MZ_HUMAN), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS|M1_HUMANOID, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY| + M2_PRINCE|M2_MALE|M2_COLLECT, + M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Baalzebub", S_DEMON, + LVL(89, 9, -5, 85, 20), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_BITE, AD_DRST, 2, 6), ATTK(AT_GAZE, AD_STUN, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_BRIBE, MZ_LARGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY| + M2_PRINCE|M2_MALE, + M3_WANTSAMUL|M3_WAITFORU|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Asmodeus", S_DEMON, + LVL(105, 12, -7, 90, 20), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 4, 4), ATTK(AT_MAGC, AD_COLD, 6, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_BRIBE, MZ_HUGE), MR_FIRE|MR_COLD|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_HUMANOID|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG| + M2_NASTY|M2_PRINCE|M2_MALE, + M3_WANTSAMUL|M3_WAITFORU|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Demogorgon", S_DEMON, + LVL(106, 15, -8, 95, -20), (G_HELL|G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_MAGC, AD_SPEL, 8, 6), ATTK(AT_STNG, AD_DRLI, 1, 4), + ATTK(AT_CLAW, AD_DISE, 1, 6), ATTK(AT_CLAW, AD_DISE, 1, 6), + NO_ATTK, NO_ATTK), + SIZ(1500, 500, 0, MS_GROWL, MZ_HUGE), MR_FIRE|MR_POISON, 0, + M1_FLY|M1_SEE_INVIS|M1_NOHANDS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_PNAME|M2_NASTY| + M2_PRINCE|M2_MALE, + M3_WANTSAMUL|M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + /* Riders -- the Four Horsemen of the Apocalypse ("War" == player) + */ + MON("Death", S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ|G_NOGEN), + A(ATTK(AT_TUCH, AD_DETH, 8, 8), ATTK(AT_TUCH, AD_DETH, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 1, 0, MS_RIDER, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL, + M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Pestilence", S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ|G_NOGEN), + A(ATTK(AT_TUCH, AD_PEST, 8, 8), ATTK(AT_TUCH, AD_PEST, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 1, 0, MS_RIDER, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL, + M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + MON("Famine", S_DEMON, + LVL(30, 12, -5, 100, 0), (G_UNIQ|G_NOGEN), + A(ATTK(AT_TUCH, AD_FAMN, 8, 8), ATTK(AT_TUCH, AD_FAMN, 8, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 1, 0, MS_RIDER, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_HUMANOID|M1_REGEN|M1_SEE_INVIS|M1_TPORT_CNTRL, + M2_NOPOLY|M2_STALK|M2_HOSTILE|M2_PNAME|M2_STRONG|M2_NASTY, + M3_INFRAVISIBLE|M3_INFRAVISION, HI_LORD), + /* other demons + */ +#ifdef MAIL + MON("mail daemon", S_DEMON, + LVL(56, 24, 10, 127, 0), (G_NOGEN|G_NOCORPSE), + A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(600, 300, 0, MS_SILENT, MZ_HUMAN), + MR_FIRE|MR_COLD|MR_ELEC|MR_SLEEP|MR_POISON|MR_STONE, 0, + M1_FLY|M1_SWIM|M1_BREATHLESS|M1_SEE_INVIS|M1_HUMANOID|M1_POIS, + M2_NOPOLY|M2_STALK|M2_PEACEFUL, M3_INFRAVISIBLE|M3_INFRAVISION, + CLR_BRIGHT_BLUE), +#endif + MON("djinni", S_DEMON, + LVL(7, 12, 4, 30, 0), (G_NOGEN|G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 400, 0, MS_DJINNI, MZ_HUMAN), MR_POISON|MR_STONE, 0, + M1_HUMANOID|M1_FLY|M1_POIS, + M2_NOPOLY|M2_STALK|M2_COLLECT, M3_INFRAVISIBLE, CLR_YELLOW), + MON("sandestin", S_DEMON, + LVL(13, 12, 4, 60, -5), (G_HELL|G_NOCORPSE|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1500, 400, 0, MS_CUSS, MZ_HUMAN), MR_STONE, 0, + M1_HUMANOID, M2_NOPOLY|M2_STALK|M2_STRONG|M2_COLLECT, + M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY), +/* + * sea monsters + */ + MON("jellyfish", S_EEL, + LVL(3, 3, 6, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_STNG, AD_DRST, 3, 3), NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(80, 20, 0, MS_SILENT, MZ_SMALL), MR_POISON, MR_POISON, + M1_SWIM|M1_AMPHIBIOUS|M1_SLITHY|M1_NOLIMBS|M1_NOTAKE|M1_POIS, + M2_HOSTILE, 0, CLR_BLUE), + MON("piranha", S_EEL, + LVL(5, 12, 4, 0, 0), (G_GENO|G_NOGEN|G_SGROUP), + A(ATTK(AT_BITE, AD_PHYS, 2, 6), NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(60, 30, 0, MS_SILENT, MZ_SMALL), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_SLITHY|M1_NOLIMBS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, 0, CLR_RED), + MON("shark", S_EEL, + LVL(7, 12, 2, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_BITE, AD_PHYS, 5, 6), NO_ATTK, + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(500, 350, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_SLITHY|M1_NOLIMBS| + M1_CARNIVORE|M1_OVIPAROUS|M1_THICK_HIDE|M1_NOTAKE, + M2_HOSTILE, 0, CLR_GRAY), + MON("giant eel", S_EEL, + LVL(5, 9, -1, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_BITE, AD_PHYS, 3, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 250, 0, MS_SILENT, MZ_HUGE), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_SLITHY|M1_NOLIMBS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, M3_INFRAVISIBLE, CLR_CYAN), + MON("electric eel", S_EEL, + LVL(7, 10, -3, 0, 0), (G_GENO|G_NOGEN), + A(ATTK(AT_BITE, AD_ELEC, 4, 6), ATTK(AT_TUCH, AD_WRAP, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 250, 0, MS_SILENT, MZ_HUGE), MR_ELEC, MR_ELEC, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_SLITHY|M1_NOLIMBS| + M1_CARNIVORE|M1_OVIPAROUS|M1_NOTAKE, + M2_HOSTILE, M3_INFRAVISIBLE, CLR_BRIGHT_BLUE), + MON("kraken", S_EEL, + LVL(20, 3, 6, 0, -3), (G_GENO|G_NOGEN), + A(ATTK(AT_CLAW, AD_PHYS, 2, 4), ATTK(AT_CLAW, AD_PHYS, 2, 4), + ATTK(AT_HUGS, AD_WRAP, 2, 6), ATTK(AT_BITE, AD_PHYS, 5, 4), + NO_ATTK, NO_ATTK), + SIZ(1800, 1000, 0, MS_SILENT, MZ_HUGE), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_NOPOLY|M2_HOSTILE|M2_STRONG, M3_INFRAVISIBLE, CLR_RED), +/* + * lizards, &c + */ + MON("newt", S_LIZARD, + LVL(0, 6, 8, 0, 0), (G_GENO|5), + A(ATTK(AT_BITE, AD_PHYS, 1, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 20, 0, MS_SILENT, MZ_TINY), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_YELLOW), + MON("gecko", S_LIZARD, + LVL(1, 6, 8, 0, 0), (G_GENO|5), + A(ATTK(AT_BITE, AD_PHYS, 1, 3), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 20, 0, MS_SQEEK, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, 0, CLR_GREEN), + MON("iguana", S_LIZARD, + LVL(2, 6, 7, 0, 0), (G_GENO|5), + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(30, 30, 0, MS_SILENT, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, 0, CLR_BROWN), + MON("baby crocodile", S_LIZARD, + LVL(3, 6, 7, 0, 0), G_GENO, + A(ATTK(AT_BITE, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(200, 200, 0, MS_SILENT, MZ_MEDIUM), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, + M2_HOSTILE, 0, CLR_BROWN), + MON("lizard", S_LIZARD, + LVL(5, 6, 6, 10, 0), (G_GENO|5), + A(ATTK(AT_BITE, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(10, 40, 0, MS_SILENT, MZ_TINY), MR_STONE, MR_STONE, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_HOSTILE, 0, CLR_GREEN), + MON("chameleon", S_LIZARD, + LVL(6, 5, 6, 10, 0), (G_GENO|2), + A(ATTK(AT_BITE, AD_PHYS, 4, 2), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(100, 100, 0, MS_SILENT, MZ_TINY), 0, 0, + M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_NOPOLY|M2_HOSTILE, 0, CLR_BROWN), + MON("crocodile", S_LIZARD, + LVL(6, 9, 5, 0, 0), (G_GENO|1), + A(ATTK(AT_BITE, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 1,12), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_SILENT, MZ_LARGE), 0, 0, + M1_SWIM|M1_AMPHIBIOUS|M1_ANIMAL|M1_THICK_HIDE|M1_NOHANDS| + M1_OVIPAROUS|M1_CARNIVORE, + M2_STRONG|M2_HOSTILE, 0, CLR_BROWN), + MON("salamander", S_LIZARD, + LVL(8, 12, -1, 0, -9), (G_HELL|1), + A(ATTK(AT_WEAP, AD_PHYS, 2, 8), ATTK(AT_TUCH, AD_FIRE, 1, 6), + ATTK(AT_HUGS, AD_PHYS, 2, 6), ATTK(AT_HUGS, AD_FIRE, 3, 6), + NO_ATTK, NO_ATTK), + SIZ(1500, 400, 0, MS_MUMBLE, MZ_HUMAN), MR_SLEEP|MR_FIRE, MR_FIRE, + M1_HUMANOID|M1_SLITHY|M1_THICK_HIDE|M1_POIS, + M2_STALK|M2_HOSTILE|M2_COLLECT|M2_MAGIC, M3_INFRAVISIBLE, CLR_ORANGE), + +/* + * dummy monster needed for visual interface + */ + /* (marking it unique prevents figurines) + */ + MON("long worm tail", S_WORM_TAIL, + LVL(0, 0, 0, 0, 0), (G_NOGEN|G_NOCORPSE|G_UNIQ), + A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, 0, 0), 0, 0, 0L, M2_NOPOLY, 0, CLR_BROWN), + + /* Note: + * Worm tail must be between the normal monsters and the special + * quest & pseudo-character ones because an optimization in the + * random monster selection code assumes everything beyond here + * has the G_NOGEN and M2_NOPOLY attributes. + */ + +/* + * character classes + */ + MON("archeologist", S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_TUNNEL|M1_NEEDPICK|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("barbarian", S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("caveman", S_HUMAN, + LVL(10, 12, 10, 0, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_MALE|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("cavewoman", S_HUMAN, + LVL(10, 12, 10, 0, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("healer", S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("knight", S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), + MON("monk", S_HUMAN, + LVL(10, 12, 10, 2, 0), G_NOGEN, + A(ATTK(AT_CLAW, AD_PHYS, 1, 8), ATTK(AT_KICK, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_HERBIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT|M2_MALE, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("priest", S_HUMAN, + LVL(10, 12, 10, 2, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_MALE|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("priestess", S_HUMAN, + LVL(10, 12, 10, 2, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("ranger", S_HUMAN, + LVL(10, 12, 10, 2, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("rogue", S_HUMAN, + LVL(10, 12, 10, 1, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_GREEDY|M2_JEWELS|M2_COLLECT, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("samurai", S_HUMAN, + LVL(10, 12, 10, 1, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), +#ifdef TOURIST + MON("tourist", S_HUMAN, + LVL(10, 12, 10, 1, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, HI_DOMESTIC), +#endif + MON("valkyrie", S_HUMAN, + LVL(10, 12, 10, 1, -1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), MR_COLD, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_FEMALE|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("wizard", S_HUMAN, + LVL(10, 12, 10, 3, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_COLLECT|M2_MAGIC, M3_INFRAVISIBLE, + HI_DOMESTIC), +/* + * quest leaders + */ + MON("Lord Carnarvon", S_HUMAN, + LVL(20, 12, 0, 30, 20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("Pelias", S_HUMAN, + LVL(20, 12, 0, 30, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("Shaman Karnov", S_HUMAN, + LVL(20, 12, 0, 30, 20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), +#if 0 /* OBSOLETE */ + /* Two for elves - one of each sex. + */ + MON("Earendil", S_HUMAN, + LVL(20, 12, 0, 50, -20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_LEADER, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_ELF|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG| + M2_MALE|M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD), + MON("Elwing", S_HUMAN, + LVL(20, 12, 0, 50, -20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_LEADER, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_ELF|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG| + M2_FEMALE|M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD), +#endif + MON("Hippocrates", S_HUMAN, + LVL(20, 12, 0, 40, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("King Arthur", S_HUMAN, + LVL(20, 12, 0, 40, 20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("Grand Master", S_HUMAN, + LVL(25, 12, 0, 70, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 4, 10), ATTK(AT_KICK, AD_PHYS, 2, 8), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), + MR_FIRE|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_HUMANOID|M1_SEE_INVIS|M1_HERBIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_NASTY|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, CLR_BLACK), + MON("Arch Priest", S_HUMAN, + LVL(25, 12, 7, 70, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4,10), ATTK(AT_KICK, AD_PHYS, 2, 8), + ATTK(AT_MAGC, AD_CLRC, 2, 8), ATTK(AT_MAGC, AD_CLRC, 2, 8), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), + MR_FIRE|MR_ELEC|MR_SLEEP|MR_POISON, 0, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, CLR_WHITE), + MON("Orion", S_HUMAN, + LVL(20, 12, 0, 30, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE|M1_SEE_INVIS|M1_SWIM|M1_AMPHIBIOUS, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD), + /* Note: Master of Thieves is also the Tourist's nemesis. + */ + MON("Master of Thieves", S_HUMAN, + LVL(20, 12, 0, 30, -20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + ATTK(AT_CLAW, AD_SAMU, 2, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_MALE|M2_GREEDY| + M2_JEWELS|M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("Lord Sato", S_HUMAN, + LVL(20, 12, 0, 30, 20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), +#ifdef TOURIST + MON("Twoflower", S_HUMAN, + LVL(20, 12, 10, 20, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_PEACEFUL|M2_STRONG|M2_MALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_DOMESTIC), +#endif + MON("Norn", S_HUMAN, + LVL(20, 12, 0, 80, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), MR_COLD, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_FEMALE| + M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, HI_LORD), + MON("Neferet the Green", S_HUMAN, + LVL(20, 12, 0, 60, 0), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 2, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_LEADER, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_FEMALE|M2_PNAME|M2_PEACEFUL| + M2_STRONG|M2_COLLECT|M2_MAGIC, + M3_CLOSE|M3_INFRAVISIBLE, CLR_GREEN), +/* + * quest nemeses + */ + MON("Minion of Huhetotl", S_DEMON, + LVL(16, 12, -2, 75, -14), (G_NOCORPSE|G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 2, 6), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_LARGE), + MR_FIRE|MR_POISON|MR_STONE, 0, M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_STALK|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_COLLECT, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, CLR_RED), + MON("Thoth Amon", S_HUMAN, + LVL(16, 12, 0, 10, -14), (G_NOGEN|G_UNIQ|G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_POISON|MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_STRONG|M2_MALE|M2_STALK|M2_HOSTILE| + M2_NASTY|M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), + /* Multi-headed, possessing the breath attacks of all the other dragons + * (selected at random when attacking). + */ + MON("Chromatic Dragon", S_DRAGON, + LVL(16, 12, 0, 30, -14), (G_NOGEN|G_UNIQ), + A(ATTK(AT_BREA, AD_RBRE, 6, 8), ATTK(AT_MAGC, AD_SPEL, 0, 0), + ATTK(AT_CLAW, AD_SAMU, 2, 8), ATTK(AT_BITE, AD_PHYS, 4, 8), + ATTK(AT_BITE, AD_PHYS, 4, 8), ATTK(AT_STNG, AD_PHYS, 1, 6)), + SIZ(WT_DRAGON, 1700, 0, MS_NEMESIS, MZ_GIGANTIC), + MR_FIRE|MR_COLD|MR_SLEEP|MR_DISINT|MR_ELEC|MR_POISON|MR_ACID|MR_STONE, + MR_FIRE|MR_COLD|MR_SLEEP|MR_DISINT|MR_ELEC|MR_POISON|MR_STONE, + M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_HOSTILE|M2_FEMALE|M2_STALK|M2_STRONG|M2_NASTY| + M2_GREEDY|M2_JEWELS|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), +#if 0 /* OBSOLETE */ + MON("Goblin King", S_ORC, + LVL(15, 12, 10, 0, -15), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + ATTK(AT_CLAW, AD_SAMU, 1, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(750, 350, 0, MS_NEMESIS, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_ORC|M2_HOSTILE|M2_STRONG|M2_STALK|M2_NASTY|M2_MALE| + M2_GREEDY|M2_JEWELS|M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD), +#endif + MON("Cyclops", S_GIANT, + LVL(18, 12, 0, 0, -15), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 4, 8), ATTK(AT_WEAP, AD_PHYS, 4, 8), + ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(1900, 700, 0, MS_NEMESIS, MZ_HUGE), MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_GIANT|M2_STRONG|M2_ROCKTHROW|M2_STALK|M2_HOSTILE| + M2_NASTY|M2_MALE|M2_JEWELS|M2_COLLECT, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, CLR_GRAY), + MON("Ixoth", S_DRAGON, + LVL(15, 12, -1, 20, -14), (G_NOGEN|G_UNIQ), + A(ATTK(AT_BREA, AD_FIRE, 8, 6), ATTK(AT_BITE, AD_PHYS, 4, 8), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_PHYS, 2, 4), + ATTK(AT_CLAW, AD_SAMU, 2, 4), NO_ATTK ), + SIZ(WT_DRAGON, 1600, 0, MS_NEMESIS, MZ_GIGANTIC), + MR_FIRE|MR_STONE, MR_FIRE, + M1_FLY|M1_THICK_HIDE|M1_NOHANDS|M1_CARNIVORE|M1_SEE_INVIS, + M2_NOPOLY|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_NASTY|M2_STALK| + M2_GREEDY|M2_JEWELS|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, CLR_RED), + MON("Master Kaen", S_HUMAN, + LVL(25, 12, -10, 10, -20), (G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 16, 2), ATTK(AT_CLAW, AD_PHYS, 16, 2), + ATTK(AT_MAGC, AD_CLRC, 0, 0), ATTK(AT_CLAW, AD_SAMU, 1, 4), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), + MR_POISON|MR_STONE, MR_POISON, + M1_HUMANOID|M1_HERBIVORE|M1_SEE_INVIS, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_NASTY| + M2_STALK|M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), + MON("Nalzok", S_DEMON, + LVL(16, 12, -2, 85, -127), (G_NOGEN|G_UNIQ|G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 8, 4), ATTK(AT_WEAP, AD_PHYS, 4, 6), + ATTK(AT_MAGC, AD_SPEL, 0, 0), ATTK(AT_CLAW, AD_SAMU, 2, 6), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_LARGE), + MR_FIRE|MR_POISON|MR_STONE, 0, M1_FLY|M1_SEE_INVIS|M1_POIS, + M2_NOPOLY|M2_DEMON|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_STALK| + M2_NASTY|M2_COLLECT, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, CLR_RED), + MON("Scorpius", S_SPIDER, + LVL(15, 12, 10, 0, -15), (G_NOGEN|G_UNIQ), + A(ATTK(AT_CLAW, AD_PHYS, 2, 6), ATTK(AT_CLAW, AD_SAMU, 2, 6), + ATTK(AT_STNG, AD_DISE, 1, 4), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(750, 350, 0, MS_NEMESIS, MZ_HUMAN), MR_POISON|MR_STONE, MR_POISON, + M1_ANIMAL|M1_NOHANDS|M1_OVIPAROUS|M1_POIS|M1_CARNIVORE, + M2_NOPOLY|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_STALK|M2_NASTY| + M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU, HI_LORD), + MON("Master Assassin", S_HUMAN, + LVL(15, 12, 0, 30, 18), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_DRST, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 8), + ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_HOSTILE|M2_STALK|M2_NASTY| + M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), + /* A renegade daimyo who led a 13 year civil war against the shogun + * of his time. + */ + MON("Ashikaga Takauji", S_HUMAN, + LVL(15, 12, 0, 40, -13), (G_NOGEN|G_UNIQ|G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6), + ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PNAME|M2_HOSTILE|M2_STRONG|M2_STALK| + M2_NASTY|M2_MALE|M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, HI_LORD), +#ifdef TOURIST + /* + * Note: the Master of Thieves was defined above. + */ +#endif + MON("Lord Surtur", S_GIANT, + LVL(15, 12, 2, 50, 12), (G_NOGEN|G_UNIQ), + A(ATTK(AT_WEAP, AD_PHYS, 2,10), ATTK(AT_WEAP, AD_PHYS, 2,10), + ATTK(AT_CLAW, AD_SAMU, 2, 6), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(2250, 850, 0, MS_NEMESIS, MZ_HUGE), MR_FIRE|MR_STONE, MR_FIRE, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_GIANT|M2_MALE|M2_PNAME|M2_HOSTILE|M2_STALK| + M2_STRONG|M2_NASTY|M2_ROCKTHROW|M2_JEWELS|M2_COLLECT, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISION|M3_INFRAVISIBLE, HI_LORD), + MON("Dark One", S_HUMAN, + LVL(15, 12, 0, 80, -10), (G_NOGEN|G_UNIQ|G_NOCORPSE), + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + ATTK(AT_CLAW, AD_SAMU, 1, 4), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_NEMESIS, MZ_HUMAN), MR_STONE, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_STRONG|M2_HOSTILE|M2_STALK|M2_NASTY| + M2_COLLECT|M2_MAGIC, + M3_WANTSARTI|M3_WAITFORU|M3_INFRAVISIBLE, CLR_BLACK), +/* + * quest "guardians" + */ + MON("student", S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_TUNNEL|M1_NEEDPICK|M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("chieftain", S_HUMAN, + LVL(5, 12, 10, 10, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("neanderthal", S_HUMAN, + LVL(5, 12, 10, 10, 1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), +#if 0 /* OBSOLETE */ + MON("High-elf", S_HUMAN, + LVL(5, 12, 10, 10, -7), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 2, 4), ATTK(AT_MAGC, AD_CLRC, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_ELF, 350, 0, MS_GUARDIAN, MZ_HUMAN), MR_SLEEP, MR_SLEEP, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_ELF|M2_PEACEFUL|M2_COLLECT, + M3_INFRAVISION|M3_INFRAVISIBLE, HI_DOMESTIC), +#endif + MON("attendant", S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), MR_POISON, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("page", S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("abbot", S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, + A(ATTK(AT_CLAW, AD_PHYS, 8, 2), ATTK(AT_KICK, AD_STUN, 3, 2), + ATTK(AT_MAGC, AD_CLRC, 0, 0), NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_HERBIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("acolyte", S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_CLRC, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("hunter", S_HUMAN, + LVL(5, 12, 10, 10, -7), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 4), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_SEE_INVIS|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, + M3_INFRAVISION|M3_INFRAVISIBLE, HI_DOMESTIC), + MON("thug", S_HUMAN, + LVL(5, 12, 10, 10, -3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_WEAP, AD_PHYS, 1, 6), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_GREEDY|M2_COLLECT, + M3_INFRAVISIBLE, HI_DOMESTIC), + MON("ninja", S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_HUMANOID, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("roshi", S_HUMAN, + LVL(5, 12, 10, 10, 3), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT, M3_INFRAVISIBLE, + HI_DOMESTIC), +#ifdef TOURIST + MON("guide", S_HUMAN, + LVL(5, 12, 10, 20, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL | M2_STRONG|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE, HI_DOMESTIC), +#endif + MON("warrior", S_HUMAN, + LVL(5, 12, 10, 10, -1), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 8), ATTK(AT_WEAP, AD_PHYS, 1, 8), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT|M2_FEMALE, M3_INFRAVISIBLE, + HI_DOMESTIC), + MON("apprentice", S_HUMAN, + LVL(5, 12, 10, 30, 0), G_NOGEN, + A(ATTK(AT_WEAP, AD_PHYS, 1, 6), ATTK(AT_MAGC, AD_SPEL, 0, 0), + NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(WT_HUMAN, 400, 0, MS_GUARDIAN, MZ_HUMAN), 0, 0, + M1_HUMANOID|M1_OMNIVORE, + M2_NOPOLY|M2_HUMAN|M2_PEACEFUL|M2_STRONG|M2_COLLECT|M2_MAGIC, + M3_INFRAVISIBLE, HI_DOMESTIC), +/* + * array terminator + */ + MON("", 0, + LVL(0, 0, 0, 0, 0), (0), + A(NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK), + SIZ(0, 0, 0, 0, 0), 0, 0, 0L, 0L, 0, 0) +}; +#endif /* !SPLITMON_1 */ + +#ifndef SPLITMON_1 +/* dummy routine used to force linkage */ +void +monst_init() +{ + return; +} +#endif + +/*monst.c*/ diff --git a/src/mplayer.c b/src/mplayer.c new file mode 100644 index 0000000..8a0d08c --- /dev/null +++ b/src/mplayer.c @@ -0,0 +1,340 @@ +/* SCCS Id: @(#)mplayer.c 3.4 1997/02/04 */ +/* Copyright (c) Izchak Miller, 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL const char *NDECL(dev_name); +STATIC_DCL void FDECL(get_mplname, (struct monst *, char *)); +STATIC_DCL void FDECL(mk_mplayer_armor, (struct monst *, SHORT_P)); + +/* These are the names of those who + * contributed to the development of NetHack 3.2/3.3/3.4. + * + * Keep in alphabetical order within teams. + * Same first name is entered once within each team. + */ +static const char *developers[] = { + /* devteam */ + "Dave", "Dean", "Eric", "Izchak", "Janet", "Jessie", + "Ken", "Kevin", "Michael", "Mike", "Pat", "Paul", "Steve", "Timo", + "Warwick", + /* PC team */ + "Bill", "Eric", "Keizo", "Ken", "Kevin", "Michael", "Mike", "Paul", + "Stephen", "Steve", "Timo", "Yitzhak", + /* Amiga team */ + "Andy", "Gregg", "Janne", "Keni", "Mike", "Olaf", "Richard", + /* Mac team */ + "Andy", "Chris", "Dean", "Jon", "Jonathan", "Kevin", "Wang", + /* Atari team */ + "Eric", "Marvin", "Warwick", + /* NT team */ + "Alex", "Dion", "Michael", + /* OS/2 team */ + "Helge", "Ron", "Timo", + /* VMS team */ + "Joshua", "Pat", + ""}; + + +/* return a randomly chosen developer name */ +STATIC_OVL const char * +dev_name() +{ + register int i, m = 0, n = SIZE(developers); + register struct monst *mtmp; + register boolean match; + + do { + match = FALSE; + i = rn2(n); + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if(!is_mplayer(mtmp->data)) continue; + if(!strncmp(developers[i], NAME(mtmp), + strlen(developers[i]))) { + match = TRUE; + break; + } + } + m++; + } while (match && m < 100); /* m for insurance */ + + if (match) return (const char *)0; + return(developers[i]); +} + +STATIC_OVL void +get_mplname(mtmp, nam) +register struct monst *mtmp; +char *nam; +{ + boolean fmlkind = is_female(mtmp->data); + const char *devnam; + + devnam = dev_name(); + if (!devnam) + Strcpy(nam, fmlkind ? "Eve" : "Adam"); + else if (fmlkind && !!strcmp(devnam, "Janet")) + Strcpy(nam, rn2(2) ? "Maud" : "Eve"); + else Strcpy(nam, devnam); + + if (fmlkind || !strcmp(nam, "Janet")) + mtmp->female = 1; + else + mtmp->female = 0; + Strcat(nam, " the "); + Strcat(nam, rank_of((int)mtmp->m_lev, + monsndx(mtmp->data), + (boolean)mtmp->female)); +} + +STATIC_OVL void +mk_mplayer_armor(mon, typ) +struct monst *mon; +short typ; +{ + struct obj *obj; + + if (typ == STRANGE_OBJECT) return; + obj = mksobj(typ, FALSE, FALSE); + if (!rn2(3)) obj->oerodeproof = 1; + if (!rn2(3)) curse(obj); + if (!rn2(3)) bless(obj); + /* Most players who get to the endgame who have cursed equipment + * have it because the wizard or other monsters cursed it, so its + * chances of having plusses is the same as usual.... + */ + obj->spe = rn2(10) ? (rn2(3) ? rn2(5) : rn1(4,4)) : -rnd(3); + (void) mpickobj(mon, obj); +} + +struct monst * +mk_mplayer(ptr, x, y, special) +register struct permonst *ptr; +xchar x, y; +register boolean special; +{ + register struct monst *mtmp; + char nam[PL_NSIZ]; + + if(!is_mplayer(ptr)) + return((struct monst *)0); + + if(MON_AT(x, y)) + (void) rloc(m_at(x, y), FALSE); /* insurance */ + + if(!In_endgame(&u.uz)) special = FALSE; + + if ((mtmp = makemon(ptr, x, y, NO_MM_FLAGS)) != 0) { + short weapon = rn2(2) ? LONG_SWORD : rnd_class(SPEAR, BULLWHIP); + short armor = rnd_class(GRAY_DRAGON_SCALE_MAIL, YELLOW_DRAGON_SCALE_MAIL); + short cloak = !rn2(8) ? STRANGE_OBJECT : + rnd_class(OILSKIN_CLOAK, CLOAK_OF_DISPLACEMENT); + short helm = !rn2(8) ? STRANGE_OBJECT : + rnd_class(ELVEN_LEATHER_HELM, HELM_OF_TELEPATHY); + short shield = !rn2(8) ? STRANGE_OBJECT : + rnd_class(ELVEN_SHIELD, SHIELD_OF_REFLECTION); + int quan; + struct obj *otmp; + + mtmp->m_lev = (special ? rn1(16,15) : rnd(16)); + mtmp->mhp = mtmp->mhpmax = d((int)mtmp->m_lev,10) + + (special ? (30 + rnd(30)) : 30); + if(special) { + get_mplname(mtmp, nam); + mtmp = christen_monst(mtmp, nam); + /* that's why they are "stuck" in the endgame :-) */ + (void)mongets(mtmp, FAKE_AMULET_OF_YENDOR); + } + mtmp->mpeaceful = 0; + set_malign(mtmp); /* peaceful may have changed again */ + + switch(monsndx(ptr)) { + case PM_ARCHEOLOGIST: + if (rn2(2)) weapon = BULLWHIP; + break; + case PM_BARBARIAN: + if (rn2(2)) { + weapon = rn2(2) ? TWO_HANDED_SWORD : BATTLE_AXE; + shield = STRANGE_OBJECT; + } + if (rn2(2)) armor = rnd_class(PLATE_MAIL, CHAIN_MAIL); + if (helm == HELM_OF_BRILLIANCE) helm = STRANGE_OBJECT; + break; + case PM_CAVEMAN: + case PM_CAVEWOMAN: + if (rn2(4)) weapon = MACE; + else if (rn2(2)) weapon = CLUB; + if (helm == HELM_OF_BRILLIANCE) helm = STRANGE_OBJECT; + break; + case PM_HEALER: + if (rn2(4)) weapon = QUARTERSTAFF; + else if (rn2(2)) weapon = rn2(2) ? UNICORN_HORN : SCALPEL; + if (rn2(4)) helm = rn2(2) ? HELM_OF_BRILLIANCE : HELM_OF_TELEPATHY; + if (rn2(2)) shield = STRANGE_OBJECT; + break; + case PM_KNIGHT: + if (rn2(4)) weapon = LONG_SWORD; + if (rn2(2)) armor = rnd_class(PLATE_MAIL, CHAIN_MAIL); + break; + case PM_MONK: + weapon = STRANGE_OBJECT; + armor = STRANGE_OBJECT; + cloak = ROBE; + if (rn2(2)) shield = STRANGE_OBJECT; + break; + case PM_PRIEST: + case PM_PRIESTESS: + if (rn2(2)) weapon = MACE; + if (rn2(2)) armor = rnd_class(PLATE_MAIL, CHAIN_MAIL); + if (rn2(4)) cloak = ROBE; + if (rn2(4)) helm = rn2(2) ? HELM_OF_BRILLIANCE : HELM_OF_TELEPATHY; + if (rn2(2)) shield = STRANGE_OBJECT; + break; + case PM_RANGER: + if (rn2(2)) weapon = ELVEN_DAGGER; + break; + case PM_ROGUE: + if (rn2(2)) weapon = SHORT_SWORD; + break; + case PM_SAMURAI: + if (rn2(2)) weapon = KATANA; + break; +#ifdef TOURIST + case PM_TOURIST: + /* Defaults are just fine */ + break; +#endif + case PM_VALKYRIE: + if (rn2(2)) weapon = WAR_HAMMER; + if (rn2(2)) armor = rnd_class(PLATE_MAIL, CHAIN_MAIL); + break; + case PM_WIZARD: + if (rn2(4)) weapon = rn2(2) ? QUARTERSTAFF : ATHAME; + if (rn2(2)) { + armor = rn2(2) ? BLACK_DRAGON_SCALE_MAIL : + SILVER_DRAGON_SCALE_MAIL; + cloak = CLOAK_OF_MAGIC_RESISTANCE; + } + if (rn2(4)) helm = HELM_OF_BRILLIANCE; + shield = STRANGE_OBJECT; + break; + default: impossible("bad mplayer monster"); + weapon = 0; + break; + } + + if (weapon != STRANGE_OBJECT) { + otmp = mksobj(weapon, TRUE, FALSE); + otmp->spe = (special ? rn1(5,4) : rn2(4)); + if (!rn2(3)) otmp->oerodeproof = 1; + else if (!rn2(2)) otmp->greased = 1; + if (special && rn2(2)) + otmp = mk_artifact(otmp, A_NONE); + /* mplayers knew better than to overenchant Magicbane */ + if (otmp->oartifact == ART_MAGICBANE) + otmp->spe = rnd(4); + (void) mpickobj(mtmp, otmp); + } + + if(special) { + if (!rn2(10)) + (void) mongets(mtmp, rn2(3) ? LUCKSTONE : LOADSTONE); + mk_mplayer_armor(mtmp, armor); + mk_mplayer_armor(mtmp, cloak); + mk_mplayer_armor(mtmp, helm); + mk_mplayer_armor(mtmp, shield); + if (rn2(8)) + mk_mplayer_armor(mtmp, rnd_class(LEATHER_GLOVES, + GAUNTLETS_OF_DEXTERITY)); + if (rn2(8)) + mk_mplayer_armor(mtmp, rnd_class(LOW_BOOTS, LEVITATION_BOOTS)); + m_dowear(mtmp, TRUE); + + quan = rn2(3) ? rn2(3) : rn2(16); + while(quan--) + (void)mongets(mtmp, rnd_class(DILITHIUM_CRYSTAL, JADE)); + /* To get the gold "right" would mean a player can double his */ + /* gold supply by killing one mplayer. Not good. */ +#ifndef GOLDOBJ + mtmp->mgold = rn2(1000); +#else + mkmonmoney(mtmp, rn2(1000)); +#endif + quan = rn2(10); + while(quan--) + (void) mpickobj(mtmp, mkobj(RANDOM_CLASS, FALSE)); + } + quan = rnd(3); + while(quan--) + (void)mongets(mtmp, rnd_offensive_item(mtmp)); + quan = rnd(3); + while(quan--) + (void)mongets(mtmp, rnd_defensive_item(mtmp)); + quan = rnd(3); + while(quan--) + (void)mongets(mtmp, rnd_misc_item(mtmp)); + } + + return(mtmp); +} + +/* create the indicated number (num) of monster-players, + * randomly chosen, and in randomly chosen (free) locations + * on the level. If "special", the size of num should not + * be bigger than the number of _non-repeated_ names in the + * developers array, otherwise a bunch of Adams and Eves will + * fill up the overflow. + */ +void +create_mplayers(num, special) +register int num; +boolean special; +{ + int pm, x, y; + struct monst fakemon; + + while(num) { + int tryct = 0; + + /* roll for character class */ + pm = PM_ARCHEOLOGIST + rn2(PM_WIZARD - PM_ARCHEOLOGIST + 1); + fakemon.data = &mons[pm]; + + /* roll for an available location */ + do { + x = rn1(COLNO-4, 2); + y = rnd(ROWNO-2); + } while(!goodpos(x, y, &fakemon, 0) && tryct++ <= 50); + + /* if pos not found in 50 tries, don't bother to continue */ + if(tryct > 50) return; + + (void) mk_mplayer(&mons[pm], (xchar)x, (xchar)y, special); + num--; + } +} + +void +mplayer_talk(mtmp) +register struct monst *mtmp; +{ + static const char *same_class_msg[3] = { + "I can't win, and neither will you!", + "You don't deserve to win!", + "Mine should be the honor, not yours!", + }, *other_class_msg[3] = { + "The low-life wants to talk, eh?", + "Fight, scum!", + "Here is what I have to say!", + }; + + if(mtmp->mpeaceful) return; /* will drop to humanoid talk */ + + pline("Talk? -- %s", + (mtmp->data == &mons[urole.malenum] || + mtmp->data == &mons[urole.femalenum]) ? + same_class_msg[rn2(3)] : other_class_msg[rn2(3)]); +} + +/*mplayer.c*/ diff --git a/src/mthrowu.c b/src/mthrowu.c new file mode 100644 index 0000000..3038ced --- /dev/null +++ b/src/mthrowu.c @@ -0,0 +1,824 @@ +/* SCCS Id: @(#)mthrowu.c 3.4 2003/05/09 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL int FDECL(drop_throw,(struct obj *,BOOLEAN_P,int,int)); + +#define URETREATING(x,y) (distmin(u.ux,u.uy,x,y) > distmin(u.ux0,u.uy0,x,y)) + +#define POLE_LIM 5 /* How far monsters can use pole-weapons */ + +#ifndef OVLB + +STATIC_DCL const char *breathwep[]; + +#else /* OVLB */ + +/* + * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h. + */ +STATIC_OVL NEARDATA const char *breathwep[] = { + "fragments", + "fire", + "frost", + "sleep gas", + "a disintegration blast", + "lightning", + "poison gas", + "acid", + "strange breath #8", + "strange breath #9" +}; + +/* hero is hit by something other than a monster */ +int +thitu(tlev, dam, obj, name) +int tlev, dam; +struct obj *obj; +const char *name; /* if null, then format `obj' */ +{ + const char *onm, *knm; + boolean is_acid; + int kprefix = KILLED_BY_AN; + char onmbuf[BUFSZ], knmbuf[BUFSZ]; + + if (!name) { + if (!obj) panic("thitu: name & obj both null?"); + name = strcpy(onmbuf, + (obj->quan > 1L) ? doname(obj) : mshot_xname(obj)); + knm = strcpy(knmbuf, killer_xname(obj)); + kprefix = KILLED_BY; /* killer_name supplies "an" if warranted */ + } else { + knm = name; + /* [perhaps ought to check for plural here to] */ + if (!strncmpi(name, "the ", 4) || + !strncmpi(name, "an ", 3) || + !strncmpi(name, "a ", 2)) kprefix = KILLED_BY; + } + onm = (obj && obj_is_pname(obj)) ? the(name) : + (obj && obj->quan > 1L) ? name : an(name); + is_acid = (obj && obj->otyp == ACID_VENOM); + + if(u.uac + tlev <= rnd(20)) { + if(Blind || !flags.verbose) pline("It misses."); + else You("are almost hit by %s.", onm); + return(0); + } else { + if(Blind || !flags.verbose) You("are hit!"); + else You("are hit by %s%s", onm, exclam(dam)); + + if (obj && objects[obj->otyp].oc_material == SILVER + && hates_silver(youmonst.data)) { + dam += rnd(20); + pline_The("silver sears your flesh!"); + exercise(A_CON, FALSE); + } + if (is_acid && Acid_resistance) + pline("It doesn't seem to hurt you."); + else { + if (is_acid) pline("It burns!"); + if (Half_physical_damage) dam = (dam+1) / 2; + losehp(dam, knm, kprefix); + exercise(A_STR, FALSE); + } + return(1); + } +} + +/* Be sure this corresponds with what happens to player-thrown objects in + * dothrow.c (for consistency). --KAA + * Returns 0 if object still exists (not destroyed). + */ + +STATIC_OVL int +drop_throw(obj, ohit, x, y) +register struct obj *obj; +boolean ohit; +int x,y; +{ + int retvalu = 1; + int create; + struct monst *mtmp; + struct trap *t; + + if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS || + (ohit && obj->otyp == EGG)) + create = 0; + else if (ohit && (is_multigen(obj) || obj->otyp == ROCK)) + create = !rn2(3); + else create = 1; + + if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) && + (t = t_at(x, y)) && ((t->ttyp == PIT) || + (t->ttyp == SPIKED_PIT)))) { + int objgone = 0; + + if (down_gate(x, y) != -1) + objgone = ship_object(obj, x, y, FALSE); + if (!objgone) { + if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */ + place_object(obj, x, y); + if (!mtmp && x == u.ux && y == u.uy) + mtmp = &youmonst; + if (mtmp && ohit) + passive_obj(mtmp, obj, (struct attack *)0); + stackobj(obj); + retvalu = 0; + } + } + } else obfree(obj, (struct obj*) 0); + return retvalu; +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* an object launched by someone/thing other than player attacks a monster; + return 1 if the object has stopped moving (hit or its range used up) */ +int +ohitmon(mtmp, otmp, range, verbose) +struct monst *mtmp; /* accidental target */ +struct obj *otmp; /* missile; might be destroyed by drop_throw */ +int range; /* how much farther will object travel if it misses */ + /* Use -1 to signify to keep going even after hit, */ + /* unless its gone (used for rolling_boulder_traps) */ +boolean verbose; /* give message(s) even when you can't see what happened */ +{ + int damage, tmp; + boolean vis, ismimic; + int objgone = 1; + + ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER; + vis = cansee(bhitpos.x, bhitpos.y); + + tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE); + if (tmp < rnd(20)) { + if (!ismimic) { + if (vis) miss(distant_name(otmp, mshot_xname), mtmp); + else if (verbose) pline("It is missed."); + } + if (!range) { /* Last position; object drops */ + (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my); + return 1; + } + } else if (otmp->oclass == POTION_CLASS) { + if (ismimic) seemimic(mtmp); + mtmp->msleeping = 0; + if (vis) otmp->dknown = 1; + potionhit(mtmp, otmp, FALSE); + return 1; + } else { + damage = dmgval(otmp, mtmp); + if (otmp->otyp == ACID_VENOM && resists_acid(mtmp)) + damage = 0; + if (ismimic) seemimic(mtmp); + mtmp->msleeping = 0; + if (vis) hit(distant_name(otmp,mshot_xname), mtmp, exclam(damage)); + else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage)); + + if (otmp->opoisoned && is_poisonable(otmp)) { + if (resists_poison(mtmp)) { + if (vis) pline_The("poison doesn't seem to affect %s.", + mon_nam(mtmp)); + } else { + if (rn2(30)) { + damage += rnd(6); + } else { + if (vis) pline_The("poison was deadly..."); + damage = mtmp->mhp; + } + } + } + if (objects[otmp->otyp].oc_material == SILVER && + hates_silver(mtmp->data)) { + if (vis) pline_The("silver sears %s flesh!", + s_suffix(mon_nam(mtmp))); + else if (verbose) pline("Its flesh is seared!"); + } + if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) { + if (resists_acid(mtmp)) { + if (vis || verbose) + pline("%s is unaffected.", Monnam(mtmp)); + damage = 0; + } else { + if (vis) pline_The("acid burns %s!", mon_nam(mtmp)); + else if (verbose) pline("It is burned!"); + } + } + mtmp->mhp -= damage; + if (mtmp->mhp < 1) { + if (vis || verbose) + pline("%s is %s!", Monnam(mtmp), + (nonliving(mtmp->data) || !canspotmon(mtmp)) + ? "destroyed" : "killed"); + /* don't blame hero for unknown rolling boulder trap */ + if (!flags.mon_moving && + (otmp->otyp != BOULDER || range >= 0 || !otmp->otrapped)) + xkilled(mtmp,0); + else mondied(mtmp); + } + + if (can_blnd((struct monst*)0, mtmp, + (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP), + otmp)) { + if (vis && mtmp->mcansee) + pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp))); + mtmp->mcansee = 0; + tmp = (int)mtmp->mblinded + rnd(25) + 20; + if (tmp > 127) tmp = 127; + mtmp->mblinded = tmp; + } + + objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y); + if (!objgone && range == -1) { /* special case */ + obj_extract_self(otmp); /* free it for motion again */ + return 0; + } + return 1; + } + return 0; +} + +void +m_throw(mon, x, y, dx, dy, range, obj) + register struct monst *mon; + register int x,y,dx,dy,range; /* direction and range */ + register struct obj *obj; +{ + register struct monst *mtmp; + struct obj *singleobj; + char sym = obj->oclass; + int hitu, blindinc = 0; + + bhitpos.x = x; + bhitpos.y = y; + + if (obj->quan == 1L) { + /* + * Remove object from minvent. This cannot be done later on; + * what if the player dies before then, leaving the monster + * with 0 daggers? (This caused the infamous 2^32-1 orcish + * dagger bug). + * + * VENOM is not in minvent - it should already be OBJ_FREE. + * The extract below does nothing. + */ + + /* not possibly_unwield, which checks the object's */ + /* location, not its existence */ + if (MON_WEP(mon) == obj) { + setmnotwielded(mon,obj); + MON_NOWEP(mon); + } + obj_extract_self(obj); + singleobj = obj; + obj = (struct obj *) 0; + } else { + singleobj = splitobj(obj, 1L); + obj_extract_self(singleobj); + } + + singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */ + + if (singleobj->cursed && (dx || dy) && !rn2(7)) { + if(canseemon(mon) && flags.verbose) { + if(is_ammo(singleobj)) + pline("%s misfires!", Monnam(mon)); + else + pline("%s as %s throws it!", + Tobjnam(singleobj, "slip"), mon_nam(mon)); + } + dx = rn2(3)-1; + dy = rn2(3)-1; + /* check validity of new direction */ + if (!dx && !dy) { + (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); + return; + } + } + + /* pre-check for doors, walls and boundaries. + Also need to pre-check for bars regardless of direction; + the random chance for small objects hitting bars is + skipped when reaching them at point blank range */ + if (!isok(bhitpos.x+dx,bhitpos.y+dy) + || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ) + || closed_door(bhitpos.x+dx, bhitpos.y+dy) + || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS && + hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) { + (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); + return; + } + + /* Note: drop_throw may destroy singleobj. Since obj must be destroyed + * early to avoid the dagger bug, anyone who modifies this code should + * be careful not to use either one after it's been freed. + */ + if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj)); + while(range-- > 0) { /* Actually the loop is always exited by break */ + bhitpos.x += dx; + bhitpos.y += dy; + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { + if (ohitmon(mtmp, singleobj, range, TRUE)) + break; + } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { + if (multi) nomul(0); + + if (singleobj->oclass == GEM_CLASS && + singleobj->otyp <= LAST_GEM+9 /* 9 glass colors */ + && is_unicorn(youmonst.data)) { + if (singleobj->otyp > LAST_GEM) { + You("catch the %s.", xname(singleobj)); + You("are not interested in %s junk.", + s_suffix(mon_nam(mon))); + makeknown(singleobj->otyp); + dropy(singleobj); + } else { + You("accept %s gift in the spirit in which it was intended.", + s_suffix(mon_nam(mon))); + (void)hold_another_object(singleobj, + "You catch, but drop, %s.", xname(singleobj), + "You catch:"); + } + break; + } + if (singleobj->oclass == POTION_CLASS) { + if (!Blind) singleobj->dknown = 1; + potionhit(&youmonst, singleobj, FALSE); + break; + } + switch(singleobj->otyp) { + int dam, hitv; + case EGG: + if (!touch_petrifies(&mons[singleobj->corpsenm])) { + impossible("monster throwing egg type %d", + singleobj->corpsenm); + hitu = 0; + break; + } + /* fall through */ + case CREAM_PIE: + case BLINDING_VENOM: + hitu = thitu(8, 0, singleobj, (char *)0); + break; + default: + dam = dmgval(singleobj, &youmonst); + hitv = 3 - distmin(u.ux,u.uy, mon->mx,mon->my); + if (hitv < -4) hitv = -4; + if (is_elf(mon->data) && + objects[singleobj->otyp].oc_skill == P_BOW) { + hitv++; + if (MON_WEP(mon) && + MON_WEP(mon)->otyp == ELVEN_BOW) + hitv++; + if(singleobj->otyp == ELVEN_ARROW) dam++; + } + if (bigmonst(youmonst.data)) hitv++; + hitv += 8 + singleobj->spe; + if (dam < 1) dam = 1; + hitu = thitu(hitv, dam, singleobj, (char *)0); + } + if (hitu && singleobj->opoisoned && + is_poisonable(singleobj)) { + char onmbuf[BUFSZ], knmbuf[BUFSZ]; + + Strcpy(onmbuf, xname(singleobj)); + Strcpy(knmbuf, killer_xname(singleobj)); + poisoned(onmbuf, A_STR, knmbuf, -10); + } + if(hitu && + can_blnd((struct monst*)0, &youmonst, + (uchar)(singleobj->otyp == BLINDING_VENOM ? + AT_SPIT : AT_WEAP), singleobj)) { + blindinc = rnd(25); + if(singleobj->otyp == CREAM_PIE) { + if(!Blind) pline("Yecch! You've been creamed."); + else pline("There's %s sticky all over your %s.", + something, + body_part(FACE)); + } else if(singleobj->otyp == BLINDING_VENOM) { + int num_eyes = eyecount(youmonst.data); + /* venom in the eyes */ + if(!Blind) pline_The("venom blinds you."); + else Your("%s sting%s.", + (num_eyes == 1) ? body_part(EYE) : + makeplural(body_part(EYE)), + (num_eyes == 1) ? "s" : ""); + } + } + if (hitu && singleobj->otyp == EGG) { + if (!Stone_resistance + && !(poly_when_stoned(youmonst.data) && + polymon(PM_STONE_GOLEM))) { + Stoned = 5; + killer = (char *) 0; + } + } + stop_occupation(); + if (hitu || !range) { + (void) drop_throw(singleobj, hitu, u.ux, u.uy); + break; + } + } else if (!range /* reached end of path */ + /* missile hits edge of screen */ + || !isok(bhitpos.x+dx,bhitpos.y+dy) + /* missile hits the wall */ + || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ) + /* missile hit closed door */ + || closed_door(bhitpos.x+dx, bhitpos.y+dy) + /* missile might hit iron bars */ + || (levl[bhitpos.x+dx][bhitpos.y+dy].typ == IRONBARS && + hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0)) +#ifdef SINKS + /* Thrown objects "sink" */ + || IS_SINK(levl[bhitpos.x][bhitpos.y].typ) +#endif + ) { + if (singleobj) /* hits_bars might have destroyed it */ + (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); + break; + } + tmp_at(bhitpos.x, bhitpos.y); + delay_output(); + } + tmp_at(bhitpos.x, bhitpos.y); + delay_output(); + tmp_at(DISP_END, 0); + + if (blindinc) { + u.ucreamed += blindinc; + make_blinded(Blinded + (long)blindinc, FALSE); + if (!Blind) Your(vision_clears); + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* Remove an item from the monster's inventory and destroy it. */ +void +m_useup(mon, obj) +struct monst *mon; +struct obj *obj; +{ + if (obj->quan > 1L) { + obj->quan--; + obj->owt = weight(obj); + } else { + obj_extract_self(obj); + possibly_unwield(mon, FALSE); + if (obj->owornmask) { + mon->misc_worn_check &= ~obj->owornmask; + update_mon_intrinsics(mon, obj, FALSE, FALSE); + } + obfree(obj, (struct obj*) 0); + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* monster attempts ranged weapon attack against player */ +void +thrwmu(mtmp) +struct monst *mtmp; +{ + struct obj *otmp, *mwep; + xchar x, y; + schar skill; + int multishot; + const char *onm; + + /* Rearranged beginning so monsters can use polearms not in a line */ + if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) { + mtmp->weapon_check = NEED_RANGED_WEAPON; + /* mon_wield_item resets weapon_check as appropriate */ + if(mon_wield_item(mtmp) != 0) return; + } + + /* Pick a weapon */ + otmp = select_rwep(mtmp); + if (!otmp) return; + + if (is_pole(otmp)) { + int dam, hitv; + + if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM || + !couldsee(mtmp->mx, mtmp->my)) + return; /* Out of range, or intervening wall */ + + if (canseemon(mtmp)) { + onm = xname(otmp); + pline("%s thrusts %s.", Monnam(mtmp), + obj_is_pname(otmp) ? the(onm) : an(onm)); + } + + dam = dmgval(otmp, &youmonst); + hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my); + if (hitv < -4) hitv = -4; + if (bigmonst(youmonst.data)) hitv++; + hitv += 8 + otmp->spe; + if (dam < 1) dam = 1; + + (void) thitu(hitv, dam, otmp, (char *)0); + stop_occupation(); + return; + } + + x = mtmp->mx; + y = mtmp->my; + /* If you are coming toward the monster, the monster + * should try to soften you up with missiles. If you are + * going away, you are probably hurt or running. Give + * chase, but if you are getting too far away, throw. + */ + if (!lined_up(mtmp) || + (URETREATING(x,y) && + rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy)))) + return; + + skill = objects[otmp->otyp].oc_skill; + mwep = MON_WEP(mtmp); /* wielded weapon */ + + /* Multishot calculations */ + multishot = 1; + if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER || + skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) { + /* Assumes lords are skilled, princes are expert */ + if (is_prince(mtmp->data)) multishot += 2; + else if (is_lord(mtmp->data)) multishot++; + + switch (monsndx(mtmp->data)) { + case PM_RANGER: + multishot++; + break; + case PM_ROGUE: + if (skill == P_DAGGER) multishot++; + break; + case PM_NINJA: + case PM_SAMURAI: + if (otmp->otyp == YA && mwep && + mwep->otyp == YUMI) multishot++; + break; + default: + break; + } + /* racial bonus */ + if ((is_elf(mtmp->data) && + otmp->otyp == ELVEN_ARROW && + mwep && mwep->otyp == ELVEN_BOW) || + (is_orc(mtmp->data) && + otmp->otyp == ORCISH_ARROW && + mwep && mwep->otyp == ORCISH_BOW)) + multishot++; + + if ((long)multishot > otmp->quan) multishot = (int)otmp->quan; + if (multishot < 1) multishot = 1; + else multishot = rnd(multishot); + } + + if (canseemon(mtmp)) { + char onmbuf[BUFSZ]; + + if (multishot > 1) { + /* "N arrows"; multishot > 1 implies otmp->quan > 1, so + xname()'s result will already be pluralized */ + Sprintf(onmbuf, "%d %s", multishot, xname(otmp)); + onm = onmbuf; + } else { + /* "an arrow" */ + onm = singular(otmp, xname); + onm = obj_is_pname(otmp) ? the(onm) : an(onm); + } + m_shot.s = ammo_and_launcher(otmp,mwep) ? TRUE : FALSE; + pline("%s %s %s!", Monnam(mtmp), + m_shot.s ? "shoots" : "throws", onm); + m_shot.o = otmp->otyp; + } else { + m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */ + } + + m_shot.n = multishot; + for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) + m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), + distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp); + m_shot.n = m_shot.i = 0; + m_shot.o = STRANGE_OBJECT; + m_shot.s = FALSE; + + nomul(0); +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +spitmu(mtmp, mattk) /* monster spits substance at you */ +register struct monst *mtmp; +register struct attack *mattk; +{ + register struct obj *otmp; + + if(mtmp->mcan) { + + if(flags.soundok) + pline("A dry rattle comes from %s throat.", + s_suffix(mon_nam(mtmp))); + return 0; + } + if(lined_up(mtmp)) { + switch (mattk->adtyp) { + case AD_BLND: + case AD_DRST: + otmp = mksobj(BLINDING_VENOM, TRUE, FALSE); + break; + default: + impossible("bad attack type in spitmu"); + /* fall through */ + case AD_ACID: + otmp = mksobj(ACID_VENOM, TRUE, FALSE); + break; + } + if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) { + if (canseemon(mtmp)) + pline("%s spits venom!", Monnam(mtmp)); + m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), + distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp); + nomul(0); + return 0; + } + } + return 0; +} + +#endif /* OVLB */ +#ifdef OVL1 + +int +breamu(mtmp, mattk) /* monster breathes at you (ranged) */ + register struct monst *mtmp; + register struct attack *mattk; +{ + /* if new breath types are added, change AD_ACID to max type */ + int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ; + + if(lined_up(mtmp)) { + + if(mtmp->mcan) { + if(flags.soundok) { + if(canseemon(mtmp)) + pline("%s coughs.", Monnam(mtmp)); + else + You_hear("a cough."); + } + return(0); + } + if(!mtmp->mspec_used && rn2(3)) { + + if((typ >= AD_MAGM) && (typ <= AD_ACID)) { + + if(canseemon(mtmp)) + pline("%s breathes %s!", Monnam(mtmp), + breathwep[typ-1]); + buzz((int) (-20 - (typ-1)), (int)mattk->damn, + mtmp->mx, mtmp->my, sgn(tbx), sgn(tby)); + nomul(0); + /* breath runs out sometimes. Also, give monster some + * cunning; don't breath if the player fell asleep. + */ + if(!rn2(3)) + mtmp->mspec_used = 10+rn2(20); + if(typ == AD_SLEE && !Sleep_resistance) + mtmp->mspec_used += rnd(20); + } else impossible("Breath weapon %d used", typ-1); + } + } + return(1); +} + +boolean +linedup(ax, ay, bx, by) +register xchar ax, ay, bx, by; +{ + tbx = ax - bx; /* These two values are set for use */ + tby = ay - by; /* after successful return. */ + + /* sometimes displacement makes a monster think that you're at its + own location; prevent it from throwing and zapping in that case */ + if (!tbx && !tby) return FALSE; + + if((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */ + && distmin(tbx, tby, 0, 0) < BOLT_LIM) { + if(ax == u.ux && ay == u.uy) return((boolean)(couldsee(bx,by))); + else if(clear_path(ax,ay,bx,by)) return TRUE; + } + return FALSE; +} + +boolean +lined_up(mtmp) /* is mtmp in position to use ranged attack? */ + register struct monst *mtmp; +{ + return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my)); +} + +#endif /* OVL1 */ +#ifdef OVL0 + +/* Check if a monster is carrying a particular item. + */ +struct obj * +m_carrying(mtmp, type) +struct monst *mtmp; +int type; +{ + register struct obj *otmp; + + for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if(otmp->otyp == type) + return(otmp); + return((struct obj *) 0); +} + +/* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */ +boolean +hits_bars(obj_p, x, y, always_hit, whodidit) +struct obj **obj_p; /* *obj_p will be set to NULL if object breaks */ +int x, y; +int always_hit; /* caller can force a hit for items which would fit through */ +int whodidit; /* 1==hero, 0=other, -1==just check whether it'll pass thru */ +{ + struct obj *otmp = *obj_p; + int obj_type = otmp->otyp; + boolean hits = always_hit; + + if (!hits) + switch (otmp->oclass) { + case WEAPON_CLASS: + { + int oskill = objects[obj_type].oc_skill; + + hits = (oskill != -P_BOW && oskill != -P_CROSSBOW && + oskill != -P_DART && oskill != -P_SHURIKEN && + oskill != P_SPEAR && oskill != P_JAVELIN && + oskill != P_KNIFE); /* but not dagger */ + break; + } + case ARMOR_CLASS: + hits = (objects[obj_type].oc_armcat != ARM_GLOVES); + break; + case TOOL_CLASS: + hits = (obj_type != SKELETON_KEY && + obj_type != LOCK_PICK && +#ifdef TOURIST + obj_type != CREDIT_CARD && +#endif + obj_type != TALLOW_CANDLE && + obj_type != WAX_CANDLE && + obj_type != LENSES && + obj_type != TIN_WHISTLE && + obj_type != MAGIC_WHISTLE); + break; + case ROCK_CLASS: /* includes boulder */ + if (obj_type != STATUE || + mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE; + break; + case FOOD_CLASS: + if (obj_type == CORPSE && + mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE; + else + hits = (obj_type == MEAT_STICK || + obj_type == HUGE_CHUNK_OF_MEAT); + break; + case SPBOOK_CLASS: + case WAND_CLASS: + case BALL_CLASS: + case CHAIN_CLASS: + hits = TRUE; + break; + default: + break; + } + + if (hits && whodidit != -1) { + if (whodidit ? hero_breaks(otmp, x, y, FALSE) : breaks(otmp, x, y)) + *obj_p = otmp = 0; /* object is now gone */ + /* breakage makes its own noises */ + else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL) + pline("Whang!"); + else if (otmp->oclass == COIN_CLASS || + objects[obj_type].oc_material == GOLD || + objects[obj_type].oc_material == SILVER) + pline("Clink!"); + else + pline("Clonk!"); + } + + return hits; +} + +#endif /* OVL0 */ + +/*mthrowu.c*/ diff --git a/src/muse.c b/src/muse.c new file mode 100644 index 0000000..86044e5 --- /dev/null +++ b/src/muse.c @@ -0,0 +1,2182 @@ +/* SCCS Id: @(#)muse.c 3.4 2002/12/23 */ +/* Copyright (C) 1990 by Ken Arromdee */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Monster item usage routines. + */ + +#include "hack.h" +#include "edog.h" + +extern const int monstr[]; + +boolean m_using = FALSE; + +/* Let monsters use magic items. Arbitrary assumptions: Monsters only use + * scrolls when they can see, monsters know when wands have 0 charges, monsters + * cannot recognize if items are cursed are not, monsters which are confused + * don't know not to read scrolls, etc.... + */ + +STATIC_DCL struct permonst *FDECL(muse_newcham_mon, (struct monst *)); +STATIC_DCL int FDECL(precheck, (struct monst *,struct obj *)); +STATIC_DCL void FDECL(mzapmsg, (struct monst *,struct obj *,BOOLEAN_P)); +STATIC_DCL void FDECL(mreadmsg, (struct monst *,struct obj *)); +STATIC_DCL void FDECL(mquaffmsg, (struct monst *,struct obj *)); +STATIC_PTR int FDECL(mbhitm, (struct monst *,struct obj *)); +STATIC_DCL void FDECL(mbhit, + (struct monst *,int,int FDECL((*),(MONST_P,OBJ_P)), + int FDECL((*),(OBJ_P,OBJ_P)),struct obj *)); +STATIC_DCL void FDECL(you_aggravate, (struct monst *)); +STATIC_DCL void FDECL(mon_consume_unstone, (struct monst *,struct obj *, + BOOLEAN_P,BOOLEAN_P)); + +static struct musable { + struct obj *offensive; + struct obj *defensive; + struct obj *misc; + int has_offense, has_defense, has_misc; + /* =0, no capability; otherwise, different numbers. + * If it's an object, the object is also set (it's 0 otherwise). + */ +} m; +static int trapx, trapy; +static boolean zap_oseen; + /* for wands which use mbhitm and are zapped at players. We usually + * want an oseen local to the function, but this is impossible since the + * function mbhitm has to be compatible with the normal zap routines, + * and those routines don't remember who zapped the wand. + */ + +/* Any preliminary checks which may result in the monster being unable to use + * the item. Returns 0 if nothing happened, 2 if the monster can't do anything + * (i.e. it teleported) and 1 if it's dead. + */ +STATIC_OVL int +precheck(mon, obj) +struct monst *mon; +struct obj *obj; +{ + boolean vis; + + if (!obj) return 0; + vis = cansee(mon->mx, mon->my); + + if (obj->oclass == POTION_CLASS) { + coord cc; + static const char *empty = "The potion turns out to be empty."; + const char *potion_descr; + struct monst *mtmp; +#define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n)) /* also in potion.c */ + + potion_descr = OBJ_DESCR(objects[obj->otyp]); + if (potion_descr && !strcmp(potion_descr, "milky")) { + if ( flags.ghost_count < MAXMONNO && + !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) { + if (!enexto(&cc, mon->mx, mon->my, &mons[PM_GHOST])) return 0; + mquaffmsg(mon, obj); + m_useup(mon, obj); + mtmp = makemon(&mons[PM_GHOST], cc.x, cc.y, NO_MM_FLAGS); + if (!mtmp) { + if (vis) pline(empty); + } else { + if (vis) { + pline("As %s opens the bottle, an enormous %s emerges!", + mon_nam(mon), + Hallucination ? rndmonnam() : (const char *)"ghost"); + pline("%s is frightened to death, and unable to move.", + Monnam(mon)); + } + mon->mcanmove = 0; + mon->mfrozen = 3; + } + return 2; + } + } + if (potion_descr && !strcmp(potion_descr, "smoky") && + flags.djinni_count < MAXMONNO && + !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) { + if (!enexto(&cc, mon->mx, mon->my, &mons[PM_DJINNI])) return 0; + mquaffmsg(mon, obj); + m_useup(mon, obj); + mtmp = makemon(&mons[PM_DJINNI], cc.x, cc.y, NO_MM_FLAGS); + if (!mtmp) { + if (vis) pline(empty); + } else { + if (vis) + pline("In a cloud of smoke, %s emerges!", + a_monnam(mtmp)); + pline("%s speaks.", vis ? Monnam(mtmp) : Something); + /* I suspect few players will be upset that monsters */ + /* can't wish for wands of death here.... */ + if (rn2(2)) { + verbalize("You freed me!"); + mtmp->mpeaceful = 1; + set_malign(mtmp); + } else { + verbalize("It is about time."); + if (vis) pline("%s vanishes.", Monnam(mtmp)); + mongone(mtmp); + } + } + return 2; + } + } + if (obj->oclass == WAND_CLASS && obj->cursed && !rn2(100)) { + int dam = d(obj->spe+2, 6); + + if (flags.soundok) { + if (vis) pline("%s zaps %s, which suddenly explodes!", + Monnam(mon), an(xname(obj))); + else You_hear("a zap and an explosion in the distance."); + } + m_useup(mon, obj); + if (mon->mhp <= dam) { + monkilled(mon, "", AD_RBRE); + return 1; + } + else mon->mhp -= dam; + m.has_defense = m.has_offense = m.has_misc = 0; + /* Only one needed to be set to 0 but the others are harmless */ + } + return 0; +} + +STATIC_OVL void +mzapmsg(mtmp, otmp, self) +struct monst *mtmp; +struct obj *otmp; +boolean self; +{ + if (!canseemon(mtmp)) { + if (flags.soundok) + You_hear("a %s zap.", + (distu(mtmp->mx,mtmp->my) <= (BOLT_LIM+1)*(BOLT_LIM+1)) ? + "nearby" : "distant"); + } else if (self) + pline("%s zaps %sself with %s!", + Monnam(mtmp), mhim(mtmp), doname(otmp)); + else { + pline("%s zaps %s!", Monnam(mtmp), an(xname(otmp))); + stop_occupation(); + } +} + +STATIC_OVL void +mreadmsg(mtmp, otmp) +struct monst *mtmp; +struct obj *otmp; +{ + boolean vismon = canseemon(mtmp); + char onambuf[BUFSZ]; + short saverole; + unsigned savebknown; + + if (!vismon && !flags.soundok) + return; /* no feedback */ + + otmp->dknown = 1; /* seeing or hearing it read reveals its label */ + /* shouldn't be able to hear curse/bless status of unseen scrolls; + for priest characters, bknown will always be set during naming */ + savebknown = otmp->bknown; + saverole = Role_switch; + if (!vismon) { + otmp->bknown = 0; + if (Role_if(PM_PRIEST)) Role_switch = 0; + } + Strcpy(onambuf, singular(otmp, doname)); + Role_switch = saverole; + otmp->bknown = savebknown; + + if (vismon) + pline("%s reads %s!", Monnam(mtmp), onambuf); + else + You_hear("%s reading %s.", + x_monnam(mtmp, ARTICLE_A, (char *)0, + (SUPPRESS_IT|SUPPRESS_INVISIBLE|SUPPRESS_SADDLE), FALSE), + onambuf); + + if (mtmp->mconf) + pline("Being confused, %s mispronounces the magic words...", + vismon ? mon_nam(mtmp) : mhe(mtmp)); +} + +STATIC_OVL void +mquaffmsg(mtmp, otmp) +struct monst *mtmp; +struct obj *otmp; +{ + if (canseemon(mtmp)) { + otmp->dknown = 1; + pline("%s drinks %s!", Monnam(mtmp), singular(otmp, doname)); + } else + if (flags.soundok) + You_hear("a chugging sound."); +} + +/* Defines for various types of stuff. The order in which monsters prefer + * to use them is determined by the order of the code logic, not the + * numerical order in which they are defined. + */ +#define MUSE_SCR_TELEPORTATION 1 +#define MUSE_WAN_TELEPORTATION_SELF 2 +#define MUSE_POT_HEALING 3 +#define MUSE_POT_EXTRA_HEALING 4 +#define MUSE_WAN_DIGGING 5 +#define MUSE_TRAPDOOR 6 +#define MUSE_TELEPORT_TRAP 7 +#define MUSE_UPSTAIRS 8 +#define MUSE_DOWNSTAIRS 9 +#define MUSE_WAN_CREATE_MONSTER 10 +#define MUSE_SCR_CREATE_MONSTER 11 +#define MUSE_UP_LADDER 12 +#define MUSE_DN_LADDER 13 +#define MUSE_SSTAIRS 14 +#define MUSE_WAN_TELEPORTATION 15 +#define MUSE_BUGLE 16 +#define MUSE_UNICORN_HORN 17 +#define MUSE_POT_FULL_HEALING 18 +#define MUSE_LIZARD_CORPSE 19 +/* +#define MUSE_INNATE_TPT 9999 + * We cannot use this. Since monsters get unlimited teleportation, if they + * were allowed to teleport at will you could never catch them. Instead, + * assume they only teleport at random times, despite the inconsistency that if + * you polymorph into one you teleport at will. + */ + +/* Select a defensive item/action for a monster. Returns TRUE iff one is + * found. + */ +boolean +find_defensive(mtmp) +struct monst *mtmp; +{ + register struct obj *obj = 0; + struct trap *t; + int x=mtmp->mx, y=mtmp->my; + boolean stuck = (mtmp == u.ustuck); + boolean immobile = (mtmp->data->mmove == 0); + int fraction; + + if (is_animal(mtmp->data) || mindless(mtmp->data)) + return FALSE; + if(dist2(x, y, mtmp->mux, mtmp->muy) > 25) + return FALSE; + if (u.uswallow && stuck) return FALSE; + + m.defensive = (struct obj *)0; + m.has_defense = 0; + + /* since unicorn horns don't get used up, the monster would look + * silly trying to use the same cursed horn round after round + */ + if (mtmp->mconf || mtmp->mstun || !mtmp->mcansee) { + if (!is_unicorn(mtmp->data) && !nohands(mtmp->data)) { + for(obj = mtmp->minvent; obj; obj = obj->nobj) + if (obj->otyp == UNICORN_HORN && !obj->cursed) + break; + } + if (obj || is_unicorn(mtmp->data)) { + m.defensive = obj; + m.has_defense = MUSE_UNICORN_HORN; + return TRUE; + } + } + + if (mtmp->mconf) { + for(obj = mtmp->minvent; obj; obj = obj->nobj) { + if (obj->otyp == CORPSE && obj->corpsenm == PM_LIZARD) { + m.defensive = obj; + m.has_defense = MUSE_LIZARD_CORPSE; + return TRUE; + } + } + } + + /* It so happens there are two unrelated cases when we might want to + * check specifically for healing alone. The first is when the monster + * is blind (healing cures blindness). The second is when the monster + * is peaceful; then we don't want to flee the player, and by + * coincidence healing is all there is that doesn't involve fleeing. + * These would be hard to combine because of the control flow. + * Pestilence won't use healing even when blind. + */ + if (!mtmp->mcansee && !nohands(mtmp->data) && + mtmp->data != &mons[PM_PESTILENCE]) { + if ((obj = m_carrying(mtmp, POT_FULL_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_FULL_HEALING; + return TRUE; + } + if ((obj = m_carrying(mtmp, POT_EXTRA_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_EXTRA_HEALING; + return TRUE; + } + if ((obj = m_carrying(mtmp, POT_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_HEALING; + return TRUE; + } + } + + fraction = u.ulevel < 10 ? 5 : u.ulevel < 14 ? 4 : 3; + if(mtmp->mhp >= mtmp->mhpmax || + (mtmp->mhp >= 10 && mtmp->mhp*fraction >= mtmp->mhpmax)) + return FALSE; + + if (mtmp->mpeaceful) { + if (!nohands(mtmp->data)) { + if ((obj = m_carrying(mtmp, POT_FULL_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_FULL_HEALING; + return TRUE; + } + if ((obj = m_carrying(mtmp, POT_EXTRA_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_EXTRA_HEALING; + return TRUE; + } + if ((obj = m_carrying(mtmp, POT_HEALING)) != 0) { + m.defensive = obj; + m.has_defense = MUSE_POT_HEALING; + return TRUE; + } + } + return FALSE; + } + + if (levl[x][y].typ == STAIRS && !stuck && !immobile) { + if (x == xdnstair && y == ydnstair && !is_floater(mtmp->data)) + m.has_defense = MUSE_DOWNSTAIRS; + if (x == xupstair && y == yupstair && ledger_no(&u.uz) != 1) + /* Unfair to let the monsters leave the dungeon with the Amulet */ + /* (or go to the endlevel since you also need it, to get there) */ + m.has_defense = MUSE_UPSTAIRS; + } else if (levl[x][y].typ == LADDER && !stuck && !immobile) { + if (x == xupladder && y == yupladder) + m.has_defense = MUSE_UP_LADDER; + if (x == xdnladder && y == ydnladder && !is_floater(mtmp->data)) + m.has_defense = MUSE_DN_LADDER; + } else if (sstairs.sx && sstairs.sx == x && sstairs.sy == y) { + m.has_defense = MUSE_SSTAIRS; + } else if (!stuck && !immobile) { + /* Note: trap doors take precedence over teleport traps. */ + int xx, yy; + + for(xx = x-1; xx <= x+1; xx++) for(yy = y-1; yy <= y+1; yy++) + if (isok(xx,yy)) + if (xx != u.ux && yy != u.uy) + if (mtmp->data != &mons[PM_GRID_BUG] || xx == x || yy == y) + if ((xx==x && yy==y) || !level.monsters[xx][yy]) + if ((t = t_at(xx,yy)) != 0) + if ((verysmall(mtmp->data) || throws_rocks(mtmp->data) || + passes_walls(mtmp->data)) || !sobj_at(BOULDER, xx, yy)) + if (!onscary(xx,yy,mtmp)) { + if ((t->ttyp == TRAPDOOR || t->ttyp == HOLE) + && !is_floater(mtmp->data) + && !mtmp->isshk && !mtmp->isgd + && !mtmp->ispriest + && Can_fall_thru(&u.uz) + ) { + trapx = xx; + trapy = yy; + m.has_defense = MUSE_TRAPDOOR; + } else if (t->ttyp == TELEP_TRAP && m.has_defense != MUSE_TRAPDOOR) { + trapx = xx; + trapy = yy; + m.has_defense = MUSE_TELEPORT_TRAP; + } + } + } + + if (nohands(mtmp->data)) /* can't use objects */ + goto botm; + + if (is_mercenary(mtmp->data) && (obj = m_carrying(mtmp, BUGLE))) { + int xx, yy; + struct monst *mon; + + /* Distance is arbitrary. What we really want to do is + * have the soldier play the bugle when it sees or + * remembers soldiers nearby... + */ + for(xx = x-3; xx <= x+3; xx++) for(yy = y-3; yy <= y+3; yy++) + if (isok(xx,yy)) + if ((mon = m_at(xx,yy)) && is_mercenary(mon->data) && + mon->data != &mons[PM_GUARD] && + (mon->msleeping || (!mon->mcanmove))) { + m.defensive = obj; + m.has_defense = MUSE_BUGLE; + } + } + + /* use immediate physical escape prior to attempting magic */ + if (m.has_defense) /* stairs, trap door or tele-trap, bugle alert */ + goto botm; + + /* kludge to cut down on trap destruction (particularly portals) */ + t = t_at(x,y); + if (t && (t->ttyp == PIT || t->ttyp == SPIKED_PIT || + t->ttyp == WEB || t->ttyp == BEAR_TRAP)) + t = 0; /* ok for monster to dig here */ + +#define nomore(x) if(m.has_defense==x) continue; + for (obj = mtmp->minvent; obj; obj = obj->nobj) { + /* don't always use the same selection pattern */ + if (m.has_defense && !rn2(3)) break; + + /* nomore(MUSE_WAN_DIGGING); */ + if (m.has_defense == MUSE_WAN_DIGGING) break; + if (obj->otyp == WAN_DIGGING && obj->spe > 0 && !stuck && !t + && !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest + && !is_floater(mtmp->data) + /* monsters digging in Sokoban can ruin things */ + && !In_sokoban(&u.uz) + /* digging wouldn't be effective; assume they know that */ + && !(levl[x][y].wall_info & W_NONDIGGABLE) + && !(Is_botlevel(&u.uz) || In_endgame(&u.uz)) + && !(is_ice(x,y) || is_pool(x,y) || is_lava(x,y)) + && !(mtmp->data == &mons[PM_VLAD_THE_IMPALER] + && In_V_tower(&u.uz))) { + m.defensive = obj; + m.has_defense = MUSE_WAN_DIGGING; + } + nomore(MUSE_WAN_TELEPORTATION_SELF); + nomore(MUSE_WAN_TELEPORTATION); + if(obj->otyp == WAN_TELEPORTATION && obj->spe > 0) { + /* use the TELEP_TRAP bit to determine if they know + * about noteleport on this level or not. Avoids + * ineffective re-use of teleportation. This does + * mean if the monster leaves the level, they'll know + * about teleport traps. + */ + if (!level.flags.noteleport || + !(mtmp->mtrapseen & (1 << (TELEP_TRAP-1)))) { + m.defensive = obj; + m.has_defense = (mon_has_amulet(mtmp)) + ? MUSE_WAN_TELEPORTATION + : MUSE_WAN_TELEPORTATION_SELF; + } + } + nomore(MUSE_SCR_TELEPORTATION); + if(obj->otyp == SCR_TELEPORTATION && mtmp->mcansee + && haseyes(mtmp->data) + && (!obj->cursed || + (!(mtmp->isshk && inhishop(mtmp)) + && !mtmp->isgd && !mtmp->ispriest))) { + /* see WAN_TELEPORTATION case above */ + if (!level.flags.noteleport || + !(mtmp->mtrapseen & (1 << (TELEP_TRAP-1)))) { + m.defensive = obj; + m.has_defense = MUSE_SCR_TELEPORTATION; + } + } + + if (mtmp->data != &mons[PM_PESTILENCE]) { + nomore(MUSE_POT_FULL_HEALING); + if(obj->otyp == POT_FULL_HEALING) { + m.defensive = obj; + m.has_defense = MUSE_POT_FULL_HEALING; + } + nomore(MUSE_POT_EXTRA_HEALING); + if(obj->otyp == POT_EXTRA_HEALING) { + m.defensive = obj; + m.has_defense = MUSE_POT_EXTRA_HEALING; + } + nomore(MUSE_WAN_CREATE_MONSTER); + if(obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) { + m.defensive = obj; + m.has_defense = MUSE_WAN_CREATE_MONSTER; + } + nomore(MUSE_POT_HEALING); + if(obj->otyp == POT_HEALING) { + m.defensive = obj; + m.has_defense = MUSE_POT_HEALING; + } + } else { /* Pestilence */ + nomore(MUSE_POT_FULL_HEALING); + if (obj->otyp == POT_SICKNESS) { + m.defensive = obj; + m.has_defense = MUSE_POT_FULL_HEALING; + } + nomore(MUSE_WAN_CREATE_MONSTER); + if (obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) { + m.defensive = obj; + m.has_defense = MUSE_WAN_CREATE_MONSTER; + } + } + nomore(MUSE_SCR_CREATE_MONSTER); + if(obj->otyp == SCR_CREATE_MONSTER) { + m.defensive = obj; + m.has_defense = MUSE_SCR_CREATE_MONSTER; + } + } +botm: return((boolean)(!!m.has_defense)); +#undef nomore +} + +/* Perform a defensive action for a monster. Must be called immediately + * after find_defensive(). Return values are 0: did something, 1: died, + * 2: did something and can't attack again (i.e. teleported). + */ +int +use_defensive(mtmp) +struct monst *mtmp; +{ + int i, fleetim, how = 0; + struct obj *otmp = m.defensive; + boolean vis, vismon, oseen; + const char *mcsa = "%s can see again."; + + if ((i = precheck(mtmp, otmp)) != 0) return i; + vis = cansee(mtmp->mx, mtmp->my); + vismon = canseemon(mtmp); + oseen = otmp && vismon; + + /* when using defensive choice to run away, we want monster to avoid + rushing right straight back; don't override if already scared */ + fleetim = !mtmp->mflee ? (33 - (30 * mtmp->mhp / mtmp->mhpmax)) : 0; +#define m_flee(m) if (fleetim && !m->iswiz) \ + { monflee(m, fleetim, FALSE, FALSE); } + + switch(m.has_defense) { + case MUSE_UNICORN_HORN: + if (vismon) { + if (otmp) + pline("%s uses a unicorn horn!", Monnam(mtmp)); + else + pline_The("tip of %s's horn glows!", mon_nam(mtmp)); + } + if (!mtmp->mcansee) { + mtmp->mcansee = 1; + mtmp->mblinded = 0; + if (vismon) pline(mcsa, Monnam(mtmp)); + } else if (mtmp->mconf || mtmp->mstun) { + mtmp->mconf = mtmp->mstun = 0; + if (vismon) + pline("%s seems steadier now.", Monnam(mtmp)); + } else impossible("No need for unicorn horn?"); + return 2; + case MUSE_BUGLE: + if (vismon) + pline("%s plays %s!", Monnam(mtmp), doname(otmp)); + else if (flags.soundok) + You_hear("a bugle playing reveille!"); + awaken_soldiers(); + return 2; + case MUSE_WAN_TELEPORTATION_SELF: + if ((mtmp->isshk && inhishop(mtmp)) + || mtmp->isgd || mtmp->ispriest) return 2; + m_flee(mtmp); + mzapmsg(mtmp, otmp, TRUE); + otmp->spe--; + how = WAN_TELEPORTATION; +mon_tele: + if (tele_restrict(mtmp)) { /* mysterious force... */ + if (vismon && how) /* mentions 'teleport' */ + makeknown(how); + /* monster learns that teleportation isn't useful here */ + if (level.flags.noteleport) + mtmp->mtrapseen |= (1 << (TELEP_TRAP-1)); + return 2; + } + if (( +#if 0 + mon_has_amulet(mtmp) || +#endif + On_W_tower_level(&u.uz)) && !rn2(3)) { + if (vismon) + pline("%s seems disoriented for a moment.", + Monnam(mtmp)); + return 2; + } + if (oseen && how) makeknown(how); + (void) rloc(mtmp, FALSE); + return 2; + case MUSE_WAN_TELEPORTATION: + zap_oseen = oseen; + mzapmsg(mtmp, otmp, FALSE); + otmp->spe--; + m_using = TRUE; + mbhit(mtmp,rn1(8,6),mbhitm,bhito,otmp); + /* monster learns that teleportation isn't useful here */ + if (level.flags.noteleport) + mtmp->mtrapseen |= (1 << (TELEP_TRAP-1)); + m_using = FALSE; + return 2; + case MUSE_SCR_TELEPORTATION: + { + int obj_is_cursed = otmp->cursed; + + if (mtmp->isshk || mtmp->isgd || mtmp->ispriest) return 2; + m_flee(mtmp); + mreadmsg(mtmp, otmp); + m_useup(mtmp, otmp); /* otmp might be free'ed */ + how = SCR_TELEPORTATION; + if (obj_is_cursed || mtmp->mconf) { + int nlev; + d_level flev; + + if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) { + if (vismon) + pline("%s seems very disoriented for a moment.", + Monnam(mtmp)); + return 2; + } + nlev = random_teleport_level(); + if (nlev == depth(&u.uz)) { + if (vismon) + pline("%s shudders for a moment.", + Monnam(mtmp)); + return 2; + } + get_level(&flev, nlev); + migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM, + (coord *)0); + if (oseen) makeknown(SCR_TELEPORTATION); + } else goto mon_tele; + return 2; + } + case MUSE_WAN_DIGGING: + { struct trap *ttmp; + + m_flee(mtmp); + mzapmsg(mtmp, otmp, FALSE); + otmp->spe--; + if (oseen) makeknown(WAN_DIGGING); + if (IS_FURNITURE(levl[mtmp->mx][mtmp->my].typ) || + IS_DRAWBRIDGE(levl[mtmp->mx][mtmp->my].typ) || + (is_drawbridge_wall(mtmp->mx, mtmp->my) >= 0) || + (sstairs.sx && sstairs.sx == mtmp->mx && + sstairs.sy == mtmp->my)) { + pline_The("digging ray is ineffective."); + return 2; + } + if (!Can_dig_down(&u.uz)) { + if(canseemon(mtmp)) + pline_The("%s here is too hard to dig in.", + surface(mtmp->mx, mtmp->my)); + return 2; + } + ttmp = maketrap(mtmp->mx, mtmp->my, HOLE); + if (!ttmp) return 2; + seetrap(ttmp); + if (vis) { + pline("%s has made a hole in the %s.", Monnam(mtmp), + surface(mtmp->mx, mtmp->my)); + pline("%s %s through...", Monnam(mtmp), + is_flyer(mtmp->data) ? "dives" : "falls"); + } else if (flags.soundok) + You_hear("%s crash through the %s.", something, + surface(mtmp->mx, mtmp->my)); + /* we made sure that there is a level for mtmp to go to */ + migrate_to_level(mtmp, ledger_no(&u.uz) + 1, + MIGR_RANDOM, (coord *)0); + return 2; + } + case MUSE_WAN_CREATE_MONSTER: + { coord cc; + /* pm: 0 => random, eel => aquatic, croc => amphibious */ + struct permonst *pm = !is_pool(mtmp->mx, mtmp->my) ? 0 : + &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE]; + struct monst *mon; + + if (!enexto(&cc, mtmp->mx, mtmp->my, pm)) return 0; + mzapmsg(mtmp, otmp, FALSE); + otmp->spe--; + mon = makemon((struct permonst *)0, cc.x, cc.y, NO_MM_FLAGS); + if (mon && canspotmon(mon) && oseen) + makeknown(WAN_CREATE_MONSTER); + return 2; + } + case MUSE_SCR_CREATE_MONSTER: + { coord cc; + struct permonst *pm = 0, *fish = 0; + int cnt = 1; + struct monst *mon; + boolean known = FALSE; + + if (!rn2(73)) cnt += rnd(4); + if (mtmp->mconf || otmp->cursed) cnt += 12; + if (mtmp->mconf) pm = fish = &mons[PM_ACID_BLOB]; + else if (is_pool(mtmp->mx, mtmp->my)) + fish = &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE]; + mreadmsg(mtmp, otmp); + while(cnt--) { + /* `fish' potentially gives bias towards water locations; + `pm' is what to actually create (0 => random) */ + if (!enexto(&cc, mtmp->mx, mtmp->my, fish)) break; + mon = makemon(pm, cc.x, cc.y, NO_MM_FLAGS); + if (mon && canspotmon(mon)) known = TRUE; + } + /* The only case where we don't use oseen. For wands, you + * have to be able to see the monster zap the wand to know + * what type it is. For teleport scrolls, you have to see + * the monster to know it teleported. + */ + if (known) + makeknown(SCR_CREATE_MONSTER); + else if (!objects[SCR_CREATE_MONSTER].oc_name_known + && !objects[SCR_CREATE_MONSTER].oc_uname) + docall(otmp); + m_useup(mtmp, otmp); + return 2; + } + case MUSE_TRAPDOOR: + /* trap doors on "bottom" levels of dungeons are rock-drop + * trap doors, not holes in the floor. We check here for + * safety. + */ + if (Is_botlevel(&u.uz)) return 0; + m_flee(mtmp); + if (vis) { + struct trap *t; + t = t_at(trapx,trapy); + pline("%s %s into a %s!", Monnam(mtmp), + makeplural(locomotion(mtmp->data, "jump")), + t->ttyp == TRAPDOOR ? "trap door" : "hole"); + if (levl[trapx][trapy].typ == SCORR) { + levl[trapx][trapy].typ = CORR; + unblock_point(trapx, trapy); + } + seetrap(t_at(trapx,trapy)); + } + + /* don't use rloc_to() because worm tails must "move" */ + remove_monster(mtmp->mx, mtmp->my); + newsym(mtmp->mx, mtmp->my); /* update old location */ + place_monster(mtmp, trapx, trapy); + if (mtmp->wormno) worm_move(mtmp); + newsym(trapx, trapy); + + migrate_to_level(mtmp, ledger_no(&u.uz) + 1, + MIGR_RANDOM, (coord *)0); + return 2; + case MUSE_UPSTAIRS: + /* Monsters without amulets escape the dungeon and are + * gone for good when they leave up the up stairs. + * Monsters with amulets would reach the endlevel, + * which we cannot allow since that would leave the + * player stranded. + */ + if (ledger_no(&u.uz) == 1) { + if (mon_has_special(mtmp)) + return 0; + if (vismon) + pline("%s escapes the dungeon!", Monnam(mtmp)); + mongone(mtmp); + return 2; + } + m_flee(mtmp); + if (Inhell && mon_has_amulet(mtmp) && !rn2(4) && + (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz) - 3)) { + if (vismon) pline( + "As %s climbs the stairs, a mysterious force momentarily surrounds %s...", + mon_nam(mtmp), mhim(mtmp)); + /* simpler than for the player; this will usually be + the Wizard and he'll immediately go right to the + upstairs, so there's not much point in having any + chance for a random position on the current level */ + migrate_to_level(mtmp, ledger_no(&u.uz) + 1, + MIGR_RANDOM, (coord *)0); + } else { + if (vismon) pline("%s escapes upstairs!", Monnam(mtmp)); + migrate_to_level(mtmp, ledger_no(&u.uz) - 1, + MIGR_STAIRS_DOWN, (coord *)0); + } + return 2; + case MUSE_DOWNSTAIRS: + m_flee(mtmp); + if (vismon) pline("%s escapes downstairs!", Monnam(mtmp)); + migrate_to_level(mtmp, ledger_no(&u.uz) + 1, + MIGR_STAIRS_UP, (coord *)0); + return 2; + case MUSE_UP_LADDER: + m_flee(mtmp); + if (vismon) pline("%s escapes up the ladder!", Monnam(mtmp)); + migrate_to_level(mtmp, ledger_no(&u.uz) - 1, + MIGR_LADDER_DOWN, (coord *)0); + return 2; + case MUSE_DN_LADDER: + m_flee(mtmp); + if (vismon) pline("%s escapes down the ladder!", Monnam(mtmp)); + migrate_to_level(mtmp, ledger_no(&u.uz) + 1, + MIGR_LADDER_UP, (coord *)0); + return 2; + case MUSE_SSTAIRS: + m_flee(mtmp); + /* the stairs leading up from the 1st level are */ + /* regular stairs, not sstairs. */ + if (sstairs.up) { + if (vismon) + pline("%s escapes upstairs!", Monnam(mtmp)); + if(Inhell) { + migrate_to_level(mtmp, ledger_no(&sstairs.tolev), + MIGR_RANDOM, (coord *)0); + return 2; + } + } else if (vismon) + pline("%s escapes downstairs!", Monnam(mtmp)); + migrate_to_level(mtmp, ledger_no(&sstairs.tolev), + MIGR_SSTAIRS, (coord *)0); + return 2; + case MUSE_TELEPORT_TRAP: + m_flee(mtmp); + if (vis) { + pline("%s %s onto a teleport trap!", Monnam(mtmp), + makeplural(locomotion(mtmp->data, "jump"))); + if (levl[trapx][trapy].typ == SCORR) { + levl[trapx][trapy].typ = CORR; + unblock_point(trapx, trapy); + } + seetrap(t_at(trapx,trapy)); + } + /* don't use rloc_to() because worm tails must "move" */ + remove_monster(mtmp->mx, mtmp->my); + newsym(mtmp->mx, mtmp->my); /* update old location */ + place_monster(mtmp, trapx, trapy); + if (mtmp->wormno) worm_move(mtmp); + newsym(trapx, trapy); + + goto mon_tele; + case MUSE_POT_HEALING: + mquaffmsg(mtmp, otmp); + i = d(6 + 2 * bcsign(otmp), 4); + mtmp->mhp += i; + if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = ++mtmp->mhpmax; + if (!otmp->cursed && !mtmp->mcansee) { + mtmp->mcansee = 1; + mtmp->mblinded = 0; + if (vismon) pline(mcsa, Monnam(mtmp)); + } + if (vismon) pline("%s looks better.", Monnam(mtmp)); + if (oseen) makeknown(POT_HEALING); + m_useup(mtmp, otmp); + return 2; + case MUSE_POT_EXTRA_HEALING: + mquaffmsg(mtmp, otmp); + i = d(6 + 2 * bcsign(otmp), 8); + mtmp->mhp += i; + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2)); + if (!mtmp->mcansee) { + mtmp->mcansee = 1; + mtmp->mblinded = 0; + if (vismon) pline(mcsa, Monnam(mtmp)); + } + if (vismon) pline("%s looks much better.", Monnam(mtmp)); + if (oseen) makeknown(POT_EXTRA_HEALING); + m_useup(mtmp, otmp); + return 2; + case MUSE_POT_FULL_HEALING: + mquaffmsg(mtmp, otmp); + if (otmp->otyp == POT_SICKNESS) unbless(otmp); /* Pestilence */ + mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4)); + if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) { + mtmp->mcansee = 1; + mtmp->mblinded = 0; + if (vismon) pline(mcsa, Monnam(mtmp)); + } + if (vismon) pline("%s looks completely healed.", Monnam(mtmp)); + if (oseen) makeknown(otmp->otyp); + m_useup(mtmp, otmp); + return 2; + case MUSE_LIZARD_CORPSE: + /* not actually called for its unstoning effect */ + mon_consume_unstone(mtmp, otmp, FALSE, FALSE); + return 2; + case 0: return 0; /* i.e. an exploded wand */ + default: impossible("%s wanted to perform action %d?", Monnam(mtmp), + m.has_defense); + break; + } + return 0; +#undef m_flee +} + +int +rnd_defensive_item(mtmp) +struct monst *mtmp; +{ + struct permonst *pm = mtmp->data; + int difficulty = monstr[(monsndx(pm))]; + int trycnt = 0; + + if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data) + || pm->mlet == S_GHOST +# ifdef KOPS + || pm->mlet == S_KOP +# endif + ) return 0; + try_again: + switch (rn2(8 + (difficulty > 3) + (difficulty > 6) + + (difficulty > 8))) { + case 6: case 9: + if (level.flags.noteleport && ++trycnt < 2) + goto try_again; + if (!rn2(3)) return WAN_TELEPORTATION; + /* else FALLTHRU */ + case 0: case 1: + return SCR_TELEPORTATION; + case 8: case 10: + if (!rn2(3)) return WAN_CREATE_MONSTER; + /* else FALLTHRU */ + case 2: return SCR_CREATE_MONSTER; + case 3: return POT_HEALING; + case 4: return POT_EXTRA_HEALING; + case 5: return (mtmp->data != &mons[PM_PESTILENCE]) ? + POT_FULL_HEALING : POT_SICKNESS; + case 7: if (is_floater(pm) || mtmp->isshk || mtmp->isgd + || mtmp->ispriest + ) + return 0; + else + return WAN_DIGGING; + } + /*NOTREACHED*/ + return 0; +} + +#define MUSE_WAN_DEATH 1 +#define MUSE_WAN_SLEEP 2 +#define MUSE_WAN_FIRE 3 +#define MUSE_WAN_COLD 4 +#define MUSE_WAN_LIGHTNING 5 +#define MUSE_WAN_MAGIC_MISSILE 6 +#define MUSE_WAN_STRIKING 7 +#define MUSE_SCR_FIRE 8 +#define MUSE_POT_PARALYSIS 9 +#define MUSE_POT_BLINDNESS 10 +#define MUSE_POT_CONFUSION 11 +#define MUSE_FROST_HORN 12 +#define MUSE_FIRE_HORN 13 +#define MUSE_POT_ACID 14 +/*#define MUSE_WAN_TELEPORTATION 15*/ +#define MUSE_POT_SLEEPING 16 +#define MUSE_SCR_EARTH 17 + +/* Select an offensive item/action for a monster. Returns TRUE iff one is + * found. + */ +boolean +find_offensive(mtmp) +struct monst *mtmp; +{ + register struct obj *obj; + boolean ranged_stuff = lined_up(mtmp); + boolean reflection_skip = (Reflecting && rn2(2)); + struct obj *helmet = which_armor(mtmp, W_ARMH); + + m.offensive = (struct obj *)0; + m.has_offense = 0; + if (mtmp->mpeaceful || is_animal(mtmp->data) || + mindless(mtmp->data) || nohands(mtmp->data)) + return FALSE; + if (u.uswallow) return FALSE; + if (in_your_sanctuary(mtmp, 0, 0)) return FALSE; + if (dmgtype(mtmp->data, AD_HEAL) && !uwep +#ifdef TOURIST + && !uarmu +#endif + && !uarm && !uarmh && !uarms && !uarmg && !uarmc && !uarmf) + return FALSE; + + if (!ranged_stuff) return FALSE; +#define nomore(x) if(m.has_offense==x) continue; + for(obj=mtmp->minvent; obj; obj=obj->nobj) { + /* nomore(MUSE_WAN_DEATH); */ + if (!reflection_skip) { + if(obj->otyp == WAN_DEATH && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_DEATH; + } + nomore(MUSE_WAN_SLEEP); + if(obj->otyp == WAN_SLEEP && obj->spe > 0 && multi >= 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_SLEEP; + } + nomore(MUSE_WAN_FIRE); + if(obj->otyp == WAN_FIRE && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_FIRE; + } + nomore(MUSE_FIRE_HORN); + if(obj->otyp == FIRE_HORN && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_FIRE_HORN; + } + nomore(MUSE_WAN_COLD); + if(obj->otyp == WAN_COLD && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_COLD; + } + nomore(MUSE_FROST_HORN); + if(obj->otyp == FROST_HORN && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_FROST_HORN; + } + nomore(MUSE_WAN_LIGHTNING); + if(obj->otyp == WAN_LIGHTNING && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_LIGHTNING; + } + nomore(MUSE_WAN_MAGIC_MISSILE); + if(obj->otyp == WAN_MAGIC_MISSILE && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_MAGIC_MISSILE; + } + } + nomore(MUSE_WAN_STRIKING); + if(obj->otyp == WAN_STRIKING && obj->spe > 0) { + m.offensive = obj; + m.has_offense = MUSE_WAN_STRIKING; + } + nomore(MUSE_POT_PARALYSIS); + if(obj->otyp == POT_PARALYSIS && multi >= 0) { + m.offensive = obj; + m.has_offense = MUSE_POT_PARALYSIS; + } + nomore(MUSE_POT_BLINDNESS); + if(obj->otyp == POT_BLINDNESS && !attacktype(mtmp->data, AT_GAZE)) { + m.offensive = obj; + m.has_offense = MUSE_POT_BLINDNESS; + } + nomore(MUSE_POT_CONFUSION); + if(obj->otyp == POT_CONFUSION) { + m.offensive = obj; + m.has_offense = MUSE_POT_CONFUSION; + } + nomore(MUSE_POT_SLEEPING); + if(obj->otyp == POT_SLEEPING) { + m.offensive = obj; + m.has_offense = MUSE_POT_SLEEPING; + } + nomore(MUSE_POT_ACID); + if(obj->otyp == POT_ACID) { + m.offensive = obj; + m.has_offense = MUSE_POT_ACID; + } + /* we can safely put this scroll here since the locations that + * are in a 1 square radius are a subset of the locations that + * are in wand range + */ + nomore(MUSE_SCR_EARTH); + if (obj->otyp == SCR_EARTH + && ((helmet && is_metallic(helmet)) || + mtmp->mconf || amorphous(mtmp->data) || + passes_walls(mtmp->data) || + noncorporeal(mtmp->data) || + unsolid(mtmp->data) || !rn2(10)) + && dist2(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy) <= 2 + && mtmp->mcansee && haseyes(mtmp->data) +#ifdef REINCARNATION + && !Is_rogue_level(&u.uz) +#endif + && (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) { + m.offensive = obj; + m.has_offense = MUSE_SCR_EARTH; + } +#if 0 + nomore(MUSE_SCR_FIRE); + if (obj->otyp == SCR_FIRE && resists_fire(mtmp) + && dist2(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy) <= 2 + && mtmp->mcansee && haseyes(mtmp->data)) { + m.offensive = obj; + m.has_offense = MUSE_SCR_FIRE; + } +#endif + } + return((boolean)(!!m.has_offense)); +#undef nomore +} + +STATIC_PTR +int +mbhitm(mtmp, otmp) +register struct monst *mtmp; +register struct obj *otmp; +{ + int tmp; + + boolean reveal_invis = FALSE; + if (mtmp != &youmonst) { + mtmp->msleeping = 0; + if (mtmp->m_ap_type) seemimic(mtmp); + } + switch(otmp->otyp) { + case WAN_STRIKING: + reveal_invis = TRUE; + if (mtmp == &youmonst) { + if (zap_oseen) makeknown(WAN_STRIKING); + if (Antimagic) { + shieldeff(u.ux, u.uy); + pline("Boing!"); + } else if (rnd(20) < 10 + u.uac) { + pline_The("wand hits you!"); + tmp = d(2,12); + if(Half_spell_damage) tmp = (tmp+1) / 2; + losehp(tmp, "wand", KILLED_BY_AN); + } else pline_The("wand misses you."); + stop_occupation(); + nomul(0); + } else if (resists_magm(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + pline("Boing!"); + } else if (rnd(20) < 10+find_mac(mtmp)) { + tmp = d(2,12); + hit("wand", mtmp, exclam(tmp)); + (void) resist(mtmp, otmp->oclass, tmp, TELL); + if (cansee(mtmp->mx, mtmp->my) && zap_oseen) + makeknown(WAN_STRIKING); + } else { + miss("wand", mtmp); + if (cansee(mtmp->mx, mtmp->my) && zap_oseen) + makeknown(WAN_STRIKING); + } + break; + case WAN_TELEPORTATION: + if (mtmp == &youmonst) { + if (zap_oseen) makeknown(WAN_TELEPORTATION); + tele(); + } else { + /* for consistency with zap.c, don't identify */ + if (mtmp->ispriest && + *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { + if (cansee(mtmp->mx, mtmp->my)) + pline("%s resists the magic!", Monnam(mtmp)); + mtmp->msleeping = 0; + if(mtmp->m_ap_type) seemimic(mtmp); + } else if (!tele_restrict(mtmp)) + (void) rloc(mtmp, FALSE); + } + break; + case WAN_CANCELLATION: + case SPE_CANCELLATION: + (void) cancel_monst(mtmp, otmp, FALSE, TRUE, FALSE); + break; + } + if (reveal_invis) { + if (mtmp->mhp > 0 && cansee(bhitpos.x,bhitpos.y) + && !canspotmon(mtmp)) + map_invisible(bhitpos.x, bhitpos.y); + } + return 0; +} + +/* A modified bhit() for monsters. Based on bhit() in zap.c. Unlike + * buzz(), bhit() doesn't take into account the possibility of a monster + * zapping you, so we need a special function for it. (Unless someone wants + * to merge the two functions...) + */ +STATIC_OVL void +mbhit(mon,range,fhitm,fhito,obj) +struct monst *mon; /* monster shooting the wand */ +register int range; /* direction and range */ +int FDECL((*fhitm),(MONST_P,OBJ_P)); +int FDECL((*fhito),(OBJ_P,OBJ_P)); /* fns called when mon/obj hit */ +struct obj *obj; /* 2nd arg to fhitm/fhito */ +{ + register struct monst *mtmp; + register struct obj *otmp; + register uchar typ; + int ddx, ddy; + + bhitpos.x = mon->mx; + bhitpos.y = mon->my; + ddx = sgn(mon->mux - mon->mx); + ddy = sgn(mon->muy - mon->my); + + while(range-- > 0) { + int x,y; + + bhitpos.x += ddx; + bhitpos.y += ddy; + x = bhitpos.x; y = bhitpos.y; + + if (!isok(x,y)) { + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + if (find_drawbridge(&x,&y)) + switch (obj->otyp) { + case WAN_STRIKING: + destroy_drawbridge(x,y); + } + if(bhitpos.x==u.ux && bhitpos.y==u.uy) { + (*fhitm)(&youmonst, obj); + range -= 3; + } else if(MON_AT(bhitpos.x, bhitpos.y)){ + mtmp = m_at(bhitpos.x,bhitpos.y); + if (cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp)) + map_invisible(bhitpos.x, bhitpos.y); + (*fhitm)(mtmp, obj); + range -= 3; + } + /* modified by GAN to hit all objects */ + if(fhito){ + int hitanything = 0; + register struct obj *next_obj; + + for(otmp = level.objects[bhitpos.x][bhitpos.y]; + otmp; otmp = next_obj) { + /* Fix for polymorph bug, Tim Wright */ + next_obj = otmp->nexthere; + hitanything += (*fhito)(otmp, obj); + } + if(hitanything) range--; + } + typ = levl[bhitpos.x][bhitpos.y].typ; + if(IS_DOOR(typ) || typ == SDOOR) { + switch (obj->otyp) { + /* note: monsters don't use opening or locking magic + at present, but keep these as placeholders */ + case WAN_OPENING: + case WAN_LOCKING: + case WAN_STRIKING: + if (doorlock(obj, bhitpos.x, bhitpos.y)) { + makeknown(obj->otyp); + /* if a shop door gets broken, add it to + the shk's fix list (no cost to player) */ + if (levl[bhitpos.x][bhitpos.y].doormask == + D_BROKEN && + *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) + add_damage(bhitpos.x, bhitpos.y, 0L); + } + break; + } + } + if(!ZAP_POS(typ) || (IS_DOOR(typ) && + (levl[bhitpos.x][bhitpos.y].doormask & (D_LOCKED | D_CLOSED))) + ) { + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + } +} + +/* Perform an offensive action for a monster. Must be called immediately + * after find_offensive(). Return values are same as use_defensive(). + */ +int +use_offensive(mtmp) +struct monst *mtmp; +{ + int i; + struct obj *otmp = m.offensive; + boolean oseen; + + /* offensive potions are not drunk, they're thrown */ + if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0) + return i; + oseen = otmp && canseemon(mtmp); + + switch(m.has_offense) { + case MUSE_WAN_DEATH: + case MUSE_WAN_SLEEP: + case MUSE_WAN_FIRE: + case MUSE_WAN_COLD: + case MUSE_WAN_LIGHTNING: + case MUSE_WAN_MAGIC_MISSILE: + mzapmsg(mtmp, otmp, FALSE); + otmp->spe--; + if (oseen) makeknown(otmp->otyp); + m_using = TRUE; + buzz((int)(-30 - (otmp->otyp - WAN_MAGIC_MISSILE)), + (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6, + mtmp->mx, mtmp->my, + sgn(mtmp->mux-mtmp->mx), sgn(mtmp->muy-mtmp->my)); + m_using = FALSE; + return (mtmp->mhp <= 0) ? 1 : 2; + case MUSE_FIRE_HORN: + case MUSE_FROST_HORN: + if (oseen) { + makeknown(otmp->otyp); + pline("%s plays a %s!", Monnam(mtmp), xname(otmp)); + } else + You_hear("a horn being played."); + otmp->spe--; + m_using = TRUE; + buzz(-30 - ((otmp->otyp==FROST_HORN) ? AD_COLD-1 : AD_FIRE-1), + rn1(6,6), mtmp->mx, mtmp->my, + sgn(mtmp->mux-mtmp->mx), sgn(mtmp->muy-mtmp->my)); + m_using = FALSE; + return (mtmp->mhp <= 0) ? 1 : 2; + case MUSE_WAN_TELEPORTATION: + case MUSE_WAN_STRIKING: + zap_oseen = oseen; + mzapmsg(mtmp, otmp, FALSE); + otmp->spe--; + m_using = TRUE; + mbhit(mtmp,rn1(8,6),mbhitm,bhito,otmp); + m_using = FALSE; + return 2; + case MUSE_SCR_EARTH: + { + /* TODO: handle steeds */ + register int x, y; + /* don't use monster fields after killing it */ + boolean confused = (mtmp->mconf ? TRUE : FALSE); + int mmx = mtmp->mx, mmy = mtmp->my; + + mreadmsg(mtmp, otmp); + /* Identify the scroll */ + if (canspotmon(mtmp)) { + pline_The("%s rumbles %s %s!", ceiling(mtmp->mx, mtmp->my), + otmp->blessed ? "around" : "above", + mon_nam(mtmp)); + if (oseen) makeknown(otmp->otyp); + } else if (cansee(mtmp->mx, mtmp->my)) { + pline_The("%s rumbles in the middle of nowhere!", + ceiling(mtmp->mx, mtmp->my)); + if (mtmp->minvis) + map_invisible(mtmp->mx, mtmp->my); + if (oseen) makeknown(otmp->otyp); + } + + /* Loop through the surrounding squares */ + for (x = mmx-1; x <= mmx+1; x++) { + for (y = mmy-1; y <= mmy+1; y++) { + /* Is this a suitable spot? */ + if (isok(x, y) && !closed_door(x, y) && + !IS_ROCK(levl[x][y].typ) && + !IS_AIR(levl[x][y].typ) && + (((x == mmx) && (y == mmy)) ? + !otmp->blessed : !otmp->cursed) && + (x != u.ux || y != u.uy)) { + register struct obj *otmp2; + register struct monst *mtmp2; + + /* Make the object(s) */ + otmp2 = mksobj(confused ? ROCK : BOULDER, + FALSE, FALSE); + if (!otmp2) continue; /* Shouldn't happen */ + otmp2->quan = confused ? rn1(5,2) : 1; + otmp2->owt = weight(otmp2); + + /* Find the monster here (might be same as mtmp) */ + mtmp2 = m_at(x, y); + if (mtmp2 && !amorphous(mtmp2->data) && + !passes_walls(mtmp2->data) && + !noncorporeal(mtmp2->data) && + !unsolid(mtmp2->data)) { + struct obj *helmet = which_armor(mtmp2, W_ARMH); + int mdmg; + + if (cansee(mtmp2->mx, mtmp2->my)) { + pline("%s is hit by %s!", Monnam(mtmp2), + doname(otmp2)); + if (mtmp2->minvis && !canspotmon(mtmp2)) + map_invisible(mtmp2->mx, mtmp2->my); + } + mdmg = dmgval(otmp2, mtmp2) * otmp2->quan; + if (helmet) { + if(is_metallic(helmet)) { + if (canspotmon(mtmp2)) + pline("Fortunately, %s is wearing a hard helmet.", mon_nam(mtmp2)); + else if (flags.soundok) + You_hear("a clanging sound."); + if (mdmg > 2) mdmg = 2; + } else { + if (canspotmon(mtmp2)) + pline("%s's %s does not protect %s.", + Monnam(mtmp2), xname(helmet), + mhim(mtmp2)); + } + } + mtmp2->mhp -= mdmg; + if (mtmp2->mhp <= 0) { + pline("%s is killed.", Monnam(mtmp2)); + mondied(mtmp2); + } + } + /* Drop the rock/boulder to the floor */ + if (!flooreffects(otmp2, x, y, "fall")) { + place_object(otmp2, x, y); + stackobj(otmp2); + newsym(x, y); /* map the rock */ + } + } + } + } + m_useup(mtmp, otmp); + /* Attack the player */ + if (distmin(mmx, mmy, u.ux, u.uy) == 1 && !otmp->cursed) { + int dmg; + struct obj *otmp2; + + /* Okay, _you_ write this without repeating the code */ + otmp2 = mksobj(confused ? ROCK : BOULDER, + FALSE, FALSE); + if (!otmp2) goto xxx_noobj; /* Shouldn't happen */ + otmp2->quan = confused ? rn1(5,2) : 1; + otmp2->owt = weight(otmp2); + if (!amorphous(youmonst.data) && + !Passes_walls && + !noncorporeal(youmonst.data) && + !unsolid(youmonst.data)) { + You("are hit by %s!", doname(otmp2)); + dmg = dmgval(otmp2, &youmonst) * otmp2->quan; + if (uarmh) { + if(is_metallic(uarmh)) { + pline("Fortunately, you are wearing a hard helmet."); + if (dmg > 2) dmg = 2; + } else if (flags.verbose) { + Your("%s does not protect you.", + xname(uarmh)); + } + } + } else + dmg = 0; + if (!flooreffects(otmp2, u.ux, u.uy, "fall")) { + place_object(otmp2, u.ux, u.uy); + stackobj(otmp2); + newsym(u.ux, u.uy); + } + if (dmg) losehp(dmg, "scroll of earth", KILLED_BY_AN); + } + xxx_noobj: + + return (mtmp->mhp <= 0) ? 1 : 2; + } +#if 0 + case MUSE_SCR_FIRE: + { + boolean vis = cansee(mtmp->mx, mtmp->my); + + mreadmsg(mtmp, otmp); + if (mtmp->mconf) { + if (vis) + pline("Oh, what a pretty fire!"); + } else { + struct monst *mtmp2; + int num; + + if (vis) + pline_The("scroll erupts in a tower of flame!"); + shieldeff(mtmp->mx, mtmp->my); + pline("%s is uninjured.", Monnam(mtmp)); + (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); + num = (2*(rn1(3, 3) + 2 * bcsign(otmp)) + 1)/3; + if (Fire_resistance) + You("are not harmed."); + burn_away_slime(); + if (Half_spell_damage) num = (num+1) / 2; + else losehp(num, "scroll of fire", KILLED_BY_AN); + for(mtmp2 = fmon; mtmp2; mtmp2 = mtmp2->nmon) { + if(DEADMONSTER(mtmp2)) continue; + if(mtmp == mtmp2) continue; + if(dist2(mtmp2->mx,mtmp2->my,mtmp->mx,mtmp->my) < 3){ + if (resists_fire(mtmp2)) continue; + mtmp2->mhp -= num; + if (resists_cold(mtmp2)) + mtmp2->mhp -= 3*num; + if(mtmp2->mhp < 1) { + mondied(mtmp2); + break; + } + } + } + } + return 2; + } +#endif /* 0 */ + case MUSE_POT_PARALYSIS: + case MUSE_POT_BLINDNESS: + case MUSE_POT_CONFUSION: + case MUSE_POT_SLEEPING: + case MUSE_POT_ACID: + /* Note: this setting of dknown doesn't suffice. A monster + * which is out of sight might throw and it hits something _in_ + * sight, a problem not existing with wands because wand rays + * are not objects. Also set dknown in mthrowu.c. + */ + if (cansee(mtmp->mx, mtmp->my)) { + otmp->dknown = 1; + pline("%s hurls %s!", Monnam(mtmp), + singular(otmp, doname)); + } + m_throw(mtmp, mtmp->mx, mtmp->my, sgn(mtmp->mux-mtmp->mx), + sgn(mtmp->muy-mtmp->my), + distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp); + return 2; + case 0: return 0; /* i.e. an exploded wand */ + default: impossible("%s wanted to perform action %d?", Monnam(mtmp), + m.has_offense); + break; + } + return 0; +} + +int +rnd_offensive_item(mtmp) +struct monst *mtmp; +{ + struct permonst *pm = mtmp->data; + int difficulty = monstr[(monsndx(pm))]; + + if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data) + || pm->mlet == S_GHOST +# ifdef KOPS + || pm->mlet == S_KOP +# endif + ) return 0; + if (difficulty > 7 && !rn2(35)) return WAN_DEATH; + switch (rn2(9 - (difficulty < 4) + 4 * (difficulty > 6))) { + case 0: { + struct obj *helmet = which_armor(mtmp, W_ARMH); + + if ((helmet && is_metallic(helmet)) || amorphous(pm) || passes_walls(pm) || noncorporeal(pm) || unsolid(pm)) + return SCR_EARTH; + } /* fall through */ + case 1: return WAN_STRIKING; + case 2: return POT_ACID; + case 3: return POT_CONFUSION; + case 4: return POT_BLINDNESS; + case 5: return POT_SLEEPING; + case 6: return POT_PARALYSIS; + case 7: case 8: + return WAN_MAGIC_MISSILE; + case 9: return WAN_SLEEP; + case 10: return WAN_FIRE; + case 11: return WAN_COLD; + case 12: return WAN_LIGHTNING; + } + /*NOTREACHED*/ + return 0; +} + +#define MUSE_POT_GAIN_LEVEL 1 +#define MUSE_WAN_MAKE_INVISIBLE 2 +#define MUSE_POT_INVISIBILITY 3 +#define MUSE_POLY_TRAP 4 +#define MUSE_WAN_POLYMORPH 5 +#define MUSE_POT_SPEED 6 +#define MUSE_WAN_SPEED_MONSTER 7 +#define MUSE_BULLWHIP 8 +#define MUSE_POT_POLYMORPH 9 + +boolean +find_misc(mtmp) +struct monst *mtmp; +{ + register struct obj *obj; + struct permonst *mdat = mtmp->data; + int x = mtmp->mx, y = mtmp->my; + struct trap *t; + int xx, yy; + boolean immobile = (mdat->mmove == 0); + boolean stuck = (mtmp == u.ustuck); + + m.misc = (struct obj *)0; + m.has_misc = 0; + if (is_animal(mdat) || mindless(mdat)) + return 0; + if (u.uswallow && stuck) return FALSE; + + /* We arbitrarily limit to times when a player is nearby for the + * same reason as Junior Pac-Man doesn't have energizers eaten until + * you can see them... + */ + if(dist2(x, y, mtmp->mux, mtmp->muy) > 36) + return FALSE; + + if (!stuck && !immobile && !mtmp->cham && monstr[monsndx(mdat)] < 6) { + boolean ignore_boulders = (verysmall(mdat) || + throws_rocks(mdat) || + passes_walls(mdat)); + for(xx = x-1; xx <= x+1; xx++) + for(yy = y-1; yy <= y+1; yy++) + if (isok(xx,yy) && (xx != u.ux || yy != u.uy)) + if (mdat != &mons[PM_GRID_BUG] || xx == x || yy == y) + if (/* (xx==x && yy==y) || */ !level.monsters[xx][yy]) + if ((t = t_at(xx, yy)) != 0 && + (ignore_boulders || !sobj_at(BOULDER, xx, yy)) + && !onscary(xx, yy, mtmp)) { + if (t->ttyp == POLY_TRAP) { + trapx = xx; + trapy = yy; + m.has_misc = MUSE_POLY_TRAP; + return TRUE; + } + } + } + if (nohands(mdat)) + return 0; + +#define nomore(x) if(m.has_misc==x) continue; + for(obj=mtmp->minvent; obj; obj=obj->nobj) { + /* Monsters shouldn't recognize cursed items; this kludge is */ + /* necessary to prevent serious problems though... */ + if(obj->otyp == POT_GAIN_LEVEL && (!obj->cursed || + (!mtmp->isgd && !mtmp->isshk && !mtmp->ispriest))) { + m.misc = obj; + m.has_misc = MUSE_POT_GAIN_LEVEL; + } + nomore(MUSE_BULLWHIP); + if(obj->otyp == BULLWHIP && (MON_WEP(mtmp) == obj) && + distu(mtmp->mx,mtmp->my)==1 && uwep && !mtmp->mpeaceful) { + m.misc = obj; + m.has_misc = MUSE_BULLWHIP; + } + /* Note: peaceful/tame monsters won't make themselves + * invisible unless you can see them. Not really right, but... + */ + nomore(MUSE_WAN_MAKE_INVISIBLE); + if(obj->otyp == WAN_MAKE_INVISIBLE && obj->spe > 0 && + !mtmp->minvis && !mtmp->invis_blkd && + (!mtmp->mpeaceful || See_invisible) && + (!attacktype(mtmp->data, AT_GAZE) || mtmp->mcan)) { + m.misc = obj; + m.has_misc = MUSE_WAN_MAKE_INVISIBLE; + } + nomore(MUSE_POT_INVISIBILITY); + if(obj->otyp == POT_INVISIBILITY && + !mtmp->minvis && !mtmp->invis_blkd && + (!mtmp->mpeaceful || See_invisible) && + (!attacktype(mtmp->data, AT_GAZE) || mtmp->mcan)) { + m.misc = obj; + m.has_misc = MUSE_POT_INVISIBILITY; + } + nomore(MUSE_WAN_SPEED_MONSTER); + if(obj->otyp == WAN_SPEED_MONSTER && obj->spe > 0 + && mtmp->mspeed != MFAST && !mtmp->isgd) { + m.misc = obj; + m.has_misc = MUSE_WAN_SPEED_MONSTER; + } + nomore(MUSE_POT_SPEED); + if(obj->otyp == POT_SPEED && mtmp->mspeed != MFAST + && !mtmp->isgd) { + m.misc = obj; + m.has_misc = MUSE_POT_SPEED; + } + nomore(MUSE_WAN_POLYMORPH); + if(obj->otyp == WAN_POLYMORPH && obj->spe > 0 && !mtmp->cham + && monstr[monsndx(mdat)] < 6) { + m.misc = obj; + m.has_misc = MUSE_WAN_POLYMORPH; + } + nomore(MUSE_POT_POLYMORPH); + if(obj->otyp == POT_POLYMORPH && !mtmp->cham + && monstr[monsndx(mdat)] < 6) { + m.misc = obj; + m.has_misc = MUSE_POT_POLYMORPH; + } + } + return((boolean)(!!m.has_misc)); +#undef nomore +} + +/* type of monster to polymorph into; defaults to one suitable for the + current level rather than the totally arbitrary choice of newcham() */ +static struct permonst * +muse_newcham_mon(mon) +struct monst *mon; +{ + struct obj *m_armr; + + if ((m_armr = which_armor(mon, W_ARM)) != 0) { + if (Is_dragon_scales(m_armr)) + return Dragon_scales_to_pm(m_armr); + else if (Is_dragon_mail(m_armr)) + return Dragon_mail_to_pm(m_armr); + } + return rndmonst(); +} + +int +use_misc(mtmp) +struct monst *mtmp; +{ + int i; + struct obj *otmp = m.misc; + boolean vis, vismon, oseen; + char nambuf[BUFSZ]; + + if ((i = precheck(mtmp, otmp)) != 0) return i; + vis = cansee(mtmp->mx, mtmp->my); + vismon = canseemon(mtmp); + oseen = otmp && vismon; + + switch(m.has_misc) { + case MUSE_POT_GAIN_LEVEL: + mquaffmsg(mtmp, otmp); + if (otmp->cursed) { + if (Can_rise_up(mtmp->mx, mtmp->my, &u.uz)) { + register int tolev = depth(&u.uz)-1; + d_level tolevel; + + get_level(&tolevel, tolev); + /* insurance against future changes... */ + if(on_level(&tolevel, &u.uz)) goto skipmsg; + if (vismon) { + pline("%s rises up, through the %s!", + Monnam(mtmp), ceiling(mtmp->mx, mtmp->my)); + if(!objects[POT_GAIN_LEVEL].oc_name_known + && !objects[POT_GAIN_LEVEL].oc_uname) + docall(otmp); + } + m_useup(mtmp, otmp); + migrate_to_level(mtmp, ledger_no(&tolevel), + MIGR_RANDOM, (coord *)0); + return 2; + } else { +skipmsg: + if (vismon) { + pline("%s looks uneasy.", Monnam(mtmp)); + if(!objects[POT_GAIN_LEVEL].oc_name_known + && !objects[POT_GAIN_LEVEL].oc_uname) + docall(otmp); + } + m_useup(mtmp, otmp); + return 2; + } + } + if (vismon) pline("%s seems more experienced.", Monnam(mtmp)); + if (oseen) makeknown(POT_GAIN_LEVEL); + m_useup(mtmp, otmp); + if (!grow_up(mtmp,(struct monst *)0)) return 1; + /* grew into genocided monster */ + return 2; + case MUSE_WAN_MAKE_INVISIBLE: + case MUSE_POT_INVISIBILITY: + if (otmp->otyp == WAN_MAKE_INVISIBLE) { + mzapmsg(mtmp, otmp, TRUE); + otmp->spe--; + } else + mquaffmsg(mtmp, otmp); + /* format monster's name before altering its visibility */ + Strcpy(nambuf, See_invisible ? Monnam(mtmp) : mon_nam(mtmp)); + mon_set_minvis(mtmp); + if (vismon && mtmp->minvis) { /* was seen, now invisible */ + if (See_invisible) + pline("%s body takes on a %s transparency.", + s_suffix(nambuf), + Hallucination ? "normal" : "strange"); + else + pline("Suddenly you cannot see %s.", nambuf); + if (oseen) makeknown(otmp->otyp); + } + if (otmp->otyp == POT_INVISIBILITY) { + if (otmp->cursed) you_aggravate(mtmp); + m_useup(mtmp, otmp); + } + return 2; + case MUSE_WAN_SPEED_MONSTER: + mzapmsg(mtmp, otmp, TRUE); + otmp->spe--; + mon_adjust_speed(mtmp, 1, otmp); + return 2; + case MUSE_POT_SPEED: + mquaffmsg(mtmp, otmp); + /* note difference in potion effect due to substantially + different methods of maintaining speed ratings: + player's character becomes "very fast" temporarily; + monster becomes "one stage faster" permanently */ + mon_adjust_speed(mtmp, 1, otmp); + m_useup(mtmp, otmp); + return 2; + case MUSE_WAN_POLYMORPH: + mzapmsg(mtmp, otmp, TRUE); + otmp->spe--; + (void) newcham(mtmp, muse_newcham_mon(mtmp), TRUE, FALSE); + if (oseen) makeknown(WAN_POLYMORPH); + return 2; + case MUSE_POT_POLYMORPH: + mquaffmsg(mtmp, otmp); + if (vismon) pline("%s suddenly mutates!", Monnam(mtmp)); + (void) newcham(mtmp, muse_newcham_mon(mtmp), FALSE, FALSE); + if (oseen) makeknown(POT_POLYMORPH); + m_useup(mtmp, otmp); + return 2; + case MUSE_POLY_TRAP: + if (vismon) + pline("%s deliberately %s onto a polymorph trap!", + Monnam(mtmp), + makeplural(locomotion(mtmp->data, "jump"))); + if (vis) seetrap(t_at(trapx,trapy)); + + /* don't use rloc() due to worms */ + remove_monster(mtmp->mx, mtmp->my); + newsym(mtmp->mx, mtmp->my); + place_monster(mtmp, trapx, trapy); + if (mtmp->wormno) worm_move(mtmp); + newsym(trapx, trapy); + + (void) newcham(mtmp, (struct permonst *)0, FALSE, FALSE); + return 2; + case MUSE_BULLWHIP: + /* attempt to disarm hero */ + if (uwep && !rn2(5)) { + const char *The_whip = vismon ? "The bullwhip" : "A whip"; + int where_to = rn2(4); + struct obj *obj = uwep; + const char *hand; + char the_weapon[BUFSZ]; + + Strcpy(the_weapon, the(xname(obj))); + hand = body_part(HAND); + if (bimanual(obj)) hand = makeplural(hand); + + if (vismon) + pline("%s flicks a bullwhip towards your %s!", + Monnam(mtmp), hand); + if (obj->otyp == HEAVY_IRON_BALL) { + pline("%s fails to wrap around %s.", + The_whip, the_weapon); + return 1; + } + pline("%s wraps around %s you're wielding!", + The_whip, the_weapon); + if (welded(obj)) { + pline("%s welded to your %s%c", + !is_plural(obj) ? "It is" : "They are", + hand, !obj->bknown ? '!' : '.'); + /* obj->bknown = 1; */ /* welded() takes care of this */ + where_to = 0; + } + if (!where_to) { + pline_The("whip slips free."); /* not `The_whip' */ + return 1; + } else if (where_to == 3 && hates_silver(mtmp->data) && + objects[obj->otyp].oc_material == SILVER) { + /* this monster won't want to catch a silver + weapon; drop it at hero's feet instead */ + where_to = 2; + } + freeinv(obj); + uwepgone(); + switch (where_to) { + case 1: /* onto floor beneath mon */ + pline("%s yanks %s from your %s!", Monnam(mtmp), + the_weapon, hand); + place_object(obj, mtmp->mx, mtmp->my); + break; + case 2: /* onto floor beneath you */ + pline("%s yanks %s to the %s!", Monnam(mtmp), + the_weapon, surface(u.ux, u.uy)); + dropy(obj); + break; + case 3: /* into mon's inventory */ + pline("%s snatches %s!", Monnam(mtmp), + the_weapon); + (void) mpickobj(mtmp,obj); + break; + } + return 1; + } + return 0; + case 0: return 0; /* i.e. an exploded wand */ + default: impossible("%s wanted to perform action %d?", Monnam(mtmp), + m.has_misc); + break; + } + return 0; +} + +STATIC_OVL void +you_aggravate(mtmp) +struct monst *mtmp; +{ + pline("For some reason, %s presence is known to you.", + s_suffix(noit_mon_nam(mtmp))); + cls(); +#ifdef CLIPPING + cliparound(mtmp->mx, mtmp->my); +#endif + show_glyph(mtmp->mx, mtmp->my, mon_to_glyph(mtmp)); + display_self(); + You_feel("aggravated at %s.", noit_mon_nam(mtmp)); + display_nhwindow(WIN_MAP, TRUE); + docrt(); + if (unconscious()) { + multi = -1; + nomovemsg = + "Aggravated, you are jolted into full consciousness."; + } + newsym(mtmp->mx,mtmp->my); + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); +} + +int +rnd_misc_item(mtmp) +struct monst *mtmp; +{ + struct permonst *pm = mtmp->data; + int difficulty = monstr[(monsndx(pm))]; + + if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data) + || pm->mlet == S_GHOST +# ifdef KOPS + || pm->mlet == S_KOP +# endif + ) return 0; + /* Unlike other rnd_item functions, we only allow _weak_ monsters + * to have this item; after all, the item will be used to strengthen + * the monster and strong monsters won't use it at all... + */ + if (difficulty < 6 && !rn2(30)) + return rn2(6) ? POT_POLYMORPH : WAN_POLYMORPH; + + if (!rn2(40) && !nonliving(pm)) return AMULET_OF_LIFE_SAVING; + + switch (rn2(3)) { + case 0: + if (mtmp->isgd) return 0; + return rn2(6) ? POT_SPEED : WAN_SPEED_MONSTER; + case 1: + if (mtmp->mpeaceful && !See_invisible) return 0; + return rn2(6) ? POT_INVISIBILITY : WAN_MAKE_INVISIBLE; + case 2: + return POT_GAIN_LEVEL; + } + /*NOTREACHED*/ + return 0; +} + +boolean +searches_for_item(mon, obj) +struct monst *mon; +struct obj *obj; +{ + int typ = obj->otyp; + + if (is_animal(mon->data) || + mindless(mon->data) || + mon->data == &mons[PM_GHOST]) /* don't loot bones piles */ + return FALSE; + + if (typ == WAN_MAKE_INVISIBLE || typ == POT_INVISIBILITY) + return (boolean)(!mon->minvis && !mon->invis_blkd && !attacktype(mon->data, AT_GAZE)); + if (typ == WAN_SPEED_MONSTER || typ == POT_SPEED) + return (boolean)(mon->mspeed != MFAST); + + switch (obj->oclass) { + case WAND_CLASS: + if (obj->spe <= 0) + return FALSE; + if (typ == WAN_DIGGING) + return (boolean)(!is_floater(mon->data)); + if (typ == WAN_POLYMORPH) + return (boolean)(monstr[monsndx(mon->data)] < 6); + if (objects[typ].oc_dir == RAY || + typ == WAN_STRIKING || + typ == WAN_TELEPORTATION || + typ == WAN_CREATE_MONSTER) + return TRUE; + break; + case POTION_CLASS: + if (typ == POT_HEALING || + typ == POT_EXTRA_HEALING || + typ == POT_FULL_HEALING || + typ == POT_POLYMORPH || + typ == POT_GAIN_LEVEL || + typ == POT_PARALYSIS || + typ == POT_SLEEPING || + typ == POT_ACID || + typ == POT_CONFUSION) + return TRUE; + if (typ == POT_BLINDNESS && !attacktype(mon->data, AT_GAZE)) + return TRUE; + break; + case SCROLL_CLASS: + if (typ == SCR_TELEPORTATION || typ == SCR_CREATE_MONSTER + || typ == SCR_EARTH) + return TRUE; + break; + case AMULET_CLASS: + if (typ == AMULET_OF_LIFE_SAVING) + return (boolean)(!nonliving(mon->data)); + if (typ == AMULET_OF_REFLECTION) + return TRUE; + break; + case TOOL_CLASS: + if (typ == PICK_AXE) + return (boolean)needspick(mon->data); + if (typ == UNICORN_HORN) + return (boolean)(!obj->cursed && !is_unicorn(mon->data)); + if (typ == FROST_HORN || typ == FIRE_HORN) + return (obj->spe > 0); + break; + case FOOD_CLASS: + if (typ == CORPSE) + return (boolean)(((mon->misc_worn_check & W_ARMG) && + touch_petrifies(&mons[obj->corpsenm])) || + (!resists_ston(mon) && + (obj->corpsenm == PM_LIZARD || + (acidic(&mons[obj->corpsenm]) && + obj->corpsenm != PM_GREEN_SLIME)))); + if (typ == EGG) + return (boolean)(touch_petrifies(&mons[obj->corpsenm])); + break; + default: + break; + } + + return FALSE; +} + +boolean +mon_reflects(mon,str) +struct monst *mon; +const char *str; +{ + struct obj *orefl = which_armor(mon, W_ARMS); + + if (orefl && orefl->otyp == SHIELD_OF_REFLECTION) { + if (str) { + pline(str, s_suffix(mon_nam(mon)), "shield"); + makeknown(SHIELD_OF_REFLECTION); + } + return TRUE; + } else if (arti_reflects(MON_WEP(mon))) { + /* due to wielded artifact weapon */ + if (str) + pline(str, s_suffix(mon_nam(mon)), "weapon"); + return TRUE; + } else if ((orefl = which_armor(mon, W_AMUL)) && + orefl->otyp == AMULET_OF_REFLECTION) { + if (str) { + pline(str, s_suffix(mon_nam(mon)), "amulet"); + makeknown(AMULET_OF_REFLECTION); + } + return TRUE; + } else if ((orefl = which_armor(mon, W_ARM)) && + (orefl->otyp == SILVER_DRAGON_SCALES || orefl->otyp == SILVER_DRAGON_SCALE_MAIL)) { + if (str) + pline(str, s_suffix(mon_nam(mon)), "armor"); + return TRUE; + } else if (mon->data == &mons[PM_SILVER_DRAGON] || + mon->data == &mons[PM_CHROMATIC_DRAGON]) { + /* Silver dragons only reflect when mature; babies do not */ + if (str) + pline(str, s_suffix(mon_nam(mon)), "scales"); + return TRUE; + } + return FALSE; +} + +boolean +ureflects (fmt, str) +const char *fmt, *str; +{ + /* Check from outermost to innermost objects */ + if (EReflecting & W_ARMS) { + if (fmt && str) { + pline(fmt, str, "shield"); + makeknown(SHIELD_OF_REFLECTION); + } + return TRUE; + } else if (EReflecting & W_WEP) { + /* Due to wielded artifact weapon */ + if (fmt && str) + pline(fmt, str, "weapon"); + return TRUE; + } else if (EReflecting & W_AMUL) { + if (fmt && str) { + pline(fmt, str, "medallion"); + makeknown(AMULET_OF_REFLECTION); + } + return TRUE; + } else if (EReflecting & W_ARM) { + if (fmt && str) + pline(fmt, str, "armor"); + return TRUE; + } else if (youmonst.data == &mons[PM_SILVER_DRAGON]) { + if (fmt && str) + pline(fmt, str, "scales"); + return TRUE; + } + return FALSE; +} + + +/* TRUE if the monster ate something */ +boolean +munstone(mon, by_you) +struct monst *mon; +boolean by_you; +{ + struct obj *obj; + + if (resists_ston(mon)) return FALSE; + if (mon->meating || !mon->mcanmove || mon->msleeping) return FALSE; + + for(obj = mon->minvent; obj; obj = obj->nobj) { + /* Monsters can also use potions of acid */ + if ((obj->otyp == POT_ACID) || (obj->otyp == CORPSE && + (obj->corpsenm == PM_LIZARD || (acidic(&mons[obj->corpsenm]) && obj->corpsenm != PM_GREEN_SLIME)))) { + mon_consume_unstone(mon, obj, by_you, TRUE); + return TRUE; + } + } + return FALSE; +} + +STATIC_OVL void +mon_consume_unstone(mon, obj, by_you, stoning) +struct monst *mon; +struct obj *obj; +boolean by_you; +boolean stoning; +{ + int nutrit = (obj->otyp == CORPSE) ? dog_nutrition(mon, obj) : 0; + /* also sets meating */ + + /* give a " is slowing down" message and also remove + intrinsic speed (comparable to similar effect on the hero) */ + mon_adjust_speed(mon, -3, (struct obj *)0); + + if (canseemon(mon)) { + long save_quan = obj->quan; + + obj->quan = 1L; + pline("%s %ss %s.", Monnam(mon), + (obj->otyp == POT_ACID) ? "quaff" : "eat", + distant_name(obj,doname)); + obj->quan = save_quan; + } else if (flags.soundok) + You_hear("%s.", (obj->otyp == POT_ACID) ? "drinking" : "chewing"); + m_useup(mon, obj); + if (((obj->otyp == POT_ACID) || acidic(&mons[obj->corpsenm])) && + !resists_acid(mon)) { + mon->mhp -= rnd(15); + pline("%s has a very bad case of stomach acid.", + Monnam(mon)); + } + if (mon->mhp <= 0) { + pline("%s dies!", Monnam(mon)); + if (by_you) xkilled(mon, 0); + else mondead(mon); + return; + } + if (stoning && canseemon(mon)) { + if (Hallucination) + pline("What a pity - %s just ruined a future piece of art!", + mon_nam(mon)); + else + pline("%s seems limber!", Monnam(mon)); + } + if (obj->otyp == CORPSE && obj->corpsenm == PM_LIZARD && mon->mconf) { + mon->mconf = 0; + if (canseemon(mon)) + pline("%s seems steadier now.", Monnam(mon)); + } + if (mon->mtame && !mon->isminion && nutrit > 0) { + struct edog *edog = EDOG(mon); + + if (edog->hungrytime < monstermoves) edog->hungrytime = monstermoves; + edog->hungrytime += nutrit; + mon->mconf = 0; + } + mon->mlstmv = monstermoves; /* it takes a turn */ +} + +/*muse.c*/ diff --git a/src/music.c b/src/music.c new file mode 100644 index 0000000..e7391d8 --- /dev/null +++ b/src/music.c @@ -0,0 +1,755 @@ +/* SCCS Id: @(#)music.c 3.4 2003/05/25 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the different functions designed to manipulate the + * musical instruments and their various effects. + * + * Actually the list of instruments / effects is : + * + * (wooden) flute may calm snakes if player has enough dexterity + * magic flute may put monsters to sleep: area of effect depends + * on player level. + * (tooled) horn Will awaken monsters: area of effect depends on player + * level. May also scare monsters. + * fire horn Acts like a wand of fire. + * frost horn Acts like a wand of cold. + * bugle Will awaken soldiers (if any): area of effect depends + * on player level. + * (wooden) harp May calm nymph if player has enough dexterity. + * magic harp Charm monsters: area of effect depends on player + * level. + * (leather) drum Will awaken monsters like the horn. + * drum of earthquake Will initiate an earthquake whose intensity depends + * on player level. That is, it creates random pits + * called here chasms. + */ + +#include "hack.h" + +STATIC_DCL void FDECL(awaken_monsters,(int)); +STATIC_DCL void FDECL(put_monsters_to_sleep,(int)); +STATIC_DCL void FDECL(charm_snakes,(int)); +STATIC_DCL void FDECL(calm_nymphs,(int)); +STATIC_DCL void FDECL(charm_monsters,(int)); +STATIC_DCL void FDECL(do_earthquake,(int)); +STATIC_DCL int FDECL(do_improvisation,(struct obj *)); + +#ifdef UNIX386MUSIC +STATIC_DCL int NDECL(atconsole); +STATIC_DCL void FDECL(speaker,(struct obj *,char *)); +#endif +#ifdef VPIX_MUSIC +extern int sco_flag_console; /* will need changing if not _M_UNIX */ +STATIC_DCL void NDECL(playinit); +STATIC_DCL void FDECL(playstring, (char *,size_t)); +STATIC_DCL void FDECL(speaker,(struct obj *,char *)); +#endif +#ifdef PCMUSIC +void FDECL( pc_speaker, ( struct obj *, char * ) ); +#endif +#ifdef AMIGA +void FDECL( amii_speaker, ( struct obj *, char *, int ) ); +#endif + +/* + * Wake every monster in range... + */ + +STATIC_OVL void +awaken_monsters(distance) +int distance; +{ + register struct monst *mtmp = fmon; + register int distm; + + while(mtmp) { + if (!DEADMONSTER(mtmp)) { + distm = distu(mtmp->mx, mtmp->my); + if (distm < distance) { + mtmp->msleeping = 0; + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + /* May scare some monsters */ + if (distm < distance/3 && + !resist(mtmp, TOOL_CLASS, 0, NOTELL)) + monflee(mtmp, 0, FALSE, TRUE); + } + } + mtmp = mtmp->nmon; + } +} + +/* + * Make monsters fall asleep. Note that they may resist the spell. + */ + +STATIC_OVL void +put_monsters_to_sleep(distance) +int distance; +{ + register struct monst *mtmp = fmon; + + while(mtmp) { + if (!DEADMONSTER(mtmp) && distu(mtmp->mx, mtmp->my) < distance && + sleep_monst(mtmp, d(10,10), TOOL_CLASS)) { + mtmp->msleeping = 1; /* 10d10 turns + wake_nearby to rouse */ + slept_monst(mtmp); + } + mtmp = mtmp->nmon; + } +} + +/* + * Charm snakes in range. Note that the snakes are NOT tamed. + */ + +STATIC_OVL void +charm_snakes(distance) +int distance; +{ + register struct monst *mtmp = fmon; + int could_see_mon, was_peaceful; + + while (mtmp) { + if (!DEADMONSTER(mtmp) && mtmp->data->mlet == S_SNAKE && mtmp->mcanmove && + distu(mtmp->mx, mtmp->my) < distance) { + was_peaceful = mtmp->mpeaceful; + mtmp->mpeaceful = 1; + mtmp->mavenge = 0; + could_see_mon = canseemon(mtmp); + mtmp->mundetected = 0; + newsym(mtmp->mx, mtmp->my); + if (canseemon(mtmp)) { + if (!could_see_mon) + You("notice %s, swaying with the music.", + a_monnam(mtmp)); + else + pline("%s freezes, then sways with the music%s.", + Monnam(mtmp), + was_peaceful ? "" : ", and now seems quieter"); + } + } + mtmp = mtmp->nmon; + } +} + +/* + * Calm nymphs in range. + */ + +STATIC_OVL void +calm_nymphs(distance) +int distance; +{ + register struct monst *mtmp = fmon; + + while (mtmp) { + if (!DEADMONSTER(mtmp) && mtmp->data->mlet == S_NYMPH && mtmp->mcanmove && + distu(mtmp->mx, mtmp->my) < distance) { + mtmp->msleeping = 0; + mtmp->mpeaceful = 1; + mtmp->mavenge = 0; + if (canseemon(mtmp)) + pline( + "%s listens cheerfully to the music, then seems quieter.", + Monnam(mtmp)); + } + mtmp = mtmp->nmon; + } +} + +/* Awake only soldiers of the level. */ + +void +awaken_soldiers() +{ + register struct monst *mtmp = fmon; + + while(mtmp) { + if (!DEADMONSTER(mtmp) && + is_mercenary(mtmp->data) && mtmp->data != &mons[PM_GUARD]) { + mtmp->mpeaceful = mtmp->msleeping = mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + if (canseemon(mtmp)) + pline("%s is now ready for battle!", Monnam(mtmp)); + else + Norep("You hear the rattle of battle gear being readied."); + } + mtmp = mtmp->nmon; + } +} + +/* Charm monsters in range. Note that they may resist the spell. + * If swallowed, range is reduced to 0. + */ + +STATIC_OVL void +charm_monsters(distance) +int distance; +{ + struct monst *mtmp, *mtmp2; + + if (u.uswallow) { + if (!resist(u.ustuck, TOOL_CLASS, 0, NOTELL)) + (void) tamedog(u.ustuck, (struct obj *) 0); + } else { + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp)) continue; + + if (distu(mtmp->mx, mtmp->my) <= distance) { + if (!resist(mtmp, TOOL_CLASS, 0, NOTELL)) + (void) tamedog(mtmp, (struct obj *) 0); + } + } + } + +} + +/* Generate earthquake :-) of desired force. + * That is: create random chasms (pits). + */ + +STATIC_OVL void +do_earthquake(force) +int force; +{ + register int x,y; + struct monst *mtmp; + struct obj *otmp; + struct trap *chasm; + int start_x, start_y, end_x, end_y; + + start_x = u.ux - (force * 2); + start_y = u.uy - (force * 2); + end_x = u.ux + (force * 2); + end_y = u.uy + (force * 2); + if (start_x < 1) start_x = 1; + if (start_y < 1) start_y = 1; + if (end_x >= COLNO) end_x = COLNO - 1; + if (end_y >= ROWNO) end_y = ROWNO - 1; + for (x=start_x; x<=end_x; x++) for (y=start_y; y<=end_y; y++) { + if ((mtmp = m_at(x,y)) != 0) { + wakeup(mtmp); /* peaceful monster will become hostile */ + if (mtmp->mundetected && is_hider(mtmp->data)) { + mtmp->mundetected = 0; + if (cansee(x,y)) + pline("%s is shaken loose from the ceiling!", + Amonnam(mtmp)); + else + You_hear("a thumping sound."); + if (x==u.ux && y==u.uy) + You("easily dodge the falling %s.", + mon_nam(mtmp)); + newsym(x,y); + } + } + if (!rn2(14 - force)) switch (levl[x][y].typ) { + case FOUNTAIN : /* Make the fountain disappear */ + if (cansee(x,y)) + pline_The("fountain falls into a chasm."); + goto do_pit; +#ifdef SINKS + case SINK : + if (cansee(x,y)) + pline_The("kitchen sink falls into a chasm."); + goto do_pit; +#endif + case ALTAR : + if (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)) break; + + if (cansee(x,y)) + pline_The("altar falls into a chasm."); + goto do_pit; + case GRAVE : + if (cansee(x,y)) + pline_The("headstone topples into a chasm."); + goto do_pit; + case THRONE : + if (cansee(x,y)) + pline_The("throne falls into a chasm."); + /* Falls into next case */ + case ROOM : + case CORR : /* Try to make a pit */ +do_pit: chasm = maketrap(x,y,PIT); + if (!chasm) break; /* no pit if portal at that location */ + chasm->tseen = 1; + + levl[x][y].doormask = 0; + + mtmp = m_at(x,y); + + if ((otmp = sobj_at(BOULDER, x, y)) != 0) { + if (cansee(x, y)) + pline("KADOOM! The boulder falls into a chasm%s!", + ((x == u.ux) && (y == u.uy)) ? " below you" : ""); + if (mtmp) + mtmp->mtrapped = 0; + obj_extract_self(otmp); + (void) flooreffects(otmp, x, y, ""); + break; + } + + /* We have to check whether monsters or player + falls in a chasm... */ + + if (mtmp) { + if(!is_flyer(mtmp->data) && !is_clinger(mtmp->data)) { + mtmp->mtrapped = 1; + if(cansee(x,y)) + pline("%s falls into a chasm!", Monnam(mtmp)); + else if (flags.soundok && humanoid(mtmp->data)) + You_hear("a scream!"); + mselftouch(mtmp, "Falling, ", TRUE); + if (mtmp->mhp > 0) + if ((mtmp->mhp -= rnd(6)) <= 0) { + if(!cansee(x,y)) + pline("It is destroyed!"); + else { + You("destroy %s!", mtmp->mtame ? + x_monnam(mtmp, ARTICLE_THE, "poor", + mtmp->mnamelth ? SUPPRESS_SADDLE : 0, FALSE): + mon_nam(mtmp)); + } + xkilled(mtmp,0); + } + } + } else if (x == u.ux && y == u.uy) { + if (Levitation || Flying || + is_clinger(youmonst.data)) { + pline("A chasm opens up under you!"); + You("don't fall in!"); + } else { + You("fall into a chasm!"); + u.utrap = rn1(6,2); + u.utraptype = TT_PIT; + losehp(rnd(6),"fell into a chasm", + NO_KILLER_PREFIX); + selftouch("Falling, you"); + } + } else newsym(x,y); + break; + case DOOR : /* Make the door collapse */ + if (levl[x][y].doormask == D_NODOOR) goto do_pit; + if (cansee(x,y)) + pline_The("door collapses."); + if (*in_rooms(x, y, SHOPBASE)) + add_damage(x, y, 0L); + levl[x][y].doormask = D_NODOOR; + unblock_point(x,y); + newsym(x,y); + break; + } + } +} + +/* + * The player is trying to extract something from his/her instrument. + */ + +STATIC_OVL int +do_improvisation(instr) +struct obj *instr; +{ + int damage, do_spec = !Confusion; +#if defined(MAC) || defined(AMIGA) || defined(VPIX_MUSIC) || defined (PCMUSIC) + struct obj itmp; + + itmp = *instr; + /* if won't yield special effect, make sound of mundane counterpart */ + if (!do_spec || instr->spe <= 0) + while (objects[itmp.otyp].oc_magic) itmp.otyp -= 1; +# ifdef MAC + mac_speaker(&itmp, "C"); +# endif +# ifdef AMIGA + amii_speaker(&itmp, "Cw", AMII_OKAY_VOLUME); +# endif +# ifdef VPIX_MUSIC + if (sco_flag_console) + speaker(&itmp, "C"); +# endif +#ifdef PCMUSIC + pc_speaker ( &itmp, "C"); +#endif +#endif /* MAC || AMIGA || VPIX_MUSIC || PCMUSIC */ + + if (!do_spec) + pline("What you produce is quite far from music..."); + else + You("start playing %s.", the(xname(instr))); + + switch (instr->otyp) { + case MAGIC_FLUTE: /* Make monster fall asleep */ + if (do_spec && instr->spe > 0) { + consume_obj_charge(instr, TRUE); + + You("produce soft music."); + put_monsters_to_sleep(u.ulevel * 5); + exercise(A_DEX, TRUE); + break; + } /* else FALLTHRU */ + case WOODEN_FLUTE: /* May charm snakes */ + do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); + pline("%s.", Tobjnam(instr, do_spec ? "trill" : "toot")); + if (do_spec) charm_snakes(u.ulevel * 3); + exercise(A_DEX, TRUE); + break; + case FROST_HORN: /* Idem wand of cold */ + case FIRE_HORN: /* Idem wand of fire */ + if (do_spec && instr->spe > 0) { + consume_obj_charge(instr, TRUE); + + if (!getdir((char *)0)) { + pline("%s.", Tobjnam(instr, "vibrate")); + break; + } else if (!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(instr, TRUE)) != 0) { + char buf[BUFSZ]; + Sprintf(buf, "using a magical horn on %sself", uhim()); + losehp(damage, buf, KILLED_BY); + } + } else { + buzz((instr->otyp == FROST_HORN) ? AD_COLD-1 : AD_FIRE-1, + rn1(6,6), u.ux, u.uy, u.dx, u.dy); + } + makeknown(instr->otyp); + break; + } /* else FALLTHRU */ + case TOOLED_HORN: /* Awaken or scare monsters */ + You("produce a frightful, grave sound."); + awaken_monsters(u.ulevel * 30); + exercise(A_WIS, FALSE); + break; + case BUGLE: /* Awaken & attract soldiers */ + You("extract a loud noise from %s.", the(xname(instr))); + awaken_soldiers(); + exercise(A_WIS, FALSE); + break; + case MAGIC_HARP: /* Charm monsters */ + if (do_spec && instr->spe > 0) { + consume_obj_charge(instr, TRUE); + + pline("%s very attractive music.", Tobjnam(instr, "produce")); + charm_monsters((u.ulevel - 1) / 3 + 1); + exercise(A_DEX, TRUE); + break; + } /* else FALLTHRU */ + case WOODEN_HARP: /* May calm Nymph */ + do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25); + pline("%s %s.", The(xname(instr)), + do_spec ? "produces a lilting melody" : "twangs"); + if (do_spec) calm_nymphs(u.ulevel * 3); + exercise(A_DEX, TRUE); + break; + case DRUM_OF_EARTHQUAKE: /* create several pits */ + if (do_spec && instr->spe > 0) { + consume_obj_charge(instr, TRUE); + + You("produce a heavy, thunderous rolling!"); + pline_The("entire dungeon is shaking around you!"); + do_earthquake((u.ulevel - 1) / 3 + 1); + /* shake up monsters in a much larger radius... */ + awaken_monsters(ROWNO * COLNO); + makeknown(DRUM_OF_EARTHQUAKE); + break; + } /* else FALLTHRU */ + case LEATHER_DRUM: /* Awaken monsters */ + You("beat a deafening row!"); + awaken_monsters(u.ulevel * 40); + exercise(A_WIS, FALSE); + break; + default: + impossible("What a weird instrument (%d)!", instr->otyp); + break; + } + return 2; /* That takes time */ +} + +/* + * So you want music... + */ + +int +do_play_instrument(instr) +struct obj *instr; +{ + char buf[BUFSZ], c = 'y'; + char *s; + int x,y; + boolean ok; + + if (Underwater) { + You_cant("play music underwater!"); + return(0); + } + if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE) { + c = yn("Improvise?"); + } + if (c == 'n') { + if (u.uevent.uheard_tune == 2 && yn("Play the passtune?") == 'y') { + Strcpy(buf, tune); + } else { + getlin("What tune are you playing? [5 notes, A-G]", buf); + (void)mungspaces(buf); + /* convert to uppercase and change any "H" to the expected "B" */ + for (s = buf; *s; s++) { +#ifndef AMIGA + *s = highc(*s); +#else + /* The AMIGA supports two octaves of notes */ + if (*s == 'h') *s = 'b'; +#endif + if (*s == 'H') *s = 'B'; + } + } + You("extract a strange sound from %s!", the(xname(instr))); +#ifdef UNIX386MUSIC + /* if user is at the console, play through the console speaker */ + if (atconsole()) + speaker(instr, buf); +#endif +#ifdef VPIX_MUSIC + if (sco_flag_console) + speaker(instr, buf); +#endif +#ifdef MAC + mac_speaker ( instr , buf ) ; +#endif +#ifdef PCMUSIC + pc_speaker ( instr, buf ); +#endif +#ifdef AMIGA + { + char nbuf[ 20 ]; + int i; + for( i = 0; buf[i] && i < 5; ++i ) + { + nbuf[ i*2 ] = buf[ i ]; + nbuf[ (i*2)+1 ] = 'h'; + } + nbuf[ i*2 ] = 0; + amii_speaker ( instr , nbuf, AMII_OKAY_VOLUME ) ; + } +#endif + /* Check if there was the Stronghold drawbridge near + * and if the tune conforms to what we're waiting for. + */ + if(Is_stronghold(&u.uz)) { + exercise(A_WIS, TRUE); /* just for trying */ + if(!strcmp(buf,tune)) { + /* Search for the drawbridge */ + for(y=u.uy-1; y<=u.uy+1; y++) + for(x=u.ux-1;x<=u.ux+1;x++) + if(isok(x,y)) + if(find_drawbridge(&x,&y)) { + u.uevent.uheard_tune = 2; /* tune now fully known */ + if(levl[x][y].typ == DRAWBRIDGE_DOWN) + close_drawbridge(x,y); + else + open_drawbridge(x,y); + return 0; + } + } else if(flags.soundok) { + if (u.uevent.uheard_tune < 1) u.uevent.uheard_tune = 1; + /* Okay, it wasn't the right tune, but perhaps + * we can give the player some hints like in the + * Mastermind game */ + ok = FALSE; + for(y = u.uy-1; y <= u.uy+1 && !ok; y++) + for(x = u.ux-1; x <= u.ux+1 && !ok; x++) + if(isok(x,y)) + if(IS_DRAWBRIDGE(levl[x][y].typ) || + is_drawbridge_wall(x,y) >= 0) + ok = TRUE; + if(ok) { /* There is a drawbridge near */ + int tumblers, gears; + boolean matched[5]; + + tumblers = gears = 0; + for(x=0; x < 5; x++) + matched[x] = FALSE; + + for(x=0; x < (int)strlen(buf); x++) + if(x < 5) { + if(buf[x] == tune[x]) { + gears++; + matched[x] = TRUE; + } else + for(y=0; y < 5; y++) + if(!matched[y] && + buf[x] == tune[y] && + buf[y] != tune[y]) { + tumblers++; + matched[y] = TRUE; + break; + } + } + if(tumblers) + if(gears) + You_hear("%d tumbler%s click and %d gear%s turn.", + tumblers, plur(tumblers), gears, plur(gears)); + else + You_hear("%d tumbler%s click.", + tumblers, plur(tumblers)); + else if(gears) { + You_hear("%d gear%s turn.", gears, plur(gears)); + /* could only get `gears == 5' by playing five + correct notes followed by excess; otherwise, + tune would have matched above */ + if (gears == 5) u.uevent.uheard_tune = 2; + } + } + } + } + return 1; + } else + return do_improvisation(instr); +} + +#ifdef UNIX386MUSIC +/* + * Play audible music on the machine's speaker if appropriate. + */ + +STATIC_OVL int +atconsole() +{ + /* + * Kluge alert: This code assumes that your [34]86 has no X terminals + * attached and that the console tty type is AT386 (this is always true + * under AT&T UNIX for these boxen). The theory here is that your remote + * ttys will have terminal type `ansi' or something else other than + * `AT386' or `xterm'. We'd like to do better than this, but testing + * to see if we're running on the console physical terminal is quite + * difficult given the presence of virtual consoles and other modern + * UNIX impedimenta... + */ + char *termtype = nh_getenv("TERM"); + + return(!strcmp(termtype, "AT386") || !strcmp(termtype, "xterm")); +} + +STATIC_OVL void +speaker(instr, buf) +struct obj *instr; +char *buf; +{ + /* + * For this to work, you need to have installed the PD speaker-control + * driver for PC-compatible UNIX boxes that I (esr@snark.thyrsus.com) + * posted to comp.sources.unix in Feb 1990. A copy should be included + * with your nethack distribution. + */ + int fd; + + if ((fd = open("/dev/speaker", 1)) != -1) + { + /* send a prefix to modify instrumental `timbre' */ + switch (instr->otyp) + { + case WOODEN_FLUTE: + case MAGIC_FLUTE: + (void) write(fd, ">ol", 1); /* up one octave & lock */ + break; + case TOOLED_HORN: + case FROST_HORN: + case FIRE_HORN: + (void) write(fd, "< +#include +#include +# else +#define KIOC ('K' << 8) +#define KDMKTONE (KIOC | 8) +# endif + +#define noDEBUG + +STATIC_OVL void tone(hz, ticks) +/* emit tone of frequency hz for given number of ticks */ +unsigned int hz, ticks; +{ + ioctl(0,KDMKTONE,hz|((ticks*10)<<16)); +# ifdef DEBUG + printf("TONE: %6d %6d\n",hz,ticks * 10); +# endif + nap(ticks * 10); +} + +STATIC_OVL void rest(ticks) +/* rest for given number of ticks */ +int ticks; +{ + nap(ticks * 10); +# ifdef DEBUG + printf("REST: %6d\n",ticks * 10); +# endif +} + + +#include "interp.c" /* from snd86unx.shr */ + + +STATIC_OVL void +speaker(instr, buf) +struct obj *instr; +char *buf; +{ + /* emit a prefix to modify instrumental `timbre' */ + playinit(); + switch (instr->otyp) + { + case WOODEN_FLUTE: + case MAGIC_FLUTE: + playstring(">ol", 1); /* up one octave & lock */ + break; + case TOOLED_HORN: + case FROST_HORN: + case FIRE_HORN: + playstring("< maxledgerno()) + ? maxledgerno() : ledger_no(dlev); + else + lev = 0; + first = bases[GEM_CLASS]; + + for(j = 0; j < 9-lev/3; j++) + objects[first+j].oc_prob = 0; + first += j; + if (first > LAST_GEM || objects[first].oc_class != GEM_CLASS || + OBJ_NAME(objects[first]) == (char *)0) { + raw_printf("Not enough gems? - first=%d j=%d LAST_GEM=%d", + first, j, LAST_GEM); + wait_synch(); + } + for (j = first; j <= LAST_GEM; j++) + objects[j].oc_prob = (171+j-first)/(LAST_GEM+1-first); +} + +/* shuffle descriptions on objects o_low to o_high */ +STATIC_OVL void +shuffle(o_low, o_high, domaterial) + int o_low, o_high; + boolean domaterial; +{ + int i, j, num_to_shuffle; + short sw; + int color; + + for (num_to_shuffle = 0, j=o_low; j <= o_high; j++) + if (!objects[j].oc_name_known) num_to_shuffle++; + if (num_to_shuffle < 2) return; + + for (j=o_low; j <= o_high; j++) { + if (objects[j].oc_name_known) continue; + do + i = j + rn2(o_high-j+1); + while (objects[i].oc_name_known); + sw = objects[j].oc_descr_idx; + objects[j].oc_descr_idx = objects[i].oc_descr_idx; + objects[i].oc_descr_idx = sw; + sw = objects[j].oc_tough; + objects[j].oc_tough = objects[i].oc_tough; + objects[i].oc_tough = sw; + color = objects[j].oc_color; + objects[j].oc_color = objects[i].oc_color; + objects[i].oc_color = color; + + /* shuffle material */ + if (domaterial) { + sw = objects[j].oc_material; + objects[j].oc_material = objects[i].oc_material; + objects[i].oc_material = sw; + } + } +} + +void +init_objects() +{ +register int i, first, last, sum; +register char oclass; +#ifdef TEXTCOLOR +# define COPY_OBJ_DESCR(o_dst,o_src) \ + o_dst.oc_descr_idx = o_src.oc_descr_idx,\ + o_dst.oc_color = o_src.oc_color +#else +# define COPY_OBJ_DESCR(o_dst,o_src) o_dst.oc_descr_idx = o_src.oc_descr_idx +#endif + + /* bug fix to prevent "initialization error" abort on Intel Xenix. + * reported by mikew@semike + */ + for (i = 0; i < MAXOCLASSES; i++) + bases[i] = 0; + /* initialize object descriptions */ + for (i = 0; i < NUM_OBJECTS; i++) + objects[i].oc_name_idx = objects[i].oc_descr_idx = i; + /* init base; if probs given check that they add up to 1000, + otherwise compute probs */ + first = 0; + while( first < NUM_OBJECTS ) { + oclass = objects[first].oc_class; + last = first+1; + while (last < NUM_OBJECTS && objects[last].oc_class == oclass) last++; + bases[(int)oclass] = first; + + if (oclass == GEM_CLASS) { + setgemprobs((d_level *)0); + + if (rn2(2)) { /* change turquoise from green to blue? */ + COPY_OBJ_DESCR(objects[TURQUOISE],objects[SAPPHIRE]); + } + if (rn2(2)) { /* change aquamarine from green to blue? */ + COPY_OBJ_DESCR(objects[AQUAMARINE],objects[SAPPHIRE]); + } + switch (rn2(4)) { /* change fluorite from violet? */ + case 0: break; + case 1: /* blue */ + COPY_OBJ_DESCR(objects[FLUORITE],objects[SAPPHIRE]); + break; + case 2: /* white */ + COPY_OBJ_DESCR(objects[FLUORITE],objects[DIAMOND]); + break; + case 3: /* green */ + COPY_OBJ_DESCR(objects[FLUORITE],objects[EMERALD]); + break; + } + } + check: + sum = 0; + for(i = first; i < last; i++) sum += objects[i].oc_prob; + if(sum == 0) { + for(i = first; i < last; i++) + objects[i].oc_prob = (1000+i-first)/(last-first); + goto check; + } + if(sum != 1000) + error("init-prob error for class %d (%d%%)", oclass, sum); + first = last; + } + /* shuffle descriptions */ + shuffle_all(); +#ifdef USE_TILES + shuffle_tiles(); +#endif +} + +STATIC_OVL void +shuffle_all() +{ + int first, last, oclass; + + for (oclass = 1; oclass < MAXOCLASSES; oclass++) { + first = bases[oclass]; + last = first+1; + while (last < NUM_OBJECTS && objects[last].oc_class == oclass) + last++; + + if (OBJ_DESCR(objects[first]) != (char *)0 && + oclass != TOOL_CLASS && + oclass != WEAPON_CLASS && + oclass != ARMOR_CLASS && + oclass != GEM_CLASS) { + int j = last-1; + + if (oclass == POTION_CLASS) + j -= 1; /* only water has a fixed description */ + else if (oclass == AMULET_CLASS || + oclass == SCROLL_CLASS || + oclass == SPBOOK_CLASS) { + while (!objects[j].oc_magic || objects[j].oc_unique) + j--; + } + + /* non-magical amulets, scrolls, and spellbooks + * (ex. imitation Amulets, blank, scrolls of mail) + * and one-of-a-kind magical artifacts at the end of + * their class in objects[] have fixed descriptions. + */ + shuffle(first, j, TRUE); + } + } + + /* shuffle the helmets */ + shuffle(HELMET, HELM_OF_TELEPATHY, FALSE); + + /* shuffle the gloves */ + shuffle(LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY, FALSE); + + /* shuffle the cloaks */ + shuffle(CLOAK_OF_PROTECTION, CLOAK_OF_DISPLACEMENT, FALSE); + + /* shuffle the boots [if they change, update find_skates() below] */ + shuffle(SPEED_BOOTS, LEVITATION_BOOTS, FALSE); +} + +/* find the object index for snow boots; used [once] by slippery ice code */ +int +find_skates() +{ + register int i; + register const char *s; + + for (i = SPEED_BOOTS; i <= LEVITATION_BOOTS; i++) + if ((s = OBJ_DESCR(objects[i])) != 0 && !strcmp(s, "snow boots")) + return i; + + impossible("snow boots not found?"); + return -1; /* not 0, or caller would try again each move */ +} + +void +oinit() /* level dependent initialization */ +{ + setgemprobs(&u.uz); +} + +void +savenames(fd, mode) +int fd, mode; +{ + register int i; + unsigned int len; + + if (perform_bwrite(mode)) { + bwrite(fd, (genericptr_t)bases, sizeof bases); + bwrite(fd, (genericptr_t)disco, sizeof disco); + bwrite(fd, (genericptr_t)objects, + sizeof(struct objclass) * NUM_OBJECTS); + } + /* as long as we use only one version of Hack we + need not save oc_name and oc_descr, but we must save + oc_uname for all objects */ + for (i = 0; i < NUM_OBJECTS; i++) + if (objects[i].oc_uname) { + if (perform_bwrite(mode)) { + len = strlen(objects[i].oc_uname)+1; + bwrite(fd, (genericptr_t)&len, sizeof len); + bwrite(fd, (genericptr_t)objects[i].oc_uname, len); + } + if (release_data(mode)) { + free((genericptr_t)objects[i].oc_uname); + objects[i].oc_uname = 0; + } + } +} + +void +restnames(fd) +register int fd; +{ + register int i; + unsigned int len; + + mread(fd, (genericptr_t) bases, sizeof bases); + mread(fd, (genericptr_t) disco, sizeof disco); + mread(fd, (genericptr_t) objects, sizeof(struct objclass) * NUM_OBJECTS); + for (i = 0; i < NUM_OBJECTS; i++) + if (objects[i].oc_uname) { + mread(fd, (genericptr_t) &len, sizeof len); + objects[i].oc_uname = (char *) alloc(len); + mread(fd, (genericptr_t)objects[i].oc_uname, len); + } +#ifdef USE_TILES + shuffle_tiles(); +#endif +} + +void +discover_object(oindx, mark_as_known, credit_hero) +register int oindx; +boolean mark_as_known; +boolean credit_hero; +{ + if (!objects[oindx].oc_name_known) { + register int dindx, acls = objects[oindx].oc_class; + + /* Loop thru disco[] 'til we find the target (which may have been + uname'd) or the next open slot; one or the other will be found + before we reach the next class... + */ + for (dindx = bases[acls]; disco[dindx] != 0; dindx++) + if (disco[dindx] == oindx) break; + disco[dindx] = oindx; + + if (mark_as_known) { + objects[oindx].oc_name_known = 1; + if (credit_hero) exercise(A_WIS, TRUE); + } + if (moves > 1L) update_inventory(); + } +} + +/* if a class name has been cleared, we may need to purge it from disco[] */ +void +undiscover_object(oindx) +register int oindx; +{ + if (!objects[oindx].oc_name_known) { + register int dindx, acls = objects[oindx].oc_class; + register boolean found = FALSE; + + /* find the object; shift those behind it forward one slot */ + for (dindx = bases[acls]; + dindx < NUM_OBJECTS && disco[dindx] != 0 + && objects[dindx].oc_class == acls; dindx++) + if (found) + disco[dindx-1] = disco[dindx]; + else if (disco[dindx] == oindx) + found = TRUE; + + /* clear last slot */ + if (found) disco[dindx-1] = 0; + else impossible("named object not in disco"); + update_inventory(); + } +} + +STATIC_OVL boolean +interesting_to_discover(i) +register int i; +{ + /* Pre-discovered objects are now printed with a '*' */ + return((boolean)(objects[i].oc_uname != (char *)0 || + (objects[i].oc_name_known && OBJ_DESCR(objects[i]) != (char *)0))); +} + +/* items that should stand out once they're known */ +static short uniq_objs[] = { + AMULET_OF_YENDOR, + SPE_BOOK_OF_THE_DEAD, + CANDELABRUM_OF_INVOCATION, + BELL_OF_OPENING, +}; + +int +dodiscovered() /* free after Robert Viduya */ +{ + register int i, dis; + int ct = 0; + char *s, oclass, prev_class, classes[MAXOCLASSES]; + winid tmpwin; + char buf[BUFSZ]; + + tmpwin = create_nhwindow(NHW_MENU); + putstr(tmpwin, 0, "Discoveries"); + putstr(tmpwin, 0, ""); + + /* gather "unique objects" into a pseudo-class; note that they'll + also be displayed individually within their regular class */ + for (i = dis = 0; i < SIZE(uniq_objs); i++) + if (objects[uniq_objs[i]].oc_name_known) { + if (!dis++) + putstr(tmpwin, iflags.menu_headings, "Unique Items"); + Sprintf(buf, " %s", OBJ_NAME(objects[uniq_objs[i]])); + putstr(tmpwin, 0, buf); + ++ct; + } + /* display any known artifacts as another pseudo-class */ + ct += disp_artifact_discoveries(tmpwin); + + /* several classes are omitted from packorder; one is of interest here */ + Strcpy(classes, flags.inv_order); + if (!index(classes, VENOM_CLASS)) { + s = eos(classes); + *s++ = VENOM_CLASS; + *s = '\0'; + } + + for (s = classes; *s; s++) { + oclass = *s; + prev_class = oclass + 1; /* forced different from oclass */ + for (i = bases[(int)oclass]; + i < NUM_OBJECTS && objects[i].oc_class == oclass; i++) { + if ((dis = disco[i]) && interesting_to_discover(dis)) { + ct++; + if (oclass != prev_class) { + putstr(tmpwin, iflags.menu_headings, let_to_name(oclass, FALSE)); + prev_class = oclass; + } + Sprintf(buf, "%s %s",(objects[dis].oc_pre_discovered ? "*" : " "), + obj_typename(dis)); + putstr(tmpwin, 0, buf); + } + } + } + if (ct == 0) { + You("haven't discovered anything yet..."); + } else + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + + return 0; +} + +/*o_init.c*/ diff --git a/src/objects.c b/src/objects.c new file mode 100644 index 0000000..1b18a3d --- /dev/null +++ b/src/objects.c @@ -0,0 +1,991 @@ +/* SCCS Id: @(#)objects.c 3.4 2002/07/31 */ +/* Copyright (c) Mike Threepoint, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef OBJECTS_PASS_2_ +/* first pass */ +struct monst { struct monst *dummy; }; /* lint: struct obj's union */ +#include "config.h" +#include "obj.h" +#include "objclass.h" +#include "prop.h" +#include "skills.h" + +#else /* !OBJECTS_PASS_2_ */ +/* second pass */ +#include "color.h" +# define COLOR_FIELD(X) X, +#endif /* !OBJECTS_PASS_2_ */ + + +/* objects have symbols: ) [ = " ( % ! ? + / $ * ` 0 _ . */ + +/* + * Note: OBJ() and BITS() macros are used to avoid exceeding argument + * limits imposed by some compilers. The ctnr field of BITS currently + * does not map into struct objclass, and is ignored in the expansion. + * The 0 in the expansion corresponds to oc_pre_discovered, which is + * set at run-time during role-specific character initialization. + */ + +#ifndef OBJECTS_PASS_2_ +/* first pass -- object descriptive text */ +# define OBJ(name,desc) name,desc +# define OBJECT(obj,bits,prp,sym,prob,dly,wt,cost,sdam,ldam,oc1,oc2,nut,color) \ + {obj} + +NEARDATA struct objdescr obj_descr[] = { +#else +/* second pass -- object definitions */ + +# define BITS(nmkn,mrg,uskn,ctnr,mgc,chrg,uniq,nwsh,big,tuf,dir,sub,mtrl) \ + nmkn,mrg,uskn,0,mgc,chrg,uniq,nwsh,big,tuf,dir,mtrl,sub /* SCO ODT 1.1 cpp fodder */ +# define OBJECT(obj,bits,prp,sym,prob,dly,wt,cost,sdam,ldam,oc1,oc2,nut,color) \ + {0, 0, (char *)0, bits, prp, sym, dly, COLOR_FIELD(color) \ + prob, wt, cost, sdam, ldam, oc1, oc2, nut} +# ifndef lint +# define HARDGEM(n) (n >= 8) +# else +# define HARDGEM(n) (0) +# endif + +NEARDATA struct objclass objects[] = { +#endif +/* dummy object[0] -- description [2nd arg] *must* be NULL */ + OBJECT(OBJ("strange object",(char *)0), BITS(1,0,0,0,0,0,0,0,0,0,0,P_NONE,0), + 0, ILLOBJ_CLASS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), + +/* weapons ... */ +#define WEAPON(name,app,kn,mg,bi,prob,wt,cost,sdam,ldam,hitbon,typ,sub,metal,color) \ + OBJECT( \ + OBJ(name,app), BITS(kn,mg,1,0,0,1,0,0,bi,0,typ,sub,metal), 0, \ + WEAPON_CLASS, prob, 0, \ + wt, cost, sdam, ldam, hitbon, 0, wt, color ) +#define PROJECTILE(name,app,kn,prob,wt,cost,sdam,ldam,hitbon,metal,sub,color) \ + OBJECT( \ + OBJ(name,app), \ + BITS(kn,1,1,0,0,1,0,0,0,0,PIERCE,sub,metal), 0, \ + WEAPON_CLASS, prob, 0, \ + wt, cost, sdam, ldam, hitbon, 0, wt, color ) +#define BOW(name,app,kn,prob,wt,cost,hitbon,metal,sub,color) \ + OBJECT( \ + OBJ(name,app), BITS(kn,0,1,0,0,1,0,0,0,0,0,sub,metal), 0, \ + WEAPON_CLASS, prob, 0, \ + wt, cost, 2, 2, hitbon, 0, wt, color ) + +/* Note: for weapons that don't do an even die of damage (ex. 2-7 or 3-18) + * the extra damage is added on in weapon.c, not here! */ + +#define P PIERCE +#define S SLASH +#define B WHACK + +/* missiles */ +PROJECTILE("arrow", (char *)0, + 1, 55, 1, 2, 6, 6, 0, IRON, -P_BOW, HI_METAL), +PROJECTILE("elven arrow", "runed arrow", + 0, 20, 1, 2, 7, 6, 0, WOOD, -P_BOW, HI_WOOD), +PROJECTILE("orcish arrow", "crude arrow", + 0, 20, 1, 2, 5, 6, 0, IRON, -P_BOW, CLR_BLACK), +PROJECTILE("silver arrow", (char *)0, + 1, 12, 1, 5, 6, 6, 0, SILVER, -P_BOW, HI_SILVER), +PROJECTILE("ya", "bamboo arrow", + 0, 15, 1, 4, 7, 7, 1, METAL, -P_BOW, HI_METAL), +PROJECTILE("crossbow bolt", (char *)0, + 1, 55, 1, 2, 4, 6, 0, IRON, -P_CROSSBOW, HI_METAL), + +WEAPON("dart", (char *)0, + 1, 1, 0, 60, 1, 2, 3, 2, 0, P, -P_DART, IRON, HI_METAL), +WEAPON("shuriken", "throwing star", + 0, 1, 0, 35, 1, 5, 8, 6, 2, P, -P_SHURIKEN, IRON, HI_METAL), +WEAPON("boomerang", (char *)0, + 1, 1, 0, 15, 5, 20, 9, 9, 0, 0, -P_BOOMERANG, WOOD, HI_WOOD), + +/* spears */ +WEAPON("spear", (char *)0, + 1, 1, 0, 50, 30, 3, 6, 8, 0, P, P_SPEAR, IRON, HI_METAL), +WEAPON("elven spear", "runed spear", + 0, 1, 0, 10, 30, 3, 7, 8, 0, P, P_SPEAR, WOOD, HI_WOOD), +WEAPON("orcish spear", "crude spear", + 0, 1, 0, 13, 30, 3, 5, 8, 0, P, P_SPEAR, IRON, CLR_BLACK), +WEAPON("dwarvish spear", "stout spear", + 0, 1, 0, 12, 35, 3, 8, 8, 0, P, P_SPEAR, IRON, HI_METAL), +WEAPON("silver spear", (char *)0, + 1, 1, 0, 2, 36, 40, 6, 8, 0, P, P_SPEAR, SILVER, HI_SILVER), +WEAPON("javelin", "throwing spear", + 0, 1, 0, 10, 20, 3, 6, 6, 0, P, P_JAVELIN, IRON, HI_METAL), + +WEAPON("trident", (char *)0, + 1, 0, 0, 8, 25, 5, 6, 4, 0, P, P_TRIDENT, IRON, HI_METAL), + /* +1 small, +2d4 large */ + +/* blades */ +WEAPON("dagger", (char *)0, + 1, 1, 0, 30, 10, 4, 4, 3, 2, P, P_DAGGER, IRON, HI_METAL), +WEAPON("elven dagger", "runed dagger", + 0, 1, 0, 10, 10, 4, 5, 3, 2, P, P_DAGGER, WOOD, HI_WOOD), +WEAPON("orcish dagger", "crude dagger", + 0, 1, 0, 12, 10, 4, 3, 3, 2, P, P_DAGGER, IRON, CLR_BLACK), +WEAPON("silver dagger", (char *)0, + 1, 1, 0, 3, 12, 40, 4, 3, 2, P, P_DAGGER, SILVER, HI_SILVER), +WEAPON("athame", (char *)0, + 1, 1, 0, 0, 10, 4, 4, 3, 2, S, P_DAGGER, IRON, HI_METAL), +WEAPON("scalpel", (char *)0, + 1, 1, 0, 0, 5, 6, 3, 3, 2, S, P_KNIFE, METAL, HI_METAL), +WEAPON("knife", (char *)0, + 1, 1, 0, 20, 5, 4, 3, 2, 0, P|S, P_KNIFE, IRON, HI_METAL), +WEAPON("stiletto", (char *)0, + 1, 1, 0, 5, 5, 4, 3, 2, 0, P|S, P_KNIFE, IRON, HI_METAL), +WEAPON("worm tooth", (char *)0, + 1, 0, 0, 0, 20, 2, 2, 2, 0, 0, P_KNIFE, 0, CLR_WHITE), +WEAPON("crysknife", (char *)0, + 1, 0, 0, 0, 20,100, 10, 10, 3, P, P_KNIFE, MINERAL, CLR_WHITE), + +WEAPON("axe", (char *)0, + 1, 0, 0, 40, 60, 8, 6, 4, 0, S, P_AXE, IRON, HI_METAL), +WEAPON("battle-axe", "double-headed axe", + 0, 0, 1, 10,120, 40, 8, 6, 0, S, P_AXE, IRON, HI_METAL), + /* "double-bitted" ? */ + +/* swords */ +WEAPON("short sword", (char *)0, + 1, 0, 0, 8, 30, 10, 6, 8, 0, P, P_SHORT_SWORD, IRON, HI_METAL), +WEAPON("elven short sword", "runed short sword", + 0, 0, 0, 2, 30, 10, 8, 8, 0, P, P_SHORT_SWORD, WOOD, HI_WOOD), +WEAPON("orcish short sword", "crude short sword", + 0, 0, 0, 3, 30, 10, 5, 8, 0, P, P_SHORT_SWORD, IRON, CLR_BLACK), +WEAPON("dwarvish short sword", "broad short sword", + 0, 0, 0, 2, 30, 10, 7, 8, 0, P, P_SHORT_SWORD, IRON, HI_METAL), +WEAPON("scimitar", "curved sword", + 0, 0, 0, 15, 40, 15, 8, 8, 0, S, P_SCIMITAR, IRON, HI_METAL), +WEAPON("silver saber", (char *)0, + 1, 0, 0, 6, 40, 75, 8, 8, 0, S, P_SABER, SILVER, HI_SILVER), +WEAPON("broadsword", (char *)0, + 1, 0, 0, 8, 70, 10, 4, 6, 0, S, P_BROAD_SWORD, IRON, HI_METAL), + /* +d4 small, +1 large */ +WEAPON("elven broadsword", "runed broadsword", + 0, 0, 0, 4, 70, 10, 6, 6, 0, S, P_BROAD_SWORD, WOOD, HI_WOOD), + /* +d4 small, +1 large */ +WEAPON("long sword", (char *)0, + 1, 0, 0, 50, 40, 15, 8, 12, 0, S, P_LONG_SWORD, IRON, HI_METAL), +WEAPON("two-handed sword", (char *)0, + 1, 0, 1, 22,150, 50, 12, 6, 0, S, P_TWO_HANDED_SWORD, IRON, HI_METAL), + /* +2d6 large */ +WEAPON("katana", "samurai sword", + 0, 0, 0, 4, 40, 80, 10, 12, 1, S, P_LONG_SWORD, IRON, HI_METAL), +/* special swords set up for artifacts */ +WEAPON("tsurugi", "long samurai sword", + 0, 0, 1, 0, 60,500, 16, 8, 2, S, P_TWO_HANDED_SWORD, METAL, HI_METAL), + /* +2d6 large */ +WEAPON("runesword", "runed broadsword", + 0, 0, 0, 0, 40,300, 4, 6, 0, S, P_BROAD_SWORD, IRON, CLR_BLACK), + /* +d4 small, +1 large */ + /* +5d2 +d8 from level drain */ + +/* polearms */ +/* spear-type */ +WEAPON("partisan", "vulgar polearm", + 0, 0, 1, 5, 80, 10, 6, 6, 0, P, P_POLEARMS, IRON, HI_METAL), + /* +1 large */ +WEAPON("ranseur", "hilted polearm", + 0, 0, 1, 5, 50, 6, 4, 4, 0, P, P_POLEARMS, IRON, HI_METAL), + /* +d4 both */ +WEAPON("spetum", "forked polearm", + 0, 0, 1, 5, 50, 5, 6, 6, 0, P, P_POLEARMS, IRON, HI_METAL), + /* +1 small, +d6 large */ +WEAPON("glaive", "single-edged polearm", + 0, 0, 1, 8, 75, 6, 6, 10, 0, S, P_POLEARMS, IRON, HI_METAL), +WEAPON("lance", (char *)0, + 1, 0, 0, 4,180, 10, 6, 8, 0, P, P_LANCE, IRON, HI_METAL), +/* axe-type */ +WEAPON("halberd", "angled poleaxe", + 0, 0, 1, 8,150, 10, 10, 6, 0, P|S, P_POLEARMS, IRON, HI_METAL), + /* +1d6 large */ +WEAPON("bardiche", "long poleaxe", + 0, 0, 1, 4,120, 7, 4, 4, 0, S, P_POLEARMS, IRON, HI_METAL), + /* +1d4 small, +2d4 large */ +WEAPON("voulge", "pole cleaver", + 0, 0, 1, 4,125, 5, 4, 4, 0, S, P_POLEARMS, IRON, HI_METAL), + /* +d4 both */ +WEAPON("dwarvish mattock", "broad pick", + 0, 0, 1, 13,120, 50, 12, 8,-1, B, P_PICK_AXE, IRON, HI_METAL), + +/* curved/hooked */ +WEAPON("fauchard", "pole sickle", + 0, 0, 1, 6, 60, 5, 6, 8, 0, P|S, P_POLEARMS, IRON, HI_METAL), +WEAPON("guisarme", "pruning hook", + 0, 0, 1, 6, 80, 5, 4, 8, 0, S, P_POLEARMS, IRON, HI_METAL), + /* +1d4 small */ +WEAPON("bill-guisarme", "hooked polearm", + 0, 0, 1, 4,120, 7, 4, 10, 0, P|S, P_POLEARMS, IRON, HI_METAL), + /* +1d4 small */ +/* other */ +WEAPON("lucern hammer", "pronged polearm", + 0, 0, 1, 5,150, 7, 4, 6, 0, B|P, P_POLEARMS, IRON, HI_METAL), + /* +1d4 small */ +WEAPON("bec de corbin", "beaked polearm", + 0, 0, 1, 4,100, 8, 8, 6, 0, B|P, P_POLEARMS, IRON, HI_METAL), + +/* bludgeons */ +WEAPON("mace", (char *)0, + 1, 0, 0, 40, 30, 5, 6, 6, 0, B, P_MACE, IRON, HI_METAL), + /* +1 small */ +WEAPON("morning star", (char *)0, + 1, 0, 0, 12,120, 10, 4, 6, 0, B, P_MORNING_STAR, IRON, HI_METAL), + /* +d4 small, +1 large */ +WEAPON("war hammer", (char *)0, + 1, 0, 0, 15, 50, 5, 4, 4, 0, B, P_HAMMER, IRON, HI_METAL), + /* +1 small */ +WEAPON("club", (char *)0, + 1, 0, 0, 12, 30, 3, 6, 3, 0, B, P_CLUB, WOOD, HI_WOOD), +#ifdef KOPS +WEAPON("rubber hose", (char *)0, + 1, 0, 0, 0, 20, 3, 4, 3, 0, B, P_WHIP, PLASTIC, CLR_BROWN), +#endif +WEAPON("quarterstaff", "staff", + 0, 0, 1, 11, 40, 5, 6, 6, 0, B, P_QUARTERSTAFF, WOOD, HI_WOOD), +/* two-piece */ +WEAPON("aklys", "thonged club", + 0, 0, 0, 8, 15, 4, 6, 3, 0, B, P_CLUB, IRON, HI_METAL), +WEAPON("flail", (char *)0, + 1, 0, 0, 40, 15, 4, 6, 4, 0, B, P_FLAIL, IRON, HI_METAL), + /* +1 small, +1d4 large */ +/* misc */ +WEAPON("bullwhip", (char *)0, + 1, 0, 0, 2, 20, 4, 2, 1, 0, 0, P_WHIP, LEATHER, CLR_BROWN), + +/* bows */ +BOW("bow", (char *)0, 1, 24, 30, 60, 0, WOOD, P_BOW, HI_WOOD), +BOW("elven bow", "runed bow", 0, 12, 30, 60, 0, WOOD, P_BOW, HI_WOOD), +BOW("orcish bow", "crude bow", 0, 12, 30, 60, 0, WOOD, P_BOW, CLR_BLACK), +BOW("yumi", "long bow", 0, 0, 30, 60, 0, WOOD, P_BOW, HI_WOOD), +BOW("sling", (char *)0, 1, 40, 3, 20, 0, LEATHER, P_SLING, HI_LEATHER), +BOW("crossbow", (char *)0, 1, 45, 50, 40, 0, WOOD, P_CROSSBOW, HI_WOOD), + +#undef P +#undef S +#undef B + +#undef WEAPON +#undef PROJECTILE +#undef BOW + +/* armor ... */ +/* IRON denotes ferrous metals, including steel. + * Only IRON weapons and armor can rust. + * Only COPPER (including brass) corrodes. + * Some creatures are vulnerable to SILVER. + */ +#define ARMOR(name,desc,kn,mgc,blk,power,prob,delay,wt,cost,ac,can,sub,metal,c) \ + OBJECT( \ + OBJ(name,desc), BITS(kn,0,1,0,mgc,1,0,0,blk,0,0,sub,metal), power, \ + ARMOR_CLASS, prob, delay, wt, cost, \ + 0, 0, 10 - ac, can, wt, c ) +#define HELM(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c) \ + ARMOR(name,desc,kn,mgc,0,power,prob,delay,wt,cost,ac,can,ARM_HELM,metal,c) +#define CLOAK(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c) \ + ARMOR(name,desc,kn,mgc,0,power,prob,delay,wt,cost,ac,can,ARM_CLOAK,metal,c) +#define SHIELD(name,desc,kn,mgc,blk,power,prob,delay,wt,cost,ac,can,metal,c) \ + ARMOR(name,desc,kn,mgc,blk,power,prob,delay,wt,cost,ac,can,ARM_SHIELD,metal,c) +#define GLOVES(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c) \ + ARMOR(name,desc,kn,mgc,0,power,prob,delay,wt,cost,ac,can,ARM_GLOVES,metal,c) +#define BOOTS(name,desc,kn,mgc,power,prob,delay,wt,cost,ac,can,metal,c) \ + ARMOR(name,desc,kn,mgc,0,power,prob,delay,wt,cost,ac,can,ARM_BOOTS,metal,c) + +/* helmets */ +HELM("elven leather helm", "leather hat", + 0, 0, 0, 6, 1, 3, 8, 9, 0, LEATHER, HI_LEATHER), +HELM("orcish helm", "iron skull cap", + 0, 0, 0, 6, 1, 30, 10, 9, 0, IRON, CLR_BLACK), +HELM("dwarvish iron helm", "hard hat", + 0, 0, 0, 6, 1, 40, 20, 8, 0, IRON, HI_METAL), +HELM("fedora", (char *)0, + 1, 0, 0, 0, 0, 3, 1,10, 0, CLOTH, CLR_BROWN), +HELM("cornuthaum", "conical hat", + 0, 1, CLAIRVOYANT, + 3, 1, 4, 80,10, 2, CLOTH, CLR_BLUE), +HELM("dunce cap", "conical hat", + 0, 1, 0, 3, 1, 4, 1,10, 0, CLOTH, CLR_BLUE), +HELM("dented pot", (char *)0, + 1, 0, 0, 2, 0, 10, 8, 9, 0, IRON, CLR_BLACK), +/* With shuffled appearances... */ +HELM("helmet", "plumed helmet", + 0, 0, 0, 10, 1, 30, 10, 9, 0, IRON, HI_METAL), +HELM("helm of brilliance", "etched helmet", + 0, 1, 0, 6, 1, 50, 50, 9, 0, IRON, CLR_GREEN), +HELM("helm of opposite alignment", "crested helmet", + 0, 1, 0, 6, 1, 50, 50, 9, 0, IRON, HI_METAL), +HELM("helm of telepathy", "visored helmet", + 0, 1, TELEPAT, 2, 1, 50, 50, 9, 0, IRON, HI_METAL), + +/* suits of armor */ +/* + * There is code in polyself.c that assumes (1) and (2). + * There is code in obj.h, objnam.c, mon.c, read.c that assumes (2). + * + * (1) The dragon scale mails and the dragon scales are together. + * (2) That the order of the dragon scale mail and dragon scales is the + * the same defined in monst.c. + */ +#define DRGN_ARMR(name,mgc,power,cost,ac,color) \ + ARMOR(name,(char *)0,1,mgc,1,power,0,5,40,cost,ac,0,ARM_SUIT,DRAGON_HIDE,color) +/* 3.4.1: dragon scale mail reclassified as "magic" since magic is + needed to create them */ +DRGN_ARMR("gray dragon scale mail", 1, ANTIMAGIC, 1200, 1, CLR_GRAY), +DRGN_ARMR("silver dragon scale mail", 1, REFLECTING, 1200, 1, DRAGON_SILVER), +#if 0 /* DEFERRED */ +DRGN_ARMR("shimmering dragon scale mail", 1, DISPLACED, 1200, 1, CLR_CYAN), +#endif +DRGN_ARMR("red dragon scale mail", 1, FIRE_RES, 900, 1, CLR_RED), +DRGN_ARMR("white dragon scale mail", 1, COLD_RES, 900, 1, CLR_WHITE), +DRGN_ARMR("orange dragon scale mail", 1, SLEEP_RES, 900, 1, CLR_ORANGE), +DRGN_ARMR("black dragon scale mail", 1, DISINT_RES, 1200, 1, CLR_BLACK), +DRGN_ARMR("blue dragon scale mail", 1, SHOCK_RES, 900, 1, CLR_BLUE), +DRGN_ARMR("green dragon scale mail", 1, POISON_RES, 900, 1, CLR_GREEN), +DRGN_ARMR("yellow dragon scale mail", 1, ACID_RES, 900, 1, CLR_YELLOW), + +/* For now, only dragons leave these. */ +/* 3.4.1: dragon scales left classified as "non-magic"; they confer + magical properties but are produced "naturally" */ +DRGN_ARMR("gray dragon scales", 0, ANTIMAGIC, 700, 7, CLR_GRAY), +DRGN_ARMR("silver dragon scales", 0, REFLECTING, 700, 7, DRAGON_SILVER), +#if 0 /* DEFERRED */ +DRGN_ARMR("shimmering dragon scales", 0, DISPLACED, 700, 7, CLR_CYAN), +#endif +DRGN_ARMR("red dragon scales", 0, FIRE_RES, 500, 7, CLR_RED), +DRGN_ARMR("white dragon scales", 0, COLD_RES, 500, 7, CLR_WHITE), +DRGN_ARMR("orange dragon scales", 0, SLEEP_RES, 500, 7, CLR_ORANGE), +DRGN_ARMR("black dragon scales", 0, DISINT_RES, 700, 7, CLR_BLACK), +DRGN_ARMR("blue dragon scales", 0, SHOCK_RES, 500, 7, CLR_BLUE), +DRGN_ARMR("green dragon scales", 0, POISON_RES, 500, 7, CLR_GREEN), +DRGN_ARMR("yellow dragon scales", 0, ACID_RES, 500, 7, CLR_YELLOW), +#undef DRGN_ARMR + +ARMOR("plate mail", (char *)0, + 1, 0, 1, 0, 44, 5, 450, 600, 3, 2, ARM_SUIT, IRON, HI_METAL), +ARMOR("crystal plate mail", (char *)0, + 1, 0, 1, 0, 10, 5, 450, 820, 3, 2, ARM_SUIT, GLASS, CLR_WHITE), +#ifdef TOURIST +ARMOR("bronze plate mail", (char *)0, + 1, 0, 1, 0, 25, 5, 450, 400, 4, 0, ARM_SUIT, COPPER, HI_COPPER), +#else +ARMOR("bronze plate mail", (char *)0, + 1, 0, 1, 0, 35, 5, 450, 400, 4, 0, ARM_SUIT, COPPER, HI_COPPER), +#endif +ARMOR("splint mail", (char *)0, + 1, 0, 1, 0, 62, 5, 400, 80, 4, 1, ARM_SUIT, IRON, HI_METAL), +ARMOR("banded mail", (char *)0, + 1, 0, 1, 0, 72, 5, 350, 90, 4, 0, ARM_SUIT, IRON, HI_METAL), +ARMOR("dwarvish mithril-coat", (char *)0, + 1, 0, 0, 0, 10, 1, 150, 240, 4, 3, ARM_SUIT, MITHRIL, HI_METAL), +ARMOR("elven mithril-coat", (char *)0, + 1, 0, 0, 0, 15, 1, 150, 240, 5, 3, ARM_SUIT, MITHRIL, HI_METAL), +ARMOR("chain mail", (char *)0, + 1, 0, 0, 0, 72, 5, 300, 75, 5, 1, ARM_SUIT, IRON, HI_METAL), +ARMOR("orcish chain mail", "crude chain mail", + 0, 0, 0, 0, 20, 5, 300, 75, 6, 1, ARM_SUIT, IRON, CLR_BLACK), +ARMOR("scale mail", (char *)0, + 1, 0, 0, 0, 72, 5, 250, 45, 6, 0, ARM_SUIT, IRON, HI_METAL), +ARMOR("studded leather armor", (char *)0, + 1, 0, 0, 0, 72, 3, 200, 15, 7, 1, ARM_SUIT, LEATHER, HI_LEATHER), +ARMOR("ring mail", (char *)0, + 1, 0, 0, 0, 72, 5, 250, 100, 7, 0, ARM_SUIT, IRON, HI_METAL), +ARMOR("orcish ring mail", "crude ring mail", + 0, 0, 0, 0, 20, 5, 250, 80, 8, 1, ARM_SUIT, IRON, CLR_BLACK), +ARMOR("leather armor", (char *)0, + 1, 0, 0, 0, 82, 3, 150, 5, 8, 0, ARM_SUIT, LEATHER, HI_LEATHER), +ARMOR("leather jacket", (char *)0, + 1, 0, 0, 0, 12, 0, 30, 10, 9, 0, ARM_SUIT, LEATHER, CLR_BLACK), + +#ifdef TOURIST +/* shirts */ +ARMOR("Hawaiian shirt", (char *)0, + 1, 0, 0, 0, 8, 0, 5, 3, 10, 0, ARM_SHIRT, CLOTH, CLR_MAGENTA), +ARMOR("T-shirt", (char *)0, + 1, 0, 0, 0, 2, 0, 5, 2, 10, 0, ARM_SHIRT, CLOTH, CLR_WHITE), +#endif + +/* cloaks */ +/* 'cope' is not a spelling mistake... leave it be */ +CLOAK("mummy wrapping", (char *)0, + 1, 0, 0, 0, 0, 3, 2, 10, 1, CLOTH, CLR_GRAY), +CLOAK("elven cloak", "faded pall", + 0, 1, STEALTH, 8, 0, 10, 60, 9, 3, CLOTH, CLR_BLACK), +CLOAK("orcish cloak", "coarse mantelet", + 0, 0, 0, 8, 0, 10, 40, 10, 2, CLOTH, CLR_BLACK), +CLOAK("dwarvish cloak", "hooded cloak", + 0, 0, 0, 8, 0, 10, 50, 10, 2, CLOTH, HI_CLOTH), +CLOAK("oilskin cloak", "slippery cloak", + 0, 0, 0, 8, 0, 10, 50, 9, 3, CLOTH, HI_CLOTH), +CLOAK("robe", (char *)0, + 1, 1, 0, 3, 0, 15, 50, 8, 3, CLOTH, CLR_RED), +CLOAK("alchemy smock", "apron", + 0, 1, POISON_RES, 9, 0, 10, 50, 9, 1, CLOTH, CLR_WHITE), +CLOAK("leather cloak", (char *)0, + 1, 0, 0, 8, 0, 15, 40, 9, 1, LEATHER, CLR_BROWN), +/* With shuffled appearances... */ +CLOAK("cloak of protection", "tattered cape", + 0, 1, PROTECTION, 9, 0, 10, 50, 7, 3, CLOTH, HI_CLOTH), +CLOAK("cloak of invisibility", "opera cloak", + 0, 1, INVIS, 10, 0, 10, 60, 9, 2, CLOTH, CLR_BRIGHT_MAGENTA), +CLOAK("cloak of magic resistance", "ornamental cope", + 0, 1, ANTIMAGIC, 2, 0, 10, 60, 9, 3, CLOTH, CLR_WHITE), +CLOAK("cloak of displacement", "piece of cloth", + 0, 1, DISPLACED, 10, 0, 10, 50, 9, 2, CLOTH, HI_CLOTH), + +/* shields */ +SHIELD("small shield", (char *)0, + 1, 0, 0, 0, 6, 0, 30, 3, 9, 0, WOOD, HI_WOOD), +SHIELD("elven shield", "blue and green shield", + 0, 0, 0, 0, 2, 0, 40, 7, 8, 0, WOOD, CLR_GREEN), +SHIELD("Uruk-hai shield", "white-handed shield", + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, HI_METAL), +SHIELD("orcish shield", "red-eyed shield", + 0, 0, 0, 0, 2, 0, 50, 7, 9, 0, IRON, CLR_RED), +SHIELD("large shield", (char *)0, + 1, 0, 1, 0, 7, 0,100, 10, 8, 0, IRON, HI_METAL), +SHIELD("dwarvish roundshield", "large round shield", + 0, 0, 0, 0, 4, 0,100, 10, 8, 0, IRON, HI_METAL), +SHIELD("shield of reflection", "polished silver shield", + 0, 1, 0, REFLECTING, 3, 0, 50, 50, 8, 0, SILVER, HI_SILVER), + +/* gloves */ +/* these have their color but not material shuffled, so the IRON must stay + * CLR_BROWN (== HI_LEATHER) + */ +GLOVES("leather gloves", "old gloves", + 0, 0, 0, 16, 1, 10, 8, 9, 0, LEATHER, HI_LEATHER), +GLOVES("gauntlets of fumbling", "padded gloves", + 0, 1, FUMBLING, 8, 1, 10, 50, 9, 0, LEATHER, HI_LEATHER), +GLOVES("gauntlets of power", "riding gloves", + 0, 1, 0, 8, 1, 30, 50, 9, 0, IRON, CLR_BROWN), +GLOVES("gauntlets of dexterity", "fencing gloves", + 0, 1, 0, 8, 1, 10, 50, 9, 0, LEATHER, HI_LEATHER), + +/* boots */ +BOOTS("low boots", "walking shoes", + 0, 0, 0, 25, 2, 10, 8, 9, 0, LEATHER, HI_LEATHER), +BOOTS("iron shoes", "hard shoes", + 0, 0, 0, 7, 2, 50, 16, 8, 0, IRON, HI_METAL), +BOOTS("high boots", "jackboots", + 0, 0, 0, 15, 2, 20, 12, 8, 0, LEATHER, HI_LEATHER), +/* With shuffled appearances... */ +BOOTS("speed boots", "combat boots", + 0, 1, FAST, 12, 2, 20, 50, 9, 0, LEATHER, HI_LEATHER), +BOOTS("water walking boots", "jungle boots", + 0, 1, WWALKING, 12, 2, 20, 50, 9, 0, LEATHER, HI_LEATHER), +BOOTS("jumping boots", "hiking boots", + 0, 1, JUMPING, 12, 2, 20, 50, 9, 0, LEATHER, HI_LEATHER), +BOOTS("elven boots", "mud boots", + 0, 1, STEALTH, 12, 2, 15, 8, 9, 0, LEATHER, HI_LEATHER), +BOOTS("kicking boots", "buckled boots", + 0, 1, 0, 12, 2, 15, 8, 9, 0, IRON, CLR_BROWN), +BOOTS("fumble boots", "riding boots", + 0, 1, FUMBLING, 12, 2, 20, 30, 9, 0, LEATHER, HI_LEATHER), +BOOTS("levitation boots", "snow boots", + 0, 1, LEVITATION,12, 2, 15, 30, 9, 0, LEATHER, HI_LEATHER), +#undef HELM +#undef CLOAK +#undef SHIELD +#undef GLOVES +#undef BOOTS +#undef ARMOR + +/* rings ... */ +#define RING(name,power,stone,cost,mgc,spec,mohs,metal,color) OBJECT( \ + OBJ(name,stone), \ + BITS(0,0,spec,0,mgc,spec,0,0,0,HARDGEM(mohs),0,P_NONE,metal), \ + power, RING_CLASS, 0, 0, 3, cost, 0, 0, 0, 0, 15, color ) +RING("adornment", ADORNED, "wooden", 100, 1, 1, 2, WOOD, HI_WOOD), +RING("gain strength", 0, "granite", 150, 1, 1, 7, MINERAL, HI_MINERAL), +RING("gain constitution", 0, "opal", 150, 1, 1, 7, MINERAL, HI_MINERAL), +RING("increase accuracy", 0, "clay", 150, 1, 1, 4, MINERAL, CLR_RED), +RING("increase damage", 0, "coral", 150, 1, 1, 4, MINERAL, CLR_ORANGE), +RING("protection", PROTECTION, "black onyx",100, 1, 1, 7, MINERAL, CLR_BLACK), +RING("regeneration", REGENERATION, "moonstone", + 200, 1, 0, 6, MINERAL, HI_MINERAL), +RING("searching", SEARCHING, "tiger eye", 200, 1, 0, 6, GEMSTONE, CLR_BROWN), +RING("stealth", STEALTH, "jade", 100, 1, 0, 6, GEMSTONE, CLR_GREEN), +RING("sustain ability", FIXED_ABIL, "bronze", + 100, 1, 0, 4, COPPER, HI_COPPER), +RING("levitation", LEVITATION, "agate", 200, 1, 0, 7, GEMSTONE, CLR_RED), +RING("hunger", HUNGER, "topaz", 100, 1, 0, 8, GEMSTONE, CLR_CYAN), +RING("aggravate monster", AGGRAVATE_MONSTER, "sapphire", + 150, 1, 0, 9, GEMSTONE, CLR_BLUE), +RING("conflict", CONFLICT, "ruby", 300, 1, 0, 9, GEMSTONE, CLR_RED), +RING("warning", WARNING, "diamond", 100, 1, 0,10, GEMSTONE, CLR_WHITE), +RING("poison resistance", POISON_RES, "pearl", + 150, 1, 0, 4, IRON, CLR_WHITE), +RING("fire resistance", FIRE_RES, "iron", 200, 1, 0, 5, IRON, HI_METAL), +RING("cold resistance", COLD_RES, "brass", 150, 1, 0, 4, COPPER, HI_COPPER), +RING("shock resistance", SHOCK_RES, "copper", + 150, 1, 0, 3, COPPER, HI_COPPER), +RING("free action", FREE_ACTION, "twisted", + 200, 1, 0, 6, IRON, HI_METAL), +RING("slow digestion", SLOW_DIGESTION, "steel", + 200, 1, 0, 8, IRON, HI_METAL), +RING("teleportation", TELEPORT, "silver", 200, 1, 0, 3, SILVER, HI_SILVER), +RING("teleport control", TELEPORT_CONTROL, "gold", + 300, 1, 0, 3, GOLD, HI_GOLD), +RING("polymorph", POLYMORPH, "ivory", 300, 1, 0, 4, BONE, CLR_WHITE), +RING("polymorph control", POLYMORPH_CONTROL, "emerald", + 300, 1, 0, 8, GEMSTONE, CLR_BRIGHT_GREEN), +RING("invisibility", INVIS, "wire", 150, 1, 0, 5, IRON, HI_METAL), +RING("see invisible", SEE_INVIS, "engagement", + 150, 1, 0, 5, IRON, HI_METAL), +RING("protection from shape changers", PROT_FROM_SHAPE_CHANGERS, "shiny", + 100, 1, 0, 5, IRON, CLR_BRIGHT_CYAN), +#undef RING + +/* amulets ... - THE Amulet comes last because it is special */ +#define AMULET(name,desc,power,prob) OBJECT( \ + OBJ(name,desc), BITS(0,0,0,0,1,0,0,0,0,0,0,P_NONE,IRON), power, \ + AMULET_CLASS, prob, 0, 20, 150, 0, 0, 0, 0, 20, HI_METAL ) + +AMULET("amulet of ESP", "circular", TELEPAT, 175), +AMULET("amulet of life saving", "spherical", LIFESAVED, 75), +AMULET("amulet of strangulation", "oval", STRANGLED, 135), +AMULET("amulet of restful sleep", "triangular", SLEEPING, 135), +AMULET("amulet versus poison", "pyramidal", POISON_RES, 165), +AMULET("amulet of change", "square", 0, 130), + /* POLYMORPH */ +AMULET("amulet of unchanging", "concave", UNCHANGING, 45), +AMULET("amulet of reflection", "hexagonal", REFLECTING, 75), +AMULET("amulet of magical breathing", "octagonal", MAGICAL_BREATHING, 65), +OBJECT(OBJ("cheap plastic imitation of the Amulet of Yendor", + "Amulet of Yendor"), BITS(0,0,1,0,0,0,0,0,0,0,0,0,PLASTIC), 0, + AMULET_CLASS, 0, 0, 20, 0, 0, 0, 0, 0, 1, HI_METAL), +OBJECT(OBJ("Amulet of Yendor", /* note: description == name */ + "Amulet of Yendor"), BITS(0,0,1,0,1,0,1,1,0,0,0,0,MITHRIL), 0, + AMULET_CLASS, 0, 0, 20, 30000, 0, 0, 0, 0, 20, HI_METAL), +#undef AMULET + +/* tools ... */ +/* tools with weapon characteristics come last */ +#define TOOL(name,desc,kn,mrg,mgc,chg,prob,wt,cost,mat,color) \ + OBJECT( OBJ(name,desc), \ + BITS(kn,mrg,chg,0,mgc,chg,0,0,0,0,0,P_NONE,mat), \ + 0, TOOL_CLASS, prob, 0, \ + wt, cost, 0, 0, 0, 0, wt, color ) +#define CONTAINER(name,desc,kn,mgc,chg,prob,wt,cost,mat,color) \ + OBJECT( OBJ(name,desc), \ + BITS(kn,0,chg,1,mgc,chg,0,0,0,0,0,P_NONE,mat), \ + 0, TOOL_CLASS, prob, 0, \ + wt, cost, 0, 0, 0, 0, wt, color ) +#define WEPTOOL(name,desc,kn,mgc,bi,prob,wt,cost,sdam,ldam,hitbon,sub,mat,clr) \ + OBJECT( OBJ(name,desc), \ + BITS(kn,0,1,0,mgc,1,0,0,bi,0,hitbon,sub,mat), \ + 0, TOOL_CLASS, prob, 0, \ + wt, cost, sdam, ldam, hitbon, 0, wt, clr ) +/* containers */ +CONTAINER("large box", (char *)0, 1, 0, 0, 40,350, 8, WOOD, HI_WOOD), +CONTAINER("chest", (char *)0, 1, 0, 0, 35,600, 16, WOOD, HI_WOOD), +CONTAINER("ice box", (char *)0, 1, 0, 0, 5,900, 42, PLASTIC, CLR_WHITE), +CONTAINER("sack", "bag", 0, 0, 0, 35, 15, 2, CLOTH, HI_CLOTH), +CONTAINER("oilskin sack", "bag", 0, 0, 0, 5, 15, 100, CLOTH, HI_CLOTH), +CONTAINER("bag of holding", "bag", 0, 1, 0, 20, 15, 100, CLOTH, HI_CLOTH), +CONTAINER("bag of tricks", "bag", 0, 1, 1, 20, 15, 100, CLOTH, HI_CLOTH), +#undef CONTAINER + +/* lock opening tools */ +TOOL("skeleton key", "key", 0, 0, 0, 0, 80, 3, 10, IRON, HI_METAL), +#ifdef TOURIST +TOOL("lock pick", (char *)0, 1, 0, 0, 0, 60, 4, 20, IRON, HI_METAL), +TOOL("credit card", (char *)0, 1, 0, 0, 0, 15, 1, 10, PLASTIC, CLR_WHITE), +#else +TOOL("lock pick", (char *)0, 1, 0, 0, 0, 75, 4, 20, IRON, HI_METAL), +#endif +/* light sources */ +TOOL("tallow candle", "candle", 0, 1, 0, 0, 20, 2, 10, WAX, CLR_WHITE), +TOOL("wax candle", "candle", 0, 1, 0, 0, 5, 2, 20, WAX, CLR_WHITE), +TOOL("brass lantern", (char *)0,1, 0, 0, 0, 30, 30, 12, COPPER, CLR_YELLOW), +TOOL("oil lamp", "lamp", 0, 0, 0, 0, 45, 20, 10, COPPER, CLR_YELLOW), +TOOL("magic lamp", "lamp", 0, 0, 1, 0, 15, 20, 50, COPPER, CLR_YELLOW), +/* other tools */ +#ifdef TOURIST +TOOL("expensive camera", (char *)0, + 1, 0, 0, 1, 15, 12, 200, PLASTIC, CLR_BLACK), +TOOL("mirror", "looking glass", 0, 0, 0, 0, 45, 13, 10, GLASS, HI_SILVER), +#else +TOOL("mirror", "looking glass", 0, 0, 0, 0, 60, 13, 10, GLASS, HI_SILVER), +#endif +TOOL("crystal ball", "glass orb", + 0, 0, 1, 1, 15,150, 60, GLASS, HI_GLASS), +TOOL("lenses", (char *)0, 1, 0, 0, 0, 5, 3, 80, GLASS, HI_GLASS), +TOOL("blindfold", (char *)0, 1, 0, 0, 0, 50, 2, 20, CLOTH, CLR_BLACK), +TOOL("towel", (char *)0, 1, 0, 0, 0, 50, 2, 50, CLOTH, CLR_MAGENTA), +#ifdef STEED +TOOL("saddle", (char *)0, 1, 0, 0, 0, 5,200, 150, LEATHER, HI_LEATHER), +TOOL("leash", (char *)0, 1, 0, 0, 0, 65, 12, 20, LEATHER, HI_LEATHER), +#else +TOOL("leash", (char *)0, 1, 0, 0, 0, 70, 12, 20, LEATHER, HI_LEATHER), +#endif +TOOL("stethoscope", (char *)0, 1, 0, 0, 0, 25, 4, 75, IRON, HI_METAL), +TOOL("tinning kit", (char *)0, 1, 0, 0, 1, 15,100, 30, IRON, HI_METAL), +TOOL("tin opener", (char *)0, 1, 0, 0, 0, 35, 4, 30, IRON, HI_METAL), +TOOL("can of grease", (char *)0,1, 0, 0, 1, 15, 15, 20, IRON, HI_METAL), +TOOL("figurine", (char *)0, 1, 0, 1, 0, 25, 50, 80, MINERAL, HI_MINERAL), +TOOL("magic marker", (char *)0, 1, 0, 1, 1, 15, 2, 50, PLASTIC, CLR_RED), +/* traps */ +TOOL("land mine",(char *)0, 1, 0, 0, 0, 0,300, 180, IRON, CLR_RED), +TOOL("beartrap", (char *)0, 1, 0, 0, 0, 0,200, 60, IRON, HI_METAL), +/* instruments */ +TOOL("tin whistle", "whistle", 0, 0, 0, 0, 100, 3, 10, METAL, HI_METAL), +TOOL("magic whistle", "whistle",0, 0, 1, 0, 30, 3, 10, METAL, HI_METAL), +/* "If tin whistles are made out of tin, what do they make foghorns out of?" */ +TOOL("wooden flute", "flute", 0, 0, 0, 0, 4, 5, 12, WOOD, HI_WOOD), +TOOL("magic flute", "flute", 0, 0, 1, 1, 2, 5, 36, WOOD, HI_WOOD), +TOOL("tooled horn", "horn", 0, 0, 0, 0, 5, 18, 15, BONE, CLR_WHITE), +TOOL("frost horn", "horn", 0, 0, 1, 1, 2, 18, 50, BONE, CLR_WHITE), +TOOL("fire horn", "horn", 0, 0, 1, 1, 2, 18, 50, BONE, CLR_WHITE), +TOOL("horn of plenty", "horn", 0, 0, 1, 1, 2, 18, 50, BONE, CLR_WHITE), +TOOL("wooden harp", "harp", 0, 0, 0, 0, 4, 30, 50, WOOD, HI_WOOD), +TOOL("magic harp", "harp", 0, 0, 1, 1, 2, 30, 50, WOOD, HI_WOOD), +TOOL("bell", (char *)0, 1, 0, 0, 0, 2, 30, 50, COPPER, HI_COPPER), +TOOL("bugle", (char *)0, 1, 0, 0, 0, 4, 10, 15, COPPER, HI_COPPER), +TOOL("leather drum", "drum", 0, 0, 0, 0, 4, 25, 25, LEATHER, HI_LEATHER), +TOOL("drum of earthquake", "drum", + 0, 0, 1, 1, 2, 25, 25, LEATHER, HI_LEATHER), +/* tools useful as weapons */ +WEPTOOL("pick-axe", (char *)0, + 1, 0, 0, 20, 100, 50, 6, 3, WHACK, P_PICK_AXE, IRON, HI_METAL), +WEPTOOL("grappling hook", "iron hook", + 0, 0, 0, 5, 30, 50, 2, 6, WHACK, P_FLAIL, IRON, HI_METAL), +/* 3.4.1: unicorn horn left classified as "magic" */ +WEPTOOL("unicorn horn", (char *)0, + 1, 1, 1, 0, 20, 100, 12, 12, PIERCE, P_UNICORN_HORN, BONE, CLR_WHITE), + +/* two special unique artifact "tools" */ +OBJECT(OBJ("Candelabrum of Invocation", "candelabrum"), + BITS(0,0,1,0,1,0,1,1,0,0,0,P_NONE,GOLD), 0, + TOOL_CLASS, 0, 0,10, 5000, 0, 0, 0, 0, 200, HI_GOLD), +OBJECT(OBJ("Bell of Opening", "silver bell"), + BITS(0,0,1,0,1,1,1,1,0,0,0,P_NONE,SILVER), 0, + TOOL_CLASS, 0, 0,10, 5000, 0, 0, 0, 0, 50, HI_SILVER), +#undef TOOL +#undef WEPTOOL + +/* Comestibles ... */ +#define FOOD(name,prob,delay,wt,unk,tin,nutrition,color) OBJECT( \ + OBJ(name,(char *)0), BITS(1,1,unk,0,0,0,0,0,0,0,0,P_NONE,tin), 0, \ + FOOD_CLASS, prob, delay, \ + wt, nutrition/20 + 5, 0, 0, 0, 0, nutrition, color ) +/* all types of food (except tins & corpses) must have a delay of at least 1. */ +/* delay on corpses is computed and is weight dependant */ +/* dog eats foods 0-4 but prefers tripe rations above all others */ +/* fortune cookies can be read */ +/* carrots improve your vision */ +/* +0 tins contain monster meat */ +/* +1 tins (of spinach) make you stronger (like Popeye) */ +/* food CORPSE is a cadaver of some type */ +/* meatballs/sticks/rings are only created from objects via stone to flesh */ + +/* meat */ +FOOD("tripe ration", 140, 2, 10, 0, FLESH, 200, CLR_BROWN), +FOOD("corpse", 0, 1, 0, 0, FLESH, 0, CLR_BROWN), +FOOD("egg", 85, 1, 1, 1, FLESH, 80, CLR_WHITE), +FOOD("meatball", 0, 1, 1, 0, FLESH, 5, CLR_BROWN), +FOOD("meat stick", 0, 1, 1, 0, FLESH, 5, CLR_BROWN), +FOOD("huge chunk of meat", 0,20,400, 0, FLESH,2000, CLR_BROWN), +/* special case because it's not mergable */ +OBJECT(OBJ("meat ring", (char *)0), + BITS(1,0,0,0,0,0,0,0,0,0,0,0,FLESH), + 0, FOOD_CLASS, 0, 1, 5, 1, 0, 0, 0, 0, 5, CLR_BROWN), + +/* fruits & veggies */ +FOOD("kelp frond", 0, 1, 1, 0, VEGGY, 30, CLR_GREEN), +FOOD("eucalyptus leaf", 3, 1, 1, 0, VEGGY, 30, CLR_GREEN), +FOOD("apple", 15, 1, 2, 0, VEGGY, 50, CLR_RED), +FOOD("orange", 10, 1, 2, 0, VEGGY, 80, CLR_ORANGE), +FOOD("pear", 10, 1, 2, 0, VEGGY, 50, CLR_BRIGHT_GREEN), +FOOD("melon", 10, 1, 5, 0, VEGGY, 100, CLR_BRIGHT_GREEN), +FOOD("banana", 10, 1, 2, 0, VEGGY, 80, CLR_YELLOW), +FOOD("carrot", 15, 1, 2, 0, VEGGY, 50, CLR_ORANGE), +FOOD("sprig of wolfsbane", 7, 1, 1, 0, VEGGY, 40, CLR_GREEN), +FOOD("clove of garlic", 7, 1, 1, 0, VEGGY, 40, CLR_WHITE), +FOOD("slime mold", 75, 1, 5, 0, VEGGY, 250, HI_ORGANIC), + +/* people food */ +FOOD("lump of royal jelly", 0, 1, 2, 0, VEGGY, 200, CLR_YELLOW), +FOOD("cream pie", 25, 1, 10, 0, VEGGY, 100, CLR_WHITE), +FOOD("candy bar", 13, 1, 2, 0, VEGGY, 100, CLR_BROWN), +FOOD("fortune cookie", 55, 1, 1, 0, VEGGY, 40, CLR_YELLOW), +FOOD("pancake", 25, 2, 2, 0, VEGGY, 200, CLR_YELLOW), +FOOD("lembas wafer", 20, 2, 5, 0, VEGGY, 800, CLR_WHITE), +FOOD("cram ration", 20, 3, 15, 0, VEGGY, 600, HI_ORGANIC), +FOOD("food ration", 380, 5, 20, 0, VEGGY, 800, HI_ORGANIC), +FOOD("K-ration", 0, 1, 10, 0, VEGGY, 400, HI_ORGANIC), +FOOD("C-ration", 0, 1, 10, 0, VEGGY, 300, HI_ORGANIC), +FOOD("tin", 75, 0, 10, 1, METAL, 0, HI_METAL), +#undef FOOD + +/* potions ... */ +#define POTION(name,desc,mgc,power,prob,cost,color) OBJECT( \ + OBJ(name,desc), BITS(0,1,0,0,mgc,0,0,0,0,0,0,P_NONE,GLASS), power, \ + POTION_CLASS, prob, 0, 20, cost, 0, 0, 0, 0, 10, color ) +POTION("gain ability", "ruby", 1, 0, 42, 300, CLR_RED), +POTION("restore ability", "pink", 1, 0, 40, 100, CLR_BRIGHT_MAGENTA), +POTION("confusion", "orange", 1, CONFUSION, 42, 100, CLR_ORANGE), +POTION("blindness", "yellow", 1, BLINDED, 40, 150, CLR_YELLOW), +POTION("paralysis", "emerald", 1, 0, 42, 300, CLR_BRIGHT_GREEN), +POTION("speed", "dark green", 1, FAST, 42, 200, CLR_GREEN), +POTION("levitation", "cyan", 1, LEVITATION, 42, 200, CLR_CYAN), +POTION("hallucination", "sky blue", 1, HALLUC, 40, 100, CLR_CYAN), +POTION("invisibility", "brilliant blue",1, INVIS, 40, 150, CLR_BRIGHT_BLUE), +POTION("see invisible", "magenta", 1, SEE_INVIS, 42, 50, CLR_MAGENTA), +POTION("healing", "purple-red", 1, 0, 57, 100, CLR_MAGENTA), +POTION("extra healing", "puce", 1, 0, 47, 100, CLR_RED), +POTION("gain level", "milky", 1, 0, 20, 300, CLR_WHITE), +POTION("enlightenment", "swirly", 1, 0, 20, 200, CLR_BROWN), +POTION("monster detection", "bubbly", 1, 0, 40, 150, CLR_WHITE), +POTION("object detection", "smoky", 1, 0, 42, 150, CLR_GRAY), +POTION("gain energy", "cloudy", 1, 0, 42, 150, CLR_WHITE), +POTION("sleeping", "effervescent", 1, 0, 42, 100, CLR_GRAY), +POTION("full healing", "black", 1, 0, 10, 200, CLR_BLACK), +POTION("polymorph", "golden", 1, 0, 10, 200, CLR_YELLOW), +POTION("booze", "brown", 0, 0, 42, 50, CLR_BROWN), +POTION("sickness", "fizzy", 0, 0, 42, 50, CLR_CYAN), +POTION("fruit juice", "dark", 0, 0, 42, 50, CLR_BLACK), +POTION("acid", "white", 0, 0, 10, 250, CLR_WHITE), +POTION("oil", "murky", 0, 0, 30, 250, CLR_BROWN), +POTION("water", "clear", 0, 0, 92, 100, CLR_CYAN), +#undef POTION + +/* scrolls ... */ +#define SCROLL(name,text,mgc,prob,cost) OBJECT( \ + OBJ(name,text), BITS(0,1,0,0,mgc,0,0,0,0,0,0,P_NONE,PAPER), 0, \ + SCROLL_CLASS, prob, 0, 5, cost, 0, 0, 0, 0, 6, HI_PAPER ) + SCROLL("enchant armor", "ZELGO MER", 1, 63, 80), + SCROLL("destroy armor", "JUYED AWK YACC", 1, 45, 100), + SCROLL("confuse monster", "NR 9", 1, 53, 100), + SCROLL("scare monster", "XIXAXA XOXAXA XUXAXA", 1, 35, 100), + SCROLL("remove curse", "PRATYAVAYAH", 1, 65, 80), + SCROLL("enchant weapon", "DAIYEN FOOELS", 1, 80, 60), + SCROLL("create monster", "LEP GEX VEN ZEA", 1, 45, 200), + SCROLL("taming", "PRIRUTSENIE", 1, 15, 200), + SCROLL("genocide", "ELBIB YLOH", 1, 15, 300), + SCROLL("light", "VERR YED HORRE", 1, 90, 50), + SCROLL("teleportation", "VENZAR BORGAVVE", 1, 55, 100), + SCROLL("gold detection", "THARR", 1, 33, 100), + SCROLL("food detection", "YUM YUM", 1, 25, 100), + SCROLL("identify", "KERNOD WEL", 1, 180, 20), + SCROLL("magic mapping", "ELAM EBOW", 1, 45, 100), + SCROLL("amnesia", "DUAM XNAHT", 1, 35, 200), + SCROLL("fire", "ANDOVA BEGARIN", 1, 30, 100), + SCROLL("earth", "KIRJE", 1, 18, 200), + SCROLL("punishment", "VE FORBRYDERNE", 1, 15, 300), + SCROLL("charging", "HACKEM MUCHE", 1, 15, 300), + SCROLL("stinking cloud", "VELOX NEB", 1, 15, 300), + SCROLL((char *)0, "FOOBIE BLETCH", 1, 0, 100), + SCROLL((char *)0, "TEMOV", 1, 0, 100), + SCROLL((char *)0, "GARVEN DEH", 1, 0, 100), + SCROLL((char *)0, "READ ME", 1, 0, 100), + /* these must come last because they have special descriptions */ +#ifdef MAIL + SCROLL("mail", "stamped", 0, 0, 0), +#endif + SCROLL("blank paper", "unlabeled", 0, 28, 60), +#undef SCROLL + +/* spellbooks ... */ +#define SPELL(name,desc,sub,prob,delay,level,mgc,dir,color) OBJECT( \ + OBJ(name,desc), BITS(0,0,0,0,mgc,0,0,0,0,0,dir,sub,PAPER), 0, \ + SPBOOK_CLASS, prob, delay, \ + 50, level*100, 0, 0, 0, level, 20, color ) +SPELL("dig", "parchment", P_MATTER_SPELL, 20, 6, 5, 1, RAY, HI_PAPER), +SPELL("magic missile", "vellum", P_ATTACK_SPELL, 45, 2, 2, 1, RAY, HI_PAPER), +SPELL("fireball", "ragged", P_ATTACK_SPELL, 20, 4, 4, 1, RAY, HI_PAPER), +SPELL("cone of cold", "dog eared", P_ATTACK_SPELL, 10, 7, 4, 1, RAY, HI_PAPER), +SPELL("sleep", "mottled", P_ENCHANTMENT_SPELL, 50, 1, 1, 1, RAY, HI_PAPER), +SPELL("finger of death", "stained", P_ATTACK_SPELL, 5, 10, 7, 1, RAY, HI_PAPER), +SPELL("light", "cloth", P_DIVINATION_SPELL, 45, 1, 1, 1, NODIR, HI_CLOTH), +SPELL("detect monsters", "leather", P_DIVINATION_SPELL, 43, 1, 1, 1, NODIR, HI_LEATHER), +SPELL("healing", "white", P_HEALING_SPELL, 40, 2, 1, 1, IMMEDIATE, CLR_WHITE), +SPELL("knock", "pink", P_MATTER_SPELL, 35, 1, 1, 1, IMMEDIATE, CLR_BRIGHT_MAGENTA), +SPELL("force bolt", "red", P_ATTACK_SPELL, 35, 2, 1, 1, IMMEDIATE, CLR_RED), +SPELL("confuse monster", "orange", P_ENCHANTMENT_SPELL, 30, 2, 2, 1, IMMEDIATE, CLR_ORANGE), +SPELL("cure blindness", "yellow", P_HEALING_SPELL, 25, 2, 2, 1, IMMEDIATE, CLR_YELLOW), +SPELL("drain life", "velvet", P_ATTACK_SPELL, 10, 2, 2, 1, IMMEDIATE, CLR_MAGENTA), +SPELL("slow monster", "light green", P_ENCHANTMENT_SPELL, 30, 2, 2, 1, IMMEDIATE, CLR_BRIGHT_GREEN), +SPELL("wizard lock", "dark green", P_MATTER_SPELL, 30, 3, 2, 1, IMMEDIATE, CLR_GREEN), +SPELL("create monster", "turquoise", P_CLERIC_SPELL, 35, 3, 2, 1, NODIR, CLR_BRIGHT_CYAN), +SPELL("detect food", "cyan", P_DIVINATION_SPELL, 30, 3, 2, 1, NODIR, CLR_CYAN), +SPELL("cause fear", "light blue", P_ENCHANTMENT_SPELL, 25, 3, 3, 1, NODIR, CLR_BRIGHT_BLUE), +SPELL("clairvoyance", "dark blue", P_DIVINATION_SPELL, 15, 3, 3, 1, NODIR, CLR_BLUE), +SPELL("cure sickness", "indigo", P_HEALING_SPELL, 32, 3, 3, 1, NODIR, CLR_BLUE), +SPELL("charm monster", "magenta", P_ENCHANTMENT_SPELL, 20, 3, 3, 1, IMMEDIATE, CLR_MAGENTA), +SPELL("haste self", "purple", P_ESCAPE_SPELL, 33, 4, 3, 1, NODIR, CLR_MAGENTA), +SPELL("detect unseen", "violet", P_DIVINATION_SPELL, 20, 4, 3, 1, NODIR, CLR_MAGENTA), +SPELL("levitation", "tan", P_ESCAPE_SPELL, 20, 4, 4, 1, NODIR, CLR_BROWN), +SPELL("extra healing", "plaid", P_HEALING_SPELL, 27, 5, 3, 1, IMMEDIATE, CLR_GREEN), +SPELL("restore ability", "light brown", P_HEALING_SPELL, 25, 5, 4, 1, NODIR, CLR_BROWN), +SPELL("invisibility", "dark brown", P_ESCAPE_SPELL, 25, 5, 4, 1, NODIR, CLR_BROWN), +SPELL("detect treasure", "gray", P_DIVINATION_SPELL, 20, 5, 4, 1, NODIR, CLR_GRAY), +SPELL("remove curse", "wrinkled", P_CLERIC_SPELL, 25, 5, 3, 1, NODIR, HI_PAPER), +SPELL("magic mapping", "dusty", P_DIVINATION_SPELL, 18, 7, 5, 1, NODIR, HI_PAPER), +SPELL("identify", "bronze", P_DIVINATION_SPELL, 20, 6, 3, 1, NODIR, HI_COPPER), +SPELL("turn undead", "copper", P_CLERIC_SPELL, 16, 8, 6, 1, IMMEDIATE, HI_COPPER), +SPELL("polymorph", "silver", P_MATTER_SPELL, 10, 8, 6, 1, IMMEDIATE, HI_SILVER), +SPELL("teleport away", "gold", P_ESCAPE_SPELL, 15, 6, 6, 1, IMMEDIATE, HI_GOLD), +SPELL("create familiar", "glittering", P_CLERIC_SPELL, 10, 7, 6, 1, NODIR, CLR_WHITE), +SPELL("cancellation", "shining", P_MATTER_SPELL, 15, 8, 7, 1, IMMEDIATE, CLR_WHITE), +SPELL("protection", "dull", P_CLERIC_SPELL, 18, 3, 1, 1, NODIR, HI_PAPER), +SPELL("jumping", "thin", P_ESCAPE_SPELL, 20, 3, 1, 1, IMMEDIATE, HI_PAPER), +SPELL("stone to flesh", "thick", P_HEALING_SPELL, 15, 1, 3, 1, IMMEDIATE, HI_PAPER), +#if 0 /* DEFERRED */ +SPELL("flame sphere", "canvas", P_MATTER_SPELL, 20, 2, 1, 1, NODIR, CLR_BROWN), +SPELL("freeze sphere", "hardcover", P_MATTER_SPELL, 20, 2, 1, 1, NODIR, CLR_BROWN), +#endif +/* blank spellbook must come last because it retains its description */ +SPELL("blank paper", "plain", P_NONE, 18, 0, 0, 0, 0, HI_PAPER), +/* a special, one of a kind, spellbook */ +OBJECT(OBJ("Book of the Dead", "papyrus"), BITS(0,0,1,0,1,0,1,1,0,0,0,P_NONE,PAPER), 0, + SPBOOK_CLASS, 0, 0,20, 10000, 0, 0, 0, 7, 20, HI_PAPER), +#undef SPELL + +/* wands ... */ +#define WAND(name,typ,prob,cost,mgc,dir,metal,color) OBJECT( \ + OBJ(name,typ), BITS(0,0,1,0,mgc,1,0,0,0,0,dir,P_NONE,metal), 0, \ + WAND_CLASS, prob, 0, 7, cost, 0, 0, 0, 0, 30, color ) +WAND("light", "glass", 95, 100, 1, NODIR, GLASS, HI_GLASS), +WAND("secret door detection", "balsa", + 50, 150, 1, NODIR, WOOD, HI_WOOD), +WAND("enlightenment", "crystal", 15, 150, 1, NODIR, GLASS, HI_GLASS), +WAND("create monster", "maple", 45, 200, 1, NODIR, WOOD, HI_WOOD), +WAND("wishing", "pine", 5, 500, 1, NODIR, WOOD, HI_WOOD), +WAND("nothing", "oak", 25, 100, 0, IMMEDIATE, WOOD, HI_WOOD), +WAND("striking", "ebony", 75, 150, 1, IMMEDIATE, WOOD, HI_WOOD), +WAND("make invisible", "marble", 45, 150, 1, IMMEDIATE, MINERAL, HI_MINERAL), +WAND("slow monster", "tin", 50, 150, 1, IMMEDIATE, METAL, HI_METAL), +WAND("speed monster", "brass", 50, 150, 1, IMMEDIATE, COPPER, HI_COPPER), +WAND("undead turning", "copper", 50, 150, 1, IMMEDIATE, COPPER, HI_COPPER), +WAND("polymorph", "silver", 45, 200, 1, IMMEDIATE, SILVER, HI_SILVER), +WAND("cancellation", "platinum", 45, 200, 1, IMMEDIATE, PLATINUM, CLR_WHITE), +WAND("teleportation", "iridium", 45, 200, 1, IMMEDIATE, METAL, CLR_BRIGHT_CYAN), +WAND("opening", "zinc", 25, 150, 1, IMMEDIATE, METAL, HI_METAL), +WAND("locking", "aluminum", 25, 150, 1, IMMEDIATE, METAL, HI_METAL), +WAND("probing", "uranium", 30, 150, 1, IMMEDIATE, METAL, HI_METAL), +WAND("digging", "iron", 55, 150, 1, RAY, IRON, HI_METAL), +WAND("magic missile", "steel", 50, 150, 1, RAY, IRON, HI_METAL), +WAND("fire", "hexagonal",40, 175, 1, RAY, IRON, HI_METAL), +WAND("cold", "short", 40, 175, 1, RAY, IRON, HI_METAL), +WAND("sleep", "runed", 50, 175, 1, RAY, IRON, HI_METAL), +WAND("death", "long", 5, 500, 1, RAY, IRON, HI_METAL), +WAND("lightning", "curved", 40, 175, 1, RAY, IRON, HI_METAL), +WAND((char *)0, "forked", 0, 150, 1, 0, WOOD, HI_WOOD), +WAND((char *)0, "spiked", 0, 150, 1, 0, IRON, HI_METAL), +WAND((char *)0, "jeweled", 0, 150, 1, 0, IRON, HI_MINERAL), +#undef WAND + +/* coins ... - so far, gold is all there is */ +#define COIN(name,prob,metal,worth) OBJECT( \ + OBJ(name,(char *)0), BITS(0,1,0,0,0,0,0,0,0,0,0,P_NONE,metal), 0, \ + COIN_CLASS, prob, 0, 1, worth, 0, 0, 0, 0, 0, HI_GOLD ) + COIN("gold piece", 1000, GOLD,1), +#undef COIN + +/* gems ... - includes stones and rocks but not boulders */ +#define GEM(name,desc,prob,wt,gval,nutr,mohs,glass,color) OBJECT( \ + OBJ(name,desc), \ + BITS(0,1,0,0,0,0,0,0,0,HARDGEM(mohs),0,-P_SLING,glass), 0, \ + GEM_CLASS, prob, 0, 1, gval, 3, 3, 0, 0, nutr, color ) +#define ROCK(name,desc,kn,prob,wt,gval,sdam,ldam,mgc,nutr,mohs,glass,color) OBJECT( \ + OBJ(name,desc), \ + BITS(kn,1,0,0,mgc,0,0,0,0,HARDGEM(mohs),0,-P_SLING,glass), 0, \ + GEM_CLASS, prob, 0, wt, gval, sdam, ldam, 0, 0, nutr, color ) +GEM("dilithium crystal", "white", 2, 1, 4500, 15, 5, GEMSTONE, CLR_WHITE), +GEM("diamond", "white", 3, 1, 4000, 15, 10, GEMSTONE, CLR_WHITE), +GEM("ruby", "red", 4, 1, 3500, 15, 9, GEMSTONE, CLR_RED), +GEM("jacinth", "orange", 3, 1, 3250, 15, 9, GEMSTONE, CLR_ORANGE), +GEM("sapphire", "blue", 4, 1, 3000, 15, 9, GEMSTONE, CLR_BLUE), +GEM("black opal", "black", 3, 1, 2500, 15, 8, GEMSTONE, CLR_BLACK), +GEM("emerald", "green", 5, 1, 2500, 15, 8, GEMSTONE, CLR_GREEN), +GEM("turquoise", "green", 6, 1, 2000, 15, 6, GEMSTONE, CLR_GREEN), +GEM("citrine", "yellow", 4, 1, 1500, 15, 6, GEMSTONE, CLR_YELLOW), +GEM("aquamarine", "green", 6, 1, 1500, 15, 8, GEMSTONE, CLR_GREEN), +GEM("amber", "yellowish brown", 8, 1, 1000, 15, 2, GEMSTONE, CLR_BROWN), +GEM("topaz", "yellowish brown", 10, 1, 900, 15, 8, GEMSTONE, CLR_BROWN), +GEM("jet", "black", 6, 1, 850, 15, 7, GEMSTONE, CLR_BLACK), +GEM("opal", "white", 12, 1, 800, 15, 6, GEMSTONE, CLR_WHITE), +GEM("chrysoberyl", "yellow", 8, 1, 700, 15, 5, GEMSTONE, CLR_YELLOW), +GEM("garnet", "red", 12, 1, 700, 15, 7, GEMSTONE, CLR_RED), +GEM("amethyst", "violet", 14, 1, 600, 15, 7, GEMSTONE, CLR_MAGENTA), +GEM("jasper", "red", 15, 1, 500, 15, 7, GEMSTONE, CLR_RED), +GEM("fluorite", "violet", 15, 1, 400, 15, 4, GEMSTONE, CLR_MAGENTA), +GEM("obsidian", "black", 9, 1, 200, 15, 6, GEMSTONE, CLR_BLACK), +GEM("agate", "orange", 12, 1, 200, 15, 6, GEMSTONE, CLR_ORANGE), +GEM("jade", "green", 10, 1, 300, 15, 6, GEMSTONE, CLR_GREEN), +GEM("worthless piece of white glass", "white", 77, 1, 0, 6, 5, GLASS, CLR_WHITE), +GEM("worthless piece of blue glass", "blue", 77, 1, 0, 6, 5, GLASS, CLR_BLUE), +GEM("worthless piece of red glass", "red", 77, 1, 0, 6, 5, GLASS, CLR_RED), +GEM("worthless piece of yellowish brown glass", "yellowish brown", + 77, 1, 0, 6, 5, GLASS, CLR_BROWN), +GEM("worthless piece of orange glass", "orange", 76, 1, 0, 6, 5, GLASS, CLR_ORANGE), +GEM("worthless piece of yellow glass", "yellow", 77, 1, 0, 6, 5, GLASS, CLR_YELLOW), +GEM("worthless piece of black glass", "black", 76, 1, 0, 6, 5, GLASS, CLR_BLACK), +GEM("worthless piece of green glass", "green", 77, 1, 0, 6, 5, GLASS, CLR_GREEN), +GEM("worthless piece of violet glass", "violet", 77, 1, 0, 6, 5, GLASS, CLR_MAGENTA), + +/* Placement note: there is a wishable subrange for + * "gray stones" in the o_ranges[] array in objnam.c + * that is currently everything between luckstones and flint (inclusive). + */ +ROCK("luckstone", "gray", 0, 10, 10, 60, 3, 3, 1, 10, 7, MINERAL, CLR_GRAY), +ROCK("loadstone", "gray", 0, 10, 500, 1, 3, 3, 1, 10, 6, MINERAL, CLR_GRAY), +ROCK("touchstone", "gray", 0, 8, 10, 45, 3, 3, 1, 10, 6, MINERAL, CLR_GRAY), +ROCK("flint", "gray", 0, 10, 10, 1, 6, 6, 0, 10, 7, MINERAL, CLR_GRAY), +ROCK("rock", (char *)0, 1,100, 10, 0, 3, 3, 0, 10, 7, MINERAL, CLR_GRAY), +#undef GEM +#undef ROCK + +/* miscellaneous ... */ +/* Note: boulders and rocks are not normally created at random; the + * probabilities only come into effect when you try to polymorph them. + * Boulders weigh more than MAX_CARR_CAP; statues use corpsenm to take + * on a specific type and may act as containers (both affect weight). + */ +OBJECT(OBJ("boulder",(char *)0), BITS(1,0,0,0,0,0,0,0,1,0,0,P_NONE,MINERAL), 0, + ROCK_CLASS, 100, 0, 6000, 0, 20, 20, 0, 0, 2000, HI_MINERAL), +OBJECT(OBJ("statue", (char *)0), BITS(1,0,0,1,0,0,0,0,0,0,0,P_NONE,MINERAL), 0, + ROCK_CLASS, 900, 0, 2500, 0, 20, 20, 0, 0, 2500, CLR_WHITE), + +OBJECT(OBJ("heavy iron ball", (char *)0), BITS(1,0,0,0,0,0,0,0,0,0,WHACK,P_NONE,IRON), 0, + BALL_CLASS, 1000, 0, 480, 10, 25, 25, 0, 0, 200, HI_METAL), + /* +d4 when "very heavy" */ +OBJECT(OBJ("iron chain", (char *)0), BITS(1,0,0,0,0,0,0,0,0,0,WHACK,P_NONE,IRON), 0, + CHAIN_CLASS, 1000, 0, 120, 0, 4, 4, 0, 0, 200, HI_METAL), + /* +1 both l & s */ + +OBJECT(OBJ("blinding venom", "splash of venom"), + BITS(0,1,0,0,0,0,0,1,0,0,0,P_NONE,LIQUID), 0, + VENOM_CLASS, 500, 0, 1, 0, 0, 0, 0, 0, 0, HI_ORGANIC), +OBJECT(OBJ("acid venom", "splash of venom"), + BITS(0,1,0,0,0,0,0,1,0,0,0,P_NONE,LIQUID), 0, + VENOM_CLASS, 500, 0, 1, 0, 6, 6, 0, 0, 0, HI_ORGANIC), + /* +d6 small or large */ + +/* fencepost, the deadly Array Terminator -- name [1st arg] *must* be NULL */ + OBJECT(OBJ((char *)0,(char *)0), BITS(0,0,0,0,0,0,0,0,0,0,0,P_NONE,0), 0, + ILLOBJ_CLASS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) +}; /* objects[] */ + +#ifndef OBJECTS_PASS_2_ + +/* perform recursive compilation for second structure */ +# undef OBJ +# undef OBJECT +# define OBJECTS_PASS_2_ +#include "objects.c" + +void NDECL(objects_init); + +/* dummy routine used to force linkage */ +void +objects_init() +{ + return; +} + +#endif /* !OBJECTS_PASS_2_ */ + +/*objects.c*/ diff --git a/src/objnam.c b/src/objnam.c new file mode 100644 index 0000000..2130432 --- /dev/null +++ b/src/objnam.c @@ -0,0 +1,2787 @@ +/* SCCS Id: @(#)objnam.c 3.4 2003/12/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* "an uncursed greased partly eaten guardian naga hatchling [corpse]" */ +#define PREFIX 80 /* (56) */ +#define SCHAR_LIM 127 +#define NUMOBUF 12 + +STATIC_DCL char *FDECL(strprepend,(char *,const char *)); +#ifdef OVLB +static boolean FDECL(wishymatch, (const char *,const char *,BOOLEAN_P)); +#endif +static char *NDECL(nextobuf); +static void FDECL(add_erosion_words, (struct obj *, char *)); + +struct Jitem { + int item; + const char *name; +}; + +/* true for gems/rocks that should have " stone" appended to their names */ +#define GemStone(typ) (typ == FLINT || \ + (objects[typ].oc_material == GEMSTONE && \ + (typ != DILITHIUM_CRYSTAL && typ != RUBY && \ + typ != DIAMOND && typ != SAPPHIRE && \ + typ != BLACK_OPAL && \ + typ != EMERALD && typ != OPAL))) + +#ifndef OVLB + +STATIC_DCL struct Jitem Japanese_items[]; + +#else /* OVLB */ + +STATIC_OVL struct Jitem Japanese_items[] = { + { SHORT_SWORD, "wakizashi" }, + { BROADSWORD, "ninja-to" }, + { FLAIL, "nunchaku" }, + { GLAIVE, "naginata" }, + { LOCK_PICK, "osaku" }, + { WOODEN_HARP, "koto" }, + { KNIFE, "shito" }, + { PLATE_MAIL, "tanko" }, + { HELMET, "kabuto" }, + { LEATHER_GLOVES, "yugake" }, + { FOOD_RATION, "gunyoki" }, + { POT_BOOZE, "sake" }, + {0, "" } +}; + +#endif /* OVLB */ + +STATIC_DCL const char *FDECL(Japanese_item_name,(int i)); + +#ifdef OVL1 + +STATIC_OVL char * +strprepend(s,pref) +register char *s; +register const char *pref; +{ + register int i = (int)strlen(pref); + + if(i > PREFIX) { + impossible("PREFIX too short (for %d).", i); + return(s); + } + s -= i; + (void) strncpy(s, pref, i); /* do not copy trailing 0 */ + return(s); +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* manage a pool of BUFSZ buffers, so callers don't have to */ +static char * +nextobuf() +{ + static char NEARDATA bufs[NUMOBUF][BUFSZ]; + static int bufidx = 0; + + bufidx = (bufidx + 1) % NUMOBUF; + return bufs[bufidx]; +} + +char * +obj_typename(otyp) +register int otyp; +{ + char *buf = nextobuf(); + register struct objclass *ocl = &objects[otyp]; + register const char *actualn = OBJ_NAME(*ocl); + register const char *dn = OBJ_DESCR(*ocl); + register const char *un = ocl->oc_uname; + register int nn = ocl->oc_name_known; + + if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp)) + actualn = Japanese_item_name(otyp); + switch(ocl->oc_class) { + case COIN_CLASS: + Strcpy(buf, "coin"); + break; + case POTION_CLASS: + Strcpy(buf, "potion"); + break; + case SCROLL_CLASS: + Strcpy(buf, "scroll"); + break; + case WAND_CLASS: + Strcpy(buf, "wand"); + break; + case SPBOOK_CLASS: + Strcpy(buf, "spellbook"); + break; + case RING_CLASS: + Strcpy(buf, "ring"); + break; + case AMULET_CLASS: + if(nn) + Strcpy(buf,actualn); + else + Strcpy(buf,"amulet"); + if(un) + Sprintf(eos(buf)," called %s",un); + if(dn) + Sprintf(eos(buf)," (%s)",dn); + return(buf); + default: + if(nn) { + Strcpy(buf, actualn); + if (GemStone(otyp)) + Strcat(buf, " stone"); + if(un) + Sprintf(eos(buf), " called %s", un); + if(dn) + Sprintf(eos(buf), " (%s)", dn); + } else { + Strcpy(buf, dn ? dn : actualn); + if(ocl->oc_class == GEM_CLASS) + Strcat(buf, (ocl->oc_material == MINERAL) ? + " stone" : " gem"); + if(un) + Sprintf(eos(buf), " called %s", un); + } + return(buf); + } + /* here for ring/scroll/potion/wand */ + if(nn) { + if (ocl->oc_unique) + Strcpy(buf, actualn); /* avoid spellbook of Book of the Dead */ + else + Sprintf(eos(buf), " of %s", actualn); + } + if(un) + Sprintf(eos(buf), " called %s", un); + if(dn) + Sprintf(eos(buf), " (%s)", dn); + return(buf); +} + +/* less verbose result than obj_typename(); either the actual name + or the description (but not both); user-assigned name is ignored */ +char * +simple_typename(otyp) +int otyp; +{ + char *bufp, *pp, *save_uname = objects[otyp].oc_uname; + + objects[otyp].oc_uname = 0; /* suppress any name given by user */ + bufp = obj_typename(otyp); + objects[otyp].oc_uname = save_uname; + if ((pp = strstri(bufp, " (")) != 0) + *pp = '\0'; /* strip the appended description */ + return bufp; +} + +boolean +obj_is_pname(obj) +register struct obj *obj; +{ + return((boolean)(obj->dknown && obj->known && obj->onamelth && + /* Since there aren't any objects which are both + artifacts and unique, the last check is redundant. */ + obj->oartifact && !objects[obj->otyp].oc_unique)); +} + +/* Give the name of an object seen at a distance. Unlike xname/doname, + * we don't want to set dknown if it's not set already. The kludge used is + * to temporarily set Blind so that xname() skips the dknown setting. This + * assumes that we don't want to do this too often; if this function becomes + * frequently used, it'd probably be better to pass a parameter to xname() + * or doname() instead. + */ +char * +distant_name(obj, func) +register struct obj *obj; +char *FDECL((*func), (OBJ_P)); +{ + char *str; + + long save_Blinded = Blinded; + Blinded = 1; + str = (*func)(obj); + Blinded = save_Blinded; + return str; +} + +/* convert player specified fruit name into corresponding fruit juice name + ("slice of pizza" -> "pizza juice" rather than "slice of pizza juice") */ +char * +fruitname(juice) +boolean juice; /* whether or not to append " juice" to the name */ +{ + char *buf = nextobuf(); + const char *fruit_nam = strstri(pl_fruit, " of "); + + if (fruit_nam) + fruit_nam += 4; /* skip past " of " */ + else + fruit_nam = pl_fruit; /* use it as is */ + + Sprintf(buf, "%s%s", makesingular(fruit_nam), juice ? " juice" : ""); + return buf; +} + +#endif /* OVLB */ +#ifdef OVL1 + +char * +xname(obj) +register struct obj *obj; +{ + register char *buf; + register int typ = obj->otyp; + register struct objclass *ocl = &objects[typ]; + register int nn = ocl->oc_name_known; + register const char *actualn = OBJ_NAME(*ocl); + register const char *dn = OBJ_DESCR(*ocl); + register const char *un = ocl->oc_uname; + + buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */ + if (Role_if(PM_SAMURAI) && Japanese_item_name(typ)) + actualn = Japanese_item_name(typ); + + buf[0] = '\0'; + /* + * clean up known when it's tied to oc_name_known, eg after AD_DRIN + * This is only required for unique objects since the article + * printed for the object is tied to the combination of the two + * and printing the wrong article gives away information. + */ + if (!nn && ocl->oc_uses_known && ocl->oc_unique) obj->known = 0; + if (!Blind) obj->dknown = TRUE; + if (Role_if(PM_PRIEST)) obj->bknown = TRUE; + if (obj_is_pname(obj)) + goto nameit; + switch (obj->oclass) { + case AMULET_CLASS: + if (!obj->dknown) + Strcpy(buf, "amulet"); + else if (typ == AMULET_OF_YENDOR || + typ == FAKE_AMULET_OF_YENDOR) + /* each must be identified individually */ + Strcpy(buf, obj->known ? actualn : dn); + else if (nn) + Strcpy(buf, actualn); + else if (un) + Sprintf(buf,"amulet called %s", un); + else + Sprintf(buf,"%s amulet", dn); + break; + case WEAPON_CLASS: + if (is_poisonable(obj) && obj->opoisoned) + Strcpy(buf, "poisoned "); + case VENOM_CLASS: + case TOOL_CLASS: + if (typ == LENSES) + Strcpy(buf, "pair of "); + + if (!obj->dknown) + Strcat(buf, dn ? dn : actualn); + else if (nn) + Strcat(buf, actualn); + else if (un) { + Strcat(buf, dn ? dn : actualn); + Strcat(buf, " called "); + Strcat(buf, un); + } else + Strcat(buf, dn ? dn : actualn); + /* If we use an() here we'd have to remember never to use */ + /* it whenever calling doname() or xname(). */ + if (typ == FIGURINE) + Sprintf(eos(buf), " of a%s %s", + index(vowels,*(mons[obj->corpsenm].mname)) ? "n" : "", + mons[obj->corpsenm].mname); + break; + case ARMOR_CLASS: + /* depends on order of the dragon scales objects */ + if (typ >= GRAY_DRAGON_SCALES && typ <= YELLOW_DRAGON_SCALES) { + Sprintf(buf, "set of %s", actualn); + break; + } + if(is_boots(obj) || is_gloves(obj)) Strcpy(buf,"pair of "); + + if(obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD + && !obj->dknown) { + Strcpy(buf, "shield"); + break; + } + if(obj->otyp == SHIELD_OF_REFLECTION && !obj->dknown) { + Strcpy(buf, "smooth shield"); + break; + } + + if(nn) Strcat(buf, actualn); + else if(un) { + if(is_boots(obj)) + Strcat(buf,"boots"); + else if(is_gloves(obj)) + Strcat(buf,"gloves"); + else if(is_cloak(obj)) + Strcpy(buf,"cloak"); + else if(is_helmet(obj)) + Strcpy(buf,"helmet"); + else if(is_shield(obj)) + Strcpy(buf,"shield"); + else + Strcpy(buf,"armor"); + Strcat(buf, " called "); + Strcat(buf, un); + } else Strcat(buf, dn); + break; + case FOOD_CLASS: + if (typ == SLIME_MOLD) { + register struct fruit *f; + + for(f=ffruit; f; f = f->nextf) { + if(f->fid == obj->spe) { + Strcpy(buf, f->fname); + break; + } + } + if (!f) impossible("Bad fruit #%d?", obj->spe); + break; + } + + Strcpy(buf, actualn); + if (typ == TIN && obj->known) { + if(obj->spe > 0) + Strcat(buf, " of spinach"); + else if (obj->corpsenm == NON_PM) + Strcpy(buf, "empty tin"); + else if (vegetarian(&mons[obj->corpsenm])) + Sprintf(eos(buf), " of %s", mons[obj->corpsenm].mname); + else + Sprintf(eos(buf), " of %s meat", mons[obj->corpsenm].mname); + } + break; + case COIN_CLASS: + case CHAIN_CLASS: + Strcpy(buf, actualn); + break; + case ROCK_CLASS: + if (typ == STATUE) + Sprintf(buf, "%s%s of %s%s", + (Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) ? "historic " : "" , + actualn, + type_is_pname(&mons[obj->corpsenm]) ? "" : + (mons[obj->corpsenm].geno & G_UNIQ) ? "the " : + (index(vowels,*(mons[obj->corpsenm].mname)) ? + "an " : "a "), + mons[obj->corpsenm].mname); + else Strcpy(buf, actualn); + break; + case BALL_CLASS: + Sprintf(buf, "%sheavy iron ball", + (obj->owt > ocl->oc_weight) ? "very " : ""); + break; + case POTION_CLASS: + if (obj->dknown && obj->odiluted) + Strcpy(buf, "diluted "); + if(nn || un || !obj->dknown) { + Strcat(buf, "potion"); + if(!obj->dknown) break; + if(nn) { + Strcat(buf, " of "); + if (typ == POT_WATER && + obj->bknown && (obj->blessed || obj->cursed)) { + Strcat(buf, obj->blessed ? "holy " : "unholy "); + } + Strcat(buf, actualn); + } else { + Strcat(buf, " called "); + Strcat(buf, un); + } + } else { + Strcat(buf, dn); + Strcat(buf, " potion"); + } + break; + case SCROLL_CLASS: + Strcpy(buf, "scroll"); + if(!obj->dknown) break; + if(nn) { + Strcat(buf, " of "); + Strcat(buf, actualn); + } else if(un) { + Strcat(buf, " called "); + Strcat(buf, un); + } else if (ocl->oc_magic) { + Strcat(buf, " labeled "); + Strcat(buf, dn); + } else { + Strcpy(buf, dn); + Strcat(buf, " scroll"); + } + break; + case WAND_CLASS: + if(!obj->dknown) + Strcpy(buf, "wand"); + else if(nn) + Sprintf(buf, "wand of %s", actualn); + else if(un) + Sprintf(buf, "wand called %s", un); + else + Sprintf(buf, "%s wand", dn); + break; + case SPBOOK_CLASS: + if (!obj->dknown) { + Strcpy(buf, "spellbook"); + } else if (nn) { + if (typ != SPE_BOOK_OF_THE_DEAD) + Strcpy(buf, "spellbook of "); + Strcat(buf, actualn); + } else if (un) { + Sprintf(buf, "spellbook called %s", un); + } else + Sprintf(buf, "%s spellbook", dn); + break; + case RING_CLASS: + if(!obj->dknown) + Strcpy(buf, "ring"); + else if(nn) + Sprintf(buf, "ring of %s", actualn); + else if(un) + Sprintf(buf, "ring called %s", un); + else + Sprintf(buf, "%s ring", dn); + break; + case GEM_CLASS: + { + const char *rock = + (ocl->oc_material == MINERAL) ? "stone" : "gem"; + if (!obj->dknown) { + Strcpy(buf, rock); + } else if (!nn) { + if (un) Sprintf(buf,"%s called %s", rock, un); + else Sprintf(buf, "%s %s", dn, rock); + } else { + Strcpy(buf, actualn); + if (GemStone(typ)) Strcat(buf, " stone"); + } + break; + } + default: + Sprintf(buf,"glorkum %d %d %d", obj->oclass, typ, obj->spe); + } + if (obj->quan != 1L) Strcpy(buf, makeplural(buf)); + + if (obj->onamelth && obj->dknown) { + Strcat(buf, " named "); +nameit: + Strcat(buf, ONAME(obj)); + } + + if (!strncmpi(buf, "the ", 4)) buf += 4; + return(buf); +} + +/* xname() output augmented for multishot missile feedback */ +char * +mshot_xname(obj) +struct obj *obj; +{ + char tmpbuf[BUFSZ]; + char *onm = xname(obj); + + if (m_shot.n > 1 && m_shot.o == obj->otyp) { + /* copy xname's result so that we can reuse its return buffer */ + Strcpy(tmpbuf, onm); + /* "the Nth arrow"; value will eventually be passed to an() or + The(), both of which correctly handle this "the " prefix */ + Sprintf(onm, "the %d%s %s", m_shot.i, ordin(m_shot.i), tmpbuf); + } + + return onm; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +/* used for naming "the unique_item" instead of "a unique_item" */ +boolean +the_unique_obj(obj) +register struct obj *obj; +{ + if (!obj->dknown) + return FALSE; + else if (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known) + return TRUE; /* lie */ + else + return (boolean)(objects[obj->otyp].oc_unique && + (obj->known || obj->otyp == AMULET_OF_YENDOR)); +} + +static void +add_erosion_words(obj,prefix) +struct obj *obj; +char *prefix; +{ + boolean iscrys = (obj->otyp == CRYSKNIFE); + + + if (!is_damageable(obj) && !iscrys) return; + + /* The only cases where any of these bits do double duty are for + * rotted food and diluted potions, which are all not is_damageable(). + */ + if (obj->oeroded && !iscrys) { + switch (obj->oeroded) { + case 2: Strcat(prefix, "very "); break; + case 3: Strcat(prefix, "thoroughly "); break; + } + Strcat(prefix, is_rustprone(obj) ? "rusty " : "burnt "); + } + if (obj->oeroded2 && !iscrys) { + switch (obj->oeroded2) { + case 2: Strcat(prefix, "very "); break; + case 3: Strcat(prefix, "thoroughly "); break; + } + Strcat(prefix, is_corrodeable(obj) ? "corroded " : + "rotted "); + } + if (obj->rknown && obj->oerodeproof) + Strcat(prefix, + iscrys ? "fixed " : + is_rustprone(obj) ? "rustproof " : + is_corrodeable(obj) ? "corrodeproof " : /* "stainless"? */ + is_flammable(obj) ? "fireproof " : ""); +} + +char * +doname(obj) +register struct obj *obj; +{ + boolean ispoisoned = FALSE; + char prefix[PREFIX]; + char tmpbuf[PREFIX+1]; + /* when we have to add something at the start of prefix instead of the + * end (Strcat is used on the end) + */ + register char *bp = xname(obj); + + /* When using xname, we want "poisoned arrow", and when using + * doname, we want "poisoned +0 arrow". This kludge is about the only + * way to do it, at least until someone overhauls xname() and doname(), + * combining both into one function taking a parameter. + */ + /* must check opoisoned--someone can have a weirdly-named fruit */ + if (!strncmp(bp, "poisoned ", 9) && obj->opoisoned) { + bp += 9; + ispoisoned = TRUE; + } + + if(obj->quan != 1L) + Sprintf(prefix, "%ld ", obj->quan); + else if (obj_is_pname(obj) || the_unique_obj(obj)) { + if (!strncmpi(bp, "the ", 4)) + bp += 4; + Strcpy(prefix, "the "); + } else + Strcpy(prefix, "a "); + +#ifdef INVISIBLE_OBJECTS + if (obj->oinvis) Strcat(prefix,"invisible "); +#endif + + if (obj->bknown && + obj->oclass != COIN_CLASS && + (obj->otyp != POT_WATER || !objects[POT_WATER].oc_name_known + || (!obj->cursed && !obj->blessed))) { + /* allow 'blessed clear potion' if we don't know it's holy water; + * always allow "uncursed potion of water" + */ + if (obj->cursed) + Strcat(prefix, "cursed "); + else if (obj->blessed) + Strcat(prefix, "blessed "); + else if ((!obj->known || !objects[obj->otyp].oc_charged || + (obj->oclass == ARMOR_CLASS || + obj->oclass == RING_CLASS)) + /* For most items with charges or +/-, if you know how many + * charges are left or what the +/- is, then you must have + * totally identified the item, so "uncursed" is unneccesary, + * because an identified object not described as "blessed" or + * "cursed" must be uncursed. + * + * If the charges or +/- is not known, "uncursed" must be + * printed to avoid ambiguity between an item whose curse + * status is unknown, and an item known to be uncursed. + */ +#ifdef MAIL + && obj->otyp != SCR_MAIL +#endif + && obj->otyp != FAKE_AMULET_OF_YENDOR + && obj->otyp != AMULET_OF_YENDOR + && !Role_if(PM_PRIEST)) + Strcat(prefix, "uncursed "); + } + + if (obj->greased) Strcat(prefix, "greased "); + + switch(obj->oclass) { + case AMULET_CLASS: + if(obj->owornmask & W_AMUL) + Strcat(bp, " (being worn)"); + break; + case WEAPON_CLASS: + if(ispoisoned) + Strcat(prefix, "poisoned "); +plus: + add_erosion_words(obj, prefix); + if(obj->known) { + Strcat(prefix, sitoa(obj->spe)); + Strcat(prefix, " "); + } + break; + case ARMOR_CLASS: + if(obj->owornmask & W_ARMOR) + Strcat(bp, (obj == uskin) ? " (embedded in your skin)" : + " (being worn)"); + goto plus; + case TOOL_CLASS: + /* weptools already get this done when we go to the +n code */ + if (!is_weptool(obj)) + add_erosion_words(obj, prefix); + if(obj->owornmask & (W_TOOL /* blindfold */ +#ifdef STEED + | W_SADDLE +#endif + )) { + Strcat(bp, " (being worn)"); + break; + } + if (obj->otyp == LEASH && obj->leashmon != 0) { + Strcat(bp, " (in use)"); + break; + } + if (is_weptool(obj)) + goto plus; + if (obj->otyp == CANDELABRUM_OF_INVOCATION) { + if (!obj->spe) + Strcpy(tmpbuf, "no"); + else + Sprintf(tmpbuf, "%d", obj->spe); + Sprintf(eos(bp), " (%s candle%s%s)", + tmpbuf, plur(obj->spe), + !obj->lamplit ? " attached" : ", lit"); + break; + } else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP || + obj->otyp == BRASS_LANTERN || Is_candle(obj)) { + if (Is_candle(obj) && + obj->age < 20L * (long)objects[obj->otyp].oc_cost) + Strcat(prefix, "partly used "); + if(obj->lamplit) + Strcat(bp, " (lit)"); + break; + } + if(objects[obj->otyp].oc_charged) + goto charges; + break; + case WAND_CLASS: + add_erosion_words(obj, prefix); +charges: + if(obj->known) + Sprintf(eos(bp), " (%d:%d)", (int)obj->recharged, obj->spe); + break; + case POTION_CLASS: + if (obj->otyp == POT_OIL && obj->lamplit) + Strcat(bp, " (lit)"); + break; + case RING_CLASS: + add_erosion_words(obj, prefix); +ring: + if(obj->owornmask & W_RINGR) Strcat(bp, " (on right "); + if(obj->owornmask & W_RINGL) Strcat(bp, " (on left "); + if(obj->owornmask & W_RING) { + Strcat(bp, body_part(HAND)); + Strcat(bp, ")"); + } + if(obj->known && objects[obj->otyp].oc_charged) { + Strcat(prefix, sitoa(obj->spe)); + Strcat(prefix, " "); + } + break; + case FOOD_CLASS: + if (obj->oeaten) + Strcat(prefix, "partly eaten "); + if (obj->otyp == CORPSE) { + if (mons[obj->corpsenm].geno & G_UNIQ) { + Sprintf(prefix, "%s%s ", + (type_is_pname(&mons[obj->corpsenm]) ? + "" : "the "), + s_suffix(mons[obj->corpsenm].mname)); + if (obj->oeaten) Strcat(prefix, "partly eaten "); + } else { + Strcat(prefix, mons[obj->corpsenm].mname); + Strcat(prefix, " "); + } + } else if (obj->otyp == EGG) { +#if 0 /* corpses don't tell if they're stale either */ + if (obj->known && stale_egg(obj)) + Strcat(prefix, "stale "); +#endif + if (obj->corpsenm >= LOW_PM && + (obj->known || + mvitals[obj->corpsenm].mvflags & MV_KNOWS_EGG)) { + Strcat(prefix, mons[obj->corpsenm].mname); + Strcat(prefix, " "); + if (obj->spe) + Strcat(bp, " (laid by you)"); + } + } + if (obj->otyp == MEAT_RING) goto ring; + break; + case BALL_CLASS: + case CHAIN_CLASS: + add_erosion_words(obj, prefix); + if(obj->owornmask & W_BALL) + Strcat(bp, " (chained to you)"); + break; + } + + if((obj->owornmask & W_WEP) && !mrg_to_wielded) { + if (obj->quan != 1L) { + Strcat(bp, " (wielded)"); + } else { + const char *hand_s = body_part(HAND); + + if (bimanual(obj)) hand_s = makeplural(hand_s); + Sprintf(eos(bp), " (weapon in %s)", hand_s); + } + } + if(obj->owornmask & W_SWAPWEP) { + if (u.twoweap) + Sprintf(eos(bp), " (wielded in other %s)", + body_part(HAND)); + else + Strcat(bp, " (alternate weapon; not wielded)"); + } + if(obj->owornmask & W_QUIVER) Strcat(bp, " (in quiver)"); + if(obj->unpaid) { + xchar ox, oy; + long quotedprice = unpaid_cost(obj); + struct monst *shkp = (struct monst *)0; + + if (Has_contents(obj) && + get_obj_location(obj, &ox, &oy, BURIED_TOO|CONTAINED_TOO) && + costly_spot(ox, oy) && + (shkp = shop_keeper(*in_rooms(ox, oy, SHOPBASE)))) + quotedprice += contained_cost(obj, shkp, 0L, FALSE, TRUE); + Sprintf(eos(bp), " (unpaid, %ld %s)", + quotedprice, currency(quotedprice)); + } + if (!strncmp(prefix, "a ", 2) && + index(vowels, *(prefix+2) ? *(prefix+2) : *bp) + && (*(prefix+2) || (strncmp(bp, "uranium", 7) + && strncmp(bp, "unicorn", 7) + && strncmp(bp, "eucalyptus", 10)))) { + Strcpy(tmpbuf, prefix); + Strcpy(prefix, "an "); + Strcpy(prefix+3, tmpbuf+2); + } + bp = strprepend(bp, prefix); + return(bp); +} + +#endif /* OVL0 */ +#ifdef OVLB + +/* used from invent.c */ +boolean +not_fully_identified(otmp) +register struct obj *otmp; +{ +#ifdef GOLDOBJ + /* gold doesn't have any interesting attributes [yet?] */ + if (otmp->oclass == COIN_CLASS) return FALSE; /* always fully ID'd */ +#endif + /* check fundamental ID hallmarks first */ + if (!otmp->known || !otmp->dknown || +#ifdef MAIL + (!otmp->bknown && otmp->otyp != SCR_MAIL) || +#else + !otmp->bknown || +#endif + !objects[otmp->otyp].oc_name_known) /* ?redundant? */ + return TRUE; + if (otmp->oartifact && undiscovered_artifact(otmp->oartifact)) + return TRUE; + /* otmp->rknown is the only item of interest if we reach here */ + /* + * Note: if a revision ever allows scrolls to become fireproof or + * rings to become shockproof, this checking will need to be revised. + * `rknown' ID only matters if xname() will provide the info about it. + */ + if (otmp->rknown || (otmp->oclass != ARMOR_CLASS && + otmp->oclass != WEAPON_CLASS && + !is_weptool(otmp) && /* (redunant) */ + otmp->oclass != BALL_CLASS)) /* (useless) */ + return FALSE; + else /* lack of `rknown' only matters for vulnerable objects */ + return (boolean)(is_rustprone(otmp) || + is_corrodeable(otmp) || + is_flammable(otmp)); +} + +char * +corpse_xname(otmp, ignore_oquan) +struct obj *otmp; +boolean ignore_oquan; /* to force singular */ +{ + char *nambuf = nextobuf(); + + Sprintf(nambuf, "%s corpse", mons[otmp->corpsenm].mname); + + if (ignore_oquan || otmp->quan < 2) + return nambuf; + else + return makeplural(nambuf); +} + +/* xname, unless it's a corpse, then corpse_xname(obj, FALSE) */ +char * +cxname(obj) +struct obj *obj; +{ + if (obj->otyp == CORPSE) + return corpse_xname(obj, FALSE); + return xname(obj); +} + +/* treat an object as fully ID'd when it might be used as reason for death */ +char * +killer_xname(obj) +struct obj *obj; +{ + struct obj save_obj; + unsigned save_ocknown; + char *buf, *save_ocuname; + + /* remember original settings for core of the object; + oname and oattached extensions don't matter here--since they + aren't modified they don't need to be saved and restored */ + save_obj = *obj; + /* killer name should be more specific than general xname; however, exact + info like blessed/cursed and rustproof makes things be too verbose */ + obj->known = obj->dknown = 1; + obj->bknown = obj->rknown = obj->greased = 0; + /* if character is a priest[ess], bknown will get toggled back on */ + obj->blessed = obj->cursed = 0; + /* "killed by poisoned " would be misleading when poison is + not the cause of death and "poisoned by poisoned " would + be redundant when it is, so suppress "poisoned" prefix */ + obj->opoisoned = 0; + /* strip user-supplied name; artifacts keep theirs */ + if (!obj->oartifact) obj->onamelth = 0; + /* temporarily identify the type of object */ + save_ocknown = objects[obj->otyp].oc_name_known; + objects[obj->otyp].oc_name_known = 1; + save_ocuname = objects[obj->otyp].oc_uname; + objects[obj->otyp].oc_uname = 0; /* avoid "foo called bar" */ + + buf = xname(obj); + if (obj->quan == 1L) buf = obj_is_pname(obj) ? the(buf) : an(buf); + + objects[obj->otyp].oc_name_known = save_ocknown; + objects[obj->otyp].oc_uname = save_ocuname; + *obj = save_obj; /* restore object's core settings */ + + return buf; +} + +/* + * Used if only one of a collection of objects is named (e.g. in eat.c). + */ +const char * +singular(otmp, func) +register struct obj *otmp; +char *FDECL((*func), (OBJ_P)); +{ + long savequan; + char *nam; + + /* Note: using xname for corpses will not give the monster type */ + if (otmp->otyp == CORPSE && func == xname) + return corpse_xname(otmp, TRUE); + + savequan = otmp->quan; + otmp->quan = 1L; + nam = (*func)(otmp); + otmp->quan = savequan; + return nam; +} + +char * +an(str) +register const char *str; +{ + char *buf = nextobuf(); + + buf[0] = '\0'; + + if (strncmpi(str, "the ", 4) && + strcmp(str, "molten lava") && + strcmp(str, "iron bars") && + strcmp(str, "ice")) { + if (index(vowels, *str) && + strncmp(str, "one-", 4) && + strncmp(str, "useful", 6) && + strncmp(str, "unicorn", 7) && + strncmp(str, "uranium", 7) && + strncmp(str, "eucalyptus", 10)) + Strcpy(buf, "an "); + else + Strcpy(buf, "a "); + } + + Strcat(buf, str); + return buf; +} + +char * +An(str) +const char *str; +{ + register char *tmp = an(str); + *tmp = highc(*tmp); + return tmp; +} + +/* + * Prepend "the" if necessary; assumes str is a subject derived from xname. + * Use type_is_pname() for monster names, not the(). the() is idempotent. + */ +char * +the(str) +const char *str; +{ + char *buf = nextobuf(); + boolean insert_the = FALSE; + + if (!strncmpi(str, "the ", 4)) { + buf[0] = lowc(*str); + Strcpy(&buf[1], str+1); + return buf; + } else if (*str < 'A' || *str > 'Z') { + /* not a proper name, needs an article */ + insert_the = TRUE; + } else { + /* Probably a proper name, might not need an article */ + register char *tmp, *named, *called; + int l; + + /* some objects have capitalized adjectives in their names */ + if(((tmp = rindex(str, ' ')) || (tmp = rindex(str, '-'))) && + (tmp[1] < 'A' || tmp[1] > 'Z')) + insert_the = TRUE; + else if (tmp && index(str, ' ') < tmp) { /* has spaces */ + /* it needs an article if the name contains "of" */ + tmp = strstri(str, " of "); + named = strstri(str, " named "); + called = strstri(str, " called "); + if (called && (!named || called < named)) named = called; + + if (tmp && (!named || tmp < named)) /* found an "of" */ + insert_the = TRUE; + /* stupid special case: lacks "of" but needs "the" */ + else if (!named && (l = strlen(str)) >= 31 && + !strcmp(&str[l - 31], "Platinum Yendorian Express Card")) + insert_the = TRUE; + } + } + if (insert_the) + Strcpy(buf, "the "); + else + buf[0] = '\0'; + Strcat(buf, str); + + return buf; +} + +char * +The(str) +const char *str; +{ + register char *tmp = the(str); + *tmp = highc(*tmp); + return tmp; +} + +/* returns "count cxname(otmp)" or just cxname(otmp) if count == 1 */ +char * +aobjnam(otmp,verb) +register struct obj *otmp; +register const char *verb; +{ + register char *bp = cxname(otmp); + char prefix[PREFIX]; + + if(otmp->quan != 1L) { + Sprintf(prefix, "%ld ", otmp->quan); + bp = strprepend(bp, prefix); + } + + if(verb) { + Strcat(bp, " "); + Strcat(bp, otense(otmp, verb)); + } + return(bp); +} + +/* like aobjnam, but prepend "The", not count, and use xname */ +char * +Tobjnam(otmp, verb) +register struct obj *otmp; +register const char *verb; +{ + char *bp = The(xname(otmp)); + + if(verb) { + Strcat(bp, " "); + Strcat(bp, otense(otmp, verb)); + } + return(bp); +} + +/* return form of the verb (input plural) if xname(otmp) were the subject */ +char * +otense(otmp, verb) +register struct obj *otmp; +register const char *verb; +{ + char *buf; + + /* + * verb is given in plural (without trailing s). Return as input + * if the result of xname(otmp) would be plural. Don't bother + * recomputing xname(otmp) at this time. + */ + if (!is_plural(otmp)) + return vtense((char *)0, verb); + + buf = nextobuf(); + Strcpy(buf, verb); + return buf; +} + +/* various singular words that vtense would otherwise categorize as plural */ +static const char * const special_subjs[] = { + "erinys", + "manes", /* this one is ambiguous */ + "Cyclops", + "Hippocrates", + "Pelias", + "aklys", + "amnesia", + "paralysis", + 0 +}; + +/* return form of the verb (input plural) for present tense 3rd person subj */ +char * +vtense(subj, verb) +register const char *subj; +register const char *verb; +{ + char *buf = nextobuf(); + int len, ltmp; + const char *sp, *spot; + const char * const *spec; + + /* + * verb is given in plural (without trailing s). Return as input + * if subj appears to be plural. Add special cases as necessary. + * Many hard cases can already be handled by using otense() instead. + * If this gets much bigger, consider decomposing makeplural. + * Note: monster names are not expected here (except before corpse). + * + * special case: allow null sobj to get the singular 3rd person + * present tense form so we don't duplicate this code elsewhere. + */ + if (subj) { + if (!strncmpi(subj, "a ", 2) || !strncmpi(subj, "an ", 3)) + goto sing; + spot = (const char *)0; + for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) { + if (!strncmp(sp, " of ", 4) || + !strncmp(sp, " from ", 6) || + !strncmp(sp, " called ", 8) || + !strncmp(sp, " named ", 7) || + !strncmp(sp, " labeled ", 9)) { + if (sp != subj) spot = sp - 1; + break; + } + } + len = (int) strlen(subj); + if (!spot) spot = subj + len - 1; + + /* + * plural: anything that ends in 's', but not '*us' or '*ss'. + * Guess at a few other special cases that makeplural creates. + */ + if ((*spot == 's' && spot != subj && + (*(spot-1) != 'u' && *(spot-1) != 's')) || + ((spot - subj) >= 4 && !strncmp(spot-3, "eeth", 4)) || + ((spot - subj) >= 3 && !strncmp(spot-3, "feet", 4)) || + ((spot - subj) >= 2 && !strncmp(spot-1, "ia", 2)) || + ((spot - subj) >= 2 && !strncmp(spot-1, "ae", 2))) { + /* check for special cases to avoid false matches */ + len = (int)(spot - subj) + 1; + for (spec = special_subjs; *spec; spec++) { + ltmp = strlen(*spec); + if (len == ltmp && !strncmpi(*spec, subj, len)) goto sing; + /* also check for + to catch things like "the invisible erinys" */ + if (len > ltmp && *(spot - ltmp) == ' ' && + !strncmpi(*spec, spot - ltmp + 1, ltmp)) goto sing; + } + + return strcpy(buf, verb); + } + /* + * 3rd person plural doesn't end in telltale 's'; + * 2nd person singular behaves as if plural. + */ + if (!strcmpi(subj, "they") || !strcmpi(subj, "you")) + return strcpy(buf, verb); + } + + sing: + len = strlen(verb); + spot = verb + len - 1; + + if (!strcmp(verb, "are")) + Strcpy(buf, "is"); + else if (!strcmp(verb, "have")) + Strcpy(buf, "has"); + else if (index("zxs", *spot) || + (len >= 2 && *spot=='h' && index("cs", *(spot-1))) || + (len == 2 && *spot == 'o')) { + /* Ends in z, x, s, ch, sh; add an "es" */ + Strcpy(buf, verb); + Strcat(buf, "es"); + } else if (*spot == 'y' && (!index(vowels, *(spot-1)))) { + /* like "y" case in makeplural */ + Strcpy(buf, verb); + Strcpy(buf + len - 1, "ies"); + } else { + Strcpy(buf, verb); + Strcat(buf, "s"); + } + + return buf; +} + +/* capitalized variant of doname() */ +char * +Doname2(obj) +register struct obj *obj; +{ + register char *s = doname(obj); + + *s = highc(*s); + return(s); +} + +/* returns "your xname(obj)" or "Foobar's xname(obj)" or "the xname(obj)" */ +char * +yname(obj) +struct obj *obj; +{ + char *outbuf = nextobuf(); + char *s = shk_your(outbuf, obj); /* assert( s == outbuf ); */ + int space_left = BUFSZ - strlen(s) - sizeof " "; + + return strncat(strcat(s, " "), cxname(obj), space_left); +} + +/* capitalized variant of yname() */ +char * +Yname2(obj) +struct obj *obj; +{ + char *s = yname(obj); + + *s = highc(*s); + return s; +} + +/* returns "your simple_typename(obj->otyp)" + * or "Foobar's simple_typename(obj->otyp)" + * or "the simple_typename(obj-otyp)" + */ +char * +ysimple_name(obj) +struct obj *obj; +{ + char *outbuf = nextobuf(); + char *s = shk_your(outbuf, obj); /* assert( s == outbuf ); */ + int space_left = BUFSZ - strlen(s) - sizeof " "; + + return strncat(strcat(s, " "), simple_typename(obj->otyp), space_left); +} + +/* capitalized variant of ysimple_name() */ +char * +Ysimple_name2(obj) +struct obj *obj; +{ + char *s = ysimple_name(obj); + + *s = highc(*s); + return s; +} + +static const char *wrp[] = { + "wand", "ring", "potion", "scroll", "gem", "amulet", + "spellbook", "spell book", + /* for non-specific wishes */ + "weapon", "armor", "armour", "tool", "food", "comestible", +}; +static const char wrpsym[] = { + WAND_CLASS, RING_CLASS, POTION_CLASS, SCROLL_CLASS, GEM_CLASS, + AMULET_CLASS, SPBOOK_CLASS, SPBOOK_CLASS, + WEAPON_CLASS, ARMOR_CLASS, ARMOR_CLASS, TOOL_CLASS, FOOD_CLASS, + FOOD_CLASS +}; + +#endif /* OVLB */ +#ifdef OVL0 + +/* Plural routine; chiefly used for user-defined fruits. We have to try to + * account for everything reasonable the player has; something unreasonable + * can still break the code. However, it's still a lot more accurate than + * "just add an s at the end", which Rogue uses... + * + * Also used for plural monster names ("Wiped out all homunculi.") + * and body parts. + * + * Also misused by muse.c to convert 1st person present verbs to 2nd person. + */ +char * +makeplural(oldstr) +const char *oldstr; +{ + /* Note: cannot use strcmpi here -- it'd give MATZot, CAVEMeN,... */ + register char *spot; + char *str = nextobuf(); + const char *excess = (char *)0; + int len; + + while (*oldstr==' ') oldstr++; + if (!oldstr || !*oldstr) { + impossible("plural of null?"); + Strcpy(str, "s"); + return str; + } + Strcpy(str, oldstr); + + /* + * Skip changing "pair of" to "pairs of". According to Webster, usual + * English usage is use pairs for humans, e.g. 3 pairs of dancers, + * and pair for objects and non-humans, e.g. 3 pair of boots. We don't + * refer to pairs of humans in this game so just skip to the bottom. + */ + if (!strncmp(str, "pair of ", 8)) + goto bottom; + + /* Search for common compounds, ex. lump of royal jelly */ + for(spot=str; *spot; spot++) { + if (!strncmp(spot, " of ", 4) + || !strncmp(spot, " labeled ", 9) + || !strncmp(spot, " called ", 8) + || !strncmp(spot, " named ", 7) + || !strcmp(spot, " above") /* lurkers above */ + || !strncmp(spot, " versus ", 8) + || !strncmp(spot, " from ", 6) + || !strncmp(spot, " in ", 4) + || !strncmp(spot, " on ", 4) + || !strncmp(spot, " a la ", 6) + || !strncmp(spot, " with", 5) /* " with "? */ + || !strncmp(spot, " de ", 4) + || !strncmp(spot, " d'", 3) + || !strncmp(spot, " du ", 4)) { + excess = oldstr + (int) (spot - str); + *spot = 0; + break; + } + } + spot--; + while (*spot==' ') spot--; /* Strip blanks from end */ + *(spot+1) = 0; + /* Now spot is the last character of the string */ + + len = strlen(str); + + /* Single letters */ + if (len==1 || !letter(*spot)) { + Strcpy(spot+1, "'s"); + goto bottom; + } + + /* Same singular and plural; mostly Japanese words except for "manes" */ + if ((len == 2 && !strcmp(str, "ya")) || + (len >= 2 && !strcmp(spot-1, "ai")) || /* samurai, Uruk-hai */ + (len >= 3 && !strcmp(spot-2, " ya")) || + (len >= 4 && + (!strcmp(spot-3, "fish") || !strcmp(spot-3, "tuna") || + !strcmp(spot-3, "deer") || !strcmp(spot-3, "yaki"))) || + (len >= 5 && (!strcmp(spot-4, "sheep") || + !strcmp(spot-4, "ninja") || + !strcmp(spot-4, "ronin") || + !strcmp(spot-4, "shito") || + !strcmp(spot-7, "shuriken") || + !strcmp(spot-4, "tengu") || + !strcmp(spot-4, "manes"))) || + (len >= 6 && !strcmp(spot-5, "ki-rin")) || + (len >= 7 && !strcmp(spot-6, "gunyoki"))) + goto bottom; + + /* man/men ("Wiped out all cavemen.") */ + if (len >= 3 && !strcmp(spot-2, "man") && + (len<6 || strcmp(spot-5, "shaman")) && + (len<5 || strcmp(spot-4, "human"))) { + *(spot-1) = 'e'; + goto bottom; + } + + /* tooth/teeth */ + if (len >= 5 && !strcmp(spot-4, "tooth")) { + Strcpy(spot-3, "eeth"); + goto bottom; + } + + /* knife/knives, etc... */ + if (!strcmp(spot-1, "fe")) { + Strcpy(spot-1, "ves"); + goto bottom; + } else if (*spot == 'f') { + if (index("lr", *(spot-1)) || index(vowels, *(spot-1))) { + Strcpy(spot, "ves"); + goto bottom; + } else if (len >= 5 && !strncmp(spot-4, "staf", 4)) { + Strcpy(spot-1, "ves"); + goto bottom; + } + } + + /* foot/feet (body part) */ + if (len >= 4 && !strcmp(spot-3, "foot")) { + Strcpy(spot-2, "eet"); + goto bottom; + } + + /* ium/ia (mycelia, baluchitheria) */ + if (len >= 3 && !strcmp(spot-2, "ium")) { + *(spot--) = (char)0; + *spot = 'a'; + goto bottom; + } + + /* algae, larvae, hyphae (another fungus part) */ + if ((len >= 4 && !strcmp(spot-3, "alga")) || + (len >= 5 && + (!strcmp(spot-4, "hypha") || !strcmp(spot-4, "larva")))) { + Strcpy(spot, "ae"); + goto bottom; + } + + /* fungus/fungi, homunculus/homunculi, but buses, lotuses, wumpuses */ + if (len > 3 && !strcmp(spot-1, "us") && + (len < 5 || (strcmp(spot-4, "lotus") && + (len < 6 || strcmp(spot-5, "wumpus"))))) { + *(spot--) = (char)0; + *spot = 'i'; + goto bottom; + } + + /* vortex/vortices */ + if (len >= 6 && !strcmp(spot-3, "rtex")) { + Strcpy(spot-1, "ices"); + goto bottom; + } + + /* djinni/djinn (note: also efreeti/efreet) */ + if (len >= 6 && !strcmp(spot-5, "djinni")) { + *spot = (char)0; + goto bottom; + } + + /* mumak/mumakil */ + if (len >= 5 && !strcmp(spot-4, "mumak")) { + Strcpy(spot+1, "il"); + goto bottom; + } + + /* sis/ses (nemesis) */ + if (len >= 3 && !strcmp(spot-2, "sis")) { + *(spot-1) = 'e'; + goto bottom; + } + + /* erinys/erinyes */ + if (len >= 6 && !strcmp(spot-5, "erinys")) { + Strcpy(spot, "es"); + goto bottom; + } + + /* mouse/mice,louse/lice (not a monster, but possible in food names) */ + if (len >= 5 && !strcmp(spot-3, "ouse") && index("MmLl", *(spot-4))) { + Strcpy(spot-3, "ice"); + goto bottom; + } + + /* matzoh/matzot, possible food name */ + if (len >= 6 && (!strcmp(spot-5, "matzoh") + || !strcmp(spot-5, "matzah"))) { + Strcpy(spot-1, "ot"); + goto bottom; + } + if (len >= 5 && (!strcmp(spot-4, "matzo") + || !strcmp(spot-5, "matza"))) { + Strcpy(spot, "ot"); + goto bottom; + } + + /* child/children (for wise guys who give their food funny names) */ + if (len >= 5 && !strcmp(spot-4, "child")) { + Strcpy(spot, "dren"); + goto bottom; + } + + /* note: -eau/-eaux (gateau, bordeau...) */ + /* note: ox/oxen, VAX/VAXen, goose/geese */ + + /* Ends in z, x, s, ch, sh; add an "es" */ + if (index("zxs", *spot) + || (len >= 2 && *spot=='h' && index("cs", *(spot-1))) + /* Kludge to get "tomatoes" and "potatoes" right */ + || (len >= 4 && !strcmp(spot-2, "ato"))) { + Strcpy(spot+1, "es"); + goto bottom; + } + + /* Ends in y preceded by consonant (note: also "qu") change to "ies" */ + if (*spot == 'y' && + (!index(vowels, *(spot-1)))) { + Strcpy(spot, "ies"); + goto bottom; + } + + /* Default: append an 's' */ + Strcpy(spot+1, "s"); + +bottom: if (excess) Strcpy(eos(str), excess); + return str; +} + +#endif /* OVL0 */ + +struct o_range { + const char *name, oclass; + int f_o_range, l_o_range; +}; + +#ifndef OVLB + +STATIC_DCL const struct o_range o_ranges[]; + +#else /* OVLB */ + +/* wishable subranges of objects */ +STATIC_OVL NEARDATA const struct o_range o_ranges[] = { + { "bag", TOOL_CLASS, SACK, BAG_OF_TRICKS }, + { "lamp", TOOL_CLASS, OIL_LAMP, MAGIC_LAMP }, + { "candle", TOOL_CLASS, TALLOW_CANDLE, WAX_CANDLE }, + { "horn", TOOL_CLASS, TOOLED_HORN, HORN_OF_PLENTY }, + { "shield", ARMOR_CLASS, SMALL_SHIELD, SHIELD_OF_REFLECTION }, + { "helm", ARMOR_CLASS, ELVEN_LEATHER_HELM, HELM_OF_TELEPATHY }, + { "gloves", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY }, + { "gauntlets", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY }, + { "boots", ARMOR_CLASS, LOW_BOOTS, LEVITATION_BOOTS }, + { "shoes", ARMOR_CLASS, LOW_BOOTS, IRON_SHOES }, + { "cloak", ARMOR_CLASS, MUMMY_WRAPPING, CLOAK_OF_DISPLACEMENT }, +#ifdef TOURIST + { "shirt", ARMOR_CLASS, HAWAIIAN_SHIRT, T_SHIRT }, +#endif + { "dragon scales", + ARMOR_CLASS, GRAY_DRAGON_SCALES, YELLOW_DRAGON_SCALES }, + { "dragon scale mail", + ARMOR_CLASS, GRAY_DRAGON_SCALE_MAIL, YELLOW_DRAGON_SCALE_MAIL }, + { "sword", WEAPON_CLASS, SHORT_SWORD, KATANA }, +#ifdef WIZARD + { "venom", VENOM_CLASS, BLINDING_VENOM, ACID_VENOM }, +#endif + { "gray stone", GEM_CLASS, LUCKSTONE, FLINT }, + { "grey stone", GEM_CLASS, LUCKSTONE, FLINT }, +}; + +#define BSTRCMP(base,ptr,string) ((ptr) < base || strcmp((ptr),string)) +#define BSTRCMPI(base,ptr,string) ((ptr) < base || strcmpi((ptr),string)) +#define BSTRNCMP(base,ptr,string,num) ((ptr)= bp+1 && p[-1] == 's') { + if (p >= bp+2 && p[-2] == 'e') { + if (p >= bp+3 && p[-3] == 'i') { + if(!BSTRCMP(bp, p-7, "cookies") || + !BSTRCMP(bp, p-4, "pies")) + goto mins; + Strcpy(p-3, "y"); + return bp; + } + + /* note: cloves / knives from clove / knife */ + if(!BSTRCMP(bp, p-6, "knives")) { + Strcpy(p-3, "fe"); + return bp; + } + if(!BSTRCMP(bp, p-6, "staves")) { + Strcpy(p-3, "ff"); + return bp; + } + if (!BSTRCMPI(bp, p-6, "leaves")) { + Strcpy(p-3, "f"); + return bp; + } + if (!BSTRCMP(bp, p-8, "vortices")) { + Strcpy(p-4, "ex"); + return bp; + } + + /* note: nurses, axes but boxes */ + if (!BSTRCMP(bp, p-5, "boxes") || + !BSTRCMP(bp, p-4, "ches")) { + p[-2] = '\0'; + return bp; + } + + if (!BSTRCMP(bp, p-6, "gloves") || + !BSTRCMP(bp, p-6, "lenses") || + !BSTRCMP(bp, p-5, "shoes") || + !BSTRCMP(bp, p-6, "scales")) + return bp; + + } else if (!BSTRCMP(bp, p-5, "boots") || + !BSTRCMP(bp, p-9, "gauntlets") || + !BSTRCMP(bp, p-6, "tricks") || + !BSTRCMP(bp, p-9, "paralysis") || + !BSTRCMP(bp, p-5, "glass") || + !BSTRCMP(bp, p-4, "ness") || + !BSTRCMP(bp, p-14, "shape changers") || + !BSTRCMP(bp, p-15, "detect monsters") || + !BSTRCMPI(bp, p-11, "Aesculapius") || /* staff */ + !BSTRCMP(bp, p-10, "eucalyptus") || +#ifdef WIZARD + !BSTRCMP(bp, p-9, "iron bars") || +#endif + !BSTRCMP(bp, p-5, "aklys") || + !BSTRCMP(bp, p-6, "fungus")) + return bp; + mins: + p[-1] = '\0'; + + } else { + + if(!BSTRCMP(bp, p-5, "teeth")) { + Strcpy(p-5, "tooth"); + return bp; + } + + if (!BSTRCMP(bp, p-5, "fungi")) { + Strcpy(p-5, "fungus"); + return bp; + } + + /* here we cannot find the plural suffix */ + } + return bp; +} + +/* compare user string against object name string using fuzzy matching */ +static boolean +wishymatch(u_str, o_str, retry_inverted) +const char *u_str; /* from user, so might be variant spelling */ +const char *o_str; /* from objects[], so is in canonical form */ +boolean retry_inverted; /* optional extra "of" handling */ +{ + /* special case: wizards can wish for traps. The object is "beartrap" + * and the trap is "bear trap", so to let wizards wish for both we + * must not fuzzymatch. + */ +#ifdef WIZARD + if (wizard && !strcmp(o_str, "beartrap")) + return !strncmpi(o_str, u_str, 8); +#endif + + /* ignore spaces & hyphens and upper/lower case when comparing */ + if (fuzzymatch(u_str, o_str, " -", TRUE)) return TRUE; + + if (retry_inverted) { + const char *u_of, *o_of; + char *p, buf[BUFSZ]; + + /* when just one of the strings is in the form "foo of bar", + convert it into "bar foo" and perform another comparison */ + u_of = strstri(u_str, " of "); + o_of = strstri(o_str, " of "); + if (u_of && !o_of) { + Strcpy(buf, u_of + 4); + p = eos(strcat(buf, " ")); + while (u_str < u_of) *p++ = *u_str++; + *p = '\0'; + return fuzzymatch(buf, o_str, " -", TRUE); + } else if (o_of && !u_of) { + Strcpy(buf, o_of + 4); + p = eos(strcat(buf, " ")); + while (o_str < o_of) *p++ = *o_str++; + *p = '\0'; + return fuzzymatch(u_str, buf, " -", TRUE); + } + } + + /* [note: if something like "elven speed boots" ever gets added, these + special cases should be changed to call wishymatch() recursively in + order to get the "of" inversion handling] */ + if (!strncmp(o_str, "dwarvish ", 9)) { + if (!strncmpi(u_str, "dwarven ", 8)) + return fuzzymatch(u_str + 8, o_str + 9, " -", TRUE); + } else if (!strncmp(o_str, "elven ", 6)) { + if (!strncmpi(u_str, "elvish ", 7)) + return fuzzymatch(u_str + 7, o_str + 6, " -", TRUE); + else if (!strncmpi(u_str, "elfin ", 6)) + return fuzzymatch(u_str + 6, o_str + 6, " -", TRUE); + } else if (!strcmp(o_str, "aluminum")) { + /* this special case doesn't really fit anywhere else... */ + /* (note that " wand" will have been stripped off by now) */ + if (!strcmpi(u_str, "aluminium")) + return fuzzymatch(u_str + 9, o_str + 8, " -", TRUE); + } + + return FALSE; +} + +/* alternate spellings; if the difference is only the presence or + absence of spaces and/or hyphens (such as "pickaxe" vs "pick axe" + vs "pick-axe") then there is no need for inclusion in this list; + likewise for ``"of" inversions'' ("boots of speed" vs "speed boots") */ +struct alt_spellings { + const char *sp; + int ob; +} spellings[] = { + { "pickax", PICK_AXE }, + { "whip", BULLWHIP }, + { "saber", SILVER_SABER }, + { "silver sabre", SILVER_SABER }, + { "smooth shield", SHIELD_OF_REFLECTION }, + { "grey dragon scale mail", GRAY_DRAGON_SCALE_MAIL }, + { "grey dragon scales", GRAY_DRAGON_SCALES }, + { "enchant armour", SCR_ENCHANT_ARMOR }, + { "destroy armour", SCR_DESTROY_ARMOR }, + { "scroll of enchant armour", SCR_ENCHANT_ARMOR }, + { "scroll of destroy armour", SCR_DESTROY_ARMOR }, + { "leather armour", LEATHER_ARMOR }, + { "studded leather armour", STUDDED_LEATHER_ARMOR }, + { "iron ball", HEAVY_IRON_BALL }, + { "lantern", BRASS_LANTERN }, + { "mattock", DWARVISH_MATTOCK }, + { "amulet of poison resistance", AMULET_VERSUS_POISON }, + { "stone", ROCK }, +#ifdef TOURIST + { "camera", EXPENSIVE_CAMERA }, + { "tee shirt", T_SHIRT }, +#endif + { "can", TIN }, + { "can opener", TIN_OPENER }, + { "kelp", KELP_FROND }, + { "eucalyptus", EUCALYPTUS_LEAF }, + { "grapple", GRAPPLING_HOOK }, + { (const char *)0, 0 }, +}; + +/* + * Return something wished for. Specifying a null pointer for + * the user request string results in a random object. Otherwise, + * if asking explicitly for "nothing" (or "nil") return no_wish; + * if not an object return &zeroobj; if an error (no matching object), + * return null. + * If from_user is false, we're reading from the wizkit, nothing was typed in. + */ +struct obj * +readobjnam(bp, no_wish, from_user) +register char *bp; +struct obj *no_wish; +boolean from_user; +{ + register char *p; + register int i; + register struct obj *otmp; + int cnt, spe, spesgn, typ, very, rechrg; + int blessed, uncursed, iscursed, ispoisoned, isgreased; + int eroded, eroded2, erodeproof; +#ifdef INVISIBLE_OBJECTS + int isinvisible; +#endif + int halfeaten, mntmp, contents; + int islit, unlabeled, ishistoric, isdiluted; + struct fruit *f; + int ftype = current_fruit; + char fruitbuf[BUFSZ]; + /* Fruits may not mess up the ability to wish for real objects (since + * you can leave a fruit in a bones file and it will be added to + * another person's game), so they must be checked for last, after + * stripping all the possible prefixes and seeing if there's a real + * name in there. So we have to save the full original name. However, + * it's still possible to do things like "uncursed burnt Alaska", + * or worse yet, "2 burned 5 course meals", so we need to loop to + * strip off the prefixes again, this time stripping only the ones + * possible on food. + * We could get even more detailed so as to allow food names with + * prefixes that _are_ possible on food, so you could wish for + * "2 3 alarm chilis". Currently this isn't allowed; options.c + * automatically sticks 'candied' in front of such names. + */ + + char oclass; + char *un, *dn, *actualn; + const char *name=0; + + cnt = spe = spesgn = typ = very = rechrg = + blessed = uncursed = iscursed = +#ifdef INVISIBLE_OBJECTS + isinvisible = +#endif + ispoisoned = isgreased = eroded = eroded2 = erodeproof = + halfeaten = islit = unlabeled = ishistoric = isdiluted = 0; + mntmp = NON_PM; +#define UNDEFINED 0 +#define EMPTY 1 +#define SPINACH 2 + contents = UNDEFINED; + oclass = 0; + actualn = dn = un = 0; + + if (!bp) goto any; + /* first, remove extra whitespace they may have typed */ + (void)mungspaces(bp); + /* allow wishing for "nothing" to preserve wishless conduct... + [now requires "wand of nothing" if that's what was really wanted] */ + if (!strcmpi(bp, "nothing") || !strcmpi(bp, "nil") || + !strcmpi(bp, "none")) return no_wish; + /* save the [nearly] unmodified choice string */ + Strcpy(fruitbuf, bp); + + for(;;) { + register int l; + + if (!bp || !*bp) goto any; + if (!strncmpi(bp, "an ", l=3) || + !strncmpi(bp, "a ", l=2)) { + cnt = 1; + } else if (!strncmpi(bp, "the ", l=4)) { + ; /* just increment `bp' by `l' below */ + } else if (!cnt && digit(*bp) && strcmp(bp, "0")) { + cnt = atoi(bp); + while(digit(*bp)) bp++; + while(*bp == ' ') bp++; + l = 0; + } else if (*bp == '+' || *bp == '-') { + spesgn = (*bp++ == '+') ? 1 : -1; + spe = atoi(bp); + while(digit(*bp)) bp++; + while(*bp == ' ') bp++; + l = 0; + } else if (!strncmpi(bp, "blessed ", l=8) || + !strncmpi(bp, "holy ", l=5)) { + blessed = 1; + } else if (!strncmpi(bp, "cursed ", l=7) || + !strncmpi(bp, "unholy ", l=7)) { + iscursed = 1; + } else if (!strncmpi(bp, "uncursed ", l=9)) { + uncursed = 1; +#ifdef INVISIBLE_OBJECTS + } else if (!strncmpi(bp, "invisible ", l=10)) { + isinvisible = 1; +#endif + } else if (!strncmpi(bp, "rustproof ", l=10) || + !strncmpi(bp, "erodeproof ", l=11) || + !strncmpi(bp, "corrodeproof ", l=13) || + !strncmpi(bp, "fixed ", l=6) || + !strncmpi(bp, "fireproof ", l=10) || + !strncmpi(bp, "rotproof ", l=9)) { + erodeproof = 1; + } else if (!strncmpi(bp,"lit ", l=4) || + !strncmpi(bp,"burning ", l=8)) { + islit = 1; + } else if (!strncmpi(bp,"unlit ", l=6) || + !strncmpi(bp,"extinguished ", l=13)) { + islit = 0; + /* "unlabeled" and "blank" are synonymous */ + } else if (!strncmpi(bp,"unlabeled ", l=10) || + !strncmpi(bp,"unlabelled ", l=11) || + !strncmpi(bp,"blank ", l=6)) { + unlabeled = 1; + } else if(!strncmpi(bp, "poisoned ",l=9) +#ifdef WIZARD + || (wizard && !strncmpi(bp, "trapped ",l=8)) +#endif + ) { + ispoisoned=1; + } else if(!strncmpi(bp, "greased ",l=8)) { + isgreased=1; + } else if (!strncmpi(bp, "very ", l=5)) { + /* very rusted very heavy iron ball */ + very = 1; + } else if (!strncmpi(bp, "thoroughly ", l=11)) { + very = 2; + } else if (!strncmpi(bp, "rusty ", l=6) || + !strncmpi(bp, "rusted ", l=7) || + !strncmpi(bp, "burnt ", l=6) || + !strncmpi(bp, "burned ", l=7)) { + eroded = 1 + very; + very = 0; + } else if (!strncmpi(bp, "corroded ", l=9) || + !strncmpi(bp, "rotted ", l=7)) { + eroded2 = 1 + very; + very = 0; + } else if (!strncmpi(bp, "partly eaten ", l=13)) { + halfeaten = 1; + } else if (!strncmpi(bp, "historic ", l=9)) { + ishistoric = 1; + } else if (!strncmpi(bp, "diluted ", l=8)) { + isdiluted = 1; + } else if(!strncmpi(bp, "empty ", l=6)) { + contents = EMPTY; + } else break; + bp += l; + } + if(!cnt) cnt = 1; /* %% what with "gems" etc. ? */ + if (strlen(bp) > 1) { + if ((p = rindex(bp, '(')) != 0) { + if (p > bp && p[-1] == ' ') p[-1] = 0; + else *p = 0; + p++; + if (!strcmpi(p, "lit)")) { + islit = 1; + } else { + spe = atoi(p); + while (digit(*p)) p++; + if (*p == ':') { + p++; + rechrg = spe; + spe = atoi(p); + while (digit(*p)) p++; + } + if (*p != ')') { + spe = rechrg = 0; + } else { + spesgn = 1; + p++; + if (*p) Strcat(bp, p); + } + } + } + } +/* + otmp->spe is type schar; so we don't want spe to be any bigger or smaller. + also, spe should always be positive -- some cheaters may try to confuse + atoi() +*/ + if (spe < 0) { + spesgn = -1; /* cheaters get what they deserve */ + spe = abs(spe); + } + if (spe > SCHAR_LIM) + spe = SCHAR_LIM; + if (rechrg < 0 || rechrg > 7) rechrg = 7; /* recharge_limit */ + + /* now we have the actual name, as delivered by xname, say + green potions called whisky + scrolls labeled "QWERTY" + egg + fortune cookies + very heavy iron ball named hoei + wand of wishing + elven cloak + */ + if ((p = strstri(bp, " named ")) != 0) { + *p = 0; + name = p+7; + } + if ((p = strstri(bp, " called ")) != 0) { + *p = 0; + un = p+8; + /* "helmet called telepathy" is not "helmet" (a specific type) + * "shield called reflection" is not "shield" (a general type) + */ + for(i = 0; i < SIZE(o_ranges); i++) + if(!strcmpi(bp, o_ranges[i].name)) { + oclass = o_ranges[i].oclass; + goto srch; + } + } + if ((p = strstri(bp, " labeled ")) != 0) { + *p = 0; + dn = p+9; + } else if ((p = strstri(bp, " labelled ")) != 0) { + *p = 0; + dn = p+10; + } + if ((p = strstri(bp, " of spinach")) != 0) { + *p = 0; + contents = SPINACH; + } + + /* + Skip over "pair of ", "pairs of", "set of" and "sets of". + + Accept "3 pair of boots" as well as "3 pairs of boots". It is valid + English either way. See makeplural() for more on pair/pairs. + + We should only double count if the object in question is not + refered to as a "pair of". E.g. We should double if the player + types "pair of spears", but not if the player types "pair of + lenses". Luckily (?) all objects that are refered to as pairs + -- boots, gloves, and lenses -- are also not mergable, so cnt is + ignored anyway. + */ + if(!strncmpi(bp, "pair of ",8)) { + bp += 8; + cnt *= 2; + } else if(cnt > 1 && !strncmpi(bp, "pairs of ",9)) { + bp += 9; + cnt *= 2; + } else if (!strncmpi(bp, "set of ",7)) { + bp += 7; + } else if (!strncmpi(bp, "sets of ",8)) { + bp += 8; + } + + /* + * Find corpse type using "of" (figurine of an orc, tin of orc meat) + * Don't check if it's a wand or spellbook. + * (avoid "wand/finger of death" confusion). + */ + if (!strstri(bp, "wand ") + && !strstri(bp, "spellbook ") + && !strstri(bp, "finger ")) { + if ((p = strstri(bp, " of ")) != 0 + && (mntmp = name_to_mon(p+4)) >= LOW_PM) + *p = 0; + } + /* Find corpse type w/o "of" (red dragon scale mail, yeti corpse) */ + if (strncmpi(bp, "samurai sword", 13)) /* not the "samurai" monster! */ + if (strncmpi(bp, "wizard lock", 11)) /* not the "wizard" monster! */ + if (strncmpi(bp, "ninja-to", 8)) /* not the "ninja" rank */ + if (strncmpi(bp, "master key", 10)) /* not the "master" rank */ + if (strncmpi(bp, "magenta", 7)) /* not the "mage" rank */ + if (mntmp < LOW_PM && strlen(bp) > 2 && + (mntmp = name_to_mon(bp)) >= LOW_PM) { + int mntmptoo, mntmplen; /* double check for rank title */ + char *obp = bp; + mntmptoo = title_to_mon(bp, (int *)0, &mntmplen); + bp += mntmp != mntmptoo ? (int)strlen(mons[mntmp].mname) : mntmplen; + if (*bp == ' ') bp++; + else if (!strncmpi(bp, "s ", 2)) bp += 2; + else if (!strncmpi(bp, "es ", 3)) bp += 3; + else if (!*bp && !actualn && !dn && !un && !oclass) { + /* no referent; they don't really mean a monster type */ + bp = obp; + mntmp = NON_PM; + } + } + + /* first change to singular if necessary */ + if (*bp) { + char *sng = makesingular(bp); + if (strcmp(bp, sng)) { + if (cnt == 1) cnt = 2; + Strcpy(bp, sng); + } + } + + /* Alternate spellings (pick-ax, silver sabre, &c) */ + { + struct alt_spellings *as = spellings; + + while (as->sp) { + if (fuzzymatch(bp, as->sp, " -", TRUE)) { + typ = as->ob; + goto typfnd; + } + as++; + } + /* can't use spellings list for this one due to shuffling */ + if (!strncmpi(bp, "grey spell", 10)) + *(bp + 2) = 'a'; + } + + /* dragon scales - assumes order of dragons */ + if(!strcmpi(bp, "scales") && + mntmp >= PM_GRAY_DRAGON && mntmp <= PM_YELLOW_DRAGON) { + typ = GRAY_DRAGON_SCALES + mntmp - PM_GRAY_DRAGON; + mntmp = NON_PM; /* no monster */ + goto typfnd; + } + + p = eos(bp); + if(!BSTRCMPI(bp, p-10, "holy water")) { + typ = POT_WATER; + if ((p-bp) >= 12 && *(p-12) == 'u') + iscursed = 1; /* unholy water */ + else blessed = 1; + goto typfnd; + } + if(unlabeled && !BSTRCMPI(bp, p-6, "scroll")) { + typ = SCR_BLANK_PAPER; + goto typfnd; + } + if(unlabeled && !BSTRCMPI(bp, p-9, "spellbook")) { + typ = SPE_BLANK_PAPER; + goto typfnd; + } + /* + * NOTE: Gold pieces are handled as objects nowadays, and therefore + * this section should probably be reconsidered as well as the entire + * gold/money concept. Maybe we want to add other monetary units as + * well in the future. (TH) + */ + if(!BSTRCMPI(bp, p-10, "gold piece") || !BSTRCMPI(bp, p-7, "zorkmid") || + !strcmpi(bp, "gold") || !strcmpi(bp, "money") || + !strcmpi(bp, "coin") || *bp == GOLD_SYM) { + if (cnt > 5000 +#ifdef WIZARD + && !wizard +#endif + ) cnt=5000; + if (cnt < 1) cnt=1; +#ifndef GOLDOBJ + if (from_user) + pline("%d gold piece%s.", cnt, plur(cnt)); + u.ugold += cnt; + flags.botl=1; + return (&zeroobj); +#else + otmp = mksobj(GOLD_PIECE, FALSE, FALSE); + otmp->quan = cnt; + otmp->owt = weight(otmp); + flags.botl=1; + return (otmp); +#endif + } + if (strlen(bp) == 1 && + (i = def_char_to_objclass(*bp)) < MAXOCLASSES && i > ILLOBJ_CLASS +#ifdef WIZARD + && (wizard || i != VENOM_CLASS) +#else + && i != VENOM_CLASS +#endif + ) { + oclass = i; + goto any; + } + + /* Search for class names: XXXXX potion, scroll of XXXXX. Avoid */ + /* false hits on, e.g., rings for "ring mail". */ + if(strncmpi(bp, "enchant ", 8) && + strncmpi(bp, "destroy ", 8) && + strncmpi(bp, "food detection", 14) && + strncmpi(bp, "ring mail", 9) && + strncmpi(bp, "studded leather arm", 19) && + strncmpi(bp, "leather arm", 11) && + strncmpi(bp, "tooled horn", 11) && + strncmpi(bp, "food ration", 11) && + strncmpi(bp, "meat ring", 9) + ) + for (i = 0; i < (int)(sizeof wrpsym); i++) { + register int j = strlen(wrp[i]); + if(!strncmpi(bp, wrp[i], j)){ + oclass = wrpsym[i]; + if(oclass != AMULET_CLASS) { + bp += j; + if(!strncmpi(bp, " of ", 4)) actualn = bp+4; + /* else if(*bp) ?? */ + } else + actualn = bp; + goto srch; + } + if(!BSTRCMPI(bp, p-j, wrp[i])){ + oclass = wrpsym[i]; + p -= j; + *p = 0; + if(p > bp && p[-1] == ' ') p[-1] = 0; + actualn = dn = bp; + goto srch; + } + } + + /* "grey stone" check must be before general "stone" */ + for (i = 0; i < SIZE(o_ranges); i++) + if(!strcmpi(bp, o_ranges[i].name)) { + typ = rnd_class(o_ranges[i].f_o_range, o_ranges[i].l_o_range); + goto typfnd; + } + + if (!BSTRCMPI(bp, p-6, " stone")) { + p[-6] = 0; + oclass = GEM_CLASS; + dn = actualn = bp; + goto srch; + } else if (!strcmpi(bp, "looking glass")) { + ; /* avoid false hit on "* glass" */ + } else if (!BSTRCMPI(bp, p-6, " glass") || !strcmpi(bp, "glass")) { + register char *g = bp; + if (strstri(g, "broken")) return (struct obj *)0; + if (!strncmpi(g, "worthless ", 10)) g += 10; + if (!strncmpi(g, "piece of ", 9)) g += 9; + if (!strncmpi(g, "colored ", 8)) g += 8; + else if (!strncmpi(g, "coloured ", 9)) g += 9; + if (!strcmpi(g, "glass")) { /* choose random color */ + /* 9 different kinds */ + typ = LAST_GEM + rnd(9); + if (objects[typ].oc_class == GEM_CLASS) goto typfnd; + else typ = 0; /* somebody changed objects[]? punt */ + } else { /* try to construct canonical form */ + char tbuf[BUFSZ]; + Strcpy(tbuf, "worthless piece of "); + Strcat(tbuf, g); /* assume it starts with the color */ + Strcpy(bp, tbuf); + } + } + + actualn = bp; + if (!dn) dn = actualn; /* ex. "skull cap" */ +srch: + /* check real names of gems first */ + if(!oclass && actualn) { + for(i = bases[GEM_CLASS]; i <= LAST_GEM; i++) { + register const char *zn; + + if((zn = OBJ_NAME(objects[i])) && !strcmpi(actualn, zn)) { + typ = i; + goto typfnd; + } + } + } + i = oclass ? bases[(int)oclass] : 1; + while(i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass)){ + register const char *zn; + + if (actualn && (zn = OBJ_NAME(objects[i])) != 0 && + wishymatch(actualn, zn, TRUE)) { + typ = i; + goto typfnd; + } + if (dn && (zn = OBJ_DESCR(objects[i])) != 0 && + wishymatch(dn, zn, FALSE)) { + /* don't match extra descriptions (w/o real name) */ + if (!OBJ_NAME(objects[i])) return (struct obj *)0; + typ = i; + goto typfnd; + } + if (un && (zn = objects[i].oc_uname) != 0 && + wishymatch(un, zn, FALSE)) { + typ = i; + goto typfnd; + } + i++; + } + if (actualn) { + struct Jitem *j = Japanese_items; + while(j->item) { + if (actualn && !strcmpi(actualn, j->name)) { + typ = j->item; + goto typfnd; + } + j++; + } + } + if (!strcmpi(bp, "spinach")) { + contents = SPINACH; + typ = TIN; + goto typfnd; + } + /* Note: not strncmpi. 2 fruits, one capital, one not, are possible. */ + { + char *fp; + int l, cntf; + int blessedf, iscursedf, uncursedf, halfeatenf; + + blessedf = iscursedf = uncursedf = halfeatenf = 0; + cntf = 0; + + fp = fruitbuf; + for(;;) { + if (!fp || !*fp) break; + if (!strncmpi(fp, "an ", l=3) || + !strncmpi(fp, "a ", l=2)) { + cntf = 1; + } else if (!cntf && digit(*fp)) { + cntf = atoi(fp); + while(digit(*fp)) fp++; + while(*fp == ' ') fp++; + l = 0; + } else if (!strncmpi(fp, "blessed ", l=8)) { + blessedf = 1; + } else if (!strncmpi(fp, "cursed ", l=7)) { + iscursedf = 1; + } else if (!strncmpi(fp, "uncursed ", l=9)) { + uncursedf = 1; + } else if (!strncmpi(fp, "partly eaten ", l=13)) { + halfeatenf = 1; + } else break; + fp += l; + } + + for(f=ffruit; f; f = f->nextf) { + char *f1 = f->fname, *f2 = makeplural(f->fname); + + if(!strncmp(fp, f1, strlen(f1)) || + !strncmp(fp, f2, strlen(f2))) { + typ = SLIME_MOLD; + blessed = blessedf; + iscursed = iscursedf; + uncursed = uncursedf; + halfeaten = halfeatenf; + cnt = cntf; + ftype = f->fid; + goto typfnd; + } + } + } + + if(!oclass && actualn) { + short objtyp; + + /* Perhaps it's an artifact specified by name, not type */ + name = artifact_name(actualn, &objtyp); + if(name) { + typ = objtyp; + goto typfnd; + } + } +#ifdef WIZARD + /* Let wizards wish for traps --KAA */ + /* must come after objects check so wizards can still wish for + * trap objects like beartraps + */ + if (wizard && from_user) { + int trap; + + for (trap = NO_TRAP+1; trap < TRAPNUM; trap++) { + const char *tname; + + tname = defsyms[trap_to_defsym(trap)].explanation; + if (!strncmpi(tname, bp, strlen(tname))) { + /* avoid stupid mistakes */ + if((trap == TRAPDOOR || trap == HOLE) + && !Can_fall_thru(&u.uz)) trap = ROCKTRAP; + (void) maketrap(u.ux, u.uy, trap); + pline("%s.", An(tname)); + return(&zeroobj); + } + } + /* or some other dungeon features -dlc */ + p = eos(bp); + if(!BSTRCMP(bp, p-8, "fountain")) { + levl[u.ux][u.uy].typ = FOUNTAIN; + level.flags.nfountains++; + if(!strncmpi(bp, "magic ", 6)) + levl[u.ux][u.uy].blessedftn = 1; + pline("A %sfountain.", + levl[u.ux][u.uy].blessedftn ? "magic " : ""); + newsym(u.ux, u.uy); + return(&zeroobj); + } + if(!BSTRCMP(bp, p-6, "throne")) { + levl[u.ux][u.uy].typ = THRONE; + pline("A throne."); + newsym(u.ux, u.uy); + return(&zeroobj); + } +# ifdef SINKS + if(!BSTRCMP(bp, p-4, "sink")) { + levl[u.ux][u.uy].typ = SINK; + level.flags.nsinks++; + pline("A sink."); + newsym(u.ux, u.uy); + return &zeroobj; + } +# endif + if(!BSTRCMP(bp, p-4, "pool")) { + levl[u.ux][u.uy].typ = POOL; + del_engr_at(u.ux, u.uy); + pline("A pool."); + /* Must manually make kelp! */ + water_damage(level.objects[u.ux][u.uy], FALSE, TRUE); + newsym(u.ux, u.uy); + return &zeroobj; + } + if (!BSTRCMP(bp, p-4, "lava")) { /* also matches "molten lava" */ + levl[u.ux][u.uy].typ = LAVAPOOL; + del_engr_at(u.ux, u.uy); + pline("A pool of molten lava."); + if (!(Levitation || Flying)) (void) lava_effects(); + newsym(u.ux, u.uy); + return &zeroobj; + } + + if(!BSTRCMP(bp, p-5, "altar")) { + aligntyp al; + + levl[u.ux][u.uy].typ = ALTAR; + if(!strncmpi(bp, "chaotic ", 8)) + al = A_CHAOTIC; + else if(!strncmpi(bp, "neutral ", 8)) + al = A_NEUTRAL; + else if(!strncmpi(bp, "lawful ", 7)) + al = A_LAWFUL; + else if(!strncmpi(bp, "unaligned ", 10)) + al = A_NONE; + else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */ + al = (!rn2(6)) ? A_NONE : rn2((int)A_LAWFUL+2) - 1; + levl[u.ux][u.uy].altarmask = Align2amask( al ); + pline("%s altar.", An(align_str(al))); + newsym(u.ux, u.uy); + return(&zeroobj); + } + + if(!BSTRCMP(bp, p-5, "grave") || !BSTRCMP(bp, p-9, "headstone")) { + make_grave(u.ux, u.uy, (char *) 0); + pline("A grave."); + newsym(u.ux, u.uy); + return(&zeroobj); + } + + if(!BSTRCMP(bp, p-4, "tree")) { + levl[u.ux][u.uy].typ = TREE; + pline("A tree."); + newsym(u.ux, u.uy); + block_point(u.ux, u.uy); + return &zeroobj; + } + + if(!BSTRCMP(bp, p-4, "bars")) { + levl[u.ux][u.uy].typ = IRONBARS; + pline("Iron bars."); + newsym(u.ux, u.uy); + return &zeroobj; + } + } +#endif + if(!oclass) return((struct obj *)0); +any: + if(!oclass) oclass = wrpsym[rn2((int)sizeof(wrpsym))]; +typfnd: + if (typ) oclass = objects[typ].oc_class; + + /* check for some objects that are not allowed */ + if (typ && objects[typ].oc_unique) { +#ifdef WIZARD + if (wizard) + ; /* allow unique objects */ + else +#endif + switch (typ) { + case AMULET_OF_YENDOR: + typ = FAKE_AMULET_OF_YENDOR; + break; + case CANDELABRUM_OF_INVOCATION: + typ = rnd_class(TALLOW_CANDLE, WAX_CANDLE); + break; + case BELL_OF_OPENING: + typ = BELL; + break; + case SPE_BOOK_OF_THE_DEAD: + typ = SPE_BLANK_PAPER; + break; + } + } + + /* catch any other non-wishable objects */ + if (objects[typ].oc_nowish +#ifdef WIZARD + && !wizard +#endif + ) + return((struct obj *)0); + + /* convert magic lamps to regular lamps before lighting them or setting + the charges */ + if (typ == MAGIC_LAMP +#ifdef WIZARD + && !wizard +#endif + ) + typ = OIL_LAMP; + + if(typ) { + otmp = mksobj(typ, TRUE, FALSE); + } else { + otmp = mkobj(oclass, FALSE); + if (otmp) typ = otmp->otyp; + } + + if (islit && + (typ == OIL_LAMP || typ == MAGIC_LAMP || typ == BRASS_LANTERN || + Is_candle(otmp) || typ == POT_OIL)) { + place_object(otmp, u.ux, u.uy); /* make it viable light source */ + begin_burn(otmp, FALSE); + obj_extract_self(otmp); /* now release it for caller's use */ + } + + if(cnt > 0 && objects[typ].oc_merge && oclass != SPBOOK_CLASS && + (cnt < rnd(6) || +#ifdef WIZARD + wizard || +#endif + (cnt <= 7 && Is_candle(otmp)) || + (cnt <= 20 && + ((oclass == WEAPON_CLASS && is_ammo(otmp)) + || typ == ROCK || is_missile(otmp))))) + otmp->quan = (long) cnt; + +#ifdef WIZARD + if (oclass == VENOM_CLASS) otmp->spe = 1; +#endif + + if (spesgn == 0) spe = otmp->spe; +#ifdef WIZARD + else if (wizard) /* no alteration to spe */ ; +#endif + else if (oclass == ARMOR_CLASS || oclass == WEAPON_CLASS || + is_weptool(otmp) || + (oclass==RING_CLASS && objects[typ].oc_charged)) { + if(spe > rnd(5) && spe > otmp->spe) spe = 0; + if(spe > 2 && Luck < 0) spesgn = -1; + } else { + if (oclass == WAND_CLASS) { + if (spe > 1 && spesgn == -1) spe = 1; + } else { + if (spe > 0 && spesgn == -1) spe = 0; + } + if (spe > otmp->spe) spe = otmp->spe; + } + + if (spesgn == -1) spe = -spe; + + /* set otmp->spe. This may, or may not, use spe... */ + switch (typ) { + case TIN: if (contents==EMPTY) { + otmp->corpsenm = NON_PM; + otmp->spe = 0; + } else if (contents==SPINACH) { + otmp->corpsenm = NON_PM; + otmp->spe = 1; + } + break; + case SLIME_MOLD: otmp->spe = ftype; + /* Fall through */ + case SKELETON_KEY: case CHEST: case LARGE_BOX: + case HEAVY_IRON_BALL: case IRON_CHAIN: case STATUE: + /* otmp->cobj already done in mksobj() */ + break; +#ifdef MAIL + case SCR_MAIL: otmp->spe = 1; break; +#endif + case WAN_WISHING: +#ifdef WIZARD + if (!wizard) { +#endif + otmp->spe = (rn2(10) ? -1 : 0); + break; +#ifdef WIZARD + } + /* fall through, if wizard */ +#endif + default: otmp->spe = spe; + } + + /* set otmp->corpsenm or dragon scale [mail] */ + if (mntmp >= LOW_PM) { + if (mntmp == PM_LONG_WORM_TAIL) mntmp = PM_LONG_WORM; + + switch (typ) { + case TIN: + otmp->spe = 0; /* No spinach */ + if (dead_species(mntmp, FALSE)) { + otmp->corpsenm = NON_PM; /* it's empty */ + } else if (!(mons[mntmp].geno & G_UNIQ) && + !(mvitals[mntmp].mvflags & G_NOCORPSE) && + mons[mntmp].cnutrit != 0) { + otmp->corpsenm = mntmp; + } + break; + case CORPSE: + if (!(mons[mntmp].geno & G_UNIQ) && + !(mvitals[mntmp].mvflags & G_NOCORPSE)) { + /* beware of random troll or lizard corpse, + or of ordinary one being forced to such */ + if (otmp->timed) obj_stop_timers(otmp); + if (mons[mntmp].msound == MS_GUARDIAN) + otmp->corpsenm = genus(mntmp,1); + else + otmp->corpsenm = mntmp; + start_corpse_timeout(otmp); + } + break; + case FIGURINE: + if (!(mons[mntmp].geno & G_UNIQ) + && !is_human(&mons[mntmp]) +#ifdef MAIL + && mntmp != PM_MAIL_DAEMON +#endif + ) + otmp->corpsenm = mntmp; + break; + case EGG: + mntmp = can_be_hatched(mntmp); + if (mntmp != NON_PM) { + otmp->corpsenm = mntmp; + if (!dead_species(mntmp, TRUE)) + attach_egg_hatch_timeout(otmp); + else + kill_egg(otmp); + } + break; + case STATUE: otmp->corpsenm = mntmp; + if (Has_contents(otmp) && verysmall(&mons[mntmp])) + delete_contents(otmp); /* no spellbook */ + otmp->spe = ishistoric ? STATUE_HISTORIC : 0; + break; + case SCALE_MAIL: + /* Dragon mail - depends on the order of objects */ + /* & dragons. */ + if (mntmp >= PM_GRAY_DRAGON && + mntmp <= PM_YELLOW_DRAGON) + otmp->otyp = GRAY_DRAGON_SCALE_MAIL + + mntmp - PM_GRAY_DRAGON; + break; + } + } + + /* set blessed/cursed -- setting the fields directly is safe + * since weight() is called below and addinv() will take care + * of luck */ + if (iscursed) { + curse(otmp); + } else if (uncursed) { + otmp->blessed = 0; + otmp->cursed = (Luck < 0 +#ifdef WIZARD + && !wizard +#endif + ); + } else if (blessed) { + otmp->blessed = (Luck >= 0 +#ifdef WIZARD + || wizard +#endif + ); + otmp->cursed = (Luck < 0 +#ifdef WIZARD + && !wizard +#endif + ); + } else if (spesgn < 0) { + curse(otmp); + } + +#ifdef INVISIBLE_OBJECTS + if (isinvisible) otmp->oinvis = 1; +#endif + + /* set eroded */ + if (is_damageable(otmp) || otmp->otyp == CRYSKNIFE) { + if (eroded && (is_flammable(otmp) || is_rustprone(otmp))) + otmp->oeroded = eroded; + if (eroded2 && (is_corrodeable(otmp) || is_rottable(otmp))) + otmp->oeroded2 = eroded2; + + /* set erodeproof */ + if (erodeproof && !eroded && !eroded2) + otmp->oerodeproof = (Luck >= 0 +#ifdef WIZARD + || wizard +#endif + ); + } + + /* set otmp->recharged */ + if (oclass == WAND_CLASS) { + /* prevent wishing abuse */ + if (otmp->otyp == WAN_WISHING +#ifdef WIZARD + && !wizard +#endif + ) rechrg = 1; + otmp->recharged = (unsigned)rechrg; + } + + /* set poisoned */ + if (ispoisoned) { + if (is_poisonable(otmp)) + otmp->opoisoned = (Luck >= 0); + else if (Is_box(otmp) || typ == TIN) + otmp->otrapped = 1; + else if (oclass == FOOD_CLASS) + /* try to taint by making it as old as possible */ + otmp->age = 1L; + } + + if (isgreased) otmp->greased = 1; + + if (isdiluted && otmp->oclass == POTION_CLASS && + otmp->otyp != POT_WATER) + otmp->odiluted = 1; + + if (name) { + const char *aname; + short objtyp; + + /* an artifact name might need capitalization fixing */ + aname = artifact_name(name, &objtyp); + if (aname && objtyp == otmp->otyp) name = aname; + + otmp = oname(otmp, name); + if (otmp->oartifact) { + otmp->quan = 1L; + u.uconduct.wisharti++; /* KMH, conduct */ + } + } + + /* more wishing abuse: don't allow wishing for certain artifacts */ + /* and make them pay; charge them for the wish anyway! */ + if ((is_quest_artifact(otmp) || + (otmp->oartifact && rn2(nartifact_exist()) > 1)) +#ifdef WIZARD + && !wizard +#endif + ) { + artifact_exists(otmp, ONAME(otmp), FALSE); + obfree(otmp, (struct obj *) 0); + otmp = &zeroobj; + pline("For a moment, you feel %s in your %s, but it disappears!", + something, + makeplural(body_part(HAND))); + } + + if (halfeaten && otmp->oclass == FOOD_CLASS) { + if (otmp->otyp == CORPSE) + otmp->oeaten = mons[otmp->corpsenm].cnutrit; + else otmp->oeaten = objects[otmp->otyp].oc_nutrition; + /* (do this adjustment before setting up object's weight) */ + consume_oeaten(otmp, 1); + } + otmp->owt = weight(otmp); + if (very && otmp->otyp == HEAVY_IRON_BALL) otmp->owt += 160; + + return(otmp); +} + +int +rnd_class(first,last) +int first,last; +{ + int i, x, sum=0; + + if (first == last) + return (first); + for(i=first; i<=last; i++) + sum += objects[i].oc_prob; + if (!sum) /* all zero */ + return first + rn2(last-first+1); + x = rnd(sum); + for(i=first; i<=last; i++) + if (objects[i].oc_prob && (x -= objects[i].oc_prob) <= 0) + return i; + return 0; +} + +STATIC_OVL const char * +Japanese_item_name(i) +int i; +{ + struct Jitem *j = Japanese_items; + + while(j->item) { + if (i == j->item) + return j->name; + j++; + } + return (const char *)0; +} + +const char * +cloak_simple_name(cloak) +struct obj *cloak; +{ + if (cloak) { + switch (cloak->otyp) { + case ROBE: + return "robe"; + case MUMMY_WRAPPING: + return "wrapping"; + case ALCHEMY_SMOCK: + return (objects[cloak->otyp].oc_name_known && + cloak->dknown) ? "smock" : "apron"; + default: + break; + } + } + return "cloak"; +} + +const char * +mimic_obj_name(mtmp) +struct monst *mtmp; +{ + if (mtmp->m_ap_type == M_AP_OBJECT && mtmp->mappearance != STRANGE_OBJECT) { + int idx = objects[mtmp->mappearance].oc_descr_idx; + if (mtmp->mappearance == GOLD_PIECE) return "gold"; + return obj_descr[idx].oc_name; + } + return "whatcha-may-callit"; +} +#endif /* OVLB */ + +/*objnam.c*/ diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..dfb8006 --- /dev/null +++ b/src/options.c @@ -0,0 +1,3862 @@ +/* SCCS Id: @(#)options.c 3.4 2003/11/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */ +#include "config.h" +#include "objclass.h" +#include "flag.h" +NEARDATA struct flag flags; /* provide linkage */ +NEARDATA struct instance_flags iflags; /* provide linkage */ +#define static +#else +#include "hack.h" +#include "tcap.h" +#include +#endif + +#define WINTYPELEN 16 + +#ifdef DEFAULT_WC_TILED_MAP +#define PREFER_TILED TRUE +#else +#define PREFER_TILED FALSE +#endif + +/* + * NOTE: If you add (or delete) an option, please update the short + * options help (option_help()), the long options help (dat/opthelp), + * and the current options setting display function (doset()), + * and also the Guidebooks. + * + * The order matters. If an option is a an initial substring of another + * option (e.g. time and timed_delay) the shorter one must come first. + */ + +static struct Bool_Opt +{ + const char *name; + boolean *addr, initvalue; + int optflags; +} boolopt[] = { +#ifdef AMIGA + {"altmeta", &flags.altmeta, TRUE, DISP_IN_GAME}, +#else + {"altmeta", (boolean *)0, TRUE, DISP_IN_GAME}, +#endif + {"ascii_map", &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME}, /*WC*/ +#ifdef MFLOPPY + {"asksavedisk", &flags.asksavedisk, FALSE, SET_IN_GAME}, +#else + {"asksavedisk", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"autodig", &flags.autodig, FALSE, SET_IN_GAME}, + {"autopickup", &flags.pickup, TRUE, SET_IN_GAME}, + {"autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME}, +#if defined(MICRO) && !defined(AMIGA) + {"BIOS", &iflags.BIOS, FALSE, SET_IN_FILE}, +#else + {"BIOS", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifdef INSURANCE + {"checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME}, +#else + {"checkpoint", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifdef MFLOPPY + {"checkspace", &iflags.checkspace, TRUE, SET_IN_GAME}, +#else + {"checkspace", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME}, +# if defined(MICRO) || defined(WIN32) + {"color", &iflags.wc_color,TRUE, SET_IN_GAME}, /*WC*/ +# else /* systems that support multiple terminals, many monochrome */ + {"color", &iflags.wc_color, FALSE, SET_IN_GAME}, /*WC*/ +# endif + {"confirm",&flags.confirm, TRUE, SET_IN_GAME}, +#if defined(TERMLIB) && !defined(MAC_GRAPHICS_ENV) + {"DECgraphics", &iflags.DECgraphics, FALSE, SET_IN_GAME}, +#else + {"DECgraphics", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME}, /*WC*/ +#ifdef TTY_GRAPHICS + {"extmenu", &iflags.extmenu, FALSE, SET_IN_GAME}, +#else + {"extmenu", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifdef OPT_DISPMAP + {"fast_map", &flags.fast_map, TRUE, SET_IN_GAME}, +#else + {"fast_map", (boolean *)0, TRUE, SET_IN_FILE}, +#endif + {"female", &flags.female, FALSE, DISP_IN_GAME}, + {"fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME}, +#ifdef AMIFLUSH + {"flush", &flags.amiflush, FALSE, SET_IN_GAME}, +#else + {"flush", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE}, + {"help", &flags.help, TRUE, SET_IN_GAME}, + {"hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME}, /*WC*/ +#ifdef ASCIIGRAPH + {"IBMgraphics", &iflags.IBMgraphics, FALSE, SET_IN_GAME}, +#else + {"IBMgraphics", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifndef MAC + {"ignintr", &flags.ignintr, FALSE, SET_IN_GAME}, +#else + {"ignintr", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"large_font", &iflags.obsolete, FALSE, SET_IN_FILE}, /* OBSOLETE */ + {"legacy", &flags.legacy, TRUE, DISP_IN_GAME}, + {"lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME}, + {"lootabc", &iflags.lootabc, FALSE, SET_IN_GAME}, +#ifdef MAC_GRAPHICS_ENV + {"Macgraphics", &iflags.MACgraphics, TRUE, SET_IN_GAME}, +#else + {"Macgraphics", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifdef MAIL + {"mail", &flags.biff, TRUE, SET_IN_GAME}, +#else + {"mail", (boolean *)0, TRUE, SET_IN_FILE}, +#endif +#ifdef WIZARD + /* for menu debugging only*/ + {"menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_GAME}, +#else + {"menu_tab_sep", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"mouse_support", &iflags.wc_mouse_support, TRUE, DISP_IN_GAME}, /*WC*/ +#ifdef NEWS + {"news", &iflags.news, TRUE, DISP_IN_GAME}, +#else + {"news", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"null", &flags.null, TRUE, SET_IN_GAME}, +#ifdef MAC + {"page_wait", &flags.page_wait, TRUE, SET_IN_GAME}, +#else + {"page_wait", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"perm_invent", &flags.perm_invent, FALSE, SET_IN_GAME}, + {"popup_dialog", &iflags.wc_popup_dialog, FALSE, SET_IN_GAME}, /*WC*/ + {"prayconfirm", &flags.prayconfirm, TRUE, SET_IN_GAME}, + {"preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME}, /*WC*/ + {"pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME}, +#if defined(MICRO) && !defined(AMIGA) + {"rawio", &iflags.rawio, FALSE, DISP_IN_GAME}, +#else + {"rawio", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME}, + {"safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME}, +#ifdef WIZARD + {"sanity_check", &iflags.sanity_check, FALSE, SET_IN_GAME}, +#else + {"sanity_check", (boolean *)0, FALSE, SET_IN_FILE}, +#endif +#ifdef EXP_ON_BOTL + {"showexp", &flags.showexp, FALSE, SET_IN_GAME}, +#else + {"showexp", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"showrace", &iflags.showrace, FALSE, SET_IN_GAME}, +#ifdef SCORE_ON_BOTL + {"showscore", &flags.showscore, FALSE, SET_IN_GAME}, +#else + {"showscore", (boolean *)0, FALSE, SET_IN_FILE}, +#endif + {"silent", &flags.silent, TRUE, SET_IN_GAME}, + {"softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE}, + {"sortpack", &flags.sortpack, TRUE, SET_IN_GAME}, + {"sound", &flags.soundok, TRUE, SET_IN_GAME}, + {"sparkle", &flags.sparkle, TRUE, SET_IN_GAME}, + {"standout", &flags.standout, FALSE, SET_IN_GAME}, + {"splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME}, /*WC*/ + {"tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME}, /*WC*/ + {"time", &flags.time, FALSE, SET_IN_GAME}, +#ifdef TIMED_DELAY + {"timed_delay", &flags.nap, TRUE, SET_IN_GAME}, +#else + {"timed_delay", (boolean *)0, FALSE, SET_IN_GAME}, +#endif + {"tombstone",&flags.tombstone, TRUE, SET_IN_GAME}, + {"toptenwin",&flags.toptenwin, FALSE, SET_IN_GAME}, + {"travel", &iflags.travelcmd, TRUE, SET_IN_GAME}, +#ifdef WIN32CON + {"use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME}, /*WC*/ +#else + {"use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME}, /*WC*/ +#endif + {"verbose", &flags.verbose, TRUE, SET_IN_GAME}, + {"wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME}, + {(char *)0, (boolean *)0, FALSE, 0} +}; + +/* compound options, for option_help() and external programs like Amiga + * frontend */ +static struct Comp_Opt +{ + const char *name, *descr; + int size; /* for frontends and such allocating space -- + * usually allowed size of data in game, but + * occasionally maximum reasonable size for + * typing when game maintains information in + * a different format */ + int optflags; +} compopt[] = { + { "align", "your starting alignment (lawful, neutral, or chaotic)", + 8, DISP_IN_GAME }, + { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/ + { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/ + { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME }, + { "boulder", "the symbol to use for displaying boulders", + 1, SET_IN_GAME }, + { "catname", "the name of your (first) cat (e.g., catname:Tabby)", + PL_PSIZ, DISP_IN_GAME }, + { "disclose", "the kinds of information to disclose at end of game", + sizeof(flags.end_disclose) * 2, + SET_IN_GAME }, + { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", + PL_PSIZ, DISP_IN_GAME }, + { "dungeon", "the symbols to use in drawing the dungeon map", + MAXDCHARS+1, SET_IN_FILE }, + { "effects", "the symbols to use in drawing special effects", + MAXECHARS+1, SET_IN_FILE }, + { "font_map", "the font to use in the map window", 40, DISP_IN_GAME }, /*WC*/ + { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/ + { "font_message", "the font to use in the message window", + 40, DISP_IN_GAME }, /*WC*/ + { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/ + { "font_size_menu", "the size of the menu font", 20, DISP_IN_GAME }, /*WC*/ + { "font_size_message", "the size of the message font", 20, DISP_IN_GAME }, /*WC*/ + { "font_size_status", "the size of the status font", 20, DISP_IN_GAME }, /*WC*/ + { "font_size_text", "the size of the text font", 20, DISP_IN_GAME }, /*WC*/ + { "font_status", "the font to use in status window", 40, DISP_IN_GAME }, /*WC*/ + { "font_text", "the font to use in text windows", 40, DISP_IN_GAME }, /*WC*/ + { "fruit", "the name of a fruit you enjoy eating", + PL_FSIZ, SET_IN_GAME }, + { "gender", "your starting gender (male or female)", + 8, DISP_IN_GAME }, + { "horsename", "the name of your (first) horse (e.g., horsename:Silver)", + PL_PSIZ, DISP_IN_GAME }, + { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/ + { "menustyle", "user interface for object selection", + MENUTYPELEN, SET_IN_GAME }, + { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE }, + { "menu_deselect_page", "deselect all items on this page of a menu", + 4, SET_IN_FILE }, + { "menu_first_page", "jump to the first page in a menu", + 4, SET_IN_FILE }, + { "menu_headings", "bold, inverse, or underline headings", 9, SET_IN_GAME }, + { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE }, + { "menu_invert_page", "invert all items on this page of a menu", + 4, SET_IN_FILE }, + { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE }, + { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE }, + { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE }, + { "menu_search", "search for a menu item", 4, SET_IN_FILE }, + { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE }, + { "menu_select_page", "select all items on this page of a menu", + 4, SET_IN_FILE }, + { "monsters", "the symbols to use for monsters", + MAXMCLASSES, SET_IN_FILE }, + { "msghistory", "number of top line messages to save", + 5, DISP_IN_GAME }, +# ifdef TTY_GRAPHICS + {"msg_window", "the type of message window required",1, SET_IN_GAME}, +# else + {"msg_window", "the type of message window required", 1, SET_IN_FILE}, +# endif + { "name", "your character's name (e.g., name:Merlin-W)", + PL_NSIZ, DISP_IN_GAME }, + { "number_pad", "use the number pad", 1, SET_IN_GAME}, + { "objects", "the symbols to use for objects", + MAXOCLASSES, SET_IN_FILE }, + { "packorder", "the inventory order of the items in your pack", + MAXOCLASSES, SET_IN_GAME }, +#ifdef CHANGE_COLOR + { "palette", "palette (00c/880/-fff is blue/yellow/reverse white)", + 15 , SET_IN_GAME }, +# if defined(MAC) + { "hicolor", "same as palette, only order is reversed", + 15, SET_IN_FILE }, +# endif +#endif + { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME }, + { "pickup_burden", "maximum burden picked up before prompt", + 20, SET_IN_GAME }, + { "pickup_types", "types of objects to pick up automatically", + MAXOCLASSES, SET_IN_GAME }, + { "player_selection", "choose character via dialog or prompts", + 12, DISP_IN_GAME }, + { "race", "your starting race (e.g., Human, Elf)", + PL_CSIZ, DISP_IN_GAME }, + { "role", "your starting role (e.g., Barbarian, Valkyrie)", + PL_CSIZ, DISP_IN_GAME }, + { "runmode", "display frequency when `running' or `travelling'", + sizeof "teleport", SET_IN_GAME }, + { "scores", "the parts of the score list you wish to see", + 32, SET_IN_GAME }, + { "scroll_amount", "amount to scroll map when scroll_margin is reached", + 20, DISP_IN_GAME }, /*WC*/ + { "scroll_margin", "scroll map when this far from the edge", 20, DISP_IN_GAME }, /*WC*/ +#ifdef MSDOS + { "soundcard", "type of sound card to use", 20, SET_IN_FILE }, +#endif + { "suppress_alert", "suppress alerts about version-specific features", + 8, SET_IN_GAME }, + { "tile_width", "width of tiles", 20, DISP_IN_GAME}, /*WC*/ + { "tile_height", "height of tiles", 20, DISP_IN_GAME}, /*WC*/ + { "tile_file", "name of tile file", 70, DISP_IN_GAME}, /*WC*/ + { "traps", "the symbols to use in drawing traps", + MAXTCHARS+1, SET_IN_FILE }, + { "vary_msgcount", "show more old messages at a time", 20, DISP_IN_GAME }, /*WC*/ +#ifdef MSDOS + { "video", "method of video updating", 20, SET_IN_FILE }, +#endif +#ifdef VIDEOSHADES + { "videocolors", "color mappings for internal screen routines", + 40, DISP_IN_GAME }, + { "videoshades", "gray shades to map to black/gray/white", + 32, DISP_IN_GAME }, +#endif +#ifdef WIN32CON + {"subkeyvalue", "override keystroke value", 7, SET_IN_FILE}, +#endif + { "windowcolors", "the foreground/background colors of windows", /*WC*/ + 80, DISP_IN_GAME }, + { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME }, + { (char *)0, (char *)0, 0, 0 } +}; + +#ifdef OPTION_LISTS_ONLY +#undef static + +#else /* use rest of file */ + +static boolean need_redraw; /* for doset() */ + +#if defined(TOS) && defined(TEXTCOLOR) +extern boolean colors_changed; /* in tos.c */ +#endif + +#ifdef VIDEOSHADES +extern char *shade[3]; /* in sys/msdos/video.c */ +extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ +#endif + +static char def_inv_order[MAXOCLASSES] = { + COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS, + SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS, + TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0, +}; + +/* + * Default menu manipulation command accelerators. These may _not_ be: + * + * + a number - reserved for counts + * + an upper or lower case US ASCII letter - used for accelerators + * + ESC - reserved for escaping the menu + * + NULL, CR or LF - reserved for commiting the selection(s). NULL + * is kind of odd, but the tty's xwaitforspace() will return it if + * someone hits a . + * + a default object class symbol - used for object class accelerators + * + * Standard letters (for now) are: + * + * < back 1 page + * > forward 1 page + * ^ first page + * | last page + * : search + * + * page all + * , select . + * \ deselect - + * ~ invert @ + * + * The command name list is duplicated in the compopt array. + */ +typedef struct { + const char *name; + char cmd; +} menu_cmd_t; + +#define NUM_MENU_CMDS 11 +static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = { +/* 0*/ { "menu_first_page", MENU_FIRST_PAGE }, + { "menu_last_page", MENU_LAST_PAGE }, + { "menu_next_page", MENU_NEXT_PAGE }, + { "menu_previous_page", MENU_PREVIOUS_PAGE }, + { "menu_select_all", MENU_SELECT_ALL }, +/* 5*/ { "menu_deselect_all", MENU_UNSELECT_ALL }, + { "menu_invert_all", MENU_INVERT_ALL }, + { "menu_select_page", MENU_SELECT_PAGE }, + { "menu_deselect_page", MENU_UNSELECT_PAGE }, + { "menu_invert_page", MENU_INVERT_PAGE }, +/*10*/ { "menu_search", MENU_SEARCH }, +}; + +/* + * Allow the user to map incoming characters to various menu commands. + * The accelerator list must be a valid C string. + */ +#define MAX_MENU_MAPPED_CMDS 32 /* some number */ + char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS+1]; /* exported */ +static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1]; +static short n_menu_mapped = 0; + + +static boolean initial, from_file; + +STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int)); +STATIC_DCL void FDECL(nmcpy, (char *, const char *, int)); +STATIC_DCL void FDECL(escapes, (const char *, char *)); +STATIC_DCL void FDECL(rejectoption, (const char *)); +STATIC_DCL void FDECL(badoption, (const char *)); +STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P)); +STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P)); +STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P)); +STATIC_DCL int FDECL(change_inv_order, (char *)); +STATIC_DCL void FDECL(oc_to_str, (char *, char *)); +STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int)); +STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *)); +STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *)); +STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P)); +STATIC_DCL void FDECL(warning_opts, (char *,const char *)); +STATIC_DCL void FDECL(duplicate_opt_detection, (const char *, int)); + +STATIC_OVL void FDECL(wc_set_font_name, (int, char *)); +STATIC_OVL int FDECL(wc_set_window_colors, (char *)); +STATIC_OVL boolean FDECL(is_wc_option, (const char *)); +STATIC_OVL boolean FDECL(wc_supported, (const char *)); +STATIC_OVL boolean FDECL(is_wc2_option, (const char *)); +STATIC_OVL boolean FDECL(wc2_supported, (const char *)); +#ifdef AUTOPICKUP_EXCEPTIONS +STATIC_DCL void FDECL(remove_autopickup_exception, (struct autopickup_exception *)); +STATIC_OVL int FDECL(count_ape_maps, (int *, int *)); +#endif + +/* check whether a user-supplied option string is a proper leading + substring of a particular option name; option string might have + a colon or equals sign and arbitrary value appended to it */ +boolean +match_optname(user_string, opt_name, min_length, val_allowed) +const char *user_string, *opt_name; +int min_length; +boolean val_allowed; +{ + int len = (int)strlen(user_string); + + if (val_allowed) { + const char *p = index(user_string, ':'), + *q = index(user_string, '='); + + if (!p || (q && q < p)) p = q; + while(p && p > user_string && isspace(*(p-1))) p--; + if (p) len = (int)(p - user_string); + } + + return (len >= min_length) && !strncmpi(opt_name, user_string, len); +} + +/* most environment variables will eventually be printed in an error + * message if they don't work, and most error message paths go through + * BUFSZ buffers, which could be overflowed by a maliciously long + * environment variable. if a variable can legitimately be long, or + * if it's put in a smaller buffer, the responsible code will have to + * bounds-check itself. + */ +char * +nh_getenv(ev) +const char *ev; +{ + char *getev = getenv(ev); + + if (getev && strlen(getev) <= (BUFSZ / 2)) + return getev; + else + return (char *)0; +} + +void +initoptions() +{ +#ifndef MAC + char *opts; +#endif + int i; + + /* initialize the random number generator */ + setrandom(); + + /* for detection of configfile options specified multiple times */ + iflags.opt_booldup = iflags.opt_compdup = (int *)0; + + for (i = 0; boolopt[i].name; i++) { + if (boolopt[i].addr) + *(boolopt[i].addr) = boolopt[i].initvalue; + } + flags.end_own = FALSE; + flags.end_top = 3; + flags.end_around = 2; + iflags.runmode = RUN_LEAP; + iflags.msg_history = 20; +#ifdef TTY_GRAPHICS + iflags.prevmsg_window = 's'; +#endif + iflags.menu_headings = ATR_INVERSE; + + /* Use negative indices to indicate not yet selected */ + flags.initrole = -1; + flags.initrace = -1; + flags.initgend = -1; + flags.initalign = -1; + + /* Set the default monster and object class symbols. Don't use */ + /* memcpy() --- sizeof char != sizeof uchar on some machines. */ + for (i = 0; i < MAXOCLASSES; i++) + oc_syms[i] = (uchar) def_oc_syms[i]; + for (i = 0; i < MAXMCLASSES; i++) + monsyms[i] = (uchar) def_monsyms[i]; + for (i = 0; i < WARNCOUNT; i++) + warnsyms[i] = def_warnsyms[i].sym; + iflags.bouldersym = 0; + iflags.travelcc.x = iflags.travelcc.y = -1; + flags.warnlevel = 1; + flags.warntype = 0L; + + /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */ + (void)memcpy((genericptr_t)flags.inv_order, + (genericptr_t)def_inv_order, sizeof flags.inv_order); + flags.pickup_types[0] = '\0'; + flags.pickup_burden = MOD_ENCUMBER; + + for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) + flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO; + switch_graphics(ASCII_GRAPHICS); /* set default characters */ +#if defined(UNIX) && defined(TTY_GRAPHICS) + /* + * Set defaults for some options depending on what we can + * detect about the environment's capabilities. + * This has to be done after the global initialization above + * and before reading user-specific initialization via + * config file/environment variable below. + */ + /* this detects the IBM-compatible console on most 386 boxes */ + if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) { + switch_graphics(IBM_GRAPHICS); +# ifdef TEXTCOLOR + iflags.use_color = TRUE; +# endif + } +#endif /* UNIX && TTY_GRAPHICS */ +#if defined(UNIX) || defined(VMS) +# ifdef TTY_GRAPHICS + /* detect whether a "vt" terminal can handle alternate charsets */ + if ((opts = nh_getenv("TERM")) && + !strncmpi(opts, "vt", 2) && AS && AE && + index(AS, '\016') && index(AE, '\017')) { + switch_graphics(DEC_GRAPHICS); + } +# endif +#endif /* UNIX || VMS */ + +#ifdef MAC_GRAPHICS_ENV + switch_graphics(MAC_GRAPHICS); +#endif /* MAC_GRAPHICS_ENV */ + flags.menu_style = MENU_FULL; + + /* since this is done before init_objects(), do partial init here */ + objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; + nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); +#ifndef MAC + opts = getenv("NETHACKOPTIONS"); + if (!opts) opts = getenv("HACKOPTIONS"); + if (opts) { + if (*opts == '/' || *opts == '\\' || *opts == '@') { + if (*opts == '@') opts++; /* @filename */ + /* looks like a filename */ + if (strlen(opts) < BUFSZ/2) + read_config_file(opts); + } else { + read_config_file((char *)0); + /* let the total length of options be long; + * parseoptions() will check each individually + */ + parseoptions(opts, TRUE, FALSE); + } + } else +#endif + read_config_file((char *)0); + + (void)fruitadd(pl_fruit); + /* Remove "slime mold" from list of object names; this will */ + /* prevent it from being wished unless it's actually present */ + /* as a named (or default) fruit. Wishing for "fruit" will */ + /* result in the player's preferred fruit [better than "\033"]. */ + obj_descr[SLIME_MOLD].oc_name = "fruit"; + + return; +} + +STATIC_OVL void +nmcpy(dest, src, maxlen) + char *dest; + const char *src; + int maxlen; +{ + int count; + + for(count = 1; count < maxlen; count++) { + if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/ + *dest++ = *src++; + } + *dest = 0; +} + +/* + * escapes: escape expansion for showsyms. C-style escapes understood include + * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix + * for control characters is also understood, and \[mM] followed by any of the + * previous forms or by a character has the effect of 'meta'-ing the value (so + * that the alternate character set will be enabled). + */ +STATIC_OVL void +escapes(cp, tp) +const char *cp; +char *tp; +{ + while (*cp) + { + int cval = 0, meta = 0; + + if (*cp == '\\' && index("mM", cp[1])) { + meta = 1; + cp += 2; + } + if (*cp == '\\' && index("0123456789xXoO", cp[1])) + { + const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; + int dcount = 0; + + cp++; + if (*cp == 'x' || *cp == 'X') + for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++) + cval = (cval * 16) + (dp - hex) / 2; + else if (*cp == 'o' || *cp == 'O') + for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++) + cval = (cval * 8) + (*cp - '0'); + else + for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++) + cval = (cval * 10) + (*cp - '0'); + } + else if (*cp == '\\') /* C-style character escapes */ + { + switch (*++cp) + { + case '\\': cval = '\\'; break; + case 'n': cval = '\n'; break; + case 't': cval = '\t'; break; + case 'b': cval = '\b'; break; + case 'r': cval = '\r'; break; + default: cval = *cp; + } + cp++; + } + else if (*cp == '^') /* expand control-character syntax */ + { + cval = (*++cp & 0x1f); + cp++; + } + else + cval = *cp++; + if (meta) + cval |= 0x80; + *tp++ = cval; + } + *tp = '\0'; +} + +STATIC_OVL void +rejectoption(optname) +const char *optname; +{ +#ifdef MICRO + pline("\"%s\" settable only from %s.", optname, configfile); +#else + pline("%s can be set only from NETHACKOPTIONS or %s.", optname, + configfile); +#endif +} + +STATIC_OVL void +badoption(opts) +const char *opts; +{ + if (!initial) { + if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1)) + option_help(); + else + pline("Bad syntax: %s. Enter \"?g\" for help.", opts); + return; + } +#ifdef MAC + else return; +#endif + + if(from_file) + raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts); + else + raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts); + + wait_synch(); +} + +STATIC_OVL char * +string_for_opt(opts, val_optional) +char *opts; +boolean val_optional; +{ + char *colon, *equals; + + colon = index(opts, ':'); + equals = index(opts, '='); + if (!colon || (equals && equals < colon)) colon = equals; + + if (!colon || !*++colon) { + if (!val_optional) badoption(opts); + return (char *)0; + } + return colon; +} + +STATIC_OVL char * +string_for_env_opt(optname, opts, val_optional) +const char *optname; +char *opts; +boolean val_optional; +{ + if(!initial) { + rejectoption(optname); + return (char *)0; + } + return string_for_opt(opts, val_optional); +} + +STATIC_OVL void +bad_negation(optname, with_parameter) +const char *optname; +boolean with_parameter; +{ + pline_The("%s option may not %sbe negated.", + optname, + with_parameter ? "both have a value and " : ""); +} + +/* + * Change the inventory order, using the given string as the new order. + * Missing characters in the new order are filled in at the end from + * the current inv_order, except for gold, which is forced to be first + * if not explicitly present. + * + * This routine returns 1 unless there is a duplicate or bad char in + * the string. + */ +STATIC_OVL int +change_inv_order(op) +char *op; +{ + int oc_sym, num; + char *sp, buf[BUFSZ]; + + num = 0; +#ifndef GOLDOBJ + if (!index(op, GOLD_SYM)) + buf[num++] = COIN_CLASS; +#else + /* !!!! probably unnecessary with gold as normal inventory */ +#endif + + for (sp = op; *sp; sp++) { + oc_sym = def_char_to_objclass(*sp); + /* reject bad or duplicate entries */ + if (oc_sym == MAXOCLASSES || + oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS || + !index(flags.inv_order, oc_sym) || index(sp+1, *sp)) + return 0; + /* retain good ones */ + buf[num++] = (char) oc_sym; + } + buf[num] = '\0'; + + /* fill in any omitted classes, using previous ordering */ + for (sp = flags.inv_order; *sp; sp++) + if (!index(buf, *sp)) { + buf[num++] = *sp; + buf[num] = '\0'; /* explicitly terminate for next index() */ + } + + Strcpy(flags.inv_order, buf); + return 1; +} + +STATIC_OVL void +graphics_opts(opts, optype, maxlen, offset) +register char *opts; +const char *optype; +int maxlen, offset; +{ + uchar translate[MAXPCHARS+1]; + int length, i; + + if (!(opts = string_for_env_opt(optype, opts, FALSE))) + return; + escapes(opts, opts); + + length = strlen(opts); + if (length > maxlen) length = maxlen; + /* match the form obtained from PC configuration files */ + for (i = 0; i < length; i++) + translate[i] = (uchar) opts[i]; + assign_graphics(translate, length, maxlen, offset); +} + +STATIC_OVL void +warning_opts(opts, optype) +register char *opts; +const char *optype; +{ + uchar translate[MAXPCHARS+1]; + int length, i; + + if (!(opts = string_for_env_opt(optype, opts, FALSE))) + return; + escapes(opts, opts); + + length = strlen(opts); + if (length > WARNCOUNT) length = WARNCOUNT; + /* match the form obtained from PC configuration files */ + for (i = 0; i < length; i++) + translate[i] = (((i < WARNCOUNT) && opts[i]) ? + (uchar) opts[i] : def_warnsyms[i].sym); + assign_warnings(translate); +} + +void +assign_warnings(graph_chars) +register uchar *graph_chars; +{ + int i; + for (i = 0; i < WARNCOUNT; i++) + if (graph_chars[i]) warnsyms[i] = graph_chars[i]; +} + +STATIC_OVL int +feature_alert_opts(op, optn) +char *op; +const char *optn; +{ + char buf[BUFSZ]; + boolean rejectver = FALSE; + unsigned long fnv = get_feature_notice_ver(op); /* version.c */ + if (fnv == 0L) return 0; + if (fnv > get_current_feature_ver()) + rejectver = TRUE; + else + flags.suppress_alert = fnv; + if (rejectver) { + if (!initial) + You_cant("disable new feature alerts for future versions."); + else { + Sprintf(buf, + "\n%s=%s Invalid reference to a future version ignored", + optn, op); + badoption(buf); + } + return 0; + } + if (!initial) { + Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ, + FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH); + pline("Feature change alerts disabled for NetHack %s features and prior.", + buf); + } + return 1; +} + +void +set_duplicate_opt_detection(on_or_off) +int on_or_off; +{ + int k, *optptr; + if (on_or_off != 0) { + /*-- ON --*/ + if (iflags.opt_booldup) + impossible("iflags.opt_booldup already on (memory leak)"); + iflags.opt_booldup = (int *)alloc(SIZE(boolopt) * sizeof(int)); + optptr = iflags.opt_booldup; + for (k = 0; k < SIZE(boolopt); ++k) + *optptr++ = 0; + + if (iflags.opt_compdup) + impossible("iflags.opt_compdup already on (memory leak)"); + iflags.opt_compdup = (int *)alloc(SIZE(compopt) * sizeof(int)); + optptr = iflags.opt_compdup; + for (k = 0; k < SIZE(compopt); ++k) + *optptr++ = 0; + } else { + /*-- OFF --*/ + if (iflags.opt_booldup) free((genericptr_t) iflags.opt_booldup); + iflags.opt_booldup = (int *)0; + if (iflags.opt_compdup) free((genericptr_t) iflags.opt_compdup); + iflags.opt_compdup = (int *)0; + } +} + +STATIC_OVL void +duplicate_opt_detection(opts, bool_or_comp) +const char *opts; +int bool_or_comp; /* 0 == boolean option, 1 == compound */ +{ + int i, *optptr; +#if defined(MAC) + /* the Mac has trouble dealing with the output of messages while + * processing the config file. That should get fixed one day. + * For now just return. + */ + return; +#endif + if ((bool_or_comp == 0) && iflags.opt_booldup && initial && from_file) { + for (i = 0; boolopt[i].name; i++) { + if (match_optname(opts, boolopt[i].name, 3, FALSE)) { + optptr = iflags.opt_booldup + i; + if (*optptr == 1) { + raw_printf( + "\nWarning - Boolean option specified multiple times: %s.\n", + opts); + wait_synch(); + } + *optptr += 1; + break; /* don't match multiple options */ + } + } + } else if ((bool_or_comp == 1) && iflags.opt_compdup && initial && from_file) { + for (i = 0; compopt[i].name; i++) { + if (match_optname(opts, compopt[i].name, strlen(compopt[i].name), TRUE)) { + optptr = iflags.opt_compdup + i; + if (*optptr == 1) { + raw_printf( + "\nWarning - compound option specified multiple times: %s.\n", + compopt[i].name); + wait_synch(); + } + *optptr += 1; + break; /* don't match multiple options */ + } + } + } +} + +void +parseoptions(opts, tinitial, tfrom_file) +register char *opts; +boolean tinitial, tfrom_file; +{ + register char *op; + unsigned num; + boolean negated; + int i; + const char *fullname; + + initial = tinitial; + from_file = tfrom_file; + if ((op = index(opts, ',')) != 0) { + *op++ = 0; + parseoptions(op, initial, from_file); + } + if (strlen(opts) > BUFSZ/2) { + badoption("option too long"); + return; + } + + /* strip leading and trailing white space */ + while (isspace(*opts)) opts++; + op = eos(opts); + while (--op >= opts && isspace(*op)) *op = '\0'; + + if (!*opts) return; + negated = FALSE; + while ((*opts == '!') || !strncmpi(opts, "no", 2)) { + if (*opts == '!') opts++; else opts += 2; + negated = !negated; + } + + /* variant spelling */ + + if (match_optname(opts, "colour", 5, FALSE)) + Strcpy(opts, "color"); /* fortunately this isn't longer */ + + if (!match_optname(opts, "subkeyvalue", 11, TRUE)) /* allow multiple */ + duplicate_opt_detection(opts, 1); /* 1 means compound opts */ + + /* special boolean options */ + + if (match_optname(opts, "female", 3, FALSE)) { + if(!initial && flags.female == negated) + pline("That is not anatomically possible."); + else + flags.initgend = flags.female = !negated; + return; + } + + if (match_optname(opts, "male", 4, FALSE)) { + if(!initial && flags.female != negated) + pline("That is not anatomically possible."); + else + flags.initgend = flags.female = negated; + return; + } + +#if defined(MICRO) && !defined(AMIGA) + /* included for compatibility with old NetHack.cnf files */ + if (match_optname(opts, "IBM_", 4, FALSE)) { + iflags.BIOS = !negated; + return; + } +#endif /* MICRO */ + + /* compound options */ + + fullname = "pettype"; + if (match_optname(opts, fullname, 3, TRUE)) { + if ((op = string_for_env_opt(fullname, opts, negated)) != 0) { + if (negated) bad_negation(fullname, TRUE); + else switch (*op) { + case 'd': /* dog */ + case 'D': + preferred_pet = 'd'; + break; + case 'c': /* cat */ + case 'C': + case 'f': /* feline */ + case 'F': + preferred_pet = 'c'; + break; + case 'n': /* no pet */ + case 'N': + preferred_pet = 'n'; + break; + default: + pline("Unrecognized pet type '%s'.", op); + break; + } + } else if (negated) preferred_pet = 'n'; + return; + } + + fullname = "catname"; + if (match_optname(opts, fullname, 3, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) + nmcpy(catname, op, PL_PSIZ); + return; + } + + fullname = "dogname"; + if (match_optname(opts, fullname, 3, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) + nmcpy(dogname, op, PL_PSIZ); + return; + } + + fullname = "horsename"; + if (match_optname(opts, fullname, 5, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) + nmcpy(horsename, op, PL_PSIZ); + return; + } + + fullname = "number_pad"; + if (match_optname(opts, fullname, 10, TRUE)) { + boolean compat = (strlen(opts) <= 10); + number_pad(iflags.num_pad ? 1 : 0); + op = string_for_opt(opts, (compat || !initial)); + if (!op) { + if (compat || negated || initial) { + /* for backwards compatibility, "number_pad" without a + value is a synonym for number_pad:1 */ + iflags.num_pad = !negated; + if (iflags.num_pad) iflags.num_pad_mode = 0; + } + return; + } + if (negated) { + bad_negation("number_pad", TRUE); + return; + } + if (*op == '1' || *op == '2') { + iflags.num_pad = 1; + if (*op == '2') iflags.num_pad_mode = 1; + else iflags.num_pad_mode = 0; + } else if (*op == '0') { + iflags.num_pad = 0; + iflags.num_pad_mode = 0; + } else badoption(opts); + return; + } + + fullname = "runmode"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) { + iflags.runmode = RUN_TPORT; + } else if ((op = string_for_opt(opts, FALSE)) != 0) { + if (!strncmpi(op, "teleport", strlen(op))) + iflags.runmode = RUN_TPORT; + else if (!strncmpi(op, "run", strlen(op))) + iflags.runmode = RUN_LEAP; + else if (!strncmpi(op, "walk", strlen(op))) + iflags.runmode = RUN_STEP; + else if (!strncmpi(op, "crawl", strlen(op))) + iflags.runmode = RUN_CRAWL; + else + badoption(opts); + } + return; + } + + fullname = "msghistory"; + if (match_optname(opts, fullname, 3, TRUE)) { + op = string_for_env_opt(fullname, opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.msg_history = negated ? 0 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + + fullname="msg_window"; + /* msg_window:single, combo, full or reversed */ + if (match_optname(opts, fullname, 4, TRUE)) { + /* allow option to be silently ignored by non-tty ports */ +#ifdef TTY_GRAPHICS + int tmp; + if (!(op = string_for_opt(opts, TRUE))) { + tmp = negated ? 's' : 'f'; + } else { + if (negated) { + bad_negation(fullname, TRUE); + return; + } + tmp = tolower(*op); + } + switch (tmp) { + case 's': /* single message history cycle (default if negated) */ + iflags.prevmsg_window = 's'; + break; + case 'c': /* combination: two singles, then full page reversed */ + iflags.prevmsg_window = 'c'; + break; + case 'f': /* full page (default if no opts) */ + iflags.prevmsg_window = 'f'; + break; + case 'r': /* full page (reversed) */ + iflags.prevmsg_window = 'r'; + break; + default: + badoption(opts); + } +#endif + return; + } + + /* WINCAP + * setting font options */ + fullname = "font"; + if (!strncmpi(opts, fullname, 4)) + { + int wintype = -1; + char *fontopts = opts + 4; + + if (!strncmpi(fontopts, "map", 3) || + !strncmpi(fontopts, "_map", 4)) + wintype = NHW_MAP; + else if (!strncmpi(fontopts, "message", 7) || + !strncmpi(fontopts, "_message", 8)) + wintype = NHW_MESSAGE; + else if (!strncmpi(fontopts, "text", 4) || + !strncmpi(fontopts, "_text", 5)) + wintype = NHW_TEXT; + else if (!strncmpi(fontopts, "menu", 4) || + !strncmpi(fontopts, "_menu", 5)) + wintype = NHW_MENU; + else if (!strncmpi(fontopts, "status", 6) || + !strncmpi(fontopts, "_status", 7)) + wintype = NHW_STATUS; + else if (!strncmpi(fontopts, "_size", 5)) { + if (!strncmpi(fontopts, "_size_map", 8)) + wintype = NHW_MAP; + else if (!strncmpi(fontopts, "_size_message", 12)) + wintype = NHW_MESSAGE; + else if (!strncmpi(fontopts, "_size_text", 9)) + wintype = NHW_TEXT; + else if (!strncmpi(fontopts, "_size_menu", 9)) + wintype = NHW_MENU; + else if (!strncmpi(fontopts, "_size_status", 11)) + wintype = NHW_STATUS; + else { + badoption(opts); + return; + } + if (wintype > 0 && !negated && + (op = string_for_opt(opts, FALSE)) != 0) { + switch(wintype) { + case NHW_MAP: + iflags.wc_fontsiz_map = atoi(op); + break; + case NHW_MESSAGE: + iflags.wc_fontsiz_message = atoi(op); + break; + case NHW_TEXT: + iflags.wc_fontsiz_text = atoi(op); + break; + case NHW_MENU: + iflags.wc_fontsiz_menu = atoi(op); + break; + case NHW_STATUS: + iflags.wc_fontsiz_status = atoi(op); + break; + } + } + return; + } else { + badoption(opts); + } + if (wintype > 0 && + (op = string_for_opt(opts, FALSE)) != 0) { + wc_set_font_name(wintype, op); +#ifdef MAC + set_font_name (wintype, op); +#endif + return; + } else if (negated) bad_negation(fullname, TRUE); + return; + } +#ifdef CHANGE_COLOR + if (match_optname(opts, "palette", 3, TRUE) +# ifdef MAC + || match_optname(opts, "hicolor", 3, TRUE) +# endif + ) { + int color_number, color_incr; + +# ifdef MAC + if (match_optname(opts, "hicolor", 3, TRUE)) { + if (negated) { + bad_negation("hicolor", FALSE); + return; + } + color_number = CLR_MAX + 4; /* HARDCODED inverse number */ + color_incr = -1; + } else { +# endif + if (negated) { + bad_negation("palette", FALSE); + return; + } + color_number = 0; + color_incr = 1; +# ifdef MAC + } +# endif + if ((op = string_for_opt(opts, FALSE)) != (char *)0) { + char *pt = op; + int cnt, tmp, reverse; + long rgb; + + while (*pt && color_number >= 0) { + cnt = 3; + rgb = 0L; + if (*pt == '-') { + reverse = 1; + pt++; + } else { + reverse = 0; + } + while (cnt-- > 0) { + if (*pt && *pt != '/') { +# ifdef AMIGA + rgb <<= 4; +# else + rgb <<= 8; +# endif + tmp = *(pt++); + if (isalpha(tmp)) { + tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */ + } else { + tmp &= 0xf; /* Digits in ASCII too... */ + } +# ifndef AMIGA + /* Add an extra so we fill f -> ff and 0 -> 00 */ + rgb += tmp << 4; +# endif + rgb += tmp; + } + } + if (*pt == '/') { + pt++; + } + change_color(color_number, rgb, reverse); + color_number += color_incr; + } + } + if (!initial) { + need_redraw = TRUE; + } + return; + } +#endif /* CHANGE_COLOR */ + + if (match_optname(opts, "fruit", 2, TRUE)) { + char empty_str = '\0'; + op = string_for_opt(opts, negated); + if (negated) { + if (op) { + bad_negation("fruit", TRUE); + return; + } + op = &empty_str; + goto goodfruit; + } + if (!op) return; + if (!initial) { + struct fruit *f; + + num = 0; + for(f=ffruit; f; f=f->nextf) { + if (!strcmp(op, f->fname)) goto goodfruit; + num++; + } + if (num >= 100) { + pline("Doing that so many times isn't very fruitful."); + return; + } + } +goodfruit: + nmcpy(pl_fruit, op, PL_FSIZ); + /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */ + if (!*pl_fruit) + nmcpy(pl_fruit, "slime mold", PL_FSIZ); + if (!initial) + (void)fruitadd(pl_fruit); + /* If initial, then initoptions is allowed to do it instead + * of here (initoptions always has to do it even if there's + * no fruit option at all. Also, we don't want people + * setting multiple fruits in their options.) + */ + return; + } + + /* graphics:string */ + fullname = "graphics"; + if (match_optname(opts, fullname, 2, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else graphics_opts(opts, fullname, MAXPCHARS, 0); + return; + } + fullname = "dungeon"; + if (match_optname(opts, fullname, 2, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else graphics_opts(opts, fullname, MAXDCHARS, 0); + return; + } + fullname = "traps"; + if (match_optname(opts, fullname, 2, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS); + return; + } + fullname = "effects"; + if (match_optname(opts, fullname, 2, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else + graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS); + return; + } + + /* objects:string */ + fullname = "objects"; + if (match_optname(opts, fullname, 7, TRUE)) { + int length; + + if (negated) { + bad_negation(fullname, FALSE); + return; + } + if (!(opts = string_for_env_opt(fullname, opts, FALSE))) + return; + escapes(opts, opts); + + /* + * Override the default object class symbols. The first + * object in the object class is the "random object". I + * don't want to use 0 as an object class, so the "random + * object" is basically a place holder. + * + * The object class symbols have already been initialized in + * initoptions(). + */ + length = strlen(opts); + if (length >= MAXOCLASSES) + length = MAXOCLASSES-1; /* don't count RANDOM_OBJECT */ + + for (i = 0; i < length; i++) + oc_syms[i+1] = (uchar) opts[i]; + return; + } + + /* monsters:string */ + fullname = "monsters"; + if (match_optname(opts, fullname, 8, TRUE)) { + int length; + + if (negated) { + bad_negation(fullname, FALSE); + return; + } + if (!(opts = string_for_env_opt(fullname, opts, FALSE))) + return; + escapes(opts, opts); + + /* Override default mon class symbols set in initoptions(). */ + length = strlen(opts); + if (length >= MAXMCLASSES) + length = MAXMCLASSES-1; /* mon class 0 unused */ + + for (i = 0; i < length; i++) + monsyms[i+1] = (uchar) opts[i]; + return; + } + fullname = "warnings"; + if (match_optname(opts, fullname, 5, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else warning_opts(opts, fullname); + return; + } + /* boulder:symbol */ + fullname = "boulder"; + if (match_optname(opts, fullname, 7, TRUE)) { + int clash = 0; + if (negated) { + bad_negation(fullname, FALSE); + return; + } +/* if (!(opts = string_for_env_opt(fullname, opts, FALSE))) */ + if (!(opts = string_for_opt(opts, FALSE))) + return; + escapes(opts, opts); + if (def_char_to_monclass(opts[0]) != MAXMCLASSES) + clash = 1; + else if (opts[0] >= '1' && opts[0] <= '5') + clash = 2; + if (clash) { + /* symbol chosen matches a used monster or warning + symbol which is not good - reject it*/ + pline( + "Badoption - boulder symbol '%c' conflicts with a %s symbol.", + opts[0], (clash == 1) ? "monster" : "warning"); + } else { + /* + * Override the default boulder symbol. + */ + iflags.bouldersym = (uchar) opts[0]; + } + if (!initial) need_redraw = TRUE; + return; + } + + /* name:string */ + fullname = "name"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) + nmcpy(plname, op, PL_NSIZ); + return; + } + + /* role:string or character:string */ + fullname = "role"; + if (match_optname(opts, fullname, 4, TRUE) || + match_optname(opts, (fullname = "character"), 4, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + if ((flags.initrole = str2role(op)) == ROLE_NONE) + badoption(opts); + else /* Backwards compatibility */ + nmcpy(pl_character, op, PL_NSIZ); + } + return; + } + + /* race:string */ + fullname = "race"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + if ((flags.initrace = str2race(op)) == ROLE_NONE) + badoption(opts); + else /* Backwards compatibility */ + pl_race = *op; + } + return; + } + + /* gender:string */ + fullname = "gender"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + if ((flags.initgend = str2gend(op)) == ROLE_NONE) + badoption(opts); + else + flags.female = flags.initgend; + } + return; + } + + /* altkeyhandler:string */ + fullname = "altkeyhandler"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_opt(opts, negated))) { +#ifdef WIN32CON + (void)strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5); + load_keyboard_handler(); +#endif + } + return; + } + + /* WINCAP + * align_status:[left|top|right|bottom] */ + fullname = "align_status"; + if (match_optname(opts, fullname, sizeof("align_status")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (op && !negated) { + if (!strncmpi (op, "left", sizeof("left")-1)) + iflags.wc_align_status = ALIGN_LEFT; + else if (!strncmpi (op, "top", sizeof("top")-1)) + iflags.wc_align_status = ALIGN_TOP; + else if (!strncmpi (op, "right", sizeof("right")-1)) + iflags.wc_align_status = ALIGN_RIGHT; + else if (!strncmpi (op, "bottom", sizeof("bottom")-1)) + iflags.wc_align_status = ALIGN_BOTTOM; + else + badoption(opts); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* WINCAP + * align_message:[left|top|right|bottom] */ + fullname = "align_message"; + if (match_optname(opts, fullname, sizeof("align_message")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (op && !negated) { + if (!strncmpi (op, "left", sizeof("left")-1)) + iflags.wc_align_message = ALIGN_LEFT; + else if (!strncmpi (op, "top", sizeof("top")-1)) + iflags.wc_align_message = ALIGN_TOP; + else if (!strncmpi (op, "right", sizeof("right")-1)) + iflags.wc_align_message = ALIGN_RIGHT; + else if (!strncmpi (op, "bottom", sizeof("bottom")-1)) + iflags.wc_align_message = ALIGN_BOTTOM; + else + badoption(opts); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* align:string */ + fullname = "align"; + if (match_optname(opts, fullname, sizeof("align")-1, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) + if ((flags.initalign = str2align(op)) == ROLE_NONE) + badoption(opts); + return; + } + + /* the order to list the pack */ + fullname = "packorder"; + if (match_optname(opts, fullname, 4, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } else if (!(op = string_for_opt(opts, FALSE))) return; + + if (!change_inv_order(op)) + badoption(opts); + return; + } + + /* maximum burden picked up before prompt (Warren Cheung) */ + fullname = "pickup_burden"; + if (match_optname(opts, fullname, 8, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + switch (tolower(*op)) { + /* Unencumbered */ + case 'u': + flags.pickup_burden = UNENCUMBERED; + break; + /* Burdened (slight encumbrance) */ + case 'b': + flags.pickup_burden = SLT_ENCUMBER; + break; + /* streSsed (moderate encumbrance) */ + case 's': + flags.pickup_burden = MOD_ENCUMBER; + break; + /* straiNed (heavy encumbrance) */ + case 'n': + flags.pickup_burden = HVY_ENCUMBER; + break; + /* OverTaxed (extreme encumbrance) */ + case 'o': + case 't': + flags.pickup_burden = EXT_ENCUMBER; + break; + /* overLoaded */ + case 'l': + flags.pickup_burden = OVERLOADED; + break; + default: + badoption(opts); + } + } + return; + } + + /* types of objects to pick up automatically */ + if (match_optname(opts, "pickup_types", 8, TRUE)) { + char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], + qbuf[QBUFSZ], abuf[BUFSZ]; + int oc_sym; + boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu; + + oc_to_str(flags.pickup_types, tbuf); + flags.pickup_types[0] = '\0'; /* all */ + op = string_for_opt(opts, (compat || !initial)); + if (!op) { + if (compat || negated || initial) { + /* for backwards compatibility, "pickup" without a + value is a synonym for autopickup of all types + (and during initialization, we can't prompt yet) */ + flags.pickup = !negated; + return; + } + oc_to_str(flags.inv_order, ocl); + use_menu = TRUE; + if (flags.menu_style == MENU_TRADITIONAL || + flags.menu_style == MENU_COMBINATION) { + use_menu = FALSE; + Sprintf(qbuf, "New pickup_types: [%s am] (%s)", + ocl, *tbuf ? tbuf : "all"); + getlin(qbuf, abuf); + op = mungspaces(abuf); + if (abuf[0] == '\0' || abuf[0] == '\033') + op = tbuf; /* restore */ + else if (abuf[0] == 'm') + use_menu = TRUE; + } + if (use_menu) { + (void) choose_classes_menu("Auto-Pickup what?", 1, + TRUE, ocl, tbuf); + op = tbuf; + } + } + if (negated) { + bad_negation("pickup_types", TRUE); + return; + } + while (*op == ' ') op++; + if (*op != 'a' && *op != 'A') { + num = 0; + while (*op) { + oc_sym = def_char_to_objclass(*op); + /* make sure all are valid obj symbols occuring once */ + if (oc_sym != MAXOCLASSES && + !index(flags.pickup_types, oc_sym)) { + flags.pickup_types[num] = (char)oc_sym; + flags.pickup_types[++num] = '\0'; + } else + badopt = TRUE; + op++; + } + if (badopt) badoption(opts); + } + return; + } + /* WINCAP + * player_selection: dialog | prompts */ + fullname = "player_selection"; + if (match_optname(opts, fullname, sizeof("player_selection")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (op && !negated) { + if (!strncmpi (op, "dialog", sizeof("dialog")-1)) + iflags.wc_player_selection = VIA_DIALOG; + else if (!strncmpi (op, "prompt", sizeof("prompt")-1)) + iflags.wc_player_selection = VIA_PROMPTS; + else + badoption(opts); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + + /* things to disclose at end of game */ + if (match_optname(opts, "disclose", 7, TRUE)) { + /* + * The order that the end_disclore options are stored: + * inventory, attribs, vanquished, genocided, conduct + * There is an array in flags: + * end_disclose[NUM_DISCLOSURE_OPT]; + * with option settings for the each of the following: + * iagvc [see disclosure_options in decl.c]: + * Legal setting values in that array are: + * DISCLOSE_PROMPT_DEFAULT_YES ask with default answer yes + * DISCLOSE_PROMPT_DEFAULT_NO ask with default answer no + * DISCLOSE_YES_WITHOUT_PROMPT always disclose and don't ask + * DISCLOSE_NO_WITHOUT_PROMPT never disclose and don't ask + * + * Those setting values can be used in the option + * string as a prefix to get the desired behaviour. + * + * For backward compatibility, no prefix is required, + * and the presence of a i,a,g,v, or c without a prefix + * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT. + */ + boolean badopt = FALSE; + int idx, prefix_val; + + op = string_for_opt(opts, TRUE); + if (op && negated) { + bad_negation("disclose", TRUE); + return; + } + /* "disclose" without a value means "all with prompting" + and negated means "none without prompting" */ + if (!op || !strcmpi(op, "all") || !strcmpi(op, "none")) { + if (op && !strcmpi(op, "none")) negated = TRUE; + for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++) + flags.end_disclose[num] = negated ? + DISCLOSE_NO_WITHOUT_PROMPT : + DISCLOSE_PROMPT_DEFAULT_YES; + return; + } + + num = 0; + prefix_val = -1; + while (*op && num < sizeof flags.end_disclose - 1) { + register char c, *dop; + static char valid_settings[] = { + DISCLOSE_PROMPT_DEFAULT_YES, + DISCLOSE_PROMPT_DEFAULT_NO, + DISCLOSE_YES_WITHOUT_PROMPT, + DISCLOSE_NO_WITHOUT_PROMPT, + '\0' + }; + c = lowc(*op); + if (c == 'k') c = 'v'; /* killed -> vanquished */ + dop = index(disclosure_options, c); + if (dop) { + idx = dop - disclosure_options; + if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) { + impossible("bad disclosure index %d %c", + idx, c); + continue; + } + if (prefix_val != -1) { + flags.end_disclose[idx] = prefix_val; + prefix_val = -1; + } else + flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT; + } else if (index(valid_settings, c)) { + prefix_val = c; + } else if (c == ' ') { + /* do nothing */ + } else + badopt = TRUE; + op++; + } + if (badopt) badoption(opts); + return; + } + + /* scores:5t[op] 5a[round] o[wn] */ + if (match_optname(opts, "scores", 4, TRUE)) { + if (negated) { + bad_negation("scores", FALSE); + return; + } + if (!(op = string_for_opt(opts, FALSE))) return; + + while (*op) { + int inum = 1; + + if (digit(*op)) { + inum = atoi(op); + while (digit(*op)) op++; + } else if (*op == '!') { + negated = !negated; + op++; + } + while (*op == ' ') op++; + + switch (*op) { + case 't': + case 'T': flags.end_top = inum; + break; + case 'a': + case 'A': flags.end_around = inum; + break; + case 'o': + case 'O': flags.end_own = !negated; + break; + default: badoption(opts); + return; + } + while (letter(*++op) || *op == ' ') continue; + if (*op == '/') op++; + } + return; + } + + fullname = "suppress_alert"; + if (match_optname(opts, fullname, 4, TRUE)) { + op = string_for_opt(opts, negated); + if (negated) bad_negation(fullname, FALSE); + else if (op) (void) feature_alert_opts(op,fullname); + return; + } + +#ifdef VIDEOSHADES + /* videocolors:string */ + fullname = "videocolors"; + if (match_optname(opts, fullname, 6, TRUE) || + match_optname(opts, "videocolours", 10, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } + else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { + return; + } + if (!assign_videocolors(opts)) + badoption(opts); + return; + } + /* videoshades:string */ + fullname = "videoshades"; + if (match_optname(opts, fullname, 6, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } + else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { + return; + } + if (!assign_videoshades(opts)) + badoption(opts); + return; + } +#endif /* VIDEOSHADES */ +#ifdef MSDOS +# ifdef NO_TERMS + /* video:string -- must be after longer tests */ + fullname = "video"; + if (match_optname(opts, fullname, 5, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } + else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { + return; + } + if (!assign_video(opts)) + badoption(opts); + return; + } +# endif /* NO_TERMS */ + /* soundcard:string -- careful not to match boolean 'sound' */ + fullname = "soundcard"; + if (match_optname(opts, fullname, 6, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } + else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { + return; + } + if (!assign_soundcard(opts)) + badoption(opts); + return; + } +#endif /* MSDOS */ + + /* WINCAP + * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|ascii8x12| + ascii16x12|ascii12x16|ascii10x18|fit_to_screen] */ + fullname = "map_mode"; + if (match_optname(opts, fullname, sizeof("map_mode")-1, TRUE)) { + op = string_for_opt(opts, negated); + if (op && !negated) { + if (!strncmpi (op, "tiles", sizeof("tiles")-1)) + iflags.wc_map_mode = MAP_MODE_TILES; + else if (!strncmpi (op, "ascii4x6", sizeof("ascii4x6")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII4x6; + else if (!strncmpi (op, "ascii6x8", sizeof("ascii6x8")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII6x8; + else if (!strncmpi (op, "ascii8x8", sizeof("ascii8x8")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII8x8; + else if (!strncmpi (op, "ascii16x8", sizeof("ascii16x8")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII16x8; + else if (!strncmpi (op, "ascii7x12", sizeof("ascii7x12")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII7x12; + else if (!strncmpi (op, "ascii8x12", sizeof("ascii8x12")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII8x12; + else if (!strncmpi (op, "ascii16x12", sizeof("ascii16x12")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII16x12; + else if (!strncmpi (op, "ascii12x16", sizeof("ascii12x16")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII12x16; + else if (!strncmpi (op, "ascii10x18", sizeof("ascii10x18")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII10x18; + else if (!strncmpi (op, "fit_to_screen", sizeof("fit_to_screen")-1)) + iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN; + else + badoption(opts); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* WINCAP + * scroll_amount:nn */ + fullname = "scroll_amount"; + if (match_optname(opts, fullname, sizeof("scroll_amount")-1, TRUE)) { + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.wc_scroll_amount = negated ? 1 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* WINCAP + * scroll_margin:nn */ + fullname = "scroll_margin"; + if (match_optname(opts, fullname, sizeof("scroll_margin")-1, TRUE)) { + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.wc_scroll_margin = negated ? 5 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + fullname = "subkeyvalue"; + if (match_optname(opts, fullname, 5, TRUE)) { + if (negated) bad_negation(fullname, FALSE); + else { +#if defined(WIN32CON) + op = string_for_opt(opts, 0); + map_subkeyvalue(op); +#endif + } + return; + } + /* WINCAP + * tile_width:nn */ + fullname = "tile_width"; + if (match_optname(opts, fullname, sizeof("tile_width")-1, TRUE)) { + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.wc_tile_width = negated ? 0 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* WINCAP + * tile_file:name */ + fullname = "tile_file"; + if (match_optname(opts, fullname, sizeof("tile_file")-1, TRUE)) { + if ((op = string_for_opt(opts, FALSE)) != 0) { + if (iflags.wc_tile_file) free(iflags.wc_tile_file); + iflags.wc_tile_file = (char *)alloc(strlen(op) + 1); + Strcpy(iflags.wc_tile_file, op); + } + return; + } + /* WINCAP + * tile_height:nn */ + fullname = "tile_height"; + if (match_optname(opts, fullname, sizeof("tile_height")-1, TRUE)) { + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.wc_tile_height = negated ? 0 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + /* WINCAP + * vary_msgcount:nn */ + fullname = "vary_msgcount"; + if (match_optname(opts, fullname, sizeof("vary_msgcount")-1, TRUE)) { + op = string_for_opt(opts, negated); + if ((negated && !op) || (!negated && op)) { + iflags.wc_vary_msgcount = negated ? 0 : atoi(op); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + fullname = "windowtype"; + if (match_optname(opts, fullname, 3, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { + char buf[WINTYPELEN]; + nmcpy(buf, op, WINTYPELEN); + choose_windows(buf); + } + return; + } + + /* WINCAP + * setting window colors + * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd + */ + fullname = "windowcolors"; + if (match_optname(opts, fullname, 7, TRUE)) { + if ((op = string_for_opt(opts, FALSE)) != 0) { + if (!wc_set_window_colors(op)) + badoption(opts); + } else if (negated) bad_negation(fullname, TRUE); + return; + } + + /* menustyle:traditional or combo or full or partial */ + if (match_optname(opts, "menustyle", 4, TRUE)) { + int tmp; + boolean val_required = (strlen(opts) > 5 && !negated); + + if (!(op = string_for_opt(opts, !val_required))) { + if (val_required) return; /* string_for_opt gave feedback */ + tmp = negated ? 'n' : 'f'; + } else { + tmp = tolower(*op); + } + switch (tmp) { + case 'n': /* none */ + case 't': /* traditional */ + flags.menu_style = MENU_TRADITIONAL; + break; + case 'c': /* combo: trad.class sel+menu */ + flags.menu_style = MENU_COMBINATION; + break; + case 'p': /* partial: no class menu */ + flags.menu_style = MENU_PARTIAL; + break; + case 'f': /* full: class menu + menu */ + flags.menu_style = MENU_FULL; + break; + default: + badoption(opts); + } + return; + } + + fullname = "menu_headings"; + if (match_optname(opts, fullname, 12, TRUE)) { + if (negated) { + bad_negation(fullname, FALSE); + return; + } + else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { + return; + } + if (!strcmpi(opts,"bold")) + iflags.menu_headings = ATR_BOLD; + else if (!strcmpi(opts,"inverse")) + iflags.menu_headings = ATR_INVERSE; + else if (!strcmpi(opts,"underline")) + iflags.menu_headings = ATR_ULINE; + else + badoption(opts); + return; + } + + /* check for menu command mapping */ + for (i = 0; i < NUM_MENU_CMDS; i++) { + fullname = default_menu_cmd_info[i].name; + if (match_optname(opts, fullname, (int)strlen(fullname), TRUE)) { + if (negated) + bad_negation(fullname, FALSE); + else if ((op = string_for_opt(opts, FALSE)) != 0) { + int j; + char c, op_buf[BUFSZ]; + boolean isbad = FALSE; + + escapes(op, op_buf); + c = *op_buf; + + if (c == 0 || c == '\r' || c == '\n' || c == '\033' || + c == ' ' || digit(c) || (letter(c) && c != '@')) + isbad = TRUE; + else /* reject default object class symbols */ + for (j = 1; j < MAXOCLASSES; j++) + if (c == def_oc_syms[i]) { + isbad = TRUE; + break; + } + + if (isbad) + badoption(opts); + else + add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd); + } + return; + } + } + + /* OK, if we still haven't recognized the option, check the boolean + * options list + */ + for (i = 0; boolopt[i].name; i++) { + if (match_optname(opts, boolopt[i].name, 3, FALSE)) { + /* options that don't exist */ + if (!boolopt[i].addr) { + if (!initial && !negated) + pline_The("\"%s\" option is not available.", + boolopt[i].name); + return; + } + /* options that must come from config file */ + if (!initial && (boolopt[i].optflags == SET_IN_FILE)) { + rejectoption(boolopt[i].name); + return; + } + + *(boolopt[i].addr) = !negated; + + duplicate_opt_detection(boolopt[i].name, 0); + +#if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV) + if (FALSE +# ifdef TERMLIB + || (boolopt[i].addr) == &iflags.DECgraphics +# endif +# ifdef ASCIIGRAPH + || (boolopt[i].addr) == &iflags.IBMgraphics +# endif +# ifdef MAC_GRAPHICS_ENV + || (boolopt[i].addr) == &iflags.MACgraphics +# endif + ) { +# ifdef REINCARNATION + if (!initial && Is_rogue_level(&u.uz)) + assign_rogue_graphics(FALSE); +# endif + need_redraw = TRUE; +# ifdef TERMLIB + if ((boolopt[i].addr) == &iflags.DECgraphics) + switch_graphics(iflags.DECgraphics ? + DEC_GRAPHICS : ASCII_GRAPHICS); +# endif +# ifdef ASCIIGRAPH + if ((boolopt[i].addr) == &iflags.IBMgraphics) + switch_graphics(iflags.IBMgraphics ? + IBM_GRAPHICS : ASCII_GRAPHICS); +# endif +# ifdef MAC_GRAPHICS_ENV + if ((boolopt[i].addr) == &iflags.MACgraphics) + switch_graphics(iflags.MACgraphics ? + MAC_GRAPHICS : ASCII_GRAPHICS); +# endif +# ifdef REINCARNATION + if (!initial && Is_rogue_level(&u.uz)) + assign_rogue_graphics(TRUE); +# endif + } +#endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */ + + /* only do processing below if setting with doset() */ + if (initial) return; + + if ((boolopt[i].addr) == &flags.time +#ifdef EXP_ON_BOTL + || (boolopt[i].addr) == &flags.showexp +#endif +#ifdef SCORE_ON_BOTL + || (boolopt[i].addr) == &flags.showscore +#endif + ) + flags.botl = TRUE; + + else if ((boolopt[i].addr) == &flags.invlet_constant) { + if (flags.invlet_constant) reassign(); + } +#ifdef LAN_MAIL + else if ((boolopt[i].addr) == &flags.biff) { + if (flags.biff) lan_mail_init(); + else lan_mail_finish(); + } +#endif + else if ((boolopt[i].addr) == &flags.lit_corridor) { + /* + * All corridor squares seen via night vision or + * candles & lamps change. Update them by calling + * newsym() on them. Don't do this if we are + * initializing the options --- the vision system + * isn't set up yet. + */ + vision_recalc(2); /* shut down vision */ + vision_full_recalc = 1; /* delayed recalc */ + } + else if ((boolopt[i].addr) == &iflags.use_inverse || + (boolopt[i].addr) == &iflags.showrace || + (boolopt[i].addr) == &iflags.hilite_pet) { + need_redraw = TRUE; + } +#ifdef TEXTCOLOR + else if ((boolopt[i].addr) == &iflags.use_color) { + need_redraw = TRUE; +# ifdef TOS + if ((boolopt[i].addr) == &iflags.use_color + && iflags.BIOS) { + if (colors_changed) + restore_colors(); + else + set_colors(); + } +# endif + } +#endif + + return; + } + } + + /* out of valid options */ + badoption(opts); +} + + +static NEARDATA const char *menutype[] = { + "traditional", "combination", "partial", "full" +}; + +static NEARDATA const char *burdentype[] = { + "unencumbered", "burdened", "stressed", + "strained", "overtaxed", "overloaded" +}; + +static NEARDATA const char *runmodes[] = { + "teleport", "run", "walk", "crawl" +}; + +/* + * Convert the given string of object classes to a string of default object + * symbols. + */ +STATIC_OVL void +oc_to_str(src,dest) + char *src, *dest; +{ + int i; + + while ((i = (int) *src++) != 0) { + if (i < 0 || i >= MAXOCLASSES) + impossible("oc_to_str: illegal object class %d", i); + else + *dest++ = def_oc_syms[i]; + } + *dest = '\0'; +} + +/* + * Add the given mapping to the menu command map list. Always keep the + * maps valid C strings. + */ +void +add_menu_cmd_alias(from_ch, to_ch) + char from_ch, to_ch; +{ + if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) + pline("out of menu map space."); + else { + mapped_menu_cmds[n_menu_mapped] = from_ch; + mapped_menu_op[n_menu_mapped] = to_ch; + n_menu_mapped++; + mapped_menu_cmds[n_menu_mapped] = 0; + mapped_menu_op[n_menu_mapped] = 0; + } +} + +/* + * Map the given character to its corresponding menu command. If it + * doesn't match anything, just return the original. + */ +char +map_menu_cmd(ch) + char ch; +{ + char *found = index(mapped_menu_cmds, ch); + if (found) { + int idx = found - mapped_menu_cmds; + ch = mapped_menu_op[idx]; + } + return ch; +} + + +#if defined(MICRO) || defined(MAC) || defined(WIN32) +# define OPTIONS_HEADING "OPTIONS" +#else +# define OPTIONS_HEADING "NETHACKOPTIONS" +#endif + +static char fmtstr_doset_add_menu[] = "%s%-15s [%s] "; +static char fmtstr_doset_add_menu_tab[] = "%s\t[%s]"; + +STATIC_OVL void +doset_add_menu(win, option, indexoffset) + winid win; /* window to add to */ + const char *option; /* option name */ + int indexoffset; /* value to add to index in compopt[], or zero + if option cannot be changed */ +{ + const char *value = "unknown"; /* current value */ + char buf[BUFSZ], buf2[BUFSZ]; + anything any; + int i; + + any.a_void = 0; + if (indexoffset == 0) { + any.a_int = 0; + value = get_compopt_value(option, buf2); + } else { + for (i=0; compopt[i].name; i++) + if (strcmp(option, compopt[i].name) == 0) break; + + if (compopt[i].name) { + any.a_int = i + 1 + indexoffset; + value = get_compopt_value(option, buf2); + } else { + /* We are trying to add an option not found in compopt[]. + This is almost certainly bad, but we'll let it through anyway + (with a zero value, so it can't be selected). */ + any.a_int = 0; + } + } + /* " " replaces "a - " -- assumes menus follow that style */ + if (!iflags.menu_tab_sep) + Sprintf(buf, fmtstr_doset_add_menu, any.a_int ? "" : " ", option, value); + else + Sprintf(buf, fmtstr_doset_add_menu_tab, option, value); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); +} + +/* Changing options via menu by Per Liboriussen */ +int +doset() +{ + char buf[BUFSZ], buf2[BUFSZ]; + int i, pass, boolcount, pick_cnt, pick_idx, opt_indx; + boolean *bool_p; + winid tmpwin; + anything any; + menu_item *pick_list; + int indexoffset, startpass, endpass; + boolean setinitial = FALSE, fromfile = FALSE; + int biggest_name = 0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + + any.a_void = 0; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + "Booleans (selecting will toggle value):", MENU_UNSELECTED); + any.a_int = 0; + /* first list any other non-modifiable booleans, then modifiable ones */ + for (pass = 0; pass <= 1; pass++) + for (i = 0; boolopt[i].name; i++) + if ((bool_p = boolopt[i].addr) != 0 && + ((boolopt[i].optflags == DISP_IN_GAME && pass == 0) || + (boolopt[i].optflags == SET_IN_GAME && pass == 1))) { + if (bool_p == &flags.female) continue; /* obsolete */ +#ifdef WIZARD + if (bool_p == &iflags.sanity_check && !wizard) continue; + if (bool_p == &iflags.menu_tab_sep && !wizard) continue; +#endif + if (is_wc_option(boolopt[i].name) && + !wc_supported(boolopt[i].name)) continue; + if (is_wc2_option(boolopt[i].name) && + !wc2_supported(boolopt[i].name)) continue; + any.a_int = (pass == 0) ? 0 : i + 1; + if (!iflags.menu_tab_sep) + Sprintf(buf, "%s%-13s [%s]", + pass == 0 ? " " : "", + boolopt[i].name, *bool_p ? "true" : "false"); + else + Sprintf(buf, "%s\t[%s]", + boolopt[i].name, *bool_p ? "true" : "false"); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, buf, MENU_UNSELECTED); + } + + boolcount = i; + indexoffset = boolcount; + any.a_void = 0; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + "Compounds (selecting will prompt for new value):", + MENU_UNSELECTED); + + startpass = DISP_IN_GAME; + endpass = SET_IN_GAME; + + /* spin through the options to find the biggest name + and adjust the format string accordingly if needed */ + biggest_name = 0; + for (i = 0; compopt[i].name; i++) + if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass && + strlen(compopt[i].name) > (unsigned) biggest_name) + biggest_name = (int) strlen(compopt[i].name); + if (biggest_name > 30) biggest_name = 30; + if (!iflags.menu_tab_sep) + Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name); + + /* deliberately put `name', `role', `race', `gender' first */ + doset_add_menu(tmpwin, "name", 0); + doset_add_menu(tmpwin, "role", 0); + doset_add_menu(tmpwin, "race", 0); + doset_add_menu(tmpwin, "gender", 0); + + for (pass = startpass; pass <= endpass; pass++) + for (i = 0; compopt[i].name; i++) + if (compopt[i].optflags == pass) { + if (!strcmp(compopt[i].name, "name") || + !strcmp(compopt[i].name, "role") || + !strcmp(compopt[i].name, "race") || + !strcmp(compopt[i].name, "gender")) + continue; + else if (is_wc_option(compopt[i].name) && + !wc_supported(compopt[i].name)) + continue; + else if (is_wc2_option(compopt[i].name) && + !wc2_supported(compopt[i].name)) + continue; + else + doset_add_menu(tmpwin, compopt[i].name, + (pass == DISP_IN_GAME) ? 0 : indexoffset); + } +#ifdef AUTOPICKUP_EXCEPTIONS + any.a_int = -1; + Sprintf(buf, "autopickup exceptions (%d currently set)", + count_ape_maps((int *)0, (int *)0)); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); + +#endif /* AUTOPICKUP_EXCEPTIONS */ +#ifdef PREFIXES_IN_USE + any.a_void = 0; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + "Variable playground locations:", MENU_UNSELECTED); + for (i = 0; i < PREFIX_COUNT; i++) + doset_add_menu(tmpwin, fqn_prefix_names[i], 0); +#endif + end_menu(tmpwin, "Set what options?"); + need_redraw = FALSE; + if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) { + /* + * Walk down the selection list and either invert the booleans + * or prompt for new values. In most cases, call parseoptions() + * to take care of options that require special attention, like + * redraws. + */ + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { + opt_indx = pick_list[pick_idx].item.a_int - 1; +#ifdef AUTOPICKUP_EXCEPTIONS + if (opt_indx == -2) { + special_handling("autopickup_exception", + setinitial, fromfile); + } else +#endif + if (opt_indx < boolcount) { + /* boolean option */ + Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "", + boolopt[opt_indx].name); + parseoptions(buf, setinitial, fromfile); + if (wc_supported(boolopt[opt_indx].name) || + wc2_supported(boolopt[opt_indx].name)) + preference_update(boolopt[opt_indx].name); + } else { + /* compound option */ + opt_indx -= boolcount; + + if (!special_handling(compopt[opt_indx].name, + setinitial, fromfile)) { + Sprintf(buf, "Set %s to what?", compopt[opt_indx].name); + getlin(buf, buf2); + if (buf2[0] == '\033') + continue; + Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2); + /* pass the buck */ + parseoptions(buf, setinitial, fromfile); + } + if (wc_supported(compopt[opt_indx].name) || + wc2_supported(compopt[opt_indx].name)) + preference_update(compopt[opt_indx].name); + } + } + free((genericptr_t)pick_list); + pick_list = (menu_item *)0; + } + + destroy_nhwindow(tmpwin); + if (need_redraw) + (void) doredraw(); + return 0; +} + +STATIC_OVL boolean +special_handling(optname, setinitial, setfromfile) +const char *optname; +boolean setinitial,setfromfile; +{ + winid tmpwin; + anything any; + int i; + char buf[BUFSZ]; + boolean retval = FALSE; + + /* Special handling of menustyle, pickup_burden, pickup_types, + * disclose, runmode, msg_window, menu_headings, and number_pad options. +#ifdef AUTOPICKUP_EXCEPTIONS + * Also takes care of interactive autopickup_exception_handling changes. +#endif + */ + if (!strcmp("menustyle", optname)) { + const char *style_name; + menu_item *style_pick = (menu_item *)0; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(menutype); i++) { + style_name = menutype[i]; + /* note: separate `style_name' variable used + to avoid an optimizer bug in VAX C V2.3 */ + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, + ATR_NONE, style_name, MENU_UNSELECTED); + } + end_menu(tmpwin, "Select menustyle:"); + if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) { + flags.menu_style = style_pick->item.a_int - 1; + free((genericptr_t)style_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } else if (!strcmp("pickup_burden", optname)) { + const char *burden_name, *burden_letters = "ubsntl"; + menu_item *burden_pick = (menu_item *)0; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(burdentype); i++) { + burden_name = burdentype[i]; + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, + ATR_NONE, burden_name, MENU_UNSELECTED); + } + end_menu(tmpwin, "Select encumbrance level:"); + if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) { + flags.pickup_burden = burden_pick->item.a_int - 1; + free((genericptr_t)burden_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } else if (!strcmp("pickup_types", optname)) { + /* parseoptions will prompt for the list of types */ + parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile); + retval = TRUE; + } else if (!strcmp("disclose", optname)) { + int pick_cnt, pick_idx, opt_idx; + menu_item *disclosure_category_pick = (menu_item *)0; + /* + * The order of disclose_names[] + * must correspond to disclosure_options in decl.h + */ + static const char *disclosure_names[] = { + "inventory", "attributes", "vanquished", "genocides", "conduct" + }; + int disc_cat[NUM_DISCLOSURE_OPTIONS]; + const char *disclosure_name; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { + disclosure_name = disclosure_names[i]; + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0, + ATR_NONE, disclosure_name, MENU_UNSELECTED); + disc_cat[i] = 0; + } + end_menu(tmpwin, "Change which disclosure options categories:"); + if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_category_pick)) > 0) { + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { + opt_idx = disclosure_category_pick[pick_idx].item.a_int - 1; + disc_cat[opt_idx] = 1; + } + free((genericptr_t)disclosure_category_pick); + disclosure_category_pick = (menu_item *)0; + } + destroy_nhwindow(tmpwin); + + for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { + if (disc_cat[i]) { + char dbuf[BUFSZ]; + menu_item *disclosure_option_pick = (menu_item *)0; + Sprintf(dbuf, "Disclosure options for %s:", disclosure_names[i]); + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_char = DISCLOSE_NO_WITHOUT_PROMPT; + add_menu(tmpwin, NO_GLYPH, &any, 'a', 0, + ATR_NONE,"Never disclose and don't prompt", MENU_UNSELECTED); + any.a_void = 0; + any.a_char = DISCLOSE_YES_WITHOUT_PROMPT; + add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, + ATR_NONE,"Always disclose and don't prompt", MENU_UNSELECTED); + any.a_void = 0; + any.a_char = DISCLOSE_PROMPT_DEFAULT_NO; + add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, + ATR_NONE,"Prompt and default answer to \"No\"", MENU_UNSELECTED); + any.a_void = 0; + any.a_char = DISCLOSE_PROMPT_DEFAULT_YES; + add_menu(tmpwin, NO_GLYPH, &any, 'd', 0, + ATR_NONE,"Prompt and default answer to \"Yes\"", MENU_UNSELECTED); + end_menu(tmpwin, dbuf); + if (select_menu(tmpwin, PICK_ONE, &disclosure_option_pick) > 0) { + flags.end_disclose[i] = disclosure_option_pick->item.a_char; + free((genericptr_t)disclosure_option_pick); + } + destroy_nhwindow(tmpwin); + } + } + retval = TRUE; + } else if (!strcmp("runmode", optname)) { + const char *mode_name; + menu_item *mode_pick = (menu_item *)0; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(runmodes); i++) { + mode_name = runmodes[i]; + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, + ATR_NONE, mode_name, MENU_UNSELECTED); + } + end_menu(tmpwin, "Select run/travel display mode:"); + if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) { + iflags.runmode = mode_pick->item.a_int - 1; + free((genericptr_t)mode_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } +#ifdef TTY_GRAPHICS + else if (!strcmp("msg_window", optname)) { + /* by Christian W. Cooper */ + menu_item *window_pick = (menu_item *)0; + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_char = 's'; + add_menu(tmpwin, NO_GLYPH, &any, 's', 0, + ATR_NONE, "single", MENU_UNSELECTED); + any.a_char = 'c'; + add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, + ATR_NONE, "combination", MENU_UNSELECTED); + any.a_char = 'f'; + add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, + ATR_NONE, "full", MENU_UNSELECTED); + any.a_char = 'r'; + add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, + ATR_NONE, "reversed", MENU_UNSELECTED); + end_menu(tmpwin, "Select message history display type:"); + if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) { + iflags.prevmsg_window = window_pick->item.a_char; + free((genericptr_t)window_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } +#endif + else if (!strcmp("align_message", optname) || + !strcmp("align_status", optname)) { + menu_item *window_pick = (menu_item *)0; + char abuf[BUFSZ]; + boolean msg = (*(optname+6) == 'm'); + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_int = ALIGN_TOP; + add_menu(tmpwin, NO_GLYPH, &any, 't', 0, + ATR_NONE, "top", MENU_UNSELECTED); + any.a_int = ALIGN_BOTTOM; + add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, + ATR_NONE, "bottom", MENU_UNSELECTED); + any.a_int = ALIGN_LEFT; + add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, + ATR_NONE, "left", MENU_UNSELECTED); + any.a_int = ALIGN_RIGHT; + add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, + ATR_NONE, "right", MENU_UNSELECTED); + Sprintf(abuf, "Select %s window placement relative to the map:", + msg ? "message" : "status"); + end_menu(tmpwin, abuf); + if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) { + if (msg) iflags.wc_align_message = window_pick->item.a_int; + else iflags.wc_align_status = window_pick->item.a_int; + free((genericptr_t)window_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } else if (!strcmp("number_pad", optname)) { + static const char *npchoices[3] = + {"0 (off)", "1 (on)", "2 (on, DOS compatible)"}; + const char *npletters = "abc"; + menu_item *mode_pick = (menu_item *)0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(npchoices); i++) { + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, npletters[i], 0, + ATR_NONE, npchoices[i], MENU_UNSELECTED); + } + end_menu(tmpwin, "Select number_pad mode:"); + if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) { + int mode = mode_pick->item.a_int - 1; + switch(mode) { + case 2: + iflags.num_pad = 1; + iflags.num_pad_mode = 1; + break; + case 1: + iflags.num_pad = 1; + iflags.num_pad_mode = 0; + break; + case 0: + default: + iflags.num_pad = 0; + iflags.num_pad_mode = 0; + } + free((genericptr_t)mode_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; + } else if (!strcmp("menu_headings", optname)) { + static const char *mhchoices[3] = {"bold", "inverse", "underline"}; + const char *npletters = "biu"; + menu_item *mode_pick = (menu_item *)0; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (i = 0; i < SIZE(mhchoices); i++) { + any.a_int = i + 1; + add_menu(tmpwin, NO_GLYPH, &any, npletters[i], 0, + ATR_NONE, mhchoices[i], MENU_UNSELECTED); + } + end_menu(tmpwin, "How to highlight menu headings:"); + if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) { + int mode = mode_pick->item.a_int - 1; + switch(mode) { + case 2: + iflags.menu_headings = ATR_ULINE; + break; + case 0: + iflags.menu_headings = ATR_BOLD; + break; + case 1: + default: + iflags.menu_headings = ATR_INVERSE; + } + free((genericptr_t)mode_pick); + } + destroy_nhwindow(tmpwin); + retval = TRUE; +#ifdef AUTOPICKUP_EXCEPTIONS + } else if (!strcmp("autopickup_exception", optname)) { + boolean retval; + int pick_cnt, pick_idx, opt_idx, pass; + int totalapes = 0, numapes[2] = {0,0}; + menu_item *pick_list = (menu_item *)0; + anything any; + char apebuf[BUFSZ]; + struct autopickup_exception *ape; + static const char *action_titles[] = { + "a", "add new autopickup exception", + "l", "list autopickup exceptions", + "r", "remove existing autopickup exception", + "e", "exit this menu", + }; +ape_again: + opt_idx = 0; + totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]); + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_int = 0; + for (i = 0; i < SIZE(action_titles) ; i += 2) { + any.a_int++; + if (!totalapes && (i >= 2 && i < 6)) continue; + add_menu(tmpwin, NO_GLYPH, &any, *action_titles[i], + 0, ATR_NONE, action_titles[i+1], MENU_UNSELECTED); + } + end_menu(tmpwin, "Do what?"); + if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) { + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { + opt_idx = pick_list[pick_idx].item.a_int - 1; + } + free((genericptr_t)pick_list); + pick_list = (menu_item *)0; + } + destroy_nhwindow(tmpwin); + if (pick_cnt < 1) return FALSE; + + if (opt_idx == 0) { /* add new */ + getlin("What new autopickup exception pattern?", &apebuf[1]); + if (apebuf[1] == '\033') return FALSE; + apebuf[0] = '"'; + Strcat(apebuf,"\""); + add_autopickup_exception(apebuf); + goto ape_again; + } else if (opt_idx == 3) { + retval = TRUE; + } else { /* remove */ + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) { + if (numapes[pass] == 0) continue; + ape = iflags.autopickup_exceptions[pass]; + any.a_void = 0; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + (pass == 0) ? "Never pickup" : "Always pickup", + MENU_UNSELECTED); + for (i = 0; i < numapes[pass] && ape; i++) { + any.a_void = (opt_idx == 1) ? 0 : ape; + Sprintf(apebuf, "\"%s\"", ape->pattern); + add_menu(tmpwin, NO_GLYPH, &any, + 0, 0, ATR_NONE, apebuf, MENU_UNSELECTED); + ape = ape->next; + } + } + Sprintf(apebuf, "%s autopickup exceptions", + (opt_idx == 1) ? "List of" : "Remove which"); + end_menu(tmpwin, apebuf); + pick_cnt = select_menu(tmpwin, + (opt_idx == 1) ? PICK_NONE : PICK_ANY, + &pick_list); + if (pick_cnt > 0) { + for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) + remove_autopickup_exception( + (struct autopickup_exception *)pick_list[pick_idx].item.a_void); + } + free((genericptr_t)pick_list); + pick_list = (menu_item *)0; + destroy_nhwindow(tmpwin); + goto ape_again; + } + retval = TRUE; +#endif /* AUTOPICKUP_EXCEPTIONS */ + } + return retval; +} + +#define rolestring(val,array,field) ((val >= 0) ? array[val].field : \ + (val == ROLE_RANDOM) ? randomrole : none) + +/* This is ugly. We have all the option names in the compopt[] array, + but we need to look at each option individually to get the value. */ +STATIC_OVL const char * +get_compopt_value(optname, buf) +const char *optname; +char *buf; +{ + char ocl[MAXOCLASSES+1]; + static const char none[] = "(none)", randomrole[] = "random", + to_be_done[] = "(to be done)", + defopt[] = "default", + defbrief[] = "def"; + int i; + + buf[0] = '\0'; + if (!strcmp(optname,"align_message")) + Sprintf(buf, "%s", iflags.wc_align_message == ALIGN_TOP ? "top" : + iflags.wc_align_message == ALIGN_LEFT ? "left" : + iflags.wc_align_message == ALIGN_BOTTOM ? "bottom" : + iflags.wc_align_message == ALIGN_RIGHT ? "right" : + defopt); + else if (!strcmp(optname,"align_status")) + Sprintf(buf, "%s", iflags.wc_align_status == ALIGN_TOP ? "top" : + iflags.wc_align_status == ALIGN_LEFT ? "left" : + iflags.wc_align_status == ALIGN_BOTTOM ? "bottom" : + iflags.wc_align_status == ALIGN_RIGHT ? "right" : + defopt); + else if (!strcmp(optname,"align")) + Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj)); +#ifdef WIN32CON + else if (!strcmp(optname,"altkeyhandler")) + Sprintf(buf, "%s", iflags.altkeyhandler[0] ? + iflags.altkeyhandler : "default"); +#endif + else if (!strcmp(optname, "boulder")) + Sprintf(buf, "%c", iflags.bouldersym ? + iflags.bouldersym : oc_syms[(int)objects[BOULDER].oc_class]); + else if (!strcmp(optname, "catname")) + Sprintf(buf, "%s", catname[0] ? catname : none ); + else if (!strcmp(optname, "disclose")) { + for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) { + char topt[2]; + if (i) Strcat(buf," "); + topt[1] = '\0'; + topt[0] = flags.end_disclose[i]; + Strcat(buf, topt); + topt[0] = disclosure_options[i]; + Strcat(buf, topt); + } + } + else if (!strcmp(optname, "dogname")) + Sprintf(buf, "%s", dogname[0] ? dogname : none ); + else if (!strcmp(optname, "dungeon")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "effects")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "font_map")) + Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt); + else if (!strcmp(optname, "font_message")) + Sprintf(buf, "%s", iflags.wc_font_message ? iflags.wc_font_message : defopt); + else if (!strcmp(optname, "font_status")) + Sprintf(buf, "%s", iflags.wc_font_status ? iflags.wc_font_status : defopt); + else if (!strcmp(optname, "font_menu")) + Sprintf(buf, "%s", iflags.wc_font_menu ? iflags.wc_font_menu : defopt); + else if (!strcmp(optname, "font_text")) + Sprintf(buf, "%s", iflags.wc_font_text ? iflags.wc_font_text : defopt); + else if (!strcmp(optname, "font_size_map")) { + if (iflags.wc_fontsiz_map) Sprintf(buf, "%d", iflags.wc_fontsiz_map); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "font_size_message")) { + if (iflags.wc_fontsiz_message) Sprintf(buf, "%d", + iflags.wc_fontsiz_message); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "font_size_status")) { + if (iflags.wc_fontsiz_status) Sprintf(buf, "%d", iflags.wc_fontsiz_status); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "font_size_menu")) { + if (iflags.wc_fontsiz_menu) Sprintf(buf, "%d", iflags.wc_fontsiz_menu); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "font_size_text")) { + if (iflags.wc_fontsiz_text) Sprintf(buf, "%d",iflags.wc_fontsiz_text); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "fruit")) + Sprintf(buf, "%s", pl_fruit); + else if (!strcmp(optname, "gender")) + Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj)); + else if (!strcmp(optname, "horsename")) + Sprintf(buf, "%s", horsename[0] ? horsename : none); + else if (!strcmp(optname, "map_mode")) + Sprintf(buf, "%s", + iflags.wc_map_mode == MAP_MODE_TILES ? "tiles" : + iflags.wc_map_mode == MAP_MODE_ASCII4x6 ? "ascii4x6" : + iflags.wc_map_mode == MAP_MODE_ASCII6x8 ? "ascii6x8" : + iflags.wc_map_mode == MAP_MODE_ASCII8x8 ? "ascii8x8" : + iflags.wc_map_mode == MAP_MODE_ASCII16x8 ? "ascii16x8" : + iflags.wc_map_mode == MAP_MODE_ASCII7x12 ? "ascii7x12" : + iflags.wc_map_mode == MAP_MODE_ASCII8x12 ? "ascii8x12" : + iflags.wc_map_mode == MAP_MODE_ASCII16x12 ? "ascii16x12" : + iflags.wc_map_mode == MAP_MODE_ASCII12x16 ? "ascii12x16" : + iflags.wc_map_mode == MAP_MODE_ASCII10x18 ? "ascii10x18" : + iflags.wc_map_mode == MAP_MODE_ASCII_FIT_TO_SCREEN ? + "fit_to_screen" : defopt); + else if (!strcmp(optname, "menustyle")) + Sprintf(buf, "%s", menutype[(int)flags.menu_style] ); + else if (!strcmp(optname, "menu_deselect_all")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_deselect_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_first_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_invert_all")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_headings")) { + Sprintf(buf, "%s", (iflags.menu_headings == ATR_BOLD) ? + "bold" : (iflags.menu_headings == ATR_INVERSE) ? + "inverse" : (iflags.menu_headings == ATR_ULINE) ? + "underline" : "unknown"); + } + else if (!strcmp(optname, "menu_invert_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_last_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_next_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_previous_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_search")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_select_all")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "menu_select_page")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "monsters")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "msghistory")) + Sprintf(buf, "%u", iflags.msg_history); +#ifdef TTY_GRAPHICS + else if (!strcmp(optname, "msg_window")) + Sprintf(buf, "%s", (iflags.prevmsg_window=='s') ? "single" : + (iflags.prevmsg_window=='c') ? "combination" : + (iflags.prevmsg_window=='f') ? "full" : "reversed"); +#endif + else if (!strcmp(optname, "name")) + Sprintf(buf, "%s", plname); + else if (!strcmp(optname, "number_pad")) + Sprintf(buf, "%s", + (!iflags.num_pad) ? "0=off" : + (iflags.num_pad_mode) ? "2=on, DOS compatible" : "1=on"); + else if (!strcmp(optname, "objects")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "packorder")) { + oc_to_str(flags.inv_order, ocl); + Sprintf(buf, "%s", ocl); + } +#ifdef CHANGE_COLOR + else if (!strcmp(optname, "palette")) + Sprintf(buf, "%s", get_color_string()); +#endif + else if (!strcmp(optname, "pettype")) + Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" : + (preferred_pet == 'd') ? "dog" : + (preferred_pet == 'n') ? "none" : "random"); + else if (!strcmp(optname, "pickup_burden")) + Sprintf(buf, "%s", burdentype[flags.pickup_burden] ); + else if (!strcmp(optname, "pickup_types")) { + oc_to_str(flags.pickup_types, ocl); + Sprintf(buf, "%s", ocl[0] ? ocl : "all" ); + } + else if (!strcmp(optname, "race")) + Sprintf(buf, "%s", rolestring(flags.initrace, races, noun)); + else if (!strcmp(optname, "role")) + Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m)); + else if (!strcmp(optname, "runmode")) + Sprintf(buf, "%s", runmodes[iflags.runmode]); + else if (!strcmp(optname, "scores")) { + Sprintf(buf, "%d top/%d around%s", flags.end_top, + flags.end_around, flags.end_own ? "/own" : ""); + } + else if (!strcmp(optname, "scroll_amount")) { + if (iflags.wc_scroll_amount) Sprintf(buf, "%d",iflags.wc_scroll_amount); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "scroll_margin")) { + if (iflags.wc_scroll_margin) Sprintf(buf, "%d",iflags.wc_scroll_margin); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "player_selection")) + Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog"); +#ifdef MSDOS + else if (!strcmp(optname, "soundcard")) + Sprintf(buf, "%s", to_be_done); +#endif + else if (!strcmp(optname, "suppress_alert")) { + if (flags.suppress_alert == 0L) + Strcpy(buf, none); + else + Sprintf(buf, "%lu.%lu.%lu", + FEATURE_NOTICE_VER_MAJ, + FEATURE_NOTICE_VER_MIN, + FEATURE_NOTICE_VER_PATCH); + } + else if (!strcmp(optname, "tile_file")) + Sprintf(buf, "%s", iflags.wc_tile_file ? iflags.wc_tile_file : defopt); + else if (!strcmp(optname, "tile_height")) { + if (iflags.wc_tile_height) Sprintf(buf, "%d",iflags.wc_tile_height); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "tile_width")) { + if (iflags.wc_tile_width) Sprintf(buf, "%d",iflags.wc_tile_width); + else Strcpy(buf, defopt); + } + else if (!strcmp(optname, "traps")) + Sprintf(buf, "%s", to_be_done); + else if (!strcmp(optname, "vary_msgcount")) { + if (iflags.wc_vary_msgcount) Sprintf(buf, "%d",iflags.wc_vary_msgcount); + else Strcpy(buf, defopt); + } +#ifdef MSDOS + else if (!strcmp(optname, "video")) + Sprintf(buf, "%s", to_be_done); +#endif +#ifdef VIDEOSHADES + else if (!strcmp(optname, "videoshades")) + Sprintf(buf, "%s-%s-%s", shade[0],shade[1],shade[2]); + else if (!strcmp(optname, "videocolors")) + Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d", + ttycolors[CLR_RED], ttycolors[CLR_GREEN], + ttycolors[CLR_BROWN], ttycolors[CLR_BLUE], + ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN], + ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN], + ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE], + ttycolors[CLR_BRIGHT_MAGENTA], + ttycolors[CLR_BRIGHT_CYAN]); +#endif /* VIDEOSHADES */ + else if (!strcmp(optname, "windowtype")) + Sprintf(buf, "%s", windowprocs.name); + else if (!strcmp(optname, "windowcolors")) + Sprintf(buf, "%s/%s %s/%s %s/%s %s/%s", + iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief, + iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief, + iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message : defbrief, + iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message : defbrief, + iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief, + iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief, + iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief, + iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief); +#ifdef PREFIXES_IN_USE + else { + for (i = 0; i < PREFIX_COUNT; ++i) + if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i]) + Sprintf(buf, "%s", fqn_prefix[i]); + } +#endif + + if (buf[0]) return buf; + else return "unknown"; +} + +int +dotogglepickup() +{ + char buf[BUFSZ], ocl[MAXOCLASSES+1]; + + flags.pickup = !flags.pickup; + if (flags.pickup) { + oc_to_str(flags.pickup_types, ocl); + Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all", +#ifdef AUTOPICKUP_EXCEPTIONS + (iflags.autopickup_exceptions[AP_LEAVE] || + iflags.autopickup_exceptions[AP_GRAB]) ? + ((count_ape_maps((int *)0, (int *)0) == 1) ? + ", with one exception" : ", with some exceptions") : +#endif + ""); + } else { + Strcpy(buf, "OFF"); + } + pline("Autopickup: %s.", buf); + return 0; +} + +#ifdef AUTOPICKUP_EXCEPTIONS +int +add_autopickup_exception(mapping) +const char *mapping; +{ + struct autopickup_exception *ape, **apehead; + char text[256], *text2; + int textsize = 0; + boolean grab = FALSE; + + if (sscanf(mapping, "\"%255[^\"]\"", text) == 1) { + text2 = &text[0]; + if (*text2 == '<') { /* force autopickup */ + grab = TRUE; + ++text2; + } else if (*text2 == '>') { /* default - Do not pickup */ + grab = FALSE; + ++text2; + } + textsize = strlen(text2); + apehead = (grab) ? &iflags.autopickup_exceptions[AP_GRAB] : + &iflags.autopickup_exceptions[AP_LEAVE]; + ape = (struct autopickup_exception *) + alloc(sizeof(struct autopickup_exception)); + ape->pattern = (char *) alloc(textsize+1); + Strcpy(ape->pattern, text2); + ape->grab = grab; + if (!*apehead) ape->next = (struct autopickup_exception *)0; + else ape->next = *apehead; + *apehead = ape; + } else { + raw_print("syntax error in AUTOPICKUP_EXCEPTION"); + return 0; + } + return 1; +} + +STATIC_OVL void +remove_autopickup_exception(whichape) +struct autopickup_exception *whichape; +{ + struct autopickup_exception *ape, *prev = 0; + int chain = whichape->grab ? AP_GRAB : AP_LEAVE; + + for (ape = iflags.autopickup_exceptions[chain]; ape;) { + if (ape == whichape) { + struct autopickup_exception *freeape = ape; + ape = ape->next; + if (prev) prev->next = ape; + else iflags.autopickup_exceptions[chain] = ape; + free(freeape->pattern); + free(freeape); + } else { + prev = ape; + ape = ape->next; + } + } +} + +STATIC_OVL int +count_ape_maps(leave, grab) +int *leave, *grab; +{ + struct autopickup_exception *ape; + int pass, totalapes, numapes[2] = {0,0}; + + for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) { + ape = iflags.autopickup_exceptions[pass]; + while(ape) { + ape = ape->next; + numapes[pass]++; + } + } + totalapes = numapes[AP_LEAVE] + numapes[AP_GRAB]; + if (leave) *leave = numapes[AP_LEAVE]; + if (grab) *grab = numapes[AP_GRAB]; + return totalapes; +} + +void +free_autopickup_exceptions() +{ + struct autopickup_exception *ape; + int pass; + + for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) { + while((ape = iflags.autopickup_exceptions[pass]) != 0) { + free(ape->pattern); + iflags.autopickup_exceptions[pass] = ape->next; + free(ape); + } + } +} +#endif /* AUTOPICKUP_EXCEPTIONS */ + +/* data for option_help() */ +static const char *opt_intro[] = { + "", + " NetHack Options Help:", + "", +#define CONFIG_SLOT 3 /* fill in next value at run-time */ + (char *)0, +#if !defined(MICRO) && !defined(MAC) + "or use `NETHACKOPTIONS=\"\"' in your environment", +#endif + "( is a list of options separated by commas)", +#ifdef VMS + "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"", +#endif + "or press \"O\" while playing and use the menu.", + "", + "Boolean options (which can be negated by prefixing them with '!' or \"no\"):", + (char *)0 +}; + +static const char *opt_epilog[] = { + "", + "Some of the options can be set only before the game is started; those", + "items will not be selectable in the 'O' command's menu.", + (char *)0 +}; + +void +option_help() +{ + char buf[BUFSZ], buf2[BUFSZ]; + register int i; + winid datawin; + + datawin = create_nhwindow(NHW_TEXT); + Sprintf(buf, "Set options as OPTIONS= in %s", configfile); + opt_intro[CONFIG_SLOT] = (const char *) buf; + for (i = 0; opt_intro[i]; i++) + putstr(datawin, 0, opt_intro[i]); + + /* Boolean options */ + for (i = 0; boolopt[i].name; i++) { + if (boolopt[i].addr) { +#ifdef WIZARD + if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue; + if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard) continue; +#endif + next_opt(datawin, boolopt[i].name); + } + } + next_opt(datawin, ""); + + /* Compound options */ + putstr(datawin, 0, "Compound options:"); + for (i = 0; compopt[i].name; i++) { + Sprintf(buf2, "`%s'", compopt[i].name); + Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr, + compopt[i+1].name ? ',' : '.'); + putstr(datawin, 0, buf); + } + + for (i = 0; opt_epilog[i]; i++) + putstr(datawin, 0, opt_epilog[i]); + + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); + return; +} + +/* + * prints the next boolean option, on the same line if possible, on a new + * line if not. End with next_opt(""). + */ +void +next_opt(datawin, str) +winid datawin; +const char *str; +{ + static char *buf = 0; + int i; + char *s; + + if (!buf) *(buf = (char *)alloc(BUFSZ)) = '\0'; + + if (!*str) { + s = eos(buf); + if (s > &buf[1] && s[-2] == ',') + Strcpy(s - 2, "."); /* replace last ", " */ + i = COLNO; /* (greater than COLNO - 2) */ + } else { + i = strlen(buf) + strlen(str) + 2; + } + + if (i > COLNO - 2) { /* rule of thumb */ + putstr(datawin, 0, buf); + buf[0] = 0; + } + if (*str) { + Strcat(buf, str); + Strcat(buf, ", "); + } else { + putstr(datawin, 0, str); + free(buf), buf = 0; + } + return; +} + +/* Returns the fid of the fruit type; if that type already exists, it + * returns the fid of that one; if it does not exist, it adds a new fruit + * type to the chain and returns the new one. + */ +int +fruitadd(str) +char *str; +{ + register int i; + register struct fruit *f; + struct fruit *lastf = 0; + int highest_fruit_id = 0; + char buf[PL_FSIZ]; + boolean user_specified = (str == pl_fruit); + /* if not user-specified, then it's a fruit name for a fruit on + * a bones level... + */ + + /* Note: every fruit has an id (spe for fruit objects) of at least + * 1; 0 is an error. + */ + if (user_specified) { + /* disallow naming after other foods (since it'd be impossible + * to tell the difference) + */ + + boolean found = FALSE, numeric = FALSE; + + for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; + i++) { + if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) { + found = TRUE; + break; + } + } + { + char *c; + + c = pl_fruit; + + for(c = pl_fruit; *c >= '0' && *c <= '9'; c++) + ; + if (isspace(*c) || *c == 0) numeric = TRUE; + } + if (found || numeric || + !strncmp(str, "cursed ", 7) || + !strncmp(str, "uncursed ", 9) || + !strncmp(str, "blessed ", 8) || + !strncmp(str, "partly eaten ", 13) || + (!strncmp(str, "tin of ", 7) && + (!strcmp(str+7, "spinach") || + name_to_mon(str+7) >= LOW_PM)) || + !strcmp(str, "empty tin") || + ((!strncmp(eos(str)-7," corpse",7) || + !strncmp(eos(str)-4, " egg",4)) && + name_to_mon(str) >= LOW_PM)) + { + Strcpy(buf, pl_fruit); + Strcpy(pl_fruit, "candied "); + nmcpy(pl_fruit+8, buf, PL_FSIZ-8); + } + } + for(f=ffruit; f; f = f->nextf) { + lastf = f; + if(f->fid > highest_fruit_id) highest_fruit_id = f->fid; + if(!strncmp(str, f->fname, PL_FSIZ)) + goto nonew; + } + /* if adding another fruit would overflow spe, use a random + fruit instead... we've got a lot to choose from. */ + if (highest_fruit_id >= 127) return rnd(127); + highest_fruit_id++; + f = newfruit(); + if (ffruit) lastf->nextf = f; + else ffruit = f; + Strcpy(f->fname, str); + f->fid = highest_fruit_id; + f->nextf = 0; +nonew: + if (user_specified) current_fruit = highest_fruit_id; + return f->fid; +} + +/* + * This is a somewhat generic menu for taking a list of NetHack style + * class choices and presenting them via a description + * rather than the traditional NetHack characters. + * (Benefits users whose first exposure to NetHack is via tiles). + * + * prompt + * The title at the top of the menu. + * + * category: 0 = monster class + * 1 = object class + * + * way + * FALSE = PICK_ONE, TRUE = PICK_ANY + * + * class_list + * a null terminated string containing the list of choices. + * + * class_selection + * a null terminated string containing the selected characters. + * + * Returns number selected. + */ +int +choose_classes_menu(prompt, category, way, class_list, class_select) +const char *prompt; +int category; +boolean way; +char *class_list; +char *class_select; +{ + menu_item *pick_list = (menu_item *)0; + winid win; + anything any; + char buf[BUFSZ]; + int i, n; + int ret; + int next_accelerator, accelerator; + + if (class_list == (char *)0 || class_select == (char *)0) return 0; + accelerator = 0; + next_accelerator = 'a'; + any.a_void = 0; + win = create_nhwindow(NHW_MENU); + start_menu(win); + while (*class_list) { + const char *text; + boolean selected; + + text = (char *)0; + selected = FALSE; + switch (category) { + case 0: + text = monexplain[def_char_to_monclass(*class_list)]; + accelerator = *class_list; + Sprintf(buf, "%s", text); + break; + case 1: + text = objexplain[def_char_to_objclass(*class_list)]; + accelerator = next_accelerator; + Sprintf(buf, "%c %s", *class_list, text); + break; + default: + impossible("choose_classes_menu: invalid category %d", + category); + } + if (way && *class_select) { /* Selections there already */ + if (index(class_select, *class_list)) { + selected = TRUE; + } + } + any.a_int = *class_list; + add_menu(win, NO_GLYPH, &any, accelerator, + category ? *class_list : 0, + ATR_NONE, buf, selected); + ++class_list; + if (category > 0) { + ++next_accelerator; + if (next_accelerator == ('z' + 1)) next_accelerator = 'A'; + if (next_accelerator == ('Z' + 1)) break; + } + } + end_menu(win, prompt); + n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n > 0) { + for (i = 0; i < n; ++i) + *class_select++ = (char)pick_list[i].item.a_int; + free((genericptr_t)pick_list); + ret = n; + } else if (n == -1) { + class_select = eos(class_select); + ret = -1; + } else + ret = 0; + *class_select = '\0'; + return ret; +} + +struct wc_Opt wc_options[] = { + {"ascii_map", WC_ASCII_MAP}, + {"color", WC_COLOR}, + {"eight_bit_tty", WC_EIGHT_BIT_IN}, + {"hilite_pet", WC_HILITE_PET}, + {"popup_dialog", WC_POPUP_DIALOG}, + {"player_selection", WC_PLAYER_SELECTION}, + {"preload_tiles", WC_PRELOAD_TILES}, + {"tiled_map", WC_TILED_MAP}, + {"tile_file", WC_TILE_FILE}, + {"tile_width", WC_TILE_WIDTH}, + {"tile_height", WC_TILE_HEIGHT}, + {"use_inverse", WC_INVERSE}, + {"align_message", WC_ALIGN_MESSAGE}, + {"align_status", WC_ALIGN_STATUS}, + {"font_map", WC_FONT_MAP}, + {"font_menu", WC_FONT_MENU}, + {"font_message",WC_FONT_MESSAGE}, +#if 0 + {"perm_invent",WC_PERM_INVENT}, +#endif + {"font_size_map", WC_FONTSIZ_MAP}, + {"font_size_menu", WC_FONTSIZ_MENU}, + {"font_size_message", WC_FONTSIZ_MESSAGE}, + {"font_size_status", WC_FONTSIZ_STATUS}, + {"font_size_text", WC_FONTSIZ_TEXT}, + {"font_status", WC_FONT_STATUS}, + {"font_text", WC_FONT_TEXT}, + {"map_mode", WC_MAP_MODE}, + {"scroll_amount", WC_SCROLL_AMOUNT}, + {"scroll_margin", WC_SCROLL_MARGIN}, + {"splash_screen", WC_SPLASH_SCREEN}, + {"vary_msgcount",WC_VARY_MSGCOUNT}, + {"windowcolors", WC_WINDOWCOLORS}, + {"mouse_support", WC_MOUSE_SUPPORT}, + {(char *)0, 0L} +}; + +struct wc_Opt wc2_options[] = { + {"fullscreen", WC2_FULLSCREEN}, + {"softkeyboard", WC2_SOFTKEYBOARD}, + {"wraptext", WC2_WRAPTEXT}, + {(char *)0, 0L} +}; + + +/* + * If a port wants to change or ensure that the + * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is + * correct (for controlling its display in the option menu) call + * set_option_mod_status() + * with the second argument of 0,2, or 3 respectively. + */ +void +set_option_mod_status(optnam, status) +const char *optnam; +int status; +{ + int k; + if (status < SET_IN_FILE || status > SET_IN_GAME) { + impossible("set_option_mod_status: status out of range %d.", + status); + return; + } + for (k = 0; boolopt[k].name; k++) { + if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) { + boolopt[k].optflags = status; + return; + } + } + for (k = 0; compopt[k].name; k++) { + if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) { + compopt[k].optflags = status; + return; + } + } +} + +/* + * You can set several wc_options in one call to + * set_wc_option_mod_status() by setting + * the appropriate bits for each option that you + * are setting in the optmask argument + * prior to calling. + * example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN, SET_IN_GAME); + */ +void +set_wc_option_mod_status(optmask, status) +unsigned long optmask; +int status; +{ + int k = 0; + if (status < SET_IN_FILE || status > SET_IN_GAME) { + impossible("set_wc_option_mod_status: status out of range %d.", + status); + return; + } + while (wc_options[k].wc_name) { + if (optmask & wc_options[k].wc_bit) { + set_option_mod_status(wc_options[k].wc_name, status); + } + k++; + } +} + +STATIC_OVL boolean +is_wc_option(optnam) +const char *optnam; +{ + int k = 0; + while (wc_options[k].wc_name) { + if (strcmp(wc_options[k].wc_name, optnam) == 0) + return TRUE; + k++; + } + return FALSE; +} + +STATIC_OVL boolean +wc_supported(optnam) +const char *optnam; +{ + int k = 0; + while (wc_options[k].wc_name) { + if (!strcmp(wc_options[k].wc_name, optnam) && + (windowprocs.wincap & wc_options[k].wc_bit)) + return TRUE; + k++; + } + return FALSE; +} + + +/* + * You can set several wc2_options in one call to + * set_wc2_option_mod_status() by setting + * the appropriate bits for each option that you + * are setting in the optmask argument + * prior to calling. + * example: set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT, SET_IN_FILE); + */ + +void +set_wc2_option_mod_status(optmask, status) +unsigned long optmask; +int status; +{ + int k = 0; + if (status < SET_IN_FILE || status > SET_IN_GAME) { + impossible("set_wc2_option_mod_status: status out of range %d.", + status); + return; + } + while (wc2_options[k].wc_name) { + if (optmask & wc2_options[k].wc_bit) { + set_option_mod_status(wc2_options[k].wc_name, status); + } + k++; + } +} + +STATIC_OVL boolean +is_wc2_option(optnam) +const char *optnam; +{ + int k = 0; + while (wc2_options[k].wc_name) { + if (strcmp(wc2_options[k].wc_name, optnam) == 0) + return TRUE; + k++; + } + return FALSE; +} + +STATIC_OVL boolean +wc2_supported(optnam) +const char *optnam; +{ + int k = 0; + while (wc2_options[k].wc_name) { + if (!strcmp(wc2_options[k].wc_name, optnam) && + (windowprocs.wincap2 & wc2_options[k].wc_bit)) + return TRUE; + k++; + } + return FALSE; +} + + +STATIC_OVL void +wc_set_font_name(wtype, fontname) +int wtype; +char *fontname; +{ + char **fn = (char **)0; + if (!fontname) return; + switch(wtype) { + case NHW_MAP: + fn = &iflags.wc_font_map; + break; + case NHW_MESSAGE: + fn = &iflags.wc_font_message; + break; + case NHW_TEXT: + fn = &iflags.wc_font_text; + break; + case NHW_MENU: + fn = &iflags.wc_font_menu; + break; + case NHW_STATUS: + fn = &iflags.wc_font_status; + break; + default: + return; + } + if (fn) { + if (*fn) free(*fn); + *fn = (char *)alloc(strlen(fontname) + 1); + Strcpy(*fn, fontname); + } + return; +} + +STATIC_OVL int +wc_set_window_colors(op) +char *op; +{ + /* syntax: + * menu white/black message green/yellow status white/blue text white/black + */ + + int j; + char buf[BUFSZ]; + char *wn, *tfg, *tbg, *newop; + static const char *wnames[] = { "menu", "message", "status", "text" }; + static const char *shortnames[] = { "mnu", "msg", "sts", "txt" }; + static char **fgp[] = { + &iflags.wc_foregrnd_menu, + &iflags.wc_foregrnd_message, + &iflags.wc_foregrnd_status, + &iflags.wc_foregrnd_text + }; + static char **bgp[] = { + &iflags.wc_backgrnd_menu, + &iflags.wc_backgrnd_message, + &iflags.wc_backgrnd_status, + &iflags.wc_backgrnd_text + }; + + Strcpy(buf, op); + newop = mungspaces(buf); + while (newop && *newop) { + + wn = tfg = tbg = (char *)0; + + /* until first non-space in case there's leading spaces - before colorname*/ + while(*newop && isspace(*newop)) newop++; + if (*newop) wn = newop; + else return 0; + + /* until first space - colorname*/ + while(*newop && !isspace(*newop)) newop++; + if (*newop) *newop = '\0'; + else return 0; + newop++; + + /* until first non-space - before foreground*/ + while(*newop && isspace(*newop)) newop++; + if (*newop) tfg = newop; + else return 0; + + /* until slash - foreground */ + while(*newop && *newop != '/') newop++; + if (*newop) *newop = '\0'; + else return 0; + newop++; + + /* until first non-space (in case there's leading space after slash) - before background */ + while(*newop && isspace(*newop)) newop++; + if (*newop) tbg = newop; + else return 0; + + /* until first space - background */ + while(*newop && !isspace(*newop)) newop++; + if (*newop) *newop++ = '\0'; + + for (j = 0; j < 4; ++j) { + if (!strcmpi(wn, wnames[j]) || + !strcmpi(wn, shortnames[j])) { + if (tfg && !strstri(tfg, " ")) { + if (*fgp[j]) free(*fgp[j]); + *fgp[j] = (char *)alloc(strlen(tfg) + 1); + Strcpy(*fgp[j], tfg); + } + if (tbg && !strstri(tbg, " ")) { + if (*bgp[j]) free(*bgp[j]); + *bgp[j] = (char *)alloc(strlen(tbg) + 1); + Strcpy(*bgp[j], tbg); + } + break; + } + } + } + return 1; +} + +#endif /* OPTION_LISTS_ONLY */ + +/*options.c*/ diff --git a/src/pager.c b/src/pager.c new file mode 100644 index 0000000..96093f4 --- /dev/null +++ b/src/pager.c @@ -0,0 +1,964 @@ +/* SCCS Id: @(#)pager.c 3.4 2003/08/13 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file contains the command routines dowhatis() and dohelp() and */ +/* a few other help related facilities */ + +#include "hack.h" +#include "dlb.h" + +STATIC_DCL boolean FDECL(is_swallow_sym, (int)); +STATIC_DCL int FDECL(append_str, (char *, const char *)); +STATIC_DCL struct permonst * FDECL(lookat, (int, int, char *, char *)); +STATIC_DCL void FDECL(checkfile, + (char *,struct permonst *,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL int FDECL(do_look, (BOOLEAN_P)); +STATIC_DCL boolean FDECL(help_menu, (int *)); +#ifdef PORT_HELP +extern void NDECL(port_help); +#endif + +/* Returns "true" for characters that could represent a monster's stomach. */ +STATIC_OVL boolean +is_swallow_sym(c) +int c; +{ + int i; + for (i = S_sw_tl; i <= S_sw_br; i++) + if ((int)showsyms[i] == c) return TRUE; + return FALSE; +} + +/* + * Append new_str to the end of buf if new_str doesn't already exist as + * a substring of buf. Return 1 if the string was appended, 0 otherwise. + * It is expected that buf is of size BUFSZ. + */ +STATIC_OVL int +append_str(buf, new_str) + char *buf; + const char *new_str; +{ + int space_left; /* space remaining in buf */ + + if (strstri(buf, new_str)) return 0; + + space_left = BUFSZ - strlen(buf) - 1; + (void) strncat(buf, " or ", space_left); + (void) strncat(buf, new_str, space_left - 4); + return 1; +} + +/* + * Return the name of the glyph found at (x,y). + * If not hallucinating and the glyph is a monster, also monster data. + */ +STATIC_OVL struct permonst * +lookat(x, y, buf, monbuf) + int x, y; + char *buf, *monbuf; +{ + register struct monst *mtmp = (struct monst *) 0; + struct permonst *pm = (struct permonst *) 0; + int glyph; + + buf[0] = monbuf[0] = 0; + glyph = glyph_at(x,y); + if (u.ux == x && u.uy == y && senseself()) { + char race[QBUFSZ]; + + /* if not polymorphed, show both the role and the race */ + race[0] = 0; + if (!Upolyd) { + Sprintf(race, "%s ", urace.adj); + } + + Sprintf(buf, "%s%s%s called %s", + Invis ? "invisible " : "", + race, + mons[u.umonnum].mname, + plname); + /* file lookup can't distinguish between "gnomish wizard" monster + and correspondingly named player character, always picking the + former; force it to find the general "wizard" entry instead */ + if (Role_if(PM_WIZARD) && Race_if(PM_GNOME) && !Upolyd) + pm = &mons[PM_WIZARD]; + +#ifdef STEED + if (u.usteed) { + char steedbuf[BUFSZ]; + + Sprintf(steedbuf, ", mounted on %s", y_monnam(u.usteed)); + /* assert((sizeof buf >= strlen(buf)+strlen(steedbuf)+1); */ + Strcat(buf, steedbuf); + } +#endif + /* When you see yourself normally, no explanation is appended + (even if you could also see yourself via other means). + Sensing self while blind or swallowed is treated as if it + were by normal vision (cf canseeself()). */ + if ((Invisible || u.uundetected) && !Blind && !u.uswallow) { + unsigned how = 0; + + if (Infravision) how |= 1; + if (Unblind_telepat) how |= 2; + if (Detect_monsters) how |= 4; + + if (how) + Sprintf(eos(buf), " [seen: %s%s%s%s%s]", + (how & 1) ? "infravision" : "", + /* add comma if telep and infrav */ + ((how & 3) > 2) ? ", " : "", + (how & 2) ? "telepathy" : "", + /* add comma if detect and (infrav or telep or both) */ + ((how & 7) > 4) ? ", " : "", + (how & 4) ? "monster detection" : ""); + } + } else if (u.uswallow) { + /* all locations when swallowed other than the hero are the monster */ + Sprintf(buf, "interior of %s", + Blind ? "a monster" : a_monnam(u.ustuck)); + pm = u.ustuck->data; + } else if (glyph_is_monster(glyph)) { + bhitpos.x = x; + bhitpos.y = y; + mtmp = m_at(x,y); + if (mtmp != (struct monst *) 0) { + char *name, monnambuf[BUFSZ]; + boolean accurate = !Hallucination; + + if (mtmp->data == &mons[PM_COYOTE] && accurate) + name = coyotename(mtmp, monnambuf); + else + name = distant_monnam(mtmp, ARTICLE_NONE, monnambuf); + + pm = mtmp->data; + Sprintf(buf, "%s%s%s", + (mtmp->mx != x || mtmp->my != y) ? + ((mtmp->isshk && accurate) + ? "tail of " : "tail of a ") : "", + (mtmp->mtame && accurate) ? "tame " : + (mtmp->mpeaceful && accurate) ? "peaceful " : "", + name); + if (u.ustuck == mtmp) + Strcat(buf, (Upolyd && sticks(youmonst.data)) ? + ", being held" : ", holding you"); + if (mtmp->mleashed) + Strcat(buf, ", leashed to you"); + + if (mtmp->mtrapped && cansee(mtmp->mx, mtmp->my)) { + struct trap *t = t_at(mtmp->mx, mtmp->my); + int tt = t ? t->ttyp : NO_TRAP; + + /* newsym lets you know of the trap, so mention it here */ + if (tt == BEAR_TRAP || tt == PIT || + tt == SPIKED_PIT || tt == WEB) + Sprintf(eos(buf), ", trapped in %s", + an(defsyms[trap_to_defsym(tt)].explanation)); + } + + { + int ways_seen = 0, normal = 0, xraydist; + boolean useemon = (boolean) canseemon(mtmp); + + xraydist = (u.xray_range<0) ? -1 : u.xray_range * u.xray_range; + /* normal vision */ + if ((mtmp->wormno ? worm_known(mtmp) : cansee(mtmp->mx, mtmp->my)) && + mon_visible(mtmp) && !mtmp->minvis) { + ways_seen++; + normal++; + } + /* see invisible */ + if (useemon && mtmp->minvis) + ways_seen++; + /* infravision */ + if ((!mtmp->minvis || See_invisible) && see_with_infrared(mtmp)) + ways_seen++; + /* telepathy */ + if (tp_sensemon(mtmp)) + ways_seen++; + /* xray */ + if (useemon && xraydist > 0 && + distu(mtmp->mx, mtmp->my) <= xraydist) + ways_seen++; + if (Detect_monsters) + ways_seen++; + if (MATCH_WARN_OF_MON(mtmp)) + ways_seen++; + + if (ways_seen > 1 || !normal) { + if (normal) { + Strcat(monbuf, "normal vision"); + /* can't actually be 1 yet here */ + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if (useemon && mtmp->minvis) { + Strcat(monbuf, "see invisible"); + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if ((!mtmp->minvis || See_invisible) && + see_with_infrared(mtmp)) { + Strcat(monbuf, "infravision"); + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if (tp_sensemon(mtmp)) { + Strcat(monbuf, "telepathy"); + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if (useemon && xraydist > 0 && + distu(mtmp->mx, mtmp->my) <= xraydist) { + /* Eyes of the Overworld */ + Strcat(monbuf, "astral vision"); + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if (Detect_monsters) { + Strcat(monbuf, "monster detection"); + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + if (MATCH_WARN_OF_MON(mtmp)) { + char wbuf[BUFSZ]; + if (Hallucination) + Strcat(monbuf, "paranoid delusion"); + else { + Sprintf(wbuf, "warned of %s", + makeplural(mtmp->data->mname)); + Strcat(monbuf, wbuf); + } + if (ways_seen-- > 1) Strcat(monbuf, ", "); + } + } + } + } + } + else if (glyph_is_object(glyph)) { + struct obj *otmp = vobj_at(x,y); + + if (!otmp || otmp->otyp != glyph_to_obj(glyph)) { + if (glyph_to_obj(glyph) != STRANGE_OBJECT) { + otmp = mksobj(glyph_to_obj(glyph), FALSE, FALSE); + if (otmp->oclass == COIN_CLASS) + otmp->quan = 2L; /* to force pluralization */ + else if (otmp->otyp == SLIME_MOLD) + otmp->spe = current_fruit; /* give the fruit a type */ + Strcpy(buf, distant_name(otmp, xname)); + dealloc_obj(otmp); + } + } else + Strcpy(buf, distant_name(otmp, xname)); + + if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) + Strcat(buf, " embedded in stone"); + else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) + Strcat(buf, " embedded in a wall"); + else if (closed_door(x,y)) + Strcat(buf, " embedded in a door"); + else if (is_pool(x,y)) + Strcat(buf, " in water"); + else if (is_lava(x,y)) + Strcat(buf, " in molten lava"); /* [can this ever happen?] */ + } else if (glyph_is_trap(glyph)) { + int tnum = what_trap(glyph_to_trap(glyph)); + Strcpy(buf, defsyms[trap_to_defsym(tnum)].explanation); + } else if(!glyph_is_cmap(glyph)) { + Strcpy(buf,"dark part of a room"); + } else switch(glyph_to_cmap(glyph)) { + case S_altar: + if(!In_endgame(&u.uz)) + Sprintf(buf, "%s altar", + align_str(Amask2align(levl[x][y].altarmask & ~AM_SHRINE))); + else Sprintf(buf, "aligned altar"); + break; + case S_ndoor: + if (is_drawbridge_wall(x, y) >= 0) + Strcpy(buf,"open drawbridge portcullis"); + else if ((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN) + Strcpy(buf,"broken door"); + else + Strcpy(buf,"doorway"); + break; + case S_cloud: + Strcpy(buf, Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); + break; + default: + Strcpy(buf,defsyms[glyph_to_cmap(glyph)].explanation); + break; + } + + return ((pm && !Hallucination) ? pm : (struct permonst *) 0); +} + +/* + * Look in the "data" file for more info. Called if the user typed in the + * whole name (user_typed_name == TRUE), or we've found a possible match + * with a character/glyph and flags.help is TRUE. + * + * NOTE: when (user_typed_name == FALSE), inp is considered read-only and + * must not be changed directly, e.g. via lcase(). We want to force + * lcase() for data.base lookup so that we can have a clean key. + * Therefore, we create a copy of inp _just_ for data.base lookup. + */ +STATIC_OVL void +checkfile(inp, pm, user_typed_name, without_asking) + char *inp; + struct permonst *pm; + boolean user_typed_name, without_asking; +{ + dlb *fp; + char buf[BUFSZ], newstr[BUFSZ]; + char *ep, *dbase_str; + long txt_offset; + int chk_skip; + boolean found_in_file = FALSE, skipping_entry = FALSE; + + fp = dlb_fopen(DATAFILE, "r"); + if (!fp) { + pline("Cannot open data file!"); + return; + } + + /* To prevent the need for entries in data.base like *ngel to account + * for Angel and angel, make the lookup string the same for both + * user_typed_name and picked name. + */ + if (pm != (struct permonst *) 0 && !user_typed_name) + dbase_str = strcpy(newstr, pm->mname); + else dbase_str = strcpy(newstr, inp); + (void) lcase(dbase_str); + + if (!strncmp(dbase_str, "interior of ", 12)) + dbase_str += 12; + if (!strncmp(dbase_str, "a ", 2)) + dbase_str += 2; + else if (!strncmp(dbase_str, "an ", 3)) + dbase_str += 3; + else if (!strncmp(dbase_str, "the ", 4)) + dbase_str += 4; + if (!strncmp(dbase_str, "tame ", 5)) + dbase_str += 5; + else if (!strncmp(dbase_str, "peaceful ", 9)) + dbase_str += 9; + if (!strncmp(dbase_str, "invisible ", 10)) + dbase_str += 10; + if (!strncmp(dbase_str, "statue of ", 10)) + dbase_str[6] = '\0'; + else if (!strncmp(dbase_str, "figurine of ", 12)) + dbase_str[8] = '\0'; + + /* Make sure the name is non-empty. */ + if (*dbase_str) { + /* adjust the input to remove "named " and convert to lower case */ + char *alt = 0; /* alternate description */ + + if ((ep = strstri(dbase_str, " named ")) != 0) + alt = ep + 7; + else + ep = strstri(dbase_str, " called "); + if (!ep) ep = strstri(dbase_str, ", "); + if (ep && ep > dbase_str) *ep = '\0'; + + /* + * If the object is named, then the name is the alternate description; + * otherwise, the result of makesingular() applied to the name is. This + * isn't strictly optimal, but named objects of interest to the user + * will usually be found under their name, rather than under their + * object type, so looking for a singular form is pointless. + */ + + if (!alt) + alt = makesingular(dbase_str); + else + if (user_typed_name) + (void) lcase(alt); + + /* skip first record; read second */ + txt_offset = 0L; + if (!dlb_fgets(buf, BUFSZ, fp) || !dlb_fgets(buf, BUFSZ, fp)) { + impossible("can't read 'data' file"); + (void) dlb_fclose(fp); + return; + } else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset <= 0) + goto bad_data_file; + + /* look for the appropriate entry */ + while (dlb_fgets(buf,BUFSZ,fp)) { + if (*buf == '.') break; /* we passed last entry without success */ + + if (digit(*buf)) { + /* a number indicates the end of current entry */ + skipping_entry = FALSE; + } else if (!skipping_entry) { + if (!(ep = index(buf, '\n'))) goto bad_data_file; + *ep = 0; + /* if we match a key that begins with "~", skip this entry */ + chk_skip = (*buf == '~') ? 1 : 0; + if (pmatch(&buf[chk_skip], dbase_str) || + (alt && pmatch(&buf[chk_skip], alt))) { + if (chk_skip) { + skipping_entry = TRUE; + continue; + } else { + found_in_file = TRUE; + break; + } + } + } + } + } + + if(found_in_file) { + long entry_offset; + int entry_count; + int i; + + /* skip over other possible matches for the info */ + do { + if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; + } while (!digit(*buf)); + if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) { +bad_data_file: impossible("'data' file in wrong format"); + (void) dlb_fclose(fp); + return; + } + + if (user_typed_name || without_asking || yn("More info?") == 'y') { + winid datawin; + + if (dlb_fseek(fp, txt_offset + entry_offset, SEEK_SET) < 0) { + pline("? Seek error on 'data' file!"); + (void) dlb_fclose(fp); + return; + } + datawin = create_nhwindow(NHW_MENU); + for (i = 0; i < entry_count; i++) { + if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; + if ((ep = index(buf, '\n')) != 0) *ep = 0; + if (index(buf+1, '\t') != 0) (void) tabexpand(buf+1); + putstr(datawin, 0, buf+1); + } + display_nhwindow(datawin, FALSE); + destroy_nhwindow(datawin); + } + } else if (user_typed_name) + pline("I don't have any information on those things."); + + (void) dlb_fclose(fp); +} + +/* getpos() return values */ +#define LOOK_TRADITIONAL 0 /* '.' -- ask about "more info?" */ +#define LOOK_QUICK 1 /* ',' -- skip "more info?" */ +#define LOOK_ONCE 2 /* ';' -- skip and stop looping */ +#define LOOK_VERBOSE 3 /* ':' -- show more info w/o asking */ + +/* also used by getpos hack in do_name.c */ +const char what_is_an_unknown_object[] = "an unknown object"; + +STATIC_OVL int +do_look(quick) + boolean quick; /* use cursor && don't search for "more info" */ +{ + char out_str[BUFSZ], look_buf[BUFSZ]; + const char *x_str, *firstmatch = 0; + struct permonst *pm = 0; + int i, ans = 0; + int sym; /* typed symbol or converted glyph */ + int found; /* count of matching syms found */ + coord cc; /* screen pos of unknown glyph */ + boolean save_verbose; /* saved value of flags.verbose */ + boolean from_screen; /* question from the screen */ + boolean need_to_look; /* need to get explan. from glyph */ + boolean hit_trap; /* true if found trap explanation */ + int skipped_venom; /* non-zero if we ignored "splash of venom" */ + static const char *mon_interior = "the interior of a monster"; + + if (quick) { + from_screen = TRUE; /* yes, we want to use the cursor */ + } else { + i = ynq("Specify unknown object by cursor?"); + if (i == 'q') return 0; + from_screen = (i == 'y'); + } + + if (from_screen) { + cc.x = u.ux; + cc.y = u.uy; + sym = 0; /* gcc -Wall lint */ + } else { + getlin("Specify what? (type the word)", out_str); + if (out_str[0] == '\0' || out_str[0] == '\033') + return 0; + + if (out_str[1]) { /* user typed in a complete string */ + checkfile(out_str, pm, TRUE, TRUE); + return 0; + } + sym = out_str[0]; + } + + /* Save the verbose flag, we change it later. */ + save_verbose = flags.verbose; + flags.verbose = flags.verbose && !quick; + /* + * The user typed one letter, or we're identifying from the screen. + */ + do { + /* Reset some variables. */ + need_to_look = FALSE; + pm = (struct permonst *)0; + skipped_venom = 0; + found = 0; + out_str[0] = '\0'; + + if (from_screen) { + int glyph; /* glyph at selected position */ + + if (flags.verbose) + pline("Please move the cursor to %s.", + what_is_an_unknown_object); + else + pline("Pick an object."); + + ans = getpos(&cc, quick, what_is_an_unknown_object); + if (ans < 0 || cc.x < 0) { + flags.verbose = save_verbose; + return 0; /* done */ + } + flags.verbose = FALSE; /* only print long question once */ + + /* Convert the glyph at the selected position to a symbol. */ + glyph = glyph_at(cc.x,cc.y); + if (glyph_is_cmap(glyph)) { + sym = showsyms[glyph_to_cmap(glyph)]; + } else if (glyph_is_trap(glyph)) { + sym = showsyms[trap_to_defsym(glyph_to_trap(glyph))]; + } else if (glyph_is_object(glyph)) { + sym = oc_syms[(int)objects[glyph_to_obj(glyph)].oc_class]; + if (sym == '`' && iflags.bouldersym && (int)glyph_to_obj(glyph) == BOULDER) + sym = iflags.bouldersym; + } else if (glyph_is_monster(glyph)) { + /* takes care of pets, detected, ridden, and regular mons */ + sym = monsyms[(int)mons[glyph_to_mon(glyph)].mlet]; + } else if (glyph_is_swallow(glyph)) { + sym = showsyms[glyph_to_swallow(glyph)+S_sw_tl]; + } else if (glyph_is_invisible(glyph)) { + sym = DEF_INVISIBLE; + } else if (glyph_is_warning(glyph)) { + sym = glyph_to_warning(glyph); + sym = warnsyms[sym]; + } else { + impossible("do_look: bad glyph %d at (%d,%d)", + glyph, (int)cc.x, (int)cc.y); + sym = ' '; + } + } + + /* + * Check all the possibilities, saving all explanations in a buffer. + * When all have been checked then the string is printed. + */ + + /* Check for monsters */ + for (i = 0; i < MAXMCLASSES; i++) { + if (sym == (from_screen ? monsyms[i] : def_monsyms[i]) && + monexplain[i]) { + need_to_look = TRUE; + if (!found) { + Sprintf(out_str, "%c %s", sym, an(monexplain[i])); + firstmatch = monexplain[i]; + found++; + } else { + found += append_str(out_str, an(monexplain[i])); + } + } + } + /* handle '@' as a special case if it refers to you and you're + playing a character which isn't normally displayed by that + symbol; firstmatch is assumed to already be set for '@' */ + if ((from_screen ? + (sym == monsyms[S_HUMAN] && cc.x == u.ux && cc.y == u.uy) : + (sym == def_monsyms[S_HUMAN] && !iflags.showrace)) && + !(Race_if(PM_HUMAN) || Race_if(PM_ELF)) && !Upolyd) + found += append_str(out_str, "you"); /* tack on "or you" */ + + /* + * Special case: if identifying from the screen, and we're swallowed, + * and looking at something other than our own symbol, then just say + * "the interior of a monster". + */ + if (u.uswallow && from_screen && is_swallow_sym(sym)) { + if (!found) { + Sprintf(out_str, "%c %s", sym, mon_interior); + firstmatch = mon_interior; + } else { + found += append_str(out_str, mon_interior); + } + need_to_look = TRUE; + } + + /* Now check for objects */ + for (i = 1; i < MAXOCLASSES; i++) { + if (sym == (from_screen ? oc_syms[i] : def_oc_syms[i])) { + need_to_look = TRUE; + if (from_screen && i == VENOM_CLASS) { + skipped_venom++; + continue; + } + if (!found) { + Sprintf(out_str, "%c %s", sym, an(objexplain[i])); + firstmatch = objexplain[i]; + found++; + } else { + found += append_str(out_str, an(objexplain[i])); + } + } + } + + if (sym == DEF_INVISIBLE) { + if (!found) { + Sprintf(out_str, "%c %s", sym, an(invisexplain)); + firstmatch = invisexplain; + found++; + } else { + found += append_str(out_str, an(invisexplain)); + } + } + +#define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap) +#define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge) + + /* Now check for graphics symbols */ + for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) { + x_str = defsyms[i].explanation; + if (sym == (from_screen ? showsyms[i] : defsyms[i].sym) && *x_str) { + /* avoid "an air", "a water", or "a floor of a room" */ + int article = (i == S_room) ? 2 : /* 2=>"the" */ + !(strcmp(x_str, "air") == 0 || /* 1=>"an" */ + strcmp(x_str, "water") == 0); /* 0=>(none)*/ + + if (!found) { + if (is_cmap_trap(i)) { + Sprintf(out_str, "%c a trap", sym); + hit_trap = TRUE; + } else { + Sprintf(out_str, "%c %s", sym, + article == 2 ? the(x_str) : + article == 1 ? an(x_str) : x_str); + } + firstmatch = x_str; + found++; + } else if (!u.uswallow && !(hit_trap && is_cmap_trap(i)) && + !(found >= 3 && is_cmap_drawbridge(i))) { + found += append_str(out_str, + article == 2 ? the(x_str) : + article == 1 ? an(x_str) : x_str); + if (is_cmap_trap(i)) hit_trap = TRUE; + } + + if (i == S_altar || is_cmap_trap(i)) + need_to_look = TRUE; + } + } + + /* Now check for warning symbols */ + for (i = 1; i < WARNCOUNT; i++) { + x_str = def_warnsyms[i].explanation; + if (sym == (from_screen ? warnsyms[i] : def_warnsyms[i].sym)) { + if (!found) { + Sprintf(out_str, "%c %s", + sym, def_warnsyms[i].explanation); + firstmatch = def_warnsyms[i].explanation; + found++; + } else { + found += append_str(out_str, def_warnsyms[i].explanation); + } + /* Kludge: warning trumps boulders on the display. + Reveal the boulder too or player can get confused */ + if (from_screen && sobj_at(BOULDER, cc.x, cc.y)) + Strcat(out_str, " co-located with a boulder"); + break; /* out of for loop*/ + } + } + + /* if we ignored venom and list turned out to be short, put it back */ + if (skipped_venom && found < 2) { + x_str = objexplain[VENOM_CLASS]; + if (!found) { + Sprintf(out_str, "%c %s", sym, an(x_str)); + firstmatch = x_str; + found++; + } else { + found += append_str(out_str, an(x_str)); + } + } + + /* handle optional boulder symbol as a special case */ + if (iflags.bouldersym && sym == iflags.bouldersym) { + if (!found) { + firstmatch = "boulder"; + Sprintf(out_str, "%c %s", sym, an(firstmatch)); + found++; + } else { + found += append_str(out_str, "boulder"); + } + } + + /* + * If we are looking at the screen, follow multiple possibilities or + * an ambiguous explanation by something more detailed. + */ + if (from_screen) { + if (found > 1 || need_to_look) { + char monbuf[BUFSZ]; + char temp_buf[BUFSZ]; + + pm = lookat(cc.x, cc.y, look_buf, monbuf); + firstmatch = look_buf; + if (*firstmatch) { + Sprintf(temp_buf, " (%s)", firstmatch); + (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1); + found = 1; /* we have something to look up */ + } + if (monbuf[0]) { + Sprintf(temp_buf, " [seen: %s]", monbuf); + (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1); + } + } + } + + /* Finally, print out our explanation. */ + if (found) { + pline("%s", out_str); + /* check the data file for information about this thing */ + if (found == 1 && ans != LOOK_QUICK && ans != LOOK_ONCE && + (ans == LOOK_VERBOSE || (flags.help && !quick))) { + char temp_buf[BUFSZ]; + Strcpy(temp_buf, firstmatch); + checkfile(temp_buf, pm, FALSE, (boolean)(ans == LOOK_VERBOSE)); + } + } else { + pline("I've never heard of such things."); + } + + } while (from_screen && !quick && ans != LOOK_ONCE); + + flags.verbose = save_verbose; + return 0; +} + + +int +dowhatis() +{ + return do_look(FALSE); +} + +int +doquickwhatis() +{ + return do_look(TRUE); +} + +int +doidtrap() +{ + register struct trap *trap; + int x, y, tt; + + if (!getdir("^")) return 0; + x = u.ux + u.dx; + y = u.uy + u.dy; + for (trap = ftrap; trap; trap = trap->ntrap) + if (trap->tx == x && trap->ty == y) { + if (!trap->tseen) break; + tt = trap->ttyp; + if (u.dz) { + if (u.dz < 0 ? (tt == TRAPDOOR || tt == HOLE) : + tt == ROCKTRAP) break; + } + tt = what_trap(tt); + pline("That is %s%s%s.", + an(defsyms[trap_to_defsym(tt)].explanation), + !trap->madeby_u ? "" : (tt == WEB) ? " woven" : + /* trap doors & spiked pits can't be made by + player, and should be considered at least + as much "set" as "dug" anyway */ + (tt == HOLE || tt == PIT) ? " dug" : " set", + !trap->madeby_u ? "" : " by you"); + return 0; + } + pline("I can't see a trap there."); + return 0; +} + +char * +dowhatdoes_core(q, cbuf) +char q; +char *cbuf; +{ + dlb *fp; + char bufr[BUFSZ]; + register char *buf = &bufr[6], *ep, ctrl, meta; + + fp = dlb_fopen(CMDHELPFILE, "r"); + if (!fp) { + pline("Cannot open data file!"); + return 0; + } + + ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0); + meta = ((0x80 & q) ? (0x7f & q) : 0); + while(dlb_fgets(buf,BUFSZ-6,fp)) { + if ((ctrl && *buf=='^' && *(buf+1)==ctrl) || + (meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) || + *buf==q) { + ep = index(buf, '\n'); + if(ep) *ep = 0; + if (ctrl && buf[2] == '\t'){ + buf = bufr + 1; + (void) strncpy(buf, "^? ", 8); + buf[1] = ctrl; + } else if (meta && buf[3] == '\t'){ + buf = bufr + 2; + (void) strncpy(buf, "M-? ", 8); + buf[2] = meta; + } else if(buf[1] == '\t'){ + buf = bufr; + buf[0] = q; + (void) strncpy(buf+1, " ", 7); + } + (void) dlb_fclose(fp); + Strcpy(cbuf, buf); + return cbuf; + } + } + (void) dlb_fclose(fp); + return (char *)0; +} + +int +dowhatdoes() +{ + char bufr[BUFSZ]; + char q, *reslt; + +#if defined(UNIX) || defined(VMS) + introff(); +#endif + q = yn_function("What command?", (char *)0, '\0'); +#if defined(UNIX) || defined(VMS) + intron(); +#endif + reslt = dowhatdoes_core(q, bufr); + if (reslt) + pline("%s", reslt); + else + pline("I've never heard of such commands."); + return 0; +} + +/* data for help_menu() */ +static const char *help_menu_items[] = { +/* 0*/ "Long description of the game and commands.", +/* 1*/ "List of game commands.", +/* 2*/ "Concise history of NetHack.", +/* 3*/ "Info on a character in the game display.", +/* 4*/ "Info on what a given key does.", +/* 5*/ "List of game options.", +/* 6*/ "Longer explanation of game options.", +/* 7*/ "List of extended commands.", +/* 8*/ "The NetHack license.", +#ifdef PORT_HELP + "%s-specific help and commands.", +#define PORT_HELP_ID 100 +#define WIZHLP_SLOT 10 +#else +#define WIZHLP_SLOT 9 +#endif +#ifdef WIZARD + "List of wizard-mode commands.", +#endif + "", + (char *)0 +}; + +STATIC_OVL boolean +help_menu(sel) + int *sel; +{ + winid tmpwin = create_nhwindow(NHW_MENU); +#ifdef PORT_HELP + char helpbuf[QBUFSZ]; +#endif + int i, n; + menu_item *selected; + anything any; + + any.a_void = 0; /* zero all bits */ + start_menu(tmpwin); +#ifdef WIZARD + if (!wizard) help_menu_items[WIZHLP_SLOT] = "", + help_menu_items[WIZHLP_SLOT+1] = (char *)0; +#endif + for (i = 0; help_menu_items[i]; i++) +#ifdef PORT_HELP + /* port-specific line has a %s in it for the PORT_ID */ + if (help_menu_items[i][0] == '%') { + Sprintf(helpbuf, help_menu_items[i], PORT_ID); + any.a_int = PORT_HELP_ID + 1; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + helpbuf, MENU_UNSELECTED); + } else +#endif + { + any.a_int = (*help_menu_items[i]) ? i+1 : 0; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, + ATR_NONE, help_menu_items[i], MENU_UNSELECTED); + } + end_menu(tmpwin, "Select one item:"); + n = select_menu(tmpwin, PICK_ONE, &selected); + destroy_nhwindow(tmpwin); + if (n > 0) { + *sel = selected[0].item.a_int - 1; + free((genericptr_t)selected); + return TRUE; + } + return FALSE; +} + +int +dohelp() +{ + int sel = 0; + + if (help_menu(&sel)) { + switch (sel) { + case 0: display_file(HELP, TRUE); break; + case 1: display_file(SHELP, TRUE); break; + case 2: (void) dohistory(); break; + case 3: (void) dowhatis(); break; + case 4: (void) dowhatdoes(); break; + case 5: option_help(); break; + case 6: display_file(OPTIONFILE, TRUE); break; + case 7: (void) doextlist(); break; + case 8: display_file(LICENSE, TRUE); break; +#ifdef WIZARD + /* handle slot 9 or 10 */ + default: display_file(DEBUGHELP, TRUE); break; +#endif +#ifdef PORT_HELP + case PORT_HELP_ID: port_help(); break; +#endif + } + } + return 0; +} + +int +dohistory() +{ + display_file(HISTORY, TRUE); + return 0; +} + +/*pager.c*/ diff --git a/src/pickup.c b/src/pickup.c new file mode 100644 index 0000000..07be607 --- /dev/null +++ b/src/pickup.c @@ -0,0 +1,2417 @@ +/* SCCS Id: @(#)pickup.c 3.4 2003/07/27 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Contains code for picking objects up, and container use. + */ + +#include "hack.h" + +STATIC_DCL void FDECL(simple_look, (struct obj *,BOOLEAN_P)); +#ifndef GOLDOBJ +STATIC_DCL boolean FDECL(query_classes, (char *,boolean *,boolean *, + const char *,struct obj *,BOOLEAN_P,BOOLEAN_P,int *)); +#else +STATIC_DCL boolean FDECL(query_classes, (char *,boolean *,boolean *, + const char *,struct obj *,BOOLEAN_P,int *)); +#endif +STATIC_DCL void FDECL(check_here, (BOOLEAN_P)); +STATIC_DCL boolean FDECL(n_or_more, (struct obj *)); +STATIC_DCL boolean FDECL(all_but_uchain, (struct obj *)); +#if 0 /* not used */ +STATIC_DCL boolean FDECL(allow_cat_no_uchain, (struct obj *)); +#endif +STATIC_DCL int FDECL(autopick, (struct obj*, int, menu_item **)); +STATIC_DCL int FDECL(count_categories, (struct obj *,int)); +STATIC_DCL long FDECL(carry_count, + (struct obj *,struct obj *,long,BOOLEAN_P,int *,int *)); +STATIC_DCL int FDECL(lift_object, (struct obj *,struct obj *,long *,BOOLEAN_P)); +STATIC_DCL boolean FDECL(mbag_explodes, (struct obj *,int)); +STATIC_PTR int FDECL(in_container,(struct obj *)); +STATIC_PTR int FDECL(ck_bag,(struct obj *)); +STATIC_PTR int FDECL(out_container,(struct obj *)); +STATIC_DCL long FDECL(mbag_item_gone, (int,struct obj *)); +STATIC_DCL void FDECL(observe_quantum_cat, (struct obj *)); +STATIC_DCL int FDECL(menu_loot, (int, struct obj *, BOOLEAN_P)); +STATIC_DCL int FDECL(in_or_out_menu, (const char *,struct obj *, BOOLEAN_P, BOOLEAN_P)); +STATIC_DCL int FDECL(container_at, (int, int, BOOLEAN_P)); +STATIC_DCL boolean FDECL(able_to_loot, (int, int)); +STATIC_DCL boolean FDECL(mon_beside, (int, int)); + +/* define for query_objlist() and autopickup() */ +#define FOLLOW(curr, flags) \ + (((flags) & BY_NEXTHERE) ? (curr)->nexthere : (curr)->nobj) + +/* + * How much the weight of the given container will change when the given + * object is removed from it. This calculation must match the one used + * by weight() in mkobj.c. + */ +#define DELTA_CWT(cont,obj) \ + ((cont)->cursed ? (obj)->owt * 2 : \ + 1 + ((obj)->owt / ((cont)->blessed ? 4 : 2))) +#define GOLD_WT(n) (((n) + 50L) / 100L) +/* if you can figure this out, give yourself a hearty pat on the back... */ +#define GOLD_CAPACITY(w,n) (((w) * -100L) - ((n) + 50L) - 1L) + +static const char moderateloadmsg[] = "You have a little trouble lifting"; +static const char nearloadmsg[] = "You have much trouble lifting"; +static const char overloadmsg[] = "You have extreme difficulty lifting"; + +/* BUG: this lets you look at cockatrice corpses while blind without + touching them */ +/* much simpler version of the look-here code; used by query_classes() */ +STATIC_OVL void +simple_look(otmp, here) +struct obj *otmp; /* list of objects */ +boolean here; /* flag for type of obj list linkage */ +{ + /* Neither of the first two cases is expected to happen, since + * we're only called after multiple classes of objects have been + * detected, hence multiple objects must be present. + */ + if (!otmp) { + impossible("simple_look(null)"); + } else if (!(here ? otmp->nexthere : otmp->nobj)) { + pline("%s", doname(otmp)); + } else { + winid tmpwin = create_nhwindow(NHW_MENU); + putstr(tmpwin, 0, ""); + do { + putstr(tmpwin, 0, doname(otmp)); + otmp = here ? otmp->nexthere : otmp->nobj; + } while (otmp); + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + } +} + +#ifndef GOLDOBJ +int +collect_obj_classes(ilets, otmp, here, incl_gold, filter, itemcount) +char ilets[]; +register struct obj *otmp; +boolean here, incl_gold; +boolean FDECL((*filter),(OBJ_P)); +int *itemcount; +#else +int +collect_obj_classes(ilets, otmp, here, filter, itemcount) +char ilets[]; +register struct obj *otmp; +boolean here; +boolean FDECL((*filter),(OBJ_P)); +int *itemcount; +#endif +{ + register int iletct = 0; + register char c; + + *itemcount = 0; +#ifndef GOLDOBJ + if (incl_gold) + ilets[iletct++] = def_oc_syms[COIN_CLASS]; +#endif + ilets[iletct] = '\0'; /* terminate ilets so that index() will work */ + while (otmp) { + c = def_oc_syms[(int)otmp->oclass]; + if (!index(ilets, c) && (!filter || (*filter)(otmp))) + ilets[iletct++] = c, ilets[iletct] = '\0'; + *itemcount += 1; + otmp = here ? otmp->nexthere : otmp->nobj; + } + + return iletct; +} + +/* + * Suppose some '?' and '!' objects are present, but '/' objects aren't: + * "a" picks all items without further prompting; + * "A" steps through all items, asking one by one; + * "?" steps through '?' items, asking, and ignores '!' ones; + * "/" becomes 'A', since no '/' present; + * "?a" or "a?" picks all '?' without further prompting; + * "/a" or "a/" becomes 'A' since there aren't any '/' + * (bug fix: 3.1.0 thru 3.1.3 treated it as "a"); + * "?/a" or "a?/" or "/a?",&c picks all '?' even though no '/' + * (ie, treated as if it had just been "?a"). + */ +#ifndef GOLDOBJ +STATIC_OVL boolean +query_classes(oclasses, one_at_a_time, everything, action, objs, + here, incl_gold, menu_on_demand) +char oclasses[]; +boolean *one_at_a_time, *everything; +const char *action; +struct obj *objs; +boolean here, incl_gold; +int *menu_on_demand; +#else +STATIC_OVL boolean +query_classes(oclasses, one_at_a_time, everything, action, objs, + here, menu_on_demand) +char oclasses[]; +boolean *one_at_a_time, *everything; +const char *action; +struct obj *objs; +boolean here; +int *menu_on_demand; +#endif +{ + char ilets[20], inbuf[BUFSZ]; + int iletct, oclassct; + boolean not_everything; + char qbuf[QBUFSZ]; + boolean m_seen; + int itemcount; + + oclasses[oclassct = 0] = '\0'; + *one_at_a_time = *everything = m_seen = FALSE; + iletct = collect_obj_classes(ilets, objs, here, +#ifndef GOLDOBJ + incl_gold, +#endif + (boolean FDECL((*),(OBJ_P))) 0, &itemcount); + if (iletct == 0) { + return FALSE; + } else if (iletct == 1) { + oclasses[0] = def_char_to_objclass(ilets[0]); + oclasses[1] = '\0'; + if (itemcount && menu_on_demand) { + ilets[iletct++] = 'm'; + *menu_on_demand = 0; + ilets[iletct] = '\0'; + } + } else { /* more than one choice available */ + const char *where = 0; + register char sym, oc_of_sym, *p; + /* additional choices */ + ilets[iletct++] = ' '; + ilets[iletct++] = 'a'; + ilets[iletct++] = 'A'; + ilets[iletct++] = (objs == invent ? 'i' : ':'); + if (menu_on_demand) { + ilets[iletct++] = 'm'; + *menu_on_demand = 0; + } + ilets[iletct] = '\0'; +ask_again: + oclasses[oclassct = 0] = '\0'; + *one_at_a_time = *everything = FALSE; + not_everything = FALSE; + Sprintf(qbuf,"What kinds of thing do you want to %s? [%s]", + action, ilets); + getlin(qbuf,inbuf); + if (*inbuf == '\033') return FALSE; + + for (p = inbuf; (sym = *p++); ) { + /* new A function (selective all) added by GAN 01/09/87 */ + if (sym == ' ') continue; + else if (sym == 'A') *one_at_a_time = TRUE; + else if (sym == 'a') *everything = TRUE; + else if (sym == ':') { + simple_look(objs, here); /* dumb if objs==invent */ + goto ask_again; + } else if (sym == 'i') { + (void) display_inventory((char *)0, TRUE); + goto ask_again; + } else if (sym == 'm') { + m_seen = TRUE; + } else { + oc_of_sym = def_char_to_objclass(sym); + if (index(ilets,sym)) { + add_valid_menu_class(oc_of_sym); + oclasses[oclassct++] = oc_of_sym; + oclasses[oclassct] = '\0'; + } else { + if (!where) + where = !strcmp(action,"pick up") ? "here" : + !strcmp(action,"take out") ? + "inside" : ""; + if (*where) + There("are no %c's %s.", sym, where); + else + You("have no %c's.", sym); + not_everything = TRUE; + } + } + } + if (m_seen && menu_on_demand) { + *menu_on_demand = (*everything || !oclassct) ? -2 : -3; + return FALSE; + } + if (!oclassct && (!*everything || not_everything)) { + /* didn't pick anything, + or tried to pick something that's not present */ + *one_at_a_time = TRUE; /* force 'A' */ + *everything = FALSE; /* inhibit 'a' */ + } + } + return TRUE; +} + +/* look at the objects at our location, unless there are too many of them */ +STATIC_OVL void +check_here(picked_some) +boolean picked_some; +{ + register struct obj *obj; + register int ct = 0; + + /* count the objects here */ + for (obj = level.objects[u.ux][u.uy]; obj; obj = obj->nexthere) { + if (obj != uchain) + ct++; + } + + /* If there are objects here, take a look. */ + if (ct) { + if (flags.run) nomul(0); + flush_screen(1); + (void) look_here(ct, picked_some); + } else { + read_engr_at(u.ux,u.uy); + } +} + +/* Value set by query_objlist() for n_or_more(). */ +static long val_for_n_or_more; + +/* query_objlist callback: return TRUE if obj's count is >= reference value */ +STATIC_OVL boolean +n_or_more(obj) +struct obj *obj; +{ + if (obj == uchain) return FALSE; + return (obj->quan >= val_for_n_or_more); +} + +/* List of valid menu classes for query_objlist() and allow_category callback */ +static char valid_menu_classes[MAXOCLASSES + 2]; + +void +add_valid_menu_class(c) +int c; +{ + static int vmc_count = 0; + + if (c == 0) /* reset */ + vmc_count = 0; + else + valid_menu_classes[vmc_count++] = (char)c; + valid_menu_classes[vmc_count] = '\0'; +} + +/* query_objlist callback: return TRUE if not uchain */ +STATIC_OVL boolean +all_but_uchain(obj) +struct obj *obj; +{ + return (obj != uchain); +} + +/* query_objlist callback: return TRUE */ +/*ARGSUSED*/ +boolean +allow_all(obj) +struct obj *obj; +{ + return TRUE; +} + +boolean +allow_category(obj) +struct obj *obj; +{ + if (Role_if(PM_PRIEST)) obj->bknown = TRUE; + if (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) || + (index(valid_menu_classes, obj->oclass) != (char *)0)) + return TRUE; + else if (((index(valid_menu_classes,'U') != (char *)0) && + (obj->oclass != COIN_CLASS && obj->bknown && !obj->blessed && !obj->cursed))) + return TRUE; + else if (((index(valid_menu_classes,'B') != (char *)0) && + (obj->oclass != COIN_CLASS && obj->bknown && obj->blessed))) + return TRUE; + else if (((index(valid_menu_classes,'C') != (char *)0) && + (obj->oclass != COIN_CLASS && obj->bknown && obj->cursed))) + return TRUE; + else if (((index(valid_menu_classes,'X') != (char *)0) && + (obj->oclass != COIN_CLASS && !obj->bknown))) + return TRUE; + else + return FALSE; +} + +#if 0 /* not used */ +/* query_objlist callback: return TRUE if valid category (class), no uchain */ +STATIC_OVL boolean +allow_cat_no_uchain(obj) +struct obj *obj; +{ + if ((obj != uchain) && + (((index(valid_menu_classes,'u') != (char *)0) && obj->unpaid) || + (index(valid_menu_classes, obj->oclass) != (char *)0))) + return TRUE; + else + return FALSE; +} +#endif + +/* query_objlist callback: return TRUE if valid class and worn */ +boolean +is_worn_by_type(otmp) +register struct obj *otmp; +{ + return((boolean)(!!(otmp->owornmask & + (W_ARMOR | W_RING | W_AMUL | W_TOOL | W_WEP | W_SWAPWEP | W_QUIVER))) + && (index(valid_menu_classes, otmp->oclass) != (char *)0)); +} + +/* + * Have the hero pick things from the ground + * or a monster's inventory if swallowed. + * + * Arg what: + * >0 autopickup + * =0 interactive + * <0 pickup count of something + * + * Returns 1 if tried to pick something up, whether + * or not it succeeded. + */ +int +pickup(what) +int what; /* should be a long */ +{ + int i, n, res, count, n_tried = 0, n_picked = 0; + menu_item *pick_list = (menu_item *) 0; + boolean autopickup = what > 0; + struct obj *objchain; + int traverse_how; + + if (what < 0) /* pick N of something */ + count = -what; + else /* pick anything */ + count = 0; + + if (!u.uswallow) { + struct trap *ttmp = t_at(u.ux, u.uy); + /* no auto-pick if no-pick move, nothing there, or in a pool */ + if (autopickup && (flags.nopick || !OBJ_AT(u.ux, u.uy) || + (is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) { + read_engr_at(u.ux, u.uy); + return (0); + } + + /* no pickup if levitating & not on air or water level */ + if (!can_reach_floor()) { + if ((multi && !flags.run) || (autopickup && !flags.pickup)) + read_engr_at(u.ux, u.uy); + return (0); + } + if (ttmp && ttmp->tseen) { + /* Allow pickup from holes and trap doors that you escaped + * from because that stuff is teetering on the edge just + * like you, but not pits, because there is an elevation + * discrepancy with stuff in pits. + */ + if ((ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT) && + (!u.utrap || (u.utrap && u.utraptype != TT_PIT))) { + read_engr_at(u.ux, u.uy); + return(0); + } + } + /* multi && !flags.run means they are in the middle of some other + * action, or possibly paralyzed, sleeping, etc.... and they just + * teleported onto the object. They shouldn't pick it up. + */ + if ((multi && !flags.run) || (autopickup && !flags.pickup)) { + check_here(FALSE); + return (0); + } + if (notake(youmonst.data)) { + if (!autopickup) + You("are physically incapable of picking anything up."); + else + check_here(FALSE); + return (0); + } + + /* if there's anything here, stop running */ + if (OBJ_AT(u.ux,u.uy) && flags.run && flags.run != 8 && !flags.nopick) nomul(0); + } + + add_valid_menu_class(0); /* reset */ + if (!u.uswallow) { + objchain = level.objects[u.ux][u.uy]; + traverse_how = BY_NEXTHERE; + } else { + objchain = u.ustuck->minvent; + traverse_how = 0; /* nobj */ + } + /* + * Start the actual pickup process. This is split into two main + * sections, the newer menu and the older "traditional" methods. + * Automatic pickup has been split into its own menu-style routine + * to make things less confusing. + */ + if (autopickup) { + n = autopick(objchain, traverse_how, &pick_list); + goto menu_pickup; + } + + if (flags.menu_style != MENU_TRADITIONAL || iflags.menu_requested) { + + /* use menus exclusively */ + if (count) { /* looking for N of something */ + char buf[QBUFSZ]; + Sprintf(buf, "Pick %d of what?", count); + val_for_n_or_more = count; /* set up callback selector */ + n = query_objlist(buf, objchain, + traverse_how|AUTOSELECT_SINGLE|INVORDER_SORT, + &pick_list, PICK_ONE, n_or_more); + /* correct counts, if any given */ + for (i = 0; i < n; i++) + pick_list[i].count = count; + } else { + n = query_objlist("Pick up what?", objchain, + traverse_how|AUTOSELECT_SINGLE|INVORDER_SORT|FEEL_COCKATRICE, + &pick_list, PICK_ANY, all_but_uchain); + } +menu_pickup: + n_tried = n; + for (n_picked = i = 0 ; i < n; i++) { + res = pickup_object(pick_list[i].item.a_obj,pick_list[i].count, + FALSE); + if (res < 0) break; /* can't continue */ + n_picked += res; + } + if (pick_list) free((genericptr_t)pick_list); + + } else { + /* old style interface */ + int ct = 0; + long lcount; + boolean all_of_a_type, selective; + char oclasses[MAXOCLASSES]; + struct obj *obj, *obj2; + + oclasses[0] = '\0'; /* types to consider (empty for all) */ + all_of_a_type = TRUE; /* take all of considered types */ + selective = FALSE; /* ask for each item */ + + /* check for more than one object */ + for (obj = objchain; + obj; obj = (traverse_how == BY_NEXTHERE) ? obj->nexthere : obj->nobj) + ct++; + + if (ct == 1 && count) { + /* if only one thing, then pick it */ + obj = objchain; + lcount = min(obj->quan, (long)count); + n_tried++; + if (pickup_object(obj, lcount, FALSE) > 0) + n_picked++; /* picked something */ + goto end_query; + + } else if (ct >= 2) { + int via_menu = 0; + + There("are %s objects here.", + (ct <= 10) ? "several" : "many"); + if (!query_classes(oclasses, &selective, &all_of_a_type, + "pick up", objchain, + traverse_how == BY_NEXTHERE, +#ifndef GOLDOBJ + FALSE, +#endif + &via_menu)) { + if (!via_menu) return (0); + n = query_objlist("Pick up what?", + objchain, + traverse_how|(selective ? 0 : INVORDER_SORT), + &pick_list, PICK_ANY, + via_menu == -2 ? allow_all : allow_category); + goto menu_pickup; + } + } + + for (obj = objchain; obj; obj = obj2) { + if (traverse_how == BY_NEXTHERE) + obj2 = obj->nexthere; /* perhaps obj will be picked up */ + else + obj2 = obj->nobj; + lcount = -1L; + + if (!selective && oclasses[0] && !index(oclasses,obj->oclass)) + continue; + + if (!all_of_a_type) { + char qbuf[BUFSZ]; + Sprintf(qbuf, "Pick up %s?", + safe_qbuf("", sizeof("Pick up ?"), doname(obj), + an(simple_typename(obj->otyp)), "something")); + switch ((obj->quan < 2L) ? ynaq(qbuf) : ynNaq(qbuf)) { + case 'q': goto end_query; /* out 2 levels */ + case 'n': continue; + case 'a': + all_of_a_type = TRUE; + if (selective) { + selective = FALSE; + oclasses[0] = obj->oclass; + oclasses[1] = '\0'; + } + break; + case '#': /* count was entered */ + if (!yn_number) continue; /* 0 count => No */ + lcount = (long) yn_number; + if (lcount > obj->quan) lcount = obj->quan; + /* fall thru */ + default: /* 'y' */ + break; + } + } + if (lcount == -1L) lcount = obj->quan; + + n_tried++; + if ((res = pickup_object(obj, lcount, FALSE)) < 0) break; + n_picked += res; + } +end_query: + ; /* semicolon needed by brain-damaged compilers */ + } + + if (!u.uswallow) { + if (!OBJ_AT(u.ux,u.uy)) u.uundetected = 0; + + /* position may need updating (invisible hero) */ + if (n_picked) newsym(u.ux,u.uy); + + /* see whether there's anything else here, after auto-pickup is done */ + if (autopickup) check_here(n_picked > 0); + } + return (n_tried > 0); +} + +#ifdef AUTOPICKUP_EXCEPTIONS +boolean +is_autopickup_exception(obj, grab) +struct obj *obj; +boolean grab; /* forced pickup, rather than forced leave behind? */ +{ + /* + * Does the text description of this match an exception? + */ + char *objdesc = makesingular(doname(obj)); + struct autopickup_exception *ape = (grab) ? + iflags.autopickup_exceptions[AP_GRAB] : + iflags.autopickup_exceptions[AP_LEAVE]; + while (ape) { + if (pmatch(ape->pattern, objdesc)) return TRUE; + ape = ape->next; + } + return FALSE; +} +#endif /* AUTOPICKUP_EXCEPTIONS */ + +/* + * Pick from the given list using flags.pickup_types. Return the number + * of items picked (not counts). Create an array that returns pointers + * and counts of the items to be picked up. If the number of items + * picked is zero, the pickup list is left alone. The caller of this + * function must free the pickup list. + */ +STATIC_OVL int +autopick(olist, follow, pick_list) +struct obj *olist; /* the object list */ +int follow; /* how to follow the object list */ +menu_item **pick_list; /* list of objects and counts to pick up */ +{ + menu_item *pi; /* pick item */ + struct obj *curr; + int n; + const char *otypes = flags.pickup_types; + + /* first count the number of eligible items */ + for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow)) + + +#ifndef AUTOPICKUP_EXCEPTIONS + if (!*otypes || index(otypes, curr->oclass)) +#else + if ((!*otypes || index(otypes, curr->oclass) || + is_autopickup_exception(curr, TRUE)) && + !is_autopickup_exception(curr, FALSE)) +#endif + n++; + + if (n) { + *pick_list = pi = (menu_item *) alloc(sizeof(menu_item) * n); + for (n = 0, curr = olist; curr; curr = FOLLOW(curr, follow)) +#ifndef AUTOPICKUP_EXCEPTIONS + if (!*otypes || index(otypes, curr->oclass)) { +#else + if ((!*otypes || index(otypes, curr->oclass) || + is_autopickup_exception(curr, TRUE)) && + !is_autopickup_exception(curr, FALSE)) { +#endif + pi[n].item.a_obj = curr; + pi[n].count = curr->quan; + n++; + } + } + return n; +} + + +/* + * Put up a menu using the given object list. Only those objects on the + * list that meet the approval of the allow function are displayed. Return + * a count of the number of items selected, as well as an allocated array of + * menu_items, containing pointers to the objects selected and counts. The + * returned counts are guaranteed to be in bounds and non-zero. + * + * Query flags: + * BY_NEXTHERE - Follow object list via nexthere instead of nobj. + * AUTOSELECT_SINGLE - Don't ask if only 1 object qualifies - just + * use it. + * USE_INVLET - Use object's invlet. + * INVORDER_SORT - Use hero's pack order. + * SIGNAL_NOMENU - Return -1 rather than 0 if nothing passes "allow". + */ +int +query_objlist(qstr, olist, qflags, pick_list, how, allow) +const char *qstr; /* query string */ +struct obj *olist; /* the list to pick from */ +int qflags; /* options to control the query */ +menu_item **pick_list; /* return list of items picked */ +int how; /* type of query */ +boolean FDECL((*allow), (OBJ_P));/* allow function */ +{ + int n; + winid win; + struct obj *curr, *last; + char *pack; + anything any; + boolean printed_type_name; + + *pick_list = (menu_item *) 0; + if (!olist) return 0; + + /* count the number of items allowed */ + for (n = 0, last = 0, curr = olist; curr; curr = FOLLOW(curr, qflags)) + if ((*allow)(curr)) { + last = curr; + n++; + } + + if (n == 0) /* nothing to pick here */ + return (qflags & SIGNAL_NOMENU) ? -1 : 0; + + if (n == 1 && (qflags & AUTOSELECT_SINGLE)) { + *pick_list = (menu_item *) alloc(sizeof(menu_item)); + (*pick_list)->item.a_obj = last; + (*pick_list)->count = last->quan; + return 1; + } + + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_obj = (struct obj *) 0; + + /* + * Run through the list and add the objects to the menu. If + * INVORDER_SORT is set, we'll run through the list once for + * each type so we can group them. The allow function will only + * be called once per object in the list. + */ + pack = flags.inv_order; + do { + printed_type_name = FALSE; + for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { + if ((qflags & FEEL_COCKATRICE) && curr->otyp == CORPSE && + will_feel_cockatrice(curr, FALSE)) { + destroy_nhwindow(win); /* stop the menu and revert */ + (void) look_here(0, FALSE); + return 0; + } + if ((!(qflags & INVORDER_SORT) || curr->oclass == *pack) + && (*allow)(curr)) { + + /* if sorting, print type name (once only) */ + if (qflags & INVORDER_SORT && !printed_type_name) { + any.a_obj = (struct obj *) 0; + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + let_to_name(*pack, FALSE), MENU_UNSELECTED); + printed_type_name = TRUE; + } + + any.a_obj = curr; + add_menu(win, obj_to_glyph(curr), &any, + qflags & USE_INVLET ? curr->invlet : 0, + def_oc_syms[(int)objects[curr->otyp].oc_class], + ATR_NONE, doname(curr), MENU_UNSELECTED); + } + } + pack++; + } while (qflags & INVORDER_SORT && *pack); + + end_menu(win, qstr); + n = select_menu(win, how, pick_list); + destroy_nhwindow(win); + + if (n > 0) { + menu_item *mi; + int i; + + /* fix up counts: -1 means no count used => pick all */ + for (i = 0, mi = *pick_list; i < n; i++, mi++) + if (mi->count == -1L || mi->count > mi->item.a_obj->quan) + mi->count = mi->item.a_obj->quan; + } else if (n < 0) { + n = 0; /* caller's don't expect -1 */ + } + return n; +} + +/* + * allow menu-based category (class) selection (for Drop,take off etc.) + * + */ +int +query_category(qstr, olist, qflags, pick_list, how) +const char *qstr; /* query string */ +struct obj *olist; /* the list to pick from */ +int qflags; /* behaviour modification flags */ +menu_item **pick_list; /* return list of items picked */ +int how; /* type of query */ +{ + int n; + winid win; + struct obj *curr; + char *pack; + anything any; + boolean collected_type_name; + char invlet; + int ccount; + boolean do_unpaid = FALSE; + boolean do_blessed = FALSE, do_cursed = FALSE, do_uncursed = FALSE, + do_buc_unknown = FALSE; + int num_buc_types = 0; + + *pick_list = (menu_item *) 0; + if (!olist) return 0; + if ((qflags & UNPAID_TYPES) && count_unpaid(olist)) do_unpaid = TRUE; + if ((qflags & BUC_BLESSED) && count_buc(olist, BUC_BLESSED)) { + do_blessed = TRUE; + num_buc_types++; + } + if ((qflags & BUC_CURSED) && count_buc(olist, BUC_CURSED)) { + do_cursed = TRUE; + num_buc_types++; + } + if ((qflags & BUC_UNCURSED) && count_buc(olist, BUC_UNCURSED)) { + do_uncursed = TRUE; + num_buc_types++; + } + if ((qflags & BUC_UNKNOWN) && count_buc(olist, BUC_UNKNOWN)) { + do_buc_unknown = TRUE; + num_buc_types++; + } + + ccount = count_categories(olist, qflags); + /* no point in actually showing a menu for a single category */ + if (ccount == 1 && !do_unpaid && num_buc_types <= 1 && !(qflags & BILLED_TYPES)) { + for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { + if ((qflags & WORN_TYPES) && + !(curr->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL|W_WEP|W_SWAPWEP|W_QUIVER))) + continue; + break; + } + if (curr) { + *pick_list = (menu_item *) alloc(sizeof(menu_item)); + (*pick_list)->item.a_int = curr->oclass; + return 1; + } else { +#ifdef DEBUG + impossible("query_category: no single object match"); +#endif + } + return 0; + } + + win = create_nhwindow(NHW_MENU); + start_menu(win); + pack = flags.inv_order; + if ((qflags & ALL_TYPES) && (ccount > 1)) { + invlet = 'a'; + any.a_void = 0; + any.a_int = ALL_TYPES_SELECTED; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + (qflags & WORN_TYPES) ? "All worn types" : "All types", + MENU_UNSELECTED); + invlet = 'b'; + } else + invlet = 'a'; + do { + collected_type_name = FALSE; + for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { + if (curr->oclass == *pack) { + if ((qflags & WORN_TYPES) && + !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL | + W_WEP | W_SWAPWEP | W_QUIVER))) + continue; + if (!collected_type_name) { + any.a_void = 0; + any.a_int = curr->oclass; + add_menu(win, NO_GLYPH, &any, invlet++, + def_oc_syms[(int)objects[curr->otyp].oc_class], + ATR_NONE, let_to_name(*pack, FALSE), + MENU_UNSELECTED); + collected_type_name = TRUE; + } + } + } + pack++; + if (invlet >= 'u') { + impossible("query_category: too many categories"); + return 0; + } + } while (*pack); + /* unpaid items if there are any */ + if (do_unpaid) { + invlet = 'u'; + any.a_void = 0; + any.a_int = 'u'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Unpaid items", MENU_UNSELECTED); + } + /* billed items: checked by caller, so always include if BILLED_TYPES */ + if (qflags & BILLED_TYPES) { + invlet = 'x'; + any.a_void = 0; + any.a_int = 'x'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Unpaid items already used up", MENU_UNSELECTED); + } + if (qflags & CHOOSE_ALL) { + invlet = 'A'; + any.a_void = 0; + any.a_int = 'A'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + (qflags & WORN_TYPES) ? + "Auto-select every item being worn" : + "Auto-select every item", MENU_UNSELECTED); + } + /* items with b/u/c/unknown if there are any */ + if (do_blessed) { + invlet = 'B'; + any.a_void = 0; + any.a_int = 'B'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Items known to be Blessed", MENU_UNSELECTED); + } + if (do_cursed) { + invlet = 'C'; + any.a_void = 0; + any.a_int = 'C'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Items known to be Cursed", MENU_UNSELECTED); + } + if (do_uncursed) { + invlet = 'U'; + any.a_void = 0; + any.a_int = 'U'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Items known to be Uncursed", MENU_UNSELECTED); + } + if (do_buc_unknown) { + invlet = 'X'; + any.a_void = 0; + any.a_int = 'X'; + add_menu(win, NO_GLYPH, &any, invlet, 0, ATR_NONE, + "Items of unknown B/C/U status", + MENU_UNSELECTED); + } + end_menu(win, qstr); + n = select_menu(win, how, pick_list); + destroy_nhwindow(win); + if (n < 0) + n = 0; /* caller's don't expect -1 */ + return n; +} + +STATIC_OVL int +count_categories(olist, qflags) +struct obj *olist; +int qflags; +{ + char *pack; + boolean counted_category; + int ccount = 0; + struct obj *curr; + + pack = flags.inv_order; + do { + counted_category = FALSE; + for (curr = olist; curr; curr = FOLLOW(curr, qflags)) { + if (curr->oclass == *pack) { + if ((qflags & WORN_TYPES) && + !(curr->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL | + W_WEP | W_SWAPWEP | W_QUIVER))) + continue; + if (!counted_category) { + ccount++; + counted_category = TRUE; + } + } + } + pack++; + } while (*pack); + return ccount; +} + +/* could we carry `obj'? if not, could we carry some of it/them? */ +STATIC_OVL long +carry_count(obj, container, count, telekinesis, wt_before, wt_after) +struct obj *obj, *container; /* object to pick up, bag it's coming out of */ +long count; +boolean telekinesis; +int *wt_before, *wt_after; +{ + boolean adjust_wt = container && carried(container), + is_gold = obj->oclass == COIN_CLASS; + int wt, iw, ow, oow; + long qq, savequan; +#ifdef GOLDOBJ + long umoney = money_cnt(invent); +#endif + unsigned saveowt; + const char *verb, *prefx1, *prefx2, *suffx; + char obj_nambuf[BUFSZ], where[BUFSZ]; + + savequan = obj->quan; + saveowt = obj->owt; + + iw = max_capacity(); + + if (count != savequan) { + obj->quan = count; + obj->owt = (unsigned)weight(obj); + } + wt = iw + (int)obj->owt; + if (adjust_wt) + wt -= (container->otyp == BAG_OF_HOLDING) ? + (int)DELTA_CWT(container, obj) : (int)obj->owt; +#ifndef GOLDOBJ + if (is_gold) /* merged gold might affect cumulative weight */ + wt -= (GOLD_WT(u.ugold) + GOLD_WT(count) - GOLD_WT(u.ugold + count)); +#else + /* This will go with silver+copper & new gold weight */ + if (is_gold) /* merged gold might affect cumulative weight */ + wt -= (GOLD_WT(umoney) + GOLD_WT(count) - GOLD_WT(umoney + count)); +#endif + if (count != savequan) { + obj->quan = savequan; + obj->owt = saveowt; + } + *wt_before = iw; + *wt_after = wt; + + if (wt < 0) + return count; + + /* see how many we can lift */ + if (is_gold) { +#ifndef GOLDOBJ + iw -= (int)GOLD_WT(u.ugold); + if (!adjust_wt) { + qq = GOLD_CAPACITY((long)iw, u.ugold); + } else { + oow = 0; + qq = 50L - (u.ugold % 100L) - 1L; +#else + iw -= (int)GOLD_WT(umoney); + if (!adjust_wt) { + qq = GOLD_CAPACITY((long)iw, umoney); + } else { + oow = 0; + qq = 50L - (umoney % 100L) - 1L; +#endif + if (qq < 0L) qq += 100L; + for ( ; qq <= count; qq += 100L) { + obj->quan = qq; + obj->owt = (unsigned)GOLD_WT(qq); +#ifndef GOLDOBJ + ow = (int)GOLD_WT(u.ugold + qq); +#else + ow = (int)GOLD_WT(umoney + qq); +#endif + ow -= (container->otyp == BAG_OF_HOLDING) ? + (int)DELTA_CWT(container, obj) : (int)obj->owt; + if (iw + ow >= 0) break; + oow = ow; + } + iw -= oow; + qq -= 100L; + } + if (qq < 0L) qq = 0L; + else if (qq > count) qq = count; +#ifndef GOLDOBJ + wt = iw + (int)GOLD_WT(u.ugold + qq); +#else + wt = iw + (int)GOLD_WT(umoney + qq); +#endif + } else if (count > 1 || count < obj->quan) { + /* + * Ugh. Calc num to lift by changing the quan of of the + * object and calling weight. + * + * This works for containers only because containers + * don't merge. -dean + */ + for (qq = 1L; qq <= count; qq++) { + obj->quan = qq; + obj->owt = (unsigned)(ow = weight(obj)); + if (adjust_wt) + ow -= (container->otyp == BAG_OF_HOLDING) ? + (int)DELTA_CWT(container, obj) : (int)obj->owt; + if (iw + ow >= 0) + break; + wt = iw + ow; + } + --qq; + } else { + /* there's only one, and we can't lift it */ + qq = 0L; + } + obj->quan = savequan; + obj->owt = saveowt; + + if (qq < count) { + /* some message will be given */ + Strcpy(obj_nambuf, doname(obj)); + if (container) { + Sprintf(where, "in %s", the(xname(container))); + verb = "carry"; + } else { + Strcpy(where, "lying here"); + verb = telekinesis ? "acquire" : "lift"; + } + } else { + /* lint supppression */ + *obj_nambuf = *where = '\0'; + verb = ""; + } + /* we can carry qq of them */ + if (qq > 0) { + if (qq < count) + You("can only %s %s of the %s %s.", + verb, (qq == 1L) ? "one" : "some", obj_nambuf, where); + *wt_after = wt; + return qq; + } + + if (!container) Strcpy(where, "here"); /* slightly shorter form */ +#ifndef GOLDOBJ + if (invent || u.ugold) { +#else + if (invent || umoney) { +#endif + prefx1 = "you cannot "; + prefx2 = ""; + suffx = " any more"; + } else { + prefx1 = (obj->quan == 1L) ? "it " : "even one "; + prefx2 = "is too heavy for you to "; + suffx = ""; + } + There("%s %s %s, but %s%s%s%s.", + otense(obj, "are"), obj_nambuf, where, + prefx1, prefx2, verb, suffx); + + /* *wt_after = iw; */ + return 0L; +} + +/* determine whether character is able and player is willing to carry `obj' */ +STATIC_OVL +int +lift_object(obj, container, cnt_p, telekinesis) +struct obj *obj, *container; /* object to pick up, bag it's coming out of */ +long *cnt_p; +boolean telekinesis; +{ + int result, old_wt, new_wt, prev_encumbr, next_encumbr; + + if (obj->otyp == BOULDER && In_sokoban(&u.uz)) { + You("cannot get your %s around this %s.", + body_part(HAND), xname(obj)); + return -1; + } + if (obj->otyp == LOADSTONE || + (obj->otyp == BOULDER && throws_rocks(youmonst.data))) + return 1; /* lift regardless of current situation */ + + *cnt_p = carry_count(obj, container, *cnt_p, telekinesis, &old_wt, &new_wt); + if (*cnt_p < 1L) { + result = -1; /* nothing lifted */ +#ifndef GOLDOBJ + } else if (obj->oclass != COIN_CLASS && inv_cnt() >= 52 && + !merge_choice(invent, obj)) { +#else + } else if (inv_cnt() >= 52 && !merge_choice(invent, obj)) { +#endif + Your("knapsack cannot accommodate any more items."); + result = -1; /* nothing lifted */ + } else { + result = 1; + prev_encumbr = near_capacity(); + if (prev_encumbr < flags.pickup_burden) + prev_encumbr = flags.pickup_burden; + next_encumbr = calc_capacity(new_wt - old_wt); + if (next_encumbr > prev_encumbr) { + if (telekinesis) { + result = 0; /* don't lift */ + } else { + char qbuf[BUFSZ]; + long savequan = obj->quan; + + obj->quan = *cnt_p; + Strcpy(qbuf, + (next_encumbr > HVY_ENCUMBER) ? overloadmsg : + (next_encumbr > MOD_ENCUMBER) ? nearloadmsg : + moderateloadmsg); + Sprintf(eos(qbuf), " %s. Continue?", + safe_qbuf(qbuf, sizeof(" . Continue?"), + doname(obj), an(simple_typename(obj->otyp)), "something")); + obj->quan = savequan; + switch (ynq(qbuf)) { + case 'q': result = -1; break; + case 'n': result = 0; break; + default: break; /* 'y' => result == 1 */ + } + clear_nhwindow(WIN_MESSAGE); + } + } + } + + if (obj->otyp == SCR_SCARE_MONSTER && result <= 0 && !container) + obj->spe = 0; + return result; +} + +/* To prevent qbuf overflow in prompts use planA only + * if it fits, or planB if PlanA doesn't fit, + * finally using the fallback as a last resort. + * last_restort is expected to be very short. + */ +const char * +safe_qbuf(qbuf, padlength, planA, planB, last_resort) +const char *qbuf, *planA, *planB, *last_resort; +unsigned padlength; +{ + /* convert size_t (or int for ancient systems) to ordinary unsigned */ + unsigned len_qbuf = (unsigned)strlen(qbuf), + len_planA = (unsigned)strlen(planA), + len_planB = (unsigned)strlen(planB), + len_lastR = (unsigned)strlen(last_resort); + unsigned textleft = QBUFSZ - (len_qbuf + padlength); + + if (len_lastR >= textleft) { + impossible("safe_qbuf: last_resort too large at %u characters.", + len_lastR); + return ""; + } + return (len_planA < textleft) ? planA : + (len_planB < textleft) ? planB : last_resort; +} + +/* + * Pick up of obj from the ground and add it to the hero's inventory. + * Returns -1 if caller should break out of its loop, 0 if nothing picked + * up, 1 if otherwise. + */ +int +pickup_object(obj, count, telekinesis) +struct obj *obj; +long count; +boolean telekinesis; /* not picking it up directly by hand */ +{ + int res, nearload; +#ifndef GOLDOBJ + const char *where = (obj->ox == u.ux && obj->oy == u.uy) ? + "here" : "there"; +#endif + + if (obj->quan < count) { + impossible("pickup_object: count %ld > quan %ld?", + count, obj->quan); + return 0; + } + + /* In case of auto-pickup, where we haven't had a chance + to look at it yet; affects docall(SCR_SCARE_MONSTER). */ + if (!Blind) +#ifdef INVISIBLE_OBJECTS + if (!obj->oinvis || See_invisible) +#endif + obj->dknown = 1; + + if (obj == uchain) { /* do not pick up attached chain */ + return 0; + } else if (obj->oartifact && !touch_artifact(obj,&youmonst)) { + return 0; +#ifndef GOLDOBJ + } else if (obj->oclass == COIN_CLASS) { + /* Special consideration for gold pieces... */ + long iw = (long)max_capacity() - GOLD_WT(u.ugold); + long gold_capacity = GOLD_CAPACITY(iw, u.ugold); + + if (gold_capacity <= 0L) { + pline( + "There %s %ld gold piece%s %s, but you cannot carry any more.", + otense(obj, "are"), + obj->quan, plur(obj->quan), where); + return 0; + } else if (gold_capacity < count) { + You("can only %s %s of the %ld gold pieces lying %s.", + telekinesis ? "acquire" : "carry", + gold_capacity == 1L ? "one" : "some", obj->quan, where); + pline("%s %ld gold piece%s.", + nearloadmsg, gold_capacity, plur(gold_capacity)); + u.ugold += gold_capacity; + obj->quan -= gold_capacity; + costly_gold(obj->ox, obj->oy, gold_capacity); + } else { + u.ugold += count; + if ((nearload = near_capacity()) != 0) + pline("%s %ld gold piece%s.", + nearload < MOD_ENCUMBER ? + moderateloadmsg : nearloadmsg, + count, plur(count)); + else + prinv((char *) 0, obj, count); + costly_gold(obj->ox, obj->oy, count); + if (count == obj->quan) + delobj(obj); + else + obj->quan -= count; + } + flags.botl = 1; + if (flags.run) nomul(0); + return 1; +#endif + } else if (obj->otyp == CORPSE) { + if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg + && !Stone_resistance && !telekinesis) { + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + display_nhwindow(WIN_MESSAGE, FALSE); + else { + char kbuf[BUFSZ]; + + Strcpy(kbuf, an(corpse_xname(obj, TRUE))); + pline("Touching %s is a fatal mistake.", kbuf); + instapetrify(kbuf); + return -1; + } + } else if (is_rider(&mons[obj->corpsenm])) { + pline("At your %s, the corpse suddenly moves...", + telekinesis ? "attempted acquisition" : "touch"); + (void) revive_corpse(obj); + exercise(A_WIS, FALSE); + return -1; + } + } else if (obj->otyp == SCR_SCARE_MONSTER) { + if (obj->blessed) obj->blessed = 0; + else if (!obj->spe && !obj->cursed) obj->spe = 1; + else { + pline_The("scroll%s %s to dust as you %s %s up.", + plur(obj->quan), otense(obj, "turn"), + telekinesis ? "raise" : "pick", + (obj->quan == 1L) ? "it" : "them"); + if (!(objects[SCR_SCARE_MONSTER].oc_name_known) && + !(objects[SCR_SCARE_MONSTER].oc_uname)) + docall(obj); + useupf(obj, obj->quan); + return 1; /* tried to pick something up and failed, but + don't want to terminate pickup loop yet */ + } + } + + if ((res = lift_object(obj, (struct obj *)0, &count, telekinesis)) <= 0) + return res; + +#ifdef GOLDOBJ + /* Whats left of the special case for gold :-) */ + if (obj->oclass == COIN_CLASS) flags.botl = 1; +#endif + if (obj->quan != count && obj->otyp != LOADSTONE) + obj = splitobj(obj, count); + + obj = pick_obj(obj); + + if (uwep && uwep == obj) mrg_to_wielded = TRUE; + nearload = near_capacity(); + prinv(nearload == SLT_ENCUMBER ? moderateloadmsg : (char *) 0, + obj, count); + mrg_to_wielded = FALSE; + return 1; +} + +/* + * Do the actual work of picking otmp from the floor or monster's interior + * and putting it in the hero's inventory. Take care of billing. Return a + * pointer to the object where otmp ends up. This may be different + * from otmp because of merging. + * + * Gold never reaches this routine unless GOLDOBJ is defined. + */ +struct obj * +pick_obj(otmp) +struct obj *otmp; +{ + obj_extract_self(otmp); + if (!u.uswallow && otmp != uball && costly_spot(otmp->ox, otmp->oy)) { + char saveushops[5], fakeshop[2]; + + /* addtobill cares about your location rather than the object's; + usually they'll be the same, but not when using telekinesis + (if ever implemented) or a grappling hook */ + Strcpy(saveushops, u.ushops); + fakeshop[0] = *in_rooms(otmp->ox, otmp->oy, SHOPBASE); + fakeshop[1] = '\0'; + Strcpy(u.ushops, fakeshop); + /* sets obj->unpaid if necessary */ + addtobill(otmp, TRUE, FALSE, FALSE); + Strcpy(u.ushops, saveushops); + /* if you're outside the shop, make shk notice */ + if (!index(u.ushops, *fakeshop)) + remote_burglary(otmp->ox, otmp->oy); + } + if (otmp->no_charge) /* only applies to objects outside invent */ + otmp->no_charge = 0; + newsym(otmp->ox, otmp->oy); + return addinv(otmp); /* might merge it with other objects */ +} + +/* + * prints a message if encumbrance changed since the last check and + * returns the new encumbrance value (from near_capacity()). + */ +int +encumber_msg() +{ + static int oldcap = UNENCUMBERED; + int newcap = near_capacity(); + + if(oldcap < newcap) { + switch(newcap) { + case 1: Your("movements are slowed slightly because of your load."); + break; + case 2: You("rebalance your load. Movement is difficult."); + break; + case 3: You("%s under your heavy load. Movement is very hard.", + stagger(youmonst.data, "stagger")); + break; + default: You("%s move a handspan with this load!", + newcap == 4 ? "can barely" : "can't even"); + break; + } + flags.botl = 1; + } else if(oldcap > newcap) { + switch(newcap) { + case 0: Your("movements are now unencumbered."); + break; + case 1: Your("movements are only slowed slightly by your load."); + break; + case 2: You("rebalance your load. Movement is still difficult."); + break; + case 3: You("%s under your load. Movement is still very hard.", + stagger(youmonst.data, "stagger")); + break; + } + flags.botl = 1; + } + + oldcap = newcap; + return (newcap); +} + +/* Is there a container at x,y. Optional: return count of containers at x,y */ +STATIC_OVL int +container_at(x, y, countem) +int x,y; +boolean countem; +{ + struct obj *cobj, *nobj; + int container_count = 0; + + for(cobj = level.objects[x][y]; cobj; cobj = nobj) { + nobj = cobj->nexthere; + if(Is_container(cobj)) { + container_count++; + if (!countem) break; + } + } + return container_count; +} + +STATIC_OVL boolean +able_to_loot(x, y) +int x, y; +{ + if (!can_reach_floor()) { +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) + rider_cant_reach(); /* not skilled enough to reach */ + else +#endif + You("cannot reach the %s.", surface(x, y)); + return FALSE; + } else if (is_pool(x, y) || is_lava(x, y)) { + /* at present, can't loot in water even when Underwater */ + You("cannot loot things that are deep in the %s.", + is_lava(x, y) ? "lava" : "water"); + return FALSE; + } else if (nolimbs(youmonst.data)) { + pline("Without limbs, you cannot loot anything."); + return FALSE; + } else if (!freehand()) { + pline("Without a free %s, you cannot loot anything.", + body_part(HAND)); + return FALSE; + } + return TRUE; +} + +STATIC_OVL boolean +mon_beside(x,y) +int x, y; +{ + int i,j,nx,ny; + for(i = -1; i <= 1; i++) + for(j = -1; j <= 1; j++) { + nx = x + i; + ny = y + j; + if(isok(nx, ny) && MON_AT(nx, ny)) + return TRUE; + } + return FALSE; +} + +int +doloot() /* loot a container on the floor or loot saddle from mon. */ +{ + register struct obj *cobj, *nobj; + register int c = -1; + int timepassed = 0; + coord cc; + boolean underfoot = TRUE; + const char *dont_find_anything = "don't find anything"; + struct monst *mtmp; + char qbuf[BUFSZ]; + int prev_inquiry = 0; + boolean prev_loot = FALSE; + + if (check_capacity((char *)0)) { + /* "Can't do that while carrying so much stuff." */ + return 0; + } + if (nohands(youmonst.data)) { + You("have no hands!"); /* not `body_part(HAND)' */ + return 0; + } + cc.x = u.ux; cc.y = u.uy; + +lootcont: + + if (container_at(cc.x, cc.y, FALSE)) { + boolean any = FALSE; + + if (!able_to_loot(cc.x, cc.y)) return 0; + for (cobj = level.objects[cc.x][cc.y]; cobj; cobj = nobj) { + nobj = cobj->nexthere; + + if (Is_container(cobj)) { + Sprintf(qbuf, "There is %s here, loot it?", + safe_qbuf("", sizeof("There is here, loot it?"), + doname(cobj), an(simple_typename(cobj->otyp)), + "a container")); + c = ynq(qbuf); + if (c == 'q') return (timepassed); + if (c == 'n') continue; + any = TRUE; + + if (cobj->olocked) { + pline("Hmmm, it seems to be locked."); + continue; + } + if (cobj->otyp == BAG_OF_TRICKS) { + int tmp; + You("carefully open the bag..."); + pline("It develops a huge set of teeth and bites you!"); + tmp = rnd(10); + if (Half_physical_damage) tmp = (tmp+1) / 2; + losehp(tmp, "carnivorous bag", KILLED_BY_AN); + makeknown(BAG_OF_TRICKS); + timepassed = 1; + continue; + } + + You("carefully open %s...", the(xname(cobj))); + timepassed |= use_container(cobj, 0); + if (multi < 0) return 1; /* chest trap */ + } + } + if (any) c = 'y'; + } else if (Confusion) { +#ifndef GOLDOBJ + if (u.ugold){ + long contribution = rnd((int)min(LARGEST_INT,u.ugold)); + struct obj *goldob = mkgoldobj(contribution); +#else + struct obj *goldob; + /* Find a money object to mess with */ + for (goldob = invent; goldob; goldob = goldob->nobj) { + if (goldob->oclass == COIN_CLASS) break; + } + if (goldob){ + long contribution = rnd((int)min(LARGEST_INT, goldob->quan)); + if (contribution < goldob->quan) + goldob = splitobj(goldob, contribution); + freeinv(goldob); +#endif + if (IS_THRONE(levl[u.ux][u.uy].typ)){ + struct obj *coffers; + int pass; + /* find the original coffers chest, or any chest */ + for (pass = 2; pass > -1; pass -= 2) + for (coffers = fobj; coffers; coffers = coffers->nobj) + if (coffers->otyp == CHEST && coffers->spe == pass) + goto gotit; /* two level break */ +gotit: + if (coffers) { + verbalize("Thank you for your contribution to reduce the debt."); + (void) add_to_container(coffers, goldob); + coffers->owt = weight(coffers); + } else { + struct monst *mon = makemon(courtmon(), + u.ux, u.uy, NO_MM_FLAGS); + if (mon) { +#ifndef GOLDOBJ + mon->mgold += goldob->quan; + delobj(goldob); + pline("The exchequer accepts your contribution."); + } else { + dropx(goldob); + } + } + } else { + dropx(goldob); +#else + add_to_minv(mon, goldob); + pline("The exchequer accepts your contribution."); + } else { + dropy(goldob); + } + } + } else { + dropy(goldob); +#endif + pline("Ok, now there is loot here."); + } + } + } else if (IS_GRAVE(levl[cc.x][cc.y].typ)) { + You("need to dig up the grave to effectively loot it..."); + } + /* + * 3.3.1 introduced directional looting for some things. + */ + if (c != 'y' && mon_beside(u.ux, u.uy)) { + if (!get_adjacent_loc("Loot in what direction?", "Invalid loot location", + u.ux, u.uy, &cc)) return 0; + if (cc.x == u.ux && cc.y == u.uy) { + underfoot = TRUE; + if (container_at(cc.x, cc.y, FALSE)) + goto lootcont; + } else + underfoot = FALSE; + if (u.dz < 0) { + You("%s to loot on the %s.", dont_find_anything, + ceiling(cc.x, cc.y)); + timepassed = 1; + return timepassed; + } + mtmp = m_at(cc.x, cc.y); + if (mtmp) timepassed = loot_mon(mtmp, &prev_inquiry, &prev_loot); + + /* Preserve pre-3.3.1 behaviour for containers. + * Adjust this if-block to allow container looting + * from one square away to change that in the future. + */ + if (!underfoot) { + if (container_at(cc.x, cc.y, FALSE)) { + if (mtmp) { + You_cant("loot anything %sthere with %s in the way.", + prev_inquiry ? "else " : "", mon_nam(mtmp)); + return timepassed; + } else { + You("have to be at a container to loot it."); + } + } else { + You("%s %sthere to loot.", dont_find_anything, + (prev_inquiry || prev_loot) ? "else " : ""); + return timepassed; + } + } + } else if (c != 'y' && c != 'n') { + You("%s %s to loot.", dont_find_anything, + underfoot ? "here" : "there"); + } + return (timepassed); +} + +/* loot_mon() returns amount of time passed. + */ +int +loot_mon(mtmp, passed_info, prev_loot) +struct monst *mtmp; +int *passed_info; +boolean *prev_loot; +{ + int c = -1; + int timepassed = 0; +#ifdef STEED + struct obj *otmp; + char qbuf[QBUFSZ]; + + /* 3.3.1 introduced the ability to remove saddle from a steed */ + /* *passed_info is set to TRUE if a loot query was given. */ + /* *prev_loot is set to TRUE if something was actually acquired in here. */ + if (mtmp && mtmp != u.usteed && (otmp = which_armor(mtmp, W_SADDLE))) { + long unwornmask; + if (passed_info) *passed_info = 1; + Sprintf(qbuf, "Do you want to remove the saddle from %s?", + x_monnam(mtmp, ARTICLE_THE, (char *)0, SUPPRESS_SADDLE, FALSE)); + if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') { + if (nolimbs(youmonst.data)) { + You_cant("do that without limbs."); /* not body_part(HAND) */ + return (0); + } + if (otmp->cursed) { + You("can't. The saddle seems to be stuck to %s.", + x_monnam(mtmp, ARTICLE_THE, (char *)0, + SUPPRESS_SADDLE, FALSE)); + + /* the attempt costs you time */ + return (1); + } + obj_extract_self(otmp); + if ((unwornmask = otmp->owornmask) != 0L) { + mtmp->misc_worn_check &= ~unwornmask; + otmp->owornmask = 0L; + update_mon_intrinsics(mtmp, otmp, FALSE, FALSE); + } + otmp = hold_another_object(otmp, "You drop %s!", doname(otmp), + (const char *)0); + timepassed = rnd(3); + if (prev_loot) *prev_loot = TRUE; + } else if (c == 'q') { + return (0); + } + } +#endif /* STEED */ + /* 3.4.0 introduced the ability to pick things up from within swallower's stomach */ + if (u.uswallow) { + int count = passed_info ? *passed_info : 0; + timepassed = pickup(count); + } + return timepassed; +} + +/* + * Decide whether an object being placed into a magic bag will cause + * it to explode. If the object is a bag itself, check recursively. + */ +STATIC_OVL boolean +mbag_explodes(obj, depthin) + struct obj *obj; + int depthin; +{ + /* these won't cause an explosion when they're empty */ + if ((obj->otyp == WAN_CANCELLATION || obj->otyp == BAG_OF_TRICKS) && + obj->spe <= 0) + return FALSE; + + /* odds: 1/1, 2/2, 3/4, 4/8, 5/16, 6/32, 7/64, 8/128, 9/128, 10/128,... */ + if ((Is_mbag(obj) || obj->otyp == WAN_CANCELLATION) && + (rn2(1 << (depthin > 7 ? 7 : depthin)) <= depthin)) + return TRUE; + else if (Has_contents(obj)) { + struct obj *otmp; + + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) + if (mbag_explodes(otmp, depthin+1)) return TRUE; + } + return FALSE; +} + +/* A variable set in use_container(), to be used by the callback routines */ +/* in_container(), and out_container() from askchain() and use_container(). */ +static NEARDATA struct obj *current_container; +#define Icebox (current_container->otyp == ICE_BOX) + +/* Returns: -1 to stop, 1 item was inserted, 0 item was not inserted. */ +STATIC_PTR int +in_container(obj) +register struct obj *obj; +{ + boolean floor_container = !carried(current_container); + boolean was_unpaid = FALSE; + char buf[BUFSZ]; + + if (!current_container) { + impossible(" no current_container?"); + return 0; + } else if (obj == uball || obj == uchain) { + You("must be kidding."); + return 0; + } else if (obj == current_container) { + pline("That would be an interesting topological exercise."); + return 0; + } else if (obj->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)) { + Norep("You cannot %s %s you are wearing.", + Icebox ? "refrigerate" : "stash", something); + return 0; + } else if ((obj->otyp == LOADSTONE) && obj->cursed) { + obj->bknown = 1; + pline_The("stone%s won't leave your person.", plur(obj->quan)); + return 0; + } else if (obj->otyp == AMULET_OF_YENDOR || + obj->otyp == CANDELABRUM_OF_INVOCATION || + obj->otyp == BELL_OF_OPENING || + obj->otyp == SPE_BOOK_OF_THE_DEAD) { + /* Prohibit Amulets in containers; if you allow it, monsters can't + * steal them. It also becomes a pain to check to see if someone + * has the Amulet. Ditto for the Candelabrum, the Bell and the Book. + */ + pline("%s cannot be confined in such trappings.", The(xname(obj))); + return 0; + } else if (obj->otyp == LEASH && obj->leashmon != 0) { + pline("%s attached to your pet.", Tobjnam(obj, "are")); + return 0; + } else if (obj == uwep) { + if (welded(obj)) { + weldmsg(obj); + return 0; + } + setuwep((struct obj *) 0); + if (uwep) return 0; /* unwielded, died, rewielded */ + } else if (obj == uswapwep) { + setuswapwep((struct obj *) 0); + if (uswapwep) return 0; /* unwielded, died, rewielded */ + } else if (obj == uquiver) { + setuqwep((struct obj *) 0); + if (uquiver) return 0; /* unwielded, died, rewielded */ + } + + if (obj->otyp == CORPSE) { + if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg + && !Stone_resistance) { + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + display_nhwindow(WIN_MESSAGE, FALSE); + else { + char kbuf[BUFSZ]; + + Strcpy(kbuf, an(corpse_xname(obj, TRUE))); + pline("Touching %s is a fatal mistake.", kbuf); + instapetrify(kbuf); + return -1; + } + } + } + + /* boxes, boulders, and big statues can't fit into any container */ + if (obj->otyp == ICE_BOX || Is_box(obj) || obj->otyp == BOULDER || + (obj->otyp == STATUE && bigmonst(&mons[obj->corpsenm]))) { + /* + * xname() uses a static result array. Save obj's name + * before current_container's name is computed. Don't + * use the result of strcpy() within You() --- the order + * of evaluation of the parameters is undefined. + */ + Strcpy(buf, the(xname(obj))); + You("cannot fit %s into %s.", buf, + the(xname(current_container))); + return 0; + } + + freeinv(obj); + + if (obj_is_burning(obj)) /* this used to be part of freeinv() */ + (void) snuff_lit(obj); + + if (floor_container && costly_spot(u.ux, u.uy)) { + if (current_container->no_charge && !obj->unpaid) { + /* don't sell when putting the item into your own container */ + obj->no_charge = 1; + } else if (obj->oclass != COIN_CLASS) { + /* sellobj() will take an unpaid item off the shop bill + * note: coins are handled later */ + was_unpaid = obj->unpaid ? TRUE : FALSE; + sellobj_state(SELL_DELIBERATE); + sellobj(obj, u.ux, u.uy); + sellobj_state(SELL_NORMAL); + } + } + if (Icebox && !age_is_relative(obj)) { + obj->age = monstermoves - obj->age; /* actual age */ + /* stop any corpse timeouts when frozen */ + if (obj->otyp == CORPSE && obj->timed) { + long rot_alarm = stop_timer(ROT_CORPSE, (genericptr_t)obj); + (void) stop_timer(REVIVE_MON, (genericptr_t)obj); + /* mark a non-reviving corpse as such */ + if (rot_alarm) obj->norevive = 1; + } + } else if (Is_mbag(current_container) && mbag_explodes(obj, 0)) { + /* explicitly mention what item is triggering the explosion */ + pline( + "As you put %s inside, you are blasted by a magical explosion!", + doname(obj)); + /* did not actually insert obj yet */ + if (was_unpaid) addtobill(obj, FALSE, FALSE, TRUE); + obfree(obj, (struct obj *)0); + delete_contents(current_container); + if (!floor_container) + useup(current_container); + else if (obj_here(current_container, u.ux, u.uy)) + useupf(current_container, obj->quan); + else + panic("in_container: bag not found."); + + losehp(d(6,6),"magical explosion", KILLED_BY_AN); + current_container = 0; /* baggone = TRUE; */ + } + + if (current_container) { + Strcpy(buf, the(xname(current_container))); + You("put %s into %s.", doname(obj), buf); + + /* gold in container always needs to be added to credit */ + if (floor_container && obj->oclass == COIN_CLASS) + sellobj(obj, current_container->ox, current_container->oy); + (void) add_to_container(current_container, obj); + current_container->owt = weight(current_container); + } + /* gold needs this, and freeinv() many lines above may cause + * the encumbrance to disappear from the status, so just always + * update status immediately. + */ + bot(); + + return(current_container ? 1 : -1); +} + +STATIC_PTR int +ck_bag(obj) +struct obj *obj; +{ + return current_container && obj != current_container; +} + +/* Returns: -1 to stop, 1 item was removed, 0 item was not removed. */ +STATIC_PTR int +out_container(obj) +register struct obj *obj; +{ + register struct obj *otmp; + boolean is_gold = (obj->oclass == COIN_CLASS); + int res, loadlev; + long count; + + if (!current_container) { + impossible(" no current_container?"); + return -1; + } else if (is_gold) { + obj->owt = weight(obj); + } + + if(obj->oartifact && !touch_artifact(obj,&youmonst)) return 0; + + if (obj->otyp == CORPSE) { + if ( (touch_petrifies(&mons[obj->corpsenm])) && !uarmg + && !Stone_resistance) { + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + display_nhwindow(WIN_MESSAGE, FALSE); + else { + char kbuf[BUFSZ]; + + Strcpy(kbuf, an(corpse_xname(obj, TRUE))); + pline("Touching %s is a fatal mistake.", kbuf); + instapetrify(kbuf); + return -1; + } + } + } + + count = obj->quan; + if ((res = lift_object(obj, current_container, &count, FALSE)) <= 0) + return res; + + if (obj->quan != count && obj->otyp != LOADSTONE) + obj = splitobj(obj, count); + + /* Remove the object from the list. */ + obj_extract_self(obj); + current_container->owt = weight(current_container); + + if (Icebox && !age_is_relative(obj)) { + obj->age = monstermoves - obj->age; /* actual age */ + if (obj->otyp == CORPSE) + start_corpse_timeout(obj); + } + /* simulated point of time */ + + if(!obj->unpaid && !carried(current_container) && + costly_spot(current_container->ox, current_container->oy)) { + obj->ox = current_container->ox; + obj->oy = current_container->oy; + addtobill(obj, FALSE, FALSE, FALSE); + } + if (is_pick(obj) && !obj->unpaid && *u.ushops && shop_keeper(*u.ushops)) + verbalize("You sneaky cad! Get out of here with that pick!"); + + otmp = addinv(obj); + loadlev = near_capacity(); + prinv(loadlev ? + (loadlev < MOD_ENCUMBER ? + "You have a little trouble removing" : + "You have much trouble removing") : (char *)0, + otmp, count); + + if (is_gold) { +#ifndef GOLDOBJ + dealloc_obj(obj); +#endif + bot(); /* update character's gold piece count immediately */ + } + return 1; +} + +/* an object inside a cursed bag of holding is being destroyed */ +STATIC_OVL long +mbag_item_gone(held, item) +int held; +struct obj *item; +{ + struct monst *shkp; + long loss = 0L; + + if (item->dknown) + pline("%s %s vanished!", Doname2(item), otense(item, "have")); + else + You("%s %s disappear!", Blind ? "notice" : "see", doname(item)); + + if (*u.ushops && (shkp = shop_keeper(*u.ushops)) != 0) { + if (held ? (boolean) item->unpaid : costly_spot(u.ux, u.uy)) + loss = stolen_value(item, u.ux, u.uy, + (boolean)shkp->mpeaceful, TRUE); + } + obfree(item, (struct obj *) 0); + return loss; +} + +STATIC_OVL void +observe_quantum_cat(box) +struct obj *box; +{ + static NEARDATA const char sc[] = "Schroedinger's Cat"; + struct obj *deadcat; + struct monst *livecat; + xchar ox, oy; + + box->spe = 0; /* box->owt will be updated below */ + if (get_obj_location(box, &ox, &oy, 0)) + box->ox = ox, box->oy = oy; /* in case it's being carried */ + + /* this isn't really right, since any form of observation + (telepathic or monster/object/food detection) ought to + force the determination of alive vs dead state; but basing + it just on opening the box is much simpler to cope with */ + livecat = rn2(2) ? makemon(&mons[PM_HOUSECAT], + box->ox, box->oy, NO_MINVENT) : 0; + if (livecat) { + livecat->mpeaceful = 1; + set_malign(livecat); + if (!canspotmon(livecat)) + You("think %s brushed your %s.", something, body_part(FOOT)); + else + pline("%s inside the box is still alive!", Monnam(livecat)); + (void) christen_monst(livecat, sc); + } else { + deadcat = mk_named_object(CORPSE, &mons[PM_HOUSECAT], + box->ox, box->oy, sc); + if (deadcat) { + obj_extract_self(deadcat); + (void) add_to_container(box, deadcat); + } + pline_The("%s inside the box is dead!", + Hallucination ? rndmonnam() : "housecat"); + } + box->owt = weight(box); + return; +} + +#undef Icebox + +int +use_container(obj, held) +register struct obj *obj; +register int held; +{ + struct obj *curr, *otmp; +#ifndef GOLDOBJ + struct obj *u_gold = (struct obj *)0; +#endif + boolean one_by_one, allflag, quantum_cat = FALSE, + loot_out = FALSE, loot_in = FALSE; + char select[MAXOCLASSES+1]; + char qbuf[BUFSZ], emptymsg[BUFSZ], pbuf[QBUFSZ]; + long loss = 0L; + int cnt = 0, used = 0, + menu_on_request; + + emptymsg[0] = '\0'; + if (nohands(youmonst.data)) { + You("have no hands!"); /* not `body_part(HAND)' */ + return 0; + } else if (!freehand()) { + You("have no free %s.", body_part(HAND)); + return 0; + } + if (obj->olocked) { + pline("%s to be locked.", Tobjnam(obj, "seem")); + if (held) You("must put it down to unlock."); + return 0; + } else if (obj->otrapped) { + if (held) You("open %s...", the(xname(obj))); + (void) chest_trap(obj, HAND, FALSE); + /* even if the trap fails, you've used up this turn */ + if (multi >= 0) { /* in case we didn't become paralyzed */ + nomul(-1); + nomovemsg = ""; + } + return 1; + } + current_container = obj; /* for use by in/out_container */ + + if (obj->spe == 1) { + observe_quantum_cat(obj); + used = 1; + quantum_cat = TRUE; /* for adjusting "it's empty" message */ + } + /* Count the number of contained objects. Sometimes toss objects if */ + /* a cursed magic bag. */ + for (curr = obj->cobj; curr; curr = otmp) { + otmp = curr->nobj; + if (Is_mbag(obj) && obj->cursed && !rn2(13)) { + obj_extract_self(curr); + loss += mbag_item_gone(held, curr); + used = 1; + } else { + cnt++; + } + } + + if (loss) /* magic bag lost some shop goods */ + You("owe %ld %s for lost merchandise.", loss, currency(loss)); + obj->owt = weight(obj); /* in case any items were lost */ + + if (!cnt) + Sprintf(emptymsg, "%s is %sempty.", Yname2(obj), + quantum_cat ? "now " : ""); + + if (cnt || flags.menu_style == MENU_FULL) { + Strcpy(qbuf, "Do you want to take something out of "); + Sprintf(eos(qbuf), "%s?", + safe_qbuf(qbuf, 1, yname(obj), ysimple_name(obj), "it")); + if (flags.menu_style != MENU_TRADITIONAL) { + if (flags.menu_style == MENU_FULL) { + int t; + char menuprompt[BUFSZ]; + boolean outokay = (cnt != 0); +#ifndef GOLDOBJ + boolean inokay = (invent != 0) || (u.ugold != 0); +#else + boolean inokay = (invent != 0); +#endif + if (!outokay && !inokay) { + pline("%s", emptymsg); + You("don't have anything to put in."); + return used; + } + menuprompt[0] = '\0'; + if (!cnt) Sprintf(menuprompt, "%s ", emptymsg); + Strcat(menuprompt, "Do what?"); + t = in_or_out_menu(menuprompt, current_container, outokay, inokay); + if (t <= 0) return 0; + loot_out = (t & 0x01) != 0; + loot_in = (t & 0x02) != 0; + } else { /* MENU_COMBINATION or MENU_PARTIAL */ + loot_out = (yn_function(qbuf, "ynq", 'n') == 'y'); + } + if (loot_out) { + add_valid_menu_class(0); /* reset */ + used |= menu_loot(0, current_container, FALSE) > 0; + } + } else { + /* traditional code */ +ask_again2: + menu_on_request = 0; + add_valid_menu_class(0); /* reset */ + Strcpy(pbuf, ":ynq"); + if (cnt) Strcat(pbuf, "m"); + switch (yn_function(qbuf, pbuf, 'n')) { + case ':': + container_contents(current_container, FALSE, FALSE); + goto ask_again2; + case 'y': + if (query_classes(select, &one_by_one, &allflag, + "take out", current_container->cobj, + FALSE, +#ifndef GOLDOBJ + FALSE, +#endif + &menu_on_request)) { + if (askchain((struct obj **)¤t_container->cobj, + (one_by_one ? (char *)0 : select), + allflag, out_container, + (int FDECL((*),(OBJ_P)))0, + 0, "nodot")) + used = 1; + } else if (menu_on_request < 0) { + used |= menu_loot(menu_on_request, + current_container, FALSE) > 0; + } + /*FALLTHRU*/ + case 'n': + break; + case 'm': + menu_on_request = -2; /* triggers ALL_CLASSES */ + used |= menu_loot(menu_on_request, current_container, FALSE) > 0; + break; + case 'q': + default: + return used; + } + } + } else { + pline("%s", emptymsg); /* is empty. */ + } + +#ifndef GOLDOBJ + if (!invent && u.ugold == 0) { +#else + if (!invent) { +#endif + /* nothing to put in, but some feedback is necessary */ + You("don't have anything to put in."); + return used; + } + if (flags.menu_style != MENU_FULL) { + Sprintf(qbuf, "Do you wish to put %s in?", something); + Strcpy(pbuf, ynqchars); + if (flags.menu_style == MENU_TRADITIONAL && invent && inv_cnt() > 0) + Strcat(pbuf, "m"); + switch (yn_function(qbuf, pbuf, 'n')) { + case 'y': + loot_in = TRUE; + break; + case 'n': + break; + case 'm': + add_valid_menu_class(0); /* reset */ + menu_on_request = -2; /* triggers ALL_CLASSES */ + used |= menu_loot(menu_on_request, current_container, TRUE) > 0; + break; + case 'q': + default: + return used; + } + } + /* + * Gone: being nice about only selecting food if we know we are + * putting things in an ice chest. + */ + if (loot_in) { +#ifndef GOLDOBJ + if (u.ugold) { + /* + * Hack: gold is not in the inventory, so make a gold object + * and put it at the head of the inventory list. + */ + u_gold = mkgoldobj(u.ugold); /* removes from u.ugold */ + u_gold->in_use = TRUE; + u.ugold = u_gold->quan; /* put the gold back */ + assigninvlet(u_gold); /* might end up as NOINVSYM */ + u_gold->nobj = invent; + invent = u_gold; + } +#endif + add_valid_menu_class(0); /* reset */ + if (flags.menu_style != MENU_TRADITIONAL) { + used |= menu_loot(0, current_container, TRUE) > 0; + } else { + /* traditional code */ + menu_on_request = 0; + if (query_classes(select, &one_by_one, &allflag, "put in", + invent, FALSE, +#ifndef GOLDOBJ + (u.ugold != 0L), +#endif + &menu_on_request)) { + (void) askchain((struct obj **)&invent, + (one_by_one ? (char *)0 : select), allflag, + in_container, ck_bag, 0, "nodot"); + used = 1; + } else if (menu_on_request < 0) { + used |= menu_loot(menu_on_request, + current_container, TRUE) > 0; + } + } + } + +#ifndef GOLDOBJ + if (u_gold && invent && invent->oclass == COIN_CLASS) { + /* didn't stash [all of] it */ + u_gold = invent; + invent = u_gold->nobj; + u_gold->in_use = FALSE; + dealloc_obj(u_gold); + } +#endif + return used; +} + +/* Loot a container (take things out, put things in), using a menu. */ +STATIC_OVL int +menu_loot(retry, container, put_in) +int retry; +struct obj *container; +boolean put_in; +{ + int n, i, n_looted = 0; + boolean all_categories = TRUE, loot_everything = FALSE; + char buf[BUFSZ]; + const char *takeout = "Take out", *putin = "Put in"; + struct obj *otmp, *otmp2; + menu_item *pick_list; + int mflags, res; + long count; + + if (retry) { + all_categories = (retry == -2); + } else if (flags.menu_style == MENU_FULL) { + all_categories = FALSE; + Sprintf(buf,"%s what type of objects?", put_in ? putin : takeout); + mflags = put_in ? ALL_TYPES | BUC_ALLBKNOWN | BUC_UNKNOWN : + ALL_TYPES | CHOOSE_ALL | BUC_ALLBKNOWN | BUC_UNKNOWN; + n = query_category(buf, put_in ? invent : container->cobj, + mflags, &pick_list, PICK_ANY); + if (!n) return 0; + for (i = 0; i < n; i++) { + if (pick_list[i].item.a_int == 'A') + loot_everything = TRUE; + else if (pick_list[i].item.a_int == ALL_TYPES_SELECTED) + all_categories = TRUE; + else + add_valid_menu_class(pick_list[i].item.a_int); + } + free((genericptr_t) pick_list); + } + + if (loot_everything) { + for (otmp = container->cobj; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + res = out_container(otmp); + if (res < 0) break; + } + } else { + mflags = INVORDER_SORT; + if (put_in && flags.invlet_constant) mflags |= USE_INVLET; + Sprintf(buf,"%s what?", put_in ? putin : takeout); + n = query_objlist(buf, put_in ? invent : container->cobj, + mflags, &pick_list, PICK_ANY, + all_categories ? allow_all : allow_category); + if (n) { + n_looted = n; + for (i = 0; i < n; i++) { + otmp = pick_list[i].item.a_obj; + count = pick_list[i].count; + if (count > 0 && count < otmp->quan) { + otmp = splitobj(otmp, count); + /* special split case also handled by askchain() */ + } + res = put_in ? in_container(otmp) : out_container(otmp); + if (res < 0) { + if (otmp != pick_list[i].item.a_obj) { + /* split occurred, merge again */ + (void) merged(&pick_list[i].item.a_obj, &otmp); + } + break; + } + } + free((genericptr_t)pick_list); + } + } + return n_looted; +} + +STATIC_OVL int +in_or_out_menu(prompt, obj, outokay, inokay) +const char *prompt; +struct obj *obj; +boolean outokay, inokay; +{ + winid win; + anything any; + menu_item *pick_list; + char buf[BUFSZ]; + int n; + const char *menuselector = iflags.lootabc ? "abc" : "oib"; + + any.a_void = 0; + win = create_nhwindow(NHW_MENU); + start_menu(win); + if (outokay) { + any.a_int = 1; + Sprintf(buf,"Take %s out of %s", something, the(xname(obj))); + add_menu(win, NO_GLYPH, &any, *menuselector, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + menuselector++; + if (inokay) { + any.a_int = 2; + Sprintf(buf,"Put %s into %s", something, the(xname(obj))); + add_menu(win, NO_GLYPH, &any, *menuselector, 0, ATR_NONE, buf, MENU_UNSELECTED); + } + menuselector++; + if (outokay && inokay) { + any.a_int = 3; + add_menu(win, NO_GLYPH, &any, *menuselector, 0, ATR_NONE, + "Both of the above", MENU_UNSELECTED); + } + end_menu(win, prompt); + n = select_menu(win, PICK_ONE, &pick_list); + destroy_nhwindow(win); + if (n > 0) { + n = pick_list[0].item.a_int; + free((genericptr_t) pick_list); + } + return n; +} + +/*pickup.c*/ diff --git a/src/pline.c b/src/pline.c new file mode 100644 index 0000000..8210f8d --- /dev/null +++ b/src/pline.c @@ -0,0 +1,440 @@ +/* SCCS Id: @(#)pline.c 3.4 1999/11/28 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers */ +#include "hack.h" +#include "epri.h" +#ifdef WIZARD +#include "edog.h" +#endif + +#ifdef OVLB + +static boolean no_repeat = FALSE; + +static char *FDECL(You_buf, (int)); + +/*VARARGS1*/ +/* Note that these declarations rely on knowledge of the internals + * of the variable argument handling stuff in "tradstdc.h" + */ + +#if defined(USE_STDARG) || defined(USE_VARARGS) +static void FDECL(vpline, (const char *, va_list)); + +void +pline VA_DECL(const char *, line) + VA_START(line); + VA_INIT(line, char *); + vpline(line, VA_ARGS); + VA_END(); +} + +# ifdef USE_STDARG +static void +vpline(const char *line, va_list the_args) { +# else +static void +vpline(line, the_args) const char *line; va_list the_args; { +# endif + +#else /* USE_STDARG | USE_VARARG */ + +#define vpline pline + +void +pline VA_DECL(const char *, line) +#endif /* USE_STDARG | USE_VARARG */ + + char pbuf[BUFSZ]; +/* Do NOT use VA_START and VA_END in here... see above */ + + if (!line || !*line) return; + if (index(line, '%')) { + Vsprintf(pbuf,line,VA_ARGS); + line = pbuf; + } + if (!iflags.window_inited) { + raw_print(line); + return; + } +#ifndef MAC + if (no_repeat && !strcmp(line, toplines)) + return; +#endif /* MAC */ + if (vision_full_recalc) vision_recalc(0); + if (u.ux) flush_screen(1); /* %% */ + putstr(WIN_MESSAGE, 0, line); +} + +/*VARARGS1*/ +void +Norep VA_DECL(const char *, line) + VA_START(line); + VA_INIT(line, const char *); + no_repeat = TRUE; + vpline(line, VA_ARGS); + no_repeat = FALSE; + VA_END(); + return; +} + +/* work buffer for You(), &c and verbalize() */ +static char *you_buf = 0; +static int you_buf_siz = 0; + +static char * +You_buf(siz) +int siz; +{ + if (siz > you_buf_siz) { + if (you_buf) free((genericptr_t) you_buf); + you_buf_siz = siz + 10; + you_buf = (char *) alloc((unsigned) you_buf_siz); + } + return you_buf; +} + +void +free_youbuf() +{ + if (you_buf) free((genericptr_t) you_buf), you_buf = (char *)0; + you_buf_siz = 0; +} + +/* `prefix' must be a string literal, not a pointer */ +#define YouPrefix(pointer,prefix,text) \ + Strcpy((pointer = You_buf((int)(strlen(text) + sizeof prefix))), prefix) + +#define YouMessage(pointer,prefix,text) \ + strcat((YouPrefix(pointer, prefix, text), pointer), text) + +/*VARARGS1*/ +void +You VA_DECL(const char *, line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "You ", line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +Your VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "Your ", line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +You_feel VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "You feel ", line), VA_ARGS); + VA_END(); +} + + +/*VARARGS1*/ +void +You_cant VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "You can't ", line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +pline_The VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "The ", line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +There VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + vpline(YouMessage(tmp, "There ", line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +You_hear VA_DECL(const char *,line) + char *tmp; + VA_START(line); + VA_INIT(line, const char *); + if (Underwater) + YouPrefix(tmp, "You barely hear ", line); + else if (u.usleep) + YouPrefix(tmp, "You dream that you hear ", line); + else + YouPrefix(tmp, "You hear ", line); + vpline(strcat(tmp, line), VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +void +verbalize VA_DECL(const char *,line) + char *tmp; + if (!flags.soundok) return; + VA_START(line); + VA_INIT(line, const char *); + tmp = You_buf((int)strlen(line) + sizeof "\"\""); + Strcpy(tmp, "\""); + Strcat(tmp, line); + Strcat(tmp, "\""); + vpline(tmp, VA_ARGS); + VA_END(); +} + +/*VARARGS1*/ +/* Note that these declarations rely on knowledge of the internals + * of the variable argument handling stuff in "tradstdc.h" + */ + +#if defined(USE_STDARG) || defined(USE_VARARGS) +static void FDECL(vraw_printf,(const char *,va_list)); + +void +raw_printf VA_DECL(const char *, line) + VA_START(line); + VA_INIT(line, char *); + vraw_printf(line, VA_ARGS); + VA_END(); +} + +# ifdef USE_STDARG +static void +vraw_printf(const char *line, va_list the_args) { +# else +static void +vraw_printf(line, the_args) const char *line; va_list the_args; { +# endif + +#else /* USE_STDARG | USE_VARARG */ + +void +raw_printf VA_DECL(const char *, line) +#endif +/* Do NOT use VA_START and VA_END in here... see above */ + + if(!index(line, '%')) + raw_print(line); + else { + char pbuf[BUFSZ]; + Vsprintf(pbuf,line,VA_ARGS); + raw_print(pbuf); + } +} + + +/*VARARGS1*/ +void +impossible VA_DECL(const char *, s) + VA_START(s); + VA_INIT(s, const char *); + if (program_state.in_impossible) + panic("impossible called impossible"); + program_state.in_impossible = 1; + { + char pbuf[BUFSZ]; + Vsprintf(pbuf,s,VA_ARGS); + paniclog("impossible", pbuf); + } + vpline(s,VA_ARGS); + pline("Program in disorder - perhaps you'd better #quit."); + program_state.in_impossible = 0; + VA_END(); +} + +const char * +align_str(alignment) + aligntyp alignment; +{ + switch ((int)alignment) { + case A_CHAOTIC: return "chaotic"; + case A_NEUTRAL: return "neutral"; + case A_LAWFUL: return "lawful"; + case A_NONE: return "unaligned"; + } + return "unknown"; +} + +void +mstatusline(mtmp) +register struct monst *mtmp; +{ + aligntyp alignment; + char info[BUFSZ], monnambuf[BUFSZ]; + + if (mtmp->ispriest || mtmp->data == &mons[PM_ALIGNED_PRIEST] + || mtmp->data == &mons[PM_ANGEL]) + alignment = EPRI(mtmp)->shralign; + else + alignment = mtmp->data->maligntyp; + alignment = (alignment > 0) ? A_LAWFUL : + (alignment < 0) ? A_CHAOTIC : + A_NEUTRAL; + + info[0] = 0; + if (mtmp->mtame) { Strcat(info, ", tame"); +#ifdef WIZARD + if (wizard) { + Sprintf(eos(info), " (%d", mtmp->mtame); + if (!mtmp->isminion) + Sprintf(eos(info), "; hungry %ld; apport %d", + EDOG(mtmp)->hungrytime, EDOG(mtmp)->apport); + Strcat(info, ")"); + } +#endif + } + else if (mtmp->mpeaceful) Strcat(info, ", peaceful"); + if (mtmp->meating) Strcat(info, ", eating"); + if (mtmp->mcan) Strcat(info, ", cancelled"); + if (mtmp->mconf) Strcat(info, ", confused"); + if (mtmp->mblinded || !mtmp->mcansee) + Strcat(info, ", blind"); + if (mtmp->mstun) Strcat(info, ", stunned"); + if (mtmp->msleeping) Strcat(info, ", asleep"); +#if 0 /* unfortunately mfrozen covers temporary sleep and being busy + (donning armor, for instance) as well as paralysis */ + else if (mtmp->mfrozen) Strcat(info, ", paralyzed"); +#else + else if (mtmp->mfrozen || !mtmp->mcanmove) + Strcat(info, ", can't move"); +#endif + /* [arbitrary reason why it isn't moving] */ + else if (mtmp->mstrategy & STRAT_WAITMASK) + Strcat(info, ", meditating"); + else if (mtmp->mflee) Strcat(info, ", scared"); + if (mtmp->mtrapped) Strcat(info, ", trapped"); + if (mtmp->mspeed) Strcat(info, + mtmp->mspeed == MFAST ? ", fast" : + mtmp->mspeed == MSLOW ? ", slow" : + ", ???? speed"); + if (mtmp->mundetected) Strcat(info, ", concealed"); + if (mtmp->minvis) Strcat(info, ", invisible"); + if (mtmp == u.ustuck) Strcat(info, + (sticks(youmonst.data)) ? ", held by you" : + u.uswallow ? (is_animal(u.ustuck->data) ? + ", swallowed you" : + ", engulfed you") : + ", holding you"); +#ifdef STEED + if (mtmp == u.usteed) Strcat(info, ", carrying you"); +#endif + + /* avoid "Status of the invisible newt ..., invisible" */ + /* and unlike a normal mon_nam, use "saddled" even if it has a name */ + Strcpy(monnambuf, x_monnam(mtmp, ARTICLE_THE, (char *)0, + (SUPPRESS_IT|SUPPRESS_INVISIBLE), FALSE)); + + pline("Status of %s (%s): Level %d HP %d(%d) AC %d%s.", + monnambuf, + align_str(alignment), + mtmp->m_lev, + mtmp->mhp, + mtmp->mhpmax, + find_mac(mtmp), + info); +} + +void +ustatusline() +{ + char info[BUFSZ]; + + info[0] = '\0'; + if (Sick) { + Strcat(info, ", dying from"); + if (u.usick_type & SICK_VOMITABLE) + Strcat(info, " food poisoning"); + if (u.usick_type & SICK_NONVOMITABLE) { + if (u.usick_type & SICK_VOMITABLE) + Strcat(info, " and"); + Strcat(info, " illness"); + } + } + if (Stoned) Strcat(info, ", solidifying"); + if (Slimed) Strcat(info, ", becoming slimy"); + if (Strangled) Strcat(info, ", being strangled"); + if (Vomiting) Strcat(info, ", nauseated"); /* !"nauseous" */ + if (Confusion) Strcat(info, ", confused"); + if (Blind) { + Strcat(info, ", blind"); + if (u.ucreamed) { + if ((long)u.ucreamed < Blinded || Blindfolded + || !haseyes(youmonst.data)) + Strcat(info, ", cover"); + Strcat(info, "ed by sticky goop"); + } /* note: "goop" == "glop"; variation is intentional */ + } + if (Stunned) Strcat(info, ", stunned"); +#ifdef STEED + if (!u.usteed) +#endif + if (Wounded_legs) { + const char *what = body_part(LEG); + if ((Wounded_legs & BOTH_SIDES) == BOTH_SIDES) + what = makeplural(what); + Sprintf(eos(info), ", injured %s", what); + } + if (Glib) Sprintf(eos(info), ", slippery %s", + makeplural(body_part(HAND))); + if (u.utrap) Strcat(info, ", trapped"); + if (Fast) Strcat(info, Very_fast ? + ", very fast" : ", fast"); + if (u.uundetected) Strcat(info, ", concealed"); + if (Invis) Strcat(info, ", invisible"); + if (u.ustuck) { + if (sticks(youmonst.data)) + Strcat(info, ", holding "); + else + Strcat(info, ", held by "); + Strcat(info, mon_nam(u.ustuck)); + } + + pline("Status of %s (%s%s): Level %d HP %d(%d) AC %d%s.", + plname, + (u.ualign.record >= 20) ? "piously " : + (u.ualign.record > 13) ? "devoutly " : + (u.ualign.record > 8) ? "fervently " : + (u.ualign.record > 3) ? "stridently " : + (u.ualign.record == 3) ? "" : + (u.ualign.record >= 1) ? "haltingly " : + (u.ualign.record == 0) ? "nominally " : + "insufficiently ", + align_str(u.ualign.type), + Upolyd ? mons[u.umonnum].mlevel : u.ulevel, + Upolyd ? u.mh : u.uhp, + Upolyd ? u.mhmax : u.uhpmax, + u.uac, + info); +} + +void +self_invis_message() +{ + pline("%s %s.", + Hallucination ? "Far out, man! You" : "Gee! All of a sudden, you", + See_invisible ? "can see right through yourself" : + "can't see yourself"); +} + +#endif /* OVLB */ +/*pline.c*/ diff --git a/src/polyself.c b/src/polyself.c new file mode 100644 index 0000000..b051acb --- /dev/null +++ b/src/polyself.c @@ -0,0 +1,1339 @@ +/* SCCS Id: @(#)polyself.c 3.4 2003/01/08 */ +/* Copyright (C) 1987, 1988, 1989 by Ken Arromdee */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Polymorph self routine. + * + * Note: the light source handling code assumes that both youmonst.m_id + * and youmonst.mx will always remain 0 when it handles the case of the + * player polymorphed into a light-emitting monster. + */ + +#include "hack.h" + +#ifdef OVLB +STATIC_DCL void FDECL(polyman, (const char *,const char *)); +STATIC_DCL void NDECL(break_armor); +STATIC_DCL void FDECL(drop_weapon,(int)); +STATIC_DCL void NDECL(uunstick); +STATIC_DCL int FDECL(armor_to_dragon,(int)); +STATIC_DCL void NDECL(newman); + +/* update the youmonst.data structure pointer */ +void +set_uasmon() +{ + set_mon_data(&youmonst, &mons[u.umonnum], 0); +} + +/* make a (new) human out of the player */ +STATIC_OVL void +polyman(fmt, arg) +const char *fmt, *arg; +{ + boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow, + was_mimicking = (youmonst.m_ap_type == M_AP_OBJECT); + boolean could_pass_walls = Passes_walls; + boolean was_blind = !!Blind; + + if (Upolyd) { + u.acurr = u.macurr; /* restore old attribs */ + u.amax = u.mamax; + u.umonnum = u.umonster; + flags.female = u.mfemale; + } + set_uasmon(); + + u.mh = u.mhmax = 0; + u.mtimedone = 0; + skinback(FALSE); + u.uundetected = 0; + + if (sticky) uunstick(); + find_ac(); + if (was_mimicking) { + if (multi < 0) unmul(""); + youmonst.m_ap_type = M_AP_NOTHING; + } + + newsym(u.ux,u.uy); + + You(fmt, arg); + /* check whether player foolishly genocided self while poly'd */ + if ((mvitals[urole.malenum].mvflags & G_GENOD) || + (urole.femalenum != NON_PM && + (mvitals[urole.femalenum].mvflags & G_GENOD)) || + (mvitals[urace.malenum].mvflags & G_GENOD) || + (urace.femalenum != NON_PM && + (mvitals[urace.femalenum].mvflags & G_GENOD))) { + /* intervening activity might have clobbered genocide info */ + killer = delayed_killer; + if (!killer || !strstri(killer, "genocid")) { + killer_format = KILLED_BY; + killer = "self-genocide"; + } + done(GENOCIDED); + } + + if (u.twoweap && !could_twoweap(youmonst.data)) + untwoweapon(); + + if (u.utraptype == TT_PIT) { + if (could_pass_walls) { /* player forms cannot pass walls */ + u.utrap = rn1(6,2); + } + } + if (was_blind && !Blind) { /* reverting from eyeless */ + Blinded = 1L; + make_blinded(0L, TRUE); /* remove blindness */ + } + + if(!Levitation && !u.ustuck && + (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) + spoteffects(TRUE); + + see_monsters(); +} + +void +change_sex() +{ + /* setting u.umonster for caveman/cavewoman or priest/priestess + swap unintentionally makes `Upolyd' appear to be true */ + boolean already_polyd = (boolean) Upolyd; + + /* Some monsters are always of one sex and their sex can't be changed */ + /* succubi/incubi can change, but are handled below */ + /* !already_polyd check necessary because is_male() and is_female() + are true if the player is a priest/priestess */ + if (!already_polyd || (!is_male(youmonst.data) && !is_female(youmonst.data) && !is_neuter(youmonst.data))) + flags.female = !flags.female; + if (already_polyd) /* poly'd: also change saved sex */ + u.mfemale = !u.mfemale; + max_rank_sz(); /* [this appears to be superfluous] */ + if ((already_polyd ? u.mfemale : flags.female) && urole.name.f) + Strcpy(pl_character, urole.name.f); + else + Strcpy(pl_character, urole.name.m); + u.umonster = ((already_polyd ? u.mfemale : flags.female) && urole.femalenum != NON_PM) ? + urole.femalenum : urole.malenum; + if (!already_polyd) { + u.umonnum = u.umonster; + } else if (u.umonnum == PM_SUCCUBUS || u.umonnum == PM_INCUBUS) { + flags.female = !flags.female; + /* change monster type to match new sex */ + u.umonnum = (u.umonnum == PM_SUCCUBUS) ? PM_INCUBUS : PM_SUCCUBUS; + set_uasmon(); + } +} + +STATIC_OVL void +newman() +{ + int tmp, oldlvl; + + tmp = u.uhpmax; + oldlvl = u.ulevel; + u.ulevel = u.ulevel + rn1(5, -2); + if (u.ulevel > 127 || u.ulevel < 1) { /* level went below 0? */ + u.ulevel = oldlvl; /* restore old level in case they lifesave */ + goto dead; + } + if (u.ulevel > MAXULEV) u.ulevel = MAXULEV; + /* If your level goes down, your peak level goes down by + the same amount so that you can't simply use blessed + full healing to undo the decrease. But if your level + goes up, your peak level does *not* undergo the same + adjustment; you might end up losing out on the chance + to regain some levels previously lost to other causes. */ + if (u.ulevel < oldlvl) u.ulevelmax -= (oldlvl - u.ulevel); + if (u.ulevelmax < u.ulevel) u.ulevelmax = u.ulevel; + + if (!rn2(10)) change_sex(); + + adjabil(oldlvl, (int)u.ulevel); + reset_rndmonst(NON_PM); /* new monster generation criteria */ + + /* random experience points for the new experience level */ + u.uexp = rndexp(FALSE); + + /* u.uhpmax * u.ulevel / oldlvl: proportionate hit points to new level + * -10 and +10: don't apply proportionate HP to 10 of a starting + * character's hit points (since a starting character's hit points + * are not on the same scale with hit points obtained through level + * gain) + * 9 - rn2(19): random change of -9 to +9 hit points + */ +#ifndef LINT + u.uhpmax = ((u.uhpmax - 10) * (long)u.ulevel / oldlvl + 10) + + (9 - rn2(19)); +#endif + +#ifdef LINT + u.uhp = u.uhp + tmp; +#else + u.uhp = u.uhp * (long)u.uhpmax/tmp; +#endif + + tmp = u.uenmax; +#ifndef LINT + u.uenmax = u.uenmax * (long)u.ulevel / oldlvl + 9 - rn2(19); +#endif + if (u.uenmax < 0) u.uenmax = 0; +#ifndef LINT + u.uen = (tmp ? u.uen * (long)u.uenmax / tmp : u.uenmax); +#endif + + redist_attr(); + u.uhunger = rn1(500,500); + if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL); + Stoned = 0; + delayed_killer = 0; + if (u.uhp <= 0 || u.uhpmax <= 0) { + if (Polymorph_control) { + if (u.uhp <= 0) u.uhp = 1; + if (u.uhpmax <= 0) u.uhpmax = 1; + } else { +dead: /* we come directly here if their experience level went to 0 or less */ + Your("new form doesn't seem healthy enough to survive."); + killer_format = KILLED_BY_AN; + killer="unsuccessful polymorph"; + done(DIED); + newuhs(FALSE); + return; /* lifesaved */ + } + } + newuhs(FALSE); + polyman("feel like a new %s!", + (flags.female && urace.individual.f) ? urace.individual.f : + (urace.individual.m) ? urace.individual.m : urace.noun); + if (Slimed) { + Your("body transforms, but there is still slime on you."); + Slimed = 10L; + } + flags.botl = 1; + see_monsters(); + (void) encumber_msg(); +} + +void +polyself(forcecontrol) +boolean forcecontrol; +{ + char buf[BUFSZ]; + int old_light, new_light; + int mntmp = NON_PM; + int tries=0; + boolean draconian = (uarm && + uarm->otyp >= GRAY_DRAGON_SCALE_MAIL && + uarm->otyp <= YELLOW_DRAGON_SCALES); + boolean iswere = (u.ulycn >= LOW_PM || is_were(youmonst.data)); + boolean isvamp = (youmonst.data->mlet == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT); + boolean was_floating = (Levitation || Flying); + + if(!Polymorph_control && !forcecontrol && !draconian && !iswere && !isvamp) { + if (rn2(20) > ACURR(A_CON)) { + You(shudder_for_moment); + losehp(rnd(30), "system shock", KILLED_BY_AN); + exercise(A_CON, FALSE); + return; + } + } + old_light = Upolyd ? emits_light(youmonst.data) : 0; + + if (Polymorph_control || forcecontrol) { + do { + getlin("Become what kind of monster? [type the name]", + buf); + mntmp = name_to_mon(buf); + if (mntmp < LOW_PM) + pline("I've never heard of such monsters."); + /* Note: humans are illegal as monsters, but an + * illegal monster forces newman(), which is what we + * want if they specified a human.... */ + else if (!polyok(&mons[mntmp]) && !your_race(&mons[mntmp])) + You("cannot polymorph into that."); + else break; + } while(++tries < 5); + if (tries==5) pline(thats_enough_tries); + /* allow skin merging, even when polymorph is controlled */ + if (draconian && + (mntmp == armor_to_dragon(uarm->otyp) || tries == 5)) + goto do_merge; + } else if (draconian || iswere || isvamp) { + /* special changes that don't require polyok() */ + if (draconian) { + do_merge: + mntmp = armor_to_dragon(uarm->otyp); + if (!(mvitals[mntmp].mvflags & G_GENOD)) { + /* allow G_EXTINCT */ + You("merge with your scaly armor."); + uskin = uarm; + uarm = (struct obj *)0; + /* save/restore hack */ + uskin->owornmask |= I_SPECIAL; + } + } else if (iswere) { + if (is_were(youmonst.data)) + mntmp = PM_HUMAN; /* Illegal; force newman() */ + else + mntmp = u.ulycn; + } else { + if (youmonst.data->mlet == S_VAMPIRE) + mntmp = PM_VAMPIRE_BAT; + else + mntmp = PM_VAMPIRE; + } + /* if polymon fails, "you feel" message has been given + so don't follow up with another polymon or newman */ + if (mntmp == PM_HUMAN) newman(); /* werecritter */ + else (void) polymon(mntmp); + goto made_change; /* maybe not, but this is right anyway */ + } + + if (mntmp < LOW_PM) { + tries = 0; + do { + /* randomly pick an "ordinary" monster */ + mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM); + } while((!polyok(&mons[mntmp]) || is_placeholder(&mons[mntmp])) + && tries++ < 200); + } + + /* The below polyok() fails either if everything is genocided, or if + * we deliberately chose something illegal to force newman(). + */ + if (!polyok(&mons[mntmp]) || !rn2(5) || your_race(&mons[mntmp])) + newman(); + else if(!polymon(mntmp)) return; + + if (!uarmg) selftouch("No longer petrify-resistant, you"); + + made_change: + new_light = Upolyd ? emits_light(youmonst.data) : 0; + if (old_light != new_light) { + if (old_light) + del_light_source(LS_MONSTER, (genericptr_t)&youmonst); + if (new_light == 1) ++new_light; /* otherwise it's undetectable */ + if (new_light) + new_light_source(u.ux, u.uy, new_light, + LS_MONSTER, (genericptr_t)&youmonst); + } + if (is_pool(u.ux,u.uy) && was_floating && !(Levitation || Flying) && + !breathless(youmonst.data) && !amphibious(youmonst.data) && + !Swimming) drown(); +} + +/* (try to) make a mntmp monster out of the player */ +int +polymon(mntmp) /* returns 1 if polymorph successful */ +int mntmp; +{ + boolean sticky = sticks(youmonst.data) && u.ustuck && !u.uswallow, + was_blind = !!Blind, dochange = FALSE; + boolean could_pass_walls = Passes_walls; + int mlvl; + + if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */ + You_feel("rather %s-ish.",mons[mntmp].mname); + exercise(A_WIS, TRUE); + return(0); + } + + /* KMH, conduct */ + u.uconduct.polyselfs++; + + if (!Upolyd) { + /* Human to monster; save human stats */ + u.macurr = u.acurr; + u.mamax = u.amax; + u.mfemale = flags.female; + } else { + /* Monster to monster; restore human stats, to be + * immediately changed to provide stats for the new monster + */ + u.acurr = u.macurr; + u.amax = u.mamax; + flags.female = u.mfemale; + } + + if (youmonst.m_ap_type) { + /* stop mimicking immediately */ + if (multi < 0) unmul(""); + } else if (mons[mntmp].mlet != S_MIMIC) { + /* as in polyman() */ + youmonst.m_ap_type = M_AP_NOTHING; + } + if (is_male(&mons[mntmp])) { + if(flags.female) dochange = TRUE; + } else if (is_female(&mons[mntmp])) { + if(!flags.female) dochange = TRUE; + } else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) { + if(!rn2(10)) dochange = TRUE; + } + if (dochange) { + flags.female = !flags.female; + You("%s %s%s!", + (u.umonnum != mntmp) ? "turn into a" : "feel like a new", + (is_male(&mons[mntmp]) || is_female(&mons[mntmp])) ? "" : + flags.female ? "female " : "male ", + mons[mntmp].mname); + } else { + if (u.umonnum != mntmp) + You("turn into %s!", an(mons[mntmp].mname)); + else + You_feel("like a new %s!", mons[mntmp].mname); + } + if (Stoned && poly_when_stoned(&mons[mntmp])) { + /* poly_when_stoned already checked stone golem genocide */ + You("turn to stone!"); + mntmp = PM_STONE_GOLEM; + Stoned = 0; + delayed_killer = 0; + } + + u.mtimedone = rn1(500, 500); + u.umonnum = mntmp; + set_uasmon(); + + /* New stats for monster, to last only as long as polymorphed. + * Currently only strength gets changed. + */ + if(strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = STR18(100); + + if (Stone_resistance && Stoned) { /* parnes@eniac.seas.upenn.edu */ + Stoned = 0; + delayed_killer = 0; + You("no longer seem to be petrifying."); + } + if (Sick_resistance && Sick) { + make_sick(0L, (char *) 0, FALSE, SICK_ALL); + You("no longer feel sick."); + } + if (Slimed) { + if (flaming(youmonst.data)) { + pline_The("slime burns away!"); + Slimed = 0L; + flags.botl = 1; + } else if (mntmp == PM_GREEN_SLIME) { + /* do it silently */ + Slimed = 0L; + flags.botl = 1; + } + } + if (nohands(youmonst.data)) Glib = 0; + + /* + mlvl = adj_lev(&mons[mntmp]); + * We can't do the above, since there's no such thing as an + * "experience level of you as a monster" for a polymorphed character. + */ + mlvl = (int)mons[mntmp].mlevel; + if (youmonst.data->mlet == S_DRAGON && mntmp >= PM_GRAY_DRAGON) { + u.mhmax = In_endgame(&u.uz) ? (8*mlvl) : (4*mlvl + d(mlvl,4)); + } else if (is_golem(youmonst.data)) { + u.mhmax = golemhp(mntmp); + } else { + if (!mlvl) u.mhmax = rnd(4); + else u.mhmax = d(mlvl, 8); + if (is_home_elemental(&mons[mntmp])) u.mhmax *= 3; + } + u.mh = u.mhmax; + + if (u.ulevel < mlvl) { + /* Low level characters can't become high level monsters for long */ +#ifdef DUMB + /* DRS/NS 2.2.6 messes up -- Peter Kendell */ + int mtd = u.mtimedone, ulv = u.ulevel; + + u.mtimedone = mtd * ulv / mlvl; +#else + u.mtimedone = u.mtimedone * u.ulevel / mlvl; +#endif + } + + if (uskin && mntmp != armor_to_dragon(uskin->otyp)) + skinback(FALSE); + break_armor(); + drop_weapon(1); + if (hides_under(youmonst.data)) + u.uundetected = OBJ_AT(u.ux, u.uy); + else if (youmonst.data->mlet == S_EEL) + u.uundetected = is_pool(u.ux, u.uy); + else + u.uundetected = 0; + + if (u.utraptype == TT_PIT) { + if (could_pass_walls && !Passes_walls) { + u.utrap = rn1(6,2); + } else if (!could_pass_walls && Passes_walls) { + u.utrap = 0; + } + } + if (was_blind && !Blind) { /* previous form was eyeless */ + Blinded = 1L; + make_blinded(0L, TRUE); /* remove blindness */ + } + newsym(u.ux,u.uy); /* Change symbol */ + + if (!sticky && !u.uswallow && u.ustuck && sticks(youmonst.data)) u.ustuck = 0; + else if (sticky && !sticks(youmonst.data)) uunstick(); +#ifdef STEED + if (u.usteed) { + if (touch_petrifies(u.usteed->data) && + !Stone_resistance && rnl(3)) { + char buf[BUFSZ]; + + pline("No longer petrifying-resistant, you touch %s.", + mon_nam(u.usteed)); + Sprintf(buf, "riding %s", an(u.usteed->data->mname)); + instapetrify(buf); + } + if (!can_ride(u.usteed)) dismount_steed(DISMOUNT_POLY); + } +#endif + + if (flags.verbose) { + static const char use_thec[] = "Use the command #%s to %s."; + static const char monsterc[] = "monster"; + if (can_breathe(youmonst.data)) + pline(use_thec,monsterc,"use your breath weapon"); + if (attacktype(youmonst.data, AT_SPIT)) + pline(use_thec,monsterc,"spit venom"); + if (youmonst.data->mlet == S_NYMPH) + pline(use_thec,monsterc,"remove an iron ball"); + if (attacktype(youmonst.data, AT_GAZE)) + pline(use_thec,monsterc,"gaze at monsters"); + if (is_hider(youmonst.data)) + pline(use_thec,monsterc,"hide"); + if (is_were(youmonst.data)) + pline(use_thec,monsterc,"summon help"); + if (webmaker(youmonst.data)) + pline(use_thec,monsterc,"spin a web"); + if (u.umonnum == PM_GREMLIN) + pline(use_thec,monsterc,"multiply in a fountain"); + if (is_unicorn(youmonst.data)) + pline(use_thec,monsterc,"use your horn"); + if (is_mind_flayer(youmonst.data)) + pline(use_thec,monsterc,"emit a mental blast"); + if (youmonst.data->msound == MS_SHRIEK) /* worthless, actually */ + pline(use_thec,monsterc,"shriek"); + if (lays_eggs(youmonst.data) && flags.female) + pline(use_thec,"sit","lay an egg"); + } + /* you now know what an egg of your type looks like */ + if (lays_eggs(youmonst.data)) { + learn_egg_type(u.umonnum); + /* make queen bees recognize killer bee eggs */ + learn_egg_type(egg_type_from_parent(u.umonnum, TRUE)); + } + find_ac(); + if((!Levitation && !u.ustuck && !Flying && + (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) || + (Underwater && !Swimming)) + spoteffects(TRUE); + if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) { + u.utrap = 0; + pline_The("rock seems to no longer trap you."); + } else if (likes_lava(youmonst.data) && u.utrap && u.utraptype == TT_LAVA) { + u.utrap = 0; + pline_The("lava now feels soothing."); + } + if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) { + if (Punished) { + You("slip out of the iron chain."); + unpunish(); + } + } + if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) && + (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data) || + (youmonst.data->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) { + You("are no longer stuck in the %s.", + u.utraptype == TT_WEB ? "web" : "bear trap"); + /* probably should burn webs too if PM_FIRE_ELEMENTAL */ + u.utrap = 0; + } + if (webmaker(youmonst.data) && u.utrap && u.utraptype == TT_WEB) { + You("orient yourself on the web."); + u.utrap = 0; + } + flags.botl = 1; + vision_full_recalc = 1; + see_monsters(); + exercise(A_CON, FALSE); + exercise(A_WIS, TRUE); + (void) encumber_msg(); + return(1); +} + +STATIC_OVL void +break_armor() +{ + register struct obj *otmp; + + if (breakarm(youmonst.data)) { + if ((otmp = uarm) != 0) { + if (donning(otmp)) cancel_don(); + You("break out of your armor!"); + exercise(A_STR, FALSE); + (void) Armor_gone(); + useup(otmp); + } + if ((otmp = uarmc) != 0) { + if(otmp->oartifact) { + Your("%s falls off!", cloak_simple_name(otmp)); + (void) Cloak_off(); + dropx(otmp); + } else { + Your("%s tears apart!", cloak_simple_name(otmp)); + (void) Cloak_off(); + useup(otmp); + } + } +#ifdef TOURIST + if (uarmu) { + Your("shirt rips to shreds!"); + useup(uarmu); + } +#endif + } else if (sliparm(youmonst.data)) { + if (((otmp = uarm) != 0) && (racial_exception(&youmonst, otmp) < 1)) { + if (donning(otmp)) cancel_don(); + Your("armor falls around you!"); + (void) Armor_gone(); + dropx(otmp); + } + if ((otmp = uarmc) != 0) { + if (is_whirly(youmonst.data)) + Your("%s falls, unsupported!", cloak_simple_name(otmp)); + else You("shrink out of your %s!", cloak_simple_name(otmp)); + (void) Cloak_off(); + dropx(otmp); + } +#ifdef TOURIST + if ((otmp = uarmu) != 0) { + if (is_whirly(youmonst.data)) + You("seep right through your shirt!"); + else You("become much too small for your shirt!"); + setworn((struct obj *)0, otmp->owornmask & W_ARMU); + dropx(otmp); + } +#endif + } + if (has_horns(youmonst.data)) { + if ((otmp = uarmh) != 0) { + if (is_flimsy(otmp) && !donning(otmp)) { + char hornbuf[BUFSZ], yourbuf[BUFSZ]; + + /* Future possiblities: This could damage/destroy helmet */ + Sprintf(hornbuf, "horn%s", plur(num_horns(youmonst.data))); + Your("%s %s through %s %s.", hornbuf, vtense(hornbuf, "pierce"), + shk_your(yourbuf, otmp), xname(otmp)); + } else { + if (donning(otmp)) cancel_don(); + Your("helmet falls to the %s!", surface(u.ux, u.uy)); + (void) Helmet_off(); + dropx(otmp); + } + } + } + if (nohands(youmonst.data) || verysmall(youmonst.data)) { + if ((otmp = uarmg) != 0) { + if (donning(otmp)) cancel_don(); + /* Drop weapon along with gloves */ + You("drop your gloves%s!", uwep ? " and weapon" : ""); + drop_weapon(0); + (void) Gloves_off(); + dropx(otmp); + } + if ((otmp = uarms) != 0) { + You("can no longer hold your shield!"); + (void) Shield_off(); + dropx(otmp); + } + if ((otmp = uarmh) != 0) { + if (donning(otmp)) cancel_don(); + Your("helmet falls to the %s!", surface(u.ux, u.uy)); + (void) Helmet_off(); + dropx(otmp); + } + } + if (nohands(youmonst.data) || verysmall(youmonst.data) || + slithy(youmonst.data) || youmonst.data->mlet == S_CENTAUR) { + if ((otmp = uarmf) != 0) { + if (donning(otmp)) cancel_don(); + if (is_whirly(youmonst.data)) + Your("boots fall away!"); + else Your("boots %s off your feet!", + verysmall(youmonst.data) ? "slide" : "are pushed"); + (void) Boots_off(); + dropx(otmp); + } + } +} + +STATIC_OVL void +drop_weapon(alone) +int alone; +{ + struct obj *otmp; + struct obj *otmp2; + + if ((otmp = uwep) != 0) { + /* !alone check below is currently superfluous but in the + * future it might not be so if there are monsters which cannot + * wear gloves but can wield weapons + */ + if (!alone || cantwield(youmonst.data)) { + struct obj *wep = uwep; + + if (alone) You("find you must drop your weapon%s!", + u.twoweap ? "s" : ""); + otmp2 = u.twoweap ? uswapwep : 0; + uwepgone(); + if (!wep->cursed || wep->otyp != LOADSTONE) + dropx(otmp); + if (otmp2 != 0) { + uswapwepgone(); + if (!otmp2->cursed || otmp2->otyp != LOADSTONE) + dropx(otmp2); + } + untwoweapon(); + } else if (!could_twoweap(youmonst.data)) { + untwoweapon(); + } + } +} + +void +rehumanize() +{ + /* You can't revert back while unchanging */ + if (Unchanging && (u.mh < 1)) { + killer_format = NO_KILLER_PREFIX; + killer = "killed while stuck in creature form"; + done(DIED); + } + + if (emits_light(youmonst.data)) + del_light_source(LS_MONSTER, (genericptr_t)&youmonst); + polyman("return to %s form!", urace.adj); + + if (u.uhp < 1) { + char kbuf[256]; + + Sprintf(kbuf, "reverting to unhealthy %s form", urace.adj); + killer_format = KILLED_BY; + killer = kbuf; + done(DIED); + } + if (!uarmg) selftouch("No longer petrify-resistant, you"); + nomul(0); + + flags.botl = 1; + vision_full_recalc = 1; + (void) encumber_msg(); +} + +int +dobreathe() +{ + struct attack *mattk; + + if (Strangled) { + You_cant("breathe. Sorry."); + return(0); + } + if (u.uen < 15) { + You("don't have enough energy to breathe!"); + return(0); + } + u.uen -= 15; + flags.botl = 1; + + if (!getdir((char *)0)) return(0); + + mattk = attacktype_fordmg(youmonst.data, AT_BREA, AD_ANY); + if (!mattk) + impossible("bad breath attack?"); /* mouthwash needed... */ + else + buzz((int) (20 + mattk->adtyp-1), (int)mattk->damn, + u.ux, u.uy, u.dx, u.dy); + return(1); +} + +int +dospit() +{ + struct obj *otmp; + + if (!getdir((char *)0)) return(0); + otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM, + TRUE, FALSE); + otmp->spe = 1; /* to indicate it's yours */ + throwit(otmp, 0L, FALSE); + return(1); +} + +int +doremove() +{ + if (!Punished) { + You("are not chained to anything!"); + return(0); + } + unpunish(); + return(1); +} + +int +dospinweb() +{ + register struct trap *ttmp = t_at(u.ux,u.uy); + + if (Levitation || Is_airlevel(&u.uz) + || Underwater || Is_waterlevel(&u.uz)) { + You("must be on the ground to spin a web."); + return(0); + } + if (u.uswallow) { + You("release web fluid inside %s.", mon_nam(u.ustuck)); + if (is_animal(u.ustuck->data)) { + expels(u.ustuck, u.ustuck->data, TRUE); + return(0); + } + if (is_whirly(u.ustuck->data)) { + int i; + + for (i = 0; i < NATTK; i++) + if (u.ustuck->data->mattk[i].aatyp == AT_ENGL) + break; + if (i == NATTK) + impossible("Swallower has no engulfing attack?"); + else { + char sweep[30]; + + sweep[0] = '\0'; + switch(u.ustuck->data->mattk[i].adtyp) { + case AD_FIRE: + Strcpy(sweep, "ignites and "); + break; + case AD_ELEC: + Strcpy(sweep, "fries and "); + break; + case AD_COLD: + Strcpy(sweep, + "freezes, shatters and "); + break; + } + pline_The("web %sis swept away!", sweep); + } + return(0); + } /* default: a nasty jelly-like creature */ + pline_The("web dissolves into %s.", mon_nam(u.ustuck)); + return(0); + } + if (u.utrap) { + You("cannot spin webs while stuck in a trap."); + return(0); + } + exercise(A_DEX, TRUE); + if (ttmp) switch (ttmp->ttyp) { + case PIT: + case SPIKED_PIT: You("spin a web, covering up the pit."); + deltrap(ttmp); + bury_objs(u.ux, u.uy); + newsym(u.ux, u.uy); + return(1); + case SQKY_BOARD: pline_The("squeaky board is muffled."); + deltrap(ttmp); + newsym(u.ux, u.uy); + return(1); + case TELEP_TRAP: + case LEVEL_TELEP: + case MAGIC_PORTAL: + Your("webbing vanishes!"); + return(0); + case WEB: You("make the web thicker."); + return(1); + case HOLE: + case TRAPDOOR: + You("web over the %s.", + (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole"); + deltrap(ttmp); + newsym(u.ux, u.uy); + return 1; + case ROLLING_BOULDER_TRAP: + You("spin a web, jamming the trigger."); + deltrap(ttmp); + newsym(u.ux, u.uy); + return(1); + case ARROW_TRAP: + case DART_TRAP: + case BEAR_TRAP: + case ROCKTRAP: + case FIRE_TRAP: + case LANDMINE: + case SLP_GAS_TRAP: + case RUST_TRAP: + case MAGIC_TRAP: + case ANTI_MAGIC: + case POLY_TRAP: + You("have triggered a trap!"); + dotrap(ttmp, 0); + return(1); + default: + impossible("Webbing over trap type %d?", ttmp->ttyp); + return(0); + } + else if (On_stairs(u.ux, u.uy)) { + /* cop out: don't let them hide the stairs */ + Your("web fails to impede access to the %s.", + (levl[u.ux][u.uy].typ == STAIRS) ? "stairs" : "ladder"); + return(1); + + } + ttmp = maketrap(u.ux, u.uy, WEB); + if (ttmp) { + ttmp->tseen = 1; + ttmp->madeby_u = 1; + } + newsym(u.ux, u.uy); + return(1); +} + +int +dosummon() +{ + int placeholder; + if (u.uen < 10) { + You("lack the energy to send forth a call for help!"); + return(0); + } + u.uen -= 10; + flags.botl = 1; + + You("call upon your brethren for help!"); + exercise(A_WIS, TRUE); + if (!were_summon(youmonst.data, TRUE, &placeholder, (char *)0)) + pline("But none arrive."); + return(1); +} + +int +dogaze() +{ + register struct monst *mtmp; + int looked = 0; + char qbuf[QBUFSZ]; + int i; + uchar adtyp = 0; + + for (i = 0; i < NATTK; i++) { + if(youmonst.data->mattk[i].aatyp == AT_GAZE) { + adtyp = youmonst.data->mattk[i].adtyp; + break; + } + } + if (adtyp != AD_CONF && adtyp != AD_FIRE) { + impossible("gaze attack %d?", adtyp); + return 0; + } + + + if (Blind) { + You_cant("see anything to gaze at."); + return 0; + } + if (u.uen < 15) { + You("lack the energy to use your special gaze!"); + return(0); + } + u.uen -= 15; + flags.botl = 1; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) { + looked++; + if (Invis && !perceives(mtmp->data)) + pline("%s seems not to notice your gaze.", Monnam(mtmp)); + else if (mtmp->minvis && !See_invisible) + You_cant("see where to gaze at %s.", Monnam(mtmp)); + else if (mtmp->m_ap_type == M_AP_FURNITURE + || mtmp->m_ap_type == M_AP_OBJECT) { + looked--; + continue; + } else if (flags.safe_dog && !Confusion && !Hallucination + && mtmp->mtame) { + You("avoid gazing at %s.", y_monnam(mtmp)); + } else { + if (flags.confirm && mtmp->mpeaceful && !Confusion + && !Hallucination) { + Sprintf(qbuf, "Really %s %s?", + (adtyp == AD_CONF) ? "confuse" : "attack", + mon_nam(mtmp)); + if (yn(qbuf) != 'y') continue; + setmangry(mtmp); + } + if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleeping || + !mtmp->mcansee || !haseyes(mtmp->data)) { + looked--; + continue; + } + /* No reflection check for consistency with when a monster + * gazes at *you*--only medusa gaze gets reflected then. + */ + if (adtyp == AD_CONF) { + if (!mtmp->mconf) + Your("gaze confuses %s!", mon_nam(mtmp)); + else + pline("%s is getting more and more confused.", + Monnam(mtmp)); + mtmp->mconf = 1; + } else if (adtyp == AD_FIRE) { + int dmg = d(2,6); + You("attack %s with a fiery gaze!", mon_nam(mtmp)); + if (resists_fire(mtmp)) { + pline_The("fire doesn't burn %s!", mon_nam(mtmp)); + dmg = 0; + } + if((int) u.ulevel > rn2(20)) + (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); + if((int) u.ulevel > rn2(20)) + (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); + if((int) u.ulevel > rn2(25)) + (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); + if (dmg && !DEADMONSTER(mtmp)) mtmp->mhp -= dmg; + if (mtmp->mhp <= 0) killed(mtmp); + } + /* For consistency with passive() in uhitm.c, this only + * affects you if the monster is still alive. + */ + if (!DEADMONSTER(mtmp) && + (mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) { + if (!Free_action) { + You("are frozen by %s gaze!", + s_suffix(mon_nam(mtmp))); + nomul((u.ulevel > 6 || rn2(4)) ? + -d((int)mtmp->m_lev+1, + (int)mtmp->data->mattk[0].damd) + : -200); + return 1; + } else + You("stiffen momentarily under %s gaze.", + s_suffix(mon_nam(mtmp))); + } + /* Technically this one shouldn't affect you at all because + * the Medusa gaze is an active monster attack that only + * works on the monster's turn, but for it to *not* have an + * effect would be too weird. + */ + if (!DEADMONSTER(mtmp) && + (mtmp->data == &mons[PM_MEDUSA]) && !mtmp->mcan) { + pline( + "Gazing at the awake %s is not a very good idea.", + l_monnam(mtmp)); + /* as if gazing at a sleeping anything is fruitful... */ + You("turn to stone..."); + killer_format = KILLED_BY; + killer = "deliberately meeting Medusa's gaze"; + done(STONING); + } + } + } + } + if (!looked) You("gaze at no place in particular."); + return 1; +} + +int +dohide() +{ + boolean ismimic = youmonst.data->mlet == S_MIMIC; + + if (u.uundetected || (ismimic && youmonst.m_ap_type != M_AP_NOTHING)) { + You("are already hiding."); + return(0); + } + if (ismimic) { + /* should bring up a dialog "what would you like to imitate?" */ + youmonst.m_ap_type = M_AP_OBJECT; + youmonst.mappearance = STRANGE_OBJECT; + } else + u.uundetected = 1; + newsym(u.ux,u.uy); + return(1); +} + +int +domindblast() +{ + struct monst *mtmp, *nmon; + + if (u.uen < 10) { + You("concentrate but lack the energy to maintain doing so."); + return(0); + } + u.uen -= 10; + flags.botl = 1; + + You("concentrate."); + pline("A wave of psychic energy pours out."); + for(mtmp=fmon; mtmp; mtmp = nmon) { + int u_sen; + + nmon = mtmp->nmon; + if (DEADMONSTER(mtmp)) + continue; + if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM) + continue; + if(mtmp->mpeaceful) + continue; + u_sen = telepathic(mtmp->data) && !mtmp->mcansee; + if (u_sen || (telepathic(mtmp->data) && rn2(2)) || !rn2(10)) { + You("lock in on %s %s.", s_suffix(mon_nam(mtmp)), + u_sen ? "telepathy" : + telepathic(mtmp->data) ? "latent telepathy" : + "mind"); + mtmp->mhp -= rnd(15); + if (mtmp->mhp <= 0) + killed(mtmp); + } + } + return 1; +} + +STATIC_OVL void +uunstick() +{ + pline("%s is no longer in your clutches.", Monnam(u.ustuck)); + u.ustuck = 0; +} + +void +skinback(silently) +boolean silently; +{ + if (uskin) { + if (!silently) Your("skin returns to its original form."); + uarm = uskin; + uskin = (struct obj *)0; + /* undo save/restore hack */ + uarm->owornmask &= ~I_SPECIAL; + } +} + +#endif /* OVLB */ +#ifdef OVL1 + +const char * +mbodypart(mon, part) +struct monst *mon; +int part; +{ + static NEARDATA const char + *humanoid_parts[] = { "arm", "eye", "face", "finger", + "fingertip", "foot", "hand", "handed", "head", "leg", + "light headed", "neck", "spine", "toe", "hair", + "blood", "lung", "nose", "stomach"}, + *jelly_parts[] = { "pseudopod", "dark spot", "front", + "pseudopod extension", "pseudopod extremity", + "pseudopod root", "grasp", "grasped", "cerebral area", + "lower pseudopod", "viscous", "middle", "surface", + "pseudopod extremity", "ripples", "juices", + "surface", "sensor", "stomach" }, + *animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip", + "rear claw", "foreclaw", "clawed", "head", "rear limb", + "light headed", "neck", "spine", "rear claw tip", + "fur", "blood", "lung", "nose", "stomach" }, + *bird_parts[] = { "wing", "eye", "face", "wing", "wing tip", + "foot", "wing", "winged", "head", "leg", + "light headed", "neck", "spine", "toe", + "feathers", "blood", "lung", "bill", "stomach" }, + *horse_parts[] = { "foreleg", "eye", "face", "forehoof", "hoof tip", + "rear hoof", "foreclaw", "hooved", "head", "rear leg", + "light headed", "neck", "backbone", "rear hoof tip", + "mane", "blood", "lung", "nose", "stomach"}, + *sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle", + "tentacle tip", "lower appendage", "tentacle", "tentacled", + "body", "lower tentacle", "rotational", "equator", "body", + "lower tentacle tip", "cilia", "life force", "retina", + "olfactory nerve", "interior" }, + *fungus_parts[] = { "mycelium", "visual area", "front", "hypha", + "hypha", "root", "strand", "stranded", "cap area", + "rhizome", "sporulated", "stalk", "root", "rhizome tip", + "spores", "juices", "gill", "gill", "interior" }, + *vortex_parts[] = { "region", "eye", "front", "minor current", + "minor current", "lower current", "swirl", "swirled", + "central core", "lower current", "addled", "center", + "currents", "edge", "currents", "life force", + "center", "leading edge", "interior" }, + *snake_parts[] = { "vestigial limb", "eye", "face", "large scale", + "large scale tip", "rear region", "scale gap", "scale gapped", + "head", "rear region", "light headed", "neck", "length", + "rear scale", "scales", "blood", "lung", "forked tongue", "stomach" }, + *fish_parts[] = { "fin", "eye", "premaxillary", "pelvic axillary", + "pelvic fin", "anal fin", "pectoral fin", "finned", "head", "peduncle", + "played out", "gills", "dorsal fin", "caudal fin", + "scales", "blood", "gill", "nostril", "stomach" }; + /* claw attacks are overloaded in mons[]; most humanoids with + such attacks should still reference hands rather than claws */ + static const char not_claws[] = { + S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL, + S_NYMPH, S_LEPRECHAUN, S_QUANTMECH, S_VAMPIRE, + S_ORC, S_GIANT, /* quest nemeses */ + '\0' /* string terminator; assert( S_xxx != 0 ); */ + }; + struct permonst *mptr = mon->data; + + if (part == HAND || part == HANDED) { /* some special cases */ + if (mptr->mlet == S_DOG || mptr->mlet == S_FELINE || + mptr->mlet == S_YETI) + return part == HAND ? "paw" : "pawed"; + if (humanoid(mptr) && attacktype(mptr, AT_CLAW) && + !index(not_claws, mptr->mlet) && + mptr != &mons[PM_STONE_GOLEM] && + mptr != &mons[PM_INCUBUS] && mptr != &mons[PM_SUCCUBUS]) + return part == HAND ? "claw" : "clawed"; + } + if ((mptr == &mons[PM_MUMAK] || mptr == &mons[PM_MASTODON]) && + part == NOSE) + return "trunk"; + if (mptr == &mons[PM_SHARK] && part == HAIR) + return "skin"; /* sharks don't have scales */ + if (mptr == &mons[PM_JELLYFISH] && (part == ARM || part == FINGER || + part == HAND || part == FOOT || part == TOE)) + return "tentacle"; + if (mptr == &mons[PM_FLOATING_EYE] && part == EYE) + return "cornea"; + if (humanoid(mptr) && + (part == ARM || part == FINGER || part == FINGERTIP || + part == HAND || part == HANDED)) + return humanoid_parts[part]; + if (mptr == &mons[PM_RAVEN]) + return bird_parts[part]; + if (mptr->mlet == S_CENTAUR || mptr->mlet == S_UNICORN || + (mptr == &mons[PM_ROTHE] && part != HAIR)) + return horse_parts[part]; + if (mptr->mlet == S_LIGHT) { + if (part == HANDED) return "rayed"; + else if (part == ARM || part == FINGER || + part == FINGERTIP || part == HAND) return "ray"; + else return "beam"; + } + if (mptr->mlet == S_EEL && mptr != &mons[PM_JELLYFISH]) + return fish_parts[part]; + if (slithy(mptr) || (mptr->mlet == S_DRAGON && part == HAIR)) + return snake_parts[part]; + if (mptr->mlet == S_EYE) + return sphere_parts[part]; + if (mptr->mlet == S_JELLY || mptr->mlet == S_PUDDING || + mptr->mlet == S_BLOB || mptr == &mons[PM_JELLYFISH]) + return jelly_parts[part]; + if (mptr->mlet == S_VORTEX || mptr->mlet == S_ELEMENTAL) + return vortex_parts[part]; + if (mptr->mlet == S_FUNGUS) + return fungus_parts[part]; + if (humanoid(mptr)) + return humanoid_parts[part]; + return animal_parts[part]; +} + +const char * +body_part(part) +int part; +{ + return mbodypart(&youmonst, part); +} + +#endif /* OVL1 */ +#ifdef OVL0 + +int +poly_gender() +{ +/* Returns gender of polymorphed player; 0/1=same meaning as flags.female, + * 2=none. + */ + if (is_neuter(youmonst.data) || !humanoid(youmonst.data)) return 2; + return flags.female; +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +ugolemeffects(damtype, dam) +int damtype, dam; +{ + int heal = 0; + /* We won't bother with "slow"/"haste" since players do not + * have a monster-specific slow/haste so there is no way to + * restore the old velocity once they are back to human. + */ + if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM) + return; + switch (damtype) { + case AD_ELEC: if (u.umonnum == PM_FLESH_GOLEM) + heal = dam / 6; /* Approx 1 per die */ + break; + case AD_FIRE: if (u.umonnum == PM_IRON_GOLEM) + heal = dam; + break; + } + if (heal && (u.mh < u.mhmax)) { + u.mh += heal; + if (u.mh > u.mhmax) u.mh = u.mhmax; + flags.botl = 1; + pline("Strangely, you feel better than before."); + exercise(A_STR, TRUE); + } +} + +STATIC_OVL int +armor_to_dragon(atyp) +int atyp; +{ + switch(atyp) { + case GRAY_DRAGON_SCALE_MAIL: + case GRAY_DRAGON_SCALES: + return PM_GRAY_DRAGON; + case SILVER_DRAGON_SCALE_MAIL: + case SILVER_DRAGON_SCALES: + return PM_SILVER_DRAGON; +#if 0 /* DEFERRED */ + case SHIMMERING_DRAGON_SCALE_MAIL: + case SHIMMERING_DRAGON_SCALES: + return PM_SHIMMERING_DRAGON; +#endif + case RED_DRAGON_SCALE_MAIL: + case RED_DRAGON_SCALES: + return PM_RED_DRAGON; + case ORANGE_DRAGON_SCALE_MAIL: + case ORANGE_DRAGON_SCALES: + return PM_ORANGE_DRAGON; + case WHITE_DRAGON_SCALE_MAIL: + case WHITE_DRAGON_SCALES: + return PM_WHITE_DRAGON; + case BLACK_DRAGON_SCALE_MAIL: + case BLACK_DRAGON_SCALES: + return PM_BLACK_DRAGON; + case BLUE_DRAGON_SCALE_MAIL: + case BLUE_DRAGON_SCALES: + return PM_BLUE_DRAGON; + case GREEN_DRAGON_SCALE_MAIL: + case GREEN_DRAGON_SCALES: + return PM_GREEN_DRAGON; + case YELLOW_DRAGON_SCALE_MAIL: + case YELLOW_DRAGON_SCALES: + return PM_YELLOW_DRAGON; + default: + return -1; + } +} + +#endif /* OVLB */ + +/*polyself.c*/ diff --git a/src/potion.c b/src/potion.c new file mode 100644 index 0000000..04abd41 --- /dev/null +++ b/src/potion.c @@ -0,0 +1,2012 @@ +/* SCCS Id: @(#)potion.c 3.4 2002/10/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVLB +boolean notonhead = FALSE; + +static NEARDATA int nothing, unkn; +static NEARDATA const char beverages[] = { POTION_CLASS, 0 }; + +STATIC_DCL long FDECL(itimeout, (long)); +STATIC_DCL long FDECL(itimeout_incr, (long,int)); +STATIC_DCL void NDECL(ghost_from_bottle); +STATIC_DCL short FDECL(mixtype, (struct obj *,struct obj *)); + +/* force `val' to be within valid range for intrinsic timeout value */ +STATIC_OVL long +itimeout(val) +long val; +{ + if (val >= TIMEOUT) val = TIMEOUT; + else if (val < 1) val = 0; + + return val; +} + +/* increment `old' by `incr' and force result to be valid intrinsic timeout */ +STATIC_OVL long +itimeout_incr(old, incr) +long old; +int incr; +{ + return itimeout((old & TIMEOUT) + (long)incr); +} + +/* set the timeout field of intrinsic `which' */ +void +set_itimeout(which, val) +long *which, val; +{ + *which &= ~TIMEOUT; + *which |= itimeout(val); +} + +/* increment the timeout field of intrinsic `which' */ +void +incr_itimeout(which, incr) +long *which; +int incr; +{ + set_itimeout(which, itimeout_incr(*which, incr)); +} + +void +make_confused(xtime,talk) +long xtime; +boolean talk; +{ + long old = HConfusion; + + if (!xtime && old) { + if (talk) + You_feel("less %s now.", + Hallucination ? "trippy" : "confused"); + } + if ((xtime && !old) || (!xtime && old)) flags.botl = TRUE; + + set_itimeout(&HConfusion, xtime); +} + +void +make_stunned(xtime,talk) +long xtime; +boolean talk; +{ + long old = HStun; + + if (!xtime && old) { + if (talk) + You_feel("%s now.", + Hallucination ? "less wobbly" : "a bit steadier"); + } + if (xtime && !old) { + if (talk) { +#ifdef STEED + if (u.usteed) + You("wobble in the saddle."); + else +#endif + You("%s...", stagger(youmonst.data, "stagger")); + } + } + if ((!xtime && old) || (xtime && !old)) flags.botl = TRUE; + + set_itimeout(&HStun, xtime); +} + +void +make_sick(xtime, cause, talk, type) +long xtime; +const char *cause; /* sickness cause */ +boolean talk; +int type; +{ + long old = Sick; + + if (xtime > 0L) { + if (Sick_resistance) return; + if (!old) { + /* newly sick */ + You_feel("deathly sick."); + } else { + /* already sick */ + if (talk) You_feel("%s worse.", + xtime <= Sick/2L ? "much" : "even"); + } + set_itimeout(&Sick, xtime); + u.usick_type |= type; + flags.botl = TRUE; + } else if (old && (type & u.usick_type)) { + /* was sick, now not */ + u.usick_type &= ~type; + if (u.usick_type) { /* only partly cured */ + if (talk) You_feel("somewhat better."); + set_itimeout(&Sick, Sick * 2); /* approximation */ + } else { + if (talk) pline("What a relief!"); + Sick = 0L; /* set_itimeout(&Sick, 0L) */ + } + flags.botl = TRUE; + } + + if (Sick) { + exercise(A_CON, FALSE); + if (cause) { + (void) strncpy(u.usick_cause, cause, sizeof(u.usick_cause)); + u.usick_cause[sizeof(u.usick_cause)-1] = 0; + } + else + u.usick_cause[0] = 0; + } else + u.usick_cause[0] = 0; +} + +void +make_vomiting(xtime, talk) +long xtime; +boolean talk; +{ + long old = Vomiting; + + if(!xtime && old) + if(talk) You_feel("much less nauseated now."); + + set_itimeout(&Vomiting, xtime); +} + +static const char vismsg[] = "vision seems to %s for a moment but is %s now."; +static const char eyemsg[] = "%s momentarily %s."; + +void +make_blinded(xtime, talk) +long xtime; +boolean talk; +{ + long old = Blinded; + boolean u_could_see, can_see_now; + int eyecnt; + char buf[BUFSZ]; + + /* we need to probe ahead in case the Eyes of the Overworld + are or will be overriding blindness */ + u_could_see = !Blind; + Blinded = xtime ? 1L : 0L; + can_see_now = !Blind; + Blinded = old; /* restore */ + + if (u.usleep) talk = FALSE; + + if (can_see_now && !u_could_see) { /* regaining sight */ + if (talk) { + if (Hallucination) + pline("Far out! Everything is all cosmic again!"); + else + You("can see again."); + } + } else if (old && !xtime) { + /* clearing temporary blindness without toggling blindness */ + if (talk) { + if (!haseyes(youmonst.data)) { + strange_feeling((struct obj *)0, (char *)0); + } else if (Blindfolded) { + Strcpy(buf, body_part(EYE)); + eyecnt = eyecount(youmonst.data); + Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf), + (eyecnt == 1) ? "itches" : "itch"); + } else { /* Eyes of the Overworld */ + Your(vismsg, "brighten", + Hallucination ? "sadder" : "normal"); + } + } + } + + if (u_could_see && !can_see_now) { /* losing sight */ + if (talk) { + if (Hallucination) + pline("Oh, bummer! Everything is dark! Help!"); + else + pline("A cloud of darkness falls upon you."); + } + /* Before the hero goes blind, set the ball&chain variables. */ + if (Punished) set_bc(0); + } else if (!old && xtime) { + /* setting temporary blindness without toggling blindness */ + if (talk) { + if (!haseyes(youmonst.data)) { + strange_feeling((struct obj *)0, (char *)0); + } else if (Blindfolded) { + Strcpy(buf, body_part(EYE)); + eyecnt = eyecount(youmonst.data); + Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf), + (eyecnt == 1) ? "twitches" : "twitch"); + } else { /* Eyes of the Overworld */ + Your(vismsg, "dim", + Hallucination ? "happier" : "normal"); + } + } + } + + set_itimeout(&Blinded, xtime); + + if (u_could_see ^ can_see_now) { /* one or the other but not both */ + flags.botl = 1; + vision_full_recalc = 1; /* blindness just got toggled */ + if (Blind_telepat || Infravision) see_monsters(); + } +} + +boolean +make_hallucinated(xtime, talk, mask) +long xtime; /* nonzero if this is an attempt to turn on hallucination */ +boolean talk; +long mask; /* nonzero if resistance status should change by mask */ +{ + long old = HHallucination; + boolean changed = 0; + const char *message, *verb; + + message = (!xtime) ? "Everything %s SO boring now." : + "Oh wow! Everything %s so cosmic!"; + verb = (!Blind) ? "looks" : "feels"; + + if (mask) { + if (HHallucination) changed = TRUE; + + if (!xtime) EHalluc_resistance |= mask; + else EHalluc_resistance &= ~mask; + } else { + if (!EHalluc_resistance && (!!HHallucination != !!xtime)) + changed = TRUE; + set_itimeout(&HHallucination, xtime); + + /* clearing temporary hallucination without toggling vision */ + if (!changed && !HHallucination && old && talk) { + if (!haseyes(youmonst.data)) { + strange_feeling((struct obj *)0, (char *)0); + } else if (Blind) { + char buf[BUFSZ]; + int eyecnt = eyecount(youmonst.data); + + Strcpy(buf, body_part(EYE)); + Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf), + (eyecnt == 1) ? "itches" : "itch"); + } else { /* Grayswandir */ + Your(vismsg, "flatten", "normal"); + } + } + } + + if (changed) { + if (u.uswallow) { + swallowed(0); /* redraw swallow display */ + } else { + /* The see_* routines should be called *before* the pline. */ + see_monsters(); + see_objects(); + see_traps(); + } + + /* for perm_inv and anything similar + (eg. Qt windowport's equipped items display) */ + update_inventory(); + + flags.botl = 1; + if (talk) pline(message, verb); + } + return changed; +} + +STATIC_OVL void +ghost_from_bottle() +{ + struct monst *mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, NO_MM_FLAGS); + + if (!mtmp) { + pline("This bottle turns out to be empty."); + return; + } + if (Blind) { + pline("As you open the bottle, %s emerges.", something); + return; + } + pline("As you open the bottle, an enormous %s emerges!", + Hallucination ? rndmonnam() : (const char *)"ghost"); + if(flags.verbose) + You("are frightened to death, and unable to move."); + nomul(-3); + nomovemsg = "You regain your composure."; +} + +/* "Quaffing is like drinking, except you spill more." -- Terry Pratchett + */ +int +dodrink() +{ + register struct obj *otmp; + const char *potion_descr; + + if (Strangled) { + pline("If you can't breathe air, how can you drink liquid?"); + return 0; + } + /* Is there a fountain to drink from here? */ + if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) && !Levitation) { + if(yn("Drink from the fountain?") == 'y') { + drinkfountain(); + return 1; + } + } +#ifdef SINKS + /* Or a kitchen sink? */ + if (IS_SINK(levl[u.ux][u.uy].typ)) { + if (yn("Drink from the sink?") == 'y') { + drinksink(); + return 1; + } + } +#endif + + /* Or are you surrounded by water? */ + if (Underwater) { + if (yn("Drink the water around you?") == 'y') { + pline("Do you know what lives in this water!"); + return 1; + } + } + + otmp = getobj(beverages, "drink"); + if(!otmp) return(0); + otmp->in_use = TRUE; /* you've opened the stopper */ + +#define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n)) /* also in muse.c */ + + potion_descr = OBJ_DESCR(objects[otmp->otyp]); + if (potion_descr) { + if (!strcmp(potion_descr, "milky") && + flags.ghost_count < MAXMONNO && + !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) { + ghost_from_bottle(); + useup(otmp); + return(1); + } else if (!strcmp(potion_descr, "smoky") && + flags.djinni_count < MAXMONNO && + !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) { + djinni_from_bottle(otmp); + useup(otmp); + return(1); + } + } + return dopotion(otmp); +} + +int +dopotion(otmp) +register struct obj *otmp; +{ + int retval; + + otmp->in_use = TRUE; + nothing = unkn = 0; + if((retval = peffects(otmp)) >= 0) return(retval); + + if(nothing) { + unkn++; + You("have a %s feeling for a moment, then it passes.", + Hallucination ? "normal" : "peculiar"); + } + if(otmp->dknown && !objects[otmp->otyp].oc_name_known) { + if(!unkn) { + makeknown(otmp->otyp); + more_experienced(0,10); + } else if(!objects[otmp->otyp].oc_uname) + docall(otmp); + } + useup(otmp); + return(1); +} + +int +peffects(otmp) + register struct obj *otmp; +{ + register int i, ii, lim; + + switch(otmp->otyp){ + case POT_RESTORE_ABILITY: + case SPE_RESTORE_ABILITY: + unkn++; + if(otmp->cursed) { + pline("Ulch! This makes you feel mediocre!"); + break; + } else { + pline("Wow! This makes you feel %s!", + (otmp->blessed) ? + (unfixable_trouble_count(FALSE) ? "better" : "great") + : "good"); + i = rn2(A_MAX); /* start at a random point */ + for (ii = 0; ii < A_MAX; ii++) { + lim = AMAX(i); + if (i == A_STR && u.uhs >= 3) --lim; /* WEAK */ + if (ABASE(i) < lim) { + ABASE(i) = lim; + flags.botl = 1; + /* only first found if not blessed */ + if (!otmp->blessed) break; + } + if(++i >= A_MAX) i = 0; + } + } + break; + case POT_HALLUCINATION: + if (Hallucination || Halluc_resistance) nothing++; + (void) make_hallucinated(itimeout_incr(HHallucination, + rn1(200, 600 - 300 * bcsign(otmp))), + TRUE, 0L); + break; + case POT_WATER: + if(!otmp->blessed && !otmp->cursed) { + pline("This tastes like water."); + u.uhunger += rnd(10); + newuhs(FALSE); + break; + } + unkn++; + if(is_undead(youmonst.data) || is_demon(youmonst.data) || + u.ualign.type == A_CHAOTIC) { + if(otmp->blessed) { + pline("This burns like acid!"); + exercise(A_CON, FALSE); + if (u.ulycn >= LOW_PM) { + Your("affinity to %s disappears!", + makeplural(mons[u.ulycn].mname)); + if (youmonst.data == &mons[u.ulycn]) + you_unwere(FALSE); + u.ulycn = NON_PM; /* cure lycanthropy */ + } + losehp(d(2,6), "potion of holy water", KILLED_BY_AN); + } else if(otmp->cursed) { + You_feel("quite proud of yourself."); + healup(d(2,6),0,0,0); + if (u.ulycn >= LOW_PM && !Upolyd) you_were(); + exercise(A_CON, TRUE); + } + } else { + if(otmp->blessed) { + You_feel("full of awe."); + make_sick(0L, (char *) 0, TRUE, SICK_ALL); + exercise(A_WIS, TRUE); + exercise(A_CON, TRUE); + if (u.ulycn >= LOW_PM) + you_unwere(TRUE); /* "Purified" */ + /* make_confused(0L,TRUE); */ + } else { + if(u.ualign.type == A_LAWFUL) { + pline("This burns like acid!"); + losehp(d(2,6), "potion of unholy water", + KILLED_BY_AN); + } else + You_feel("full of dread."); + if (u.ulycn >= LOW_PM && !Upolyd) you_were(); + exercise(A_CON, FALSE); + } + } + break; + case POT_BOOZE: + unkn++; + pline("Ooph! This tastes like %s%s!", + otmp->odiluted ? "watered down " : "", + Hallucination ? "dandelion wine" : "liquid fire"); + if (!otmp->blessed) + make_confused(itimeout_incr(HConfusion, d(3,8)), FALSE); + /* the whiskey makes us feel better */ + if (!otmp->odiluted) healup(1, 0, FALSE, FALSE); + u.uhunger += 10 * (2 + bcsign(otmp)); + newuhs(FALSE); + exercise(A_WIS, FALSE); + if(otmp->cursed) { + You("pass out."); + multi = -rnd(15); + nomovemsg = "You awake with a headache."; + } + break; + case POT_ENLIGHTENMENT: + if(otmp->cursed) { + unkn++; + You("have an uneasy feeling..."); + exercise(A_WIS, FALSE); + } else { + if (otmp->blessed) { + (void) adjattrib(A_INT, 1, FALSE); + (void) adjattrib(A_WIS, 1, FALSE); + } + You_feel("self-knowledgeable..."); + display_nhwindow(WIN_MESSAGE, FALSE); + enlightenment(0); + pline_The("feeling subsides."); + exercise(A_WIS, TRUE); + } + break; + case SPE_INVISIBILITY: + /* spell cannot penetrate mummy wrapping */ + if (BInvis && uarmc->otyp == MUMMY_WRAPPING) { + You_feel("rather itchy under your %s.", xname(uarmc)); + break; + } + /* FALLTHRU */ + case POT_INVISIBILITY: + if (Invis || Blind || BInvis) { + nothing++; + } else { + self_invis_message(); + } + if (otmp->blessed) HInvis |= FROMOUTSIDE; + else incr_itimeout(&HInvis, rn1(15,31)); + newsym(u.ux,u.uy); /* update position */ + if(otmp->cursed) { + pline("For some reason, you feel your presence is known."); + aggravate(); + } + break; + case POT_SEE_INVISIBLE: + /* tastes like fruit juice in Rogue */ + case POT_FRUIT_JUICE: + { + int msg = Invisible && !Blind; + + unkn++; + if (otmp->cursed) + pline("Yecch! This tastes %s.", + Hallucination ? "overripe" : "rotten"); + else + pline(Hallucination ? + "This tastes like 10%% real %s%s all-natural beverage." : + "This tastes like %s%s.", + otmp->odiluted ? "reconstituted " : "", + fruitname(TRUE)); + if (otmp->otyp == POT_FRUIT_JUICE) { + u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp)); + newuhs(FALSE); + break; + } + if (!otmp->cursed) { + /* Tell them they can see again immediately, which + * will help them identify the potion... + */ + make_blinded(0L,TRUE); + } + if (otmp->blessed) + HSee_invisible |= FROMOUTSIDE; + else + incr_itimeout(&HSee_invisible, rn1(100,750)); + set_mimic_blocking(); /* do special mimic handling */ + see_monsters(); /* see invisible monsters */ + newsym(u.ux,u.uy); /* see yourself! */ + if (msg && !Blind) { /* Blind possible if polymorphed */ + You("can see through yourself, but you are visible!"); + unkn--; + } + break; + } + case POT_PARALYSIS: + if (Free_action) + You("stiffen momentarily."); + else { + if (Levitation || Is_airlevel(&u.uz)||Is_waterlevel(&u.uz)) + You("are motionlessly suspended."); +#ifdef STEED + else if (u.usteed) + You("are frozen in place!"); +#endif + else + Your("%s are frozen to the %s!", + makeplural(body_part(FOOT)), surface(u.ux, u.uy)); + nomul(-(rn1(10, 25 - 12*bcsign(otmp)))); + nomovemsg = You_can_move_again; + exercise(A_DEX, FALSE); + } + break; + case POT_SLEEPING: + if(Sleep_resistance || Free_action) + You("yawn."); + else { + You("suddenly fall asleep!"); + fall_asleep(-rn1(10, 25 - 12*bcsign(otmp)), TRUE); + } + break; + case POT_MONSTER_DETECTION: + case SPE_DETECT_MONSTERS: + if (otmp->blessed) { + int x, y; + + if (Detect_monsters) nothing++; + unkn++; + /* after a while, repeated uses become less effective */ + if (HDetect_monsters >= 300L) + i = 1; + else + i = rn1(40,21); + incr_itimeout(&HDetect_monsters, i); + for (x = 1; x < COLNO; x++) { + for (y = 0; y < ROWNO; y++) { + if (levl[x][y].glyph == GLYPH_INVISIBLE) { + unmap_object(x, y); + newsym(x,y); + } + if (MON_AT(x,y)) unkn = 0; + } + } + see_monsters(); + if (unkn) You_feel("lonely."); + break; + } + if (monster_detect(otmp, 0)) + return(1); /* nothing detected */ + exercise(A_WIS, TRUE); + break; + case POT_OBJECT_DETECTION: + case SPE_DETECT_TREASURE: + if (object_detect(otmp, 0)) + return(1); /* nothing detected */ + exercise(A_WIS, TRUE); + break; + case POT_SICKNESS: + pline("Yecch! This stuff tastes like poison."); + if (otmp->blessed) { + pline("(But in fact it was mildly stale %s.)", + fruitname(TRUE)); + if (!Role_if(PM_HEALER)) { + /* NB: blessed otmp->fromsink is not possible */ + losehp(1, "mildly contaminated potion", KILLED_BY_AN); + } + } else { + if(Poison_resistance) + pline( + "(But in fact it was biologically contaminated %s.)", + fruitname(TRUE)); + if (Role_if(PM_HEALER)) + pline("Fortunately, you have been immunized."); + else { + int typ = rn2(A_MAX); + + if (!Fixed_abil) { + poisontell(typ); + (void) adjattrib(typ, + Poison_resistance ? -1 : -rn1(4,3), + TRUE); + } + if(!Poison_resistance) { + if (otmp->fromsink) + losehp(rnd(10)+5*!!(otmp->cursed), + "contaminated tap water", KILLED_BY); + else + losehp(rnd(10)+5*!!(otmp->cursed), + "contaminated potion", KILLED_BY_AN); + } + exercise(A_CON, FALSE); + } + } + if(Hallucination) { + You("are shocked back to your senses!"); + (void) make_hallucinated(0L,FALSE,0L); + } + break; + case POT_CONFUSION: + if(!Confusion) + if (Hallucination) { + pline("What a trippy feeling!"); + unkn++; + } else + pline("Huh, What? Where am I?"); + else nothing++; + make_confused(itimeout_incr(HConfusion, + rn1(7, 16 - 8 * bcsign(otmp))), + FALSE); + break; + case POT_GAIN_ABILITY: + if(otmp->cursed) { + pline("Ulch! That potion tasted foul!"); + unkn++; + } else if (Fixed_abil) { + nothing++; + } else { /* If blessed, increase all; if not, try up to */ + int itmp; /* 6 times to find one which can be increased. */ + i = -1; /* increment to 0 */ + for (ii = A_MAX; ii > 0; ii--) { + i = (otmp->blessed ? i + 1 : rn2(A_MAX)); + /* only give "your X is already as high as it can get" + message on last attempt (except blessed potions) */ + itmp = (otmp->blessed || ii == 1) ? 0 : -1; + if (adjattrib(i, 1, itmp) && !otmp->blessed) + break; + } + } + break; + case POT_SPEED: + if(Wounded_legs && !otmp->cursed +#ifdef STEED + && !u.usteed /* heal_legs() would heal steeds legs */ +#endif + ) { + heal_legs(); + unkn++; + break; + } /* and fall through */ + case SPE_HASTE_SELF: + if(!Very_fast) /* wwf@doe.carleton.ca */ + You("are suddenly moving %sfaster.", + Fast ? "" : "much "); + else { + Your("%s get new energy.", + makeplural(body_part(LEG))); + unkn++; + } + exercise(A_DEX, TRUE); + incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp))); + break; + case POT_BLINDNESS: + if(Blind) nothing++; + make_blinded(itimeout_incr(Blinded, + rn1(200, 250 - 125 * bcsign(otmp))), + (boolean)!Blind); + break; + case POT_GAIN_LEVEL: + if (otmp->cursed) { + unkn++; + /* they went up a level */ + if((ledger_no(&u.uz) == 1 && u.uhave.amulet) || + Can_rise_up(u.ux, u.uy, &u.uz)) { + const char *riseup ="rise up, through the %s!"; + if(ledger_no(&u.uz) == 1) { + You(riseup, ceiling(u.ux,u.uy)); + goto_level(&earth_level, FALSE, FALSE, FALSE); + } else { + register int newlev = depth(&u.uz)-1; + d_level newlevel; + + get_level(&newlevel, newlev); + if(on_level(&newlevel, &u.uz)) { + pline("It tasted bad."); + break; + } else You(riseup, ceiling(u.ux,u.uy)); + goto_level(&newlevel, FALSE, FALSE, FALSE); + } + } + else You("have an uneasy feeling."); + break; + } + pluslvl(FALSE); + if (otmp->blessed) + /* blessed potions place you at a random spot in the + * middle of the new level instead of the low point + */ + u.uexp = rndexp(TRUE); + break; + case POT_HEALING: + You_feel("better."); + healup(d(6 + 2 * bcsign(otmp), 4), + !otmp->cursed ? 1 : 0, !!otmp->blessed, !otmp->cursed); + exercise(A_CON, TRUE); + break; + case POT_EXTRA_HEALING: + You_feel("much better."); + healup(d(6 + 2 * bcsign(otmp), 8), + otmp->blessed ? 5 : !otmp->cursed ? 2 : 0, + !otmp->cursed, TRUE); + (void) make_hallucinated(0L,TRUE,0L); + exercise(A_CON, TRUE); + exercise(A_STR, TRUE); + break; + case POT_FULL_HEALING: + You_feel("completely healed."); + healup(400, 4+4*bcsign(otmp), !otmp->cursed, TRUE); + /* Restore one lost level if blessed */ + if (otmp->blessed && u.ulevel < u.ulevelmax) { + /* when multiple levels have been lost, drinking + multiple potions will only get half of them back */ + u.ulevelmax -= 1; + pluslvl(FALSE); + } + (void) make_hallucinated(0L,TRUE,0L); + exercise(A_STR, TRUE); + exercise(A_CON, TRUE); + break; + case POT_LEVITATION: + case SPE_LEVITATION: + if (otmp->cursed) HLevitation &= ~I_SPECIAL; + if(!Levitation) { + /* kludge to ensure proper operation of float_up() */ + HLevitation = 1; + float_up(); + /* reverse kludge */ + HLevitation = 0; + if (otmp->cursed && !Is_waterlevel(&u.uz)) { + if((u.ux != xupstair || u.uy != yupstair) + && (u.ux != sstairs.sx || u.uy != sstairs.sy || !sstairs.up) + && (!xupladder || u.ux != xupladder || u.uy != yupladder) + ) { + You("hit your %s on the %s.", + body_part(HEAD), + ceiling(u.ux,u.uy)); + losehp(uarmh ? 1 : rnd(10), + "colliding with the ceiling", + KILLED_BY); + } else (void) doup(); + } + } else + nothing++; + if (otmp->blessed) { + incr_itimeout(&HLevitation, rn1(50,250)); + HLevitation |= I_SPECIAL; + } else incr_itimeout(&HLevitation, rn1(140,10)); + spoteffects(FALSE); /* for sinks */ + break; + case POT_GAIN_ENERGY: /* M. Stephenson */ + { register int num; + if(otmp->cursed) + You_feel("lackluster."); + else + pline("Magical energies course through your body."); + num = rnd(5) + 5 * otmp->blessed + 1; + u.uenmax += (otmp->cursed) ? -num : num; + u.uen += (otmp->cursed) ? -num : num; + if(u.uenmax <= 0) u.uenmax = 0; + if(u.uen <= 0) u.uen = 0; + flags.botl = 1; + exercise(A_WIS, TRUE); + } + break; + case POT_OIL: /* P. Winner */ + { + boolean good_for_you = FALSE; + + if (otmp->lamplit) { + if (likes_fire(youmonst.data)) { + pline("Ahh, a refreshing drink."); + good_for_you = TRUE; + } else { + You("burn your %s.", body_part(FACE)); + losehp(d(Fire_resistance ? 1 : 3, 4), + "burning potion of oil", KILLED_BY_AN); + } + } else if(otmp->cursed) + pline("This tastes like castor oil."); + else + pline("That was smooth!"); + exercise(A_WIS, good_for_you); + } + break; + case POT_ACID: + if (Acid_resistance) + /* Not necessarily a creature who _likes_ acid */ + pline("This tastes %s.", Hallucination ? "tangy" : "sour"); + else { + pline("This burns%s!", otmp->blessed ? " a little" : + otmp->cursed ? " a lot" : " like acid"); + losehp(d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8), + "potion of acid", KILLED_BY_AN); + exercise(A_CON, FALSE); + } + if (Stoned) fix_petrification(); + unkn++; /* holy/unholy water can burn like acid too */ + break; + case POT_POLYMORPH: + You_feel("a little %s.", Hallucination ? "normal" : "strange"); + if (!Unchanging) polyself(FALSE); + break; + default: + impossible("What a funny potion! (%u)", otmp->otyp); + return(0); + } + return(-1); +} + +void +healup(nhp, nxtra, curesick, cureblind) + int nhp, nxtra; + register boolean curesick, cureblind; +{ + if (nhp) { + if (Upolyd) { + u.mh += nhp; + if (u.mh > u.mhmax) u.mh = (u.mhmax += nxtra); + } else { + u.uhp += nhp; + if(u.uhp > u.uhpmax) u.uhp = (u.uhpmax += nxtra); + } + } + if(cureblind) make_blinded(0L,TRUE); + if(curesick) make_sick(0L, (char *) 0, TRUE, SICK_ALL); + flags.botl = 1; + return; +} + +void +strange_feeling(obj,txt) +register struct obj *obj; +register const char *txt; +{ + if (flags.beginner || !txt) + You("have a %s feeling for a moment, then it passes.", + Hallucination ? "normal" : "strange"); + else + pline(txt); + + if(!obj) /* e.g., crystal ball finds no traps */ + return; + + if(obj->dknown && !objects[obj->otyp].oc_name_known && + !objects[obj->otyp].oc_uname) + docall(obj); + useup(obj); +} + +const char *bottlenames[] = { + "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial" +}; + + +const char * +bottlename() +{ + return bottlenames[rn2(SIZE(bottlenames))]; +} + +void +potionhit(mon, obj, your_fault) +register struct monst *mon; +register struct obj *obj; +boolean your_fault; +{ + register const char *botlnam = bottlename(); + boolean isyou = (mon == &youmonst); + int distance; + + if(isyou) { + distance = 0; + pline_The("%s crashes on your %s and breaks into shards.", + botlnam, body_part(HEAD)); + losehp(rnd(2), "thrown potion", KILLED_BY_AN); + } else { + distance = distu(mon->mx,mon->my); + if (!cansee(mon->mx,mon->my)) pline("Crash!"); + else { + char *mnam = mon_nam(mon); + char buf[BUFSZ]; + + if(has_head(mon->data)) { + Sprintf(buf, "%s %s", + s_suffix(mnam), + (notonhead ? "body" : "head")); + } else { + Strcpy(buf, mnam); + } + pline_The("%s crashes on %s and breaks into shards.", + botlnam, buf); + } + if(rn2(5) && mon->mhp > 1) + mon->mhp--; + } + + /* oil doesn't instantly evaporate */ + if (obj->otyp != POT_OIL && cansee(mon->mx,mon->my)) + pline("%s.", Tobjnam(obj, "evaporate")); + + if (isyou) { + switch (obj->otyp) { + case POT_OIL: + if (obj->lamplit) + splatter_burning_oil(u.ux, u.uy); + break; + case POT_POLYMORPH: + You_feel("a little %s.", Hallucination ? "normal" : "strange"); + if (!Unchanging && !Antimagic) polyself(FALSE); + break; + case POT_ACID: + if (!Acid_resistance) { + pline("This burns%s!", obj->blessed ? " a little" : + obj->cursed ? " a lot" : ""); + losehp(d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8), + "potion of acid", KILLED_BY_AN); + } + break; + } + } else { + boolean angermon = TRUE; + + if (!your_fault) angermon = FALSE; + switch (obj->otyp) { + case POT_HEALING: + case POT_EXTRA_HEALING: + case POT_FULL_HEALING: + if (mon->data == &mons[PM_PESTILENCE]) goto do_illness; + /*FALLTHRU*/ + case POT_RESTORE_ABILITY: + case POT_GAIN_ABILITY: + do_healing: + angermon = FALSE; + if(mon->mhp < mon->mhpmax) { + mon->mhp = mon->mhpmax; + if (canseemon(mon)) + pline("%s looks sound and hale again.", Monnam(mon)); + } + break; + case POT_SICKNESS: + if (mon->data == &mons[PM_PESTILENCE]) goto do_healing; + if (dmgtype(mon->data, AD_DISE) || + dmgtype(mon->data, AD_PEST) || /* won't happen, see prior goto */ + resists_poison(mon)) { + if (canseemon(mon)) + pline("%s looks unharmed.", Monnam(mon)); + break; + } + do_illness: + if((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL)) + mon->mhpmax /= 2; + if((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL)) + mon->mhp /= 2; + if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (canseemon(mon)) + pline("%s looks rather ill.", Monnam(mon)); + break; + case POT_CONFUSION: + case POT_BOOZE: + if(!resist(mon, POTION_CLASS, 0, NOTELL)) mon->mconf = TRUE; + break; + case POT_INVISIBILITY: + angermon = FALSE; + mon_set_minvis(mon); + break; + case POT_SLEEPING: + /* wakeup() doesn't rouse victims of temporary sleep */ + if (sleep_monst(mon, rnd(12), POTION_CLASS)) { + pline("%s falls asleep.", Monnam(mon)); + slept_monst(mon); + } + break; + case POT_PARALYSIS: + if (mon->mcanmove) { + mon->mcanmove = 0; + /* really should be rnd(5) for consistency with players + * breathing potions, but... + */ + mon->mfrozen = rnd(25); + } + break; + case POT_SPEED: + angermon = FALSE; + mon_adjust_speed(mon, 1, obj); + break; + case POT_BLINDNESS: + if(haseyes(mon->data)) { + register int btmp = 64 + rn2(32) + + rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL); + btmp += mon->mblinded; + mon->mblinded = min(btmp,127); + mon->mcansee = 0; + } + break; + case POT_WATER: + if (is_undead(mon->data) || is_demon(mon->data) || + is_were(mon->data)) { + if (obj->blessed) { + pline("%s %s in pain!", Monnam(mon), + is_silent(mon->data) ? "writhes" : "shrieks"); + mon->mhp -= d(2,6); + /* should only be by you */ + if (mon->mhp < 1) killed(mon); + else if (is_were(mon->data) && !is_human(mon->data)) + new_were(mon); /* revert to human */ + } else if (obj->cursed) { + angermon = FALSE; + if (canseemon(mon)) + pline("%s looks healthier.", Monnam(mon)); + mon->mhp += d(2,6); + if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (is_were(mon->data) && is_human(mon->data) && + !Protection_from_shape_changers) + new_were(mon); /* transform into beast */ + } + } else if(mon->data == &mons[PM_GREMLIN]) { + angermon = FALSE; + (void)split_mon(mon, (struct monst *)0); + } else if(mon->data == &mons[PM_IRON_GOLEM]) { + if (canseemon(mon)) + pline("%s rusts.", Monnam(mon)); + mon->mhp -= d(1,6); + /* should only be by you */ + if (mon->mhp < 1) killed(mon); + } + break; + case POT_OIL: + if (obj->lamplit) + splatter_burning_oil(mon->mx, mon->my); + break; + case POT_ACID: + if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) { + pline("%s %s in pain!", Monnam(mon), + is_silent(mon->data) ? "writhes" : "shrieks"); + mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8); + if (mon->mhp < 1) { + if (your_fault) + killed(mon); + else + monkilled(mon, "", AD_ACID); + } + } + break; + case POT_POLYMORPH: + (void) bhitm(mon, obj); + break; +/* + case POT_GAIN_LEVEL: + case POT_LEVITATION: + case POT_FRUIT_JUICE: + case POT_MONSTER_DETECTION: + case POT_OBJECT_DETECTION: + break; +*/ + } + if (angermon) + wakeup(mon); + else + mon->msleeping = 0; + } + + /* Note: potionbreathe() does its own docall() */ + if ((distance==0 || ((distance < 3) && rn2(5))) && + (!breathless(youmonst.data) || haseyes(youmonst.data))) + potionbreathe(obj); + else if (obj->dknown && !objects[obj->otyp].oc_name_known && + !objects[obj->otyp].oc_uname && cansee(mon->mx,mon->my)) + docall(obj); + if(*u.ushops && obj->unpaid) { + register struct monst *shkp = + shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + + if(!shkp) + obj->unpaid = 0; + else { + (void)stolen_value(obj, u.ux, u.uy, + (boolean)shkp->mpeaceful, FALSE); + subfrombill(obj, shkp); + } + } + obfree(obj, (struct obj *)0); +} + +/* vapors are inhaled or get in your eyes */ +void +potionbreathe(obj) +register struct obj *obj; +{ + register int i, ii, isdone, kn = 0; + + switch(obj->otyp) { + case POT_RESTORE_ABILITY: + case POT_GAIN_ABILITY: + if(obj->cursed) { + if (!breathless(youmonst.data)) + pline("Ulch! That potion smells terrible!"); + else if (haseyes(youmonst.data)) { + int numeyes = eyecount(youmonst.data); + Your("%s sting%s!", + (numeyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)), + (numeyes == 1) ? "s" : ""); + } + break; + } else { + i = rn2(A_MAX); /* start at a random point */ + for(isdone = ii = 0; !isdone && ii < A_MAX; ii++) { + if(ABASE(i) < AMAX(i)) { + ABASE(i)++; + /* only first found if not blessed */ + isdone = !(obj->blessed); + flags.botl = 1; + } + if(++i >= A_MAX) i = 0; + } + } + break; + case POT_FULL_HEALING: + if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; + if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; + /*FALL THROUGH*/ + case POT_EXTRA_HEALING: + if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; + if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; + /*FALL THROUGH*/ + case POT_HEALING: + if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; + if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; + exercise(A_CON, TRUE); + break; + case POT_SICKNESS: + if (!Role_if(PM_HEALER)) { + if (Upolyd) { + if (u.mh <= 5) u.mh = 1; else u.mh -= 5; + } else { + if (u.uhp <= 5) u.uhp = 1; else u.uhp -= 5; + } + flags.botl = 1; + exercise(A_CON, FALSE); + } + break; + case POT_HALLUCINATION: + You("have a momentary vision."); + break; + case POT_CONFUSION: + case POT_BOOZE: + if(!Confusion) + You_feel("somewhat dizzy."); + make_confused(itimeout_incr(HConfusion, rnd(5)), FALSE); + break; + case POT_INVISIBILITY: + if (!Blind && !Invis) { + kn++; + pline("For an instant you %s!", + See_invisible ? "could see right through yourself" + : "couldn't see yourself"); + } + break; + case POT_PARALYSIS: + kn++; + if (!Free_action) { + pline("%s seems to be holding you.", Something); + nomul(-rnd(5)); + nomovemsg = You_can_move_again; + exercise(A_DEX, FALSE); + } else You("stiffen momentarily."); + break; + case POT_SLEEPING: + kn++; + if (!Free_action && !Sleep_resistance) { + You_feel("rather tired."); + nomul(-rnd(5)); + nomovemsg = You_can_move_again; + exercise(A_DEX, FALSE); + } else You("yawn."); + break; + case POT_SPEED: + if (!Fast) Your("knees seem more flexible now."); + incr_itimeout(&HFast, rnd(5)); + exercise(A_DEX, TRUE); + break; + case POT_BLINDNESS: + if (!Blind && !u.usleep) { + kn++; + pline("It suddenly gets dark."); + } + make_blinded(itimeout_incr(Blinded, rnd(5)), FALSE); + if (!Blind && !u.usleep) Your(vision_clears); + break; + case POT_WATER: + if(u.umonnum == PM_GREMLIN) { + (void)split_mon(&youmonst, (struct monst *)0); + } else if (u.ulycn >= LOW_PM) { + /* vapor from [un]holy water will trigger + transformation but won't cure lycanthropy */ + if (obj->blessed && youmonst.data == &mons[u.ulycn]) + you_unwere(FALSE); + else if (obj->cursed && !Upolyd) + you_were(); + } + break; + case POT_ACID: + case POT_POLYMORPH: + exercise(A_CON, FALSE); + break; +/* + case POT_GAIN_LEVEL: + case POT_LEVITATION: + case POT_FRUIT_JUICE: + case POT_MONSTER_DETECTION: + case POT_OBJECT_DETECTION: + case POT_OIL: + break; +*/ + } + /* note: no obfree() */ + if (obj->dknown) { + if (kn) + makeknown(obj->otyp); + else if (!objects[obj->otyp].oc_name_known && + !objects[obj->otyp].oc_uname) + docall(obj); + } +} + +STATIC_OVL short +mixtype(o1, o2) +register struct obj *o1, *o2; +/* returns the potion type when o1 is dipped in o2 */ +{ + /* cut down on the number of cases below */ + if (o1->oclass == POTION_CLASS && + (o2->otyp == POT_GAIN_LEVEL || + o2->otyp == POT_GAIN_ENERGY || + o2->otyp == POT_HEALING || + o2->otyp == POT_EXTRA_HEALING || + o2->otyp == POT_FULL_HEALING || + o2->otyp == POT_ENLIGHTENMENT || + o2->otyp == POT_FRUIT_JUICE)) { + struct obj *swp; + + swp = o1; o1 = o2; o2 = swp; + } + + switch (o1->otyp) { + case POT_HEALING: + switch (o2->otyp) { + case POT_SPEED: + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + return POT_EXTRA_HEALING; + } + case POT_EXTRA_HEALING: + switch (o2->otyp) { + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + return POT_FULL_HEALING; + } + case POT_FULL_HEALING: + switch (o2->otyp) { + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + return POT_GAIN_ABILITY; + } + case UNICORN_HORN: + switch (o2->otyp) { + case POT_SICKNESS: + return POT_FRUIT_JUICE; + case POT_HALLUCINATION: + case POT_BLINDNESS: + case POT_CONFUSION: + return POT_WATER; + } + break; + case AMETHYST: /* "a-methyst" == "not intoxicated" */ + if (o2->otyp == POT_BOOZE) + return POT_FRUIT_JUICE; + break; + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + switch (o2->otyp) { + case POT_CONFUSION: + return (rn2(3) ? POT_BOOZE : POT_ENLIGHTENMENT); + case POT_HEALING: + return POT_EXTRA_HEALING; + case POT_EXTRA_HEALING: + return POT_FULL_HEALING; + case POT_FULL_HEALING: + return POT_GAIN_ABILITY; + case POT_FRUIT_JUICE: + return POT_SEE_INVISIBLE; + case POT_BOOZE: + return POT_HALLUCINATION; + } + break; + case POT_FRUIT_JUICE: + switch (o2->otyp) { + case POT_SICKNESS: + return POT_SICKNESS; + case POT_SPEED: + return POT_BOOZE; + case POT_GAIN_LEVEL: + case POT_GAIN_ENERGY: + return POT_SEE_INVISIBLE; + } + break; + case POT_ENLIGHTENMENT: + switch (o2->otyp) { + case POT_LEVITATION: + if (rn2(3)) return POT_GAIN_LEVEL; + break; + case POT_FRUIT_JUICE: + return POT_BOOZE; + case POT_BOOZE: + return POT_CONFUSION; + } + break; + } + + return 0; +} + + +boolean +get_wet(obj) +register struct obj *obj; +/* returns TRUE if something happened (potion should be used up) */ +{ + char Your_buf[BUFSZ]; + + if (snuff_lit(obj)) return(TRUE); + + if (obj->greased) { + grease_protect(obj,(char *)0,&youmonst); + return(FALSE); + } + (void) Shk_Your(Your_buf, obj); + /* (Rusting shop goods ought to be charged for.) */ + switch (obj->oclass) { + case POTION_CLASS: + if (obj->otyp == POT_WATER) return FALSE; + /* KMH -- Water into acid causes an explosion */ + if (obj->otyp == POT_ACID) { + pline("It boils vigorously!"); + You("are caught in the explosion!"); + losehp(rnd(10), "elementary chemistry", KILLED_BY); + makeknown(obj->otyp); + update_inventory(); + return (TRUE); + } + pline("%s %s%s.", Your_buf, aobjnam(obj,"dilute"), + obj->odiluted ? " further" : ""); + if(obj->unpaid && costly_spot(u.ux, u.uy)) { + You("dilute it, you pay for it."); + bill_dummy_object(obj); + } + if (obj->odiluted) { + obj->odiluted = 0; +#ifdef UNIXPC + obj->blessed = FALSE; + obj->cursed = FALSE; +#else + obj->blessed = obj->cursed = FALSE; +#endif + obj->otyp = POT_WATER; + } else obj->odiluted++; + update_inventory(); + return TRUE; + case SCROLL_CLASS: + if (obj->otyp != SCR_BLANK_PAPER +#ifdef MAIL + && obj->otyp != SCR_MAIL +#endif + ) { + if (!Blind) { + boolean oq1 = obj->quan == 1L; + pline_The("scroll%s %s.", + oq1 ? "" : "s", otense(obj, "fade")); + } + if(obj->unpaid && costly_spot(u.ux, u.uy)) { + You("erase it, you pay for it."); + bill_dummy_object(obj); + } + obj->otyp = SCR_BLANK_PAPER; + obj->spe = 0; + update_inventory(); + return TRUE; + } else break; + case SPBOOK_CLASS: + if (obj->otyp != SPE_BLANK_PAPER) { + + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + pline("%s suddenly heats up; steam rises and it remains dry.", + The(xname(obj))); + } else { + if (!Blind) { + boolean oq1 = obj->quan == 1L; + pline_The("spellbook%s %s.", + oq1 ? "" : "s", otense(obj, "fade")); + } + if(obj->unpaid && costly_spot(u.ux, u.uy)) { + You("erase it, you pay for it."); + bill_dummy_object(obj); + } + obj->otyp = SPE_BLANK_PAPER; + update_inventory(); + } + return TRUE; + } + break; + case WEAPON_CLASS: + /* Just "fall through" to generic rustprone check for now. */ + /* fall through */ + default: + if (!obj->oerodeproof && is_rustprone(obj) && + (obj->oeroded < MAX_ERODE) && !rn2(2)) { + pline("%s %s some%s.", + Your_buf, aobjnam(obj, "rust"), + obj->oeroded ? " more" : "what"); + obj->oeroded++; + update_inventory(); + return TRUE; + } else break; + } + pline("%s %s wet.", Your_buf, aobjnam(obj,"get")); + return FALSE; +} + +int +dodip() +{ + register struct obj *potion, *obj; + struct obj *singlepotion; + const char *tmp; + uchar here; + char allowall[2]; + short mixture; + char qbuf[QBUFSZ], Your_buf[BUFSZ]; + + allowall[0] = ALL_CLASSES; allowall[1] = '\0'; + if(!(obj = getobj(allowall, "dip"))) + return(0); + + here = levl[u.ux][u.uy].typ; + /* Is there a fountain to dip into here? */ + if (IS_FOUNTAIN(here)) { + if(yn("Dip it into the fountain?") == 'y') { + dipfountain(obj); + return(1); + } + } else if (is_pool(u.ux,u.uy)) { + tmp = waterbody_name(u.ux,u.uy); + Sprintf(qbuf, "Dip it into the %s?", tmp); + if (yn(qbuf) == 'y') { + if (Levitation) { + floating_above(tmp); +#ifdef STEED + } else if (u.usteed && !is_swimmer(u.usteed->data) && + P_SKILL(P_RIDING) < P_BASIC) { + rider_cant_reach(); /* not skilled enough to reach */ +#endif + } else { + (void) get_wet(obj); + if (obj->otyp == POT_ACID) useup(obj); + } + return 1; + } + } + + if(!(potion = getobj(beverages, "dip into"))) + return(0); + if (potion == obj && potion->quan == 1L) { + pline("That is a potion bottle, not a Klein bottle!"); + return 0; + } + potion->in_use = TRUE; /* assume it will be used up */ + if(potion->otyp == POT_WATER) { + boolean useeit = !Blind; + if (useeit) (void) Shk_Your(Your_buf, obj); + if (potion->blessed) { + if (obj->cursed) { + if (useeit) + pline("%s %s %s.", + Your_buf, + aobjnam(obj, "softly glow"), + hcolor(NH_AMBER)); + uncurse(obj); + obj->bknown=1; + poof: + if(!(objects[potion->otyp].oc_name_known) && + !(objects[potion->otyp].oc_uname)) + docall(potion); + useup(potion); + return(1); + } else if(!obj->blessed) { + if (useeit) { + tmp = hcolor(NH_LIGHT_BLUE); + pline("%s %s with a%s %s aura.", + Your_buf, + aobjnam(obj, "softly glow"), + index(vowels, *tmp) ? "n" : "", tmp); + } + bless(obj); + obj->bknown=1; + goto poof; + } + } else if (potion->cursed) { + if (obj->blessed) { + if (useeit) + pline("%s %s %s.", + Your_buf, + aobjnam(obj, "glow"), + hcolor((const char *)"brown")); + unbless(obj); + obj->bknown=1; + goto poof; + } else if(!obj->cursed) { + if (useeit) { + tmp = hcolor(NH_BLACK); + pline("%s %s with a%s %s aura.", + Your_buf, + aobjnam(obj, "glow"), + index(vowels, *tmp) ? "n" : "", tmp); + } + curse(obj); + obj->bknown=1; + goto poof; + } + } else + if (get_wet(obj)) + goto poof; + } else if (obj->otyp == POT_POLYMORPH || + potion->otyp == POT_POLYMORPH) { + /* some objects can't be polymorphed */ + if (obj->otyp == potion->otyp || /* both POT_POLY */ + obj->otyp == WAN_POLYMORPH || + obj->otyp == SPE_POLYMORPH || + obj == uball || obj == uskin || + obj_resists(obj->otyp == POT_POLYMORPH ? + potion : obj, 5, 95)) { + pline(nothing_happens); + } else { + boolean was_wep = FALSE, was_swapwep = FALSE, was_quiver = FALSE; + short save_otyp = obj->otyp; + /* KMH, conduct */ + u.uconduct.polypiles++; + + if (obj == uwep) was_wep = TRUE; + else if (obj == uswapwep) was_swapwep = TRUE; + else if (obj == uquiver) was_quiver = TRUE; + + obj = poly_obj(obj, STRANGE_OBJECT); + + if (was_wep) setuwep(obj); + else if (was_swapwep) setuswapwep(obj); + else if (was_quiver) setuqwep(obj); + + if (obj->otyp != save_otyp) { + makeknown(POT_POLYMORPH); + useup(potion); + prinv((char *)0, obj, 0L); + return 1; + } else { + pline("Nothing seems to happen."); + goto poof; + } + } + potion->in_use = FALSE; /* didn't go poof */ + return(1); + } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) { + /* Mixing potions is dangerous... */ + pline_The("potions mix..."); + /* KMH, balance patch -- acid is particularly unstable */ + if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { + pline("BOOM! They explode!"); + exercise(A_STR, FALSE); + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(obj); + useup(obj); + useup(potion); + losehp(rnd(10), "alchemic blast", KILLED_BY_AN); + return(1); + } + + obj->blessed = obj->cursed = obj->bknown = 0; + if (Blind || Hallucination) obj->dknown = 0; + + if ((mixture = mixtype(obj, potion)) != 0) { + obj->otyp = mixture; + } else { + switch (obj->odiluted ? 1 : rnd(8)) { + case 1: + obj->otyp = POT_WATER; + break; + case 2: + case 3: + obj->otyp = POT_SICKNESS; + break; + case 4: + { + struct obj *otmp; + otmp = mkobj(POTION_CLASS,FALSE); + obj->otyp = otmp->otyp; + obfree(otmp, (struct obj *)0); + } + break; + default: + if (!Blind) + pline_The("mixture glows brightly and evaporates."); + useup(obj); + useup(potion); + return(1); + } + } + + obj->odiluted = (obj->otyp != POT_WATER); + + if (obj->otyp == POT_WATER && !Hallucination) { + pline_The("mixture bubbles%s.", + Blind ? "" : ", then clears"); + } else if (!Blind) { + pline_The("mixture looks %s.", + hcolor(OBJ_DESCR(objects[obj->otyp]))); + } + + useup(potion); + return(1); + } + +#ifdef INVISIBLE_OBJECTS + if (potion->otyp == POT_INVISIBILITY && !obj->oinvis) { + obj->oinvis = TRUE; + if (!Blind) { + if (!See_invisible) pline("Where did %s go?", + the(xname(obj))); + else You("notice a little haziness around %s.", + the(xname(obj))); + } + goto poof; + } else if (potion->otyp == POT_SEE_INVISIBLE && obj->oinvis) { + obj->oinvis = FALSE; + if (!Blind) { + if (!See_invisible) pline("So that's where %s went!", + the(xname(obj))); + else pline_The("haziness around %s disappears.", + the(xname(obj))); + } + goto poof; + } +#endif + + if(is_poisonable(obj)) { + if(potion->otyp == POT_SICKNESS && !obj->opoisoned) { + char buf[BUFSZ]; + if (potion->quan > 1L) + Sprintf(buf, "One of %s", the(xname(potion))); + else + Strcpy(buf, The(xname(potion))); + pline("%s forms a coating on %s.", + buf, the(xname(obj))); + obj->opoisoned = TRUE; + goto poof; + } else if(obj->opoisoned && + (potion->otyp == POT_HEALING || + potion->otyp == POT_EXTRA_HEALING || + potion->otyp == POT_FULL_HEALING)) { + pline("A coating wears off %s.", the(xname(obj))); + obj->opoisoned = 0; + goto poof; + } + } + + if (potion->otyp == POT_OIL) { + boolean wisx = FALSE; + if (potion->lamplit) { /* burning */ + int omat = objects[obj->otyp].oc_material; + /* the code here should be merged with fire_damage */ + if (catch_lit(obj)) { + /* catch_lit does all the work if true */ + } else if (obj->oerodeproof || obj_resists(obj, 5, 95) || + !is_flammable(obj) || obj->oclass == FOOD_CLASS) { + pline("%s %s to burn for a moment.", + Yname2(obj), otense(obj, "seem")); + } else { + if ((omat == PLASTIC || omat == PAPER) && !obj->oartifact) + obj->oeroded = MAX_ERODE; + pline_The("burning oil %s %s.", + obj->oeroded == MAX_ERODE ? "destroys" : "damages", + yname(obj)); + if (obj->oeroded == MAX_ERODE) { + obj_extract_self(obj); + obfree(obj, (struct obj *)0); + obj = (struct obj *) 0; + } else { + /* we know it's carried */ + if (obj->unpaid) { + /* create a dummy duplicate to put on bill */ + verbalize("You burnt it, you bought it!"); + bill_dummy_object(obj); + } + obj->oeroded++; + } + } + } else if (potion->cursed) { + pline_The("potion spills and covers your %s with oil.", + makeplural(body_part(FINGER))); + incr_itimeout(&Glib, d(2,10)); + } else if (obj->oclass != WEAPON_CLASS && !is_weptool(obj)) { + /* the following cases apply only to weapons */ + goto more_dips; + /* Oil removes rust and corrosion, but doesn't unburn. + * Arrows, etc are classed as metallic due to arrowhead + * material, but dipping in oil shouldn't repair them. + */ + } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) || + is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) { + /* uses up potion, doesn't set obj->greased */ + pline("%s %s with an oily sheen.", + Yname2(obj), otense(obj, "gleam")); + } else { + pline("%s %s less %s.", + Yname2(obj), otense(obj, "are"), + (obj->oeroded && obj->oeroded2) ? "corroded and rusty" : + obj->oeroded ? "rusty" : "corroded"); + if (obj->oeroded > 0) obj->oeroded--; + if (obj->oeroded2 > 0) obj->oeroded2--; + wisx = TRUE; + } + exercise(A_WIS, wisx); + makeknown(potion->otyp); + useup(potion); + return 1; + } + more_dips: + + /* Allow filling of MAGIC_LAMPs to prevent identification by player */ + if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) && + (potion->otyp == POT_OIL)) { + /* Turn off engine before fueling, turn off fuel too :-) */ + if (obj->lamplit || potion->lamplit) { + useup(potion); + explode(u.ux, u.uy, 11, d(6,6), 0, EXPL_FIERY); + exercise(A_WIS, FALSE); + return 1; + } + /* Adding oil to an empty magic lamp renders it into an oil lamp */ + if ((obj->otyp == MAGIC_LAMP) && obj->spe == 0) { + obj->otyp = OIL_LAMP; + obj->age = 0; + } + if (obj->age > 1000L) { + pline("%s %s full.", Yname2(obj), otense(obj, "are")); + potion->in_use = FALSE; /* didn't go poof */ + } else { + You("fill %s with oil.", yname(obj)); + check_unpaid(potion); /* Yendorian Fuel Tax */ + obj->age += 2*potion->age; /* burns more efficiently */ + if (obj->age > 1500L) obj->age = 1500L; + useup(potion); + exercise(A_WIS, TRUE); + } + makeknown(POT_OIL); + obj->spe = 1; + update_inventory(); + return 1; + } + + potion->in_use = FALSE; /* didn't go poof */ + if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) && + (mixture = mixtype(obj, potion)) != 0) { + char oldbuf[BUFSZ], newbuf[BUFSZ]; + short old_otyp = potion->otyp; + boolean old_dknown = FALSE; + boolean more_than_one = potion->quan > 1; + + oldbuf[0] = '\0'; + if (potion->dknown) { + old_dknown = TRUE; + Sprintf(oldbuf, "%s ", + hcolor(OBJ_DESCR(objects[potion->otyp]))); + } + /* with multiple merged potions, split off one and + just clear it */ + if (potion->quan > 1L) { + singlepotion = splitobj(potion, 1L); + } else singlepotion = potion; + + if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) { + You("use it, you pay for it."); + bill_dummy_object(singlepotion); + } + singlepotion->otyp = mixture; + singlepotion->blessed = 0; + if (mixture == POT_WATER) + singlepotion->cursed = singlepotion->odiluted = 0; + else + singlepotion->cursed = obj->cursed; /* odiluted left as-is */ + singlepotion->bknown = FALSE; + if (Blind) { + singlepotion->dknown = FALSE; + } else { + singlepotion->dknown = !Hallucination; + if (mixture == POT_WATER && singlepotion->dknown) + Sprintf(newbuf, "clears"); + else + Sprintf(newbuf, "turns %s", + hcolor(OBJ_DESCR(objects[mixture]))); + pline_The("%spotion%s %s.", oldbuf, + more_than_one ? " that you dipped into" : "", + newbuf); + if(!objects[old_otyp].oc_uname && + !objects[old_otyp].oc_name_known && old_dknown) { + struct obj fakeobj; + fakeobj = zeroobj; + fakeobj.dknown = 1; + fakeobj.otyp = old_otyp; + fakeobj.oclass = POTION_CLASS; + docall(&fakeobj); + } + } + obj_extract_self(singlepotion); + singlepotion = hold_another_object(singlepotion, + "You juggle and drop %s!", + doname(singlepotion), (const char *)0); + update_inventory(); + return(1); + } + + pline("Interesting..."); + return(1); +} + + +void +djinni_from_bottle(obj) +register struct obj *obj; +{ + struct monst *mtmp; + int chance; + + if(!(mtmp = makemon(&mons[PM_DJINNI], u.ux, u.uy, NO_MM_FLAGS))){ + pline("It turns out to be empty."); + return; + } + + if (!Blind) { + pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp)); + pline("%s speaks.", Monnam(mtmp)); + } else { + You("smell acrid fumes."); + pline("%s speaks.", Something); + } + + chance = rn2(5); + if (obj->blessed) chance = (chance == 4) ? rnd(4) : 0; + else if (obj->cursed) chance = (chance == 0) ? rn2(4) : 4; + /* 0,1,2,3,4: b=80%,5,5,5,5; nc=20%,20,20,20,20; c=5%,5,5,5,80 */ + + switch (chance) { + case 0 : verbalize("I am in your debt. I will grant one wish!"); + makewish(); + mongone(mtmp); + break; + case 1 : verbalize("Thank you for freeing me!"); + (void) tamedog(mtmp, (struct obj *)0); + break; + case 2 : verbalize("You freed me!"); + mtmp->mpeaceful = TRUE; + set_malign(mtmp); + break; + case 3 : verbalize("It is about time!"); + pline("%s vanishes.", Monnam(mtmp)); + mongone(mtmp); + break; + default: verbalize("You disturbed me, fool!"); + break; + } +} + +/* clone a gremlin or mold (2nd arg non-null implies heat as the trigger); + hit points are cut in half (odd HP stays with original) */ +struct monst * +split_mon(mon, mtmp) +struct monst *mon, /* monster being split */ + *mtmp; /* optional attacker whose heat triggered it */ +{ + struct monst *mtmp2; + char reason[BUFSZ]; + + reason[0] = '\0'; + if (mtmp) Sprintf(reason, " from %s heat", + (mtmp == &youmonst) ? (const char *)"your" : + (const char *)s_suffix(mon_nam(mtmp))); + + if (mon == &youmonst) { + mtmp2 = cloneu(); + if (mtmp2) { + mtmp2->mhpmax = u.mhmax / 2; + u.mhmax -= mtmp2->mhpmax; + flags.botl = 1; + You("multiply%s!", reason); + } + } else { + mtmp2 = clone_mon(mon, 0, 0); + if (mtmp2) { + mtmp2->mhpmax = mon->mhpmax / 2; + mon->mhpmax -= mtmp2->mhpmax; + if (canspotmon(mon)) + pline("%s multiplies%s!", Monnam(mon), reason); + } + } + return mtmp2; +} + +#endif /* OVLB */ + +/*potion.c*/ diff --git a/src/pray.c b/src/pray.c new file mode 100644 index 0000000..0df2855 --- /dev/null +++ b/src/pray.c @@ -0,0 +1,1872 @@ +/* SCCS Id: @(#)pray.c 3.4 2003/03/23 */ +/* Copyright (c) Benson I. Margulies, Mike Stephenson, Steve Linhart, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "epri.h" + +STATIC_PTR int NDECL(prayer_done); +STATIC_DCL struct obj *NDECL(worst_cursed_item); +STATIC_DCL int NDECL(in_trouble); +STATIC_DCL void FDECL(fix_worst_trouble,(int)); +STATIC_DCL void FDECL(angrygods,(ALIGNTYP_P)); +STATIC_DCL void FDECL(at_your_feet, (const char *)); +#ifdef ELBERETH +STATIC_DCL void NDECL(gcrownu); +#endif /*ELBERETH*/ +STATIC_DCL void FDECL(pleased,(ALIGNTYP_P)); +STATIC_DCL void FDECL(godvoice,(ALIGNTYP_P,const char*)); +STATIC_DCL void FDECL(god_zaps_you,(ALIGNTYP_P)); +STATIC_DCL void FDECL(fry_by_god,(ALIGNTYP_P)); +STATIC_DCL void FDECL(gods_angry,(ALIGNTYP_P)); +STATIC_DCL void FDECL(gods_upset,(ALIGNTYP_P)); +STATIC_DCL void FDECL(consume_offering,(struct obj *)); +STATIC_DCL boolean FDECL(water_prayer,(BOOLEAN_P)); +STATIC_DCL boolean FDECL(blocked_boulder,(int,int)); + +/* simplify a few tests */ +#define Cursed_obj(obj,typ) ((obj) && (obj)->otyp == (typ) && (obj)->cursed) + +/* + * Logic behind deities and altars and such: + * + prayers are made to your god if not on an altar, and to the altar's god + * if you are on an altar + * + If possible, your god answers all prayers, which is why bad things happen + * if you try to pray on another god's altar + * + sacrifices work basically the same way, but the other god may decide to + * accept your allegiance, after which they are your god. If rejected, + * your god takes over with your punishment. + * + if you're in Gehennom, all messages come from Moloch + */ + +/* + * Moloch, who dwells in Gehennom, is the "renegade" cruel god + * responsible for the theft of the Amulet from Marduk, the Creator. + * Moloch is unaligned. + */ +static const char *Moloch = "Moloch"; + +static const char *godvoices[] = { + "booms out", + "thunders", + "rings out", + "booms", +}; + +/* values calculated when prayer starts, and used when completed */ +static aligntyp p_aligntyp; +static int p_trouble; +static int p_type; /* (-1)-3: (-1)=really naughty, 3=really good */ + +#define PIOUS 20 +#define DEVOUT 14 +#define FERVENT 9 +#define STRIDENT 4 + +/* + * The actual trouble priority is determined by the order of the + * checks performed in in_trouble() rather than by these numeric + * values, so keep that code and these values synchronized in + * order to have the values be meaningful. + */ + +#define TROUBLE_STONED 13 +#define TROUBLE_SLIMED 12 +#define TROUBLE_STRANGLED 11 +#define TROUBLE_LAVA 10 +#define TROUBLE_SICK 9 +#define TROUBLE_STARVING 8 +#define TROUBLE_HIT 7 +#define TROUBLE_LYCANTHROPE 6 +#define TROUBLE_COLLAPSING 5 +#define TROUBLE_STUCK_IN_WALL 4 +#define TROUBLE_CURSED_LEVITATION 3 +#define TROUBLE_UNUSEABLE_HANDS 2 +#define TROUBLE_CURSED_BLINDFOLD 1 + +#define TROUBLE_PUNISHED (-1) +#define TROUBLE_FUMBLING (-2) +#define TROUBLE_CURSED_ITEMS (-3) +#define TROUBLE_SADDLE (-4) +#define TROUBLE_BLIND (-5) +#define TROUBLE_POISONED (-6) +#define TROUBLE_WOUNDED_LEGS (-7) +#define TROUBLE_HUNGRY (-8) +#define TROUBLE_STUNNED (-9) +#define TROUBLE_CONFUSED (-10) +#define TROUBLE_HALLUCINATION (-11) + +/* We could force rehumanize of polyselfed people, but we can't tell + unintentional shape changes from the other kind. Oh well. + 3.4.2: make an exception if polymorphed into a form which lacks + hands; that's a case where the ramifications override this doubt. + */ + +/* Return 0 if nothing particular seems wrong, positive numbers for + serious trouble, and negative numbers for comparative annoyances. This + returns the worst problem. There may be others, and the gods may fix + more than one. + +This could get as bizarre as noting surrounding opponents, (or hostile dogs), +but that's really hard. + */ + +#define ugod_is_angry() (u.ualign.record < 0) +#define on_altar() IS_ALTAR(levl[u.ux][u.uy].typ) +#define on_shrine() ((levl[u.ux][u.uy].altarmask & AM_SHRINE) != 0) +#define a_align(x,y) ((aligntyp)Amask2align(levl[x][y].altarmask & AM_MASK)) + +STATIC_OVL int +in_trouble() +{ + struct obj *otmp; + int i, j, count=0; + +/* Borrowed from eat.c */ + +#define SATIATED 0 +#define NOT_HUNGRY 1 +#define HUNGRY 2 +#define WEAK 3 +#define FAINTING 4 +#define FAINTED 5 +#define STARVED 6 + + /* + * major troubles + */ + if(Stoned) return(TROUBLE_STONED); + if(Slimed) return(TROUBLE_SLIMED); + if(Strangled) return(TROUBLE_STRANGLED); + if(u.utrap && u.utraptype == TT_LAVA) return(TROUBLE_LAVA); + if(Sick) return(TROUBLE_SICK); + if(u.uhs >= WEAK) return(TROUBLE_STARVING); + if (Upolyd ? (u.mh <= 5 || u.mh*7 <= u.mhmax) : + (u.uhp <= 5 || u.uhp*7 <= u.uhpmax)) return TROUBLE_HIT; + if(u.ulycn >= LOW_PM) return(TROUBLE_LYCANTHROPE); + if(near_capacity() >= EXT_ENCUMBER && AMAX(A_STR)-ABASE(A_STR) > 3) + return(TROUBLE_COLLAPSING); + + for (i= -1; i<=1; i++) for(j= -1; j<=1; j++) { + if (!i && !j) continue; + if (!isok(u.ux+i, u.uy+j) || IS_ROCK(levl[u.ux+i][u.uy+j].typ) + || (blocked_boulder(i,j) && !throws_rocks(youmonst.data))) + count++; + } + if (count == 8 && !Passes_walls) + return(TROUBLE_STUCK_IN_WALL); + + if (Cursed_obj(uarmf, LEVITATION_BOOTS) || + stuck_ring(uleft, RIN_LEVITATION) || + stuck_ring(uright, RIN_LEVITATION)) + return(TROUBLE_CURSED_LEVITATION); + if (nohands(youmonst.data) || !freehand()) { + /* for bag/box access [cf use_container()]... + make sure it's a case that we know how to handle; + otherwise "fix all troubles" would get stuck in a loop */ + if (welded(uwep)) return TROUBLE_UNUSEABLE_HANDS; + if (Upolyd && nohands(youmonst.data) && (!Unchanging || + ((otmp = unchanger()) != 0 && otmp->cursed))) + return TROUBLE_UNUSEABLE_HANDS; + } + if(Blindfolded && ublindf->cursed) return(TROUBLE_CURSED_BLINDFOLD); + + /* + * minor troubles + */ + if(Punished) return(TROUBLE_PUNISHED); + if (Cursed_obj(uarmg, GAUNTLETS_OF_FUMBLING) || + Cursed_obj(uarmf, FUMBLE_BOOTS)) + return TROUBLE_FUMBLING; + if (worst_cursed_item()) return TROUBLE_CURSED_ITEMS; +#ifdef STEED + if (u.usteed) { /* can't voluntarily dismount from a cursed saddle */ + otmp = which_armor(u.usteed, W_SADDLE); + if (Cursed_obj(otmp, SADDLE)) return TROUBLE_SADDLE; + } +#endif + + if (Blinded > 1 && haseyes(youmonst.data)) return(TROUBLE_BLIND); + for(i=0; i= HUNGRY) return(TROUBLE_HUNGRY); + if(HStun) return (TROUBLE_STUNNED); + if(HConfusion) return (TROUBLE_CONFUSED); + if(Hallucination) return(TROUBLE_HALLUCINATION); + return(0); +} + +/* select an item for TROUBLE_CURSED_ITEMS */ +STATIC_OVL struct obj * +worst_cursed_item() +{ + register struct obj *otmp; + + /* if strained or worse, check for loadstone first */ + if (near_capacity() >= HVY_ENCUMBER) { + for (otmp = invent; otmp; otmp = otmp->nobj) + if (Cursed_obj(otmp, LOADSTONE)) return otmp; + } + /* weapon takes precedence if it is interfering + with taking off a ring or putting on a shield */ + if (welded(uwep) && (uright || bimanual(uwep))) { /* weapon */ + otmp = uwep; + /* gloves come next, due to rings */ + } else if (uarmg && uarmg->cursed) { /* gloves */ + otmp = uarmg; + /* then shield due to two handed weapons and spells */ + } else if (uarms && uarms->cursed) { /* shield */ + otmp = uarms; + /* then cloak due to body armor */ + } else if (uarmc && uarmc->cursed) { /* cloak */ + otmp = uarmc; + } else if (uarm && uarm->cursed) { /* suit */ + otmp = uarm; + } else if (uarmh && uarmh->cursed) { /* helmet */ + otmp = uarmh; + } else if (uarmf && uarmf->cursed) { /* boots */ + otmp = uarmf; +#ifdef TOURIST + } else if (uarmu && uarmu->cursed) { /* shirt */ + otmp = uarmu; +#endif + } else if (uamul && uamul->cursed) { /* amulet */ + otmp = uamul; + } else if (uleft && uleft->cursed) { /* left ring */ + otmp = uleft; + } else if (uright && uright->cursed) { /* right ring */ + otmp = uright; + } else if (ublindf && ublindf->cursed) { /* eyewear */ + otmp = ublindf; /* must be non-blinding lenses */ + /* if weapon wasn't handled above, do it now */ + } else if (welded(uwep)) { /* weapon */ + otmp = uwep; + /* active secondary weapon even though it isn't welded */ + } else if (uswapwep && uswapwep->cursed && u.twoweap) { + otmp = uswapwep; + /* all worn items ought to be handled by now */ + } else { + for (otmp = invent; otmp; otmp = otmp->nobj) { + if (!otmp->cursed) continue; + if (otmp->otyp == LOADSTONE || confers_luck(otmp)) + break; + } + } + return otmp; +} + +STATIC_OVL void +fix_worst_trouble(trouble) +register int trouble; +{ + int i; + struct obj *otmp = 0; + const char *what = (const char *)0; + static NEARDATA const char leftglow[] = "left ring softly glows", + rightglow[] = "right ring softly glows"; + + switch (trouble) { + case TROUBLE_STONED: + You_feel("more limber."); + Stoned = 0; + flags.botl = 1; + delayed_killer = 0; + break; + case TROUBLE_SLIMED: + pline_The("slime disappears."); + Slimed = 0; + flags.botl = 1; + delayed_killer = 0; + break; + case TROUBLE_STRANGLED: + if (uamul && uamul->otyp == AMULET_OF_STRANGULATION) { + Your("amulet vanishes!"); + useup(uamul); + } + You("can breathe again."); + Strangled = 0; + flags.botl = 1; + break; + case TROUBLE_LAVA: + You("are back on solid ground."); + /* teleport should always succeed, but if not, + * just untrap them. + */ + if(!safe_teleds(FALSE)) + u.utrap = 0; + break; + case TROUBLE_STARVING: + losestr(-1); + /* fall into... */ + case TROUBLE_HUNGRY: + Your("%s feels content.", body_part(STOMACH)); + init_uhunger(); + flags.botl = 1; + break; + case TROUBLE_SICK: + You_feel("better."); + make_sick(0L, (char *) 0, FALSE, SICK_ALL); + break; + case TROUBLE_HIT: + /* "fix all troubles" will keep trying if hero has + 5 or less hit points, so make sure they're always + boosted to be more than that */ + You_feel("much better."); + if (Upolyd) { + u.mhmax += rnd(5); + if (u.mhmax <= 5) u.mhmax = 5+1; + u.mh = u.mhmax; + } + if (u.uhpmax < u.ulevel * 5 + 11) u.uhpmax += rnd(5); + if (u.uhpmax <= 5) u.uhpmax = 5+1; + u.uhp = u.uhpmax; + flags.botl = 1; + break; + case TROUBLE_COLLAPSING: + ABASE(A_STR) = AMAX(A_STR); + flags.botl = 1; + break; + case TROUBLE_STUCK_IN_WALL: + Your("surroundings change."); + /* no control, but works on no-teleport levels */ + (void) safe_teleds(FALSE); + break; + case TROUBLE_CURSED_LEVITATION: + if (Cursed_obj(uarmf, LEVITATION_BOOTS)) { + otmp = uarmf; + } else if ((otmp = stuck_ring(uleft,RIN_LEVITATION)) !=0) { + if (otmp == uleft) what = leftglow; + } else if ((otmp = stuck_ring(uright,RIN_LEVITATION))!=0) { + if (otmp == uright) what = rightglow; + } + goto decurse; + case TROUBLE_UNUSEABLE_HANDS: + if (welded(uwep)) { + otmp = uwep; + goto decurse; + } + if (Upolyd && nohands(youmonst.data)) { + if (!Unchanging) { + Your("shape becomes uncertain."); + rehumanize(); /* "You return to {normal} form." */ + } else if ((otmp = unchanger()) != 0 && otmp->cursed) { + /* otmp is an amulet of unchanging */ + goto decurse; + } + } + if (nohands(youmonst.data) || !freehand()) + impossible("fix_worst_trouble: couldn't cure hands."); + break; + case TROUBLE_CURSED_BLINDFOLD: + otmp = ublindf; + goto decurse; + case TROUBLE_LYCANTHROPE: + you_unwere(TRUE); + break; + /* + */ + case TROUBLE_PUNISHED: + Your("chain disappears."); + unpunish(); + break; + case TROUBLE_FUMBLING: + if (Cursed_obj(uarmg, GAUNTLETS_OF_FUMBLING)) + otmp = uarmg; + else if (Cursed_obj(uarmf, FUMBLE_BOOTS)) + otmp = uarmf; + goto decurse; + /*NOTREACHED*/ + break; + case TROUBLE_CURSED_ITEMS: + otmp = worst_cursed_item(); + if (otmp == uright) what = rightglow; + else if (otmp == uleft) what = leftglow; +decurse: + if (!otmp) { + impossible("fix_worst_trouble: nothing to uncurse."); + return; + } + uncurse(otmp); + if (!Blind) { + Your("%s %s.", what ? what : + (const char *)aobjnam(otmp, "softly glow"), + hcolor(NH_AMBER)); + otmp->bknown = TRUE; + } + update_inventory(); + break; + case TROUBLE_POISONED: + if (Hallucination) + pline("There's a tiger in your tank."); + else + You_feel("in good health again."); + for(i=0; ibknown = TRUE; + } + break; +#endif + } +} + +/* "I am sometimes shocked by... the nuns who never take a bath without + * wearing a bathrobe all the time. When asked why, since no man can see them, + * they reply 'Oh, but you forget the good God'. Apparently they conceive of + * the Deity as a Peeping Tom, whose omnipotence enables Him to see through + * bathroom walls, but who is foiled by bathrobes." --Bertrand Russell, 1943 + * Divine wrath, dungeon walls, and armor follow the same principle. + */ +STATIC_OVL void +god_zaps_you(resp_god) +aligntyp resp_god; +{ + if (u.uswallow) { + pline("Suddenly a bolt of lightning comes down at you from the heavens!"); + pline("It strikes %s!", mon_nam(u.ustuck)); + if (!resists_elec(u.ustuck)) { + pline("%s fries to a crisp!", Monnam(u.ustuck)); + /* Yup, you get experience. It takes guts to successfully + * pull off this trick on your god, anyway. + */ + xkilled(u.ustuck, 0); + } else + pline("%s seems unaffected.", Monnam(u.ustuck)); + } else { + pline("Suddenly, a bolt of lightning strikes you!"); + if (Reflecting) { + shieldeff(u.ux, u.uy); + if (Blind) + pline("For some reason you're unaffected."); + else + (void) ureflects("%s reflects from your %s.", "It"); + } else if (Shock_resistance) { + shieldeff(u.ux, u.uy); + pline("It seems not to affect you."); + } else + fry_by_god(resp_god); + } + + pline("%s is not deterred...", align_gname(resp_god)); + if (u.uswallow) { + pline("A wide-angle disintegration beam aimed at you hits %s!", + mon_nam(u.ustuck)); + if (!resists_disint(u.ustuck)) { + pline("%s fries to a crisp!", Monnam(u.ustuck)); + xkilled(u.ustuck, 2); /* no corpse */ + } else + pline("%s seems unaffected.", Monnam(u.ustuck)); + } else { + pline("A wide-angle disintegration beam hits you!"); + + /* disintegrate shield and body armor before disintegrating + * the impudent mortal, like black dragon breath -3. + */ + if (uarms && !(EReflecting & W_ARMS) && + !(EDisint_resistance & W_ARMS)) + (void) destroy_arm(uarms); + if (uarmc && !(EReflecting & W_ARMC) && + !(EDisint_resistance & W_ARMC)) + (void) destroy_arm(uarmc); + if (uarm && !(EReflecting & W_ARM) && + !(EDisint_resistance & W_ARM) && !uarmc) + (void) destroy_arm(uarm); +#ifdef TOURIST + if (uarmu && !uarm && !uarmc) (void) destroy_arm(uarmu); +#endif + if (!Disint_resistance) + fry_by_god(resp_god); + else { + You("bask in its %s glow for a minute...", NH_BLACK); + godvoice(resp_god, "I believe it not!"); + } + if (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)) { + /* one more try for high altars */ + verbalize("Thou cannot escape my wrath, mortal!"); + summon_minion(resp_god, FALSE); + summon_minion(resp_god, FALSE); + summon_minion(resp_god, FALSE); + verbalize("Destroy %s, my servants!", uhim()); + } + } +} + +STATIC_OVL void +fry_by_god(resp_god) +aligntyp resp_god; +{ + char killerbuf[64]; + + You("fry to a crisp."); + killer_format = KILLED_BY; + Sprintf(killerbuf, "the wrath of %s", align_gname(resp_god)); + killer = killerbuf; + done(DIED); +} + +STATIC_OVL void +angrygods(resp_god) +aligntyp resp_god; +{ + register int maxanger; + + if(Inhell) resp_god = A_NONE; + u.ublessed = 0; + + /* changed from tmp = u.ugangr + abs (u.uluck) -- rph */ + /* added test for alignment diff -dlc */ + if(resp_god != u.ualign.type) + maxanger = u.ualign.record/2 + (Luck > 0 ? -Luck/3 : -Luck); + else + maxanger = 3*u.ugangr + + ((Luck > 0 || u.ualign.record >= STRIDENT) ? -Luck/3 : -Luck); + if (maxanger < 1) maxanger = 1; /* possible if bad align & good luck */ + else if (maxanger > 15) maxanger = 15; /* be reasonable */ + + switch (rn2(maxanger)) { + case 0: + case 1: You_feel("that %s is %s.", align_gname(resp_god), + Hallucination ? "bummed" : "displeased"); + break; + case 2: + case 3: + godvoice(resp_god,(char *)0); + pline("\"Thou %s, %s.\"", + (ugod_is_angry() && resp_god == u.ualign.type) + ? "hast strayed from the path" : + "art arrogant", + youmonst.data->mlet == S_HUMAN ? "mortal" : "creature"); + verbalize("Thou must relearn thy lessons!"); + (void) adjattrib(A_WIS, -1, FALSE); + losexp((char *)0); + break; + case 6: if (!Punished) { + gods_angry(resp_god); + punish((struct obj *)0); + break; + } /* else fall thru */ + case 4: + case 5: gods_angry(resp_god); + if (!Blind && !Antimagic) + pline("%s glow surrounds you.", + An(hcolor(NH_BLACK))); + rndcurse(); + break; + case 7: + case 8: godvoice(resp_god,(char *)0); + verbalize("Thou durst %s me?", + (on_altar() && + (a_align(u.ux,u.uy) != resp_god)) ? + "scorn":"call upon"); + pline("\"Then die, %s!\"", + youmonst.data->mlet == S_HUMAN ? "mortal" : "creature"); + summon_minion(resp_god, FALSE); + break; + + default: gods_angry(resp_god); + god_zaps_you(resp_god); + break; + } + u.ublesscnt = rnz(300); + return; +} + +/* helper to print "str appears at your feet", or appropriate */ +static void +at_your_feet(str) + const char *str; +{ + if (Blind) str = Something; + if (u.uswallow) { + /* barrier between you and the floor */ + pline("%s %s into %s %s.", str, vtense(str, "drop"), + s_suffix(mon_nam(u.ustuck)), mbodypart(u.ustuck, STOMACH)); + } else { + pline("%s %s %s your %s!", str, + Blind ? "lands" : vtense(str, "appear"), + Levitation ? "beneath" : "at", + makeplural(body_part(FOOT))); + } +} + +#ifdef ELBERETH +STATIC_OVL void +gcrownu() +{ + struct obj *obj; + boolean already_exists, in_hand; + short class_gift; + int sp_no; +#define ok_wep(o) ((o) && ((o)->oclass == WEAPON_CLASS || is_weptool(o))) + + HSee_invisible |= FROMOUTSIDE; + HFire_resistance |= FROMOUTSIDE; + HCold_resistance |= FROMOUTSIDE; + HShock_resistance |= FROMOUTSIDE; + HSleep_resistance |= FROMOUTSIDE; + HPoison_resistance |= FROMOUTSIDE; + godvoice(u.ualign.type, (char *)0); + + obj = ok_wep(uwep) ? uwep : 0; + already_exists = in_hand = FALSE; /* lint suppression */ + switch (u.ualign.type) { + case A_LAWFUL: + u.uevent.uhand_of_elbereth = 1; + verbalize("I crown thee... The Hand of Elbereth!"); + break; + case A_NEUTRAL: + u.uevent.uhand_of_elbereth = 2; + in_hand = (uwep && uwep->oartifact == ART_VORPAL_BLADE); + already_exists = exist_artifact(LONG_SWORD, artiname(ART_VORPAL_BLADE)); + verbalize("Thou shalt be my Envoy of Balance!"); + break; + case A_CHAOTIC: + u.uevent.uhand_of_elbereth = 3; + in_hand = (uwep && uwep->oartifact == ART_STORMBRINGER); + already_exists = exist_artifact(RUNESWORD, artiname(ART_STORMBRINGER)); + verbalize("Thou art chosen to %s for My Glory!", + already_exists && !in_hand ? "take lives" : "steal souls"); + break; + } + + class_gift = STRANGE_OBJECT; + /* 3.3.[01] had this in the A_NEUTRAL case below, + preventing chaotic wizards from receiving a spellbook */ + if (Role_if(PM_WIZARD) && + (!uwep || (uwep->oartifact != ART_VORPAL_BLADE && + uwep->oartifact != ART_STORMBRINGER)) && + !carrying(SPE_FINGER_OF_DEATH)) { + class_gift = SPE_FINGER_OF_DEATH; + make_splbk: + obj = mksobj(class_gift, TRUE, FALSE); + bless(obj); + obj->bknown = TRUE; + at_your_feet("A spellbook"); + dropy(obj); + u.ugifts++; + /* when getting a new book for known spell, enhance + currently wielded weapon rather than the book */ + for (sp_no = 0; sp_no < MAXSPELL; sp_no++) + if (spl_book[sp_no].sp_id == class_gift) { + if (ok_wep(uwep)) obj = uwep; /* to be blessed,&c */ + break; + } + } else if (Role_if(PM_MONK) && + (!uwep || !uwep->oartifact) && + !carrying(SPE_RESTORE_ABILITY)) { + /* monks rarely wield a weapon */ + class_gift = SPE_RESTORE_ABILITY; + goto make_splbk; + } + + switch (u.ualign.type) { + case A_LAWFUL: + if (class_gift != STRANGE_OBJECT) { + ; /* already got bonus above */ + } else if (obj && obj->otyp == LONG_SWORD && !obj->oartifact) { + if (!Blind) Your("sword shines brightly for a moment."); + obj = oname(obj, artiname(ART_EXCALIBUR)); + if (obj && obj->oartifact == ART_EXCALIBUR) u.ugifts++; + } + /* acquire Excalibur's skill regardless of weapon or gift */ + unrestrict_weapon_skill(P_LONG_SWORD); + if (obj && obj->oartifact == ART_EXCALIBUR) + discover_artifact(ART_EXCALIBUR); + break; + case A_NEUTRAL: + if (class_gift != STRANGE_OBJECT) { + ; /* already got bonus above */ + } else if (in_hand) { + Your("%s goes snicker-snack!", xname(obj)); + obj->dknown = TRUE; + } else if (!already_exists) { + obj = mksobj(LONG_SWORD, FALSE, FALSE); + obj = oname(obj, artiname(ART_VORPAL_BLADE)); + obj->spe = 1; + at_your_feet("A sword"); + dropy(obj); + u.ugifts++; + } + /* acquire Vorpal Blade's skill regardless of weapon or gift */ + unrestrict_weapon_skill(P_LONG_SWORD); + if (obj && obj->oartifact == ART_VORPAL_BLADE) + discover_artifact(ART_VORPAL_BLADE); + break; + case A_CHAOTIC: + { + char swordbuf[BUFSZ]; + + Sprintf(swordbuf, "%s sword", hcolor(NH_BLACK)); + if (class_gift != STRANGE_OBJECT) { + ; /* already got bonus above */ + } else if (in_hand) { + Your("%s hums ominously!", swordbuf); + obj->dknown = TRUE; + } else if (!already_exists) { + obj = mksobj(RUNESWORD, FALSE, FALSE); + obj = oname(obj, artiname(ART_STORMBRINGER)); + at_your_feet(An(swordbuf)); + obj->spe = 1; + dropy(obj); + u.ugifts++; + } + /* acquire Stormbringer's skill regardless of weapon or gift */ + unrestrict_weapon_skill(P_BROAD_SWORD); + if (obj && obj->oartifact == ART_STORMBRINGER) + discover_artifact(ART_STORMBRINGER); + break; + } + default: + obj = 0; /* lint */ + break; + } + + /* enhance weapon regardless of alignment or artifact status */ + if (ok_wep(obj)) { + bless(obj); + obj->oeroded = obj->oeroded2 = 0; + obj->oerodeproof = TRUE; + obj->bknown = obj->rknown = TRUE; + if (obj->spe < 1) obj->spe = 1; + /* acquire skill in this weapon */ + unrestrict_weapon_skill(weapon_type(obj)); + } else if (class_gift == STRANGE_OBJECT) { + /* opportunity knocked, but there was nobody home... */ + You_feel("unworthy."); + } + update_inventory(); + return; +} +#endif /*ELBERETH*/ + +STATIC_OVL void +pleased(g_align) + aligntyp g_align; +{ + /* don't use p_trouble, worst trouble may get fixed while praying */ + int trouble = in_trouble(); /* what's your worst difficulty? */ + int pat_on_head = 0, kick_on_butt; + + You_feel("that %s is %s.", align_gname(g_align), + u.ualign.record >= DEVOUT ? + Hallucination ? "pleased as punch" : "well-pleased" : + u.ualign.record >= STRIDENT ? + Hallucination ? "ticklish" : "pleased" : + Hallucination ? "full" : "satisfied"); + + /* not your deity */ + if (on_altar() && p_aligntyp != u.ualign.type) { + adjalign(-1); + return; + } else if (u.ualign.record < 2 && trouble <= 0) adjalign(1); + + /* depending on your luck & align level, the god you prayed to will: + - fix your worst problem if it's major. + - fix all your major problems. + - fix your worst problem if it's minor. + - fix all of your problems. + - do you a gratuitous favor. + + if you make it to the the last category, you roll randomly again + to see what they do for you. + + If your luck is at least 0, then you are guaranteed rescued + from your worst major problem. */ + + if (!trouble && u.ualign.record >= DEVOUT) { + /* if hero was in trouble, but got better, no special favor */ + if (p_trouble == 0) pat_on_head = 1; + } else { + int action = rn1(Luck + (on_altar() ? 3 + on_shrine() : 2), 1); + + if (!on_altar()) action = min(action, 3); + if (u.ualign.record < STRIDENT) + action = (u.ualign.record > 0 || !rnl(2)) ? 1 : 0; + + switch(min(action,5)) { + case 5: pat_on_head = 1; + case 4: do fix_worst_trouble(trouble); + while ((trouble = in_trouble()) != 0); + break; + + case 3: fix_worst_trouble(trouble); + case 2: while ((trouble = in_trouble()) > 0) + fix_worst_trouble(trouble); + break; + + case 1: if (trouble > 0) fix_worst_trouble(trouble); + case 0: break; /* your god blows you off, too bad */ + } + } + + /* note: can't get pat_on_head unless all troubles have just been + fixed or there were no troubles to begin with; hallucination + won't be in effect so special handling for it is superfluous */ + if(pat_on_head) + switch(rn2((Luck + 6)>>1)) { + case 0: break; + case 1: + if (uwep && (welded(uwep) || uwep->oclass == WEAPON_CLASS || + is_weptool(uwep))) { + char repair_buf[BUFSZ]; + + *repair_buf = '\0'; + if (uwep->oeroded || uwep->oeroded2) + Sprintf(repair_buf, " and %s now as good as new", + otense(uwep, "are")); + + if (uwep->cursed) { + uncurse(uwep); + uwep->bknown = TRUE; + if (!Blind) + Your("%s %s%s.", aobjnam(uwep, "softly glow"), + hcolor(NH_AMBER), repair_buf); + else You_feel("the power of %s over your %s.", + u_gname(), xname(uwep)); + *repair_buf = '\0'; + } else if (!uwep->blessed) { + bless(uwep); + uwep->bknown = TRUE; + if (!Blind) + Your("%s with %s aura%s.", + aobjnam(uwep, "softly glow"), + an(hcolor(NH_LIGHT_BLUE)), repair_buf); + else You_feel("the blessing of %s over your %s.", + u_gname(), xname(uwep)); + *repair_buf = '\0'; + } + + /* fix any rust/burn/rot damage, but don't protect + against future damage */ + if (uwep->oeroded || uwep->oeroded2) { + uwep->oeroded = uwep->oeroded2 = 0; + /* only give this message if we didn't just bless + or uncurse (which has already given a message) */ + if (*repair_buf) + Your("%s as good as new!", + aobjnam(uwep, Blind ? "feel" : "look")); + } + update_inventory(); + } + break; + case 3: + /* takes 2 hints to get the music to enter the stronghold */ + if (!u.uevent.uopened_dbridge) { + if (u.uevent.uheard_tune < 1) { + godvoice(g_align,(char *)0); + verbalize("Hark, %s!", + youmonst.data->mlet == S_HUMAN ? "mortal" : "creature"); + verbalize( + "To enter the castle, thou must play the right tune!"); + u.uevent.uheard_tune++; + break; + } else if (u.uevent.uheard_tune < 2) { + You_hear("a divine music..."); + pline("It sounds like: \"%s\".", tune); + u.uevent.uheard_tune++; + break; + } + } + /* Otherwise, falls into next case */ + case 2: + if (!Blind) + You("are surrounded by %s glow.", an(hcolor(NH_GOLDEN))); + /* if any levels have been lost (and not yet regained), + treat this effect like blessed full healing */ + if (u.ulevel < u.ulevelmax) { + u.ulevelmax -= 1; /* see potion.c */ + pluslvl(FALSE); + } else { + u.uhpmax += 5; + if (Upolyd) u.mhmax += 5; + } + u.uhp = u.uhpmax; + if (Upolyd) u.mh = u.mhmax; + ABASE(A_STR) = AMAX(A_STR); + if (u.uhunger < 900) init_uhunger(); + if (u.uluck < 0) u.uluck = 0; + make_blinded(0L,TRUE); + flags.botl = 1; + break; + case 4: { + register struct obj *otmp; + int any = 0; + + if (Blind) + You_feel("the power of %s.", u_gname()); + else You("are surrounded by %s aura.", + an(hcolor(NH_LIGHT_BLUE))); + for(otmp=invent; otmp; otmp=otmp->nobj) { + if (otmp->cursed) { + uncurse(otmp); + if (!Blind) { + Your("%s %s.", aobjnam(otmp, "softly glow"), + hcolor(NH_AMBER)); + otmp->bknown = TRUE; + ++any; + } + } + } + if (any) update_inventory(); + break; + } + case 5: { + const char *msg="\"and thus I grant thee the gift of %s!\""; + godvoice(u.ualign.type, "Thou hast pleased me with thy progress,"); + if (!(HTelepat & INTRINSIC)) { + HTelepat |= FROMOUTSIDE; + pline(msg, "Telepathy"); + if (Blind) see_monsters(); + } else if (!(HFast & INTRINSIC)) { + HFast |= FROMOUTSIDE; + pline(msg, "Speed"); + } else if (!(HStealth & INTRINSIC)) { + HStealth |= FROMOUTSIDE; + pline(msg, "Stealth"); + } else { + if (!(HProtection & INTRINSIC)) { + HProtection |= FROMOUTSIDE; + if (!u.ublessed) u.ublessed = rn1(3, 2); + } else u.ublessed++; + pline(msg, "my protection"); + } + verbalize("Use it wisely in my name!"); + break; + } + case 7: + case 8: + case 9: /* KMH -- can occur during full moons */ +#ifdef ELBERETH + if (u.ualign.record >= PIOUS && !u.uevent.uhand_of_elbereth) { + gcrownu(); + break; + } /* else FALLTHRU */ +#endif /*ELBERETH*/ + case 6: { + struct obj *otmp; + int sp_no, trycnt = u.ulevel + 1; + + at_your_feet("An object"); + /* not yet known spells given preference over already known ones */ + /* Also, try to grant a spell for which there is a skill slot */ + otmp = mkobj(SPBOOK_CLASS, TRUE); + while (--trycnt > 0) { + if (otmp->otyp != SPE_BLANK_PAPER) { + for (sp_no = 0; sp_no < MAXSPELL; sp_no++) + if (spl_book[sp_no].sp_id == otmp->otyp) break; + if (sp_no == MAXSPELL && + !P_RESTRICTED(spell_skilltype(otmp->otyp))) + break; /* usable, but not yet known */ + } else { + if (!objects[SPE_BLANK_PAPER].oc_name_known || + carrying(MAGIC_MARKER)) break; + } + otmp->otyp = rnd_class(bases[SPBOOK_CLASS], SPE_BLANK_PAPER); + } + bless(otmp); + place_object(otmp, u.ux, u.uy); + break; + } + default: impossible("Confused deity!"); + break; + } + + u.ublesscnt = rnz(350); + kick_on_butt = u.uevent.udemigod ? 1 : 0; +#ifdef ELBERETH + if (u.uevent.uhand_of_elbereth) kick_on_butt++; +#endif + if (kick_on_butt) u.ublesscnt += kick_on_butt * rnz(1000); + + return; +} + +/* either blesses or curses water on the altar, + * returns true if it found any water here. + */ +STATIC_OVL boolean +water_prayer(bless_water) + boolean bless_water; +{ + register struct obj* otmp; + register long changed = 0; + boolean other = FALSE, bc_known = !(Blind || Hallucination); + + for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) { + /* turn water into (un)holy water */ + if (otmp->otyp == POT_WATER && + (bless_water ? !otmp->blessed : !otmp->cursed)) { + otmp->blessed = bless_water; + otmp->cursed = !bless_water; + otmp->bknown = bc_known; + changed += otmp->quan; + } else if(otmp->oclass == POTION_CLASS) + other = TRUE; + } + if(!Blind && changed) { + pline("%s potion%s on the altar glow%s %s for a moment.", + ((other && changed > 1L) ? "Some of the" : + (other ? "One of the" : "The")), + ((other || changed > 1L) ? "s" : ""), (changed > 1L ? "" : "s"), + (bless_water ? hcolor(NH_LIGHT_BLUE) : hcolor(NH_BLACK))); + } + return((boolean)(changed > 0L)); +} + +STATIC_OVL void +godvoice(g_align, words) + aligntyp g_align; + const char *words; +{ + const char *quot = ""; + if(words) + quot = "\""; + else + words = ""; + + pline_The("voice of %s %s: %s%s%s", align_gname(g_align), + godvoices[rn2(SIZE(godvoices))], quot, words, quot); +} + +STATIC_OVL void +gods_angry(g_align) + aligntyp g_align; +{ + godvoice(g_align, "Thou hast angered me."); +} + +/* The g_align god is upset with you. */ +STATIC_OVL void +gods_upset(g_align) + aligntyp g_align; +{ + if(g_align == u.ualign.type) u.ugangr++; + else if(u.ugangr) u.ugangr--; + angrygods(g_align); +} + +static NEARDATA const char sacrifice_types[] = { FOOD_CLASS, AMULET_CLASS, 0 }; + +STATIC_OVL void +consume_offering(otmp) +register struct obj *otmp; +{ + if (Hallucination) + switch (rn2(3)) { + case 0: + Your("sacrifice sprouts wings and a propeller and roars away!"); + break; + case 1: + Your("sacrifice puffs up, swelling bigger and bigger, and pops!"); + break; + case 2: + Your("sacrifice collapses into a cloud of dancing particles and fades away!"); + break; + } + else if (Blind && u.ualign.type == A_LAWFUL) + Your("sacrifice disappears!"); + else Your("sacrifice is consumed in a %s!", + u.ualign.type == A_LAWFUL ? "flash of light" : "burst of flame"); + if (carried(otmp)) useup(otmp); + else useupf(otmp, 1L); + exercise(A_WIS, TRUE); +} + +int +dosacrifice() +{ + register struct obj *otmp; + int value = 0; + int pm; + aligntyp altaralign = a_align(u.ux,u.uy); + + if (!on_altar() || u.uswallow) { + You("are not standing on an altar."); + return 0; + } + + if (In_endgame(&u.uz)) { + if (!(otmp = getobj(sacrifice_types, "sacrifice"))) return 0; + } else { + if (!(otmp = floorfood("sacrifice", 1))) return 0; + } + /* + Was based on nutritional value and aging behavior (< 50 moves). + Sacrificing a food ration got you max luck instantly, making the + gods as easy to please as an angry dog! + + Now only accepts corpses, based on the game's evaluation of their + toughness. Human and pet sacrifice, as well as sacrificing unicorns + of your alignment, is strongly discouraged. + */ + +#define MAXVALUE 24 /* Highest corpse value (besides Wiz) */ + + if (otmp->otyp == CORPSE) { + register struct permonst *ptr = &mons[otmp->corpsenm]; + struct monst *mtmp; + extern const int monstr[]; + + /* KMH, conduct */ + u.uconduct.gnostic++; + + /* you're handling this corpse, even if it was killed upon the altar */ + feel_cockatrice(otmp, TRUE); + + if (otmp->corpsenm == PM_ACID_BLOB + || (monstermoves <= peek_at_iced_corpse_age(otmp) + 50)) { + value = monstr[otmp->corpsenm] + 1; + if (otmp->oeaten) + value = eaten_stat(value, otmp); + } + + if (your_race(ptr)) { + if (is_demon(youmonst.data)) { + You("find the idea very satisfying."); + exercise(A_WIS, TRUE); + } else if (u.ualign.type != A_CHAOTIC) { + pline("You'll regret this infamous offense!"); + exercise(A_WIS, FALSE); + } + + if (altaralign != A_CHAOTIC && altaralign != A_NONE) { + /* curse the lawful/neutral altar */ + pline_The("altar is stained with %s blood.", urace.adj); + if(!Is_astralevel(&u.uz)) + levl[u.ux][u.uy].altarmask = AM_CHAOTIC; + angry_priest(); + } else { + struct monst *dmon; + const char *demonless_msg; + + /* Human sacrifice on a chaotic or unaligned altar */ + /* is equivalent to demon summoning */ + if (altaralign == A_CHAOTIC && u.ualign.type != A_CHAOTIC) { + pline( + "The blood floods the altar, which vanishes in %s cloud!", + an(hcolor(NH_BLACK))); + levl[u.ux][u.uy].typ = ROOM; + levl[u.ux][u.uy].altarmask = 0; + newsym(u.ux, u.uy); + angry_priest(); + demonless_msg = "cloud dissipates"; + } else { + /* either you're chaotic or altar is Moloch's or both */ + pline_The("blood covers the altar!"); + change_luck(altaralign == A_NONE ? -2 : 2); + demonless_msg = "blood coagulates"; + } + if ((pm = dlord(altaralign)) != NON_PM && + (dmon = makemon(&mons[pm], u.ux, u.uy, NO_MM_FLAGS))) { + You("have summoned %s!", a_monnam(dmon)); + if (sgn(u.ualign.type) == sgn(dmon->data->maligntyp)) + dmon->mpeaceful = TRUE; + You("are terrified, and unable to move."); + nomul(-3); + } else pline_The("%s.", demonless_msg); + } + + if (u.ualign.type != A_CHAOTIC) { + adjalign(-5); + u.ugangr += 3; + (void) adjattrib(A_WIS, -1, TRUE); + if (!Inhell) angrygods(u.ualign.type); + change_luck(-5); + } else adjalign(5); + if (carried(otmp)) useup(otmp); + else useupf(otmp, 1L); + return(1); + } else if (otmp->oxlth && otmp->oattached == OATTACHED_MONST + && ((mtmp = get_mtraits(otmp, FALSE)) != (struct monst *)0) + && mtmp->mtame) { + /* mtmp is a temporary pointer to a tame monster's attributes, + * not a real monster */ + pline("So this is how you repay loyalty?"); + adjalign(-3); + value = -1; + HAggravate_monster |= FROMOUTSIDE; + } else if (is_undead(ptr)) { /* Not demons--no demon corpses */ + if (u.ualign.type != A_CHAOTIC) + value += 1; + } else if (is_unicorn(ptr)) { + int unicalign = sgn(ptr->maligntyp); + + /* If same as altar, always a very bad action. */ + if (unicalign == altaralign) { + pline("Such an action is an insult to %s!", + (unicalign == A_CHAOTIC) + ? "chaos" : unicalign ? "law" : "balance"); + (void) adjattrib(A_WIS, -1, TRUE); + value = -5; + } else if (u.ualign.type == altaralign) { + /* If different from altar, and altar is same as yours, */ + /* it's a very good action */ + if (u.ualign.record < ALIGNLIM) + You_feel("appropriately %s.", align_str(u.ualign.type)); + else You_feel("you are thoroughly on the right path."); + adjalign(5); + value += 3; + } else + /* If sacrificing unicorn of your alignment to altar not of */ + /* your alignment, your god gets angry and it's a conversion */ + if (unicalign == u.ualign.type) { + u.ualign.record = -1; + value = 1; + } else value += 3; + } + } /* corpse */ + + if (otmp->otyp == AMULET_OF_YENDOR) { + if (!Is_astralevel(&u.uz)) { + if (Hallucination) + You_feel("homesick."); + else + You_feel("an urge to return to the surface."); + return 1; + } else { + /* The final Test. Did you win? */ + if(uamul == otmp) Amulet_off(); + u.uevent.ascended = 1; + if(carried(otmp)) useup(otmp); /* well, it's gone now */ + else useupf(otmp, 1L); + You("offer the Amulet of Yendor to %s...", a_gname()); + if (u.ualign.type != altaralign) { + /* And the opposing team picks you up and + carries you off on their shoulders */ + adjalign(-99); + pline("%s accepts your gift, and gains dominion over %s...", + a_gname(), u_gname()); + pline("%s is enraged...", u_gname()); + pline("Fortunately, %s permits you to live...", a_gname()); + pline("A cloud of %s smoke surrounds you...", + hcolor((const char *)"orange")); + done(ESCAPED); + } else { /* super big win */ + adjalign(10); +pline("An invisible choir sings, and you are bathed in radiance..."); + godvoice(altaralign, "Congratulations, mortal!"); + display_nhwindow(WIN_MESSAGE, FALSE); +verbalize("In return for thy service, I grant thee the gift of Immortality!"); + You("ascend to the status of Demigod%s...", + flags.female ? "dess" : ""); + done(ASCENDED); + } + } + } /* real Amulet */ + + if (otmp->otyp == FAKE_AMULET_OF_YENDOR) { + if (flags.soundok) + You_hear("a nearby thunderclap."); + if (!otmp->known) { + You("realize you have made a %s.", + Hallucination ? "boo-boo" : "mistake"); + otmp->known = TRUE; + change_luck(-1); + return 1; + } else { + /* don't you dare try to fool the gods */ + change_luck(-3); + adjalign(-1); + u.ugangr += 3; + value = -3; + } + } /* fake Amulet */ + + if (value == 0) { + pline(nothing_happens); + return (1); + } + + if (altaralign != u.ualign.type && + (Is_astralevel(&u.uz) || Is_sanctum(&u.uz))) { + /* + * REAL BAD NEWS!!! High altars cannot be converted. Even an attempt + * gets the god who owns it truely pissed off. + */ + You_feel("the air around you grow charged..."); + pline("Suddenly, you realize that %s has noticed you...", a_gname()); + godvoice(altaralign, "So, mortal! You dare desecrate my High Temple!"); + /* Throw everything we have at the player */ + god_zaps_you(altaralign); + } else if (value < 0) { /* I don't think the gods are gonna like this... */ + gods_upset(altaralign); + } else { + int saved_anger = u.ugangr; + int saved_cnt = u.ublesscnt; + int saved_luck = u.uluck; + + /* Sacrificing at an altar of a different alignment */ + if (u.ualign.type != altaralign) { + /* Is this a conversion ? */ + /* An unaligned altar in Gehennom will always elicit rejection. */ + if (ugod_is_angry() || (altaralign == A_NONE && Inhell)) { + if(u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL] && + altaralign != A_NONE) { + You("have a strong feeling that %s is angry...", u_gname()); + consume_offering(otmp); + pline("%s accepts your allegiance.", a_gname()); + + /* The player wears a helm of opposite alignment? */ + if (uarmh && uarmh->otyp == HELM_OF_OPPOSITE_ALIGNMENT) + u.ualignbase[A_CURRENT] = altaralign; + else + u.ualign.type = u.ualignbase[A_CURRENT] = altaralign; + u.ublessed = 0; + flags.botl = 1; + + You("have a sudden sense of a new direction."); + /* Beware, Conversion is costly */ + change_luck(-3); + u.ublesscnt += 300; + adjalign((int)(u.ualignbase[A_ORIGINAL] * (ALIGNLIM / 2))); + } else { + u.ugangr += 3; + adjalign(-5); + pline("%s rejects your sacrifice!", a_gname()); + godvoice(altaralign, "Suffer, infidel!"); + change_luck(-5); + (void) adjattrib(A_WIS, -2, TRUE); + if (!Inhell) angrygods(u.ualign.type); + } + return(1); + } else { + consume_offering(otmp); + You("sense a conflict between %s and %s.", + u_gname(), a_gname()); + if (rn2(8 + u.ulevel) > 5) { + struct monst *pri; + You_feel("the power of %s increase.", u_gname()); + exercise(A_WIS, TRUE); + change_luck(1); + /* Yes, this is supposed to be &=, not |= */ + levl[u.ux][u.uy].altarmask &= AM_SHRINE; + /* the following accommodates stupid compilers */ + levl[u.ux][u.uy].altarmask = + levl[u.ux][u.uy].altarmask | (Align2amask(u.ualign.type)); + if (!Blind) + pline_The("altar glows %s.", + hcolor( + u.ualign.type == A_LAWFUL ? NH_WHITE : + u.ualign.type ? NH_BLACK : (const char *)"gray")); + + if (rnl(u.ulevel) > 6 && u.ualign.record > 0 && + rnd(u.ualign.record) > (3*ALIGNLIM)/4) + summon_minion(altaralign, TRUE); + /* anger priest; test handles bones files */ + if((pri = findpriest(temple_occupied(u.urooms))) && + !p_coaligned(pri)) + angry_priest(); + } else { + pline("Unluckily, you feel the power of %s decrease.", + u_gname()); + change_luck(-1); + exercise(A_WIS, FALSE); + if (rnl(u.ulevel) > 6 && u.ualign.record > 0 && + rnd(u.ualign.record) > (7*ALIGNLIM)/8) + summon_minion(altaralign, TRUE); + } + return(1); + } + } + + consume_offering(otmp); + /* OK, you get brownie points. */ + if(u.ugangr) { + u.ugangr -= + ((value * (u.ualign.type == A_CHAOTIC ? 2 : 3)) / MAXVALUE); + if(u.ugangr < 0) u.ugangr = 0; + if(u.ugangr != saved_anger) { + if (u.ugangr) { + pline("%s seems %s.", u_gname(), + Hallucination ? "groovy" : "slightly mollified"); + + if ((int)u.uluck < 0) change_luck(1); + } else { + pline("%s seems %s.", u_gname(), Hallucination ? + "cosmic (not a new fact)" : "mollified"); + + if ((int)u.uluck < 0) u.uluck = 0; + } + } else { /* not satisfied yet */ + if (Hallucination) + pline_The("gods seem tall."); + else You("have a feeling of inadequacy."); + } + } else if(ugod_is_angry()) { + if(value > MAXVALUE) value = MAXVALUE; + if(value > -u.ualign.record) value = -u.ualign.record; + adjalign(value); + You_feel("partially absolved."); + } else if (u.ublesscnt > 0) { + u.ublesscnt -= + ((value * (u.ualign.type == A_CHAOTIC ? 500 : 300)) / MAXVALUE); + if(u.ublesscnt < 0) u.ublesscnt = 0; + if(u.ublesscnt != saved_cnt) { + if (u.ublesscnt) { + if (Hallucination) + You("realize that the gods are not like you and I."); + else + You("have a hopeful feeling."); + if ((int)u.uluck < 0) change_luck(1); + } else { + if (Hallucination) + pline("Overall, there is a smell of fried onions."); + else + You("have a feeling of reconciliation."); + if ((int)u.uluck < 0) u.uluck = 0; + } + } + } else { + int nartifacts = nartifact_exist(); + + /* you were already in pretty good standing */ + /* The player can gain an artifact */ + /* The chance goes down as the number of artifacts goes up */ + if (u.ulevel > 2 && u.uluck >= 0 && + !rn2(10 + (2 * u.ugifts * nartifacts))) { + otmp = mk_artifact((struct obj *)0, a_align(u.ux,u.uy)); + if (otmp) { + if (otmp->spe < 0) otmp->spe = 0; + if (otmp->cursed) uncurse(otmp); + otmp->oerodeproof = TRUE; + dropy(otmp); + at_your_feet("An object"); + godvoice(u.ualign.type, "Use my gift wisely!"); + u.ugifts++; + u.ublesscnt = rnz(300 + (50 * nartifacts)); + exercise(A_WIS, TRUE); + /* make sure we can use this weapon */ + unrestrict_weapon_skill(weapon_type(otmp)); + discover_artifact(otmp->oartifact); + return(1); + } + } + change_luck((value * LUCKMAX) / (MAXVALUE * 2)); + if ((int)u.uluck < 0) u.uluck = 0; + if (u.uluck != saved_luck) { + if (Blind) + You("think %s brushed your %s.",something, body_part(FOOT)); + else You(Hallucination ? + "see crabgrass at your %s. A funny thing in a dungeon." : + "glimpse a four-leaf clover at your %s.", + makeplural(body_part(FOOT))); + } + } + } + return(1); +} + + +/* determine prayer results in advance; also used for enlightenment */ +boolean +can_pray(praying) +boolean praying; /* false means no messages should be given */ +{ + int alignment; + + p_aligntyp = on_altar() ? a_align(u.ux,u.uy) : u.ualign.type; + p_trouble = in_trouble(); + + if (is_demon(youmonst.data) && (p_aligntyp != A_CHAOTIC)) { + if (praying) + pline_The("very idea of praying to a %s god is repugnant to you.", + p_aligntyp ? "lawful" : "neutral"); + return FALSE; + } + + if (praying) + You("begin praying to %s.", align_gname(p_aligntyp)); + + if (u.ualign.type && u.ualign.type == -p_aligntyp) + alignment = -u.ualign.record; /* Opposite alignment altar */ + else if (u.ualign.type != p_aligntyp) + alignment = u.ualign.record / 2; /* Different alignment altar */ + else alignment = u.ualign.record; + + if ((p_trouble > 0) ? (u.ublesscnt > 200) : /* big trouble */ + (p_trouble < 0) ? (u.ublesscnt > 100) : /* minor difficulties */ + (u.ublesscnt > 0)) /* not in trouble */ + p_type = 0; /* too soon... */ + else if ((int)Luck < 0 || u.ugangr || alignment < 0) + p_type = 1; /* too naughty... */ + else /* alignment >= 0 */ { + if(on_altar() && u.ualign.type != p_aligntyp) + p_type = 2; + else + p_type = 3; + } + + if (is_undead(youmonst.data) && !Inhell && + (p_aligntyp == A_LAWFUL || (p_aligntyp == A_NEUTRAL && !rn2(10)))) + p_type = -1; + /* Note: when !praying, the random factor for neutrals makes the + return value a non-deterministic approximation for enlightenment. + This case should be uncommon enough to live with... */ + + return !praying ? (boolean)(p_type == 3 && !Inhell) : TRUE; +} + +int +dopray() +{ + /* Confirm accidental slips of Alt-P */ + if (flags.prayconfirm) + if (yn("Are you sure you want to pray?") == 'n') + return 0; + + u.uconduct.gnostic++; + /* Praying implies that the hero is conscious and since we have + no deafness attribute this implies that all verbalized messages + can be heard. So, in case the player has used the 'O' command + to toggle this accessible flag off, force it to be on. */ + flags.soundok = 1; + + /* set up p_type and p_alignment */ + if (!can_pray(TRUE)) return 0; + +#ifdef WIZARD + if (wizard && p_type >= 0) { + if (yn("Force the gods to be pleased?") == 'y') { + u.ublesscnt = 0; + if (u.uluck < 0) u.uluck = 0; + if (u.ualign.record <= 0) u.ualign.record = 1; + u.ugangr = 0; + if(p_type < 2) p_type = 3; + } + } +#endif + nomul(-3); + nomovemsg = "You finish your prayer."; + afternmv = prayer_done; + + if(p_type == 3 && !Inhell) { + /* if you've been true to your god you can't die while you pray */ + if (!Blind) + You("are surrounded by a shimmering light."); + u.uinvulnerable = TRUE; + } + + return(1); +} + +STATIC_PTR int +prayer_done() /* M. Stephenson (1.0.3b) */ +{ + aligntyp alignment = p_aligntyp; + + u.uinvulnerable = FALSE; + if(p_type == -1) { + godvoice(alignment, + alignment == A_LAWFUL ? + "Vile creature, thou durst call upon me?" : + "Walk no more, perversion of nature!"); + You_feel("like you are falling apart."); + /* KMH -- Gods have mastery over unchanging */ + rehumanize(); + losehp(rnd(20), "residual undead turning effect", KILLED_BY_AN); + exercise(A_CON, FALSE); + return(1); + } + if (Inhell) { + pline("Since you are in Gehennom, %s won't help you.", + align_gname(alignment)); + /* haltingly aligned is least likely to anger */ + if (u.ualign.record <= 0 || rnl(u.ualign.record)) + angrygods(u.ualign.type); + return(0); + } + + if (p_type == 0) { + if(on_altar() && u.ualign.type != alignment) + (void) water_prayer(FALSE); + u.ublesscnt += rnz(250); + change_luck(-3); + gods_upset(u.ualign.type); + } else if(p_type == 1) { + if(on_altar() && u.ualign.type != alignment) + (void) water_prayer(FALSE); + angrygods(u.ualign.type); /* naughty */ + } else if(p_type == 2) { + if(water_prayer(FALSE)) { + /* attempted water prayer on a non-coaligned altar */ + u.ublesscnt += rnz(250); + change_luck(-3); + gods_upset(u.ualign.type); + } else pleased(alignment); + } else { + /* coaligned */ + if(on_altar()) + (void) water_prayer(TRUE); + pleased(alignment); /* nice */ + } + return(1); +} + +int +doturn() +{ /* Knights & Priest(esse)s only please */ + + struct monst *mtmp, *mtmp2; + int once, range, xlev; + + if (!Role_if(PM_PRIEST) && !Role_if(PM_KNIGHT)) { + /* Try to use turn undead spell. */ + if (objects[SPE_TURN_UNDEAD].oc_name_known) { + register int sp_no; + for (sp_no = 0; sp_no < MAXSPELL && + spl_book[sp_no].sp_id != NO_SPELL && + spl_book[sp_no].sp_id != SPE_TURN_UNDEAD; sp_no++); + + if (sp_no < MAXSPELL && + spl_book[sp_no].sp_id == SPE_TURN_UNDEAD) + return spelleffects(sp_no, TRUE); + } + + You("don't know how to turn undead!"); + return(0); + } + u.uconduct.gnostic++; + + if ((u.ualign.type != A_CHAOTIC && + (is_demon(youmonst.data) || is_undead(youmonst.data))) || + u.ugangr > 6 /* "Die, mortal!" */) { + + pline("For some reason, %s seems to ignore you.", u_gname()); + aggravate(); + exercise(A_WIS, FALSE); + return(0); + } + + if (Inhell) { + pline("Since you are in Gehennom, %s won't help you.", u_gname()); + aggravate(); + return(0); + } + pline("Calling upon %s, you chant an arcane formula.", u_gname()); + exercise(A_WIS, TRUE); + + /* note: does not perform unturn_dead() on victims' inventories */ + range = BOLT_LIM + (u.ulevel / 5); /* 5 to 11 */ + range *= range; + once = 0; + for(mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + + if (DEADMONSTER(mtmp)) continue; + if (!cansee(mtmp->mx,mtmp->my) || + distu(mtmp->mx,mtmp->my) > range) continue; + + if (!mtmp->mpeaceful && (is_undead(mtmp->data) || + (is_demon(mtmp->data) && (u.ulevel > (MAXULEV/2))))) { + + mtmp->msleeping = 0; + if (Confusion) { + if (!once++) + pline("Unfortunately, your voice falters."); + mtmp->mflee = 0; + mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + } else if (!resist(mtmp, '\0', 0, TELL)) { + xlev = 6; + switch (mtmp->data->mlet) { + /* this is intentional, lichs are tougher + than zombies. */ + case S_LICH: xlev += 2; /*FALLTHRU*/ + case S_GHOST: xlev += 2; /*FALLTHRU*/ + case S_VAMPIRE: xlev += 2; /*FALLTHRU*/ + case S_WRAITH: xlev += 2; /*FALLTHRU*/ + case S_MUMMY: xlev += 2; /*FALLTHRU*/ + case S_ZOMBIE: + if (u.ulevel >= xlev && + !resist(mtmp, '\0', 0, NOTELL)) { + if (u.ualign.type == A_CHAOTIC) { + mtmp->mpeaceful = 1; + set_malign(mtmp); + } else { /* damn them */ + killed(mtmp); + } + break; + } /* else flee */ + /*FALLTHRU*/ + default: + monflee(mtmp, 0, FALSE, TRUE); + break; + } + } + } + } + nomul(-5); + return(1); +} + +const char * +a_gname() +{ + return(a_gname_at(u.ux, u.uy)); +} + +const char * +a_gname_at(x,y) /* returns the name of an altar's deity */ +xchar x, y; +{ + if(!IS_ALTAR(levl[x][y].typ)) return((char *)0); + + return align_gname(a_align(x,y)); +} + +const char * +u_gname() /* returns the name of the player's deity */ +{ + return align_gname(u.ualign.type); +} + +const char * +align_gname(alignment) +aligntyp alignment; +{ + const char *gnam; + + switch (alignment) { + case A_NONE: gnam = Moloch; break; + case A_LAWFUL: gnam = urole.lgod; break; + case A_NEUTRAL: gnam = urole.ngod; break; + case A_CHAOTIC: gnam = urole.cgod; break; + default: impossible("unknown alignment."); + gnam = "someone"; break; + } + if (*gnam == '_') ++gnam; + return gnam; +} + +/* hallucination handling for priest/minion names: select a random god + iff character is hallucinating */ +const char * +halu_gname(alignment) +aligntyp alignment; +{ + const char *gnam; + int which; + + if (!Hallucination) return align_gname(alignment); + + which = randrole(); + switch (rn2(3)) { + case 0: gnam = roles[which].lgod; break; + case 1: gnam = roles[which].ngod; break; + case 2: gnam = roles[which].cgod; break; + default: gnam = 0; break; /* lint suppression */ + } + if (!gnam) gnam = Moloch; + if (*gnam == '_') ++gnam; + return gnam; +} + +/* deity's title */ +const char * +align_gtitle(alignment) +aligntyp alignment; +{ + const char *gnam, *result = "god"; + + switch (alignment) { + case A_LAWFUL: gnam = urole.lgod; break; + case A_NEUTRAL: gnam = urole.ngod; break; + case A_CHAOTIC: gnam = urole.cgod; break; + default: gnam = 0; break; + } + if (gnam && *gnam == '_') result = "goddess"; + return result; +} + +void +altar_wrath(x, y) +register int x, y; +{ + aligntyp altaralign = a_align(x,y); + + if(!strcmp(align_gname(altaralign), u_gname())) { + godvoice(altaralign, "How darest thou desecrate my altar!"); + (void) adjattrib(A_WIS, -1, FALSE); + } else { + pline("A voice (could it be %s?) whispers:", + align_gname(altaralign)); + verbalize("Thou shalt pay, infidel!"); + change_luck(-1); + } +} + +/* assumes isok() at one space away, but not necessarily at two */ +STATIC_OVL boolean +blocked_boulder(dx,dy) +int dx,dy; +{ + register struct obj *otmp; + long count = 0L; + + for(otmp = level.objects[u.ux+dx][u.uy+dy]; otmp; otmp = otmp->nexthere) { + if(otmp->otyp == BOULDER) + count += otmp->quan; + } + + switch(count) { + case 0: return FALSE; /* no boulders--not blocked */ + case 1: break; /* possibly blocked depending on if it's pushable */ + default: return TRUE; /* >1 boulder--blocked after they push the top + one; don't force them to push it first to find out */ + } + + if (!isok(u.ux+2*dx, u.uy+2*dy)) + return TRUE; + if (IS_ROCK(levl[u.ux+2*dx][u.uy+2*dy].typ)) + return TRUE; + if (sobj_at(BOULDER, u.ux+2*dx, u.uy+2*dy)) + return TRUE; + + return FALSE; +} + +/*pray.c*/ diff --git a/src/priest.c b/src/priest.c new file mode 100644 index 0000000..5681930 --- /dev/null +++ b/src/priest.c @@ -0,0 +1,721 @@ +/* SCCS Id: @(#)priest.c 3.4 2002/11/06 */ +/* Copyright (c) Izchak Miller, Steve Linhart, 1989. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "mfndpos.h" +#include "eshk.h" +#include "epri.h" +#include "emin.h" + +/* this matches the categorizations shown by enlightenment */ +#define ALGN_SINNED (-4) /* worse than strayed */ + +#ifdef OVLB + +STATIC_DCL boolean FDECL(histemple_at,(struct monst *,XCHAR_P,XCHAR_P)); +STATIC_DCL boolean FDECL(has_shrine,(struct monst *)); + +/* + * Move for priests and shopkeepers. Called from shk_move() and pri_move(). + * Valid returns are 1: moved 0: didn't -1: let m_move do it -2: died. + */ +int +move_special(mtmp,in_his_shop,appr,uondoor,avoid,omx,omy,gx,gy) +register struct monst *mtmp; +boolean in_his_shop; +schar appr; +boolean uondoor,avoid; +register xchar omx,omy,gx,gy; +{ + register xchar nx,ny,nix,niy; + register schar i; + schar chcnt,cnt; + coord poss[9]; + long info[9]; + long allowflags; + struct obj *ib = (struct obj *)0; + + if(omx == gx && omy == gy) + return(0); + if(mtmp->mconf) { + avoid = FALSE; + appr = 0; + } + + nix = omx; + niy = omy; + if (mtmp->isshk) allowflags = ALLOW_SSM; + else allowflags = ALLOW_SSM | ALLOW_SANCT; + if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK|ALLOW_WALL); + if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK; + if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG; + if (!nohands(mtmp->data) && !verysmall(mtmp->data)) { + allowflags |= OPENDOOR; + if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR; + } + if (is_giant(mtmp->data)) allowflags |= BUSTDOOR; + cnt = mfndpos(mtmp, poss, info, allowflags); + + if(mtmp->isshk && avoid && uondoor) { /* perhaps we cannot avoid him */ + for(i=0; iispriest && + levl[nx][ny].typ == ALTAR) || + (mtmp->isshk && + (!in_his_shop || ESHK(mtmp)->following))) { + if(avoid && (info[i] & NOTONL)) + continue; + if((!appr && !rn2(++chcnt)) || + (appr && GDIST(nx,ny) < GDIST(nix,niy))) { + nix = nx; + niy = ny; + } + } + } + if(mtmp->ispriest && avoid && + nix == omx && niy == omy && onlineu(omx,omy)) { + /* might as well move closer as long it's going to stay + * lined up */ + avoid = FALSE; + goto pick_move; + } + + if(nix != omx || niy != omy) { + remove_monster(omx, omy); + place_monster(mtmp, nix, niy); + newsym(nix,niy); + if (mtmp->isshk && !in_his_shop && inhishop(mtmp)) + check_special_room(FALSE); + if(ib) { + if (cansee(mtmp->mx,mtmp->my)) + pline("%s picks up %s.", Monnam(mtmp), + distant_name(ib,doname)); + obj_extract_self(ib); + (void) mpickobj(mtmp, ib); + } + return(1); + } + return(0); +} + +#endif /* OVLB */ + +#ifdef OVL0 + +char +temple_occupied(array) +register char *array; +{ + register char *ptr; + + for (ptr = array; *ptr; ptr++) + if (rooms[*ptr - ROOMOFFSET].rtype == TEMPLE) + return(*ptr); + return('\0'); +} + +#endif /* OVL0 */ +#ifdef OVLB + +STATIC_OVL boolean +histemple_at(priest, x, y) +register struct monst *priest; +register xchar x, y; +{ + return((boolean)((EPRI(priest)->shroom == *in_rooms(x, y, TEMPLE)) && + on_level(&(EPRI(priest)->shrlevel), &u.uz))); +} + +/* + * pri_move: return 1: moved 0: didn't -1: let m_move do it -2: died + */ +int +pri_move(priest) +register struct monst *priest; +{ + register xchar gx,gy,omx,omy; + schar temple; + boolean avoid = TRUE; + + omx = priest->mx; + omy = priest->my; + + if(!histemple_at(priest, omx, omy)) return(-1); + + temple = EPRI(priest)->shroom; + + gx = EPRI(priest)->shrpos.x; + gy = EPRI(priest)->shrpos.y; + + gx += rn1(3,-1); /* mill around the altar */ + gy += rn1(3,-1); + + if(!priest->mpeaceful || + (Conflict && !resist(priest, RING_CLASS, 0, 0))) { + if(monnear(priest, u.ux, u.uy)) { + if(Displaced) + Your("displaced image doesn't fool %s!", + mon_nam(priest)); + (void) mattacku(priest); + return(0); + } else if(index(u.urooms, temple)) { + /* chase player if inside temple & can see him */ + if(priest->mcansee && m_canseeu(priest)) { + gx = u.ux; + gy = u.uy; + } + avoid = FALSE; + } + } else if(Invis) avoid = FALSE; + + return(move_special(priest,FALSE,TRUE,FALSE,avoid,omx,omy,gx,gy)); +} + +/* exclusively for mktemple() */ +void +priestini(lvl, sroom, sx, sy, sanctum) +d_level *lvl; +struct mkroom *sroom; +int sx, sy; +boolean sanctum; /* is it the seat of the high priest? */ +{ + struct monst *priest; + struct obj *otmp; + int cnt; + + if(MON_AT(sx+1, sy)) + (void) rloc(m_at(sx+1, sy), FALSE); /* insurance */ + + priest = makemon(&mons[sanctum ? PM_HIGH_PRIEST : PM_ALIGNED_PRIEST], + sx + 1, sy, NO_MM_FLAGS); + if (priest) { + EPRI(priest)->shroom = (sroom - rooms) + ROOMOFFSET; + EPRI(priest)->shralign = Amask2align(levl[sx][sy].altarmask); + EPRI(priest)->shrpos.x = sx; + EPRI(priest)->shrpos.y = sy; + assign_level(&(EPRI(priest)->shrlevel), lvl); + priest->mtrapseen = ~0; /* traps are known */ + priest->mpeaceful = 1; + priest->ispriest = 1; + priest->msleeping = 0; + set_malign(priest); /* mpeaceful may have changed */ + + /* now his/her goodies... */ + if(sanctum && EPRI(priest)->shralign == A_NONE && + on_level(&sanctum_level, &u.uz)) { + (void) mongets(priest, AMULET_OF_YENDOR); + } + /* 2 to 4 spellbooks */ + for (cnt = rn1(3,2); cnt > 0; --cnt) { + (void) mpickobj(priest, mkobj(SPBOOK_CLASS, FALSE)); + } + /* robe [via makemon()] */ + if (rn2(2) && (otmp = which_armor(priest, W_ARMC)) != 0) { + if (p_coaligned(priest)) + uncurse(otmp); + else + curse(otmp); + } + } +} + +/* + * Specially aligned monsters are named specially. + * - aligned priests with ispriest and high priests have shrines + * they retain ispriest and epri when polymorphed + * - aligned priests without ispriest and Angels are roamers + * they retain isminion and access epri as emin when polymorphed + * (coaligned Angels are also created as minions, but they + * use the same naming convention) + * - minions do not have ispriest but have isminion and emin + * - caller needs to inhibit Hallucination if it wants to force + * the true name even when under that influence + */ +char * +priestname(mon, pname) +register struct monst *mon; +char *pname; /* caller-supplied output buffer */ +{ + const char *what = Hallucination ? rndmonnam() : mon->data->mname; + + Strcpy(pname, "the "); + if (mon->minvis) Strcat(pname, "invisible "); + if (mon->ispriest || mon->data == &mons[PM_ALIGNED_PRIEST] || + mon->data == &mons[PM_ANGEL]) { + /* use epri */ + if (mon->mtame && mon->data == &mons[PM_ANGEL]) + Strcat(pname, "guardian "); + if (mon->data != &mons[PM_ALIGNED_PRIEST] && + mon->data != &mons[PM_HIGH_PRIEST]) { + Strcat(pname, what); + Strcat(pname, " "); + } + if (mon->data != &mons[PM_ANGEL]) { + if (!mon->ispriest && EPRI(mon)->renegade) + Strcat(pname, "renegade "); + if (mon->data == &mons[PM_HIGH_PRIEST]) + Strcat(pname, "high "); + if (Hallucination) + Strcat(pname, "poohbah "); + else if (mon->female) + Strcat(pname, "priestess "); + else + Strcat(pname, "priest "); + } + Strcat(pname, "of "); + Strcat(pname, halu_gname((int)EPRI(mon)->shralign)); + return(pname); + } + /* use emin instead of epri */ + Strcat(pname, what); + Strcat(pname, " of "); + Strcat(pname, halu_gname(EMIN(mon)->min_align)); + return(pname); +} + +boolean +p_coaligned(priest) +struct monst *priest; +{ + return((boolean)(u.ualign.type == ((int)EPRI(priest)->shralign))); +} + +STATIC_OVL boolean +has_shrine(pri) +struct monst *pri; +{ + struct rm *lev; + + if(!pri) + return(FALSE); + lev = &levl[EPRI(pri)->shrpos.x][EPRI(pri)->shrpos.y]; + if (!IS_ALTAR(lev->typ) || !(lev->altarmask & AM_SHRINE)) + return(FALSE); + return((boolean)(EPRI(pri)->shralign == Amask2align(lev->altarmask & ~AM_SHRINE))); +} + +struct monst * +findpriest(roomno) +char roomno; +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if(mtmp->ispriest && (EPRI(mtmp)->shroom == roomno) && + histemple_at(mtmp,mtmp->mx,mtmp->my)) + return(mtmp); + } + return (struct monst *)0; +} + +/* called from check_special_room() when the player enters the temple room */ +void +intemple(roomno) +register int roomno; +{ + register struct monst *priest = findpriest((char)roomno); + boolean tended = (priest != (struct monst *)0); + boolean shrined, sanctum, can_speak; + const char *msg1, *msg2; + char buf[BUFSZ]; + + if(!temple_occupied(u.urooms0)) { + if(tended) { + shrined = has_shrine(priest); + sanctum = (priest->data == &mons[PM_HIGH_PRIEST] && + (Is_sanctum(&u.uz) || In_endgame(&u.uz))); + can_speak = (priest->mcanmove && !priest->msleeping && + flags.soundok); + if (can_speak) { + unsigned save_priest = priest->ispriest; + /* don't reveal the altar's owner upon temple entry in + the endgame; for the Sanctum, the next message names + Moloch so suppress the "of Moloch" for him here too */ + if (sanctum && !Hallucination) priest->ispriest = 0; + pline("%s intones:", + canseemon(priest) ? Monnam(priest) : "A nearby voice"); + priest->ispriest = save_priest; + } + msg2 = 0; + if(sanctum && Is_sanctum(&u.uz)) { + if(priest->mpeaceful) { + msg1 = "Infidel, you have entered Moloch's Sanctum!"; + msg2 = "Be gone!"; + priest->mpeaceful = 0; + set_malign(priest); + } else + msg1 = "You desecrate this place by your presence!"; + } else { + Sprintf(buf, "Pilgrim, you enter a %s place!", + !shrined ? "desecrated" : "sacred"); + msg1 = buf; + } + if (can_speak) { + verbalize(msg1); + if (msg2) verbalize(msg2); + } + if(!sanctum) { + /* !tended -> !shrined */ + if (!shrined || !p_coaligned(priest) || + u.ualign.record <= ALGN_SINNED) + You("have a%s forbidding feeling...", + (!shrined) ? "" : " strange"); + else You("experience a strange sense of peace."); + } + } else { + switch(rn2(3)) { + case 0: You("have an eerie feeling..."); break; + case 1: You_feel("like you are being watched."); break; + default: pline("A shiver runs down your %s.", + body_part(SPINE)); break; + } + if(!rn2(5)) { + struct monst *mtmp; + + if(!(mtmp = makemon(&mons[PM_GHOST],u.ux,u.uy,NO_MM_FLAGS))) + return; + if (!Blind || sensemon(mtmp)) + pline("An enormous ghost appears next to you!"); + else You("sense a presence close by!"); + mtmp->mpeaceful = 0; + set_malign(mtmp); + if(flags.verbose) + You("are frightened to death, and unable to move."); + nomul(-3); + nomovemsg = "You regain your composure."; + } + } + } +} + +void +priest_talk(priest) +register struct monst *priest; +{ + boolean coaligned = p_coaligned(priest); + boolean strayed = (u.ualign.record < 0); + + /* KMH, conduct */ + u.uconduct.gnostic++; + + if(priest->mflee || (!priest->ispriest && coaligned && strayed)) { + pline("%s doesn't want anything to do with you!", + Monnam(priest)); + priest->mpeaceful = 0; + return; + } + + /* priests don't chat unless peaceful and in their own temple */ + if(!histemple_at(priest,priest->mx,priest->my) || + !priest->mpeaceful || !priest->mcanmove || priest->msleeping) { + static const char *cranky_msg[3] = { + "Thou wouldst have words, eh? I'll give thee a word or two!", + "Talk? Here is what I have to say!", + "Pilgrim, I would speak no longer with thee." + }; + + if(!priest->mcanmove || priest->msleeping) { + pline("%s breaks out of %s reverie!", + Monnam(priest), mhis(priest)); + priest->mfrozen = priest->msleeping = 0; + priest->mcanmove = 1; + } + priest->mpeaceful = 0; + verbalize(cranky_msg[rn2(3)]); + return; + } + + /* you desecrated the temple and now you want to chat? */ + if(priest->mpeaceful && *in_rooms(priest->mx, priest->my, TEMPLE) && + !has_shrine(priest)) { + verbalize("Begone! Thou desecratest this holy place with thy presence."); + priest->mpeaceful = 0; + return; + } +#ifndef GOLDOBJ + if(!u.ugold) { + if(coaligned && !strayed) { + if (priest->mgold > 0L) { + /* Note: two bits is actually 25 cents. Hmm. */ + pline("%s gives you %s for an ale.", Monnam(priest), + (priest->mgold == 1L) ? "one bit" : "two bits"); + if (priest->mgold > 1L) + u.ugold = 2L; + else + u.ugold = 1L; + priest->mgold -= u.ugold; + flags.botl = 1; +#else + if(!money_cnt(invent)) { + if(coaligned && !strayed) { + long pmoney = money_cnt(priest->minvent); + if (pmoney > 0L) { + /* Note: two bits is actually 25 cents. Hmm. */ + pline("%s gives you %s for an ale.", Monnam(priest), + (pmoney == 1L) ? "one bit" : "two bits"); + money2u(priest, pmoney > 1L ? 2 : 1); +#endif + } else + pline("%s preaches the virtues of poverty.", Monnam(priest)); + exercise(A_WIS, TRUE); + } else + pline("%s is not interested.", Monnam(priest)); + return; + } else { + long offer; + + pline("%s asks you for a contribution for the temple.", + Monnam(priest)); + if((offer = bribe(priest)) == 0) { + verbalize("Thou shalt regret thine action!"); + if(coaligned) adjalign(-1); + } else if(offer < (u.ulevel * 200)) { +#ifndef GOLDOBJ + if(u.ugold > (offer * 2L)) verbalize("Cheapskate."); +#else + if(money_cnt(invent) > (offer * 2L)) verbalize("Cheapskate."); +#endif + else { + verbalize("I thank thee for thy contribution."); + /* give player some token */ + exercise(A_WIS, TRUE); + } + } else if(offer < (u.ulevel * 400)) { + verbalize("Thou art indeed a pious individual."); +#ifndef GOLDOBJ + if(u.ugold < (offer * 2L)) { +#else + if(money_cnt(invent) < (offer * 2L)) { +#endif + if (coaligned && u.ualign.record <= ALGN_SINNED) + adjalign(1); + verbalize("I bestow upon thee a blessing."); + incr_itimeout(&HClairvoyant, rn1(500,500)); + } + } else if(offer < (u.ulevel * 600) && + u.ublessed < 20 && + (u.ublessed < 9 || !rn2(u.ublessed))) { + verbalize("Thy devotion has been rewarded."); + if (!(HProtection & INTRINSIC)) { + HProtection |= FROMOUTSIDE; + if (!u.ublessed) u.ublessed = rn1(3, 2); + } else u.ublessed++; + } else { + verbalize("Thy selfless generosity is deeply appreciated."); +#ifndef GOLDOBJ + if(u.ugold < (offer * 2L) && coaligned) { +#else + if(money_cnt(invent) < (offer * 2L) && coaligned) { +#endif + if(strayed && (moves - u.ucleansed) > 5000L) { + u.ualign.record = 0; /* cleanse thee */ + u.ucleansed = moves; + } else { + adjalign(2); + } + } + } + } +} + +struct monst * +mk_roamer(ptr, alignment, x, y, peaceful) +register struct permonst *ptr; +aligntyp alignment; +xchar x, y; +boolean peaceful; +{ + register struct monst *roamer; + register boolean coaligned = (u.ualign.type == alignment); + + if (ptr != &mons[PM_ALIGNED_PRIEST] && ptr != &mons[PM_ANGEL]) + return((struct monst *)0); + + if (MON_AT(x, y)) (void) rloc(m_at(x, y), FALSE); /* insurance */ + + if (!(roamer = makemon(ptr, x, y, NO_MM_FLAGS))) + return((struct monst *)0); + + EPRI(roamer)->shralign = alignment; + if (coaligned && !peaceful) + EPRI(roamer)->renegade = TRUE; + /* roamer->ispriest == FALSE naturally */ + roamer->isminion = TRUE; /* borrowing this bit */ + roamer->mtrapseen = ~0; /* traps are known */ + roamer->mpeaceful = peaceful; + roamer->msleeping = 0; + set_malign(roamer); /* peaceful may have changed */ + + /* MORE TO COME */ + return(roamer); +} + +void +reset_hostility(roamer) +register struct monst *roamer; +{ + if(!(roamer->isminion && (roamer->data == &mons[PM_ALIGNED_PRIEST] || + roamer->data == &mons[PM_ANGEL]))) + return; + + if(EPRI(roamer)->shralign != u.ualign.type) { + roamer->mpeaceful = roamer->mtame = 0; + set_malign(roamer); + } + newsym(roamer->mx, roamer->my); +} + +boolean +in_your_sanctuary(mon, x, y) +struct monst *mon; /* if non-null, overrides */ +xchar x, y; +{ + register char roomno; + register struct monst *priest; + + if (mon) { + if (is_minion(mon->data) || is_rider(mon->data)) return FALSE; + x = mon->mx, y = mon->my; + } + if (u.ualign.record <= ALGN_SINNED) /* sinned or worse */ + return FALSE; + if ((roomno = temple_occupied(u.urooms)) == 0 || + roomno != *in_rooms(x, y, TEMPLE)) + return FALSE; + if ((priest = findpriest(roomno)) == 0) + return FALSE; + return (boolean)(has_shrine(priest) && + p_coaligned(priest) && + priest->mpeaceful); +} + +void +ghod_hitsu(priest) /* when attacking "priest" in his temple */ +struct monst *priest; +{ + int x, y, ax, ay, roomno = (int)temple_occupied(u.urooms); + struct mkroom *troom; + + if (!roomno || !has_shrine(priest)) + return; + + ax = x = EPRI(priest)->shrpos.x; + ay = y = EPRI(priest)->shrpos.y; + troom = &rooms[roomno - ROOMOFFSET]; + + if((u.ux == x && u.uy == y) || !linedup(u.ux, u.uy, x, y)) { + if(IS_DOOR(levl[u.ux][u.uy].typ)) { + + if(u.ux == troom->lx - 1) { + x = troom->hx; + y = u.uy; + } else if(u.ux == troom->hx + 1) { + x = troom->lx; + y = u.uy; + } else if(u.uy == troom->ly - 1) { + x = u.ux; + y = troom->hy; + } else if(u.uy == troom->hy + 1) { + x = u.ux; + y = troom->ly; + } + } else { + switch(rn2(4)) { + case 0: x = u.ux; y = troom->ly; break; + case 1: x = u.ux; y = troom->hy; break; + case 2: x = troom->lx; y = u.uy; break; + default: x = troom->hx; y = u.uy; break; + } + } + if(!linedup(u.ux, u.uy, x, y)) return; + } + + switch(rn2(3)) { + case 0: + pline("%s roars in anger: \"Thou shalt suffer!\"", + a_gname_at(ax, ay)); + break; + case 1: + pline("%s voice booms: \"How darest thou harm my servant!\"", + s_suffix(a_gname_at(ax, ay))); + break; + default: + pline("%s roars: \"Thou dost profane my shrine!\"", + a_gname_at(ax, ay)); + break; + } + + buzz(-10-(AD_ELEC-1), 6, x, y, sgn(tbx), sgn(tby)); /* bolt of lightning */ + exercise(A_WIS, FALSE); +} + +void +angry_priest() +{ + register struct monst *priest; + struct rm *lev; + + if ((priest = findpriest(temple_occupied(u.urooms))) != 0) { + wakeup(priest); + /* + * If the altar has been destroyed or converted, let the + * priest run loose. + * (When it's just a conversion and there happens to be + * a fresh corpse nearby, the priest ought to have an + * opportunity to try converting it back; maybe someday...) + */ + lev = &levl[EPRI(priest)->shrpos.x][EPRI(priest)->shrpos.y]; + if (!IS_ALTAR(lev->typ) || + ((aligntyp)Amask2align(lev->altarmask & AM_MASK) != + EPRI(priest)->shralign)) { + priest->ispriest = 0; /* now a roamer */ + priest->isminion = 1; /* but still aligned */ + /* this overloads the `shroom' field, which is now clobbered */ + EPRI(priest)->renegade = 0; + } + } +} + +/* + * When saving bones, find priests that aren't on their shrine level, + * and remove them. This avoids big problems when restoring bones. + */ +void +clearpriests() +{ + register struct monst *mtmp, *mtmp2; + + for(mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (!DEADMONSTER(mtmp) && mtmp->ispriest && !on_level(&(EPRI(mtmp)->shrlevel), &u.uz)) + mongone(mtmp); + } +} + +/* munge priest-specific structure when restoring -dlc */ +void +restpriest(mtmp, ghostly) +register struct monst *mtmp; +boolean ghostly; +{ + if(u.uz.dlevel) { + if (ghostly) + assign_level(&(EPRI(mtmp)->shrlevel), &u.uz); + } +} + +#endif /* OVLB */ + +/*priest.c*/ diff --git a/src/quest.c b/src/quest.c new file mode 100644 index 0000000..13597ac --- /dev/null +++ b/src/quest.c @@ -0,0 +1,387 @@ +/* SCCS Id: @(#)quest.c 3.4 2000/05/05 */ +/* Copyright 1991, M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* quest dungeon branch routines. */ + +#include "quest.h" +#include "qtext.h" + +#define Not_firsttime (on_level(&u.uz0, &u.uz)) +#define Qstat(x) (quest_status.x) + +STATIC_DCL void NDECL(on_start); +STATIC_DCL void NDECL(on_locate); +STATIC_DCL void NDECL(on_goal); +STATIC_DCL boolean NDECL(not_capable); +STATIC_DCL int FDECL(is_pure, (BOOLEAN_P)); +STATIC_DCL void FDECL(expulsion, (BOOLEAN_P)); +STATIC_DCL void NDECL(chat_with_leader); +STATIC_DCL void NDECL(chat_with_nemesis); +STATIC_DCL void NDECL(chat_with_guardian); +STATIC_DCL void FDECL(prisoner_speaks, (struct monst *)); + + +STATIC_OVL void +on_start() +{ + if(!Qstat(first_start)) { + qt_pager(QT_FIRSTTIME); + Qstat(first_start) = TRUE; + } else if((u.uz0.dnum != u.uz.dnum) || (u.uz0.dlevel < u.uz.dlevel)) { + if(Qstat(not_ready) <= 2) qt_pager(QT_NEXTTIME); + else qt_pager(QT_OTHERTIME); + } +} + +STATIC_OVL void +on_locate() +{ + if(!Qstat(first_locate)) { + qt_pager(QT_FIRSTLOCATE); + Qstat(first_locate) = TRUE; + } else if(u.uz0.dlevel < u.uz.dlevel && !Qstat(killed_nemesis)) + qt_pager(QT_NEXTLOCATE); +} + +STATIC_OVL void +on_goal() +{ + if (Qstat(killed_nemesis)) { + return; + } else if (!Qstat(made_goal)) { + qt_pager(QT_FIRSTGOAL); + Qstat(made_goal) = 1; + } else { + qt_pager(QT_NEXTGOAL); + if(Qstat(made_goal) < 7) Qstat(made_goal)++; + } +} + +void +onquest() +{ + if(u.uevent.qcompleted || Not_firsttime) return; + if(!Is_special(&u.uz)) return; + + if(Is_qstart(&u.uz)) on_start(); + else if(Is_qlocate(&u.uz) && u.uz.dlevel > u.uz0.dlevel) on_locate(); + else if(Is_nemesis(&u.uz)) on_goal(); + return; +} + +void +nemdead() +{ + if(!Qstat(killed_nemesis)) { + Qstat(killed_nemesis) = TRUE; + qt_pager(QT_KILLEDNEM); + } +} + +void +artitouch() +{ + if(!Qstat(touched_artifact)) { + Qstat(touched_artifact) = TRUE; + qt_pager(QT_GOTIT); + exercise(A_WIS, TRUE); + } +} + +/* external hook for do.c (level change check) */ +boolean +ok_to_quest() +{ + return((boolean)((Qstat(got_quest) || Qstat(got_thanks))) + && (is_pure(FALSE) > 0)); +} + +STATIC_OVL boolean +not_capable() +{ + return((boolean)(u.ulevel < MIN_QUEST_LEVEL)); +} + +STATIC_OVL int +is_pure(talk) +boolean talk; +{ + int purity; + aligntyp original_alignment = u.ualignbase[A_ORIGINAL]; + +#ifdef WIZARD + if (wizard && talk) { + if (u.ualign.type != original_alignment) { + You("are currently %s instead of %s.", + align_str(u.ualign.type), align_str(original_alignment)); + } else if (u.ualignbase[A_CURRENT] != original_alignment) { + You("have converted."); + } else if (u.ualign.record < MIN_QUEST_ALIGN) { + You("are currently %d and require %d.", + u.ualign.record, MIN_QUEST_ALIGN); + if (yn_function("adjust?", (char *)0, 'y') == 'y') + u.ualign.record = MIN_QUEST_ALIGN; + } + } +#endif + purity = (u.ualign.record >= MIN_QUEST_ALIGN && + u.ualign.type == original_alignment && + u.ualignbase[A_CURRENT] == original_alignment) ? 1 : + (u.ualignbase[A_CURRENT] != original_alignment) ? -1 : 0; + return purity; +} + +/* + * Expell the player to the stairs on the parent of the quest dungeon. + * + * This assumes that the hero is currently _in_ the quest dungeon and that + * there is a single branch to and from it. + */ +STATIC_OVL void +expulsion(seal) +boolean seal; +{ + branch *br; + d_level *dest; + struct trap *t; + int portal_flag; + + br = dungeon_branch("The Quest"); + dest = (br->end1.dnum == u.uz.dnum) ? &br->end2 : &br->end1; + portal_flag = u.uevent.qexpelled ? 0 : /* returned via artifact? */ + !seal ? 1 : -1; + schedule_goto(dest, FALSE, FALSE, portal_flag, (char *)0, (char *)0); + if (seal) { /* remove the portal to the quest - sealing it off */ + int reexpelled = u.uevent.qexpelled; + u.uevent.qexpelled = 1; + /* Delete the near portal now; the far (main dungeon side) + portal will be deleted as part of arrival on that level. + If monster movement is in progress, any who haven't moved + yet will now miss out on a chance to wander through it... */ + for (t = ftrap; t; t = t->ntrap) + if (t->ttyp == MAGIC_PORTAL) break; + if (t) deltrap(t); /* (display might be briefly out of sync) */ + else if (!reexpelled) impossible("quest portal already gone?"); + } +} + +/* Either you've returned to quest leader while carrying the quest + artifact or you've just thrown it to/at him or her. If quest + completion text hasn't been given yet, give it now. Otherwise + give another message about the character keeping the artifact + and using the magic portal to return to the dungeon. */ +void +finish_quest(obj) +struct obj *obj; /* quest artifact; possibly null if carrying Amulet */ +{ + struct obj *otmp; + + if (u.uhave.amulet) { /* unlikely but not impossible */ + qt_pager(QT_HASAMULET); + /* leader IDs the real amulet but ignores any fakes */ + if ((otmp = carrying(AMULET_OF_YENDOR)) != 0) + fully_identify_obj(otmp); + } else { + qt_pager(!Qstat(got_thanks) ? QT_OFFEREDIT : QT_OFFEREDIT2); + /* should have obtained bell during quest; + if not, suggest returning for it now */ + if ((otmp = carrying(BELL_OF_OPENING)) == 0) + com_pager(5); + } + Qstat(got_thanks) = TRUE; + + if (obj) { + u.uevent.qcompleted = 1; /* you did it! */ + /* behave as if leader imparts sufficient info about the + quest artifact */ + fully_identify_obj(obj); + update_inventory(); + } +} + +STATIC_OVL void +chat_with_leader() +{ +/* Rule 0: Cheater checks. */ + if(u.uhave.questart && !Qstat(met_nemesis)) + Qstat(cheater) = TRUE; + +/* It is possible for you to get the amulet without completing + * the quest. If so, try to induce the player to quest. + */ + if(Qstat(got_thanks)) { +/* Rule 1: You've gone back with/without the amulet. */ + if(u.uhave.amulet) finish_quest((struct obj *)0); + +/* Rule 2: You've gone back before going for the amulet. */ + else qt_pager(QT_POSTHANKS); + } + +/* Rule 3: You've got the artifact and are back to return it. */ + else if(u.uhave.questart) { + struct obj *otmp; + + for (otmp = invent; otmp; otmp = otmp->nobj) + if (is_quest_artifact(otmp)) break; + + finish_quest(otmp); + +/* Rule 4: You haven't got the artifact yet. */ + } else if(Qstat(got_quest)) { + qt_pager(rn1(10, QT_ENCOURAGE)); + +/* Rule 5: You aren't yet acceptable - or are you? */ + } else { + if(!Qstat(met_leader)) { + qt_pager(QT_FIRSTLEADER); + Qstat(met_leader) = TRUE; + Qstat(not_ready) = 0; + } else qt_pager(QT_NEXTLEADER); + /* the quest leader might have passed through the portal into + the regular dungeon; none of the remaining make sense there */ + if (!on_level(&u.uz, &qstart_level)) return; + + if(not_capable()) { + qt_pager(QT_BADLEVEL); + exercise(A_WIS, TRUE); + expulsion(FALSE); + } else if(is_pure(TRUE) < 0) { + com_pager(QT_BANISHED); + expulsion(TRUE); + } else if(is_pure(TRUE) == 0) { + qt_pager(QT_BADALIGN); + if(Qstat(not_ready) == MAX_QUEST_TRIES) { + qt_pager(QT_LASTLEADER); + expulsion(TRUE); + } else { + Qstat(not_ready)++; + exercise(A_WIS, TRUE); + expulsion(FALSE); + } + } else { /* You are worthy! */ + qt_pager(QT_ASSIGNQUEST); + exercise(A_WIS, TRUE); + Qstat(got_quest) = TRUE; + } + } +} + +void +leader_speaks(mtmp) + register struct monst *mtmp; +{ + /* maybe you attacked leader? */ + if(!mtmp->mpeaceful) { + Qstat(pissed_off) = TRUE; + mtmp->mstrategy &= ~STRAT_WAITMASK; /* end the inaction */ + } + /* the quest leader might have passed through the portal into the + regular dungeon; if so, mustn't perform "backwards expulsion" */ + if (!on_level(&u.uz, &qstart_level)) return; + + if(Qstat(pissed_off)) { + qt_pager(QT_LASTLEADER); + expulsion(TRUE); + } else chat_with_leader(); +} + +STATIC_OVL void +chat_with_nemesis() +{ +/* The nemesis will do most of the talking, but... */ + qt_pager(rn1(10, QT_DISCOURAGE)); + if(!Qstat(met_nemesis)) Qstat(met_nemesis++); +} + +void +nemesis_speaks() +{ + if(!Qstat(in_battle)) { + if(u.uhave.questart) qt_pager(QT_NEMWANTSIT); + else if(Qstat(made_goal) == 1 || !Qstat(met_nemesis)) + qt_pager(QT_FIRSTNEMESIS); + else if(Qstat(made_goal) < 4) qt_pager(QT_NEXTNEMESIS); + else if(Qstat(made_goal) < 7) qt_pager(QT_OTHERNEMESIS); + else if(!rn2(5)) qt_pager(rn1(10, QT_DISCOURAGE)); + if(Qstat(made_goal) < 7) Qstat(made_goal)++; + Qstat(met_nemesis) = TRUE; + } else /* he will spit out random maledictions */ + if(!rn2(5)) qt_pager(rn1(10, QT_DISCOURAGE)); +} + +STATIC_OVL void +chat_with_guardian() +{ +/* These guys/gals really don't have much to say... */ + if (u.uhave.questart && Qstat(killed_nemesis)) + qt_pager(rn1(5, QT_GUARDTALK2)); + else + qt_pager(rn1(5, QT_GUARDTALK)); +} + +STATIC_OVL void +prisoner_speaks (mtmp) + register struct monst *mtmp; +{ + if (mtmp->data == &mons[PM_PRISONER] && + (mtmp->mstrategy & STRAT_WAITMASK)) { + /* Awaken the prisoner */ + if (canseemon(mtmp)) + pline("%s speaks:", Monnam(mtmp)); + verbalize("I'm finally free!"); + mtmp->mstrategy &= ~STRAT_WAITMASK; + mtmp->mpeaceful = 1; + + /* Your god is happy... */ + adjalign(3); + + /* ...But the guards are not */ + (void) angry_guards(FALSE); + } + return; +} + +void +quest_chat(mtmp) + register struct monst *mtmp; +{ + if (mtmp->m_id == Qstat(leader_m_id)) { + chat_with_leader(); + return; + } + switch(mtmp->data->msound) { + case MS_NEMESIS: chat_with_nemesis(); break; + case MS_GUARDIAN: chat_with_guardian(); break; + default: impossible("quest_chat: Unknown quest character %s.", + mon_nam(mtmp)); + } +} + +void +quest_talk(mtmp) + register struct monst *mtmp; +{ + if (mtmp->m_id == Qstat(leader_m_id)) { + leader_speaks(mtmp); + return; + } + switch(mtmp->data->msound) { + case MS_NEMESIS: nemesis_speaks(); break; + case MS_DJINNI: prisoner_speaks(mtmp); break; + default: break; + } +} + +void +quest_stat_check(mtmp) + struct monst *mtmp; +{ + if(mtmp->data->msound == MS_NEMESIS) + Qstat(in_battle) = (mtmp->mcanmove && !mtmp->msleeping && + monnear(mtmp, u.ux, u.uy)); +} + +/*quest.c*/ diff --git a/src/questpgr.c b/src/questpgr.c new file mode 100644 index 0000000..b4b80af --- /dev/null +++ b/src/questpgr.c @@ -0,0 +1,442 @@ +/* SCCS Id: @(#)questpgr.c 3.4 2000/05/05 */ +/* Copyright 1991, M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" + +/* quest-specific pager routines. */ + +#include "qtext.h" + +#define QTEXT_FILE "quest.dat" + +/* #define DEBUG */ /* uncomment for debugging */ + +static void FDECL(Fread, (genericptr_t,int,int,dlb *)); +STATIC_DCL struct qtmsg * FDECL(construct_qtlist, (long)); +STATIC_DCL const char * NDECL(intermed); +STATIC_DCL const char * NDECL(neminame); +STATIC_DCL const char * NDECL(guardname); +STATIC_DCL const char * NDECL(homebase); +STATIC_DCL struct qtmsg * FDECL(msg_in, (struct qtmsg *,int)); +STATIC_DCL void FDECL(convert_arg, (CHAR_P)); +STATIC_DCL void NDECL(convert_line); +STATIC_DCL void FDECL(deliver_by_pline, (struct qtmsg *)); +STATIC_DCL void FDECL(deliver_by_window, (struct qtmsg *,int)); + +static char in_line[80], cvt_buf[64], out_line[128]; +static struct qtlists qt_list; +static dlb *msg_file; +/* used by ldrname() and neminame(), then copied into cvt_buf */ +static char nambuf[sizeof cvt_buf]; + +#ifdef DEBUG +static void NDECL(dump_qtlist); + +static void +dump_qtlist() /* dump the character msg list to check appearance */ +{ + struct qtmsg *msg; + long size; + + for (msg = qt_list.chrole; msg->msgnum > 0; msg++) { + pline("msgnum %d: delivery %c", + msg->msgnum, msg->delivery); + more(); + (void) dlb_fseek(msg_file, msg->offset, SEEK_SET); + deliver_by_window(msg, NHW_TEXT); + } +} +#endif /* DEBUG */ + +static void +Fread(ptr, size, nitems, stream) +genericptr_t ptr; +int size, nitems; +dlb *stream; +{ + int cnt; + + if ((cnt = dlb_fread(ptr, size, nitems, stream)) != nitems) { + + panic("PREMATURE EOF ON QUEST TEXT FILE! Expected %d bytes, got %d", + (size * nitems), (size * cnt)); + } +} + +STATIC_OVL struct qtmsg * +construct_qtlist(hdr_offset) +long hdr_offset; +{ + struct qtmsg *msg_list; + int n_msgs; + + (void) dlb_fseek(msg_file, hdr_offset, SEEK_SET); + Fread(&n_msgs, sizeof(int), 1, msg_file); + msg_list = (struct qtmsg *) + alloc((unsigned)(n_msgs+1)*sizeof(struct qtmsg)); + + /* + * Load up the list. + */ + Fread((genericptr_t)msg_list, n_msgs*sizeof(struct qtmsg), 1, msg_file); + + msg_list[n_msgs].msgnum = -1; + return(msg_list); +} + +void +load_qtlist() +{ + + int n_classes, i; + char qt_classes[N_HDR][LEN_HDR]; + long qt_offsets[N_HDR]; + + msg_file = dlb_fopen(QTEXT_FILE, RDBMODE); + if (!msg_file) + panic("CANNOT OPEN QUEST TEXT FILE %s.", QTEXT_FILE); + + /* + * Read in the number of classes, then the ID's & offsets for + * each header. + */ + + Fread(&n_classes, sizeof(int), 1, msg_file); + Fread(&qt_classes[0][0], sizeof(char)*LEN_HDR, n_classes, msg_file); + Fread(qt_offsets, sizeof(long), n_classes, msg_file); + + /* + * Now construct the message lists for quick reference later + * on when we are actually paging the messages out. + */ + + qt_list.common = qt_list.chrole = (struct qtmsg *)0; + + for (i = 0; i < n_classes; i++) { + if (!strncmp(COMMON_ID, qt_classes[i], LEN_HDR)) + qt_list.common = construct_qtlist(qt_offsets[i]); + else if (!strncmp(urole.filecode, qt_classes[i], LEN_HDR)) + qt_list.chrole = construct_qtlist(qt_offsets[i]); +#if 0 /* UNUSED but available */ + else if (!strncmp(urace.filecode, qt_classes[i], LEN_HDR)) + qt_list.chrace = construct_qtlist(qt_offsets[i]); +#endif + } + + if (!qt_list.common || !qt_list.chrole) + impossible("load_qtlist: cannot load quest text."); +#ifdef DEBUG + dump_qtlist(); +#endif + return; /* no ***DON'T*** close the msg_file */ +} + +/* called at program exit */ +void +unload_qtlist() +{ + if (msg_file) + (void) dlb_fclose(msg_file), msg_file = 0; + if (qt_list.common) + free((genericptr_t) qt_list.common), qt_list.common = 0; + if (qt_list.chrole) + free((genericptr_t) qt_list.chrole), qt_list.chrole = 0; + return; +} + +short +quest_info(typ) +int typ; +{ + switch (typ) { + case 0: return (urole.questarti); + case MS_LEADER: return (urole.ldrnum); + case MS_NEMESIS: return (urole.neminum); + case MS_GUARDIAN: return (urole.guardnum); + default: impossible("quest_info(%d)", typ); + } + return 0; +} + +const char * +ldrname() /* return your role leader's name */ +{ + int i = urole.ldrnum; + + Sprintf(nambuf, "%s%s", + type_is_pname(&mons[i]) ? "" : "the ", + mons[i].mname); + return nambuf; +} + +STATIC_OVL const char * +intermed() /* return your intermediate target string */ +{ + return (urole.intermed); +} + +boolean +is_quest_artifact(otmp) +struct obj *otmp; +{ + return((boolean)(otmp->oartifact == urole.questarti)); +} + +STATIC_OVL const char * +neminame() /* return your role nemesis' name */ +{ + int i = urole.neminum; + + Sprintf(nambuf, "%s%s", + type_is_pname(&mons[i]) ? "" : "the ", + mons[i].mname); + return nambuf; +} + +STATIC_OVL const char * +guardname() /* return your role leader's guard monster name */ +{ + int i = urole.guardnum; + + return(mons[i].mname); +} + +STATIC_OVL const char * +homebase() /* return your role leader's location */ +{ + return(urole.homebase); +} + +STATIC_OVL struct qtmsg * +msg_in(qtm_list, msgnum) +struct qtmsg *qtm_list; +int msgnum; +{ + struct qtmsg *qt_msg; + + for (qt_msg = qtm_list; qt_msg->msgnum > 0; qt_msg++) + if (qt_msg->msgnum == msgnum) return(qt_msg); + + return((struct qtmsg *)0); +} + +STATIC_OVL void +convert_arg(c) +char c; +{ + register const char *str; + + switch (c) { + + case 'p': str = plname; + break; + case 'c': str = (flags.female && urole.name.f) ? + urole.name.f : urole.name.m; + break; + case 'r': str = rank_of(u.ulevel, Role_switch, flags.female); + break; + case 'R': str = rank_of(MIN_QUEST_LEVEL, Role_switch, + flags.female); + break; + case 's': str = (flags.female) ? "sister" : "brother"; + break; + case 'S': str = (flags.female) ? "daughter" : "son"; + break; + case 'l': str = ldrname(); + break; + case 'i': str = intermed(); + break; + case 'o': str = the(artiname(urole.questarti)); + break; + case 'n': str = neminame(); + break; + case 'g': str = guardname(); + break; + case 'G': str = align_gtitle(u.ualignbase[A_ORIGINAL]); + break; + case 'H': str = homebase(); + break; + case 'a': str = align_str(u.ualignbase[A_ORIGINAL]); + break; + case 'A': str = align_str(u.ualign.type); + break; + case 'd': str = align_gname(u.ualignbase[A_ORIGINAL]); + break; + case 'D': str = align_gname(A_LAWFUL); + break; + case 'C': str = "chaotic"; + break; + case 'N': str = "neutral"; + break; + case 'L': str = "lawful"; + break; + case 'x': str = Blind ? "sense" : "see"; + break; + case 'Z': str = dungeons[0].dname; + break; + case '%': str = "%"; + break; + default: str = ""; + break; + } + Strcpy(cvt_buf, str); +} + +STATIC_OVL void +convert_line() +{ + char *c, *cc; + char xbuf[BUFSZ]; + + cc = out_line; + for (c = xcrypt(in_line, xbuf); *c; c++) { + + *cc = 0; + switch(*c) { + + case '\r': + case '\n': + *(++cc) = 0; + return; + + case '%': + if (*(c+1)) { + convert_arg(*(++c)); + switch (*(++c)) { + + /* insert "a"/"an" prefix */ + case 'A': Strcat(cc, An(cvt_buf)); + cc += strlen(cc); + continue; /* for */ + case 'a': Strcat(cc, an(cvt_buf)); + cc += strlen(cc); + continue; /* for */ + + /* capitalize */ + case 'C': cvt_buf[0] = highc(cvt_buf[0]); + break; + + /* pluralize */ + case 'P': cvt_buf[0] = highc(cvt_buf[0]); + case 'p': Strcpy(cvt_buf, makeplural(cvt_buf)); + break; + + /* append possessive suffix */ + case 'S': cvt_buf[0] = highc(cvt_buf[0]); + case 's': Strcpy(cvt_buf, s_suffix(cvt_buf)); + break; + + /* strip any "the" prefix */ + case 't': if (!strncmpi(cvt_buf, "the ", 4)) { + Strcat(cc, &cvt_buf[4]); + cc += strlen(cc); + continue; /* for */ + } + break; + + default: --c; /* undo switch increment */ + break; + } + Strcat(cc, cvt_buf); + cc += strlen(cvt_buf); + break; + } /* else fall through */ + + default: + *cc++ = *c; + break; + } + } + if (cc >= out_line + sizeof out_line) + panic("convert_line: overflow"); + *cc = 0; + return; +} + +STATIC_OVL void +deliver_by_pline(qt_msg) +struct qtmsg *qt_msg; +{ + long size; + + for (size = 0; size < qt_msg->size; size += (long)strlen(in_line)) { + (void) dlb_fgets(in_line, 80, msg_file); + convert_line(); + pline(out_line); + } + +} + +STATIC_OVL void +deliver_by_window(qt_msg, how) +struct qtmsg *qt_msg; +int how; +{ + long size; + winid datawin = create_nhwindow(how); + + for (size = 0; size < qt_msg->size; size += (long)strlen(in_line)) { + (void) dlb_fgets(in_line, 80, msg_file); + convert_line(); + putstr(datawin, 0, out_line); + } + display_nhwindow(datawin, TRUE); + destroy_nhwindow(datawin); +} + +void +com_pager(msgnum) +int msgnum; +{ + struct qtmsg *qt_msg; + + if (!(qt_msg = msg_in(qt_list.common, msgnum))) { + impossible("com_pager: message %d not found.", msgnum); + return; + } + + (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET); + if (qt_msg->delivery == 'p') deliver_by_pline(qt_msg); + else if (msgnum == 1) deliver_by_window(qt_msg, NHW_MENU); + else deliver_by_window(qt_msg, NHW_TEXT); + return; +} + +void +qt_pager(msgnum) +int msgnum; +{ + struct qtmsg *qt_msg; + + if (!(qt_msg = msg_in(qt_list.chrole, msgnum))) { + impossible("qt_pager: message %d not found.", msgnum); + return; + } + + (void) dlb_fseek(msg_file, qt_msg->offset, SEEK_SET); + if (qt_msg->delivery == 'p' && strcmp(windowprocs.name, "X11")) + deliver_by_pline(qt_msg); + else deliver_by_window(qt_msg, NHW_TEXT); + return; +} + +struct permonst * +qt_montype() +{ + int qpm; + + if (rn2(5)) { + qpm = urole.enemy1num; + if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD)) + return (&mons[qpm]); + return (mkclass(urole.enemy1sym, 0)); + } + qpm = urole.enemy2num; + if (qpm != NON_PM && rn2(5) && !(mvitals[qpm].mvflags & G_GENOD)) + return (&mons[qpm]); + return (mkclass(urole.enemy2sym, 0)); +} + +/*questpgr.c*/ diff --git a/src/read.c b/src/read.c new file mode 100644 index 0000000..ab530f0 --- /dev/null +++ b/src/read.c @@ -0,0 +1,1878 @@ +/* SCCS Id: @(#)read.c 3.4 2003/10/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* KMH -- Copied from pray.c; this really belongs in a header file */ +#define DEVOUT 14 +#define STRIDENT 4 + +#define Your_Own_Role(mndx) \ + ((mndx) == urole.malenum || \ + (urole.femalenum != NON_PM && (mndx) == urole.femalenum)) +#define Your_Own_Race(mndx) \ + ((mndx) == urace.malenum || \ + (urace.femalenum != NON_PM && (mndx) == urace.femalenum)) + +#ifdef OVLB + +boolean known; + +static NEARDATA const char readable[] = + { ALL_CLASSES, SCROLL_CLASS, SPBOOK_CLASS, 0 }; +static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; + +static void FDECL(wand_explode, (struct obj *)); +static void NDECL(do_class_genocide); +static void FDECL(stripspe,(struct obj *)); +static void FDECL(p_glow1,(struct obj *)); +static void FDECL(p_glow2,(struct obj *,const char *)); +static void FDECL(randomize,(int *, int)); +static void FDECL(forget_single_object, (int)); +static void FDECL(forget, (int)); +static void FDECL(maybe_tame, (struct monst *,struct obj *)); + +STATIC_PTR void FDECL(set_lit, (int,int,genericptr_t)); + +int +doread() +{ + register struct obj *scroll; + register boolean confused; + + known = FALSE; + if(check_capacity((char *)0)) return (0); + scroll = getobj(readable, "read"); + if(!scroll) return(0); + + /* outrumor has its own blindness check */ + if(scroll->otyp == FORTUNE_COOKIE) { + if(flags.verbose) + You("break up the cookie and throw away the pieces."); + outrumor(bcsign(scroll), BY_COOKIE); + if (!Blind) u.uconduct.literate++; + useup(scroll); + return(1); +#ifdef TOURIST + } else if (scroll->otyp == T_SHIRT) { + static const char *shirt_msgs[] = { /* Scott Bigham */ + "I explored the Dungeons of Doom and all I got was this lousy T-shirt!", + "Is that Mjollnir in your pocket or are you just happy to see me?", + "It's not the size of your sword, it's how #enhance'd you are with it.", + "Madame Elvira's House O' Succubi Lifetime Customer", + "Madame Elvira's House O' Succubi Employee of the Month", + "Ludios Vault Guards Do It In Small, Dark Rooms", + "Yendor Military Soldiers Do It In Large Groups", + "I survived Yendor Military Boot Camp", + "Ludios Accounting School Intra-Mural Lacrosse Team", + "Oracle(TM) Fountains 10th Annual Wet T-Shirt Contest", + "Hey, black dragon! Disintegrate THIS!", + "I'm With Stupid -->", + "Don't blame me, I voted for Izchak!", + "Don't Panic", /* HHGTTG */ + "Furinkan High School Athletic Dept.", /* Ranma 1/2 */ + "Hel-LOOO, Nurse!", /* Animaniacs */ + }; + char buf[BUFSZ]; + int erosion; + + if (Blind) { + You_cant("feel any Braille writing."); + return 0; + } + u.uconduct.literate++; + if(flags.verbose) + pline("It reads:"); + Strcpy(buf, shirt_msgs[scroll->o_id % SIZE(shirt_msgs)]); + erosion = greatest_erosion(scroll); + if (erosion) + wipeout_text(buf, + (int)(strlen(buf) * erosion / (2*MAX_ERODE)), + scroll->o_id ^ (unsigned)u.ubirthday); + pline("\"%s\"", buf); + return 1; +#endif /* TOURIST */ + } else if (scroll->oclass != SCROLL_CLASS + && scroll->oclass != SPBOOK_CLASS) { + pline(silly_thing_to, "read"); + return(0); + } else if (Blind) { + const char *what = 0; + if (scroll->oclass == SPBOOK_CLASS) + what = "mystic runes"; + else if (!scroll->dknown) + what = "formula on the scroll"; + if (what) { + pline("Being blind, you cannot read the %s.", what); + return(0); + } + } + + /* Actions required to win the game aren't counted towards conduct */ + if (scroll->otyp != SPE_BOOK_OF_THE_DEAD && + scroll->otyp != SPE_BLANK_PAPER && + scroll->otyp != SCR_BLANK_PAPER) + u.uconduct.literate++; + + confused = (Confusion != 0); +#ifdef MAIL + if (scroll->otyp == SCR_MAIL) confused = FALSE; +#endif + if(scroll->oclass == SPBOOK_CLASS) { + return(study_book(scroll)); + } + scroll->in_use = TRUE; /* scroll, not spellbook, now being read */ + if(scroll->otyp != SCR_BLANK_PAPER) { + if(Blind) + pline("As you %s the formula on it, the scroll disappears.", + is_silent(youmonst.data) ? "cogitate" : "pronounce"); + else + pline("As you read the scroll, it disappears."); + if(confused) { + if (Hallucination) + pline("Being so trippy, you screw up..."); + else + pline("Being confused, you mis%s the magic words...", + is_silent(youmonst.data) ? "understand" : "pronounce"); + } + } + if(!seffects(scroll)) { + if(!objects[scroll->otyp].oc_name_known) { + if(known) { + makeknown(scroll->otyp); + more_experienced(0,10); + } else if(!objects[scroll->otyp].oc_uname) + docall(scroll); + } + if(scroll->otyp != SCR_BLANK_PAPER) + useup(scroll); + else scroll->in_use = FALSE; + } + return(1); +} + +static void +stripspe(obj) +register struct obj *obj; +{ + if (obj->blessed) pline(nothing_happens); + else { + if (obj->spe > 0) { + obj->spe = 0; + if (obj->otyp == OIL_LAMP || obj->otyp == BRASS_LANTERN) + obj->age = 0; + Your("%s %s briefly.",xname(obj), otense(obj, "vibrate")); + } else pline(nothing_happens); + } +} + +static void +p_glow1(otmp) +register struct obj *otmp; +{ + Your("%s %s briefly.", xname(otmp), + otense(otmp, Blind ? "vibrate" : "glow")); +} + +static void +p_glow2(otmp,color) +register struct obj *otmp; +register const char *color; +{ + Your("%s %s%s%s for a moment.", + xname(otmp), + otense(otmp, Blind ? "vibrate" : "glow"), + Blind ? "" : " ", + Blind ? nul : hcolor(color)); +} + +/* Is the object chargeable? For purposes of inventory display; it is */ +/* possible to be able to charge things for which this returns FALSE. */ +boolean +is_chargeable(obj) +struct obj *obj; +{ + if (obj->oclass == WAND_CLASS) return TRUE; + /* known && !uname is possible after amnesia/mind flayer */ + if (obj->oclass == RING_CLASS) + return (boolean)(objects[obj->otyp].oc_charged && + (obj->known || objects[obj->otyp].oc_uname)); + if (is_weptool(obj)) /* specific check before general tools */ + return FALSE; + if (obj->oclass == TOOL_CLASS) + return (boolean)(objects[obj->otyp].oc_charged); + return FALSE; /* why are weapons/armor considered charged anyway? */ +} + +/* + * recharge an object; curse_bless is -1 if the recharging implement + * was cursed, +1 if blessed, 0 otherwise. + */ +void +recharge(obj, curse_bless) +struct obj *obj; +int curse_bless; +{ + register int n; + boolean is_cursed, is_blessed; + + is_cursed = curse_bless < 0; + is_blessed = curse_bless > 0; + + if (obj->oclass == WAND_CLASS) { + /* undo any prior cancellation, even when is_cursed */ + if (obj->spe == -1) obj->spe = 0; + + /* + * Recharging might cause wands to explode. + * v = number of previous recharges + * v = percentage chance to explode on this attempt + * v = cumulative odds for exploding + * 0 : 0 0 + * 1 : 0.29 0.29 + * 2 : 2.33 2.62 + * 3 : 7.87 10.28 + * 4 : 18.66 27.02 + * 5 : 36.44 53.62 + * 6 : 62.97 82.83 + * 7 : 100 100 + */ + n = (int)obj->recharged; + if (n > 0 && (obj->otyp == WAN_WISHING || + (n * n * n > rn2(7*7*7)))) { /* recharge_limit */ + wand_explode(obj); + return; + } + /* didn't explode, so increment the recharge count */ + obj->recharged = (unsigned)(n + 1); + + /* now handle the actual recharging */ + if (is_cursed) { + stripspe(obj); + } else { + int lim = (obj->otyp == WAN_WISHING) ? 3 : + (objects[obj->otyp].oc_dir != NODIR) ? 8 : 15; + + n = (lim == 3) ? 3 : rn1(5, lim + 1 - 5); + if (!is_blessed) n = rnd(n); + + if (obj->spe < n) obj->spe = n; + else obj->spe++; + if (obj->otyp == WAN_WISHING && obj->spe > 3) { + wand_explode(obj); + return; + } + if (obj->spe >= lim) p_glow2(obj, NH_BLUE); + else p_glow1(obj); + } + + } else if (obj->oclass == RING_CLASS && + objects[obj->otyp].oc_charged) { + /* charging does not affect ring's curse/bless status */ + int s = is_blessed ? rnd(3) : is_cursed ? -rnd(2) : 1; + boolean is_on = (obj == uleft || obj == uright); + + /* destruction depends on current state, not adjustment */ + if (obj->spe > rn2(7) || obj->spe <= -5) { + Your("%s %s momentarily, then %s!", + xname(obj), otense(obj,"pulsate"), otense(obj,"explode")); + if (is_on) Ring_gone(obj); + s = rnd(3 * abs(obj->spe)); /* amount of damage */ + useup(obj); + losehp(s, "exploding ring", KILLED_BY_AN); + } else { + long mask = is_on ? (obj == uleft ? LEFT_RING : + RIGHT_RING) : 0L; + Your("%s spins %sclockwise for a moment.", + xname(obj), s < 0 ? "counter" : ""); + /* cause attributes and/or properties to be updated */ + if (is_on) Ring_off(obj); + obj->spe += s; /* update the ring while it's off */ + if (is_on) setworn(obj, mask), Ring_on(obj); + /* oartifact: if a touch-sensitive artifact ring is + ever created the above will need to be revised */ + } + + } else if (obj->oclass == TOOL_CLASS) { + int rechrg = (int)obj->recharged; + + if (objects[obj->otyp].oc_charged) { + /* tools don't have a limit, but the counter used does */ + if (rechrg < 7) /* recharge_limit */ + obj->recharged++; + } + switch(obj->otyp) { + case BELL_OF_OPENING: + if (is_cursed) stripspe(obj); + else if (is_blessed) obj->spe += rnd(3); + else obj->spe += 1; + if (obj->spe > 5) obj->spe = 5; + break; + case MAGIC_MARKER: + case TINNING_KIT: +#ifdef TOURIST + case EXPENSIVE_CAMERA: +#endif + if (is_cursed) stripspe(obj); + else if (rechrg && obj->otyp == MAGIC_MARKER) { /* previously recharged */ + obj->recharged = 1; /* override increment done above */ + if (obj->spe < 3) + Your("marker seems permanently dried out."); + else + pline(nothing_happens); + } else if (is_blessed) { + n = rn1(16,15); /* 15..30 */ + if (obj->spe + n <= 50) + obj->spe = 50; + else if (obj->spe + n <= 75) + obj->spe = 75; + else { + int chrg = (int)obj->spe; + if ((chrg + n) > 127) + obj->spe = 127; + else + obj->spe += n; + } + p_glow2(obj, NH_BLUE); + } else { + n = rn1(11,10); /* 10..20 */ + if (obj->spe + n <= 50) + obj->spe = 50; + else { + int chrg = (int)obj->spe; + if ((chrg + n) > 127) + obj->spe = 127; + else + obj->spe += n; + } + p_glow2(obj, NH_WHITE); + } + break; + case OIL_LAMP: + case BRASS_LANTERN: + if (is_cursed) { + stripspe(obj); + if (obj->lamplit) { + if (!Blind) + pline("%s out!", Tobjnam(obj, "go")); + end_burn(obj, TRUE); + } + } else if (is_blessed) { + obj->spe = 1; + obj->age = 1500; + p_glow2(obj, NH_BLUE); + } else { + obj->spe = 1; + obj->age += 750; + if (obj->age > 1500) obj->age = 1500; + p_glow1(obj); + } + break; + case CRYSTAL_BALL: + if (is_cursed) stripspe(obj); + else if (is_blessed) { + obj->spe = 6; + p_glow2(obj, NH_BLUE); + } else { + if (obj->spe < 5) { + obj->spe++; + p_glow1(obj); + } else pline(nothing_happens); + } + break; + case HORN_OF_PLENTY: + case BAG_OF_TRICKS: + case CAN_OF_GREASE: + if (is_cursed) stripspe(obj); + else if (is_blessed) { + if (obj->spe <= 10) + obj->spe += rn1(10, 6); + else obj->spe += rn1(5, 6); + if (obj->spe > 50) obj->spe = 50; + p_glow2(obj, NH_BLUE); + } else { + obj->spe += rnd(5); + if (obj->spe > 50) obj->spe = 50; + p_glow1(obj); + } + break; + case MAGIC_FLUTE: + case MAGIC_HARP: + case FROST_HORN: + case FIRE_HORN: + case DRUM_OF_EARTHQUAKE: + if (is_cursed) { + stripspe(obj); + } else if (is_blessed) { + obj->spe += d(2,4); + if (obj->spe > 20) obj->spe = 20; + p_glow2(obj, NH_BLUE); + } else { + obj->spe += rnd(4); + if (obj->spe > 20) obj->spe = 20; + p_glow1(obj); + } + break; + default: + goto not_chargable; + /*NOTREACHED*/ + break; + } /* switch */ + + } else { + not_chargable: + You("have a feeling of loss."); + } +} + + +/* Forget known information about this object class. */ +static void +forget_single_object(obj_id) + int obj_id; +{ + objects[obj_id].oc_name_known = 0; + objects[obj_id].oc_pre_discovered = 0; /* a discovery when relearned */ + if (objects[obj_id].oc_uname) { + free((genericptr_t)objects[obj_id].oc_uname); + objects[obj_id].oc_uname = 0; + } + undiscover_object(obj_id); /* after clearing oc_name_known */ + + /* clear & free object names from matching inventory items too? */ +} + + +#if 0 /* here if anyone wants it.... */ +/* Forget everything known about a particular object class. */ +static void +forget_objclass(oclass) + int oclass; +{ + int i; + + for (i=bases[oclass]; + i < NUM_OBJECTS && objects[i].oc_class==oclass; i++) + forget_single_object(i); +} +#endif + + +/* randomize the given list of numbers 0 <= i < count */ +static void +randomize(indices, count) + int *indices; + int count; +{ + int i, iswap, temp; + + for (i = count - 1; i > 0; i--) { + if ((iswap = rn2(i + 1)) == i) continue; + temp = indices[i]; + indices[i] = indices[iswap]; + indices[iswap] = temp; + } +} + + +/* Forget % of known objects. */ +void +forget_objects(percent) + int percent; +{ + int i, count; + int indices[NUM_OBJECTS]; + + if (percent == 0) return; + if (percent <= 0 || percent > 100) { + impossible("forget_objects: bad percent %d", percent); + return; + } + + for (count = 0, i = 1; i < NUM_OBJECTS; i++) + if (OBJ_DESCR(objects[i]) && + (objects[i].oc_name_known || objects[i].oc_uname)) + indices[count++] = i; + + randomize(indices, count); + + /* forget first % of randomized indices */ + count = ((count * percent) + 50) / 100; + for (i = 0; i < count; i++) + forget_single_object(indices[i]); +} + + +/* Forget some or all of map (depends on parameters). */ +void +forget_map(howmuch) + int howmuch; +{ + register int zx, zy; + + if (In_sokoban(&u.uz)) + return; + + known = TRUE; + for(zx = 0; zx < COLNO; zx++) for(zy = 0; zy < ROWNO; zy++) + if (howmuch & ALL_MAP || rn2(7)) { + /* Zonk all memory of this location. */ + levl[zx][zy].seenv = 0; + levl[zx][zy].waslit = 0; + levl[zx][zy].glyph = cmap_to_glyph(S_stone); + } +} + +/* Forget all traps on the level. */ +void +forget_traps() +{ + register struct trap *trap; + + /* forget all traps (except the one the hero is in :-) */ + for (trap = ftrap; trap; trap = trap->ntrap) + if ((trap->tx != u.ux || trap->ty != u.uy) && (trap->ttyp != HOLE)) + trap->tseen = 0; +} + +/* + * Forget given % of all levels that the hero has visited and not forgotten, + * except this one. + */ +void +forget_levels(percent) + int percent; +{ + int i, count; + xchar maxl, this_lev; + int indices[MAXLINFO]; + + if (percent == 0) return; + + if (percent <= 0 || percent > 100) { + impossible("forget_levels: bad percent %d", percent); + return; + } + + this_lev = ledger_no(&u.uz); + maxl = maxledgerno(); + + /* count & save indices of non-forgotten visited levels */ + /* Sokoban levels are pre-mapped for the player, and should stay + * so, or they become nearly impossible to solve. But try to + * shift the forgetting elsewhere by fiddling with percent + * instead of forgetting fewer levels. + */ + for (count = 0, i = 0; i <= maxl; i++) + if ((level_info[i].flags & VISITED) && + !(level_info[i].flags & FORGOTTEN) && i != this_lev) { + if (ledger_to_dnum(i) == sokoban_dnum) + percent += 2; + else + indices[count++] = i; + } + + if (percent > 100) percent = 100; + + randomize(indices, count); + + /* forget first % of randomized indices */ + count = ((count * percent) + 50) / 100; + for (i = 0; i < count; i++) { + level_info[indices[i]].flags |= FORGOTTEN; + } +} + +/* + * Forget some things (e.g. after reading a scroll of amnesia). When called, + * the following are always forgotten: + * + * - felt ball & chain + * - traps + * - part (6 out of 7) of the map + * + * Other things are subject to flags: + * + * howmuch & ALL_MAP = forget whole map + * howmuch & ALL_SPELLS = forget all spells + */ +static void +forget(howmuch) +int howmuch; +{ + + if (Punished) u.bc_felt = 0; /* forget felt ball&chain */ + + forget_map(howmuch); + forget_traps(); + + /* 1 in 3 chance of forgetting some levels */ + if (!rn2(3)) forget_levels(rn2(25)); + + /* 1 in 3 chance of forgeting some objects */ + if (!rn2(3)) forget_objects(rn2(25)); + + if (howmuch & ALL_SPELLS) losespells(); + /* + * Make sure that what was seen is restored correctly. To do this, + * we need to go blind for an instant --- turn off the display, + * then restart it. All this work is needed to correctly handle + * walls which are stone on one side and wall on the other. Turning + * off the seen bits above will make the wall revert to stone, but + * there are cases where we don't want this to happen. The easiest + * thing to do is to run it through the vision system again, which + * is always correct. + */ + docrt(); /* this correctly will reset vision */ +} + +/* monster is hit by scroll of taming's effect */ +static void +maybe_tame(mtmp, sobj) +struct monst *mtmp; +struct obj *sobj; +{ + if (sobj->cursed) { + setmangry(mtmp); + } else { + if (mtmp->isshk) + make_happy_shk(mtmp, FALSE); + else if (!resist(mtmp, sobj->oclass, 0, NOTELL)) + (void) tamedog(mtmp, (struct obj *) 0); + } +} + +int +seffects(sobj) +register struct obj *sobj; +{ + register int cval; + register boolean confused = (Confusion != 0); + register struct obj *otmp; + + if (objects[sobj->otyp].oc_magic) + exercise(A_WIS, TRUE); /* just for trying */ + switch(sobj->otyp) { +#ifdef MAIL + case SCR_MAIL: + known = TRUE; + if (sobj->spe) + pline("This seems to be junk mail addressed to the finder of the Eye of Larn."); + /* note to the puzzled: the game Larn actually sends you junk + * mail if you win! + */ + else readmail(sobj); + break; +#endif + case SCR_ENCHANT_ARMOR: + { + register schar s; + boolean special_armor; + boolean same_color; + + otmp = some_armor(&youmonst); + if(!otmp) { + strange_feeling(sobj, + !Blind ? "Your skin glows then fades." : + "Your skin feels warm for a moment."); + exercise(A_CON, !sobj->cursed); + exercise(A_STR, !sobj->cursed); + return(1); + } + if(confused) { + otmp->oerodeproof = !(sobj->cursed); + if(Blind) { + otmp->rknown = FALSE; + Your("%s %s warm for a moment.", + xname(otmp), otense(otmp, "feel")); + } else { + otmp->rknown = TRUE; + Your("%s %s covered by a %s %s %s!", + xname(otmp), otense(otmp, "are"), + sobj->cursed ? "mottled" : "shimmering", + hcolor(sobj->cursed ? NH_BLACK : NH_GOLDEN), + sobj->cursed ? "glow" : + (is_shield(otmp) ? "layer" : "shield")); + } + if (otmp->oerodeproof && + (otmp->oeroded || otmp->oeroded2)) { + otmp->oeroded = otmp->oeroded2 = 0; + Your("%s %s as good as new!", + xname(otmp), + otense(otmp, Blind ? "feel" : "look")); + } + break; + } + /* elven armor vibrates warningly when enchanted beyond a limit */ + special_armor = is_elven_armor(otmp) || + (Role_if(PM_WIZARD) && otmp->otyp == CORNUTHAUM); + if (sobj->cursed) + same_color = + (otmp->otyp == BLACK_DRAGON_SCALE_MAIL || + otmp->otyp == BLACK_DRAGON_SCALES); + else + same_color = + (otmp->otyp == SILVER_DRAGON_SCALE_MAIL || + otmp->otyp == SILVER_DRAGON_SCALES || + otmp->otyp == SHIELD_OF_REFLECTION); + if (Blind) same_color = FALSE; + + /* KMH -- catch underflow */ + s = sobj->cursed ? -otmp->spe : otmp->spe; + if (s > (special_armor ? 5 : 3) && rn2(s)) { + Your("%s violently %s%s%s for a while, then %s.", + xname(otmp), + otense(otmp, Blind ? "vibrate" : "glow"), + (!Blind && !same_color) ? " " : nul, + (Blind || same_color) ? nul : + hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), + otense(otmp, "evaporate")); + if(is_cloak(otmp)) (void) Cloak_off(); + if(is_boots(otmp)) (void) Boots_off(); + if(is_helmet(otmp)) (void) Helmet_off(); + if(is_gloves(otmp)) (void) Gloves_off(); + if(is_shield(otmp)) (void) Shield_off(); + if(otmp == uarm) (void) Armor_gone(); + useup(otmp); + break; + } + s = sobj->cursed ? -1 : + otmp->spe >= 9 ? (rn2(otmp->spe) == 0) : + sobj->blessed ? rnd(3-otmp->spe/3) : 1; + if (s >= 0 && otmp->otyp >= GRAY_DRAGON_SCALES && + otmp->otyp <= YELLOW_DRAGON_SCALES) { + /* dragon scales get turned into dragon scale mail */ + Your("%s merges and hardens!", xname(otmp)); + setworn((struct obj *)0, W_ARM); + /* assumes same order */ + otmp->otyp = GRAY_DRAGON_SCALE_MAIL + + otmp->otyp - GRAY_DRAGON_SCALES; + otmp->cursed = 0; + if (sobj->blessed) { + otmp->spe++; + otmp->blessed = 1; + } + otmp->known = 1; + setworn(otmp, W_ARM); + break; + } + Your("%s %s%s%s%s for a %s.", + xname(otmp), + s == 0 ? "violently " : nul, + otense(otmp, Blind ? "vibrate" : "glow"), + (!Blind && !same_color) ? " " : nul, + (Blind || same_color) ? nul : hcolor(sobj->cursed ? NH_BLACK : NH_SILVER), + (s*s>1) ? "while" : "moment"); + otmp->cursed = sobj->cursed; + if (!otmp->blessed || sobj->cursed) + otmp->blessed = sobj->blessed; + if (s) { + otmp->spe += s; + adj_abon(otmp, s); + known = otmp->known; + } + + if ((otmp->spe > (special_armor ? 5 : 3)) && + (special_armor || !rn2(7))) + Your("%s suddenly %s %s.", + xname(otmp), otense(otmp, "vibrate"), + Blind ? "again" : "unexpectedly"); + break; + } + case SCR_DESTROY_ARMOR: + { + otmp = some_armor(&youmonst); + if(confused) { + if(!otmp) { + strange_feeling(sobj,"Your bones itch."); + exercise(A_STR, FALSE); + exercise(A_CON, FALSE); + return(1); + } + otmp->oerodeproof = sobj->cursed; + p_glow2(otmp, NH_PURPLE); + break; + } + if(!sobj->cursed || !otmp || !otmp->cursed) { + if(!destroy_arm(otmp)) { + strange_feeling(sobj,"Your skin itches."); + exercise(A_STR, FALSE); + exercise(A_CON, FALSE); + return(1); + } else + known = TRUE; + } else { /* armor and scroll both cursed */ + Your("%s %s.", xname(otmp), otense(otmp, "vibrate")); + if (otmp->spe >= -6) otmp->spe--; + make_stunned(HStun + rn1(10, 10), TRUE); + } + } + break; + case SCR_CONFUSE_MONSTER: + case SPE_CONFUSE_MONSTER: + if(youmonst.data->mlet != S_HUMAN || sobj->cursed) { + if(!HConfusion) You_feel("confused."); + make_confused(HConfusion + rnd(100),FALSE); + } else if(confused) { + if(!sobj->blessed) { + Your("%s begin to %s%s.", + makeplural(body_part(HAND)), + Blind ? "tingle" : "glow ", + Blind ? nul : hcolor(NH_PURPLE)); + make_confused(HConfusion + rnd(100),FALSE); + } else { + pline("A %s%s surrounds your %s.", + Blind ? nul : hcolor(NH_RED), + Blind ? "faint buzz" : " glow", + body_part(HEAD)); + make_confused(0L,TRUE); + } + } else { + if (!sobj->blessed) { + Your("%s%s %s%s.", + makeplural(body_part(HAND)), + Blind ? "" : " begin to glow", + Blind ? (const char *)"tingle" : hcolor(NH_RED), + u.umconf ? " even more" : ""); + u.umconf++; + } else { + if (Blind) + Your("%s tingle %s sharply.", + makeplural(body_part(HAND)), + u.umconf ? "even more" : "very"); + else + Your("%s glow a%s brilliant %s.", + makeplural(body_part(HAND)), + u.umconf ? "n even more" : "", + hcolor(NH_RED)); + /* after a while, repeated uses become less effective */ + if (u.umconf >= 40) + u.umconf++; + else + u.umconf += rn1(8, 2); + } + } + break; + case SCR_SCARE_MONSTER: + case SPE_CAUSE_FEAR: + { register int ct = 0; + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if(cansee(mtmp->mx,mtmp->my)) { + if(confused || sobj->cursed) { + mtmp->mflee = mtmp->mfrozen = mtmp->msleeping = 0; + mtmp->mcanmove = 1; + } else + if (! resist(mtmp, sobj->oclass, 0, NOTELL)) + monflee(mtmp, 0, FALSE, FALSE); + if(!mtmp->mtame) ct++; /* pets don't laugh at you */ + } + } + if(!ct) + You_hear("%s in the distance.", + (confused || sobj->cursed) ? "sad wailing" : + "maniacal laughter"); + else if(sobj->otyp == SCR_SCARE_MONSTER) + You_hear("%s close by.", + (confused || sobj->cursed) ? "sad wailing" : + "maniacal laughter"); + break; + } + case SCR_BLANK_PAPER: + if (Blind) + You("don't remember there being any magic words on this scroll."); + else + pline("This scroll seems to be blank."); + known = TRUE; + break; + case SCR_REMOVE_CURSE: + case SPE_REMOVE_CURSE: + { register struct obj *obj; + if(confused) + if (Hallucination) + You_feel("the power of the Force against you!"); + else + You_feel("like you need some help."); + else + if (Hallucination) + You_feel("in touch with the Universal Oneness."); + else + You_feel("like someone is helping you."); + + if (sobj->cursed) { + pline_The("scroll disintegrates."); + } else { + for (obj = invent; obj; obj = obj->nobj) { + long wornmask; +#ifdef GOLDOBJ + /* gold isn't subject to cursing and blessing */ + if (obj->oclass == COIN_CLASS) continue; +#endif + wornmask = (obj->owornmask & ~(W_BALL|W_ART|W_ARTI)); + if (wornmask && !sobj->blessed) { + /* handle a couple of special cases; we don't + allow auxiliary weapon slots to be used to + artificially increase number of worn items */ + if (obj == uswapwep) { + if (!u.twoweap) wornmask = 0L; + } else if (obj == uquiver) { + if (obj->oclass == WEAPON_CLASS) { + /* mergeable weapon test covers ammo, + missiles, spears, daggers & knives */ + if (!objects[obj->otyp].oc_merge) + wornmask = 0L; + } else if (obj->oclass == GEM_CLASS) { + /* possibly ought to check whether + alternate weapon is a sling... */ + if (!uslinging()) wornmask = 0L; + } else { + /* weptools don't merge and aren't + reasonable quivered weapons */ + wornmask = 0L; + } + } + } + if (sobj->blessed || wornmask || + obj->otyp == LOADSTONE || + (obj->otyp == LEASH && obj->leashmon)) { + if(confused) blessorcurse(obj, 2); + else uncurse(obj); + } + } + } + if(Punished && !confused) unpunish(); + update_inventory(); + break; + } + case SCR_CREATE_MONSTER: + case SPE_CREATE_MONSTER: + if (create_critters(1 + ((confused || sobj->cursed) ? 12 : 0) + + ((sobj->blessed || rn2(73)) ? 0 : rnd(4)), + confused ? &mons[PM_ACID_BLOB] : (struct permonst *)0)) + known = TRUE; + /* no need to flush monsters; we ask for identification only if the + * monsters are not visible + */ + break; + case SCR_ENCHANT_WEAPON: + if(uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)) + && confused) { + /* oclass check added 10/25/86 GAN */ + uwep->oerodeproof = !(sobj->cursed); + if (Blind) { + uwep->rknown = FALSE; + Your("weapon feels warm for a moment."); + } else { + uwep->rknown = TRUE; + Your("%s covered by a %s %s %s!", + aobjnam(uwep, "are"), + sobj->cursed ? "mottled" : "shimmering", + hcolor(sobj->cursed ? NH_PURPLE : NH_GOLDEN), + sobj->cursed ? "glow" : "shield"); + } + if (uwep->oerodeproof && (uwep->oeroded || uwep->oeroded2)) { + uwep->oeroded = uwep->oeroded2 = 0; + Your("%s as good as new!", + aobjnam(uwep, Blind ? "feel" : "look")); + } + } else return !chwepon(sobj, + sobj->cursed ? -1 : + !uwep ? 1 : + uwep->spe >= 9 ? (rn2(uwep->spe) == 0) : + sobj->blessed ? rnd(3-uwep->spe/3) : 1); + break; + case SCR_TAMING: + case SPE_CHARM_MONSTER: + if (u.uswallow) { + maybe_tame(u.ustuck, sobj); + } else { + int i, j, bd = confused ? 5 : 1; + struct monst *mtmp; + + for (i = -bd; i <= bd; i++) for(j = -bd; j <= bd; j++) { + if (!isok(u.ux + i, u.uy + j)) continue; + if ((mtmp = m_at(u.ux + i, u.uy + j)) != 0) + maybe_tame(mtmp, sobj); + } + } + break; + case SCR_GENOCIDE: + You("have found a scroll of genocide!"); + known = TRUE; + if (sobj->blessed) do_class_genocide(); + else do_genocide(!sobj->cursed | (2 * !!Confusion)); + break; + case SCR_LIGHT: + if(!Blind) known = TRUE; + litroom(!confused && !sobj->cursed, sobj); + break; + case SCR_TELEPORTATION: + if(confused || sobj->cursed) level_tele(); + else { + if (sobj->blessed && !Teleport_control) { + known = TRUE; + if (yn("Do you wish to teleport?")=='n') + break; + } + tele(); + if(Teleport_control || !couldsee(u.ux0, u.uy0) || + (distu(u.ux0, u.uy0) >= 16)) + known = TRUE; + } + break; + case SCR_GOLD_DETECTION: + if (confused || sobj->cursed) return(trap_detect(sobj)); + else return(gold_detect(sobj)); + case SCR_FOOD_DETECTION: + case SPE_DETECT_FOOD: + if (food_detect(sobj)) + return(1); /* nothing detected */ + break; + case SPE_IDENTIFY: + cval = rn2(5); + goto id; + case SCR_IDENTIFY: + /* known = TRUE; */ + if(confused) + You("identify this as an identify scroll."); + else + pline("This is an identify scroll."); + if (sobj->blessed || (!sobj->cursed && !rn2(5))) { + cval = rn2(5); + /* Note: if rn2(5)==0, identify all items */ + if (cval == 1 && sobj->blessed && Luck > 0) ++cval; + } else cval = 1; + if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10); + useup(sobj); + makeknown(SCR_IDENTIFY); + id: + if(invent && !confused) { + identify_pack(cval); + } + return(1); + case SCR_CHARGING: + if (confused) { + You_feel("charged up!"); + if (u.uen < u.uenmax) + u.uen = u.uenmax; + else + u.uen = (u.uenmax += d(5,4)); + flags.botl = 1; + break; + } + known = TRUE; + pline("This is a charging scroll."); + otmp = getobj(all_count, "charge"); + if (!otmp) break; + recharge(otmp, sobj->cursed ? -1 : (sobj->blessed ? 1 : 0)); + break; + case SCR_MAGIC_MAPPING: + if (level.flags.nommap) { + Your("mind is filled with crazy lines!"); + if (Hallucination) + pline("Wow! Modern art."); + else + Your("%s spins in bewilderment.", body_part(HEAD)); + make_confused(HConfusion + rnd(30), FALSE); + break; + } + if (sobj->blessed) { + register int x, y; + + for (x = 1; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + if (levl[x][y].typ == SDOOR) + cvt_sdoor_to_door(&levl[x][y]); + /* do_mapping() already reveals secret passages */ + } + known = TRUE; + case SPE_MAGIC_MAPPING: + if (level.flags.nommap) { + Your("%s spins as %s blocks the spell!", body_part(HEAD), something); + make_confused(HConfusion + rnd(30), FALSE); + break; + } + pline("A map coalesces in your mind!"); + cval = (sobj->cursed && !confused); + if(cval) HConfusion = 1; /* to screw up map */ + do_mapping(); + if(cval) { + HConfusion = 0; /* restore */ + pline("Unfortunately, you can't grasp the details."); + } + break; + case SCR_AMNESIA: + known = TRUE; + forget( (!sobj->blessed ? ALL_SPELLS : 0) | + (!confused || sobj->cursed ? ALL_MAP : 0) ); + if (Hallucination) /* Ommmmmm! */ + Your("mind releases itself from mundane concerns."); + else if (!strncmpi(plname, "Maud", 4)) + pline("As your mind turns inward on itself, you forget everything else."); + else if (rn2(2)) + pline("Who was that Maud person anyway?"); + else + pline("Thinking of Maud you forget everything else."); + exercise(A_WIS, FALSE); + break; + case SCR_FIRE: + /* + * Note: Modifications have been made as of 3.0 to allow for + * some damage under all potential cases. + */ + cval = bcsign(sobj); + if(!objects[sobj->otyp].oc_name_known) more_experienced(0,10); + useup(sobj); + makeknown(SCR_FIRE); + if(confused) { + if(Fire_resistance) { + shieldeff(u.ux, u.uy); + if(!Blind) + pline("Oh, look, what a pretty fire in your %s.", + makeplural(body_part(HAND))); + else You_feel("a pleasant warmth in your %s.", + makeplural(body_part(HAND))); + } else { + pline_The("scroll catches fire and you burn your %s.", + makeplural(body_part(HAND))); + losehp(1, "scroll of fire", KILLED_BY_AN); + } + return(1); + } + if (Underwater) + pline_The("water around you vaporizes violently!"); + else { + pline_The("scroll erupts in a tower of flame!"); + burn_away_slime(); + } + explode(u.ux, u.uy, 11, (2*(rn1(3, 3) + 2 * cval) + 1)/3, + SCROLL_CLASS, EXPL_FIERY); + return(1); + case SCR_EARTH: + /* TODO: handle steeds */ + if ( +#ifdef REINCARNATION + !Is_rogue_level(&u.uz) && +#endif + (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) { + register int x, y; + + /* Identify the scroll */ + pline_The("%s rumbles %s you!", ceiling(u.ux,u.uy), + sobj->blessed ? "around" : "above"); + known = 1; + if (In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + + /* Loop through the surrounding squares */ + if (!sobj->cursed) for (x = u.ux-1; x <= u.ux+1; x++) { + for (y = u.uy-1; y <= u.uy+1; y++) { + + /* Is this a suitable spot? */ + if (isok(x, y) && !closed_door(x, y) && + !IS_ROCK(levl[x][y].typ) && + !IS_AIR(levl[x][y].typ) && + (x != u.ux || y != u.uy)) { + register struct obj *otmp2; + register struct monst *mtmp; + + /* Make the object(s) */ + otmp2 = mksobj(confused ? ROCK : BOULDER, + FALSE, FALSE); + if (!otmp2) continue; /* Shouldn't happen */ + otmp2->quan = confused ? rn1(5,2) : 1; + otmp2->owt = weight(otmp2); + + /* Find the monster here (won't be player) */ + mtmp = m_at(x, y); + if (mtmp && !amorphous(mtmp->data) && + !passes_walls(mtmp->data) && + !noncorporeal(mtmp->data) && + !unsolid(mtmp->data)) { + struct obj *helmet = which_armor(mtmp, W_ARMH); + int mdmg; + + if (cansee(mtmp->mx, mtmp->my)) { + pline("%s is hit by %s!", Monnam(mtmp), + doname(otmp2)); + if (mtmp->minvis && !canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + } + mdmg = dmgval(otmp2, mtmp) * otmp2->quan; + if (helmet) { + if(is_metallic(helmet)) { + if (canspotmon(mtmp)) + pline("Fortunately, %s is wearing a hard helmet.", mon_nam(mtmp)); + else if (flags.soundok) + You_hear("a clanging sound."); + if (mdmg > 2) mdmg = 2; + } else { + if (canspotmon(mtmp)) + pline("%s's %s does not protect %s.", + Monnam(mtmp), xname(helmet), + mhim(mtmp)); + } + } + mtmp->mhp -= mdmg; + if (mtmp->mhp <= 0) + xkilled(mtmp, 1); + } + /* Drop the rock/boulder to the floor */ + if (!flooreffects(otmp2, x, y, "fall")) { + place_object(otmp2, x, y); + stackobj(otmp2); + newsym(x, y); /* map the rock */ + } + } + } + } + /* Attack the player */ + if (!sobj->blessed) { + int dmg; + struct obj *otmp2; + + /* Okay, _you_ write this without repeating the code */ + otmp2 = mksobj(confused ? ROCK : BOULDER, + FALSE, FALSE); + if (!otmp2) break; + otmp2->quan = confused ? rn1(5,2) : 1; + otmp2->owt = weight(otmp2); + if (!amorphous(youmonst.data) && + !Passes_walls && + !noncorporeal(youmonst.data) && + !unsolid(youmonst.data)) { + You("are hit by %s!", doname(otmp2)); + dmg = dmgval(otmp2, &youmonst) * otmp2->quan; + if (uarmh && !sobj->cursed) { + if(is_metallic(uarmh)) { + pline("Fortunately, you are wearing a hard helmet."); + if (dmg > 2) dmg = 2; + } else if (flags.verbose) { + Your("%s does not protect you.", + xname(uarmh)); + } + } + } else + dmg = 0; + /* Must be before the losehp(), for bones files */ + if (!flooreffects(otmp2, u.ux, u.uy, "fall")) { + place_object(otmp2, u.ux, u.uy); + stackobj(otmp2); + newsym(u.ux, u.uy); + } + if (dmg) losehp(dmg, "scroll of earth", KILLED_BY_AN); + } + } + break; + case SCR_PUNISHMENT: + known = TRUE; + if(confused || sobj->blessed) { + You_feel("guilty."); + break; + } + punish(sobj); + break; + case SCR_STINKING_CLOUD: { + coord cc; + + You("have found a scroll of stinking cloud!"); + known = TRUE; + pline("Where do you want to center the cloud?"); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the desired position") < 0) { + pline(Never_mind); + return 0; + } + if (!cansee(cc.x, cc.y) || distu(cc.x, cc.y) >= 32) { + You("smell rotten eggs."); + return 0; + } + (void) create_gas_cloud(cc.x, cc.y, 3+bcsign(sobj), + 8+4*bcsign(sobj)); + break; + } + default: + impossible("What weird effect is this? (%u)", sobj->otyp); + } + return(0); +} + +static void +wand_explode(obj) +register struct obj *obj; +{ + obj->in_use = TRUE; /* in case losehp() is fatal */ + Your("%s vibrates violently, and explodes!",xname(obj)); + nhbell(); + losehp(rnd(2*(u.uhpmax+1)/3), "exploding wand", KILLED_BY_AN); + useup(obj); + exercise(A_STR, FALSE); +} + +/* + * Low-level lit-field update routine. + */ +STATIC_PTR void +set_lit(x,y,val) +int x, y; +genericptr_t val; +{ + if (val) + levl[x][y].lit = 1; + else { + levl[x][y].lit = 0; + snuff_light_source(x, y); + } +} + +void +litroom(on,obj) +register boolean on; +struct obj *obj; +{ + char is_lit; /* value is irrelevant; we use its address + as a `not null' flag for set_lit() */ + + /* first produce the text (provided you're not blind) */ + if(!on) { + register struct obj *otmp; + + if (!Blind) { + if(u.uswallow) { + pline("It seems even darker in here than before."); + return; + } + if (uwep && artifact_light(uwep) && uwep->lamplit) + pline("Suddenly, the only light left comes from %s!", + the(xname(uwep))); + else + You("are surrounded by darkness!"); + } + + /* the magic douses lamps, et al, too */ + for(otmp = invent; otmp; otmp = otmp->nobj) + if (otmp->lamplit) + (void) snuff_lit(otmp); + if (Blind) goto do_it; + } else { + if (Blind) goto do_it; + if(u.uswallow){ + if (is_animal(u.ustuck->data)) + pline("%s %s is lit.", + s_suffix(Monnam(u.ustuck)), + mbodypart(u.ustuck, STOMACH)); + else + if (is_whirly(u.ustuck->data)) + pline("%s shines briefly.", + Monnam(u.ustuck)); + else + pline("%s glistens.", Monnam(u.ustuck)); + return; + } + pline("A lit field surrounds you!"); + } + +do_it: + /* No-op in water - can only see the adjacent squares and that's it! */ + if (Underwater || Is_waterlevel(&u.uz)) return; + /* + * If we are darkening the room and the hero is punished but not + * blind, then we have to pick up and replace the ball and chain so + * that we don't remember them if they are out of sight. + */ + if (Punished && !on && !Blind) + move_bc(1, 0, uball->ox, uball->oy, uchain->ox, uchain->oy); + +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { + /* Can't use do_clear_area because MAX_RADIUS is too small */ + /* rogue lighting must light the entire room */ + int rnum = levl[u.ux][u.uy].roomno - ROOMOFFSET; + int rx, ry; + if(rnum >= 0) { + for(rx = rooms[rnum].lx-1; rx <= rooms[rnum].hx+1; rx++) + for(ry = rooms[rnum].ly-1; ry <= rooms[rnum].hy+1; ry++) + set_lit(rx, ry, + (genericptr_t)(on ? &is_lit : (char *)0)); + rooms[rnum].rlit = on; + } + /* hallways remain dark on the rogue level */ + } else +#endif + do_clear_area(u.ux,u.uy, + (obj && obj->oclass==SCROLL_CLASS && obj->blessed) ? 9 : 5, + set_lit, (genericptr_t)(on ? &is_lit : (char *)0)); + + /* + * If we are not blind, then force a redraw on all positions in sight + * by temporarily blinding the hero. The vision recalculation will + * correctly update all previously seen positions *and* correctly + * set the waslit bit [could be messed up from above]. + */ + if (!Blind) { + vision_recalc(2); + + /* replace ball&chain */ + if (Punished && !on) + move_bc(0, 0, uball->ox, uball->oy, uchain->ox, uchain->oy); + } + + vision_full_recalc = 1; /* delayed vision recalculation */ +} + +static void +do_class_genocide() +{ + int i, j, immunecnt, gonecnt, goodcnt, class, feel_dead = 0; + char buf[BUFSZ]; + boolean gameover = FALSE; /* true iff killed self */ + + for(j=0; ; j++) { + if (j >= 5) { + pline(thats_enough_tries); + return; + } + do { + getlin("What class of monsters do you wish to genocide?", + buf); + (void)mungspaces(buf); + } while (buf[0]=='\033' || !buf[0]); + /* choosing "none" preserves genocideless conduct */ + if (!strcmpi(buf, "none") || + !strcmpi(buf, "nothing")) return; + + if (strlen(buf) == 1) { + if (buf[0] == ILLOBJ_SYM) + buf[0] = def_monsyms[S_MIMIC]; + class = def_char_to_monclass(buf[0]); + } else { + char buf2[BUFSZ]; + + class = 0; + Strcpy(buf2, makesingular(buf)); + Strcpy(buf, buf2); + } + immunecnt = gonecnt = goodcnt = 0; + for (i = LOW_PM; i < NUMMONS; i++) { + if (class == 0 && + strstri(monexplain[(int)mons[i].mlet], buf) != 0) + class = mons[i].mlet; + if (mons[i].mlet == class) { + if (!(mons[i].geno & G_GENO)) immunecnt++; + else if(mvitals[i].mvflags & G_GENOD) gonecnt++; + else goodcnt++; + } + } + /* + * TODO[?]: If user's input doesn't match any class + * description, check individual species names. + */ + if (!goodcnt && class != mons[urole.malenum].mlet && + class != mons[urace.malenum].mlet) { + if (gonecnt) + pline("All such monsters are already nonexistent."); + else if (immunecnt || + (buf[0] == DEF_INVISIBLE && buf[1] == '\0')) + You("aren't permitted to genocide such monsters."); + else +#ifdef WIZARD /* to aid in topology testing; remove pesky monsters */ + if (wizard && buf[0] == '*') { + register struct monst *mtmp, *mtmp2; + + gonecnt = 0; + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (DEADMONSTER(mtmp)) continue; + mongone(mtmp); + gonecnt++; + } + pline("Eliminated %d monster%s.", gonecnt, plur(gonecnt)); + return; + } else +#endif + pline("That symbol does not represent any monster."); + continue; + } + + for (i = LOW_PM; i < NUMMONS; i++) { + if(mons[i].mlet == class) { + char nam[BUFSZ]; + + Strcpy(nam, makeplural(mons[i].mname)); + /* Although "genus" is Latin for race, the hero benefits + * from both race and role; thus genocide affects either. + */ + if (Your_Own_Role(i) || Your_Own_Race(i) || + ((mons[i].geno & G_GENO) + && !(mvitals[i].mvflags & G_GENOD))) { + /* This check must be first since player monsters might + * have G_GENOD or !G_GENO. + */ + mvitals[i].mvflags |= (G_GENOD|G_NOCORPSE); + reset_rndmonst(i); + kill_genocided_monsters(); + update_inventory(); /* eggs & tins */ + pline("Wiped out all %s.", nam); + if (Upolyd && i == u.umonnum) { + u.mh = -1; + if (Unchanging) { + if (!feel_dead++) You("die."); + /* finish genociding this class of + monsters before ultimately dying */ + gameover = TRUE; + } else + rehumanize(); + } + /* Self-genocide if it matches either your race + or role. Assumption: male and female forms + share same monster class. */ + if (i == urole.malenum || i == urace.malenum) { + u.uhp = -1; + if (Upolyd) { + if (!feel_dead++) You_feel("dead inside."); + } else { + if (!feel_dead++) You("die."); + gameover = TRUE; + } + } + } else if (mvitals[i].mvflags & G_GENOD) { + if (!gameover) + pline("All %s are already nonexistent.", nam); + } else if (!gameover) { + /* suppress feedback about quest beings except + for those applicable to our own role */ + if ((mons[i].msound != MS_LEADER || + quest_info(MS_LEADER) == i) + && (mons[i].msound != MS_NEMESIS || + quest_info(MS_NEMESIS) == i) + && (mons[i].msound != MS_GUARDIAN || + quest_info(MS_GUARDIAN) == i) + /* non-leader/nemesis/guardian role-specific monster */ + && (i != PM_NINJA || /* nuisance */ + Role_if(PM_SAMURAI))) { + boolean named, uniq; + + named = type_is_pname(&mons[i]) ? TRUE : FALSE; + uniq = (mons[i].geno & G_UNIQ) ? TRUE : FALSE; + /* one special case */ + if (i == PM_HIGH_PRIEST) uniq = FALSE; + + You("aren't permitted to genocide %s%s.", + (uniq && !named) ? "the " : "", + (uniq || named) ? mons[i].mname : nam); + } + } + } + } + if (gameover || u.uhp == -1) { + killer_format = KILLED_BY_AN; + killer = "scroll of genocide"; + if (gameover) done(GENOCIDED); + } + return; + } +} + +#define REALLY 1 +#define PLAYER 2 +#define ONTHRONE 4 +void +do_genocide(how) +int how; +/* 0 = no genocide; create monsters (cursed scroll) */ +/* 1 = normal genocide */ +/* 3 = forced genocide of player */ +/* 5 (4 | 1) = normal genocide from throne */ +{ + char buf[BUFSZ]; + register int i, killplayer = 0; + register int mndx; + register struct permonst *ptr; + const char *which; + + if (how & PLAYER) { + mndx = u.umonster; /* non-polymorphed mon num */ + ptr = &mons[mndx]; + Strcpy(buf, ptr->mname); + killplayer++; + } else { + for(i = 0; ; i++) { + if(i >= 5) { + pline(thats_enough_tries); + return; + } + getlin("What monster do you want to genocide? [type the name]", + buf); + (void)mungspaces(buf); + /* choosing "none" preserves genocideless conduct */ + if (!strcmpi(buf, "none") || !strcmpi(buf, "nothing")) { + /* ... but no free pass if cursed */ + if (!(how & REALLY)) { + ptr = rndmonst(); + if (!ptr) return; /* no message, like normal case */ + mndx = monsndx(ptr); + break; /* remaining checks don't apply */ + } else return; + } + + mndx = name_to_mon(buf); + if (mndx == NON_PM || (mvitals[mndx].mvflags & G_GENOD)) { + pline("Such creatures %s exist in this world.", + (mndx == NON_PM) ? "do not" : "no longer"); + continue; + } + ptr = &mons[mndx]; + /* Although "genus" is Latin for race, the hero benefits + * from both race and role; thus genocide affects either. + */ + if (Your_Own_Role(mndx) || Your_Own_Race(mndx)) { + killplayer++; + break; + } + if (is_human(ptr)) adjalign(-sgn(u.ualign.type)); + if (is_demon(ptr)) adjalign(sgn(u.ualign.type)); + + if(!(ptr->geno & G_GENO)) { + if(flags.soundok) { + /* fixme: unconditional "caverns" will be silly in some circumstances */ + if(flags.verbose) + pline("A thunderous voice booms through the caverns:"); + verbalize("No, mortal! That will not be done."); + } + continue; + } + /* KMH -- Unchanging prevents rehumanization */ + if (Unchanging && ptr == youmonst.data) + killplayer++; + break; + } + } + + which = "all "; + if (Hallucination) { + if (Upolyd) + Strcpy(buf,youmonst.data->mname); + else { + Strcpy(buf, (flags.female && urole.name.f) ? + urole.name.f : urole.name.m); + buf[0] = lowc(buf[0]); + } + } else { + Strcpy(buf, ptr->mname); /* make sure we have standard singular */ + if ((ptr->geno & G_UNIQ) && ptr != &mons[PM_HIGH_PRIEST]) + which = !type_is_pname(ptr) ? "the " : ""; + } + if (how & REALLY) { + /* setting no-corpse affects wishing and random tin generation */ + mvitals[mndx].mvflags |= (G_GENOD | G_NOCORPSE); + pline("Wiped out %s%s.", which, + (*which != 'a') ? buf : makeplural(buf)); + + if (killplayer) { + /* might need to wipe out dual role */ + if (urole.femalenum != NON_PM && mndx == urole.malenum) + mvitals[urole.femalenum].mvflags |= (G_GENOD | G_NOCORPSE); + if (urole.femalenum != NON_PM && mndx == urole.femalenum) + mvitals[urole.malenum].mvflags |= (G_GENOD | G_NOCORPSE); + if (urace.femalenum != NON_PM && mndx == urace.malenum) + mvitals[urace.femalenum].mvflags |= (G_GENOD | G_NOCORPSE); + if (urace.femalenum != NON_PM && mndx == urace.femalenum) + mvitals[urace.malenum].mvflags |= (G_GENOD | G_NOCORPSE); + + u.uhp = -1; + if (how & PLAYER) { + killer_format = KILLED_BY; + killer = "genocidal confusion"; + } else if (how & ONTHRONE) { + /* player selected while on a throne */ + killer_format = KILLED_BY_AN; + killer = "imperious order"; + } else { /* selected player deliberately, not confused */ + killer_format = KILLED_BY_AN; + killer = "scroll of genocide"; + } + + /* Polymorphed characters will die as soon as they're rehumanized. */ + /* KMH -- Unchanging prevents rehumanization */ + if (Upolyd && ptr != youmonst.data) { + delayed_killer = killer; + killer = 0; + You_feel("dead inside."); + } else + done(GENOCIDED); + } else if (ptr == youmonst.data) { + rehumanize(); + } + reset_rndmonst(mndx); + kill_genocided_monsters(); + update_inventory(); /* in case identified eggs were affected */ + } else { + int cnt = 0; + + if (!(mons[mndx].geno & G_UNIQ) && + !(mvitals[mndx].mvflags & (G_GENOD | G_EXTINCT))) + for (i = rn1(3, 4); i > 0; i--) { + if (!makemon(ptr, u.ux, u.uy, NO_MINVENT)) + break; /* couldn't make one */ + ++cnt; + if (mvitals[mndx].mvflags & G_EXTINCT) + break; /* just made last one */ + } + if (cnt) + pline("Sent in some %s.", makeplural(buf)); + else + pline(nothing_happens); + } +} + +void +punish(sobj) +register struct obj *sobj; +{ + /* KMH -- Punishment is still okay when you are riding */ + You("are being punished for your misbehavior!"); + if(Punished){ + Your("iron ball gets heavier."); + uball->owt += 160 * (1 + sobj->cursed); + return; + } + if (amorphous(youmonst.data) || is_whirly(youmonst.data) || unsolid(youmonst.data)) { + pline("A ball and chain appears, then falls away."); + dropy(mkobj(BALL_CLASS, TRUE)); + return; + } + setworn(mkobj(CHAIN_CLASS, TRUE), W_CHAIN); + setworn(mkobj(BALL_CLASS, TRUE), W_BALL); + uball->spe = 1; /* special ball (see save) */ + + /* + * Place ball & chain if not swallowed. If swallowed, the ball & + * chain variables will be set at the next call to placebc(). + */ + if (!u.uswallow) { + placebc(); + if (Blind) set_bc(1); /* set up ball and chain variables */ + newsym(u.ux,u.uy); /* see ball&chain if can't see self */ + } +} + +void +unpunish() +{ /* remove the ball and chain */ + struct obj *savechain = uchain; + + obj_extract_self(uchain); + newsym(uchain->ox,uchain->oy); + setworn((struct obj *)0, W_CHAIN); + dealloc_obj(savechain); + uball->spe = 0; + setworn((struct obj *)0, W_BALL); +} + +/* some creatures have special data structures that only make sense in their + * normal locations -- if the player tries to create one elsewhere, or to revive + * one, the disoriented creature becomes a zombie + */ +boolean +cant_create(mtype, revival) +int *mtype; +boolean revival; +{ + + /* SHOPKEEPERS can be revived now */ + if (*mtype==PM_GUARD || (*mtype==PM_SHOPKEEPER && !revival) + || *mtype==PM_ALIGNED_PRIEST || *mtype==PM_ANGEL) { + *mtype = PM_HUMAN_ZOMBIE; + return TRUE; + } else if (*mtype==PM_LONG_WORM_TAIL) { /* for create_particular() */ + *mtype = PM_LONG_WORM; + return TRUE; + } + return FALSE; +} + +#ifdef WIZARD +/* + * Make a new monster with the type controlled by the user. + * + * Note: when creating a monster by class letter, specifying the + * "strange object" (']') symbol produces a random monster rather + * than a mimic; this behavior quirk is useful so don't "fix" it... + */ +boolean +create_particular() +{ + char buf[BUFSZ], *bufp, monclass = MAXMCLASSES; + int which, tries, i; + struct permonst *whichpm; + struct monst *mtmp; + boolean madeany = FALSE; + boolean maketame, makepeaceful, makehostile; + + tries = 0; + do { + which = urole.malenum; /* an arbitrary index into mons[] */ + maketame = makepeaceful = makehostile = FALSE; + getlin("Create what kind of monster? [type the name or symbol]", + buf); + bufp = mungspaces(buf); + if (*bufp == '\033') return FALSE; + /* allow the initial disposition to be specified */ + if (!strncmpi(bufp, "tame ", 5)) { + bufp += 5; + maketame = TRUE; + } else if (!strncmpi(bufp, "peaceful ", 9)) { + bufp += 9; + makepeaceful = TRUE; + } else if (!strncmpi(bufp, "hostile ", 8)) { + bufp += 8; + makehostile = TRUE; + } + /* decide whether a valid monster was chosen */ + if (strlen(bufp) == 1) { + monclass = def_char_to_monclass(*bufp); + if (monclass != MAXMCLASSES) break; /* got one */ + } else { + which = name_to_mon(bufp); + if (which >= LOW_PM) break; /* got one */ + } + /* no good; try again... */ + pline("I've never heard of such monsters."); + } while (++tries < 5); + + if (tries == 5) { + pline(thats_enough_tries); + } else { + (void) cant_create(&which, FALSE); + whichpm = &mons[which]; + for (i = 0; i <= multi; i++) { + if (monclass != MAXMCLASSES) + whichpm = mkclass(monclass, 0); + if (maketame) { + mtmp = makemon(whichpm, u.ux, u.uy, MM_EDOG); + if (mtmp) { + initedog(mtmp); + set_malign(mtmp); + } + } else { + mtmp = makemon(whichpm, u.ux, u.uy, NO_MM_FLAGS); + if ((makepeaceful || makehostile) && mtmp) { + mtmp->mtame = 0; /* sanity precaution */ + mtmp->mpeaceful = makepeaceful ? 1 : 0; + set_malign(mtmp); + } + } + if (mtmp) madeany = TRUE; + } + } + return madeany; +} +#endif /* WIZARD */ + +#endif /* OVLB */ + +/*read.c*/ diff --git a/src/rect.c b/src/rect.c new file mode 100644 index 0000000..dc6be73 --- /dev/null +++ b/src/rect.c @@ -0,0 +1,194 @@ +/* SCCS Id: @(#)rect.c 3.4 1990/02/22 */ +/* Copyright (c) 1990 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +int FDECL(get_rect_ind, (NhRect *)); + +static boolean FDECL(intersect, (NhRect *,NhRect *,NhRect *)); + + /* + * In this file, we will handle the various rectangle functions we + * need for room generation. + */ + +#define MAXRECT 50 +#define XLIM 4 +#define YLIM 3 + +static NhRect rect[MAXRECT+1]; +static int rect_cnt; + +/* + * Initialisation of internal structures. Should be called for every + * new level to be build... + */ + +void +init_rect() +{ + rect_cnt = 1; + rect[0].lx = rect[0].ly = 0; + rect[0].hx = COLNO - 1; + rect[0].hy = ROWNO - 1; +} + +/* + * Search Index of one precise NhRect. + * + */ + +int +get_rect_ind(r) +NhRect *r; +{ + register NhRect *rectp; + register int lx, ly, hx, hy; + register int i; + + lx = r->lx; ly = r->ly; + hx = r->hx; hy = r->hy; + for (i=0,rectp = &rect[0];ilx && ly == rectp->ly && + hx == rectp->hx && hy == rectp->hy) + return i; + return -1; +} + +/* + * Search a free rectangle that include the one given in arg + */ + +NhRect * +get_rect(r) +NhRect *r; +{ + register NhRect *rectp; + register int lx, ly, hx, hy; + register int i; + + lx = r->lx; ly = r->ly; + hx = r->hx; hy = r->hy; + for (i=0,rectp = &rect[0];i= rectp->lx && ly >= rectp->ly && + hx <= rectp->hx && hy <= rectp->hy) + return rectp; + return 0; +} + +/* + * Get some random NhRect from the list. + */ + +NhRect * +rnd_rect() +{ + return rect_cnt > 0 ? &rect[rn2(rect_cnt)] : 0; +} + +/* + * Search intersection between two rectangles (r1 & r2). + * return TRUE if intersection exist and put it in r3. + * otherwise returns FALSE + */ + +static boolean +intersect(r1, r2, r3) +NhRect *r1, *r2, *r3; +{ + if (r2->lx > r1->hx || r2->ly > r1->hy || + r2->hx < r1->lx || r2->hy < r1->ly) + return FALSE; + + r3->lx = (r2->lx > r1->lx ? r2->lx : r1->lx); + r3->ly = (r2->ly > r1->ly ? r2->ly : r1->ly); + r3->hx = (r2->hx > r1->hx ? r1->hx : r2->hx); + r3->hy = (r2->hy > r1->hy ? r1->hy : r2->hy); + + if (r3->lx > r3->hx || r3->ly > r3->hy) + return FALSE; + return TRUE; +} + +/* + * Remove a rectangle from the list of free NhRect. + */ + +void +remove_rect(r) +NhRect *r; +{ + int ind; + + ind = get_rect_ind(r); + if ( ind >=0 ) + rect[ind] = rect[--rect_cnt]; +} + +/* + * Add a NhRect to the list. + */ + +void +add_rect(r) +NhRect *r; +{ + if (rect_cnt >= MAXRECT) { +#ifdef WIZARD + if (wizard) pline("MAXRECT may be too small."); +#endif + return; + } + /* Check that this NhRect is not included in another one */ + if (get_rect(r)) + return; + rect[rect_cnt] = *r; + rect_cnt++; +} + +/* + * Okay, here we have two rectangles (r1 & r2). + * r1 was already in the list and r2 is included in r1. + * What we want is to allocate r2, that is split r1 into smaller rectangles + * then remove it. + */ + +void +split_rects(r1, r2) +NhRect *r1, *r2; +{ + NhRect r, old_r; + int i; + + old_r = *r1; + remove_rect(r1); + + /* Walk down since rect_cnt & rect[] will change... */ + for (i=rect_cnt-1; i>=0; i--) + if (intersect(&rect[i], r2, &r)) + split_rects(&rect[i], &r); + + if (r2->ly - old_r.ly-1 > (old_r.hy < ROWNO - 1 ? 2*YLIM : YLIM+1)+4) { + r = old_r; + r.hy = r2->ly - 2; + add_rect(&r); + } + if (r2->lx - old_r.lx-1 > (old_r.hx < COLNO - 1 ? 2*XLIM : XLIM+1)+4) { + r = old_r; + r.hx = r2->lx - 2; + add_rect(&r); + } + if (old_r.hy - r2->hy-1 > (old_r.ly > 0 ? 2*YLIM : YLIM+1)+4) { + r = old_r; + r.ly = r2->hy + 2; + add_rect(&r); + } + if (old_r.hx - r2->hx-1 > (old_r.lx > 0 ? 2*XLIM : XLIM+1)+4) { + r = old_r; + r.lx = r2->hx + 2; + add_rect(&r); + } +} + +/*rect.c*/ diff --git a/src/region.c b/src/region.c new file mode 100644 index 0000000..e4d12af --- /dev/null +++ b/src/region.c @@ -0,0 +1,985 @@ +/* SCCS Id: @(#)region.c 3.4 2002/10/15 */ +/* Copyright (c) 1996 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" + +/* + * This should really go into the level structure, but + * I'll start here for ease. It *WILL* move into the level + * structure eventually. + */ + +static NhRegion **regions; +static int n_regions = 0; +static int max_regions = 0; + +#define NO_CALLBACK (-1) + +boolean FDECL(inside_gas_cloud, (genericptr,genericptr)); +boolean FDECL(expire_gas_cloud, (genericptr,genericptr)); +boolean FDECL(inside_rect, (NhRect *,int,int)); +boolean FDECL(inside_region, (NhRegion *,int,int)); +NhRegion *FDECL(create_region, (NhRect *,int)); +void FDECL(add_rect_to_reg, (NhRegion *,NhRect *)); +void FDECL(add_mon_to_reg, (NhRegion *,struct monst *)); +void FDECL(remove_mon_from_reg, (NhRegion *,struct monst *)); +boolean FDECL(mon_in_region, (NhRegion *,struct monst *)); + +#if 0 +NhRegion *FDECL(clone_region, (NhRegion *)); +#endif +void FDECL(free_region, (NhRegion *)); +void FDECL(add_region, (NhRegion *)); +void FDECL(remove_region, (NhRegion *)); + +#if 0 +void FDECL(replace_mon_regions, (struct monst *,struct monst *)); +void FDECL(remove_mon_from_regions, (struct monst *)); +NhRegion *FDECL(create_msg_region, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P, + const char *,const char *)); +boolean FDECL(enter_force_field, (genericptr,genericptr)); +NhRegion *FDECL(create_force_field, (XCHAR_P,XCHAR_P,int,int)); +#endif + +static void FDECL(reset_region_mids, (NhRegion *)); + +static callback_proc callbacks[] = { +#define INSIDE_GAS_CLOUD 0 + inside_gas_cloud, +#define EXPIRE_GAS_CLOUD 1 + expire_gas_cloud +}; + +/* Should be inlined. */ +boolean +inside_rect(r, x, y) +NhRect *r; +int x, y; +{ + return (x >= r->lx && x <= r->hx && y >= r->ly && y <= r->hy); +} + +/* + * Check if a point is inside a region. + */ +boolean +inside_region(reg, x, y) +NhRegion *reg; +int x, y; +{ + int i; + + if (reg == NULL || !inside_rect(&(reg->bounding_box), x, y)) + return FALSE; + for (i = 0; i < reg->nrects; i++) + if (inside_rect(&(reg->rects[i]), x, y)) + return TRUE; + return FALSE; +} + +/* + * Create a region. It does not activate it. + */ +NhRegion * +create_region(rects, nrect) +NhRect *rects; +int nrect; +{ + int i; + NhRegion *reg; + + reg = (NhRegion *) alloc(sizeof (NhRegion)); + /* Determines bounding box */ + if (nrect > 0) { + reg->bounding_box = rects[0]; + } else { + reg->bounding_box.lx = 99; + reg->bounding_box.ly = 99; + reg->bounding_box.hx = 0; + reg->bounding_box.hy = 0; + } + reg->nrects = nrect; + reg->rects = nrect > 0 ? (NhRect *)alloc((sizeof (NhRect)) * nrect) : NULL; + for (i = 0; i < nrect; i++) { + if (rects[i].lx < reg->bounding_box.lx) + reg->bounding_box.lx = rects[i].lx; + if (rects[i].ly < reg->bounding_box.ly) + reg->bounding_box.ly = rects[i].ly; + if (rects[i].hx > reg->bounding_box.hx) + reg->bounding_box.hx = rects[i].hx; + if (rects[i].hy > reg->bounding_box.hy) + reg->bounding_box.hy = rects[i].hy; + reg->rects[i] = rects[i]; + } + reg->ttl = -1; /* Defaults */ + reg->attach_2_u = FALSE; + reg->attach_2_m = 0; + /* reg->attach_2_o = NULL; */ + reg->enter_msg = NULL; + reg->leave_msg = NULL; + reg->expire_f = NO_CALLBACK; + reg->enter_f = NO_CALLBACK; + reg->can_enter_f = NO_CALLBACK; + reg->leave_f = NO_CALLBACK; + reg->can_leave_f = NO_CALLBACK; + reg->inside_f = NO_CALLBACK; + clear_hero_inside(reg); + clear_heros_fault(reg); + reg->n_monst = 0; + reg->max_monst = 0; + reg->monsters = NULL; + reg->arg = NULL; + return reg; +} + +/* + * Add rectangle to region. + */ +void +add_rect_to_reg(reg, rect) +NhRegion *reg; +NhRect *rect; +{ + NhRect *tmp_rect; + + tmp_rect = (NhRect *) alloc(sizeof (NhRect) * (reg->nrects + 1)); + if (reg->nrects > 0) { + (void) memcpy((genericptr_t) tmp_rect, (genericptr_t) reg->rects, + (sizeof (NhRect) * reg->nrects)); + free((genericptr_t) reg->rects); + } + tmp_rect[reg->nrects] = *rect; + reg->nrects++; + reg->rects = tmp_rect; + /* Update bounding box if needed */ + if (reg->bounding_box.lx > rect->lx) + reg->bounding_box.lx = rect->lx; + if (reg->bounding_box.ly > rect->ly) + reg->bounding_box.ly = rect->ly; + if (reg->bounding_box.hx < rect->hx) + reg->bounding_box.hx = rect->hx; + if (reg->bounding_box.hy < rect->hy) + reg->bounding_box.hy = rect->hy; +} + +/* + * Add a monster to the region + */ +void +add_mon_to_reg(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + int i; + unsigned *tmp_m; + + if (reg->max_monst <= reg->n_monst) { + tmp_m = (unsigned *) + alloc(sizeof (unsigned) * (reg->max_monst + MONST_INC)); + if (reg->max_monst > 0) { + for (i = 0; i < reg->max_monst; i++) + tmp_m[i] = reg->monsters[i]; + free((genericptr_t) reg->monsters); + } + reg->monsters = tmp_m; + reg->max_monst += MONST_INC; + } + reg->monsters[reg->n_monst++] = mon->m_id; +} + +/* + * Remove a monster from the region list (it left or died...) + */ +void +remove_mon_from_reg(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + register int i; + + for (i = 0; i < reg->n_monst; i++) + if (reg->monsters[i] == mon->m_id) { + reg->n_monst--; + reg->monsters[i] = reg->monsters[reg->n_monst]; + return; + } +} + +/* + * Check if a monster is inside the region. + * It's probably quicker to check with the region internal list + * than to check for coordinates. + */ +boolean +mon_in_region(reg, mon) +NhRegion *reg; +struct monst *mon; +{ + int i; + + for (i = 0; i < reg->n_monst; i++) + if (reg->monsters[i] == mon->m_id) + return TRUE; + return FALSE; +} + +#if 0 +/* not yet used */ + +/* + * Clone (make a standalone copy) the region. + */ +NhRegion * +clone_region(reg) +NhRegion *reg; +{ + NhRegion *ret_reg; + + ret_reg = create_region(reg->rects, reg->nrects); + ret_reg->ttl = reg->ttl; + ret_reg->attach_2_u = reg->attach_2_u; + ret_reg->attach_2_m = reg->attach_2_m; + /* ret_reg->attach_2_o = reg->attach_2_o; */ + ret_reg->expire_f = reg->expire_f; + ret_reg->enter_f = reg->enter_f; + ret_reg->can_enter_f = reg->can_enter_f; + ret_reg->leave_f = reg->leave_f; + ret_reg->can_leave_f = reg->can_leave_f; + ret_reg->player_flags = reg->player_flags; /* set/clear_hero_inside,&c*/ + ret_reg->n_monst = reg->n_monst; + if (reg->n_monst > 0) { + ret_reg->monsters = (unsigned *) + alloc((sizeof (unsigned)) * reg->n_monst); + (void) memcpy((genericptr_t) ret_reg->monsters, (genericptr_t) reg->monsters, + sizeof (unsigned) * reg->n_monst); + } else + ret_reg->monsters = NULL; + return ret_reg; +} + +#endif /*0*/ + +/* + * Free mem from region. + */ +void +free_region(reg) +NhRegion *reg; +{ + if (reg) { + if (reg->rects) + free((genericptr_t) reg->rects); + if (reg->monsters) + free((genericptr_t) reg->monsters); + free((genericptr_t) reg); + } +} + +/* + * Add a region to the list. + * This actually activates the region. + */ +void +add_region(reg) +NhRegion *reg; +{ + NhRegion **tmp_reg; + int i, j; + + if (max_regions <= n_regions) { + tmp_reg = regions; + regions = (NhRegion **)alloc(sizeof (NhRegion *) * (max_regions + 10)); + if (max_regions > 0) { + (void) memcpy((genericptr_t) regions, (genericptr_t) tmp_reg, + max_regions * sizeof (NhRegion *)); + free((genericptr_t) tmp_reg); + } + max_regions += 10; + } + regions[n_regions] = reg; + n_regions++; + /* Check for monsters inside the region */ + for (i = reg->bounding_box.lx; i <= reg->bounding_box.hx; i++) + for (j = reg->bounding_box.ly; j <= reg->bounding_box.hy; j++) { + /* Some regions can cross the level boundaries */ + if (!isok(i,j)) + continue; + if (MON_AT(i, j) && inside_region(reg, i, j)) + add_mon_to_reg(reg, level.monsters[i][j]); + if (reg->visible && cansee(i, j)) + newsym(i, j); + } + /* Check for player now... */ + if (inside_region(reg, u.ux, u.uy)) + set_hero_inside(reg); + else + clear_hero_inside(reg); +} + +/* + * Remove a region from the list & free it. + */ +void +remove_region(reg) +NhRegion *reg; +{ + register int i, x, y; + + for (i = 0; i < n_regions; i++) + if (regions[i] == reg) + break; + if (i == n_regions) + return; + + /* Update screen if necessary */ + if (reg->visible) + for (x = reg->bounding_box.lx; x <= reg->bounding_box.hx; x++) + for (y = reg->bounding_box.ly; y <= reg->bounding_box.hy; y++) + if (isok(x,y) && inside_region(reg, x, y) && cansee(x, y)) + newsym(x, y); + + free_region(reg); + regions[i] = regions[n_regions - 1]; + regions[n_regions - 1] = (NhRegion *) 0; + n_regions--; +} + +/* + * Remove all regions and clear all related data (This must be down + * when changing level, for instance). + */ +void +clear_regions() +{ + register int i; + + for (i = 0; i < n_regions; i++) + free_region(regions[i]); + n_regions = 0; + if (max_regions > 0) + free((genericptr_t) regions); + max_regions = 0; + regions = NULL; +} + +/* + * This function is called every turn. + * It makes the regions age, if necessary and calls the appropriate + * callbacks when needed. + */ +void +run_regions() +{ + register int i, j, k; + int f_indx; + + /* End of life ? */ + /* Do it backward because the array will be modified */ + for (i = n_regions - 1; i >= 0; i--) { + if (regions[i]->ttl == 0) { + if ((f_indx = regions[i]->expire_f) == NO_CALLBACK || + (*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + remove_region(regions[i]); + } + } + + /* Process remaining regions */ + for (i = 0; i < n_regions; i++) { + /* Make the region age */ + if (regions[i]->ttl > 0) + regions[i]->ttl--; + /* Check if player is inside region */ + f_indx = regions[i]->inside_f; + if (f_indx != NO_CALLBACK && hero_inside(regions[i])) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + /* Check if any monster is inside region */ + if (f_indx != NO_CALLBACK) { + for (j = 0; j < regions[i]->n_monst; j++) { + struct monst *mtmp = find_mid(regions[i]->monsters[j], FM_FMON); + + if (!mtmp || mtmp->mhp <= 0 || + (*callbacks[f_indx])(regions[i], mtmp)) { + /* The monster died, remove it from list */ + k = (regions[i]->n_monst -= 1); + regions[i]->monsters[j] = regions[i]->monsters[k]; + regions[i]->monsters[k] = 0; + --j; /* current slot has been reused; recheck it next */ + } + } + } + } +} + +/* + * check whether player enters/leaves one or more regions. + */ +boolean +in_out_region(x, y) +xchar + x, y; +{ + int i, f_indx; + + /* First check if we can do the move */ + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], x, y) + && !hero_inside(regions[i]) && !regions[i]->attach_2_u) { + if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + return FALSE; + } else + if (hero_inside(regions[i]) + && !inside_region(regions[i], x, y) + && !regions[i]->attach_2_u) { + if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], (genericptr_t) 0)) + return FALSE; + } + } + + /* Callbacks for the regions we do leave */ + for (i = 0; i < n_regions; i++) + if (hero_inside(regions[i]) && + !regions[i]->attach_2_u && !inside_region(regions[i], x, y)) { + clear_hero_inside(regions[i]); + if (regions[i]->leave_msg != NULL) + pline(regions[i]->leave_msg); + if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + } + + /* Callbacks for the regions we do enter */ + for (i = 0; i < n_regions; i++) + if (!hero_inside(regions[i]) && + !regions[i]->attach_2_u && inside_region(regions[i], x, y)) { + set_hero_inside(regions[i]); + if (regions[i]->enter_msg != NULL) + pline(regions[i]->enter_msg); + if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], (genericptr_t) 0); + } + return TRUE; +} + +/* + * check wether a monster enters/leaves one or more region. +*/ +boolean +m_in_out_region(mon, x, y) +struct monst *mon; +xchar x, y; +{ + int i, f_indx; + + /* First check if we can do the move */ + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], x, y) && + !mon_in_region(regions[i], mon) && + regions[i]->attach_2_m != mon->m_id) { + if ((f_indx = regions[i]->can_enter_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], mon)) + return FALSE; + } else if (mon_in_region(regions[i], mon) && + !inside_region(regions[i], x, y) && + regions[i]->attach_2_m != mon->m_id) { + if ((f_indx = regions[i]->can_leave_f) != NO_CALLBACK) + if (!(*callbacks[f_indx])(regions[i], mon)) + return FALSE; + } + } + + /* Callbacks for the regions we do leave */ + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], mon) && + regions[i]->attach_2_m != mon->m_id && + !inside_region(regions[i], x, y)) { + remove_mon_from_reg(regions[i], mon); + if ((f_indx = regions[i]->leave_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], mon); + } + + /* Callbacks for the regions we do enter */ + for (i = 0; i < n_regions; i++) + if (!hero_inside(regions[i]) && + !regions[i]->attach_2_u && inside_region(regions[i], x, y)) { + add_mon_to_reg(regions[i], mon); + if ((f_indx = regions[i]->enter_f) != NO_CALLBACK) + (void) (*callbacks[f_indx])(regions[i], mon); + } + return TRUE; +} + +/* + * Checks player's regions after a teleport for instance. + */ +void +update_player_regions() +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (!regions[i]->attach_2_u && inside_region(regions[i], u.ux, u.uy)) + set_hero_inside(regions[i]); + else + clear_hero_inside(regions[i]); +} + +/* + * Ditto for a specified monster. + */ +void +update_monster_region(mon) +struct monst *mon; +{ + register int i; + + for (i = 0; i < n_regions; i++) { + if (inside_region(regions[i], mon->mx, mon->my)) { + if (!mon_in_region(regions[i], mon)) + add_mon_to_reg(regions[i], mon); + } else { + if (mon_in_region(regions[i], mon)) + remove_mon_from_reg(regions[i], mon); + } + } +} + +#if 0 +/* not yet used */ + +/* + * Change monster pointer in regions + * This happens, for instance, when a monster grows and + * need a new structure (internally that is). + */ +void +replace_mon_regions(monold, monnew) +struct monst *monold, *monnew; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], monold)) { + remove_mon_from_reg(regions[i], monold); + add_mon_to_reg(regions[i], monnew); + } +} + +/* + * Remove monster from all regions it was in (ie monster just died) + */ +void +remove_mon_from_regions(mon) +struct monst *mon; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (mon_in_region(regions[i], mon)) + remove_mon_from_reg(regions[i], mon); +} + +#endif /*0*/ + +/* + * Check if a spot is under a visible region (eg: gas cloud). + * Returns NULL if not, otherwise returns region. + */ +NhRegion * +visible_region_at(x, y) +xchar x, y; +{ + register int i; + + for (i = 0; i < n_regions; i++) + if (inside_region(regions[i], x, y) && regions[i]->visible && + regions[i]->ttl != 0) + return regions[i]; + return (NhRegion *) 0; +} + +void +show_region(reg, x, y) +NhRegion *reg; +xchar x, y; +{ + show_glyph(x, y, reg->glyph); +} + +/** + * save_regions : + */ +void +save_regions(fd, mode) +int fd; +int mode; +{ + int i, j; + unsigned n; + + if (!perform_bwrite(mode)) goto skip_lots; + + bwrite(fd, (genericptr_t) &moves, sizeof (moves)); /* timestamp */ + bwrite(fd, (genericptr_t) &n_regions, sizeof (n_regions)); + for (i = 0; i < n_regions; i++) { + bwrite(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect)); + bwrite(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short)); + for (j = 0; j < regions[i]->nrects; j++) + bwrite(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect)); + bwrite(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); + n = 0; + bwrite(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned)); + n = regions[i]->enter_msg != NULL ? strlen(regions[i]->enter_msg) : 0; + bwrite(fd, (genericptr_t) &n, sizeof n); + if (n > 0) + bwrite(fd, (genericptr_t) regions[i]->enter_msg, n); + n = regions[i]->leave_msg != NULL ? strlen(regions[i]->leave_msg) : 0; + bwrite(fd, (genericptr_t) &n, sizeof n); + if (n > 0) + bwrite(fd, (genericptr_t) regions[i]->leave_msg, n); + bwrite(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); + bwrite(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean)); + bwrite(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short)); + for (j = 0; j < regions[i]->n_monst; j++) + bwrite(fd, (genericptr_t) ®ions[i]->monsters[j], + sizeof (unsigned)); + bwrite(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); + bwrite(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); + bwrite(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t)); + } + +skip_lots: + if (release_data(mode)) + clear_regions(); +} + +void +rest_regions(fd, ghostly) +int fd; +boolean ghostly; /* If a bones file restore */ +{ + int i, j; + unsigned n; + long tmstamp; + char *msg_buf; + + clear_regions(); /* Just for security */ + mread(fd, (genericptr_t) &tmstamp, sizeof (tmstamp)); + if (ghostly) tmstamp = 0; + else tmstamp = (moves - tmstamp); + mread(fd, (genericptr_t) &n_regions, sizeof (n_regions)); + max_regions = n_regions; + if (n_regions > 0) + regions = (NhRegion **) alloc(sizeof (NhRegion *) * n_regions); + for (i = 0; i < n_regions; i++) { + regions[i] = (NhRegion *) alloc(sizeof (NhRegion)); + mread(fd, (genericptr_t) ®ions[i]->bounding_box, sizeof (NhRect)); + mread(fd, (genericptr_t) ®ions[i]->nrects, sizeof (short)); + + if (regions[i]->nrects > 0) + regions[i]->rects = (NhRect *) + alloc(sizeof (NhRect) * regions[i]->nrects); + for (j = 0; j < regions[i]->nrects; j++) + mread(fd, (genericptr_t) ®ions[i]->rects[j], sizeof (NhRect)); + mread(fd, (genericptr_t) ®ions[i]->attach_2_u, sizeof (boolean)); + mread(fd, (genericptr_t) ®ions[i]->attach_2_m, sizeof (unsigned)); + + mread(fd, (genericptr_t) &n, sizeof n); + if (n > 0) { + msg_buf = (char *) alloc(n + 1); + mread(fd, (genericptr_t) msg_buf, n); + msg_buf[n] = '\0'; + regions[i]->enter_msg = (const char *) msg_buf; + } else + regions[i]->enter_msg = NULL; + + mread(fd, (genericptr_t) &n, sizeof n); + if (n > 0) { + msg_buf = (char *) alloc(n + 1); + mread(fd, (genericptr_t) msg_buf, n); + msg_buf[n] = '\0'; + regions[i]->leave_msg = (const char *) msg_buf; + } else + regions[i]->leave_msg = NULL; + + mread(fd, (genericptr_t) ®ions[i]->ttl, sizeof (short)); + /* check for expired region */ + if (regions[i]->ttl >= 0) + regions[i]->ttl = + (regions[i]->ttl > tmstamp) ? regions[i]->ttl - tmstamp : 0; + mread(fd, (genericptr_t) ®ions[i]->expire_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->can_enter_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->enter_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->can_leave_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->leave_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->inside_f, sizeof (short)); + mread(fd, (genericptr_t) ®ions[i]->player_flags, sizeof (boolean)); + if (ghostly) { /* settings pertained to old player */ + clear_hero_inside(regions[i]); + clear_heros_fault(regions[i]); + } + mread(fd, (genericptr_t) ®ions[i]->n_monst, sizeof (short)); + if (regions[i]->n_monst > 0) + regions[i]->monsters = + (unsigned *) alloc(sizeof (unsigned) * regions[i]->n_monst); + else + regions[i]->monsters = NULL; + regions[i]->max_monst = regions[i]->n_monst; + for (j = 0; j < regions[i]->n_monst; j++) + mread(fd, (genericptr_t) ®ions[i]->monsters[j], + sizeof (unsigned)); + mread(fd, (genericptr_t) ®ions[i]->visible, sizeof (boolean)); + mread(fd, (genericptr_t) ®ions[i]->glyph, sizeof (int)); + mread(fd, (genericptr_t) ®ions[i]->arg, sizeof (genericptr_t)); + } + /* remove expired regions, do not trigger the expire_f callback (yet!); + also update monster lists if this data is coming from a bones file */ + for (i = n_regions - 1; i >= 0; i--) + if (regions[i]->ttl == 0) + remove_region(regions[i]); + else if (ghostly && regions[i]->n_monst > 0) + reset_region_mids(regions[i]); +} + +/* update monster IDs for region being loaded from bones; `ghostly' implied */ +static void +reset_region_mids(reg) +NhRegion *reg; +{ + int i = 0, n = reg->n_monst; + unsigned *mid_list = reg->monsters; + + while (i < n) + if (!lookup_id_mapping(mid_list[i], &mid_list[i])) { + /* shrink list to remove missing monster; order doesn't matter */ + mid_list[i] = mid_list[--n]; + } else { + /* move on to next monster */ + ++i; + } + reg->n_monst = n; + return; +} + +#if 0 +/* not yet used */ + +/*--------------------------------------------------------------* + * * + * Create Region with just a message * + * * + *--------------------------------------------------------------*/ + +NhRegion * +create_msg_region(x, y, w, h, msg_enter, msg_leave) +xchar x, y; +xchar w, h; +const char *msg_enter; +const char *msg_leave; +{ + NhRect tmprect; + NhRegion *reg = create_region((NhRect *) 0, 0); + + reg->enter_msg = msg_enter; + reg->leave_msg = msg_leave; + tmprect.lx = x; + tmprect.ly = y; + tmprect.hx = x + w; + tmprect.hy = y + h; + add_rect_to_reg(reg, &tmprect); + reg->ttl = -1; + return reg; +} + + +/*--------------------------------------------------------------* + * * + * Force Field Related Code * + * (unused yet) * + *--------------------------------------------------------------*/ + +boolean +enter_force_field(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + struct monst *mtmp; + + if (p2 == NULL) { /* That means the player */ + if (!Blind) + You("bump into %s. Ouch!", + Hallucination ? "an invisible tree" : + "some kind of invisible wall"); + else + pline("Ouch!"); + } else { + mtmp = (struct monst *) p2; + if (canseemon(mtmp)) + pline("%s bumps into %s!", Monnam(mtmp), something); + } + return FALSE; +} + +NhRegion * +create_force_field(x, y, radius, ttl) +xchar x, y; +int radius, ttl; +{ + int i; + NhRegion *ff; + int nrect; + NhRect tmprect; + + ff = create_region((NhRect *) 0, 0); + nrect = radius; + tmprect.lx = x; + tmprect.hx = x; + tmprect.ly = y - (radius - 1); + tmprect.hy = y + (radius - 1); + for (i = 0; i < nrect; i++) { + add_rect_to_reg(ff, &tmprect); + tmprect.lx--; + tmprect.hx++; + tmprect.ly++; + tmprect.hy--; + } + ff->ttl = ttl; + if (!in_mklev && !flags.mon_moving) + set_heros_fault(ff); /* assume player has created it */ + /* ff->can_enter_f = enter_force_field; */ + /* ff->can_leave_f = enter_force_field; */ + add_region(ff); + return ff; +} + +#endif /*0*/ + +/*--------------------------------------------------------------* + * * + * Gas cloud related code * + * * + *--------------------------------------------------------------*/ + +/* + * Here is an example of an expire function that may prolong + * region life after some mods... + */ +boolean +expire_gas_cloud(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + NhRegion *reg; + int damage; + + reg = (NhRegion *) p1; + damage = (int) reg->arg; + + /* If it was a thick cloud, it dissipates a little first */ + if (damage >= 5) { + damage /= 2; /* It dissipates, let's do less damage */ + reg->arg = (genericptr_t) damage; + reg->ttl = 2; /* Here's the trick : reset ttl */ + return FALSE; /* THEN return FALSE, means "still there" */ + } + return TRUE; /* OK, it's gone, you can free it! */ +} + +boolean +inside_gas_cloud(p1, p2) +genericptr_t p1; +genericptr_t p2; +{ + NhRegion *reg; + struct monst *mtmp; + int dam; + + reg = (NhRegion *) p1; + dam = (int) reg->arg; + if (p2 == NULL) { /* This means *YOU* Bozo! */ + if (nonliving(youmonst.data) || Breathless) + return FALSE; + if (!Blind) + make_blinded(1L, FALSE); + if (!Poison_resistance) { + pline("%s is burning your %s!", Something, makeplural(body_part(LUNG))); + You("cough and spit blood!"); + losehp(rnd(dam) + 5, "gas cloud", KILLED_BY_AN); + return FALSE; + } else { + You("cough!"); + return FALSE; + } + } else { /* A monster is inside the cloud */ + mtmp = (struct monst *) p2; + + /* Non living and non breathing monsters are not concerned */ + if (!nonliving(mtmp->data) && !breathless(mtmp->data)) { + if (cansee(mtmp->mx, mtmp->my)) + pline("%s coughs!", Monnam(mtmp)); + setmangry(mtmp); + if (haseyes(mtmp->data) && mtmp->mcansee) { + mtmp->mblinded = 1; + mtmp->mcansee = 0; + } + if (resists_poison(mtmp)) + return FALSE; + mtmp->mhp -= rnd(dam) + 5; + if (mtmp->mhp <= 0) { + if (heros_fault(reg)) + killed(mtmp); + else + monkilled(mtmp, "gas cloud", AD_DRST); + if (mtmp->mhp <= 0) { /* not lifesaved */ + return TRUE; + } + } + } + } + return FALSE; /* Monster is still alive */ +} + +NhRegion * +create_gas_cloud(x, y, radius, damage) +xchar x, y; +int radius; +int damage; +{ + NhRegion *cloud; + int i, nrect; + NhRect tmprect; + + cloud = create_region((NhRect *) 0, 0); + nrect = radius; + tmprect.lx = x; + tmprect.hx = x; + tmprect.ly = y - (radius - 1); + tmprect.hy = y + (radius - 1); + for (i = 0; i < nrect; i++) { + add_rect_to_reg(cloud, &tmprect); + tmprect.lx--; + tmprect.hx++; + tmprect.ly++; + tmprect.hy--; + } + cloud->ttl = rn1(3,4); + if (!in_mklev && !flags.mon_moving) + set_heros_fault(cloud); /* assume player has created it */ + cloud->inside_f = INSIDE_GAS_CLOUD; + cloud->expire_f = EXPIRE_GAS_CLOUD; + cloud->arg = (genericptr_t) damage; + cloud->visible = TRUE; + cloud->glyph = cmap_to_glyph(S_cloud); + add_region(cloud); + return cloud; +} + +/*region.c*/ diff --git a/src/restore.c b/src/restore.c new file mode 100644 index 0000000..aaabbed --- /dev/null +++ b/src/restore.c @@ -0,0 +1,1093 @@ +/* SCCS Id: @(#)restore.c 3.4 2003/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" +#include "tcap.h" /* for TERMLIB and ASCIIGRAPH */ + +#if defined(MICRO) +extern int dotcnt; /* shared with save */ +extern int dotrow; /* shared with save */ +#endif + +#ifdef USE_TILES +extern void FDECL(substitute_tiles, (d_level *)); /* from tile.c */ +#endif + +#ifdef ZEROCOMP +static int NDECL(mgetc); +#endif +STATIC_DCL void NDECL(find_lev_obj); +STATIC_DCL void FDECL(restlevchn, (int)); +STATIC_DCL void FDECL(restdamage, (int,BOOLEAN_P)); +STATIC_DCL struct obj *FDECL(restobjchn, (int,BOOLEAN_P,BOOLEAN_P)); +STATIC_DCL struct monst *FDECL(restmonchn, (int,BOOLEAN_P)); +STATIC_DCL struct fruit *FDECL(loadfruitchn, (int)); +STATIC_DCL void FDECL(freefruitchn, (struct fruit *)); +STATIC_DCL void FDECL(ghostfruit, (struct obj *)); +STATIC_DCL boolean FDECL(restgamestate, (int, unsigned int *, unsigned int *)); +STATIC_DCL void FDECL(restlevelstate, (unsigned int, unsigned int)); +STATIC_DCL int FDECL(restlevelfile, (int,XCHAR_P)); +STATIC_DCL void FDECL(reset_oattached_mids, (BOOLEAN_P)); + +/* + * Save a mapping of IDs from ghost levels to the current level. This + * map is used by the timer routines when restoring ghost levels. + */ +#define N_PER_BUCKET 64 +struct bucket { + struct bucket *next; + struct { + unsigned gid; /* ghost ID */ + unsigned nid; /* new ID */ + } map[N_PER_BUCKET]; +}; + +STATIC_DCL void NDECL(clear_id_mapping); +STATIC_DCL void FDECL(add_id_mapping, (unsigned, unsigned)); + +static int n_ids_mapped = 0; +static struct bucket *id_map = 0; + + +#ifdef AMII_GRAPHICS +void FDECL( amii_setpens, (int) ); /* use colors from save file */ +extern int amii_numcolors; +#endif + +#include "quest.h" + +boolean restoring = FALSE; +static NEARDATA struct fruit *oldfruit; +static NEARDATA long omoves; + +#define Is_IceBox(o) ((o)->otyp == ICE_BOX ? TRUE : FALSE) + +/* Recalculate level.objects[x][y], since this info was not saved. */ +STATIC_OVL void +find_lev_obj() +{ + register struct obj *fobjtmp = (struct obj *)0; + register struct obj *otmp; + int x,y; + + for(x=0; xnobj; + otmp->nobj = fobjtmp; + otmp->where = OBJ_FREE; + fobjtmp = otmp; + } + /* fobj should now be empty */ + + /* Set level.objects (as well as reversing the chain back again) */ + while ((otmp = fobjtmp) != 0) { + fobjtmp = otmp->nobj; + place_object(otmp, otmp->ox, otmp->oy); + } +} + +/* Things that were marked "in_use" when the game was saved (ex. via the + * infamous "HUP" cheat) get used up here. + */ +void +inven_inuse(quietly) +boolean quietly; +{ + register struct obj *otmp, *otmp2; + + for (otmp = invent; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; +#ifndef GOLDOBJ + if (otmp->oclass == COIN_CLASS) { + /* in_use gold is created by some menu operations */ + if (!otmp->in_use) { + impossible("inven_inuse: !in_use gold in inventory"); + } + extract_nobj(otmp, &invent); + otmp->in_use = FALSE; + dealloc_obj(otmp); + } else +#endif /* GOLDOBJ */ + if (otmp->in_use) { + if (!quietly) pline("Finishing off %s...", xname(otmp)); + useup(otmp); + } + } +} + +STATIC_OVL void +restlevchn(fd) +register int fd; +{ + int cnt; + s_level *tmplev, *x; + + sp_levchn = (s_level *) 0; + mread(fd, (genericptr_t) &cnt, sizeof(int)); + for(; cnt > 0; cnt--) { + + tmplev = (s_level *)alloc(sizeof(s_level)); + mread(fd, (genericptr_t) tmplev, sizeof(s_level)); + if(!sp_levchn) sp_levchn = tmplev; + else { + + for(x = sp_levchn; x->next; x = x->next); + x->next = tmplev; + } + tmplev->next = (s_level *)0; + } +} + +STATIC_OVL void +restdamage(fd, ghostly) +int fd; +boolean ghostly; +{ + int counter; + struct damage *tmp_dam; + + mread(fd, (genericptr_t) &counter, sizeof(counter)); + if (!counter) + return; + tmp_dam = (struct damage *)alloc(sizeof(struct damage)); + while (--counter >= 0) { + char damaged_shops[5], *shp = (char *)0; + + mread(fd, (genericptr_t) tmp_dam, sizeof(*tmp_dam)); + if (ghostly) + tmp_dam->when += (monstermoves - omoves); + Strcpy(damaged_shops, + in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE)); + if (u.uz.dlevel) { + /* when restoring, there are two passes over the current + * level. the first time, u.uz isn't set, so neither is + * shop_keeper(). just wait and process the damage on + * the second pass. + */ + for (shp = damaged_shops; *shp; shp++) { + struct monst *shkp = shop_keeper(*shp); + + if (shkp && inhishop(shkp) && + repair_damage(shkp, tmp_dam, TRUE)) + break; + } + } + if (!shp || !*shp) { + tmp_dam->next = level.damagelist; + level.damagelist = tmp_dam; + tmp_dam = (struct damage *)alloc(sizeof(*tmp_dam)); + } + } + free((genericptr_t)tmp_dam); +} + +STATIC_OVL struct obj * +restobjchn(fd, ghostly, frozen) +register int fd; +boolean ghostly, frozen; +{ + register struct obj *otmp, *otmp2 = 0; + register struct obj *first = (struct obj *)0; + int xl; + + while(1) { + mread(fd, (genericptr_t) &xl, sizeof(xl)); + if(xl == -1) break; + otmp = newobj(xl); + if(!first) first = otmp; + else otmp2->nobj = otmp; + mread(fd, (genericptr_t) otmp, + (unsigned) xl + sizeof(struct obj)); + if (ghostly) { + unsigned nid = flags.ident++; + add_id_mapping(otmp->o_id, nid); + otmp->o_id = nid; + } + if (ghostly && otmp->otyp == SLIME_MOLD) ghostfruit(otmp); + /* Ghost levels get object age shifted from old player's clock + * to new player's clock. Assumption: new player arrived + * immediately after old player died. + */ + if (ghostly && !frozen && !age_is_relative(otmp)) + otmp->age = monstermoves - omoves + otmp->age; + + /* get contents of a container or statue */ + if (Has_contents(otmp)) { + struct obj *otmp3; + otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp)); + /* restore container back pointers */ + for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj) + otmp3->ocontainer = otmp; + } + if (otmp->bypass) otmp->bypass = 0; + + otmp2 = otmp; + } + if(first && otmp2->nobj){ + impossible("Restobjchn: error reading objchn."); + otmp2->nobj = 0; + } + + return(first); +} + +STATIC_OVL struct monst * +restmonchn(fd, ghostly) +register int fd; +boolean ghostly; +{ + register struct monst *mtmp, *mtmp2 = 0; + register struct monst *first = (struct monst *)0; + int xl; + struct permonst *monbegin; + boolean moved; + + /* get the original base address */ + mread(fd, (genericptr_t)&monbegin, sizeof(monbegin)); + moved = (monbegin != mons); + + while(1) { + mread(fd, (genericptr_t) &xl, sizeof(xl)); + if(xl == -1) break; + mtmp = newmonst(xl); + if(!first) first = mtmp; + else mtmp2->nmon = mtmp; + mread(fd, (genericptr_t) mtmp, (unsigned) xl + sizeof(struct monst)); + if (ghostly) { + unsigned nid = flags.ident++; + add_id_mapping(mtmp->m_id, nid); + mtmp->m_id = nid; + } + if (moved && mtmp->data) { + int offset = mtmp->data - monbegin; /*(ptrdiff_t)*/ + mtmp->data = mons + offset; /* new permonst location */ + } + if (ghostly) { + int mndx = monsndx(mtmp->data); + if (propagate(mndx, TRUE, ghostly) == 0) { + /* cookie to trigger purge in getbones() */ + mtmp->mhpmax = DEFUNCT_MONSTER; + } + } + if(mtmp->minvent) { + struct obj *obj; + mtmp->minvent = restobjchn(fd, ghostly, FALSE); + /* restore monster back pointer */ + for (obj = mtmp->minvent; obj; obj = obj->nobj) + obj->ocarry = mtmp; + } + if (mtmp->mw) { + struct obj *obj; + + for(obj = mtmp->minvent; obj; obj = obj->nobj) + if (obj->owornmask & W_WEP) break; + if (obj) mtmp->mw = obj; + else { + MON_NOWEP(mtmp); + impossible("bad monster weapon restore"); + } + } + + if (mtmp->isshk) restshk(mtmp, ghostly); + if (mtmp->ispriest) restpriest(mtmp, ghostly); + + mtmp2 = mtmp; + } + if(first && mtmp2->nmon){ + impossible("Restmonchn: error reading monchn."); + mtmp2->nmon = 0; + } + return(first); +} + +STATIC_OVL struct fruit * +loadfruitchn(fd) +int fd; +{ + register struct fruit *flist, *fnext; + + flist = 0; + while (fnext = newfruit(), + mread(fd, (genericptr_t)fnext, sizeof *fnext), + fnext->fid != 0) { + fnext->nextf = flist; + flist = fnext; + } + dealloc_fruit(fnext); + return flist; +} + +STATIC_OVL void +freefruitchn(flist) +register struct fruit *flist; +{ + register struct fruit *fnext; + + while (flist) { + fnext = flist->nextf; + dealloc_fruit(flist); + flist = fnext; + } +} + +STATIC_OVL void +ghostfruit(otmp) +register struct obj *otmp; +{ + register struct fruit *oldf; + + for (oldf = oldfruit; oldf; oldf = oldf->nextf) + if (oldf->fid == otmp->spe) break; + + if (!oldf) impossible("no old fruit?"); + else otmp->spe = fruitadd(oldf->fname); +} + +STATIC_OVL +boolean +restgamestate(fd, stuckid, steedid) +register int fd; +unsigned int *stuckid, *steedid; /* STEED */ +{ + /* discover is actually flags.explore */ + boolean remember_discover = discover; + struct obj *otmp; + int uid; + + mread(fd, (genericptr_t) &uid, sizeof uid); + if (uid != getuid()) { /* strange ... */ + /* for wizard mode, issue a reminder; for others, treat it + as an attempt to cheat and refuse to restore this file */ + pline("Saved game was not yours."); +#ifdef WIZARD + if (!wizard) +#endif + return FALSE; + } + + mread(fd, (genericptr_t) &flags, sizeof(struct flag)); + flags.bypasses = 0; /* never use the saved value of bypasses */ + if (remember_discover) discover = remember_discover; + + role_init(); /* Reset the initial role, race, gender, and alignment */ +#ifdef AMII_GRAPHICS + amii_setpens(amii_numcolors); /* use colors from save file */ +#endif + mread(fd, (genericptr_t) &u, sizeof(struct you)); + set_uasmon(); +#ifdef CLIPPING + cliparound(u.ux, u.uy); +#endif + if(u.uhp <= 0 && (!Upolyd || u.mh <= 0)) { + u.ux = u.uy = 0; /* affects pline() [hence You()] */ + You("were not healthy enough to survive restoration."); + /* wiz1_level.dlevel is used by mklev.c to see if lots of stuff is + * uninitialized, so we only have to set it and not the other stuff. + */ + wiz1_level.dlevel = 0; + u.uz.dnum = 0; + u.uz.dlevel = 1; + return(FALSE); + } + + /* this stuff comes after potential aborted restore attempts */ + restore_timers(fd, RANGE_GLOBAL, FALSE, 0L); + restore_light_sources(fd); + invent = restobjchn(fd, FALSE, FALSE); + migrating_objs = restobjchn(fd, FALSE, FALSE); + migrating_mons = restmonchn(fd, FALSE); + mread(fd, (genericptr_t) mvitals, sizeof(mvitals)); + + /* this comes after inventory has been loaded */ + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->owornmask) + setworn(otmp, otmp->owornmask); + /* reset weapon so that player will get a reminder about "bashing" + during next fight when bare-handed or wielding an unconventional + item; for pick-axe, we aren't able to distinguish between having + applied or wielded it, so be conservative and assume the former */ + otmp = uwep; /* `uwep' usually init'd by setworn() in loop above */ + uwep = 0; /* clear it and have setuwep() reinit */ + setuwep(otmp); /* (don't need any null check here) */ + if (!uwep || uwep->otyp == PICK_AXE || uwep->otyp == GRAPPLING_HOOK) + unweapon = TRUE; + + restore_dungeon(fd); + restlevchn(fd); + mread(fd, (genericptr_t) &moves, sizeof moves); + mread(fd, (genericptr_t) &monstermoves, sizeof monstermoves); + mread(fd, (genericptr_t) &quest_status, sizeof(struct q_score)); + mread(fd, (genericptr_t) spl_book, + sizeof(struct spell) * (MAXSPELL + 1)); + restore_artifacts(fd); + restore_oracles(fd); + if (u.ustuck) + mread(fd, (genericptr_t) stuckid, sizeof (*stuckid)); +#ifdef STEED + if (u.usteed) + mread(fd, (genericptr_t) steedid, sizeof (*steedid)); +#endif + mread(fd, (genericptr_t) pl_character, sizeof pl_character); + + mread(fd, (genericptr_t) pl_fruit, sizeof pl_fruit); + mread(fd, (genericptr_t) ¤t_fruit, sizeof current_fruit); + freefruitchn(ffruit); /* clean up fruit(s) made by initoptions() */ + ffruit = loadfruitchn(fd); + + restnames(fd); + restore_waterlevel(fd); + /* must come after all mons & objs are restored */ + relink_timers(FALSE); + relink_light_sources(FALSE); + return(TRUE); +} + +/* update game state pointers to those valid for the current level (so we + * don't dereference a wild u.ustuck when saving the game state, for instance) + */ +STATIC_OVL void +restlevelstate(stuckid, steedid) +unsigned int stuckid, steedid; /* STEED */ +{ + register struct monst *mtmp; + + if (stuckid) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == stuckid) break; + if (!mtmp) panic("Cannot find the monster ustuck."); + u.ustuck = mtmp; + } +#ifdef STEED + if (steedid) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (mtmp->m_id == steedid) break; + if (!mtmp) panic("Cannot find the monster usteed."); + u.usteed = mtmp; + remove_monster(mtmp->mx, mtmp->my); + } +#endif +} + +/*ARGSUSED*/ /* fd used in MFLOPPY only */ +STATIC_OVL int +restlevelfile(fd, ltmp) +register int fd; +xchar ltmp; +#if defined(macintosh) && (defined(__SC__) || defined(__MRC__)) +# pragma unused(fd) +#endif +{ + register int nfd; + char whynot[BUFSZ]; + + nfd = create_levelfile(ltmp, whynot); + if (nfd < 0) { + /* BUG: should suppress any attempt to write a panic + save file if file creation is now failing... */ + panic("restlevelfile: %s", whynot); + } +#ifdef MFLOPPY + if (!savelev(nfd, ltmp, COUNT_SAVE)) { + + /* The savelev can't proceed because the size required + * is greater than the available disk space. + */ + pline("Not enough space on `%s' to restore your game.", + levels); + + /* Remove levels and bones that may have been created. + */ + (void) close(nfd); +# ifdef AMIGA + clearlocks(); +# else + eraseall(levels, alllevels); + eraseall(levels, allbones); + + /* Perhaps the person would like to play without a + * RAMdisk. + */ + if (ramdisk) { + /* PlaywoRAMdisk may not return, but if it does + * it is certain that ramdisk will be 0. + */ + playwoRAMdisk(); + /* Rewind save file and try again */ + (void) lseek(fd, (off_t)0, 0); + (void) uptodate(fd, (char *)0); /* skip version */ + return dorecover(fd); /* 0 or 1 */ + } else { +# endif + pline("Be seeing you..."); + terminate(EXIT_SUCCESS); +# ifndef AMIGA + } +# endif + } +#endif + bufon(nfd); + savelev(nfd, ltmp, WRITE_SAVE | FREE_SAVE); + bclose(nfd); + return(2); +} + +int +dorecover(fd) +register int fd; +{ + unsigned int stuckid = 0, steedid = 0; /* not a register */ + xchar ltmp; + int rtmp; + struct obj *otmp; + +#ifdef STORE_PLNAME_IN_FILE + mread(fd, (genericptr_t) plname, PL_NSIZ); +#endif + + restoring = TRUE; + getlev(fd, 0, (xchar)0, FALSE); + if (!restgamestate(fd, &stuckid, &steedid)) { + display_nhwindow(WIN_MESSAGE, TRUE); + savelev(-1, 0, FREE_SAVE); /* discard current level */ + (void) close(fd); + (void) delete_savefile(); + restoring = FALSE; + return(0); + } + restlevelstate(stuckid, steedid); +#ifdef INSURANCE + savestateinlock(); +#endif + rtmp = restlevelfile(fd, ledger_no(&u.uz)); + if (rtmp < 2) return(rtmp); /* dorecover called recursively */ + + /* these pointers won't be valid while we're processing the + * other levels, but they'll be reset again by restlevelstate() + * afterwards, and in the meantime at least u.usteed may mislead + * place_monster() on other levels + */ + u.ustuck = (struct monst *)0; +#ifdef STEED + u.usteed = (struct monst *)0; +#endif + +#ifdef MICRO +# ifdef AMII_GRAPHICS + { + extern struct window_procs amii_procs; + if(windowprocs.win_init_nhwindows== amii_procs.win_init_nhwindows){ + extern winid WIN_BASE; + clear_nhwindow(WIN_BASE); /* hack until there's a hook for this */ + } + } +# else + clear_nhwindow(WIN_MAP); +# endif + clear_nhwindow(WIN_MESSAGE); + You("return to level %d in %s%s.", + depth(&u.uz), dungeons[u.uz.dnum].dname, + flags.debug ? " while in debug mode" : + flags.explore ? " while in explore mode" : ""); + curs(WIN_MAP, 1, 1); + dotcnt = 0; + dotrow = 2; + if (strncmpi("X11", windowprocs.name, 3)) + putstr(WIN_MAP, 0, "Restoring:"); +#endif + while(1) { +#ifdef ZEROCOMP + if(mread(fd, (genericptr_t) <mp, sizeof ltmp) < 0) +#else + if(read(fd, (genericptr_t) <mp, sizeof ltmp) != sizeof ltmp) +#endif + break; + getlev(fd, 0, ltmp, FALSE); +#ifdef MICRO + curs(WIN_MAP, 1+dotcnt++, dotrow); + if (dotcnt >= (COLNO - 1)) { + dotrow++; + dotcnt = 0; + } + if (strncmpi("X11", windowprocs.name, 3)){ + putstr(WIN_MAP, 0, "."); + } + mark_synch(); +#endif + rtmp = restlevelfile(fd, ltmp); + if (rtmp < 2) return(rtmp); /* dorecover called recursively */ + } + +#ifdef BSD + (void) lseek(fd, 0L, 0); +#else + (void) lseek(fd, (off_t)0, 0); +#endif + (void) uptodate(fd, (char *)0); /* skip version info */ +#ifdef STORE_PLNAME_IN_FILE + mread(fd, (genericptr_t) plname, PL_NSIZ); +#endif + getlev(fd, 0, (xchar)0, FALSE); + (void) close(fd); + + if (!wizard && !discover) + (void) delete_savefile(); +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) assign_rogue_graphics(TRUE); +#endif +#ifdef USE_TILES + substitute_tiles(&u.uz); +#endif + restlevelstate(stuckid, steedid); +#ifdef MFLOPPY + gameDiskPrompt(); +#endif + max_rank_sz(); /* to recompute mrank_sz (botl.c) */ + /* take care of iron ball & chain */ + for(otmp = fobj; otmp; otmp = otmp->nobj) + if(otmp->owornmask) + setworn(otmp, otmp->owornmask); + + /* in_use processing must be after: + * + The inventory has been read so that freeinv() works. + * + The current level has been restored so billing information + * is available. + */ + inven_inuse(FALSE); + + load_qtlist(); /* re-load the quest text info */ + reset_attribute_clock(); + /* Set up the vision internals, after levl[] data is loaded */ + /* but before docrt(). */ + vision_reset(); + vision_full_recalc = 1; /* recompute vision (not saved) */ + + run_timers(); /* expire all timers that have gone off while away */ + docrt(); + restoring = FALSE; + clear_nhwindow(WIN_MESSAGE); + program_state.something_worth_saving++; /* useful data now exists */ + + /* Success! */ + welcome(FALSE); + return(1); +} + +void +trickery(reason) +char *reason; +{ + pline("Strange, this map is not as I remember it."); + pline("Somebody is trying some trickery here..."); + pline("This game is void."); + killer = reason; + done(TRICKED); +} + +void +getlev(fd, pid, lev, ghostly) +int fd, pid; +xchar lev; +boolean ghostly; +{ + register struct trap *trap; + register struct monst *mtmp; + branch *br; + int hpid; + xchar dlvl; + int x, y; +#ifdef TOS + short tlev; +#endif + + if (ghostly) + clear_id_mapping(); + +#if defined(MSDOS) || defined(OS2) + setmode(fd, O_BINARY); +#endif + /* Load the old fruit info. We have to do it first, so the + * information is available when restoring the objects. + */ + if (ghostly) oldfruit = loadfruitchn(fd); + + /* First some sanity checks */ + mread(fd, (genericptr_t) &hpid, sizeof(hpid)); +/* CHECK: This may prevent restoration */ +#ifdef TOS + mread(fd, (genericptr_t) &tlev, sizeof(tlev)); + dlvl=tlev&0x00ff; +#else + mread(fd, (genericptr_t) &dlvl, sizeof(dlvl)); +#endif + if ((pid && pid != hpid) || (lev && dlvl != lev)) { + char trickbuf[BUFSZ]; + + if (pid && pid != hpid) + Sprintf(trickbuf, "PID (%d) doesn't match saved PID (%d)!", + hpid, pid); + else + Sprintf(trickbuf, "This is level %d, not %d!", dlvl, lev); +#ifdef WIZARD + if (wizard) pline(trickbuf); +#endif + trickery(trickbuf); + } + +#ifdef RLECOMP + { + short i, j; + uchar len; + struct rm r; + +#if defined(MAC) + /* Suppress warning about used before set */ + (void) memset((genericptr_t) &r, 0, sizeof(r)); +#endif + i = 0; j = 0; len = 0; + while(i < ROWNO) { + while(j < COLNO) { + if(len > 0) { + levl[j][i] = r; + len -= 1; + j += 1; + } else { + mread(fd, (genericptr_t)&len, sizeof(uchar)); + mread(fd, (genericptr_t)&r, sizeof(struct rm)); + } + } + j = 0; + i += 1; + } + } +#else + mread(fd, (genericptr_t) levl, sizeof(levl)); +#endif /* RLECOMP */ + + mread(fd, (genericptr_t)&omoves, sizeof(omoves)); + mread(fd, (genericptr_t)&upstair, sizeof(stairway)); + mread(fd, (genericptr_t)&dnstair, sizeof(stairway)); + mread(fd, (genericptr_t)&upladder, sizeof(stairway)); + mread(fd, (genericptr_t)&dnladder, sizeof(stairway)); + mread(fd, (genericptr_t)&sstairs, sizeof(stairway)); + mread(fd, (genericptr_t)&updest, sizeof(dest_area)); + mread(fd, (genericptr_t)&dndest, sizeof(dest_area)); + mread(fd, (genericptr_t)&level.flags, sizeof(level.flags)); + mread(fd, (genericptr_t)doors, sizeof(doors)); + rest_rooms(fd); /* No joke :-) */ + if (nroom) + doorindex = rooms[nroom - 1].fdoor + rooms[nroom - 1].doorct; + else + doorindex = 0; + + restore_timers(fd, RANGE_LEVEL, ghostly, monstermoves - omoves); + restore_light_sources(fd); + fmon = restmonchn(fd, ghostly); + + /* regenerate animals while on another level */ + if (u.uz.dlevel) { + register struct monst *mtmp2; + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (ghostly) { + /* reset peaceful/malign relative to new character */ + if(!mtmp->isshk) + /* shopkeepers will reset based on name */ + mtmp->mpeaceful = peace_minded(mtmp->data); + set_malign(mtmp); + } else if (monstermoves > omoves) + mon_catchup_elapsed_time(mtmp, monstermoves - omoves); + + /* update shape-changers in case protection against + them is different now than when the level was saved */ + restore_cham(mtmp); + } + } + + rest_worm(fd); /* restore worm information */ + ftrap = 0; + while (trap = newtrap(), + mread(fd, (genericptr_t)trap, sizeof(struct trap)), + trap->tx != 0) { /* need "!= 0" to work around DICE 3.0 bug */ + trap->ntrap = ftrap; + ftrap = trap; + } + dealloc_trap(trap); + fobj = restobjchn(fd, ghostly, FALSE); + find_lev_obj(); + /* restobjchn()'s `frozen' argument probably ought to be a callback + routine so that we can check for objects being buried under ice */ + level.buriedobjlist = restobjchn(fd, ghostly, FALSE); + billobjs = restobjchn(fd, ghostly, FALSE); + rest_engravings(fd); + + /* reset level.monsters for new level */ + for (x = 0; x < COLNO; x++) + for (y = 0; y < ROWNO; y++) + level.monsters[x][y] = (struct monst *) 0; + for (mtmp = level.monlist; mtmp; mtmp = mtmp->nmon) { + if (mtmp->isshk) + set_residency(mtmp, FALSE); + place_monster(mtmp, mtmp->mx, mtmp->my); + if (mtmp->wormno) place_wsegs(mtmp); + } + restdamage(fd, ghostly); + + rest_regions(fd, ghostly); + if (ghostly) { + /* Now get rid of all the temp fruits... */ + freefruitchn(oldfruit), oldfruit = 0; + + if (lev > ledger_no(&medusa_level) && + lev < ledger_no(&stronghold_level) && xdnstair == 0) { + coord cc; + + mazexy(&cc); + xdnstair = cc.x; + ydnstair = cc.y; + levl[cc.x][cc.y].typ = STAIRS; + } + + br = Is_branchlev(&u.uz); + if (br && u.uz.dlevel == 1) { + d_level ltmp; + + if (on_level(&u.uz, &br->end1)) + assign_level(<mp, &br->end2); + else + assign_level(<mp, &br->end1); + + switch(br->type) { + case BR_STAIR: + case BR_NO_END1: + case BR_NO_END2: /* OK to assign to sstairs if it's not used */ + assign_level(&sstairs.tolev, <mp); + break; + case BR_PORTAL: /* max of 1 portal per level */ + { + register struct trap *ttmp; + for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + if (ttmp->ttyp == MAGIC_PORTAL) + break; + if (!ttmp) panic("getlev: need portal but none found"); + assign_level(&ttmp->dst, <mp); + } + break; + } + } else if (!br) { + /* Remove any dangling portals. */ + register struct trap *ttmp; + for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) + if (ttmp->ttyp == MAGIC_PORTAL) { + deltrap(ttmp); + break; /* max of 1 portal/level */ + } + } + } + + /* must come after all mons & objs are restored */ + relink_timers(ghostly); + relink_light_sources(ghostly); + reset_oattached_mids(ghostly); + + if (ghostly) + clear_id_mapping(); +} + + +/* Clear all structures for object and monster ID mapping. */ +STATIC_OVL void +clear_id_mapping() +{ + struct bucket *curr; + + while ((curr = id_map) != 0) { + id_map = curr->next; + free((genericptr_t) curr); + } + n_ids_mapped = 0; +} + +/* Add a mapping to the ID map. */ +STATIC_OVL void +add_id_mapping(gid, nid) + unsigned gid, nid; +{ + int idx; + + idx = n_ids_mapped % N_PER_BUCKET; + /* idx is zero on first time through, as well as when a new bucket is */ + /* needed */ + if (idx == 0) { + struct bucket *gnu = (struct bucket *) alloc(sizeof(struct bucket)); + gnu->next = id_map; + id_map = gnu; + } + + id_map->map[idx].gid = gid; + id_map->map[idx].nid = nid; + n_ids_mapped++; +} + +/* + * Global routine to look up a mapping. If found, return TRUE and fill + * in the new ID value. Otherwise, return false and return -1 in the new + * ID. + */ +boolean +lookup_id_mapping(gid, nidp) + unsigned gid, *nidp; +{ + int i; + struct bucket *curr; + + if (n_ids_mapped) + for (curr = id_map; curr; curr = curr->next) { + /* first bucket might not be totally full */ + if (curr == id_map) { + i = n_ids_mapped % N_PER_BUCKET; + if (i == 0) i = N_PER_BUCKET; + } else + i = N_PER_BUCKET; + + while (--i >= 0) + if (gid == curr->map[i].gid) { + *nidp = curr->map[i].nid; + return TRUE; + } + } + + return FALSE; +} + +STATIC_OVL void +reset_oattached_mids(ghostly) +boolean ghostly; +{ + struct obj *otmp; + unsigned oldid, nid; + for (otmp = fobj; otmp; otmp = otmp->nobj) { + if (ghostly && otmp->oattached == OATTACHED_MONST && otmp->oxlth) { + struct monst *mtmp = (struct monst *)otmp->oextra; + + mtmp->m_id = 0; + mtmp->mpeaceful = mtmp->mtame = 0; /* pet's owner died! */ + } + if (ghostly && otmp->oattached == OATTACHED_M_ID) { + (void) memcpy((genericptr_t)&oldid, (genericptr_t)otmp->oextra, + sizeof(oldid)); + if (lookup_id_mapping(oldid, &nid)) + (void) memcpy((genericptr_t)otmp->oextra, (genericptr_t)&nid, + sizeof(nid)); + else + otmp->oattached = OATTACHED_NOTHING; + } + } +} + + +#ifdef ZEROCOMP +#define RLESC '\0' /* Leading character for run of RLESC's */ + +#ifndef ZEROCOMP_BUFSIZ +#define ZEROCOMP_BUFSIZ BUFSZ +#endif +static NEARDATA unsigned char inbuf[ZEROCOMP_BUFSIZ]; +static NEARDATA unsigned short inbufp = 0; +static NEARDATA unsigned short inbufsz = 0; +static NEARDATA short inrunlength = -1; +static NEARDATA int mreadfd; + +static int +mgetc() +{ + if (inbufp >= inbufsz) { + inbufsz = read(mreadfd, (genericptr_t)inbuf, sizeof inbuf); + if (!inbufsz) { + if (inbufp > sizeof inbuf) + error("EOF on file #%d.\n", mreadfd); + inbufp = 1 + sizeof inbuf; /* exactly one warning :-) */ + return -1; + } + inbufp = 0; + } + return inbuf[inbufp++]; +} + +void +minit() +{ + inbufsz = 0; + inbufp = 0; + inrunlength = -1; +} + +int +mread(fd, buf, len) +int fd; +genericptr_t buf; +register unsigned len; +{ + /*register int readlen = 0;*/ + if (fd < 0) error("Restore error; mread attempting to read file %d.", fd); + mreadfd = fd; + while (len--) { + if (inrunlength > 0) { + inrunlength--; + *(*((char **)&buf))++ = '\0'; + } else { + register short ch = mgetc(); + if (ch < 0) return -1; /*readlen;*/ + if ((*(*(char **)&buf)++ = (char)ch) == RLESC) { + inrunlength = mgetc(); + } + } + /*readlen++;*/ + } + return 0; /*readlen;*/ +} + +#else /* ZEROCOMP */ + +void +minit() +{ + return; +} + +void +mread(fd, buf, len) +register int fd; +register genericptr_t buf; +register unsigned int len; +{ + register int rlen; + +#if defined(BSD) || defined(ULTRIX) + rlen = read(fd, buf, (int) len); + if(rlen != len){ +#else /* e.g. SYSV, __TURBOC__ */ + rlen = read(fd, buf, (unsigned) len); + if((unsigned)rlen != len){ +#endif + pline("Read %d instead of %u bytes.", rlen, len); + if(restoring) { + (void) close(fd); + (void) delete_savefile(); + error("Error restoring old game."); + } + panic("Error reading level file."); + } +} +#endif /* ZEROCOMP */ + +/*restore.c*/ diff --git a/src/rip.c b/src/rip.c new file mode 100644 index 0000000..9a82630 --- /dev/null +++ b/src/rip.c @@ -0,0 +1,175 @@ +/* SCCS Id: @(#)rip.c 3.4 2003/01/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL void FDECL(center, (int, char *)); + +extern const char * const killed_by_prefix[]; /* from topten.c */ + +#if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) || defined(MSWIN_GRAPHICS) +# define TEXT_TOMBSTONE +#endif +#if defined(mac) || defined(__BEOS__) || defined(WIN32_GRAPHICS) +# ifndef TEXT_TOMBSTONE +# define TEXT_TOMBSTONE +# endif +#endif + +#ifdef TEXT_TOMBSTONE + +#ifndef NH320_DEDICATION +/* A normal tombstone for end of game display. */ +static const char *rip_txt[] = { +" ----------", +" / \\", +" / REST \\", +" / IN \\", +" / PEACE \\", +" / \\", +" | |", /* Name of player */ +" | |", /* Amount of $ */ +" | |", /* Type of death */ +" | |", /* . */ +" | |", /* . */ +" | |", /* . */ +" | 1001 |", /* Real year of death */ +" *| * * * | *", +" _________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______", +0 +}; +#define STONE_LINE_CENT 28 /* char[] element of center of stone face */ +#else /* NH320_DEDICATION */ +/* NetHack 3.2.x displayed a dual tombstone as a tribute to Izchak. */ +static const char *rip_txt[] = { +" ---------- ----------", +" / \\ / \\", +" / REST \\ / This \\", +" / IN \\ / release of \\", +" / PEACE \\ / NetHack is \\", +" / \\ / dedicated to \\", +" | | | the memory of |", +" | | | |", +" | | | Izchak Miller |", +" | | | 1935 - 1994 |", +" | | | |", +" | | | Ascended |", +" | 1001 | | |", +" * | * * * | * * | * * * | *", +" _____)/\\|\\__//(\\/(/\\)/\\//\\/|_)________)/|\\\\_/_/(\\/(/\\)/\\/\\/|_)____", +0 +}; +#define STONE_LINE_CENT 19 /* char[] element of center of stone face */ +#endif /* NH320_DEDICATION */ +#define STONE_LINE_LEN 16 /* # chars that fit on one line + * (note 1 ' ' border) + */ +#define NAME_LINE 6 /* *char[] line # for player name */ +#define GOLD_LINE 7 /* *char[] line # for amount of gold */ +#define DEATH_LINE 8 /* *char[] line # for death description */ +#define YEAR_LINE 12 /* *char[] line # for year */ + +static char **rip; + +STATIC_OVL void +center(line, text) +int line; +char *text; +{ + register char *ip,*op; + ip = text; + op = &rip[line][STONE_LINE_CENT - ((strlen(text)+1)>>1)]; + while(*ip) *op++ = *ip++; +} + + +void +genl_outrip(tmpwin, how) +winid tmpwin; +int how; +{ + register char **dp; + register char *dpx; + char buf[BUFSZ]; + register int x; + int line; + + rip = dp = (char **) alloc(sizeof(rip_txt)); + for (x = 0; rip_txt[x]; x++) { + dp[x] = (char *) alloc((unsigned int)(strlen(rip_txt[x]) + 1)); + Strcpy(dp[x], rip_txt[x]); + } + dp[x] = (char *)0; + + /* Put name on stone */ + Sprintf(buf, "%s", plname); + buf[STONE_LINE_LEN] = 0; + center(NAME_LINE, buf); + + /* Put $ on stone */ +#ifndef GOLDOBJ + Sprintf(buf, "%ld Au", u.ugold); +#else + Sprintf(buf, "%ld Au", done_money); +#endif + buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */ + center(GOLD_LINE, buf); + + /* Put together death description */ + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + + /* Put death type on stone */ + for (line=DEATH_LINE, dpx = buf; line STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + center(line, dpx); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + Sprintf(buf, "%4d", getyear()); + center(YEAR_LINE, buf); + + putstr(tmpwin, 0, ""); + for(; *dp; dp++) + putstr(tmpwin, 0, *dp); + + putstr(tmpwin, 0, ""); + putstr(tmpwin, 0, ""); + + for (x = 0; rip_txt[x]; x++) { + free((genericptr_t)rip[x]); + } + free((genericptr_t)rip); + rip = 0; +} + +#endif /* TEXT_TOMBSTONE */ + +/*rip.c*/ diff --git a/src/rnd.c b/src/rnd.c new file mode 100644 index 0000000..313b70b --- /dev/null +++ b/src/rnd.c @@ -0,0 +1,145 @@ +/* SCCS Id: @(#)rnd.c 3.4 1996/02/07 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* "Rand()"s definition is determined by [OS]conf.h */ +#if defined(LINT) && defined(UNIX) /* rand() is long... */ +extern int NDECL(rand); +#define RND(x) (rand() % x) +#else /* LINT */ +# if defined(UNIX) || defined(RANDOM) +#define RND(x) (int)(Rand() % (long)(x)) +# else +/* Good luck: the bottom order bits are cyclic. */ +#define RND(x) (int)((Rand()>>3) % (x)) +# endif +#endif /* LINT */ + +#ifdef OVL0 + +int +rn2(x) /* 0 <= rn2(x) < x */ +register int x; +{ +#ifdef DEBUG + if (x <= 0) { + impossible("rn2(%d) attempted", x); + return(0); + } + x = RND(x); + return(x); +#else + return(RND(x)); +#endif +} + +#endif /* OVL0 */ +#ifdef OVLB + +int +rnl(x) /* 0 <= rnl(x) < x; sometimes subtracting Luck */ +register int x; /* good luck approaches 0, bad luck approaches (x-1) */ +{ + register int i; + +#ifdef DEBUG + if (x <= 0) { + impossible("rnl(%d) attempted", x); + return(0); + } +#endif + i = RND(x); + + if (Luck && rn2(50 - Luck)) { + i -= (x <= 15 && Luck >= -5 ? Luck/3 : Luck); + if (i < 0) i = 0; + else if (i >= x) i = x-1; + } + + return i; +} + +#endif /* OVLB */ +#ifdef OVL0 + +int +rnd(x) /* 1 <= rnd(x) <= x */ +register int x; +{ +#ifdef DEBUG + if (x <= 0) { + impossible("rnd(%d) attempted", x); + return(1); + } + x = RND(x)+1; + return(x); +#else + return(RND(x)+1); +#endif +} + +#endif /* OVL0 */ +#ifdef OVL1 + +int +d(n,x) /* n <= d(n,x) <= (n*x) */ +register int n, x; +{ + register int tmp = n; + +#ifdef DEBUG + if (x < 0 || n < 0 || (x == 0 && n != 0)) { + impossible("d(%d,%d) attempted", n, x); + return(1); + } +#endif + while(n--) tmp += RND(x); + return(tmp); /* Alea iacta est. -- J.C. */ +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +rne(x) +register int x; +{ + register int tmp, utmp; + + utmp = (u.ulevel < 15) ? 5 : u.ulevel/3; + tmp = 1; + while (tmp < utmp && !rn2(x)) + tmp++; + return tmp; + + /* was: + * tmp = 1; + * while(!rn2(x)) tmp++; + * return(min(tmp,(u.ulevel < 15) ? 5 : u.ulevel/3)); + * which is clearer but less efficient and stands a vanishingly + * small chance of overflowing tmp + */ +} + +int +rnz(i) +int i; +{ +#ifdef LINT + int x = i; + int tmp = 1000; +#else + register long x = i; + register long tmp = 1000; +#endif + tmp += rn2(1000); + tmp *= rne(4); + if (rn2(2)) { x *= tmp; x /= 1000; } + else { x *= 1000; x /= tmp; } + return((int)x); +} + +#endif /* OVLB */ + +/*rnd.c*/ diff --git a/src/role.c b/src/role.c new file mode 100644 index 0000000..c751d10 --- /dev/null +++ b/src/role.c @@ -0,0 +1,1491 @@ +/* SCCS Id: @(#)role.c 3.4 2003/01/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + + +/*** Table of all roles ***/ +/* According to AD&D, HD for some classes (ex. Wizard) should be smaller + * (4-sided for wizards). But this is not AD&D, and using the AD&D + * rule here produces an unplayable character. Thus I have used a minimum + * of an 10-sided hit die for everything. Another AD&D change: wizards get + * a minimum strength of 4 since without one you can't teleport or cast + * spells. --KAA + * + * As the wizard has been updated (wizard patch 5 jun '96) their HD can be + * brought closer into line with AD&D. This forces wizards to use magic more + * and distance themselves from their attackers. --LSZ + * + * With the introduction of races, some hit points and energy + * has been reallocated for each race. The values assigned + * to the roles has been reduced by the amount allocated to + * humans. --KMH + * + * God names use a leading underscore to flag goddesses. + */ +const struct Role roles[] = { +{ {"Archeologist", 0}, { + {"Digger", 0}, + {"Field Worker",0}, + {"Investigator",0}, + {"Exhumer", 0}, + {"Excavator", 0}, + {"Spelunker", 0}, + {"Speleologist",0}, + {"Collector", 0}, + {"Curator", 0} }, + "Quetzalcoatl", "Camaxtli", "Huhetotl", /* Central American */ + "Arc", "the College of Archeology", "the Tomb of the Toltec Kings", + PM_ARCHEOLOGIST, NON_PM, NON_PM, + PM_LORD_CARNARVON, PM_STUDENT, PM_MINION_OF_HUHETOTL, + NON_PM, PM_HUMAN_MUMMY, S_SNAKE, S_MUMMY, + ART_ORB_OF_DETECTION, + MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE | + ROLE_LAWFUL|ROLE_NEUTRAL, + /* Str Int Wis Dex Con Cha */ + { 7, 10, 10, 7, 7, 7 }, + { 20, 20, 20, 10, 20, 10 }, + /* Init Lower Higher */ + { 11, 0, 0, 8, 1, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },14, /* Energy */ + 10, 5, 0, 2, 10, A_INT, SPE_MAGIC_MAPPING, -4 +}, +{ {"Barbarian", 0}, { + {"Plunderer", "Plunderess"}, + {"Pillager", 0}, + {"Bandit", 0}, + {"Brigand", 0}, + {"Raider", 0}, + {"Reaver", 0}, + {"Slayer", 0}, + {"Chieftain", "Chieftainess"}, + {"Conqueror", "Conqueress"} }, + "Mitra", "Crom", "Set", /* Hyborian */ + "Bar", "the Camp of the Duali Tribe", "the Duali Oasis", + PM_BARBARIAN, NON_PM, NON_PM, + PM_PELIAS, PM_CHIEFTAIN, PM_THOTH_AMON, + PM_OGRE, PM_TROLL, S_OGRE, S_TROLL, + ART_HEART_OF_AHRIMAN, + MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE | + ROLE_NEUTRAL|ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 16, 7, 7, 15, 16, 6 }, + { 30, 6, 7, 20, 30, 7 }, + /* Init Lower Higher */ + { 14, 0, 0,10, 2, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },10, /* Energy */ + 10, 14, 0, 0, 8, A_INT, SPE_HASTE_SELF, -4 +}, +{ {"Caveman", "Cavewoman"}, { + {"Troglodyte", 0}, + {"Aborigine", 0}, + {"Wanderer", 0}, + {"Vagrant", 0}, + {"Wayfarer", 0}, + {"Roamer", 0}, + {"Nomad", 0}, + {"Rover", 0}, + {"Pioneer", 0} }, + "Anu", "_Ishtar", "Anshar", /* Babylonian */ + "Cav", "the Caves of the Ancestors", "the Dragon's Lair", + PM_CAVEMAN, PM_CAVEWOMAN, PM_LITTLE_DOG, + PM_SHAMAN_KARNOV, PM_NEANDERTHAL, PM_CHROMATIC_DRAGON, + PM_BUGBEAR, PM_HILL_GIANT, S_HUMANOID, S_GIANT, + ART_SCEPTRE_OF_MIGHT, + MH_HUMAN|MH_DWARF|MH_GNOME | ROLE_MALE|ROLE_FEMALE | + ROLE_LAWFUL|ROLE_NEUTRAL, + /* Str Int Wis Dex Con Cha */ + { 10, 7, 7, 7, 8, 6 }, + { 30, 6, 7, 20, 30, 7 }, + /* Init Lower Higher */ + { 14, 0, 0, 8, 2, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },10, /* Energy */ + 0, 12, 0, 1, 8, A_INT, SPE_DIG, -4 +}, +{ {"Healer", 0}, { + {"Rhizotomist", 0}, + {"Empiric", 0}, + {"Embalmer", 0}, + {"Dresser", 0}, + {"Medicus ossium", "Medica ossium"}, + {"Herbalist", 0}, + {"Magister", "Magistra"}, + {"Physician", 0}, + {"Chirurgeon", 0} }, + "_Athena", "Hermes", "Poseidon", /* Greek */ + "Hea", "the Temple of Epidaurus", "the Temple of Coeus", + PM_HEALER, NON_PM, NON_PM, + PM_HIPPOCRATES, PM_ATTENDANT, PM_CYCLOPS, + PM_GIANT_RAT, PM_SNAKE, S_RODENT, S_YETI, + ART_STAFF_OF_AESCULAPIUS, + MH_HUMAN|MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, + /* Str Int Wis Dex Con Cha */ + { 7, 7, 13, 7, 11, 16 }, + { 15, 20, 20, 15, 25, 5 }, + /* Init Lower Higher */ + { 11, 0, 0, 8, 1, 0 }, /* Hit points */ + { 1, 4, 0, 1, 0, 2 },20, /* Energy */ + 10, 3,-3, 2, 10, A_WIS, SPE_CURE_SICKNESS, -4 +}, +{ {"Knight", 0}, { + {"Gallant", 0}, + {"Esquire", 0}, + {"Bachelor", 0}, + {"Sergeant", 0}, + {"Knight", 0}, + {"Banneret", 0}, + {"Chevalier", "Chevaliere"}, + {"Seignieur", "Dame"}, + {"Paladin", 0} }, + "Lugh", "_Brigit", "Manannan Mac Lir", /* Celtic */ + "Kni", "Camelot Castle", "the Isle of Glass", + PM_KNIGHT, NON_PM, PM_PONY, + PM_KING_ARTHUR, PM_PAGE, PM_IXOTH, + PM_QUASIT, PM_OCHRE_JELLY, S_IMP, S_JELLY, + ART_MAGIC_MIRROR_OF_MERLIN, + MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, + /* Str Int Wis Dex Con Cha */ + { 13, 7, 14, 8, 10, 17 }, + { 30, 15, 15, 10, 20, 10 }, + /* Init Lower Higher */ + { 14, 0, 0, 8, 2, 0 }, /* Hit points */ + { 1, 4, 0, 1, 0, 2 },10, /* Energy */ + 10, 8,-2, 0, 9, A_WIS, SPE_TURN_UNDEAD, -4 +}, +{ {"Monk", 0}, { + {"Candidate", 0}, + {"Novice", 0}, + {"Initiate", 0}, + {"Student of Stones", 0}, + {"Student of Waters", 0}, + {"Student of Metals", 0}, + {"Student of Winds", 0}, + {"Student of Fire", 0}, + {"Master", 0} }, + "Shan Lai Ching", "Chih Sung-tzu", "Huan Ti", /* Chinese */ + "Mon", "the Monastery of Chan-Sune", + "the Monastery of the Earth-Lord", + PM_MONK, NON_PM, NON_PM, + PM_GRAND_MASTER, PM_ABBOT, PM_MASTER_KAEN, + PM_EARTH_ELEMENTAL, PM_XORN, S_ELEMENTAL, S_XORN, + ART_EYES_OF_THE_OVERWORLD, + MH_HUMAN | ROLE_MALE|ROLE_FEMALE | + ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 10, 7, 8, 8, 7, 7 }, + { 25, 10, 20, 20, 15, 10 }, + /* Init Lower Higher */ + { 12, 0, 0, 8, 1, 0 }, /* Hit points */ + { 2, 2, 0, 2, 0, 2 },10, /* Energy */ + 10, 8,-2, 2, 20, A_WIS, SPE_RESTORE_ABILITY, -4 +}, +{ {"Priest", "Priestess"}, { + {"Aspirant", 0}, + {"Acolyte", 0}, + {"Adept", 0}, + {"Priest", "Priestess"}, + {"Curate", 0}, + {"Canon", "Canoness"}, + {"Lama", 0}, + {"Patriarch", "Matriarch"}, + {"High Priest", "High Priestess"} }, + 0, 0, 0, /* chosen randomly from among the other roles */ + "Pri", "the Great Temple", "the Temple of Nalzok", + PM_PRIEST, PM_PRIESTESS, NON_PM, + PM_ARCH_PRIEST, PM_ACOLYTE, PM_NALZOK, + PM_HUMAN_ZOMBIE, PM_WRAITH, S_ZOMBIE, S_WRAITH, + ART_MITRE_OF_HOLINESS, + MH_HUMAN|MH_ELF | ROLE_MALE|ROLE_FEMALE | + ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 7, 7, 10, 7, 7, 7 }, + { 15, 10, 30, 15, 20, 10 }, + /* Init Lower Higher */ + { 12, 0, 0, 8, 1, 0 }, /* Hit points */ + { 4, 3, 0, 2, 0, 2 },10, /* Energy */ + 0, 3,-2, 2, 10, A_WIS, SPE_REMOVE_CURSE, -4 +}, + /* Note: Rogue precedes Ranger so that use of `-R' on the command line + retains its traditional meaning. */ +{ {"Rogue", 0}, { + {"Footpad", 0}, + {"Cutpurse", 0}, + {"Rogue", 0}, + {"Pilferer", 0}, + {"Robber", 0}, + {"Burglar", 0}, + {"Filcher", 0}, + {"Magsman", "Magswoman"}, + {"Thief", 0} }, + "Issek", "Mog", "Kos", /* Nehwon */ + "Rog", "the Thieves' Guild Hall", "the Assassins' Guild Hall", + PM_ROGUE, NON_PM, NON_PM, + PM_MASTER_OF_THIEVES, PM_THUG, PM_MASTER_ASSASSIN, + PM_LEPRECHAUN, PM_GUARDIAN_NAGA, S_NYMPH, S_NAGA, + ART_MASTER_KEY_OF_THIEVERY, + MH_HUMAN|MH_ORC | ROLE_MALE|ROLE_FEMALE | + ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 7, 7, 7, 10, 7, 6 }, + { 20, 10, 10, 30, 20, 10 }, + /* Init Lower Higher */ + { 10, 0, 0, 8, 1, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },11, /* Energy */ + 10, 8, 0, 1, 9, A_INT, SPE_DETECT_TREASURE, -4 +}, +{ {"Ranger", 0}, { +#if 0 /* OBSOLETE */ + {"Edhel", "Elleth"}, + {"Edhel", "Elleth"}, /* elf-maid */ + {"Ohtar", "Ohtie"}, /* warrior */ + {"Kano", /* commander (Q.) ['a] */ + "Kanie"}, /* educated guess, until further research- SAC */ + {"Arandur", /* king's servant, minister (Q.) - guess */ + "Aranduriel"}, /* educated guess */ + {"Hir", "Hiril"}, /* lord, lady (S.) ['ir] */ + {"Aredhel", "Arwen"}, /* noble elf, maiden (S.) */ + {"Ernil", "Elentariel"}, /* prince (S.), elf-maiden (Q.) */ + {"Elentar", "Elentari"}, /* Star-king, -queen (Q.) */ + "Solonor Thelandira", "Aerdrie Faenya", "Lolth", /* Elven */ +#endif + {"Tenderfoot", 0}, + {"Lookout", 0}, + {"Trailblazer", 0}, + {"Reconnoiterer", "Reconnoiteress"}, + {"Scout", 0}, + {"Arbalester", 0}, /* One skilled at crossbows */ + {"Archer", 0}, + {"Sharpshooter", 0}, + {"Marksman", "Markswoman"} }, + "Mercury", "_Venus", "Mars", /* Roman/planets */ + "Ran", "Orion's camp", "the cave of the wumpus", + PM_RANGER, NON_PM, PM_LITTLE_DOG /* Orion & canis major */, + PM_ORION, PM_HUNTER, PM_SCORPIUS, + PM_FOREST_CENTAUR, PM_SCORPION, S_CENTAUR, S_SPIDER, + ART_LONGBOW_OF_DIANA, + MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE | + ROLE_NEUTRAL|ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 13, 13, 13, 9, 13, 7 }, + { 30, 10, 10, 20, 20, 10 }, + /* Init Lower Higher */ + { 13, 0, 0, 6, 1, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },12, /* Energy */ + 10, 9, 2, 1, 10, A_INT, SPE_INVISIBILITY, -4 +}, +{ {"Samurai", 0}, { + {"Hatamoto", 0}, /* Banner Knight */ + {"Ronin", 0}, /* no allegiance */ + {"Ninja", "Kunoichi"}, /* secret society */ + {"Joshu", 0}, /* heads a castle */ + {"Ryoshu", 0}, /* has a territory */ + {"Kokushu", 0}, /* heads a province */ + {"Daimyo", 0}, /* a samurai lord */ + {"Kuge", 0}, /* Noble of the Court */ + {"Shogun", 0} },/* supreme commander, warlord */ + "_Amaterasu Omikami", "Raijin", "Susanowo", /* Japanese */ + "Sam", "the Castle of the Taro Clan", "the Shogun's Castle", + PM_SAMURAI, NON_PM, PM_LITTLE_DOG, + PM_LORD_SATO, PM_ROSHI, PM_ASHIKAGA_TAKAUJI, + PM_WOLF, PM_STALKER, S_DOG, S_ELEMENTAL, + ART_TSURUGI_OF_MURAMASA, + MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, + /* Str Int Wis Dex Con Cha */ + { 10, 8, 7, 10, 17, 6 }, + { 30, 10, 8, 30, 14, 8 }, + /* Init Lower Higher */ + { 13, 0, 0, 8, 1, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },11, /* Energy */ + 10, 10, 0, 0, 8, A_INT, SPE_CLAIRVOYANCE, -4 +}, +#ifdef TOURIST +{ {"Tourist", 0}, { + {"Rambler", 0}, + {"Sightseer", 0}, + {"Excursionist",0}, + {"Peregrinator","Peregrinatrix"}, + {"Traveler", 0}, + {"Journeyer", 0}, + {"Voyager", 0}, + {"Explorer", 0}, + {"Adventurer", 0} }, + "Blind Io", "_The Lady", "Offler", /* Discworld */ + "Tou", "Ankh-Morpork", "the Thieves' Guild Hall", + PM_TOURIST, NON_PM, NON_PM, + PM_TWOFLOWER, PM_GUIDE, PM_MASTER_OF_THIEVES, + PM_GIANT_SPIDER, PM_FOREST_CENTAUR, S_SPIDER, S_CENTAUR, + ART_YENDORIAN_EXPRESS_CARD, + MH_HUMAN | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, + /* Str Int Wis Dex Con Cha */ + { 7, 10, 6, 7, 7, 10 }, + { 15, 10, 10, 15, 30, 20 }, + /* Init Lower Higher */ + { 8, 0, 0, 8, 0, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },14, /* Energy */ + 0, 5, 1, 2, 10, A_INT, SPE_CHARM_MONSTER, -4 +}, +#endif +{ {"Valkyrie", 0}, { + {"Stripling", 0}, + {"Skirmisher", 0}, + {"Fighter", 0}, + {"Man-at-arms", "Woman-at-arms"}, + {"Warrior", 0}, + {"Swashbuckler",0}, + {"Hero", "Heroine"}, + {"Champion", 0}, + {"Lord", "Lady"} }, + "Tyr", "Odin", "Loki", /* Norse */ + "Val", "the Shrine of Destiny", "the cave of Surtur", + PM_VALKYRIE, NON_PM, NON_PM /*PM_WINTER_WOLF_CUB*/, + PM_NORN, PM_WARRIOR, PM_LORD_SURTUR, + PM_FIRE_ANT, PM_FIRE_GIANT, S_ANT, S_GIANT, + ART_ORB_OF_FATE, + MH_HUMAN|MH_DWARF | ROLE_FEMALE | ROLE_LAWFUL|ROLE_NEUTRAL, + /* Str Int Wis Dex Con Cha */ + { 10, 7, 7, 7, 10, 7 }, + { 30, 6, 7, 20, 30, 7 }, + /* Init Lower Higher */ + { 14, 0, 0, 8, 2, 0 }, /* Hit points */ + { 1, 0, 0, 1, 0, 1 },10, /* Energy */ + 0, 10,-2, 0, 9, A_WIS, SPE_CONE_OF_COLD, -4 +}, +{ {"Wizard", 0}, { + {"Evoker", 0}, + {"Conjurer", 0}, + {"Thaumaturge", 0}, + {"Magician", 0}, + {"Enchanter", "Enchantress"}, + {"Sorcerer", "Sorceress"}, + {"Necromancer", 0}, + {"Wizard", 0}, + {"Mage", 0} }, + "Ptah", "Thoth", "Anhur", /* Egyptian */ + "Wiz", "the Lonely Tower", "the Tower of Darkness", + PM_WIZARD, NON_PM, PM_KITTEN, + PM_NEFERET_THE_GREEN, PM_APPRENTICE, PM_DARK_ONE, + PM_VAMPIRE_BAT, PM_XORN, S_BAT, S_WRAITH, + ART_EYE_OF_THE_AETHIOPICA, + MH_HUMAN|MH_ELF|MH_GNOME|MH_ORC | ROLE_MALE|ROLE_FEMALE | + ROLE_NEUTRAL|ROLE_CHAOTIC, + /* Str Int Wis Dex Con Cha */ + { 7, 10, 7, 7, 7, 7 }, + { 10, 30, 10, 20, 20, 10 }, + /* Init Lower Higher */ + { 10, 0, 0, 8, 1, 0 }, /* Hit points */ + { 4, 3, 0, 2, 0, 3 },12, /* Energy */ + 0, 1, 0, 3, 10, A_INT, SPE_MAGIC_MISSILE, -4 +}, +/* Array terminator */ +{{0, 0}} +}; + + +/* The player's role, created at runtime from initial + * choices. This may be munged in role_init(). + */ +struct Role urole = +{ {"Undefined", 0}, { {0, 0}, {0, 0}, {0, 0}, + {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} }, + "L", "N", "C", "Xxx", "home", "locate", + NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, + NON_PM, NON_PM, 0, 0, 0, 0, + /* Str Int Wis Dex Con Cha */ + { 7, 7, 7, 7, 7, 7 }, + { 20, 15, 15, 20, 20, 10 }, + /* Init Lower Higher */ + { 10, 0, 0, 8, 1, 0 }, /* Hit points */ + { 2, 0, 0, 2, 0, 3 },14, /* Energy */ + 0, 10, 0, 0, 4, A_INT, 0, -3 +}; + + + +/* Table of all races */ +const struct Race races[] = { +{ "human", "human", "humanity", "Hum", + {"man", "woman"}, + PM_HUMAN, NON_PM, PM_HUMAN_MUMMY, PM_HUMAN_ZOMBIE, + MH_HUMAN | ROLE_MALE|ROLE_FEMALE | + ROLE_LAWFUL|ROLE_NEUTRAL|ROLE_CHAOTIC, + MH_HUMAN, 0, MH_GNOME|MH_ORC, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + { STR18(100), 18, 18, 18, 18, 18 }, + /* Init Lower Higher */ + { 2, 0, 0, 2, 1, 0 }, /* Hit points */ + { 1, 0, 2, 0, 2, 0 } /* Energy */ +}, +{ "elf", "elven", "elvenkind", "Elf", + {0, 0}, + PM_ELF, NON_PM, PM_ELF_MUMMY, PM_ELF_ZOMBIE, + MH_ELF | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC, + MH_ELF, MH_ELF, MH_ORC, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + { 18, 20, 20, 18, 16, 18 }, + /* Init Lower Higher */ + { 1, 0, 0, 1, 1, 0 }, /* Hit points */ + { 2, 0, 3, 0, 3, 0 } /* Energy */ +}, +{ "dwarf", "dwarven", "dwarvenkind", "Dwa", + {0, 0}, + PM_DWARF, NON_PM, PM_DWARF_MUMMY, PM_DWARF_ZOMBIE, + MH_DWARF | ROLE_MALE|ROLE_FEMALE | ROLE_LAWFUL, + MH_DWARF, MH_DWARF|MH_GNOME, MH_ORC, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + { STR18(100), 16, 16, 20, 20, 16 }, + /* Init Lower Higher */ + { 4, 0, 0, 3, 2, 0 }, /* Hit points */ + { 0, 0, 0, 0, 0, 0 } /* Energy */ +}, +{ "gnome", "gnomish", "gnomehood", "Gno", + {0, 0}, + PM_GNOME, NON_PM, PM_GNOME_MUMMY, PM_GNOME_ZOMBIE, + MH_GNOME | ROLE_MALE|ROLE_FEMALE | ROLE_NEUTRAL, + MH_GNOME, MH_DWARF|MH_GNOME, MH_HUMAN, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + {STR18(50),19, 18, 18, 18, 18 }, + /* Init Lower Higher */ + { 1, 0, 0, 1, 0, 0 }, /* Hit points */ + { 2, 0, 2, 0, 2, 0 } /* Energy */ +}, +{ "orc", "orcish", "orcdom", "Orc", + {0, 0}, + PM_ORC, NON_PM, PM_ORC_MUMMY, PM_ORC_ZOMBIE, + MH_ORC | ROLE_MALE|ROLE_FEMALE | ROLE_CHAOTIC, + MH_ORC, 0, MH_HUMAN|MH_ELF|MH_DWARF, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + {STR18(50),16, 16, 18, 18, 16 }, + /* Init Lower Higher */ + { 1, 0, 0, 1, 0, 0 }, /* Hit points */ + { 1, 0, 1, 0, 1, 0 } /* Energy */ +}, +/* Array terminator */ +{ 0, 0, 0, 0 }}; + + +/* The player's race, created at runtime from initial + * choices. This may be munged in role_init(). + */ +struct Race urace = +{ "something", "undefined", "something", "Xxx", + {0, 0}, + NON_PM, NON_PM, NON_PM, NON_PM, + 0, 0, 0, 0, + /* Str Int Wis Dex Con Cha */ + { 3, 3, 3, 3, 3, 3 }, + { STR18(100), 18, 18, 18, 18, 18 }, + /* Init Lower Higher */ + { 2, 0, 0, 2, 1, 0 }, /* Hit points */ + { 1, 0, 2, 0, 2, 0 } /* Energy */ +}; + + +/* Table of all genders */ +const struct Gender genders[] = { + {"male", "he", "him", "his", "Mal", ROLE_MALE}, + {"female", "she", "her", "her", "Fem", ROLE_FEMALE}, + {"neuter", "it", "it", "its", "Ntr", ROLE_NEUTER} +}; + + +/* Table of all alignments */ +const struct Align aligns[] = { + {"law", "lawful", "Law", ROLE_LAWFUL, A_LAWFUL}, + {"balance", "neutral", "Neu", ROLE_NEUTRAL, A_NEUTRAL}, + {"chaos", "chaotic", "Cha", ROLE_CHAOTIC, A_CHAOTIC}, + {"evil", "unaligned", "Una", 0, A_NONE} +}; + +STATIC_DCL char * FDECL(promptsep, (char *, int)); +STATIC_DCL int FDECL(role_gendercount, (int)); +STATIC_DCL int FDECL(race_alignmentcount, (int)); + +/* used by str2XXX() */ +static char NEARDATA randomstr[] = "random"; + + +boolean +validrole(rolenum) + int rolenum; +{ + return (rolenum >= 0 && rolenum < SIZE(roles)-1); +} + + +int +randrole() +{ + return (rn2(SIZE(roles)-1)); +} + + +int +str2role(str) + char *str; +{ + int i, len; + + /* Is str valid? */ + if (!str || !str[0]) + return ROLE_NONE; + + /* Match as much of str as is provided */ + len = strlen(str); + for (i = 0; roles[i].name.m; i++) { + /* Does it match the male name? */ + if (!strncmpi(str, roles[i].name.m, len)) + return i; + /* Or the female name? */ + if (roles[i].name.f && !strncmpi(str, roles[i].name.f, len)) + return i; + /* Or the filecode? */ + if (!strcmpi(str, roles[i].filecode)) + return i; + } + + if ((len == 1 && (*str == '*' || *str == '@')) || + !strncmpi(str, randomstr, len)) + return ROLE_RANDOM; + + /* Couldn't find anything appropriate */ + return ROLE_NONE; +} + + +boolean +validrace(rolenum, racenum) + int rolenum, racenum; +{ + /* Assumes validrole */ + return (racenum >= 0 && racenum < SIZE(races)-1 && + (roles[rolenum].allow & races[racenum].allow & ROLE_RACEMASK)); +} + + +int +randrace(rolenum) + int rolenum; +{ + int i, n = 0; + + /* Count the number of valid races */ + for (i = 0; races[i].noun; i++) + if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) + n++; + + /* Pick a random race */ + /* Use a factor of 100 in case of bad random number generators */ + if (n) n = rn2(n*100)/100; + for (i = 0; races[i].noun; i++) + if (roles[rolenum].allow & races[i].allow & ROLE_RACEMASK) { + if (n) n--; + else return (i); + } + + /* This role has no permitted races? */ + return (rn2(SIZE(races)-1)); +} + + +int +str2race(str) + char *str; +{ + int i, len; + + /* Is str valid? */ + if (!str || !str[0]) + return ROLE_NONE; + + /* Match as much of str as is provided */ + len = strlen(str); + for (i = 0; races[i].noun; i++) { + /* Does it match the noun? */ + if (!strncmpi(str, races[i].noun, len)) + return i; + /* Or the filecode? */ + if (!strcmpi(str, races[i].filecode)) + return i; + } + + if ((len == 1 && (*str == '*' || *str == '@')) || + !strncmpi(str, randomstr, len)) + return ROLE_RANDOM; + + /* Couldn't find anything appropriate */ + return ROLE_NONE; +} + + +boolean +validgend(rolenum, racenum, gendnum) + int rolenum, racenum, gendnum; +{ + /* Assumes validrole and validrace */ + return (gendnum >= 0 && gendnum < ROLE_GENDERS && + (roles[rolenum].allow & races[racenum].allow & + genders[gendnum].allow & ROLE_GENDMASK)); +} + + +int +randgend(rolenum, racenum) + int rolenum, racenum; +{ + int i, n = 0; + + /* Count the number of valid genders */ + for (i = 0; i < ROLE_GENDERS; i++) + if (roles[rolenum].allow & races[racenum].allow & + genders[i].allow & ROLE_GENDMASK) + n++; + + /* Pick a random gender */ + if (n) n = rn2(n); + for (i = 0; i < ROLE_GENDERS; i++) + if (roles[rolenum].allow & races[racenum].allow & + genders[i].allow & ROLE_GENDMASK) { + if (n) n--; + else return (i); + } + + /* This role/race has no permitted genders? */ + return (rn2(ROLE_GENDERS)); +} + + +int +str2gend(str) + char *str; +{ + int i, len; + + /* Is str valid? */ + if (!str || !str[0]) + return ROLE_NONE; + + /* Match as much of str as is provided */ + len = strlen(str); + for (i = 0; i < ROLE_GENDERS; i++) { + /* Does it match the adjective? */ + if (!strncmpi(str, genders[i].adj, len)) + return i; + /* Or the filecode? */ + if (!strcmpi(str, genders[i].filecode)) + return i; + } + if ((len == 1 && (*str == '*' || *str == '@')) || + !strncmpi(str, randomstr, len)) + return ROLE_RANDOM; + + /* Couldn't find anything appropriate */ + return ROLE_NONE; +} + + +boolean +validalign(rolenum, racenum, alignnum) + int rolenum, racenum, alignnum; +{ + /* Assumes validrole and validrace */ + return (alignnum >= 0 && alignnum < ROLE_ALIGNS && + (roles[rolenum].allow & races[racenum].allow & + aligns[alignnum].allow & ROLE_ALIGNMASK)); +} + + +int +randalign(rolenum, racenum) + int rolenum, racenum; +{ + int i, n = 0; + + /* Count the number of valid alignments */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (roles[rolenum].allow & races[racenum].allow & + aligns[i].allow & ROLE_ALIGNMASK) + n++; + + /* Pick a random alignment */ + if (n) n = rn2(n); + for (i = 0; i < ROLE_ALIGNS; i++) + if (roles[rolenum].allow & races[racenum].allow & + aligns[i].allow & ROLE_ALIGNMASK) { + if (n) n--; + else return (i); + } + + /* This role/race has no permitted alignments? */ + return (rn2(ROLE_ALIGNS)); +} + + +int +str2align(str) + char *str; +{ + int i, len; + + /* Is str valid? */ + if (!str || !str[0]) + return ROLE_NONE; + + /* Match as much of str as is provided */ + len = strlen(str); + for (i = 0; i < ROLE_ALIGNS; i++) { + /* Does it match the adjective? */ + if (!strncmpi(str, aligns[i].adj, len)) + return i; + /* Or the filecode? */ + if (!strcmpi(str, aligns[i].filecode)) + return i; + } + if ((len == 1 && (*str == '*' || *str == '@')) || + !strncmpi(str, randomstr, len)) + return ROLE_RANDOM; + + /* Couldn't find anything appropriate */ + return ROLE_NONE; +} + +/* is rolenum compatible with any racenum/gendnum/alignnum constraints? */ +boolean +ok_role(rolenum, racenum, gendnum, alignnum) +int rolenum, racenum, gendnum, alignnum; +{ + int i; + short allow; + + if (rolenum >= 0 && rolenum < SIZE(roles)-1) { + allow = roles[rolenum].allow; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_RACEMASK)) + return FALSE; + if (gendnum >= 0 && gendnum < ROLE_GENDERS && + !(allow & genders[gendnum].allow & ROLE_GENDMASK)) + return FALSE; + if (alignnum >= 0 && alignnum < ROLE_ALIGNS && + !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) + return FALSE; + return TRUE; + } else { + for (i = 0; i < SIZE(roles)-1; i++) { + allow = roles[i].allow; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_RACEMASK)) + continue; + if (gendnum >= 0 && gendnum < ROLE_GENDERS && + !(allow & genders[gendnum].allow & ROLE_GENDMASK)) + continue; + if (alignnum >= 0 && alignnum < ROLE_ALIGNS && + !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) + continue; + return TRUE; + } + return FALSE; + } +} + +/* pick a random role subject to any racenum/gendnum/alignnum constraints */ +/* If pickhow == PICK_RIGID a role is returned only if there is */ +/* a single possibility */ +int +pick_role(racenum, gendnum, alignnum, pickhow) +int racenum, gendnum, alignnum, pickhow; +{ + int i; + int roles_ok = 0; + + for (i = 0; i < SIZE(roles)-1; i++) { + if (ok_role(i, racenum, gendnum, alignnum)) + roles_ok++; + } + if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID)) + return ROLE_NONE; + roles_ok = rn2(roles_ok); + for (i = 0; i < SIZE(roles)-1; i++) { + if (ok_role(i, racenum, gendnum, alignnum)) { + if (roles_ok == 0) + return i; + else + roles_ok--; + } + } + return ROLE_NONE; +} + +/* is racenum compatible with any rolenum/gendnum/alignnum constraints? */ +boolean +ok_race(rolenum, racenum, gendnum, alignnum) +int rolenum, racenum, gendnum, alignnum; +{ + int i; + short allow; + + if (racenum >= 0 && racenum < SIZE(races)-1) { + allow = races[racenum].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_RACEMASK)) + return FALSE; + if (gendnum >= 0 && gendnum < ROLE_GENDERS && + !(allow & genders[gendnum].allow & ROLE_GENDMASK)) + return FALSE; + if (alignnum >= 0 && alignnum < ROLE_ALIGNS && + !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) + return FALSE; + return TRUE; + } else { + for (i = 0; i < SIZE(races)-1; i++) { + allow = races[i].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_RACEMASK)) + continue; + if (gendnum >= 0 && gendnum < ROLE_GENDERS && + !(allow & genders[gendnum].allow & ROLE_GENDMASK)) + continue; + if (alignnum >= 0 && alignnum < ROLE_ALIGNS && + !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) + continue; + return TRUE; + } + return FALSE; + } +} + +/* pick a random race subject to any rolenum/gendnum/alignnum constraints */ +/* If pickhow == PICK_RIGID a race is returned only if there is */ +/* a single possibility */ +int +pick_race(rolenum, gendnum, alignnum, pickhow) +int rolenum, gendnum, alignnum, pickhow; +{ + int i; + int races_ok = 0; + + for (i = 0; i < SIZE(races)-1; i++) { + if (ok_race(rolenum, i, gendnum, alignnum)) + races_ok++; + } + if (races_ok == 0 || (races_ok > 1 && pickhow == PICK_RIGID)) + return ROLE_NONE; + races_ok = rn2(races_ok); + for (i = 0; i < SIZE(races)-1; i++) { + if (ok_race(rolenum, i, gendnum, alignnum)) { + if (races_ok == 0) + return i; + else + races_ok--; + } + } + return ROLE_NONE; +} + +/* is gendnum compatible with any rolenum/racenum/alignnum constraints? */ +/* gender and alignment are not comparable (and also not constrainable) */ +boolean +ok_gend(rolenum, racenum, gendnum, alignnum) +int rolenum, racenum, gendnum, alignnum; +{ + int i; + short allow; + + if (gendnum >= 0 && gendnum < ROLE_GENDERS) { + allow = genders[gendnum].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_GENDMASK)) + return FALSE; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_GENDMASK)) + return FALSE; + return TRUE; + } else { + for (i = 0; i < ROLE_GENDERS; i++) { + allow = genders[i].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_GENDMASK)) + continue; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_GENDMASK)) + continue; + return TRUE; + } + return FALSE; + } +} + +/* pick a random gender subject to any rolenum/racenum/alignnum constraints */ +/* gender and alignment are not comparable (and also not constrainable) */ +/* If pickhow == PICK_RIGID a gender is returned only if there is */ +/* a single possibility */ +int +pick_gend(rolenum, racenum, alignnum, pickhow) +int rolenum, racenum, alignnum, pickhow; +{ + int i; + int gends_ok = 0; + + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(rolenum, racenum, i, alignnum)) + gends_ok++; + } + if (gends_ok == 0 || (gends_ok > 1 && pickhow == PICK_RIGID)) + return ROLE_NONE; + gends_ok = rn2(gends_ok); + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(rolenum, racenum, i, alignnum)) { + if (gends_ok == 0) + return i; + else + gends_ok--; + } + } + return ROLE_NONE; +} + +/* is alignnum compatible with any rolenum/racenum/gendnum constraints? */ +/* alignment and gender are not comparable (and also not constrainable) */ +boolean +ok_align(rolenum, racenum, gendnum, alignnum) +int rolenum, racenum, gendnum, alignnum; +{ + int i; + short allow; + + if (alignnum >= 0 && alignnum < ROLE_ALIGNS) { + allow = aligns[alignnum].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) + return FALSE; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_ALIGNMASK)) + return FALSE; + return TRUE; + } else { + for (i = 0; i < ROLE_ALIGNS; i++) { + allow = races[i].allow; + if (rolenum >= 0 && rolenum < SIZE(roles)-1 && + !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) + continue; + if (racenum >= 0 && racenum < SIZE(races)-1 && + !(allow & races[racenum].allow & ROLE_ALIGNMASK)) + continue; + return TRUE; + } + return FALSE; + } +} + +/* pick a random alignment subject to any rolenum/racenum/gendnum constraints */ +/* alignment and gender are not comparable (and also not constrainable) */ +/* If pickhow == PICK_RIGID an alignment is returned only if there is */ +/* a single possibility */ +int +pick_align(rolenum, racenum, gendnum, pickhow) +int rolenum, racenum, gendnum, pickhow; +{ + int i; + int aligns_ok = 0; + + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(rolenum, racenum, gendnum, i)) + aligns_ok++; + } + if (aligns_ok == 0 || (aligns_ok > 1 && pickhow == PICK_RIGID)) + return ROLE_NONE; + aligns_ok = rn2(aligns_ok); + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(rolenum, racenum, gendnum, i)) { + if (aligns_ok == 0) + return i; + else + aligns_ok--; + } + } + return ROLE_NONE; +} + +void +rigid_role_checks() +{ + /* Some roles are limited to a single race, alignment, or gender and + * calling this routine prior to XXX_player_selection() will help + * prevent an extraneous prompt that actually doesn't allow + * you to choose anything further. Note the use of PICK_RIGID which + * causes the pick_XX() routine to return a value only if there is one + * single possible selection, otherwise it returns ROLE_NONE. + * + */ + if (flags.initrole == ROLE_RANDOM) { + /* If the role was explicitly specified as ROLE_RANDOM + * via -uXXXX-@ then choose the role in here to narrow down + * later choices. Pick a random role in this case. + */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) + flags.initrole = randrole(); + } + if (flags.initrole != ROLE_NONE) { + if (flags.initrace == ROLE_NONE) + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RIGID); + if (flags.initalign == ROLE_NONE) + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RIGID); + if (flags.initgend == ROLE_NONE) + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RIGID); + } +} + +#define BP_ALIGN 0 +#define BP_GEND 1 +#define BP_RACE 2 +#define BP_ROLE 3 +#define NUM_BP 4 + +STATIC_VAR char pa[NUM_BP], post_attribs; + +STATIC_OVL char * +promptsep(buf, num_post_attribs) +char *buf; +int num_post_attribs; +{ + const char *conj = "and "; + if (num_post_attribs > 1 + && post_attribs < num_post_attribs && post_attribs > 1) + Strcat(buf, ","); + Strcat(buf, " "); + --post_attribs; + if (!post_attribs && num_post_attribs > 1) Strcat(buf, conj); + return buf; +} + +STATIC_OVL int +role_gendercount(rolenum) +int rolenum; +{ + int gendcount = 0; + if (validrole(rolenum)) { + if (roles[rolenum].allow & ROLE_MALE) ++gendcount; + if (roles[rolenum].allow & ROLE_FEMALE) ++gendcount; + if (roles[rolenum].allow & ROLE_NEUTER) ++gendcount; + } + return gendcount; +} + +STATIC_OVL int +race_alignmentcount(racenum) +int racenum; +{ + int aligncount = 0; + if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) { + if (races[racenum].allow & ROLE_CHAOTIC) ++aligncount; + if (races[racenum].allow & ROLE_LAWFUL) ++aligncount; + if (races[racenum].allow & ROLE_NEUTRAL) ++aligncount; + } + return aligncount; +} + +char * +root_plselection_prompt(suppliedbuf, buflen, rolenum, racenum, gendnum, alignnum) +char *suppliedbuf; +int buflen, rolenum, racenum, gendnum, alignnum; +{ + int k, gendercount = 0, aligncount = 0; + char buf[BUFSZ]; + static char err_ret[] = " character's"; + boolean donefirst = FALSE; + + if (!suppliedbuf || buflen < 1) return err_ret; + + /* initialize these static variables each time this is called */ + post_attribs = 0; + for (k=0; k < NUM_BP; ++k) + pa[k] = 0; + buf[0] = '\0'; + *suppliedbuf = '\0'; + + /* How many alignments are allowed for the desired race? */ + if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) + aligncount = race_alignmentcount(racenum); + + if (alignnum != ROLE_NONE && alignnum != ROLE_RANDOM) { + /* if race specified, and multiple choice of alignments for it */ + if ((racenum >= 0) && (aligncount > 1)) { + if (donefirst) Strcat(buf, " "); + Strcat(buf, aligns[alignnum].adj); + donefirst = TRUE; + } else { + if (donefirst) Strcat(buf, " "); + Strcat(buf, aligns[alignnum].adj); + donefirst = TRUE; + } + } else { + /* if alignment not specified, but race is specified + and only one choice of alignment for that race then + don't include it in the later list */ + if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM) && + ok_race(rolenum, racenum, gendnum, alignnum)) + && (aligncount > 1)) + || (racenum == ROLE_NONE || racenum == ROLE_RANDOM)) { + pa[BP_ALIGN] = 1; + post_attribs++; + } + } + /* */ + + /* How many genders are allowed for the desired role? */ + if (validrole(rolenum)) + gendercount = role_gendercount(rolenum); + + if (gendnum != ROLE_NONE && gendnum != ROLE_RANDOM) { + if (validrole(rolenum)) { + /* if role specified, and multiple choice of genders for it, + and name of role itself does not distinguish gender */ + if ((rolenum != ROLE_NONE) && (gendercount > 1) + && !roles[rolenum].name.f) { + if (donefirst) Strcat(buf, " "); + Strcat(buf, genders[gendnum].adj); + donefirst = TRUE; + } + } else { + if (donefirst) Strcat(buf, " "); + Strcat(buf, genders[gendnum].adj); + donefirst = TRUE; + } + } else { + /* if gender not specified, but role is specified + and only one choice of gender then + don't include it in the later list */ + if ((validrole(rolenum) && (gendercount > 1)) || !validrole(rolenum)) { + pa[BP_GEND] = 1; + post_attribs++; + } + } + /* */ + + if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) { + if (validrole(rolenum) && ok_race(rolenum, racenum, gendnum, alignnum)) { + if (donefirst) Strcat(buf, " "); + Strcat(buf, (rolenum == ROLE_NONE) ? + races[racenum].noun : + races[racenum].adj); + donefirst = TRUE; + } else if (!validrole(rolenum)) { + if (donefirst) Strcat(buf, " "); + Strcat(buf, races[racenum].noun); + donefirst = TRUE; + } else { + pa[BP_RACE] = 1; + post_attribs++; + } + } else { + pa[BP_RACE] = 1; + post_attribs++; + } + /* || */ + + if (validrole(rolenum)) { + if (donefirst) Strcat(buf, " "); + if (gendnum != ROLE_NONE) { + if (gendnum == 1 && roles[rolenum].name.f) + Strcat(buf, roles[rolenum].name.f); + else + Strcat(buf, roles[rolenum].name.m); + } else { + if (roles[rolenum].name.f) { + Strcat(buf, roles[rolenum].name.m); + Strcat(buf, "/"); + Strcat(buf, roles[rolenum].name.f); + } else + Strcat(buf, roles[rolenum].name.m); + } + donefirst = TRUE; + } else if (rolenum == ROLE_NONE) { + pa[BP_ROLE] = 1; + post_attribs++; + } + + if ((racenum == ROLE_NONE || racenum == ROLE_RANDOM) && !validrole(rolenum)) { + if (donefirst) Strcat(buf, " "); + Strcat(buf, "character"); + donefirst = TRUE; + } + /* || + * || + */ + if (buflen > (int) (strlen(buf) + 1)) { + Strcpy(suppliedbuf, buf); + return suppliedbuf; + } else + return err_ret; +} + +char * +build_plselection_prompt(buf, buflen, rolenum, racenum, gendnum, alignnum) +char *buf; +int buflen, rolenum, racenum, gendnum, alignnum; +{ + const char *defprompt = "Shall I pick a character for you? [ynq] "; + int num_post_attribs = 0; + char tmpbuf[BUFSZ]; + + if (buflen < QBUFSZ) + return (char *)defprompt; + + Strcpy(tmpbuf, "Shall I pick "); + if (racenum != ROLE_NONE || validrole(rolenum)) + Strcat(tmpbuf, "your "); + else { + Strcat(tmpbuf, "a "); + } + /* */ + + (void) root_plselection_prompt(eos(tmpbuf), buflen - strlen(tmpbuf), + rolenum, racenum, gendnum, alignnum); + Sprintf(buf, "%s", s_suffix(tmpbuf)); + + /* buf should now be: + * < your lawful female gnomish cavewoman's> || + * || + * + * Now append the post attributes to it + */ + + num_post_attribs = post_attribs; + if (post_attribs) { + if (pa[BP_RACE]) { + (void) promptsep(eos(buf), num_post_attribs); + Strcat(buf, "race"); + } + if (pa[BP_ROLE]) { + (void) promptsep(eos(buf), num_post_attribs); + Strcat(buf, "role"); + } + if (pa[BP_GEND]) { + (void) promptsep(eos(buf), num_post_attribs); + Strcat(buf, "gender"); + } + if (pa[BP_ALIGN]) { + (void) promptsep(eos(buf), num_post_attribs); + Strcat(buf, "alignment"); + } + } + Strcat(buf, " for you? [ynq] "); + return buf; +} + +#undef BP_ALIGN +#undef BP_GEND +#undef BP_RACE +#undef BP_ROLE +#undef NUM_BP + +void +plnamesuffix() +{ + char *sptr, *eptr; + int i; + + /* Look for tokens delimited by '-' */ + if ((eptr = index(plname, '-')) != (char *) 0) + *eptr++ = '\0'; + while (eptr) { + /* Isolate the next token */ + sptr = eptr; + if ((eptr = index(sptr, '-')) != (char *)0) + *eptr++ = '\0'; + + /* Try to match it to something */ + if ((i = str2role(sptr)) != ROLE_NONE) + flags.initrole = i; + else if ((i = str2race(sptr)) != ROLE_NONE) + flags.initrace = i; + else if ((i = str2gend(sptr)) != ROLE_NONE) + flags.initgend = i; + else if ((i = str2align(sptr)) != ROLE_NONE) + flags.initalign = i; + } + if(!plname[0]) { + askname(); + plnamesuffix(); + } + + /* commas in the plname confuse the record file, convert to spaces */ + for (sptr = plname; *sptr; sptr++) { + if (*sptr == ',') *sptr = ' '; + } +} + + +/* + * Special setup modifications here: + * + * Unfortunately, this is going to have to be done + * on each newgame or restore, because you lose the permonst mods + * across a save/restore. :-) + * + * 1 - The Rogue Leader is the Tourist Nemesis. + * 2 - Priests start with a random alignment - convert the leader and + * guardians here. + * 3 - Elves can have one of two different leaders, but can't work it + * out here because it requires hacking the level file data (see + * sp_lev.c). + * + * This code also replaces quest_init(). + */ +void +role_init() +{ + int alignmnt; + + /* Strip the role letter out of the player name. + * This is included for backwards compatibility. + */ + plnamesuffix(); + + /* Check for a valid role. Try flags.initrole first. */ + if (!validrole(flags.initrole)) { + /* Try the player letter second */ + if ((flags.initrole = str2role(pl_character)) < 0) + /* None specified; pick a random role */ + flags.initrole = randrole(); + } + + /* We now have a valid role index. Copy the role name back. */ + /* This should become OBSOLETE */ + Strcpy(pl_character, roles[flags.initrole].name.m); + pl_character[PL_CSIZ-1] = '\0'; + + /* Check for a valid race */ + if (!validrace(flags.initrole, flags.initrace)) + flags.initrace = randrace(flags.initrole); + + /* Check for a valid gender. If new game, check both initgend + * and female. On restore, assume flags.female is correct. */ + if (flags.pantheon == -1) { /* new game */ + if (!validgend(flags.initrole, flags.initrace, flags.female)) + flags.female = !flags.female; + } + if (!validgend(flags.initrole, flags.initrace, flags.initgend)) + /* Note that there is no way to check for an unspecified gender. */ + flags.initgend = flags.female; + + /* Check for a valid alignment */ + if (!validalign(flags.initrole, flags.initrace, flags.initalign)) + /* Pick a random alignment */ + flags.initalign = randalign(flags.initrole, flags.initrace); + alignmnt = aligns[flags.initalign].value; + + /* Initialize urole and urace */ + urole = roles[flags.initrole]; + urace = races[flags.initrace]; + + /* Fix up the quest leader */ + if (urole.ldrnum != NON_PM) { + mons[urole.ldrnum].msound = MS_LEADER; + mons[urole.ldrnum].mflags2 |= (M2_PEACEFUL); + mons[urole.ldrnum].mflags3 |= M3_CLOSE; + mons[urole.ldrnum].maligntyp = alignmnt * 3; + } + + /* Fix up the quest guardians */ + if (urole.guardnum != NON_PM) { + mons[urole.guardnum].mflags2 |= (M2_PEACEFUL); + mons[urole.guardnum].maligntyp = alignmnt * 3; + } + + /* Fix up the quest nemesis */ + if (urole.neminum != NON_PM) { + mons[urole.neminum].msound = MS_NEMESIS; + mons[urole.neminum].mflags2 &= ~(M2_PEACEFUL); + mons[urole.neminum].mflags2 |= (M2_NASTY|M2_STALK|M2_HOSTILE); + mons[urole.neminum].mflags3 |= M3_WANTSARTI | M3_WAITFORU; + } + + /* Fix up the god names */ + if (flags.pantheon == -1) { /* new game */ + flags.pantheon = flags.initrole; /* use own gods */ + while (!roles[flags.pantheon].lgod) /* unless they're missing */ + flags.pantheon = randrole(); + } + if (!urole.lgod) { + urole.lgod = roles[flags.pantheon].lgod; + urole.ngod = roles[flags.pantheon].ngod; + urole.cgod = roles[flags.pantheon].cgod; + } + + /* Fix up infravision */ + if (mons[urace.malenum].mflags3 & M3_INFRAVISION) { + /* although an infravision intrinsic is possible, infravision + * is purely a property of the physical race. This means that we + * must put the infravision flag in the player's current race + * (either that or have separate permonst entries for + * elven/non-elven members of each class). The side effect is that + * all NPCs of that class will have (probably bogus) infravision, + * but since infravision has no effect for NPCs anyway we can + * ignore this. + */ + mons[urole.malenum].mflags3 |= M3_INFRAVISION; + if (urole.femalenum != NON_PM) + mons[urole.femalenum].mflags3 |= M3_INFRAVISION; + } + + /* Artifacts are fixed in hack_artifacts() */ + + /* Success! */ + return; +} + +const char * +Hello(mtmp) +struct monst *mtmp; +{ + switch (Role_switch) { + case PM_KNIGHT: + return ("Salutations"); /* Olde English */ + case PM_SAMURAI: + return (mtmp && mtmp->data == &mons[PM_SHOPKEEPER] ? + "Irasshaimase" : "Konnichi wa"); /* Japanese */ +#ifdef TOURIST + case PM_TOURIST: + return ("Aloha"); /* Hawaiian */ +#endif + case PM_VALKYRIE: + return ( +#ifdef MAIL + mtmp && mtmp->data == &mons[PM_MAIL_DAEMON] ? "Hallo" : +#endif + "Velkommen"); /* Norse */ + default: + return ("Hello"); + } +} + +const char * +Goodbye() +{ + switch (Role_switch) { + case PM_KNIGHT: + return ("Fare thee well"); /* Olde English */ + case PM_SAMURAI: + return ("Sayonara"); /* Japanese */ +#ifdef TOURIST + case PM_TOURIST: + return ("Aloha"); /* Hawaiian */ +#endif + case PM_VALKYRIE: + return ("Farvel"); /* Norse */ + default: + return ("Goodbye"); + } +} + +/* role.c */ diff --git a/src/rumors.c b/src/rumors.c new file mode 100644 index 0000000..3272d1c --- /dev/null +++ b/src/rumors.c @@ -0,0 +1,382 @@ +/* SCCS Id: @(#)rumors.c 3.4 1996/04/20 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" +#include "dlb.h" + +/* [note: this comment is fairly old, but still accurate for 3.1] + * Rumors have been entirely rewritten to speed up the access. This is + * essential when working from floppies. Using fseek() the way that's done + * here means rumors following longer rumors are output more often than those + * following shorter rumors. Also, you may see the same rumor more than once + * in a particular game (although the odds are highly against it), but + * this also happens with real fortune cookies. -dgk + */ + +/* 3.1 + * The rumors file consists of a "do not edit" line, a hexadecimal number + * giving the number of bytes of useful/true rumors, followed by those + * true rumors (one per line), followed by the useless/false/misleading/cute + * rumors (also one per line). Number of bytes of untrue rumors is derived + * via fseek(EOF)+ftell(). + * + * The oracles file consists of a "do not edit" comment, a decimal count N + * and set of N+1 hexadecimal fseek offsets, followed by N multiple-line + * records, separated by "---" lines. The first oracle is a special case, + * and placed there by 'makedefs'. + */ + +STATIC_DCL void FDECL(init_rumors, (dlb *)); +STATIC_DCL void FDECL(init_oracles, (dlb *)); + +static long true_rumor_start, true_rumor_size, true_rumor_end, + false_rumor_start, false_rumor_size, false_rumor_end; +static int oracle_flg = 0; /* -1=>don't use, 0=>need init, 1=>init done */ +static unsigned oracle_cnt = 0; +static long *oracle_loc = 0; + +STATIC_OVL void +init_rumors(fp) +dlb *fp; +{ + char line[BUFSZ]; + + (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment */ + (void) dlb_fgets(line, sizeof line, fp); + if (sscanf(line, "%6lx\n", &true_rumor_size) == 1 && + true_rumor_size > 0L) { + (void) dlb_fseek(fp, 0L, SEEK_CUR); + true_rumor_start = dlb_ftell(fp); + true_rumor_end = true_rumor_start + true_rumor_size; + (void) dlb_fseek(fp, 0L, SEEK_END); + false_rumor_end = dlb_ftell(fp); + false_rumor_start = true_rumor_end; /* ok, so it's redundant... */ + false_rumor_size = false_rumor_end - false_rumor_start; + } else + true_rumor_size = -1L; /* init failed */ +} + +/* exclude_cookie is a hack used because we sometimes want to get rumors in a + * context where messages such as "You swallowed the fortune!" that refer to + * cookies should not appear. This has no effect for true rumors since none + * of them contain such references anyway. + */ +char * +getrumor(truth, rumor_buf, exclude_cookie) +int truth; /* 1=true, -1=false, 0=either */ +char *rumor_buf; +boolean exclude_cookie; +{ + dlb *rumors; + long tidbit, beginning; + char *endp, line[BUFSZ], xbuf[BUFSZ]; + + rumor_buf[0] = '\0'; + if (true_rumor_size < 0L) /* we couldn't open RUMORFILE */ + return rumor_buf; + + rumors = dlb_fopen(RUMORFILE, "r"); + + if (rumors) { + int count = 0; + int adjtruth; + + do { + rumor_buf[0] = '\0'; + if (true_rumor_size == 0L) { /* if this is 1st outrumor() */ + init_rumors(rumors); + if (true_rumor_size < 0L) { /* init failed */ + Sprintf(rumor_buf, "Error reading \"%.80s\".", + RUMORFILE); + return rumor_buf; + } + } + /* + * input: 1 0 -1 + * rn2 \ +1 2=T 1=T 0=F + * adj./ +0 1=T 0=F -1=F + */ + switch (adjtruth = truth + rn2(2)) { + case 2: /*(might let a bogus input arg sneak thru)*/ + case 1: beginning = true_rumor_start; + tidbit = Rand() % true_rumor_size; + break; + case 0: /* once here, 0 => false rather than "either"*/ + case -1: beginning = false_rumor_start; + tidbit = Rand() % false_rumor_size; + break; + default: + impossible("strange truth value for rumor"); + return strcpy(rumor_buf, "Oops..."); + } + (void) dlb_fseek(rumors, beginning + tidbit, SEEK_SET); + (void) dlb_fgets(line, sizeof line, rumors); + if (!dlb_fgets(line, sizeof line, rumors) || + (adjtruth > 0 && dlb_ftell(rumors) > true_rumor_end)) { + /* reached end of rumors -- go back to beginning */ + (void) dlb_fseek(rumors, beginning, SEEK_SET); + (void) dlb_fgets(line, sizeof line, rumors); + } + if ((endp = index(line, '\n')) != 0) *endp = 0; + Strcat(rumor_buf, xcrypt(line, xbuf)); + } while(count++ < 50 && exclude_cookie && (strstri(rumor_buf, "fortune") || strstri(rumor_buf, "pity"))); + (void) dlb_fclose(rumors); + if (count >= 50) + impossible("Can't find non-cookie rumor?"); + else + exercise(A_WIS, (adjtruth > 0)); + } else { + pline("Can't open rumors file!"); + true_rumor_size = -1; /* don't try to open it again */ + } + return rumor_buf; +} + +void +outrumor(truth, mechanism) +int truth; /* 1=true, -1=false, 0=either */ +int mechanism; +{ + static const char fortune_msg[] = + "This cookie has a scrap of paper inside."; + const char *line; + char buf[BUFSZ]; + boolean reading = (mechanism == BY_COOKIE || + mechanism == BY_PAPER); + + if (reading) { + /* deal with various things that prevent reading */ + if (is_fainted() && mechanism == BY_COOKIE) + return; + else if (Blind) { + if (mechanism == BY_COOKIE) + pline(fortune_msg); + pline("What a pity that you cannot read it!"); + return; + } + } + line = getrumor(truth, buf, reading ? FALSE : TRUE); + if (!*line) + line = "NetHack rumors file closed for renovation."; + switch (mechanism) { + case BY_ORACLE: + /* Oracle delivers the rumor */ + pline("True to her word, the Oracle %ssays: ", + (!rn2(4) ? "offhandedly " : (!rn2(3) ? "casually " : + (rn2(2) ? "nonchalantly " : "")))); + verbalize("%s", line); + exercise(A_WIS, TRUE); + return; + case BY_COOKIE: + pline(fortune_msg); + /* FALLTHRU */ + case BY_PAPER: + pline("It reads:"); + break; + } + pline("%s", line); +} + +STATIC_OVL void +init_oracles(fp) +dlb *fp; +{ + register int i; + char line[BUFSZ]; + int cnt = 0; + + /* this assumes we're only called once */ + (void) dlb_fgets(line, sizeof line, fp); /* skip "don't edit" comment*/ + (void) dlb_fgets(line, sizeof line, fp); + if (sscanf(line, "%5d\n", &cnt) == 1 && cnt > 0) { + oracle_cnt = (unsigned) cnt; + oracle_loc = (long *) alloc((unsigned)cnt * sizeof (long)); + for (i = 0; i < cnt; i++) { + (void) dlb_fgets(line, sizeof line, fp); + (void) sscanf(line, "%5lx\n", &oracle_loc[i]); + } + } + return; +} + +void +save_oracles(fd, mode) +int fd, mode; +{ + if (perform_bwrite(mode)) { + bwrite(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt); + if (oracle_cnt) + bwrite(fd, (genericptr_t)oracle_loc, oracle_cnt*sizeof (long)); + } + if (release_data(mode)) { + if (oracle_cnt) { + free((genericptr_t)oracle_loc); + oracle_loc = 0, oracle_cnt = 0, oracle_flg = 0; + } + } +} + +void +restore_oracles(fd) +int fd; +{ + mread(fd, (genericptr_t) &oracle_cnt, sizeof oracle_cnt); + if (oracle_cnt) { + oracle_loc = (long *) alloc(oracle_cnt * sizeof (long)); + mread(fd, (genericptr_t) oracle_loc, oracle_cnt * sizeof (long)); + oracle_flg = 1; /* no need to call init_oracles() */ + } +} + +void +outoracle(special, delphi) +boolean special; +boolean delphi; +{ + char line[COLNO]; + char *endp; + dlb *oracles; + int oracle_idx; + char xbuf[BUFSZ]; + + if(oracle_flg < 0 || /* couldn't open ORACLEFILE */ + (oracle_flg > 0 && oracle_cnt == 0)) /* oracles already exhausted */ + return; + + oracles = dlb_fopen(ORACLEFILE, "r"); + + if (oracles) { + winid tmpwin; + if (oracle_flg == 0) { /* if this is the first outoracle() */ + init_oracles(oracles); + oracle_flg = 1; + if (oracle_cnt == 0) return; + } + /* oracle_loc[0] is the special oracle; */ + /* oracle_loc[1..oracle_cnt-1] are normal ones */ + if (oracle_cnt <= 1 && !special) return; /*(shouldn't happen)*/ + oracle_idx = special ? 0 : rnd((int) oracle_cnt - 1); + (void) dlb_fseek(oracles, oracle_loc[oracle_idx], SEEK_SET); + if (!special) oracle_loc[oracle_idx] = oracle_loc[--oracle_cnt]; + + tmpwin = create_nhwindow(NHW_TEXT); + if (delphi) + putstr(tmpwin, 0, special ? + "The Oracle scornfully takes all your money and says:" : + "The Oracle meditates for a moment and then intones:"); + else + putstr(tmpwin, 0, "The message reads:"); + putstr(tmpwin, 0, ""); + + while(dlb_fgets(line, COLNO, oracles) && strcmp(line,"---\n")) { + if ((endp = index(line, '\n')) != 0) *endp = 0; + putstr(tmpwin, 0, xcrypt(line, xbuf)); + } + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + (void) dlb_fclose(oracles); + } else { + pline("Can't open oracles file!"); + oracle_flg = -1; /* don't try to open it again */ + } +} + +int +doconsult(oracl) +register struct monst *oracl; +{ +#ifdef GOLDOBJ + long umoney = money_cnt(invent); +#endif + int u_pay, minor_cost = 50, major_cost = 500 + 50 * u.ulevel; + int add_xpts; + char qbuf[QBUFSZ]; + + multi = 0; + + if (!oracl) { + There("is no one here to consult."); + return 0; + } else if (!oracl->mpeaceful) { + pline("%s is in no mood for consultations.", Monnam(oracl)); + return 0; +#ifndef GOLDOBJ + } else if (!u.ugold) { +#else + } else if (!umoney) { +#endif + You("have no money."); + return 0; + } + + Sprintf(qbuf, + "\"Wilt thou settle for a minor consultation?\" (%d %s)", + minor_cost, currency((long)minor_cost)); + switch (ynq(qbuf)) { + default: + case 'q': + return 0; + case 'y': +#ifndef GOLDOBJ + if (u.ugold < (long)minor_cost) { +#else + if (umoney < (long)minor_cost) { +#endif + You("don't even have enough money for that!"); + return 0; + } + u_pay = minor_cost; + break; + case 'n': +#ifndef GOLDOBJ + if (u.ugold <= (long)minor_cost || /* don't even ask */ +#else + if (umoney <= (long)minor_cost || /* don't even ask */ +#endif + (oracle_cnt == 1 || oracle_flg < 0)) return 0; + Sprintf(qbuf, + "\"Then dost thou desire a major one?\" (%d %s)", + major_cost, currency((long)major_cost)); + if (yn(qbuf) != 'y') return 0; +#ifndef GOLDOBJ + u_pay = (u.ugold < (long)major_cost ? (int)u.ugold + : major_cost); +#else + u_pay = (umoney < (long)major_cost ? (int)umoney + : major_cost); +#endif + break; + } +#ifndef GOLDOBJ + u.ugold -= (long)u_pay; + oracl->mgold += (long)u_pay; +#else + money2mon(oracl, (long)u_pay); +#endif + flags.botl = 1; + add_xpts = 0; /* first oracle of each type gives experience points */ + if (u_pay == minor_cost) { + outrumor(1, BY_ORACLE); + if (!u.uevent.minor_oracle) + add_xpts = u_pay / (u.uevent.major_oracle ? 25 : 10); + /* 5 pts if very 1st, or 2 pts if major already done */ + u.uevent.minor_oracle = TRUE; + } else { + boolean cheapskate = u_pay < major_cost; + outoracle(cheapskate, TRUE); + if (!cheapskate && !u.uevent.major_oracle) + add_xpts = u_pay / (u.uevent.minor_oracle ? 25 : 10); + /* ~100 pts if very 1st, ~40 pts if minor already done */ + u.uevent.major_oracle = TRUE; + exercise(A_WIS, !cheapskate); + } + if (add_xpts) { + more_experienced(add_xpts, u_pay/50); + newexplevel(); + } + return 1; +} + +/*rumors.c*/ diff --git a/src/save.c b/src/save.c new file mode 100644 index 0000000..9291eb9 --- /dev/null +++ b/src/save.c @@ -0,0 +1,1120 @@ +/* SCCS Id: @(#)save.c 3.4 2003/11/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" +#include "quest.h" + +#ifndef NO_SIGNAL +#include +#endif +#if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C) +#include +#endif + +#ifdef MFLOPPY +long bytes_counted; +static int count_only; +#endif + +#ifdef MICRO +int dotcnt, dotrow; /* also used in restore */ +#endif + +#ifdef ZEROCOMP +STATIC_DCL void FDECL(bputc, (int)); +#endif +STATIC_DCL void FDECL(savelevchn, (int,int)); +STATIC_DCL void FDECL(savedamage, (int,int)); +STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int)); +STATIC_DCL void FDECL(savemonchn, (int,struct monst *,int)); +STATIC_DCL void FDECL(savetrapchn, (int,struct trap *,int)); +STATIC_DCL void FDECL(savegamestate, (int,int)); +#ifdef MFLOPPY +STATIC_DCL void FDECL(savelev0, (int,XCHAR_P,int)); +STATIC_DCL boolean NDECL(swapout_oldest); +STATIC_DCL void FDECL(copyfile, (char *,char *)); +#endif /* MFLOPPY */ +#ifdef GCC_WARN +static long nulls[10]; +#else +#define nulls nul +#endif + +#if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32) +#define HUP if (!program_state.done_hup) +#else +#define HUP +#endif + +/* need to preserve these during save to avoid accessing freed memory */ +static unsigned ustuck_id = 0, usteed_id = 0; + +int +dosave() +{ + clear_nhwindow(WIN_MESSAGE); + if(yn("Really save?") == 'n') { + clear_nhwindow(WIN_MESSAGE); + if(multi > 0) nomul(0); + } else { + clear_nhwindow(WIN_MESSAGE); + pline("Saving..."); +#if defined(UNIX) || defined(VMS) || defined(__EMX__) + program_state.done_hup = 0; +#endif + if(dosave0()) { + program_state.something_worth_saving = 0; + u.uhp = -1; /* universal game's over indicator */ + /* make sure they see the Saving message */ + display_nhwindow(WIN_MESSAGE, TRUE); + exit_nhwindows("Be seeing you..."); + terminate(EXIT_SUCCESS); + } else (void)doredraw(); + } + return 0; +} + + +#if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32) +/*ARGSUSED*/ +void +hangup(sig_unused) /* called as signal() handler, so sent at least one arg */ +int sig_unused; +{ +# ifdef NOSAVEONHANGUP + (void) signal(SIGINT, SIG_IGN); + clearlocks(); +# ifndef VMS + terminate(EXIT_FAILURE); +# endif +# else /* SAVEONHANGUP */ + if (!program_state.done_hup++) { + if (program_state.something_worth_saving) + (void) dosave0(); +# ifdef VMS + /* don't call exit when already within an exit handler; + that would cancel any other pending user-mode handlers */ + if (!program_state.exiting) +# endif + { + clearlocks(); + terminate(EXIT_FAILURE); + } + } +# endif + return; +} +#endif + +/* returns 1 if save successful */ +int +dosave0() +{ + const char *fq_save; + register int fd, ofd; + xchar ltmp; + d_level uz_save; + char whynot[BUFSZ]; + + if (!SAVEF[0]) + return 0; + fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */ + +#if defined(UNIX) || defined(VMS) + (void) signal(SIGHUP, SIG_IGN); +#endif +#ifndef NO_SIGNAL + (void) signal(SIGINT, SIG_IGN); +#endif + +#if defined(MICRO) && defined(MFLOPPY) + if (!saveDiskPrompt(0)) return 0; +#endif + + HUP if (iflags.window_inited) { + uncompress(fq_save); + fd = open_savefile(); + if (fd > 0) { + (void) close(fd); + clear_nhwindow(WIN_MESSAGE); + There("seems to be an old save file."); + if (yn("Overwrite the old file?") == 'n') { + compress(fq_save); + return 0; + } + } + } + + HUP mark_synch(); /* flush any buffered screen output */ + + fd = create_savefile(); + if(fd < 0) { + HUP pline("Cannot open save file."); + (void) delete_savefile(); /* ab@unido */ + return(0); + } + + vision_recalc(2); /* shut down vision to prevent problems + in the event of an impossible() call */ + + /* undo date-dependent luck adjustments made at startup time */ + if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */ + change_luck(-1); /* and unido!ab */ + if(flags.friday13) + change_luck(1); + if(iflags.window_inited) + HUP clear_nhwindow(WIN_MESSAGE); + +#ifdef MICRO + dotcnt = 0; + dotrow = 2; + curs(WIN_MAP, 1, 1); + if (strncmpi("X11", windowprocs.name, 3)) + putstr(WIN_MAP, 0, "Saving:"); +#endif +#ifdef MFLOPPY + /* make sure there is enough disk space */ + if (iflags.checkspace) { + long fds, needed; + + savelev(fd, ledger_no(&u.uz), COUNT_SAVE); + savegamestate(fd, COUNT_SAVE); + needed = bytes_counted; + + for (ltmp = 1; ltmp <= maxledgerno(); ltmp++) + if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where) + needed += level_info[ltmp].size + (sizeof ltmp); + fds = freediskspace(fq_save); + if (needed > fds) { + HUP { + There("is insufficient space on SAVE disk."); + pline("Require %ld bytes but only have %ld.", needed, fds); + } + flushout(); + (void) close(fd); + (void) delete_savefile(); + return 0; + } + + co_false(); + } +#endif /* MFLOPPY */ + + store_version(fd); +#ifdef STORE_PLNAME_IN_FILE + bwrite(fd, (genericptr_t) plname, PL_NSIZ); +#endif + ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); +#ifdef STEED + usteed_id = (u.usteed ? u.usteed->m_id : 0); +#endif + savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE); + savegamestate(fd, WRITE_SAVE | FREE_SAVE); + + /* While copying level files around, zero out u.uz to keep + * parts of the restore code from completely initializing all + * in-core data structures, since all we're doing is copying. + * This also avoids at least one nasty core dump. + */ + uz_save = u.uz; + u.uz.dnum = u.uz.dlevel = 0; + /* these pointers are no longer valid, and at least u.usteed + * may mislead place_monster() on other levels + */ + u.ustuck = (struct monst *)0; +#ifdef STEED + u.usteed = (struct monst *)0; +#endif + + for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) { + if (ltmp == ledger_no(&uz_save)) continue; + if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue; +#ifdef MICRO + curs(WIN_MAP, 1 + dotcnt++, dotrow); + if (dotcnt >= (COLNO - 1)) { + dotrow++; + dotcnt = 0; + } + if (strncmpi("X11", windowprocs.name, 3)){ + putstr(WIN_MAP, 0, "."); + } + mark_synch(); +#endif + ofd = open_levelfile(ltmp, whynot); + if (ofd < 0) { + HUP pline("%s", whynot); + (void) close(fd); + (void) delete_savefile(); + HUP killer = whynot; + HUP done(TRICKED); + return(0); + } + minit(); /* ZEROCOMP */ + getlev(ofd, hackpid, ltmp, FALSE); + (void) close(ofd); + bwrite(fd, (genericptr_t) <mp, sizeof ltmp); /* level number*/ + savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/ + delete_levelfile(ltmp); + } + bclose(fd); + + u.uz = uz_save; + + /* get rid of current level --jgm */ + delete_levelfile(ledger_no(&u.uz)); + delete_levelfile(0); + compress(fq_save); + return(1); +} + +STATIC_OVL void +savegamestate(fd, mode) +register int fd, mode; +{ + int uid; + +#ifdef MFLOPPY + count_only = (mode & COUNT_SAVE); +#endif + uid = getuid(); + bwrite(fd, (genericptr_t) &uid, sizeof uid); + bwrite(fd, (genericptr_t) &flags, sizeof(struct flag)); + bwrite(fd, (genericptr_t) &u, sizeof(struct you)); + + /* must come before migrating_objs and migrating_mons are freed */ + save_timers(fd, mode, RANGE_GLOBAL); + save_light_sources(fd, mode, RANGE_GLOBAL); + + saveobjchn(fd, invent, mode); + saveobjchn(fd, migrating_objs, mode); + savemonchn(fd, migrating_mons, mode); + if (release_data(mode)) { + invent = 0; + migrating_objs = 0; + migrating_mons = 0; + } + bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals)); + + save_dungeon(fd, (boolean)!!perform_bwrite(mode), + (boolean)!!release_data(mode)); + savelevchn(fd, mode); + bwrite(fd, (genericptr_t) &moves, sizeof moves); + bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves); + bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score)); + bwrite(fd, (genericptr_t) spl_book, + sizeof(struct spell) * (MAXSPELL + 1)); + save_artifacts(fd); + save_oracles(fd, mode); + if(ustuck_id) + bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id); +#ifdef STEED + if(usteed_id) + bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id); +#endif + bwrite(fd, (genericptr_t) pl_character, sizeof pl_character); + bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit); + bwrite(fd, (genericptr_t) ¤t_fruit, sizeof current_fruit); + savefruitchn(fd, mode); + savenames(fd, mode); + save_waterlevel(fd, mode); + bflush(fd); +} + +#ifdef INSURANCE +void +savestateinlock() +{ + int fd, hpid; + static boolean havestate = TRUE; + char whynot[BUFSZ]; + + /* When checkpointing is on, the full state needs to be written + * on each checkpoint. When checkpointing is off, only the pid + * needs to be in the level.0 file, so it does not need to be + * constantly rewritten. When checkpointing is turned off during + * a game, however, the file has to be rewritten once to truncate + * it and avoid restoring from outdated information. + * + * Restricting havestate to this routine means that an additional + * noop pid rewriting will take place on the first "checkpoint" after + * the game is started or restored, if checkpointing is off. + */ + if (flags.ins_chkpt || havestate) { + /* save the rest of the current game state in the lock file, + * following the original int pid, the current level number, + * and the current savefile name, which should not be subject + * to any internal compression schemes since they must be + * readable by an external utility + */ + fd = open_levelfile(0, whynot); + if (fd < 0) { + pline("%s", whynot); + pline("Probably someone removed it."); + killer = whynot; + done(TRICKED); + return; + } + + (void) read(fd, (genericptr_t) &hpid, sizeof(hpid)); + if (hackpid != hpid) { + Sprintf(whynot, + "Level #0 pid (%d) doesn't match ours (%d)!", + hpid, hackpid); + pline("%s", whynot); + killer = whynot; + done(TRICKED); + } + (void) close(fd); + + fd = create_levelfile(0, whynot); + if (fd < 0) { + pline("%s", whynot); + killer = whynot; + done(TRICKED); + return; + } + (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); + if (flags.ins_chkpt) { + int currlev = ledger_no(&u.uz); + + (void) write(fd, (genericptr_t) &currlev, sizeof(currlev)); + save_savefile_name(fd); + store_version(fd); +#ifdef STORE_PLNAME_IN_FILE + bwrite(fd, (genericptr_t) plname, PL_NSIZ); +#endif + ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); +#ifdef STEED + usteed_id = (u.usteed ? u.usteed->m_id : 0); +#endif + savegamestate(fd, WRITE_SAVE); + } + bclose(fd); + } + havestate = flags.ins_chkpt; +} +#endif + +#ifdef MFLOPPY +boolean +savelev(fd, lev, mode) +int fd; +xchar lev; +int mode; +{ + if (mode & COUNT_SAVE) { + bytes_counted = 0; + savelev0(fd, lev, COUNT_SAVE); + /* probably bytes_counted will be filled in again by an + * immediately following WRITE_SAVE anyway, but we'll + * leave it out of checkspace just in case */ + if (iflags.checkspace) { + while (bytes_counted > freediskspace(levels)) + if (!swapout_oldest()) + return FALSE; + } + } + if (mode & (WRITE_SAVE | FREE_SAVE)) { + bytes_counted = 0; + savelev0(fd, lev, mode); + } + if (mode != FREE_SAVE) { + level_info[lev].where = ACTIVE; + level_info[lev].time = moves; + level_info[lev].size = bytes_counted; + } + return TRUE; +} + +STATIC_OVL void +savelev0(fd,lev,mode) +#else +void +savelev(fd,lev,mode) +#endif +int fd; +xchar lev; +int mode; +{ +#ifdef TOS + short tlev; +#endif + + /* if we're tearing down the current level without saving anything + (which happens upon entrance to the endgame or after an aborted + restore attempt) then we don't want to do any actual I/O */ + if (mode == FREE_SAVE) goto skip_lots; + if (iflags.purge_monsters) { + /* purge any dead monsters (necessary if we're starting + * a panic save rather than a normal one, or sometimes + * when changing levels without taking time -- e.g. + * create statue trap then immediately level teleport) */ + dmonsfree(); + } + + if(fd < 0) panic("Save on bad file!"); /* impossible */ +#ifdef MFLOPPY + count_only = (mode & COUNT_SAVE); +#endif + if (lev >= 0 && lev <= maxledgerno()) + level_info[lev].flags |= VISITED; + bwrite(fd,(genericptr_t) &hackpid,sizeof(hackpid)); +#ifdef TOS + tlev=lev; tlev &= 0x00ff; + bwrite(fd,(genericptr_t) &tlev,sizeof(tlev)); +#else + bwrite(fd,(genericptr_t) &lev,sizeof(lev)); +#endif +#ifdef RLECOMP + { + /* perform run-length encoding of rm structs */ + struct rm *prm, *rgrm; + int x, y; + uchar match; + + rgrm = &levl[0][0]; /* start matching at first rm */ + match = 0; + + for (y = 0; y < ROWNO; y++) { + for (x = 0; x < COLNO; x++) { + prm = &levl[x][y]; + if (prm->glyph == rgrm->glyph + && prm->typ == rgrm->typ + && prm->seenv == rgrm->seenv + && prm->horizontal == rgrm->horizontal + && prm->flags == rgrm->flags + && prm->lit == rgrm->lit + && prm->waslit == rgrm->waslit + && prm->roomno == rgrm->roomno + && prm->edge == rgrm->edge) { + match++; + if (match > 254) { + match = 254; /* undo this match */ + goto writeout; + } + } else { + /* the run has been broken, + * write out run-length encoding */ + writeout: + bwrite(fd, (genericptr_t)&match, sizeof(uchar)); + bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); + /* start encoding again. we have at least 1 rm + * in the next run, viz. this one. */ + match = 1; + rgrm = prm; + } + } + } + if (match > 0) { + bwrite(fd, (genericptr_t)&match, sizeof(uchar)); + bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); + } + } +#else + bwrite(fd,(genericptr_t) levl,sizeof(levl)); +#endif /* RLECOMP */ + + bwrite(fd,(genericptr_t) &monstermoves,sizeof(monstermoves)); + bwrite(fd,(genericptr_t) &upstair,sizeof(stairway)); + bwrite(fd,(genericptr_t) &dnstair,sizeof(stairway)); + bwrite(fd,(genericptr_t) &upladder,sizeof(stairway)); + bwrite(fd,(genericptr_t) &dnladder,sizeof(stairway)); + bwrite(fd,(genericptr_t) &sstairs,sizeof(stairway)); + bwrite(fd,(genericptr_t) &updest,sizeof(dest_area)); + bwrite(fd,(genericptr_t) &dndest,sizeof(dest_area)); + bwrite(fd,(genericptr_t) &level.flags,sizeof(level.flags)); + bwrite(fd, (genericptr_t) doors, sizeof(doors)); + save_rooms(fd); /* no dynamic memory to reclaim */ + + /* from here on out, saving also involves allocated memory cleanup */ + skip_lots: + /* must be saved before mons, objs, and buried objs */ + save_timers(fd, mode, RANGE_LEVEL); + save_light_sources(fd, mode, RANGE_LEVEL); + + savemonchn(fd, fmon, mode); + save_worm(fd, mode); /* save worm information */ + savetrapchn(fd, ftrap, mode); + saveobjchn(fd, fobj, mode); + saveobjchn(fd, level.buriedobjlist, mode); + saveobjchn(fd, billobjs, mode); + if (release_data(mode)) { + fmon = 0; + ftrap = 0; + fobj = 0; + level.buriedobjlist = 0; + billobjs = 0; + } + save_engravings(fd, mode); + savedamage(fd, mode); + save_regions(fd, mode); + if (mode != FREE_SAVE) bflush(fd); +} + +#ifdef ZEROCOMP +/* The runs of zero-run compression are flushed after the game state or a + * level is written out. This adds a couple bytes to a save file, where + * the runs could be mashed together, but it allows gluing together game + * state and level files to form a save file, and it means the flushing + * does not need to be specifically called for every other time a level + * file is written out. + */ + +#define RLESC '\0' /* Leading character for run of LRESC's */ +#define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1) + +#ifndef ZEROCOMP_BUFSIZ +# define ZEROCOMP_BUFSIZ BUFSZ +#endif +static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ]; +static NEARDATA unsigned short outbufp = 0; +static NEARDATA short outrunlength = -1; +static NEARDATA int bwritefd; +static NEARDATA boolean compressing = FALSE; + +/*dbg() +{ + HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength); +}*/ + +STATIC_OVL void +bputc(c) +int c; +{ +#ifdef MFLOPPY + bytes_counted++; + if (count_only) + return; +#endif + if (outbufp >= sizeof outbuf) { + (void) write(bwritefd, outbuf, sizeof outbuf); + outbufp = 0; + } + outbuf[outbufp++] = (unsigned char)c; +} + +/*ARGSUSED*/ +void +bufon(fd) +int fd; +{ + compressing = TRUE; + return; +} + +/*ARGSUSED*/ +void +bufoff(fd) +int fd; +{ + if (outbufp) { + outbufp = 0; + panic("closing file with buffered data still unwritten"); + } + outrunlength = -1; + compressing = FALSE; + return; +} + +void +bflush(fd) /* flush run and buffer */ +register int fd; +{ + bwritefd = fd; + if (outrunlength >= 0) { /* flush run */ + flushoutrun(outrunlength); + } +#ifdef MFLOPPY + if (count_only) outbufp = 0; +#endif + + if (outbufp) { + if (write(fd, outbuf, outbufp) != outbufp) { +#if defined(UNIX) || defined(VMS) || defined(__EMX__) + if (program_state.done_hup) + terminate(EXIT_FAILURE); + else +#endif + bclose(fd); /* panic (outbufp != 0) */ + } + outbufp = 0; + } +} + +void +bwrite(fd, loc, num) +int fd; +genericptr_t loc; +register unsigned num; +{ + register unsigned char *bp = (unsigned char *)loc; + + if (!compressing) { +#ifdef MFLOPPY + bytes_counted += num; + if (count_only) return; +#endif + if ((unsigned) write(fd, loc, num) != num) { +#if defined(UNIX) || defined(VMS) || defined(__EMX__) + if (program_state.done_hup) + terminate(EXIT_FAILURE); + else +#endif + panic("cannot write %u bytes to file #%d", num, fd); + } + } else { + bwritefd = fd; + for (; num; num--, bp++) { + if (*bp == RLESC) { /* One more char in run */ + if (++outrunlength == 0xFF) { + flushoutrun(outrunlength); + } + } else { /* end of run */ + if (outrunlength >= 0) { /* flush run */ + flushoutrun(outrunlength); + } + bputc(*bp); + } + } + } +} + +void +bclose(fd) +int fd; +{ + bufoff(fd); + (void) close(fd); + return; +} + +#else /* ZEROCOMP */ + +static int bw_fd = -1; +static FILE *bw_FILE = 0; +static boolean buffering = FALSE; + +void +bufon(fd) + int fd; +{ +#ifdef UNIX + if(bw_fd >= 0) + panic("double buffering unexpected"); + bw_fd = fd; + if((bw_FILE = fdopen(fd, "w")) == 0) + panic("buffering of file %d failed", fd); +#endif + buffering = TRUE; +} + +void +bufoff(fd) +int fd; +{ + bflush(fd); + buffering = FALSE; +} + +void +bflush(fd) + int fd; +{ +#ifdef UNIX + if(fd == bw_fd) { + if(fflush(bw_FILE) == EOF) + panic("flush of savefile failed!"); + } +#endif + return; +} + +void +bwrite(fd,loc,num) +register int fd; +register genericptr_t loc; +register unsigned num; +{ + boolean failed; + +#ifdef MFLOPPY + bytes_counted += num; + if (count_only) return; +#endif + +#ifdef UNIX + if (buffering) { + if(fd != bw_fd) + panic("unbuffered write to fd %d (!= %d)", fd, bw_fd); + + failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1); + } else +#endif /* UNIX */ + { +/* lint wants the 3rd arg of write to be an int; lint -p an unsigned */ +#if defined(BSD) || defined(ULTRIX) + failed = (write(fd, loc, (int)num) != (int)num); +#else /* e.g. SYSV, __TURBOC__ */ + failed = (write(fd, loc, num) != num); +#endif + } + + if (failed) { +#if defined(UNIX) || defined(VMS) || defined(__EMX__) + if (program_state.done_hup) + terminate(EXIT_FAILURE); + else +#endif + panic("cannot write %u bytes to file #%d", num, fd); + } +} + +void +bclose(fd) + int fd; +{ + bufoff(fd); +#ifdef UNIX + if (fd == bw_fd) { + (void) fclose(bw_FILE); + bw_fd = -1; + bw_FILE = 0; + } else +#endif + (void) close(fd); + return; +} +#endif /* ZEROCOMP */ + +STATIC_OVL void +savelevchn(fd, mode) +register int fd, mode; +{ + s_level *tmplev, *tmplev2; + int cnt = 0; + + for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next) cnt++; + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) &cnt, sizeof(int)); + + for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) { + tmplev2 = tmplev->next; + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) tmplev, sizeof(s_level)); + if (release_data(mode)) + free((genericptr_t) tmplev); + } + if (release_data(mode)) + sp_levchn = 0; +} + +STATIC_OVL void +savedamage(fd, mode) +register int fd, mode; +{ + register struct damage *damageptr, *tmp_dam; + unsigned int xl = 0; + + damageptr = level.damagelist; + for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next) + xl++; + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) &xl, sizeof(xl)); + + while (xl--) { + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr)); + tmp_dam = damageptr; + damageptr = damageptr->next; + if (release_data(mode)) + free((genericptr_t)tmp_dam); + } + if (release_data(mode)) + level.damagelist = 0; +} + +STATIC_OVL void +saveobjchn(fd, otmp, mode) +register int fd, mode; +register struct obj *otmp; +{ + register struct obj *otmp2; + unsigned int xl; + int minusone = -1; + + while(otmp) { + otmp2 = otmp->nobj; + if (perform_bwrite(mode)) { + xl = otmp->oxlth + otmp->onamelth; + bwrite(fd, (genericptr_t) &xl, sizeof(int)); + bwrite(fd, (genericptr_t) otmp, xl + sizeof(struct obj)); + } + if (Has_contents(otmp)) + saveobjchn(fd,otmp->cobj,mode); + if (release_data(mode)) { + if (otmp->oclass == FOOD_CLASS) food_disappears(otmp); + if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp); + otmp->where = OBJ_FREE; /* set to free so dealloc will work */ + otmp->timed = 0; /* not timed any more */ + otmp->lamplit = 0; /* caller handled lights */ + dealloc_obj(otmp); + } + otmp = otmp2; + } + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) &minusone, sizeof(int)); +} + +STATIC_OVL void +savemonchn(fd, mtmp, mode) +register int fd, mode; +register struct monst *mtmp; +{ + register struct monst *mtmp2; + unsigned int xl; + int minusone = -1; + struct permonst *monbegin = &mons[0]; + + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) &monbegin, sizeof(monbegin)); + + while (mtmp) { + mtmp2 = mtmp->nmon; + if (perform_bwrite(mode)) { + xl = mtmp->mxlth + mtmp->mnamelth; + bwrite(fd, (genericptr_t) &xl, sizeof(int)); + bwrite(fd, (genericptr_t) mtmp, xl + sizeof(struct monst)); + } + if (mtmp->minvent) + saveobjchn(fd,mtmp->minvent,mode); + if (release_data(mode)) + dealloc_monst(mtmp); + mtmp = mtmp2; + } + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) &minusone, sizeof(int)); +} + +STATIC_OVL void +savetrapchn(fd, trap, mode) +register int fd, mode; +register struct trap *trap; +{ + register struct trap *trap2; + + while (trap) { + trap2 = trap->ntrap; + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t) trap, sizeof(struct trap)); + if (release_data(mode)) + dealloc_trap(trap); + trap = trap2; + } + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t)nulls, sizeof(struct trap)); +} + +/* save all the fruit names and ID's; this is used only in saving whole games + * (not levels) and in saving bones levels. When saving a bones level, + * we only want to save the fruits which exist on the bones level; the bones + * level routine marks nonexistent fruits by making the fid negative. + */ +void +savefruitchn(fd, mode) +register int fd, mode; +{ + register struct fruit *f2, *f1; + + f1 = ffruit; + while (f1) { + f2 = f1->nextf; + if (f1->fid >= 0 && perform_bwrite(mode)) + bwrite(fd, (genericptr_t) f1, sizeof(struct fruit)); + if (release_data(mode)) + dealloc_fruit(f1); + f1 = f2; + } + if (perform_bwrite(mode)) + bwrite(fd, (genericptr_t)nulls, sizeof(struct fruit)); + if (release_data(mode)) + ffruit = 0; +} + +/* also called by prscore(); this probably belongs in dungeon.c... */ +void +free_dungeons() +{ +#ifdef FREE_ALL_MEMORY + savelevchn(0, FREE_SAVE); + save_dungeon(0, FALSE, TRUE); +#endif + return; +} + +void +freedynamicdata() +{ + unload_qtlist(); + free_invbuf(); /* let_to_name (invent.c) */ + free_youbuf(); /* You_buf,&c (pline.c) */ + tmp_at(DISP_FREEMEM, 0); /* temporary display effects */ +#ifdef FREE_ALL_MEMORY +# define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0) +# define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0) +# define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0) +# define freefruitchn() savefruitchn(0, FREE_SAVE) +# define freenames() savenames(0, FREE_SAVE) +# define free_oracles() save_oracles(0, FREE_SAVE) +# define free_waterlevel() save_waterlevel(0, FREE_SAVE) +# define free_worm() save_worm(0, FREE_SAVE) +# define free_timers(R) save_timers(0, FREE_SAVE, R) +# define free_light_sources(R) save_light_sources(0, FREE_SAVE, R); +# define free_engravings() save_engravings(0, FREE_SAVE) +# define freedamage() savedamage(0, FREE_SAVE) +# define free_animals() mon_animal_list(FALSE) + + /* move-specific data */ + dmonsfree(); /* release dead monsters */ + + /* level-specific data */ + free_timers(RANGE_LEVEL); + free_light_sources(RANGE_LEVEL); + freemonchn(fmon); + free_worm(); /* release worm segment information */ + freetrapchn(ftrap); + freeobjchn(fobj); + freeobjchn(level.buriedobjlist); + freeobjchn(billobjs); + free_engravings(); + freedamage(); + + /* game-state data */ + free_timers(RANGE_GLOBAL); + free_light_sources(RANGE_GLOBAL); + freeobjchn(invent); + freeobjchn(migrating_objs); + freemonchn(migrating_mons); + freemonchn(mydogs); /* ascension or dungeon escape */ + /* freelevchn(); [folded into free_dungeons()] */ + free_animals(); + free_oracles(); + freefruitchn(); + freenames(); + free_waterlevel(); + free_dungeons(); + + /* some pointers in iflags */ + if (iflags.wc_font_map) free(iflags.wc_font_map); + if (iflags.wc_font_message) free(iflags.wc_font_message); + if (iflags.wc_font_text) free(iflags.wc_font_text); + if (iflags.wc_font_menu) free(iflags.wc_font_menu); + if (iflags.wc_font_status) free(iflags.wc_font_status); + if (iflags.wc_tile_file) free(iflags.wc_tile_file); +#ifdef AUTOPICKUP_EXCEPTIONS + free_autopickup_exceptions(); +#endif + +#endif /* FREE_ALL_MEMORY */ + return; +} + +#ifdef MFLOPPY +boolean +swapin_file(lev) +int lev; +{ + char to[PATHLEN], from[PATHLEN]; + + Sprintf(from, "%s%s", permbones, alllevels); + Sprintf(to, "%s%s", levels, alllevels); + set_levelfile_name(from, lev); + set_levelfile_name(to, lev); + if (iflags.checkspace) { + while (level_info[lev].size > freediskspace(to)) + if (!swapout_oldest()) + return FALSE; + } +# ifdef WIZARD + if (wizard) { + pline("Swapping in `%s'.", from); + wait_synch(); + } +# endif + copyfile(from, to); + (void) unlink(from); + level_info[lev].where = ACTIVE; + return TRUE; +} + +STATIC_OVL boolean +swapout_oldest() { + char to[PATHLEN], from[PATHLEN]; + int i, oldest; + long oldtime; + + if (!ramdisk) + return FALSE; + for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++) + if (level_info[i].where == ACTIVE + && (!oldtime || level_info[i].time < oldtime)) { + oldest = i; + oldtime = level_info[i].time; + } + if (!oldest) + return FALSE; + Sprintf(from, "%s%s", levels, alllevels); + Sprintf(to, "%s%s", permbones, alllevels); + set_levelfile_name(from, oldest); + set_levelfile_name(to, oldest); +# ifdef WIZARD + if (wizard) { + pline("Swapping out `%s'.", from); + wait_synch(); + } +# endif + copyfile(from, to); + (void) unlink(from); + level_info[oldest].where = SWAPPED; + return TRUE; +} + +STATIC_OVL void +copyfile(from, to) +char *from, *to; +{ +# ifdef TOS + + if (_copyfile(from, to)) + panic("Can't copy %s to %s", from, to); +# else + char buf[BUFSIZ]; /* this is system interaction, therefore + * BUFSIZ instead of NetHack's BUFSZ */ + int nfrom, nto, fdfrom, fdto; + + if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0) + panic("Can't copy from %s !?", from); + if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0) + panic("Can't copy to %s", to); + do { + nfrom = read(fdfrom, buf, BUFSIZ); + nto = write(fdto, buf, nfrom); + if (nto != nfrom) + panic("Copyfile failed!"); + } while (nfrom == BUFSIZ); + (void) close(fdfrom); + (void) close(fdto); +# endif /* TOS */ +} + +void +co_false() /* see comment in bones.c */ +{ + count_only = FALSE; + return; +} + +#endif /* MFLOPPY */ + +/*save.c*/ diff --git a/src/shk.c b/src/shk.c new file mode 100644 index 0000000..53c868b --- /dev/null +++ b/src/shk.c @@ -0,0 +1,4098 @@ +/* SCCS Id: @(#)shk.c 3.4 2003/12/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "eshk.h" + +/*#define DEBUG*/ + +#define PAY_SOME 2 +#define PAY_BUY 1 +#define PAY_CANT 0 /* too poor */ +#define PAY_SKIP (-1) +#define PAY_BROKE (-2) + +#ifdef KOPS +STATIC_DCL void FDECL(makekops, (coord *)); +STATIC_DCL void FDECL(call_kops, (struct monst *,BOOLEAN_P)); +# ifdef OVLB +STATIC_DCL void FDECL(kops_gone, (BOOLEAN_P)); +# endif /* OVLB */ +#endif /* KOPS */ + +#define IS_SHOP(x) (rooms[x].rtype >= SHOPBASE) + +extern const struct shclass shtypes[]; /* defined in shknam.c */ +extern struct obj *thrownobj; /* defined in dothrow.c */ + +STATIC_VAR NEARDATA long int followmsg; /* last time of follow message */ + +STATIC_DCL void FDECL(setpaid, (struct monst *)); +STATIC_DCL long FDECL(addupbill, (struct monst *)); +STATIC_DCL void FDECL(pacify_shk, (struct monst *)); +STATIC_DCL struct bill_x *FDECL(onbill, (struct obj *, struct monst *, BOOLEAN_P)); +STATIC_DCL struct monst *FDECL(next_shkp, (struct monst *, BOOLEAN_P)); +STATIC_DCL long FDECL(shop_debt, (struct eshk *)); +STATIC_DCL char *FDECL(shk_owns, (char *,struct obj *)); +STATIC_DCL char *FDECL(mon_owns, (char *,struct obj *)); +STATIC_DCL void FDECL(clear_unpaid,(struct obj *)); +STATIC_DCL long FDECL(check_credit, (long, struct monst *)); +STATIC_DCL void FDECL(pay, (long, struct monst *)); +STATIC_DCL long FDECL(get_cost, (struct obj *, struct monst *)); +STATIC_DCL long FDECL(set_cost, (struct obj *, struct monst *)); +STATIC_DCL const char *FDECL(shk_embellish, (struct obj *, long)); +STATIC_DCL long FDECL(cost_per_charge, (struct monst *,struct obj *,BOOLEAN_P)); +STATIC_DCL long FDECL(cheapest_item, (struct monst *)); +STATIC_DCL int FDECL(dopayobj, (struct monst *, struct bill_x *, + struct obj **, int, BOOLEAN_P)); +STATIC_DCL long FDECL(stolen_container, (struct obj *, struct monst *, long, + BOOLEAN_P)); +STATIC_DCL long FDECL(getprice, (struct obj *,BOOLEAN_P)); +STATIC_DCL void FDECL(shk_names_obj, + (struct monst *,struct obj *,const char *,long,const char *)); +STATIC_DCL struct obj *FDECL(bp_to_obj, (struct bill_x *)); +STATIC_DCL boolean FDECL(inherits, (struct monst *,int,int)); +STATIC_DCL void FDECL(set_repo_loc, (struct eshk *)); +STATIC_DCL boolean NDECL(angry_shk_exists); +STATIC_DCL void FDECL(rile_shk, (struct monst *)); +STATIC_DCL void FDECL(rouse_shk, (struct monst *,BOOLEAN_P)); +STATIC_DCL void FDECL(remove_damage, (struct monst *, BOOLEAN_P)); +STATIC_DCL void FDECL(sub_one_frombill, (struct obj *, struct monst *)); +STATIC_DCL void FDECL(add_one_tobill, (struct obj *, BOOLEAN_P)); +STATIC_DCL void FDECL(dropped_container, (struct obj *, struct monst *, + BOOLEAN_P)); +STATIC_DCL void FDECL(add_to_billobjs, (struct obj *)); +STATIC_DCL void FDECL(bill_box_content, (struct obj *, BOOLEAN_P, BOOLEAN_P, + struct monst *)); +#ifdef OVL1 +static boolean FDECL(rob_shop, (struct monst *)); +#endif + +#ifdef OVLB +/* + invariants: obj->unpaid iff onbill(obj) [unless bp->useup] + obj->quan <= bp->bquan + */ + + +#ifdef GOLDOBJ +/* + Transfer money from inventory to monster when paying + shopkeepers, priests, oracle, succubus, & other demons. + Simple with only gold coins. + This routine will handle money changing when multiple + coin types is implemented, only appropriate + monsters will pay change. (Peaceful shopkeepers, priests + & the oracle try to maintain goodwill while selling + their wares or services. Angry monsters and all demons + will keep anything they get their hands on. + Returns the amount actually paid, so we can know + if the monster kept the change. + */ +long +money2mon(mon, amount) +struct monst *mon; +long amount; +{ + struct obj *ygold = findgold(invent); + + if (amount <= 0) { + impossible("%s payment in money2mon!", amount ? "negative" : "zero"); + return 0L; + } + if (!ygold || ygold->quan < amount) { + impossible("Paying without %s money?", ygold ? "enough" : ""); + return 0L; + } + + if (ygold->quan > amount) + ygold = splitobj(ygold, amount); + else if (ygold->owornmask) + remove_worn_item(ygold, FALSE); /* quiver */ + freeinv(ygold); + add_to_minv(mon, ygold); + flags.botl = 1; + return amount; +} + + +/* + Transfer money from monster to inventory. + Used when the shopkeeper pay for items, and when + the priest gives you money for an ale. + */ +void +money2u(mon, amount) +struct monst *mon; +long amount; +{ + struct obj *mongold = findgold(mon->minvent); + + if (amount <= 0) { + impossible("%s payment in money2u!", amount ? "negative" : "zero"); + return; + } + if (!mongold || mongold->quan < amount) { + impossible("%s paying without %s money?", a_monnam(mon), + mongold ? "enough" : ""); + return; + } + + if (mongold->quan > amount) mongold = splitobj(mongold, amount); + obj_extract_self(mongold); + + if (!merge_choice(invent, mongold) && inv_cnt() >= 52) { + You("have no room for the money!"); + dropy(mongold); + } else { + addinv(mongold); + flags.botl = 1; + } +} + +#endif /* GOLDOBJ */ + +STATIC_OVL struct monst * +next_shkp(shkp, withbill) +register struct monst *shkp; +register boolean withbill; +{ + for (; shkp; shkp = shkp->nmon) { + if (DEADMONSTER(shkp)) continue; + if (shkp->isshk && (ESHK(shkp)->billct || !withbill)) break; + } + + if (shkp) { + if (NOTANGRY(shkp)) { + if (ESHK(shkp)->surcharge) pacify_shk(shkp); + } else { + if (!ESHK(shkp)->surcharge) rile_shk(shkp); + } + } + return(shkp); +} + +char * +shkname(mtmp) /* called in do_name.c */ +register struct monst *mtmp; +{ + return(ESHK(mtmp)->shknam); +} + +void +shkgone(mtmp) /* called in mon.c */ +struct monst *mtmp; +{ + struct eshk *eshk = ESHK(mtmp); + struct mkroom *sroom = &rooms[eshk->shoproom - ROOMOFFSET]; + struct obj *otmp; + char *p; + int sx, sy; + + /* [BUG: some of this should be done on the shop level */ + /* even when the shk dies on a different level.] */ + if (on_level(&eshk->shoplevel, &u.uz)) { + remove_damage(mtmp, TRUE); + sroom->resident = (struct monst *)0; + if (!search_special(ANY_SHOP)) + level.flags.has_shop = 0; + + /* items on shop floor revert to ordinary objects */ + for (sx = sroom->lx; sx <= sroom->hx; sx++) + for (sy = sroom->ly; sy <= sroom->hy; sy++) + for (otmp = level.objects[sx][sy]; otmp; otmp = otmp->nexthere) + otmp->no_charge = 0; + + /* Make sure bill is set only when the + dead shk is the resident shk. */ + if ((p = index(u.ushops, eshk->shoproom)) != 0) { + setpaid(mtmp); + eshk->bill_p = (struct bill_x *)0; + /* remove eshk->shoproom from u.ushops */ + do { *p = *(p + 1); } while (*++p); + } + } +} + +void +set_residency(shkp, zero_out) +register struct monst *shkp; +register boolean zero_out; +{ + if (on_level(&(ESHK(shkp)->shoplevel), &u.uz)) + rooms[ESHK(shkp)->shoproom - ROOMOFFSET].resident = + (zero_out)? (struct monst *)0 : shkp; +} + +void +replshk(mtmp,mtmp2) +register struct monst *mtmp, *mtmp2; +{ + rooms[ESHK(mtmp2)->shoproom - ROOMOFFSET].resident = mtmp2; + if (inhishop(mtmp) && *u.ushops == ESHK(mtmp)->shoproom) { + ESHK(mtmp2)->bill_p = &(ESHK(mtmp2)->bill[0]); + } +} + +/* do shopkeeper specific structure munging -dlc */ +void +restshk(shkp, ghostly) +struct monst *shkp; +boolean ghostly; +{ + if (u.uz.dlevel) { + struct eshk *eshkp = ESHK(shkp); + + if (eshkp->bill_p != (struct bill_x *) -1000) + eshkp->bill_p = &eshkp->bill[0]; + /* shoplevel can change as dungeons move around */ + /* savebones guarantees that non-homed shk's will be gone */ + if (ghostly) { + assign_level(&eshkp->shoplevel, &u.uz); + if (ANGRY(shkp) && strncmpi(eshkp->customer, plname, PL_NSIZ)) + pacify_shk(shkp); + } + } +} + +#endif /* OVLB */ +#ifdef OVL3 + +/* Clear the unpaid bit on all of the objects in the list. */ +STATIC_OVL void +clear_unpaid(list) +register struct obj *list; +{ + while (list) { + if (Has_contents(list)) clear_unpaid(list->cobj); + list->unpaid = 0; + list = list->nobj; + } +} +#endif /*OVL3*/ +#ifdef OVLB + +/* either you paid or left the shop or the shopkeeper died */ +STATIC_OVL void +setpaid(shkp) +register struct monst *shkp; +{ + register struct obj *obj; + register struct monst *mtmp; + + /* FIXME: object handling should be limited to + items which are on this particular shk's bill */ + + clear_unpaid(invent); + clear_unpaid(fobj); + clear_unpaid(level.buriedobjlist); + if (thrownobj) thrownobj->unpaid = 0; + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + clear_unpaid(mtmp->minvent); + for(mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) + clear_unpaid(mtmp->minvent); + + while ((obj = billobjs) != 0) { + obj_extract_self(obj); + dealloc_obj(obj); + } + if(shkp) { + ESHK(shkp)->billct = 0; + ESHK(shkp)->credit = 0L; + ESHK(shkp)->debit = 0L; + ESHK(shkp)->loan = 0L; + } +} + +STATIC_OVL long +addupbill(shkp) +register struct monst *shkp; +{ + register int ct = ESHK(shkp)->billct; + register struct bill_x *bp = ESHK(shkp)->bill_p; + register long total = 0L; + + while(ct--){ + total += bp->price * bp->bquan; + bp++; + } + return(total); +} + +#endif /* OVLB */ +#ifdef OVL1 + +#ifdef KOPS +STATIC_OVL void +call_kops(shkp, nearshop) +register struct monst *shkp; +register boolean nearshop; +{ + /* Keystone Kops srt@ucla */ + register boolean nokops; + + if(!shkp) return; + + if (flags.soundok) + pline("An alarm sounds!"); + + nokops = ((mvitals[PM_KEYSTONE_KOP].mvflags & G_GONE) && + (mvitals[PM_KOP_SERGEANT].mvflags & G_GONE) && + (mvitals[PM_KOP_LIEUTENANT].mvflags & G_GONE) && + (mvitals[PM_KOP_KAPTAIN].mvflags & G_GONE)); + + if(!angry_guards(!flags.soundok) && nokops) { + if(flags.verbose && flags.soundok) + pline("But no one seems to respond to it."); + return; + } + + if(nokops) return; + + { + coord mm; + + if (nearshop) { + /* Create swarm around you, if you merely "stepped out" */ + if (flags.verbose) + pline_The("Keystone Kops appear!"); + mm.x = u.ux; + mm.y = u.uy; + makekops(&mm); + return; + } + if (flags.verbose) + pline_The("Keystone Kops are after you!"); + /* Create swarm near down staircase (hinders return to level) */ + mm.x = xdnstair; + mm.y = ydnstair; + makekops(&mm); + /* Create swarm near shopkeeper (hinders return to shop) */ + mm.x = shkp->mx; + mm.y = shkp->my; + makekops(&mm); + } +} +#endif /* KOPS */ + +/* x,y is strictly inside shop */ +char +inside_shop(x, y) +register xchar x, y; +{ + register char rno; + + rno = levl[x][y].roomno; + if ((rno < ROOMOFFSET) || levl[x][y].edge || !IS_SHOP(rno-ROOMOFFSET)) + return(NO_ROOM); + else + return(rno); +} + +void +u_left_shop(leavestring, newlev) +char *leavestring; +boolean newlev; +{ + struct monst *shkp; + struct eshk *eshkp; + + /* + * IF player + * ((didn't leave outright) AND + * ((he is now strictly-inside the shop) OR + * (he wasn't strictly-inside last turn anyway))) + * THEN (there's nothing to do, so just return) + */ + if(!*leavestring && + (!levl[u.ux][u.uy].edge || levl[u.ux0][u.uy0].edge)) + return; + + shkp = shop_keeper(*u.ushops0); + if (!shkp || !inhishop(shkp)) + return; /* shk died, teleported, changed levels... */ + + eshkp = ESHK(shkp); + if (!eshkp->billct && !eshkp->debit) /* bill is settled */ + return; + + if (!*leavestring && shkp->mcanmove && !shkp->msleeping) { + /* + * Player just stepped onto shop-boundary (known from above logic). + * Try to intimidate him into paying his bill + */ + verbalize(NOTANGRY(shkp) ? + "%s! Please pay before leaving." : + "%s! Don't you leave without paying!", + plname); + return; + } + + if (rob_shop(shkp)) { +#ifdef KOPS + call_kops(shkp, (!newlev && levl[u.ux0][u.uy0].edge)); +#else + (void) angry_guards(FALSE); +#endif + } +} + +/* robbery from outside the shop via telekinesis or grappling hook */ +void +remote_burglary(x, y) +xchar x, y; +{ + struct monst *shkp; + struct eshk *eshkp; + + shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + if (!shkp || !inhishop(shkp)) + return; /* shk died, teleported, changed levels... */ + + eshkp = ESHK(shkp); + if (!eshkp->billct && !eshkp->debit) /* bill is settled */ + return; + + if (rob_shop(shkp)) { +#ifdef KOPS + /*[might want to set 2nd arg based on distance from shop doorway]*/ + call_kops(shkp, FALSE); +#else + (void) angry_guards(FALSE); +#endif + } +} + +/* shop merchandise has been taken; pay for it with any credit available; + return false if the debt is fully covered by credit, true otherwise */ +static boolean +rob_shop(shkp) +struct monst *shkp; +{ + struct eshk *eshkp; + long total; + + eshkp = ESHK(shkp); + rouse_shk(shkp, TRUE); + total = (addupbill(shkp) + eshkp->debit); + if (eshkp->credit >= total) { + Your("credit of %ld %s is used to cover your shopping bill.", + eshkp->credit, currency(eshkp->credit)); + total = 0L; /* credit gets cleared by setpaid() */ + } else { + You("escaped the shop without paying!"); + total -= eshkp->credit; + } + setpaid(shkp); + if (!total) return FALSE; + + /* by this point, we know an actual robbery has taken place */ + eshkp->robbed += total; + You("stole %ld %s worth of merchandise.", + total, currency(total)); + if (!Role_if(PM_ROGUE)) /* stealing is unlawful */ + adjalign(-sgn(u.ualign.type)); + + hot_pursuit(shkp); + return TRUE; +} + +void +u_entered_shop(enterstring) +register char *enterstring; +{ + + register int rt; + register struct monst *shkp; + register struct eshk *eshkp; + static const char no_shk[] = "This shop appears to be deserted."; + static char empty_shops[5]; + + if(!*enterstring) + return; + + if(!(shkp = shop_keeper(*enterstring))) { + if (!index(empty_shops, *enterstring) && + in_rooms(u.ux, u.uy, SHOPBASE) != + in_rooms(u.ux0, u.uy0, SHOPBASE)) + pline(no_shk); + Strcpy(empty_shops, u.ushops); + u.ushops[0] = '\0'; + return; + } + + eshkp = ESHK(shkp); + + if (!inhishop(shkp)) { + /* dump core when referenced */ + eshkp->bill_p = (struct bill_x *) -1000; + if (!index(empty_shops, *enterstring)) + pline(no_shk); + Strcpy(empty_shops, u.ushops); + u.ushops[0] = '\0'; + return; + } + + eshkp->bill_p = &(eshkp->bill[0]); + + if ((!eshkp->visitct || *eshkp->customer) && + strncmpi(eshkp->customer, plname, PL_NSIZ)) { + /* You seem to be new here */ + eshkp->visitct = 0; + eshkp->following = 0; + (void) strncpy(eshkp->customer,plname,PL_NSIZ); + pacify_shk(shkp); + } + + if (shkp->msleeping || !shkp->mcanmove || eshkp->following) + return; /* no dialog */ + + if (Invis) { + pline("%s senses your presence.", shkname(shkp)); + verbalize("Invisible customers are not welcome!"); + return; + } + + rt = rooms[*enterstring - ROOMOFFSET].rtype; + + if (ANGRY(shkp)) { + verbalize("So, %s, you dare return to %s %s?!", + plname, + s_suffix(shkname(shkp)), + shtypes[rt - SHOPBASE].name); + } else if (eshkp->robbed) { + pline("%s mutters imprecations against shoplifters.", shkname(shkp)); + } else { + verbalize("%s, %s! Welcome%s to %s %s!", + Hello(shkp), plname, + eshkp->visitct++ ? " again" : "", + s_suffix(shkname(shkp)), + shtypes[rt - SHOPBASE].name); + } + /* can't do anything about blocking if teleported in */ + if (!inside_shop(u.ux, u.uy)) { + boolean should_block; + int cnt; + const char *tool; + struct obj *pick = carrying(PICK_AXE), + *mattock = carrying(DWARVISH_MATTOCK); + + if (pick || mattock) { + cnt = 1; /* so far */ + if (pick && mattock) { /* carrying both types */ + tool = "digging tool"; + cnt = 2; /* `more than 1' is all that matters */ + } else if (pick) { + tool = "pick-axe"; + /* hack: `pick' already points somewhere into inventory */ + while ((pick = pick->nobj) != 0) + if (pick->otyp == PICK_AXE) ++cnt; + } else { /* assert(mattock != 0) */ + tool = "mattock"; + while ((mattock = mattock->nobj) != 0) + if (mattock->otyp == DWARVISH_MATTOCK) ++cnt; + /* [ALI] Shopkeeper identifies mattock(s) */ + if (!Blind) makeknown(DWARVISH_MATTOCK); + } + verbalize(NOTANGRY(shkp) ? + "Will you please leave your %s%s outside?" : + "Leave the %s%s outside.", + tool, plur(cnt)); + should_block = TRUE; +#ifdef STEED + } else if (u.usteed) { + verbalize(NOTANGRY(shkp) ? + "Will you please leave %s outside?" : + "Leave %s outside.", y_monnam(u.usteed)); + should_block = TRUE; +#endif + } else { + should_block = (Fast && (sobj_at(PICK_AXE, u.ux, u.uy) || + sobj_at(DWARVISH_MATTOCK, u.ux, u.uy))); + } + if (should_block) (void) dochug(shkp); /* shk gets extra move */ + } + return; +} + +/* + Decide whether two unpaid items are mergable; caller is responsible for + making sure they're unpaid and the same type of object; we check the price + quoted by the shopkeeper and also that they both belong to the same shk. + */ +boolean +same_price(obj1, obj2) +struct obj *obj1, *obj2; +{ + register struct monst *shkp1, *shkp2; + register struct bill_x *bp1 = 0, *bp2 = 0; + register boolean are_mergable = FALSE; + + /* look up the first object by finding shk whose bill it's on */ + for (shkp1 = next_shkp(fmon, TRUE); shkp1; + shkp1 = next_shkp(shkp1->nmon, TRUE)) + if ((bp1 = onbill(obj1, shkp1, TRUE)) != 0) break; + /* second object is probably owned by same shk; if not, look harder */ + if (shkp1 && (bp2 = onbill(obj2, shkp1, TRUE)) != 0) { + shkp2 = shkp1; + } else { + for (shkp2 = next_shkp(fmon, TRUE); shkp2; + shkp2 = next_shkp(shkp2->nmon, TRUE)) + if ((bp2 = onbill(obj2, shkp2, TRUE)) != 0) break; + } + + if (!bp1 || !bp2) impossible("same_price: object wasn't on any bill!"); + else are_mergable = (shkp1 == shkp2 && bp1->price == bp2->price); + return are_mergable; +} + +/* + * Figure out how much is owed to a given shopkeeper. + * At present, we ignore any amount robbed from the shop, to avoid + * turning the `$' command into a way to discover that the current + * level is bones data which has a shk on the warpath. + */ +STATIC_OVL long +shop_debt(eshkp) +struct eshk *eshkp; +{ + struct bill_x *bp; + int ct; + long debt = eshkp->debit; + + for (bp = eshkp->bill_p, ct = eshkp->billct; ct > 0; bp++, ct--) + debt += bp->price * bp->bquan; + return debt; +} + +/* called in response to the `$' command */ +void +shopper_financial_report() +{ + struct monst *shkp, *this_shkp = shop_keeper(inside_shop(u.ux, u.uy)); + struct eshk *eshkp; + long amt; + int pass; + + if (this_shkp && + !(ESHK(this_shkp)->credit || shop_debt(ESHK(this_shkp)))) { + You("have no credit or debt in here."); + this_shkp = 0; /* skip first pass */ + } + + /* pass 0: report for the shop we're currently in, if any; + pass 1: report for all other shops on this level. */ + for (pass = this_shkp ? 0 : 1; pass <= 1; pass++) + for (shkp = next_shkp(fmon, FALSE); + shkp; shkp = next_shkp(shkp->nmon, FALSE)) { + if ((shkp != this_shkp) ^ pass) continue; + eshkp = ESHK(shkp); + if ((amt = eshkp->credit) != 0) + You("have %ld %s credit at %s %s.", + amt, currency(amt), s_suffix(shkname(shkp)), + shtypes[eshkp->shoptype - SHOPBASE].name); + else if (shkp == this_shkp) + You("have no credit in here."); + if ((amt = shop_debt(eshkp)) != 0) + You("owe %s %ld %s.", + shkname(shkp), amt, currency(amt)); + else if (shkp == this_shkp) + You("don't owe any money here."); + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +int +inhishop(mtmp) +register struct monst *mtmp; +{ + return(index(in_rooms(mtmp->mx, mtmp->my, SHOPBASE), + ESHK(mtmp)->shoproom) && + on_level(&(ESHK(mtmp)->shoplevel), &u.uz)); +} + +struct monst * +shop_keeper(rmno) +register char rmno; +{ + struct monst *shkp = rmno >= ROOMOFFSET ? + rooms[rmno - ROOMOFFSET].resident : 0; + + if (shkp) { + if (NOTANGRY(shkp)) { + if (ESHK(shkp)->surcharge) pacify_shk(shkp); + } else { + if (!ESHK(shkp)->surcharge) rile_shk(shkp); + } + } + return shkp; +} + +boolean +tended_shop(sroom) +register struct mkroom *sroom; +{ + register struct monst *mtmp = sroom->resident; + + if (!mtmp) + return(FALSE); + else + return((boolean)(inhishop(mtmp))); +} + +STATIC_OVL struct bill_x * +onbill(obj, shkp, silent) +register struct obj *obj; +register struct monst *shkp; +register boolean silent; +{ + if (shkp) { + register struct bill_x *bp = ESHK(shkp)->bill_p; + register int ct = ESHK(shkp)->billct; + + while (--ct >= 0) + if (bp->bo_id == obj->o_id) { + if (!obj->unpaid) pline("onbill: paid obj on bill?"); + return bp; + } else bp++; + } + if(obj->unpaid & !silent) pline("onbill: unpaid obj not on bill?"); + return (struct bill_x *)0; +} + +/* Delete the contents of the given object. */ +void +delete_contents(obj) +register struct obj *obj; +{ + register struct obj *curr; + + while ((curr = obj->cobj) != 0) { + obj_extract_self(curr); + obfree(curr, (struct obj *)0); + } +} + +/* called with two args on merge */ +void +obfree(obj, merge) +register struct obj *obj, *merge; +{ + register struct bill_x *bp; + register struct bill_x *bpm; + register struct monst *shkp; + + if (obj->otyp == LEASH && obj->leashmon) o_unleash(obj); + if (obj->oclass == FOOD_CLASS) food_disappears(obj); + if (obj->oclass == SPBOOK_CLASS) book_disappears(obj); + if (Has_contents(obj)) delete_contents(obj); + + shkp = 0; + if (obj->unpaid) { + /* look for a shopkeeper who owns this object */ + for (shkp = next_shkp(fmon, TRUE); shkp; + shkp = next_shkp(shkp->nmon, TRUE)) + if (onbill(obj, shkp, TRUE)) break; + } + /* sanity check, more or less */ + if (!shkp) shkp = shop_keeper(*u.ushops); + /* + * Note: `shkp = shop_keeper(*u.ushops)' used to be + * unconditional. But obfree() is used all over + * the place, so making its behavior be dependent + * upon player location doesn't make much sense. + */ + + if ((bp = onbill(obj, shkp, FALSE)) != 0) { + if(!merge){ + bp->useup = 1; + obj->unpaid = 0; /* only for doinvbill */ + add_to_billobjs(obj); + return; + } + bpm = onbill(merge, shkp, FALSE); + if(!bpm){ + /* this used to be a rename */ + impossible("obfree: not on bill??"); + return; + } else { + /* this was a merger */ + bpm->bquan += bp->bquan; + ESHK(shkp)->billct--; +#ifdef DUMB + { + /* DRS/NS 2.2.6 messes up -- Peter Kendell */ + int indx = ESHK(shkp)->billct; + *bp = ESHK(shkp)->bill_p[indx]; + } +#else + *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct]; +#endif + } + } + dealloc_obj(obj); +} +#endif /* OVLB */ +#ifdef OVL3 + +STATIC_OVL long +check_credit(tmp, shkp) +long tmp; +register struct monst *shkp; +{ + long credit = ESHK(shkp)->credit; + + if(credit == 0L) return(tmp); + if(credit >= tmp) { + pline_The("price is deducted from your credit."); + ESHK(shkp)->credit -=tmp; + tmp = 0L; + } else { + pline_The("price is partially covered by your credit."); + ESHK(shkp)->credit = 0L; + tmp -= credit; + } + return(tmp); +} + +STATIC_OVL void +pay(tmp,shkp) +long tmp; +register struct monst *shkp; +{ + long robbed = ESHK(shkp)->robbed; + long balance = ((tmp <= 0L) ? tmp : check_credit(tmp, shkp)); + +#ifndef GOLDOBJ + u.ugold -= balance; + shkp->mgold += balance; +#else + if (balance > 0) money2mon(shkp, balance); + else if (balance < 0) money2u(shkp, -balance); +#endif + flags.botl = 1; + if(robbed) { + robbed -= tmp; + if(robbed < 0) robbed = 0L; + ESHK(shkp)->robbed = robbed; + } +} +#endif /*OVL3*/ +#ifdef OVLB + +/* return shkp to home position */ +void +home_shk(shkp, killkops) +register struct monst *shkp; +register boolean killkops; +{ + register xchar x = ESHK(shkp)->shk.x, y = ESHK(shkp)->shk.y; + + (void) mnearto(shkp, x, y, TRUE); + level.flags.has_shop = 1; + if (killkops) { +#ifdef KOPS + kops_gone(TRUE); +#else + You_feel("vaguely apprehensive."); +#endif + pacify_guards(); + } + after_shk_move(shkp); +} + +STATIC_OVL boolean +angry_shk_exists() +{ + register struct monst *shkp; + + for (shkp = next_shkp(fmon, FALSE); + shkp; shkp = next_shkp(shkp->nmon, FALSE)) + if (ANGRY(shkp)) return(TRUE); + return(FALSE); +} + +/* remove previously applied surcharge from all billed items */ +STATIC_OVL void +pacify_shk(shkp) +register struct monst *shkp; +{ + NOTANGRY(shkp) = TRUE; /* make peaceful */ + if (ESHK(shkp)->surcharge) { + register struct bill_x *bp = ESHK(shkp)->bill_p; + register int ct = ESHK(shkp)->billct; + + ESHK(shkp)->surcharge = FALSE; + while (ct-- > 0) { + register long reduction = (bp->price + 3L) / 4L; + bp->price -= reduction; /* undo 33% increase */ + bp++; + } + } +} + +/* add aggravation surcharge to all billed items */ +STATIC_OVL void +rile_shk(shkp) +register struct monst *shkp; +{ + NOTANGRY(shkp) = FALSE; /* make angry */ + if (!ESHK(shkp)->surcharge) { + register struct bill_x *bp = ESHK(shkp)->bill_p; + register int ct = ESHK(shkp)->billct; + + ESHK(shkp)->surcharge = TRUE; + while (ct-- > 0) { + register long surcharge = (bp->price + 2L) / 3L; + bp->price += surcharge; + bp++; + } + } +} + +/* wakeup and/or unparalyze shopkeeper */ +STATIC_OVL void +rouse_shk(shkp, verbosely) +struct monst *shkp; +boolean verbosely; +{ + if (!shkp->mcanmove || shkp->msleeping) { + /* greed induced recovery... */ + if (verbosely && canspotmon(shkp)) + pline("%s %s.", Monnam(shkp), + shkp->msleeping ? "wakes up" : "can move again"); + shkp->msleeping = 0; + shkp->mfrozen = 0; + shkp->mcanmove = 1; + } +} + +void +make_happy_shk(shkp, silentkops) +register struct monst *shkp; +register boolean silentkops; +{ + boolean wasmad = ANGRY(shkp); + struct eshk *eshkp = ESHK(shkp); + + pacify_shk(shkp); + eshkp->following = 0; + eshkp->robbed = 0L; + if (!Role_if(PM_ROGUE)) + adjalign(sgn(u.ualign.type)); + if(!inhishop(shkp)) { + char shk_nam[BUFSZ]; + boolean vanished = canseemon(shkp); + + Strcpy(shk_nam, mon_nam(shkp)); + if (on_level(&eshkp->shoplevel, &u.uz)) { + home_shk(shkp, FALSE); + /* didn't disappear if shk can still be seen */ + if (canseemon(shkp)) vanished = FALSE; + } else { + /* if sensed, does disappear regardless whether seen */ + if (sensemon(shkp)) vanished = TRUE; + /* can't act as porter for the Amulet, even if shk + happens to be going farther down rather than up */ + mdrop_special_objs(shkp); + /* arrive near shop's door */ + migrate_to_level(shkp, ledger_no(&eshkp->shoplevel), + MIGR_APPROX_XY, &eshkp->shd); + } + if (vanished) + pline("Satisfied, %s suddenly disappears!", shk_nam); + } else if(wasmad) + pline("%s calms down.", Monnam(shkp)); + + if(!angry_shk_exists()) { +#ifdef KOPS + kops_gone(silentkops); +#endif + pacify_guards(); + } +} + +void +hot_pursuit(shkp) +register struct monst *shkp; +{ + if(!shkp->isshk) return; + + rile_shk(shkp); + (void) strncpy(ESHK(shkp)->customer, plname, PL_NSIZ); + ESHK(shkp)->following = 1; +} + +/* used when the shkp is teleported or falls (ox == 0) out of his shop, + * or when the player is not on a costly_spot and he + * damages something inside the shop. these conditions + * must be checked by the calling function. + */ +void +make_angry_shk(shkp, ox, oy) +register struct monst *shkp; +register xchar ox,oy; +{ + xchar sx, sy; + struct eshk *eshkp = ESHK(shkp); + + /* all pending shop transactions are now "past due" */ + if (eshkp->billct || eshkp->debit || eshkp->loan || eshkp->credit) { + eshkp->robbed += (addupbill(shkp) + eshkp->debit + eshkp->loan); + eshkp->robbed -= eshkp->credit; + if (eshkp->robbed < 0L) eshkp->robbed = 0L; + /* billct, debit, loan, and credit will be cleared by setpaid */ + setpaid(shkp); + } + + /* If you just used a wand of teleportation to send the shk away, you + might not be able to see her any more. Monnam would yield "it", + which makes this message look pretty silly, so temporarily restore + her original location during the call to Monnam. */ + sx = shkp->mx, sy = shkp->my; + if (isok(ox, oy) && cansee(ox, oy) && !cansee(sx, sy)) + shkp->mx = ox, shkp->my = oy; + pline("%s %s!", Monnam(shkp), + !ANGRY(shkp) ? "gets angry" : "is furious"); + shkp->mx = sx, shkp->my = sy; + hot_pursuit(shkp); +} + +STATIC_VAR const char no_money[] = "Moreover, you%s have no money."; +STATIC_VAR const char not_enough_money[] = + "Besides, you don't have enough to interest %s."; + +#else +STATIC_VAR const char no_money[]; +STATIC_VAR const char not_enough_money[]; +#endif /*OVLB*/ + +#ifdef OVL3 + +STATIC_OVL long +cheapest_item(shkp) /* delivers the cheapest item on the list */ +register struct monst *shkp; +{ + register int ct = ESHK(shkp)->billct; + register struct bill_x *bp = ESHK(shkp)->bill_p; + register long gmin = (bp->price * bp->bquan); + + while(ct--){ + if(bp->price * bp->bquan < gmin) + gmin = bp->price * bp->bquan; + bp++; + } + return(gmin); +} +#endif /*OVL3*/ +#ifdef OVL0 + +int +dopay() +{ + register struct eshk *eshkp; + register struct monst *shkp; + struct monst *nxtm, *resident; + long ltmp; +#ifdef GOLDOBJ + long umoney; +#endif + int pass, tmp, sk = 0, seensk = 0; + boolean paid = FALSE, stashed_gold = (hidden_gold() > 0L); + + multi = 0; + + /* find how many shk's there are, how many are in */ + /* sight, and are you in a shop room with one. */ + nxtm = resident = 0; + for (shkp = next_shkp(fmon, FALSE); + shkp; shkp = next_shkp(shkp->nmon, FALSE)) { + sk++; + if (ANGRY(shkp) && distu(shkp->mx, shkp->my) <= 2) nxtm = shkp; + if (canspotmon(shkp)) seensk++; + if (inhishop(shkp) && (*u.ushops == ESHK(shkp)->shoproom)) + resident = shkp; + } + + if (nxtm) { /* Player should always appease an */ + shkp = nxtm; /* irate shk standing next to them. */ + goto proceed; + } + + if ((!sk && (!Blind || Blind_telepat)) || (!Blind && !seensk)) { + There("appears to be no shopkeeper here to receive your payment."); + return(0); + } + + if(!seensk) { + You_cant("see..."); + return(0); + } + + /* the usual case. allow paying at a distance when */ + /* inside a tended shop. should we change that? */ + if(sk == 1 && resident) { + shkp = resident; + goto proceed; + } + + if (seensk == 1) { + for (shkp = next_shkp(fmon, FALSE); + shkp; shkp = next_shkp(shkp->nmon, FALSE)) + if (canspotmon(shkp)) break; + if (shkp != resident && distu(shkp->mx, shkp->my) > 2) { + pline("%s is not near enough to receive your payment.", + Monnam(shkp)); + return(0); + } + } else { + struct monst *mtmp; + coord cc; + int cx, cy; + + pline("Pay whom?"); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the creature you want to pay") < 0) + return 0; /* player pressed ESC */ + cx = cc.x; + cy = cc.y; + if(cx < 0) { + pline("Try again..."); + return(0); + } + if(u.ux == cx && u.uy == cy) { + You("are generous to yourself."); + return(0); + } + mtmp = m_at(cx, cy); + if(!mtmp) { + There("is no one there to receive your payment."); + return(0); + } + if(!mtmp->isshk) { + pline("%s is not interested in your payment.", + Monnam(mtmp)); + return(0); + } + if (mtmp != resident && distu(mtmp->mx, mtmp->my) > 2) { + pline("%s is too far to receive your payment.", + Monnam(mtmp)); + return(0); + } + shkp = mtmp; + } + + if(!shkp) { +#ifdef DEBUG + pline("dopay: null shkp."); +#endif + return(0); + } +proceed: + eshkp = ESHK(shkp); + ltmp = eshkp->robbed; + + /* wake sleeping shk when someone who owes money offers payment */ + if (ltmp || eshkp->billct || eshkp->debit) + rouse_shk(shkp, TRUE); + + if (!shkp->mcanmove || shkp->msleeping) { /* still asleep/paralyzed */ + pline("%s %s.", Monnam(shkp), + rn2(2) ? "seems to be napping" : "doesn't respond"); + return 0; + } + + if(shkp != resident && NOTANGRY(shkp)) { +#ifdef GOLDOBJ + umoney = money_cnt(invent); +#endif + if(!ltmp) + You("do not owe %s anything.", mon_nam(shkp)); +#ifndef GOLDOBJ + else if(!u.ugold) { +#else + else if(!umoney) { +#endif + You("%shave no money.", stashed_gold ? "seem to " : ""); + if(stashed_gold) + pline("But you have some gold stashed away."); + } else { +#ifndef GOLDOBJ + long ugold = u.ugold; + if(ugold > ltmp) { +#else + if(umoney > ltmp) { +#endif + You("give %s the %ld gold piece%s %s asked for.", + mon_nam(shkp), ltmp, plur(ltmp), mhe(shkp)); + pay(ltmp, shkp); + } else { + You("give %s all your%s gold.", mon_nam(shkp), + stashed_gold ? " openly kept" : ""); +#ifndef GOLDOBJ + pay(u.ugold, shkp); +#else + pay(umoney, shkp); +#endif + if (stashed_gold) pline("But you have hidden gold!"); + } +#ifndef GOLDOBJ + if((ugold < ltmp/2L) || (ugold < ltmp && stashed_gold)) +#else + if((umoney < ltmp/2L) || (umoney < ltmp && stashed_gold)) +#endif + pline("Unfortunately, %s doesn't look satisfied.", + mhe(shkp)); + else + make_happy_shk(shkp, FALSE); + } + return(1); + } + + /* ltmp is still eshkp->robbed here */ + if (!eshkp->billct && !eshkp->debit) { +#ifdef GOLDOBJ + umoney = money_cnt(invent); +#endif + if(!ltmp && NOTANGRY(shkp)) { + You("do not owe %s anything.", mon_nam(shkp)); +#ifndef GOLDOBJ + if (!u.ugold) +#else + if (!umoney) +#endif + pline(no_money, stashed_gold ? " seem to" : ""); + } else if(ltmp) { + pline("%s is after blood, not money!", Monnam(shkp)); +#ifndef GOLDOBJ + if(u.ugold < ltmp/2L || + (u.ugold < ltmp && stashed_gold)) { + if (!u.ugold) +#else + if(umoney < ltmp/2L || + (umoney < ltmp && stashed_gold)) { + if (!umoney) +#endif + pline(no_money, stashed_gold ? " seem to" : ""); + else pline(not_enough_money, mhim(shkp)); + return(1); + } + pline("But since %s shop has been robbed recently,", + mhis(shkp)); + pline("you %scompensate %s for %s losses.", +#ifndef GOLDOBJ + (u.ugold < ltmp) ? +#else + (umoney < ltmp) ? +#endif + "partially " : "", + mon_nam(shkp), mhis(shkp)); +#ifndef GOLDOBJ + pay(u.ugold < ltmp ? u.ugold : ltmp, shkp); +#else + pay(umoney < ltmp ? umoney : ltmp, shkp); +#endif + make_happy_shk(shkp, FALSE); + } else { + /* shopkeeper is angry, but has not been robbed -- + * door broken, attacked, etc. */ + pline("%s is after your hide, not your money!", + Monnam(shkp)); +#ifndef GOLDOBJ + if(u.ugold < 1000L) { + if (!u.ugold) +#else + if(umoney < 1000L) { + if (!umoney) +#endif + pline(no_money, stashed_gold ? " seem to" : ""); + else pline(not_enough_money, mhim(shkp)); + return(1); + } + You("try to appease %s by giving %s 1000 gold pieces.", + x_monnam(shkp, ARTICLE_THE, "angry", 0, FALSE), + mhim(shkp)); + pay(1000L,shkp); + if (strncmp(eshkp->customer, plname, PL_NSIZ) || rn2(3)) + make_happy_shk(shkp, FALSE); + else + pline("But %s is as angry as ever.", mon_nam(shkp)); + } + return(1); + } + if(shkp != resident) { + impossible("dopay: not to shopkeeper?"); + if(resident) setpaid(resident); + return(0); + } + /* pay debt, if any, first */ + if(eshkp->debit) { + long dtmp = eshkp->debit; + long loan = eshkp->loan; + char sbuf[BUFSZ]; +#ifdef GOLDOBJ + umoney = money_cnt(invent); +#endif + Sprintf(sbuf, "You owe %s %ld %s ", + shkname(shkp), dtmp, currency(dtmp)); + if(loan) { + if(loan == dtmp) + Strcat(sbuf, "you picked up in the store."); + else Strcat(sbuf, + "for gold picked up and the use of merchandise."); + } else Strcat(sbuf, "for the use of merchandise."); + pline(sbuf); +#ifndef GOLDOBJ + if (u.ugold + eshkp->credit < dtmp) { +#else + if (umoney + eshkp->credit < dtmp) { +#endif + pline("But you don't%s have enough gold%s.", + stashed_gold ? " seem to" : "", + eshkp->credit ? " or credit" : ""); + return(1); + } else { + if (eshkp->credit >= dtmp) { + eshkp->credit -= dtmp; + eshkp->debit = 0L; + eshkp->loan = 0L; + Your("debt is covered by your credit."); + } else if (!eshkp->credit) { +#ifndef GOLDOBJ + u.ugold -= dtmp; + shkp->mgold += dtmp; +#else + money2mon(shkp, dtmp); +#endif + eshkp->debit = 0L; + eshkp->loan = 0L; + You("pay that debt."); + flags.botl = 1; + } else { + dtmp -= eshkp->credit; + eshkp->credit = 0L; +#ifndef GOLDOBJ + u.ugold -= dtmp; + shkp->mgold += dtmp; +#else + money2mon(shkp, dtmp); +#endif + eshkp->debit = 0L; + eshkp->loan = 0L; + pline("That debt is partially offset by your credit."); + You("pay the remainder."); + flags.botl = 1; + } + paid = TRUE; + } + } + /* now check items on bill */ + if (eshkp->billct) { + register boolean itemize; +#ifndef GOLDOBJ + if (!u.ugold && !eshkp->credit) { +#else + umoney = money_cnt(invent); + if (!umoney && !eshkp->credit) { +#endif + You("%shave no money or credit%s.", + stashed_gold ? "seem to " : "", + paid ? " left" : ""); + return(0); + } +#ifndef GOLDOBJ + if ((u.ugold + eshkp->credit) < cheapest_item(shkp)) { +#else + if ((umoney + eshkp->credit) < cheapest_item(shkp)) { +#endif + You("don't have enough money to buy%s the item%s you picked.", + eshkp->billct > 1 ? " any of" : "", plur(eshkp->billct)); + if(stashed_gold) + pline("Maybe you have some gold stashed away?"); + return(0); + } + + /* this isn't quite right; it itemizes without asking if the + * single item on the bill is partly used up and partly unpaid */ + itemize = (eshkp->billct > 1 ? yn("Itemized billing?") == 'y' : 1); + + for (pass = 0; pass <= 1; pass++) { + tmp = 0; + while (tmp < eshkp->billct) { + struct obj *otmp; + register struct bill_x *bp = &(eshkp->bill_p[tmp]); + + /* find the object on one of the lists */ + if ((otmp = bp_to_obj(bp)) != 0) { + /* if completely used up, object quantity is stale; + restoring it to its original value here avoids + making the partly-used-up code more complicated */ + if (bp->useup) otmp->quan = bp->bquan; + } else { + impossible("Shopkeeper administration out of order."); + setpaid(shkp); /* be nice to the player */ + return 1; + } + if (pass == bp->useup && otmp->quan == bp->bquan) { + /* pay for used-up items on first pass and others + * on second, so player will be stuck in the store + * less often; things which are partly used up + * are processed on both passes */ + tmp++; + } else { + switch (dopayobj(shkp, bp, &otmp, pass, itemize)) { + case PAY_CANT: + return 1; /*break*/ + case PAY_BROKE: + paid = TRUE; + goto thanks; /*break*/ + case PAY_SKIP: + tmp++; + continue; /*break*/ + case PAY_SOME: + paid = TRUE; + if (itemize) bot(); + continue; /*break*/ + case PAY_BUY: + paid = TRUE; + break; + } + if (itemize) bot(); + *bp = eshkp->bill_p[--eshkp->billct]; + } + } + } + thanks: + if (!itemize) + update_inventory(); /* Done in dopayobj() if itemize. */ + } + if(!ANGRY(shkp) && paid) + verbalize("Thank you for shopping in %s %s!", + s_suffix(shkname(shkp)), + shtypes[eshkp->shoptype - SHOPBASE].name); + return(1); +} +#endif /*OVL0*/ +#ifdef OVL3 + +/* return 2 if used-up portion paid */ +/* 1 if paid successfully */ +/* 0 if not enough money */ +/* -1 if skip this object */ +/* -2 if no money/credit left */ +STATIC_OVL int +dopayobj(shkp, bp, obj_p, which, itemize) +register struct monst *shkp; +register struct bill_x *bp; +struct obj **obj_p; +int which; /* 0 => used-up item, 1 => other (unpaid or lost) */ +boolean itemize; +{ + register struct obj *obj = *obj_p; + long ltmp, quan, save_quan; +#ifdef GOLDOBJ + long umoney = money_cnt(invent); +#endif + int buy; + boolean stashed_gold = (hidden_gold() > 0L), + consumed = (which == 0); + + if(!obj->unpaid && !bp->useup){ + impossible("Paid object on bill??"); + return PAY_BUY; + } +#ifndef GOLDOBJ + if(itemize && u.ugold + ESHK(shkp)->credit == 0L){ +#else + if(itemize && umoney + ESHK(shkp)->credit == 0L){ +#endif + You("%shave no money or credit left.", + stashed_gold ? "seem to " : ""); + return PAY_BROKE; + } + /* we may need to temporarily adjust the object, if part of the + original quantity has been used up but part remains unpaid */ + save_quan = obj->quan; + if (consumed) { + /* either completely used up (simple), or split needed */ + quan = bp->bquan; + if (quan > obj->quan) /* difference is amount used up */ + quan -= obj->quan; + } else { + /* dealing with ordinary unpaid item */ + quan = obj->quan; + } + obj->quan = quan; /* to be used by doname() */ + obj->unpaid = 0; /* ditto */ + ltmp = bp->price * quan; + buy = PAY_BUY; /* flag; if changed then return early */ + + if (itemize) { + char qbuf[BUFSZ]; + Sprintf(qbuf,"%s for %ld %s. Pay?", quan == 1L ? + Doname2(obj) : doname(obj), ltmp, currency(ltmp)); + if (yn(qbuf) == 'n') { + buy = PAY_SKIP; /* don't want to buy */ + } else if (quan < bp->bquan && !consumed) { /* partly used goods */ + obj->quan = bp->bquan - save_quan; /* used up amount */ + verbalize("%s for the other %s before buying %s.", + ANGRY(shkp) ? "Pay" : "Please pay", xname(obj), + save_quan > 1L ? "these" : "this one"); + buy = PAY_SKIP; /* shk won't sell */ + } + } +#ifndef GOLDOBJ + if (buy == PAY_BUY && u.ugold + ESHK(shkp)->credit < ltmp) { +#else + if (buy == PAY_BUY && umoney + ESHK(shkp)->credit < ltmp) { +#endif + You("don't%s have gold%s enough to pay for %s.", + stashed_gold ? " seem to" : "", + (ESHK(shkp)->credit > 0L) ? " or credit" : "", + doname(obj)); + buy = itemize ? PAY_SKIP : PAY_CANT; + } + + if (buy != PAY_BUY) { + /* restore unpaid object to original state */ + obj->quan = save_quan; + obj->unpaid = 1; + return buy; + } + + pay(ltmp, shkp); + shk_names_obj(shkp, obj, consumed ? + "paid for %s at a cost of %ld gold piece%s.%s" : + "bought %s for %ld gold piece%s.%s", ltmp, ""); + obj->quan = save_quan; /* restore original count */ + /* quan => amount just bought, save_quan => remaining unpaid count */ + if (consumed) { + if (quan != bp->bquan) { + /* eliminate used-up portion; remainder is still unpaid */ + bp->bquan = obj->quan; + obj->unpaid = 1; + bp->useup = 0; + buy = PAY_SOME; + } else { /* completely used-up, so get rid of it */ + obj_extract_self(obj); + /* assert( obj == *obj_p ); */ + dealloc_obj(obj); + *obj_p = 0; /* destroy pointer to freed object */ + } + } else if (itemize) + update_inventory(); /* Done just once in dopay() if !itemize. */ + return buy; +} +#endif /*OVL3*/ +#ifdef OVLB + +static coord repo_location; /* repossession context */ + +/* routine called after dying (or quitting) */ +boolean +paybill(croaked) +int croaked; /* -1: escaped dungeon; 0: quit; 1: died */ +{ + register struct monst *mtmp, *mtmp2, *resident= (struct monst *)0; + register boolean taken = FALSE; + register int numsk = 0; + + /* if we escaped from the dungeon, shopkeepers can't reach us; + shops don't occur on level 1, but this could happen if hero + level teleports out of the dungeon and manages not to die */ + if (croaked < 0) return FALSE; + + /* this is where inventory will end up if any shk takes it */ + repo_location.x = repo_location.y = 0; + + /* give shopkeeper first crack */ + if ((mtmp = shop_keeper(*u.ushops)) && inhishop(mtmp)) { + numsk++; + resident = mtmp; + taken = inherits(resident, numsk, croaked); + } + for (mtmp = next_shkp(fmon, FALSE); + mtmp; mtmp = next_shkp(mtmp2, FALSE)) { + mtmp2 = mtmp->nmon; + if (mtmp != resident) { + /* for bones: we don't want a shopless shk around */ + if(!on_level(&(ESHK(mtmp)->shoplevel), &u.uz)) + mongone(mtmp); + else { + numsk++; + taken |= inherits(mtmp, numsk, croaked); + } + } + } + if(numsk == 0) return(FALSE); + return(taken); +} + +STATIC_OVL boolean +inherits(shkp, numsk, croaked) +struct monst *shkp; +int numsk; +int croaked; +{ + long loss = 0L; +#ifdef GOLDOBJ + long umoney; +#endif + struct eshk *eshkp = ESHK(shkp); + boolean take = FALSE, taken = FALSE; + int roomno = *u.ushops; + char takes[BUFSZ]; + + /* the simplifying principle is that first-come */ + /* already took everything you had. */ + if (numsk > 1) { + if (cansee(shkp->mx, shkp->my && croaked)) + pline("%s %slooks at your corpse%s and %s.", + Monnam(shkp), + (!shkp->mcanmove || shkp->msleeping) ? "wakes up, " : "", + !rn2(2) ? (shkp->female ? ", shakes her head," : + ", shakes his head,") : "", + !inhishop(shkp) ? "disappears" : "sighs"); + rouse_shk(shkp, FALSE); /* wake shk for bones */ + taken = (roomno == eshkp->shoproom); + goto skip; + } + + /* get one case out of the way: you die in the shop, the */ + /* shopkeeper is peaceful, nothing stolen, nothing owed. */ + if(roomno == eshkp->shoproom && inhishop(shkp) && + !eshkp->billct && !eshkp->robbed && !eshkp->debit && + NOTANGRY(shkp) && !eshkp->following) { + if (invent) + pline("%s gratefully inherits all your possessions.", + shkname(shkp)); + set_repo_loc(eshkp); + goto clear; + } + + if (eshkp->billct || eshkp->debit || eshkp->robbed) { + if (roomno == eshkp->shoproom && inhishop(shkp)) + loss = addupbill(shkp) + eshkp->debit; + if (loss < eshkp->robbed) loss = eshkp->robbed; + take = TRUE; + } + + if (eshkp->following || ANGRY(shkp) || take) { +#ifndef GOLDOBJ + if (!invent && !u.ugold) goto skip; +#else + if (!invent) goto skip; + umoney = money_cnt(invent); +#endif + takes[0] = '\0'; + if (!shkp->mcanmove || shkp->msleeping) + Strcat(takes, "wakes up and "); + if (distu(shkp->mx, shkp->my) > 2) + Strcat(takes, "comes and "); + Strcat(takes, "takes"); + +#ifndef GOLDOBJ + if (loss > u.ugold || !loss || roomno == eshkp->shoproom) { + eshkp->robbed -= u.ugold; + if (eshkp->robbed < 0L) eshkp->robbed = 0L; + shkp->mgold += u.ugold; + u.ugold = 0L; +#else + if (loss > umoney || !loss || roomno == eshkp->shoproom) { + eshkp->robbed -= umoney; + if (eshkp->robbed < 0L) eshkp->robbed = 0L; + if (umoney > 0) money2mon(shkp, umoney); +#endif + flags.botl = 1; + pline("%s %s all your possessions.", + shkname(shkp), takes); + taken = TRUE; + /* where to put player's invent (after disclosure) */ + set_repo_loc(eshkp); + } else { +#ifndef GOLDOBJ + shkp->mgold += loss; + u.ugold -= loss; +#else + money2mon(shkp, loss); +#endif + flags.botl = 1; + pline("%s %s the %ld %s %sowed %s.", + Monnam(shkp), takes, + loss, currency(loss), + strncmp(eshkp->customer, plname, PL_NSIZ) ? + "" : "you ", + shkp->female ? "her" : "him"); + /* shopkeeper has now been paid in full */ + pacify_shk(shkp); + eshkp->following = 0; + eshkp->robbed = 0L; + } +skip: + /* in case we create bones */ + rouse_shk(shkp, FALSE); /* wake up */ + if (!inhishop(shkp)) + home_shk(shkp, FALSE); + } +clear: + setpaid(shkp); + return(taken); +} + +STATIC_OVL void +set_repo_loc(eshkp) +struct eshk *eshkp; +{ + register xchar ox, oy; + + /* if you're not in this shk's shop room, or if you're in its doorway + or entry spot, then your gear gets dumped all the way inside */ + if (*u.ushops != eshkp->shoproom || + IS_DOOR(levl[u.ux][u.uy].typ) || + (u.ux == eshkp->shk.x && u.uy == eshkp->shk.y)) { + /* shk.x,shk.y is the position immediately in + * front of the door -- move in one more space + */ + ox = eshkp->shk.x; + oy = eshkp->shk.y; + ox += sgn(ox - eshkp->shd.x); + oy += sgn(oy - eshkp->shd.y); + } else { /* already inside this shk's shop */ + ox = u.ux; + oy = u.uy; + } + /* finish_paybill will deposit invent here */ + repo_location.x = ox; + repo_location.y = oy; +} + +/* called at game exit, after inventory disclosure but before making bones */ +void +finish_paybill() +{ + register struct obj *otmp; + int ox = repo_location.x, + oy = repo_location.y; + +#if 0 /* don't bother */ + if (ox == 0 && oy == 0) impossible("finish_paybill: no location"); +#endif + /* normally done by savebones(), but that's too late in this case */ + unleash_all(); + /* transfer all of the character's inventory to the shop floor */ + while ((otmp = invent) != 0) { + otmp->owornmask = 0L; /* perhaps we should call setnotworn? */ + otmp->lamplit = 0; /* avoid "goes out" msg from freeinv */ + if (rn2(5)) curse(otmp); /* normal bones treatment for invent */ + obj_extract_self(otmp); + place_object(otmp, ox, oy); + } +} + +/* find obj on one of the lists */ +STATIC_OVL struct obj * +bp_to_obj(bp) +register struct bill_x *bp; +{ + register struct obj *obj; + register unsigned int id = bp->bo_id; + + if(bp->useup) + obj = o_on(id, billobjs); + else + obj = find_oid(id); + return obj; +} + +/* + * Look for o_id on all lists but billobj. Return obj or NULL if not found. + * Its OK for restore_timers() to call this function, there should not + * be any timeouts on the billobjs chain. + */ +struct obj * +find_oid(id) +unsigned id; +{ + struct obj *obj; + struct monst *mon, *mmtmp[3]; + int i; + + /* first check various obj lists directly */ + if ((obj = o_on(id, invent)) != 0) return obj; + if ((obj = o_on(id, fobj)) != 0) return obj; + if ((obj = o_on(id, level.buriedobjlist)) != 0) return obj; + if ((obj = o_on(id, migrating_objs)) != 0) return obj; + + /* not found yet; check inventory for members of various monst lists */ + mmtmp[0] = fmon; + mmtmp[1] = migrating_mons; + mmtmp[2] = mydogs; /* for use during level changes */ + for (i = 0; i < 3; i++) + for (mon = mmtmp[i]; mon; mon = mon->nmon) + if ((obj = o_on(id, mon->minvent)) != 0) return obj; + + /* not found at all */ + return (struct obj *)0; +} +#endif /*OVLB*/ +#ifdef OVL3 + +/* calculate the value that the shk will charge for [one of] an object */ +STATIC_OVL long +get_cost(obj, shkp) +register struct obj *obj; +register struct monst *shkp; /* if angry, impose a surcharge */ +{ + register long tmp = getprice(obj, FALSE); + + if (!tmp) tmp = 5L; + /* shopkeeper may notice if the player isn't very knowledgeable - + especially when gem prices are concerned */ + if (!obj->dknown || !objects[obj->otyp].oc_name_known) { + if (obj->oclass == GEM_CLASS && + objects[obj->otyp].oc_material == GLASS) { + int i; + /* get a value that's 'random' from game to game, but the + same within the same game */ + boolean pseudorand = + (((int)u.ubirthday % obj->otyp) >= obj->otyp/2); + + /* all gems are priced high - real or not */ + switch(obj->otyp - LAST_GEM) { + case 1: /* white */ + i = pseudorand ? DIAMOND : OPAL; + break; + case 2: /* blue */ + i = pseudorand ? SAPPHIRE : AQUAMARINE; + break; + case 3: /* red */ + i = pseudorand ? RUBY : JASPER; + break; + case 4: /* yellowish brown */ + i = pseudorand ? AMBER : TOPAZ; + break; + case 5: /* orange */ + i = pseudorand ? JACINTH : AGATE; + break; + case 6: /* yellow */ + i = pseudorand ? CITRINE : CHRYSOBERYL; + break; + case 7: /* black */ + i = pseudorand ? BLACK_OPAL : JET; + break; + case 8: /* green */ + i = pseudorand ? EMERALD : JADE; + break; + case 9: /* violet */ + i = pseudorand ? AMETHYST : FLUORITE; + break; + default: impossible("bad glass gem %d?", obj->otyp); + i = STRANGE_OBJECT; + break; + } + tmp = (long) objects[i].oc_cost; + } else if (!(obj->o_id % 4)) /* arbitrarily impose surcharge */ + tmp += tmp / 3L; + } +#ifdef TOURIST + if ((Role_if(PM_TOURIST) && u.ulevel < (MAXULEV/2)) + || (uarmu && !uarm && !uarmc)) /* touristy shirt visible */ + tmp += tmp / 3L; + else +#endif + if (uarmh && uarmh->otyp == DUNCE_CAP) + tmp += tmp / 3L; + + if (ACURR(A_CHA) > 18) tmp /= 2L; + else if (ACURR(A_CHA) > 17) tmp -= tmp / 3L; + else if (ACURR(A_CHA) > 15) tmp -= tmp / 4L; + else if (ACURR(A_CHA) < 6) tmp *= 2L; + else if (ACURR(A_CHA) < 8) tmp += tmp / 2L; + else if (ACURR(A_CHA) < 11) tmp += tmp / 3L; + if (tmp <= 0L) tmp = 1L; + else if (obj->oartifact) tmp *= 4L; + /* anger surcharge should match rile_shk's */ + if (shkp && ESHK(shkp)->surcharge) tmp += (tmp + 2L) / 3L; + return tmp; +} +#endif /*OVL3*/ +#ifdef OVLB + +/* returns the price of a container's content. the price + * of the "top" container is added in the calling functions. + * a different price quoted for selling as vs. buying. + */ +long +contained_cost(obj, shkp, price, usell, unpaid_only) +register struct obj *obj; +register struct monst *shkp; +long price; +register boolean usell; +register boolean unpaid_only; +{ + register struct obj *otmp; + + /* the price of contained objects */ + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if (otmp->oclass == COIN_CLASS) continue; + /* the "top" container is evaluated by caller */ + if (usell) { + if (saleable(shkp, otmp) && + !otmp->unpaid && otmp->oclass != BALL_CLASS && + !(otmp->oclass == FOOD_CLASS && otmp->oeaten) && + !(Is_candle(otmp) && otmp->age < + 20L * (long)objects[otmp->otyp].oc_cost)) + price += set_cost(otmp, shkp); + } else if (!otmp->no_charge && + (!unpaid_only || (unpaid_only && otmp->unpaid))) { + price += get_cost(otmp, shkp) * otmp->quan; + } + + if (Has_contents(otmp)) + price += contained_cost(otmp, shkp, price, usell, unpaid_only); + } + + return(price); +} + +long +contained_gold(obj) +register struct obj *obj; +{ + register struct obj *otmp; + register long value = 0L; + + /* accumulate contained gold */ + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) + if (otmp->oclass == COIN_CLASS) + value += otmp->quan; + else if (Has_contents(otmp)) + value += contained_gold(otmp); + + return(value); +} + +STATIC_OVL void +dropped_container(obj, shkp, sale) +register struct obj *obj; +register struct monst *shkp; +register boolean sale; +{ + register struct obj *otmp; + + /* the "top" container is treated in the calling fn */ + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if (otmp->oclass == COIN_CLASS) continue; + + if (!otmp->unpaid && !(sale && saleable(shkp, otmp))) + otmp->no_charge = 1; + + if (Has_contents(otmp)) + dropped_container(otmp, shkp, sale); + } +} + +void +picked_container(obj) +register struct obj *obj; +{ + register struct obj *otmp; + + /* the "top" container is treated in the calling fn */ + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if (otmp->oclass == COIN_CLASS) continue; + + if (otmp->no_charge) + otmp->no_charge = 0; + + if (Has_contents(otmp)) + picked_container(otmp); + } +} +#endif /*OVLB*/ +#ifdef OVL3 + +/* calculate how much the shk will pay when buying [all of] an object */ +STATIC_OVL long +set_cost(obj, shkp) +register struct obj *obj; +register struct monst *shkp; +{ + long tmp = getprice(obj, TRUE) * obj->quan; + +#ifdef TOURIST + if ((Role_if(PM_TOURIST) && u.ulevel < (MAXULEV/2)) + || (uarmu && !uarm && !uarmc)) /* touristy shirt visible */ + tmp /= 3L; + else +#endif + if (uarmh && uarmh->otyp == DUNCE_CAP) + tmp /= 3L; + else + tmp /= 2L; + + /* shopkeeper may notice if the player isn't very knowledgeable - + especially when gem prices are concerned */ + if (!obj->dknown || !objects[obj->otyp].oc_name_known) { + if (obj->oclass == GEM_CLASS) { + /* different shop keepers give different prices */ + if (objects[obj->otyp].oc_material == GEMSTONE || + objects[obj->otyp].oc_material == GLASS) { + tmp = (obj->otyp % (6 - shkp->m_id % 3)); + tmp = (tmp + 3) * obj->quan; + } + } else if (tmp > 1L && !rn2(4)) + tmp -= tmp / 4L; + } + return tmp; +} + +#endif /*OVL3*/ +#ifdef OVLB + +/* called from doinv(invent.c) for inventory of unpaid objects */ +long +unpaid_cost(unp_obj) +register struct obj *unp_obj; /* known to be unpaid */ +{ + register struct bill_x *bp = (struct bill_x *)0; + register struct monst *shkp; + + for(shkp = next_shkp(fmon, TRUE); shkp; + shkp = next_shkp(shkp->nmon, TRUE)) + if ((bp = onbill(unp_obj, shkp, TRUE)) != 0) break; + + /* onbill() gave no message if unexpected problem occurred */ + if(!bp) impossible("unpaid_cost: object wasn't on any bill!"); + + return bp ? unp_obj->quan * bp->price : 0L; +} + +STATIC_OVL void +add_one_tobill(obj, dummy) +register struct obj *obj; +register boolean dummy; +{ + register struct monst *shkp; + register struct bill_x *bp; + register int bct; + register char roomno = *u.ushops; + + if (!roomno) return; + if (!(shkp = shop_keeper(roomno))) return; + if (!inhishop(shkp)) return; + + if (onbill(obj, shkp, FALSE) || /* perhaps thrown away earlier */ + (obj->oclass == FOOD_CLASS && obj->oeaten)) + return; + + if (ESHK(shkp)->billct == BILLSZ) { + You("got that for free!"); + return; + } + + /* To recognize objects the shopkeeper is not interested in. -dgk + */ + if (obj->no_charge) { + obj->no_charge = 0; + return; + } + + bct = ESHK(shkp)->billct; + bp = &(ESHK(shkp)->bill_p[bct]); + bp->bo_id = obj->o_id; + bp->bquan = obj->quan; + if(dummy) { /* a dummy object must be inserted into */ + bp->useup = 1; /* the billobjs chain here. crucial for */ + add_to_billobjs(obj); /* eating floorfood in shop. see eat.c */ + } else bp->useup = 0; + bp->price = get_cost(obj, shkp); + ESHK(shkp)->billct++; + obj->unpaid = 1; +} + +STATIC_OVL void +add_to_billobjs(obj) + struct obj *obj; +{ + if (obj->where != OBJ_FREE) + panic("add_to_billobjs: obj not free"); + if (obj->timed) + obj_stop_timers(obj); + + obj->nobj = billobjs; + billobjs = obj; + obj->where = OBJ_ONBILL; +} + +/* recursive billing of objects within containers. */ +STATIC_OVL void +bill_box_content(obj, ininv, dummy, shkp) +register struct obj *obj; +register boolean ininv, dummy; +register struct monst *shkp; +{ + register struct obj *otmp; + + for (otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if (otmp->oclass == COIN_CLASS) continue; + + /* the "top" box is added in addtobill() */ + if (!otmp->no_charge) + add_one_tobill(otmp, dummy); + if (Has_contents(otmp)) + bill_box_content(otmp, ininv, dummy, shkp); + } + +} + +/* shopkeeper tells you what you bought or sold, sometimes partly IDing it */ +STATIC_OVL void +shk_names_obj(shkp, obj, fmt, amt, arg) +struct monst *shkp; +struct obj *obj; +const char *fmt; /* "%s %ld %s %s", doname(obj), amt, plur(amt), arg */ +long amt; +const char *arg; +{ + char *obj_name, fmtbuf[BUFSZ]; + boolean was_unknown = !obj->dknown; + + obj->dknown = TRUE; + /* Use real name for ordinary weapons/armor, and spell-less + * scrolls/books (that is, blank and mail), but only if the + * object is within the shk's area of interest/expertise. + */ + if (!objects[obj->otyp].oc_magic && saleable(shkp, obj) && + (obj->oclass == WEAPON_CLASS || obj->oclass == ARMOR_CLASS || + obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS || + obj->otyp == MIRROR)) { + was_unknown |= !objects[obj->otyp].oc_name_known; + makeknown(obj->otyp); + } + obj_name = doname(obj); + /* Use an alternate message when extra information is being provided */ + if (was_unknown) { + Sprintf(fmtbuf, "%%s; you %s", fmt); + obj_name[0] = highc(obj_name[0]); + pline(fmtbuf, obj_name, (obj->quan > 1) ? "them" : "it", + amt, plur(amt), arg); + } else { + You(fmt, obj_name, amt, plur(amt), arg); + } +} + +void +addtobill(obj, ininv, dummy, silent) +register struct obj *obj; +register boolean ininv, dummy, silent; +{ + register struct monst *shkp; + register char roomno = *u.ushops; + long ltmp = 0L, cltmp = 0L, gltmp = 0L; + register boolean container = Has_contents(obj); + + if(!*u.ushops) return; + + if(!(shkp = shop_keeper(roomno))) return; + + if(!inhishop(shkp)) return; + + if(/* perhaps we threw it away earlier */ + onbill(obj, shkp, FALSE) || + (obj->oclass == FOOD_CLASS && obj->oeaten) + ) return; + + if(ESHK(shkp)->billct == BILLSZ) { + You("got that for free!"); + return; + } + + if(obj->oclass == COIN_CLASS) { + costly_gold(obj->ox, obj->oy, obj->quan); + return; + } + + if(!obj->no_charge) + ltmp = get_cost(obj, shkp); + + if (obj->no_charge && !container) { + obj->no_charge = 0; + return; + } + + if(container) { + if(obj->cobj == (struct obj *)0) { + if(obj->no_charge) { + obj->no_charge = 0; + return; + } else { + add_one_tobill(obj, dummy); + goto speak; + } + } else { + cltmp += contained_cost(obj, shkp, cltmp, FALSE, FALSE); + gltmp += contained_gold(obj); + } + + if(ltmp) add_one_tobill(obj, dummy); + if(cltmp) bill_box_content(obj, ininv, dummy, shkp); + picked_container(obj); /* reset contained obj->no_charge */ + + ltmp += cltmp; + + if(gltmp) { + costly_gold(obj->ox, obj->oy, gltmp); + if(!ltmp) return; + } + + if(obj->no_charge) + obj->no_charge = 0; + + } else /* i.e., !container */ + add_one_tobill(obj, dummy); +speak: + if (shkp->mcanmove && !shkp->msleeping && !silent) { + char buf[BUFSZ]; + + if(!ltmp) { + pline("%s has no interest in %s.", Monnam(shkp), + the(xname(obj))); + return; + } + Strcpy(buf, "\"For you, "); + if (ANGRY(shkp)) Strcat(buf, "scum "); + else { + static const char *honored[5] = { + "good", "honored", "most gracious", "esteemed", + "most renowned and sacred" + }; + Strcat(buf, honored[rn2(4) + u.uevent.udemigod]); + if (!is_human(youmonst.data)) Strcat(buf, " creature"); + else + Strcat(buf, (flags.female) ? " lady" : " sir"); + } + if(ininv) { + long quan = obj->quan; + obj->quan = 1L; /* fool xname() into giving singular */ + pline("%s; only %ld %s %s.\"", buf, ltmp, + (quan > 1L) ? "per" : "for this", xname(obj)); + obj->quan = quan; + } else + pline("%s will cost you %ld %s%s.", + The(xname(obj)), ltmp, currency(ltmp), + (obj->quan > 1L) ? " each" : ""); + } else if(!silent) { + if(ltmp) pline_The("list price of %s is %ld %s%s.", + the(xname(obj)), ltmp, currency(ltmp), + (obj->quan > 1L) ? " each" : ""); + else pline("%s does not notice.", Monnam(shkp)); + } +} + +void +splitbill(obj, otmp) +register struct obj *obj, *otmp; +{ + /* otmp has been split off from obj */ + register struct bill_x *bp; + register long tmp; + register struct monst *shkp = shop_keeper(*u.ushops); + + if(!shkp || !inhishop(shkp)) { + impossible("splitbill: no resident shopkeeper??"); + return; + } + bp = onbill(obj, shkp, FALSE); + if(!bp) { + impossible("splitbill: not on bill?"); + return; + } + if(bp->bquan < otmp->quan) { + impossible("Negative quantity on bill??"); + } + if(bp->bquan == otmp->quan) { + impossible("Zero quantity on bill??"); + } + bp->bquan -= otmp->quan; + + if(ESHK(shkp)->billct == BILLSZ) otmp->unpaid = 0; + else { + tmp = bp->price; + bp = &(ESHK(shkp)->bill_p[ESHK(shkp)->billct]); + bp->bo_id = otmp->o_id; + bp->bquan = otmp->quan; + bp->useup = 0; + bp->price = tmp; + ESHK(shkp)->billct++; + } +} + +STATIC_OVL void +sub_one_frombill(obj, shkp) +register struct obj *obj; +register struct monst *shkp; +{ + register struct bill_x *bp; + + if((bp = onbill(obj, shkp, FALSE)) != 0) { + register struct obj *otmp; + + obj->unpaid = 0; + if(bp->bquan > obj->quan){ + otmp = newobj(0); + *otmp = *obj; + bp->bo_id = otmp->o_id = flags.ident++; + otmp->where = OBJ_FREE; + otmp->quan = (bp->bquan -= obj->quan); + otmp->owt = 0; /* superfluous */ + otmp->onamelth = 0; + otmp->oxlth = 0; + otmp->oattached = OATTACHED_NOTHING; + bp->useup = 1; + add_to_billobjs(otmp); + return; + } + ESHK(shkp)->billct--; +#ifdef DUMB + { + /* DRS/NS 2.2.6 messes up -- Peter Kendell */ + int indx = ESHK(shkp)->billct; + *bp = ESHK(shkp)->bill_p[indx]; + } +#else + *bp = ESHK(shkp)->bill_p[ESHK(shkp)->billct]; +#endif + return; + } else if (obj->unpaid) { + impossible("sub_one_frombill: unpaid object not on bill"); + obj->unpaid = 0; + } +} + +/* recursive check of unpaid objects within nested containers. */ +void +subfrombill(obj, shkp) +register struct obj *obj; +register struct monst *shkp; +{ + register struct obj *otmp; + + sub_one_frombill(obj, shkp); + + if (Has_contents(obj)) + for(otmp = obj->cobj; otmp; otmp = otmp->nobj) { + if(otmp->oclass == COIN_CLASS) continue; + + if (Has_contents(otmp)) + subfrombill(otmp, shkp); + else + sub_one_frombill(otmp, shkp); + } +} + +#endif /*OVLB*/ +#ifdef OVL3 + +STATIC_OVL long +stolen_container(obj, shkp, price, ininv) +register struct obj *obj; +register struct monst *shkp; +long price; +register boolean ininv; +{ + register struct obj *otmp; + + if(ininv && obj->unpaid) + price += get_cost(obj, shkp); + else { + if(!obj->no_charge) + price += get_cost(obj, shkp); + obj->no_charge = 0; + } + + /* the price of contained objects, if any */ + for(otmp = obj->cobj; otmp; otmp = otmp->nobj) { + + if(otmp->oclass == COIN_CLASS) continue; + + if (!Has_contents(otmp)) { + if(ininv) { + if(otmp->unpaid) + price += otmp->quan * get_cost(otmp, shkp); + } else { + if(!otmp->no_charge) { + if(otmp->oclass != FOOD_CLASS || !otmp->oeaten) + price += otmp->quan * get_cost(otmp, shkp); + } + otmp->no_charge = 0; + } + } else + price += stolen_container(otmp, shkp, price, ininv); + } + + return(price); +} +#endif /*OVL3*/ +#ifdef OVLB + +long +stolen_value(obj, x, y, peaceful, silent) +register struct obj *obj; +register xchar x, y; +register boolean peaceful, silent; +{ + register long value = 0L, gvalue = 0L; + register struct monst *shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + + if (!shkp || !inhishop(shkp)) + return (0L); + + if(obj->oclass == COIN_CLASS) { + gvalue += obj->quan; + } else if (Has_contents(obj)) { + register boolean ininv = !!count_unpaid(obj->cobj); + + value += stolen_container(obj, shkp, value, ininv); + if(!ininv) gvalue += contained_gold(obj); + } else if (!obj->no_charge && saleable(shkp, obj)) { + value += get_cost(obj, shkp); + } + + if(gvalue + value == 0L) return(0L); + + value += gvalue; + + if(peaceful) { + boolean credit_use = !!ESHK(shkp)->credit; + value = check_credit(value, shkp); + /* 'peaceful' affects general treatment, but doesn't affect + * the fact that other code expects that all charges after the + * shopkeeper is angry are included in robbed, not debit */ + if (ANGRY(shkp)) + ESHK(shkp)->robbed += value; + else + ESHK(shkp)->debit += value; + + if(!silent) { + const char *still = ""; + + if (credit_use) { + if (ESHK(shkp)->credit) { + You("have %ld %s credit remaining.", + ESHK(shkp)->credit, currency(ESHK(shkp)->credit)); + return value; + } else if (!value) { + You("have no credit remaining."); + return 0; + } + still = "still "; + } + if(obj->oclass == COIN_CLASS) + You("%sowe %s %ld %s!", still, + mon_nam(shkp), value, currency(value)); + else + You("%sowe %s %ld %s for %s!", still, + mon_nam(shkp), value, currency(value), + obj->quan > 1L ? "them" : "it"); + } + } else { + ESHK(shkp)->robbed += value; + + if(!silent) { + if(cansee(shkp->mx, shkp->my)) { + Norep("%s booms: \"%s, you are a thief!\"", + Monnam(shkp), plname); + } else Norep("You hear a scream, \"Thief!\""); + } + hot_pursuit(shkp); + (void) angry_guards(FALSE); + } + return(value); +} + +/* auto-response flag for/from "sell foo?" 'a' => 'y', 'q' => 'n' */ +static char sell_response = 'a'; +static int sell_how = SELL_NORMAL; +/* can't just use sell_response='y' for auto_credit because the 'a' response + shouldn't carry over from ordinary selling to credit selling */ +static boolean auto_credit = FALSE; + +void +sellobj_state(deliberate) +int deliberate; +{ + /* If we're deliberately dropping something, there's no automatic + response to the shopkeeper's "want to sell" query; however, if we + accidentally drop anything, the shk will buy it/them without asking. + This retains the old pre-query risk that slippery fingers while in + shops entailed: you drop it, you've lost it. + */ + sell_response = (deliberate != SELL_NORMAL) ? '\0' : 'a'; + sell_how = deliberate; + auto_credit = FALSE; +} + +void +sellobj(obj, x, y) +register struct obj *obj; +xchar x, y; +{ + register struct monst *shkp; + register struct eshk *eshkp; + long ltmp = 0L, cltmp = 0L, gltmp = 0L, offer; + boolean saleitem, cgold = FALSE, container = Has_contents(obj); + boolean isgold = (obj->oclass == COIN_CLASS); + boolean only_partially_your_contents = FALSE; + + if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || + !inhishop(shkp)) return; + if(!costly_spot(x, y)) return; + if(!*u.ushops) return; + + if(obj->unpaid && !container && !isgold) { + sub_one_frombill(obj, shkp); + return; + } + if(container) { + /* find the price of content before subfrombill */ + cltmp += contained_cost(obj, shkp, cltmp, TRUE, FALSE); + /* find the value of contained gold */ + gltmp += contained_gold(obj); + cgold = (gltmp > 0L); + } + + saleitem = saleable(shkp, obj); + if(!isgold && !obj->unpaid && saleitem) + ltmp = set_cost(obj, shkp); + + offer = ltmp + cltmp; + + /* get one case out of the way: nothing to sell, and no gold */ + if(!isgold && + ((offer + gltmp) == 0L || sell_how == SELL_DONTSELL)) { + register boolean unpaid = (obj->unpaid || + (container && count_unpaid(obj->cobj))); + + if(container) { + dropped_container(obj, shkp, FALSE); + if(!obj->unpaid && !saleitem) + obj->no_charge = 1; + if(obj->unpaid || count_unpaid(obj->cobj)) + subfrombill(obj, shkp); + } else obj->no_charge = 1; + + if(!unpaid && (sell_how != SELL_DONTSELL)) + pline("%s seems uninterested.", Monnam(shkp)); + return; + } + + /* you dropped something of your own - probably want to sell it */ + rouse_shk(shkp, TRUE); /* wake up sleeping or paralyzed shk */ + eshkp = ESHK(shkp); + + if (ANGRY(shkp)) { /* they become shop-objects, no pay */ + pline("Thank you, scum!"); + subfrombill(obj, shkp); + return; + } + + if(eshkp->robbed) { /* shkp is not angry? */ + if(isgold) offer = obj->quan; + else if(cgold) offer += cgold; + if((eshkp->robbed -= offer < 0L)) + eshkp->robbed = 0L; + if(offer) verbalize( + "Thank you for your contribution to restock this recently plundered shop."); + subfrombill(obj, shkp); + return; + } + + if(isgold || cgold) { + if(!cgold) gltmp = obj->quan; + + if(eshkp->debit >= gltmp) { + if(eshkp->loan) { /* you carry shop's gold */ + if(eshkp->loan >= gltmp) + eshkp->loan -= gltmp; + else eshkp->loan = 0L; + } + eshkp->debit -= gltmp; + Your("debt is %spaid off.", + eshkp->debit ? "partially " : ""); + } else { + long delta = gltmp - eshkp->debit; + + eshkp->credit += delta; + if(eshkp->debit) { + eshkp->debit = 0L; + eshkp->loan = 0L; + Your("debt is paid off."); + } + pline("%ld %s %s added to your credit.", + delta, currency(delta), delta > 1L ? "are" : "is"); + } + if(offer) goto move_on; + else { + if(!isgold) { + if (container) + dropped_container(obj, shkp, FALSE); + if (!obj->unpaid && !saleitem) obj->no_charge = 1; + subfrombill(obj, shkp); + } + return; + } + } +move_on: + if((!saleitem && !(container && cltmp > 0L)) + || eshkp->billct == BILLSZ + || obj->oclass == BALL_CLASS + || obj->oclass == CHAIN_CLASS || offer == 0L + || (obj->oclass == FOOD_CLASS && obj->oeaten) + || (Is_candle(obj) && + obj->age < 20L * (long)objects[obj->otyp].oc_cost)) { + pline("%s seems uninterested%s.", Monnam(shkp), + cgold ? " in the rest" : ""); + if (container) + dropped_container(obj, shkp, FALSE); + obj->no_charge = 1; + return; + } + +#ifndef GOLDOBJ + if(!shkp->mgold) { +#else + if(!money_cnt(shkp->minvent)) { +#endif + char c, qbuf[BUFSZ]; + long tmpcr = ((offer * 9L) / 10L) + (offer <= 1L); + + if (sell_how == SELL_NORMAL || auto_credit) { + c = sell_response = 'y'; + } else if (sell_response != 'n') { + pline("%s cannot pay you at present.", Monnam(shkp)); + Sprintf(qbuf, + "Will you accept %ld %s in credit for %s?", + tmpcr, currency(tmpcr), doname(obj)); + /* won't accept 'a' response here */ + /* KLY - 3/2000 yes, we will, it's a damn nuisance + to have to constantly hit 'y' to sell for credit */ + c = ynaq(qbuf); + if (c == 'a') { + c = 'y'; + auto_credit = TRUE; + } + } else /* previously specified "quit" */ + c = 'n'; + + if (c == 'y') { + shk_names_obj(shkp, obj, (sell_how != SELL_NORMAL) ? + "traded %s for %ld zorkmid%s in %scredit." : + "relinquish %s and acquire %ld zorkmid%s in %scredit.", + tmpcr, + (eshkp->credit > 0L) ? "additional " : ""); + eshkp->credit += tmpcr; + subfrombill(obj, shkp); + } else { + if (c == 'q') sell_response = 'n'; + if (container) + dropped_container(obj, shkp, FALSE); + if (!obj->unpaid) obj->no_charge = 1; + subfrombill(obj, shkp); + } + } else { + char qbuf[BUFSZ]; +#ifndef GOLDOBJ + boolean short_funds = (offer > shkp->mgold); + if (short_funds) offer = shkp->mgold; +#else + long shkmoney = money_cnt(shkp->minvent); + boolean short_funds = (offer > shkmoney); + if (short_funds) offer = shkmoney; +#endif + if (!sell_response) { + only_partially_your_contents = + (contained_cost(obj, shkp, 0L, FALSE, FALSE) != + contained_cost(obj, shkp, 0L, FALSE, TRUE)); + Sprintf(qbuf, + "%s offers%s %ld gold piece%s for%s %s %s. Sell %s?", + Monnam(shkp), short_funds ? " only" : "", + offer, plur(offer), + (!ltmp && cltmp && only_partially_your_contents) ? + " your items in" : (!ltmp && cltmp) ? " the contents of" : "", + obj->unpaid ? "the" : "your", cxname(obj), + (obj->quan == 1L && + !(!ltmp && cltmp && only_partially_your_contents)) ? + "it" : "them"); + } else qbuf[0] = '\0'; /* just to pacify lint */ + + switch (sell_response ? sell_response : ynaq(qbuf)) { + case 'q': sell_response = 'n'; + case 'n': if (container) + dropped_container(obj, shkp, FALSE); + if (!obj->unpaid) obj->no_charge = 1; + subfrombill(obj, shkp); + break; + case 'a': sell_response = 'y'; + case 'y': if (container) + dropped_container(obj, shkp, TRUE); + if (!obj->unpaid && !saleitem) obj->no_charge = 1; + subfrombill(obj, shkp); + pay(-offer, shkp); + shk_names_obj(shkp, obj, (sell_how != SELL_NORMAL) ? + (!ltmp && cltmp && only_partially_your_contents) ? + "sold some items inside %s for %ld gold pieces%s.%s" : + "sold %s for %ld gold piece%s.%s" : + "relinquish %s and receive %ld gold piece%s in compensation.%s", + offer, ""); + break; + default: impossible("invalid sell response"); + } + } +} + +int +doinvbill(mode) +int mode; /* 0: deliver count 1: paged */ +{ +#ifdef __SASC + void sasc_bug(struct obj *, unsigned); +#endif + struct monst *shkp; + struct eshk *eshkp; + struct bill_x *bp, *end_bp; + struct obj *obj; + long totused; + char *buf_p; + winid datawin; + + shkp = shop_keeper(*u.ushops); + if (!shkp || !inhishop(shkp)) { + if (mode != 0) impossible("doinvbill: no shopkeeper?"); + return 0; + } + eshkp = ESHK(shkp); + + if (mode == 0) { + /* count expended items, so that the `I' command can decide + whether to include 'x' in its prompt string */ + int cnt = !eshkp->debit ? 0 : 1; + + for (bp = eshkp->bill_p, end_bp = &eshkp->bill_p[eshkp->billct]; + bp < end_bp; bp++) + if (bp->useup || + ((obj = bp_to_obj(bp)) != 0 && obj->quan < bp->bquan)) + cnt++; + return cnt; + } + + datawin = create_nhwindow(NHW_MENU); + putstr(datawin, 0, "Unpaid articles already used up:"); + putstr(datawin, 0, ""); + + totused = 0L; + for (bp = eshkp->bill_p, end_bp = &eshkp->bill_p[eshkp->billct]; + bp < end_bp; bp++) { + obj = bp_to_obj(bp); + if(!obj) { + impossible("Bad shopkeeper administration."); + goto quit; + } + if(bp->useup || bp->bquan > obj->quan) { + long oquan, uquan, thisused; + unsigned save_unpaid; + + save_unpaid = obj->unpaid; + oquan = obj->quan; + uquan = (bp->useup ? bp->bquan : bp->bquan - oquan); + thisused = bp->price * uquan; + totused += thisused; + obj->unpaid = 0; /* ditto */ + /* Why 'x'? To match `I x', more or less. */ + buf_p = xprname(obj, (char *)0, 'x', FALSE, thisused, uquan); +#ifdef __SASC + /* SAS/C 6.2 can't cope for some reason */ + sasc_bug(obj,save_unpaid); +#else + obj->unpaid = save_unpaid; +#endif + putstr(datawin, 0, buf_p); + } + } + if (eshkp->debit) { + /* additional shop debt which has no itemization available */ + if (totused) putstr(datawin, 0, ""); + totused += eshkp->debit; + buf_p = xprname((struct obj *)0, + "usage charges and/or other fees", + GOLD_SYM, FALSE, eshkp->debit, 0L); + putstr(datawin, 0, buf_p); + } + buf_p = xprname((struct obj *)0, "Total:", '*', FALSE, totused, 0L); + putstr(datawin, 0, ""); + putstr(datawin, 0, buf_p); + display_nhwindow(datawin, FALSE); + quit: + destroy_nhwindow(datawin); + return(0); +} + +#define HUNGRY 2 + +STATIC_OVL long +getprice(obj, shk_buying) +register struct obj *obj; +boolean shk_buying; +{ + register long tmp = (long) objects[obj->otyp].oc_cost; + + if (obj->oartifact) { + tmp = arti_cost(obj); + if (shk_buying) tmp /= 4; + } + switch(obj->oclass) { + case FOOD_CLASS: + /* simpler hunger check, (2-4)*cost */ + if (u.uhs >= HUNGRY && !shk_buying) tmp *= (long) u.uhs; + if (obj->oeaten) tmp = 0L; + break; + case WAND_CLASS: + if (obj->spe == -1) tmp = 0L; + break; + case POTION_CLASS: + if (obj->otyp == POT_WATER && !obj->blessed && !obj->cursed) + tmp = 0L; + break; + case ARMOR_CLASS: + case WEAPON_CLASS: + if (obj->spe > 0) tmp += 10L * (long) obj->spe; + break; + case TOOL_CLASS: + if (Is_candle(obj) && + obj->age < 20L * (long)objects[obj->otyp].oc_cost) + tmp /= 2L; + break; + } + return tmp; +} + +/* shk catches thrown pick-axe */ +struct monst * +shkcatch(obj, x, y) +register struct obj *obj; +register xchar x, y; +{ + register struct monst *shkp; + + if (!(shkp = shop_keeper(inside_shop(x, y))) || + !inhishop(shkp)) return(0); + + if (shkp->mcanmove && !shkp->msleeping && + (*u.ushops != ESHK(shkp)->shoproom || !inside_shop(u.ux, u.uy)) && + dist2(shkp->mx, shkp->my, x, y) < 3 && + /* if it is the shk's pos, you hit and anger him */ + (shkp->mx != x || shkp->my != y)) { + if (mnearto(shkp, x, y, TRUE)) + verbalize("Out of my way, scum!"); + if (cansee(x, y)) { + pline("%s nimbly%s catches %s.", + Monnam(shkp), + (x == shkp->mx && y == shkp->my) ? "" : " reaches over and", + the(xname(obj))); + if (!canspotmon(shkp)) + map_invisible(x, y); + delay_output(); + mark_synch(); + } + subfrombill(obj, shkp); + (void) mpickobj(shkp, obj); + return shkp; + } + return (struct monst *)0; +} + +void +add_damage(x, y, cost) +register xchar x, y; +long cost; +{ + struct damage *tmp_dam; + char *shops; + + if (IS_DOOR(levl[x][y].typ)) { + struct monst *mtmp; + + /* Don't schedule for repair unless it's a real shop entrance */ + for (shops = in_rooms(x, y, SHOPBASE); *shops; shops++) + if ((mtmp = shop_keeper(*shops)) != 0 && + x == ESHK(mtmp)->shd.x && y == ESHK(mtmp)->shd.y) + break; + if (!*shops) return; + } + for (tmp_dam = level.damagelist; tmp_dam; tmp_dam = tmp_dam->next) + if (tmp_dam->place.x == x && tmp_dam->place.y == y) { + tmp_dam->cost += cost; + return; + } + tmp_dam = (struct damage *)alloc((unsigned)sizeof(struct damage)); + tmp_dam->when = monstermoves; + tmp_dam->place.x = x; + tmp_dam->place.y = y; + tmp_dam->cost = cost; + tmp_dam->typ = levl[x][y].typ; + tmp_dam->next = level.damagelist; + level.damagelist = tmp_dam; + /* If player saw damage, display as a wall forever */ + if (cansee(x, y)) + levl[x][y].seenv = SVALL; +} + +#endif /*OVLB*/ +#ifdef OVL0 + +/* + * Do something about damage. Either (!croaked) try to repair it, or + * (croaked) just discard damage structs for non-shared locations, since + * they'll never get repaired. Assume that shared locations will get + * repaired eventually by the other shopkeeper(s). This might be an erroneous + * assumption (they might all be dead too), but we have no reasonable way of + * telling that. + */ +STATIC_OVL +void +remove_damage(shkp, croaked) +register struct monst *shkp; +register boolean croaked; +{ + register struct damage *tmp_dam, *tmp2_dam; + register boolean did_repair = FALSE, saw_door = FALSE; + register boolean saw_floor = FALSE, stop_picking = FALSE; + register boolean saw_untrap = FALSE; + uchar saw_walls = 0; + + tmp_dam = level.damagelist; + tmp2_dam = 0; + while (tmp_dam) { + register xchar x = tmp_dam->place.x, y = tmp_dam->place.y; + char shops[5]; + int disposition; + + disposition = 0; + Strcpy(shops, in_rooms(x, y, SHOPBASE)); + if (index(shops, ESHK(shkp)->shoproom)) { + if (croaked) + disposition = (shops[1])? 0 : 1; + else if (stop_picking) + disposition = repair_damage(shkp, tmp_dam, FALSE); + else { + /* Defer the stop_occupation() until after repair msgs */ + if (closed_door(x, y)) + stop_picking = picking_at(x, y); + disposition = repair_damage(shkp, tmp_dam, FALSE); + if (!disposition) + stop_picking = FALSE; + } + } + + if (!disposition) { + tmp2_dam = tmp_dam; + tmp_dam = tmp_dam->next; + continue; + } + + if (disposition > 1) { + did_repair = TRUE; + if (cansee(x, y)) { + if (IS_WALL(levl[x][y].typ)) + saw_walls++; + else if (IS_DOOR(levl[x][y].typ)) + saw_door = TRUE; + else if (disposition == 3) /* untrapped */ + saw_untrap = TRUE; + else + saw_floor = TRUE; + } + } + + tmp_dam = tmp_dam->next; + if (!tmp2_dam) { + free((genericptr_t)level.damagelist); + level.damagelist = tmp_dam; + } else { + free((genericptr_t)tmp2_dam->next); + tmp2_dam->next = tmp_dam; + } + } + if (!did_repair) + return; + if (saw_walls) { + pline("Suddenly, %s section%s of wall close%s up!", + (saw_walls == 1) ? "a" : (saw_walls <= 3) ? + "some" : "several", + (saw_walls == 1) ? "" : "s", (saw_walls == 1) ? "s" : ""); + if (saw_door) + pline_The("shop door reappears!"); + if (saw_floor) + pline_The("floor is repaired!"); + } else { + if (saw_door) + pline("Suddenly, the shop door reappears!"); + else if (saw_floor) + pline("Suddenly, the floor damage is gone!"); + else if (saw_untrap) + pline("Suddenly, the trap is removed from the floor!"); + else if (inside_shop(u.ux, u.uy) == ESHK(shkp)->shoproom) + You_feel("more claustrophobic than before."); + else if (flags.soundok && !rn2(10)) + Norep("The dungeon acoustics noticeably change."); + } + if (stop_picking) + stop_occupation(); +} + +/* + * 0: repair postponed, 1: silent repair (no messages), 2: normal repair + * 3: untrap + */ +int +repair_damage(shkp, tmp_dam, catchup) +register struct monst *shkp; +register struct damage *tmp_dam; +boolean catchup; /* restoring a level */ +{ + register xchar x, y, i; + xchar litter[9]; + register struct monst *mtmp; + register struct obj *otmp; + register struct trap *ttmp; + + if ((monstermoves - tmp_dam->when) < REPAIR_DELAY) + return(0); + if (shkp->msleeping || !shkp->mcanmove || ESHK(shkp)->following) + return(0); + x = tmp_dam->place.x; + y = tmp_dam->place.y; + if (!IS_ROOM(tmp_dam->typ)) { + if (x == u.ux && y == u.uy) + if (!Passes_walls) + return(0); + if (x == shkp->mx && y == shkp->my) + return(0); + if ((mtmp = m_at(x, y)) && (!passes_walls(mtmp->data))) + return(0); + } + if ((ttmp = t_at(x, y)) != 0) { + if (x == u.ux && y == u.uy) + if (!Passes_walls) + return(0); + if (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP) { + /* convert to an object */ + otmp = mksobj((ttmp->ttyp == LANDMINE) ? LAND_MINE : + BEARTRAP, TRUE, FALSE); + otmp->quan= 1; + otmp->owt = weight(otmp); + (void) mpickobj(shkp, otmp); + } + deltrap(ttmp); + if(IS_DOOR(tmp_dam->typ)) { + levl[x][y].doormask = D_CLOSED; /* arbitrary */ + block_point(x, y); + } else if (IS_WALL(tmp_dam->typ)) { + levl[x][y].typ = tmp_dam->typ; + block_point(x, y); + } + newsym(x, y); + return(3); + } + if (IS_ROOM(tmp_dam->typ)) { + /* No messages, because player already filled trap door */ + return(1); + } + if ((tmp_dam->typ == levl[x][y].typ) && + (!IS_DOOR(tmp_dam->typ) || (levl[x][y].doormask > D_BROKEN))) + /* No messages if player already replaced shop door */ + return(1); + levl[x][y].typ = tmp_dam->typ; + (void) memset((genericptr_t)litter, 0, sizeof(litter)); + if ((otmp = level.objects[x][y]) != 0) { + /* Scatter objects haphazardly into the shop */ +#define NEED_UPDATE 1 +#define OPEN 2 +#define INSHOP 4 +#define horiz(i) ((i%3)-1) +#define vert(i) ((i/3)-1) + for (i = 0; i < 9; i++) { + if ((i == 4) || (!ZAP_POS(levl[x+horiz(i)][y+vert(i)].typ))) + continue; + litter[i] = OPEN; + if (inside_shop(x+horiz(i), + y+vert(i)) == ESHK(shkp)->shoproom) + litter[i] |= INSHOP; + } + if (Punished && !u.uswallow && + ((uchain->ox == x && uchain->oy == y) || + (uball->ox == x && uball->oy == y))) { + /* + * Either the ball or chain is in the repair location. + * + * Take the easy way out and put ball&chain under hero. + */ + verbalize("Get your junk out of my wall!"); + unplacebc(); /* pick 'em up */ + placebc(); /* put 'em down */ + } + while ((otmp = level.objects[x][y]) != 0) + /* Don't mess w/ boulders -- just merge into wall */ + if ((otmp->otyp == BOULDER) || (otmp->otyp == ROCK)) { + obj_extract_self(otmp); + obfree(otmp, (struct obj *)0); + } else { + while (!(litter[i = rn2(9)] & INSHOP)); + remove_object(otmp); + place_object(otmp, x+horiz(i), y+vert(i)); + litter[i] |= NEED_UPDATE; + } + } + if (catchup) return 1; /* repair occurred while off level */ + + block_point(x, y); + if(IS_DOOR(tmp_dam->typ)) { + levl[x][y].doormask = D_CLOSED; /* arbitrary */ + newsym(x, y); + } else { + /* don't set doormask - it is (hopefully) the same as it was */ + /* if not, perhaps save it with the damage array... */ + + if (IS_WALL(tmp_dam->typ) && cansee(x, y)) { + /* Player sees actual repair process, so they KNOW it's a wall */ + levl[x][y].seenv = SVALL; + newsym(x, y); + } + /* Mark this wall as "repaired". There currently is no code */ + /* to do anything about repaired walls, so don't do it. */ + } + for (i = 0; i < 9; i++) + if (litter[i] & NEED_UPDATE) + newsym(x+horiz(i), y+vert(i)); + return(2); +#undef NEED_UPDATE +#undef OPEN +#undef INSHOP +#undef vert +#undef horiz +} +#endif /*OVL0*/ +#ifdef OVL3 +/* + * shk_move: return 1: moved 0: didn't -1: let m_move do it -2: died + */ +int +shk_move(shkp) +register struct monst *shkp; +{ + register xchar gx,gy,omx,omy; + register int udist; + register schar appr; + register struct eshk *eshkp = ESHK(shkp); + int z; + boolean uondoor = FALSE, satdoor, avoid = FALSE, badinv; + + omx = shkp->mx; + omy = shkp->my; + + if (inhishop(shkp)) + remove_damage(shkp, FALSE); + + if((udist = distu(omx,omy)) < 3 && + (shkp->data != &mons[PM_GRID_BUG] || (omx==u.ux || omy==u.uy))) { + if(ANGRY(shkp) || + (Conflict && !resist(shkp, RING_CLASS, 0, 0))) { + if(Displaced) + Your("displaced image doesn't fool %s!", + mon_nam(shkp)); + (void) mattacku(shkp); + return(0); + } + if(eshkp->following) { + if(strncmp(eshkp->customer, plname, PL_NSIZ)) { + verbalize("%s, %s! I was looking for %s.", + Hello(shkp), plname, eshkp->customer); + eshkp->following = 0; + return(0); + } + if(moves > followmsg+4) { + verbalize("%s, %s! Didn't you forget to pay?", + Hello(shkp), plname); + followmsg = moves; + if (!rn2(9)) { + pline("%s doesn't like customers who don't pay.", + Monnam(shkp)); + rile_shk(shkp); + } + } + if(udist < 2) + return(0); + } + } + + appr = 1; + gx = eshkp->shk.x; + gy = eshkp->shk.y; + satdoor = (gx == omx && gy == omy); + if(eshkp->following || ((z = holetime()) >= 0 && z*z <= udist)){ + /* [This distance check used to apply regardless of + whether the shk was following, but that resulted in + m_move() sometimes taking the shk out of the shop if + the player had fenced him in with boulders or traps. + Such voluntary abandonment left unpaid objects in + invent, triggering billing impossibilities on the + next level once the character fell through the hole.] */ + if (udist > 4 && eshkp->following) + return(-1); /* leave it to m_move */ + gx = u.ux; + gy = u.uy; + } else if(ANGRY(shkp)) { + /* Move towards the hero if the shopkeeper can see him. */ + if(shkp->mcansee && m_canseeu(shkp)) { + gx = u.ux; + gy = u.uy; + } + avoid = FALSE; + } else { +#define GDIST(x,y) (dist2(x,y,gx,gy)) + if (Invis +#ifdef STEED + || u.usteed +#endif + ) { + avoid = FALSE; + } else { + uondoor = (u.ux == eshkp->shd.x && u.uy == eshkp->shd.y); + if(uondoor) { + badinv = (carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK) || + (Fast && (sobj_at(PICK_AXE, u.ux, u.uy) || + sobj_at(DWARVISH_MATTOCK, u.ux, u.uy)))); + if(satdoor && badinv) + return(0); + avoid = !badinv; + } else { + avoid = (*u.ushops && distu(gx,gy) > 8); + badinv = FALSE; + } + + if(((!eshkp->robbed && !eshkp->billct && !eshkp->debit) + || avoid) && GDIST(omx,omy) < 3) { + if (!badinv && !onlineu(omx,omy)) + return(0); + if(satdoor) + appr = gx = gy = 0; + } + } + } + + z = move_special(shkp,inhishop(shkp),appr,uondoor,avoid,omx,omy,gx,gy); + if (z > 0) after_shk_move(shkp); + + return z; +} + +/* called after shopkeeper moves, in case the move causes re-entry into shop */ +void +after_shk_move(shkp) +struct monst *shkp; +{ + struct eshk *eshkp = ESHK(shkp); + + if (eshkp->bill_p == (struct bill_x *) -1000 && inhishop(shkp)) { + /* reset bill_p, need to re-calc player's occupancy too */ + eshkp->bill_p = &eshkp->bill[0]; + check_special_room(FALSE); + } +} + +#endif /*OVL3*/ +#ifdef OVLB + +/* for use in levl_follower (mondata.c) */ +boolean +is_fshk(mtmp) +register struct monst *mtmp; +{ + return((boolean)(mtmp->isshk && ESHK(mtmp)->following)); +} + +/* You are digging in the shop. */ +void +shopdig(fall) +register int fall; +{ + register struct monst *shkp = shop_keeper(*u.ushops); + int lang; + const char *grabs = "grabs"; + + if(!shkp) return; + + /* 0 == can't speak, 1 == makes animal noises, 2 == speaks */ + lang = 0; + if (shkp->msleeping || !shkp->mcanmove || is_silent(shkp->data)) + ; /* lang stays 0 */ + else if (shkp->data->msound <= MS_ANIMAL) + lang = 1; + else if (shkp->data->msound >= MS_HUMANOID) + lang = 2; + + if(!inhishop(shkp)) { + if (Role_if(PM_KNIGHT)) { + You_feel("like a common thief."); + adjalign(-sgn(u.ualign.type)); + } + return; + } + + if(!fall) { + if (lang == 2) { + if(u.utraptype == TT_PIT) + verbalize( + "Be careful, %s, or you might fall through the floor.", + flags.female ? "madam" : "sir"); + else + verbalize("%s, do not damage the floor here!", + flags.female ? "Madam" : "Sir"); + } + if (Role_if(PM_KNIGHT)) { + You_feel("like a common thief."); + adjalign(-sgn(u.ualign.type)); + } + } else if(!um_dist(shkp->mx, shkp->my, 5) && + !shkp->msleeping && shkp->mcanmove && + (ESHK(shkp)->billct || ESHK(shkp)->debit)) { + register struct obj *obj, *obj2; + if (nolimbs(shkp->data)) { + grabs = "knocks off"; +#if 0 + /* This is what should happen, but for balance + * reasons, it isn't currently. + */ + if (lang == 2) + pline("%s curses %s inability to grab your backpack!", + shkname(shkp), mhim(shkp)); + rile_shk(shkp); + return; +#endif + } + if (distu(shkp->mx, shkp->my) > 2) { + mnexto(shkp); + /* for some reason the shopkeeper can't come next to you */ + if (distu(shkp->mx, shkp->my) > 2) { + if (lang == 2) + pline("%s curses you in anger and frustration!", + shkname(shkp)); + rile_shk(shkp); + return; + } else + pline("%s %s, and %s your backpack!", + shkname(shkp), + makeplural(locomotion(shkp->data,"leap")), grabs); + } else + pline("%s %s your backpack!", shkname(shkp), grabs); + + for(obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if ((obj->owornmask & ~(W_SWAPWEP|W_QUIVER)) != 0 || + (obj == uswapwep && u.twoweap) || + (obj->otyp == LEASH && obj->leashmon)) continue; + if (obj == current_wand) continue; + setnotworn(obj); + freeinv(obj); + subfrombill(obj, shkp); + (void) add_to_minv(shkp, obj); /* may free obj */ + } + } +} + +#ifdef KOPS +STATIC_OVL void +makekops(mm) +coord *mm; +{ + static const short k_mndx[4] = { + PM_KEYSTONE_KOP, PM_KOP_SERGEANT, PM_KOP_LIEUTENANT, PM_KOP_KAPTAIN + }; + int k_cnt[4], cnt, mndx, k; + + k_cnt[0] = cnt = abs(depth(&u.uz)) + rnd(5); + k_cnt[1] = (cnt / 3) + 1; /* at least one sarge */ + k_cnt[2] = (cnt / 6); /* maybe a lieutenant */ + k_cnt[3] = (cnt / 9); /* and maybe a kaptain */ + + for (k = 0; k < 4; k++) { + if ((cnt = k_cnt[k]) == 0) break; + mndx = k_mndx[k]; + if (mvitals[mndx].mvflags & G_GONE) continue; + + while (cnt--) + if (enexto(mm, mm->x, mm->y, &mons[mndx])) + (void) makemon(&mons[mndx], mm->x, mm->y, NO_MM_FLAGS); + } +} +#endif /* KOPS */ + +void +pay_for_damage(dmgstr, cant_mollify) +const char *dmgstr; +boolean cant_mollify; +{ + register struct monst *shkp = (struct monst *)0; + char shops_affected[5]; + register boolean uinshp = (*u.ushops != '\0'); + char qbuf[80]; + register xchar x, y; + boolean dugwall = !strcmp(dmgstr, "dig into") || /* wand */ + !strcmp(dmgstr, "damage"); /* pick-axe */ + struct damage *tmp_dam, *appear_here = 0; + /* any number >= (80*80)+(24*24) would do, actually */ + long cost_of_damage = 0L; + unsigned int nearest_shk = 7000, nearest_damage = 7000; + int picks = 0; + + for (tmp_dam = level.damagelist; + (tmp_dam && (tmp_dam->when == monstermoves)); + tmp_dam = tmp_dam->next) { + char *shp; + + if (!tmp_dam->cost) + continue; + cost_of_damage += tmp_dam->cost; + Strcpy(shops_affected, + in_rooms(tmp_dam->place.x, tmp_dam->place.y, SHOPBASE)); + for (shp = shops_affected; *shp; shp++) { + struct monst *tmp_shk; + unsigned int shk_distance; + + if (!(tmp_shk = shop_keeper(*shp))) + continue; + if (tmp_shk == shkp) { + unsigned int damage_distance = + distu(tmp_dam->place.x, tmp_dam->place.y); + + if (damage_distance < nearest_damage) { + nearest_damage = damage_distance; + appear_here = tmp_dam; + } + continue; + } + if (!inhishop(tmp_shk)) + continue; + shk_distance = distu(tmp_shk->mx, tmp_shk->my); + if (shk_distance > nearest_shk) + continue; + if ((shk_distance == nearest_shk) && picks) { + if (rn2(++picks)) + continue; + } else + picks = 1; + shkp = tmp_shk; + nearest_shk = shk_distance; + appear_here = tmp_dam; + nearest_damage = distu(tmp_dam->place.x, tmp_dam->place.y); + } + } + + if (!cost_of_damage || !shkp) + return; + + x = appear_here->place.x; + y = appear_here->place.y; + + /* not the best introduction to the shk... */ + (void) strncpy(ESHK(shkp)->customer,plname,PL_NSIZ); + + /* if the shk is already on the war path, be sure it's all out */ + if(ANGRY(shkp) || ESHK(shkp)->following) { + hot_pursuit(shkp); + return; + } + + /* if the shk is not in their shop.. */ + if(!*in_rooms(shkp->mx,shkp->my,SHOPBASE)) { + if(!cansee(shkp->mx, shkp->my)) + return; + goto getcad; + } + + if(uinshp) { + if(um_dist(shkp->mx, shkp->my, 1) && + !um_dist(shkp->mx, shkp->my, 3)) { + pline("%s leaps towards you!", shkname(shkp)); + mnexto(shkp); + } + if(um_dist(shkp->mx, shkp->my, 1)) goto getcad; + } else { + /* + * Make shkp show up at the door. Effect: If there is a monster + * in the doorway, have the hero hear the shopkeeper yell a bit, + * pause, then have the shopkeeper appear at the door, having + * yanked the hapless critter out of the way. + */ + if (MON_AT(x, y)) { + if(flags.soundok) { + You_hear("an angry voice:"); + verbalize("Out of my way, scum!"); + wait_synch(); +#if defined(UNIX) || defined(VMS) +# if defined(SYSV) || defined(ULTRIX) || defined(VMS) + (void) +# endif + sleep(1); +#endif + } + } + (void) mnearto(shkp, x, y, TRUE); + } + + if((um_dist(x, y, 1) && !uinshp) || cant_mollify || +#ifndef GOLDOBJ + (u.ugold + ESHK(shkp)->credit) < cost_of_damage +#else + (money_cnt(invent) + ESHK(shkp)->credit) < cost_of_damage +#endif + || !rn2(50)) { + if(um_dist(x, y, 1) && !uinshp) { + pline("%s shouts:", shkname(shkp)); + verbalize("Who dared %s my %s?", dmgstr, + dugwall ? "shop" : "door"); + } else { +getcad: + verbalize("How dare you %s my %s?", dmgstr, + dugwall ? "shop" : "door"); + } + hot_pursuit(shkp); + return; + } + + if (Invis) Your("invisibility does not fool %s!", shkname(shkp)); + Sprintf(qbuf,"\"Cad! You did %ld %s worth of damage!\" Pay? ", + cost_of_damage, currency(cost_of_damage)); + if(yn(qbuf) != 'n') { + cost_of_damage = check_credit(cost_of_damage, shkp); +#ifndef GOLDOBJ + u.ugold -= cost_of_damage; + shkp->mgold += cost_of_damage; +#else + money2mon(shkp, cost_of_damage); +#endif + flags.botl = 1; + pline("Mollified, %s accepts your restitution.", + shkname(shkp)); + /* move shk back to his home loc */ + home_shk(shkp, FALSE); + pacify_shk(shkp); + } else { + verbalize("Oh, yes! You'll pay!"); + hot_pursuit(shkp); + adjalign(-sgn(u.ualign.type)); + } +} +#endif /*OVLB*/ +#ifdef OVL0 +/* called in dokick.c when we kick an object that might be in a store */ +boolean +costly_spot(x, y) +register xchar x, y; +{ + register struct monst *shkp; + + if (!level.flags.has_shop) return FALSE; + shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + if(!shkp || !inhishop(shkp)) return(FALSE); + + return((boolean)(inside_shop(x, y) && + !(x == ESHK(shkp)->shk.x && + y == ESHK(shkp)->shk.y))); +} +#endif /*OVL0*/ +#ifdef OVLB + +/* called by dotalk(sounds.c) when #chatting; returns obj if location + contains shop goods and shopkeeper is willing & able to speak */ +struct obj * +shop_object(x, y) +register xchar x, y; +{ + register struct obj *otmp; + register struct monst *shkp; + + if(!(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) || !inhishop(shkp)) + return(struct obj *)0; + + for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if (otmp->oclass != COIN_CLASS) + break; + /* note: otmp might have ->no_charge set, but that's ok */ + return (otmp && costly_spot(x, y) && NOTANGRY(shkp) + && shkp->mcanmove && !shkp->msleeping) + ? otmp : (struct obj *)0; +} + +/* give price quotes for all objects linked to this one (ie, on this spot) */ +void +price_quote(first_obj) +register struct obj *first_obj; +{ + register struct obj *otmp; + char buf[BUFSZ], price[40]; + long cost; + int cnt = 0; + winid tmpwin; + struct monst *shkp = shop_keeper(inside_shop(u.ux, u.uy)); + + tmpwin = create_nhwindow(NHW_MENU); + putstr(tmpwin, 0, "Fine goods for sale:"); + putstr(tmpwin, 0, ""); + for (otmp = first_obj; otmp; otmp = otmp->nexthere) { + if (otmp->oclass == COIN_CLASS) continue; + cost = (otmp->no_charge || otmp == uball || otmp == uchain) ? 0L : + get_cost(otmp, (struct monst *)0); + if (Has_contents(otmp)) + cost += contained_cost(otmp, shkp, 0L, FALSE, FALSE); + if (!cost) { + Strcpy(price, "no charge"); + } else { + Sprintf(price, "%ld %s%s", cost, currency(cost), + otmp->quan > 1L ? " each" : ""); + } + Sprintf(buf, "%s, %s", doname(otmp), price); + putstr(tmpwin, 0, buf), cnt++; + } + if (cnt > 1) { + display_nhwindow(tmpwin, TRUE); + } else if (cnt == 1) { + if (first_obj->no_charge || first_obj == uball || first_obj == uchain){ + pline("%s!", buf); /* buf still contains the string */ + } else { + /* print cost in slightly different format, so can't reuse buf */ + cost = get_cost(first_obj, (struct monst *)0); + if (Has_contents(first_obj)) + cost += contained_cost(first_obj, shkp, 0L, FALSE, FALSE); + pline("%s, price %ld %s%s%s", doname(first_obj), + cost, currency(cost), first_obj->quan > 1L ? " each" : "", + shk_embellish(first_obj, cost)); + } + } + destroy_nhwindow(tmpwin); +} +#endif /*OVLB*/ +#ifdef OVL3 + +STATIC_OVL const char * +shk_embellish(itm, cost) +register struct obj *itm; +long cost; +{ + if (!rn2(3)) { + register int o, choice = rn2(5); + if (choice == 0) choice = (cost < 100L ? 1 : cost < 500L ? 2 : 3); + switch (choice) { + case 4: + if (cost < 10L) break; else o = itm->oclass; + if (o == FOOD_CLASS) return ", gourmets' delight!"; + if (objects[itm->otyp].oc_name_known + ? objects[itm->otyp].oc_magic + : (o == AMULET_CLASS || o == RING_CLASS || + o == WAND_CLASS || o == POTION_CLASS || + o == SCROLL_CLASS || o == SPBOOK_CLASS)) + return ", painstakingly developed!"; + return ", superb craftsmanship!"; + case 3: return ", finest quality."; + case 2: return ", an excellent choice."; + case 1: return ", a real bargain."; + default: break; + } + } else if (itm->oartifact) { + return ", one of a kind!"; + } + return "."; +} +#endif /*OVL3*/ +#ifdef OVLB + +/* First 4 supplied by Ronen and Tamar, remainder by development team */ +const char *Izchak_speaks[]={ + "%s says: 'These shopping malls give me a headache.'", + "%s says: 'Slow down. Think clearly.'", + "%s says: 'You need to take things one at a time.'", + "%s says: 'I don't like poofy coffee... give me Columbian Supremo.'", + "%s says that getting the devteam's agreement on anything is difficult.", + "%s says that he has noticed those who serve their deity will prosper.", + "%s says: 'Don't try to steal from me - I have friends in high places!'", + "%s says: 'You may well need something from this shop in the future.'", + "%s comments about the Valley of the Dead as being a gateway." +}; + +void +shk_chat(shkp) +struct monst *shkp; +{ + struct eshk *eshk; +#ifdef GOLDOBJ + long shkmoney; +#endif + if (!shkp->isshk) { + /* The monster type is shopkeeper, but this monster is + not actually a shk, which could happen if someone + wishes for a shopkeeper statue and then animates it. + (Note: shkname() would be "" in a case like this.) */ + pline("%s asks whether you've seen any untended shops recently.", + Monnam(shkp)); + /* [Perhaps we ought to check whether this conversation + is taking place inside an untended shop, but a shopless + shk can probably be expected to be rather disoriented.] */ + return; + } + + eshk = ESHK(shkp); + if (ANGRY(shkp)) + pline("%s mentions how much %s dislikes %s customers.", + shkname(shkp), mhe(shkp), + eshk->robbed ? "non-paying" : "rude"); + else if (eshk->following) { + if (strncmp(eshk->customer, plname, PL_NSIZ)) { + verbalize("%s %s! I was looking for %s.", + Hello(shkp), plname, eshk->customer); + eshk->following = 0; + } else { + verbalize("%s %s! Didn't you forget to pay?", + Hello(shkp), plname); + } + } else if (eshk->billct) { + register long total = addupbill(shkp) + eshk->debit; + pline("%s says that your bill comes to %ld %s.", + shkname(shkp), total, currency(total)); + } else if (eshk->debit) + pline("%s reminds you that you owe %s %ld %s.", + shkname(shkp), mhim(shkp), + eshk->debit, currency(eshk->debit)); + else if (eshk->credit) + pline("%s encourages you to use your %ld %s of credit.", + shkname(shkp), eshk->credit, currency(eshk->credit)); + else if (eshk->robbed) + pline("%s complains about a recent robbery.", shkname(shkp)); +#ifndef GOLDOBJ + else if (shkp->mgold < 50) +#else + else if ((shkmoney = money_cnt(shkp->minvent)) < 50) +#endif + pline("%s complains that business is bad.", shkname(shkp)); +#ifndef GOLDOBJ + else if (shkp->mgold > 4000) +#else + else if (shkmoney > 4000) +#endif + pline("%s says that business is good.", shkname(shkp)); + else if (strcmp(shkname(shkp), "Izchak") == 0) + pline(Izchak_speaks[rn2(SIZE(Izchak_speaks))],shkname(shkp)); + else + pline("%s talks about the problem of shoplifters.",shkname(shkp)); +} + +#ifdef KOPS +STATIC_OVL void +kops_gone(silent) +register boolean silent; +{ + register int cnt = 0; + register struct monst *mtmp, *mtmp2; + + for (mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; + if (mtmp->data->mlet == S_KOP) { + if (canspotmon(mtmp)) cnt++; + mongone(mtmp); + } + } + if (cnt && !silent) + pline_The("Kop%s (disappointed) vanish%s into thin air.", + plur(cnt), cnt == 1 ? "es" : ""); +} +#endif /* KOPS */ + +#endif /*OVLB*/ +#ifdef OVL3 + +STATIC_OVL long +cost_per_charge(shkp, otmp, altusage) +struct monst *shkp; +struct obj *otmp; +boolean altusage; /* some items have an "alternate" use with different cost */ +{ + long tmp = 0L; + + if(!shkp || !inhishop(shkp)) return(0L); /* insurance */ + tmp = get_cost(otmp, shkp); + + /* The idea is to make the exhaustive use of */ + /* an unpaid item more expensive than buying */ + /* it outright. */ + if(otmp->otyp == MAGIC_LAMP) { /* 1 */ + /* normal use (ie, as light source) of a magic lamp never + degrades its value, but not charging anything would make + identifcation too easy; charge an amount comparable to + what is charged for an ordinary lamp (don't bother with + angry shk surchage) */ + if (!altusage) tmp = (long) objects[OIL_LAMP].oc_cost; + else tmp += tmp / 3L; /* djinni is being released */ + } else if(otmp->otyp == MAGIC_MARKER) { /* 70 - 100 */ + /* no way to determine in advance */ + /* how many charges will be wasted. */ + /* so, arbitrarily, one half of the */ + /* price per use. */ + tmp /= 2L; + } else if(otmp->otyp == BAG_OF_TRICKS || /* 1 - 20 */ + otmp->otyp == HORN_OF_PLENTY) { + tmp /= 5L; + } else if(otmp->otyp == CRYSTAL_BALL || /* 1 - 5 */ + otmp->otyp == OIL_LAMP || /* 1 - 10 */ + otmp->otyp == BRASS_LANTERN || + (otmp->otyp >= MAGIC_FLUTE && + otmp->otyp <= DRUM_OF_EARTHQUAKE) || /* 5 - 9 */ + otmp->oclass == WAND_CLASS) { /* 3 - 11 */ + if (otmp->spe > 1) tmp /= 4L; + } else if (otmp->oclass == SPBOOK_CLASS) { + tmp -= tmp / 5L; + } else if (otmp->otyp == CAN_OF_GREASE || + otmp->otyp == TINNING_KIT +#ifdef TOURIST + || otmp->otyp == EXPENSIVE_CAMERA +#endif + ) { + tmp /= 10L; + } else if (otmp->otyp == POT_OIL) { + tmp /= 5L; + } + return(tmp); +} +#endif /*OVL3*/ +#ifdef OVLB + +/* Charge the player for partial use of an unpaid object. + * + * Note that bill_dummy_object() should be used instead + * when an object is completely used. + */ +void +check_unpaid_usage(otmp, altusage) +struct obj *otmp; +boolean altusage; +{ + struct monst *shkp; + const char *fmt, *arg1, *arg2; + long tmp; + + if (!otmp->unpaid || !*u.ushops || + (otmp->spe <= 0 && objects[otmp->otyp].oc_charged)) + return; + if (!(shkp = shop_keeper(*u.ushops)) || !inhishop(shkp)) + return; + if ((tmp = cost_per_charge(shkp, otmp, altusage)) == 0L) + return; + + arg1 = arg2 = ""; + if (otmp->oclass == SPBOOK_CLASS) { + fmt = "%sYou owe%s %ld %s."; + arg1 = rn2(2) ? "This is no free library, cad! " : ""; + arg2 = ESHK(shkp)->debit > 0L ? " an additional" : ""; + } else if (otmp->otyp == POT_OIL) { + fmt = "%s%sThat will cost you %ld %s (Yendorian Fuel Tax)."; + } else { + fmt = "%s%sUsage fee, %ld %s."; + if (!rn2(3)) arg1 = "Hey! "; + if (!rn2(3)) arg2 = "Ahem. "; + } + + if (shkp->mcanmove || !shkp->msleeping) + verbalize(fmt, arg1, arg2, tmp, currency(tmp)); + ESHK(shkp)->debit += tmp; + exercise(A_WIS, TRUE); /* you just got info */ +} + +/* for using charges of unpaid objects "used in the normal manner" */ +void +check_unpaid(otmp) +struct obj *otmp; +{ + check_unpaid_usage(otmp, FALSE); /* normal item use */ +} + +void +costly_gold(x, y, amount) +register xchar x, y; +register long amount; +{ + register long delta; + register struct monst *shkp; + register struct eshk *eshkp; + + if(!costly_spot(x, y)) return; + /* shkp now guaranteed to exist by costly_spot() */ + shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)); + + eshkp = ESHK(shkp); + if(eshkp->credit >= amount) { + if(eshkp->credit > amount) + Your("credit is reduced by %ld %s.", + amount, currency(amount)); + else Your("credit is erased."); + eshkp->credit -= amount; + } else { + delta = amount - eshkp->credit; + if(eshkp->credit) + Your("credit is erased."); + if(eshkp->debit) + Your("debt increases by %ld %s.", + delta, currency(delta)); + else You("owe %s %ld %s.", + shkname(shkp), delta, currency(delta)); + eshkp->debit += delta; + eshkp->loan += delta; + eshkp->credit = 0L; + } +} + +/* used in domove to block diagonal shop-exit */ +/* x,y should always be a door */ +boolean +block_door(x,y) +register xchar x, y; +{ + register int roomno = *in_rooms(x, y, SHOPBASE); + register struct monst *shkp; + + if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE); + if(!IS_DOOR(levl[x][y].typ)) return(FALSE); + if(roomno != *u.ushops) return(FALSE); + + if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp)) + return(FALSE); + + if(shkp->mx == ESHK(shkp)->shk.x && shkp->my == ESHK(shkp)->shk.y + /* Actually, the shk should be made to block _any_ + * door, including a door the player digs, if the + * shk is within a 'jumping' distance. + */ + && ESHK(shkp)->shd.x == x && ESHK(shkp)->shd.y == y + && shkp->mcanmove && !shkp->msleeping + && (ESHK(shkp)->debit || ESHK(shkp)->billct || + ESHK(shkp)->robbed)) { + pline("%s%s blocks your way!", shkname(shkp), + Invis ? " senses your motion and" : ""); + return(TRUE); + } + return(FALSE); +} + +/* used in domove to block diagonal shop-entry */ +/* u.ux, u.uy should always be a door */ +boolean +block_entry(x,y) +register xchar x, y; +{ + register xchar sx, sy; + register int roomno; + register struct monst *shkp; + + if(!(IS_DOOR(levl[u.ux][u.uy].typ) && + levl[u.ux][u.uy].doormask == D_BROKEN)) return(FALSE); + + roomno = *in_rooms(x, y, SHOPBASE); + if(roomno < 0 || !IS_SHOP(roomno)) return(FALSE); + if(!(shkp = shop_keeper((char)roomno)) || !inhishop(shkp)) + return(FALSE); + + if(ESHK(shkp)->shd.x != u.ux || ESHK(shkp)->shd.y != u.uy) + return(FALSE); + + sx = ESHK(shkp)->shk.x; + sy = ESHK(shkp)->shk.y; + + if(shkp->mx == sx && shkp->my == sy + && shkp->mcanmove && !shkp->msleeping + && (x == sx-1 || x == sx+1 || y == sy-1 || y == sy+1) + && (Invis || carrying(PICK_AXE) || carrying(DWARVISH_MATTOCK) +#ifdef STEED + || u.usteed +#endif + )) { + pline("%s%s blocks your way!", shkname(shkp), + Invis ? " senses your motion and" : ""); + return(TRUE); + } + return(FALSE); +} + +#endif /* OVLB */ +#ifdef OVL2 + +char * +shk_your(buf, obj) +char *buf; +struct obj *obj; +{ + if (!shk_owns(buf, obj) && !mon_owns(buf, obj)) + Strcpy(buf, carried(obj) ? "your" : "the"); + return buf; +} + +char * +Shk_Your(buf, obj) +char *buf; +struct obj *obj; +{ + (void) shk_your(buf, obj); + *buf = highc(*buf); + return buf; +} + +STATIC_OVL char * +shk_owns(buf, obj) +char *buf; +struct obj *obj; +{ + struct monst *shkp; + xchar x, y; + + if (get_obj_location(obj, &x, &y, 0) && + (obj->unpaid || + (obj->where==OBJ_FLOOR && !obj->no_charge && costly_spot(x,y)))) { + shkp = shop_keeper(inside_shop(x, y)); + return strcpy(buf, shkp ? s_suffix(shkname(shkp)) : "the"); + } + return (char *)0; +} + +STATIC_OVL char * +mon_owns(buf, obj) +char *buf; +struct obj *obj; +{ + if (obj->where == OBJ_MINVENT) + return strcpy(buf, s_suffix(mon_nam(obj->ocarry))); + return (char *)0; +} + +#endif /* OVL2 */ +#ifdef OVLB + +#ifdef __SASC +void +sasc_bug(struct obj *op, unsigned x){ + op->unpaid=x; +} +#endif + +#endif /* OVLB */ + +/*shk.c*/ diff --git a/src/shknam.c b/src/shknam.c new file mode 100644 index 0000000..f029303 --- /dev/null +++ b/src/shknam.c @@ -0,0 +1,530 @@ +/* SCCS Id: @(#)shknam.c 3.4 2003/01/09 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* shknam.c -- initialize a shop */ + +#include "hack.h" +#include "eshk.h" + +#ifndef OVLB +extern const struct shclass shtypes[]; + +#else + +STATIC_DCL void FDECL(mkshobj_at, (const struct shclass *,int,int)); +STATIC_DCL void FDECL(nameshk, (struct monst *,const char * const *)); +STATIC_DCL int FDECL(shkinit, (const struct shclass *,struct mkroom *)); + +static const char * const shkliquors[] = { + /* Ukraine */ + "Njezjin", "Tsjernigof", "Ossipewsk", "Gorlowka", + /* Belarus */ + "Gomel", + /* N. Russia */ + "Konosja", "Weliki Oestjoeg", "Syktywkar", "Sablja", + "Narodnaja", "Kyzyl", + /* Silezie */ + "Walbrzych", "Swidnica", "Klodzko", "Raciborz", "Gliwice", + "Brzeg", "Krnov", "Hradec Kralove", + /* Schweiz */ + "Leuk", "Brig", "Brienz", "Thun", "Sarnen", "Burglen", "Elm", + "Flims", "Vals", "Schuls", "Zum Loch", + 0 +}; + +static const char * const shkbooks[] = { + /* Eire */ + "Skibbereen", "Kanturk", "Rath Luirc", "Ennistymon", "Lahinch", + "Kinnegad", "Lugnaquillia", "Enniscorthy", "Gweebarra", + "Kittamagh", "Nenagh", "Sneem", "Ballingeary", "Kilgarvan", + "Cahersiveen", "Glenbeigh", "Kilmihil", "Kiltamagh", + "Droichead Atha", "Inniscrone", "Clonegal", "Lisnaskea", + "Culdaff", "Dunfanaghy", "Inishbofin", "Kesh", + 0 +}; + +static const char * const shkarmors[] = { + /* Turquie */ + "Demirci", "Kalecik", "Boyabai", "Yildizeli", "Gaziantep", + "Siirt", "Akhalataki", "Tirebolu", "Aksaray", "Ermenak", + "Iskenderun", "Kadirli", "Siverek", "Pervari", "Malasgirt", + "Bayburt", "Ayancik", "Zonguldak", "Balya", "Tefenni", + "Artvin", "Kars", "Makharadze", "Malazgirt", "Midyat", + "Birecik", "Kirikkale", "Alaca", "Polatli", "Nallihan", + 0 +}; + +static const char * const shkwands[] = { + /* Wales */ + "Yr Wyddgrug", "Trallwng", "Mallwyd", "Pontarfynach", + "Rhaeader", "Llandrindod", "Llanfair-ym-muallt", + "Y-Fenni", "Maesteg", "Rhydaman", "Beddgelert", + "Curig", "Llanrwst", "Llanerchymedd", "Caergybi", + /* Scotland */ + "Nairn", "Turriff", "Inverurie", "Braemar", "Lochnagar", + "Kerloch", "Beinn a Ghlo", "Drumnadrochit", "Morven", + "Uist", "Storr", "Sgurr na Ciche", "Cannich", "Gairloch", + "Kyleakin", "Dunvegan", + 0 +}; + +static const char * const shkrings[] = { + /* Hollandse familienamen */ + "Feyfer", "Flugi", "Gheel", "Havic", "Haynin", "Hoboken", + "Imbyze", "Juyn", "Kinsky", "Massis", "Matray", "Moy", + "Olycan", "Sadelin", "Svaving", "Tapper", "Terwen", "Wirix", + "Ypey", + /* Skandinaviske navne */ + "Rastegaisa", "Varjag Njarga", "Kautekeino", "Abisko", + "Enontekis", "Rovaniemi", "Avasaksa", "Haparanda", + "Lulea", "Gellivare", "Oeloe", "Kajaani", "Fauske", + 0 +}; + +static const char * const shkfoods[] = { + /* Indonesia */ + "Djasinga", "Tjibarusa", "Tjiwidej", "Pengalengan", + "Bandjar", "Parbalingga", "Bojolali", "Sarangan", + "Ngebel", "Djombang", "Ardjawinangun", "Berbek", + "Papar", "Baliga", "Tjisolok", "Siboga", "Banjoewangi", + "Trenggalek", "Karangkobar", "Njalindoeng", "Pasawahan", + "Pameunpeuk", "Patjitan", "Kediri", "Pemboeang", "Tringanoe", + "Makin", "Tipor", "Semai", "Berhala", "Tegal", "Samoe", + 0 +}; + +static const char * const shkweapons[] = { + /* Perigord */ + "Voulgezac", "Rouffiac", "Lerignac", "Touverac", "Guizengeard", + "Melac", "Neuvicq", "Vanzac", "Picq", "Urignac", "Corignac", + "Fleac", "Lonzac", "Vergt", "Queyssac", "Liorac", "Echourgnac", + "Cazelon", "Eypau", "Carignan", "Monbazillac", "Jonzac", + "Pons", "Jumilhac", "Fenouilledes", "Laguiolet", "Saujon", + "Eymoutiers", "Eygurande", "Eauze", "Labouheyre", + 0 +}; + +static const char * const shktools[] = { + /* Spmi */ + "Ymla", "Eed-morra", "Cubask", "Nieb", "Bnowr Falr", "Telloc Cyaj", + "Sperc", "Noskcirdneh", "Yawolloh", "Hyeghu", "Niskal", "Trahnil", + "Htargcm", "Enrobwem", "Kachzi Rellim", "Regien", "Donmyar", + "Yelpur", "Nosnehpets", "Stewe", "Renrut", "_Zlaw", "Nosalnef", + "Rewuorb", "Rellenk", "Yad", "Cire Htims", "Y-crad", "Nenilukah", + "Corsh", "Aned", +#ifdef OVERLAY + "Erreip", "Nehpets", "Mron", "Snivek", "Lapu", "Kahztiy", +#endif +#ifdef WIN32 + "Lechaim", "Lexa", "Niod", +#endif +#ifdef MAC + "Nhoj-lee", "Evad\'kh", "Ettaw-noj", "Tsew-mot", "Ydna-s", + "Yao-hang", "Tonbar", "Kivenhoug", +#endif +#ifdef AMIGA + "Falo", "Nosid-da\'r", "Ekim-p", "Rebrol-nek", "Noslo", "Yl-rednow", + "Mured-oog", "Ivrajimsal", +#endif +#ifdef TOS + "Nivram", +#endif +#ifdef VMS + "Lez-tneg", "Ytnu-haled", "Niknar", +#endif + 0 +}; + +static const char * const shklight[] = { + /* Romania */ + "Zarnesti", "Slanic", "Nehoiasu", "Ludus", "Sighisoara", "Nisipitu", + "Razboieni", "Bicaz", "Dorohoi", "Vaslui", "Fetesti", "Tirgu Neamt", + "Babadag", "Zimnicea", "Zlatna", "Jiu", "Eforie", "Mamaia", + /* Bulgaria */ + "Silistra", "Tulovo", "Panagyuritshte", "Smolyan", "Kirklareli", + "Pernik", "Lom", "Haskovo", "Dobrinishte", "Varvara", "Oryahovo", + "Troyan", "Lovech", "Sliven", + 0 +}; + +static const char * const shkgeneral[] = { + /* Suriname */ + "Hebiwerie", "Possogroenoe", "Asidonhopo", "Manlobbi", + "Adjama", "Pakka Pakka", "Kabalebo", "Wonotobo", + "Akalapi", "Sipaliwini", + /* Greenland */ + "Annootok", "Upernavik", "Angmagssalik", + /* N. Canada */ + "Aklavik", "Inuvik", "Tuktoyaktuk", + "Chicoutimi", "Ouiatchouane", "Chibougamau", + "Matagami", "Kipawa", "Kinojevis", + "Abitibi", "Maganasipi", + /* Iceland */ + "Akureyri", "Kopasker", "Budereyri", "Akranes", "Bordeyri", + "Holmavik", + 0 +}; + +/* + * To add new shop types, all that is necessary is to edit the shtypes[] array. + * See mkroom.h for the structure definition. Typically, you'll have to lower + * some or all of the probability fields in old entries to free up some + * percentage for the new type. + * + * The placement type field is not yet used but will be in the near future. + * + * The iprobs array in each entry defines the probabilities for various kinds + * of objects to be present in the given shop type. You can associate with + * each percentage either a generic object type (represented by one of the + * *_CLASS macros) or a specific object (represented by an onames.h define). + * In the latter case, prepend it with a unary minus so the code can know + * (by testing the sign) whether to use mkobj() or mksobj(). + */ + +const struct shclass shtypes[] = { + {"general store", RANDOM_CLASS, 44, + D_SHOP, {{100, RANDOM_CLASS}, {0, 0}, {0, 0}}, shkgeneral}, + {"used armor dealership", ARMOR_CLASS, 14, + D_SHOP, {{90, ARMOR_CLASS}, {10, WEAPON_CLASS}, {0, 0}}, + shkarmors}, + {"second-hand bookstore", SCROLL_CLASS, 10, D_SHOP, + {{90, SCROLL_CLASS}, {10, SPBOOK_CLASS}, {0, 0}}, shkbooks}, + {"liquor emporium", POTION_CLASS, 10, D_SHOP, + {{100, POTION_CLASS}, {0, 0}, {0, 0}}, shkliquors}, + {"antique weapons outlet", WEAPON_CLASS, 5, D_SHOP, + {{90, WEAPON_CLASS}, {10, ARMOR_CLASS}, {0, 0}}, shkweapons}, + {"delicatessen", FOOD_CLASS, 5, D_SHOP, + {{83, FOOD_CLASS}, {5, -POT_FRUIT_JUICE}, {4, -POT_BOOZE}, + {5, -POT_WATER}, {3, -ICE_BOX}}, shkfoods}, + {"jewelers", RING_CLASS, 3, D_SHOP, + {{85, RING_CLASS}, {10, GEM_CLASS}, {5, AMULET_CLASS}, {0, 0}}, + shkrings}, + {"quality apparel and accessories", WAND_CLASS, 3, D_SHOP, + {{90, WAND_CLASS}, {5, -LEATHER_GLOVES}, {5, -ELVEN_CLOAK}, {0, 0}}, + shkwands}, + {"hardware store", TOOL_CLASS, 3, D_SHOP, + {{100, TOOL_CLASS}, {0, 0}, {0, 0}}, shktools}, + /* Actually shktools is ignored; the code specifically chooses a + * random implementor name (along with candle shops having + * random shopkeepers) + */ + {"rare books", SPBOOK_CLASS, 3, D_SHOP, + {{90, SPBOOK_CLASS}, {10, SCROLL_CLASS}, {0, 0}}, shkbooks}, + /* Shops below this point are "unique". That is they must all have a + * probability of zero. They are only created via the special level + * loader. + */ + {"lighting store", TOOL_CLASS, 0, D_SHOP, + {{32, -WAX_CANDLE}, {50, -TALLOW_CANDLE}, + {5, -BRASS_LANTERN}, {10, -OIL_LAMP}, {3, -MAGIC_LAMP}}, shklight}, + {(char *)0, 0, 0, 0, {{0, 0}, {0, 0}, {0, 0}}, 0} +}; + +#if 0 +/* validate shop probabilities; otherwise incorrect local changes could + end up provoking infinite loops or wild subscripts fetching garbage */ +void +init_shop_selection() +{ + register int i, j, item_prob, shop_prob; + + for (shop_prob = 0, i = 0; i < SIZE(shtypes); i++) { + shop_prob += shtypes[i].prob; + for (item_prob = 0, j = 0; j < SIZE(shtypes[0].iprobs); j++) + item_prob += shtypes[i].iprobs[j].iprob; + if (item_prob != 100) + panic("item probabilities total to %d for %s shops!", + item_prob, shtypes[i].name); + } + if (shop_prob != 100) + panic("shop probabilities total to %d!", shop_prob); +} +#endif /*0*/ + +STATIC_OVL void +mkshobj_at(shp, sx, sy) +/* make an object of the appropriate type for a shop square */ +const struct shclass *shp; +int sx, sy; +{ + struct monst *mtmp; + int atype; + struct permonst *ptr; + + if (rn2(100) < depth(&u.uz) && + !MON_AT(sx, sy) && (ptr = mkclass(S_MIMIC,0)) && + (mtmp = makemon(ptr,sx,sy,NO_MM_FLAGS)) != 0) { + /* note: makemon will set the mimic symbol to a shop item */ + if (rn2(10) >= depth(&u.uz)) { + mtmp->m_ap_type = M_AP_OBJECT; + mtmp->mappearance = STRANGE_OBJECT; + } + } else { + atype = get_shop_item(shp - shtypes); + if (atype < 0) + (void) mksobj_at(-atype, sx, sy, TRUE, TRUE); + else + (void) mkobj_at(atype, sx, sy, TRUE); + } +} + +/* extract a shopkeeper name for the given shop type */ +STATIC_OVL void +nameshk(shk, nlp) +struct monst *shk; +const char * const *nlp; +{ + int i, trycnt, names_avail; + const char *shname = 0; + struct monst *mtmp; + int name_wanted; + s_level *sptr; + + if (nlp == shklight && In_mines(&u.uz) + && (sptr = Is_special(&u.uz)) != 0 && sptr->flags.town) { + /* special-case minetown lighting shk */ + shname = "Izchak"; + shk->female = FALSE; + } else { + /* We want variation from game to game, without needing the save + and restore support which would be necessary for randomization; + try not to make too many assumptions about time_t's internals; + use ledger_no rather than depth to keep mine town distinct. */ + int nseed = (int)((long)u.ubirthday / 257L); + + name_wanted = ledger_no(&u.uz) + (nseed % 13) - (nseed % 5); + if (name_wanted < 0) name_wanted += (13 + 5); + shk->female = name_wanted & 1; + + for (names_avail = 0; nlp[names_avail]; names_avail++) + continue; + + for (trycnt = 0; trycnt < 50; trycnt++) { + if (nlp == shktools) { + shname = shktools[rn2(names_avail)]; + shk->female = (*shname == '_'); + if (shk->female) shname++; + } else if (name_wanted < names_avail) { + shname = nlp[name_wanted]; + } else if ((i = rn2(names_avail)) != 0) { + shname = nlp[i - 1]; + } else if (nlp != shkgeneral) { + nlp = shkgeneral; /* try general names */ + for (names_avail = 0; nlp[names_avail]; names_avail++) + continue; + continue; /* next `trycnt' iteration */ + } else { + shname = shk->female ? "Lucrezia" : "Dirk"; + } + + /* is name already in use on this level? */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp) || (mtmp == shk) || !mtmp->isshk) continue; + if (strcmp(ESHK(mtmp)->shknam, shname)) continue; + break; + } + if (!mtmp) break; /* new name */ + } + } + (void) strncpy(ESHK(shk)->shknam, shname, PL_NSIZ); + ESHK(shk)->shknam[PL_NSIZ-1] = 0; +} + +STATIC_OVL int +shkinit(shp, sroom) /* create a new shopkeeper in the given room */ +const struct shclass *shp; +struct mkroom *sroom; +{ + register int sh, sx, sy; + struct monst *shk; + + /* place the shopkeeper in the given room */ + sh = sroom->fdoor; + sx = doors[sh].x; + sy = doors[sh].y; + + /* check that the shopkeeper placement is sane */ + if(sroom->irregular) { + int rmno = (sroom - rooms) + ROOMOFFSET; + if (isok(sx-1,sy) && !levl[sx-1][sy].edge && + (int) levl[sx-1][sy].roomno == rmno) sx--; + else if (isok(sx+1,sy) && !levl[sx+1][sy].edge && + (int) levl[sx+1][sy].roomno == rmno) sx++; + else if (isok(sx,sy-1) && !levl[sx][sy-1].edge && + (int) levl[sx][sy-1].roomno == rmno) sy--; + else if (isok(sx,sy+1) && !levl[sx][sy+1].edge && + (int) levl[sx][sy+1].roomno == rmno) sx++; + else goto shk_failed; + } + else if(sx == sroom->lx-1) sx++; + else if(sx == sroom->hx+1) sx--; + else if(sy == sroom->ly-1) sy++; + else if(sy == sroom->hy+1) sy--; else { + shk_failed: +#ifdef DEBUG +# ifdef WIZARD + /* Said to happen sometimes, but I have never seen it. */ + /* Supposedly fixed by fdoor change in mklev.c */ + if(wizard) { + register int j = sroom->doorct; + + pline("Where is shopdoor?"); + pline("Room at (%d,%d),(%d,%d).", + sroom->lx, sroom->ly, sroom->hx, sroom->hy); + pline("doormax=%d doorct=%d fdoor=%d", + doorindex, sroom->doorct, sh); + while(j--) { + pline("door [%d,%d]", doors[sh].x, doors[sh].y); + sh++; + } + display_nhwindow(WIN_MESSAGE, FALSE); + } +# endif +#endif + return(-1); + } + + if(MON_AT(sx, sy)) (void) rloc(m_at(sx, sy), FALSE); /* insurance */ + + /* now initialize the shopkeeper monster structure */ + if(!(shk = makemon(&mons[PM_SHOPKEEPER], sx, sy, NO_MM_FLAGS))) + return(-1); + shk->isshk = shk->mpeaceful = 1; + set_malign(shk); + shk->msleeping = 0; + shk->mtrapseen = ~0; /* we know all the traps already */ + ESHK(shk)->shoproom = (sroom - rooms) + ROOMOFFSET; + sroom->resident = shk; + ESHK(shk)->shoptype = sroom->rtype; + assign_level(&(ESHK(shk)->shoplevel), &u.uz); + ESHK(shk)->shd = doors[sh]; + ESHK(shk)->shk.x = sx; + ESHK(shk)->shk.y = sy; + ESHK(shk)->robbed = 0L; + ESHK(shk)->credit = 0L; + ESHK(shk)->debit = 0L; + ESHK(shk)->loan = 0L; + ESHK(shk)->visitct = 0; + ESHK(shk)->following = 0; + ESHK(shk)->billct = 0; +#ifndef GOLDOBJ + shk->mgold = 1000L + 30L*(long)rnd(100); /* initial capital */ +#else + mkmonmoney(shk, 1000L + 30L*(long)rnd(100)); /* initial capital */ +#endif + if (shp->shknms == shkrings) + (void) mongets(shk, TOUCHSTONE); + nameshk(shk, shp->shknms); + + return(sh); +} + +/* stock a newly-created room with objects */ +void +stock_room(shp_indx, sroom) +int shp_indx; +register struct mkroom *sroom; +{ + /* + * Someday soon we'll dispatch on the shdist field of shclass to do + * different placements in this routine. Currently it only supports + * shop-style placement (all squares except a row nearest the first + * door get objects). + */ + register int sx, sy, sh; + char buf[BUFSZ]; + int rmno = (sroom - rooms) + ROOMOFFSET; + const struct shclass *shp = &shtypes[shp_indx]; + + /* first, try to place a shopkeeper in the room */ + if ((sh = shkinit(shp, sroom)) < 0) + return; + + /* make sure no doorways without doors, and no */ + /* trapped doors, in shops. */ + sx = doors[sroom->fdoor].x; + sy = doors[sroom->fdoor].y; + + if(levl[sx][sy].doormask == D_NODOOR) { + levl[sx][sy].doormask = D_ISOPEN; + newsym(sx,sy); + } + if(levl[sx][sy].typ == SDOOR) { + cvt_sdoor_to_door(&levl[sx][sy]); /* .typ = DOOR */ + newsym(sx,sy); + } + if(levl[sx][sy].doormask & D_TRAPPED) + levl[sx][sy].doormask = D_LOCKED; + + if(levl[sx][sy].doormask == D_LOCKED) { + register int m = sx, n = sy; + + if(inside_shop(sx+1,sy)) m--; + else if(inside_shop(sx-1,sy)) m++; + if(inside_shop(sx,sy+1)) n--; + else if(inside_shop(sx,sy-1)) n++; + Sprintf(buf, "Closed for inventory"); + make_engr_at(m, n, buf, 0L, DUST); + } + + for(sx = sroom->lx; sx <= sroom->hx; sx++) + for(sy = sroom->ly; sy <= sroom->hy; sy++) { + if(sroom->irregular) { + if (levl[sx][sy].edge || (int) levl[sx][sy].roomno != rmno || + distmin(sx, sy, doors[sh].x, doors[sh].y) <= 1) + continue; + } else if((sx == sroom->lx && doors[sh].x == sx-1) || + (sx == sroom->hx && doors[sh].x == sx+1) || + (sy == sroom->ly && doors[sh].y == sy-1) || + (sy == sroom->hy && doors[sh].y == sy+1)) continue; + mkshobj_at(shp, sx, sy); + } + + /* + * Special monster placements (if any) should go here: that way, + * monsters will sit on top of objects and not the other way around. + */ + + level.flags.has_shop = TRUE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* does shkp's shop stock this item type? */ +boolean +saleable(shkp, obj) +struct monst *shkp; +struct obj *obj; +{ + int i, shp_indx = ESHK(shkp)->shoptype - SHOPBASE; + const struct shclass *shp = &shtypes[shp_indx]; + + if (shp->symb == RANDOM_CLASS) return TRUE; + else for (i = 0; i < SIZE(shtypes[0].iprobs) && shp->iprobs[i].iprob; i++) + if (shp->iprobs[i].itype < 0 ? + shp->iprobs[i].itype == - obj->otyp : + shp->iprobs[i].itype == obj->oclass) return TRUE; + /* not found */ + return FALSE; +} + +/* positive value: class; negative value: specific object type */ +int +get_shop_item(type) +int type; +{ + const struct shclass *shp = shtypes+type; + register int i,j; + + /* select an appropriate object type at random */ + for(j = rnd(100), i = 0; (j -= shp->iprobs[i].iprob) > 0; i++) + continue; + + return shp->iprobs[i].itype; +} + +#endif /* OVL0 */ + +/*shknam.c*/ diff --git a/src/sit.c b/src/sit.c new file mode 100644 index 0000000..2d2df2b --- /dev/null +++ b/src/sit.c @@ -0,0 +1,451 @@ +/* SCCS Id: @(#)sit.c 3.4 2002/09/21 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "artifact.h" + +void +take_gold() +{ +#ifndef GOLDOBJ + if (u.ugold <= 0) { + You_feel("a strange sensation."); + } else { + You("notice you have no gold!"); + u.ugold = 0; + flags.botl = 1; + } +#else + struct obj *otmp, *nobj; + int lost_money = 0; + for (otmp = invent; otmp; otmp = nobj) { + nobj = otmp->nobj; + if (otmp->oclass == COIN_CLASS) { + lost_money = 1; + delobj(otmp); + } + } + if (!lost_money) { + You_feel("a strange sensation."); + } else { + You("notice you have no money!"); + flags.botl = 1; + } +#endif +} + +int +dosit() +{ + static const char sit_message[] = "sit on the %s."; + register struct trap *trap; + register int typ = levl[u.ux][u.uy].typ; + + +#ifdef STEED + if (u.usteed) { + You("are already sitting on %s.", mon_nam(u.usteed)); + return (0); + } +#endif + + if(!can_reach_floor()) { + if (Levitation) + You("tumble in place."); + else + You("are sitting on air."); + return 0; + } else if (is_pool(u.ux, u.uy) && !Underwater) { /* water walking */ + goto in_water; + } + + if(OBJ_AT(u.ux, u.uy)) { + register struct obj *obj; + + obj = level.objects[u.ux][u.uy]; + You("sit on %s.", the(xname(obj))); + if (!(Is_box(obj) || objects[obj->otyp].oc_material == CLOTH)) + pline("It's not very comfortable..."); + + } else if ((trap = t_at(u.ux, u.uy)) != 0 || + (u.utrap && (u.utraptype >= TT_LAVA))) { + + if (u.utrap) { + exercise(A_WIS, FALSE); /* you're getting stuck longer */ + if(u.utraptype == TT_BEARTRAP) { + You_cant("sit down with your %s in the bear trap.", body_part(FOOT)); + u.utrap++; + } else if(u.utraptype == TT_PIT) { + if(trap->ttyp == SPIKED_PIT) { + You("sit down on a spike. Ouch!"); + losehp(1, "sitting on an iron spike", KILLED_BY); + exercise(A_STR, FALSE); + } else + You("sit down in the pit."); + u.utrap += rn2(5); + } else if(u.utraptype == TT_WEB) { + You("sit in the spider web and get entangled further!"); + u.utrap += rn1(10, 5); + } else if(u.utraptype == TT_LAVA) { + /* Must have fire resistance or they'd be dead already */ + You("sit in the lava!"); + u.utrap += rnd(4); + losehp(d(2,10), "sitting in lava", KILLED_BY); + } else if(u.utraptype == TT_INFLOOR) { + You_cant("maneuver to sit!"); + u.utrap++; + } + } else { + You("sit down."); + dotrap(trap, 0); + } + } else if(Underwater || Is_waterlevel(&u.uz)) { + if (Is_waterlevel(&u.uz)) + There("are no cushions floating nearby."); + else + You("sit down on the muddy bottom."); + } else if(is_pool(u.ux, u.uy)) { + in_water: + You("sit in the water."); + if (!rn2(10) && uarm) + (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst); + if (!rn2(10) && uarmf && uarmf->otyp != WATER_WALKING_BOOTS) + (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst); +#ifdef SINKS + } else if(IS_SINK(typ)) { + + You(sit_message, defsyms[S_sink].explanation); + Your("%s gets wet.", humanoid(youmonst.data) ? "rump" : "underside"); +#endif + } else if(IS_ALTAR(typ)) { + + You(sit_message, defsyms[S_altar].explanation); + altar_wrath(u.ux, u.uy); + + } else if(IS_GRAVE(typ)) { + + You(sit_message, defsyms[S_grave].explanation); + + } else if(typ == STAIRS) { + + You(sit_message, "stairs"); + + } else if(typ == LADDER) { + + You(sit_message, "ladder"); + + } else if (is_lava(u.ux, u.uy)) { + + /* must be WWalking */ + You(sit_message, "lava"); + burn_away_slime(); + if (likes_lava(youmonst.data)) { + pline_The("lava feels warm."); + return 1; + } + pline_The("lava burns you!"); + losehp(d((Fire_resistance ? 2 : 10), 10), + "sitting on lava", KILLED_BY); + + } else if (is_ice(u.ux, u.uy)) { + + You(sit_message, defsyms[S_ice].explanation); + if (!Cold_resistance) pline_The("ice feels cold."); + + } else if (typ == DRAWBRIDGE_DOWN) { + + You(sit_message, "drawbridge"); + + } else if(IS_THRONE(typ)) { + + You(sit_message, defsyms[S_throne].explanation); + if (rnd(6) > 4) { + switch (rnd(13)) { + case 1: + (void) adjattrib(rn2(A_MAX), -rn1(4,3), FALSE); + losehp(rnd(10), "cursed throne", KILLED_BY_AN); + break; + case 2: + (void) adjattrib(rn2(A_MAX), 1, FALSE); + break; + case 3: + pline("A%s electric shock shoots through your body!", + (Shock_resistance) ? "n" : " massive"); + losehp(Shock_resistance ? rnd(6) : rnd(30), + "electric chair", KILLED_BY_AN); + exercise(A_CON, FALSE); + break; + case 4: + You_feel("much, much better!"); + if (Upolyd) { + if (u.mh >= (u.mhmax - 5)) u.mhmax += 4; + u.mh = u.mhmax; + } + if(u.uhp >= (u.uhpmax - 5)) u.uhpmax += 4; + u.uhp = u.uhpmax; + make_blinded(0L,TRUE); + make_sick(0L, (char *) 0, FALSE, SICK_ALL); + heal_legs(); + flags.botl = 1; + break; + case 5: + take_gold(); + break; + case 6: + if(u.uluck + rn2(5) < 0) { + You_feel("your luck is changing."); + change_luck(1); + } else makewish(); + break; + case 7: + { + register int cnt = rnd(10); + + pline("A voice echoes:"); + verbalize("Thy audience hath been summoned, %s!", + flags.female ? "Dame" : "Sire"); + while(cnt--) + (void) makemon(courtmon(), u.ux, u.uy, NO_MM_FLAGS); + break; + } + case 8: + pline("A voice echoes:"); + verbalize("By thy Imperious order, %s...", + flags.female ? "Dame" : "Sire"); + do_genocide(5); /* REALLY|ONTHRONE, see do_genocide() */ + break; + case 9: + pline("A voice echoes:"); + verbalize("A curse upon thee for sitting upon this most holy throne!"); + if (Luck > 0) { + make_blinded(Blinded + rn1(100,250),TRUE); + } else rndcurse(); + break; + case 10: + if (Luck < 0 || (HSee_invisible & INTRINSIC)) { + if (level.flags.nommap) { + pline( + "A terrible drone fills your head!"); + make_confused(HConfusion + rnd(30), + FALSE); + } else { + pline("An image forms in your mind."); + do_mapping(); + } + } else { + Your("vision becomes clear."); + HSee_invisible |= FROMOUTSIDE; + newsym(u.ux, u.uy); + } + break; + case 11: + if (Luck < 0) { + You_feel("threatened."); + aggravate(); + } else { + + You_feel("a wrenching sensation."); + tele(); /* teleport him */ + } + break; + case 12: + You("are granted an insight!"); + if (invent) { + /* rn2(5) agrees w/seffects() */ + identify_pack(rn2(5)); + } + break; + case 13: + Your("mind turns into a pretzel!"); + make_confused(HConfusion + rn1(7,16),FALSE); + break; + default: impossible("throne effect"); + break; + } + } else { + if (is_prince(youmonst.data)) + You_feel("very comfortable here."); + else + You_feel("somehow out of place..."); + } + + if (!rn2(3) && IS_THRONE(levl[u.ux][u.uy].typ)) { + /* may have teleported */ + levl[u.ux][u.uy].typ = ROOM; + pline_The("throne vanishes in a puff of logic."); + newsym(u.ux,u.uy); + } + + } else if (lays_eggs(youmonst.data)) { + struct obj *uegg; + + if (!flags.female) { + pline("Males can't lay eggs!"); + return 0; + } + + if (u.uhunger < (int)objects[EGG].oc_nutrition) { + You("don't have enough energy to lay an egg."); + return 0; + } + + uegg = mksobj(EGG, FALSE, FALSE); + uegg->spe = 1; + uegg->quan = 1; + uegg->owt = weight(uegg); + uegg->corpsenm = egg_type_from_parent(u.umonnum, FALSE); + uegg->known = uegg->dknown = 1; + attach_egg_hatch_timeout(uegg); + You("lay an egg."); + dropy(uegg); + stackobj(uegg); + morehungry((int)objects[EGG].oc_nutrition); + } else if (u.uswallow) + There("are no seats in here!"); + else + pline("Having fun sitting on the %s?", surface(u.ux,u.uy)); + return(1); +} + +void +rndcurse() /* curse a few inventory items at random! */ +{ + int nobj = 0; + int cnt, onum; + struct obj *otmp; + static const char mal_aura[] = "feel a malignant aura surround %s."; + + if (uwep && (uwep->oartifact == ART_MAGICBANE) && rn2(20)) { + You(mal_aura, "the magic-absorbing blade"); + return; + } + + if(Antimagic) { + shieldeff(u.ux, u.uy); + You(mal_aura, "you"); + } + + for (otmp = invent; otmp; otmp = otmp->nobj) { +#ifdef GOLDOBJ + /* gold isn't subject to being cursed or blessed */ + if (otmp->oclass == COIN_CLASS) continue; +#endif + nobj++; + } + if (nobj) { + for (cnt = rnd(6/((!!Antimagic) + (!!Half_spell_damage) + 1)); + cnt > 0; cnt--) { + onum = rnd(nobj); + for (otmp = invent; otmp; otmp = otmp->nobj) { +#ifdef GOLDOBJ + /* as above */ + if (otmp->oclass == COIN_CLASS) continue; +#endif + if (--onum == 0) break; /* found the target */ + } + /* the !otmp case should never happen; picking an already + cursed item happens--avoid "resists" message in that case */ + if (!otmp || otmp->cursed) continue; /* next target */ + + if(otmp->oartifact && spec_ability(otmp, SPFX_INTEL) && + rn2(10) < 8) { + pline("%s!", Tobjnam(otmp, "resist")); + continue; + } + + if(otmp->blessed) + unbless(otmp); + else + curse(otmp); + } + update_inventory(); + } + +#ifdef STEED + /* treat steed's saddle as extended part of hero's inventory */ + if (u.usteed && !rn2(4) && + (otmp = which_armor(u.usteed, W_SADDLE)) != 0 && + !otmp->cursed) { /* skip if already cursed */ + if (otmp->blessed) + unbless(otmp); + else + curse(otmp); + if (!Blind) { + pline("%s %s %s.", + s_suffix(upstart(y_monnam(u.usteed))), + aobjnam(otmp, "glow"), + hcolor(otmp->cursed ? NH_BLACK : (const char *)"brown")); + otmp->bknown = TRUE; + } + } +#endif /*STEED*/ +} + +void +attrcurse() /* remove a random INTRINSIC ability */ +{ + switch(rnd(11)) { + case 1 : if (HFire_resistance & INTRINSIC) { + HFire_resistance &= ~INTRINSIC; + You_feel("warmer."); + break; + } + case 2 : if (HTeleportation & INTRINSIC) { + HTeleportation &= ~INTRINSIC; + You_feel("less jumpy."); + break; + } + case 3 : if (HPoison_resistance & INTRINSIC) { + HPoison_resistance &= ~INTRINSIC; + You_feel("a little sick!"); + break; + } + case 4 : if (HTelepat & INTRINSIC) { + HTelepat &= ~INTRINSIC; + if (Blind && !Blind_telepat) + see_monsters(); /* Can't sense mons anymore! */ + Your("senses fail!"); + break; + } + case 5 : if (HCold_resistance & INTRINSIC) { + HCold_resistance &= ~INTRINSIC; + You_feel("cooler."); + break; + } + case 6 : if (HInvis & INTRINSIC) { + HInvis &= ~INTRINSIC; + You_feel("paranoid."); + break; + } + case 7 : if (HSee_invisible & INTRINSIC) { + HSee_invisible &= ~INTRINSIC; + You("%s!", Hallucination ? "tawt you taw a puttie tat" + : "thought you saw something"); + break; + } + case 8 : if (HFast & INTRINSIC) { + HFast &= ~INTRINSIC; + You_feel("slower."); + break; + } + case 9 : if (HStealth & INTRINSIC) { + HStealth &= ~INTRINSIC; + You_feel("clumsy."); + break; + } + case 10: if (HProtection & INTRINSIC) { + HProtection &= ~INTRINSIC; + You_feel("vulnerable."); + break; + } + case 11: if (HAggravate_monster & INTRINSIC) { + HAggravate_monster &= ~INTRINSIC; + You_feel("less attractive."); + break; + } + default: break; + } +} + +/*sit.c*/ diff --git a/src/sounds.c b/src/sounds.c new file mode 100644 index 0000000..ebd51aa --- /dev/null +++ b/src/sounds.c @@ -0,0 +1,1033 @@ +/* SCCS Id: @(#)sounds.c 3.4 2002/05/06 */ +/* Copyright (c) 1989 Janet Walz, Mike Threepoint */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "edog.h" +#ifdef USER_SOUNDS +# ifdef USER_SOUNDS_REGEX +#include +# endif +#endif + +#ifdef OVLB + +static int FDECL(domonnoise,(struct monst *)); +static int NDECL(dochat); + +#endif /* OVLB */ + +#ifdef OVL0 + +static int FDECL(mon_in_room, (struct monst *,int)); + +/* this easily could be a macro, but it might overtax dumb compilers */ +static int +mon_in_room(mon, rmtyp) +struct monst *mon; +int rmtyp; +{ + int rno = levl[mon->mx][mon->my].roomno; + + return rooms[rno - ROOMOFFSET].rtype == rmtyp; +} + +void +dosounds() +{ + register struct mkroom *sroom; + register int hallu, vx, vy; +#if defined(AMIGA) && defined(AZTEC_C_WORKAROUND) + int xx; +#endif + struct monst *mtmp; + + if (!flags.soundok || u.uswallow || Underwater) return; + + hallu = Hallucination ? 1 : 0; + + if (level.flags.nfountains && !rn2(400)) { + static const char * const fountain_msg[4] = { + "bubbling water.", + "water falling on coins.", + "the splashing of a naiad.", + "a soda fountain!", + }; + You_hear(fountain_msg[rn2(3)+hallu]); + } +#ifdef SINK + if (level.flags.nsinks && !rn2(300)) { + static const char * const sink_msg[3] = { + "a slow drip.", + "a gurgling noise.", + "dishes being washed!", + }; + You_hear(sink_msg[rn2(2)+hallu]); + } +#endif + if (level.flags.has_court && !rn2(200)) { + static const char * const throne_msg[4] = { + "the tones of courtly conversation.", + "a sceptre pounded in judgment.", + "Someone shouts \"Off with %s head!\"", + "Queen Beruthiel's cats!", + }; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->msleeping || + is_lord(mtmp->data) || is_prince(mtmp->data)) && + !is_animal(mtmp->data) && + mon_in_room(mtmp, COURT)) { + /* finding one is enough, at least for now */ + int which = rn2(3)+hallu; + + if (which != 2) You_hear(throne_msg[which]); + else pline(throne_msg[2], uhis()); + return; + } + } + } + if (level.flags.has_swamp && !rn2(200)) { + static const char * const swamp_msg[3] = { + "hear mosquitoes!", + "smell marsh gas!", /* so it's a smell...*/ + "hear Donald Duck!", + }; + You(swamp_msg[rn2(2)+hallu]); + return; + } + if (level.flags.has_vault && !rn2(200)) { + if (!(sroom = search_special(VAULT))) { + /* strange ... */ + level.flags.has_vault = 0; + return; + } + if(gd_sound()) + switch (rn2(2)+hallu) { + case 1: { + boolean gold_in_vault = FALSE; + + for (vx = sroom->lx;vx <= sroom->hx; vx++) + for (vy = sroom->ly; vy <= sroom->hy; vy++) + if (g_at(vx, vy)) + gold_in_vault = TRUE; +#if defined(AMIGA) && defined(AZTEC_C_WORKAROUND) + /* Bug in aztec assembler here. Workaround below */ + xx = ROOM_INDEX(sroom) + ROOMOFFSET; + xx = (xx != vault_occupied(u.urooms)); + if(xx) +#else + if (vault_occupied(u.urooms) != + (ROOM_INDEX(sroom) + ROOMOFFSET)) +#endif /* AZTEC_C_WORKAROUND */ + { + if (gold_in_vault) + You_hear(!hallu ? "someone counting money." : + "the quarterback calling the play."); + else + You_hear("someone searching."); + break; + } + /* fall into... (yes, even for hallucination) */ + } + case 0: + You_hear("the footsteps of a guard on patrol."); + break; + case 2: + You_hear("Ebenezer Scrooge!"); + break; + } + return; + } + if (level.flags.has_beehive && !rn2(200)) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->data->mlet == S_ANT && is_flyer(mtmp->data)) && + mon_in_room(mtmp, BEEHIVE)) { + switch (rn2(2)+hallu) { + case 0: + You_hear("a low buzzing."); + break; + case 1: + You_hear("an angry drone."); + break; + case 2: + You_hear("bees in your %sbonnet!", + uarmh ? "" : "(nonexistent) "); + break; + } + return; + } + } + } + if (level.flags.has_morgue && !rn2(200)) { + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if (is_undead(mtmp->data) && + mon_in_room(mtmp, MORGUE)) { + switch (rn2(2)+hallu) { + case 0: + You("suddenly realize it is unnaturally quiet."); + break; + case 1: + pline_The("%s on the back of your %s stands up.", + body_part(HAIR), body_part(NECK)); + break; + case 2: + pline_The("%s on your %s seems to stand up.", + body_part(HAIR), body_part(HEAD)); + break; + } + return; + } + } + } + if (level.flags.has_barracks && !rn2(200)) { + static const char * const barracks_msg[4] = { + "blades being honed.", + "loud snoring.", + "dice being thrown.", + "General MacArthur!", + }; + int count = 0; + + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if (is_mercenary(mtmp->data) && +#if 0 /* don't bother excluding these */ + !strstri(mtmp->data->mname, "watch") && + !strstri(mtmp->data->mname, "guard") && +#endif + mon_in_room(mtmp, BARRACKS) && + /* sleeping implies not-yet-disturbed (usually) */ + (mtmp->msleeping || ++count > 5)) { + You_hear(barracks_msg[rn2(3)+hallu]); + return; + } + } + } + if (level.flags.has_zoo && !rn2(200)) { + static const char * const zoo_msg[3] = { + "a sound reminiscent of an elephant stepping on a peanut.", + "a sound reminiscent of a seal barking.", + "Doctor Doolittle!", + }; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->msleeping || is_animal(mtmp->data)) && + mon_in_room(mtmp, ZOO)) { + You_hear(zoo_msg[rn2(2)+hallu]); + return; + } + } + } + if (level.flags.has_shop && !rn2(200)) { + if (!(sroom = search_special(ANY_SHOP))) { + /* strange... */ + level.flags.has_shop = 0; + return; + } + if (tended_shop(sroom) && + !index(u.ushops, ROOM_INDEX(sroom) + ROOMOFFSET)) { + static const char * const shop_msg[3] = { + "someone cursing shoplifters.", + "the chime of a cash register.", + "Neiman and Marcus arguing!", + }; + You_hear(shop_msg[rn2(2)+hallu]); + } + return; + } + if (Is_oracle_level(&u.uz) && !rn2(400)) { + /* make sure the Oracle is still here */ + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && mtmp->data == &mons[PM_ORACLE]) + break; + /* and don't produce silly effects when she's clearly visible */ + if (mtmp && (hallu || !canseemon(mtmp))) { + static const char * const ora_msg[5] = { + "a strange wind.", /* Jupiter at Dodona */ + "convulsive ravings.", /* Apollo at Delphi */ + "snoring snakes.", /* AEsculapius at Epidaurus */ + "someone say \"No more woodchucks!\"", + "a loud ZOT!" /* both rec.humor.oracle */ + }; + You_hear(ora_msg[rn2(3)+hallu*2]); + } + return; + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +static const char * const h_sounds[] = { + "beep", "boing", "sing", "belche", "creak", "cough", "rattle", + "ululate", "pop", "jingle", "sniffle", "tinkle", "eep" +}; + +const char * +growl_sound(mtmp) +register struct monst *mtmp; +{ + const char *ret; + + switch (mtmp->data->msound) { + case MS_MEW: + case MS_HISS: + ret = "hiss"; + break; + case MS_BARK: + case MS_GROWL: + ret = "growl"; + break; + case MS_ROAR: + ret = "roar"; + break; + case MS_BUZZ: + ret = "buzz"; + break; + case MS_SQEEK: + ret = "squeal"; + break; + case MS_SQAWK: + ret = "screech"; + break; + case MS_NEIGH: + ret = "neigh"; + break; + case MS_WAIL: + ret = "wail"; + break; + case MS_SILENT: + ret = "commotion"; + break; + default: + ret = "scream"; + } + return ret; +} + +/* the sounds of a seriously abused pet, including player attacking it */ +void +growl(mtmp) +register struct monst *mtmp; +{ + register const char *growl_verb = 0; + + if (mtmp->msleeping || !mtmp->mcanmove || !mtmp->data->msound) + return; + + /* presumably nearness and soundok checks have already been made */ + if (Hallucination) + growl_verb = h_sounds[rn2(SIZE(h_sounds))]; + else + growl_verb = growl_sound(mtmp); + if (growl_verb) { + pline("%s %s!", Monnam(mtmp), vtense((char *)0, growl_verb)); + if(flags.run) nomul(0); + wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 18); + } +} + +/* the sounds of mistreated pets */ +void +yelp(mtmp) +register struct monst *mtmp; +{ + register const char *yelp_verb = 0; + + if (mtmp->msleeping || !mtmp->mcanmove || !mtmp->data->msound) + return; + + /* presumably nearness and soundok checks have already been made */ + if (Hallucination) + yelp_verb = h_sounds[rn2(SIZE(h_sounds))]; + else switch (mtmp->data->msound) { + case MS_MEW: + yelp_verb = "yowl"; + break; + case MS_BARK: + case MS_GROWL: + yelp_verb = "yelp"; + break; + case MS_ROAR: + yelp_verb = "snarl"; + break; + case MS_SQEEK: + yelp_verb = "squeal"; + break; + case MS_SQAWK: + yelp_verb = "screak"; + break; + case MS_WAIL: + yelp_verb = "wail"; + break; + } + if (yelp_verb) { + pline("%s %s!", Monnam(mtmp), vtense((char *)0, yelp_verb)); + if(flags.run) nomul(0); + wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 12); + } +} + +/* the sounds of distressed pets */ +void +whimper(mtmp) +register struct monst *mtmp; +{ + register const char *whimper_verb = 0; + + if (mtmp->msleeping || !mtmp->mcanmove || !mtmp->data->msound) + return; + + /* presumably nearness and soundok checks have already been made */ + if (Hallucination) + whimper_verb = h_sounds[rn2(SIZE(h_sounds))]; + else switch (mtmp->data->msound) { + case MS_MEW: + case MS_GROWL: + whimper_verb = "whimper"; + break; + case MS_BARK: + whimper_verb = "whine"; + break; + case MS_SQEEK: + whimper_verb = "squeal"; + break; + } + if (whimper_verb) { + pline("%s %s.", Monnam(mtmp), vtense((char *)0, whimper_verb)); + if(flags.run) nomul(0); + wake_nearto(mtmp->mx, mtmp->my, mtmp->data->mlevel * 6); + } +} + +/* pet makes "I'm hungry" noises */ +void +beg(mtmp) +register struct monst *mtmp; +{ + if (mtmp->msleeping || !mtmp->mcanmove || + !(carnivorous(mtmp->data) || herbivorous(mtmp->data))) + return; + + /* presumably nearness and soundok checks have already been made */ + if (!is_silent(mtmp->data) && mtmp->data->msound <= MS_ANIMAL) + (void) domonnoise(mtmp); + else if (mtmp->data->msound >= MS_HUMANOID) { + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + verbalize("I'm hungry."); + } +} + +static int +domonnoise(mtmp) +register struct monst *mtmp; +{ + register const char *pline_msg = 0, /* Monnam(mtmp) will be prepended */ + *verbl_msg = 0; /* verbalize() */ + struct permonst *ptr = mtmp->data; + char verbuf[BUFSZ]; + + /* presumably nearness and sleep checks have already been made */ + if (!flags.soundok) return(0); + if (is_silent(ptr)) return(0); + + /* Make sure its your role's quest quardian; adjust if not */ + if (ptr->msound == MS_GUARDIAN && ptr != &mons[urole.guardnum]) { + int mndx = monsndx(ptr); + ptr = &mons[genus(mndx,1)]; + } + + /* be sure to do this before talking; the monster might teleport away, in + * which case we want to check its pre-teleport position + */ + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + + switch (ptr->msound) { + case MS_ORACLE: + return doconsult(mtmp); + case MS_PRIEST: + priest_talk(mtmp); + break; + case MS_LEADER: + case MS_NEMESIS: + case MS_GUARDIAN: + quest_chat(mtmp); + break; + case MS_SELL: /* pitch, pay, total */ + shk_chat(mtmp); + break; + case MS_VAMPIRE: + { + /* vampire messages are varied by tameness, peacefulness, and time of night */ + boolean isnight = night(); + boolean kindred = (Upolyd && (u.umonnum == PM_VAMPIRE || + u.umonnum == PM_VAMPIRE_LORD)); + boolean nightchild = (Upolyd && (u.umonnum == PM_WOLF || + u.umonnum == PM_WINTER_WOLF || + u.umonnum == PM_WINTER_WOLF_CUB)); + const char *racenoun = (flags.female && urace.individual.f) ? + urace.individual.f : (urace.individual.m) ? + urace.individual.m : urace.noun; + + if (mtmp->mtame) { + if (kindred) { + Sprintf(verbuf, "Good %s to you Master%s", + isnight ? "evening" : "day", + isnight ? "!" : ". Why do we not rest?"); + verbl_msg = verbuf; + } else { + Sprintf(verbuf,"%s%s", + nightchild ? "Child of the night, " : "", + midnight() ? + "I can stand this craving no longer!" : + isnight ? + "I beg you, help me satisfy this growing craving!" : + "I find myself growing a little weary."); + verbl_msg = verbuf; + } + } else if (mtmp->mpeaceful) { + if (kindred && isnight) { + Sprintf(verbuf, "Good feeding %s!", + flags.female ? "sister" : "brother"); + verbl_msg = verbuf; + } else if (nightchild && isnight) { + Sprintf(verbuf, + "How nice to hear you, child of the night!"); + verbl_msg = verbuf; + } else + verbl_msg = "I only drink... potions."; + } else { + int vampindex; + static const char * const vampmsg[] = { + /* These first two (0 and 1) are specially handled below */ + "I vant to suck your %s!", + "I vill come after %s without regret!", + /* other famous vampire quotes can follow here if desired */ + }; + if (kindred) + verbl_msg = "This is my hunting ground that you dare to prowl!"; + else if (youmonst.data == &mons[PM_SILVER_DRAGON] || + youmonst.data == &mons[PM_BABY_SILVER_DRAGON]) { + /* Silver dragons are silver in color, not made of silver */ + Sprintf(verbuf, "%s! Your silver sheen does not frighten me!", + youmonst.data == &mons[PM_SILVER_DRAGON] ? + "Fool" : "Young Fool"); + verbl_msg = verbuf; + } else { + vampindex = rn2(SIZE(vampmsg)); + if (vampindex == 0) { + Sprintf(verbuf, vampmsg[vampindex], body_part(BLOOD)); + verbl_msg = verbuf; + } else if (vampindex == 1) { + Sprintf(verbuf, vampmsg[vampindex], + Upolyd ? an(mons[u.umonnum].mname) : an(racenoun)); + verbl_msg = verbuf; + } else + verbl_msg = vampmsg[vampindex]; + } + } + } + break; + case MS_WERE: + if (flags.moonphase == FULL_MOON && (night() ^ !rn2(13))) { + pline("%s throws back %s head and lets out a blood curdling %s!", + Monnam(mtmp), mhis(mtmp), + ptr == &mons[PM_HUMAN_WERERAT] ? "shriek" : "howl"); + wake_nearto(mtmp->mx, mtmp->my, 11*11); + } else + pline_msg = + "whispers inaudibly. All you can make out is \"moon\"."; + break; + case MS_BARK: + if (flags.moonphase == FULL_MOON && night()) { + pline_msg = "howls."; + } else if (mtmp->mpeaceful) { + if (mtmp->mtame && + (mtmp->mconf || mtmp->mflee || mtmp->mtrapped || + moves > EDOG(mtmp)->hungrytime || mtmp->mtame < 5)) + pline_msg = "whines."; + else if (mtmp->mtame && EDOG(mtmp)->hungrytime > moves + 1000) + pline_msg = "yips."; + else { + if (mtmp->data != &mons[PM_DINGO]) /* dingos do not actually bark */ + pline_msg = "barks."; + } + } else { + pline_msg = "growls."; + } + break; + case MS_MEW: + if (mtmp->mtame) { + if (mtmp->mconf || mtmp->mflee || mtmp->mtrapped || + mtmp->mtame < 5) + pline_msg = "yowls."; + else if (moves > EDOG(mtmp)->hungrytime) + pline_msg = "meows."; + else if (EDOG(mtmp)->hungrytime > moves + 1000) + pline_msg = "purrs."; + else + pline_msg = "mews."; + break; + } /* else FALLTHRU */ + case MS_GROWL: + pline_msg = mtmp->mpeaceful ? "snarls." : "growls!"; + break; + case MS_ROAR: + pline_msg = mtmp->mpeaceful ? "snarls." : "roars!"; + break; + case MS_SQEEK: + pline_msg = "squeaks."; + break; + case MS_SQAWK: + if (ptr == &mons[PM_RAVEN] && !mtmp->mpeaceful) + verbl_msg = "Nevermore!"; + else + pline_msg = "squawks."; + break; + case MS_HISS: + if (!mtmp->mpeaceful) + pline_msg = "hisses!"; + else return 0; /* no sound */ + break; + case MS_BUZZ: + pline_msg = mtmp->mpeaceful ? "drones." : "buzzes angrily."; + break; + case MS_GRUNT: + pline_msg = "grunts."; + break; + case MS_NEIGH: + if (mtmp->mtame < 5) + pline_msg = "neighs."; + else if (moves > EDOG(mtmp)->hungrytime) + pline_msg = "whinnies."; + else + pline_msg = "whickers."; + break; + case MS_WAIL: + pline_msg = "wails mournfully."; + break; + case MS_GURGLE: + pline_msg = "gurgles."; + break; + case MS_BURBLE: + pline_msg = "burbles."; + break; + case MS_SHRIEK: + pline_msg = "shrieks."; + aggravate(); + break; + case MS_IMITATE: + pline_msg = "imitates you."; + break; + case MS_BONES: + pline("%s rattles noisily.", Monnam(mtmp)); + You("freeze for a moment."); + nomul(-2); + break; + case MS_LAUGH: + { + static const char * const laugh_msg[4] = { + "giggles.", "chuckles.", "snickers.", "laughs.", + }; + pline_msg = laugh_msg[rn2(4)]; + } + break; + case MS_MUMBLE: + pline_msg = "mumbles incomprehensibly."; + break; + case MS_DJINNI: + if (mtmp->mtame) { + verbl_msg = "Sorry, I'm all out of wishes."; + } else if (mtmp->mpeaceful) { + if (ptr == &mons[PM_WATER_DEMON]) + pline_msg = "gurgles."; + else + verbl_msg = "I'm free!"; + } else verbl_msg = "This will teach you not to disturb me!"; + break; + case MS_BOAST: /* giants */ + if (!mtmp->mpeaceful) { + switch (rn2(4)) { + case 0: pline("%s boasts about %s gem collection.", + Monnam(mtmp), mhis(mtmp)); + break; + case 1: pline_msg = "complains about a diet of mutton."; + break; + default: pline_msg = "shouts \"Fee Fie Foe Foo!\" and guffaws."; + wake_nearto(mtmp->mx, mtmp->my, 7*7); + break; + } + break; + } + /* else FALLTHRU */ + case MS_HUMANOID: + if (!mtmp->mpeaceful) { + if (In_endgame(&u.uz) && is_mplayer(ptr)) { + mplayer_talk(mtmp); + break; + } else return 0; /* no sound */ + } + /* Generic peaceful humanoid behaviour. */ + if (mtmp->mflee) + pline_msg = "wants nothing to do with you."; + else if (mtmp->mhp < mtmp->mhpmax/4) + pline_msg = "moans."; + else if (mtmp->mconf || mtmp->mstun) + verbl_msg = !rn2(3) ? "Huh?" : rn2(2) ? "What?" : "Eh?"; + else if (!mtmp->mcansee) + verbl_msg = "I can't see!"; + else if (mtmp->mtrapped) { + struct trap *t = t_at(mtmp->mx, mtmp->my); + + if (t) t->tseen = 1; + verbl_msg = "I'm trapped!"; + } else if (mtmp->mhp < mtmp->mhpmax/2) + pline_msg = "asks for a potion of healing."; + else if (mtmp->mtame && !mtmp->isminion && + moves > EDOG(mtmp)->hungrytime) + verbl_msg = "I'm hungry."; + /* Specific monsters' interests */ + else if (is_elf(ptr)) + pline_msg = "curses orcs."; + else if (is_dwarf(ptr)) + pline_msg = "talks about mining."; + else if (likes_magic(ptr)) + pline_msg = "talks about spellcraft."; + else if (ptr->mlet == S_CENTAUR) + pline_msg = "discusses hunting."; + else switch (monsndx(ptr)) { + case PM_HOBBIT: + pline_msg = (mtmp->mhpmax - mtmp->mhp >= 10) ? + "complains about unpleasant dungeon conditions." + : "asks you about the One Ring."; + break; + case PM_ARCHEOLOGIST: + pline_msg = "describes a recent article in \"Spelunker Today\" magazine."; + break; +#ifdef TOURIST + case PM_TOURIST: + verbl_msg = "Aloha."; + break; +#endif + default: + pline_msg = "discusses dungeon exploration."; + break; + } + break; + case MS_SEDUCE: +#ifdef SEDUCE + if (ptr->mlet != S_NYMPH && + could_seduce(mtmp, &youmonst, (struct attack *)0) == 1) { + (void) doseduce(mtmp); + break; + } + switch ((poly_gender() != (int) mtmp->female) ? rn2(3) : 0) +#else + switch ((poly_gender() == 0) ? rn2(3) : 0) +#endif + { + case 2: + verbl_msg = "Hello, sailor."; + break; + case 1: + pline_msg = "comes on to you."; + break; + default: + pline_msg = "cajoles you."; + } + break; +#ifdef KOPS + case MS_ARREST: + if (mtmp->mpeaceful) + verbalize("Just the facts, %s.", + flags.female ? "Ma'am" : "Sir"); + else { + static const char * const arrest_msg[3] = { + "Anything you say can be used against you.", + "You're under arrest!", + "Stop in the name of the Law!", + }; + verbl_msg = arrest_msg[rn2(3)]; + } + break; +#endif + case MS_BRIBE: + if (mtmp->mpeaceful && !mtmp->mtame) { + (void) demon_talk(mtmp); + break; + } + /* fall through */ + case MS_CUSS: + if (!mtmp->mpeaceful) + cuss(mtmp); + break; + case MS_SPELL: + /* deliberately vague, since it's not actually casting any spell */ + pline_msg = "seems to mutter a cantrip."; + break; + case MS_NURSE: + if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) + verbl_msg = "Put that weapon away before you hurt someone!"; + else if (uarmc || uarm || uarmh || uarms || uarmg || uarmf) + verbl_msg = Role_if(PM_HEALER) ? + "Doc, I can't help you unless you cooperate." : + "Please undress so I can examine you."; +#ifdef TOURIST + else if (uarmu) + verbl_msg = "Take off your shirt, please."; +#endif + else verbl_msg = "Relax, this won't hurt a bit."; + break; + case MS_GUARD: +#ifndef GOLDOBJ + if (u.ugold) +#else + if (money_cnt(invent)) +#endif + verbl_msg = "Please drop that gold and follow me."; + else + verbl_msg = "Please follow me."; + break; + case MS_SOLDIER: + { + static const char * const soldier_foe_msg[3] = { + "Resistance is useless!", + "You're dog meat!", + "Surrender!", + }, * const soldier_pax_msg[3] = { + "What lousy pay we're getting here!", + "The food's not fit for Orcs!", + "My feet hurt, I've been on them all day!", + }; + verbl_msg = mtmp->mpeaceful ? soldier_pax_msg[rn2(3)] + : soldier_foe_msg[rn2(3)]; + } + break; + case MS_RIDER: + if (ptr == &mons[PM_DEATH] && !rn2(10)) + pline_msg = "is busy reading a copy of Sandman #8."; + else verbl_msg = "Who do you think you are, War?"; + break; + } + + if (pline_msg) pline("%s %s", Monnam(mtmp), pline_msg); + else if (verbl_msg) verbalize(verbl_msg); + return(1); +} + + +int +dotalk() +{ + int result; + boolean save_soundok = flags.soundok; + flags.soundok = 1; /* always allow sounds while chatting */ + result = dochat(); + flags.soundok = save_soundok; + return result; +} + +static int +dochat() +{ + register struct monst *mtmp; + register int tx,ty; + struct obj *otmp; + + if (is_silent(youmonst.data)) { + pline("As %s, you cannot speak.", an(youmonst.data->mname)); + return(0); + } + if (Strangled) { + You_cant("speak. You're choking!"); + return(0); + } + if (u.uswallow) { + pline("They won't hear you out there."); + return(0); + } + if (Underwater) { + Your("speech is unintelligible underwater."); + return(0); + } + + if (!Blind && (otmp = shop_object(u.ux, u.uy)) != (struct obj *)0) { + /* standing on something in a shop and chatting causes the shopkeeper + to describe the price(s). This can inhibit other chatting inside + a shop, but that shouldn't matter much. shop_object() returns an + object iff inside a shop and the shopkeeper is present and willing + (not angry) and able (not asleep) to speak and the position contains + any objects other than just gold. + */ + price_quote(otmp); + return(1); + } + + if (!getdir("Talk to whom? (in what direction)")) { + /* decided not to chat */ + return(0); + } + +#ifdef STEED + if (u.usteed && u.dz > 0) + return (domonnoise(u.usteed)); +#endif + if (u.dz) { + pline("They won't hear you %s there.", u.dz < 0 ? "up" : "down"); + return(0); + } + + if (u.dx == 0 && u.dy == 0) { +/* + * Let's not include this. It raises all sorts of questions: can you wear + * 2 helmets, 2 amulets, 3 pairs of gloves or 6 rings as a marilith, + * etc... --KAA + if (u.umonnum == PM_ETTIN) { + You("discover that your other head makes boring conversation."); + return(1); + } +*/ + pline("Talking to yourself is a bad habit for a dungeoneer."); + return(0); + } + + tx = u.ux+u.dx; ty = u.uy+u.dy; + mtmp = m_at(tx, ty); + + if (!mtmp || mtmp->mundetected || + mtmp->m_ap_type == M_AP_FURNITURE || + mtmp->m_ap_type == M_AP_OBJECT) + return(0); + + /* sleeping monsters won't talk, except priests (who wake up) */ + if ((!mtmp->mcanmove || mtmp->msleeping) && !mtmp->ispriest) { + /* If it is unseen, the player can't tell the difference between + not noticing him and just not existing, so skip the message. */ + if (canspotmon(mtmp)) + pline("%s seems not to notice you.", Monnam(mtmp)); + return(0); + } + + /* if this monster is waiting for something, prod it into action */ + mtmp->mstrategy &= ~STRAT_WAITMASK; + + if (mtmp->mtame && mtmp->meating) { + if (!canspotmon(mtmp)) + map_invisible(mtmp->mx, mtmp->my); + pline("%s is eating noisily.", Monnam(mtmp)); + return (0); + } + + return domonnoise(mtmp); +} + +#ifdef USER_SOUNDS + +extern void FDECL(play_usersound, (const char*, int)); + +typedef struct audio_mapping_rec { +#ifdef USER_SOUNDS_REGEX + struct re_pattern_buffer regex; +#else + char *pattern; +#endif + char *filename; + int volume; + struct audio_mapping_rec *next; +} audio_mapping; + +static audio_mapping *soundmap = 0; + +char* sounddir = "."; + +/* adds a sound file mapping, returns 0 on failure, 1 on success */ +int +add_sound_mapping(mapping) +const char *mapping; +{ + char text[256]; + char filename[256]; + char filespec[256]; + int volume; + + if (sscanf(mapping, "MESG \"%255[^\"]\"%*[\t ]\"%255[^\"]\" %d", + text, filename, &volume) == 3) { + const char *err; + audio_mapping *new_map; + + if (strlen(sounddir) + strlen(filename) > 254) { + raw_print("sound file name too long"); + return 0; + } + Sprintf(filespec, "%s/%s", sounddir, filename); + + if (can_read_file(filespec)) { + new_map = (audio_mapping *)alloc(sizeof(audio_mapping)); +#ifdef USER_SOUNDS_REGEX + new_map->regex.translate = 0; + new_map->regex.fastmap = 0; + new_map->regex.buffer = 0; + new_map->regex.allocated = 0; + new_map->regex.regs_allocated = REGS_FIXED; +#else + new_map->pattern = (char *)alloc(strlen(text) + 1); + Strcpy(new_map->pattern, text); +#endif + new_map->filename = strdup(filespec); + new_map->volume = volume; + new_map->next = soundmap; + +#ifdef USER_SOUNDS_REGEX + err = re_compile_pattern(text, strlen(text), &new_map->regex); +#else + err = 0; +#endif + if (err) { + raw_print(err); + free(new_map->filename); + free(new_map); + return 0; + } else { + soundmap = new_map; + } + } else { + Sprintf(text, "cannot read %.243s", filespec); + raw_print(text); + return 0; + } + } else { + raw_print("syntax error in SOUND"); + return 0; + } + + return 1; +} + +void +play_sound_for_message(msg) +const char* msg; +{ + audio_mapping* cursor = soundmap; + + while (cursor) { +#ifdef USER_SOUNDS_REGEX + if (re_search(&cursor->regex, msg, strlen(msg), 0, 9999, 0) >= 0) { +#else + if (pmatch(cursor->pattern, msg)) { +#endif + play_usersound(cursor->filename, cursor->volume); + } + cursor = cursor->next; + } +} + +#endif /* USER_SOUNDS */ + +#endif /* OVLB */ + +/*sounds.c*/ diff --git a/src/sp_lev.c b/src/sp_lev.c new file mode 100644 index 0000000..435609f --- /dev/null +++ b/src/sp_lev.c @@ -0,0 +1,2663 @@ +/* SCCS Id: @(#)sp_lev.c 3.4 2001/09/06 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the various functions that are related to the special + * levels. + * It contains also the special level loader. + * + */ + +#include "hack.h" +#include "dlb.h" +/* #define DEBUG */ /* uncomment to enable code debugging */ + +#ifdef DEBUG +# ifdef WIZARD +#define debugpline if (wizard) pline +# else +#define debugpline pline +# endif +#endif + +#include "sp_lev.h" +#include "rect.h" + +extern void FDECL(mkmap, (lev_init *)); + +STATIC_DCL void FDECL(get_room_loc, (schar *, schar *, struct mkroom *)); +STATIC_DCL void FDECL(get_free_room_loc, (schar *, schar *, struct mkroom *)); +STATIC_DCL void FDECL(create_trap, (trap *, struct mkroom *)); +STATIC_DCL int FDECL(noncoalignment, (ALIGNTYP_P)); +STATIC_DCL void FDECL(create_monster, (monster *, struct mkroom *)); +STATIC_DCL void FDECL(create_object, (object *, struct mkroom *)); +STATIC_DCL void FDECL(create_engraving, (engraving *,struct mkroom *)); +STATIC_DCL void FDECL(create_stairs, (stair *, struct mkroom *)); +STATIC_DCL void FDECL(create_altar, (altar *, struct mkroom *)); +STATIC_DCL void FDECL(create_gold, (gold *, struct mkroom *)); +STATIC_DCL void FDECL(create_feature, (int,int,struct mkroom *,int)); +STATIC_DCL boolean FDECL(search_door, (struct mkroom *, xchar *, xchar *, + XCHAR_P, int)); +STATIC_DCL void NDECL(fix_stair_rooms); +STATIC_DCL void FDECL(create_corridor, (corridor *)); + +STATIC_DCL boolean FDECL(create_subroom, (struct mkroom *, XCHAR_P, XCHAR_P, + XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P)); + +#define LEFT 1 +#define H_LEFT 2 +#define CENTER 3 +#define H_RIGHT 4 +#define RIGHT 5 + +#define TOP 1 +#define BOTTOM 5 + +#define sq(x) ((x)*(x)) + +#define XLIM 4 +#define YLIM 3 + +#define Fread (void)dlb_fread +#define Fgetc (schar)dlb_fgetc +#define New(type) (type *) alloc(sizeof(type)) +#define NewTab(type, size) (type **) alloc(sizeof(type *) * (unsigned)size) +#define Free(ptr) if(ptr) free((genericptr_t) (ptr)) + +static NEARDATA walk walklist[50]; +extern int min_rx, max_rx, min_ry, max_ry; /* from mkmap.c */ + +static char Map[COLNO][ROWNO]; +static char robjects[10], rloc_x[10], rloc_y[10], rmonst[10]; +static aligntyp ralign[3] = { AM_CHAOTIC, AM_NEUTRAL, AM_LAWFUL }; +static NEARDATA xchar xstart, ystart; +static NEARDATA char xsize, ysize; + +STATIC_DCL void FDECL(set_wall_property, (XCHAR_P,XCHAR_P,XCHAR_P,XCHAR_P,int)); +STATIC_DCL int NDECL(rnddoor); +STATIC_DCL int NDECL(rndtrap); +STATIC_DCL void FDECL(get_location, (schar *,schar *,int)); +STATIC_DCL void FDECL(sp_lev_shuffle, (char *,char *,int)); +STATIC_DCL void FDECL(light_region, (region *)); +STATIC_DCL void FDECL(load_common_data, (dlb *,int)); +STATIC_DCL void FDECL(load_one_monster, (dlb *,monster *)); +STATIC_DCL void FDECL(load_one_object, (dlb *,object *)); +STATIC_DCL void FDECL(load_one_engraving, (dlb *,engraving *)); +STATIC_DCL boolean FDECL(load_rooms, (dlb *)); +STATIC_DCL void FDECL(maze1xy, (coord *,int)); +STATIC_DCL boolean FDECL(load_maze, (dlb *)); +STATIC_DCL void FDECL(create_door, (room_door *, struct mkroom *)); +STATIC_DCL void FDECL(free_rooms,(room **, int)); +STATIC_DCL void FDECL(build_room, (room *, room*)); + +char *lev_message = 0; +lev_region *lregions = 0; +int num_lregions = 0; +lev_init init_lev; + +/* + * Make walls of the area (x1, y1, x2, y2) non diggable/non passwall-able + */ + +STATIC_OVL void +set_wall_property(x1,y1,x2,y2, prop) +xchar x1, y1, x2, y2; +int prop; +{ + register xchar x, y; + + for(y = y1; y <= y2; y++) + for(x = x1; x <= x2; x++) + if(IS_STWALL(levl[x][y].typ)) + levl[x][y].wall_info |= prop; +} + +/* + * Choose randomly the state (nodoor, open, closed or locked) for a door + */ +STATIC_OVL int +rnddoor() +{ + int i = 1 << rn2(5); + i >>= 1; + return i; +} + +/* + * Select a random trap + */ +STATIC_OVL int +rndtrap() +{ + int rtrap; + + do { + rtrap = rnd(TRAPNUM-1); + switch (rtrap) { + case HOLE: /* no random holes on special levels */ + case MAGIC_PORTAL: rtrap = NO_TRAP; + break; + case TRAPDOOR: if (!Can_dig_down(&u.uz)) rtrap = NO_TRAP; + break; + case LEVEL_TELEP: + case TELEP_TRAP: if (level.flags.noteleport) rtrap = NO_TRAP; + break; + case ROLLING_BOULDER_TRAP: + case ROCKTRAP: if (In_endgame(&u.uz)) rtrap = NO_TRAP; + break; + } + } while (rtrap == NO_TRAP); + return rtrap; +} + +/* + * Coordinates in special level files are handled specially: + * + * if x or y is -11, we generate a random coordinate. + * if x or y is between -1 and -10, we read one from the corresponding + * register (x0, x1, ... x9). + * if x or y is nonnegative, we convert it from relative to the local map + * to global coordinates. + * The "humidity" flag is used to insure that engravings aren't + * created underwater, or eels on dry land. + */ +#define DRY 0x1 +#define WET 0x2 + +STATIC_DCL boolean FDECL(is_ok_location, (SCHAR_P, SCHAR_P, int)); + +STATIC_OVL void +get_location(x, y, humidity) +schar *x, *y; +int humidity; +{ + int cpt = 0; + + if (*x >= 0) { /* normal locations */ + *x += xstart; + *y += ystart; + } else if (*x > -11) { /* special locations */ + *y = ystart + rloc_y[ - *y - 1]; + *x = xstart + rloc_x[ - *x - 1]; + } else { /* random location */ + do { + *x = xstart + rn2((int)xsize); + *y = ystart + rn2((int)ysize); + if (is_ok_location(*x,*y,humidity)) break; + } while (++cpt < 100); + if (cpt >= 100) { + register int xx, yy; + /* last try */ + for (xx = 0; xx < xsize; xx++) + for (yy = 0; yy < ysize; yy++) { + *x = xstart + xx; + *y = ystart + yy; + if (is_ok_location(*x,*y,humidity)) goto found_it; + } + panic("get_location: can't find a place!"); + } + } +found_it:; + + if (!isok(*x,*y)) { + impossible("get_location: (%d,%d) out of bounds", *x, *y); + *x = x_maze_max; *y = y_maze_max; + } +} + +STATIC_OVL boolean +is_ok_location(x, y, humidity) +register schar x, y; +register int humidity; +{ + register int typ; + + if (Is_waterlevel(&u.uz)) return TRUE; /* accept any spot */ + + if (humidity & DRY) { + typ = levl[x][y].typ; + if (typ == ROOM || typ == AIR || + typ == CLOUD || typ == ICE || typ == CORR) + return TRUE; + } + if (humidity & WET) { + if (is_pool(x,y) || is_lava(x,y)) + return TRUE; + } + return FALSE; +} + +/* + * Shuffle the registers for locations, objects or monsters + */ + +STATIC_OVL void +sp_lev_shuffle(list1, list2, n) +char list1[], list2[]; +int n; +{ + register int i, j; + register char k; + + for (i = n - 1; i > 0; i--) { + if ((j = rn2(i + 1)) == i) continue; + k = list1[j]; + list1[j] = list1[i]; + list1[i] = k; + if (list2) { + k = list2[j]; + list2[j] = list2[i]; + list2[i] = k; + } + } +} + +/* + * Get a relative position inside a room. + * negative values for x or y means RANDOM! + */ + +STATIC_OVL void +get_room_loc(x,y, croom) +schar *x, *y; +struct mkroom *croom; +{ + coord c; + + if (*x <0 && *y <0) { + if (somexy(croom, &c)) { + *x = c.x; + *y = c.y; + } else + panic("get_room_loc : can't find a place!"); + } else { + if (*x < 0) + *x = rn2(croom->hx - croom->lx + 1); + if (*y < 0) + *y = rn2(croom->hy - croom->ly + 1); + *x += croom->lx; + *y += croom->ly; + } +} + +/* + * Get a relative position inside a room. + * negative values for x or y means RANDOM! + */ + +STATIC_OVL void +get_free_room_loc(x,y, croom) +schar *x, *y; +struct mkroom *croom; +{ + schar try_x, try_y; + register int trycnt = 0; + + do { + try_x = *x, try_y = *y; + get_room_loc(&try_x, &try_y, croom); + } while (levl[try_x][try_y].typ != ROOM && ++trycnt <= 100); + + if (trycnt > 100) + panic("get_free_room_loc: can't find a place!"); + *x = try_x, *y = try_y; +} + +boolean +check_room(lowx, ddx, lowy, ddy, vault) +xchar *lowx, *ddx, *lowy, *ddy; +boolean vault; +{ + register int x,y,hix = *lowx + *ddx, hiy = *lowy + *ddy; + register struct rm *lev; + int xlim, ylim, ymax; + + xlim = XLIM + (vault ? 1 : 0); + ylim = YLIM + (vault ? 1 : 0); + + if (*lowx < 3) *lowx = 3; + if (*lowy < 2) *lowy = 2; + if (hix > COLNO-3) hix = COLNO-3; + if (hiy > ROWNO-3) hiy = ROWNO-3; +chk: + if (hix <= *lowx || hiy <= *lowy) return FALSE; + + /* check area around room (and make room smaller if necessary) */ + for (x = *lowx - xlim; x<= hix + xlim; x++) { + if(x <= 0 || x >= COLNO) continue; + y = *lowy - ylim; ymax = hiy + ylim; + if(y < 0) y = 0; + if(ymax >= ROWNO) ymax = (ROWNO-1); + lev = &levl[x][y]; + for (; y <= ymax; y++) { + if (lev++->typ) { +#ifdef DEBUG + if(!vault) + debugpline("strange area [%d,%d] in check_room.",x,y); +#endif + if (!rn2(3)) return FALSE; + if (x < *lowx) + *lowx = x + xlim + 1; + else + hix = x - xlim - 1; + if (y < *lowy) + *lowy = y + ylim + 1; + else + hiy = y - ylim - 1; + goto chk; + } + } + } + *ddx = hix - *lowx; + *ddy = hiy - *lowy; + return TRUE; +} + +/* + * Create a new room. + * This is still very incomplete... + */ + +boolean +create_room(x,y,w,h,xal,yal,rtype,rlit) +xchar x,y; +xchar w,h; +xchar xal,yal; +xchar rtype, rlit; +{ + xchar xabs, yabs; + int wtmp, htmp, xaltmp, yaltmp, xtmp, ytmp; + NhRect *r1 = 0, r2; + int trycnt = 0; + boolean vault = FALSE; + int xlim = XLIM, ylim = YLIM; + + if (rtype == -1) /* Is the type random ? */ + rtype = OROOM; + + if (rtype == VAULT) { + vault = TRUE; + xlim++; + ylim++; + } + + /* on low levels the room is lit (usually) */ + /* some other rooms may require lighting */ + + /* is light state random ? */ + if (rlit == -1) + rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE; + + /* + * Here we will try to create a room. If some parameters are + * random we are willing to make several try before we give + * it up. + */ + do { + xchar xborder, yborder; + wtmp = w; htmp = h; + xtmp = x; ytmp = y; + xaltmp = xal; yaltmp = yal; + + /* First case : a totaly random room */ + + if((xtmp < 0 && ytmp <0 && wtmp < 0 && xaltmp < 0 && + yaltmp < 0) || vault) { + xchar hx, hy, lx, ly, dx, dy; + r1 = rnd_rect(); /* Get a random rectangle */ + + if (!r1) { /* No more free rectangles ! */ +#ifdef DEBUG + debugpline("No more rects..."); +#endif + return FALSE; + } + hx = r1->hx; + hy = r1->hy; + lx = r1->lx; + ly = r1->ly; + if (vault) + dx = dy = 1; + else { + dx = 2 + rn2((hx-lx > 28) ? 12 : 8); + dy = 2 + rn2(4); + if(dx*dy > 50) + dy = 50/dx; + } + xborder = (lx > 0 && hx < COLNO -1) ? 2*xlim : xlim+1; + yborder = (ly > 0 && hy < ROWNO -1) ? 2*ylim : ylim+1; + if(hx-lx < dx + 3 + xborder || + hy-ly < dy + 3 + yborder) { + r1 = 0; + continue; + } + xabs = lx + (lx > 0 ? xlim : 3) + + rn2(hx - (lx>0?lx : 3) - dx - xborder + 1); + yabs = ly + (ly > 0 ? ylim : 2) + + rn2(hy - (ly>0?ly : 2) - dy - yborder + 1); + if (ly == 0 && hy >= (ROWNO-1) && + (!nroom || !rn2(nroom)) && (yabs+dy > ROWNO/2)) { + yabs = rn1(3, 2); + if(nroom < 4 && dy>1) dy--; + } + if (!check_room(&xabs, &dx, &yabs, &dy, vault)) { + r1 = 0; + continue; + } + wtmp = dx+1; + htmp = dy+1; + r2.lx = xabs-1; r2.ly = yabs-1; + r2.hx = xabs + wtmp; + r2.hy = yabs + htmp; + } else { /* Only some parameters are random */ + int rndpos = 0; + if (xtmp < 0 && ytmp < 0) { /* Position is RANDOM */ + xtmp = rnd(5); + ytmp = rnd(5); + rndpos = 1; + } + if (wtmp < 0 || htmp < 0) { /* Size is RANDOM */ + wtmp = rn1(15, 3); + htmp = rn1(8, 2); + } + if (xaltmp == -1) /* Horizontal alignment is RANDOM */ + xaltmp = rnd(3); + if (yaltmp == -1) /* Vertical alignment is RANDOM */ + yaltmp = rnd(3); + + /* Try to generate real (absolute) coordinates here! */ + + xabs = (((xtmp-1) * COLNO) / 5) + 1; + yabs = (((ytmp-1) * ROWNO) / 5) + 1; + switch (xaltmp) { + case LEFT: + break; + case RIGHT: + xabs += (COLNO / 5) - wtmp; + break; + case CENTER: + xabs += ((COLNO / 5) - wtmp) / 2; + break; + } + switch (yaltmp) { + case TOP: + break; + case BOTTOM: + yabs += (ROWNO / 5) - htmp; + break; + case CENTER: + yabs += ((ROWNO / 5) - htmp) / 2; + break; + } + + if (xabs + wtmp - 1 > COLNO - 2) + xabs = COLNO - wtmp - 3; + if (xabs < 2) + xabs = 2; + if (yabs + htmp - 1> ROWNO - 2) + yabs = ROWNO - htmp - 3; + if (yabs < 2) + yabs = 2; + + /* Try to find a rectangle that fit our room ! */ + + r2.lx = xabs-1; r2.ly = yabs-1; + r2.hx = xabs + wtmp + rndpos; + r2.hy = yabs + htmp + rndpos; + r1 = get_rect(&r2); + } + } while (++trycnt <= 100 && !r1); + if (!r1) { /* creation of room failed ? */ + return FALSE; + } + split_rects(r1, &r2); + + if (!vault) { + smeq[nroom] = nroom; + add_room(xabs, yabs, xabs+wtmp-1, yabs+htmp-1, + rlit, rtype, FALSE); + } else { + rooms[nroom].lx = xabs; + rooms[nroom].ly = yabs; + } + return TRUE; +} + +/* + * Create a subroom in room proom at pos x,y with width w & height h. + * x & y are relative to the parent room. + */ + +STATIC_OVL boolean +create_subroom(proom, x, y, w, h, rtype, rlit) +struct mkroom *proom; +xchar x,y; +xchar w,h; +xchar rtype, rlit; +{ + xchar width, height; + + width = proom->hx - proom->lx + 1; + height = proom->hy - proom->ly + 1; + + /* There is a minimum size for the parent room */ + if (width < 4 || height < 4) + return FALSE; + + /* Check for random position, size, etc... */ + + if (w == -1) + w = rnd(width - 3); + if (h == -1) + h = rnd(height - 3); + if (x == -1) + x = rnd(width - w - 1) - 1; + if (y == -1) + y = rnd(height - h - 1) - 1; + if (x == 1) + x = 0; + if (y == 1) + y = 0; + if ((x + w + 1) == width) + x++; + if ((y + h + 1) == height) + y++; + if (rtype == -1) + rtype = OROOM; + if (rlit == -1) + rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) ? TRUE : FALSE; + add_subroom(proom, proom->lx + x, proom->ly + y, + proom->lx + x + w - 1, proom->ly + y + h - 1, + rlit, rtype, FALSE); + return TRUE; +} + +/* + * Create a new door in a room. + * It's placed on a wall (north, south, east or west). + */ + +STATIC_OVL void +create_door(dd, broom) +room_door *dd; +struct mkroom *broom; +{ + int x, y; + int trycnt = 0; + + if (dd->secret == -1) + dd->secret = rn2(2); + + if (dd->mask == -1) { + /* is it a locked door, closed, or a doorway? */ + if (!dd->secret) { + if(!rn2(3)) { + if(!rn2(5)) + dd->mask = D_ISOPEN; + else if(!rn2(6)) + dd->mask = D_LOCKED; + else + dd->mask = D_CLOSED; + if (dd->mask != D_ISOPEN && !rn2(25)) + dd->mask |= D_TRAPPED; + } else + dd->mask = D_NODOOR; + } else { + if(!rn2(5)) dd->mask = D_LOCKED; + else dd->mask = D_CLOSED; + + if(!rn2(20)) dd->mask |= D_TRAPPED; + } + } + + do { + register int dwall, dpos; + + dwall = dd->wall; + if (dwall == -1) /* The wall is RANDOM */ + dwall = 1 << rn2(4); + + dpos = dd->pos; + if (dpos == -1) /* The position is RANDOM */ + dpos = rn2((dwall == W_WEST || dwall == W_EAST) ? + (broom->hy - broom->ly) : (broom->hx - broom->lx)); + + /* Convert wall and pos into an absolute coordinate! */ + + switch (dwall) { + case W_NORTH: + y = broom->ly - 1; + x = broom->lx + dpos; + break; + case W_SOUTH: + y = broom->hy + 1; + x = broom->lx + dpos; + break; + case W_WEST: + x = broom->lx - 1; + y = broom->ly + dpos; + break; + case W_EAST: + x = broom->hx + 1; + y = broom->ly + dpos; + break; + default: + x = y = 0; + panic("create_door: No wall for door!"); + break; + } + if (okdoor(x,y)) + break; + } while (++trycnt <= 100); + if (trycnt > 100) { + impossible("create_door: Can't find a proper place!"); + return; + } + add_door(x,y,broom); + levl[x][y].typ = (dd->secret ? SDOOR : DOOR); + levl[x][y].doormask = dd->mask; +} + +/* + * Create a secret door in croom on any one of the specified walls. + */ +void +create_secret_door(croom, walls) + struct mkroom *croom; + xchar walls; /* any of W_NORTH | W_SOUTH | W_EAST | W_WEST (or W_ANY) */ +{ + xchar sx, sy; /* location of the secret door */ + int count; + + for(count = 0; count < 100; count++) { + sx = rn1(croom->hx - croom->lx + 1, croom->lx); + sy = rn1(croom->hy - croom->ly + 1, croom->ly); + + switch(rn2(4)) { + case 0: /* top */ + if(!(walls & W_NORTH)) continue; + sy = croom->ly-1; break; + case 1: /* bottom */ + if(!(walls & W_SOUTH)) continue; + sy = croom->hy+1; break; + case 2: /* left */ + if(!(walls & W_EAST)) continue; + sx = croom->lx-1; break; + case 3: /* right */ + if(!(walls & W_WEST)) continue; + sx = croom->hx+1; break; + } + + if(okdoor(sx,sy)) { + levl[sx][sy].typ = SDOOR; + levl[sx][sy].doormask = D_CLOSED; + add_door(sx,sy,croom); + return; + } + } + + impossible("couldn't create secret door on any walls 0x%x", walls); +} + +/* + * Create a trap in a room. + */ + +STATIC_OVL void +create_trap(t,croom) +trap *t; +struct mkroom *croom; +{ + schar x,y; + coord tm; + + if (rn2(100) < t->chance) { + x = t->x; + y = t->y; + if (croom) + get_free_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + tm.x = x; + tm.y = y; + + mktrap(t->type, 1, (struct mkroom*) 0, &tm); + } +} + +/* + * Create a monster in a room. + */ + +STATIC_OVL int +noncoalignment(alignment) +aligntyp alignment; +{ + int k; + + k = rn2(2); + if (!alignment) + return(k ? -1 : 1); + return(k ? -alignment : 0); +} + +STATIC_OVL void +create_monster(m,croom) +monster *m; +struct mkroom *croom; +{ + struct monst *mtmp; + schar x, y; + char class; + aligntyp amask; + coord cc; + struct permonst *pm; + unsigned g_mvflags; + + if (rn2(100) < m->chance) { + + if (m->class >= 0) + class = (char) def_char_to_monclass((char)m->class); + else if (m->class > -11) + class = (char) def_char_to_monclass(rmonst[- m->class - 1]); + else + class = 0; + + if (class == MAXMCLASSES) + panic("create_monster: unknown monster class '%c'", m->class); + + amask = (m->align == AM_SPLEV_CO) ? + Align2amask(u.ualignbase[A_ORIGINAL]) : + (m->align == AM_SPLEV_NONCO) ? + Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) : + (m->align <= -11) ? induced_align(80) : + (m->align < 0 ? ralign[-m->align-1] : m->align); + + if (!class) + pm = (struct permonst *) 0; + else if (m->id != NON_PM) { + pm = &mons[m->id]; + g_mvflags = (unsigned) mvitals[monsndx(pm)].mvflags; + if ((pm->geno & G_UNIQ) && (g_mvflags & G_EXTINCT)) + goto m_done; + else if (g_mvflags & G_GONE) /* genocided or extinct */ + pm = (struct permonst *) 0; /* make random monster */ + } else { + pm = mkclass(class,G_NOGEN); + /* if we can't get a specific monster type (pm == 0) then the + class has been genocided, so settle for a random monster */ + } + if (In_mines(&u.uz) && pm && your_race(pm) && + (Race_if(PM_DWARF) || Race_if(PM_GNOME)) && rn2(3)) + pm = (struct permonst *) 0; + + x = m->x; + y = m->y; + if (croom) + get_room_loc(&x, &y, croom); + else { + if (!pm || !is_swimmer(pm)) + get_location(&x, &y, DRY); + else if (pm->mlet == S_EEL) + get_location(&x, &y, WET); + else + get_location(&x, &y, DRY|WET); + } + /* try to find a close place if someone else is already there */ + if (MON_AT(x,y) && enexto(&cc, x, y, pm)) + x = cc.x, y = cc.y; + + if(m->align != -12) + mtmp = mk_roamer(pm, Amask2align(amask), x, y, m->peaceful); + else if(PM_ARCHEOLOGIST <= m->id && m->id <= PM_WIZARD) + mtmp = mk_mplayer(pm, x, y, FALSE); + else mtmp = makemon(pm, x, y, NO_MM_FLAGS); + + if (mtmp) { + /* handle specific attributes for some special monsters */ + if (m->name.str) mtmp = christen_monst(mtmp, m->name.str); + + /* + * This is currently hardwired for mimics only. It should + * eventually be expanded. + */ + if (m->appear_as.str && mtmp->data->mlet == S_MIMIC) { + int i; + + switch (m->appear) { + case M_AP_NOTHING: + impossible( + "create_monster: mon has an appearance, \"%s\", but no type", + m->appear_as.str); + break; + + case M_AP_FURNITURE: + for (i = 0; i < MAXPCHARS; i++) + if (!strcmp(defsyms[i].explanation, + m->appear_as.str)) + break; + if (i == MAXPCHARS) { + impossible( + "create_monster: can't find feature \"%s\"", + m->appear_as.str); + } else { + mtmp->m_ap_type = M_AP_FURNITURE; + mtmp->mappearance = i; + } + break; + + case M_AP_OBJECT: + for (i = 0; i < NUM_OBJECTS; i++) + if (OBJ_NAME(objects[i]) && + !strcmp(OBJ_NAME(objects[i]),m->appear_as.str)) + break; + if (i == NUM_OBJECTS) { + impossible( + "create_monster: can't find object \"%s\"", + m->appear_as.str); + } else { + mtmp->m_ap_type = M_AP_OBJECT; + mtmp->mappearance = i; + } + break; + + case M_AP_MONSTER: + /* note: mimics don't appear as monsters! */ + /* (but chameleons can :-) */ + default: + impossible( + "create_monster: unimplemented mon appear type [%d,\"%s\"]", + m->appear, m->appear_as.str); + break; + } + if (does_block(x, y, &levl[x][y])) + block_point(x, y); + } + + if (m->peaceful >= 0) { + mtmp->mpeaceful = m->peaceful; + /* changed mpeaceful again; have to reset malign */ + set_malign(mtmp); + } + if (m->asleep >= 0) { +#ifdef UNIXPC + /* optimizer bug strikes again */ + if (m->asleep) + mtmp->msleeping = 1; + else + mtmp->msleeping = 0; +#else + mtmp->msleeping = m->asleep; +#endif + } + } + + } /* if (rn2(100) < m->chance) */ + m_done: + Free(m->name.str); + Free(m->appear_as.str); +} + +/* + * Create an object in a room. + */ + +STATIC_OVL void +create_object(o,croom) +object *o; +struct mkroom *croom; +{ + struct obj *otmp; + schar x, y; + char c; + boolean named; /* has a name been supplied in level description? */ + + if (rn2(100) < o->chance) { + named = o->name.str ? TRUE : FALSE; + + x = o->x; y = o->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + if (o->class >= 0) + c = o->class; + else if (o->class > -11) + c = robjects[ -(o->class+1)]; + else + c = 0; + + if (!c) + otmp = mkobj_at(RANDOM_CLASS, x, y, !named); + else if (o->id != -1) + otmp = mksobj_at(o->id, x, y, TRUE, !named); + else { + /* + * The special levels are compiled with the default "text" object + * class characters. We must convert them to the internal format. + */ + char oclass = (char) def_char_to_objclass(c); + + if (oclass == MAXOCLASSES) + panic("create_object: unexpected object class '%c'",c); + + /* KMH -- Create piles of gold properly */ + if (oclass == COIN_CLASS) + otmp = mkgold(0L, x, y); + else + otmp = mkobj_at(oclass, x, y, !named); + } + + if (o->spe != -127) /* That means NOT RANDOM! */ + otmp->spe = (schar)o->spe; + + switch (o->curse_state) { + case 1: bless(otmp); break; /* BLESSED */ + case 2: unbless(otmp); uncurse(otmp); break; /* uncursed */ + case 3: curse(otmp); break; /* CURSED */ + default: break; /* Otherwise it's random and we're happy + * with what mkobj gave us! */ + } + + /* corpsenm is "empty" if -1, random if -2, otherwise specific */ + if (o->corpsenm == NON_PM - 1) otmp->corpsenm = rndmonnum(); + else if (o->corpsenm != NON_PM) otmp->corpsenm = o->corpsenm; + + /* assume we wouldn't be given an egg corpsenm unless it was + hatchable */ + if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) { + if (dead_species(otmp->otyp, TRUE)) + kill_egg(otmp); /* make sure nothing hatches */ + else + attach_egg_hatch_timeout(otmp); /* attach new hatch timeout */ + } + + if (named) + otmp = oname(otmp, o->name.str); + + switch(o->containment) { + static struct obj *container = 0; + + /* contents */ + case 1: + if (!container) { + impossible("create_object: no container"); + break; + } + remove_object(otmp); + (void) add_to_container(container, otmp); + goto o_done; /* don't stack, but do other cleanup */ + /* container */ + case 2: + delete_contents(otmp); + container = otmp; + break; + /* nothing */ + case 0: break; + + default: impossible("containment type %d?", (int) o->containment); + } + + /* Medusa level special case: statues are petrified monsters, so they + * are not stone-resistant and have monster inventory. They also lack + * other contents, but that can be specified as an empty container. + */ + if (o->id == STATUE && Is_medusa_level(&u.uz) && + o->corpsenm == NON_PM) { + struct monst *was; + struct obj *obj; + int wastyp; + + /* Named random statues are of player types, and aren't stone- + * resistant (if they were, we'd have to reset the name as well as + * setting corpsenm). + */ + for (wastyp = otmp->corpsenm; ; wastyp = rndmonnum()) { + /* makemon without rndmonst() might create a group */ + was = makemon(&mons[wastyp], 0, 0, NO_MM_FLAGS); + if (!resists_ston(was)) break; + mongone(was); + } + otmp->corpsenm = wastyp; + while(was->minvent) { + obj = was->minvent; + obj->owornmask = 0; + obj_extract_self(obj); + (void) add_to_container(otmp, obj); + } + otmp->owt = weight(otmp); + mongone(was); + } + + stackobj(otmp); + + } /* if (rn2(100) < o->chance) */ + o_done: + Free(o->name.str); +} + +/* + * Randomly place a specific engraving, then release its memory. + */ +STATIC_OVL void +create_engraving(e, croom) +engraving *e; +struct mkroom *croom; +{ + xchar x, y; + + x = e->x, y = e->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + make_engr_at(x, y, e->engr.str, 0L, e->etype); + free((genericptr_t) e->engr.str); +} + +/* + * Create stairs in a room. + * + */ + +STATIC_OVL void +create_stairs(s,croom) +stair *s; +struct mkroom *croom; +{ + schar x,y; + + x = s->x; y = s->y; + get_free_room_loc(&x, &y, croom); + mkstairs(x,y,(char)s->up, croom); +} + +/* + * Create an altar in a room. + */ + +STATIC_OVL void +create_altar(a, croom) + altar *a; + struct mkroom *croom; +{ + schar sproom,x,y; + aligntyp amask; + boolean croom_is_temple = TRUE; + int oldtyp; + + x = a->x; y = a->y; + + if (croom) { + get_free_room_loc(&x, &y, croom); + if (croom->rtype != TEMPLE) + croom_is_temple = FALSE; + } else { + get_location(&x, &y, DRY); + if ((sproom = (schar) *in_rooms(x, y, TEMPLE)) != 0) + croom = &rooms[sproom - ROOMOFFSET]; + else + croom_is_temple = FALSE; + } + + /* check for existing features */ + oldtyp = levl[x][y].typ; + if (oldtyp == STAIRS || oldtyp == LADDER) + return; + + a->x = x; + a->y = y; + + /* Is the alignment random ? + * If so, it's an 80% chance that the altar will be co-aligned. + * + * The alignment is encoded as amask values instead of alignment + * values to avoid conflicting with the rest of the encoding, + * shared by many other parts of the special level code. + */ + + amask = (a->align == AM_SPLEV_CO) ? + Align2amask(u.ualignbase[A_ORIGINAL]) : + (a->align == AM_SPLEV_NONCO) ? + Align2amask(noncoalignment(u.ualignbase[A_ORIGINAL])) : + (a->align == -11) ? induced_align(80) : + (a->align < 0 ? ralign[-a->align-1] : a->align); + + levl[x][y].typ = ALTAR; + levl[x][y].altarmask = amask; + + if (a->shrine < 0) a->shrine = rn2(2); /* handle random case */ + + if (oldtyp == FOUNTAIN) + level.flags.nfountains--; + else if (oldtyp == SINK) + level.flags.nsinks--; + + if (!croom_is_temple || !a->shrine) return; + + if (a->shrine) { /* Is it a shrine or sanctum? */ + priestini(&u.uz, croom, x, y, (a->shrine > 1)); + levl[x][y].altarmask |= AM_SHRINE; + level.flags.has_temple = TRUE; + } +} + +/* + * Create a gold pile in a room. + */ + +STATIC_OVL void +create_gold(g,croom) +gold *g; +struct mkroom *croom; +{ + schar x,y; + + x = g->x; y= g->y; + if (croom) + get_room_loc(&x, &y, croom); + else + get_location(&x, &y, DRY); + + if (g->amount == -1) + g->amount = rnd(200); + (void) mkgold((long) g->amount, x, y); +} + +/* + * Create a feature (e.g a fountain) in a room. + */ + +STATIC_OVL void +create_feature(fx, fy, croom, typ) +int fx, fy; +struct mkroom *croom; +int typ; +{ + schar x,y; + int trycnt = 0; + + x = fx; y = fy; + if (croom) { + if (x < 0 && y < 0) + do { + x = -1; y = -1; + get_room_loc(&x, &y, croom); + } while (++trycnt <= 200 && occupied(x,y)); + else + get_room_loc(&x, &y, croom); + if(trycnt > 200) + return; + } else { + get_location(&x, &y, DRY); + } + /* Don't cover up an existing feature (particularly randomly + placed stairs). However, if the _same_ feature is already + here, it came from the map drawing and we still need to + update the special counters. */ + if (IS_FURNITURE(levl[x][y].typ) && levl[x][y].typ != typ) + return; + + levl[x][y].typ = typ; + if (typ == FOUNTAIN) + level.flags.nfountains++; + else if (typ == SINK) + level.flags.nsinks++; +} + +/* + * Search for a door in a room on a specified wall. + */ + +STATIC_OVL boolean +search_door(croom,x,y,wall,cnt) +struct mkroom *croom; +xchar *x, *y; +xchar wall; +int cnt; +{ + int dx, dy; + int xx,yy; + + switch(wall) { + case W_NORTH: + dy = 0; dx = 1; + xx = croom->lx; + yy = croom->hy + 1; + break; + case W_SOUTH: + dy = 0; dx = 1; + xx = croom->lx; + yy = croom->ly - 1; + break; + case W_EAST: + dy = 1; dx = 0; + xx = croom->hx + 1; + yy = croom->ly; + break; + case W_WEST: + dy = 1; dx = 0; + xx = croom->lx - 1; + yy = croom->ly; + break; + default: + dx = dy = xx = yy = 0; + panic("search_door: Bad wall!"); + break; + } + while (xx <= croom->hx+1 && yy <= croom->hy+1) { + if (IS_DOOR(levl[xx][yy].typ) || levl[xx][yy].typ == SDOOR) { + *x = xx; + *y = yy; + if (cnt-- <= 0) + return TRUE; + } + xx += dx; + yy += dy; + } + return FALSE; +} + +/* + * Dig a corridor between two points. + */ + +boolean +dig_corridor(org,dest,nxcor,ftyp,btyp) +coord *org, *dest; +boolean nxcor; +schar ftyp, btyp; +{ + register int dx=0, dy=0, dix, diy, cct; + register struct rm *crm; + register int tx, ty, xx, yy; + + xx = org->x; yy = org->y; + tx = dest->x; ty = dest->y; + if (xx <= 0 || yy <= 0 || tx <= 0 || ty <= 0 || + xx > COLNO-1 || tx > COLNO-1 || + yy > ROWNO-1 || ty > ROWNO-1) { +#ifdef DEBUG + debugpline("dig_corridor: bad coords : (%d,%d) (%d,%d).", + xx,yy,tx,ty); +#endif + return FALSE; + } + if (tx > xx) dx = 1; + else if (ty > yy) dy = 1; + else if (tx < xx) dx = -1; + else dy = -1; + + xx -= dx; + yy -= dy; + cct = 0; + while(xx != tx || yy != ty) { + /* loop: dig corridor at [xx,yy] and find new [xx,yy] */ + if(cct++ > 500 || (nxcor && !rn2(35))) + return FALSE; + + xx += dx; + yy += dy; + + if(xx >= COLNO-1 || xx <= 0 || yy <= 0 || yy >= ROWNO-1) + return FALSE; /* impossible */ + + crm = &levl[xx][yy]; + if(crm->typ == btyp) { + if(ftyp != CORR || rn2(100)) { + crm->typ = ftyp; + if(nxcor && !rn2(50)) + (void) mksobj_at(BOULDER, xx, yy, TRUE, FALSE); + } else { + crm->typ = SCORR; + } + } else + if(crm->typ != ftyp && crm->typ != SCORR) { + /* strange ... */ + return FALSE; + } + + /* find next corridor position */ + dix = abs(xx-tx); + diy = abs(yy-ty); + + /* do we have to change direction ? */ + if(dy && dix > diy) { + register int ddx = (xx > tx) ? -1 : 1; + + crm = &levl[xx+ddx][yy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { + dx = ddx; + dy = 0; + continue; + } + } else if(dx && diy > dix) { + register int ddy = (yy > ty) ? -1 : 1; + + crm = &levl[xx][yy+ddy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) { + dy = ddy; + dx = 0; + continue; + } + } + + /* continue straight on? */ + crm = &levl[xx+dx][yy+dy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) + continue; + + /* no, what must we do now?? */ + if(dx) { + dx = 0; + dy = (ty < yy) ? -1 : 1; + } else { + dy = 0; + dx = (tx < xx) ? -1 : 1; + } + crm = &levl[xx+dx][yy+dy]; + if(crm->typ == btyp || crm->typ == ftyp || crm->typ == SCORR) + continue; + dy = -dy; + dx = -dx; + } + return TRUE; +} + +/* + * Disgusting hack: since special levels have their rooms filled before + * sorting the rooms, we have to re-arrange the speed values upstairs_room + * and dnstairs_room after the rooms have been sorted. On normal levels, + * stairs don't get created until _after_ sorting takes place. + */ +STATIC_OVL void +fix_stair_rooms() +{ + int i; + struct mkroom *croom; + + if(xdnstair && + !((dnstairs_room->lx <= xdnstair && xdnstair <= dnstairs_room->hx) && + (dnstairs_room->ly <= ydnstair && ydnstair <= dnstairs_room->hy))) { + for(i=0; i < nroom; i++) { + croom = &rooms[i]; + if((croom->lx <= xdnstair && xdnstair <= croom->hx) && + (croom->ly <= ydnstair && ydnstair <= croom->hy)) { + dnstairs_room = croom; + break; + } + } + if(i == nroom) + panic("Couldn't find dnstair room in fix_stair_rooms!"); + } + if(xupstair && + !((upstairs_room->lx <= xupstair && xupstair <= upstairs_room->hx) && + (upstairs_room->ly <= yupstair && yupstair <= upstairs_room->hy))) { + for(i=0; i < nroom; i++) { + croom = &rooms[i]; + if((croom->lx <= xupstair && xupstair <= croom->hx) && + (croom->ly <= yupstair && yupstair <= croom->hy)) { + upstairs_room = croom; + break; + } + } + if(i == nroom) + panic("Couldn't find upstair room in fix_stair_rooms!"); + } +} + +/* + * Corridors always start from a door. But it can end anywhere... + * Basically we search for door coordinates or for endpoints coordinates + * (from a distance). + */ + +STATIC_OVL void +create_corridor(c) +corridor *c; +{ + coord org, dest; + + if (c->src.room == -1) { + sort_rooms(); + fix_stair_rooms(); + makecorridors(); + return; + } + + if( !search_door(&rooms[c->src.room], &org.x, &org.y, c->src.wall, + c->src.door)) + return; + + if (c->dest.room != -1) { + if(!search_door(&rooms[c->dest.room], &dest.x, &dest.y, + c->dest.wall, c->dest.door)) + return; + switch(c->src.wall) { + case W_NORTH: org.y--; break; + case W_SOUTH: org.y++; break; + case W_WEST: org.x--; break; + case W_EAST: org.x++; break; + } + switch(c->dest.wall) { + case W_NORTH: dest.y--; break; + case W_SOUTH: dest.y++; break; + case W_WEST: dest.x--; break; + case W_EAST: dest.x++; break; + } + (void) dig_corridor(&org, &dest, FALSE, CORR, STONE); + } +} + + +/* + * Fill a room (shop, zoo, etc...) with appropriate stuff. + */ + +void +fill_room(croom, prefilled) +struct mkroom *croom; +boolean prefilled; +{ + if (!croom || croom->rtype == OROOM) + return; + + if (!prefilled) { + int x,y; + + /* Shop ? */ + if (croom->rtype >= SHOPBASE) { + stock_room(croom->rtype - SHOPBASE, croom); + level.flags.has_shop = TRUE; + return; + } + + switch (croom->rtype) { + case VAULT: + for (x=croom->lx;x<=croom->hx;x++) + for (y=croom->ly;y<=croom->hy;y++) + (void) mkgold((long)rn1(abs(depth(&u.uz))*100, 51), x, y); + break; + case COURT: + case ZOO: + case BEEHIVE: + case MORGUE: + case BARRACKS: + fill_zoo(croom); + break; + } + } + switch (croom->rtype) { + case VAULT: + level.flags.has_vault = TRUE; + break; + case ZOO: + level.flags.has_zoo = TRUE; + break; + case COURT: + level.flags.has_court = TRUE; + break; + case MORGUE: + level.flags.has_morgue = TRUE; + break; + case BEEHIVE: + level.flags.has_beehive = TRUE; + break; + case BARRACKS: + level.flags.has_barracks = TRUE; + break; + case TEMPLE: + level.flags.has_temple = TRUE; + break; + case SWAMP: + level.flags.has_swamp = TRUE; + break; + } +} + +STATIC_OVL void +free_rooms(ro, n) +room **ro; +int n; +{ + short j; + room *r; + + while(n--) { + r = ro[n]; + Free(r->name); + Free(r->parent); + if ((j = r->ndoor) != 0) { + while(j--) + Free(r->doors[j]); + Free(r->doors); + } + if ((j = r->nstair) != 0) { + while(j--) + Free(r->stairs[j]); + Free(r->stairs); + } + if ((j = r->naltar) != 0) { + while (j--) + Free(r->altars[j]); + Free(r->altars); + } + if ((j = r->nfountain) != 0) { + while(j--) + Free(r->fountains[j]); + Free(r->fountains); + } + if ((j = r->nsink) != 0) { + while(j--) + Free(r->sinks[j]); + Free(r->sinks); + } + if ((j = r->npool) != 0) { + while(j--) + Free(r->pools[j]); + Free(r->pools); + } + if ((j = r->ntrap) != 0) { + while (j--) + Free(r->traps[j]); + Free(r->traps); + } + if ((j = r->nmonster) != 0) { + while (j--) + Free(r->monsters[j]); + Free(r->monsters); + } + if ((j = r->nobject) != 0) { + while (j--) + Free(r->objects[j]); + Free(r->objects); + } + if ((j = r->ngold) != 0) { + while(j--) + Free(r->golds[j]); + Free(r->golds); + } + if ((j = r->nengraving) != 0) { + while (j--) + Free(r->engravings[j]); + Free(r->engravings); + } + Free(r); + } + Free(ro); +} + +STATIC_OVL void +build_room(r, pr) +room *r, *pr; +{ + boolean okroom; + struct mkroom *aroom; + short i; + xchar rtype = (!r->chance || rn2(100) < r->chance) ? r->rtype : OROOM; + + if(pr) { + aroom = &subrooms[nsubroom]; + okroom = create_subroom(pr->mkr, r->x, r->y, r->w, r->h, + rtype, r->rlit); + } else { + aroom = &rooms[nroom]; + okroom = create_room(r->x, r->y, r->w, r->h, r->xalign, + r->yalign, rtype, r->rlit); + r->mkr = aroom; + } + + if (okroom) { + /* Create subrooms if necessary... */ + for(i=0; i < r->nsubroom; i++) + build_room(r->subrooms[i], r); + /* And now we can fill the room! */ + + /* Priority to the stairs */ + + for(i=0; i nstair; i++) + create_stairs(r->stairs[i], aroom); + + /* Then to the various elements (sinks, etc..) */ + for(i = 0; insink; i++) + create_feature(r->sinks[i]->x, r->sinks[i]->y, aroom, SINK); + for(i = 0; inpool; i++) + create_feature(r->pools[i]->x, r->pools[i]->y, aroom, POOL); + for(i = 0; infountain; i++) + create_feature(r->fountains[i]->x, r->fountains[i]->y, + aroom, FOUNTAIN); + for(i = 0; inaltar; i++) + create_altar(r->altars[i], aroom); + for(i = 0; indoor; i++) + create_door(r->doors[i], aroom); + + /* The traps */ + for(i = 0; intrap; i++) + create_trap(r->traps[i], aroom); + + /* The monsters */ + for(i = 0; inmonster; i++) + create_monster(r->monsters[i], aroom); + + /* The objects */ + for(i = 0; inobject; i++) + create_object(r->objects[i], aroom); + + /* The gold piles */ + for(i = 0; ingold; i++) + create_gold(r->golds[i], aroom); + + /* The engravings */ + for (i = 0; i < r->nengraving; i++) + create_engraving(r->engravings[i], aroom); + +#ifdef SPECIALIZATION + topologize(aroom,FALSE); /* set roomno */ +#else + topologize(aroom); /* set roomno */ +#endif + /* MRS - 07/04/91 - This is temporary but should result + * in proper filling of shops, etc. + * DLC - this can fail if corridors are added to this room + * at a later point. Currently no good way to fix this. + */ + if(aroom->rtype != OROOM && r->filled) fill_room(aroom, FALSE); + } +} + +/* + * set lighting in a region that will not become a room. + */ +STATIC_OVL void +light_region(tmpregion) + region *tmpregion; +{ + register boolean litstate = tmpregion->rlit ? 1 : 0; + register int hiy = tmpregion->y2; + register int x, y; + register struct rm *lev; + int lowy = tmpregion->y1; + int lowx = tmpregion->x1, hix = tmpregion->x2; + + if(litstate) { + /* adjust region size for walls, but only if lighted */ + lowx = max(lowx-1,1); + hix = min(hix+1,COLNO-1); + lowy = max(lowy-1,0); + hiy = min(hiy+1, ROWNO-1); + } + for(x = lowx; x <= hix; x++) { + lev = &levl[x][lowy]; + for(y = lowy; y <= hiy; y++) { + if (lev->typ != LAVAPOOL) /* this overrides normal lighting */ + lev->lit = litstate; + lev++; + } + } +} + +/* initialization common to all special levels */ +STATIC_OVL void +load_common_data(fd, typ) +dlb *fd; +int typ; +{ + uchar n; + long lev_flags; + int i; + + { + aligntyp atmp; + /* shuffle 3 alignments; can't use sp_lev_shuffle() on aligntyp's */ + i = rn2(3); atmp=ralign[2]; ralign[2]=ralign[i]; ralign[i]=atmp; + if (rn2(2)) { atmp=ralign[1]; ralign[1]=ralign[0]; ralign[0]=atmp; } + } + + level.flags.is_maze_lev = typ == SP_LEV_MAZE; + + /* Read the level initialization data */ + Fread((genericptr_t) &init_lev, 1, sizeof(lev_init), fd); + if(init_lev.init_present) { + if(init_lev.lit < 0) + init_lev.lit = rn2(2); + mkmap(&init_lev); + } + + /* Read the per level flags */ + Fread((genericptr_t) &lev_flags, 1, sizeof(lev_flags), fd); + if (lev_flags & NOTELEPORT) + level.flags.noteleport = 1; + if (lev_flags & HARDFLOOR) + level.flags.hardfloor = 1; + if (lev_flags & NOMMAP) + level.flags.nommap = 1; + if (lev_flags & SHORTSIGHTED) + level.flags.shortsighted = 1; + if (lev_flags & ARBOREAL) + level.flags.arboreal = 1; + + /* Read message */ + Fread((genericptr_t) &n, 1, sizeof(n), fd); + if (n) { + lev_message = (char *) alloc(n + 1); + Fread((genericptr_t) lev_message, 1, (int) n, fd); + lev_message[n] = 0; + } +} + +STATIC_OVL void +load_one_monster(fd, m) +dlb *fd; +monster *m; +{ + int size; + + Fread((genericptr_t) m, 1, sizeof *m, fd); + if ((size = m->name.len) != 0) { + m->name.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) m->name.str, 1, size, fd); + m->name.str[size] = '\0'; + } else + m->name.str = (char *) 0; + if ((size = m->appear_as.len) != 0) { + m->appear_as.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) m->appear_as.str, 1, size, fd); + m->appear_as.str[size] = '\0'; + } else + m->appear_as.str = (char *) 0; +} + +STATIC_OVL void +load_one_object(fd, o) +dlb *fd; +object *o; +{ + int size; + + Fread((genericptr_t) o, 1, sizeof *o, fd); + if ((size = o->name.len) != 0) { + o->name.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) o->name.str, 1, size, fd); + o->name.str[size] = '\0'; + } else + o->name.str = (char *) 0; +} + +STATIC_OVL void +load_one_engraving(fd, e) +dlb *fd; +engraving *e; +{ + int size; + + Fread((genericptr_t) e, 1, sizeof *e, fd); + size = e->engr.len; + e->engr.str = (char *) alloc((unsigned)size+1); + Fread((genericptr_t) e->engr.str, 1, size, fd); + e->engr.str[size] = '\0'; +} + +STATIC_OVL boolean +load_rooms(fd) +dlb *fd; +{ + xchar nrooms, ncorr; + char n; + short size; + corridor tmpcor; + room** tmproom; + int i, j; + + load_common_data(fd, SP_LEV_ROOMS); + + Fread((genericptr_t) &n, 1, sizeof(n), fd); /* nrobjects */ + if (n) { + Fread((genericptr_t)robjects, sizeof(*robjects), n, fd); + sp_lev_shuffle(robjects, (char *)0, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); /* nrmonst */ + if (n) { + Fread((genericptr_t)rmonst, sizeof(*rmonst), n, fd); + sp_lev_shuffle(rmonst, (char *)0, (int)n); + } + + Fread((genericptr_t) &nrooms, 1, sizeof(nrooms), fd); + /* Number of rooms to read */ + tmproom = NewTab(room,nrooms); + for (i=0;i 0) { /* Yup, it does! */ + r->name = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) r->name, 1, size, fd); + r->name[size] = 0; + } else + r->name = (char *) 0; + + /* Let's see if this room has a parent */ + Fread((genericptr_t) &size, 1, sizeof(size), fd); + if (size > 0) { /* Yup, it does! */ + r->parent = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) r->parent, 1, size, fd); + r->parent[size] = 0; + } else + r->parent = (char *) 0; + + Fread((genericptr_t) &r->x, 1, sizeof(r->x), fd); + /* x pos on the grid (1-5) */ + Fread((genericptr_t) &r->y, 1, sizeof(r->y), fd); + /* y pos on the grid (1-5) */ + Fread((genericptr_t) &r->w, 1, sizeof(r->w), fd); + /* width of the room */ + Fread((genericptr_t) &r->h, 1, sizeof(r->h), fd); + /* height of the room */ + Fread((genericptr_t) &r->xalign, 1, sizeof(r->xalign), fd); + /* horizontal alignment */ + Fread((genericptr_t) &r->yalign, 1, sizeof(r->yalign), fd); + /* vertical alignment */ + Fread((genericptr_t) &r->rtype, 1, sizeof(r->rtype), fd); + /* type of room (zoo, shop, etc.) */ + Fread((genericptr_t) &r->chance, 1, sizeof(r->chance), fd); + /* chance of room being special. */ + Fread((genericptr_t) &r->rlit, 1, sizeof(r->rlit), fd); + /* lit or not ? */ + Fread((genericptr_t) &r->filled, 1, sizeof(r->filled), fd); + /* to be filled? */ + r->nsubroom= 0; + + /* read the doors */ + Fread((genericptr_t) &r->ndoor, 1, sizeof(r->ndoor), fd); + if ((n = r->ndoor) != 0) + r->doors = NewTab(room_door, n); + while(n--) { + r->doors[(int)n] = New(room_door); + Fread((genericptr_t) r->doors[(int)n], 1, + sizeof(room_door), fd); + } + + /* read the stairs */ + Fread((genericptr_t) &r->nstair, 1, sizeof(r->nstair), fd); + if ((n = r->nstair) != 0) + r->stairs = NewTab(stair, n); + while (n--) { + r->stairs[(int)n] = New(stair); + Fread((genericptr_t) r->stairs[(int)n], 1, + sizeof(stair), fd); + } + + /* read the altars */ + Fread((genericptr_t) &r->naltar, 1, sizeof(r->naltar), fd); + if ((n = r->naltar) != 0) + r->altars = NewTab(altar, n); + while (n--) { + r->altars[(int)n] = New(altar); + Fread((genericptr_t) r->altars[(int)n], 1, + sizeof(altar), fd); + } + + /* read the fountains */ + Fread((genericptr_t) &r->nfountain, 1, + sizeof(r->nfountain), fd); + if ((n = r->nfountain) != 0) + r->fountains = NewTab(fountain, n); + while (n--) { + r->fountains[(int)n] = New(fountain); + Fread((genericptr_t) r->fountains[(int)n], 1, + sizeof(fountain), fd); + } + + /* read the sinks */ + Fread((genericptr_t) &r->nsink, 1, sizeof(r->nsink), fd); + if ((n = r->nsink) != 0) + r->sinks = NewTab(sink, n); + while (n--) { + r->sinks[(int)n] = New(sink); + Fread((genericptr_t) r->sinks[(int)n], 1, sizeof(sink), fd); + } + + /* read the pools */ + Fread((genericptr_t) &r->npool, 1, sizeof(r->npool), fd); + if ((n = r->npool) != 0) + r->pools = NewTab(pool,n); + while (n--) { + r->pools[(int)n] = New(pool); + Fread((genericptr_t) r->pools[(int)n], 1, sizeof(pool), fd); + } + + /* read the traps */ + Fread((genericptr_t) &r->ntrap, 1, sizeof(r->ntrap), fd); + if ((n = r->ntrap) != 0) + r->traps = NewTab(trap, n); + while(n--) { + r->traps[(int)n] = New(trap); + Fread((genericptr_t) r->traps[(int)n], 1, sizeof(trap), fd); + } + + /* read the monsters */ + Fread((genericptr_t) &r->nmonster, 1, sizeof(r->nmonster), fd); + if ((n = r->nmonster) != 0) { + r->monsters = NewTab(monster, n); + while(n--) { + r->monsters[(int)n] = New(monster); + load_one_monster(fd, r->monsters[(int)n]); + } + } else + r->monsters = 0; + + /* read the objects, in same order as mazes */ + Fread((genericptr_t) &r->nobject, 1, sizeof(r->nobject), fd); + if ((n = r->nobject) != 0) { + r->objects = NewTab(object, n); + for (j = 0; j < n; ++j) { + r->objects[j] = New(object); + load_one_object(fd, r->objects[j]); + } + } else + r->objects = 0; + + /* read the gold piles */ + Fread((genericptr_t) &r->ngold, 1, sizeof(r->ngold), fd); + if ((n = r->ngold) != 0) + r->golds = NewTab(gold, n); + while (n--) { + r->golds[(int)n] = New(gold); + Fread((genericptr_t) r->golds[(int)n], 1, sizeof(gold), fd); + } + + /* read the engravings */ + Fread((genericptr_t) &r->nengraving, 1, + sizeof(r->nengraving), fd); + if ((n = r->nengraving) != 0) { + r->engravings = NewTab(engraving,n); + while (n--) { + r->engravings[(int)n] = New(engraving); + load_one_engraving(fd, r->engravings[(int)n]); + } + } else + r->engravings = 0; + + } + + /* Now that we have loaded all the rooms, search the + * subrooms and create the links. + */ + + for (i = 0; iparent) { + /* Search the parent room */ + for(j=0; jname && !strcmp(tmproom[j]->name, + tmproom[i]->parent)) { + n = tmproom[j]->nsubroom++; + tmproom[j]->subrooms[(int)n] = tmproom[i]; + break; + } + } + + /* + * Create the rooms now... + */ + + for (i=0; i < nrooms; i++) + if(!tmproom[i]->parent) + build_room(tmproom[i], (room *) 0); + + free_rooms(tmproom, nrooms); + + /* read the corridors */ + + Fread((genericptr_t) &ncorr, sizeof(ncorr), 1, fd); + for (i=0; ix = (xchar)x, m->y = (xchar)y; +} + +/* + * The Big Thing: special maze loader + * + * Could be cleaner, but it works. + */ + +STATIC_OVL boolean +load_maze(fd) +dlb *fd; +{ + xchar x, y, typ; + boolean prefilled, room_not_needed; + + char n, numpart = 0; + xchar nwalk = 0, nwalk_sav; + schar filling; + char halign, valign; + + int xi, dir, size; + coord mm; + int mapcount, mapcountmax, mapfact; + + lev_region tmplregion; + region tmpregion; + door tmpdoor; + trap tmptrap; + monster tmpmons; + object tmpobj; + drawbridge tmpdb; + walk tmpwalk; + digpos tmpdig; + lad tmplad; + stair tmpstair, prevstair; + altar tmpaltar; + gold tmpgold; + fountain tmpfountain; + engraving tmpengraving; + xchar mustfill[(MAXNROFROOMS+1)*2]; + struct trap *badtrap; + boolean has_bounds; + + (void) memset((genericptr_t)&Map[0][0], 0, sizeof Map); + load_common_data(fd, SP_LEV_MAZE); + + /* Initialize map */ + Fread((genericptr_t) &filling, 1, sizeof(filling), fd); + if (!init_lev.init_present) { /* don't init if mkmap() has been called */ + for(x = 2; x <= x_maze_max; x++) + for(y = 0; y <= y_maze_max; y++) + if (filling == -1) { +#ifndef WALLIFIED_MAZE + levl[x][y].typ = STONE; +#else + levl[x][y].typ = + (y < 2 || ((x % 2) && (y % 2))) ? STONE : HWALL; +#endif + } else { + levl[x][y].typ = filling; + } + } + + /* Start reading the file */ + Fread((genericptr_t) &numpart, 1, sizeof(numpart), fd); + /* Number of parts */ + if (!numpart || numpart > 9) + panic("load_maze error: numpart = %d", (int) numpart); + + while (numpart--) { + Fread((genericptr_t) &halign, 1, sizeof(halign), fd); + /* Horizontal alignment */ + Fread((genericptr_t) &valign, 1, sizeof(valign), fd); + /* Vertical alignment */ + Fread((genericptr_t) &xsize, 1, sizeof(xsize), fd); + /* size in X */ + Fread((genericptr_t) &ysize, 1, sizeof(ysize), fd); + /* size in Y */ + switch((int) halign) { + case LEFT: xstart = 3; break; + case H_LEFT: xstart = 2+((x_maze_max-2-xsize)/4); break; + case CENTER: xstart = 2+((x_maze_max-2-xsize)/2); break; + case H_RIGHT: xstart = 2+((x_maze_max-2-xsize)*3/4); break; + case RIGHT: xstart = x_maze_max-xsize-1; break; + } + switch((int) valign) { + case TOP: ystart = 3; break; + case CENTER: ystart = 2+((y_maze_max-2-ysize)/2); break; + case BOTTOM: ystart = y_maze_max-ysize-1; break; + } + if (!(xstart % 2)) xstart++; + if (!(ystart % 2)) ystart++; + if ((ystart < 0) || (ystart + ysize > ROWNO)) { + /* try to move the start a bit */ + ystart += (ystart > 0) ? -2 : 2; + if(ysize == ROWNO) ystart = 0; + if(ystart < 0 || ystart + ysize > ROWNO) + panic("reading special level with ysize too large"); + } + + /* + * If any CROSSWALLs are found, must change to ROOM after REGION's + * are laid out. CROSSWALLS are used to specify "invisible" + * boundaries where DOOR syms look bad or aren't desirable. + */ + has_bounds = FALSE; + + if(init_lev.init_present && xsize <= 1 && ysize <= 1) { + xstart = 1; + ystart = 0; + xsize = COLNO-1; + ysize = ROWNO; + } else { + /* Load the map */ + for(y = ystart; y < ystart+ysize; y++) + for(x = xstart; x < xstart+xsize; x++) { + levl[x][y].typ = Fgetc(fd); + levl[x][y].lit = FALSE; + /* clear out levl: load_common_data may set them */ + levl[x][y].flags = 0; + levl[x][y].horizontal = 0; + levl[x][y].roomno = 0; + levl[x][y].edge = 0; + /* + * Note: Even though levl[x][y].typ is type schar, + * lev_comp.y saves it as type char. Since schar != char + * all the time we must make this exception or hack + * through lev_comp.y to fix. + */ + + /* + * Set secret doors to closed (why not trapped too?). Set + * the horizontal bit. + */ + if (levl[x][y].typ == SDOOR || IS_DOOR(levl[x][y].typ)) { + if(levl[x][y].typ == SDOOR) + levl[x][y].doormask = D_CLOSED; + /* + * If there is a wall to the left that connects to a + * (secret) door, then it is horizontal. This does + * not allow (secret) doors to be corners of rooms. + */ + if (x != xstart && (IS_WALL(levl[x-1][y].typ) || + levl[x-1][y].horizontal)) + levl[x][y].horizontal = 1; + } else if(levl[x][y].typ == HWALL || + levl[x][y].typ == IRONBARS) + levl[x][y].horizontal = 1; + else if(levl[x][y].typ == LAVAPOOL) + levl[x][y].lit = 1; + else if(levl[x][y].typ == CROSSWALL) + has_bounds = TRUE; + Map[x][y] = 1; + } + if (init_lev.init_present && init_lev.joined) + remove_rooms(xstart, ystart, xstart+xsize, ystart+ysize); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of level regions */ + if(n) { + if(num_lregions) { + /* realloc the lregion space to add the new ones */ + /* don't really free it up until the whole level is done */ + lev_region *newl = (lev_region *) alloc(sizeof(lev_region) * + (unsigned)(n+num_lregions)); + (void) memcpy((genericptr_t)(newl+n), (genericptr_t)lregions, + sizeof(lev_region) * num_lregions); + Free(lregions); + num_lregions += n; + lregions = newl; + } else { + num_lregions = n; + lregions = (lev_region *) + alloc(sizeof(lev_region) * (unsigned)n); + } + } + + while(n--) { + Fread((genericptr_t) &tmplregion, sizeof(tmplregion), 1, fd); + if ((size = tmplregion.rname.len) != 0) { + tmplregion.rname.str = (char *) alloc((unsigned)size + 1); + Fread((genericptr_t) tmplregion.rname.str, size, 1, fd); + tmplregion.rname.str[size] = '\0'; + } else + tmplregion.rname.str = (char *) 0; + if(!tmplregion.in_islev) { + get_location(&tmplregion.inarea.x1, &tmplregion.inarea.y1, + DRY|WET); + get_location(&tmplregion.inarea.x2, &tmplregion.inarea.y2, + DRY|WET); + } + if(!tmplregion.del_islev) { + get_location(&tmplregion.delarea.x1, &tmplregion.delarea.y1, + DRY|WET); + get_location(&tmplregion.delarea.x2, &tmplregion.delarea.y2, + DRY|WET); + } + lregions[(int)n] = tmplregion; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random objects */ + if(n) { + Fread((genericptr_t)robjects, sizeof(*robjects), (int) n, fd); + sp_lev_shuffle(robjects, (char *)0, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random locations */ + if(n) { + Fread((genericptr_t)rloc_x, sizeof(*rloc_x), (int) n, fd); + Fread((genericptr_t)rloc_y, sizeof(*rloc_y), (int) n, fd); + sp_lev_shuffle(rloc_x, rloc_y, (int)n); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Random monsters */ + if(n) { + Fread((genericptr_t)rmonst, sizeof(*rmonst), (int) n, fd); + sp_lev_shuffle(rmonst, (char *)0, (int)n); + } + + (void) memset((genericptr_t)mustfill, 0, sizeof(mustfill)); + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of subrooms */ + while(n--) { + register struct mkroom *troom; + + Fread((genericptr_t)&tmpregion, 1, sizeof(tmpregion), fd); + + if(tmpregion.rtype > MAXRTYPE) { + tmpregion.rtype -= MAXRTYPE+1; + prefilled = TRUE; + } else + prefilled = FALSE; + + if(tmpregion.rlit < 0) + tmpregion.rlit = (rnd(1+abs(depth(&u.uz))) < 11 && rn2(77)) + ? TRUE : FALSE; + + get_location(&tmpregion.x1, &tmpregion.y1, DRY|WET); + get_location(&tmpregion.x2, &tmpregion.y2, DRY|WET); + + /* for an ordinary room, `prefilled' is a flag to force + an actual room to be created (such rooms are used to + control placement of migrating monster arrivals) */ + room_not_needed = (tmpregion.rtype == OROOM && + !tmpregion.rirreg && !prefilled); + if (room_not_needed || nroom >= MAXNROFROOMS) { + if (!room_not_needed) + impossible("Too many rooms on new level!"); + light_region(&tmpregion); + continue; + } + + troom = &rooms[nroom]; + + /* mark rooms that must be filled, but do it later */ + if (tmpregion.rtype != OROOM) + mustfill[nroom] = (prefilled ? 2 : 1); + + if(tmpregion.rirreg) { + min_rx = max_rx = tmpregion.x1; + min_ry = max_ry = tmpregion.y1; + flood_fill_rm(tmpregion.x1, tmpregion.y1, + nroom+ROOMOFFSET, tmpregion.rlit, TRUE); + add_room(min_rx, min_ry, max_rx, max_ry, + FALSE, tmpregion.rtype, TRUE); + troom->rlit = tmpregion.rlit; + troom->irregular = TRUE; + } else { + add_room(tmpregion.x1, tmpregion.y1, + tmpregion.x2, tmpregion.y2, + tmpregion.rlit, tmpregion.rtype, TRUE); +#ifdef SPECIALIZATION + topologize(troom,FALSE); /* set roomno */ +#else + topologize(troom); /* set roomno */ +#endif + } + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of doors */ + while(n--) { + struct mkroom *croom = &rooms[0]; + + Fread((genericptr_t)&tmpdoor, 1, sizeof(tmpdoor), fd); + + x = tmpdoor.x; y = tmpdoor.y; + typ = tmpdoor.mask == -1 ? rnddoor() : tmpdoor.mask; + + get_location(&x, &y, DRY); + if(levl[x][y].typ != SDOOR) + levl[x][y].typ = DOOR; + else { + if(typ < D_CLOSED) + typ = D_CLOSED; /* force it to be closed */ + } + levl[x][y].doormask = typ; + + /* Now the complicated part, list it with each subroom */ + /* The dog move and mail daemon routines use this */ + while(croom->hx >= 0 && doorindex < DOORMAX) { + if(croom->hx >= x-1 && croom->lx <= x+1 && + croom->hy >= y-1 && croom->ly <= y+1) { + /* Found it */ + add_door(x, y, croom); + } + croom++; + } + } + + /* now that we have rooms _and_ associated doors, fill the rooms */ + for(n = 0; n < SIZE(mustfill); n++) + if(mustfill[(int)n]) + fill_room(&rooms[(int)n], (mustfill[(int)n] == 2)); + + /* if special boundary syms (CROSSWALL) in map, remove them now */ + if(has_bounds) { + for(x = xstart; x < xstart+xsize; x++) + for(y = ystart; y < ystart+ysize; y++) + if(levl[x][y].typ == CROSSWALL) + levl[x][y].typ = ROOM; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of drawbridges */ + while(n--) { + Fread((genericptr_t)&tmpdb, 1, sizeof(tmpdb), fd); + + x = tmpdb.x; y = tmpdb.y; + get_location(&x, &y, DRY|WET); + + if (!create_drawbridge(x, y, tmpdb.dir, tmpdb.db_open)) + impossible("Cannot create drawbridge."); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of mazewalks */ + while(n--) { + Fread((genericptr_t)&tmpwalk, 1, sizeof(tmpwalk), fd); + + get_location(&tmpwalk.x, &tmpwalk.y, DRY|WET); + + walklist[nwalk++] = tmpwalk; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of non_diggables */ + while(n--) { + Fread((genericptr_t)&tmpdig, 1, sizeof(tmpdig), fd); + + get_location(&tmpdig.x1, &tmpdig.y1, DRY|WET); + get_location(&tmpdig.x2, &tmpdig.y2, DRY|WET); + + set_wall_property(tmpdig.x1, tmpdig.y1, + tmpdig.x2, tmpdig.y2, W_NONDIGGABLE); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of non_passables */ + while(n--) { + Fread((genericptr_t)&tmpdig, 1, sizeof(tmpdig), fd); + + get_location(&tmpdig.x1, &tmpdig.y1, DRY|WET); + get_location(&tmpdig.x2, &tmpdig.y2, DRY|WET); + + set_wall_property(tmpdig.x1, tmpdig.y1, + tmpdig.x2, tmpdig.y2, W_NONPASSWALL); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of ladders */ + while(n--) { + Fread((genericptr_t)&tmplad, 1, sizeof(tmplad), fd); + + x = tmplad.x; y = tmplad.y; + get_location(&x, &y, DRY); + + levl[x][y].typ = LADDER; + if (tmplad.up == 1) { + xupladder = x; yupladder = y; + levl[x][y].ladder = LA_UP; + } else { + xdnladder = x; ydnladder = y; + levl[x][y].ladder = LA_DOWN; + } + } + + prevstair.x = prevstair.y = 0; + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of stairs */ + while(n--) { + Fread((genericptr_t)&tmpstair, 1, sizeof(tmpstair), fd); + + xi = 0; + do { + x = tmpstair.x; y = tmpstair.y; + get_location(&x, &y, DRY); + } while(prevstair.x && xi++ < 100 && + distmin(x,y,prevstair.x,prevstair.y) <= 8); + if ((badtrap = t_at(x,y)) != 0) deltrap(badtrap); + mkstairs(x, y, (char)tmpstair.up, (struct mkroom *)0); + prevstair.x = x; + prevstair.y = y; + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of altars */ + while(n--) { + Fread((genericptr_t)&tmpaltar, 1, sizeof(tmpaltar), fd); + + create_altar(&tmpaltar, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of fountains */ + while (n--) { + Fread((genericptr_t)&tmpfountain, 1, sizeof(tmpfountain), fd); + + create_feature(tmpfountain.x, tmpfountain.y, + (struct mkroom *)0, FOUNTAIN); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of traps */ + while(n--) { + Fread((genericptr_t)&tmptrap, 1, sizeof(tmptrap), fd); + + create_trap(&tmptrap, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of monsters */ + while(n--) { + load_one_monster(fd, &tmpmons); + + create_monster(&tmpmons, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of objects */ + while(n--) { + load_one_object(fd, &tmpobj); + + create_object(&tmpobj, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of gold piles */ + while (n--) { + Fread((genericptr_t)&tmpgold, 1, sizeof(tmpgold), fd); + + create_gold(&tmpgold, (struct mkroom *)0); + } + + Fread((genericptr_t) &n, 1, sizeof(n), fd); + /* Number of engravings */ + while(n--) { + load_one_engraving(fd, &tmpengraving); + + create_engraving(&tmpengraving, (struct mkroom *)0); + } + + } /* numpart loop */ + + nwalk_sav = nwalk; + while(nwalk--) { + x = (xchar) walklist[nwalk].x; + y = (xchar) walklist[nwalk].y; + dir = walklist[nwalk].dir; + + /* don't use move() - it doesn't use W_NORTH, etc. */ + switch (dir) { + case W_NORTH: --y; break; + case W_SOUTH: y++; break; + case W_EAST: x++; break; + case W_WEST: --x; break; + default: panic("load_maze: bad MAZEWALK direction"); + } + + if(!IS_DOOR(levl[x][y].typ)) { +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + + /* + * We must be sure that the parity of the coordinates for + * walkfrom() is odd. But we must also take into account + * what direction was chosen. + */ + if(!(x % 2)) { + if (dir == W_EAST) + x++; + else + x--; + + /* no need for IS_DOOR check; out of map bounds */ +#ifndef WALLIFIED_MAZE + levl[x][y].typ = CORR; +#else + levl[x][y].typ = ROOM; +#endif + levl[x][y].flags = 0; + } + + if (!(y % 2)) { + if (dir == W_SOUTH) + y++; + else + y--; + } + + walkfrom(x, y); + } + wallification(1, 0, COLNO-1, ROWNO-1); + + /* + * If there's a significant portion of maze unused by the special level, + * we don't want it empty. + * + * Makes the number of traps, monsters, etc. proportional + * to the size of the maze. + */ + mapcountmax = mapcount = (x_maze_max - 2) * (y_maze_max - 2); + + for(x = 2; x < x_maze_max; x++) + for(y = 0; y < y_maze_max; y++) + if(Map[x][y]) mapcount--; + + if (nwalk_sav && (mapcount > (int) (mapcountmax / 10))) { + mapfact = (int) ((mapcount * 100L) / mapcountmax); + for(x = rnd((int) (20 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mkobj_at(rn2(2) ? GEM_CLASS : RANDOM_CLASS, + mm.x, mm.y, TRUE); + } + for(x = rnd((int) (12 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mksobj_at(BOULDER, mm.x, mm.y, TRUE, FALSE); + } + for (x = rn2(2); x; x--) { + maze1xy(&mm, DRY); + (void) makemon(&mons[PM_MINOTAUR], mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rnd((int) (12 * mapfact) / 100); x; x--) { + maze1xy(&mm, WET|DRY); + (void) makemon((struct permonst *) 0, mm.x, mm.y, NO_MM_FLAGS); + } + for(x = rn2((int) (15 * mapfact) / 100); x; x--) { + maze1xy(&mm, DRY); + (void) mkgold(0L,mm.x,mm.y); + } + for(x = rn2((int) (15 * mapfact) / 100); x; x--) { + int trytrap; + + maze1xy(&mm, DRY); + trytrap = rndtrap(); + if (sobj_at(BOULDER, mm.x, mm.y)) + while (trytrap == PIT || trytrap == SPIKED_PIT || + trytrap == TRAPDOOR || trytrap == HOLE) + trytrap = rndtrap(); + (void) maketrap(mm.x, mm.y, trytrap); + } + } + return TRUE; +} + +/* + * General loader + */ + +boolean +load_special(name) +const char *name; +{ + dlb *fd; + boolean result = FALSE; + char c; + struct version_info vers_info; + + fd = dlb_fopen(name, RDBMODE); + if (!fd) return FALSE; + + Fread((genericptr_t) &vers_info, sizeof vers_info, 1, fd); + if (!check_version(&vers_info, name, TRUE)) + goto give_up; + + Fread((genericptr_t) &c, sizeof c, 1, fd); /* c Header */ + + switch (c) { + case SP_LEV_ROOMS: + result = load_rooms(fd); + break; + case SP_LEV_MAZE: + result = load_maze(fd); + break; + default: /* ??? */ + result = FALSE; + } + give_up: + (void)dlb_fclose(fd); + return result; +} + +/*sp_lev.c*/ diff --git a/src/spell.c b/src/spell.c new file mode 100644 index 0000000..4659cfa --- /dev/null +++ b/src/spell.c @@ -0,0 +1,1264 @@ +/* SCCS Id: @(#)spell.c 3.4 2003/01/17 */ +/* Copyright (c) M. Stephenson 1988 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +static NEARDATA schar delay; /* moves left for this spell */ +static NEARDATA struct obj *book; /* last/current book being xscribed */ + +/* spellmenu arguments; 0 thru n-1 used as spl_book[] index when swapping */ +#define SPELLMENU_CAST (-2) +#define SPELLMENU_VIEW (-1) + +#define KEEN 20000 +#define MAX_SPELL_STUDY 3 +#define incrnknow(spell) spl_book[spell].sp_know = KEEN + +#define spellev(spell) spl_book[spell].sp_lev +#define spellname(spell) OBJ_NAME(objects[spellid(spell)]) +#define spellet(spell) \ + ((char)((spell < 26) ? ('a' + spell) : ('A' + spell - 26))) + +STATIC_DCL int FDECL(spell_let_to_idx, (CHAR_P)); +STATIC_DCL boolean FDECL(cursed_book, (struct obj *bp)); +STATIC_DCL boolean FDECL(confused_book, (struct obj *)); +STATIC_DCL void FDECL(deadbook, (struct obj *)); +STATIC_PTR int NDECL(learn); +STATIC_DCL boolean FDECL(getspell, (int *)); +STATIC_DCL boolean FDECL(dospellmenu, (const char *,int,int *)); +STATIC_DCL int FDECL(percent_success, (int)); +STATIC_DCL int NDECL(throwspell); +STATIC_DCL void NDECL(cast_protection); +STATIC_DCL void FDECL(spell_backfire, (int)); +STATIC_DCL const char *FDECL(spelltypemnemonic, (int)); +STATIC_DCL int FDECL(isqrt, (int)); + +/* The roles[] table lists the role-specific values for tuning + * percent_success(). + * + * Reasoning: + * spelbase, spelheal: + * Arc are aware of magic through historical research + * Bar abhor magic (Conan finds it "interferes with his animal instincts") + * Cav are ignorant to magic + * Hea are very aware of healing magic through medical research + * Kni are moderately aware of healing from Paladin training + * Mon use magic to attack and defend in lieu of weapons and armor + * Pri are very aware of healing magic through theological research + * Ran avoid magic, preferring to fight unseen and unheard + * Rog are moderately aware of magic through trickery + * Sam have limited magical awareness, prefering meditation to conjuring + * Tou are aware of magic from all the great films they have seen + * Val have limited magical awareness, prefering fighting + * Wiz are trained mages + * + * The arms penalty is lessened for trained fighters Bar, Kni, Ran, + * Sam, Val - + * the penalty is its metal interference, not encumbrance. + * The `spelspec' is a single spell which is fundamentally easier + * for that role to cast. + * + * spelspec, spelsbon: + * Arc map masters (SPE_MAGIC_MAPPING) + * Bar fugue/berserker (SPE_HASTE_SELF) + * Cav born to dig (SPE_DIG) + * Hea to heal (SPE_CURE_SICKNESS) + * Kni to turn back evil (SPE_TURN_UNDEAD) + * Mon to preserve their abilities (SPE_RESTORE_ABILITY) + * Pri to bless (SPE_REMOVE_CURSE) + * Ran to hide (SPE_INVISIBILITY) + * Rog to find loot (SPE_DETECT_TREASURE) + * Sam to be At One (SPE_CLAIRVOYANCE) + * Tou to smile (SPE_CHARM_MONSTER) + * Val control the cold (SPE_CONE_OF_COLD) + * Wiz all really, but SPE_MAGIC_MISSILE is their party trick + * + * See percent_success() below for more comments. + * + * uarmbon, uarmsbon, uarmhbon, uarmgbon, uarmfbon: + * Fighters find body armour & shield a little less limiting. + * Headgear, Gauntlets and Footwear are not role-specific (but + * still have an effect, except helm of brilliance, which is designed + * to permit magic-use). + */ + +#define uarmhbon 4 /* Metal helmets interfere with the mind */ +#define uarmgbon 6 /* Casting channels through the hands */ +#define uarmfbon 2 /* All metal interferes to some degree */ + +/* since the spellbook itself doesn't blow up, don't say just "explodes" */ +static const char explodes[] = "radiates explosive energy"; + +/* convert a letter into a number in the range 0..51, or -1 if not a letter */ +STATIC_OVL int +spell_let_to_idx(ilet) +char ilet; +{ + int indx; + + indx = ilet - 'a'; + if (indx >= 0 && indx < 26) return indx; + indx = ilet - 'A'; + if (indx >= 0 && indx < 26) return indx + 26; + return -1; +} + +/* TRUE: book should be destroyed by caller */ +STATIC_OVL boolean +cursed_book(bp) + struct obj *bp; +{ + int lev = objects[bp->otyp].oc_level; + + switch(rn2(lev)) { + case 0: + You_feel("a wrenching sensation."); + tele(); /* teleport him */ + break; + case 1: + You_feel("threatened."); + aggravate(); + break; + case 2: + make_blinded(Blinded + rn1(100,250),TRUE); + break; + case 3: + take_gold(); + break; + case 4: + pline("These runes were just too much to comprehend."); + make_confused(HConfusion + rn1(7,16),FALSE); + break; + case 5: + pline_The("book was coated with contact poison!"); + if (uarmg) { + if (uarmg->oerodeproof || !is_corrodeable(uarmg)) { + Your("gloves seem unaffected."); + } else if (uarmg->oeroded2 < MAX_ERODE) { + if (uarmg->greased) { + grease_protect(uarmg, "gloves", &youmonst); + } else { + Your("gloves corrode%s!", + uarmg->oeroded2+1 == MAX_ERODE ? + " completely" : uarmg->oeroded2 ? + " further" : ""); + uarmg->oeroded2++; + } + } else + Your("gloves %s completely corroded.", + Blind ? "feel" : "look"); + break; + } + /* temp disable in_use; death should not destroy the book */ + bp->in_use = FALSE; + losestr(Poison_resistance ? rn1(2,1) : rn1(4,3)); + losehp(rnd(Poison_resistance ? 6 : 10), + "contact-poisoned spellbook", KILLED_BY_AN); + bp->in_use = TRUE; + break; + case 6: + if(Antimagic) { + shieldeff(u.ux, u.uy); + pline_The("book %s, but you are unharmed!", explodes); + } else { + pline("As you read the book, it %s in your %s!", + explodes, body_part(FACE)); + losehp(2*rnd(10)+5, "exploding rune", KILLED_BY_AN); + } + return TRUE; + default: + rndcurse(); + break; + } + return FALSE; +} + +/* study while confused: returns TRUE if the book is destroyed */ +STATIC_OVL boolean +confused_book(spellbook) +struct obj *spellbook; +{ + boolean gone = FALSE; + + if (!rn2(3) && spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { + spellbook->in_use = TRUE; /* in case called from learn */ + pline( + "Being confused you have difficulties in controlling your actions."); + display_nhwindow(WIN_MESSAGE, FALSE); + You("accidentally tear the spellbook to pieces."); + if (!objects[spellbook->otyp].oc_name_known && + !objects[spellbook->otyp].oc_uname) + docall(spellbook); + useup(spellbook); + gone = TRUE; + } else { + You("find yourself reading the %s line over and over again.", + spellbook == book ? "next" : "first"); + } + return gone; +} + +/* special effects for The Book of the Dead */ +STATIC_OVL void +deadbook(book2) +struct obj *book2; +{ + struct monst *mtmp, *mtmp2; + coord mm; + + You("turn the pages of the Book of the Dead..."); + makeknown(SPE_BOOK_OF_THE_DEAD); + /* KMH -- Need ->known to avoid "_a_ Book of the Dead" */ + book2->known = 1; + if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) { + register struct obj *otmp; + register boolean arti1_primed = FALSE, arti2_primed = FALSE, + arti_cursed = FALSE; + + if(book2->cursed) { + pline_The("runes appear scrambled. You can't read them!"); + return; + } + + if(!u.uhave.bell || !u.uhave.menorah) { + pline("A chill runs down your %s.", body_part(SPINE)); + if(!u.uhave.bell) You_hear("a faint chime..."); + if(!u.uhave.menorah) pline("Vlad's doppelganger is amused."); + return; + } + + for(otmp = invent; otmp; otmp = otmp->nobj) { + if(otmp->otyp == CANDELABRUM_OF_INVOCATION && + otmp->spe == 7 && otmp->lamplit) { + if(!otmp->cursed) arti1_primed = TRUE; + else arti_cursed = TRUE; + } + if(otmp->otyp == BELL_OF_OPENING && + (moves - otmp->age) < 5L) { /* you rang it recently */ + if(!otmp->cursed) arti2_primed = TRUE; + else arti_cursed = TRUE; + } + } + + if(arti_cursed) { + pline_The("invocation fails!"); + pline("At least one of your artifacts is cursed..."); + } else if(arti1_primed && arti2_primed) { + unsigned soon = (unsigned) d(2,6); /* time til next intervene() */ + + /* successful invocation */ + mkinvokearea(); + u.uevent.invoked = 1; + /* in case you haven't killed the Wizard yet, behave as if + you just did */ + u.uevent.udemigod = 1; /* wizdead() */ + if (!u.udg_cnt || u.udg_cnt > soon) u.udg_cnt = soon; + } else { /* at least one artifact not prepared properly */ + You("have a feeling that %s is amiss...", something); + goto raise_dead; + } + return; + } + + /* when not an invocation situation */ + if (book2->cursed) { +raise_dead: + + You("raised the dead!"); + /* first maybe place a dangerous adversary */ + if (!rn2(3) && ((mtmp = makemon(&mons[PM_MASTER_LICH], + u.ux, u.uy, NO_MINVENT)) != 0 || + (mtmp = makemon(&mons[PM_NALFESHNEE], + u.ux, u.uy, NO_MINVENT)) != 0)) { + mtmp->mpeaceful = 0; + set_malign(mtmp); + } + /* next handle the affect on things you're carrying */ + (void) unturn_dead(&youmonst); + /* last place some monsters around you */ + mm.x = u.ux; + mm.y = u.uy; + mkundead(&mm, TRUE, NO_MINVENT); + } else if(book2->blessed) { + for(mtmp = fmon; mtmp; mtmp = mtmp2) { + mtmp2 = mtmp->nmon; /* tamedog() changes chain */ + if (DEADMONSTER(mtmp)) continue; + + if (is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) { + mtmp->mpeaceful = TRUE; + if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type) + && distu(mtmp->mx, mtmp->my) < 4) + if (mtmp->mtame) { + if (mtmp->mtame < 20) + mtmp->mtame++; + } else + (void) tamedog(mtmp, (struct obj *)0); + else monflee(mtmp, 0, FALSE, TRUE); + } + } + } else { + switch(rn2(3)) { + case 0: + Your("ancestors are annoyed with you!"); + break; + case 1: + pline_The("headstones in the cemetery begin to move!"); + break; + default: + pline("Oh my! Your name appears in the book!"); + } + } + return; +} + +STATIC_PTR int +learn() +{ + int i; + short booktype; + char splname[BUFSZ]; + boolean costly = TRUE; + + /* JDS: lenses give 50% faster reading; 33% smaller read time */ + if (delay && ublindf && ublindf->otyp == LENSES && rn2(2)) delay++; + if (Confusion) { /* became confused while learning */ + (void) confused_book(book); + book = 0; /* no longer studying */ + nomul(delay); /* remaining delay is uninterrupted */ + delay = 0; + return(0); + } + if (delay) { /* not if (delay++), so at end delay == 0 */ + delay++; + return(1); /* still busy */ + } + exercise(A_WIS, TRUE); /* you're studying. */ + booktype = book->otyp; + if(booktype == SPE_BOOK_OF_THE_DEAD) { + deadbook(book); + return(0); + } + + Sprintf(splname, objects[booktype].oc_name_known ? + "\"%s\"" : "the \"%s\" spell", + OBJ_NAME(objects[booktype])); + for (i = 0; i < MAXSPELL; i++) { + if (spellid(i) == booktype) { + if (book->spestudied > MAX_SPELL_STUDY) { + pline("This spellbook is too faint to be read any more."); + book->otyp = booktype = SPE_BLANK_PAPER; + } else if (spellknow(i) <= 1000) { + Your("knowledge of %s is keener.", splname); + incrnknow(i); + book->spestudied++; + exercise(A_WIS,TRUE); /* extra study */ + } else { /* 1000 < spellknow(i) <= MAX_SPELL_STUDY */ + You("know %s quite well already.", splname); + costly = FALSE; + } + /* make book become known even when spell is already + known, in case amnesia made you forget the book */ + makeknown((int)booktype); + break; + } else if (spellid(i) == NO_SPELL) { + spl_book[i].sp_id = booktype; + spl_book[i].sp_lev = objects[booktype].oc_level; + incrnknow(i); + book->spestudied++; + You(i > 0 ? "add %s to your repertoire." : "learn %s.", + splname); + makeknown((int)booktype); + break; + } + } + if (i == MAXSPELL) impossible("Too many spells memorized!"); + + if (book->cursed) { /* maybe a demon cursed it */ + if (cursed_book(book)) { + useup(book); + book = 0; + return 0; + } + } + if (costly) check_unpaid(book); + book = 0; + return(0); +} + +int +study_book(spellbook) +register struct obj *spellbook; +{ + register int booktype = spellbook->otyp; + register boolean confused = (Confusion != 0); + boolean too_hard = FALSE; + + if (delay && !confused && spellbook == book && + /* handle the sequence: start reading, get interrupted, + have book become erased somehow, resume reading it */ + booktype != SPE_BLANK_PAPER) { + You("continue your efforts to memorize the spell."); + } else { + /* KMH -- Simplified this code */ + if (booktype == SPE_BLANK_PAPER) { + pline("This spellbook is all blank."); + makeknown(booktype); + return(1); + } + switch (objects[booktype].oc_level) { + case 1: + case 2: + delay = -objects[booktype].oc_delay; + break; + case 3: + case 4: + delay = -(objects[booktype].oc_level - 1) * + objects[booktype].oc_delay; + break; + case 5: + case 6: + delay = -objects[booktype].oc_level * + objects[booktype].oc_delay; + break; + case 7: + delay = -8 * objects[booktype].oc_delay; + break; + default: + impossible("Unknown spellbook level %d, book %d;", + objects[booktype].oc_level, booktype); + return 0; + } + + /* Books are often wiser than their readers (Rus.) */ + spellbook->in_use = TRUE; + if (!spellbook->blessed && + spellbook->otyp != SPE_BOOK_OF_THE_DEAD) { + if (spellbook->cursed) { + too_hard = TRUE; + } else { + /* uncursed - chance to fail */ + int read_ability = ACURR(A_INT) + 4 + u.ulevel/2 + - 2*objects[booktype].oc_level + + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0); + /* only wizards know if a spell is too difficult */ + if (Role_if(PM_WIZARD) && read_ability < 20 && + !confused) { + char qbuf[QBUFSZ]; + Sprintf(qbuf, + "This spellbook is %sdifficult to comprehend. Continue?", + (read_ability < 12 ? "very " : "")); + if (yn(qbuf) != 'y') { + spellbook->in_use = FALSE; + return(1); + } + } + /* its up to random luck now */ + if (rnd(20) > read_ability) { + too_hard = TRUE; + } + } + } + + if (too_hard) { + boolean gone = cursed_book(spellbook); + + nomul(delay); /* study time */ + delay = 0; + if(gone || !rn2(3)) { + if (!gone) pline_The("spellbook crumbles to dust!"); + if (!objects[spellbook->otyp].oc_name_known && + !objects[spellbook->otyp].oc_uname) + docall(spellbook); + useup(spellbook); + } else + spellbook->in_use = FALSE; + return(1); + } else if (confused) { + if (!confused_book(spellbook)) { + spellbook->in_use = FALSE; + } + nomul(delay); + delay = 0; + return(1); + } + spellbook->in_use = FALSE; + + You("begin to %s the runes.", + spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" : + "memorize"); + } + + book = spellbook; + set_occupation(learn, "studying", 0); + return(1); +} + +/* a spellbook has been destroyed or the character has changed levels; + the stored address for the current book is no longer valid */ +void +book_disappears(obj) +struct obj *obj; +{ + if (obj == book) book = (struct obj *)0; +} + +/* renaming an object usually results in it having a different address; + so the sequence start reading, get interrupted, name the book, resume + reading would read the "new" book from scratch */ +void +book_substitution(old_obj, new_obj) +struct obj *old_obj, *new_obj; +{ + if (old_obj == book) book = new_obj; +} + +/* called from moveloop() */ +void +age_spells() +{ + int i; + /* + * The time relative to the hero (a pass through move + * loop) causes all spell knowledge to be decremented. + * The hero's speed, rest status, conscious status etc. + * does not alter the loss of memory. + */ + for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) + if (spellknow(i)) + decrnknow(i); + return; +} + +/* + * Return TRUE if a spell was picked, with the spell index in the return + * parameter. Otherwise return FALSE. + */ +STATIC_OVL boolean +getspell(spell_no) + int *spell_no; +{ + int nspells, idx; + char ilet, lets[BUFSZ], qbuf[QBUFSZ]; + + if (spellid(0) == NO_SPELL) { + You("don't know any spells right now."); + return FALSE; + } + if (flags.menu_style == MENU_TRADITIONAL) { + /* we know there is at least 1 known spell */ + for (nspells = 1; nspells < MAXSPELL + && spellid(nspells) != NO_SPELL; nspells++) + continue; + + if (nspells == 1) Strcpy(lets, "a"); + else if (nspells < 27) Sprintf(lets, "a-%c", 'a' + nspells - 1); + else if (nspells == 27) Sprintf(lets, "a-zA"); + else Sprintf(lets, "a-zA-%c", 'A' + nspells - 27); + + for(;;) { + Sprintf(qbuf, "Cast which spell? [%s ?]", lets); + if ((ilet = yn_function(qbuf, (char *)0, '\0')) == '?') + break; + + if (index(quitchars, ilet)) + return FALSE; + + idx = spell_let_to_idx(ilet); + if (idx >= 0 && idx < nspells) { + *spell_no = idx; + return TRUE; + } else + You("don't know that spell."); + } + } + return dospellmenu("Choose which spell to cast", + SPELLMENU_CAST, spell_no); +} + +/* the 'Z' command -- cast a spell */ +int +docast() +{ + int spell_no; + + if (getspell(&spell_no)) + return spelleffects(spell_no, FALSE); + return 0; +} + +STATIC_OVL const char * +spelltypemnemonic(skill) +int skill; +{ + switch (skill) { + case P_ATTACK_SPELL: + return "attack"; + case P_HEALING_SPELL: + return "healing"; + case P_DIVINATION_SPELL: + return "divination"; + case P_ENCHANTMENT_SPELL: + return "enchantment"; + case P_CLERIC_SPELL: + return "clerical"; + case P_ESCAPE_SPELL: + return "escape"; + case P_MATTER_SPELL: + return "matter"; + default: + impossible("Unknown spell skill, %d;", skill); + return ""; + } +} + +int +spell_skilltype(booktype) +int booktype; +{ + return (objects[booktype].oc_skill); +} + +STATIC_OVL void +cast_protection() +{ + int loglev = 0; + int l = u.ulevel; + int natac = u.uac - u.uspellprot; + int gain; + + /* loglev=log2(u.ulevel)+1 (1..5) */ + while (l) { + loglev++; + l /= 2; + } + + /* The more u.uspellprot you already have, the less you get, + * and the better your natural ac, the less you get. + * + * LEVEL AC SPELLPROT from sucessive SPE_PROTECTION casts + * 1 10 0, 1, 2, 3, 4 + * 1 0 0, 1, 2, 3 + * 1 -10 0, 1, 2 + * 2-3 10 0, 2, 4, 5, 6, 7, 8 + * 2-3 0 0, 2, 4, 5, 6 + * 2-3 -10 0, 2, 3, 4 + * 4-7 10 0, 3, 6, 8, 9, 10, 11, 12 + * 4-7 0 0, 3, 5, 7, 8, 9 + * 4-7 -10 0, 3, 5, 6 + * 7-15 -10 0, 3, 5, 6 + * 8-15 10 0, 4, 7, 10, 12, 13, 14, 15, 16 + * 8-15 0 0, 4, 7, 9, 10, 11, 12 + * 8-15 -10 0, 4, 6, 7, 8 + * 16-30 10 0, 5, 9, 12, 14, 16, 17, 18, 19, 20 + * 16-30 0 0, 5, 9, 11, 13, 14, 15 + * 16-30 -10 0, 5, 8, 9, 10 + */ + gain = loglev - (int)u.uspellprot / (4 - min(3,(10 - natac)/10)); + + if (gain > 0) { + if (!Blind) { + const char *hgolden = hcolor(NH_GOLDEN); + + if (u.uspellprot) + pline_The("%s haze around you becomes more dense.", + hgolden); + else + pline_The("%s around you begins to shimmer with %s haze.", + /*[ what about being inside solid rock while polyd? ]*/ + (Underwater || Is_waterlevel(&u.uz)) ? "water" : "air", + an(hgolden)); + } + u.uspellprot += gain; + u.uspmtime = + P_SKILL(spell_skilltype(SPE_PROTECTION)) == P_EXPERT ? 20 : 10; + if (!u.usptime) + u.usptime = u.uspmtime; + find_ac(); + } else { + Your("skin feels warm for a moment."); + } +} + +/* attempting to cast a forgotten spell will cause disorientation */ +STATIC_OVL void +spell_backfire(spell) +int spell; +{ + long duration = (long)((spellev(spell) + 1) * 3); /* 6..24 */ + + /* prior to 3.4.1, the only effect was confusion; it still predominates */ + switch (rn2(10)) { + case 0: + case 1: + case 2: + case 3: make_confused(duration, FALSE); /* 40% */ + break; + case 4: + case 5: + case 6: make_confused(2L * duration / 3L, FALSE); /* 30% */ + make_stunned(duration / 3L, FALSE); + break; + case 7: + case 8: make_stunned(2L * duration / 3L, FALSE); /* 20% */ + make_confused(duration / 3L, FALSE); + break; + case 9: make_stunned(duration, FALSE); /* 10% */ + break; + } + return; +} + +int +spelleffects(spell, atme) +int spell; +boolean atme; +{ + int energy, damage, chance, n, intell; + int skill, role_skill; + boolean confused = (Confusion != 0); + struct obj *pseudo; + coord cc; + + /* + * Spell casting no longer affects knowledge of the spell. A + * decrement of spell knowledge is done every turn. + */ + if (spellknow(spell) <= 0) { + Your("knowledge of this spell is twisted."); + pline("It invokes nightmarish images in your mind..."); + spell_backfire(spell); + return(0); + } else if (spellknow(spell) <= 100) { + You("strain to recall the spell."); + } else if (spellknow(spell) <= 1000) { + Your("knowledge of this spell is growing faint."); + } + energy = (spellev(spell) * 5); /* 5 <= energy <= 35 */ + + if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) { + You("are too hungry to cast that spell."); + return(0); + } else if (ACURR(A_STR) < 4) { + You("lack the strength to cast spells."); + return(0); + } else if(check_capacity( + "Your concentration falters while carrying so much stuff.")) { + return (1); + } else if (!freehand()) { + Your("arms are not free to cast!"); + return (0); + } + + if (u.uhave.amulet) { + You_feel("the amulet draining your energy away."); + energy += rnd(2*energy); + } + if(energy > u.uen) { + You("don't have enough energy to cast that spell."); + return(0); + } else { + if (spellid(spell) != SPE_DETECT_FOOD) { + int hungr = energy * 2; + + /* If hero is a wizard, their current intelligence + * (bonuses + temporary + current) + * affects hunger reduction in casting a spell. + * 1. int = 17-18 no reduction + * 2. int = 16 1/4 hungr + * 3. int = 15 1/2 hungr + * 4. int = 1-14 normal reduction + * The reason for this is: + * a) Intelligence affects the amount of exertion + * in thinking. + * b) Wizards have spent their life at magic and + * understand quite well how to cast spells. + */ + intell = acurr(A_INT); + if (!Role_if(PM_WIZARD)) intell = 10; + switch (intell) { + case 25: case 24: case 23: case 22: + case 21: case 20: case 19: case 18: + case 17: hungr = 0; break; + case 16: hungr /= 4; break; + case 15: hungr /= 2; break; + } + /* don't put player (quite) into fainting from + * casting a spell, particularly since they might + * not even be hungry at the beginning; however, + * this is low enough that they must eat before + * casting anything else except detect food + */ + if (hungr > u.uhunger-3) + hungr = u.uhunger-3; + morehungry(hungr); + } + } + + chance = percent_success(spell); + if (confused || (rnd(100) > chance)) { + You("fail to cast the spell correctly."); + u.uen -= energy / 2; + flags.botl = 1; + return(1); + } + + u.uen -= energy; + flags.botl = 1; + exercise(A_WIS, TRUE); + /* pseudo is a temporary "false" object containing the spell stats */ + pseudo = mksobj(spellid(spell), FALSE, FALSE); + pseudo->blessed = pseudo->cursed = 0; + pseudo->quan = 20L; /* do not let useup get it */ + /* + * Find the skill the hero has in a spell type category. + * See spell_skilltype for categories. + */ + skill = spell_skilltype(pseudo->otyp); + role_skill = P_SKILL(skill); + + switch(pseudo->otyp) { + /* + * At first spells act as expected. As the hero increases in skill + * with the appropriate spell type, some spells increase in their + * effects, e.g. more damage, further distance, and so on, without + * additional cost to the spellcaster. + */ + case SPE_CONE_OF_COLD: + case SPE_FIREBALL: + if (role_skill >= P_SKILLED) { + if (throwspell()) { + cc.x=u.dx;cc.y=u.dy; + n=rnd(8)+1; + while(n--) { + if(!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(pseudo, TRUE)) != 0) { + char buf[BUFSZ]; + Sprintf(buf, "zapped %sself with a spell", uhim()); + losehp(damage, buf, NO_KILLER_PREFIX); + } + } else { + explode(u.dx, u.dy, + pseudo->otyp - SPE_MAGIC_MISSILE + 10, + u.ulevel/2 + 1 + spell_damage_bonus(), 0, + (pseudo->otyp == SPE_CONE_OF_COLD) ? + EXPL_FROSTY : EXPL_FIERY); + } + u.dx = cc.x+rnd(3)-2; u.dy = cc.y+rnd(3)-2; + if (!isok(u.dx,u.dy) || !cansee(u.dx,u.dy) || + IS_STWALL(levl[u.dx][u.dy].typ) || u.uswallow) { + /* Spell is reflected back to center */ + u.dx = cc.x; + u.dy = cc.y; + } + } + } + break; + } /* else fall through... */ + + /* these spells are all duplicates of wand effects */ + case SPE_FORCE_BOLT: + case SPE_SLEEP: + case SPE_MAGIC_MISSILE: + case SPE_KNOCK: + case SPE_SLOW_MONSTER: + case SPE_WIZARD_LOCK: + case SPE_DIG: + case SPE_TURN_UNDEAD: + case SPE_POLYMORPH: + case SPE_TELEPORT_AWAY: + case SPE_CANCELLATION: + case SPE_FINGER_OF_DEATH: + case SPE_LIGHT: + case SPE_DETECT_UNSEEN: + case SPE_HEALING: + case SPE_EXTRA_HEALING: + case SPE_DRAIN_LIFE: + case SPE_STONE_TO_FLESH: + if (!(objects[pseudo->otyp].oc_dir == NODIR)) { + if (atme) u.dx = u.dy = u.dz = 0; + else if (!getdir((char *)0)) { + /* getdir cancelled, re-use previous direction */ + pline_The("magical energy is released!"); + } + if(!u.dx && !u.dy && !u.dz) { + if ((damage = zapyourself(pseudo, TRUE)) != 0) { + char buf[BUFSZ]; + Sprintf(buf, "zapped %sself with a spell", uhim()); + losehp(damage, buf, NO_KILLER_PREFIX); + } + } else weffects(pseudo); + } else weffects(pseudo); + update_inventory(); /* spell may modify inventory */ + break; + + /* these are all duplicates of scroll effects */ + case SPE_REMOVE_CURSE: + case SPE_CONFUSE_MONSTER: + case SPE_DETECT_FOOD: + case SPE_CAUSE_FEAR: + /* high skill yields effect equivalent to blessed scroll */ + if (role_skill >= P_SKILLED) pseudo->blessed = 1; + /* fall through */ + case SPE_CHARM_MONSTER: + case SPE_MAGIC_MAPPING: + case SPE_CREATE_MONSTER: + case SPE_IDENTIFY: + (void) seffects(pseudo); + break; + + /* these are all duplicates of potion effects */ + case SPE_HASTE_SELF: + case SPE_DETECT_TREASURE: + case SPE_DETECT_MONSTERS: + case SPE_LEVITATION: + case SPE_RESTORE_ABILITY: + /* high skill yields effect equivalent to blessed potion */ + if (role_skill >= P_SKILLED) pseudo->blessed = 1; + /* fall through */ + case SPE_INVISIBILITY: + (void) peffects(pseudo); + break; + + case SPE_CURE_BLINDNESS: + healup(0, 0, FALSE, TRUE); + break; + case SPE_CURE_SICKNESS: + if (Sick) You("are no longer ill."); + if (Slimed) { + pline_The("slime disappears!"); + Slimed = 0; + /* flags.botl = 1; -- healup() handles this */ + } + healup(0, 0, TRUE, FALSE); + break; + case SPE_CREATE_FAMILIAR: + (void) make_familiar((struct obj *)0, u.ux, u.uy, FALSE); + break; + case SPE_CLAIRVOYANCE: + if (!BClairvoyant) + do_vicinity_map(); + /* at present, only one thing blocks clairvoyance */ + else if (uarmh && uarmh->otyp == CORNUTHAUM) + You("sense a pointy hat on top of your %s.", + body_part(HEAD)); + break; + case SPE_PROTECTION: + cast_protection(); + break; + case SPE_JUMPING: + if (!jump(max(role_skill,1))) + pline(nothing_happens); + break; + default: + impossible("Unknown spell %d attempted.", spell); + obfree(pseudo, (struct obj *)0); + return(0); + } + + /* gain skill for successful cast */ + use_skill(skill, spellev(spell)); + + obfree(pseudo, (struct obj *)0); /* now, get rid of it */ + return(1); +} + +/* Choose location where spell takes effect. */ +STATIC_OVL int +throwspell() +{ + coord cc; + + if (u.uinwater) { + pline("You're joking! In this weather?"); return 0; + } else if (Is_waterlevel(&u.uz)) { + You("had better wait for the sun to come out."); return 0; + } + + pline("Where do you want to cast the spell?"); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the desired position") < 0) + return 0; /* user pressed ESC */ + /* The number of moves from hero to where the spell drops.*/ + if (distmin(u.ux, u.uy, cc.x, cc.y) > 10) { + pline_The("spell dissipates over the distance!"); + return 0; + } else if (u.uswallow) { + pline_The("spell is cut short!"); + exercise(A_WIS, FALSE); /* What were you THINKING! */ + u.dx = 0; + u.dy = 0; + return 1; + } else if (!cansee(cc.x, cc.y) || IS_STWALL(levl[cc.x][cc.y].typ)) { + Your("mind fails to lock onto that location!"); + return 0; + } else { + u.dx=cc.x; + u.dy=cc.y; + return 1; + } +} + +void +losespells() +{ + boolean confused = (Confusion != 0); + int n, nzap, i; + + book = 0; + for (n = 0; n < MAXSPELL && spellid(n) != NO_SPELL; n++) + continue; + if (n) { + nzap = rnd(n) + confused ? 1 : 0; + if (nzap > n) nzap = n; + for (i = n - nzap; i < n; i++) { + spellid(i) = NO_SPELL; + exercise(A_WIS, FALSE); /* ouch! */ + } + } +} + +/* the '+' command -- view known spells */ +int +dovspell() +{ + char qbuf[QBUFSZ]; + int splnum, othnum; + struct spell spl_tmp; + + if (spellid(0) == NO_SPELL) + You("don't know any spells right now."); + else { + while (dospellmenu("Currently known spells", + SPELLMENU_VIEW, &splnum)) { + Sprintf(qbuf, "Reordering spells; swap '%c' with", + spellet(splnum)); + if (!dospellmenu(qbuf, splnum, &othnum)) break; + + spl_tmp = spl_book[splnum]; + spl_book[splnum] = spl_book[othnum]; + spl_book[othnum] = spl_tmp; + } + } + return 0; +} + +STATIC_OVL boolean +dospellmenu(prompt, splaction, spell_no) +const char *prompt; +int splaction; /* SPELLMENU_CAST, SPELLMENU_VIEW, or spl_book[] index */ +int *spell_no; +{ + winid tmpwin; + int i, n, how; + char buf[BUFSZ]; + menu_item *selected; + anything any; + + tmpwin = create_nhwindow(NHW_MENU); + start_menu(tmpwin); + any.a_void = 0; /* zero out all bits */ + + /* + * The correct spacing of the columns depends on the + * following that (1) the font is monospaced and (2) + * that selection letters are pre-pended to the given + * string and are of the form "a - ". + * + * To do it right would require that we implement columns + * in the window-ports (say via a tab character). + */ + if (!iflags.menu_tab_sep) + Sprintf(buf, "%-20s Level %-12s Fail", " Name", "Category"); + else + Sprintf(buf, "Name\tLevel\tCategory\tFail"); + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED); + for (i = 0; i < MAXSPELL && spellid(i) != NO_SPELL; i++) { + Sprintf(buf, iflags.menu_tab_sep ? + "%s\t%-d%s\t%s\t%-d%%" : "%-20s %2d%s %-12s %3d%%", + spellname(i), spellev(i), + spellknow(i) ? " " : "*", + spelltypemnemonic(spell_skilltype(spellid(i))), + 100 - percent_success(i)); + + any.a_int = i+1; /* must be non-zero */ + add_menu(tmpwin, NO_GLYPH, &any, + spellet(i), 0, ATR_NONE, buf, + (i == splaction) ? MENU_SELECTED : MENU_UNSELECTED); + } + end_menu(tmpwin, prompt); + + how = PICK_ONE; + if (splaction == SPELLMENU_VIEW && spellid(1) == NO_SPELL) + how = PICK_NONE; /* only one spell => nothing to swap with */ + n = select_menu(tmpwin, how, &selected); + destroy_nhwindow(tmpwin); + if (n > 0) { + *spell_no = selected[0].item.a_int - 1; + /* menu selection for `PICK_ONE' does not + de-select any preselected entry */ + if (n > 1 && *spell_no == splaction) + *spell_no = selected[1].item.a_int - 1; + free((genericptr_t)selected); + /* default selection of preselected spell means that + user chose not to swap it with anything */ + if (*spell_no == splaction) return FALSE; + return TRUE; + } else if (splaction >= 0) { + /* explicit de-selection of preselected spell means that + user is still swapping but not for the current spell */ + *spell_no = splaction; + return TRUE; + } + return FALSE; +} + +/* Integer square root function without using floating point. */ +STATIC_OVL int +isqrt(val) +int val; +{ + int rt = 0; + int odd = 1; + while(val >= odd) { + val = val-odd; + odd = odd+2; + rt = rt + 1; + } + return rt; +} + +STATIC_OVL int +percent_success(spell) +int spell; +{ + /* Intrinsic and learned ability are combined to calculate + * the probability of player's success at cast a given spell. + */ + int chance, splcaster, special, statused; + int difficulty; + int skill; + + /* Calculate intrinsic ability (splcaster) */ + + splcaster = urole.spelbase; + special = urole.spelheal; + statused = ACURR(urole.spelstat); + + if (uarm && is_metallic(uarm)) + splcaster += (uarmc && uarmc->otyp == ROBE) ? + urole.spelarmr/2 : urole.spelarmr; + else if (uarmc && uarmc->otyp == ROBE) + splcaster -= urole.spelarmr; + if (uarms) splcaster += urole.spelshld; + + if (uarmh && is_metallic(uarmh) && uarmh->otyp != HELM_OF_BRILLIANCE) + splcaster += uarmhbon; + if (uarmg && is_metallic(uarmg)) splcaster += uarmgbon; + if (uarmf && is_metallic(uarmf)) splcaster += uarmfbon; + + if (spellid(spell) == urole.spelspec) + splcaster += urole.spelsbon; + + + /* `healing spell' bonus */ + if (spellid(spell) == SPE_HEALING || + spellid(spell) == SPE_EXTRA_HEALING || + spellid(spell) == SPE_CURE_BLINDNESS || + spellid(spell) == SPE_CURE_SICKNESS || + spellid(spell) == SPE_RESTORE_ABILITY || + spellid(spell) == SPE_REMOVE_CURSE) splcaster += special; + + if (splcaster > 20) splcaster = 20; + + /* Calculate learned ability */ + + /* Players basic likelihood of being able to cast any spell + * is based of their `magic' statistic. (Int or Wis) + */ + chance = 11 * statused / 2; + + /* + * High level spells are harder. Easier for higher level casters. + * The difficulty is based on the hero's level and their skill level + * in that spell type. + */ + skill = P_SKILL(spell_skilltype(spellid(spell))); + skill = max(skill,P_UNSKILLED) - 1; /* unskilled => 0 */ + difficulty= (spellev(spell)-1) * 4 - ((skill * 6) + (u.ulevel/3) + 1); + + if (difficulty > 0) { + /* Player is too low level or unskilled. */ + chance -= isqrt(900 * difficulty + 2000); + } else { + /* Player is above level. Learning continues, but the + * law of diminishing returns sets in quickly for + * low-level spells. That is, a player quickly gains + * no advantage for raising level. + */ + int learning = 15 * -difficulty / spellev(spell); + chance += learning > 20 ? 20 : learning; + } + + /* Clamp the chance: >18 stat and advanced learning only help + * to a limit, while chances below "hopeless" only raise the + * specter of overflowing 16-bit ints (and permit wearing a + * shield to raise the chances :-). + */ + if (chance < 0) chance = 0; + if (chance > 120) chance = 120; + + /* Wearing anything but a light shield makes it very awkward + * to cast a spell. The penalty is not quite so bad for the + * player's role-specific spell. + */ + if (uarms && weight(uarms) > (int) objects[SMALL_SHIELD].oc_weight) { + if (spellid(spell) == urole.spelspec) { + chance /= 2; + } else { + chance /= 4; + } + } + + /* Finally, chance (based on player intell/wisdom and level) is + * combined with ability (based on player intrinsics and + * encumbrances). No matter how intelligent/wise and advanced + * a player is, intrinsics and encumbrance can prevent casting; + * and no matter how able, learning is always required. + */ + chance = chance * (20-splcaster) / 15 - splcaster; + + /* Clamp to percentile */ + if (chance > 100) chance = 100; + if (chance < 0) chance = 0; + + return chance; +} + + +/* Learn a spell during creation of the initial inventory */ +void +initialspell(obj) +struct obj *obj; +{ + int i; + + for (i = 0; i < MAXSPELL; i++) { + if (spellid(i) == obj->otyp) { + pline("Error: Spell %s already known.", + OBJ_NAME(objects[obj->otyp])); + return; + } + if (spellid(i) == NO_SPELL) { + spl_book[i].sp_id = obj->otyp; + spl_book[i].sp_lev = objects[obj->otyp].oc_level; + incrnknow(i); + return; + } + } + impossible("Too many spells memorized!"); + return; +} + +/*spell.c*/ diff --git a/src/steal.c b/src/steal.c new file mode 100644 index 0000000..1feed77 --- /dev/null +++ b/src/steal.c @@ -0,0 +1,635 @@ +/* SCCS Id: @(#)steal.c 3.4 2003/12/04 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_PTR int NDECL(stealarm); + +#ifdef OVLB +STATIC_DCL const char *FDECL(equipname, (struct obj *)); +STATIC_DCL void FDECL(mdrop_obj, (struct monst *,struct obj *,BOOLEAN_P)); + +STATIC_OVL const char * +equipname(otmp) +register struct obj *otmp; +{ + return ( +#ifdef TOURIST + (otmp == uarmu) ? "shirt" : +#endif + (otmp == uarmf) ? "boots" : + (otmp == uarms) ? "shield" : + (otmp == uarmg) ? "gloves" : + (otmp == uarmc) ? cloak_simple_name(otmp) : + (otmp == uarmh) ? "helmet" : "armor"); +} + +#ifndef GOLDOBJ +long /* actually returns something that fits in an int */ +somegold() +{ +#ifdef LINT /* long conv. ok */ + return(0L); +#else + return (long)( (u.ugold < 100) ? u.ugold : + (u.ugold > 10000) ? rnd(10000) : rnd((int) u.ugold) ); +#endif +} + +void +stealgold(mtmp) +register struct monst *mtmp; +{ + register struct obj *gold = g_at(u.ux, u.uy); + register long tmp; + + if (gold && ( !u.ugold || gold->quan > u.ugold || !rn2(5))) { + mtmp->mgold += gold->quan; + delobj(gold); + newsym(u.ux, u.uy); + pline("%s quickly snatches some gold from between your %s!", + Monnam(mtmp), makeplural(body_part(FOOT))); + if(!u.ugold || !rn2(5)) { + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + /* do not set mtmp->mavenge here; gold on the floor is fair game */ + monflee(mtmp, 0, FALSE, FALSE); + } + } else if(u.ugold) { + u.ugold -= (tmp = somegold()); + Your("purse feels lighter."); + mtmp->mgold += tmp; + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + mtmp->mavenge = 1; + monflee(mtmp, 0, FALSE, FALSE); + flags.botl = 1; + } +} + +#else /* !GOLDOBJ */ + +long /* actually returns something that fits in an int */ +somegold(umoney) +long umoney; +{ +#ifdef LINT /* long conv. ok */ + return(0L); +#else + return (long)( (umoney < 100) ? umoney : + (umoney > 10000) ? rnd(10000) : rnd((int) umoney) ); +#endif +} + +/* +Find the first (and hopefully only) gold object in a chain. +Used when leprechaun (or you as leprechaun) looks for +someone else's gold. Returns a pointer so the gold may +be seized without further searching. +May search containers too. +Deals in gold only, as leprechauns don't care for lesser coins. +*/ +struct obj * +findgold(chain) +register struct obj *chain; +{ + while (chain && chain->otyp != GOLD_PIECE) chain = chain->nobj; + return chain; +} + +/* +Steal gold coins only. Leprechauns don't care for lesser coins. +*/ +void +stealgold(mtmp) +register struct monst *mtmp; +{ + register struct obj *fgold = g_at(u.ux, u.uy); + register struct obj *ygold; + register long tmp; + + /* skip lesser coins on the floor */ + while (fgold && fgold->otyp != GOLD_PIECE) fgold = fgold->nexthere; + + /* Do you have real gold? */ + ygold = findgold(invent); + + if (fgold && ( !ygold || fgold->quan > ygold->quan || !rn2(5))) { + obj_extract_self(fgold); + add_to_minv(mtmp, fgold); + newsym(u.ux, u.uy); + pline("%s quickly snatches some gold from between your %s!", + Monnam(mtmp), makeplural(body_part(FOOT))); + if(!ygold || !rn2(5)) { + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + monflee(mtmp, 0, FALSE, FALSE); + } + } else if(ygold) { + const int gold_price = objects[GOLD_PIECE].oc_cost; + tmp = (somegold(money_cnt(invent)) + gold_price - 1) / gold_price; + tmp = min(tmp, ygold->quan); + if (tmp < ygold->quan) ygold = splitobj(ygold, tmp); + freeinv(ygold); + add_to_minv(mtmp, ygold); + Your("purse feels lighter."); + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + monflee(mtmp, 0, FALSE, FALSE); + flags.botl = 1; + } +} +#endif /* GOLDOBJ */ + +/* steal armor after you finish taking it off */ +unsigned int stealoid; /* object to be stolen */ +unsigned int stealmid; /* monster doing the stealing */ + +STATIC_PTR int +stealarm() +{ + register struct monst *mtmp; + register struct obj *otmp; + + for(otmp = invent; otmp; otmp = otmp->nobj) { + if(otmp->o_id == stealoid) { + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if(mtmp->m_id == stealmid) { + if(DEADMONSTER(mtmp)) impossible("stealarm(): dead monster stealing"); + if(!dmgtype(mtmp->data, AD_SITM)) /* polymorphed */ + goto botm; + if(otmp->unpaid) + subfrombill(otmp, shop_keeper(*u.ushops)); + freeinv(otmp); + pline("%s steals %s!", Monnam(mtmp), doname(otmp)); + (void) mpickobj(mtmp,otmp); /* may free otmp */ + /* Implies seduction, "you gladly hand over ..." + so we don't set mavenge bit here. */ + monflee(mtmp, 0, FALSE, FALSE); + if (!tele_restrict(mtmp)) (void) rloc(mtmp, FALSE); + break; + } + } + break; + } + } +botm: stealoid = 0; + return 0; +} + +/* An object you're wearing has been taken off by a monster (theft or + seduction). Also used if a worn item gets transformed (stone to flesh). */ +void +remove_worn_item(obj, unchain_ball) +struct obj *obj; +boolean unchain_ball; /* whether to unpunish or just unwield */ +{ + if (donning(obj)) + cancel_don(); + if (!obj->owornmask) + return; + + if (obj->owornmask & W_ARMOR) { + if (obj == uskin) { + impossible("Removing embedded scales?"); + skinback(TRUE); /* uarm = uskin; uskin = 0; */ + } + if (obj == uarm) (void) Armor_off(); + else if (obj == uarmc) (void) Cloak_off(); + else if (obj == uarmf) (void) Boots_off(); + else if (obj == uarmg) (void) Gloves_off(); + else if (obj == uarmh) (void) Helmet_off(); + else if (obj == uarms) (void) Shield_off(); +#ifdef TOURIST + else if (obj == uarmu) (void) Shirt_off(); +#endif + /* catchall -- should never happen */ + else setworn((struct obj *)0, obj->owornmask & W_ARMOR); + } else if (obj->owornmask & W_AMUL) { + Amulet_off(); + } else if (obj->owornmask & W_RING) { + Ring_gone(obj); + } else if (obj->owornmask & W_TOOL) { + Blindf_off(obj); + } else if (obj->owornmask & (W_WEP|W_SWAPWEP|W_QUIVER)) { + if (obj == uwep) + uwepgone(); + if (obj == uswapwep) + uswapwepgone(); + if (obj == uquiver) + uqwepgone(); + } + + if (obj->owornmask & (W_BALL|W_CHAIN)) { + if (unchain_ball) unpunish(); + } else if (obj->owornmask) { + /* catchall */ + setnotworn(obj); + } +} + +/* Returns 1 when something was stolen (or at least, when N should flee now) + * Returns -1 if the monster died in the attempt + * Avoid stealing the object stealoid + */ +int +steal(mtmp, objnambuf) +struct monst *mtmp; +char *objnambuf; +{ + struct obj *otmp; + int tmp, could_petrify, named = 0, armordelay; + boolean monkey_business; /* true iff an animal is doing the thievery */ + + if (objnambuf) *objnambuf = '\0'; + /* the following is true if successful on first of two attacks. */ + if(!monnear(mtmp, u.ux, u.uy)) return(0); + + /* food being eaten might already be used up but will not have + been removed from inventory yet; we don't want to steal that, + so this will cause it to be removed now */ + if (occupation) (void) maybe_finished_meal(FALSE); + + if (!invent || (inv_cnt() == 1 && uskin)) { +nothing_to_steal: + /* Not even a thousand men in armor can strip a naked man. */ + if(Blind) + pline("Somebody tries to rob you, but finds nothing to steal."); + else + pline("%s tries to rob you, but there is nothing to steal!", + Monnam(mtmp)); + return(1); /* let her flee */ + } + + monkey_business = is_animal(mtmp->data); + if (monkey_business) { + ; /* skip ring special cases */ + } else if (Adornment & LEFT_RING) { + otmp = uleft; + goto gotobj; + } else if (Adornment & RIGHT_RING) { + otmp = uright; + goto gotobj; + } + + tmp = 0; + for(otmp = invent; otmp; otmp = otmp->nobj) + if ((!uarm || otmp != uarmc) && otmp != uskin +#ifdef INVISIBLE_OBJECTS + && (!otmp->oinvis || perceives(mtmp->data)) +#endif + ) + tmp += ((otmp->owornmask & + (W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1); + if (!tmp) goto nothing_to_steal; + tmp = rn2(tmp); + for(otmp = invent; otmp; otmp = otmp->nobj) + if ((!uarm || otmp != uarmc) && otmp != uskin +#ifdef INVISIBLE_OBJECTS + && (!otmp->oinvis || perceives(mtmp->data)) +#endif + ) + if((tmp -= ((otmp->owornmask & + (W_ARMOR | W_RING | W_AMUL | W_TOOL)) ? 5 : 1)) < 0) + break; + if(!otmp) { + impossible("Steal fails!"); + return(0); + } + /* can't steal gloves while wielding - so steal the wielded item. */ + if (otmp == uarmg && uwep) + otmp = uwep; + /* can't steal armor while wearing cloak - so steal the cloak. */ + else if(otmp == uarm && uarmc) otmp = uarmc; +#ifdef TOURIST + else if(otmp == uarmu && uarmc) otmp = uarmc; + else if(otmp == uarmu && uarm) otmp = uarm; +#endif +gotobj: + if(otmp->o_id == stealoid) return(0); + + /* animals can't overcome curse stickiness nor unlock chains */ + if (monkey_business) { + boolean ostuck; + /* is the player prevented from voluntarily giving up this item? + (ignores loadstones; the !can_carry() check will catch those) */ + if (otmp == uball) + ostuck = TRUE; /* effectively worn; curse is implicit */ + else if (otmp == uquiver || (otmp == uswapwep && !u.twoweap)) + ostuck = FALSE; /* not really worn; curse doesn't matter */ + else + ostuck = (otmp->cursed && otmp->owornmask); + + if (ostuck || !can_carry(mtmp, otmp)) { + static const char * const how[] = { "steal","snatch","grab","take" }; + cant_take: + pline("%s tries to %s your %s but gives up.", + Monnam(mtmp), how[rn2(SIZE(how))], + (otmp->owornmask & W_ARMOR) ? equipname(otmp) : + cxname(otmp)); + /* the fewer items you have, the less likely the thief + is going to stick around to try again (0) instead of + running away (1) */ + return !rn2(inv_cnt() / 5 + 2); + } + } + + if (otmp->otyp == LEASH && otmp->leashmon) { + if (monkey_business && otmp->cursed) goto cant_take; + o_unleash(otmp); + } + + /* you're going to notice the theft... */ + stop_occupation(); + + if((otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL))){ + switch(otmp->oclass) { + case TOOL_CLASS: + case AMULET_CLASS: + case RING_CLASS: + case FOOD_CLASS: /* meat ring */ + remove_worn_item(otmp, TRUE); + break; + case ARMOR_CLASS: + armordelay = objects[otmp->otyp].oc_delay; + /* Stop putting on armor which has been stolen. */ + if (donning(otmp)) { + remove_worn_item(otmp, TRUE); + break; + } else if (monkey_business) { + /* animals usually don't have enough patience + to take off items which require extra time */ + if (armordelay >= 1 && rn2(10)) goto cant_take; + remove_worn_item(otmp, TRUE); + break; + } else { + int curssv = otmp->cursed; + int slowly; + boolean seen = canspotmon(mtmp); + + otmp->cursed = 0; + /* can't charm you without first waking you */ + if (multi < 0 && is_fainted()) unmul((char *)0); + slowly = (armordelay >= 1 || multi < 0); + if(flags.female) + pline("%s charms you. You gladly %s your %s.", + !seen ? "She" : Monnam(mtmp), + curssv ? "let her take" : + slowly ? "start removing" : "hand over", + equipname(otmp)); + else + pline("%s seduces you and %s off your %s.", + !seen ? "She" : Adjmonnam(mtmp, "beautiful"), + curssv ? "helps you to take" : + slowly ? "you start taking" : "you take", + equipname(otmp)); + named++; + /* the following is to set multi for later on */ + nomul(-armordelay); + remove_worn_item(otmp, TRUE); + otmp->cursed = curssv; + if(multi < 0){ + /* + multi = 0; + nomovemsg = 0; + afternmv = 0; + */ + stealoid = otmp->o_id; + stealmid = mtmp->m_id; + afternmv = stealarm; + return(0); + } + } + break; + default: + impossible("Tried to steal a strange worn thing. [%d]", + otmp->oclass); + } + } + else if (otmp->owornmask) + remove_worn_item(otmp, TRUE); + + /* do this before removing it from inventory */ + if (objnambuf) Strcpy(objnambuf, yname(otmp)); + /* set mavenge bit so knights won't suffer an + * alignment penalty during retaliation; + */ + mtmp->mavenge = 1; + + freeinv(otmp); + pline("%s stole %s.", named ? "She" : Monnam(mtmp), doname(otmp)); + could_petrify = (otmp->otyp == CORPSE && + touch_petrifies(&mons[otmp->corpsenm])); + (void) mpickobj(mtmp,otmp); /* may free otmp */ + if (could_petrify && !(mtmp->misc_worn_check & W_ARMG)) { + minstapetrify(mtmp, TRUE); + return -1; + } + return((multi < 0) ? 0 : 1); +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* Returns 1 if otmp is free'd, 0 otherwise. */ +int +mpickobj(mtmp,otmp) +register struct monst *mtmp; +register struct obj *otmp; +{ + int freed_otmp; + +#ifndef GOLDOBJ + if (otmp->oclass == COIN_CLASS) { + mtmp->mgold += otmp->quan; + obfree(otmp, (struct obj *)0); + freed_otmp = 1; + } else { +#endif + boolean snuff_otmp = FALSE; + /* don't want hidden light source inside the monster; assumes that + engulfers won't have external inventories; whirly monsters cause + the light to be extinguished rather than letting it shine thru */ + if (otmp->lamplit && /* hack to avoid function calls for most objs */ + obj_sheds_light(otmp) && + attacktype(mtmp->data, AT_ENGL)) { + /* this is probably a burning object that you dropped or threw */ + if (u.uswallow && mtmp == u.ustuck && !Blind) + pline("%s out.", Tobjnam(otmp, "go")); + snuff_otmp = TRUE; + } + /* Must do carrying effects on object prior to add_to_minv() */ + carry_obj_effects(otmp); + /* add_to_minv() might free otmp [if merged with something else], + so we have to call it after doing the object checks */ + freed_otmp = add_to_minv(mtmp, otmp); + /* and we had to defer this until object is in mtmp's inventory */ + if (snuff_otmp) snuff_light_source(mtmp->mx, mtmp->my); +#ifndef GOLDOBJ + } +#endif + return freed_otmp; +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +stealamulet(mtmp) +struct monst *mtmp; +{ + struct obj *otmp = (struct obj *)0; + int real=0, fake=0; + + /* select the artifact to steal */ + if(u.uhave.amulet) { + real = AMULET_OF_YENDOR; + fake = FAKE_AMULET_OF_YENDOR; + } else if(u.uhave.questart) { + for(otmp = invent; otmp; otmp = otmp->nobj) + if(is_quest_artifact(otmp)) break; + if (!otmp) return; /* should we panic instead? */ + } else if(u.uhave.bell) { + real = BELL_OF_OPENING; + fake = BELL; + } else if(u.uhave.book) { + real = SPE_BOOK_OF_THE_DEAD; + } else if(u.uhave.menorah) { + real = CANDELABRUM_OF_INVOCATION; + } else return; /* you have nothing of special interest */ + + if (!otmp) { + /* If we get here, real and fake have been set up. */ + for(otmp = invent; otmp; otmp = otmp->nobj) + if(otmp->otyp == real || (otmp->otyp == fake && !mtmp->iswiz)) + break; + } + + if (otmp) { /* we have something to snatch */ + if (otmp->owornmask) + remove_worn_item(otmp, TRUE); + freeinv(otmp); + /* mpickobj wont merge otmp because none of the above things + to steal are mergable */ + (void) mpickobj(mtmp,otmp); /* may merge and free otmp */ + pline("%s stole %s!", Monnam(mtmp), doname(otmp)); + if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) + (void) rloc(mtmp, FALSE); + } +} + +#endif /* OVLB */ +#ifdef OVL0 + +/* drop one object taken from a (possibly dead) monster's inventory */ +STATIC_OVL void +mdrop_obj(mon, obj, verbosely) +struct monst *mon; +struct obj *obj; +boolean verbosely; +{ + int omx = mon->mx, omy = mon->my; + + if (obj->owornmask) { + /* perform worn item handling if the monster is still alive */ + if (mon->mhp > 0) { + mon->misc_worn_check &= ~obj->owornmask; + update_mon_intrinsics(mon, obj, FALSE, TRUE); + /* obj_no_longer_held(obj); -- done by place_object */ + if (obj->owornmask & W_WEP) setmnotwielded(mon, obj); +#ifdef STEED + /* don't charge for an owned saddle on dead steed */ + } else if (mon->mtame && (obj->owornmask & W_SADDLE) && + !obj->unpaid && costly_spot(omx, omy)) { + obj->no_charge = 1; +#endif + } + obj->owornmask = 0L; + } + if (verbosely && cansee(omx, omy)) + pline("%s drops %s.", Monnam(mon), distant_name(obj, doname)); + if (!flooreffects(obj, omx, omy, "fall")) { + place_object(obj, omx, omy); + stackobj(obj); + } +} + +/* some monsters bypass the normal rules for moving between levels or + even leaving the game entirely; when that happens, prevent them from + taking the Amulet or invocation tools with them */ +void +mdrop_special_objs(mon) +struct monst *mon; +{ + struct obj *obj, *otmp; + + for (obj = mon->minvent; obj; obj = otmp) { + otmp = obj->nobj; + /* the Amulet, invocation tools, and Rider corpses resist even when + artifacts and ordinary objects are given 0% resistance chance */ + if (obj_resists(obj, 0, 0)) { + obj_extract_self(obj); + mdrop_obj(mon, obj, FALSE); + } + } +} + +/* release the objects the creature is carrying */ +void +relobj(mtmp,show,is_pet) +register struct monst *mtmp; +register int show; +boolean is_pet; /* If true, pet should keep wielded/worn items */ +{ + register struct obj *otmp; + register int omx = mtmp->mx, omy = mtmp->my; + struct obj *keepobj = 0; + struct obj *wep = MON_WEP(mtmp); + boolean item1 = FALSE, item2 = FALSE; + + if (!is_pet || mindless(mtmp->data) || is_animal(mtmp->data)) + item1 = item2 = TRUE; + if (!tunnels(mtmp->data) || !needspick(mtmp->data)) + item1 = TRUE; + + while ((otmp = mtmp->minvent) != 0) { + obj_extract_self(otmp); + /* special case: pick-axe and unicorn horn are non-worn */ + /* items that we also want pets to keep 1 of */ + /* (It is a coincidence that these can also be wielded.) */ + if (otmp->owornmask || otmp == wep || + ((!item1 && otmp->otyp == PICK_AXE) || + (!item2 && otmp->otyp == UNICORN_HORN && !otmp->cursed))) { + if (is_pet) { /* dont drop worn/wielded item */ + if (otmp->otyp == PICK_AXE) + item1 = TRUE; + if (otmp->otyp == UNICORN_HORN && !otmp->cursed) + item2 = TRUE; + otmp->nobj = keepobj; + keepobj = otmp; + continue; + } + } + mdrop_obj(mtmp, otmp, is_pet && flags.verbose); + } + + /* put kept objects back */ + while ((otmp = keepobj) != (struct obj *)0) { + keepobj = otmp->nobj; + (void) add_to_minv(mtmp, otmp); + } +#ifndef GOLDOBJ + if (mtmp->mgold) { + register long g = mtmp->mgold; + (void) mkgold(g, omx, omy); + if (is_pet && cansee(omx, omy) && flags.verbose) + pline("%s drops %ld gold piece%s.", Monnam(mtmp), + g, plur(g)); + mtmp->mgold = 0L; + } +#endif + + if (show & cansee(omx, omy)) + newsym(omx, omy); +} + +#endif /* OVL0 */ + +/*steal.c*/ diff --git a/src/steed.c b/src/steed.c new file mode 100644 index 0000000..52e97dc --- /dev/null +++ b/src/steed.c @@ -0,0 +1,641 @@ +/* SCCS Id: @(#)steed.c 3.4 2003/01/10 */ +/* Copyright (c) Kevin Hugo, 1998-1999. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + + +#ifdef STEED + +/* Monsters that might be ridden */ +static NEARDATA const char steeds[] = { + S_QUADRUPED, S_UNICORN, S_ANGEL, S_CENTAUR, S_DRAGON, S_JABBERWOCK, '\0' +}; + +STATIC_DCL boolean FDECL(landing_spot, (coord *, int, int)); + +/* caller has decided that hero can't reach something while mounted */ +void +rider_cant_reach() +{ + You("aren't skilled enough to reach from %s.", y_monnam(u.usteed)); +} + +/*** Putting the saddle on ***/ + +/* Can this monster wear a saddle? */ +boolean +can_saddle(mtmp) + struct monst *mtmp; +{ + struct permonst *ptr = mtmp->data; + + return (index(steeds, ptr->mlet) && (ptr->msize >= MZ_MEDIUM) && + (!humanoid(ptr) || ptr->mlet == S_CENTAUR) && + !amorphous(ptr) && !noncorporeal(ptr) && + !is_whirly(ptr) && !unsolid(ptr)); +} + + +int +use_saddle(otmp) + struct obj *otmp; +{ + struct monst *mtmp; + struct permonst *ptr; + int chance; + const char *s; + + + /* Can you use it? */ + if (nohands(youmonst.data)) { + You("have no hands!"); /* not `body_part(HAND)' */ + return 0; + } else if (!freehand()) { + You("have no free %s.", body_part(HAND)); + return 0; + } + + /* Select an animal */ + if (u.uswallow || Underwater || !getdir((char *)0)) { + pline(Never_mind); + return 0; + } + if (!u.dx && !u.dy) { + pline("Saddle yourself? Very funny..."); + return 0; + } + if (!isok(u.ux+u.dx, u.uy+u.dy) || + !(mtmp = m_at(u.ux+u.dx, u.uy+u.dy)) || + !canspotmon(mtmp)) { + pline("I see nobody there."); + return 1; + } + + /* Is this a valid monster? */ + if (mtmp->misc_worn_check & W_SADDLE || + which_armor(mtmp, W_SADDLE)) { + pline("%s doesn't need another one.", Monnam(mtmp)); + return 1; + } + ptr = mtmp->data; + if (touch_petrifies(ptr) && !uarmg && !Stone_resistance) { + char kbuf[BUFSZ]; + + You("touch %s.", mon_nam(mtmp)); + if (!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) { + Sprintf(kbuf, "attempting to saddle %s", an(mtmp->data->mname)); + instapetrify(kbuf); + } + } + if (ptr == &mons[PM_INCUBUS] || ptr == &mons[PM_SUCCUBUS]) { + pline("Shame on you!"); + exercise(A_WIS, FALSE); + return 1; + } + if (mtmp->isminion || mtmp->isshk || mtmp->ispriest || + mtmp->isgd || mtmp->iswiz) { + pline("I think %s would mind.", mon_nam(mtmp)); + return 1; + } + if (!can_saddle(mtmp)) { + You_cant("saddle such a creature."); + return 1; + } + + /* Calculate your chance */ + chance = ACURR(A_DEX) + ACURR(A_CHA)/2 + 2*mtmp->mtame; + chance += u.ulevel * (mtmp->mtame ? 20 : 5); + if (!mtmp->mtame) chance -= 10*mtmp->m_lev; + if (Role_if(PM_KNIGHT)) + chance += 20; + switch (P_SKILL(P_RIDING)) { + case P_ISRESTRICTED: + case P_UNSKILLED: + default: + chance -= 20; break; + case P_BASIC: + break; + case P_SKILLED: + chance += 15; break; + case P_EXPERT: + chance += 30; break; + } + if (Confusion || Fumbling || Glib) + chance -= 20; + else if (uarmg && + (s = OBJ_DESCR(objects[uarmg->otyp])) != (char *)0 && + !strncmp(s, "riding ", 7)) + /* Bonus for wearing "riding" (but not fumbling) gloves */ + chance += 10; + else if (uarmf && + (s = OBJ_DESCR(objects[uarmf->otyp])) != (char *)0 && + !strncmp(s, "riding ", 7)) + /* ... or for "riding boots" */ + chance += 10; + if (otmp->cursed) + chance -= 50; + + /* Make the attempt */ + if (rn2(100) < chance) { + You("put the saddle on %s.", mon_nam(mtmp)); + if (otmp->owornmask) remove_worn_item(otmp, FALSE); + freeinv(otmp); + /* mpickobj may free otmp it if merges, but we have already + checked for a saddle above, so no merger should happen */ + (void) mpickobj(mtmp, otmp); + mtmp->misc_worn_check |= W_SADDLE; + otmp->owornmask = W_SADDLE; + otmp->leashmon = mtmp->m_id; + update_mon_intrinsics(mtmp, otmp, TRUE, FALSE); + } else + pline("%s resists!", Monnam(mtmp)); + return 1; +} + + +/*** Riding the monster ***/ + +/* Can we ride this monster? Caller should also check can_saddle() */ +boolean +can_ride(mtmp) + struct monst *mtmp; +{ + return (mtmp->mtame && humanoid(youmonst.data) && + !verysmall(youmonst.data) && !bigmonst(youmonst.data) && + (!Underwater || is_swimmer(mtmp->data))); +} + + +int +doride() +{ + boolean forcemount = FALSE; + + if (u.usteed) + dismount_steed(DISMOUNT_BYCHOICE); + else if (getdir((char *)0) && isok(u.ux+u.dx, u.uy+u.dy)) { +#ifdef WIZARD + if (wizard && yn("Force the mount to succeed?") == 'y') + forcemount = TRUE; +#endif + return (mount_steed(m_at(u.ux+u.dx, u.uy+u.dy), forcemount)); + } else + return 0; + return 1; +} + + +/* Start riding, with the given monster */ +boolean +mount_steed(mtmp, force) + struct monst *mtmp; /* The animal */ + boolean force; /* Quietly force this animal */ +{ + struct obj *otmp; + char buf[BUFSZ]; + struct permonst *ptr; + + /* Sanity checks */ + if (u.usteed) { + You("are already riding %s.", mon_nam(u.usteed)); + return (FALSE); + } + + /* Is the player in the right form? */ + if (Hallucination && !force) { + pline("Maybe you should find a designated driver."); + return (FALSE); + } + /* While riding Wounded_legs refers to the steed's, + * not the hero's legs. + * That opens up a potential abuse where the player + * can mount a steed, then dismount immediately to + * heal leg damage, because leg damage is always + * healed upon dismount (Wounded_legs context switch). + * By preventing a hero with Wounded_legs from + * mounting a steed, the potential for abuse is + * minimized, if not eliminated altogether. + */ + if (Wounded_legs) { + Your("%s are in no shape for riding.", makeplural(body_part(LEG))); +#ifdef WIZARD + if (force && wizard && yn("Heal your legs?") == 'y') + HWounded_legs = EWounded_legs = 0; + else +#endif + return (FALSE); + } + + if (Upolyd && (!humanoid(youmonst.data) || verysmall(youmonst.data) || + bigmonst(youmonst.data) || slithy(youmonst.data))) { + You("won't fit on a saddle."); + return (FALSE); + } + if(!force && (near_capacity() > SLT_ENCUMBER)) { + You_cant("do that while carrying so much stuff."); + return (FALSE); + } + + /* Can the player reach and see the monster? */ + if (!mtmp || (!force && ((Blind && !Blind_telepat) || + mtmp->mundetected || + mtmp->m_ap_type == M_AP_FURNITURE || + mtmp->m_ap_type == M_AP_OBJECT))) { + pline("I see nobody there."); + return (FALSE); + } + if (u.uswallow || u.ustuck || u.utrap || Punished || + !test_move(u.ux, u.uy, mtmp->mx-u.ux, mtmp->my-u.uy, TEST_MOVE)) { + if (Punished || !(u.uswallow || u.ustuck || u.utrap)) + You("are unable to swing your %s over.", body_part(LEG)); + else + You("are stuck here for now."); + return (FALSE); + } + + /* Is this a valid monster? */ + otmp = which_armor(mtmp, W_SADDLE); + if (!otmp) { + pline("%s is not saddled.", Monnam(mtmp)); + return (FALSE); + } + ptr = mtmp->data; + if (touch_petrifies(ptr) && !Stone_resistance) { + char kbuf[BUFSZ]; + + You("touch %s.", mon_nam(mtmp)); + Sprintf(kbuf, "attempting to ride %s", an(mtmp->data->mname)); + instapetrify(kbuf); + } + if (!mtmp->mtame || mtmp->isminion) { + pline("I think %s would mind.", mon_nam(mtmp)); + return (FALSE); + } + if (mtmp->mtrapped) { + struct trap *t = t_at(mtmp->mx, mtmp->my); + + You_cant("mount %s while %s's trapped in %s.", + mon_nam(mtmp), mhe(mtmp), + an(defsyms[trap_to_defsym(t->ttyp)].explanation)); + return (FALSE); + } + + if (!force && !Role_if(PM_KNIGHT) && !(--mtmp->mtame)) { + /* no longer tame */ + newsym(mtmp->mx, mtmp->my); + pline("%s resists%s!", Monnam(mtmp), + mtmp->mleashed ? " and its leash comes off" : ""); + if (mtmp->mleashed) m_unleash(mtmp, FALSE); + return (FALSE); + } + if (!force && Underwater && !is_swimmer(ptr)) { + You_cant("ride that creature while under water."); + return (FALSE); + } + if (!can_saddle(mtmp) || !can_ride(mtmp)) { + You_cant("ride such a creature."); + return (0); + } + + /* Is the player impaired? */ + if (!force && !is_floater(ptr) && !is_flyer(ptr) && + Levitation && !Lev_at_will) { + You("cannot reach %s.", mon_nam(mtmp)); + return (FALSE); + } + if (!force && uarm && is_metallic(uarm) && + greatest_erosion(uarm)) { + Your("%s armor is too stiff to be able to mount %s.", + uarm->oeroded ? "rusty" : "corroded", + mon_nam(mtmp)); + return (FALSE); + } + if (!force && (Confusion || Fumbling || Glib || Wounded_legs || + otmp->cursed || (u.ulevel+mtmp->mtame < rnd(MAXULEV/2+5)))) { + if (Levitation) { + pline("%s slips away from you.", Monnam(mtmp)); + return FALSE; + } + You("slip while trying to get on %s.", mon_nam(mtmp)); + + Sprintf(buf, "slipped while mounting %s", + /* "a saddled mumak" or "a saddled pony called Dobbin" */ + x_monnam(mtmp, ARTICLE_A, (char *)0, + SUPPRESS_IT|SUPPRESS_INVISIBLE|SUPPRESS_HALLUCINATION, + TRUE)); + losehp(rn1(5,10), buf, NO_KILLER_PREFIX); + return (FALSE); + } + + /* Success */ + if (!force) { + if (Levitation && !is_floater(ptr) && !is_flyer(ptr)) + /* Must have Lev_at_will at this point */ + pline("%s magically floats up!", Monnam(mtmp)); + You("mount %s.", mon_nam(mtmp)); + } + /* setuwep handles polearms differently when you're mounted */ + if (uwep && is_pole(uwep)) unweapon = FALSE; + u.usteed = mtmp; + remove_monster(mtmp->mx, mtmp->my); + teleds(mtmp->mx, mtmp->my, TRUE); + return (TRUE); +} + + +/* You and your steed have moved */ +void +exercise_steed() +{ + if (!u.usteed) + return; + + /* It takes many turns of riding to exercise skill */ + if (u.urideturns++ >= 100) { + u.urideturns = 0; + use_skill(P_RIDING, 1); + } + return; +} + + +/* The player kicks or whips the steed */ +void +kick_steed() +{ + char He[4]; + if (!u.usteed) + return; + + /* [ALI] Various effects of kicking sleeping/paralyzed steeds */ + if (u.usteed->msleeping || !u.usteed->mcanmove) { + /* We assume a message has just been output of the form + * "You kick ." + */ + Strcpy(He, mhe(u.usteed)); + *He = highc(*He); + if ((u.usteed->mcanmove || u.usteed->mfrozen) && !rn2(2)) { + if (u.usteed->mcanmove) + u.usteed->msleeping = 0; + else if (u.usteed->mfrozen > 2) + u.usteed->mfrozen -= 2; + else { + u.usteed->mfrozen = 0; + u.usteed->mcanmove = 1; + } + if (u.usteed->msleeping || !u.usteed->mcanmove) + pline("%s stirs.", He); + else + pline("%s rouses %sself!", He, mhim(u.usteed)); + } else + pline("%s does not respond.", He); + return; + } + + /* Make the steed less tame and check if it resists */ + if (u.usteed->mtame) u.usteed->mtame--; + if (!u.usteed->mtame && u.usteed->mleashed) m_unleash(u.usteed, TRUE); + if (!u.usteed->mtame || (u.ulevel+u.usteed->mtame < rnd(MAXULEV/2+5))) { + newsym(u.usteed->mx, u.usteed->my); + dismount_steed(DISMOUNT_THROWN); + return; + } + + pline("%s gallops!", Monnam(u.usteed)); + u.ugallop += rn1(20, 30); + return; +} + +/* + * Try to find a dismount point adjacent to the steed's location. + * If all else fails, try enexto(). Use enexto() as a last resort because + * enexto() chooses its point randomly, possibly even outside the + * room's walls, which is not what we want. + * Adapted from mail daemon code. + */ +STATIC_OVL boolean +landing_spot(spot, reason, forceit) +coord *spot; /* landing position (we fill it in) */ +int reason; +int forceit; +{ + int i = 0, x, y, distance, min_distance = -1; + boolean found = FALSE; + struct trap *t; + + /* avoid known traps (i == 0) and boulders, but allow them as a backup */ + if (reason != DISMOUNT_BYCHOICE || Stunned || Confusion || Fumbling) i = 1; + for (; !found && i < 2; ++i) { + for (x = u.ux-1; x <= u.ux+1; x++) + for (y = u.uy-1; y <= u.uy+1; y++) { + if (!isok(x, y) || (x == u.ux && y == u.uy)) continue; + + if (ACCESSIBLE(levl[x][y].typ) && + !MON_AT(x,y) && !closed_door(x,y)) { + distance = distu(x,y); + if (min_distance < 0 || distance < min_distance || + (distance == min_distance && rn2(2))) { + if (i > 0 || (((t = t_at(x, y)) == 0 || !t->tseen) && + (!sobj_at(BOULDER, x, y) || + throws_rocks(youmonst.data)))) { + spot->x = x; + spot->y = y; + min_distance = distance; + found = TRUE; + } + } + } + } + } + + /* If we didn't find a good spot and forceit is on, try enexto(). */ + if (forceit && min_distance < 0 && + !enexto(spot, u.ux, u.uy, youmonst.data)) + return FALSE; + + return found; +} + +/* Stop riding the current steed */ +void +dismount_steed(reason) + int reason; /* Player was thrown off etc. */ +{ + struct monst *mtmp; + struct obj *otmp; + coord cc; + const char *verb = "fall"; + boolean repair_leg_damage = TRUE; + unsigned save_utrap = u.utrap; + boolean have_spot = landing_spot(&cc,reason,0); + + mtmp = u.usteed; /* make a copy of steed pointer */ + /* Sanity check */ + if (!mtmp) /* Just return silently */ + return; + + /* Check the reason for dismounting */ + otmp = which_armor(mtmp, W_SADDLE); + switch (reason) { + case DISMOUNT_THROWN: + verb = "are thrown"; + case DISMOUNT_FELL: + You("%s off of %s!", verb, mon_nam(mtmp)); + if (!have_spot) have_spot = landing_spot(&cc,reason,1); + losehp(rn1(10,10), "riding accident", KILLED_BY_AN); + set_wounded_legs(BOTH_SIDES, (int)HWounded_legs + rn1(5,5)); + repair_leg_damage = FALSE; + break; + case DISMOUNT_POLY: + You("can no longer ride %s.", mon_nam(u.usteed)); + if (!have_spot) have_spot = landing_spot(&cc,reason,1); + break; + case DISMOUNT_ENGULFED: + /* caller displays message */ + break; + case DISMOUNT_BONES: + /* hero has just died... */ + break; + case DISMOUNT_GENERIC: + /* no messages, just make it so */ + break; + case DISMOUNT_BYCHOICE: + default: + if (otmp && otmp->cursed) { + You("can't. The saddle %s cursed.", + otmp->bknown ? "is" : "seems to be"); + otmp->bknown = TRUE; + return; + } + if (!have_spot) { + You("can't. There isn't anywhere for you to stand."); + return; + } + if (!mtmp->mnamelth) { + pline("You've been through the dungeon on %s with no name.", + an(mtmp->data->mname)); + if (Hallucination) + pline("It felt good to get out of the rain."); + } else + You("dismount %s.", mon_nam(mtmp)); + } + /* While riding these refer to the steed's legs + * so after dismounting they refer to the player's + * legs once again. + */ + if (repair_leg_damage) HWounded_legs = EWounded_legs = 0; + + /* Release the steed and saddle */ + u.usteed = 0; + u.ugallop = 0L; + + /* Set player and steed's position. Try moving the player first + unless we're in the midst of creating a bones file. */ + if (reason == DISMOUNT_BONES) { + /* move the steed to an adjacent square */ + if (enexto(&cc, u.ux, u.uy, mtmp->data)) + rloc_to(mtmp, cc.x, cc.y); + else /* evidently no room nearby; move steed elsewhere */ + (void) rloc(mtmp, FALSE); + return; + } + if (!DEADMONSTER(mtmp)) { + place_monster(mtmp, u.ux, u.uy); + if (!u.uswallow && !u.ustuck && have_spot) { + struct permonst *mdat = mtmp->data; + + /* The steed may drop into water/lava */ + if (!is_flyer(mdat) && !is_floater(mdat) && !is_clinger(mdat)) { + if (is_pool(u.ux, u.uy)) { + if (!Underwater) + pline("%s falls into the %s!", Monnam(mtmp), + surface(u.ux, u.uy)); + if (!is_swimmer(mdat) && !amphibious(mdat)) { + killed(mtmp); + adjalign(-1); + } + } else if (is_lava(u.ux, u.uy)) { + pline("%s is pulled into the lava!", Monnam(mtmp)); + if (!likes_lava(mdat)) { + killed(mtmp); + adjalign(-1); + } + } + } + /* Steed dismounting consists of two steps: being moved to another + * square, and descending to the floor. We have functions to do + * each of these activities, but they're normally called + * individually and include an attempt to look at or pick up the + * objects on the floor: + * teleds() --> spoteffects() --> pickup() + * float_down() --> pickup() + * We use this kludge to make sure there is only one such attempt. + * + * Clearly this is not the best way to do it. A full fix would + * involve having these functions not call pickup() at all, instead + * calling them first and calling pickup() afterwards. But it + * would take a lot of work to keep this change from having any + * unforseen side effects (for instance, you would no longer be + * able to walk onto a square with a hole, and autopickup before + * falling into the hole). + */ + /* [ALI] No need to move the player if the steed died. */ + if (!DEADMONSTER(mtmp)) { + /* Keep steed here, move the player to cc; + * teleds() clears u.utrap + */ + in_steed_dismounting = TRUE; + teleds(cc.x, cc.y, TRUE); + in_steed_dismounting = FALSE; + + /* Put your steed in your trap */ + if (save_utrap) + (void) mintrap(mtmp); + } + /* Couldn't... try placing the steed */ + } else if (enexto(&cc, u.ux, u.uy, mtmp->data)) { + /* Keep player here, move the steed to cc */ + rloc_to(mtmp, cc.x, cc.y); + /* Player stays put */ + /* Otherwise, kill the steed */ + } else { + killed(mtmp); + adjalign(-1); + } + } + + /* Return the player to the floor */ + if (reason != DISMOUNT_ENGULFED) { + in_steed_dismounting = TRUE; + (void) float_down(0L, W_SADDLE); + in_steed_dismounting = FALSE; + flags.botl = 1; + (void)encumber_msg(); + vision_full_recalc = 1; + } else + flags.botl = 1; + /* polearms behave differently when not mounted */ + if (uwep && is_pole(uwep)) unweapon = TRUE; + return; +} + +void +place_monster(mon, x, y) +struct monst *mon; +int x, y; +{ + if (mon == u.usteed || + /* special case is for convoluted vault guard handling */ + (DEADMONSTER(mon) && !(mon->isgd && x == 0 && y == 0))) { + impossible("placing %s onto map?", + (mon == u.usteed) ? "steed" : "defunct monster"); + return; + } + mon->mx = x, mon->my = y; + level.monsters[x][y] = mon; +} + +#endif /* STEED */ + +/*steed.c*/ diff --git a/src/teleport.c b/src/teleport.c new file mode 100644 index 0000000..c913e3c --- /dev/null +++ b/src/teleport.c @@ -0,0 +1,1291 @@ +/* SCCS Id: @(#)teleport.c 3.4 2003/08/11 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL boolean FDECL(tele_jump_ok, (int,int,int,int)); +STATIC_DCL boolean FDECL(teleok, (int,int,BOOLEAN_P)); +STATIC_DCL void NDECL(vault_tele); +STATIC_DCL boolean FDECL(rloc_pos_ok, (int,int,struct monst *)); +STATIC_DCL void FDECL(mvault_tele, (struct monst *)); + +/* + * Is (x,y) a good position of mtmp? If mtmp is NULL, then is (x,y) good + * for an object? + * + * This function will only look at mtmp->mdat, so makemon, mplayer, etc can + * call it to generate new monster positions with fake monster structures. + */ +boolean +goodpos(x, y, mtmp, gpflags) +int x,y; +struct monst *mtmp; +unsigned gpflags; +{ + struct permonst *mdat = NULL; + boolean ignorewater = ((gpflags & MM_IGNOREWATER) != 0); + + if (!isok(x, y)) return FALSE; + + /* in many cases, we're trying to create a new monster, which + * can't go on top of the player or any existing monster. + * however, occasionally we are relocating engravings or objects, + * which could be co-located and thus get restricted a bit too much. + * oh well. + */ + if (mtmp != &youmonst && x == u.ux && y == u.uy +#ifdef STEED + && (!u.usteed || mtmp != u.usteed) +#endif + ) + return FALSE; + + if (mtmp) { + struct monst *mtmp2 = m_at(x,y); + + /* Be careful with long worms. A monster may be placed back in + * its own location. Normally, if m_at() returns the same monster + * that we're trying to place, the monster is being placed in its + * own location. However, that is not correct for worm segments, + * because all the segments of the worm return the same m_at(). + * Actually we overdo the check a little bit--a worm can't be placed + * in its own location, period. If we just checked for mtmp->mx + * != x || mtmp->my != y, we'd miss the case where we're called + * to place the worm segment and the worm's head is at x,y. + */ + if (mtmp2 && (mtmp2 != mtmp || mtmp->wormno)) + return FALSE; + + mdat = mtmp->data; + if (is_pool(x,y) && !ignorewater) { + if (mtmp == &youmonst) + return !!(HLevitation || Flying || Wwalking || + Swimming || Amphibious); + else return (is_flyer(mdat) || is_swimmer(mdat) || + is_clinger(mdat)); + } else if (mdat->mlet == S_EEL && rn2(13) && !ignorewater) { + return FALSE; + } else if (is_lava(x,y)) { + if (mtmp == &youmonst) + return !!HLevitation; + else + return (is_flyer(mdat) || likes_lava(mdat)); + } + if (passes_walls(mdat) && may_passwall(x,y)) return TRUE; + } + if (!ACCESSIBLE(levl[x][y].typ)) { + if (!(is_pool(x,y) && ignorewater)) return FALSE; + } + + if (closed_door(x, y) && (!mdat || !amorphous(mdat))) + return FALSE; + if (sobj_at(BOULDER, x, y) && (!mdat || !throws_rocks(mdat))) + return FALSE; + return TRUE; +} + +/* + * "entity next to" + * + * Attempt to find a good place for the given monster type in the closest + * position to (xx,yy). Do so in successive square rings around (xx,yy). + * If there is more than one valid positon in the ring, choose one randomly. + * Return TRUE and the position chosen when successful, FALSE otherwise. + */ +boolean +enexto(cc, xx, yy, mdat) +coord *cc; +register xchar xx, yy; +struct permonst *mdat; +{ + return enexto_core(cc, xx, yy, mdat, 0); +} + +boolean +enexto_core(cc, xx, yy, mdat, entflags) +coord *cc; +register xchar xx, yy; +struct permonst *mdat; +unsigned entflags; +{ +#define MAX_GOOD 15 + coord good[MAX_GOOD], *good_ptr; + int x, y, range, i; + int xmin, xmax, ymin, ymax; + struct monst fakemon; /* dummy monster */ + + if (!mdat) { +#ifdef DEBUG + pline("enexto() called with mdat==0"); +#endif + /* default to player's original monster type */ + mdat = &mons[u.umonster]; + } + fakemon.data = mdat; /* set up for goodpos */ + good_ptr = good; + range = 1; + /* + * Walk around the border of the square with center (xx,yy) and + * radius range. Stop when we find at least one valid position. + */ + do { + xmin = max(1, xx-range); + xmax = min(COLNO-1, xx+range); + ymin = max(0, yy-range); + ymax = min(ROWNO-1, yy+range); + + for (x = xmin; x <= xmax; x++) + if (goodpos(x, ymin, &fakemon, entflags)) { + good_ptr->x = x; + good_ptr->y = ymin ; + /* beware of accessing beyond segment boundaries.. */ + if (good_ptr++ == &good[MAX_GOOD-1]) goto full; + } + for (x = xmin; x <= xmax; x++) + if (goodpos(x, ymax, &fakemon, entflags)) { + good_ptr->x = x; + good_ptr->y = ymax ; + /* beware of accessing beyond segment boundaries.. */ + if (good_ptr++ == &good[MAX_GOOD-1]) goto full; + } + for (y = ymin+1; y < ymax; y++) + if (goodpos(xmin, y, &fakemon, entflags)) { + good_ptr->x = xmin; + good_ptr-> y = y ; + /* beware of accessing beyond segment boundaries.. */ + if (good_ptr++ == &good[MAX_GOOD-1]) goto full; + } + for (y = ymin+1; y < ymax; y++) + if (goodpos(xmax, y, &fakemon, entflags)) { + good_ptr->x = xmax; + good_ptr->y = y ; + /* beware of accessing beyond segment boundaries.. */ + if (good_ptr++ == &good[MAX_GOOD-1]) goto full; + } + range++; + + /* return if we've grown too big (nothing is valid) */ + if (range > ROWNO && range > COLNO) return FALSE; + } while (good_ptr == good); + +full: + i = rn2((int)(good_ptr - good)); + cc->x = good[i].x; + cc->y = good[i].y; + return TRUE; +} + +/* + * Check for restricted areas present in some special levels. (This might + * need to be augmented to allow deliberate passage in wizard mode, but + * only for explicitly chosen destinations.) + */ +STATIC_OVL boolean +tele_jump_ok(x1, y1, x2, y2) +int x1, y1, x2, y2; +{ + if (dndest.nlx > 0) { + /* if inside a restricted region, can't teleport outside */ + if (within_bounded_area(x1, y1, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy) && + !within_bounded_area(x2, y2, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy)) + return FALSE; + /* and if outside, can't teleport inside */ + if (!within_bounded_area(x1, y1, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy) && + within_bounded_area(x2, y2, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy)) + return FALSE; + } + if (updest.nlx > 0) { /* ditto */ + if (within_bounded_area(x1, y1, updest.nlx, updest.nly, + updest.nhx, updest.nhy) && + !within_bounded_area(x2, y2, updest.nlx, updest.nly, + updest.nhx, updest.nhy)) + return FALSE; + if (!within_bounded_area(x1, y1, updest.nlx, updest.nly, + updest.nhx, updest.nhy) && + within_bounded_area(x2, y2, updest.nlx, updest.nly, + updest.nhx, updest.nhy)) + return FALSE; + } + return TRUE; +} + +STATIC_OVL boolean +teleok(x, y, trapok) +register int x, y; +boolean trapok; +{ + if (!trapok && t_at(x, y)) return FALSE; + if (!goodpos(x, y, &youmonst, 0)) return FALSE; + if (!tele_jump_ok(u.ux, u.uy, x, y)) return FALSE; + if (!in_out_region(x, y)) return FALSE; + return TRUE; +} + +void +teleds(nux, nuy, allow_drag) +register int nux,nuy; +boolean allow_drag; +{ + boolean ball_active = (Punished && uball->where != OBJ_FREE), + ball_still_in_range = FALSE; + + /* If they have to move the ball, then drag if allow_drag is true; + * otherwise they are teleporting, so unplacebc(). + * If they don't have to move the ball, then always "drag" whether or + * not allow_drag is true, because we are calling that function, not + * to drag, but to move the chain. *However* there are some dumb + * special cases: + * 0 0 + * _X move east -----> X_ + * @ @ + * These are permissible if teleporting, but not if dragging. As a + * result, drag_ball() needs to know about allow_drag and might end + * up dragging the ball anyway. Also, drag_ball() might find that + * dragging the ball is completely impossible (ball in range but there's + * rock in the way), in which case it teleports the ball on its own. + */ + if (ball_active) { + if (!carried(uball) && distmin(nux, nuy, uball->ox, uball->oy) <= 2) + ball_still_in_range = TRUE; /* don't have to move the ball */ + else { + /* have to move the ball */ + if (!allow_drag || distmin(u.ux, u.uy, nux, nuy) > 1) { + /* we should not have dist > 1 and allow_drag at the same + * time, but just in case, we must then revert to teleport. + */ + allow_drag = FALSE; + unplacebc(); + } + } + } + u.utrap = 0; + u.ustuck = 0; + u.ux0 = u.ux; + u.uy0 = u.uy; + + if (hides_under(youmonst.data)) + u.uundetected = OBJ_AT(nux, nuy); + else if (youmonst.data->mlet == S_EEL) + u.uundetected = is_pool(nux, nuy); + else { + u.uundetected = 0; + /* mimics stop being unnoticed */ + if (youmonst.data->mlet == S_MIMIC) + youmonst.m_ap_type = M_AP_NOTHING; + } + + if (u.uswallow) { + u.uswldtim = u.uswallow = 0; + if (Punished && !ball_active) { + /* ensure ball placement, like unstuck */ + ball_active = TRUE; + allow_drag = FALSE; + } + docrt(); + } + if (ball_active) { + if (ball_still_in_range || allow_drag) { + int bc_control; + xchar ballx, bally, chainx, chainy; + boolean cause_delay; + + if (drag_ball(nux, nuy, &bc_control, &ballx, &bally, + &chainx, &chainy, &cause_delay, allow_drag)) + move_bc(0, bc_control, ballx, bally, chainx, chainy); + } + } + /* must set u.ux, u.uy after drag_ball(), which may need to know + the old position if allow_drag is true... */ + u.ux = nux; + u.uy = nuy; + fill_pit(u.ux0, u.uy0); + if (ball_active) { + if (!ball_still_in_range && !allow_drag) + placebc(); + } + initrack(); /* teleports mess up tracking monsters without this */ + update_player_regions(); +#ifdef STEED + /* Move your steed, too */ + if (u.usteed) { + u.usteed->mx = nux; + u.usteed->my = nuy; + } +#endif + /* + * Make sure the hero disappears from the old location. This will + * not happen if she is teleported within sight of her previous + * location. Force a full vision recalculation because the hero + * is now in a new location. + */ + newsym(u.ux0,u.uy0); + see_monsters(); + vision_full_recalc = 1; + nomul(0); + vision_recalc(0); /* vision before effects */ + spoteffects(TRUE); + invocation_message(); +} + +boolean +safe_teleds(allow_drag) +boolean allow_drag; +{ + register int nux, nuy, tcnt = 0; + + do { + nux = rnd(COLNO-1); + nuy = rn2(ROWNO); + } while (!teleok(nux, nuy, (boolean)(tcnt > 200)) && ++tcnt <= 400); + + if (tcnt <= 400) { + teleds(nux, nuy, allow_drag); + return TRUE; + } else + return FALSE; +} + +STATIC_OVL void +vault_tele() +{ + register struct mkroom *croom = search_special(VAULT); + coord c; + + if (croom && somexy(croom, &c) && teleok(c.x,c.y,FALSE)) { + teleds(c.x,c.y,FALSE); + return; + } + tele(); +} + +boolean +teleport_pet(mtmp, force_it) +register struct monst *mtmp; +boolean force_it; +{ + register struct obj *otmp; + +#ifdef STEED + if (mtmp == u.usteed) + return (FALSE); +#endif + + if (mtmp->mleashed) { + otmp = get_mleash(mtmp); + if (!otmp) { + impossible("%s is leashed, without a leash.", Monnam(mtmp)); + goto release_it; + } + if (otmp->cursed && !force_it) { + yelp(mtmp); + return FALSE; + } else { + Your("leash goes slack."); + release_it: + m_unleash(mtmp, FALSE); + return TRUE; + } + } + return TRUE; +} + +void +tele() +{ + coord cc; + + /* Disable teleportation in stronghold && Vlad's Tower */ + if (level.flags.noteleport) { +#ifdef WIZARD + if (!wizard) { +#endif + pline("A mysterious force prevents you from teleporting!"); + return; +#ifdef WIZARD + } +#endif + } + + /* don't show trap if "Sorry..." */ + if (!Blinded) make_blinded(0L,FALSE); + + if ((u.uhave.amulet || On_W_tower_level(&u.uz)) && !rn2(3)) { + You_feel("disoriented for a moment."); + return; + } + if ((Teleport_control && !Stunned) +#ifdef WIZARD + || wizard +#endif + ) { + if (unconscious()) { + pline("Being unconscious, you cannot control your teleport."); + } else { +#ifdef STEED + char buf[BUFSZ]; + if (u.usteed) Sprintf(buf," and %s", mon_nam(u.usteed)); +#endif + pline("To what position do you%s want to be teleported?", +#ifdef STEED + u.usteed ? buf : +#endif + ""); + cc.x = u.ux; + cc.y = u.uy; + if (getpos(&cc, TRUE, "the desired position") < 0) + return; /* abort */ + /* possible extensions: introduce a small error if + magic power is low; allow transfer to solid rock */ + if (teleok(cc.x, cc.y, FALSE)) { + teleds(cc.x, cc.y, FALSE); + return; + } + pline("Sorry..."); + } + } + + (void) safe_teleds(FALSE); +} + +int +dotele() +{ + struct trap *trap; + + trap = t_at(u.ux, u.uy); + if (trap && (!trap->tseen || trap->ttyp != TELEP_TRAP)) + trap = 0; + + if (trap) { + if (trap->once) { + pline("This is a vault teleport, usable once only."); + if (yn("Jump in?") == 'n') + trap = 0; + else { + deltrap(trap); + newsym(u.ux, u.uy); + } + } + if (trap) + You("%s onto the teleportation trap.", + locomotion(youmonst.data, "jump")); + } + if (!trap) { + boolean castit = FALSE; + register int sp_no = 0, energy = 0; + + if (!Teleportation || (u.ulevel < (Role_if(PM_WIZARD) ? 8 : 12) + && !can_teleport(youmonst.data))) { + /* Try to use teleport away spell. */ + if (objects[SPE_TELEPORT_AWAY].oc_name_known && !Confusion) + for (sp_no = 0; sp_no < MAXSPELL; sp_no++) + if (spl_book[sp_no].sp_id == SPE_TELEPORT_AWAY) { + castit = TRUE; + break; + } +#ifdef WIZARD + if (!wizard) { +#endif + if (!castit) { + if (!Teleportation) + You("don't know that spell."); + else You("are not able to teleport at will."); + return(0); + } +#ifdef WIZARD + } +#endif + } + + if (u.uhunger <= 100 || ACURR(A_STR) < 6) { +#ifdef WIZARD + if (!wizard) { +#endif + You("lack the strength %s.", + castit ? "for a teleport spell" : "to teleport"); + return 1; +#ifdef WIZARD + } +#endif + } + + energy = objects[SPE_TELEPORT_AWAY].oc_level * 7 / 2 - 2; + if (u.uen <= energy) { +#ifdef WIZARD + if (wizard) + energy = u.uen; + else +#endif + { + You("lack the energy %s.", + castit ? "for a teleport spell" : "to teleport"); + return 1; + } + } + + if (check_capacity( + "Your concentration falters from carrying so much.")) + return 1; + + if (castit) { + exercise(A_WIS, TRUE); + if (spelleffects(sp_no, TRUE)) + return(1); + else +#ifdef WIZARD + if (!wizard) +#endif + return(0); + } else { + u.uen -= energy; + flags.botl = 1; + } + } + + if (next_to_u()) { + if (trap && trap->once) vault_tele(); + else tele(); + (void) next_to_u(); + } else { + You(shudder_for_moment); + return(0); + } + if (!trap) morehungry(100); + return(1); +} + + +void +level_tele() +{ + register int newlev; + d_level newlevel; + const char *escape_by_flying = 0; /* when surviving dest of -N */ + char buf[BUFSZ]; + boolean force_dest = FALSE; + + if ((u.uhave.amulet || In_endgame(&u.uz) || In_sokoban(&u.uz)) +#ifdef WIZARD + && !wizard +#endif + ) { + You_feel("very disoriented for a moment."); + return; + } + if ((Teleport_control && !Stunned) +#ifdef WIZARD + || wizard +#endif + ) { + char qbuf[BUFSZ]; + int trycnt = 0; + + Strcpy(qbuf, "To what level do you want to teleport?"); + do { + if (++trycnt == 2) { +#ifdef WIZARD + if (wizard) Strcat(qbuf, " [type a number or ? for a menu]"); + else +#endif + Strcat(qbuf, " [type a number]"); + } + getlin(qbuf, buf); + if (!strcmp(buf,"\033")) { /* cancelled */ + if (Confusion && rnl(5)) { + pline("Oops..."); + goto random_levtport; + } + return; + } else if (!strcmp(buf,"*")) { + goto random_levtport; + } else if (Confusion && rnl(5)) { + pline("Oops..."); + goto random_levtport; + } +#ifdef WIZARD + if (wizard && !strcmp(buf,"?")) { + schar destlev = 0; + xchar destdnum = 0; + + if ((newlev = (int)print_dungeon(TRUE, &destlev, &destdnum))) { + newlevel.dnum = destdnum; + newlevel.dlevel = destlev; + if (In_endgame(&newlevel) && !In_endgame(&u.uz)) { + Sprintf(buf, + "Destination is earth level"); + if (!u.uhave.amulet) { + struct obj *obj; + obj = mksobj(AMULET_OF_YENDOR, + TRUE, FALSE); + if (obj) { + obj = addinv(obj); + Strcat(buf, " with the amulet"); + } + } + assign_level(&newlevel, &earth_level); + pline("%s.", buf); + } + force_dest = TRUE; + } else return; + } else +#endif + if ((newlev = lev_by_name(buf)) == 0) newlev = atoi(buf); + } while (!newlev && !digit(buf[0]) && + (buf[0] != '-' || !digit(buf[1])) && + trycnt < 10); + + /* no dungeon escape via this route */ + if (newlev == 0) { + if (trycnt >= 10) + goto random_levtport; + if (ynq("Go to Nowhere. Are you sure?") != 'y') return; + You("%s in agony as your body begins to warp...", + is_silent(youmonst.data) ? "writhe" : "scream"); + display_nhwindow(WIN_MESSAGE, FALSE); + You("cease to exist."); + if (invent) Your("possessions land on the %s with a thud.", + surface(u.ux, u.uy)); + killer_format = NO_KILLER_PREFIX; + killer = "committed suicide"; + done(DIED); + pline("An energized cloud of dust begins to coalesce."); + Your("body rematerializes%s.", invent ? + ", and you gather up all your possessions" : ""); + return; + } + + /* if in Knox and the requested level > 0, stay put. + * we let negative values requests fall into the "heaven" loop. + */ + if (Is_knox(&u.uz) && newlev > 0) { + You(shudder_for_moment); + return; + } + /* if in Quest, the player sees "Home 1", etc., on the status + * line, instead of the logical depth of the level. controlled + * level teleport request is likely to be relativized to the + * status line, and consequently it should be incremented to + * the value of the logical depth of the target level. + * + * we let negative values requests fall into the "heaven" loop. + */ + if (In_quest(&u.uz) && newlev > 0) + newlev = newlev + dungeons[u.uz.dnum].depth_start - 1; + } else { /* involuntary level tele */ + random_levtport: + newlev = random_teleport_level(); + if (newlev == depth(&u.uz)) { + You(shudder_for_moment); + return; + } + } + + if (!next_to_u()) { + You(shudder_for_moment); + return; + } +#ifdef WIZARD + if (In_endgame(&u.uz)) { /* must already be wizard */ + int llimit = dunlevs_in_dungeon(&u.uz); + + if (newlev >= 0 || newlev <= -llimit) { + You_cant("get there from here."); + return; + } + newlevel.dnum = u.uz.dnum; + newlevel.dlevel = llimit + newlev; + schedule_goto(&newlevel, FALSE, FALSE, 0, (char *)0, (char *)0); + return; + } +#endif + + killer = 0; /* still alive, so far... */ + + if (newlev < 0 && !force_dest) { + if (*u.ushops0) { + /* take unpaid inventory items off of shop bills */ + in_mklev = TRUE; /* suppress map update */ + u_left_shop(u.ushops0, TRUE); + /* you're now effectively out of the shop */ + *u.ushops0 = *u.ushops = '\0'; + in_mklev = FALSE; + } + if (newlev <= -10) { + You("arrive in heaven."); + verbalize("Thou art early, but we'll admit thee."); + killer_format = NO_KILLER_PREFIX; + killer = "went to heaven prematurely"; + } else if (newlev == -9) { + You_feel("deliriously happy. "); + pline("(In fact, you're on Cloud 9!) "); + display_nhwindow(WIN_MESSAGE, FALSE); + } else + You("are now high above the clouds..."); + + if (killer) { + ; /* arrival in heaven is pending */ + } else if (Levitation) { + escape_by_flying = "float gently down to earth"; + } else if (Flying) { + escape_by_flying = "fly down to the ground"; + } else { + pline("Unfortunately, you don't know how to fly."); + You("plummet a few thousand feet to your death."); + Sprintf(buf, + "teleported out of the dungeon and fell to %s death", + uhis()); + killer = buf; + killer_format = NO_KILLER_PREFIX; + } + } + + if (killer) { /* the chosen destination was not survivable */ + d_level lsav; + + /* set specific death location; this also suppresses bones */ + lsav = u.uz; /* save current level, see below */ + u.uz.dnum = 0; /* main dungeon */ + u.uz.dlevel = (newlev <= -10) ? -10 : 0; /* heaven or surface */ + done(DIED); + /* can only get here via life-saving (or declining to die in + explore|debug mode); the hero has now left the dungeon... */ + escape_by_flying = "find yourself back on the surface"; + u.uz = lsav; /* restore u.uz so escape code works */ + } + + /* calls done(ESCAPED) if newlevel==0 */ + if (escape_by_flying) { + You("%s.", escape_by_flying); + newlevel.dnum = 0; /* specify main dungeon */ + newlevel.dlevel = 0; /* escape the dungeon */ + /* [dlevel used to be set to 1, but it doesn't make sense to + teleport out of the dungeon and float or fly down to the + surface but then actually arrive back inside the dungeon] */ + } else if (u.uz.dnum == medusa_level.dnum && + newlev >= dungeons[u.uz.dnum].depth_start + + dunlevs_in_dungeon(&u.uz)) { +#ifdef WIZARD + if (!(wizard && force_dest)) +#endif + find_hell(&newlevel); + } else { + /* if invocation did not yet occur, teleporting into + * the last level of Gehennom is forbidden. + */ +#ifdef WIZARD + if (!wizard) +#endif + if (Inhell && !u.uevent.invoked && + newlev >= (dungeons[u.uz.dnum].depth_start + + dunlevs_in_dungeon(&u.uz) - 1)) { + newlev = dungeons[u.uz.dnum].depth_start + + dunlevs_in_dungeon(&u.uz) - 2; + pline("Sorry..."); + } + /* no teleporting out of quest dungeon */ + if (In_quest(&u.uz) && newlev < depth(&qstart_level)) + newlev = depth(&qstart_level); + /* the player thinks of levels purely in logical terms, so + * we must translate newlev to a number relative to the + * current dungeon. + */ +#ifdef WIZARD + if (!(wizard && force_dest)) +#endif + get_level(&newlevel, newlev); + } + schedule_goto(&newlevel, FALSE, FALSE, 0, (char *)0, (char *)0); + /* in case player just read a scroll and is about to be asked to + call it something, we can't defer until the end of the turn */ + if (u.utotype && !flags.mon_moving) deferred_goto(); +} + +void +domagicportal(ttmp) +register struct trap *ttmp; +{ + struct d_level target_level; + + if (!next_to_u()) { + You(shudder_for_moment); + return; + } + + /* if landed from another portal, do nothing */ + /* problem: level teleport landing escapes the check */ + if (!on_level(&u.uz, &u.uz0)) return; + + You("activated a magic portal!"); + + /* prevent the poor shnook, whose amulet was stolen while in + * the endgame, from accidently triggering the portal to the + * next level, and thus losing the game + */ + if (In_endgame(&u.uz) && !u.uhave.amulet) { + You_feel("dizzy for a moment, but nothing happens..."); + return; + } + + target_level = ttmp->dst; + schedule_goto(&target_level, FALSE, FALSE, 1, + "You feel dizzy for a moment, but the sensation passes.", + (char *)0); +} + +void +tele_trap(trap) +struct trap *trap; +{ + if (In_endgame(&u.uz) || Antimagic) { + if (Antimagic) + shieldeff(u.ux, u.uy); + You_feel("a wrenching sensation."); + } else if (!next_to_u()) { + You(shudder_for_moment); + } else if (trap->once) { + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + vault_tele(); + } else + tele(); +} + +void +level_tele_trap(trap) +struct trap *trap; +{ + You("%s onto a level teleport trap!", + Levitation ? (const char *)"float" : + locomotion(youmonst.data, "step")); + if (Antimagic) { + shieldeff(u.ux, u.uy); + } + if (Antimagic || In_endgame(&u.uz)) { + You_feel("a wrenching sensation."); + return; + } + if (!Blind) + You("are momentarily blinded by a flash of light."); + else + You("are momentarily disoriented."); + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + level_tele(); +} + +/* check whether monster can arrive at location via Tport (or fall) */ +STATIC_OVL boolean +rloc_pos_ok(x, y, mtmp) +register int x, y; /* coordinates of candidate location */ +struct monst *mtmp; +{ + register int xx, yy; + + if (!goodpos(x, y, mtmp, 0)) return FALSE; + /* + * Check for restricted areas present in some special levels. + * + * `xx' is current column; if 0, then `yy' will contain flag bits + * rather than row: bit #0 set => moving upwards; bit #1 set => + * inside the Wizard's tower. + */ + xx = mtmp->mx; + yy = mtmp->my; + if (!xx) { + /* no current location (migrating monster arrival) */ + if (dndest.nlx && On_W_tower_level(&u.uz)) + return ((yy & 2) != 0) ^ /* inside xor not within */ + !within_bounded_area(x, y, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy); + if (updest.lx && (yy & 1) != 0) /* moving up */ + return (within_bounded_area(x, y, updest.lx, updest.ly, + updest.hx, updest.hy) && + (!updest.nlx || + !within_bounded_area(x, y, updest.nlx, updest.nly, + updest.nhx, updest.nhy))); + if (dndest.lx && (yy & 1) == 0) /* moving down */ + return (within_bounded_area(x, y, dndest.lx, dndest.ly, + dndest.hx, dndest.hy) && + (!dndest.nlx || + !within_bounded_area(x, y, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy))); + } else { + /* current location is */ + if (!tele_jump_ok(xx, yy, x, y)) return FALSE; + } + /* is ok */ + return TRUE; +} + +/* + * rloc_to() + * + * Pulls a monster from its current position and places a monster at + * a new x and y. If oldx is 0, then the monster was not in the levels.monsters + * array. However, if oldx is 0, oldy may still have a value because mtmp is a + * migrating_mon. Worm tails are always placed randomly around the head of + * the worm. + */ +void +rloc_to(mtmp, x, y) +struct monst *mtmp; +register int x, y; +{ + register int oldx = mtmp->mx, oldy = mtmp->my; + boolean resident_shk = mtmp->isshk && inhishop(mtmp); + + if (x == mtmp->mx && y == mtmp->my) /* that was easy */ + return; + + if (oldx) { /* "pick up" monster */ + if (mtmp->wormno) + remove_worm(mtmp); + else { + remove_monster(oldx, oldy); + newsym(oldx, oldy); /* update old location */ + } + } + + place_monster(mtmp, x, y); /* put monster down */ + update_monster_region(mtmp); + + if (mtmp->wormno) /* now put down tail */ + place_worm_tail_randomly(mtmp, x, y); + + if (u.ustuck == mtmp) { + if (u.uswallow) { + u.ux = x; + u.uy = y; + docrt(); + } else u.ustuck = 0; + } + + newsym(x, y); /* update new location */ + set_apparxy(mtmp); /* orient monster */ + + /* shopkeepers will only teleport if you zap them with a wand of + teleportation or if they've been transformed into a jumpy monster; + the latter only happens if you've attacked them with polymorph */ + if (resident_shk && !inhishop(mtmp)) make_angry_shk(mtmp, oldx, oldy); +} + +/* place a monster at a random location, typically due to teleport */ +/* return TRUE if successful, FALSE if not */ +boolean +rloc(mtmp, suppress_impossible) +struct monst *mtmp; /* mx==0 implies migrating monster arrival */ +boolean suppress_impossible; +{ + register int x, y, trycount; + +#ifdef STEED + if (mtmp == u.usteed) { + tele(); + return TRUE; + } +#endif + + if (mtmp->iswiz && mtmp->mx) { /* Wizard, not just arriving */ + if (!In_W_tower(u.ux, u.uy, &u.uz)) + x = xupstair, y = yupstair; + else if (!xdnladder) /* bottom level of tower */ + x = xupladder, y = yupladder; + else + x = xdnladder, y = ydnladder; + /* if the wiz teleports away to heal, try the up staircase, + to block the player's escaping before he's healed + (deliberately use `goodpos' rather than `rloc_pos_ok' here) */ + if (goodpos(x, y, mtmp, 0)) + goto found_xy; + } + + trycount = 0; + do { + x = rn1(COLNO-3,2); + y = rn2(ROWNO); + if ((trycount < 500) ? rloc_pos_ok(x, y, mtmp) + : goodpos(x, y, mtmp, 0)) + goto found_xy; + } while (++trycount < 1000); + + /* last ditch attempt to find a good place */ + for (x = 2; x < COLNO - 1; x++) + for (y = 0; y < ROWNO; y++) + if (goodpos(x, y, mtmp, 0)) + goto found_xy; + + /* level either full of monsters or somehow faulty */ + if (!suppress_impossible) + impossible("rloc(): couldn't relocate monster"); + return FALSE; + + found_xy: + rloc_to(mtmp, x, y); + return TRUE; +} + +STATIC_OVL void +mvault_tele(mtmp) +struct monst *mtmp; +{ + register struct mkroom *croom = search_special(VAULT); + coord c; + + if (croom && somexy(croom, &c) && + goodpos(c.x, c.y, mtmp, 0)) { + rloc_to(mtmp, c.x, c.y); + return; + } + (void) rloc(mtmp, FALSE); +} + +boolean +tele_restrict(mon) +struct monst *mon; +{ + if (level.flags.noteleport) { + if (canseemon(mon)) + pline("A mysterious force prevents %s from teleporting!", + mon_nam(mon)); + return TRUE; + } + return FALSE; +} + +void +mtele_trap(mtmp, trap, in_sight) +struct monst *mtmp; +struct trap *trap; +int in_sight; +{ + char *monname; + + if (tele_restrict(mtmp)) return; + if (teleport_pet(mtmp, FALSE)) { + /* save name with pre-movement visibility */ + monname = Monnam(mtmp); + + /* Note: don't remove the trap if a vault. Other- + * wise the monster will be stuck there, since + * the guard isn't going to come for it... + */ + if (trap->once) mvault_tele(mtmp); + else (void) rloc(mtmp, FALSE); + + if (in_sight) { + if (canseemon(mtmp)) + pline("%s seems disoriented.", monname); + else + pline("%s suddenly disappears!", monname); + seetrap(trap); + } + } +} + +/* return 0 if still on level, 3 if not */ +int +mlevel_tele_trap(mtmp, trap, force_it, in_sight) +struct monst *mtmp; +struct trap *trap; +boolean force_it; +int in_sight; +{ + int tt = trap->ttyp; + struct permonst *mptr = mtmp->data; + + if (mtmp == u.ustuck) /* probably a vortex */ + return 0; /* temporary? kludge */ + if (teleport_pet(mtmp, force_it)) { + d_level tolevel; + int migrate_typ = MIGR_RANDOM; + + if ((tt == HOLE || tt == TRAPDOOR)) { + if (Is_stronghold(&u.uz)) { + assign_level(&tolevel, &valley_level); + } else if (Is_botlevel(&u.uz)) { + if (in_sight && trap->tseen) + pline("%s avoids the %s.", Monnam(mtmp), + (tt == HOLE) ? "hole" : "trap"); + return 0; + } else { + get_level(&tolevel, depth(&u.uz) + 1); + } + } else if (tt == MAGIC_PORTAL) { + if (In_endgame(&u.uz) && + (mon_has_amulet(mtmp) || is_home_elemental(mptr))) { + if (in_sight && mptr->mlet != S_ELEMENTAL) { + pline("%s seems to shimmer for a moment.", + Monnam(mtmp)); + seetrap(trap); + } + return 0; + } else { + assign_level(&tolevel, &trap->dst); + migrate_typ = MIGR_PORTAL; + } + } else { /* (tt == LEVEL_TELEP) */ + int nlev; + + if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) { + if (in_sight) + pline("%s seems very disoriented for a moment.", + Monnam(mtmp)); + return 0; + } + nlev = random_teleport_level(); + if (nlev == depth(&u.uz)) { + if (in_sight) + pline("%s shudders for a moment.", Monnam(mtmp)); + return 0; + } + get_level(&tolevel, nlev); + } + + if (in_sight) { + pline("Suddenly, %s disappears out of sight.", mon_nam(mtmp)); + seetrap(trap); + } + migrate_to_level(mtmp, ledger_no(&tolevel), + migrate_typ, (coord *)0); + return 3; /* no longer on this level */ + } + return 0; +} + + +void +rloco(obj) +register struct obj *obj; +{ + register xchar tx, ty, otx, oty; + boolean restricted_fall; + int try_limit = 4000; + + if (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm])) { + if (revive_corpse(obj)) return; + } + + obj_extract_self(obj); + otx = obj->ox; + oty = obj->oy; + restricted_fall = (otx == 0 && dndest.lx); + do { + tx = rn1(COLNO-3,2); + ty = rn2(ROWNO); + if (!--try_limit) break; + } while (!goodpos(tx, ty, (struct monst *)0, 0) || + /* bug: this lacks provision for handling the Wizard's tower */ + (restricted_fall && + (!within_bounded_area(tx, ty, dndest.lx, dndest.ly, + dndest.hx, dndest.hy) || + (dndest.nlx && + within_bounded_area(tx, ty, dndest.nlx, dndest.nly, + dndest.nhx, dndest.nhy))))); + + if (flooreffects(obj, tx, ty, "fall")) { + return; + } else if (otx == 0 && oty == 0) { + ; /* fell through a trap door; no update of old loc needed */ + } else { + if (costly_spot(otx, oty) + && (!costly_spot(tx, ty) || + !index(in_rooms(tx, ty, 0), *in_rooms(otx, oty, 0)))) { + if (costly_spot(u.ux, u.uy) && + index(u.urooms, *in_rooms(otx, oty, 0))) + addtobill(obj, FALSE, FALSE, FALSE); + else (void)stolen_value(obj, otx, oty, FALSE, FALSE); + } + newsym(otx, oty); /* update old location */ + } + place_object(obj, tx, ty); + newsym(tx, ty); +} + +/* Returns an absolute depth */ +int +random_teleport_level() +{ + int nlev, max_depth, min_depth, + cur_depth = (int)depth(&u.uz); + + if (!rn2(5) || Is_knox(&u.uz)) + return cur_depth; + + /* What I really want to do is as follows: + * -- If in a dungeon that goes down, the new level is to be restricted + * to [top of parent, bottom of current dungeon] + * -- If in a dungeon that goes up, the new level is to be restricted + * to [top of current dungeon, bottom of parent] + * -- If in a quest dungeon or similar dungeon entered by portals, + * the new level is to be restricted to [top of current dungeon, + * bottom of current dungeon] + * The current behavior is not as sophisticated as that ideal, but is + * still better what we used to do, which was like this for players + * but different for monsters for no obvious reason. Currently, we + * must explicitly check for special dungeons. We check for Knox + * above; endgame is handled in the caller due to its different + * message ("disoriented"). + * --KAA + * 3.4.2: explicitly handle quest here too, to fix the problem of + * monsters sometimes level teleporting out of it into main dungeon. + * Also prevent monsters reaching the Sanctum prior to invocation. + */ + min_depth = In_quest(&u.uz) ? dungeons[u.uz.dnum].depth_start : 1; + max_depth = dunlevs_in_dungeon(&u.uz) + + (dungeons[u.uz.dnum].depth_start - 1); + /* can't reach the Sanctum if the invocation hasn't been performed */ + if (Inhell && !u.uevent.invoked) max_depth -= 1; + + /* Get a random value relative to the current dungeon */ + /* Range is 1 to current+3, current not counting */ + nlev = rn2(cur_depth + 3 - min_depth) + min_depth; + if (nlev >= cur_depth) nlev++; + + if (nlev > max_depth) { + nlev = max_depth; + /* teleport up if already on bottom */ + if (Is_botlevel(&u.uz)) nlev -= rnd(3); + } + if (nlev < min_depth) { + nlev = min_depth; + if (nlev == cur_depth) { + nlev += rnd(3); + if (nlev > max_depth) + nlev = max_depth; + } + } + return nlev; +} + +/* you teleport a monster (via wand, spell, or poly'd q.mechanic attack); + return false iff the attempt fails */ +boolean +u_teleport_mon(mtmp, give_feedback) +struct monst *mtmp; +boolean give_feedback; +{ + coord cc; + + if (mtmp->ispriest && *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) { + if (give_feedback) + pline("%s resists your magic!", Monnam(mtmp)); + return FALSE; + } else if (level.flags.noteleport && u.uswallow && mtmp == u.ustuck) { + if (give_feedback) + You("are no longer inside %s!", mon_nam(mtmp)); + unstuck(mtmp); + (void) rloc(mtmp, FALSE); + } else if (is_rider(mtmp->data) && rn2(13) && + enexto(&cc, u.ux, u.uy, mtmp->data)) + rloc_to(mtmp, cc.x, cc.y); + else + (void) rloc(mtmp, FALSE); + return TRUE; +} + +/*teleport.c*/ diff --git a/src/timeout.c b/src/timeout.c new file mode 100644 index 0000000..6a336d6 --- /dev/null +++ b/src/timeout.c @@ -0,0 +1,1856 @@ +/* SCCS Id: @(#)timeout.c 3.4 2002/12/17 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" /* for checking save modes */ + +STATIC_DCL void NDECL(stoned_dialogue); +STATIC_DCL void NDECL(vomiting_dialogue); +STATIC_DCL void NDECL(choke_dialogue); +STATIC_DCL void NDECL(slime_dialogue); +STATIC_DCL void NDECL(slip_or_trip); +STATIC_DCL void FDECL(see_lamp_flicker, (struct obj *, const char *)); +STATIC_DCL void FDECL(lantern_message, (struct obj *)); +STATIC_DCL void FDECL(cleanup_burn, (genericptr_t,long)); + +#ifdef OVLB + +/* He is being petrified - dialogue by inmet!tower */ +static NEARDATA const char * const stoned_texts[] = { + "You are slowing down.", /* 5 */ + "Your limbs are stiffening.", /* 4 */ + "Your limbs have turned to stone.", /* 3 */ + "You have turned to stone.", /* 2 */ + "You are a statue." /* 1 */ +}; + +STATIC_OVL void +stoned_dialogue() +{ + register long i = (Stoned & TIMEOUT); + + if (i > 0L && i <= SIZE(stoned_texts)) + pline(stoned_texts[SIZE(stoned_texts) - i]); + if (i == 5L) + HFast = 0L; + if (i == 3L) + nomul(-3); + exercise(A_DEX, FALSE); +} + +/* He is getting sicker and sicker prior to vomiting */ +static NEARDATA const char * const vomiting_texts[] = { + "are feeling mildly nauseated.", /* 14 */ + "feel slightly confused.", /* 11 */ + "can't seem to think straight.", /* 8 */ + "feel incredibly sick.", /* 5 */ + "suddenly vomit!" /* 2 */ +}; + +STATIC_OVL void +vomiting_dialogue() +{ + register long i = (Vomiting & TIMEOUT) / 3L; + + if ((((Vomiting & TIMEOUT) % 3L) == 2) && (i >= 0) + && (i < SIZE(vomiting_texts))) + You(vomiting_texts[SIZE(vomiting_texts) - i - 1]); + + switch ((int) i) { + case 0: + vomit(); + morehungry(20); + break; + case 2: + make_stunned(HStun + d(2,4), FALSE); + /* fall through */ + case 3: + make_confused(HConfusion + d(2,4), FALSE); + break; + } + exercise(A_CON, FALSE); +} + +static NEARDATA const char * const choke_texts[] = { + "You find it hard to breathe.", + "You're gasping for air.", + "You can no longer breathe.", + "You're turning %s.", + "You suffocate." +}; + +static NEARDATA const char * const choke_texts2[] = { + "Your %s is becoming constricted.", + "Your blood is having trouble reaching your brain.", + "The pressure on your %s increases.", + "Your consciousness is fading.", + "You suffocate." +}; + +STATIC_OVL void +choke_dialogue() +{ + register long i = (Strangled & TIMEOUT); + + if(i > 0 && i <= SIZE(choke_texts)) { + if (Breathless || !rn2(50)) + pline(choke_texts2[SIZE(choke_texts2) - i], body_part(NECK)); + else { + const char *str = choke_texts[SIZE(choke_texts)-i]; + + if (index(str, '%')) + pline(str, hcolor(NH_BLUE)); + else + pline(str); + } + } + exercise(A_STR, FALSE); +} + +static NEARDATA const char * const slime_texts[] = { + "You are turning a little %s.", /* 5 */ + "Your limbs are getting oozy.", /* 4 */ + "Your skin begins to peel away.", /* 3 */ + "You are turning into %s.", /* 2 */ + "You have become %s." /* 1 */ +}; + +STATIC_OVL void +slime_dialogue() +{ + register long i = (Slimed & TIMEOUT) / 2L; + + if (((Slimed & TIMEOUT) % 2L) && i >= 0L + && i < SIZE(slime_texts)) { + const char *str = slime_texts[SIZE(slime_texts) - i - 1L]; + + if (index(str, '%')) { + if (i == 4L) { /* "you are turning green" */ + if (!Blind) /* [what if you're already green?] */ + pline(str, hcolor(NH_GREEN)); + } else + pline(str, an(Hallucination ? rndmonnam() : "green slime")); + } else + pline(str); + } + if (i == 3L) { /* limbs becoming oozy */ + HFast = 0L; /* lose intrinsic speed */ + stop_occupation(); + if (multi > 0) nomul(0); + } + exercise(A_DEX, FALSE); +} + +void +burn_away_slime() +{ + if (Slimed) { + pline_The("slime that covers you is burned away!"); + Slimed = 0L; + flags.botl = 1; + } + return; +} + + +#endif /* OVLB */ +#ifdef OVL0 + +void +nh_timeout() +{ + register struct prop *upp; + int sleeptime; + int m_idx; + int baseluck = (flags.moonphase == FULL_MOON) ? 1 : 0; + + if (flags.friday13) baseluck -= 1; + + if (u.uluck != baseluck && + moves % (u.uhave.amulet || u.ugangr ? 300 : 600) == 0) { + /* Cursed luckstones stop bad luck from timing out; blessed luckstones + * stop good luck from timing out; normal luckstones stop both; + * neither is stopped if you don't have a luckstone. + * Luck is based at 0 usually, +1 if a full moon and -1 on Friday 13th + */ + register int time_luck = stone_luck(FALSE); + boolean nostone = !carrying(LUCKSTONE) && !stone_luck(TRUE); + + if(u.uluck > baseluck && (nostone || time_luck < 0)) + u.uluck--; + else if(u.uluck < baseluck && (nostone || time_luck > 0)) + u.uluck++; + } + if(u.uinvulnerable) return; /* things past this point could kill you */ + if(Stoned) stoned_dialogue(); + if(Slimed) slime_dialogue(); + if(Vomiting) vomiting_dialogue(); + if(Strangled) choke_dialogue(); + if(u.mtimedone && !--u.mtimedone) { + if (Unchanging) + u.mtimedone = rnd(100*youmonst.data->mlevel + 1); + else + rehumanize(); + } + if(u.ucreamed) u.ucreamed--; + + /* Dissipate spell-based protection. */ + if (u.usptime) { + if (--u.usptime == 0 && u.uspellprot) { + u.usptime = u.uspmtime; + u.uspellprot--; + find_ac(); + if (!Blind) + Norep("The %s haze around you %s.", hcolor(NH_GOLDEN), + u.uspellprot ? "becomes less dense" : "disappears"); + } + } + +#ifdef STEED + if (u.ugallop) { + if (--u.ugallop == 0L && u.usteed) + pline("%s stops galloping.", Monnam(u.usteed)); + } +#endif + + for(upp = u.uprops; upp < u.uprops+SIZE(u.uprops); upp++) + if((upp->intrinsic & TIMEOUT) && !(--upp->intrinsic & TIMEOUT)) { + switch(upp - u.uprops){ + case STONED: + if (delayed_killer && !killer) { + killer = delayed_killer; + delayed_killer = 0; + } + if (!killer) { + /* leaving killer_format would make it + "petrified by petrification" */ + killer_format = NO_KILLER_PREFIX; + killer = "killed by petrification"; + } + done(STONING); + break; + case SLIMED: + if (delayed_killer && !killer) { + killer = delayed_killer; + delayed_killer = 0; + } + if (!killer) { + killer_format = NO_KILLER_PREFIX; + killer = "turned into green slime"; + } + done(TURNED_SLIME); + break; + case VOMITING: + make_vomiting(0L, TRUE); + break; + case SICK: + You("die from your illness."); + killer_format = KILLED_BY_AN; + killer = u.usick_cause; + if ((m_idx = name_to_mon(killer)) >= LOW_PM) { + if (type_is_pname(&mons[m_idx])) { + killer_format = KILLED_BY; + } else if (mons[m_idx].geno & G_UNIQ) { + killer = the(killer); + Strcpy(u.usick_cause, killer); + killer_format = KILLED_BY; + } + } + u.usick_type = 0; + done(POISONING); + break; + case FAST: + if (!Very_fast) + You_feel("yourself slowing down%s.", + Fast ? " a bit" : ""); + break; + case CONFUSION: + HConfusion = 1; /* So make_confused works properly */ + make_confused(0L, TRUE); + stop_occupation(); + break; + case STUNNED: + HStun = 1; + make_stunned(0L, TRUE); + stop_occupation(); + break; + case BLINDED: + Blinded = 1; + make_blinded(0L, TRUE); + stop_occupation(); + break; + case INVIS: + newsym(u.ux,u.uy); + if (!Invis && !BInvis && !Blind) { + You(!See_invisible ? + "are no longer invisible." : + "can no longer see through yourself."); + stop_occupation(); + } + break; + case SEE_INVIS: + set_mimic_blocking(); /* do special mimic handling */ + see_monsters(); /* make invis mons appear */ + newsym(u.ux,u.uy); /* make self appear */ + stop_occupation(); + break; + case WOUNDED_LEGS: + heal_legs(); + stop_occupation(); + break; + case HALLUC: + HHallucination = 1; + (void) make_hallucinated(0L, TRUE, 0L); + stop_occupation(); + break; + case SLEEPING: + if (unconscious() || Sleep_resistance) + HSleeping += rnd(100); + else if (Sleeping) { + You("fall asleep."); + sleeptime = rnd(20); + fall_asleep(-sleeptime, TRUE); + HSleeping += sleeptime + rnd(100); + } + break; + case LEVITATION: + (void) float_down(I_SPECIAL|TIMEOUT, 0L); + break; + case STRANGLED: + killer_format = KILLED_BY; + killer = (u.uburied) ? "suffocation" : "strangulation"; + done(DIED); + break; + case FUMBLING: + /* call this only when a move took place. */ + /* otherwise handle fumbling msgs locally. */ + if (u.umoved && !Levitation) { + slip_or_trip(); + nomul(-2); + nomovemsg = ""; + /* The more you are carrying the more likely you + * are to make noise when you fumble. Adjustments + * to this number must be thoroughly play tested. + */ + if ((inv_weight() > -500)) { + You("make a lot of noise!"); + wake_nearby(); + } + } + /* from outside means slippery ice; don't reset + counter if that's the only fumble reason */ + HFumbling &= ~FROMOUTSIDE; + if (Fumbling) + HFumbling += rnd(20); + break; + case DETECT_MONSTERS: + see_monsters(); + break; + } + } + + run_timers(); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +void +fall_asleep(how_long, wakeup_msg) +int how_long; +boolean wakeup_msg; +{ + stop_occupation(); + nomul(how_long); + /* generally don't notice sounds while sleeping */ + if (wakeup_msg && multi == how_long) { + /* caller can follow with a direct call to Hear_again() if + there's a need to override this when wakeup_msg is true */ + flags.soundok = 0; + afternmv = Hear_again; /* this won't give any messages */ + } + /* early wakeup from combat won't be possible until next monster turn */ + u.usleep = monstermoves; + nomovemsg = wakeup_msg ? "You wake up." : You_can_move_again; +} + +/* Attach an egg hatch timeout to the given egg. */ +void +attach_egg_hatch_timeout(egg) +struct obj *egg; +{ + int i; + + /* stop previous timer, if any */ + (void) stop_timer(HATCH_EGG, (genericptr_t) egg); + + /* + * Decide if and when to hatch the egg. The old hatch_it() code tried + * once a turn from age 151 to 200 (inclusive), hatching if it rolled + * a number x, 1<=x<=age, where x>150. This yields a chance of + * hatching > 99.9993%. Mimic that here. + */ + for (i = (MAX_EGG_HATCH_TIME-50)+1; i <= MAX_EGG_HATCH_TIME; i++) + if (rnd(i) > 150) { + /* egg will hatch */ + (void) start_timer((long)i, TIMER_OBJECT, + HATCH_EGG, (genericptr_t)egg); + break; + } +} + +/* prevent an egg from ever hatching */ +void +kill_egg(egg) +struct obj *egg; +{ + /* stop previous timer, if any */ + (void) stop_timer(HATCH_EGG, (genericptr_t) egg); +} + +/* timer callback routine: hatch the given egg */ +void +hatch_egg(arg, timeout) +genericptr_t arg; +long timeout; +{ + struct obj *egg; + struct monst *mon, *mon2; + coord cc; + xchar x, y; + boolean yours, silent, knows_egg = FALSE; + boolean cansee_hatchspot = FALSE; + int i, mnum, hatchcount = 0; + + egg = (struct obj *) arg; + /* sterilized while waiting */ + if (egg->corpsenm == NON_PM) return; + + mon = mon2 = (struct monst *)0; + mnum = big_to_little(egg->corpsenm); + /* The identity of one's father is learned, not innate */ + yours = (egg->spe || (!flags.female && carried(egg) && !rn2(2))); + silent = (timeout != monstermoves); /* hatched while away */ + + /* only can hatch when in INVENT, FLOOR, MINVENT */ + if (get_obj_location(egg, &x, &y, 0)) { + hatchcount = rnd((int)egg->quan); + cansee_hatchspot = cansee(x, y) && !silent; + if (!(mons[mnum].geno & G_UNIQ) && + !(mvitals[mnum].mvflags & (G_GENOD | G_EXTINCT))) { + for (i = hatchcount; i > 0; i--) { + if (!enexto(&cc, x, y, &mons[mnum]) || + !(mon = makemon(&mons[mnum], cc.x, cc.y, NO_MINVENT))) + break; + /* tame if your own egg hatches while you're on the + same dungeon level, or any dragon egg which hatches + while it's in your inventory */ + if ((yours && !silent) || + (carried(egg) && mon->data->mlet == S_DRAGON)) { + if ((mon2 = tamedog(mon, (struct obj *)0)) != 0) { + mon = mon2; + if (carried(egg) && mon->data->mlet != S_DRAGON) + mon->mtame = 20; + } + } + if (mvitals[mnum].mvflags & G_EXTINCT) + break; /* just made last one */ + mon2 = mon; /* in case makemon() fails on 2nd egg */ + } + if (!mon) mon = mon2; + hatchcount -= i; + egg->quan -= (long)hatchcount; + } + } +#if 0 + /* + * We could possibly hatch while migrating, but the code isn't + * set up for it... + */ + else if (obj->where == OBJ_MIGRATING) { + /* + We can do several things. The first ones that come to + mind are: + + + Create the hatched monster then place it on the migrating + mons list. This is tough because all makemon() is made + to place the monster as well. Makemon() also doesn't + lend itself well to splitting off a "not yet placed" + subroutine. + + + Mark the egg as hatched, then place the monster when we + place the migrating objects. + + + Or just kill any egg which gets sent to another level. + Falling is the usual reason such transportation occurs. + */ + cansee_hatchspot = FALSE; + mon = ??? + } +#endif + + if (mon) { + char monnambuf[BUFSZ], carriedby[BUFSZ]; + boolean siblings = (hatchcount > 1), redraw = FALSE; + + if (cansee_hatchspot) { + Sprintf(monnambuf, "%s%s", + siblings ? "some " : "", + siblings ? + makeplural(m_monnam(mon)) : an(m_monnam(mon))); + /* we don't learn the egg type here because learning + an egg type requires either seeing the egg hatch + or being familiar with the egg already, + as well as being able to see the resulting + monster, checked below + */ + } + switch (egg->where) { + case OBJ_INVENT: + knows_egg = TRUE; /* true even if you are blind */ + if (!cansee_hatchspot) + You_feel("%s %s from your pack!", something, + locomotion(mon->data, "drop")); + else + You("see %s %s out of your pack!", + monnambuf, locomotion(mon->data, "drop")); + if (yours) { + pline("%s cries sound like \"%s%s\"", + siblings ? "Their" : "Its", + flags.female ? "mommy" : "daddy", + egg->spe ? "." : "?"); + } else if (mon->data->mlet == S_DRAGON) { + verbalize("Gleep!"); /* Mything eggs :-) */ + } + break; + + case OBJ_FLOOR: + if (cansee_hatchspot) { + knows_egg = TRUE; + You("see %s hatch.", monnambuf); + redraw = TRUE; /* update egg's map location */ + } + break; + + case OBJ_MINVENT: + if (cansee_hatchspot) { + /* egg carring monster might be invisible */ + if (canseemon(egg->ocarry)) { + Sprintf(carriedby, "%s pack", + s_suffix(a_monnam(egg->ocarry))); + knows_egg = TRUE; + } + else if (is_pool(mon->mx, mon->my)) + Strcpy(carriedby, "empty water"); + else + Strcpy(carriedby, "thin air"); + You("see %s %s out of %s!", monnambuf, + locomotion(mon->data, "drop"), carriedby); + } + break; +#if 0 + case OBJ_MIGRATING: + break; +#endif + default: + impossible("egg hatched where? (%d)", (int)egg->where); + break; + } + + if (cansee_hatchspot && knows_egg) + learn_egg_type(mnum); + + if (egg->quan > 0) { + /* still some eggs left */ + attach_egg_hatch_timeout(egg); + if (egg->timed) { + /* replace ordinary egg timeout with a short one */ + (void) stop_timer(HATCH_EGG, (genericptr_t)egg); + (void) start_timer((long)rnd(12), TIMER_OBJECT, + HATCH_EGG, (genericptr_t)egg); + } + } else if (carried(egg)) { + useup(egg); + } else { + /* free egg here because we use it above */ + obj_extract_self(egg); + obfree(egg, (struct obj *)0); + } + if (redraw) newsym(x, y); + } +} + +/* Learn to recognize eggs of the given type. */ +void +learn_egg_type(mnum) +int mnum; +{ + /* baby monsters hatch from grown-up eggs */ + mnum = little_to_big(mnum); + mvitals[mnum].mvflags |= MV_KNOWS_EGG; + /* we might have just learned about other eggs being carried */ + update_inventory(); +} + +/* Attach a fig_transform timeout to the given figurine. */ +void +attach_fig_transform_timeout(figurine) +struct obj *figurine; +{ + int i; + + /* stop previous timer, if any */ + (void) stop_timer(FIG_TRANSFORM, (genericptr_t) figurine); + + /* + * Decide when to transform the figurine. + */ + i = rnd(9000) + 200; + /* figurine will transform */ + (void) start_timer((long)i, TIMER_OBJECT, + FIG_TRANSFORM, (genericptr_t)figurine); +} + +/* give a fumble message */ +STATIC_OVL void +slip_or_trip() +{ + struct obj *otmp = vobj_at(u.ux, u.uy); + const char *what, *pronoun; + char buf[BUFSZ]; + boolean on_foot = TRUE; +#ifdef STEED + if (u.usteed) on_foot = FALSE; +#endif + + if (otmp && on_foot && !u.uinwater && is_pool(u.ux, u.uy)) otmp = 0; + + if (otmp && on_foot) { /* trip over something in particular */ + /* + If there is only one item, it will have just been named + during the move, so refer to by via pronoun; otherwise, + if the top item has been or can be seen, refer to it by + name; if not, look for rocks to trip over; trip over + anonymous "something" if there aren't any rocks. + */ + pronoun = otmp->quan == 1L ? "it" : Hallucination ? "they" : "them"; + what = !otmp->nexthere ? pronoun : + (otmp->dknown || !Blind) ? doname(otmp) : + ((otmp = sobj_at(ROCK, u.ux, u.uy)) == 0 ? something : + (otmp->quan == 1L ? "a rock" : "some rocks")); + if (Hallucination) { + what = strcpy(buf, what); + buf[0] = highc(buf[0]); + pline("Egads! %s bite%s your %s!", + what, (!otmp || otmp->quan == 1L) ? "s" : "", + body_part(FOOT)); + } else { + You("trip over %s.", what); + } + } else if (rn2(3) && is_ice(u.ux, u.uy)) { + pline("%s %s%s on the ice.", +#ifdef STEED + u.usteed ? upstart(x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + (char *)0, SUPPRESS_SADDLE, FALSE)) : +#endif + "You", rn2(2) ? "slip" : "slide", on_foot ? "" : "s"); + } else { + if (on_foot) { + switch (rn2(4)) { + case 1: + You("trip over your own %s.", Hallucination ? + "elbow" : makeplural(body_part(FOOT))); + break; + case 2: + You("slip %s.", Hallucination ? + "on a banana peel" : "and nearly fall"); + break; + case 3: + You("flounder."); + break; + default: + You("stumble."); + break; + } + } +#ifdef STEED + else { + switch (rn2(4)) { + case 1: + Your("%s slip out of the stirrups.", makeplural(body_part(FOOT))); + break; + case 2: + You("let go of the reins."); + break; + case 3: + You("bang into the saddle-horn."); + break; + default: + You("slide to one side of the saddle."); + break; + } + dismount_steed(DISMOUNT_FELL); + } +#endif + } +} + +/* Print a lamp flicker message with tailer. */ +STATIC_OVL void +see_lamp_flicker(obj, tailer) +struct obj *obj; +const char *tailer; +{ + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s flickers%s.", Yname2(obj), tailer); + break; + case OBJ_FLOOR: + You("see %s flicker%s.", an(xname(obj)), tailer); + break; + } +} + +/* Print a dimming message for brass lanterns. */ +STATIC_OVL void +lantern_message(obj) +struct obj *obj; +{ + /* from adventure */ + switch (obj->where) { + case OBJ_INVENT: + Your("lantern is getting dim."); + if (Hallucination) + pline("Batteries have not been invented yet."); + break; + case OBJ_FLOOR: + You("see a lantern getting dim."); + break; + case OBJ_MINVENT: + pline("%s lantern is getting dim.", + s_suffix(Monnam(obj->ocarry))); + break; + } +} + +/* + * Timeout callback for for objects that are burning. E.g. lamps, candles. + * See begin_burn() for meanings of obj->age and obj->spe. + */ +void +burn_object(arg, timeout) +genericptr_t arg; +long timeout; +{ + struct obj *obj = (struct obj *) arg; + boolean canseeit, many, menorah, need_newsym; + xchar x, y; + char whose[BUFSZ]; + + menorah = obj->otyp == CANDELABRUM_OF_INVOCATION; + many = menorah ? obj->spe > 1 : obj->quan > 1L; + + /* timeout while away */ + if (timeout != monstermoves) { + long how_long = monstermoves - timeout; + + if (how_long >= obj->age) { + obj->age = 0; + end_burn(obj, FALSE); + + if (menorah) { + obj->spe = 0; /* no more candles */ + } else if (Is_candle(obj) || obj->otyp == POT_OIL) { + /* get rid of candles and burning oil potions */ + obj_extract_self(obj); + obfree(obj, (struct obj *)0); + obj = (struct obj *) 0; + } + + } else { + obj->age -= how_long; + begin_burn(obj, TRUE); + } + return; + } + + /* only interested in INVENT, FLOOR, and MINVENT */ + if (get_obj_location(obj, &x, &y, 0)) { + canseeit = !Blind && cansee(x, y); + /* set up `whose[]' to be "Your" or "Fred's" or "The goblin's" */ + (void) Shk_Your(whose, obj); + } else { + canseeit = FALSE; + } + need_newsym = FALSE; + + /* obj->age is the age remaining at this point. */ + switch (obj->otyp) { + case POT_OIL: + /* this should only be called when we run out */ + if (canseeit) { + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s potion of oil has burnt away.", + whose); + break; + case OBJ_FLOOR: + You("see a burning potion of oil go out."); + need_newsym = TRUE; + break; + } + } + end_burn(obj, FALSE); /* turn off light source */ + obj_extract_self(obj); + obfree(obj, (struct obj *)0); + obj = (struct obj *) 0; + break; + + case BRASS_LANTERN: + case OIL_LAMP: + switch((int)obj->age) { + case 150: + case 100: + case 50: + if (canseeit) { + if (obj->otyp == BRASS_LANTERN) + lantern_message(obj); + else + see_lamp_flicker(obj, + obj->age == 50L ? " considerably" : ""); + } + break; + + case 25: + if (canseeit) { + if (obj->otyp == BRASS_LANTERN) + lantern_message(obj); + else { + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s %s seems about to go out.", + whose, xname(obj)); + break; + case OBJ_FLOOR: + You("see %s about to go out.", + an(xname(obj))); + break; + } + } + } + break; + + case 0: + /* even if blind you'll know if holding it */ + if (canseeit || obj->where == OBJ_INVENT) { + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + if (obj->otyp == BRASS_LANTERN) + pline("%s lantern has run out of power.", + whose); + else + pline("%s %s has gone out.", + whose, xname(obj)); + break; + case OBJ_FLOOR: + if (obj->otyp == BRASS_LANTERN) + You("see a lantern run out of power."); + else + You("see %s go out.", + an(xname(obj))); + break; + } + } + end_burn(obj, FALSE); + break; + + default: + /* + * Someone added fuel to the lamp while it was + * lit. Just fall through and let begin burn + * handle the new age. + */ + break; + } + + if (obj->age) + begin_burn(obj, TRUE); + + break; + + case CANDELABRUM_OF_INVOCATION: + case TALLOW_CANDLE: + case WAX_CANDLE: + switch (obj->age) { + case 75: + if (canseeit) + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s %scandle%s getting short.", + whose, + menorah ? "candelabrum's " : "", + many ? "s are" : " is"); + break; + case OBJ_FLOOR: + You("see %scandle%s getting short.", + menorah ? "a candelabrum's " : + many ? "some " : "a ", + many ? "s" : ""); + break; + } + break; + + case 15: + if (canseeit) + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline( + "%s %scandle%s flame%s flicker%s low!", + whose, + menorah ? "candelabrum's " : "", + many ? "s'" : "'s", + many ? "s" : "", + many ? "" : "s"); + break; + case OBJ_FLOOR: + You("see %scandle%s flame%s flicker low!", + menorah ? "a candelabrum's " : + many ? "some " : "a ", + many ? "s'" : "'s", + many ? "s" : ""); + break; + } + break; + + case 0: + /* we know even if blind and in our inventory */ + if (canseeit || obj->where == OBJ_INVENT) { + if (menorah) { + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s candelabrum's flame%s.", + whose, + many ? "s die" : " dies"); + break; + case OBJ_FLOOR: + You("see a candelabrum's flame%s die.", + many ? "s" : ""); + break; + } + } else { + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MINVENT: + pline("%s %s %s consumed!", + whose, + xname(obj), + many ? "are" : "is"); + break; + case OBJ_FLOOR: + /* + You see some wax candles consumed! + You see a wax candle consumed! + */ + You("see %s%s consumed!", + many ? "some " : "", + many ? xname(obj):an(xname(obj))); + need_newsym = TRUE; + break; + } + + /* post message */ + pline(Hallucination ? + (many ? "They shriek!" : + "It shrieks!") : + Blind ? "" : + (many ? "Their flames die." : + "Its flame dies.")); + } + } + end_burn(obj, FALSE); + + if (menorah) { + obj->spe = 0; + } else { + obj_extract_self(obj); + obfree(obj, (struct obj *)0); + obj = (struct obj *) 0; + } + break; + + default: + /* + * Someone added fuel (candles) to the menorah while + * it was lit. Just fall through and let begin burn + * handle the new age. + */ + break; + } + + if (obj && obj->age) + begin_burn(obj, TRUE); + + break; + + default: + impossible("burn_object: unexpeced obj %s", xname(obj)); + break; + } + if (need_newsym) newsym(x, y); +} + +/* + * Start a burn timeout on the given object. If not "already lit" then + * create a light source for the vision system. There had better not + * be a burn already running on the object. + * + * Magic lamps stay lit as long as there's a genie inside, so don't start + * a timer. + * + * Burn rules: + * potions of oil, lamps & candles: + * age = # of turns of fuel left + * spe = + * + * magic lamps: + * age = + * spe = 0 not lightable, 1 lightable forever + * + * candelabrum: + * age = # of turns of fuel left + * spe = # of candles + * + * Once the burn begins, the age will be set to the amount of fuel + * remaining _once_the_burn_finishes_. If the burn is terminated + * early then fuel is added back. + * + * This use of age differs from the use of age for corpses and eggs. + * For the latter items, age is when the object was created, so we + * know when it becomes "bad". + * + * This is a "silent" routine - it should not print anything out. + */ +void +begin_burn(obj, already_lit) + struct obj *obj; + boolean already_lit; +{ + int radius = 3; + long turns = 0; + boolean do_timer = TRUE; + + if (obj->age == 0 && obj->otyp != MAGIC_LAMP && !artifact_light(obj)) + return; + + switch (obj->otyp) { + case MAGIC_LAMP: + obj->lamplit = 1; + do_timer = FALSE; + break; + + case POT_OIL: + turns = obj->age; + radius = 1; /* very dim light */ + break; + + case BRASS_LANTERN: + case OIL_LAMP: + /* magic times are 150, 100, 50, 25, and 0 */ + if (obj->age > 150L) + turns = obj->age - 150L; + else if (obj->age > 100L) + turns = obj->age - 100L; + else if (obj->age > 50L) + turns = obj->age - 50L; + else if (obj->age > 25L) + turns = obj->age - 25L; + else + turns = obj->age; + break; + + case CANDELABRUM_OF_INVOCATION: + case TALLOW_CANDLE: + case WAX_CANDLE: + /* magic times are 75, 15, and 0 */ + if (obj->age > 75L) + turns = obj->age - 75L; + else if (obj->age > 15L) + turns = obj->age - 15L; + else + turns = obj->age; + radius = candle_light_range(obj); + break; + + default: + /* [ALI] Support artifact light sources */ + if (artifact_light(obj)) { + obj->lamplit = 1; + do_timer = FALSE; + radius = 2; + } else { + impossible("begin burn: unexpected %s", xname(obj)); + turns = obj->age; + } + break; + } + + if (do_timer) { + if (start_timer(turns, TIMER_OBJECT, + BURN_OBJECT, (genericptr_t)obj)) { + obj->lamplit = 1; + obj->age -= turns; + if (carried(obj) && !already_lit) + update_inventory(); + } else { + obj->lamplit = 0; + } + } else { + if (carried(obj) && !already_lit) + update_inventory(); + } + + if (obj->lamplit && !already_lit) { + xchar x, y; + + if (get_obj_location(obj, &x, &y, CONTAINED_TOO|BURIED_TOO)) + new_light_source(x, y, radius, LS_OBJECT, (genericptr_t) obj); + else + impossible("begin_burn: can't get obj position"); + } +} + +/* + * Stop a burn timeout on the given object if timer attached. Darken + * light source. + */ +void +end_burn(obj, timer_attached) + struct obj *obj; + boolean timer_attached; +{ + if (!obj->lamplit) { + impossible("end_burn: obj %s not lit", xname(obj)); + return; + } + + if (obj->otyp == MAGIC_LAMP || artifact_light(obj)) + timer_attached = FALSE; + + if (!timer_attached) { + /* [DS] Cleanup explicitly, since timer cleanup won't happen */ + del_light_source(LS_OBJECT, (genericptr_t)obj); + obj->lamplit = 0; + if (obj->where == OBJ_INVENT) + update_inventory(); + } else if (!stop_timer(BURN_OBJECT, (genericptr_t) obj)) + impossible("end_burn: obj %s not timed!", xname(obj)); +} + +#endif /* OVL1 */ +#ifdef OVL0 + +/* + * Cleanup a burning object if timer stopped. + */ +static void +cleanup_burn(arg, expire_time) + genericptr_t arg; + long expire_time; +{ + struct obj *obj = (struct obj *)arg; + if (!obj->lamplit) { + impossible("cleanup_burn: obj %s not lit", xname(obj)); + return; + } + + del_light_source(LS_OBJECT, arg); + + /* restore unused time */ + obj->age += expire_time - monstermoves; + + obj->lamplit = 0; + + if (obj->where == OBJ_INVENT) + update_inventory(); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +void +do_storms() +{ + int nstrike; + register int x, y; + int dirx, diry; + int count; + + /* no lightning if not the air level or too often, even then */ + if(!Is_airlevel(&u.uz) || rn2(8)) + return; + + /* the number of strikes is 8-log2(nstrike) */ + for(nstrike = rnd(64); nstrike <= 64; nstrike *= 2) { + count = 0; + do { + x = rnd(COLNO-1); + y = rn2(ROWNO); + } while (++count < 100 && levl[x][y].typ != CLOUD); + + if(count < 100) { + dirx = rn2(3) - 1; + diry = rn2(3) - 1; + if(dirx != 0 || diry != 0) + buzz(-15, /* "monster" LIGHTNING spell */ + 8, x, y, dirx, diry); + } + } + + if(levl[u.ux][u.uy].typ == CLOUD) { + /* inside a cloud during a thunder storm is deafening */ + pline("Kaboom!!! Boom!! Boom!!"); + if(!u.uinvulnerable) { + stop_occupation(); + nomul(-3); + } + } else + You_hear("a rumbling noise."); +} +#endif /* OVL1 */ + + +#ifdef OVL0 +/* ------------------------------------------------------------------------- */ +/* + * Generic Timeout Functions. + * + * Interface: + * + * General: + * boolean start_timer(long timeout,short kind,short func_index, + * genericptr_t arg) + * Start a timer of kind 'kind' that will expire at time + * monstermoves+'timeout'. Call the function at 'func_index' + * in the timeout table using argument 'arg'. Return TRUE if + * a timer was started. This places the timer on a list ordered + * "sooner" to "later". If an object, increment the object's + * timer count. + * + * long stop_timer(short func_index, genericptr_t arg) + * Stop a timer specified by the (func_index, arg) pair. This + * assumes that such a pair is unique. Return the time the + * timer would have gone off. If no timer is found, return 0. + * If an object, decrement the object's timer count. + * + * void run_timers(void) + * Call timers that have timed out. + * + * + * Save/Restore: + * void save_timers(int fd, int mode, int range) + * Save all timers of range 'range'. Range is either global + * or local. Global timers follow game play, local timers + * are saved with a level. Object and monster timers are + * saved using their respective id's instead of pointers. + * + * void restore_timers(int fd, int range, boolean ghostly, long adjust) + * Restore timers of range 'range'. If from a ghost pile, + * adjust the timeout by 'adjust'. The object and monster + * ids are not restored until later. + * + * void relink_timers(boolean ghostly) + * Relink all object and monster timers that had been saved + * using their object's or monster's id number. + * + * Object Specific: + * void obj_move_timers(struct obj *src, struct obj *dest) + * Reassign all timers from src to dest. + * + * void obj_split_timers(struct obj *src, struct obj *dest) + * Duplicate all timers assigned to src and attach them to dest. + * + * void obj_stop_timers(struct obj *obj) + * Stop all timers attached to obj. + */ + +#ifdef WIZARD +STATIC_DCL const char *FDECL(kind_name, (SHORT_P)); +STATIC_DCL void FDECL(print_queue, (winid, timer_element *)); +#endif +STATIC_DCL void FDECL(insert_timer, (timer_element *)); +STATIC_DCL timer_element *FDECL(remove_timer, (timer_element **, SHORT_P, + genericptr_t)); +STATIC_DCL void FDECL(write_timer, (int, timer_element *)); +STATIC_DCL boolean FDECL(mon_is_local, (struct monst *)); +STATIC_DCL boolean FDECL(timer_is_local, (timer_element *)); +STATIC_DCL int FDECL(maybe_write_timer, (int, int, BOOLEAN_P)); + +/* ordered timer list */ +static timer_element *timer_base; /* "active" */ +static unsigned long timer_id = 1; + +/* If defined, then include names when printing out the timer queue */ +#define VERBOSE_TIMER + +typedef struct { + timeout_proc f, cleanup; +#ifdef VERBOSE_TIMER + const char *name; +# define TTAB(a, b, c) {a,b,c} +#else +# define TTAB(a, b, c) {a,b} +#endif +} ttable; + +/* table of timeout functions */ +static const ttable timeout_funcs[NUM_TIME_FUNCS] = { + TTAB(rot_organic, (timeout_proc)0, "rot_organic"), + TTAB(rot_corpse, (timeout_proc)0, "rot_corpse"), + TTAB(revive_mon, (timeout_proc)0, "revive_mon"), + TTAB(burn_object, cleanup_burn, "burn_object"), + TTAB(hatch_egg, (timeout_proc)0, "hatch_egg"), + TTAB(fig_transform, (timeout_proc)0, "fig_transform") +}; +#undef TTAB + + +#if defined(WIZARD) + +STATIC_OVL const char * +kind_name(kind) + short kind; +{ + switch (kind) { + case TIMER_LEVEL: return "level"; + case TIMER_GLOBAL: return "global"; + case TIMER_OBJECT: return "object"; + case TIMER_MONSTER: return "monster"; + } + return "unknown"; +} + +STATIC_OVL void +print_queue(win, base) + winid win; + timer_element *base; +{ + timer_element *curr; + char buf[BUFSZ], arg_address[20]; + + if (!base) { + putstr(win, 0, ""); + } else { + putstr(win, 0, "timeout id kind call"); + for (curr = base; curr; curr = curr->next) { +#ifdef VERBOSE_TIMER + Sprintf(buf, " %4ld %4ld %-6s %s(%s)", + curr->timeout, curr->tid, kind_name(curr->kind), + timeout_funcs[curr->func_index].name, + fmt_ptr((genericptr_t)curr->arg, arg_address)); +#else + Sprintf(buf, " %4ld %4ld %-6s #%d(%s)", + curr->timeout, curr->tid, kind_name(curr->kind), + curr->func_index, + fmt_ptr((genericptr_t)curr->arg, arg_address)); +#endif + putstr(win, 0, buf); + } + } +} + +int +wiz_timeout_queue() +{ + winid win; + char buf[BUFSZ]; + + win = create_nhwindow(NHW_MENU); /* corner text window */ + if (win == WIN_ERR) return 0; + + Sprintf(buf, "Current time = %ld.", monstermoves); + putstr(win, 0, buf); + putstr(win, 0, ""); + putstr(win, 0, "Active timeout queue:"); + putstr(win, 0, ""); + print_queue(win, timer_base); + + display_nhwindow(win, FALSE); + destroy_nhwindow(win); + + return 0; +} + +void +timer_sanity_check() +{ + timer_element *curr; + char obj_address[20]; + + /* this should be much more complete */ + for (curr = timer_base; curr; curr = curr->next) + if (curr->kind == TIMER_OBJECT) { + struct obj *obj = (struct obj *) curr->arg; + if (obj->timed == 0) { + pline("timer sanity: untimed obj %s, timer %ld", + fmt_ptr((genericptr_t)obj, obj_address), curr->tid); + } + } +} + +#endif /* WIZARD */ + + +/* + * Pick off timeout elements from the global queue and call their functions. + * Do this until their time is less than or equal to the move count. + */ +void +run_timers() +{ + timer_element *curr; + + /* + * Always use the first element. Elements may be added or deleted at + * any time. The list is ordered, we are done when the first element + * is in the future. + */ + while (timer_base && timer_base->timeout <= monstermoves) { + curr = timer_base; + timer_base = curr->next; + + if (curr->kind == TIMER_OBJECT) ((struct obj *)(curr->arg))->timed--; + (*timeout_funcs[curr->func_index].f)(curr->arg, curr->timeout); + free((genericptr_t) curr); + } +} + + +/* + * Start a timer. Return TRUE if successful. + */ +boolean +start_timer(when, kind, func_index, arg) +long when; +short kind; +short func_index; +genericptr_t arg; +{ + timer_element *gnu; + + if (func_index < 0 || func_index >= NUM_TIME_FUNCS) + panic("start_timer"); + + gnu = (timer_element *) alloc(sizeof(timer_element)); + gnu->next = 0; + gnu->tid = timer_id++; + gnu->timeout = monstermoves + when; + gnu->kind = kind; + gnu->needs_fixup = 0; + gnu->func_index = func_index; + gnu->arg = arg; + insert_timer(gnu); + + if (kind == TIMER_OBJECT) /* increment object's timed count */ + ((struct obj *)arg)->timed++; + + /* should check for duplicates and fail if any */ + return TRUE; +} + + +/* + * Remove the timer from the current list and free it up. Return the time + * it would have gone off, 0 if not found. + */ +long +stop_timer(func_index, arg) +short func_index; +genericptr_t arg; +{ + timer_element *doomed; + long timeout; + + doomed = remove_timer(&timer_base, func_index, arg); + + if (doomed) { + timeout = doomed->timeout; + if (doomed->kind == TIMER_OBJECT) + ((struct obj *)arg)->timed--; + if (timeout_funcs[doomed->func_index].cleanup) + (*timeout_funcs[doomed->func_index].cleanup)(arg, timeout); + free((genericptr_t) doomed); + return timeout; + } + return 0; +} + + +/* + * Move all object timers from src to dest, leaving src untimed. + */ +void +obj_move_timers(src, dest) + struct obj *src, *dest; +{ + int count; + timer_element *curr; + + for (count = 0, curr = timer_base; curr; curr = curr->next) + if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)src) { + curr->arg = (genericptr_t) dest; + dest->timed++; + count++; + } + if (count != src->timed) + panic("obj_move_timers"); + src->timed = 0; +} + + +/* + * Find all object timers and duplicate them for the new object "dest". + */ +void +obj_split_timers(src, dest) + struct obj *src, *dest; +{ + timer_element *curr, *next_timer=0; + + for (curr = timer_base; curr; curr = next_timer) { + next_timer = curr->next; /* things may be inserted */ + if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)src) { + (void) start_timer(curr->timeout-monstermoves, TIMER_OBJECT, + curr->func_index, (genericptr_t)dest); + } + } +} + + +/* + * Stop all timers attached to this object. We can get away with this because + * all object pointers are unique. + */ +void +obj_stop_timers(obj) + struct obj *obj; +{ + timer_element *curr, *prev, *next_timer=0; + + for (prev = 0, curr = timer_base; curr; curr = next_timer) { + next_timer = curr->next; + if (curr->kind == TIMER_OBJECT && curr->arg == (genericptr_t)obj) { + if (prev) + prev->next = curr->next; + else + timer_base = curr->next; + if (timeout_funcs[curr->func_index].cleanup) + (*timeout_funcs[curr->func_index].cleanup)(curr->arg, + curr->timeout); + free((genericptr_t) curr); + } else { + prev = curr; + } + } + obj->timed = 0; +} + + +/* Insert timer into the global queue */ +STATIC_OVL void +insert_timer(gnu) + timer_element *gnu; +{ + timer_element *curr, *prev; + + for (prev = 0, curr = timer_base; curr; prev = curr, curr = curr->next) + if (curr->timeout >= gnu->timeout) break; + + gnu->next = curr; + if (prev) + prev->next = gnu; + else + timer_base = gnu; +} + + +STATIC_OVL timer_element * +remove_timer(base, func_index, arg) +timer_element **base; +short func_index; +genericptr_t arg; +{ + timer_element *prev, *curr; + + for (prev = 0, curr = *base; curr; prev = curr, curr = curr->next) + if (curr->func_index == func_index && curr->arg == arg) break; + + if (curr) { + if (prev) + prev->next = curr->next; + else + *base = curr->next; + } + + return curr; +} + + +STATIC_OVL void +write_timer(fd, timer) + int fd; + timer_element *timer; +{ + genericptr_t arg_save; + + switch (timer->kind) { + case TIMER_GLOBAL: + case TIMER_LEVEL: + /* assume no pointers in arg */ + bwrite(fd, (genericptr_t) timer, sizeof(timer_element)); + break; + + case TIMER_OBJECT: + if (timer->needs_fixup) + bwrite(fd, (genericptr_t)timer, sizeof(timer_element)); + else { + /* replace object pointer with id */ + arg_save = timer->arg; + timer->arg = (genericptr_t)((struct obj *)timer->arg)->o_id; + timer->needs_fixup = 1; + bwrite(fd, (genericptr_t)timer, sizeof(timer_element)); + timer->arg = arg_save; + timer->needs_fixup = 0; + } + break; + + case TIMER_MONSTER: + if (timer->needs_fixup) + bwrite(fd, (genericptr_t)timer, sizeof(timer_element)); + else { + /* replace monster pointer with id */ + arg_save = timer->arg; + timer->arg = (genericptr_t)((struct monst *)timer->arg)->m_id; + timer->needs_fixup = 1; + bwrite(fd, (genericptr_t)timer, sizeof(timer_element)); + timer->arg = arg_save; + timer->needs_fixup = 0; + } + break; + + default: + panic("write_timer"); + break; + } +} + + +/* + * Return TRUE if the object will stay on the level when the level is + * saved. + */ +boolean +obj_is_local(obj) + struct obj *obj; +{ + switch (obj->where) { + case OBJ_INVENT: + case OBJ_MIGRATING: return FALSE; + case OBJ_FLOOR: + case OBJ_BURIED: return TRUE; + case OBJ_CONTAINED: return obj_is_local(obj->ocontainer); + case OBJ_MINVENT: return mon_is_local(obj->ocarry); + } + panic("obj_is_local"); + return FALSE; +} + + +/* + * Return TRUE if the given monster will stay on the level when the + * level is saved. + */ +STATIC_OVL boolean +mon_is_local(mon) +struct monst *mon; +{ + struct monst *curr; + + for (curr = migrating_mons; curr; curr = curr->nmon) + if (curr == mon) return FALSE; + /* `mydogs' is used during level changes, never saved and restored */ + for (curr = mydogs; curr; curr = curr->nmon) + if (curr == mon) return FALSE; + return TRUE; +} + + +/* + * Return TRUE if the timer is attached to something that will stay on the + * level when the level is saved. + */ +STATIC_OVL boolean +timer_is_local(timer) + timer_element *timer; +{ + switch (timer->kind) { + case TIMER_LEVEL: return TRUE; + case TIMER_GLOBAL: return FALSE; + case TIMER_OBJECT: return obj_is_local((struct obj *)timer->arg); + case TIMER_MONSTER: return mon_is_local((struct monst *)timer->arg); + } + panic("timer_is_local"); + return FALSE; +} + + +/* + * Part of the save routine. Count up the number of timers that would + * be written. If write_it is true, actually write the timer. + */ +STATIC_OVL int +maybe_write_timer(fd, range, write_it) + int fd, range; + boolean write_it; +{ + int count = 0; + timer_element *curr; + + for (curr = timer_base; curr; curr = curr->next) { + if (range == RANGE_GLOBAL) { + /* global timers */ + + if (!timer_is_local(curr)) { + count++; + if (write_it) write_timer(fd, curr); + } + + } else { + /* local timers */ + + if (timer_is_local(curr)) { + count++; + if (write_it) write_timer(fd, curr); + } + + } + } + + return count; +} + + +/* + * Save part of the timer list. The parameter 'range' specifies either + * global or level timers to save. The timer ID is saved with the global + * timers. + * + * Global range: + * + timeouts that follow the hero (global) + * + timeouts that follow obj & monst that are migrating + * + * Level range: + * + timeouts that are level specific (e.g. storms) + * + timeouts that stay with the level (obj & monst) + */ +void +save_timers(fd, mode, range) + int fd, mode, range; +{ + timer_element *curr, *prev, *next_timer=0; + int count; + + if (perform_bwrite(mode)) { + if (range == RANGE_GLOBAL) + bwrite(fd, (genericptr_t) &timer_id, sizeof(timer_id)); + + count = maybe_write_timer(fd, range, FALSE); + bwrite(fd, (genericptr_t) &count, sizeof count); + (void) maybe_write_timer(fd, range, TRUE); + } + + if (release_data(mode)) { + for (prev = 0, curr = timer_base; curr; curr = next_timer) { + next_timer = curr->next; /* in case curr is removed */ + + if ( !(!!(range == RANGE_LEVEL) ^ !!timer_is_local(curr)) ) { + if (prev) + prev->next = curr->next; + else + timer_base = curr->next; + free((genericptr_t) curr); + /* prev stays the same */ + } else { + prev = curr; + } + } + } +} + + +/* + * Pull in the structures from disk, but don't recalculate the object and + * monster pointers. + */ +void +restore_timers(fd, range, ghostly, adjust) + int fd, range; + boolean ghostly; /* restoring from a ghost level */ + long adjust; /* how much to adjust timeout */ +{ + int count; + timer_element *curr; + + if (range == RANGE_GLOBAL) + mread(fd, (genericptr_t) &timer_id, sizeof timer_id); + + /* restore elements */ + mread(fd, (genericptr_t) &count, sizeof count); + while (count-- > 0) { + curr = (timer_element *) alloc(sizeof(timer_element)); + mread(fd, (genericptr_t) curr, sizeof(timer_element)); + if (ghostly) + curr->timeout += adjust; + insert_timer(curr); + } +} + + +/* reset all timers that are marked for reseting */ +void +relink_timers(ghostly) + boolean ghostly; +{ + timer_element *curr; + unsigned nid; + + for (curr = timer_base; curr; curr = curr->next) { + if (curr->needs_fixup) { + if (curr->kind == TIMER_OBJECT) { + if (ghostly) { + if (!lookup_id_mapping((unsigned)curr->arg, &nid)) + panic("relink_timers 1"); + } else + nid = (unsigned) curr->arg; + curr->arg = (genericptr_t) find_oid(nid); + if (!curr->arg) panic("cant find o_id %d", nid); + curr->needs_fixup = 0; + } else if (curr->kind == TIMER_MONSTER) { + panic("relink_timers: no monster timer implemented"); + } else + panic("relink_timers 2"); + } + } +} + +#endif /* OVL0 */ + +/*timeout.c*/ diff --git a/src/topten.c b/src/topten.c new file mode 100644 index 0000000..19a0488 --- /dev/null +++ b/src/topten.c @@ -0,0 +1,977 @@ +/* SCCS Id: @(#)topten.c 3.4 2000/01/21 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +#ifdef VMS + /* We don't want to rewrite the whole file, because that entails */ + /* creating a new version which requires that the old one be deletable. */ +# define UPDATE_RECORD_IN_PLACE +#endif + +/* + * Updating in place can leave junk at the end of the file in some + * circumstances (if it shrinks and the O.S. doesn't have a straightforward + * way to truncate it). The trailing junk is harmless and the code + * which reads the scores will ignore it. + */ +#ifdef UPDATE_RECORD_IN_PLACE +static long final_fpos; +#endif + +#define done_stopprint program_state.stopprint + +#define newttentry() (struct toptenentry *) alloc(sizeof(struct toptenentry)) +#define dealloc_ttentry(ttent) free((genericptr_t) (ttent)) +#define NAMSZ 10 +#define DTHSZ 100 +#define ROLESZ 3 +#define PERSMAX 3 /* entries per name/uid per char. allowed */ +#define POINTSMIN 1 /* must be > 0 */ +#define ENTRYMAX 100 /* must be >= 10 */ + +#if !defined(MICRO) && !defined(MAC) && !defined(WIN32) +#define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ +#endif +struct toptenentry { + struct toptenentry *tt_next; +#ifdef UPDATE_RECORD_IN_PLACE + long fpos; +#endif + long points; + int deathdnum, deathlev; + int maxlvl, hp, maxhp, deaths; + int ver_major, ver_minor, patchlevel; + long deathdate, birthdate; + int uid; + char plrole[ROLESZ+1]; + char plrace[ROLESZ+1]; + char plgend[ROLESZ+1]; + char plalign[ROLESZ+1]; + char name[NAMSZ+1]; + char death[DTHSZ+1]; +} *tt_head; + +STATIC_DCL void FDECL(topten_print, (const char *)); +STATIC_DCL void FDECL(topten_print_bold, (const char *)); +STATIC_DCL xchar FDECL(observable_depth, (d_level *)); +STATIC_DCL void NDECL(outheader); +STATIC_DCL void FDECL(outentry, (int,struct toptenentry *,BOOLEAN_P)); +STATIC_DCL void FDECL(readentry, (FILE *,struct toptenentry *)); +STATIC_DCL void FDECL(writeentry, (FILE *,struct toptenentry *)); +STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *)); +STATIC_DCL int FDECL(classmon, (char *,BOOLEAN_P)); +STATIC_DCL int FDECL(score_wanted, + (BOOLEAN_P, int,struct toptenentry *,int,const char **,int)); +#ifdef NO_SCAN_BRACK +STATIC_DCL void FDECL(nsb_mung_line,(char*)); +STATIC_DCL void FDECL(nsb_unmung_line,(char*)); +#endif + +/* must fit with end.c; used in rip.c */ +NEARDATA const char * const killed_by_prefix[] = { + "killed by ", "choked on ", "poisoned by ", "died of ", "drowned in ", + "burned by ", "dissolved in ", "crushed to death by ", "petrified by ", + "turned to slime by ", "killed by ", "", "", "", "", "" +}; + +static winid toptenwin = WIN_ERR; + +STATIC_OVL void +topten_print(x) +const char *x; +{ + if (toptenwin == WIN_ERR) + raw_print(x); + else + putstr(toptenwin, ATR_NONE, x); +} + +STATIC_OVL void +topten_print_bold(x) +const char *x; +{ + if (toptenwin == WIN_ERR) + raw_print_bold(x); + else + putstr(toptenwin, ATR_BOLD, x); +} + +STATIC_OVL xchar +observable_depth(lev) +d_level *lev; +{ +#if 0 /* if we ever randomize the order of the elemental planes, we + must use a constant external representation in the record file */ + if (In_endgame(lev)) { + if (Is_astralevel(lev)) return -5; + else if (Is_waterlevel(lev)) return -4; + else if (Is_firelevel(lev)) return -3; + else if (Is_airlevel(lev)) return -2; + else if (Is_earthlevel(lev)) return -1; + else return 0; /* ? */ + } else +#endif + return depth(lev); +} + +STATIC_OVL void +readentry(rfile,tt) +FILE *rfile; +struct toptenentry *tt; +{ +#ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */ + static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c"; + static const char fmt32[] = "%c%c %s %s%*c"; + static const char fmt33[] = "%s %s %s %s %s %s%*c"; +#else + static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d "; + static const char fmt32[] = "%c%c %[^,],%[^\n]%*c"; + static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c"; +#endif + +#ifdef UPDATE_RECORD_IN_PLACE + /* note: fscanf() below must read the record's terminating newline */ + final_fpos = tt->fpos = ftell(rfile); +#endif +#define TTFIELDS 13 + if(fscanf(rfile, fmt, + &tt->ver_major, &tt->ver_minor, &tt->patchlevel, + &tt->points, &tt->deathdnum, &tt->deathlev, + &tt->maxlvl, &tt->hp, &tt->maxhp, &tt->deaths, + &tt->deathdate, &tt->birthdate, + &tt->uid) != TTFIELDS) +#undef TTFIELDS + tt->points = 0; + else { + /* Check for backwards compatibility */ + if (tt->ver_major < 3 || + (tt->ver_major == 3 && tt->ver_minor < 3)) { + int i; + + if (fscanf(rfile, fmt32, + tt->plrole, tt->plgend, + tt->name, tt->death) != 4) + tt->points = 0; + tt->plrole[1] = '\0'; + if ((i = str2role(tt->plrole)) >= 0) + Strcpy(tt->plrole, roles[i].filecode); + Strcpy(tt->plrace, "?"); + Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem"); + Strcpy(tt->plalign, "?"); + } else if (fscanf(rfile, fmt33, + tt->plrole, tt->plrace, tt->plgend, + tt->plalign, tt->name, tt->death) != 6) + tt->points = 0; +#ifdef NO_SCAN_BRACK + if(tt->points > 0) { + nsb_unmung_line(tt->name); + nsb_unmung_line(tt->death); + } +#endif + } + + /* check old score entries for Y2K problem and fix whenever found */ + if (tt->points > 0) { + if (tt->birthdate < 19000000L) tt->birthdate += 19000000L; + if (tt->deathdate < 19000000L) tt->deathdate += 19000000L; + } +} + +STATIC_OVL void +writeentry(rfile,tt) +FILE *rfile; +struct toptenentry *tt; +{ +#ifdef NO_SCAN_BRACK + nsb_mung_line(tt->name); + nsb_mung_line(tt->death); + /* Version_ Pts DgnLevs_ Hp___ Died__Born id */ + (void) fprintf(rfile,"%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ", +#else + (void) fprintf(rfile,"%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ", +#endif + tt->ver_major, tt->ver_minor, tt->patchlevel, + tt->points, tt->deathdnum, tt->deathlev, + tt->maxlvl, tt->hp, tt->maxhp, tt->deaths, + tt->deathdate, tt->birthdate, tt->uid); + if (tt->ver_major < 3 || + (tt->ver_major == 3 && tt->ver_minor < 3)) +#ifdef NO_SCAN_BRACK + (void) fprintf(rfile,"%c%c %s %s\n", +#else + (void) fprintf(rfile,"%c%c %s,%s\n", +#endif + tt->plrole[0], tt->plgend[0], + onlyspace(tt->name) ? "_" : tt->name, tt->death); + else +#ifdef NO_SCAN_BRACK + (void) fprintf(rfile,"%s %s %s %s %s %s\n", +#else + (void) fprintf(rfile,"%s %s %s %s %s,%s\n", +#endif + tt->plrole, tt->plrace, tt->plgend, tt->plalign, + onlyspace(tt->name) ? "_" : tt->name, tt->death); + +#ifdef NO_SCAN_BRACK + nsb_unmung_line(tt->name); + nsb_unmung_line(tt->death); +#endif +} + +STATIC_OVL void +free_ttlist(tt) +struct toptenentry *tt; +{ + struct toptenentry *ttnext; + + while (tt->points > 0) { + ttnext = tt->tt_next; + dealloc_ttentry(tt); + tt = ttnext; + } + dealloc_ttentry(tt); +} + +void +topten(how) +int how; +{ + int uid = getuid(); + int rank, rank0 = -1, rank1 = 0; + int occ_cnt = PERSMAX; + register struct toptenentry *t0, *tprev; + struct toptenentry *t1; + FILE *rfile; + register int flg = 0; + boolean t0_used; +#ifdef LOGFILE + FILE *lfile; +#endif /* LOGFILE */ + +/* Under DICE 3.0, this crashes the system consistently, apparently due to + * corruption of *rfile somewhere. Until I figure this out, just cut out + * topten support entirely - at least then the game exits cleanly. --AC + */ +#ifdef _DCC + return; +#endif + +/* If we are in the midst of a panic, cut out topten entirely. + * topten uses alloc() several times, which will lead to + * problems if the panic was the result of an alloc() failure. + */ + if (program_state.panicking) + return; + + if (flags.toptenwin) { + toptenwin = create_nhwindow(NHW_TEXT); + } + +#if defined(UNIX) || defined(VMS) || defined(__EMX__) +#define HUP if (!program_state.done_hup) +#else +#define HUP +#endif + +#ifdef TOS + restore_colors(); /* make sure the screen is black on white */ +#endif + /* create a new 'topten' entry */ + t0_used = FALSE; + t0 = newttentry(); + /* deepest_lev_reached() is in terms of depth(), and reporting the + * deepest level reached in the dungeon death occurred in doesn't + * seem right, so we have to report the death level in depth() terms + * as well (which also seems reasonable since that's all the player + * sees on the screen anyway) + */ + t0->ver_major = VERSION_MAJOR; + t0->ver_minor = VERSION_MINOR; + t0->patchlevel = PATCHLEVEL; + t0->points = u.urexp; + t0->deathdnum = u.uz.dnum; + t0->deathlev = observable_depth(&u.uz); + t0->maxlvl = deepest_lev_reached(TRUE); + t0->hp = u.uhp; + t0->maxhp = u.uhpmax; + t0->deaths = u.umortality; + t0->uid = uid; + (void) strncpy(t0->plrole, urole.filecode, ROLESZ); + t0->plrole[ROLESZ] = '\0'; + (void) strncpy(t0->plrace, urace.filecode, ROLESZ); + t0->plrace[ROLESZ] = '\0'; + (void) strncpy(t0->plgend, genders[flags.female].filecode, ROLESZ); + t0->plgend[ROLESZ] = '\0'; + (void) strncpy(t0->plalign, aligns[1-u.ualign.type].filecode, ROLESZ); + t0->plalign[ROLESZ] = '\0'; + (void) strncpy(t0->name, plname, NAMSZ); + t0->name[NAMSZ] = '\0'; + t0->death[0] = '\0'; + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcat(t0->death, killed_by_prefix[how]); + (void) strncat(t0->death, an(killer), + DTHSZ-strlen(t0->death)); + break; + case KILLED_BY: + Strcat(t0->death, killed_by_prefix[how]); + (void) strncat(t0->death, killer, + DTHSZ-strlen(t0->death)); + break; + case NO_KILLER_PREFIX: + (void) strncat(t0->death, killer, DTHSZ); + break; + } + t0->birthdate = yyyymmdd(u.ubirthday); + t0->deathdate = yyyymmdd((time_t)0L); + t0->tt_next = 0; +#ifdef UPDATE_RECORD_IN_PLACE + t0->fpos = -1L; +#endif + +#ifdef LOGFILE /* used for debugging (who dies of what, where) */ + if (lock_file(LOGFILE, SCOREPREFIX, 10)) { + if(!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) { + HUP raw_print("Cannot open log file!"); + } else { + writeentry(lfile, t0); + (void) fclose(lfile); + } + unlock_file(LOGFILE); + } +#endif /* LOGFILE */ + + if (wizard || discover) { + if (how != PANICKED) HUP { + char pbuf[BUFSZ]; + topten_print(""); + Sprintf(pbuf, + "Since you were in %s mode, the score list will not be checked.", + wizard ? "wizard" : "discover"); + topten_print(pbuf); + } + goto showwin; + } + + if (!lock_file(RECORD, SCOREPREFIX, 60)) + goto destroywin; + +#ifdef UPDATE_RECORD_IN_PLACE + rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX); +#else + rfile = fopen_datafile(RECORD, "r", SCOREPREFIX); +#endif + + if (!rfile) { + HUP raw_print("Cannot open record file!"); + unlock_file(RECORD); + goto destroywin; + } + + HUP topten_print(""); + + /* assure minimum number of points */ + if(t0->points < POINTSMIN) t0->points = 0; + + t1 = tt_head = newttentry(); + tprev = 0; + /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ + for(rank = 1; ; ) { + readentry(rfile, t1); + if (t1->points < POINTSMIN) t1->points = 0; + if(rank0 < 0 && t1->points < t0->points) { + rank0 = rank++; + if(tprev == 0) + tt_head = t0; + else + tprev->tt_next = t0; + t0->tt_next = t1; +#ifdef UPDATE_RECORD_IN_PLACE + t0->fpos = t1->fpos; /* insert here */ +#endif + t0_used = TRUE; + occ_cnt--; + flg++; /* ask for a rewrite */ + } else tprev = t1; + + if(t1->points == 0) break; + if( +#ifdef PERS_IS_UID + t1->uid == t0->uid && +#else + strncmp(t1->name, t0->name, NAMSZ) == 0 && +#endif + !strncmp(t1->plrole, t0->plrole, ROLESZ) && + --occ_cnt <= 0) { + if(rank0 < 0) { + rank0 = 0; + rank1 = rank; + HUP { + char pbuf[BUFSZ]; + Sprintf(pbuf, + "You didn't beat your previous score of %ld points.", + t1->points); + topten_print(pbuf); + topten_print(""); + } + } + if(occ_cnt < 0) { + flg++; + continue; + } + } + if(rank <= ENTRYMAX) { + t1->tt_next = newttentry(); + t1 = t1->tt_next; + rank++; + } + if(rank > ENTRYMAX) { + t1->points = 0; + break; + } + } + if(flg) { /* rewrite record file */ +#ifdef UPDATE_RECORD_IN_PLACE + (void) fseek(rfile, (t0->fpos >= 0 ? + t0->fpos : final_fpos), SEEK_SET); +#else + (void) fclose(rfile); + if(!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))){ + HUP raw_print("Cannot write record file"); + unlock_file(RECORD); + free_ttlist(tt_head); + goto destroywin; + } +#endif /* UPDATE_RECORD_IN_PLACE */ + if(!done_stopprint) if(rank0 > 0){ + if(rank0 <= 10) + topten_print("You made the top ten list!"); + else { + char pbuf[BUFSZ]; + Sprintf(pbuf, + "You reached the %d%s place on the top %d list.", + rank0, ordin(rank0), ENTRYMAX); + topten_print(pbuf); + } + topten_print(""); + } + } + if(rank0 == 0) rank0 = rank1; + if(rank0 <= 0) rank0 = rank; + if(!done_stopprint) outheader(); + t1 = tt_head; + for(rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if(flg +#ifdef UPDATE_RECORD_IN_PLACE + && rank >= rank0 +#endif + ) writeentry(rfile, t1); + if (done_stopprint) continue; + if (rank > flags.end_top && + (rank < rank0 - flags.end_around || + rank > rank0 + flags.end_around) && + (!flags.end_own || +#ifdef PERS_IS_UID + t1->uid != t0->uid +#else + strncmp(t1->name, t0->name, NAMSZ) +#endif + )) continue; + if (rank == rank0 - flags.end_around && + rank0 > flags.end_top + flags.end_around + 1 && + !flags.end_own) + topten_print(""); + if(rank != rank0) + outentry(rank, t1, FALSE); + else if(!rank1) + outentry(rank, t1, TRUE); + else { + outentry(rank, t1, TRUE); + outentry(0, t0, TRUE); + } + } + if(rank0 >= rank) if(!done_stopprint) + outentry(0, t0, TRUE); +#ifdef UPDATE_RECORD_IN_PLACE + if (flg) { +# ifdef TRUNCATE_FILE + /* if a reasonable way to truncate a file exists, use it */ + truncate_file(rfile); +# else + /* use sentinel record rather than relying on truncation */ + t1->points = 0L; /* terminates file when read back in */ + t1->ver_major = t1->ver_minor = t1->patchlevel = 0; + t1->uid = t1->deathdnum = t1->deathlev = 0; + t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0; + t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-'; + t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0; + t1->birthdate = t1->deathdate = yyyymmdd((time_t)0L); + Strcpy(t1->name, "@"); + Strcpy(t1->death, "\n"); + writeentry(rfile, t1); + (void) fflush(rfile); +# endif /* TRUNCATE_FILE */ + } +#endif /* UPDATE_RECORD_IN_PLACE */ + (void) fclose(rfile); + unlock_file(RECORD); + free_ttlist(tt_head); + + showwin: + if (flags.toptenwin && !done_stopprint) display_nhwindow(toptenwin, 1); + destroywin: + if (!t0_used) dealloc_ttentry(t0); + if (flags.toptenwin) { + destroy_nhwindow(toptenwin); + toptenwin=WIN_ERR; + } +} + +STATIC_OVL void +outheader() +{ + char linebuf[BUFSZ]; + register char *bp; + + Strcpy(linebuf, " No Points Name"); + bp = eos(linebuf); + while(bp < linebuf + COLNO - 9) *bp++ = ' '; + Strcpy(bp, "Hp [max]"); + topten_print(linebuf); +} + +/* so>0: standout line; so=0: ordinary line */ +STATIC_OVL void +outentry(rank, t1, so) +struct toptenentry *t1; +int rank; +boolean so; +{ + boolean second_line = TRUE; + char linebuf[BUFSZ]; + char *bp, hpbuf[24], linebuf3[BUFSZ]; + int hppos, lngr; + + + linebuf[0] = '\0'; + if (rank) Sprintf(eos(linebuf), "%3d", rank); + else Strcat(linebuf, " "); + + Sprintf(eos(linebuf), " %10ld %.10s", t1->points, t1->name); + Sprintf(eos(linebuf), "-%s", t1->plrole); + if (t1->plrace[0] != '?') + Sprintf(eos(linebuf), "-%s", t1->plrace); + /* Printing of gender and alignment is intentional. It has been + * part of the NetHack Geek Code, and illustrates a proper way to + * specify a character from the command line. + */ + Sprintf(eos(linebuf), "-%s", t1->plgend); + if (t1->plalign[0] != '?') + Sprintf(eos(linebuf), "-%s ", t1->plalign); + else + Strcat(linebuf, " "); + if (!strncmp("escaped", t1->death, 7)) { + Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]", + !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "", + t1->maxlvl); + /* fixup for closing paren in "escaped... with...Amulet)[max..." */ + if ((bp = index(linebuf, ')')) != 0) + *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' '; + second_line = FALSE; + } else if (!strncmp("ascended", t1->death, 8)) { + Sprintf(eos(linebuf), "ascended to demigod%s-hood", + (t1->plgend[0] == 'F') ? "dess" : ""); + second_line = FALSE; + } else { + if (!strncmp(t1->death, "quit", 4)) { + Strcat(linebuf, "quit"); + second_line = FALSE; + } else if (!strncmp(t1->death, "died of st", 10)) { + Strcat(linebuf, "starved to death"); + second_line = FALSE; + } else if (!strncmp(t1->death, "choked", 6)) { + Sprintf(eos(linebuf), "choked on h%s food", + (t1->plgend[0] == 'F') ? "er" : "is"); + } else if (!strncmp(t1->death, "poisoned", 8)) { + Strcat(linebuf, "was poisoned"); + } else if (!strncmp(t1->death, "crushed", 7)) { + Strcat(linebuf, "was crushed to death"); + } else if (!strncmp(t1->death, "petrified by ", 13)) { + Strcat(linebuf, "turned to stone"); + } else Strcat(linebuf, "died"); + + if (t1->deathdnum == astral_level.dnum) { + const char *arg, *fmt = " on the Plane of %s"; + + switch (t1->deathlev) { + case -5: + fmt = " on the %s Plane"; + arg = "Astral"; break; + case -4: + arg = "Water"; break; + case -3: + arg = "Fire"; break; + case -2: + arg = "Air"; break; + case -1: + arg = "Earth"; break; + default: + arg = "Void"; break; + } + Sprintf(eos(linebuf), fmt, arg); + } else { + Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname); + if (t1->deathdnum != knox_level.dnum) + Sprintf(eos(linebuf), " on level %d", t1->deathlev); + if (t1->deathlev != t1->maxlvl) + Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); + } + + /* kludge for "quit while already on Charon's boat" */ + if (!strncmp(t1->death, "quit ", 5)) + Strcat(linebuf, t1->death + 4); + } + Strcat(linebuf, "."); + + /* Quit, starved, ascended, and escaped contain no second line */ + if (second_line) + Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death+1); + + lngr = (int)strlen(linebuf); + if (t1->hp <= 0) hpbuf[0] = '-', hpbuf[1] = '\0'; + else Sprintf(hpbuf, "%d", t1->hp); + /* beginning of hp column after padding (not actually padded yet) */ + hppos = COLNO - (sizeof(" Hp [max]")-1); /* sizeof(str) includes \0 */ + while (lngr >= hppos) { + for(bp = eos(linebuf); + !(*bp == ' ' && (bp-linebuf < hppos)); + bp--) + ; + /* special case: if about to wrap in the middle of maximum + dungeon depth reached, wrap in front of it instead */ + if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5)) bp -= 5; + Strcpy(linebuf3, bp+1); + *bp = 0; + if (so) { + while (bp < linebuf + (COLNO-1)) *bp++ = ' '; + *bp = 0; + topten_print_bold(linebuf); + } else + topten_print(linebuf); + Sprintf(linebuf, "%15s %s", "", linebuf3); + lngr = strlen(linebuf); + } + /* beginning of hp column not including padding */ + hppos = COLNO - 7 - (int)strlen(hpbuf); + bp = eos(linebuf); + + if (bp <= linebuf + hppos) { + /* pad any necessary blanks to the hit point entry */ + while (bp < linebuf + hppos) *bp++ = ' '; + Strcpy(bp, hpbuf); + Sprintf(eos(bp), " %s[%d]", + (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "", + t1->maxhp); + } + + if (so) { + bp = eos(linebuf); + if (so >= COLNO) so = COLNO-1; + while (bp < linebuf + so) *bp++ = ' '; + *bp = 0; + topten_print_bold(linebuf); + } else + topten_print(linebuf); +} + +STATIC_OVL int +score_wanted(current_ver, rank, t1, playerct, players, uid) +boolean current_ver; +int rank; +struct toptenentry *t1; +int playerct; +const char **players; +int uid; +{ + int i; + + if (current_ver && (t1->ver_major != VERSION_MAJOR || + t1->ver_minor != VERSION_MINOR || + t1->patchlevel != PATCHLEVEL)) + return 0; + +#ifdef PERS_IS_UID + if (!playerct && t1->uid == uid) + return 1; +#endif + + for (i = 0; i < playerct; i++) { + if (players[i][0] == '-' && index("pr", players[i][1]) && + players[i][2] == 0 && i + 1 < playerct) { + char *arg = (char *)players[i + 1]; + if ((players[i][1] == 'p' && + str2role(arg) == str2role(t1->plrole)) || + (players[i][1] == 'r' && + str2race(arg) == str2race(t1->plrace))) + return 1; + i++; + } else if (strcmp(players[i], "all") == 0 || + strncmp(t1->name, players[i], NAMSZ) == 0 || + (players[i][0] == '-' && + players[i][1] == t1->plrole[0] && + players[i][2] == 0) || + (digit(players[i][0]) && rank <= atoi(players[i]))) + return 1; + } + return 0; +} + +/* + * print selected parts of score list. + * argc >= 2, with argv[0] untrustworthy (directory names, et al.), + * and argv[1] starting with "-s". + */ +void +prscore(argc,argv) +int argc; +char **argv; +{ + const char **players; + int playerct, rank; + boolean current_ver = TRUE, init_done = FALSE; + register struct toptenentry *t1; + FILE *rfile; + boolean match_found = FALSE; + register int i; + char pbuf[BUFSZ]; + int uid = -1; +#ifndef PERS_IS_UID + const char *player0; +#endif + + if (argc < 2 || strncmp(argv[1], "-s", 2)) { + raw_printf("prscore: bad arguments (%d)", argc); + return; + } + + rfile = fopen_datafile(RECORD, "r", SCOREPREFIX); + if (!rfile) { + raw_print("Cannot open record file!"); + return; + } + +#ifdef AMIGA + { + extern winid amii_rawprwin; + init_nhwindows(&argc, argv); + amii_rawprwin = create_nhwindow(NHW_TEXT); + } +#endif + + /* If the score list isn't after a game, we never went through + * initialization. */ + if (wiz1_level.dlevel == 0) { + dlb_init(); + init_dungeons(); + init_done = TRUE; + } + + if (!argv[1][2]){ /* plain "-s" */ + argc--; + argv++; + } else argv[1] += 2; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + current_ver = FALSE; + argc--; + argv++; + } + + if (argc <= 1) { +#ifdef PERS_IS_UID + uid = getuid(); + playerct = 0; + players = (const char **)0; +#else + player0 = plname; + if (!*player0) +# ifdef AMIGA + player0 = "all"; /* single user system */ +# else + player0 = "hackplayer"; +# endif + playerct = 1; + players = &player0; +#endif + } else { + playerct = --argc; + players = (const char **)++argv; + } + raw_print(""); + + t1 = tt_head = newttentry(); + for (rank = 1; ; rank++) { + readentry(rfile, t1); + if (t1->points == 0) break; + if (!match_found && + score_wanted(current_ver, rank, t1, playerct, players, uid)) + match_found = TRUE; + t1->tt_next = newttentry(); + t1 = t1->tt_next; + } + + (void) fclose(rfile); + if (init_done) { + free_dungeons(); + dlb_cleanup(); + } + + if (match_found) { + outheader(); + t1 = tt_head; + for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { + if (score_wanted(current_ver, rank, t1, playerct, players, uid)) + (void) outentry(rank, t1, 0); + } + } else { + Sprintf(pbuf, "Cannot find any %sentries for ", + current_ver ? "current " : ""); + if (playerct < 1) Strcat(pbuf, "you."); + else { + if (playerct > 1) Strcat(pbuf, "any of "); + for (i = 0; i < playerct; i++) { + /* stop printing players if there are too many to fit */ + if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) { + if (strlen(pbuf) < BUFSZ-4) Strcat(pbuf, "..."); + else Strcpy(pbuf+strlen(pbuf)-4, "..."); + break; + } + Strcat(pbuf, players[i]); + if (i < playerct-1) { + if (players[i][0] == '-' && + index("pr", players[i][1]) && players[i][2] == 0) + Strcat(pbuf, " "); + else Strcat(pbuf, ":"); + } + } + } + raw_print(pbuf); + raw_printf("Usage: %s -s [-v] [maxrank] [playernames]", + + hname); + raw_printf("Player types are: [-p role] [-r race]"); + } + free_ttlist(tt_head); +#ifdef AMIGA + { + extern winid amii_rawprwin; + display_nhwindow(amii_rawprwin, 1); + destroy_nhwindow(amii_rawprwin); + amii_rawprwin = WIN_ERR; + } +#endif +} + +STATIC_OVL int +classmon(plch, fem) + char *plch; + boolean fem; +{ + int i; + + /* Look for this role in the role table */ + for (i = 0; roles[i].name.m; i++) + if (!strncmp(plch, roles[i].filecode, ROLESZ)) { + if (fem && roles[i].femalenum != NON_PM) + return roles[i].femalenum; + else if (roles[i].malenum != NON_PM) + return roles[i].malenum; + else + return PM_HUMAN; + } + /* this might be from a 3.2.x score for former Elf class */ + if (!strcmp(plch, "E")) return PM_RANGER; + + impossible("What weird role is this? (%s)", plch); + return (PM_HUMAN_MUMMY); +} + +/* + * Get a random player name and class from the high score list, + * and attach them to an object (for statues or morgue corpses). + */ +struct obj * +tt_oname(otmp) +struct obj *otmp; +{ + int rank; + register int i; + register struct toptenentry *tt; + FILE *rfile; + struct toptenentry tt_buf; + + if (!otmp) return((struct obj *) 0); + + rfile = fopen_datafile(RECORD, "r", SCOREPREFIX); + if (!rfile) { + impossible("Cannot open record file!"); + return (struct obj *)0; + } + + tt = &tt_buf; + rank = rnd(10); +pickentry: + for(i = rank; i; i--) { + readentry(rfile, tt); + if(tt->points == 0) break; + } + + if(tt->points == 0) { + if(rank > 1) { + rank = 1; + rewind(rfile); + goto pickentry; + } + otmp = (struct obj *) 0; + } else { + /* reset timer in case corpse started out as lizard or troll */ + if (otmp->otyp == CORPSE) obj_stop_timers(otmp); + otmp->corpsenm = classmon(tt->plrole, (tt->plgend[0] == 'F')); + otmp->owt = weight(otmp); + otmp = oname(otmp, tt->name); + if (otmp->otyp == CORPSE) start_corpse_timeout(otmp); + } + + (void) fclose(rfile); + return otmp; +} + +#ifdef NO_SCAN_BRACK +/* Lattice scanf isn't up to reading the scorefile. What */ +/* follows deals with that; I admit it's ugly. (KL) */ +/* Now generally available (KL) */ +STATIC_OVL void +nsb_mung_line(p) + char *p; +{ + while ((p = index(p, ' ')) != 0) *p = '|'; +} + +STATIC_OVL void +nsb_unmung_line(p) + char *p; +{ + while ((p = index(p, '|')) != 0) *p = ' '; +} +#endif /* NO_SCAN_BRACK */ + +/*topten.c*/ diff --git a/src/track.c b/src/track.c new file mode 100644 index 0000000..d8d9e71 --- /dev/null +++ b/src/track.c @@ -0,0 +1,68 @@ +/* SCCS Id: @(#)track.c 3.4 87/08/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ +/* track.c - version 1.0.2 */ + +#include "hack.h" + +#define UTSZ 50 + +STATIC_VAR NEARDATA int utcnt, utpnt; +STATIC_VAR NEARDATA coord utrack[UTSZ]; + +#ifdef OVLB + +void +initrack() +{ + utcnt = utpnt = 0; +} + +#endif /* OVLB */ +#ifdef OVL1 + +/* add to track */ +void +settrack() +{ + if(utcnt < UTSZ) utcnt++; + if(utpnt == UTSZ) utpnt = 0; + utrack[utpnt].x = u.ux; + utrack[utpnt].y = u.uy; + utpnt++; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +coord * +gettrack(x, y) +register int x, y; +{ + register int cnt, ndist; + register coord *tc; + cnt = utcnt; + for(tc = &utrack[utpnt]; cnt--; ){ + if(tc == utrack) tc = &utrack[UTSZ-1]; + else tc--; + ndist = distmin(x,y,tc->x,tc->y); + + /* if far away, skip track entries til we're closer */ + if(ndist > 2) { + ndist -= 2; /* be careful due to extra decrement at top of loop */ + cnt -= ndist; + if(cnt <= 0) + return (coord *) 0; /* too far away, no matches possible */ + if(tc < &utrack[ndist]) + tc += (UTSZ-ndist); + else + tc -= ndist; + } else if(ndist <= 1) + return(ndist ? tc : 0); + } + return (coord *)0; +} + +#endif /* OVL0 */ + +/*track.c*/ diff --git a/src/trap.c b/src/trap.c new file mode 100644 index 0000000..d336276 --- /dev/null +++ b/src/trap.c @@ -0,0 +1,4015 @@ +/* SCCS Id: @(#)trap.c 3.4 2003/10/20 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +extern const char * const destroy_strings[]; /* from zap.c */ + +STATIC_DCL void FDECL(dofiretrap, (struct obj *)); +STATIC_DCL void NDECL(domagictrap); +STATIC_DCL boolean FDECL(emergency_disrobe,(boolean *)); +STATIC_DCL int FDECL(untrap_prob, (struct trap *ttmp)); +STATIC_DCL void FDECL(cnv_trap_obj, (int, int, struct trap *)); +STATIC_DCL void FDECL(move_into_trap, (struct trap *)); +STATIC_DCL int FDECL(try_disarm, (struct trap *,BOOLEAN_P)); +STATIC_DCL void FDECL(reward_untrap, (struct trap *, struct monst *)); +STATIC_DCL int FDECL(disarm_holdingtrap, (struct trap *)); +STATIC_DCL int FDECL(disarm_landmine, (struct trap *)); +STATIC_DCL int FDECL(disarm_squeaky_board, (struct trap *)); +STATIC_DCL int FDECL(disarm_shooting_trap, (struct trap *, int)); +STATIC_DCL int FDECL(try_lift, (struct monst *, struct trap *, int, BOOLEAN_P)); +STATIC_DCL int FDECL(help_monster_out, (struct monst *, struct trap *)); +STATIC_DCL boolean FDECL(thitm, (int,struct monst *,struct obj *,int,BOOLEAN_P)); +STATIC_DCL int FDECL(mkroll_launch, + (struct trap *,XCHAR_P,XCHAR_P,SHORT_P,long)); +STATIC_DCL boolean FDECL(isclearpath,(coord *, int, SCHAR_P, SCHAR_P)); +#ifdef STEED +STATIC_OVL int FDECL(steedintrap, (struct trap *, struct obj *)); +STATIC_OVL boolean FDECL(keep_saddle_with_steedcorpse, + (unsigned, struct obj *, struct obj *)); +#endif + +#ifndef OVLB +STATIC_VAR const char *a_your[2]; +STATIC_VAR const char *A_Your[2]; +STATIC_VAR const char tower_of_flame[]; +STATIC_VAR const char *A_gush_of_water_hits; +STATIC_VAR const char * const blindgas[6]; + +#else + +STATIC_VAR const char * const a_your[2] = { "a", "your" }; +STATIC_VAR const char * const A_Your[2] = { "A", "Your" }; +STATIC_VAR const char tower_of_flame[] = "tower of flame"; +STATIC_VAR const char * const A_gush_of_water_hits = "A gush of water hits"; +STATIC_VAR const char * const blindgas[6] = + {"humid", "odorless", "pungent", "chilling", "acrid", "biting"}; + +#endif /* OVLB */ + +#ifdef OVLB + +/* called when you're hit by fire (dofiretrap,buzz,zapyourself,explode) */ +boolean /* returns TRUE if hit on torso */ +burnarmor(victim) +struct monst *victim; +{ + struct obj *item; + char buf[BUFSZ]; + int mat_idx; + + if (!victim) return 0; +#define burn_dmg(obj,descr) rust_dmg(obj, descr, 0, FALSE, victim) + while (1) { + switch (rn2(5)) { + case 0: + item = (victim == &youmonst) ? uarmh : which_armor(victim, W_ARMH); + if (item) { + mat_idx = objects[item->otyp].oc_material; + Sprintf(buf,"%s helmet", materialnm[mat_idx] ); + } + if (!burn_dmg(item, item ? buf : "helmet")) continue; + break; + case 1: + item = (victim == &youmonst) ? uarmc : which_armor(victim, W_ARMC); + if (item) { + (void) burn_dmg(item, cloak_simple_name(item)); + return TRUE; + } + item = (victim == &youmonst) ? uarm : which_armor(victim, W_ARM); + if (item) { + (void) burn_dmg(item, xname(item)); + return TRUE; + } +#ifdef TOURIST + item = (victim == &youmonst) ? uarmu : which_armor(victim, W_ARMU); + if (item) + (void) burn_dmg(item, "shirt"); +#endif + return TRUE; + case 2: + item = (victim == &youmonst) ? uarms : which_armor(victim, W_ARMS); + if (!burn_dmg(item, "wooden shield")) continue; + break; + case 3: + item = (victim == &youmonst) ? uarmg : which_armor(victim, W_ARMG); + if (!burn_dmg(item, "gloves")) continue; + break; + case 4: + item = (victim == &youmonst) ? uarmf : which_armor(victim, W_ARMF); + if (!burn_dmg(item, "boots")) continue; + break; + } + break; /* Out of while loop */ + } + return FALSE; +#undef burn_dmg +} + +/* Generic rust-armor function. Returns TRUE if a message was printed; + * "print", if set, means to print a message (and thus to return TRUE) even + * if the item could not be rusted; otherwise a message is printed and TRUE is + * returned only for rustable items. + */ +boolean +rust_dmg(otmp, ostr, type, print, victim) +register struct obj *otmp; +register const char *ostr; +int type; +boolean print; +struct monst *victim; +{ + static NEARDATA const char * const action[] = { "smoulder", "rust", "rot", "corrode" }; + static NEARDATA const char * const msg[] = { "burnt", "rusted", "rotten", "corroded" }; + boolean vulnerable = FALSE; + boolean grprot = FALSE; + boolean is_primary = TRUE; + boolean vismon = (victim != &youmonst) && canseemon(victim); + int erosion; + + if (!otmp) return(FALSE); + switch(type) { + case 0: vulnerable = is_flammable(otmp); + break; + case 1: vulnerable = is_rustprone(otmp); + grprot = TRUE; + break; + case 2: vulnerable = is_rottable(otmp); + is_primary = FALSE; + break; + case 3: vulnerable = is_corrodeable(otmp); + grprot = TRUE; + is_primary = FALSE; + break; + } + erosion = is_primary ? otmp->oeroded : otmp->oeroded2; + + if (!print && (!vulnerable || otmp->oerodeproof || erosion == MAX_ERODE)) + return FALSE; + + if (!vulnerable) { + if (flags.verbose) { + if (victim == &youmonst) + Your("%s %s not affected.", ostr, vtense(ostr, "are")); + else if (vismon) + pline("%s's %s %s not affected.", Monnam(victim), ostr, + vtense(ostr, "are")); + } + } else if (erosion < MAX_ERODE) { + if (grprot && otmp->greased) { + grease_protect(otmp,ostr,victim); + } else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) { + if (flags.verbose) { + if (victim == &youmonst) + pline("Somehow, your %s %s not affected.", + ostr, vtense(ostr, "are")); + else if (vismon) + pline("Somehow, %s's %s %s not affected.", + mon_nam(victim), ostr, vtense(ostr, "are")); + } + } else { + if (victim == &youmonst) + Your("%s %s%s!", ostr, + vtense(ostr, action[type]), + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + else if (vismon) + pline("%s's %s %s%s!", Monnam(victim), ostr, + vtense(ostr, action[type]), + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + if (is_primary) + otmp->oeroded++; + else + otmp->oeroded2++; + update_inventory(); + } + } else { + if (flags.verbose) { + if (victim == &youmonst) + Your("%s %s completely %s.", ostr, + vtense(ostr, Blind ? "feel" : "look"), + msg[type]); + else if (vismon) + pline("%s's %s %s completely %s.", + Monnam(victim), ostr, + vtense(ostr, "look"), msg[type]); + } + } + return(TRUE); +} + +void +grease_protect(otmp,ostr,victim) +register struct obj *otmp; +register const char *ostr; +struct monst *victim; +{ + static const char txt[] = "protected by the layer of grease!"; + boolean vismon = victim && (victim != &youmonst) && canseemon(victim); + + if (ostr) { + if (victim == &youmonst) + Your("%s %s %s", ostr, vtense(ostr, "are"), txt); + else if (vismon) + pline("%s's %s %s %s", Monnam(victim), + ostr, vtense(ostr, "are"), txt); + } else { + if (victim == &youmonst) + Your("%s %s",aobjnam(otmp,"are"), txt); + else if (vismon) + pline("%s's %s %s", Monnam(victim), aobjnam(otmp,"are"), txt); + } + if (!rn2(2)) { + otmp->greased = 0; + if (carried(otmp)) { + pline_The("grease dissolves."); + update_inventory(); + } + } +} + +struct trap * +maketrap(x,y,typ) +register int x, y, typ; +{ + register struct trap *ttmp; + register struct rm *lev; + register boolean oldplace; + + if ((ttmp = t_at(x,y)) != 0) { + if (ttmp->ttyp == MAGIC_PORTAL) return (struct trap *)0; + oldplace = TRUE; + if (u.utrap && (x == u.ux) && (y == u.uy) && + ((u.utraptype == TT_BEARTRAP && typ != BEAR_TRAP) || + (u.utraptype == TT_WEB && typ != WEB) || + (u.utraptype == TT_PIT && typ != PIT && typ != SPIKED_PIT))) + u.utrap = 0; + } else { + oldplace = FALSE; + ttmp = newtrap(); + ttmp->tx = x; + ttmp->ty = y; + ttmp->launch.x = -1; /* force error if used before set */ + ttmp->launch.y = -1; + } + ttmp->ttyp = typ; + switch(typ) { + case STATUE_TRAP: /* create a "living" statue */ + { struct monst *mtmp; + struct obj *otmp, *statue; + + statue = mkcorpstat(STATUE, (struct monst *)0, + &mons[rndmonnum()], x, y, FALSE); + mtmp = makemon(&mons[statue->corpsenm], 0, 0, NO_MM_FLAGS); + if (!mtmp) break; /* should never happen */ + while(mtmp->minvent) { + otmp = mtmp->minvent; + otmp->owornmask = 0; + obj_extract_self(otmp); + (void) add_to_container(statue, otmp); + } + statue->owt = weight(statue); + mongone(mtmp); + break; + } + case ROLLING_BOULDER_TRAP: /* boulder will roll towards trigger */ + (void) mkroll_launch(ttmp, x, y, BOULDER, 1L); + break; + case HOLE: + case PIT: + case SPIKED_PIT: + case TRAPDOOR: + lev = &levl[x][y]; + if (*in_rooms(x, y, SHOPBASE) && + ((typ == HOLE || typ == TRAPDOOR) || + IS_DOOR(lev->typ) || IS_WALL(lev->typ))) + add_damage(x, y, /* schedule repair */ + ((IS_DOOR(lev->typ) || IS_WALL(lev->typ)) + && !flags.mon_moving) ? 200L : 0L); + lev->doormask = 0; /* subsumes altarmask, icedpool... */ + if (IS_ROOM(lev->typ)) /* && !IS_AIR(lev->typ) */ + lev->typ = ROOM; + + /* + * some cases which can happen when digging + * down while phazing thru solid areas + */ + else if (lev->typ == STONE || lev->typ == SCORR) + lev->typ = CORR; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + lev->typ = level.flags.is_maze_lev ? ROOM : + level.flags.is_cavernous_lev ? CORR : DOOR; + + unearth_objs(x, y); + break; + } + if (ttmp->ttyp == HOLE) ttmp->tseen = 1; /* You can't hide a hole */ + else ttmp->tseen = 0; + ttmp->once = 0; + ttmp->madeby_u = 0; + ttmp->dst.dnum = -1; + ttmp->dst.dlevel = -1; + if (!oldplace) { + ttmp->ntrap = ftrap; + ftrap = ttmp; + } + return(ttmp); +} + +void +fall_through(td) +boolean td; /* td == TRUE : trap door or hole */ +{ + d_level dtmp; + char msgbuf[BUFSZ]; + const char *dont_fall = 0; + register int newlevel = dunlev(&u.uz); + + /* KMH -- You can't escape the Sokoban level traps */ + if(Blind && Levitation && !In_sokoban(&u.uz)) return; + + do { + newlevel++; + } while(!rn2(4) && newlevel < dunlevs_in_dungeon(&u.uz)); + + if(td) { + struct trap *t=t_at(u.ux,u.uy); + seetrap(t); + if (!In_sokoban(&u.uz)) { + if (t->ttyp == TRAPDOOR) + pline("A trap door opens up under you!"); + else + pline("There's a gaping hole under you!"); + } + } else pline_The("%s opens up under you!", surface(u.ux,u.uy)); + + if (In_sokoban(&u.uz) && Can_fall_thru(&u.uz)) + ; /* KMH -- You can't escape the Sokoban level traps */ + else if(Levitation || u.ustuck || !Can_fall_thru(&u.uz) + || Flying || is_clinger(youmonst.data) + || (Inhell && !u.uevent.invoked && + newlevel == dunlevs_in_dungeon(&u.uz)) + ) { + dont_fall = "don't fall in."; + } else if (youmonst.data->msize >= MZ_HUGE) { + dont_fall = "don't fit through."; + } else if (!next_to_u()) { + dont_fall = "are jerked back by your pet!"; + } + if (dont_fall) { + You(dont_fall); + /* hero didn't fall through, but any objects here might */ + impact_drop((struct obj *)0, u.ux, u.uy, 0); + if (!td) { + display_nhwindow(WIN_MESSAGE, FALSE); + pline_The("opening under you closes up."); + } + return; + } + + if(*u.ushops) shopdig(1); + if (Is_stronghold(&u.uz)) { + find_hell(&dtmp); + } else { + dtmp.dnum = u.uz.dnum; + dtmp.dlevel = newlevel; + } + if (!td) + Sprintf(msgbuf, "The hole in the %s above you closes up.", + ceiling(u.ux,u.uy)); + schedule_goto(&dtmp, FALSE, TRUE, 0, + (char *)0, !td ? msgbuf : (char *)0); +} + +/* + * Animate the given statue. May have been via shatter attempt, trap, + * or stone to flesh spell. Return a monster if successfully animated. + * If the monster is animated, the object is deleted. If fail_reason + * is non-null, then fill in the reason for failure (or success). + * + * The cause of animation is: + * + * ANIMATE_NORMAL - hero "finds" the monster + * ANIMATE_SHATTER - hero tries to destroy the statue + * ANIMATE_SPELL - stone to flesh spell hits the statue + * + * Perhaps x, y is not needed if we can use get_obj_location() to find + * the statue's location... ??? + */ +struct monst * +animate_statue(statue, x, y, cause, fail_reason) +struct obj *statue; +xchar x, y; +int cause; +int *fail_reason; +{ + struct permonst *mptr; + struct monst *mon = 0; + struct obj *item; + coord cc; + boolean historic = (Role_if(PM_ARCHEOLOGIST) && !flags.mon_moving && (statue->spe & STATUE_HISTORIC)); + char statuename[BUFSZ]; + + Strcpy(statuename,the(xname(statue))); + + if (statue->oxlth && statue->oattached == OATTACHED_MONST) { + cc.x = x, cc.y = y; + mon = montraits(statue, &cc); + if (mon && mon->mtame && !mon->isminion) + wary_dog(mon, TRUE); + } else { + /* statue of any golem hit with stone-to-flesh becomes flesh golem */ + if (is_golem(&mons[statue->corpsenm]) && cause == ANIMATE_SPELL) + mptr = &mons[PM_FLESH_GOLEM]; + else + mptr = &mons[statue->corpsenm]; + /* + * Guard against someone wishing for a statue of a unique monster + * (which is allowed in normal play) and then tossing it onto the + * [detected or guessed] location of a statue trap. Normally the + * uppermost statue is the one which would be activated. + */ + if ((mptr->geno & G_UNIQ) && cause != ANIMATE_SPELL) { + if (fail_reason) *fail_reason = AS_MON_IS_UNIQUE; + return (struct monst *)0; + } + if (cause == ANIMATE_SPELL && + ((mptr->geno & G_UNIQ) || mptr->msound == MS_GUARDIAN)) { + /* Statues of quest guardians or unique monsters + * will not stone-to-flesh as the real thing. + */ + mon = makemon(&mons[PM_DOPPELGANGER], x, y, + NO_MINVENT|MM_NOCOUNTBIRTH|MM_ADJACENTOK); + if (mon) { + /* makemon() will set mon->cham to + * CHAM_ORDINARY if hero is wearing + * ring of protection from shape changers + * when makemon() is called, so we have to + * check the field before calling newcham(). + */ + if (mon->cham == CHAM_DOPPELGANGER) + (void) newcham(mon, mptr, FALSE, FALSE); + } + } else + mon = makemon(mptr, x, y, (cause == ANIMATE_SPELL) ? + (NO_MINVENT | MM_ADJACENTOK) : NO_MINVENT); + } + + if (!mon) { + if (fail_reason) *fail_reason = AS_NO_MON; + return (struct monst *)0; + } + + /* in case statue is wielded and hero zaps stone-to-flesh at self */ + if (statue->owornmask) remove_worn_item(statue, TRUE); + + /* allow statues to be of a specific gender */ + if (statue->spe & STATUE_MALE) + mon->female = FALSE; + else if (statue->spe & STATUE_FEMALE) + mon->female = TRUE; + /* if statue has been named, give same name to the monster */ + if (statue->onamelth) + mon = christen_monst(mon, ONAME(statue)); + /* transfer any statue contents to monster's inventory */ + while ((item = statue->cobj) != 0) { + obj_extract_self(item); + (void) add_to_minv(mon, item); + } + m_dowear(mon, TRUE); + delobj(statue); + + /* mimic statue becomes seen mimic; other hiders won't be hidden */ + if (mon->m_ap_type) seemimic(mon); + else mon->mundetected = FALSE; + if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) { + const char *comes_to_life = nonliving(mon->data) ? + "moves" : "comes to life"; + if (cause == ANIMATE_SPELL) + pline("%s %s!", upstart(statuename), + canspotmon(mon) ? comes_to_life : "disappears"); + else + pline_The("statue %s!", + canspotmon(mon) ? comes_to_life : "disappears"); + if (historic) { + You_feel("guilty that the historic statue is now gone."); + adjalign(-1); + } + } else if (cause == ANIMATE_SHATTER) + pline("Instead of shattering, the statue suddenly %s!", + canspotmon(mon) ? "comes to life" : "disappears"); + else { /* cause == ANIMATE_NORMAL */ + You("find %s posing as a statue.", + canspotmon(mon) ? a_monnam(mon) : something); + stop_occupation(); + } + /* avoid hiding under nothing */ + if (x == u.ux && y == u.uy && + Upolyd && hides_under(youmonst.data) && !OBJ_AT(x, y)) + u.uundetected = 0; + + if (fail_reason) *fail_reason = AS_OK; + return mon; +} + +/* + * You've either stepped onto a statue trap's location or you've triggered a + * statue trap by searching next to it or by trying to break it with a wand + * or pick-axe. + */ +struct monst * +activate_statue_trap(trap, x, y, shatter) +struct trap *trap; +xchar x, y; +boolean shatter; +{ + struct monst *mtmp = (struct monst *)0; + struct obj *otmp = sobj_at(STATUE, x, y); + int fail_reason; + + /* + * Try to animate the first valid statue. Stop the loop when we + * actually create something or the failure cause is not because + * the mon was unique. + */ + deltrap(trap); + while (otmp) { + mtmp = animate_statue(otmp, x, y, + shatter ? ANIMATE_SHATTER : ANIMATE_NORMAL, &fail_reason); + if (mtmp || fail_reason != AS_MON_IS_UNIQUE) break; + + while ((otmp = otmp->nexthere) != 0) + if (otmp->otyp == STATUE) break; + } + + if (Blind) feel_location(x, y); + else newsym(x, y); + return mtmp; +} + +#ifdef STEED +STATIC_OVL boolean +keep_saddle_with_steedcorpse(steed_mid, objchn, saddle) +unsigned steed_mid; +struct obj *objchn, *saddle; +{ + if (!saddle) return FALSE; + while(objchn) { + if(objchn->otyp == CORPSE && + objchn->oattached == OATTACHED_MONST && objchn->oxlth) { + struct monst *mtmp = (struct monst *)objchn->oextra; + if (mtmp->m_id == steed_mid) { + /* move saddle */ + xchar x,y; + if (get_obj_location(objchn, &x, &y, 0)) { + obj_extract_self(saddle); + place_object(saddle, x, y); + stackobj(saddle); + } + return TRUE; + } + } + if (Has_contents(objchn) && + keep_saddle_with_steedcorpse(steed_mid, objchn->cobj, saddle)) + return TRUE; + objchn = objchn->nobj; + } + return FALSE; +} +#endif /*STEED*/ + +void +dotrap(trap, trflags) +register struct trap *trap; +unsigned trflags; +{ + register int ttype = trap->ttyp; + register struct obj *otmp; + boolean already_seen = trap->tseen; + boolean webmsgok = (!(trflags & NOWEBMSG)); + boolean forcebungle = (trflags & FORCEBUNGLE); + + nomul(0); + + /* KMH -- You can't escape the Sokoban level traps */ + if (In_sokoban(&u.uz) && + (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE || + ttype == TRAPDOOR)) { + /* The "air currents" message is still appropriate -- even when + * the hero isn't flying or levitating -- because it conveys the + * reason why the player cannot escape the trap with a dexterity + * check, clinging to the ceiling, etc. + */ + pline("Air currents pull you down into %s %s!", + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + /* then proceed to normal trap effect */ + } else if (already_seen) { + if ((Levitation || Flying) && + (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE || + ttype == BEAR_TRAP)) { + You("%s over %s %s.", + Levitation ? "float" : "fly", + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + return; + } + if(!Fumbling && ttype != MAGIC_PORTAL && + ttype != ANTI_MAGIC && !forcebungle && + (!rn2(5) || + ((ttype == PIT || ttype == SPIKED_PIT) && is_clinger(youmonst.data)))) { + You("escape %s %s.", + (ttype == ARROW_TRAP && !trap->madeby_u) ? "an" : + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + return; + } + } + +#ifdef STEED + if (u.usteed) u.usteed->mtrapseen |= (1 << (ttype-1)); +#endif + + switch(ttype) { + case ARROW_TRAP: + if (trap->once && trap->tseen && !rn2(15)) { + You_hear("a loud click!"); + deltrap(trap); + newsym(u.ux,u.uy); + break; + } + trap->once = 1; + seetrap(trap); + pline("An arrow shoots out at you!"); + otmp = mksobj(ARROW, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + otmp->opoisoned = 0; +#ifdef STEED + if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */; + else +#endif + if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) { + obfree(otmp, (struct obj *)0); + } else { + place_object(otmp, u.ux, u.uy); + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux, u.uy); + } + break; + case DART_TRAP: + if (trap->once && trap->tseen && !rn2(15)) { + You_hear("a soft click."); + deltrap(trap); + newsym(u.ux,u.uy); + break; + } + trap->once = 1; + seetrap(trap); + pline("A little dart shoots out at you!"); + otmp = mksobj(DART, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (!rn2(6)) otmp->opoisoned = 1; +#ifdef STEED + if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */; + else +#endif + if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) { + if (otmp->opoisoned) + poisoned("dart", A_CON, "little dart", -10); + obfree(otmp, (struct obj *)0); + } else { + place_object(otmp, u.ux, u.uy); + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux, u.uy); + } + break; + case ROCKTRAP: + if (trap->once && trap->tseen && !rn2(15)) { + pline("A trap door in %s opens, but nothing falls out!", + the(ceiling(u.ux,u.uy))); + deltrap(trap); + newsym(u.ux,u.uy); + } else { + int dmg = d(2,6); /* should be std ROCK dmg? */ + + trap->once = 1; + seetrap(trap); + otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + + pline("A trap door in %s opens and %s falls on your %s!", + the(ceiling(u.ux,u.uy)), + an(xname(otmp)), + body_part(HEAD)); + + if (uarmh) { + if(is_metallic(uarmh)) { + pline("Fortunately, you are wearing a hard helmet."); + dmg = 2; + } else if (flags.verbose) { + Your("%s does not protect you.", xname(uarmh)); + } + } + + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux,u.uy); /* map the rock */ + + losehp(dmg, "falling rock", KILLED_BY_AN); + exercise(A_STR, FALSE); + } + break; + + case SQKY_BOARD: /* stepped on a squeaky board */ + if (Levitation || Flying) { + if (!Blind) { + seetrap(trap); + if (Hallucination) + You("notice a crease in the linoleum."); + else + You("notice a loose board below you."); + } + } else { + seetrap(trap); + pline("A board beneath you squeaks loudly."); + wake_nearby(); + } + break; + + case BEAR_TRAP: + if(Levitation || Flying) break; + seetrap(trap); + if(amorphous(youmonst.data) || is_whirly(youmonst.data) || + unsolid(youmonst.data)) { + pline("%s bear trap closes harmlessly through you.", + A_Your[trap->madeby_u]); + break; + } + if( +#ifdef STEED + !u.usteed && +#endif + youmonst.data->msize <= MZ_SMALL) { + pline("%s bear trap closes harmlessly over you.", + A_Your[trap->madeby_u]); + break; + } + u.utrap = rn1(4, 4); + u.utraptype = TT_BEARTRAP; +#ifdef STEED + if (u.usteed) { + pline("%s bear trap closes on %s %s!", + A_Your[trap->madeby_u], s_suffix(mon_nam(u.usteed)), + mbodypart(u.usteed, FOOT)); + } else +#endif + { + pline("%s bear trap closes on your %s!", + A_Your[trap->madeby_u], body_part(FOOT)); + if(u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR) + You("howl in anger!"); + } + exercise(A_DEX, FALSE); + break; + + case SLP_GAS_TRAP: + seetrap(trap); + if(Sleep_resistance || breathless(youmonst.data)) { + You("are enveloped in a cloud of gas!"); + break; + } + pline("A cloud of gas puts you to sleep!"); + fall_asleep(-rnd(25), TRUE); +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + break; + + case RUST_TRAP: + seetrap(trap); + if (u.umonnum == PM_IRON_GOLEM) { + int dam = u.mhmax; + + pline("%s you!", A_gush_of_water_hits); + You("are covered with rust!"); + if (Half_physical_damage) dam = (dam+1) / 2; + losehp(dam, "rusting away", KILLED_BY); + break; + } else if (u.umonnum == PM_GREMLIN && rn2(3)) { + pline("%s you!", A_gush_of_water_hits); + (void)split_mon(&youmonst, (struct monst *)0); + break; + } + + /* Unlike monsters, traps cannot aim their rust attacks at + * you, so instead of looping through and taking either the + * first rustable one or the body, we take whatever we get, + * even if it is not rustable. + */ + switch (rn2(5)) { + case 0: + pline("%s you on the %s!", A_gush_of_water_hits, + body_part(HEAD)); + (void) rust_dmg(uarmh, "helmet", 1, TRUE, &youmonst); + break; + case 1: + pline("%s your left %s!", A_gush_of_water_hits, + body_part(ARM)); + if (rust_dmg(uarms, "shield", 1, TRUE, &youmonst)) + break; + if (u.twoweap || (uwep && bimanual(uwep))) + erode_obj(u.twoweap ? uswapwep : uwep, FALSE, TRUE); +glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst); + /* Not "metal gauntlets" since it gets called + * even if it's leather for the message + */ + break; + case 2: + pline("%s your right %s!", A_gush_of_water_hits, + body_part(ARM)); + erode_obj(uwep, FALSE, TRUE); + goto glovecheck; + default: + pline("%s you!", A_gush_of_water_hits); + for (otmp=invent; otmp; otmp = otmp->nobj) + (void) snuff_lit(otmp); + if (uarmc) + (void) rust_dmg(uarmc, cloak_simple_name(uarmc), + 1, TRUE, &youmonst); + else if (uarm) + (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst); +#ifdef TOURIST + else if (uarmu) + (void) rust_dmg(uarmu, "shirt", 1, TRUE, &youmonst); +#endif + } + update_inventory(); + break; + + case FIRE_TRAP: + seetrap(trap); + dofiretrap((struct obj *)0); + break; + + case PIT: + case SPIKED_PIT: + /* KMH -- You can't escape the Sokoban level traps */ + if (!In_sokoban(&u.uz) && (Levitation || Flying)) break; + seetrap(trap); + if (!In_sokoban(&u.uz) && is_clinger(youmonst.data)) { + if(trap->tseen) { + You("see %s %spit below you.", a_your[trap->madeby_u], + ttype == SPIKED_PIT ? "spiked " : ""); + } else { + pline("%s pit %sopens up under you!", + A_Your[trap->madeby_u], + ttype == SPIKED_PIT ? "full of spikes " : ""); + You("don't fall in!"); + } + break; + } + if (!In_sokoban(&u.uz)) { + char verbbuf[BUFSZ]; +#ifdef STEED + if (u.usteed) { + if ((trflags & RECURSIVETRAP) != 0) + Sprintf(verbbuf, "and %s fall", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + (char *)0, SUPPRESS_SADDLE, FALSE)); + else + Sprintf(verbbuf,"lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)); + } else +#endif + Strcpy(verbbuf,"fall"); + You("%s into %s pit!", verbbuf, a_your[trap->madeby_u]); + } + /* wumpus reference */ + if (Role_if(PM_RANGER) && !trap->madeby_u && !trap->once && + In_quest(&u.uz) && Is_qlocate(&u.uz)) { + pline("Fortunately it has a bottom after all..."); + trap->once = 1; + } else if (u.umonnum == PM_PIT_VIPER || + u.umonnum == PM_PIT_FIEND) + pline("How pitiful. Isn't that the pits?"); + if (ttype == SPIKED_PIT) { + const char *predicament = "on a set of sharp iron spikes"; +#ifdef STEED + if (u.usteed) { + pline("%s lands %s!", + upstart(x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)), + predicament); + } else +#endif + You("land %s!", predicament); + } + if (!Passes_walls) + u.utrap = rn1(6,2); + u.utraptype = TT_PIT; +#ifdef STEED + if (!steedintrap(trap, (struct obj *)0)) { +#endif + if (ttype == SPIKED_PIT) { + losehp(rnd(10),"fell into a pit of iron spikes", + NO_KILLER_PREFIX); + if (!rn2(6)) + poisoned("spikes", A_STR, "fall onto poison spikes", 8); + } else + losehp(rnd(6),"fell into a pit", NO_KILLER_PREFIX); + if (Punished && !carried(uball)) { + unplacebc(); + ballfall(); + placebc(); + } + selftouch("Falling, you"); + vision_full_recalc = 1; /* vision limits change */ + exercise(A_STR, FALSE); + exercise(A_DEX, FALSE); +#ifdef STEED + } +#endif + break; + case HOLE: + case TRAPDOOR: + if (!Can_fall_thru(&u.uz)) { + seetrap(trap); /* normally done in fall_through */ + impossible("dotrap: %ss cannot exist on this level.", + defsyms[trap_to_defsym(ttype)].explanation); + break; /* don't activate it after all */ + } + fall_through(TRUE); + break; + + case TELEP_TRAP: + seetrap(trap); + tele_trap(trap); + break; + case LEVEL_TELEP: + seetrap(trap); + level_tele_trap(trap); + break; + + case WEB: /* Our luckless player has stumbled into a web. */ + seetrap(trap); + if (amorphous(youmonst.data) || is_whirly(youmonst.data) || + unsolid(youmonst.data)) { + if (acidic(youmonst.data) || u.umonnum == PM_GELATINOUS_CUBE || + u.umonnum == PM_FIRE_ELEMENTAL) { + if (webmsgok) + You("%s %s spider web!", + (u.umonnum == PM_FIRE_ELEMENTAL) ? "burn" : "dissolve", + a_your[trap->madeby_u]); + deltrap(trap); + newsym(u.ux,u.uy); + break; + } + if (webmsgok) You("flow through %s spider web.", + a_your[trap->madeby_u]); + break; + } + if (webmaker(youmonst.data)) { + if (webmsgok) + pline(trap->madeby_u ? "You take a walk on your web." + : "There is a spider web here."); + break; + } + if (webmsgok) { + char verbbuf[BUFSZ]; + verbbuf[0] = '\0'; +#ifdef STEED + if (u.usteed) + Sprintf(verbbuf,"lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)); + else +#endif + + Sprintf(verbbuf, "%s", Levitation ? (const char *)"float" : + locomotion(youmonst.data, "stumble")); + You("%s into %s spider web!", + verbbuf, a_your[trap->madeby_u]); + } + u.utraptype = TT_WEB; + + /* Time stuck in the web depends on your/steed strength. */ + { + register int str = ACURR(A_STR); + +#ifdef STEED + /* If mounted, the steed gets trapped. Use mintrap + * to do all the work. If mtrapped is set as a result, + * unset it and set utrap instead. In the case of a + * strongmonst and mintrap said it's trapped, use a + * short but non-zero trap time. Otherwise, monsters + * have no specific strength, so use player strength. + * This gets skipped for webmsgok, which implies that + * the steed isn't a factor. + */ + if (u.usteed && webmsgok) { + /* mtmp location might not be up to date */ + u.usteed->mx = u.ux; + u.usteed->my = u.uy; + + /* mintrap currently does not return 2(died) for webs */ + if (mintrap(u.usteed)) { + u.usteed->mtrapped = 0; + if (strongmonst(u.usteed->data)) str = 17; + } else { + break; + } + + webmsgok = FALSE; /* mintrap printed the messages */ + } +#endif + if (str <= 3) u.utrap = rn1(6,6); + else if (str < 6) u.utrap = rn1(6,4); + else if (str < 9) u.utrap = rn1(4,4); + else if (str < 12) u.utrap = rn1(4,2); + else if (str < 15) u.utrap = rn1(2,2); + else if (str < 18) u.utrap = rnd(2); + else if (str < 69) u.utrap = 1; + else { + u.utrap = 0; + if (webmsgok) + You("tear through %s web!", a_your[trap->madeby_u]); + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + } + } + break; + + case STATUE_TRAP: + (void) activate_statue_trap(trap, u.ux, u.uy, FALSE); + break; + + case MAGIC_TRAP: /* A magic trap. */ + seetrap(trap); + if (!rn2(30)) { + deltrap(trap); + newsym(u.ux,u.uy); /* update position */ + You("are caught in a magical explosion!"); + losehp(rnd(10), "magical explosion", KILLED_BY_AN); + Your("body absorbs some of the magical energy!"); + u.uen = (u.uenmax += 2); + } else domagictrap(); +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + break; + + case ANTI_MAGIC: + seetrap(trap); + if(Antimagic) { + shieldeff(u.ux, u.uy); + You_feel("momentarily lethargic."); + } else drain_en(rnd(u.ulevel) + 1); + break; + + case POLY_TRAP: { + char verbbuf[BUFSZ]; + seetrap(trap); +#ifdef STEED + if (u.usteed) + Sprintf(verbbuf, "lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + (char *)0, SUPPRESS_SADDLE, FALSE)); + else +#endif + Sprintf(verbbuf,"%s", + Levitation ? (const char *)"float" : + locomotion(youmonst.data, "step")); + You("%s onto a polymorph trap!", verbbuf); + if(Antimagic || Unchanging) { + shieldeff(u.ux, u.uy); + You_feel("momentarily different."); + /* Trap did nothing; don't remove it --KAA */ + } else { +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + deltrap(trap); /* delete trap before polymorph */ + newsym(u.ux,u.uy); /* get rid of trap symbol */ + You_feel("a change coming over you."); + polyself(FALSE); + } + break; + } + case LANDMINE: { +#ifdef STEED + unsigned steed_mid = 0; + struct obj *saddle = 0; +#endif + if (Levitation || Flying) { + if (!already_seen && rn2(3)) break; + seetrap(trap); + pline("%s %s in a pile of soil below you.", + already_seen ? "There is" : "You discover", + trap->madeby_u ? "the trigger of your mine" : + "a trigger"); + if (already_seen && rn2(3)) break; + pline("KAABLAMM!!! %s %s%s off!", + forcebungle ? "Your inept attempt sets" : + "The air currents set", + already_seen ? a_your[trap->madeby_u] : "", + already_seen ? " land mine" : "it"); + } else { +#ifdef STEED + /* prevent landmine from killing steed, throwing you to + * the ground, and you being affected again by the same + * mine because it hasn't been deleted yet + */ + static boolean recursive_mine = FALSE; + + if (recursive_mine) break; +#endif + seetrap(trap); + pline("KAABLAMM!!! You triggered %s land mine!", + a_your[trap->madeby_u]); +#ifdef STEED + if (u.usteed) steed_mid = u.usteed->m_id; + recursive_mine = TRUE; + (void) steedintrap(trap, (struct obj *)0); + recursive_mine = FALSE; + saddle = sobj_at(SADDLE,u.ux, u.uy); +#endif + set_wounded_legs(LEFT_SIDE, rn1(35, 41)); + set_wounded_legs(RIGHT_SIDE, rn1(35, 41)); + exercise(A_DEX, FALSE); + } + blow_up_landmine(trap); +#ifdef STEED + if (steed_mid && saddle && !u.usteed) + (void)keep_saddle_with_steedcorpse(steed_mid, fobj, saddle); +#endif + newsym(u.ux,u.uy); /* update trap symbol */ + losehp(rnd(16), "land mine", KILLED_BY_AN); + /* fall recursively into the pit... */ + if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap, RECURSIVETRAP); + fill_pit(u.ux, u.uy); + break; + } + case ROLLING_BOULDER_TRAP: { + int style = ROLL | (trap->tseen ? LAUNCH_KNOWN : 0); + + seetrap(trap); + pline("Click! You trigger a rolling boulder trap!"); + if(!launch_obj(BOULDER, trap->launch.x, trap->launch.y, + trap->launch2.x, trap->launch2.y, style)) { + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + pline("Fortunately for you, no boulder was released."); + } + break; + } + case MAGIC_PORTAL: + seetrap(trap); + domagicportal(trap); + break; + + default: + seetrap(trap); + impossible("You hit a trap of type %u", trap->ttyp); + } +} + +#ifdef STEED +STATIC_OVL int +steedintrap(trap, otmp) +struct trap *trap; +struct obj *otmp; +{ + struct monst *mtmp = u.usteed; + struct permonst *mptr; + int tt; + boolean in_sight; + boolean trapkilled = FALSE; + boolean steedhit = FALSE; + + if (!u.usteed || !trap) return 0; + mptr = mtmp->data; + tt = trap->ttyp; + mtmp->mx = u.ux; + mtmp->my = u.uy; + + in_sight = !Blind; + switch (tt) { + case ARROW_TRAP: + if(!otmp) { + impossible("steed hit by non-existant arrow?"); + return 0; + } + if (thitm(8, mtmp, otmp, 0, FALSE)) trapkilled = TRUE; + steedhit = TRUE; + break; + case DART_TRAP: + if(!otmp) { + impossible("steed hit by non-existant dart?"); + return 0; + } + if (thitm(7, mtmp, otmp, 0, FALSE)) trapkilled = TRUE; + steedhit = TRUE; + break; + case SLP_GAS_TRAP: + if (!resists_sleep(mtmp) && !breathless(mptr) && + !mtmp->msleeping && mtmp->mcanmove) { + mtmp->mcanmove = 0; + mtmp->mfrozen = rnd(25); + if (in_sight) { + pline("%s suddenly falls asleep!", + Monnam(mtmp)); + } + } + steedhit = TRUE; + break; + case LANDMINE: + if (thitm(0, mtmp, (struct obj *)0, rnd(16), FALSE)) + trapkilled = TRUE; + steedhit = TRUE; + break; + case PIT: + case SPIKED_PIT: + if (mtmp->mhp <= 0 || + thitm(0, mtmp, (struct obj *)0, + rnd((tt == PIT) ? 6 : 10), FALSE)) + trapkilled = TRUE; + steedhit = TRUE; + break; + case POLY_TRAP: + if (!resists_magm(mtmp)) { + if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { + (void) newcham(mtmp, (struct permonst *)0, + FALSE, FALSE); + if (!can_saddle(mtmp) || !can_ride(mtmp)) { + dismount_steed(DISMOUNT_POLY); + } else { + You("have to adjust yourself in the saddle on %s.", + x_monnam(mtmp, + mtmp->mnamelth ? ARTICLE_NONE : ARTICLE_A, + (char *)0, SUPPRESS_SADDLE, FALSE)); + } + + } + steedhit = TRUE; + break; + default: + return 0; + } + } + if(trapkilled) { + dismount_steed(DISMOUNT_POLY); + return 2; + } + else if(steedhit) return 1; + else return 0; +} +#endif /*STEED*/ + +/* some actions common to both player and monsters for triggered landmine */ +void +blow_up_landmine(trap) +struct trap *trap; +{ + (void)scatter(trap->tx, trap->ty, 4, + MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS, + (struct obj *)0); + del_engr_at(trap->tx, trap->ty); + wake_nearto(trap->tx, trap->ty, 400); + if (IS_DOOR(levl[trap->tx][trap->ty].typ)) + levl[trap->tx][trap->ty].doormask = D_BROKEN; + /* TODO: destroy drawbridge if present */ + /* caller may subsequently fill pit, e.g. with a boulder */ + trap->ttyp = PIT; /* explosion creates a pit */ + trap->madeby_u = FALSE; /* resulting pit isn't yours */ + seetrap(trap); /* and it isn't concealed */ +} + +#endif /* OVLB */ +#ifdef OVL3 + +/* + * Move obj from (x1,y1) to (x2,y2) + * + * Return 0 if no object was launched. + * 1 if an object was launched and placed somewhere. + * 2 if an object was launched, but used up. + */ +int +launch_obj(otyp, x1, y1, x2, y2, style) +short otyp; +register int x1,y1,x2,y2; +int style; +{ + register struct monst *mtmp; + register struct obj *otmp, *otmp2; + register int dx,dy; + struct obj *singleobj; + boolean used_up = FALSE; + boolean otherside = FALSE; + int dist; + int tmp; + int delaycnt = 0; + + otmp = sobj_at(otyp, x1, y1); + /* Try the other side too, for rolling boulder traps */ + if (!otmp && otyp == BOULDER) { + otherside = TRUE; + otmp = sobj_at(otyp, x2, y2); + } + if (!otmp) return 0; + if (otherside) { /* swap 'em */ + int tx, ty; + + tx = x1; ty = y1; + x1 = x2; y1 = y2; + x2 = tx; y2 = ty; + } + + if (otmp->quan == 1L) { + obj_extract_self(otmp); + singleobj = otmp; + otmp = (struct obj *) 0; + } else { + singleobj = splitobj(otmp, 1L); + obj_extract_self(singleobj); + } + newsym(x1,y1); + /* in case you're using a pick-axe to chop the boulder that's being + launched (perhaps a monster triggered it), destroy context so that + next dig attempt never thinks you're resuming previous effort */ + if ((otyp == BOULDER || otyp == STATUE) && + singleobj->ox == digging.pos.x && singleobj->oy == digging.pos.y) + (void) memset((genericptr_t)&digging, 0, sizeof digging); + + dist = distmin(x1,y1,x2,y2); + bhitpos.x = x1; + bhitpos.y = y1; + dx = sgn(x2 - x1); + dy = sgn(y2 - y1); + switch (style) { + case ROLL|LAUNCH_UNSEEN: + if (otyp == BOULDER) { + You_hear(Hallucination ? + "someone bowling." : + "rumbling in the distance."); + } + style &= ~LAUNCH_UNSEEN; + goto roll; + case ROLL|LAUNCH_KNOWN: + /* use otrapped as a flag to ohitmon */ + singleobj->otrapped = 1; + style &= ~LAUNCH_KNOWN; + /* fall through */ + roll: + case ROLL: + delaycnt = 2; + /* fall through */ + default: + if (!delaycnt) delaycnt = 1; + if (!cansee(bhitpos.x,bhitpos.y)) curs_on_u(); + tmp_at(DISP_FLASH, obj_to_glyph(singleobj)); + tmp_at(bhitpos.x, bhitpos.y); + } + + /* Set the object in motion */ + while(dist-- > 0 && !used_up) { + struct trap *t; + tmp_at(bhitpos.x, bhitpos.y); + tmp = delaycnt; + + /* dstage@u.washington.edu -- Delay only if hero sees it */ + if (cansee(bhitpos.x, bhitpos.y)) + while (tmp-- > 0) delay_output(); + + bhitpos.x += dx; + bhitpos.y += dy; + t = t_at(bhitpos.x, bhitpos.y); + + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { + if (otyp == BOULDER && throws_rocks(mtmp->data)) { + if (rn2(3)) { + pline("%s snatches the boulder.", + Monnam(mtmp)); + singleobj->otrapped = 0; + (void) mpickobj(mtmp, singleobj); + used_up = TRUE; + break; + } + } + if (ohitmon(mtmp,singleobj, + (style==ROLL) ? -1 : dist, FALSE)) { + used_up = TRUE; + break; + } + } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { + if (multi) nomul(0); + if (thitu(9 + singleobj->spe, + dmgval(singleobj, &youmonst), + singleobj, (char *)0)) + stop_occupation(); + } + if (style == ROLL) { + if (down_gate(bhitpos.x, bhitpos.y) != -1) { + if(ship_object(singleobj, bhitpos.x, bhitpos.y, FALSE)){ + used_up = TRUE; + break; + } + } + if (t && otyp == BOULDER) { + switch(t->ttyp) { + case LANDMINE: + if (rn2(10) > 2) { + pline( + "KAABLAMM!!!%s", + cansee(bhitpos.x, bhitpos.y) ? + " The rolling boulder triggers a land mine." : ""); + deltrap(t); + del_engr_at(bhitpos.x,bhitpos.y); + place_object(singleobj, bhitpos.x, bhitpos.y); + singleobj->otrapped = 0; + fracture_rock(singleobj); + (void)scatter(bhitpos.x,bhitpos.y, 4, + MAY_DESTROY|MAY_HIT|MAY_FRACTURE|VIS_EFFECTS, + (struct obj *)0); + if (cansee(bhitpos.x,bhitpos.y)) + newsym(bhitpos.x,bhitpos.y); + used_up = TRUE; + } + break; + case LEVEL_TELEP: + case TELEP_TRAP: + if (cansee(bhitpos.x, bhitpos.y)) + pline("Suddenly the rolling boulder disappears!"); + else + You_hear("a rumbling stop abruptly."); + singleobj->otrapped = 0; + if (t->ttyp == TELEP_TRAP) + rloco(singleobj); + else { + int newlev = random_teleport_level(); + d_level dest; + + if (newlev == depth(&u.uz) || In_endgame(&u.uz)) + continue; + add_to_migration(singleobj); + get_level(&dest, newlev); + singleobj->ox = dest.dnum; + singleobj->oy = dest.dlevel; + singleobj->owornmask = (long)MIGR_RANDOM; + } + seetrap(t); + used_up = TRUE; + break; + case PIT: + case SPIKED_PIT: + case HOLE: + case TRAPDOOR: + /* the boulder won't be used up if there is a + monster in the trap; stop rolling anyway */ + x2 = bhitpos.x, y2 = bhitpos.y; /* stops here */ + if (flooreffects(singleobj, x2, y2, "fall")) + used_up = TRUE; + dist = -1; /* stop rolling immediately */ + break; + } + if (used_up || dist == -1) break; + } + if (flooreffects(singleobj, bhitpos.x, bhitpos.y, "fall")) { + used_up = TRUE; + break; + } + if (otyp == BOULDER && + (otmp2 = sobj_at(BOULDER, bhitpos.x, bhitpos.y)) != 0) { + const char *bmsg = + " as one boulder sets another in motion"; + + if (!isok(bhitpos.x + dx, bhitpos.y + dy) || !dist || + IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ)) + bmsg = " as one boulder hits another"; + + You_hear("a loud crash%s!", + cansee(bhitpos.x, bhitpos.y) ? bmsg : ""); + obj_extract_self(otmp2); + /* pass off the otrapped flag to the next boulder */ + otmp2->otrapped = singleobj->otrapped; + singleobj->otrapped = 0; + place_object(singleobj, bhitpos.x, bhitpos.y); + singleobj = otmp2; + otmp2 = (struct obj *)0; + wake_nearto(bhitpos.x, bhitpos.y, 10*10); + } + } + if (otyp == BOULDER && closed_door(bhitpos.x,bhitpos.y)) { + if (cansee(bhitpos.x, bhitpos.y)) + pline_The("boulder crashes through a door."); + levl[bhitpos.x][bhitpos.y].doormask = D_BROKEN; + if (dist) unblock_point(bhitpos.x, bhitpos.y); + } + + /* if about to hit iron bars, do so now */ + if (dist > 0 && isok(bhitpos.x + dx,bhitpos.y + dy) && + levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS) { + x2 = bhitpos.x, y2 = bhitpos.y; /* object stops here */ + if (hits_bars(&singleobj, x2, y2, !rn2(20), 0)) { + if (!singleobj) used_up = TRUE; + break; + } + } + } + tmp_at(DISP_END, 0); + if (!used_up) { + singleobj->otrapped = 0; + place_object(singleobj, x2,y2); + newsym(x2,y2); + return 1; + } else + return 2; +} + +#endif /* OVL3 */ +#ifdef OVLB + +void +seetrap(trap) + register struct trap *trap; +{ + if(!trap->tseen) { + trap->tseen = 1; + newsym(trap->tx, trap->ty); + } +} + +#endif /* OVLB */ +#ifdef OVL3 + +STATIC_OVL int +mkroll_launch(ttmp, x, y, otyp, ocount) +struct trap *ttmp; +xchar x,y; +short otyp; +long ocount; +{ + struct obj *otmp; + register int tmp; + schar dx,dy; + int distance; + coord cc; + coord bcc; + int trycount = 0; + boolean success = FALSE; + int mindist = 4; + + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) mindist = 2; + distance = rn1(5,4); /* 4..8 away */ + tmp = rn2(8); /* randomly pick a direction to try first */ + while (distance >= mindist) { + dx = xdir[tmp]; + dy = ydir[tmp]; + cc.x = x; cc.y = y; + /* Prevent boulder from being placed on water */ + if (ttmp->ttyp == ROLLING_BOULDER_TRAP + && is_pool(x+distance*dx,y+distance*dy)) + success = FALSE; + else success = isclearpath(&cc, distance, dx, dy); + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) { + boolean success_otherway; + bcc.x = x; bcc.y = y; + success_otherway = isclearpath(&bcc, distance, + -(dx), -(dy)); + if (!success_otherway) success = FALSE; + } + if (success) break; + if (++tmp > 7) tmp = 0; + if ((++trycount % 8) == 0) --distance; + } + if (!success) { + /* create the trap without any ammo, launch pt at trap location */ + cc.x = bcc.x = x; + cc.y = bcc.y = y; + } else { + otmp = mksobj(otyp, TRUE, FALSE); + otmp->quan = ocount; + otmp->owt = weight(otmp); + place_object(otmp, cc.x, cc.y); + stackobj(otmp); + } + ttmp->launch.x = cc.x; + ttmp->launch.y = cc.y; + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) { + ttmp->launch2.x = bcc.x; + ttmp->launch2.y = bcc.y; + } else + ttmp->launch_otyp = otyp; + newsym(ttmp->launch.x, ttmp->launch.y); + return 1; +} + +STATIC_OVL boolean +isclearpath(cc,distance,dx,dy) +coord *cc; +int distance; +schar dx,dy; +{ + uchar typ; + xchar x, y; + + x = cc->x; + y = cc->y; + while (distance-- > 0) { + x += dx; + y += dy; + typ = levl[x][y].typ; + if (!isok(x,y) || !ZAP_POS(typ) || closed_door(x,y)) + return FALSE; + } + cc->x = x; + cc->y = y; + return TRUE; +} +#endif /* OVL3 */ +#ifdef OVL1 + +int +mintrap(mtmp) +register struct monst *mtmp; +{ + register struct trap *trap = t_at(mtmp->mx, mtmp->my); + boolean trapkilled = FALSE; + struct permonst *mptr = mtmp->data; + struct obj *otmp; + + if (!trap) { + mtmp->mtrapped = 0; /* perhaps teleported? */ + } else if (mtmp->mtrapped) { /* is currently in the trap */ + if (!trap->tseen && + cansee(mtmp->mx, mtmp->my) && canseemon(mtmp) && + (trap->ttyp == SPIKED_PIT || trap->ttyp == BEAR_TRAP || + trap->ttyp == HOLE || trap->ttyp == PIT || + trap->ttyp == WEB)) { + /* If you come upon an obviously trapped monster, then + * you must be able to see the trap it's in too. + */ + seetrap(trap); + } + + if (!rn2(40)) { + if (sobj_at(BOULDER, mtmp->mx, mtmp->my) && + (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)) { + if (!rn2(2)) { + mtmp->mtrapped = 0; + if (canseemon(mtmp)) + pline("%s pulls free...", Monnam(mtmp)); + fill_pit(mtmp->mx, mtmp->my); + } + } else { + mtmp->mtrapped = 0; + } + } else if (metallivorous(mptr)) { + if (trap->ttyp == BEAR_TRAP) { + if (canseemon(mtmp)) + pline("%s eats a bear trap!", Monnam(mtmp)); + deltrap(trap); + mtmp->meating = 5; + mtmp->mtrapped = 0; + } else if (trap->ttyp == SPIKED_PIT) { + if (canseemon(mtmp)) + pline("%s munches on some spikes!", Monnam(mtmp)); + trap->ttyp = PIT; + mtmp->meating = 5; + } + } + } else { + register int tt = trap->ttyp; + boolean in_sight, tear_web, see_it, + inescapable = ((tt == HOLE || tt == PIT) && + In_sokoban(&u.uz) && !trap->madeby_u); + const char *fallverb; + +#ifdef STEED + /* true when called from dotrap, inescapable is not an option */ + if (mtmp == u.usteed) inescapable = TRUE; +#endif + if (!inescapable && + ((mtmp->mtrapseen & (1 << (tt-1))) != 0 || + (tt == HOLE && !mindless(mtmp->data)))) { + /* it has been in such a trap - perhaps it escapes */ + if(rn2(4)) return(0); + } else { + mtmp->mtrapseen |= (1 << (tt-1)); + } + /* Monster is aggravated by being trapped by you. + Recognizing who made the trap isn't completely + unreasonable; everybody has their own style. */ + if (trap->madeby_u && rnl(5)) setmangry(mtmp); + + in_sight = canseemon(mtmp); + see_it = cansee(mtmp->mx, mtmp->my); +#ifdef STEED + /* assume hero can tell what's going on for the steed */ + if (mtmp == u.usteed) in_sight = TRUE; +#endif + switch (tt) { + case ARROW_TRAP: + if (trap->once && trap->tseen && !rn2(15)) { + if (in_sight && see_it) + pline("%s triggers a trap but nothing happens.", + Monnam(mtmp)); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + break; + } + trap->once = 1; + otmp = mksobj(ARROW, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + otmp->opoisoned = 0; + if (in_sight) seetrap(trap); + if (thitm(8, mtmp, otmp, 0, FALSE)) trapkilled = TRUE; + break; + case DART_TRAP: + if (trap->once && trap->tseen && !rn2(15)) { + if (in_sight && see_it) + pline("%s triggers a trap but nothing happens.", + Monnam(mtmp)); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + break; + } + trap->once = 1; + otmp = mksobj(DART, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (!rn2(6)) otmp->opoisoned = 1; + if (in_sight) seetrap(trap); + if (thitm(7, mtmp, otmp, 0, FALSE)) trapkilled = TRUE; + break; + case ROCKTRAP: + if (trap->once && trap->tseen && !rn2(15)) { + if (in_sight && see_it) + pline("A trap door above %s opens, but nothing falls out!", + mon_nam(mtmp)); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + break; + } + trap->once = 1; + otmp = mksobj(ROCK, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (in_sight) seetrap(trap); + if (thitm(0, mtmp, otmp, d(2, 6), FALSE)) + trapkilled = TRUE; + break; + + case SQKY_BOARD: + if(is_flyer(mptr)) break; + /* stepped on a squeaky board */ + if (in_sight) { + pline("A board beneath %s squeaks loudly.", mon_nam(mtmp)); + seetrap(trap); + } else + You_hear("a distant squeak."); + /* wake up nearby monsters */ + wake_nearto(mtmp->mx, mtmp->my, 40); + break; + + case BEAR_TRAP: + if(mptr->msize > MZ_SMALL && + !amorphous(mptr) && !is_flyer(mptr) && + !is_whirly(mptr) && !unsolid(mptr)) { + mtmp->mtrapped = 1; + if(in_sight) { + pline("%s is caught in %s bear trap!", + Monnam(mtmp), a_your[trap->madeby_u]); + seetrap(trap); + } else { + if((mptr == &mons[PM_OWLBEAR] + || mptr == &mons[PM_BUGBEAR]) + && flags.soundok) + You_hear("the roaring of an angry bear!"); + } + } + break; + + case SLP_GAS_TRAP: + if (!resists_sleep(mtmp) && !breathless(mptr) && + !mtmp->msleeping && mtmp->mcanmove) { + mtmp->mcanmove = 0; + mtmp->mfrozen = rnd(25); + if (in_sight) { + pline("%s suddenly falls asleep!", + Monnam(mtmp)); + seetrap(trap); + } + } + break; + + case RUST_TRAP: + { + struct obj *target; + + if (in_sight) + seetrap(trap); + switch (rn2(5)) { + case 0: + if (in_sight) + pline("%s %s on the %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, HEAD)); + target = which_armor(mtmp, W_ARMH); + (void) rust_dmg(target, "helmet", 1, TRUE, mtmp); + break; + case 1: + if (in_sight) + pline("%s %s's left %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, ARM)); + target = which_armor(mtmp, W_ARMS); + if (rust_dmg(target, "shield", 1, TRUE, mtmp)) + break; + target = MON_WEP(mtmp); + if (target && bimanual(target)) + erode_obj(target, FALSE, TRUE); +glovecheck: target = which_armor(mtmp, W_ARMG); + (void) rust_dmg(target, "gauntlets", 1, TRUE, mtmp); + break; + case 2: + if (in_sight) + pline("%s %s's right %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, ARM)); + erode_obj(MON_WEP(mtmp), FALSE, TRUE); + goto glovecheck; + default: + if (in_sight) + pline("%s %s!", A_gush_of_water_hits, + mon_nam(mtmp)); + for (otmp=mtmp->minvent; otmp; otmp = otmp->nobj) + (void) snuff_lit(otmp); + target = which_armor(mtmp, W_ARMC); + if (target) + (void) rust_dmg(target, cloak_simple_name(target), + 1, TRUE, mtmp); + else { + target = which_armor(mtmp, W_ARM); + if (target) + (void) rust_dmg(target, "armor", 1, TRUE, mtmp); +#ifdef TOURIST + else { + target = which_armor(mtmp, W_ARMU); + (void) rust_dmg(target, "shirt", 1, TRUE, mtmp); + } +#endif + } + } + if (mptr == &mons[PM_IRON_GOLEM]) { + if (in_sight) + pline("%s falls to pieces!", Monnam(mtmp)); + else if(mtmp->mtame) + pline("May %s rust in peace.", + mon_nam(mtmp)); + mondied(mtmp); + if (mtmp->mhp <= 0) + trapkilled = TRUE; + } else if (mptr == &mons[PM_GREMLIN] && rn2(3)) { + (void)split_mon(mtmp, (struct monst *)0); + } + break; + } + case FIRE_TRAP: + mfiretrap: + if (in_sight) + pline("A %s erupts from the %s under %s!", + tower_of_flame, + surface(mtmp->mx,mtmp->my), mon_nam(mtmp)); + else if (see_it) /* evidently `mtmp' is invisible */ + You("see a %s erupt from the %s!", + tower_of_flame, surface(mtmp->mx,mtmp->my)); + + if (resists_fire(mtmp)) { + if (in_sight) { + shieldeff(mtmp->mx,mtmp->my); + pline("%s is uninjured.", Monnam(mtmp)); + } + } else { + int num = d(2,4), alt; + boolean immolate = FALSE; + + /* paper burns very fast, assume straw is tightly + * packed and burns a bit slower */ + switch (monsndx(mtmp->data)) { + case PM_PAPER_GOLEM: immolate = TRUE; + alt = mtmp->mhpmax; break; + case PM_STRAW_GOLEM: alt = mtmp->mhpmax / 2; break; + case PM_WOOD_GOLEM: alt = mtmp->mhpmax / 4; break; + case PM_LEATHER_GOLEM: alt = mtmp->mhpmax / 8; break; + default: alt = 0; break; + } + if (alt > num) num = alt; + + if (thitm(0, mtmp, (struct obj *)0, num, immolate)) + trapkilled = TRUE; + else + /* we know mhp is at least `num' below mhpmax, + so no (mhp > mhpmax) check is needed here */ + mtmp->mhpmax -= rn2(num + 1); + } + if (burnarmor(mtmp) || rn2(3)) { + (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); + } + if (burn_floor_paper(mtmp->mx, mtmp->my, see_it, FALSE) && + !see_it && distu(mtmp->mx, mtmp->my) <= 3*3) + You("smell smoke."); + if (is_ice(mtmp->mx,mtmp->my)) + melt_ice(mtmp->mx,mtmp->my); + if (see_it) seetrap(trap); + break; + + case PIT: + case SPIKED_PIT: + fallverb = "falls"; + if (is_flyer(mptr) || is_floater(mptr) || + (mtmp->wormno && count_wsegs(mtmp) > 5) || + is_clinger(mptr)) { + if (!inescapable) break; /* avoids trap */ + fallverb = "is dragged"; /* sokoban pit */ + } + if (!passes_walls(mptr)) + mtmp->mtrapped = 1; + if (in_sight) { + pline("%s %s into %s pit!", + Monnam(mtmp), fallverb, + a_your[trap->madeby_u]); + if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND]) + pline("How pitiful. Isn't that the pits?"); + seetrap(trap); + } + mselftouch(mtmp, "Falling, ", FALSE); + if (mtmp->mhp <= 0 || + thitm(0, mtmp, (struct obj *)0, + rnd((tt == PIT) ? 6 : 10), FALSE)) + trapkilled = TRUE; + break; + case HOLE: + case TRAPDOOR: + if (!Can_fall_thru(&u.uz)) { + impossible("mintrap: %ss cannot exist on this level.", + defsyms[trap_to_defsym(tt)].explanation); + break; /* don't activate it after all */ + } + if (is_flyer(mptr) || is_floater(mptr) || + mptr == &mons[PM_WUMPUS] || + (mtmp->wormno && count_wsegs(mtmp) > 5) || + mptr->msize >= MZ_HUGE) { + if (inescapable) { /* sokoban hole */ + if (in_sight) { + pline("%s seems to be yanked down!", + Monnam(mtmp)); + /* suppress message in mlevel_tele_trap() */ + in_sight = FALSE; + seetrap(trap); + } + } else + break; + } + /* Fall through */ + case LEVEL_TELEP: + case MAGIC_PORTAL: + { + int mlev_res; + mlev_res = mlevel_tele_trap(mtmp, trap, + inescapable, in_sight); + if (mlev_res) return(mlev_res); + } + break; + + case TELEP_TRAP: + mtele_trap(mtmp, trap, in_sight); + break; + + case WEB: + /* Monster in a web. */ + if (webmaker(mptr)) break; + if (amorphous(mptr) || is_whirly(mptr) || unsolid(mptr)){ + if(acidic(mptr) || + mptr == &mons[PM_GELATINOUS_CUBE] || + mptr == &mons[PM_FIRE_ELEMENTAL]) { + if (in_sight) + pline("%s %s %s spider web!", + Monnam(mtmp), + (mptr == &mons[PM_FIRE_ELEMENTAL]) ? + "burns" : "dissolves", + a_your[trap->madeby_u]); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + break; + } + if (in_sight) { + pline("%s flows through %s spider web.", + Monnam(mtmp), + a_your[trap->madeby_u]); + seetrap(trap); + } + break; + } + tear_web = FALSE; + switch (monsndx(mptr)) { + case PM_OWLBEAR: /* Eric Backus */ + case PM_BUGBEAR: + if (!in_sight) { + You_hear("the roaring of a confused bear!"); + mtmp->mtrapped = 1; + break; + } + /* fall though */ + default: + if (mptr->mlet == S_GIANT || + (mptr->mlet == S_DRAGON && + extra_nasty(mptr)) || /* excl. babies */ + (mtmp->wormno && count_wsegs(mtmp) > 5)) { + tear_web = TRUE; + } else if (in_sight) { + pline("%s is caught in %s spider web.", + Monnam(mtmp), + a_your[trap->madeby_u]); + seetrap(trap); + } + mtmp->mtrapped = tear_web ? 0 : 1; + break; + /* this list is fairly arbitrary; it deliberately + excludes wumpus & giant/ettin zombies/mummies */ + case PM_TITANOTHERE: + case PM_BALUCHITHERIUM: + case PM_PURPLE_WORM: + case PM_JABBERWOCK: + case PM_IRON_GOLEM: + case PM_BALROG: + case PM_KRAKEN: + case PM_MASTODON: + tear_web = TRUE; + break; + } + if (tear_web) { + if (in_sight) + pline("%s tears through %s spider web!", + Monnam(mtmp), a_your[trap->madeby_u]); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + } + break; + + case STATUE_TRAP: + break; + + case MAGIC_TRAP: + /* A magic trap. Monsters usually immune. */ + if (!rn2(21)) goto mfiretrap; + break; + case ANTI_MAGIC: + break; + + case LANDMINE: + if(rn2(3)) + break; /* monsters usually don't set it off */ + if(is_flyer(mptr)) { + boolean already_seen = trap->tseen; + if (in_sight && !already_seen) { + pline("A trigger appears in a pile of soil below %s.", mon_nam(mtmp)); + seetrap(trap); + } + if (rn2(3)) break; + if (in_sight) { + newsym(mtmp->mx, mtmp->my); + pline_The("air currents set %s off!", + already_seen ? "a land mine" : "it"); + } + } else if(in_sight) { + newsym(mtmp->mx, mtmp->my); + pline("KAABLAMM!!! %s triggers %s land mine!", + Monnam(mtmp), a_your[trap->madeby_u]); + } + if (!in_sight) + pline("Kaablamm! You hear an explosion in the distance!"); + blow_up_landmine(trap); + if (thitm(0, mtmp, (struct obj *)0, rnd(16), FALSE)) + trapkilled = TRUE; + else { + /* monsters recursively fall into new pit */ + if (mintrap(mtmp) == 2) trapkilled=TRUE; + } + /* a boulder may fill the new pit, crushing monster */ + fill_pit(trap->tx, trap->ty); + if (mtmp->mhp <= 0) trapkilled = TRUE; + if (unconscious()) { + multi = -1; + nomovemsg="The explosion awakens you!"; + } + break; + + case POLY_TRAP: + if (resists_magm(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { + (void) newcham(mtmp, (struct permonst *)0, + FALSE, FALSE); + if (in_sight) seetrap(trap); + } + break; + + case ROLLING_BOULDER_TRAP: + if (!is_flyer(mptr)) { + int style = ROLL | (in_sight ? 0 : LAUNCH_UNSEEN); + + newsym(mtmp->mx,mtmp->my); + if (in_sight) + pline("Click! %s triggers %s.", Monnam(mtmp), + trap->tseen ? + "a rolling boulder trap" : + something); + if (launch_obj(BOULDER, trap->launch.x, trap->launch.y, + trap->launch2.x, trap->launch2.y, style)) { + if (in_sight) trap->tseen = TRUE; + if (mtmp->mhp <= 0) trapkilled = TRUE; + } else { + deltrap(trap); + newsym(mtmp->mx,mtmp->my); + } + } + break; + + default: + impossible("Some monster encountered a strange trap of type %d.", tt); + } + } + if(trapkilled) return 2; + return mtmp->mtrapped; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* Combine cockatrice checks into single functions to avoid repeating code. */ +void +instapetrify(str) +const char *str; +{ + if (Stone_resistance) return; + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + return; + You("turn to stone..."); + killer_format = KILLED_BY; + killer = str; + done(STONING); +} + +void +minstapetrify(mon,byplayer) +struct monst *mon; +boolean byplayer; +{ + if (resists_ston(mon)) return; + if (poly_when_stoned(mon->data)) { + mon_to_stone(mon); + return; + } + + /* give a " is slowing down" message and also remove + intrinsic speed (comparable to similar effect on the hero) */ + mon_adjust_speed(mon, -3, (struct obj *)0); + + if (cansee(mon->mx, mon->my)) + pline("%s turns to stone.", Monnam(mon)); + if (byplayer) { + stoned = TRUE; + xkilled(mon,0); + } else monstone(mon); +} + +void +selftouch(arg) +const char *arg; +{ + char kbuf[BUFSZ]; + + if(uwep && uwep->otyp == CORPSE && touch_petrifies(&mons[uwep->corpsenm]) + && !Stone_resistance) { + pline("%s touch the %s corpse.", arg, + mons[uwep->corpsenm].mname); + Sprintf(kbuf, "%s corpse", an(mons[uwep->corpsenm].mname)); + instapetrify(kbuf); + } + /* Or your secondary weapon, if wielded */ + if(u.twoweap && uswapwep && uswapwep->otyp == CORPSE && + touch_petrifies(&mons[uswapwep->corpsenm]) && !Stone_resistance){ + pline("%s touch the %s corpse.", arg, + mons[uswapwep->corpsenm].mname); + Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname)); + instapetrify(kbuf); + } +} + +void +mselftouch(mon,arg,byplayer) +struct monst *mon; +const char *arg; +boolean byplayer; +{ + struct obj *mwep = MON_WEP(mon); + + if (mwep && mwep->otyp == CORPSE && touch_petrifies(&mons[mwep->corpsenm])) { + if (cansee(mon->mx, mon->my)) { + pline("%s%s touches the %s corpse.", + arg ? arg : "", arg ? mon_nam(mon) : Monnam(mon), + mons[mwep->corpsenm].mname); + } + minstapetrify(mon, byplayer); + } +} + +void +float_up() +{ + if(u.utrap) { + if(u.utraptype == TT_PIT) { + u.utrap = 0; + You("float up, out of the pit!"); + vision_full_recalc = 1; /* vision limits change */ + fill_pit(u.ux, u.uy); + } else if (u.utraptype == TT_INFLOOR) { + Your("body pulls upward, but your %s are still stuck.", + makeplural(body_part(LEG))); + } else { + You("float up, only your %s is still stuck.", + body_part(LEG)); + } + } + else if(Is_waterlevel(&u.uz)) + pline("It feels as though you've lost some weight."); + else if(u.uinwater) + spoteffects(TRUE); + else if(u.uswallow) + You(is_animal(u.ustuck->data) ? + "float away from the %s." : + "spiral up into %s.", + is_animal(u.ustuck->data) ? + surface(u.ux, u.uy) : + mon_nam(u.ustuck)); + else if (Hallucination) + pline("Up, up, and awaaaay! You're walking on air!"); + else if(Is_airlevel(&u.uz)) + You("gain control over your movements."); + else + You("start to float in the air!"); +#ifdef STEED + if (u.usteed && !is_floater(u.usteed->data) && + !is_flyer(u.usteed->data)) { + if (Lev_at_will) + pline("%s magically floats up!", Monnam(u.usteed)); + else { + You("cannot stay on %s.", mon_nam(u.usteed)); + dismount_steed(DISMOUNT_GENERIC); + } + } +#endif + return; +} + +void +fill_pit(x, y) +int x, y; +{ + struct obj *otmp; + struct trap *t; + + if ((t = t_at(x, y)) && + ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)) && + (otmp = sobj_at(BOULDER, x, y))) { + obj_extract_self(otmp); + (void) flooreffects(otmp, x, y, "settle"); + } +} + +int +float_down(hmask, emask) +long hmask, emask; /* might cancel timeout */ +{ + register struct trap *trap = (struct trap *)0; + d_level current_dungeon_level; + boolean no_msg = FALSE; + + HLevitation &= ~hmask; + ELevitation &= ~emask; + if(Levitation) return(0); /* maybe another ring/potion/boots */ + if(u.uswallow) { + You("float down, but you are still %s.", + is_animal(u.ustuck->data) ? "swallowed" : "engulfed"); + return(1); + } + + if (Punished && !carried(uball) && + (is_pool(uball->ox, uball->oy) || + ((trap = t_at(uball->ox, uball->oy)) && + ((trap->ttyp == PIT) || (trap->ttyp == SPIKED_PIT) || + (trap->ttyp == TRAPDOOR) || (trap->ttyp == HOLE))))) { + u.ux0 = u.ux; + u.uy0 = u.uy; + u.ux = uball->ox; + u.uy = uball->oy; + movobj(uchain, uball->ox, uball->oy); + newsym(u.ux0, u.uy0); + vision_full_recalc = 1; /* in case the hero moved. */ + } + /* check for falling into pool - added by GAN 10/20/86 */ + if(!Flying) { + if (!u.uswallow && u.ustuck) { + if (sticks(youmonst.data)) + You("aren't able to maintain your hold on %s.", + mon_nam(u.ustuck)); + else + pline("Startled, %s can no longer hold you!", + mon_nam(u.ustuck)); + u.ustuck = 0; + } + /* kludge alert: + * drown() and lava_effects() print various messages almost + * every time they're called which conflict with the "fall + * into" message below. Thus, we want to avoid printing + * confusing, duplicate or out-of-order messages. + * Use knowledge of the two routines as a hack -- this + * should really be handled differently -dlc + */ + if(is_pool(u.ux,u.uy) && !Wwalking && !Swimming && !u.uinwater) + no_msg = drown(); + + if(is_lava(u.ux,u.uy)) { + (void) lava_effects(); + no_msg = TRUE; + } + } + if (!trap) { + trap = t_at(u.ux,u.uy); + if(Is_airlevel(&u.uz)) + You("begin to tumble in place."); + else if (Is_waterlevel(&u.uz) && !no_msg) + You_feel("heavier."); + /* u.uinwater msgs already in spoteffects()/drown() */ + else if (!u.uinwater && !no_msg) { +#ifdef STEED + if (!(emask & W_SADDLE)) +#endif + { + boolean sokoban_trap = (In_sokoban(&u.uz) && trap); + if (Hallucination) + pline("Bummer! You've %s.", + is_pool(u.ux,u.uy) ? + "splashed down" : sokoban_trap ? "crashed" : + "hit the ground"); + else { + if (!sokoban_trap) + You("float gently to the %s.", + surface(u.ux, u.uy)); + else { + /* Justification elsewhere for Sokoban traps + * is based on air currents. This is + * consistent with that. + * The unexpected additional force of the + * air currents once leviation + * ceases knocks you off your feet. + */ + You("fall over."); + losehp(rnd(2), "dangerous winds", KILLED_BY); +#ifdef STEED + if (u.usteed) dismount_steed(DISMOUNT_FELL); +#endif + selftouch("As you fall, you"); + } + } + } + } + } + + /* can't rely on u.uz0 for detecting trap door-induced level change; + it gets changed to reflect the new level before we can check it */ + assign_level(¤t_dungeon_level, &u.uz); + + if(trap) + switch(trap->ttyp) { + case STATUE_TRAP: + break; + case HOLE: + case TRAPDOOR: + if(!Can_fall_thru(&u.uz) || u.ustuck) + break; + /* fall into next case */ + default: + if (!u.utrap) /* not already in the trap */ + dotrap(trap, 0); + } + + if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !u.uswallow && + /* falling through trap door calls goto_level, + and goto_level does its own pickup() call */ + on_level(&u.uz, ¤t_dungeon_level)) + (void) pickup(1); + return 1; +} + +STATIC_OVL void +dofiretrap(box) +struct obj *box; /* null for floor trap */ +{ + boolean see_it = !Blind; + int num, alt; + +/* Bug: for box case, the equivalent of burn_floor_paper() ought + * to be done upon its contents. + */ + + if ((box && !carried(box)) ? is_pool(box->ox, box->oy) : Underwater) { + pline("A cascade of steamy bubbles erupts from %s!", + the(box ? xname(box) : surface(u.ux,u.uy))); + if (Fire_resistance) You("are uninjured."); + else losehp(rnd(3), "boiling water", KILLED_BY); + return; + } + pline("A %s %s from %s!", tower_of_flame, + box ? "bursts" : "erupts", + the(box ? xname(box) : surface(u.ux,u.uy))); + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + num = rn2(2); + } else if (Upolyd) { + num = d(2,4); + switch (u.umonnum) { + case PM_PAPER_GOLEM: alt = u.mhmax; break; + case PM_STRAW_GOLEM: alt = u.mhmax / 2; break; + case PM_WOOD_GOLEM: alt = u.mhmax / 4; break; + case PM_LEATHER_GOLEM: alt = u.mhmax / 8; break; + default: alt = 0; break; + } + if (alt > num) num = alt; + if (u.mhmax > mons[u.umonnum].mlevel) + u.mhmax -= rn2(min(u.mhmax,num + 1)), flags.botl = 1; + } else { + num = d(2,4); + if (u.uhpmax > u.ulevel) + u.uhpmax -= rn2(min(u.uhpmax,num + 1)), flags.botl = 1; + } + if (!num) + You("are uninjured."); + else + losehp(num, tower_of_flame, KILLED_BY_AN); + burn_away_slime(); + + if (burnarmor(&youmonst) || rn2(3)) { + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + } + if (!box && burn_floor_paper(u.ux, u.uy, see_it, TRUE) && !see_it) + You("smell paper burning."); + if (is_ice(u.ux, u.uy)) + melt_ice(u.ux, u.uy); +} + +STATIC_OVL void +domagictrap() +{ + register int fate = rnd(20); + + /* What happened to the poor sucker? */ + + if (fate < 10) { + /* Most of the time, it creates some monsters. */ + register int cnt = rnd(4); + + if (!resists_blnd(&youmonst)) { + You("are momentarily blinded by a flash of light!"); + make_blinded((long)rn1(5,10),FALSE); + if (!Blind) Your(vision_clears); + } else if (!Blind) { + You("see a flash of light!"); + } else + You_hear("a deafening roar!"); + while(cnt--) + (void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS); + } + else + switch (fate) { + + case 10: + case 11: + /* sometimes nothing happens */ + break; + case 12: /* a flash of fire */ + dofiretrap((struct obj *)0); + break; + + /* odd feelings */ + case 13: pline("A shiver runs up and down your %s!", + body_part(SPINE)); + break; + case 14: You_hear(Hallucination ? + "the moon howling at you." : + "distant howling."); + break; + case 15: if (on_level(&u.uz, &qstart_level)) + You_feel("%slike the prodigal son.", + (flags.female || (Upolyd && is_neuter(youmonst.data))) ? + "oddly " : ""); + else + You("suddenly yearn for %s.", + Hallucination ? "Cleveland" : + (In_quest(&u.uz) || at_dgn_entrance("The Quest")) ? + "your nearby homeland" : + "your distant homeland"); + break; + case 16: Your("pack shakes violently!"); + break; + case 17: You(Hallucination ? + "smell hamburgers." : + "smell charred flesh."); + break; + case 18: You_feel("tired."); + break; + + /* very occasionally something nice happens. */ + + case 19: + /* tame nearby monsters */ + { register int i,j; + register struct monst *mtmp; + + (void) adjattrib(A_CHA,1,FALSE); + for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) { + if(!isok(u.ux+i, u.uy+j)) continue; + mtmp = m_at(u.ux+i, u.uy+j); + if(mtmp) + (void) tamedog(mtmp, (struct obj *)0); + } + break; + } + + case 20: + /* uncurse stuff */ + { struct obj pseudo; + long save_conf = HConfusion; + + pseudo = zeroobj; /* neither cursed nor blessed */ + pseudo.otyp = SCR_REMOVE_CURSE; + HConfusion = 0L; + (void) seffects(&pseudo); + HConfusion = save_conf; + break; + } + default: break; + } +} + +/* + * Scrolls, spellbooks, potions, and flammable items + * may get affected by the fire. + * + * Return number of objects destroyed. --ALI + */ +int +fire_damage(chain, force, here, x, y) +struct obj *chain; +boolean force, here; +xchar x, y; +{ + int chance; + struct obj *obj, *otmp, *nobj, *ncobj; + int retval = 0; + int in_sight = !Blind && couldsee(x, y); /* Don't care if it's lit */ + int dindx; + + for (obj = chain; obj; obj = nobj) { + nobj = here ? obj->nexthere : obj->nobj; + + /* object might light in a controlled manner */ + if (catch_lit(obj)) + continue; + + if (Is_container(obj)) { + switch (obj->otyp) { + case ICE_BOX: + continue; /* Immune */ + /*NOTREACHED*/ + break; + case CHEST: + chance = 40; + break; + case LARGE_BOX: + chance = 30; + break; + default: + chance = 20; + break; + } + if (!force && (Luck + 5) > rn2(chance)) + continue; + /* Container is burnt up - dump contents out */ + if (in_sight) pline("%s catches fire and burns.", Yname2(obj)); + if (Has_contents(obj)) { + if (in_sight) pline("Its contents fall out."); + for (otmp = obj->cobj; otmp; otmp = ncobj) { + ncobj = otmp->nobj; + obj_extract_self(otmp); + if (!flooreffects(otmp, x, y, "")) + place_object(otmp, x, y); + } + } + delobj(obj); + retval++; + } else if (!force && (Luck + 5) > rn2(20)) { + /* chance per item of sustaining damage: + * max luck (full moon): 5% + * max luck (elsewhen): 10% + * avg luck (Luck==0): 75% + * awful luck (Luck<-4): 100% + */ + continue; + } else if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) { + if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) + continue; + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + if (in_sight) pline("Smoke rises from %s.", the(xname(obj))); + continue; + } + dindx = (obj->oclass == SCROLL_CLASS) ? 2 : 3; + if (in_sight) + pline("%s %s.", Yname2(obj), (obj->quan > 1) ? + destroy_strings[dindx*3 + 1] : destroy_strings[dindx*3]); + delobj(obj); + retval++; + } else if (obj->oclass == POTION_CLASS) { + dindx = 1; + if (in_sight) + pline("%s %s.", Yname2(obj), (obj->quan > 1) ? + destroy_strings[dindx*3 + 1] : destroy_strings[dindx*3]); + delobj(obj); + retval++; + } else if (is_flammable(obj) && obj->oeroded < MAX_ERODE && + !(obj->oerodeproof || (obj->blessed && !rnl(4)))) { + if (in_sight) { + pline("%s %s%s.", Yname2(obj), otense(obj, "burn"), + obj->oeroded+1 == MAX_ERODE ? " completely" : + obj->oeroded ? " further" : ""); + } + obj->oeroded++; + } + } + + if (retval && !in_sight) + You("smell smoke."); + return retval; +} + +void +water_damage(obj, force, here) +register struct obj *obj; +register boolean force, here; +{ + struct obj *otmp; + + /* Scrolls, spellbooks, potions, weapons and + pieces of armor may get affected by the water */ + for (; obj; obj = otmp) { + otmp = here ? obj->nexthere : obj->nobj; + + (void) snuff_lit(obj); + + if(obj->otyp == CAN_OF_GREASE && obj->spe > 0) { + continue; + } else if(obj->greased) { + if (force || !rn2(2)) obj->greased = 0; + } else if(Is_container(obj) && !Is_box(obj) && + (obj->otyp != OILSKIN_SACK || (obj->cursed && !rn2(3)))) { + water_damage(obj->cobj, force, FALSE); + } else if (!force && (Luck + 5) > rn2(20)) { + /* chance per item of sustaining damage: + * max luck (full moon): 5% + * max luck (elsewhen): 10% + * avg luck (Luck==0): 75% + * awful luck (Luck<-4): 100% + */ + continue; + } else if (obj->oclass == SCROLL_CLASS) { +#ifdef MAIL + if (obj->otyp != SCR_MAIL) +#endif + { + obj->otyp = SCR_BLANK_PAPER; + obj->spe = 0; + } + } else if (obj->oclass == SPBOOK_CLASS) { + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) + pline("Steam rises from %s.", the(xname(obj))); + else obj->otyp = SPE_BLANK_PAPER; + } else if (obj->oclass == POTION_CLASS) { + if (obj->otyp == POT_ACID) { + /* damage player/monster? */ + pline("A potion explodes!"); + delobj(obj); + continue; + } else if (obj->odiluted) { + obj->otyp = POT_WATER; + obj->blessed = obj->cursed = 0; + obj->odiluted = 0; + } else if (obj->otyp != POT_WATER) + obj->odiluted++; + } else if (is_rustprone(obj) && obj->oeroded < MAX_ERODE && + !(obj->oerodeproof || (obj->blessed && !rnl(4)))) { + /* all metal stuff and armor except (body armor + protected by oilskin cloak) */ + if(obj->oclass != ARMOR_CLASS || obj != uarm || + !uarmc || uarmc->otyp != OILSKIN_CLOAK || + (uarmc->cursed && !rn2(3))) + obj->oeroded++; + } + } +} + +/* + * This function is potentially expensive - rolling + * inventory list multiple times. Luckily it's seldom needed. + * Returns TRUE if disrobing made player unencumbered enough to + * crawl out of the current predicament. + */ +STATIC_OVL boolean +emergency_disrobe(lostsome) +boolean *lostsome; +{ + int invc = inv_cnt(); + + while (near_capacity() > (Punished ? UNENCUMBERED : SLT_ENCUMBER)) { + register struct obj *obj, *otmp = (struct obj *)0; + register int i; + + /* Pick a random object */ + if (invc > 0) { + i = rn2(invc); + for (obj = invent; obj; obj = obj->nobj) { + /* + * Undroppables are: body armor, boots, gloves, + * amulets, and rings because of the time and effort + * in removing them + loadstone and other cursed stuff + * for obvious reasons. + */ + if (!((obj->otyp == LOADSTONE && obj->cursed) || + obj == uamul || obj == uleft || obj == uright || + obj == ublindf || obj == uarm || obj == uarmc || + obj == uarmg || obj == uarmf || +#ifdef TOURIST + obj == uarmu || +#endif + (obj->cursed && (obj == uarmh || obj == uarms)) || + welded(obj))) + otmp = obj; + /* reached the mark and found some stuff to drop? */ + if (--i < 0 && otmp) break; + + /* else continue */ + } + } +#ifndef GOLDOBJ + if (!otmp) { + /* Nothing available left to drop; try gold */ + if (u.ugold) { + pline("In desperation, you drop your purse."); + /* Hack: gold is not in the inventory, so make a gold object + * and put it at the head of the inventory list. + */ + obj = mkgoldobj(u.ugold); /* removes from u.ugold */ + obj->in_use = TRUE; + u.ugold = obj->quan; /* put the gold back */ + assigninvlet(obj); /* might end up as NOINVSYM */ + obj->nobj = invent; + invent = obj; + *lostsome = TRUE; + dropx(obj); + continue; /* Try again */ + } + /* We can't even drop gold! */ + return (FALSE); + } +#else + if (!otmp) return (FALSE); /* nothing to drop! */ +#endif + if (otmp->owornmask) remove_worn_item(otmp, FALSE); + *lostsome = TRUE; + dropx(otmp); + invc--; + } + return(TRUE); +} + +/* + * return(TRUE) == player relocated + */ +boolean +drown() +{ + boolean inpool_ok = FALSE, crawl_ok; + int i, x, y; + + /* happily wading in the same contiguous pool */ + if (u.uinwater && is_pool(u.ux-u.dx,u.uy-u.dy) && + (Swimming || Amphibious)) { + /* water effects on objects every now and then */ + if (!rn2(5)) inpool_ok = TRUE; + else return(FALSE); + } + + if (!u.uinwater) { + You("%s into the water%c", + Is_waterlevel(&u.uz) ? "plunge" : "fall", + Amphibious || Swimming ? '.' : '!'); + if (!Swimming && !Is_waterlevel(&u.uz)) + You("sink like %s.", + Hallucination ? "the Titanic" : "a rock"); + } + + water_damage(invent, FALSE, FALSE); + + if (u.umonnum == PM_GREMLIN && rn2(3)) + (void)split_mon(&youmonst, (struct monst *)0); + else if (u.umonnum == PM_IRON_GOLEM) { + You("rust!"); + i = d(2,6); + if (u.mhmax > i) u.mhmax -= i; + losehp(i, "rusting away", KILLED_BY); + } + if (inpool_ok) return(FALSE); + + if ((i = number_leashed()) > 0) { + pline_The("leash%s slip%s loose.", + (i > 1) ? "es" : "", + (i > 1) ? "" : "s"); + unleash_all(); + } + + if (Amphibious || Swimming) { + if (Amphibious) { + if (flags.verbose) + pline("But you aren't drowning."); + if (!Is_waterlevel(&u.uz)) { + if (Hallucination) + Your("keel hits the bottom."); + else + You("touch bottom."); + } + } + if (Punished) { + unplacebc(); + placebc(); + } + vision_recalc(2); /* unsee old position */ + u.uinwater = 1; + under_water(1); + vision_full_recalc = 1; + return(FALSE); + } + if ((Teleportation || can_teleport(youmonst.data)) && + !u.usleep && (Teleport_control || rn2(3) < Luck+2)) { + You("attempt a teleport spell."); /* utcsri!carroll */ + if (!level.flags.noteleport) { + (void) dotele(); + if(!is_pool(u.ux,u.uy)) + return(TRUE); + } else pline_The("attempted teleport spell fails."); + } +#ifdef STEED + if (u.usteed) { + dismount_steed(DISMOUNT_GENERIC); + if(!is_pool(u.ux,u.uy)) + return(TRUE); + } +#endif + crawl_ok = FALSE; + x = y = 0; /* lint suppression */ + /* if sleeping, wake up now so that we don't crawl out of water + while still asleep; we can't do that the same way that waking + due to combat is handled; note unmul() clears u.usleep */ + if (u.usleep) unmul("Suddenly you wake up!"); + /* can't crawl if unable to move (crawl_ok flag stays false) */ + if (multi < 0 || (Upolyd && !youmonst.data->mmove)) goto crawl; + /* look around for a place to crawl to */ + for (i = 0; i < 100; i++) { + x = rn1(3,u.ux - 1); + y = rn1(3,u.uy - 1); + if (goodpos(x, y, &youmonst, 0)) { + crawl_ok = TRUE; + goto crawl; + } + } + /* one more scan */ + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) + if (goodpos(x, y, &youmonst, 0)) { + crawl_ok = TRUE; + goto crawl; + } + crawl: + if (crawl_ok) { + boolean lost = FALSE; + /* time to do some strip-tease... */ + boolean succ = Is_waterlevel(&u.uz) ? TRUE : + emergency_disrobe(&lost); + + You("try to crawl out of the water."); + if (lost) + You("dump some of your gear to lose weight..."); + if (succ) { + pline("Pheew! That was close."); + teleds(x,y,TRUE); + return(TRUE); + } + /* still too much weight */ + pline("But in vain."); + } + u.uinwater = 1; + You("drown."); + killer_format = KILLED_BY_AN; + killer = (levl[u.ux][u.uy].typ == POOL || Is_medusa_level(&u.uz)) ? + "pool of water" : "moat"; + done(DROWNING); + /* oops, we're still alive. better get out of the water. */ + while (!safe_teleds(TRUE)) { + pline("You're still drowning."); + done(DROWNING); + } + if (u.uinwater) { + u.uinwater = 0; + You("find yourself back %s.", Is_waterlevel(&u.uz) ? + "in an air bubble" : "on land"); + } + return(TRUE); +} + +void +drain_en(n) +register int n; +{ + if (!u.uenmax) return; + You_feel("your magical energy drain away!"); + u.uen -= n; + if(u.uen < 0) { + u.uenmax += u.uen; + if(u.uenmax < 0) u.uenmax = 0; + u.uen = 0; + } + flags.botl = 1; +} + +int +dountrap() /* disarm a trap */ +{ + if (near_capacity() >= HVY_ENCUMBER) { + pline("You're too strained to do that."); + return 0; + } + if ((nohands(youmonst.data) && !webmaker(youmonst.data)) || !youmonst.data->mmove) { + pline("And just how do you expect to do that?"); + return 0; + } else if (u.ustuck && sticks(youmonst.data)) { + pline("You'll have to let go of %s first.", mon_nam(u.ustuck)); + return 0; + } + if (u.ustuck || (welded(uwep) && bimanual(uwep))) { + Your("%s seem to be too busy for that.", + makeplural(body_part(HAND))); + return 0; + } + return untrap(FALSE); +} +#endif /* OVLB */ +#ifdef OVL2 + +/* Probability of disabling a trap. Helge Hafting */ +STATIC_OVL int +untrap_prob(ttmp) +struct trap *ttmp; +{ + int chance = 3; + + /* Only spiders know how to deal with webs reliably */ + if (ttmp->ttyp == WEB && !webmaker(youmonst.data)) + chance = 30; + if (Confusion || Hallucination) chance++; + if (Blind) chance++; + if (Stunned) chance += 2; + if (Fumbling) chance *= 2; + /* Your own traps are better known than others. */ + if (ttmp && ttmp->madeby_u) chance--; + if (Role_if(PM_ROGUE)) { + if (rn2(2 * MAXULEV) < u.ulevel) chance--; + if (u.uhave.questart && chance > 1) chance--; + } else if (Role_if(PM_RANGER) && chance > 1) chance--; + return rn2(chance); +} + +/* Replace trap with object(s). Helge Hafting */ +STATIC_OVL void +cnv_trap_obj(otyp, cnt, ttmp) +int otyp; +int cnt; +struct trap *ttmp; +{ + struct obj *otmp = mksobj(otyp, TRUE, FALSE); + otmp->quan=cnt; + otmp->owt = weight(otmp); + /* Only dart traps are capable of being poisonous */ + if (otyp != DART) + otmp->opoisoned = 0; + place_object(otmp, ttmp->tx, ttmp->ty); + /* Sell your own traps only... */ + if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty); + stackobj(otmp); + newsym(ttmp->tx, ttmp->ty); + deltrap(ttmp); +} + +/* while attempting to disarm an adjacent trap, we've fallen into it */ +STATIC_OVL void +move_into_trap(ttmp) +struct trap *ttmp; +{ + int bc; + xchar x = ttmp->tx, y = ttmp->ty, bx, by, cx, cy; + boolean unused; + + /* we know there's no monster in the way, and we're not trapped */ + if (!Punished || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused, + TRUE)) { + u.ux0 = u.ux, u.uy0 = u.uy; + u.ux = x, u.uy = y; + u.umoved = TRUE; + newsym(u.ux0, u.uy0); + vision_recalc(1); + check_leash(u.ux0, u.uy0); + if (Punished) move_bc(0, bc, bx, by, cx, cy); + spoteffects(FALSE); /* dotrap() */ + exercise(A_WIS, FALSE); + } +} + +/* 0: doesn't even try + * 1: tries and fails + * 2: succeeds + */ +STATIC_OVL int +try_disarm(ttmp, force_failure) +struct trap *ttmp; +boolean force_failure; +{ + struct monst *mtmp = m_at(ttmp->tx,ttmp->ty); + int ttype = ttmp->ttyp; + boolean under_u = (!u.dx && !u.dy); + boolean holdingtrap = (ttype == BEAR_TRAP || ttype == WEB); + + /* Test for monster first, monsters are displayed instead of trap. */ + if (mtmp && (!mtmp->mtrapped || !holdingtrap)) { + pline("%s is in the way.", Monnam(mtmp)); + return 0; + } + /* We might be forced to move onto the trap's location. */ + if (sobj_at(BOULDER, ttmp->tx, ttmp->ty) + && !Passes_walls && !under_u) { + There("is a boulder in your way."); + return 0; + } + /* duplicate tight-space checks from test_move */ + if (u.dx && u.dy && + bad_rock(youmonst.data,u.ux,ttmp->ty) && + bad_rock(youmonst.data,ttmp->tx,u.uy)) { + if ((invent && (inv_weight() + weight_cap() > 600)) || + bigmonst(youmonst.data)) { + /* don't allow untrap if they can't get thru to it */ + You("are unable to reach the %s!", + defsyms[trap_to_defsym(ttype)].explanation); + return 0; + } + } + /* untrappable traps are located on the ground. */ + if (!can_reach_floor()) { +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) + You("aren't skilled enough to reach from %s.", + mon_nam(u.usteed)); + else +#endif + You("are unable to reach the %s!", + defsyms[trap_to_defsym(ttype)].explanation); + return 0; + } + + /* Will our hero succeed? */ + if (force_failure || untrap_prob(ttmp)) { + if (rnl(5)) { + pline("Whoops..."); + if (mtmp) { /* must be a trap that holds monsters */ + if (ttype == BEAR_TRAP) { + if (mtmp->mtame) abuse_dog(mtmp); + if ((mtmp->mhp -= rnd(4)) <= 0) killed(mtmp); + } else if (ttype == WEB) { + if (!webmaker(youmonst.data)) { + struct trap *ttmp2 = maketrap(u.ux, u.uy, WEB); + if (ttmp2) { + pline_The("webbing sticks to you. You're caught too!"); + dotrap(ttmp2, NOWEBMSG); +#ifdef STEED + if (u.usteed && u.utrap) { + /* you, not steed, are trapped */ + dismount_steed(DISMOUNT_FELL); + } +#endif + } + } else + pline("%s remains entangled.", Monnam(mtmp)); + } + } else if (under_u) { + dotrap(ttmp, 0); + } else { + move_into_trap(ttmp); + } + } else { + pline("%s %s is difficult to %s.", + ttmp->madeby_u ? "Your" : under_u ? "This" : "That", + defsyms[trap_to_defsym(ttype)].explanation, + (ttype == WEB) ? "remove" : "disarm"); + } + return 1; + } + return 2; +} + +STATIC_OVL void +reward_untrap(ttmp, mtmp) +struct trap *ttmp; +struct monst *mtmp; +{ + if (!ttmp->madeby_u) { + if (rnl(10) < 8 && !mtmp->mpeaceful && + !mtmp->msleeping && !mtmp->mfrozen && + !mindless(mtmp->data) && + mtmp->data->mlet != S_HUMAN) { + mtmp->mpeaceful = 1; + set_malign(mtmp); /* reset alignment */ + pline("%s is grateful.", Monnam(mtmp)); + } + /* Helping someone out of a trap is a nice thing to do, + * A lawful may be rewarded, but not too often. */ + if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) { + adjalign(1); + You_feel("that you did the right thing."); + } + } +} + +STATIC_OVL int +disarm_holdingtrap(ttmp) /* Helge Hafting */ +struct trap *ttmp; +{ + struct monst *mtmp; + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + + /* ok, disarm it. */ + + /* untrap the monster, if any. + There's no need for a cockatrice test, only the trap is touched */ + if ((mtmp = m_at(ttmp->tx,ttmp->ty)) != 0) { + mtmp->mtrapped = 0; + You("remove %s %s from %s.", the_your[ttmp->madeby_u], + (ttmp->ttyp == BEAR_TRAP) ? "bear trap" : "webbing", + mon_nam(mtmp)); + reward_untrap(ttmp, mtmp); + } else { + if (ttmp->ttyp == BEAR_TRAP) { + You("disarm %s bear trap.", the_your[ttmp->madeby_u]); + cnv_trap_obj(BEARTRAP, 1, ttmp); + } else /* if (ttmp->ttyp == WEB) */ { + You("succeed in removing %s web.", the_your[ttmp->madeby_u]); + deltrap(ttmp); + } + } + newsym(u.ux + u.dx, u.uy + u.dy); + return 1; +} + +STATIC_OVL int +disarm_landmine(ttmp) /* Helge Hafting */ +struct trap *ttmp; +{ + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + You("disarm %s land mine.", the_your[ttmp->madeby_u]); + cnv_trap_obj(LAND_MINE, 1, ttmp); + return 1; +} + +/* getobj will filter down to cans of grease and known potions of oil */ +static NEARDATA const char oil[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS, 0 }; + +/* it may not make much sense to use grease on floor boards, but so what? */ +STATIC_OVL int +disarm_squeaky_board(ttmp) +struct trap *ttmp; +{ + struct obj *obj; + boolean bad_tool; + int fails; + + obj = getobj(oil, "untrap with"); + if (!obj) return 0; + + bad_tool = (obj->cursed || + ((obj->otyp != POT_OIL || obj->lamplit) && + (obj->otyp != CAN_OF_GREASE || !obj->spe))); + + fails = try_disarm(ttmp, bad_tool); + if (fails < 2) return fails; + + /* successfully used oil or grease to fix squeaky board */ + if (obj->otyp == CAN_OF_GREASE) { + consume_obj_charge(obj, TRUE); + } else { + useup(obj); /* oil */ + makeknown(POT_OIL); + } + You("repair the squeaky board."); /* no madeby_u */ + deltrap(ttmp); + newsym(u.ux + u.dx, u.uy + u.dy); + more_experienced(1, 5); + newexplevel(); + return 1; +} + +/* removes traps that shoot arrows, darts, etc. */ +STATIC_OVL int +disarm_shooting_trap(ttmp, otyp) +struct trap *ttmp; +int otyp; +{ + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + You("disarm %s trap.", the_your[ttmp->madeby_u]); + cnv_trap_obj(otyp, 50-rnl(50), ttmp); + return 1; +} + +/* Is the weight too heavy? + * Formula as in near_capacity() & check_capacity() */ +STATIC_OVL int +try_lift(mtmp, ttmp, wt, stuff) +struct monst *mtmp; +struct trap *ttmp; +int wt; +boolean stuff; +{ + int wc = weight_cap(); + + if (((wt * 2) / wc) >= HVY_ENCUMBER) { + pline("%s is %s for you to lift.", Monnam(mtmp), + stuff ? "carrying too much" : "too heavy"); + if (!ttmp->madeby_u && !mtmp->mpeaceful && mtmp->mcanmove && + !mindless(mtmp->data) && + mtmp->data->mlet != S_HUMAN && rnl(10) < 3) { + mtmp->mpeaceful = 1; + set_malign(mtmp); /* reset alignment */ + pline("%s thinks it was nice of you to try.", Monnam(mtmp)); + } + return 0; + } + return 1; +} + +/* Help trapped monster (out of a (spiked) pit) */ +STATIC_OVL int +help_monster_out(mtmp, ttmp) +struct monst *mtmp; +struct trap *ttmp; +{ + int wt; + struct obj *otmp; + boolean uprob; + + /* + * This works when levitating too -- consistent with the ability + * to hit monsters while levitating. + * + * Should perhaps check that our hero has arms/hands at the + * moment. Helping can also be done by engulfing... + * + * Test the monster first - monsters are displayed before traps. + */ + if (!mtmp->mtrapped) { + pline("%s isn't trapped.", Monnam(mtmp)); + return 0; + } + /* Do you have the necessary capacity to lift anything? */ + if (check_capacity((char *)0)) return 1; + + /* Will our hero succeed? */ + if ((uprob = untrap_prob(ttmp)) && !mtmp->msleeping && mtmp->mcanmove) { + You("try to reach out your %s, but %s backs away skeptically.", + makeplural(body_part(ARM)), + mon_nam(mtmp)); + return 1; + } + + + /* is it a cockatrice?... */ + if (touch_petrifies(mtmp->data) && !uarmg && !Stone_resistance) { + You("grab the trapped %s using your bare %s.", + mtmp->data->mname, makeplural(body_part(HAND))); + + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + display_nhwindow(WIN_MESSAGE, FALSE); + else { + char kbuf[BUFSZ]; + + Sprintf(kbuf, "trying to help %s out of a pit", + an(mtmp->data->mname)); + instapetrify(kbuf); + return 1; + } + } + /* need to do cockatrice check first if sleeping or paralyzed */ + if (uprob) { + You("try to grab %s, but cannot get a firm grasp.", + mon_nam(mtmp)); + if (mtmp->msleeping) { + mtmp->msleeping = 0; + pline("%s awakens.", Monnam(mtmp)); + } + return 1; + } + + You("reach out your %s and grab %s.", + makeplural(body_part(ARM)), mon_nam(mtmp)); + + if (mtmp->msleeping) { + mtmp->msleeping = 0; + pline("%s awakens.", Monnam(mtmp)); + } else if (mtmp->mfrozen && !rn2(mtmp->mfrozen)) { + /* After such manhandling, perhaps the effect wears off */ + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + pline("%s stirs.", Monnam(mtmp)); + } + + /* is the monster too heavy? */ + wt = inv_weight() + mtmp->data->cwt; + if (!try_lift(mtmp, ttmp, wt, FALSE)) return 1; + + /* is the monster with inventory too heavy? */ + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + wt += otmp->owt; + if (!try_lift(mtmp, ttmp, wt, TRUE)) return 1; + + You("pull %s out of the pit.", mon_nam(mtmp)); + mtmp->mtrapped = 0; + fill_pit(mtmp->mx, mtmp->my); + reward_untrap(ttmp, mtmp); + return 1; +} + +int +untrap(force) +boolean force; +{ + register struct obj *otmp; + register boolean confused = (Confusion > 0 || Hallucination > 0); + register int x,y; + int ch; + struct trap *ttmp; + struct monst *mtmp; + boolean trap_skipped = FALSE; + boolean box_here = FALSE; + boolean deal_with_floor_trap = FALSE; + char the_trap[BUFSZ], qbuf[QBUFSZ]; + int containercnt = 0; + + if(!getdir((char *)0)) return(0); + x = u.ux + u.dx; + y = u.uy + u.dy; + + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) { + if(Is_box(otmp) && !u.dx && !u.dy) { + box_here = TRUE; + containercnt++; + if (containercnt > 1) break; + } + } + + if ((ttmp = t_at(x,y)) && ttmp->tseen) { + deal_with_floor_trap = TRUE; + Strcpy(the_trap, the(defsyms[trap_to_defsym(ttmp->ttyp)].explanation)); + if (box_here) { + if (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT) { + You_cant("do much about %s%s.", + the_trap, u.utrap ? + " that you're stuck in" : + " while standing on the edge of it"); + trap_skipped = TRUE; + deal_with_floor_trap = FALSE; + } else { + Sprintf(qbuf, "There %s and %s here. %s %s?", + (containercnt == 1) ? "is a container" : "are containers", + an(defsyms[trap_to_defsym(ttmp->ttyp)].explanation), + ttmp->ttyp == WEB ? "Remove" : "Disarm", the_trap); + switch (ynq(qbuf)) { + case 'q': return(0); + case 'n': trap_skipped = TRUE; + deal_with_floor_trap = FALSE; + break; + } + } + } + if (deal_with_floor_trap) { + if (u.utrap) { + You("cannot deal with %s while trapped%s!", the_trap, + (x == u.ux && y == u.uy) ? " in it" : ""); + return 1; + } + switch(ttmp->ttyp) { + case BEAR_TRAP: + case WEB: + return disarm_holdingtrap(ttmp); + case LANDMINE: + return disarm_landmine(ttmp); + case SQKY_BOARD: + return disarm_squeaky_board(ttmp); + case DART_TRAP: + return disarm_shooting_trap(ttmp, DART); + case ARROW_TRAP: + return disarm_shooting_trap(ttmp, ARROW); + case PIT: + case SPIKED_PIT: + if (!u.dx && !u.dy) { + You("are already on the edge of the pit."); + return 0; + } + if (!(mtmp = m_at(x,y))) { + pline("Try filling the pit instead."); + return 0; + } + return help_monster_out(mtmp, ttmp); + default: + You("cannot disable %s trap.", (u.dx || u.dy) ? "that" : "this"); + return 0; + } + } + } /* end if */ + + if(!u.dx && !u.dy) { + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if(Is_box(otmp)) { + Sprintf(qbuf, "There is %s here. Check it for traps?", + safe_qbuf("", sizeof("There is here. Check it for traps?"), + doname(otmp), an(simple_typename(otmp->otyp)), "a box")); + switch (ynq(qbuf)) { + case 'q': return(0); + case 'n': continue; + } +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) { + You("aren't skilled enough to reach from %s.", + mon_nam(u.usteed)); + return(0); + } +#endif + if((otmp->otrapped && (force || (!confused + && rn2(MAXULEV + 1 - u.ulevel) < 10))) + || (!force && confused && !rn2(3))) { + You("find a trap on %s!", the(xname(otmp))); + if (!confused) exercise(A_WIS, TRUE); + + switch (ynq("Disarm it?")) { + case 'q': return(1); + case 'n': trap_skipped = TRUE; continue; + } + + if(otmp->otrapped) { + exercise(A_DEX, TRUE); + ch = ACURR(A_DEX) + u.ulevel; + if (Role_if(PM_ROGUE)) ch *= 2; + if(!force && (confused || Fumbling || + rnd(75+level_difficulty()/2) > ch)) { + (void) chest_trap(otmp, FINGER, TRUE); + } else { + You("disarm it!"); + otmp->otrapped = 0; + } + } else pline("That %s was not trapped.", xname(otmp)); + return(1); + } else { + You("find no traps on %s.", the(xname(otmp))); + return(1); + } + } + + You(trap_skipped ? "find no other traps here." + : "know of no traps here."); + return(0); + } + + if ((mtmp = m_at(x,y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + if (!IS_DOOR(levl[x][y].typ)) { + if ((ttmp = t_at(x,y)) && ttmp->tseen) + You("cannot disable that trap."); + else + You("know of no traps there."); + return(0); + } + + switch (levl[x][y].doormask) { + case D_NODOOR: + You("%s no door there.", Blind ? "feel" : "see"); + return(0); + case D_ISOPEN: + pline("This door is safely open."); + return(0); + case D_BROKEN: + pline("This door is broken."); + return(0); + } + + if ((levl[x][y].doormask & D_TRAPPED + && (force || + (!confused && rn2(MAXULEV - u.ulevel + 11) < 10))) + || (!force && confused && !rn2(3))) { + You("find a trap on the door!"); + exercise(A_WIS, TRUE); + if (ynq("Disarm it?") != 'y') return(1); + if (levl[x][y].doormask & D_TRAPPED) { + ch = 15 + (Role_if(PM_ROGUE) ? u.ulevel*3 : u.ulevel); + exercise(A_DEX, TRUE); + if(!force && (confused || Fumbling || + rnd(75+level_difficulty()/2) > ch)) { + You("set it off!"); + b_trapped("door", FINGER); + levl[x][y].doormask = D_NODOOR; + unblock_point(x, y); + newsym(x, y); + /* (probably ought to charge for this damage...) */ + if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); + } else { + You("disarm it!"); + levl[x][y].doormask &= ~D_TRAPPED; + } + } else pline("This door was not trapped."); + return(1); + } else { + You("find no traps on the door."); + return(1); + } +} +#endif /* OVL2 */ +#ifdef OVLB + +/* only called when the player is doing something to the chest directly */ +boolean +chest_trap(obj, bodypart, disarm) +register struct obj *obj; +register int bodypart; +boolean disarm; +{ + register struct obj *otmp = obj, *otmp2; + char buf[80]; + const char *msg; + coord cc; + + if (get_obj_location(obj, &cc.x, &cc.y, 0)) /* might be carried */ + obj->ox = cc.x, obj->oy = cc.y; + + otmp->otrapped = 0; /* trap is one-shot; clear flag first in case + chest kills you and ends up in bones file */ + You(disarm ? "set it off!" : "trigger a trap!"); + display_nhwindow(WIN_MESSAGE, FALSE); + if (Luck > -13 && rn2(13+Luck) > 7) { /* saved by luck */ + /* trap went off, but good luck prevents damage */ + switch (rn2(13)) { + case 12: + case 11: msg = "explosive charge is a dud"; break; + case 10: + case 9: msg = "electric charge is grounded"; break; + case 8: + case 7: msg = "flame fizzles out"; break; + case 6: + case 5: + case 4: msg = "poisoned needle misses"; break; + case 3: + case 2: + case 1: + case 0: msg = "gas cloud blows away"; break; + default: impossible("chest disarm bug"); msg = (char *)0; + break; + } + if (msg) pline("But luckily the %s!", msg); + } else { + switch(rn2(20) ? ((Luck >= 13) ? 0 : rn2(13-Luck)) : rn2(26)) { + case 25: + case 24: + case 23: + case 22: + case 21: { + struct monst *shkp = 0; + long loss = 0L; + boolean costly, insider; + register xchar ox = obj->ox, oy = obj->oy; + + /* the obj location need not be that of player */ + costly = (costly_spot(ox, oy) && + (shkp = shop_keeper(*in_rooms(ox, oy, + SHOPBASE))) != (struct monst *)0); + insider = (*u.ushops && inside_shop(u.ux, u.uy) && + *in_rooms(ox, oy, SHOPBASE) == *u.ushops); + + pline("%s!", Tobjnam(obj, "explode")); + Sprintf(buf, "exploding %s", xname(obj)); + + if(costly) + loss += stolen_value(obj, ox, oy, + (boolean)shkp->mpeaceful, TRUE); + delete_contents(obj); + /* we're about to delete all things at this location, + * which could include the ball & chain. + * If we attempt to call unpunish() in the + * for-loop below we can end up with otmp2 + * being invalid once the chain is gone. + * Deal with ball & chain right now instead. + */ + if (Punished && !carried(uball) && + ((uchain->ox == u.ux && uchain->oy == u.uy) || + (uball->ox == u.ux && uball->oy == u.uy))) + unpunish(); + + for(otmp = level.objects[u.ux][u.uy]; + otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if(costly) + loss += stolen_value(otmp, otmp->ox, + otmp->oy, (boolean)shkp->mpeaceful, + TRUE); + delobj(otmp); + } + wake_nearby(); + losehp(d(6,6), buf, KILLED_BY_AN); + exercise(A_STR, FALSE); + if(costly && loss) { + if(insider) + You("owe %ld %s for objects destroyed.", + loss, currency(loss)); + else { + You("caused %ld %s worth of damage!", + loss, currency(loss)); + make_angry_shk(shkp, ox, oy); + } + } + return TRUE; + } + case 20: + case 19: + case 18: + case 17: + pline("A cloud of noxious gas billows from %s.", + the(xname(obj))); + poisoned("gas cloud", A_STR, "cloud of poison gas",15); + exercise(A_CON, FALSE); + break; + case 16: + case 15: + case 14: + case 13: + You_feel("a needle prick your %s.",body_part(bodypart)); + poisoned("needle", A_CON, "poisoned needle",10); + exercise(A_CON, FALSE); + break; + case 12: + case 11: + case 10: + case 9: + dofiretrap(obj); + break; + case 8: + case 7: + case 6: { + int dmg; + + You("are jolted by a surge of electricity!"); + if(Shock_resistance) { + shieldeff(u.ux, u.uy); + You("don't seem to be affected."); + dmg = 0; + } else + dmg = d(4, 4); + destroy_item(RING_CLASS, AD_ELEC); + destroy_item(WAND_CLASS, AD_ELEC); + if (dmg) losehp(dmg, "electric shock", KILLED_BY_AN); + break; + } + case 5: + case 4: + case 3: + if (!Free_action) { + pline("Suddenly you are frozen in place!"); + nomul(-d(5, 6)); + exercise(A_DEX, FALSE); + nomovemsg = You_can_move_again; + } else You("momentarily stiffen."); + break; + case 2: + case 1: + case 0: + pline("A cloud of %s gas billows from %s.", + Blind ? blindgas[rn2(SIZE(blindgas))] : + rndcolor(), the(xname(obj))); + if(!Stunned) { + if (Hallucination) + pline("What a groovy feeling!"); + else if (Blind) + You("%s and get dizzy...", + stagger(youmonst.data, "stagger")); + else + You("%s and your vision blurs...", + stagger(youmonst.data, "stagger")); + } + make_stunned(HStun + rn1(7, 16),FALSE); + (void) make_hallucinated(HHallucination + rn1(5, 16),FALSE,0L); + break; + default: impossible("bad chest trap"); + break; + } + bot(); /* to get immediate botl re-display */ + } + + return FALSE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +struct trap * +t_at(x,y) +register int x, y; +{ + register struct trap *trap = ftrap; + while(trap) { + if(trap->tx == x && trap->ty == y) return(trap); + trap = trap->ntrap; + } + return((struct trap *)0); +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +deltrap(trap) +register struct trap *trap; +{ + register struct trap *ttmp; + + if(trap == ftrap) + ftrap = ftrap->ntrap; + else { + for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ; + ttmp->ntrap = trap->ntrap; + } + dealloc_trap(trap); +} + +boolean +delfloortrap(ttmp) +register struct trap *ttmp; +{ + /* Destroy a trap that emanates from the floor. */ + /* some of these are arbitrary -dlc */ + if (ttmp && ((ttmp->ttyp == SQKY_BOARD) || + (ttmp->ttyp == BEAR_TRAP) || + (ttmp->ttyp == LANDMINE) || + (ttmp->ttyp == FIRE_TRAP) || + (ttmp->ttyp == PIT) || + (ttmp->ttyp == SPIKED_PIT) || + (ttmp->ttyp == HOLE) || + (ttmp->ttyp == TRAPDOOR) || + (ttmp->ttyp == TELEP_TRAP) || + (ttmp->ttyp == LEVEL_TELEP) || + (ttmp->ttyp == WEB) || + (ttmp->ttyp == MAGIC_TRAP) || + (ttmp->ttyp == ANTI_MAGIC))) { + register struct monst *mtmp; + + if (ttmp->tx == u.ux && ttmp->ty == u.uy) { + u.utrap = 0; + u.utraptype = 0; + } else if ((mtmp = m_at(ttmp->tx, ttmp->ty)) != 0) { + mtmp->mtrapped = 0; + } + deltrap(ttmp); + return TRUE; + } else + return FALSE; +} + +/* used for doors (also tins). can be used for anything else that opens. */ +void +b_trapped(item, bodypart) +register const char *item; +register int bodypart; +{ + register int lvl = level_difficulty(); + int dmg = rnd(5 + (lvl < 5 ? lvl : 2+lvl/2)); + + pline("KABOOM!! %s was booby-trapped!", The(item)); + wake_nearby(); + losehp(dmg, "explosion", KILLED_BY_AN); + exercise(A_STR, FALSE); + if (bodypart) exercise(A_CON, FALSE); + make_stunned(HStun + dmg, TRUE); +} + +/* Monster is hit by trap. */ +/* Note: doesn't work if both obj and d_override are null */ +STATIC_OVL boolean +thitm(tlev, mon, obj, d_override, nocorpse) +int tlev; +struct monst *mon; +struct obj *obj; +int d_override; +boolean nocorpse; +{ + int strike; + boolean trapkilled = FALSE; + + if (d_override) strike = 1; + else if (obj) strike = (find_mac(mon) + tlev + obj->spe <= rnd(20)); + else strike = (find_mac(mon) + tlev <= rnd(20)); + + /* Actually more accurate than thitu, which doesn't take + * obj->spe into account. + */ + if(!strike) { + if (obj && cansee(mon->mx, mon->my)) + pline("%s is almost hit by %s!", Monnam(mon), doname(obj)); + } else { + int dam = 1; + + if (obj && cansee(mon->mx, mon->my)) + pline("%s is hit by %s!", Monnam(mon), doname(obj)); + if (d_override) dam = d_override; + else if (obj) { + dam = dmgval(obj, mon); + if (dam < 1) dam = 1; + } + if ((mon->mhp -= dam) <= 0) { + int xx = mon->mx; + int yy = mon->my; + + monkilled(mon, "", nocorpse ? -AD_RBRE : AD_PHYS); + if (mon->mhp <= 0) { + newsym(xx, yy); + trapkilled = TRUE; + } + } + } + if (obj && (!strike || d_override)) { + place_object(obj, mon->mx, mon->my); + stackobj(obj); + } else if (obj) dealloc_obj(obj); + + return trapkilled; +} + +boolean +unconscious() +{ + return((boolean)(multi < 0 && (!nomovemsg || + u.usleep || + !strncmp(nomovemsg,"You regain con", 14) || + !strncmp(nomovemsg,"You are consci", 14)))); +} + +static const char lava_killer[] = "molten lava"; + +boolean +lava_effects() +{ + register struct obj *obj, *obj2; + int dmg; + boolean usurvive; + + burn_away_slime(); + if (likes_lava(youmonst.data)) return FALSE; + + if (!Fire_resistance) { + if(Wwalking) { + dmg = d(6,6); + pline_The("lava here burns you!"); + if(dmg < u.uhp) { + losehp(dmg, lava_killer, KILLED_BY); + goto burn_stuff; + } + } else + You("fall into the lava!"); + + usurvive = Lifesaved || discover; +#ifdef WIZARD + if (wizard) usurvive = TRUE; +#endif + for(obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if(is_organic(obj) && !obj->oerodeproof) { + if(obj->owornmask) { + if (usurvive) + Your("%s into flame!", aobjnam(obj, "burst")); + + if(obj == uarm) (void) Armor_gone(); + else if(obj == uarmc) (void) Cloak_off(); + else if(obj == uarmh) (void) Helmet_off(); + else if(obj == uarms) (void) Shield_off(); + else if(obj == uarmg) (void) Gloves_off(); + else if(obj == uarmf) (void) Boots_off(); +#ifdef TOURIST + else if(obj == uarmu) setnotworn(obj); +#endif + else if(obj == uleft) Ring_gone(obj); + else if(obj == uright) Ring_gone(obj); + else if(obj == ublindf) Blindf_off(obj); + else if(obj == uamul) Amulet_off(); + else if(obj == uwep) uwepgone(); + else if (obj == uquiver) uqwepgone(); + else if (obj == uswapwep) uswapwepgone(); + } + useupall(obj); + } + } + + /* s/he died... */ + u.uhp = -1; + killer_format = KILLED_BY; + killer = lava_killer; + You("burn to a crisp..."); + done(BURNING); + while (!safe_teleds(TRUE)) { + pline("You're still burning."); + done(BURNING); + } + You("find yourself back on solid %s.", surface(u.ux, u.uy)); + return(TRUE); + } + + if (!Wwalking) { + u.utrap = rn1(4, 4) + (rn1(4, 12) << 8); + u.utraptype = TT_LAVA; + You("sink into the lava, but it only burns slightly!"); + if (u.uhp > 1) + losehp(1, lava_killer, KILLED_BY); + } + /* just want to burn boots, not all armor; destroy_item doesn't work on + armor anyway */ +burn_stuff: + if(uarmf && !uarmf->oerodeproof && is_organic(uarmf)) { + /* save uarmf value because Boots_off() sets uarmf to null */ + obj = uarmf; + Your("%s bursts into flame!", xname(obj)); + (void) Boots_off(); + useup(obj); + } + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + return(FALSE); +} + +#endif /* OVLB */ + +/*trap.c*/ diff --git a/src/u_init.c b/src/u_init.c new file mode 100644 index 0000000..77c92b0 --- /dev/null +++ b/src/u_init.c @@ -0,0 +1,1062 @@ +/* SCCS Id: @(#)u_init.c 3.4 2002/10/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +struct trobj { + short trotyp; + schar trspe; + char trclass; + Bitfield(trquan,6); + Bitfield(trbless,2); +}; + +STATIC_DCL void FDECL(ini_inv, (struct trobj *)); +STATIC_DCL void FDECL(knows_object,(int)); +STATIC_DCL void FDECL(knows_class,(CHAR_P)); +STATIC_DCL boolean FDECL(restricted_spell_discipline, (int)); + +#define UNDEF_TYP 0 +#define UNDEF_SPE '\177' +#define UNDEF_BLESS 2 + +/* + * Initial inventory for the various roles. + */ + +static struct trobj Archeologist[] = { + /* if adventure has a name... idea from tan@uvm-gen */ + { BULLWHIP, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, + { LEATHER_JACKET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { FEDORA, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, + { PICK_AXE, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, + { TINNING_KIT, UNDEF_SPE, TOOL_CLASS, 1, UNDEF_BLESS }, + { TOUCHSTONE, 0, GEM_CLASS, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Barbarian[] = { +#define B_MAJOR 0 /* two-handed sword or battle-axe */ +#define B_MINOR 1 /* matched with axe or short sword */ + { TWO_HANDED_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { AXE, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { RING_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Cave_man[] = { +#define C_AMMO 2 + { CLUB, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { SLING, 2, WEAPON_CLASS, 1, UNDEF_BLESS }, + { FLINT, 0, GEM_CLASS, 15, UNDEF_BLESS }, /* quan is variable */ + { ROCK, 0, GEM_CLASS, 3, 0 }, /* yields 18..33 */ + { LEATHER_ARMOR, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Healer[] = { + { SCALPEL, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, + { STETHOSCOPE, 0, TOOL_CLASS, 1, 0 }, + { POT_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 4, UNDEF_BLESS }, + { WAN_SLEEP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, + /* always blessed, so it's guaranteed readable */ + { SPE_HEALING, 0, SPBOOK_CLASS, 1, 1 }, + { SPE_EXTRA_HEALING, 0, SPBOOK_CLASS, 1, 1 }, + { SPE_STONE_TO_FLESH, 0, SPBOOK_CLASS, 1, 1 }, + { APPLE, 0, FOOD_CLASS, 5, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Knight[] = { + { LONG_SWORD, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { LANCE, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { RING_MAIL, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, + { HELMET, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { LEATHER_GLOVES, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { APPLE, 0, FOOD_CLASS, 10, 0 }, + { CARROT, 0, FOOD_CLASS, 10, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Monk[] = { +#define M_BOOK 2 + { LEATHER_GLOVES, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, + { ROBE, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, 1 }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 1, UNDEF_BLESS }, + { POT_HEALING, 0, POTION_CLASS, 3, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 3, 0 }, + { APPLE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, + { ORANGE, 0, FOOD_CLASS, 5, UNDEF_BLESS }, + /* Yes, we know fortune cookies aren't really from China. They were + * invented by George Jung in Los Angeles, California, USA in 1916. + */ + { FORTUNE_COOKIE, 0, FOOD_CLASS, 3, UNDEF_BLESS }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Priest[] = { + { MACE, 1, WEAPON_CLASS, 1, 1 }, + { ROBE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { POT_WATER, 0, POTION_CLASS, 4, 1 }, /* holy water */ + { CLOVE_OF_GARLIC, 0, FOOD_CLASS, 1, 0 }, + { SPRIG_OF_WOLFSBANE, 0, FOOD_CLASS, 1, 0 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 2, UNDEF_BLESS }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Ranger[] = { +#define RAN_BOW 1 +#define RAN_TWO_ARROWS 2 +#define RAN_ZERO_ARROWS 3 + { DAGGER, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { BOW, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { ARROW, 2, WEAPON_CLASS, 50, UNDEF_BLESS }, + { ARROW, 0, WEAPON_CLASS, 30, UNDEF_BLESS }, + { CLOAK_OF_DISPLACEMENT, 2, ARMOR_CLASS, 1, UNDEF_BLESS }, + { CRAM_RATION, 0, FOOD_CLASS, 4, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Rogue[] = { +#define R_DAGGERS 1 + { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 10, 0 }, /* quan is variable */ + { LEATHER_ARMOR, 1, ARMOR_CLASS, 1, UNDEF_BLESS }, + { POT_SICKNESS, 0, POTION_CLASS, 1, 0 }, + { LOCK_PICK, 9, TOOL_CLASS, 1, 0 }, + { SACK, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Samurai[] = { +#define S_ARROWS 3 + { KATANA, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { SHORT_SWORD, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, /* wakizashi */ + { YUMI, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { YA, 0, WEAPON_CLASS, 25, UNDEF_BLESS }, /* variable quan */ + { SPLINT_MAIL, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0 } +}; +#ifdef TOURIST +static struct trobj Tourist[] = { +#define T_DARTS 0 + { DART, 2, WEAPON_CLASS, 25, UNDEF_BLESS }, /* quan is variable */ + { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 10, 0 }, + { POT_EXTRA_HEALING, 0, POTION_CLASS, 2, UNDEF_BLESS }, + { SCR_MAGIC_MAPPING, 0, SCROLL_CLASS, 4, UNDEF_BLESS }, + { HAWAIIAN_SHIRT, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { EXPENSIVE_CAMERA, UNDEF_SPE, TOOL_CLASS, 1, 0 }, + { CREDIT_CARD, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +#endif +static struct trobj Valkyrie[] = { + { LONG_SWORD, 1, WEAPON_CLASS, 1, UNDEF_BLESS }, + { DAGGER, 0, WEAPON_CLASS, 1, UNDEF_BLESS }, + { SMALL_SHIELD, 3, ARMOR_CLASS, 1, UNDEF_BLESS }, + { FOOD_RATION, 0, FOOD_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Wizard[] = { +#define W_MULTSTART 2 +#define W_MULTEND 6 + { QUARTERSTAFF, 1, WEAPON_CLASS, 1, 1 }, + { CLOAK_OF_MAGIC_RESISTANCE, 0, ARMOR_CLASS, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, WAND_CLASS, 1, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, RING_CLASS, 2, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, POTION_CLASS, 3, UNDEF_BLESS }, + { UNDEF_TYP, UNDEF_SPE, SCROLL_CLASS, 3, UNDEF_BLESS }, + { SPE_FORCE_BOLT, 0, SPBOOK_CLASS, 1, 1 }, + { UNDEF_TYP, UNDEF_SPE, SPBOOK_CLASS, 1, UNDEF_BLESS }, + { 0, 0, 0, 0, 0 } +}; + +/* + * Optional extra inventory items. + */ + +static struct trobj Tinopener[] = { + { TIN_OPENER, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Magicmarker[] = { + { MAGIC_MARKER, UNDEF_SPE, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Lamp[] = { + { OIL_LAMP, 1, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Blindfold[] = { + { BLINDFOLD, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Instrument[] = { + { WOODEN_FLUTE, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Xtra_food[] = { + { UNDEF_TYP, UNDEF_SPE, FOOD_CLASS, 2, 0 }, + { 0, 0, 0, 0, 0 } +}; +#ifdef TOURIST +static struct trobj Leash[] = { + { LEASH, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +static struct trobj Towel[] = { + { TOWEL, 0, TOOL_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +#endif /* TOURIST */ +static struct trobj Wishing[] = { + { WAN_WISHING, 3, WAND_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +#ifdef GOLDOBJ +static struct trobj Money[] = { + { GOLD_PIECE, 0 , COIN_CLASS, 1, 0 }, + { 0, 0, 0, 0, 0 } +}; +#endif + +/* race-based substitutions for initial inventory; + the weaker cloak for elven rangers is intentional--they shoot better */ +static struct inv_sub { short race_pm, item_otyp, subs_otyp; } inv_subs[] = { + { PM_ELF, DAGGER, ELVEN_DAGGER }, + { PM_ELF, SPEAR, ELVEN_SPEAR }, + { PM_ELF, SHORT_SWORD, ELVEN_SHORT_SWORD }, + { PM_ELF, BOW, ELVEN_BOW }, + { PM_ELF, ARROW, ELVEN_ARROW }, + { PM_ELF, HELMET, ELVEN_LEATHER_HELM }, + /* { PM_ELF, SMALL_SHIELD, ELVEN_SHIELD }, */ + { PM_ELF, CLOAK_OF_DISPLACEMENT, ELVEN_CLOAK }, + { PM_ELF, CRAM_RATION, LEMBAS_WAFER }, + { PM_ORC, DAGGER, ORCISH_DAGGER }, + { PM_ORC, SPEAR, ORCISH_SPEAR }, + { PM_ORC, SHORT_SWORD, ORCISH_SHORT_SWORD }, + { PM_ORC, BOW, ORCISH_BOW }, + { PM_ORC, ARROW, ORCISH_ARROW }, + { PM_ORC, HELMET, ORCISH_HELM }, + { PM_ORC, SMALL_SHIELD, ORCISH_SHIELD }, + { PM_ORC, RING_MAIL, ORCISH_RING_MAIL }, + { PM_ORC, CHAIN_MAIL, ORCISH_CHAIN_MAIL }, + { PM_DWARF, SPEAR, DWARVISH_SPEAR }, + { PM_DWARF, SHORT_SWORD, DWARVISH_SHORT_SWORD }, + { PM_DWARF, HELMET, DWARVISH_IRON_HELM }, + /* { PM_DWARF, SMALL_SHIELD, DWARVISH_ROUNDSHIELD }, */ + /* { PM_DWARF, PICK_AXE, DWARVISH_MATTOCK }, */ + { PM_GNOME, BOW, CROSSBOW }, + { PM_GNOME, ARROW, CROSSBOW_BOLT }, + { NON_PM, STRANGE_OBJECT, STRANGE_OBJECT } +}; + +static const struct def_skill Skill_A[] = { + { P_DAGGER, P_BASIC }, { P_KNIFE, P_BASIC }, + { P_PICK_AXE, P_EXPERT }, { P_SHORT_SWORD, P_BASIC }, + { P_SCIMITAR, P_SKILLED }, { P_SABER, P_EXPERT }, + { P_CLUB, P_SKILLED }, { P_QUARTERSTAFF, P_SKILLED }, + { P_SLING, P_SKILLED }, { P_DART, P_BASIC }, + { P_BOOMERANG, P_EXPERT }, { P_WHIP, P_EXPERT }, + { P_UNICORN_HORN, P_SKILLED }, + { P_ATTACK_SPELL, P_BASIC }, { P_HEALING_SPELL, P_BASIC }, + { P_DIVINATION_SPELL, P_EXPERT}, { P_MATTER_SPELL, P_BASIC}, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_TWO_WEAPON_COMBAT, P_BASIC }, + { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_B[] = { + { P_DAGGER, P_BASIC }, { P_AXE, P_EXPERT }, + { P_PICK_AXE, P_SKILLED }, { P_SHORT_SWORD, P_EXPERT }, + { P_BROAD_SWORD, P_SKILLED }, { P_LONG_SWORD, P_SKILLED }, + { P_TWO_HANDED_SWORD, P_EXPERT }, { P_SCIMITAR, P_SKILLED }, + { P_SABER, P_BASIC }, { P_CLUB, P_SKILLED }, + { P_MACE, P_SKILLED }, { P_MORNING_STAR, P_SKILLED }, + { P_FLAIL, P_BASIC }, { P_HAMMER, P_EXPERT }, + { P_QUARTERSTAFF, P_BASIC }, { P_SPEAR, P_SKILLED }, + { P_TRIDENT, P_SKILLED }, { P_BOW, P_BASIC }, + { P_ATTACK_SPELL, P_SKILLED }, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_TWO_WEAPON_COMBAT, P_BASIC }, + { P_BARE_HANDED_COMBAT, P_MASTER }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_C[] = { + { P_DAGGER, P_BASIC }, { P_KNIFE, P_SKILLED }, + { P_AXE, P_SKILLED }, { P_PICK_AXE, P_BASIC }, + { P_CLUB, P_EXPERT }, { P_MACE, P_EXPERT }, + { P_MORNING_STAR, P_BASIC }, { P_FLAIL, P_SKILLED }, + { P_HAMMER, P_SKILLED }, { P_QUARTERSTAFF, P_EXPERT }, + { P_POLEARMS, P_SKILLED }, { P_SPEAR, P_EXPERT }, + { P_JAVELIN, P_SKILLED }, { P_TRIDENT, P_SKILLED }, + { P_BOW, P_SKILLED }, { P_SLING, P_EXPERT }, + { P_ATTACK_SPELL, P_BASIC }, { P_MATTER_SPELL, P_SKILLED }, + { P_BOOMERANG, P_EXPERT }, { P_UNICORN_HORN, P_BASIC }, + { P_BARE_HANDED_COMBAT, P_MASTER }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_H[] = { + { P_DAGGER, P_SKILLED }, { P_KNIFE, P_EXPERT }, + { P_SHORT_SWORD, P_SKILLED }, { P_SCIMITAR, P_BASIC }, + { P_SABER, P_BASIC }, { P_CLUB, P_SKILLED }, + { P_MACE, P_BASIC }, { P_QUARTERSTAFF, P_EXPERT }, + { P_POLEARMS, P_BASIC }, { P_SPEAR, P_BASIC }, + { P_JAVELIN, P_BASIC }, { P_TRIDENT, P_BASIC }, + { P_SLING, P_SKILLED }, { P_DART, P_EXPERT }, + { P_SHURIKEN, P_SKILLED }, { P_UNICORN_HORN, P_EXPERT }, + { P_HEALING_SPELL, P_EXPERT }, + { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_K[] = { + { P_DAGGER, P_BASIC }, { P_KNIFE, P_BASIC }, + { P_AXE, P_SKILLED }, { P_PICK_AXE, P_BASIC }, + { P_SHORT_SWORD, P_SKILLED }, { P_BROAD_SWORD, P_SKILLED }, + { P_LONG_SWORD, P_EXPERT }, { P_TWO_HANDED_SWORD, P_SKILLED }, + { P_SCIMITAR, P_BASIC }, { P_SABER, P_SKILLED }, + { P_CLUB, P_BASIC }, { P_MACE, P_SKILLED }, + { P_MORNING_STAR, P_SKILLED }, { P_FLAIL, P_BASIC }, + { P_HAMMER, P_BASIC }, { P_POLEARMS, P_SKILLED }, + { P_SPEAR, P_SKILLED }, { P_JAVELIN, P_SKILLED }, + { P_TRIDENT, P_BASIC }, { P_LANCE, P_EXPERT }, + { P_BOW, P_BASIC }, { P_CROSSBOW, P_SKILLED }, + { P_ATTACK_SPELL, P_SKILLED }, { P_HEALING_SPELL, P_SKILLED }, + { P_CLERIC_SPELL, P_SKILLED }, +#ifdef STEED + { P_RIDING, P_EXPERT }, +#endif + { P_TWO_WEAPON_COMBAT, P_SKILLED }, + { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_Mon[] = { + { P_QUARTERSTAFF, P_BASIC }, { P_SPEAR, P_BASIC }, + { P_JAVELIN, P_BASIC }, { P_CROSSBOW, P_BASIC }, + { P_SHURIKEN, P_BASIC }, + { P_ATTACK_SPELL, P_BASIC }, { P_HEALING_SPELL, P_EXPERT }, + { P_DIVINATION_SPELL, P_BASIC },{ P_ENCHANTMENT_SPELL, P_BASIC }, + { P_CLERIC_SPELL, P_SKILLED }, { P_ESCAPE_SPELL, P_BASIC }, + { P_MATTER_SPELL, P_BASIC }, + { P_MARTIAL_ARTS, P_GRAND_MASTER }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_P[] = { + { P_CLUB, P_EXPERT }, { P_MACE, P_EXPERT }, + { P_MORNING_STAR, P_EXPERT }, { P_FLAIL, P_EXPERT }, + { P_HAMMER, P_EXPERT }, { P_QUARTERSTAFF, P_EXPERT }, + { P_POLEARMS, P_SKILLED }, { P_SPEAR, P_SKILLED }, + { P_JAVELIN, P_SKILLED }, { P_TRIDENT, P_SKILLED }, + { P_LANCE, P_BASIC }, { P_BOW, P_BASIC }, + { P_SLING, P_BASIC }, { P_CROSSBOW, P_BASIC }, + { P_DART, P_BASIC }, { P_SHURIKEN, P_BASIC }, + { P_BOOMERANG, P_BASIC }, { P_UNICORN_HORN, P_SKILLED }, + { P_HEALING_SPELL, P_EXPERT }, { P_DIVINATION_SPELL, P_EXPERT }, + { P_CLERIC_SPELL, P_EXPERT }, + { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_R[] = { + { P_DAGGER, P_EXPERT }, { P_KNIFE, P_EXPERT }, + { P_SHORT_SWORD, P_EXPERT }, { P_BROAD_SWORD, P_SKILLED }, + { P_LONG_SWORD, P_SKILLED }, { P_TWO_HANDED_SWORD, P_BASIC }, + { P_SCIMITAR, P_SKILLED }, { P_SABER, P_SKILLED }, + { P_CLUB, P_SKILLED }, { P_MACE, P_SKILLED }, + { P_MORNING_STAR, P_BASIC }, { P_FLAIL, P_BASIC }, + { P_HAMMER, P_BASIC }, { P_POLEARMS, P_BASIC }, + { P_SPEAR, P_BASIC }, { P_CROSSBOW, P_EXPERT }, + { P_DART, P_EXPERT }, { P_SHURIKEN, P_SKILLED }, + { P_DIVINATION_SPELL, P_SKILLED }, { P_ESCAPE_SPELL, P_SKILLED }, + { P_MATTER_SPELL, P_SKILLED }, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_TWO_WEAPON_COMBAT, P_EXPERT }, + { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_Ran[] = { + { P_DAGGER, P_EXPERT }, { P_KNIFE, P_SKILLED }, + { P_AXE, P_SKILLED }, { P_PICK_AXE, P_BASIC }, + { P_SHORT_SWORD, P_BASIC }, { P_MORNING_STAR, P_BASIC }, + { P_FLAIL, P_SKILLED }, { P_HAMMER, P_BASIC }, + { P_QUARTERSTAFF, P_BASIC }, { P_POLEARMS, P_SKILLED }, + { P_SPEAR, P_SKILLED }, { P_JAVELIN, P_EXPERT }, + { P_TRIDENT, P_BASIC }, { P_BOW, P_EXPERT }, + { P_SLING, P_EXPERT }, { P_CROSSBOW, P_EXPERT }, + { P_DART, P_EXPERT }, { P_SHURIKEN, P_SKILLED }, + { P_BOOMERANG, P_EXPERT }, { P_WHIP, P_BASIC }, + { P_HEALING_SPELL, P_BASIC }, + { P_DIVINATION_SPELL, P_EXPERT }, + { P_ESCAPE_SPELL, P_BASIC }, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_S[] = { + { P_DAGGER, P_BASIC }, { P_KNIFE, P_SKILLED }, + { P_SHORT_SWORD, P_EXPERT }, { P_BROAD_SWORD, P_SKILLED }, + { P_LONG_SWORD, P_EXPERT }, { P_TWO_HANDED_SWORD, P_EXPERT }, + { P_SCIMITAR, P_BASIC }, { P_SABER, P_BASIC }, + { P_FLAIL, P_SKILLED }, { P_QUARTERSTAFF, P_BASIC }, + { P_POLEARMS, P_SKILLED }, { P_SPEAR, P_BASIC }, + { P_JAVELIN, P_BASIC }, { P_LANCE, P_SKILLED }, + { P_BOW, P_EXPERT }, { P_SHURIKEN, P_EXPERT }, + { P_ATTACK_SPELL, P_SKILLED }, { P_CLERIC_SPELL, P_SKILLED }, +#ifdef STEED + { P_RIDING, P_SKILLED }, +#endif + { P_TWO_WEAPON_COMBAT, P_EXPERT }, + { P_MARTIAL_ARTS, P_MASTER }, + { P_NONE, 0 } +}; + +#ifdef TOURIST +static const struct def_skill Skill_T[] = { + { P_DAGGER, P_EXPERT }, { P_KNIFE, P_SKILLED }, + { P_AXE, P_BASIC }, { P_PICK_AXE, P_BASIC }, + { P_SHORT_SWORD, P_EXPERT }, { P_BROAD_SWORD, P_BASIC }, + { P_LONG_SWORD, P_BASIC }, { P_TWO_HANDED_SWORD, P_BASIC }, + { P_SCIMITAR, P_SKILLED }, { P_SABER, P_SKILLED }, + { P_MACE, P_BASIC }, { P_MORNING_STAR, P_BASIC }, + { P_FLAIL, P_BASIC }, { P_HAMMER, P_BASIC }, + { P_QUARTERSTAFF, P_BASIC }, { P_POLEARMS, P_BASIC }, + { P_SPEAR, P_BASIC }, { P_JAVELIN, P_BASIC }, + { P_TRIDENT, P_BASIC }, { P_LANCE, P_BASIC }, + { P_BOW, P_BASIC }, { P_SLING, P_BASIC }, + { P_CROSSBOW, P_BASIC }, { P_DART, P_EXPERT }, + { P_SHURIKEN, P_BASIC }, { P_BOOMERANG, P_BASIC }, + { P_WHIP, P_BASIC }, { P_UNICORN_HORN, P_SKILLED }, + { P_DIVINATION_SPELL, P_BASIC }, { P_ENCHANTMENT_SPELL, P_BASIC }, + { P_ESCAPE_SPELL, P_SKILLED }, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_TWO_WEAPON_COMBAT, P_SKILLED }, + { P_BARE_HANDED_COMBAT, P_SKILLED }, + { P_NONE, 0 } +}; +#endif /* TOURIST */ + +static const struct def_skill Skill_V[] = { + { P_DAGGER, P_EXPERT }, { P_AXE, P_EXPERT }, + { P_PICK_AXE, P_SKILLED }, { P_SHORT_SWORD, P_SKILLED }, + { P_BROAD_SWORD, P_SKILLED }, { P_LONG_SWORD, P_EXPERT }, + { P_TWO_HANDED_SWORD, P_EXPERT }, { P_SCIMITAR, P_BASIC }, + { P_SABER, P_BASIC }, { P_HAMMER, P_EXPERT }, + { P_QUARTERSTAFF, P_BASIC }, { P_POLEARMS, P_SKILLED }, + { P_SPEAR, P_SKILLED }, { P_JAVELIN, P_BASIC }, + { P_TRIDENT, P_BASIC }, { P_LANCE, P_SKILLED }, + { P_SLING, P_BASIC }, + { P_ATTACK_SPELL, P_BASIC }, { P_ESCAPE_SPELL, P_BASIC }, +#ifdef STEED + { P_RIDING, P_SKILLED }, +#endif + { P_TWO_WEAPON_COMBAT, P_SKILLED }, + { P_BARE_HANDED_COMBAT, P_EXPERT }, + { P_NONE, 0 } +}; + +static const struct def_skill Skill_W[] = { + { P_DAGGER, P_EXPERT }, { P_KNIFE, P_SKILLED }, + { P_AXE, P_SKILLED }, { P_SHORT_SWORD, P_BASIC }, + { P_CLUB, P_SKILLED }, { P_MACE, P_BASIC }, + { P_QUARTERSTAFF, P_EXPERT }, { P_POLEARMS, P_SKILLED }, + { P_SPEAR, P_BASIC }, { P_JAVELIN, P_BASIC }, + { P_TRIDENT, P_BASIC }, { P_SLING, P_SKILLED }, + { P_DART, P_EXPERT }, { P_SHURIKEN, P_BASIC }, + { P_ATTACK_SPELL, P_EXPERT }, { P_HEALING_SPELL, P_SKILLED }, + { P_DIVINATION_SPELL, P_EXPERT }, { P_ENCHANTMENT_SPELL, P_SKILLED }, + { P_CLERIC_SPELL, P_SKILLED }, { P_ESCAPE_SPELL, P_EXPERT }, + { P_MATTER_SPELL, P_EXPERT }, +#ifdef STEED + { P_RIDING, P_BASIC }, +#endif + { P_BARE_HANDED_COMBAT, P_BASIC }, + { P_NONE, 0 } +}; + + +STATIC_OVL void +knows_object(obj) +register int obj; +{ + discover_object(obj,TRUE,FALSE); + objects[obj].oc_pre_discovered = 1; /* not a "discovery" */ +} + +/* Know ordinary (non-magical) objects of a certain class, + * like all gems except the loadstone and luckstone. + */ +STATIC_OVL void +knows_class(sym) +register char sym; +{ + register int ct; + for (ct = 1; ct < NUM_OBJECTS; ct++) + if (objects[ct].oc_class == sym && !objects[ct].oc_magic) + knows_object(ct); +} + +void +u_init() +{ + register int i; + + flags.female = flags.initgend; + flags.beginner = 1; + + /* zero u, including pointer values -- + * necessary when aborting from a failed restore */ + (void) memset((genericptr_t)&u, 0, sizeof(u)); + u.ustuck = (struct monst *)0; + +#if 0 /* documentation of more zero values as desirable */ + u.usick_cause[0] = 0; + u.uluck = u.moreluck = 0; +# ifdef TOURIST + uarmu = 0; +# endif + uarm = uarmc = uarmh = uarms = uarmg = uarmf = 0; + uwep = uball = uchain = uleft = uright = 0; + uswapwep = uquiver = 0; + u.twoweap = 0; + u.ublessed = 0; /* not worthy yet */ + u.ugangr = 0; /* gods not angry */ + u.ugifts = 0; /* no divine gifts bestowed */ +# ifdef ELBERETH + u.uevent.uhand_of_elbereth = 0; +# endif + u.uevent.uheard_tune = 0; + u.uevent.uopened_dbridge = 0; + u.uevent.udemigod = 0; /* not a demi-god yet... */ + u.udg_cnt = 0; + u.mh = u.mhmax = u.mtimedone = 0; + u.uz.dnum = u.uz0.dnum = 0; + u.utotype = 0; +#endif /* 0 */ + + u.uz.dlevel = 1; + u.uz0.dlevel = 0; + u.utolev = u.uz; + + u.umoved = FALSE; + u.umortality = 0; + u.ugrave_arise = NON_PM; + + u.umonnum = u.umonster = (flags.female && + urole.femalenum != NON_PM) ? urole.femalenum : + urole.malenum; + set_uasmon(); + + u.ulevel = 0; /* set up some of the initial attributes */ + u.uhp = u.uhpmax = newhp(); + u.uenmax = urole.enadv.infix + urace.enadv.infix; + if (urole.enadv.inrnd > 0) + u.uenmax += rnd(urole.enadv.inrnd); + if (urace.enadv.inrnd > 0) + u.uenmax += rnd(urace.enadv.inrnd); + u.uen = u.uenmax; + u.uspellprot = 0; + adjabil(0,1); + u.ulevel = u.ulevelmax = 1; + + init_uhunger(); + for (i = 0; i <= MAXSPELL; i++) spl_book[i].sp_id = NO_SPELL; + u.ublesscnt = 300; /* no prayers just yet */ + u.ualignbase[A_CURRENT] = u.ualignbase[A_ORIGINAL] = u.ualign.type = + aligns[flags.initalign].value; + u.ulycn = NON_PM; + +#if defined(BSD) && !defined(POSIX_TYPES) + (void) time((long *)&u.ubirthday); +#else + (void) time(&u.ubirthday); +#endif + + /* + * For now, everyone starts out with a night vision range of 1 and + * their xray range disabled. + */ + u.nv_range = 1; + u.xray_range = -1; + + + /*** Role-specific initializations ***/ + switch (Role_switch) { + /* rn2(100) > 50 necessary for some choices because some + * random number generators are bad enough to seriously + * skew the results if we use rn2(2)... --KAA + */ + case PM_ARCHEOLOGIST: + ini_inv(Archeologist); + if(!rn2(10)) ini_inv(Tinopener); + else if(!rn2(4)) ini_inv(Lamp); + else if(!rn2(10)) ini_inv(Magicmarker); + knows_object(SACK); + knows_object(TOUCHSTONE); + skill_init(Skill_A); + break; + case PM_BARBARIAN: + if (rn2(100) >= 50) { /* see above comment */ + Barbarian[B_MAJOR].trotyp = BATTLE_AXE; + Barbarian[B_MINOR].trotyp = SHORT_SWORD; + } + ini_inv(Barbarian); + if(!rn2(6)) ini_inv(Lamp); + knows_class(WEAPON_CLASS); + knows_class(ARMOR_CLASS); + skill_init(Skill_B); + break; + case PM_CAVEMAN: + Cave_man[C_AMMO].trquan = rn1(11, 10); /* 10..20 */ + ini_inv(Cave_man); + skill_init(Skill_C); + break; + case PM_HEALER: +#ifndef GOLDOBJ + u.ugold = u.ugold0 = rn1(1000, 1001); +#else + u.umoney0 = rn1(1000, 1001); +#endif + ini_inv(Healer); + if(!rn2(25)) ini_inv(Lamp); + knows_object(POT_FULL_HEALING); + skill_init(Skill_H); + break; + case PM_KNIGHT: + ini_inv(Knight); + knows_class(WEAPON_CLASS); + knows_class(ARMOR_CLASS); + /* give knights chess-like mobility + * -- idea from wooledge@skybridge.scl.cwru.edu */ + HJumping |= FROMOUTSIDE; + skill_init(Skill_K); + break; + case PM_MONK: + switch (rn2(90) / 30) { + case 0: Monk[M_BOOK].trotyp = SPE_HEALING; break; + case 1: Monk[M_BOOK].trotyp = SPE_PROTECTION; break; + case 2: Monk[M_BOOK].trotyp = SPE_SLEEP; break; + } + ini_inv(Monk); + if(!rn2(5)) ini_inv(Magicmarker); + else if(!rn2(10)) ini_inv(Lamp); + knows_class(ARMOR_CLASS); + skill_init(Skill_Mon); + break; + case PM_PRIEST: + ini_inv(Priest); + if(!rn2(10)) ini_inv(Magicmarker); + else if(!rn2(10)) ini_inv(Lamp); + knows_object(POT_WATER); + skill_init(Skill_P); + /* KMH, conduct -- + * Some may claim that this isn't agnostic, since they + * are literally "priests" and they have holy water. + * But we don't count it as such. Purists can always + * avoid playing priests and/or confirm another player's + * role in their YAAP. + */ + break; + case PM_RANGER: + Ranger[RAN_TWO_ARROWS].trquan = rn1(10, 50); + Ranger[RAN_ZERO_ARROWS].trquan = rn1(10, 30); + ini_inv(Ranger); + skill_init(Skill_Ran); + break; + case PM_ROGUE: + Rogue[R_DAGGERS].trquan = rn1(10, 6); +#ifndef GOLDOBJ + u.ugold = u.ugold0 = 0; +#else + u.umoney0 = 0; +#endif + ini_inv(Rogue); + if(!rn2(5)) ini_inv(Blindfold); + knows_object(SACK); + skill_init(Skill_R); + break; + case PM_SAMURAI: + Samurai[S_ARROWS].trquan = rn1(20, 26); + ini_inv(Samurai); + if(!rn2(5)) ini_inv(Blindfold); + knows_class(WEAPON_CLASS); + knows_class(ARMOR_CLASS); + skill_init(Skill_S); + break; +#ifdef TOURIST + case PM_TOURIST: + Tourist[T_DARTS].trquan = rn1(20, 21); +#ifndef GOLDOBJ + u.ugold = u.ugold0 = rnd(1000); +#else + u.umoney0 = rnd(1000); +#endif + ini_inv(Tourist); + if(!rn2(25)) ini_inv(Tinopener); + else if(!rn2(25)) ini_inv(Leash); + else if(!rn2(25)) ini_inv(Towel); + else if(!rn2(25)) ini_inv(Magicmarker); + skill_init(Skill_T); + break; +#endif + case PM_VALKYRIE: + ini_inv(Valkyrie); + if(!rn2(6)) ini_inv(Lamp); + knows_class(WEAPON_CLASS); + knows_class(ARMOR_CLASS); + skill_init(Skill_V); + break; + case PM_WIZARD: + ini_inv(Wizard); + if(!rn2(5)) ini_inv(Magicmarker); + if(!rn2(5)) ini_inv(Blindfold); + skill_init(Skill_W); + break; + + default: /* impossible */ + break; + } + + + /*** Race-specific initializations ***/ + switch (Race_switch) { + case PM_HUMAN: + /* Nothing special */ + break; + + case PM_ELF: + /* + * Elves are people of music and song, or they are warriors. + * Non-warriors get an instrument. We use a kludge to + * get only non-magic instruments. + */ + if (Role_if(PM_PRIEST) || Role_if(PM_WIZARD)) { + static int trotyp[] = { + WOODEN_FLUTE, TOOLED_HORN, WOODEN_HARP, + BELL, BUGLE, LEATHER_DRUM + }; + Instrument[0].trotyp = trotyp[rn2(SIZE(trotyp))]; + ini_inv(Instrument); + } + + /* Elves can recognize all elvish objects */ + knows_object(ELVEN_SHORT_SWORD); + knows_object(ELVEN_ARROW); + knows_object(ELVEN_BOW); + knows_object(ELVEN_SPEAR); + knows_object(ELVEN_DAGGER); + knows_object(ELVEN_BROADSWORD); + knows_object(ELVEN_MITHRIL_COAT); + knows_object(ELVEN_LEATHER_HELM); + knows_object(ELVEN_SHIELD); + knows_object(ELVEN_BOOTS); + knows_object(ELVEN_CLOAK); + break; + + case PM_DWARF: + /* Dwarves can recognize all dwarvish objects */ + knows_object(DWARVISH_SPEAR); + knows_object(DWARVISH_SHORT_SWORD); + knows_object(DWARVISH_MATTOCK); + knows_object(DWARVISH_IRON_HELM); + knows_object(DWARVISH_MITHRIL_COAT); + knows_object(DWARVISH_CLOAK); + knows_object(DWARVISH_ROUNDSHIELD); + break; + + case PM_GNOME: + break; + + case PM_ORC: + /* compensate for generally inferior equipment */ + if (!Role_if(PM_WIZARD)) + ini_inv(Xtra_food); + /* Orcs can recognize all orcish objects */ + knows_object(ORCISH_SHORT_SWORD); + knows_object(ORCISH_ARROW); + knows_object(ORCISH_BOW); + knows_object(ORCISH_SPEAR); + knows_object(ORCISH_DAGGER); + knows_object(ORCISH_CHAIN_MAIL); + knows_object(ORCISH_RING_MAIL); + knows_object(ORCISH_HELM); + knows_object(ORCISH_SHIELD); + knows_object(URUK_HAI_SHIELD); + knows_object(ORCISH_CLOAK); + break; + + default: /* impossible */ + break; + } + + if (discover) + ini_inv(Wishing); + +#ifdef WIZARD + if (wizard) + read_wizkit(); +#endif + +#ifndef GOLDOBJ + u.ugold0 += hidden_gold(); /* in case sack has gold in it */ +#else + if (u.umoney0) ini_inv(Money); + u.umoney0 += hidden_gold(); /* in case sack has gold in it */ +#endif + + find_ac(); /* get initial ac value */ + init_attr(75); /* init attribute values */ + max_rank_sz(); /* set max str size for class ranks */ +/* + * Do we really need this? + */ + for(i = 0; i < A_MAX; i++) + if(!rn2(20)) { + register int xd = rn2(7) - 2; /* biased variation */ + (void) adjattrib(i, xd, TRUE); + if (ABASE(i) < AMAX(i)) AMAX(i) = ABASE(i); + } + + /* make sure you can carry all you have - especially for Tourists */ + while (inv_weight() > 0) { + if (adjattrib(A_STR, 1, TRUE)) continue; + if (adjattrib(A_CON, 1, TRUE)) continue; + /* only get here when didn't boost strength or constitution */ + break; + } + + return; +} + +/* skills aren't initialized, so we use the role-specific skill lists */ +STATIC_OVL boolean +restricted_spell_discipline(otyp) +int otyp; +{ + const struct def_skill *skills; + int this_skill = spell_skilltype(otyp); + + switch (Role_switch) { + case PM_ARCHEOLOGIST: skills = Skill_A; break; + case PM_BARBARIAN: skills = Skill_B; break; + case PM_CAVEMAN: skills = Skill_C; break; + case PM_HEALER: skills = Skill_H; break; + case PM_KNIGHT: skills = Skill_K; break; + case PM_MONK: skills = Skill_Mon; break; + case PM_PRIEST: skills = Skill_P; break; + case PM_RANGER: skills = Skill_Ran; break; + case PM_ROGUE: skills = Skill_R; break; + case PM_SAMURAI: skills = Skill_S; break; +#ifdef TOURIST + case PM_TOURIST: skills = Skill_T; break; +#endif + case PM_VALKYRIE: skills = Skill_V; break; + case PM_WIZARD: skills = Skill_W; break; + default: skills = 0; break; /* lint suppression */ + } + + while (skills->skill != P_NONE) { + if (skills->skill == this_skill) return FALSE; + ++skills; + } + return TRUE; +} + +STATIC_OVL void +ini_inv(trop) +register struct trobj *trop; +{ + struct obj *obj; + int otyp, i; + + while (trop->trclass) { + if (trop->trotyp != UNDEF_TYP) { + otyp = (int)trop->trotyp; + if (urace.malenum != PM_HUMAN) { + /* substitute specific items for generic ones */ + for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) + if (inv_subs[i].race_pm == urace.malenum && + otyp == inv_subs[i].item_otyp) { + otyp = inv_subs[i].subs_otyp; + break; + } + } + obj = mksobj(otyp, TRUE, FALSE); + } else { /* UNDEF_TYP */ + static NEARDATA short nocreate = STRANGE_OBJECT; + static NEARDATA short nocreate2 = STRANGE_OBJECT; + static NEARDATA short nocreate3 = STRANGE_OBJECT; + static NEARDATA short nocreate4 = STRANGE_OBJECT; + /* + * For random objects, do not create certain overly powerful + * items: wand of wishing, ring of levitation, or the + * polymorph/polymorph control combination. Specific objects, + * i.e. the discovery wishing, are still OK. + * Also, don't get a couple of really useless items. (Note: + * punishment isn't "useless". Some players who start out with + * one will immediately read it and use the iron ball as a + * weapon.) + */ + obj = mkobj(trop->trclass, FALSE); + otyp = obj->otyp; + while (otyp == WAN_WISHING + || otyp == nocreate + || otyp == nocreate2 + || otyp == nocreate3 + || otyp == nocreate4 +#ifdef ELBERETH + || otyp == RIN_LEVITATION +#endif + /* 'useless' items */ + || otyp == POT_HALLUCINATION + || otyp == POT_ACID + || otyp == SCR_AMNESIA + || otyp == SCR_FIRE + || otyp == SCR_BLANK_PAPER + || otyp == SPE_BLANK_PAPER + || otyp == RIN_AGGRAVATE_MONSTER + || otyp == RIN_HUNGER + || otyp == WAN_NOTHING + /* Monks don't use weapons */ + || (otyp == SCR_ENCHANT_WEAPON && + Role_if(PM_MONK)) + /* wizard patch -- they already have one */ + || (otyp == SPE_FORCE_BOLT && + Role_if(PM_WIZARD)) + /* powerful spells are either useless to + low level players or unbalancing; also + spells in restricted skill categories */ + || (obj->oclass == SPBOOK_CLASS && + (objects[otyp].oc_level > 3 || + restricted_spell_discipline(otyp))) + ) { + dealloc_obj(obj); + obj = mkobj(trop->trclass, FALSE); + otyp = obj->otyp; + } + + /* Don't start with +0 or negative rings */ + if (objects[otyp].oc_charged && obj->spe <= 0) + obj->spe = rne(3); + + /* Heavily relies on the fact that 1) we create wands + * before rings, 2) that we create rings before + * spellbooks, and that 3) not more than 1 object of a + * particular symbol is to be prohibited. (For more + * objects, we need more nocreate variables...) + */ + switch (otyp) { + case WAN_POLYMORPH: + case RIN_POLYMORPH: + case POT_POLYMORPH: + nocreate = RIN_POLYMORPH_CONTROL; + break; + case RIN_POLYMORPH_CONTROL: + nocreate = RIN_POLYMORPH; + nocreate2 = SPE_POLYMORPH; + nocreate3 = POT_POLYMORPH; + } + /* Don't have 2 of the same ring or spellbook */ + if (obj->oclass == RING_CLASS || + obj->oclass == SPBOOK_CLASS) + nocreate4 = otyp; + } + +#ifdef GOLDOBJ + if (trop->trclass == COIN_CLASS) { + /* no "blessed" or "identified" money */ + obj->quan = u.umoney0; + } else { +#endif + obj->dknown = obj->bknown = obj->rknown = 1; + if (objects[otyp].oc_uses_known) obj->known = 1; + obj->cursed = 0; + if (obj->opoisoned && u.ualign.type != A_CHAOTIC) + obj->opoisoned = 0; + if (obj->oclass == WEAPON_CLASS || + obj->oclass == TOOL_CLASS) { + obj->quan = (long) trop->trquan; + trop->trquan = 1; + } else if (obj->oclass == GEM_CLASS && + is_graystone(obj) && obj->otyp != FLINT) { + obj->quan = 1L; + } + if (trop->trspe != UNDEF_SPE) + obj->spe = trop->trspe; + if (trop->trbless != UNDEF_BLESS) + obj->blessed = trop->trbless; +#ifdef GOLDOBJ + } +#endif + /* defined after setting otyp+quan + blessedness */ + obj->owt = weight(obj); + obj = addinv(obj); + + /* Make the type known if necessary */ + if (OBJ_DESCR(objects[otyp]) && obj->known) + discover_object(otyp, TRUE, FALSE); + if (otyp == OIL_LAMP) + discover_object(POT_OIL, TRUE, FALSE); + + if(obj->oclass == ARMOR_CLASS){ + if (is_shield(obj) && !uarms) { + setworn(obj, W_ARMS); + if (uswapwep) setuswapwep((struct obj *) 0); + } else if (is_helmet(obj) && !uarmh) + setworn(obj, W_ARMH); + else if (is_gloves(obj) && !uarmg) + setworn(obj, W_ARMG); +#ifdef TOURIST + else if (is_shirt(obj) && !uarmu) + setworn(obj, W_ARMU); +#endif + else if (is_cloak(obj) && !uarmc) + setworn(obj, W_ARMC); + else if (is_boots(obj) && !uarmf) + setworn(obj, W_ARMF); + else if (is_suit(obj) && !uarm) + setworn(obj, W_ARM); + } + + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || + otyp == TIN_OPENER || otyp == FLINT || otyp == ROCK) { + if (is_ammo(obj) || is_missile(obj)) { + if (!uquiver) setuqwep(obj); + } else if (!uwep) setuwep(obj); + else if (!uswapwep) setuswapwep(obj); + } + if (obj->oclass == SPBOOK_CLASS && + obj->otyp != SPE_BLANK_PAPER) + initialspell(obj); + +#if !defined(PYRAMID_BUG) && !defined(MAC) + if(--trop->trquan) continue; /* make a similar object */ +#else + if(trop->trquan) { /* check if zero first */ + --trop->trquan; + if(trop->trquan) + continue; /* make a similar object */ + } +#endif + trop++; + } +} + +/*u_init.c*/ diff --git a/src/uhitm.c b/src/uhitm.c new file mode 100644 index 0000000..3dd028a --- /dev/null +++ b/src/uhitm.c @@ -0,0 +1,2512 @@ +/* SCCS Id: @(#)uhitm.c 3.4 2003/02/18 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL boolean FDECL(known_hitum, (struct monst *,int *,struct attack *)); +STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *)); +STATIC_DCL boolean FDECL(hitum, (struct monst *,int,struct attack *)); +STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *,struct obj *,int)); +#ifdef STEED +STATIC_DCL int FDECL(joust, (struct monst *,struct obj *)); +#endif +STATIC_DCL void NDECL(demonpet); +STATIC_DCL boolean FDECL(m_slips_free, (struct monst *mtmp,struct attack *mattk)); +STATIC_DCL int FDECL(explum, (struct monst *,struct attack *)); +STATIC_DCL void FDECL(start_engulf, (struct monst *)); +STATIC_DCL void NDECL(end_engulf); +STATIC_DCL int FDECL(gulpum, (struct monst *,struct attack *)); +STATIC_DCL boolean FDECL(hmonas, (struct monst *,int)); +STATIC_DCL void FDECL(nohandglow, (struct monst *)); +STATIC_DCL boolean FDECL(shade_aware, (struct obj *)); + +extern boolean notonhead; /* for long worms */ +/* The below might become a parameter instead if we use it a lot */ +static int dieroll; +/* Used to flag attacks caused by Stormbringer's maliciousness. */ +static boolean override_confirmation = FALSE; + +#define PROJECTILE(obj) ((obj) && is_ammo(obj)) + +/* modified from hurtarmor() in mhitu.c */ +/* This is not static because it is also used for monsters rusting monsters */ +void +hurtmarmor(mdef, attk) +struct monst *mdef; +int attk; +{ + int hurt; + struct obj *target; + + switch(attk) { + /* 0 is burning, which we should never be called with */ + case AD_RUST: hurt = 1; break; + case AD_CORR: hurt = 3; break; + default: hurt = 2; break; + } + /* What the following code does: it keeps looping until it + * finds a target for the rust monster. + * Head, feet, etc... not covered by metal, or covered by + * rusty metal, are not targets. However, your body always + * is, no matter what covers it. + */ + while (1) { + switch(rn2(5)) { + case 0: + target = which_armor(mdef, W_ARMH); + if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) + continue; + break; + case 1: + target = which_armor(mdef, W_ARMC); + if (target) { + (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); + break; + } + if ((target = which_armor(mdef, W_ARM)) != (struct obj *)0) { + (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); +#ifdef TOURIST + } else if ((target = which_armor(mdef, W_ARMU)) != (struct obj *)0) { + (void)rust_dmg(target, xname(target), hurt, TRUE, mdef); +#endif + } + break; + case 2: + target = which_armor(mdef, W_ARMS); + if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) + continue; + break; + case 3: + target = which_armor(mdef, W_ARMG); + if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) + continue; + break; + case 4: + target = which_armor(mdef, W_ARMF); + if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef)) + continue; + break; + } + break; /* Out of while loop */ + } +} + +/* FALSE means it's OK to attack */ +boolean +attack_checks(mtmp, wep) +register struct monst *mtmp; +struct obj *wep; /* uwep for attack(), null for kick_monster() */ +{ + char qbuf[QBUFSZ]; + + /* if you're close enough to attack, alert any waiting monster */ + mtmp->mstrategy &= ~STRAT_WAITMASK; + + if (u.uswallow && mtmp == u.ustuck) return FALSE; + + if (flags.forcefight) { + /* Do this in the caller, after we checked that the monster + * didn't die from the blow. Reason: putting the 'I' there + * causes the hero to forget the square's contents since + * both 'I' and remembered contents are stored in .glyph. + * If the monster dies immediately from the blow, the 'I' will + * not stay there, so the player will have suddenly forgotten + * the square's contents for no apparent reason. + if (!canspotmon(mtmp) && + !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph)) + map_invisible(u.ux+u.dx, u.uy+u.dy); + */ + return FALSE; + } + + /* Put up an invisible monster marker, but with exceptions for + * monsters that hide and monsters you've been warned about. + * The former already prints a warning message and + * prevents you from hitting the monster just via the hidden monster + * code below; if we also did that here, similar behavior would be + * happening two turns in a row. The latter shows a glyph on + * the screen, so you know something is there. + */ + if (!canspotmon(mtmp) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && + !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) && + !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) { + pline("Wait! There's %s there you can't see!", + something); + map_invisible(u.ux+u.dx, u.uy+u.dy); + /* if it was an invisible mimic, treat it as if we stumbled + * onto a visible mimic + */ + if(mtmp->m_ap_type && !Protection_from_shape_changers) { + if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK)) + u.ustuck = mtmp; + } + wakeup(mtmp); /* always necessary; also un-mimics mimics */ + return TRUE; + } + + if (mtmp->m_ap_type && !Protection_from_shape_changers && + !sensemon(mtmp) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy))) { + /* If a hidden mimic was in a square where a player remembers + * some (probably different) unseen monster, the player is in + * luck--he attacks it even though it's hidden. + */ + if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) { + seemimic(mtmp); + return(FALSE); + } + stumble_onto_mimic(mtmp); + return TRUE; + } + + if (mtmp->mundetected && !canseemon(mtmp) && + !glyph_is_warning(glyph_at(u.ux+u.dx,u.uy+u.dy)) && + (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) { + mtmp->mundetected = mtmp->msleeping = 0; + newsym(mtmp->mx, mtmp->my); + if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) { + seemimic(mtmp); + return(FALSE); + } + if (!(Blind ? Blind_telepat : Unblind_telepat)) { + struct obj *obj; + + if (Blind || (is_pool(mtmp->mx,mtmp->my) && !Underwater)) + pline("Wait! There's a hidden monster there!"); + else if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0) + pline("Wait! There's %s hiding under %s!", + an(l_monnam(mtmp)), doname(obj)); + return TRUE; + } + } + + /* + * make sure to wake up a monster from the above cases if the + * hero can sense that the monster is there. + */ + if ((mtmp->mundetected || mtmp->m_ap_type) && sensemon(mtmp)) { + mtmp->mundetected = 0; + wakeup(mtmp); + } + + if (flags.confirm && mtmp->mpeaceful + && !Confusion && !Hallucination && !Stunned) { + /* Intelligent chaotic weapons (Stormbringer) want blood */ + if (wep && wep->oartifact == ART_STORMBRINGER) { + override_confirmation = TRUE; + return(FALSE); + } + if (canspotmon(mtmp)) { + Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp)); + if (yn(qbuf) != 'y') { + flags.move = 0; + return(TRUE); + } + } + } + + return(FALSE); +} + +/* + * It is unchivalrous for a knight to attack the defenseless or from behind. + */ +void +check_caitiff(mtmp) +struct monst *mtmp; +{ + if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL && + (!mtmp->mcanmove || mtmp->msleeping || + (mtmp->mflee && !mtmp->mavenge)) && + u.ualign.record > -10) { + You("caitiff!"); + adjalign(-1); + } +} + +schar +find_roll_to_hit(mtmp) +register struct monst *mtmp; +{ + schar tmp; + int tmp2; + + tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc + + maybe_polyd(youmonst.data->mlevel, u.ulevel); + + check_caitiff(mtmp); + +/* attacking peaceful creatures is bad for the samurai's giri */ + if (Role_if(PM_SAMURAI) && mtmp->mpeaceful && + u.ualign.record > -10) { + You("dishonorably attack the innocent!"); + adjalign(-1); + } + +/* Adjust vs. (and possibly modify) monster state. */ + + if(mtmp->mstun) tmp += 2; + if(mtmp->mflee) tmp += 2; + + if (mtmp->msleeping) { + mtmp->msleeping = 0; + tmp += 2; + } + if(!mtmp->mcanmove) { + tmp += 4; + if(!rn2(10)) { + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + } + } + if (is_orc(mtmp->data) && maybe_polyd(is_elf(youmonst.data), + Race_if(PM_ELF))) + tmp++; + if(Role_if(PM_MONK) && !Upolyd) { + if (uarm) { + Your("armor is rather cumbersome..."); + tmp -= urole.spelarmr; + } else if (!uwep && !uarms) { + tmp += (u.ulevel / 3) + 2; + } + } + +/* with a lot of luggage, your agility diminishes */ + if ((tmp2 = near_capacity()) != 0) tmp -= (tmp2*2) - 1; + if (u.utrap) tmp -= 3; +/* Some monsters have a combination of weapon attacks and non-weapon + * attacks. It is therefore wrong to add hitval to tmp; we must add + * it only for the specific attack (in hmonas()). + */ + if (uwep && !Upolyd) { + tmp += hitval(uwep, mtmp); + tmp += weapon_hit_bonus(uwep); + } + return tmp; +} + +/* try to attack; return FALSE if monster evaded */ +/* u.dx and u.dy must be set */ +boolean +attack(mtmp) +register struct monst *mtmp; +{ + schar tmp; + register struct permonst *mdat = mtmp->data; + + /* This section of code provides protection against accidentally + * hitting peaceful (like '@') and tame (like 'd') monsters. + * Protection is provided as long as player is not: blind, confused, + * hallucinating or stunned. + * changes by wwp 5/16/85 + * More changes 12/90, -dkh-. if its tame and safepet, (and protected + * 07/92) then we assume that you're not trying to attack. Instead, + * you'll usually just swap places if this is a movement command + */ + /* Intelligent chaotic weapons (Stormbringer) want blood */ + if (is_safepet(mtmp) && !flags.forcefight) { + if (!uwep || uwep->oartifact != ART_STORMBRINGER) { + /* there are some additional considerations: this won't work + * if in a shop or Punished or you miss a random roll or + * if you can walk thru walls and your pet cannot (KAA) or + * if your pet is a long worm (unless someone does better). + * there's also a chance of displacing a "frozen" monster. + * sleeping monsters might magically walk in their sleep. + */ + boolean foo = (Punished || !rn2(7) || is_longworm(mtmp->data)), + inshop = FALSE; + char *p; + + for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++) + if (tended_shop(&rooms[*p - ROOMOFFSET])) { + inshop = TRUE; + break; + } + + if (inshop || foo || + (IS_ROCK(levl[u.ux][u.uy].typ) && + !passes_walls(mtmp->data))) { + char buf[BUFSZ]; + + monflee(mtmp, rnd(6), FALSE, FALSE); + Strcpy(buf, y_monnam(mtmp)); + buf[0] = highc(buf[0]); + You("stop. %s is in the way!", buf); + return(TRUE); + } else if ((mtmp->mfrozen || (! mtmp->mcanmove) + || (mtmp->data->mmove == 0)) && rn2(6)) { + pline("%s doesn't seem to move!", Monnam(mtmp)); + return(TRUE); + } else return(FALSE); + } + } + + /* possibly set in attack_checks; + examined in known_hitum, called via hitum or hmonas below */ + override_confirmation = FALSE; + if (attack_checks(mtmp, uwep)) return(TRUE); + + if (Upolyd) { + /* certain "pacifist" monsters don't attack */ + if(noattacks(youmonst.data)) { + You("have no way to attack monsters physically."); + mtmp->mstrategy &= ~STRAT_WAITMASK; + goto atk_done; + } + } + + if(check_capacity("You cannot fight while so heavily loaded.")) + goto atk_done; + + if (u.twoweap && !can_twoweapon()) + untwoweapon(); + + if(unweapon) { + unweapon = FALSE; + if(flags.verbose) { + if(uwep) + You("begin bashing monsters with your %s.", + aobjnam(uwep, (char *)0)); + else if (!cantwield(youmonst.data)) + You("begin %sing monsters with your %s %s.", + Role_if(PM_MONK) ? "strik" : "bash", + uarmg ? "gloved" : "bare", /* Del Lamb */ + makeplural(body_part(HAND))); + } + } + exercise(A_STR, TRUE); /* you're exercising muscles */ + /* andrew@orca: prevent unlimited pick-axe attacks */ + u_wipe_engr(3); + + /* Is the "it died" check actually correct? */ + if(mdat->mlet == S_LEPRECHAUN && !mtmp->mfrozen && !mtmp->msleeping && + !mtmp->mconf && mtmp->mcansee && !rn2(7) && + (m_move(mtmp, 0) == 2 || /* it died */ + mtmp->mx != u.ux+u.dx || mtmp->my != u.uy+u.dy)) /* it moved */ + return(FALSE); + + tmp = find_roll_to_hit(mtmp); + if (Upolyd) + (void) hmonas(mtmp, tmp); + else + (void) hitum(mtmp, tmp, youmonst.data->mattk); + mtmp->mstrategy &= ~STRAT_WAITMASK; + +atk_done: + /* see comment in attack_checks() */ + /* we only need to check for this if we did an attack_checks() + * and it returned 0 (it's okay to attack), and the monster didn't + * evade. + */ + if (flags.forcefight && mtmp->mhp > 0 && !canspotmon(mtmp) && + !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) && + !(u.uswallow && mtmp == u.ustuck)) + map_invisible(u.ux+u.dx, u.uy+u.dy); + + return(TRUE); +} + +STATIC_OVL boolean +known_hitum(mon, mhit, uattk) /* returns TRUE if monster still lives */ +register struct monst *mon; +register int *mhit; +struct attack *uattk; +{ + register boolean malive = TRUE; + + if (override_confirmation) { + /* this may need to be generalized if weapons other than + Stormbringer acquire similar anti-social behavior... */ + if (flags.verbose) Your("bloodthirsty blade attacks!"); + } + + if(!*mhit) { + missum(mon, uattk); + } else { + int oldhp = mon->mhp, + x = u.ux + u.dx, y = u.uy + u.dy; + + /* KMH, conduct */ + if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) + u.uconduct.weaphit++; + + /* we hit the monster; be careful: it might die or + be knocked into a different location */ + notonhead = (mon->mx != x || mon->my != y); + malive = hmon(mon, uwep, 0); + /* this assumes that Stormbringer was uwep not uswapwep */ + if (malive && u.twoweap && !override_confirmation && + m_at(x, y) == mon) + malive = hmon(mon, uswapwep, 0); + if (malive) { + /* monster still alive */ + if(!rn2(25) && mon->mhp < mon->mhpmax/2 + && !(u.uswallow && mon == u.ustuck)) { + /* maybe should regurgitate if swallowed? */ + if(!rn2(3)) { + monflee(mon, rnd(100), FALSE, TRUE); + } else monflee(mon, 0, FALSE, TRUE); + + if(u.ustuck == mon && !u.uswallow && !sticks(youmonst.data)) + u.ustuck = 0; + } + /* Vorpal Blade hit converted to miss */ + /* could be headless monster or worm tail */ + if (mon->mhp == oldhp) { + *mhit = 0; + /* a miss does not break conduct */ + if (uwep && + (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))) + --u.uconduct.weaphit; + } + if (mon->wormno && *mhit) + cutworm(mon, x, y, uwep); + } + } + return(malive); +} + +STATIC_OVL boolean +hitum(mon, tmp, uattk) /* returns TRUE if monster still lives */ +struct monst *mon; +int tmp; +struct attack *uattk; +{ + boolean malive; + int mhit = (tmp > (dieroll = rnd(20)) || u.uswallow); + + if(tmp > dieroll) exercise(A_DEX, TRUE); + malive = known_hitum(mon, &mhit, uattk); + (void) passive(mon, mhit, malive, AT_WEAP); + return(malive); +} + +boolean /* general "damage monster" routine */ +hmon(mon, obj, thrown) /* return TRUE if mon still alive */ +struct monst *mon; +struct obj *obj; +int thrown; +{ + boolean result, anger_guards; + + anger_guards = (mon->mpeaceful && + (mon->ispriest || mon->isshk || + mon->data == &mons[PM_WATCHMAN] || + mon->data == &mons[PM_WATCH_CAPTAIN])); + result = hmon_hitmon(mon, obj, thrown); + if (mon->ispriest && !rn2(2)) ghod_hitsu(mon); + if (anger_guards) (void)angry_guards(!flags.soundok); + return result; +} + +/* guts of hmon() */ +STATIC_OVL boolean +hmon_hitmon(mon, obj, thrown) +struct monst *mon; +struct obj *obj; +int thrown; +{ + int tmp; + struct permonst *mdat = mon->data; + int barehand_silver_rings = 0; + /* The basic reason we need all these booleans is that we don't want + * a "hit" message when a monster dies, so we have to know how much + * damage it did _before_ outputting a hit message, but any messages + * associated with the damage don't come out until _after_ outputting + * a hit message. + */ + boolean hittxt = FALSE, destroyed = FALSE, already_killed = FALSE; + boolean get_dmg_bonus = TRUE; + boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE; + boolean silvermsg = FALSE, silverobj = FALSE; + boolean valid_weapon_attack = FALSE; + boolean unarmed = !uwep && !uarm && !uarms; +#ifdef STEED + int jousting = 0; +#endif + int wtype; + struct obj *monwep; + char yourbuf[BUFSZ]; + char unconventional[BUFSZ]; /* substituted for word "attack" in msg */ + char saved_oname[BUFSZ]; + + unconventional[0] = '\0'; + saved_oname[0] = '\0'; + + wakeup(mon); + if(!obj) { /* attack with bare hands */ + if (mdat == &mons[PM_SHADE]) + tmp = 0; + else if (martial_bonus()) + tmp = rnd(4); /* bonus for martial arts */ + else + tmp = rnd(2); + valid_weapon_attack = (tmp > 1); + /* blessed gloves give bonuses when fighting 'bare-handed' */ + if (uarmg && uarmg->blessed && (is_undead(mdat) || is_demon(mdat))) + tmp += rnd(4); + /* So do silver rings. Note: rings are worn under gloves, so you + * don't get both bonuses. + */ + if (!uarmg) { + if (uleft && objects[uleft->otyp].oc_material == SILVER) + barehand_silver_rings++; + if (uright && objects[uright->otyp].oc_material == SILVER) + barehand_silver_rings++; + if (barehand_silver_rings && hates_silver(mdat)) { + tmp += rnd(20); + silvermsg = TRUE; + } + } + } else { + Strcpy(saved_oname, cxname(obj)); + if(obj->oclass == WEAPON_CLASS || is_weptool(obj) || + obj->oclass == GEM_CLASS) { + + /* is it not a melee weapon? */ + if (/* if you strike with a bow... */ + is_launcher(obj) || + /* or strike with a missile in your hand... */ + (!thrown && (is_missile(obj) || is_ammo(obj))) || + /* or use a pole at short range and not mounted... */ + (!thrown && +#ifdef STEED + !u.usteed && +#endif + is_pole(obj)) || + /* or throw a missile without the proper bow... */ + (is_ammo(obj) && !ammo_and_launcher(obj, uwep))) { + /* then do only 1-2 points of damage */ + if (mdat == &mons[PM_SHADE] && obj->otyp != SILVER_ARROW) + tmp = 0; + else + tmp = rnd(2); + if (!thrown && obj == uwep && obj->otyp == BOOMERANG && + rnl(4) == 4-1) { + boolean more_than_1 = (obj->quan > 1L); + + pline("As you hit %s, %s%s %s breaks into splinters.", + mon_nam(mon), more_than_1 ? "one of " : "", + shk_your(yourbuf, obj), xname(obj)); + if (!more_than_1) uwepgone(); /* set unweapon */ + useup(obj); + if (!more_than_1) obj = (struct obj *) 0; + hittxt = TRUE; + if (mdat != &mons[PM_SHADE]) + tmp++; + } + } else { + tmp = dmgval(obj, mon); + /* a minimal hit doesn't exercise proficiency */ + valid_weapon_attack = (tmp > 1); + if (!valid_weapon_attack || mon == u.ustuck || u.twoweap) { + ; /* no special bonuses */ + } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd) { + You("strike %s from behind!", mon_nam(mon)); + tmp += rnd(u.ulevel); + hittxt = TRUE; + } else if (dieroll == 2 && obj == uwep && + obj->oclass == WEAPON_CLASS && + (bimanual(obj) || + (Role_if(PM_SAMURAI) && obj->otyp == KATANA && !uarms)) && + ((wtype = uwep_skill_type()) != P_NONE && + P_SKILL(wtype) >= P_SKILLED) && + ((monwep = MON_WEP(mon)) != 0 && + !is_flimsy(monwep) && + !obj_resists(monwep, + 50 + 15 * greatest_erosion(obj), 100))) { + /* + * 2.5% chance of shattering defender's weapon when + * using a two-handed weapon; less if uwep is rusted. + * [dieroll == 2 is most successful non-beheading or + * -bisecting hit, in case of special artifact damage; + * the percentage chance is (1/20)*(50/100).] + */ + setmnotwielded(mon,monwep); + MON_NOWEP(mon); + mon->weapon_check = NEED_WEAPON; + pline("%s %s %s from the force of your blow!", + s_suffix(Monnam(mon)), xname(monwep), + otense(monwep, "shatter")); + m_useup(mon, monwep); + /* If someone just shattered MY weapon, I'd flee! */ + if (rn2(4)) { + monflee(mon, d(2,3), TRUE, TRUE); + } + hittxt = TRUE; + } + + if (obj->oartifact && + artifact_hit(&youmonst, mon, obj, &tmp, dieroll)) { + if(mon->mhp <= 0) /* artifact killed monster */ + return FALSE; + if (tmp == 0) return TRUE; + hittxt = TRUE; + } + if (objects[obj->otyp].oc_material == SILVER + && hates_silver(mdat)) { + silvermsg = TRUE; silverobj = TRUE; + } +#ifdef STEED + if (u.usteed && !thrown && tmp > 0 && + weapon_type(obj) == P_LANCE && mon != u.ustuck) { + jousting = joust(mon, obj); + /* exercise skill even for minimal damage hits */ + if (jousting) valid_weapon_attack = TRUE; + } +#endif + if (thrown && (is_ammo(obj) || is_missile(obj))) { + if (ammo_and_launcher(obj, uwep)) { + /* Elves and Samurai do extra damage using + * their bows&arrows; they're highly trained. + */ + if (Role_if(PM_SAMURAI) && + obj->otyp == YA && uwep->otyp == YUMI) + tmp++; + else if (Race_if(PM_ELF) && + obj->otyp == ELVEN_ARROW && + uwep->otyp == ELVEN_BOW) + tmp++; + } + if(obj->opoisoned && is_poisonable(obj)) + ispoisoned = TRUE; + } + } + } else if(obj->oclass == POTION_CLASS) { + if (obj->quan > 1L) + obj = splitobj(obj, 1L); + else + setuwep((struct obj *)0); + freeinv(obj); + potionhit(mon, obj, TRUE); + if (mon->mhp <= 0) return FALSE; /* killed */ + hittxt = TRUE; + /* in case potion effect causes transformation */ + mdat = mon->data; + tmp = (mdat == &mons[PM_SHADE]) ? 0 : 1; + } else { + if (mdat == &mons[PM_SHADE] && !shade_aware(obj)) { + tmp = 0; + Strcpy(unconventional, cxname(obj)); + } else { + switch(obj->otyp) { + case BOULDER: /* 1d20 */ + case HEAVY_IRON_BALL: /* 1d25 */ + case IRON_CHAIN: /* 1d4+1 */ + tmp = dmgval(obj, mon); + break; + case MIRROR: + if (breaktest(obj)) { + You("break %s mirror. That's bad luck!", + shk_your(yourbuf, obj)); + change_luck(-2); + useup(obj); + obj = (struct obj *) 0; + unarmed = FALSE; /* avoid obj==0 confusion */ + get_dmg_bonus = FALSE; + hittxt = TRUE; + } + tmp = 1; + break; +#ifdef TOURIST + case EXPENSIVE_CAMERA: + You("succeed in destroying %s camera. Congratulations!", + shk_your(yourbuf, obj)); + useup(obj); + return(TRUE); + /*NOTREACHED*/ + break; +#endif + case CORPSE: /* fixed by polder@cs.vu.nl */ + if (touch_petrifies(&mons[obj->corpsenm])) { + static const char withwhat[] = "corpse"; + tmp = 1; + hittxt = TRUE; + You("hit %s with %s %s.", mon_nam(mon), + obj->dknown ? the(mons[obj->corpsenm].mname) : + an(mons[obj->corpsenm].mname), + (obj->quan > 1) ? makeplural(withwhat) : withwhat); + if (!munstone(mon, TRUE)) + minstapetrify(mon, TRUE); + if (resists_ston(mon)) break; + /* note: hp may be <= 0 even if munstoned==TRUE */ + return (boolean) (mon->mhp > 0); +#if 0 + } else if (touch_petrifies(mdat)) { + /* maybe turn the corpse into a statue? */ +#endif + } + tmp = (obj->corpsenm >= LOW_PM ? + mons[obj->corpsenm].msize : 0) + 1; + break; + case EGG: + { +#define useup_eggs(o) { if (thrown) obfree(o,(struct obj *)0); \ + else useupall(o); \ + o = (struct obj *)0; } /* now gone */ + long cnt = obj->quan; + + tmp = 1; /* nominal physical damage */ + get_dmg_bonus = FALSE; + hittxt = TRUE; /* message always given */ + /* egg is always either used up or transformed, so next + hand-to-hand attack should yield a "bashing" mesg */ + if (obj == uwep) unweapon = TRUE; + if (obj->spe && obj->corpsenm >= LOW_PM) { + if (obj->quan < 5) + change_luck((schar) -(obj->quan)); + else + change_luck(-5); + } + + if (touch_petrifies(&mons[obj->corpsenm])) { + /*learn_egg_type(obj->corpsenm);*/ + pline("Splat! You hit %s with %s %s egg%s!", + mon_nam(mon), + obj->known ? "the" : cnt > 1L ? "some" : "a", + obj->known ? mons[obj->corpsenm].mname : "petrifying", + plur(cnt)); + obj->known = 1; /* (not much point...) */ + useup_eggs(obj); + if (!munstone(mon, TRUE)) + minstapetrify(mon, TRUE); + if (resists_ston(mon)) break; + return (boolean) (mon->mhp > 0); + } else { /* ordinary egg(s) */ + const char *eggp = + (obj->corpsenm != NON_PM && obj->known) ? + the(mons[obj->corpsenm].mname) : + (cnt > 1L) ? "some" : "an"; + You("hit %s with %s egg%s.", + mon_nam(mon), eggp, plur(cnt)); + if (touch_petrifies(mdat) && !stale_egg(obj)) { + pline_The("egg%s %s alive any more...", + plur(cnt), + (cnt == 1L) ? "isn't" : "aren't"); + if (obj->timed) obj_stop_timers(obj); + obj->otyp = ROCK; + obj->oclass = GEM_CLASS; + obj->oartifact = 0; + obj->spe = 0; + obj->known = obj->dknown = obj->bknown = 0; + obj->owt = weight(obj); + if (thrown) place_object(obj, mon->mx, mon->my); + } else { + pline("Splat!"); + useup_eggs(obj); + exercise(A_WIS, FALSE); + } + } + break; +#undef useup_eggs + } + case CLOVE_OF_GARLIC: /* no effect against demons */ + if (is_undead(mdat)) { + monflee(mon, d(2, 4), FALSE, TRUE); + } + tmp = 1; + break; + case CREAM_PIE: + case BLINDING_VENOM: + mon->msleeping = 0; + if (can_blnd(&youmonst, mon, (uchar) + (obj->otyp == BLINDING_VENOM + ? AT_SPIT : AT_WEAP), obj)) { + if (Blind) { + pline(obj->otyp == CREAM_PIE ? + "Splat!" : "Splash!"); + } else if (obj->otyp == BLINDING_VENOM) { + pline_The("venom blinds %s%s!", mon_nam(mon), + mon->mcansee ? "" : " further"); + } else { + char *whom = mon_nam(mon); + char *what = The(xname(obj)); + if (!thrown && obj->quan > 1) + what = An(singular(obj, xname)); + /* note: s_suffix returns a modifiable buffer */ + if (haseyes(mdat) + && mdat != &mons[PM_FLOATING_EYE]) + whom = strcat(strcat(s_suffix(whom), " "), + mbodypart(mon, FACE)); + pline("%s %s over %s!", + what, vtense(what, "splash"), whom); + } + setmangry(mon); + mon->mcansee = 0; + tmp = rn1(25, 21); + if(((int) mon->mblinded + tmp) > 127) + mon->mblinded = 127; + else mon->mblinded += tmp; + } else { + pline(obj->otyp==CREAM_PIE ? "Splat!" : "Splash!"); + setmangry(mon); + } + if (thrown) obfree(obj, (struct obj *)0); + else useup(obj); + hittxt = TRUE; + get_dmg_bonus = FALSE; + tmp = 0; + break; + case ACID_VENOM: /* thrown (or spit) */ + if (resists_acid(mon)) { + Your("venom hits %s harmlessly.", + mon_nam(mon)); + tmp = 0; + } else { + Your("venom burns %s!", mon_nam(mon)); + tmp = dmgval(obj, mon); + } + if (thrown) obfree(obj, (struct obj *)0); + else useup(obj); + hittxt = TRUE; + get_dmg_bonus = FALSE; + break; + default: + /* non-weapons can damage because of their weight */ + /* (but not too much) */ + tmp = obj->owt/100; + if(tmp < 1) tmp = 1; + else tmp = rnd(tmp); + if(tmp > 6) tmp = 6; + /* + * Things like silver wands can arrive here so + * so we need another silver check. + */ + if (objects[obj->otyp].oc_material == SILVER + && hates_silver(mdat)) { + tmp += rnd(20); + silvermsg = TRUE; silverobj = TRUE; + } + } + } + } + } + + /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG) + * *OR* if attacking bare-handed!! */ + + if (get_dmg_bonus && tmp > 0) { + tmp += u.udaminc; + /* If you throw using a propellor, you don't get a strength + * bonus but you do get an increase-damage bonus. + */ + if(!thrown || !obj || !uwep || !ammo_and_launcher(obj, uwep)) + tmp += dbon(); + } + + if (valid_weapon_attack) { + struct obj *wep; + + /* to be valid a projectile must have had the correct projector */ + wep = PROJECTILE(obj) ? uwep : obj; + tmp += weapon_dam_bonus(wep); + /* [this assumes that `!thrown' implies wielded...] */ + wtype = thrown ? weapon_type(wep) : uwep_skill_type(); + use_skill(wtype, 1); + } + + if (ispoisoned) { + int nopoison = (10 - (obj->owt/10)); + if(nopoison < 2) nopoison = 2; + if Role_if(PM_SAMURAI) { + You("dishonorably use a poisoned weapon!"); + adjalign(-sgn(u.ualign.type)); + } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) { + You_feel("like an evil coward for using a poisoned weapon."); + adjalign(-1); + } + if (obj && !rn2(nopoison)) { + obj->opoisoned = FALSE; + Your("%s %s no longer poisoned.", xname(obj), + otense(obj, "are")); + } + if (resists_poison(mon)) + needpoismsg = TRUE; + else if (rn2(10)) + tmp += rnd(6); + else poiskilled = TRUE; + } + if (tmp < 1) { + /* make sure that negative damage adjustment can't result + in inadvertently boosting the victim's hit points */ + tmp = 0; + if (mdat == &mons[PM_SHADE]) { + if (!hittxt) { + const char *what = unconventional[0] ? unconventional : "attack"; + Your("%s %s harmlessly through %s.", + what, vtense(what, "pass"), + mon_nam(mon)); + hittxt = TRUE; + } + } else { + if (get_dmg_bonus) tmp = 1; + } + } + +#ifdef STEED + if (jousting) { + tmp += d(2, (obj == uwep) ? 10 : 2); /* [was in dmgval()] */ + You("joust %s%s", + mon_nam(mon), canseemon(mon) ? exclam(tmp) : "."); + if (jousting < 0) { + Your("%s shatters on impact!", xname(obj)); + /* (must be either primary or secondary weapon to get here) */ + u.twoweap = FALSE; /* untwoweapon() is too verbose here */ + if (obj == uwep) uwepgone(); /* set unweapon */ + /* minor side-effect: broken lance won't split puddings */ + useup(obj); + obj = 0; + } + /* avoid migrating a dead monster */ + if (mon->mhp > tmp) { + mhurtle(mon, u.dx, u.dy, 1); + mdat = mon->data; /* in case of a polymorph trap */ + if (DEADMONSTER(mon)) already_killed = TRUE; + } + hittxt = TRUE; + } else +#endif + + /* VERY small chance of stunning opponent if unarmed. */ + if (unarmed && tmp > 1 && !thrown && !obj && !Upolyd) { + if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) && + !bigmonst(mdat) && !thick_skinned(mdat)) { + if (canspotmon(mon)) + pline("%s %s from your powerful strike!", Monnam(mon), + makeplural(stagger(mon->data, "stagger"))); + /* avoid migrating a dead monster */ + if (mon->mhp > tmp) { + mhurtle(mon, u.dx, u.dy, 1); + mdat = mon->data; /* in case of a polymorph trap */ + if (DEADMONSTER(mon)) already_killed = TRUE; + } + hittxt = TRUE; + } + } + + if (!already_killed) mon->mhp -= tmp; + /* adjustments might have made tmp become less than what + a level draining artifact has already done to max HP */ + if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; + if (mon->mhp < 1) + destroyed = TRUE; + if (mon->mtame && (!mon->mflee || mon->mfleetim) && tmp > 0) { + abuse_dog(mon); + monflee(mon, 10 * rnd(tmp), FALSE, FALSE); + } + if((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING]) + && obj && obj == uwep + && objects[obj->otyp].oc_material == IRON + && mon->mhp > 1 && !thrown && !mon->mcan + /* && !destroyed -- guaranteed by mhp > 1 */ ) { + if (clone_mon(mon, 0, 0)) { + pline("%s divides as you hit it!", Monnam(mon)); + hittxt = TRUE; + } + } + + if (!hittxt && /*( thrown => obj exists )*/ + (!destroyed || (thrown && m_shot.n > 1 && m_shot.o == obj->otyp))) { + if (thrown) hit(mshot_xname(obj), mon, exclam(tmp)); + else if (!flags.verbose) You("hit it."); + else You("%s %s%s", Role_if(PM_BARBARIAN) ? "smite" : "hit", + mon_nam(mon), canseemon(mon) ? exclam(tmp) : "."); + } + + if (silvermsg) { + const char *fmt; + char *whom = mon_nam(mon); + char silverobjbuf[BUFSZ]; + + if (canspotmon(mon)) { + if (barehand_silver_rings == 1) + fmt = "Your silver ring sears %s!"; + else if (barehand_silver_rings == 2) + fmt = "Your silver rings sear %s!"; + else if (silverobj && saved_oname[0]) { + Sprintf(silverobjbuf, "Your %s%s %s %%s!", + strstri(saved_oname, "silver") ? + "" : "silver ", + saved_oname, vtense(saved_oname, "sear")); + fmt = silverobjbuf; + } else + fmt = "The silver sears %s!"; + } else { + *whom = highc(*whom); /* "it" -> "It" */ + fmt = "%s is seared!"; + } + /* note: s_suffix returns a modifiable buffer */ + if (!noncorporeal(mdat)) + whom = strcat(s_suffix(whom), " flesh"); + pline(fmt, whom); + } + + if (needpoismsg) + pline_The("poison doesn't seem to affect %s.", mon_nam(mon)); + if (poiskilled) { + pline_The("poison was deadly..."); + if (!already_killed) xkilled(mon, 0); + return FALSE; + } else if (destroyed) { + if (!already_killed) + killed(mon); /* takes care of most messages */ + } else if(u.umconf && !thrown) { + nohandglow(mon); + if (!mon->mconf && !resist(mon, SPBOOK_CLASS, 0, NOTELL)) { + mon->mconf = 1; + if (!mon->mstun && mon->mcanmove && !mon->msleeping && + canseemon(mon)) + pline("%s appears confused.", Monnam(mon)); + } + } + + return((boolean)(destroyed ? FALSE : TRUE)); +} + +STATIC_OVL boolean +shade_aware(obj) +struct obj *obj; +{ + if (!obj) return FALSE; + /* + * The things in this list either + * 1) affect shades. + * OR + * 2) are dealt with properly by other routines + * when it comes to shades. + */ + if (obj->otyp == BOULDER || obj->otyp == HEAVY_IRON_BALL + || obj->otyp == IRON_CHAIN /* dmgval handles those first three */ + || obj->otyp == MIRROR /* silver in the reflective surface */ + || obj->otyp == CLOVE_OF_GARLIC /* causes shades to flee */ + || objects[obj->otyp].oc_material == SILVER) + return TRUE; + return FALSE; +} + +/* check whether slippery clothing protects from hug or wrap attack */ +/* [currently assumes that you are the attacker] */ +STATIC_OVL boolean +m_slips_free(mdef, mattk) +struct monst *mdef; +struct attack *mattk; +{ + struct obj *obj; + + if (mattk->adtyp == AD_DRIN) { + /* intelligence drain attacks the head */ + obj = which_armor(mdef, W_ARMH); + } else { + /* grabbing attacks the body */ + obj = which_armor(mdef, W_ARMC); /* cloak */ + if (!obj) obj = which_armor(mdef, W_ARM); /* suit */ +#ifdef TOURIST + if (!obj) obj = which_armor(mdef, W_ARMU); /* shirt */ +#endif + } + + /* if your cloak/armor is greased, monster slips off; this + protection might fail (33% chance) when the armor is cursed */ + if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) && + (!obj->cursed || rn2(3))) { + You("%s %s %s %s!", + mattk->adtyp == AD_WRAP ? + "slip off of" : "grab, but cannot hold onto", + s_suffix(mon_nam(mdef)), + obj->greased ? "greased" : "slippery", + /* avoid "slippery slippery cloak" + for undiscovered oilskin cloak */ + (obj->greased || objects[obj->otyp].oc_name_known) ? + xname(obj) : cloak_simple_name(obj)); + + if (obj->greased && !rn2(2)) { + pline_The("grease wears off."); + obj->greased = 0; + } + return TRUE; + } + return FALSE; +} + +/* used when hitting a monster with a lance while mounted */ +STATIC_OVL int /* 1: joust hit; 0: ordinary hit; -1: joust but break lance */ +joust(mon, obj) +struct monst *mon; /* target */ +struct obj *obj; /* weapon */ +{ + int skill_rating, joust_dieroll; + + if (Fumbling || Stunned) return 0; + /* sanity check; lance must be wielded in order to joust */ + if (obj != uwep && (obj != uswapwep || !u.twoweap)) return 0; + + /* if using two weapons, use worse of lance and two-weapon skills */ + skill_rating = P_SKILL(weapon_type(obj)); /* lance skill */ + if (u.twoweap && P_SKILL(P_TWO_WEAPON_COMBAT) < skill_rating) + skill_rating = P_SKILL(P_TWO_WEAPON_COMBAT); + if (skill_rating == P_ISRESTRICTED) skill_rating = P_UNSKILLED; /* 0=>1 */ + + /* odds to joust are expert:80%, skilled:60%, basic:40%, unskilled:20% */ + if ((joust_dieroll = rn2(5)) < skill_rating) { + if (joust_dieroll == 0 && rnl(50) == (50-1) && + !unsolid(mon->data) && !obj_resists(obj, 0, 100)) + return -1; /* hit that breaks lance */ + return 1; /* successful joust */ + } + return 0; /* no joust bonus; revert to ordinary attack */ +} + +/* + * Send in a demon pet for the hero. Exercise wisdom. + * + * This function used to be inline to damageum(), but the Metrowerks compiler + * (DR4 and DR4.5) screws up with an internal error 5 "Expression Too Complex." + * Pulling it out makes it work. + */ +STATIC_OVL void +demonpet() +{ + int i; + struct permonst *pm; + struct monst *dtmp; + + pline("Some hell-p has arrived!"); + i = !rn2(6) ? ndemon(u.ualign.type) : NON_PM; + pm = i != NON_PM ? &mons[i] : youmonst.data; + if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0) + (void)tamedog(dtmp, (struct obj *)0); + exercise(A_WIS, TRUE); +} + +/* + * Player uses theft attack against monster. + * + * If the target is wearing body armor, take all of its possesions; + * otherwise, take one object. [Is this really the behavior we want?] + * + * This routine implicitly assumes that there is no way to be able to + * resist petfication (ie, be polymorphed into a xorn or golem) at the + * same time as being able to steal (poly'd into nymph or succubus). + * If that ever changes, the check for touching a cockatrice corpse + * will need to be smarter about whether to break out of the theft loop. + */ +STATIC_OVL void +steal_it(mdef, mattk) +struct monst *mdef; +struct attack *mattk; +{ + struct obj *otmp, *stealoid, **minvent_ptr; + long unwornmask; + + if (!mdef->minvent) return; /* nothing to take */ + + /* look for worn body armor */ + stealoid = (struct obj *)0; + if (could_seduce(&youmonst, mdef, mattk)) { + /* find armor, and move it to end of inventory in the process */ + minvent_ptr = &mdef->minvent; + while ((otmp = *minvent_ptr) != 0) + if (otmp->owornmask & W_ARM) { + if (stealoid) panic("steal_it: multiple worn suits"); + *minvent_ptr = otmp->nobj; /* take armor out of minvent */ + stealoid = otmp; + stealoid->nobj = (struct obj *)0; + } else { + minvent_ptr = &otmp->nobj; + } + *minvent_ptr = stealoid; /* put armor back into minvent */ + } + + if (stealoid) { /* we will be taking everything */ + if (gender(mdef) == (int) u.mfemale && + youmonst.data->mlet == S_NYMPH) + You("charm %s. She gladly hands over her possessions.", + mon_nam(mdef)); + else + You("seduce %s and %s starts to take off %s clothes.", + mon_nam(mdef), mhe(mdef), mhis(mdef)); + } + + while ((otmp = mdef->minvent) != 0) { + if (!Upolyd) break; /* no longer have ability to steal */ + /* take the object away from the monster */ + obj_extract_self(otmp); + if ((unwornmask = otmp->owornmask) != 0L) { + mdef->misc_worn_check &= ~unwornmask; + if (otmp->owornmask & W_WEP) { + setmnotwielded(mdef,otmp); + MON_NOWEP(mdef); + } + otmp->owornmask = 0L; + update_mon_intrinsics(mdef, otmp, FALSE, FALSE); + + if (otmp == stealoid) /* special message for final item */ + pline("%s finishes taking off %s suit.", + Monnam(mdef), mhis(mdef)); + } + /* give the object to the character */ + otmp = hold_another_object(otmp, "You snatched but dropped %s.", + doname(otmp), "You steal: "); + if (otmp->where != OBJ_INVENT) continue; + if (otmp->otyp == CORPSE && + touch_petrifies(&mons[otmp->corpsenm]) && !uarmg) { + char kbuf[BUFSZ]; + + Sprintf(kbuf, "stolen %s corpse", mons[otmp->corpsenm].mname); + instapetrify(kbuf); + break; /* stop the theft even if hero survives */ + } + /* more take-away handling, after theft message */ + if (unwornmask & W_WEP) { /* stole wielded weapon */ + possibly_unwield(mdef, FALSE); + } else if (unwornmask & W_ARMG) { /* stole worn gloves */ + mselftouch(mdef, (const char *)0, TRUE); + if (mdef->mhp <= 0) /* it's now a statue */ + return; /* can't continue stealing */ + } + + if (!stealoid) break; /* only taking one item */ + } +} + +int +damageum(mdef, mattk) +register struct monst *mdef; +register struct attack *mattk; +{ + register struct permonst *pd = mdef->data; + register int tmp = d((int)mattk->damn, (int)mattk->damd); + int armpro; + boolean negated; + + armpro = magic_negation(mdef); + /* since hero can't be cancelled, only defender's armor applies */ + negated = !((rn2(3) >= armpro) || !rn2(50)); + + if (is_demon(youmonst.data) && !rn2(13) && !uwep + && u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS + && u.umonnum != PM_BALROG) { + demonpet(); + return(0); + } + switch(mattk->adtyp) { + case AD_STUN: + if(!Blind) + pline("%s %s for a moment.", Monnam(mdef), + makeplural(stagger(mdef->data, "stagger"))); + mdef->mstun = 1; + goto physical; + case AD_LEGS: + /* if (u.ucancelled) { */ + /* tmp = 0; */ + /* break; */ + /* } */ + goto physical; + case AD_WERE: /* no special effect on monsters */ + case AD_HEAL: /* likewise */ + case AD_PHYS: + physical: + if(mattk->aatyp == AT_WEAP) { + if(uwep) tmp = 0; + } else if(mattk->aatyp == AT_KICK) { + if(thick_skinned(mdef->data)) tmp = 0; + if(mdef->data == &mons[PM_SHADE]) { + if (!(uarmf && uarmf->blessed)) { + impossible("bad shade attack function flow?"); + tmp = 0; + } else + tmp = rnd(4); /* bless damage */ + } + } + break; + case AD_FIRE: + if (negated) { + tmp = 0; + break; + } + if (!Blind) + pline("%s is %s!", Monnam(mdef), + on_fire(mdef->data, mattk)); + if (pd == &mons[PM_STRAW_GOLEM] || + pd == &mons[PM_PAPER_GOLEM]) { + if (!Blind) + pline("%s burns completely!", Monnam(mdef)); + xkilled(mdef,2); + tmp = 0; + break; + /* Don't return yet; keep hp<1 and tmp=0 for pet msg */ + } + tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE); + tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE); + if (resists_fire(mdef)) { + if (!Blind) + pline_The("fire doesn't heat %s!", mon_nam(mdef)); + golemeffects(mdef, AD_FIRE, tmp); + shieldeff(mdef->mx, mdef->my); + tmp = 0; + } + /* only potions damage resistant players in destroy_item */ + tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE); + break; + case AD_COLD: + if (negated) { + tmp = 0; + break; + } + if (!Blind) pline("%s is covered in frost!", Monnam(mdef)); + if (resists_cold(mdef)) { + shieldeff(mdef->mx, mdef->my); + if (!Blind) + pline_The("frost doesn't chill %s!", mon_nam(mdef)); + golemeffects(mdef, AD_COLD, tmp); + tmp = 0; + } + tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD); + break; + case AD_ELEC: + if (negated) { + tmp = 0; + break; + } + if (!Blind) pline("%s is zapped!", Monnam(mdef)); + tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC); + if (resists_elec(mdef)) { + if (!Blind) + pline_The("zap doesn't shock %s!", mon_nam(mdef)); + golemeffects(mdef, AD_ELEC, tmp); + shieldeff(mdef->mx, mdef->my); + tmp = 0; + } + /* only rings damage resistant players in destroy_item */ + tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC); + break; + case AD_ACID: + if (resists_acid(mdef)) tmp = 0; + break; + case AD_STON: + if (!munstone(mdef, TRUE)) + minstapetrify(mdef, TRUE); + tmp = 0; + break; +#ifdef SEDUCE + case AD_SSEX: +#endif + case AD_SEDU: + case AD_SITM: + steal_it(mdef, mattk); + tmp = 0; + break; + case AD_SGLD: +#ifndef GOLDOBJ + if (mdef->mgold) { + u.ugold += mdef->mgold; + mdef->mgold = 0; + Your("purse feels heavier."); + } +#else + /* This you as a leprechaun, so steal + real gold only, no lesser coins */ + { + struct obj *mongold = findgold(mdef->minvent); + if (mongold) { + obj_extract_self(mongold); + if (merge_choice(invent, mongold) || inv_cnt() < 52) { + addinv(mongold); + Your("purse feels heavier."); + } else { + You("grab %s's gold, but find no room in your knapsack.", mon_nam(mdef)); + dropy(mongold); + } + } + } +#endif + exercise(A_DEX, TRUE); + tmp = 0; + break; + case AD_TLPT: + if (tmp <= 0) tmp = 1; + if (!negated && tmp < mdef->mhp) { + char nambuf[BUFSZ]; + boolean u_saw_mon = canseemon(mdef) || + (u.uswallow && u.ustuck == mdef); + /* record the name before losing sight of monster */ + Strcpy(nambuf, Monnam(mdef)); + if (u_teleport_mon(mdef, FALSE) && + u_saw_mon && !canseemon(mdef)) + pline("%s suddenly disappears!", nambuf); + } + break; + case AD_BLND: + if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj*)0)) { + if(!Blind && mdef->mcansee) + pline("%s is blinded.", Monnam(mdef)); + mdef->mcansee = 0; + tmp += mdef->mblinded; + if (tmp > 127) tmp = 127; + mdef->mblinded = tmp; + } + tmp = 0; + break; + case AD_CURS: + if (night() && !rn2(10) && !mdef->mcan) { + if (mdef->data == &mons[PM_CLAY_GOLEM]) { + if (!Blind) + pline("Some writing vanishes from %s head!", + s_suffix(mon_nam(mdef))); + xkilled(mdef, 0); + /* Don't return yet; keep hp<1 and tmp=0 for pet msg */ + } else { + mdef->mcan = 1; + You("chuckle."); + } + } + tmp = 0; + break; + case AD_DRLI: + if (!negated && !rn2(3) && !resists_drli(mdef)) { + int xtmp = d(2,6); + pline("%s suddenly seems weaker!", Monnam(mdef)); + mdef->mhpmax -= xtmp; + if ((mdef->mhp -= xtmp) <= 0 || !mdef->m_lev) { + pline("%s dies!", Monnam(mdef)); + xkilled(mdef,0); + } else + mdef->m_lev--; + tmp = 0; + } + break; + case AD_RUST: + if (pd == &mons[PM_IRON_GOLEM]) { + pline("%s falls to pieces!", Monnam(mdef)); + xkilled(mdef,0); + } + hurtmarmor(mdef, AD_RUST); + tmp = 0; + break; + case AD_CORR: + hurtmarmor(mdef, AD_CORR); + tmp = 0; + break; + case AD_DCAY: + if (pd == &mons[PM_WOOD_GOLEM] || + pd == &mons[PM_LEATHER_GOLEM]) { + pline("%s falls to pieces!", Monnam(mdef)); + xkilled(mdef,0); + } + hurtmarmor(mdef, AD_DCAY); + tmp = 0; + break; + case AD_DRST: + case AD_DRDX: + case AD_DRCO: + if (!negated && !rn2(8)) { + Your("%s was poisoned!", mpoisons_subj(&youmonst, mattk)); + if (resists_poison(mdef)) + pline_The("poison doesn't seem to affect %s.", + mon_nam(mdef)); + else { + if (!rn2(10)) { + Your("poison was deadly..."); + tmp = mdef->mhp; + } else tmp += rn1(10,6); + } + } + break; + case AD_DRIN: + if (notonhead || !has_head(mdef->data)) { + pline("%s doesn't seem harmed.", Monnam(mdef)); + tmp = 0; + if (!Unchanging && mdef->data == &mons[PM_GREEN_SLIME]) { + if (!Slimed) { + You("suck in some slime and don't feel very well."); + Slimed = 10L; + } + } + break; + } + if (m_slips_free(mdef, mattk)) break; + + if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) { + pline("%s helmet blocks your attack to %s head.", + s_suffix(Monnam(mdef)), mhis(mdef)); + break; + } + + You("eat %s brain!", s_suffix(mon_nam(mdef))); + u.uconduct.food++; + if (touch_petrifies(mdef->data) && !Stone_resistance && !Stoned) { + Stoned = 5; + killer_format = KILLED_BY_AN; + delayed_killer = mdef->data->mname; + } + if (!vegan(mdef->data)) + u.uconduct.unvegan++; + if (!vegetarian(mdef->data)) + violated_vegetarian(); + if (mindless(mdef->data)) { + pline("%s doesn't notice.", Monnam(mdef)); + break; + } + tmp += rnd(10); + morehungry(-rnd(30)); /* cannot choke */ + if (ABASE(A_INT) < AMAX(A_INT)) { + ABASE(A_INT) += rnd(4); + if (ABASE(A_INT) > AMAX(A_INT)) + ABASE(A_INT) = AMAX(A_INT); + flags.botl = 1; + } + exercise(A_WIS, TRUE); + break; + case AD_STCK: + if (!negated && !sticks(mdef->data)) + u.ustuck = mdef; /* it's now stuck to you */ + break; + case AD_WRAP: + if (!sticks(mdef->data)) { + if (!u.ustuck && !rn2(10)) { + if (m_slips_free(mdef, mattk)) { + tmp = 0; + } else { + You("swing yourself around %s!", + mon_nam(mdef)); + u.ustuck = mdef; + } + } else if(u.ustuck == mdef) { + /* Monsters don't wear amulets of magical breathing */ + if (is_pool(u.ux,u.uy) && !is_swimmer(mdef->data) && + !amphibious(mdef->data)) { + You("drown %s...", mon_nam(mdef)); + tmp = mdef->mhp; + } else if(mattk->aatyp == AT_HUGS) + pline("%s is being crushed.", Monnam(mdef)); + } else { + tmp = 0; + if (flags.verbose) + You("brush against %s %s.", + s_suffix(mon_nam(mdef)), + mbodypart(mdef, LEG)); + } + } else tmp = 0; + break; + case AD_PLYS: + if (!negated && mdef->mcanmove && !rn2(3) && tmp < mdef->mhp) { + if (!Blind) pline("%s is frozen by you!", Monnam(mdef)); + mdef->mcanmove = 0; + mdef->mfrozen = rnd(10); + } + break; + case AD_SLEE: + if (!negated && !mdef->msleeping && + sleep_monst(mdef, rnd(10), -1)) { + if (!Blind) + pline("%s is put to sleep by you!", Monnam(mdef)); + slept_monst(mdef); + } + break; + case AD_SLIM: + if (negated) break; /* physical damage only */ + if (!rn2(4) && !flaming(mdef->data) && + mdef->data != &mons[PM_GREEN_SLIME]) { + You("turn %s into slime.", mon_nam(mdef)); + (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE); + tmp = 0; + } + break; + case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ + /* there's no msomearmor() function, so just do damage */ + /* if (negated) break; */ + break; + case AD_SLOW: + if (!negated && mdef->mspeed != MSLOW) { + unsigned int oldspeed = mdef->mspeed; + + mon_adjust_speed(mdef, -1, (struct obj *)0); + if (mdef->mspeed != oldspeed && canseemon(mdef)) + pline("%s slows down.", Monnam(mdef)); + } + break; + case AD_CONF: + if (!mdef->mconf) { + if (canseemon(mdef)) + pline("%s looks confused.", Monnam(mdef)); + mdef->mconf = 1; + } + break; + default: tmp = 0; + break; + } + + mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */ + if((mdef->mhp -= tmp) < 1) { + if (mdef->mtame && !cansee(mdef->mx,mdef->my)) { + You_feel("embarrassed for a moment."); + if (tmp) xkilled(mdef, 0); /* !tmp but hp<1: already killed */ + } else if (!flags.verbose) { + You("destroy it!"); + if (tmp) xkilled(mdef, 0); + } else + if (tmp) killed(mdef); + return(2); + } + return(1); +} + +STATIC_OVL int +explum(mdef, mattk) +register struct monst *mdef; +register struct attack *mattk; +{ + register int tmp = d((int)mattk->damn, (int)mattk->damd); + + You("explode!"); + switch(mattk->adtyp) { + boolean resistance; /* only for cold/fire/elec */ + + case AD_BLND: + if (!resists_blnd(mdef)) { + pline("%s is blinded by your flash of light!", Monnam(mdef)); + mdef->mblinded = min((int)mdef->mblinded + tmp, 127); + mdef->mcansee = 0; + } + break; + case AD_HALU: + if (haseyes(mdef->data) && mdef->mcansee) { + pline("%s is affected by your flash of light!", + Monnam(mdef)); + mdef->mconf = 1; + } + break; + case AD_COLD: + resistance = resists_cold(mdef); + goto common; + case AD_FIRE: + resistance = resists_fire(mdef); + goto common; + case AD_ELEC: + resistance = resists_elec(mdef); +common: + if (!resistance) { + pline("%s gets blasted!", Monnam(mdef)); + mdef->mhp -= tmp; + if (mdef->mhp <= 0) { + killed(mdef); + return(2); + } + } else { + shieldeff(mdef->mx, mdef->my); + if (is_golem(mdef->data)) + golemeffects(mdef, (int)mattk->adtyp, tmp); + else + pline_The("blast doesn't seem to affect %s.", + mon_nam(mdef)); + } + break; + default: + break; + } + return(1); +} + +STATIC_OVL void +start_engulf(mdef) +struct monst *mdef; +{ + if (!Invisible) { + map_location(u.ux, u.uy, TRUE); + tmp_at(DISP_ALWAYS, mon_to_glyph(&youmonst)); + tmp_at(mdef->mx, mdef->my); + } + You("engulf %s!", mon_nam(mdef)); + delay_output(); + delay_output(); +} + +STATIC_OVL void +end_engulf() +{ + if (!Invisible) { + tmp_at(DISP_END, 0); + newsym(u.ux, u.uy); + } +} + +STATIC_OVL int +gulpum(mdef,mattk) +register struct monst *mdef; +register struct attack *mattk; +{ + register int tmp; + register int dam = d((int)mattk->damn, (int)mattk->damd); + struct obj *otmp; + /* Not totally the same as for real monsters. Specifically, these + * don't take multiple moves. (It's just too hard, for too little + * result, to program monsters which attack from inside you, which + * would be necessary if done accurately.) Instead, we arbitrarily + * kill the monster immediately for AD_DGST and we regurgitate them + * after exactly 1 round of attack otherwise. -KAA + */ + + if(mdef->data->msize >= MZ_HUGE) return 0; + + if(u.uhunger < 1500 && !u.uswallow) { + for (otmp = mdef->minvent; otmp; otmp = otmp->nobj) + (void) snuff_lit(otmp); + + if(!touch_petrifies(mdef->data) || Stone_resistance) { +#ifdef LINT /* static char msgbuf[BUFSZ]; */ + char msgbuf[BUFSZ]; +#else + static char msgbuf[BUFSZ]; +#endif + start_engulf(mdef); + switch(mattk->adtyp) { + case AD_DGST: + /* eating a Rider or its corpse is fatal */ + if (is_rider(mdef->data)) { + pline("Unfortunately, digesting any of it is fatal."); + end_engulf(); + Sprintf(msgbuf, "unwisely tried to eat %s", + mdef->data->mname); + killer = msgbuf; + killer_format = NO_KILLER_PREFIX; + done(DIED); + return 0; /* lifesaved */ + } + + if (Slow_digestion) { + dam = 0; + break; + } + + /* KMH, conduct */ + u.uconduct.food++; + if (!vegan(mdef->data)) + u.uconduct.unvegan++; + if (!vegetarian(mdef->data)) + violated_vegetarian(); + + /* Use up amulet of life saving */ + if (!!(otmp = mlifesaver(mdef))) m_useup(mdef, otmp); + + newuhs(FALSE); + xkilled(mdef,2); + if (mdef->mhp > 0) { /* monster lifesaved */ + You("hurriedly regurgitate the sizzling in your %s.", + body_part(STOMACH)); + } else { + tmp = 1 + (mdef->data->cwt >> 8); + if (corpse_chance(mdef, &youmonst, TRUE) && + !(mvitals[monsndx(mdef->data)].mvflags & + G_NOCORPSE)) { + /* nutrition only if there can be a corpse */ + u.uhunger += (mdef->data->cnutrit+1) / 2; + } else tmp = 0; + Sprintf(msgbuf, "You totally digest %s.", + mon_nam(mdef)); + if (tmp != 0) { + /* setting afternmv = end_engulf is tempting, + * but will cause problems if the player is + * attacked (which uses his real location) or + * if his See_invisible wears off + */ + You("digest %s.", mon_nam(mdef)); + if (Slow_digestion) tmp *= 2; + nomul(-tmp); + nomovemsg = msgbuf; + } else pline("%s", msgbuf); + if (mdef->data == &mons[PM_GREEN_SLIME]) { + Sprintf(msgbuf, "%s isn't sitting well with you.", + The(mdef->data->mname)); + if (!Unchanging) { + Slimed = 5L; + flags.botl = 1; + } + } else + exercise(A_CON, TRUE); + } + end_engulf(); + return(2); + case AD_PHYS: + if (youmonst.data == &mons[PM_FOG_CLOUD]) { + pline("%s is laden with your moisture.", + Monnam(mdef)); + if (amphibious(mdef->data) && + !flaming(mdef->data)) { + dam = 0; + pline("%s seems unharmed.", Monnam(mdef)); + } + } else + pline("%s is pummeled with your debris!", + Monnam(mdef)); + break; + case AD_ACID: + pline("%s is covered with your goo!", Monnam(mdef)); + if (resists_acid(mdef)) { + pline("It seems harmless to %s.", mon_nam(mdef)); + dam = 0; + } + break; + case AD_BLND: + if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj *)0)) { + if (mdef->mcansee) + pline("%s can't see in there!", Monnam(mdef)); + mdef->mcansee = 0; + dam += mdef->mblinded; + if (dam > 127) dam = 127; + mdef->mblinded = dam; + } + dam = 0; + break; + case AD_ELEC: + if (rn2(2)) { + pline_The("air around %s crackles with electricity.", mon_nam(mdef)); + if (resists_elec(mdef)) { + pline("%s seems unhurt.", Monnam(mdef)); + dam = 0; + } + golemeffects(mdef,(int)mattk->adtyp,dam); + } else dam = 0; + break; + case AD_COLD: + if (rn2(2)) { + if (resists_cold(mdef)) { + pline("%s seems mildly chilly.", Monnam(mdef)); + dam = 0; + } else + pline("%s is freezing to death!",Monnam(mdef)); + golemeffects(mdef,(int)mattk->adtyp,dam); + } else dam = 0; + break; + case AD_FIRE: + if (rn2(2)) { + if (resists_fire(mdef)) { + pline("%s seems mildly hot.", Monnam(mdef)); + dam = 0; + } else + pline("%s is burning to a crisp!",Monnam(mdef)); + golemeffects(mdef,(int)mattk->adtyp,dam); + } else dam = 0; + break; + } + end_engulf(); + if ((mdef->mhp -= dam) <= 0) { + killed(mdef); + if (mdef->mhp <= 0) /* not lifesaved */ + return(2); + } + You("%s %s!", is_animal(youmonst.data) ? "regurgitate" + : "expel", mon_nam(mdef)); + if (Slow_digestion || is_animal(youmonst.data)) { + pline("Obviously, you didn't like %s taste.", + s_suffix(mon_nam(mdef))); + } + } else { + char kbuf[BUFSZ]; + + You("bite into %s.", mon_nam(mdef)); + Sprintf(kbuf, "swallowing %s whole", an(mdef->data->mname)); + instapetrify(kbuf); + } + } + return(0); +} + +void +missum(mdef,mattk) +register struct monst *mdef; +register struct attack *mattk; +{ + if (could_seduce(&youmonst, mdef, mattk)) + You("pretend to be friendly to %s.", mon_nam(mdef)); + else if(canspotmon(mdef) && flags.verbose) + You("miss %s.", mon_nam(mdef)); + else + You("miss it."); + if (!mdef->msleeping && mdef->mcanmove) + wakeup(mdef); +} + +STATIC_OVL boolean +hmonas(mon, tmp) /* attack monster as a monster. */ +register struct monst *mon; +register int tmp; +{ + struct attack *mattk, alt_attk; + int i, sum[NATTK], hittmp = 0; + int nsum = 0; + int dhit = 0; + + for(i = 0; i < NATTK; i++) { + + sum[i] = 0; + mattk = getmattk(youmonst.data, i, sum, &alt_attk); + switch(mattk->aatyp) { + case AT_WEAP: +use_weapon: + /* Certain monsters don't use weapons when encountered as enemies, + * but players who polymorph into them have hands or claws and thus + * should be able to use weapons. This shouldn't prohibit the use + * of most special abilities, either. + */ + /* Potential problem: if the monster gets multiple weapon attacks, + * we currently allow the player to get each of these as a weapon + * attack. Is this really desirable? + */ + if (uwep) { + hittmp = hitval(uwep, mon); + hittmp += weapon_hit_bonus(uwep); + tmp += hittmp; + } + dhit = (tmp > (dieroll = rnd(20)) || u.uswallow); + /* KMH -- Don't accumulate to-hit bonuses */ + if (uwep) tmp -= hittmp; + /* Enemy dead, before any special abilities used */ + if (!known_hitum(mon,&dhit,mattk)) { + sum[i] = 2; + break; + } else sum[i] = dhit; + /* might be a worm that gets cut in half */ + if (m_at(u.ux+u.dx, u.uy+u.dy) != mon) return((boolean)(nsum != 0)); + /* Do not print "You hit" message, since known_hitum + * already did it. + */ + if (dhit && mattk->adtyp != AD_SPEL + && mattk->adtyp != AD_PHYS) + sum[i] = damageum(mon,mattk); + break; + case AT_CLAW: + if (i==0 && uwep && !cantwield(youmonst.data)) goto use_weapon; +#ifdef SEDUCE + /* succubi/incubi are humanoid, but their _second_ + * attack is AT_CLAW, not their first... + */ + if (i==1 && uwep && (u.umonnum == PM_SUCCUBUS || + u.umonnum == PM_INCUBUS)) goto use_weapon; +#endif + case AT_KICK: + case AT_BITE: + case AT_STNG: + case AT_TUCH: + case AT_BUTT: + case AT_TENT: + if (i==0 && uwep && (youmonst.data->mlet==S_LICH)) goto use_weapon; + if ((dhit = (tmp > rnd(20) || u.uswallow)) != 0) { + int compat; + + if (!u.uswallow && + (compat=could_seduce(&youmonst, mon, mattk))) { + You("%s %s %s.", + mon->mcansee && haseyes(mon->data) + ? "smile at" : "talk to", + mon_nam(mon), + compat == 2 ? "engagingly":"seductively"); + /* doesn't anger it; no wakeup() */ + sum[i] = damageum(mon, mattk); + break; + } + wakeup(mon); + /* maybe this check should be in damageum()? */ + if (mon->data == &mons[PM_SHADE] && + !(mattk->aatyp == AT_KICK && + uarmf && uarmf->blessed)) { + Your("attack passes harmlessly through %s.", + mon_nam(mon)); + break; + } + if (mattk->aatyp == AT_KICK) + You("kick %s.", mon_nam(mon)); + else if (mattk->aatyp == AT_BITE) + You("bite %s.", mon_nam(mon)); + else if (mattk->aatyp == AT_STNG) + You("sting %s.", mon_nam(mon)); + else if (mattk->aatyp == AT_BUTT) + You("butt %s.", mon_nam(mon)); + else if (mattk->aatyp == AT_TUCH) + You("touch %s.", mon_nam(mon)); + else if (mattk->aatyp == AT_TENT) + Your("tentacles suck %s.", mon_nam(mon)); + else You("hit %s.", mon_nam(mon)); + sum[i] = damageum(mon, mattk); + } else + missum(mon, mattk); + break; + + case AT_HUGS: + /* automatic if prev two attacks succeed, or if + * already grabbed in a previous attack + */ + dhit = 1; + wakeup(mon); + if (mon->data == &mons[PM_SHADE]) + Your("hug passes harmlessly through %s.", + mon_nam(mon)); + else if (!sticks(mon->data) && !u.uswallow) { + if (mon==u.ustuck) { + pline("%s is being %s.", Monnam(mon), + u.umonnum==PM_ROPE_GOLEM ? "choked": + "crushed"); + sum[i] = damageum(mon, mattk); + } else if(i >= 2 && sum[i-1] && sum[i-2]) { + You("grab %s!", mon_nam(mon)); + u.ustuck = mon; + sum[i] = damageum(mon, mattk); + } + } + break; + + case AT_EXPL: /* automatic hit if next to */ + dhit = -1; + wakeup(mon); + sum[i] = explum(mon, mattk); + break; + + case AT_ENGL: + if((dhit = (tmp > rnd(20+i)))) { + wakeup(mon); + if (mon->data == &mons[PM_SHADE]) + Your("attempt to surround %s is harmless.", + mon_nam(mon)); + else { + sum[i]= gulpum(mon,mattk); + if (sum[i] == 2 && + (mon->data->mlet == S_ZOMBIE || + mon->data->mlet == S_MUMMY) && + rn2(5) && + !Sick_resistance) { + You_feel("%ssick.", + (Sick) ? "very " : ""); + mdamageu(mon, rnd(8)); + } + } + } else + missum(mon, mattk); + break; + + case AT_MAGC: + /* No check for uwep; if wielding nothing we want to + * do the normal 1-2 points bare hand damage... + */ + if (i==0 && (youmonst.data->mlet==S_KOBOLD + || youmonst.data->mlet==S_ORC + || youmonst.data->mlet==S_GNOME + )) goto use_weapon; + + case AT_NONE: + case AT_BOOM: + continue; + /* Not break--avoid passive attacks from enemy */ + + case AT_BREA: + case AT_SPIT: + case AT_GAZE: /* all done using #monster command */ + dhit = 0; + break; + + default: /* Strange... */ + impossible("strange attack of yours (%d)", + mattk->aatyp); + } + if (dhit == -1) { + u.mh = -1; /* dead in the current form */ + rehumanize(); + } + if (sum[i] == 2) + return((boolean)passive(mon, 1, 0, mattk->aatyp)); + /* defender dead */ + else { + (void) passive(mon, sum[i], 1, mattk->aatyp); + nsum |= sum[i]; + } + if (!Upolyd) + break; /* No extra attacks if no longer a monster */ + if (multi < 0) + break; /* If paralyzed while attacking, i.e. floating eye */ + } + return((boolean)(nsum != 0)); +} + +/* Special (passive) attacks on you by monsters done here. */ + +int +passive(mon, mhit, malive, aatyp) +register struct monst *mon; +register boolean mhit; +register int malive; +uchar aatyp; +{ + register struct permonst *ptr = mon->data; + register int i, tmp; + + for(i = 0; ; i++) { + if(i >= NATTK) return(malive | mhit); /* no passive attacks */ + if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */ + } + /* Note: tmp not always used */ + if (ptr->mattk[i].damn) + tmp = d((int)ptr->mattk[i].damn, (int)ptr->mattk[i].damd); + else if(ptr->mattk[i].damd) + tmp = d((int)mon->m_lev+1, (int)ptr->mattk[i].damd); + else + tmp = 0; + +/* These affect you even if they just died */ + + switch(ptr->mattk[i].adtyp) { + + case AD_ACID: + if(mhit && rn2(2)) { + if (Blind || !flags.verbose) You("are splashed!"); + else You("are splashed by %s acid!", + s_suffix(mon_nam(mon))); + + if (!Acid_resistance) + mdamageu(mon, tmp); + if(!rn2(30)) erode_armor(&youmonst, TRUE); + } + if (mhit) { + if (aatyp == AT_KICK) { + if (uarmf && !rn2(6)) + (void)rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst); + } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || + aatyp == AT_MAGC || aatyp == AT_TUCH) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + exercise(A_STR, FALSE); + break; + case AD_STON: + if (mhit) { /* successful attack */ + long protector = attk_protection((int)aatyp); + + /* hero using monsters' AT_MAGC attack is hitting hand to + hand rather than casting a spell */ + if (aatyp == AT_MAGC) protector = W_ARMG; + + if (protector == 0L || /* no protection */ + (protector == W_ARMG && !uarmg && !uwep) || + (protector == W_ARMF && !uarmf) || + (protector == W_ARMH && !uarmh) || + (protector == (W_ARMC|W_ARMG) && (!uarmc || !uarmg))) { + if (!Stone_resistance && + !(poly_when_stoned(youmonst.data) && + polymon(PM_STONE_GOLEM))) { + You("turn to stone..."); + done_in_by(mon); + return 2; + } + } + } + break; + case AD_RUST: + if(mhit && !mon->mcan) { + if (aatyp == AT_KICK) { + if (uarmf) + (void)rust_dmg(uarmf, xname(uarmf), 1, TRUE, &youmonst); + } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || + aatyp == AT_MAGC || aatyp == AT_TUCH) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + break; + case AD_CORR: + if(mhit && !mon->mcan) { + if (aatyp == AT_KICK) { + if (uarmf) + (void)rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst); + } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || + aatyp == AT_MAGC || aatyp == AT_TUCH) + passive_obj(mon, (struct obj*)0, &(ptr->mattk[i])); + } + break; + case AD_MAGM: + /* wrath of gods for attacking Oracle */ + if(Antimagic) { + shieldeff(u.ux, u.uy); + pline("A hail of magic missiles narrowly misses you!"); + } else { + You("are hit by magic missiles appearing from thin air!"); + mdamageu(mon, tmp); + } + break; + case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */ + if (mhit) { + struct obj *obj = (struct obj *)0; + + if (aatyp == AT_KICK) { + obj = uarmf; + if (!obj) break; + } else if (aatyp == AT_BITE || aatyp == AT_BUTT || + (aatyp >= AT_STNG && aatyp < AT_WEAP)) { + break; /* no object involved */ + } + passive_obj(mon, obj, &(ptr->mattk[i])); + } + break; + default: + break; + } + +/* These only affect you if they still live */ + + if(malive && !mon->mcan && rn2(3)) { + + switch(ptr->mattk[i].adtyp) { + + case AD_PLYS: + if(ptr == &mons[PM_FLOATING_EYE]) { + if (!canseemon(mon)) { + break; + } + if(mon->mcansee) { + if (ureflects("%s gaze is reflected by your %s.", + s_suffix(Monnam(mon)))) + ; + else if (Free_action) + You("momentarily stiffen under %s gaze!", + s_suffix(mon_nam(mon))); + else { + You("are frozen by %s gaze!", + s_suffix(mon_nam(mon))); + nomul((ACURR(A_WIS) > 12 || rn2(4)) ? -tmp : -127); + } + } else { + pline("%s cannot defend itself.", + Adjmonnam(mon,"blind")); + if(!rn2(500)) change_luck(-1); + } + } else if (Free_action) { + You("momentarily stiffen."); + } else { /* gelatinous cube */ + You("are frozen by %s!", mon_nam(mon)); + nomovemsg = 0; /* default: "you can move again" */ + nomul(-tmp); + exercise(A_DEX, FALSE); + } + break; + case AD_COLD: /* brown mold or blue jelly */ + if(monnear(mon, u.ux, u.uy)) { + if(Cold_resistance) { + shieldeff(u.ux, u.uy); + You_feel("a mild chill."); + ugolemeffects(AD_COLD, tmp); + break; + } + You("are suddenly very cold!"); + mdamageu(mon, tmp); + /* monster gets stronger with your heat! */ + mon->mhp += tmp / 2; + if (mon->mhpmax < mon->mhp) mon->mhpmax = mon->mhp; + /* at a certain point, the monster will reproduce! */ + if(mon->mhpmax > ((int) (mon->m_lev+1) * 8)) + (void)split_mon(mon, &youmonst); + } + break; + case AD_STUN: /* specifically yellow mold */ + if(!Stunned) + make_stunned((long)tmp, TRUE); + break; + case AD_FIRE: + if(monnear(mon, u.ux, u.uy)) { + if(Fire_resistance) { + shieldeff(u.ux, u.uy); + You_feel("mildly warm."); + ugolemeffects(AD_FIRE, tmp); + break; + } + You("are suddenly very hot!"); + mdamageu(mon, tmp); + } + break; + case AD_ELEC: + if(Shock_resistance) { + shieldeff(u.ux, u.uy); + You_feel("a mild tingle."); + ugolemeffects(AD_ELEC, tmp); + break; + } + You("are jolted with electricity!"); + mdamageu(mon, tmp); + break; + default: + break; + } + } + return(malive | mhit); +} + +/* + * Special (passive) attacks on an attacking object by monsters done here. + * Assumes the attack was successful. + */ +void +passive_obj(mon, obj, mattk) +register struct monst *mon; +register struct obj *obj; /* null means pick uwep, uswapwep or uarmg */ +struct attack *mattk; /* null means we find one internally */ +{ + register struct permonst *ptr = mon->data; + register int i; + + /* if caller hasn't specified an object, use uwep, uswapwep or uarmg */ + if (!obj) { + obj = (u.twoweap && uswapwep && !rn2(2)) ? uswapwep : uwep; + if (!obj && mattk->adtyp == AD_ENCH) + obj = uarmg; /* no weapon? then must be gloves */ + if (!obj) return; /* no object to affect */ + } + + /* if caller hasn't specified an attack, find one */ + if (!mattk) { + for(i = 0; ; i++) { + if(i >= NATTK) return; /* no passive attacks */ + if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */ + } + mattk = &(ptr->mattk[i]); + } + + switch(mattk->adtyp) { + + case AD_ACID: + if(!rn2(6)) { + erode_obj(obj, TRUE, FALSE); + } + break; + case AD_RUST: + if(!mon->mcan) { + erode_obj(obj, FALSE, FALSE); + } + break; + case AD_CORR: + if(!mon->mcan) { + erode_obj(obj, TRUE, FALSE); + } + break; + case AD_ENCH: + if (!mon->mcan) { + if (drain_item(obj) && carried(obj) && + (obj->known || obj->oclass == ARMOR_CLASS)) { + Your("%s less effective.", aobjnam(obj, "seem")); + } + break; + } + default: + break; + } + + if (carried(obj)) update_inventory(); +} + +/* Note: caller must ascertain mtmp is mimicking... */ +void +stumble_onto_mimic(mtmp) +struct monst *mtmp; +{ + const char *fmt = "Wait! That's %s!", + *generic = "a monster", + *what = 0; + + if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK)) + u.ustuck = mtmp; + + if (Blind) { + if (!Blind_telepat) + what = generic; /* with default fmt */ + else if (mtmp->m_ap_type == M_AP_MONSTER) + what = a_monnam(mtmp); /* differs from what was sensed */ + } else { + int glyph = levl[u.ux+u.dx][u.uy+u.dy].glyph; + + if (glyph_is_cmap(glyph) && + (glyph_to_cmap(glyph) == S_hcdoor || + glyph_to_cmap(glyph) == S_vcdoor)) + fmt = "The door actually was %s!"; + else if (glyph_is_object(glyph) && + glyph_to_obj(glyph) == GOLD_PIECE) + fmt = "That gold was %s!"; + + /* cloned Wiz starts out mimicking some other monster and + might make himself invisible before being revealed */ + if (mtmp->minvis && !See_invisible) + what = generic; + else + what = a_monnam(mtmp); + } + if (what) pline(fmt, what); + + wakeup(mtmp); /* clears mimicking */ +} + +STATIC_OVL void +nohandglow(mon) +struct monst *mon; +{ + char *hands=makeplural(body_part(HAND)); + + if (!u.umconf || mon->mconf) return; + if (u.umconf == 1) { + if (Blind) + Your("%s stop tingling.", hands); + else + Your("%s stop glowing %s.", hands, hcolor(NH_RED)); + } else { + if (Blind) + pline_The("tingling in your %s lessens.", hands); + else + Your("%s no longer glow so brightly %s.", hands, + hcolor(NH_RED)); + } + u.umconf--; +} + +int +flash_hits_mon(mtmp, otmp) +struct monst *mtmp; +struct obj *otmp; /* source of flash */ +{ + int tmp, amt, res = 0, useeit = canseemon(mtmp); + + if (mtmp->msleeping) { + mtmp->msleeping = 0; + if (useeit) { + pline_The("flash awakens %s.", mon_nam(mtmp)); + res = 1; + } + } else if (mtmp->data->mlet != S_LIGHT) { + if (!resists_blnd(mtmp)) { + tmp = dist2(otmp->ox, otmp->oy, mtmp->mx, mtmp->my); + if (useeit) { + pline("%s is blinded by the flash!", Monnam(mtmp)); + res = 1; + } + if (mtmp->data == &mons[PM_GREMLIN]) { + /* Rule #1: Keep them out of the light. */ + amt = otmp->otyp == WAN_LIGHT ? d(1 + otmp->spe, 4) : + rn2(min(mtmp->mhp,4)); + pline("%s %s!", Monnam(mtmp), amt > mtmp->mhp / 2 ? + "wails in agony" : "cries out in pain"); + if ((mtmp->mhp -= amt) <= 0) { + if (flags.mon_moving) + monkilled(mtmp, (char *)0, AD_BLND); + else + killed(mtmp); + } else if (cansee(mtmp->mx,mtmp->my) && !canspotmon(mtmp)){ + map_invisible(mtmp->mx, mtmp->my); + } + } + if (mtmp->mhp > 0) { + if (!flags.mon_moving) setmangry(mtmp); + if (tmp < 9 && !mtmp->isshk && rn2(4)) { + if (rn2(4)) + monflee(mtmp, rnd(100), FALSE, TRUE); + else + monflee(mtmp, 0, FALSE, TRUE); + } + mtmp->mcansee = 0; + mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50/tmp); + } + } + } + return res; +} + +/*uhitm.c*/ diff --git a/src/vault.c b/src/vault.c new file mode 100644 index 0000000..8f8b27e --- /dev/null +++ b/src/vault.c @@ -0,0 +1,826 @@ +/* SCCS Id: @(#)vault.c 3.4 2003/01/15 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "vault.h" + +STATIC_DCL struct monst *NDECL(findgd); + +#define g_monnam(mtmp) \ + x_monnam(mtmp, ARTICLE_NONE, (char *)0, SUPPRESS_IT, FALSE) + +#ifdef OVLB + +STATIC_DCL boolean FDECL(clear_fcorr, (struct monst *,BOOLEAN_P)); +STATIC_DCL void FDECL(restfakecorr,(struct monst *)); +STATIC_DCL boolean FDECL(in_fcorridor, (struct monst *,int,int)); +STATIC_DCL void FDECL(move_gold,(struct obj *,int)); +STATIC_DCL void FDECL(wallify_vault,(struct monst *)); + +STATIC_OVL boolean +clear_fcorr(grd, forceshow) +register struct monst *grd; +register boolean forceshow; +{ + register int fcx, fcy, fcbeg; + register struct monst *mtmp; + + if (!on_level(&(EGD(grd)->gdlevel), &u.uz)) return TRUE; + + while((fcbeg = EGD(grd)->fcbeg) < EGD(grd)->fcend) { + fcx = EGD(grd)->fakecorr[fcbeg].fx; + fcy = EGD(grd)->fakecorr[fcbeg].fy; + if((grd->mhp <= 0 || !in_fcorridor(grd, u.ux, u.uy)) && + EGD(grd)->gddone) + forceshow = TRUE; + if((u.ux == fcx && u.uy == fcy && grd->mhp > 0) + || (!forceshow && couldsee(fcx,fcy)) + || (Punished && !carried(uball) + && uball->ox == fcx && uball->oy == fcy)) + return FALSE; + + if ((mtmp = m_at(fcx,fcy)) != 0) { + if(mtmp->isgd) return(FALSE); + else if(!in_fcorridor(grd, u.ux, u.uy)) { + if(mtmp->mtame) yelp(mtmp); + (void) rloc(mtmp, FALSE); + } + } + levl[fcx][fcy].typ = EGD(grd)->fakecorr[fcbeg].ftyp; + map_location(fcx, fcy, 1); /* bypass vision */ + if(!ACCESSIBLE(levl[fcx][fcy].typ)) block_point(fcx,fcy); + EGD(grd)->fcbeg++; + } + if(grd->mhp <= 0) { + pline_The("corridor disappears."); + if(IS_ROCK(levl[u.ux][u.uy].typ)) You("are encased in rock."); + } + return(TRUE); +} + +STATIC_OVL void +restfakecorr(grd) +register struct monst *grd; +{ + /* it seems you left the corridor - let the guard disappear */ + if(clear_fcorr(grd, FALSE)) mongone(grd); +} + +boolean +grddead(grd) /* called in mon.c */ +register struct monst *grd; +{ + register boolean dispose = clear_fcorr(grd, TRUE); + + if(!dispose) { + /* see comment by newpos in gd_move() */ + remove_monster(grd->mx, grd->my); + newsym(grd->mx, grd->my); + place_monster(grd, 0, 0); + EGD(grd)->ogx = grd->mx; + EGD(grd)->ogy = grd->my; + dispose = clear_fcorr(grd, TRUE); + } + return(dispose); +} + +STATIC_OVL boolean +in_fcorridor(grd, x, y) +register struct monst *grd; +int x, y; +{ + register int fci; + + for(fci = EGD(grd)->fcbeg; fci < EGD(grd)->fcend; fci++) + if(x == EGD(grd)->fakecorr[fci].fx && + y == EGD(grd)->fakecorr[fci].fy) + return(TRUE); + return(FALSE); +} + +STATIC_OVL +struct monst * +findgd() +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(mtmp->isgd && !DEADMONSTER(mtmp) && on_level(&(EGD(mtmp)->gdlevel), &u.uz)) + return(mtmp); + return((struct monst *)0); +} + +#endif /* OVLB */ +#ifdef OVL0 + +char +vault_occupied(array) +char *array; +{ + register char *ptr; + + for (ptr = array; *ptr; ptr++) + if (rooms[*ptr - ROOMOFFSET].rtype == VAULT) + return(*ptr); + return('\0'); +} + +void +invault() +{ +#ifdef BSD_43_BUG + int dummy; /* hack to avoid schain botch */ +#endif + struct monst *guard; + int trycount, vaultroom = (int)vault_occupied(u.urooms); + + if(!vaultroom) { + u.uinvault = 0; + return; + } + + vaultroom -= ROOMOFFSET; + + guard = findgd(); + if(++u.uinvault % 30 == 0 && !guard) { /* if time ok and no guard now. */ + char buf[BUFSZ]; + register int x, y, dd, gx, gy; + int lx = 0, ly = 0; +#ifdef GOLDOBJ + long umoney; +#endif + /* first find the goal for the guard */ + for(dd = 2; (dd < ROWNO || dd < COLNO); dd++) { + for(y = u.uy-dd; y <= u.uy+dd; ly = y, y++) { + if(y < 0 || y > ROWNO-1) continue; + for(x = u.ux-dd; x <= u.ux+dd; lx = x, x++) { + if(y != u.uy-dd && y != u.uy+dd && x != u.ux-dd) + x = u.ux+dd; + if(x < 1 || x > COLNO-1) continue; + if(levl[x][y].typ == CORR) { + if(x < u.ux) lx = x + 1; + else if(x > u.ux) lx = x - 1; + else lx = x; + if(y < u.uy) ly = y + 1; + else if(y > u.uy) ly = y - 1; + else ly = y; + if(levl[lx][ly].typ != STONE && levl[lx][ly].typ != CORR) + goto incr_radius; + goto fnd; + } + } + } +incr_radius: ; + } + impossible("Not a single corridor on this level??"); + tele(); + return; +fnd: + gx = x; gy = y; + + /* next find a good place for a door in the wall */ + x = u.ux; y = u.uy; + if(levl[x][y].typ != ROOM) { /* player dug a door and is in it */ + if(levl[x+1][y].typ == ROOM) x = x + 1; + else if(levl[x][y+1].typ == ROOM) y = y + 1; + else if(levl[x-1][y].typ == ROOM) x = x - 1; + else if(levl[x][y-1].typ == ROOM) y = y - 1; + else if(levl[x+1][y+1].typ == ROOM) { + x = x + 1; + y = y + 1; + } else if (levl[x-1][y-1].typ == ROOM) { + x = x - 1; + y = y - 1; + } else if (levl[x+1][y-1].typ == ROOM) { + x = x + 1; + y = y - 1; + } else if (levl[x-1][y+1].typ == ROOM) { + x = x - 1; + y = y + 1; + } + } + while(levl[x][y].typ == ROOM) { + register int dx,dy; + + dx = (gx > x) ? 1 : (gx < x) ? -1 : 0; + dy = (gy > y) ? 1 : (gy < y) ? -1 : 0; + if(abs(gx-x) >= abs(gy-y)) + x += dx; + else + y += dy; + } + if(x == u.ux && y == u.uy) { + if(levl[x+1][y].typ == HWALL || levl[x+1][y].typ == DOOR) + x = x + 1; + else if(levl[x-1][y].typ == HWALL || levl[x-1][y].typ == DOOR) + x = x - 1; + else if(levl[x][y+1].typ == VWALL || levl[x][y+1].typ == DOOR) + y = y + 1; + else if(levl[x][y-1].typ == VWALL || levl[x][y-1].typ == DOOR) + y = y - 1; + else return; + } + + /* make something interesting happen */ + if(!(guard = makemon(&mons[PM_GUARD], x, y, NO_MM_FLAGS))) return; + guard->isgd = 1; + guard->mpeaceful = 1; + set_malign(guard); + EGD(guard)->gddone = 0; + EGD(guard)->ogx = x; + EGD(guard)->ogy = y; + assign_level(&(EGD(guard)->gdlevel), &u.uz); + EGD(guard)->vroom = vaultroom; + EGD(guard)->warncnt = 0; + + reset_faint(); /* if fainted - wake up */ + if (canspotmon(guard)) + pline("Suddenly one of the Vault's %s enters!", + makeplural(g_monnam(guard))); + else + pline("Someone else has entered the Vault."); + newsym(guard->mx,guard->my); + if (youmonst.m_ap_type == M_AP_OBJECT || u.uundetected) { + if (youmonst.m_ap_type == M_AP_OBJECT && + youmonst.mappearance != GOLD_PIECE) + verbalize("Hey! Who left that %s in here?", mimic_obj_name(&youmonst)); + /* You're mimicking some object or you're hidden. */ + pline("Puzzled, %s turns around and leaves.", mhe(guard)); + mongone(guard); + return; + } + if (Strangled || is_silent(youmonst.data) || multi < 0) { + /* [we ought to record whether this this message has already + been given in order to vary it upon repeat visits, but + discarding the monster and its egd data renders that hard] */ + verbalize("I'll be back when you're ready to speak to me!"); + mongone(guard); + return; + } + + stop_occupation(); /* if occupied, stop it *now* */ + if (multi > 0) { nomul(0); unmul((char *)0); } + trycount = 5; + do { + getlin("\"Hello stranger, who are you?\" -", buf); + (void) mungspaces(buf); + } while (!letter(buf[0]) && --trycount > 0); + + if (u.ualign.type == A_LAWFUL && + /* ignore trailing text, in case player includes character's rank */ + strncmpi(buf, plname, (int) strlen(plname)) != 0) { + adjalign(-1); /* Liar! */ + } + + if (!strcmpi(buf, "Croesus") || !strcmpi(buf, "Kroisos") +#ifdef TOURIST + || !strcmpi(buf, "Creosote") +#endif + ) { + if (!mvitals[PM_CROESUS].died) { + verbalize("Oh, yes, of course. Sorry to have disturbed you."); + mongone(guard); + } else { + setmangry(guard); + verbalize("Back from the dead, are you? I'll remedy that!"); + /* don't want guard to waste next turn wielding a weapon */ + if (!MON_WEP(guard)) { + guard->weapon_check = NEED_HTH_WEAPON; + (void) mon_wield_item(guard); + } + } + return; + } + verbalize("I don't know you."); +#ifndef GOLDOBJ + if (!u.ugold && !hidden_gold()) + verbalize("Please follow me."); + else { + if (!u.ugold) + verbalize("You have hidden gold."); + verbalize("Most likely all your gold was stolen from this vault."); + verbalize("Please drop that gold and follow me."); + } +#else + umoney = money_cnt(invent); + if (!umoney && !hidden_gold()) + verbalize("Please follow me."); + else { + if (!umoney) + verbalize("You have hidden money."); + verbalize("Most likely all your money was stolen from this vault."); + verbalize("Please drop that money and follow me."); + } +#endif + EGD(guard)->gdx = gx; + EGD(guard)->gdy = gy; + EGD(guard)->fcbeg = 0; + EGD(guard)->fakecorr[0].fx = x; + EGD(guard)->fakecorr[0].fy = y; + if(IS_WALL(levl[x][y].typ)) + EGD(guard)->fakecorr[0].ftyp = levl[x][y].typ; + else { /* the initial guard location is a dug door */ + int vlt = EGD(guard)->vroom; + xchar lowx = rooms[vlt].lx, hix = rooms[vlt].hx; + xchar lowy = rooms[vlt].ly, hiy = rooms[vlt].hy; + + if(x == lowx-1 && y == lowy-1) + EGD(guard)->fakecorr[0].ftyp = TLCORNER; + else if(x == hix+1 && y == lowy-1) + EGD(guard)->fakecorr[0].ftyp = TRCORNER; + else if(x == lowx-1 && y == hiy+1) + EGD(guard)->fakecorr[0].ftyp = BLCORNER; + else if(x == hix+1 && y == hiy+1) + EGD(guard)->fakecorr[0].ftyp = BRCORNER; + else if(y == lowy-1 || y == hiy+1) + EGD(guard)->fakecorr[0].ftyp = HWALL; + else if(x == lowx-1 || x == hix+1) + EGD(guard)->fakecorr[0].ftyp = VWALL; + } + levl[x][y].typ = DOOR; + levl[x][y].doormask = D_NODOOR; + unblock_point(x, y); /* doesn't block light */ + EGD(guard)->fcend = 1; + EGD(guard)->warncnt = 1; + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +STATIC_OVL void +move_gold(gold, vroom) +struct obj *gold; +int vroom; +{ + xchar nx, ny; + + remove_object(gold); + newsym(gold->ox, gold->oy); + nx = rooms[vroom].lx + rn2(2); + ny = rooms[vroom].ly + rn2(2); + place_object(gold, nx, ny); + stackobj(gold); + newsym(nx,ny); +} + +STATIC_OVL void +wallify_vault(grd) +struct monst *grd; +{ + int x, y, typ; + int vlt = EGD(grd)->vroom; + char tmp_viz; + xchar lox = rooms[vlt].lx - 1, hix = rooms[vlt].hx + 1, + loy = rooms[vlt].ly - 1, hiy = rooms[vlt].hy + 1; + struct monst *mon; + struct obj *gold; + struct trap *trap; + boolean fixed = FALSE; + boolean movedgold = FALSE; + + for (x = lox; x <= hix; x++) + for (y = loy; y <= hiy; y++) { + /* if not on the room boundary, skip ahead */ + if (x != lox && x != hix && y != loy && y != hiy) continue; + + if (!IS_WALL(levl[x][y].typ) && !in_fcorridor(grd, x, y)) { + if ((mon = m_at(x, y)) != 0 && mon != grd) { + if (mon->mtame) yelp(mon); + (void) rloc(mon, FALSE); + } + if ((gold = g_at(x, y)) != 0) { + move_gold(gold, EGD(grd)->vroom); + movedgold = TRUE; + } + if ((trap = t_at(x, y)) != 0) + deltrap(trap); + if (x == lox) + typ = (y == loy) ? TLCORNER : + (y == hiy) ? BLCORNER : VWALL; + else if (x == hix) + typ = (y == loy) ? TRCORNER : + (y == hiy) ? BRCORNER : VWALL; + else /* not left or right side, must be top or bottom */ + typ = HWALL; + levl[x][y].typ = typ; + levl[x][y].doormask = 0; + /* + * hack: player knows walls are restored because of the + * message, below, so show this on the screen. + */ + tmp_viz = viz_array[y][x]; + viz_array[y][x] = IN_SIGHT|COULD_SEE; + newsym(x,y); + viz_array[y][x] = tmp_viz; + block_point(x,y); + fixed = TRUE; + } + } + + if(movedgold || fixed) { + if(in_fcorridor(grd, grd->mx, grd->my) || cansee(grd->mx, grd->my)) + pline_The("%s whispers an incantation.", g_monnam(grd)); + else You_hear("a distant chant."); + if(movedgold) + pline("A mysterious force moves the gold into the vault."); + if(fixed) + pline_The("damaged vault's walls are magically restored!"); + } +} + +/* + * return 1: guard moved, 0: guard didn't, -1: let m_move do it, -2: died + */ +int +gd_move(grd) +register struct monst *grd; +{ + int x, y, nx, ny, m, n; + int dx, dy, gx, gy, fci; + uchar typ; + struct fakecorridor *fcp; + register struct egd *egrd = EGD(grd); + register struct rm *crm; + register boolean goldincorridor = FALSE, + u_in_vault = vault_occupied(u.urooms)? TRUE : FALSE, + grd_in_vault = *in_rooms(grd->mx, grd->my, VAULT)? + TRUE : FALSE; + boolean disappear_msg_seen = FALSE, semi_dead = (grd->mhp <= 0); +#ifndef GOLDOBJ + register boolean u_carry_gold = ((u.ugold + hidden_gold()) > 0L); +#else + long umoney = money_cnt(invent); + register boolean u_carry_gold = ((umoney + hidden_gold()) > 0L); +#endif + boolean see_guard; + + if(!on_level(&(egrd->gdlevel), &u.uz)) return(-1); + nx = ny = m = n = 0; + if(!u_in_vault && !grd_in_vault) + wallify_vault(grd); + if(!grd->mpeaceful) { + if(semi_dead) { + egrd->gddone =1; + goto newpos; + } + if(!u_in_vault && + (grd_in_vault || + (in_fcorridor(grd, grd->mx, grd->my) && + !in_fcorridor(grd, u.ux, u.uy)))) { + (void) rloc(grd, FALSE); + wallify_vault(grd); + (void) clear_fcorr(grd, TRUE); + goto letknow; + } + if(!in_fcorridor(grd, grd->mx, grd->my)) + (void) clear_fcorr(grd, TRUE); + return(-1); + } + if(abs(egrd->ogx - grd->mx) > 1 || + abs(egrd->ogy - grd->my) > 1) + return(-1); /* teleported guard - treat as monster */ + if(egrd->fcend == 1) { + if(u_in_vault && + (u_carry_gold || um_dist(grd->mx, grd->my, 1))) { + if(egrd->warncnt == 3) + verbalize("I repeat, %sfollow me!", + u_carry_gold ? ( +#ifndef GOLDOBJ + !u.ugold ? + "drop that hidden gold and " : + "drop that gold and ") : ""); +#else + !umoney ? + "drop that hidden money and " : + "drop that money and ") : ""); +#endif + if(egrd->warncnt == 7) { + m = grd->mx; + n = grd->my; + verbalize("You've been warned, knave!"); + mnexto(grd); + levl[m][n].typ = egrd->fakecorr[0].ftyp; + newsym(m,n); + grd->mpeaceful = 0; + return(-1); + } + /* not fair to get mad when (s)he's fainted or paralyzed */ + if(!is_fainted() && multi >= 0) egrd->warncnt++; + return(0); + } + + if (!u_in_vault) { + if (u_carry_gold) { /* player teleported */ + m = grd->mx; + n = grd->my; + (void) rloc(grd, FALSE); + levl[m][n].typ = egrd->fakecorr[0].ftyp; + newsym(m,n); + grd->mpeaceful = 0; +letknow: + if (!cansee(grd->mx, grd->my) || !mon_visible(grd)) + You_hear("the shrill sound of a guard's whistle."); + else + You(um_dist(grd->mx, grd->my, 2) ? + "see an angry %s approaching." : + "are confronted by an angry %s.", + g_monnam(grd)); + return(-1); + } else { + verbalize("Well, begone."); + wallify_vault(grd); + egrd->gddone = 1; + goto cleanup; + } + } + } + + if(egrd->fcend > 1) { + if(egrd->fcend > 2 && in_fcorridor(grd, grd->mx, grd->my) && + !egrd->gddone && !in_fcorridor(grd, u.ux, u.uy) && + levl[egrd->fakecorr[0].fx][egrd->fakecorr[0].fy].typ + == egrd->fakecorr[0].ftyp) { + pline_The("%s, confused, disappears.", g_monnam(grd)); + disappear_msg_seen = TRUE; + goto cleanup; + } + if(u_carry_gold && + (in_fcorridor(grd, u.ux, u.uy) || + /* cover a 'blind' spot */ + (egrd->fcend > 1 && u_in_vault))) { + if(!grd->mx) { + restfakecorr(grd); + return(-2); + } + if(egrd->warncnt < 6) { + egrd->warncnt = 6; + verbalize("Drop all your gold, scoundrel!"); + return(0); + } else { + verbalize("So be it, rogue!"); + grd->mpeaceful = 0; + return(-1); + } + } + } + for(fci = egrd->fcbeg; fci < egrd->fcend; fci++) + if(g_at(egrd->fakecorr[fci].fx, egrd->fakecorr[fci].fy)){ + m = egrd->fakecorr[fci].fx; + n = egrd->fakecorr[fci].fy; + goldincorridor = TRUE; + } + if(goldincorridor && !egrd->gddone) { + x = grd->mx; + y = grd->my; + if (m == u.ux && n == u.uy) { + struct obj *gold = g_at(m,n); + /* Grab the gold from between the hero's feet. */ +#ifndef GOLDOBJ + grd->mgold += gold->quan; + delobj(gold); +#else + obj_extract_self(gold); + add_to_minv(grd, gold); +#endif + newsym(m,n); + } else if (m == x && n == y) { + mpickgold(grd); /* does a newsym */ + } else { + /* just for insurance... */ + if (MON_AT(m, n) && m != grd->mx && n != grd->my) { + verbalize("Out of my way, scum!"); + (void) rloc(m_at(m, n), FALSE); + } + remove_monster(grd->mx, grd->my); + newsym(grd->mx, grd->my); + place_monster(grd, m, n); + mpickgold(grd); /* does a newsym */ + } + if(cansee(m,n)) + pline("%s%s picks up the gold.", Monnam(grd), + grd->mpeaceful ? " calms down and" : ""); + if(x != grd->mx || y != grd->my) { + remove_monster(grd->mx, grd->my); + newsym(grd->mx, grd->my); + place_monster(grd, x, y); + newsym(x, y); + } + if(!grd->mpeaceful) return(-1); + else { + egrd->warncnt = 5; + return(0); + } + } + if(um_dist(grd->mx, grd->my, 1) || egrd->gddone) { + if(!egrd->gddone && !rn2(10)) verbalize("Move along!"); + restfakecorr(grd); + return(0); /* didn't move */ + } + x = grd->mx; + y = grd->my; + + if(u_in_vault) goto nextpos; + + /* look around (hor & vert only) for accessible places */ + for(nx = x-1; nx <= x+1; nx++) for(ny = y-1; ny <= y+1; ny++) { + if((nx == x || ny == y) && (nx != x || ny != y) && isok(nx, ny)) { + + typ = (crm = &levl[nx][ny])->typ; + if(!IS_STWALL(typ) && !IS_POOL(typ)) { + + if(in_fcorridor(grd, nx, ny)) + goto nextnxy; + + if(*in_rooms(nx,ny,VAULT)) + continue; + + /* seems we found a good place to leave him alone */ + egrd->gddone = 1; + if(ACCESSIBLE(typ)) goto newpos; +#ifdef STUPID + if (typ == SCORR) + crm->typ = CORR; + else + crm->typ = DOOR; +#else + crm->typ = (typ == SCORR) ? CORR : DOOR; +#endif + if(crm->typ == DOOR) crm->doormask = D_NODOOR; + goto proceed; + } + } +nextnxy: ; + } +nextpos: + nx = x; + ny = y; + gx = egrd->gdx; + gy = egrd->gdy; + dx = (gx > x) ? 1 : (gx < x) ? -1 : 0; + dy = (gy > y) ? 1 : (gy < y) ? -1 : 0; + if(abs(gx-x) >= abs(gy-y)) nx += dx; else ny += dy; + + while((typ = (crm = &levl[nx][ny])->typ) != 0) { + /* in view of the above we must have IS_WALL(typ) or typ == POOL */ + /* must be a wall here */ + if(isok(nx+nx-x,ny+ny-y) && !IS_POOL(typ) && + IS_ROOM(levl[nx+nx-x][ny+ny-y].typ)){ + crm->typ = DOOR; + crm->doormask = D_NODOOR; + goto proceed; + } + if(dy && nx != x) { + nx = x; ny = y+dy; + continue; + } + if(dx && ny != y) { + ny = y; nx = x+dx; dy = 0; + continue; + } + /* I don't like this, but ... */ + if(IS_ROOM(typ)) { + crm->typ = DOOR; + crm->doormask = D_NODOOR; + goto proceed; + } + break; + } + crm->typ = CORR; +proceed: + unblock_point(nx, ny); /* doesn't block light */ + if (cansee(nx,ny)) + newsym(nx,ny); + + fcp = &(egrd->fakecorr[egrd->fcend]); + if(egrd->fcend++ == FCSIZ) panic("fakecorr overflow"); + fcp->fx = nx; + fcp->fy = ny; + fcp->ftyp = typ; +newpos: + if(egrd->gddone) { + /* The following is a kludge. We need to keep */ + /* the guard around in order to be able to make */ + /* the fake corridor disappear as the player */ + /* moves out of it, but we also need the guard */ + /* out of the way. We send the guard to never- */ + /* never land. We set ogx ogy to mx my in order */ + /* to avoid a check at the top of this function. */ + /* At the end of the process, the guard is killed */ + /* in restfakecorr(). */ +cleanup: + x = grd->mx; y = grd->my; + + see_guard = canspotmon(grd); + wallify_vault(grd); + remove_monster(grd->mx, grd->my); + newsym(grd->mx,grd->my); + place_monster(grd, 0, 0); + egrd->ogx = grd->mx; + egrd->ogy = grd->my; + restfakecorr(grd); + if(!semi_dead && (in_fcorridor(grd, u.ux, u.uy) || + cansee(x, y))) { + if (!disappear_msg_seen && see_guard) + pline("Suddenly, the %s disappears.", g_monnam(grd)); + return(1); + } + return(-2); + } + egrd->ogx = grd->mx; /* update old positions */ + egrd->ogy = grd->my; + remove_monster(grd->mx, grd->my); + place_monster(grd, nx, ny); + newsym(grd->mx,grd->my); + restfakecorr(grd); + return(1); +} + +/* Routine when dying or quitting with a vault guard around */ +void +paygd() +{ + register struct monst *grd = findgd(); +#ifndef GOLDOBJ + struct obj *gold; +#else + long umoney = money_cnt(invent); + struct obj *coins, *nextcoins; +#endif + int gx,gy; + char buf[BUFSZ]; + +#ifndef GOLDOBJ + if (!u.ugold || !grd) return; +#else + if (!umoney || !grd) return; +#endif + + if (u.uinvault) { + Your("%ld %s goes into the Magic Memory Vault.", +#ifndef GOLDOBJ + u.ugold, + currency(u.ugold)); +#else + umoney, + currency(umoney)); +#endif + gx = u.ux; + gy = u.uy; + } else { + if(grd->mpeaceful) { /* guard has no "right" to your gold */ + mongone(grd); + return; + } + mnexto(grd); + pline("%s remits your gold to the vault.", Monnam(grd)); + gx = rooms[EGD(grd)->vroom].lx + rn2(2); + gy = rooms[EGD(grd)->vroom].ly + rn2(2); + Sprintf(buf, + "To Croesus: here's the gold recovered from %s the %s.", + plname, mons[u.umonster].mname); + make_grave(gx, gy, buf); + } +#ifndef GOLDOBJ + place_object(gold = mkgoldobj(u.ugold), gx, gy); + stackobj(gold); +#else + for (coins = invent; coins; coins = nextcoins) { + nextcoins = coins->nobj; + if (objects[coins->otyp].oc_class == COIN_CLASS) { + freeinv(coins); + place_object(coins, gx, gy); + stackobj(coins); + } + } +#endif + mongone(grd); +} + +long +hidden_gold() +{ + register long value = 0L; + register struct obj *obj; + + for (obj = invent; obj; obj = obj->nobj) + if (Has_contents(obj)) + value += contained_gold(obj); + /* unknown gold stuck inside statues may cause some consternation... */ + + return(value); +} + +boolean +gd_sound() /* prevent "You hear footsteps.." when inappropriate */ +{ + register struct monst *grd = findgd(); + + if (vault_occupied(u.urooms)) return(FALSE); + else return((boolean)(grd == (struct monst *)0)); +} + +#endif /* OVLB */ + +/*vault.c*/ diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..b35475c --- /dev/null +++ b/src/version.c @@ -0,0 +1,185 @@ +/* SCCS Id: @(#)version.c 3.4 2003/12/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "date.h" +/* + * All the references to the contents of patchlevel.h have been moved + * into makedefs.... + */ +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +/* #define BETA_INFO "" */ /* "[ beta n]" */ + +/* fill buffer with short version (so caller can avoid including date.h) */ +char * +version_string(buf) +char *buf; +{ + return strcpy(buf, VERSION_STRING); +} + +/* fill and return the given buffer with the long nethack version string */ +char * +getversionstring(buf) +char *buf; +{ + Strcpy(buf, VERSION_ID); +#if defined(BETA) && defined(BETA_INFO) + Sprintf(eos(buf), " %s", BETA_INFO); +#endif +#if defined(RUNTIME_PORT_ID) + append_port_id(buf); +#endif + return buf; +} + +int +doversion() +{ + char buf[BUFSZ]; + + pline("%s", getversionstring(buf)); + return 0; +} + +int +doextversion() +{ + display_file(OPTIONS_USED, TRUE); + return 0; +} + +#ifdef MICRO +boolean +comp_times(filetime) +long filetime; +{ + return((boolean)(filetime < BUILD_TIME)); +} +#endif + +boolean +check_version(version_data, filename, complain) +struct version_info *version_data; +const char *filename; +boolean complain; +{ + if ( +#ifdef VERSION_COMPATIBILITY + version_data->incarnation < VERSION_COMPATIBILITY || + version_data->incarnation > VERSION_NUMBER +#else + version_data->incarnation != VERSION_NUMBER +#endif + ) { + if (complain) + pline("Version mismatch for file \"%s\".", filename); + return FALSE; + } else if ( +#ifndef IGNORED_FEATURES + version_data->feature_set != VERSION_FEATURES || +#else + (version_data->feature_set & ~IGNORED_FEATURES) != + (VERSION_FEATURES & ~IGNORED_FEATURES) || +#endif + version_data->entity_count != VERSION_SANITY1 || + version_data->struct_sizes != VERSION_SANITY2) { + if (complain) + pline("Configuration incompatibility for file \"%s\".", + filename); + return FALSE; + } + return TRUE; +} + +/* this used to be based on file date and somewhat OS-dependant, + but now examines the initial part of the file's contents */ +boolean +uptodate(fd, name) +int fd; +const char *name; +{ + int rlen; + struct version_info vers_info; + boolean verbose = name ? TRUE : FALSE; + + rlen = read(fd, (genericptr_t) &vers_info, sizeof vers_info); + minit(); /* ZEROCOMP */ + if (rlen == 0) { + if (verbose) { + pline("File \"%s\" is empty?", name); + wait_synch(); + } + return FALSE; + } + if (!check_version(&vers_info, name, verbose)) { + if (verbose) wait_synch(); + return FALSE; + } + return TRUE; +} + +void +store_version(fd) +int fd; +{ + const static struct version_info version_data = { + VERSION_NUMBER, VERSION_FEATURES, + VERSION_SANITY1, VERSION_SANITY2 + }; + + bufoff(fd); + /* bwrite() before bufon() uses plain write() */ + bwrite(fd,(genericptr_t)&version_data,(unsigned)(sizeof version_data)); + bufon(fd); + return; +} + +#ifdef AMIGA +const char amiga_version_string[] = AMIGA_VERSION_STRING; +#endif + +unsigned long +get_feature_notice_ver(str) +char *str; +{ + char buf[BUFSZ]; + int ver_maj, ver_min, patch; + char *istr[3]; + int j = 0; + + if (!str) return 0L; + str = strcpy(buf, str); + istr[j] = str; + while (*str) { + if (*str == '.') { + *str++ = '\0'; + j++; + istr[j] = str; + if (j == 2) break; + } else if (index("0123456789", *str) != 0) { + str++; + } else + return 0L; + } + if (j != 2) return 0L; + ver_maj = atoi(istr[0]); + ver_min = atoi(istr[1]); + patch = atoi(istr[2]); + return FEATURE_NOTICE_VER(ver_maj,ver_min,patch); + /* macro from hack.h */ +} + +unsigned long +get_current_feature_ver() +{ + return FEATURE_NOTICE_VER(VERSION_MAJOR,VERSION_MINOR,PATCHLEVEL); +} + +/*version.c*/ diff --git a/src/vision.c b/src/vision.c new file mode 100644 index 0000000..e614bdd --- /dev/null +++ b/src/vision.c @@ -0,0 +1,2622 @@ +/* SCCS Id: @(#)vision.c 3.4 1999/02/18 */ +/* Copyright (c) Dean Luick, with acknowledgements to Dave Cohrs, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* Circles ==================================================================*/ + +/* + * These numbers are limit offsets for one quadrant of a circle of a given + * radius (the first number of each line) from the source. The number in + * the comment is the element number (so pointers can be set up). Each + * "circle" has as many elements as its radius+1. The radius is the number + * of points away from the source that the limit exists. The radius of the + * offset on the same row as the source *is* included so we don't have to + * make an extra check. For example, a circle of radius 4 has offsets: + * + * XXX +2 + * ...X +3 + * ....X +4 + * ....X +4 + * @...X +4 + * + */ +char circle_data[] = { +/* 0*/ 1, 1, +/* 2*/ 2, 2, 1, +/* 5*/ 3, 3, 2, 1, +/* 9*/ 4, 4, 4, 3, 2, +/* 14*/ 5, 5, 5, 4, 3, 2, +/* 20*/ 6, 6, 6, 5, 5, 4, 2, +/* 27*/ 7, 7, 7, 6, 6, 5, 4, 2, +/* 35*/ 8, 8, 8, 7, 7, 6, 6, 4, 2, +/* 44*/ 9, 9, 9, 9, 8, 8, 7, 6, 5, 3, +/* 54*/ 10,10,10,10, 9, 9, 8, 7, 6, 5, 3, +/* 65*/ 11,11,11,11,10,10, 9, 9, 8, 7, 5, 3, +/* 77*/ 12,12,12,12,11,11,10,10, 9, 8, 7, 5, 3, +/* 90*/ 13,13,13,13,12,12,12,11,10,10, 9, 7, 6, 3, +/*104*/ 14,14,14,14,13,13,13,12,12,11,10, 9, 8, 6, 3, +/*119*/ 15,15,15,15,14,14,14,13,13,12,11,10, 9, 8, 6, 3, +/*135*/ 16 /* should be MAX_RADIUS+1; used to terminate range loops -dlc */ +}; + +/* + * These are the starting indexes into the circle_data[] array for a + * circle of a given radius. + */ +char circle_start[] = { +/* */ 0, /* circles of radius zero are not used */ +/* 1*/ 0, +/* 2*/ 2, +/* 3*/ 5, +/* 4*/ 9, +/* 5*/ 14, +/* 6*/ 20, +/* 7*/ 27, +/* 8*/ 35, +/* 9*/ 44, +/*10*/ 54, +/*11*/ 65, +/*12*/ 77, +/*13*/ 90, +/*14*/ 104, +/*15*/ 119, +}; + + +/*===========================================================================*/ +/* Vision (arbitrary line of sight) =========================================*/ + +/*------ global variables ------*/ + +#if 0 /* (moved to decl.c) */ +/* True if we need to run a full vision recalculation. */ +boolean vision_full_recalc = 0; + +/* Pointers to the current vision array. */ +char **viz_array; +#endif +char *viz_rmin, *viz_rmax; /* current vision cs bounds */ + + +/*------ local variables ------*/ + + +static char could_see[2][ROWNO][COLNO]; /* vision work space */ +static char *cs_rows0[ROWNO], *cs_rows1[ROWNO]; +static char cs_rmin0[ROWNO], cs_rmax0[ROWNO]; +static char cs_rmin1[ROWNO], cs_rmax1[ROWNO]; + +static char viz_clear[ROWNO][COLNO]; /* vision clear/blocked map */ +static char *viz_clear_rows[ROWNO]; + +static char left_ptrs[ROWNO][COLNO]; /* LOS algorithm helpers */ +static char right_ptrs[ROWNO][COLNO]; + +/* Forward declarations. */ +STATIC_DCL void FDECL(fill_point, (int,int)); +STATIC_DCL void FDECL(dig_point, (int,int)); +STATIC_DCL void NDECL(view_init); +STATIC_DCL void FDECL(view_from,(int,int,char **,char *,char *,int, + void (*)(int,int,genericptr_t),genericptr_t)); +STATIC_DCL void FDECL(get_unused_cs, (char ***,char **,char **)); +#ifdef REINCARNATION +STATIC_DCL void FDECL(rogue_vision, (char **,char *,char *)); +#endif + +/* Macro definitions that I can't find anywhere. */ +#define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0 )) +#define v_abs(z) ((z) < 0 ? -(z) : (z)) /* don't use abs -- it may exist */ + +/* + * vision_init() + * + * The one-time vision initialization routine. + * + * This must be called before mklev() is called in newgame() [allmain.c], + * or before a game restore. Else we die a horrible death. + */ +void +vision_init() +{ + int i; + + /* Set up the pointers. */ + for (i = 0; i < ROWNO; i++) { + cs_rows0[i] = could_see[0][i]; + cs_rows1[i] = could_see[1][i]; + viz_clear_rows[i] = viz_clear[i]; + } + + /* Start out with cs0 as our current array */ + viz_array = cs_rows0; + viz_rmin = cs_rmin0; + viz_rmax = cs_rmax0; + + vision_full_recalc = 0; + (void) memset((genericptr_t) could_see, 0, sizeof(could_see)); + + /* Initialize the vision algorithm (currently C or D). */ + view_init(); + +#ifdef VISION_TABLES + /* Note: this initializer doesn't do anything except guarantee that + we're linked properly. + */ + vis_tab_init(); +#endif +} + +/* + * does_block() + * + * Returns true if the level feature, object, or monster at (x,y) blocks + * sight. + */ +int +does_block(x,y,lev) + int x, y; + register struct rm *lev; +{ + struct obj *obj; + struct monst *mon; + + /* Features that block . . */ + if (IS_ROCK(lev->typ) || lev->typ == TREE || (IS_DOOR(lev->typ) && + (lev->doormask & (D_CLOSED|D_LOCKED|D_TRAPPED) ))) + return 1; + + if (lev->typ == CLOUD || lev->typ == WATER || + (lev->typ == MOAT && Underwater)) + return 1; + + /* Boulders block light. */ + for (obj = level.objects[x][y]; obj; obj = obj->nexthere) + if (obj->otyp == BOULDER) return 1; + + /* Mimics mimicing a door or boulder block light. */ + if ((mon = m_at(x,y)) && (!mon->minvis || See_invisible) && + ((mon->m_ap_type == M_AP_FURNITURE && + (mon->mappearance == S_hcdoor || mon->mappearance == S_vcdoor)) || + (mon->m_ap_type == M_AP_OBJECT && mon->mappearance == BOULDER))) + return 1; + + return 0; +} + +/* + * vision_reset() + * + * This must be called *after* the levl[][] structure is set with the new + * level and the level monsters and objects are in place. + */ +void +vision_reset() +{ + int y; + register int x, i, dig_left, block; + register struct rm *lev; + + /* Start out with cs0 as our current array */ + viz_array = cs_rows0; + viz_rmin = cs_rmin0; + viz_rmax = cs_rmax0; + + (void) memset((genericptr_t) could_see, 0, sizeof(could_see)); + + /* Reset the pointers and clear so that we have a "full" dungeon. */ + (void) memset((genericptr_t) viz_clear, 0, sizeof(viz_clear)); + + /* Dig the level */ + for (y = 0; y < ROWNO; y++) { + dig_left = 0; + block = TRUE; /* location (0,y) is always stone; it's !isok() */ + lev = &levl[1][y]; + for (x = 1; x < COLNO; x++, lev += ROWNO) + if (block != (IS_ROCK(lev->typ) || does_block(x,y,lev))) { + if(block) { + for(i=dig_left; i= 0) { + for (zy = rooms[rnum].ly-1; zy <= rooms[rnum].hy+1; zy++) { + rmin[zy] = start = rooms[rnum].lx-1; + rmax[zy] = stop = rooms[rnum].hx+1; + + for (zx = start; zx <= stop; zx++) { + if (rooms[rnum].rlit) { + next[zy][zx] = COULD_SEE | IN_SIGHT; + levl[zx][zy].seenv = SVALL; /* see the walls */ + } else + next[zy][zx] = COULD_SEE; + } + } + } + + in_door = levl[u.ux][u.uy].typ == DOOR; + + /* Can always see adjacent. */ + ylo = max(u.uy - 1, 0); + yhi = min(u.uy + 1, ROWNO - 1); + xlo = max(u.ux - 1, 1); + xhi = min(u.ux + 1, COLNO - 1); + for (zy = ylo; zy <= yhi; zy++) { + if (xlo < rmin[zy]) rmin[zy] = xlo; + if (xhi > rmax[zy]) rmax[zy] = xhi; + + for (zx = xlo; zx <= xhi; zx++) { + next[zy][zx] = COULD_SEE | IN_SIGHT; + /* + * Yuck, update adjacent non-diagonal positions when in a doorway. + * We need to do this to catch the case when we first step into + * a room. The room's walls were not seen from the outside, but + * now are seen (the seen bits are set just above). However, the + * positions are not updated because they were already in sight. + * So, we have to do it here. + */ + if (in_door && (zx == u.ux || zy == u.uy)) newsym(zx,zy); + } + } +} +#endif /* REINCARNATION */ + +/*#define EXTEND_SPINE*/ /* possibly better looking wall-angle */ + +#ifdef EXTEND_SPINE + +STATIC_DCL int FDECL(new_angle, (struct rm *, unsigned char *, int, int)); +/* + * new_angle() + * + * Return the new angle seen by the hero for this location. The angle + * bit is given in the value pointed at by sv. + * + * For T walls and crosswall, just setting the angle bit, even though + * it is technically correct, doesn't look good. If we can see the + * next position beyond the current one and it is a wall that we can + * see, then we want to extend a spine of the T to connect with the wall + * that is beyond. Example: + * + * Correct, but ugly Extend T spine + * + * | ... | ... + * | ... <-- wall beyond & floor --> | ... + * | ... | ... + * Unseen --> ... | ... + * spine +-... <-- trwall & doorway --> +-... + * | ... | ... + * + * + * @ <-- hero --> @ + * + * + * We fake the above check by only checking if the horizontal & + * vertical positions adjacent to the crosswall and T wall are + * unblocked. Then, _in general_ we can see beyond. Generally, + * this is good enough. + * + * + When this function is called we don't have all of the seen + * information (we're doing a top down scan in vision_recalc). + * We would need to scan once to set all IN_SIGHT and COULD_SEE + * bits, then again to correctly set the seenv bits. + * + I'm trying to make this as cheap as possible. The display & + * vision eat up too much CPU time. + * + * + * Note: Even as I write this, I'm still not convinced. There are too + * many exceptions. I may have to bite the bullet and do more + * checks. - Dean 2/11/93 + */ +STATIC_OVL int +new_angle(lev, sv, row, col) + struct rm *lev; + unsigned char *sv; + int row, col; +{ + register int res = *sv; + + /* + * Do extra checks for crosswalls and T walls if we see them from + * an angle. + */ + if (lev->typ >= CROSSWALL && lev->typ <= TRWALL) { + switch (res) { + case SV0: + if (col > 0 && viz_clear[row][col-1]) res |= SV7; + if (row > 0 && viz_clear[row-1][col]) res |= SV1; + break; + case SV2: + if (row > 0 && viz_clear[row-1][col]) res |= SV1; + if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3; + break; + case SV4: + if (col < COLNO-1 && viz_clear[row][col+1]) res |= SV3; + if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5; + break; + case SV6: + if (row < ROWNO-1 && viz_clear[row+1][col]) res |= SV5; + if (col > 0 && viz_clear[row][col-1]) res |= SV7; + break; + } + } + return res; +} +#else +/* + * new_angle() + * + * Return the new angle seen by the hero for this location. The angle + * bit is given in the value pointed at by sv. + * + * The other parameters are not used. + */ +#define new_angle(lev, sv, row, col) (*sv) + +#endif + + +/* + * vision_recalc() + * + * Do all of the heavy vision work. Recalculate all locations that could + * possibly be seen by the hero --- if the location were lit, etc. Note + * which locations are actually seen because of lighting. Then add to + * this all locations that be seen by hero due to night vision and x-ray + * vision. Finally, compare with what the hero was able to see previously. + * Update the difference. + * + * This function is usually called only when the variable 'vision_full_recalc' + * is set. The following is a list of places where this function is called, + * with three valid values for the control flag parameter: + * + * Control flag = 0. A complete vision recalculation. Generate the vision + * tables from scratch. This is necessary to correctly set what the hero + * can see. (1) and (2) call this routine for synchronization purposes, (3) + * calls this routine so it can operate correctly. + * + * + After the monster move, before input from the player. [moveloop()] + * + At end of moveloop. [moveloop() ??? not sure why this is here] + * + Right before something is printed. [pline()] + * + Right before we do a vision based operation. [do_clear_area()] + * + screen redraw, so we can renew all positions in sight. [docrt()] + * + * Control flag = 1. An adjacent vision recalculation. The hero has moved + * one square. Knowing this, it might be possible to optimize the vision + * recalculation using the current knowledge. This is presently unimplemented + * and is treated as a control = 0 call. + * + * + Right after the hero moves. [domove()] + * + * Control flag = 2. Turn off the vision system. Nothing new will be + * displayed, since nothing is seen. This is usually done when you need + * a newsym() run on all locations in sight, or on some locations but you + * don't know which ones. + * + * + Before a screen redraw, so all positions are renewed. [docrt()] + * + Right before the hero arrives on a new level. [goto_level()] + * + Right after a scroll of light is read. [litroom()] + * + After an option has changed that affects vision [parseoptions()] + * + Right after the hero is swallowed. [gulpmu()] + * + Just before bubbles are moved. [movebubbles()] + */ +void +vision_recalc(control) + int control; +{ + char **temp_array; /* points to the old vision array */ + char **next_array; /* points to the new vision array */ + char *next_row; /* row pointer for the new array */ + char *old_row; /* row pointer for the old array */ + char *next_rmin; /* min pointer for the new array */ + char *next_rmax; /* max pointer for the new array */ + char *ranges; /* circle ranges -- used for xray & night vision */ + int row; /* row counter (outer loop) */ + int start, stop; /* inner loop starting/stopping index */ + int dx, dy; /* one step from a lit door or lit wall (see below) */ + register int col; /* inner loop counter */ + register struct rm *lev; /* pointer to current pos */ + struct rm *flev; /* pointer to position in "front" of current pos */ + extern unsigned char seenv_matrix[3][3]; /* from display.c */ + static unsigned char colbump[COLNO+1]; /* cols to bump sv */ + unsigned char *sv; /* ptr to seen angle bits */ + int oldseenv; /* previous seenv value */ + + vision_full_recalc = 0; /* reset flag */ + if (in_mklev || !iflags.vision_inited) return; + +#ifdef GCC_WARN + row = 0; +#endif + + /* + * Either the light sources have been taken care of, or we must + * recalculate them here. + */ + + /* Get the unused could see, row min, and row max arrays. */ + get_unused_cs(&next_array, &next_rmin, &next_rmax); + + /* You see nothing, nothing can see you --- if swallowed or refreshing. */ + if (u.uswallow || control == 2) { + /* do nothing -- get_unused_cs() nulls out the new work area */ + + } else if (Blind) { + /* + * Calculate the could_see array even when blind so that monsters + * can see you, even if you can't see them. Note that the current + * setup allows: + * + * + Monsters to see with the "new" vision, even on the rogue + * level. + * + * + Monsters can see you even when you're in a pit. + */ + view_from(u.uy, u.ux, next_array, next_rmin, next_rmax, + 0, (void FDECL((*),(int,int,genericptr_t)))0, (genericptr_t)0); + + /* + * Our own version of the update loop below. We know we can't see + * anything, so we only need update positions we used to be able + * to see. + */ + temp_array = viz_array; /* set viz_array so newsym() will work */ + viz_array = next_array; + + for (row = 0; row < ROWNO; row++) { + old_row = temp_array[row]; + + /* Find the min and max positions on the row. */ + start = min(viz_rmin[row], next_rmin[row]); + stop = max(viz_rmax[row], next_rmax[row]); + + for (col = start; col <= stop; col++) + if (old_row[col] & IN_SIGHT) newsym(col,row); + } + + /* skip the normal update loop */ + goto skip; + } +#ifdef REINCARNATION + else if (Is_rogue_level(&u.uz)) { + rogue_vision(next_array,next_rmin,next_rmax); + } +#endif + else { + int has_night_vision = 1; /* hero has night vision */ + + if (Underwater && !Is_waterlevel(&u.uz)) { + /* + * The hero is under water. Only see surrounding locations if + * they are also underwater. This overrides night vision but + * does not override x-ray vision. + */ + has_night_vision = 0; + + for (row = u.uy-1; row <= u.uy+1; row++) + for (col = u.ux-1; col <= u.ux+1; col++) { + if (!isok(col,row) || !is_pool(col,row)) continue; + + next_rmin[row] = min(next_rmin[row], col); + next_rmax[row] = max(next_rmax[row], col); + next_array[row][col] = IN_SIGHT | COULD_SEE; + } + } + + /* if in a pit, just update for immediate locations */ + else if (u.utrap && u.utraptype == TT_PIT) { + for (row = u.uy-1; row <= u.uy+1; row++) { + if (row < 0) continue; if (row >= ROWNO) break; + + next_rmin[row] = max( 0, u.ux - 1); + next_rmax[row] = min(COLNO-1, u.ux + 1); + next_row = next_array[row]; + + for(col=next_rmin[row]; col <= next_rmax[row]; col++) + next_row[col] = IN_SIGHT | COULD_SEE; + } + } else + view_from(u.uy, u.ux, next_array, next_rmin, next_rmax, + 0, (void FDECL((*),(int,int,genericptr_t)))0, (genericptr_t)0); + + /* + * Set the IN_SIGHT bit for xray and night vision. + */ + if (u.xray_range >= 0) { + if (u.xray_range) { + ranges = circle_ptr(u.xray_range); + + for (row = u.uy-u.xray_range; row <= u.uy+u.xray_range; row++) { + if (row < 0) continue; if (row >= ROWNO) break; + dy = v_abs(u.uy-row); next_row = next_array[row]; + + start = max( 0, u.ux - ranges[dy]); + stop = min(COLNO-1, u.ux + ranges[dy]); + + for (col = start; col <= stop; col++) { + char old_row_val = next_row[col]; + next_row[col] |= IN_SIGHT; + oldseenv = levl[col][row].seenv; + levl[col][row].seenv = SVALL; /* see all! */ + /* Update if previously not in sight or new angle. */ + if (!(old_row_val & IN_SIGHT) || oldseenv != SVALL) + newsym(col,row); + } + + next_rmin[row] = min(start, next_rmin[row]); + next_rmax[row] = max(stop, next_rmax[row]); + } + + } else { /* range is 0 */ + next_array[u.uy][u.ux] |= IN_SIGHT; + levl[u.ux][u.uy].seenv = SVALL; + next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]); + next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]); + } + } + + if (has_night_vision && u.xray_range < u.nv_range) { + if (!u.nv_range) { /* range is 0 */ + next_array[u.uy][u.ux] |= IN_SIGHT; + levl[u.ux][u.uy].seenv = SVALL; + next_rmin[u.uy] = min(u.ux, next_rmin[u.uy]); + next_rmax[u.uy] = max(u.ux, next_rmax[u.uy]); + } else if (u.nv_range > 0) { + ranges = circle_ptr(u.nv_range); + + for (row = u.uy-u.nv_range; row <= u.uy+u.nv_range; row++) { + if (row < 0) continue; if (row >= ROWNO) break; + dy = v_abs(u.uy-row); next_row = next_array[row]; + + start = max( 0, u.ux - ranges[dy]); + stop = min(COLNO-1, u.ux + ranges[dy]); + + for (col = start; col <= stop; col++) + if (next_row[col]) next_row[col] |= IN_SIGHT; + + next_rmin[row] = min(start, next_rmin[row]); + next_rmax[row] = max(stop, next_rmax[row]); + } + } + } + } + + /* Set the correct bits for all light sources. */ + do_light_sources(next_array); + + + /* + * Make the viz_array the new array so that cansee() will work correctly. + */ + temp_array = viz_array; + viz_array = next_array; + + /* + * The main update loop. Here we do two things: + * + * + Set the IN_SIGHT bit for places that we could see and are lit. + * + Reset changed places. + * + * There is one thing that make deciding what the hero can see + * difficult: + * + * 1. Directional lighting. Items that block light create problems. + * The worst offenders are doors. Suppose a door to a lit room + * is closed. It is lit on one side, but not on the other. How + * do you know? You have to check the closest adjacent position. + * Even so, that is not entirely correct. But it seems close + * enough for now. + */ + colbump[u.ux] = colbump[u.ux+1] = 1; + for (row = 0; row < ROWNO; row++) { + dy = u.uy - row; dy = sign(dy); + next_row = next_array[row]; old_row = temp_array[row]; + + /* Find the min and max positions on the row. */ + start = min(viz_rmin[row], next_rmin[row]); + stop = max(viz_rmax[row], next_rmax[row]); + lev = &levl[start][row]; + + sv = &seenv_matrix[dy+1][start < u.ux ? 0 : (start > u.ux ? 2:1)]; + + for (col = start; col <= stop; + lev += ROWNO, sv += (int) colbump[++col]) { + if (next_row[col] & IN_SIGHT) { + /* + * We see this position because of night- or xray-vision. + */ + oldseenv = lev->seenv; + lev->seenv |= new_angle(lev,sv,row,col); /* update seen angle */ + + /* Update pos if previously not in sight or new angle. */ + if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv) + newsym(col,row); + } + + else if ((next_row[col] & COULD_SEE) + && (lev->lit || (next_row[col] & TEMP_LIT))) { + /* + * We see this position because it is lit. + */ + if ((IS_DOOR(lev->typ) || lev->typ == SDOOR || + IS_WALL(lev->typ)) && !viz_clear[row][col]) { + /* + * Make sure doors, walls, boulders or mimics don't show up + * at the end of dark hallways. We do this by checking + * the adjacent position. If it is lit, then we can see + * the door or wall, otherwise we can't. + */ + dx = u.ux - col; dx = sign(dx); + flev = &(levl[col+dx][row+dy]); + if (flev->lit || next_array[row+dy][col+dx] & TEMP_LIT) { + next_row[col] |= IN_SIGHT; /* we see it */ + + oldseenv = lev->seenv; + lev->seenv |= new_angle(lev,sv,row,col); + + /* Update pos if previously not in sight or new angle.*/ + if (!(old_row[col] & IN_SIGHT) || oldseenv!=lev->seenv) + newsym(col,row); + } else + goto not_in_sight; /* we don't see it */ + + } else { + next_row[col] |= IN_SIGHT; /* we see it */ + + oldseenv = lev->seenv; + lev->seenv |= new_angle(lev,sv,row,col); + + /* Update pos if previously not in sight or new angle. */ + if ( !(old_row[col] & IN_SIGHT) || oldseenv != lev->seenv) + newsym(col,row); + } + } else if ((next_row[col] & COULD_SEE) && lev->waslit) { + /* + * If we make it here, the hero _could see_ the location, + * but doesn't see it (location is not lit). + * However, the hero _remembers_ it as lit (waslit is true). + * The hero can now see that it is not lit, so change waslit + * and update the location. + */ + lev->waslit = 0; /* remember lit condition */ + newsym(col,row); + } + /* + * At this point we know that the row position is *not* in normal + * sight. That is, the position is could be seen, but is dark + * or LOS is just plain blocked. + * + * Update the position if: + * o If the old one *was* in sight. We may need to clean up + * the glyph -- E.g. darken room spot, etc. + * o If we now could see the location (yet the location is not + * lit), but previously we couldn't see the location, or vice + * versa. Update the spot because there there may be an infared + * monster there. + */ + else { +not_in_sight: + if ((old_row[col] & IN_SIGHT) + || ((next_row[col] & COULD_SEE) + ^ (old_row[col] & COULD_SEE))) + newsym(col,row); + } + + } /* end for col . . */ + } /* end for row . . */ + colbump[u.ux] = colbump[u.ux+1] = 0; + +skip: + /* This newsym() caused a crash delivering msg about failure to open + * dungeon file init_dungeons() -> panic() -> done(11) -> + * vision_recalc(2) -> newsym() -> crash! u.ux and u.uy are 0 and + * program_state.panicking == 1 under those circumstances + */ + if (!program_state.panicking) + newsym(u.ux, u.uy); /* Make sure the hero shows up! */ + + /* Set the new min and max pointers. */ + viz_rmin = next_rmin; + viz_rmax = next_rmax; +} + + +/* + * block_point() + * + * Make the location opaque to light. + */ +void +block_point(x,y) + int x, y; +{ + fill_point(y,x); + + /* recalc light sources here? */ + + /* + * We have to do a full vision recalculation if we "could see" the + * location. Why? Suppose some monster opened a way so that the + * hero could see a lit room. However, the position of the opening + * was out of night-vision range of the hero. Suddenly the hero should + * see the lit room. + */ + if (viz_array[y][x]) vision_full_recalc = 1; +} + +/* + * unblock_point() + * + * Make the location transparent to light. + */ +void +unblock_point(x,y) + int x, y; +{ + dig_point(y,x); + + /* recalc light sources here? */ + + if (viz_array[y][x]) vision_full_recalc = 1; +} + + +/*===========================================================================*\ + | | + | Everything below this line uses (y,x) instead of (x,y) --- the | + | algorithms are faster if they are less recursive and can scan | + | on a row longer. | + | | +\*===========================================================================*/ + + +/* ========================================================================= *\ + Left and Right Pointer Updates +\* ========================================================================= */ + +/* + * LEFT and RIGHT pointer rules + * + * + * **NOTE** The rules changed on 4/4/90. This comment reflects the + * new rules. The change was so that the stone-wall optimization + * would work. + * + * OK, now the tough stuff. We must maintain our left and right + * row pointers. The rules are as follows: + * + * Left Pointers: + * ______________ + * + * + If you are a clear spot, your left will point to the first + * stone to your left. If there is none, then point the first + * legal position in the row (0). + * + * + If you are a blocked spot, then your left will point to the + * left-most blocked spot to your left that is connected to you. + * This means that a left-edge (a blocked spot that has an open + * spot on its left) will point to itself. + * + * + * Right Pointers: + * --------------- + * + If you are a clear spot, your right will point to the first + * stone to your right. If there is none, then point the last + * legal position in the row (COLNO-1). + * + * + If you are a blocked spot, then your right will point to the + * right-most blocked spot to your right that is connected to you. + * This means that a right-edge (a blocked spot that has an open + * spot on its right) will point to itself. + */ +STATIC_OVL void +dig_point(row,col) + int row,col; +{ + int i; + + if (viz_clear[row][col]) return; /* already done */ + + viz_clear[row][col] = 1; + + /* + * Boundary cases first. + */ + if (col == 0) { /* left edge */ + if (viz_clear[row][1]) { + right_ptrs[row][0] = right_ptrs[row][1]; + } else { + right_ptrs[row][0] = 1; + for (i = 1; i <= right_ptrs[row][1]; i++) + left_ptrs[row][i] = 1; + } + } else if (col == (COLNO-1)) { /* right edge */ + + if (viz_clear[row][COLNO-2]) { + left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2]; + } else { + left_ptrs[row][COLNO-1] = COLNO-2; + for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++) + right_ptrs[row][i] = COLNO-2; + } + } + + /* + * At this point, we know we aren't on the boundaries. + */ + else if (viz_clear[row][col-1] && viz_clear[row][col+1]) { + /* Both sides clear */ + for (i = left_ptrs[row][col-1]; i <= col; i++) { + if (!viz_clear[row][i]) continue; /* catch non-end case */ + right_ptrs[row][i] = right_ptrs[row][col+1]; + } + for (i = col; i <= right_ptrs[row][col+1]; i++) { + if (!viz_clear[row][i]) continue; /* catch non-end case */ + left_ptrs[row][i] = left_ptrs[row][col-1]; + } + + } else if (viz_clear[row][col-1]) { + /* Left side clear, right side blocked. */ + for (i = col+1; i <= right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = col+1; + + for (i = left_ptrs[row][col-1]; i <= col; i++) { + if (!viz_clear[row][i]) continue; /* catch non-end case */ + right_ptrs[row][i] = col+1; + } + left_ptrs[row][col] = left_ptrs[row][col-1]; + + } else if (viz_clear[row][col+1]) { + /* Right side clear, left side blocked. */ + for (i = left_ptrs[row][col-1]; i < col; i++) + right_ptrs[row][i] = col-1; + + for (i = col; i <= right_ptrs[row][col+1]; i++) { + if (!viz_clear[row][i]) continue; /* catch non-end case */ + left_ptrs[row][i] = col-1; + } + right_ptrs[row][col] = right_ptrs[row][col+1]; + + } else { + /* Both sides blocked */ + for (i = left_ptrs[row][col-1]; i < col; i++) + right_ptrs[row][i] = col-1; + + for (i = col+1; i <= right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = col+1; + + left_ptrs[row][col] = col-1; + right_ptrs[row][col] = col+1; + } +} + +STATIC_OVL void +fill_point(row,col) + int row, col; +{ + int i; + + if (!viz_clear[row][col]) return; + + viz_clear[row][col] = 0; + + if (col == 0) { + if (viz_clear[row][1]) { /* adjacent is clear */ + right_ptrs[row][0] = 0; + } else { + right_ptrs[row][0] = right_ptrs[row][1]; + for (i = 1; i <= right_ptrs[row][1]; i++) + left_ptrs[row][i] = 0; + } + } else if (col == COLNO-1) { + if (viz_clear[row][COLNO-2]) { /* adjacent is clear */ + left_ptrs[row][COLNO-1] = COLNO-1; + } else { + left_ptrs[row][COLNO-1] = left_ptrs[row][COLNO-2]; + for (i = left_ptrs[row][COLNO-2]; i < COLNO-1; i++) + right_ptrs[row][i] = COLNO-1; + } + } + + /* + * Else we know that we are not on an edge. + */ + else if (viz_clear[row][col-1] && viz_clear[row][col+1]) { + /* Both sides clear */ + for (i = left_ptrs[row][col-1]+1; i <= col; i++) + right_ptrs[row][i] = col; + + if (!left_ptrs[row][col-1]) /* catch the end case */ + right_ptrs[row][0] = col; + + for (i = col; i < right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = col; + + if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */ + left_ptrs[row][COLNO-1] = col; + + } else if (viz_clear[row][col-1]) { + /* Left side clear, right side blocked. */ + for (i = col; i <= right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = col; + + for (i = left_ptrs[row][col-1]+1; i < col; i++) + right_ptrs[row][i] = col; + + if (!left_ptrs[row][col-1]) /* catch the end case */ + right_ptrs[row][i] = col; + + right_ptrs[row][col] = right_ptrs[row][col+1]; + + } else if (viz_clear[row][col+1]) { + /* Right side clear, left side blocked. */ + for (i = left_ptrs[row][col-1]; i <= col; i++) + right_ptrs[row][i] = col; + + for (i = col+1; i < right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = col; + + if (right_ptrs[row][col+1] == COLNO-1) /* catch the end case */ + left_ptrs[row][i] = col; + + left_ptrs[row][col] = left_ptrs[row][col-1]; + + } else { + /* Both sides blocked */ + for (i = left_ptrs[row][col-1]; i <= col; i++) + right_ptrs[row][i] = right_ptrs[row][col+1]; + + for (i = col; i <= right_ptrs[row][col+1]; i++) + left_ptrs[row][i] = left_ptrs[row][col-1]; + } +} + + +/*===========================================================================*/ +/*===========================================================================*/ +/* Use either algorithm C or D. See the config.h for more details. =========*/ + +/* + * Variables local to both Algorithms C and D. + */ +static int start_row; +static int start_col; +static int step; +static char **cs_rows; +static char *cs_left; +static char *cs_right; + +static void FDECL((*vis_func), (int,int,genericptr_t)); +static genericptr_t varg; + +/* + * Both Algorithms C and D use the following macros. + * + * good_row(z) - Return TRUE if the argument is a legal row. + * set_cs(rowp,col) - Set the local could see array. + * set_min(z) - Save the min value of the argument and the current + * row minimum. + * set_max(z) - Save the max value of the argument and the current + * row maximum. + * + * The last three macros depend on having local pointers row_min, row_max, + * and rowp being set correctly. + */ +#define set_cs(rowp,col) (rowp[col] = COULD_SEE) +#define good_row(z) ((z) >= 0 && (z) < ROWNO) +#define set_min(z) if (*row_min > (z)) *row_min = (z) +#define set_max(z) if (*row_max < (z)) *row_max = (z) +#define is_clear(row,col) viz_clear_rows[row][col] + +/* + * clear_path() expanded into 4 macros/functions: + * + * q1_path() + * q2_path() + * q3_path() + * q4_path() + * + * "Draw" a line from the start to the given location. Stop if we hit + * something that blocks light. The start and finish points themselves are + * not checked, just the points between them. These routines do _not_ + * expect to be called with the same starting and stopping point. + * + * These routines use the generalized integer Bresenham's algorithm (fast + * line drawing) for all quadrants. The algorithm was taken from _Procedural + * Elements for Computer Graphics_, by David F. Rogers. McGraw-Hill, 1985. + */ +#ifdef MACRO_CPATH /* quadrant calls are macros */ + +/* + * When called, the result is in "result". + * The first two arguments (srow,scol) are one end of the path. The next + * two arguments (row,col) are the destination. The last argument is + * used as a C language label. This means that it must be different + * in each pair of calls. + */ + +/* + * Quadrant I (step < 0). + */ +#define q1_path(srow,scol,y2,x2,label) \ +{ \ + int dx, dy; \ + register int k, err, x, y, dxs, dys; \ + \ + x = (scol); y = (srow); \ + dx = (x2) - x; dy = y - (y2); \ + \ + result = 0; /* default to a blocked path */\ + \ + dxs = dx << 1; /* save the shifted values */\ + dys = dy << 1; \ + if (dy > dx) { \ + err = dxs - dy; \ + \ + for (k = dy-1; k; k--) { \ + if (err >= 0) { \ + x++; \ + err -= dys; \ + } \ + y--; \ + err += dxs; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } else { \ + err = dys - dx; \ + \ + for (k = dx-1; k; k--) { \ + if (err >= 0) { \ + y--; \ + err -= dxs; \ + } \ + x++; \ + err += dys; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } \ + \ + result = 1; \ +} + +/* + * Quadrant IV (step > 0). + */ +#define q4_path(srow,scol,y2,x2,label) \ +{ \ + int dx, dy; \ + register int k, err, x, y, dxs, dys; \ + \ + x = (scol); y = (srow); \ + dx = (x2) - x; dy = (y2) - y; \ + \ + result = 0; /* default to a blocked path */\ + \ + dxs = dx << 1; /* save the shifted values */\ + dys = dy << 1; \ + if (dy > dx) { \ + err = dxs - dy; \ + \ + for (k = dy-1; k; k--) { \ + if (err >= 0) { \ + x++; \ + err -= dys; \ + } \ + y++; \ + err += dxs; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + \ + } else { \ + err = dys - dx; \ + \ + for (k = dx-1; k; k--) { \ + if (err >= 0) { \ + y++; \ + err -= dxs; \ + } \ + x++; \ + err += dys; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } \ + \ + result = 1; \ +} + +/* + * Quadrant II (step < 0). + */ +#define q2_path(srow,scol,y2,x2,label) \ +{ \ + int dx, dy; \ + register int k, err, x, y, dxs, dys; \ + \ + x = (scol); y = (srow); \ + dx = x - (x2); dy = y - (y2); \ + \ + result = 0; /* default to a blocked path */\ + \ + dxs = dx << 1; /* save the shifted values */\ + dys = dy << 1; \ + if (dy > dx) { \ + err = dxs - dy; \ + \ + for (k = dy-1; k; k--) { \ + if (err >= 0) { \ + x--; \ + err -= dys; \ + } \ + y--; \ + err += dxs; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } else { \ + err = dys - dx; \ + \ + for (k = dx-1; k; k--) { \ + if (err >= 0) { \ + y--; \ + err -= dxs; \ + } \ + x--; \ + err += dys; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } \ + \ + result = 1; \ +} + +/* + * Quadrant III (step > 0). + */ +#define q3_path(srow,scol,y2,x2,label) \ +{ \ + int dx, dy; \ + register int k, err, x, y, dxs, dys; \ + \ + x = (scol); y = (srow); \ + dx = x - (x2); dy = (y2) - y; \ + \ + result = 0; /* default to a blocked path */\ + \ + dxs = dx << 1; /* save the shifted values */\ + dys = dy << 1; \ + if (dy > dx) { \ + err = dxs - dy; \ + \ + for (k = dy-1; k; k--) { \ + if (err >= 0) { \ + x--; \ + err -= dys; \ + } \ + y++; \ + err += dxs; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + \ + } else { \ + err = dys - dx; \ + \ + for (k = dx-1; k; k--) { \ + if (err >= 0) { \ + y++; \ + err -= dxs; \ + } \ + x--; \ + err += dys; \ + if (!is_clear(y,x)) goto label;/* blocked */\ + } \ + } \ + \ + result = 1; \ +} + +#else /* quadrants are really functions */ + +STATIC_DCL int FDECL(_q1_path, (int,int,int,int)); +STATIC_DCL int FDECL(_q2_path, (int,int,int,int)); +STATIC_DCL int FDECL(_q3_path, (int,int,int,int)); +STATIC_DCL int FDECL(_q4_path, (int,int,int,int)); + +#define q1_path(sy,sx,y,x,dummy) result = _q1_path(sy,sx,y,x) +#define q2_path(sy,sx,y,x,dummy) result = _q2_path(sy,sx,y,x) +#define q3_path(sy,sx,y,x,dummy) result = _q3_path(sy,sx,y,x) +#define q4_path(sy,sx,y,x,dummy) result = _q4_path(sy,sx,y,x) + +/* + * Quadrant I (step < 0). + */ +STATIC_OVL int +_q1_path(srow,scol,y2,x2) + int scol, srow, y2, x2; +{ + int dx, dy; + register int k, err, x, y, dxs, dys; + + x = scol; y = srow; + dx = x2 - x; dy = y - y2; + + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + if (dy > dx) { + err = dxs - dy; + + for (k = dy-1; k; k--) { + if (err >= 0) { + x++; + err -= dys; + } + y--; + err += dxs; + if (!is_clear(y,x)) return 0; /* blocked */ + } + } else { + err = dys - dx; + + for (k = dx-1; k; k--) { + if (err >= 0) { + y--; + err -= dxs; + } + x++; + err += dys; + if (!is_clear(y,x)) return 0;/* blocked */ + } + } + + return 1; +} + +/* + * Quadrant IV (step > 0). + */ +STATIC_OVL int +_q4_path(srow,scol,y2,x2) + int scol, srow, y2, x2; +{ + int dx, dy; + register int k, err, x, y, dxs, dys; + + x = scol; y = srow; + dx = x2 - x; dy = y2 - y; + + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + if (dy > dx) { + err = dxs - dy; + + for (k = dy-1; k; k--) { + if (err >= 0) { + x++; + err -= dys; + } + y++; + err += dxs; + if (!is_clear(y,x)) return 0; /* blocked */ + } + } else { + err = dys - dx; + + for (k = dx-1; k; k--) { + if (err >= 0) { + y++; + err -= dxs; + } + x++; + err += dys; + if (!is_clear(y,x)) return 0;/* blocked */ + } + } + + return 1; +} + +/* + * Quadrant II (step < 0). + */ +STATIC_OVL int +_q2_path(srow,scol,y2,x2) + int scol, srow, y2, x2; +{ + int dx, dy; + register int k, err, x, y, dxs, dys; + + x = scol; y = srow; + dx = x - x2; dy = y - y2; + + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + if (dy > dx) { + err = dxs - dy; + + for (k = dy-1; k; k--) { + if (err >= 0) { + x--; + err -= dys; + } + y--; + err += dxs; + if (!is_clear(y,x)) return 0; /* blocked */ + } + } else { + err = dys - dx; + + for (k = dx-1; k; k--) { + if (err >= 0) { + y--; + err -= dxs; + } + x--; + err += dys; + if (!is_clear(y,x)) return 0;/* blocked */ + } + } + + return 1; +} + +/* + * Quadrant III (step > 0). + */ +STATIC_OVL int +_q3_path(srow,scol,y2,x2) + int scol, srow, y2, x2; +{ + int dx, dy; + register int k, err, x, y, dxs, dys; + + x = scol; y = srow; + dx = x - x2; dy = y2 - y; + + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + if (dy > dx) { + err = dxs - dy; + + for (k = dy-1; k; k--) { + if (err >= 0) { + x--; + err -= dys; + } + y++; + err += dxs; + if (!is_clear(y,x)) return 0; /* blocked */ + } + } else { + err = dys - dx; + + for (k = dx-1; k; k--) { + if (err >= 0) { + y++; + err -= dxs; + } + x--; + err += dys; + if (!is_clear(y,x)) return 0;/* blocked */ + } + } + + return 1; +} + +#endif /* quadrants are functions */ + +/* + * Use vision tables to determine if there is a clear path from + * (col1,row1) to (col2,row2). This is used by: + * m_cansee() + * m_canseeu() + * do_light_sources() + */ +boolean +clear_path(col1,row1,col2,row2) + int col1, row1, col2, row2; +{ + int result; + + if(col1 < col2) { + if(row1 > row2) { + q1_path(row1,col1,row2,col2,cleardone); + } else { + q4_path(row1,col1,row2,col2,cleardone); + } + } else { + if(row1 > row2) { + q2_path(row1,col1,row2,col2,cleardone); + } else if(row1 == row2 && col1 == col2) { + result = 1; + } else { + q3_path(row1,col1,row2,col2,cleardone); + } + } +#ifdef MACRO_CPATH +cleardone: +#endif + return((boolean)result); +} + +#ifdef VISION_TABLES +/*===========================================================================*\ + GENERAL LINE OF SIGHT + Algorithm D +\*===========================================================================*/ + + +/* + * Indicate caller for the shadow routines. + */ +#define FROM_RIGHT 0 +#define FROM_LEFT 1 + + +/* + * Include the table definitions. + */ +#include "vis_tab.h" + + +/* 3D table pointers. */ +static close2d *close_dy[CLOSE_MAX_BC_DY]; +static far2d *far_dy[FAR_MAX_BC_DY]; + +STATIC_DCL void FDECL(right_side, (int,int,int,int,int,int,int,char*)); +STATIC_DCL void FDECL(left_side, (int,int,int,int,int,int,int,char*)); +STATIC_DCL int FDECL(close_shadow, (int,int,int,int)); +STATIC_DCL int FDECL(far_shadow, (int,int,int,int)); + +/* + * Initialize algorithm D's table pointers. If we don't have these, + * then we do 3D table lookups. Verrrry slow. + */ +STATIC_OVL void +view_init() +{ + int i; + + for (i = 0; i < CLOSE_MAX_BC_DY; i++) + close_dy[i] = &close_table[i]; + + for (i = 0; i < FAR_MAX_BC_DY; i++) + far_dy[i] = &far_table[i]; +} + + +/* + * If the far table has an entry of OFF_TABLE, then the far block prevents + * us from seeing the location just above/below it. I.e. the first visible + * location is one *before* the block. + */ +#define OFF_TABLE 0xff + +STATIC_OVL int +close_shadow(side,this_row,block_row,block_col) + int side,this_row,block_row,block_col; +{ + register int sdy, sdx, pdy, offset; + + /* + * If on the same column (block_row = -1), then we can see it. + */ + if (block_row < 0) return block_col; + + /* Take explicit absolute values. Adjust. */ + if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; --sdy; /* src dy */ + if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; /* src dx */ + if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; /* point dy */ + + if (sdy < 0 || sdy >= CLOSE_MAX_SB_DY || sdx >= CLOSE_MAX_SB_DX || + pdy >= CLOSE_MAX_BC_DY) { + impossible("close_shadow: bad value"); + return block_col; + } + offset = close_dy[sdy]->close[sdx][pdy]; + if (side == FROM_RIGHT) + return block_col + offset; + + return block_col - offset; +} + + +STATIC_OVL int +far_shadow(side,this_row,block_row,block_col) + int side,this_row,block_row,block_col; +{ + register int sdy, sdx, pdy, offset; + + /* + * Take care of a bug that shows up only on the borders. + * + * If the block is beyond the border, then the row is negative. Return + * the block's column number (should be 0 or COLNO-1). + * + * Could easily have the column be -1, but then wouldn't know if it was + * the left or right border. + */ + if (block_row < 0) return block_col; + + /* Take explicit absolute values. Adjust. */ + if ((sdy = (start_row-block_row)) < 0) sdy = -sdy; /* src dy */ + if ((sdx = (start_col-block_col)) < 0) sdx = -sdx; --sdx; /* src dx */ + if ((pdy = (block_row-this_row)) < 0) pdy = -pdy; --pdy; /* point dy */ + + if (sdy >= FAR_MAX_SB_DY || sdx < 0 || sdx >= FAR_MAX_SB_DX || + pdy < 0 || pdy >= FAR_MAX_BC_DY) { + impossible("far_shadow: bad value"); + return block_col; + } + if ((offset = far_dy[sdy]->far_q[sdx][pdy]) == OFF_TABLE) offset = -1; + if (side == FROM_RIGHT) + return block_col + offset; + + return block_col - offset; +} + + +/* + * right_side() + * + * Figure out what could be seen on the right side of the source. + */ +STATIC_OVL void +right_side(row, cb_row, cb_col, fb_row, fb_col, left, right_mark, limits) + int row; /* current row */ + int cb_row, cb_col; /* close block row and col */ + int fb_row, fb_col; /* far block row and col */ + int left; /* left mark of the previous row */ + int right_mark; /* right mark of previous row */ + char *limits; /* points at range limit for current row, or NULL */ +{ + register int i; + register char *rowp; + int hit_stone = 0; + int left_shadow, right_shadow, loc_right; + int lblock_col; /* local block column (current row) */ + int nrow, deeper; + char *row_min; /* left most */ + char *row_max; /* right most */ + int lim_max; /* right most limit of circle */ + +#ifdef GCC_WARN + rowp = 0; +#endif + nrow = row + step; + deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1))); + if(!vis_func) { + rowp = cs_rows[row]; + row_min = &cs_left[row]; + row_max = &cs_right[row]; + } + if(limits) { + lim_max = start_col + *limits; + if(lim_max > COLNO-1) lim_max = COLNO-1; + if(right_mark > lim_max) right_mark = lim_max; + limits++; /* prepare for next row */ + } else + lim_max = COLNO-1; + + /* + * Get the left shadow from the close block. This value could be + * illegal. + */ + left_shadow = close_shadow(FROM_RIGHT,row,cb_row,cb_col); + + /* + * Mark all stone walls as seen before the left shadow. All this work + * for a special case. + * + * NOTE. With the addition of this code in here, it is now *required* + * for the algorithm to work correctly. If this is commented out, + * change the above assignment so that left and not left_shadow is the + * variable that gets the shadow. + */ + while (left <= right_mark) { + loc_right = right_ptrs[row][left]; + if(loc_right > lim_max) loc_right = lim_max; + if (viz_clear_rows[row][left]) { + if (loc_right >= left_shadow) { + left = left_shadow; /* opening ends beyond shadow */ + break; + } + left = loc_right; + loc_right = right_ptrs[row][left]; + if(loc_right > lim_max) loc_right = lim_max; + if (left == loc_right) return; /* boundary */ + + /* Shadow covers opening, beyond right mark */ + if (left == right_mark && left_shadow > right_mark) return; + } + + if (loc_right > right_mark) /* can't see stone beyond the mark */ + loc_right = right_mark; + + if(vis_func) { + for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg); + } else { + for (i = left; i <= loc_right; i++) set_cs(rowp,i); + set_min(left); set_max(loc_right); + } + + if (loc_right == right_mark) return; /* all stone */ + if (loc_right >= left_shadow) hit_stone = 1; + left = loc_right + 1; + } + + /* + * At this point we are at the first visible clear spot on or beyond + * the left shadow, unless the left shadow is an illegal value. If we + * have "hit stone" then we have a stone wall just to our left. + */ + + /* + * Get the right shadow. Make sure that it is a legal value. + */ + if ((right_shadow = far_shadow(FROM_RIGHT,row,fb_row,fb_col)) >= COLNO) + right_shadow = COLNO-1; + /* + * Make vertical walls work the way we want them. In this case, we + * note when the close block blocks the column just above/beneath + * it (right_shadow < fb_col [actually right_shadow == fb_col-1]). If + * the location is filled, then we want to see it, so we put the + * right shadow back (same as fb_col). + */ + if (right_shadow < fb_col && !viz_clear_rows[row][fb_col]) + right_shadow = fb_col; + if(right_shadow > lim_max) right_shadow = lim_max; + + /* + * Main loop. Within the range of sight of the previous row, mark all + * stone walls as seen. Follow open areas recursively. + */ + while (left <= right_mark) { + /* Get the far right of the opening or wall */ + loc_right = right_ptrs[row][left]; + if(loc_right > lim_max) loc_right = lim_max; + + if (!viz_clear_rows[row][left]) { + hit_stone = 1; /* use stone on this row as close block */ + /* + * We can see all of the wall until the next open spot or the + * start of the shadow caused by the far block (right). + * + * Can't see stone beyond the right mark. + */ + if (loc_right > right_mark) loc_right = right_mark; + + if(vis_func) { + for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg); + } else { + for (i = left; i <= loc_right; i++) set_cs(rowp,i); + set_min(left); set_max(loc_right); + } + + if (loc_right == right_mark) return; /* hit the end */ + left = loc_right + 1; + loc_right = right_ptrs[row][left]; + if(loc_right > lim_max) loc_right = lim_max; + /* fall through... we know at least one position is visible */ + } + + /* + * We are in an opening. + * + * If this is the first open spot since the could see area (this is + * true if we have hit stone), get the shadow generated by the wall + * just to our left. + */ + if (hit_stone) { + lblock_col = left-1; /* local block column */ + left = close_shadow(FROM_RIGHT,row,row,lblock_col); + if (left > lim_max) break; /* off the end */ + } + + /* + * Check if the shadow covers the opening. If it does, then + * move to end of the opening. A shadow generated on from a + * wall on this row does *not* cover the wall on the right + * of the opening. + */ + if (left >= loc_right) { + if (loc_right == lim_max) { /* boundary */ + if (left == lim_max) { + if(vis_func) (*vis_func)(lim_max, row, varg); + else { + set_cs(rowp,lim_max); /* last pos */ + set_max(lim_max); + } + } + return; /* done */ + } + left = loc_right; + continue; + } + + /* + * If the far wall of the opening (loc_right) is closer than the + * shadow limit imposed by the far block (right) then use the far + * wall as our new far block when we recurse. + * + * If the limits are the the same, and the far block really exists + * (fb_row >= 0) then do the same as above. + * + * Normally, the check would be for the far wall being closer OR EQUAL + * to the shadow limit. However, there is a bug that arises from the + * fact that the clear area pointers end in an open space (if it + * exists) on a boundary. This then makes a far block exist where it + * shouldn't --- on a boundary. To get around that, I had to + * introduce the concept of a non-existent far block (when the + * row < 0). Next I have to check for it. Here is where that check + * exists. + */ + if ((loc_right < right_shadow) || + (fb_row >= 0 && loc_right == right_shadow)) { + if(vis_func) { + for (i = left; i <= loc_right; i++) (*vis_func)(i, row, varg); + } else { + for (i = left; i <= loc_right; i++) set_cs(rowp,i); + set_min(left); set_max(loc_right); + } + + if (deeper) { + if (hit_stone) + right_side(nrow,row,lblock_col,row,loc_right, + left,loc_right,limits); + else + right_side(nrow,cb_row,cb_col,row,loc_right, + left,loc_right,limits); + } + + /* + * The following line, setting hit_stone, is needed for those + * walls that are only 1 wide. If hit stone is *not* set and + * the stone is only one wide, then the close block is the old + * one instead one on the current row. A way around having to + * set it here is to make left = loc_right (not loc_right+1) and + * let the outer loop take care of it. However, if we do that + * then we then have to check for boundary conditions here as + * well. + */ + hit_stone = 1; + + left = loc_right+1; + } + /* + * The opening extends beyond the right mark. This means that + * the next far block is the current far block. + */ + else { + if(vis_func) { + for (i=left; i <= right_shadow; i++) (*vis_func)(i, row, varg); + } else { + for (i = left; i <= right_shadow; i++) set_cs(rowp,i); + set_min(left); set_max(right_shadow); + } + + if (deeper) { + if (hit_stone) + right_side(nrow, row,lblock_col,fb_row,fb_col, + left,right_shadow,limits); + else + right_side(nrow,cb_row, cb_col,fb_row,fb_col, + left,right_shadow,limits); + } + + return; /* we're outta here */ + } + } +} + + +/* + * left_side() + * + * This routine is the mirror image of right_side(). Please see right_side() + * for blow by blow comments. + */ +STATIC_OVL void +left_side(row, cb_row, cb_col, fb_row, fb_col, left_mark, right, limits) + int row; /* the current row */ + int cb_row, cb_col; /* close block row and col */ + int fb_row, fb_col; /* far block row and col */ + int left_mark; /* left mark of previous row */ + int right; /* right mark of the previous row */ + char *limits; +{ + register int i; + register char *rowp; + int hit_stone = 0; + int left_shadow, right_shadow, loc_left; + int lblock_col; /* local block column (current row) */ + int nrow, deeper; + char *row_min; /* left most */ + char *row_max; /* right most */ + int lim_min; + +#ifdef GCC_WARN + rowp = 0; +#endif + nrow = row + step; + deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1))); + if(!vis_func) { + rowp = cs_rows[row]; + row_min = &cs_left[row]; + row_max = &cs_right[row]; + } + if(limits) { + lim_min = start_col - *limits; + if(lim_min < 0) lim_min = 0; + if(left_mark < lim_min) left_mark = lim_min; + limits++; /* prepare for next row */ + } else + lim_min = 0; + + /* This value could be illegal. */ + right_shadow = close_shadow(FROM_LEFT,row,cb_row,cb_col); + + while ( right >= left_mark ) { + loc_left = left_ptrs[row][right]; + if(loc_left < lim_min) loc_left = lim_min; + if (viz_clear_rows[row][right]) { + if (loc_left <= right_shadow) { + right = right_shadow; /* opening ends beyond shadow */ + break; + } + right = loc_left; + loc_left = left_ptrs[row][right]; + if(loc_left < lim_min) loc_left = lim_min; + if (right == loc_left) return; /* boundary */ + } + + if (loc_left < left_mark) /* can't see beyond the left mark */ + loc_left = left_mark; + + if(vis_func) { + for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg); + } else { + for (i = loc_left; i <= right; i++) set_cs(rowp,i); + set_min(loc_left); set_max(right); + } + + if (loc_left == left_mark) return; /* all stone */ + if (loc_left <= right_shadow) hit_stone = 1; + right = loc_left - 1; + } + + /* At first visible clear spot on or beyond the right shadow. */ + + if ((left_shadow = far_shadow(FROM_LEFT,row,fb_row,fb_col)) < 0) + left_shadow = 0; + + /* Do vertical walls as we want. */ + if (left_shadow > fb_col && !viz_clear_rows[row][fb_col]) + left_shadow = fb_col; + if(left_shadow < lim_min) left_shadow = lim_min; + + while (right >= left_mark) { + loc_left = left_ptrs[row][right]; + + if (!viz_clear_rows[row][right]) { + hit_stone = 1; /* use stone on this row as close block */ + + /* We can only see walls until the left mark */ + if (loc_left < left_mark) loc_left = left_mark; + + if(vis_func) { + for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg); + } else { + for (i = loc_left; i <= right; i++) set_cs(rowp,i); + set_min(loc_left); set_max(right); + } + + if (loc_left == left_mark) return; /* hit end */ + right = loc_left - 1; + loc_left = left_ptrs[row][right]; + if (loc_left < lim_min) loc_left = lim_min; + /* fall through...*/ + } + + /* We are in an opening. */ + if (hit_stone) { + lblock_col = right+1; /* stone block (local) */ + right = close_shadow(FROM_LEFT,row,row,lblock_col); + if (right < lim_min) return; /* off the end */ + } + + /* Check if the shadow covers the opening. */ + if (right <= loc_left) { + /* Make a boundary condition work. */ + if (loc_left == lim_min) { /* at boundary */ + if (right == lim_min) { + if(vis_func) (*vis_func)(lim_min, row, varg); + else { + set_cs(rowp,lim_min); /* caught the last pos */ + set_min(lim_min); + } + } + return; /* and break out the loop */ + } + + right = loc_left; + continue; + } + + /* If the far wall of the opening is closer than the shadow limit. */ + if ((loc_left > left_shadow) || + (fb_row >= 0 && loc_left == left_shadow)) { + if(vis_func) { + for (i = loc_left; i <= right; i++) (*vis_func)(i, row, varg); + } else { + for (i = loc_left; i <= right; i++) set_cs(rowp,i); + set_min(loc_left); set_max(right); + } + + if (deeper) { + if (hit_stone) + left_side(nrow,row,lblock_col,row,loc_left, + loc_left,right,limits); + else + left_side(nrow,cb_row,cb_col,row,loc_left, + loc_left,right,limits); + } + + hit_stone = 1; /* needed for walls of width 1 */ + right = loc_left-1; + } + /* The opening extends beyond the left mark. */ + else { + if(vis_func) { + for (i=left_shadow; i <= right; i++) (*vis_func)(i, row, varg); + } else { + for (i = left_shadow; i <= right; i++) set_cs(rowp,i); + set_min(left_shadow); set_max(right); + } + + if (deeper) { + if (hit_stone) + left_side(nrow,row,lblock_col,fb_row,fb_col, + left_shadow,right,limits); + else + left_side(nrow,cb_row,cb_col,fb_row,fb_col, + left_shadow,right,limits); + } + + return; /* we're outta here */ + } + + } +} + +/* + * view_from + * + * Calculate a view from the given location. Initialize and fill a + * ROWNOxCOLNO array (could_see) with all the locations that could be + * seen from the source location. Initialize and fill the left most + * and right most boundaries of what could be seen. + */ +STATIC_OVL void +view_from(srow,scol,loc_cs_rows,left_most,right_most, range, func, arg) + int srow, scol; /* source row and column */ + char **loc_cs_rows; /* could_see array (row pointers) */ + char *left_most, *right_most; /* limits of what could be seen */ + int range; /* 0 if unlimited */ + void FDECL((*func), (int,int,genericptr_t)); + genericptr_t arg; +{ + register int i; + char *rowp; + int nrow, left, right, left_row, right_row; + char *limits; + + /* Set globals for near_shadow(), far_shadow(), etc. to use. */ + start_col = scol; + start_row = srow; + cs_rows = loc_cs_rows; + cs_left = left_most; + cs_right = right_most; + vis_func = func; + varg = arg; + + /* Find the left and right limits of sight on the starting row. */ + if (viz_clear_rows[srow][scol]) { + left = left_ptrs[srow][scol]; + right = right_ptrs[srow][scol]; + } else { + left = (!scol) ? 0 : + (viz_clear_rows[srow][scol-1] ? left_ptrs[srow][scol-1] : scol-1); + right = (scol == COLNO-1) ? COLNO-1 : + (viz_clear_rows[srow][scol+1] ? right_ptrs[srow][scol+1] : scol+1); + } + + if(range) { + if(range > MAX_RADIUS || range < 1) + panic("view_from called with range %d", range); + limits = circle_ptr(range) + 1; /* start at next row */ + if(left < scol - range) left = scol - range; + if(right > scol + range) right = scol + range; + } else + limits = (char*) 0; + + if(func) { + for (i = left; i <= right; i++) (*func)(i, srow, arg); + } else { + /* Row optimization */ + rowp = cs_rows[srow]; + + /* We know that we can see our row. */ + for (i = left; i <= right; i++) set_cs(rowp,i); + cs_left[srow] = left; + cs_right[srow] = right; + } + + /* The far block has a row number of -1 if we are on an edge. */ + right_row = (right == COLNO-1) ? -1 : srow; + left_row = (!left) ? -1 : srow; + + /* + * Check what could be seen in quadrants. + */ + if ( (nrow = srow+1) < ROWNO ) { + step = 1; /* move down */ + if (scol= 0 ) { + step = -1; /* move up */ + if (scol= *(limits+1))); + if(!vis_func) { + rowp = cs_rows[row]; /* optimization */ + row_min = &cs_left[row]; + row_max = &cs_right[row]; + } + if(limits) { + lim_max = start_col + *limits; + if(lim_max > COLNO-1) lim_max = COLNO-1; + if(right_mark > lim_max) right_mark = lim_max; + limits++; /* prepare for next row */ + } else + lim_max = COLNO-1; + + while (left <= right_mark) { + right_edge = right_ptrs[row][left]; + if(right_edge > lim_max) right_edge = lim_max; + + if (!is_clear(row,left)) { + /* + * Jump to the far side of a stone wall. We can set all + * the points in between as seen. + * + * If the right edge goes beyond the right mark, check to see + * how much we can see. + */ + if (right_edge > right_mark) { + /* + * If the mark on the previous row was a clear position, + * the odds are that we can actually see part of the wall + * beyond the mark on this row. If so, then see one beyond + * the mark. Otherwise don't. This is a kludge so corners + * with an adjacent doorway show up in nethack. + */ + right_edge = is_clear(row-step,right_mark) ? + right_mark+1 : right_mark; + } + if(vis_func) { + for (i = left; i <= right_edge; i++) (*vis_func)(i, row, varg); + } else { + for (i = left; i <= right_edge; i++) set_cs(rowp,i); + set_min(left); set_max(right_edge); + } + left = right_edge + 1; /* no limit check necessary */ + continue; + } + + /* No checking needed if our left side is the start column. */ + if (left != start_col) { + /* + * Find the left side. Move right until we can see it or we run + * into a wall. + */ + for (; left <= right_edge; left++) { + if (step < 0) { + q1_path(start_row,start_col,row,left,rside1); + } else { + q4_path(start_row,start_col,row,left,rside1); + } +rside1: /* used if q?_path() is a macro */ + if (result) break; + } + + /* + * Check for boundary conditions. We *need* check (2) to break + * an infinite loop where: + * + * left == right_edge == right_mark == lim_max. + * + */ + if (left > lim_max) return; /* check (1) */ + if (left == lim_max) { /* check (2) */ + if(vis_func) (*vis_func)(lim_max, row, varg); + else { + set_cs(rowp,lim_max); + set_max(lim_max); + } + return; + } + /* + * Check if we can see any spots in the opening. We might + * (left == right_edge) or might not (left == right_edge+1) have + * been able to see the far wall. Make sure we *can* see the + * wall (remember, we can see the spot above/below this one) + * by backing up. + */ + if (left >= right_edge) { + left = right_edge; /* for the case left == right_edge+1 */ + continue; + } + } + + /* + * Find the right side. If the marker from the previous row is + * closer than the edge on this row, then we have to check + * how far we can see around the corner (under the overhang). Stop + * at the first non-visible spot or we actually hit the far wall. + * + * Otherwise, we know we can see the right edge of the current row. + * + * This must be a strict less than so that we can always see a + * horizontal wall, even if it is adjacent to us. + */ + if (right_mark < right_edge) { + for (right = right_mark; right <= right_edge; right++) { + if (step < 0) { + q1_path(start_row,start_col,row,right,rside2); + } else { + q4_path(start_row,start_col,row,right,rside2); + } +rside2: /* used if q?_path() is a macro */ + if (!result) break; + } + --right; /* get rid of the last increment */ + } + else + right = right_edge; + + /* + * We have the range that we want. Set the bits. Note that + * there is no else --- we no longer handle splinters. + */ + if (left <= right) { + /* + * An ugly special case. If you are adjacent to a vertical wall + * and it has a break in it, then the right mark is set to be + * start_col. We *want* to be able to see adjacent vertical + * walls, so we have to set it back. + */ + if (left == right && left == start_col && + start_col < (COLNO-1) && !is_clear(row,start_col+1)) + right = start_col+1; + + if(right > lim_max) right = lim_max; + /* set the bits */ + if(vis_func) + for (i = left; i <= right; i++) (*vis_func)(i, row, varg); + else { + for (i = left; i <= right; i++) set_cs(rowp,i); + set_min(left); set_max(right); + } + + /* recursive call for next finger of light */ + if (deeper) right_side(nrow,left,right,limits); + left = right + 1; /* no limit check necessary */ + } + } +} + + +/* + * This routine is the mirror image of right_side(). See right_side() for + * extensive comments. + */ +STATIC_OVL void +left_side(row, left_mark, right, limits) + int row, left_mark, right; + char *limits; +{ + int left, left_edge, nrow, deeper, result; + register int i; + register char *rowp; + char *row_min, *row_max; + int lim_min; + +#ifdef GCC_WARN + rowp = row_min = row_max = 0; +#endif + nrow = row+step; + deeper = good_row(nrow) && (!limits || (*limits >= *(limits+1))); + if(!vis_func) { + rowp = cs_rows[row]; + row_min = &cs_left[row]; + row_max = &cs_right[row]; + } + if(limits) { + lim_min = start_col - *limits; + if(lim_min < 0) lim_min = 0; + if(left_mark < lim_min) left_mark = lim_min; + limits++; /* prepare for next row */ + } else + lim_min = 0; + + while (right >= left_mark) { + left_edge = left_ptrs[row][right]; + if(left_edge < lim_min) left_edge = lim_min; + + if (!is_clear(row,right)) { + /* Jump to the far side of a stone wall. */ + if (left_edge < left_mark) { + /* Maybe see more (kludge). */ + left_edge = is_clear(row-step,left_mark) ? + left_mark-1 : left_mark; + } + if(vis_func) { + for (i = left_edge; i <= right; i++) (*vis_func)(i, row, varg); + } else { + for (i = left_edge; i <= right; i++) set_cs(rowp,i); + set_min(left_edge); set_max(right); + } + right = left_edge - 1; /* no limit check necessary */ + continue; + } + + if (right != start_col) { + /* Find the right side. */ + for (; right >= left_edge; right--) { + if (step < 0) { + q2_path(start_row,start_col,row,right,lside1); + } else { + q3_path(start_row,start_col,row,right,lside1); + } +lside1: /* used if q?_path() is a macro */ + if (result) break; + } + + /* Check for boundary conditions. */ + if (right < lim_min) return; + if (right == lim_min) { + if(vis_func) (*vis_func)(lim_min, row, varg); + else { + set_cs(rowp,lim_min); + set_min(lim_min); + } + return; + } + /* Check if we can see any spots in the opening. */ + if (right <= left_edge) { + right = left_edge; + continue; + } + } + + /* Find the left side. */ + if (left_mark > left_edge) { + for (left = left_mark; left >= left_edge; --left) { + if (step < 0) { + q2_path(start_row,start_col,row,left,lside2); + } else { + q3_path(start_row,start_col,row,left,lside2); + } +lside2: /* used if q?_path() is a macro */ + if (!result) break; + } + left++; /* get rid of the last decrement */ + } + else + left = left_edge; + + if (left <= right) { + /* An ugly special case. */ + if (left == right && right == start_col && + start_col > 0 && !is_clear(row,start_col-1)) + left = start_col-1; + + if(left < lim_min) left = lim_min; + if(vis_func) + for (i = left; i <= right; i++) (*vis_func)(i, row, varg); + else { + for (i = left; i <= right; i++) set_cs(rowp,i); + set_min(left); set_max(right); + } + + /* Recurse */ + if (deeper) left_side(nrow,left,right,limits); + right = left - 1; /* no limit check necessary */ + } + } +} + + +/* + * Calculate all possible visible locations from the given location + * (srow,scol). NOTE this is (y,x)! Mark the visible locations in the + * array provided. + */ +STATIC_OVL void +view_from(srow, scol, loc_cs_rows, left_most, right_most, range, func, arg) + int srow, scol; /* starting row and column */ + char **loc_cs_rows; /* pointers to the rows of the could_see array */ + char *left_most; /* min mark on each row */ + char *right_most; /* max mark on each row */ + int range; /* 0 if unlimited */ + void FDECL((*func), (int,int,genericptr_t)); + genericptr_t arg; +{ + register int i; /* loop counter */ + char *rowp; /* optimization for setting could_see */ + int nrow; /* the next row */ + int left; /* the left-most visible column */ + int right; /* the right-most visible column */ + char *limits; /* range limit for next row */ + + /* Set globals for q?_path(), left_side(), and right_side() to use. */ + start_col = scol; + start_row = srow; + cs_rows = loc_cs_rows; /* 'could see' rows */ + cs_left = left_most; + cs_right = right_most; + vis_func = func; + varg = arg; + + /* + * Determine extent of sight on the starting row. + */ + if (is_clear(srow,scol)) { + left = left_ptrs[srow][scol]; + right = right_ptrs[srow][scol]; + } else { + /* + * When in stone, you can only see your adjacent squares, unless + * you are on an array boundary or a stone/clear boundary. + */ + left = (!scol) ? 0 : + (is_clear(srow,scol-1) ? left_ptrs[srow][scol-1] : scol-1); + right = (scol == COLNO-1) ? COLNO-1 : + (is_clear(srow,scol+1) ? right_ptrs[srow][scol+1] : scol+1); + } + + if(range) { + if(range > MAX_RADIUS || range < 1) + panic("view_from called with range %d", range); + limits = circle_ptr(range) + 1; /* start at next row */ + if(left < scol - range) left = scol - range; + if(right > scol + range) right = scol + range; + } else + limits = (char*) 0; + + if(func) { + for (i = left; i <= right; i++) (*func)(i, srow, arg); + } else { + /* Row pointer optimization. */ + rowp = cs_rows[srow]; + + /* We know that we can see our row. */ + for (i = left; i <= right; i++) set_cs(rowp,i); + cs_left[srow] = left; + cs_right[srow] = right; + } + + /* + * Check what could be seen in quadrants. We need to check for valid + * rows here, since we don't do it in the routines right_side() and + * left_side() [ugliness to remove extra routine calls]. + */ + if ( (nrow = srow+1) < ROWNO ) { /* move down */ + step = 1; + if (scol < COLNO-1) right_side(nrow, scol, right, limits); + if (scol) left_side (nrow, left, scol, limits); + } + + if ( (nrow = srow-1) >= 0 ) { /* move up */ + step = -1; + if (scol < COLNO-1) right_side(nrow, scol, right, limits); + if (scol) left_side (nrow, left, scol, limits); + } +} + +#endif /*===== End of algorithm C =====*/ + +/* + * AREA OF EFFECT "ENGINE" + * + * Calculate all possible visible locations as viewed from the given location + * (srow,scol) within the range specified. Perform "func" with (x, y) args and + * additional argument "arg" for each square. + * + * If not centered on the hero, just forward arguments to view_from(); it + * will call "func" when necessary. If the hero is the center, use the + * vision matrix and reduce extra work. + */ +void +do_clear_area(scol,srow,range,func,arg) + int scol, srow, range; + void FDECL((*func), (int,int,genericptr_t)); + genericptr_t arg; +{ + /* If not centered on hero, do the hard work of figuring the area */ + if (scol != u.ux || srow != u.uy) + view_from(srow, scol, (char **)0, (char *)0, (char *)0, + range, func, arg); + else { + register int x; + int y, min_x, max_x, max_y, offset; + char *limits; + + if (range > MAX_RADIUS || range < 1) + panic("do_clear_area: illegal range %d", range); + if(vision_full_recalc) + vision_recalc(0); /* recalc vision if dirty */ + limits = circle_ptr(range); + if ((max_y = (srow + range)) >= ROWNO) max_y = ROWNO-1; + if ((y = (srow - range)) < 0) y = 0; + for (; y <= max_y; y++) { + offset = limits[v_abs(y-srow)]; + if((min_x = (scol - offset)) < 0) min_x = 0; + if((max_x = (scol + offset)) >= COLNO) max_x = COLNO-1; + for (x = min_x; x <= max_x; x++) + if (couldsee(x, y)) + (*func)(x, y, arg); + } + } +} + +/*vision.c*/ diff --git a/src/weapon.c b/src/weapon.c new file mode 100644 index 0000000..a595df7 --- /dev/null +++ b/src/weapon.c @@ -0,0 +1,1319 @@ +/* SCCS Id: @(#)weapon.c 3.4 2002/11/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This module contains code for calculation of "to hit" and damage + * bonuses for any given weapon used, as well as weapons selection + * code for monsters. + */ +#include "hack.h" + +/* Categories whose names don't come from OBJ_NAME(objects[type]) + */ +#define PN_BARE_HANDED (-1) /* includes martial arts */ +#define PN_TWO_WEAPONS (-2) +#define PN_RIDING (-3) +#define PN_POLEARMS (-4) +#define PN_SABER (-5) +#define PN_HAMMER (-6) +#define PN_WHIP (-7) +#define PN_ATTACK_SPELL (-8) +#define PN_HEALING_SPELL (-9) +#define PN_DIVINATION_SPELL (-10) +#define PN_ENCHANTMENT_SPELL (-11) +#define PN_CLERIC_SPELL (-12) +#define PN_ESCAPE_SPELL (-13) +#define PN_MATTER_SPELL (-14) + +STATIC_DCL void FDECL(give_may_advance_msg, (int)); + +#ifndef OVLB + +STATIC_DCL NEARDATA const short skill_names_indices[]; +STATIC_DCL NEARDATA const char *odd_skill_names[]; +STATIC_DCL NEARDATA const char *barehands_or_martial[]; + +#else /* OVLB */ + +STATIC_VAR NEARDATA const short skill_names_indices[P_NUM_SKILLS] = { + 0, DAGGER, KNIFE, AXE, + PICK_AXE, SHORT_SWORD, BROADSWORD, LONG_SWORD, + TWO_HANDED_SWORD, SCIMITAR, PN_SABER, CLUB, + MACE, MORNING_STAR, FLAIL, + PN_HAMMER, QUARTERSTAFF, PN_POLEARMS, SPEAR, + JAVELIN, TRIDENT, LANCE, BOW, + SLING, CROSSBOW, DART, + SHURIKEN, BOOMERANG, PN_WHIP, UNICORN_HORN, + PN_ATTACK_SPELL, PN_HEALING_SPELL, + PN_DIVINATION_SPELL, PN_ENCHANTMENT_SPELL, + PN_CLERIC_SPELL, PN_ESCAPE_SPELL, + PN_MATTER_SPELL, + PN_BARE_HANDED, PN_TWO_WEAPONS, +#ifdef STEED + PN_RIDING +#endif +}; + +/* note: entry [0] isn't used */ +STATIC_VAR NEARDATA const char * const odd_skill_names[] = { + "no skill", + "bare hands", /* use barehands_or_martial[] instead */ + "two weapon combat", + "riding", + "polearms", + "saber", + "hammer", + "whip", + "attack spells", + "healing spells", + "divination spells", + "enchantment spells", + "clerical spells", + "escape spells", + "matter spells", +}; +/* indexed vis `is_martial() */ +STATIC_VAR NEARDATA const char * const barehands_or_martial[] = { + "bare handed combat", "martial arts" +}; + +STATIC_OVL void +give_may_advance_msg(skill) +int skill; +{ + You_feel("more confident in your %sskills.", + skill == P_NONE ? + "" : + skill <= P_LAST_WEAPON ? + "weapon " : + skill <= P_LAST_SPELL ? + "spell casting " : + "fighting "); +} + +#endif /* OVLB */ + +STATIC_DCL boolean FDECL(can_advance, (int, BOOLEAN_P)); +STATIC_DCL boolean FDECL(could_advance, (int)); +STATIC_DCL boolean FDECL(peaked_skill, (int)); +STATIC_DCL int FDECL(slots_required, (int)); + +#ifdef OVL1 + +STATIC_DCL char *FDECL(skill_level_name, (int,char *)); +STATIC_DCL void FDECL(skill_advance, (int)); + +#endif /* OVL1 */ + +#define P_NAME(type) ((skill_names_indices[type] > 0) ? \ + OBJ_NAME(objects[skill_names_indices[type]]) : \ + (type == P_BARE_HANDED_COMBAT) ? \ + barehands_or_martial[martial_bonus()] : \ + odd_skill_names[-skill_names_indices[type]]) + +#ifdef OVLB +static NEARDATA const char kebabable[] = { + S_XORN, S_DRAGON, S_JABBERWOCK, S_NAGA, S_GIANT, '\0' +}; + +/* + * hitval returns an integer representing the "to hit" bonuses + * of "otmp" against the monster. + */ +int +hitval(otmp, mon) +struct obj *otmp; +struct monst *mon; +{ + int tmp = 0; + struct permonst *ptr = mon->data; + boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp)); + + if (Is_weapon) + tmp += otmp->spe; + +/* Put weapon specific "to hit" bonuses in below: */ + tmp += objects[otmp->otyp].oc_hitbon; + +/* Put weapon vs. monster type "to hit" bonuses in below: */ + + /* Blessed weapons used against undead or demons */ + if (Is_weapon && otmp->blessed && + (is_demon(ptr) || is_undead(ptr))) tmp += 2; + + if (is_spear(otmp) && + index(kebabable, ptr->mlet)) tmp += 2; + + /* trident is highly effective against swimmers */ + if (otmp->otyp == TRIDENT && is_swimmer(ptr)) { + if (is_pool(mon->mx, mon->my)) tmp += 4; + else if (ptr->mlet == S_EEL || ptr->mlet == S_SNAKE) tmp += 2; + } + + /* Picks used against xorns and earth elementals */ + if (is_pick(otmp) && + (passes_walls(ptr) && thick_skinned(ptr))) tmp += 2; + +#ifdef INVISIBLE_OBJECTS + /* Invisible weapons against monsters who can't see invisible */ + if (otmp->oinvis && !perceives(ptr)) tmp += 3; +#endif + + /* Check specially named weapon "to hit" bonuses */ + if (otmp->oartifact) tmp += spec_abon(otmp, mon); + + return tmp; +} + +/* Historical note: The original versions of Hack used a range of damage + * which was similar to, but not identical to the damage used in Advanced + * Dungeons and Dragons. I figured that since it was so close, I may as well + * make it exactly the same as AD&D, adding some more weapons in the process. + * This has the advantage that it is at least possible that the player would + * already know the damage of at least some of the weapons. This was circa + * 1987 and I used the table from Unearthed Arcana until I got tired of typing + * them in (leading to something of an imbalance towards weapons early in + * alphabetical order). The data structure still doesn't include fields that + * fully allow the appropriate damage to be described (there's no way to say + * 3d6 or 1d6+1) so we add on the extra damage in dmgval() if the weapon + * doesn't do an exact die of damage. + * + * Of course new weapons were added later in the development of Nethack. No + * AD&D consistency was kept, but most of these don't exist in AD&D anyway. + * + * Second edition AD&D came out a few years later; luckily it used the same + * table. As of this writing (1999), third edition is in progress but not + * released. Let's see if the weapon table stays the same. --KAA + * October 2000: It didn't. Oh, well. + */ + +/* + * dmgval returns an integer representing the damage bonuses + * of "otmp" against the monster. + */ +int +dmgval(otmp, mon) +struct obj *otmp; +struct monst *mon; +{ + int tmp = 0, otyp = otmp->otyp; + struct permonst *ptr = mon->data; + boolean Is_weapon = (otmp->oclass == WEAPON_CLASS || is_weptool(otmp)); + + if (otyp == CREAM_PIE) return 0; + + if (bigmonst(ptr)) { + if (objects[otyp].oc_wldam) + tmp = rnd(objects[otyp].oc_wldam); + switch (otyp) { + case IRON_CHAIN: + case CROSSBOW_BOLT: + case MORNING_STAR: + case PARTISAN: + case RUNESWORD: + case ELVEN_BROADSWORD: + case BROADSWORD: tmp++; break; + + case FLAIL: + case RANSEUR: + case VOULGE: tmp += rnd(4); break; + + case ACID_VENOM: + case HALBERD: + case SPETUM: tmp += rnd(6); break; + + case BATTLE_AXE: + case BARDICHE: + case TRIDENT: tmp += d(2,4); break; + + case TSURUGI: + case DWARVISH_MATTOCK: + case TWO_HANDED_SWORD: tmp += d(2,6); break; + } + } else { + if (objects[otyp].oc_wsdam) + tmp = rnd(objects[otyp].oc_wsdam); + switch (otyp) { + case IRON_CHAIN: + case CROSSBOW_BOLT: + case MACE: + case WAR_HAMMER: + case FLAIL: + case SPETUM: + case TRIDENT: tmp++; break; + + case BATTLE_AXE: + case BARDICHE: + case BILL_GUISARME: + case GUISARME: + case LUCERN_HAMMER: + case MORNING_STAR: + case RANSEUR: + case BROADSWORD: + case ELVEN_BROADSWORD: + case RUNESWORD: + case VOULGE: tmp += rnd(4); break; + + case ACID_VENOM: tmp += rnd(6); break; + } + } + if (Is_weapon) { + tmp += otmp->spe; + /* negative enchantment mustn't produce negative damage */ + if (tmp < 0) tmp = 0; + } + + if (objects[otyp].oc_material <= LEATHER && thick_skinned(ptr)) + /* thick skinned/scaled creatures don't feel it */ + tmp = 0; + if (ptr == &mons[PM_SHADE] && objects[otyp].oc_material != SILVER) + tmp = 0; + + /* "very heavy iron ball"; weight increase is in increments of 160 */ + if (otyp == HEAVY_IRON_BALL && tmp > 0) { + int wt = (int)objects[HEAVY_IRON_BALL].oc_weight; + + if ((int)otmp->owt > wt) { + wt = ((int)otmp->owt - wt) / 160; + tmp += rnd(4 * wt); + if (tmp > 25) tmp = 25; /* objects[].oc_wldam */ + } + } + +/* Put weapon vs. monster type damage bonuses in below: */ + if (Is_weapon || otmp->oclass == GEM_CLASS || + otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS) { + int bonus = 0; + + if (otmp->blessed && (is_undead(ptr) || is_demon(ptr))) + bonus += rnd(4); + if (is_axe(otmp) && is_wooden(ptr)) + bonus += rnd(4); + if (objects[otyp].oc_material == SILVER && hates_silver(ptr)) + bonus += rnd(20); + + /* if the weapon is going to get a double damage bonus, adjust + this bonus so that effectively it's added after the doubling */ + if (bonus > 1 && otmp->oartifact && spec_dbon(otmp, mon, 25) >= 25) + bonus = (bonus + 1) / 2; + + tmp += bonus; + } + + if (tmp > 0) { + /* It's debateable whether a rusted blunt instrument + should do less damage than a pristine one, since + it will hit with essentially the same impact, but + there ought to some penalty for using damaged gear + so always subtract erosion even for blunt weapons. */ + tmp -= greatest_erosion(otmp); + if (tmp < 1) tmp = 1; + } + + return(tmp); +} + +#endif /* OVLB */ +#ifdef OVL0 + +STATIC_DCL struct obj *FDECL(oselect, (struct monst *,int)); +#define Oselect(x) if ((otmp = oselect(mtmp, x)) != 0) return(otmp); + +STATIC_OVL struct obj * +oselect(mtmp, x) +struct monst *mtmp; +int x; +{ + struct obj *otmp; + + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) { + if (otmp->otyp == x && + /* never select non-cockatrice corpses */ + !((x == CORPSE || x == EGG) && + !touch_petrifies(&mons[otmp->corpsenm])) && + (!otmp->oartifact || touch_artifact(otmp,mtmp))) + return otmp; + } + return (struct obj *)0; +} + +static NEARDATA const int rwep[] = +{ DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, + JAVELIN, SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, + ORCISH_ARROW, CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, + ORCISH_DAGGER, KNIFE, FLINT, ROCK, LOADSTONE, LUCKSTONE, DART, + /* BOOMERANG, */ CREAM_PIE + /* note: CREAM_PIE should NOT be #ifdef KOPS */ +}; + +static NEARDATA const int pwep[] = +{ HALBERD, BARDICHE, SPETUM, BILL_GUISARME, VOULGE, RANSEUR, GUISARME, + GLAIVE, LUCERN_HAMMER, BEC_DE_CORBIN, FAUCHARD, PARTISAN, LANCE +}; + +static struct obj *propellor; + +struct obj * +select_rwep(mtmp) /* select a ranged weapon for the monster */ +register struct monst *mtmp; +{ + register struct obj *otmp; + int i; + +#ifdef KOPS + char mlet = mtmp->data->mlet; +#endif + + propellor = &zeroobj; + Oselect(EGG); /* cockatrice egg */ +#ifdef KOPS + if(mlet == S_KOP) /* pies are first choice for Kops */ + Oselect(CREAM_PIE); +#endif + if(throws_rocks(mtmp->data)) /* ...boulders for giants */ + Oselect(BOULDER); + + /* Select polearms first; they do more damage and aren't expendable */ + /* The limit of 13 here is based on the monster polearm range limit + * (defined as 5 in mthrowu.c). 5 corresponds to a distance of 2 in + * one direction and 1 in another; one space beyond that would be 3 in + * one direction and 2 in another; 3^2+2^2=13. + */ + if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 13 && couldsee(mtmp->mx, mtmp->my)) { + for (i = 0; i < SIZE(pwep); i++) { + /* Only strong monsters can wield big (esp. long) weapons. + * Big weapon is basically the same as bimanual. + * All monsters can wield the remaining weapons. + */ + if (((strongmonst(mtmp->data) && (mtmp->misc_worn_check & W_ARMS) == 0) + || !objects[pwep[i]].oc_bimanual) && + (objects[pwep[i]].oc_material != SILVER + || !hates_silver(mtmp->data))) { + if ((otmp = oselect(mtmp, pwep[i])) != 0) { + propellor = otmp; /* force the monster to wield it */ + return otmp; + } + } + } + } + + /* + * other than these two specific cases, always select the + * most potent ranged weapon to hand. + */ + for (i = 0; i < SIZE(rwep); i++) { + int prop; + + /* shooting gems from slings; this goes just before the darts */ + /* (shooting rocks is already handled via the rwep[] ordering) */ + if (rwep[i] == DART && !likes_gems(mtmp->data) && + m_carrying(mtmp, SLING)) { /* propellor */ + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (otmp->oclass == GEM_CLASS && + (otmp->otyp != LOADSTONE || !otmp->cursed)) { + propellor = m_carrying(mtmp, SLING); + return otmp; + } + } + + /* KMH -- This belongs here so darts will work */ + propellor = &zeroobj; + + prop = (objects[rwep[i]]).oc_skill; + if (prop < 0) { + switch (-prop) { + case P_BOW: + propellor = (oselect(mtmp, YUMI)); + if (!propellor) propellor = (oselect(mtmp, ELVEN_BOW)); + if (!propellor) propellor = (oselect(mtmp, BOW)); + if (!propellor) propellor = (oselect(mtmp, ORCISH_BOW)); + break; + case P_SLING: + propellor = (oselect(mtmp, SLING)); + break; + case P_CROSSBOW: + propellor = (oselect(mtmp, CROSSBOW)); + } + if ((otmp = MON_WEP(mtmp)) && otmp->cursed && otmp != propellor + && mtmp->weapon_check == NO_WEAPON_WANTED) + propellor = 0; + } + /* propellor = obj, propellor to use + * propellor = &zeroobj, doesn't need a propellor + * propellor = 0, needed one and didn't have one + */ + if (propellor != 0) { + /* Note: cannot use m_carrying for loadstones, since it will + * always select the first object of a type, and maybe the + * monster is carrying two but only the first is unthrowable. + */ + if (rwep[i] != LOADSTONE) { + /* Don't throw a cursed weapon-in-hand or an artifact */ + if ((otmp = oselect(mtmp, rwep[i])) && !otmp->oartifact + && (!otmp->cursed || otmp != MON_WEP(mtmp))) + return(otmp); + } else for(otmp=mtmp->minvent; otmp; otmp=otmp->nobj) { + if (otmp->otyp == LOADSTONE && !otmp->cursed) + return otmp; + } + } + } + + /* failure */ + return (struct obj *)0; +} + +/* Weapons in order of preference */ +static const NEARDATA short hwep[] = { + CORPSE, /* cockatrice corpse */ + TSURUGI, RUNESWORD, DWARVISH_MATTOCK, TWO_HANDED_SWORD, BATTLE_AXE, + KATANA, UNICORN_HORN, CRYSKNIFE, TRIDENT, LONG_SWORD, + ELVEN_BROADSWORD, BROADSWORD, SCIMITAR, SILVER_SABER, + MORNING_STAR, ELVEN_SHORT_SWORD, DWARVISH_SHORT_SWORD, SHORT_SWORD, + ORCISH_SHORT_SWORD, MACE, AXE, DWARVISH_SPEAR, SILVER_SPEAR, + ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, FLAIL, BULLWHIP, QUARTERSTAFF, + JAVELIN, AKLYS, CLUB, PICK_AXE, +#ifdef KOPS + RUBBER_HOSE, +#endif /* KOPS */ + WAR_HAMMER, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, + ATHAME, SCALPEL, KNIFE, WORM_TOOTH + }; + +struct obj * +select_hwep(mtmp) /* select a hand to hand weapon for the monster */ +register struct monst *mtmp; +{ + register struct obj *otmp; + register int i; + boolean strong = strongmonst(mtmp->data); + boolean wearing_shield = (mtmp->misc_worn_check & W_ARMS) != 0; + + /* prefer artifacts to everything else */ + for(otmp=mtmp->minvent; otmp; otmp = otmp->nobj) { + if (otmp->oclass == WEAPON_CLASS + && otmp->oartifact && touch_artifact(otmp,mtmp) + && ((strong && !wearing_shield) + || !objects[otmp->otyp].oc_bimanual)) + return otmp; + } + + if(is_giant(mtmp->data)) /* giants just love to use clubs */ + Oselect(CLUB); + + /* only strong monsters can wield big (esp. long) weapons */ + /* big weapon is basically the same as bimanual */ + /* all monsters can wield the remaining weapons */ + for (i = 0; i < SIZE(hwep); i++) { + if (hwep[i] == CORPSE && !(mtmp->misc_worn_check & W_ARMG)) + continue; + if (((strong && !wearing_shield) + || !objects[hwep[i]].oc_bimanual) && + (objects[hwep[i]].oc_material != SILVER + || !hates_silver(mtmp->data))) + Oselect(hwep[i]); + } + + /* failure */ + return (struct obj *)0; +} + +/* Called after polymorphing a monster, robbing it, etc.... Monsters + * otherwise never unwield stuff on their own. Might print message. + */ +void +possibly_unwield(mon, polyspot) +struct monst *mon; +boolean polyspot; +{ + struct obj *obj, *mw_tmp; + + if (!(mw_tmp = MON_WEP(mon))) + return; + for (obj = mon->minvent; obj; obj = obj->nobj) + if (obj == mw_tmp) break; + if (!obj) { /* The weapon was stolen or destroyed */ + MON_NOWEP(mon); + mon->weapon_check = NEED_WEAPON; + return; + } + if (!attacktype(mon->data, AT_WEAP)) { + setmnotwielded(mon, mw_tmp); + MON_NOWEP(mon); + mon->weapon_check = NO_WEAPON_WANTED; + obj_extract_self(obj); + if (cansee(mon->mx, mon->my)) { + pline("%s drops %s.", Monnam(mon), + distant_name(obj, doname)); + newsym(mon->mx, mon->my); + } + /* might be dropping object into water or lava */ + if (!flooreffects(obj, mon->mx, mon->my, "drop")) { + if (polyspot) bypass_obj(obj); + place_object(obj, mon->mx, mon->my); + stackobj(obj); + } + return; + } + /* The remaining case where there is a change is where a monster + * is polymorphed into a stronger/weaker monster with a different + * choice of weapons. This has no parallel for players. It can + * be handled by waiting until mon_wield_item is actually called. + * Though the monster still wields the wrong weapon until then, + * this is OK since the player can't see it. (FIXME: Not okay since + * probing can reveal it.) + * Note that if there is no change, setting the check to NEED_WEAPON + * is harmless. + * Possible problem: big monster with big cursed weapon gets + * polymorphed into little monster. But it's not quite clear how to + * handle this anyway.... + */ + if (!(mw_tmp->cursed && mon->weapon_check == NO_WEAPON_WANTED)) + mon->weapon_check = NEED_WEAPON; + return; +} + +/* Let a monster try to wield a weapon, based on mon->weapon_check. + * Returns 1 if the monster took time to do it, 0 if it did not. + */ +int +mon_wield_item(mon) +register struct monst *mon; +{ + struct obj *obj; + + /* This case actually should never happen */ + if (mon->weapon_check == NO_WEAPON_WANTED) return 0; + switch(mon->weapon_check) { + case NEED_HTH_WEAPON: + obj = select_hwep(mon); + break; + case NEED_RANGED_WEAPON: + (void)select_rwep(mon); + obj = propellor; + break; + case NEED_PICK_AXE: + obj = m_carrying(mon, PICK_AXE); + /* KMH -- allow other picks */ + if (!obj && !which_armor(mon, W_ARMS)) + obj = m_carrying(mon, DWARVISH_MATTOCK); + break; + case NEED_AXE: + /* currently, only 2 types of axe */ + obj = m_carrying(mon, BATTLE_AXE); + if (!obj || which_armor(mon, W_ARMS)) + obj = m_carrying(mon, AXE); + break; + case NEED_PICK_OR_AXE: + /* prefer pick for fewer switches on most levels */ + obj = m_carrying(mon, DWARVISH_MATTOCK); + if (!obj) obj = m_carrying(mon, BATTLE_AXE); + if (!obj || which_armor(mon, W_ARMS)) { + obj = m_carrying(mon, PICK_AXE); + if (!obj) obj = m_carrying(mon, AXE); + } + break; + default: impossible("weapon_check %d for %s?", + mon->weapon_check, mon_nam(mon)); + return 0; + } + if (obj && obj != &zeroobj) { + struct obj *mw_tmp = MON_WEP(mon); + if (mw_tmp && mw_tmp->otyp == obj->otyp) { + /* already wielding it */ + mon->weapon_check = NEED_WEAPON; + return 0; + } + /* Actually, this isn't necessary--as soon as the monster + * wields the weapon, the weapon welds itself, so the monster + * can know it's cursed and needn't even bother trying. + * Still.... + */ + if (mw_tmp && mw_tmp->cursed && mw_tmp->otyp != CORPSE) { + if (canseemon(mon)) { + char welded_buf[BUFSZ]; + const char *mon_hand = mbodypart(mon, HAND); + + if (bimanual(mw_tmp)) mon_hand = makeplural(mon_hand); + Sprintf(welded_buf, "%s welded to %s %s", + otense(mw_tmp, "are"), + mhis(mon), mon_hand); + + if (obj->otyp == PICK_AXE) { + pline("Since %s weapon%s %s,", + s_suffix(mon_nam(mon)), + plur(mw_tmp->quan), welded_buf); + pline("%s cannot wield that %s.", + mon_nam(mon), xname(obj)); + } else { + pline("%s tries to wield %s.", Monnam(mon), + doname(obj)); + pline("%s %s %s!", + s_suffix(Monnam(mon)), + xname(mw_tmp), welded_buf); + } + mw_tmp->bknown = 1; + } + mon->weapon_check = NO_WEAPON_WANTED; + return 1; + } + mon->mw = obj; /* wield obj */ + setmnotwielded(mon, mw_tmp); + mon->weapon_check = NEED_WEAPON; + if (canseemon(mon)) { + pline("%s wields %s!", Monnam(mon), doname(obj)); + if (obj->cursed && obj->otyp != CORPSE) { + pline("%s %s to %s %s!", + Tobjnam(obj, "weld"), + is_plural(obj) ? "themselves" : "itself", + s_suffix(mon_nam(mon)), mbodypart(mon,HAND)); + obj->bknown = 1; + } + } + if (artifact_light(obj) && !obj->lamplit) { + begin_burn(obj, FALSE); + if (canseemon(mon)) + pline("%s brilliantly in %s %s!", + Tobjnam(obj, "glow"), + s_suffix(mon_nam(mon)), mbodypart(mon,HAND)); + } + obj->owornmask = W_WEP; + return 1; + } + mon->weapon_check = NEED_WEAPON; + return 0; +} + +int +abon() /* attack bonus for strength & dexterity */ +{ + int sbon; + int str = ACURR(A_STR), dex = ACURR(A_DEX); + + if (Upolyd) return(adj_lev(&mons[u.umonnum]) - 3); + if (str < 6) sbon = -2; + else if (str < 8) sbon = -1; + else if (str < 17) sbon = 0; + else if (str <= STR18(50)) sbon = 1; /* up to 18/50 */ + else if (str < STR18(100)) sbon = 2; + else sbon = 3; + +/* Game tuning kludge: make it a bit easier for a low level character to hit */ + sbon += (u.ulevel < 3) ? 1 : 0; + + if (dex < 4) return(sbon-3); + else if (dex < 6) return(sbon-2); + else if (dex < 8) return(sbon-1); + else if (dex < 14) return(sbon); + else return(sbon + dex-14); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +int +dbon() /* damage bonus for strength */ +{ + int str = ACURR(A_STR); + + if (Upolyd) return(0); + + if (str < 6) return(-1); + else if (str < 16) return(0); + else if (str < 18) return(1); + else if (str == 18) return(2); /* up to 18 */ + else if (str <= STR18(75)) return(3); /* up to 18/75 */ + else if (str <= STR18(90)) return(4); /* up to 18/90 */ + else if (str < STR18(100)) return(5); /* up to 18/99 */ + else return(6); +} + + +/* copy the skill level name into the given buffer */ +STATIC_OVL char * +skill_level_name(skill, buf) +int skill; +char *buf; +{ + const char *ptr; + + switch (P_SKILL(skill)) { + case P_UNSKILLED: ptr = "Unskilled"; break; + case P_BASIC: ptr = "Basic"; break; + case P_SKILLED: ptr = "Skilled"; break; + case P_EXPERT: ptr = "Expert"; break; + /* these are for unarmed combat/martial arts only */ + case P_MASTER: ptr = "Master"; break; + case P_GRAND_MASTER: ptr = "Grand Master"; break; + default: ptr = "Unknown"; break; + } + Strcpy(buf, ptr); + return buf; +} + +/* return the # of slots required to advance the skill */ +STATIC_OVL int +slots_required(skill) +int skill; +{ + int tmp = P_SKILL(skill); + + /* The more difficult the training, the more slots it takes. + * unskilled -> basic 1 + * basic -> skilled 2 + * skilled -> expert 3 + */ + if (skill <= P_LAST_WEAPON || skill == P_TWO_WEAPON_COMBAT) + return tmp; + + /* Fewer slots used up for unarmed or martial. + * unskilled -> basic 1 + * basic -> skilled 1 + * skilled -> expert 2 + * expert -> master 2 + * master -> grand master 3 + */ + return (tmp + 1) / 2; +} + +/* return true if this skill can be advanced */ +/*ARGSUSED*/ +STATIC_OVL boolean +can_advance(skill, speedy) +int skill; +boolean speedy; +{ + return !P_RESTRICTED(skill) + && P_SKILL(skill) < P_MAX_SKILL(skill) && ( +#ifdef WIZARD + (wizard && speedy) || +#endif + (P_ADVANCE(skill) >= + (unsigned) practice_needed_to_advance(P_SKILL(skill)) + && u.skills_advanced < P_SKILL_LIMIT + && u.weapon_slots >= slots_required(skill))); +} + +/* return true if this skill could be advanced if more slots were available */ +STATIC_OVL boolean +could_advance(skill) +int skill; +{ + return !P_RESTRICTED(skill) + && P_SKILL(skill) < P_MAX_SKILL(skill) && ( + (P_ADVANCE(skill) >= + (unsigned) practice_needed_to_advance(P_SKILL(skill)) + && u.skills_advanced < P_SKILL_LIMIT)); +} + +/* return true if this skill has reached its maximum and there's been enough + practice to become eligible for the next step if that had been possible */ +STATIC_OVL boolean +peaked_skill(skill) +int skill; +{ + return !P_RESTRICTED(skill) + && P_SKILL(skill) >= P_MAX_SKILL(skill) && ( + (P_ADVANCE(skill) >= + (unsigned) practice_needed_to_advance(P_SKILL(skill)))); +} + +STATIC_OVL void +skill_advance(skill) +int skill; +{ + u.weapon_slots -= slots_required(skill); + P_SKILL(skill)++; + u.skill_record[u.skills_advanced++] = skill; + /* subtly change the advance message to indicate no more advancement */ + You("are now %s skilled in %s.", + P_SKILL(skill) >= P_MAX_SKILL(skill) ? "most" : "more", + P_NAME(skill)); +} + +const static struct skill_range { + short first, last; + const char *name; +} skill_ranges[] = { + { P_FIRST_H_TO_H, P_LAST_H_TO_H, "Fighting Skills" }, + { P_FIRST_WEAPON, P_LAST_WEAPON, "Weapon Skills" }, + { P_FIRST_SPELL, P_LAST_SPELL, "Spellcasting Skills" }, +}; + +/* + * The `#enhance' extended command. What we _really_ would like is + * to keep being able to pick things to advance until we couldn't any + * more. This is currently not possible -- the menu code has no way + * to call us back for instant action. Even if it did, we would also need + * to be able to update the menu since selecting one item could make + * others unselectable. + */ +int +enhance_weapon_skill() +{ + int pass, i, n, len, longest, + to_advance, eventually_advance, maxxed_cnt; + char buf[BUFSZ], sklnambuf[BUFSZ]; + const char *prefix; + menu_item *selected; + anything any; + winid win; + boolean speedy = FALSE; + +#ifdef WIZARD + if (wizard && yn("Advance skills without practice?") == 'y') + speedy = TRUE; +#endif + + do { + /* find longest available skill name, count those that can advance */ + to_advance = eventually_advance = maxxed_cnt = 0; + for (longest = 0, i = 0; i < P_NUM_SKILLS; i++) { + if (P_RESTRICTED(i)) continue; + if ((len = strlen(P_NAME(i))) > longest) + longest = len; + if (can_advance(i, speedy)) to_advance++; + else if (could_advance(i)) eventually_advance++; + else if (peaked_skill(i)) maxxed_cnt++; + } + + win = create_nhwindow(NHW_MENU); + start_menu(win); + + /* start with a legend if any entries will be annotated + with "*" or "#" below */ + if (eventually_advance > 0 || maxxed_cnt > 0) { + any.a_void = 0; + if (eventually_advance > 0) { + Sprintf(buf, + "(Skill%s flagged by \"*\" may be enhanced %s.)", + plur(eventually_advance), + (u.ulevel < MAXULEV) ? + "when you're more experienced" : + "if skill slots become available"); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + if (maxxed_cnt > 0) { + Sprintf(buf, + "(Skill%s flagged by \"#\" cannot be enhanced any further.)", + plur(maxxed_cnt)); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + "", MENU_UNSELECTED); + } + + /* List the skills, making ones that could be advanced + selectable. List the miscellaneous skills first. + Possible future enhancement: list spell skills before + weapon skills for spellcaster roles. */ + for (pass = 0; pass < SIZE(skill_ranges); pass++) + for (i = skill_ranges[pass].first; + i <= skill_ranges[pass].last; i++) { + /* Print headings for skill types */ + any.a_void = 0; + if (i == skill_ranges[pass].first) + add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings, + skill_ranges[pass].name, MENU_UNSELECTED); + + if (P_RESTRICTED(i)) continue; + /* + * Sigh, this assumes a monospaced font unless + * iflags.menu_tab_sep is set in which case it puts + * tabs between columns. + * The 12 is the longest skill level name. + * The " " is room for a selection letter and dash, "a - ". + */ + if (can_advance(i, speedy)) + prefix = ""; /* will be preceded by menu choice */ + else if (could_advance(i)) + prefix = " * "; + else if (peaked_skill(i)) + prefix = " # "; + else + prefix = (to_advance + eventually_advance + + maxxed_cnt > 0) ? " " : ""; + (void) skill_level_name(i, sklnambuf); +#ifdef WIZARD + if (wizard) { + if (!iflags.menu_tab_sep) + Sprintf(buf, " %s%-*s %-12s %5d(%4d)", + prefix, longest, P_NAME(i), sklnambuf, + P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + else + Sprintf(buf, " %s%s\t%s\t%5d(%4d)", + prefix, P_NAME(i), sklnambuf, + P_ADVANCE(i), + practice_needed_to_advance(P_SKILL(i))); + } else +#endif + { + if (!iflags.menu_tab_sep) + Sprintf(buf, " %s %-*s [%s]", + prefix, longest, P_NAME(i), sklnambuf); + else + Sprintf(buf, " %s%s\t[%s]", + prefix, P_NAME(i), sklnambuf); + } + any.a_int = can_advance(i, speedy) ? i+1 : 0; + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + + Strcpy(buf, (to_advance > 0) ? "Pick a skill to advance:" : + "Current skills:"); +#ifdef WIZARD + if (wizard && !speedy) + Sprintf(eos(buf), " (%d slot%s available)", + u.weapon_slots, plur(u.weapon_slots)); +#endif + end_menu(win, buf); + n = select_menu(win, to_advance ? PICK_ONE : PICK_NONE, &selected); + destroy_nhwindow(win); + if (n > 0) { + n = selected[0].item.a_int - 1; /* get item selected */ + free((genericptr_t)selected); + skill_advance(n); + /* check for more skills able to advance, if so then .. */ + for (n = i = 0; i < P_NUM_SKILLS; i++) { + if (can_advance(i, speedy)) { + if (!speedy) You_feel("you could be more dangerous!"); + n++; + break; + } + } + } + } while (speedy && n > 0); + return 0; +} + +/* + * Change from restricted to unrestricted, allowing P_BASIC as max. This + * function may be called with with P_NONE. Used in pray.c. + */ +void +unrestrict_weapon_skill(skill) +int skill; +{ + if (skill < P_NUM_SKILLS && P_RESTRICTED(skill)) { + P_SKILL(skill) = P_UNSKILLED; + P_MAX_SKILL(skill) = P_BASIC; + P_ADVANCE(skill) = 0; + } +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +use_skill(skill,degree) +int skill; +int degree; +{ + boolean advance_before; + + if (skill != P_NONE && !P_RESTRICTED(skill)) { + advance_before = can_advance(skill, FALSE); + P_ADVANCE(skill)+=degree; + if (!advance_before && can_advance(skill, FALSE)) + give_may_advance_msg(skill); + } +} + +void +add_weapon_skill(n) +int n; /* number of slots to gain; normally one */ +{ + int i, before, after; + + for (i = 0, before = 0; i < P_NUM_SKILLS; i++) + if (can_advance(i, FALSE)) before++; + u.weapon_slots += n; + for (i = 0, after = 0; i < P_NUM_SKILLS; i++) + if (can_advance(i, FALSE)) after++; + if (before < after) + give_may_advance_msg(P_NONE); +} + +void +lose_weapon_skill(n) +int n; /* number of slots to lose; normally one */ +{ + int skill; + + while (--n >= 0) { + /* deduct first from unused slots, then from last placed slot, if any */ + if (u.weapon_slots) { + u.weapon_slots--; + } else if (u.skills_advanced) { + skill = u.skill_record[--u.skills_advanced]; + if (P_SKILL(skill) <= P_UNSKILLED) + panic("lose_weapon_skill (%d)", skill); + P_SKILL(skill)--; /* drop skill one level */ + /* Lost skill might have taken more than one slot; refund rest. */ + u.weapon_slots = slots_required(skill) - 1; + /* It might now be possible to advance some other pending + skill by using the refunded slots, but giving a message + to that effect would seem pretty confusing.... */ + } + } +} + +int +weapon_type(obj) +struct obj *obj; +{ + /* KMH -- now uses the object table */ + int type; + + if (!obj) + /* Not using a weapon */ + return (P_BARE_HANDED_COMBAT); + if (obj->oclass != WEAPON_CLASS && obj->oclass != TOOL_CLASS && + obj->oclass != GEM_CLASS) + /* Not a weapon, weapon-tool, or ammo */ + return (P_NONE); + type = objects[obj->otyp].oc_skill; + return ((type < 0) ? -type : type); +} + +int +uwep_skill_type() +{ + if (u.twoweap) + return P_TWO_WEAPON_COMBAT; + return weapon_type(uwep); +} + +/* + * Return hit bonus/penalty based on skill of weapon. + * Treat restricted weapons as unskilled. + */ +int +weapon_hit_bonus(weapon) +struct obj *weapon; +{ + int type, wep_type, skill, bonus = 0; + static const char bad_skill[] = "weapon_hit_bonus: bad skill %d"; + + wep_type = weapon_type(weapon); + /* use two weapon skill only if attacking with one of the wielded weapons */ + type = (u.twoweap && (weapon == uwep || weapon == uswapwep)) ? + P_TWO_WEAPON_COMBAT : wep_type; + if (type == P_NONE) { + bonus = 0; + } else if (type <= P_LAST_WEAPON) { + switch (P_SKILL(type)) { + default: impossible(bad_skill, P_SKILL(type)); /* fall through */ + case P_ISRESTRICTED: + case P_UNSKILLED: bonus = -4; break; + case P_BASIC: bonus = 0; break; + case P_SKILLED: bonus = 2; break; + case P_EXPERT: bonus = 3; break; + } + } else if (type == P_TWO_WEAPON_COMBAT) { + skill = P_SKILL(P_TWO_WEAPON_COMBAT); + if (P_SKILL(wep_type) < skill) skill = P_SKILL(wep_type); + switch (skill) { + default: impossible(bad_skill, skill); /* fall through */ + case P_ISRESTRICTED: + case P_UNSKILLED: bonus = -9; break; + case P_BASIC: bonus = -7; break; + case P_SKILLED: bonus = -5; break; + case P_EXPERT: bonus = -3; break; + } + } else if (type == P_BARE_HANDED_COMBAT) { + /* + * b.h. m.a. + * unskl: +1 n/a + * basic: +1 +3 + * skild: +2 +4 + * exprt: +2 +5 + * mastr: +3 +6 + * grand: +3 +7 + */ + bonus = P_SKILL(type); + bonus = max(bonus,P_UNSKILLED) - 1; /* unskilled => 0 */ + bonus = ((bonus + 2) * (martial_bonus() ? 2 : 1)) / 2; + } + +#ifdef STEED + /* KMH -- It's harder to hit while you are riding */ + if (u.usteed) { + switch (P_SKILL(P_RIDING)) { + case P_ISRESTRICTED: + case P_UNSKILLED: bonus -= 2; break; + case P_BASIC: bonus -= 1; break; + case P_SKILLED: break; + case P_EXPERT: break; + } + if (u.twoweap) bonus -= 2; + } +#endif + + return bonus; +} + +/* + * Return damage bonus/penalty based on skill of weapon. + * Treat restricted weapons as unskilled. + */ +int +weapon_dam_bonus(weapon) +struct obj *weapon; +{ + int type, wep_type, skill, bonus = 0; + + wep_type = weapon_type(weapon); + /* use two weapon skill only if attacking with one of the wielded weapons */ + type = (u.twoweap && (weapon == uwep || weapon == uswapwep)) ? + P_TWO_WEAPON_COMBAT : wep_type; + if (type == P_NONE) { + bonus = 0; + } else if (type <= P_LAST_WEAPON) { + switch (P_SKILL(type)) { + default: impossible("weapon_dam_bonus: bad skill %d",P_SKILL(type)); + /* fall through */ + case P_ISRESTRICTED: + case P_UNSKILLED: bonus = -2; break; + case P_BASIC: bonus = 0; break; + case P_SKILLED: bonus = 1; break; + case P_EXPERT: bonus = 2; break; + } + } else if (type == P_TWO_WEAPON_COMBAT) { + skill = P_SKILL(P_TWO_WEAPON_COMBAT); + if (P_SKILL(wep_type) < skill) skill = P_SKILL(wep_type); + switch (skill) { + default: + case P_ISRESTRICTED: + case P_UNSKILLED: bonus = -3; break; + case P_BASIC: bonus = -1; break; + case P_SKILLED: bonus = 0; break; + case P_EXPERT: bonus = 1; break; + } + } else if (type == P_BARE_HANDED_COMBAT) { + /* + * b.h. m.a. + * unskl: 0 n/a + * basic: +1 +3 + * skild: +1 +4 + * exprt: +2 +6 + * mastr: +2 +7 + * grand: +3 +9 + */ + bonus = P_SKILL(type); + bonus = max(bonus,P_UNSKILLED) - 1; /* unskilled => 0 */ + bonus = ((bonus + 1) * (martial_bonus() ? 3 : 1)) / 2; + } + +#ifdef STEED + /* KMH -- Riding gives some thrusting damage */ + if (u.usteed && type != P_TWO_WEAPON_COMBAT) { + switch (P_SKILL(P_RIDING)) { + case P_ISRESTRICTED: + case P_UNSKILLED: break; + case P_BASIC: break; + case P_SKILLED: bonus += 1; break; + case P_EXPERT: bonus += 2; break; + } + } +#endif + + return bonus; +} + +/* + * Initialize weapon skill array for the game. Start by setting all + * skills to restricted, then set the skill for every weapon the + * hero is holding, finally reading the given array that sets + * maximums. + */ +void +skill_init(class_skill) +const struct def_skill *class_skill; +{ + struct obj *obj; + int skmax, skill; + + /* initialize skill array; by default, everything is restricted */ + for (skill = 0; skill < P_NUM_SKILLS; skill++) { + P_SKILL(skill) = P_ISRESTRICTED; + P_MAX_SKILL(skill) = P_ISRESTRICTED; + P_ADVANCE(skill) = 0; + } + + /* Set skill for all weapons in inventory to be basic */ + for (obj = invent; obj; obj = obj->nobj) { + skill = weapon_type(obj); + if (skill != P_NONE) + P_SKILL(skill) = P_BASIC; + } + + /* set skills for magic */ + if (Role_if(PM_HEALER) || Role_if(PM_MONK)) { + P_SKILL(P_HEALING_SPELL) = P_BASIC; + } else if (Role_if(PM_PRIEST)) { + P_SKILL(P_CLERIC_SPELL) = P_BASIC; + } else if (Role_if(PM_WIZARD)) { + P_SKILL(P_ATTACK_SPELL) = P_BASIC; + P_SKILL(P_ENCHANTMENT_SPELL) = P_BASIC; + } + + /* walk through array to set skill maximums */ + for (; class_skill->skill != P_NONE; class_skill++) { + skmax = class_skill->skmax; + skill = class_skill->skill; + + P_MAX_SKILL(skill) = skmax; + if (P_SKILL(skill) == P_ISRESTRICTED) /* skill pre-set */ + P_SKILL(skill) = P_UNSKILLED; + } + + /* High potential fighters already know how to use their hands. */ + if (P_MAX_SKILL(P_BARE_HANDED_COMBAT) > P_EXPERT) + P_SKILL(P_BARE_HANDED_COMBAT) = P_BASIC; + + /* Roles that start with a horse know how to ride it */ +#ifdef STEED + if (urole.petnum == PM_PONY) + P_SKILL(P_RIDING) = P_BASIC; +#endif + + /* + * Make sure we haven't missed setting the max on a skill + * & set advance + */ + for (skill = 0; skill < P_NUM_SKILLS; skill++) { + if (!P_RESTRICTED(skill)) { + if (P_MAX_SKILL(skill) < P_SKILL(skill)) { + impossible("skill_init: curr > max: %s", P_NAME(skill)); + P_MAX_SKILL(skill) = P_SKILL(skill); + } + P_ADVANCE(skill) = practice_needed_to_advance(P_SKILL(skill)-1); + } + } +} + +void +setmnotwielded(mon,obj) +register struct monst *mon; +register struct obj *obj; +{ + if (!obj) return; + if (artifact_light(obj) && obj->lamplit) { + end_burn(obj, FALSE); + if (canseemon(mon)) + pline("%s in %s %s %s glowing.", The(xname(obj)), + s_suffix(mon_nam(mon)), mbodypart(mon,HAND), + otense(obj, "stop")); + } + obj->owornmask &= ~W_WEP; +} + +#endif /* OVLB */ + +/*weapon.c*/ diff --git a/src/were.c b/src/were.c new file mode 100644 index 0000000..486fc03 --- /dev/null +++ b/src/were.c @@ -0,0 +1,165 @@ +/* SCCS Id: @(#)were.c 3.4 2002/11/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef OVL0 + +void +were_change(mon) +register struct monst *mon; +{ + if (!is_were(mon->data)) + return; + + if (is_human(mon->data)) { + if (!Protection_from_shape_changers && + !rn2(night() ? (flags.moonphase == FULL_MOON ? 3 : 30) + : (flags.moonphase == FULL_MOON ? 10 : 50))) { + new_were(mon); /* change into animal form */ + if (flags.soundok && !canseemon(mon)) { + const char *howler; + + switch (monsndx(mon->data)) { + case PM_WEREWOLF: howler = "wolf"; break; + case PM_WEREJACKAL: howler = "jackal"; break; + default: howler = (char *)0; break; + } + if (howler) + You_hear("a %s howling at the moon.", howler); + } + } + } else if (!rn2(30) || Protection_from_shape_changers) { + new_were(mon); /* change back into human form */ + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +STATIC_DCL int FDECL(counter_were,(int)); + +STATIC_OVL int +counter_were(pm) +int pm; +{ + switch(pm) { + case PM_WEREWOLF: return(PM_HUMAN_WEREWOLF); + case PM_HUMAN_WEREWOLF: return(PM_WEREWOLF); + case PM_WEREJACKAL: return(PM_HUMAN_WEREJACKAL); + case PM_HUMAN_WEREJACKAL: return(PM_WEREJACKAL); + case PM_WERERAT: return(PM_HUMAN_WERERAT); + case PM_HUMAN_WERERAT: return(PM_WERERAT); + default: return(0); + } +} + +void +new_were(mon) +register struct monst *mon; +{ + register int pm; + + pm = counter_were(monsndx(mon->data)); + if(!pm) { + impossible("unknown lycanthrope %s.", mon->data->mname); + return; + } + + if(canseemon(mon) && !Hallucination) + pline("%s changes into a %s.", Monnam(mon), + is_human(&mons[pm]) ? "human" : + mons[pm].mname+4); + + set_mon_data(mon, &mons[pm], 0); + if (mon->msleeping || !mon->mcanmove) { + /* transformation wakens and/or revitalizes */ + mon->msleeping = 0; + mon->mfrozen = 0; /* not asleep or paralyzed */ + mon->mcanmove = 1; + } + /* regenerate by 1/4 of the lost hit points */ + mon->mhp += (mon->mhpmax - mon->mhp) / 4; + newsym(mon->mx,mon->my); + mon_break_armor(mon, FALSE); + possibly_unwield(mon, FALSE); +} + +int +were_summon(ptr,yours,visible,genbuf) /* were-creature (even you) summons a horde */ +register struct permonst *ptr; +register boolean yours; +int *visible; /* number of visible helpers created */ +char *genbuf; +{ + register int i, typ, pm = monsndx(ptr); + register struct monst *mtmp; + int total = 0; + + *visible = 0; + if(Protection_from_shape_changers && !yours) + return 0; + for(i = rnd(5); i > 0; i--) { + switch(pm) { + + case PM_WERERAT: + case PM_HUMAN_WERERAT: + typ = rn2(3) ? PM_SEWER_RAT : rn2(3) ? PM_GIANT_RAT : PM_RABID_RAT ; + if (genbuf) Strcpy(genbuf, "rat"); + break; + case PM_WEREJACKAL: + case PM_HUMAN_WEREJACKAL: + typ = PM_JACKAL; + if (genbuf) Strcpy(genbuf, "jackal"); + break; + case PM_WEREWOLF: + case PM_HUMAN_WEREWOLF: + typ = rn2(5) ? PM_WOLF : PM_WINTER_WOLF ; + if (genbuf) Strcpy(genbuf, "wolf"); + break; + default: + continue; + } + mtmp = makemon(&mons[typ], u.ux, u.uy, NO_MM_FLAGS); + if (mtmp) { + total++; + if (canseemon(mtmp)) *visible += 1; + } + if (yours && mtmp) + (void) tamedog(mtmp, (struct obj *) 0); + } + return total; +} + +void +you_were() +{ + char qbuf[QBUFSZ]; + + if (Unchanging || (u.umonnum == u.ulycn)) return; + if (Polymorph_control) { + /* `+4' => skip "were" prefix to get name of beast */ + Sprintf(qbuf, "Do you want to change into %s? ", + an(mons[u.ulycn].mname+4)); + if(yn(qbuf) == 'n') return; + } + (void) polymon(u.ulycn); +} + +void +you_unwere(purify) +boolean purify; +{ + if (purify) { + You_feel("purified."); + u.ulycn = NON_PM; /* cure lycanthropy */ + } + if (!Unchanging && is_were(youmonst.data) && + (!Polymorph_control || yn("Remain in beast form?") == 'n')) + rehumanize(); +} + +#endif /* OVLB */ + +/*were.c*/ diff --git a/src/wield.c b/src/wield.c new file mode 100644 index 0000000..0423116 --- /dev/null +++ b/src/wield.c @@ -0,0 +1,800 @@ +/* SCCS Id: @(#)wield.c 3.4 2003/01/29 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* KMH -- Differences between the three weapon slots. + * + * The main weapon (uwep): + * 1. Is filled by the (w)ield command. + * 2. Can be filled with any type of item. + * 3. May be carried in one or both hands. + * 4. Is used as the melee weapon and as the launcher for + * ammunition. + * 5. Only conveys intrinsics when it is a weapon, weapon-tool, + * or artifact. + * 6. Certain cursed items will weld to the hand and cannot be + * unwielded or dropped. See erodeable_wep() and will_weld() + * below for the list of which items apply. + * + * The secondary weapon (uswapwep): + * 1. Is filled by the e(x)change command, which swaps this slot + * with the main weapon. If the "pushweapon" option is set, + * the (w)ield command will also store the old weapon in the + * secondary slot. + * 2. Can be field with anything that will fit in the main weapon + * slot; that is, any type of item. + * 3. Is usually NOT considered to be carried in the hands. + * That would force too many checks among the main weapon, + * second weapon, shield, gloves, and rings; and it would + * further be complicated by bimanual weapons. A special + * exception is made for two-weapon combat. + * 4. Is used as the second weapon for two-weapon combat, and as + * a convenience to swap with the main weapon. + * 5. Never conveys intrinsics. + * 6. Cursed items never weld (see #3 for reasons), but they also + * prevent two-weapon combat. + * + * The quiver (uquiver): + * 1. Is filled by the (Q)uiver command. + * 2. Can be filled with any type of item. + * 3. Is considered to be carried in a special part of the pack. + * 4. Is used as the item to throw with the (f)ire command. + * This is a convenience over the normal (t)hrow command. + * 5. Never conveys intrinsics. + * 6. Cursed items never weld; their effect is handled by the normal + * throwing code. + * + * No item may be in more than one of these slots. + */ + + +STATIC_DCL int FDECL(ready_weapon, (struct obj *)); + +/* used by will_weld() */ +/* probably should be renamed */ +#define erodeable_wep(optr) ((optr)->oclass == WEAPON_CLASS \ + || is_weptool(optr) \ + || (optr)->otyp == HEAVY_IRON_BALL \ + || (optr)->otyp == IRON_CHAIN) + +/* used by welded(), and also while wielding */ +#define will_weld(optr) ((optr)->cursed \ + && (erodeable_wep(optr) \ + || (optr)->otyp == TIN_OPENER)) + + +/*** Functions that place a given item in a slot ***/ +/* Proper usage includes: + * 1. Initializing the slot during character generation or a + * restore. + * 2. Setting the slot due to a player's actions. + * 3. If one of the objects in the slot are split off, these + * functions can be used to put the remainder back in the slot. + * 4. Putting an item that was thrown and returned back into the slot. + * 5. Emptying the slot, by passing a null object. NEVER pass + * zeroobj! + * + * If the item is being moved from another slot, it is the caller's + * responsibility to handle that. It's also the caller's responsibility + * to print the appropriate messages. + */ +void +setuwep(obj) +register struct obj *obj; +{ + struct obj *olduwep = uwep; + + if (obj == uwep) return; /* necessary to not set unweapon */ + /* This message isn't printed in the caller because it happens + * *whenever* Sunsword is unwielded, from whatever cause. + */ + setworn(obj, W_WEP); + if (uwep == obj && artifact_light(olduwep) && olduwep->lamplit) { + end_burn(olduwep, FALSE); + if (!Blind) pline("%s glowing.", Tobjnam(olduwep, "stop")); + } + /* Note: Explicitly wielding a pick-axe will not give a "bashing" + * message. Wielding one via 'a'pplying it will. + * 3.2.2: Wielding arbitrary objects will give bashing message too. + */ + if (obj) { + unweapon = (obj->oclass == WEAPON_CLASS) ? + is_launcher(obj) || is_ammo(obj) || + is_missile(obj) || (is_pole(obj) +#ifdef STEED + && !u.usteed +#endif + ) : !is_weptool(obj); + } else + unweapon = TRUE; /* for "bare hands" message */ + update_inventory(); +} + +STATIC_OVL int +ready_weapon(wep) +struct obj *wep; +{ + /* Separated function so swapping works easily */ + int res = 0; + + if (!wep) { + /* No weapon */ + if (uwep) { + You("are empty %s.", body_part(HANDED)); + setuwep((struct obj *) 0); + res++; + } else + You("are already empty %s.", body_part(HANDED)); + } else if (!uarmg && !Stone_resistance && wep->otyp == CORPSE + && touch_petrifies(&mons[wep->corpsenm])) { + /* Prevent wielding cockatrice when not wearing gloves --KAA */ + char kbuf[BUFSZ]; + + You("wield the %s corpse in your bare %s.", + mons[wep->corpsenm].mname, makeplural(body_part(HAND))); + Sprintf(kbuf, "%s corpse", an(mons[wep->corpsenm].mname)); + instapetrify(kbuf); + } else if (uarms && bimanual(wep)) + You("cannot wield a two-handed %s while wearing a shield.", + is_sword(wep) ? "sword" : + wep->otyp == BATTLE_AXE ? "axe" : "weapon"); + else if (wep->oartifact && !touch_artifact(wep, &youmonst)) { + res++; /* takes a turn even though it doesn't get wielded */ + } else { + /* Weapon WILL be wielded after this point */ + res++; + if (will_weld(wep)) { + const char *tmp = xname(wep), *thestr = "The "; + if (strncmp(tmp, thestr, 4) && !strncmp(The(tmp),thestr,4)) + tmp = thestr; + else tmp = ""; + pline("%s%s %s to your %s!", tmp, aobjnam(wep, "weld"), + (wep->quan == 1L) ? "itself" : "themselves", /* a3 */ + bimanual(wep) ? + (const char *)makeplural(body_part(HAND)) + : body_part(HAND)); + wep->bknown = TRUE; + } else { + /* The message must be printed before setuwep (since + * you might die and be revived from changing weapons), + * and the message must be before the death message and + * Lifesaved rewielding. Yet we want the message to + * say "weapon in hand", thus this kludge. + */ + long dummy = wep->owornmask; + wep->owornmask |= W_WEP; + prinv((char *)0, wep, 0L); + wep->owornmask = dummy; + } + setuwep(wep); + + /* KMH -- Talking artifacts are finally implemented */ + arti_speak(wep); + + if (artifact_light(wep) && !wep->lamplit) { + begin_burn(wep, FALSE); + if (!Blind) + pline("%s to glow brilliantly!", Tobjnam(wep, "begin")); + } + +#if 0 + /* we'll get back to this someday, but it's not balanced yet */ + if (Race_if(PM_ELF) && !wep->oartifact && + objects[wep->otyp].oc_material == IRON) { + /* Elves are averse to wielding cold iron */ + You("have an uneasy feeling about wielding cold iron."); + change_luck(-1); + } +#endif + + if (wep->unpaid) { + struct monst *this_shkp; + + if ((this_shkp = shop_keeper(inside_shop(u.ux, u.uy))) != + (struct monst *)0) { + pline("%s says \"You be careful with my %s!\"", + shkname(this_shkp), + xname(wep)); + } + } + } + return(res); +} + +void +setuqwep(obj) +register struct obj *obj; +{ + setworn(obj, W_QUIVER); + update_inventory(); +} + +void +setuswapwep(obj) +register struct obj *obj; +{ + setworn(obj, W_SWAPWEP); + update_inventory(); +} + + +/*** Commands to change particular slot(s) ***/ + +static NEARDATA const char wield_objs[] = + { ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, TOOL_CLASS, 0 }; +static NEARDATA const char ready_objs[] = + { ALL_CLASSES, ALLOW_NONE, WEAPON_CLASS, 0 }; +static NEARDATA const char bullets[] = /* (note: different from dothrow.c) */ + { ALL_CLASSES, ALLOW_NONE, GEM_CLASS, WEAPON_CLASS, 0 }; + +int +dowield() +{ + register struct obj *wep, *oldwep; + int result; + + /* May we attempt this? */ + multi = 0; + if (cantwield(youmonst.data)) { + pline("Don't be ridiculous!"); + return(0); + } + + /* Prompt for a new weapon */ + if (!(wep = getobj(wield_objs, "wield"))) + /* Cancelled */ + return (0); + else if (wep == uwep) { + You("are already wielding that!"); + if (is_weptool(wep)) unweapon = FALSE; /* [see setuwep()] */ + return (0); + } else if (welded(uwep)) { + weldmsg(uwep); + /* previously interrupted armor removal mustn't be resumed */ + reset_remarm(); + return (0); + } + + /* Handle no object, or object in other slot */ + if (wep == &zeroobj) + wep = (struct obj *) 0; + else if (wep == uswapwep) + return (doswapweapon()); + else if (wep == uquiver) + setuqwep((struct obj *) 0); + else if (wep->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL +#ifdef STEED + | W_SADDLE +#endif + )) { + You("cannot wield that!"); + return (0); + } + + /* Set your new primary weapon */ + oldwep = uwep; + result = ready_weapon(wep); + if (flags.pushweapon && oldwep && uwep != oldwep) + setuswapwep(oldwep); + untwoweapon(); + + return (result); +} + +int +doswapweapon() +{ + register struct obj *oldwep, *oldswap; + int result = 0; + + + /* May we attempt this? */ + multi = 0; + if (cantwield(youmonst.data)) { + pline("Don't be ridiculous!"); + return(0); + } + if (welded(uwep)) { + weldmsg(uwep); + return (0); + } + + /* Unwield your current secondary weapon */ + oldwep = uwep; + oldswap = uswapwep; + setuswapwep((struct obj *) 0); + + /* Set your new primary weapon */ + result = ready_weapon(oldswap); + + /* Set your new secondary weapon */ + if (uwep == oldwep) + /* Wield failed for some reason */ + setuswapwep(oldswap); + else { + setuswapwep(oldwep); + if (uswapwep) + prinv((char *)0, uswapwep, 0L); + else + You("have no secondary weapon readied."); + } + + if (u.twoweap && !can_twoweapon()) + untwoweapon(); + + return (result); +} + +int +dowieldquiver() +{ + register struct obj *newquiver; + const char *quivee_types = (uslinging() || + (uswapwep && objects[uswapwep->otyp].oc_skill == P_SLING)) ? + bullets : ready_objs; + + /* Since the quiver isn't in your hands, don't check cantwield(), */ + /* will_weld(), touch_petrifies(), etc. */ + multi = 0; + + /* Because 'Q' used to be quit... */ + if (flags.suppress_alert < FEATURE_NOTICE_VER(3,3,0)) + pline("Note: Please use #quit if you wish to exit the game."); + + /* Prompt for a new quiver */ + if (!(newquiver = getobj(quivee_types, "ready"))) + /* Cancelled */ + return (0); + + /* Handle no object, or object in other slot */ + /* Any type is okay, since we give no intrinsics anyways */ + if (newquiver == &zeroobj) { + /* Explicitly nothing */ + if (uquiver) { + You("now have no ammunition readied."); + setuqwep(newquiver = (struct obj *) 0); + } else { + You("already have no ammunition readied!"); + return(0); + } + } else if (newquiver == uquiver) { + pline("That ammunition is already readied!"); + return(0); + } else if (newquiver == uwep) { + /* Prevent accidentally readying the main weapon */ + pline("%s already being used as a weapon!", + !is_plural(uwep) ? "That is" : "They are"); + return(0); + } else if (newquiver->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL +#ifdef STEED + | W_SADDLE +#endif + )) { + You("cannot ready that!"); + return (0); + } else { + long dummy; + + + /* Check if it's the secondary weapon */ + if (newquiver == uswapwep) { + setuswapwep((struct obj *) 0); + untwoweapon(); + } + + /* Okay to put in quiver; print it */ + dummy = newquiver->owornmask; + newquiver->owornmask |= W_QUIVER; + prinv((char *)0, newquiver, 0L); + newquiver->owornmask = dummy; + } + + /* Finally, place it in the quiver */ + setuqwep(newquiver); + /* Take no time since this is a convenience slot */ + return (0); +} + +/* used for #rub and for applying pick-axe, whip, grappling hook, or polearm */ +/* (moved from apply.c) */ +boolean +wield_tool(obj, verb) +struct obj *obj; +const char *verb; /* "rub",&c */ +{ + const char *what; + boolean more_than_1; + + if (obj == uwep) return TRUE; /* nothing to do if already wielding it */ + + if (!verb) verb = "wield"; + what = xname(obj); + more_than_1 = (obj->quan > 1L || + strstri(what, "pair of ") != 0 || + strstri(what, "s of ") != 0); + + if (obj->owornmask & (W_ARMOR|W_RING|W_AMUL|W_TOOL)) { + char yourbuf[BUFSZ]; + + You_cant("%s %s %s while wearing %s.", + verb, shk_your(yourbuf, obj), what, + more_than_1 ? "them" : "it"); + return FALSE; + } + if (welded(uwep)) { + if (flags.verbose) { + const char *hand = body_part(HAND); + + if (bimanual(uwep)) hand = makeplural(hand); + if (strstri(what, "pair of ") != 0) more_than_1 = FALSE; + pline( + "Since your weapon is welded to your %s, you cannot %s %s %s.", + hand, verb, more_than_1 ? "those" : "that", xname(obj)); + } else { + You_cant("do that."); + } + return FALSE; + } + if (cantwield(youmonst.data)) { + You_cant("hold %s strongly enough.", more_than_1 ? "them" : "it"); + return FALSE; + } + /* check shield */ + if (uarms && bimanual(obj)) { + You("cannot %s a two-handed %s while wearing a shield.", + verb, (obj->oclass == WEAPON_CLASS) ? "weapon" : "tool"); + return FALSE; + } + if (uquiver == obj) setuqwep((struct obj *)0); + if (uswapwep == obj) { + (void) doswapweapon(); + /* doswapweapon might fail */ + if (uswapwep == obj) return FALSE; + } else { + You("now wield %s.", doname(obj)); + setuwep(obj); + } + if (uwep != obj) return FALSE; /* rewielded old object after dying */ + /* applying weapon or tool that gets wielded ends two-weapon combat */ + if (u.twoweap) + untwoweapon(); + if (obj->oclass != WEAPON_CLASS) + unweapon = TRUE; + return TRUE; +} + +int +can_twoweapon() +{ + struct obj *otmp; + +#define NOT_WEAPON(obj) (!is_weptool(obj) && obj->oclass != WEAPON_CLASS) + if (!could_twoweap(youmonst.data)) { + if (Upolyd) + You_cant("use two weapons in your current form."); + else + pline("%s aren't able to use two weapons at once.", + makeplural((flags.female && urole.name.f) ? + urole.name.f : urole.name.m)); + } else if (!uwep || !uswapwep) + Your("%s%s%s empty.", uwep ? "left " : uswapwep ? "right " : "", + body_part(HAND), (!uwep && !uswapwep) ? "s are" : " is"); + else if (NOT_WEAPON(uwep) || NOT_WEAPON(uswapwep)) { + otmp = NOT_WEAPON(uwep) ? uwep : uswapwep; + pline("%s %s.", Yname2(otmp), + is_plural(otmp) ? "aren't weapons" : "isn't a weapon"); + } else if (bimanual(uwep) || bimanual(uswapwep)) { + otmp = bimanual(uwep) ? uwep : uswapwep; + pline("%s isn't one-handed.", Yname2(otmp)); + } else if (uarms) + You_cant("use two weapons while wearing a shield."); + else if (uswapwep->oartifact) + pline("%s %s being held second to another weapon!", + Yname2(uswapwep), otense(uswapwep, "resist")); + else if (!uarmg && !Stone_resistance && (uswapwep->otyp == CORPSE && + touch_petrifies(&mons[uswapwep->corpsenm]))) { + char kbuf[BUFSZ]; + + You("wield the %s corpse with your bare %s.", + mons[uswapwep->corpsenm].mname, body_part(HAND)); + Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname)); + instapetrify(kbuf); + } else if (Glib || uswapwep->cursed) { + if (!Glib) + uswapwep->bknown = TRUE; + drop_uswapwep(); + } else + return (TRUE); + return (FALSE); +} + +void +drop_uswapwep() +{ + char str[BUFSZ]; + struct obj *obj = uswapwep; + + /* Avoid trashing makeplural's static buffer */ + Strcpy(str, makeplural(body_part(HAND))); + Your("%s from your %s!", aobjnam(obj, "slip"), str); + dropx(obj); +} + +int +dotwoweapon() +{ + /* You can always toggle it off */ + if (u.twoweap) { + You("switch to your primary weapon."); + u.twoweap = 0; + update_inventory(); + return (0); + } + + /* May we use two weapons? */ + if (can_twoweapon()) { + /* Success! */ + You("begin two-weapon combat."); + u.twoweap = 1; + update_inventory(); + return (rnd(20) > ACURR(A_DEX)); + } + return (0); +} + +/*** Functions to empty a given slot ***/ +/* These should be used only when the item can't be put back in + * the slot by life saving. Proper usage includes: + * 1. The item has been eaten, stolen, burned away, or rotted away. + * 2. Making an item disappear for a bones pile. + */ +void +uwepgone() +{ + if (uwep) { + if (artifact_light(uwep) && uwep->lamplit) { + end_burn(uwep, FALSE); + if (!Blind) pline("%s glowing.", Tobjnam(uwep, "stop")); + } + setworn((struct obj *)0, W_WEP); + unweapon = TRUE; + update_inventory(); + } +} + +void +uswapwepgone() +{ + if (uswapwep) { + setworn((struct obj *)0, W_SWAPWEP); + update_inventory(); + } +} + +void +uqwepgone() +{ + if (uquiver) { + setworn((struct obj *)0, W_QUIVER); + update_inventory(); + } +} + +void +untwoweapon() +{ + if (u.twoweap) { + You("can no longer use two weapons at once."); + u.twoweap = FALSE; + update_inventory(); + } + return; +} + +/* Maybe rust object, or corrode it if acid damage is called for */ +void +erode_obj(target, acid_dmg, fade_scrolls) +struct obj *target; /* object (e.g. weapon or armor) to erode */ +boolean acid_dmg; +boolean fade_scrolls; +{ + int erosion; + struct monst *victim; + boolean vismon; + boolean visobj; + + if (!target) + return; + victim = carried(target) ? &youmonst : + mcarried(target) ? target->ocarry : (struct monst *)0; + vismon = victim && (victim != &youmonst) && canseemon(victim); + visobj = !victim && cansee(bhitpos.x, bhitpos.y); /* assume thrown */ + + erosion = acid_dmg ? target->oeroded2 : target->oeroded; + + if (target->greased) { + grease_protect(target,(char *)0,victim); + } else if (target->oclass == SCROLL_CLASS) { + if(fade_scrolls && target->otyp != SCR_BLANK_PAPER +#ifdef MAIL + && target->otyp != SCR_MAIL +#endif + ) + { + if (!Blind) { + if (victim == &youmonst) + Your("%s.", aobjnam(target, "fade")); + else if (vismon) + pline("%s's %s.", Monnam(victim), + aobjnam(target, "fade")); + else if (visobj) + pline_The("%s.", aobjnam(target, "fade")); + } + target->otyp = SCR_BLANK_PAPER; + target->spe = 0; + } + } else if (target->oerodeproof || + (acid_dmg ? !is_corrodeable(target) : !is_rustprone(target))) { + if (flags.verbose || !(target->oerodeproof && target->rknown)) { + if (victim == &youmonst) + Your("%s not affected.", aobjnam(target, "are")); + else if (vismon) + pline("%s's %s not affected.", Monnam(victim), + aobjnam(target, "are")); + /* no message if not carried */ + } + if (target->oerodeproof) target->rknown = TRUE; + } else if (erosion < MAX_ERODE) { + if (victim == &youmonst) + Your("%s%s!", aobjnam(target, acid_dmg ? "corrode" : "rust"), + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + else if (vismon) + pline("%s's %s%s!", Monnam(victim), + aobjnam(target, acid_dmg ? "corrode" : "rust"), + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + else if (visobj) + pline_The("%s%s!", + aobjnam(target, acid_dmg ? "corrode" : "rust"), + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + if (acid_dmg) + target->oeroded2++; + else + target->oeroded++; + } else { + if (flags.verbose) { + if (victim == &youmonst) + Your("%s completely %s.", + aobjnam(target, Blind ? "feel" : "look"), + acid_dmg ? "corroded" : "rusty"); + else if (vismon) + pline("%s's %s completely %s.", Monnam(victim), + aobjnam(target, "look"), + acid_dmg ? "corroded" : "rusty"); + else if (visobj) + pline_The("%s completely %s.", + aobjnam(target, "look"), + acid_dmg ? "corroded" : "rusty"); + } + } +} + +int +chwepon(otmp, amount) +register struct obj *otmp; +register int amount; +{ + const char *color = hcolor((amount < 0) ? NH_BLACK : NH_BLUE); + const char *xtime; + int otyp = STRANGE_OBJECT; + + if(!uwep || (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))) { + char buf[BUFSZ]; + + Sprintf(buf, "Your %s %s.", makeplural(body_part(HAND)), + (amount >= 0) ? "twitch" : "itch"); + strange_feeling(otmp, buf); + exercise(A_DEX, (boolean) (amount >= 0)); + return(0); + } + + if (otmp && otmp->oclass == SCROLL_CLASS) otyp = otmp->otyp; + + if(uwep->otyp == WORM_TOOTH && amount >= 0) { + uwep->otyp = CRYSKNIFE; + uwep->oerodeproof = 0; + Your("weapon seems sharper now."); + uwep->cursed = 0; + if (otyp != STRANGE_OBJECT) makeknown(otyp); + return(1); + } + + if(uwep->otyp == CRYSKNIFE && amount < 0) { + uwep->otyp = WORM_TOOTH; + uwep->oerodeproof = 0; + Your("weapon seems duller now."); + if (otyp != STRANGE_OBJECT && otmp->bknown) makeknown(otyp); + return(1); + } + + if (amount < 0 && uwep->oartifact && restrict_name(uwep, ONAME(uwep))) { + if (!Blind) + Your("%s %s.", aobjnam(uwep, "faintly glow"), color); + return(1); + } + /* there is a (soft) upper and lower limit to uwep->spe */ + if(((uwep->spe > 5 && amount >= 0) || (uwep->spe < -5 && amount < 0)) + && rn2(3)) { + if (!Blind) + Your("%s %s for a while and then %s.", + aobjnam(uwep, "violently glow"), color, + otense(uwep, "evaporate")); + else + Your("%s.", aobjnam(uwep, "evaporate")); + + useupall(uwep); /* let all of them disappear */ + return(1); + } + if (!Blind) { + xtime = (amount*amount == 1) ? "moment" : "while"; + Your("%s %s for a %s.", + aobjnam(uwep, amount == 0 ? "violently glow" : "glow"), + color, xtime); + if (otyp != STRANGE_OBJECT && uwep->known && + (amount > 0 || (amount < 0 && otmp->bknown))) + makeknown(otyp); + } + uwep->spe += amount; + if(amount > 0) uwep->cursed = 0; + + /* + * Enchantment, which normally improves a weapon, has an + * addition adverse reaction on Magicbane whose effects are + * spe dependent. Give an obscure clue here. + */ + if (uwep->oartifact == ART_MAGICBANE && uwep->spe >= 0) { + Your("right %s %sches!", + body_part(HAND), + (((amount > 1) && (uwep->spe > 1)) ? "flin" : "it")); + } + + /* an elven magic clue, cookie@keebler */ + /* elven weapons vibrate warningly when enchanted beyond a limit */ + if ((uwep->spe > 5) + && (is_elven_weapon(uwep) || uwep->oartifact || !rn2(7))) + Your("%s unexpectedly.", + aobjnam(uwep, "suddenly vibrate")); + + return(1); +} + +int +welded(obj) +register struct obj *obj; +{ + if (obj && obj == uwep && will_weld(obj)) { + obj->bknown = TRUE; + return 1; + } + return 0; +} + +void +weldmsg(obj) +register struct obj *obj; +{ + long savewornmask; + + savewornmask = obj->owornmask; + Your("%s %s welded to your %s!", + xname(obj), otense(obj, "are"), + bimanual(obj) ? (const char *)makeplural(body_part(HAND)) + : body_part(HAND)); + obj->owornmask = savewornmask; +} + +/*wield.c*/ diff --git a/src/windows.c b/src/windows.c new file mode 100644 index 0000000..3c59080 --- /dev/null +++ b/src/windows.c @@ -0,0 +1,146 @@ +/* SCCS Id: @(#)windows.c 3.4 1996/05/19 */ +/* Copyright (c) D. Cohrs, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#ifdef TTY_GRAPHICS +#include "wintty.h" +#endif +#ifdef X11_GRAPHICS +/* cannot just blindly include winX.h without including all of X11 stuff */ +/* and must get the order of include files right. Don't bother */ +extern struct window_procs X11_procs; +extern void NDECL(win_X11_init); +#endif +#ifdef QT_GRAPHICS +extern struct window_procs Qt_procs; +#endif +#ifdef GEM_GRAPHICS +#include "wingem.h" +#endif +#ifdef MAC +extern struct window_procs mac_procs; +#endif +#ifdef BEOS_GRAPHICS +extern struct window_procs beos_procs; +extern void NDECL(be_win_init); +#endif +#ifdef AMIGA_INTUITION +extern struct window_procs amii_procs; +extern struct window_procs amiv_procs; +extern void NDECL(ami_wininit_data); +#endif +#ifdef WIN32_GRAPHICS +extern struct window_procs win32_procs; +#endif +#ifdef GNOME_GRAPHICS +#include "winGnome.h" +extern struct window_procs Gnome_procs; +#endif +#ifdef MSWIN_GRAPHICS +extern struct window_procs mswin_procs; +#endif + +STATIC_DCL void FDECL(def_raw_print, (const char *s)); + +NEARDATA struct window_procs windowprocs; + +static +struct win_choices { + struct window_procs *procs; + void NDECL((*ini_routine)); /* optional (can be 0) */ +} winchoices[] = { +#ifdef TTY_GRAPHICS + { &tty_procs, win_tty_init }, +#endif +#ifdef X11_GRAPHICS + { &X11_procs, win_X11_init }, +#endif +#ifdef QT_GRAPHICS + { &Qt_procs, 0 }, +#endif +#ifdef GEM_GRAPHICS + { &Gem_procs, win_Gem_init }, +#endif +#ifdef MAC + { &mac_procs, 0 }, +#endif +#ifdef BEOS_GRAPHICS + { &beos_procs, be_win_init }, +#endif +#ifdef AMIGA_INTUITION + { &amii_procs, ami_wininit_data }, /* Old font version of the game */ + { &amiv_procs, ami_wininit_data }, /* Tile version of the game */ +#endif +#ifdef WIN32_GRAPHICS + { &win32_procs, 0 }, +#endif +#ifdef GNOME_GRAPHICS + { &Gnome_procs, 0 }, +#endif +#ifdef MSWIN_GRAPHICS + { &mswin_procs, 0 }, +#endif + { 0, 0 } /* must be last */ +}; + +STATIC_OVL +void +def_raw_print(s) +const char *s; +{ + puts(s); +} + +void +choose_windows(s) +const char *s; +{ + register int i; + + for(i=0; winchoices[i].procs; i++) + if (!strcmpi(s, winchoices[i].procs->name)) { + windowprocs = *winchoices[i].procs; + if (winchoices[i].ini_routine) (*winchoices[i].ini_routine)(); + return; + } + + if (!windowprocs.win_raw_print) + windowprocs.win_raw_print = def_raw_print; + + raw_printf("Window type %s not recognized. Choices are:", s); + for(i=0; winchoices[i].procs; i++) + raw_printf(" %s", winchoices[i].procs->name); + + if (windowprocs.win_raw_print == def_raw_print) + terminate(EXIT_SUCCESS); + wait_synch(); +} + +/* + * tty_message_menu() provides a means to get feedback from the + * --More-- prompt; other interfaces generally don't need that. + */ +/*ARGSUSED*/ +char +genl_message_menu(let, how, mesg) +char let; +int how; +const char *mesg; +{ + pline("%s", mesg); + return 0; +} + +/*ARGSUSED*/ +void +genl_preference_update(pref) +const char *pref; +{ + /* window ports are expected to provide + their own preference update routine + for the preference capabilities that + they support. + Just return in this genl one. */ +} +/*windows.c*/ diff --git a/src/wizard.c b/src/wizard.c new file mode 100644 index 0000000..c0f67e3 --- /dev/null +++ b/src/wizard.c @@ -0,0 +1,638 @@ +/* SCCS Id: @(#)wizard.c 3.4 2003/02/18 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */ +/* - heavily modified to give the wiz balls. (genat!mike) */ +/* - dewimped and given some maledictions. -3. */ +/* - generalized for 3.1 (mike@bullns.on01.bull.ca) */ + +#include "hack.h" +#include "qtext.h" +#include "epri.h" + +extern const int monstr[]; + +#ifdef OVLB + +STATIC_DCL short FDECL(which_arti, (int)); +STATIC_DCL boolean FDECL(mon_has_arti, (struct monst *,SHORT_P)); +STATIC_DCL struct monst *FDECL(other_mon_has_arti, (struct monst *,SHORT_P)); +STATIC_DCL struct obj *FDECL(on_ground, (SHORT_P)); +STATIC_DCL boolean FDECL(you_have, (int)); +STATIC_DCL long FDECL(target_on, (int,struct monst *)); +STATIC_DCL long FDECL(strategy, (struct monst *)); + +static NEARDATA const int nasties[] = { + PM_COCKATRICE, PM_ETTIN, PM_STALKER, PM_MINOTAUR, PM_RED_DRAGON, + PM_BLACK_DRAGON, PM_GREEN_DRAGON, PM_OWLBEAR, PM_PURPLE_WORM, + PM_ROCK_TROLL, PM_XAN, PM_GREMLIN, PM_UMBER_HULK, PM_VAMPIRE_LORD, + PM_XORN, PM_ZRUTY, PM_ELF_LORD, PM_ELVENKING, PM_YELLOW_DRAGON, + PM_LEOCROTTA, PM_BALUCHITHERIUM, PM_CARNIVOROUS_APE, PM_FIRE_GIANT, + PM_COUATL, PM_CAPTAIN, PM_WINGED_GARGOYLE, PM_MASTER_MIND_FLAYER, + PM_FIRE_ELEMENTAL, PM_JABBERWOCK, PM_ARCH_LICH, PM_OGRE_KING, + PM_OLOG_HAI, PM_IRON_GOLEM, PM_OCHRE_JELLY, PM_GREEN_SLIME, + PM_DISENCHANTER + }; + +static NEARDATA const unsigned wizapp[] = { + PM_HUMAN, PM_WATER_DEMON, PM_VAMPIRE, + PM_RED_DRAGON, PM_TROLL, PM_UMBER_HULK, + PM_XORN, PM_XAN, PM_COCKATRICE, + PM_FLOATING_EYE, + PM_GUARDIAN_NAGA, + PM_TRAPPER +}; + +#endif /* OVLB */ +#ifdef OVL0 + +/* If you've found the Amulet, make the Wizard appear after some time */ +/* Also, give hints about portal locations, if amulet is worn/wielded -dlc */ +void +amulet() +{ + struct monst *mtmp; + struct trap *ttmp; + struct obj *amu; + +#if 0 /* caller takes care of this check */ + if (!u.uhave.amulet) + return; +#endif + if ((((amu = uamul) != 0 && amu->otyp == AMULET_OF_YENDOR) || + ((amu = uwep) != 0 && amu->otyp == AMULET_OF_YENDOR)) + && !rn2(15)) { + for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { + if(ttmp->ttyp == MAGIC_PORTAL) { + int du = distu(ttmp->tx, ttmp->ty); + if (du <= 9) + pline("%s hot!", Tobjnam(amu, "feel")); + else if (du <= 64) + pline("%s very warm.", Tobjnam(amu, "feel")); + else if (du <= 144) + pline("%s warm.", Tobjnam(amu, "feel")); + /* else, the amulet feels normal */ + break; + } + } + } + + if (!flags.no_of_wizards) + return; + /* find Wizard, and wake him if necessary */ + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp) && mtmp->iswiz && mtmp->msleeping && !rn2(40)) { + mtmp->msleeping = 0; + if (distu(mtmp->mx,mtmp->my) > 2) + You( + "get the creepy feeling that somebody noticed your taking the Amulet." + ); + return; + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +int +mon_has_amulet(mtmp) +register struct monst *mtmp; +{ + register struct obj *otmp; + + for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if(otmp->otyp == AMULET_OF_YENDOR) return(1); + return(0); +} + +int +mon_has_special(mtmp) +register struct monst *mtmp; +{ + register struct obj *otmp; + + for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if(otmp->otyp == AMULET_OF_YENDOR || + is_quest_artifact(otmp) || + otmp->otyp == BELL_OF_OPENING || + otmp->otyp == CANDELABRUM_OF_INVOCATION || + otmp->otyp == SPE_BOOK_OF_THE_DEAD) return(1); + return(0); +} + +/* + * New for 3.1 Strategy / Tactics for the wiz, as well as other + * monsters that are "after" something (defined via mflag3). + * + * The strategy section decides *what* the monster is going + * to attempt, the tactics section implements the decision. + */ +#define STRAT(w, x, y, typ) (w | ((long)(x)<<16) | ((long)(y)<<8) | (long)typ) + +#define M_Wants(mask) (mtmp->data->mflags3 & (mask)) + +STATIC_OVL short +which_arti(mask) + register int mask; +{ + switch(mask) { + case M3_WANTSAMUL: return(AMULET_OF_YENDOR); + case M3_WANTSBELL: return(BELL_OF_OPENING); + case M3_WANTSCAND: return(CANDELABRUM_OF_INVOCATION); + case M3_WANTSBOOK: return(SPE_BOOK_OF_THE_DEAD); + default: break; /* 0 signifies quest artifact */ + } + return(0); +} + +/* + * If "otyp" is zero, it triggers a check for the quest_artifact, + * since bell, book, candle, and amulet are all objects, not really + * artifacts right now. [MRS] + */ +STATIC_OVL boolean +mon_has_arti(mtmp, otyp) + register struct monst *mtmp; + register short otyp; +{ + register struct obj *otmp; + + for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) { + if(otyp) { + if(otmp->otyp == otyp) + return(1); + } + else if(is_quest_artifact(otmp)) return(1); + } + return(0); + +} + +STATIC_OVL struct monst * +other_mon_has_arti(mtmp, otyp) + register struct monst *mtmp; + register short otyp; +{ + register struct monst *mtmp2; + + for(mtmp2 = fmon; mtmp2; mtmp2 = mtmp2->nmon) + /* no need for !DEADMONSTER check here since they have no inventory */ + if(mtmp2 != mtmp) + if(mon_has_arti(mtmp2, otyp)) return(mtmp2); + + return((struct monst *)0); +} + +STATIC_OVL struct obj * +on_ground(otyp) + register short otyp; +{ + register struct obj *otmp; + + for (otmp = fobj; otmp; otmp = otmp->nobj) + if (otyp) { + if (otmp->otyp == otyp) + return(otmp); + } else if (is_quest_artifact(otmp)) + return(otmp); + return((struct obj *)0); +} + +STATIC_OVL boolean +you_have(mask) + register int mask; +{ + switch(mask) { + case M3_WANTSAMUL: return(boolean)(u.uhave.amulet); + case M3_WANTSBELL: return(boolean)(u.uhave.bell); + case M3_WANTSCAND: return(boolean)(u.uhave.menorah); + case M3_WANTSBOOK: return(boolean)(u.uhave.book); + case M3_WANTSARTI: return(boolean)(u.uhave.questart); + default: break; + } + return(0); +} + +STATIC_OVL long +target_on(mask, mtmp) + register int mask; + register struct monst *mtmp; +{ + register short otyp; + register struct obj *otmp; + register struct monst *mtmp2; + + if(!M_Wants(mask)) return(STRAT_NONE); + + otyp = which_arti(mask); + if(!mon_has_arti(mtmp, otyp)) { + if(you_have(mask)) + return(STRAT(STRAT_PLAYER, u.ux, u.uy, mask)); + else if((otmp = on_ground(otyp))) + return(STRAT(STRAT_GROUND, otmp->ox, otmp->oy, mask)); + else if((mtmp2 = other_mon_has_arti(mtmp, otyp))) + return(STRAT(STRAT_MONSTR, mtmp2->mx, mtmp2->my, mask)); + } + return(STRAT_NONE); +} + +STATIC_OVL long +strategy(mtmp) + register struct monst *mtmp; +{ + long strat, dstrat; + + if (!is_covetous(mtmp->data) || + /* perhaps a shopkeeper has been polymorphed into a master + lich; we don't want it teleporting to the stairs to heal + because that will leave its shop untended */ + (mtmp->isshk && inhishop(mtmp))) + return STRAT_NONE; + + switch((mtmp->mhp*3)/mtmp->mhpmax) { /* 0-3 */ + + default: + case 0: /* panic time - mtmp is almost snuffed */ + return(STRAT_HEAL); + + case 1: /* the wiz is less cautious */ + if(mtmp->data != &mons[PM_WIZARD_OF_YENDOR]) + return(STRAT_HEAL); + /* else fall through */ + + case 2: dstrat = STRAT_HEAL; + break; + + case 3: dstrat = STRAT_NONE; + break; + } + + if(flags.made_amulet) + if((strat = target_on(M3_WANTSAMUL, mtmp)) != STRAT_NONE) + return(strat); + + if(u.uevent.invoked) { /* priorities change once gate opened */ + + if((strat = target_on(M3_WANTSARTI, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSBOOK, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSBELL, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSCAND, mtmp)) != STRAT_NONE) + return(strat); + } else { + + if((strat = target_on(M3_WANTSBOOK, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSBELL, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSCAND, mtmp)) != STRAT_NONE) + return(strat); + if((strat = target_on(M3_WANTSARTI, mtmp)) != STRAT_NONE) + return(strat); + } + return(dstrat); +} + +int +tactics(mtmp) + register struct monst *mtmp; +{ + long strat = strategy(mtmp); + + mtmp->mstrategy = (mtmp->mstrategy & STRAT_WAITMASK) | strat; + + switch (strat) { + case STRAT_HEAL: /* hide and recover */ + /* if wounded, hole up on or near the stairs (to block them) */ + /* unless, of course, there are no stairs (e.g. endlevel) */ + mtmp->mavenge = 1; /* covetous monsters attack while fleeing */ + if (In_W_tower(mtmp->mx, mtmp->my, &u.uz) || + (mtmp->iswiz && !xupstair && !mon_has_amulet(mtmp))) { + if (!rn2(3 + mtmp->mhp/10)) (void) rloc(mtmp, FALSE); + } else if (xupstair && + (mtmp->mx != xupstair || mtmp->my != yupstair)) { + (void) mnearto(mtmp, xupstair, yupstair, TRUE); + } + /* if you're not around, cast healing spells */ + if (distu(mtmp->mx,mtmp->my) > (BOLT_LIM * BOLT_LIM)) + if(mtmp->mhp <= mtmp->mhpmax - 8) { + mtmp->mhp += rnd(8); + return(1); + } + /* fall through :-) */ + + case STRAT_NONE: /* harrass */ + if (!rn2(!mtmp->mflee ? 5 : 33)) mnexto(mtmp); + return(0); + + default: /* kill, maim, pillage! */ + { + long where = (strat & STRAT_STRATMASK); + xchar tx = STRAT_GOALX(strat), + ty = STRAT_GOALY(strat); + int targ = strat & STRAT_GOAL; + struct obj *otmp; + + if(!targ) { /* simply wants you to close */ + return(0); + } + if((u.ux == tx && u.uy == ty) || where == STRAT_PLAYER) { + /* player is standing on it (or has it) */ + mnexto(mtmp); + return(0); + } + if(where == STRAT_GROUND) { + if(!MON_AT(tx, ty) || (mtmp->mx == tx && mtmp->my == ty)) { + /* teleport to it and pick it up */ + rloc_to(mtmp, tx, ty); /* clean old pos */ + + if ((otmp = on_ground(which_arti(targ))) != 0) { + if (cansee(mtmp->mx, mtmp->my)) + pline("%s picks up %s.", + Monnam(mtmp), + (distu(mtmp->mx, mtmp->my) <= 5) ? + doname(otmp) : distant_name(otmp, doname)); + obj_extract_self(otmp); + (void) mpickobj(mtmp, otmp); + return(1); + } else return(0); + } else { + /* a monster is standing on it - cause some trouble */ + if (!rn2(5)) mnexto(mtmp); + return(0); + } + } else { /* a monster has it - 'port beside it. */ + (void) mnearto(mtmp, tx, ty, FALSE); + return(0); + } + } + } + /*NOTREACHED*/ + return(0); +} + +void +aggravate() +{ + register struct monst *mtmp; + + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if (!DEADMONSTER(mtmp)) { + mtmp->msleeping = 0; + if(!mtmp->mcanmove && !rn2(5)) { + mtmp->mfrozen = 0; + mtmp->mcanmove = 1; + } + } +} + +void +clonewiz() +{ + register struct monst *mtmp2; + + if ((mtmp2 = makemon(&mons[PM_WIZARD_OF_YENDOR], + u.ux, u.uy, NO_MM_FLAGS)) != 0) { + mtmp2->msleeping = mtmp2->mtame = mtmp2->mpeaceful = 0; + if (!u.uhave.amulet && rn2(2)) { /* give clone a fake */ + (void) add_to_minv(mtmp2, mksobj(FAKE_AMULET_OF_YENDOR, + TRUE, FALSE)); + } + mtmp2->m_ap_type = M_AP_MONSTER; + mtmp2->mappearance = wizapp[rn2(SIZE(wizapp))]; + newsym(mtmp2->mx,mtmp2->my); + } +} + +/* also used by newcham() */ +int +pick_nasty() +{ + /* To do? Possibly should filter for appropriate forms when + in the elemental planes or surrounded by water or lava. */ + return nasties[rn2(SIZE(nasties))]; +} + +/* create some nasty monsters, aligned or neutral with the caster */ +/* a null caster defaults to a chaotic caster (e.g. the wizard) */ +int +nasty(mcast) + struct monst *mcast; +{ + register struct monst *mtmp; + register int i, j, tmp; + int castalign = (mcast ? mcast->data->maligntyp : -1); + coord bypos; + int count=0; + + if(!rn2(10) && Inhell) { + msummon((struct monst *) 0); /* summons like WoY */ + count++; + } else { + tmp = (u.ulevel > 3) ? u.ulevel/3 : 1; /* just in case -- rph */ + /* if we don't have a casting monster, the nasties appear around you */ + bypos.x = u.ux; + bypos.y = u.uy; + for(i = rnd(tmp); i > 0; --i) + for(j=0; j<20; j++) { + int makeindex; + + /* Don't create more spellcasters of the monsters' level or + * higher--avoids chain summoners filling up the level. + */ + do { + makeindex = pick_nasty(); + } while(mcast && attacktype(&mons[makeindex], AT_MAGC) && + monstr[makeindex] >= monstr[mcast->mnum]); + /* do this after picking the monster to place */ + if (mcast && + !enexto(&bypos, mcast->mux, mcast->muy, &mons[makeindex])) + continue; + if ((mtmp = makemon(&mons[makeindex], + bypos.x, bypos.y, NO_MM_FLAGS)) != 0) { + mtmp->msleeping = mtmp->mpeaceful = mtmp->mtame = 0; + set_malign(mtmp); + } else /* GENOD? */ + mtmp = makemon((struct permonst *)0, + bypos.x, bypos.y, NO_MM_FLAGS); + if(mtmp && (mtmp->data->maligntyp == 0 || + sgn(mtmp->data->maligntyp) == sgn(castalign)) ) { + count++; + break; + } + } + } + return count; +} + +/* Let's resurrect the wizard, for some unexpected fun. */ +void +resurrect() +{ + struct monst *mtmp, **mmtmp; + long elapsed; + const char *verb; + + if (!flags.no_of_wizards) { + /* make a new Wizard */ + verb = "kill"; + mtmp = makemon(&mons[PM_WIZARD_OF_YENDOR], u.ux, u.uy, MM_NOWAIT); + } else { + /* look for a migrating Wizard */ + verb = "elude"; + mmtmp = &migrating_mons; + while ((mtmp = *mmtmp) != 0) { + if (mtmp->iswiz && + /* if he has the Amulet, he won't bring it to you */ + !mon_has_amulet(mtmp) && + (elapsed = monstermoves - mtmp->mlstmv) > 0L) { + mon_catchup_elapsed_time(mtmp, elapsed); + if (elapsed >= LARGEST_INT) elapsed = LARGEST_INT - 1; + elapsed /= 50L; + if (mtmp->msleeping && rn2((int)elapsed + 1)) + mtmp->msleeping = 0; + if (mtmp->mfrozen == 1) /* would unfreeze on next move */ + mtmp->mfrozen = 0, mtmp->mcanmove = 1; + if (mtmp->mcanmove && !mtmp->msleeping) { + *mmtmp = mtmp->nmon; + mon_arrive(mtmp, TRUE); + /* note: there might be a second Wizard; if so, + he'll have to wait til the next resurrection */ + break; + } + } + mmtmp = &mtmp->nmon; + } + } + + if (mtmp) { + mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0; + set_malign(mtmp); + pline("A voice booms out..."); + verbalize("So thou thought thou couldst %s me, fool.", verb); + } + +} + +/* Here, we make trouble for the poor shmuck who actually */ +/* managed to do in the Wizard. */ +void +intervene() +{ + int which = Is_astralevel(&u.uz) ? rnd(4) : rn2(6); + /* cases 0 and 5 don't apply on the Astral level */ + switch (which) { + case 0: + case 1: You_feel("vaguely nervous."); + break; + case 2: if (!Blind) + You("notice a %s glow surrounding you.", + hcolor(NH_BLACK)); + rndcurse(); + break; + case 3: aggravate(); + break; + case 4: (void)nasty((struct monst *)0); + break; + case 5: resurrect(); + break; + } +} + +void +wizdead() +{ + flags.no_of_wizards--; + if (!u.uevent.udemigod) { + u.uevent.udemigod = TRUE; + u.udg_cnt = rn1(250, 50); + } +} + +const char * const random_insult[] = { + "antic", + "blackguard", + "caitiff", + "chucklehead", + "coistrel", + "craven", + "cretin", + "cur", + "dastard", + "demon fodder", + "dimwit", + "dolt", + "fool", + "footpad", + "imbecile", + "knave", + "maledict", + "miscreant", + "niddering", + "poltroon", + "rattlepate", + "reprobate", + "scapegrace", + "varlet", + "villein", /* (sic.) */ + "wittol", + "worm", + "wretch", +}; + +const char * const random_malediction[] = { + "Hell shall soon claim thy remains,", + "I chortle at thee, thou pathetic", + "Prepare to die, thou", + "Resistance is useless,", + "Surrender or die, thou", + "There shall be no mercy, thou", + "Thou shalt repent of thy cunning,", + "Thou art as a flea to me,", + "Thou art doomed,", + "Thy fate is sealed,", + "Verily, thou shalt be one dead" +}; + +/* Insult or intimidate the player */ +void +cuss(mtmp) +register struct monst *mtmp; +{ + if (mtmp->iswiz) { + if (!rn2(5)) /* typical bad guy action */ + pline("%s laughs fiendishly.", Monnam(mtmp)); + else + if (u.uhave.amulet && !rn2(SIZE(random_insult))) + verbalize("Relinquish the amulet, %s!", + random_insult[rn2(SIZE(random_insult))]); + else if (u.uhp < 5 && !rn2(2)) /* Panic */ + verbalize(rn2(2) ? + "Even now thy life force ebbs, %s!" : + "Savor thy breath, %s, it be thy last!", + random_insult[rn2(SIZE(random_insult))]); + else if (mtmp->mhp < 5 && !rn2(2)) /* Parthian shot */ + verbalize(rn2(2) ? + "I shall return." : + "I'll be back."); + else + verbalize("%s %s!", + random_malediction[rn2(SIZE(random_malediction))], + random_insult[rn2(SIZE(random_insult))]); + } else if(is_lminion(mtmp)) { + com_pager(rn2(QTN_ANGELIC - 1 + (Hallucination ? 1 : 0)) + + QT_ANGELIC); + } else { + if (!rn2(5)) + pline("%s casts aspersions on your ancestry.", Monnam(mtmp)); + else + com_pager(rn2(QTN_DEMONIC) + QT_DEMONIC); + } +} + +#endif /* OVLB */ + +/*wizard.c*/ diff --git a/src/worm.c b/src/worm.c new file mode 100644 index 0000000..0380e3f --- /dev/null +++ b/src/worm.c @@ -0,0 +1,748 @@ +/* SCCS Id: @(#)worm.c 3.4 1995/01/28 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "lev.h" + +#define newseg() (struct wseg *) alloc(sizeof(struct wseg)) +#define dealloc_seg(wseg) free((genericptr_t) (wseg)) + +/* worm segment structure */ +struct wseg { + struct wseg *nseg; + xchar wx, wy; /* the segment's position */ +}; + +STATIC_DCL void FDECL(toss_wsegs, (struct wseg *,BOOLEAN_P)); +STATIC_DCL void FDECL(shrink_worm, (int)); +STATIC_DCL void FDECL(random_dir, (XCHAR_P,XCHAR_P,xchar *,xchar *)); +STATIC_DCL struct wseg *FDECL(create_worm_tail, (int)); + +/* Description of long worm implementation. + * + * Each monst struct of the head of a tailed worm has a wormno set to + * 1 <= wormno < MAX_NUM_WORMS + * If wormno == 0 this does not mean that the monster is not a worm, + * it just means that the monster does not have a long worm tail. + * + * The actual segments of a worm are not full blown monst structs. + * They are small wseg structs, and their position in the levels.monsters[][] + * array is held by the monst struct of the head of the worm. This makes + * things like probing and hit point bookkeeping much easier. + * + * The segments of the long worms on a level are kept as an array of + * singly threaded linked lists. The wormno variable is used as an index + * for these segment arrays. + * + * wtails: The first (starting struct) of a linked list. This points + * to the tail (last) segment of the worm. + * + * wheads: The last (end) of a linked list of segments. This points to + * the segment that is at the same position as the real monster + * (the head). Note that the segment that wheads[wormno] points + * to, is not displayed. It is simply there to keep track of + * where the head came from, so that worm movement and display are + * simplified later. + * Keeping the head segment of the worm at the end of the list + * of tail segments is an endless source of confusion, but it is + * necessary. + * From now on, we will use "start" and "end" to refer to the + * linked list and "head" and "tail" to refer to the worm. + * + * One final worm array is: + * + * wgrowtime: This tells us when to add another segment to the worm. + * + * When a worm is moved, we add a new segment at the head, and delete the + * segment at the tail (unless we want it to grow). This new head segment is + * located in the same square as the actual head of the worm. If we want + * to grow the worm, we don't delete the tail segment, and we give the worm + * extra hit points, which possibly go into its maximum. + * + * Non-moving worms (worm_nomove) are assumed to be surrounded by their own + * tail, and, thus, shrink instead of grow (as their tails keep going while + * their heads are stopped short). In this case, we delete the last tail + * segment, and remove hit points from the worm. + */ + +struct wseg *wheads[MAX_NUM_WORMS] = DUMMY, *wtails[MAX_NUM_WORMS] = DUMMY; +long wgrowtime[MAX_NUM_WORMS] = DUMMY; + +/* + * get_wormno() + * + * Find an unused worm tail slot and return the index. A zero means that + * there are no slots available. This means that the worm head can exist, + * it just cannot ever grow a tail. + * + * It, also, means that there is an optimisation to made. The [0] positions + * of the arrays are never used. Meaning, we really *could* have one more + * tailed worm on the level, or use a smaller array (using wormno - 1). + * + * Implementation is left to the interested hacker. + */ +int +get_wormno() +{ + register int new_wormno = 1; + + while (new_wormno < MAX_NUM_WORMS) { + if (!wheads[new_wormno]) + return new_wormno; /* found an empty wtails[] slot at new_wormno */ + new_wormno++; + } + + return(0); /* level infested with worms */ +} + +/* + * initworm() + * + * Use if (mon->wormno = get_wormno()) before calling this function! + * + * Initialize the worm entry. This will set up the worm grow time, and + * create and initialize the dummy segment for wheads[] and wtails[]. + * + * If the worm has no tail (ie get_wormno() fails) then this function need + * not be called. + */ +void +initworm(worm, wseg_count) + struct monst *worm; + int wseg_count; +{ + register struct wseg *seg, *new_tail = create_worm_tail(wseg_count); + register int wnum = worm->wormno; + +/* if (!wnum) return; bullet proofing */ + + if (new_tail) { + wtails[wnum] = new_tail; + for (seg = new_tail; seg->nseg; seg = seg->nseg); + wheads[wnum] = seg; + } else { + wtails[wnum] = wheads[wnum] = seg = newseg(); + seg->nseg = (struct wseg *) 0; + seg->wx = worm->mx; + seg->wy = worm->my; + } + wgrowtime[wnum] = 0L; +} + + +/* + * toss_wsegs() + * + * Get rid of all worm segments on and following the given pointer curr. + * The display may or may not need to be updated as we free the segments. + */ +STATIC_OVL +void +toss_wsegs(curr, display_update) + register struct wseg *curr; + register boolean display_update; +{ + register struct wseg *seg; + + while (curr) { + seg = curr->nseg; + + /* remove from level.monsters[][] */ + + /* need to check curr->wx for genocided while migrating_mon */ + if (curr->wx) { + remove_monster(curr->wx, curr->wy); + + /* update screen before deallocation */ + if (display_update) newsym(curr->wx,curr->wy); + } + + /* free memory used by the segment */ + dealloc_seg(curr); + curr = seg; + } +} + + +/* + * shrink_worm() + * + * Remove the tail segment of the worm (the starting segment of the list). + */ +STATIC_OVL +void +shrink_worm(wnum) + int wnum; /* worm number */ +{ + struct wseg *seg; + + if (wtails[wnum] == wheads[wnum]) return; /* no tail */ + + seg = wtails[wnum]; + wtails[wnum] = seg->nseg; + seg->nseg = (struct wseg *) 0; + toss_wsegs(seg, TRUE); +} + +/* + * worm_move() + * + * Check for mon->wormno before calling this function! + * + * Move the worm. Maybe grow. + */ +void +worm_move(worm) + struct monst *worm; +{ + register struct wseg *seg, *new_seg; /* new segment */ + register int wnum = worm->wormno; /* worm number */ + + +/* if (!wnum) return; bullet proofing */ + + /* + * Place a segment at the old worm head. The head has already moved. + */ + seg = wheads[wnum]; + place_worm_seg(worm, seg->wx, seg->wy); + newsym(seg->wx,seg->wy); /* display the new segment */ + + /* + * Create a new dummy segment head and place it at the end of the list. + */ + new_seg = newseg(); + new_seg->wx = worm->mx; + new_seg->wy = worm->my; + new_seg->nseg = (struct wseg *) 0; + seg->nseg = new_seg; /* attach it to the end of the list */ + wheads[wnum] = new_seg; /* move the end pointer */ + + + if (wgrowtime[wnum] <= moves) { + if (!wgrowtime[wnum]) + wgrowtime[wnum] = moves + rnd(5); + else + wgrowtime[wnum] += rn1(15, 3); + worm->mhp += 3; + if (worm->mhp > MHPMAX) worm->mhp = MHPMAX; + if (worm->mhp > worm->mhpmax) worm->mhpmax = worm->mhp; + } else + /* The worm doesn't grow, so the last segment goes away. */ + shrink_worm(wnum); +} + +/* + * worm_nomove() + * + * Check for mon->wormno before calling this function! + * + * The worm don't move so it should shrink. + */ +void +worm_nomove(worm) + register struct monst *worm; +{ + shrink_worm((int) worm->wormno); /* shrink */ + + if (worm->mhp > 3) + worm->mhp -= 3; /* mhpmax not changed ! */ + else + worm->mhp = 1; +} + +/* + * wormgone() + * + * Check for mon->wormno before calling this function! + * + * Kill a worm tail. + */ +void +wormgone(worm) + register struct monst *worm; +{ + register int wnum = worm->wormno; + +/* if (!wnum) return; bullet proofing */ + + worm->wormno = 0; + + /* This will also remove the real monster (ie 'w') from the its + * position in level.monsters[][]. + */ + toss_wsegs(wtails[wnum], TRUE); + + wheads[wnum] = wtails[wnum] = (struct wseg *) 0; +} + +/* + * wormhitu() + * + * Check for mon->wormno before calling this function! + * + * If the hero is near any part of the worm, the worm will try to attack. + */ +void +wormhitu(worm) + register struct monst *worm; +{ + register int wnum = worm->wormno; + register struct wseg *seg; + +/* if (!wnum) return; bullet proofing */ + +/* This does not work right now because mattacku() thinks that the head is + * out of range of the player. We might try to kludge, and bring the head + * within range for a tiny moment, but this needs a bit more looking at + * before we decide to do this. + */ + for (seg = wtails[wnum]; seg; seg = seg->nseg) + if (distu(seg->wx, seg->wy) < 3) + (void) mattacku(worm); +} + +/* cutworm() + * + * Check for mon->wormno before calling this function! + * + * When hitting a worm (worm) at position x, y, with a weapon (weap), + * there is a chance that the worm will be cut in half, and a chance + * that both halves will survive. + */ +void +cutworm(worm, x, y, weap) + struct monst *worm; + xchar x,y; + struct obj *weap; +{ + register struct wseg *curr, *new_tail; + register struct monst *new_worm; + int wnum = worm->wormno; + int cut_chance, new_wnum; + + if (!wnum) return; /* bullet proofing */ + + if (x == worm->mx && y == worm->my) return; /* hit on head */ + + /* cutting goes best with a bladed weapon */ + cut_chance = rnd(20); /* Normally 1-16 does not cut */ + /* Normally 17-20 does */ + + if (weap && is_blade(weap)) /* With a blade 1- 6 does not cut */ + cut_chance += 10; /* 7-20 does */ + + if (cut_chance < 17) return; /* not good enough */ + + /* Find the segment that was attacked. */ + curr = wtails[wnum]; + + while ( (curr->wx != x) || (curr->wy != y) ) { + curr = curr->nseg; + if (!curr) { + impossible("cutworm: no segment at (%d,%d)", (int) x, (int) y); + return; + } + } + + /* If this is the tail segment, then the worm just loses it. */ + if (curr == wtails[wnum]) { + shrink_worm(wnum); + return; + } + + /* + * Split the worm. The tail for the new worm is the old worm's tail. + * The tail for the old worm is the segment that follows "curr", + * and "curr" becomes the dummy segment under the new head. + */ + new_tail = wtails[wnum]; + wtails[wnum] = curr->nseg; + curr->nseg = (struct wseg *) 0; /* split the worm */ + + /* + * At this point, the old worm is correct. Any new worm will have + * it's head at "curr" and its tail at "new_tail". + */ + + /* Sometimes the tail end dies. */ + if (rn2(3) || !(new_wnum = get_wormno())) { + if (flags.mon_moving) + pline("Part of the tail of %s is cut off.", mon_nam(worm)); + else + You("cut part of the tail off of %s.", mon_nam(worm)); + toss_wsegs(new_tail, TRUE); + if (worm->mhp > 1) worm->mhp /= 2; + return; + } + + remove_monster(x, y); /* clone_mon puts new head here */ + new_worm = clone_mon(worm, x, y); + new_worm->wormno = new_wnum; /* affix new worm number */ + + /* Devalue the monster level of both halves of the worm. */ + worm->m_lev = ((unsigned)worm->m_lev <= 3) ? + (unsigned)worm->m_lev : max((unsigned)worm->m_lev - 2, 3); + new_worm->m_lev = worm->m_lev; + + /* Calculate the mhp on the new_worm for the (lower) monster level. */ + new_worm->mhpmax = new_worm->mhp = d((int)new_worm->m_lev, 8); + + /* Calculate the mhp on the old worm for the (lower) monster level. */ + if (worm->m_lev > 3) { + worm->mhpmax = d((int)worm->m_lev, 8); + if (worm->mhpmax < worm->mhp) worm->mhp = worm->mhpmax; + } + + wtails[new_wnum] = new_tail; /* We've got all the info right now */ + wheads[new_wnum] = curr; /* so we can do this faster than */ + wgrowtime[new_wnum] = 0L; /* trying to call initworm(). */ + + /* Place the new monster at all the segment locations. */ + place_wsegs(new_worm); + + if (flags.mon_moving) + pline("%s is cut in half.", Monnam(worm)); + else + You("cut %s in half.", mon_nam(worm)); +} + + +/* + * see_wsegs() + * + * Refresh all of the segments of the given worm. This is only called + * from see_monster() in display.c or when a monster goes minvis. It + * is located here for modularity. + */ +void +see_wsegs(worm) + struct monst *worm; +{ + struct wseg *curr = wtails[worm->wormno]; + +/* if (!mtmp->wormno) return; bullet proofing */ + + while (curr != wheads[worm->wormno]) { + newsym(curr->wx,curr->wy); + curr = curr->nseg; + } +} + +/* + * detect_wsegs() + * + * Display all of the segments of the given worm for detection. + */ +void +detect_wsegs(worm, use_detection_glyph) + struct monst *worm; + boolean use_detection_glyph; +{ + int num; + struct wseg *curr = wtails[worm->wormno]; + +/* if (!mtmp->wormno) return; bullet proofing */ + + while (curr != wheads[worm->wormno]) { + num = use_detection_glyph ? + detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)) : + monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); + show_glyph(curr->wx,curr->wy,num); + curr = curr->nseg; + } +} + + +/* + * save_worm() + * + * Save the worm information for later use. The count is the number + * of segments, including the dummy. Called from save.c. + */ +void +save_worm(fd, mode) + int fd, mode; +{ + int i; + int count; + struct wseg *curr, *temp; + + if (perform_bwrite(mode)) { + for (i = 1; i < MAX_NUM_WORMS; i++) { + for (count = 0, curr = wtails[i]; curr; curr = curr->nseg) count++; + /* Save number of segments */ + bwrite(fd, (genericptr_t) &count, sizeof(int)); + /* Save segment locations of the monster. */ + if (count) { + for (curr = wtails[i]; curr; curr = curr->nseg) { + bwrite(fd, (genericptr_t) &(curr->wx), sizeof(xchar)); + bwrite(fd, (genericptr_t) &(curr->wy), sizeof(xchar)); + } + } + } + bwrite(fd, (genericptr_t) wgrowtime, sizeof(wgrowtime)); + } + + if (release_data(mode)) { + /* Free the segments only. savemonchn() will take care of the + * monsters. */ + for (i = 1; i < MAX_NUM_WORMS; i++) { + if (!(curr = wtails[i])) continue; + + while (curr) { + temp = curr->nseg; + dealloc_seg(curr); /* free the segment */ + curr = temp; + } + wheads[i] = wtails[i] = (struct wseg *) 0; + } + } + +} + +/* + * rest_worm() + * + * Restore the worm information from the save file. Called from restore.c + */ +void +rest_worm(fd) + int fd; +{ + int i, j, count; + struct wseg *curr, *temp; + + for (i = 1; i < MAX_NUM_WORMS; i++) { + mread(fd, (genericptr_t) &count, sizeof(int)); + if (!count) continue; /* none */ + + /* Get the segments. */ + for (curr = (struct wseg *) 0, j = 0; j < count; j++) { + temp = newseg(); + temp->nseg = (struct wseg *) 0; + mread(fd, (genericptr_t) &(temp->wx), sizeof(xchar)); + mread(fd, (genericptr_t) &(temp->wy), sizeof(xchar)); + if (curr) + curr->nseg = temp; + else + wtails[i] = temp; + curr = temp; + } + wheads[i] = curr; + } + mread(fd, (genericptr_t) wgrowtime, sizeof(wgrowtime)); +} + +/* + * place_wsegs() + * + * Place the segments of the given worm. Called from restore.c + */ +void +place_wsegs(worm) + struct monst *worm; +{ + struct wseg *curr = wtails[worm->wormno]; + +/* if (!mtmp->wormno) return; bullet proofing */ + + while (curr != wheads[worm->wormno]) { + place_worm_seg(worm,curr->wx,curr->wy); + curr = curr->nseg; + } +} + +/* + * remove_worm() + * + * This function is equivalent to the remove_monster #define in + * rm.h, only it will take the worm *and* tail out of the levels array. + * It does not get rid of (dealloc) the worm tail structures, and it does + * not remove the mon from the fmon chain. + */ +void +remove_worm(worm) + register struct monst *worm; +{ + register struct wseg *curr = wtails[worm->wormno]; + +/* if (!mtmp->wormno) return; bullet proofing */ + + while (curr) { + remove_monster(curr->wx, curr->wy); + newsym(curr->wx, curr->wy); + curr = curr->nseg; + } +} + +/* + * place_worm_tail_randomly() + * + * Place a worm tail somewhere on a level behind the head. + * This routine essentially reverses the order of the wsegs from head + * to tail while placing them. + * x, and y are most likely the worm->mx, and worm->my, but don't *need* to + * be, if somehow the head is disjoint from the tail. + */ +void +place_worm_tail_randomly(worm, x, y) + struct monst *worm; + xchar x, y; +{ + int wnum = worm->wormno; + struct wseg *curr = wtails[wnum]; + struct wseg *new_tail; + register xchar ox = x, oy = y; + +/* if (!wnum) return; bullet proofing */ + + if (wnum && (!wtails[wnum] || !wheads[wnum]) ) { + impossible("place_worm_tail_randomly: wormno is set without a tail!"); + return; + } + + wheads[wnum] = new_tail = curr; + curr = curr->nseg; + new_tail->nseg = (struct wseg *) 0; + new_tail->wx = x; + new_tail->wy = y; + + while(curr) { + xchar nx, ny; + char tryct = 0; + + /* pick a random direction from x, y and search for goodpos() */ + + do { + random_dir(ox, oy, &nx, &ny); + } while (!goodpos(nx, ny, worm, 0) && (tryct++ < 50)); + + if (tryct < 50) { + place_worm_seg(worm, nx, ny); + curr->wx = ox = nx; + curr->wy = oy = ny; + wtails[wnum] = curr; + curr = curr->nseg; + wtails[wnum]->nseg = new_tail; + new_tail = wtails[wnum]; + newsym(nx, ny); + } else { /* Oops. Truncate because there was */ + toss_wsegs(curr, FALSE); /* no place for the rest of it */ + curr = (struct wseg *) 0; + } + } +} + +/* + * Given a coordinate x, y. + * return in *nx, *ny, the coordinates of one of the <= 8 squares adjoining. + * + * This function, and the loop it serves, could be eliminated by coding + * enexto() with a search radius. + */ +STATIC_OVL +void +random_dir(x, y, nx, ny) + register xchar x, y; + register xchar *nx, *ny; +{ + *nx = x; + *ny = y; + + *nx += (x > 1 ? /* extreme left ? */ + (x < COLNO ? /* extreme right ? */ + (rn2(3) - 1) /* neither so +1, 0, or -1 */ + : -rn2(2)) /* 0, or -1 */ + : rn2(2)); /* 0, or 1 */ + + *ny += (*nx == x ? /* same kind of thing with y */ + (y > 1 ? + (y < ROWNO ? + (rn2(2) ? + 1 + : -1) + : -1) + : 1) + : (y > 1 ? + (y < ROWNO ? + (rn2(3) - 1) + : -rn2(2)) + : rn2(2))); +} + +/* count_wsegs() + * + * returns + * the number of visible segments that a worm has. + */ + +int +count_wsegs(mtmp) + struct monst *mtmp; +{ + register int i=0; + register struct wseg *curr = (wtails[mtmp->wormno])->nseg; + +/* if (!mtmp->wormno) return 0; bullet proofing */ + + while (curr) { + i++; + curr = curr->nseg; + } + + return i; +} + +/* create_worm_tail() + * + * will create a worm tail chain of (num_segs + 1) and return a pointer to it. + */ +STATIC_OVL +struct wseg * +create_worm_tail(num_segs) + int num_segs; +{ + register int i=0; + register struct wseg *new_tail, *curr; + + if (!num_segs) return (struct wseg *)0; + + new_tail = curr = newseg(); + curr->nseg = (struct wseg *)0; + curr->wx = 0; + curr->wy = 0; + + while (i < num_segs) { + curr->nseg = newseg(); + curr = curr->nseg; + curr->nseg = (struct wseg *)0; + curr->wx = 0; + curr->wy = 0; + i++; + } + + return (new_tail); +} + +/* worm_known() + * + * Is any segment of this worm in viewing range? Note: caller must check + * invisibility and telepathy (which should only show the head anyway). + * Mostly used in the canseemon() macro. + */ +boolean +worm_known(worm) +struct monst *worm; +{ + struct wseg *curr = wtails[worm->wormno]; + + while (curr) { + if(cansee(curr->wx,curr->wy)) return TRUE; + curr = curr->nseg; + } + return FALSE; +} + +/*worm.c*/ diff --git a/src/worn.c b/src/worn.c new file mode 100644 index 0000000..61a239c --- /dev/null +++ b/src/worn.c @@ -0,0 +1,794 @@ +/* SCCS Id: @(#)worn.c 3.4 2003/01/08 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL void FDECL(m_lose_armor, (struct monst *,struct obj *)); +STATIC_DCL void FDECL(m_dowear_type, (struct monst *,long, BOOLEAN_P, BOOLEAN_P)); +STATIC_DCL int FDECL(extra_pref, (struct monst *, struct obj *)); + +const struct worn { + long w_mask; + struct obj **w_obj; +} worn[] = { + { W_ARM, &uarm }, + { W_ARMC, &uarmc }, + { W_ARMH, &uarmh }, + { W_ARMS, &uarms }, + { W_ARMG, &uarmg }, + { W_ARMF, &uarmf }, +#ifdef TOURIST + { W_ARMU, &uarmu }, +#endif + { W_RINGL, &uleft }, + { W_RINGR, &uright }, + { W_WEP, &uwep }, + { W_SWAPWEP, &uswapwep }, + { W_QUIVER, &uquiver }, + { W_AMUL, &uamul }, + { W_TOOL, &ublindf }, + { W_BALL, &uball }, + { W_CHAIN, &uchain }, + { 0, 0 } +}; + +/* This only allows for one blocking item per property */ +#define w_blocks(o,m) \ + ((o->otyp == MUMMY_WRAPPING && ((m) & W_ARMC)) ? INVIS : \ + (o->otyp == CORNUTHAUM && ((m) & W_ARMH) && \ + !Role_if(PM_WIZARD)) ? CLAIRVOYANT : 0) + /* note: monsters don't have clairvoyance, so your role + has no significant effect on their use of w_blocks() */ + + +/* Updated to use the extrinsic and blocked fields. */ +void +setworn(obj, mask) +register struct obj *obj; +long mask; +{ + register const struct worn *wp; + register struct obj *oobj; + register int p; + + if ((mask & (W_ARM|I_SPECIAL)) == (W_ARM|I_SPECIAL)) { + /* restoring saved game; no properties are conferred via skin */ + uskin = obj; + /* assert( !uarm ); */ + } else { + for(wp = worn; wp->w_mask; wp++) if(wp->w_mask & mask) { + oobj = *(wp->w_obj); + if(oobj && !(oobj->owornmask & wp->w_mask)) + impossible("Setworn: mask = %ld.", wp->w_mask); + if(oobj) { + if (u.twoweap && (oobj->owornmask & (W_WEP|W_SWAPWEP))) + u.twoweap = 0; + oobj->owornmask &= ~wp->w_mask; + if (wp->w_mask & ~(W_SWAPWEP|W_QUIVER)) { + /* leave as "x = x y", here and below, for broken + * compilers */ + p = objects[oobj->otyp].oc_oprop; + u.uprops[p].extrinsic = + u.uprops[p].extrinsic & ~wp->w_mask; + if ((p = w_blocks(oobj,mask)) != 0) + u.uprops[p].blocked &= ~wp->w_mask; + if (oobj->oartifact) + set_artifact_intrinsic(oobj, 0, mask); + } + } + *(wp->w_obj) = obj; + if(obj) { + obj->owornmask |= wp->w_mask; + /* Prevent getting/blocking intrinsics from wielding + * potions, through the quiver, etc. + * Allow weapon-tools, too. + * wp_mask should be same as mask at this point. + */ + if (wp->w_mask & ~(W_SWAPWEP|W_QUIVER)) { + if (obj->oclass == WEAPON_CLASS || is_weptool(obj) || + mask != W_WEP) { + p = objects[obj->otyp].oc_oprop; + u.uprops[p].extrinsic = + u.uprops[p].extrinsic | wp->w_mask; + if ((p = w_blocks(obj, mask)) != 0) + u.uprops[p].blocked |= wp->w_mask; + } + if (obj->oartifact) + set_artifact_intrinsic(obj, 1, mask); + } + } + } + } + update_inventory(); +} + +/* called e.g. when obj is destroyed */ +/* Updated to use the extrinsic and blocked fields. */ +void +setnotworn(obj) +register struct obj *obj; +{ + register const struct worn *wp; + register int p; + + if (!obj) return; + if (obj == uwep || obj == uswapwep) u.twoweap = 0; + for(wp = worn; wp->w_mask; wp++) + if(obj == *(wp->w_obj)) { + *(wp->w_obj) = 0; + p = objects[obj->otyp].oc_oprop; + u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask; + obj->owornmask &= ~wp->w_mask; + if (obj->oartifact) + set_artifact_intrinsic(obj, 0, wp->w_mask); + if ((p = w_blocks(obj,wp->w_mask)) != 0) + u.uprops[p].blocked &= ~wp->w_mask; + } + update_inventory(); +} + +void +mon_set_minvis(mon) +struct monst *mon; +{ + mon->perminvis = 1; + if (!mon->invis_blkd) { + mon->minvis = 1; + newsym(mon->mx, mon->my); /* make it disappear */ + if (mon->wormno) see_wsegs(mon); /* and any tail too */ + } +} + +void +mon_adjust_speed(mon, adjust, obj) +struct monst *mon; +int adjust; /* positive => increase speed, negative => decrease */ +struct obj *obj; /* item to make known if effect can be seen */ +{ + struct obj *otmp; + boolean give_msg = !in_mklev, petrify = FALSE; + unsigned int oldspeed = mon->mspeed; + + switch (adjust) { + case 2: + mon->permspeed = MFAST; + give_msg = FALSE; /* special case monster creation */ + break; + case 1: + if (mon->permspeed == MSLOW) mon->permspeed = 0; + else mon->permspeed = MFAST; + break; + case 0: /* just check for worn speed boots */ + break; + case -1: + if (mon->permspeed == MFAST) mon->permspeed = 0; + else mon->permspeed = MSLOW; + break; + case -2: + mon->permspeed = MSLOW; + give_msg = FALSE; /* (not currently used) */ + break; + case -3: /* petrification */ + /* take away intrinsic speed but don't reduce normal speed */ + if (mon->permspeed == MFAST) mon->permspeed = 0; + petrify = TRUE; + break; + } + + for (otmp = mon->minvent; otmp; otmp = otmp->nobj) + if (otmp->owornmask && objects[otmp->otyp].oc_oprop == FAST) + break; + if (otmp) /* speed boots */ + mon->mspeed = MFAST; + else + mon->mspeed = mon->permspeed; + + if (give_msg && (mon->mspeed != oldspeed || petrify) && canseemon(mon)) { + /* fast to slow (skipping intermediate state) or vice versa */ + const char *howmuch = (mon->mspeed + oldspeed == MFAST + MSLOW) ? + "much " : ""; + + if (petrify) { + /* mimic the player's petrification countdown; "slowing down" + even if fast movement rate retained via worn speed boots */ + if (flags.verbose) pline("%s is slowing down.", Monnam(mon)); + } else if (adjust > 0 || mon->mspeed == MFAST) + pline("%s is suddenly moving %sfaster.", Monnam(mon), howmuch); + else + pline("%s seems to be moving %sslower.", Monnam(mon), howmuch); + + /* might discover an object if we see the speed change happen, but + avoid making possibly forgotten book known when casting its spell */ + if (obj != 0 && obj->dknown && + objects[obj->otyp].oc_class != SPBOOK_CLASS) + makeknown(obj->otyp); + } +} + +/* armor put on or taken off; might be magical variety */ +void +update_mon_intrinsics(mon, obj, on, silently) +struct monst *mon; +struct obj *obj; +boolean on, silently; +{ + int unseen; + uchar mask; + struct obj *otmp; + int which = (int) objects[obj->otyp].oc_oprop; + + unseen = !canseemon(mon); + if (!which) goto maybe_blocks; + + if (on) { + switch (which) { + case INVIS: + mon->minvis = !mon->invis_blkd; + break; + case FAST: + { + boolean save_in_mklev = in_mklev; + if (silently) in_mklev = TRUE; + mon_adjust_speed(mon, 0, obj); + in_mklev = save_in_mklev; + break; + } + /* properties handled elsewhere */ + case ANTIMAGIC: + case REFLECTING: + break; + /* properties which have no effect for monsters */ + case CLAIRVOYANT: + case STEALTH: + case TELEPAT: + break; + /* properties which should have an effect but aren't implemented */ + case LEVITATION: + case WWALKING: + break; + /* properties which maybe should have an effect but don't */ + case DISPLACED: + case FUMBLING: + case JUMPING: + case PROTECTION: + break; + default: + if (which <= 8) { /* 1 thru 8 correspond to MR_xxx mask values */ + /* FIRE,COLD,SLEEP,DISINT,SHOCK,POISON,ACID,STONE */ + mask = (uchar) (1 << (which - 1)); + mon->mintrinsics |= (unsigned short) mask; + } + break; + } + } else { /* off */ + switch (which) { + case INVIS: + mon->minvis = mon->perminvis; + break; + case FAST: + { + boolean save_in_mklev = in_mklev; + if (silently) in_mklev = TRUE; + mon_adjust_speed(mon, 0, obj); + in_mklev = save_in_mklev; + break; + } + case FIRE_RES: + case COLD_RES: + case SLEEP_RES: + case DISINT_RES: + case SHOCK_RES: + case POISON_RES: + case ACID_RES: + case STONE_RES: + mask = (uchar) (1 << (which - 1)); + /* If the monster doesn't have this resistance intrinsically, + check whether any other worn item confers it. Note that + we don't currently check for anything conferred via simply + carrying an object. */ + if (!(mon->data->mresists & mask)) { + for (otmp = mon->minvent; otmp; otmp = otmp->nobj) + if (otmp->owornmask && + (int) objects[otmp->otyp].oc_oprop == which) + break; + if (!otmp) + mon->mintrinsics &= ~((unsigned short) mask); + } + break; + default: + break; + } + } + + maybe_blocks: + /* obj->owornmask has been cleared by this point, so we can't use it. + However, since monsters don't wield armor, we don't have to guard + against that and can get away with a blanket worn-mask value. */ + switch (w_blocks(obj,~0L)) { + case INVIS: + mon->invis_blkd = on ? 1 : 0; + mon->minvis = on ? 0 : mon->perminvis; + break; + default: + break; + } + +#ifdef STEED + if (!on && mon == u.usteed && obj->otyp == SADDLE) + dismount_steed(DISMOUNT_FELL); +#endif + + /* if couldn't see it but now can, or vice versa, update display */ + if (!silently && (unseen ^ !canseemon(mon))) + newsym(mon->mx, mon->my); +} + +int +find_mac(mon) +register struct monst *mon; +{ + register struct obj *obj; + int base = mon->data->ac; + long mwflags = mon->misc_worn_check; + + for (obj = mon->minvent; obj; obj = obj->nobj) { + if (obj->owornmask & mwflags) + base -= ARM_BONUS(obj); + /* since ARM_BONUS is positive, subtracting it increases AC */ + } + return base; +} + +/* weapons are handled separately; rings and eyewear aren't used by monsters */ + +/* Wear the best object of each type that the monster has. During creation, + * the monster can put everything on at once; otherwise, wearing takes time. + * This doesn't affect monster searching for objects--a monster may very well + * search for objects it would not want to wear, because we don't want to + * check which_armor() each round. + * + * We'll let monsters put on shirts and/or suits under worn cloaks, but + * not shirts under worn suits. This is somewhat arbitrary, but it's + * too tedious to have them remove and later replace outer garments, + * and preventing suits under cloaks makes it a little bit too easy for + * players to influence what gets worn. Putting on a shirt underneath + * already worn body armor is too obviously buggy... + */ +void +m_dowear(mon, creation) +register struct monst *mon; +boolean creation; +{ +#define RACE_EXCEPTION TRUE + /* Note the restrictions here are the same as in dowear in do_wear.c + * except for the additional restriction on intelligence. (Players + * are always intelligent, even if polymorphed). + */ + if (verysmall(mon->data) || nohands(mon->data) || is_animal(mon->data)) + return; + /* give mummies a chance to wear their wrappings + * and let skeletons wear their initial armor */ + if (mindless(mon->data) && (!creation || + (mon->data->mlet != S_MUMMY && mon->data != &mons[PM_SKELETON]))) + return; + + m_dowear_type(mon, W_AMUL, creation, FALSE); +#ifdef TOURIST + /* can't put on shirt if already wearing suit */ + if (!cantweararm(mon->data) || (mon->misc_worn_check & W_ARM)) + m_dowear_type(mon, W_ARMU, creation, FALSE); +#endif + /* treating small as a special case allows + hobbits, gnomes, and kobolds to wear cloaks */ + if (!cantweararm(mon->data) || mon->data->msize == MZ_SMALL) + m_dowear_type(mon, W_ARMC, creation, FALSE); + m_dowear_type(mon, W_ARMH, creation, FALSE); + if (!MON_WEP(mon) || !bimanual(MON_WEP(mon))) + m_dowear_type(mon, W_ARMS, creation, FALSE); + m_dowear_type(mon, W_ARMG, creation, FALSE); + if (!slithy(mon->data) && mon->data->mlet != S_CENTAUR) + m_dowear_type(mon, W_ARMF, creation, FALSE); + if (!cantweararm(mon->data)) + m_dowear_type(mon, W_ARM, creation, FALSE); + else + m_dowear_type(mon, W_ARM, creation, RACE_EXCEPTION); +} + +STATIC_OVL void +m_dowear_type(mon, flag, creation, racialexception) +struct monst *mon; +long flag; +boolean creation; +boolean racialexception; +{ + struct obj *old, *best, *obj; + int m_delay = 0; + int unseen = !canseemon(mon); + char nambuf[BUFSZ]; + + if (mon->mfrozen) return; /* probably putting previous item on */ + + /* Get a copy of monster's name before altering its visibility */ + Strcpy(nambuf, See_invisible ? Monnam(mon) : mon_nam(mon)); + + old = which_armor(mon, flag); + if (old && old->cursed) return; + if (old && flag == W_AMUL) return; /* no such thing as better amulets */ + best = old; + + for(obj = mon->minvent; obj; obj = obj->nobj) { + switch(flag) { + case W_AMUL: + if (obj->oclass != AMULET_CLASS || + (obj->otyp != AMULET_OF_LIFE_SAVING && + obj->otyp != AMULET_OF_REFLECTION)) + continue; + best = obj; + goto outer_break; /* no such thing as better amulets */ +#ifdef TOURIST + case W_ARMU: + if (!is_shirt(obj)) continue; + break; +#endif + case W_ARMC: + if (!is_cloak(obj)) continue; + break; + case W_ARMH: + if (!is_helmet(obj)) continue; + /* (flimsy exception matches polyself handling) */ + if (has_horns(mon->data) && !is_flimsy(obj)) continue; + break; + case W_ARMS: + if (!is_shield(obj)) continue; + break; + case W_ARMG: + if (!is_gloves(obj)) continue; + break; + case W_ARMF: + if (!is_boots(obj)) continue; + break; + case W_ARM: + if (!is_suit(obj)) continue; + if (racialexception && (racial_exception(mon, obj) < 1)) continue; + break; + } + if (obj->owornmask) continue; + /* I'd like to define a VISIBLE_ARM_BONUS which doesn't assume the + * monster knows obj->spe, but if I did that, a monster would keep + * switching forever between two -2 caps since when it took off one + * it would forget spe and once again think the object is better + * than what it already has. + */ + if (best && (ARM_BONUS(best) + extra_pref(mon,best) >= ARM_BONUS(obj) + extra_pref(mon,obj))) + continue; + best = obj; + } +outer_break: + if (!best || best == old) return; + + /* if wearing a cloak, account for the time spent removing + and re-wearing it when putting on a suit or shirt */ + if ((flag == W_ARM +#ifdef TOURIST + || flag == W_ARMU +#endif + ) && (mon->misc_worn_check & W_ARMC)) + m_delay += 2; + /* when upgrading a piece of armor, account for time spent + taking off current one */ + if (old) + m_delay += objects[old->otyp].oc_delay; + + if (old) /* do this first to avoid "(being worn)" */ + old->owornmask = 0L; + if (!creation) { + if (canseemon(mon)) { + char buf[BUFSZ]; + + if (old) + Sprintf(buf, " removes %s and", distant_name(old, doname)); + else + buf[0] = '\0'; + pline("%s%s puts on %s.", Monnam(mon), + buf, distant_name(best,doname)); + } /* can see it */ + m_delay += objects[best->otyp].oc_delay; + mon->mfrozen = m_delay; + if (mon->mfrozen) mon->mcanmove = 0; + } + if (old) + update_mon_intrinsics(mon, old, FALSE, creation); + mon->misc_worn_check |= flag; + best->owornmask |= flag; + update_mon_intrinsics(mon, best, TRUE, creation); + /* if couldn't see it but now can, or vice versa, */ + if (!creation && (unseen ^ !canseemon(mon))) { + if (mon->minvis && !See_invisible) { + pline("Suddenly you cannot see %s.", nambuf); + makeknown(best->otyp); + } /* else if (!mon->minvis) pline("%s suddenly appears!", Amonnam(mon)); */ + } +} +#undef RACE_EXCEPTION + +struct obj * +which_armor(mon, flag) +struct monst *mon; +long flag; +{ + register struct obj *obj; + + for(obj = mon->minvent; obj; obj = obj->nobj) + if (obj->owornmask & flag) return obj; + return((struct obj *)0); +} + +/* remove an item of armor and then drop it */ +STATIC_OVL void +m_lose_armor(mon, obj) +struct monst *mon; +struct obj *obj; +{ + mon->misc_worn_check &= ~obj->owornmask; + if (obj->owornmask) + update_mon_intrinsics(mon, obj, FALSE, FALSE); + obj->owornmask = 0L; + + obj_extract_self(obj); + place_object(obj, mon->mx, mon->my); + /* call stackobj() if we ever drop anything that can merge */ + newsym(mon->mx, mon->my); +} + +/* all objects with their bypass bit set should now be reset to normal */ +void +clear_bypasses() +{ + struct obj *otmp, *nobj; + struct monst *mtmp; + + for (otmp = fobj; otmp; otmp = nobj) { + nobj = otmp->nobj; + if (otmp->bypass) { + otmp->bypass = 0; + /* bypass will have inhibited any stacking, but since it's + used for polymorph handling, the objects here probably + have been transformed and won't be stacked in the usual + manner afterwards; so don't bother with this */ +#if 0 + if (objects[otmp->otyp].oc_merge) { + xchar ox, oy; + + (void) get_obj_location(otmp, &ox, &oy, 0); + stack_object(otmp); + newsym(ox, oy); + } +#endif /*0*/ + } + } + /* invent and mydogs chains shouldn't matter here */ + for (otmp = migrating_objs; otmp; otmp = otmp->nobj) + otmp->bypass = 0; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + otmp->bypass = 0; + } + for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) { + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + otmp->bypass = 0; + } + flags.bypasses = FALSE; +} + +void +bypass_obj(obj) +struct obj *obj; +{ + obj->bypass = 1; + flags.bypasses = TRUE; +} + +void +mon_break_armor(mon, polyspot) +struct monst *mon; +boolean polyspot; +{ + register struct obj *otmp; + struct permonst *mdat = mon->data; + boolean vis = cansee(mon->mx, mon->my); + boolean handless_or_tiny = (nohands(mdat) || verysmall(mdat)); + const char *pronoun = mhim(mon), + *ppronoun = mhis(mon); + + if (breakarm(mdat)) { + if ((otmp = which_armor(mon, W_ARM)) != 0) { + if ((Is_dragon_scales(otmp) && + mdat == Dragon_scales_to_pm(otmp)) || + (Is_dragon_mail(otmp) && mdat == Dragon_mail_to_pm(otmp))) + ; /* no message here; + "the dragon merges with his scaly armor" is odd + and the monster's previous form is already gone */ + else if (vis) + pline("%s breaks out of %s armor!", Monnam(mon), ppronoun); + else + You_hear("a cracking sound."); + m_useup(mon, otmp); + } + if ((otmp = which_armor(mon, W_ARMC)) != 0) { + if (otmp->oartifact) { + if (vis) + pline("%s %s falls off!", s_suffix(Monnam(mon)), + cloak_simple_name(otmp)); + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } else { + if (vis) + pline("%s %s tears apart!", s_suffix(Monnam(mon)), + cloak_simple_name(otmp)); + else + You_hear("a ripping sound."); + m_useup(mon, otmp); + } + } +#ifdef TOURIST + if ((otmp = which_armor(mon, W_ARMU)) != 0) { + if (vis) + pline("%s shirt rips to shreds!", s_suffix(Monnam(mon))); + else + You_hear("a ripping sound."); + m_useup(mon, otmp); + } +#endif + } else if (sliparm(mdat)) { + if ((otmp = which_armor(mon, W_ARM)) != 0) { + if (vis) + pline("%s armor falls around %s!", + s_suffix(Monnam(mon)), pronoun); + else + You_hear("a thud."); + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } + if ((otmp = which_armor(mon, W_ARMC)) != 0) { + if (vis) { + if (is_whirly(mon->data)) + pline("%s %s falls, unsupported!", + s_suffix(Monnam(mon)), cloak_simple_name(otmp)); + else + pline("%s shrinks out of %s %s!", Monnam(mon), + ppronoun, cloak_simple_name(otmp)); + } + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } +#ifdef TOURIST + if ((otmp = which_armor(mon, W_ARMU)) != 0) { + if (vis) { + if (sliparm(mon->data)) + pline("%s seeps right through %s shirt!", + Monnam(mon), ppronoun); + else + pline("%s becomes much too small for %s shirt!", + Monnam(mon), ppronoun); + } + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } +#endif + } + if (handless_or_tiny) { + /* [caller needs to handle weapon checks] */ + if ((otmp = which_armor(mon, W_ARMG)) != 0) { + if (vis) + pline("%s drops %s gloves%s!", Monnam(mon), ppronoun, + MON_WEP(mon) ? " and weapon" : ""); + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } + if ((otmp = which_armor(mon, W_ARMS)) != 0) { + if (vis) + pline("%s can no longer hold %s shield!", Monnam(mon), + ppronoun); + else + You_hear("a clank."); + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } + } + if (handless_or_tiny || has_horns(mdat)) { + if ((otmp = which_armor(mon, W_ARMH)) != 0 && + /* flimsy test for horns matches polyself handling */ + (handless_or_tiny || !is_flimsy(otmp))) { + if (vis) + pline("%s helmet falls to the %s!", + s_suffix(Monnam(mon)), surface(mon->mx, mon->my)); + else + You_hear("a clank."); + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } + } + if (handless_or_tiny || slithy(mdat) || mdat->mlet == S_CENTAUR) { + if ((otmp = which_armor(mon, W_ARMF)) != 0) { + if (vis) { + if (is_whirly(mon->data)) + pline("%s boots fall away!", + s_suffix(Monnam(mon))); + else pline("%s boots %s off %s feet!", + s_suffix(Monnam(mon)), + verysmall(mdat) ? "slide" : "are pushed", ppronoun); + } + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + } + } +#ifdef STEED + if (!can_saddle(mon)) { + if ((otmp = which_armor(mon, W_SADDLE)) != 0) { + if (polyspot) bypass_obj(otmp); + m_lose_armor(mon, otmp); + if (vis) + pline("%s saddle falls off.", s_suffix(Monnam(mon))); + } + if (mon == u.usteed) + goto noride; + } else if (mon == u.usteed && !can_ride(mon)) { + noride: + You("can no longer ride %s.", mon_nam(mon)); + if (touch_petrifies(u.usteed->data) && + !Stone_resistance && rnl(3)) { + char buf[BUFSZ]; + + You("touch %s.", mon_nam(u.usteed)); + Sprintf(buf, "falling off %s", + an(u.usteed->data->mname)); + instapetrify(buf); + } + dismount_steed(DISMOUNT_FELL); + } +#endif + return; +} + +/* bias a monster's preferences towards armor that has special benefits. */ +/* currently only does speed boots, but might be expanded if monsters get to + use more armor abilities */ +static int +extra_pref(mon, obj) +struct monst *mon; +struct obj *obj; +{ + if (obj) { + if (obj->otyp == SPEED_BOOTS && mon->permspeed != MFAST) + return 20; + } + return 0; +} + +/* + * Exceptions to things based on race. Correctly checks polymorphed player race. + * Returns: + * 0 No exception, normal rules apply. + * 1 If the race/object combination is acceptable. + * -1 If the race/object combination is unacceptable. + */ +int +racial_exception(mon, obj) +struct monst *mon; +struct obj *obj; +{ + const struct permonst *ptr = raceptr(mon); + + /* Acceptable Exceptions: */ + /* Allow hobbits to wear elven armor - LoTR */ + if (ptr == &mons[PM_HOBBIT] && is_elven_armor(obj)) + return 1; + /* Unacceptable Exceptions: */ + /* Checks for object that certain races should never use go here */ + /* return -1; */ + + return 0; +} +/*worn.c*/ diff --git a/src/write.c b/src/write.c new file mode 100644 index 0000000..669c7f1 --- /dev/null +++ b/src/write.c @@ -0,0 +1,241 @@ +/* SCCS Id: @(#)write.c 3.4 2001/11/29 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL int FDECL(cost,(struct obj *)); + +/* + * returns basecost of a scroll or a spellbook + */ +STATIC_OVL int +cost(otmp) +register struct obj *otmp; +{ + + if (otmp->oclass == SPBOOK_CLASS) + return(10 * objects[otmp->otyp].oc_level); + + switch (otmp->otyp) { +# ifdef MAIL + case SCR_MAIL: + return(2); +/* break; */ +# endif + case SCR_LIGHT: + case SCR_GOLD_DETECTION: + case SCR_FOOD_DETECTION: + case SCR_MAGIC_MAPPING: + case SCR_AMNESIA: + case SCR_FIRE: + case SCR_EARTH: + return(8); +/* break; */ + case SCR_DESTROY_ARMOR: + case SCR_CREATE_MONSTER: + case SCR_PUNISHMENT: + return(10); +/* break; */ + case SCR_CONFUSE_MONSTER: + return(12); +/* break; */ + case SCR_IDENTIFY: + return(14); +/* break; */ + case SCR_ENCHANT_ARMOR: + case SCR_REMOVE_CURSE: + case SCR_ENCHANT_WEAPON: + case SCR_CHARGING: + return(16); +/* break; */ + case SCR_SCARE_MONSTER: + case SCR_STINKING_CLOUD: + case SCR_TAMING: + case SCR_TELEPORTATION: + return(20); +/* break; */ + case SCR_GENOCIDE: + return(30); +/* break; */ + case SCR_BLANK_PAPER: + default: + impossible("You can't write such a weird scroll!"); + } + return(1000); +} + +static NEARDATA const char write_on[] = { SCROLL_CLASS, SPBOOK_CLASS, 0 }; + +int +dowrite(pen) +register struct obj *pen; +{ + register struct obj *paper; + char namebuf[BUFSZ], *nm, *bp; + register struct obj *new_obj; + int basecost, actualcost; + int curseval; + char qbuf[QBUFSZ]; + int first, last, i; + boolean by_descr = FALSE; + const char *typeword; + + if (nohands(youmonst.data)) { + You("need hands to be able to write!"); + return 0; + } else if (Glib) { + pline("%s from your %s.", + Tobjnam(pen, "slip"), makeplural(body_part(FINGER))); + dropx(pen); + return 1; + } + + /* get paper to write on */ + paper = getobj(write_on,"write on"); + if(!paper) + return(0); + typeword = (paper->oclass == SPBOOK_CLASS) ? "spellbook" : "scroll"; + if(Blind && !paper->dknown) { + You("don't know if that %s is blank or not!", typeword); + return(1); + } + paper->dknown = 1; + if(paper->otyp != SCR_BLANK_PAPER && paper->otyp != SPE_BLANK_PAPER) { + pline("That %s is not blank!", typeword); + exercise(A_WIS, FALSE); + return(1); + } + + /* what to write */ + Sprintf(qbuf, "What type of %s do you want to write?", typeword); + getlin(qbuf, namebuf); + (void)mungspaces(namebuf); /* remove any excess whitespace */ + if(namebuf[0] == '\033' || !namebuf[0]) + return(1); + nm = namebuf; + if (!strncmpi(nm, "scroll ", 7)) nm += 7; + else if (!strncmpi(nm, "spellbook ", 10)) nm += 10; + if (!strncmpi(nm, "of ", 3)) nm += 3; + + if ((bp = strstri(nm, " armour")) != 0) { + (void)strncpy(bp, " armor ", 7); /* won't add '\0' */ + (void)mungspaces(bp + 1); /* remove the extra space */ + } + + first = bases[(int)paper->oclass]; + last = bases[(int)paper->oclass + 1] - 1; + for (i = first; i <= last; i++) { + /* extra shufflable descr not representing a real object */ + if (!OBJ_NAME(objects[i])) continue; + + if (!strcmpi(OBJ_NAME(objects[i]), nm)) + goto found; + if (!strcmpi(OBJ_DESCR(objects[i]), nm)) { + by_descr = TRUE; + goto found; + } + } + + There("is no such %s!", typeword); + return 1; +found: + + if (i == SCR_BLANK_PAPER || i == SPE_BLANK_PAPER) { + You_cant("write that!"); + pline("It's obscene!"); + return 1; + } else if (i == SPE_BOOK_OF_THE_DEAD) { + pline("No mere dungeon adventurer could write that."); + return 1; + } else if (by_descr && paper->oclass == SPBOOK_CLASS && + !objects[i].oc_name_known) { + /* can't write unknown spellbooks by description */ + pline( + "Unfortunately you don't have enough information to go on."); + return 1; + } + + /* KMH, conduct */ + u.uconduct.literate++; + + new_obj = mksobj(i, FALSE, FALSE); + new_obj->bknown = (paper->bknown && pen->bknown); + + /* shk imposes a flat rate per use, not based on actual charges used */ + check_unpaid(pen); + + /* see if there's enough ink */ + basecost = cost(new_obj); + if(pen->spe < basecost/2) { + Your("marker is too dry to write that!"); + obfree(new_obj, (struct obj *) 0); + return(1); + } + + /* we're really going to write now, so calculate cost + */ + actualcost = rn1(basecost/2,basecost/2); + curseval = bcsign(pen) + bcsign(paper); + exercise(A_WIS, TRUE); + /* dry out marker */ + if (pen->spe < actualcost) { + pen->spe = 0; + Your("marker dries out!"); + /* scrolls disappear, spellbooks don't */ + if (paper->oclass == SPBOOK_CLASS) { + pline_The( + "spellbook is left unfinished and your writing fades."); + update_inventory(); /* pen charges */ + } else { + pline_The("scroll is now useless and disappears!"); + useup(paper); + } + obfree(new_obj, (struct obj *) 0); + return(1); + } + pen->spe -= actualcost; + + /* can't write if we don't know it - unless we're lucky */ + if(!(objects[new_obj->otyp].oc_name_known) && + !(objects[new_obj->otyp].oc_uname) && + (rnl(Role_if(PM_WIZARD) ? 3 : 15))) { + You("%s to write that!", by_descr ? "fail" : "don't know how"); + /* scrolls disappear, spellbooks don't */ + if (paper->oclass == SPBOOK_CLASS) { + You( + "write in your best handwriting: \"My Diary\", but it quickly fades."); + update_inventory(); /* pen charges */ + } else { + if (by_descr) { + Strcpy(namebuf, OBJ_DESCR(objects[new_obj->otyp])); + wipeout_text(namebuf, (6+MAXULEV - u.ulevel)/6, 0); + } else + Sprintf(namebuf, "%s was here!", plname); + You("write \"%s\" and the scroll disappears.", namebuf); + useup(paper); + } + obfree(new_obj, (struct obj *) 0); + return(1); + } + + /* useup old scroll / spellbook */ + useup(paper); + + /* success */ + if (new_obj->oclass == SPBOOK_CLASS) { + /* acknowledge the change in the object's description... */ + pline_The("spellbook warps strangely, then turns %s.", + OBJ_DESCR(objects[new_obj->otyp])); + } + new_obj->blessed = (curseval > 0); + new_obj->cursed = (curseval < 0); +#ifdef MAIL + if (new_obj->otyp == SCR_MAIL) new_obj->spe = 1; +#endif + new_obj = hold_another_object(new_obj, "Oops! %s out of your grasp!", + The(aobjnam(new_obj, "slip")), + (const char *)0); + return(1); +} + +/*write.c*/ diff --git a/src/zap.c b/src/zap.c new file mode 100644 index 0000000..991c429 --- /dev/null +++ b/src/zap.c @@ -0,0 +1,4146 @@ +/* SCCS Id: @(#)zap.c 3.4 2003/08/24 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +/* Disintegration rays have special treatment; corpses are never left. + * But the routine which calculates the damage is separate from the routine + * which kills the monster. The damage routine returns this cookie to + * indicate that the monster should be disintegrated. + */ +#define MAGIC_COOKIE 1000 + +#ifdef OVLB +static NEARDATA boolean obj_zapped; +static NEARDATA int poly_zapped; +#endif + +extern boolean notonhead; /* for long worms */ + +/* kludge to use mondied instead of killed */ +extern boolean m_using; + +STATIC_DCL void FDECL(costly_cancel, (struct obj *)); +STATIC_DCL void FDECL(polyuse, (struct obj*, int, int)); +STATIC_DCL void FDECL(create_polymon, (struct obj *, int)); +STATIC_DCL boolean FDECL(zap_updown, (struct obj *)); +STATIC_DCL int FDECL(zhitm, (struct monst *,int,int,struct obj **)); +STATIC_DCL void FDECL(zhitu, (int,int,const char *,XCHAR_P,XCHAR_P)); +STATIC_DCL void FDECL(revive_egg, (struct obj *)); +#ifdef STEED +STATIC_DCL boolean FDECL(zap_steed, (struct obj *)); +#endif + +#ifdef OVLB +STATIC_DCL int FDECL(zap_hit, (int,int)); +#endif +#ifdef OVL0 +STATIC_DCL void FDECL(backfire, (struct obj *)); +STATIC_DCL int FDECL(spell_hit_bonus, (int)); +#endif + +#define ZT_MAGIC_MISSILE (AD_MAGM-1) +#define ZT_FIRE (AD_FIRE-1) +#define ZT_COLD (AD_COLD-1) +#define ZT_SLEEP (AD_SLEE-1) +#define ZT_DEATH (AD_DISN-1) /* or disintegration */ +#define ZT_LIGHTNING (AD_ELEC-1) +#define ZT_POISON_GAS (AD_DRST-1) +#define ZT_ACID (AD_ACID-1) +/* 8 and 9 are currently unassigned */ + +#define ZT_WAND(x) (x) +#define ZT_SPELL(x) (10+(x)) +#define ZT_BREATH(x) (20+(x)) + +#define is_hero_spell(type) ((type) >= 10 && (type) < 20) + +#ifndef OVLB +STATIC_VAR const char are_blinded_by_the_flash[]; +extern const char * const flash_types[]; +#else +STATIC_VAR const char are_blinded_by_the_flash[] = "are blinded by the flash!"; + +const char * const flash_types[] = { /* also used in buzzmu(mcastu.c) */ + "magic missile", /* Wands must be 0-9 */ + "bolt of fire", + "bolt of cold", + "sleep ray", + "death ray", + "bolt of lightning", + "", + "", + "", + "", + + "magic missile", /* Spell equivalents must be 10-19 */ + "fireball", + "cone of cold", + "sleep ray", + "finger of death", + "bolt of lightning", /* There is no spell, used for retribution */ + "", + "", + "", + "", + + "blast of missiles", /* Dragon breath equivalents 20-29*/ + "blast of fire", + "blast of frost", + "blast of sleep gas", + "blast of disintegration", + "blast of lightning", + "blast of poison gas", + "blast of acid", + "", + "" +}; + +/* Routines for IMMEDIATE wands and spells. */ +/* bhitm: monster mtmp was hit by the effect of wand or spell otmp */ +int +bhitm(mtmp, otmp) +struct monst *mtmp; +struct obj *otmp; +{ + boolean wake = TRUE; /* Most 'zaps' should wake monster */ + boolean reveal_invis = FALSE; + boolean dbldam = Role_if(PM_KNIGHT) && u.uhave.questart; + int dmg, otyp = otmp->otyp; + const char *zap_type_text = "spell"; + struct obj *obj; + boolean disguised_mimic = (mtmp->data->mlet == S_MIMIC && + mtmp->m_ap_type != M_AP_NOTHING); + + if (u.uswallow && mtmp == u.ustuck) + reveal_invis = FALSE; + + switch(otyp) { + case WAN_STRIKING: + zap_type_text = "wand"; + /* fall through */ + case SPE_FORCE_BOLT: + reveal_invis = TRUE; + if (resists_magm(mtmp)) { /* match effect on player */ + shieldeff(mtmp->mx, mtmp->my); + break; /* skip makeknown */ + } else if (u.uswallow || rnd(20) < 10 + find_mac(mtmp)) { + dmg = d(2,12); + if(dbldam) dmg *= 2; + if (otyp == SPE_FORCE_BOLT) + dmg += spell_damage_bonus(); + hit(zap_type_text, mtmp, exclam(dmg)); + (void) resist(mtmp, otmp->oclass, dmg, TELL); + } else miss(zap_type_text, mtmp); + makeknown(otyp); + break; + case WAN_SLOW_MONSTER: + case SPE_SLOW_MONSTER: + if (!resist(mtmp, otmp->oclass, 0, NOTELL)) { + mon_adjust_speed(mtmp, -1, otmp); + m_dowear(mtmp, FALSE); /* might want speed boots */ + if (u.uswallow && (mtmp == u.ustuck) && + is_whirly(mtmp->data)) { + You("disrupt %s!", mon_nam(mtmp)); + pline("A huge hole opens up..."); + expels(mtmp, mtmp->data, TRUE); + } + } + break; + case WAN_SPEED_MONSTER: + if (!resist(mtmp, otmp->oclass, 0, NOTELL)) { + mon_adjust_speed(mtmp, 1, otmp); + m_dowear(mtmp, FALSE); /* might want speed boots */ + } + break; + case WAN_UNDEAD_TURNING: + case SPE_TURN_UNDEAD: + wake = FALSE; + if (unturn_dead(mtmp)) wake = TRUE; + if (is_undead(mtmp->data)) { + reveal_invis = TRUE; + wake = TRUE; + dmg = rnd(8); + if(dbldam) dmg *= 2; + if (otyp == SPE_TURN_UNDEAD) + dmg += spell_damage_bonus(); + flags.bypasses = TRUE; /* for make_corpse() */ + if (!resist(mtmp, otmp->oclass, dmg, NOTELL)) { + if (mtmp->mhp > 0) monflee(mtmp, 0, FALSE, TRUE); + } + } + break; + case WAN_POLYMORPH: + case SPE_POLYMORPH: + case POT_POLYMORPH: + if (resists_magm(mtmp)) { + /* magic resistance protects from polymorph traps, so make + it guard against involuntary polymorph attacks too... */ + shieldeff(mtmp->mx, mtmp->my); + } else if (!resist(mtmp, otmp->oclass, 0, NOTELL)) { + /* natural shapechangers aren't affected by system shock + (unless protection from shapechangers is interfering + with their metabolism...) */ + if (mtmp->cham == CHAM_ORDINARY && !rn2(25)) { + if (canseemon(mtmp)) { + pline("%s shudders!", Monnam(mtmp)); + makeknown(otyp); + } + /* dropped inventory shouldn't be hit by this zap */ + for (obj = mtmp->minvent; obj; obj = obj->nobj) + bypass_obj(obj); + /* flags.bypasses = TRUE; ## for make_corpse() */ + /* no corpse after system shock */ + xkilled(mtmp, 3); + } else if (newcham(mtmp, (struct permonst *)0, + (otyp != POT_POLYMORPH), FALSE)) { + if (!Hallucination && canspotmon(mtmp)) + makeknown(otyp); + } + } + break; + case WAN_CANCELLATION: + case SPE_CANCELLATION: + (void) cancel_monst(mtmp, otmp, TRUE, TRUE, FALSE); + break; + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + reveal_invis = !u_teleport_mon(mtmp, TRUE); + break; + case WAN_MAKE_INVISIBLE: + { + int oldinvis = mtmp->minvis; + char nambuf[BUFSZ]; + + /* format monster's name before altering its visibility */ + Strcpy(nambuf, Monnam(mtmp)); + mon_set_minvis(mtmp); + if (!oldinvis && knowninvisible(mtmp)) { + pline("%s turns transparent!", nambuf); + makeknown(otyp); + } + break; + } + case WAN_NOTHING: + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + wake = FALSE; + break; + case WAN_PROBING: + wake = FALSE; + reveal_invis = TRUE; + probe_monster(mtmp); + makeknown(otyp); + break; + case WAN_OPENING: + case SPE_KNOCK: + wake = FALSE; /* don't want immediate counterattack */ + if (u.uswallow && mtmp == u.ustuck) { + if (is_animal(mtmp->data)) { + if (Blind) You_feel("a sudden rush of air!"); + else pline("%s opens its mouth!", Monnam(mtmp)); + } + expels(mtmp, mtmp->data, TRUE); +#ifdef STEED + } else if (!!(obj = which_armor(mtmp, W_SADDLE))) { + mtmp->misc_worn_check &= ~obj->owornmask; + update_mon_intrinsics(mtmp, obj, FALSE, FALSE); + obj->owornmask = 0L; + obj_extract_self(obj); + place_object(obj, mtmp->mx, mtmp->my); + /* call stackobj() if we ever drop anything that can merge */ + newsym(mtmp->mx, mtmp->my); +#endif + } + break; + case SPE_HEALING: + case SPE_EXTRA_HEALING: + reveal_invis = TRUE; + if (mtmp->data != &mons[PM_PESTILENCE]) { + wake = FALSE; /* wakeup() makes the target angry */ + mtmp->mhp += d(6, otyp == SPE_EXTRA_HEALING ? 8 : 4); + if (mtmp->mhp > mtmp->mhpmax) + mtmp->mhp = mtmp->mhpmax; + if (mtmp->mblinded) { + mtmp->mblinded = 0; + mtmp->mcansee = 1; + } + if (canseemon(mtmp)) { + if (disguised_mimic) { + if (mtmp->m_ap_type == M_AP_OBJECT && + mtmp->mappearance == STRANGE_OBJECT) { + /* it can do better now */ + set_mimic_sym(mtmp); + newsym(mtmp->mx, mtmp->my); + } else + mimic_hit_msg(mtmp, otyp); + } else pline("%s looks%s better.", Monnam(mtmp), + otyp == SPE_EXTRA_HEALING ? " much" : "" ); + } + if (mtmp->mtame || mtmp->mpeaceful) { + adjalign(Role_if(PM_HEALER) ? 1 : sgn(u.ualign.type)); + } + } else { /* Pestilence */ + /* Pestilence will always resist; damage is half of 3d{4,8} */ + (void) resist(mtmp, otmp->oclass, + d(3, otyp == SPE_EXTRA_HEALING ? 8 : 4), TELL); + } + break; + case WAN_LIGHT: /* (broken wand) */ + if (flash_hits_mon(mtmp, otmp)) { + makeknown(WAN_LIGHT); + reveal_invis = TRUE; + } + break; + case WAN_SLEEP: /* (broken wand) */ + /* [wakeup() doesn't rouse victims of temporary sleep, + so it's okay to leave `wake' set to TRUE here] */ + reveal_invis = TRUE; + if (sleep_monst(mtmp, d(1 + otmp->spe, 12), WAND_CLASS)) + slept_monst(mtmp); + if (!Blind) makeknown(WAN_SLEEP); + break; + case SPE_STONE_TO_FLESH: + if (monsndx(mtmp->data) == PM_STONE_GOLEM) { + char *name = Monnam(mtmp); + /* turn into flesh golem */ + if (newcham(mtmp, &mons[PM_FLESH_GOLEM], FALSE, FALSE)) { + if (canseemon(mtmp)) + pline("%s turns to flesh!", name); + } else { + if (canseemon(mtmp)) + pline("%s looks rather fleshy for a moment.", + name); + } + } else + wake = FALSE; + break; + case SPE_DRAIN_LIFE: + dmg = rnd(8); + if(dbldam) dmg *= 2; + if (otyp == SPE_DRAIN_LIFE) + dmg += spell_damage_bonus(); + if (resists_drli(mtmp)) + shieldeff(mtmp->mx, mtmp->my); + else if (!resist(mtmp, otmp->oclass, dmg, NOTELL) && + mtmp->mhp > 0) { + mtmp->mhp -= dmg; + mtmp->mhpmax -= dmg; + if (mtmp->mhp <= 0 || mtmp->mhpmax <= 0 || mtmp->m_lev < 1) + xkilled(mtmp, 1); + else { + mtmp->m_lev--; + if (canseemon(mtmp)) + pline("%s suddenly seems weaker!", Monnam(mtmp)); + } + } + break; + default: + impossible("What an interesting effect (%d)", otyp); + break; + } + if(wake) { + if(mtmp->mhp > 0) { + wakeup(mtmp); + m_respond(mtmp); + if(mtmp->isshk && !*u.ushops) hot_pursuit(mtmp); + } else if(mtmp->m_ap_type) + seemimic(mtmp); /* might unblock if mimicing a boulder/door */ + } + /* note: bhitpos won't be set if swallowed, but that's okay since + * reveal_invis will be false. We can't use mtmp->mx, my since it + * might be an invisible worm hit on the tail. + */ + if (reveal_invis) { + if (mtmp->mhp > 0 && cansee(bhitpos.x, bhitpos.y) && + !canspotmon(mtmp)) + map_invisible(bhitpos.x, bhitpos.y); + } + return 0; +} + +void +probe_monster(mtmp) +struct monst *mtmp; +{ + struct obj *otmp; + + mstatusline(mtmp); + if (notonhead) return; /* don't show minvent for long worm tail */ + +#ifndef GOLDOBJ + if (mtmp->minvent || mtmp->mgold) { +#else + if (mtmp->minvent) { +#endif + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + otmp->dknown = 1; /* treat as "seen" */ + (void) display_minventory(mtmp, MINV_ALL, (char *)0); + } else { + pline("%s is not carrying anything.", noit_Monnam(mtmp)); + } +} + +#endif /*OVLB*/ +#ifdef OVL1 + +/* + * Return the object's physical location. This only makes sense for + * objects that are currently on the level (i.e. migrating objects + * are nowhere). By default, only things that can be seen (in hero's + * inventory, monster's inventory, or on the ground) are reported. + * By adding BURIED_TOO and/or CONTAINED_TOO flags, you can also get + * the location of buried and contained objects. Note that if an + * object is carried by a monster, its reported position may change + * from turn to turn. This function returns FALSE if the position + * is not available or subject to the constraints above. + */ +boolean +get_obj_location(obj, xp, yp, locflags) +struct obj *obj; +xchar *xp, *yp; +int locflags; +{ + switch (obj->where) { + case OBJ_INVENT: + *xp = u.ux; + *yp = u.uy; + return TRUE; + case OBJ_FLOOR: + *xp = obj->ox; + *yp = obj->oy; + return TRUE; + case OBJ_MINVENT: + if (obj->ocarry->mx) { + *xp = obj->ocarry->mx; + *yp = obj->ocarry->my; + return TRUE; + } + break; /* !mx => migrating monster */ + case OBJ_BURIED: + if (locflags & BURIED_TOO) { + *xp = obj->ox; + *yp = obj->oy; + return TRUE; + } + break; + case OBJ_CONTAINED: + if (locflags & CONTAINED_TOO) + return get_obj_location(obj->ocontainer, xp, yp, locflags); + break; + } + *xp = *yp = 0; + return FALSE; +} + +boolean +get_mon_location(mon, xp, yp, locflags) +struct monst *mon; +xchar *xp, *yp; +int locflags; /* non-zero means get location even if monster is buried */ +{ + if (mon == &youmonst) { + *xp = u.ux; + *yp = u.uy; + return TRUE; + } else if (mon->mx > 0 && (!mon->mburied || locflags)) { + *xp = mon->mx; + *yp = mon->my; + return TRUE; + } else { /* migrating or buried */ + *xp = *yp = 0; + return FALSE; + } +} + +/* used by revive() and animate_statue() */ +struct monst * +montraits(obj,cc) +struct obj *obj; +coord *cc; +{ + struct monst *mtmp = (struct monst *)0; + struct monst *mtmp2 = (struct monst *)0; + + if (obj->oxlth && (obj->oattached == OATTACHED_MONST)) + mtmp2 = get_mtraits(obj, TRUE); + if (mtmp2) { + /* save_mtraits() validated mtmp2->mnum */ + mtmp2->data = &mons[mtmp2->mnum]; + if (mtmp2->mhpmax <= 0 && !is_rider(mtmp2->data)) + return (struct monst *)0; + mtmp = makemon(mtmp2->data, + cc->x, cc->y, NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH); + if (!mtmp) return mtmp; + + /* heal the monster */ + if (mtmp->mhpmax > mtmp2->mhpmax && is_rider(mtmp2->data)) + mtmp2->mhpmax = mtmp->mhpmax; + mtmp2->mhp = mtmp2->mhpmax; + /* Get these ones from mtmp */ + mtmp2->minvent = mtmp->minvent; /*redundant*/ + /* monster ID is available if the monster died in the current + game, but should be zero if the corpse was in a bones level + (we cleared it when loading bones) */ + if (!mtmp2->m_id) + mtmp2->m_id = mtmp->m_id; + mtmp2->mx = mtmp->mx; + mtmp2->my = mtmp->my; + mtmp2->mux = mtmp->mux; + mtmp2->muy = mtmp->muy; + mtmp2->mw = mtmp->mw; + mtmp2->wormno = mtmp->wormno; + mtmp2->misc_worn_check = mtmp->misc_worn_check; + mtmp2->weapon_check = mtmp->weapon_check; + mtmp2->mtrapseen = mtmp->mtrapseen; + mtmp2->mflee = mtmp->mflee; + mtmp2->mburied = mtmp->mburied; + mtmp2->mundetected = mtmp->mundetected; + mtmp2->mfleetim = mtmp->mfleetim; + mtmp2->mlstmv = mtmp->mlstmv; + mtmp2->m_ap_type = mtmp->m_ap_type; + /* set these ones explicitly */ + mtmp2->mavenge = 0; + mtmp2->meating = 0; + mtmp2->mleashed = 0; + mtmp2->mtrapped = 0; + mtmp2->msleeping = 0; + mtmp2->mfrozen = 0; + mtmp2->mcanmove = 1; + /* most cancelled monsters return to normal, + but some need to stay cancelled */ + if (!dmgtype(mtmp2->data, AD_SEDU) +#ifdef SEDUCE + && !dmgtype(mtmp2->data, AD_SSEX) +#endif + ) mtmp2->mcan = 0; + mtmp2->mcansee = 1; /* set like in makemon */ + mtmp2->mblinded = 0; + mtmp2->mstun = 0; + mtmp2->mconf = 0; + replmon(mtmp,mtmp2); + } + return mtmp2; +} + +/* + * get_container_location() returns the following information + * about the outermost container: + * loc argument gets set to: + * OBJ_INVENT if in hero's inventory; return 0. + * OBJ_FLOOR if on the floor; return 0. + * OBJ_BURIED if buried; return 0. + * OBJ_MINVENT if in monster's inventory; return monster. + * container_nesting is updated with the nesting depth of the containers + * if applicable. + */ +struct monst * +get_container_location(obj, loc, container_nesting) +struct obj *obj; +int *loc; +int *container_nesting; +{ + if (!obj || !loc) + return 0; + + if (container_nesting) *container_nesting = 0; + while (obj && obj->where == OBJ_CONTAINED) { + if (container_nesting) *container_nesting += 1; + obj = obj->ocontainer; + } + if (obj) { + *loc = obj->where; /* outermost container's location */ + if (obj->where == OBJ_MINVENT) return obj->ocarry; + } + return (struct monst *)0; +} + +/* + * Attempt to revive the given corpse, return the revived monster if + * successful. Note: this does NOT use up the corpse if it fails. + */ +struct monst * +revive(obj) +register struct obj *obj; +{ + register struct monst *mtmp = (struct monst *)0; + struct obj *container = (struct obj *)0; + int container_nesting = 0; + schar savetame = 0; + boolean recorporealization = FALSE; + boolean in_container = FALSE; + if(obj->otyp == CORPSE) { + int montype = obj->corpsenm; + xchar x, y; + + if (obj->where == OBJ_CONTAINED) { + /* deal with corpses in [possibly nested] containers */ + struct monst *carrier; + int holder = 0; + + container = obj->ocontainer; + carrier = get_container_location(container, &holder, + &container_nesting); + switch(holder) { + case OBJ_MINVENT: + x = carrier->mx; y = carrier->my; + in_container = TRUE; + break; + case OBJ_INVENT: + x = u.ux; y = u.uy; + in_container = TRUE; + break; + case OBJ_FLOOR: + if (!get_obj_location(obj, &x, &y, CONTAINED_TOO)) + return (struct monst *) 0; + in_container = TRUE; + break; + default: + return (struct monst *)0; + } + } else { + /* only for invent, minvent, or floor */ + if (!get_obj_location(obj, &x, &y, 0)) + return (struct monst *) 0; + } + if (in_container) { + /* Rules for revival from containers: + - the container cannot be locked + - the container cannot be heavily nested (>2 is arbitrary) + - the container cannot be a statue or bag of holding + (except in very rare cases for the latter) + */ + if (!x || !y || container->olocked || container_nesting > 2 || + container->otyp == STATUE || + (container->otyp == BAG_OF_HOLDING && rn2(40))) + return (struct monst *)0; + } + + if (MON_AT(x,y)) { + coord new_xy; + + if (enexto(&new_xy, x, y, &mons[montype])) + x = new_xy.x, y = new_xy.y; + } + + if(cant_create(&montype, TRUE)) { + /* make a zombie or worm instead */ + mtmp = makemon(&mons[montype], x, y, + NO_MINVENT|MM_NOWAIT); + if (mtmp) { + mtmp->mhp = mtmp->mhpmax = 100; + mon_adjust_speed(mtmp, 2, (struct obj *)0); /* MFAST */ + } + } else { + if (obj->oxlth && (obj->oattached == OATTACHED_MONST)) { + coord xy; + xy.x = x; xy.y = y; + mtmp = montraits(obj, &xy); + if (mtmp && mtmp->mtame && !mtmp->isminion) + wary_dog(mtmp, TRUE); + } else + mtmp = makemon(&mons[montype], x, y, + NO_MINVENT|MM_NOWAIT|MM_NOCOUNTBIRTH); + if (mtmp) { + if (obj->oxlth && (obj->oattached == OATTACHED_M_ID)) { + unsigned m_id; + struct monst *ghost; + (void) memcpy((genericptr_t)&m_id, + (genericptr_t)obj->oextra, sizeof(m_id)); + ghost = find_mid(m_id, FM_FMON); + if (ghost && ghost->data == &mons[PM_GHOST]) { + int x2, y2; + x2 = ghost->mx; y2 = ghost->my; + if (ghost->mtame) + savetame = ghost->mtame; + if (canseemon(ghost)) + pline("%s is suddenly drawn into its former body!", + Monnam(ghost)); + mondead(ghost); + recorporealization = TRUE; + newsym(x2, y2); + } + /* don't mess with obj->oxlth here */ + obj->oattached = OATTACHED_NOTHING; + } + /* Monster retains its name */ + if (obj->onamelth) + mtmp = christen_monst(mtmp, ONAME(obj)); + /* flag the quest leader as alive. */ + if (mtmp->data->msound == MS_LEADER || mtmp->m_id == + quest_status.leader_m_id) + quest_status.leader_is_dead = FALSE; + } + } + if (mtmp) { + if (obj->oeaten) + mtmp->mhp = eaten_stat(mtmp->mhp, obj); + /* track that this monster was revived at least once */ + mtmp->mrevived = 1; + + if (recorporealization) { + /* If mtmp is revivification of former tame ghost*/ + if (savetame) { + struct monst *mtmp2 = tamedog(mtmp, (struct obj *)0); + if (mtmp2) { + mtmp2->mtame = savetame; + mtmp = mtmp2; + } + } + /* was ghost, now alive, it's all very confusing */ + mtmp->mconf = 1; + } + + switch (obj->where) { + case OBJ_INVENT: + useup(obj); + break; + case OBJ_FLOOR: + /* in case MON_AT+enexto for invisible mon */ + x = obj->ox, y = obj->oy; + /* not useupf(), which charges */ + if (obj->quan > 1L) + obj = splitobj(obj, 1L); + delobj(obj); + newsym(x, y); + break; + case OBJ_MINVENT: + m_useup(obj->ocarry, obj); + break; + case OBJ_CONTAINED: + obj_extract_self(obj); + obfree(obj, (struct obj *) 0); + break; + default: + panic("revive"); + } + } + } + return mtmp; +} + +STATIC_OVL void +revive_egg(obj) +struct obj *obj; +{ + /* + * Note: generic eggs with corpsenm set to NON_PM will never hatch. + */ + if (obj->otyp != EGG) return; + if (obj->corpsenm != NON_PM && !dead_species(obj->corpsenm, TRUE)) + attach_egg_hatch_timeout(obj); +} + +/* try to revive all corpses and eggs carried by `mon' */ +int +unturn_dead(mon) +struct monst *mon; +{ + struct obj *otmp, *otmp2; + struct monst *mtmp2; + char owner[BUFSZ], corpse[BUFSZ]; + boolean youseeit; + int once = 0, res = 0; + + youseeit = (mon == &youmonst) ? TRUE : canseemon(mon); + otmp2 = (mon == &youmonst) ? invent : mon->minvent; + + while ((otmp = otmp2) != 0) { + otmp2 = otmp->nobj; + if (otmp->otyp == EGG) + revive_egg(otmp); + if (otmp->otyp != CORPSE) continue; + /* save the name; the object is liable to go away */ + if (youseeit) Strcpy(corpse, corpse_xname(otmp, TRUE)); + + /* for a merged group, only one is revived; should this be fixed? */ + if ((mtmp2 = revive(otmp)) != 0) { + ++res; + if (youseeit) { + if (!once++) Strcpy(owner, + (mon == &youmonst) ? "Your" : + s_suffix(Monnam(mon))); + pline("%s %s suddenly comes alive!", owner, corpse); + } else if (canseemon(mtmp2)) + pline("%s suddenly appears!", Amonnam(mtmp2)); + } + } + return res; +} +#endif /*OVL1*/ + +#ifdef OVLB +static const char charged_objs[] = { WAND_CLASS, WEAPON_CLASS, ARMOR_CLASS, 0 }; + +STATIC_OVL void +costly_cancel(obj) +register struct obj *obj; +{ + char objroom; + struct monst *shkp = (struct monst *)0; + + if (obj->no_charge) return; + + switch (obj->where) { + case OBJ_INVENT: + if (obj->unpaid) { + shkp = shop_keeper(*u.ushops); + if (!shkp) return; + Norep("You cancel an unpaid object, you pay for it!"); + bill_dummy_object(obj); + } + break; + case OBJ_FLOOR: + objroom = *in_rooms(obj->ox, obj->oy, SHOPBASE); + shkp = shop_keeper(objroom); + if (!shkp || !inhishop(shkp)) return; + if (costly_spot(u.ux, u.uy) && objroom == *u.ushops) { + Norep("You cancel it, you pay for it!"); + bill_dummy_object(obj); + } else + (void) stolen_value(obj, obj->ox, obj->oy, FALSE, FALSE); + break; + } +} + +/* cancel obj, possibly carried by you or a monster */ +void +cancel_item(obj) +register struct obj *obj; +{ + boolean u_ring = (obj == uleft) || (obj == uright); + register boolean holy = (obj->otyp == POT_WATER && obj->blessed); + + switch(obj->otyp) { + case RIN_GAIN_STRENGTH: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_STR) -= obj->spe; + flags.botl = 1; + } + break; + case RIN_GAIN_CONSTITUTION: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_CON) -= obj->spe; + flags.botl = 1; + } + break; + case RIN_ADORNMENT: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_CHA) -= obj->spe; + flags.botl = 1; + } + break; + case RIN_INCREASE_ACCURACY: + if ((obj->owornmask & W_RING) && u_ring) + u.uhitinc -= obj->spe; + break; + case RIN_INCREASE_DAMAGE: + if ((obj->owornmask & W_RING) && u_ring) + u.udaminc -= obj->spe; + break; + case GAUNTLETS_OF_DEXTERITY: + if ((obj->owornmask & W_ARMG) && (obj == uarmg)) { + ABON(A_DEX) -= obj->spe; + flags.botl = 1; + } + break; + case HELM_OF_BRILLIANCE: + if ((obj->owornmask & W_ARMH) && (obj == uarmh)) { + ABON(A_INT) -= obj->spe; + ABON(A_WIS) -= obj->spe; + flags.botl = 1; + } + break; + /* case RIN_PROTECTION: not needed */ + } + if (objects[obj->otyp].oc_magic + || (obj->spe && (obj->oclass == ARMOR_CLASS || + obj->oclass == WEAPON_CLASS || is_weptool(obj))) + || obj->otyp == POT_ACID || obj->otyp == POT_SICKNESS) { + if (obj->spe != ((obj->oclass == WAND_CLASS) ? -1 : 0) && + obj->otyp != WAN_CANCELLATION && + /* can't cancel cancellation */ + obj->otyp != MAGIC_LAMP && + obj->otyp != CANDELABRUM_OF_INVOCATION) { + costly_cancel(obj); + obj->spe = (obj->oclass == WAND_CLASS) ? -1 : 0; + } + switch (obj->oclass) { + case SCROLL_CLASS: + costly_cancel(obj); + obj->otyp = SCR_BLANK_PAPER; + obj->spe = 0; + break; + case SPBOOK_CLASS: + if (obj->otyp != SPE_CANCELLATION && + obj->otyp != SPE_BOOK_OF_THE_DEAD) { + costly_cancel(obj); + obj->otyp = SPE_BLANK_PAPER; + } + break; + case POTION_CLASS: + costly_cancel(obj); + if (obj->otyp == POT_SICKNESS || + obj->otyp == POT_SEE_INVISIBLE) { + /* sickness is "biologically contaminated" fruit juice; cancel it + * and it just becomes fruit juice... whereas see invisible + * tastes like "enchanted" fruit juice, it similarly cancels. + */ + obj->otyp = POT_FRUIT_JUICE; + } else { + obj->otyp = POT_WATER; + obj->odiluted = 0; /* same as any other water */ + } + break; + } + } + if (holy) costly_cancel(obj); + unbless(obj); + uncurse(obj); +#ifdef INVISIBLE_OBJECTS + if (obj->oinvis) obj->oinvis = 0; +#endif + return; +} + +/* Remove a positive enchantment or charge from obj, + * possibly carried by you or a monster + */ +boolean +drain_item(obj) +register struct obj *obj; +{ + boolean u_ring; + + /* Is this a charged/enchanted object? */ + if (!obj || (!objects[obj->otyp].oc_charged && + obj->oclass != WEAPON_CLASS && + obj->oclass != ARMOR_CLASS && !is_weptool(obj)) || + obj->spe <= 0) + return (FALSE); + if (obj_resists(obj, 10, 90)) + return (FALSE); + + /* Charge for the cost of the object */ + costly_cancel(obj); /* The term "cancel" is okay for now */ + + /* Drain the object and any implied effects */ + obj->spe--; + u_ring = (obj == uleft) || (obj == uright); + switch(obj->otyp) { + case RIN_GAIN_STRENGTH: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_STR)--; + flags.botl = 1; + } + break; + case RIN_GAIN_CONSTITUTION: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_CON)--; + flags.botl = 1; + } + break; + case RIN_ADORNMENT: + if ((obj->owornmask & W_RING) && u_ring) { + ABON(A_CHA)--; + flags.botl = 1; + } + break; + case RIN_INCREASE_ACCURACY: + if ((obj->owornmask & W_RING) && u_ring) + u.uhitinc--; + break; + case RIN_INCREASE_DAMAGE: + if ((obj->owornmask & W_RING) && u_ring) + u.udaminc--; + break; + case HELM_OF_BRILLIANCE: + if ((obj->owornmask & W_ARMH) && (obj == uarmh)) { + ABON(A_INT)--; + ABON(A_WIS)--; + flags.botl = 1; + } + break; + case GAUNTLETS_OF_DEXTERITY: + if ((obj->owornmask & W_ARMG) && (obj == uarmg)) { + ABON(A_DEX)--; + flags.botl = 1; + } + break; + case RIN_PROTECTION: + flags.botl = 1; + break; + } + if (carried(obj)) update_inventory(); + return (TRUE); +} + +#endif /*OVLB*/ +#ifdef OVL0 + +boolean +obj_resists(obj, ochance, achance) +struct obj *obj; +int ochance, achance; /* percent chance for ordinary objects, artifacts */ +{ + if (obj->otyp == AMULET_OF_YENDOR || + obj->otyp == SPE_BOOK_OF_THE_DEAD || + obj->otyp == CANDELABRUM_OF_INVOCATION || + obj->otyp == BELL_OF_OPENING || + (obj->otyp == CORPSE && is_rider(&mons[obj->corpsenm]))) { + return TRUE; + } else { + int chance = rn2(100); + + return((boolean)(chance < (obj->oartifact ? achance : ochance))); + } +} + +boolean +obj_shudders(obj) +struct obj *obj; +{ + int zap_odds; + + if (obj->oclass == WAND_CLASS) + zap_odds = 3; /* half-life = 2 zaps */ + else if (obj->cursed) + zap_odds = 3; /* half-life = 2 zaps */ + else if (obj->blessed) + zap_odds = 12; /* half-life = 8 zaps */ + else + zap_odds = 8; /* half-life = 6 zaps */ + + /* adjust for "large" quantities of identical things */ + if(obj->quan > 4L) zap_odds /= 2; + + return((boolean)(! rn2(zap_odds))); +} +#endif /*OVL0*/ +#ifdef OVLB + +/* Use up at least minwt number of things made of material mat. + * There's also a chance that other stuff will be used up. Finally, + * there's a random factor here to keep from always using the stuff + * at the top of the pile. + */ +STATIC_OVL void +polyuse(objhdr, mat, minwt) + struct obj *objhdr; + int mat, minwt; +{ + register struct obj *otmp, *otmp2; + + for(otmp = objhdr; minwt > 0 && otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if (otmp == uball || otmp == uchain) continue; + if (obj_resists(otmp, 0, 0)) continue; /* preserve unique objects */ +#ifdef MAIL + if (otmp->otyp == SCR_MAIL) continue; +#endif + + if (((int) objects[otmp->otyp].oc_material == mat) == + (rn2(minwt + 1) != 0)) { + /* appropriately add damage to bill */ + if (costly_spot(otmp->ox, otmp->oy)) { + if (*u.ushops) + addtobill(otmp, FALSE, FALSE, FALSE); + else + (void)stolen_value(otmp, + otmp->ox, otmp->oy, FALSE, FALSE); + } + if (otmp->quan < LARGEST_INT) + minwt -= (int)otmp->quan; + else + minwt = 0; + delobj(otmp); + } + } +} + +/* + * Polymorph some of the stuff in this pile into a monster, preferably + * a golem of the kind okind. + */ +STATIC_OVL void +create_polymon(obj, okind) + struct obj *obj; + int okind; +{ + struct permonst *mdat = (struct permonst *)0; + struct monst *mtmp; + const char *material; + int pm_index; + + /* no golems if you zap only one object -- not enough stuff */ + if(!obj || (!obj->nexthere && obj->quan == 1L)) return; + + /* some of these choices are arbitrary */ + switch(okind) { + case IRON: + case METAL: + case MITHRIL: + pm_index = PM_IRON_GOLEM; + material = "metal "; + break; + case COPPER: + case SILVER: + case PLATINUM: + case GEMSTONE: + case MINERAL: + pm_index = rn2(2) ? PM_STONE_GOLEM : PM_CLAY_GOLEM; + material = "lithic "; + break; + case 0: + case FLESH: + /* there is no flesh type, but all food is type 0, so we use it */ + pm_index = PM_FLESH_GOLEM; + material = "organic "; + break; + case WOOD: + pm_index = PM_WOOD_GOLEM; + material = "wood "; + break; + case LEATHER: + pm_index = PM_LEATHER_GOLEM; + material = "leather "; + break; + case CLOTH: + pm_index = PM_ROPE_GOLEM; + material = "cloth "; + break; + case BONE: + pm_index = PM_SKELETON; /* nearest thing to "bone golem" */ + material = "bony "; + break; + case GOLD: + pm_index = PM_GOLD_GOLEM; + material = "gold "; + break; + case GLASS: + pm_index = PM_GLASS_GOLEM; + material = "glassy "; + break; + case PAPER: + pm_index = PM_PAPER_GOLEM; + material = "paper "; + break; + default: + /* if all else fails... */ + pm_index = PM_STRAW_GOLEM; + material = ""; + break; + } + + if (!(mvitals[pm_index].mvflags & G_GENOD)) + mdat = &mons[pm_index]; + + mtmp = makemon(mdat, obj->ox, obj->oy, NO_MM_FLAGS); + polyuse(obj, okind, (int)mons[pm_index].cwt); + + if(mtmp && cansee(mtmp->mx, mtmp->my)) { + pline("Some %sobjects meld, and %s arises from the pile!", + material, a_monnam(mtmp)); + } +} + +/* Assumes obj is on the floor. */ +void +do_osshock(obj) +struct obj *obj; +{ + long i; + +#ifdef MAIL + if (obj->otyp == SCR_MAIL) return; +#endif + obj_zapped = TRUE; + + if(poly_zapped < 0) { + /* some may metamorphosize */ + for (i = obj->quan; i; i--) + if (! rn2(Luck + 45)) { + poly_zapped = objects[obj->otyp].oc_material; + break; + } + } + + /* if quan > 1 then some will survive intact */ + if (obj->quan > 1L) { + if (obj->quan > LARGEST_INT) + obj = splitobj(obj, (long)rnd(30000)); + else + obj = splitobj(obj, (long)rnd((int)obj->quan - 1)); + } + + /* appropriately add damage to bill */ + if (costly_spot(obj->ox, obj->oy)) { + if (*u.ushops) + addtobill(obj, FALSE, FALSE, FALSE); + else + (void)stolen_value(obj, + obj->ox, obj->oy, FALSE, FALSE); + } + + /* zap the object */ + delobj(obj); +} + +/* + * Polymorph the object to the given object ID. If the ID is STRANGE_OBJECT + * then pick random object from the source's class (this is the standard + * "polymorph" case). If ID is set to a specific object, inhibit fusing + * n objects into 1. This could have been added as a flag, but currently + * it is tied to not being the standard polymorph case. The new polymorphed + * object replaces obj in its link chains. Return value is a pointer to + * the new object. + * + * This should be safe to call for an object anywhere. + */ +struct obj * +poly_obj(obj, id) + struct obj *obj; + int id; +{ + struct obj *otmp; + xchar ox, oy; + boolean can_merge = (id == STRANGE_OBJECT); + int obj_location = obj->where; + + if (obj->otyp == BOULDER && In_sokoban(&u.uz)) + change_luck(-1); /* Sokoban guilt */ + if (id == STRANGE_OBJECT) { /* preserve symbol */ + int try_limit = 3; + /* Try up to 3 times to make the magic-or-not status of + the new item be the same as it was for the old one. */ + otmp = (struct obj *)0; + do { + if (otmp) delobj(otmp); + otmp = mkobj(obj->oclass, FALSE); + } while (--try_limit > 0 && + objects[obj->otyp].oc_magic != objects[otmp->otyp].oc_magic); + } else { + /* literally replace obj with this new thing */ + otmp = mksobj(id, FALSE, FALSE); + /* Actually more things use corpsenm but they polymorph differently */ +#define USES_CORPSENM(typ) ((typ)==CORPSE || (typ)==STATUE || (typ)==FIGURINE) + if (USES_CORPSENM(obj->otyp) && USES_CORPSENM(id)) + otmp->corpsenm = obj->corpsenm; +#undef USES_CORPSENM + } + + /* preserve quantity */ + otmp->quan = obj->quan; + /* preserve the shopkeepers (lack of) interest */ + otmp->no_charge = obj->no_charge; + /* preserve inventory letter if in inventory */ + if (obj_location == OBJ_INVENT) + otmp->invlet = obj->invlet; +#ifdef MAIL + /* You can't send yourself 100 mail messages and then + * polymorph them into useful scrolls + */ + if (obj->otyp == SCR_MAIL) { + otmp->otyp = SCR_MAIL; + otmp->spe = 1; + } +#endif + + /* avoid abusing eggs laid by you */ + if (obj->otyp == EGG && obj->spe) { + int mnum, tryct = 100; + + /* first, turn into a generic egg */ + if (otmp->otyp == EGG) + kill_egg(otmp); + else { + otmp->otyp = EGG; + otmp->owt = weight(otmp); + } + otmp->corpsenm = NON_PM; + otmp->spe = 0; + + /* now change it into something layed by the hero */ + while (tryct--) { + mnum = can_be_hatched(random_monster()); + if (mnum != NON_PM && !dead_species(mnum, TRUE)) { + otmp->spe = 1; /* layed by hero */ + otmp->corpsenm = mnum; + attach_egg_hatch_timeout(otmp); + break; + } + } + } + + /* keep special fields (including charges on wands) */ + if (index(charged_objs, otmp->oclass)) otmp->spe = obj->spe; + otmp->recharged = obj->recharged; + + otmp->cursed = obj->cursed; + otmp->blessed = obj->blessed; + otmp->oeroded = obj->oeroded; + otmp->oeroded2 = obj->oeroded2; + if (!is_flammable(otmp) && !is_rustprone(otmp)) otmp->oeroded = 0; + if (!is_corrodeable(otmp) && !is_rottable(otmp)) otmp->oeroded2 = 0; + if (is_damageable(otmp)) + otmp->oerodeproof = obj->oerodeproof; + + /* Keep chest/box traps and poisoned ammo if we may */ + if (obj->otrapped && Is_box(otmp)) otmp->otrapped = TRUE; + + if (obj->opoisoned && is_poisonable(otmp)) + otmp->opoisoned = TRUE; + + if (id == STRANGE_OBJECT && obj->otyp == CORPSE) { + /* turn crocodile corpses into shoes */ + if (obj->corpsenm == PM_CROCODILE) { + otmp->otyp = LOW_BOOTS; + otmp->oclass = ARMOR_CLASS; + otmp->spe = 0; + otmp->oeroded = 0; + otmp->oerodeproof = TRUE; + otmp->quan = 1L; + otmp->cursed = FALSE; + } + } + + /* no box contents --KAA */ + if (Has_contents(otmp)) delete_contents(otmp); + + /* 'n' merged objects may be fused into 1 object */ + if (otmp->quan > 1L && (!objects[otmp->otyp].oc_merge || + (can_merge && otmp->quan > (long)rn2(1000)))) + otmp->quan = 1L; + + switch (otmp->oclass) { + + case TOOL_CLASS: + if (otmp->otyp == MAGIC_LAMP) { + otmp->otyp = OIL_LAMP; + otmp->age = 1500L; /* "best" oil lamp possible */ + } else if (otmp->otyp == MAGIC_MARKER) { + otmp->recharged = 1; /* degraded quality */ + } + /* don't care about the recharge count of other tools */ + break; + + case WAND_CLASS: + while (otmp->otyp == WAN_WISHING || otmp->otyp == WAN_POLYMORPH) + otmp->otyp = rnd_class(WAN_LIGHT, WAN_LIGHTNING); + /* altering the object tends to degrade its quality + (analogous to spellbook `read count' handling) */ + if ((int)otmp->recharged < rn2(7)) /* recharge_limit */ + otmp->recharged++; + break; + + case POTION_CLASS: + while (otmp->otyp == POT_POLYMORPH) + otmp->otyp = rnd_class(POT_GAIN_ABILITY, POT_WATER); + break; + + case SPBOOK_CLASS: + while (otmp->otyp == SPE_POLYMORPH) + otmp->otyp = rnd_class(SPE_DIG, SPE_BLANK_PAPER); + /* reduce spellbook abuse */ + otmp->spestudied = obj->spestudied + 1; + break; + + case GEM_CLASS: + if (otmp->quan > (long) rnd(4) && + objects[obj->otyp].oc_material == MINERAL && + objects[otmp->otyp].oc_material != MINERAL) { + otmp->otyp = ROCK; /* transmutation backfired */ + otmp->quan /= 2L; /* some material has been lost */ + } + break; + } + + /* update the weight */ + otmp->owt = weight(otmp); + + /* for now, take off worn items being polymorphed */ + if (obj_location == OBJ_INVENT) { + if (id == STRANGE_OBJECT) + remove_worn_item(obj, TRUE); + else { + /* This is called only for stone to flesh. It's a lot simpler + * than it otherwise might be. We don't need to check for + * special effects when putting them on (no meat objects have + * any) and only three worn masks are possible. + */ + otmp->owornmask = obj->owornmask; + remove_worn_item(obj, TRUE); + setworn(otmp, otmp->owornmask); + if (otmp->owornmask & LEFT_RING) + uleft = otmp; + if (otmp->owornmask & RIGHT_RING) + uright = otmp; + if (otmp->owornmask & W_WEP) + uwep = otmp; + if (otmp->owornmask & W_SWAPWEP) + uswapwep = otmp; + if (otmp->owornmask & W_QUIVER) + uquiver = otmp; + goto no_unwear; + } + } + + /* preserve the mask in case being used by something else */ + otmp->owornmask = obj->owornmask; +no_unwear: + + if (obj_location == OBJ_FLOOR && obj->otyp == BOULDER && + otmp->otyp != BOULDER) + unblock_point(obj->ox, obj->oy); + + /* ** we are now done adjusting the object ** */ + + + /* swap otmp for obj */ + replace_object(obj, otmp); + if (obj_location == OBJ_INVENT) { + /* + * We may need to do extra adjustments for the hero if we're + * messing with the hero's inventory. The following calls are + * equivalent to calling freeinv on obj and addinv on otmp, + * while doing an in-place swap of the actual objects. + */ + freeinv_core(obj); + addinv_core1(otmp); + addinv_core2(otmp); + } + + if ((!carried(otmp) || obj->unpaid) && + get_obj_location(otmp, &ox, &oy, BURIED_TOO|CONTAINED_TOO) && + costly_spot(ox, oy)) { + register struct monst *shkp = + shop_keeper(*in_rooms(ox, oy, SHOPBASE)); + + if ((!obj->no_charge || + (Has_contents(obj) && + (contained_cost(obj, shkp, 0L, FALSE, FALSE) != 0L))) + && inhishop(shkp)) { + if(shkp->mpeaceful) { + if(*u.ushops && *in_rooms(u.ux, u.uy, 0) == + *in_rooms(shkp->mx, shkp->my, 0) && + !costly_spot(u.ux, u.uy)) + make_angry_shk(shkp, ox, oy); + else { + pline("%s gets angry!", Monnam(shkp)); + hot_pursuit(shkp); + } + } else Norep("%s is furious!", Monnam(shkp)); + } + } + delobj(obj); + return otmp; +} + +/* + * Object obj was hit by the effect of the wand/spell otmp. Return + * non-zero if the wand/spell had any effect. + */ +int +bhito(obj, otmp) +struct obj *obj, *otmp; +{ + int res = 1; /* affected object by default */ + xchar refresh_x, refresh_y; + + if (obj->bypass) { + /* The bypass bit is currently only used as follows: + * + * POLYMORPH - When a monster being polymorphed drops something + * from its inventory as a result of the change. + * If the items fall to the floor, they are not + * subject to direct subsequent polymorphing + * themselves on that same zap. This makes it + * consistent with items that remain in the + * monster's inventory. They are not polymorphed + * either. + * UNDEAD_TURNING - When an undead creature gets killed via + * undead turning, prevent its corpse from being + * immediately revived by the same effect. + * + * The bypass bit on all objects is reset each turn, whenever + * flags.bypasses is set. + * + * We check the obj->bypass bit above AND flags.bypasses + * as a safeguard against any stray occurrence left in an obj + * struct someplace, although that should never happen. + */ + if (flags.bypasses) + return 0; + else { +#ifdef DEBUG + pline("%s for a moment.", Tobjnam(obj, "pulsate")); +#endif + obj->bypass = 0; + } + } + + /* + * Some parts of this function expect the object to be on the floor + * obj->{ox,oy} to be valid. The exception to this (so far) is + * for the STONE_TO_FLESH spell. + */ + if (!(obj->where == OBJ_FLOOR || otmp->otyp == SPE_STONE_TO_FLESH)) + impossible("bhito: obj is not floor or Stone To Flesh spell"); + + if (obj == uball) { + res = 0; + } else if (obj == uchain) { + if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK) { + unpunish(); + makeknown(otmp->otyp); + } else + res = 0; + } else + switch(otmp->otyp) { + case WAN_POLYMORPH: + case SPE_POLYMORPH: + if (obj->otyp == WAN_POLYMORPH || + obj->otyp == SPE_POLYMORPH || + obj->otyp == POT_POLYMORPH || + obj_resists(obj, 5, 95)) { + res = 0; + break; + } + /* KMH, conduct */ + u.uconduct.polypiles++; + /* any saved lock context will be dangerously obsolete */ + if (Is_box(obj)) (void) boxlock(obj, otmp); + + if (obj_shudders(obj)) { + if (cansee(obj->ox, obj->oy)) + makeknown(otmp->otyp); + do_osshock(obj); + break; + } + obj = poly_obj(obj, STRANGE_OBJECT); + newsym(obj->ox,obj->oy); + break; + case WAN_PROBING: + res = !obj->dknown; + /* target object has now been "seen (up close)" */ + obj->dknown = 1; + if (Is_container(obj) || obj->otyp == STATUE) { + if (!obj->cobj) + pline("%s empty.", Tobjnam(obj, "are")); + else { + struct obj *o; + /* view contents (not recursively) */ + for (o = obj->cobj; o; o = o->nobj) + o->dknown = 1; /* "seen", even if blind */ + (void) display_cinventory(obj); + } + res = 1; + } + if (res) makeknown(WAN_PROBING); + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + if (obj->otyp == BOULDER) + fracture_rock(obj); + else if (obj->otyp == STATUE) + (void) break_statue(obj); + else { + if (!flags.mon_moving) + (void)hero_breaks(obj, obj->ox, obj->oy, FALSE); + else + (void)breaks(obj, obj->ox, obj->oy); + res = 0; + } + /* BUG[?]: shouldn't this depend upon you seeing it happen? */ + makeknown(otmp->otyp); + break; + case WAN_CANCELLATION: + case SPE_CANCELLATION: + cancel_item(obj); +#ifdef TEXTCOLOR + newsym(obj->ox,obj->oy); /* might change color */ +#endif + break; + case SPE_DRAIN_LIFE: + (void) drain_item(obj); + break; + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + rloco(obj); + break; + case WAN_MAKE_INVISIBLE: +#ifdef INVISIBLE_OBJECTS + obj->oinvis = TRUE; + newsym(obj->ox,obj->oy); /* make object disappear */ +#endif + break; + case WAN_UNDEAD_TURNING: + case SPE_TURN_UNDEAD: + if (obj->otyp == EGG) + revive_egg(obj); + else + res = !!revive(obj); + break; + case WAN_OPENING: + case SPE_KNOCK: + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + if(Is_box(obj)) + res = boxlock(obj, otmp); + else + res = 0; + if (res /* && otmp->oclass == WAND_CLASS */) + makeknown(otmp->otyp); + break; + case WAN_SLOW_MONSTER: /* no effect on objects */ + case SPE_SLOW_MONSTER: + case WAN_SPEED_MONSTER: + case WAN_NOTHING: + case SPE_HEALING: + case SPE_EXTRA_HEALING: + res = 0; + break; + case SPE_STONE_TO_FLESH: + refresh_x = obj->ox; refresh_y = obj->oy; + if (objects[obj->otyp].oc_material != MINERAL && + objects[obj->otyp].oc_material != GEMSTONE) { + res = 0; + break; + } + /* add more if stone objects are added.. */ + switch (objects[obj->otyp].oc_class) { + case ROCK_CLASS: /* boulders and statues */ + if (obj->otyp == BOULDER) { + obj = poly_obj(obj, HUGE_CHUNK_OF_MEAT); + goto smell; + } else if (obj->otyp == STATUE) { + xchar oox, ooy; + + (void) get_obj_location(obj, &oox, &ooy, 0); + refresh_x = oox; refresh_y = ooy; + if (vegetarian(&mons[obj->corpsenm])) { + /* Don't animate monsters that aren't flesh */ + obj = poly_obj(obj, MEATBALL); + goto smell; + } + if (!animate_statue(obj, oox, ooy, + ANIMATE_SPELL, (int *)0)) { + struct obj *item; +makecorpse: if (mons[obj->corpsenm].geno & + (G_NOCORPSE|G_UNIQ)) { + res = 0; + break; + } + /* Unlikely to get here since genociding + * monsters also sets the G_NOCORPSE flag. + * Drop the contents, poly_obj looses them. + */ + while ((item = obj->cobj) != 0) { + obj_extract_self(item); + place_object(item, oox, ooy); + } + obj = poly_obj(obj, CORPSE); + break; + } + } else { /* new rock class object... */ + /* impossible? */ + res = 0; + } + break; + case TOOL_CLASS: /* figurine */ + { + struct monst *mon; + xchar oox, ooy; + + if (obj->otyp != FIGURINE) { + res = 0; + break; + } + if (vegetarian(&mons[obj->corpsenm])) { + /* Don't animate monsters that aren't flesh */ + obj = poly_obj(obj, MEATBALL); + goto smell; + } + (void) get_obj_location(obj, &oox, &ooy, 0); + refresh_x = oox; refresh_y = ooy; + mon = makemon(&mons[obj->corpsenm], + oox, ooy, NO_MM_FLAGS); + if (mon) { + delobj(obj); + if (cansee(mon->mx, mon->my)) + pline_The("figurine animates!"); + break; + } + goto makecorpse; + } + /* maybe add weird things to become? */ + case RING_CLASS: /* some of the rings are stone */ + obj = poly_obj(obj, MEAT_RING); + goto smell; + case WAND_CLASS: /* marble wand */ + obj = poly_obj(obj, MEAT_STICK); + goto smell; + case GEM_CLASS: /* rocks & gems */ + obj = poly_obj(obj, MEATBALL); +smell: + if (herbivorous(youmonst.data) && + (!carnivorous(youmonst.data) || + Role_if(PM_MONK) || !u.uconduct.unvegetarian)) + Norep("You smell the odor of meat."); + else + Norep("You smell a delicious smell."); + break; + case WEAPON_CLASS: /* crysknife */ + /* fall through */ + default: + res = 0; + break; + } + newsym(refresh_x, refresh_y); + break; + default: + impossible("What an interesting effect (%d)", otmp->otyp); + break; + } + return res; +} + +/* returns nonzero if something was hit */ +int +bhitpile(obj,fhito,tx,ty) + struct obj *obj; + int FDECL((*fhito), (OBJ_P,OBJ_P)); + int tx, ty; +{ + int hitanything = 0; + register struct obj *otmp, *next_obj; + + if (obj->otyp == SPE_FORCE_BOLT || obj->otyp == WAN_STRIKING) { + struct trap *t = t_at(tx, ty); + + /* We can't settle for the default calling sequence of + bhito(otmp) -> break_statue(otmp) -> activate_statue_trap(ox,oy) + because that last call might end up operating on our `next_obj' + (below), rather than on the current object, if it happens to + encounter a statue which mustn't become animated. */ + if (t && t->ttyp == STATUE_TRAP && + activate_statue_trap(t, tx, ty, TRUE) && obj->otyp == WAN_STRIKING) + makeknown(obj->otyp); + } + + poly_zapped = -1; + for(otmp = level.objects[tx][ty]; otmp; otmp = next_obj) { + /* Fix for polymorph bug, Tim Wright */ + next_obj = otmp->nexthere; + hitanything += (*fhito)(otmp, obj); + } + if(poly_zapped >= 0) + create_polymon(level.objects[tx][ty], poly_zapped); + + return hitanything; +} +#endif /*OVLB*/ +#ifdef OVL1 + +/* + * zappable - returns 1 if zap is available, 0 otherwise. + * it removes a charge from the wand if zappable. + * added by GAN 11/03/86 + */ +int +zappable(wand) +register struct obj *wand; +{ + if(wand->spe < 0 || (wand->spe == 0 && rn2(121))) + return 0; + if(wand->spe == 0) + You("wrest one last charge from the worn-out wand."); + wand->spe--; + return 1; +} + +/* + * zapnodir - zaps a NODIR wand/spell. + * added by GAN 11/03/86 + */ +void +zapnodir(obj) +register struct obj *obj; +{ + boolean known = FALSE; + + switch(obj->otyp) { + case WAN_LIGHT: + case SPE_LIGHT: + litroom(TRUE,obj); + if (!Blind) known = TRUE; + break; + case WAN_SECRET_DOOR_DETECTION: + case SPE_DETECT_UNSEEN: + if(!findit()) return; + if (!Blind) known = TRUE; + break; + case WAN_CREATE_MONSTER: + known = create_critters(rn2(23) ? 1 : rn1(7,2), + (struct permonst *)0); + break; + case WAN_WISHING: + known = TRUE; + if(Luck + rn2(5) < 0) { + pline("Unfortunately, nothing happens."); + break; + } + makewish(); + break; + case WAN_ENLIGHTENMENT: + known = TRUE; + You_feel("self-knowledgeable..."); + display_nhwindow(WIN_MESSAGE, FALSE); + enlightenment(FALSE); + pline_The("feeling subsides."); + exercise(A_WIS, TRUE); + break; + } + if (known && !objects[obj->otyp].oc_name_known) { + makeknown(obj->otyp); + more_experienced(0,10); + } +} +#endif /*OVL1*/ +#ifdef OVL0 + +STATIC_OVL void +backfire(otmp) +struct obj *otmp; +{ + otmp->in_use = TRUE; /* in case losehp() is fatal */ + pline("%s suddenly explodes!", The(xname(otmp))); + losehp(d(otmp->spe+2,6), "exploding wand", KILLED_BY_AN); + useup(otmp); +} + +static NEARDATA const char zap_syms[] = { WAND_CLASS, 0 }; + +int +dozap() +{ + register struct obj *obj; + int damage; + + if(check_capacity((char *)0)) return(0); + obj = getobj(zap_syms, "zap"); + if(!obj) return(0); + + check_unpaid(obj); + + /* zappable addition done by GAN 11/03/86 */ + if(!zappable(obj)) pline(nothing_happens); + else if(obj->cursed && !rn2(100)) { + backfire(obj); /* the wand blows up in your face! */ + exercise(A_STR, FALSE); + return(1); + } else if(!(objects[obj->otyp].oc_dir == NODIR) && !getdir((char *)0)) { + if (!Blind) + pline("%s glows and fades.", The(xname(obj))); + /* make him pay for knowing !NODIR */ + } else if(!u.dx && !u.dy && !u.dz && !(objects[obj->otyp].oc_dir == NODIR)) { + if ((damage = zapyourself(obj, TRUE)) != 0) { + char buf[BUFSZ]; + Sprintf(buf, "zapped %sself with a wand", uhim()); + losehp(damage, buf, NO_KILLER_PREFIX); + } + } else { + + /* Are we having fun yet? + * weffects -> buzz(obj->otyp) -> zhitm (temple priest) -> + * attack -> hitum -> known_hitum -> ghod_hitsu -> + * buzz(AD_ELEC) -> destroy_item(WAND_CLASS) -> + * useup -> obfree -> dealloc_obj -> free(obj) + */ + current_wand = obj; + weffects(obj); + obj = current_wand; + current_wand = 0; + } + if (obj && obj->spe < 0) { + pline("%s to dust.", Tobjnam(obj, "turn")); + useup(obj); + } + update_inventory(); /* maybe used a charge */ + return(1); +} + +int +zapyourself(obj, ordinary) +struct obj *obj; +boolean ordinary; +{ + int damage = 0; + char buf[BUFSZ]; + + switch(obj->otyp) { + case WAN_STRIKING: + makeknown(WAN_STRIKING); + case SPE_FORCE_BOLT: + if(Antimagic) { + shieldeff(u.ux, u.uy); + pline("Boing!"); + } else { + if (ordinary) { + You("bash yourself!"); + damage = d(2,12); + } else + damage = d(1 + obj->spe,6); + exercise(A_STR, FALSE); + } + break; + + case WAN_LIGHTNING: + makeknown(WAN_LIGHTNING); + if (!Shock_resistance) { + You("shock yourself!"); + damage = d(12,6); + exercise(A_CON, FALSE); + } else { + shieldeff(u.ux, u.uy); + You("zap yourself, but seem unharmed."); + ugolemeffects(AD_ELEC, d(12,6)); + } + destroy_item(WAND_CLASS, AD_ELEC); + destroy_item(RING_CLASS, AD_ELEC); + if (!resists_blnd(&youmonst)) { + You(are_blinded_by_the_flash); + make_blinded((long)rnd(100),FALSE); + if (!Blind) Your(vision_clears); + } + break; + + case SPE_FIREBALL: + You("explode a fireball on top of yourself!"); + explode(u.ux, u.uy, 11, d(6,6), WAND_CLASS, EXPL_FIERY); + break; + case WAN_FIRE: + makeknown(WAN_FIRE); + case FIRE_HORN: + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + You_feel("rather warm."); + ugolemeffects(AD_FIRE, d(12,6)); + } else { + pline("You've set yourself afire!"); + damage = d(12,6); + } + burn_away_slime(); + (void) burnarmor(&youmonst); + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + break; + + case WAN_COLD: + makeknown(WAN_COLD); + case SPE_CONE_OF_COLD: + case FROST_HORN: + if (Cold_resistance) { + shieldeff(u.ux, u.uy); + You_feel("a little chill."); + ugolemeffects(AD_COLD, d(12,6)); + } else { + You("imitate a popsicle!"); + damage = d(12,6); + } + destroy_item(POTION_CLASS, AD_COLD); + break; + + case WAN_MAGIC_MISSILE: + makeknown(WAN_MAGIC_MISSILE); + case SPE_MAGIC_MISSILE: + if(Antimagic) { + shieldeff(u.ux, u.uy); + pline_The("missiles bounce!"); + } else { + damage = d(4,6); + pline("Idiot! You've shot yourself!"); + } + break; + + case WAN_POLYMORPH: + if (!Unchanging) + makeknown(WAN_POLYMORPH); + case SPE_POLYMORPH: + if (!Unchanging) + polyself(FALSE); + break; + + case WAN_CANCELLATION: + case SPE_CANCELLATION: + (void) cancel_monst(&youmonst, obj, TRUE, FALSE, TRUE); + break; + + case SPE_DRAIN_LIFE: + if (!Drain_resistance) { + losexp("life drainage"); + makeknown(obj->otyp); + } + damage = 0; /* No additional damage */ + break; + + case WAN_MAKE_INVISIBLE: { + /* have to test before changing HInvis but must change + * HInvis before doing newsym(). + */ + int msg = !Invis && !Blind && !BInvis; + + if (BInvis && uarmc->otyp == MUMMY_WRAPPING) { + /* A mummy wrapping absorbs it and protects you */ + You_feel("rather itchy under your %s.", xname(uarmc)); + break; + } + if (ordinary || !rn2(10)) { /* permanent */ + HInvis |= FROMOUTSIDE; + } else { /* temporary */ + incr_itimeout(&HInvis, d(obj->spe, 250)); + } + if (msg) { + makeknown(WAN_MAKE_INVISIBLE); + newsym(u.ux, u.uy); + self_invis_message(); + } + break; + } + + case WAN_SPEED_MONSTER: + if (!(HFast & INTRINSIC)) { + if (!Fast) + You("speed up."); + else + Your("quickness feels more natural."); + makeknown(WAN_SPEED_MONSTER); + exercise(A_DEX, TRUE); + } + HFast |= FROMOUTSIDE; + break; + + case WAN_SLEEP: + makeknown(WAN_SLEEP); + case SPE_SLEEP: + if(Sleep_resistance) { + shieldeff(u.ux, u.uy); + You("don't feel sleepy!"); + } else { + pline_The("sleep ray hits you!"); + fall_asleep(-rnd(50), TRUE); + } + break; + + case WAN_SLOW_MONSTER: + case SPE_SLOW_MONSTER: + if(HFast & (TIMEOUT | INTRINSIC)) { + u_slow_down(); + makeknown(obj->otyp); + } + break; + + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + tele(); + break; + + case WAN_DEATH: + case SPE_FINGER_OF_DEATH: + if (nonliving(youmonst.data) || is_demon(youmonst.data)) { + pline((obj->otyp == WAN_DEATH) ? + "The wand shoots an apparently harmless beam at you." + : "You seem no deader than before."); + break; + } + Sprintf(buf, "shot %sself with a death ray", uhim()); + killer = buf; + killer_format = NO_KILLER_PREFIX; + You("irradiate yourself with pure energy!"); + You("die."); + makeknown(obj->otyp); + /* They might survive with an amulet of life saving */ + done(DIED); + break; + case WAN_UNDEAD_TURNING: + makeknown(WAN_UNDEAD_TURNING); + case SPE_TURN_UNDEAD: + (void) unturn_dead(&youmonst); + if (is_undead(youmonst.data)) { + You_feel("frightened and %sstunned.", + Stunned ? "even more " : ""); + make_stunned(HStun + rnd(30), FALSE); + } else + You("shudder in dread."); + break; + case SPE_HEALING: + case SPE_EXTRA_HEALING: + healup(d(6, obj->otyp == SPE_EXTRA_HEALING ? 8 : 4), + 0, FALSE, (obj->otyp == SPE_EXTRA_HEALING)); + You_feel("%sbetter.", + obj->otyp == SPE_EXTRA_HEALING ? "much " : ""); + break; + case WAN_LIGHT: /* (broken wand) */ + /* assert( !ordinary ); */ + damage = d(obj->spe, 25); +#ifdef TOURIST + case EXPENSIVE_CAMERA: +#endif + damage += rnd(25); + if (!resists_blnd(&youmonst)) { + You(are_blinded_by_the_flash); + make_blinded((long)damage, FALSE); + makeknown(obj->otyp); + if (!Blind) Your(vision_clears); + } + damage = 0; /* reset */ + break; + case WAN_OPENING: + if (Punished) makeknown(WAN_OPENING); + case SPE_KNOCK: + if (Punished) Your("chain quivers for a moment."); + break; + case WAN_DIGGING: + case SPE_DIG: + case SPE_DETECT_UNSEEN: + case WAN_NOTHING: + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + break; + case WAN_PROBING: + for (obj = invent; obj; obj = obj->nobj) + obj->dknown = 1; + /* note: `obj' reused; doesn't point at wand anymore */ + makeknown(WAN_PROBING); + ustatusline(); + break; + case SPE_STONE_TO_FLESH: + { + struct obj *otemp, *onext; + boolean didmerge; + + if (u.umonnum == PM_STONE_GOLEM) + (void) polymon(PM_FLESH_GOLEM); + if (Stoned) fix_petrification(); /* saved! */ + /* but at a cost.. */ + for (otemp = invent; otemp; otemp = onext) { + onext = otemp->nobj; + (void) bhito(otemp, obj); + } + /* + * It is possible that we can now merge some inventory. + * Do a higly paranoid merge. Restart from the beginning + * until no merges. + */ + do { + didmerge = FALSE; + for (otemp = invent; !didmerge && otemp; otemp = otemp->nobj) + for (onext = otemp->nobj; onext; onext = onext->nobj) + if (merged(&otemp, &onext)) { + didmerge = TRUE; + break; + } + } while (didmerge); + } + break; + default: impossible("object %d used?",obj->otyp); + break; + } + return(damage); +} + +#ifdef STEED +/* you've zapped a wand downwards while riding + * Return TRUE if the steed was hit by the wand. + * Return FALSE if the steed was not hit by the wand. + */ +STATIC_OVL boolean +zap_steed(obj) +struct obj *obj; /* wand or spell */ +{ + int steedhit = FALSE; + + switch (obj->otyp) { + + /* + * Wands that are allowed to hit the steed + * Carefully test the results of any that are + * moved here from the bottom section. + */ + case WAN_PROBING: + probe_monster(u.usteed); + makeknown(WAN_PROBING); + steedhit = TRUE; + break; + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + /* you go together */ + tele(); + if(Teleport_control || !couldsee(u.ux0, u.uy0) || + (distu(u.ux0, u.uy0) >= 16)) + makeknown(obj->otyp); + steedhit = TRUE; + break; + + /* Default processing via bhitm() for these */ + case SPE_CURE_SICKNESS: + case WAN_MAKE_INVISIBLE: + case WAN_CANCELLATION: + case SPE_CANCELLATION: + case WAN_POLYMORPH: + case SPE_POLYMORPH: + case WAN_STRIKING: + case SPE_FORCE_BOLT: + case WAN_SLOW_MONSTER: + case SPE_SLOW_MONSTER: + case WAN_SPEED_MONSTER: + case SPE_HEALING: + case SPE_EXTRA_HEALING: + case SPE_DRAIN_LIFE: + case WAN_OPENING: + case SPE_KNOCK: + (void) bhitm(u.usteed, obj); + steedhit = TRUE; + break; + + default: + steedhit = FALSE; + break; + } + return steedhit; +} +#endif + +#endif /*OVL0*/ +#ifdef OVL3 + +/* + * cancel a monster (possibly the hero). inventory is cancelled only + * if the monster is zapping itself directly, since otherwise the + * effect is too strong. currently non-hero monsters do not zap + * themselves with cancellation. + */ +boolean +cancel_monst(mdef, obj, youattack, allow_cancel_kill, self_cancel) +register struct monst *mdef; +register struct obj *obj; +boolean youattack, allow_cancel_kill, self_cancel; +{ + boolean youdefend = (mdef == &youmonst); + static const char writing_vanishes[] = + "Some writing vanishes from %s head!"; + static const char your[] = "your"; /* should be extern */ + + if (youdefend ? (!youattack && Antimagic) + : resist(mdef, obj->oclass, 0, NOTELL)) + return FALSE; /* resisted cancellation */ + + if (self_cancel) { /* 1st cancel inventory */ + struct obj *otmp; + + for (otmp = (youdefend ? invent : mdef->minvent); + otmp; otmp = otmp->nobj) + cancel_item(otmp); + if (youdefend) { + flags.botl = 1; /* potential AC change */ + find_ac(); + } + } + + /* now handle special cases */ + if (youdefend) { + if (Upolyd) { + if ((u.umonnum == PM_CLAY_GOLEM) && !Blind) + pline(writing_vanishes, your); + + if (Unchanging) + Your("amulet grows hot for a moment, then cools."); + else + rehumanize(); + } + } else { + mdef->mcan = TRUE; + + if (is_were(mdef->data) && mdef->data->mlet != S_HUMAN) + were_change(mdef); + + if (mdef->data == &mons[PM_CLAY_GOLEM]) { + if (canseemon(mdef)) + pline(writing_vanishes, s_suffix(mon_nam(mdef))); + + if (allow_cancel_kill) { + if (youattack) + killed(mdef); + else + monkilled(mdef, "", AD_SPEL); + } + } + } + return TRUE; +} + +/* you've zapped an immediate type wand up or down */ +STATIC_OVL boolean +zap_updown(obj) +struct obj *obj; /* wand or spell */ +{ + boolean striking = FALSE, disclose = FALSE; + int x, y, xx, yy, ptmp; + struct obj *otmp; + struct engr *e; + struct trap *ttmp; + char buf[BUFSZ]; + + /* some wands have special effects other than normal bhitpile */ + /* drawbridge might change */ + x = xx = u.ux; /* is zap location */ + y = yy = u.uy; /* is drawbridge (portcullis) position */ + ttmp = t_at(x, y); /* trap if there is one */ + + switch (obj->otyp) { + case WAN_PROBING: + ptmp = 0; + if (u.dz < 0) { + You("probe towards the %s.", ceiling(x,y)); + } else { + ptmp += bhitpile(obj, bhito, x, y); + You("probe beneath the %s.", surface(x,y)); + ptmp += display_binventory(x, y, TRUE); + } + if (!ptmp) Your("probe reveals nothing."); + return TRUE; /* we've done our own bhitpile */ + case WAN_OPENING: + case SPE_KNOCK: + /* up or down, but at closed portcullis only */ + if (is_db_wall(x,y) && find_drawbridge(&xx, &yy)) { + open_drawbridge(xx, yy); + disclose = TRUE; + } else if (u.dz > 0 && (x == xdnstair && y == ydnstair) && + /* can't use the stairs down to quest level 2 until + leader "unlocks" them; give feedback if you try */ + on_level(&u.uz, &qstart_level) && !ok_to_quest()) { + pline_The("stairs seem to ripple momentarily."); + disclose = TRUE; + } + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + striking = TRUE; + /*FALLTHRU*/ + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + /* down at open bridge or up or down at open portcullis */ + if ((levl[x][y].typ == DRAWBRIDGE_DOWN) ? (u.dz > 0) : + (is_drawbridge_wall(x,y) && !is_db_wall(x,y)) && + find_drawbridge(&xx, &yy)) { + if (!striking) + close_drawbridge(xx, yy); + else + destroy_drawbridge(xx, yy); + disclose = TRUE; + } else if (striking && u.dz < 0 && rn2(3) && + !Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && + !Underwater && !Is_qstart(&u.uz)) { + /* similar to zap_dig() */ + pline("A rock is dislodged from the %s and falls on your %s.", + ceiling(x, y), body_part(HEAD)); + losehp(rnd((uarmh && is_metallic(uarmh)) ? 2 : 6), + "falling rock", KILLED_BY_AN); + if ((otmp = mksobj_at(ROCK, x, y, FALSE, FALSE)) != 0) { + (void)xname(otmp); /* set dknown, maybe bknown */ + stackobj(otmp); + } + newsym(x, y); + } else if (!striking && ttmp && ttmp->ttyp == TRAPDOOR && u.dz > 0) { + if (!Blind) { + if (ttmp->tseen) { + pline("A trap door beneath you closes up then vanishes."); + disclose = TRUE; + } else { + You("see a swirl of %s beneath you.", + is_ice(x,y) ? "frost" : "dust"); + } + } else { + You_hear("a twang followed by a thud."); + } + deltrap(ttmp); + ttmp = (struct trap *)0; + newsym(x, y); + } + break; + case SPE_STONE_TO_FLESH: + if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz) || + Underwater || (Is_qstart(&u.uz) && u.dz < 0)) { + pline(nothing_happens); + } else if (u.dz < 0) { /* we should do more... */ + pline("Blood drips on your %s.", body_part(FACE)); + } else if (u.dz > 0 && !OBJ_AT(u.ux, u.uy)) { + /* + Print this message only if there wasn't an engraving + affected here. If water or ice, act like waterlevel case. + */ + e = engr_at(u.ux, u.uy); + if (!(e && e->engr_type == ENGRAVE)) { + if (is_pool(u.ux, u.uy) || is_ice(u.ux, u.uy)) + pline(nothing_happens); + else + pline("Blood %ss %s your %s.", + is_lava(u.ux, u.uy) ? "boil" : "pool", + Levitation ? "beneath" : "at", + makeplural(body_part(FOOT))); + } + } + break; + default: + break; + } + + if (u.dz > 0) { + /* zapping downward */ + (void) bhitpile(obj, bhito, x, y); + + /* subset of engraving effects; none sets `disclose' */ + if ((e = engr_at(x, y)) != 0 && e->engr_type != HEADSTONE) { + switch (obj->otyp) { + case WAN_POLYMORPH: + case SPE_POLYMORPH: + del_engr(e); + make_engr_at(x, y, random_engraving(buf), moves, (xchar)0); + break; + case WAN_CANCELLATION: + case SPE_CANCELLATION: + case WAN_MAKE_INVISIBLE: + del_engr(e); + break; + case WAN_TELEPORTATION: + case SPE_TELEPORT_AWAY: + rloc_engr(e); + break; + case SPE_STONE_TO_FLESH: + if (e->engr_type == ENGRAVE) { + /* only affects things in stone */ + pline_The(Hallucination ? + "floor runs like butter!" : + "edges on the floor get smoother."); + wipe_engr_at(x, y, d(2,4)); + } + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + wipe_engr_at(x, y, d(2,4)); + break; + default: + break; + } + } + } + + return disclose; +} + +#endif /*OVL3*/ +#ifdef OVLB + +/* called for various wand and spell effects - M. Stephenson */ +void +weffects(obj) +register struct obj *obj; +{ + int otyp = obj->otyp; + boolean disclose = FALSE, was_unkn = !objects[otyp].oc_name_known; + + exercise(A_WIS, TRUE); +#ifdef STEED + if (u.usteed && (objects[otyp].oc_dir != NODIR) && + !u.dx && !u.dy && (u.dz > 0) && zap_steed(obj)) { + disclose = TRUE; + } else +#endif + if (objects[otyp].oc_dir == IMMEDIATE) { + obj_zapped = FALSE; + + if (u.uswallow) { + (void) bhitm(u.ustuck, obj); + /* [how about `bhitpile(u.ustuck->minvent)' effect?] */ + } else if (u.dz) { + disclose = zap_updown(obj); + } else { + (void) bhit(u.dx,u.dy, rn1(8,6),ZAPPED_WAND, bhitm,bhito, obj); + } + /* give a clue if obj_zapped */ + if (obj_zapped) + You_feel("shuddering vibrations."); + + } else if (objects[otyp].oc_dir == NODIR) { + zapnodir(obj); + + } else { + /* neither immediate nor directionless */ + + if (otyp == WAN_DIGGING || otyp == SPE_DIG) + zap_dig(); + else if (otyp >= SPE_MAGIC_MISSILE && otyp <= SPE_FINGER_OF_DEATH) + buzz(otyp - SPE_MAGIC_MISSILE + 10, + u.ulevel / 2 + 1, + u.ux, u.uy, u.dx, u.dy); + else if (otyp >= WAN_MAGIC_MISSILE && otyp <= WAN_LIGHTNING) + buzz(otyp - WAN_MAGIC_MISSILE, + (otyp == WAN_MAGIC_MISSILE) ? 2 : 6, + u.ux, u.uy, u.dx, u.dy); + else + impossible("weffects: unexpected spell or wand"); + disclose = TRUE; + } + if (disclose && was_unkn) { + makeknown(otyp); + more_experienced(0,10); + } + return; +} +#endif /*OVLB*/ +#ifdef OVL0 + +/* + * Generate the to damage bonus for a spell. Based on the hero's intelligence + */ +int +spell_damage_bonus() +{ + int tmp, intell = ACURR(A_INT); + + /* Punish low intellegence before low level else low intellegence + gets punished only when high level */ + if (intell < 10) + tmp = -3; + else if (u.ulevel < 5) + tmp = 0; + else if (intell < 14) + tmp = 0; + else if (intell <= 18) + tmp = 1; + else /* helm of brilliance */ + tmp = 2; + + return tmp; +} + +/* + * Generate the to hit bonus for a spell. Based on the hero's skill in + * spell class and dexterity. + */ +STATIC_OVL int +spell_hit_bonus(skill) +int skill; +{ + int hit_bon = 0; + int dex = ACURR(A_DEX); + + switch (P_SKILL(spell_skilltype(skill))) { + case P_ISRESTRICTED: + case P_UNSKILLED: hit_bon = -4; break; + case P_BASIC: hit_bon = 0; break; + case P_SKILLED: hit_bon = 2; break; + case P_EXPERT: hit_bon = 3; break; + } + + if (dex < 4) + hit_bon -= 3; + else if (dex < 6) + hit_bon -= 2; + else if (dex < 8) + hit_bon -= 1; + else if (dex < 14) + hit_bon -= 0; /* Will change when print stuff below removed */ + else + hit_bon += dex - 14; /* Even increment for dextrous heroes (see weapon.c abon) */ + + return hit_bon; +} + +const char * +exclam(force) +register int force; +{ + /* force == 0 occurs e.g. with sleep ray */ + /* note that large force is usual with wands so that !! would + require information about hand/weapon/wand */ + return (const char *)((force < 0) ? "?" : (force <= 4) ? "." : "!"); +} + +void +hit(str,mtmp,force) +register const char *str; +register struct monst *mtmp; +register const char *force; /* usually either "." or "!" */ +{ + if((!cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp) && + !(u.uswallow && mtmp == u.ustuck)) + || !flags.verbose) + pline("%s %s it.", The(str), vtense(str, "hit")); + else pline("%s %s %s%s", The(str), vtense(str, "hit"), + mon_nam(mtmp), force); +} + +void +miss(str,mtmp) +register const char *str; +register struct monst *mtmp; +{ + pline("%s %s %s.", The(str), vtense(str, "miss"), + ((cansee(bhitpos.x,bhitpos.y) || canspotmon(mtmp)) + && flags.verbose) ? + mon_nam(mtmp) : "it"); +} +#endif /*OVL0*/ +#ifdef OVL1 + +/* + * Called for the following distance effects: + * when a weapon is thrown (weapon == THROWN_WEAPON) + * when an object is kicked (KICKED_WEAPON) + * when an IMMEDIATE wand is zapped (ZAPPED_WAND) + * when a light beam is flashed (FLASHED_LIGHT) + * when a mirror is applied (INVIS_BEAM) + * A thrown/kicked object falls down at the end of its range or when a monster + * is hit. The variable 'bhitpos' is set to the final position of the weapon + * thrown/zapped. The ray of a wand may affect (by calling a provided + * function) several objects and monsters on its path. The return value + * is the monster hit (weapon != ZAPPED_WAND), or a null monster pointer. + * + * Check !u.uswallow before calling bhit(). + * This function reveals the absence of a remembered invisible monster in + * necessary cases (throwing or kicking weapons). The presence of a real + * one is revealed for a weapon, but if not a weapon is left up to fhitm(). + */ +struct monst * +bhit(ddx,ddy,range,weapon,fhitm,fhito,obj) +register int ddx,ddy,range; /* direction and range */ +int weapon; /* see values in hack.h */ +int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */ + FDECL((*fhito), (OBJ_P, OBJ_P)); +struct obj *obj; /* object tossed/used */ +{ + struct monst *mtmp; + uchar typ; + boolean shopdoor = FALSE, point_blank = TRUE; + + if (weapon == KICKED_WEAPON) { + /* object starts one square in front of player */ + bhitpos.x = u.ux + ddx; + bhitpos.y = u.uy + ddy; + range--; + } else { + bhitpos.x = u.ux; + bhitpos.y = u.uy; + } + + if (weapon == FLASHED_LIGHT) { + tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam)); + } else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) + tmp_at(DISP_FLASH, obj_to_glyph(obj)); + + while(range-- > 0) { + int x,y; + + bhitpos.x += ddx; + bhitpos.y += ddy; + x = bhitpos.x; y = bhitpos.y; + + if(!isok(x, y)) { + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + + if(is_pick(obj) && inside_shop(x, y) && + (mtmp = shkcatch(obj, x, y))) { + tmp_at(DISP_END, 0); + return(mtmp); + } + + typ = levl[bhitpos.x][bhitpos.y].typ; + + /* iron bars will block anything big enough */ + if ((weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) && + typ == IRONBARS && + hits_bars(&obj, x - ddx, y - ddy, + point_blank ? 0 : !rn2(5), 1)) { + /* caveat: obj might now be null... */ + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + + if (weapon == ZAPPED_WAND && find_drawbridge(&x,&y)) + switch (obj->otyp) { + case WAN_OPENING: + case SPE_KNOCK: + if (is_db_wall(bhitpos.x, bhitpos.y)) { + if (cansee(x,y) || cansee(bhitpos.x,bhitpos.y)) + makeknown(obj->otyp); + open_drawbridge(x,y); + } + break; + case WAN_LOCKING: + case SPE_WIZARD_LOCK: + if ((cansee(x,y) || cansee(bhitpos.x, bhitpos.y)) + && levl[x][y].typ == DRAWBRIDGE_DOWN) + makeknown(obj->otyp); + close_drawbridge(x,y); + break; + case WAN_STRIKING: + case SPE_FORCE_BOLT: + if (typ != DRAWBRIDGE_UP) + destroy_drawbridge(x,y); + makeknown(obj->otyp); + break; + } + + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { + notonhead = (bhitpos.x != mtmp->mx || + bhitpos.y != mtmp->my); + if (weapon != FLASHED_LIGHT) { + if(weapon != ZAPPED_WAND) { + if(weapon != INVIS_BEAM) tmp_at(DISP_END, 0); + if (cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp)) { + if (weapon != INVIS_BEAM) { + map_invisible(bhitpos.x, bhitpos.y); + return(mtmp); + } + } else + return(mtmp); + } + if (weapon != INVIS_BEAM) { + (*fhitm)(mtmp, obj); + range -= 3; + } + } else { + /* FLASHED_LIGHT hitting invisible monster + should pass through instead of stop so + we call flash_hits_mon() directly rather + than returning mtmp back to caller. That + allows the flash to keep on going. Note + that we use mtmp->minvis not canspotmon() + because it makes no difference whether + the hero can see the monster or not.*/ + if (mtmp->minvis) { + obj->ox = u.ux, obj->oy = u.uy; + (void) flash_hits_mon(mtmp, obj); + } else { + tmp_at(DISP_END, 0); + return(mtmp); /* caller will call flash_hits_mon */ + } + } + } else { + if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING && + glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) { + unmap_object(bhitpos.x, bhitpos.y); + newsym(x, y); + } + } + if(fhito) { + if(bhitpile(obj,fhito,bhitpos.x,bhitpos.y)) + range--; + } else { + if(weapon == KICKED_WEAPON && + ((obj->oclass == COIN_CLASS && + OBJ_AT(bhitpos.x, bhitpos.y)) || + ship_object(obj, bhitpos.x, bhitpos.y, + costly_spot(bhitpos.x, bhitpos.y)))) { + tmp_at(DISP_END, 0); + return (struct monst *)0; + } + } + if(weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) { + switch (obj->otyp) { + case WAN_OPENING: + case WAN_LOCKING: + case WAN_STRIKING: + case SPE_KNOCK: + case SPE_WIZARD_LOCK: + case SPE_FORCE_BOLT: + if (doorlock(obj, bhitpos.x, bhitpos.y)) { + if (cansee(bhitpos.x, bhitpos.y) || + (obj->otyp == WAN_STRIKING)) + makeknown(obj->otyp); + if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN + && *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) { + shopdoor = TRUE; + add_damage(bhitpos.x, bhitpos.y, 400L); + } + } + break; + } + } + if(!ZAP_POS(typ) || closed_door(bhitpos.x, bhitpos.y)) { + bhitpos.x -= ddx; + bhitpos.y -= ddy; + break; + } + if(weapon != ZAPPED_WAND && weapon != INVIS_BEAM) { + /* 'I' present but no monster: erase */ + /* do this before the tmp_at() */ + if (glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph) + && cansee(x, y)) { + unmap_object(bhitpos.x, bhitpos.y); + newsym(x, y); + } + tmp_at(bhitpos.x, bhitpos.y); + delay_output(); + /* kicked objects fall in pools */ + if((weapon == KICKED_WEAPON) && + (is_pool(bhitpos.x, bhitpos.y) || + is_lava(bhitpos.x, bhitpos.y))) + break; +#ifdef SINKS + if(IS_SINK(typ) && weapon != FLASHED_LIGHT) + break; /* physical objects fall onto sink */ +#endif + } + /* limit range of ball so hero won't make an invalid move */ + if (weapon == THROWN_WEAPON && range > 0 && + obj->otyp == HEAVY_IRON_BALL) { + struct obj *bobj; + struct trap *t; + if ((bobj = sobj_at(BOULDER, x, y)) != 0) { + if (cansee(x,y)) + pline("%s hits %s.", + The(distant_name(obj, xname)), an(xname(bobj))); + range = 0; + } else if (obj == uball) { + if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) { + /* nb: it didn't hit anything directly */ + if (cansee(x,y)) + pline("%s jerks to an abrupt halt.", + The(distant_name(obj, xname))); /* lame */ + range = 0; + } else if (In_sokoban(&u.uz) && (t = t_at(x, y)) != 0 && + (t->ttyp == PIT || t->ttyp == SPIKED_PIT || + t->ttyp == HOLE || t->ttyp == TRAPDOOR)) { + /* hero falls into the trap, so ball stops */ + range = 0; + } + } + } + + /* thrown/kicked missile has moved away from its starting spot */ + point_blank = FALSE; /* affects passing through iron bars */ + } + + if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) tmp_at(DISP_END, 0); + + if(shopdoor) + pay_for_damage("destroy", FALSE); + + return (struct monst *)0; +} + +struct monst * +boomhit(dx, dy) +int dx, dy; +{ + register int i, ct; + int boom = S_boomleft; /* showsym[] index */ + struct monst *mtmp; + + bhitpos.x = u.ux; + bhitpos.y = u.uy; + + for (i = 0; i < 8; i++) if (xdir[i] == dx && ydir[i] == dy) break; + tmp_at(DISP_FLASH, cmap_to_glyph(boom)); + for (ct = 0; ct < 10; ct++) { + if(i == 8) i = 0; + boom = (boom == S_boomleft) ? S_boomright : S_boomleft; + tmp_at(DISP_CHANGE, cmap_to_glyph(boom));/* change glyph */ + dx = xdir[i]; + dy = ydir[i]; + bhitpos.x += dx; + bhitpos.y += dy; + if(MON_AT(bhitpos.x, bhitpos.y)) { + mtmp = m_at(bhitpos.x,bhitpos.y); + m_respond(mtmp); + tmp_at(DISP_END, 0); + return(mtmp); + } + if(!ZAP_POS(levl[bhitpos.x][bhitpos.y].typ) || + closed_door(bhitpos.x, bhitpos.y)) { + bhitpos.x -= dx; + bhitpos.y -= dy; + break; + } + if(bhitpos.x == u.ux && bhitpos.y == u.uy) { /* ct == 9 */ + if(Fumbling || rn2(20) >= ACURR(A_DEX)) { + /* we hit ourselves */ + (void) thitu(10, rnd(10), (struct obj *)0, + "boomerang"); + break; + } else { /* we catch it */ + tmp_at(DISP_END, 0); + You("skillfully catch the boomerang."); + return(&youmonst); + } + } + tmp_at(bhitpos.x, bhitpos.y); + delay_output(); + if(ct % 5 != 0) i++; +#ifdef SINKS + if(IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) + break; /* boomerang falls on sink */ +#endif + } + tmp_at(DISP_END, 0); /* do not leave last symbol */ + return (struct monst *)0; +} + +STATIC_OVL int +zhitm(mon, type, nd, ootmp) /* returns damage to mon */ +register struct monst *mon; +register int type, nd; +struct obj **ootmp; /* to return worn armor for caller to disintegrate */ +{ + register int tmp = 0; + register int abstype = abs(type) % 10; + boolean sho_shieldeff = FALSE; + boolean spellcaster = is_hero_spell(type); /* maybe get a bonus! */ + + *ootmp = (struct obj *)0; + switch(abstype) { + case ZT_MAGIC_MISSILE: + if (resists_magm(mon)) { + sho_shieldeff = TRUE; + break; + } + tmp = d(nd,6); + if (spellcaster) + tmp += spell_damage_bonus(); +#ifdef WIZ_PATCH_DEBUG + if (spellcaster) + pline("Damage = %d + %d", tmp-spell_damage_bonus(), + spell_damage_bonus()); +#endif + break; + case ZT_FIRE: + if (resists_fire(mon)) { + sho_shieldeff = TRUE; + break; + } + tmp = d(nd,6); + if (resists_cold(mon)) tmp += 7; + if (spellcaster) + tmp += spell_damage_bonus(); +#ifdef WIZ_PATCH_DEBUG + if (spellcaster) + pline("Damage = %d + %d",tmp-spell_damage_bonus(), + spell_damage_bonus()); +#endif + if (burnarmor(mon)) { + if (!rn2(3)) (void)destroy_mitem(mon, POTION_CLASS, AD_FIRE); + if (!rn2(3)) (void)destroy_mitem(mon, SCROLL_CLASS, AD_FIRE); + if (!rn2(5)) (void)destroy_mitem(mon, SPBOOK_CLASS, AD_FIRE); + } + break; + case ZT_COLD: + if (resists_cold(mon)) { + sho_shieldeff = TRUE; + break; + } + tmp = d(nd,6); + if (resists_fire(mon)) tmp += d(nd, 3); + if (spellcaster) + tmp += spell_damage_bonus(); +#ifdef WIZ_PATCH_DEBUG + if (spellcaster) + pline("Damage = %d + %d", tmp-spell_damage_bonus(), + spell_damage_bonus()); +#endif + if (!rn2(3)) (void)destroy_mitem(mon, POTION_CLASS, AD_COLD); + break; + case ZT_SLEEP: + tmp = 0; + (void)sleep_monst(mon, d(nd, 25), + type == ZT_WAND(ZT_SLEEP) ? WAND_CLASS : '\0'); + break; + case ZT_DEATH: /* death/disintegration */ + if(abs(type) != ZT_BREATH(ZT_DEATH)) { /* death */ + if(mon->data == &mons[PM_DEATH]) { + mon->mhpmax += mon->mhpmax/2; + if (mon->mhpmax >= MAGIC_COOKIE) + mon->mhpmax = MAGIC_COOKIE - 1; + mon->mhp = mon->mhpmax; + tmp = 0; + break; + } + if (nonliving(mon->data) || is_demon(mon->data) || + resists_magm(mon)) { /* similar to player */ + sho_shieldeff = TRUE; + break; + } + type = -1; /* so they don't get saving throws */ + } else { + struct obj *otmp2; + + if (resists_disint(mon)) { + sho_shieldeff = TRUE; + } else if (mon->misc_worn_check & W_ARMS) { + /* destroy shield; victim survives */ + *ootmp = which_armor(mon, W_ARMS); + } else if (mon->misc_worn_check & W_ARM) { + /* destroy body armor, also cloak if present */ + *ootmp = which_armor(mon, W_ARM); + if ((otmp2 = which_armor(mon, W_ARMC)) != 0) + m_useup(mon, otmp2); + } else { + /* no body armor, victim dies; destroy cloak + and shirt now in case target gets life-saved */ + tmp = MAGIC_COOKIE; + if ((otmp2 = which_armor(mon, W_ARMC)) != 0) + m_useup(mon, otmp2); +#ifdef TOURIST + if ((otmp2 = which_armor(mon, W_ARMU)) != 0) + m_useup(mon, otmp2); +#endif + } + type = -1; /* no saving throw wanted */ + break; /* not ordinary damage */ + } + tmp = mon->mhp+1; + break; + case ZT_LIGHTNING: + if (resists_elec(mon)) { + sho_shieldeff = TRUE; + tmp = 0; + /* can still blind the monster */ + } else + tmp = d(nd,6); + if (spellcaster) + tmp += spell_damage_bonus(); +#ifdef WIZ_PATCH_DEBUG + if (spellcaster) + pline("Damage = %d + %d", tmp-spell_damage_bonus(), + spell_damage_bonus()); +#endif + if (!resists_blnd(mon) && + !(type > 0 && u.uswallow && mon == u.ustuck)) { + register unsigned rnd_tmp = rnd(50); + mon->mcansee = 0; + if((mon->mblinded + rnd_tmp) > 127) + mon->mblinded = 127; + else mon->mblinded += rnd_tmp; + } + if (!rn2(3)) (void)destroy_mitem(mon, WAND_CLASS, AD_ELEC); + /* not actually possible yet */ + if (!rn2(3)) (void)destroy_mitem(mon, RING_CLASS, AD_ELEC); + break; + case ZT_POISON_GAS: + if (resists_poison(mon)) { + sho_shieldeff = TRUE; + break; + } + tmp = d(nd,6); + break; + case ZT_ACID: + if (resists_acid(mon)) { + sho_shieldeff = TRUE; + break; + } + tmp = d(nd,6); + if (!rn2(6)) erode_obj(MON_WEP(mon), TRUE, TRUE); + if (!rn2(6)) erode_armor(mon, TRUE); + break; + } + if (sho_shieldeff) shieldeff(mon->mx, mon->my); + if (is_hero_spell(type) && (Role_if(PM_KNIGHT) && u.uhave.questart)) + tmp *= 2; + if (tmp > 0 && type >= 0 && + resist(mon, type < ZT_SPELL(0) ? WAND_CLASS : '\0', 0, NOTELL)) + tmp /= 2; + if (tmp < 0) tmp = 0; /* don't allow negative damage */ +#ifdef WIZ_PATCH_DEBUG + pline("zapped monster hp = %d (= %d - %d)", mon->mhp-tmp,mon->mhp,tmp); +#endif + mon->mhp -= tmp; + return(tmp); +} + +STATIC_OVL void +zhitu(type, nd, fltxt, sx, sy) +int type, nd; +const char *fltxt; +xchar sx, sy; +{ + int dam = 0; + + switch (abs(type) % 10) { + case ZT_MAGIC_MISSILE: + if (Antimagic) { + shieldeff(sx, sy); + pline_The("missiles bounce off!"); + } else { + dam = d(nd,6); + exercise(A_STR, FALSE); + } + break; + case ZT_FIRE: + if (Fire_resistance) { + shieldeff(sx, sy); + You("don't feel hot!"); + ugolemeffects(AD_FIRE, d(nd, 6)); + } else { + dam = d(nd, 6); + } + burn_away_slime(); + if (burnarmor(&youmonst)) { /* "body hit" */ + if (!rn2(3)) destroy_item(POTION_CLASS, AD_FIRE); + if (!rn2(3)) destroy_item(SCROLL_CLASS, AD_FIRE); + if (!rn2(5)) destroy_item(SPBOOK_CLASS, AD_FIRE); + } + break; + case ZT_COLD: + if (Cold_resistance) { + shieldeff(sx, sy); + You("don't feel cold."); + ugolemeffects(AD_COLD, d(nd, 6)); + } else { + dam = d(nd, 6); + } + if (!rn2(3)) destroy_item(POTION_CLASS, AD_COLD); + break; + case ZT_SLEEP: + if (Sleep_resistance) { + shieldeff(u.ux, u.uy); + You("don't feel sleepy."); + } else { + fall_asleep(-d(nd,25), TRUE); /* sleep ray */ + } + break; + case ZT_DEATH: + if (abs(type) == ZT_BREATH(ZT_DEATH)) { + if (Disint_resistance) { + You("are not disintegrated."); + break; + } else if (uarms) { + /* destroy shield; other possessions are safe */ + (void) destroy_arm(uarms); + break; + } else if (uarm) { + /* destroy suit; if present, cloak goes too */ + if (uarmc) (void) destroy_arm(uarmc); + (void) destroy_arm(uarm); + break; + } + /* no shield or suit, you're dead; wipe out cloak + and/or shirt in case of life-saving or bones */ + if (uarmc) (void) destroy_arm(uarmc); +#ifdef TOURIST + if (uarmu) (void) destroy_arm(uarmu); +#endif + } else if (nonliving(youmonst.data) || is_demon(youmonst.data)) { + shieldeff(sx, sy); + You("seem unaffected."); + break; + } else if (Antimagic) { + shieldeff(sx, sy); + You("aren't affected."); + break; + } + killer_format = KILLED_BY_AN; + killer = fltxt; + /* when killed by disintegration breath, don't leave corpse */ + u.ugrave_arise = (type == -ZT_BREATH(ZT_DEATH)) ? -3 : NON_PM; + done(DIED); + return; /* lifesaved */ + case ZT_LIGHTNING: + if (Shock_resistance) { + shieldeff(sx, sy); + You("aren't affected."); + ugolemeffects(AD_ELEC, d(nd, 6)); + } else { + dam = d(nd, 6); + exercise(A_CON, FALSE); + } + if (!rn2(3)) destroy_item(WAND_CLASS, AD_ELEC); + if (!rn2(3)) destroy_item(RING_CLASS, AD_ELEC); + break; + case ZT_POISON_GAS: + poisoned("blast", A_DEX, "poisoned blast", 15); + break; + case ZT_ACID: + if (Acid_resistance) { + dam = 0; + } else { + pline_The("acid burns!"); + dam = d(nd,6); + exercise(A_STR, FALSE); + } + /* using two weapons at once makes both of them more vulnerable */ + if (!rn2(u.twoweap ? 3 : 6)) erode_obj(uwep, TRUE, TRUE); + if (u.twoweap && !rn2(3)) erode_obj(uswapwep, TRUE, TRUE); + if (!rn2(6)) erode_armor(&youmonst, TRUE); + break; + } + + if (Half_spell_damage && dam && + type < 0 && (type > -20 || type < -29)) /* !Breath */ + dam = (dam + 1) / 2; + losehp(dam, fltxt, KILLED_BY_AN); + return; +} + +#endif /*OVL1*/ +#ifdef OVLB + +/* + * burn scrolls and spellbooks on floor at position x,y + * return the number of scrolls and spellbooks burned + */ +int +burn_floor_paper(x, y, give_feedback, u_caused) +int x, y; +boolean give_feedback; /* caller needs to decide about visibility checks */ +boolean u_caused; +{ + struct obj *obj, *obj2; + long i, scrquan, delquan; + char buf1[BUFSZ], buf2[BUFSZ]; + int cnt = 0; + + for (obj = level.objects[x][y]; obj; obj = obj2) { + obj2 = obj->nexthere; + if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) { + if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL || + obj_resists(obj, 2, 100)) + continue; + scrquan = obj->quan; /* number present */ + delquan = 0; /* number to destroy */ + for (i = scrquan; i > 0; i--) + if (!rn2(3)) delquan++; + if (delquan) { + /* save name before potential delobj() */ + if (give_feedback) { + obj->quan = 1; + Strcpy(buf1, (x == u.ux && y == u.uy) ? + xname(obj) : distant_name(obj, xname)); + obj->quan = 2; + Strcpy(buf2, (x == u.ux && y == u.uy) ? + xname(obj) : distant_name(obj, xname)); + obj->quan = scrquan; + } + /* useupf(), which charges, only if hero caused damage */ + if (u_caused) useupf(obj, delquan); + else if (delquan < scrquan) obj->quan -= delquan; + else delobj(obj); + cnt += delquan; + if (give_feedback) { + if (delquan > 1) + pline("%ld %s burn.", delquan, buf2); + else + pline("%s burns.", An(buf1)); + } + } + } + } + return cnt; +} + +/* will zap/spell/breath attack score a hit against armor class `ac'? */ +STATIC_OVL int +zap_hit(ac, type) +int ac; +int type; /* either hero cast spell type or 0 */ +{ + int chance = rn2(20); + int spell_bonus = type ? spell_hit_bonus(type) : 0; + + /* small chance for naked target to avoid being hit */ + if (!chance) return rnd(10) < ac+spell_bonus; + + /* very high armor protection does not achieve invulnerability */ + ac = AC_VALUE(ac); + + return (3 - chance) < ac+spell_bonus; +} + +/* type == 0 to 9 : you shooting a wand */ +/* type == 10 to 19 : you casting a spell */ +/* type == 20 to 29 : you breathing as a monster */ +/* type == -10 to -19 : monster casting spell */ +/* type == -20 to -29 : monster breathing at you */ +/* type == -30 to -39 : monster shooting a wand */ +/* called with dx = dy = 0 with vertical bolts */ +void +buzz(type,nd,sx,sy,dx,dy) +register int type, nd; +register xchar sx,sy; +register int dx,dy; +{ + int range, abstype = abs(type) % 10; + struct rm *lev; + register xchar lsx, lsy; + struct monst *mon; + coord save_bhitpos; + boolean shopdamage = FALSE; + register const char *fltxt; + struct obj *otmp; + int spell_type; + + /* if its a Hero Spell then get its SPE_TYPE */ + spell_type = is_hero_spell(type) ? SPE_MAGIC_MISSILE + abstype : 0; + + fltxt = flash_types[(type <= -30) ? abstype : abs(type)]; + if(u.uswallow) { + register int tmp; + + if(type < 0) return; + tmp = zhitm(u.ustuck, type, nd, &otmp); + if(!u.ustuck) u.uswallow = 0; + else pline("%s rips into %s%s", + The(fltxt), mon_nam(u.ustuck), exclam(tmp)); + /* Using disintegration from the inside only makes a hole... */ + if (tmp == MAGIC_COOKIE) + u.ustuck->mhp = 0; + if (u.ustuck->mhp < 1) + killed(u.ustuck); + return; + } + if(type < 0) newsym(u.ux,u.uy); + range = rn1(7,7); + if(dx == 0 && dy == 0) range = 1; + save_bhitpos = bhitpos; + + tmp_at(DISP_BEAM, zapdir_to_glyph(dx, dy, abstype)); + while(range-- > 0) { + lsx = sx; sx += dx; + lsy = sy; sy += dy; + if(isok(sx,sy) && (lev = &levl[sx][sy])->typ) { + mon = m_at(sx, sy); + if(cansee(sx,sy)) { + /* reveal/unreveal invisible monsters before tmp_at() */ + if (mon && !canspotmon(mon)) + map_invisible(sx, sy); + else if (!mon && glyph_is_invisible(levl[sx][sy].glyph)) { + unmap_object(sx, sy); + newsym(sx, sy); + } + if(ZAP_POS(lev->typ) || cansee(lsx,lsy)) + tmp_at(sx,sy); + delay_output(); /* wait a little */ + } + } else + goto make_bounce; + + /* hit() and miss() need bhitpos to match the target */ + bhitpos.x = sx, bhitpos.y = sy; + /* Fireballs only damage when they explode */ + if (type != ZT_SPELL(ZT_FIRE)) + range += zap_over_floor(sx, sy, type, &shopdamage); + + if (mon) { + if (type == ZT_SPELL(ZT_FIRE)) break; + if (type >= 0) mon->mstrategy &= ~STRAT_WAITMASK; +#ifdef STEED + buzzmonst: +#endif + if (zap_hit(find_mac(mon), spell_type)) { + if (mon_reflects(mon, (char *)0)) { + if(cansee(mon->mx,mon->my)) { + hit(fltxt, mon, exclam(0)); + shieldeff(mon->mx, mon->my); + (void) mon_reflects(mon, "But it reflects from %s %s!"); + } + dx = -dx; + dy = -dy; + } else { + boolean mon_could_move = mon->mcanmove; + int tmp = zhitm(mon, type, nd, &otmp); + + if (is_rider(mon->data) && abs(type) == ZT_BREATH(ZT_DEATH)) { + if (canseemon(mon)) { + hit(fltxt, mon, "."); + pline("%s disintegrates.", Monnam(mon)); + pline("%s body reintegrates before your %s!", + s_suffix(Monnam(mon)), + (eyecount(youmonst.data) == 1) ? + body_part(EYE) : makeplural(body_part(EYE))); + pline("%s resurrects!", Monnam(mon)); + } + mon->mhp = mon->mhpmax; + break; /* Out of while loop */ + } + if (mon->data == &mons[PM_DEATH] && abstype == ZT_DEATH) { + if (canseemon(mon)) { + hit(fltxt, mon, "."); + pline("%s absorbs the deadly %s!", Monnam(mon), + type == ZT_BREATH(ZT_DEATH) ? + "blast" : "ray"); + pline("It seems even stronger than before."); + } + break; /* Out of while loop */ + } + + if (tmp == MAGIC_COOKIE) { /* disintegration */ + struct obj *otmp2, *m_amulet = mlifesaver(mon); + + if (canseemon(mon)) { + if (!m_amulet) + pline("%s is disintegrated!", Monnam(mon)); + else + hit(fltxt, mon, "!"); + } +#ifndef GOLDOBJ + mon->mgold = 0L; +#endif + +/* note: worn amulet of life saving must be preserved in order to operate */ +#define oresist_disintegration(obj) \ + (objects[obj->otyp].oc_oprop == DISINT_RES || \ + obj_resists(obj, 5, 50) || is_quest_artifact(obj) || \ + obj == m_amulet) + + for (otmp = mon->minvent; otmp; otmp = otmp2) { + otmp2 = otmp->nobj; + if (!oresist_disintegration(otmp)) { + obj_extract_self(otmp); + obfree(otmp, (struct obj *)0); + } + } + + if (type < 0) + monkilled(mon, (char *)0, -AD_RBRE); + else + xkilled(mon, 2); + } else if(mon->mhp < 1) { + if(type < 0) + monkilled(mon, fltxt, AD_RBRE); + else + killed(mon); + } else { + if (!otmp) { + /* normal non-fatal hit */ + hit(fltxt, mon, exclam(tmp)); + } else { + /* some armor was destroyed; no damage done */ + if (canseemon(mon)) + pline("%s %s is disintegrated!", + s_suffix(Monnam(mon)), + distant_name(otmp, xname)); + m_useup(mon, otmp); + } + if (mon_could_move && !mon->mcanmove) /* ZT_SLEEP */ + slept_monst(mon); + } + } + range -= 2; + } else { + miss(fltxt,mon); + } + } else if (sx == u.ux && sy == u.uy && range >= 0) { + nomul(0); +#ifdef STEED + if (u.usteed && !rn2(3) && !mon_reflects(u.usteed, (char *)0)) { + mon = u.usteed; + goto buzzmonst; + } else +#endif + if (zap_hit((int) u.uac, 0)) { + range -= 2; + pline("%s hits you!", The(fltxt)); + if (Reflecting) { + if (!Blind) { + (void) ureflects("But %s reflects from your %s!", "it"); + } else + pline("For some reason you are not affected."); + dx = -dx; + dy = -dy; + shieldeff(sx, sy); + } else { + zhitu(type, nd, fltxt, sx, sy); + } + } else { + pline("%s whizzes by you!", The(fltxt)); + } + if (abstype == ZT_LIGHTNING && !resists_blnd(&youmonst)) { + You(are_blinded_by_the_flash); + make_blinded((long)d(nd,50),FALSE); + if (!Blind) Your(vision_clears); + } + stop_occupation(); + nomul(0); + } + + if(!ZAP_POS(lev->typ) || (closed_door(sx, sy) && (range >= 0))) { + int bounce; + uchar rmn; + + make_bounce: + if (type == ZT_SPELL(ZT_FIRE)) { + sx = lsx; + sy = lsy; + break; /* fireballs explode before the wall */ + } + bounce = 0; + range--; + if(range && isok(lsx, lsy) && cansee(lsx,lsy)) + pline("%s bounces!", The(fltxt)); + if(!dx || !dy || !rn2(20)) { + dx = -dx; + dy = -dy; + } else { + if(isok(sx,lsy) && ZAP_POS(rmn = levl[sx][lsy].typ) && + !closed_door(sx,lsy) && + (IS_ROOM(rmn) || (isok(sx+dx,lsy) && + ZAP_POS(levl[sx+dx][lsy].typ)))) + bounce = 1; + if(isok(lsx,sy) && ZAP_POS(rmn = levl[lsx][sy].typ) && + !closed_door(lsx,sy) && + (IS_ROOM(rmn) || (isok(lsx,sy+dy) && + ZAP_POS(levl[lsx][sy+dy].typ)))) + if(!bounce || rn2(2)) + bounce = 2; + + switch(bounce) { + case 0: dx = -dx; /* fall into... */ + case 1: dy = -dy; break; + case 2: dx = -dx; break; + } + tmp_at(DISP_CHANGE, zapdir_to_glyph(dx,dy,abstype)); + } + } + } + tmp_at(DISP_END,0); + if (type == ZT_SPELL(ZT_FIRE)) + explode(sx, sy, type, d(12,6), 0, EXPL_FIERY); + if (shopdamage) + pay_for_damage(abstype == ZT_FIRE ? "burn away" : + abstype == ZT_COLD ? "shatter" : + abstype == ZT_DEATH ? "disintegrate" : "destroy", FALSE); + bhitpos = save_bhitpos; +} +#endif /*OVLB*/ +#ifdef OVL0 + +void +melt_ice(x, y) +xchar x, y; +{ + struct rm *lev = &levl[x][y]; + struct obj *otmp; + + if (lev->typ == DRAWBRIDGE_UP) + lev->drawbridgemask &= ~DB_ICE; /* revert to DB_MOAT */ + else { /* lev->typ == ICE */ +#ifdef STUPID + if (lev->icedpool == ICED_POOL) lev->typ = POOL; + else lev->typ = MOAT; +#else + lev->typ = (lev->icedpool == ICED_POOL ? POOL : MOAT); +#endif + lev->icedpool = 0; + } + obj_ice_effects(x, y, FALSE); + unearth_objs(x, y); + if (Underwater) vision_recalc(1); + newsym(x,y); + if (cansee(x,y)) Norep("The ice crackles and melts."); + if ((otmp = sobj_at(BOULDER, x, y)) != 0) { + if (cansee(x,y)) pline("%s settles...", An(xname(otmp))); + do { + obj_extract_self(otmp); /* boulder isn't being pushed */ + if (!boulder_hits_pool(otmp, x, y, FALSE)) + impossible("melt_ice: no pool?"); + /* try again if there's another boulder and pool didn't fill */ + } while (is_pool(x,y) && (otmp = sobj_at(BOULDER, x, y)) != 0); + newsym(x,y); + } + if (x == u.ux && y == u.uy) + spoteffects(TRUE); /* possibly drown, notice objects */ +} + +/* Burn floor scrolls, evaporate pools, etc... in a single square. Used + * both for normal bolts of fire, cold, etc... and for fireballs. + * Sets shopdamage to TRUE if a shop door is destroyed, and returns the + * amount by which range is reduced (the latter is just ignored by fireballs) + */ +int +zap_over_floor(x, y, type, shopdamage) +xchar x, y; +int type; +boolean *shopdamage; +{ + struct monst *mon; + int abstype = abs(type) % 10; + struct rm *lev = &levl[x][y]; + int rangemod = 0; + + if(abstype == ZT_FIRE) { + struct trap *t = t_at(x, y); + + if (t && t->ttyp == WEB) { + /* a burning web is too flimsy to notice if you can't see it */ + if (cansee(x,y)) Norep("A web bursts into flames!"); + (void) delfloortrap(t); + if (cansee(x,y)) newsym(x,y); + } + if(is_ice(x, y)) { + melt_ice(x, y); + } else if(is_pool(x,y)) { + const char *msgtxt = "You hear hissing gas."; + if(lev->typ != POOL) { /* MOAT or DRAWBRIDGE_UP */ + if (cansee(x,y)) msgtxt = "Some water evaporates."; + } else { + register struct trap *ttmp; + + rangemod -= 3; + lev->typ = ROOM; + ttmp = maketrap(x, y, PIT); + if (ttmp) ttmp->tseen = 1; + if (cansee(x,y)) msgtxt = "The water evaporates."; + } + Norep(msgtxt); + if (lev->typ == ROOM) newsym(x,y); + } else if(IS_FOUNTAIN(lev->typ)) { + if (cansee(x,y)) + pline("Steam billows from the fountain."); + rangemod -= 1; + dryup(x, y, type > 0); + } + } + else if(abstype == ZT_COLD && (is_pool(x,y) || is_lava(x,y))) { + boolean lava = is_lava(x,y); + boolean moat = (!lava && (lev->typ != POOL) && + (lev->typ != WATER) && + !Is_medusa_level(&u.uz) && + !Is_waterlevel(&u.uz)); + + if (lev->typ == WATER) { + /* For now, don't let WATER freeze. */ + if (cansee(x,y)) + pline_The("water freezes for a moment."); + else + You_hear("a soft crackling."); + rangemod -= 1000; /* stop */ + } else { + rangemod -= 3; + if (lev->typ == DRAWBRIDGE_UP) { + lev->drawbridgemask &= ~DB_UNDER; /* clear lava */ + lev->drawbridgemask |= (lava ? DB_FLOOR : DB_ICE); + } else { + if (!lava) + lev->icedpool = + (lev->typ == POOL ? ICED_POOL : ICED_MOAT); + lev->typ = (lava ? ROOM : ICE); + } + bury_objs(x,y); + if(cansee(x,y)) { + if(moat) + Norep("The moat is bridged with ice!"); + else if(lava) + Norep("The lava cools and solidifies."); + else + Norep("The water freezes."); + newsym(x,y); + } else if(flags.soundok && !lava) + You_hear("a crackling sound."); + + if (x == u.ux && y == u.uy) { + if (u.uinwater) { /* not just `if (Underwater)' */ + /* leave the no longer existent water */ + u.uinwater = 0; + u.uundetected = 0; + docrt(); + vision_full_recalc = 1; + } else if (u.utrap && u.utraptype == TT_LAVA) { + if (Passes_walls) { + You("pass through the now-solid rock."); + } else { + u.utrap = rn1(50,20); + u.utraptype = TT_INFLOOR; + You("are firmly stuck in the cooling rock."); + } + } + } else if ((mon = m_at(x,y)) != 0) { + /* probably ought to do some hefty damage to any + non-ice creature caught in freezing water; + at a minimum, eels are forced out of hiding */ + if (is_swimmer(mon->data) && mon->mundetected) { + mon->mundetected = 0; + newsym(x,y); + } + } + } + obj_ice_effects(x,y,TRUE); + } + if(closed_door(x, y)) { + int new_doormask = -1; + const char *see_txt = 0, *sense_txt = 0, *hear_txt = 0; + rangemod = -1000; + switch(abstype) { + case ZT_FIRE: + new_doormask = D_NODOOR; + see_txt = "The door is consumed in flames!"; + sense_txt = "smell smoke."; + break; + case ZT_COLD: + new_doormask = D_NODOOR; + see_txt = "The door freezes and shatters!"; + sense_txt = "feel cold."; + break; + case ZT_DEATH: + /* death spells/wands don't disintegrate */ + if(abs(type) != ZT_BREATH(ZT_DEATH)) + goto def_case; + new_doormask = D_NODOOR; + see_txt = "The door disintegrates!"; + hear_txt = "crashing wood."; + break; + case ZT_LIGHTNING: + new_doormask = D_BROKEN; + see_txt = "The door splinters!"; + hear_txt = "crackling."; + break; + default: + def_case: + if(cansee(x,y)) { + pline_The("door absorbs %s %s!", + (type < 0) ? "the" : "your", + abs(type) < ZT_SPELL(0) ? "bolt" : + abs(type) < ZT_BREATH(0) ? "spell" : + "blast"); + } else You_feel("vibrations."); + break; + } + if (new_doormask >= 0) { /* door gets broken */ + if (*in_rooms(x, y, SHOPBASE)) { + if (type >= 0) { + add_damage(x, y, 400L); + *shopdamage = TRUE; + } else /* caused by monster */ + add_damage(x, y, 0L); + } + lev->doormask = new_doormask; + unblock_point(x, y); /* vision */ + if (cansee(x, y)) { + pline(see_txt); + newsym(x, y); + } else if (sense_txt) { + You(sense_txt); + } else if (hear_txt) { + if (flags.soundok) You_hear(hear_txt); + } + if (picking_at(x, y)) { + stop_occupation(); + reset_pick(); + } + } + } + + if(OBJ_AT(x, y) && abstype == ZT_FIRE) + if (burn_floor_paper(x, y, FALSE, type > 0) && couldsee(x, y)) { + newsym(x,y); + You("%s of smoke.", + !Blind ? "see a puff" : "smell a whiff"); + } + if ((mon = m_at(x,y)) != 0) { + /* Cannot use wakeup() which also angers the monster */ + mon->msleeping = 0; + if(mon->m_ap_type) seemimic(mon); + if(type >= 0) { + setmangry(mon); + if(mon->ispriest && *in_rooms(mon->mx, mon->my, TEMPLE)) + ghod_hitsu(mon); + if(mon->isshk && !*u.ushops) + hot_pursuit(mon); + } + } + return rangemod; +} + +#endif /*OVL0*/ +#ifdef OVL3 + +void +fracture_rock(obj) /* fractured by pick-axe or wand of striking */ +register struct obj *obj; /* no texts here! */ +{ + /* A little Sokoban guilt... */ + if (obj->otyp == BOULDER && In_sokoban(&u.uz) && !flags.mon_moving) + change_luck(-1); + + obj->otyp = ROCK; + obj->quan = (long) rn1(60, 7); + obj->owt = weight(obj); + obj->oclass = GEM_CLASS; + obj->known = FALSE; + obj->onamelth = 0; /* no names */ + obj->oxlth = 0; /* no extra data */ + obj->oattached = OATTACHED_NOTHING; + if (obj->where == OBJ_FLOOR) { + obj_extract_self(obj); /* move rocks back on top */ + place_object(obj, obj->ox, obj->oy); + if(!does_block(obj->ox,obj->oy,&levl[obj->ox][obj->oy])) + unblock_point(obj->ox,obj->oy); + if(cansee(obj->ox,obj->oy)) + newsym(obj->ox,obj->oy); + } +} + +/* handle statue hit by striking/force bolt/pick-axe */ +boolean +break_statue(obj) +register struct obj *obj; +{ + /* [obj is assumed to be on floor, so no get_obj_location() needed] */ + struct trap *trap = t_at(obj->ox, obj->oy); + struct obj *item; + + if (trap && trap->ttyp == STATUE_TRAP && + activate_statue_trap(trap, obj->ox, obj->oy, TRUE)) + return FALSE; + /* drop any objects contained inside the statue */ + while ((item = obj->cobj) != 0) { + obj_extract_self(item); + place_object(item, obj->ox, obj->oy); + } + if (Role_if(PM_ARCHEOLOGIST) && !flags.mon_moving && (obj->spe & STATUE_HISTORIC)) { + You_feel("guilty about damaging such a historic statue."); + adjalign(-1); + } + obj->spe = 0; + fracture_rock(obj); + return TRUE; +} + +const char * const destroy_strings[] = { /* also used in trap.c */ + "freezes and shatters", "freeze and shatter", "shattered potion", + "boils and explodes", "boil and explode", "boiling potion", + "catches fire and burns", "catch fire and burn", "burning scroll", + "catches fire and burns", "catch fire and burn", "burning book", + "turns to dust and vanishes", "turn to dust and vanish", "", + "breaks apart and explodes", "break apart and explode", "exploding wand" +}; + +void +destroy_item(osym, dmgtyp) +register int osym, dmgtyp; +{ + register struct obj *obj, *obj2; + register int dmg, xresist, skip; + register long i, cnt, quan; + register int dindx; + const char *mult; + + for(obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if(obj->oclass != osym) continue; /* test only objs of type osym */ + if(obj->oartifact) continue; /* don't destroy artifacts */ + if(obj->in_use && obj->quan == 1) continue; /* not available */ + xresist = skip = 0; +#ifdef GCC_WARN + dmg = dindx = 0; + quan = 0L; +#endif + switch(dmgtyp) { + case AD_COLD: + if(osym == POTION_CLASS && obj->otyp != POT_OIL) { + quan = obj->quan; + dindx = 0; + dmg = rnd(4); + } else skip++; + break; + case AD_FIRE: + xresist = (Fire_resistance && obj->oclass != POTION_CLASS); + + if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) + skip++; + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + skip++; + if (!Blind) + pline("%s glows a strange %s, but remains intact.", + The(xname(obj)), hcolor("dark red")); + } + quan = obj->quan; + switch(osym) { + case POTION_CLASS: + dindx = 1; + dmg = rnd(6); + break; + case SCROLL_CLASS: + dindx = 2; + dmg = 1; + break; + case SPBOOK_CLASS: + dindx = 3; + dmg = 1; + break; + default: + skip++; + break; + } + break; + case AD_ELEC: + xresist = (Shock_resistance && obj->oclass != RING_CLASS); + quan = obj->quan; + switch(osym) { + case RING_CLASS: + if(obj->otyp == RIN_SHOCK_RESISTANCE) + { skip++; break; } + dindx = 4; + dmg = 0; + break; + case WAND_CLASS: + if(obj->otyp == WAN_LIGHTNING) { skip++; break; } +#if 0 + if (obj == current_wand) { skip++; break; } +#endif + dindx = 5; + dmg = rnd(10); + break; + default: + skip++; + break; + } + break; + default: + skip++; + break; + } + if(!skip) { + if (obj->in_use) --quan; /* one will be used up elsewhere */ + for(i = cnt = 0L; i < quan; i++) + if(!rn2(3)) cnt++; + + if(!cnt) continue; + if(cnt == quan) mult = "Your"; + else mult = (cnt == 1L) ? "One of your" : "Some of your"; + pline("%s %s %s!", mult, xname(obj), + (cnt > 1L) ? destroy_strings[dindx*3 + 1] + : destroy_strings[dindx*3]); + if(osym == POTION_CLASS && dmgtyp != AD_COLD) { + if (!breathless(youmonst.data) || haseyes(youmonst.data)) + potionbreathe(obj); + } + if (obj->owornmask) { + if (obj->owornmask & W_RING) /* ring being worn */ + Ring_gone(obj); + else + setnotworn(obj); + } + if (obj == current_wand) current_wand = 0; /* destroyed */ + for (i = 0; i < cnt; i++) + useup(obj); + if(dmg) { + if(xresist) You("aren't hurt!"); + else { + const char *how = destroy_strings[dindx * 3 + 2]; + boolean one = (cnt == 1L); + + losehp(dmg, one ? how : (const char *)makeplural(how), + one ? KILLED_BY_AN : KILLED_BY); + exercise(A_STR, FALSE); + } + } + } + } + return; +} + +int +destroy_mitem(mtmp, osym, dmgtyp) +struct monst *mtmp; +int osym, dmgtyp; +{ + struct obj *obj, *obj2; + int skip, tmp = 0; + long i, cnt, quan; + int dindx; + boolean vis; + + if (mtmp == &youmonst) { /* this simplifies artifact_hit() */ + destroy_item(osym, dmgtyp); + return 0; /* arbitrary; value doesn't matter to artifact_hit() */ + } + + vis = canseemon(mtmp); + for(obj = mtmp->minvent; obj; obj = obj2) { + obj2 = obj->nobj; + if(obj->oclass != osym) continue; /* test only objs of type osym */ + skip = 0; + quan = 0L; + dindx = 0; + + switch(dmgtyp) { + case AD_COLD: + if(osym == POTION_CLASS && obj->otyp != POT_OIL) { + quan = obj->quan; + dindx = 0; + tmp++; + } else skip++; + break; + case AD_FIRE: + if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL) + skip++; + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { + skip++; + if (vis) + pline("%s glows a strange %s, but remains intact.", + The(distant_name(obj, xname)), + hcolor("dark red")); + } + quan = obj->quan; + switch(osym) { + case POTION_CLASS: + dindx = 1; + tmp++; + break; + case SCROLL_CLASS: + dindx = 2; + tmp++; + break; + case SPBOOK_CLASS: + dindx = 3; + tmp++; + break; + default: + skip++; + break; + } + break; + case AD_ELEC: + quan = obj->quan; + switch(osym) { + case RING_CLASS: + if(obj->otyp == RIN_SHOCK_RESISTANCE) + { skip++; break; } + dindx = 4; + break; + case WAND_CLASS: + if(obj->otyp == WAN_LIGHTNING) { skip++; break; } + dindx = 5; + tmp++; + break; + default: + skip++; + break; + } + break; + default: + skip++; + break; + } + if(!skip) { + for(i = cnt = 0L; i < quan; i++) + if(!rn2(3)) cnt++; + + if(!cnt) continue; + if (vis) pline("%s %s %s!", + s_suffix(Monnam(mtmp)), xname(obj), + (cnt > 1L) ? destroy_strings[dindx*3 + 1] + : destroy_strings[dindx*3]); + for(i = 0; i < cnt; i++) m_useup(mtmp, obj); + } + } + return(tmp); +} + +#endif /*OVL3*/ +#ifdef OVL2 + +int +resist(mtmp, oclass, damage, tell) +struct monst *mtmp; +char oclass; +int damage, tell; +{ + int resisted; + int alev, dlev; + + /* attack level */ + switch (oclass) { + case WAND_CLASS: alev = 12; break; + case TOOL_CLASS: alev = 10; break; /* instrument */ + case WEAPON_CLASS: alev = 10; break; /* artifact */ + case SCROLL_CLASS: alev = 9; break; + case POTION_CLASS: alev = 6; break; + case RING_CLASS: alev = 5; break; + default: alev = u.ulevel; break; /* spell */ + } + /* defense level */ + dlev = (int)mtmp->m_lev; + if (dlev > 50) dlev = 50; + else if (dlev < 1) dlev = is_mplayer(mtmp->data) ? u.ulevel : 1; + + resisted = rn2(100 + alev - dlev) < mtmp->data->mr; + if (resisted) { + if (tell) { + shieldeff(mtmp->mx, mtmp->my); + pline("%s resists!", Monnam(mtmp)); + } + damage = (damage + 1) / 2; + } + + if (damage) { + mtmp->mhp -= damage; + if (mtmp->mhp < 1) { + if(m_using) monkilled(mtmp, "", AD_RBRE); + else killed(mtmp); + } + } + return(resisted); +} + +void +makewish() +{ + char buf[BUFSZ]; + struct obj *otmp, nothing; + int tries = 0; + + nothing = zeroobj; /* lint suppression; only its address matters */ + if (flags.verbose) You("may wish for an object."); +retry: + getlin("For what do you wish?", buf); + if(buf[0] == '\033') buf[0] = 0; + /* + * Note: if they wished for and got a non-object successfully, + * otmp == &zeroobj. That includes gold, or an artifact that + * has been denied. Wishing for "nothing" requires a separate + * value to remain distinct. + */ + otmp = readobjnam(buf, ¬hing, TRUE); + if (!otmp) { + pline("Nothing fitting that description exists in the game."); + if (++tries < 5) goto retry; + pline(thats_enough_tries); + otmp = readobjnam((char *)0, (struct obj *)0, TRUE); + if (!otmp) return; /* for safety; should never happen */ + } else if (otmp == ¬hing) { + /* explicitly wished for "nothing", presumeably attempting + to retain wishless conduct */ + return; + } + + /* KMH, conduct */ + u.uconduct.wishes++; + + if (otmp != &zeroobj) { + /* The(aobjnam()) is safe since otmp is unidentified -dlc */ + (void) hold_another_object(otmp, u.uswallow ? + "Oops! %s out of your reach!" : + (Is_airlevel(&u.uz) || + Is_waterlevel(&u.uz) || + levl[u.ux][u.uy].typ < IRONBARS || + levl[u.ux][u.uy].typ >= ICE) ? + "Oops! %s away from you!" : + "Oops! %s to the floor!", + The(aobjnam(otmp, + Is_airlevel(&u.uz) || u.uinwater ? + "slip" : "drop")), + (const char *)0); + u.ublesscnt += rn1(100,50); /* the gods take notice */ + } +} + +#endif /*OVL2*/ + +/*zap.c*/ diff --git a/sys/amiga/Build.ami b/sys/amiga/Build.ami new file mode 100644 index 0000000..babca2e --- /dev/null +++ b/sys/amiga/Build.ami @@ -0,0 +1,146 @@ + + Compiling Amiga NetHack 3.4 + Last Revision: 21 February 2002 for NetHack 3.4.2 + + + We would like to thank each and every one of the people who took + the time and effort to report bugs to us. THANK YOU! (And keep + up the good work!) + +I. Introduction + + The Amiga-specific documentation has been split since the 3.1.3 release + - please read the file Install.ami for information specific to the + Amiga port before continuing. + + If you have problems with compilation, installation, or think you have + found a bug in the game, please report it by electronic mail to the + development group at nethack-bugs@nethack.org, where it will be routed + to the appropriate person. Include your configuration, the version of + NetHack you are playing (use the 'v' command or see + include/patchlevel.h), and as much specific information as possible. + As NetHack runs on many different machines, be sure to mention that you + are playing the Amiga version and also mention if you are using the + version for mc68k or ppc. + + If you want to find out about distributing NetHack, read the license + (in NetHack:license or type ?i during the game). + +II. Compiling Amiga NetHack 3.4 + +II.A. Compilation Overview + Compiling NetHack is not very hard - basically you do a little + configuration and start make. It does, however, require a good amount + of disk space and time. It also needs a good bit of memory, especially + for linking. + +II.B. Basic Compilation + + NetHack can be built with SAS/C version 6.5x. The commercial version + of DICE might work, but NetHack version 3.2.2 or later haven't been + compiled with it. The "official" compiler for NetHack 3.4 is SAS/C 6.58 + - we have dropped support for SAS/C 5.x. + + The Manx/Aztec port has not been tested recently and is certainly + broken. Anyone managing to compile NetHack with this compiler is + encouraged to submit context diffs of the required changes. When last + tested, NetHack required version 5.0B of that compiler. + + Compiling with gcc should also work. + +II.B.1. Introduction to Compiling NetHack + Before doing any compilation, read the README files distributed with + the source. These should familiarize you with the source tree layout + and what files are shared with what computers; everything in the + sys/amiga directory is used exclusively by the Amiga. + + The makefile (sys/amiga/Makefile.ami) depends on certain assignments, + providing the developer with a fairly flexible environment. See + sys/amiga/Makefile.ami for assignment assumptions. DICE users should + see section II.B.3 for information on creating a DMakefile usable with + DMake. + + Edit config.h to your liking and system configuration. The defaults + should be satisfactory for most systems. + + Read VERY CAREFULLY through the Makefile to familiarize yourself + with which assignments are assumed. Otherwise, you're going to get + something like "Insert NH: in any drive." You will need uudecode, + and, if you need to modify dgn_comp or lev_comp, flex, and bison. + The first thing Makefile.ami does is build makedefs, which handles + a variety of data file generation, and then lev_comp and dgn_comp + which compile the special levels. Makedefs will then be run to create + a few files, followed by a roughly alphabetically sorted compilation + of the entire source tree. This process will compile selected files + from the sys/amiga, sys/share, win/tty, and src directories, eventually + creating sbin/nethack. After building the main binary, a make install + will build the auxiliary files including help files, special levels, + icons, and the font files and will put these files into their final + positions - most will be in dlb archives (if DLB was defined in config.h). + The first make run should be done in NH:obj and the make install should be + done in NetHack:; for both runs, the makefile is NH:sys/amiga/Makefile.ami + (or NH:sys/amiga/DMakefile for DMake and NH:sys/amiga/Makefile.agc for + gcc). + + Note that not all the source is simple C code. If you are modifying + lev_comp or dgn_comp you may need bison and/or flex (depending on what + modifications you are making). You do not need any of these tools to + simply build NetHack - all the C output files are provided in the source + distribution. Also, the ifchange script requires a version of diff that + produces standard Unix format context diffs for proper operation - the + version shipped with SAS/C is not sufficient. + + If you do not have bison and flex, copy the files from sys/share. The + include files go in include/ and the C files go in util/. If the compile + fails due to prototype errors for malloc and realloc, try deleting + the first line of lev_comp.c and dgn_comp.c. + +II.B.2. Compiling NetHack with SAS/C version 6.58 + + NOTE WELL - Amiga NetHack has dropped support for SAS/C version 5. + This version of NetHack was developed with SAS/C 6.58. Earlier versions + than version of the compiler are known to cause problems - don't use them. + + A couple of notes and warnings from the SAS/C users on the team: + + * Included in the SAS/C port is code for generating a SnapShot.tb + file upon catching various internal disasters. That is why the + debug=l flag is in the makefile. This adds about 270K to the disk + image, but it does not increase the run time memory requirements. + + * The 5.10b optimizer did not produce correct code for NetHack. The + current optimizer has not been tested. + +II.B.3. Compiling NetHack with the commercial version of DICE + + IMPORTANT NOTE: If you are using DMake, you need to create DMakefile + from Makefile.ami. Do the following: + + cd NH:sys/amiga + edit from Makefile.ami to DMakefile with mkdmake opt w255 + + Some versions of DMake have been known to crash horribly on the + makefile - if this happens, you'll need to download another make + utility, such as AMake (ftp://ftp.dragonfire.net/amiga/utils/amake), + which will run in DMake-compatibility mode if invoked with the -C switch + (e.g. "amake -C -f NH:sys/amiga/DMakefile", or just + "alias dmake amake -C"). + + SECOND IMPORTANT NOTE: The score list is currently disabled when + compiling under DICE, due to an as-yet-unknown problem which causes + system crashes when displaying the score list. + + NetHack can be compiled using the commercial version of DICE only. The + registered shareware version had a bug in it which resulted in odd- + aligned procedures. (It is possible to patch DC1 to fix this problem; + however, this is not recommended, and you should upgrade to the + commercial version.) + + DICE 3.0 (the first commercial release) has a couple of bugs in it which + turn up in several of the NetHack sources; the DCC30_BUG define fixes + them. If you have a more recent version of the compiler, you may be + able to compile without this (and get slightly more efficient code) by + commenting out the define in amiconf.h. + + During compilation, DICE will output a lot of warnings; they can be + safely ignored. diff --git a/sys/amiga/Install.ami b/sys/amiga/Install.ami new file mode 100644 index 0000000..9045a16 --- /dev/null +++ b/sys/amiga/Install.ami @@ -0,0 +1,200 @@ + + Using and Installing Amiga NetHack 3.4 + (or Everything You Never Wanted to Know Before NetHacking) + (or Not Everything That Happens Always Comes Knocking) + + Last Revision: 28 March 2000 for NetHack 3.4.2 + + +0. Pre-intro for NetHack 3.4.2: + Amiga-specific changes for 3.4.2: + Most (around 99%) known bugs fixed (volunteers welcome). + HackWB and HackCli are no longer supported. Use the main binary. + + We would like to thank each and every one of the people who took + the time and effort to report bugs to us. THANK YOU! + +I. Introduction + +I.A. Overview + Welcome to Amiga NetHack! If this is your first visit to our fair + city, you are in for an amazing but dangerous journey; if you have + visited us before, beware! the city has changed in many strange and + subtle ways; it has also grown quite a bit. This missive brings to + light those mundane tasks which must be dealt with before beginning + your journey; for those of you who are faced with the task of + installing the pre-fabricated version of our town, section III + (Installing Amiga NetHack 3.4) will guide you through the task at + hand. If you are ready to visit, the local visitors guide is in + section II (Using Amiga NetHack 3.4); please also see the general + guide packaged separately (the file "GuideBook"). + + To all our visitors, a hearty Welcome! - and please be careful. + + [Those responsible for the previous paragraphs have been sacked. The + documentation has been completed at great expense in a more traditional + style. -- The Management] + +I.B. Getting Help + If you have questions about strategy, weapons, or monsters, the best + place to go for help is the Usenet newsgroup rec.games.roguelike.nethack. + + If you have problems with installation or think you have found a bug + in the game, please report it by electronic mail to the development + team at nethack-bugs@nethack.org, where it will be routed to the + appropriate person. Include your configuration, the version of + NetHack you are playing (use the 'v' command), whether or not you are + using an official binary release (and if so which one) and as much + specific information as possible. As NetHack runs on many different + machines, be sure to mention that you are playing the Amiga version. + +I.C. Credits + Olaf Seibert first ported NetHack 2.3 and 3.0 to the Amiga. Richard + Addison, Andrew Church, Jochen Erwied, Mark Gooderum, Ken Lorber, + Greg Olson, Mike Passaretti, and Gregg Wonderly polished and extended + the 3.0 and 3.1 ports. Andrew Church, Ken Lorber, and Gregg Wonderly + are responsible for the 3.2 port. Janne Salmijärvi resurrected the + amigaport for 3.3 and Teemu Suikki joined before 3.4.0. + +II. Using Amiga NetHack 3.4 + Run NetHack from the shell or from some tool that allows that, + ie. ToolManager. See the NetHack.txt file for command line options + and other usage. + +II.A. Sources of Information + Where to go depends on what you want to find out. If you want to find + out about distributing NetHack, read the license (in NetHack:license + or type ?i during the game). For an introduction to NetHack, read + the GuideBook file. To find out what options are compiled into your + copy of NetHack, type #v during the game. Finally, for information + during the game on all kinds of things, type ? and select from the + menu or by pressing Help key. + +II.B. The Amiga NetHack WorkBench Front End + Starting from 3.3.0 HackWB is not supported. + +II.C. The Amiga NetHack CLI Front End + Starting from 3.3.0 CLI Front end is not supported either. + + Instead, use the main binary. See NetHack.txt file for the standard Unix + flags for NetHack. In addition to those flags, Amiga NetHack accepts + the flags -l to force non-interlaced mode and -L to force interlaced mode. + +II.D. Amiga-Specific Information for NetHack + + There are several options that are unique to the Amiga version of + NetHack that may be specified in the NetHack.cnf file or on an + OPTIONS line: + + altmeta allows the ALT keys to function as META keys. The default + is altmeta. + flush flush discards all characters in the queue except the first, + which limits typeahead accidents. The default is !flush. + silent turn off the audio output. The default is silent. + + The current version of Amiga NetHack also supports menu accelerators. + See Guidebook.txt for a detailed description. Also supported is + selecting the number of stacked objects to drop, used with the (D)rop + command. Type the number and then select an item (or items with + accelerators). Items with a count associated with them are denoted + with # in place of -. I.e. 'd - 3 blessed daggers' becomes + 'd # 3 blessed daggers'. You can clear the count by hitting esc + while counting or deselect and reselect the item. The default + is to drop all selected items (as before). + + For other options how to configure the screen setting and colors refer + to Nethack.cnf. + +III. Installing Amiga NetHack 3.4 + +III.A. General Installation + Installation should be easy - basically it consists of putting files + where they belong and adding an assign to your startup. If you are + installing from the official binary distribution, simply unpacking + the archive in the appropriate directory will put the files in the + places they belong. + + IF YOU ALREADY HAVE A PREVIOUS VERSION INSTALLED YOU MUST DELETE THE + OLD SAVE AND BONES FILES - THEY WILL NOT WORK! This includes save + and bones files from all previous versions of NetHack (yes, even 3.3.1). + If you have a game in progress and want to finish it, use your + current version and then update. + +Will NetHack fit on your machine? + NetHack 3.4 is large. NetHack 3.4 is very large. You will need: + > Any standard series Amiga: 500, 600, 1000, 1200, 2000, 2500, 3000, 4000. + > WorkBench 2.04 or later. + > At least 3 meg of RAM. NetHack will NOT run in 1 meg (probably even 2). + > Mass storage: A hard drive with over 3 meg of free space is highly + recommended. + +Hard Disk Installation: + Unpack the archive to your place of choice. Since you are reading this + you've probably done that already. Now just assign NetHack: to + NetHack directory containing the executable and datafiles and other needed + directories. + + Use the table in the next section to see where things should end up. + Be sure that the file 8 ends up in NetHack:hack/8. + +Configuration + Using your favorite text editor, edit NetHack:NetHack.cnf to match + your system. + + Create the save file directory (makedir NetHack:save) and the levels file + directory (makedir NetHack:levels), if they don't already exist. + + Create the score file (echo to NetHack:record) and, if desired, the log + file (echo to NetHack:logfile), if they don't already exist. You may + leave out logfile, but record is needed. + +III.B. File Location Table +NetHack: + amii.hlp Guidebook.txt hack.font + license NetHack NetHack.cnf + NetHack.txt nhdat nhsdat + record Recover Recover.txt + logfile (optional, but useful) +NetHack:hack + 8 +NetHack:tiles + monsters.iff objects.iff other.iff + +IV. BBS Interface + + [Since HackCli and split binary is no longer supported the following + probably doesn't apply anymore. Due to lack of a suitable environment + it is also untested.] + + The BBS mode is based on the standard NetHack tty port and is designed + for use in a BBS setting - it is specifically not recommended for use + on the console. The current TTY mode has changed significantly since + the preliminary version released with 3.1.2. In particular, BBS mode + now works with split binaries (only), and now supports multiple games + in progress at the same time for multi-line boards (note however that + any individual user should not be allowed to run two instances of + NetHack at the same time). + + To set up NetHack for use with a BBS, set OPTIONS=windowtype:tty + and unset DUNGEONS, TRAPS, and EFFECTS in NetHack.cnf. Configure + the BBS to expect I/O through stdin and stdout, and have NetHack + invoked as: + HackCLI :uid -u uname options... + where uid is any string (without embedded spaces, colons, or slashes) + that is unique for each BBS user and uname is some corresponding human- + readable name for that user. Uid is used in constructing file names + to prevent collisions between simultaneous games and to prevent + people from using other people's save files. Uname is the name the + character will have in the game and the name that will appear in the + record file. + + The terminal is assumed to be a 24x80 ANSI-compatible terminal. + The present version does not deal with situations such as low + memory gracefully - as NetHack uses a considerable amount of + memory this is particularly painful with multiple games in + progress. Sysops are reminded to be familiar with the recover + utility, which may be needed from time to time and which should + probably not be available directly to users. Bug reports and + suggestions for improvements are requested from the user community - + this is still considered alpha software. + diff --git a/sys/amiga/Makefile.agc b/sys/amiga/Makefile.agc new file mode 100644 index 0000000..ea1d36b --- /dev/null +++ b/sys/amiga/Makefile.agc @@ -0,0 +1,1337 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.agc 3.2 2000/01/12 +# Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1991,1992,1993,1996. +# NetHack may be freely redistributed. See license for details. + +### +### modified for gcc by Teemu Suikki (zu@iki.fi) +### +### note: you need to use smake.. sorry +### + +### +### DIRECTORY STRUCTURE +### + +NH = nh: + +SBIN = $(NH)sbin/ +SLIB = $(NH)slib/ +NETHACK = $(NH)NetHack/ +HACKEXE = $(NH)HackExe/ +AMI = $(NH)sys/amiga/ +DAT = $(NH)dat/ +DOC = $(NH)doc/ +I = $(NH)include/ +SHARE = $(NH)sys/share/ +NHS = $(NH)src/ +TTY = $(NH)win/tty/ +WSHARE = $(NH)win/share/ +UTIL = $(NH)util/ +O = $(NH)obj/ +OO = $(NH)objo/ +# NB: O and OO MUST be different directories + +### +### INVOCATION +### + +MAKE = smake + +# Startup makefile with: +# +# $(MAKE) -f $(AMI)Makefile.amigcc +# $(MAKE) -f $(AMI)Makefile.amigcc install +# +# You may use following targets on $(MAKE) command lines: +# all do it all (default) +# link just create binary from object files +# obj just create common object files +# obja just create amiga object files +# objs just create shared object files +# clean deletes the object files +# spotless deletes the object files, main binary, and more +# +# Note: We do not build the Guidebook here since it needs tbl +# (See the file sys/unix/Makefile.doc for more information) + +#[SAS5] [and gcc?] +# If we were to use the precompiled header file feature in a newer version +# of SAS/C, we would comment out these following two lines. +# If we don't use precompiled header files, we uncomment it as well. + +HDEP = $(I)hack.h +CSYM = + +#Pathname for uudecode program: +UUDEC = uudecode + +# Flex/Bison command assignments -- Useful only if you have flex/bison +FLEX = flex +BISON = bison +# FBFIL and FBLIB may be used, if required by your version of flex or bison, +# to specify additional files or libraries to be linked with +FBFIL = +FBLIB = #lib lib:compat.lib + +# If you're compiling this on a 1.3 system, you'll have to uncomment the +# following (for use with the ifchange script below). Also useful instead of +# "protect ifchange +s" +EXECUTE = execute + +# Headers we depend on +AMDEP = $(AMI)winproto.h $(AMI)winext.h $(AMI)windefs.h $(I)winami.h + +# Pathname for the C compiler being used. + +CC = gcc -c +ASM = as + +# Compilation flags for selected C Compiler: +# $(CFLAGS) should appear before filename arguments of $(CC) command line. + +CFLAGS = -O3 -I $(I) + +# Components of various link command lines: +# $(LINK) should be the pathname of the linker being used (with any options +# that should appear at the beginning of the command line). The name of the +# output file should appear immediately after $(LNSPEC). $(LIN) should +# appear before the list of object files in each link command. $(LLINK) +# should appear as the list of object files in the link command line that +# creates the NetHack executable. $(LLIB) should appear at the end of each +# link command line. + +LINK = gcc -noixemul -O3 +LIN = +LLINK = +LLIB = +FLLIB = +OBJSPEC = -o +PNSPEC = -o +LNSPEC = -o +CCLINK = gcc -noixemul +CLFLAGS = -O3 +INCLSPEC = -I +DEFSPEC = -D +IGNSPEC = -j + +### +### FILE LISTS +### + +# A more reasonable random number generator (recommended for the Amiga): + +RANDOBJ = $(O)random.o + +.PRECIOUS: $(I)config.h $(I)decl.h $(I)hack.h $(I)permonst.h $(I)you.h + +# Almost nothing below this line should have to be changed. +# (Exceptions are marked by [SAS6], [MANX], etc.) +# +# Other things that have to be reconfigured are in config.h, +# (amiconf.h, pcconf.h), and possibly system.h, tradstdc.h. + +# Object files for makedefs: + +MAKEOBJS = \ + $(OO)makedefs.o $(O)monst.o $(O)objects.o + +# Object files for special levels compiler: + +SPLEVOBJS = \ + $(OO)lev_yacc.o $(OO)lev_lex.o $(OO)lev_main.o \ + $(O)decl.o $(O)drawing.o $(O)monst.o \ + $(O)objects.o $(OO)panic.o + +# Object files for dungeon compiler + +DGNCOMPOBJS = \ + $(OO)dgn_yacc.o $(OO)dgn_lex.o $(OO)dgn_main.o $(O)alloc.o $(OO)panic.o + +# Object files for NetHack: + +COMMOBJ = \ + $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o \ + $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o \ + $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o \ + $(O)dig.o $(O)display.o $(O)dlb.o $(O)do.o \ + $(O)do_name.o $(O)do_wear.o $(O)dog.o $(O)dogmove.o \ + $(O)dokick.o $(O)dothrow.o $(O)drawing.o $(O)dungeon.o \ + $(O)eat.o $(O)end.o $(O)engrave.o $(O)exper.o \ + $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o \ + $(O)hack.o $(O)hacklib.o $(O)invent.o $(O)light.o \ + $(O)lock.o $(O)mail.o $(O)makemon.o $(O)mapglyph.o \ + $(O)mcastu.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o \ + $(O)mklev.o $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o \ + $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o \ + $(O)monst.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o \ + $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o \ + $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o \ + $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o \ + $(O)quest.o $(O)questpgr.o $(O)read.o $(O)rect.o \ + $(O)region.o $(O)restore.o $(O)rnd.o $(O)role.o \ + $(O)rumors.o $(O)save.o $(O)shk.o $(O)shknam.o \ + $(O)sit.o $(O)sounds.o $(O)sp_lev.o $(O)spell.o \ + $(O)steal.o $(O)steed.o $(O)teleport.o $(O)timeout.o \ + $(O)topten.o $(O)track.o $(O)trap.o $(O)u_init.o \ + $(O)uhitm.o $(O)vault.o $(O)version.o $(O)vision.o \ + $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o \ + $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o \ + $(O)zap.o + +MAKEDEFOBJ = \ + $(O)monstr.o + +AMIGAOBJ = \ + $(O)amidos.o $(O)amirip.o $(O)amisnd.o $(O)amistack.o \ + $(O)amiwind.o $(O)winami.o $(O)winchar.o $(O)winfuncs.o \ + $(O)winkey.o $(O)winmenu.o $(O)winreq.o $(O)winstr.o + +# Objects from assembly sources (because DMake can't handle default rules) +AMIGAOBJ2 = \ +# $(O)dispmap.o + +SHAREOBJ = \ + $(O)pcmain.o $(RANDOBJ) + +TTYOBJ = \ + $(O)getline.o $(O)termcap.o $(O)topl.o $(O)wintty.o $(O)amitty.o \ + $(O)rip.o + +# Yuck yuck yuck. Have to tell DMake where these are, since they're not +# all in the same place. +TTYSRC = \ + $(TTY)getline.c $(TTY)termcap.c $(TTY)topl.c $(TTY)wintty.c \ + $(AMI)amitty.c $(NHS)rip.c + +# All the object files for NetHack: + +HOBJ = $(COMMOBJ) $(AMIGAOBJ) $(AMIGAOBJ2) $(SHAREOBJ) $(MAKEDEFOBJ) $(TTYOBJ) + +### +### DATA FILES +### + +# quest files +ADFILES1= $(SLIB)Arc-fila.lev $(SLIB)Arc-filb.lev $(SLIB)Arc-loca.lev \ + $(SLIB)Arc-strt.lev +ADFILES= $(SLIB)Arc-goal.lev $(ADFILES1) + +BDFILES1= $(SLIB)Bar-fila.lev $(SLIB)Bar-filb.lev $(SLIB)Bar-loca.lev \ + $(SLIB)Bar-strt.lev +BDFILES= $(SLIB)Bar-goal.lev $(BDFILES1) + +CDFILES1= $(SLIB)Cav-fila.lev $(SLIB)Cav-filb.lev $(SLIB)Cav-loca.lev \ + $(SLIB)Cav-strt.lev +CDFILES= $(SLIB)Cav-goal.lev $(CDFILES1) + +HDFILES1= $(SLIB)Hea-fila.lev $(SLIB)Hea-filb.lev $(SLIB)Hea-loca.lev \ + $(SLIB)Hea-strt.lev +HDFILES= $(SLIB)Hea-goal.lev $(HDFILES1) + +KDFILES1= $(SLIB)Kni-fila.lev $(SLIB)Kni-filb.lev $(SLIB)Kni-loca.lev \ + $(SLIB)Kni-strt.lev +KDFILES= $(SLIB)Kni-goal.lev $(KDFILES1) + +MDFILES1= $(SLIB)Mon-fila.lev $(SLIB)Mon-filb.lev $(SLIB)Mon-loca.lev \ + $(SLIB)Mon-strt.lev +MDFILES= $(SLIB)Mon-goal.lev $(MDFILES1) + +PDFILES1= $(SLIB)Pri-fila.lev $(SLIB)Pri-filb.lev $(SLIB)Pri-loca.lev \ + $(SLIB)Pri-strt.lev +PDFILES= $(SLIB)Pri-goal.lev $(PDFILES1) + +RDFILES1= $(SLIB)Rog-fila.lev $(SLIB)Rog-filb.lev $(SLIB)Rog-loca.lev \ + $(SLIB)Rog-strt.lev +RDFILES= $(SLIB)Rog-goal.lev $(RDFILES1) + +RANFILES1= $(SLIB)Ran-fila.lev $(SLIB)Ran-filb.lev $(SLIB)Ran-loca.lev \ + $(SLIB)Ran-strt.lev +RANFILES= $(SLIB)Ran-goal.lev $(RANFILES1) + +SDFILES1= $(SLIB)Sam-fila.lev $(SLIB)Sam-filb.lev $(SLIB)Sam-loca.lev \ + $(SLIB)Sam-strt.lev +SDFILES= $(SLIB)Sam-goal.lev $(SDFILES1) + +TDFILES1= $(SLIB)Tou-fila.lev $(SLIB)Tou-filb.lev $(SLIB)Tou-loca.lev \ + $(SLIB)Tou-strt.lev +TDFILES= $(SLIB)Tou-goal.lev $(TDFILES1) + +VDFILES1= $(SLIB)Val-fila.lev $(SLIB)Val-filb.lev $(SLIB)Val-loca.lev \ + $(SLIB)Val-strt.lev +VDFILES= $(SLIB)Val-goal.lev $(VDFILES1) + +WDFILES1= $(SLIB)Wiz-fila.lev $(SLIB)Wiz-filb.lev $(SLIB)Wiz-loca.lev \ + $(SLIB)Wiz-strt.lev +WDFILES= $(SLIB)Wiz-goal.lev $(WDFILES1) + +XDFILES= $(ADFILES) $(BDFILES) $(CDFILES) $(HDFILES) $(KDFILES) \ + $(MDFILES) $(PDFILES) $(RDFILES) $(RANFILES) $(SDFILES) $(TDFILES) \ + $(VDFILES) $(WDFILES) + +SOUNDFILES= \ + $(SBIN)cvtsnd \ + $(SLIB)sounds \ + $(SLIB)sounds/Bell $(SLIB)sounds/Bugle \ + $(SLIB)sounds/Drum_Of_Earthquake \ + $(SLIB)sounds/Fire_Horn $(SLIB)sounds/Frost_Horn \ + $(SLIB)sounds/Leather_Drum $(SLIB)sounds/Magic_Flute \ + $(SLIB)sounds/Magic_Harp $(SLIB)sounds/Tooled_Horn \ + $(SLIB)sounds/Wooden_Flute $(SLIB)sounds/Wooden_Harp + +TILEFILES= \ + $(SBIN)txt2iff \ + $(NETHACK)tiles \ + $(NETHACK)tiles/objects.iff \ + $(NETHACK)tiles/monsters.iff \ + $(NETHACK)tiles/other.iff + +INSTDUNGEONFILES1= \ + $(SLIB)air.lev $(SLIB)asmodeus.lev $(SLIB)astral.lev \ + $(SLIB)baalz.lev $(SLIB)bigrm-1.lev $(SLIB)bigrm-2.lev \ + $(SLIB)bigrm-3.lev $(SLIB)bigrm-4.lev $(SLIB)bigrm-5.lev \ + $(SLIB)castle.lev $(SLIB)dungeon $(SLIB)earth.lev \ + $(SLIB)fakewiz1.lev $(SLIB)fakewiz2.lev $(SLIB)fire.lev \ + $(SLIB)juiblex.lev $(SLIB)knox.lev $(SLIB)medusa-1.lev \ + $(SLIB)medusa-2.lev $(SLIB)minend-1.lev $(SLIB)minend-2.lev \ + $(SLIB)minetn-1.lev $(SLIB)minetn-2.lev $(SLIB)minefill.lev \ + $(SLIB)options $(SLIB)oracle.lev $(SLIB)orcus.lev \ + $(SLIB)sanctum.lev $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev \ + $(SLIB)soko2-1.lev $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev \ + $(SLIB)soko3-2.lev $(SLIB)soko4-1.lev $(SLIB)soko4-2.lev \ + $(SLIB)tower1.lev $(SLIB)tower2.lev $(SLIB)tower3.lev \ + $(SLIB)valley.lev $(SLIB)water.lev $(SLIB)wizard1.lev \ + $(SLIB)wizard2.lev $(SLIB)wizard3.lev \ + $(XDFILES) + +INSTDUNGEONFILES= $(NETHACK)NetHack.cnf $(INSTDUNGEONFILES1) + + +INSTDATAFILES= \ + $(NETHACK)license $(NETHACK)logfile $(NETHACK)record \ + $(NETHACK)tomb.iff $(NETHACK)amii.hlp $(NETHACK)Recover.txt \ + $(NETHACK)GuideBook.txt $(NETHACK)NetHack.txt $(NETHACK)Install.ami \ +# $(NETHACK)HackWB.hlp $(NETHACK)WBDefaults.def + +LIBFILES= \ + $(INSTDUNGEONFILES1) \ + $(SLIB)cmdhelp $(SLIB)data $(SLIB)dungeon \ + $(SLIB)help $(SLIB)hh $(SLIB)history \ + $(SLIB)opthelp $(SLIB)oracles $(SLIB)rumors \ + $(SLIB)quest.dat $(SLIB)wizhelp + +### +### Getting down to business: +### + +all: $(COMPACT_HEADERS) $(SBIN)lev_comp $(SBIN)dgn_comp $(SBIN)NetHack \ + $(SBIN)dlb $(NETHACK)recover #$(NETHACK)HackCli $(SBIN)splitter \ +# $(NETHACK)HackWB + +install: inst-data inst-dungeon inst-fonts inst-sounds inst-tiles \ + $(NETHACK)recover $(NETHACK)NetHack $(NETHACK)nhdat + #$(NETHACK)NetHack.dir inst-icons + +$(SBIN)NetHack: link + +$(NETHACK)NetHack: $(SBIN)NetHack + copy $(SBIN)NetHack $(NETHACK)NetHack + +## uuh this is messy.. smake has weird command line length limit +link: $(HOBJ) + list to t:link lformat="$(O)%s" $(O)\#?.o QUICK NOHEAD + echo "\#sh" to t:cc + echo "$(LINK) $(LNSPEC) $(SBIN)NetHack $(LIN) $(LLIB) $(LLINK) " >>t:cc noline + fmt -u -w 2500 t:link >>t:cc + sh t:cc + delete t:cc t:link + + +## dlb support +$(OO)dlb_main.o: $(UTIL)dlb_main.c $(HDEP) $(I)dlb.h $(I)date.h + $(CC) $(CFLAGS) $(OBJSPEC)$(OO)dlb_main.o $(UTIL)dlb_main.c + +$(SBIN)dlb: $(OO)dlb_main.o $(O)dlb.o $(O)alloc.o $(OO)panic.o + $(LINK) $(PNSPEC) $(SBIN)dlb $(LIN) $(OO)dlb_main.o $(O)dlb.o \ + $(O)alloc.o $(OO)panic.o $(LLIB) + +obj: $(HOBJ) + +obja: $(AMIGAOBJ) + +objs: $(SHAREOBJ) + + +SUFFIXES = .lev .des +.des.lev: + $(SBIN)lev_comp $< + +# The default method for creating object files: + +#$(O)%.o: $(NHS)%.c +.c.o: + $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)$@ $< + +clean: + -delete $(O)\#?.o $(OO)\#?.o + +spotless: clean + -delete $(SBIN)NetHack $(SBIN)lev_comp $(SBIN)makedefs $(SBIN)dgn_comp + -delete $(SBIN)cvtsnd $(SBIN)dlb $(SBIN)txt2iff $(SBIN)splitter + -delete $(SBIN)tilemap + -delete $(SLIB)data $(SLIB)rumors + -delete $(SLIB)\#?.lev + -delete $(SLIB)dungeon + -delete $(SLIB)cmdhelp $(SLIB)help $(SLIB)hh $(SLIB)history + -delete $(SLIB)opthelp $(SLIB)options $(SLIB)oracles + -delete $(SLIB)quest.dat $(SLIB)wizhelp +# -delete $(SLIB)earth.lev $(SLIB)air.lev $(SLIB)fire.lev +# -delete $(SLIB)water.lev $(SLIB)astral.lev +# -delete $(SLIB)tower1.lev $(SLIB)tower2.lev $(SLIB)tower3.lev +# -delete $(SLIB)fakewiz1.lev $(SLIB)fakewiz2.lev +# -delete $(SLIB)medusa-1.lev $(SLIB)medusa-2.lev +# -delete $(SLIB)oracle.lev $(SLIB)wizard1.lev $(SLIB)wizard2.lev +# -delete $(SLIB)wizard3.lev $(DAT)dungeon.pdf $(SLIB)valley.lev +# -delete $(SLIB)minefill.lev +# -delete $(SLIB)minetn-1 $(SLIB)minetn-2 $(SLIB)minend-1 $(SLIB)minend-2 +# -delete $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev $(SLIB)soko2-1.lev +# -delete $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev $(SLIB)soko3-2.lev +# -delete $(SLIB)soko4-1.lev $(SLIB)soko4-2.lev +# -delete $(ADFILES) +# -delete $(BDFILES) +# -delete $(CDFILES) +# -delete $(HDFILES) +# -delete $(KDFILES) +# -delete $(MDFILES) +# -delete $(PDFILES) +# -delete $(RDFILES) +# -delete $(RANFILES) +# -delete $(SDFILES) +# -delete $(TDFILES) +# -delete $(VDFILES) +# -delete $(WDFILES) + -delete $(I)onames.h $(I)pm.h $(I)date.h + -delete $(NHS)tile.c $(NHS)monstr.c + -delete $(I)tile.h +# -echo to $(I)onames.h "" noline +# -c:wait 2 +# -echo to $(I)pm.h "" noline +# -c:wait 2 +# -setdate $(UTIL)makedefs.c +# -c:wait 2 + +# Creating precompiled version of $(I)hack.h to save disk I/O. + +# +# Please note: The dependency lines for the modules here are +# deliberately incorrect. Including "hack.h" in +# the dependency list would cause a dependency +# loop. +# + +$(SBIN)makedefs: $(MAKEOBJS) + $(LINK) $(LNSPEC) $(SBIN)makedefs $(LIN) $(MAKEOBJS) $(LLIB) + +$(OO)makedefs.o: $(UTIL)makedefs.c $(I)config.h $(I)permonst.h $(I)monsym.h \ + $(I)objclass.h $(I)patchlevel.h $(I)qtext.h $(I)artilist.h + $(CC) $(DEFSPEC)MAKEDEFS_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)makedefs.c + +$(SBIN)lev_comp: $(SPLEVOBJS) + $(LINK) $(LNSPEC) $(SBIN)lev_comp $(LIN) $(SPLEVOBJS) $(FBFIL) $(FLLIB) + +$(SBIN)dgn_comp: $(DGNCOMPOBJS) + $(LINK) $(LNSPEC) $(SBIN)dgn_comp $(LIN) $(DGNCOMPOBJS) $(FBFIL) $(FLLIB) + +$(OO)lev_yacc.o: $(UTIL)lev_yacc.c $(HDEP) $(I)sp_lev.h $(I)pm.h $(I)onames.h +# setdate $(UTIL)lev_yacc.c + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)PREFIX="NH:slib/" $(CFLAGS) \ + $(DEFSPEC)alloca=malloc $(OBJSPEC)$@ $(UTIL)lev_yacc.c + +$(OO)lev_lex.o: $(UTIL)lev_lex.c $(HDEP) $(I)lev_comp.h $(I)sp_lev.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)lev_lex.c + +$(OO)lev_main.o: $(UTIL)lev_main.c $(HDEP) $(I)pm.h $(I)onames.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)lev_main.c + +$(OO)dgn_yacc.o: $(UTIL)dgn_yacc.c $(HDEP) $(I)dgn_file.h $(I)patchlevel.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(DEFSPEC)alloca=malloc \ + $(OBJSPEC)$@ $(UTIL)dgn_yacc.c + +$(OO)dgn_lex.o: $(UTIL)dgn_lex.c $(I)config.h $(I)dgn_comp.h $(I)dgn_file.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)dgn_lex.c + +$(OO)dgn_main.o: $(UTIL)dgn_main.c $(I)config.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)dgn_main.c + +$(OO)panic.o: $(UTIL)panic.c $(HDEP) + +$(OO)recover.o: $(UTIL)recover.c $(I)config.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)recover.c + +$(NETHACK)recover: $(OO)recover.o + $(LINK) $(LNSPEC) $(NETHACK)recover $(LIN) $(OO)recover.o $(LLIB) + +# [OPTION] -- If you have flex/bison, leave these uncommented. Otherwise, +# comment them out and be careful! (You're not guaranteed to have the most +# up to date *_comp.c, *_comp.h and *_lex.c) + +$(I)lev_comp.h: $(UTIL)lev_yacc.c $(I)patchlevel.h + +$(UTIL)lev_yacc.c: $(UTIL)lev_comp.y $(I)patchlevel.h + $(BISON) -d $(UTIL)lev_comp.y +# copy y.tab.c $(UTIL)lev_yacc.c +# copy y.tab.h $(I)lev_comp.h + copy $(UTIL)lev_comp.tab.c $(UTIL)lev_yacc.c + copy $(UTIL)lev_comp.tab.h $(I)lev_comp.h +# delete y.tab.c +# delete y.tab.h + delete $(UTIL)lev_comp.tab.c + delete $(UTIL)lev_comp.tab.h + +$(UTIL)lev_lex.c: $(UTIL)lev_comp.l $(I)patchlevel.h + $(FLEX) $(UTIL)lev_comp.l + copy lex.yy.c $(UTIL)lev_lex.c + delete lex.yy.c + +$(I)dgn_comp.h: $(UTIL)dgn_yacc.c $(I)patchlevel.h + +$(UTIL)dgn_yacc.c: $(UTIL)dgn_comp.y $(I)patchlevel.h + $(BISON) -d $(UTIL)dgn_comp.y +# copy y.tab.c $(UTIL)dgn_yacc.c +# copy y.tab.h $(I)dgn_comp.h + copy $(UTIL)dgn_comp.tab.c $(UTIL)dgn_yacc.c + copy $(UTIL)dgn_comp.tab.h $(I)dgn_comp.h +# delete y.tab.c +# delete y.tab.h + delete $(UTIL)dgn_comp.tab.c + delete $(UTIL)dgn_comp.tab.h + +$(UTIL)dgn_lex.c: $(UTIL)dgn_comp.l $(I)patchlevel.h + $(FLEX) $(UTIL)dgn_comp.l + copy lex.yy.c $(UTIL)dgn_lex.c + delete lex.yy.c + +# +# The following include files depend on makedefs to be created. +# As a result, they are not defined in HACKINCL, instead, their +# dependencies are explicitly outlined here. +# + +# +# date.h should be remade any time any of the source or include code +# is modified. Unfortunately, this would make the contents of this +# file far more complex. Since "hack.h" depends on most of the include +# files, we kludge around this by making date.h dependent on hack.h, +# even though it doesn't include this file. +# + +$(I)date.h $(DAT)options: $(HDEP) $(SBIN)makedefs $(AMIGAOBJ) $(I)patchlevel.h + $(SBIN)makedefs -v + $(EXECUTE) ifchange MOVE $(I)t.date.h $(I)date.h + -c:wait 2 + +$(I)onames.h: $(SBIN)makedefs + $(SBIN)makedefs -o + $(EXECUTE) ifchange TOUCH $(I)t.onames.h $(I)onames.h $(I)decl.h + $(EXECUTE) ifchange MOVE $(I)t.onames.h $(I)onames.h + -c:wait 2 + +$(I)pm.h: $(SBIN)makedefs + $(SBIN)makedefs -p + $(EXECUTE) ifchange TOUCH $(I)t.pm.h $(I)pm.h $(I)decl.h $(I)youprop.h + $(EXECUTE) ifchange MOVE $(I)t.pm.h $(I)pm.h + -c:wait 2 + +$(SLIB)quest.dat: $(DAT)quest.txt $(SBIN)makedefs + $(SBIN)makedefs -q + +$(NHS)monstr.c: $(HDEP) $(SBIN)makedefs + $(SBIN)makedefs -m + -c:wait 2 + +$(SLIB)oracles: $(DAT)oracles.txt $(SBIN)makedefs + $(SBIN)makedefs -h + -c:wait 2 + +# +# The following programs vary depending on what OS you are using. +# As a result, they are not defined in HACKSRC and their dependencies +# are explicitly outlined here. +# + +$(O)amidos.o: $(AMI)amidos.c $(HDEP) + +$(O)amirip.o: $(AMI)amirip.c $(HDEP) + +$(O)aglue.o: $(AMI)aglue.a + $(ASM) $(AFLAGS) $(AOBJSPEC)$(O)aglue.o $(AMI)aglue.a + +$(O)amisnd.o: $(AMI)amisnd.c $(HDEP) + +$(O)winchar.o: $(AMI)winchar.c $(NHS)tile.c $(HDEP) + +$(NHS)tile.c: $(WSHARE)tilemap.c + $(CCLINK) $(CFLAGS) $(PNSPEC) $(SBIN)tilemap $(WSHARE)tilemap.c + $(SBIN)tilemap + +$(O)winstr.o: $(AMI)winstr.c $(HDEP) $(AMDEP) + +$(O)winreq.o: $(AMI)winreq.c $(HDEP) $(AMDEP) $(AMI)colorwin.c $(AMI)clipwin.c + +$(O)winfuncs.o: $(AMI)winfuncs.c $(HDEP) $(AMDEP) $(I)patchlevel.h + +$(O)winkey.o: $(AMI)winkey.c $(HDEP) $(AMDEP) + +$(O)winmenu.o: $(AMI)winmenu.c $(HDEP) $(AMDEP) + +$(O)winami.o: $(AMI)winami.c $(HDEP) $(AMDEP) #$(AMI)char.c $(AMI)randwin.c + +#$(O)amilib.o: $(AMI)amilib.c $(HDEP) $(AMDEP) + +$(O)amiwind.o: $(AMI)amiwind.c $(AMI)amimenu.c $(HDEP) $(AMDEP) + +$(O)amiwbench.o: $(AMI)amiwbench.c $(HDEP) + +$(O)random.o: $(SHARE)random.c + +$(O)pcmain.o: $(SHARE)pcmain.c $(HDEP) $(I)dlb.h + +$(O)dispmap.o: $(AMI)dispmap.s + $(ASM) $(AFLAGS) $(AOBJSPEC)$@ $< + +# Stuff to build the front ends +$(NETHACK)HackWB: $(OO)wb.o $(OO)wbx.o $(OO)loader.o $(OO)multi.o + $(LINK) $(LNSPEC) $(NETHACK)HackWB $(LIN) $(OO)wb.o $(OO)wbx.o \ + $(OO)loader.o $(OO)multi.o $(LLIB) + +$(NETHACK)HackCli: $(OO)cli.o $(OO)loader.o $(OO)multi.o + $(LINK) $(LNSPEC) $(NETHACK)HackCli $(LIN) $(OO)cli.o $(OO)loader.o \ + $(OO)multi.o $(LLIB) + +# This needs to exist to eliminate the HackWB startup message +$(NETHACK)WBDefaults.def: + echo to $(NETHACK)WBDefaults.def + +WBH = $(AMI)wbdefs.h $(AMI)wbstruct.h $(AMI)wbprotos.h +ASP = $(AMI)splitter +$(OO)wb.o: $(WBH) $(AMI)wb.c $(AMI)wbwin.c $(AMI)wbdata.c $(AMI)wbgads.c \ + $(I)patchlevel.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)wb.o $(AMI)wb.c + +$(OO)wbx.o: $(WBH) $(AMI)wbcli.c $(AMI)wbwin.c $(AMI)wbdata.c \ + $(I)patchlevel.h $(I)date.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)wbx.o $(AMI)wbcli.c + +$(OO)loader.o: $(ASP)/loader.c $(ASP)/split.h $(ASP)/amiout.h $(ASP)/multi.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)loader.o $(ASP)/loader.c + +$(OO)multi.o: $(ASP)/multi.c $(ASP)/multi.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)multi.o $(ASP)/multi.c + +$(OO)cli.o: $(WBH) $(AMI)wbcli.c $(I)patchlevel.h $(I)date.h + $(CC) $(WBCFLAGS) $(WBC2FLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)cli.o \ + $(AMI)wbcli.c + +#### +# splitter support +$(SBIN)splitter: $(OO)splitter.o $(OO)arg.o + $(LINK) $(LNSPEC) $(SBIN)splitter $(LIN) $(OO)splitter.o $(OO)arg.o \ + $(LLIB) + +$(NETHACK)NetHack.dir: $(SBIN)splitter $(SBIN)NetHack + $(SBIN)splitter $(SBIN)NetHack + +$(OO)splitter.o: $(ASP)/splitter.c $(ASP)/split.h $(ASP)/amiout.h $(ASP)/arg.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)splitter.o \ + $(ASP)/splitter.c + +$(OO)arg.o: $(ASP)/arg.c $(ASP)/arg.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)arg.o $(ASP)/arg.c + +# Create/copy other stuff into NetHack: directory: + +$(NETHACK)tomb.iff: $(SBIN)xpm2iff $(AMI)grave16.xpm + $(SBIN)xpm2iff $(AMI)grave16.xpm $(NETHACK)tomb.iff + +$(OO)xpm2iff.o: $(AMI)xpm2iff.c + $(CC) $(CFLAGS) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(AMI)xpm2iff.c + +$(SBIN)xpm2iff: $(OO)xpm2iff.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)xpm2iff.o $(FLLIB) + +# Tile installation for the tile version of the game +inst-tiles: $(TILEFILES) + +$(NETHACK)tiles: + -makedir $(NETHACK)tiles + +$(OO)txt2iff.o: $(AMI)txt2iff.c + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ \ + $(AMI)txt2iff.c + +$(OO)ppmwrite.o: $(WSHARE)ppmwrite.c + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)ppmwrite.c + +$(OO)tiletext.o: $(WSHARE)tiletext.c $(I)config.h $(WSHARE)tile.h + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)tiletext.c + +$(OO)tiletxt.o: $(WSHARE)tilemap.c $(I)hack.h + $(CC) $(CFLAGS) $(CSYM) $(DEFSPEC)TILETEXT $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)tilemap.c + +NAMEOBJS = $(O)drawing.o $(O)decl.o $(O)monst.o $(O)objects.o + +$(SBIN)txt2ppm: $(OO)ppmwrite.o $(NAMEOBJS) $(O)alloc.o $(OO)panic.o $(OO)tiletext.o $(OO)tiletxt.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)ppmwrite.o $(NAMEOBJS) $(OO)tiletext.o $(OO)tiletxt.o $(O)alloc.o $(OO)panic.o $(FLLIB) + +$(SBIN)txt2iff: $(OO)txt2iff.o $(NAMEOBJS) $(OO)tiletext.o $(OO)tiletxt.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)txt2iff.o $(NAMEOBJS) $(OO)tiletext.o \ + $(OO)tiletxt.o $(FLLIB) + +$(NETHACK)tiles/objects.iff: $(WSHARE)objects.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)objects.txt $(NETHACK)tiles/objects.iff + +$(NETHACK)tiles/monsters.iff: $(WSHARE)monsters.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)monsters.txt $(NETHACK)tiles/monsters.iff + +$(NETHACK)tiles/other.iff: $(WSHARE)other.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)other.txt $(NETHACK)tiles/other.iff + +# Sound installation rules. +inst-sounds: $(SOUNDFILES) + list to T:nhsdat.lst $(SLIB)sounds QUICK NOHEAD + echo >T:make-nhsdat $(SBIN)dlb cCfI $(SLIB)sounds $(NETHACK)nhsdat T:nhsdat.lst + echo >>T:make-nhsdat if not exists $(NETHACK)nhsdat + echo >>T:make-nhsdat copy $(SLIB)sounds/\#? $(NETHACK)sounds + echo >>T:make-nhsdat endif + execute T:make-nhsdat + -delete T:make-nhsdat + +$(SLIB)sounds: + -makedir $(SLIB)sounds + +$(SBIN)cvtsnd: $(OO)cvtsnd.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)cvtsnd.o $(FLLIB) + +$(OO)cvtsnd.o: $(AMI)cvtsnd.c + +$(SLIB)sounds/Bell: $(SHARE)sounds/bell.uu + $(UUDEC) $(SHARE)sounds/bell.uu + $(SBIN)cvtsnd Bell $(SLIB)sounds/Bell + -delete Bell + +$(SLIB)sounds/Bugle: $(SHARE)sounds/bugle.uu + $(UUDEC) $(SHARE)sounds/bugle.uu + $(SBIN)cvtsnd Bugle $(SLIB)sounds/Bugle + -delete Bugle + +$(SLIB)sounds/Drum_Of_Earthquake: $(SHARE)sounds/erthdrum.uu + $(UUDEC) $(SHARE)sounds/erthdrum.uu + $(SBIN)cvtsnd Drum_Of_Earthquake $(SLIB)sounds/Drum_Of_Earthquake + -delete Drum_Of_Earthquake + +$(SLIB)sounds/Fire_Horn: $(SHARE)sounds/firehorn.uu + $(UUDEC) $(SHARE)sounds/firehorn.uu + $(SBIN)cvtsnd Fire_Horn $(SLIB)sounds/Fire_Horn + -delete Fire_Horn + +$(SLIB)sounds/Frost_Horn: $(SHARE)sounds/frsthorn.uu + $(UUDEC) $(SHARE)sounds/frsthorn.uu + $(SBIN)cvtsnd Frost_Horn $(SLIB)sounds/Frost_Horn + -delete Frost_Horn + +$(SLIB)sounds/Leather_Drum: $(SHARE)sounds/lethdrum.uu + $(UUDEC) $(SHARE)sounds/lethdrum.uu + $(SBIN)cvtsnd Leather_Drum $(SLIB)sounds/Leather_Drum + -delete Leather_Drum + +$(SLIB)sounds/Magic_Flute: $(SHARE)sounds/mgcflute.uu + $(UUDEC) $(SHARE)sounds/mgcflute.uu + $(SBIN)cvtsnd Magic_Flute $(SLIB)sounds/Magic_Flute + -delete Magic_Flute + +$(SLIB)sounds/Magic_Harp: $(SHARE)sounds/mgcharp.uu + $(UUDEC) $(SHARE)sounds/mgcharp.uu + $(SBIN)cvtsnd Magic_Harp $(SLIB)sounds/Magic_Harp + -delete Magic_Harp + +$(SLIB)sounds/Tooled_Horn: $(SHARE)sounds/toolhorn.uu + $(UUDEC) $(SHARE)sounds/toolhorn.uu + $(SBIN)cvtsnd Tooled_Horn $(SLIB)sounds/Tooled_Horn + -delete Tooled_Horn + +$(SLIB)sounds/Wooden_Flute: $(SHARE)sounds/wdnflute.uu + $(UUDEC) $(SHARE)sounds/wdnflute.uu + $(SBIN)cvtsnd Wooden_Flute $(SLIB)sounds/Wooden_Flute + -delete Wooden_Flute + +$(SLIB)sounds/Wooden_Harp: $(SHARE)sounds/wdnharp.uu + $(UUDEC) $(SHARE)sounds/wdnharp.uu + $(SBIN)cvtsnd Wooden_Harp $(SLIB)sounds/Wooden_Harp + -delete Wooden_Harp + +inst-dungeon: $(INSTDUNGEONFILES) + +$(NETHACK)options : $(DAT)options + copy $(DAT)options $@ + +# Create compiled dungeon files +BGM= $(SLIB)bigrm-2.lev $(SLIB)bigrm-3.lev $(SLIB)bigrm-4.lev $(SLIB)bigrm-5.lev +$(BGM): $(SLIB)bigrm-1.lev + +$(SLIB)bigrm-1.lev: $(DAT)bigroom.des $(SBIN)lev_comp + +$(SLIB)castle.lev: $(DAT)castle.des $(SBIN)lev_comp + +ENDGAME1= $(SLIB)air.lev $(SLIB)earth.lev $(SLIB)fire.lev $(SLIB)water.lev +$(ENDGAME1): $(SLIB)astral.lev + +$(SLIB)astral.lev: $(DAT)endgame.des $(SBIN)lev_comp + +GEHENNOM1= $(SLIB)asmodeus.lev $(SLIB)baalz.lev $(SLIB)juiblex.lev \ + $(SLIB)orcus.lev $(SLIB)sanctum.lev +$(GEHENNOM1): $(SLIB)valley.lev + +$(SLIB)valley.lev: $(DAT)gehennom.des $(SBIN)lev_comp + +$(SLIB)knox.lev: $(DAT)knox.des $(SBIN)lev_comp + +MINES1= $(SLIB)minend-1.lev $(SLIB)minend-2.lev $(SLIB)minetn-1.lev $(SLIB)minetn-2.lev +$(MINES1): $(SLIB)minefill.lev + +$(SLIB)minefill.lev: $(DAT)mines.des $(SBIN)lev_comp + +$(SLIB)oracle.lev: $(DAT)oracle.des $(SBIN)lev_comp + +TOWER1= $(SLIB)tower1.lev $(SLIB)tower2.lev +$(TOWER1): $(SLIB)tower3.lev + +$(SLIB)tower3.lev: $(DAT)tower.des $(SBIN)lev_comp + +WIZARD1= $(SLIB)wizard1.lev $(SLIB)wizard2.lev $(SLIB)wizard3.lev \ + $(SLIB)fakewiz1.lev +$(WIZARD1): $(SLIB)fakewiz2.lev + +$(SLIB)fakewiz2.lev: $(DAT)yendor.des $(SBIN)lev_comp + +MEDUSA1= $(SLIB)medusa-1.lev +$(MEDUSA1): $(SLIB)medusa-2.lev + +$(SLIB)medusa-2.lev: $(DAT)medusa.des $(SBIN)lev_comp + +SOKOBAN1= $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev $(SLIB)soko2-1.lev \ + $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev $(SLIB)soko3-2.lev \ + $(SLIB)soko4-1.lev +$(SOKOBAN1): $(SLIB)soko4-2.lev + +$(SLIB)soko4-2.lev: $(DAT)sokoban.des $(SBIN)lev_comp + +$(ADFILES1): $(SLIB)Arc-goal.lev + +$(SLIB)Arc-goal.lev: $(DAT)Arch.des $(SBIN)lev_comp + +$(BDFILES1): $(SLIB)Bar-goal.lev + +$(SLIB)Bar-goal.lev: $(DAT)Barb.des $(SBIN)lev_comp + +$(CDFILES1): $(SLIB)Cav-goal.lev + +$(SLIB)Cav-goal.lev: $(DAT)Caveman.des $(SBIN)lev_comp + +$(HDFILES1): $(SLIB)Hea-goal.lev + +$(SLIB)Hea-goal.lev: $(DAT)Healer.des $(SBIN)lev_comp + +$(KDFILES1): $(SLIB)Kni-goal.lev + +$(SLIB)Kni-goal.lev: $(DAT)Knight.des $(SBIN)lev_comp + +$(MDFILES1): $(SLIB)Mon-goal.lev + +$(SLIB)Mon-goal.lev: $(DAT)Monk.des $(SBIN)lev_comp + +$(PDFILES1): $(SLIB)Pri-goal.lev + +$(SLIB)Pri-goal.lev: $(DAT)Priest.des $(SBIN)lev_comp + +$(RDFILES1): $(SLIB)Rog-goal.lev + +$(SLIB)Rog-goal.lev: $(DAT)Rogue.des $(SBIN)lev_comp + +$(RANFILES1): $(SLIB)Ran-goal.lev + +$(SLIB)Ran-goal.lev: $(DAT)Ranger.des $(SBIN)lev_comp + +$(SDFILES1): $(SLIB)Sam-goal.lev + +$(SLIB)Sam-goal.lev: $(DAT)Samurai.des $(SBIN)lev_comp + +$(TDFILES1): $(SLIB)Tou-goal.lev + +$(SLIB)Tou-goal.lev: $(DAT)Tourist.des $(SBIN)lev_comp + +$(VDFILES1): $(SLIB)Val-goal.lev + +$(SLIB)Val-goal.lev: $(DAT)Valkyrie.des $(SBIN)lev_comp + +$(WDFILES1): $(SLIB)Wiz-goal.lev + +$(SLIB)Wiz-goal.lev: $(DAT)Wizard.des $(SBIN)lev_comp + +$(SLIB)dungeon: $(DAT)dungeon.def $(SBIN)makedefs $(SBIN)dgn_comp + $(SBIN)makedefs -e + $(SBIN)dgn_comp $(DAT)dungeon.pdf + copy $(DAT)dungeon $(SLIB)dungeon + delete $(DAT)dungeon + +inst-data: $(INSTDATAFILES) + +$(NETHACK)amii.hlp: $(AMI)amii.hlp + copy $(AMI)amii.hlp $@ + +#$(NETHACK)data: $(DAT)data +# copy $(DAT)data $@ + +$(SLIB)data: $(DAT)data.base $(I)config.h $(SBIN)makedefs + $(SBIN)makedefs -d + +#$(NETHACK)rumors: $(DAT)rumors +# copy $(DAT)rumors $@ + +$(SLIB)rumors: $(DAT)rumors.tru $(DAT)rumors.fal $(SBIN)makedefs + $(SBIN)makedefs -r + +$(SLIB)cmdhelp: $(DAT)cmdhelp + copy $(DAT)cmdhelp $@ + +$(SLIB)help: $(DAT)help + copy $(DAT)help $@ + +$(SLIB)hh: $(DAT)hh + copy $(DAT)hh $@ + +$(NETHACK)HackWB.hlp: $(AMI)HackWB.hlp + copy $(AMI)HackWB.hlp $@ + +$(SLIB)history: $(DAT)history + copy $(DAT)history $@ + +$(NETHACK)license: $(DAT)license + copy $(DAT)license $@ + +$(SLIB)opthelp: $(DAT)opthelp + copy $(DAT)opthelp $@ + +$(NETHACK)Recover.txt: $(DOC)Recover.txt + copy $(DOC)Recover.txt $@ + +$(NETHACK)GuideBook.txt: $(DOC)GuideBook.txt + copy $(DOC)GuideBook.txt $@ + +$(NETHACK)NetHack.txt: $(DOC)NetHack.txt + copy $(DOC)NetHack.txt $@ + +$(NETHACK)Install.ami: $(AMI)Install.ami + copy $(AMI)Install.ami $@ + +$(NETHACK)logfile: + echo to $@ + +$(NETHACK)record: + echo to $@ + +$(SLIB)wizhelp: $(DAT)wizhelp + copy $(DAT)wizhelp $@ + +# Create the directories here because NetHack.cnf puts them there by default +$(NETHACK)NetHack.cnf: $(AMI)NetHack.cnf + copy $(AMI)NetHack.cnf $@ + -makedir $(NETHACK)save + -makedir $(NETHACK)levels + +# Unpack and install fonts + +INSTFONTFILES= $(NETHACK)hack.font $(NETHACK)hack $(NETHACK)hack/8 + +inst-fonts: $(INSTFONTFILES) + +$(NETHACK)hack/8: $(AMI)amifont8.uu $(NETHACK)hack + $(UUDEC) $(AMI)amifont8.uu + copy 8 $(NETHACK)hack/8 + delete 8 + +$(NETHACK)hack.font: $(AMI)amifont.uu + $(UUDEC) $(AMI)amifont.uu + copy hack.font $(NETHACK)hack.font + delete hack.font + +$(NETHACK)hack: + -makedir $@ + +INSTICONFILES= \ + $(NETHACK)default.icon $(NETHACK)NetHack.info $(NETHACK)NewGame.info \ + $(NETHACK)HackWB.info + +inst-icons: $(INSTICONFILES) + +# Unpack the icons into place + +$(NETHACK)default.icon: $(AMI)dflticon.uu + $(UUDEC) $(AMI)dflticon.uu +# copy default.icon $(NETHACK)default.icon +# delete default.icon + +$(NETHACK)NetHack.info: $(AMI)NHinfo.uu + $(UUDEC) $(AMI)NHinfo.uu +# copy NetHack.info $(NETHACK)NetHack.info +# delete NetHack.info + +$(NETHACK)NewGame.info: $(AMI)NewGame.uu + $(UUDEC) $(AMI)NewGame.uu +# copy NewGame.info $(NETHACK)NewGame.info +# delete NewGame.info + +$(NETHACK)HackWB.info: $(AMI)HackWB.uu + $(UUDEC) $(AMI)HackWB.uu +# copy HackWB.info $(NETHACK)HackWB.info +# delete HackWB.info + +# If DLB is defined, create the nhdat library file in the playground +# directory. If not, move all the data files there. +$(NETHACK)nhdat: $(LIBFILES) + list to T:nhdat.lst $(SLIB) QUICK NOHEAD FILES + echo >T:make-nhdat $(SBIN)dlb cCfI $(SLIB) $(NETHACK)nhdat T:nhdat.lst + echo >>T:make-nhdat if not exists $(NETHACK)nhdat + echo >>T:make-nhdat copy $(SLIB)\#? $(NETHACK) + echo >>T:make-nhdat endif + execute T:make-nhdat + -delete T:make-nhdat + +# DO NOT DELETE THIS LINE + +$(O)allmain.o: $(NHS)allmain.c $(HDEP) + +$(O)alloc.o: $(NHS)alloc.c $(I)config.h + +$(O)apply.o: $(NHS)apply.c $(HDEP) $(I)edog.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)apply.c + +$(O)artifact.o: $(NHS)artifact.c $(HDEP) $(I)artifact.h $(I)artilist.h + +$(O)attrib.o: $(NHS)attrib.c $(HDEP) $(I)artifact.h + +$(O)ball.o: $(NHS)ball.c $(HDEP) + +$(O)bones.o: $(NHS)bones.c $(HDEP) $(I)lev.h + +$(O)botl.o: $(NHS)botl.c $(HDEP) + +$(O)cmd.o: $(NHS)cmd.c $(HDEP) $(I)func_tab.h + +$(O)dbridge.o: $(NHS)dbridge.c $(HDEP) + +$(O)decl.o: $(NHS)decl.c $(HDEP) $(I)quest.h + +$(O)detect.o: $(NHS)detect.c $(HDEP) $(I)artifact.h + +$(O)dig.o: $(NHS)dig.c $(HDEP) $(I)edog.h + +$(O)display.o: $(NHS)display.c $(HDEP) + +$(O)dlb.o: $(NHS)dlb.c $(HDEP) $(I)dlb.h + +$(O)do.o: $(NHS)do.c $(HDEP) $(I)lev.h + +$(O)do_name.o: $(NHS)do_name.c $(HDEP) + +$(O)do_wear.o: $(NHS)do_wear.c $(HDEP) + +$(O)dog.o: $(NHS)dog.c $(HDEP) $(I)edog.h + +$(O)dogmove.o: $(NHS)dogmove.c $(HDEP) $(I)mfndpos.h $(I)edog.h + +$(O)dokick.o: $(NHS)dokick.c $(HDEP) $(I)eshk.h + +$(O)dothrow.o: $(NHS)dothrow.c $(HDEP) + +$(O)drawing.o: $(NHS)drawing.c $(HDEP) $(I)tcap.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)drawing.c + +$(O)dungeon.o: $(NHS)dungeon.c $(HDEP) $(I)dgn_file.h $(I)dlb.h + +$(O)eat.o: $(NHS)eat.c $(HDEP) + +$(O)end.o: $(NHS)end.c $(HDEP) $(I)eshk.h $(I)dlb.h + +$(O)engrave.o: $(NHS)engrave.c $(HDEP) $(I)lev.h + +$(O)exper.o: $(NHS)exper.c $(HDEP) + +$(O)explode.o: $(NHS)explode.c $(HDEP) + +$(O)extralev.o: $(NHS)extralev.c $(HDEP) + +$(O)files.o: $(NHS)files.c $(HDEP) $(I)dlb.h $(I)date.h + +$(O)fountain.o: $(NHS)fountain.c $(HDEP) + +$(O)hack.o: $(NHS)hack.c $(HDEP) + +$(O)hacklib.o: $(NHS)hacklib.c $(HDEP) + +$(O)invent.o: $(NHS)invent.c $(HDEP) $(I)artifact.h + +$(O)light.o: $(NHS)light.c $(HDEP) $(I)lev.h + +$(O)lock.o: $(NHS)lock.c $(HDEP) + +$(O)mail.o: $(NHS)mail.c $(HDEP) $(I)mail.h + +$(O)makemon.o: $(NHS)makemon.c $(HDEP) $(I)epri.h $(I)emin.h $(I)edog.h + +$(O)mapglyph.o: $(NHS)mapglyph.c $(HDEP) + +$(O)mcastu.o: $(NHS)mcastu.c $(HDEP) + +$(O)mhitm.o: $(NHS)mhitm.c $(HDEP) $(I)artifact.h $(I)edog.h + +$(O)mhitu.o: $(NHS)mhitu.c $(HDEP) $(I)artifact.h $(I)edog.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)mhitu.c + +$(O)minion.o: $(NHS)minion.c $(HDEP) $(I)emin.h $(I)epri.h + +$(O)mklev.o: $(NHS)mklev.c $(HDEP) + +$(O)mkmap.o: $(NHS)mkmap.c $(HDEP) $(I)sp_lev.h + +$(O)mkmaze.o: $(NHS)mkmaze.c $(HDEP) $(I)sp_lev.h $(I)lev.h + +$(O)mkobj.o: $(NHS)mkobj.c $(HDEP) $(I)artifact.h $(I)prop.h + +$(O)mkroom.o: $(NHS)mkroom.c $(HDEP) + +$(O)mon.o: $(NHS)mon.c $(HDEP) $(I)mfndpos.h $(I)edog.h + +$(O)mondata.o: $(NHS)mondata.c $(HDEP) $(I)eshk.h $(I)epri.h + +$(O)monmove.o: $(NHS)monmove.c $(HDEP) $(I)mfndpos.h $(I)artifact.h + +$(O)monst.o: $(NHS)monst.c $(I)config.h $(I)permonst.h $(I)monsym.h \ + $(I)eshk.h $(I)vault.h $(I)epri.h $(I)color.h + +$(O)monstr.o: $(NHS)monstr.c $(HDEP) + +$(O)mplayer.o: $(NHS)mplayer.c $(HDEP) + +$(O)mthrowu.o: $(NHS)mthrowu.c $(HDEP) + +$(O)muse.o: $(NHS)muse.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)muse.c + +$(O)music.o: $(NHS)music.c $(HDEP) #interp.c + +$(O)o_init.o: $(NHS)o_init.c $(HDEP) $(I)lev.h + +$(O)objects.o: $(NHS)objects.c $(I)config.h $(I)obj.h $(I)objclass.h \ + $(I)prop.h $(I)skills.h $(I)color.h + $(CC) $(CFLAGS) $(INCLSPEC)$(NHS) $(OBJSPEC)$@ $(NHS)objects.c + +$(O)objnam.o: $(NHS)objnam.c $(HDEP) + +$(O)options.o: $(NHS)options.c $(HDEP) $(I)tcap.h $(I)config.h \ + $(I)objclass.h $(I)flag.h + +$(O)pager.o: $(NHS)pager.c $(HDEP) $(I)dlb.h + +$(O)pickup.o: $(NHS)pickup.c $(HDEP) + +$(O)pline.o: $(NHS)pline.c $(HDEP) $(I)epri.h + +$(O)polyself.o: $(NHS)polyself.c $(HDEP) + +$(O)potion.o: $(NHS)potion.c $(HDEP) + +$(O)pray.o: $(NHS)pray.c $(HDEP) $(I)epri.h + +$(O)priest.o: $(NHS)priest.c $(HDEP) $(I)mfndpos.h $(I)eshk.h $(I)epri.h \ + $(I)emin.h + +$(O)quest.o: $(NHS)quest.c $(HDEP) $(I)quest.h $(I)qtext.h + +$(O)questpgr.o: $(NHS)questpgr.c $(HDEP) $(I)qtext.h $(I)dlb.h + +$(O)read.o: $(NHS)read.c $(HDEP) + +$(O)rect.o: $(NHS)rect.c $(HDEP) + +$(O)region.o: $(NHS)region.c $(HDEP) + +$(O)restore.o: $(NHS)restore.c $(HDEP) $(I)lev.h $(I)tcap.h $(I)quest.h + +$(O)rnd.o: $(NHS)rnd.c $(HDEP) + +$(O)role.o: $(NHS)role.c $(HDEP) + +$(O)rumors.o: $(NHS)rumors.c $(HDEP) $(I)dlb.h + +$(O)save.o: $(NHS)save.c $(HDEP) $(I)lev.h $(I)quest.h + +$(O)shk.o: $(NHS)shk.c $(HDEP) $(I)eshk.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)shk.c + +$(O)shknam.o: $(NHS)shknam.c $(HDEP) $(I)eshk.h + +$(O)sit.o: $(NHS)sit.c $(HDEP) $(I)artifact.h + +$(O)sounds.o: $(NHS)sounds.c $(HDEP) $(I)edog.h + +$(O)sp_lev.o: $(NHS)sp_lev.c $(HDEP) $(I)sp_lev.h $(I)rect.h $(I)dlb.h + +$(O)spell.o: $(NHS)spell.c $(HDEP) + +$(O)steal.o: $(NHS)steal.c $(HDEP) + +$(O)steed.o: $(NHS)steed.c $(HDEP) + +$(O)teleport.o: $(NHS)teleport.c $(HDEP) + +$(O)timeout.o: $(NHS)timeout.c $(HDEP) $(I)lev.h + +$(O)topten.o: $(NHS)topten.c $(HDEP) $(I)dlb.h + +$(O)track.o: $(NHS)track.c $(HDEP) + +$(O)trap.o: $(NHS)trap.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)trap.c + +$(O)u_init.o: $(NHS)u_init.c $(HDEP) + +$(O)uhitm.o: $(NHS)uhitm.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)uhitm.c + +$(O)vault.o: $(NHS)vault.c $(HDEP) $(I)vault.h + +$(O)version.o: $(NHS)version.c $(HDEP) $(I)date.h $(I)patchlevel.h + +$(O)vision.o: $(NHS)vision.c $(HDEP) #$(I)vis_tab.h + +$(O)weapon.o: $(NHS)weapon.c $(HDEP) + +$(O)were.o: $(NHS)were.c $(HDEP) + +$(O)wield.o: $(NHS)wield.c $(HDEP) + +$(O)windows.o: $(NHS)windows.c $(HDEP) $(I)wintty.h + +$(O)wizard.o: $(NHS)wizard.c $(HDEP) $(I)qtext.h + +$(O)worm.o: $(NHS)worm.c $(HDEP) $(I)lev.h + +$(O)worn.o: $(NHS)worn.c $(HDEP) + +$(O)write.o: $(NHS)write.c $(HDEP) + +$(O)zap.o: $(NHS)zap.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)zap.c + +$(O)getline.o: $(TTY)getline.c $(HDEP) $(I)wintty.h + +$(O)termcap.o: $(TTY)termcap.c $(HDEP) $(I)wintty.h $(I)tcap.h + +$(O)topl.o: $(TTY)topl.c $(HDEP) $(I)wintty.h $(I)tcap.h + +$(O)wintty.o: $(TTY)wintty.c $(HDEP) $(I)wintty.h $(I)tcap.h \ + $(I)patchlevel.h + +$(O)amitty.o: $(AMI)amitty.c $(HDEP) + +$(O)amistack.o: $(AMI)amistack.c + $(CC) $(CFLAGS3) $(CSYM) $(OBJSPEC)$@ $(AMI)amistack.c + +$(O)rip.o: $(NHS)rip.c $(HDEP) + + +$(I)config.h: $(I)config1.h $(I)tradstdc.h $(I)global.h + -setdate $(I)config.h + -c:wait 2 + +# onames.h handled at onames.h target, pm.h + +$(I)decl.h: $(I)quest.h $(I)spell.h $(I)color.h $(I)obj.h $(I)you.h + -setdate $(I)decl.h + -c:wait 2 + +$(I)global.h: $(I)coord.h $(I)pcconf.h $(I)amiconf.h + -setdate $(I)global.h + -c:wait 2 + +$(I)hack.h: $(I)config.h $(I)trap.h $(I)decl.h $(I)dungeon.h $(I)monsym.h \ + $(I)mkroom.h $(I)objclass.h $(I)flag.h $(I)rm.h $(I)vision.h \ + $(I)display.h $(I)wintype.h $(I)engrave.h $(I)rect.h \ + $(I)region.h $(I)trampoli.h + -setdate $(I)hack.h + -c:wait 2 + +$(I)permonst.h: $(I)monattk.h $(I)monflag.h $(I)align.h + -setdate $(I)permonst.h + -c:wait 2 + +$(I)you.h: $(I)align.h $(I)attrib.h $(I)monst.h $(I)youprop.h $(I)skills.h + -setdate $(I)you.h + -c:wait 2 + +# pm.h handled at target + +$(I)youprop.h: $(I)prop.h $(I)permonst.h $(I)mondata.h + -setdate $(I)youprop.h + -c:wait 2 + +$(I)display.h: $(I)vision.h $(I)mondata.h + -setdate $(I)display.h + -c:wait 2 + +$(I)dungeon.h: $(I)align.h + -setdate $(I)dungeon.h + -c:wait 2 + +$(I)emin.h: $(I)dungeon.h + -setdate $(I)emin.h + -c:wait 2 + +$(I)epri.h: $(I)dungeon.h $(I)align.h + -setdate $(I)epri.h + -c:wait 2 + +$(I)eshk.h: $(I)dungeon.h + -setdate $(I)eshk.h + -c:wait 2 + +$(I)engrave.h: $(I)trampoli.h $(I)rect.h + -setdate $(I)engrave.h + -c:wait 2 + +$(I)mondata.h: $(I)align.h + -setdate $(I)mondata.h + -c:wait 2 + +$(I)monst.h: $(I)align.h + -setdate $(I)monst.h + -c:wait 2 + +$(I)pcconf.h: $(I)micro.h $(I)system.h + -setdate $(I)pcconf.h + -c:wait 2 + +$(I)rm.h: $(I)align.h + -setdate $(I)rm.h + -c:wait 2 + +$(I)vault.h: $(I)dungeon.h + -setdate $(I)vault.h + -c:wait 2 + +#notes +# install keeps doing re-install because it keeps rebuilding lev_comp??? +# fixed(?) - deleted setdate diff --git a/sys/amiga/Makefile.ami b/sys/amiga/Makefile.ami new file mode 100644 index 0000000..5e334a9 --- /dev/null +++ b/sys/amiga/Makefile.ami @@ -0,0 +1,1679 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.ami 3.4 2002/21/02 +# Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1991,1992,1993,1996. +# NetHack may be freely redistributed. See license for details. + +### +### INTRODUCTION +### + +# This makefile is arranged for compiling for the Amiga with SAS/C 6.51 but +# can be configured for compiling with Manx C 5 or commercial DICE with +# simple changes. The appropriate changes are identified by #[compiler] +# where compiler is one of: SAS6, MANX, or DICE; the options in this +# makefile as should be set according to the compiler being used. (But see +# note 3 below.) + +# Note: When using the Manx compiler, an alternate make utility is +# required. The bundled Aztec make is just too damaged. + +# Note 2: The #SFD_xxx lines are used with mkdmake to generate a DMake- +# compatible makefile (DMakefile) from this file. Any line beginning with +# #SFD_INSTEAD replaces, in DMakefile, the following line from Makefile.ami. +# #SFD_BEGIN, #SFD_ELSE, and #SFD_END bracket multi-line sections for the two +# makefile formats. +# When changing this file, #SFD_INSTEAD lines will need to be inserted for +# the following cases: +# - Dependencies with different numbers of filenames (both > 1) on +# either side. The #SFD_INSTEAD line should immediately precede +# the line with the colon, and should contain a double colon "::" +# instead of a single colon. +# - Special command lists that override the default. A line containing +# "#SFD_INSTEAD #none" should precede such a rule. If the rule is +# more than one line long, precede it with "#SFD_BEGIN" and +# "#SFD_ELSE", and follow it with "#SFD_END". +# - Files not in the src, sys/amiga, sys/share, or win/tty directories +# that rely on the default ".c.o" rule. Following the dependency +# should be "#SFD_INSTEAD " with the filename inserted +# into the default rule where appropriate, then a line contianing +# "#none". +# In any SFD_BEGIN/ELSE/END block added, put a '##' before every line +# between the BEGIN and ELSE. Any line that's really a comment needs three +# '#'s, e.g. "### DICE comment". + +# Note 2A: Whenever an SFD line/block is added, the appropriate repeat count +# in mkdmake must be changed. (The repeat count "0" meaning "repeat +# until end of file" doesn't work as advertised.) + +# Note 3: mkdmake will automatically substitute DICE flags, etc. for SAS +# where appropriate. Since the makefile is already set up for SAS, +# the only people who end up having to make changes here are Manx +# users (or people who want to change the defaults). + +### +### DIRECTORY STRUCTURE +### + +NH = NH: +SBIN = $(NH)sbin/ +SLIB = $(NH)slib/ +NETHACK = $(NH)NetHack/ +HACKEXE = $(NH)HackExe/ +AMI = $(NH)sys/amiga/ +DAT = $(NH)dat/ +DOC = $(NH)doc/ +I = $(NH)include/ +SHARE = $(NH)sys/share/ +NHS = $(NH)src/ +TTY = $(NH)win/tty/ +WSHARE = $(NH)win/share/ +UTIL = $(NH)util/ +O = $(NH)obj/ +OO = $(NH)objo/ +# NB: O and OO MUST be different directories + +### +### INVOCATION +### + +#[SAS6] +#MAKE = smake +#[MANX] +#MAKE = make +#[DICE] +#MAKE = dmake + +# Startup makefile with: +# +#[SAS6] +#[MANX] +# $(MAKE) -f $(AMI)Makefile.ami +# $(MAKE) -f $(AMI)Makefile.ami install +# +#[DICE] +# $(MAKE) -f $(AMI)DMakefile +# $(MAKE) -f $(AMI)DMakefile install +# +# +# You may use following targets on $(MAKE) command lines: +# all do it all (default) +# link just create binary from object files +# obj just create common object files +# obja just create amiga object files +# objs just create shared object files +# clean deletes the object files +# spotless deletes the object files, main binary, and more +# +# Note: We do not build the Guidebook here since it needs tbl +# (See the file sys/unix/Makefile.doc for more information) + +#X# Precompiled header files: +#X# $(HDEP) should appear in any dependency list for an object file where +#X# we would want to make use of the precompiled version of $(I)hack.h, +#X# while $(CSYM) should appear in the C compiler command line that creates +#X# any such object file. (Changes made here should agree with the $(HDEP): +#X# target that appears later in this makefile.) +#X# + +#SFD_BEGIN +## +###[DICE] +### If we were compiling with DICE and wanted to use the symbol table +### pre-loading feature, we would uncomment these following two lines. +## +##HDEP = $(I)hack.sym +##CSYM = -H$(I)hack.sym=hack.h +## +#SFD_ELSE + +#[SAS5] +# If we were to use the precompiled header file feature in a newer version +# of SAS/C, we would comment out these following two lines. +# If we don't use precompiled header files, we uncomment it as well. + +HDEP = $(I)hack.h $(I)pm.h $(I)onames.h +CSYM = + +#[MANX] +# If we were compiling with Aztec, and wanted to use the symbol table +# pre-loading feature, we would uncomment these following two lines. + +#HDEP = Ram:hack.sym +#CSYM = +IRam:hack.sym + +#SFD_END + +#Pathname for uudecode program: +UUDEC = uudecode + +# Flex/Bison command assignments -- Useful only if you have flex/bison +FLEX = flex +BISON = bison +# FBFIL and FBLIB may be used, if required by your version of flex or bison, +# to specify additional files or libraries to be linked with +FBFIL = +FBLIB = #lib lib:compat.lib + +# If you're compiling this on a 1.3 system, you'll have to uncomment the +# following (for use with the ifchange script below). Also useful instead of +# "protect ifchange +s" +EXECUTE = execute + +# Headers we depend on +AMDEP = $(AMI)winproto.h $(AMI)winext.h $(AMI)windefs.h $(I)winami.h + +# Pathname for the C compiler being used. + +#SFD_BEGIN +## +###[DICE] +##CC = dcc +##ASM = das +## +#SFD_ELSE + +#[SAS6] +CC = sc +ASM = asm + +#[MANX] +#CC = cc + +#SFD_END + +# Compilation flags for selected C Compiler: +# $(CFLAGS) should appear before filename arguments of $(CC) command line. + +#SFD_BEGIN +## +###[DICE] +##CFLAGS = -c -I$(I) -mC -mD -ms -// +##CFLAGS2 = +##WBCFLAGS = -c -I$(I) -mC -mD -ms -// +##WBC2FLAGS = -DCLI +##SPLFLAGS = -DSPLIT +## +#SFD_ELSE + +#[SAS6] +# Note: make sure your CLI stack size is large (at least 50K) or lev_comp +# and makedefs may fail terribly - stack checking is disabled. +# +# **** WARNING **** GST support is not fool proof. You must make makedefs +# without a GST first so that the generated headers +# that are part of the GST can be made. +# +#GSTSRC=$(AMI)gst.c +# +#GSTHEAD=$(I)hack.h $(I)pm.h $(I)trap.h $(I)onames.h \ +# $(AMI)winami.p $(AMI)amidos.p $(AMI)amiwind.p +# +#GSTFILE=$(O)NetHack.gst +# undefine this to not compile with GSTs +#GST=gst=$(GSTFILE) +# +DEBUG=debug=symbol +CPU=cpu=68000 +#OPTFLAGS=opt opttime optpeep optgo optinl optsched optcomp=10 optdep=5 optrdep=5 #optalias +OPTTIME -OPTSIZE +CFLAGS = data=far nominc $(DEBUG) idir=$(I) $(CPU) nostkchk nover \ + codename=nhcode dataname=nhdata strmerge $(OPTFLAGS) $(TILES) $(SAVEDS) \ + afp $(ERRREXX) $(GST) +# for files that are too large for the standard flags: +CFLAGS2 = code=far strmerge $(SAVEDS) +WBCFLAGS = ignore=217,62 data=far ansi nminc code=far idir=$(I) $(CPU) afp \ + $(DEBUG) $(ERRREXX) define=AMIGA $(GST) +XXX = data=far ansi nminc idir=$(I) $(CPU) afp opt optinline optinlocal \ + optloop opttime +WBC2FLAGS = define=CLI +SPLFLAGS = define=SPLIT #dollarok +#for amistack.c +CFLAGS3 = data=near dataname=__MERGED nominc $(DEBUG) idir=$(I) $(CPU) nover nostkchk \ + codename=nhcode strmerge $(OPTFLAGS) $(TILES) $(SAVEDS) \ + afp $(ERRREXX) $(GST) + +#[MANX] +#CFLAGS = -i$(I) -mc -md -ms -pa -ps -bs -wo -qq +#WBCFLAGS = -mc -md -ms -pa -ps -bs -wo -qq -pp + +#SFD_END + +# Assembly flags: + +#SFD_BEGIN +## +###[DICE] +##AFLAGS = +##AOBJSPEC = -o +## +#SFD_ELSE + +#[SAS6] +AFLAGS = #what to put here? +AOBJSPEC = -o + +#SFD_END + +# Components of various link command lines: +# $(LINK) should be the pathname of the linker being used (with any options +# that should appear at the beginning of the command line). The name of the +# output file should appear immediately after $(LNSPEC). $(LIN) should +# appear before the list of object files in each link command. $(LLINK) +# should appear as the list of object files in the link command line that +# creates the NetHack executable. $(LLIB) should appear at the end of each +# link command line. + +# Note: amiga.lib added due to missing prototypes/pragmas. +# Should be deleted when this is resolved. + +#SFD_BEGIN +## +###[DICE] +### If you have flex/bison libraries, use the second definition of FLLIB +### instead of the first. +## +##LINK = dcc -mD +##LIN = +##LLINK = @$(AMI)ami.lnk +##LLIB = +##FLLIB = +###FLLIB = -l$(FBLIB) +##OBJSPEC = -o +##PNSPEC = -o +##LNSPEC = -o +##CCLINK = dcc +##CLFLAGS = -I$(I) -mC -mD -ms -// +##INCLSPEC = -I +##DEFSPEC = -D +##IGNSPEC = -j +## +#SFD_ELSE + +#[SAS6] + +LINK = slink noicons verbose maxhunk 262144 stripdebug +LIN = from lib:catch.o +LLINK = with $(AMI)ami.lnk +LLIB = lib lib:scnb.lib BATCH #lib lib:amiga.lib BATCH #scnb.lib or sc.lib +FLLIB = $(FBLIB) lib Lib:sc.lib BATCH +OBJSPEC = objname= +PNSPEC = noicons to #pname= +LNSPEC = to +CCLINK = sc link +INCLSPEC = idir= +DEFSPEC = define= +IGNSPEC = ignore= +COMPACT_HEADERS=$(GSTFILE) + +#[MANX] + +#LINK = ln -g +q +ss -o +#LIN = +#LLINK = -f $(AMI)ami.lnk +#LLIB = -lcl16 +#FLLIB = -lcl16 +#OBJSPEC = -o +#PNSPEC = -o +#LNSPEC = -o +#CCLINK = cc +#INCLSPEC = -i +#DEFSPEC = -d +#IGNSPEC = -j + +#SFD_END + +### +### FILE LISTS +### + +# A more reasonable random number generator (recommended for the Amiga): + +RANDOBJ = $(O)random.o + +#SFD_INSTEAD #none +.PRECIOUS: $(I)config.h $(I)decl.h $(I)hack.h $(I)permonst.h $(I)you.h + +# Almost nothing below this line should have to be changed. +# (Exceptions are marked by [SAS6], [MANX], etc.) +# +# Other things that have to be reconfigured are in config.h, +# (amiconf.h, pcconf.h), and possibly system.h, tradstdc.h. + +# Object files for makedefs: + +MAKEOBJS = \ + $(OO)makedefs.o $(O)monst.o $(O)objects.o + +# Object files for special levels compiler: + +SPLEVOBJS = \ + $(OO)lev_yacc.o $(OO)lev_lex.o $(OO)lev_main.o \ + $(O)decl.o $(O)drawing.o $(O)monst.o \ + $(O)objects.o $(OO)panic.o + +# Object files for dungeon compiler + +DGNCOMPOBJS = \ + $(OO)dgn_yacc.o $(OO)dgn_lex.o $(OO)dgn_main.o $(O)alloc.o $(OO)panic.o + +# Object files for NetHack: + +COMMOBJ = \ + $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o \ + $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o \ + $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o \ + $(O)dig.o $(O)display.o $(O)dlb.o $(O)do.o \ + $(O)do_name.o $(O)do_wear.o $(O)dog.o $(O)dogmove.o \ + $(O)dokick.o $(O)dothrow.o $(O)drawing.o $(O)dungeon.o \ + $(O)eat.o $(O)end.o $(O)engrave.o $(O)exper.o \ + $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o \ + $(O)hack.o $(O)hacklib.o $(O)invent.o $(O)light.o \ + $(O)lock.o $(O)mail.o $(O)makemon.o $(O)mapglyph.o \ + $(O)mcastu.o $(O)mhitm.o $(O)mhitu.o $(O)minion.o \ + $(O)mklev.o $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o \ + $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o \ + $(O)monst.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o \ + $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o \ + $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o \ + $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o \ + $(O)quest.o $(O)questpgr.o $(O)read.o $(O)rect.o \ + $(O)region.o $(O)restore.o $(O)rnd.o $(O)role.o \ + $(O)rumors.o $(O)save.o $(O)shk.o $(O)shknam.o \ + $(O)sit.o $(O)sounds.o $(O)sp_lev.o $(O)spell.o \ + $(O)steal.o $(O)steed.o $(O)teleport.o $(O)timeout.o \ + $(O)topten.o $(O)track.o $(O)trap.o $(O)u_init.o \ + $(O)uhitm.o $(O)vault.o $(O)version.o $(O)vision.o \ + $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o \ + $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o \ + $(O)zap.o + +MAKEDEFOBJ = \ + $(O)monstr.o + +AMIGAOBJ = \ + $(O)amidos.o $(O)amirip.o $(O)amisnd.o $(O)amistack.o \ + $(O)amiwind.o $(O)winami.o $(O)winchar.o $(O)winfuncs.o \ + $(O)winkey.o $(O)winmenu.o $(O)winreq.o $(O)winstr.o + +# Objects from assembly sources (because DMake can't handle default rules) +AMIGAOBJ2 = \ +# $(O)dispmap.o + +SHAREOBJ = \ + $(O)pcmain.o $(RANDOBJ) + +TTYOBJ = \ + $(O)getline.o $(O)termcap.o $(O)topl.o $(O)wintty.o $(O)amitty.o \ + $(O)rip.o + +# Yuck yuck yuck. Have to tell DMake where these are, since they're not +# all in the same place. +TTYSRC = \ + $(TTY)getline.c $(TTY)termcap.c $(TTY)topl.c $(TTY)wintty.c \ + $(AMI)amitty.c $(NHS)rip.c + +# All the object files for NetHack: + +HOBJ = $(COMMOBJ) $(AMIGAOBJ) $(AMIGAOBJ2) $(SHAREOBJ) $(MAKEDEFOBJ) $(TTYOBJ) + +### +### DATA FILES +### + +# quest files +ADFILES1= $(SLIB)Arc-fila.lev $(SLIB)Arc-filb.lev $(SLIB)Arc-loca.lev \ + $(SLIB)Arc-strt.lev +ADFILES= $(SLIB)Arc-goal.lev $(ADFILES1) + +BDFILES1= $(SLIB)Bar-fila.lev $(SLIB)Bar-filb.lev $(SLIB)Bar-loca.lev \ + $(SLIB)Bar-strt.lev +BDFILES= $(SLIB)Bar-goal.lev $(BDFILES1) + +CDFILES1= $(SLIB)Cav-fila.lev $(SLIB)Cav-filb.lev $(SLIB)Cav-loca.lev \ + $(SLIB)Cav-strt.lev +CDFILES= $(SLIB)Cav-goal.lev $(CDFILES1) + +HDFILES1= $(SLIB)Hea-fila.lev $(SLIB)Hea-filb.lev $(SLIB)Hea-loca.lev \ + $(SLIB)Hea-strt.lev +HDFILES= $(SLIB)Hea-goal.lev $(HDFILES1) + +KDFILES1= $(SLIB)Kni-fila.lev $(SLIB)Kni-filb.lev $(SLIB)Kni-loca.lev \ + $(SLIB)Kni-strt.lev +KDFILES= $(SLIB)Kni-goal.lev $(KDFILES1) + +MDFILES1= $(SLIB)Mon-fila.lev $(SLIB)Mon-filb.lev $(SLIB)Mon-loca.lev \ + $(SLIB)Mon-strt.lev +MDFILES= $(SLIB)Mon-goal.lev $(MDFILES1) + +PDFILES1= $(SLIB)Pri-fila.lev $(SLIB)Pri-filb.lev $(SLIB)Pri-loca.lev \ + $(SLIB)Pri-strt.lev +PDFILES= $(SLIB)Pri-goal.lev $(PDFILES1) + +RDFILES1= $(SLIB)Rog-fila.lev $(SLIB)Rog-filb.lev $(SLIB)Rog-loca.lev \ + $(SLIB)Rog-strt.lev +RDFILES= $(SLIB)Rog-goal.lev $(RDFILES1) + +RANFILES1= $(SLIB)Ran-fila.lev $(SLIB)Ran-filb.lev $(SLIB)Ran-loca.lev \ + $(SLIB)Ran-strt.lev +RANFILES= $(SLIB)Ran-goal.lev $(RANFILES1) + +SDFILES1= $(SLIB)Sam-fila.lev $(SLIB)Sam-filb.lev $(SLIB)Sam-loca.lev \ + $(SLIB)Sam-strt.lev +SDFILES= $(SLIB)Sam-goal.lev $(SDFILES1) + +TDFILES1= $(SLIB)Tou-fila.lev $(SLIB)Tou-filb.lev $(SLIB)Tou-loca.lev \ + $(SLIB)Tou-strt.lev +TDFILES= $(SLIB)Tou-goal.lev $(TDFILES1) + +VDFILES1= $(SLIB)Val-fila.lev $(SLIB)Val-filb.lev $(SLIB)Val-loca.lev \ + $(SLIB)Val-strt.lev +VDFILES= $(SLIB)Val-goal.lev $(VDFILES1) + +WDFILES1= $(SLIB)Wiz-fila.lev $(SLIB)Wiz-filb.lev $(SLIB)Wiz-loca.lev \ + $(SLIB)Wiz-strt.lev +WDFILES= $(SLIB)Wiz-goal.lev $(WDFILES1) + +XDFILES= $(ADFILES) $(BDFILES) $(CDFILES) $(HDFILES) $(KDFILES) \ + $(MDFILES) $(PDFILES) $(RDFILES) $(RANFILES) $(SDFILES) $(TDFILES) \ + $(VDFILES) $(WDFILES) + +SOUNDFILES= \ + $(SBIN)cvtsnd \ + $(SLIB)sounds \ + $(SLIB)sounds/Bell $(SLIB)sounds/Bugle \ + $(SLIB)sounds/Drum_Of_Earthquake \ + $(SLIB)sounds/Fire_Horn $(SLIB)sounds/Frost_Horn \ + $(SLIB)sounds/Leather_Drum $(SLIB)sounds/Magic_Flute \ + $(SLIB)sounds/Magic_Harp $(SLIB)sounds/Tooled_Horn \ + $(SLIB)sounds/Wooden_Flute $(SLIB)sounds/Wooden_Harp + +TILEFILES= \ + $(SBIN)txt2iff \ + $(NETHACK)tiles \ + $(NETHACK)tiles/objects.iff \ + $(NETHACK)tiles/monsters.iff \ + $(NETHACK)tiles/other.iff + +INSTDUNGEONFILES1= \ + $(SLIB)air.lev $(SLIB)asmodeus.lev $(SLIB)astral.lev \ + $(SLIB)baalz.lev $(SLIB)bigrm-1.lev $(SLIB)bigrm-2.lev \ + $(SLIB)bigrm-3.lev $(SLIB)bigrm-4.lev $(SLIB)bigrm-5.lev \ + $(SLIB)castle.lev $(SLIB)dungeon $(SLIB)earth.lev \ + $(SLIB)fakewiz1.lev $(SLIB)fakewiz2.lev $(SLIB)fire.lev \ + $(SLIB)juiblex.lev $(SLIB)knox.lev $(SLIB)medusa-1.lev \ + $(SLIB)medusa-2.lev $(SLIB)minend-1.lev $(SLIB)minend-2.lev \ + $(SLIB)minetn-1.lev $(SLIB)minetn-2.lev $(SLIB)minefill.lev \ + $(SLIB)options $(SLIB)oracle.lev $(SLIB)orcus.lev \ + $(SLIB)sanctum.lev $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev \ + $(SLIB)soko2-1.lev $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev \ + $(SLIB)soko3-2.lev $(SLIB)soko4-1.lev $(SLIB)soko4-2.lev \ + $(SLIB)tower1.lev $(SLIB)tower2.lev $(SLIB)tower3.lev \ + $(SLIB)valley.lev $(SLIB)water.lev $(SLIB)wizard1.lev \ + $(SLIB)wizard2.lev $(SLIB)wizard3.lev \ + $(XDFILES) + +INSTDUNGEONFILES= $(NETHACK)NetHack.cnf $(INSTDUNGEONFILES1) + + +INSTDATAFILES= \ + $(NETHACK)license $(NETHACK)logfile $(NETHACK)record \ + $(NETHACK)tomb.iff $(NETHACK)amii.hlp $(NETHACK)Recover.txt \ + $(NETHACK)GuideBook.txt $(NETHACK)NetHack.txt $(NETHACK)Install.ami + +LIBFILES= \ + $(INSTDUNGEONFILES1) \ + $(SLIB)cmdhelp $(SLIB)data $(SLIB)dungeon \ + $(SLIB)help $(SLIB)hh $(SLIB)history \ + $(SLIB)opthelp $(SLIB)oracles $(SLIB)rumors \ + $(SLIB)quest.dat $(SLIB)wizhelp + +### +### Getting down to business: +### + +#SFD_INSTEAD all: $(SBIN)lev_comp $(SBIN)dgn_comp $(SBIN)NetHack \ +all: $(COMPACT_HEADERS) $(SBIN)lev_comp $(SBIN)dgn_comp $(SBIN)NetHack \ + $(SBIN)dlb $(NETHACK)recover + +install: all inst-data inst-dungeon inst-fonts inst-sounds inst-tiles \ + $(NETHACK)nhdat $(NETHACK)NetHack + +$(SBIN)NetHack: $(HOBJ) $(AMI)ami.lnk + $(LINK) $(LNSPEC) $(SBIN)NetHack $(LIN) $(LLINK) $(LLIB) + +$(NETHACK)NetHack: $(SBIN)NetHack + copy $(SBIN)NetHack $(NETHACK)NetHack + +link: + $(LINK) $(LNSPEC) $(SBIN)NetHack $(LIN) $(LLINK) $(LLIB) + +$(AMI)ami.lnk: $(AMI)Makefile.ami + list to $(AMI)ami.lnk lformat="$(O)%s" $(O)\#?.o QUICK NOHEAD + +## dlb support +$(OO)dlb_main.o: $(UTIL)dlb_main.c $(HDEP) $(I)dlb.h $(I)date.h + $(CC) $(CFLAGS) $(OBJSPEC)$(OO)dlb_main.o $(UTIL)dlb_main.c + +$(SBIN)dlb: $(OO)dlb_main.o $(O)dlb.o $(O)alloc.o $(OO)panic.o + $(LINK) $(PNSPEC) $(SBIN)dlb $(LIN) $(OO)dlb_main.o $(O)dlb.o \ + $(O)alloc.o $(OO)panic.o $(LLIB) + +obj: $(HOBJ) + +obja: $(AMIGAOBJ) + +objs: $(SHAREOBJ) + + +#SFD_BEGIN +#SFD_ELSE +SUFFIXES = .lev .des +.des.lev: + $(SBIN)lev_comp $< +#SFD_END + + +# The default method for creating object files: + +#SFD_BEGIN +## +###[DICE] +## +##$(COMMOBJ): $(COMMOBJ:"$(O)*.o":"$(NHS)%1.c") +## $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)%(left) %(right) +## +##$(AMIGAOBJ): $(AMIGAOBJ:"$(O)*.o":"$(AMI)%1.c") +## $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)%(left) %(right) +## +##$(SHAREOBJ): $(SHAREOBJ:"$(O)*.o":"$(SHARE)%1.c") +## $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)%(left) %(right) +## +##$(TTYOBJ): $(TTYSRC) +## $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)%(left) %(right) +## +#SFD_ELSE + +#[SAS6] + +.c.o: + $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)$@ $< + +#SFD_END + + +clean: + -delete $(O)\#?.o $(OO)\#?.o + +spotless: clean + -delete $(SBIN)NetHack $(SBIN)lev_comp $(SBIN)makedefs $(SBIN)dgn_comp + -delete $(SBIN)cvtsnd $(SBIN)dlb $(SBIN)txt2iff $(SBIN)splitter + -delete $(SBIN)tilemap + -delete $(SLIB)data $(SLIB)rumors + -delete $(SLIB)\#?.lev + -delete $(SLIB)dungeon + -delete $(SLIB)cmdhelp $(SLIB)help $(SLIB)hh $(SLIB)history + -delete $(SLIB)opthelp $(SLIB)options $(SLIB)oracles + -delete $(SLIB)quest.dat $(SLIB)wizhelp +# -delete $(SLIB)earth.lev $(SLIB)air.lev $(SLIB)fire.lev +# -delete $(SLIB)water.lev $(SLIB)astral.lev +# -delete $(SLIB)tower1.lev $(SLIB)tower2.lev $(SLIB)tower3.lev +# -delete $(SLIB)fakewiz1.lev $(SLIB)fakewiz2.lev +# -delete $(SLIB)medusa-1.lev $(SLIB)medusa-2.lev +# -delete $(SLIB)oracle.lev $(SLIB)wizard1.lev $(SLIB)wizard2.lev +# -delete $(SLIB)wizard3.lev $(DAT)dungeon.pdf $(SLIB)valley.lev +# -delete $(SLIB)minefill.lev +# -delete $(SLIB)minetn-1 $(SLIB)minetn-2 $(SLIB)minend-1 $(SLIB)minend-2 +# -delete $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev $(SLIB)soko2-1.lev +# -delete $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev $(SLIB)soko3-2.lev +# -delete $(SLIB)soko4-1.lev $(SLIB)soko4-2.lev +# -delete $(ADFILES) +# -delete $(BDFILES) +# -delete $(CDFILES) +# -delete $(HDFILES) +# -delete $(KDFILES) +# -delete $(MDFILES) +# -delete $(PDFILES) +# -delete $(RDFILES) +# -delete $(RANFILES) +# -delete $(SDFILES) +# -delete $(TDFILES) +# -delete $(VDFILES) +# -delete $(WDFILES) + -delete $(I)onames.h $(I)pm.h $(I)date.h + -delete $(NHS)tile.c $(NHS)monstr.c + -delete $(I)tile.h +# -echo to $(I)onames.h "" noline +# -wait 2 +# -echo to $(I)pm.h "" noline +# -wait 2 +# -setdate $(UTIL)makedefs.c +# -wait 2 + +# Creating precompiled version of $(I)hack.h to save disk I/O. + +#SFD_BEGIN +## +###[DICE] +### If we were compiling with DICE and wanted to use the symbol table +### pre-loading feature, we would technically not need a rule to make the +### precompiled header file, because DCC handles this automatically; +### however, we must delete the precompiled header file if any of the +### includes change, and we need to create it manually because the +### sys/amiga sources, compiled first, define things differently than the +### main sources want them. +## +##$(HDEP): $(I)hack.h $(I)pm.h $(I)onames.h +## -delete $(I)hack.sym +## echo to Ram:hackincl.c "#include " +## $(CC) $(CFLAGS) $(CSYM) $(OBJSPEC)Ram:hackincl.o Ram:hackincl.c +## -delete Ram:hackincl.c Ram:hackincl.o +## +#SFD_ELSE + +#X#[SAS5] +#X# If we were to use the precompiled header file feature of SAS/C, we +#X# would uncomment the following lines. (Also see defines for HDEP and +#X# CSYM near the beginning of this file, as these should be appropriately +#X# defined.) + +#X#$(HDEP): $(I)hack.h $(SBIN)makedefs +#X# echo to Ram:hackincl.c "#include <$(I)hack.h>" +#X# $(CC) $(CFLAGS) -ph $(OBJSPEC)$@ Ram:hackincl.c +#X# -delete Ram:hackincl.c + +#[MANX] +# If we were compiling with Aztec, and wanted to use the symbol table +# pre-loading feature, we would uncomment these following two lines. + +#$(HDEP): $(I)hack.h $(SBIN)makedefs +# $(CC) $(CFLAGS) -a $(OBJSPEC)Ram:hack.asm +h$@ $(I)hack.h +# -delete Ram:hack.asm + +#SFD_END + +# +# Please note: The dependency lines for the modules here are +# deliberately incorrect. Including "hack.h" in +# the dependency list would cause a dependency +# loop. +# + +$(SBIN)makedefs: $(MAKEOBJS) + $(LINK) $(LNSPEC) $(SBIN)makedefs $(LIN) $(MAKEOBJS) $(LLIB) + +$(OO)makedefs.o: $(UTIL)makedefs.c $(I)config.h $(I)permonst.h $(I)monsym.h \ + $(I)objclass.h $(I)patchlevel.h $(I)qtext.h $(I)artilist.h + $(CC) $(DEFSPEC)MAKEDEFS_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)makedefs.c + +$(SBIN)lev_comp: $(SPLEVOBJS) + $(LINK) $(LNSPEC) $(SBIN)lev_comp $(LIN) $(SPLEVOBJS) $(FBFIL) $(FLLIB) + +$(SBIN)dgn_comp: $(DGNCOMPOBJS) + $(LINK) $(LNSPEC) $(SBIN)dgn_comp $(LIN) $(DGNCOMPOBJS) $(FBFIL) $(FLLIB) + +$(OO)lev_yacc.o: $(UTIL)lev_yacc.c $(HDEP) $(I)sp_lev.h $(I)pm.h $(I)onames.h +# setdate $(UTIL)lev_yacc.c + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)PREFIX="NH:slib/" $(CFLAGS) \ + $(DEFSPEC)alloca=malloc $(OBJSPEC)$@ $(UTIL)lev_yacc.c + +$(OO)lev_lex.o: $(UTIL)lev_lex.c $(HDEP) $(I)lev_comp.h $(I)sp_lev.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)lev_lex.c + +$(OO)lev_main.o: $(UTIL)lev_main.c $(HDEP) $(I)pm.h $(I)onames.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)lev_main.c + +$(OO)dgn_yacc.o: $(UTIL)dgn_yacc.c $(HDEP) $(I)dgn_file.h $(I)patchlevel.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(DEFSPEC)alloca=malloc \ + $(OBJSPEC)$@ $(UTIL)dgn_yacc.c + +$(OO)dgn_lex.o: $(UTIL)dgn_lex.c $(I)config.h $(I)dgn_comp.h $(I)dgn_file.h + $(CC) $(DEFSPEC)LEV_LEX_C $(CFLAGS) $(OBJSPEC)$@ $(UTIL)dgn_lex.c + +$(OO)dgn_main.o: $(UTIL)dgn_main.c $(I)config.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)dgn_main.c + +$(OO)panic.o: $(UTIL)panic.c $(HDEP) +#SFD_INSTEAD $(CC) $(CFLAGS) $(OBJSPEC)%(left) $(UTIL)panic.c +#none + +$(OO)recover.o: $(UTIL)recover.c $(I)config.h $(I)date.h + $(CC) $(DEFSPEC)LEV_LEX_C $(DEFSPEC)AMIGA $(CFLAGS) $(OBJSPEC)$@ \ + $(UTIL)recover.c + +$(NETHACK)recover: $(OO)recover.o + $(LINK) $(LNSPEC) $(NETHACK)recover $(LIN) $(OO)recover.o $(LLIB) + +# [OPTION] -- If you have flex/bison, leave these uncommented. Otherwise, +# comment them out and be careful! (You're not guaranteed to have the most +# up to date *_comp.c, *_comp.h and *_lex.c) + +$(I)lev_comp.h: $(UTIL)lev_yacc.c $(I)patchlevel.h + +$(UTIL)lev_yacc.c: $(UTIL)lev_comp.y $(I)patchlevel.h + $(BISON) -d $(UTIL)lev_comp.y +# copy y.tab.c $(UTIL)lev_yacc.c +# copy y.tab.h $(I)lev_comp.h + copy $(UTIL)lev_comp.tab.c $(UTIL)lev_yacc.c + copy $(UTIL)lev_comp.tab.h $(I)lev_comp.h +# delete y.tab.c +# delete y.tab.h + delete $(UTIL)lev_comp.tab.c + delete $(UTIL)lev_comp.tab.h + +$(UTIL)lev_lex.c: $(UTIL)lev_comp.l $(I)patchlevel.h + $(FLEX) $(UTIL)lev_comp.l + copy lex.yy.c $(UTIL)lev_lex.c + delete lex.yy.c + +$(I)dgn_comp.h: $(UTIL)dgn_yacc.c $(I)patchlevel.h + +$(UTIL)dgn_yacc.c: $(UTIL)dgn_comp.y $(I)patchlevel.h + $(BISON) -d $(UTIL)dgn_comp.y +# copy y.tab.c $(UTIL)dgn_yacc.c +# copy y.tab.h $(I)dgn_comp.h + copy $(UTIL)dgn_comp.tab.c $(UTIL)dgn_yacc.c + copy $(UTIL)dgn_comp.tab.h $(I)dgn_comp.h +# delete y.tab.c +# delete y.tab.h + delete $(UTIL)dgn_comp.tab.c + delete $(UTIL)dgn_comp.tab.h + +$(UTIL)dgn_lex.c: $(UTIL)dgn_comp.l $(I)patchlevel.h + $(FLEX) $(UTIL)dgn_comp.l + copy lex.yy.c $(UTIL)dgn_lex.c + delete lex.yy.c + +# +# The following include files depend on makedefs to be created. +# As a result, they are not defined in HACKINCL, instead, their +# dependencies are explicitly outlined here. +# + +# +# date.h should be remade any time any of the source or include code +# is modified. Unfortunately, this would make the contents of this +# file far more complex. Since "hack.h" depends on most of the include +# files, we kludge around this by making date.h dependent on hack.h, +# even though it doesn't include this file. +# + +#SFD_INSTEAD $(I)date.h $(DAT)options:: $(HDEP) $(SBIN)makedefs $(AMIGAOBJ) +$(I)date.h $(DAT)options: $(HDEP) $(SBIN)makedefs $(AMIGAOBJ) $(I)patchlevel.h + $(SBIN)makedefs -v + $(EXECUTE) $(AMI)ifchange MOVE $(I)t.date.h $(I)date.h + -wait 2 + +$(I)onames.h: $(SBIN)makedefs + $(SBIN)makedefs -o + $(EXECUTE) $(AMI)ifchange TOUCH $(I)t.onames.h $(I)onames.h $(I)decl.h + $(EXECUTE) $(AMI)ifchange MOVE $(I)t.onames.h $(I)onames.h + -wait 2 + +$(I)pm.h: $(SBIN)makedefs + $(SBIN)makedefs -p + $(EXECUTE) $(AMI)ifchange TOUCH $(I)t.pm.h $(I)pm.h $(I)decl.h $(I)youprop.h + $(EXECUTE) $(AMI)ifchange MOVE $(I)t.pm.h $(I)pm.h + -wait 2 + +$(SLIB)quest.dat: $(DAT)quest.txt $(SBIN)makedefs + $(SBIN)makedefs -q + +$(NHS)monstr.c: $(HDEP) $(SBIN)makedefs + $(SBIN)makedefs -m + -wait 2 + +$(SLIB)oracles: $(DAT)oracles.txt $(SBIN)makedefs + $(SBIN)makedefs -h + -wait 2 + +# +# The following programs vary depending on what OS you are using. +# As a result, they are not defined in HACKSRC and their dependencies +# are explicitly outlined here. +# + +$(O)amidos.o: $(AMI)amidos.c $(HDEP) + +$(O)amirip.o: $(AMI)amirip.c $(HDEP) + +$(O)aglue.o: $(AMI)aglue.a + $(ASM) $(AFLAGS) $(AOBJSPEC)$(O)aglue.o $(AMI)aglue.a + +$(O)amisnd.o: $(AMI)amisnd.c $(HDEP) + +$(O)winchar.o: $(AMI)winchar.c $(NHS)tile.c $(HDEP) + +$(NHS)tile.c: $(WSHARE)tilemap.c +#SFD_INSTEAD $(CCLINK) $(CLFLAGS) $(PNSPEC) $(SBIN)tilemap $(WSHARE)tilemap.c + $(CCLINK) $(CFLAGS) $(PNSPEC) $(SBIN)tilemap $(WSHARE)tilemap.c + $(SBIN)tilemap + +$(O)winstr.o: $(AMI)winstr.c $(HDEP) $(AMDEP) + +$(O)winreq.o: $(AMI)winreq.c $(HDEP) $(AMDEP) $(AMI)colorwin.c $(AMI)clipwin.c + +$(O)winfuncs.o: $(AMI)winfuncs.c $(HDEP) $(AMDEP) $(I)patchlevel.h + +$(O)winkey.o: $(AMI)winkey.c $(HDEP) $(AMDEP) + +$(O)winmenu.o: $(AMI)winmenu.c $(HDEP) $(AMDEP) + +$(O)winami.o: $(AMI)winami.c $(HDEP) $(AMDEP) #$(AMI)char.c $(AMI)randwin.c + +#$(O)amilib.o: $(AMI)amilib.c $(HDEP) $(AMDEP) + +$(O)amiwind.o: $(AMI)amiwind.c $(AMI)amimenu.c $(HDEP) $(AMDEP) + +$(O)amiwbench.o: $(AMI)amiwbench.c $(HDEP) + +$(O)random.o: $(SHARE)random.c + +$(O)pcmain.o: $(SHARE)pcmain.c $(HDEP) $(I)dlb.h + +$(O)dispmap.o: $(AMI)dispmap.s + $(ASM) $(AFLAGS) $(AOBJSPEC)$@ $< + +# Stuff to build the front ends +$(NETHACK)HackWB: $(OO)wb.o $(OO)wbx.o $(OO)loader.o $(OO)multi.o + $(LINK) $(LNSPEC) $(NETHACK)HackWB $(LIN) $(OO)wb.o $(OO)wbx.o \ + $(OO)loader.o $(OO)multi.o $(LLIB) + +$(NETHACK)HackCli: $(OO)cli.o $(OO)loader.o $(OO)multi.o + $(LINK) $(LNSPEC) $(NETHACK)HackCli $(LIN) $(OO)cli.o $(OO)loader.o \ + $(OO)multi.o $(LLIB) + +# This needs to exist to eliminate the HackWB startup message +#SFD_INSTEAD $(NETHACK)WBDefaults.def: $(NETHACK)WBDefaults.def +$(NETHACK)WBDefaults.def: + echo to $(NETHACK)WBDefaults.def + +WBH = $(AMI)wbdefs.h $(AMI)wbstruct.h $(AMI)wbprotos.h +ASP = $(AMI)splitter +$(OO)wb.o: $(WBH) $(AMI)wb.c $(AMI)wbwin.c $(AMI)wbdata.c $(AMI)wbgads.c \ + $(I)patchlevel.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)wb.o $(AMI)wb.c + +$(OO)wbx.o: $(WBH) $(AMI)wbcli.c $(AMI)wbwin.c $(AMI)wbdata.c \ + $(I)patchlevel.h $(I)date.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)wbx.o $(AMI)wbcli.c + +$(OO)loader.o: $(ASP)/loader.c $(ASP)/split.h $(ASP)/amiout.h $(ASP)/multi.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)loader.o $(ASP)/loader.c + +$(OO)multi.o: $(ASP)/multi.c $(ASP)/multi.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)multi.o $(ASP)/multi.c + +$(OO)cli.o: $(WBH) $(AMI)wbcli.c $(I)patchlevel.h $(I)date.h + $(CC) $(WBCFLAGS) $(WBC2FLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)cli.o \ + $(AMI)wbcli.c + +#### +# splitter support +$(SBIN)splitter: $(OO)splitter.o $(OO)arg.o + $(LINK) $(LNSPEC) $(SBIN)splitter $(LIN) $(OO)splitter.o $(OO)arg.o \ + $(LLIB) + +$(NETHACK)NetHack.dir: $(SBIN)splitter $(SBIN)NetHack + $(SBIN)splitter $(SBIN)NetHack + +$(OO)splitter.o: $(ASP)/splitter.c $(ASP)/split.h $(ASP)/amiout.h $(ASP)/arg.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)splitter.o \ + $(ASP)/splitter.c + +$(OO)arg.o: $(ASP)/arg.c $(ASP)/arg.h + $(CC) $(WBCFLAGS) $(SPLFLAGS) $(OBJSPEC)$(OO)arg.o $(ASP)/arg.c + +# Create/copy other stuff into NetHack: directory: + +$(NETHACK)tomb.iff: $(SBIN)xpm2iff $(AMI)grave16.xpm + $(SBIN)xpm2iff $(AMI)grave16.xpm $(NETHACK)tomb.iff + +$(OO)xpm2iff.o: $(AMI)xpm2iff.c + $(CC) $(CFLAGS) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(AMI)xpm2iff.c + +$(SBIN)xpm2iff: $(OO)xpm2iff.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)xpm2iff.o $(FLLIB) + +# Tile installation for the tile version of the game +inst-tiles: $(TILEFILES) + +#SFD_INSTEAD $(NETHACK)tiles: $(NETHACK)tiles +$(NETHACK)tiles: + -makedir $(NETHACK)tiles + +$(OO)txt2iff.o: $(AMI)txt2iff.c + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ \ + $(AMI)txt2iff.c + +$(OO)ppmwrite.o: $(WSHARE)ppmwrite.c + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)ppmwrite.c + +$(OO)tiletext.o: $(WSHARE)tiletext.c $(I)config.h $(WSHARE)tile.h + $(CC) $(CFLAGS) $(CSYM) $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)tiletext.c + +$(OO)tiletxt.o: $(WSHARE)tilemap.c $(I)hack.h + $(CC) $(CFLAGS) $(CSYM) $(DEFSPEC)TILETEXT $(INCLSPEC)$(WSHARE) $(OBJSPEC)$@ $(WSHARE)tilemap.c + +NAMEOBJS = $(O)drawing.o $(O)decl.o $(O)monst.o $(O)objects.o + +$(SBIN)txt2ppm: $(OO)ppmwrite.o $(NAMEOBJS) $(O)alloc.o $(OO)panic.o $(OO)tiletext.o $(OO)tiletxt.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)ppmwrite.o $(NAMEOBJS) $(OO)tiletext.o $(OO)tiletxt.o $(O)alloc.o $(OO)panic.o $(FLLIB) + +$(SBIN)txt2iff: $(OO)txt2iff.o $(NAMEOBJS) $(OO)tiletext.o $(OO)tiletxt.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)txt2iff.o $(NAMEOBJS) $(OO)tiletext.o \ + $(OO)tiletxt.o $(FLLIB) + +$(NETHACK)tiles/objects.iff: $(WSHARE)objects.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)objects.txt $(NETHACK)tiles/objects.iff + +$(NETHACK)tiles/monsters.iff: $(WSHARE)monsters.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)monsters.txt $(NETHACK)tiles/monsters.iff + +$(NETHACK)tiles/other.iff: $(WSHARE)other.txt $(SBIN)txt2iff + $(SBIN)txt2iff $(WSHARE)other.txt $(NETHACK)tiles/other.iff + +# Sound installation rules. +inst-sounds: $(SOUNDFILES) + list to T:nhsdat.lst $(SLIB)sounds QUICK NOHEAD + echo >T:make-nhsdat $(SBIN)dlb cCfI $(SLIB)sounds $(NETHACK)nhsdat T:nhsdat.lst + echo >>T:make-nhsdat if not exists $(NETHACK)nhsdat + echo >>T:make-nhsdat copy $(SLIB)sounds/\#? $(NETHACK)sounds + echo >>T:make-nhsdat endif + execute T:make-nhsdat + -delete T:make-nhsdat + +#SFD_INSTEAD $(SLIB)sounds: $(SLIB)sounds +$(SLIB)sounds: + -makedir $(SLIB)sounds + +$(SBIN)cvtsnd: $(OO)cvtsnd.o + $(LINK) $(LNSPEC) $@ $(LIN) $(OO)cvtsnd.o $(FLLIB) + +$(OO)cvtsnd.o: $(AMI)cvtsnd.c +#SFD_INSTEAD $(CC) $(CFLAGS) $(OBJSPEC)%(left) %(right) +#none + +$(SLIB)sounds/Bell: $(SHARE)sounds/bell.uu + $(UUDEC) $(SHARE)sounds/bell.uu + $(SBIN)cvtsnd Bell $(SLIB)sounds/Bell + -delete Bell + +$(SLIB)sounds/Bugle: $(SHARE)sounds/bugle.uu + $(UUDEC) $(SHARE)sounds/bugle.uu + $(SBIN)cvtsnd Bugle $(SLIB)sounds/Bugle + -delete Bugle + +$(SLIB)sounds/Drum_Of_Earthquake: $(SHARE)sounds/erthdrum.uu + $(UUDEC) $(SHARE)sounds/erthdrum.uu + $(SBIN)cvtsnd Drum_Of_Earthquake $(SLIB)sounds/Drum_Of_Earthquake + -delete Drum_Of_Earthquake + +$(SLIB)sounds/Fire_Horn: $(SHARE)sounds/firehorn.uu + $(UUDEC) $(SHARE)sounds/firehorn.uu + $(SBIN)cvtsnd Fire_Horn $(SLIB)sounds/Fire_Horn + -delete Fire_Horn + +$(SLIB)sounds/Frost_Horn: $(SHARE)sounds/frsthorn.uu + $(UUDEC) $(SHARE)sounds/frsthorn.uu + $(SBIN)cvtsnd Frost_Horn $(SLIB)sounds/Frost_Horn + -delete Frost_Horn + +$(SLIB)sounds/Leather_Drum: $(SHARE)sounds/lethdrum.uu + $(UUDEC) $(SHARE)sounds/lethdrum.uu + $(SBIN)cvtsnd Leather_Drum $(SLIB)sounds/Leather_Drum + -delete Leather_Drum + +$(SLIB)sounds/Magic_Flute: $(SHARE)sounds/mgcflute.uu + $(UUDEC) $(SHARE)sounds/mgcflute.uu + $(SBIN)cvtsnd Magic_Flute $(SLIB)sounds/Magic_Flute + -delete Magic_Flute + +$(SLIB)sounds/Magic_Harp: $(SHARE)sounds/mgcharp.uu + $(UUDEC) $(SHARE)sounds/mgcharp.uu + $(SBIN)cvtsnd Magic_Harp $(SLIB)sounds/Magic_Harp + -delete Magic_Harp + +$(SLIB)sounds/Tooled_Horn: $(SHARE)sounds/toolhorn.uu + $(UUDEC) $(SHARE)sounds/toolhorn.uu + $(SBIN)cvtsnd Tooled_Horn $(SLIB)sounds/Tooled_Horn + -delete Tooled_Horn + +$(SLIB)sounds/Wooden_Flute: $(SHARE)sounds/wdnflute.uu + $(UUDEC) $(SHARE)sounds/wdnflute.uu + $(SBIN)cvtsnd Wooden_Flute $(SLIB)sounds/Wooden_Flute + -delete Wooden_Flute + +$(SLIB)sounds/Wooden_Harp: $(SHARE)sounds/wdnharp.uu + $(UUDEC) $(SHARE)sounds/wdnharp.uu + $(SBIN)cvtsnd Wooden_Harp $(SLIB)sounds/Wooden_Harp + -delete Wooden_Harp + +inst-dungeon: $(INSTDUNGEONFILES) + +$(NETHACK)options : $(DAT)options + copy $(DAT)options $@ + +# Create compiled dungeon files +BGM= $(SLIB)bigrm-2.lev $(SLIB)bigrm-3.lev $(SLIB)bigrm-4.lev $(SLIB)bigrm-5.lev +$(BGM): $(SLIB)bigrm-1.lev + +$(SLIB)bigrm-1.lev: $(DAT)bigroom.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)bigroom.des +#none + +$(SLIB)castle.lev: $(DAT)castle.des $(SBIN)lev_comp + +ENDGAME1= $(SLIB)air.lev $(SLIB)earth.lev $(SLIB)fire.lev $(SLIB)water.lev +$(ENDGAME1): $(SLIB)astral.lev + +$(SLIB)astral.lev: $(DAT)endgame.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)endgame.des +#none + +GEHENNOM1= $(SLIB)asmodeus.lev $(SLIB)baalz.lev $(SLIB)juiblex.lev \ + $(SLIB)orcus.lev $(SLIB)sanctum.lev +$(GEHENNOM1): $(SLIB)valley.lev + +$(SLIB)valley.lev: $(DAT)gehennom.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)gehennom.des +#none + +$(SLIB)knox.lev: $(DAT)knox.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)knox.des +#none + +MINES1= $(SLIB)minend-1.lev $(SLIB)minend-2.lev $(SLIB)minetn-1.lev $(SLIB)minetn-2.lev +$(MINES1): $(SLIB)minefill.lev + +$(SLIB)minefill.lev: $(DAT)mines.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)mines.des +#none + +$(SLIB)oracle.lev: $(DAT)oracle.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)oracle.des +#none + +TOWER1= $(SLIB)tower1.lev $(SLIB)tower2.lev +$(TOWER1): $(SLIB)tower3.lev + +$(SLIB)tower3.lev: $(DAT)tower.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)tower.des +#none + +WIZARD1= $(SLIB)wizard1.lev $(SLIB)wizard2.lev $(SLIB)wizard3.lev \ + $(SLIB)fakewiz1.lev +$(WIZARD1): $(SLIB)fakewiz2.lev + +$(SLIB)fakewiz2.lev: $(DAT)yendor.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)yendor.des +#none + +MEDUSA1= $(SLIB)medusa-1.lev +$(MEDUSA1): $(SLIB)medusa-2.lev + +$(SLIB)medusa-2.lev: $(DAT)medusa.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)medusa.des +#none + +SOKOBAN1= $(SLIB)soko1-1.lev $(SLIB)soko1-2.lev $(SLIB)soko2-1.lev \ + $(SLIB)soko2-2.lev $(SLIB)soko3-1.lev $(SLIB)soko3-2.lev \ + $(SLIB)soko4-1.lev +$(SOKOBAN1): $(SLIB)soko4-2.lev + +$(SLIB)soko4-2.lev: $(DAT)sokoban.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)sokoban.des +#none + +$(ADFILES1): $(SLIB)Arc-goal.lev + +$(SLIB)Arc-goal.lev: $(DAT)Arch.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Arch.des +#none + +$(BDFILES1): $(SLIB)Bar-goal.lev + +$(SLIB)Bar-goal.lev: $(DAT)Barb.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Barb.des +#none + +$(CDFILES1): $(SLIB)Cav-goal.lev + +$(SLIB)Cav-goal.lev: $(DAT)Caveman.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Caveman.des +#none + +$(HDFILES1): $(SLIB)Hea-goal.lev + +$(SLIB)Hea-goal.lev: $(DAT)Healer.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Healer.des +#none + +$(KDFILES1): $(SLIB)Kni-goal.lev + +$(SLIB)Kni-goal.lev: $(DAT)Knight.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Knight.des +#none + +$(MDFILES1): $(SLIB)Mon-goal.lev + +$(SLIB)Mon-goal.lev: $(DAT)Monk.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Monk.des +#none + +$(PDFILES1): $(SLIB)Pri-goal.lev + +$(SLIB)Pri-goal.lev: $(DAT)Priest.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Priest.des +#none + +$(RDFILES1): $(SLIB)Rog-goal.lev + +$(SLIB)Rog-goal.lev: $(DAT)Rogue.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Rogue.des +#none + +$(RANFILES1): $(SLIB)Ran-goal.lev + +$(SLIB)Ran-goal.lev: $(DAT)Ranger.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Ranger.des +#none + +$(SDFILES1): $(SLIB)Sam-goal.lev + +$(SLIB)Sam-goal.lev: $(DAT)Samurai.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Samurai.des +#none + +$(TDFILES1): $(SLIB)Tou-goal.lev + +$(SLIB)Tou-goal.lev: $(DAT)Tourist.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Tourist.des +#none + +$(VDFILES1): $(SLIB)Val-goal.lev + +$(SLIB)Val-goal.lev: $(DAT)Valkyrie.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Valkyrie.des +#none + +$(WDFILES1): $(SLIB)Wiz-goal.lev + +$(SLIB)Wiz-goal.lev: $(DAT)Wizard.des $(SBIN)lev_comp +#SFD_INSTEAD $(SBIN)lev_comp $(DAT)Wizard.des +#none + +$(SLIB)dungeon: $(DAT)dungeon.def $(SBIN)makedefs $(SBIN)dgn_comp + $(SBIN)makedefs -e + $(SBIN)dgn_comp $(DAT)dungeon.pdf + copy $(DAT)dungeon $(SLIB)dungeon + delete $(DAT)dungeon + +inst-data: $(INSTDATAFILES) + +$(NETHACK)amii.hlp: $(AMI)amii.hlp + copy $(AMI)amii.hlp $@ + +#$(NETHACK)data: $(DAT)data +# copy $(DAT)data $@ + +$(SLIB)data: $(DAT)data.base $(I)config.h $(SBIN)makedefs + $(SBIN)makedefs -d + +#$(NETHACK)rumors: $(DAT)rumors +# copy $(DAT)rumors $@ + +$(SLIB)rumors: $(DAT)rumors.tru $(DAT)rumors.fal $(SBIN)makedefs + $(SBIN)makedefs -r + +$(SLIB)cmdhelp: $(DAT)cmdhelp + copy $(DAT)cmdhelp $@ + +$(SLIB)help: $(DAT)help + copy $(DAT)help $@ + +$(SLIB)hh: $(DAT)hh + copy $(DAT)hh $@ + +$(NETHACK)HackWB.hlp: $(AMI)HackWB.hlp + copy $(AMI)HackWB.hlp $@ + +$(SLIB)history: $(DAT)history + copy $(DAT)history $@ + +$(NETHACK)license: $(DAT)license + copy $(DAT)license $@ + +$(SLIB)opthelp: $(DAT)opthelp + copy $(DAT)opthelp $@ + +$(NETHACK)Recover.txt: $(DOC)Recover.txt + copy $(DOC)Recover.txt $@ + +$(NETHACK)GuideBook.txt: $(DOC)GuideBook.txt + copy $(DOC)GuideBook.txt $@ + +$(NETHACK)NetHack.txt: $(DOC)NetHack.txt + copy $(DOC)NetHack.txt $@ + +$(NETHACK)Install.ami: $(AMI)Install.ami + copy $(AMI)Install.ami $@ + +#SFD_INSTEAD $(NETHACK)logfile: $(NETHACK)logfile +$(NETHACK)logfile: + echo to $@ + +#SFD_INSTEAD $(NETHACK)record: $(NETHACK)record +$(NETHACK)record: + echo to $@ + +$(SLIB)wizhelp: $(DAT)wizhelp + copy $(DAT)wizhelp $@ + +# Create the directories here because NetHack.cnf puts them there by default +$(NETHACK)NetHack.cnf: $(AMI)NetHack.cnf + copy $(AMI)NetHack.cnf $@ + -makedir $(NETHACK)save + -makedir $(NETHACK)levels + +#SFD_BEGIN +#SFD_ELSE +$(O)NetHack.gst: $(GSTSRC) $(I)hack.h + sc makegst=$(GSTFILE) $(CFLAGS) $(GSTSRC) +#SFD_END + +# Unpack and install fonts + +INSTFONTFILES= $(NETHACK)hack.font $(NETHACK)hack $(NETHACK)hack/8 + +inst-fonts: $(INSTFONTFILES) + +$(NETHACK)hack/8: $(AMI)amifont8.uu $(NETHACK)hack + $(UUDEC) $(AMI)amifont8.uu + copy 8 $(NETHACK)hack/8 + delete 8 + +$(NETHACK)hack.font: $(AMI)amifont.uu + $(UUDEC) $(AMI)amifont.uu + copy hack.font $(NETHACK)hack.font + delete hack.font + +#SFD_INSTEAD $(NETHACK)hack: $(NETHACK)hack +$(NETHACK)hack: + -makedir $@ + +INSTICONFILES= \ + $(NETHACK)default.icon $(NETHACK)NetHack.info $(NETHACK)NewGame.info \ + $(NETHACK)HackWB.info + +inst-icons: $(INSTICONFILES) + +# Unpack the icons into place + +$(NETHACK)default.icon: $(AMI)dflticon.uu + $(UUDEC) $(AMI)dflticon.uu +# copy default.icon $(NETHACK)default.icon +# delete default.icon + +$(NETHACK)NetHack.info: $(AMI)NHinfo.uu + $(UUDEC) $(AMI)NHinfo.uu +# copy NetHack.info $(NETHACK)NetHack.info +# delete NetHack.info + +$(NETHACK)NewGame.info: $(AMI)NewGame.uu + $(UUDEC) $(AMI)NewGame.uu +# copy NewGame.info $(NETHACK)NewGame.info +# delete NewGame.info + +$(NETHACK)HackWB.info: $(AMI)HackWB.uu + $(UUDEC) $(AMI)HackWB.uu +# copy HackWB.info $(NETHACK)HackWB.info +# delete HackWB.info + +# If DLB is defined, create the nhdat library file in the playground +# directory. If not, move all the data files there. +$(NETHACK)nhdat: $(LIBFILES) $(SBIN)dlb + list to T:nhdat.lst $(SLIB) QUICK NOHEAD FILES + echo >T:make-nhdat $(SBIN)dlb cCfI $(SLIB) $(NETHACK)nhdat T:nhdat.lst + echo >>T:make-nhdat if not exists $(NETHACK)nhdat + echo >>T:make-nhdat copy $(SLIB)\#? $(NETHACK) + echo >>T:make-nhdat endif + execute T:make-nhdat + -delete T:make-nhdat + +# DO NOT DELETE THIS LINE + +$(O)allmain.o: $(NHS)allmain.c $(HDEP) + +$(O)alloc.o: $(NHS)alloc.c $(I)config.h + +$(O)apply.o: $(NHS)apply.c $(HDEP) $(I)edog.h +#SFD_INSTEAD #none + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)apply.c + +$(O)artifact.o: $(NHS)artifact.c $(HDEP) $(I)artifact.h $(I)artilist.h + +$(O)attrib.o: $(NHS)attrib.c $(HDEP) $(I)artifact.h + +$(O)ball.o: $(NHS)ball.c $(HDEP) + +$(O)bones.o: $(NHS)bones.c $(HDEP) $(I)lev.h + +$(O)botl.o: $(NHS)botl.c $(HDEP) + +$(O)cmd.o: $(NHS)cmd.c $(HDEP) $(I)func_tab.h + +$(O)dbridge.o: $(NHS)dbridge.c $(HDEP) + +$(O)decl.o: $(NHS)decl.c $(HDEP) $(I)quest.h + +$(O)detect.o: $(NHS)detect.c $(HDEP) $(I)artifact.h + +$(O)dig.o: $(NHS)dig.c $(HDEP) $(I)edog.h + +$(O)display.o: $(NHS)display.c $(HDEP) + +$(O)dlb.o: $(NHS)dlb.c $(HDEP) $(I)dlb.h + +$(O)do.o: $(NHS)do.c $(HDEP) $(I)lev.h + +$(O)do_name.o: $(NHS)do_name.c $(HDEP) + +$(O)do_wear.o: $(NHS)do_wear.c $(HDEP) + +$(O)dog.o: $(NHS)dog.c $(HDEP) $(I)edog.h + +$(O)dogmove.o: $(NHS)dogmove.c $(HDEP) $(I)mfndpos.h $(I)edog.h + +$(O)dokick.o: $(NHS)dokick.c $(HDEP) $(I)eshk.h + +$(O)dothrow.o: $(NHS)dothrow.c $(HDEP) + +$(O)drawing.o: $(NHS)drawing.c $(HDEP) $(I)tcap.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)drawing.c + +$(O)dungeon.o: $(NHS)dungeon.c $(HDEP) $(I)dgn_file.h $(I)dlb.h + +$(O)eat.o: $(NHS)eat.c $(HDEP) + +$(O)end.o: $(NHS)end.c $(HDEP) $(I)eshk.h $(I)dlb.h + +$(O)engrave.o: $(NHS)engrave.c $(HDEP) $(I)lev.h + +$(O)exper.o: $(NHS)exper.c $(HDEP) + +$(O)explode.o: $(NHS)explode.c $(HDEP) + +$(O)extralev.o: $(NHS)extralev.c $(HDEP) + +$(O)files.o: $(NHS)files.c $(HDEP) $(I)dlb.h $(I)date.h + +$(O)fountain.o: $(NHS)fountain.c $(HDEP) + +$(O)hack.o: $(NHS)hack.c $(HDEP) + +$(O)hacklib.o: $(NHS)hacklib.c $(HDEP) + +$(O)invent.o: $(NHS)invent.c $(HDEP) $(I)artifact.h + +$(O)light.o: $(NHS)light.c $(HDEP) $(I)lev.h + +$(O)lock.o: $(NHS)lock.c $(HDEP) + +$(O)mail.o: $(NHS)mail.c $(HDEP) $(I)mail.h + +$(O)makemon.o: $(NHS)makemon.c $(HDEP) $(I)epri.h $(I)emin.h $(I)edog.h + +$(O)mapglyph.o: $(NHS)mapglyph.c $(HDEP) + +$(O)mcastu.o: $(NHS)mcastu.c $(HDEP) + +$(O)mhitm.o: $(NHS)mhitm.c $(HDEP) $(I)artifact.h $(I)edog.h + +$(O)mhitu.o: $(NHS)mhitu.c $(HDEP) $(I)artifact.h $(I)edog.h + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)mhitu.c + +$(O)minion.o: $(NHS)minion.c $(HDEP) $(I)emin.h $(I)epri.h + +$(O)mklev.o: $(NHS)mklev.c $(HDEP) + +$(O)mkmap.o: $(NHS)mkmap.c $(HDEP) $(I)sp_lev.h + +$(O)mkmaze.o: $(NHS)mkmaze.c $(HDEP) $(I)sp_lev.h $(I)lev.h + +$(O)mkobj.o: $(NHS)mkobj.c $(HDEP) $(I)artifact.h $(I)prop.h + +$(O)mkroom.o: $(NHS)mkroom.c $(HDEP) + +$(O)mon.o: $(NHS)mon.c $(HDEP) $(I)mfndpos.h $(I)edog.h + +$(O)mondata.o: $(NHS)mondata.c $(HDEP) $(I)eshk.h $(I)epri.h + +$(O)monmove.o: $(NHS)monmove.c $(HDEP) $(I)mfndpos.h $(I)artifact.h + +$(O)monst.o: $(NHS)monst.c $(I)config.h $(I)permonst.h $(I)monsym.h \ + $(I)eshk.h $(I)vault.h $(I)epri.h $(I)color.h + +$(O)monstr.o: $(NHS)monstr.c $(HDEP) +#SFD_INSTEAD $(CC) $(CFLAGS) $(OBJSPEC)%(left) $(NHS)monstr.c +#none + +$(O)mplayer.o: $(NHS)mplayer.c $(HDEP) + +$(O)mthrowu.o: $(NHS)mthrowu.c $(HDEP) + +$(O)muse.o: $(NHS)muse.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)muse.c + +$(O)music.o: $(NHS)music.c $(HDEP) #interp.c + +$(O)o_init.o: $(NHS)o_init.c $(HDEP) $(I)lev.h + +$(O)objects.o: $(NHS)objects.c $(I)config.h $(I)obj.h $(I)objclass.h \ + $(I)prop.h $(I)skills.h $(I)color.h +#SFD_INSTEAD #none + $(CC) $(CFLAGS) $(INCLSPEC)$(NHS) $(OBJSPEC)$@ $(NHS)objects.c + +$(O)objnam.o: $(NHS)objnam.c $(HDEP) + +$(O)options.o: $(NHS)options.c $(HDEP) $(I)tcap.h $(I)config.h \ + $(I)objclass.h $(I)flag.h + +$(O)pager.o: $(NHS)pager.c $(HDEP) $(I)dlb.h + +$(O)pickup.o: $(NHS)pickup.c $(HDEP) + +$(O)pline.o: $(NHS)pline.c $(HDEP) $(I)epri.h + +$(O)polyself.o: $(NHS)polyself.c $(HDEP) + +$(O)potion.o: $(NHS)potion.c $(HDEP) + +$(O)pray.o: $(NHS)pray.c $(HDEP) $(I)epri.h + +$(O)priest.o: $(NHS)priest.c $(HDEP) $(I)mfndpos.h $(I)eshk.h $(I)epri.h \ + $(I)emin.h + +$(O)quest.o: $(NHS)quest.c $(HDEP) $(I)quest.h $(I)qtext.h + +$(O)questpgr.o: $(NHS)questpgr.c $(HDEP) $(I)qtext.h $(I)dlb.h + +$(O)read.o: $(NHS)read.c $(HDEP) + +$(O)rect.o: $(NHS)rect.c $(HDEP) + +$(O)region.o: $(NHS)region.c $(HDEP) + +$(O)restore.o: $(NHS)restore.c $(HDEP) $(I)lev.h $(I)tcap.h $(I)quest.h + +$(O)rnd.o: $(NHS)rnd.c $(HDEP) + +$(O)role.o: $(NHS)role.c $(HDEP) + +$(O)rumors.o: $(NHS)rumors.c $(HDEP) $(I)dlb.h + +$(O)save.o: $(NHS)save.c $(HDEP) $(I)lev.h $(I)quest.h + +$(O)shk.o: $(NHS)shk.c $(HDEP) $(I)eshk.h +#SFD_INSTEAD #none + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)shk.c + +$(O)shknam.o: $(NHS)shknam.c $(HDEP) $(I)eshk.h + +$(O)sit.o: $(NHS)sit.c $(HDEP) $(I)artifact.h + +$(O)sounds.o: $(NHS)sounds.c $(HDEP) $(I)edog.h + +$(O)sp_lev.o: $(NHS)sp_lev.c $(HDEP) $(I)sp_lev.h $(I)rect.h $(I)dlb.h + +$(O)spell.o: $(NHS)spell.c $(HDEP) + +$(O)steal.o: $(NHS)steal.c $(HDEP) + +$(O)steed.o: $(NHS)steed.c $(HDEP) + +$(O)teleport.o: $(NHS)teleport.c $(HDEP) + +$(O)timeout.o: $(NHS)timeout.c $(HDEP) $(I)lev.h + +$(O)topten.o: $(NHS)topten.c $(HDEP) $(I)dlb.h + +$(O)track.o: $(NHS)track.c $(HDEP) + +$(O)trap.o: $(NHS)trap.c $(HDEP) +#SFD_INSTEAD #none + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)trap.c + +$(O)u_init.o: $(NHS)u_init.c $(HDEP) + +$(O)uhitm.o: $(NHS)uhitm.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)uhitm.c + +$(O)vault.o: $(NHS)vault.c $(HDEP) $(I)vault.h + +$(O)version.o: $(NHS)version.c $(HDEP) $(I)date.h $(I)patchlevel.h + +# DMake doesn't grok mid-line comments +#SFD_INSTEAD $(O)vision.o: $(NHS)vision.c $(HDEP) +$(O)vision.o: $(NHS)vision.c $(HDEP) #$(I)vis_tab.h + +$(O)weapon.o: $(NHS)weapon.c $(HDEP) + +$(O)were.o: $(NHS)were.c $(HDEP) + +$(O)wield.o: $(NHS)wield.c $(HDEP) + +$(O)windows.o: $(NHS)windows.c $(HDEP) $(I)wintty.h + +$(O)wizard.o: $(NHS)wizard.c $(HDEP) $(I)qtext.h + +$(O)worm.o: $(NHS)worm.c $(HDEP) $(I)lev.h + +$(O)worn.o: $(NHS)worn.c $(HDEP) + +$(O)write.o: $(NHS)write.c $(HDEP) + +$(O)zap.o: $(NHS)zap.c $(HDEP) + $(CC) $(CFLAGS) $(CFLAGS2) $(OBJSPEC)$@ $(NHS)zap.c + +$(O)getline.o: $(TTY)getline.c $(HDEP) $(I)wintty.h + +$(O)termcap.o: $(TTY)termcap.c $(HDEP) $(I)wintty.h $(I)tcap.h + +$(O)topl.o: $(TTY)topl.c $(HDEP) $(I)wintty.h $(I)tcap.h + +$(O)wintty.o: $(TTY)wintty.c $(HDEP) $(I)wintty.h $(I)tcap.h \ + $(I)patchlevel.h + +$(O)amitty.o: $(AMI)amitty.c $(HDEP) + +$(O)amistack.o: $(AMI)amistack.c + $(CC) $(CFLAGS3) $(CSYM) $(OBJSPEC)$@ $(AMI)amistack.c + +$(O)rip.o: $(NHS)rip.c $(HDEP) + + +$(I)config.h: $(I)config1.h $(I)tradstdc.h $(I)global.h + -setdate $(I)config.h + -wait 2 + +# onames.h handled at onames.h target, pm.h + +$(I)decl.h: $(I)quest.h $(I)spell.h $(I)color.h $(I)obj.h $(I)you.h + -setdate $(I)decl.h + -wait 2 + +$(I)global.h: $(I)coord.h $(I)pcconf.h $(I)amiconf.h + -setdate $(I)global.h + -wait 2 + +$(I)hack.h: $(I)config.h $(I)trap.h $(I)decl.h $(I)dungeon.h $(I)monsym.h \ + $(I)mkroom.h $(I)objclass.h $(I)flag.h $(I)rm.h $(I)vision.h \ + $(I)display.h $(I)wintype.h $(I)engrave.h $(I)rect.h \ + $(I)region.h $(I)trampoli.h + -setdate $(I)hack.h + -wait 2 + +$(I)permonst.h: $(I)monattk.h $(I)monflag.h $(I)align.h + -setdate $(I)permonst.h + -wait 2 + +$(I)you.h: $(I)align.h $(I)attrib.h $(I)monst.h $(I)youprop.h $(I)skills.h + -setdate $(I)you.h + -wait 2 + +# pm.h handled at target + +$(I)youprop.h: $(I)prop.h $(I)permonst.h $(I)mondata.h + -setdate $(I)youprop.h + -wait 2 + +$(I)display.h: $(I)vision.h $(I)mondata.h + -setdate $(I)display.h + -wait 2 + +$(I)dungeon.h: $(I)align.h + -setdate $(I)dungeon.h + -wait 2 + +$(I)emin.h: $(I)dungeon.h + -setdate $(I)emin.h + -wait 2 + +$(I)epri.h: $(I)dungeon.h $(I)align.h + -setdate $(I)epri.h + -wait 2 + +$(I)eshk.h: $(I)dungeon.h + -setdate $(I)eshk.h + -wait 2 + +$(I)engrave.h: $(I)trampoli.h $(I)rect.h + -setdate $(I)engrave.h + -wait 2 + +$(I)mondata.h: $(I)align.h + -setdate $(I)mondata.h + -wait 2 + +$(I)monst.h: $(I)align.h + -setdate $(I)monst.h + -wait 2 + +$(I)pcconf.h: $(I)micro.h $(I)system.h + -setdate $(I)pcconf.h + -wait 2 + +$(I)rm.h: $(I)align.h + -setdate $(I)rm.h + -wait 2 + +$(I)vault.h: $(I)dungeon.h + -setdate $(I)vault.h + -wait 2 + + +#notes +# install keeps doing re-install because it keeps rebuilding lev_comp??? +# fixed(?) - deleted setdate +# make nhdat rebuils sys/amiga objects diff --git a/sys/amiga/NetHack.cnf b/sys/amiga/NetHack.cnf new file mode 100644 index 0000000..89794c0 --- /dev/null +++ b/sys/amiga/NetHack.cnf @@ -0,0 +1,213 @@ +# A '#' at the beginning of a line means the rest of the line is a comment. + +# This is an example configuration file. +# If several people are to use it, don't specify "name" or personal +# prefences like "dogname" or "packorder" in OPTIONS. + +# To change configuration, comment out the unwanted configurations, and +# remove the comment from the configuration you want. + +# Some options to set personal preferences. If several people are to +# use it, options like these should not be set here - use the command line +#OPTIONS=name:Janet-V,female,dogname:Fido,fruit:apricot +#OPTIONS=packorder:")[%?+/=!(*0_`,scores:10t/2a,noverbose +#OPTIONS=gender:male +#OPTIONS=role:random +#OPTIONS=race:random +#OPTIONS=align:chaotic +# Other general options +#OPTIONS=time,rest_on_space,noautopickup + +# The search path for files like record, help, opthelp, etc. +PATH=NetHack: + +# My own setup +#OPTIONS=nolegacy,fruit:lemon,time,autopickup,checkpoint,showexp,showscore,standout,nonews +#OPTIONS=nomail,flush,eight_bit_tty,scores:10t/2a,pickup_types:$,suppress_alert:3.3.0,autoquiver + +# The windowtype option must be set before any options regarding colors and palette +# are set otherwise previously set values will be overridden by the defaults +# +# The font version of the game +OPTIONS=windowtype:amii +# +# New tile version of the game +#OPTIONS=windowtype:amitile +# +# A hard disk configuration. +# +HACKDIR=NetHack: +LEVELS=Nethack:Levels +SAVE=Nethack:Save +BONESDIR=Nethack:Levels +SCOREDIR=Nethack: +LOCKDIR=Nethack: +CONFIGDIR=Nethack: +DATADIR=Nethack: +TROUBLEDIR=Nethack: + +# *** CHARACTER GRAPHICS *** +# +# See the on-line help or the Guidebook for which symbols are in which +# positions. +# +# Note that the hack.font has special graphics characters from 192 on. + +# An example using the hack.font graphics character set: +DUNGEON = 032 192 193 194 195 196 197 198 216 214 \ + 215 213 217 145 146 147 148 035 035 217 \ + 218 229 060 062 060 062 095 124 092 035 \ + 123 125 042 125 042 042 035 035 046 035 \ + 125 + +TRAPS = 094 094 094 094 094 094 094 094 094 094 \ + 094 094 094 094 094 094 094 034 094 094 \ + 094 094 + +EFFECTS = 241 240 242 243 042 033 123 125 \ + 064 038 042 035 \ + 244 245 246 247 239 248 249 250 \ + 230 234 231 236 212 237 232 235 233 + +WARNINGS = 048 049 050 051 052 053 + +# Monitors vary greatly in their color response. If the default colors +# are not good on your monitor, here are some other alternatives for the +# font version of the game: +# +# Last color of the palette is always used for the cursor. +# +#CBM 1960, set color/contrast for good pure red, green, and blue. True colors. +#PENS=000,fff,a61,7bb,0f0,e0c,00f,f00 +#CBM 1960, set color/contrast as above, better colors for NetHack. +#PENS=667,fff,da1,7bb,2f0,e0d,0af,f42 +#and other suggestions: +#PENS=888,ffc,840,0b8,4e4,e8b,7be,a04 +#PENS=000,fff,830,7ae,181,c06,23e,c00 +# +# For an "interlaced"+ line screen, the default font is courier:13. If you want +# a different font, set it here. The format is "fontname.font:size"; i.e. the +# .font extension is required. +#FONT=courier.font:13 +#FONT=topaz.font:8 +# +# Proportional fonts such as CGTimes are probably not a good idea because they +# result in many things not being spaced out correctly. +#FONT=CGTimes.font:15 +# +# This sized proportional font is readable, but still has spacing problems +#FONT=CGTimes.font:21 +# +# FOR AGA OR OTHER DISPLAYS CAPABLE OF 5 OR MORE PLANES... +# +# For a screen of depth 5 the following dripens provide a brown border +# using pens 16-31. +# +# Pens 16-31 can be redefined with PENS= if you want different colors, +# using the PENS= values below for a 4 plane screen as the first 16 colors. +# +#DEPTH=5 +#DRIPENS=0,0,0,17,27,23,1,23,15,0,23,27 +# +# The APEN and BPEN values in the various types of windows can be set in +# the manner shown below. These values are for the 16 color version of +# the tile game. +# +# These values are specified as APEN,BPEN (foreground,background) +# +#MSGPENS=1,12 +#STATUSPENS=1,12 +#MENUPENS=1,23 +#TEXTPENS=1,23 +#OTHERPENS=1,23 +# +# FOR ECS OR OTHERS ONLY CAPABLE OF 4 PLANES... +# +# These values work good for the TILE version of the game on ECS machines +# These are the default values for reference purposes. +# +#DEPTH=4 +#defaults for tilemode: +#PENS=000,fff,0bf,f60,00f,090,69b,f00,6f0,ff0,f0f,940,466,c40,ddb,fb9 +#DRIPENS=0,1,0,2,4,12,14,12,7,1,12,4 +#defaults for fontmode: +#PENS=000,fff,830,7ac,181,c06,23e,c00 +#DRIPENS=0,6,2,1,6,3,1,3,7,1,3,6 +# +# The APEN and BPEN values in the various types of windows can be set in +# the manner shown below. These values are for a 32 color version of +# the tile game. +# +# These values are specified as APEN,BPEN (foreground,background) +# +#MSGPENS=1,12 +#STATUSPENS=1,12 +#MENUPENS=0,14 +#TEXTPENS=0,14 +#OTHERPENS=1,12 +# +# New alternative color scheme for 16 color font mode. +# This changes the colors of monsters, objects etc. +# +# FGPENS and BGPENS define APEN and BPEN for objects and monsters on the map. +# The colors are in the following order: +# black, red, green, brown, blue, magenta, cyan, gray, no color, orange, +# bright green, yellow, bright blue, bright magenta, bright cyan, white +# +DEPTH=4 +PENS=000,fff,830,7ac,181,c06,23e,c00,888,f60,4f4,ff0,4af,f8f,8ff,f00 +FGPENS= 0, 7, 4, 2, 6, 5, 3, 8, 1, 9,10,11,12,13,14, 1 +BGPENS= 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +# +# Screen mode selections below should all work for either the font or tile +# version of the game. Other modes can be tried and as long as they are at +# least 640x200, the game should adapt to them... +# +# Select screenmode with a requester +#SCREENMODE=Req +# NTSC_MONITOR_ID +#SCREENMODE=0x00019000 +# PAL_MONITOR_ID +#SCREENMODE=0x00029000 +# NTSC_MONITOR_ID+LACE +#SCREENMODE=0x00019004 +# PAL_MONITOR_ID+LACE +#SCREENMODE=0x00029004 +# NTSC_MONITOR_ID+HIRES+LACE +#SCREENMODE=0x00019024 +# PAL_MONITOR_ID+HIRES+LACE +#SCREENMODE=0x00029024 +# VGA_MONITOR_ID +#SCREENMODE=0x00031000 +# VGAPRODUCT_KEY +#SCREENMODE=0x00039024 +# A2024TENHERTZ_KEY +#SCREENMODE=0x00041000 +# A2024FIFTEENHERTZ_KEY +#SCREENMODE=0x00049000 +# EURO72_MONITOR_ID +#SCREENMODE=0x00061000 +# EURO72PRODUCT_KEY +#SCREENMODE=0x00069024 +# EURO72PRODUCTLACE_KEY +#SCREENMODE=0x00069025 +# EURO72PRODUCTDBL_KEY +#SCREENMODE=0x00069020 +# EURO36_MONITOR_ID +#SCREENMODE=0x00071000 +# SUPER72HIRESDBL_KEY +#SCREENMODE=0x00089008 +# SUPER72SUPERDBL_KEY +#SCREENMODE=0x00089028 +# DBLNTSCHIRES_KEY +#SCREENMODE=0x00099000 +# DBLNTSCHIRESFF_KEY +#SCREENMODE=0x00099004 +# DBLNTSCHIRESLACE_KEY +#SCREENMODE=0x00099005 +# DBLPALHIRES_KEY +#SCREENMODE=0x000a9000 +# DBLPALHIRESFF_KEY +#SCREENMODE=0x000a9004 +# DBLPALHIRESLACE_KEY +#SCREENMODE=0x000a9005 diff --git a/sys/amiga/amidos.c b/sys/amiga/amidos.c new file mode 100644 index 0000000..e5a975c --- /dev/null +++ b/sys/amiga/amidos.c @@ -0,0 +1,490 @@ +/* SCCS Id: @(#)amidos.c 3.2 2000/01/12 +/* Copyright (c) Olaf Seibert, Nijmegen, The Netherlands, 1988,1990. */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1991,1992,1993,1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * An assortment of imitations of cheap plastic MSDOS and Unix functions. + */ + +#include "hack.h" +#include "winami.h" + +/* Defined in config.h, let's undefine it here (static function below) */ +#undef strcmpi + +#include +#include +#include + +#undef COUNT +#if defined(__SASC_60) || defined(__GNUC__) +# include +# include +#endif + +#ifdef AZTEC_50 +# include +# undef strcmpi +#endif + +/* Prototypes */ +#include "NH:sys/amiga/winami.p" +#include "NH:sys/amiga/amiwind.p" +#include "NH:sys/amiga/amidos.p" + +extern char Initialized; +extern struct window_procs amii_procs; + +#ifndef __SASC_60 +int Enable_Abort = 0; /* for stdio package */ +#endif + +/* Initial path, so we can find NetHack.cnf */ +char PATH[PATHLEN] = "NetHack:"; + +static boolean record_exists(void); + +void +flushout() +{ + (void) fflush(stdout); +} + +#ifndef getuid +getuid() +{ + return 1; +} +#endif + +#ifndef getlogin +char * +getlogin() +{ + return ((char *) NULL); +} +#endif + +#ifndef AZTEC_50 +int +abs(x) +int x; +{ + return x < 0? -x: x; +} +#endif + +#ifdef SHELL +int +dosh() +{ + int i; + char buf[ BUFSZ ]; + extern struct ExecBase *SysBase; + + /* Only under 2.0 and later ROMs do we have System() */ + if( SysBase->LibNode.lib_Version >= 37 && !amibbs) + { + getlin("Enter CLI Command...", buf ); + if (buf[0] != '\033') + i = System( buf, NULL ); + } + else + { + i = 0; + pline("No mysterious force prevented you from using multitasking."); + } + return i; +} +#endif /* SHELL */ + +#ifdef MFLOPPY +# include + +# define Sprintf (void) sprintf + +#define EXTENSION 72 + +/* + * This routine uses an approximation of the free bytes on a disk. + * How large a file you can actually write depends on the number of + * extension blocks you need for it. + * In each extenstion block there are maximum 72 pointers to blocks, + * so every 73 disk blocks have only 72 available for data. + * The (necessary) file header is also good for 72 data block pointers. + */ +/* TODO: update this for FFS */ +long +freediskspace(path) +char *path; +{ + register long freeBytes = 0; + register struct InfoData *infoData; /* Remember... longword aligned */ + char fileName[32]; + + /* + * Find a valid path on the device of which we want the free space. + * If there is a colon in the name, it is an absolute path + * and all up to the colon is everything we need. + * Remember slashes in a volume name are allowed! + * If there is no colon, it is relative to the current directory, + * so must be on the current device, so "" is enough... + */ + { + register char *colon; + + strncpy(fileName, path, sizeof(fileName) - 1); + fileName[31] = 0; + if (colon = index(fileName, ':')) + colon[1] = '\0'; + else + fileName[0] = '\0'; + } + { + BPTR fileLock; + infoData = (struct InfoData *) alloc(sizeof(struct InfoData)); + if (fileLock = Lock(fileName, SHARED_LOCK)) { + if (Info(fileLock, infoData)) { + /* We got a kind of DOS volume, since we can Lock it. */ + /* Calculate number of blocks available for new file */ + /* Kludge for the ever-full VOID: (oops RAM:) device */ + if (infoData->id_UnitNumber == -1 && + infoData->id_NumBlocks == infoData->id_NumBlocksUsed) { + freeBytes = AvailMem(0L) - 64 * 1024L; + /* Just a stupid guess at the */ + /* Ram-Handler overhead per block: */ + freeBytes -= freeBytes/16; + } else { + /* Normal kind of DOS file system device/volume */ + freeBytes = infoData->id_NumBlocks - infoData->id_NumBlocksUsed; + freeBytes -= (freeBytes + EXTENSION) / (EXTENSION + 1); + freeBytes *= infoData->id_BytesPerBlock; + } + if (freeBytes < 0) + freeBytes = 0; + } + UnLock(fileLock); + } + free(infoData); + return freeBytes; + } +} + + +long +filesize(file) +char *file; +{ + register BPTR fileLock; + register struct FileInfoBlock *fileInfoBlock; + register long size = 0; + + fileInfoBlock = (struct FileInfoBlock *)alloc(sizeof(struct FileInfoBlock)); + if (fileLock = Lock(file, SHARED_LOCK)) { + if (Examine(fileLock, fileInfoBlock)) { + size = fileInfoBlock->fib_Size; + } + UnLock(fileLock); + } + free(fileInfoBlock); + return size; +} + +#if 0 +void +eraseall(path, files) +const char *path, *files; +{ + BPTR dirLock, dirLock2; + struct FileInfoBlock *fibp; + int chklen; +#ifdef BETA + if(files != alllevels)panic("eraseall"); +#endif + chklen=(int)index(files,'*')-(int)files; + + if (dirLock = Lock( (char *)path ,SHARED_LOCK)) { + dirLock2=DupLock(dirLock); + dirLock2= CurrentDir(dirLock2); + fibp=AllocMem(sizeof(struct FileInfoBlock),0); + if(fibp){ + if(Examine(dirLock,fibp)){ + while(ExNext(dirLock,fibp)){ + if(!strncmp(fibp->fib_FileName,files,chklen)){ + DeleteFile(fibp->fib_FileName); + } + } + } + FreeMem(fibp,sizeof(struct FileInfoBlock)); + } + UnLock(dirLock); + UnLock(CurrentDir(dirLock2)); + } +} +#endif + +/* This size makes that most files can be copied with two Read()/Write()s */ + +#if 0 /* Unused */ +#define COPYSIZE 4096 + +char *CopyFile(from, to) +const char *from, *to; +{ + register BPTR fromFile, toFile; + register char *buffer; + register long size; + char *error = NULL; + + buffer = (char *) alloc(COPYSIZE); + if (fromFile = Open( (char *)from, MODE_OLDFILE)) { + if (toFile = Open( (char *)to, MODE_NEWFILE)) { + while (size = Read(fromFile, buffer, (long)COPYSIZE)) { + if (size == -1){ + error = "Read error"; + break; + } + if (size != Write(toFile, buffer, size)) { + error = "Write error"; + break; + } + } + Close(toFile); + } else + error = "Cannot open destination"; + Close(fromFile); + } else + error = "Cannot open source (this should not occur)"; + free(buffer); + return error; +} +#endif + +/* this should be replaced */ +saveDiskPrompt(start) +{ + char buf[BUFSIZ], *bp; + BPTR fileLock; + + if (flags.asksavedisk) { + /* Don't prompt if you can find the save file */ + if (fileLock = Lock(SAVEF, SHARED_LOCK)) { + UnLock(fileLock); +#if defined(TTY_GRAPHICS) + if(windowprocs.win_init_nhwindows!=amii_procs.win_init_nhwindows) + clear_nhwindow( WIN_MAP ); +#endif +#if defined(AMII_GRAPHICS) + if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows) + clear_nhwindow( WIN_BASE ); +#endif + return 1; + } + pline( "If save file is on a SAVE disk, put that disk in now." ); + if( strlen( SAVEF ) > QBUFSZ - 25 - 22 ) + panic( "not enough buffer space for prompt" ); +/* THIS IS A HACK */ +#if defined(TTY_GRAPHICS) + if(windowprocs.win_init_nhwindows!=amii_procs.win_init_nhwindows){ + getlin("File name ?",buf); + clear_nhwindow( WIN_MAP ); + } +#endif +#if defined(AMII_GRAPHICS) + if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows){ + getlind("File name ?", buf, SAVEF); + clear_nhwindow( WIN_BASE ); + } +#endif + clear_nhwindow( WIN_MESSAGE); + if (!start && *buf == '\033') + return 0; + + /* Strip any whitespace. Also, if nothing was entered except + * whitespace, do not change the value of SAVEF. + */ + for (bp = buf; *bp; bp++) { + if (!isspace(*bp)) { + strncpy(SAVEF, bp, PATHLEN); + break; + } + } + } + return 1; +} + +/* Return 1 if the record file was found */ +static boolean +record_exists() +{ + FILE *file; + + if (file = fopenp(RECORD, "r")) { + fclose(file); + return TRUE; + } + return FALSE; +} + +#ifdef MFLOPPY +/* + * Under MSDOS: Prompt for game disk, then check for record file. + * For Amiga: do nothing, but called from restore.c + */ +void +gameDiskPrompt(){} +#endif + +/* + * Add a slash to any name not ending in / or :. There must + * be room for the /. + */ +void +append_slash(name) +char *name; +{ + char *ptr; + + if (!*name)return; + + ptr = eos(name) - 1; + if (*ptr != '/' && *ptr != ':') { + *++ptr = '/'; + *++ptr = '\0'; + } +} + + +void +getreturn(str) +const char *str; +{ + int ch; + + raw_printf("Hit %s.", str); + while ((ch = nhgetch()) != '\n' && ch != '\r' ) + continue; +} + +/* Follow the PATH, trying to fopen the file. + */ +#define PATHSEP ';' + +FILE * +fopenp(name, mode) +register const char *name, *mode; +{ + register char *bp, *pp, lastch; + register FILE *fp; + register BPTR theLock; + char buf[BUFSIZ]; + + /* Try the default directory first. Then look along PATH. + */ + if (strlen(name) >= BUFSIZ) return( NULL ); + strcpy(buf, name); + if (theLock = Lock(buf, SHARED_LOCK)) { + UnLock(theLock); + if (fp = fopen(buf, mode)) + return fp; + } + pp = PATH; + while (pp && *pp) { + bp = buf; + while (*pp && *pp != PATHSEP){ + if( bp > buf + BUFSIZ - 1 ) return( NULL ); + lastch = *bp++ = *pp++; + } + if (lastch != ':' && lastch != '/' && bp != buf) + *bp++ = '/'; + if (bp + strlen(name) > buf + BUFSIZ - 1) return( NULL ); + strcpy(bp, name); + if (theLock = Lock(buf, SHARED_LOCK)) { + UnLock(theLock); + if (fp = fopen(buf, mode)) return fp; + } + if (*pp) + pp++; + } + return NULL; +} +#endif /* MFLOPPY */ + +#ifdef CHDIR + +/* + * A not general-purpose directory changing routine. + * Assumes you want to return to the original directory eventually, + * by chdir()ing to orgdir (which is defined in pcmain.c). + * Assumes -1 is not a valid lock, since 0 is valid. + */ + +#define NO_LOCK ((BPTR) -1) + +static BPTR OrgDirLock = NO_LOCK; + +chdir(dir) +char *dir; +{ + extern char orgdir[]; + + if (dir == orgdir) { + /* We want to go back to where we came from. */ + if (OrgDirLock != NO_LOCK) { + UnLock(CurrentDir(OrgDirLock)); + OrgDirLock = NO_LOCK; + } + } else { + /* + * Go to some new place. If still at the original + * directory, save the FileLock. + */ + BPTR newDir; + + if (newDir = Lock( (char *)dir, SHARED_LOCK)) { + if (OrgDirLock == NO_LOCK) { + OrgDirLock = CurrentDir(newDir); + } else { + UnLock(CurrentDir(newDir)); + } + } else { + return -1; /* Failed */ + } + } + /* CurrentDir always succeeds if you have a lock */ + return 0; +} + +#endif /* CHDIR */ + +/* Chdir back to original directory + */ +#undef exit +void +nethack_exit(code) +{ +#ifdef CHDIR + extern char orgdir[]; +#endif + +#ifdef CHDIR + chdir(orgdir); /* chdir, not chdirx */ +#endif + +#ifdef AMII_GRAPHICS + if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows) + CleanUp(); +#endif + exit(code); +} + +void +regularize(s) /* normalize file name - we don't like :'s or /'s */ +register char *s; +{ + register char *lp; + + while((lp = index(s, ':')) || (lp = index(s, '/'))) + *lp = '_'; +} diff --git a/sys/amiga/amidos.p b/sys/amiga/amidos.p new file mode 100644 index 0000000..ee91b3a --- /dev/null +++ b/sys/amiga/amidos.p @@ -0,0 +1,42 @@ +/* SCCS Id: @(#)amidos.p 3.1 93/01/08 +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1992, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* amidos.c */ +void NDECL(flushout ); +#ifndef getuid +int NDECL(getuid ); +#endif +#ifndef getpid +int NDECL(getpid ); +#endif +#ifndef getlogin +char *NDECL(getlogin ); +#endif +#ifndef abs +int FDECL(abs, (int )); +#endif +int NDECL(tgetch ); +int NDECL(dosh ); +long FDECL(freediskspace, (char *)); +long FDECL(filesize, (char *)); +void FDECL(eraseall, (const char * , const char *)); +char *FDECL(CopyFile, (const char * , const char *)); +void FDECL(copybones, (int )); +void NDECL(playwoRAMdisk ); +int FDECL(saveDiskPrompt, (int )); +void NDECL(gameDiskPrompt ); +void FDECL(append_slash, (char *)); +void FDECL(getreturn, (const char *)); +#ifndef msmsg +void FDECL(msmsg, ( const char *, ... )); +#endif +#if !defined(__SASC_60) && !defined(_DCC) +int FDECL(chdir, (char *)); +#endif +#ifndef strcmpi +int FDECL(strcmpi, (char * , char *)); +#endif +#if !defined(memcmp) && !defined(AZTEC_C) && !defined(_DCC) && !defined(__GNUC__) +int FDECL(memcmp, (unsigned char * , unsigned char * , int )); +#endif diff --git a/sys/amiga/amifont.uu b/sys/amiga/amifont.uu new file mode 100644 index 0000000..3290ddc --- /dev/null +++ b/sys/amiga/amifont.uu @@ -0,0 +1,9 @@ +begin 777 hack.font +M#P```6AA8VLO.``````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +G````````````````````````````````````````````````"`!` +` +end diff --git a/sys/amiga/amifont8.uu b/sys/amiga/amifont8.uu new file mode 100644 index 0000000..c6120e1 --- /dev/null +++ b/sys/amiga/amifont8.uu @@ -0,0 +1,59 @@ +begin 644 8 +M```#\P`````````!``````````````)E```#Z0```F5P_TYU```````````, +M`````!H/@``!``````````````````````````````````````````!&140` +M```````````,`````!H`````"20`"`!```@`!@`!```@_P```&X`M```!@X` +M```````````8;&P8`#@8##````````,\&#P\''X?G +MYVH```!J:A@````8&!@``!@88L!6`%96```8`!@8`*@5P8.BP:@5YP/_[W_^ +M````P,```P`\;&P^QFP8&!AF&`````9F.&9F/&`P9F9F&!@8`!AFQCQF9FQF +M9F9F&`9F8,;F;&9L9F9:9L/&9L/&,&`,.``8`&``!@`V`&```&`8```````` +M`!@````````8&!B<,Z6BH:&EI*2AI:6EI*2AI*3__\,8`&:DH:6EI:2DH:2D +MI58```!65CP````8&!@``!@\96-J`&IJ```8`!@8UB/$`\`C+"O4A`/_]Y_Y +M````P,```P`\`/Y@S&@P,`P\&`````QN&`8&;'Q@!F9F&!@P?@P&WCQFP&9@ +M8,!F&`9L8.[VQF;&9G`89F;&/&:,,#`,;``,/&P\-CPP.VPX!F889GP\W#WL +M/CYF9F-C9GX8&!@`S.7BY^?GY^?AY^?GY^3GY^?_`.<8`,/GX>?GY^?DY^?G +MY6K_#_!KZCP````8&!@``!AF8#97_];7`&8<`#@\;&I6)F1I/&F6YP./]]_[ +M#__PP,```P`8`&P\&'8`,`S_?@!^`!A^&!P,Y^&`9X8/[>QGS&?#@89F;6&#P8,!@,Q@``!G9F;F9X9G88!FP8=V9F9F9V +M8!AF9FLV9DQP&`X`,Z6BI*&AH:6AI:&EI:2EI*3_`.<8_P`E(24A)24D)20D +MI595/5Q55F;_#_`/\/\``-O#?AMJ5:JJ```?__C_JIPY:];X*]I;A`-F[\_S +M.``,/\//C^ +M\#YF?CSF_L;&./`\XSP\/AC&PSS^/`,\````.SP\.SQXQN8\9N8\8V8\8`;P +M?`P[ C&'X.&'``S`````````````````````#__\,8`&8````````````` +M`&H`:VH``,,`&!@``!B<&#P89F-6:E8``&88&!@`K`/`(\0L(T&"``/_W_F? +MP``#P````````````````````#```````````````````#`````````````` +M```````````````&`````````````````/X`````````?```/```````\`<` +M````````<```````,P````````````````````#__\,8`&8````````````` +M`%8`5E8`````&!@``!@``!@8`,!J5FH````8&!@``,&#J!7!HL&#``/_[_Y_ +MP``#P`````````@`"``(`!``"``8``@`(``(`"@`"``P``@`.``(`$``"`!( +M``@`4``(`%@`"`!@``@`:``(`'``"`!X``@`@``(`(@`"`"0``@`F``(`*`` +M"`"H``@`L``(`+@`"`#```@`R``(`-``"`#8``@`X``(`.@`"`#P``@`^``( +M`0``"`$(``@!$``(`1@`"`$@``@!*``(`3``"`$X``@!0``(`4@`"`%0``@! +M6``(`6``"`%H``@!<``(`7@`"`&```@!B``(`9``"`&8``@!H``(`:@`"`&P +M``@!N``(`<``"`'(``@!T``(`=@`"`'@``@!Z``(`?``"`'X``@"```(`@@` +M"`(0``@"&``(`B``"`(H``@",``(`C@`"`)```@"2``(`E``"`)8``@"8``( +M`F@`"`)P``@">``(`H``"`*(``@"D``(`I@`"`*@``@"J``(`K``"`*X``@" +MP``(`L@`"`+0``@"V``(`N``"`+H``@"\``(`O@`"`,```@#"``(`Q``"`,8 +M``@#(``(`R@`"`,P``@#.``(`T``"`-(``@#4``(`U@`"`-@``@#:``(`W`` +M"`-X``@#@``(`X@`"`.0``@#F``(`Z``"`.H``@#L``(`[@`"`/```@#R``( +M`]``"`/8``@#X``(`^@`"`/P``@#^``(!```"``(``@`$``(`!@`"``@``@` +M*``(`#``"``X``@`0``(`$@`"`!0``@`6``(`&``"`!H``@`<``(`'@`"`"` +M``@`B``(`)``"`"8``@`H``(`*@`"`"P``@`N``(`,``"`#(``@`T``(`-@` +M"`#@``@`Z``(`/``"`#X``@$"``(!!``"`08``@$(``(!"@`"`0P``@#J``( +M!#@`"`.8``@$0``(!$@`"`10``@$6``(!&``"`1H``@$<``(!'@`"`2```@$ +MB``(!)``"`28``@$H``(!*@`"`2P``@$N``(!,``"`3(``@!V``(`>``"`'H +M``@!\``(`?@`"`(```@$T``(!-@`"`3@``@$Z``(!/``"`3X``@%```(!0@` +M"`40``@%&``(!2``"`4H``@%,``(!3@`"`5```@%2``(!5``"`58``@%8``( +M!6@`"`5P``@%>``(!8``"`6(``@%D``(!9@`"`+8``@"X``(`N@`"`+P``@" +M^``(````"``````#[`````0`````````#@```$0```!<````8@````````/R +` +end diff --git a/sys/amiga/amigst.c b/sys/amiga/amigst.c new file mode 100644 index 0000000..04859c4 --- /dev/null +++ b/sys/amiga/amigst.c @@ -0,0 +1,43 @@ +/* SCCS Id: @(#)amigst.c 3.1 93/01/08 +/* Copyright (c) Gregg Wonderly, Naperville, IL, 1992, 1993 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef strcmpi +#include +#include + +#ifdef __SASC +#include /* for __emit */ +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "hack.h" +#include "winprocs.h" +#include "winami.h" + +#ifdef AZTEC +#include +#endif + +#include "NH:sys/amiga/winami.p" +#include "NH:sys/amiga/amiwind.p" +#include "NH:sys/amiga/amidos.p" + +/* end amigst.c */ diff --git a/sys/amiga/amii.hlp b/sys/amiga/amii.hlp new file mode 100644 index 0000000..c014e42 --- /dev/null +++ b/sys/amiga/amii.hlp @@ -0,0 +1,31 @@ + Amiga-specific help file for NetHack 3.4 + +The Amiga port of NetHack supports a number of additional commands +and facilities specific to the Amiga. Listed below are the things +which are either specific to the Amiga port or might not appear +in other ports. + +While playing NetHack you can press: + + ALT-HELP Color requestor. + CTL-HELP Scale display (amitile only). + SHIFT-HELP Overview window (amitile only). + +Amiga-specific run-time options: + altmeta use the alt keys as meta keys + flush throw away keyboard type-ahead + +Command line options recognized are + + -n No News at game startup. + -X Play in discovery mode. + -D Play in debug mode. + -L Interlaced screen. + -l Never Interlaced screen. + -u Play as player given as + an argument. + -r Pick a race given as an + argument. + -p Pick a profession given + as an argument + -? Gives command line usage. diff --git a/sys/amiga/amimenu.c b/sys/amiga/amimenu.c new file mode 100644 index 0000000..422dfba --- /dev/null +++ b/sys/amiga/amimenu.c @@ -0,0 +1,95 @@ +/* SCCS Id: @(#)amimenu.c 3.2 96/02/04 */ +/* Copyright (c) Olaf 'Rhialto' Seibert, 1989 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1992, 1993, 1996 */ +/* Copyright (c) Janne Salmijärvi, 2000 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Originally by John Toebes. */ + +/* GadTools menus by jhsa */ + +struct NewMenu GTHackMenu[] = { + { NM_TITLE, "Commands", 0, 0, 0, 0}, + { NM_ITEM, "? Display help menu", 0, 0, 0, (void *)'?'}, + { NM_ITEM, "& Explain a command", 0, 0, 0, (void *)'&'}, + { NM_ITEM, "O Set options", 0, 0, 0, (void *)'O'}, + { NM_ITEM, "! AmigaDos command", 0, 0, 0, (void *)'!'}, + { NM_ITEM, "v Version number", 0, 0, 0, (void *)'v'}, + { NM_ITEM, "V Long version and game history", 0, 0, 0, (void *)'V'}, + { NM_ITEM, "^R Redraw screen", 0, 0, 0, (void *)022}, + { NM_ITEM, "^P Repeat previous messages", 0, 0, 0, (void *)020}, + { NM_ITEM, "M-q #quit the game", 0, 0, 0, (void *)(128+'q')}, + { NM_ITEM, "S Save the game", 0, 0, 0, (void *)'S'}, + { NM_TITLE, "Inventory", 0, 0, 0, 0}, + { NM_ITEM, "i Inventory", 0, 0, 0, (void *)'i'}, + { NM_ITEM, "p Pay your bill", 0, 0, 0, (void *)'p'}, + { NM_ITEM, "d Drop an object", 0, 0, 0, (void *)'d'}, + { NM_ITEM, "D Drop several things", 0, 0, 0, (void *)'D'}, + { NM_ITEM, ", Pickup an object", 0, 0, 0, (void *)','}, + { NM_ITEM, "@ Toggle pickup", 0, 0, 0, (void *)'@'}, + { NM_ITEM, "/ Identify something", 0, 0, 0, (void *)'/'}, + { NM_ITEM, "C Christen a monster", 0, 0, 0, (void *)'C'}, + { NM_ITEM, "+ List known spells", 0, 0, 0, (void *)'+'}, + { NM_ITEM, "$ Your gold", 0, 0, 0, (void *)'$'}, + { NM_TITLE, "Actions", 0, 0, 0, 0}, + { NM_ITEM, "a Apply/use something", 0, 0, 0, (void *)'a'}, + { NM_ITEM, "e Eat something", 0, 0, 0, (void *)'e'}, + { NM_ITEM, "f Fire ammunition", 0, 0, 0, (void *)'f'}, + { NM_ITEM, "F Fight a monster", 0, 0, 0, (void *)'F'}, + { NM_ITEM, "q Quaff a monster", 0, 0, 0, (void *)'q'}, + { NM_ITEM, "r Read scroll/book", 0, 0, 0, (void *)'r'}, + { NM_ITEM, "t Throw something", 0, 0, 0, (void *)'t'}, + { NM_ITEM, "z Zap a wand", 0, 0, 0, (void *)'z'}, + { NM_ITEM, "Z Cast a spell", 0, 0, 0, (void *)'Z'}, + { NM_TITLE, "Preparations", 0, 0, 0, 0}, + { NM_ITEM, "A Remove all armor", 0, 0, 0, (void *)'A'}, + { NM_ITEM, "P Put on a ring", 0, 0, 0, (void *)'P'}, + { NM_ITEM, "R Remove ring", 0, 0, 0, (void *)'R'}, + { NM_ITEM, "Q Select ammunition for quiver", 0, 0, 0, (void *)'Q'}, + { NM_ITEM, "T Take off armor", 0, 0, 0, (void *)'T'}, + { NM_ITEM, "w Wield a weapon", 0, 0, 0, (void *)'w'}, + { NM_ITEM, "W Wear armor", 0, 0, 0, (void *)'W'}, + { NM_ITEM, "x Swap wielded and secondary weapons", 0, 0, 0, (void *)'x'}, + { NM_ITEM, ") Current weapon", 0, 0, 0, (void *)')'}, + { NM_ITEM, "[ Current armor", 0, 0, 0, (void *)'['}, + { NM_ITEM, "= Current rings", 0, 0, 0, (void *)'='}, + { NM_ITEM,"\" Current amulet", 0, 0, 0, (void *)'"'}, + { NM_ITEM, "( Current tools", 0, 0, 0, (void *)'('}, + { NM_ITEM, "* Current equipment", 0, 0, 0, (void *)'*'}, + { NM_TITLE, "Movement", 0, 0, 0, 0}, + { NM_ITEM, "o Open door", 0, 0, 0, (void *)'o'}, + { NM_ITEM, "c Close door", 0, 0, 0, (void *)'c'}, + { NM_ITEM, "^D Kick door", 0, 0, 0, (void *)004}, + { NM_ITEM, "s Search", 0, 0, 0, (void *)'s'}, + { NM_ITEM, "< Go up stairs", 0, 0, 0, (void *)'<'}, + { NM_ITEM, "> Go down stairs", 0, 0, 0, (void *)'>'}, + { NM_ITEM, "^T Teleport", 0, 0, 0, (void *)024}, + { NM_ITEM, ". Wait a moment", 0, 0, 0, (void *)'.'}, + { NM_ITEM, "E Engrave message on floor", 0, 0, 0, (void *)'E'}, + { NM_TITLE, "Extended", 0, 0, 0, 0}, + { NM_ITEM, "M-a #adjust inventory letters", 0, 0, 0, (void *)(128+'a')}, + { NM_ITEM, "M-c #chat with someone", 0, 0, 0, (void *)(128+'c')}, + { NM_ITEM, "M-d #dip an object into something", 0, 0, 0, (void *)(128+'d')}, +#ifdef WEAPON_SKILLS + { NM_ITEM, "M-e #enhance weapon skills", 0, 0, 0, (void *)(128+'e')}, +#endif + { NM_ITEM, "M-f #force a lock", 0, 0, 0, (void *)(128+'f')}, + { NM_ITEM, "M-i #invoke an object's special powers", 0, 0, 0, (void *)(128+'i')}, + { NM_ITEM, "M-j #jump to another location", 0, 0, 0, (void *)(128+'j')}, + { NM_ITEM, "M-l #loot a box on the floor", 0, 0, 0, (void *)(128+'l')}, + { NM_ITEM, "M-m Use a #monster's special ability", 0, 0, 0, (void *)(128+'m')}, + { NM_ITEM, "M-n #name an item or type of object", 0, 0, 0, (void *)(128+'n')}, + { NM_ITEM, "M-o #offer a sacrifice to the gods", 0, 0, 0, (void *)(128+'o')}, + { NM_ITEM, "M-p #pray to the gods for help", 0, 0, 0, (void *)(128+'p')}, + { NM_ITEM, "M-q #quit the game", 0, 0, 0, (void *)(128+'q')}, + { NM_ITEM, "M-r #rub a lamp", 0, 0, 0, (void *)(128+'r')}, + { NM_ITEM, "M-s #sit down", 0, 0, 0, (void *)(128+'s')}, + { NM_ITEM, "M-t #turn undead", 0, 0, 0, (void *)(128+'t')}, + { NM_ITEM, "M-u #untrap something", 0, 0, 0, (void *)(128+'u')}, + { NM_ITEM, "M-v Long #version information", 0, 0, 0, (void *)(128+'v')}, + { NM_ITEM, "M-w #wipe off your face", 0, 0, 0, (void *)(128+'w')}, + { NM_ITEM, " Your #conduct", 0, 0, 0, (void *)'#'}, /* "#co\n" */ + { NM_ITEM, " #ride your steed", 0, 0, 0, (void *)'#'}, /* "#ri\n" */ + { NM_ITEM, "M-2 Switch #twoweapon mode on/off", 0, 0, 0, (void *)(128+'2')}, + { NM_END, NULL, 0, 0, 0, 0} +}; diff --git a/sys/amiga/amirip.c b/sys/amiga/amirip.c new file mode 100644 index 0000000..c41110a --- /dev/null +++ b/sys/amiga/amirip.c @@ -0,0 +1,405 @@ +/* SCCS Id: @(#)amirip.c 3.2 96/02/04 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland 1991,1992,1993,1995,1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "winami.h" +#include "windefs.h" +#include "winext.h" +#include "winproto.h" + +static struct RastPort *rp; + +#ifdef AMII_GRAPHICS + +#undef NULL +#define NULL 0 + +#ifdef AZTEC_C +# include +#else +# ifdef _DCC +# include +# include +# include +# include +# else +# include +# include +# include +# include +# endif + +static char *load_list[]={"tomb.iff",0}; +static BitMapHeader tomb_bmhd; +static struct BitMap *tbmp[ 1 ] = {0}; + +static int cols[2]={154,319}; /* X location of center of columns */ +static int cno = 0; /* current column */ +#define TEXT_TOP (65+yoff) + +static xoff, yoff; /* image centering */ + +/* terrible kludge */ +/* this is why prototypes should have ONLY types in them! */ +# undef red +# undef green +# undef blue +# undef index +# ifdef _DCC +# include +# include +# else +# include +# include +# endif +#endif /* AZTEC_C */ + +extern char *killed_by_prefix[]; +static struct Window *ripwin=0; +static void tomb_text(char*); +static void dofade(int,int,int); +static int search_cmap(int,int,int); + +#define STONE_LINE_LEN 13 /* # chars that fit on one line + * (note 1 ' ' border) */ + +#define DEATH_LINE 10 +#define YEAR_LINE 15 + +static unsigned short tomb_line; + +extern struct amii_DisplayDesc *amiIDisplay; +extern struct Screen *HackScreen; +extern int havelace; + +static unsigned short transpalette[ AMII_MAXCOLORS ] = { 0x0000, }; + +static struct NewWindow newwin = +{ + 0,0,640,200,1,0, + MOUSEBUTTONS|VANILLAKEY|NOCAREREFRESH, + BORDERLESS|ACTIVATE|SMART_REFRESH, + NULL,NULL,(UBYTE*)NULL,NULL,NULL,-1,-1,0xffff,0xffff,CUSTOMSCREEN +}; + +int wh; /* was local in outrip, but needed for SCALE macro */ + +int cmap_white, cmap_black; + +void +amii_outrip( tmpwin, how ) +winid tmpwin; +int how; +{ + int just_return = 0; + int done, rtxth; + struct IntuiMessage *imsg; + int i; + register char *dpx; + char buf[ 200 ]; + int line, tw, ww; + char *errstr = NULL; + + if(!WINVERS_AMIV || HackScreen->RastPort.BitMap->Depth < 4)goto cleanup; + + /* Use the users display size */ + newwin.Height = amiIDisplay->ypix - newwin.TopEdge; + newwin.Width = amiIDisplay->xpix; + newwin.Screen = HackScreen; + + for( i = 0; i < amii_numcolors; ++i ) + flags.amii_curmap[i] = GetRGB4( HackScreen->ViewPort.ColorMap, i ); + + ripwin = OpenWindow( (void *)&newwin ); + if( !ripwin ) goto cleanup; + + LoadRGB4( &HackScreen->ViewPort, transpalette, amii_numcolors ); + + rp= ripwin->RPort; + wh = ripwin->Height; + ww = ripwin->Width; + +#ifdef HACKFONT + if (HackFont) + SetFont(rp, HackFont); +#endif + + tomb_bmhd = ReadImageFiles(load_list, tbmp, &errstr ); + if(errstr)goto cleanup; + if(tomb_bmhd.w > ww || tomb_bmhd.h > wh)goto cleanup; + +#define GENOFF(full,used) ((((full)-(used))/2) & ~7) + xoff = GENOFF(ww,tomb_bmhd.w); + yoff = GENOFF(wh,tomb_bmhd.h); + for(i=0;iBitMap, xoff, yoff, tomb_bmhd.w, tomb_bmhd.h, 0xc0, 0xff, NULL); + + /* Put together death description */ + switch (killer_format) { + default: + impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + + tw = TextLength(rp,buf,STONE_LINE_LEN) + 40; + + { + char *p=buf; + int x, tmp; + for(x=STONE_LINE_LEN;x;x--)*p++='W'; + *p='\0'; + tmp = TextLength(rp,buf,STONE_LINE_LEN) + 40; + tw = max( tw, tmp); + } + + /* There are 5 lines of text on the stone. */ + rtxth = ripwin->RPort->TxHeight * 5; + + SetAfPt( rp, (UWORD *)NULL, 0 ); + SetDrPt( rp, 0xFFFF ); + + tomb_line=TEXT_TOP; + + SetDrMd(rp,JAM1); + + /* Put name on stone */ + Sprintf(buf, "%s", plname); + buf[STONE_LINE_LEN] = 0; + tomb_text(buf); + + /* Put $ on stone */ + Sprintf(buf, "%ld Au", +#ifndef GOLDOBJ + u.ugold); +#else + done_money); +#endif + buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */ + tomb_text(buf); + + /* Put together death description */ + switch (killer_format) { + default: + impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + + /* Put death type on stone */ + for (line=DEATH_LINE, dpx = buf; line STONE_LINE_LEN) + { + for(i=STONE_LINE_LEN;((i0 > STONE_LINE_LEN) && i); i--) + { + if(dpx[i] == ' ') + i0 = i; + } + if(!i) + i0 = STONE_LINE_LEN; + } + + tmpchar = dpx[i0]; + dpx[i0] = 0; + tomb_text(dpx); + + if (tmpchar != ' ') + { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } + else + { + dpx= &dpx[i0+1]; + } + } + + /* Put year on stone */ + Sprintf(buf, "%4d", getyear()); + tomb_text(buf); + +#ifdef NH320_DEDICATION + /* dedication */ + cno = 1; + tomb_line=TEXT_TOP; + tomb_text("This release"); + tomb_text("of NetHack"); + tomb_text("is dedicated"); + tomb_text("to the"); + tomb_text("memory of"); + tomb_text(""); + tomb_text("Izchak"); + tomb_text(" Miller"); + tomb_text(""); + tomb_text("1935-1994"); + tomb_text(""); + tomb_text("Ascended"); +#endif + /* Fade from black to full color */ + dofade(0,16,1); + + /* Flush all messages to avoid typeahead */ + while( imsg = (struct IntuiMessage *)GetMsg( ripwin->UserPort ) ) + ReplyMsg( (struct Message *) imsg ); + done = 0; + while( !done ) + { + WaitPort( ripwin->UserPort ); + while( imsg = (struct IntuiMessage *)GetMsg(ripwin->UserPort) ) + { + switch( imsg->Class ) + { + case MOUSEBUTTONS: + case VANILLAKEY: + done = 1; + break; + } + ReplyMsg( (struct Message *)imsg ); + } + } + + /* Fade out */ + dofade(16,0,-1); + just_return = 1; + +cleanup: + /* free everything */ + if(ripwin){ + Forbid(); + while( imsg = (struct IntuiMessage *)GetMsg( ripwin->UserPort ) ) + ReplyMsg( (struct Message *)imsg ); + CloseWindow( ripwin ); + Permit(); + } + LoadRGB4( &HackScreen->ViewPort, flags.amii_curmap, amii_numcolors ); + + if(tbmp[0])FreeImageFiles(load_list, tbmp); + if(just_return) return; + /* fall back to the straight-ASCII version */ + genl_outrip(tmpwin, how); +} + +static void tomb_text(p) +char *p; +{ + char buf[STONE_LINE_LEN*2]; + int l; + + tomb_line += rp->TxHeight; + + if( !*p ) + return; + sprintf(buf," %s ",p); + l=TextLength(rp,buf,strlen(buf)); + + SetAPen(rp,cmap_white); + Move(rp,cols[cno]-(l/2)-1, tomb_line); + Text(rp,buf,strlen(buf)); + + SetAPen(rp,cmap_white); + Move(rp,cols[cno]-(l/2)+1, tomb_line); + Text(rp,buf,strlen(buf)); + + SetAPen(rp,cmap_white); + Move(rp,cols[cno]-(l/2), tomb_line-1); + Text(rp,buf,strlen(buf)); + + SetAPen(rp,cmap_white); + Move(rp,cols[cno]-(l/2), tomb_line+1); + Text(rp,buf,strlen(buf)); + + SetAPen(rp,cmap_black); + Move(rp,cols[cno]-(l/2), tomb_line); + Text(rp,buf,strlen(buf)); +} + +/* search colormap for best match to given color */ +static int +search_cmap(int r0, int g0, int b0){ + int best = 0; + int bdiff = 0x0fffffff; + int x; + for(x=0;x> 8) & 15); + int g = g0-((amiv_init_map[x] >> 4) & 15); + int b = b0-((amiv_init_map[x] ) & 15); + int diff = (r*r) + (g*g) + (b*b); + if(diff> 8; + g = ( amiv_init_map[ j ] & 0xf0 ) >> 4; + b = ( amiv_init_map[ j ] & 0xf ); + r = ( r * i ) / 16; + g = ( g * i ) / 16; + b = ( b * i ) / 16; + transpalette[ j ] = ((r<<8)|(g<<4)|b); + } + LoadRGB4( &HackScreen->ViewPort, transpalette, amii_numcolors ); + Delay( 1 ); + } +} + + + +#endif /* AMII_GRAPHICS */ + +/* +TODO: + memory leaks + fix ReadImageFiles to return error instead of panic on error +*/ diff --git a/sys/amiga/amisnd.c b/sys/amiga/amisnd.c new file mode 100644 index 0000000..85a007d --- /dev/null +++ b/sys/amiga/amisnd.c @@ -0,0 +1,284 @@ +/* SCCS Id: @(#)amisnd.c 3.2 2000/01/12*/ +/* Copyright (c) 1992, 1993, 1995 by Gregg Wonderly */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains music playing code. + * + * If we were REALLY determined, we would make the sound play + * asynchronously, but I'll save that one for a rainy day... + */ + +#include "hack.h" +#include "dlb.h" + +#undef red +#undef blue +#undef green +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define AMII_AVERAGE_VOLUME 60 + +int amii_volume = AMII_AVERAGE_VOLUME; + +typedef struct VHDR +{ + char name[4]; + long len; + unsigned long oneshot, repeat, samples; + UWORD freq; + UBYTE n_octaves, compress; + LONG volume; +} VHDR; + +typedef struct IFFHEAD +{ + char FORM[4]; + long flen; + char _8SVX[4]; + VHDR vhdr; + char NAME[4]; + long namelen; +} IFFHEAD; + +extern struct GfxBase *GfxBase; + +UBYTE whichannel[] = { 1, 2, 4, 8 }; +void makesound( char *, char *, int vol); +void amii_speaker( struct obj *instr, char *melody, int vol ); + +/* A major scale in indexs to freqtab... */ +int notetab[] = { 0, 2, 4, 5, 7, 9, 11, 12 }; + +/* Frequencies for a scale starting at one octave below 'middle C' */ +long freqtab[] = { + 220, /*A */ + 233, /*Bb*/ + 246, /*B */ + 261, /*C */ + 277, /*Db*/ + 293, /*D */ + 311, /*Eb*/ + 329, /*E */ + 349, /*F */ + 370, /*Gb*/ + 392, /*G */ + 415, /*Ab*/ + 440, /*A */ +}; + +#ifdef TESTING +main( argc, argv ) + int argc; + char **argv; +{ + makesound( "wooden_flute", "AwBwCwDwEwFwGwawbwcwdwewfwgw", 60 ); + makesound( "wooden_flute", "AhBhChDhEhFhGhahbhchdhehfhgh", 60 ); + makesound( "wooden_flute", "AqBqCqDqEqFqGqaqbqcqdqeqfqgq", 60 ); + makesound( "wooden_flute", "AeBeCeDeEeFeGeaebecedeeefege", 60 ); + makesound( "wooden_flute", "AxBxCxDxExFxGxaxbxcxdxexfxgx", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); + makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); +} +#else +void +amii_speaker( struct obj *instr, char *melody, int vol ) +{ + int typ = instr->otyp; + char * actualn = (char *)OBJ_NAME( objects[typ] ) ; + + /* Make volume be relative to users volume level, with 60 being the + * average level that will be passed to us. + */ + vol = vol * amii_volume / AMII_AVERAGE_VOLUME; + + makesound( actualn, melody, vol ); +} +#endif + +void +makesound ( char *actualn , char * melody, int vol ) +{ + char *t; + int c, cycles, dot, dlay; + dlb *stream = 0; + IFFHEAD iffhead; + struct IOAudio *AudioIO = 0; + struct MsgPort *AudioMP = 0; + struct Message *AudioMSG = 0; + ULONG device = -1; + BYTE *waveptr = 0; + LONG frequency=440, duration=1, clock, samp, samples, samcyc=1; + unsigned char name [ 100 ] ; + + if ( flags.silent ) + return; + + if( GfxBase->DisplayFlags & PAL ) + clock = 3546895; + else + clock = 3579545; + + /* + * Convert type to file name - if there's nothing to play we + * shouldn't be here in the first place. + */ + strncpy(name, actualn,sizeof(name) ) ; + for( t = strchr( name, ' ' ); t; t = strchr( name, ' ' ) ) + *t = '_'; + if( (stream = dlb_fopen( name, "r" )) == NULL ) + { + perror( name ); + return; + } + + AudioIO = (struct IOAudio *) + AllocMem( sizeof( struct IOAudio ), MEMF_PUBLIC|MEMF_CLEAR ); + if( AudioIO == 0 ) + goto killaudio; + + AudioMP = CreateMsgPort(); + if( AudioMP == 0 ) + goto killaudio; + + AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP; + AudioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0; + AudioIO->ioa_Request.io_Command = ADCMD_ALLOCATE; + AudioIO->ioa_Request.io_Flags = ADIOF_NOWAIT; + AudioIO->ioa_AllocKey = 0; + AudioIO->ioa_Data = whichannel; + AudioIO->ioa_Length = sizeof( whichannel ); + + device = OpenDevice( AUDIONAME, 0L, (struct IORequest *)AudioIO, 0L ); + if( device != 0 ) + goto killaudio; + + if( dlb_fread( (genericptr_t)&iffhead, sizeof( iffhead ), 1, stream ) != 1 ) + goto killaudio; + + /* This is an even number of bytes long */ + if( dlb_fread( name, (iffhead.namelen+1) & ~1, 1, stream ) != 1 ) + goto killaudio; + + if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 ) + goto killaudio; + + if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 ) + goto killaudio; + + waveptr = AllocMem( samples, MEMF_CHIP|MEMF_PUBLIC ); + if( !waveptr ) + goto killaudio; + + if( dlb_fread( waveptr, samples, 1, stream ) != 1 ) + goto killaudio; + + while( melody[0] && melody[1] ) + { + c = *melody++; + duration = *melody++; + dot = 0; + if( *melody == '.' ) + { + dot = 1; + ++melody; + } + switch( duration ) + { + case 'w': dlay = 3; duration = 1; cycles = 1; break; + case 'h': dlay = 3; duration = 2; cycles = 1; break; + case 'q': dlay = 2; duration = 4; cycles = 1; break; + case 'e': dlay = 1; duration = 8; cycles = 1; break; + case 'x': dlay = 0; duration = 16; cycles = 1; break; + case 't': dlay = 0; duration = 32; cycles = 1; break; + default: goto killaudio; /* unrecognized duration */ + } + + /* Lower case characters are one octave above upper case */ + switch( c ) + { + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': case 'g': + c -= 'a' - 7; + break; + + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': case 'G': + c -= 'A'; + break; + + default: + continue; + } + + samcyc = samples; + + /* lowercase start at middle 'C', upper case are one octave below */ + frequency = c > 7 ? freqtab[notetab[c%7]]*2 : freqtab[notetab[c]]; + + /* We can't actually do a dotted whole note unless we add code for a real + * 8SVX sample which includes sustain sample information to tell us how + * to hold the note steady... So when duration == 1, ignore 'dot'... + */ + if( dot && duration > 1 ) + samp = ((samples / duration) * 3) / 2; + else + samp = samples / duration; + + /* Only use some of the samples based on frequency */ + samp = frequency * samp / 880; + + /* The 22khz samples are middle C samples, so adjust the play + * back frequency accordingly + */ + frequency = (frequency * (iffhead.vhdr.freq*2)/3) / 440L; + + AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP; + AudioIO->ioa_Request.io_Command = CMD_WRITE; + AudioIO->ioa_Request.io_Flags = ADIOF_PERVOL; + AudioIO->ioa_Data = (BYTE *)waveptr; + AudioIO->ioa_Length = samp; + + /* The clock rate represents the unity rate, so dividing by + * the frequency gives us a period ratio... + */ +/*printf( "clock: %ld, freq: %ld, div: %ld\n", clock, frequency, clock/frequency );*/ + AudioIO->ioa_Period = clock/frequency; + AudioIO->ioa_Volume = vol; + AudioIO->ioa_Cycles = cycles; + + BeginIO( (struct IORequest *)AudioIO ); + WaitPort( AudioMP ); + AudioMSG = GetMsg( AudioMP ); + if( dlay ) + Delay( dlay ); + } + + killaudio: + if( stream ) dlb_fclose( stream ); + if( waveptr ) FreeMem( waveptr, samples ); + if( device == 0 ) CloseDevice( (struct IORequest *)AudioIO ); + if( AudioMP ) DeleteMsgPort( AudioMP ); + if( AudioIO ) FreeMem( AudioIO, sizeof( *AudioIO ) ); +} diff --git a/sys/amiga/amistack.c b/sys/amiga/amistack.c new file mode 100644 index 0000000..7e0b6fc --- /dev/null +++ b/sys/amiga/amistack.c @@ -0,0 +1,21 @@ +/* SCCS Id: @(#)amistack.c 3.4 2000/05/03 */ +/* Copyright (c) Janne Salmijärvi, Tampere, Finland, 2000 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Increase stack size to allow deep recursions. + * + * Note: This is SAS/C specific, using other compiler probably + * requires another method for increasing stack. + * + */ + +#ifdef __SASC_60 +#include + +/* + * At the moment 90*1024 would suffice, but just to be on the safe side ... + */ + +long __stack = 128*1024; +#endif diff --git a/sys/amiga/amitty.c b/sys/amiga/amitty.c new file mode 100644 index 0000000..236d4b3 --- /dev/null +++ b/sys/amiga/amitty.c @@ -0,0 +1,67 @@ +/* SCCS Id: @(#)amitty.c 3.2 2000/01/12 +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland 1993,1996 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* TTY-specific code for the Amiga + * This is still experimental. + * Still to do: + * add real termcap handling - currently requires ANSI_DEFAULT + */ + +#include "hack.h" +#include "tcap.h" +#include +#include + +#ifdef _DCC +# define getch() getchar() +#endif +#ifdef __SASC_60 +# include +#endif + +void NDECL( tty_change_color ); +char *NDECL( tty_get_color_string ); + +#ifdef TTY_GRAPHICS + +int amibbs=0; /* BBS mode */ +char bbs_id[80]=""; /* BBS uid equivalent */ +long afh_in, afh_out; /* BBS mode Amiga filehandles */ + +void settty(const char *s){ + end_screen(); + if(s)raw_print(s); + iflags.cbreak=ON; /* this is too easy: probably wrong */ +#if 1 /* should be version>=36 */ +/* if(IsInteractive(afh_in)){ */ + SetMode(afh_in,0); /* con mode */ +/* } */ +#endif +} +void gettty(){ +#if 1 /* should be VERSION >=36 */ +/* if(IsInteractive(afh_in)){ */ + SetMode(afh_in,1); /* raw mode */ +/* } */ +#endif +} +void setftty(){ + iflags.cbreak=ON; /* ditto */ +} +char kill_char='X'-'@'; +char erase_char='\b'; +tgetch(){ + char x; + Read(afh_in,&x,1); + return (x=='\r')?'\n':x; +} +void get_scr_size(){ + CO=80; + LI=24; +} + +#endif + +void tty_change_color() {} +char *tty_get_color_string() { return( "" ); } diff --git a/sys/amiga/amiwind.c b/sys/amiga/amiwind.c new file mode 100644 index 0000000..860bc3c --- /dev/null +++ b/sys/amiga/amiwind.c @@ -0,0 +1,949 @@ +/* SCCS Id: @(#)amiwind.c 3.2 2000/01/12 +/* Copyright (c) Olaf Seibert (KosmoSoft), 1989, 1992 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland 1993,1996 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +/* Have to undef CLOSE as display.h and intuition.h both use it */ +#undef CLOSE + +#ifdef AMII_GRAPHICS /* too early in the file? too late? */ + +#ifdef AMIFLUSH +static struct Message *FDECL(GetFMsg,(struct MsgPort *)); +#endif + +static int BufferGetchar(void); +static void ProcessMessage( register struct IntuiMessage *message ); + +#define BufferQueueChar(ch) (KbdBuffer[KbdBuffered++] = (ch)) + +struct Library *ConsoleDevice; + +#include "NH:sys/amiga/amimenu.c" + +/* Now our own variables */ + +struct IntuitionBase *IntuitionBase; +struct Screen *HackScreen; +struct Window *pr_WindowPtr; +struct MsgPort *HackPort; +struct IOStdReq ConsoleIO; +struct Menu *MenuStrip; +APTR *VisualInfo; +char Initialized = 0; +WEVENT lastevent; + +#ifdef HACKFONT +struct GfxBase *GfxBase; +struct Library *DiskfontBase; +#endif + +#define KBDBUFFER 10 +static unsigned char KbdBuffer[KBDBUFFER]; +unsigned char KbdBuffered; + +#ifdef HACKFONT + +struct TextFont *TextsFont = NULL; +struct TextFont *HackFont = NULL; +struct TextFont *RogueFont = NULL; + +UBYTE FontName[] = "NetHack:hack.font"; + /* # chars in "NetHack:": */ +#define SIZEOF_DISKNAME 8 + +#endif + +struct TextAttr Hack80 = { +#ifdef HACKFONT + &FontName[SIZEOF_DISKNAME], +#else + (UBYTE *) "topaz.font", +#endif + 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED + | FPF_ROMFONT +}; + +struct TextAttr TextsFont13 = { + (UBYTE *) "courier.font", + 13, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED +#ifndef HACKFONT + | FPF_ROMFONT +#endif +}; + +/* Avoid doing a ReplyMsg through a window that no longer exists. */ +static enum {NoAction, CloseOver} delayed_key_action = NoAction; + +/* + * Open a window that shares the HackPort IDCMP. Use CloseShWindow() + * to close. + */ + +struct Window *OpenShWindow(nw) +struct NewWindow *nw; +{ + register struct Window *win; + register ULONG idcmpflags; + + if (!HackPort) /* Sanity check */ + return (struct Window *) 0; + + idcmpflags = nw->IDCMPFlags; + nw->IDCMPFlags = 0; + if (!(win = OpenWindow((void *)nw))) + { + nw->IDCMPFlags = idcmpflags; + return (struct Window *) 0; + } + + nw->IDCMPFlags = idcmpflags; + win->UserPort = HackPort; + ModifyIDCMP(win, idcmpflags); + return win; +} + + +/* + * Close a window that shared the HackPort IDCMP port. + */ + +void FDECL(CloseShWindow, (struct Window *)); +void CloseShWindow(win) +struct Window *win; +{ + register struct IntuiMessage *msg; + + if( !HackPort ) + panic("HackPort NULL in CloseShWindow" ); + if (!win) + return; + + Forbid(); + /* Flush all messages for all windows to avoid typeahead and other + * similar problems... + */ + while( msg = (struct IntuiMessage *)GetMsg( win->UserPort ) ) + ReplyMsg( (struct Message *) msg ); + KbdBuffered = 0; + win->UserPort = (struct MsgPort *) 0; + ModifyIDCMP(win, 0L); + Permit(); + CloseWindow(win); +} + +static int BufferGetchar() +{ + register int c; + + if (KbdBuffered > 0) { + c = KbdBuffer[0]; + KbdBuffered--; + /* Move the remaining characters */ + if( KbdBuffered < sizeof( KbdBuffer ) ) + memcpy( KbdBuffer, KbdBuffer+1, KbdBuffered ); + return c; + } + + return NO_CHAR; +} + +/* + * This should remind you remotely of DeadKeyConvert, but we are cheating + * a bit. We want complete control over the numeric keypad, and no dead + * keys... (they are assumed to be on Alted keys). + * + * Also assumed is that the IntuiMessage is of type RAWKEY. For some + * reason, IECODE_UP_PREFIX events seem to be lost when they occur while + * our console window is inactive. This is particulary troublesome with + * qualifier keys... Is this because I never RawKeyConvert those events??? + */ + +int ConvertKey(message) +register struct IntuiMessage *message; +{ + static struct InputEvent theEvent; + static char numpad[] = "bjnh.lyku"; + static char ctrl_numpad[] = "\x02\x0A\x0E\x08.\x0C\x19\x0B\x15"; + static char shift_numpad[] = "BJNH.LYKU"; + + unsigned char buffer[10]; + struct Window *w = message->IDCMPWindow; + register int length; + register ULONG qualifier; + char numeric_pad, shift, control, alt; + + if( amii_wins[ WIN_MAP ] ) + w = amii_wins[ WIN_MAP ]->win; + qualifier = message->Qualifier; + + control = (qualifier & IEQUALIFIER_CONTROL) != 0; + shift = (qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) != 0; + alt = (qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT )) != 0; + + /* Allow ALT to function as a META key ... */ + /* But make it switchable - alt is needed for some non-US keymaps */ + if(flags.altmeta) + qualifier &= ~(IEQUALIFIER_LALT | IEQUALIFIER_RALT); + numeric_pad = (qualifier & IEQUALIFIER_NUMERICPAD) != 0; + + /* + * Shortcut for HELP and arrow keys. I suppose this is allowed. + * The defines are in intuition/intuition.h, and the keys don't + * serve 'text' input, normally. Also, parsing their escape + * sequences is such a mess... + */ + + switch (message->Code) { + case RAWHELP: + if( alt ) + { + EditColor(); + return( -1 ); + } +#ifdef CLIPPING + else if( WINVERS_AMIV && control ) + { + EditClipping(); + + CO = ( w->Width - w->BorderLeft - w->BorderRight ) / mxsize; + LI = ( w->Height - w->BorderTop - w->BorderBottom ) / mysize; + clipxmax = CO + clipx; + clipymax = LI + clipy; + if( CO < COLNO || LI < ROWNO ) + { + clipping = TRUE; + amii_cliparound( u.ux, u.uy ); + } + else + { + clipping = FALSE; + clipx = clipy = 0; + } + BufferQueueChar( 'R'-64 ); + return(-1); + } +#endif + else if( WINVERS_AMIV && shift ) + { + if( WIN_OVER == WIN_ERR ) + { + WIN_OVER = amii_create_nhwindow( NHW_OVER ); + BufferQueueChar( 'R'-64 ); + } + else + { + delayed_key_action = CloseOver; + } + return( -1 ); + } + return( '?' ); + break; + case CURSORLEFT: + length = '4'; + numeric_pad = 1; + goto arrow; + case CURSORDOWN: + length = '2'; + numeric_pad = 1; + goto arrow; + case CURSORUP: + length = '8'; + numeric_pad = 1; + goto arrow; + case CURSORRIGHT: + length = '6'; + numeric_pad = 1; + goto arrow; + } + + theEvent.ie_Class = IECLASS_RAWKEY; + theEvent.ie_Code = message->Code; + theEvent.ie_Qualifier = numeric_pad ? IEQUALIFIER_NUMERICPAD : qualifier; + theEvent.ie_EventAddress = (APTR) (message->IAddress); + + length = RawKeyConvert(&theEvent, (char *)buffer, + (long) sizeof(buffer), NULL); + + if (length == 1) { /* Plain ASCII character */ + length = buffer[0]; + /* + * If iflags.num_pad is set, movement is by 4286. + * If not set, translate 4286 into hjkl. + * This way, the numeric pad can /always/ be used + * for moving, though best results are when it is off. + */ +arrow: + if (!iflags.num_pad && numeric_pad && length >= '1' && length <= '9') { + length -= '1'; + if (control) { + length = ctrl_numpad[length]; + } else if (shift) { + length = shift_numpad[length]; + } else { + length = numpad[length]; + } + } + + /* Kludge to allow altmeta on eg. scandinavian keymap (# == shift+alt+3) + and prevent it from interfering with # command (M-#) */ + if (length == ('#'|0x80)) + return '#'; + if (alt && flags.altmeta) + length |= 0x80; + return(length); + } /* else shift, ctrl, alt, amiga, F-key, shift-tab, etc */ + else if( length > 1 ) + { + int i; + + if( length == 3 && buffer[ 0 ] == 155 && buffer[ 2 ] == 126 ) + { + int got = 1; + switch( buffer[ 1 ] ) + { + case 53: mxsize = mysize = 8; break; + case 54: mxsize = mysize = 16; break; + case 55: mxsize = mysize = 24; break; + case 56: mxsize = mysize = 32; break; + case 57: mxsize = mysize = 48; break; + default: got = 0; break; + } +#ifdef OPT_DISPMAP + dispmap_sanity(); +#endif + + if( got ) + { + CO = (w->Width-w->BorderLeft-w->BorderRight)/mxsize; + LI = (w->Height-w->BorderTop-w->BorderBottom)/mysize; + clipxmax = CO + clipx; + clipymax = LI + clipy; + if( CO < COLNO || LI < ROWNO ) + { + amii_cliparound( u.ux, u.uy ); + } + else + { + CO = COLNO; + LI = ROWNO; + } + reclip = 1; + doredraw(); + flush_screen( 1 ); + reclip = 0; + /*BufferQueueChar( 'R'-64 );*/ + return( -1 ); + } + } + printf( "Unrecognized key: %d ", (int)buffer[0]); + for( i = 1; i < length; ++i ) + printf( "%d ", (int)buffer[i]); + printf( "\n" ); + } + return( -1 ); +} + +/* + * Process an incoming IntuiMessage. + * It would certainly look nicer if this could be done using a + * PA_SOFTINT message port, but we cannot call RawKeyConvert() + * during a software interrupt. + * Anyway, amikbhit()/kbhit() is called often enough, and usually gets + * ahead of input demands, when the user types ahead. + */ + +static void ProcessMessage(message) +register struct IntuiMessage *message; +{ + int c; + int cnt; + menu_item *mip; + static int skip_mouse=0; /* need to ignore next mouse event on + * a window activation */ + struct Window *w = message->IDCMPWindow; + + switch(message->Class) { + case ACTIVEWINDOW: + if( alwaysinvent && WIN_INVEN != WIN_ERR && + w == amii_wins[ WIN_INVEN ]->win ) + { + cnt = DoMenuScroll( WIN_INVEN, 0, PICK_NONE, &mip ); + } + else if( scrollmsg && WIN_MESSAGE != WIN_ERR && + w == amii_wins[ WIN_MESSAGE ]->win ) + { + cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip ); + } + else + { + skip_mouse=1; + } + break; + + case MOUSEBUTTONS: + { + if( skip_mouse ) + { + skip_mouse=0; + break; + } + + if( !amii_wins[ WIN_MAP ] || w != amii_wins[ WIN_MAP ]->win ) + break; + + if( message->Code == SELECTDOWN ) + { + lastevent.type = WEMOUSE; + lastevent.un.mouse.x = message->MouseX; + lastevent.un.mouse.y = message->MouseY; + /* With shift equals RUN */ + lastevent.un.mouse.qual = (message->Qualifier & + (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT)) != 0; + } + } + break; + + case MENUPICK: + { + USHORT thismenu; + struct MenuItem *item; + + thismenu = message->Code; + while (thismenu != MENUNULL) + { + item = ItemAddress(MenuStrip, (ULONG) thismenu); + if (KbdBuffered < KBDBUFFER) + BufferQueueChar((char)(GTMENUITEM_USERDATA(item))); + thismenu = item->NextSelect; + } + } + break; + + case REFRESHWINDOW: + { + if( scrollmsg + && amii_wins[ WIN_MESSAGE ] + && w == amii_wins[ WIN_MESSAGE ]->win + ){ + cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip ); + } + } + break; + + case CLOSEWINDOW: + if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win ) + { + dismiss_nhwindow( WIN_INVEN ); + } + if( WINVERS_AMIV + && ( WIN_OVER != WIN_ERR && w == amii_wins[ WIN_OVER ]->win ) + ){ + destroy_nhwindow( WIN_OVER ); + WIN_OVER = WIN_ERR; + } + break; + + case RAWKEY: + if (!(message->Code & IECODE_UP_PREFIX)){ + /* May queue multiple characters + * but doesn't do that yet... + */ + if( ( c = ConvertKey(message) ) > 0 ) + BufferQueueChar( c ); + } + break; + + case GADGETDOWN: + if( WIN_MESSAGE != WIN_ERR && w == amii_wins[ WIN_MESSAGE ]->win ) + { + cnt = DoMenuScroll( WIN_MESSAGE, 0, PICK_NONE, &mip ); + } + else if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win ) + { + cnt = DoMenuScroll( WIN_INVEN, 0, PICK_NONE, &mip ); + } + break; + + case NEWSIZE: + if( WIN_MESSAGE != WIN_ERR && w == amii_wins[ WIN_MESSAGE ]->win ) + { + if( WINVERS_AMIV ) + { + /* Make sure that new size is honored for good. */ + SetAPen( w->RPort, amii_msgBPen ); + SetBPen( w->RPort, amii_msgBPen ); + SetDrMd( w->RPort, JAM2 ); + RectFill( w->RPort, w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight-1, + w->Height - w->BorderBottom-1 ); + } + ReDisplayData( WIN_MESSAGE ); + } + else if( WIN_INVEN != WIN_ERR && w == amii_wins[ WIN_INVEN ]->win ) + { + ReDisplayData( WIN_INVEN ); + } + else if( WINVERS_AMIV + && ( WIN_OVER != WIN_ERR && w == amii_wins[ WIN_OVER ]->win ) + ){ + BufferQueueChar( 'R'-64 ); + } + else if( WIN_MAP != WIN_ERR && w == amii_wins[ WIN_MAP ]->win ) + { +#ifdef CLIPPING + CO = (w->Width-w->BorderLeft-w->BorderRight)/mxsize; + LI = (w->Height-w->BorderTop-w->BorderBottom)/mysize; + clipxmax = CO + clipx; + clipymax = LI + clipy; + if( CO < COLNO || LI < ROWNO ) + { + amii_cliparound( u.ux, u.uy ); + } + else + { + clipping = FALSE; + clipx = clipy = 0; + } + BufferQueueChar( 'R'-64 ); +#endif + } + break; + } + ReplyMsg((struct Message *) message); + + switch(delayed_key_action){ + case CloseOver: + amii_destroy_nhwindow( WIN_OVER ); + WIN_OVER = WIN_ERR; + delayed_key_action = NoAction; + case NoAction: + ; /* null */ + } +} + +#endif /* AMII_GRAPHICS */ +/* + * Get all incoming messages and fill up the keyboard buffer, + * thus allowing Intuition to (maybe) free up the IntuiMessages. + * Return when no more messages left, or keyboard buffer half full. + * We need to do this since there is no one-to-one correspondence + * between characters and incoming messages. + */ + +#if defined(TTY_GRAPHICS) && !defined(AMII_GRAPHICS) +int kbhit(){ + return 0; +} +#else +int +kbhit() +{ + int c; +# ifdef TTY_GRAPHICS + /* a kludge to defuse the mess in allmain.c */ + /* I hope this is the right approach */ + if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows)return 0; +# endif + c = amikbhit(); + if( c <= 0 ) + return( 0 ); + return( c ); +} +#endif + +#ifdef AMII_GRAPHICS + +int +amikbhit() +{ + register struct IntuiMessage *message; + while( KbdBuffered < KBDBUFFER / 2 ) + { +#ifdef AMIFLUSH + message = (struct IntuiMessage *) GetFMsg(HackPort); +#else + message = (struct IntuiMessage *) GetMsg(HackPort); +#endif + if(message) + { + ProcessMessage(message); + if( lastevent.type != WEUNK && lastevent.type != WEKEY ) + break; + } + else + break; + } + return ( lastevent.type == WEUNK ) ? KbdBuffered : -1; +} + +/* + * Get a character from the keyboard buffer, waiting if not available. + * Ignore other kinds of events that happen in the mean time. + */ + +int WindowGetchar( ) +{ + while ((lastevent.type = WEUNK), amikbhit() <= 0) { + WaitPort(HackPort); + } + return BufferGetchar(); +} + +WETYPE WindowGetevent() +{ + lastevent.type = WEUNK; + while (amikbhit() == 0) + { + WaitPort(HackPort); + } + + if( KbdBuffered ) + { + lastevent.type = WEKEY; + lastevent.un.key = BufferGetchar(); + } + return( lastevent.type ); +} + +/* + * Clean up everything. But before we do, ask the user to hit return + * when there is something that s/he should read. + */ + +void amii_cleanup() +{ + register struct IntuiMessage *msg; + + /* Close things up */ + if( HackPort ) + { + amii_raw_print(""); + amii_getret(); + } + + if (ConsoleIO.io_Device) + CloseDevice( (struct IORequest *)&ConsoleIO ); + ConsoleIO.io_Device = 0; + + if( ConsoleIO.io_Message.mn_ReplyPort ) + DeleteMsgPort( ConsoleIO.io_Message.mn_ReplyPort ); + ConsoleIO.io_Message.mn_ReplyPort = 0; + + /* Strip messages before deleting the port */ + if( HackPort ) + { + Forbid(); + while (msg = (struct IntuiMessage *) GetMsg(HackPort)) + ReplyMsg((struct Message *) msg); + kill_nhwindows( 1 ); + DeleteMsgPort( HackPort ); + HackPort = NULL; + Permit(); + } + + /* Close the screen, under v37 or greater it is a pub screen and there may + * be visitors, so check close status and wait till everyone is gone. + */ + if( HackScreen ) + { +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + if (MenuStrip) FreeMenus(MenuStrip); + if (VisualInfo) FreeVisualInfo(VisualInfo); + while( CloseScreen( HackScreen ) == FALSE ) + { + struct EasyStruct easy = + { + sizeof( struct EasyStruct ), + 0, + "Nethack Problem", + "Can't Close Screen, Close Visiting Windows", + "Okay" + }; + EasyRequest( NULL, &easy, NULL, NULL ); + } + } + else +#endif + { + CloseScreen(HackScreen); + } + HackScreen = NULL; + } + +#ifdef HACKFONT + if (HackFont) + { + CloseFont(HackFont); + HackFont = NULL; + } + + if( TextsFont ) + { + CloseFont( TextsFont ); + TextsFont = NULL; + } + + if( RogueFont ) + { + CloseFont( RogueFont ); + RogueFont = NULL; + } + + if( DiskfontBase ) + { + CloseLibrary(DiskfontBase); + DiskfontBase = NULL; + } +#endif + + if (GadToolsBase) { + CloseLibrary((struct Library *)GadToolsBase); + GadToolsBase=NULL; + } + + if (LayersBase) { + CloseLibrary((struct Library *)LayersBase); + LayersBase = NULL; + } + + if (GfxBase) { + CloseLibrary((struct Library *)GfxBase); + GfxBase = NULL; + } + + if (IntuitionBase) { + CloseLibrary((struct Library *)IntuitionBase); + IntuitionBase = NULL; + } + +#ifdef SHAREDLIB + if (DOSBase) { + CloseLibrary((struct Library *)DOSBase); + DOSBase = NULL; + } +#endif + + ((struct Process *) FindTask(NULL))->pr_WindowPtr = (APTR) pr_WindowPtr; + + Initialized = 0; +} + +#endif /* AMII_GRAPHICS */ + +#ifndef SHAREDLIB +void Abort(rc) +long rc; +{ + int fault = 1; +#ifdef CHDIR + extern char orgdir[]; + chdir(orgdir); +#endif +#ifdef AMII_GRAPHICS + if (Initialized + && ConsoleDevice + && windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows) { + printf("\n\nAbort with alert code %08lx...\n", rc); + amii_getret(); + } else +#endif + printf("\n\nAbort with alert code %08lx...\n",rc); + /* Alert(rc); this is too severe */ +#ifdef __SASC +# ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + struct EasyStruct es = + { + sizeof( struct EasyStruct ), + 0, + "NetHack Panic Request", + "NetHack is Aborting with code == 0x%08lx", + "Continue Abort|Return to Program|Clean up and exit", + }; + fault = EasyRequest( NULL, &es, NULL, (long)rc ); + if( fault == 2 ) + return; + } +# endif + if( fault == 1 ) + { +/* __emit(0x4afc); */ /* illegal instruction */ + __emit(0x40fc); /* divide by */ + __emit(0x0000); /* #0 */ + /* NOTE: don't move amii_cleanup() above here - */ + /* it is too likely to kill the system */ + /* before it can get the SnapShot out, if */ + /* there is something really wrong. */ + } +#endif +#ifdef AMII_GRAPHICS + if(windowprocs.win_init_nhwindows==amii_procs.win_init_nhwindows) + amii_cleanup(); +#endif +#undef exit +#ifdef AZTEC_C + _abort(); +#endif + exit((int) rc); +} + +void +CleanUp() +{ + amii_cleanup(); +} +#endif + +#ifdef AMII_GRAPHICS + +#ifdef AMIFLUSH +/* This routine adapted from AmigaMail IV-37 by Michael Sinz */ +static struct Message * +GetFMsg(port) + struct MsgPort *port; + { + struct IntuiMessage *msg,*succ,*succ1; + + if(msg=(struct IntuiMessage *)GetMsg(port)){ + if(!flags.amiflush)return((struct Message *)msg); + if(msg->Class==RAWKEY){ + Forbid(); + succ=(struct IntuiMessage *)(port->mp_MsgList.lh_Head); + while(succ1=(struct IntuiMessage *) + (succ->ExecMessage.mn_Node.ln_Succ)){ + if(succ->Class==RAWKEY){ + Remove((struct Node *)succ); + ReplyMsg((struct Message *)succ); + } + succ=succ1; + } + Permit(); + } + } + return((struct Message *)msg); +} +#endif + +struct NewWindow * +DupNewWindow( win ) + struct NewWindow *win; +{ + struct NewWindow *nwin; + struct Gadget *ngd, *gd, *pgd = NULL; + struct PropInfo *pip; + struct StringInfo *sip; + + /* Copy the (Ext)NewWindow structure */ + + nwin = (struct NewWindow *)alloc( sizeof( struct NewWindow ) ); + *nwin = *win; + + /* Now do the gadget list */ + + nwin->FirstGadget = NULL; + for( gd = win->FirstGadget; gd; gd = gd->NextGadget ) + { + ngd = (struct Gadget *)alloc( sizeof( struct Gadget ) ); + *ngd = *gd; + if( gd->GadgetType == STRGADGET ) + { + sip = (struct StringInfo *)alloc( sizeof( struct StringInfo ) ); + *sip = *((struct StringInfo *)gd->SpecialInfo); + sip->Buffer = (UBYTE *) alloc( sip->MaxChars ); + *sip->Buffer = 0; + ngd->SpecialInfo = (APTR)sip; + } + else if( gd->GadgetType == PROPGADGET ) + { + pip = (struct PropInfo *)alloc( sizeof( struct PropInfo ) ); + *pip = *((struct PropInfo *)gd->SpecialInfo); + ngd->SpecialInfo = (APTR)pip; + } + if( pgd ) + pgd->NextGadget = ngd; + else + nwin->FirstGadget = ngd; + pgd = ngd; + ngd->NextGadget = NULL; + ngd->UserData = (APTR) 0x45f35c3d; // magic cookie for FreeNewWindow() + } + return( nwin ); +} + +void +FreeNewWindow( win ) + struct NewWindow *win; +{ + register struct Gadget *gd, *pgd; + register struct StringInfo *sip; + + for( gd = win->FirstGadget; gd; gd = pgd ) { + pgd = gd->NextGadget; + if ((ULONG)gd->UserData == 0x45f35c3d) { + if( gd->GadgetType == STRGADGET ) { + sip = (struct StringInfo *)gd->SpecialInfo; + free( sip->Buffer ); + free( sip ); + } else if( gd->GadgetType == PROPGADGET ) { + free( (struct PropInfo *)gd->SpecialInfo ); + } + free( gd ); + } + } + free( win ); +} + +void +bell() +{ + if (flags.silent) return; + DisplayBeep(NULL); +} + +void +amii_delay_output() +{ + /* delay 50 ms */ + Delay(2L); +} + +void +amii_number_pad(state) +int state; +{ +} +#endif /* AMII_GRAPHICS */ + +#ifndef SHAREDLIB +void +amiv_loadlib( void ) +{ +} + +void +amii_loadlib( void ) +{ +} + +/* fatal error */ +/*VARARGS1*/ +void error VA_DECL(const char *, s) + VA_START(s); + VA_INIT(s, char *); + + putchar('\n'); + vprintf(s, VA_ARGS); + putchar('\n'); + + VA_END(); + Abort(0L); +} +#endif diff --git a/sys/amiga/amiwind.p b/sys/amiga/amiwind.p new file mode 100644 index 0000000..ca009d4 --- /dev/null +++ b/sys/amiga/amiwind.p @@ -0,0 +1,40 @@ +/* SCCS Id: @(#)amiwind.p 3.1 93/01/08 */ +/* Copyright (c) Gregg Wonderly, Naperville, IL, 1992, 1993 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* amiwind.c */ +#ifdef INTUI_NEW_LOOK +struct Window *FDECL( OpenShWindow, (struct ExtNewWindow *) ); +#else +struct Window *FDECL( OpenShWindow, (struct NewWindow *) ); +#endif +void FDECL( CloseShWindow, (struct Window *)); +int NDECL( kbhit ); +int NDECL( amikbhit ); +int NDECL( WindowGetchar ); +WETYPE NDECL( WindowGetevent ); +void NDECL( WindowFlush ); +void FDECL( WindowPutchar, (char )); +void FDECL( WindowFPuts, (const char *)); +void FDECL( WindowPuts, (const char *)); +void FDECL( WindowPrintf, ( char *,... )); +void NDECL( CleanUp ); +int FDECL( ConvertKey, ( struct IntuiMessage * )); +#ifndef SHAREDLIB +void FDECL( Abort, (long )); +#endif +void FDECL( flush_glyph_buffer, (struct Window *)); +void FDECL( amiga_print_glyph, (winid , int , int )); +void FDECL( start_glyphout, (winid )); +void FDECL( amii_end_glyphout, (winid )); +#ifdef INTUI_NEW_LOOK +struct ExtNewWindow *FDECL( DupNewWindow, (struct ExtNewWindow *)); +void FDECL( FreeNewWindow, (struct ExtNewWindow *)); +#else +struct NewWindow *FDECL( DupNewWindow, (struct NewWindow *)); +void FDECL( FreeNewWindow, (struct NewWindow *)); +#endif +void NDECL( bell ); +void NDECL( amii_delay_output ); +void FDECL( amii_number_pad, (int )); +void amii_cleanup( void ); diff --git a/sys/amiga/clipwin.c b/sys/amiga/clipwin.c new file mode 100644 index 0000000..a5ede30 --- /dev/null +++ b/sys/amiga/clipwin.c @@ -0,0 +1,268 @@ +static USHORT Palette[] = { + 0x0AAA, /* color #0 */ + 0x0000, /* color #1 */ + 0x0FFF, /* color #2 */ + 0x058B, /* color #3 */ + 0x000F, /* color #4 */ + 0x0F0F, /* color #5 */ + 0x00FF, /* color #6 */ + 0x0FFF /* color #7 */ +#define PaletteColorCount 8 +}; + +#define PALETTE Palette + +static SHORT ClipBorderVectors1[] = { + 0,0, + 76,0, + 76,11, + 0,11, + 0,0 +}; +static struct Border ClipBorder1 = { + -1,-1, /* XY origin relative to container TopLeft */ + 3,0,JAM1, /* front pen, back pen and drawmode */ + 5, /* number of XY vectors */ + ClipBorderVectors1, /* pointer to XY vectors */ + NULL /* next border in list */ +}; + +static struct IntuiText ClipIText1 = { + 4,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 15,1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Cancel", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipCancel = { + NULL, /* next gadget */ + 240,59, /* origin XY of hit box relative to window TopLeft */ + 75,10, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY, /* activation flags */ + BOOLGADGET, /* gadget type flags */ + (APTR)&ClipBorder1, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText1, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + NULL, /* SpecialInfo structure */ + GADCANCEL, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +static SHORT ClipBorderVectors2[] = { + 0,0, + 78,0, + 78,11, + 0,11, + 0,0 +}; +static struct Border ClipBorder2 = { + -1,-1, /* XY origin relative to container TopLeft */ + 3,0,JAM1, /* front pen, back pen and drawmode */ + 5, /* number of XY vectors */ + ClipBorderVectors2, /* pointer to XY vectors */ + NULL /* next border in list */ +}; + +static struct IntuiText ClipIText2 = { + 4,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 24,1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Okay", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipOkay = { + &ClipCancel, /* next gadget */ + 17,60, /* origin XY of hit box relative to window TopLeft */ + 77,10, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY, /* activation flags */ + BOOLGADGET, /* gadget type flags */ + (APTR)&ClipBorder2, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText2, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + NULL, /* SpecialInfo structure */ + GADOKAY, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +static struct PropInfo ClipClipXCLIPSInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 24504,-1, /* horizontal and vertical pot values */ + 10922,-1, /* horizontal and vertical body values */ +}; + +static struct Image ClipImage1 = { + 43,0, /* XY origin relative to container TopLeft */ + 24,3, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +static struct IntuiText ClipIText3 = { + 3,0,JAM1, /* front and back text pens, drawmode and fill byte */ + -116,-1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "X Clip Border:", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipXCLIP = { + &ClipOkay, /* next gadget */ + 134,37, /* origin XY of hit box relative to window TopLeft */ + -199,7, /* hit box width and height */ + GRELWIDTH, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&ClipImage1, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText3, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&ClipClipXCLIPSInfo, /* SpecialInfo structure */ + XCLIP, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +static struct PropInfo ClipClipYCLIPSInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 13106,-1, /* horizontal and vertical pot values */ + 10922,-1, /* horizontal and vertical body values */ +}; + +static struct Image ClipImage2 = { + 22,0, /* XY origin relative to container TopLeft */ + 24,3, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +static struct IntuiText ClipIText4 = { + 3,0,JAM1, /* front and back text pens, drawmode and fill byte */ + -116,-1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Y Clip Border:", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipYCLIP = { + &ClipXCLIP, /* next gadget */ + 134,46, /* origin XY of hit box relative to window TopLeft */ + -199,7, /* hit box width and height */ + GRELWIDTH, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&ClipImage2, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText4, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&ClipClipYCLIPSInfo, /* SpecialInfo structure */ + YCLIP, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +static struct PropInfo ClipClipXSIZESInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 26212,-1, /* horizontal and vertical pot values */ + 10922,-1, /* horizontal and vertical body values */ +}; + +static struct Image ClipImage3 = { + 45,0, /* XY origin relative to container TopLeft */ + 24,3, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +static struct IntuiText ClipIText5 = { + 3,0,JAM1, /* front and back text pens, drawmode and fill byte */ + -124,-1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "X Scale Factor:", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipXSIZE = { + &ClipYCLIP, /* next gadget */ + 134,15, /* origin XY of hit box relative to window TopLeft */ + -199,7, /* hit box width and height */ + GRELWIDTH, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&ClipImage3, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText5, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&ClipClipXSIZESInfo, /* SpecialInfo structure */ + XSIZE, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +static struct PropInfo ClipClipYSIZESInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + -25937,-1, /* horizontal and vertical pot values */ + 10922,-1, /* horizontal and vertical body values */ +}; + +static struct Image ClipImage4 = { + 69,0, /* XY origin relative to container TopLeft */ + 24,3, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +static struct IntuiText ClipIText6 = { + 3,0,JAM1, /* front and back text pens, drawmode and fill byte */ + -124,-1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Y Scale Factor:", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +static struct Gadget ClipYSIZE = { + &ClipXSIZE, /* next gadget */ + 134,24, /* origin XY of hit box relative to window TopLeft */ + -199,7, /* hit box width and height */ + GRELWIDTH, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&ClipImage4, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &ClipIText6, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&ClipClipYSIZESInfo, /* SpecialInfo structure */ + YSIZE, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +#define ClipGadgetList1 ClipYSIZE + +static struct NewWindow ClipNewWindowStructure1 = { + 114,16, /* window XY origin relative to TopLeft of screen */ + 346,76, /* window width and height */ + 0,1, /* detail and block pens */ + NEWSIZE+MOUSEMOVE+GADGETDOWN+GADGETUP+CLOSEWINDOW+ACTIVEWINDOW+VANILLAKEY+INTUITICKS, /* IDCMP flags */ + WINDOWSIZING+WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH, /* other window flags */ + &ClipYSIZE, /* first gadget in gadget list */ + NULL, /* custom CHECKMARK imagery */ + "Edit Clipping Parameters", /* window title */ + NULL, /* custom screen pointer */ + NULL, /* custom bitmap */ + 350,76, /* minimum width and height */ + -1,-1, /* maximum width and height */ + CUSTOMSCREEN /* destination screen type */ +}; + + +/* end of PowerWindows source generation */ diff --git a/sys/amiga/colorwin.c b/sys/amiga/colorwin.c new file mode 100644 index 0000000..a9cf25c --- /dev/null +++ b/sys/amiga/colorwin.c @@ -0,0 +1,256 @@ +SHORT Col_BorderVectors1[] = { + 0,0, + 59,0, + 59,12, + 0,12, + 0,0 +}; +struct Border Col_Border1 = { + -1,-1, /* XY origin relative to container TopLeft */ + 3,0,JAM1, /* front pen, back pen and drawmode */ + 5, /* number of XY vectors */ + Col_BorderVectors1, /* pointer to XY vectors */ + NULL /* next border in list */ +}; + +struct IntuiText Col_IText1 = { + 7,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 13,1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Save", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +struct Gadget Col_Save = { + NULL, /* next gadget */ + 9,77, /* origin XY of hit box relative to window TopLeft */ + 58,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY, /* activation flags */ + BOOLGADGET, /* gadget type flags */ + (APTR)&Col_Border1, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &Col_IText1, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + NULL, /* SpecialInfo structure */ + GADCOLSAVE, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +SHORT Col_BorderVectors2[] = { + 0,0, + 59,0, + 59,12, + 0,12, + 0,0 +}; +struct Border Col_Border2 = { + -1,-1, /* XY origin relative to container TopLeft */ + 3,0,JAM1, /* front pen, back pen and drawmode */ + 5, /* number of XY vectors */ + Col_BorderVectors2, /* pointer to XY vectors */ + NULL /* next border in list */ +}; + +struct IntuiText Col_IText2 = { + 7,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 17,1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Use", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +struct Gadget Col_Okay = { + &Col_Save, /* next gadget */ + 128,77, /* origin XY of hit box relative to window TopLeft */ + 58,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY, /* activation flags */ + BOOLGADGET, /* gadget type flags */ + (APTR)&Col_Border2, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &Col_IText2, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + NULL, /* SpecialInfo structure */ + GADCOLOKAY, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +SHORT Col_BorderVectors3[] = { + 0,0, + 59,0, + 59,12, + 0,12, + 0,0 +}; +struct Border Col_Border3 = { + -1,-1, /* XY origin relative to container TopLeft */ + 3,0,JAM1, /* front pen, back pen and drawmode */ + 5, /* number of XY vectors */ + Col_BorderVectors3, /* pointer to XY vectors */ + NULL /* next border in list */ +}; + +struct IntuiText Col_IText3 = { + 7,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 6,1, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "Cancel", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +struct Gadget Col_Cancel = { + &Col_Okay, /* next gadget */ + 244,77, /* origin XY of hit box relative to window TopLeft */ + 58,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY, /* activation flags */ + BOOLGADGET, /* gadget type flags */ + (APTR)&Col_Border3, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + &Col_IText3, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + NULL, /* SpecialInfo structure */ + GADCOLCANCEL, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +struct PropInfo Col_Col_RedPenSInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 0,0, /* horizontal and vertical pot values */ + -1,-1, /* horizontal and vertical body values */ +}; + +struct Image Col_Image1 = { + 0,0, /* XY origin relative to container TopLeft */ + 263,7, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +struct Gadget Col_RedPen = { + &Col_Cancel, /* next gadget */ + 32,12, /* origin XY of hit box relative to window TopLeft */ + 271,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE+FOLLOWMOUSE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&Col_Image1, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + NULL, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&Col_Col_RedPenSInfo, /* SpecialInfo structure */ + GADREDPEN, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +struct PropInfo Col_Col_GreenPenSInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 0,0, /* horizontal and vertical pot values */ + -1,-1, /* horizontal and vertical body values */ +}; + +struct Image Col_Image2 = { + 0,0, /* XY origin relative to container TopLeft */ + 263,7, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +struct Gadget Col_GreenPen = { + &Col_RedPen, /* next gadget */ + 32,24, /* origin XY of hit box relative to window TopLeft */ + 271,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE+FOLLOWMOUSE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&Col_Image2, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + NULL, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&Col_Col_GreenPenSInfo, /* SpecialInfo structure */ + GADGREENPEN, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +struct PropInfo Col_Col_BluePenSInfo = { + AUTOKNOB+FREEHORIZ, /* PropInfo flags */ + 0,0, /* horizontal and vertical pot values */ + -1,-1, /* horizontal and vertical body values */ +}; + +struct Image Col_Image3 = { + 0,0, /* XY origin relative to container TopLeft */ + 263,7, /* Image width and height in pixels */ + 0, /* number of bitplanes in Image */ + NULL, /* pointer to ImageData */ + 0x0000,0x0000, /* PlanePick and PlaneOnOff */ + NULL /* next Image structure */ +}; + +struct Gadget Col_BluePen = { + &Col_GreenPen, /* next gadget */ + 32,36, /* origin XY of hit box relative to window TopLeft */ + 271,11, /* hit box width and height */ + NULL, /* gadget flags */ + RELVERIFY+GADGIMMEDIATE+FOLLOWMOUSE, /* activation flags */ + PROPGADGET, /* gadget type flags */ + (APTR)&Col_Image3, /* gadget border or image to be rendered */ + NULL, /* alternate imagery for selection */ + NULL, /* first IntuiText structure */ + NULL, /* gadget mutual-exclude long word */ + (APTR)&Col_Col_BluePenSInfo, /* SpecialInfo structure */ + GADBLUEPEN, /* user-definable data */ + NULL /* pointer to user-definable data */ +}; + +#define Col_GadgetList1 Col_BluePen + +struct IntuiText Col_IText6 = { + 3,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 17,38, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "B", /* pointer to text */ + NULL /* next IntuiText structure */ +}; + +struct IntuiText Col_IText5 = { + 4,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 16,26, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "G", /* pointer to text */ + &Col_IText6 /* next IntuiText structure */ +}; + +struct IntuiText Col_IText4 = { + 7,0,JAM1, /* front and back text pens, drawmode and fill byte */ + 16,14, /* XY origin relative to container TopLeft */ + NULL, /* font pointer or NULL for default */ + "R", /* pointer to text */ + &Col_IText5 /* next IntuiText structure */ +}; + +#define Col_IntuiTextList1 Col_IText4 + +struct NewWindow Col_NewWindowStructure1 = { + 175,45, /* window XY origin relative to TopLeft of screen */ + 312,93, /* window width and height */ + 0,1, /* detail and block pens */ + MOUSEBUTTONS+MOUSEMOVE+GADGETDOWN+GADGETUP+CLOSEWINDOW+VANILLAKEY+INTUITICKS, /* IDCMP flags */ + WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH, /* other window flags */ + &Col_BluePen, /* first gadget in gadget list */ + NULL, /* custom CHECKMARK imagery */ + "Edit Screen Colors", /* window title */ + NULL, /* custom screen pointer */ + NULL, /* custom bitmap */ + 5,5, /* minimum width and height */ + -1,-1, /* maximum width and height */ + CUSTOMSCREEN /* destination screen type */ +}; + + +/* end of PowerWindows source generation */ diff --git a/sys/amiga/cvtsnd.c b/sys/amiga/cvtsnd.c new file mode 100644 index 0000000..0e5f9c5 --- /dev/null +++ b/sys/amiga/cvtsnd.c @@ -0,0 +1,96 @@ +/* SCCS Id: @(#)cvtsnd.c 3.2 95/09/10 */ +/* Copyright (c) 1995, Andrew Church, Olney, Maryland */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include + +typedef struct { + short namelen; + char name[62]; + char misc[64]; /* rest of MacBinary header */ + long FORM; + long flen; + long AIFF; + long SSND; + long sndlen; +} AIFF; + +typedef struct { + char FORM[4]; + long flen; + char _8SVX[4]; + char VHDR[4]; + long vhlen; + long oneshot, repeat; + long samples; /* 'samplesPerHiCycle' in the docs - usually 32, so + * we'll use that */ + short freq; + char octaves, compress; + long volume; + char NAME[4]; + long nlen; /* should be 64; see name[] comment */ + char name[64]; /* for simplicity, i.e. just fwrite() entiree header */ + char BODY[4]; + long blen; +} IFF; + + +main(int ac, char **av) +{ + FILE *in, *out; + AIFF aiff; + IFF iff; + static char buf[16384]; + long n, len; + + if (ac != 3) { + fprintf(stderr, "Usage: %s input-file output-file\n", av[0]); + exit(20); + } + if (!(in = fopen(av[1], "r"))) { + fprintf(stderr, "Can't open input file\n"); + exit(20); + } + if (!(out = fopen(av[2], "w"))) { + fprintf(stderr, "Can't open output file\n"); + exit(20); + } + + fread(&aiff, sizeof(aiff), 1, in); + memcpy(iff.FORM, "FORM", 4); + iff.flen = sizeof(iff) + aiff.sndlen - 8; + memcpy(iff._8SVX, "8SVX", 4); + memcpy(iff.VHDR, "VHDR", 4); + iff.vhlen = 20; + iff.oneshot = aiff.sndlen; + iff.repeat = 0; + iff.samples = 32; + iff.freq = 22000; + iff.octaves = 1; + iff.compress= 0; + iff.volume = 0x10000; + memcpy(iff.NAME, "NAME", 4); + iff.nlen = 64; + strncpy(iff.name, aiff.name, 62); iff.name[aiff.namelen] = 0; + memcpy(iff.BODY, "BODY", 4); + iff.blen = aiff.sndlen; + fwrite(&iff, sizeof(iff), 1, out); + len = aiff.sndlen; + do { + if (len >= sizeof(buf)) + n = fread(buf, 1, sizeof(buf), in); + else + n = fread(buf, 1, len, in); + if (n) { + fwrite(buf, 1, n, out); + len -= n; + } + } while (len && n); + + if (len) + fprintf(stderr, "Warning: %ld bytes of sample missing\n", len); + fclose(in); fclose(out); + exit(0); +} diff --git a/sys/amiga/grave16.xpm b/sys/amiga/grave16.xpm new file mode 100644 index 0000000..bb0466d --- /dev/null +++ b/sys/amiga/grave16.xpm @@ -0,0 +1,223 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"400 200 16 1", +/* colors */ +"` c #66686A", +"a c #797979", +"b c #929291", +"c c #43444A", +"d c #758A74", +"e c #F2F2F2", +"f c #D1D0CE", +"g c #066906", +"h c #065506", +"i c #53535C", +"j c #0C0D0F", +"k c #A3A5A2", +"l c #2D332E", +"m c #C3C4C1", +"n c #B4B4B2", +"o c #07840A", +/* pixels */ +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjjjjjjjjjjjjjjljjjjjjjjjjjljljjljljjljjllljljclljlllljlllljlllllllllllllllcicl``clcllclclllclclclcclclclcccccccccccccccccccii`iiccicicicciiciiiiiibiic`iii`a`iiii`icaii`iiii`iiii`ii`ii`iii```iii`i`ii`ii`ii`iiii`iii`ii`iifi`ii`ii`iiiiiiiiiiiiiiiiiiiiii`iciibicicicicciiccciiibiicciiiiccccbicicccccccciccclcccccclc`aiciiiciccclilccccccccccclcccacccccccclccclcclccc", +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjjjjjjljjjjjjljjjjjjajljjljljljljjjjljjljc`baljjllcljjcjlljllllllllllllllllllllllcclllllllclllcclcllclclccccccccclccccccccccccicciicciicicicic`cciiciciciciacicii`iiiiii``iiakii`iiii`ii`ii`iiii`bkb`i`ci``iiiiii`iii`ii``i`iiiiiaiiiiiiiiii`iiiiiiiiiiiiiiiiiiiiiciiciciicicciciiiicllic`iciiiicicciliccccccccccccc`ccccliccccccccciciccclccccclclclclcclccclclclllccclcllccll", +"ijjjjjjjjjjjjjjjjjjljjjjjjjjljjjjjjjjjjjljjjljjjjjjljljjjjjjjljjjljljljljjjjlljljljljcclljllllljlllljllllllllllllllllcclccclcjcllclclclclllcclclcccccccccccciccccciiic`a`icciiccciciiiiiiiiibi`iiiiii`ii`ci`iii`ii`i`iiiiiiiii`iii``ici`i`i`i``ii`ii`iiiiiiic``iiiiiiiiiiiiiiaiiiiiiiiiiciicicii`iiiiiliiccicicccicccci`ccicccicccclccinccccccccclcccaccccccclccccccccccilcl`lllccclcclcclcllcllclcccclllilcllci", +"jjjjjjjjjjjjj`cjjjjljjjjjjjjcljljjjjjjjljjljijjljjjjjjjljljljjljljljlljljllljjljllflcjljllljljlljllllcllllllllllllcllllccllcc`clclllcllcclclcccclccccccccciccciciccccccic`iciic`iiiiciiiiiiiaiii`iii`i`icak`c``i`iiiiii`ii`i`iii`i`ii`iiiiiiici`iii`ii`i`ii``iiii`c`iiiiiiiii`iiiiiiiiciiiiiiiiiiiccccciccicicciiccccciaillcccciiciccclcccccccccccccc`cclclccillccllcliibilikaicllcclccccclcccclcccccllccllcclcl", +"jjcjjjjjjjjljjjjjjjjjjjjjjjjljjjljjjjjjjjjjjljjjjjljjcjjjljjljjljjjljjljljjjlljllljlllcljllllllllllllcclllllllllclllcclccibccclclcclcclcllicicccccccclcccccccciciicciccciaaiciccciiiiiiii`ciiiiii`iiiii`ic`iiiiiiii`ii`iiiiiiiiii`iiiiiciiiii`i`iiiii`ii`iiiciiiiiiiiiiiiiiciiiciiciciiccicciiiiiicicc`cccccccicccccicciiccicccicclilccccccclccccclccllcccilcccclcciclccciclcclccclccclclllcllcllccllcccclaacccl", +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljljjjjjjjlljjjjljljjjjjljjljjljljlljljlljlllllljajljljlllllllllllllllcllllllllclccilcicclc`clalcclclclcclcccicclclccccciiccicciccciciiiiiiciib`iiiiiiiiiii`iiii``i`i`iiiiiiii`iiiiiiiiiiiiiiiiiibaiiici`iiiiibiiiii`iiiic`kaiiiiiiiiiiiiiciiiciiiciiciciccicciiiiciiiciccicicccccccciclcccccciiccccaciclclcccciilcccclcclcclcllclilacclcccclccclclcccccclccclclccllcclllllkblllc", +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjljjjljjjjjljljjjjjjjljljjljjljljlllljljljljjjjjljllljcjllllllllllllllllllllccclclabclccccciccccllclclcclcclclccccccccciiaiiicciciiicccccciiiiiiiiiciiiiiicii`i`i``i`iiiii`iiiiiiiiiiiiiiiiiiiiiibiciiiicciiii`iiiiiiiiiie`i``ic`iiiiiiiiiciiciciiciciciciciiiciic`cccclcccccccccciccccccciccciicilcillcccccclciccccclclccclibaili`lillccccciicliccccccclcccllallclliiiclcliilcll", +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjljjjjjjjjljjljjjljjjjjljjljljlllllljllljlclllllljlllllllllllllllllllllllllcccclccliiccccciccccclclcclccccccclcciccccciacciciiiccci`iiiiciiiiiiiiiiiiiii`i`c``````i``iiiiciiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii`kciiiiiiiiii`c`iciciiiiiiciiiiiiciccicicicciciiiiii`ifcccciccicccccccccccccccclccccccclcclccclccclclclcllilclcclcllclcliilclclclccicccccccccllllcecclclliaacllcllclc", +"jjjjjjjjjjljjjjjjjjjjjjjjjjjjjjjjljljjjljjjjlljjljjjljljjlijjllllllllllllljljajlljlalllllllllllllllllllllccccclllccclclccccccccccclcccccc`clcccccccccccicacicciciciciiiiiiib`iiciiiii`c`iiii``iii``ii``ciiiiiiciiiiciiiii`biii`iiiiciiiiciiicib`iiiiiiiiiiiiiiiiicii```cccciciciciiiiiiiciiiciiccccccccccciicciccccccccccccciccccclccclcccllccclclc`ccclcclcccccclcccllclccclclllcllcliccllclmccclcccl`lclclclll", +"jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjjljjjlljjjllljlljjjljljjjljljlcllllljcljjcljlllijllllllllllllllilcllcccccllicllllclcllcclliccccclccclcccccccccccccccc`iciicicciiiaiciciicl`ici`iiiiiiii`ii`i`ii``iiiiiiiiiiiiiii`iiiiiaciiciiiiiiiciiiciiiiiiicici`ciiciiiiciicciiiciiciciiiciiiiiii`cbi`liicicccccccccclccccccccclclccccccclcilccllccccllcclcccclcclclclllllcllllcllllllclcllccccclllcllilllclllclilillccll", +"jjjjjjjljljjjjjjjjjjjjjjjjjjljjjjljljjjjjjjjlljjljlllljllljllljlcljjljljcjlllljcljllllcllllllllllim`lljccllcclcbacllcllclccliclcccclccccccccccccccicciciciiciiciiiiiibiiiib``iiii`i`iiaii`ii`iiiiiciiiiiiiiiiicciiiiiii`iiiiiciiiiciciici`cciiciciiiiib`c`aaicciicicciccccciiiiiiiicciiicciilccccclcccccccccccclccccccclccliccccclclclcillccccllcclclccilcccclclcclcccclclclclllcllllcclllllllclllcllcllclcll`cc", +"jjjjjjjjjjjjjjjjjjjjijljjjjjjjljjljjljjjljjjjjjcjljljlcjjlcljlljkclllllllcllllljllllllljllllllllllliiccccclllllcccllcclcclclclcicccccclcclcccccccicccciicciciiciiiicc`ciii`ciiiiiiiiii``ii`iiiiiiiicii`i`i`iii`iicic`ii``iiiiiiiiiiciciciiiicc`ciicici`cc`iiiciciicicciicicci`iiiiiciciicicccibiccccccclcccccclclcclclcclccicclclcicclikclcllclllcllcllclccccllclclcliclci`cllclllillliccclcllclclbilllclllclcal", +"jjjjjjcbljjcajjjjjjjljjjljljjjjjljjjjjjljljjljjjlljcjllljlilllllaljlljjl`mllljcillllclllllllllllcllclllciiilccclllclclclcclckaclccccccccclccccccccciiciciiciiciiciiiiii`ic`ii`ii`ii`````i`iiiiiiiiciiciiiiiiiiiiic`ibiiciiiiiiiiicicicicccicicicciciccccicccciiicccciccciccciiiiiiiiciccii`cicbicciccciiciclcccccccclcclcccccllcclllliclclcccccclcclcllclcccclcllclclccllc`lllcllcll`llllllcllllllilllllllclcclc", +"jjjjjjjljjjlijjjjjjjjljjjjjjjjljjjjjlljjjjljjljljljajljljlljlljcljjlllllljlljijjlllllllllllllllcjcllllaclclllllcccclccclllccclcccccccccccccccccii``a`cccciciciiiiic`i`baba`iiii`ii```ii`ii`iiiiiiiiicciiccciciiiic`ia`iccii`c`iiciciccccccccbcccccic`iciciiiiccciccicicccccccccciiiiciccccccciccccccccccciicii`a``icccccccilclclciaa`iiccciclllcllclcllcllcccllllclllcllllclclcllllamiccaclllllccllllccllclcllcl", +"ljjjjjjjjljjjjjjjjljljnjjljljljjljllcjljjjljjjlbjllblljljjjljlcljclljlljlljclikllljlllllllllllllcljclccccccccllclllllcccccclcclcccciccciccciabbkakbnaccici`iciiiiiiic`bbnnnnkbaa``iiii`ciiiiicici``ic``ciiiaiiiiiiiciiciciiiiiiicccciciciiccbcciiccccccciccciicicicicccccccccicccicccccccliccicliiccccici```iabkbaccccccccclccclciammnbbbaaicclcclclclclllllllllllclllllclllllllllccijlakillcllllllclllllllcjcll", +"jjljjjjjjljjjjjljjjjjjjljjjjjjljjjjjljjjljjljljjljjijjljljl`liklljljllllllillljlllllllllllllllllllclllccccllccclcccccllcclcclccccccccciii`abnnbknmnmblciiibiiciicik`i`nbkmnmmnmnnk``i`niiiiciiiiciicccicccibciciiiciciccciciiciccicicccccccciccccccicc`ccccicibfcccccccicccccccccccccccclc`cclcclcci`abbbbaii`nnk`ccccccclllc`cciiknnnnbbakbkkicllclllclclllllclclllcllllclllllllllllclccllcllllllllkalllcljclll", +"jljjljjjjjljjjliljjljjjjjcljjjjjjljljljljlljjjljljljlljlljlijblljljlljljllcjllllllliklllllllllcllllclccccccclclcccclccclccclcccclccciiaa`babnmnnmnnmkjiiccacciiiiiiiiakkknmmmmmnmnkkbbaiibciiccciciiccccccccccciciicicciccccccciicccccccccccciiiilicicccccccciiiilcccccci`ailciclcccclcccckbcllclciabbkkb`icianmnkcccclllclccccii`ammmnbabknkkaa`iillclllcjcclllllcllli`lllllllllclclllclllllllllllllljcllclllll", +"jjjljjljlljljjjljjjjjjljcejjlljlljjjjjjjjjjjjlljjljljljljljllkijlllllllcjkllllcllllcallllllllllllclcclilcclclclcllcccllccccccccccc`bk```baabnmnmmmfmklciiciiiiiciiciiakbbknnmnmnnnkbbbbaknicciiiicccciiiccicciccccciicciiccicccccccccccccccccccccicililccccccciiccicccccciiclcclcccclilclliclccli`abbbnkkacciaknmkllllllclllcccii`amnnnnbbakbk`i`aaa`ccicclllcclcljclllclllllllllllllclllccllcllcjcj`alllllllcll", +"jjjjljljljjjjjjjjljljjjjljjjjljjjjlllljlljjlljjlljljljlllllljilljllljlciacjlljb`llllclllcllllcclclllccciccclclcccclcccccclccllc`aabnkii`bbabbnnnnnmnbljiiiciiiiii`i`i`kkkbknmmmnmnnbbknkak`iccccicicccccccccccccicccccibccccccclcciccccccccccccciciicni`ccccccccccccccclcclccclcclacclcclclliiciabbbbbbnb`cc`bkknnlllllclcllccc`iiamnknnnkaaknaiiabbabbkkcccclccjcclllllllllllllllillllllcllcccllllccelllccclicl", +"jljjjjjljjjljljljjjjjjljjjjjljbjljjjjjjljjljjlljjljljlljljjljljlllllljlllclllcjllllllllllllcllllll`ccilicccclclc`illcccccccci`bnbaakk``abbb``knbnmmkbllciii`i`i`i`i``akbbbbkmmnnnnbbkkkabnmnb`icccccccciccccccccccccccc`ccccccccciicccccccccccciccccciilcccccccclcclclclcclcclccclicllcllcc`aiiabbbkkbakacl`bnnknklccllllcclciiii`bmnknmmbaabka``akaaaakkicllclccibclllllllllllllc`lllllllcllcllllllcjlccclcc`ll", +"jjjjljljjjjjjjjjljjljjjljlljjjljljljljljljlljljlljllljlljlcjllllllllllllljcjllmclllljcllllccllllcclcccccclcccccccccclccccciibnmmaaabbbbbbbb``bmbbnmnbljc`ii`ib`iikb``bnkbabbknmmmkbnnkbbbkmmkkaccccccccccccccccccccccclccccclccclcicccclclccclciic`lccciccccccccclclilccclcclcllcllllc`cc`abaliabkkkkkabiciabnmnfnlllllcllllii`i`i`nkbbmmnbbabaakbbba`iknbbbciclcl`llljcllllllllllllllllllllcllllclljclcclllclll", +"jljjjjjjljjljljjjjjjlljjljjljjjljjjljljliljljljljlljlljllllllljljljllllllllcllallllclllllcbkbaclcciillclcccccccclcccccccciaaaknknbai`bbbbbbb`bmnkmnnalliii`i`aiii`ii``bbbbbbnmmnmmbkkkbbbbnmnnnbaiccccccccccccccccclcccillcccclclccllcclcclclccccclciliccclclclcclclcllclcclclcllcilclbaabb`icaakbbknkbaii`abknmnbclllccllllii`iiiibabbbkkkaa`abbbbkbaaknnkmnalclllclclllllllllllljllllllllccllclljlclclllclclll", +"jjjljjjjjjljjljljlljjjljlicjljlllljljli`cccjl`cjljlljlllljllllllllllllllljcjlllllllllllllliclclclcmbclicccccccclccccccci`bkbbbbbbaaaabbkbbbbabnmfnbacljici`iiii`ici`iiiaabbbkmmnmnbbbababbnnmknnkbiclc`acccccccclccccclccclccclcllclccccccclccccccicllciccccccccllcclilccllclclclcjccababbicc`bbbbbkknbac`ababbaicllllllllllcii`i`iiiababbbaaaabbbkabbabnnnmmfna`acclclllllllllllllllllllllllllclcalllllclclllll", +"jjjjjjljjljjjjjl`ailjljjjljjljjljlc`aabkbaba`aiclljllljljcb`ljlllllllllllclllclillllclllllcllclcililclkclcccccccccccccannbabbbbaaaabbbbkkkkbbkmnbiljlllciiiiic`i`i`iiillcakbbknfnkbbnbkbabbkmnnnknnbilccccclccccccccclcaacllcllccclclllcllclccccclclccccccccclllcclclbclclllcllllccl`aaabaicabkkbbkkknka``bkbbilljlllllllllcciiaaijlljcabaaaaaabbbkkkbbknmnnmnnmbillccjlllllllllljlllcllllllllllllllllclllccllll", +"jljljljjljjlljljcjljjljljjlljljc`bbknnkkkkkkkbbbbbcjllljllllllllllllljlllllllllcclllclllllclllcacclcccclcliliclcclcc`mfnnmkbkkbaai`a`aakmkkbikmmlllljllciiii`iiii`iin`iljcbbdbnmnbbbmknnkaaakmkmmnbbbilclclclclcclclk`abilcaclclcllclcclicccccclillcccclilccclclllclllclllcclllcllibbbaaailiakkkkbbnkkk`bbbaballllcllllllcllci`a`lljlllcba`c`a`aakmbkbabmmnnmnnmbacllccllllljllllllllbjlljljllllllclllllllclllll", +"jljjjljljjljjljjljljljjjjljjlc`akkkkbkmmnnmnkbkkbkb`cjjllllllllllllllcllllllclacjcllllflclllclc`cclliiicclccclciaciabkkmnmmkkka`cc`a`akbnkkb`knkjllljlll``iiii`aiiiiicccllakkkkkkbbbnnmnmkaabnmnkbbkkbiccccliacllillilccccllli`llllcllllcccclllcailllllcllclcclcclccllllclllllllliabka`i`clibbkknbbkknkbkkbbbillllllllllllllc```llllljjlb`cci``abbknkbianmnnmmnnnallilllljllllcjllljllllllcllllllllllcllllllijjl", +"ljljjjjjljjljjljljjjjjlljljjibbkmnnkknnfffmnnknnnkbaa`clllllllllllllllllclilllllcccllclclcllccccccclcccccccclcic`cannbbmnnmnbaiiii``akkdknkaabmblljllllciiiabknmniiiiiijljannkkkbbbkkmmnmk``bbkkbbkkkkkaclcclclclclcllclbilcclllillllccllccclclcclcliccll`llilcclllllllclllllllliabba````ccabbbkknbbkkbknkbbbiljcljcljcbnkbabballlljlllc`iii`a`bkbbknbabnnnnmnnnnalcicilllllljllllllljclljljllljljjclllllllla`ll", +"llljljljjljjljljjlj`ljljljlikbbmnfnnmmnfffmnmnmmmkkbkbiiljlllllllllllllciaiclcllllllclcllclccccclcccclcclaclcilc``bknnbkmnnnka`bba`adbkbbkbaabmkcljllllciicbnmnnkiiiiicljcanmnmnkkbbkmnnn`ibbabbbknkabknbclclllclllcllclcllllllcllcclllllilllilllllliclllllcjliccllclllclclllccci`aa`i`aii`bnkbkkkbabaakbnnkkacllllllc`knmkbbbbljjjjjlj``abba``bkkabkaaaknmnmnnnbacli`acllljllllljlllllllllljlbblljllljcllclabll", +"jljljljljjljljjljjl`jjljjcabkkbefmmfnfefefffmffmfmfnkkbaillllljlllllcllll`llllllajiclllclcccccclcclcccccc`lciccakbbbknkbnnnmnnnnbaabbbbkbb`iadmmaclllllcclcaknkmmkiiiiicc`knkmmmbkbnnnnnbi`bkbbkbkbbbaakkaiclcclclllcclllcclllllllllllcll`ill`cllcllcllcikllillc`cccclclllllcciii`aa``iii`akkkkbaaaaiakkbknknkaallllcabkknnkabkallllllibkmka`aaabkbbai`akmmnmnnmballcibbillcljllllljlljljlllljcjlllljlllcjlllljl", +"jlljljjjjljljjljlljljlajl`bkkkkmffffffeffefffmmfffmmnmkbbillllllllllllllcjcllclllilllilclcccccccllclcccccccclcakmnbbbbbkbkmnnmmbabnkbbbkk`cc`bmmmb`ciia`iii`aabnnmaa`c`abkknmnmmbbknmmnkbiababbbaba`ba`aakk`clclllllciccllllcllclllllllllclllllllllclllclllllllliclclcclcclli`aaaaaa`iclc`bbnnkbaiiiibnbnbbknkbb`ccci`knnknnkbbbb`cjjikfmnbabnnbakkk`li`knmnmnnnb`icccaabclilljll`llllllllljllcljlljlljlllllljal", +"ljljjllljljjljljjjljlabliakknmknmfffefmffefefffffffffnmkkailllllllllllialclcllilcllc`jicccccicccccccclcccciccbnnknnkbbkbbknnnmnbbkmmnkbbbiccabnnmmnknkb`ccii`abmnbbbmknnmmnmmmnmkbkkfmnkaa`aabaaa``ia`akbbkkacllccllliillllllllljiaaillclllclllcclllllllllllllclclllllcllcciaaabkbb`iicciabbkkba`iiiaknbmkbbkkbabknbabnknkknmkkbkabbkmnknnabnmmkbbbbiliabnmmnnnnbbaicii`akallllljilljjllllicljjlljljcljljllllcbl", +"jljllllcjjljljljlljlljllbbnnmmffffeffeefeefefeeffmmefmmknkbclllllllclllclllclclccccccicccicccclcccccccccclccbnmnnnnmmnbbbbbmnnkknmnnfnbb`i`aabnmmmmnnnbaiii`abbkkbkkknkmmfkmnmmmnkkknnmnaaabkkbaaiii`bknbkbkbcllclclclblllclllllllllllllclllllllcjllclcllllllllllllllllllliabbaaaaa````a`abbkba`a```abbknnkbbbkknkkk`bnmnmnnknnmkbkbabnmnnbnmknfnka`iiaabnmmmnnnnbaci``aabkaljllljlljcljllljllllljlliljlljlljjll", +"llljljjjljljjljljljjljc`bkbmnfffefeeeefeefeefffeefffmmnnmnkaclllclllclllllclccccccclccccccciccccccliccclbccanmnmmmnknmnbkbbkmnnnnfmmnmnbi`abbaknmnmnnkb``iai`aa`aabknmmnmnnnmnmmmbkbkfnnkbakmnabaabaabnakbbkbbilllllllllllllllllllllllllllllllllllcjallllllllllllllllllllinkbabbba`iaaaa`akbb``````abbbbkfmbabnmmnbakknmmnmnmnnnmkbbbbknnnnnnnmnnmbiiabbakknmmnnkb`````iaa`ailjlljljlljlljlljljlllljljljiclllljl", +"jljljljljljljljljjllljlbknkfnffffefeeeeefeeeeefeeeefffmmnkkbillllllllllclijcclclclccclclccccccccicinacciiibnmmnnnmnnnmnkbkbamnkmnmmnmmnabdaabbbbmmnnnba``iaii``ii`bnknmnnmnmmmmnnkkbbknmmkbknnbkkbbbabnbbkkbbnaclllllllllllllllllllllllclllllllllllcllllccclllllliclllllikfkbaaaaa``aba```baa`a`aaaabbbbkmnnknmnnkbbknnnnmmmnnnnnnnkbbbknnnnmmmnnnabaaakabbnnmnnba`````c``i`iicjlllljllllljllllijjcjlllllcjjjllj", +"lljljjljljljljljlcjjllkbknmfnffffeeefeeeeeeeeeefeeefffffmnkkbclllllcl`llllccccklllclclcclcccclcccccilccccbmmnmmnmnmnnnmmkkbbnnnbknmmnmkkmkaabbbaknnkkbaa```c``iiiabkbmmmmnmnnnnmmmkkkkknmnbnmkbkkkaa`bbbbbkkbnkacllljcllllllllllllllllllllllllllllllallllllllllll`cjlljlabkbab`aaa`iaba``abaaaa`aabkkkkkknnnmmnkbabnnnnnnmnnnmnknmnmkbbbnkkbnnnnnkbnnabbbbbbnnnkkaaiii`iaiciaailjjlll`bialjlljjclljljajjljjlljll", +"jlljjljljjljlljlcjllljanbmmfnffefeefefeeeeeeeeeeeeeeeeffmfnnk`clcllllaclllcclllclcllccciclcccccccccclicc`mmnnnmmnmnnnknmfbbbbdba`abbdbabkaaaabkbbnnnkbaa``ic`iiiaaabbmnbkkbbdbbkbbabkknmnmkmnnabkkbabbkbkbnkkkbb`llcjllllllllllljllllllllllllllllljlllclllljlllljlllcjl`kbbbbbaaaa```a```abaab`ii``aabbabbbbbkkai`bmnnmmmbbkbkmnnnnmka`aaa``aabbbbaknbabbkbbknmkb``a`ii``iiiaaacllijlcklllbljlljljlljijlljla`ljj", +"ljjllljljljljlljljjljibbknffmfeffeeeeeeeeeeeeeeeeeefeffemfmnkbclllcllclcicbcclillccccclcccccccccclcciclinmnnmnmnnnmmnnnnnnkaai``lllllcllllcllc`abknmnac`i`clclliaaaabnalllclclcclcllcc`kmmnnnkbbbkbkknkknkkkbbaa`iccclljlllljllllljllllljllllllllacljljlllllllllljllcliabnnbaaabbaa``````aababllllllcllllcllcciii`bnnnnnncllllnnnnnnnilcllllllcllllciadkbkkbbnnnb`i``iciilc`aaaaclealljllcecllljjjljljjljljjjjll", +"jjljljjjlljljljlljllclbbmnmffeefeeeeeeeeeeeeeeeeeeeeefefffmkkb`lclcllllclicciclccclccllccccccccccicccciknmnnmnnmmnnnmnnnkmnbiia`lllllllllllllllliaknnb`i`aclllli``i`abilllllllljllllllliknnnnkbbbbbbknnnkknkbkaiiaillljlllllllljlljlllllllljllllcjjlllcllllllljlllljlcabbbba`babbba```iaaaakkbllllllllllllllclicadkmnmnknlllllmmmnnnmijlllllllllllllccibbkkkkbknna`i```icli``i`aaclljjljljlljjlllljljlljjllllllj", +"ljljljllljljljlljifccibbmmfffeffeefeeeeeeeeeeeeeeeefefeffffnknaccclccclcccicclcllllclcccccccccccccccccanmnnnknnmnnmnmmmnnnnbi`nilllcllllllllllllliabbkb`aacllll```iiabillllllcllllllllllibnmnnbabbbknnnnnnknbkaiib`ijljcljlljljllllcljcljlllljll`cjlcjjlljllljlllllllibbka``bbbkbba`i``abbbknbcclclllllllclllllladnnnmnnncllllnnnnnnnallllllllllllllllc`bkknbbakbbaaaaaiiii```ciaalllllljljljljjjl`jljllljjjjjjl", +"ljljljl`llljlljljlcclaakmnfffffeeeeeeeeeeeeeeeeeeeeefefefffmnnacccccllccilcclclccci`cclccccccccccccccinknnmkkmmknnnnnmmmmnbaaakicclc``ai````illlllibbaabaalcllc`aai`abilllciada```aicllllannnmkbbbknnkkkkkbnbk````aklllllljlllllljlillijllljlllljlllllcjlljllllljljllbknbaa`kkbbkbai``abbbbbnbilclcccici`icccccllaknnnkkn`lclcknnfnmndlllc`````aaaaccclldbnnbbbabbaaaa`a`ai`aaiibb`jlljljllljljlll`ljljjjjjlljlj", +"jjljjljcjljlljlllllllcbnnmneemefeeeeeeeeeeeeeeeeeeeeefeeffmmnbbiccciccccccclcclcccccccccliclicccccicibknnnnnnnnnmnnnnnnmmkabbbbiliclnmmkkkbbkalcclcbbbbaaallllc`ab``abcllllaknffnnnkalllllnkmnbkbbkkknnkkkkkbb```i`alljllllljlljlllilljlljllljlljlllljllljlljljlllljakbkkkaabnmbababbbbkbbbbkacllccciiii``aablllccnkknnnkillcldnnnmnmblllc`kkkknmmnblcillbnnkbbbbabba```aaaaaaa`aabcjlljljljlljljjljjjljlljjjljj", +"lljlljljlljljlljlllllibnkmnmffefefeeeeeeeeeeeeeeeeeeefffeffmmkkaccccclcillcllclclccccccci`icicccciciakkbknkknnnnnknnnnmnnbadbkkilclcnnmkkbbkkbllllcabbkabblcllcaabaabkilcclakknnnkbkbcilllkbbbbbbbbkbkkbkkbbbaa`iaaaallljjllljljljlclljlllljlllllljljljlbcjllljljjj`nnkbknbbabkbaabkbkkbbbbbbalcllcciabb``bnnilclckkknkkniccccannnnnnalllc`bbbbnnmmnillcldbdnkdabbbbaaaabaabab`aabb`ljlljljljljljljljljjljjljljl", +"jjljllljljllllccjcljlcbmknnnfefeefeeeeeeeeeeeeeeeeeeeefffemmnkk`cclccli`clbcblilccccccccciccccciial`kkkkknkknnnnnkknnmnnbdaabbkclllcknmnkbkmn`cclliaabbbbbcllclabkdaabilllcbkkkmnkakaliilibbbakbbkbkkbbkkbkkba``ibbaailllljlllllllllljlljljlljljlllillllljlljlljllcaknnbbkkbaaa``abkkkkbbabaa`cllcci`abkkabkkclllcbkknkknilllcannnnnk`clcc`bkbabmnnkllccldkkbkb`abbbbbkbbbbbbbbaabknajljlljljljljljljjljljllljll", +"ljlljjjljljljlccjlllccamnnnnfemeefeeeeeeeeeeeeeeeeeeeeffmffmfnbacclclllillil`cclcclccccclccccciia`canbkknnnnmnnnmnnknnmkkiidnbbclccl`aa``i``illclc`abbbkkbcllllbbkb``b`llcl````a`a`illicl`bbbbkkabbbbbabbbbkb``i``aaaalljlljjljjljljllljljlljllljlcalljlljllljlibjcbkmkmkbab``iiabbbkbbkbabbaailllcci``iii`icllcl`kkknnnm`cllcannnnnb`lllli```i`ab`ccllccnfnnkdbabbbbkkbakbabkbaiabnkijjljljljljljljjljljlllljlj", +"lljlljcjlljlljclclljclamnnknnenefeeeeeeeeeeeeeeeeeeeeefemffmmnb`clcllcllcclccclicccccccccccccciiicinnkknknmnnnnnmnmnnnnkbcc`mnklccllllcllllclccll`kkkkbbkkclclcbbkbaabillclcclllclllllllidbbkbnkbkbbkkbkbbkbaii`aiabbklljlllljlljlllljljlljljljlljlcjljllljjjljljlbbannnmba`ii`akbbbbbbbbbkaad`lllclllllllcljlllidmkbnnmn`cllcannnnnb`llccclllllllccllcldkmmnnmkkbbbbkbbbbbbbbbb`babkaljljljljjljljjljllljljljjl", +"jjljlljljllljllljjcc`l`bnmnnkeffefeeeeeeeeeeeeeeeefeeeeemffnnnb`llclccccclccclciclccccccccicccciccknknkkknnkkkbknnnmnmnka``dnkkllclccllllllcllll`kmnkkbkkkclcllbkabbabclllllllllljlllllibkkabbnkknkbbbbabbkbai`aa`abkkbjljljljljljljljljljljljljljjlljljjlljlicjliakkkmnnkkacc`kkkkbbaababnkabalccclllllcllllllcdnnkkmnkkallll`nnnnnballiclllllllllllll`akkkmmnnkkbkkbbbabbabbbabbbbmnclljljljljljjljljljljljjjj", +"alljaklllljlllllllllllibknmnneefefeeeeeeeeeeeeeeeeefeeeffmfnmkbicccllclllclcliibcccccccciciiiiiic`nmnnnknnnbbbbkkknknmnkbbkkkbdccclcccllcijcjlcbnmnmnbbkbklllllbkbabbbilcclclllllcllc``kknnbbbkknmnbkkbabbbaa`aaabbabkncljlljjljllljljljljljlljljljljlllljljljjllkbbaannkknac`bkkkkkbbbbbknkbb`lclllllllllcllcibnmnkkmkkb`ccccannnmkbkllllclllllcllllidkbbknnnnnnkkbkkkbbbbbbbbaabbkmmaljjljjljjljljljljjjjjljjl", +"ljjl`ajljlllljlljllllliinbmnmmefefeeeeeeeeeeeeeeeeeeeeffmnmnnkblclccclccclilillccccccciciciiiicccamnmnknmnkbknnnkbbkkmmknmnmkballclc`aacl`ccibknnmnnmnkbbkclcclkbbbbab`lllc```aaab`iakkkbnmnkbbkkknbbkkbkbaii`babbbbakb`jlaaljllljljljjljljljljljljljljjjlljjljj`nkbbabkknk`cbkkbbkkbbabnkbbbb`lcllcaailllll`bbmmmknnnkkballlcibkkmnnmllclci`iiaaabbkknkbkbnnnnnnnnbbbbbbbbbkabbabkmnmmcljljljjljjljlljljljljjjj", +"lllljljlllbllljllllllllcaknknmffeffeeeeeeeeeeeefeefeefemmfnnnkacclccclclcccccccccccccccccciii`ciabnmmmnnnnbknnmnmmnkkkknnnmkdballcclbmn`llcll`nknmnknkmnbdlcllcbkbbbbk`lcllkkkknnmnkbbknbnknkbkbnkbbbkbakkaiabaabbbbbbabjljcljljlljjljljljljjlljljljljljljjljjclannbbaanbkacikmnkkbbkkbbnnnkbaacclcibbbilcclcaknmnmknmkbbklllccmkkbnkmclclcai``abbnnknnnkbkkknnnnnknkkbbbbbabkbbkbknmmkaljjlljjjlllljjjjjljjajlj", +"jljljccjllljllllljllilll`bbnmmfffefefeeeeeeeeeeeefffefenmfmnkbiclccclcccccccccccccccccccciiiiiii`bbknnnnknbbmnmnnnnnnkkkmnkaad`lcllcbkmk`cllll`knmnmnknnnkllllinbbkmnn`clllbnkknknnnkkkkbknnnkbbkkkbkb`iaba`abbbbbknnbbbllilljllljljljljjljjljljljljljjljjlljia`bnkbb`akab`aaknnnnbkbbaabnnbbbdllliibbbailllclabnnnmkbkkbkclcccmnnkkkniclcl`i`akbbknknknnbbkknnmnkknnkbbbabbkbbnknnnnmnnljljjjljljljljljjjjljjlj", +"klljljjclljlljllllll`cllcakbmmmfefmffeeeeeeeeeeeeefeeffmmfnnbailclccccclccccccccccccccccciiici`iabbbbkkbkkbnmnnknmnnnnnkkkbdab`lcccibknkn`clillakmnnmnkkmkllccinmnknmncjcccknnkkknmmnnbbkbnknnbbbkbabaciaaaabaabaknmnnbbilijljljlljjljjljljjljljjljjljljljjjljjaknkbbaaba``bknnnmnkkbbabbknkbbblllccbbbbailllcc`bkbkkbkbkklclccnnmnnknillcc`c`abbbknnnnnmkbkkknnnnnnnnnkmkbkmnknnmnknnmm`lljjjljjljljjjjljjcjljj", +"ljlllllljlcljcllllllillll`bbkkmmfffffefffeeeeefeeefefffmmmnkb`ccacclcliccclccccccccicciciiciciaibbbkbkbbakbmmbnnnknnnnnnkkkbbaiclllcbnmknncllllldmfknmnknkllllikbknnnniilllknnkkknnnmmnbkbnknnkkabbbaccaaaaabbabknnnnnnkallljjljjljljjljjljjlljljjjjljjljjjljlcbknkkkbbda`aknnnnnmnkbbbbbbbkbbalcclcknkkbaicllil`bkbbbkdmnlllclnknnnnk`llclii`bbabknkkknnbbbkknnnmnnknnnnnnkbkkknnknnmkkbjjjljjjljljljljjjanjjjj", +"llmjjljllllllilljlllllllllikbbnmmmfffffefeeeeffeefffmffmmnkbaicclcccccclccccccccccccccciiiiicc``bbkkknkbakbnnknknnkkknnnmnbbbaclcccibnmnmnncllclldmnnmnnnnllclckkkbkmmilllcknnnnbkknmfnnbkknkknnkabaic`bbbabbbbknmnknnnnkljjljljljjjljjljjljjijljljljljjjlljjj`ikkkbnkbaaadnnnkknmmkbbbbbbbkbballlclbbbbbabclllcidnkbbbbnmlccilnkkkknn`llccciabaabnmnnkmnnbbbbnknmmnkkkknmnkbbkbbnnnmnnnflljjljajljljjjjjljjjjjj", +"illllllllljllclllllllclllll`bbkmnmmfefeffeffefefmeffmffmknkaiclcclclcccccccccccccccicciciicici`aabbbkknkbabknnnkkkknkkkkkknkb`clcllibknnnmkkcllllcdkmfmnnkllllcbbbbabbclcllknnnnkkknnmnbbkkmnknnnkb`c`bbkbbbbbbbknnnnmnknlljljjljjljjljjljjljcjjjjjjjjljljjjllibkkkknnbaabknmnkkkkknkbkkkbbkknklcclcbkbbakdbilllliakkbabkncllllnnnkknkalllliiaaabbbnnnknmnkbbbkbknmmnkkknkbbkbbaabbknnkkniljjjjljjjljljjjjjljjlj", +"lcljlljlllllljlllllllclllillabbnknmmfmfffffffffffmefmnnnkkaicclcilccbcccccccccicciccciciiciiii`babbbbbbkkakkkmnnknkkkkkkknmnda`clllibbbbbakbacllcl`knmnnnklllc`nmb``bbcllclknnnnnnkknnkbbbknnkknnmna`bbbkbbbbbabkknnnnnnncjjjjjjjljjjjjljjljjjljjljljljljjljjj`anknnmnbbabknmmnnkabnnknmkkbaknkcllllbbkbbakaaillclibkbbbbkiclcckkkknkkaclll`bbkkbaabbkabkbbbabbkkbnmmnnkknmmkkkb`abbkkkbkbjjlljjjljljjjjjjjjjljj", +"cbjcjllljlllllllllllcllllllclcbbkkmnmnffffffemmmfnmnmknkkbiccccclccliclcccccccciccciccciiiiciciabaabaaabkbnkknnnkkkkknnnknkdakndbkbabbbbbnbkbbaaa`bkbkmfnmbkkbbnna`ammakaaamnnnkkkkkknnkkakknkkkkmnaabakkbbabbbkknnnnnnmkajjljljjjjljljjjjjjljiljjjjjjjjjljjcccknknknnbbdknnnmmnbbbknmmnnnkkbbbabababbbbbkkkbaaa`aiabakdnkdbdbbkbkkkkkkab``bnnnnbkbbbbbbkbkkbbabbbbnnmknnmknmmm``bnmkbkbknjjjjljjjljljljjljjj`jj", +"lljljlllllllllllllcllllllllllc`abbkknkmfmmfmfknfmmnnnkkba`cclliliccccncccccciicciccciciciiiiii`bbkbaaabbkbnnbkmkkkkkkbkbkkd`bknknnmnkabbnbkkbknbbbbbbbknmnnnnmnbkiannmnnnknnmnnkkkkkkkknnkkknnkknnnbbabbbkbbbbnkbknnmmmmnbcjjjjjjjljjjjlj`jjjlljjjjllljljljlliiknnnnnnkkbkknknfnkknknnnmmnmnmkkbbknkbaabbnknkbbbbaa`bbkbnkbkmnkkkkbbkbkkaabmkknnnnbbakkbkkkkkbakbbbdbmmnmkmnkbbcdnnmnknbkma`ijjjjjjljjjjljjjjljj", +"ljcbaljllllllllllclllllllclcllcci`bbnbkbnknnmkknkkmkkba`ilccilcclcicl`cccciclcliccicicic`icici`nkkkbaabbkbknkbkfnnkkbbkkbbaannknnnnnnbkbkmkknnkkabaaabbbknnnnmfnk`annnmmnnnnnmnnknnknkknnkkknnnkkkkkkbabkkbbbbbkkknmmnmmfkjjljjlljjjlcljjljjjljjljljjjljjlljii`nknnnmmkbbbbnnnmnknknknnnnnmnmnnnkkkkkbaabkmmnbbnbbaabbkbkkkkknkkkkbkkkkdabknknnnnnnbbbkkkbkkkkbaadbbbkknkkmmfkkibnknnnnknmajjjljjljljljjjjjjjjjj", +"lljci`cjlllllllllclllllllllccllllc``bbkkkkknknnkbnkba`icccccc`ccccccclcccccccaciciciciciiiiiiiankkmkbbbbbkbbkbbknnnnnkkkbaaknkknnnmmmkbbbnkbknkkbbabbbbbbkmmnmmmb`kmknnnmnknknnnkknnnnknknnkkknnnnkkmnkbbbbbkbbnnkkmmnnnnmjc`ljjjjjjjljjjjlj`jljjjljljjlljlj`c`nnknnnmkbbaabkbbknmnnknknnnnnmnmmnkbnkbbbkbnmnnknnkbbbbbbbkkbknnnnknkbkk`akkkknknnmnnbbknkbkkkkbaaabbabbknmnnmnk`kkknnnnnnbillljjljjjljjjjjjjjljj", +"jlljcillllllllllclclllllcllllcccclcliiaabbbbbkbabaaiiiiccccccacccclciciccccciaiccicicicici`iiibnnkfnknbbbkaaknkbnnnmnnnkkbknnnkknnnnmkbbbbkbbkkbaaabbbbbbknmnknkbbknnnnnmnnnnnmmnnknknkkkmnnbkknkkkknnkbbbbkknbkkkknnnkknfjjjjjljjlljjjjljjj`jjljljjlljijjjjccaknnkkknnkb`iaabknnmnnnnknnnnnnnnnmnbkkkabkbknmnkkfnkkkbbbabbbbbknnmnnknkaknknnkknnmnnbbbbkabkbbbadaabbaabnmnknkkaknnnnnnnnfkljcjljljljjjjjjjjjjjj", +"llllcjllllllcllllclllclclclclclcccclclcliiiai``icccccccccccibcccccccccccciciiiiciciciciciiiciinmnkmmnmkkbbbbbabbnnnmknnnkkknknnnnnnnnkkbabbbbkka`aabbbbbabnnnnkknnkmnnmmnmnnmmmnmmnnkkkknnnmkbkknnknnnnnkkkkbmmkkkkkkkkkbnjjjljjjljjjljljlljajljjjjljljjjljjjc`kmnnkknnnba```akmnnnnmnnnnknnnnnnmnkkkkbbkkknnnmknmmmnbbbkbbbbbknnmkknnkknknnknnnnnkkkbabbbbkkbiabbbbbbbdnnkkmbknnknnmnnmkmbjinijljljjjjjjjjjjjjj", +"llljllllllllllllccclllcllclclllcllcccccccccclcccccccclcccccc`lccccicciccciccicciciciciiiiii`ickmmnmnmnbbbbbclciicccilcciliiilciccicccccclcclicllllcclcccllciiciciiciiiiicciiiicci`iiclliiiciiccciiiicciiiccccamknkkbkbkkkblljljjljjjjjjjjjjljljjlljjljjjjjjlllinmmnnnknmkaacccciiiciiiiiiiciiiciiiicccclciiicc`cciccillccclclciiciclicliicccliiciccccccclccccllllciccccliiciiiakkknnmmnmnnkciijljjjjjjjjjjjjjjjj", +"ljlcljlllllllllllclllcllllccccccccclccllcccccccccccccccccccclccccccccicccicci`iiciiiicicii`a`ikmnmmmnkbabbijjjjjjjjljljjjjjljjjjjljjjjjjjjljljjjjjjjjjljjjljjjjjjljjljjjjjjjjljjjjljjjjjljjjljjjljjjjjjjjjjjjinnnkkbbbbbkbjjjjjjlljjjjjjjjljjljljjljajjjjjjljccknnnnknnmkbacjjjjjljjjjjljjljjljjjjljjjjjjjjljjljjljjjljjjjjjjjjjjljjljjjljjjjjljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlikkbknnmnnmkncjljjjljjjjjjjjjjjjjj", +"llllllcllllllllcclclclclccllclclclcclcccccclilccccccccccilicciccicccccciciciibiciciciiciiia``immmmnnmknnkkillljcccjljclljlllccjijljcllljljlljjljljjljlcjlllllllllcllljlcjljjljllljjlclljllilljllclljlcicjclclcknkkkkkabbbkljjjjljjjjjjjjjjjljjljjjjjjjjjjjjjjcianmnnnknmnbacjllclcjcjcjcjllllllllljllllccjlllllljljllcjjllllljlljljjljljjjjlljjjllllllllcjllljllclljjllljljjllabbbbbknknmkn`jjljjjjjjjjjjjjjjjjj", +"lllllljcllcllllccclllclcclclclclcccccccccmkcccilccccccccccccccicccciccccciciiaiiciiiiiiiciii`immmnmnnmmnnkclnmfffffffeeffmmffmmffmfffffmmffffeffemfffffmmmffffffffffffffffmmmffffffmffmmfffffmffffmffmmmmffmmkkmnnkbbnkabkjailjjjjjjjjjjjjjjjjjjjjjjjljjjjjjlcibknmknnnnmkdidkkkmnnnnmmmnkknkkkkkkkknkkkkkknknknnkkkkkkbkbknknnkkknnkkkbkkbbbbbkkbbbbbbbbnkkbbkbkbbkkkabbkbkda`dbbbbbbkbnmnljljljjjj`jljjjjjjjjj", +"jlllllcbccnnclclllklclllllcclcclcllccclcciclclciccccccicccccicaiccccciciicciciciiicicicii`ii`innmmnnnmfnmnilmffffeffffffffffffmffmmffffmmfffffffffffmfmmmmffffefffeffeffmffmmmfffmmmffffffffffffffffffmmfffffnbnnkkbbkkkbaljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlcibbmmnknnmkka`bnknmmnnnnnnknnnkkkkkbkknnkbkkknknknnkkkkkkbbbkkkkkkkkkkknnkkkkbbbbkbbbbbkbbbkkkbbbkkkkkkbbbbkkbba`bbabbbabaknniljjjjljjjjjjjjjjjjj`", +"c`iljllllliclclclkicllicllccccccccccccccaaiciclcccciclilcicccc`cciciccciciiaiiciiiciciiiii`i``nmmnkknmmmmmilmmmfffffmmffffffmmffmfmmfffmfmmfffffemmffffmmfffffffffffffffmffmmnmffmnmmfmmfeffffffffefffmmfffffkbknnmkbbknkbljjjjjjjjjjllcjjjjjjjjjjjjjjiljjjjliibknmkknnnmnnabkkkmnmnkkknknnkkkkknkkkkknkkkbkknknnkkkkkkbbkkbkkkbkbkkkkkkbkkbbbbkbbbbbkbkkkkkkbkbkbkbkkbbbkbkba`bbbbbbaabkknbjljjjjj`jjjjjjjjijjj", +"lljlcclllllllcccclcllcllccclclcccccclccccccclciccccccciccciccaccccccicicciibiiiciiiiiii`i``i``nmnnnkknmmmnicffffffffmmmmfffmmffmffmmfffffmmmffffffmefefmfffffffnfmmmmmmmmffffffffmmmfffffffffffmefffmmffffffmnbbnnmmkbbbknjjjjjjjjjjjjjjjjjjijjjjjjjjjljjjjjliidbnmnnnknmnnaaknmnnnkkkknknnkkknkkkkbknnknkkbbkknnkkkkkkbbnkknnbbbbkbkkbbbkkkkbkkkbbbbkkkkkbkkbkbkkkbbkbkbkbkkaiabbabkkkbbbbajjjjjjjbljjjjjjjljjj", +"llllllllcjccjlcllcclalccclclaccccccccccccccccccccciccicicccicbcicicicicicic`iciiiiiiii`iii`ii`nnnnnnknmmnmccmffffffmfmfmffffmffmfffmfeffmfmmffffffmfffmmmmfffffmmmmmmmmmmffffefffffmmffffmfffffffffmmfffefffmnkbkkmmnkbbbkjjjjjjjjjjjjjjljjjjjjjjjjjjjjjjjjjci`bbkmmnkknnnn`annnnnnnnkkknkkkknkkkkkkknnknbkbkkkkkkkkkkkkbnkkkkkbkbbbbbbbbkbknnkkbkbkbkbkbbbkbbbkkkbbbkkkkkkkbbibababbkkkkba`ljjjjjjjjjjjjjjjljjj", +"llllllcllclliccclllclccllccik`lc`ccicccccclccccccccciclccicccciciciciciciiccciiiciiiiiiiii`ii`nmnkkkknnmnnlcmmmmffmfmffffffmmffmmfffefffmffmfffmmmnmmmmfmfffffmmmfmmmfmmmfffffmmffffmmfffmmfffffffmmmfffmefmnnkkbbknnnkbabjjjjjjjjjjjjjjijjjjjjjjjjjjjjjjjjjlc`kkkbknnnkmnnaakkknknknnkknnkkknkkkkknmnnkkkkkkkkkbbkbbkkkbkkkkkbkbkbbbbkbbbkbkkkbkbkbkbbbkkbkbbkkkbbbbkbbkkbbba`abbbaabknkkb`ljjjjjjjjjjjjjjjcjjj", +"cllcclllllccccclclclccllcclliccciclcccccciccciccicccicaccciciciiciciciciiciiiiiiiii`i`iiiiiiibnmkkkkkknnnmcifmfmmmmmfmmffffmfffffffeffffmffffefmmfmmffmfmfefmmmmmmfmfmmmnfmmmmnmmffffffefmfffmfffmmmmmmnmfmnmnbkbbbkknnnkbljjcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjli`bmbbbknmkkkkbannnknkkkkkknnnknnnkkknnnnkkkknknkkkkbkbkkkbkbkkkbbbbkkbkkbbbbbbbbbbbkbkbkkkkbbkkbkbkbbbbbabbkbabb`abbbbaaabkkkbljjjjjjjjjdcjjjjjjjj", +"lccllcilclllccclllclcccccccclcclccccc`biccccccc`ccicicfiiccciiliiciciciiciiciiciiii``iii`iiiibnmmnkkknnnkklcmmmmmmmmmmmmefffffefffffmmffffmfffffnfmfffnfmfffmmmmfffmfmmmffmnmmmmmnmmfffffmfffnmfnmnmnnmmmfnmfnbkkkkkkknnnmljjljjjjjjjjjjjjjjjjjjjjjjjjljjjjjlciamkba`knnnnbb`nknkkkkkkkkknmkknmnnnkkkkkknknknknnkkkkkknkkbknbbbkkbkbbbbbbkkbbbbbbbbbbbkkkbakkbbbbbbabbababbbbb`aba`aaaabbbbbjjjjljjjjjjjjjjjjjjj", +"lclccc``cllccclclccclccclcccccccccccccccbaiccci`cicicciicciciciciciciciiiiaiiii````ii`iiii`i``nmmnnkknnmnklammffmfmmmmmmmmffffmffffffffeffffemmmmffffffmmffmmmmffmffmmmmfffmmnmmnfmmmffffffffffmmmmnmnmmmfffmmnkkkknkbkmnnjjjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjcccbnbbabbbnnknn`nnknnkkkkkkkkknnnkknnnnknkkknnkkbkbbkkkkkkkkkkbbbbkkbkbkbbbkkkbbbbbbbbbbbkkkkbkkkbbbbabbabbbkbbkbbbnbi`ii`abkbbjjjjeajjjjjjjjjjjjjj", +"ccllllcclclcclcclcllcclccccclcccccccccciciicicccilckciciiciciciciiciiicicii`i`iiii`iiiiii`ii``nnmnnnnnmnnklammfffmfmmmmmmmffmmmmffmfffmfffmffnmmmfmfmfmmnmmmmmmmmmmfmmnmmmmnmmmmmmmnnmmmefffmnmfnnnnmnnmnnffmmkbbkkkbabmnkljljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl``abkbkbbbknnmm`knknnknkkkkkkknkkkkkkbkkkkknnnknbkbbkkkbbbbbbbkbbbbkkbkkkbbbbbbbbbbbbbbbbbbbkkkbbbkbbbbabbaabbbkbaakbiiii`aabbkcljjjjjjjjjjljjjjdkj", +"clllclclimalccllcclclcccclcccccccccccccccicccciiiciiccicciciciciiciiiiic`iakciiiiiiiiii`ii`iiakmmnnnnnnmnkjanmmfffmfmmmnmmmmmnmmmmmmmmmmfeffmmfmmffmmffmmmmmmmmmmmffmmmmmmmmmmmmmnmmmnnmmfffffmnmnmmmnmnnmffmnnbbbbbbabnmmcjjjj`jjjjjjjjjjjjjjjjjjjjjjjjjcljlii`knkbbbkkbnnn`kknnnnkkkkkbkknkbbkkkkbbkkbknnknkkbkkkkbkkkbbbbkbbbbbbkbkbbbbbbbbbbbbbbbababbkbkbbbbabbbbababbbbb``ka`icc``aabkljjjjjjjjjjjjjjjjjjj", +"lclcllcccclcllcclc`kcclcclccclcacicciliccccccccccicciciciciciciciiiciciiii`i`iiiiiii`iiaiii`iakmnmnmnnnnnblbmmfmfffmmmmmmfmmmmmffmmnmnmfmffmmmmmmfmmmffnfmmmmnmnmmmmmmmmmmmmmmnmmmmmmmnmmmmffmmmmmmmmmnnffmfmnnnbbbkkbbbbbcjjjjiljjjljjjjjjjjjjjjjjjjjjjjljjlc`aknnkkbbkbbbk`knnnnnnnkkkkkkkkkkkkkkkkkbbkbnkkkkkbkkkbkkkkbbkbbbbbbbbbbkbbkbkbbbbbbbbbbbbbkbbbkbbababbbbbbbbbkba`bb``lciaabkncjjjjjjjjjjjjjjjjjjj", +"llllllccccllcclccccciclccclc`iliccclccicicciiicccicciiciciiciiiiciiiiiii`iicii`c`iiiiii`iii``annmmnnnmnnnbjbmffffmfmmmnnmmmmmmmfmmffmmmnmmfmmfmmmmfnmfmmnmnmmmmffmnmnmmmnmmmmmmmmnnmmnmmfmknffmknnmmmnmmffffnmnnnkbbkbbba`ljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjli`annnnnkbbbb`aiknnknknkkkkkkkkkbkknkkbkbkkbkkkkbkkbkkbbbbkbbbbbbbkkkkbbbbbbbbbbbbbbbbbbbbabkbbbkbbbbbabbbakbkbbbb`kbailciankkn`jjjjjjjjjjjljjjjjjj", +"clllccllllccjcilccllnacllicanaclccn`ccikaicccciicci`cciciciciciiii`iiii`ib`iiiciiiii`iiiii`i`knmmmnmnnmmmblkffffmfmmmmmmmnmmmmmmmmmmmmmnfmfnmffmmmmfmmfmmmfnmmffffmnmmmmmnmnmmnmmmnmmmmfnffffmffmmnmnmmfffffmnnnnnkbbakn``jjjjajji`jjjjjjjjjjjjjljjjjjjjjjjjl`i`nnnnnnbka`ciinnnnnknkkkbkbbkkkkkkkbkkbkbkkkbkbbkkbbkkkkbkkkkbbbnknkbbbbbbbbbbbbbbbbbbbbbkbbbbkbbbbbbbabbbkkkkka`kb`clciannkk`jjjjjjjjjjjjjjjjjjj", +"clclcclccclicllclcccacciccciicciicacccli`cccccccicimiiciiiciiiiii`i`i`i`iicii`iii`iiii`ii`ii`knnmnnnmmnmmblkmffmfmmmmnmmnmmmmmmfmnmmnmfffmfmmfmmmmmfmffffffmmffmffmnmmmmmmmmmmmmmmmnmmfmfmmmmmffffmmmnmffmffmmnnmnnkbbknblijjljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiannnknkbbi`ciinnnnnkkkkkkkkkkkkkkkkkkkkbkkkkkkkkkbbkkkkkknkkkbkkkkkkbbbkbbbbbbbbbbbbbbbbbbbkkbbbkbkbbbbbbbbkbbbbibbicci`kknnnajjjjjjjjjjjjjjjjjjj", +"llcllclclcjcccclclcclcccbcccccccccicicicccciciiccccicciiciicicii`iii``iiiiiiicii`a`iii`iiiiiinnnnnmnnmnmnbjkmffmmmmmmmmmmmmmmmmmmmffffeffnmmffmmmmmmmnffmffnmffmmmmnnmmmmmmmmmmmmmmnnmmnnmmfmmmmmnmnnmmmmmmmmnmnnmnnkbbba`jijjjjjjjjjjjjjjjjjjjjjjjjjjjjljjjl`iaknmkkb`iiii``nnnkkkkkkkkkkkkkbbkkbbkknnmnkkkkkkkbkbkkbbbkkkkkkknkkkbbbbbkbbbbkbbbbbbbabbbbabkkbabbbbbabakbbbbbb`ab`````nnnkk`jjjjjjjjjjjjjjjjjjj", +"lcllccccccclclcccccccccciccccccccccccicciciccciciiciciciiciciiiiaiia`iiiiiiiiiii```i`iii`iiiikkkbmmmnmmnnalnfmmmnmmnmmmmmmmmnmmnmffmffmffffmmfmnmmmmmnmffmfmffffffmmmnnmnnmmmmmmmmmmnnnmmmmmmmnmmmmmmmnnmnnmmmnnmkmnnkabi`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjii`nnnka`ic`a``innknkkkkkkbkkkkkkkkbkkkknkkkknkkkkbbkkbkbbbkkkkkbknbkkbbbbbbbbbbbbbbbbbbbababbkbbbbbbabbbbbabbabbbcibbai`akkmkkbjjjjjjjjjjjjjjjjjjj", +"lccllccclclccclccccccccccccicccc`ciliciccccciccccciciciciciiiii`faiii`iiiiii`ii`ii`ii`iiiiiiikmnkbmmnmmnfalkmmfmmffnmnmmmmmmmmfmffffffnmfmfmffmmmmmmmmmfmfffffffmffmmmmmmmmmmnnmnnnnmmmmnmmmmmmmmmmmmnnmnnnmmmmnnmnnnkba`ajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj`i`nnbbaa`iaaa`inkkkkknkkkkkkkkkbkkkkkknknnbkkkkknkkkbkbbkbkkbbkkkkbkkknbbbbbbbbkbbbbbbabbbbbbbbbbbbbbbbbbabbababa``bba`aabbbnfkjjjjjjjjjjjjjjjjjjj", +"clcllclllclccclcclcccccccccclciccicccccciciiiciiciciciiciiiiii`iicciiiiiiiiiiiiiiiiiii`iiiii`nnmbbkmnmmnk`jnnmmmffmmnmnmmnmmnmfmmmfffffmfmfmffnmmmnmmmmfnfmfmfffffffmnnmmmmnmmnmmmnmnmmmmmmmmmmnmmmmmmmfnnnnnnknnfkkknn``aljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjji``nnnbbbaaabaaikknkknnkbkbkkkkkkbkknkkknnnkkkkkkkbbbbbbbkkbkkkknkknkkkkbbbbbkbbbbbbbbabbbbbbbbbbbbbbbbbbbbbbbabba`aba`abbbkbakkjjjjjjjjjjjjjjjjjjj", +"lclclccccccllccccccccccccciciccclic`cciiiciiiiciiiicicciiiiiiiciii`iiiiiiii`iii`ii`i`iii`ii`akbmnabnnnmnb`jffffmfffmmmnmmnmmmmfmfmmmffffffffffmfnmmnmmmfmffmmfffmmfmmmmmmnnmmmmmmmmnmnmnmmmmmmmnmnnnmfmmfmmmmnknnmnkknkaabjlljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlic`bkmkbbbabbaainnnknnnkkkkbkkkkkkknkkkkkkknkknkkkkkkkbbbbbbnnkkkbnnkbbbkbbbbbbbbbbkbbbbbbbbbbbbbbbabbbabbbkbbbbbba`baaabkbnbbkbccjjjjjjjjjjjjjjjjj", +"clcccclcall`lcclccccccccccccbiciicicccicccciicciciciciiii`iiiiiiiiccii`iiiiii`ii`iiiiiiiiiciabbkbbbbmmnnn`jffmmfmfmmmmnfmmmfffffffmmmfffmfffffffffmnmnnmfffmmmffffmmmmnnmnmmfmfmnmmmmmmmmnmnnnnnmnnmmmmmmfmmmnnknknkknbbbkljjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjj`iabknnnkknnkbbinnnnnnnkkkkkkkkknnknnkkkkkkkkkkknnknnkkbkbbbnkkkbbkkkkkkbbbbbbbbkkkbkbbbbbbbbbbbbbabbabbbbbbbbbbbbaabababbbbmnkbljjjjjjjjjjjjjjjjjj", +"clclclcl`lcmcccccccccccccciikiiiii``iiicicciiccicicicii`iiiiiiiii```iiiiii`iiiiii`ii`iiiiiii`bbbbkbbmmnmmilmfnmmmmmmmmmmmmffffffffmmffmmffmmfmffmmmmmmmffmfnmmffffmmmmnnmmmffmmmmnmmmmmmmnmnmnmmmfffnnnnmnnmmnnkkknnkkbkbkljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjji``mnkkknnmmkbbiknnnkkkknnkkkknknnnnnnnkkkkkkknkkkkkkkkbkbkbkbkkbbbkkbkbkbbbbbbkbkkbbbbbbbbbbbbbabbbbbbbbbbbabbabba`b`bkkkkabmmbljjjjjjjjjjjjjjjjjj", +"clcccllclcikcc`cccccccciiciib`iiiiabibiiiiiiciiiiiiiiiiiiiiiiiiiiiiciiiiiiii`i`iiiiiiiiiiiic`kbkbbbbnmnnmicmfffmmmfmfmnmfmmfffffmmmmffmffffmmnmmmmmmffmmmmmnmfffnmmmmfffmffffmnmmmmmmmnnmmnmmmmmmffmmnmmnmmnmnnnkkknkkdbbkljjjjjjjjjjljjjjjjjjjjjjjjjjjjjjjjji``mnkbbkknkkbaiknnnkbkknkkkknkknnknkkkkknnnknkkbkbbbkbkkkkkkkkkbbkkkbbbbkkbkbkkkbbbbbbbbbbbbbbabbkbbbkbbbbabababaaiiibknnkkknnnijjjjjjjjjjjjjjjjjj", +"cclcclcclccclcnicccccciiiiiiciii`ca`iaiciiiciiiiiiiii`iiiiiiiiiiiiii`ic`i`iiiii`iii`iiiiiiii`bknbkkbbnmnnilnfffnmmmmmmmffmfffffmmfnmffffffnmfnmfmfffffmnffmmnfmmmfmmfmmmmffffmmmmmmnmnmmnmmmmmmfmnnnmmnnmnmnnkknkkbnnkbbnbljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjii`kmnkbbkkbbbbiknnnkknkkkkkknkknnnknkbkkkkkkknbkkkkkkkkknnkbkkbbbkbbbkkkbkbbbkknkbbbkbbbbbbbbbbbbkbbbbbbabbbbbabbalilbkkkknnnnmijjjjjjjjjjjjjjjjjj", +"lccclccclcccccccccccciiiiiiiiciiiiii`i`iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii`ii`iiiiiiiiiiciankbbkbbbkkmmccnfffffffmffffmmnmmmmfmmmfffffmmfffmfffmmfffmmffffffnmmfffmmmfmfffmmmnfmnmmmnmnnmffmnnnnnnnnmmnmmnnnkknnkkbkkkknljjjjajjjjjjjjjjjjjjjjjjjjjjjjjjjjjc`iknmnbbbkbbba`bnmnnnnnnnnnnnkkkkbnkbkkknkkkkknnkbkknkkkkkbkknnkkbkbkkkbkbkkbbkkbkbbkbbbbbbababkbbabbaabbbaabbbaaalccankknkbnbkajjjjjjjjjjjjjjjjjj", +"lclliclcccccccccccccciiia`ccciiicickkiiiiiiii`ii`iiiiiiiiiiiiiiiiiiiii`ii`iiii`ai`iiiiii`ii``mnkkbbbabkkklcnnffffffffefffmmnfffmffffmmmmfmfmmnmmffmnmmmffffffmmmmmmmmmffmfffffmmmmnmnnmnmmmmnnnnnnmmmnnkmmmnmnbbknnkbbknnnljjjjjjjjjjjjjjljjjjjjjjjjjjjjjjjjjia`kbnmnbbbbbkaabknnnnmnnnnnnnkkkknkkknkkknkkbkbkkkkknkkkkknnkkkknkkbbkbkbbkkkkkkkkkbbbbbbbbbbbbbbbababbbabbbbbbkbalciakkknnknkkdjjjjjjjjjjjjjjjjjj", +"cclibccaccclcccccccciiicaccciiicic`aai`iiiiiiiiiiiiiiciii`iiiiiii`i`iii`iii`ii`iiiiiiiiciccc`knknkkkbaaa`lifmfmmmfffffffmmnmmffmmfmmmmmmffmfmfffffmfmmmfmfmmnnfnfmmmmnfffffffmmmnmnmmnmmmfmnnmfmfmmfffmmmnmnmnkkbkkkabkknncjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjc``kbknmnbbknnkabnkkbnnnnmnnnnnkkknknnnnkknbknkkkbkkkkkkkkkkbkkkbbbkbkbbbkbbkkkkkbbbbbbbbbabbbbbbbakbkbkkkbabbabbbkci`akknkknkkkajjjjjjjjjjjjjjjjjj", +"cclc`lcccccccccccccciiiccciiicciicac`ii`ii`i`iiiiiiiiiiiiiiiiiii`iiii`ii`iiiiiiiiiiiiiiciiiiakkknnmnba``ij`mmmmmffffmmnmnmfmmffmfffffmmmffmmmmnmnffffmmmmfnmmmfffnmfnnffmmmmmmmmmnmnnmmfmmnnnffffmmmmmmnmnnnmnknkbabkkkbbkljjjjjjjjjjjjjjjjjjljjjjjjjjjjjjjjji`ikkkkkmmnnnfnbbnknknnnnkknkkkkkkknkkkknnnbknnkkkkbkkknknkbkkkkbbbbkkkbkbbkkkkbbbbbbbbbabbbbkkababkbkbbbbbbbbbbabki`abkkkkkknnkbjjjjjjjjjjjjjjjjjj", +"lcllccclcccccccccciiiiiccciicciiciciiiiiiii`iiiiiciiiiiiiiiiiiiiii`iiiiii`bb`iiii`iiiciiiccibnkkbknbba`cijafmmfmfffmmmnmnnmffmffffffffffffmmmmffmffffmmnmfnnmnfmfmmfnnmnmmnnnnmmmnnnmmmmmnnnfmmmnmmfffnnnmmffmknkbabbbkkkkljjjjjjjjjjjjjjjjjjjjjjjjjjjjj`ljjjca`bknkknmnmnnmbannnknkkkkknkbkkkkknnknnnknknkkkbkknkkkkkkkkbbkbbbbbkbbkkbbbbbbbbabbbbbbbbbbkbbabbbbbbbbbkbkbaabkkbaakkkkbbbkknnnjjjjjjjjjjkjjjjjlj", +"cccclcccccccccccccccciiiiciicciccici`ii`i`i`iiiiiiiiiiiiiiiiiii`iiiii`iiiiiicci`iiiiciiiiiciakkkknkaaa`i`jamfmmmmmmfmmnnfnnmffmfmfmfmmffffffffffffmmmfmmmmnmmnfmmmmfmnmnnmmmmmnnmnnmmmmnnnnmfmmnffmmfmmmnnmmmnnnmkbkbbknnbljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjji`ikkknkbkmnnkkbannkkknknnkkbkbkbkknnnnnkkknknnknknkkkkkkkkkbbbkkbbbkkbbkbbabbbbbbbbbbbabbbbbbaabkkbbkkkbkbbbbbbkbbaakkkkkkkbbbknjjjjjjjjjjjjjjjjjj", +"clcccilcccccccciiciciiicccciciicic`iii``ii`iiiiiciiiiiiiii`iii`ii`iiiiiiiiii`iiiiiiiiiiiiiiibkknnkaaaa`abjanmnnfmmmfmmnnmmnmmmmmffmmnnmmffffmmnnfmfmfffmffmffmmmmfmmnmmnmmmmmnmnnmmmfmnnnnmmmmmfmmnnnnmmnmmnmmnmmnkkkkkkkkljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjc``kbkkkbknmnkkbannkknkkkkkbkkkbkkknkkkkkbbkkknnkkkkbkknkkkkknnkkkkbbbkkbbbbbbbbbbbbbbbbbbbbbabbbbkbbbbbbabbbbbbabbaaknknkknkkbkkljjjjjjjjjjjjjjjjk", +"lcccbf`cccccciiaccccciiiiciccciiiiim`iaii`iiiciiiiiiiiiiiii`iiiiiii`i`iii`iciiiiiiii```iiiicabbbkb`iba`abjamfnnmfmmmmmmfnffmmmmnnmmmmnmfmmmmmnnnnfffmmfffnmmmfnmmmmnnnmmmmmmnmnmnnnmmnnnnmfmnmmmnnnnnnnnmmfmffmnnmmnkknbbbljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl`ibabbkkkbnnkbkakkbkkkkknkkknkkknknkbbkkkbbkkbkkbkbbbknnkkkkkkbkkkbkbbkbbbbkbbkkbbbabbbbbbbbabbkbbakbabbabbbabbkkka`knknnmnnnbnkljjljjjjjjjjjjjjjj", +"k`ii`icclccccciccciicc`ccicicciiciicibniiiiiiiiiiiiiiii`caci`ii`i`iiiiiiiiiiiiciiciiiniiiiic`b`abaiiaaabbjbmmmmmnnnmmmfffffmfmnnmnffmnnnmmmnnnmnnmffmmmffmmffmmmnmfmmmnmmmmnnnmnfffmnnmmffmmmnmnnnnmmnmfnmmfmnnmnmmnnkkknbcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjci`akkbkkkbbbmkkanknnkkbkkknnkknnknbbbbnknkbkbkkkbbkbbbkkknbkkbbkkkbbbkknkbbbbkbbbbbbbbkkbbabkbbbbbkbbaaabbabbbbbbbaakknnnkbknnkbijjjjjjjjjjjjjjjjj", +"ilcciccciccccccciccccii`iiiiciiciiiiiiai`iiiiiiiiiiii`ic`ii`iiaciiiiiiiiiiiciiiiii`iiaiiiccaabaab`iiaabbblkfmmfmmnmmmfmmfffmmnnmnnmmffmmffffmmmmmmmmmmmnmnmmmnnmmmnmmnnmnnnnmmnmmmnmnnmfnmmfmnnnnnnmmmmmmmmmmnnnmmnmnnnkbk`jjjjjjjjjjajjjjjjjjjjjjjjjjjjjjjjjc`iabnkkkbbbbkkk`knnnnbbkknnnnnmnbkbkkkbkknkkkknkkkbbbbkkkkbbkbkbbkbbbbbbbbbbbbbbbbbbbbbbbabbkkbkbkababbbbbkkbbbbbbb`knnnnkbbkkkkijjjjjjjjjjjjjjjjj", +"cccciciclccicliaccccciliiicciiiiiii`iiiiiiiiiiiiifkiikaiiiiii`c`iiiii`aai`c`iiiiiiiiiciiiac`bb`aacc`bbkkalkmnmfmnnnnmmmnfffnnnnmnmmmmmmmmmnmmmnmmffffmmffmmmmmmmnnnffmnmnnmmfmmmnnmmmnmmnnmnnnnnnnmnffmmnmnnmmnfmnmmnmnnkbljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjli`bbbbkkkkkbkkk`knkkkkkbbkbkbknnkbbkbbbkkkbkkkkkbkkkkkkkkkkkkkkbbkbkkbbbknbbbbbbbbkbbbbkkbbbbbbbbabbbbbbbbbkbbbbabaannnknnbbknkk`jjjjjjjjjjjjjjjjj", +"cciccicccccccciicciciiiiiciiiiiiiiiii`iiiiiiiiiiiic`iicii``ii`iiiiiiiii`iaikiiiiiiiciciiikilab`a`ciakkbk`jnmnmmmmmnmmffmmfmnnmmnnmfmnmmfmmmnmmffmmmfmfmmfmmmmmmfnnmffmnmnnknnnknmmfffmnnnnnnnmmmnmfmnmnmnnnnnnmmnmmnnnnnmkljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjic`bkbbbbkkbbnkk`kkknnnnkbnnnknnkbkbbbkknkbkknkkbkbkkkkkkkkkkkkkbkkbkbbbbkkbbbbabbbbbbbbkkkkbbbaabbbbbbbbbbbbbabbaba`knnnnmkbmmnndjjjjjjjjjjjjjjjjj", +"ccccccciccccciclcicicia`aiiiicciii`iiai`iiiiiiiic`iikb```ii`eic`iiiiiciccnicbiciiccciciciiickai``c`bnkkkajnmmmmmmnmmmfmmmmmnmmmnnmfmmmffmmnnmmfmnmmfmfmfmnmmnmmnnmfmmmfffnnmnnnmmmfffnnnnnnfffmmmmfmnnmnmfmnnnnmmnkknnnnnn`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlicibnnnkbbkkbnkk`nknnkkkkknnnkkkkbkbbkbnnkkbkkkkkbkkkkbbbkkkkkbbkkbbbbbbkkbkkkbbbabbabkkkkkbbbbbkkkkkbbbkbbbabbbbbbb`aaknnknkmmnnbjjjjjjjjjjjjjjjjj", +"cclicciiccccccciccciccbib`ciiiiiiiiii`iiiiiiiiii`iiia`ica`iciin`iiii`iii``cinciccicccccciccinic``aaknnkkajnmfmmnnnnnnnnnnmnnmfmmffmfmmmmnmfmmmmnmmmffffmmmmfmnmnnmffmmmnmnnnnnnmmmmmnmnnmffffmmmmfmmmmmmnmmmnmnnmnnkknnnkn`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiciibkmnkkkkkkkkk`nmkkbbkbbkbbbbkbbkkkkkkkknkkkkbbkbkkbbbkkkknkbkkkbkkbbbkkkkbkbbbbbbbbkbbkbababkbkbkbbbkkbbbbbbbbbbb`baabnnnkmnnkkjjjjjjjjjjjjjjjjj", +"cclcccciaicccccciic`icacaiciiii`ii`iiiiiiiiiiiiiiiii`ii`i`i`ciaiiiiiiiiciliaf`ilcciccccccicckic`abkknnnnajmmmnnmmmmnmnmnnnnnmfmmnnnmffffmffffmmnnnmmmmmmmnffmmfmnmfffmmnmmfmmmffmmnmnmmffmmmnnmnfmmmfmmmnmmnmnnnnnmkbbknmkijjjjjjjbjjjjjjjjjjjjjjjjjjjjjjjjjlciibbkknnkknbkkbikkkknknkkkkkkkkbbkkknkbkkknnnnkknkkkbbbkkkbkkkkkkbbbkbbbkkbbbbkbbbkbkkbbbbbbkkkbbbbbbbbkbbbkbbbbbbbakkbbknmnmnnnkjjjjjjjjjjjjjjjjj", +"ccccciciiiicciccccciiciiii`iiiiiiiiiii`iiiiiiiiiiii```ii`iiiiiiiiiiiiii`cicia`aiicicccccciciaca`aknkknnn`jfmmnnmmnmmmnmmnnnnmnnmmmnnmnmffffffnnnnmmmfmfmnmmfmmmmfmmmmmnmnmffmmmnnnmmmmmfnnnnmfmnmmmfmmnmmnnnnnkknmnkabkknnijjjijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjci`akkkkknnbknkkannbnkkkkkkkkkbkbknbkbkbkbbkbkkkknkbbbbbbbbkkkkbbbnkkbkknkkkbkbkkkkbbbbbbbkkbbbabbbbbbbbkkkbbbbbabbb`bnnbbknmmnnkbjjjjjjjjjjjjjjjjj", +"cccccciiicccccicicicciiiii``biiii`ii`a`iiiiiiiiiiii`iiiia`ii``iciccici`ciicci`icbicccccccici`i`aknknnnnn`jffnnfmmnmnmmmnnnmmmmmnfmmmnmmmnmmffnmnnnmnnnnfnmmfmmmfmnmmmnnnmnnnmmmmmnmmfmmfnnnnmfmmmmmnnmmfnmnnmnmkknnkbbbbkkcjjjjjjjjjjjjjjjjjjjjjjjljcljjjjjjjci`abkkkknkmbnkn`nkkkkkkkkkkknkbkkkbkkknkkbkkkkbbkmkkbbbbbbbbkbbbknkbkkbbbkbkbbbbbbkkkbakkbkkkbbaabbkbkbabbbbkbbbbbbaannnkbbnnmnnnjjjjjjjjjjjjjjjjj", +"ccccccciicicicciciaaiccc`iii`iiiiiiiii`iiiiiiiii``iiiii`aiiiaa``ciiiiiiccciciiicilcclcccccciaabkkknnnnnn`jfmnnmnmmnnmmnnnnmmmmfmnnmmfnmfnmmffmmmmnmnnnmmnnnmmnnmmmmmmfffmnmmmmmmmnmmfmmmmnnnnmmnmnnnnmnnnnnmnmmnbnnbbkkbakijjjjcljjjjjjjjjjjjjjbljcjjljjjjjjcjiaakbkkbnknnnmm`nkkkkkkkkkkkbbbkkkkkkkkbknbkkkkbbkkkkkbkbbbbbkbbbkbbbbkbkkbkbkbbbbbbbkbbbkbbkkbkabkbkbbbbabbbbabbbaabnnnnbbbknmnnljjjjjjjjjjjjjjjj", +"lciccciiciciccicciiiiiciii```i``i`iiiiiiiiiiii`iii`ii``b`iiiiiicicicciiiciccccccccicccccccciabkkknnnnnnnilnnnmnmnnnnnnnmnffmmmmmnmmfmnmffmmmfmfmnmmmnnnmmmmmnmmnnnnmnmmmnnmmmnmmmmmmmmnnmmmmfffmmmmnnmmnmmnnnnnnbkbbbkbbabcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj``iabkbkbkkknknn`kkkkkkkkbkbkbkkknkknkkbkknbkknkkknkknbbkkbbbbbkbbbbbbbbbbkbbkkbbbkbkkbkkkbkbbabkbkbbbbbbaabkbbbbbbaaannnmnbbbkknmcjjjjjjjjjjjjjjjj", +"iiiccciiiiciciccicicccci``iiii`ii`iiiiiiiiii`iii`i`iiii`iiiiiciiiciciiiiciicccccci`ilccccccaabkknnnnnnnnclmnnmnnnnnnnnmnmfmmmnnmmmmmmnmmmmmmfmmmnnmnmnnnmmmnnmmmnnnnnnnnmnnnffmnmnmmmnnnfmmnmmmmmmmnmmffmmnmnkkknkbkkbabbbcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjli`abkbkbkkknkkkckkbkkbkbkbkbkkknkkkkbbknkkkbkkkkknkkkbbbkbbbbbkbkkbkkbbbbbbbbbbbbbbkkbbbkbkabbkkbbbknkbkkkbbkkbbbabaamnnnnkbbabbncjjjjj`ljjjjjjjjj", +"ciicciiiiiiiciicccciiiamb`i`i````iiiiiiiii`iii`iiii`iiciiiiiiiiiiciiiiiciiiccccccciccclccli`kkmmknknknnnccmnnfnnnnnnnnmfnmfnmnnnmmmmmmnnmnmmmmmmnnnmnnnnnnnnmmmmfmnnnnnmnnnmffmmmmmmnnnnnnnnnnmmnfmmnmmfmnnnnnkkkbbkkbabbbcjjjjjbjjjjjjjjjjjjjjjjjcjjjjjjjjjjcci`bbkbbkbkkkbkckkkkkkbbbbbkkkknkkbbbbnnkkkkbkkbbkkkkkbbbbbbbbbbbbbbkkbbbbbbbbbabkkbbbbbbkbabbbbababbbbbbbabkkbbaababnnmnmnbbbbab`jjjjjijjjjjjjjjj", +"ccccciiciiiiiiiiiiciiccaiii`iiii`iiiiiiiiiii`i`i`iii`i`iiiiiiicciiciii`iiiiiciccclcclccccccaknmmmmnnnkknlcmnnmnnnnnnnnmfmfmmmnnnfmmmffmmmmnnnmnfnmnnmnmnnnnnnmmfnnnmmmnnnmmmnmmmmfmmnknnnnnnmmmfmmmnnmmmmknnnnkbabbbabaakaljjjjjjjjjjjjjjjjjjcjjjjjjjjjjjj`jjli``bkbbbkbkknkkckkkkkbkbkbbkknnknnkbbkkkkknkkkkkbbbbbkkbbbkbbbbabbbkbkbbbbbbbbbbbbkkkkkkkkbdbaabbbkbbbkkbbbbbbbabbabakmnnnkkbkkbbijjjjjjjljjjjjjjj", +"cccciciiiiiiiiiiciiab`icci`a`ic`i`iii`ii`iiiii`i`iiiicciciiicciiiccicibiiiccccccicccccclcccanmnmmmmnkkkklimnnmnnnnnnnmfffffmnnnnfnnnmffffnmnnmnmmmnmnnnmnnnmmnmmnnmffmmnmmmnnnmmfmnmmnmmmmmnnnnnmmnmmmnmnnnnknkbabkbbbabbaljjjjjjjjjjjbjjjjjjjjjjjjjjjjjjjjijliiabbkbbkkbkkkb`kkkkkkbbkbbknnnnkkkbkbkbbkknkkkkkbbbbkkbkbbbbbbbbbbbbkbbbbkbbbbbbbbbbkbkbbbbbkkbbbbbbbbbbbbbbbbabbba`bnnnnknnbkbb`jjjjjjjjjjjjjjjj", +"ccccccciiiiiiiiii`iiiiii``iiiiciiiiiii`iiiiii`i`iiiiiiiiiiiciiicciiaicaicciiccccclcclcclllcafmnfnmnnnnnnlcmfnmnmnnnnnmmfmmnnnnnnnnnnmmmmmnmnmfnnfmnnnnnnnnnmnknnnnnnnnnmmnnnnmfmmnnnnmmmmnmnnnmfnnmmmnnmnknkknbbbbbkbaaaaaljjjjjjjjjjjljjji`jjjjjjjjjjjcljjjjj`iabkkkbkkkkkmaaknkkkkbkbbkknnkkkkbbbbnbbbkkkkkbkbnkkbkkkbbbbbbbbkbbbbbabbbbbbbbbbbbbkkbbbbbkbbbbbabbkbabkbbbbbbaaaa`aknnnmnkknbbajjjjjjjjjjjjjjjj", +"icccci`cciciiiiiii`iiiiiii``iiiiii`iiiii`i```iii``ci`ciiiiiccicicccciciiicicccccccclccccclikmmnmnmnnnnnklammnnknnnnnnnnnmmmnnknmnnnnnnmmmmmmfnnmnmmmmnmnmnnnmmnnnnnnnnnnnnmmmmmfnnnnnfnmmnnnnnkmmnnnmmnnmnmnnkbbabbba`aaailjjcjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjliiakbknkkkbknnk`nkkbbkkbkbbbkknkkbbkbkbkbbbkkkkbknbnbbbkbnkbbbkbkbbbbbbbbbabbbbbbbkbbbbbbbabkkkbbbbbbbbbabbbabbbbbaaibbbbknmmknkabjjjjjjjjjjjjjjjj", +"icciccccciicci`bi``i`iii`i``ciiiciiiiiiiiiiiii`aci`iiiciiiiiiiccciicccccccciciccccccccclclcbnnmmnnnnnnnklaffmnnmnnnnnnmmmmmnnnmmnnknmmmmnnmmfnnnnnmmnmnnnmnnmnmnknmnnmmnmmfmmnnnnnnmmfmmnnnnnnnmmmmmnnnkmnnnnkbababaa`aaicjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjliiakkabkkkkknnnannkkbbkbbkbkkkknkkkbbnbbbbbbkkkkkkbkkbbbkkkkbbbbbbbbbbbabbbbbbbkkkbbbbbabbbkbbbbabbbabkkbbabbabbbabb`abbbkknmmmkbb`ljjjjjjjjjjjjjj", +"macciiiicicciii`iii`i`i`i`knai`c`iii`iii`ii`ii`aaciiafiiiciiiciacciccccciciiccclccclccllclcaknnmmnnmnnnnjbmffmnnmnnnnnmmmfnmnnmfmnknmmmnmnmnnnmnmmnmmmnnnnnnnnnnnmmmnmmnnmmnnnnnnnnnnnnnnnmnnnmnnnmnnnknnnnnnkabaaaai```iiljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiiakbaakkknnnmnankmnkkkkkkbbkkkkkkkbbnkkbbnkkkkbbbbbkbkbnkkkbbbbabbbbbbbkkkkbbbbbbbbbbbbbbabbbbabbabbbbabkabababbbbb`abbbbbknmmnbbljjjjjjjjjjjjjjj", +"biccclcciicciccii`iiii`iii``ci`ia`iiiii`iii`iii`c`iiii`ciiicccibccciciciiiiiiicccccclccclclabkkkmnnmmnmklanmmnnnmnnnnnnmmfmnnnnmmnknmmnnmnmnnmfmmnmmmmmnnknknnnnmmmmmmnnmnnmmmmmmnmknmmmnmmmmmnnnnnmnnnnknnnmkabbaa```a`iiljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlii`kkbabkbkknmnakknkkbkkkkbbkknkkkkbbnkkbbkkbbbkkkbbknkbkbbkkbabbbbbbbbbkkbbbbbkbbbbbbbbbbbbbbkbbbbkbbbabbabbababbbb`abbbbbabknkkajjjjjjjjjjjjjjjj", +"cciiiiiicciiccib`iii`ii`i`````ic`ii`iii`i`i`iiiiiiiciiciicciiciiiccciiiciiciccilcclclcllcll``aaknmmnmnmblbnnmnnmmnmnnnnmmmmmnknnmnnnnnnnnnmnmnfnknnnnknnnnnnnnnnnnmmnmnmnmnnmmmmmnfnfmmnnnnmmmmnnnnknnnknnkknkabbaaaabba``cjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjii`baaaabakkbknakkkkbkkkkkkbbkknkkkbbbkkkbbbbbbbkkbbkkbbbbbbbbbbbbbbbkbkbbbkbkbbbkbbkkbbbbbkbbbbbbkkbbabaabaaaabaaaa`i```aabaknnbacjjjjjjjjjjjjjjl", +"icccciccicciic`b```iiiiii``iiiiiiiii`iii`iiii`iiicii`cicciiciicccccccicciicicciclcclclcllcli``akkmmmnmnbjbnmmnmnmmmmnnnnmmmmnnnkmnnnnnnnkknnnnnnnnnnnknnmnnnnmmmnmmnnnnnnknknnnmmnmnnnnnnnknmmnnkkkknnkknnnknkabbaaabbkabaijjjjjjjjjjjjjjjjjjjjjjjjjjjljjjjjjjcci`ii`abbkkbkkabnkknknknknkbbkknnkkbbbkkbbkkbbbbbkkbkbbbbbbbabbbbbbkbkkkkbbkbbababbbbkbbbbbbabbabbkbabaababbbbbbabaiciii`aabbnnkacjjjjjjjjjjjjjjj", +"liiicciiiicciiii`i`iiiii``ii``iii`iiii`i`iiiiiiiki`cciciciiccccicccccccccicciiaicccllcllclcaaabknmmnmnnbjkmmfnnnmmfmmnnknmmfmnmnnnnnnnnnnnnnnnnmnnnnnnnnnnnnnnnmmmmnnmmknnnknnmmnnknnmmfmnnmmmnmnnnnnmmnnnnkkbabaaaabkkbnaijjjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjjilcic``bkbkkkkbaankkkbkkkkkkkbbkknnkkbkbkbbbkkbbbkkbbbbkbbbbabbbbbabbbbbkbbbbbbbbababbkbbabbkkkbbbbkkkbbbbbabbbbabaaalccciabbbkkkbijjjjjjjjjjjjjjj", +"icciciccciiciiiiii`iiiiiii`iii`iiiii`i`iii`iiiiiiic`ciiiiicciicccciccccccilcclilclclclccllcbbbbmmmmmmnnbjkmmmnknnmmnmmmnmfmfmmmfnnmmmmnnknmnmnnmnnnnnnnnnnnnnnnnmmmmmmmmnnnnnnnmnnnmnmnnnnknnmmnmnnnnnnnnnnnnkbb`i`aabnnkaijjjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjjcllcc`abbbabbkbaannkbbbkkkkkkkkkkknknnkbkbkkkbbbbkbbkbnbbbbbbbbbbbbbbbbkkkkkkkbbbbbabbbababbbabbabbbbbbbbabbbbabbbab`lllciibbabbbbijjjjjjjjjjjjjjj", +"ccciciicciiciii``c`i`cii``iiiii`ikbn`iiii`iiiiiiiciiiiiccakaiccccccccccciccccclccllcllclllcbbbnmmmmmmnnajmmmnnnnnnmmnnnmnmmmmmmmmnnnnmmnnnmnmmmknmmnknmnnknknnmnmnmmmnnnmnknnnnnnkkmmmnnmnmnnnnknkknmmmnmnmkkkaaii``bkkkbaijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjicllccaakbbbann`bnnkbbkkknkkkkbkknnkknkkbbbbkkbbbbbkkbbbkbbbbbbbbabbbbkkbbbkbbbbbabbabbbabbkkbbbbbbabbbaaabbbbbbbbaaalccc`abkbbbabajjjjjjjjjjjjjjj", +"ciiil`ciciciciiii`iiiii`ii```iiii`ia`i`iic`ci`iiiiiicciiiciccccciccccccccccllcclcccllclclccbbbnnnnnmmnn`lnmnknnnnnnmmnmnnmfmmmmmmknkknnknnmnmmmnmnnmmnnnnnnnmnnnnknnnnmmnnknnnnnnnnmmnnnmnnnnnnknnnknmnnknnnmka`i``abbknaaijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjicjcii`bkbkkknmaankbkbkbbknkkkkbknnnknkbbbbbbbbbkkbkkkbbbkkbbbbbbbbbkkbabbbbbbkbbabbbbbbbbbbbbbbbbabbbabbaabbbbaabbbblli``bkbbbbaaajjjjjjjjjjjjjjj", +"ccciina`iicicii`iaiiiiiii``i`iiii`i``ii`i`a`iic`iciciiccciicciccccccclcccccccclclclccllclciabkmnnnnmmnm`jnnnnknnkknmmmmknmfmmnmfmnnnnnmnnmmnnmmmmmmmmnknnnnnmmmnnnnnmmmfkknnkknnmmmmnnnnmnmnnnnknkkknnnmnnnnmna````bbknbaaijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjljjcila`i`bkbknnnnaabbkkkbbbbnkkkbbkkkkkkknkbbbkkbbbkbkbnkkkkbbbbbbbbbbkkbbabbkbkkbaabababbbbbbbabbkbbbaaabababbbbbbabbbllababkbbbbbbbjjjjjjjjjjjjjjj", +"iiccik`iiciacii`ibiiii`iii```i`ii`i`iciiiiiiiciciiiiiiiiiccciccccccccccccclccllcieillclllciabknnnnnnnnnilknnnnnnnnmmnmmnnmmmmmmmnnkknmmmmnnnnnmmnfmmnmmknnnnnmmnnnmmmmmmnnknnknnmnnmnnnnnmmnnknnknknkmmmmnkknkaaa`abakkaab`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjicca`iibbbknnnnb`bkbkbbkkkkkknkkknknnkkkbbbbbkkkbbbbbbkkkkbkkkbbbbbkbbbbkbkkkbkbabbbabbbbkbbbabbbbbbabbaabbabbbbbbaablcabbbkkbbbbbbjjjjjjjjjjjjjjj", +"ciccccicciibiiiiiaiiiiiiiii`i`i``iii```iiiiiiiiiciiiccciciccccicilclcicccilclclclccclclcll`kknnnnmnnnnn`jnknnnnkmmmmnmmmnmmmmmmmmnnnknnnnnknnnnmnnmnnfmmmmnnmnnnmmnnnmmnnnnnknknnmnmmnnknmnnknknnnmnnmmnnnnkkkabaaabbaaibk`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjliccb`iibbbbknknk`kkkkbknknkbnnkbkkknkkkkkbbbkkkkbkbakkkbbkbbkkkbbbkbbbbbkbbbbbkbbbababbbbbbbkbabbbbaabbabbbbbbbbaaabbcibbabbkbbaabbljjjjjjjjjjjjjj", +"ciciiciicii`iii`i`ii`iiiii`iii``i``iaaai`iici`ciiicc`iccciiiccclccicccciiaillcclcjlallllll`knnnmnmmnnnn`lnnnnnnnmnnnnmmnnnnnnmnnmfmnmmnnnnknnmmmnnmmmmnmmmnmknkkmmnnnmmmnnkknnnnmmmnmmnknnnknnknnnmmnmmnnnmnnkbbb`abba`abkijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiicnk`cakbbknknkabkbkbkkkbkbkkkbbbbkbkkkkkkkbkkbbbbkbkkkbbkkkkkkkkbbbabbkkdkkbkkbabbbbbbbbbkbbabbbabbabbbbbbbbbbabbbaiiabbabkbabbbbjjjjjjjjjjjjjjj", +"icciiciciicccii`iiiiiiiiiii`i````iiii`iikiiiabiiiiccaa`ibicbbicicc`iccc`ibcclclllacjclllll`bkknmmmnnnnnilnnnnknnnnnnnmmknnknnnnnnmnmmmnnnnnmnmnnmmmmnknnnnknnkknmmmmmmmmknknnmmmmnmnmnnnnnnknnknnnnnnmnnnnmnknkaiiiab`abbk`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjji`cnnic`bbkbknkk`bkkbbkkbkkkknkkbkkkkbkbkkkkkkkbbbkbbnbnbbkbbbbbbbbbbbabkkkkkkkbbababbbkbkbbbbbabbabbbabbbbabbbbabbab`ibbabbbbbbbkbljjjjjjjjjjjjjj", +"cciiciciciiii`iiii`iii`i`i`iiii`````iiiiiiciiiicciii`iccciccccicccilccccc`lclllcclcllcllll`bbkbknmnnnnnicnnnnknmnnnnnnnnnnnnnnnnkmmmmmnnknnmnmnnnmmnknnnnnnmnnknknmmmnmnnnknnnmnnnmnnnnnnmnknmnnnnnknnnkkbnmnmbilcci``abkkijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjic`kb``akmbakkknakkbbbbkbknbbkbbbbbbbkbbbkkkkbbbbkkkkbbbkkbbbabbbbkbbbbbbbkkbkkbabbbbkkbkbkbbbbbbkbabbbaababbbaaabbbb`ibkabbbbkbbabljjjjjjjjjjjjjj", +"iciciiiiiccci`i`iiiiiiiiii``i``i``iiiiiiiiiiciciiacciilccccciicciccccccclclclcllllllllllcl`abbbknmmnmnnlcnnkknnnnmnknnnnknnnnnnmmmnmmknnnffmmmnnnmnkknnnknnknnnnnkknnnnnkknnnmmnnmmnnnmnnnnmmmnknknnmnnnnkmnnnii`ciccikkbbijjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjjjic`bb``bkknbbbnnakkbbkkkkkbbbkkbbkbkbbbknkbkkbbbbnkkkbbbkkbabbbbbbbbbbbbababbbbababbbbbbbbbbbkbbakbbbbbbbabkbbbaabbbb``bbkaknbbkkakijjjajjjjjjjjjj", +"cicciccia``ii``i`i`iiiii`i```i```i`iiiiiciiiiiiic`ciccaiciicciicicccccccclclcllclllllllcjcabbbknnmnmnnnlinnnnknmnnnknnnnnmnmmnmnmmnmnkknknmnmnnmknnnknnnknnnkknknnnknnknnnnmmmmmnmnnnnnmnnmnmnnnnnnmmmnnnnnknk`c`iiciiabbbijjjjjjjjjjjjjjjjcjjjjjjjjjjjjjjjjjjci`bb``bkkkkbbkkakbkbbkkkkbbbkkkbkkkkkkkkkkkkbbbbkkkkkkbbbbbbbabbbbababbbbabbbabbbbkbkkbkbbbabbbbbbbbbbbabbbbbbbabbab``babbbkkbbkkbijjjljjjjjjjjjj", +"cciciiiciiciii``i`i`i`i`ii`````i`iiiiiii`iicicccciciicb`ciiicicicicccccclcllclllllllllllclakknmnmnnmnmmlinknkknmmnnmnkkmnnnmnmnnmnkmmnnmnmmnmmnmmnnmmkknnnnkknknnnnnnnnknnknnnnnnnnnnknmnmmmnnknknmmnnnnkknkkkiciiclcc`ank`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjic`kb`bbbkkkkbbk`bkbbkkknkkkkkbknbbknkkbkkkkbbbbbkbkkkkkbkbbbbbbbbbbbabbbbbkabbbbbbbbbbkbbbbaabbbkkbbbababbbbbbabaaabaibabbbbkkbbnkajjjjjjjjjjjjjj", +"ciciciciiciiiiiiii`i`i``a`i```i`iiiiiiiiicic`iiiiccccci`iicccciccicciclccllclllcllcllcll`c`bbbmfmnnmmmnl`nknnkkmnnnmmnnmmnknnnnknnnkkkkknmmmnmnnmmnmnnkknnnnkknknnnnnmnnnmnnnmnnnnnnnknnnnmmnnknknnnnnnkkknknk`iicclcciakkajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlli`bbbknkkkkkkbbibbkbbbkkkbnkkkkkbbbbbbbkbbbbbbbbkkkkbbbbkbkbbbabkkbbbbbbbbbbbbbbbbbbbbbabbbbbabbkbkbbbabbbbbbabaabakaikaabkbkkbbkkajjjjjjjjjjjjjj", +"cciciciciiiiiiiiiii````ibaiiiii`iiiiiiiiiciabiiliccciiiiccccccccccciclcllclllcllllllllllcjbnkbbknkmmmnmj`nnnnnknnnnnmmnmnnkknnnnknnkknnnnmmnnnkknnnnnnnnnnnnnknkknnkknnnnmmnmmnmknmnknnnmmnnnnknnnnknnkkknnnmkaiiccici`abbbjjjjjjjjjljjjcajjjjjjjjjjjjjjjjjjjjcciabbknkkkkkkkb`kkkkbbkkbkkkkkkkbbbbbbbbbbbbbbbkkkbbbbbbbkbbbbbbkbbababbbbbabbbkkbbbbbbbbbbbabbbbbbbbaabbbbabaaabbaba`nbabbkkkkbbkkjjjjjnljjjjjjj", +"iiciciciiciiiii`iiiiiii`a`iiiiiiiiiiiiiib`iciciiiciiiibiccccccccccccclcllllclllllccllllllcbbbkkbkkkmnmnlannmnknnknknnmmnmnkknnnmnknnmmnfmnnnknnnknknnmmnknnnnnknknkkknnnmmmnnnnknnmkknnnmnnnnnnknnkkknkkknknnb`icc`ai`a`aa`jjjjjjjjjljjjjjjjjjjjjjjjjjjjjjjjjjlci`abbknkkkknnk`kkkkkbkbbbbkkkkkkbbbbkkkbbbbkbkkkbbbbbbbbabkkkbbaabbbbabaababbbbkbbkbbabbbbabbbbbbbbbabbbaaaabaabaabbikkbakkbbnkbbbljjjjajjjjjjjj", +"iiciciiiiiiiiiiiiiiii`iiiii`i`iiiiiiiiii`ccciiciiiiiccalcccccccccclclc`lcllllllllcclllllllbbbnbabbbknmkjannmnnnknnknnmmmmnnnnknmfnmnnnkkmnnnnnnnkkkkknnkknknnnnkknknknknnnmnnnnknnnnknmmmnnnknnnnkkkkkkknnnnnk`cc`ab`a`cii`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlciaabbkknkkknnkakkkkkkbbkbbkkkkkkbbbbkkkkkkkkbbkbbkbbbbbabbbbbbbbbbbbbbababbabbbbbbbbbbbbbabakbkbbbabbbabaaabaabbabbaiakbabbbbbkankijjjjjjjjjjjjj", +"iiicciciciiiiiiiiii`iii`ii`iii`iiiiiiiiciiiiciiiicciccliccciicclcllclcaclcllclllllclllllllbabbdkdddbnnklannnknnkknknnnnmmnnnkknmmmmnnfnnnknnnnnknnknnnnnkknnnnnkkkknkknnnnnnnnmknnnnmnnnmnnkkknnkkkkkkkknnnknb`c`bkbba`i``ijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjcibbbkkbknknkkn`kkbkkbbbbbkkbkkkbkkbbbknkkbkkbbbbbbbbbbbbbbbbbbbbbkbbbbbbabababbakbbbkabbbbkbbbkkbababbbaabaaaabbkaaa`bnabbkkbabbbkcjjjjjjjjjjjjj", +"ciiiiciiiiciiiiiiiiii``i`iiiiiii`iiiiiiiciiciiicicccicccililccccclccllllllclllllllllbllllikkabbbbbkbkmblanmnknnknnnkknnnnnnnmmnmmnnnnnnnnnnnknnnnknnnnnnnnnknnnknnnkknnnkkkknnknknmnkknnnnknknnbkkkkkkkknmkkkb`ibknnkbaa``cjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjli`akkbbkbknnnnnibkbbbbkbkbbbkkbkkkkkkknkkbkbbbbbbbbbbbbbbbbbbbbbabbbbbabbbbbbbbbababbbabbbkabbbbababbbaaabbaaaakbbaaa`bnbbbbnkkbbbbljjjjjjjjjjjjj", +"icciccicicii`c`ii`i``cc`ii`iii`ai`iic`iiciiciiiiccccccccclcccclcllciilclllllllllllllcjcllannkbbbkkbaknajknnkkkkkknnknmnnnnnnknnmnnnnnnnknnkkkkkkkknkknkkkknnnnkkmnnkkkkknkkknnnnnknnnknknkknknnnkkkkkkkknnmnnk`aknknknkb`d`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl``aknkbbkknnnkkikkbbbbbbbbkbbbkbbbbbbbkkkbbbbkbbbbbbbbbbbbbabbabbbbbbbabkbbaaaabaaababbbabbaabababbaabbbaaaaababbkbbb`ankabbknkbbkbijjjljjjjjjjjj", +"iccici`c`ciii`kiiiiiiiai`iiiiii`ii`iiiccicciiicccciccccc`mlclclclccccllcllllclllllllllail`bkkbaknkbabkijnnkknknnkkkkknnmnnkkknnmkkkknnnnnknknnkknnnnnkknknnnknkkmmmnnnnkkkknnnnnkkknknkknkkkknnkknnnnnnnnmmnkbaknnnknfnbbnbjjjjjljjjjlcjjjjjjjjjjjjjjjjjjjjjjjl``bnnknkkbkknkk`kbkbkbbbkbkbbkkkbbbbbbkbbbbbbbbbbbbbabbbbbbbbbbbbbbbbbbbkkbbbbbbbbbbbkbbbbabbaabbbabbbaaabbdbbbbbbbaa`amkbbbknknkbbijjjjjjjjjjjjj", +"ciiiiia`ci`bna`iii`iibfiii``i`iiii`iiiiiiiciiicccccicccclccl`cilcllllllcclllccllllllll``liabbabkkkkbbbilknkknnknnnkkknnnknnnkknnkkknknknknknknknnnnnnknknnnkknnmmnnkkkkknknknnnmknmnkknnnnnmmmnkkknknkknknnkkbbnnnknmmkdakbjjjjjjjjjjjljjjjjjjjjjjjjjjjjjjjjjjli`bnknnnkkbbkkmabbbkbkbbkbbbbbkkbkbbbbbbbbbbkbbbbbbbbbbbbbbbbbbbbbababbbkbbbabaabababbbbabbabbbbabbbbabbababbbaabaabaaamnabbkkkkkkkijjjjjjjjjjjjj", +"c`icciiciiciiiii`ii`ian`i```iiiaiciiciibiciiiicccccccccccccinlclclcccccllllclclllljcljlilibbbbbbaabkkb`jbkkknkknknkkknnnkknkkkkknkknnkkkkkknknnknnnnkkknknnkknnmmmnnkkknnnnnnnkknnnmnknnkkknnnkbkknnkkkkkkknnkbkkknmnkbbknkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl`i`bknknmkkbbbk`bbbbbbkbbkbbbkbbbbbbbbbbbbbbbbbbbbbabbbbbbbbbababbbabbbkkknbababkkbkbbbbkabbabbbabbbbbbaaabbaaabaababa`nkbabbkkkknbaljjjjjjjjjjjj", +"ibaiiiiiik`ciai`iiiiii`iii`iiii`iciiiciaiiciiccicccccccccllililcclllcclllllllllllclllllllibkbkndaabnkb`jknnkkkknkknnnnkkknknkkkknnnnnknkknkknnnnkkknknknknnnnnnnknnnnkkknmnnnnnnnnkkknnnnnnknmnmknnnkkkkkkkknnbnnknmnbbnnnncjjjjjjjjjjjjjjjjjjl`jjjjjjjjjjjjjjl``abkkkknmkbbbbibbbbbkbbbkbbkbbbkbbbbbbbbbbbbbbbabbbbbbbbbbbabbbabbbbbbbbbkbbbabbbbbbbabbbabbabbabbabbbbabbbaabbaaabaaikbbbabbkkbbbbjjjjjjjjjjjjj", +"c`iciiiii`i`iiii`a`ii`iiii`iiiiciiiiicciiiiicccccccccccclicjillccllcllcllllllllljlljllljlanmnnnbabnnnbclknnnnnnnkkknnkkkknknknnnnkknkkknnknnknnnknknnnnnknnnnnnnnnnnnnmmnnmnnnnnnkbkknnknnkkknnnnnnnkkkkkkbkkkbnknnkbbkmmnkjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl`iakkkmnkknkkbbibkkkkkkbbbbbbbbbbbbkbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbkbbbbbkbbbbbkbkbbbbbaababbbbbaabbbbbbabaaaababaaaikbababbbkkkkbjjjjjjjjji`lj", +"iiiiiciii`iiiiiiiii`iii``i`ii`i`iiicciiciiicicccccccccicc`ciiilallccllcllllllllllllllllllannmnkbbmkkfkljknnmfnnknkkkkkknkkknnnnnknkkkkkknnkknkknkkkknnnnkknnnmmmnnknkknmmmmmmnnnbknnkkmnnnkknnnnnnkkkkkkkknnkkbnknnbbakmnmbjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjl`iiaknnnnknnmnkcakknkbkbkbbbbbbbbbbbbkbbbbbbbabbbbbbbbbbababbbkbabbbbkkkbbbbbabkkbkbkbbaabaaabbbbbbaabbbbbbbaabaaabbabikbbbbaabbknbkjjjjjjjjjjjjj", +"iiiiiiiiiiiiiiiii`i````ii```ii`iiicicici`iicccccccccccclcclclcclclclllllcllllbllljcbcljllannnnbaknnmnncjnmnnnkkkkkknnkkknnnnnkkkkkkkknnnnknnknkknknnnkkkknmnnnnnmmnnnnnnnnnkkkkkkkkkkkkknnnkknnnnknnnkkknkknnb`knkkbdbnmnmbjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlii`abbkkknnkkmk`kknkkbbbbbbkbbbbbbbkkbbbbbabbbbbbbbbabbbbbbbbbbbbakkbbbbkkbbbkbkbkbbabaababbaaababbabakbbabbbaabbbababcanbaabbabkknkjjjjjjjjjjjjj", +"ciciiiiiiiiiii`i`i```i`i`icc``i`iiiiiiiiicciciccccclccccccclcccllllllclllllllijllllijllllaknkkabkkknmnclmnknknkbnknnnnkkkkkknnknkkkknknknnkknkkkkknnkkkkkknnkknfnmnnknnnnnkkkkknkkkkkkkbkknmnnnkkkknkkkkknkkkbabnkkkbknnfnmjijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjlii`akdbbbbkmmk`cabbbbbbbbbkkkbbbbbbbbbbbabbbbbbbbbbbbbabbbbbbbaabbbbabbbkbbbbbbbbbbaaabbbaaaabaaabbbkbbaaaaabbbaadababiabbbabaaabbkbljjjjjjjjjjjj", +"iiiiiiiiiiiii`i````iiiiiiii`baiiiiiiiicccccccccccccccccclclclcclclllcccllllllllllllljlljlbknkbankabknncckknknnnmnnnnmnkkknknkkkkkkknnnkknkknkkknknnnnnnnkkkkkknmknnnkkknknnnnnnknkkkkkkknkknnnnnnnnnnnknkkkkkkbknkkkknnnmnajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjiciakbkbkkkbbbaiakbkkkkbkkbkkkbbbbbbbbbbbbbbbbbbbbbbbbbababkbbbbbabbaabkbbbbaaabababbbbababbbabababbbkbbbbbbbbabbabaabc`aaaaaaaabkknijjjjjjjjjjjj", +"iiiiiiii``i`ii`i``i`i`iii`cciiia`i`i`iiiiiccccccccclccccccclccccclcllilllllllllllllllllllkmkkkknbdabkklcknmmnnnnnkknnnkkkkkknknkkkknknnkkkkkkkkkkknnkknknkkkkkkmkkkkkkknnnnnnkkkknnkknnnnkkkknmnnnkkkbkkknkkbkbnmnnnkknmnnajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjliiibbbbbbkka`caabkkbkkkkbkbbbkbbbbbbbbbbbbbbbbbbbabababbabbbbbbbbbababbbbaabbabbbbbbkbbaabbabbbbbbbbaabbbbaaaabaababaacc`iaaaa`aabkkijjjjjjjjjjjj", +"lllllllllllllllllllljlllllllllllllllllljlljlljljlljljljljjljjljljjljjljjljjjljjjljjjljjjjknknmnnbbbbkklcnnnnnnkknkknnnkbkkknkkkknknknkknkkkkkkknnnmnnnmnnnnnknnnkkkkkknnmnnkkkkkkknknkmmnknkknnkkbkkkkkkkkbkkbbnnmmkkkkmmnkjljjjjjjljljjjjjjljljjjjjjjjjjjjjjjli``bbbbbbbaiiiaaakbkbbkbkbbbkbbabbbbbbbbbbbbkbbbbbbbbbbbbbbbbbbkbkbbbbbkbbababbbkbbbaaabbbbabbbbbbbabbbbaaaaaaababaaaaiccii`ii``aaab`jjjjjjjjjjjj", +"ljljljljljljjljlljljlljljjljjjljjjjjjjljljlljljljjjjljljlljljljjljlllllljllljllljlljljjljbbknmmnkbbbbblinnnnnnnnknkknnnnkkkkknknknkkknkkkkkkknkknknnkknknnmnnnmnkkkkkknnnnkkkkknkkkknnnnknnknnnkkkknnnkkkbkkbbakmmkbbbbknnkjjjlljlljljllljljjljlllllllljlljljll`cibabaaa`cliaba`kkkkkbbkbbbbbkbbbbbbbbbbbbbbbbbbbababbbbbkkbbbbbkkbbbkbbbaababbbkbaabaabaabbbbbbababbkababbbbbbaaaaaaiciiicci```aad`ljllllllljlj", +"ljlljlljljljljljlllljljljljllljllllhllllljljljjjlllljllllllljljljjljjljljlljjjljllllllljlddknmmnkbkbbbj`mmmnkknnnknkknnnkkkkkkkknkkkkkkkkkknkknknknnnnnnnnnnnnnnknkkbkknnkkknkkkkkbkknnknnkkkknkkknnkkbkkkbkkkaknnbbbbbbknnjljljljllllljllllljlljljljljlllllljl```bbaa`cjlcabbakknkkbkkkbbkbbkkbbbbbbbbbbbbbbbababbbbbbbbbbbbbbbbbkbkbbbbbaaabbabbbbbbaaaaabbbbbbbbaabaababbaaaaaaaab`c``icci`aaaa`iljjjljjljljl", +"jlljlljlllhllhlhlljhlllljlljlljllhjljhjllhlllllhljljllhjllllhllhlhllhjllljhjlhjlhjjljlljlbkmmmmnmbkbkbj`knknnnnnnnknkknnkkkkkknkkkkkkkkkkkkknnknkkbnnnmnnnknkkkkknnnkknmmkkbkkkbkkkbknnnkkknkbkkkkkkkbkkknkknnbkmnkbbbabbadllllllhlllljhjhjljlljlllllllhjlllllc```bba`clliakkbabbbkbbbkkbkkbbbkbbbbbbbbbbbbbbbbbbbbbbbbbbabbbkkbbbbbbbabbbbbabbbbaaaabababaaabbababaaabaaaaaaaaababbaaiaa``i`a`add`cljhljlljljll", +"lljhjhlhjllllllllhlljlljhjlljllllllhlllhjllhllljlllljljlljljlllllllllhlllhllljljlllllljljnknmnmmmnkkbal`kkknnnkkkkknkknmkkkkkkkkmnkkkkkkkknmnkknnkkbkkkkkkknknknknnnknnnnkkkkbkkkkkkkbkkkkkkknkbkkkkkknnnmnkknnknmnbbbkbbbbljljljjljljhjljlhlhllhjhlllllljljllli``kkaiclc`bbkkkbbbbkkkkbbbbkbbkkbbbbabbbkbbababbabbkkbabbbaabbabbabbbbbbbbkbabbbbbbaaabaabaaabbbaababaaababaabbbbbbabacbbaaa`i`abb`iljlllljhjljj", +"llllljljlhllhlllhjllhjhllhlllllljhjljhjlllhjllhlhjhlhlhlhlhlhjhjlljhjljlljlllhlhllhllhllhkknmknmmnkkbbj`nnnnnnnnkkkknnkmnnmnkkkkkkkkkkkkkknnkkkkkkkkkkknkkkkkkkkknmnnnnnkkkkkkkkkkkkbkknkknnnkkknknknnnnknkknmnnnnnbbbbbdbdjlljhlhlhlhlllhlllllljljjhjllllllljhiiiba``ic`bnkbbkbbbkbkkbkbbbkbbbkkkkbbbbbababbbbabbbbbabbbbabbbbbbbbabaabbbkbbbkbbbaabaabaaabaaababbbbbbbabbbbbbbbbababiababba``add`iljhjllljhjhl", +"llhllhjhjllljhjhjlhjllllllllhjhlhllhlllhjljhljljllljlljljljhlllhlhllhlhlhlhlljllljljlllllkbnmkknmmnkkalakknnkkkknknnkkkkmnnnnnnkkkbkkkknkknnkkkkkkkkkknkkkkkkkkknnmnnnnnkkbkbkbkbkbkkbkkkknnkkkkbkknnkkkknknknbkbkbbbabbbkbhllhllljljljhjjljlljhllhlllhlllhjhlli`i````a`abnnkbb`bkkkbbbbbkbbbbbbkkbbkbbbbabababbbbbkbbbaaabaaaabbaababbbbbkbbkbbaaaaaabaabaaabababbabaaaaabbbbaabaabbbibabnnk`iaaa``llljhjhllllj", +"lllllhlllhllhlllhlllhlhlhjhjlllljlljlhjlllllllhlhlhlhlhlhlhlllllljlljljljlljlhjhjhjhllllckbbkkbknfmkbalannnnkkbknkkknkkknkbknnnkkkkkkknnnnnnnnkkkknnkkkkkkkkkkkkkknnnnnkbkkkkkkkkkkkkkknkkkkkbbkkkkkbkkbkkknnnbbbbbbabbbbbbllllllhlhlhljhlllllhjljljhjljhlllllhiiciiiaaabbnnmnk`kkkkbbbakbbbkbbbbbbbbkbbbbababbkbbkbbbbbbbbbbbabababbabaabbbbbbbaababaaaaabbababbabaaaaaaaaaaaaaabbbbbiabannnb``addahlllllljljll", +"jhjhjllhlllhllhlllhllllllllhjhjlllllljhjhjhjhljlllllllhlllllhlhllhlhlhlhlllhljhlllllllllhbbbbbkbknkkb`jamnnnnkkkkkkknnnnnkkkkknkkkkkkkknnnnnnkkkknnnnkkkkkkkkkkkkkkkkkkkkkkkkkkkbkkkknnknnkbkkkkkbkbkkbkbkbnnnkkkkkbbbbkbbdljhjhjljljlllllhlllllhlhllhlhljhlllli`lli`aabbkmmnnmakkkkbbbbbbbbkkkkbbbbbkbbbbbbbbbbbbkbbbkabbkbbbbbababbbaabababaaaababaabaabaabbbbbbaabababaaaababaaabbaibbaknmka`abbbillllllllllj", +"llljhjhjlllllllllhlllhlhlhllllhlhlhlhllhjllljlllljhjhljljllllllhllllhllllhlllhljlhllllllibdbbbbknkabkajbnnnnnnnkkbkknnnmkkkkkkknkkbkbkknnknmnnnnknnnnkkkkkkkkkkkkkkkkkkbkbkkbkbkbbkkkkknnkbkkkkbkkkkkkkkkkknnkbknkkkkbbbbkdlllllllhlhlhlhlljllllllllljljllllllliilc`abaabbknnnn`kkkkkkkkbabbbkkkbbbbbbbbbbababbbkbbkbkkbbbbbbbabababaabbabaaababaababaaaaaabababbbaaababaabbabaaaaaabb`abknkkka`abbbcljlljljllll", +"lllhjllllljhjhllljhllllllllhllllllllllllhlhlhlhlhlllljhlhllllljllllljlhjhjllllhlhllhllllibdkbabknnbbkalknnnnmmnnnkkkkknnkbbkknnknkkkkkkkkknnnnnnknnnkkkkkkkkkkkkkkbkkkkkkkkkkkkkbkkbknnknkkkkkkbkkkkkkkbbkbbkkbkkkkkbkbbkkkllhjhllljljljlllhlllhllhlhlhlhlllllhiic`aabbbbbbnmmn`kkbkkkkbbbbbbbbbbbbbbbbbkbbabbbbbbkbbkbbbbkbkbbbbbbaabbabababababaaabababaaabbbbbabaaaaaaaaabaaabaaaab`annnkbka`abbdilllllhllllh", +"lhlllhlhlhllllhjhlljljllllllllllhjhjhjhllllllllllllhlhllllhllhlhlhlhlhjllhjhlhllllllllll`ddbddaknmkkb`jnmnnknnkkkkkbkknnkknnkknnnkkkkkkknkknkbkknnnnnnnknnnkbkkkkkkkkkkkkkbkbkbkkkkknnnnnnkkbbbkkkkkkkkkkkbkbkbbkkbbbkkbkkbhllllhlllhlhlhllllllljljljlllljhjlll`ciakakbbkbbbknnakkkbkkkbbbkabbbkaabbbbkkbbbbbbbbbbbbbabbbkbbbkbabbbbababbabababbbbababaababbbbbbbbbbaaaababaaaabaaab`aibnmnkbnb`abbb`ljhlllllhll", +"lllhllllhllhlhlllllhlhlhlhllllllllllllllllhlhllhjlljllllllllhllhlhlhlhhhlhhlhlhhhhhhhhhlodddddddknmkdglknknnnnkkkkkkkkkkkkkkknnmkkkknkkbnkkknkknmnnnnnnknkkkkknbkkkkkknnknkkkkkkkkbnnknnnknnnnkbkbkbbbkbkkkbbbbbbkkbbkkkbkkljhlljlhlllllljhjhlllhlhlhlhllhllhlh`i`ababkbbkmbknmakkkbbkbbbbbbbbbabbbbabbbkbbbbbbabbbbbbbbbbbkbbbbbbbbbaabbababbbbbbbabaabbaabbbabbbabbbbaaaaaaaaabaaaa``annnnkbb`bbad`llllhlljlll", +"hlhllhlhllhlllhlhllllllllllllllhjhlllhjhlljhjhjhlhlhlhlhlhhhlhhhhhhhhhlhhllhhhhhhhhhhhhhhhhhhhhhghghhhhggghhghhhhhhgg`g`g`o`dddbddbbkkkkkkknnnkmnnmnnnkbnnnkbbbkbkbknkkbnknkkkknnkkkkkknnnnnmnkkkbkbkkkkbkkbkkbbbbkkkknnknklljhjhlljljhjllllljllljlllllhllhllllii`dbbbbbkbnnbkncbbbknkbabbkbbabbbbbbbbbbkbbbbabbbababbbbbbkkbkbbbbbbbabaababbbabababababbababbbbkbbbbbbaaaaaaabaaaabaa``mnmkkbb`bbdddllhllhlhlhl", +"ljllllllhllhlhllhlhhlhhlhhhhhhhlhlhhhlhhlhhlhhhhhhhhhhhhhhhhgghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhghghhghghhhhhhhhhhhhhhhhhhhhhhhchhhco`oddkknnmnnkknnnkknkkbbkkkkkkbkkkkkkkknmnnnnnnnnnkkbkbkbkbbkkbkkkkbbbkknkbknkkhlllhllhlhllhlhlhlhllhlhlllllllljhjlii`abbbbakbmnkkkibbbbkbbbbbkbbbabbbbbbakbkbbbkbabaabbabbkbbkbbkbbbbbbbbbbbaaaaabaaababaabbaabbbbbbbbbbbbabaaadaaaaaaabaa`knnkbdaadd`ddhlhlhlhlllh", +"lhlhlhlhlhllhlhlhhlhhlhhhlhhlhhhhhhhhhhhhhhhhhhhhhhhgghhggggogoogggghhhhhhhhggggggghhhhhhhhhhhhhhhhhhlghhhhhhhhhgghghhhhlhhhhhghghhhhhhhhhhhhhhhhhhgho`oknknmnnmnkkkbkkkkkkkkkkkkknnnkknnnnnnnkbkbbbkbkbkkkbkkbbkkknnkkkkkblllhllllhlllhlllllllhllllhlhlhjhlllh`i`abbabbbknnnkn`bbbabbbbknkkkbkkbbbbbbbkbbbkkbdbbbbbbbbbbbbkbbkbbabbbbbbbbbaabaabbaabababbbbbbdabbbbbbddadddddddddddddd`kmkdd`higio`ghhhhhhhhhhh", +"lhlllhllhlhlhlhhhhhhhhhhhhhhhhhhhghhhhhhhhhhhhhhgggghgggggggggggggghghhhhhhhhhhhhhhhhlljhlhhhhhhhhhhhhhhhhhhgggghhhhghggghghhhhhgghhhhhhhgghhhhghhhlhlhhhhhh`oddnnnnkkkkbkkkkkkkkkbkkknnnnnnnnkbkkkbbkkkbkbkbkbbbbbkkkkbkdblhlllllljhlljhlllllllllllljllllljhlli``aaabbanbnnknncaabbbbbbbbbbabbbbbkkbkbbbbbkkdbbdabbbbabbbbbbbkbabbbbbbbkkbbbbbababbbababaaabdbbkbkkbkdddd`oig`og`ghhhhhhhghhhhghhhhhhhhghhhgghh", +"hhhhhhhhhhhhhhhhghgggggggggggggggggggghghghggggghghghggggogggggogggggghhhhhhggoggggghhghhghggggghhhhhhhhhghghhhhgggggghghhhhgggggggggggghhhhhgghhhhghhhhllhhhhhglhgiodddnknnnnnkkkkkknnnkknnkkbbbbbkkbbkbkkkkkkbbbbbbbbdbbdlljhjhjhlljhllllllllllllllhlhjlllljli`i`baabbbbnnkkblbkkkbbbbkbbbbbbbbbbkbbbbbbbkbdabbdddabbbbbbbabbbbbbbabbabbbkbabbbbabbbbbbbdddddkddd`oihhlhhhhghgggghhhhhhhhhghhhhhhhghghghghgggg", +"hhhhhhhhhhhhhhghggggghghgghghghgggggggghgggggggggghhhhhhggggggggggggggggggggghhhghghhhhhhhhhhhhhgggghhhhghgggghghggggggggggggggggggghhhhhhghghhhhhhhgghhhhhhhhhhhhlhlhhhhhh`ddkkddbbkkkkkknkkkbkkkkbkkkbbbkbbkknnnnnkbbbkkkllhlllhllhlllhlhlhlhllhllhlllhlhlhlhiii`abbbbakkbba`ibbbbkbbkbbbababbkbbabbkkbbkbdbdbbabbbbbkkbkbbbkbbbbbbbbbabbkkbbbkbkbbdbbddd``o`ohhhhhhhhhhhhhhhlhhhhghggghhhhgghhhggggggggghgggg", +"gggggggogggggggggggggggggggggggggggghgggggggggggghghgghhhggggghgggggghhhhhggghhghhhhhhhhhhhhhgggghgggggghhhhhghghhhhghhhghghhhhhhhhhhhhhhhhhhhhghhhhhhhhhhhhhhhlhhghhhhhhhhhhhhhhhcg`bknnnnnnnnnnnknnnbbbbbbabkknnnnbbbkmnklllllllllllhllljllljlllllljhjllljlll`i`abababkaa`ii````aababbkkkkkkknnkbbbbbbbkbbabbnbkmnnmknknkkbkkkbbbbbkkkkbbkkddkkddd`gcglhhhhhhhhhhhhhhhhghghhhghhhgggggghghhhhhghhhgggghhhhhhhh", +"ggoggogogogoggggoggogoggggggghggggggghhgggggggggggggghghgggggggggggghghhhhhhhghhgghghhhhghgggggggggggghghgggghghhhhhhhhhhhhhhhghghhhhhghhhhhhhhlhhhhhhhhhhhhhhhhlhhhhhhlhhhhhhhlhlhhh`ddknnnknnnkknbnnnkabbbbbknnmnnnnnknnnhlllhjhjhjljlllhlllhllllllllllllhljhi`idkkbbabaiciiiccciabbabbknbbkkkkkbabbbbbbbbaabnnkkkmkknnnnkbbknkbdkkkknkkddo`ooggghhhghhhhhggggghghgghhhhhhhgghgggggggggghgggghggogghggghhhhggg", +"ggogoggoggggggoggggggggggggghggghgggggggggggggggghhgghgggggggggghhhhhhhhhhhhhhhhhhhlhhhhhhhhhhhhhhhhhhhhhghhghghhhhhhhhghghhhghgggghhhhhhhhhhhhhhhhhhgghhhhhhhhhhhhhhhhhhhhhhhhhhhhlhhhhhgddknnkkknknnkbbkkbbbkmnnnnnnnnnnncjhjlllllhlhlllljllljhjllllllllljllli`ibkkbbba`i`aaiccc`aka``abbkbkkkknnbkbaaabkbabbnnkbkknnnknnnkdddmkddo`gghhhhhhhchghhghhhghggggghgghhhhggghhhhhhhhhhggggghgggogogggghhhhhhhghgggg", +"hghghghghghhhhhhhghhhggghhghgggggogggggggggogggghghhgghgggggggggggghhhhhhhhhhghhhhhghhhhhhhhhhhhhhhhhhhhhhhhhhhghhhhhghhhhhhhhggghhhhhhhhhhhhhhhhghghhhhhghhhhhhhhhhghghghhhhhhhhhhhhhhlhhhhhgddbkmmmnkakkkbbbkbbbbbbnnmmnnhlllhjhljlljhjhlhlllhllhjhlllllhlhjhi`idbnkkkb```aa`ii``i```i`abbbbknbnkkbaaabbbbbabkmnkbbnnnmnkddo`oghhhhhgghhghhhhgghggghghgggggggggggghghgggghhhhhgogogggggghggggggghgghhghhhhhhhg", +"hhhghhhhhhhhhhhhhhhhghhghgggggggggggoggogoggggggghgghhgghggggggggghhhhhhhhhhghhghhhhhhhhhhhhhhghhhhghggghhhhhhhhhhgggggghhhhhgghghghhghhhhhhhhhhgghhhghhhhhhhhhhggggggggggghhhhhhhhhlhhhhlhhhhhhhhgdddbbbbbabkkkkbbaabkknmnllhllhllhlhllhllllhllllllllhlhllllhli`iabkknkbaaaa`aa```aa`icl`akkkkkkkkka`abaabbbbbbkndddndkd`oghhhhhhhhhhhhhghghhhghggggggghgggggoggggooggghhgogggghghhhgggggggggghhghghgghgghghggh", +"ggghghhhhhhhlhhhhgggggghgggghghghggggggoggggggggggghhghhhgggggghhhhhhhhhhghhhhghghhhhhhlllhhhhhhhhhhhhhhhhhhhhhhgghggggggghgggggggggggggggghgggghhhhggghghghhhhhgghghhhhhgghghhhhlhhhhhlhhhhlhhhlhlhhhhi`dkdddbbknkkkkkknnmlllhlllhlllhllhlhllhlhlhlhllllhlhlll````bbbbbabbaaabka`akkbicliannkkkbkkba`abbbdkkkbdkkdo`ghhhhhhhhhggghghhggggghggggghhgghghhggggoggghhhhghhhhhhhghgghhhhggohghhhhhgggogogggggggghgh", +"hghghghhhhhhhhhhhhgggggggghghghggggghggggggggggggggggggghhhhghghhhhhhhhgghggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhghggggggghghhhghggghghghhhhhhhhhhhhhhhhhhghhhhhhlhhhgggghhhhhhhhhhhhhhhhhhhhhhhlhhlhllhhhhh`dkkdkbbbbbbbkknlhlchlhllhllhlllllhlllllhllhlhlllhlhi`i`bbbbbbbbbabbbbaakkaiiliaknkkbkbbaddbddddddddohhhhhhhhhhhhhhhhggggggggggghhhhhggghghhhhgggoggggggggggghgggggggghghhhhhhhhhghggggggggggggghhhhh", +"gggggghghhhhhhhhggggggghhhghhhghhhgggggggggggggggogggggghgghgggggggggghhhhhhggggggggghhhhhhhhhhhgggghggghhhhhhhghgggggghhhhhhghghhhhhhhhhhhhhhhghhhghhhhhhhhhhhhhhhlhhhhgghgghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhgdnmnnnkbbbkblllllhlchlchlchlhllhlhchlchllchcllcliiiabakbbkbaaaaabaaakaaicciabknbbkbaddbddo`gggghhhhhhhhggggggghhgggggoggggggggogggggghghhhhghgggghhhhghhhhhggggggggggggggggggggggogoogggggggggggg", +"ghhhhhhhhhhhhghggggggghhhhhhhhhhgggggggggggggoggggggggghggggghghhhhhhhhhhhggghggghgghghghhhhhhhhhhhhhhhhhhhhhghggggggggghghghhghggghhhhllllhhhhgghgggghgghghhhhhhlhhhhhhhgghhhhhhhhhhhhhhhhhhhhhhhhhlhhhhlhlhhlhh`dknmkkbbdlllhlchllhllchllchlchllclllchlllhchlgciabbkbbbbabbbabbabbaailcc`abkkbd`iiihhhhhhhhgghghghgggggggggghhghggggoggogggggggghhhhghgggggggggggghggggghhgggggghggggghggggogogogggggggggghhhh", +"hhhhhhggggggghghhhhhhhhhhhhhhhhhhhhhhhhhhggggggohggggggggghhhhhhhhhhhhggggggggghghghghhhhhhhhhhhlhlhlhhhhhhgggggggggghghghhghhhhhhhhhhhlhhhhhhhggghhhhhhhhhhghghhhhhhhgggghhhhhhhhhhhhhhhhhhhhlllhhhhhhhhhhhlhhhllhlgakmkdglhlllllclcclllchllchlchlhchllhcllllccciabbabbbbbbababbaaaiiiiii`ddd`dgghhghhggghhhghhgggggggggggggggggggggogggggghhhhhhgggggggghhhhhhgghgggggggggggoggggghghggggggggggggggggghhghgggg", +"hhgggggggggghhhhhhhhhhlhhhhhhhhhhlhhhhhhgghghhhhhhhhhgggghghhhhhhhhhhhgggggggghgggggggggggggghhhhhhhhhhhhgggggggggggggggghhhhhhhhhghhhhhhlhhhhhhgghhhhjljllhhgggghghhhhghghgghhhhhhlhhhhhhhhhhhhlhlhhhhhhhhhhhhhhhhhhhhg`gchllhlhlljjjjljjljljllllllllllllhllhllhidbbaabaabbkkbbbbaa````igioihhhhghghhhghhgggggggggghhhhgggggggghhhgghgggggghghhgghhhhggggggogggggggggggghggggggoggggggoggghhhhhhhhhhhhhgggggggg", +"ggggggogggghghhhhhhhhhhhhhhhhhhhhhhhhggghghhhghhhhhhghghgghghhhhhhhhhgggggggggghgggggggggggghghhhhhhhhhgggggggggggghgggghhhghhhhhghghhhhhhhhhhhhhgghhlhjhjhhhhgghhhhhhhhhhghghghhhhhhhhhhhhhlllhhhhhhhhhhhhhhhhhhhhhhjhlhlhlhlhjjjjjjjjjjjjjjjjjjjjjjjjjjljllllhiiidbababbabbbknnkkdido`hhhhhhhhhhghgghghggghhgghgggggggggoggggoggggogggggoggggghgggggghggggggghgggggghhghggggogogghhggggggggggghhhggggggghggggg", +"ogogoggggggggggghghggggghhhghggghghghggggghghhhghghghhgggggggggghhhhghgggggggggggggggggggghgggggggghggggggggggggggghghhhhghhhhhhggghhhhhhhhhhhhhhgghhhhhhlhjhhhhhhhhghhhhhhhhghhhhhhhhhhhhhhhhhlhhhhhhhhhhhhlhhhhhhhjhhhlhlhlhllljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjc`iibbbbbaaakbdkkkddighhhhhhhhhghgggggggghgghhhhghgggggggggggggggggogggggoggggggggghggggggggggggggogogghghhgggggggghhhhhhhhgghghhghghghhhgggggogo", +"ggogogogggggghhhhhhhhhhhhhghgghhghhhghhhgggggghhghgggghggggghhhhhhhghhhhgggggggggggggggghhghgggggghghghhghhhhgggggghghhghhhhhhggghghhhhhhhhhhhhhgghghhljhjhhhhhhhhghghgghhhhhhhghhhhhhhhlhhhhhhhhhhhhhghhhhhhhhhhhhhhhllhhhhlhlllljjjjjjjjjjjjjjjjjjjjjjjjjjjjlli``bababbddddddogggghhhhhghghgghgggggggghggghggggggggggoggoggggggggggggggggghgggggggggggggggggogggggghghghgggggogghhhhhhggggggggggggogogggoggogo", +"oogogogogogghgghhhhhhhhhhhhhhhhhhhhhhhgggggggggghghhhghghghhhhhhhhghgghghhhghhhhhhhhhhhhhghggggghgghghgghgghghhhgghghhghghhhhhhghhhhhhhhhhhhhhhhghghhhhhlhhhhhhhghhhhghhhhhhhhhhhhhhhhhhlhhlhhhhhhhhgghhhghhhhhhhhhhhhhhlhhhhhhhhlhlljjjjjjjjjjjjjjjjjjjjjjjjjjjiiibabbdbd`oghhhhghhghhohhhgggggghghgggghgggggggggggggogggggggogoggggggggogghhggggogoogggggogggggogggggggggggggggggghhgggggggggggggggggggogoogoo", +"ggogoggggggggghhhhhhhhhhhhhhhhhhhhgggggghhghhhhhhhhhhhhhhhhhhhhhgghggggggghhghhhhhhghggggggghggghghghghgghhhhhghhhghhghghggggggghhhhhhhhhghgggggghhhhhhhhhhhhhghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhghhhhhghhhlhhhhhlhhhhhlhlhlhhhlhjljjjjjjjjjjjjjjjjjjjjjjjjl```kbbd`ggihhhhgggghhghhghhghhhhggggggghgggggghhghggggggoogggggggggggggggoggggggggghhggogggggoggghhhhhhhgggggghgghghgggggggogogggggggggggggggogg", +"ggggggogggggghhhhhhhhlllhlhhhhhhgghhhhhhghhhhhhhhhhhhhhhhhhhhhhhhghghghghhhhhhhhhhhhhghggghghghghhhhhhghhhhhhhhhhhhhhhhgghhggghhghghhhhgghgggggggghhhhhhhhhhhghghhhhhhhhhhhhhhhhllhhhhhhhhhhhhhhhhhhhhgghhhhhhhhhhhhhhhlhhhhlhhhhhhhlhlhjjjjjjjjjjjjjjjjjjjjjjlhi``ddihhhhhhhghhhghhhghhghhhhhhggggggggggggghhghgghgggggggogghhhghhgooogggogggggggggggggogoggggggogggggoggghghhhghhggogggghhhhghgggggggggggogggg", +"gggggggghhhhhhhhhlhhhhhhhhhhhhghghhghghhhghghhhhhhhhhhhhhhhhhhgghgggghghhghhhhhghgggggggghgggggggggggghhhhhhhhhhhhhhhghhhggggggggggghghghghggggghhhhhhlhhhhhhhghhhhhhhhhhhhghghhhhhhhhhhhhghhhhghhhhhhhhhhhhhhhhllhhhhhhhhhhlhhhhhhhlhlhlhjjjjjjjjjjjjjjjjjjhlhcgggghghhhhhgghghgghgghgggggghhhhgggggghggggghhhgghhgggggogoggggghggghhgoogogoogggooogogogoggogogogogoggghhgggghhhhhghhgoghghhghhhhhhhgggoogggggh", +"ghghhhhhhhhhlhhhhhhhhhhhghhhhghhghghhghghhghggghhhhhhhhhhhhhghhhgghghghggghghghghghhghhghggghghgggggggghhhhhlhlhhhhhhhghhhhghggggggghghghgghgggggghhhhhhhhhghhhhhhhhhghhgghgggghhhhhhhhhghhhhhghhhhhhhhhhhhhhhhhhhllhhhhhhhhhhhhhhhhhhhlhlllljjjjjjjjjjjjljlhllhhghhghghhghgggghgghggggghhhhhhhhhghggggghgggggggghhggggogggogogggggggggggggggoggogggggggggggogoggghggggggghggggogggggggggoggggggoggggggggggggggg", +"hhhhhhhlhhhhhhhhhhhhhhhhhhghghghghghggggggggggggghhhhhhhhghghggggghghhghhhhhhhhghggghgghghhggggggggggggghhhhhhhhhhhhhhhhghgggghghgghghhghghgghggghhhhhhhhgggghghghghgghghghghhhhhhhhhlhhhhhghhghhhhhhhhhhhhhhlhhhhhhhhhhhhhhhhhlhhhhhlhhhlhhhhjljjjjjjllhhhhhhhhhhhggggogghhghhhggggggggggggghhhhhgghggghgggggggghhhhgggggggogoggggggggggggogogoggggggggggggggggggggggogggggggggogggggggoggggggghggghggggggggggg", +"hhhhhhhhhhhhhhhhhhhhhhhhhghhggggggggggggggggoggggggghghhghghghgghgghghhhhhhhhhhhghghgggggghghhhghggggghghhhhhhhhhhhhhhghhghhhghghgggghhghghghhhhhhghhhhghghgggggghggghghhhhhhhhhhhhhhhhhhhgggghhhhhhhhllhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhlhhlhlhhhlljjhjhlhlhhhhhhggggggggggggggggggggooggggggggggggggghghggggoggghgghhggggggggogogggooogggggoggogogogoggghghghhggggggggggooghhhhhgogggooggggogggghhhggggggghghghg", +"hhhhhhhghhhhhhhhhhhhhhhhghggggggggghhhghgggggggggghhghhhgghghghghghghhhhhhhhhgggggggghhghghghgghgggggggggggghhhhhhhhhhhhhhhhhhhggggghhhhhhhhghhhhhhhhhhgghghhhhghgghgghghghghhhhhhlhlhlhhhhhhhghhhhhhhhhhhhhhhhlhhhhhhghghhhhhhhhhhhhhlhhlhlhhlhhlhlhlhhhhhghhggggogggogggggghggggggghhggggggghgggggggggghggogggghghhggogggoggggoogggggggoggggggoggggggggggggggggggggggogggghhhghgggggggogoggggggggggggoggghhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhggggggggggghghhhghhgggggggggghhghhhhghghggghhhhhhhhhhhghgggggggggghghghgggggogggggghhhghggghgghghhhhhhhhghgghghhhhhhhghghhhhhhghggghghhhhhhghghhghhghhhhhhhhlhjhlhhhhhhghhhhhhhhhhhhhhhhhhhhhhghhghghhhhhhhhhhhhlhlhhhhhhhhhlhhhgghggggggggghggghggghghhggoggghhhggggggggggghhgggghggggggghhhhgggogoggogoggggggggggghghgggggoggggogoogggggogggoggggghggggggoogogggggggghghghggogogogoggog", +"hhhhhhhhhhhhhggggggggggggggggghghghhhhhhgggggogggghgghhhhhhggggghghhhhhhhhhhghgggggggggghghhghgggogogggghhhhhggghggghghghhhhgggggghhhhhhhhhghggghhhhhhhghgghhhhhhhhhhhhhhhhhghhhhhhhhhhhhhhhhgghhghhhhhhghhhhhhhhhhhhgghhhhghhhlhhhhhhhhhhhhhhhhllhhlhhhhggggggoghghggoggoggggggggggghhgggggghggghghgggggggggggghhhhhgoogoggoggggooggghggggggggghggogoggggggggggggoggggggghghhhhggggggoggghgggggggggooooogogoggg", +"hghhhhhhhhhhggggggggggghhhhhhhhhhhhhhhhgghgggggggghghhhhhghghghhhhhhhhhhhhhggggggggggghhhhghhggggggggghghghgghhghghgggggghggghghgghhhhhhhhhhgggggghhhhhhhhhhgghghhhhhhhhhhhhhhhhlhhhhhhhhhhhhggghhhhhhhgghghhhhhhghghhhghghhhhhhhhhhhhhhhlhlhlhhhhlhhhhhgggggggggghgggggoggggggggggghhhggghggggggggggggggoogggghhhhgoggggggggggggogghhhggggogghhghggghggghgggggggggogooogghhhghhhhhggggghhgggogogogoogogoogogogg", +"ghghhhhhgggggggghhhhhhhhhhhhhhhhhhhhhhgghgggghghghghghgggggghhhhhhhhhhhhhhggggggggghghghggggghghgghhhhhhhhhhggggggggggggggghgghggghghhhhhhhgghghhhhghhhhhhhhhhhhhhhghhhhhhhhhhllhlhhhhhhhhhhhhghghhgggghhhhhhhhhhhhghghhghhhghhhhhhhhhhlhhhlhlhhlhhhhhhlghhggoggggggogghggogggggggghhghghggghgghggoggogggggggoogggggggggogoggogogggogghhggogghhhhhggggghhggggoooooooogggoggggghhghhhgghggggggogoghgghgggggoggggg", +"ghghggggggggggggghghhhhhhhhhlhhhhhhhhhhhgghggghggggggggghghhghghhhhhhhghhggggggggggggggggggggggghhghghhhhhhghgghghggggogggggggggggghhhhhhggggghghhhhghhhhhhhhhhhggghhhhhlhlhlhhlhhhhhhhhhhhhhghghghhhghhgghhhhlhlhhhghghhhghhhhhhhhhhhhhhhhlhhhhhhhlhhhhhlhhhggoggggggooggggoggggggghhhhggggghgggggggggggggogogghgggggghhggggggghhgggggogoggggggggggggggggggggogogoggooggggggggggggggogoggggoggggggggghggggogggg", +"hgggggghghggghghgghghhhhhhhhhhlhhhhhhhhghghhhghgggggghhhghghghhhhhhghghgghghhgggoogogogggghghhgghgghghghghghgghgggggggggggghggggggggghhggggghghhhhghghggghghhhhhhhghhhhhhlhlhlhhhhhhghhhlhhhhhgghhhhhhgghhhhhhhhhhhhhhhghghhhhhhhhhhhghhhhhhhhhhlhhhhhhhlhhhhhhhgggggghhggooggggggoggggggggghghhgooggggogogggggggggghhhhhhgggoghhhggogooggggggggggggogghhggogogoggoggggogggggggggggogogggooogoggoggggggggoggoggg", +"ghghghhghhhhhghgggggghhhhhhhlhhhlhhhhhghghghhghggggggghhgggghgggggghhhhhhhhggggggggoggggghghhhhghgggggggggggggggggggggoggghghghgggggggghggggggggghhhhghghgghhhhhhhghhlhhlhhlhlhhhhhghhhlhllhhhhhhhhhhhhhhhhhhhhhhhhhhhgggghhhhhhhhlhhhhhhhlhlhhhlhhhhhhhhlhhhhhhhhggggggogoggggggggggggggghggghgggoogogoggoogogggggghhhhhhgggggggggggogggggggooggogoggghgggoogoogoggogogoggghgggggggggggghggggggggggggggoggogogg", +"hhhhhhhhhghghggggggghhhhhhhhhhhhhhhhhhgggggggggggggghgggghgggghghghhhhhhhhgggggggoggggghhhhhhhhhghgghghhggggggggggggggggggggggggggghghghgghghgghhhhghghgghhhhhhhhggghhlhlhlhlhlhhhghhhhhlhlhhhhhhhhhhhhhhhhhhhhhhhhhhgggghhhhhhhlhhlhlhhlhhlhhhhhhhllhhhhlhhhhhhhhggggghggghggghgghhgggghhggggghggggoggggoggggggggogggghhhgghggggghgggggghgggggggggggggggooggogggggogogoggggghggggoooogoggggggggggggggggggoggggg", +"hhhhhhhhghggggggggghghhhhhhhhhhhhhhhhggggggggggggggggggggggghghghhhhhhhggggggggoggggghhhhhhhhhhhhghgghggggggghghggggogggogggggghgghggghghghgggggggghghhhhhhhlllhhhghhhhhlhhlhlhhhhhghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhghhhhhgghhjljhlhhlhlhhlhhhhhhlhhhhlhjhhlhhjhhhhhggghhhoghhgggggghhggggogggghhggggggggoggggggggggggggoggghgggggghhggogggghgggggogggggoggggghghghhhgggggggggggggoggggggggggggogggoggggggogogggg", +"hhhhggggggggggggggghhhhhhhhhhhhhhhhggggggggggggghhhghghghghgghgghhhghgggghgggggggggghhhhhhhhhhhhghghggghgghhhhhghgggggggggggghggghgghgghhhgghgghghhgghghhhlllhlhhhhhhhhhhhhhhhhhhhgghhhhhhhhhhhhlllhlhhhhhhhhhghhhhhhhhhhhhhhhlhjhlhhlhhhhhhhhhlhlhhhhllhhlhjhllhhlhlhggggogooggggggggogggggoggggggooggogggggggggggggggggggghggggggggogoggggggooogogggggggggghghhhhhhhhhhhhggggggggoggghhhhhggggogggghggggoooogg", +"gggggghggggggggggghgghhhhhhhhhhghgghghggggggggghggggghghggghggghghggghghhhghgggghghhghhhhhhhhhhghhhghggggggghhghghgggggogoggggghgghghghhghghgghghhhghghhhhlhhlhlhhhhhhhhhhhhhhhhhhhghhhhhhhgghhhhlhjhlhlhhhhhhhhhhhhhhhhhhhhhhhhlljlhhhlhhhhhhhhhhlhjhhhjhlhhhgjhhhhhhhhoggoogggoogghggghgggggggggoggggogoggoogggggghgghggghggggggogogogoooggghggoggggggggghhhhhhhhhhhhhhhhggghgggogoggghhhhhggogoghhhhhhgggggog" +}; diff --git a/sys/amiga/ifchange b/sys/amiga/ifchange new file mode 100644 index 0000000..db3f4ea --- /dev/null +++ b/sys/amiga/ifchange @@ -0,0 +1,56 @@ +.KEY oper/a,tmp/a,real/a,f1,f2,f3,f4,f5 + +. ; miscellaneous script functions for the Amiga +. ; SCCS Id: @(#)ifchange 3.2 96/02/04 +. ; Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1992, 1993, 1996. +. ; NetHack may be freely redistributed. See license for details. + +FAILAT 6 +IF EQ "MOVE" + IF EXISTS + diff >T:mic -c + search from T:mic SEARCH "---" QUIET + IF WARN + echo "MOVE: no change" + delete + ELSE + echo "MOVE: copy" + copy clone + delete + ENDIF + ELSE + echo "MOVE: copy2" + copy clone + delete + ENDIF + QUIT +ENDIF + +IF EQ "TOUCH" + IF EXISTS + diff >T:mic -c + search from T:mic SEARCH "---" QUIET + IF NOT WARN + echo "TOUCH: touch" + IF NOT EQ "@" + setdate + ENDIF + IF NOT EQ "@" + setdate + ENDIF + IF NOT EQ "@" + setdate + ENDIF + IF NOT EQ "@" + setdate + ENDIF + IF NOT EQ "@" + setdate + ENDIF + ENDIF + ENDIF + QUIT +ENDIF + +echo "ifchange: '' not recognized" +quit 10 diff --git a/sys/amiga/mkdmake b/sys/amiga/mkdmake new file mode 100644 index 0000000..2d80c85 --- /dev/null +++ b/sys/amiga/mkdmake @@ -0,0 +1,14 @@ +GE/$@/%(left)/ +GE/$ + +#include "config.h" +#include "tile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _DCC +# include +# include +# include +#endif + +void panic(const char *); +void map_colors(void); +int BestMatch(int, int, int); + +extern pixval ColorMap[3][MAXCOLORMAPSIZE]; +extern int colorsinmap; + +/* + * WARNING: + * This program carries forth the assumption that the colormaps in all + * of the .txt files are the same. This is a bug. + */ + +struct { + int Height; + int Width; +} IFFScreen; + +/* + * We are using a hybrid form of our own design which we call a BMAP (for + * bitmap) form. It is an ILBM with the bitmaps already deinterleaved, + * completely uncompressed. + * This speeds the loading of the images from the games point of view because it + * does not have to deinterleave and uncompress them. + */ +#define ID_BMAP MAKE_ID( 'B', 'M', 'A', 'P' ) /* instead of ILBM */ +#define ID_BMHD MAKE_ID( 'B', 'M', 'H', 'D' ) /* Same as ILBM */ +#define ID_CAMG MAKE_ID( 'C', 'A', 'M', 'G' ) /* Same as ILBM */ +#define ID_CMAP MAKE_ID( 'C', 'M', 'A', 'P' ) /* Same as ILBM */ +#define ID_PDAT MAKE_ID( 'P', 'D', 'A', 'T' ) /* Extra data describing plane + * size due to graphics.library + * rounding requirements. + */ +#define ID_PLNE MAKE_ID( 'P', 'L', 'N', 'E' ) /* The planes of the image */ + + +#ifndef _DCC +extern +#endif +struct Library *IFFParseBase; + +int nplanes; + + +/* BMHD from IFF documentation */ +typedef struct { + UWORD w, h; + WORD x, y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE reserved1; + UWORD transparentColor; + UBYTE xAspect, yAspect; + WORD pageWidth, pageHeight; +} BitMapHeader; + +typedef struct { + UBYTE r, g, b; +} AmiColorMap; + +pixel pixels[TILE_Y][TILE_X]; +AmiColorMap *cmap; + + +int findcolor( register pixel *pix ); +void packwritebody( pixel (*tile)[TILE_X], char **planes, int tileno ); + + +void +error( char *str ) +{ + fprintf( stderr, "ERROR: %s\n", str ); +} + +/* + * This array maps the image colors to the amiga's first 16 colors. The colors + * are reordered to help with maintaining dripen settings. + */ +int colrmap[] = { 0, 6, 9, 15, 4, 10, 2, 3, 5, 11, 7, 13, 8, 1, 14, 12 }; + +/* How many tiles fit across and down. */ + +#define COLS 20 +#define ROWS ((tiles + COLS-1) / COLS) + +main( int argc, char **argv ) +{ + int colors; + struct { + long nplanes; + long pbytes; + long across; + long down; + long npics; + long xsize; + long ysize; + } pdat; + long pbytes; /* Bytes of data in a plane */ + int i, cnt; + BitMapHeader bmhd; + struct IFFHandle *iff; + long camg = HIRES|LACE; + int tiles=0; + char **planes; + + if(argc != 3){ + fprintf(stderr, "Usage: %s source destination\n", argv[0]); + exit(1); + } + +#if defined(_DCC) || defined(__GNUC__) + IFFParseBase = OpenLibrary( "iffparse.library", 0 ); + if( !IFFParseBase ) { + error( "unable to open iffparse.library" ); + exit( 1 ); + } +#endif + + /* First, count the files in the file */ + if( fopen_text_file( argv[1], "r" ) != TRUE ) + { + perror( argv[1] ); + return( 1 ); + } + + nplanes = 0; + i = colorsinmap-1; /*IFFScreen.Colors - 1; */ + while( i != 0 ) + { + nplanes++; + i >>= 1; + } + + planes = malloc( nplanes * sizeof( char * ) ); + if( planes == 0 ) + { + error( "can not allocate planes pointer" ); + exit( 1 ); + } + + while( read_text_tile( pixels ) == TRUE ) + ++tiles; + fclose_text_file(); + + IFFScreen.Width = COLS * TILE_X; + IFFScreen.Height = ROWS * TILE_Y; + + pbytes = (COLS * ROWS * TILE_X + 15) / 16 * 2 * TILE_Y; + + for( i = 0; i < nplanes; ++i ) + { + planes[ i ] = calloc( 1, pbytes ); + if( planes[ i ] == 0 ) + { + error( "can not allocate planes pointer" ); + exit( 1 ); + } + } + + /* Now, process it */ + if( fopen_text_file( argv[1], "r" ) != TRUE ) + { + perror( argv[1] ); + return( 1 ); + } + + iff = AllocIFF(); + if( !iff ) + { + error( "Can not allocate IFFHandle" ); + return( 1 ); + } + + iff->iff_Stream = Open( argv[2], MODE_NEWFILE ); + if( !iff->iff_Stream ) + { + error( "Can not open output file" ); + return( 1 ); + } + + InitIFFasDOS( iff ); + OpenIFF( iff, IFFF_WRITE ); + + PushChunk( iff, ID_BMAP, ID_FORM, IFFSIZE_UNKNOWN ); + + bmhd.w = IFFScreen.Width; + bmhd.h = IFFScreen.Height; + bmhd.x = 0; + bmhd.y = 0; + bmhd.nPlanes = nplanes; + bmhd.masking = 0; + bmhd.compression = 0; + bmhd.reserved1 = 0; + bmhd.transparentColor = 0; + bmhd.xAspect = 100; + bmhd.yAspect = 100; + bmhd.pageWidth = TILE_X; + bmhd.pageHeight = TILE_Y; + + PushChunk( iff, ID_BMAP, ID_BMHD, sizeof( bmhd ) ); + WriteChunkBytes( iff, &bmhd, sizeof( bmhd ) ); + PopChunk( iff ); + + PushChunk( iff, ID_BMAP, ID_CAMG, sizeof( camg ) ); + WriteChunkBytes( iff, &camg, sizeof( camg ) ); + PopChunk( iff ); + + /* We need to reorder the colors to get reasonable default pens but + * we also need to know where some of the colors are - so go find out. + */ + map_colors(); + + cmap = malloc( (colors = (1L<iff_Stream ); + FreeIFF( iff ); + + printf( "\n%d tiles converted\n", cnt ); + +#if defined(_DCC) || defined(__GNUC__) + CloseLibrary( IFFParseBase ); +#endif + exit( 0 ); +} + +findcolor( register pixel *pix ) +{ + register int i; + + for( i = 0; i < MAXCOLORMAPSIZE; ++i ) + { + if( (pix->r == ColorMap[ CM_RED ][i] ) && + (pix->g == ColorMap[ CM_GREEN ][i] ) && + (pix->b == ColorMap[ CM_BLUE ][i] ) ) + { + return( i ); + } + } + return( -1 ); +} + +void +packwritebody( pixel (*tile)[TILE_X], char **planes, int tileno ) +{ + register int i, j, k, col; + register char *buf; + register int across, rowbytes, xoff, yoff; + + /* how many tiles fit across? */ + across = COLS; + + /* How many bytes per pixel row */ + rowbytes = ((IFFScreen.Width + 15)/16)*2; + + /* How many bytes to account for y distance in planes */ + yoff = ((tileno / across) * TILE_Y) * rowbytes; + + /* How many bytes to account for x distance in planes */ + xoff = (tileno % across) * (TILE_X/8); + + /* For each row... */ + for( i = 0; i < TILE_Y; ++i ) + { + /* For each bitplane... */ + for( k = 0; k < nplanes; ++k ) + { + const int mask = 1l< rate){ + bestrate = rate; + bestslot = x; + } + } +#ifdef DBG + printf("map (%d,%d,%d) -> %d (error=%d)\n",r,g,b,bestslot,bestrate); +#endif + return bestslot; +} + + +long * +alloc( unsigned int n ) +{ + long *ret = malloc( n ); + if(!ret){ + error("Can't allocate memory"); + exit(1); + } + return( ret ); +} + +void +panic(const char *msg){ + fprintf(stderr,"PANIC: %s\n",msg); + exit(1); +} diff --git a/sys/amiga/winami.c b/sys/amiga/winami.c new file mode 100644 index 0000000..7071bfb --- /dev/null +++ b/sys/amiga/winami.c @@ -0,0 +1,1725 @@ +/* SCCS Id: @(#)winami.c 3.2 2000/01/12 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993,1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" +#include "dlb.h" + +#ifdef AMIGA_INTUITION + +static int FDECL( put_ext_cmd, ( char *, int, struct amii_WinDesc *, int ) ); + +struct amii_DisplayDesc *amiIDisplay; /* the Amiga Intuition descriptor */ +struct Rectangle lastinvent, lastmsg; +int clipping = 0; +int clipx=0; +int clipy=0; +int clipxmax=0; +int clipymax=0; +int scrollmsg = 1; +int alwaysinvent = 0; +int amii_numcolors; +long amii_scrnmode; + +/* Interface definition, for use by windows.c and winprocs.h to provide + * the intuition interface for the amiga... + */ +struct window_procs amii_procs = +{ + "amii", + WC_COLOR|WC_HILITE_PET|WC_INVERSE, + 0L, + amii_init_nhwindows, + amii_player_selection, + amii_askname, + amii_get_nh_event, + amii_exit_nhwindows, + amii_suspend_nhwindows, + amii_resume_nhwindows, + amii_create_nhwindow, + amii_clear_nhwindow, + amii_display_nhwindow, + amii_destroy_nhwindow, + amii_curs, + amii_putstr, + amii_display_file, + amii_start_menu, + amii_add_menu, + amii_end_menu, + amii_select_menu, + genl_message_menu, + amii_update_inventory, + amii_mark_synch, + amii_wait_synch, +#ifdef CLIPPING + amii_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + amii_print_glyph, + amii_raw_print, + amii_raw_print_bold, + amii_nhgetch, + amii_nh_poskey, + amii_bell, + amii_doprev_message, + amii_yn_function, + amii_getlin, + amii_get_ext_cmd, + amii_number_pad, + amii_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + amii_change_color, + amii_get_color_string, +#endif + /* other defs that really should go away (they're tty specific) */ + amii_delay_output, + amii_delay_output, + amii_outrip, + genl_preference_update +}; + +/* The view window layout uses the same function names so we can use + * a shared library to allow the executable to be smaller. + */ +struct window_procs amiv_procs = +{ + "amitile", + WC_COLOR|WC_HILITE_PET|WC_INVERSE, + 0L, + amii_init_nhwindows, + amii_player_selection, + amii_askname, + amii_get_nh_event, + amii_exit_nhwindows, + amii_suspend_nhwindows, + amii_resume_nhwindows, + amii_create_nhwindow, + amii_clear_nhwindow, + amii_display_nhwindow, + amii_destroy_nhwindow, + amii_curs, + amii_putstr, + amii_display_file, + amii_start_menu, + amii_add_menu, + amii_end_menu, + amii_select_menu, + genl_message_menu, + amii_update_inventory, + amii_mark_synch, + amii_wait_synch, +#ifdef CLIPPING + amii_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + amii_print_glyph, + amii_raw_print, + amii_raw_print_bold, + amii_nhgetch, + amii_nh_poskey, + amii_bell, + amii_doprev_message, + amii_yn_function, + amii_getlin, + amii_get_ext_cmd, + amii_number_pad, + amii_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + amii_change_color, + amii_get_color_string, +#endif + /* other defs that really should go away (they're tty specific) */ + amii_delay_output, + amii_delay_output, + amii_outrip, + genl_preference_update +}; + +unsigned short amii_initmap[ AMII_MAXCOLORS ]; +/* Default pens used unless user overides in nethack.cnf. */ +unsigned short amii_init_map[ AMII_MAXCOLORS ] = +{ + 0x0000, /* color #0 C_BLACK */ + 0x0FFF, /* color #1 C_WHITE */ + 0x0830, /* color #2 C_BROWN */ + 0x07ac, /* color #3 C_CYAN */ + 0x0181, /* color #4 C_GREEN */ + 0x0C06, /* color #5 C_MAGENTA */ + 0x023E, /* color #6 C_BLUE */ + 0x0c00, /* color #7 C_RED */ +}; + +unsigned short amiv_init_map[ AMII_MAXCOLORS ] = +{ + 0x0000, /* color #0 C_BLACK */ + 0x0fff, /* color #1 C_WHITE */ + 0x00bf, /* color #2 C_CYAN */ + 0x0f60, /* color #3 C_ORANGE */ + 0x000f, /* color #4 C_BLUE */ + 0x0090, /* color #5 C_GREEN */ + 0x069b, /* color #6 C_GREY */ + 0x0f00, /* color #7 C_RED */ + 0x06f0, /* color #8 C_LTGREEN */ + 0x0ff0, /* color #9 C_YELLOW */ + 0x0f0f, /* color #10 C_MAGENTA */ + 0x0940, /* color #11 C_BROWN */ + 0x0466, /* color #12 C_GREYBLUE */ + 0x0c40, /* color #13 C_LTBROWN */ + 0x0ddb, /* color #14 C_LTGREY */ + 0x0fb9, /* color #15 C_PEACH */ + + /* Pens for dripens etc under AA or better */ + 0x0222, /* color #16 */ + 0x0fdc, /* color #17 */ + 0x0000, /* color #18 */ + 0x0ccc, /* color #19 */ + 0x0bbb, /* color #20 */ + 0x0BA9, /* color #21 */ + 0x0999, /* color #22 */ + 0x0987, /* color #23 */ + 0x0765, /* color #24 */ + 0x0666, /* color #25 */ + 0x0555, /* color #26 */ + 0x0533, /* color #27 */ + 0x0333, /* color #28 */ + 0x018f, /* color #29 */ + 0x0f81, /* color #30 */ + 0x0fff, /* color #31 */ +}; + +#if !defined( TTY_GRAPHICS ) || defined( SHAREDLIB ) /* this should be shared better */ +char morc; /* the character typed in response to a --more-- prompt */ +#endif +char spaces[ 76 ] = +" "; + +winid WIN_BASE = WIN_ERR; +winid WIN_OVER = WIN_ERR; +winid amii_rawprwin = WIN_ERR; + +/* Changed later during window/screen opens... */ +int txwidth = FONTWIDTH, txheight = FONTHEIGHT, txbaseline = FONTBASELINE; + +/* If a 240 or more row screen is in front when we start, this will be + * set to 1, and the windows will be given borders to allow them to be + * arranged differently. The Message window may eventually get a scroller... + */ +int bigscreen = 0; + +/* This gadget data is replicated for menu/text windows... */ +struct PropInfo PropScroll = { AUTOKNOB|FREEVERT, + 0xffff,0xffff, 0xffff,0xffff, }; +struct Image Image1 = { 0,0, 7,102, 0, NULL, 0x0000,0x0000, NULL }; +struct Gadget MenuScroll = { + NULL, -15,10, 15,-19, GRELRIGHT|GRELHEIGHT, + RELVERIFY|FOLLOWMOUSE|RIGHTBORDER|GADGIMMEDIATE|RELVERIFY, + PROPGADGET, (APTR)&Image1, NULL, NULL, NULL, (APTR)&PropScroll, + 1, NULL +}; + +/* This gadget is for the message window... */ +struct PropInfo MsgPropScroll = { AUTOKNOB|FREEVERT, + 0xffff,0xffff, 0xffff,0xffff, }; +struct Image MsgImage1 = { 0,0, 7,102, 0, NULL, 0x0000,0x0000, NULL }; +struct Gadget MsgScroll = { + NULL, -15,10, 14,-19, GRELRIGHT|GRELHEIGHT, + RELVERIFY|FOLLOWMOUSE|RIGHTBORDER|GADGIMMEDIATE|RELVERIFY, + PROPGADGET, (APTR)&MsgImage1, NULL, NULL, NULL, (APTR)&MsgPropScroll, + 1, NULL +}; + +int wincnt=0; /* # of nh windows opened */ + +/* We advertise a public screen to allow some people to do other things + * while they are playing... like compiling... + */ + +#ifdef INTUI_NEW_LOOK +extern struct Hook fillhook; +struct TagItem tags[] = +{ + { WA_BackFill, (ULONG)&fillhook }, + { WA_PubScreenName, (ULONG)"NetHack" }, + { TAG_DONE, 0 }, +}; +#endif + +/* + * The default dimensions and status values for each window type. The + * data here is generally changed in create_nhwindow(), so beware that + * what you see here may not be exactly what you get. + */ +struct win_setup new_wins[] = +{ + + /* First entry not used, types are based at 1 */ + {{0}}, + + /* NHW_MESSAGE */ + {{0,1,640,11, + 0xff,0xff, + NEWSIZE|GADGETUP|GADGETDOWN|MOUSEMOVE|MOUSEBUTTONS|RAWKEY, + BORDERLESS|ACTIVATE|SMART_REFRESH +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + NULL,NULL,(UBYTE*)"Messages",NULL,NULL,320,40,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,1,1,80,80}, + + /* NHW_STATUS */ + {{0,181,640,24, + 0xff,0xff, + RAWKEY|MENUPICK|DISKINSERTED, + BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + NULL,NULL,(UBYTE*)"Game Status",NULL,NULL,0,0,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,2,2,78,78}, + + /* NHW_MAP */ + {{0,0,WIDTH,WINDOWHEIGHT, + 0xff,0xff, + RAWKEY|MENUPICK|MOUSEBUTTONS|ACTIVEWINDOW|MOUSEMOVE, + BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + NULL,NULL,(UBYTE*)"Dungeon Map",NULL,NULL,64,64,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,22,22,80,80}, + + /* NHW_MENU */ + {{400,10,10,10, + 0xff,0xff, + RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE|MOUSEBUTTONS| + GADGETUP|GADGETDOWN|CLOSEWINDOW|VANILLAKEY|NEWSIZE|INACTIVEWINDOW, + WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + &MenuScroll,NULL,NULL,NULL,NULL,64,32,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,1,1,22,78}, + + /* NHW_TEXT */ + {{0,0,640,200, + 0xff,0xff, + RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE| + GADGETUP|CLOSEWINDOW|VANILLAKEY|NEWSIZE, + WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + &MenuScroll,NULL,(UBYTE*)NULL,NULL,NULL,100,32,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,1,1,22,78}, + + /* NHW_BASE */ + {{0,0,WIDTH,WINDOWHEIGHT, + 0xff,0xff, + RAWKEY|MENUPICK|MOUSEBUTTONS, + BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + NULL,NULL,(UBYTE*)NULL,NULL,NULL,-1,-1,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,22,22,80,80}, + + /* NHW_OVER */ + {{320,20,319,179, + 0xff,0xff, + RAWKEY|MENUPICK|MOUSEBUTTONS, + BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP +#ifdef INTUI_NEW_LOOK + |WFLG_NW_EXTENDED +#endif + , + NULL,NULL,(UBYTE*)NULL,NULL,NULL,64,32,0xffff,0xffff, +#ifdef INTUI_NEW_LOOK + PUBLICSCREEN,tags +#else + CUSTOMSCREEN +#endif + }, + 0,0,22,22,80,80}, +}; + +const char winpanicstr[] = "Bad winid %d in %s()"; + +/* The opened windows information */ +struct amii_WinDesc *amii_wins[ MAXWIN + 1 ]; + +#ifdef INTUI_NEW_LOOK +/* + * NUMDRIPENS varies based on headers, so don't use it + * here, its value is used elsewhere. + */ +UWORD amii_defpens[ 20 ]; + +struct TagItem scrntags[] = +{ + { SA_PubName, (ULONG)"NetHack" }, + { SA_Overscan, OSCAN_TEXT }, + { SA_AutoScroll, TRUE }, +#if LIBRARY_VERSION >= 39 + { SA_Interleaved, TRUE }, +#endif + { SA_Pens, (ULONG)0 }, + { SA_DisplayID, 0 }, + { TAG_DONE, 0 }, +}; + +#endif + +struct NewScreen NewHackScreen = +{ + 0, 0, WIDTH, SCREENHEIGHT, 3, + 0, 1, /* DetailPen, BlockPen */ + HIRES, + CUSTOMSCREEN +#ifdef INTUI_NEW_LOOK + |NS_EXTENDED +#endif + , + &Hack80, /* Font */ + NULL, /*(UBYTE *)" NetHack X.Y.Z" */ + NULL, /* Gadgets */ + NULL, /* CustomBitmap */ +#ifdef INTUI_NEW_LOOK + scrntags +#endif +}; + +/* + * plname is filled either by an option (-u Player or -uPlayer) or + * explicitly (by being the wizard) or by askname. + * It may still contain a suffix denoting pl_character. + * Always called after init_nhwindows() and before display_gamewindows(). + */ +void +amii_askname() +{ + char plnametmp[300]; /* From winreq.c: sizeof(StrStringSIBuff) */ + *plnametmp = 0; + do { + amii_getlin( "Who are you?", plnametmp ); + } while( strlen( plnametmp ) == 0 ); + + strncpy(plname, plnametmp, PL_NSIZ-1); /* Avoid overflowing plname[] */ + plname[PL_NSIZ-1] = 0; + + if( *plname == '\33' ) + { + clearlocks(); + exit_nhwindows(NULL); + terminate(0); + } +} + +/* Discarded ... -jhsa +#include "NH:sys/amiga/char.c" +*/ + +/* Get the player selection character */ + +#if 0 /* New function at the bottom */ +void +amii_player_selection() +{ + register struct Window *cwin; + register struct IntuiMessage *imsg; + register int aredone = 0; + register struct Gadget *gd; + static int once = 0; + long class, code; + + amii_clear_nhwindow( WIN_BASE ); + if (validrole(flags.initrole)) + return; + else { + flags.initrole=randrole(); + return; + } +#if 0 /* Don't query the user ... instead give random character -jhsa */ + +#if 0 /* OBSOLETE */ + if( *pl_character ){ + pl_character[ 0 ] = toupper( pl_character[ 0 ] ); + if( index( pl_classes, pl_character[ 0 ] ) ) + return; + } +#endif + + if( !once ){ + if( bigscreen ){ + Type_NewWindowStructure1.TopEdge = + (HackScreen->Height/2) - (Type_NewWindowStructure1.Height/2); + } + for( gd = Type_NewWindowStructure1.FirstGadget; gd; + gd = gd->NextGadget ) + { + if( gd->GadgetID != 0 ) + SetBorder( gd ); + } + once = 1; + } + + if( WINVERS_AMIV ) + { +# ifdef INTUI_NEW_LOOK + Type_NewWindowStructure1.Extension = wintags; + Type_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; + fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Data = (void *)-2; + fillhook.h_SubEntry = 0; +#endif + } + + Type_NewWindowStructure1.Screen = HackScreen; + if( ( cwin = OpenShWindow( (void *)&Type_NewWindowStructure1 ) ) == NULL ) + { + return; + } +#if 0 + WindowToFront( cwin ); +#endif + + while( !aredone ) + { + WaitPort( cwin->UserPort ); + while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL ) + { + class = imsg->Class; + code = imsg->Code; + gd = (struct Gadget *)imsg->IAddress; + ReplyMsg( (struct Message *)imsg ); + + switch( class ) + { + case VANILLAKEY: + if( index( pl_classes, toupper( code ) ) ) + { + pl_character[0] = toupper( code ); + aredone = 1; + } + else if( code == ' ' || code == '\n' || code == '\r' ) + { + flags.initrole = randrole(); +#if 0 /* OBSOLETE */ +#ifdef TOURIST + strcpy( pl_character, roles[ rnd( 11 ) ] ); +#else + strcpy( pl_character, roles[ rnd( 10 ) ] ); +#endif +#endif + aredone = 1; + amii_clear_nhwindow( WIN_BASE ); + CloseShWindow( cwin ); + RandomWindow( pl_character ); + return; + } + else if( code == 'q' || code == 'Q' ) + { + CloseShWindow( cwin ); + clearlocks(); + exit_nhwindows(NULL); + terminate(0); + } + else + DisplayBeep( NULL ); + break; + + case GADGETUP: + switch( gd->GadgetID ) + { + case 1: /* Random Character */ + flags.initrole = randrole(); +#if 0 /* OBSOLETE */ +#ifdef TOURIST + strcpy( pl_character, roles[ rnd( 11 ) ] ); +#else + strcpy( pl_character, roles[ rnd( 10 ) ] ); +#endif +#endif + amii_clear_nhwindow( WIN_BASE ); + CloseShWindow( cwin ); + RandomWindow( pl_character ); + return; + + default: + pl_character[0] = gd->GadgetID; + break; + } + aredone = 1; + break; + + case CLOSEWINDOW: + CloseShWindow( cwin ); + clearlocks(); + exit_nhwindows(NULL); + terminate(0); + break; + } + } + } + amii_clear_nhwindow( WIN_BASE ); + CloseShWindow( cwin ); +#endif /* Do not query user ... -jhsa */ +} +#endif /* Function elsewhere */ + +#if 0 /* Unused ... -jhsa */ + +#include "NH:sys/amiga/randwin.c" + +void +RandomWindow( name ) + char *name; +{ + struct MsgPort *tport; + struct timerequest *trq; + static int once = 0; + struct Gadget *gd; + struct Window *w; + struct IntuiMessage *imsg; + int ticks = 0, aredone = 0, timerdone = 0; + long mask, got; + + tport = CreateMsgPort(); + trq = (struct timerequest *)CreateIORequest( tport, sizeof( *trq ) ); + if( tport == NULL || trq == NULL ) + { +allocerr: + if( tport ) DeleteMsgPort( tport ); + if( trq ) DeleteIORequest( (struct IORequest *)trq ); + Delay( 8 * 50 ); + return; + } + + if( OpenDevice( TIMERNAME, UNIT_VBLANK, (struct IORequest *)trq, 0L ) != 0 ) + goto allocerr; + + trq->tr_node.io_Command = TR_ADDREQUEST; + trq->tr_time.tv_secs = 8; + trq->tr_time.tv_micro = 0; + + SendIO( (struct IORequest *)trq ); + + /* Place the name in the center of the screen */ + Rnd_IText5.IText = name; + Rnd_IText6.LeftEdge = Rnd_IText4.LeftEdge + + (strlen(Rnd_IText4.IText)+1)*8; + Rnd_NewWindowStructure1.Width = ( + (strlen( Rnd_IText2.IText )+1) * 8 ) + + HackScreen->WBorLeft + HackScreen->WBorRight; + Rnd_IText5.LeftEdge = (Rnd_NewWindowStructure1.Width - + (strlen(name)*8))/2; + + gd = Rnd_NewWindowStructure1.FirstGadget; + gd->LeftEdge = (Rnd_NewWindowStructure1.Width - gd->Width)/2; + /* Chose correct modifier */ + Rnd_IText6.IText = "a"; + switch( *name ) + { + case 'a': case 'e': case 'i': case 'o': + case 'u': case 'A': case 'E': case 'I': + case 'O': case 'U': + Rnd_IText6.IText = "an"; + break; + } + + if( !once ) + { + if( bigscreen ) + { + Rnd_NewWindowStructure1.TopEdge = + (HackScreen->Height/2) - (Rnd_NewWindowStructure1.Height/2); + } + for( gd = Rnd_NewWindowStructure1.FirstGadget; gd; gd = gd->NextGadget ) + { + if( gd->GadgetID != 0 ) + SetBorder( gd ); + } + Rnd_NewWindowStructure1.IDCMPFlags |= VANILLAKEY; + + once = 1; + } + + if( WINVERS_AMIV ) + { +#ifdef INTUI_NEW_LOOK + Rnd_NewWindowStructure1.Extension = wintags; + Rnd_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; + fillhook.h_Entry = (ULONG(*)())LayerFillHook; + fillhook.h_Data = (void *)-2; + fillhook.h_SubEntry = 0; +#endif + } + + Rnd_NewWindowStructure1.Screen = HackScreen; + if( ( w = OpenShWindow( (void *)&Rnd_NewWindowStructure1 ) ) == NULL ) + { + AbortIO( (struct IORequest *)trq ); + WaitIO( (struct IORequest *)trq ); + CloseDevice( (struct IORequest *)trq ); + DeleteIORequest( (struct IORequest *) trq ); + DeleteMsgPort( tport ); + Delay( 50 * 8 ); + return; + } + + PrintIText( w->RPort, &Rnd_IntuiTextList1, 0, 0 ); + + mask = (1L << tport->mp_SigBit)|(1L << w->UserPort->mp_SigBit); + while( !aredone ) + { + got = Wait( mask ); + if( got & (1L << tport->mp_SigBit ) ) + { + aredone = 1; + timerdone = 1; + GetMsg( tport ); + } + while( w && ( imsg = (struct IntuiMessage *) GetMsg( w->UserPort ) ) ) + { + switch( (long)imsg->Class ) + { + /* Must be up for a little while... */ + case INACTIVEWINDOW: + if( ticks >= 40 ) + aredone = 1; + break; + + case INTUITICKS: + ++ticks; + break; + + case GADGETUP: + aredone = 1; + break; + + case VANILLAKEY: + if(imsg->Code=='\n' || imsg->Code==' ' || imsg->Code=='\r') + aredone = 1; + break; + } + ReplyMsg( (struct Message *)imsg ); + } + } + + if( !timerdone ) + { + AbortIO( (struct IORequest *)trq ); + WaitIO( (struct IORequest *)trq ); + } + + CloseDevice( (struct IORequest *)trq ); + DeleteIORequest( (struct IORequest *) trq ); + DeleteMsgPort( tport ); + if(w) CloseShWindow( w ); +} +#endif /* Discarded randwin ... -jhsa */ + +/* this should probably not be needed (or be renamed) +void +flush_output(){} */ + +/* Read in an extended command - doing command line completion for + * when enough characters have been entered to make a unique command. + */ +int +amii_get_ext_cmd( void ) +{ + menu_item *mip; + anything id; + struct amii_WinDesc *cw; +#ifdef EXTMENU + winid win; + int i; + char buf[256]; +#endif + int colx; + int bottom = 0; + struct Window *w; + char obufp[ 100 ]; + register char *bufp = obufp; + register int c; + int com_index, oindex; + int did_comp=0; /* did successful completion? */ + int sel = -1; + + if( WIN_MESSAGE == WIN_ERR || ( cw = amii_wins[ WIN_MESSAGE ] ) == NULL ) + panic(winpanicstr, WIN_MESSAGE, "get_ext_cmd"); + w = cw->win; + bottom = amii_msgborder( w ); + colx = 3; + +#ifdef EXTMENU + if (iflags.extmenu) { + win = amii_create_nhwindow( NHW_MENU ); + amii_start_menu( win ); + pline("#"); + amii_putstr( WIN_MESSAGE, -1, " " ); + + for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i ) + { + id.a_char = *extcmdlist[ i ].ef_txt; + sprintf( buf, "%-10s - %s ", + extcmdlist[ i ].ef_txt, + extcmdlist[ i ].ef_desc ); + amii_add_menu( win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, 0, buf, MENU_UNSELECTED); + } + + amii_end_menu( win, (char*)0 ); + sel = amii_select_menu( win, PICK_ONE, &mip ); + amii_destroy_nhwindow( win ); + + if( sel == 1 ) + { + sel = mip->item.a_char; + for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i ) + { + if( sel == extcmdlist[i].ef_txt[0] ) + break; + } + + /* copy in the text */ + if( extcmdlist[ i ].ef_txt != NULL ) + { + amii_clear_nhwindow( WIN_MESSAGE ); + (void) put_ext_cmd( (char *)extcmdlist[i].ef_txt, 0, cw, bottom ); + return( i ); + } + else + DisplayBeep( NULL ); + } + + return( -1 ); + } else { +#else + + amii_clear_nhwindow( WIN_MESSAGE ); /* Was NHW_MESSAGE */ + if( scrollmsg ) + { + pline("#"); + amii_addtopl(" "); + } + else + { + pline("# "); + } + + sel = -1; + while((c = WindowGetchar()) != EOF) + { + amii_curs( WIN_MESSAGE, colx, bottom ); + if(c == '?' ) + { + int win, i; + char buf[ 100 ]; + + if(did_comp){ + while(bufp!=obufp) + { + bufp--; + amii_curs(WIN_MESSAGE, --colx, bottom); + Text(w->RPort,spaces,1); + amii_curs(WIN_MESSAGE,colx,bottom); + did_comp=0; + } + } + + win = amii_create_nhwindow( NHW_MENU ); + amii_start_menu( win ); + + for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i ) + { + id.a_char = extcmdlist[i].ef_txt[0]; + sprintf( buf, "%-10s - %s ", + extcmdlist[ i ].ef_txt, + extcmdlist[ i ].ef_desc ); + amii_add_menu( win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, + 0, buf, MENU_UNSELECTED); + } + + amii_end_menu( win, (char*)0 ); + sel = amii_select_menu( win, PICK_ONE, &mip ); + amii_destroy_nhwindow( win ); + + if( sel == 0 ) + { + return( -1 ); + } + else + { + sel = mip->item.a_char; + for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i ) + { + if( sel == extcmdlist[i].ef_txt[0] ) + break; + } + + /* copy in the text */ + if( extcmdlist[ i ].ef_txt != NULL ) + { + amii_clear_nhwindow( WIN_MESSAGE ); + strcpy( bufp = obufp, extcmdlist[ i ].ef_txt ); + (void) put_ext_cmd( obufp, colx, cw, bottom ); + return( i ); + } + else + DisplayBeep( NULL ); + } + } + else if(c == '\033') + { + return( -1 ); + } + else if(c == '\b') + { + if(did_comp){ + while(bufp!=obufp){ + bufp--; + amii_curs(WIN_MESSAGE, --colx, bottom); + Text(w->RPort,spaces,1); + amii_curs(WIN_MESSAGE,colx,bottom); + did_comp=0; + sel = -1; + } + } + else if(bufp != obufp) + { + sel = -1; + bufp--; + amii_curs( WIN_MESSAGE, --colx, bottom); + Text( w->RPort, spaces, 1 ); + amii_curs( WIN_MESSAGE, colx, bottom); + } + else + DisplayBeep( NULL ); + } + else if( c == '\n' || c == '\r' ) + { + return(sel); + } + else if( c >= ' ' && c < '\177') + { + /* avoid isprint() - some people don't have it + ' ' is not always a printing char */ + *bufp = c; + bufp[1] = 0; + oindex = 0; + com_index = -1; + + while(extcmdlist[oindex].ef_txt != NULL) + { + if(!strnicmp(obufp, (char *)extcmdlist[oindex].ef_txt, strlen(obufp))) + { + if(com_index == -1) /* No matches yet*/ + com_index = oindex; + else /* More than 1 match */ + com_index = -2; + } + oindex++; + } + + if(com_index >= 0 && *obufp ) + { + Strcpy(obufp, extcmdlist[com_index].ef_txt); + /* finish printing our string */ + colx = put_ext_cmd( obufp, colx, cw, bottom ); + bufp = obufp; /* reset it */ + if(strlen(obufp) < BUFSZ-1 && strlen(obufp) < COLNO) + bufp += strlen(obufp); + did_comp=1; + sel = com_index; + } + else + { + colx = put_ext_cmd( obufp, colx, cw, bottom ); + if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO) + bufp++; + } + } + else if(c == ('X'-64) || c == '\177') + { + colx = 0; + amii_clear_nhwindow( WIN_MESSAGE ); + pline( "# " ); + bufp = obufp; + } else + DisplayBeep( NULL ); + } + return(-1); +#endif +} + +static int +put_ext_cmd( obufp, colx, cw, bottom ) + char *obufp; + int colx, bottom; + struct amii_WinDesc *cw; +{ + struct Window *w = cw->win; + char *t; + + t = (char *)alloc( strlen( obufp ) + 7 ); + if( t != NULL ) + { + if( scrollmsg ) + { + sprintf( t, "xxx%s", obufp ); + t[0] = 1; + t[1] = 1; + t[2] = '#'; + amii_curs( WIN_MESSAGE, 0, bottom); + SetAPen( w->RPort, C_WHITE ); + Text(w->RPort, "># ", 3 ); + /* SetAPen( w->RPort, C_BLACK ); */ /* Black text on black screen doesn't look too well ... -jhsa */ + Text(w->RPort, t+3, strlen( t ) - 3 ); + } + else + { + sprintf( t, "# %s", obufp ); + amii_curs( WIN_MESSAGE, 0, bottom); + SetAPen( w->RPort, C_WHITE ); + Text(w->RPort, t, strlen( t ) ); + } + if( scrollmsg ) + SetAPen( w->RPort, C_WHITE ); + if( cw->data[ cw->maxrow - 1 ] ) + free( cw->data[ cw->maxrow - 1 ] ); + cw->data[ cw->maxrow - 1 ] = t; + } + else + { + amii_curs( WIN_MESSAGE, 0, bottom); + SetAPen( w->RPort, C_WHITE ); + Text(w->RPort, "# ", 2 ); + /* SetAPen( w->RPort, C_BLACK ); */ /* Black on black ... -jhsa */ + Text(w->RPort, obufp, strlen( obufp ) ); + SetAPen( w->RPort, C_WHITE ); + } + amii_curs( WIN_MESSAGE, colx = strlen( obufp ) + 3 + ( scrollmsg != 0 ), bottom); + return( colx ); +} + +/* Ask a question and get a response */ + +char amii_yn_function(query, resp, def) +const char *query,*resp; +char def; +/* + * Generic yes/no function. 'def' is the default (returned by space or + * return; 'esc' returns 'q', or 'n', or the default, depending on + * what's in the string. The 'query' string is printed before the user + * is asked about the string. + * If resp is NULL, any single character is accepted and returned. + * If not-NULL, only characters in it are allowed (exceptions: the + * quitchars are always allowed, and if it contains '#' then digits + * are allowed); if it includes an , anything beyond that won't + * be shown in the prompt to the user but will be acceptable as input. + */ +{ + register char q; + char rtmp[40]; + boolean digit_ok, allow_num; + char prompt[QBUFSZ]; + register struct amii_WinDesc *cw; + + if( cw = amii_wins[ WIN_MESSAGE ] ) + cw->disprows = 0; + if (resp) { + char *rb, respbuf[QBUFSZ]; + + allow_num = (index(resp, '#') != 0); + Strcpy(respbuf, resp); + /* any acceptable responses that follow aren't displayed */ + if ((rb = index(respbuf, '\033')) != 0) *rb = '\0'; + Sprintf(prompt, "%s [%s] ", query, respbuf); + if (def) Sprintf(eos(prompt), "(%c) ", def); + pline("%s", prompt); + } else { + amii_putstr(WIN_MESSAGE, 0, query); + cursor_on(WIN_MESSAGE); + q = WindowGetchar(); + cursor_off(WIN_MESSAGE); + *rtmp = q; + rtmp[ 1 ] = 0; + amii_addtopl(rtmp); + goto clean_up; + } + + do { /* loop until we get valid input */ + cursor_on(WIN_MESSAGE); + q = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); +#if 0 +/* fix for PL2 */ + if (q == '\020') { /* ctrl-P */ + if(!doprev) (void) tty_doprev_message(); /* need two initially */ + (void) tty_doprev_message(); + q = (char)0; + doprev = 1; + continue; + } else if(doprev) { + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + doprev = 0; + amii_addtopl(prompt); + continue; + } +#endif + digit_ok = allow_num && isdigit(q); + if (q == '\033') { + if (index(resp, 'q')) + q = 'q'; + else if (index(resp, 'n')) + q = 'n'; + else + q = def; + break; + } else if (index(quitchars, q)) { + q = def; + break; + } + if (!index(resp, q) && !digit_ok) { + amii_bell(); + q = (char)0; + } else if (q == '#' || digit_ok) { + char z, digit_string[2]; + int n_len = 0; + long value = 0; + amii_addtopl("#"), n_len++; + digit_string[1] = '\0'; + if (q != '#') { + digit_string[0] = q; + amii_addtopl(digit_string), n_len++; + value = q - '0'; + q = '#'; + } + do { /* loop until we get a non-digit */ + cursor_on(WIN_MESSAGE); + z = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); + if (isdigit(z)) { + value = (10 * value) + (z - '0'); + if (value < 0) break; /* overflow: try again */ + digit_string[0] = z; + amii_addtopl(digit_string), n_len++; + } else if (z == 'y' || index(quitchars, z)) { + if (z == '\033') value = -1; /* abort */ + z = '\n'; /* break */ + } else if ( z == '\b') { + if (n_len <= 1) { value = -1; break; } + else { value /= 10; removetopl(1), n_len--; } + } else { + value = -1; /* abort */ + amii_bell(); + break; + } + } while (z != '\n'); + if (value > 0) yn_number = value; + else if (value == 0) q = 'n'; /* 0 => "no" */ + else { /* remove number from top line, then try again */ + removetopl(n_len), n_len = 0; + q = '\0'; + } + } + } while(!q); + + if (q != '#' && q != '\033') { + Sprintf(rtmp, "%c", q); + amii_addtopl(rtmp); + } + clean_up: + cursor_off(WIN_MESSAGE); + clear_nhwindow(WIN_MESSAGE); + return q; +} + +void +amii_display_file(fn, complain) +const char *fn; +boolean complain; +{ + register struct amii_WinDesc *cw; + register int win; + register dlb *fp; + register char *t; + register char buf[ 200 ]; + + if( fn == NULL ) + panic("NULL file name in display_file()"); + + if( ( fp = dlb_fopen( fn, RDTMODE ) ) == (dlb *)NULL ) + { + if (complain) { + sprintf( buf, "Can't display %s: %s", fn, +#if defined(_DCC) || defined(__GNUC__) + strerror(errno) +#else +# ifdef __SASC_60 + __sys_errlist[ errno ] +# else + sys_errlist[ errno ] +# endif +#endif + ); + amii_addtopl( buf ); + } + return; + } + win = amii_create_nhwindow( NHW_TEXT ); + + /* Set window title to file name */ + if( cw = amii_wins[ win ] ) + cw->morestr = (char *)fn; + + while( dlb_fgets( buf, sizeof( buf ), fp ) != NULL ) + { + if( t = index( buf, '\n' ) ) + *t = 0; + amii_putstr( win, 0, buf ); + } + dlb_fclose( fp ); + + /* If there were lines in the file, display those lines */ + + if( amii_wins[ win ]->cury > 0 ) + amii_display_nhwindow( win, TRUE ); + + amii_wins[win]->morestr = NULL; /* don't free title string */ + amii_destroy_nhwindow( win ); +} + +/* Put a 3-D motif border around the gadget. String gadgets or those + * which do not have highlighting are rendered down. Boolean gadgets + * are rendered in the up position by default. + */ + +void +SetBorder( gd ) + register struct Gadget *gd; +{ + register struct Border *bp; + register short *sp; + register int i, inc = -1, dec = -1; + int borders = 6; + int hipen = flags.amii_dripens[ SHINEPEN ], shadowpen = flags.amii_dripens[ SHADOWPEN ]; +#ifdef INTUI_NEW_LOOK + struct DrawInfo *dip; +#endif + +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + if( dip = GetScreenDrawInfo( HackScreen ) ) + { + hipen = dip->dri_Pens[ SHINEPEN ]; + shadowpen = dip->dri_Pens[ SHADOWPEN ]; + FreeScreenDrawInfo( HackScreen, dip ); + } + } +#endif + /* Allocate two border structures one for up image and one for down + * image, plus vector arrays for the border lines. + */ + + if( gd->GadgetType == STRGADGET ) + borders = 12; + + if( ( bp = (struct Border *)alloc( ( ( sizeof( struct Border ) * 2 ) + + ( sizeof( short ) * borders ) ) * 2 ) ) == NULL ) + { + return; + } + + /* For a string gadget, we expand the border beyond the area where + * the text will be entered. + */ + + /* Remove any special rendering flags to avoid confusing intuition + */ + + gd->Flags &= ~(GADGHIGHBITS|GADGIMAGE); + + sp = (short *)(bp + 4); + if( gd->GadgetType == STRGADGET || ( gd->GadgetType == BOOLGADGET && + ( gd->Flags & GADGHIGHBITS ) == GADGHNONE ) ) + { + sp[0] = -1; + sp[1] = gd->Height - 1; + sp[2] = -1; + sp[3] = -1; + sp[4] = gd->Width - 1; + sp[5] = -1; + + sp[6] = gd->Width + 1; + sp[7] = -2; + sp[8] = gd->Width + 1; + sp[9] = gd->Height + 1; + sp[10] = -2; + sp[11] = gd->Height + 1; + + sp[12] = -2; + sp[13] = gd->Height; + sp[14] = -2; + sp[15] = -2; + sp[16] = gd->Width; + sp[17] = -2; + sp[18] = gd->Width; + sp[19] = gd->Height; + sp[20] = -2; + sp[21] = gd->Height; + + for( i = 0; i < 3; ++i ) + { + bp[ i ].LeftEdge = bp[ i ].TopEdge = -1; + bp[ i ].FrontPen = ( i == 0 || i == 1 ) ? shadowpen : hipen; + + /* Have to use JAM2 so that the old colors disappear. */ + bp[ i ].BackPen = C_BLACK; + bp[ i ].DrawMode = JAM2; + bp[ i ].Count = ( i == 0 || i == 1 ) ? 3 : 5; + bp[ i ].XY = &sp[ i*6 ]; + bp[ i ].NextBorder = ( i == 2 ) ? NULL : &bp[ i + 1 ]; + } + + /* bp[0] and bp[1] two pieces for the up image */ + gd->GadgetRender = (APTR) bp; + + /* No image change for select */ + gd->SelectRender = (APTR) bp; + + gd->LeftEdge++; + gd->TopEdge++; + gd->Flags |= GADGHCOMP; + } + else + { + /* Create the border vector values for up and left side, and + * also the lower and right side. + */ + + sp[0] = dec; + sp[1] = gd->Height + inc; + sp[2] = dec; + sp[3] = dec; + sp[4] = gd->Width + inc; + sp[5] = dec; + + sp[6] = gd->Width + inc; + sp[7] = dec; + sp[8] = gd->Width + inc; + sp[9] = gd->Height + inc; + sp[10] = dec; + sp[11] = gd->Height + inc; + + /* We are creating 4 sets of borders, the two sides of the + * rectangle share the border vectors with the opposite image, + * but specify different colors. + */ + + for( i = 0; i < 4; ++i ) + { + bp[ i ].TopEdge = bp[ i ].LeftEdge = 0; + + /* A GADGHNONE is always down */ + + if( gd->GadgetType == BOOLGADGET && + ( gd->Flags & GADGHIGHBITS ) != GADGHNONE ) + { + bp[ i ].FrontPen = + ( i == 1 || i == 2 ) ? shadowpen : hipen; + } + else + { + bp[ i ].FrontPen = + ( i == 1 || i == 3 ) ? hipen : shadowpen; + } + + /* Have to use JAM2 so that the old colors disappear. */ + bp[ i ].BackPen = C_BLACK; + bp[ i ].DrawMode = JAM2; + bp[ i ].Count = 3; + bp[ i ].XY = &sp[ 6 * ((i &1) != 0) ]; + bp[ i ].NextBorder = + ( i == 1 || i == 3 ) ? NULL : &bp[ i + 1 ]; + } + + /* bp[0] and bp[1] two pieces for the up image */ + gd->GadgetRender = (APTR) bp; + + /* bp[2] and bp[3] two pieces for the down image */ + gd->SelectRender = (APTR) (bp + 2); + gd->Flags |= GADGHIMAGE; + } +} + +/* Following function copied from wintty.c */ +/* Modified slightly to fit amiga needs */ + +void +amii_player_selection() +{ + int i, k, n; + char pick4u = 'n', thisch, lastch = 0; + char pbuf[QBUFSZ], plbuf[QBUFSZ], rolenamebuf[QBUFSZ]; + winid win; + anything any; + menu_item *selected = 0; + + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || + flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) { + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole, + flags.initrace, flags.initgend, flags.initalign); + pline("%s", prompt); + do { /* loop until we get valid input */ + cursor_on(WIN_MESSAGE); + pick4u = lowc(WindowGetchar()); + cursor_off(WIN_MESSAGE); + if (index(quitchars, pick4u)) pick4u = 'y'; + } while(!index(ynqchars, pick4u)); + pbuf[0] = pick4u; + pbuf[1] = 0; + amii_addtopl(pbuf); + + if (pick4u != 'y' && pick4u != 'n') { +give_up: /* Quit */ + if (selected) free((genericptr_t) selected); + clearlocks(); + exit_nhwindows(NULL); + terminate(0); + /*NOTREACHED*/ + return; + } + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + /* Select a role, if necessary */ + /* we'll try to be compatible with pre-selected race/gender/alignment, + * but may not succeed */ + if (flags.initrole < 0) { + /* Process the choice */ + if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible role!"); + flags.initrole = randrole(); + } + } else { + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + thisch = lowc(roles[i].name.m[0]); + if (thisch == lastch) thisch = highc(thisch); + if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) { + if (flags.initgend == 1 && roles[i].name.f) + Strcpy(rolenamebuf, roles[i].name.f); + else + Strcpy(rolenamebuf, roles[i].name.m); + } else { + if (roles[i].name.f) { + Strcpy(rolenamebuf, roles[i].name.m); + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } else + Strcpy(rolenamebuf, roles[i].name.m); + } + add_menu(win, NO_GLYPH, &any, thisch, + 0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED); + lastch = thisch; + } + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole()+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick a role for your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible race!"); + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, races[i].noun[0], + 0, ATR_NONE, races[i].noun, MENU_UNSELECTED); + } + any.a_int = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrace(flags.initrole)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the race of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initrace = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible gender!"); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], + 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + amii_putstr(WIN_MESSAGE, 0, "Incompatible alignment!"); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, + i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], + 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initalign = k; + } + } + /* Success! */ +} +#endif /* AMIGA_INTUITION */ diff --git a/sys/amiga/winami.p b/sys/amiga/winami.p new file mode 100644 index 0000000..b34f8e5 --- /dev/null +++ b/sys/amiga/winami.p @@ -0,0 +1,57 @@ +/* SCCS Id: @(#)winami.p 3.1 93/01/08 */ +/* Copyright (c) Gregg Wonderly, Naperville, IL, 1992, 1993 */ +/* NetHack may be freely redistributed. See license for details. */ +/* winami.c */ +void FDECL(amii_raw_print, (const char *)); +void FDECL(amii_raw_print_bold, (const char *)); +void FDECL(amii_start_menu, (winid )); +void FDECL(amii_add_menu, (winid , char , int , const char *)); +void FDECL(amii_end_menu, (winid , char , const char * , const char *)); +char FDECL(amii_select_menu, (winid )); +void NDECL(amii_update_inventory ); +void NDECL(amii_mark_synch ); +void NDECL(amii_wait_synch ); +void NDECL(amii_setclipped ); +void FDECL(amii_cliparound, (int , int )); +void NDECL(amii_askname ); +void NDECL(amii_player_selection ); +void NDECL(flush_output ); +void FDECL(amii_destroy_nhwindow, (winid )); +int FDECL(amii_create_nhwindow, (int )); +void NDECL(amii_init_nhwindows ); +int NDECL(amii_get_ext_cmd); +char FDECL(amii_yn_function, (const char * , const char * , char )); +void FDECL(amii_addtopl, (const char *)); +void FDECL(TextSpaces, (struct RastPort * , int )); +void FDECL(amii_putstr, (winid , int , const char *)); +void FDECL(amii_putsym, (winid , int , int , CHAR_P )); +void FDECL(amii_clear_nhwindow, (winid )); +void FDECL(amii_exit_nhwindows, (const char *)); +int FDECL(amii_nh_poskey, (int * , int * , int *)); +int NDECL(amii_nhgetch ); +void NDECL(amii_get_nh_event ); +void NDECL(amii_remember_topl ); +int NDECL(amii_doprev_message ); +void FDECL(amii_display_nhwindow, (winid , boolean )); +void FDECL(amii_display_file, (const char * , boolean )); +void FDECL(amii_curs, (winid , int , int )); +void FDECL(amii_print_glyph, (winid , xchar , xchar , int )); +void FDECL(DoMenuScroll, (int , int )); +void FDECL(DisplayData, (int , int , int )); +void FDECL(SetPropInfo, (struct Window * , struct Gadget * , long , long , long )); +void FDECL(kill_nhwindows, (int )); +void FDECL(amii_cl_end, (struct amii_WinDesc * , int )); +void FDECL(cursor_off, (winid )); +void FDECL(cursor_on, (winid )); +void NDECL(amii_getret ); +void FDECL(amii_getlin, (const char * , char *)); +void FDECL(getlind, (const char * , char * , const char *)); +void FDECL(amii_suspend_nhwindows, (char * )); +void NDECL(amii_resume_nhwindows); +void NDECL(amii_bell); +void NDECL(EditColor); +void FDECL(DrawCol, ( struct Window *, int, UWORD * ) ); +void FDECL( DispCol, ( struct Window *w, int idx, UWORD * ) ); +void FDECL( SetBorder, (struct Gadget *) ); +void NDECL( port_help ); +void FDECL( dismiss_nhwindow, (winid) ); diff --git a/sys/amiga/winchar.c b/sys/amiga/winchar.c new file mode 100644 index 0000000..2f6e721 --- /dev/null +++ b/sys/amiga/winchar.c @@ -0,0 +1,1246 @@ +/* SCCS Id: @(#)winchar.c 3.1 93/07/22 */ +/* Copyright (c) Olaf Seibert (KosmoSoft), 1989, 1992 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland 1993 */ +/* Copyright (c) Gregg Wonderly, Naperville Illinois, 1994. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include +#ifndef _DCC +#include +#endif + +#ifdef TESTING +# include "hack.h" +#else +# include "NH:src/tile.c" +#endif + +#include "NH:win/share/tile.h" + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +#ifdef OPT_DISPMAP +# define DISPMAP /* use display_map() from dispmap.s */ +#endif + +/* NH:sys/amiga/winvchar.c */ +int main ( int , char ** ); +struct BitMap *MyAllocBitMap ( int , int , int , long ); +void MyFreeBitMap ( struct BitMap * ); +void FreeImageFiles ( char **, struct BitMap ** ); +void amiv_flush_glyph_buffer ( struct Window * ); +void amiv_lprint_glyph ( winid , int , int ); +void amii_lprint_glyph ( winid , int , int ); +void amiv_start_glyphout ( winid ); +void amii_end_glyphout ( winid ); +void SetMazeType ( MazeType ); +int GlyphToIcon ( int ); +void amii_start_glyphout ( winid ); +void amii_end_glyphout ( winid ); +void amii_flush_glyph_buffer( struct Window * ); + +int amii_extraplanes = 0; +extern int reclip; + +struct BitMap *MyAllocBitMap( int xsize, int ysize, int depth, long mflags ); +void MyFreeBitMap( struct BitMap *bmp ); + +#ifdef DISPMAP +extern void display_map( struct Window * ); +#endif + +/* + * These values will be available from tile.c source + * + * #define MAXMONTILE 335 + * #define MAXOBJTILE 722 + * #define MAXOTHTILE 841 + */ + +#define IMGROWS 12 +#define IMGCOLUMNS 20 +#define IMGPAGESIZE (IMGROWS*IMGCOLUMNS) + +#define ID_BMAP MAKE_ID('B','M','A','P') /* The type of form we use */ +#define ID_BMHD MAKE_ID('B','M','H','D') /* The ILBM bitmap header */ +#define ID_CAMG MAKE_ID('C','A','M','G') /* The ILBM camg (ignored) */ +#define ID_CMAP MAKE_ID('C','M','A','P') /* Standard ILBM color map */ +#define ID_PLNE MAKE_ID('P','L','N','E') /* The plane data */ +#define ID_PDAT MAKE_ID('P','D','A','T') /* The PDAT structure below */ + +struct PDAT pictdata; + +#define NUMTILEIMAGES 3 +char *tileimages[] = +{ +#define TBLMONTILE 0 + "NetHack:tiles/monsters.iff", +#define TBLOBJTILE 1 + "NetHack:tiles/objects.iff", +#define TBLOTHTILE 2 + "NetHack:tiles/other.iff", + 0, +}; + +struct BitMap *ifftimg[ NUMTILEIMAGES ], *tile; + +#ifdef TESTING +short pens[NUMDRIPENS] = { 8, 3, 15, 0, 15, 7, 7, 8, 0 }; +main( int argc, char **argv ) +{ + BitMapHeader bmhd; + struct IntuiMessage *imsg; + long code, class; + char buf[100]; + int i, x, y, tbl, done = 0, num; + struct Window *w; + struct Screen *scr; + + bmhd = ReadTileImageFiles( ); + + scr = OpenScreenTags( NULL, + SA_Depth, pictdata.nplanes + amii_extraplanes, + SA_DisplayID, DBLNTSC_MONITOR_ID|HIRESLACE_KEY, + SA_Overscan, OSCAN_TEXT, + SA_Top, 0, + SA_Left, 0, + SA_Width, STDSCREENWIDTH, + SA_Height, STDSCREENHEIGHT, + SA_Type, CUSTOMSCREEN, + SA_DetailPen, 0, + SA_BlockPen, 1, + SA_Title, "NetHack Chars", + SA_Pens, pens, + TAG_DONE + ); + if( scr == NULL ) + { + printf( "no screen\n" ); +#undef exit + exit( 1 ); + } + + w = OpenWindowTags( 0, + WA_CustomScreen, scr, + WA_Flags, WFLG_DRAGBAR|WFLG_SIZEGADGET|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET, + WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|IDCMP_MOUSEBUTTONS, + WA_Left, 0, + WA_Top, scr->WBorTop + 1 + 13, + WA_MinWidth, 100, + WA_MinHeight, 100, + WA_MaxWidth, 700, + WA_MaxHeight, 1000, + WA_Width, 640, + WA_Height, 340, + WA_SmartRefresh, TRUE, + TAG_DONE ); + if( w ) + { + while( !done ) + { + for( i = 0; i < NUMTILEIMAGES * IMGPAGESIZE; ++i ) + { + int dx, dy; + tbl = i/IMGPAGESIZE; + x = i % IMGPAGESIZE; + y = x / IMGCOLUMNS; + x = x % IMGCOLUMNS; + dx = i % (IMGCOLUMNS*2); + dy = i / (IMGCOLUMNS*2); + BltBitMapRastPort( ifftimg[ tbl ], + x * pictdata.xsize, y * pictdata.ysize, + w->RPort, + w->BorderLeft + 1 + dx*pictdata.xsize, + w->BorderTop + 1 + dy*pictdata.ysize, + pictdata.xsize, pictdata.ysize, 0xc0 ); + } + WaitPort( w->UserPort ); + while( imsg = (struct IntuiMessage *)GetMsg( w->UserPort ) ) + { + class = imsg->Class; + code = imsg->Code; + ReplyMsg( (struct Message *)imsg ); + switch( class ) + { + case IDCMP_MOUSEBUTTONS: + { + x = imsg->MouseX - w->BorderLeft; + y = imsg->MouseY - w->BorderTop; + num = ((y/pictdata.ysize)*IMGCOLUMNS*2)+(x/pictdata.xsize); + sprintf( buf, "Char #%d", num ); + SetWindowTitles( w, buf, buf ); + } + break; + case IDCMP_CLOSEWINDOW: + done = 1; + break; + } + } + } + CloseWindow( w ); + CloseScreen( scr ); + } + + FreeImageFiles(tileimages, ifftimg ); + + return(0); +} +#endif + +BitMapHeader +ReadTileImageFiles(){ + char *errstr = NULL; + BitMapHeader ret = ReadImageFiles(tileimages, ifftimg, &errstr); + if(errstr){ + panic(errstr); + } + return ret; +} + +BitMapHeader +ReadImageFiles(char **filenames, struct BitMap **iffimg, char **errstrp ) +{ + BitMapHeader *bmhd = NULL, bmhds; + unsigned char *cmap; + extern int errno; + register int i, j; + struct IFFHandle *iff; + struct StoredProperty *prop; + + IFFParseBase = OpenLibrary( "iffparse.library", 0L ); + if( !IFFParseBase ) + { + *errstrp = "No iffparse.library"; + return bmhds; + } + +/* + for( i = 0; filenames[i]; ++i ) + memset( iffimg[i], 0, sizeof( struct BitMap ) ); +*/ + for( i = 0; filenames[i]; ++i ) + { + iff = AllocIFF(); + if( !iff ) + { + FreeImageFiles(filenames, iffimg ); + *errstrp = "can't start IFF processing"; + return bmhds; + } + iff->iff_Stream = Open( filenames[i], MODE_OLDFILE ); + if( iff->iff_Stream == 0 ) + { + char *buf = malloc(100+strlen(filenames[i])); + FreeImageFiles( filenames, iffimg ); + sprintf(buf, "Can't open %s: %s", filenames[i], strerror( errno )); + *errstrp = buf; + return bmhds; + } + InitIFFasDOS( iff ); + OpenIFF( iff, IFFF_READ ); + PropChunk( iff, ID_BMAP, ID_BMHD ); + PropChunk( iff, ID_BMAP, ID_CMAP ); + PropChunk( iff, ID_BMAP, ID_CAMG ); + PropChunk( iff, ID_BMAP, ID_PDAT ); + StopChunk( iff, ID_BMAP, ID_PLNE ); + if( ( j = ParseIFF( iff, IFFPARSE_SCAN ) ) != 0 ) + { + char *buf = malloc(100); + FreeImageFiles( filenames, iffimg ); + sprintf(buf,"ParseIFF failed for image %d, failure code: %d",i,j); + *errstrp = buf; + return bmhds; + } + + if( prop = FindProp( iff, ID_BMAP, ID_BMHD ) ) + { + bmhd = (BitMapHeader *)prop->sp_Data; + } + else + { + FreeImageFiles(filenames, iffimg); + CloseIFF( iff ); + Close( iff->iff_Stream ); + FreeIFF( iff ); + *errstrp = "No BMHD CHUNK in file"; + return bmhds; + } + + if( prop = FindProp( iff, ID_BMAP, ID_CMAP ) ) + { + cmap = prop->sp_Data; + for( j = 0; j < (1L << bmhd->nPlanes)*3; j+=3 ) + { +#if 0 + /* Some day we will want to use the larger palette + * resolution available under v39 and later. i.e. + * 32 instead of 12 bits of color. Ususally this + * just means shifting the color left by 16-20 bits + * depending on what intensity looks best. Experience + * says that the higher values are better intensities. + * + * For now though we won't do this. The color table + * structure is incompatible with earlier versions of + * intuition. We would have to do some funny things + * to make 3*AMII_MAXCOLORS longs work like 3*AMII_MAXCOLORS + * UWORD's at run time... A union would help, but... + */ + if( IntuitionBase->LibNode.lib_Version >= 39 ) + { + /* 8 bits of color, so shift to left end. */ + amiv_init_map[ j+0 ] = cmap[j+0]<<24; + amiv_init_map[ j+1 ] = cmap[j+1]<<24; + amiv_init_map[ j+2 ] = cmap[j+2]<<24; + } + else +#endif + { + /* We can only use 4 bits of the 8 that are stored in the + * cmap, so mask them and then shift them into position + * for the UWORD value to store. + */ +#ifndef TESTING + amii_initmap[ j/3 ] = + amiv_init_map[ j/3 ] = + ((cmap[j+0]>>4)<<8)| + ((cmap[j+1]>>4)<<4)| + (cmap[j+2]>>4); +#endif + } + } + } + else + { + FreeImageFiles(filenames, iffimg); + CloseIFF( iff ); + Close( iff->iff_Stream ); + FreeIFF( iff ); + *errstrp = "No CMAP CHUNK in file"; + return bmhds; + } + + if( prop = FindProp( iff, ID_BMAP, ID_PDAT ) ) + { + struct PDAT *pp; + + pp = (struct PDAT *)prop->sp_Data; + pictdata = *pp; + } + else + { + FreeImageFiles(filenames, iffimg); + CloseIFF( iff ); + Close( iff->iff_Stream ); + FreeIFF( iff ); + *errstrp = "No PDAT CHUNK in file"; + return bmhds; + } + + iffimg[ i ] = MyAllocBitMap( bmhd->w, bmhd->h, + pictdata.nplanes + amii_extraplanes, MEMF_CHIP|MEMF_CLEAR ); + if( iffimg[ i ] == NULL ) + { + char *buf = malloc(80); + FreeImageFiles(filenames, iffimg); + sprintf(buf, "Can't allocate bitmap for image %d\n", i ); + *errstrp = buf; + return bmhds; + } + for( j = 0; j < pictdata.nplanes + amii_extraplanes; ++j ) + { + ReadChunkBytes( iff, iffimg[i]->Planes[j], RASSIZE( bmhd->w, bmhd->h ) ); + } + bmhds = *bmhd; + CloseIFF( iff ); + Close( iff->iff_Stream ); + FreeIFF( iff ); + } + CloseLibrary( IFFParseBase ); + + tile = MyAllocBitMap( pictdata.xsize, pictdata.ysize, + pictdata.nplanes + amii_extraplanes, MEMF_CHIP|MEMF_CLEAR ); + if( tile == NULL ) + { + FreeImageFiles(filenames, iffimg); + *errstrp = "Can't allocate tile bitmap for scaling"; + } + return( bmhds ); +} + +struct MyBitMap +{ + struct BitMap bm; + long mflags; + USHORT xsize, ysize; +}; + +struct BitMap * +MyAllocBitMap( int xsize, int ysize, int depth, long mflags ) +{ + int j; + struct MyBitMap *bm; + + bm = (struct MyBitMap *)alloc( sizeof( *bm ) ); + if( !bm ) + return( NULL ); + + bm->xsize = xsize; + bm->ysize = ysize; + InitBitMap( &bm->bm, depth, xsize, ysize ); + for( j = 0; j < depth; ++j ) + { + if( mflags & MEMF_CHIP ) + bm->bm.Planes[ j ] = AllocRaster( xsize, ysize ); + else + bm->bm.Planes[ j ] = AllocMem( RASSIZE( xsize, ysize ), mflags ); + + if( bm->bm.Planes[ j ] == 0 ) + { + MyFreeBitMap( &bm->bm ); + return( NULL ); + } + if( mflags & MEMF_CLEAR ) + memset( bm->bm.Planes[ j ], 0, RASSIZE( xsize, ysize ) ); + } + return( &bm->bm ); +} + +void +MyFreeBitMap( struct BitMap *bmp ) +{ + int j; + struct MyBitMap *bm = (struct MyBitMap *)bmp; + + for( j = 0; j < bm->bm.Depth; ++j ) + { + if( bm->bm.Planes[j] ) + { + if( bm->mflags & MEMF_CHIP ) + FreeRaster( bm->bm.Planes[j], bm->xsize, bm->ysize ); + else + FreeMem( bm->bm.Planes[j], RASSIZE( bm->xsize, bm->ysize ) ); + } + } + free( bm ); +} + +#ifdef TESTING +void +panic(s,a1,a2,a3,a4) + char *s; +{ + printf( s, a1, a2, a3, a4 ); + putchar('\n'); +} +long * +alloc(unsigned int x){ + long *p = (long *)malloc(x); + if(!p){panic("malloc failed"); exit(1);} + return p; +} +#endif + +void +FreeTileImageFiles(){ + FreeImageFiles(tileimages,ifftimg); +} + +void +FreeImageFiles(char **filenames, struct BitMap **img ) +{ + register int i; + + for( i = 0; filenames[i]; ++i ) + { + if( img[ i ] ) + MyFreeBitMap( img[ i ] ); + } + + /* REALLY ugly hack alert! */ + if( tile && img==ifftimg) + MyFreeBitMap( tile ); +} + +#ifndef TESTING +/* + * Define some stuff for our special glyph drawing routines + */ +unsigned short glyph_node_index, glyph_buffer_index; +#define NUMBER_GLYPH_NODES 80 +#define GLYPH_BUFFER_SIZE 512 +struct amiv_glyph_node { + short odstx, odsty; + short srcx, srcy, dstx, dsty; + struct BitMap *bitmap; +}; +struct amiv_glyph_node amiv_g_nodes[NUMBER_GLYPH_NODES]; +static char amiv_glyph_buffer[GLYPH_BUFFER_SIZE]; + +void +flush_glyph_buffer( vw ) + struct Window *vw; +{ + if( WINVERS_AMIV ) + amiv_flush_glyph_buffer ( vw ); + else + amii_flush_glyph_buffer ( vw ); +} + +/* + * Routine to flush whatever is buffered + */ +void +amiv_flush_glyph_buffer( vw ) + struct Window *vw; +{ +#if !defined(DISPMAP) || defined(OPT_DISPMAP) + int xsize, ysize, x, y; + struct BitScaleArgs bsa; + struct BitScaleArgs bsm; + struct RastPort rast; + struct Window *w = NULL; + struct BitMap *imgbm = 0, *bm = 0; + int i, k; + int scaling_needed; + register struct RastPort *rp = vw->RPort; +#endif + + /* If nothing is buffered, return before we do anything */ + if(glyph_node_index == 0) + return; + + cursor_off( WIN_MAP ); + amiv_start_glyphout( WIN_MAP ); + +#ifdef OPT_DISPMAP + if(flags.fast_map){ +#endif +#ifdef DISPMAP + display_map( vw ); +#endif +#ifdef OPT_DISPMAP + } else { +#endif +#if !defined(DISPMAP) || defined(OPT_DISPMAP) +/* XXX fix indent */ + /* This is a dynamic value based on this relationship. */ + scaling_needed = ( pictdata.xsize != mxsize || pictdata.ysize != mysize ); + + /* If overview window is up, set up to render the correct scale there */ + if( WIN_OVER != WIN_ERR && ( w = amii_wins[ WIN_OVER ]->win ) != NULL ) + { + InitRastPort( &rast ); + + /* Calculate the x and y size of each tile for a ROWNO by COLNO map */ + xsize = (w->Width - w->BorderLeft - w->BorderRight) / COLNO; + ysize = (w->Height - w->BorderTop - w->BorderBottom) / ROWNO; + + /* Get a chip memory bitmap to blit out of */ + bm = MyAllocBitMap( pictdata.xsize, pictdata.ysize, + pictdata.nplanes + amii_extraplanes, MEMF_CLEAR|MEMF_CHIP ); + if( bm == NULL ) + { + amii_putstr( WIN_MESSAGE, 0, "Can't allocate bitmap for scaling overview window" ); + } + + rast.BitMap = bm; + + memset( &bsa, 0, sizeof( bsa ) ); + bsa.bsa_SrcX = bsa.bsa_SrcY = 0; + bsa.bsa_SrcBitMap = tile; + bsa.bsa_SrcWidth = pictdata.xsize; + bsa.bsa_SrcHeight = pictdata.ysize; + bsa.bsa_XSrcFactor = pictdata.xsize; + bsa.bsa_YSrcFactor = pictdata.ysize; + bsa.bsa_DestX = 0; + bsa.bsa_DestY = 0; + bsa.bsa_DestWidth = xsize; + bsa.bsa_DestHeight = ysize; + bsa.bsa_XDestFactor = xsize; + bsa.bsa_YDestFactor = ysize; + bsa.bsa_DestBitMap = bm; + } + + if( scaling_needed ) + { + /* Fill in scaling data for map rendering */ + memset( &bsm, 0, sizeof( bsm ) ); + bsm.bsa_SrcX = bsm.bsa_SrcY = 0; + bsm.bsa_SrcBitMap = tile; + + bsm.bsa_SrcWidth = pictdata.xsize; + bsm.bsa_SrcHeight = pictdata.ysize; + + bsm.bsa_XSrcFactor = pictdata.xsize; + bsm.bsa_YSrcFactor = pictdata.ysize; + + bsm.bsa_DestWidth = mxsize; + bsm.bsa_DestHeight = mysize; + + bsm.bsa_XDestFactor = mxsize; + bsm.bsa_YDestFactor = mysize; + bsm.bsa_DestBitMap = rp->BitMap; + bsm.bsa_DestY = bsm.bsa_DestX = 0; + + imgbm = MyAllocBitMap( mxsize, mysize, + pictdata.nplanes + amii_extraplanes, MEMF_CLEAR|MEMF_CHIP ); + if( imgbm == NULL ) + { + amii_putstr( WIN_MESSAGE, 0, + "Can't allocate scaling bitmap for map window" ); + } + else + bsm.bsa_DestBitMap = imgbm; + } + + /* Go ahead and start dumping the stuff */ + for( i=0; iBytesPerRow; + for( j = 0; j < pictdata.nplanes + amii_extraplanes; ++j ) + { + for( k = 0; k < pictdata.ysize; ++k ) + { + + /* For a 16x16 tile, this could just be short assignments, but + * this code is generalized to handle any size tile image... + */ + memcpy( tile->Planes[ j ] + ( ( k * pictdata.ysize ) / 8 ), + nodebm->Planes[ j ] + offx + offy + ( nodebm->BytesPerRow * k ), + pictdata.ysize/8 ); + } + } + + if( !clipping || + ( x >= clipx && y >= clipy && + x < clipxmax && y < clipymax ) ) + { + /* scaling is needed, do it */ + if( scaling_needed ) + { + BitMapScale( &bsm ); + BltBitMapRastPort( imgbm, 0, 0, + rp, amiv_g_nodes[ i ].dstx, amiv_g_nodes[ i ].dsty, + mxsize, mysize, 0xc0 ); + } + else + { + BltBitMapRastPort( tile, 0, 0, + rp, amiv_g_nodes[ i ].dstx, amiv_g_nodes[ i ].dsty, + pictdata.xsize, pictdata.ysize, 0xc0 ); + } + } + /* Draw the overview window unless we are scrolling the map raster around */ + if( bm && w && reclip != 2 ) + { + BitMapScale( &bsa ); + BltBitMapRastPort( rast.BitMap, 0, 0, + w->RPort, + w->BorderLeft + amiv_g_nodes[ i ].odstx*xsize, + w->BorderTop + amiv_g_nodes[ i ].odsty*ysize, + xsize, ysize, 0xc0 ); + } + } + + if( imgbm ) MyFreeBitMap( imgbm ); + if( bm ) MyFreeBitMap( bm ); +#endif /* DISPMAP */ +#ifdef OPT_DISPMAP + } +#endif + + amii_end_glyphout( WIN_MAP ); + + /* Clean up */ + glyph_node_index = glyph_buffer_index = 0; +} + +/* + * Glyph buffering routine. Called instead of WindowPuts(). + */ +void +amiv_lprint_glyph(window,color_index, glyph) + winid window; + int color_index, glyph; +{ + int base; + struct amii_WinDesc *cw; + struct Window *w; + int curx; + int cury; + int tbl, icon; + register int xoff, yoff; + + /* Get the real icon index */ + if( glyph != NO_GLYPH ) + icon = GlyphToIcon( glyph ); + + if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL ) + panic("bad winid in amiv_lprint_glyph: %d", window ); + + w = cw->win; + + if( glyph != NO_GLYPH && glyph < 10000) + { + /* decide on which image has the needed picture */ + if( icon <= MAXMONTILE ) + { + tbl = TBLMONTILE; + base = 0; + } + else if( icon <= MAXOBJTILE ) + { + tbl = TBLOBJTILE; + base = MAXMONTILE+1; + } + else if( icon <= MAXOTHTILE ) + { + tbl = TBLOTHTILE; + base = MAXOBJTILE+1; + } + else + panic( "Bad icon #%d, glyph #%d, only %d icons known\n", icon, glyph, MAXOTHTILE ); + + /* Get the relative offset in the page */ + + /* How many pixels to account for y distance down */ + yoff = ((icon-base) / pictdata.across) * pictdata.ysize; + + /* How many pixels to account for x distance across */ + xoff = ((icon-base) % pictdata.across) * pictdata.xsize; + } + + if(glyph >= 10000){ + /* Run a single ASCII character out to the rastport right now */ + char c= glyph-10000; + int xxx,xxy; + struct RastPort *rp = w->RPort; + + Move(rp, xxx=(((cw->curx-clipx)*rp->TxWidth) + w->BorderLeft), + xxy=(w->BorderTop + (((cw->cury-clipy)+1)* rp->TxHeight)+1)); + Text(rp,&c,1); + /* XXX this shouldn't be necessary: */ + if(cw->cursx == xxx && cw->cursy == xxy){ + cw->wflags &= ~FLMAP_CURSUP; + } + cw->curx += rp->TxWidth; /* keep things in sync */ + return; + } + + if( cw->type == NHW_MAP ) + { + curx = cw->curx - clipx; + cury = cw->cury - clipy; + + /* See if we're out of glyph nodes */ + if(glyph_node_index >= NUMBER_GLYPH_NODES) + amiv_flush_glyph_buffer( w ); + + /* Fill in the node. */ + amiv_g_nodes[glyph_node_index].dsty = min( w->BorderTop + (cury * mysize), + w->Height - 1 ); + +#ifdef OPT_DISPMAP + if(flags.fast_map){ +#endif /* keni */ +#ifdef DISPMAP + /* display_map() needs byte-aligned destinations, and we don't want to + * overwrite the window border. + */ + amiv_g_nodes[glyph_node_index].dstx = + (w->BorderLeft + 8 + (curx * mxsize)) & -8; +#endif +#ifdef OPT_DISPMAP +} else { +#endif +#if !defined(DISPMAP) || defined(OPT_DISPMAP) + amiv_g_nodes[glyph_node_index].dstx = min( w->BorderLeft + (curx * mxsize), + w->Width - 1 ); +#endif +#ifdef OPT_DISPMAP +} +#endif + amiv_g_nodes[glyph_node_index].odsty = cw->cury; + amiv_g_nodes[glyph_node_index].odstx = cw->curx; + amiv_g_nodes[glyph_node_index].srcx = xoff; + amiv_g_nodes[glyph_node_index].srcy = yoff; + amiv_g_nodes[glyph_node_index].bitmap = ifftimg[ tbl ]; + ++glyph_node_index; + } + else + { + /* Do it */ + register int j, k, x, y, apen; + struct RastPort *rp = w->RPort; + x = rp->cp_x - pictdata.xsize - 3; +#ifdef OPT_DISPMAP + if(flags.fast_map){ +#endif +#ifdef DISPMAP + x &= -8; + if(x==0) x = 8; +#endif +#ifdef OPT_DISPMAP + } +#endif + + y = rp->cp_y - pictdata.ysize + 1; + + if( glyph != NO_GLYPH ) + { + struct BitMap *bm = ifftimg[ tbl ]; + + /* 8 bits per byte */ + xoff /= 8; + yoff *= bm->BytesPerRow; + for( j = 0; j < pictdata.nplanes; ++j ) + { + for( k = 0; k < pictdata.ysize; ++k ) + { + memcpy( tile->Planes[ j ] + ( ( k * pictdata.ysize ) / 8 ), + bm->Planes[ j ] + xoff + yoff + ( bm->BytesPerRow * k ), + pictdata.ysize/8 ); + } + } + + BltBitMapRastPort( tile, 0, 0, + rp, x, y, + pictdata.xsize, pictdata.ysize, 0xc0 ); + + apen = rp->FgPen; + SetAPen( rp, flags.amii_dripens[ SHINEPEN ] ); + Move( rp, x-1, y + pictdata.ysize ); + Draw( rp, x-1, y - 1 ); + Draw( rp, x + pictdata.xsize, y - 1 ); + SetAPen( rp, flags.amii_dripens[ SHADOWPEN ] ); + Move( rp, x + pictdata.xsize, y ); + Draw( rp, x + pictdata.xsize, y + pictdata.ysize ); + Draw( rp, x, y + pictdata.ysize ); + SetAPen( rp, apen ); + } + else if( x > w->BorderLeft ) + { + int apen, bpen; + apen = rp->FgPen; + bpen = rp->BgPen; + SetAPen( rp, amii_menuBPen ); + SetBPen( rp, amii_menuBPen ); + RectFill( rp, x-1, y-1, x + pictdata.xsize, y + pictdata.ysize ); + SetAPen( rp, apen ); + SetBPen( rp, bpen ); + } + } +} + +/* + * Define some variables which will be used to save context when toggling + * back and forth between low level text and console I/O. + */ +static long xsave, ysave, modesave, apensave, bpensave; +static int usecolor; + +/* + * The function is called before any glyphs are driven to the screen. It + * removes the cursor, saves internal state of the window, then returns. + */ + +void +amiv_start_glyphout(window) + winid window; +{ + struct amii_WinDesc *cw; + struct Window *w; + + if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL ) + panic( "bad winid %d in start_glyphout()", window ); + + if( cw->wflags & FLMAP_INGLYPH ) + return; + + if( !(w = cw->win ) ) + panic( "bad winid %d, no window ptr set", window ); + + /* + * Save the context of the window + */ + xsave = w->RPort->cp_x; + ysave = w->RPort->cp_y; + modesave = w->RPort->DrawMode; + apensave = w->RPort->FgPen; + bpensave = w->RPort->BgPen; + + /* + * Set the mode, and be done with it + */ + usecolor = iflags.use_color; + iflags.use_color = FALSE; + cw->wflags |= FLMAP_INGLYPH; +} + +/* + * General cleanup routine -- flushes and restores cursor + */ +void +amii_end_glyphout(window) + winid window; +{ + struct amii_WinDesc *cw; + struct Window *w; + + if( ( cw = amii_wins[ window ] ) == (struct amii_WinDesc *)NULL ) + panic("bad window id %d in amii_end_glyphout()", window ); + + if( ( cw->wflags & FLMAP_INGLYPH ) == 0 ) + return; + cw->wflags &= ~(FLMAP_INGLYPH); + + if( !(w = cw->win ) ) + panic( "bad winid %d, no window ptr set", window ); + + /* + * Clean up whatever is left in the buffer + */ + iflags.use_color = usecolor; + + /* + * Reset internal data structs + */ + SetAPen(w->RPort, apensave); + SetBPen(w->RPort, bpensave); + SetDrMd(w->RPort, modesave); + + Move(w->RPort, xsave, ysave); +} + +static maze_type=COL_MAZE_BRICK; + +void SetMazeType(MazeType t) +{ + maze_type=t; +} + +int GlyphToIcon(int glyph) +{ + if(glyph>10000)return glyph; + return( glyph2tile[glyph] ); +} +#endif + +#ifdef AMII_GRAPHICS +# ifdef TESTING +/* + * Define some stuff for our special glyph drawing routines + */ +static unsigned short glyph_node_index, glyph_buffer_index; +# define NUMBER_GLYPH_NODES 80 +# define GLYPH_BUFFER_SIZE 512 +# endif /* TESTING */ + +struct amii_glyph_node { + short x; + short y; + short len; + unsigned char bg_color; + unsigned char fg_color; + char *buffer; +}; +static struct amii_glyph_node amii_g_nodes[NUMBER_GLYPH_NODES]; +static char amii_glyph_buffer[GLYPH_BUFFER_SIZE]; + +#ifdef TEXTCOLOR +/* + * Map our amiga-specific colormap into the colormap specified in color.h. + * See winami.c for the amiga specific colormap. + */ + +int foreg[AMII_MAXCOLORS] = { 0, 7, 4, 2, 6, 5, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; +int backg[AMII_MAXCOLORS] = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 7, 4, 1, 6, 5, 3, 1 }; +#if 0 + #define CLR_BLACK 0 + #define CLR_RED 1 + #define CLR_GREEN 2 + #define CLR_BROWN 3 /* on IBM, low-intensity yellow is brown */ + #define CLR_BLUE 4 + #define CLR_MAGENTA 5 + #define CLR_CYAN 6 + #define CLR_GRAY 7 /* low-intensity white */ + #define NO_COLOR 8 + #define CLR_ORANGE 9 + #define CLR_BRIGHT_GREEN 10 + #define CLR_YELLOW 11 + #define CLR_BRIGHT_BLUE 12 + #define CLR_BRIGHT_MAGENTA 13 + #define CLR_BRIGHT_CYAN 14 + #define CLR_WHITE 15 + #define CLR_MAX 16 +#endif +#endif + +#ifndef TESTING +/* + * Begin Revamped Text display routines + * + * Up until version 3.1, the only method for displaying text on the playing + * field was by using the console.device. This was nice for a number of + * reasons, the most signifigant of which was a lot of the nuts and bolts was + * done for you via escape sequences interpreted by said device. This did + * not come without a price however. And that price was speed. It has now + * come to a point where the speed has now been deemed unacceptable. + * + * The following series of routines are designed to drop into the current + * nethack display code, using hooks provided for such a measure. It works + * on similar principals as the WindowPuts(), buffering I/O internally + * until either an explicit flush or internal buffering is exceeded, thereby + * forcing the flush. The output (or glyphs) does not go to the + * console.device, however. It is driven directly to the rasterport of the + * nethack window via the low-level Text() calls, increasing the speed by + * a very signifigant factor. + */ +/* + * Routine to simply flush whatever is buffered + */ +void +amii_flush_glyph_buffer( w ) + struct Window *w; +{ + short i, x, y; + register struct RastPort *rp = w->RPort; + + /* If nothing is buffered, return before we do anything */ + if(glyph_node_index == 0) + return; + + cursor_off( WIN_MAP ); + amii_start_glyphout( WIN_MAP ); + + /* Set up the drawing mode */ + SetDrMd( rp, JAM2); + + /* Go ahead and start dumping the stuff */ + for(i=0; iBorderTop + (amii_g_nodes[i].y-2) * rp->TxHeight + + rp->TxBaseline + 1; + x = amii_g_nodes[i].x * rp->TxWidth + w->BorderLeft; + + /* Move pens to correct location */ + Move( rp, (long)x, (long)y); + + /* Setup the colors */ + SetAPen( rp, (long)amii_g_nodes[i].fg_color); + SetBPen( rp, (long)amii_g_nodes[i].bg_color); + + /* Do it */ + Text( rp, amii_g_nodes[i].buffer, amii_g_nodes[i].len); + } + + amii_end_glyphout( WIN_MAP ); + /* Clean up */ + glyph_node_index = glyph_buffer_index = 0; +} +void +amiga_print_glyph(window,color_index, glyph) + winid window; + int color_index, glyph; +{ + if( WINVERS_AMIV ) + amiv_lprint_glyph(window,color_index, glyph); + else + amii_lprint_glyph(window,color_index, glyph); +} + +/* + * Glyph buffering routine. Called instead of WindowPuts(). + */ +void +amii_lprint_glyph(window,color_index, glyph) + winid window; + int color_index, glyph; +{ + int fg_color, bg_color; + struct amii_WinDesc *cw; + struct Window *w; + int curx; + int cury; + + if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL ) + panic("bad winid in amii_lprint_glyph: %d", window ); + + w = cw->win; + curx=cw->curx; + cury=cw->cury; + +#ifdef TEXTCOLOR + fg_color = foreg[color_index]; + bg_color = backg[color_index]; +#else + fg_color = 1; + bg_color = 0; +#endif /* TEXTCOLOR */ + + /* See if we have enough character buffer space... */ + if(glyph_buffer_index >= GLYPH_BUFFER_SIZE) + amii_flush_glyph_buffer( w ); + + /* + * See if we can append it to the current active node of glyph buffer. It + * must satisfy the following conditions: + * + * * background colors are the same, AND + * * foreground colors are the same, AND + * * they are precisely side by side + */ + if((glyph_buffer_index != 0) && + (fg_color == amii_g_nodes[glyph_node_index-1].fg_color) && + (bg_color == amii_g_nodes[glyph_node_index-1].bg_color) && + (amii_g_nodes[glyph_node_index-1].x+ + amii_g_nodes[glyph_node_index-1].len == curx) && + (amii_g_nodes[glyph_node_index-1].y == cury)) { + /* + * Add it to the end of the buffer + */ + amii_glyph_buffer[glyph_buffer_index++] = glyph; + amii_g_nodes[glyph_node_index-1].len ++; + } else { + /* See if we're out of glyph nodes */ + if(glyph_node_index >= NUMBER_GLYPH_NODES) + amii_flush_glyph_buffer( w ); + amii_g_nodes[glyph_node_index].len = 1; + amii_g_nodes[glyph_node_index].x = curx; + amii_g_nodes[glyph_node_index].y = cury; + amii_g_nodes[glyph_node_index].fg_color = fg_color; + amii_g_nodes[glyph_node_index].bg_color = bg_color; + amii_g_nodes[glyph_node_index].buffer = &amii_glyph_buffer[glyph_buffer_index]; + amii_glyph_buffer[glyph_buffer_index] = glyph; + ++glyph_buffer_index; + ++glyph_node_index; + } +} +#endif /* !TESTING */ + +#ifdef TESTING +/* + * Define some variables which will be used to save context when toggling + * back and forth between low level text and console I/O. + */ +static long xsave, ysave, modesave, apensave, bpensave; +static int usecolor; +#endif /* TESTING */ + +#ifndef TESTING +/* + * The function is called before any glyphs are driven to the screen. It + * removes the cursor, saves internal state of the window, then returns. + */ + +void +amii_start_glyphout(window) + winid window; +{ + struct amii_WinDesc *cw; + struct Window *w; + + if( ( cw=amii_wins[window] ) == (struct amii_WinDesc *)NULL ) + panic( "bad winid %d in start_glyphout()", window ); + + if( cw->wflags & FLMAP_INGLYPH ) + return; + + if( !(w = cw->win ) ) + panic( "bad winid %d, no window ptr set", window ); + + /* + * Save the context of the window + */ + xsave = w->RPort->cp_x; + ysave = w->RPort->cp_y; + modesave = w->RPort->DrawMode; + apensave = w->RPort->FgPen; + bpensave = w->RPort->BgPen; + + /* + * Set the mode, and be done with it + */ + usecolor = iflags.use_color; + iflags.use_color = FALSE; + cw->wflags |= FLMAP_INGLYPH; +} +#endif /* !TESTING */ + +# if 0 +/* + * General cleanup routine -- flushes and restores cursor + */ +void +amii_end_glyphout(window) + winid window; +{ + struct amii_WinDesc *cw; + struct Window *w; + + if( ( cw = amii_wins[ window ] ) == (struct amii_WinDesc *)NULL ) + panic("bad window id %d in amii_end_glyphout()", window ); + + if( ( cw->wflags & FLMAP_INGLYPH ) == 0 ) + return; + cw->wflags &= ~(FLMAP_INGLYPH); + + if( !(w = cw->win ) ) + panic( "bad winid %d, no window ptr set", window ); + + /* + * Clean up whatever is left in the buffer + */ + iflags.use_color = usecolor; + + /* + * Reset internal data structs + */ + SetAPen(w->RPort, apensave); + SetBPen(w->RPort, bpensave); + SetDrMd(w->RPort, modesave); + + Move(w->RPort, xsave, ysave); +} +# endif +#endif + +#ifndef TESTING +# ifdef OPT_DISPMAP +/* don't use dispmap unless x & y are 8,16,24,32,48 and equal */ +void +dispmap_sanity(){ + if( + mxsize != mysize || + dispmap_sanity1(mxsize) || + dispmap_sanity1(mysize)){ + flags.fast_map = 0; + } +} +int +dispmap_sanity1(x) + int x; +{ + static unsigned char valid[] = {8,16,24,32,48,0}; + return !!strchr(valid,x); +} +# endif /* OPT_DISPMAP */ +#endif /* TESTING */ diff --git a/sys/amiga/windefs.h b/sys/amiga/windefs.h new file mode 100644 index 0000000..af017be --- /dev/null +++ b/sys/amiga/windefs.h @@ -0,0 +1,203 @@ +/* SCCS Id: @(#)windefs.h 3.1 93/04/02 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include +#if !defined(_DCC) && !defined(__GNUC__) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* stddef.h is included in the precompiled version of hack.h . If we include + * it here normally (through string.h) we'll get an "illegal typedef" later + * on. This is the easiest way I can think of to fix it without messing + * around with the rest of the #includes. --AMC + */ +#if defined(_DCC) && !defined(HACK_H) +# define ptrdiff_t ptrdiff_t_ +# define size_t size_t_ +# define wchar_t wchar_t_ +#endif +#include +#undef strcmpi +#include +#include +#if defined(_DCC) && !defined(HACK_H) +# undef ptrdiff_t +# undef size_t +# undef wchar_T +#endif + +#ifdef IDCMP_CLOSEWINDOW +# ifndef INTUI_NEW_LOOK +# define INTUI_NEW_LOOK +# endif +#endif + +#ifndef HACK_H +#include "hack.h" +#endif +#include "wintype.h" +#include "winami.h" +#include "func_tab.h" + +#ifndef CLIPPING +CLIPPING must be defined for the AMIGA version +#endif + +#undef LI +#undef CO + +/*#define TOPL_GETLINE /* Don't use a window for getlin() */ +/*#define WINDOW_YN /* Use a window for y/n questions */ + +#ifdef AZTEC_C +#include +#else +#ifdef _DCC +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +/* kludge - see amirip for why */ +# undef red +# undef green +# undef blue +#ifdef _DCC +# include +#else +# include +#endif + +#ifdef _DCC +# define __asm /* DICE doesn't like __asm */ +#endif + +#ifndef __SASC_60 +#undef index +# define index strchr +#endif + +#ifdef _DCC +#include +#else +#include +#endif +#endif + +#ifdef SHAREDLIB +#include "NH:sys/amiga/lib/libmacs.h" +#endif + +#ifdef INTUI_NEW_LOOK +#include +#endif + +#define WINVERS_AMII (strcmp("amii",windowprocs.name)==0) +#define WINVERS_AMIV (strcmp("amitile",windowprocs.name)==0) +#define WINVERS_AMIT (strcmp("amitty",windowprocs.name)==0) + +/* cw->data[x] contains 2 characters worth of special information. These + * characters are stored at the offsets as described here. + */ +#define VATTR 0 /* Video attribute is in this slot */ +#define SEL_ITEM 1 /* If this is a select item, slot is 1 else 0 */ +#define SOFF 2 /* The string starts here. */ + +#undef NULL +#define NULL 0L + +/* + * Versions we need of various libraries. We can't use LIBRARY_VERSION + * as defined in because some of the libraries we need + * don't have that version number in the 1.2 ROM. + */ + +#define LIBRARY_FONT_VERSION 34L +#define LIBRARY_TILE_VERSION 37L + +/* These values are just sorta suggestions in use, but are minimum requirements + * in reality... + */ +#define WINDOWHEIGHT 192 +#define SCREENHEIGHT 200 +#define WIDTH 640 + +/* This character is a solid block (cursor) in Hack.font */ +#define CURSOR_CHAR 0x90 + +#define FONTHEIGHT 8 +#define FONTWIDTH 8 +#define FONTBASELINE 8 + +#define MAPFTWIDTH 8 +#define MAPFTHEIGHT 8 +#define MAPFTBASELN 6 + +/* If Compiling with the "New Look", redefine these now */ +#ifdef INTUI_NEW_LOOK +#define NewWindow ExtNewWindow +#define NewScreen ExtNewScreen +#endif + +#define SIZEOF_DISKNAME 8 + +#define CSI '\x9b' +#define NO_CHAR -1 +#define RAWHELP 0x5F /* Rawkey code of the HELP key */ + + +#define C_BLACK 0 +#define C_WHITE 1 +#define C_BROWN (WINVERS_AMIV ? 11 : 2) +#define C_CYAN (WINVERS_AMIV ? 2 : 3) +#define C_GREEN (WINVERS_AMIV ? 5 : 4) +#define C_MAGENTA (WINVERS_AMIV ? 10 : 5) +#define C_BLUE (WINVERS_AMIV ? 4 : 6) +#define C_RED 7 +#define C_ORANGE 3 +#define C_GREY 6 +#define C_LTGREEN 8 +#define C_YELLOW 9 +#define C_GREYBLUE 12 +#define C_LTBROWN 13 +#define C_LTGREY 14 +#define C_PEACH 15 + +/* Structure describing tile files */ +struct PDAT +{ + long nplanes; /* Depth of images */ + long pbytes; /* Bytes in a plane of data */ + long across; /* Number of tiles across */ + long down; /* Number of tiles down */ + long npics; /* Number of pictures in this file */ + long xsize; /* X-size of a tile */ + long ysize; /* Y-size of a-tile */ +}; + +#undef MAXCOLORS +#define MAXCOLORS 256 diff --git a/sys/amiga/winext.h b/sys/amiga/winext.h new file mode 100644 index 0000000..28e423c --- /dev/null +++ b/sys/amiga/winext.h @@ -0,0 +1,144 @@ +/* SCCS Id: @(#)winext.h 3.1 2000/01/12 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +extern int reclip; + +#ifdef CLIPPING +extern int clipping; +extern int clipx; +extern int clipy; +extern int clipxmax; +extern int clipymax; +extern int xclipbord, yclipbord; +#endif + +extern int CO; +extern int LI; +extern int scrollmsg; +extern int alwaysinvent; + +#ifndef SHAREDLIB +extern unsigned short amii_defpens[ 20 ]; +extern struct amii_DisplayDesc *amiIDisplay; /* the Amiga Intuition descriptor */ +extern struct window_procs amii_procs; +extern struct window_procs amiv_procs; +extern unsigned short amii_initmap[ AMII_MAXCOLORS ]; +extern unsigned short amiv_init_map[ AMII_MAXCOLORS ]; +extern unsigned short amii_init_map[ AMII_MAXCOLORS ]; +extern int bigscreen; +extern int amii_numcolors; +extern long amii_scrnmode; +extern winid amii_rawprwin; +extern struct Screen *HackScreen; +extern char Initialized; +/* These have already been defined elsewhere (and some are conflicting) + * ... going ... going once ... going twice .... + * extern const char *roles[]; + * extern struct Library *ConsoleDevice; + * extern char toplines[ TBUFSZ ]; + * extern NEARDATA winid WIN_MESSAGE; + * extern NEARDATA winid WIN_MAP; + * extern NEARDATA winid WIN_STATUS; + * extern NEARDATA winid WIN_INVEN; + * extern winid WIN_OVER; + * extern struct GfxBase *GfxBase; + * extern struct Library *DiskfontBase; + * extern struct IntuitionBase *IntuitionBase; + * extern struct Library *LayersBase; + */ +extern int amii_msgAPen; +extern int amii_msgBPen; +extern int amii_statAPen; +extern int amii_statBPen; +extern int amii_menuAPen; +extern int amii_menuBPen; +extern int amii_textAPen; +extern int amii_textBPen; +extern int amii_otherAPen; +extern int amii_otherBPen; +#else +extern WinamiBASE *WinamiBase; +#endif +/* All kinds of shared stuff */ +extern struct TextAttr Hack160; +extern struct TextAttr Hack40; +extern struct TextAttr Hack80; +extern struct TextAttr TextsFont13; +extern struct Window *pr_WindowPtr; +extern struct Menu HackMenu[]; +extern struct Menu *MenuStrip; +extern struct NewMenu GTHackMenu[]; +extern APTR *VisualInfo; +extern unsigned char KbdBuffered; +extern struct TextFont *TextsFont; +extern struct TextFont *HackFont; +extern struct IOStdReq ConsoleIO; +extern struct MsgPort *HackPort; + +extern int txwidth, txheight, txbaseline; +#ifdef SUPERBITMAP_MAP +extern struct BitMap amii_vbm; +#endif + +/* This gadget data is replicated for menu/text windows... */ +extern struct PropInfo PropScroll; +extern struct Image Image1; +extern struct Gadget MenuScroll; + +/* This gadget is for the message window... */ +extern struct PropInfo MsgPropScroll; +extern struct Image MsgImage1; +extern struct Gadget MsgScroll; + +extern struct TagItem tags[]; + +extern struct win_setup +{ + struct NewWindow newwin; + UWORD offx,offy,maxrow,rows,maxcol,cols; /* CHECK TYPES */ +} new_wins[]; + +extern UWORD scrnpens[]; +/* The last Window event is stored here for reference. */ +extern WEVENT lastevent; +extern const char winpanicstr[]; +extern struct TagItem scrntags[]; +extern struct NewScreen NewHackScreen; + +extern int topl_addspace; +extern char spaces[ 76 ]; +extern int wincnt; /* # of nh windows opened */ +extern struct Rectangle lastinvent, lastmsg; + +typedef struct { + UWORD w, h; + WORD x, y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE reserved1; + UWORD transparentColor; + UBYTE xAspect, yAspect; + WORD pageWidth, pageHeight; +} BitMapHeader; + +typedef enum {COL_MAZE_BRICK,COL_MAZE_STONE,COL_MAZE_HEAT,COL_MAZE_WOOD} MazeType; +extern struct PDAT pictdata; +extern struct Hook fillhook; +extern struct TagItem wintags[]; +#ifndef SHAREDLIB +#ifndef __GNUC__ +void __asm LayerFillHook( + register __a0 struct Hook *hk, + register __a2 struct RastPort *rp, + register __a1 struct FillParams *fp ); +#else +#ifdef __PPC__ +struct EmulLibEntry LayerFillHook; +#else +void LayerFillHook(void); +#endif +#endif +#endif +extern int mxsize, mysize; diff --git a/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c new file mode 100644 index 0000000..81bca7a --- /dev/null +++ b/sys/amiga/winfuncs.c @@ -0,0 +1,2451 @@ +/* SCCS Id: @(#)winfuncs.c 3.1 2000/01/12 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993,1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" +#include "patchlevel.h" + +extern struct TagItem scrntags[]; + +static BitMapHeader amii_bmhd; +static void cursor_common(struct RastPort *, int, int); + +#ifdef CLIPPING +int CO, LI; + +/* Changing clipping region, skip clear of screen in overview window. */ +int reclip; + +/* Must be set to at least two or you will get stuck! */ +int xclipbord = 4, yclipbord = 2; +#endif + +int mxsize, mysize; +struct Rectangle amii_oldover; +struct Rectangle amii_oldmsg; + +extern struct TextFont *RogueFont; + +int amii_msgAPen; +int amii_msgBPen; +int amii_statAPen; +int amii_statBPen; +int amii_menuAPen; +int amii_menuBPen; +int amii_textAPen; +int amii_textBPen; +int amii_otherAPen; +int amii_otherBPen; +long amii_libvers = LIBRARY_FONT_VERSION; + +void +ami_wininit_data( void ) +{ + extern unsigned short amii_init_map[ AMII_MAXCOLORS ]; + extern unsigned short amiv_init_map[ AMII_MAXCOLORS ]; + if( !WINVERS_AMIV ) + { +# ifdef TEXTCOLOR + amii_numcolors = 8; +# else + amii_numcolors = 4; +# endif + amii_defpens[ 0 ] = C_BLACK; /* DETAILPEN */ + amii_defpens[ 1 ] = C_BLUE; /* BLOCKPEN */ + amii_defpens[ 2 ] = C_BROWN; /* TEXTPEN */ + amii_defpens[ 3 ] = C_WHITE; /* SHINEPEN */ + amii_defpens[ 4 ] = C_BLUE; /* SHADOWPEN */ + amii_defpens[ 5 ] = C_CYAN; /* FILLPEN */ + amii_defpens[ 6 ] = C_WHITE; /* FILLTEXTPEN */ + amii_defpens[ 7 ] = C_CYAN; /* BACKGROUNDPEN */ + amii_defpens[ 8 ] = C_RED; /* HIGHLIGHTTEXTPEN */ + amii_defpens[ 9 ] = C_WHITE; /* BARDETAILPEN */ + amii_defpens[ 10 ] = C_CYAN; /* BARBLOCKPEN */ + amii_defpens[ 11 ] = C_BLUE; /* BARTRIMPEN */ + amii_defpens[ 12 ] = (unsigned short) ~0; + + amii_msgAPen = C_WHITE; + amii_msgBPen = C_BLACK; + amii_statAPen = C_WHITE; + amii_statBPen = C_BLACK; + amii_menuAPen = C_WHITE; + amii_menuBPen = C_BLACK; + amii_textAPen = C_WHITE; + amii_textBPen = C_BLACK; + amii_otherAPen = C_RED; + amii_otherBPen = C_BLACK; + + mxsize = 8; + mysize = 8; + + amii_libvers = LIBRARY_FONT_VERSION; + memcpy( amii_initmap, amii_init_map, sizeof( amii_initmap ) ); + } + else + { + mxsize = 16; + mysize = 16; + + amii_numcolors = 16; + + amii_defpens[ 0 ] = C_BLACK; /* DETAILPEN */ + amii_defpens[ 1 ] = C_WHITE; /* BLOCKPEN */ + amii_defpens[ 2 ] = C_BLACK; /* TEXTPEN */ + amii_defpens[ 3 ] = C_CYAN; /* SHINEPEN */ + amii_defpens[ 4 ] = C_BLUE; /* SHADOWPEN */ + amii_defpens[ 5 ] = C_GREYBLUE; /* FILLPEN */ + amii_defpens[ 6 ] = C_LTGREY; /* FILLTEXTPEN */ + amii_defpens[ 7 ] = C_GREYBLUE; /* BACKGROUNDPEN */ + amii_defpens[ 8 ] = C_RED; /* HIGHLIGHTTEXTPEN */ + amii_defpens[ 9 ] = C_WHITE; /* BARDETAILPEN */ + amii_defpens[ 10] = C_GREYBLUE; /* BARBLOCKPEN */ + amii_defpens[ 11] = C_BLUE; /* BARTRIMPEN */ + amii_defpens[ 12] = (unsigned short) ~0; + + amii_msgAPen = C_WHITE; + amii_msgBPen = C_GREYBLUE; + amii_statAPen = C_WHITE; + amii_statBPen = C_GREYBLUE; + amii_menuAPen = C_BLACK; + amii_menuBPen = C_LTGREY; + amii_textAPen = C_BLACK; + amii_textBPen = C_LTGREY; + amii_otherAPen = C_RED; + amii_otherBPen = C_BLACK; + amii_libvers = LIBRARY_TILE_VERSION; + + memcpy( amii_initmap, amiv_init_map, sizeof( amii_initmap ) ); + } +#ifdef OPT_DISPMAP + dispmap_sanity(); +#endif + memcpy(flags.amii_dripens,amii_defpens,sizeof(flags.amii_dripens)); +} + +# ifdef INTUI_NEW_LOOK +struct Hook SM_FilterHook; +struct Hook fillhook; +struct TagItem wintags[] = +{ + { WA_BackFill, (ULONG)&fillhook }, + { WA_PubScreenName, (ULONG)"NetHack" }, + { TAG_END, 0 }, +}; +# endif + +void +amii_destroy_nhwindow(win) /* just hide */ + register winid win; +{ + int i; + int type; + register struct amii_WinDesc *cw; + + if( win == WIN_ERR || ( cw = amii_wins[win] ) == NULL ) + { + panic(winpanicstr,win,"destroy_nhwindow"); + } + + if( WINVERS_AMIV ) + { + if( cw->type == NHW_MAP ) + { + /* If inventory is up, close it now, it will be freed later */ + if( alwaysinvent && WIN_INVEN != WIN_ERR && + amii_wins[ WIN_INVEN ] && + amii_wins[ WIN_INVEN ]->win ) + { + dismiss_nhwindow( WIN_INVEN ); + } + + /* Tear down overview window if it is up */ + if( WIN_OVER != WIN_ERR ) + { + amii_destroy_nhwindow( WIN_OVER ); + WIN_OVER = WIN_ERR; + } + } + else if( cw->type == NHW_OVER ) + { + struct Window *w = amii_wins[ WIN_OVER ]->win; + amii_oldover.MinX = w->LeftEdge; + amii_oldover.MinY = w->TopEdge; + amii_oldover.MaxX = w->Width; + amii_oldover.MaxY = w->Height; + + if( WIN_MESSAGE != WIN_ERR && amii_wins[ WIN_MESSAGE ] ) + { + w = amii_wins[ WIN_MESSAGE ]->win; + amii_oldmsg.MinX = w->LeftEdge; + amii_oldmsg.MinY = w->TopEdge; + amii_oldmsg.MaxX = w->Width; + amii_oldmsg.MaxY = w->Height; + SizeWindow( amii_wins[ WIN_MESSAGE ]->win, + (amiIDisplay->xpix - + amii_wins[ WIN_MESSAGE ]->win->LeftEdge) - + amii_wins[ WIN_MESSAGE ]->win->Width, + 0 ); + } + } + } + + /* Tear down the Intuition stuff */ + dismiss_nhwindow(win); + type = cw->type; + + if( cw->resp ) { + free( cw->resp ); + cw->resp = NULL; + } + if( cw->canresp ) { + free( cw->canresp ); + cw->canresp = NULL; + } + if( cw->morestr ) { + free( cw->morestr ); + cw->morestr = NULL; + } + if( cw->hook ) { + free( cw->hook ); + cw->hook = NULL; + } + + if( cw->data && ( cw->type == NHW_MESSAGE || + cw->type == NHW_MENU || cw->type == NHW_TEXT ) ) + { + for( i = 0; i < cw->maxrow; ++i ) + { + if( cw->data[ i ] ) + free( cw->data[ i ] ); + } + free( cw->data ); + } + + free( cw ); + amii_wins[win] = NULL; + + /* Set globals to WIN_ERR for known one-of-a-kind windows. */ + if( win == WIN_MAP) WIN_MAP = WIN_ERR; + else if( win == WIN_STATUS) WIN_STATUS = WIN_ERR; + else if( win == WIN_MESSAGE) WIN_MESSAGE = WIN_ERR; + else if( win == WIN_INVEN) WIN_INVEN = WIN_ERR; + +} + +#ifdef INTUI_NEW_LOOK +struct FillParams +{ + struct Layer *layer; + struct Rectangle bounds; + WORD offsetx; + WORD offsety; +}; + +#ifdef __GNUC__ +#ifdef __PPC__ +void PPC_LayerFillHook(void); +struct EmulLibEntry LayerFillHook = {TRAP_LIB, 0, (void (*)(void)) PPC_LayerFillHook}; +void PPC_LayerFillHook(void) { + struct Hook *hk = (struct Hook*)REG_A0; + struct RastPort *rp = (struct RastPort *)REG_A2; + struct FillParams *fp = (struct FillParams*)REG_A1; +#else +void LayerFillHook(void) { + register struct Hook *hk asm("a0"); + register struct RastPort *rp asm("a2"); + register struct FillParams *fp asm("a1"); +#endif +#else +void +#ifndef _DCC +__interrupt +#endif +__saveds __asm LayerFillHook( + register __a0 struct Hook *hk, + register __a2 struct RastPort *rp, + register __a1 struct FillParams *fp ) +{ +#endif + + register long x, y, xmax, ymax; + register int apen; + struct RastPort rptmp; + + memcpy(&rptmp, rp, sizeof(struct RastPort)); + rptmp.Layer = NULL; + + switch( (int)hk->h_Data ) + { + case NHW_STATUS: + apen = amii_statBPen; + break; + case NHW_MESSAGE: + apen = amii_msgBPen; + break; + case NHW_TEXT: + apen = amii_textBPen; + break; + case NHW_MENU: + apen = amii_menuBPen; + break; + case -2: + apen = amii_otherBPen; + break; + case NHW_BASE: + case NHW_MAP: + case NHW_OVER: + default: + apen = C_BLACK; + break; + } + + x = fp->bounds.MinX; + y = fp->bounds.MinY; + xmax = fp->bounds.MaxX; + ymax = fp->bounds.MaxY; + + SetAPen(&rptmp, apen); + SetBPen(&rptmp, apen); + SetDrMd(&rptmp, JAM2); + RectFill(&rptmp, x, y, xmax, ymax); +} +#endif + + +amii_create_nhwindow(type) + register int type; +{ + register struct Window *w = NULL; + register struct NewWindow *nw = NULL; + register struct amii_WinDesc *wd = NULL; + struct Window *mapwin = NULL, *stwin = NULL, *msgwin = NULL; + register int newid; + int maph, stath, scrfontysize; + + scrfontysize = HackScreen->Font->ta_YSize; + + /* + * Initial mapwindow height, this might change later in tilemode + * and low screen + */ + maph = ( 21 * mxsize ) + 2 + (bigscreen ? + HackScreen->WBorTop + HackScreen->WBorBottom + scrfontysize + 1 : 0); + + /* Status window height, avoids having to calculate many times */ + stath = txheight * 2 + 2 + (WINVERS_AMIV || bigscreen ? + HackScreen->WBorTop + HackScreen->WBorBottom + + ( bigscreen ? scrfontysize + 1 : 0 ) : 0); + + if( WIN_STATUS != WIN_ERR && amii_wins[ WIN_STATUS ] ) + stwin = amii_wins[ WIN_STATUS ]->win; + + if( WIN_MESSAGE != WIN_ERR && amii_wins[ WIN_MESSAGE ] ) + msgwin = amii_wins[ WIN_MESSAGE ]->win; + + if( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) + mapwin = amii_wins[ WIN_MAP ]->win; + + /* Create Port anytime that we need it */ + + if( HackPort == NULL ) + { + HackPort = CreateMsgPort(); + if( !HackPort ) + panic( "no memory for msg port" ); + } + + nw = &new_wins[ type ].newwin; + nw->Width = amiIDisplay->xpix; + nw->Screen = HackScreen; + + if( WINVERS_AMIV ) + { + nw->DetailPen = C_WHITE; + nw->BlockPen = C_GREYBLUE; + } + else + { + nw->DetailPen = C_WHITE; + nw->BlockPen = C_BLACK; + } + + if ( type == NHW_BASE ) { + nw->LeftEdge = 0; + nw->TopEdge = HackScreen->BarHeight+1; + nw->Width = HackScreen->Width; + nw->Height = HackScreen->Height - nw->TopEdge; + } else if( !WINVERS_AMIV && type == NHW_MAP ) { + nw->LeftEdge = 0; + nw->Height = maph; + + if( msgwin && stwin ) { + nw->TopEdge = stwin->TopEdge - maph; + } else { + panic( "msgwin and stwin must open before map" ); + } + if (nw->TopEdge < 0) + panic( "Too small screen to fit map" ); + } + else if( type == NHW_MAP && WINVERS_AMIV ) + { + struct Window *w; + + w = amii_wins[ WIN_MESSAGE ]->win; + nw->LeftEdge = 0; + nw->TopEdge = w->TopEdge + w->Height; + nw->Width = amiIDisplay->xpix - nw->LeftEdge; + + w = amii_wins[ WIN_STATUS ]->win; + nw->Height = w->TopEdge - nw->TopEdge; + nw->MaxHeight = 0xffff; + nw->MaxWidth = 0xffff; + + if( nw->TopEdge + nw->Height > amiIDisplay->ypix - 1 ) + nw->Height = amiIDisplay->ypix - nw->TopEdge - 1; + } + else if( type == NHW_STATUS ) + { + if( !WINVERS_AMIV && ( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) ) + w = amii_wins[ WIN_MAP ]->win; + else if( WIN_BASE != WIN_ERR && amii_wins[ WIN_BASE ] ) + w = amii_wins[ WIN_BASE ]->win; + else + panic( "No window to base STATUS location from" ); + + nw->Height = stath; + nw->TopEdge = amiIDisplay->ypix - nw->Height; + nw->LeftEdge = w->LeftEdge; + + if( nw->LeftEdge + nw->Width >= amiIDisplay->xpix ) + nw->LeftEdge = 0; + + if( nw->Width >= amiIDisplay->xpix - nw->LeftEdge ) + nw->Width = amiIDisplay->xpix - nw->LeftEdge; + } + else if( WINVERS_AMIV && type == NHW_OVER ) + { + nw->Flags |= WINDOWSIZING|WINDOWDRAG|WINDOWCLOSE; + nw->IDCMPFlags |= CLOSEWINDOW; + /* Bring up window as half the width of the message window, and make + * the message window change to one half the width... + */ + if( amii_oldover.MaxX != 0 ) + { + nw->LeftEdge = amii_oldover.MinX; + nw->TopEdge = amii_oldover.MinY; + nw->Width = amii_oldover.MaxX; + nw->Height = amii_oldover.MaxY; + ChangeWindowBox( amii_wins[ WIN_MESSAGE ]->win, + amii_oldmsg.MinX, amii_oldmsg.MinY, + amii_oldmsg.MaxX, amii_oldmsg.MaxY ); + } + else + { + nw->LeftEdge = (amii_wins[ WIN_MESSAGE ]->win->Width*4)/9; + nw->TopEdge = amii_wins[ WIN_MESSAGE ]->win->TopEdge; + nw->Width = amiIDisplay->xpix - nw->LeftEdge; + nw->Height = amii_wins[ WIN_MESSAGE ]->win->Height; + SizeWindow( amii_wins[ WIN_MESSAGE ]->win, + nw->LeftEdge - amii_wins[ WIN_MESSAGE ]->win->Width, 0 ); + } + } + else if( type == NHW_MESSAGE ) + { + if( !WINVERS_AMIV && ( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) ) + w = amii_wins[ WIN_MAP ]->win; + else if( WIN_BASE != WIN_ERR && amii_wins[ WIN_BASE ] ) + w = amii_wins[ WIN_BASE ]->win; + else + panic( "No window to base STATUS location from" ); + + nw->TopEdge = bigscreen ? HackScreen->BarHeight+1 : 0; + + /* Assume highest possible message window */ + nw->Height = HackScreen->Height - nw->TopEdge - maph - stath; + + /* In tilemode we can cope with this */ + if (WINVERS_AMIV && nw->Height < 0) + nw->Height = 0; + + /* If in fontmode messagewindow is too small, open it with 3 lines + and overlap it with map */ + if (nw->Height < txheight+2) { + nw->Height = txheight*4 + 3 + HackScreen->WBorTop + HackScreen->WBorBottom; + } + + if ((nw->Height-2)/txheight < 3) { + scrollmsg = 0; + nw->Title = 0; + } else { + nw->FirstGadget = &MsgScroll; + nw->Flags |= WINDOWSIZING|WINDOWDRAG; + nw->Flags &= ~BORDERLESS; + + if( WINVERS_AMIV || nw->Height == 0) { + if( WINVERS_AMIV ) { + nw->Height = TextsFont->tf_YSize + HackScreen->WBorTop + 3 + + HackScreen->WBorBottom; + if( bigscreen ) + nw->Height += ( txheight * 6 ); + else + nw->Height += ( txheight * 3 ); + } + else + { + nw->Height = HackScreen->Height - nw->TopEdge - stath - maph; + } + } + } + + /* Do we have room for larger message window ? + * This is possible if we can show full height map in tile + * mode with default scaling. + */ + if (nw->Height + stath + maph < HackScreen->Height - nw->TopEdge ) + nw->Height = HackScreen->Height - nw->TopEdge - 1 - maph - stath; + +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + MsgPropScroll.Flags |= PROPNEWLOOK; + PropScroll.Flags |= PROPNEWLOOK; + } +#endif + } + + nw->IDCMPFlags |= MENUPICK; + + /* Check if there is "Room" for all this stuff... */ + if( ( WINVERS_AMIV || bigscreen ) && + type != NHW_BASE ) + { + nw->Flags &= ~( BORDERLESS | BACKDROP ); + + if( WINVERS_AMIV ) + { + if( type == NHW_STATUS ) + { + nw->Flags &= ~( WINDOWDRAG | WINDOWDEPTH | SIZEBRIGHT | WINDOWSIZING ); + nw->IDCMPFlags &= ~NEWSIZE; + } + else + { + nw->Flags |= ( WINDOWDRAG | WINDOWDEPTH | SIZEBRIGHT | WINDOWSIZING ); + nw->IDCMPFlags |= NEWSIZE; + } + } + else + { + if( HackScreen->Width < 657 ) + { + nw->Flags |= ( WINDOWDRAG | WINDOWDEPTH ); + } + else + { + nw->Flags |= ( WINDOWDRAG | WINDOWDEPTH | SIZEBRIGHT ); + } + } + } + + if ( WINVERS_AMII && type == NHW_MAP ) + nw->Flags &= ~WINDOWSIZING; + + if ( type == NHW_MESSAGE && scrollmsg ) { + nw->Flags |= WINDOWDRAG|WINDOWDEPTH|SIZEBRIGHT|WINDOWSIZING; + nw->Flags &= ~BORDERLESS; + } + + /* No titles on a hires only screen except for messagewindow */ + if( !(WINVERS_AMIV && type == NHW_MAP) && !bigscreen && type != NHW_MESSAGE ) + nw->Title = 0; + + wd = (struct amii_WinDesc *)alloc(sizeof(struct amii_WinDesc)); + memset( wd, 0, sizeof( struct amii_WinDesc ) ); + + /* Both, since user may have changed the pen settings so respect those */ + if( WINVERS_AMII || WINVERS_AMIV ) + { + /* Special backfill for these types of layers */ + switch( type ) + { + case NHW_MESSAGE: + case NHW_STATUS: + case NHW_TEXT: + case NHW_MENU: + case NHW_BASE: + case NHW_OVER: + case NHW_MAP: + if( wd ) + { +#ifdef __GNUC__ + fillhook.h_Entry = (void *)&LayerFillHook; +#else + fillhook.h_Entry = (ULONG(*)())LayerFillHook; +#endif + fillhook.h_Data = (void *)type; + fillhook.h_SubEntry = 0; + wd->hook = alloc( sizeof( fillhook ) ); + memcpy( wd->hook, &fillhook, sizeof( fillhook ) ); + memcpy( wd->wintags, wintags, sizeof( wd->wintags) ); + wd->wintags[0].ti_Data = (long)wd->hook; + nw->Extension = (void *)wd->wintags; + } + break; + } + } + + /* Don't open MENU or TEXT windows yet */ + + if( type == NHW_MENU || type == NHW_TEXT ) + w = NULL; + else + w=OpenShWindow( (void *)nw ); + + if( w == NULL && type != NHW_MENU && type != NHW_TEXT ) + { + char buf[ 100 ]; + + sprintf( buf, "nw type (%d) dims l: %d, t: %d, w: %d, h: %d", + type, + nw->LeftEdge, nw->TopEdge, + nw->Width, nw->Height ); + raw_print( buf ); + panic("bad openwin %d",type); + } + + /* Check for an empty slot */ + + for(newid = 0; newid wincnt ) + wincnt = newid; + + /* Do common initialization */ + + amii_wins[newid] = wd; + + wd->newwin = NULL; + wd->win = w; + wd->type = type; + wd->wflags = 0; + wd->active = FALSE; + wd->curx=wd->cury = 0; + wd->resp = wd->canresp = wd->morestr = 0; /* CHECK THESE */ + wd->maxrow = new_wins[type].maxrow; + wd->maxcol = new_wins[type].maxcol; + + if( type != NHW_TEXT && type != NHW_MENU ) + { + if( TextsFont && ( type == NHW_MESSAGE || type == NHW_STATUS ) ) + { + SetFont(w->RPort, TextsFont); + txheight = w->RPort->TxHeight; + txwidth = w->RPort->TxWidth; + txbaseline = w->RPort->TxBaseline; + if( type == NHW_MESSAGE ) + { + if (scrollmsg ) + { + if( WINVERS_AMIV ) + { + WindowLimits( w, 100, w->BorderTop + + w->BorderBottom + + ((txheight+1)*2) + 1, 0, 0 ); + } + else + { + WindowLimits( w, w->Width, w->BorderTop + + w->BorderBottom + + ((txheight+1)*2) + 1, 0, 0 ); + } + } + else + { + WindowLimits( w, w->Width, w->BorderTop + + w->BorderBottom + + txheight + 2, 0, 0 ); + } + } + } + if ( type != NHW_MAP) { + SetFont(w->RPort, TextsFont); + } +#ifdef HACKFONT + else if( HackFont ) + SetFont(w->RPort, HackFont); +#endif + } + + /* Text and menu windows are not opened yet */ + if( w ) + { + wd->rows = ( w->Height - w->BorderTop - + w->BorderBottom - 2 ) / w->RPort->TxHeight; + wd->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 2 ) / w->RPort->TxWidth; + } + + /* Okay, now do the individual type initialization */ + + switch(type) + { + /* History lines for MESSAGE windows are stored in cw->data[?]. + * maxcol and maxrow are used as cursors. maxrow is the count + * of the number of history lines stored. maxcol is the cursor + * to the last line that was displayed by ^P. + */ + case NHW_MESSAGE: + SetMenuStrip(w, MenuStrip); + MsgScroll.TopEdge = HackScreen->WBorTop + scrfontysize + 1; + iflags.msg_history = wd->rows*10; + if (iflags.msg_history < 40) + iflags.msg_history = 40; + if (iflags.msg_history > 400) + iflags.msg_history = 400; + iflags.window_inited=TRUE; + wd->data = (char **)alloc( iflags.msg_history*sizeof( char * ) ); + memset( wd->data, 0, iflags.msg_history * sizeof( char * ) ); + wd->maxrow = wd->maxcol = 0; + /* Indicate that we have not positioned the cursor yet */ + wd->curx = -1; + break; + + /* A MENU contains a list of lines in wd->data[?]. These + * lines are created in amii_putstr() by reallocating the size + * of wd->data to hold enough (char *)'s. wd->rows is the + * number of (char *)'s allocated. wd->maxrow is the number + * used. wd->maxcol is used to track how wide the menu needs + * to be. wd->resp[x] contains the characters that correspond + * to selecting wd->data[x]. wd->resp[x] corresponds to + * wd->data[x] for any x. Elements of wd->data[?] that are not + * valid selections have the corresponding element of + * wd->resp[] set to a value of '\01'; i.e. a ^A which is + * not currently a valid keystroke for responding to any + * MENU or TEXT window. + */ + case NHW_MENU: + MenuScroll.TopEdge = HackScreen->WBorTop + scrfontysize + 1; + wd->resp=(char*)alloc(256); + wd->resp[0]=0; + wd->rows = wd->maxrow = 0; + wd->cols = wd->maxcol = 0; + wd->data = NULL; + break; + + /* See the explanation of MENU above. Except, wd->resp[] is not + * used for TEXT windows since there is no selection of a + * a line performed/allowed. The window is always full + * screen width. + */ + case NHW_TEXT: + MenuScroll.TopEdge = HackScreen->WBorTop + scrfontysize + 1; + wd->rows = wd->maxrow = 0; + wd->cols = wd->maxcol = amiIDisplay->cols; + wd->data = NULL; + wd->morestr = NULL; + break; + + /* The status window has only two lines. These are stored in + * wd->data[], and here we allocate the space for them. + */ + case NHW_STATUS: + SetMenuStrip(w, MenuStrip); + /* wd->cols is the number of characters which fit across the + * screen. + */ + wd->data=(char **)alloc(3*sizeof(char *)); + wd->data[0] = (char *)alloc(wd->cols + 10); + wd->data[1] = (char *)alloc(wd->cols + 10); + wd->data[2] = NULL; + break; + + /* NHW_OVER does not use wd->data[] or the other text + * manipulating members of the amii_WinDesc structure. + */ + case NHW_OVER: + SetMenuStrip(w, MenuStrip); + break; + + /* NHW_MAP does not use wd->data[] or the other text + * manipulating members of the amii_WinDesc structure. + */ + case NHW_MAP: + SetMenuStrip(w, MenuStrip); + if( WINVERS_AMIV ) + { + CO = (w->Width-w->BorderLeft-w->BorderRight)/mxsize; + LI = (w->Height-w->BorderTop-w->BorderBottom)/mysize; + amii_setclipped(); + SetFont( w->RPort, RogueFont); + SetAPen( w->RPort, C_WHITE); /* XXX not sufficient */ + SetBPen( w->RPort, C_BLACK); + SetDrMd( w->RPort, JAM2); + } + else + { + if( HackFont ) + SetFont( w->RPort, HackFont ); + } + break; + + /* The base window must exist until CleanUp() deletes it. */ + case NHW_BASE: + SetMenuStrip(w, MenuStrip); + /* Make our requesters come to our screen */ + { + register struct Process *myProcess = + (struct Process *) FindTask(NULL); + pr_WindowPtr = (struct Window *)(myProcess->pr_WindowPtr); + myProcess->pr_WindowPtr = (APTR) w; + } + + /* Need this for RawKeyConvert() */ + + ConsoleIO.io_Data = (APTR) w; + ConsoleIO.io_Length = sizeof( struct Window ); + ConsoleIO.io_Message.mn_ReplyPort = CreateMsgPort(); + if( OpenDevice("console.device", -1L, + (struct IORequest *) &ConsoleIO, 0L) != 0) + { + Abort(AG_OpenDev | AO_ConsoleDev); + } + + ConsoleDevice = (struct Library *) ConsoleIO.io_Device; + + KbdBuffered = 0; + +#ifdef HACKFONT + if( TextsFont ) + SetFont( w->RPort, TextsFont ); + else if( HackFont ) + SetFont( w->RPort, HackFont ); +#endif + txwidth = w->RPort->TxWidth; + txheight = w->RPort->TxHeight; + txbaseline = w->RPort->TxBaseline; + break; + + default: + panic("bad create_nhwindow( %d )\n",type); + return WIN_ERR; + } + + return( newid ); +} + +#ifdef __GNUC__ +#ifdef __PPC__ +int PPC_SM_Filter(void); +struct EmulLibEntry SM_Filter = {TRAP_LIB, 0, (int (*)(void)) PPC_SM_Filter}; +int PPC_SM_Filter(void) { + struct Hook *hk = (struct Hook*)REG_A0; + ULONG modeID = (ULONG)REG_A1; + struct ScreenModeRequester *smr = (struct ScreenModeRequester *)REG_A2; +#else +int SM_Filter(void) { + register struct Hook *hk asm("a0"); + register ULONG modeID asm("a1"); + register struct ScreenModeRequester *smr asm("a2"); +#endif +#else +int +#ifndef _DCC +__interrupt +#endif +__saveds __asm SM_Filter( + register __a0 struct Hook *hk, + register __a1 ULONG modeID, + register __a2 struct ScreenModeRequester *smr) +{ +#endif + struct DimensionInfo dims; + struct DisplayInfo disp; + DisplayInfoHandle handle; + handle = FindDisplayInfo(modeID); + if (handle) { + GetDisplayInfoData(handle, (char *)&dims, sizeof(dims), DTAG_DIMS, modeID); + GetDisplayInfoData(handle, (char *)&disp, sizeof(disp), DTAG_DISP, modeID); + if (!disp.NotAvailable && + dims.MaxDepth <= 8 && + dims.StdOScan.MaxX >= WIDTH-1 && + dims.StdOScan.MaxY >= SCREENHEIGHT-1) { + return 1; + } + } + return 0; +} + +/* Initialize the windowing environment */ + +void +amii_init_nhwindows(argcp,argv) + int *argcp; + char **argv; +{ + int i; + struct Screen *wbscr; + int forcenobig = 0; + + if( HackScreen ) + panic( "init_nhwindows() called twice", 0 ); + + /* run args & set bigscreen from -L(1)/-l(-1) */ + { + int lclargc = *argcp; + int t; + char **argv_in = argv; + char **argv_out = argv; + + for(t=1;t<=lclargc;t++){ + if(!strcmp("-L",*argv_in) || !strcmp("-l",*argv_in)){ + bigscreen = (*argv_in[1]=='l') ? -1 : 1; + /* and eat the flag */ + (*argcp)--; + } else { + *argv_out = *argv_in; /* keep the flag */ + argv_out++; + } + argv_in++; + } + *argv_out = 0; + } + + WIN_MESSAGE = WIN_ERR; + WIN_MAP = WIN_ERR; + WIN_STATUS = WIN_ERR; + WIN_INVEN = WIN_ERR; + WIN_BASE = WIN_ERR; + WIN_OVER = WIN_ERR; + + if ( (IntuitionBase = (struct IntuitionBase *) + OpenLibrary("intuition.library", amii_libvers )) == NULL) + { + Abort(AG_OpenLib | AO_Intuition); + } + + if ( (GfxBase = (struct GfxBase *) + OpenLibrary("graphics.library", amii_libvers )) == NULL) + { + Abort(AG_OpenLib | AO_GraphicsLib); + } + + if( (LayersBase = (struct Library *) + OpenLibrary("layers.library", amii_libvers )) == NULL) + { + Abort(AG_OpenLib | AO_LayersLib); + } + + if ((GadToolsBase = OpenLibrary("gadtools.library", amii_libvers)) == NULL) { + Abort(AG_OpenLib | AO_GadTools); + } + + if ((AslBase = OpenLibrary("asl.library", amii_libvers)) == NULL) { + Abort(AG_OpenLib); + } + + amiIDisplay=(struct amii_DisplayDesc *)alloc(sizeof(struct amii_DisplayDesc)); + memset( amiIDisplay, 0, sizeof( struct amii_DisplayDesc ) ); + + /* Use Intuition sizes for overscan screens... */ + + amiIDisplay->xpix = 0; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + if( wbscr = LockPubScreen( "Workbench" ) ) + { + amiIDisplay->xpix = wbscr->Width; + amiIDisplay->ypix = wbscr->Height; + UnlockPubScreen( NULL, wbscr ); + } + } +#endif + if( amiIDisplay->xpix == 0 ) + { + amiIDisplay->ypix = GfxBase->NormalDisplayRows; + amiIDisplay->xpix = GfxBase->NormalDisplayColumns; + } + + amiIDisplay->cols = amiIDisplay->xpix / FONTWIDTH; + + amiIDisplay->toplin=0; + amiIDisplay->rawprint=0; + amiIDisplay->lastwin=0; + + if( bigscreen == 0 ) + { + if( ( GfxBase->ActiView->ViewPort->Modes & LACE ) == LACE ) + { + amiIDisplay->ypix *= 2; + NewHackScreen.ViewModes |= LACE; + bigscreen = 1; + } + else if( GfxBase->NormalDisplayRows >= 300 || + amiIDisplay->ypix >= 300 ) + { + bigscreen = 1; + } + } + else if( bigscreen == -1 ) + { + bigscreen = 0; + forcenobig = 1; + } + else if( bigscreen ) + { + /* If bigscreen requested and we don't have enough rows in + * noninterlaced mode, switch to interlaced... + */ + if( GfxBase->NormalDisplayRows < 300 ) + { + amiIDisplay->ypix *= 2; + NewHackScreen.ViewModes |= LACE; + } + } + + if( !bigscreen ) + { + alwaysinvent = 0; + } + amiIDisplay->rows = amiIDisplay->ypix / FONTHEIGHT; + +#ifdef HACKFONT + /* + * Load the fonts that we need. + */ + + if( DiskfontBase = + OpenLibrary( "diskfont.library", amii_libvers ) ) + { + Hack80.ta_Name -= SIZEOF_DISKNAME; + HackFont = OpenDiskFont( &Hack80 ); + Hack80.ta_Name += SIZEOF_DISKNAME; + + /* Textsfont13 is filled in with "FONT=" settings. The default is + * courier/13. + */ + TextsFont = NULL; + if( bigscreen ) + TextsFont = OpenDiskFont( &TextsFont13 ); + + /* Try hack/8 for texts if no user specified font */ + if( TextsFont == NULL ) + { + Hack80.ta_Name -= SIZEOF_DISKNAME; + TextsFont = OpenDiskFont( &Hack80 ); + Hack80.ta_Name += SIZEOF_DISKNAME; + } + + /* If no fonts, make everything topaz 8 for non-view windows. + */ + Hack80.ta_Name = "topaz.font"; + RogueFont = OpenFont( &Hack80 ); + if(!RogueFont) panic("Can't get topaz:8"); + if( !HackFont || !TextsFont ) + { + if( !HackFont ) + { + HackFont = OpenFont( &Hack80 ); + if( !HackFont ) + panic( "Can't get a map font, topaz:8" ); + } + + if( !TextsFont ) + { + TextsFont = OpenFont( &Hack80 ); + if( !TextsFont ) + panic( "Can't open text font" ); + } + } + CloseLibrary(DiskfontBase); + DiskfontBase = NULL; + } +#endif + + /* Adjust getlin window size to font */ + + if (TextsFont) { + extern SHORT BorderVectors1[]; + extern SHORT BorderVectors2[]; + extern struct Gadget Gadget2; + extern struct Gadget String; + extern struct NewWindow StrWindow; + BorderVectors1[2] += (TextsFont->tf_XSize-8)*6; /* strlen("Cancel") == 6 */ + BorderVectors1[4] += (TextsFont->tf_XSize-8)*6; + BorderVectors1[5] += TextsFont->tf_YSize-8; + BorderVectors1[7] += TextsFont->tf_YSize-8; + BorderVectors2[2] += (TextsFont->tf_XSize-8)*6; + BorderVectors2[4] += (TextsFont->tf_XSize-8)*6; + BorderVectors2[5] += TextsFont->tf_YSize-8; + BorderVectors2[7] += TextsFont->tf_YSize-8; + Gadget2.TopEdge += TextsFont->tf_YSize-8; + Gadget2.Width += (TextsFont->tf_XSize-8)*6; + Gadget2.Height += TextsFont->tf_YSize-8; + String.LeftEdge += (TextsFont->tf_XSize-8)*6; + String.TopEdge += TextsFont->tf_YSize-8; + String.Width += TextsFont->tf_XSize-8; + String.Height += TextsFont->tf_YSize-8; + StrWindow.Width += (TextsFont->tf_XSize-8)*7; + StrWindow.Height += (TextsFont->tf_YSize-8)*2; /* Titlebar + 1 row of gadgets */ + } + + /* This is the size screen we want to open, within reason... */ + + NewHackScreen.Width = max( WIDTH, amiIDisplay->xpix ); + NewHackScreen.Height = max( SCREENHEIGHT, amiIDisplay->ypix ); + { + static char fname[18]; + sprintf(fname,"NetHack %d.%d.%d", VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL); + NewHackScreen.DefaultTitle=fname; + } +#if 0 + NewHackScreen.BlockPen = C_BLACK; + NewHackScreen.DetailPen = C_WHITE; +#endif +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + int i; + struct DimensionInfo dims; + DisplayInfoHandle handle; + struct DisplayInfo disp; + ULONG modeid = DEFAULT_MONITOR_ID|HIRES_KEY; + + NewHackScreen.Width = STDSCREENWIDTH; + NewHackScreen.Height = STDSCREENHEIGHT; + +#ifdef HACKFONT + if (TextsFont) { + NewHackScreen.Font = &TextsFont13; + } +#endif + + if ( amii_scrnmode == 0xffffffff ) { + struct ScreenModeRequester *SMR; +#ifdef __GNUC__ + SM_FilterHook.h_Entry = (void *)&SM_Filter; +#else + SM_FilterHook.h_Entry = (ULONG(*)())SM_Filter; +#endif + SM_FilterHook.h_Data = 0; + SM_FilterHook.h_SubEntry = 0; + SMR = AllocAslRequest(ASL_ScreenModeRequest,NULL); + if (AslRequestTags(SMR, + ASLSM_FilterFunc, (ULONG)&SM_FilterHook, + TAG_END)) + amii_scrnmode = SMR->sm_DisplayID; + else + amii_scrnmode = 0; + FreeAslRequest(SMR); + } + + if( forcenobig == 0 ) + { + if( ( wbscr = LockPubScreen( "Workbench" ) ) != NULL || + ( wbscr = LockPubScreen( NULL ) ) != NULL ) + { + /* Get the default pub screen's size */ + modeid = GetVPModeID( &wbscr->ViewPort ); + if( modeid == INVALID_ID || + ModeNotAvailable( modeid ) || + ( handle = FindDisplayInfo( modeid ) ) == NULL || + GetDisplayInfoData( handle, (char *)&dims, sizeof( dims ), + DTAG_DIMS, modeid ) <= 0 || + GetDisplayInfoData( handle, (char *)&disp, sizeof( disp ), + DTAG_DISP, modeid ) <= 0 ) + { + modeid = DEFAULT_MONITOR_ID|HIRES_KEY; + /* If the display database seems to not work, use the screen + * dimensions + */ + NewHackScreen.Height = wbscr->Height; + NewHackScreen.Width = wbscr->Width; + + /* + * Request LACE if it looks laced. For 2.1/3.0, we will get + * promoted to the users choice of modes (if promotion is allowed) + * If the user is using a dragable screen, things will get hosed + * but that is life... + */ + if( wbscr->ViewPort.Modes & LACE ) + NewHackScreen.ViewModes |= LACE; + modeid = -1; + } + else + { + /* Use the display database to get the correct information */ + if( disp.PropertyFlags & DIPF_IS_LACE ) + NewHackScreen.ViewModes |= LACE; + NewHackScreen.Height = dims.StdOScan.MaxY+1; + NewHackScreen.Width = dims.StdOScan.MaxX+1; + } + NewHackScreen.TopEdge = 0; + NewHackScreen.LeftEdge = 0; + + UnlockPubScreen( NULL, wbscr ); + } + } + + for( i = 0; scrntags[i].ti_Tag != TAG_DONE; ++i ) + { + switch( scrntags[i].ti_Tag ) + { + case SA_DisplayID: + if( !amii_scrnmode || ModeNotAvailable( amii_scrnmode ) ) + { + if( ModeNotAvailable( modeid ) ) + { + scrntags[i].ti_Tag = TAG_IGNORE; + break; + } + else + scrntags[i].ti_Data = (long)modeid; + } + else + modeid = scrntags[i].ti_Data = (long)amii_scrnmode; + if( ( handle = FindDisplayInfo( modeid ) ) != NULL && + GetDisplayInfoData( handle, (char *)&dims, sizeof( dims ), + DTAG_DIMS, modeid ) > 0 && + GetDisplayInfoData( handle, (char *)&disp, sizeof( disp ), + DTAG_DISP, modeid ) > 0 ) + { + if( disp.PropertyFlags & DIPF_IS_LACE ) + NewHackScreen.ViewModes |= LACE; + NewHackScreen.Height = dims.StdOScan.MaxY+1; + NewHackScreen.Width = dims.StdOScan.MaxX+1; + } + break; + + case SA_Pens: + scrntags[i].ti_Data = (long)flags.amii_dripens; + break; + } + } + } +#endif + + if( WINVERS_AMIV ) + amii_bmhd = ReadTileImageFiles( ); + else + memcpy( amii_initmap, amii_init_map, sizeof( amii_initmap ) ); + memcpy(flags.amii_curmap,amii_initmap,sizeof(flags.amii_curmap)); + + /* Find out how deep the screen needs to be, 32 planes is enough! */ + for( i = 0; i < 32; ++i ) + { + if( ( 1L << i ) >= amii_numcolors ) + break; + } + + NewHackScreen.Depth = i; + + /* If for some reason Height/Width became smaller than the required, + have the required one */ + if (NewHackScreen.Height < SCREENHEIGHT) + NewHackScreen.Height = SCREENHEIGHT; + if (NewHackScreen.Width < WIDTH) + NewHackScreen.Width = WIDTH; +#ifdef HACKFONT + i = max(TextsFont->tf_XSize, HackFont->tf_XSize); + if (NewHackScreen.Width < 80*i+4) + NewHackScreen.Width = 80*i+4; +#endif + + /* While openscreen fails try fewer colors to see if that is the problem. */ + while( ( HackScreen = OpenScreen( (void *)&NewHackScreen ) ) == NULL ) + { +#ifdef TEXTCOLOR + if( --NewHackScreen.Depth < 3 ) +#else + if( --NewHackScreen.Depth < 2 ) +#endif + Abort( AN_OpenScreen & ~AT_DeadEnd ); + } + amii_numcolors = 1L << NewHackScreen.Depth; + if( HackScreen->Height > 300 && forcenobig == 0 ) + bigscreen = 1; + else + bigscreen = 0; + +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + PubScreenStatus( HackScreen, 0 ); +#endif + + amiIDisplay->ypix = HackScreen->Height; + amiIDisplay->xpix = HackScreen->Width; + + LoadRGB4(&HackScreen->ViewPort, flags.amii_curmap, amii_numcolors ); + + VisualInfo = GetVisualInfo(HackScreen, TAG_END); + MenuStrip = CreateMenus(GTHackMenu, TAG_END); + LayoutMenus(MenuStrip, VisualInfo, GTMN_NewLookMenus, TRUE, TAG_END); + + /* Display the copyright etc... */ + + if( WIN_BASE == WIN_ERR ) + WIN_BASE = amii_create_nhwindow( NHW_BASE ); + amii_clear_nhwindow( WIN_BASE ); + amii_putstr( WIN_BASE, 0, "" ); + amii_putstr( WIN_BASE, 0, "" ); + amii_putstr( WIN_BASE, 0, "" ); + amii_putstr( WIN_BASE, 0, COPYRIGHT_BANNER_A); + amii_putstr( WIN_BASE, 0, COPYRIGHT_BANNER_B); + amii_putstr( WIN_BASE, 0, COPYRIGHT_BANNER_C); + amii_putstr( WIN_BASE, 0, ""); + + Initialized = 1; +} + +void +amii_sethipens( struct Window *w, int type, int attr ) +{ + switch( type ) + { + default: + SetAPen( w->RPort, attr ? C_RED : amii_otherAPen ); + SetBPen( w->RPort, C_BLACK ); + break; + case NHW_STATUS: + SetAPen( w->RPort, attr ? C_WHITE : amii_statAPen ); + SetBPen( w->RPort, amii_statBPen ); + break; + case NHW_MESSAGE: + SetAPen( w->RPort, attr ? C_WHITE : amii_msgAPen ); + SetBPen( w->RPort, amii_msgBPen ); + break; + case NHW_MENU: + SetAPen( w->RPort, attr ? C_BLACK : amii_menuAPen ); + SetBPen( w->RPort, amii_menuBPen ); + break; + case NHW_TEXT: + SetAPen( w->RPort, attr ? C_BLACK : amii_textAPen ); + SetBPen( w->RPort, amii_textBPen ); + case -2: + SetBPen( w->RPort, amii_otherBPen ); + SetAPen( w->RPort, attr ? C_RED : amii_otherAPen ); + break; + } +} + +void +amii_setfillpens( struct Window *w, int type ) +{ + switch( type ) + { + case NHW_MESSAGE: + SetAPen( w->RPort, amii_msgBPen ); + SetBPen( w->RPort, amii_msgBPen ); + break; + case NHW_STATUS: + SetAPen( w->RPort, amii_statBPen ); + SetBPen( w->RPort, amii_statBPen ); + break; + case NHW_MENU: + SetAPen( w->RPort, amii_menuBPen ); + SetBPen( w->RPort, amii_menuBPen ); + break; + case NHW_TEXT: + SetAPen( w->RPort, amii_textBPen ); + SetBPen( w->RPort, amii_textBPen ); + break; + case NHW_MAP: + case NHW_BASE: + case NHW_OVER: + default: + SetAPen( w->RPort, C_BLACK ); + SetBPen( w->RPort, C_BLACK ); + break; + case -2: + SetAPen( w->RPort, amii_otherBPen ); + SetBPen( w->RPort, amii_otherBPen ); + break; + } +} + +void +amii_setdrawpens( struct Window *w, int type ) +{ + switch( type ) + { + case NHW_MESSAGE: + SetAPen( w->RPort, amii_msgAPen ); + SetBPen( w->RPort, amii_msgBPen ); + break; + case NHW_STATUS: + SetAPen( w->RPort, amii_statAPen ); + SetBPen( w->RPort, amii_statBPen ); + break; + case NHW_MENU: + SetAPen( w->RPort, amii_menuAPen ); + SetBPen( w->RPort, amii_menuBPen ); + break; + case NHW_TEXT: + SetAPen( w->RPort, amii_textAPen ); + SetBPen( w->RPort, amii_textBPen ); + break; + case NHW_MAP: + case NHW_BASE: + case NHW_OVER: + SetAPen( w->RPort, C_WHITE ); + SetBPen( w->RPort, C_BLACK ); + break; + default: + SetAPen( w->RPort, amii_otherAPen ); + SetBPen( w->RPort, amii_otherBPen ); + break; + } +} + +/* Clear the indicated window */ + +void +amii_clear_nhwindow(win) + register winid win; +{ + register struct amii_WinDesc *cw; + register struct Window *w; + + if( reclip == 2 ) return; + + if( win == WIN_ERR || ( cw = amii_wins[win] ) == NULL ) + panic( winpanicstr, win, "clear_nhwindow" ); + + /* Clear the overview window too if it is displayed */ + if( WINVERS_AMIV && ( cw->type == WIN_MAP && WIN_OVER != WIN_ERR && reclip == 0 ) ) + { + amii_clear_nhwindow( WIN_OVER ); + } + + if( w = cw->win ) + SetDrMd( w->RPort, JAM2); + else + return; + + if( (cw->wflags & FLMAP_CURSUP ) ) + { + if( cw->type != NHW_MAP ) + cursor_off( win ); + else + cw->wflags &= ~FLMAP_CURSUP; + } + + amii_setfillpens( w, cw->type ); + SetDrMd( w->RPort, JAM2 ); + + if( cw->type == NHW_MENU || cw->type == NHW_TEXT ) + { + RectFill( w->RPort, w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight-1, + w->Height - w->BorderBottom-1 ); + } + else + { + if( cw->type == NHW_MESSAGE ) + { + amii_curs( win, 1, 0 ); + if( !scrollmsg ) + TextSpaces( w->RPort, cw->cols ); + } + else + { + RectFill( w->RPort, w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight-1, + w->Height - w->BorderBottom-1 ); + } + } + + cw->cury = 0; + cw->curx = 0; + amii_curs( win, 1, 0 ); +} + +/* Dismiss the window from the screen */ + +void +dismiss_nhwindow(win) + register winid win; +{ + register struct Window *w; + register struct amii_WinDesc *cw; + + if( win == WIN_ERR || ( cw = amii_wins[win] ) == NULL ) + { + panic(winpanicstr,win, "dismiss_nhwindow"); + } + + w = cw->win; + + if( w ) + { + /* All windows have this stuff attached to them. */ + if( cw->type == NHW_MAP || + cw->type == NHW_OVER || + cw->type == NHW_BASE || + cw->type == NHW_MESSAGE || + cw->type == NHW_STATUS ) + { + ClearMenuStrip( w ); + } + + /* Save where user like inventory to appear */ + if( win == WIN_INVEN ) + { + lastinvent.MinX = w->LeftEdge; + lastinvent.MinY = w->TopEdge; + lastinvent.MaxX = w->Width; + lastinvent.MaxY = w->Height; + } + + /* Close the window */ + CloseShWindow( w ); + cw->win = NULL; + + /* Free copy of NewWindow structure for TEXT/MENU windows. */ + if( cw->newwin ) + FreeNewWindow( (void *)cw->newwin ); + cw->newwin = NULL; + } +} + +void +amii_exit_nhwindows(str) + const char *str; +{ + /* Seems strange to have to do this... but we need the BASE window + * left behind... + */ + kill_nhwindows( 0 ); + if( WINVERS_AMIV ) + FreeTileImageFiles( ); + + if( str ) + { + raw_print( "" ); /* be sure we're not under the top margin */ + raw_print( str ); + } +} + +void +amii_display_nhwindow(win,blocking) + winid win; + boolean blocking; +{ + menu_item *mip; + int cnt; + static int lastwin = -1; + struct amii_WinDesc *cw; + + if( !Initialized ) + return; + lastwin = win; + + if( win == WIN_ERR || ( cw = amii_wins[win] ) == NULL ) + panic(winpanicstr,win,"display_nhwindow"); + + if( cw->type == NHW_MESSAGE ) + cw->wflags &= ~FLMAP_SKIP; + + if( cw->type == NHW_MESSAGE || cw->type == NHW_STATUS ) + return; + + if( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) + { + flush_glyph_buffer( amii_wins[ WIN_MAP ]->win ); + } + + if( cw->type == NHW_MENU || cw->type == NHW_TEXT ) + { + cnt = DoMenuScroll( win, blocking, PICK_ONE, &mip ); + } + else if( cw->type==NHW_MAP ) + { + amii_end_glyphout( win ); + /* Do more if it is time... */ + if( blocking == TRUE && amii_wins[ WIN_MESSAGE ]->curx ) + { + outmore( amii_wins[ WIN_MESSAGE ] ); + } + } +} + +void +amii_curs(window, x, y) +winid window; +register int x, y; /* not xchar: perhaps xchar is unsigned and + curx-x would be unsigned as well */ +{ + register struct amii_WinDesc *cw; + register struct Window *w; + register struct RastPort *rp; + + if( window == WIN_ERR || ( cw = amii_wins[window] ) == NULL ) + panic(winpanicstr, window, "curs"); + if( (w = cw->win) == NULL ) + { + if( cw->type == NHW_MENU || cw->type == NHW_TEXT ) + return; + else + panic( "No window open yet in curs() for winid %d\n", window ); + } + amiIDisplay->lastwin = window; + + /* Make sure x is within bounds */ + if( x > 0 ) + --x; /* column 0 is never used */ + else + x = 0; + + cw->curx = x; + cw->cury = y; + +#ifdef DEBUG + if( x<0 || y<0 || y >= cw->rows || x >= cw->cols ) + { + char *s = "[unknown type]"; + switch(cw->type) + { + case NHW_MESSAGE: s = "[topl window]"; break; + case NHW_STATUS: s = "[status window]"; break; + case NHW_MAP: s = "[map window]"; break; + case NHW_MENU: s = "[menu window]"; break; + case NHW_TEXT: s = "[text window]"; break; + case NHW_BASE: s = "[base window]"; break; + case NHW_OVER: s = "[overview window]"; break; + } + impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y); + return; + } +#endif + +#ifdef CLIPPING + if(clipping && cw->type == NHW_MAP) + { + x -= clipx; + y -= clipy; + } +#endif + + /* Output all saved output before doing cursor movements for MAP */ + + if( cw->type == NHW_MAP ) + { + flush_glyph_buffer( w ); + } + + /* Actually do it */ + + rp = w->RPort; + if( cw->type == NHW_MENU ) + { + if( WINVERS_AMIV ) + { + if( window == WIN_INVEN ) + { + Move( rp, (x * rp->TxWidth) + w->BorderLeft + 1 + pictdata.xsize + 4, + (y * max(rp->TxHeight,pictdata.ysize + 3) ) + + rp->TxBaseline + pictdata.ysize - rp->TxHeight + w->BorderTop + 4 ); + } + else + { + Move( rp, (x * rp->TxWidth) + w->BorderLeft + 1, + (y * rp->TxHeight) + rp->TxBaseline + w->BorderTop + 1 ); + } + } + else + { + Move( rp, (x * rp->TxWidth) + w->BorderLeft + 1, + (y*rp->TxHeight ) + rp->TxBaseline + w->BorderTop + 1 ); + } + } + else if( cw->type == NHW_TEXT ) + { + Move( rp, (x * rp->TxWidth) + w->BorderLeft + 1, + (y*rp->TxHeight ) + rp->TxBaseline + w->BorderTop + 1 ); + } + else if( cw->type == NHW_MAP || cw->type == NHW_BASE ) + { + /* These coordinate calculations must be synced with those + * in flush_glyph_buffer() in winchar.c. curs_on_u() will + * use this code, all other drawing occurs through the glyph + * code. In order for the cursor to appear on top of the hero, + * the code must compute X,Y in the same manner relative to + * the RastPort coordinates. + * + * y = w->BorderTop + (g_nodes[i].y-2) * rp->TxHeight + + * rp->TxBaseline + 1; + * x = g_nodes[i].x * rp->TxWidth + w->BorderLeft; + */ + + if( WINVERS_AMIV ) + { + if( cw->type == NHW_MAP ) + { + if(Is_rogue_level(&u.uz)){ +#if 0 +int qqx= (x * w->RPort->TxWidth) + w->BorderLeft; +int qqy= w->BorderTop + ( (y+1) * w->RPort->TxHeight ) + 1; +printf("pos: (%d,%d)->(%d,%d)\n",x,y,qqx,qqy); +#endif + SetAPen(w->RPort,C_WHITE); /* XXX should be elsewhere (was 4)*/ + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft, + w->BorderTop + ( (y+1) * w->RPort->TxHeight ) + 1 ); + } else { + Move( rp, (x * mxsize) + w->BorderLeft, + w->BorderTop + ( (y+1) * mysize ) + 1 ); + } + } + else + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft, + w->BorderTop + ( (y + 1) * w->RPort->TxHeight ) + + w->RPort->TxBaseline + 1 ); + } + } + else + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft, + w->BorderTop + ( y * w->RPort->TxHeight ) + + w->RPort->TxBaseline + 1 ); + } + } + else if( WINVERS_AMIV && cw->type == NHW_OVER ) + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft + 2, + w->BorderTop + w->RPort->TxBaseline + 3 ); + } + else if( cw->type == NHW_MESSAGE && !scrollmsg ) + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft + 2, + w->BorderTop + w->RPort->TxBaseline + 3 ); + } + else if( cw->type == NHW_STATUS ) + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft + 2, + (y*(w->RPort->TxHeight+1)) + w->BorderTop + + w->RPort->TxBaseline + 1 ); + } + else + { + Move( rp, (x * w->RPort->TxWidth) + w->BorderLeft + 2, + (y*w->RPort->TxHeight) + w->BorderTop + + w->RPort->TxBaseline + 1 ); + } +} + +void +amii_set_text_font( name, size ) + char *name; + int size; +{ + register int i; + register struct amii_WinDesc *cw; + int osize = TextsFont13.ta_YSize; + static char nname[ 100 ]; + + strncpy( nname, name, sizeof( nname ) - 1 ); + nname[ sizeof( nname ) - 1 ] = 0; + + TextsFont13.ta_Name = nname; + TextsFont13.ta_YSize = size; + + /* No alternate text font allowed for 640x269 or smaller */ + if( !HackScreen || !bigscreen ) + return; + + /* Look for windows to set, and change them */ + + if( DiskfontBase = + OpenLibrary( "diskfont.library", amii_libvers ) ) + { + TextsFont = OpenDiskFont( &TextsFont13 ); + for( i = 0; TextsFont && i < MAXWIN; ++i ) + { + if( (cw = amii_wins[ i ]) && cw->win != NULL ) + { + switch( cw->type ) + { + case NHW_STATUS: + MoveWindow( cw->win, 0, -( size - osize ) * 2 ); + SizeWindow( cw->win, 0, ( size - osize ) * 2 ); + SetFont( cw->win->RPort, TextsFont ); + break; + case NHW_MESSAGE: + case NHW_MAP: + case NHW_BASE: + case NHW_OVER: + SetFont( cw->win->RPort, TextsFont ); + break; + } + } + } + } + CloseLibrary(DiskfontBase); + DiskfontBase = NULL; +} + +void +kill_nhwindows( all ) + register int all; +{ + register int i; + register struct amii_WinDesc *cw; + + /* Foreach open window in all of amii_wins[], CloseShWindow, free memory */ + + for( i = 0; i < MAXWIN; ++i ) + { + if( (cw = amii_wins[ i ]) && (cw->type != NHW_BASE || all) ) + { + amii_destroy_nhwindow( i ); + } + } +} + +void +amii_cl_end( cw, curs_pos ) + register struct amii_WinDesc *cw; + register int curs_pos; +{ + register struct Window *w = cw->win; + register int oy, ox; + + if( !w ) + panic("NULL window pointer in amii_cl_end()"); + + oy = w->RPort->cp_y; + ox = w->RPort->cp_x; + + TextSpaces( w->RPort, cw->cols - curs_pos ); + + Move( w->RPort, ox, oy ); +} + +void +cursor_off( window ) + winid window; +{ + register struct amii_WinDesc *cw; + register struct Window *w; + register struct RastPort *rp; + int curx, cury; + int x, y; + long dmode; + short apen, bpen; + unsigned char ch; + + if( window == WIN_ERR || ( cw = amii_wins[window] ) == NULL ) + { + iflags.window_inited=0; + panic(winpanicstr,window, "cursor_off"); + } + + if( !(cw->wflags & FLMAP_CURSUP ) ) + return; + + w = cw->win; + + if( !w ) + return; + + cw->wflags &= ~FLMAP_CURSUP; + rp = w->RPort; + + /* Save the current information */ + curx = rp->cp_x; + cury = rp->cp_y; + x = cw->cursx; + y = cw->cursy; + dmode = rp->DrawMode; + apen = rp->FgPen; + bpen = rp->BgPen; + SetAPen( rp, cw->curs_apen ); + SetBPen( rp, cw->curs_bpen ); + SetDrMd( rp, COMPLEMENT ); +/*printf("CURSOR OFF: %d %d\n",x,y);*/ + + if( WINVERS_AMIV && cw->type == NHW_MAP) + { + cursor_common(rp, x, y); + if(Is_rogue_level(&u.uz)) + Move(rp,curx,cury); + } + else + { + ch = CURSOR_CHAR; + Move( rp, x, y ); + Text( rp, &ch, 1 ); + + /* Put back the other stuff */ + + Move( rp, curx, cury ); + } + SetDrMd( rp, dmode ); + SetAPen( rp, apen ); + SetBPen( rp, bpen ); +} + +void +cursor_on( window ) + winid window; +{ + int x, y; + register struct amii_WinDesc *cw; + register struct Window *w; + register struct RastPort *rp; + unsigned char ch; + long dmode; + short apen, bpen; + + if( window == WIN_ERR || ( cw = amii_wins[window] ) == NULL ) + { + /* tty does this differently - is this OK? */ + iflags.window_inited=0; + panic(winpanicstr,window, "cursor_on"); + } + +/*printf("CURSOR ON: %d %d\n",cw->win->RPort->cp_x, cw->win->RPort->cp_y);*/ + if( (cw->wflags & FLMAP_CURSUP ) ) + cursor_off( window ); + + w = cw->win; + + if( !w ) + return; + + cw->wflags |= FLMAP_CURSUP; + rp = w->RPort; + + /* Save the current information */ + +#ifdef DISPMAP + if( WINVERS_AMIV && cw->type == NHW_MAP && !Is_rogue_level(&u.uz)) + x = cw->cursx = (rp->cp_x & -8) + 8; + else +#endif + x = cw->cursx = rp->cp_x; + y = cw->cursy = rp->cp_y; + apen = rp->FgPen; + bpen = rp->BgPen; + dmode = rp->DrawMode; + + /* Draw in complement mode. The cursor body will be C_WHITE */ + + cw->curs_apen = 0xff; /* Last color/all planes, regardless of depth */ + cw->curs_bpen = 0xff; + SetAPen( rp, cw->curs_apen ); + SetBPen( rp, cw->curs_bpen ); + SetDrMd( rp, COMPLEMENT ); + if( WINVERS_AMIV && cw->type == NHW_MAP) + { + cursor_common(rp, x, y); + } + else + { + Move( rp, x, y ); + ch = CURSOR_CHAR; + Text( rp, &ch, 1 ); + Move( rp, x, y ); + } + + SetDrMd( rp, dmode ); + SetAPen( rp, apen ); + SetBPen( rp, bpen ); +} + +static void +cursor_common(rp, x, y) + struct RastPort *rp; + int x,y; +{ + int x1,x2,y1,y2; + + if(Is_rogue_level(&u.uz)){ + x1 = x-2; y1 = y-rp->TxHeight; + x2 = x+rp->TxWidth+1; y2 = y+3; +/*printf("COMM: (%d %d) (%d %d) (%d %d) (%d %d)\n",x1,y1,x2,y2,x1+2,y1+2,x2-2,y2-2);*/ + } else { + x1 = x; y1 = y-mysize-1; + x2 = x+mxsize-1; y2 = y-2; + RectFill(rp, x1, y1, x2, y2); + } + + RectFill(rp, x1+2, y1+2, x2-2, y2-2); +} + +void amii_suspend_nhwindows( str ) + const char *str; +{ + if( HackScreen ) + ScreenToBack( HackScreen ); +} + +void amii_resume_nhwindows() +{ + if( HackScreen ) + ScreenToFront( HackScreen ); +} + +void amii_bell() +{ + DisplayBeep( NULL ); +} + +void +removetopl(cnt) + int cnt; +{ + struct amii_WinDesc *cw=amii_wins[WIN_MESSAGE]; + /* NB - this is sufficient for + * yn_function, but that's it + */ + if(cw->curx < cnt)cw->curx=0; + else cw->curx -= cnt; + + amii_curs(WIN_MESSAGE, cw->curx+1, cw->cury); + amii_cl_end(cw, cw->curx); +} +/*#endif /* AMIGA_INTUITION */ + +#ifdef PORT_HELP +void +port_help() +{ + display_file( PORT_HELP, 1 ); +} +#endif + +/* + * print_glyph + * + * Print the glyph to the output device. Don't flush the output device. + * + * Since this is only called from show_glyph(), it is assumed that the + * position and glyph are always correct (checked there)! + */ + +void +amii_print_glyph(win,x,y,glyph) + winid win; + xchar x,y; + int glyph; +{ + struct amii_WinDesc *cw; + uchar ch; + int color, och; + extern const int zapcolors[]; + unsigned special; + + /* In order for the overview window to work, we can not clip here */ + if( !WINVERS_AMIV ) + { +#ifdef CLIPPING + /* If point not in visible part of window just skip it */ + if( clipping ) + { + if( x <= clipx || y < clipy || x >= clipxmax || y >= clipymax ) + return; + } +#endif + } + + if( win == WIN_ERR || (cw=amii_wins[win]) == NULL || cw->type != NHW_MAP) + { + panic(winpanicstr,win,"amii_print_glyph"); + } + +#if 0 +{ +static int x=-1; +if(u.uz.dlevel != x){ + fprintf(stderr,"lvlchg: %d (%d)\n",u.uz.dlevel,Is_rogue_level(&u.uz)); + x = u.uz.dlevel; +} +} +#endif + if( + WINVERS_AMIV +#ifdef REINCARNATION + && !Is_rogue_level(&u.uz) +#endif + ) + { + amii_curs(win,x,y); + amiga_print_glyph(win,0,glyph); + } + else /* AMII, or Rogue level in either version */ + { + /* map glyph to character and color */ + mapglyph(glyph, &och, &color, &special, x, y); + /* XXX next if should be ifdef REINCARNATION */ + ch = (uchar)och; + if( WINVERS_AMIV ){ /* implies Rogue level here */ + amii_curs(win,x,y); + amiga_print_glyph(win,NO_COLOR,ch + 10000); + } else { + /* Move the cursor. */ + amii_curs(win,x,y+2); + +#ifdef TEXTCOLOR + /* Turn off color if rogue level. */ +# ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) + color = NO_COLOR; +# endif + + amiga_print_glyph(win,color,ch); +#else + g_putch(ch); /* print the character */ +#endif + cw->curx++; /* one character over */ + } + } +} + +/* Make sure the user sees a text string when no windowing is available */ + +void +amii_raw_print(s) + register const char *s; +{ + int argc = 0; + + if( !s ) + return; + if(amiIDisplay) + amiIDisplay->rawprint++; + + if (!Initialized) { /* Not yet screen open ... */ + puts(s); + fflush(stdout); + return; + } + + if( Initialized == 0 && WIN_BASE == WIN_ERR ) + init_nhwindows(&argc, (char **)0); + + if( amii_rawprwin != WIN_ERR ) + amii_putstr( amii_rawprwin, 0, s ); + else if( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) + amii_putstr( WIN_MAP, 0, s ); + else if( WIN_BASE != WIN_ERR && amii_wins[ WIN_BASE ] ) + amii_putstr( WIN_BASE, 0, s ); + else + { + puts( s); + fflush(stdout); + } +} + +/* Make sure the user sees a bold text string when no windowing + * is available + */ + +void +amii_raw_print_bold(s) + register const char *s; +{ + int argc = 0; + + if( !s ) + return; + + if(amiIDisplay) + amiIDisplay->rawprint++; + + if (!Initialized) { /* Not yet screen open ... */ + puts(s); + fflush(stdout); + return; + } + + if( Initialized == 0 && WIN_BASE == WIN_ERR ) + init_nhwindows(&argc, (char **)0); + + if( amii_rawprwin != WIN_ERR ) + amii_putstr( amii_rawprwin, 1, s ); + else if( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) + amii_putstr( WIN_MAP, 1, s ); + else if( WIN_BASE != WIN_ERR && amii_wins[ WIN_BASE ] ) + amii_putstr( WIN_BASE, 1, s ); + else + { + printf("\33[1m%s\33[0m\n",s); + fflush(stdout); + } +} + +/* Rebuild/update the inventory if the window is up. + */ +void +amii_update_inventory() +{ + register struct amii_WinDesc *cw; + + if( WIN_INVEN != WIN_ERR && ( cw = amii_wins[ WIN_INVEN ] ) && + cw->type == NHW_MENU && cw->win ) + { + display_inventory( NULL, FALSE ); + } +} + +/* Humm, doesn't really do anything useful */ + +void +amii_mark_synch() +{ + if(!amiIDisplay) + fflush(stderr); +/* anything else? do we need this much? */ +} + +/* Wait for everything to sync. Nothing is asynchronous, so we just + * ask for a key to be pressed. + */ +void +amii_wait_synch() +{ + if(!amiIDisplay || amiIDisplay->rawprint) + { + if(amiIDisplay) amiIDisplay->rawprint=0; + } + else + { + if( WIN_MAP != WIN_ERR ) + { + display_nhwindow(WIN_MAP,TRUE); + flush_glyph_buffer( amii_wins[ WIN_MAP ]->win ); + } + } +} + +void +amii_setclipped() +{ +#ifdef CLIPPING + clipping = TRUE; + clipx=clipy=0; + clipxmax=CO; + clipymax=LI; +/* some of this is now redundant with top of amii_cliparound XXX */ +#endif +} + +/* XXX still to do: suppress scrolling if we violate the boundary but the + * edge of the map is already displayed + */ +void +amii_cliparound(x,y) + register int x,y; +{ + extern boolean restoring; +#ifdef CLIPPING + int oldx = clipx, oldy = clipy; + int oldxmax = clipxmax, oldymax = clipymax; + int COx, LIx; +#define SCROLLCNT 1 /* Get there in 3 moves... */ + int scrollcnt = SCROLLCNT; /* ...or 1 if we changed level */ + if (!clipping) /* And 1 in anycase, cleaner, simpler, quicker */ + return; + + if(Is_rogue_level(&u.uz)){ + struct Window *w = amii_wins[WIN_MAP]->win; + struct RastPort *rp = w->RPort; + + COx = (w->Width-w->BorderLeft-w->BorderRight)/rp->TxWidth; + LIx = (w->Height-w->BorderTop-w->BorderBottom)/rp->TxHeight; + }else{ + COx = CO; + LIx = LI; + } + /* + * On a level change, move the clipping region so that for a + * reasonablely large window extra motion is avoided; for + * the rogue level hopefully this means no motion at all. + */ + { + static d_level saved_level = {127,127}; /* XXX */ + + if(!on_level(&u.uz, &saved_level)){ + scrollcnt = 1; /* jump with blanking */ + clipx=clipy=0; + clipxmax = COx; clipymax = LIx; + saved_level = u.uz; /* save as new current level */ + } + } + + if (x <= clipx + xclipbord ) { + clipx = max(0, x - (clipxmax - clipx)/2 ); + clipxmax = clipx + COx; + } + else if (x > clipxmax - xclipbord ) { + clipxmax = min(COLNO, x + (clipxmax - clipx)/2 ); + clipx = clipxmax - COx; + } + + if (y <= clipy + yclipbord ) { + clipy = max(0, y - (clipymax - clipy) / 2); + clipymax = clipy + LIx; + } + else if (y > clipymax - yclipbord ) { + clipymax = min(ROWNO, y + (clipymax - clipy) / 2); + clipy = clipymax - LIx; + } + + reclip = 1; + if (clipx != oldx || clipy != oldy || clipxmax != oldxmax || clipymax != oldymax ) + { +#ifndef NOSCROLLRASTER + struct Window *w = amii_wins[ WIN_MAP ]->win; + struct RastPort *rp = w->RPort; + int xdelta, ydelta, xmod, ymod, i; + int incx, incy, mincx, mincy; + int savex, savey, savexmax, saveymax; + int scrx, scry; + + if(Is_rogue_level(&u.uz)){ + scrx = rp->TxWidth; + scry = rp->TxHeight; + } else { + scrx = mxsize; + scry = mysize; + } + + /* Ask that the glyph routines not draw the overview window */ + reclip = 2; + cursor_off( WIN_MAP ); + + /* Compute how far we are moving in terms of tiles */ + mincx = clipx - oldx ; + mincy = clipy - oldy ; + + /* How many tiles to get there in SCROLLCNT moves */ + incx = ( clipx - oldx )/scrollcnt; + incy = ( clipy - oldy )/scrollcnt; + + /* If less than SCROLLCNT tiles, then move by 1 tile if moving at all */ + if( incx == 0 ) incx = (mincx != 0); + if( incy == 0 ) incy = (mincy != 0); + + /* Get count of pixels to move each iteration and final pixel count */ + xdelta = ((clipx-oldx )*scrx) / scrollcnt; + xmod = ((clipx-oldx )*scrx) % scrollcnt; + ydelta = ((clipy-oldy )*scry) / scrollcnt; + ymod = ((clipy-oldy )*scry) % scrollcnt; + + /* Preserve the final move location */ + savex = clipx; + savey = clipy; + saveymax = clipymax; + savexmax = clipxmax; + + /* + * Set clipping rectangle to be just the region that will be exposed so + * that drawing will be faster + */ +#if 0 /* Doesn't seem to work quite the way it should */ + /* In some cases hero is 'centered' offscreen */ + if( xdelta < 0 ) + { + clipx = oldx; + clipxmax = clipx + incx; + } + else if( xdelta > 0 ) + { + clipxmax = oldxmax; + clipx = clipxmax - incx; + } + else + { + clipx = oldx; + clipxmax = oldxmax; + } + + if( ydelta < 0 ) + { + clipy = oldy; + clipymax = clipy + incy; + } + else if( ydelta > 0 ) + { + clipymax = oldymax; + clipy = clipymax - incy; + } + else + { + clipy = oldy; + clipymax = oldymax; + } +#endif + /* Now, in scrollcnt moves, move the picture toward the final view */ + for( i = 0; i < scrollcnt; ++i ) + { +#ifdef DISPMAP + if( i == scrollcnt - 1 && (xmod != 0 || ymod != 0) && + (xdelta != 0 || ydelta != 0) ) + { + incx += (clipx - oldx)%scrollcnt; + incy += (clipy - oldy)%scrollcnt; + xdelta += xmod; + ydelta += ymod; + } +#endif + /* Scroll the raster if we are scrolling */ + if( xdelta != 0 || ydelta != 0 ) + { + ScrollRaster( rp, xdelta, ydelta, + w->BorderLeft, w->BorderTop, + w->Width - w->BorderRight - 1, + w->Height - w->BorderBottom - 1 ); + + if( mincx == 0 ) incx = 0; + else mincx -= incx; + + clipx += incx; + clipxmax += incx; + + if( mincy == 0 ) incy = 0; + else mincy -= incy; + + clipy += incy; + clipymax += incy; + + /* Draw the exposed portion */ + if (on_level(&u.uz0, &u.uz) && !restoring) + (void) doredraw(); + flush_glyph_buffer( amii_wins[ WIN_MAP ]->win ); + } + } + + clipx = savex; + clipy = savey; + clipymax = saveymax; + clipxmax = savexmax; +#endif + if (on_level(&u.uz0, &u.uz) && !restoring && moves > 1) + (void) doredraw(); + flush_glyph_buffer( amii_wins[ WIN_MAP ]->win ); + } + reclip = 0; +#endif +} + +void +flushIDCMP( port ) + struct MsgPort *port; +{ + struct Message *msg; + while( msg = GetMsg( port ) ) + ReplyMsg( msg ); +} diff --git a/sys/amiga/winkey.c b/sys/amiga/winkey.c new file mode 100644 index 0000000..7f79864 --- /dev/null +++ b/sys/amiga/winkey.c @@ -0,0 +1,106 @@ +/* SCCS Id: @(#)winkey.c 3.1 93/04/02 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +amii_nh_poskey(x, y, mod) + int*x, *y, *mod; +{ + struct amii_WinDesc *cw; + WETYPE type; + struct RastPort *rp; + struct Window *w; + + if( cw = amii_wins[WIN_MESSAGE] ) + { + cw->wflags &= ~FLMAP_SKIP; + if( scrollmsg ) + cw->wflags |= FLMSG_FIRST; + cw->disprows = 0; + } + + if( WIN_MAP != WIN_ERR && (cw = amii_wins[ WIN_MAP ]) && ( w = cw->win ) ) + { + cursor_on( WIN_MAP ); + } + else + panic( "no MAP window opened for nh_poskey\n" ); + + rp = w->RPort; + + while( 1 ) + { + type = WindowGetevent( ); + if( type == WEMOUSE ) + { + *mod = CLICK_1; + if( lastevent.un.mouse.qual ) + *mod = 0; + + /* X coordinates are 1 based, Y are 1 based. */ + *x = ( (lastevent.un.mouse.x - w->BorderLeft) / mxsize ) + 1; + *y = ( ( lastevent.un.mouse.y - w->BorderTop - MAPFTBASELN ) / + mysize ) + 1; +#ifdef CLIPPING + if( clipping ) + { + *x += clipx; + *y += clipy; + } +#endif + return( 0 ); + } + else if( type == WEKEY ) + { + lastevent.type = WEUNK; + return( lastevent.un.key ); + } + } +} + +int +amii_nhgetch() +{ + int ch; + struct amii_WinDesc *cw=amii_wins[WIN_MESSAGE]; + + if( WIN_MAP != WIN_ERR && amii_wins[ WIN_MAP ] ) + { + cursor_on( WIN_MAP ); + } + if(cw) + cw->wflags &= ~FLMAP_SKIP; + + ch = WindowGetchar(); + return( ch ); +} + +void +amii_get_nh_event() +{ + /* nothing now - later I have no idea. Is this just a Mac hook? */ +} + +void +amii_getret() +{ + register int c; + + raw_print( "" ); + raw_print( "Press Return..." ); + + c = 0; + + while( c != '\n' && c != '\r' ) + { + if( HackPort ) + c = WindowGetchar(); + else + c = getchar(); + } + return; +} + diff --git a/sys/amiga/winmenu.c b/sys/amiga/winmenu.c new file mode 100644 index 0000000..b2cf1cf --- /dev/null +++ b/sys/amiga/winmenu.c @@ -0,0 +1,1597 @@ +/* SCCS Id: @(#)winmenu.c 3.2 96/02/17 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993,1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +/* Start building the text for a menu */ +void +amii_start_menu(window) + register winid window; +{ + register int i; + register struct amii_WinDesc *cw; + register amii_menu_item *mip; + + if(window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) + panic(winpanicstr,window, "start_menu"); + + amii_clear_nhwindow(window); + + if( cw->data && ( cw->type == NHW_MESSAGE || + cw->type == NHW_MENU || cw->type == NHW_TEXT ) ) + { + for( i = 0; i < cw->maxrow; ++i ) + { + if( cw->data[ i ] ) + free( cw->data[ i ] ); + } + free( cw->data ); + cw->data = NULL; + } + + for( mip = cw->menu.items, i = 0; (mip = cw->menu.items) && i < cw->menu.count; ++i ) + { + cw->menu.items = mip->next; + free( mip ); + } + + cw->menu.items = 0; + cw->menu.count = 0; + cw->menu.chr = 'a'; + + if( cw->morestr ) free( cw->morestr ); + cw->morestr = NULL; + + if( window == WIN_INVEN && cw->win != NULL ) + { + if( alwaysinvent ) + cw->wasup = 1; + } + cw->cury = cw->rows = cw->maxrow = cw->maxcol = 0; + return; +} + +/* Add a string to a menu */ +void +amii_add_menu(window,glyph, id, ch, gch, attr, str, preselected) + register winid window; + register int glyph; + register const anything *id; + register char ch; + register char gch; + register int attr; + register const char *str; + register BOOLEAN_P preselected; +{ + register struct amii_WinDesc *cw; + amii_menu_item *mip; + char buf[ 4+BUFSZ ]; + + if(str == NULL)return; + + if(window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU) + panic(winpanicstr,window, "add_menu"); + + mip = (amii_menu_item *)alloc( sizeof( *mip ) ); + mip->identifier = *id; + mip->selected = preselected; + mip->attr = attr; + mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : glyph; + mip->selector = 0; + mip->gselector = gch; + mip->count = -1; + + if (id->a_void && !ch && cw->menu.chr != 0) + { + ch = cw->menu.chr++; + if( ch == 'z' ) + cw->menu.chr = 'A'; + if( ch == 'Z' ) + cw->menu.chr = 0; + } + + mip->canselect = ( id->a_void != 0 ); + + if( id->a_void && ch != '\0') + { + Sprintf( buf, "%c - %s", ch, str ); + str = buf; + mip->canselect = 1; + } + + mip->selector = ch; + + amii_putstr( window, attr, str ); + + mip->str = cw->data[ cw->cury - 1 ]; + cw->menu.count++; + + mip->next = NULL; + + if( cw->menu.items == 0 ) + cw->menu.last = cw->menu.items = mip; + else + { + cw->menu.last->next = mip; + cw->menu.last = mip; + } +} + +/* Done building a menu. */ + +void +amii_end_menu(window,morestr) + register winid window; + register const char *morestr; +{ + register struct amii_WinDesc *cw; + + if(window == WIN_ERR || (cw=amii_wins[window]) == NULL + || cw->type != NHW_MENU ) + panic(winpanicstr,window, "end_menu"); + + if( morestr && *morestr ) + { + anything any; +#define PROMPTFIRST /* Define this to have prompt first */ +#ifdef PROMPTFIRST + amii_menu_item *mip; + int i; + char *t; + mip = cw->menu.last; +#endif + any.a_void = 0; + amii_add_menu( window, NO_GLYPH, &any, 0, 0, ATR_NONE, morestr, + MENU_UNSELECTED); +#ifdef PROMPTFIRST /* Do some shuffling. Last first, push others one forward */ + mip->next = NULL; + cw->menu.last->next = cw->menu.items; + cw->menu.items = cw->menu.last; + cw->menu.last = mip; + t = cw->data[cw->cury-1]; + for (i=cw->cury-1; i>0; i--) { + cw->data[i] = cw->data[i-1]; + } + cw->data[0] = t; +#endif + } + + /* If prompt first, don't put same string in title where in most cases + it's not entirely visible anyway */ +#ifndef PROMPTFIRST + if( morestr ) + cw->morestr = strdup( morestr ); +#endif +} + +/* Select something from the menu. */ + +int +amii_select_menu(window, how, mip ) + register winid window; + register int how; + register menu_item **mip; +{ + int cnt; + register struct amii_WinDesc *cw; + + if( window == WIN_ERR || ( cw=amii_wins[window] ) == NULL || + cw->type != NHW_MENU ) + panic(winpanicstr,window, "select_menu"); + + cnt = DoMenuScroll( window, 1, how, mip ); + + /* This would allow the inventory window to stay open. */ + if( !alwaysinvent || window != WIN_INVEN ) + dismiss_nhwindow(window); /* Now tear it down */ + return cnt; +} + +amii_menu_item * +find_menu_item( register struct amii_WinDesc *cw, int idx ) +{ + amii_menu_item *mip; + for( mip = cw->menu.items; idx > 0 && mip; mip = mip->next ) + --idx; + + return( mip ); +} + +int +make_menu_items( register struct amii_WinDesc *cw, register menu_item **rmip ) +{ + register int idx = 0; + register amii_menu_item *mip; + register menu_item *mmip; + + for( mip = cw->menu.items; mip; mip = mip->next ) + { + if( mip->selected ) + ++idx; + } + + if( idx ) + { + mmip = *rmip = (menu_item *)alloc( idx * sizeof( *mip ) ); + for( mip = cw->menu.items; mip; mip = mip->next ) + { + if( mip->selected ) + { + mmip->item = mip->identifier; + mmip->count = mip->count; + mmip++; + } + } + + cw->mi = *rmip; + } + return( idx ); +} + +int +DoMenuScroll( win, blocking, how, retmip ) + int win, blocking, how; + menu_item **retmip; +{ + amii_menu_item *amip; + register struct Window *w; + register struct NewWindow *nw; + struct PropInfo *pip; + register struct amii_WinDesc *cw; + struct IntuiMessage *imsg; + struct Gadget *gd; + register int wheight, xsize, ysize, aredone = 0; + register int txwd, txh; + long mics, secs, class, code; + long oldmics = 0, oldsecs = 0; + int aidx, oidx, topidx, hidden; + int totalvis; + SHORT mx, my; + static char title[ 100 ]; + int dosize = 1; + struct Screen *scrn = HackScreen; + int x1,x2,y1,y2; + long counting = FALSE, count = 0, reset_counting = FALSE; + char countString[32]; + + if( win == WIN_ERR || ( cw = amii_wins[ win ] ) == NULL ) + panic(winpanicstr,win,"DoMenuScroll"); + + /* Initial guess at window sizing values */ + txwd = txwidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + txh = max( txheight, pictdata.ysize + 3 ); /* interline space */ + else + txh = txheight; /* interline space */ + } + else + txh = txheight; /* interline space */ + + /* Check to see if we should open the window, should need to for + * TEXT and MENU but not MESSAGE. + */ + + w = cw->win; + topidx = 0; + + if( w == NULL ) + { + +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + PropScroll.Flags |= PROPNEWLOOK; + } +#endif + nw = (void *)DupNewWindow( (void *)(&new_wins[ cw->type ].newwin) ); + if( !alwaysinvent || win != WIN_INVEN ) + { + xsize = scrn->WBorLeft + scrn->WBorRight + MenuScroll.Width + 1 + + (txwd * cw->maxcol); + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + xsize += pictdata.xsize + 4; + } + if( xsize > amiIDisplay->xpix ) + xsize = amiIDisplay->xpix; + + /* If next row not off window, use it, else use the bottom */ + + ysize = ( txh * cw->maxrow ) + /* The text space */ + HackScreen->WBorTop + txheight + 1 + /* Top border */ + HackScreen->WBorBottom + 3; /* The bottom border */ + if( ysize > amiIDisplay->ypix ) + ysize = amiIDisplay->ypix; + + /* Adjust the size of the menu scroll gadget */ + + nw->TopEdge = 0; + if( cw->type == NHW_TEXT && ysize < amiIDisplay->ypix ) + nw->TopEdge += ( amiIDisplay->ypix - ysize ) / 2; + nw->LeftEdge = amiIDisplay->xpix - xsize; + if( cw->type == NHW_MENU ) + { + if( nw->LeftEdge > 10 ) + nw->LeftEdge -= 10; + else + nw->LeftEdge = 0; + if( amiIDisplay->ypix - nw->Height > 10 ) + nw->TopEdge += 10; + else + nw->TopEdge = amiIDisplay->ypix - nw->Height - 1; + } + if( cw->type == NHW_TEXT && xsize < amiIDisplay->xpix ) + nw->LeftEdge -= ( amiIDisplay->xpix - xsize ) / 2; + } + else if( win == WIN_INVEN ) + { + struct Window *mw = amii_wins[ WIN_MAP ]->win; + struct Window *sw = amii_wins[ WIN_STATUS ]->win; + + xsize = scrn->WBorLeft + scrn->WBorRight + MenuScroll.Width + 1 + + (txwd * cw->maxcol); + + /* Make space for the glyph to appear at the left of the description */ + if( WINVERS_AMIV ) + xsize += pictdata.xsize + 4; + + if( xsize > amiIDisplay->xpix ) + xsize = amiIDisplay->xpix; + + /* If next row not off window, use it, else use the bottom */ + + ysize = sw->TopEdge - (mw->TopEdge + mw->Height) - 1; + if( ysize > amiIDisplay->ypix ) + ysize = amiIDisplay->ypix; + + /* Adjust the size of the menu scroll gadget */ + + nw->TopEdge = mw->TopEdge + mw->Height; + nw->LeftEdge = 0; + } + cw->newwin = (void *)nw; + if( nw == NULL ) + panic("No NewWindow Allocated" ); + + nw->Screen = HackScreen; + + if( win == WIN_INVEN ) + { + sprintf( title, "%s the %s's Inventory", plname, pl_character ); + nw->Title = title; + if( lastinvent.MaxX != 0 ) + { + nw->LeftEdge = lastinvent.MinX; + nw->TopEdge = lastinvent.MinY; + nw->Width = lastinvent.MaxX; + nw->Height = lastinvent.MaxY; + } + } + else if( cw->morestr ) + nw->Title = cw->morestr; + + /* Adjust the window coordinates and size now that we know + * how many items are to be displayed. + */ + + if( ( xsize > amiIDisplay->xpix - nw->LeftEdge ) && + ( xsize < amiIDisplay->xpix ) ) + { + nw->LeftEdge = amiIDisplay->xpix - xsize; + nw->Width = xsize; + } + else + { + nw->Width = min( xsize, amiIDisplay->xpix - nw->LeftEdge ); + } + nw->Height = min( ysize, amiIDisplay->ypix - nw->TopEdge ); + + if( WINVERS_AMIV || WINVERS_AMII ) + { + /* Make sure we are using the correct hook structure */ + nw->Extension = cw->wintags; + } + + /* Now, open the window */ + w = cw->win = OpenShWindow( (void *)nw ); + + if( w == NULL ) + { + char buf[ 130 ]; + + sprintf( buf, "No Window Opened For Menu (%d,%d,%d-%d,%d-%d)", + nw->LeftEdge, nw->TopEdge, nw->Width, amiIDisplay->xpix, + nw->Height, amiIDisplay->ypix ); + panic( buf ); + } + +#ifdef HACKFONT + if( TextsFont ) + SetFont(w->RPort, TextsFont ); + else if( HackFont ) + SetFont(w->RPort, HackFont ); +#endif + txwd = w->RPort->TxWidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + txh = max( w->RPort->TxHeight, pictdata.ysize + 3 ); /* interline space */ + else + txh = w->RPort->TxHeight; /* interline space */ + } + else + txh = w->RPort->TxHeight; /* interline space */ + + /* subtract 2 to account for spacing away from border (1 on each side) */ + wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / txh; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + totalvis = CountLines( win ); + } + else + { + txwd = w->RPort->TxWidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + txh = max( w->RPort->TxHeight, pictdata.ysize + 3 ); /* interline space */ + else + txh = w->RPort->TxHeight; /* interline space */ + } + else + { + txh = w->RPort->TxHeight; /* interline space */ + } + + /* subtract 2 to account for spacing away from border (1 on each side) */ + wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / txh; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3) / txwd; + } + else + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + + totalvis = CountLines( win ); + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( gd ) + { + pip = (struct PropInfo *)gd->SpecialInfo; + hidden = max( totalvis - wheight, 0 ); + topidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16; + } + } + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) panic("Can't find scroll gadget" ); + + morc = 0; + oidx = -1; + +#if 0 + /* Make sure there are no selections left over from last time. */ +/* XXX potential problem for preselection if this is really needed */ + for( amip = cw->menu.items; amip; amip = amip->next ) + amip->selected = 0; +#endif + + DisplayData( win, topidx ); + + /* Make the prop gadget the right size and place */ + + SetPropInfo( w, gd, wheight, totalvis, topidx ); + oldsecs = oldmics = 0; + + /* If window already up, don't stop to process events */ + if( cw->wasup ) + { + aredone = 1; + cw->wasup = 0; + } + + while( !aredone ) + { + /* Process window messages */ + + WaitPort( w->UserPort ); + while( imsg = (struct IntuiMessage * ) GetMsg( w->UserPort ) ) + { + class = imsg->Class; + code = imsg->Code; + mics = imsg->Micros; + secs = imsg->Seconds; + gd = (struct Gadget *) imsg->IAddress; + mx = imsg->MouseX; + my = imsg->MouseY; + + /* Only do our window or VANILLAKEY from other windows */ + + if( imsg->IDCMPWindow != w && class != VANILLAKEY && + class != RAWKEY ) + { + ReplyMsg( (struct Message *) imsg ); + continue; + } + + /* Do DeadKeyConvert() stuff if RAWKEY... */ + if( class == RAWKEY ) + { + class = VANILLAKEY; + code = ConvertKey( imsg ); + } + ReplyMsg( (struct Message *) imsg ); + + switch( class ) + { + case NEWSIZE: + + /* + * Ignore every other newsize, no action needed, + * except RefreshWindowFrame() in case borders got overwritten + * for some reason. It should not happen, but ... + */ + + if( !dosize ) + { + RefreshWindowFrame(w); + dosize = 1; + break; + } + + if( win == WIN_INVEN ) + { + lastinvent.MinX = w->LeftEdge; + lastinvent.MinY = w->TopEdge; + lastinvent.MaxX = w->Width; + lastinvent.MaxY = w->Height; + } + else if( win == WIN_MESSAGE ) + { + lastmsg.MinX = w->LeftEdge; + lastmsg.MinY = w->TopEdge; + lastmsg.MaxX = w->Width; + lastmsg.MaxY = w->Height; + } + + /* Find the gadget */ + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + panic("Can't find scroll gadget" ); + + totalvis = CountLines( win ); + wheight = ( w->Height - w->BorderTop - + w->BorderBottom - 2) / txh; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3) / txwd; + } + else + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + + if( wheight < 2 ) + wheight = 2; + + /* + * Clear the right side & bottom. Parts of letters are not erased by + * amii_cl_end if window shrinks and columns decrease. + */ + + if ( WINVERS_AMII || WINVERS_AMIV ) { + amii_setfillpens(w, cw->type); + SetDrMd(w->RPort, JAM2); + x2 = w->Width - w->BorderRight; + y2 = w->Height - w->BorderBottom; + x1 = x2 - w->IFont->tf_XSize - w->IFont->tf_XSize; + y1 = w->BorderTop; + if (x1 < w->BorderLeft) + x1 = w->BorderLeft; + RectFill(w->RPort, x1, y1, x2, y2); + x1 = w->BorderLeft; + y1 = y1 - w->IFont->tf_YSize; + RectFill(w->RPort, x1, y1, x2, y2); + RefreshWindowFrame(w); + } + + /* Make the prop gadget the right size and place */ + + DisplayData( win, topidx ); + SetPropInfo( w, gd, wheight, totalvis, topidx ); + + /* Force the window to a text line boundary <= to + * what the user dragged it to. This eliminates + * having to clean things up on the bottom edge. + */ + + SizeWindow( w, 0, ( wheight * txh) + + w->BorderTop + w->BorderBottom + 2 - w->Height ); + + /* Don't do next NEWSIZE, we caused it */ + dosize = 0; + oldsecs = oldmics = 0; + break; + + case VANILLAKEY: +#define CTRL(x) ((x)-'@') + morc = code = map_menu_cmd(code); + if (code == MENU_SELECT_ALL) { + if (how == PICK_ANY) { + amip = cw->menu.items; + while (amip) { + if (amip->canselect && amip->selector) { + /* + * Select those yet unselected + * and apply count if necessary + */ + if (!amip->selected) { + amip->selected = TRUE; + if (counting) { + amip->count = count; + reset_counting = TRUE; + /* + * This makes the assumption that + * the string is in format "X - foo" + * with additional selecting and formatting + * data in front (size SOFF) + */ + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + } + } + amip=amip->next; + } + DisplayData(win, topidx); + } + } else if (code == MENU_UNSELECT_ALL) { + if (how == PICK_ANY) { + amip = cw->menu.items; + while (amip) { + if (amip->selected) { + amip->selected = FALSE; + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + amip=amip->next; + } + DisplayData(win, topidx); + } + } else if (code == MENU_INVERT_ALL) { + if (how == PICK_ANY) { + amip = cw->menu.items; + while (amip) { + if (amip->canselect && amip->selector) { + amip->selected = !amip->selected; + if (counting && amip->selected) { + amip->count = count; + amip->str[SOFF+2] = '#'; + reset_counting = TRUE; + } else { + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + } + amip=amip->next; + } + DisplayData(win, topidx); + } + } else if (code == MENU_SELECT_PAGE) { + if (how == PICK_ANY) { + int i = 0; + amip = cw->menu.items; + while (amip && i++ < topidx) + amip = amip->next; + for (i=0;i < wheight && amip; i++, amip=amip->next) { + if (amip->canselect && amip->selector) { + if (!amip->selected) { + if (counting) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + } + amip->selected = TRUE; + } + } + DisplayData(win, topidx); + } + } else if (code == MENU_UNSELECT_PAGE) { + if (how == PICK_ANY) { + int i = 0; + amip = cw->menu.items; + while (amip && i++ < topidx) + amip = amip->next; + for (i=0;i < wheight && amip; i++, amip=amip->next) { + if (amip->selected) { + amip->selected = FALSE; + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + } + DisplayData(win, topidx); + } + } else if (code == MENU_INVERT_PAGE) { + if (how == PICK_ANY) { + int i = 0; + amip = cw->menu.items; + while (amip && i++ < topidx) + amip = amip->next; + for (i=0;i < wheight && amip; i++, amip=amip->next) { + if (amip->canselect && amip->selector) { + amip->selected = !amip->selected; + if (counting && amip->selected) { + amip->count = count; + amip->str[SOFF+2] = '#'; + reset_counting = TRUE; + } else { + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + } + } + DisplayData(win, topidx); + } + } else if (code == MENU_SEARCH && cw->type == NHW_MENU) { + if (how == PICK_ONE || how == PICK_ANY) { + char buf[BUFSZ]; + amip = cw->menu.items; + amii_getlin("Search for:", buf); + if (!*buf || *buf == '\033') + break; + while (amip) { + if (amip->canselect && amip->selector && amip->str && + strstri(&amip->str[SOFF], buf)) { + if (how == PICK_ONE) { + amip->selected = TRUE; + aredone = 1; + break; + } + amip->selected = !amip->selected; + if (counting && amip->selected) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + reset_counting = TRUE; + amip->str[SOFF+2] = '-'; + } + } + amip = amip->next; + } + } + DisplayData(win, topidx); + } else if (how == PICK_ANY && isdigit(code) && + (counting || (!counting && code !='0'))) { + if (count < LARGEST_INT) { + count = count*10 + (long)(code-'0'); + if (count > LARGEST_INT) + count = LARGEST_INT; + if (count > 0) { + counting = TRUE; + reset_counting = FALSE; + } else { + reset_counting = TRUE; + } + sprintf(countString, "Count: %d", count); + pline(countString); + } + } else if( code == CTRL('D') || code == CTRL('U') || + code == MENU_NEXT_PAGE || code == MENU_PREVIOUS_PAGE || + code == MENU_FIRST_PAGE || code == MENU_LAST_PAGE ) + { + int endcnt, i; + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + panic("Can't find scroll gadget" ); + + endcnt = wheight; /* /2; */ + if( endcnt == 0 ) + endcnt = 1; + + if (code == MENU_FIRST_PAGE) { + topidx = 0; + } else if (code == MENU_LAST_PAGE) { + topidx = cw->maxrow - wheight; + } else for( i = 0; i < endcnt; ++i ) + { + if (code == CTRL('D') || code == MENU_NEXT_PAGE) + { + if( topidx + wheight < cw->maxrow ) + ++topidx; + else + break; + } + else if (code = CTRL('U') || code == MENU_PREVIOUS_PAGE) + { + if( topidx > 0 ) + --topidx; + else + break; + } + } + /* Make prop gadget the right size and place */ + + DisplayData( win, topidx ); + SetPropInfo( w,gd, wheight, totalvis, topidx ); + oldsecs = oldmics = 0; + } + else if( code == '\b' ) + { + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + panic("Can't find scroll gadget" ); + + if( topidx - wheight - 2 < 0 ) + { + topidx = 0; + } + else + { + topidx -= wheight - 2; + } + DisplayData( win, topidx ); + SetPropInfo( w, gd, wheight, totalvis, topidx ); + oldsecs = oldmics = 0; + } + else if( code == ' ' ) + { + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + panic("Can't find scroll gadget" ); + + if( topidx + wheight >= cw->maxrow ) + { + morc = 0; + aredone = 1; + } + else + { + /* If there are still lines to be seen */ + + if( cw->maxrow > topidx + wheight ) + { + if( wheight > 2 ) + topidx += wheight - 2; + else + ++topidx; + DisplayData( win, topidx ); + SetPropInfo( w, gd, wheight, + totalvis, topidx ); + } + oldsecs = oldmics = 0; + } + } + else if( code == '\n' || code == '\r' ) + { + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + panic("Can't find scroll gadget" ); + + /* If all line displayed, we are done */ + + if( topidx + wheight >= cw->maxrow ) + { + morc = 0; + aredone = 1; + } + else + { + /* If there are still lines to be seen */ + + if( cw->maxrow > topidx + 1 ) + { + ++topidx; + DisplayData( win, topidx ); + SetPropInfo( w, gd, wheight, + totalvis, topidx ); + } + oldsecs = oldmics = 0; + } + } + else if( code == '\33' ) + { + if (counting) { + reset_counting = TRUE; + } else { + aredone = 1; + } + } + else + { + int selected = FALSE; + for( amip = cw->menu.items; amip; amip = amip->next ) + { + if( amip->selector == code ) + { + if( how == PICK_ONE ) + aredone = 1; + amip->selected = !amip->selected; + if (counting && amip->selected) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + reset_counting = TRUE; + amip->str[SOFF+2] = '-'; + } + selected = TRUE; + } else if (amip->gselector == code ) + { + amip->selected = !amip->selected; + if (counting) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + reset_counting = TRUE; + amip->str[SOFF+2] = '-'; + } + selected = TRUE; + } + } + if (selected) + DisplayData( win, topidx ); + } + break; + + case CLOSEWINDOW: + if( win == WIN_INVEN ) + { + lastinvent.MinX = w->LeftEdge; + lastinvent.MinY = w->TopEdge; + lastinvent.MaxX = w->Width; + lastinvent.MaxY = w->Height; + } + else if( win == WIN_MESSAGE ) + { + lastmsg.MinX = w->LeftEdge; + lastmsg.MinY = w->TopEdge; + lastmsg.MaxX = w->Width; + lastmsg.MaxY = w->Height; + } + aredone = 1; + morc = '\33'; + break; + + case GADGETUP: + if( win == WIN_MESSAGE ) + aredone = 1; + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + pip = (struct PropInfo *)gd->SpecialInfo; + totalvis = CountLines( win ); + hidden = max( totalvis - wheight, 0 ); + aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16; + if( aidx != topidx ) + DisplayData( win, topidx = aidx ); + break; + + case MOUSEMOVE: + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + pip = (struct PropInfo *)gd->SpecialInfo; + totalvis = CountLines( win ); + hidden = max( totalvis - wheight, 0 ); + aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16; + if( aidx != topidx ) + DisplayData( win, topidx = aidx ); + break; + + case INACTIVEWINDOW: + if( win == WIN_MESSAGE || ( win == WIN_INVEN && alwaysinvent ) ) + aredone = 1; + break; + + case MOUSEBUTTONS: + if( ( code == SELECTUP || code == SELECTDOWN ) && + cw->type == NHW_MENU && how != PICK_NONE ) + { + /* Which one is the mouse pointing at? */ + + aidx = ( ( my - w->BorderTop - 1 ) / txh ) + topidx; + + /* If different lines, don't select double click */ + + if( aidx != oidx ) + { + oldsecs = 0; + oldmics = 0; + } + + /* If releasing, check for double click */ + + if( code == SELECTUP ) + { + amip = find_menu_item( cw, aidx ); + if( aidx == oidx ) + { + if( DoubleClick( oldsecs, + oldmics, secs, mics ) ) + { + aredone = 1; + } + oldsecs = secs; + oldmics = mics; + } + else + { + amip = find_menu_item( cw, oidx ); + amip->selected = 0; + amip->count = -1; + reset_counting = TRUE; + if (amip->canselect && amip->selector) + amip->str[SOFF+2] = '-'; + } + if (counting && amip->selected && amip->canselect && amip->selector) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } + DisplayData( win, topidx ); + } + else if( aidx - topidx < wheight && + aidx < cw->maxrow && code == SELECTDOWN ) + { + /* Remove old highlighting if visible */ + + amip = find_menu_item( cw, oidx ); + if( amip && oidx != aidx && + ( oidx > topidx && oidx - topidx < wheight ) ) + { + if( how != PICK_ANY ) { + amip->selected = 0; + amip->count = -1; + reset_counting = TRUE; + if (amip->canselect && amip->selector) + amip->str[SOFF+2] = '-'; + } + oidx = -1; + } + amip = find_menu_item( cw, aidx ); + + if( amip && amip->canselect && amip->selector && how != PICK_NONE ) + { + oidx = aidx; + if( !DoubleClick( oldsecs, + oldmics, secs, mics ) ) + { + amip->selected = !amip->selected; + if (counting && amip->selected) { + amip->count = count; + reset_counting = TRUE; + amip->str[SOFF+2] = '#'; + } else { + amip->count = -1; + reset_counting = TRUE; + if (amip->canselect && amip->selector) + amip->str[SOFF+2] = '-'; + } + } + } + else + { + DisplayBeep( NULL ); + oldsecs = 0; + oldmics = 0; + } + DisplayData( win, topidx ); + } + } + else + { + DisplayBeep( NULL ); + } + break; + } + if (!counting && morc == '\33') { + amip = cw->menu.items; + while (amip) { + if (amip->canselect && amip->selector) { + amip->selected = FALSE; + amip->count = -1; + amip->str[SOFF+2] = '-'; + } + amip=amip->next; + } + } + if (reset_counting) { + count = 0; + if (counting) + pline("Count: 0"); + counting = FALSE; + } + } + } + /* Force a cursor reposition before next message output */ + if( win == WIN_MESSAGE ) + cw->curx = -1; + return( make_menu_items( cw, retmip ) ); +} + +void +ReDisplayData( win ) + winid win; +{ + int totalvis; + register struct amii_WinDesc *cw; + register struct Window *w; + register struct Gadget *gd; + unsigned long hidden, aidx, wheight; + struct PropInfo *pip; + + if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) ) + return; + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( !gd ) + return; + + wheight = (w->Height - w->BorderTop - w->BorderBottom-2)/w->RPort->TxHeight; + + pip = (struct PropInfo *)gd->SpecialInfo; + totalvis = CountLines( win ); + hidden = max( totalvis - wheight, 0 ); + aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16; + clear_nhwindow( win ); + DisplayData( win, aidx ); +} + +long +FindLine( win, line ) + winid win; + int line; +{ + int txwd; + register char *t; + register struct amii_WinDesc *cw; + register struct Window *w; + register int i, disprow, len; + int col = -1; + + if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) ) + { + panic( winpanicstr, win, "No Window in FindLine" ); + } + txwd = w->RPort->TxWidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3) / txwd; + } + else + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + + disprow = 0; + for( col = i = 0; line > disprow && i < cw->maxrow; ++i ) + { + t = cw->data[ i ] + SOFF; + if( cw->data[i][1] >= 0 ) + { + ++disprow; + col = 0; + } + + while( *t ) + { + len = strlen( t ); + if( col + len > cw->cols ) + len = cw->cols - col; + while( len > 0 ) + { + if( !t[len] || t[len] == ' ' ) + break; + --len; + } + if( len == 0 ) { + while ( *t && *t != ' ') { + t++; col++; + } + } else { + t += len; + col += len; + } + if( *t ) + { + while( *t == ' ' ) + ++t; + col = 0; + ++disprow; + } + } + } + return( i ); +} + +long +CountLines( win ) + winid win; +{ + int txwd; + amii_menu_item *mip; + register char *t; + register struct amii_WinDesc *cw; + register struct Window *w; + register int i, disprow, len; + int col = -1; + + if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) ) + { + panic( winpanicstr, win, "No Window in CountLines" ); + } + + txwd = w->RPort->TxWidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3) / txwd; + } + else + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + + disprow = cw->maxrow; + mip = cw->menu.items; + for( col = i = 0; i < cw->maxrow; ++i ) + { + t = cw->data[ i ] + SOFF; + if( cw->type == NHW_MESSAGE && cw->data[ i ][ SEL_ITEM ] < 0 ) + --disprow; + else + col = 0; + while( *t ) + { + len = strlen( t ); + if( col + len > cw->cols ) + len = cw->cols - col; + while( len > 0 ) + { + if( !t[len] || t[len] == ' ' ) + break; + --len; + } + if( len == 0 ) { + while ( *t && *t != ' ') { + t++; col++; + } + } else { + t += len; + col += len; + } + if( *t ) + { + while( *t == ' ' ) + ++t; + col = 0; + ++disprow; + } + } + } + return( disprow ); +} + +void +DisplayData( win, start ) + winid win; + int start; +{ + int txwd; + amii_menu_item *mip; + register char *t; + register struct amii_WinDesc *cw; + register struct Window *w; + register struct RastPort *rp; + register int i, disprow, len, wheight; + int whichcolor = -1; + int col; + + if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) ) + { + panic( winpanicstr, win, "No Window in DisplayData" ); + } + + rp = w->RPort; + SetDrMd( rp, JAM2 ); + if( WINVERS_AMIV && win == WIN_INVEN ) + { + wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / + max( rp->TxHeight, pictdata.ysize + 3 ); + } + else + { + wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / + rp->TxHeight; + } + + cw->rows = wheight; + txwd = rp->TxWidth; + if( WINVERS_AMIV ) + { + if( win == WIN_INVEN ) + { + cw->cols = ( w->Width - w->BorderLeft - + w->BorderRight - 4 - pictdata.xsize - 3) / txwd; + } + else + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + else + { + cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd; + } + + /* Get the real line to display at */ + start = FindLine( win, start ); + + mip = cw->menu.items; + for( i = 0; mip && i < start; ++i ) + { + mip = mip->next; + } + + /* Skip any initial response to a previous line */ + if( cw->type == NHW_MESSAGE && mip && mip->selected < 0 ) + ++start; + if( WINVERS_AMIV && cw->type == NHW_MESSAGE ) + SetAPen( rp, amii_msgAPen ); + + for( disprow = i = start; disprow < wheight + start; i++ ) + { + /* Just erase unused lines in the window */ + if( i >= cw->maxrow ) + { + if (WINVERS_AMIV && win == WIN_INVEN) { + amii_curs( win, 0, disprow - start ); + amiga_print_glyph( win, 0, NO_GLYPH); + } + amii_curs( win, 1, disprow - start ); + amii_cl_end( cw, 0 ); + ++disprow; + continue; + } + + /* Any string with a highlighted attribute goes + * onto the end of the current line in the message window. + */ + if( cw->type == NHW_MESSAGE ) + SetAPen( rp, cw->data[ i ][ SEL_ITEM ] < 0 ? C_RED : amii_msgAPen ); + + /* Selected text in the message window goes onto the end of the current line */ + if( cw->type != NHW_MESSAGE || cw->data[ i ][ SEL_ITEM ] >= 0 ) + { + amii_curs( win, 1, disprow - start ); + if( WINVERS_AMIV && win == WIN_INVEN ) + { + if( mip ) + amiga_print_glyph( win, 0, mip->glyph ); + amii_curs( win, 1, disprow - start ); + } + col = 0; + } + + /* If this entry is to be highlighted, do so */ + if( mip && mip->selected != 0 ) + { + if( whichcolor != 1 ) + { + SetDrMd( rp, JAM2 ); + if( WINVERS_AMIV ) + { + SetAPen( rp, amii_menuBPen ); + SetBPen( rp, C_BLUE ); + } + else + { + SetAPen( rp, C_BLUE ); + SetBPen( rp, amii_menuAPen ); + } + whichcolor = 1; + } + } + else if( whichcolor != 2 ) + { + SetDrMd( rp, JAM2 ); + if( cw->type == NHW_MESSAGE ) + { + SetAPen( rp, amii_msgAPen ); + SetBPen( rp, amii_msgBPen ); + } + else if( cw->type == NHW_MENU ) + { + SetAPen( rp, amii_menuAPen ); + SetBPen( rp, amii_menuBPen ); + } + else if( cw->type == NHW_TEXT ) + { + SetAPen( rp, amii_textAPen ); + SetBPen( rp, amii_textBPen ); + } + whichcolor = 2; + } + + /* Next line out, wrap if too long */ + + t = cw->data[ i ] + SOFF; + ++disprow; + col = 0; + while( *t ) + { + len = strlen( t ); + if( len > (cw->cols - col) ) + len = cw->cols - col; + while( len > 0 ) + { + if( !t[len] || t[len] == ' ' ) + break; + --len; + } + if( len == 0 ) { + Text( rp, t, cw->cols - col ); + while ( *t && *t != ' ') { + t++; col++; + } + } else { + Text( rp, t, len ); + t += len; + col += len; + } + amii_cl_end( cw, col ); + if( *t ) + { + ++disprow; + /* Stop at the bottom of the window */ + if( disprow > wheight + start ) + break; + while( *t == ' ' ) + ++t; + amii_curs( win, 1, disprow - start - 1 ); + if( mip && win == WIN_INVEN && WINVERS_AMIV ) + { + /* Erase any previous glyph drawn here. */ + amiga_print_glyph( win, 0, NO_GLYPH ); + amii_curs( win, 1, disprow - start - 1 ); + } + Text( rp, "+", 1 ); + col = 1; + } + } + + if( cw->type == NHW_MESSAGE ) + { + SetAPen( rp, amii_msgBPen ); + SetBPen( rp, amii_msgBPen ); + } + else if( cw->type == NHW_MENU ) + { + SetAPen( rp, amii_menuBPen ); + SetBPen( rp, amii_menuBPen ); + } + else if( cw->type == NHW_TEXT ) + { + SetAPen( rp, amii_textBPen ); + SetBPen( rp, amii_textBPen ); + } + amii_cl_end( cw, col ); + whichcolor = -1; + if( mip ) mip = mip->next; + } + RefreshWindowFrame(w); + return; +} + +void SetPropInfo( win, gad, vis, total, top ) + register struct Window *win; + register struct Gadget *gad; + register long vis, total, top; +{ + long mflags; + register long hidden; + register int body, pot; + + hidden = max( total-vis, 0 ); + + /* Force the last section to be just to the bottom */ + + if( top > hidden ) + top = hidden; + + /* Scale the body position. */ + /* 2 lines overlap */ + + if( hidden > 0 && total > 2) + body = (ULONG) ((vis - 2) * MAXBODY) / (total - 2); + else + body = MAXBODY; + + if( hidden > 0 ) + pot = (ULONG) (top * MAXPOT) / hidden; + else + pot = 0; + + mflags = AUTOKNOB|FREEVERT; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + mflags |= PROPNEWLOOK; + } +#endif + + NewModifyProp( gad, win, NULL, + mflags, 0, pot, MAXBODY, body, 1 ); +} diff --git a/sys/amiga/winproto.h b/sys/amiga/winproto.h new file mode 100644 index 0000000..d0106a0 --- /dev/null +++ b/sys/amiga/winproto.h @@ -0,0 +1,151 @@ +/* SCCS Id: @(#)winproto.h 3.2 96/01/15 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* winreq.c */ +void EditColor ( void ); +void EditClipping( void ); +void DrawCol ( struct Window *w , int idx , UWORD *colors ); +void DispCol ( struct Window *w , int idx , UWORD *colors ); +void amii_change_color( int, long, int ); +char *amii_get_color_string( ); +void amii_getlin ( const char *prompt , char *bufp ); +void getlind ( const char *prompt , char *bufp , const char *dflt ); +char *amii_get_color_string( void ); +int filecopy( char *from, char *to ); +char *basename( char *str ); +char *dirname( char *str ); + +/* winstr.c */ +void amii_putstr ( winid window , int attr , const char *str ); +void outmore ( struct amii_WinDesc *cw ); +void outsubstr ( struct amii_WinDesc *cw , char *str , int len, int fudge ); +void amii_putsym ( winid st , int i , int y , CHAR_P c ); +void amii_addtopl ( const char *s ); +void TextSpaces ( struct RastPort *rp , int nr ); +void amii_remember_topl ( void ); +long CountLines( winid ); +long FindLine( winid, int ); +int amii_doprev_message ( void ); +void flushIDCMP( struct MsgPort * ); +int amii_msgborder( struct Window * ); +void amii_scrollmsg( register struct Window *w, register struct amii_WinDesc *cw ); + +/* winkey.c */ +int amii_nh_poskey ( int *x , int *y , int *mod ); +int amii_nhgetch ( void ); +void amii_get_nh_event ( void ); +void amii_getret ( void ); + +/* winmenu.c */ +void amii_start_menu ( winid window ); +void FDECL(amii_add_menu, (winid,int,const anything *,CHAR_P,CHAR_P,int,const char *,BOOLEAN_P)); +void FDECL(amii_end_menu, (winid, const char *)); +int FDECL(amii_select_menu, (winid, int, menu_item **)); +int DoMenuScroll ( int win , int blocking, int how, menu_item ** ); +void ReDisplayData ( winid win ); +void DisplayData ( winid win , int start ); +void SetPropInfo ( struct Window *win , struct Gadget *gad , long vis , long total , long top ); + +/* amiwind.c */ +struct Window *OpenShWindow ( struct NewWindow *nw ); +void CloseShWindow ( struct Window *win ); +int ConvertKey ( struct IntuiMessage *message ); +int kbhit ( void ); +int kbhit ( void ); +int amikbhit ( void ); +int WindowGetchar ( void ); +WETYPE WindowGetevent ( void ); +void amii_cleanup ( void ); +#ifndef SHAREDLIB +void Abort ( long rc ); +#endif +void CleanUp ( void ); +void flush_glyph_buffer ( struct Window *w ); +void amiga_print_glyph ( winid window , int color_index , int glyph ); +void start_glyphout ( winid window ); +void amii_end_glyphout ( winid window ); +struct NewWindow *DupNewWindow ( struct NewWindow *win ); +void FreeNewWindow ( struct NewWindow *win ); +void bell ( void ); +void amii_delay_output ( void ); +void amii_number_pad ( int state ); +#ifndef SHAREDLIB +void amiv_loadlib ( void ); +void amii_loadlib ( void ); +#endif +void preserve_icon( void ); +void clear_icon( void ); + +/* winfuncs.c */ +void amii_destroy_nhwindow ( winid win ); +int amii_create_nhwindow ( int type ); +void amii_init_nhwindows ( int *, char ** ); +void amii_setdrawpens( struct Window *, int type ); +void amii_sethipens( struct Window *, int type, int attr ); +void amii_setfillpens( struct Window *, int type ); +void amii_clear_nhwindow ( winid win ); +void dismiss_nhwindow ( winid win ); +void amii_exit_nhwindows ( const char *str ); +void amii_display_nhwindow ( winid win , boolean blocking ); +void amii_curs ( winid window , int x , int y ); +void kill_nhwindows ( int all ); +void amii_cl_end ( struct amii_WinDesc *cw , int i ); +void cursor_off ( winid window ); +void cursor_on ( winid window ); +void amii_suspend_nhwindows ( const char *str ); +void amii_resume_nhwindows ( void ); +void amii_bell ( void ); +void removetopl ( int cnt ); +void port_help ( void ); +void amii_print_glyph ( winid win , xchar x , xchar y , int glyph ); +void amii_raw_print ( const char *s ); +void amii_raw_print_bold ( const char *s ); +void amii_update_inventory ( void ); +void amii_mark_synch ( void ); +void amii_wait_synch ( void ); +void amii_setclipped ( void ); +void amii_cliparound ( int x , int y ); +void amii_set_text_font( char *font, int size ); +BitMapHeader ReadImageFiles( char **, struct BitMap **, char ** ); +BitMapHeader ReadTileImageFiles(void); +void FreeImageFiles( char **, struct BitMap ** ); +void FreeTileImageFiles(); + +/* winami.c */ +#ifdef SHAREDLIB +int __UserLibInit ( void ); +void __UserLibCleanup ( void ); +#endif +void amii_askname ( void ); +void amii_player_selection ( void ); +void RandomWindow ( char *name ); +int amii_get_ext_cmd ( void ); +char amii_yn_function ( const char *prompt , const char *resp , char def ); +char amii_yn_function ( const char *query , const char *resp , char def ); +void amii_display_file ( const char *fn , boolean complain ); +void SetBorder ( struct Gadget *gd ); +void *malloc ( register unsigned size ); +void free ( void *q ); + +#ifdef SHAREDLIB +/* amilib.c */ +void amii_loadlib ( void ); +void amiv_loadlib ( void ); +void CleanUp ( void ); +void setup_librefs ( WinamiBASE *base ); +#else +void Abort ( long rc ); +#endif + +/* amirip.c */ +void FDECL(amii_outrip, ( winid tmpwin, int how )); + +/* winchar.c */ +void SetMazeType(MazeType); +int GlyphToIcon(int glyph); +#ifdef OPT_DISPMAP +void dispmap_sanity(void); +int dispmap_sanity1(int); +#endif +void FreeTileImageFiles(void); diff --git a/sys/amiga/winreq.c b/sys/amiga/winreq.c new file mode 100644 index 0000000..fac7ee6 --- /dev/null +++ b/sys/amiga/winreq.c @@ -0,0 +1,1177 @@ +/* SCCS Id: @(#)winreq.c 3.1 93/04/02 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +#define GADBLUEPEN 2 +#define GADREDPEN 3 +#define GADGREENPEN 4 +#define GADCOLOKAY 5 +#define GADCOLCANCEL 6 +#define GADCOLSAVE 7 + +UBYTE UNDOBUFFER[300]; +SHORT BorderVectors1[] = { 0,0, 57,0, 57,11, 0,11, 0,0 }; +struct Border Border1 = { -1,-1, 3,0,JAM1, 5, BorderVectors1, NULL }; +struct IntuiText IText1 = { 3,0,JAM1, 4,1, NULL, (UBYTE *)"Cancel", NULL }; +struct Gadget Gadget2 = { + NULL, 9,15, 56,10, NULL, RELVERIFY, BOOLGADGET, (APTR)&Border1, + NULL, &IText1, NULL, NULL, 1, NULL +}; +UBYTE StrStringSIBuff[300]; +struct StringInfo StrStringSInfo = { + StrStringSIBuff, UNDOBUFFER, 0, 300, 0, 0,0,0,0,0, 0, 0, NULL +}; +SHORT BorderVectors2[] = { 0,0, 439,0, 439,11, 0,11, 0,0 }; +struct Border Border2 = { -1,-1, 3,0,JAM1, 5, BorderVectors2, NULL }; +struct Gadget String = { + &Gadget2, 77,15, 438,10, NULL, RELVERIFY+STRINGCENTER, STRGADGET, + (APTR)&Border2, NULL, NULL, NULL, (APTR)&StrStringSInfo, 2, NULL +}; + +#define StrString \ + ((char *)(((struct StringInfo *)(String.SpecialInfo))->Buffer)) + +struct NewWindow StrWindow = { + 57,74, 526,31, 0,1, GADGETUP+CLOSEWINDOW+ACTIVEWINDOW+VANILLAKEY, + WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH, + &String, NULL, NULL, NULL, NULL, 5,5, 0xffff,0xffff, CUSTOMSCREEN +}; + +#include "NH:sys/amiga/colorwin.c" + +#define XSIZE 2 +#define YSIZE 3 +#define XCLIP 4 +#define YCLIP 5 +#define GADOKAY 6 +#define GADCANCEL 7 + +#include "NH:sys/amiga/clipwin.c" + +void ClearCol( struct Window *w ); + +void +EditColor( ) +{ + extern const char *configfile; + int i, done = 0, okay = 0; + long code, qual, class; + register struct Gadget *gd, *dgad; + register struct Window *nw; + register struct IntuiMessage *imsg; + register struct PropInfo *pip; + register struct Screen *scrn; + long aidx; + int msx, msy; + int curcol = 0, drag = 0; + int bxorx, bxory, bxxlen, bxylen; + static UWORD colors[ AMII_MAXCOLORS ]; + static UWORD svcolors[ AMII_MAXCOLORS ]; + static int once = 0; + scrn = HackScreen; + + if( !once ) + { + if( WINVERS_AMIV ) + { + Col_NewWindowStructure1.Width += 300; + Col_NewWindowStructure1.Height += 20; + Col_NewWindowStructure1.LeftEdge -= 150; + Col_BluePen.Width += 300; + Col_RedPen.Width += 300; + Col_GreenPen.Width += 300; + Col_Cancel.LeftEdge += 300; + Col_Okay.LeftEdge += 150; + Col_Cancel.TopEdge += 20; + Col_Save.TopEdge += 20; + Col_Okay.TopEdge += 20; + } + SetBorder( &Col_Okay ); + SetBorder( &Col_Cancel ); + SetBorder( &Col_Save ); + once = 1; + } + + bxylen = Col_NewWindowStructure1.Height - + ( Col_BluePen.TopEdge + Col_BluePen.Height + 6 ); + bxxlen = Col_BluePen.Width; + bxorx = Col_BluePen.LeftEdge; + bxory = Col_BluePen.TopEdge + Col_BluePen.Height + 2; + + /* Save the current colors */ + for( i = 0; i < amii_numcolors; ++i ) + svcolors[ i ] = colors[ i ] = GetRGB4( scrn->ViewPort.ColorMap, i ); + + Col_NewWindowStructure1.Screen = scrn; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + ((struct PropInfo *)Col_BluePen.SpecialInfo)->Flags |= PROPNEWLOOK; + ((struct PropInfo *)Col_RedPen.SpecialInfo)->Flags |= PROPNEWLOOK; + ((struct PropInfo *)Col_GreenPen.SpecialInfo)->Flags |= PROPNEWLOOK; + } +#endif + if( WINVERS_AMIV || WINVERS_AMII ) + { +#ifdef INTUI_NEW_LOOK + Col_NewWindowStructure1.Extension = wintags; + Col_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED; +# ifdef __GNUC__ + fillhook.h_Entry = (void *)&LayerFillHook; +# else + fillhook.h_Entry = (ULONG(*)())LayerFillHook; +# endif + fillhook.h_Data = (void *)-2; + fillhook.h_SubEntry = 0; +#endif + } + + nw = OpenWindow( (void *)&Col_NewWindowStructure1 ); + + if( nw == NULL ) + { + DisplayBeep( NULL ); + return; + } + + PrintIText( nw->RPort, &Col_IntuiTextList1, 0, 0 ); + + ClearCol( nw ); + DrawCol( nw, curcol, colors ); + while( !done ) + { + WaitPort( nw->UserPort ); + + while( imsg = (struct IntuiMessage * )GetMsg( nw->UserPort ) ) + { + gd = (struct Gadget *)imsg->IAddress; + code = imsg->Code; + class = imsg->Class; + qual = imsg->Qualifier; + msx = imsg->MouseX; + msy = imsg->MouseY; + + ReplyMsg( (struct Message *)imsg ); + + switch( class ) + { + case VANILLAKEY: + if( code == 'v' && qual == AMIGALEFT ) + okay = done = 1; + else if( code == 'b' && qual == AMIGALEFT ) + okay = 0, done = 1; + else if( code == 'o' || code == 'O' ) + okay = done = 1; + else if( code == 'c' || code == 'C' ) + okay = 0, done = 1; + break; + + case CLOSEWINDOW: + done = 1; + break; + + case GADGETUP: + drag = 0; + if( gd->GadgetID == GADREDPEN || + gd->GadgetID == GADBLUEPEN || + gd->GadgetID == GADGREENPEN ) + { + pip = (struct PropInfo *)gd->SpecialInfo; + aidx = pip->HorizPot / (MAXPOT/15); + if( gd->GadgetID == GADREDPEN ) + { + colors[ curcol ] = + ( colors[ curcol ] & ~0xf00 ) | (aidx << 8); + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + else if( gd->GadgetID == GADBLUEPEN ) + { + colors[ curcol ] = + ( colors[ curcol ] & ~0xf ) | aidx; + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + else if( gd->GadgetID == GADGREENPEN ) + { + colors[ curcol ] = ( colors[ curcol ] & ~0x0f0 ) | (aidx << 4); + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + DispCol( nw, curcol, colors ); + } + else if( gd->GadgetID == GADCOLOKAY ) + { + done = 1; + okay = 1; + } + else if( gd->GadgetID == GADCOLSAVE ) + { + FILE *fp, *nfp; + char buf[ 300 ], nname[ 300 ], oname[ 300 ]; + int once = 0; + + fp = fopen( configfile, "r" ); + if( !fp ) + { + pline( "can't find NetHack.cnf" ); + break; + } + + strcpy( oname, dirname( (char *)configfile ) ); + if( oname[ strlen(oname)-1 ] != ':' ) + { + sprintf( nname, "%s/New_NetHack.cnf", oname ); + strcat( oname, "/" ); + strcat( oname, "Old_NetHack.cnf" ); + } + else + { + sprintf( nname, "%sNew_NetHack.cnf", oname ); + strcat( oname, "Old_NetHack.cnf" ); + } + + nfp = fopen( nname, "w" ); + if( !nfp ) + { + pline( "can't write to New_NetHack.cnf" ); + fclose( fp ); + break; + } + while( fgets( buf, sizeof( buf ), fp ) ) + { + if( strncmp( buf, "PENS=", 5 ) == 0 ) + { + once = 1; + fputs( "PENS=", nfp ); + for( i = 0; i < amii_numcolors; ++i ) + { + fprintf( nfp, "%03x", colors[i] ); + if(( i + 1 ) < amii_numcolors) + putc( '/', nfp ); + } + putc( '\n', nfp ); + } + else + { + fputs( buf, nfp ); + } + } + + /* If none in the file yet, now write it */ + if( !once ) + { + fputs( "PENS=", nfp ); + for( i = 0; i < amii_numcolors; ++i ) + { + fprintf( nfp, "%03x", colors[i] ); + if(( i + 1 ) < amii_numcolors) + putc( ',', nfp ); + } + putc( '\n', nfp ); + } + fclose( fp ); + fclose( nfp ); + unlink( oname ); + if( filecopy( (char *)configfile, oname ) == 0 ) + if( filecopy( nname, (char *)configfile ) == 0 ) + unlink( nname ); + done = 1; + okay = 1; + } + else if( gd->GadgetID == GADCOLCANCEL ) + { + done = 1; + okay = 0; + } + break; + + case GADGETDOWN: + drag = 1; + dgad = gd; + break; + + case MOUSEMOVE: + if( !drag ) + break; + pip = (struct PropInfo *)dgad->SpecialInfo; + aidx = pip->HorizPot / (MAXPOT/15); + if( dgad->GadgetID == GADREDPEN ) + { + colors[ curcol ] = + ( colors[ curcol ] & ~0xf00 ) | (aidx << 8); + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + else if( dgad->GadgetID == GADBLUEPEN ) + { + colors[ curcol ] = ( colors[ curcol ] & ~0xf ) | aidx; + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + else if( dgad->GadgetID == GADGREENPEN ) + { + colors[ curcol ] = + ( colors[ curcol ] & ~0x0f0 ) | (aidx << 4); + LoadRGB4( &scrn->ViewPort, colors, amii_numcolors ); + } + DispCol( nw, curcol, colors ); + break; + + case MOUSEBUTTONS: + if( code == SELECTDOWN ) + { + if( msy > bxory && msy < bxory + bxylen - 1 && + msx > bxorx && msx < bxorx + bxxlen - 1 ) + { + curcol = ( msx - bxorx )/(bxxlen / amii_numcolors); + if( curcol >= 0 && curcol < amii_numcolors ) + DrawCol( nw, curcol, colors ); + } + } + break; + } + } + } + + if( okay ) + { + for( i = 0; i < ( amii_numcolors ); ++i ) + flags.amii_curmap[ i ] = colors[ i ]; + LoadRGB4( &scrn->ViewPort, flags.amii_curmap, amii_numcolors ); + } + else + LoadRGB4( &scrn->ViewPort, svcolors, amii_numcolors ); + CloseWindow( nw ); +} + +void +ShowClipValues( struct Window *nw ) +{ + char buf[ 50 ]; + struct Gadget *gd; + + SetAPen( nw->RPort, 5 ); + SetBPen( nw->RPort, amii_otherBPen ); + SetDrMd( nw->RPort, JAM2 ); + + sprintf( buf, "%d ", mxsize ); + gd = &ClipXSIZE; + Move( nw->RPort, gd->LeftEdge + (nw->Width + gd->Width) + 8, + gd->TopEdge + nw->RPort->TxBaseline ); + Text( nw->RPort, buf, strlen( buf ) ); + + sprintf( buf, "%d ", mysize ); + gd = &ClipYSIZE; + Move( nw->RPort, gd->LeftEdge + (nw->Width + gd->Width) + 8, + gd->TopEdge + nw->RPort->TxBaseline ); + Text( nw->RPort, buf, strlen( buf ) ); + + sprintf( buf, "%d ", xclipbord ); + gd = &ClipXCLIP; + Move( nw->RPort, gd->LeftEdge + (nw->Width + gd->Width) + 8, + gd->TopEdge + nw->RPort->TxBaseline ); + Text( nw->RPort, buf, strlen( buf ) ); + + sprintf( buf, "%d ", yclipbord ); + gd = &ClipYCLIP; + Move( nw->RPort, gd->LeftEdge + (nw->Width + gd->Width) + 8, + gd->TopEdge + nw->RPort->TxBaseline ); + Text( nw->RPort, buf, strlen( buf ) ); +} + +void +EditClipping( void ) +{ + int i; + long mflags; + static int sizes[] = { 8, 16, 20, 24, 28, 32, 36 }; + char buf[ 40 ]; + int done = 0, okay = 0; + long code, qual, class; + register struct Gadget *gd, *dgad; + register struct Window *nw; + register struct IntuiMessage *imsg; + register struct PropInfo *pip; + register struct Screen *scrn; + long aidx; + int lmxsize = mxsize, lmysize = mysize; + int lxclipbord = xclipbord, lyclipbord = yclipbord; + int msx, msy; + int drag = 0; + static int once = 0; + + scrn = HackScreen; + + if( !once ) + { + SetBorder( &ClipOkay ); + SetBorder( &ClipCancel ); + once = 1; + } + ClipNewWindowStructure1.Screen = scrn; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + ((struct PropInfo *)ClipXSIZE.SpecialInfo)->Flags |= PROPNEWLOOK; + ((struct PropInfo *)ClipYSIZE.SpecialInfo)->Flags |= PROPNEWLOOK; + ((struct PropInfo *)ClipXCLIP.SpecialInfo)->Flags |= PROPNEWLOOK; + ((struct PropInfo *)ClipYCLIP.SpecialInfo)->Flags |= PROPNEWLOOK; + } +#endif + if( WINVERS_AMIV || WINVERS_AMII ) + { +# ifdef INTUI_NEW_LOOK + ClipNewWindowStructure1.Extension = wintags; + ClipNewWindowStructure1.Flags |= WFLG_NW_EXTENDED; +# ifdef __GNUC__ + fillhook.h_Entry = (void *)&LayerFillHook; +# else + fillhook.h_Entry = (ULONG(*)())LayerFillHook; +# endif + fillhook.h_Data = (void *)-2; + fillhook.h_SubEntry = 0; +# endif + } + + nw = OpenWindow( (void *)&ClipNewWindowStructure1 ); + + if( nw == NULL ) + { + DisplayBeep( NULL ); + return; + } + + ShowClipValues( nw ); + mflags = AUTOKNOB|FREEHORIZ; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + mflags |= PROPNEWLOOK; + } +#endif + + for( i = 0; i < 7; ++i ) + { + if( mxsize <= sizes[ i ] ) + break; + } + NewModifyProp( &ClipXSIZE, nw, NULL, mflags, (i * MAXPOT ) / 6, 0, + MAXPOT/6, 0, 1 ); + for( i = 0; i < 7; ++i ) + { + if( mysize <= sizes[ i ] ) + break; + } + NewModifyProp( &ClipYSIZE, nw, NULL, mflags, (i * MAXPOT ) / 6, 0, + MAXPOT/6, 0, 1 ); + + NewModifyProp( &ClipXCLIP, nw, NULL, mflags, ((xclipbord-2) * MAXPOT ) / 6, 0, + MAXPOT/6, 0, 1 ); + NewModifyProp( &ClipYCLIP, nw, NULL, mflags, ((yclipbord-2) * MAXPOT ) / 6, 0, + MAXPOT/6, 0, 1 ); + + while( !done ) + { + WaitPort( nw->UserPort ); + + while( imsg = (struct IntuiMessage * )GetMsg( nw->UserPort ) ) + { + gd = (struct Gadget *)imsg->IAddress; + code = imsg->Code; + class = imsg->Class; + qual = imsg->Qualifier; + msx = imsg->MouseX; + msy = imsg->MouseY; + + ReplyMsg( (struct Message *)imsg ); + + switch( class ) + { + case VANILLAKEY: + if( code == '\33' ) + okay = 0, done = 1; + else if( code == 'v' && qual == AMIGALEFT ) + okay = done = 1; + else if( code == 'b' && qual == AMIGALEFT ) + okay = 0, done = 1; + else if( code == 'o' || code == 'O' ) + okay = done = 1; + else if( code == 'c' || code == 'C' ) + okay = 0, done = 1; + break; + + case CLOSEWINDOW: + done = 1; + break; + + case GADGETUP: + drag = 0; + if( gd->GadgetID == XSIZE || gd->GadgetID == YSIZE || + gd->GadgetID == XCLIP || gd->GadgetID == YCLIP ) + { + pip = (struct PropInfo *)gd->SpecialInfo; + aidx = pip->HorizPot / (MAXPOT/6); + if( gd->GadgetID == XSIZE ) + { + mxsize = sizes[ aidx ]; + } + else if( gd->GadgetID == YSIZE ) + { + mysize = sizes[ aidx ]; + } + else if( gd->GadgetID == XCLIP ) + { + xclipbord = aidx + 2; + } + else if( gd->GadgetID == YCLIP ) + { + yclipbord = aidx + 2; + } + ShowClipValues( nw ); +#ifdef OPT_DISPMAP + dispmap_sanity(); +#endif + } + else if( gd->GadgetID == GADOKAY ) + { + done = 1; + okay = 1; + } + else if( gd->GadgetID == GADCANCEL ) + { + done = 1; + okay = 0; + } + ReportMouse( 0, nw ); + reclip = 2; + doredraw(); + flush_glyph_buffer( amii_wins[ WIN_MAP ]->win ); + reclip = 0; + break; + + case GADGETDOWN: + drag = 1; + dgad = gd; + ReportMouse( 1, nw ); + break; + + case MOUSEMOVE: + if( !drag ) + break; + pip = (struct PropInfo *)dgad->SpecialInfo; + aidx = pip->HorizPot / (MAXPOT/6); + Move( nw->RPort, dgad->LeftEdge + (nw->Width + dgad->Width) + 8, + dgad->TopEdge + nw->RPort->TxBaseline ); + if( dgad->GadgetID == XSIZE ) + { + mxsize = sizes[ aidx ]; + sprintf( buf, "%d ",lmxsize ); + } + else if( dgad->GadgetID == YSIZE ) + { + mysize = sizes[ aidx ]; + sprintf( buf, "%d ", mysize ); + } + else if( dgad->GadgetID == XCLIP ) + { + xclipbord = aidx + 2; + sprintf( buf, "%d ", xclipbord ); + } + else if( dgad->GadgetID == YCLIP ) + { + yclipbord = aidx + 2; + sprintf( buf, "%d ", yclipbord ); + } + SetAPen( nw->RPort, 5 ); + SetBPen( nw->RPort, amii_otherBPen ); + SetDrMd( nw->RPort, JAM2 ); + Text( nw->RPort, buf, strlen( buf ) ); +#ifdef OPT_DISPMAP + dispmap_sanity(); +#endif + break; + } + } + } + + CloseWindow( nw ); + + /* Restore oldvalues if cancelled. */ + if( !okay ) + { + mxsize = lmxsize; + mysize = lmysize; + xclipbord = lxclipbord; + yclipbord = lyclipbord; + } +} + +char *dirname( str ) + char *str; +{ + char *t, c; + static char dir[ 300 ]; + + t = strrchr( str, '/' ); + if( !t ) + t = strrchr( str, ':' ); + if( !t ) + t = str; + else + { + c = *t; + *t = 0; + strcpy( dir, str ); + *t = c; + } + return( dir ); +} + +char *basename( str ) + char *str; +{ + char *t; + + t = strrchr( str, '/' ); + if( !t ) + t = strrchr( str, ':' ); + if( !t ) + t = str; + else + ++t; + return( t ); +} + +filecopy( from, to ) + char *from, *to; +{ + char *buf; + int i = 0; + + buf = (char *)alloc( strlen(to) + strlen(from) + 20 ); + if( buf ) + { + sprintf( buf, "c:copy \"%s\" \"%s\" clone", from, to ); + + /* Check SysBase instead? Shouldn't matter */ +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + i = System( buf, NULL ); + else +#endif + Execute( buf, NULL, NULL ); + free( buf ); + } + else + { + return( -1 ); + } + return( i ); +} + +/* The colornames, and the default values for the pens */ +static struct COLDEF +{ + char *name, *defval; +}; +struct COLDEF amii_colnames[ AMII_MAXCOLORS ] = +{ + "Black","(000)", + "White","(fff)", + "Brown","(830)", + "Cyan","(7ac)", + "Green","(181)", + "Magenta","(c06)", + "Blue","(23e)", + "Red","(c00)", +}; + +struct COLDEF amiv_colnames[ AMII_MAXCOLORS ] = +{ + "Black","(000)", + "White","(fff)", + "Cyan","(0bf)", + "Orange","(f60)", + "Blue","(00f)", + "Green","(090)", + "Grey","(69b)", + "Red","(f00)", + "Light Green","(6f0)", + "Yellow","(ff0)", + "Magenta","(f0f)", + "Brown","(940)", + "Grey Blue","(466)", + "Light Brown","(c40)", + "Light Grey","(ddb)", + "Peach","(fb9)", + "Col 16","(222)", + "Col 17","(eee)", + "Col 18","(000)", + "Col 19","(ccc)", + "Col 20","(bbb)", + "Col 21","(aaa)", + "Col 22","(999)", + "Col 23","(888)", + "Col 24","(777)", + "Col 25","(666)", + "Col 26","(555)", + "Col 27","(444)", + "Col 28","(333)", + "Col 29","(18f)", + "Col 30","(f81)", + "Col 31","(fff)", +}; + +void +ClearCol( struct Window *w ) +{ + int bxorx, bxory, bxxlen, bxylen; + int incx, incy; + + bxylen = Col_Okay.TopEdge - ( Col_BluePen.TopEdge + Col_BluePen.Height ) - 1 + - txheight - 3; + bxxlen = Col_BluePen.Width - 2; + bxorx = Col_BluePen.LeftEdge + 1; + bxory = Col_BluePen.TopEdge + Col_BluePen.Height + 2; + + incx = bxxlen / amii_numcolors; + incy = bxylen - 2; + + bxxlen /= incx; + bxxlen *= incx; + bxxlen += 2; + + SetAPen( w->RPort, C_WHITE ); + SetDrMd( w->RPort, JAM1 ); + RectFill( w->RPort, bxorx, bxory, bxorx + bxxlen + 1, bxory + bxylen ); + + SetAPen( w->RPort, C_BLACK ); + RectFill( w->RPort, bxorx+1, bxory+1, + bxorx + bxxlen, bxory + bxylen - 1); +} + +void +DrawCol( w, idx, colors ) + struct Window *w; + int idx; + UWORD *colors; +{ + int bxorx, bxory, bxxlen, bxylen; + int i, incx, incy, r, g, b; + long mflags; + + bxylen = Col_Okay.TopEdge - ( Col_BluePen.TopEdge + Col_BluePen.Height ) - 1 + - txheight - 3; + bxxlen = Col_BluePen.Width - 2; + bxorx = Col_BluePen.LeftEdge + 1; + bxory = Col_BluePen.TopEdge + Col_BluePen.Height + 2; + + incx = bxxlen / amii_numcolors; + incy = bxylen - 2; + + bxxlen /= incx; + bxxlen *= incx; + bxxlen += 2; + + for( i = 0; i < amii_numcolors; ++i ) + { + int x, y; + x = bxorx + 2 + (i*incx); + y = bxory + 2; + + if( i == idx ) + { + SetAPen( w->RPort, flags.amii_dripens[ SHADOWPEN ] ); + Move( w->RPort, x, y+bxylen-4 ); + Draw( w->RPort, x, y ); + Draw( w->RPort, x+incx-1, y ); + + Move( w->RPort, x+1, y+bxylen-5 ); + Draw( w->RPort, x+1, y+1 ); + Draw( w->RPort, x+incx-2, y+1 ); + + SetAPen( w->RPort, flags.amii_dripens[ SHINEPEN ] ); + Move( w->RPort, x+incx-1, y+1 ); + Draw( w->RPort, x+incx-1, y+bxylen-4 ); + Draw( w->RPort, x, y+bxylen-4 ); + + Move( w->RPort, x+incx-2, y+2 ); + Draw( w->RPort, x+incx-2, y+bxylen-5 ); + Draw( w->RPort, x+1, y+bxylen-5 ); + } + else + { + SetAPen( w->RPort, C_BLACK ); + Move( w->RPort, x, y ); + Draw( w->RPort, x +incx-1, y ); + Draw( w->RPort, x +incx-1, y +bxylen - 4 ); + Draw( w->RPort, x, y + bxylen - 4 ); + Draw( w->RPort, x, y ); + SetAPen( w->RPort, C_BLACK ); + Move( w->RPort, x+1, y+1 ); + Draw( w->RPort, x +incx-2, y+1 ); + Draw( w->RPort, x +incx-2, y +bxylen - 6 ); + Draw( w->RPort, x+1, y + bxylen - 6 ); + Draw( w->RPort, x+1, y+1 ); + } + + SetAPen( w->RPort, i ); + RectFill( w->RPort, x + 3, y + 3, x + incx - 4, y + bxylen - 6 ); + } + + DispCol( w, idx, colors ); + + r = (colors[ idx ] & 0xf00) >> 8; + g = (colors[ idx ] & 0x0f0) >> 4; + b = colors[ idx ] & 0x00f; + + mflags = AUTOKNOB|FREEHORIZ; +#ifdef INTUI_NEW_LOOK + if( IntuitionBase->LibNode.lib_Version >= 37 ) + { + mflags |= PROPNEWLOOK; + } +#endif + NewModifyProp( &Col_RedPen, w, NULL, mflags, (r * MAXPOT ) / 15, 0, + MAXPOT/15, 0, 1 ); + NewModifyProp( &Col_GreenPen, w, NULL, mflags, (g * MAXPOT ) / 15, 0, + MAXPOT/15, 0, 1 ); + NewModifyProp( &Col_BluePen, w, NULL, mflags, (b * MAXPOT ) / 15, 0, + MAXPOT/15, 0, 1 ); +} + +void +DispCol( w, idx, colors ) + struct Window *w; + int idx; + UWORD *colors; +{ + char buf[ 50 ]; + char *colname, *defval; + + if( WINVERS_AMIV ) + { + colname = amiv_colnames[idx].name; + defval = amiv_colnames[idx].defval; + } + else + { + colname = amii_colnames[idx].name; + defval = amii_colnames[idx].defval; + } + + if( colname == NULL ) + { + colname = "unknown"; + defval = "unknown"; + } + Move( w->RPort, Col_Save.LeftEdge, + Col_Save.TopEdge - 7 ); + sprintf( buf, "%s=%03x default=%s%s", colname, colors[idx], defval, + " "+strlen(colname)+1 ); + SetAPen( w->RPort, C_RED ); + SetBPen( w->RPort, amii_otherBPen ); + SetDrMd( w->RPort, JAM2 ); + Text( w->RPort, buf, strlen( buf ) ); +} + +void +amii_setpens( int count ) +{ +#ifdef INTUI_NEW_LOOK + struct EasyStruct ea = { + sizeof( struct EasyStruct ), + 0l, + "NetHack Request", + "Number of pens requested(%ld) not correct", + "Use default pens|Use requested pens" + }; + struct EasyStruct ea2 = { + sizeof( struct EasyStruct ), + 0l, + "NetHack Request", + "Number of pens requested(%ld) not\ncompatible with game configuration(%ld)", + "Use default pens|Use requested pens" + }; +#endif + /* If the pens in amii_curmap are + * more pens than in amii_numcolors, then we choose to ignore + * those pens. + */ +#ifdef INTUI_NEW_LOOK + if( IntuitionBase && IntuitionBase->LibNode.lib_Version >= 39 ) + { + if( count != amii_numcolors ) + { + long args[2]; + args[0] = count; + args[1] = amii_numcolors; + if( EasyRequest( NULL, &ea2, NULL, args ) == 1 ) + { + memcpy( flags.amii_curmap, amii_initmap, + amii_numcolors*sizeof(amii_initmap[0])); + } + } + } + else if( IntuitionBase && IntuitionBase->LibNode.lib_Version >= 37 ) + { + if( count != amii_numcolors ) + { + if( EasyRequest( NULL, &ea, NULL, NULL ) == 1 ) + { + memcpy( flags.amii_curmap, amii_initmap, + amii_numcolors*sizeof(amii_initmap[0])); + } + } + } + else +#endif + if( count != amii_numcolors ) + { + memcpy( flags.amii_curmap, amii_initmap, + amii_numcolors*sizeof(amii_initmap[0])); + } + + /* If the pens are set in NetHack.cnf, we can get called before + * HackScreen has been opened. + */ + if( HackScreen != NULL ) + { + LoadRGB4( &HackScreen->ViewPort, flags.amii_curmap, amii_numcolors ); + } +} + +/* Generate a requester for a string value. */ + +void amii_getlin(prompt,bufp) + const char *prompt; + char *bufp; +{ + getlind(prompt,bufp,0); +} + +/* and with default */ +void getlind(prompt,bufp, dflt) + const char *prompt; + char *bufp; + const char *dflt; +{ +#ifndef TOPL_GETLINE + register struct Window *cwin; + register struct IntuiMessage *imsg; + register long class, code, qual; + register int aredone = 0; + register struct Gadget *gd; + static int once; + + *StrString = 0; + if( dflt ) + strcpy( StrString, dflt ); + StrWindow.Title = (UBYTE *)prompt; + StrWindow.Screen = HackScreen; + + if( !once ) + { + if( bigscreen ) { + StrWindow.LeftEdge = (HackScreen->Width/2) - (StrWindow.Width/2); + if (amii_wins[WIN_MAP]) { + StrWindow.TopEdge = amii_wins[WIN_MAP]->win->TopEdge; + } else { + StrWindow.TopEdge = (HackScreen->Height/2) - (StrWindow.Height/2); + } + } + SetBorder( &String ); + SetBorder( &Gadget2 ); + once = 1; + } + + if( WINVERS_AMIV || WINVERS_AMII ) + { +#ifdef INTUI_NEW_LOOK + StrWindow.Extension = wintags; + StrWindow.Flags |= WFLG_NW_EXTENDED; +# ifdef __GNUC__ + fillhook.h_Entry = (void *)&LayerFillHook; +# else + fillhook.h_Entry = (ULONG(*)())LayerFillHook; +# endif + fillhook.h_Data = (void *)-2; + fillhook.h_SubEntry = 0; +#endif + } + + if( ( cwin = OpenWindow( (void *)&StrWindow ) ) == NULL ) + { + return; + } + + while( !aredone ) + { + WaitPort( cwin->UserPort ); + while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL ) + { + class = imsg->Class; + code = imsg->Code; + qual = imsg->Qualifier; + gd = (struct Gadget *) imsg->IAddress; + + switch( class ) + { + case VANILLAKEY: + if( code == '\033' && (qual & + (IEQUALIFIER_LALT|IEQUALIFIER_RALT| + IEQUALIFIER_LCOMMAND|IEQUALIFIER_RCOMMAND) ) == 0 ) + { + if( bufp ) + { + bufp[0]='\033'; + bufp[1]=0; + } + aredone = 1; + } + else + { + ActivateGadget( &String, cwin, NULL ); + } + break; + + case ACTIVEWINDOW: + ActivateGadget( &String, cwin, NULL ); + break; + + case GADGETUP: + switch( gd->GadgetID ) + { + case 2: + aredone = 1; + if( bufp ) + strcpy( bufp, StrString ); + break; + + case 1: + if( bufp ) + { + bufp[0]='\033'; + bufp[1]=0; + } + aredone = 1; + break; + } + break; + + case CLOSEWINDOW: + if( bufp ) + { + bufp[0]='\033'; + bufp[1]=0; + } + aredone = 1; + break; + } + ReplyMsg( (struct Message *) imsg ); + } + } + + CloseWindow( cwin ); +#else + struct amii_WinDesc *cw; + struct Window *w; + int colx, ocolx, c; + char *obufp; + + amii_clear_nhwindow( WIN_MESSAGE ); + amii_putstr( WIN_MESSAGE, 0, prompt ); + cw = amii_wins[ WIN_MESSAGE ]; + w = cw->win; + ocolx = colx = strlen( prompt ) + 1; + + obufp = bufp; + cursor_on(WIN_MESSAGE); + while((c = WindowGetchar()) != EOF) + { + cursor_off(WIN_MESSAGE); + amii_curs( WIN_MESSAGE, colx, 0 ); + if(c == '\033') + { + *obufp = c; + obufp[1] = 0; + return; + } + else if(c == '\b') + { + if(bufp != obufp) + { + bufp--; + amii_curs( WIN_MESSAGE, --colx, 0); + Text( w->RPort, "\177 ", 2 ); + amii_curs( WIN_MESSAGE, colx, 0); + } + else + DisplayBeep( NULL ); + } + else if( c == '\n' || c == '\r' ) + { + *bufp = 0; + amii_addtopl( obufp ); + return; + } + else if(' ' <= c && c < '\177') + { + /* avoid isprint() - some people don't have it + ' ' is not always a printing char */ + *bufp = c; + bufp[1] = 0; + + Text( w->RPort, bufp, 1 ); + Text( w->RPort, "\177", 1 ); + if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO) + { + colx++; + bufp++; + } + } + else if(c == ('X'-64) || c == '\177') + { + amii_curs( WIN_MESSAGE, ocolx, 0 ); + Text( w->RPort, + " ", + colx - ocolx ); + amii_curs( WIN_MESSAGE, colx = ocolx, 0 ); + } else + DisplayBeep( NULL ); + cursor_on(WIN_MESSAGE); + } + cursor_off(WIN_MESSAGE); + *bufp = 0; +#endif +} + +void amii_change_color( pen, val, rev ) + int pen, rev; + long val; +{ + if( rev ) + flags.amii_curmap[ pen ] = ~val; + else + flags.amii_curmap[ pen ] = val; + + if( HackScreen ) + LoadRGB4( &HackScreen->ViewPort, flags.amii_curmap, amii_numcolors ); +} + +char * +amii_get_color_string( ) +{ + int i; + char s[ 10 ]; + static char buf[ BUFSZ ]; + + *buf = 0; + for( i = 0; i < min(32,amii_numcolors); ++i ) + { + sprintf( s, "%s%03lx", i ? "/" : "", (long)flags.amii_curmap[ i ] ); + strcat( buf, s ); + } + + return( buf ); +} diff --git a/sys/amiga/winstr.c b/sys/amiga/winstr.c new file mode 100644 index 0000000..0ff2f7d --- /dev/null +++ b/sys/amiga/winstr.c @@ -0,0 +1,522 @@ +/* SCCS Id: @(#)winstr.c 3.1 93/04/02 */ +/* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "NH:sys/amiga/windefs.h" +#include "NH:sys/amiga/winext.h" +#include "NH:sys/amiga/winproto.h" + +/* Put a string into the indicated window using the indicated attribute */ + +void +amii_putstr(window,attr,str) + winid window; + int attr; + const char *str; +{ + int fudge; + int len; + struct Window *w; + register struct amii_WinDesc *cw; + char *ob; + int i, j, n0, bottom, totalvis, wheight; + static int wrapping = 0; + + /* Always try to avoid a panic when there is no window */ + if( window == WIN_ERR ) + { + window = WIN_BASE; + if( window == WIN_ERR ) + window = WIN_BASE = amii_create_nhwindow( NHW_BASE ); + } + + if( window == WIN_ERR || ( cw = amii_wins[window] ) == NULL ) + { + iflags.window_inited=0; + panic(winpanicstr,window, "putstr"); + } + + w = cw->win; + + if(!str) return; + amiIDisplay->lastwin = window; /* do we care??? */ + + /* NHW_MENU windows are not opened immediately, so check if we + * have the window pointer yet + */ + + if( w ) + { + /* Set the drawing mode and pen colors */ + SetDrMd( w->RPort, JAM2 ); + amii_sethipens( w, cw->type, attr ); + } + else if( cw->type != NHW_MENU && cw->type != NHW_TEXT ) + { + panic( "NULL window pointer in putstr 2: %d", window ); + } + + /* Okay now do the work for each type */ + + switch(cw->type) + { + case NHW_MESSAGE: + if( WINVERS_AMIV ) + fudge = 2; + else + { + /* 8 for --more--, 1 for preceeding sp, 1 for putstr pad */ + fudge = 10; + } + + /* There is a one pixel border at the borders, so subtract two */ + bottom = amii_msgborder( w ); + + wheight = ( w->Height - w->BorderTop - + w->BorderBottom - 3 ) / w->RPort->TxHeight; + + if (scrollmsg || wheight > 1) + fudge = 0; + + amii_scrollmsg( w, cw ); + + while (isspace(*str)) str++; + strncpy( toplines, str, TBUFSZ ); + toplines[ TBUFSZ - 1 ] = 0; + + /* For initial message to be visible, we need to explicitly position the + * cursor. This flag, cw->curx == -1 is set elsewhere to force the + * cursor to be repositioned to the "bottom". + */ + if( cw->curx == -1 ) + { + amii_curs( WIN_MESSAGE, 1, bottom ); + cw->curx = 0; + } + + /* If used all of history lines, move them down */ + if( cw->maxrow >= iflags.msg_history ) + { + if( cw->data[ 0 ] ) + free( cw->data[ 0 ] ); + memcpy( cw->data, &cw->data[ 1 ], + ( iflags.msg_history - 1 ) * sizeof( char * ) ); + cw->data[ iflags.msg_history - 1 ] = + (char *) alloc( strlen( toplines ) + 5 ); + strcpy( cw->data[ i = iflags.msg_history - 1 ] + + SOFF + (scrollmsg!=0), toplines ); + } + else + { + /* Otherwise, allocate a new one and copy the line in */ + cw->data[ cw->maxrow ] = (char *) + alloc( strlen( toplines ) + 5 ); + strcpy( cw->data[ i = cw->maxrow++ ] + + SOFF + (scrollmsg!=0), toplines ); + } + cw->data[ i ][ SEL_ITEM ] = 1; + cw->data[ i ][ VATTR ] = attr+1; + + if( scrollmsg ) + { + cw->curx = 0; + cw->data[ i ][2] = (cw->wflags & FLMSG_FIRST ) ? '>' : ' '; + } + + str = cw->data[i] + SOFF; + if( cw->curx + strlen(str) >= (cw->cols-fudge) ) + { + int i; + char *ostr = (char *)str; + char *p; + + while( cw->curx + strlen( str ) >= (cw->cols-fudge) ) + { + for(p=((char *)&str[ cw->cols-1 - cw->curx ])-fudge; !isspace(*p) && p > str;) + --p; + if (p < str) p = (char *)str; + + if( p == str ) { + /* p = (char *)&str[ cw->cols ]; */ + outmore(cw); + continue; + } + + i = (long)p-(long)str; + outsubstr( cw, (char *)str, i, fudge ); + cw->curx += i; + + while(isspace(*p)) p++; + str = p; + +#if 0 + if( str != ostr ) { + outsubstr( cw, "+", 1, fudge ); + cw->curx+=2; + } +#endif + if(*str) + amii_scrollmsg( w, cw ); + amii_cl_end( cw, cw->curx ); + } + + if( *str ) + { + if( str != ostr ) + { + outsubstr( cw, "+", 1, fudge ); + cw->curx+=2; + } + while ( isspace( *str ) ) + ++str; + outsubstr( cw, (char *)str, i = strlen( (char *)str ), fudge ); + cw->curx += i; + amii_cl_end( cw, cw->curx ); + } + } + else + { + outsubstr( cw, (char *)str, i = strlen( (char *)str ), fudge ); + cw->curx += i; + amii_cl_end( cw, cw->curx ); + } + cw->wflags &= ~FLMSG_FIRST; + len = 0; + if( scrollmsg ) + { + totalvis = CountLines( window ); + SetPropInfo( w, &MsgScroll, + ( w->Height-w->BorderTop-w->BorderBottom ) / w->RPort->TxHeight, + totalvis, totalvis ); + } + i = strlen( toplines + SOFF ); + cw->maxcol = max( cw->maxcol, i ); + cw->vwy = cw->maxrow; + break; + + case NHW_STATUS: + if( cw->data[ cw->cury ] == NULL ) + panic( "NULL pointer for status window" ); + ob = &cw->data[cw->cury][j = cw->curx]; + if(flags.botlx) *ob = 0; + + /* Display when beam at top to avoid flicker... */ + WaitTOF(); + Text(w->RPort,(char *)str,strlen((char *)str)); + if( cw->cols > strlen( str ) ) + TextSpaces( w->RPort, cw->cols - strlen( str ) ); + + (void) strncpy(cw->data[cw->cury], str, cw->cols ); + cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */ + cw->cury = (cw->cury+1) % 2; + cw->curx = 0; + break; + + case NHW_MAP: + case NHW_BASE: + if (cw->type == NHW_BASE && wrapping) { + amii_curs(window, cw->curx+1, cw->cury); + TextSpaces(w->RPort, cw->cols); + if (cw->cury < cw->rows) { + amii_curs(window, cw->curx+1, cw->cury+1); + TextSpaces(w->RPort, cw->cols); + cw->cury--; + } + } + amii_curs(window, cw->curx+1, cw->cury); + Text(w->RPort,(char *)str,strlen((char *)str)); + cw->curx = 0; + /* CR-LF is automatic in these windows */ + cw->cury++; + if (cw->type == NHW_BASE && cw->cury >= cw->rows) { + cw->cury = 0; + wrapping = 1; + } + break; + + case NHW_MENU: + case NHW_TEXT: + + /* always grows one at a time, but alloc 12 at a time */ + + if( cw->cury >= cw->rows || !cw->data ) + { + char **tmp; + + /* Allocate 12 more rows */ + cw->rows += 12; + tmp = (char**) alloc(sizeof(char*) * cw->rows); + + /* Copy the old lines */ + for(i=0; icury; i++) + tmp[i] = cw->data[i]; + + if( cw->data ) { + free( cw->data ); + cw->data = NULL; + } + + cw->data = tmp; + + /* Null out the unused entries. */ + for(i=cw->cury; irows; i++) + cw->data[i] = 0; + } + + if( !cw->data ) + panic("no data storage"); + + /* Shouldn't need to do this, but... */ + + if( cw->data && cw->data[cw->cury] ) { + free( cw->data[cw->cury] ); + cw->data[cw->cury] = NULL; + } + + n0 = strlen(str)+1; + cw->data[cw->cury] = (char*) alloc(n0+SOFF); + + /* avoid nuls, for convenience */ + cw->data[cw->cury][VATTR] = attr+1; + cw->data[cw->cury][SEL_ITEM] = 0; + Strcpy( cw->data[cw->cury] + SOFF, str); + + if(n0 > cw->maxcol) cw->maxcol = n0; + if(++cw->cury > cw->maxrow) cw->maxrow = cw->cury; + break; + + default: + panic("Invalid or unset window type in putstr()"); + } +} + +void +amii_scrollmsg( w, cw ) + register struct Window *w; + register struct amii_WinDesc *cw; +{ + int bottom, wheight; + + bottom = amii_msgborder( w ); + + wheight = ( w->Height - w->BorderTop - + w->BorderBottom - 3 ) / w->RPort->TxHeight; + + if( scrollmsg ) + { + if( ++cw->disprows > wheight ) + { + outmore( cw ); + cw->disprows = 1; /* count this line... */ + } + else + { + ScrollRaster( w->RPort, 0, w->RPort->TxHeight, + w->BorderLeft, w->BorderTop + 1, + w->Width - w->BorderRight-1, + w->Height - w->BorderBottom - 1 ); + } + amii_curs( WIN_MESSAGE, 1, bottom ); + } +} + +int +amii_msgborder( w ) + struct Window *w; +{ + register int bottom; + + /* There is a one pixel border at the borders, so subtract two */ + bottom = w->Height - w->BorderTop - w->BorderBottom - 2; + bottom /= w->RPort->TxHeight; + if( bottom > 0 ) + --bottom; + return( bottom ); +} + +void +outmore( cw ) + register struct amii_WinDesc *cw; +{ + struct Window *w = cw->win; + + if((cw->wflags & FLMAP_SKIP) == 0) + { + if( scrollmsg ) + { + int bottom; + + bottom = amii_msgborder( w ); + + ScrollRaster( w->RPort, 0, w->RPort->TxHeight, + w->BorderLeft, w->BorderTop+1, + w->Width - w->BorderRight-1, + w->Height - w->BorderBottom - 1 ); + amii_curs( WIN_MESSAGE, 1, bottom ); /* -1 for inner border */ + Text( w->RPort, "--more--", 8 ); + } + else + Text( w->RPort, " --more--", 9 ); + + /* Make sure there are no events in the queue */ + flushIDCMP( HackPort ); + + /* Allow mouse clicks to clear --more-- */ + WindowGetchar(); + if( lastevent.type == WEKEY && lastevent.un.key == '\33' ) + cw->wflags |= FLMAP_SKIP; + } + if( !scrollmsg ) + { + amii_curs( WIN_MESSAGE, 1, 0 ); + amii_cl_end( cw, cw->curx ); + } +} + +void +outsubstr( cw, str, len, fudge ) + register struct amii_WinDesc *cw; + char *str; + int len; + int fudge; +{ + struct Window *w = cw->win; + + if( cw->curx ) + { + /* Check if this string and --more-- fit, if not, + * then put out --more-- and wait for a key. + */ + if( (len + fudge ) + cw->curx >= cw->cols ) + { + if( !scrollmsg ) + outmore( cw ); + } + else + { + /* Otherwise, move and put out a blank separator */ + Text( w->RPort, spaces, 1 ); + cw->curx += 1; + } + } + + Text(w->RPort,str,len); +} + +/* Put a graphics character onto the screen */ + +void +amii_putsym( st, i, y, c ) + winid st; + int i, y; + CHAR_P c; +{ + amii_curs( st, i, y ); + Text(amii_wins[st]->win->RPort, &c, 1); +} + +/* Add to the last line in the message window */ + +void +amii_addtopl(s) + const char *s; +{ + register struct amii_WinDesc *cw = amii_wins[WIN_MESSAGE]; + + while(*s) { + if(cw->curx == cw->cols - 1) + amii_putstr(WIN_MESSAGE, 0, ""); + amii_putsym(WIN_MESSAGE, cw->curx + 1, amii_msgborder(cw->win), *s++); + cw->curx++; + } +} + +void +TextSpaces( rp, nr ) + struct RastPort *rp; + int nr; +{ + if( nr < 1 ) + return; + + while (nr > sizeof(spaces) - 1) + { + Text(rp, spaces, (long)sizeof(spaces) - 1); + nr -= sizeof(spaces) - 1; + } + if (nr > 0) + Text(rp, spaces, (long)nr); +} + +void +amii_remember_topl() +{ + /* ignore for now. I think this will be done automatically by + * the code writing to the message window, but I could be wrong. + */ +} + +int +amii_doprev_message() +{ + struct amii_WinDesc *cw; + struct Window *w; + char *str; + + if( WIN_MESSAGE == WIN_ERR || + ( cw = amii_wins[ WIN_MESSAGE ] ) == NULL || ( w = cw->win ) == NULL ) + { + panic(winpanicstr,WIN_MESSAGE, "doprev_message"); + } + + /* When an interlaced/tall screen is in use, the scroll bar will be there */ + /* Or in some other cases as well */ + if( scrollmsg ) + { + struct Gadget *gd; + struct PropInfo *pip; + int hidden, topidx, i, total, wheight; + + for( gd = w->FirstGadget; gd && gd->GadgetID != 1; ) + gd = gd->NextGadget; + + if( gd ) + { + pip = (struct PropInfo *)gd->SpecialInfo; + wheight = ( w->Height - w->BorderTop - + w->BorderBottom - 2 ) / w->RPort->TxHeight; + hidden = max( cw->maxrow - wheight, 0 ); + topidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16; + for( total = i = 0; i < cw->maxrow; ++i ) + { + if( cw->data[i][1] != 0 ) + ++total; + } + + i = 0; + topidx -= wheight/4 + 1; + if (topidx < 0) + topidx = 0; + SetPropInfo( w, &MsgScroll, wheight, total, topidx ); + DisplayData( WIN_MESSAGE, topidx ); + } + return(0); + } + + if( --cw->vwy < 0 ) + { + cw->maxcol = 0; + DisplayBeep( NULL ); + str = "\0\0No more history saved..."; + } + else + str = cw->data[ cw->vwy ]; + + amii_cl_end(cw, 0); + amii_curs( WIN_MESSAGE, 1, 0 ); + amii_setdrawpens( amii_wins[WIN_MESSAGE]->win, NHW_MESSAGE ); + Text(w->RPort,str+SOFF,strlen(str+SOFF)); + cw->curx = cw->cols + 1; + + return( 0 ); +} diff --git a/sys/amiga/xpm2iff.c b/sys/amiga/xpm2iff.c new file mode 100644 index 0000000..82bbb01 --- /dev/null +++ b/sys/amiga/xpm2iff.c @@ -0,0 +1,353 @@ +/* SCCS Id: @(#)xpm2iff.c 3.2 95/08/04 */ +/* Copyright (c) 1995 by Gregg Wonderly, Naperville, Illinois */ +/* NetHack may be freely redistributed. See license for details. */ + +#include + +#include "config.h" +#include "tile.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _DCC +# include +# include +# include +#endif + +struct xpmscreen { + int Width; + int Height; + int Colors; + int ColorResolution; + int Background; + int AspectRatio; + int Interlace; + int BytesPerRow; +} XpmScreen; + +/* translation table from xpm characters to RGB and colormap slots */ +struct Ttable { + char flag; + char r,g,b; + int slot; /* output colortable index */ +}ttable[256]; + +pixval ColorMap[3][MAXCOLORMAPSIZE]; +int colorsinmap; + +/* + * We are using a hybrid form of our own design which we call a BMAP (for + * bitmap) form. It is an ILBM with the bitmaps already deinterleaved, + * completely uncompressed. + * This speeds the loading of the images from the games point of view because it + * does not have to deinterleave and uncompress them. + */ +#define ID_BMAP MAKE_ID( 'B', 'M', 'A', 'P' ) /* instead of ILBM */ +#define ID_BMHD MAKE_ID( 'B', 'M', 'H', 'D' ) /* Same as ILBM */ +#define ID_CAMG MAKE_ID( 'C', 'A', 'M', 'G' ) /* Same as ILBM */ +#define ID_CMAP MAKE_ID( 'C', 'M', 'A', 'P' ) /* Same as ILBM */ +#define ID_PDAT MAKE_ID( 'P', 'D', 'A', 'T' ) /* Extra data describing plane + * size due to graphics.library + * rounding requirements. + */ +#define ID_PLNE MAKE_ID( 'P', 'L', 'N', 'E' ) /* The planes of the image */ + +int nplanes; + +/* BMHD from IFF documentation */ +typedef struct { + UWORD w, h; + WORD x, y; + UBYTE nPlanes; + UBYTE masking; + UBYTE compression; + UBYTE reserved1; + UWORD transparentColor; + UBYTE xAspect, yAspect; + WORD pageWidth, pageHeight; +} BitMapHeader; + +typedef struct { + UBYTE r, g, b; +} AmiColorMap; + +pixel pixels[TILE_Y][TILE_X]; +AmiColorMap *cmap; + +void +error( char *str ) +{ + fprintf( stderr, "ERROR: %s\n", str ); +} + +char **planes; + +main( int argc, char **argv ) +{ + int colors; + struct { + long nplanes; + long pbytes; + long across; + long down; + long npics; + long xsize; + long ysize; + } pdat; + long pbytes; /* Bytes of data in a plane */ + int i, cnt; + BitMapHeader bmhd; + struct IFFHandle *iff; + long camg = HIRES|LACE; + int tiles=0; + int index; + +#if defined(_DCC) || defined (__GNUC__) + IFFParseBase = OpenLibrary( "iffparse.library", 0 ); + if( !IFFParseBase ) { + error( "unable to open iffparse.library" ); + exit( 1 ); + } +#endif + + if( fopen_xpm_file( argv[1], "r" ) != TRUE ) + { + perror( argv[1] ); + return( 1 ); + } + + nplanes = 0; + i = XpmScreen.Colors - 1; + while( i != 0 ) + { + nplanes++; + i >>= 1; + } + + planes = malloc( nplanes * sizeof( char * ) ); + if( planes == 0 ) + { + error( "can not allocate planes pointer" ); + exit( 1 ); + } + + XpmScreen.BytesPerRow = ((XpmScreen.Width + 15)/16)*2; + pbytes = XpmScreen.BytesPerRow * XpmScreen.Height; + for( i = 0; i < nplanes; ++i ) + { + planes[ i ] = malloc( pbytes ); + if( planes[ i ] == 0 ) + { + error( "can not allocate planes pointer" ); + exit( 1 ); + } + memset( planes[i], 0, pbytes ); + } + + iff = AllocIFF(); + if( !iff ) + { + error( "Can not allocate IFFHandle" ); + return( 1 ); + } + + iff->iff_Stream = Open( argv[2], MODE_NEWFILE ); + if( !iff->iff_Stream ) + { + error( "Can not open output file" ); + return( 1 ); + } + + InitIFFasDOS( iff ); + OpenIFF( iff, IFFF_WRITE ); + + PushChunk( iff, ID_BMAP, ID_FORM, IFFSIZE_UNKNOWN ); + + bmhd.w = XpmScreen.Width; + bmhd.h = XpmScreen.Height; + bmhd.x = 0; + bmhd.y = 0; + bmhd.nPlanes = nplanes; + bmhd.masking = 0; + bmhd.compression = 0; + bmhd.reserved1 = 0; + bmhd.transparentColor = 0; + bmhd.xAspect = 100; + bmhd.yAspect = 100; + bmhd.pageWidth = 0; /* not needed for this program */ + bmhd.pageHeight = 0; /* not needed for this program */ + + PushChunk( iff, ID_BMAP, ID_BMHD, sizeof( bmhd ) ); + WriteChunkBytes( iff, &bmhd, sizeof( bmhd ) ); + PopChunk( iff ); + + PushChunk( iff, ID_BMAP, ID_CAMG, sizeof( camg ) ); + WriteChunkBytes( iff, &camg, sizeof( camg ) ); + PopChunk( iff ); + +#define SCALE(x) (x) + cmap = malloc( (colors = (1L<iff_Stream ); + FreeIFF( iff ); + +#if defined(_DCC) || defined (__GNUC__) + CloseLibrary( IFFParseBase ); +#endif + exit( 0 ); +} + +#define SETBIT(Plane, Plane_offset, Col, Value) \ + if(Value){ \ + planes[Plane][Plane_offset + (Col/8)] |= 1<<(7-(Col & 7)); \ + } + +conv_image(){ + int row, col, planeno; + + for(row = 0;row if any */ + for(bp = buf;*bp;bp++); + bp--; + while(isspace(*bp))bp--; + if(*bp==',')bp--; + if(*bp=='"')bp--; + bp++; + *bp = '\0'; + + return &buf[1]; +} diff --git a/sys/atari/Install.tos b/sys/atari/Install.tos new file mode 100644 index 0000000..76d1084 --- /dev/null +++ b/sys/atari/Install.tos @@ -0,0 +1,124 @@ + Instructions for compiling and installing NetHack 3.4 + on a TOS system + ===================================================== + (or, How to make ST NetHack 3.4) + Last revision: 2 February 2000 + +1. Make sure all the NetHack files are in the appropriate directory structure. + You should have a main directory with subdirectories dat, doc, include, + src, util, sys\atari, sys\share, sys\unix, and at least one of win\tty + and win\gem. You may have other subdirectories under sys and win, but + they needn't concern you. If you do not follow this structure, the + Makefiles will not function properly. The .c files for the main program + belong in src, those for utility programs in util, and Atari-specific + ones in sys\atari. All the .h files belong in include, the documentation + in doc, and assorted data files in dat. You may also use random.c from + sys\share. The Makefiles belong in sys\unix. (A more detailed + explanation of the directory structure may be found in Files, which + should be in the top directory.) + + +2. If you don't already have a good command line interpreter, get one. + Doing all of the following from the desktop or a GEM shell will + probably be a *big* pain. If you can get a Bourne shell compatible + one, and put it in \bin\sh, then you'll save yourself some trouble + with the Makefiles. There are several good shells on various + FTP sites (including atari.archive.umich.edu). + + Run the "setup.g" shell script in sys\atari. This will setup all the + makefiles and other files in the appropriate directories. It assumes + that your compiler prefers \ to / as a directory separator. If not, + simply copy the makefiles instead of running sed on them. + +3. Now go to the include subdirectory to edit a couple of the header files + there. + + First edit config.h according to the comments to match your system and + desired set of features. In particular: + make sure that UNIX is *not* defined, and TOS is (if you're using + the MiNT library, and/or the -mint option to gcc, this will + be done automatically) + make sure that HACKDIR is defined properly (or not at all) + make sure that COMPRESS is not defined + + Also edit tosconf.h; this shouldn't need much changing. If you are not + going to include random.c you will need to comment out RANDOM. Gcc users + don't need RANDOM, since the gcc and MiNT libraries have a Berkeley + derived srandom() function already. If you have no termcap support and + don't want to use the supplied termcap.uu, comment out TERMLIB. Gcc has + a termcap library, so TERMLIB should always be "on" with gcc (and you + don't need to worry about termcap.uu at all). + +4. If you want to change the high score list behavior, examine the top of + topten.c, in the src directory. You may want to change the definitions of + PERSMAX, POINTSMIN, and ENTRYMAX. I set POINTSMIN to 51 and ENTRYMAX to + 50 to keep the size of the score list down. + +5. Go to the src directory and edit your Makefile. You'll want the Systos + target configuration; the comments explain most of what needs to be done, + at least for the gcc. + + Next, go to the top, util, dat, and doc directories, and edit the Makefiles + there, as necessary. You'll need nroff and/or TeX to do the files in doc; + if you don't have one/both of these, you can skip it (docs?? we don't need + no steenking docs :-)). + + If you elected to use Fred Fish's termcap library (bundled in as + termcap.arc), you will have to generate termcap.a from those sources. + + If you are recompiling after patching your sources, or if you got your + files from somewhere other than the official distribution, "touch + makedefs.c" to ensure that certain files (onames.h and pm.h) are remade, + lest potentially troublesome timestamps fool "make." + +8. Now, enter "make all", and take a long siesta; your computer will be + occupied for a long time. If all goes well, you will get an executable. + If you tried to compile in too many features, you will probably get a + dysfunctional executable, and will have to start over. + + Hint 1: If you're short on memory, you might enter "make -n all + >make.bat," and then run script.bat with some sort of batch + program or with the gulam command "script make.bat." + + Hint 2: You'll save yourself a lot of grief if you use the GNU + version of the "make" program. Some of the smaller makes aren't + completely compatible. GNU software for the Atari is widely + available; for example, by anonymous FTP from atari.archive.umich.edu. + +9. Make sure the support files -- data, rumors, cmdhelp, opthelp, help, hh, + history, license, and oracles, or simply nhdat if DLB was defined -- + were copied to the game directory. If not, move them there from the + dat directory yourself. rumors can be created manually by entering + "makedefs -r;" data by entering "makedefs -d." + + Also, make sure that the various level files (*.lev, from the dat + subdirectory) were copied over correctly. If you're not sure what files + should have been made, double check with Makefile.dat. + +10. Go to the src\atari directory. Copy atari.cnf to your game directory + as NetHack.cnf. Edit it to reflect your particular setup and personal + preferences, following the comments. + + If you compiled in the TERMLIB feature, also move the "termcap" file to + your game directory. (Note: gcc's termcap routines have built-in + defaults, so the termcap file is not necessary with that compiler.) + + If you're running NetHack from the MultiTOS desktop, and you want a + more useful set of drop down menus than the plain system "File/Edit" + ones, copy nethack.mnu to your games directory. This file contains a + menu definition that puts a lot of the common commands into the menu. + +11. Play NetHack. If it works, you're done! + + +Notes +----- + +1) Save files and bones files from previous versions will not work with + NetHack 3.4. Don't bother trying to keep them. + +2) To install an update of NetHack after changing something, enter "make" + from the src directory. If you add, delete, or reorder monsters or + objects, or you change the format of saved level files, delete any save + and bones files. (Trying to use such files sometimes produces amusing + confusions on the game's part, but more often produces crashes.) diff --git a/sys/atari/atarifnt.uue b/sys/atari/atarifnt.uue new file mode 100644 index 0000000..7abba9d --- /dev/null +++ b/sys/atari/atarifnt.uue @@ -0,0 +1,119 @@ +table + !"#$%&'()*+,-./0123456789:;<=>? +@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ +begin 644 hackfnt.arc +M&@A(04-+1DY4+E!21P E!, #\48JO>BQ\= ,8#0 #" S4 ,! ! &@!Bz +M8 "#D!2 > . !Q_P%!5= 'D!0(GN10.X('EB9,Z1L+0"<,&1!DY*5L<@-B,#H(!<(N' 8XIH'SYK3.CQSX057EA26DQ\r +MPP$/)REU#@XPB$ 1!G0 0$(: &!033IT8:&$"0KPY1<)0XR@#%5_4O90D1D<"*$)%H((p +M\1T&9O C)HDD(,.H$I#R \9 B%1Z::9HFB&+HHQ*\Q4@$&2 !" R (!$,"!Lo +MB<0;",#!#S"!4 -(-P(L0$8"*1J 3#1,GNM,T),m +M8$< (PQ#%1UHGB%!I5*81\>GD8;JCA"2@" %H 4R0 ')RW% P@\ZO " !H l +M>>.0 !1) ))U+(4#""D*H,^.3/PP0)!!@"PRQ76 @!Z5/%1,5,HZ_(//"#Dk +MD3,#: P3_2)LS)@#4:4< V708,0A\>,3@X(80P0Z%CR-^,G"0D)$00%@$8 ("#0CD i +MH9 +/!B@4 E@R- J) ;$PX.,"_&PD$(&M E) 0B(=+E"&Q0."08%@,(T'ZM_h +MKA !"\P)">$ , W)#6UN"HD&#F PX.P&% X&!U4"L"D&8.C$0&W"(P^ 3@ Xg +M()(!LTNN4 %@Z#Z Y &(!(,>>IC1N4A;C)[Y["3$-E D(N%#EPT#F>] UAY(f +M#PD"A1M^^.?^>,\ '_YP1_<,@P<( '/_CA@GXP #/ 8 PXK1 ?d +M/O"#3FIH!C]X@#UM@H/Q . XZ7F' <#87^\P (/XF8$!/# ##. 711C R8H?c +M!( )I<@#*RX$ QXP P8\@C&G(0L!G*"UB@ E,>"(?],"Z+YAO).@#b +M !5:PSX9@0,$/O!! $PH(S^X@7A?[%__%@*!IOC Q#P0=; 8(#>#80-68,!a +M#/#0/Q#B@ ,X\(<$J1YB$ ,,L*+Iz +M$N,'#+APA"'<7/0 X_K-"%?/##0GHH0QKV<"'\<.$.=>)#]ASN<)MR'&R.y +MF$2%',!TP6/B0'182PP03R?E7(@? ( /=(K1# ?! 9P\@('9P1&-:@%!_N"Xx +M3@ LDG 04(8QC%&%.VJ. .E306O6N4YPQ& ,-A@ (;7H!OAE;IW]4Q$$_- 2w +M"&RTDW!P0 8P@ $!$"%K>$!"_GA A;00!?^L,8 Q/@ .* 2%*I<2"M?N3DAv +M N T*C@/\# !AR8$P=97(@!S"#/B8$!#DL5HSG] 9PFCV!P'T ">@:/Q:P ,:@$4.^_P/;XA7L%\Z+,& 6T-'4+3J#JQ!@CK20!C#&2IR;/EF_$1Z;!BKY;6g +M,"\\P.@+'H2<>MP5KYIUPH<-V/I^8(!!G&,(J85$6XP'68@-L+%4-YAA&S>_f +M<$!%(0M>\^,#^ #V/XRF@EJ8 \8R(L !,#L(9\?8#@Y@:B80(6=4,*"&,!"$e +M'VKH[;ZX$)0D]4-K%@*-K]'U'(&HK37TH0M&=(*%'N! #CBQ95SH8"$(U(4.d +M*GD >,P( /\PM3>JM/$9H1B7;#! +\4W$#B0\,B/(H86+D'.$&"D&\ZYE\$,!_VT$Z<^P-2KNVY1'5(b +M5QC((+ULN* =W+"-"NM0)'H0A2X '41_RC8 $ !%7HQ#QGS "/?R&P.GVV a +MI>+@'8C ,0K.+L5&L8%AMU4M6E5?.!^()*B?0P+#X%*2( ^2P B!P /X $2z +MD >>H$K L$JJI$I^ $/X#Y?!'D&8 .31U))-0"ZA'FLQGF&8VG<$'HX%G+?y +M90Q&YV+8T#LZX0\?A%YF)1(HN'DC]V/;!08?%(/(9U2' WO*A#T6IF8N! ;_x +M4'P)<&IBM'F0)44N9'08@ TVH$-VX ??8'WPM! *H N[YFO>)V+# $JP $[w +MH 4]A!' L&S)Q'X>Q 9F4&U@@ (8@':-@@+X!VYHUCL+D348 770P),Q0&Zv +M8 _6@ W#0%4

@4X,@ TZ6'LP>'Y5-%< ( 0ZD3RUt +M-SLSAV/8@ UAA0 'AGSOM!!&!V#3X S0)UX#9HR;PX6,]5/@$(;!1F)4@ 'Ss +MX ;#\ _^ _P@%G!\&(QUGZ*=F-S" -H5T.J8 9 )F3($SRV*$L\($0!V$7,r +M90[&@ 4O9C04T(B/J%.1N%0"@!"6Z&*8"$KP5#B9QD0B9T5+)#T#(@[BI56Fq +MB(J_J :K""D\ %9G%3PB\8LXYDMO)E\U5'NL&)(AA%[B- PF&8R$$XM@4(PFp +MAU5=I$+*.#O21$D&H $8,'3;A9/5. *,%"BLSEX!4\J\ ]7%8Y\0(XV\ >#o +M "IM4Y?I%8 !@TS]#Z%IPP@#.P ,U9&$YY@&6R6"])$YL!@D+@%C45&M#l +MF$.]] ?IY4LAA%7M%6#2\GRQ9ICB2$TBH0"LLY2)A5CI0P4OY&S_P ?\P \Vk +M, 8Y-#KMQP:NQP:;X@\XT 5&PP-L,)<;Y7%%B FO8SH-T$3[,P!!( BKLYZUj +MLT)&XY> "0!V0$1M!9$\Z$)Q]$'_h +M@#QPT%X?"CT>2DD8\ /YTUZ\P M_H)/!>3V(Y:$+D5"7LPQLQIS\$$C)))T&g +M<)Y@X'C]DQZ>,P#P$$G@^47Z* $-X3D1X &8XP!-! E:JA KT$^V@SWZ=@#Kf +MU#L#TA:8d +M8P"A T\!8ACS83Y"J1!PYH$C-D@D18ZIPP<&P#H(ZCIXT'B"4T'%8U3B=CT"c +M\$$A!$P3143&U(2-,M#R+>60#<601Q%*@E G"z +MIEL\$*C D$H2Z$J1Z'%@<(&Q4T'1=TL.N4ON)UW4A4ZX! (%D48U%UB9 #6y +MZ0Z#5@!DI1/!XQV'DQYKU41MY1W>!$YJYAT.^Y"%\Y#/0U>%@PJU,@.U L1(D35$'^0%3--1+/17#!$V?C@D[JI$%&u +M)XM.IZ^t +M1&&6R%\1^TI+@?)$0D%7#KA $H1BZ9EZ#^![-MP0!Jq +MX'![%G%^!@=@<'HNMD*K]T&SXW$P '(Z@6 DE4'^8'*S6%KF18.>0 /)8I47/9@?FR@;4Q@=BD$&+"RDZQ U4<'K%6SIHn +M23AL /+ P#G, Q/-@SYD D/7('>\ ^_59"L=) NA!! 53R9!P,=6'D+$8*:m +MMU16%#R;0U)])HLJ*+NI*&"95TNN2(.[=$')WR%T]*(_(-SL,0 /4l +M9 ^T'XY%GR@Q@=_4'P(D'-5Q(H^=\.$O 8$QP8VL%1V8 =9>#E># %Z$ HAk +MX6M05\9(0 7=@+[R.XYKZ -TRW[090SO@ PX-LAY+%[,T,<<]+!3" "M4B78j +M@$D1)(B0FPGS, QP,(9IY@$9@ F2/(&L!@$$@%PBX0\,21?R)$X1:470]8EFi +MA4L'9CSB99VF:)T\1%3N%))BY!TD:9,X!@-37)LKR5TMR5Y7FUAB$$4YUD,Fh +MU%U&&&!&60 7=$60&F/G ,Jg +MX%I^, ;+R0_E> ?O*YU0M*!R"(=Y'$4DP XX8 V$.R.VR 0'H8\%0 (;BU3Gf +M< X@, PXL'(;X("RFF]ZO4(>@*5DALD88 3SV:LDC % <)OYV45- ;..XQT7e +MM$(M! ::): %"@8V "?)=YD^) .*DD?VD42"I(5Z@,7FJ'8IGG9JD4(T%Z_d +M][:40TU86**__*&^I$*2?3D[/1!2Q "&\ D8\*[MI4XW>GUG=4%:)*6#M:5M68T$4(%MU8BJZJC[b +MTP"ZHP-9P]:7"@D'X(0#L %T@ 9A, 9KD%5TX (Z+@"X$.(C7N(G7N6XH.)B$ 8Wx +M3A]B@072\ 4+P1(V409*O@%\H.)E@ =I$"4J(A)63N)OP 9Rw +M\ 5D$ 9^N35L/@!$4 1&$ 15$.> H.)H@.-P\.5^61 JSN(U'B4I7A1N\ : v +M+N@B,0!%7@9T4.=XKN.4_A-VD 8QX09Q3@B53@=K, =IH =QC@A?\!)RL.EBu +MON2,<.MV'B4+P>5?8.9H'NQZK@]?8!-WKN=Z#@^W'N@K 1;'$>IN0 :O$^>#t +M(1(0$,T,@ Z@ +2H@ *X (H &H 0( .KQ !"8*)6P ZP $0X #C3@$ s +# !H r + q +end diff --git a/sys/atari/nethack.mnu b/sys/atari/nethack.mnu new file mode 100644 index 0000000..6a7d7d2 --- /dev/null +++ b/sys/atari/nethack.mnu @@ -0,0 +1,53 @@ +# Sample .mnu file for NetHack +# This will work only with MINIWIN v1.1 +# or TOSWIN v2.0 (or better) + +#%title "NetHack" +#%about "About NetHack" "v" + +File + "Shell" "!" + "Options" "O" + --- + "Save Game" "S" + --- + "Quit" ?"[1][Really quit?][ Yes | No ]":%A"#quit y":! +Edit + "Copy" %C + "Paste" %P + "Paste Options..." %O + --- + "Set Font..." %F +Inventory + "Show Inventory" "i" + -- + "Put on jewelry" "P" + "Remove jewelry" "R" + "Wear armor" "W" + "Take off armor" "T" + "Wield weapon" "w" + "Exchange weapons" "x" + "Ready ammo in quiver" "Q" + --- + "Eat" "e" + "Quaff potion" "q" + --- + "Drop" "d" + "Throw" "t" + "Fire" "f" +Move + "North" "k" 4800 + "South" "j" 5000 + "East" "l" 4d00 + "West" "h" 4b00 + --- + "Rest" "." + --- + "Open door" "o" + "Close door" "c" +Misc + "Help" "?" 6200 + "List known spells" "+" + "Cast spell" "Z" + --- + "Abort" 001b 6100 diff --git a/sys/atari/setup.g b/sys/atari/setup.g new file mode 100644 index 0000000..bcdff67 --- /dev/null +++ b/sys/atari/setup.g @@ -0,0 +1,17 @@ +# gulam shell script -- should work with tcsh and many +# other Atari shells, too + +# UNIX shells use '/' in file names, but at least some Atari shells need '\' +# so we process the UNIX makefiles to make that switch + +# sed script not included as a here document in this script because at +# least some Atari shells don't do that + +sed -f unx2atar.sed < ..\unix\Makefile.top > ..\..\Makefile +sed -f unx2atar.sed < ..\unix\Makefile.dat > ..\..\dat\Makefile +sed -f unx2atar.sed < ..\unix\Makefile.doc > ..\..\doc\Makefile +sed -f unx2atar.sed < ..\unix\Makefile.src > ..\..\src\Makefile +sed -f unx2atar.sed < ..\unix\Makefile.utl > ..\..\util\Makefile + +# KLUDGE to fix a Makefile problem +echo > ..\..\include\win32api.h diff --git a/sys/atari/tos.c b/sys/atari/tos.c new file mode 100644 index 0000000..e1f003e --- /dev/null +++ b/sys/atari/tos.c @@ -0,0 +1,372 @@ +/* SCCS Id: @(#)tos.c 3.1 90/14/08 +/* NetHack may be freely redistributed. See license for details. */ + +/* + * TOS system functions. + */ + +#define NEED_VARARGS +#include "hack.h" + +#ifdef TTY_GRAPHICS +# include "tcap.h" +#else +/* To avoid error for tos.c; will be removed later */ +static char *nh_HE="\033q"; +#endif + +#ifdef TOS + +# include +# ifndef WORD +# define WORD short /* 16 bits -- redefine if necessary */ +# endif + +#include + +static char NDECL(DOSgetch); +static char NDECL(BIOSgetch); +static void NDECL(init_aline); +char *_a_line; /* for Line A variables */ +# ifdef TEXTCOLOR +boolean colors_changed = FALSE; +# endif + +int +tgetch() +{ + char ch; + + /* BIOSgetch can use the numeric key pad on IBM compatibles. */ + if (iflags.BIOS) + ch = BIOSgetch(); + else + ch = DOSgetch(); + return ((ch == '\r') ? '\n' : ch); +} + +/* + * Keyboard translation tables. + */ +#define KEYPADLO 0x61 +#define KEYPADHI 0x71 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * When iflags.BIOS is active, shifted keypad keys are translated to the + * shift values below. + */ +static const struct pad { + char normal, shift, cntrl; +} keypad[PADKEYS] = { + {C('['), 'Q', C('[')}, /* UNDO */ + {'?', '/', '?'}, /* HELP */ + {'(', 'a', '('}, /* ( */ + {')', 'w', ')'}, /* ) */ + {'/', '/', '/'}, /* / */ + {C('p'), '$', C('p')}, /* * */ + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'h', 'H', C('h')}, /* 4 */ + {'.', '.', '.'}, + {'l', 'L', C('l')}, /* 6 */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {C('['), 'Q', C('[')} , /* UNDO */ + {'?', '/', '?'}, /* HELP */ + {'(', 'a', '('}, /* ( */ + {')', 'w', ')'}, /* ) */ + {'/', '/', '/'}, /* / */ + {C('p'), '$', C('p')}, /* * */ + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'4', M('4'), '4'}, /* 4 */ + {'.', '.', '.'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +/* + * Unlike Ctrl-letter, the Alt-letter keystrokes have no specific ASCII + * meaning unless assigned one by a keyboard conversion table, so the + * keyboard BIOS normally does not return a character code when Alt-letter + * is pressed. So, to interpret unassigned Alt-letters, we must use a + * scan code table to translate the scan code into a letter, then set the + * "meta" bit for it. -3. + */ +#define SCANLO 0x10 + +static const char scanmap[] = { /* ... */ + 'q','w','e','r','t','y','u','i','o','p','[',']', '\n', + 0, 'a','s','d','f','g','h','j','k','l',';','\'', '`', + 0, '\\', 'z','x','c','v','b','N','m',',','.','?' /* ... */ +}; + +#define inmap(x) (SCANLO <= (x) && (x) < SCANLO + SIZE(scanmap)) + +/* + * BIOSgetch gets keys directly with a BIOS call. + */ +#define SHIFT (0x1 | 0x2) +#define CTRL 0x4 +#define ALT 0x8 + +static char +BIOSgetch() +{ + unsigned char scan, shift, ch; + const struct pad *kpad; + + long x; + + /* Get scan code. + */ + x = Crawcin(); + ch = x & 0x0ff; + scan = (x & 0x00ff0000L) >> 16; + /* Get shift status. + */ + shift = Kbshift(-1); + + /* Translate keypad keys */ + if (iskeypad(scan)) { + kpad = iflags.num_pad ? numpad : keypad; + if (shift & SHIFT) + ch = kpad[scan - KEYPADLO].shift; + else if (shift & CTRL) + ch = kpad[scan - KEYPADLO].cntrl; + else + ch = kpad[scan - KEYPADLO].normal; + } + /* Translate unassigned Alt-letters */ + if ((shift & ALT) && !ch) { + if (inmap(scan)) + ch = scanmap[scan - SCANLO]; + return (isprint(ch) ? M(ch) : ch); + } + return ch; +} + +static char +DOSgetch() +{ + return (Crawcin() & 0x007f); +} + + +long +freediskspace(path) +char *path; +{ + int drive = 0; + struct { + long freal; /*free allocation units*/ + long total; /*total number of allocation units*/ + long bps; /*bytes per sector*/ + long pspal; /*physical sectors per allocation unit*/ + } freespace; + if (path[0] && path[1] == ':') + drive = (toupper(path[0]) - 'A') + 1; + if (Dfree(&freespace,drive)<0) return -1; + return freespace.freal*freespace.bps*freespace.pspal; +} + +/* + * Functions to get filenames using wildcards + */ +int +findfirst(path) +char *path; +{ + return (Fsfirst(path, 0) == 0); +} + +int +findnext() +{ + return (Fsnext() == 0); +} + +char * +foundfile_buffer() +{ + return (char *)Fgetdta() + 30; +} + +long +filesize(file) +char *file; +{ + if (findfirst(file)) + return (* (long *) ((char *)Fgetdta() + 26)); + else + return -1L; +} + +/* + * Chdrive() changes the default drive. + */ +void +chdrive(str) +char *str; +{ + char *ptr; + char drive; + + if ((ptr = index(str, ':')) != (char *)0) { + drive = toupper(*(ptr - 1)); + (void)Dsetdrv(drive - 'A'); + } + return; +} + + +void +get_scr_size() +{ +# ifdef MINT +# include + struct winsize win; + char *tmp; + + if((tmp=nh_getenv("LINES"))) + LI = atoi(tmp); + else if((tmp=nh_getenv("ROWS"))) + LI = atoi(tmp); + if(tmp && (tmp=nh_getenv("COLUMNS"))) + CO = atoi(tmp); + else { + ioctl(0,TIOCGWINSZ, &win); + LI = win.ws_row; + CO = win.ws_col; + } +# else + init_aline(); + LI = (*((WORD *)(_a_line + -42L))) + 1; + CO = (*((WORD *)(_a_line + -44L))) + 1; +# endif +} + +# define BIGBUF 8192 + +int +_copyfile(from, to) +char *from, *to; +{ + int fromfd, tofd, r; + char *buf; + + if ((fromfd = open(from, O_RDONLY|O_BINARY, 0)) < 0) + return -1; + if ((tofd = open(to, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, FCMASK)) < 0) + return -1; + buf = (char *)alloc((size_t)BIGBUF); + while ( (r = read(fromfd, buf, BIGBUF)) > 0) + write(tofd, buf, r); + close(fromfd); + close(tofd); + free(buf); + return 0; /* successful */ +} + +int kbhit() +{ + return Cconis(); +} + +static void +init_aline() +{ +# ifdef __GNUC__ +/* line A calls nuke registers d0-d2,a0-a2; not all compilers regard these + as scratch registers, though, so we save them + */ + asm(" moveml d0-d2/a0-a2, sp@-"); + asm(" .word 0xa000; movel d0, __a_line"); + asm(" moveml sp@+, d0-d2/a0-a2"); +# else + asm(" movem.l d0-d2/a0-a2, -(sp)"); + asm(" .dc.w 0xa000"); /* tweak as necessary for your compiler */ + asm(" move.l d0, __a_line"); + asm(" movem.l (sp)+, d0-d2/a0-a2"); +# endif +} + +# ifdef TEXTCOLOR +/* used in termcap.c to decide how to set up the hilites */ +unsigned long tos_numcolors = 2; + +void +set_colors() +{ + static char colorHE[] = "\033q\033b0"; + + if (!iflags.BIOS) + return; + init_aline(); + tos_numcolors = 1 << (((unsigned char *) _a_line)[1]); + if (tos_numcolors <= 2) { /* mono */ + iflags.use_color = FALSE; + return; + } else { + colors_changed = TRUE; + nh_HE = colorHE; + } +} + +void +restore_colors() +{ + static char plainHE[] = "\033q"; + + if (colors_changed) + nh_HE = plainHE; + colors_changed = FALSE; +} +# endif /* TEXTCOLOR */ + +# ifdef SUSPEND + +#include + +# ifdef MINT +extern int __mint; +# endif + +int +dosuspend() { +# ifdef MINT + extern int kill(); + if (__mint == 0) { +# endif + pline("Sorry, it seems we have no SIGTSTP here. Try ! or S."); +# ifdef MINT + } + else if(signal(SIGTSTP, SIG_IGN) == SIG_DFL) { + suspend_nhwindows((char *)0); + (void) signal(SIGTSTP, SIG_DFL); + (void) kill(0, SIGTSTP); + get_scr_size(); + resume_nhwindows(); + } else { + pline("I don't think your shell has job control."); + } +# endif /* MINT */ + return(0); +} +# endif /* SUSPEND */ + +#endif /* TOS */ diff --git a/sys/atari/unx2atar.sed b/sys/atari/unx2atar.sed new file mode 100644 index 0000000..8d44330 --- /dev/null +++ b/sys/atari/unx2atar.sed @@ -0,0 +1,23 @@ +: loop +/\\$/N +/\\$/b loop +# for each line, append any continuation lines before trying to classify it +/^ / { +# if it starts with a tab, it's meant for the shell, and we should think +# about reversing the slashes +s;cd ../util;cd ..\\util; +s;cd ../src;cd ..\\src; +/librarian/ s;dat/options;dat\\options; +/$(MAKE)/b +/$(CC)/b +s;/;\\;g +} +# unfortunately, we do not want to reverse *all* the slashes, as even the +# Atari make and gcc like forward ones, and it's messy to avoid the ones in +# sed addresses +# so, flip the first one in e.g. +# @( cd ../util ; $(MAKE) ../include/onames.h ) +# flip the librarian-related ones in dat/options +# ignore other lines related to make and gcc +# and flip any slashes left over, which include a number of UNIX-only things +# that we didn't need to do but don't hurt diff --git a/sys/be/README b/sys/be/README new file mode 100644 index 0000000..22e97db --- /dev/null +++ b/sys/be/README @@ -0,0 +1,63 @@ +This file is sys/be/README. It is for those intending to compile +NetHack 3.3 on a BeOS 4.5 system. + +BeOS NetHack currently only supports the TTY windowing system. In +order to compile it, it would benefit you greatly to think of your Be +system as a UNIX variant. It is possible to compile using BeIDE. +However, there are four executables and several steps involved in making +NetHack. Unless you are extremely familiar with the build sequence and +are willing to modify the code somewhat, I suggest you avoid it for now. +Let the UNIX Makefiles take care of all that for you. + + +Known problems: ++ No special characters for walls. None of the fonts available for use + in a tty window has the graphics characters needed to improve the look. + If such a font existed, then all you need to do is set the dungeon, + object, and/or monter mappings in your defaults file. ++ The arrow keys don't work. + + + +Build instructions. From a freshly unpacked source tree: + +1. Copy the Makfiles in sys/unix to their proper spots. You may + use setup.sh or copy them by hand. Using setup.sh to create + links instead of copying the Makefiles will work, but BeOS will + not let you edit a link. It may be helpful to read + sys/unix/Install.unx. + +2. Edit src/Makefile: + o Change System to SysBe. + o Comment out the UNIX versions of SYSSRC and SYSOBJ variables. + o Uncomment the BeOS versions of SYSRC and SYSOBJ. + o Uncomment the BeOS versions of CC, CFLAGS, LD, and LFLAGS. The + flags are different for Intel vs BeBox/Mac. + o Uncomment one of the Intel or BeBox/Mac BeOS versions of CC, CFLAGS, + LD, and LFLAGS. + o Comment out the default CFLAGS and LFLAGS. + o Change WINTTYLIB to be -ltermcap. + +3. Edit util/Makefile: + o If on a BeBox/Mac: + - Uncomment the BeOS versions of CC and CFLAGS + - Comment out the default CFLAGS and LFLAGS. + o If on Intel: + - the default values of CFLAGS and LFLAGS work fine + o Change YACC and LEX to be bison -y and flex respectively. + +4. Edit include/config.h to change HACKDIR to be the location of your + install directory. + +5. Edit top level Makefile and change GAMEDIR to match HACKDIR in + include/config.h. Make sure the path to GAMEDIR exists. Change + SHELLDIR to a "throw away" directory, like /tmp. We don't use the + shell. Change CHOWN and CHGRP commands to "true", there really + aren't groups on the BeOS. + +6. Type "make install" at the top level. + + + +It is possible that some necessary steps needed to make the game have been +omitted. Feel free to ad-lib as necessary. diff --git a/sys/be/bemain.c b/sys/be/bemain.c new file mode 100644 index 0000000..acebb15 --- /dev/null +++ b/sys/be/bemain.c @@ -0,0 +1,268 @@ +/* SCCS Id: @(#)bemain.c 3.4 1998/07/15 */ +/* Copyright (c) Dean Luick, 1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "dlb.h" +#include + +static void whoami(void); +static void process_options(int argc, char **argv); +static void chdirx(const char *dir); +static void getlock(void); + +#ifdef __begui__ + #define MAIN nhmain + int nhmain(int argc, char **argv); +#else + #define MAIN main +#endif + + +int MAIN(int argc, char **argv) +{ + int fd; + char *dir; + + dir = nh_getenv("NETHACKDIR"); + if (!dir) dir = nh_getenv("HACKDIR"); + + choose_windows(DEFAULT_WINDOW_SYS); + chdirx(dir); + initoptions(); + + init_nhwindows(&argc, argv); + whoami(); + + /* + * It seems you really want to play. + */ + u.uhp = 1; /* prevent RIP on early quits */ + process_options(argc, argv); /* command line options */ + + +#ifdef WIZARD + if (wizard) + Strcpy(plname, "wizard"); + else +#endif + if(!*plname || !strncmp(plname, "player", 4) + || !strncmp(plname, "games", 4)) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ + + Sprintf(lock,"%d%s", getuid(), plname); + getlock(); + + + dlb_init(); /* must be before newgame() */ + + /* + * Initialization of the boundaries of the mazes + * Both boundaries have to be even. + */ + x_maze_max = COLNO-1; + if (x_maze_max % 2) + x_maze_max--; + y_maze_max = ROWNO-1; + if (y_maze_max % 2) + y_maze_max--; + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + display_gamewindows(); + + if ((fd = restore_saved_game()) >= 0) { +#ifdef WIZARD + /* Since wizard is actually flags.debug, restoring might + * overwrite it. + */ + boolean remember_wiz_mode = wizard; +#endif +#ifdef NEWS + if(iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; /* in case dorecover() fails */ + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + if(!dorecover(fd)) + goto not_recovered; +#ifdef WIZARD + if(!wizard && remember_wiz_mode) wizard = TRUE; +#endif + check_special_room(FALSE); + if (discover) + You("are in non-scoring discovery mode."); + + if (discover || wizard) { + if(yn("Do you want to keep the save file?") == 'n') + (void) delete_savefile(); + else { + compress(fqname(SAVEF, SAVEPREFIX, 0)); + } + } + + flags.move = 0; + } else { +not_recovered: + player_selection(); + newgame(); + if (discover) + You("are in non-scoring discovery mode."); + + flags.move = 0; + set_wear(); + (void) pickup(1); + } + + moveloop(); + return 0; +} + +static void whoami(void) +{ + /* + * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS + * 2. Use $USER or $LOGNAME (if 1. fails) + * The resulting name is overridden by command line options. + * If everything fails, or if the resulting name is some generic + * account like "games", "play", "player", "hack" then eventually + * we'll ask him. + */ + char *s; + + if (*plname) return; + if (s = nh_getenv("USER")) { + (void) strncpy(plname, s, sizeof(plname)-1); + return; + } + if (s = nh_getenv("LOGNAME")) { + (void) strncpy(plname, s, sizeof(plname)-1); + return; + } +} + +/* normalize file name - we don't like .'s, /'s, spaces */ +void regularize(char *s) +{ + register char *lp; + + while((lp=strchr(s, '.')) || (lp=strchr(s, '/')) || (lp=strchr(s,' '))) + *lp = '_'; +} + +static void process_options(int argc, char **argv) +{ + int i; + + while (argc > 1 && argv[1][0] == '-') { + argv++; + argc--; + switch (argv[0][1]) { + case 'D': +#ifdef WIZARD + wizard = TRUE; + break; +#endif + /* otherwise fall thru to discover */ + case 'X': + discover = TRUE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if(argv[0][2]) + (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); + else if (argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname)-1); + } else + raw_print("Player name expected after -u"); + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; + case '@': + flags.randomall = 1; + break; + default: + raw_printf("Unknown option: %s", *argv); + break; + } + } +} + +static void chdirx(const char *dir) +{ + if (!dir) dir = HACKDIR; + + if (chdir(dir) < 0) + error("Cannot chdir to %s.", dir); + + /* Warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + check_recordfile(dir); +} + +void getlock(void) +{ + int fd; + + regularize(lock); + set_levelfile_name(lock, 0); + fd = creat(lock, FCMASK); + if(fd == -1) { + error("cannot creat lock file."); + } else { + if(write(fd, (genericptr_t) &hackpid, sizeof(hackpid)) + != sizeof(hackpid)){ + error("cannot write lock"); + } + if(close(fd) == -1) { + error("cannot close lock"); + } + } +} + +#ifndef __begui__ +/* + * If we are not using the Be GUI, then just exit -- we don't need to + * do anything extra. + */ +void nethack_exit(int status); +void nethack_exit(int status) +{ + exit(status); +} +#endif /* !__begui__ */ diff --git a/sys/mac/Files.r b/sys/mac/Files.r new file mode 100644 index 0000000..7842e92 --- /dev/null +++ b/sys/mac/Files.r @@ -0,0 +1,148 @@ +#include +#include "date.h" +#include "patchlevel.h" + +resource 'plst' (0, purgeable) { +}; + +resource 'vers' (1, purgeable) { + VERSION_MAJOR, (VERSION_MINOR<<4) | PATCHLEVEL, final, EDITLEVEL, verUS, + VERSION_STRING, + VERSION_STRING +}; + +resource 'vers' (2, purgeable) { + VERSION_MAJOR, (VERSION_MINOR<<4) | PATCHLEVEL, final, EDITLEVEL, verUS, + VERSION_STRING, + "devteam@nethack.org" +}; + +read 'File' (1000,"cmdhelp") ":dat:cmdhelp"; +read 'File' (1001,"help") ":dat:help"; +read 'File' (1002,"hh") ":dat:hh"; +read 'File' (1003,"history") ":dat:history"; +read 'File' (1004,"license") ":dat:license"; +read 'File' (1005,"MacHelp") ":sys:mac:MacHelp"; +read 'File' (1006,"News") ":sys:mac:News"; +read 'File' (1007,"opthelp") ":dat:opthelp"; +read 'File' (1008,"wizhelp") ":dat:wizhelp"; +read 'File' (1009,"air.lev") ":lib:air.lev"; +read 'File' (1010,"asmodeus.lev") ":lib:asmodeus.lev"; +read 'File' (1011,"astral.lev") ":lib:astral.lev"; +read 'File' (1012,"baalz.lev") ":lib:baalz.lev"; +read 'File' (1013,"bigrm-1.lev") ":lib:bigrm-1.lev"; +read 'File' (1014,"bigrm-2.lev") ":lib:bigrm-2.lev"; +read 'File' (1015,"bigrm-3.lev") ":lib:bigrm-3.lev"; +read 'File' (1016,"bigrm-4.lev") ":lib:bigrm-4.lev"; +read 'File' (1017,"bigrm-5.lev") ":lib:bigrm-5.lev"; +read 'File' (1018,"castle.lev") ":lib:castle.lev"; +read 'File' (1019,"data") ":lib:data"; +read 'File' (1020,"dungeon") ":lib:dungeon"; +read 'File' (1021,"earth.lev") ":lib:earth.lev"; +read 'File' (1022,"fakewiz1.lev") ":lib:fakewiz1.lev"; +read 'File' (1023,"fakewiz2.lev") ":lib:fakewiz2.lev"; +read 'File' (1024,"fire.lev") ":lib:fire.lev"; +read 'File' (1025,"juiblex.lev") ":lib:juiblex.lev"; +read 'File' (1026,"knox.lev") ":lib:knox.lev"; +read 'File' (1027,"medusa-1.lev") ":lib:medusa-1.lev"; +read 'File' (1028,"medusa-2.lev") ":lib:medusa-2.lev"; +read 'File' (1029,"minefill.lev") ":lib:minefill.lev"; +read 'File' (1030,"minend-1.lev") ":lib:minend-1.lev"; +read 'File' (1031,"minend-2.lev") ":lib:minend-2.lev"; +read 'File' (1032,"minend-3.lev") ":lib:minend-3.lev"; +read 'File' (1033,"minend-4.lev") ":lib:minend-4.lev"; +read 'File' (1034,"minetn-1.lev") ":lib:minetn-1.lev"; +read 'File' (1035,"minetn-2.lev") ":lib:minetn-2.lev"; +read 'File' (1036,"minetn-3.lev") ":lib:minetn-3.lev"; +read 'File' (1037,"minetn-4.lev") ":lib:minetn-4.lev"; +read 'File' (1038,"minetn-5.lev") ":lib:minetn-5.lev"; +read 'File' (1039,"minetn-6.lev") ":lib:minetn-6.lev"; +read 'File' (1040,"minetn-7.lev") ":lib:minetn-7.lev"; +read 'File' (1041,"options") ":lib:options"; +read 'File' (1042,"oracle.lev") ":lib:oracle.lev"; +read 'File' (1043,"oracles") ":lib:oracles"; +read 'File' (1044,"orcus.lev") ":lib:orcus.lev"; +read 'File' (1045,"quest.dat") ":lib:quest.dat"; +read 'File' (1046,"rumors") ":lib:rumors"; +read 'File' (1047,"sanctum.lev") ":lib:sanctum.lev"; +read 'File' (1048,"soko1-1.lev") ":lib:soko1-1.lev"; +read 'File' (1049,"soko1-2.lev") ":lib:soko1-2.lev"; +read 'File' (1050,"soko2-1.lev") ":lib:soko2-1.lev"; +read 'File' (1051,"soko2-2.lev") ":lib:soko2-2.lev"; +read 'File' (1052,"soko3-1.lev") ":lib:soko3-1.lev"; +read 'File' (1053,"soko3-2.lev") ":lib:soko3-2.lev"; +read 'File' (1054,"soko4-1.lev") ":lib:soko4-1.lev"; +read 'File' (1055,"soko4-2.lev") ":lib:soko4-2.lev"; +read 'File' (1056,"tower1.lev") ":lib:tower1.lev"; +read 'File' (1057,"tower2.lev") ":lib:tower2.lev"; +read 'File' (1058,"tower3.lev") ":lib:tower3.lev"; +read 'File' (1059,"valley.lev") ":lib:valley.lev"; +read 'File' (1060,"water.lev") ":lib:water.lev"; +read 'File' (1061,"wizard1.lev") ":lib:wizard1.lev"; +read 'File' (1062,"wizard2.lev") ":lib:wizard2.lev"; +read 'File' (1063,"wizard3.lev") ":lib:wizard3.lev"; +read 'File' (1100,"Arc-fila.lev") ":lib:Arc-fila.lev"; +read 'File' (1101,"Arc-filb.lev") ":lib:Arc-filb.lev"; +read 'File' (1102,"Arc-goal.lev") ":lib:Arc-goal.lev"; +read 'File' (1103,"Arc-loca.lev") ":lib:Arc-loca.lev"; +read 'File' (1104,"Arc-strt.lev") ":lib:Arc-strt.lev"; +read 'File' (1105,"Bar-fila.lev") ":lib:Bar-fila.lev"; +read 'File' (1106,"Bar-filb.lev") ":lib:Bar-filb.lev"; +read 'File' (1107,"Bar-goal.lev") ":lib:Bar-goal.lev"; +read 'File' (1108,"Bar-loca.lev") ":lib:Bar-loca.lev"; +read 'File' (1109,"Bar-strt.lev") ":lib:Bar-strt.lev"; +read 'File' (1110,"Cav-fila.lev") ":lib:Cav-fila.lev"; +read 'File' (1111,"Cav-filb.lev") ":lib:Cav-filb.lev"; +read 'File' (1112,"Cav-goal.lev") ":lib:Cav-goal.lev"; +read 'File' (1113,"Cav-loca.lev") ":lib:Cav-loca.lev"; +read 'File' (1114,"Cav-strt.lev") ":lib:Cav-strt.lev"; +read 'File' (1115,"Hea-fila.lev") ":lib:Hea-fila.lev"; +read 'File' (1116,"Hea-filb.lev") ":lib:Hea-filb.lev"; +read 'File' (1117,"Hea-goal.lev") ":lib:Hea-goal.lev"; +read 'File' (1118,"Hea-loca.lev") ":lib:Hea-loca.lev"; +read 'File' (1119,"Hea-strt.lev") ":lib:Hea-strt.lev"; +read 'File' (1120,"Kni-fila.lev") ":lib:Kni-fila.lev"; +read 'File' (1121,"Kni-filb.lev") ":lib:Kni-filb.lev"; +read 'File' (1122,"Kni-goal.lev") ":lib:Kni-goal.lev"; +read 'File' (1123,"Kni-loca.lev") ":lib:Kni-loca.lev"; +read 'File' (1124,"Kni-strt.lev") ":lib:Kni-strt.lev"; +read 'File' (1125,"Mon-fila.lev") ":lib:Mon-fila.lev"; +read 'File' (1126,"Mon-filb.lev") ":lib:Mon-filb.lev"; +read 'File' (1127,"Mon-goal.lev") ":lib:Mon-goal.lev"; +read 'File' (1128,"Mon-loca.lev") ":lib:Mon-loca.lev"; +read 'File' (1129,"Mon-strt.lev") ":lib:Mon-strt.lev"; +read 'File' (1130,"Pri-fila.lev") ":lib:Pri-fila.lev"; +read 'File' (1131,"Pri-filb.lev") ":lib:Pri-filb.lev"; +read 'File' (1132,"Pri-goal.lev") ":lib:Pri-goal.lev"; +read 'File' (1133,"Pri-loca.lev") ":lib:Pri-loca.lev"; +read 'File' (1134,"Pri-strt.lev") ":lib:Pri-strt.lev"; +read 'File' (1135,"Ran-fila.lev") ":lib:Ran-fila.lev"; +read 'File' (1136,"Ran-filb.lev") ":lib:Ran-filb.lev"; +read 'File' (1137,"Ran-goal.lev") ":lib:Ran-goal.lev"; +read 'File' (1138,"Ran-loca.lev") ":lib:Ran-loca.lev"; +read 'File' (1139,"Ran-strt.lev") ":lib:Ran-strt.lev"; +read 'File' (1140,"Rog-fila.lev") ":lib:Rog-fila.lev"; +read 'File' (1141,"Rog-filb.lev") ":lib:Rog-filb.lev"; +read 'File' (1142,"Rog-goal.lev") ":lib:Rog-goal.lev"; +read 'File' (1143,"Rog-loca.lev") ":lib:Rog-loca.lev"; +read 'File' (1144,"Rog-strt.lev") ":lib:Rog-strt.lev"; +read 'File' (1145,"Sam-fila.lev") ":lib:Sam-fila.lev"; +read 'File' (1146,"Sam-filb.lev") ":lib:Sam-filb.lev"; +read 'File' (1147,"Sam-goal.lev") ":lib:Sam-goal.lev"; +read 'File' (1148,"Sam-loca.lev") ":lib:Sam-loca.lev"; +read 'File' (1149,"Sam-strt.lev") ":lib:Sam-strt.lev"; +read 'File' (1150,"Tou-fila.lev") ":lib:Tou-fila.lev"; +read 'File' (1151,"Tou-filb.lev") ":lib:Tou-filb.lev"; +read 'File' (1152,"Tou-goal.lev") ":lib:Tou-goal.lev"; +read 'File' (1153,"Tou-loca.lev") ":lib:Tou-loca.lev"; +read 'File' (1154,"Tou-strt.lev") ":lib:Tou-strt.lev"; +read 'File' (1155,"Val-fila.lev") ":lib:Val-fila.lev"; +read 'File' (1156,"Val-filb.lev") ":lib:Val-filb.lev"; +read 'File' (1157,"Val-goal.lev") ":lib:Val-goal.lev"; +read 'File' (1158,"Val-loca.lev") ":lib:Val-loca.lev"; +read 'File' (1159,"Val-strt.lev") ":lib:Val-strt.lev"; +read 'File' (1160,"Wiz-fila.lev") ":lib:Wiz-fila.lev"; +read 'File' (1161,"Wiz-filb.lev") ":lib:Wiz-filb.lev"; +read 'File' (1162,"Wiz-goal.lev") ":lib:Wiz-goal.lev"; +read 'File' (1163,"Wiz-loca.lev") ":lib:Wiz-loca.lev"; +read 'File' (1164,"Wiz-strt.lev") ":lib:Wiz-strt.lev"; diff --git a/sys/mac/Install.mw b/sys/mac/Install.mw new file mode 100644 index 0000000..51580cf --- /dev/null +++ b/sys/mac/Install.mw @@ -0,0 +1,289 @@ +Building a PPC NetHack 3.4 with the Metrowerks compilers + + +You must be familiar with the Metrowerks compiler and know how to construct +projects. The NetHack source may come with the four pre-made projects that +are needed to build NetHack and the files it needs. These four projects are +in :sys:mac and are MakeDefs.u, DgnComp.u, LevComp.u, and NetHack.u. If you +do not have them, or wish to construct them yourself, see the section "Project +Contents" below. + + +1. Move the projects MakeDefs.u, DgnComp.u, LevComp.u, and NetHack.u to the +top level of the NetHack tree. If you are building your own, create each +project as needed, in the order given below. + +2. Create a folder "lib" in the top level. This is where the files used by +NetHack will be deposited by MakeDefs, DgnComp, and LevComp. + +3. Build and run MakeDefs. + +You will be presented with a list of options. Initially choose them all (the +default). Later you may wish to only run a few of them. The options are +"odemvpqrhz", each of which makes a file: + + -o creates :include:onames.h + -p creates :include:pm.h + -z creates :src:vis_tab.c + -m creates :src:monstr.c + -e creates :dat:dundeon.pdf + -v creates :lib:options + -d creates :lib:data + -r creates :lib:rumors + -h creates :lib:oracles + -q creates :lib:quest.dat + + +4. If you are _not_ using DLB, follow these directions. As of v3.3, DLB is ON +for the Mac. Copy the following files. You may want to change News or NHDeflts. + + a. copy ':sys:mac:MacHelp' to ':lib:MacHelp' + b. copy ':sys:mac:News' to ':lib:News' + c. copy ':sys:mac:NHDeflts' to ':lib:NetHack Defaults' + + d. copy ':dat:cmdhelp' to ':lib:cmdhelp' + e. copy ':dat:help' to ':lib:help' + f. copy ':dat:hh' to 'lib:hh' + g. copy ':dat:history' to ':lib:history' + h. copy ':dat:license' to ':lib:license' + i. copy ':dat:opthelp' to ':lib:opthelp' + j. copy ':dat:wizhelp' to ':lib:wizhelp' + +5. Create an empty file, ':lib:record' + +6. Build and run DgnComp. + This will create a file "dungeon" in the lib directory. + +7. Build and run LevComp. + This will build the level files (*.lev) in the lib directory. + +8. Build NetHack. + Move NetHack in the lib directory. + + +------------------------ + + +Building NetHack with MetroWerks IDE 1.x (DR7-DR10, DR11 was never used) + + +To build NetHack, you will need to create four projects at the top level of +the NetHack directory tree. These four projects are MakeDefs.u, DgnComp.u, +LevComp.u, and NetHack.u. The projects don't have to end in ".u", but you +should append some form of ".XXX" to the end of the project's name to +distinguish the project from the executable that it produces. The files +and libraries to include in these projects are listed in the "Project +Contents" section below. You must create and run Makedefs before creating +NetHack because MakeDefs will create files used by NetHack. + +Use the MacOS C/C++ template for each of the projects. The libraries included +will be overkill for all the projects (e.g. the C++ libraries are not needed). +Add the .c and resource files as indicated below. Unless otherwise noted, the +projects can use the default preferences: + + Font + The tabbing on all non-mac files is 8. All mac files have a + tab of 4. + PPC Processor + All projects must have the same alignment to build a consistent + NetHack. To share save files with 68K, the alignments + must match for their projects, as well. + Turn on Global Optimization (official version is compiled + with level 1). If you don't turn it on, some files may + not compile because of register overflow. [NetHack + only] + PPC Project + Set name to + Other settings [NetHack only] + creator: nh31 + preferred heap size:2500 + minimum heap size: 2000 + stack size: 128 [PPC only] + + +The SIOUX library may be replaced with console.stubs.c for the NetHack +project. + +NOTE: As NetHack 3.3, you must turn on OLDROUTINENAMES -- so you can't use +the default pre-compiled header. You should either remove it from the +preferences or insert another precompiled header that has this define off. + + +------------------------ + + +Building NetHack with MetroWerks IDE 2.0. + +This is for building a PowerPC version only. This doesn't take advantage +of the IDE's subprojects. These will be investigated later. + + +MakeDefs.u, DgnComp.u, LevComp.u: Select ANSI C Console PPC. + Settings: + PPC Target + + Change File Name to MakeDefs, DgnComp, or LevComp respectively. + C/C++ Language + + Turn off ANSI strict, ANSI Keywords Only, Expand Trigraphs + PPC Processor + + Turn on global optimization (at least to level 1) + +NetHack.u: Basic ToolBox PPC + PPC Target + + Change File Name to NetHack. Other settings + creator: nh31 + preferred heap size:2500 + minimum heap size: 2000 + stack size: 128 [PPC only] + C/C++ Language + + Options ANSI strict, ANSI Keywords Only, Expand Trigraphs + are already turned off, so you don't have to do anything. + PPC Processor + + Turn on global optimization (at least to level 1) + + +------------------------ +Creating projects for NetHack with MetroWerks IDE 3.3 (Pro 4) + +This is what I changed from the default settings when creating a 68K version. Some of +the settings may not be necessary. For example, NetHack doesn't use floating point, +so I didn't have to check 8 byte doubles. Some are interrelated. For example, the +codegen and the Math and MSL libraries used. + +For MakeDefs.u, DgnComp.u, LevComp.u: +1. Select File>>New Project...>>MacOS>>C_C++>>Standard Console>>Std C Console 68K +2. 68K Settings: + Target Settings: + + Set "Target Name" to {MakeDefs,DgnComp,LevComp}. + 68K Target: + + Set "File Name" to {MakeDefs,DgnComp,LevComp}. + C/C++ Language: + + Check Require Function Prototypes, uncheck everything else. + + Clear "Prefix File". + 68K Processor: + + Set "Code Model" to Large. + + Check 68020 Codegen, 4-Byte Ints, 8-Byte Doubles, Far Data, Far Method Tables, + Far String Constants. Uncheck everything else. +3. Libraries 68K + + Remove the C++ Library (it is not needed). + + Change math library to MathLib68K Fa(4i_8d).Lib. + + Change MSL C library to MSL C.68K Fa(4i_8d).Lib. + Note: The actual libraries used must match the CodeGen options in 68K Processor. + + +For NetHack.u: +1. Select File>>New Project...>>MacOS>>C_C++>>MacOS ToolBox>>MacOS ToolBox 68K +2. 68K Settings + Target Settings: + + Set "Target Name" to NetHack Debug and NetHack Final. + 68K Target: + + Set "File Name" to NetHack Debug and NetHack Final. + + Set "Creator" to 'nh31'. + + Set "Preferred Heap Size (k)" to 2500. + + Set "Minimum Heap Size (k)" to 1500. + C/C++ Language: + + Check Require Function Prototypes, uncheck everything else. + + Set "Prefix File" to LocalDefines.h. I use this header to define + OLDROUTINENAMES because the pre-compiled header doesn't have it set + any more. One of these days we'll fix up the code... + 68K Processor: + + Set "Code Model" to Large. + + Check 68020 Codegen, 4-Byte Ints, 8-Byte Doubles, Far Data, Far Method Tables, + Far String Constants. Uncheck everything else. +3. Libraries 68K + + Remove the C++ Library (it is not needed). + + Change math library to MathLib68K Fa(4i_8d).Lib. + + Change MSL C library to MSL C.68K Fa(4i_8d).Lib. + Note: The actual libraries used must match the CodeGen options in 68K Processor. + + +For Recover.u: +1. Select File>>New Project...>>MacOS>>C_C++>>MacOS ToolBox>>MacOS ToolBox 68K +2. 68K Settings + Target Settings: + + Set "Target Name" to Recover Debug and Recover Final. + 68K Target: + + Set "File Name" to Recover Debug and Recover Final. + + Set "Creator" to 'nhRc'. + C/C++ Language: + + Check Require Function Prototypes, uncheck everything else. + + Set "Prefix File" to LocalDefines.h. I use this header to define + OLDROUTINENAMES because the pre-compiled header doesn't have it set + any more. One of these days we'll fix up the code... + 68K Processor: + + Set "Code Model" to Large. + + Check 68020 Codegen, 4-Byte Ints, 8-Byte Doubles, Far Data, Far Method Tables, + Far String Constants. Uncheck everything else. +3. Libraries 68K + + Remove the C++ Library (it is not needed). + + Change math library to MathLib68K Fa(4i_8d).Lib. + + Change MSL C library to MSL C.68K Fa(4i_8d).Lib. + Note: The actual libraries used must match the CodeGen options in 68K Processor. + + +------------------------ + +Project Contents: + +MakeDefs.u should contain the following source files: + + src + objects.c + monst.c + util + makedefs.c + + +DgnComp.u should contain the following source files: + + src + alloc.c + sys:share + dgn_lex.c + dgn_yacc.c + util + dgn_main.c + panic.c + + +LevComp.u should contain the following source files: + + src + alloc.c + decl.c + drawing.c + monst.c + objects.c + sys:mac + macerrs.c + macfile.c + sys:share + lev_lex.c + lev_yacc.c + util + lev_main.c + panic.c + + +NetHack.u should contain the following source files: + + src + *.c [can do an add all] + sys:mac + *.c except mrecover.c + NetHack.rsrc + machelp.bh [for baloon help] + Sound.rsrc [if you wish to have a few, crude sounds] + Files.r [if you use DLB (on by default)] + sys:share + random.c + win:tty + *.c [can do an add all - termcap.c is not needed, but will compile to nothing] + + Note: src:monstr.c and src:vis_tab.c are created by MakeDefs -m and -s + respectively. + +Recover.u should contain the following source files: + + sys:mac + mrecover.c + mrecover.rsrc diff --git a/sys/mac/MacHelp b/sys/mac/MacHelp new file mode 100644 index 0000000..3197aed --- /dev/null +++ b/sys/mac/MacHelp @@ -0,0 +1,158 @@ + Macintosh-specific help file for NetHack 3.4 + +The following are options, features, or concerns specific to the +MacOS Classic port of NetHack. Bug reports, suggestions, comments, +and so on, should be addressed to: + + To: nethack-bugs@nethack.org + Subject: Mac NetHack 3.4 + +or you can use our on-line bug reporting form at + + http://www.nethack.org + +Please include your machine-type, system software version and other +relevant information (i.e. system extensions, monitor, accelerators +and so on). + + +=== Configuration of a playground + NetHack is packaged in a Dungeon Folder which includes: + NetHack - the application file itself. + NetHack Defaults - text file for default option settings. + License - licensing terms for nethack. + Guidebook - description of the game in long format. + Recover - the application to restore save files from crashed games. + Previous versions had a large number of data files in the Dungeon + Folder. These are now packaged as resources inside the application + file and will no longer appear in the Dungeon Folder. + + During play another file type appears: + Player level files (labelled "iName.n", i is a constant number, + Name is the player name and n is the dungeon level). + + Two other types of files will appear in the Dungeon Folder + as a result of playing NetHack: + Bones files (of previously deceased players). + Saved games (labelled "save/iName", i is a number, same as above, + and Name is the player name). + + The following files or file types may be thrown away: + logfile - if it becomes too large. A new one will be generated. + Player level files _not_ belonging to a game in progress. + Alternatively, these files may be processed by Recover, + which may be able to restore a save file from the level files. + Old bones files and saved games. + + +=== Resuming a saved game + Double-click (or open) the desired saved game file or open NetHack + and answer the "Who are you?" dialog with the player name of + the saved game in the Dungeon Folder. + + +=== Windows + The Dungeon Map and Message windows are the essential windows used + during window-mode play. During tty-mode play there is only one + window which displays the map, messages, lists and other info. + For window-mode play, lists (e.g. the list of objects that may + be wielded) and special info windows appear as needed. Windows + may be closed in the normal ways (i.e. clicking their close box, + choosing 'Close' from the File menu or typing the command + equivalent for 'Close', cmd-W) and the list windows may also be + dismissed by hitting the space bar (or Return or Enter Keys). + Hitting the ESCape key will dismiss special windows without + scrolling to the end. + + The command "Reposition" on the File menu may be used to restore the + the startup sizes and locations of the various windows. The + window positions are saved in a file labelled "NetHack Windows" + in the appropriate preferences folder. + + +=== Default options + The following options are specific to the Macintosh port: + background: - black or white + MACgraphics - use enhanced dungeon map symbols [TRUE] + page_wait - display --MORE-- after messages [TRUE] + + Default options may be set by editing the NetHack Defaults text + file (using SimpleText or your favorite editor). The following + notation is used: + + OPTIONS=name:Arnold,time,!tombstone + + It should also be mentioned here that there are two graphic + interface modes available: 'mac' and 'tty'. Choosing between + these interfaces is accomplished by the option: + window:mac - the default multi-window Macintosh(tm) interface. + window:tty - traditional Unix(tm)-style TTY window interface. + + See option help (?f or ?g) for more details. + + +=== Movement by mouse + The shape (direction) of the cursor over the Dungeon Map window, + typically, indicates the direction that you desire to move in when + the mouse is clicked. Modifier keys affect mouse-movement in the + same way that they affect keyboard movement. Clicking on yourself + means rest one turn and Shift-clicking on yourself means "open door" + in the subsequently indicated direction. + + +=== Sounds + Real sounds (resources) have been added for various instruments. + The option "silent" [FALSE] controls whether or not a sound will + be heard when an instrument is applied. + + +=== Explore and Debug Modes + As of version 3.1.2, you can enter Explore (aka Discover) mode or + Debug (aka Wizard) mode by choosing the appropriate entries on the + 'Mode' popup-menu section of the "Who are you?" startup dialog. + This same dialog allows you to specify your role, race, gender, + alignment, and name, of course. + + Starting in Explore mode is essentially the same as playing in + Regular mode except that if you are killed then you are given an + opportunity to override your death. Because of this advantage, + your Explore mode scores are not entered on the scoreboard record. + You also get a wand of wishing in your starting inventory and can + see your intrinsic abilities using the command ctl-X (also available + on the 'Explore' submenu on the File menu). + + Starting in Debug mode is only intended for developers and others + interested in characterizing bugs. Using this mode for other + purposes will have confusing results and eliminate your enjoyment + of the game! + + +=== Menus + As of version 3.1.2, the menus have been reworked to provide access + to all the NetHack commands and a special 'Kbd' menu was added to + facilitate play using only the mouse. In some cases, a command may + appear on more than one menu. In general, the commands have been + grouped to appear on an appropriate menu: + File - commands related to windows, start mode and play control. + Help - info commands generally not related to a specific game (i.e. + (key descriptions, version info, internal option editor). + Info - commands that are generally game-specific (i.e. inventory + related, describe features seen on the map or name things). + Equip - commands related to things you might wield or wear. + Act - commands for actions that you might do alone (i.e. wait, + jump) or do with another dungeon denizen (i.e. pay, chat). + Magic - commands for things that you might do with items (drop, + eat, read) or spell-related. + Bits - commands for things you might do to dungeon pieces (i.e. + open door, loot chest, engrave on the floor, climb stairs). + + The key related to a command generally appears to the left of the + menu entry for that command (i.e. w for wield and W for wear). A + leftmost # denotes an extended command (without a related key) and + a left cloverleaf or command symbol denotes a command that requires + either a control or command key modifier (i.e. holding down the + control or command key while hitting the related key). + + +=== +The members of the Macintosh NetHack port team hope you enjoy this game. diff --git a/sys/mac/NHDeflts b/sys/mac/NHDeflts new file mode 100644 index 0000000..12a9c3d --- /dev/null +++ b/sys/mac/NHDeflts @@ -0,0 +1,161 @@ +# SCCS Id: @(#)NetHack Defaults 3.4 2002/03/15 +# Copyright (c) 2002 by Dean Luick, Mark Modrall, and Kevin Hugo +# NetHack may be freely redistributed. See license for details. +# +# Default settings for the Macintosh port of NetHack. +# Lines beginning with a `#' character are "comments" and are +# ignored all the way to the end of the line. Using this +# method, some of the lines below have been disabled so you +# can see an example without having those options actually +# set. Remove the `#' character to "uncomment" the line and +# allow those options to take effect. + + +### Display ### +# Uncomment for the traditional single-window tty interface +#OPTIONS=win:tty + +# Boulder symbol +#OPTIONS=boulder:0 + +# Color +OPTIONS=color + +# Fonts +#OPTIONS=font_map:NewHackFont,font_size_map:9 +#OPTIONS=font_menu:geneva,font_size_menu:9 +#OPTIONS=font_message:PSHackFont,font_size_message:9 +#OPTIONS=font_status:monaco,font_size_status:9 +#OPTIONS=font_text:geneva,font_size_text:9 + +# Don't make dark corridors look like lit corridors +OPTIONS=!lit_corridor + +# Enable sound and beeps +OPTIONS=sound,!silent + + +### Start-up and ending ### +# Don't display the game introduction and new feature list at start +#OPTIONS=!legacy,!news + +# Save game state periodically in case of crashes (recommended) +OPTIONS=checkpoint + +# How to prompt for things after death +#OPTIONS=disclose:+i na -v yg nc + +# Show tombstone and top scores at death +OPTIONS=tombstone,scores:10t/3a/o + +# Show top ten list in its own window +#OPTIONS=toptenwin + + +### User input and feedback ### +# Choose between menus or text prompts +# (traditional, combination, partial, or full) +OPTIONS=menustyle:full + +# Extended (`#') commands by menu +#OPTIONS=extmenu + +# Increase the number of message lines remembered +#OPTIONS=msghistory:60 + +# Enable the number pad keys +OPTIONS=number_pad + +# Pause for --more-- and make it boldface +OPTIONS=page_wait,standout + +# Ask for confirmation with the #pray command +OPTIONS=prayconfirm + +# Allow spacebar as rest command +#OPTIONS=rest_on_space + +# Display experience, score, and time on status line +OPTIONS=showexp,showscore,time + +# Turn off animations +#OPTIONS=!sparkle + +# Display a little more information with some commands +#OPTIONS=suppress_alert:3.3.0 +OPTIONS=verbose + + +### Character ### +# A Valkyrie... +#OPTIONS=name:Brunhilda,role:Val + +# The old way works, too: +#OPTIONS=name:Brunhilda-V + +# How about an Elven Ranger? +#OPTIONS=name:Silwa,role:Ranger,race:Elf,gender:Male + +# Always a human female +#OPTIONS=race:human,female + +# Or leave them commented out and the game will ask you + + +### Inventory ### +# Automatically dig if wielding a pick +#OPTIONS=autodig + +# Disable autopickup (toggle it with the `@' command) +#OPTIONS=!autopickup,pickup_types:$* + +# Automatically fill the quiver +#OPTIONS=autoquiver + +# Don't use fixed inventory letters +OPTIONS=!fixinv,perm_invent,sortpack + +# What you want to call slime molds +#OPTIONS=fruit:grape + +# Desired inventory display order +#OPTIONS=packorder:)[( + +# How much you're willing to carry without confirmation +#OPTIONS=pickup_burden:B + +# Put weapon in secondary slot when wielding another +#OPTIONS=pushweapon + + +### Pets ### +# What to call your starting pet, and its type +#OPTIONS=dogname:Quinn,catname:Vladimir,horsename:Silver,pettype:dog + +# Don't intentionally attack your pets +OPTIONS=confirm,!hilite_pet,safe_pet + + +### Unused options ### + +# Now obsolete +# +# background, large_font, popup_dialog, use_stone + +# Obsolete way to obtain reverse video; use at your own risk +#OPTIONS=palette:000/c22/2c2/ca0/22c/a2a/2aa/ccc/999/f00/0f0/dd0/00f/d0d/0dd/fff/999/444/622/62c/-222 + +# Options used in tty window mode, but not mac window mode +# +# menu_..., msg_window, timed_delay, use_inverse, vary_msgcount + +# Options used by other ports but not Macintosh: +# +# align_message, align_status, ascii_map, BIOS, checkspace, +# decgraphics, eight_bit_tty, ibmgraphics, ignintr, mail, +# map_mode, null, player_selection, preload_tiles, rawio, +# splash_screen, tiled_map, tile_..., videocolors, videoshades, +# windowcolors + + +# End-of-file diff --git a/sys/mac/NHrsrc.hqx b/sys/mac/NHrsrc.hqx new file mode 100644 index 0000000..cc3db83 --- /dev/null +++ b/sys/mac/NHrsrc.hqx @@ -0,0 +1,991 @@ +(This file must be converted with BinHex 4.0) +:$%jPG%KKBfXZFR0bB`"58e*$8P0&4!%!N!I4j"fk!*!%!3#3!mRS!!$)k!!!"r` +!N!30!"!!,3!`S!)!U!#3"4B!1!!R!1')'&0dE`a1CA4)B@0V,R*cFQ0UFfecFQ0 +PER4c+f036(0`F'pM!!"58e*$8P0&4!%!!&-"%!#3%V0,SqB!N!CM'`!!b!%`!!% +!N!J,Z3#3""8!N!AQ!9`!!3#3#!Zi!*!%&3"3!'i!qJ'i!!%!N!J(e!#3""8!8!" +Z!2S"Z!!"!*!)"p-!N!39!&!!EJ$k!EJ!!3#3#!I5!*!%&3"3!'i!qJ'i!*!$(!% +)!!J"5J$`!*!+"dePFh0KCf8i#J#3!a`",J!%!9)"bJ#3#JC6G'&dGA0V1!S!N!- +@!(`!fJ$i!BS!N!3"!*!'66J+!*!$'J#q!!B"8!%b!*!%!3#3"34*EQC[)$J+!*! +$)J!S!#J!M!#L!*!%!3#3"3e1G@aX)>EQ4[Gb!K1!S!N!-m$NjeE@*PFL"[CL" +VCAPc6d019!8UN!9-8e4$"dYPH@0[C'9)3PP8"%0SBA*$5%&5"5U3"8a69%8!N!- +J!#J!+!&+!Hi!N!S,4(9ZCf9[EL"0BA!i#J#3!b!!+!!S!+`"c!#3"!%!N!8,4'P +KCfj[Fh4TBh-i#J#3!aB!!400B@-J6Q9d5'&MDb")C@a`b5mr!*!$J!#3#`rr!*! +%r`$a!I!!N!2lm!m3(`#3!`qr$`%"m!#3!r[r%"$`!!$`$lrrmI!!$`rrqr!2!!! +2%"$r[`#3!`m"!I$lm!#3!r!3m!qr!*!$$`%2!2[`!*!$rr!!$lm!N!Ilm!#3"Jq +r!*!(r`#3!i!!rj!%m!#3!r!!N!2r!*!$m!#3!rc`!!$`!*!$rrm!!2!!N!32!!$ +`$r!!!!m!!2$rr`!!$`!!m2rr$rm2!!$`$r!!!!m!!2!!N!32!!$`$r!!!!m!!2$ +`$`r`$`!!m2!2!!!2!!$`$r!!!!m!!2!!N!32!!$rN!B!N!4!!!!"`$)J14!G#!m +)*qKIN!"$`%,J)R!41!iF!!i!"`!$!!!"`$2J1I!Gq!ri*rKrN!"r`(lJ2R!I1!i +F!!i!"`!$!*!$3$rJ)$!J+#!m)!3Q"#m%,h3Q"#!%*J3TC#N%*J3J"$rm2q!rm$r +i2r`rr$rm2r`rr$rm2r`rr$rm2r`rr$rm2r`!!!%!N!B2J!!!-'!!%#!3!"`3#!! +1%!J!$a!%!!H3!!3!!r!%!!(J"!!!m!3!-2[N!%mq'!"!(J!!3!m!!%!2J!"!%m! +!3"(J!#!3m!!J%(J!%!Jm!!`B(J!$i!m!N!-(J!!!!m!!!!(J!*!$m!#3!hJ!N!- +m!*!$(J#3!`i!N!-%!*!&$i!!!$rJ!"!rm!!F(rJ!$Kri!!mIr!!(Rr`!!rrm!!( +rr!!!rr`!-2rm!(rq'!"rrJ!!Irm!!(rrJ!"rmm!!Ir(J!$r`m!!rm(J!(rJm!!r +i(J!$i!m!N!-(J!!!!m!!!!(J!*!$m!#3!hJ!N!-m!*!$(J#3!`i!N!-%!!!"!!r +rr!!)!!B!#!i&!!Q4")!*b)4!#1K%)!Ki4r!*2d!3#[b!%!SH!"!+&`!3#41!%!L +*`"!)F1!3#!"`%!J!1"!)!"J3#'!!%!L3!!!3#@J!%!PTrK!)N!!!%!KJ!"!)!!! +3#'!!%!L3!!!3#3J!%!N*[j!!#*!!!"!)B!!3#!!!%!rrrr!2rr`!$rrq!!rrr`! +2rrq!$rrr`!rrrq!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr! +2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr! +2rrr`$rrrm!rrrr!2rrr`$rrrm!!!!J#3&rrrm!#3$2m3%"r`!*!($`#3!r%"N!- +2!*!($rm!!!m3N!6`!*!(qr!!$`'3"2!!N!Il[`!2%*!%(`#3"`qlm!m"N!32!*! +)qlrr%*!%(`#3#!qlm3'3"!m!N!Rl[a#3""m!N!Er!!$rZr(rrr%2!*!&$a$rra$ +l[r!!$r!!N!82!C!%$l[`!*!)$a#3"IZr!*!)$`'3"Iqlm!#3"`m3N!3I!2Zr!*! +($`'3"!m!$l[`!*!(m"#3!am!!2Zr!*!(m3'3!`m!!!qlm!#3"Jm3N!6`!!$l[`# +3"rm"!3r`!!!2Zr!!N!Irrr!!N!6l[`#3$Jqlm!#3$[Zr!*!1$l[`!*!1qlm!N!i +2Zr!!N!ll[`#3$Jqlm!#3$[[`!*!1$`#3!`)!N!2rN!N!N!I`!*!($r!!N!E`!*! +$rr!!!!r2!*!'m!r`$a!I!!!2c2!!N!A`$lm!m3(`!!r-c`#3"I!!qr$`%"m!$mc +-m!#3"2!!$lra!3m!$rq3!`#3"2!2!2[rram!N!32!*!%m2$rrlm!m!#3"!m!N!6 +`m3%2qr!!N!82!*!%m2!3(`qr!*!&$`#3"2!2!3m!qr!!N!32!*!%m!$`%2!2[`# +3"!m!N!6`!!rr!!$lm!#3!`m!N!6`!*!&$lm!N!-2!*!%m!#3"[[`!!!2!*!%m!# +3"Jr`!!!2!*!%m!!2m!#3"`m!N!6`!2!2!*!($`#3"2!2$r$`!*!'$`#3"2!2$r$ +`$rq3!r!!$`#3"2!!m!m!N!F2!*!%m!!2m!#3"`m!N!6`!*!+$`#3"2!!$r!!N!F +2!*!%m!$`$`#3"`m!N!6`$`!!m!#3"Jm!N!6`$`!!m!r`rj!$m!m!N!6`!2!2!*! +($`#3"2!!$r!!N!F2!*!%m!#3#Jm!N!6rN!`!N!8-!&S!EJ$Z!C)!J84%!*!$6!! +#!*!&H!$'!)S"%!3#6dX!N!8+!%B!F`%3L"a$Eh9XC#"ZEh3JAM)JBQ9MBA9cC5" +H-#iJ)&ia!*!&#J!8!#S!0+!#!*!%!3!2rr`!#!!'!!J1"3!*N35!#FL%3!MS4#! +)H%I`#6p!%!VmJ"!+(J!3#KF!%!N6J"!)LF!3#($J%!J!F"!)!$J3#!!B%!Q8aP! ++95P3#P8T8!T9+9!+95P3#C6'8!J!!"!+C6'3!!U95P!+P8T3#T9+8!U95P!+C6' +3!!J!!"!2rrr`$rrm!!rrrJ!2rrm!$rrrJ!rrrm!2rrrJ$rrrm!rrrr!2rrr`$rr +rm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rr +rm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!!N!0!2q! +J-#!S)$`J"#5N+U3UT#5N)!3T*#UN+U3T*#!%2r`ri$r`2rJrr$rm2r`rr$rm2r` +rr$rm2r`rr$rm2r`rr!!!!J#3!rq3#3#3"r!!N!F2m!#3"[!!N!2rm!!!$mm!N!E +`$r!2%"m!!!r-m!#3"I!2[`$a!I!!$mc2!*!&m!$lm2!3(`!2c-c`!*!%m!!2[r% +"$`!2rj!$!*!%m!m!qrrr(`#3"!m!N!6`m2rr[`$`!*!%$`#3"2$a!3rlm!#3"3m +!N!6`m"!I$lm!N!82!*!%m!m"$`$lm!#3"!m!N!6`!2!3m!qr!*!%$`#3"2!!$rm +!!2[`!*!$$`#3"2!!N!82[`#3!`m!N!6`!*!'qr!!!!m!N!6`!*!'$r!!!!m!N!6 +`$r!2$`$r!!r`$`m!N!6`m!q3"!$`m!q3!`#3"2$`$j!%!2$`$j!$!*!%m2!2N!3 +!m2!2N!-!N!6`m!q3"!$`m!q3!`#3"2!2m!m2!2m!$r!2$`#3"2!!N!S2!*!%m2! +2m!m2!2m!$r!2!*!%m*!$$j!%!2$`$`m!N!6`N!-2N!3!m2!2$`#3"2#3!`q3"!$ +`m!m2!*!%m*!$$j!%!2$`$`m!N!6`m!r`$`m!r`!2m!m!N!6`!*!+$`#3"2q3$!# +3"5)!!3#3"9!"4!"N!Ai%!Np,!*!&"J!i!%B"I)J#AM!!N!1!!2q3"2!!N!2`!*! +$r`#3!r!!N!2mm!!!m!#3!rrr!!$`!*!%$`!!m!m!m2!2!!$`N!82!!$`N!82!!$ +`$`$`m!m!!2!!N!32!!$`m!m!m!m!!2#3"3m!!2#3"3m!!2$`$`$`$`!!m!#3"!m +!!2q3"J#3!`Q)N!!!N!6q!!B!!2rh!!B!#`1l!!N!!J#3!cm!N$2B!*!'&%%!N!8 +T3!!!-!!!'J!!!KK!"@YBL8!(i$P%))"!"*A!F"`"`K3!&3!8))r!rr!"q!#3!h1 +!)!#3"5!!!!jJ!3!3!*![F!"3L!)B"!%#J+)!"!))J!!!B!"")K0X!!!#+)!!!IL +*3!@J+83JJ%!%P8"3&!&#&!!9!"3JMm$rm!d)!*!$*%"3![2!!!3J!`!*N!!'e6R +X6#!!"FZF,l[ZF!!1Flc[Iqk2$'%BZmlc[aM'-ImD!J3!%"JK--!!N!-#!*!%UDl +j&+%N#P#YA1q"kP%846SJP(a#4#@Ll%!#55!&DeL*3!@J+83JJ%!%P8"3&!&#&!! +9!"3JMm$rm"-!2J!fG&a!"I`J%KSJ")!*)26IeV+5U3!'2'*S3$'))*'-BaM#%BS +-SEc'-Ba*'-94$3d""!!3)#!33!#3!`)!N!5UdIJ!!"J!N!-M'!+!!*!$4L5#SN! +!!$-b!!+U!!!"q)P!"D!T4##!3!593&!8!8)8!"8!&##2`2r`)`!5!!QXBL!pr$q +4+L3-Z$P!PB95e#&a!!TS3Upi-BeI3HaM#-)3LJdK@XBaM!NBa*%8L!"rR2Gcr6* +pChRfImBeM(kS%&1Hplh1Fj*M'$U1Fk-BaLlRiZI1MDXb3!&9B!9V@2P"rErTI## +!3!6eIprhrhq9q"Ai&IL2`2rhC3I)r)L[iZh,r#"mb#"cI#RqPiqj5#'[ciUSM5$ +%6SL!)Urp#2[6qJiK'FI4miNBa)SNL!#-BaLNBc4VQ-BjJNBe9%8%%&4M'-BaM&* +rreI4M'-BaM@"2%M4MQEXMriCDC!!!Iq2IM'aMmIrN!5A-``c$#"q"rmRrLIrN!2 +iT3+&&)LXBaV,r$q4+$q)I%!1N!#&&E8K)3!6+32`a*&iAd6dB`M#%BU0)4M'%BK +*'05%4%J!M'%IT'-mDaM'-(*'054)U""hiaM'(rr5BaKqNBaM'-BdKq4)dBaL!3J +%)I*0DeMj5rfpq$a!!N!%pIIrGrprJIP"r4Ai6rrm#+-#L45)G'-Gdr`J%KJJF(p +!$T!!!&Db%J%!%LSL+-54###!K'-B`K'+M+%BaK'-549Y4)4)!*aK'#4M-QXBaM! ++69988+J4*#FjcK#%%Q-B8T'-CcM'0BNZ5G'FBrmB"%"b5!(iL8SPT3!!3!*!"*3 +8!93"3!!K3#88)%rrr!LM!T-8L#49%&2m2i!)*)!iJ!#3!)!k63`!%'(2h#FiMR8 +!"(4mlhi1Mh4r',S1LiML4L6q12a[R[HMic&V&hR`mE5+LrkS$L2DeVA[Hp*M(pl +ZFjVAZFla*%E1E')!i!"`DC9V@)P+*D8!!%!#3!58&!&8!8!!)8!P&#"2rr`(E3+ +qjdKcYZ!pr#"rq5!!!)!!m!!3!*!$%#!!N!8"!*!-!3#3"L!!N!9!)!!!3#!!N!4 +!F!*`!*!$J!#3#B!%!!C!!*!*!IL*5L@P!!"!!N!%P"3"9!&!!#&!*43J6rrm!#! +!!!3!N!3Krq!!#5!!N!SJ!*!'!J#3'!1!`!!!3#!!N!-$J!!%!*!$!3#3#3F!N!- +"J!#3#!9V@)P+*qF!!%!#3!58(!(F!F!!)8!P&#"2rr`!N!3%!*!%3!#3!`M!!*" ++!3!%!!N!$J!6!"J!'J!G!#!!*3!U!#`!-3!b!$B!1`!p!%)!4`"-!&%!9J"E!'! +!C3"Q!'J!D`"`!(-!H!"p!))!K`#-!*%!PJ#E!+!!T3#S!+d!XJ#h!,`!`3$'!-X +!d!$9!0S!h`$N!1N!lJ$c!2J!r3$r!3-""3%)!3i"%!%9!4S"(`%N!5N",3%b!6F +"1!%l!8!"3J&(!8`"83&@!9X"B!&P!@N"EJ&c!AJ"I3'#!BF"LJ',!Bi"N`'6!CJ +"R3'L!DF"V!'a!EB"Z`(!!F8"bJ(0!G!"dJ(A!G`"i3(Q!HX"m!(e!IS"r`)%!JN +#$J)6!KJ#(3)L!LF#,!)a!M-#1!)p!N)#4`*,!Nm#9!*C!Pi#C!*U!QX#F!*e!RS +#I`+%!S8#L3+0!T)#P`+E!U!#S`+S!Ud#XJ+f!VS#[3,$!XN#c!,5!YJ#f`,I!Z- +#j`,Y![-#p`,p!j!$#3-2!a8$'`-I!b)$*3-T!bm$03-j!c`$3J0)!dX$6J08!eS +$A`0P!fS$E`0e!hS$J!1&!iS$M`18!jN$R`1P!kX$X31f!lN$[!1r!m-$b!20!p% +$eJ2A!p`$i!2N!qF$l3!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B +!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!D3!`)'!3B!"J!'!!B!"J)'!3B +#"J!'!!B""J!'!JB""J!'!JB!"J!'!!B!"J!'!!B!"J!'!JB""J%'!!B""J!'!!B +!"J!'!!B!"J!'!!B!"J!'!3B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B +!"J!'!JB""J)'!3B!"J%'!!B!"J!'!!B!"J%'!!B!"J)'!!B!"J%'!!B!"J!'!!B +!"J!'!!B""J!'!!B!"J!'!!B!"J)'!JB""J!'!*!$"J!'!!B!"J!'!!B!"J!'!!B +!"J!'!3B""J%'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B +#"J!'!!B!"J!'!3B""J!'!!B!"J!'!!B$"J!'!!B!"J!'!!B$"J!'!!B!"J!'!!B +!"J)'!!B!"J!'!!B!"J-'!!B!"J-'!!B!"J-'!JB#"J)'!!B!"J)'!!B!"J!'!!B +!"J!'!JB$"J-'!JB!"J!'!!B$"J!'!!B!"J-'!!B!"J!'!!B!"J!'!!B!"J!'!!B +!"J!'!!B!"J!'!!B!"J!'!!B""J%'!`B!"J!'!!B""J!'!JB!"J!'!!B""J!'rrm +!!![HN!!!N!6r!!F!!2rp!!F!$`6P!!`!!`#3!d)!N$`i!*!-"S!!N!99UeBL8!( +i$P%)%!)!#)T`"`"`!F%&!!#N!!S)%2i$rrJ!N!dJ!*"!"84!!*!%#P!!!!`!!!X +!N!-JJ!!!!IiL8!&S#P%)%!)!#)T3"3"3!8%&!!#N!!S)%2i$rrJ!N!N$`!!!)!! +!#Q!!!"!!$!#3(("`!*!3(!#3"!B!N!4%J!#3#"!!N!BJJ!"9UeBL8!&S#P%)%!) +!#)T3"3"3!8%&!!#N!!S)%2i$rrJ!N!8i!"J!p#!!!#!!!!f3!!$915a5)!!#jFi +AcIFi!!(!1mlhrZM`aK',[1mlmBaM(mJ5!)%!"!B)6!#3#5TVJ#)!L3%!J+Fli(X +!JL!1L!dI%%L%f,h%!#++!!!"rL*3!@J+83J3!J!)LP!&!&!"338!!+3!#JJ3rJ2 +rq!li2J!!%FiJ!I`J!#!J!`!j)(hIeV*5U!!$(M%d%"M%##)J4M'-)4LJbKZ-BaM +%NBaM%-J9!%%!"!J)6$!!N!5!!*!$+V4845K*!T4,@-B!T*4&%9')%+L3!*%*D-B +N!#55!&@V9L*3!@J+83J3!J!)LP!&!&!"338!!+3!#JJ3rJ2rq"&))J!ENM%J"I` +J"%`J")!T6d698Y*KF3!&&#&8)"M%%"![4M'-)3LJdKA-BaM!NBaM%-3BJ!%!"!J +)""!!N!5!!*!$+J4!!!!'!*!$#-B!S!#3!a'*%IL3!!!!$-BJ!!JJ!!!"rL*3!@J ++83J3!J!)LP!&!&!"338!!+3!#JJ3rJ2rqh&!%!!N1M%3"I`r)T)N")!Trd5&-G3 +KF3!&0#DA[#G'TmK3ap#-)3LJiK'XBaM!NBa9%833!"rR2Gcr6*pjhRfGdBeM(kS +%)ZHplh1Fj*M'$U1Fk-BaLj!!5,RcSfV'*!!@5PT9UeBq8(p[qPm)%!)!#2TIrIr +IrhrP2i#Rq!Tr%2i$rrb54mMm4%BaH"hm)#&5)%Xi)!p%MjL))Dr!#94"q'*)aX! +%P[i`MldrS-)4R(dI1*'-5+*#%!!M'-BT'-dDaM'1BT'094&*"#-BaM'-Ba5BapA +mBaM'-Bejra)dBjQpa2rTQU8!!IrMhiaXBr(rN!@,aM"JaJ`3(m"rr%IrL2q3"2b +53S8N4%IaMZ[m2rS`2l"m)!p%K498)5%2LC5"%'*)[#I)PXB`M#%BS1)4M'%BK*' +-9%4#%!!M'%IT'-iDaM'-A*'054)U"#2iaM'(rr5IrKHNBaM'-Bd35")dBaL!#)" +"+d,9UeBq8[p[IJm3!#)!#2Tqrr[IrhrJ2j3(r3Tr#2rrr!0a3SNN4%BaM@[m)#3 +4)%Kq)!p%!"@bS3%!%48"%'*)K"!3&mB`M#%BU0)4M'%BK*'0BNK"%!!M'%B*'-d +DaM'-!T'0543U"#-)aM'%)35BaK5NBaM'-Bd554)dBaMrm)"#Id+!!IiL8SPT3!! +3!#)!#)S#J!T3!8!!""3!43S)#2rrr!!43T%N4%BUMZ[m2b$a*,"pS!p%J&Da%J! +384B4&'*)LSJJN!"'-B`K'+M+%BaK'-549Z*)34!!*aM'+4M-QXBaM#+6998B+J4 +R'FjcM'%8Q-B9T'-CcM'08NZ5G'FBJ"%!"!UP9DY@)P+*D8!!%!!L!!L+!S!+8!& +!!!38!%8+#!Mrrr`!%8+M*-3k+SJVr#!(%-!!1'!!4)!k6T)!%'$RlK1F4c+!!)p +(c[IJk2G(mBZJk,L1*'*2`*!!Ia[R2FMic&V&hR`FE5+LrkS$QZDeVA1Hj*M(lVZ +FjVAZFla*%E1E')!1!!m+@J!"rL*5L@P!!"!!)J!)LJ+!#P!"3!!%&!"&#JJ)rrr +m!"C#[XG#%GY`(IrJq"!!N!-J!(d!%!!-!"!J!*!'J!#3$%!!N!9`m!#3"4!)!!! +3#!#3""!F!3#3"#!!N!NJ!3!"%!#3#9@V9L*5L@P!!"!!)J!)LJ+!#P!"3!!%&!" +&#JJ)rrrm!"!!!!3!%!!!%IJ!!"!!N!XJ!*!'!3#3$5!!N!X"%!J!!"!)!*!$!4! +!!3#3"#!!N!NJ!!!"i!#3#J(q)P+*D8!!%!!L!!L+!S!+8!&!!!38!%8+#!Mrrr` +!N!3%!$J!!"!!N!-3!*!Yi$!!N!MJ!!)!N!4!!*!)!F!!N!e9UeBL8SRj`!!3!#) +!#)S$J!j`!F!!""3!43S)#2rrr!#3$4!!N%X"!!3!#3!1!"-!'3!D!"d!)!!P!#S +!,!!a!$)!0`!m!$i!3`")!%d!8J"A!&`!B3"Q!'F!D3"Y!()!GJ"l!)%!KJ#,!*! +!!*8!QJ#I!+3!U3#X!,%!YJ#l!-!!a3$+!-m!e!$C!0i!i`$S!1d!mJ$h!2`"N!- +%!3N"$!%4!4J"'J%I!53"+3%Z!6-"0`%m!8%"3J&&!8S"6!&4!9B"@`&J!@8"DJ& +[!A-"H!&p!B)"K`'-!C%"P!'9!CJ"R3'G!D)"T`'X!E%"YJ'l!F!"a3(+!Fm"e!( +A!GS"h!(K!HB"k`(`!I8"qJ(r!J3##3)1!K-#'!)G!L)#*`)X!M%#0J)l!Md#3J* +(!N`#83*@!PX#B!*P!QS#F!*f!RF#I!+"!SJ#M`+@!TF#Q`+I!U3#U3+Y!V)#Y3+ +k!Vm#a!,)!X`#d!,A!Yi#iJ,T![!#p!,j![i$N!-+!a%$&J-G!b3$+`-b!cN$3!0 +&!dN$6305!eN$B!0N!fJ$E`0f!hS$IJ1&!iX$N!!$P31D!jm$T31U!l%$YJ1l!m! +$a32+!mm$e32E!q!$j32S!qX$l`2c!rJ$r33""!B%"`3-""!%&!3B""J%(`!(!!F +!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F +!"`!(!!F!"`!(!!F!"`-(!JF""`%(!3F!"`-(!JF#"`%(!3F#"`%(!`F""`%(!JF +""`%(!3F""`%(!3F""`%(!`F#"`%(!3F#"`%(!!F""`%(!3F""`%(!3F""`%(!JF +""`%(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!JF""`)(!3F!"`-(!3F +""`%(!3F""`)(!3F""`-(!3F""`)(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!3F +""`)(!`F#"`%(!!!""`%(!3F""`%(!3F""`%(!3F""`%(!JF#"`)(!3F""`%(!3F +""`%(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!3F$"`%(!3F""`%(!3F""`%(!3F +""`%(!3F$"`%(!3F!"`!(!!F$"`!(!!F!"`!(!!F!"`)(!!F!"`!(!!F!"`-(!!F +!"`-(!!F!"`-(!JF#"`)(!!F!"`)(!!F!"`!(!!F!"`!(!JF$"`-(!JF!"`!(!!F +$"`!(!!F!"`-(!!F""`%(!3F""`%(!3F""`!(!3F""`%(!3F""`%(!3F""`%(!3F +#"`)(!`F!"`%(!3F""`%(!`F""`!(!!F""`!"!!Irr`#3!d)!m!#A!*![!3!"!!N +!!%Z*!!`!!%Z-!*!$&J!+@APEDeae9QKAEeKX8f*8DP9Z8L`!N!-@!!TC@9Y,A&9 +@5&G[@%a63P4+98j5,!#3!d*J!!#B!*![!3!"!!N!!$J*!!`!!$J-!!!)`T!!!*! +%l`!+!!$rpJ!+!!`$C`!+!!)!N!-d!*!a!i"0!*!G(i(!!*!'F"`(!(!!N$T83*C +5J!!"J!#3%J+J!*!%&S&!!*!%J!"3&!8!8!#3#383!-$!!!#!!*!A!FB!N!i$J!1 +!!!!*#"C!N3'3!&*%"54!6!!i!!(Ki!#3#4D"6J#3!`d!!&!8"3"3!!&93!!!$c` +!hlRp*58!!,PhaI0pcJ!"MK21jrZM$')1AHGjhiaJaMp#J))!3-)6'!#3!`3!N!- +&65%GqA43P@Q"#UCYT#UD3URb3d32FK)ALd!qGU!!L$2rP[P4!!#J#3!!9j3&!&! +!!TC!)N!I`rM99NP'%K!"4iL0"!Ba""*4%M'84'--SajM'-BNM'$')b0!3J""!J% +)!*!$"!#3!`9@)5-CM%!!N!Y5j%(9MFc+h!43L!3N9)NTGXpJJ$3!%35!A2B&JGJ +!!UU)8!qI``L2dP##(j!!!88*&HJ+-D[SCUS`M%3M$5+YBaM'")a9+88L!"ZCfjq +h,fjZHhmaJaRe3(+K&BaGhGhCQC[fjQCQQCP09$[eM5d+U$j3QU4#r*5*4ZaJ[NL +J%lL%cF2mr`YJ!*!$LM"I``L&1EP5%Rcb441N(`R2%!5UUp#2G2m1)NeMk2R%LT8 +3L4)!"QCP*P0-QCQFK5T8U5JJFU(9M'D3"@95QC!&38j(pEA0#SJ)r+JIJ55N5AD +[i-PC2k&&5Aer$F2FN!!!!$m'-2r$#)!8CL)9%!*&)(iBNL%,k5Vq-)a%BidL$1- +)d#5+LLL4%J!HBRdQ8ibCQCKP+P4*48$ri41-CT!$Brrp8TQ3"3&81qq0,-J!2T! +!bU4#r+5*&UJ46fSLS6P5"3(GIP6rr!!!LM"r``J!9+C5%"!%484&'*)L""!R4M' +84'1-SJcM#XSNL3T%N3S!*QCK*P0-QCQB&53SUB9!M'-6M'D3"#)K8TQ3"3$NKF@ ++%K!!%4#)"#3NT5N@U!T)5Q*"3LN&!&8!9)!!!!K5,jr$#)!j'BNJ!3`jqi6R%Fb +J!4"(cZI$Sh4q$&d(4F4a#N5I#MmIQGNH8bbCERMLj#NAp8#-AI&cRGhGfGQG8TC +QCRGh!%p%"E(Ki!!"(h+rrbGEk4Di'ckaS%%$j)F!93"6i!#3!b(SRm2i!"!!!-! +"!*!()!!1!*!*!3#3"3''!*!%"!)!!!J3!*!$!31!!!J!N!8)!*!+1!#3#J3!!"q +!!*!%!3#3"&F!F!#3"5!IrJ#3"J)!N!G!!*!A'!`!!!J3!*!$"J#3!a!!N!83!*! +9"!#3#!)!N!4`!*!'!F!!N%J"!!3!#3!1!"B!(3!H!#%!*!!T!#i!-!!d!$8!13! +q!%!!43"+!%m!9!"C!&i!B`"S!'N!D`"Z!(-!GJ"k!)%!KJ#,!*!!!*8!Q3#G!+) +!T`#S!+d!XJ#f!,d!`J$(!-`!d3$@!0X!i!$P!1S!m3$f!2X!r`'3!`8""`%+!4! +"%J%@!4S"(J%L!5B"+J%Z!6)"0!%h!6X"23&%!8J"6!&3!93"@!&F!9m"B`&S!@m +"G!&i!A`"I`'!!B-"L!')!Bd"NJ'A!CX"S!'P!DS"VJ'b!EB"ZJ'q!F)"aJ(+!Fi +"dJ(@!GJ"fJ(G!H!"j!(S!H`"m!(d!IJ"r!)!!J3##!),!Jm#&!)C!Ki#)`)S!Ld +#03)p!N8#4`*+!Nm#9`*H!Q-#D!*X!R!#G3*k!Ri#J`+*!Si#N3+9!TS#S3+S!Ui +#XJ+c!VF#[J,"!XB#b`,4!Y8#f3,I!Z8#k3,[![8#r!-!!`B$#J-1!a!$%J-A!ai +$)`-V!bX$+`-V!bX$+`-V!bX$+`-V!bX$+`-V!bX$+`-V!bX$+`-V!bX$+`-a!cF +$2J#3$2q3"JB'rj!'!!$rN"`!"rq3"J-$!3-""3%(!!B!#3!)!3-!"!!%!3F!"J% +%!!8"!`%'!!B""J!'!!B!"J!'!!B!"J!'!!B#"!%%!38!"J%&!3B!#!%(!!B!"J! +'!!8!"3!'!!B"!`!'!!B!"3!)!!B!"J!'!!B!"J!'!!B!"J!'!!J!"J!'!!8""!% +'!33!"!!'!!-!"3!&!!8!"3!&!!3!"3!&!!-!"!!&!!-!#!!&!!8!"3!&!!8!"3! +%!!8!"J!)!!B!"J!&!!3!!J!%!!B!N!-'!3F!"J!&!!B!"J!'!!8!"3!&!!8!"3! +&!!8!"3!&!!8!"3!$!!-!"!!%!!8!"3!&!!8!"3!&!!8!"3!&!!8!"!!&!!B!"J! +'!!B!"J!'!!N!#3!*!33!"!!'!!N!"`%+!!B!"3!&!!B!"J!&!!B!"`-)!J8""3! +&!!J"#3!'!3B"!`!&!!J""3!'!3F!#!!)!3J!"J!(!`F!"J!*!!J!"3!(!3F#"`% +%!33!"J!)!!B!#Iq3+!!'!!B!"`#3"!XQN!!!N!6[!!m!!2rd!!m!$`5C!!`!!`! +"!$i!N$Y`!6)!N!3'!*!@!93!N!8IJ1!!N!S1!$J!N!X&!*!k+)J#6**!!!!*!*! +G&S#J!*!($RcJ#J!S!*!)m!!!"3!%!!!`!!!%!*!G"``!N"%(!!#)!*!$"#%!f3# +))"N'L33$4))"!*!,!93!N!8@r+!!N!3-!!!+4+!+!#J!!!P+J!!$!I[!!!A46Rj +K55!!"-6[`Ajhlc`!!2!!MjlcrhSB--)1,hchchq'$!Bar38!3)!#!`36$!#3"!) +!N!3+Q4"alq,d+"+9CJ%%8UEE%)T9L%8VX!ai!mi!!(4D!(jk!!!4!-rrPX5RJ!! +%J!L!!!TFS!S!+!!!%Nb!"%B"r#r`"GIeNT&)`!!*,4#$3)!``J!"#H#)BBS3KKJ +a3ai`````L)B-"M%-KS!JJ!)%"!%%!*!%!J#3"!UQ%#)BB``J!*!0!8N5K!94(Ki +Qi!#JK93!#J!N84EdU%!!N!-4!#!+8+!+!#J!!"+9!%!-!I`S%!@#P*5JKr)!#KB +4"8%!8-)%%3S45'#'%))B-N+Y-----!L'$%94&)3!$[1HlRfh&XeRHIGh4M%BaqU +!+&))8``ZFjcR1FjcT5XjcR1M'-9,N!#"k9%K)59!!+#-!)5I4")4&T5`,Z1NJ"% +!)!T3TrS!+1i!N!-3T&rer#J3"Br-D%#%`J!5&"-*II"23dJ)&1P2S)B3JKJd3Nd +`````#)8558S84!!4M'-8MP-R-jM'1)T'-4M%+S!S8JK6$$'-BaM'-BaP,-BaM'- +Ba693HrP5A-bN3"%JP95)4%4a%ADAX$%84)!MXP!+8+3EJ1m4!*!$!44rrI`S%!@ +&"K#JK5qI%K3JN3-)N!$#%q3P+LKJKpkIq$K#$,$p$mm)K4+SLL4%!!q-)a5-8dB +M'-B`JNBUT8453%5+$dX-,h[Hpm)aM'8SaM'-BaM%"6L(q9&5d)!"qIbN!r!r4*! +!N8D@-#m8b2`K6&#Edlpiri-4!*!$IJarqI`S%!@!"5d8K!)!)K4!B3-)N!#q)!* +&+LKJKK#''$4#$,$"$3#)K++T4%3N!"'-)r5-8iBM'-B`FN9+SN5+J%5+#%X--Ba +M'-2rrq8SaM'-BaM%"4#(p9$Fd)!!)L$&9)K%44%4GTIS8I93KN%!L5M5#8["lar +ri!!#$%2jr#J3"B!&8`L%!J!L&)"rJ`N3JK!%42IiB)B3KK`b3Ja``3b!L)5K%84 +%*!!4M#-%M&0')aM'-!T&4%9&#S$rrJK($$'-BaM#%)3P+-BaM'-Ba!83Hr23dXb +!!%)JK!#%K%85%4D8#&%'8B9"-ST2mrP+IbN3!*!$%44'(r`S%!8!&C-85!!!358 +)J8-*%)3,k!)%'''+%)BF-8)-F-%X8)L%34)NK"3!%BaM&)a6*L-BaM#+6)4)aJU +!J`BB4``aM'-BaM'-C5M'-BaR1F`&83AK85%K!!(m)B98!!4&*&%@R!54&&'!J8d +&)!!"5J!T%3#3"+4)!I`S%!@!$ScL5!!J`-Ih!6ca$cP%%%(%(jlcm(SEd2i-,d$ +d,`Ki34)Nr"4rMr1Hj(a6&L-AHI"aY)4)[qU!J`A[`[22HplhZFjcT5LjcR1DeV3 +$[S3"GKiH!!#%2RJ$rq4kcp%@J!c1kik!J3(mN!!!!8S!+1i!N!4$`!(m,r!&!!3 +!!$!!)!#3#%!!N!i)!*!'"J`!N!8)!J#3!d!J!*!%"!F!N!0!!*!'%!#3#J%!H!# +3#!&8!!"!!!!@J!#3"3%!N!3"5J!S!*!'3!!(i!!&!*!'3!#3#)!!N"b)!J#3!d! +J!*!%4!#3"#!!N!BJ!*!CJ!!!&S!!N!8"!*!%!FS!+!#3"34!!*!%"3#3,(!-!*! +*1!#3"%!!N"`"9!#3"4q!!*!&!J#3"3i!1!#3"31!!*!%"3#343%!"!!,!"!!&`! +I!#!!)`!Q!#`!-3!c!$J!13!q!%3!4J"-!&)!@3"I!'8!D`"a!(F!H!"k!(i!J`# +(!)d!P3#F!+)!U!#Z!,-!Z!#q!-3!a3$,!0%!eJ$G!1-!k3$[!28!q`'3!`J"$J% +9!4i")`%S!5d",`%d!6B"13&"!8-"5!&0!9)"9`&F!@!"C3&U!@`"E`&d!AB"I`' +%!BN"MJ'6!CJ"R3'K!DB"U`'d!EN"[J($!FB"a`(+!G!"d!(A!Gi"j!(T!Hm"p3( +l!J!#"3)+!Jm#&!)C!Ki#)`)S!Ld#-J)d!MB#13)m!N%#4J*,!P!#93*D!Pm#C!* +T!Qi#F3*e!RS#J!+'!S`#NJ+C!U%#U3+b!V3#Y`+p!XF#c`,@!YX#h`,M!ZJ#l`, +d![S$!3-'!`N$$J-6!aX$*!-V!c!$-3-f!ci$330(!di$9!0C!ei$C30X!h%$H!0 +r!iJ$M!16!jJ$R31I!k%$TJ1Y!l)$[`1r!lm$[`1r!lm$[`1r!lm$[`1r!lm$[`1 +r!lm$[`1r!lm$[`1r!lm$a32,!p8!!2rr!*!)rj!'#!MrN!B!!2q3*!3%!3-""3% +*!3F"#3!*!3-""3%&!3J""`%%!3F"!`%(!3J$#!%)!3J"#!%)!3J"#!%)!3J"!`% +%!3B""`%'!3J"#J%*!3J"#!%)!3F""`%)!3J#"3!(!3J""`%*!3J"#!%)!3J"#!% +)!!J"#!%*!3X""`%(!3F""!%(!33""!!)!33""`%(!3F""`%(!3B""`%(!38""J% +(!38"#`%(!3F""`%(!3B""`%'!3F""`%,!3F""`%(!38"!`%&!3J!!!%*!3N"#!% +(!3J"#!%)!3F""`%(!3F""`%(!3F""`%(!3F""`)&!38""3%&!3F""`%(!3F""`% +(!3F""`%(!3F""3%'!3F"#!%)!3J"#!%*!3S"#J%,!33""3%)!3`!#!!,!3F""J% +'!3F"#3%(!3J"#3)+!38!"J!'!3S"#`!(!3J#"!%(!3S""3%)!3N"#!%)!JS!#!% +*!JN"#!--!3X""J%*!3F""`%%!33""`%*!3J"$rq3+!)*!JN!#VEE!*!$4!#3#3J +!$!"k!%%!HJ!-!!J!N"-)!!`!IJ"r!(i!$!!)!*!,"`!*!*!$4!#3%J(!!8!"3!G +`!L!"3!#!!*!5!F!"`!(!"r!$i!(!!)!!#3!)!*!$4!#3#K!!-!"H!))!AJ!`!"! +!N"-3!$!!IJ$q!(i!-!!3!*!+#!!'!*!$4!#3&4!!+3!A!!N!#3!I!*!9%!!j!"m +!$`!2!"m!#`!,!*!$4!#3&!J!P!$S!*!!!*!!!2J!N"8)!*`!q!$`!2!!q!!!#`! +%!*!$42J!N!!!N!!!k!#8!!J!N"Ai!2!!m!$i!*`!#!#3&J3!"!#3!d3!N!32m!J +3#"!)%!J3#"!)%!J3#"!)%!r`!*!)$r!Iq"L3%Kri$r!!N!8(!!F!N!0%!3!#J!4 +!$Z!#J!+!!i!!N")"!!1!"m!2i!1!!i!$J!#3%`B!"`#3!d3!(`!*!!N!&`!T!"! +!N"8I!!m!$`!I!$N!%!#3&33!#`#3!b!!-!!3!8)"l3!%!*!)#d4eEQGPEfiJ6@& +`+!S!N!-f0DP6G'PMD(4TEQFJ6@&dD'9YBA4TFf0S)%0PER4bG@dX)%&YFh4PFQ4 +KE5`J-6Ni0G!a1C!$!*!$J!$rN!6`!*!$m!#3!rm!N!2`!*!$r2!!!2!!N!2rr`! +!m!#3"!m!!2!!N!32!!$`!2rr!!m!!2!2!3(`$`!!m2!Im"m2!!$`mI%2N!-!!2# +3!amI$`!!m2%2rr!2!!$`$a#3!`m!!2!!rrr`$`!!m!#3"!m!!2q3"J#3")!!rj! +%m!#3!r!!N!2r!*!$m!#3!rc`!!$`!*!$rrm!!2!!N!32!!$`!2rr!!m!!2!2%"$ +`$`!!m2(a$j!$!!$`N!-I(`m!!2$a!3%2$`!!m!m3%2!2!!$`$`ram!m!!2!2%"$ +`$`!!m!$rr`!2!!$`!*!%$`!!rj!'!*!%3$rJ)$!J+#!m)!3J"#2%*#3TP#T8+P3 +Tj#3%)q3J"$rm2q!rm$ri2r`rr$rm2r`rr$rm2r`rr$rm2r`rr$rm2r`!N!0!2q! +J-#!S)$`J"#2%*#3U9#T8+"3N*#@N*#3Ma#!%2r`ri$r`2rJrr$rm2r`rr$rm2r` +rr$rm2r`rr$rm2r`rr!!!!3!2rr`!#!!'!!J1"3!*N35!#FL%3!MS4#!)H%I`#6p +!%!VmJ"!+(J!3#KF!%!N6J"!)LF!3#($J%!J!F"!)!$J3#!!B%!J!!"!)!!!3#!! +!%!J$`"!)"#!3#!Q3!"!)#P!3#!T3%!J*i"!)"!!3#!2J%!J!!"!)!!!3#!!!%!r +rrr!2rr`!$rrq!!rrr`!2rrq!$rrr`!rrrq!2rrr`$rrrm!rrrr!2rrr`$rrrm!r +rrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!r +rrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!!!!3!2rr`!#!!'!!J +1"3!*N35!#FL%3!MS4#!)H%I`#6p!%!VmJ"!+(J!3#KF!%!N6J"!)LF!3#($J%!J +!F"!)!$J3#!!B%!L(`K!*@$83#@!0%!SXD*!!#Y+@N!!*8T83#%aN%!NJ#4!+d"D +3!!SAd*!!#5JT%!P)*4!)amB3#!!!%!rrrr!2rr`!$rrq!!rrr`!2rrq!$rrr`!r +rrq!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!r +rrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!rrrr!2rrr`$rrrm!r +rrr!2rrr`$rrrm!!!!J#3!rq3#3#3"r!!N!F2m!#3"[!!N!2rm!!!$mm!N!E`$r! +2%"m!!!r-m!#3"I!2[`$a!I!!$mc2!*!&m!$lm2!3(`!2c-c`!*!%m!!2[r%"$`! +2rj!$!*!%m!m!qrrr(`#3"!m!N!6`m2rr[`$`!*!%$`#3"2$a!3rlm!#3"3m!N!6 +`m"!I$lm!N!82!*!%m!m"$`$lm!#3"!m!N!6`!2!3m!qr!*!%$`#3"2!!$rm!!2[ +`!*!$$`#3"2!!N!82[`#3!`m!N!6`!*!'qr!!!!m!N!6`!*!'$r!!!!m!N!6`!*! ++$`#3"2!!N!S2!*!%m!#3#Jm!N!6`!*!%rrm!N!32!*!%m!#3!`m"!I!!N!-2!*! +%m!#3!r!Im"m!N!-2!*!%m!#3!r(a$`m!N!-2!*!%m!#3!r$`(am!N!-2!*!%m!# +3!r%2rr!!N!-2!*!%m!#3!`m3N!-!N!-2!*!%m!#3"2rrm!#3!`m!N!6`!*!+$`# +3"2!!N!S2!*!%m!#3#Jm!N!6rN!`!N!3#!*!$rj!*!*!(m!#3"`r`!*!'m!#3!rr +`!!!2c`#3"[!2m!m3(`!!$mc`!*!&m!qr!2%"m!!2c-m!N!A`!2[`m"!I!!r-c2! +!N!6`!!qrm3%2!!rrN!-!N!6`$`$lrrmI!*!%$`#3"2$`rrqr!2!!N!32!*!%m2% +"$r[`!*!&$`#3"2$`%"m2[`#3"3m!N!6`$`%2!2[`!*!%$`#3"2!!m"$`$lm!N!3 +2!*!%m!!2r`!!qr!!N!-2!*!%m!#3"3qr!*!$$`#3"2!!N!Elm!!!$`#3"2!!N!B +2m!!!$`#3"2!!m!!2rrm!!2!!$`#3"2!2(`r`%"$r$am!$`#3"2!2$r%"N!6r$`! +2!*!%m2!3m2m3(r$`%2!2!*!%m2(r$mcar-m2mI!2!*!%m!m2(mc`r-mI$`!2!*! +%m!!2!Im"$r%2!!!2!*!%m!m!m"#3"2!2!!m!N!6`mIm2!C!$$`ram!m!N!6`m"! +I(rrr(a!3m!m!N!6`$`(`m3%"m2%2!!m!N!6`$am!m"!3m!mI!!m!N!6`!2m!$rr +r!!r`!!m!N!6`!*!+$`#3"2q3$!#3"3G"8&"-!*!'"e"548B!!3#3"!G%394"!!) +!N!3(8d&@43!$!*!%"d*26N8!"!#3"$aZD$-a!*!$!8C548B!"!#3!i!!!3#"!!) +!JJ!$!)-!"!#%5801)`!%!!!$k!!"!qN!!J2U!!-$k`!%!q`!N!-1!$i!6J#k!E) +6L3!%-!S!N!1&!)%!N!Mrrre2"%CTE'8+8Q9NFQ&h)%eKF!"5%3!-8(*PGQP[GA- +J6A0R!&!4!!T5CA"[FfPdD@pZ!%i!!!%Y!*!%"e*PCh9XBA)!'mJ!$89ZG'9b)%9 +iF'a[FQ8!!&J!!5d!N!3%8f&fC3"68`!",3#3"!44G@Pd!&&4!*!&5!##!*!)rj! +$q`4&C'Pd"&9ZC'm!@J!!!5d!N!3$3h9d!&J!!!4$Eh"j!%-!!!93BA0dC3"@!!! +&3faPBA)!N!LX!)-!N!Mrrrhr!dYLC!a$EfjdFQpX)%YPHA-!'mN!#e"eEQ0dG@& +dD@pZ!"[+!!K#FQ&MDf9dF`!Eb`!&B5!Y)'d!'m`!"@iJ,5"k!"[0!!9")#dJ63! +EcJ!&6L!Y)&S!'mm!"6!J,5!j!"[3!!%Y!*!%##KPFf0KF'8T!*!%"bKcF'&MC5N +!N!3)+'4PE'9dC5N!N!3)+(*PG(9bELN!N!Kc!)3!N!MrN!2E"%KPE(!(6h"dD@p +ZF`!!6`!",3#3"!4)C@a`!!!r!!a%CA0MFQPLC5",CAN!!#B!!5d!N!3(9Q9bFfP +[EJ!!GJ!(5'PcG'pbH3!!9J!39Q9bFfP[EL"'C@&dGA*PF`!!)`#3"5S!J!#3#2q +3!rX"&!j"BQpeG#"1CA4)B@0Vb3#3"!%Y!*!)aJ#&!*!)rrrpl`4*EQC[$8PZGQ9 +ZG'pbH5""E'`!!'N!%%PZGQ9ZG'pbH5"6C@aPBh3!!%N!"N&NDR9cG!!!)`!",3# +3"!P-EfpV)%4[Gfi!!$S!$%4PFf0bD@*P)%pZC3!!1`!04'9cBh*TBQ8J6@&ZH3! +!,`!04'9cBh*TBQ8J9(*KF!!!AJ!",3#3"!a$B@aX)%e[ER0dCA)!!%-!#djKE@8 +J6f*UC@0d!!!M!!Y%DA0MEhCPFQPPF`!!A!#3"EN!L!#3#2rrqpm&6@&RD@-*4(* +[F#"*G'9Y!!"N!!Y%FQp`)&0PE'9MG!!!4!!'8'PMDh9`!!!X!!e8EfGRE'8JF'P +MDh9`!!"!!!%Y!*!%!d9KG!!!C3!%8Q9KC!!!FJ!&8A9KCQB!!(%!!d4TF!!!)`! +",3#3"!Y-DA0d)&0`C@aXF`!!+`!+3f&cG#"6F'9XE!!!@J!$@Q&`!!"k!!C*ERC +[Df8!!#-!!e*eBJ!!)`#3"EF!L3#3#2rrZlm%3QPdF`C6C@&bBfJ!!(-!#N0XEh0 +P)%4[Eh)!!'-!#8p`C@iJ4'p[FJ!!E`!&3A"`E(N!!'%!"%YTBfX!4"%!!5d!N!3 +'9@jdFQ&`!!!M!!9'Eh*MC3!!)`!%6'p[G!!!)`!",3#3"!G&EQGbBACP!!"&!!0 +%DA!!!#-!!e0TG!!!)`!",3!!)`))3faTEA!J9A!!!$`!#N0XD@eL)%4[Gfi!!$i +!N!@,!-S!N!MrN!3,F(9ZBh4eBA4TEfi#)#i!N!3#)#`!N!3#)$X!N!3#)$S!N!3 +#)#%!N!3#)$m!N!3#)#X!N!3#)#d!N!3#)$d!N!3#)#-!N!3#)#3!N!3#)%!!N!3 +#)#B!N!3#)#S!N!3#)(i!N!3#)&m!N!Ki!-X!N!MrN!3)BR*KBfYPG(-"@`#3"!& +G!*!%!5J!N!3"+3#3"!&l!*!%!Ad!N!3"2!#3"!%q!*!%!9i!N!3"B!#3"!%R!*! +%!5)!N!3"A!#3"!%[!*!%!A`!N!3"*3#3#'-!c!#3#2q3"!9K)#dJE3&K!*!%!@) +!N!3"B`#3"!&N!*!%!@8!N!3"CJ#3"!&R!*!%!@J!N!3"D3#3"!&U!*!%!@X!N!3 +"E!#3"!&Y!*!)B`$0!*!)rj!%"@iJ,5"k!@i!N!3"E`#3"!&`!*!%!A%!N!3"FJ# +3"!&c!*!%!A3!N!3"G3#3"!&f!*!%!AF!N!3"H!#3"!&j!*!%!AS!N!KM!-i!N!M +rN!3&35!Y)%d"33#3"!&#!*!%!8-!N!3"4!#3"!&&!*!%!8B!N!3"4`#3"!&)!*! +%!8N!N!3"5J#3"!&,!*!%!8`!N!3"63#3#'-!c`#3#2q3"!91)#dJ@J&1!*!%!8m +!N!3"8!#3"!&4!*!%!9)!N!3"8`#3"!&8!*!%!98!N!3"9J#3"!&A!*!%!9J!N!3 +"@3#3"!&D!*!)DJ$*!*!)rrrpr`aMEfjdFQpX)'YPHA-"BJ!a!!!"DJ!b!!!"EJ! +c!!!"D!!d!!!"E!!f!!!"H3!h!!!"D`!i!!!"G3!j!!!",3#3"!&N!%3!!!&`!&! +!!!&b!&)!!!&d!&3!N!C4!0!!N!MrN!3&-#!Y)$N"-!#3"!%a!*!%!6)!N!3"-`# +3"!%d!*!%!68!N!3"0J#3"!%h!*!%!6J!N!3"13#3#*%!b!#3#2q3"!ChDATKFQ3 ++3A4dFQPLGA4PF`!!H!!04'9dC@0d)&9ZFf9PEJ!!C3!*4Qa[Eh)J6@&`!!"Q!"" +(C@jPFQ&dC5"0EfjcG'9b!!"R!!K*C'9ZG'PQH3!!D3!*6'pMBA4TEfjc!!"[!!j +-CACPE#"8C@aPF'pbG!!!GJ!%9fPcD!!!G`#3"9m!d3#3#2q3"!GMGA*bC@jd"PG +PBA"[EJ!!+3!&3A*YEh)!!&X!"9*TEQGc!!!p!!C"EA9XCA3!!#)!"94[Efac!!! +S!!4(EfaN!!!N!!C6F'9XE(-!!#X!N!8X!)!!#J#!!*!$J3#3!i)!N!1$!*!$K!# +3!i8!N!1'!*!$K`#3!iJ!N!1*!*!&,!$)!!S!b!#3!mN!N!2+!*!$b`#3!m`!N!2 +0!*!$cJ#3!mm!N!23!*!$d3#3"C!!!!d4T6)`-5"$EfjdFQpX)%YPHA-3T6)`-L" +3G@jMG(9KG'P[EJfP-M!c)%*bB@0VCA4c#U8b-$3JB5!Y)'d+T6)`05"Z)#dJHJU +P-M!f)%%J,5"0#U8b-$FJ6L!Y)&S+T6)`1#!`)#dJ13+P,3JET@9cBf&`C3FJTA0 +`B@0P#!LPC'9XCA4P#!fPFQ9dGA*Z!*!$C3!0"`+PBh4X,@)(#U9MG'`YDJF1T@0 +dE#eZ"`LPBh4X,@J($+9MG'`YE!FCT@0dE#ej"`ZPBh4X,@X(&D9MG'`YG3+P,3F +%T@0dE#eN"a#PBh4X,A!(%U9MG'`YFJF8T@0dE#ed!*!$)J!3!5i",!%l!6S")3% +r!5X",3%p!5-"*!&!!5B"+J&q!9m!N!-L!"!"@`&G!5J"+3&l!Ad"2!%q!9i"B!% +R!5)"A!%[!A`"*3#3!a`!$3&K!@)"B`&N!@8"CJ&R!@J"D3&U!@X"E!&Y!*!$(!! +0!@i"E`&`!A%"FJ&c!A3"G3&f!AF"H!&j!AS!N!-F!!d"33&#!8-"4!&&!8B"4`& +)!8N"5J&,!8`"63#3!a`!$3&1!8m"8!&4!9)"8`&8!98"9J&A!9J"@3&D!*!$&J! ++!6!"-3%b!6-"0!%e!6B"0`%i!6N!N!0#!!J('+9MG'`YH!F&T@0dE#eP"`DPBh4 +X,@B("k9MG'`YC`F*T@0dE#eT"`qPBh4X,@m(&U9MG'`YGJFAT@0dE#eh!*!$%!! +(!5N"@`%p!5)"+!%N!5X!N!0,#c&cG#"YC@je)%P%4&G54!SM)'pQ)%e&6P9c6d0 +19!8UN!9-8e4$"P*PFb"*4%4A8N3)8Q9cCA*fC@4'9e*%"5U3"8a69%8!N!-E!!J +"6`+P,3%r!5B#T5d"GJ&@##0fCA*cD@pZ!*!$*J!-!@N"53FMB@4UGA0d!U8Y!6S +"1`%[!9i#T5d"3`8MEQ&YC3&F!*!$0!!#!*!&C`%%!(X"9!3#6dX!N!8,!&%!@!& +JL!4H-&ia!*!&#J!B!#S!1+!#!!%!N!0%!!-!N!9G!3i!F3&+"!*1E`#3"9d!A3" +a!*N%!ePPF`#3"JS!6`"3!91)!Pi`!*!&#J!@!#S!0U!#!*!&$J"%!%3!XJ(9!)" +%4$!+!*!$$J"+!'S!d`(A%iJ!"$!+!*!$3!!3!A-"B`&[!@%("+9MG'`YC!+P,3F +MG@jdFQ&`"L0QEh*MC38ME'p[G!+P,3&&"#0NDA!%)h0TG!+P,3%m!6i!N!-8%dj +PG%KKBfXJ8(*PCQ9bC@jMCA-!N!06!!m",J+P,3J8T5"MG'`YG!8MDR9YF!JME@p +ZFh4PFJ8MGfP`C3+P,3&K!A!("+9MG'`YC!8MBfKKG!BMEfCQCA)&)h"bBAN&)h* +TC'8&)h4eFQi!N!1F!)F!N!MrN!0l!d&MG!4AB@Pd!!!Z!!%Y!*!%#&4PE'9`Eh* +d!&34!!4+G@e`!!!M!!G0EfjcG'9b!!!M!!4ADA"P!!!M!!%Y!*!%"8&`F'aj!!" +K!!03BAN!!(!!"%YTBfX!4"%!"%0SBA3!!#-!"8pQCQ9b!!!M!!43FQ&j!!!M!!4 +5D@4P!!!M!!48GA*Z!!!M!*!&,J!2!@3"4!%X!8!#T5d"C3&b!A%%)f4TF!+P,3% +V!9S"HJFMD@jfEfYP"#0bG@)!N!0#!"%-T6)`15"$GA*bC@jd!U8Y!AF"H!&4!@B +"G!&K##0PEQKKEQ0P#L0dGfphC@&`Efi#T5d"9`&8!8%#T5d"8!&5!*!$m3#'!*! +)rrphq`9&FA9TF!G$GA*bC@jd!"[4!!%Y!*!%$>C@aN)&GPBA"[EJ!!G`!24AK +MD'&ZCf8J9f9KF'pZ!!"i!!e6C@aPBh3J8A9TGQ9b!!"4!!Y'DA*P)&&eDACPFJ! +!CJ!&9'KbEhF!!(3!"8&`F'aj!!"K!!G&EQKKEQ0P!!!M!"&8GfmJ9f9KF'pZ)%0 +[E@*KG!!!)`!",3#3"!TAC@&b)%&bE@pb!!"A!!K8B@YP)%pQCJ!!9!!+3A0V)&* +PE@pfC3!!33!",3#3"!C3GA3J6fi!!&!!"P*PE@pfC3!!8J#3"6!!N!F%!!$GN!B +!!3#3"`)!N!F$!*!("2q3"J#3!bS!3J!Z!1)"XJ!&!3#3"aG`%P0PE'9MG#"K)%0 +SBA*KBh4PFMB`#J#3!e3!N#!J!!"!!*!Srj!'!*!%!5i!%!#3"5J"%J!m!@)%"&" +XBAN!N!91!4)!BJ&L"!44G@Pd!*!&*!%1!%!"CS!!N!C3!%J!B3"k!*!(8!#k!'% +!lJ#3"hJ!5!#*!(S!N!Gi!,`!L3$Z!*!(H!%q!)N"B3#3"bN!5J!j!2!3"%jKE@8 +!N!8B!"3!1!!dS!)!!3#3"9!!*!"J!%L)"9*[E'8k!*!'8!#@!'!!ZSJ&8Q&MC6T +P!*!&H!!8!)J!5)J(4f9ZC'9b1J#3"RJ!PJ#)!,b)"N&XD@GZ1J#3"AJ"%J#)!6k +)"8e[C'8kD!#3"4-!5!!N!2+)$&GSEb"KFQ8JH@pe2`#3$!%%!!%AF!#3!c!!N!F +%!!$GN!B!!3#3"`)!N!F$!*!("2q3"J#3!c!!N!F%!!$GN!B!!3#3"`)!N!F$!*! +("2q3"J#3!c!!N!F%!!$GN!B!!3#3"`)!N!F$!*!("2q3"J#3!c!!N!F%!!$GN!B +!!3#3"`)!N!F$!*!("2q3"J!!#-i!!3#3!a`!N!01!*!%rrm!N"#!)!#3"8!!3!# +3#8J!N!0)!*!&"!!"!!3!N!B)6J#3"#)c-L+3"5-L%4)L)MYiXL+3"K)M-bY$1l- +LN!-c-L+3"5-L)K)5[(h$)T!')5)M-b9$)eXb)L)c)T!*)eHA8b+3#50$1d-bZd) +L)L3b)T!$-L)M0,CpRFXL)b)L)b+3"M1l3c)PBb)L)l-cZfc'E(ICU[fb)e-M-b+ +3#E4$-b1mXd0,HCQCUT!$rkUBa6-L*M-c)T!()b1d3c-b*'amb0aP9EZlE'9EY&D +c-L46-c)K)c)5)lY8+l4$-M-b*FCXZd-L)K&,4%ZdYV-c-eXc-L)MZj!$3V3VY$1 +3"E-L@d-b)L'lN!0%4E3c4,8c)LYPXb+3!bXl4$0%-dZl-c1l-c-L)9ZlY%3cY%4 +%1l-MCM)LN!3N*83d0%5l3c-b46-c)L*8Zl4%3lY%4$0EA&-b)T!&*84$0%5d-c) +L*6-c-L0EZl4%3cY%5c*FDc-LN!BQZd0$Y%3c-L)V3c-L+l5lZdXc+d4%-V@d-L+ +3"%)L0E4%-c4$-c)L)d3c)L@dZd4%3cY$0$)f4$)LN!3b)PY$-c3cN!3L)N-b)LC +,Zd4%-b3d4$)QY$)LN!4#)V4$-c3l-j!$)L)c-L+eZlZd4$-MY8-b*N-b)T!%3b+ +l4$0$-j!$0$)L)M)LDlZlY%3c+eDd-MBc-b+3"$3LY%-c0$13"$)L)M-PDlZd4%- +c1eCPXN8c-b+3!a%V)N3cN!BM)T!$-caEZl4$-c0,9E99Zc-b)T!$%4Xb0$13!c) +M-b)L)5)b9EZ3!c-L1l9EZl[--b+3""%E3c3c-c)L)c)LN!3c@lZ3!d-M5e9EZl4 +ADc)LN!-4%e-l-b-b)M)b)T!$)$C9@lZlXcZe@lZlY,aQY$)L)K%5`c3b-c)L-M) +LN!-Vc&@lZl3c4E9EZlY$YV[&Y#)K%4,,*$-M-L-LN!3V8la9ZlZc0&YEZj!$-f@ +e9QGfZlYSfc4$)M)LN!3VC6-m9EZdY,@lN!5d3f@lZeA-amc-M$3c)T!&1maE3c9 +VZlY9DlZ3!l4$1f@l4,Z3"$0V4$)L)L-L)XaV4$-cbe9QCEZ3!d4%-c9EXc-d4$1 +3!eZlN!1e9V9F@l-c-b*P9P@lN!0%3d-c0VY$-j!&0'Xc-l0&E-c'3c-c)L+m9PZ +lZl4$0$-c0VY%-j!&ZlXb-cAF@lY%3c-b)L%X99Y%4%-cN!3fY$13"#)NY$1c)he +EZl4%3b-L)c*'9E3cN!3b)c1fXc13"#)VXc06@0@l4*!$-L)L098d@l-b)T!%)c+ +l3c13!c)L+d-bE0GEZlY%-c-L)VZlXeZc)T!'-VXd4$-c-L)NXc*&@lY%4$-c)L+ +l-c0#Zl3LN!BbZd-cN!-b)L08-d5d4%-c-b-L+d-L)M0&Y$)cN!-L-c0E3c13!c) +L)QZl-l-c3c-b)L*$-L)L-c5l3c-c-L)M-fXc-c4%-L%Pbc)N8c1d-b)L1l-b)L) +c-l4$-cZdY%4EZj!$99Xb*'@c)M1c0&@c)L-l-c-b)MZlN!1d3c-b)VY%Zl-cYEY +Q4$)L-N)VaV-K%83L)c-b0%4%3c)dY$-L@d53!c5l9V)N3b)c0$5e-L%5Xc13!c) +VZl5d-dY$-L+l-c0%-c5l-L+d-c-N4,Zd-L)b)c0%-LZlY%-cN!-M)VXcN!BL)MY +$-c1lZc3c5c)M-c-b+d3c-c)L-b)L5c-M-j!%)L)MYEZ3!c-L-cXb)c0$-L4%0$) +L)b+3!cXb)c-c-L)M)T!$)c13!c)L*E-cN!-b*%Xc-L-c)T!$06-M-j!$)L)b)L) +c-b-c3b*#1c13!c)N5d-cN!-LN!-Q3b-cN!-LN!8M-L)L-c3L)d-c-c)MZd-cN!- +b)L)QXc-d-c-L-c)L)M)b)L)M3L)L)d3c-b1l-j!&)L)PY$13!d-LN!NM-L+3!c@ +c-cZd-j!&-M)PY$13"#+3"6-b)T!&-b)lY8)eZd3cN!3L)LZc-j!%)T!+)b-c)L- +d99@l4,-c3c-L)LYL-j!%)T!$-L+3"M-M-c)L-c)L-j!$3d-c)L)MBc-L)M-LN!J +l@d-cN!-L-L+3!b-lY$-c)L)MDc)cY@Xb)T!'+eZl@c0$3c-LN!-c-N1d-c1c)L+ +mZl9EY$)L)M)LN!-VZl3c0#-c3c0%-b)N-N4$-c5l3dI'@d4%-c-L)L-L)MZlY$) +LN!8M-L1c-d4$-b)L1l@d1c-c0%-b-c-b)VZ3!c-LN!BVZl)b-c-b)T!$5l-N3c- +dZd1lZl4$@lZl-b)L-j!&YN)L)c)LN!3M-L+dYEZlY9Zl4,9EY%Y$)L-c-b)L)cX +b)T!*)f@d-d4,3c13!l0$0%)L)c-LN!-b)l)LN!-K)T!&Zd-b)M-c-M3c4%-c0E) +L)L-LN!-K%V)LN!F4*&3c)K)K)L)l4$1c-d8b)T!%)5)L)E3LN!BK%5Bd-L+3"6Z +l3d-d9$)LN!FK+c)LN!-M)L%4YM-LN!3K)NZl3dZl3c)LN!-M)L)5)4-b%4)L)b) +K%FXLN!84)N5lY,Zc-b)K%M)LN!853b%4)M)L)5Yc)T!(-d3lZlXc)L)!N!F1!!$ +ZN!B!!Gf3"J!#c*!'!!1lN!B!"+U3"J!&L*!'!!ChN!B!"e@3"J!)4*!'!!NLN!B +!#K'3"J!,QC!'!!aQN!B!$613"J!2!*!*3J$`!*N!N#m"!!%!#3!!6)N!$!!!6)` +!!!Q)N!!!N!6q!!B!!2rh!!B!#`1l!!N!!J#3!cm!N!H!!*!("!#3%#!!N"!"!!E +!!*!'SJJ!N!3"5J!!!B!!!0!!!"$#!#YDa%S!ri2P%))"!"*AKm(Jq)8!"8!&##2 +`2r`!IJ#3!acJ#!#3"5!!!!jJ!!!3!*!&J!#3"`i!N"!J!*!3!i!#K%!3`#!)&!8 +3!#!34!!!!`!##4#EB!!!%83!&!r%5J#ZqU83JJ%!%P@pIlqSK3!&3!8))r!rr!0 +#!*!$#4!8!,R!!!3J!`!*N!!!e6RX6#!!!6PcKIGpcJ!"a(HGlrh4iB`M&hRHGq- +BaMr3d"!J!)$"#BB!N!33!*!$"8ehb+8*)&+&DZGm$e+)SLR4"+2L%L%Y&f)!%NN +!+eV%5J$Glf83JJ%!%PEZkh[BK3!&3!8))r!rr!6!$i!0R4F3!8IJ%KSJ")!*)2( +IeV+5U3!"4ia0#!Ba"")ZM'-B3M&"P$HBaM'*)aLU)C!!D!JJ!)%"!))!N!33!*! +$"9D2`!!!`!!!!4M!&!#3!`)a*"85!!!"QC!!!"93!"32a%S!VYDP%))"!"*9hAH +eU)8!"8!&##2`2r`)`!5!!QXBL!p(rj%U*!bi18#4K9,8)A%!!Nd)9Hm'-D[S2ia +K'%)43D3V@-BaJ5-BNL+)3!2mjlZIkC2V1mqcrM'XBr9!JTch[HjcR*-B`G4cR4M +'-AFr&cjdE9Q5!!UV!#YDamS2hHpPm))"!"2@lZYlhq9q"Ai&IL2`2rhC3I)r)L[ +iZh,(i(c))(0m+Ik9MlP))Dr2`P84T"L*d4!%9IqK(hTr3F3M12SqF5-BN85)3!4 +M'-8M'D0FaM(-%M'USLNJJU-BaM'-BT2rqVk-BaM'-D`*iND-FcGNIr$,6*32r([ +aVYDr(rq3!rjGhAHeU"q"rmRrLIq3!riT3+&&)LXBaV,(rj%S2iKm3!k9K4@e)5% +!"'8JIKL5,`[SPBaK'%)a8D3M'-)a#5-DN!#)K%!%B`Mp)aRM@-BaJj)aU5*&3)1 +r'-B`rrk6'-2dM'-BaM'N2b*'M'-3#%!K$j*V@XI+Aph[B2%!#3!6eZlVHprJIP" +r4Ai6rrm#+-#L45)G'-Gdaq!5'#"`Id!1P3"@XK)"!!4&4%8BNL%%%!k-BaK#-9' +8)aM#-BNLVDL3!)4!"1-)`5-CNeM'-B"5DUULK8#*)6R1F)3JNaM#P)aM1FBaV%P +b6ScM(rM!)J159!r%5P'ZeU!"!!N!%PAGGl@S!!K3#88)%rrr!LM!T-8L#49%&-I +rJ!JNJ$L!!*'!1Nd-!"!S1IZ%ja(1S!#+Mjh[`G(ZMq-A3G&a(%M%Rm,(ihch[4m +CLeLlciH0T&4Ip8"a(YDeVh[HNaMqph1FeVh1GiNL0R0M%!F!!i0-UeV%5P(Glf! +"!!N!%PEZkh[B!!K3#88)%rrr!GY!VlR5(1fi$dIJIrNJ!!#!!2%!%!#3!a!)!*! +')!!+!*!+)!#3"3)!N!8#!3!!!J%!N!3#!i!6J!!!"!#3#33!)!!b!*!*&!r%5P( +rrm!"!!N!%P2rN!2i!!K3#88)%rrr!!J!!!%!N!3)Iq!!#5!!N!SJ%!#3"N!!'`# +3%!%!N!8F"J!!!J%!N!3F!3!J!*!$#!#3#6J!N!--!*!*!9V%5P%!N!-"!!N!%P! +!N!8)8!P&#"2rr`#3"!%!N!33!*!$#-!!N%S"!!3!#3!1!"-!'!!D!"d!)!!P!#S +!,!!b!$-!13!q!%!!43"+!%m!9!"C!&i!B`"S!'N!D`"Z!(-!GJ"l!)!!K3#+!)m +!P!#C!*i!S`#S!+X!X!#e!,S![`$%!-N!cJ$6!0J!h3$L!1F!l!$a!2B!q`%!!3) +"#!%+!3d"%`%9!4S"(`%N!5N",J%b!6F"2!%p!8!"43&(!8`"83&@!9X"B!&P!@S +"EJ&c!AJ"I3'#!BF"M!'2!C!!!C-"Q!'B!Cd"SJ'R!D`"X3'f!EX"`!(&!FS"c`( +5!G8"e`(F!H%"jJ(V!I!"p3(k!Im#"!)*!Ji#%`)B!Kd#)J)R!L`#-3)f!MJ#23* +#!NF#6!*3!P3#@3*H!Q-#D3*[!R!#G3*k!Rm#K!+*!SS#MJ+5!TF#R!+J!U8#U3+ +Z!V-#Z!+m!X!#``,*!Xm#dJ,B!Yi#i3,P!ZN#l3,b![F#q`-!!`8$#`-4!aF$(3- +K!b3$*`-V!c%$0`-l!ci$4!0+!dd$8!0@!e`$B30R!f`$F30h!h`$JJ1(!i`$N31 +@!jX$S31Q!kX$X31f!lN$[!1r!m-$b!20!p%$eJ2A!p`$i!2N!qF$l3!'!!B!"J! +'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J! +'!!B!"J!'!!D3!`)'!3B!"J!'!!B!"J)'!3B#"J!'!!B""J!'!JB!"J!'!JB!"J! +'!!B!"J!'!!B!"J!'!JB""J%'!!B""J!'!!B!"J!'!!B!"J!'!!B!"J!'!3B!"J! +'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!JB!"J)'!3B!"J%'!!B!"J! +'!!B!"J%'!!B!"J)'!!B!"J%'!!B!"J!'!!B!"J!'!!B""J!'!!B!"J!'!!B!"J) +'!JB""J!'!*!$"J!'!!B!"J!'!!B!"J!'!!B!"J!'!3B""J%'!!B!"J!'!!B!"J! +'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B#"J!'!!B!"J!'!3B""J!'!!B!"J! +'!!B$"J!'!!B!"J!'!!B$"J!'!!B!"J!'!!B!"J%'!!B!"J!'!!B!"J-'!!B!"J- +'!!B!"J-'!JB""J%'!!B!"J%'!!B!"J!'!!B!"J!'!JB$"J-'!JB!"J!'!!B$"J! +'!!B!"J-'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B!"J!'!!B""J% +'!`B!"J!'!!B""J!'!JB!"J!'!!B""J!'rrm!!![mN!!!N!6r!!F!!2rp!!F!$`6 +d!!`!!`#3!d-!N$0!!*!)!F!!N!`d!*!&!UUeBL8!Ir!r+)3)!3!%46i2`2JIL#J +!"5!!8%#(m"rr`!#3#!Fi!!!%!*!'!`#3"b!!N!K!!*!2!3#3%N!!N!JU)J#3"9+ +!!!"J!!"B!!!""!!"8"rL*3"A[l8SK!J"!!4&+[VrVr@)+!!&)!"33)I`(rr!!*! +)#2`!!!3!!!&-!!!$!"!!$!#3!b!!N!MJ!*!2$`1!!*!3i!#3"$!!N!-#*!#3#B! +!N!8""!!#UV9L*3"V@ZXSK!J"!!4&0leVhAU)+!!&)!"33)I`(rr!!*!%!F!!`!M +m!!!%!!!"XJ!!!p8j,&)J!!"1A1&mhh1!!"a(HGlrh4iB`M&hRHGq-BaMq)#3!!3 +)!#!`3Q!!N!J"8e`"%!4)#!3&1Gm$f!34!(4!D2L#4#E&lL!"&&!"8"rL*3"A[A8 +SK!J"!!4&+eVeVV@)+!!&)!"33)I`(rr!Gm(`!!#1F3!)r!!%"!"J"b32J!2IeV* +5U!!!8H-633'-3))Lk-BaK#-8'80aM'-BNM'-BKL!U!))!#"!3Q'!!*!$"!#3!`& +9SU)T3NJ8SPV'-!8NSLL+M%#&4)5)5dBa)!%NN!!#UV9L*3"V@ZXSK!J"!!4&0le +VhAU)+!!&)!"33)I`(rr!LN%3!0b4L3!Sr!#*K!#3!!8Tk)!$e9,5BA%!!*&#&8) +"M%%"!rM'-B3K&"T#ZBaM'")aM')B3-3!#!!J3%!JJ!#3!`3!N!-"8#)!N!-`!*! +$4M!&!*!%M%L2a)!!!'Ba!!""!!&3(q)P!&HpG5L%#!%!"%8V@[@ZYBJS!!8J!&" +!Kr!IrpZ+!)!")G')J#Mrj&*%J*!!"6rSJ!1&-G3KF3!!Nd*THm*dDRb&@2S4K#% +8(%)eM'-B%M'+SLK!J!$r1HlRqQ6lc[2XlSaV'2e3)4FplhZFjb6'-(8FjdBaM&b +#4FqG'eBa)!#b8Y+UY@2P"qYDkbq%#!%!"(dh[@[GH[mTr!8r`&2iKr!Irq552NI +L)M',`1Mm"#T%#@F%!HL!!iqBL#'[`!%94"q'*)aX!%PIaK(hTr3B3M12SqF5-BN +85##!!4M'-8M'D0BaM(-8M'USLNJK'-BaM'-BT-BqVq-BaM'-DmriND-FcHiRrdc +9+9!IrMhieleeq2q3"FAV@[@ZYB$q!rrL2ra(rj!%j*)8+5)L2iah@2rr4JIf$i3 +"k)!$K498)5%2i4P)%3BNLm*mL9M'%B3M&"a#-B`M%*)aLSL)))!"'-)r5-C`eM' +-BZ5-DNL48#%IaM'-2rqNrr#p)aM'-BaSJN#4SaM%!%3##9S@UV9Mj5rV@ZX(L!! +4!!4p0leVhAVr!IbJ2qK6q%Irrq!ELK4*)L)aM'YBr!5#*!N2a!(SJ!-!&E+K!3! +#%9!4"L5)33%!5-B4K#-9'N)aM#-3NM'X53J3J!%B`M")aQM@-BaJ&)aU5+&3)4K +'-B`K##6'-+8M'-BaM'L55*'M'-IrK!)6qK93(q)P+0HpG3!)!"%!"%8V@[@ZYB! +!)+!#+&"!4rrri!#+&)NL)M&8GeMrj"iNPJqd!HL!!i"@X4)!%")4B4&'*)LSJJM +SaM'%)a8C3M'-)aL5+Ya*#"#!!6M'-8M'C0BaM'%8QUUS`9!M1-jcR'-)T-B`V5- +BcR'-DT*FNk-ia!#)!#"9+UUeBL8SkeVV!!J!%3!%46HpDpekJ!!JS!)S8%"(rrr +J!)S9'5BKd94"@2`!iKJ!"``!#)!$J$T1NJ!3&!jqi6R%FbJ!#+Mjh[`G(ZMq-A3 +G&a(%M%Ri#)2ihcRZ4mCLeLlci10T&4Ip8"cA0DeVR2FNaMpeh1FeVh1GiNL0R0M +%!(!!H&,48"rL*5MA[A8!#!!4!!4&+eVeVV@!!##J!LK33%Irrq!!XKAf1K#1fi$ +Sr"m#!*!$"!!2J!-!%!!-!"!%!*!'#!!!S!#3#3J!N!81#i!!N!@!3!!!J%!!N!5 +!i!J!N!-"!*!*!3!)!!L!!*!)!UUeBL8SkeVV!!J!%3!%46HpDpekJ!!JS!)S8%" +(rrrJ!)!!!#!!J!!!L2`!!J#3"`-!N!8J#!#3"K!!!+!!N!N%!*!'"!#3"3L!3!! +!J%!!N!-)J%!)!*!$!3#3#3%!N!-2!*!*!9!IiL8Srrrq!!J!%3!%44rrN!5!!## +J!LK33%Irrq!!N!3J!F!!!)rm!!)!N!F$!*!'#!#3#!'`!*!3"!#3"3F"J!#3"`F +!3"!!N!-#!*!*$J#3$JUeBL8SJ!#3!`J!%3!%43#3"b#J!LK33%Irrq!!N!`#!*! +(!`#343%!"!!*!!i!%`!C!"S!(3!J!#8!+J!X!$-!0!!l!%!!3J"(!%`!83"@!&X +!B!"P!'S!D`"Y!(%!GJ"k!(m!K!#*!)i!N`#B!*d!SJ#R!+`!V`#d!,N![J$$!-J +!c3$5!0F!h!$K!1B!k`$`!28!qJ$r!33""`%1!4%"&J%G!4m"*!%T!5i"-`%i!6` +"33&'!8F"5J&2!9%"9J&E!@!"C3&U!@m"G!&i!Ad"JJ'(!B`"N3'@!CN"QJ'G!D) +"SJ'R!D`"X3'f!EX"`!(&!FS"c`(8!GN"h!(I!H%"jJ(V!I!"p3(k!Im#"!)*!Ji +#%`)B!Kd#)J)R!L`#-3)f!MX#3!*#!NF#6!*4!PB#@`*J!Q8#DJ*[!R8#H`*m!S% +#KJ+-!T-#QJ+E!Tm#S`+S!Ud#X3+h!V`#`J,)!Xd#d3,9!YN#i!,R!ZX#mJ,j![d +$!J-(!``$%J-B!ad$)`-T!c!$0`-q!d8$5J01!e)$9`0H!f8$D30Y!h3$H`0r!i- +$LJ13!!19!jS$R`1N!kS$V`1f!lX$`!2&!mS$c`28!pN$hJ2M!qJ$k`2Z!r)$pJ2 +l"!!%N!-*"!S%$`36""F%'`3E"#)!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`! +(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F!"`!(!!F$"`)(!3F""`% +(!!F$"`)(!JF""`%(!JF!"`-(!!F""`)(!3F""`%(!3F""`%(!3F""`-(!JF""`% +(!JF""`%(!3F""`%(!3F""`%(!3F""`)(!3F""`%(!3F""`%(!3F""`%(!3F""`% +(!3F""`%(!3F""`)(!!F#"`%(!!F$"`%(!3F""`%(!3F#"`%(!3F$"`%(!3F#"`% +(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!3F#"`-(!JF""`!!!3F""`%(!3F""`% +(!3F""`%(!3F""`)(!JF#"`%(!3F""`%(!3F""`%(!3F""`%(!3F""`%(!3F""`% +(!3F""`%(!`F""`%(!3F""`%(!3F""`%(!3F""`%(!`F""`%(!!F!"`!(!`F!"`! +(!!F!"`!(!!F""`!(!!F!"`!(!!F$"`!(!!F$"`!(!!F$"`)(!3F""`!(!!F""`! +(!!F!"`!(!!F!"`)(!`F$"`)(!!F!"`!(!`F!"`!(!!F$"`!(!3F""`%(!3F""`% +(!3F!"`%(!3F""`%(!3F""`%(!3F""`%(!JF#"`-(!!F""`%(!3F""`-(!3F!"`! +(!3F!!3!(rrm!!"%Q!!%!N!-F!*!$6J#3"2rr!*!3J%!!N!9!!%!!N!P)!*!$5!# +3"3J!!3!)!*!'%%i!N!3%N!-&"*!,"C!%!J%#!33&"33%"3F4%JS&N!`#!J@3!`F +("!N("3H3"!8&"*!%"`8&"*!,"C!$"*!$!3)""3F,%483"`@3$3)#"38(N!-%#3F +&"3F*"`8&"*!%"353%`8&#a%A%3X("C!)"!@3"!3&!3)&"!3'N!-*"`F&"3F*"`8 +&"*!%"`53"`8%N!8&"j!$#a%@&a)2#33&N!B%"38%"33&"33%"33&"!3'"!H3"!8 +&"!X,"`8&"!3&"`F&N!-(#JX4$!X,$4%5%KFB'4N5#J3%"`N("C!'"!8%N!J&"*! +H3!`@3!`B($3N&"`8("a%A&K3B&"L3"4Q3""J8%K%*"`8%"!8,"`@3"!53#`8 +&"!F%"!N(N!-&N!3%"`X0%!d4%4-3#j!%#T!%#a!,N!-+"`F,#`F("33%"JN("C! +%"!%%"!8%!33%"3F(#3F%"j!&"33&N!-%"!X0$!X,"JF("353"!%""j!*#`H3!`3 +'"!N("C!&"!3(#3N("`N("`3("`3*"j!&"!8&"`D3"33%#`F("C!&"!%(N!N*"j! +(#`F&N!3,N!-("33&N!-%"38("!N(N!B&"`F'#3N'"*!$"j!$"C!%"!3"#3H3#!B +*"j!'#3F&"!8,#`F&N!B%N!-&"`3*"j!*"T!$"353!`F*"`F&"33&"!3*"j!3"3X +(#a!,"`8%"353"J@3!`3,"j!$"JH3#!8%N!3*"j!$"C!$"!3,"j!*"!H3"J8,%!X +("`8&"*!-#`H3#J8&"!3&"!3(N!-&N!-%"!F*"j!*"!H3"J3'#`F("C!$"*!("`5 +3!`B,"j!+"C!$"*!%"j!%"C!$"!X(N!S%"`B(N!3%"JX("`8&"*!)"C!%#`N(N!S +&N!3%N!3(N!-&N!-%#`H3#33%"`B(N!3%"!X(N!-&"*!)"`8%"!H3#`@3"!F%N!3 +'"C!&"`X(N!N%"!B'#3F("J3%#`F("C!$"*!("`8&"!H3#33(N!-&"3F&"353"!@ +3!`30#3H3#!8%"!B,#`F("*!$#`F&N!3%N!F("`3%"j!%"3H3"38("`@3"353"!8 +&"!X,"j!*"T!%#3d0#3B%"JX&N!8%N!8"!33("38(N!3&"`F&N!8("33&N!-%N!3 +&"3B0#3H3"`B%"T!$"`Q3"!X*#3F&N!8%N!8"!33*"38(N!-&N!i%"!%%"!8%#3X +(N!F'"!3'#!B,#3N(N!33$3F&N!-%"33&"!3"N!-("`8(N!-&N!3%"C!&"*!$"35 +3"3F'#`F*"`N(N!3'"!D3!`X,#3H3"!B*%!X("C!$"*!%!C!%"`X&"!F("C!%"!3 +&N!F%N!8!"JX*N!3(N!B%"`B,#`N(N!F0#`X*"`8%N!8"N!-%$38&"`F&N!X%N!3 +&"!3'$3d,#C!$"j!&"JB*"JX*#3H3"JN,#3N0#3N("*!$!C!%""!("!F("C!+"*! +%"38(#`B($3X*"j!&"T!$#`B*#3F*"j!&"!X,"`N*#`d4%!X(N!-+#a)@"`8("`@ +3"33&N!-%N!3&"`X,"`8($3N*"j!$"T!$"`X*#3H3#3B0#3H3"!N,$C!$%!f3""% +0"!F("C!$"!8%"C!%"!F($3d*"j!$"3X,#3N("`N*#`X*N!-(N!S,#3H3$JX*"`F +&N!-%"33%"33&N!-4$3X(N!3&N!-0"`N*#`d,#`F*"j!+"JN,"j!+"38%"!8,#3H +3"3Q3"!X*#3X1#3H3"3@3!`3,N!--#`N*"`N(N!S&"!X*"j!'"3F&N!8("`X*"C! +$"`F&"`X,$C!%#`H3"3@3!`3%"`d,$!N*"j!0"3B,"j!("C!'"`F*"j!$"C!%#a% +0#`N(N!J&"353"3d,#`N(N!J&N!F'$3H3!`8("`8("C!&"j!$"3F("38(%"8,"j! +)"C!%"!3("!3'#`X*#3F("38("C!'"!@3!`F,"j!$"C!$"`F&N!3%"j!$"38,"3X +5%`X(N!J&N!8%"!N,#`B'#`H3!`8&"!8%"C!%"*!$"38(#3H3"!8(N!-&"33&"!F +("C!$#`d9%3X(N!N&N!-%N!-(#3H3"!X(N!-&"!8%N!J&N!-*"j!'"38("38%N!- +(N!-&"3F,#3H3#3@3!`53!`H3!`8%"!F%#3N,"`53"J8&"*!$"38%#3H3!`@3"`5 +3"!8*"`F&"`B*"j!("C!%"*!$"`F&"33&"3F("JN("`8&"!3("C!$"*!$"38%#`H +3!`@3"`53"3X("`B%"JN'"j!%"C!("!F'"38%N!3'"!B'#3F("38("C!%"*!&"JX +(N!-&"3H3"!8%N!3,$3F'"!3'#`B%"j!%"38%N!-'N!3&"33&"!3("!3'"JH3$3X +(N!F*#`N'"J3%"`X,"JB%N!-'#3B%"``*"`8%N!3'"`F&N!F(N!3*#3H3"`8("C! +$#3H3#JQ3!`X,"T!$"*!$"J3'!`3($3d("*!$!3%'"`@3#!3(N!F&N!-(N!-&N!3 +,"j!,#`X*"!3("`53!`F%"!B%"`N*"`53!`%%#3@3"!H3!`8%"!H3"`8&"j!%"C! +%#3H3$38%"33(N!-%"`3%"JB(N!-'N!-%N!-&N!3(N!3&"!3(N!F&N!N%"JF("C! +$"j!%"C!&"!3&"3N("JF&"!3(N!3'N!8("C!&"j!$"33%"JH3"3@3#J3'"`@3"!F +("C!("*!$"33(#3F*N!-("`B%N!-'"!B("`@3"!H3!`8&"!B(N!8&N!S%"JN&N!i +%N!J&N!-("3F("*!%#`F&N!B("38%"JH3"3@3#`3,"`@3$J53"3@3"33&"`F%N!- +'"!F("C!)"!B(N!3&N!`%#`F&N!3(N!-&N!3%N!-&"*!&"C!+"`3&N!-("`8("C! +$"*!$"j!%"3F&"3F("C!&"!3,"`@3"!H3"!@3"J53!`8%"C!'"!8%"3F%"!@3!`3 +(N!3&"`3%"JN(N!-&N!3("`@3"33%#`N("C!$"j!%"C!&"*!("33&"*!*"38%N!- +(#`F'N!3*#3H3!`8%"j!$"3F&N!3%"!N*"`8("C!)"*!&"33&N!3%N!3&"!3&N!N +("`N'"!B,"j!)"C!&"*!%#3N'"3F&N!F%N!B&N!8%N!J&N!S("`N*#`X(#3H3"38 +("C!&"*!$"`X%"C!&"!8&"*!'"C!%"*!+"C!&"`@3#!3%"J3%"JB(N!8&N!B%"!d +%N!J&N!-%N!3&"353#!F*#`N("`@3!`F&N!J%N!F(N!B&N!8%"!X'"*!%"`X,#38 +&"*!&"353"`F*"j!$#3F&"3H3!`@3#!3%"38%"JB(N!3&"j!$"353!`F0"`F,N!- +(N!-&N!3%"C!&"*!H3"38("`8&"j!*"38%"!F%"!H3"J3(N!3%"a!0#`X(N!B +&"`53"!8%"C!$"*!$"`F*"`F&N!N%"!8%"!F&"!8("C!$"j!&"C!$"!3("`N*"`B +'"`3'"j!&"JB%N!-&N!3%"!H3"J@3"!3%"38%"38%"!8&"`F*"`@3#33&"!8%"!H +3!`B%"JB%"JH3!`N*"JB,#3N(N!8,"j!'"C!$"!@3#`F,"`@3$J3&"3F%N!-*"JF +,"j!%#3X,"`N*"j!$#`X(N!B&N!i(N!3&N"%%N!-'#j!$"`8(N!B&"`F&"3H3!`B +(N!-'"J@3$!3("!8&"`@3#!%&N!-%"C!%"!3'N!-%"C!$"!3'"J8&"!H3"`B("33 +*"J@3#J)&"!3#!J8("C!+"!@3"!)#"!B*"JB&N!-""!3""!8&"!H3#!8&"`X&N!X +""38#"3)#"`F&N!-%N!B&N!3"!3)&#`B("`@3"!53"!@3!`H3#!8(#3F&N!S%"C! +$!T!%"3F&N!-""*!&"`8%"3)#!3F,"C!*!38#"38(N!J*#3F&N!8%"!8&"!8&"*! +$!38&!J%("`8"N!-%"!@3"J)"!JX("C!+!J%&"3H3"`N(N!-&N!3""!3&"353"`8 +%!J%%"`8#!C!$"!3&"33%"3%&#K%&N!`#"C!$"j!$"!F*"j!%"C!%"!#3"aN!!2q +3"*QC!!(rN!4QCJ!#rj!%-c-!!rrrc-bCQ3!%rrr-c'CQ!!Arrmc--c-!"[rrQCP +QCJ!(rrqCQ6-c!!MrrfD3"!!*rrpQCM-c!!V-c*QC-c-!#mc-CQBc-`!-c-aQCJ# +3!`h-c$13"!!1c-`c-`#3!`qCQ@CQ-c-!%*QC-j!%!"'CQ6-c!*!$%QCQ-c-!N!- +6CQB!N!88-c-!N!89GhF!N!8@998!N!8A4%3!N!8B)L)!N!8C%4%!N!B)[J!"!*! +$(!#3!di!N!6rr`#3%)!J!*!&3!"!!*!*5!#3!dJ!N!8%!!%!"!#3"JK1!*!%E-C +Xc-c'Gfc-c'c'TRT'c-E-aQE-c+CR%A4(DQc-E-aQUNGQChHU4Xc'c-c'aUE-N!2 +'E-c-5RUUGUCk4QCXE(DNCXCN&X4-c*!$aNChT-E'c-aXE-4QGk&(4NC-afTXT+T +RDNT-a(E-N!4UV'DQc-CXc-aQaRTkGNCfGdCQc'GQCdG+UNG'aXc'aQI-ChCQc*! +%C+Ch4NCf&QCdCXaUc%DQ4'4'4QGXc-aQI'aRc'aQCRTRC(4RDKCfUQ4QCmaQ4+H +U&NCR4(CQc+GQaRE'c'TdCU5K4dGRCdGQG'TfE-c'UNTf5RC'5QE'GkGXCmE-CU6 +-HRTf4(S@G%HQ4kCQc-aXC"URCRCk3@c'HQc-CXb3"'G'4RB@C%CQC'Td4-b3"'H +UCRGQGQChaQCQC'a%CRDNCfS@Gk4N4NCf4XCXc*!%TR4'GNGXCmaXCfc'CQE+CdG +RCdGhGRGfC'6-aXb3!m6'ThCk4UahChc*aRafaN&kG(SATf4R4fD3"-b3"-I-akG +fGfGhc%GNCkCmCXCfTNCNC'GKC'CUC-b3"XCmGj!$CQCR6%V+4'I'4NGfCkCfS@G +UGT!$E-b3"'c'I+CQGk4dG%4d5RCQDXCfCQChGRCdGQHNCQc-N!4Xc(c"GhDQ4RB +@C'4'GdTfCQI+TRbKCd4kaQE-E-b3!dE"c'E'CRGU&RHNC%CRTdaR5Q4fGQG%CQC +%E-E-CXc'CRc'N!2%GfGR4hGfGQ4NI'bKa%HKCaC%C'c-c-E-I-CXaXaXChGdHKH +RC'GNC(E'E-a(B@C%CXGhc-c'GhE(Cmb3"AGf4Q4NChThCf4fGQV-ChT'4'aNE-c +%GhCfE-I-c-E-a'CRTRDRC'5NE%G-b'CQGhGRHQV'c-CRN!0%E-E-N!0XaQChGK5 +NUNG(CfG#I(V'4R4mE-CXI'Gda%V-I-b3!f6(CRCdTkDUGN4k&R4'&TGXGm&Uc-E +(FDaQUXb3"FI(4'Tm3@4f4RB@C%CU"T4QDNHXE-aQ5Nc+'XD3!mb3!fCXaXI%DQD +NTRHNC'GX&mUKG(E'E%akUQ'NE-b3"@ahCdV%CfahCQCdE'I(N!#N5N4Qc'c'c%' +K&Qc-c'E-c-E'4%GNG(CNCRGhc(Cf&KP+T'CXE-c'a"&Qc-c'CXc-E'CNT(4NGR4 +h4RCQCXI'G"CdI-c'E'E-a-b3!fE-N!6(4RC%aRDQT(GhTQE(DQ&"c'b3!mc-aXc +-E-CQE-c-E'I-ChGU`AHRGQ4fTU4'4%5Qc-E-CXCXE-aXE'4'c-c'C%SDG(&fGNC ++5UU3!k&+4d&XCXE-CXE-N!4UTmaXE'5N%8Tk38T+UU4"SDT+GfCNTQc-CQE-E-a +XCU'UGQc'C%T%C'T+HQTUUX4'4Xb3!fc*TXE'E(c'CQCf%4UNGXCRHUT%G+3@4dG +'aQI-N!2'aQbTE'c%c-E-E'CN%D&faXGd5QT"a%UU5USAE-c'aQE-N!1@E'I-c'a +XaXE"URE(E'G+UNTU5NUUUNI-CXCQaXaQE&PQGmaQCQGQCmG"GQCRT+5USD&+S8U +Ua'c-c(aXE-c-aaT+DUCQI(GQE('XE-ad4(&"4%T%T%4NaXc'aQCXN!3CN4UQ4mE +-aXCN4QbNG%TdE-E(5NUK5@aQaQaQCXc-aKT8NCSDE(aXN!9mBDT'aXaK4*!%GQC +NE'D3!fc-SA%4N!-Fc*!$aXCXCXCfV-E'bNT+DQTXE-aXN!6-CUT"4"%4('CXE'D +3!fGUa%V+CQUUT+T'Gmc'N!6-CQ`4&"4%%4GQCmI+bNUUCRUQc-c%UNC+4(4Qc'C +RCfE-aNT+4+UK&dGQN!0N&(TfBD(-E'aUT''THRaQE'GXafc'&+CU%4&"5RCfG(T +%F8TaS4UU&QURDNUKTQaQafCXc'`D4Q3D4+4hGXaRCkC(G%SD&"5R5RT"3DUNTQb +3"-CQUUUKUUUKCh('N!0RCQDRHN53!a4%3D&"T"&-E'E-akC"UUSDT%4fGfaRCfT ++&dT+4%5N4'UNS8UK34C'E-c'TK5NDUTQYQE'CRC'T+UK3A&"UT!$5U'K5U38UQC +%CQC(UU%AUN4kUQE-c'C%UR4h4(4%G%6%5N&"UUSDUUG+CN4(4(5N58CkI'aXTa& +"S@&kHNSDS8UKT*!$C'UN5RCa5RCRTkC+GNGQ4XCm`DT"4+5N4%TdT%T+DUTmCU' +3!a%4aQ4QaQI'Gfamc-c'TQGUHT!$UNUUT+HUSDc%UN4d5NV-SDUKV'D3"-c-C%4 +U4NUUUNUU&-E(ah4mc+GXbUUUaUSD3@E'UXamc'a+5Q4KG%T+UNT(E'c(C+c'N!5 +T%@GUT%CQCQQQCQCf38&%4NDUTU&"GXc'aQ3@c'c'CN5U%CURDUC'Ci&XC%U3!d5 +NTUDU'U&%GfE(a+E-aQC+DK3@4QS@E-aX4'aQT%TRTNTh4mC"3Ac(E-aXUXc-bRT +d4%UNG-6'CN&Qc'HUUNT'5N4fc-DNTXE-E-bUV-c-CQa+3Dc-5XaXa+4QDUU3!dC +K5Rc-CK%AaXCQCUSAaQCXaQT%4Xc'aXb3"'UUDN&kHUTmE-bR4XaXc'GfCQc-N!4 +Qc-c'c*!&aNTkGRGUG"E-CNCXN!0QUUT"GXE'c-bRE-aXI-c-CXc'F84aGd5UNfa +Q4Qc'DT!$GQT%c*!&CXE+aXCQCXaQG'4fI-4Q4'5UI-4Q5UUU3A&a4QE'c!#3"`` +!!*Q3"J!"CT!'!!*QCM-cCQB!!c-cCT!%!!3c-fCQ-c-!"613"'CQ!!BcN!B!"`! +!998!N!-)UT!'!!PhN!B!#P@3"J!,4*!'!!`LN!B!!"%@!!%!N!-F!*!$6J#3"2r +r!*!3J%!!N!9!!%!!N!P)!*!$5!#3"3J!!3!)!*!'%%i!N!36"a)*"`S5%`J#!`) +4%K86#3B(!`)$!K)4!`F5!`)4!`B#!K!#!`8#N!3"!T!$"J-3!`8'N!-#!K!$!K% +$"K)(%a-)%JB#!K!4%JN)%JJ("`B'!K!#%`)#%3F#%3-'%JF5%JF'!K%3!a!$%3- +#N!84!J-(!J-4"K%(%JB(%`S6%J+3!`-4!`B(%4%6%4)4!`B(%a-'!J-'"J)(%3B +(!`-'%JJ9#48(%j!$"K)5%"!("JN6"K%'"a-(#3B*N!-#!`)#%J-3!J)(!`)(!J) +$!T!$%K)(!`B'"`-4!J-#!a%#!J-#%3F$%a3(N!-*"a)("K)("a-("a13!a)+"a% +$"a%#"J)#%3B5!`)'!K!#!3)'%JF'!`8'"`B#!a!'"a!#!K-8&3F$!JB#!K%$!K! +$%JJ(%K)#"J-*"a)#!K%(%4!'%JB'%K-(%K-6"J-$%JB6#3N)%K%%%JJ$"JF6N!- +(%a3*#3B$%a-(%JB#"JB$!J-#%`F'!a%(%3-4!`)3!J)(!J)$"JF(%JF$"JF'#K- +0#JJ5"`-4%a)4!K)*#K-'%K-*#3F(&!F6"`B3!JB#"a)4#4)3!J-5#!B(!JB$%T! +$!`B6"JF'"K-#"a%'%T!$"`-5"`-4"`F4!`S6%J-#%3J6#K%$"J)$"J)$"`d6#4) +#"a-$!`B4!K%(%3B$%4)(%a%("JF'"`J(%K)(N!-'"`-'%3)'"`N'"`F6"JF4%`J +("a%#N!-'%`B'%K-5"`F*$480%`S6"`d("J-4%4-6#!)'"`F*&!N5"J-'"K)(!K% +#"JF5%JF$"K)6"`-4"`F5%3)'!`B*%`F$"a-6#3B5"`-5!`F5"a30&4-(N!-+%J) +4"a)$"J)#"`B(!J-$%!-("K)("J-4%3-'%J)("T!$!J)'%4%(!`-5#K-*"`)(%J) +4!K!#!K%$!J-#%JB*!`)$"J-#"`-'"K!#!K!$"3-5%4-(%3-(%JH3!`)$%K)("`B +'!JF(#3N@"a%#!J%5!J-'%J+3!`-'"a'3!`-'!`)4!K%5%`B(%JF5N!-("K)(%JF +5!J-'%3)4"K)5%`F5!`B'#"-0#4)+%a%'!`B(!`)4%3)3%a)6#JB5!`F'"`B5"JJ +5"`N6N!-'"`F5"a)(#4-*"a)(%3)$!`)$!J-$!K%#!JF6"a)("J)$!JB$%3-#"JF +$%`F'"a)(%JF5"JS'!a%("K-)"`F$!K%#"K)$%JF#!`)$%3)'%j!$!JB#!K!#!a- +4!a%#%3)4!J-#%3F'%JF'"`B'#")(%J+3!a-#%3F(%j!$%3B4!a)$%JF#%3B#%3) +4%3B6#K-'!K)5%`B#!`B$"`F'!K%#!`B5"`N'"JF5&3d8"a-5!`)3%K)(!`B'%JF +(!T!%!`B(%`B5!`)$%JF5%3F(%`F(#3N(!`)5%K-5%J)5%JB$"J)("J-#"a-("JN +$%JF5"`B8#")'!J)(!a)5%!)5"JF5!`+3!`B'%J-#!`F6%`)3!J)$!K%5"`N+#3B +'"`F$"J)5"JF4!a)$!JB#!`F'!a%(%JF6"`N*%JB$!K)("JB6!K)'!`B(N!-5%a- +5!`)3!J-5!4!#!K%5!`-'#JB5"a%(%JF'%3B#%4-("K)'"3-'N!-5!J-#!`B("a) +$%K-("JF'!K)6%K-5%JF'"`B4!`F5%3B(%K38"a%5"`B$!J-4!`-'!JF'"`B$!J- +("JF'!T!%%!)3"`-'%3)(%`F5!`B#%a-("a-)"`B#!a%$%K)'"`B$%JF(!`S6%J- +#%3)$#3F'!`)$!`B(%4)4!J-2!K%$"J-#!`)3!`)$%JB$"J)'%JJ("JF'%3-4"a- +(%JF(%J)'"a+3!a-'!JB#!JB$"`8'%3F5%K-(!`B("J)#"K)(%`F5"K)5"`B#"JJ +6%`B'"a%#!a)6""%4"T!$%JF5"a%)#4-(%a-(!a)#%3-6#3N6"`B(%3F("JB("J- +5#3F)#3F'%JF+#4-(#3S6"`-("`B6%JN+"J8$"j!%%a-5!`B("a-9#3F'!T!$%4- +(%a)(%JJ'"K%$%4))"K-("K-5"`B(!`B'"`-6#3N'!JB'!a))"a-'!a+3!a36%JF +#!`F5%`N(%J)'!`B'%JS6"a)#!J-#!JB(%JF6%3F(!`F$!K%$%3F(%JF5%`J(!a% +$%3F("JB(!a%(!a%$%3N6%K-'"`B'!J-$%3)'"a36"a)$%3)'"K-+%`F5"`-'!a% +3!JB#"JB5%JF5"a)5%4%#!K%5"a%$!3+3!`F(%`F$!J)$%JF$"JF5"`-'!`B#!a! +("JF5#JN8%`F5%a)'"a%#!K)(%JH3!`B'!J-'!J-#"K-(%J)(%3)5N!-'%J)3!J) +'%JF'"JF*"a)&"a%5"a%6"`-'N!-)#3F'"`B#"J-#%JN8%a-(%JB$!J-3!T!$!a% +$"J-5%`F5#K)(%J-#"J)5"`B#!K)6"JB#%J-(#3F5"`)5!`F'"`)3!a)$"`B#%JJ +6"`)$!`B'!`B'!`F'%3F("JF(%JB#!j!$%J-'%J-5%J-#%4-(!`)#%!-'%`)'"a) +5"K%'!`)3!K%4%JF'"a)4!`)'"JF$!J)4"a-'N!-5%JF'"J+3!a%&"J)#"3-4"`B +#"JF(%K%'!JB("JF5"K)$"JH3!a)5&"3,"a%$!3)$"a%4!T!$%3B'"a)("`J("JF +5!`B#!`B'!J-'%K-(%a)'"a-5%J-#%3-9%JF$"JN(!JF'%`N,%a)("JF6"`B("K% +#!`)$%!-'"a-5%`D3!`-'%4%$!a)6"a)6"`F6"a-("a)(&!F'!JB6"`B$!JB$!K) +'"J-4!J)'"`B5!J-'%JF("K%'%`F*"a)(&3J4!J-("J)$"4%(!J-6"`B5!`F5%J- +'"J)4!JN)"`)4!K)("J-#!J-(%4%(%J-'!J%4"a)6%JF(%JF6#4-'#!F#!K%("J) +$!JB(!`B(!`-("JB5!T!$%3-3!K)(%4-)%a)5!J%#!a)(%3F6N!-(!`)(%a)(!J- +5%`S*#JB("`B3%!)4!`F'%4%("K)(%K-5%J-(!K%4!J-#"K-(!a-#"`-("`B#%4% +'!`B'"a%$!K!#"K%$"T!$!`)3!a!(#!B'!J-)%K)'!`F'"`J(%a-(%`F4!`%$"JF +5%`F#%"%#!`)"%3)#%`J("a)(#a-$!J)3!K)(%JF'"a%("J)4%J)'"`D3!`F5!JB +'#"-'%K-(%JB#%4%("a)'"a)#"J)#%"!$"J)3!`B##3N+%`F#%")6%JJ5"`N(%JB +(%JF9#K-6#!B("a)("K)6"`N'"`S6%JF'!JF(%K-(#!B#"J)$%JF6%3F5&K8("JB +5N!-$"JF4!`F9#!N'!`)$"JJ8%JS6"`B$!J-$"a%$%3B$"a)6%JF$!a-5%JF'%J) +$%JB$%K)@&3X6%`F4"K3+%`-#!JF'"JF5"`B("J)4%4-6!`B'!`-#%4%'"J)$%`F +(%JB'!J)$%3F'!`F$"J)$%J)(#3S+#3)(%3F5!`)'N!-4"JB"!J)'%a))!`)$"a8 ++%JF6"`B#%3F5"a)'%JB5"JF("K)5"a)'!`)4!J-6$"-*%T!$%`B6"J-4N!-+%J3 +#N!-$%K-("`B("3)3%4-("K)5"`D3!`F5"a)("a-("a-("J-#"K!#N!-5"K-0#") +("a-)%JF#%3-8"`F5"a%5%`B'%JF5#3N(%J-3"`F5"`B$!K!#!K%$%JB(%3-#%a) +)%JF'"a)(%JB'!a%6"JF'"J-(%3B$!`)'!J)3"`-'!`)##!F4!`-#%3-#!JB'#") +'!T!$!a!#N!-$%JB6#4)(%a)(&!F5!a%$"a-(#!F$!J)5%JB%!K)(%J)3!J-#N!- +3%J-3!a%#%!-#%!-4%`B#!K!'%J)#%!-4"`-(%JF$"K%$"J-#!J-#"K)4!`8$!K% +'"`F5%")5"`B$"a-5%K!5"`-#N!-5%`S5!`8(&3S("J-'"a)$%K%("K%4"`)$"J- +#!J-#%"!#"JN("`N5!K%(%K)#!`B(!`B#"K-6!`F("T!%%JF6%K)#"JJ0#3B$"a- +)%J8$"K)*#JF)"K)'"a%#"K!#%3F*$3F5#3B#"K-(#3F*%`F'%3)$%JF#"a-5%JF +#"a)9&3F#!3F6%JF$%4!#!J-#"J)("a-'%JF)%`)#%JF(%K)(%J)$"J)#"JF5"a) +'"`)$!JB$%a-(%K-#!a%(&4-,%`F'"JS6"J)#%!F'%K)#!K))&43'"`D3!`)$%K- +5"JB)!`)4!J)3"JB5!`B$"J)4%3-4"3-#%3B$%K%("JJ6%JB'!`F5"a-6%J-#!a% +("JN(&!N6"a)("J)'%3F5!`)4!K!(!`B4!`F$"`F*%JF'"a%5"a-6#!B(!J)(%`N +(%3)#!`8$!a!#N!34"a%$"K)("J)"%!)#!`-#"a)$!J-#!`B3!K%#"a)(%K)#"`B +(%`F6%JF'!JF5%JJ*N!-'"J)(%3)'%4%$%!-4%JJ5!T!$%3)$!`B#%3+3!a3*%`F +3%3F4!a-(%K-5"a)$"J-#!`)5!JF'!a-8"JF5%J-#!J-'"J-("JF(%K-("JF'!K) +5%`N5"a)4"a+3!a3(!J%#"a%'"a-6%JS5"a)4"a)'"J-#!`B'!K-)%3B("`B("K% +$!J-'"a)(%`F'"JJ6"J-'"`N)%a%$!K)(#3N+"J)#%3)2!a%4!a)("K)("a%'!JF +'%`B$!JB("J)$%!)#"J-(%`S6%JB$%JB$"JF6"`F6"a)(!`)#"JJ5%JF'%J)'%`B +5&3S(#T!$%J)$%K)*"a)(%4)#"`B(!`%"!J)6%JB'"a)5"J)("JF'"a-(%J)$"K) ++&K-'"J)5"`J(%J-5%J)$#K38&3N5%`-6#4-)"a%("K-#"JB(%`F#%a-9&!F(%JF +5!JB(%`N(%JB$"J-5%`F6%`)$%3F5%JF'%`F$!a%(%a)$"`B$%J-'%3F#!J%#%!) +#!`F'%`-#%Jd,#4-6#3S6"J-#!a%$%3-4"a-5%`F6#"%#N!-$#4)6"`)3%3)$"K) +(%JB#N!-4"`F$%!)!%J)$%3)$"a%#%!X6%JB(!a)3!J)3!JB'!T!$"JF$!J-'"JF +$"T!$%`F(%a)6%`F*#4-+"a%5!K)'!JB$%4)5$3S6&"%5!a%$%K81%3B#!J-#!J% +"!J-5"`B5#3F'!K%'!`F4!J)$%JS*&!-(%3-5"a)6"a!#!K!#!`N("JF'!`)"!J% +#%3F5"a-(%!-("J-"N!-#%JJ8%JB(#4-*"JF5!`F5!`B'%JF5!`)'!a%(%`)'!K! +#%3-5%K-("JB$!J)4!J-4"`-'!a-("J)#!3+3!`B(%`N6"`F5#!F(!a%5"a3*"K% +4!a%5!K%$!J)6"`)$!K!#%JF0&`J6"J)("JB*!`B#"JN+$489%`-5%JF4%3-5&!S +6%a)6"a)4!`B("a-6"`)("a)+%a)(%4%'"K!3%4)5#"-6#K%(#3F)%J)'!JB+&!d +0#3B'"`F4#3F6%JF6%`F$!J)$!`B'"`N(%K%$!K!#!`B(%JF5!K)("`)#!`#3"aF +!!2rrc*!%!!(-N!B!!Xb3"*QC!!2-c*Q3"!!%c-bCQ@CQ!!@CQFc-QCN!"TQ3"J! +(QC!%CQB!#*QCCT!%!!PQN!B!#QD3"$-c!!YQCM13"!!--c0QCM-c!!dcN!B!$J! +!)L)!N!-2hC!'!"#lN!B!%DU3"J!5L*!'!"0hN!B!&&@3"J!94*!'!"BLN!B!&a' +3"J!!#)B!!3#3!a`!N!01!*!%rrm!N"#!)!#3"8!!3!#3#8J!N!0)!*!&"!!"!!3 +!N!B)6J#3"%4!0%3c-%0$4$-c3c4%!$-`-%4%-c3d-d%d4%3d-$13!d0$-$-d&%4 +%-j!$4$-$4!"%&$3c3d%4%c-d-$-c3c4%-c-$-d4%3d-!-!4%-$-c0$&%-d4"%84 +%-c3cN!4"3c0$3c0$-c3d-c0$0#-cN!0"0$4%384$-d4$-c-d4"4%-6"!4$!`3d- +`-c!`-c0!-a4%3d4%0$4%3d-$0%4%3c-c!c383d-c-`!c0$4%3d3d0$13!d-c!d3 +`!c4$4$-d-d4$4"-c-$-`!$3d383c-d4%0$3c-`-d3c-%4$!80%-c3c0$!c-d0$- +$3d53!c"$4%4$3c0$-d3c-d0%0$4$-`-c3d-d-$0$3$53!a-c!d0%4$4$!c!d0$1 +3"8%$!c13"%0%-d4$384"-c0$0$3c4%3$0%3cN!3d4%-`4$"%-c0%-d4%-$4%4$4 +"-a0%4$3cN!0%-c!c-a3c3c-a-d4$3d3c"$-c&%3dN!-c0%-84$-c3d-$0%3c4*! +$-a3c4$4$-c0$-c-80"4$0$4%4$0%0$0%-!3c3d-44%0%4$4%0%4%3c3c0%3c3$0 +%3d4$4"4%3d-$4$0%3a4%0%4%3d0"-c-`4$-c3c3$"%3c3d0"4%4$!$0$0%3c3d- +d3d&%-c3d4$-`!c0%3d0%3c4$3d4$3`3c-a3c4%4"%838&%-c&$3d!$-c"$0$3d4 +$4$3c4%-`4$0%4$4"-49"0%4"0%3c4$13"%-c0$4"3a-$-d-d0%!`-d-c4%380$- +84%3c3`-d!c3c3%13!d4%-$-a0%&$-`4$3$4%4%0%0%4"-c0$4%3c-`-$4!0%0$- +$343c3d0%0!-d4$-44%%d4$-c-%0%N!0$-%0$0%0$!c4$&$4$-c-d0$-d4*!%3d3 +d0%4"4%-c-d4%3838&%0%N!0$4$3c-d53""&%4%0%4%-c%84$-c"%3d4%&%3d-d3 +63c4%0$4!0%4%%8-84*!$0$383c!c-"3d4%4"3d4%38&%0!4%4$380%343d&$4%3 +`0%4$-j!$3d-84"%c4$0%4$3c3d-d4%0"384$&"4%N!-8363c0%4%3d4$4$0$3d3 +4-c3d-d3d3a384*!$0$4%-a4"-`"%4%0%-d4$3d3d%83c4%0%36-c4%0%0%-d4%3 +c&%0!-%3d-d-c0%-d4"4%3`-c0%36-d&%0%4%0%4$4$4$0$!c4$0$3`-$4$4%&"3 +cN!0%4$-d4%0%4$4$&%53!c3c!d53!c-d3d%d!d-c-$-c0"3c3c4$0%4%0%4%3d3 +c4%3d-c3c3d9%4%13!d!!4$0"3d3$4$-c3c!d-d%c4%-c!c0%0%-d4%0%0%-d!c0 +%-d-$3`0%4$3`0%0"&$0%3c-d0$0%3c4"3a4$-d-%-d-c0$3c0%3c4$4%4%%63d3 +cN!-`-d-c&%&$0$4$0%4%3c0$-j!$3c-c4%38%63d4$-c0$-$4$4%4%0$4%0%4%- +c0$0$-j!%4%0"&%0%4"&$-d3c3a-c&$-d3c-$3d-c-d4%0%4$4$4%-43d4"&"363 +c0%4$3d%d4%-c-d53!c-c4%0%3d0%4%0"4*!$38383c0%0$0"4$-d0%-c0$0$-c4 +$4$3c"$0%-d3d4%36360%N!0$-d&"3d4$-d4%-c-%0$0$3c"%0%4$4$4%N!Bc-c" +%3c-c4$3d-%3$-d3c4%%c0%3dN!0$4%-d4$3c3`3c0$4%-c4$0%3d0!4$3d4%&$3 +c4%3c4*!$0$3c0$!c4$3c3c!d3`0%-c0%-c0$3d%d0%0%0$36-d4$4*!%0$0$N!- +c4%!$0$3%4$0%-c0%%8-`&%0"-%%d-d4%&%3c4$!d3`4$!d4%0$4$-c4%-83d0$- +40%-c4$0$&%4%0%4$N!3c3c4$3c%c3c4$4*!$36-d&%4$0"-c3d4%0%%$3`-c4%0 +"3c0"4$4%-c4$3c0$4$0"3d-c3c4%0"0%-6-!-d4%384$-d53!d0$-d-c-c3d3a0 +$3c3d4%0%0%0%0$4$0$-c0$-d&$3d-`0$-c!`0$3c4$3d-$0$-c0$4%3d4!0%3c3 +d-%3c4%-c4!-c-c53!c-dN!-$0$-d4*!$3d!c3c3d3c3d4%&%0%-c4$3c4$4$&%0 +%-`0%4%0%N!3c-c!a4$0%4"4%4$3`0%-c&%!8360$-c!d&%&%&$3cN!4%3c-d3d3 +dN!0$0"-d0%0%3d53"!4$4$4%N!0$4$-d-d4$-853!c4%3d3d3d4%-d4"-83d0%- +8384$0$3`0%13!c0"4$0%-d4%0$-c0%4%0"0$3c-d3c3c-d4$-c3c0$!c-d3d4%% +d4$-d3c3d3c!8-c3`-c%!4$13!c3c3$-d-c3c4%4$&%4%-c4%N!-c-d-d3c0$3c3 +6-c3d3c-d-d0%4%0%3d53"N0%3c-c3d4%0!-d-j!$0%4%0%-c3c!c-d%d0!4$"$- +d4$13"%3c3j!$&%3d4%4$-c"%-`!d0!4%4$0%-d0$4%-cN!-a3d4%3d4%-%4$N!- +c-d%c!%4%0%3d4"4%4%-d3c-d0%3$4%-c4$-%4*!$3d3d3`!c-d-d4$4"3d3d-%- +d-c3d4$4%-c0%3c4%4%0$4%0%3$4%N!0$4%3c&%-d3d0%3a4$4*!%3c0%4%"!-d3 +d4$"%4%0%N!0$0%4%-c0%0%38-d4$384$3a%8-$-c4%%8!%3d-d4$4%4$4$3c%a4 +%-d3c36&%0!-84$-c4$&%N!-!N!F&!!"QN!B!!613"J!#Gj!'!!09N!B!"%53"J! +&)T!'!!!)IJ!"!*!$(!#3!di!N!6rr`#3%)!J!*!&3!"!!*!*5!#3!dJ!N!8%!!% +!"!#3"JK1!*!%%c!#-`#3""-$!`)J-!!b!$#3!c)b-$!J)!#3!a!L)K!c!!"!!!! +`!!!J!J!!-!!K%J#3!b)5)J#3"#!#!!%3!%!`!$!%!!)`%#)J!$-!!J)J!`!")5% +!)3-#!L)!!!)J!3!c!J!`)!!4)!)$-$!!)*!$!L)4!3!5)J!L)J!J!")J%!!c!!) +!!!)5)c!c-#!$)3!L)K%J)J!!!5)!)3!#)"%!%!!`!J#3!b%"*!!!-J)"!J)!N!3 +J!J!b!c!#%#-J)5!!-3)!!$!`)#!!!#%`%K%K)J#3!a)L!!!J!#!#!L)4)L!!!J! +#!`!4)J!`)$!L)3!!!L!L%3)J!#-#)#-J%4%")!!3)J)J)"!3)!-#!L!")J-L!4! +K!K!!!#3%!J!K)4%!)!%5)J!`%3!#3$-!!M%K)"!!)J)5)!)!%J!J)#)K%!%J)T! +$)!)4)!!`!b!3)J!`!L)")L!!!!)J)J!!)K)5!!)$3J)J)!!5-!-b!L!L!!"#)3) +3%6!!%K!#!`!!)J!`!5)!)5!#-J-!-#!#-#%#!L)$)K)30!)#)#!c!b!L!#!L)!) +!)5!J!$)#)!)5%L!L%J!"!M)K!!)J!!0!)K%J!!)$!J%!)6)#!`!L)J%!!K)J!#% +!!L%!)!!"-a!#%L-J)J!!!`!"!`-#N!-!)J)K!3!!!L)5%#)5)3%5!L!L!3!`!!) +L)J#3!`)J)!!b)J!L)3)!-L#3!b)#%3-3!J"!!!!#)"!!3!3#!!!3!#)5!J%J!5% +L!#!#%J)L)!)!N!-")J!#N!-5!!)L!"%#!L)J!J)L)K)J%#)5)5!K%J%J%K%3-`! +!!K)$!L)#!5!#%!)4!J!#%K)J)!!!!L)5)M-5)L-c)!!3!$-`!#)!)!)L!L%3)J! +L!L!K%!%L)3!#!!)"!J)J!5!J!!)$%3)%%K-!%3!#!!!#)J!3!*!$-#!!%3!4)L) +J!J!!)4!`!L!!)L!4)!#3!a!"!#!J)M-#!#)5)4)L)#)J-!%K!#!$)`!!)#)#!!- +J!L!J)J)$!#!J!!)`)L)6)!)!!L)$)!)3)#)d!J!#!b%K!J!J!J%!!J)J!J)L)L- +d)`!K!5-6)!)L!L!!!!)L!6)4)!!M!!!J!"!#!K!#%b!J)L!K)L)M)b)J!L!!%5! +#N!-3-!%`!!-!%!!!%3%$!3!!-J)5)J)4)5!!!3!3%%)K%L!!N!B#!#%#!!!%)3) +!%L)K!5)J)!!K!J)!!"!!!!)!!J!!!j!$!L!K-#)5)L)4%L)J)!)M)4%5)b!!)J) +#-!!!!b-N)!-J-J)!-5!J%"%J)J%4!K!#%L%b-#!!)3!M"#)!!!)!!#%$)!!"!$) +L)5)!)5!3%#%L!M)#!J#3!b!J!$-#!#)b!5-M3#!L!K)4)T!%%4!!)!-!)$!!!`) +!!L"$)$)b!`)`)`)#)L%5!K!L)K%M)!)!)!)!!J-b-5!5!!&!)*!$!$#3!b!M)L! +4)K)#)K)!)#)c-3)J-`!c)J!3)$)!-J!#)`-`%b!!)5%c)L!L%#!#)J!$-!#3!c! +a!L!J%K)c!!!b!`-5!!)K!M-!%"!J3J!!)$3!N!-#!!)L%3)L)$!$!!!$!`!b%!) +!N!-L)J-#!J!#)J)#!!-!!L!!!5)J!`-`-$)`%J!L)J!c-#)J-d!!-!!!%5!!-`) +!!J)K%5)J!`%J!`!L!!)`"$"!)M!!!!)#!")K)J-J-`!#-J%5)L!!)$-!)#!!N!N +#)!!L!"%33L%#)J)!)K)`!J!4!!!5!J!J!`!!)!!!-J)J)!%!!K!#)J&!)5!L)M! +#)#)!!a!#!#!#-$)J)$0!)!)b)#!#)!!5!J!L%L%3!#)`)#!#)#)J)b!#!L)$"!) +b)L!L!*!$-#)#)!)#!!)!)4-J-c!#!!!`!L!`!$!d-!)$)#-J-!)!)3-!!!)`)J% +3)#-$"$-!-!)!N!3#!d0!)")J"$!c!*!$!L!J!#)J!J!b!$"$3!0#)$3!!!3d3`- +!%L!%!`3J!$-!!J"!!#!J!`!J)#)c!!!d!b-#!%!c)c)$!3!!%`!J!#)L!*!$-!- +`-#!L!b!!)!-!-!-!!`!#-!)")#%!)#)J)#)#!!!b-"!!%#!J!J-!-!!M!!)!3%! +#!")J%!-!!!)4)33#!b%#-M)M-#-$!$!c-`!`)*!$-!-#)!)#!#!!)5!J!`)$!5! +#-`)`3c!!!#-$!!!L!!!3)!!L)6)J!`%#)#-!!K3!!!-d-$-!-$!!!#33)J!a!#! +#!`!!!`-!!3*!!!)L!$!$!`!J!#!M-%!!N!-L!#)#)!)$!`!`!M-#)!#3!a%c"$! +%!b!!"!!`-#!!)$!M!J!5)`!!-$!L)!!"!!3J!#-`)!-`!J!!)J"!-L)!N!-$)#! +L!L!"-#*!-!0$!!)J-!)!-`!!!J!M-!!#!`#3"#)`)3!L)$!%!$!#%`!J!$!")!! +b!J!J)!!b)#)#)L!#%J!J!!!$-$!$!$!#!#!!!!%J)c!!N!-#)#-5*!)%%#)K!3! +J!c3$)#-!3`-!!#!#)!)`0$!L!J-!!J!L)!!$!K)#)L)`)`!J-!!!-c!J-#!#)%0 +!4$!!N!-M)*!$!!!M-J-b)J!$!!!#)J!`!j!$)$)c)`3!!b!!)%!L-M!L!J#3""! +J!!*!)"%"!!!d-J-#!!-!!$!!)#)!N!-c-!)#!$)#!`!J!$)#)b)!)!3!-#!!-c- +$-$-J!J%!-*!$!`-!3J-`)J#3!a!3)!!J!`#3"`3!!*Q3"J!"Zj!'!!+UN!B!!iL +3"J!%Gj!'!!!)PJ!"!*!$(!#3!di!N!6rr`#3%)!J!*!&3!"!!*!*5!#3!dJ!N!8 +%!!%!"!#3"JK1!*!%35)R)RBR&L%aFR)L*aG"&8F@%4)LF84(%4%5*b4%&4%Q%L* +eBRB8%LBK*fBQ%Q&b)@%4CfGfGK*fCAFR&P%4&@FQ*b4#*Q8QBKChGfB5*N8L%KB +LCb)5CfBACK*K)5&K*fBP9d*fCL4@*")A&a)N38&b&L)RFRFR)RGdB5&Q&b*Q%A) +4GR)RB8BP&eF5G(Bf!L%5Gb*L%@GK*bB4%KFR%5FK%LCh)KCQC"%Q)4CK3LCR)R% +Q*h*L)4&K)K*'Gb%4*K&K)@)Q&QB9&a*fCb35%d-4%R)RB5GK%@&a0N4b)L%L)K) +N3@&KBK%Q4K)ACL48&8CeFR*RFK349RB54"*LGf)bGRB8CaBK&b)b*R*LC@08-(C +5GbBA*K)9BQ%d&h)5&eC"%5)d)4%KCK*LBA)93"96GQ*L&aFL%A3LGP3R)LC5%L% +8%8F5BR*f*b*L*Q)RFL&Q&"*K*b94BL*hN!0Q)K)A*L*48L*LBLBQ)L*#&(Ca34& +N&a*L&K9$*b*h&hFL%4*bFKFPGb*LB4)QB5GRGK4K%RCh)K*K%8%LGbFL*R*RFKF +L)5BL*Q*LF4%84RBR%5Ca4hFK&a&PCb*QCRF5FQCKF@B@GLFQ)QF@34-@BL%9)A& +"&hCLB4%KFR)8*f89CL)L3NF4GfCQB484%4)Q*Q9K*%%5)L)K*'Gb)Q%R&")Q9(* +A)4%4*QCR%4&#FPGKCK*a%83A&a0N4b)N)K4%-5&K%N)K8K&LBQGa34&"&5&KCbC +'*NGhB54"*P3P99&#)Q*L)5GKB8*LCh)R)dC"FR)RGa%43RBQ%d&h&eB@99*a%53 +MGh%4&bFLF5)LC8%L&hGb%4%@)LGP3R*e&PC8Ch0"BQ84Ca&h*a&b%L)Q3A0%4hF +@&K4L*hH3!aGa3988)9-5GeBQ%L*&348KFRC#4&3@)K&4%b*b*h%R)LG84f*K4P% +QB@)N%@&"GL%4%RBN4"*fF4&#)LGbFL)R)QBLF4Gb)5)L*R3A)QCL%4&a)Q*K%K% +K&N*L)Q)LCQGL*L%L%C!$*R*b)5BR)5F@&K4b)A)44hB8FR&44'BAFRBLGh)4%@3 +5*L*K&LFLFL%5%R)L*"*A*bGR&&-@&d4LFLG"BKBK%4FQ9aF83")QCL*h*bFL&Q) +Q%KCA&44'&K*LF@3A)5%@&a8K)5)@3KB8)@%LF8*L*Q%@&h&&-@%Q*aCfCb)L)4* +#*MGK%4*LCQ95GRC"35*LB5)L%L%aFLBA)A&b&@Gh)4*5)4F44"GKCPFL*f&hFQB +4&R&Q%LBK*hF5GLBK&b&R)LB5*534FACKF@GfFK)LF9BN)4%QBKBL&h*fCK8LBRF +KCK)K3@Gd*f*9*fCKGb)KBL%K%54@*#CK&bGb*@)RFKCRBL*b)K34)P)QB9FR)Q% +A*hGa&8)R)Q)8*LFL)4GKGb)RC5*K*Q*K)Q)5%M4LF5Gd3R9N*h)QN!4KB5%5*hC +f)6F89f*4)@*hF5%M84FLGb-QFR*e99%LB5CRB4*f%NGN8K9#Gf&h*RF5%@BN*Q) +L4"C')K&84")MBQ%5!5)@)L)R%@)K%R%ABQFRCA9(*8-4FK4b&9*8B5%L*#CdF53 +5)4%R*h&")R&K*h&a)LFP-4Ca9#GQ9$33FLFR%f)5%L%@%4)RCd%5)AGa8KB9BL4 +%-435BK45&`%9BR*M4NGh)4%8%K)@44BA)L)5%4&R&&38FK%A)QCQF94#*9944a% +L&K%4CR34CaC@*R%4BLF5GK*LF@%A)R*f9R8!84F4)8)Q%5CbCR)Q)4G&4bGK%LB +L3R)4&&)LGQGb%e4aFAF4"'BRBA%L%5BK&e&KCa%@CK%@)4%84b*PGbG%4#*Q*#* +%4'F4)5CaCQBLF4*"FA35)4%KBA8@&RFQ&P8%Gf&R&R3"FLGb&hFK%K864#*Q)Q% +R%5*K%5%4GLB598B4)Q)4*f)R*LBLBLB54P0%GL*aF4-LFKB4N!-KB4%M4"%5BR% +54#GaB4G@GQ)L88!5GbC"Ch&QFL)4G#9"44%Q&h*bB4)8Cb&K&f4hCL)L85)KF8) +Q%RBKCh)8)K35FL)LFLBLF50L*QB4FQ)L4#*a&b*aCRCR%4BAF4!AFL*L%5&%4#8 +5FRGf%@*RChBN%8)ACf*b*KCbBL*aC%GbGLB4)83d%d&4*L*LFM%Q8K&#G(FQ*Q) +R)A&LCLC4!QGbGR)L384N4K*%*N&"&a%N&%*N3Q%QBQ)QC(FR)435)d)Q&Q*d%A% +5ChG"FL4bFQ)4Cf)@*L*4*%B93Q%Q&%)P"hCQCLFLC6)R)44"%6*b%N%hB@Ga!K3 +K%9%A&(BQCR*L"N)P84)N&KB4&%3N45FL-8CQCL9")5*P%@%LCK)R45%8&#C8%KG +QGK8434*"*')4BKCQ%NBL3A&8)LCQ&dF53Q*@3K-bGK*4GK9(-4BAB5GfCT!$*QC +LC%*K*QCA%L4(*QC%)R)@F63b*LBa%4)@B5CQCe%QBQBK)K&RBP*L*KGb)K-P"aF +4*4)5*b&%GQD3!f)KB4)QGf*b%5*LF5&K*RCR3"%Q%90P38GRBN*QCP*Q&fBL4LG +a)L*hGf)KCb)5CK%@Gf*R&L&#%83L*bCK*#)5B"*&B4&R)L)R*e&#Cb*(3K*#GK% +L%a)P%aF43L%8%K3K*4853QGa*hFQC4&N)Q&L&R*KF4BL*fBd&L8$8Q*c%!&f%a% +L)N)P3LCL)5C'F83%4#F8*Q%KF488C4&"*f493(*f)8%5G4*$%4&fCKC`4!#3"`F +!!2q3"J!"QC!'!!*QN!B!!pf3"J!%Zj!'!!@UN!B!"RH3"J!(4*!'!!!4$J!"!*! +$(!#3!di!N!6rr`#3%)"!!*!&3!"!!*!*5!#3!dJ!N!8)!!%!#!#3"K"1!*!%#43 +9&!Q3"43-#438%38&%4%&N!36&Im1r`lr$JN&%C!'"4)*$!i,$!@3!a)'%`N8#a8 +5"4-*N!F8#C!'&"@3!`8'%C!&"C!$%K)6"`N8ra8(""'3"!84"4)*&Im-$4%&N!- +5%`N9&3i*%K)*N!B8#C!(&!`1r`88%C!'"385%a)6#485"385%a'3"!8&%a39r`i +9%K%&"4)6&"6r&3N&%K-*N!89#3N8#C!%&!crra),%C!'"385%a-8&!@3"")*"4% +4"385#431$K81&"%4"3B*#3`2&"+3"!Q3"")8#C!(&"Arr`B8"4%&N!85%K-*#K% +&N!8'#4%&N!-6&"81#a8-%`88"4)*&3i9%T!$"JN6%`8&%K3*N!F9&Im,#43*%38 +5"4)5%`N8&384N!3&"4)*%J85#439$K8-$48("!N$"3S1&!@3!`B6&383%386&!N +*%a-*N!-9$!`8$!N&N!35%JN*&!`&%3-4%3@3""-8&"81&488&"8-&!B!%K!&&!i +&N!85&"3%"388&")6#3J*#439$Im9#4%&%JB6%a39#3F5%C!("3B6&3crN!-&"4) +'#43%""%$"438"3B&N!35&436#4Ar%JB*N!-8#C!$&3i&N!-5"K-(%`N9#`N4%"! +4N!-&N!-5&3i-rrm&"4)6#3N8"J-3"3N8%4-&N!3'#Im,#4Ar&!F*N!-(%J8&"K3 +5%385%JB5%JN8&Im8%3-4N!3&"3N9N!-1r`8&%K)(#3N'!a!X&%J@3"3N-"4% +*rj!$&3N5"K'3!`86#4%&%38&%K)6#46r$K33%"'3!`8@3"2m&N!-'%K)*&"! +$"431"C!$%3@3!`F8"4%*$[m-#3N&"4'3!`8'#4'3!`@3!a)5#439r``&%C!%"38 +*$"@3!`i5"385N!-6&!83"48,"4%&N!85#384#Im9%`B&"4%3%"%&"3S&%C!%"C! +$%JN8r`i0%4!4"386$!i8%aAr#38'"3B5%JN*%3B9&!83"C!&%K3&%3N8"33&&2m +&%"%3%388#4'3"!8&%JN*&"Ar&3N4%385&3i*"K)9ra35%J85"K)(&"%5#a8&%3@ +3!`B5#435"4-&%C!$%`i-"3-3"388ra)4N!-&N!-(#439$[m1%`8&"`N5"38($!i +9%`B&"4)'#3`&"`N5"K)&N!-5"JN8%J85#384""%8$"33%4%5&2m9"C!&%K-8&3m +9$[m*"385N!88$[m1&4)&"3B5#Im*"C!$%K-,#38%"`N8"C!%%a)4%3B,$4)4"3B +0r``8"C!%%JN8&C!$$!N4"C!%%T!%&2q3!a8&%385$434"C!%"JN9!33-#4%&N!3 +5#4%4"3N9$K-ǡ&38&#"3)%a-9&3N*&"%&%38&%K)'%K)*&Im-#4%4"385&"3 +&N!35#3X"""34%3@3"")*"4%4#3i-&4-8$JX&N!-'#K8&%C!$"3B5%"%&"4)'%T! +%%a6r$K34N!-&"3B9&!@3!a)6&")*"4%&N!B*"4%&&!`9&3d9ra)4"385#3N4N!3 +&%`83%3@3!a+3!`B6%a39&434%4!&N!-*$4)'%K)6&!S&%384"C!%%K-6%388$[m +9r``9%4%&"4)*#4%%%4%%3%4%&%T!&"K)8$`i*%C!$"385%K39%K)6#438%"% +&N!B5%`N4"KArN!-1$")4%!8&%JF*%C!$"3B8%C!$"385N!3'%JN*&3i6%4%$%4% +&%JN1&!B6#485%4%&N!85%JN*"46r$Jcrr`N3%4%&N!-*#K)4%3B+#a'3!`8&"K+ +3""-*&!i-"4'3"!8&%JN0&3J5#434N!-&N!85%JQ3!`lrN!--#4'3"!@3!`N8"`8 +5#3`-"4%4"385N!-6%JN*&2m9%3-4""'3!`86&C!$%K36%"%&N!85"3B8#439$[r +r%a!$""'3"!8*&485#48-#4'3!`8&%T!$%a)6#48-#4!4%384%38&%K39&43+%!3 +4"C!&%K)(&!84%Jcr&!85%K'3"!8+-#4)$#3N&%4%&"4)5%a)6#4Ar&")3%"' +3!`@3!`B8r`i-#4!$"C!(%K3*"386&2q3!a3*&!84%38ǩ%`8$"3S5%4%&"4) +5%a-*#Im1&4'3"`8&%K6r$K8*%"%&N!B5%`N4%386&3X8%J@3!`B8&!N5#489%J- +%"4)*%J8&%T!$%`N*&3i-&!-4N!B&"4)9&4-'#3N4%3@3"4)*"4%4%a6r#3@3"4) +5&"@3"!8#"4'3!`8'#488#4-6#481$[m8""!3%C!%"386&435N!-*#4%&N!3'#4- +4%4-9&"%4"C!&%JB(&"@3!`3$%4!3%4%&"K-8$"88&Im9#C!%%C!&"385#3i5"4) +5%a8)"38'%JN6%38*ra)3%3@3"a)5#"6r&433%!-3N!-4"38*&"81rrm9#3J(#3N +4N!8&%K3*"C!$%K)*&4)&"4-*"JN9ra3#%"%&N!-5"C!%%K)*&480%K%3N!84"38 +*&Iq3!``8#3N6#434N!-&"K3*"C!%%K)*ra85#435%`N*$!N#!a%&N!-5"4)&%T! +$%a39&3N4%3-3%4!4%38&&"Ar$J`9&43(%a6r&"-*&"-4%3@3"")6#`i8%a)&"4) +5&"3#%"%&N!J'%K)*&3lr"4'3"`85#46r$J`9N!-6%`N1rrm6%`N4%3@3"4-9&4) +5"C!%%K-8!K!3"C!'%T!@3!a3&%38&%C!$"3B*&3i-&C!$$JB6#Im8"385&!N +4"384"385&"35"4%&"4)&%JN4%"%4"C!&%J85N!-6&3`8#4)'%J8&%38&%K39r`i +-&48*%a-8#38&%K)6&"34"4%&"4)9%`84"385N!36#4!$%4%&N!B5%JB*$43&N!3 +5%K-&"4)*&2rr$J`8#")6%`N&"4+3!`J*&4-4"4%&%K35%4%&"4+3"!B8%K!4%3@ +3"4)5"K)*&4)4N!3%"38*&!N8$!lr&")'%a)6%K%&N!-5N!-6#484%38N&%4% +&"4)5"JB5#484%"%&%3@3""+3!`N9%K!4%"%$%!8&#Irr$J`1&!85N!-*"4%&N!- +5"K)6#436"38'&!8&%4%&"4+3!`B6#488!a%4"C!&%K)*&!i&%C!%%"!4%JIr$!i +9&3i*%K)6#4%4"C!$%T!$"JN8&3N5&384"384"385"K)6%JN9$")4N!3&%J85"a6 +rr`84%334%"!&"3F9r`i9#481%a)(%a!4"C!$%T!$"K-*$2m-$K%&N!-4"385N!- +6%JN9$K85%3@3"")6&2m1ra)4!a%3%"%&"3N9&3i*"JVrra38#4%&N!-5"4+3!a- +8r`lr$!N&%3@3""+3!a-6#43-&3`9%J8&%a-*#439$!N%%C!&"4)*&Im*"4)*&3l +rra33%3@3""+3!a-*$[m1&48&N!B5"K)5#3N9$K81rj!$&!@3!a)*&"31"4'3!`3 +4"4)+ra8&"4)*$"89$"83%3@3"4)5%`Rr$K3)#43&N!85%JF)#439$K8-$J`*"38 +4"3B*#439#4'3"!8'#489"4%&"JN9$K88r`84%38&%JB5%K-*&3i*%`N8%a%&%T! +%%`N*&"@3!rm1%`84%38'%K-*#4Ar"J@3!`B*$"34"!8&%JN1$!N6#`N4N!-&"4) +5%a-8$K85%K-*&!85%K-5%`Q3!a89$K89%K'3"!85N!-6#489&!B&"JN9&4)4%38 +&%K3-&")'&2m5%3@3!a)5%`N8&3N5N!-6#486%K13!a)'"4)8&!i&N!-4"C!$"K) +5%`N*"4%&%T!$"JN8$K)&"3Ir$385%JN9ra)4"385%JN*&43&%T!%#43-%a-8#4% +4"3B*$!d4%3@3"")'%JB6%K%4"3B&"4)'%`N8$4)5&2m*"C!$%``1$JN&"3B6#"8 +9"C!&"K)*&38&%K%&"JN8$2m8%"!4%38'%T!&%4%&N!35N!-'"JN9&3Rrr`@3!a) +5&!i9"`8&%a3-%K%4"4%&N!-'"a34"C!&%K)*&"33%4%&"4)5%a-'"4!4"4%&N!- +5N!-6%`RrN!35"C!$%K6r%K!4"4)*&"84N!-&N!35#43&%3@3"4)'%a3'%4%&%K- +*#4-'%4!4"C!'%JB5%JN9rrm1#3@3!a)*&!-3"C!$%JN8&"%4"4%&"3B(&"-&N!F +'%K-8#38&"JN8#3F6!a!3%3@3"K)'%JN9rj!$$!@3!`B8"4!3%38&"K-8&384"38 +4"4)6&!i&"4%4"C!$%J85%K8*"3B8&!N6%`)$%"%&N!-5"C!$%K)*&!i9%K-8&"- +5#3-3%4%&"4)(#438%3@3"!B*$!`5"C!$%4%&N!3'#3`*#438#3S*!K!4%3@3"JB +&"JN8$"3&"4-8&C!$%"!4"C!$%K-*&"8'"C!%%K3*&")&%C!'"385#3i-&488#`N +5!K!4%3@3!a)&"K+3!`N8$K-&N!-6&481#3-4%38&"K)6&"Ar#385%a-5#3N6%C! +("385#480$"89#4)&!J)$%3@3!a)&%T!$#3N,$J84"38'#3lr$!84"C!$%K)*&"6 +r&4)5"K13!`Q3!a%&"4%4"C!$%JN9&3lr&3@3!`-!%"%%"385"4)5%`J*&484%3@ +3!a-8ra88%38&"K)5%`N8r`N5%K-6#C!%&4)4N!-&%38&%a3-&3`9%K%&"4%!!J- +4%3@3!a+3!a-8&434N!-&"4)8r``9%`8&%JB5#!N8&4+3!a13!`Q3!a38%C!%"38 +5#4@3!`i5%4%&%38!!K%3%3@3!a)5%`N8$!N4N!3&"JN1&488"C!$%K)6#43-%J8 +'%`Q3"439%4%%%385#3X1#a36!K%&N!34!J-3!`84"3B6#439&384%"%4"4)*r`` +9&4)&"4)5%`N8&4)&%K-*N!B9"4%4"JN8$!i9#433%"%4"4%4%K)3!`-4N!-&%`N +9$JN4%4!4%385#3`9N!-'"385%K-*#485"4)6#C!&&"35%3B*#439&385"K3&N!3 +4"385#4%$%3-4"3B8&3N4N!8&"4)6$!i9&4-&"4)'%`N8&!8&%K)6#C!&&43&#C! +%&!`*"3N1%a%&N!B5&43'"38(&3i1%a'3"J8`9N!-'"C!$%JF*&"3+"4)5#C! +&!*!(&J!!c-bCN!3!!Fc-QCPQCJ!#QC!'!!1CN!4QCJ!%QCPQN!3!"@D3"J!'CT! +%-c-!"fCQ-j!%!!Jc-fCQ-c-!#613"J!+-j!%!*!$#b)L!*!&$"%4!*!&$3!!)L) +!N!-1!!!4%3#3!`m!N!34%3!3L*!'!"&hN!B!%P@3"J!64*!'!"3LN!B!&4'3"J$ +r!*!)!3#3!mRS!!$)k!!!"r`$XkJd!hS!N!-F"TB!&eG*6N3!"`$#9%e36!!"!5* +1G@e`!!%"1N&-8P3!!`&54%P86!!%!B*69&)M!"%"[QPMFc3!"!+@D@0c)`!%!Y* +*3diM!!3$$QPME$3!"!0+4%a24`!!!iC'6dj8!!8$NNC26N3!!J2D3e958`!)!rj +ZD$-a!!!%DNC548B!"!4f3Nj%6!!!",*048j9!"-%[Ne195-!!3@Z8e45)!!!"FC +NBh4L!!!&dQPMG')!!!AHB@0dBJ!$"HT`F'&d!!F''J#!!#d!N!0i!*!&J3#3#i) +!#!#3!b!!N!@$!!m!N!2H!*!&K!!A!*!$3!#3"B8!(!#3!eS!N!@'!#%!!!%#!*! +&K`"f!!!jh!#3"B!!-J#3!ji!N!@"!2d!!%h1!*!&J!!h!!!LkJ#3"B%!33!!)`3 +!N!@"rrmJ!!MJ!l1Qf!#!rrm!!%lQ!l1PV"1)!58!!%li!l1Q%"1*!6%!!%*4!l1 +Pm!#"rrmJ!!M`!l1PK!#!rrm!!!b-!l1QB"1)rrm!!%jQ!l1QL"1*rrm!!%kH!l1 +PZ"G`rrm!!&+%!l1P[!#!!'JJ!!%Q!l1Q,!#$!*%!!%Z4!l1P(!#%!*8!!%iG!l1 +Pi!#&!*S!!%im!l1Pr!#'!4%!!!l1Pj!#(!*m!!%pQ!l1Pf!#)!+-!!&"G!l1 +PJ!#*!+N!!%m+!l1Q0!$*!+i!!%`P!l1QR!$+!,X!!%b1!l1Rk!$,!-F!!%bd!l1 +PM!$-!0!!!%cD!l1Q2!$0!0B!!%ck!l1PX!$1!0`!!%dD!l1PD!$2!1)!!%dk!l1 +Q"!$3!1J!!%eD!l1PT!$)!1i!!%ed!l14Y!$4!28!!%fk!l1Q)!2Srrm!!!&!!*! +%!qRrr`!!!F3!N!3$k[rr!!!-XJ#3"!2VrrmJ!$Sk!*!%!qcrrb!!1Vi!N!3$k2r +r!!!#5!#3"!2Trrm!!!+-!*!%!qVrr`!!#N3!N!3$krrr)!!l3J#3"!2XrrmJ!$Z +'!*!%!qMrr`!!!Y!!N!3$kIrr!!!$e!#3"!2Urrm!!!P!!*!%!q[rrb!!1mS!N!3 +$l2rr)!!mcJ#3"!2Srrm!!!6B!*!%!qRrr`!!"Y`!N!3$k[rr!!!+L!#3"!2Vrrm +J!$h5!*!%!qcrrb!!2pB!N!3AF!%A!!"4rJ1cT0",LIrr)!!00J1cTe4,M2rr!!! +@`J1cTe"-$!"G)!!X+J1cTda-LIrr)!"GRJ1cTdK-M2rr!!"R+J1cTd4-#3&C)!! +MC!1cTd!!P`"*"!!LT!1cTc`!Q!"5"!!M(J1cTcJ!Q3&0"!"G@!1cTc3#!2rr)!! +j"!#3"!)$rrmJ!$P-!*!%!J6rrb!!1C3!N!3#"Irr)!!h9!#3"!)(rrmJ!$HF!*! +%!J(rrb!!0q3!N!3#"[rr)!!i,!#3"!))rrmJ!$Kd!*!%!J,rrb!!1,`!N!Errb! +!1J#3"S$rrb!!3GS!N!@"rrmJ!%(P!*!&J[rr)!""m!#3"B2rrb!!3IX!N!@%rrm +J!%)'!*!&J2rr)!"#%3#3"B(rr`!!3Q-!N!@#rrm!!%,X!*!&Jrrr!!"$1!#3"B6 +rr`!!3qJ!N!@!rrm!!%4I!*!&KIrr!!"%M3#3"BErr`!!808!N!@(rrm!!%qp!*! +&L2rr!!"&9`#3"BRrr`!!4K3!N!A+rrm!!%E2!*!&brrr!!"(AJ#3"Fcrr`!!4pS +!N!A0rrm!!%K"!*!&c[rr!!")U!#3"Frrr`!!53m!N!A*rrm!!%Pf!*!&d2rr!!" +*j!#3"FMrr`!!5MN!N!A4rrm!!%V1!*!&J!#"&!",-31cT0J!J3#*&!",B31cT53 +!J!%#!!"26J1cT5`AF!%I!!"4bJ1cTG!AF2rr!!"5,!#3""1)!6N!!&1f!l1QG"1 +*!88!!&2U!l1Qa!#!rrm!!&3H!l1Ph!#"rrm!!&45!l1N%!#!rrm!!&5'!*!&J[r +r!!"c+J#3"B(rr`!!K&3!N!@$rrm!!)d@!*!&K2rr!!#H-!#3"BArr`!!TVS!N!@ +'rrm!!+mm!*!&Krrr!!#heJ#3"!G0CA0cB@GP"P0dBA4eF`G%G@jRC@pZ"%ePER8 +%9'9iG!Y%D@&REQpcG'PMF`4198a-"%jeEA!*9@jcD'PQG'9N"e0SD@CdC@3)5'& +MDdC[ER3+8&0)B@0V4QpZG!T38dKKBfY'Efjd$8eTFf-Z)&0dFQPZCh-+9&4C)&G +TEQ4[G`GYC@jeBQ&b"h0eBQePER8$5f*N"%KPE(!%5@jQE`0"Bh3&6@&RD@-%3QP +dF`aMEfjdFQpX)'YPHA-,F(9ZBh4eBA4TEfi)BR*KBfYPG(-&B5!Y)'d&EL!Y)(S +&35!Y)%d&6L!Y)&S&-#!Y)$N'GfPkBA*N"f0eFR*PER3%68j9)`j3FQ9Q)%CTE'8 +J6Q&YC39&FA9TF!GKFfYZB@eP"90dBA*d#d&LEh9d,f9bFQpb"d0[EQCTFQd,3@* +[GA3[CA*bEh)(3fpZCQPbE3Y1CAG)B@0V4QpZG!a38dKKBfY'Efjd)$Q6)`: diff --git a/sys/mac/NHsound.hqx b/sys/mac/NHsound.hqx new file mode 100644 index 0000000..f0cebe9 --- /dev/null +++ b/sys/mac/NHsound.hqx @@ -0,0 +1,4197 @@ +(This file must be converted with BinHex 4.0) + +:#e0[G@jNFbjbFh*M!(*cFQ058d9%!!!!!!!!!!-,a@BE!!!!!!%!!!-+K!!$#B3 +!!!&"!!!"MJ!)Tc`pR`!!$rF!!!'1!!LR2$fI!!!2q!!!!Bi,8fpeEQ4c,R*cFQ- +#!!!!FR0bBe*6483"!2rrrrm!!!!!FR0bBe*6483"!2rrrrm!!!!!!!!!!!!!!!! +!!!!!$hQR2DiI!!!!!!!$#m83X3!!!C8!#+Fm2eJ!!"$"!!!"P3!)Tc`r@!!!%-3 +!!!'9!!LR2$q"!!!3e!!!!C8!#+Fm2kX!!"$P!!!"P3!)Tc`rd!!!%2X!!!'1!!L +R2$rR!!!4"`!!!Bi!#+Fm2qJ!!"%)!!!"S!!)Tc`rlJ!!%3d!!!'J!!LR2%!"!!! +4(3!!!!!h3!!"!!%!"3!!!+!!!B"4!!!!!!!8!!!!!!!!0aC@lSZM!!!h&!!!0a8 +!2)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)# +!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIi#!JB+$Ji5&KBD'KSD'KB@%K)1 +#JS'"J)"rIhprIhprIhprJ)#!J)'"JB+#JS1$Ji1$Ji+#JS+#JS'"J)"rIhjpIAY +kHAGfG(0aF'p[EQp[F(*cGRKlIB#%KSL,MBq3!*!!N!#3!)q0M)Z*L)D%Ji'!Ihj +qIAepIAepIAepIRjrIi#"JB+$K)5&KB@&KB@&KB@%Ji1#JB"rIRelHAGeFR"YDfP +RC@9PC@GTE("dHAf#KSU1NT@AQ*QBPjD8NBq-LSL'K)+!IhemI(YlHhYlHhYmI(e +pIRq!JB1$K)5&KBD'KiL)LBQ)KiD%Ji&rIAYjGR*ZDQ9KA9YC9eGC@ejMD'jeHi+ +)MT5CR*qKSU'JRTbCPT12M)U(KB1"IhjmHhTkHRTkHRTkHhYmIAk!JB+$K)@'KiL +)LBU,LiZ+LBL'KB1"IRYiG'pUC9pD9P*26Np38eGEB@CYG(Z%M*1DS+5SUUUUU+@ +LRTZANiq-LBD$JAppI(YkHRTjHAPkHRYmIAk!JB+$Ji5%KBD(L)Q*LSU+LBL(KS@ +$J(ekGR&XCf&E99&058P*5de499YLDA*lKBkAS+HXX,+bXDqVTk1HQC@4MBQ'Ji" +qI(YkHAPjH(KiHAPkI(erJ)'#JS1%KBD(L)Q,M)f0MBb+LBL'K)&qHR9aDQ0G9e& +,4d0"3%*%5%e8A'9[HSD4R+DZY,LkZVLeX+ZQSCZ@NSf*KB*rIAYkHAKhGhGhGhK +iHAYmIS#"JS+$K)@(L)Q+Lib-M)Z+LBL(KS1!IAKcE@CI9e")3cik1$Sm38K4@Q9 +[HiH5RUH`YVUm[,UhXkfSSTfBNiq,Ki5!IRakHAKiGhGhGhGiHATmIRq!JB+%KBD +)LBZ0MSq3!)q1MBZ+L)5!I(KcE@CI9Nj(3$Se-M%c0Me&8&aTGiD9SUki[m2&a-+ +qZ,+VTCqCP)q,Ki5"IRakHAKhGhGhGhKjHAYpIRq!JB'#Ji5&KiL+Lib0MBf-M)Z ++L)@"IAGbDf4E8da%2MJc-#ma0$T#6PYVHiZEUVDraFR+b-5rZ,'USjfANBf*KB+ +!IRalHRPjHAKiHAPkHhapIRjrIi#!JB+$KBD(LBU,M)b,LiZ+L)@#IRPcE'9F98a +&2MJc-#ma0$T#6PYVI)kHVER"amV+b-1pYUkRS*U8MiZ)KB+!IhjpI(amHhYlHha +mIAeqIRjqIRjqIRq!JB1&KiQ+Lib0MBf-LSH%J(aeEQCH98j(3$Sd-6!`-MBq5&C +RHBZGVEV%bXh0bX@qYkqQRjQ6MSU'K)+!IhjpIAamI(amI(amIAeqIRjpIAepIRj +rJB+%KSL*LBU+LSU*L)D%JAeiF@TM@P0-4$ik0cBf1$Y#5P9NG)@@TE+ma-M*am1 +qYkqSS*U8MiZ)KB+!IhjqIAepIAamI(apIAjqIRjqIRjqIRq!JS1&KSH)LBU,LiZ +*L)D$IhTdE'9F98a%2MJc-$!a0$Y%8@&cKCHSYX()c-h,am#jX+QJQT51LSD$JB" +rIRjpIAepIAjqIRprJ)"rIhjqIRjqIRq"JS5'KiL)LBQ+LBL(KB0rHR9ZCejA6dK +!1M8a-$!b0Mj+@@TqND1c`-M1d-r,a,bdUk+EP)q,Ki5#JB"rIhjqIRjqIRjqIhp +rJ)"rIhjqIAepIAk!JS1&KiL+Lib-LiU)KB*pH(&UBeT66%3q1$-`,c!b0d"0AA# +&QDZkaXl4dXr+`lU`TjqANBb)KB1"J)"rIhprIhprIhq!J)'"JB"rIhjpI(amI(j +rJB1&KSL*LSZ-M)U)KB&pH(&UBPY66%8r163a-$!b0d"-AA''Qkfmb-r6dp$*`VQ +[TTf@N!#,Ki5$JB#!J(prIhpqIRjrIhq!J)"rIhjqIAepIAeqJ)'$KBD(L)Q*LSQ +)Ki@$IhTcE'4G98j'3$Xh0$-d0MY$6Pe[JjDSYm2,cp(1bF+jX+HIPj'-L)@$JS' +!J(prIhjqIRjqIhq!J)#!IhjqIAepIAjrJ)+$KBD(KiL*LSU+LBH%J(YdE'9F8da +$2$Bb,bi[-MP%8Q4iMD#b[mR2dY(0alkfV+1EP)k+KS5#JB"rIRjqIRjqIRjrIi# +"JB'!IhpqIRepIAjrJB1&KiQ+LSZ,LiQ)KS5!Hh9ZCej@688q0c%Y+b`[0$j,@fq +&QUbmamr6dp$+`lU`TjkANBb)KB1"J(prIRjqIRjqIRprIi#!JB'!IhpqIAepIAk +!JB1&KiL)LBU,LSU)KS5!HR4XC&e86%-m0M!X+bd`06p-AR1*RDqqbY(8e0$*`VL +[TTf@N!#,Ki@$JS'!IhpqIRjqIRjqIhq!J)'"J(prIRepIAeqJ)+%KSH*LSZ,LiZ ++L)H%J(YdE@4F8da$2$Ba,LdY,c3q5eaaKjb[[mV5eGA4bX+iVk@FPC!!LiH&Ji+ +"J(prIRjqIRjqIRjqIi#!J(prIRepIAepIS#"Ji@'L)Q+LSZ,LiU*KS0pGfpRAPC +046dh-LiX,#ic2%PDES5CV,c)d069dF[$ZV#RRjH4MBQ'K)+"J(prIRjqIRjqIRj +qIhprJ(prIRepIAeqIS#"Ji@(L)Q+LSU,LiU)KB*qH(&SB&G24cmi-LmY,M!f2NY +FEi5BUVR&cG$4cXR#ZE#SRjL5MSU(KB1"J(pqIRjpIAepIRjqIRprJ)"rIhjqIAj +qIRq"Ji@(LBZ-M)f-LiU)KB&pGQpQA94,3cXd,LXU+bmf3%pKGSZIX,l*cp,4cXM +!YkqQRTL5MBU(K)+!IhjpIAepIAepIAeqIRprIhprIRepIAeqIi##K)D)LSU,Lib +,LiQ(KB&mG@jPA94,3MNb,#NS+5`d3&"MHBqMYF2-dY66cXHrYUfNRCD4MBQ(KB1 +"J(pqIAepIAepIAeqIRprIhprIRepI(epIS##Ji@(LBU,M)f0MBb+Ki0qGfpQA94 +,3MNb,#JQ*LNa2%aKH)kMYF20dpA8cmLrYUfNR*D4MBQ(KB1"J(pqIAepIAepIAe +qIRjrIhpqIAamI(apIRq!JS5'L)U,M)f0MBZ*KS0rHR0UBPP35$mi-LiU+5S[1%C +BES@DVEh)d026d-R#ZE#RS*Q6MiZ*Ki5#JApqIAepI(amI(apIAjqIRpqIRepIAe +pIS#"JS5'L)U,Lib-M)b+L)5!Hh9YCPe85d-m0M%Z,5ia1846CRZ2SV2!bFr3cm[ +%Zl5XSjfANSk,L)D%JS"qIAemI(amI(amI(amIAepIAemI(apIi##K)D)LBZ-M)b +,LiZ+Ki5"I(C[Cej@6N8q0M%Y+b`[0N&3C(Q1SV2!bY$4d-c&[,@XT*kBNiq-L)D +%JApqIAamHhYlHhYlI(amIAepIAemI(epIS##K)D(L)U,M)b-M)Z+Ki5"I(G`D&p +@683l05mV+LSY08"3CAU2Sl6"bp$5dFc&[V@YTCqCP*!!M)Q'Ji&rIRemHhYlHhY +lHhYmI(apIAamI(amIAk!JB1&KiQ+M)b0MBf-LiQ'JhjiF@TK9dj&2$8[+LJR+M% +m6'&hMD'c`F[4dp,0aVqfVUDJQT53!)f+Ki5"IhjmI(YlHhYlHhamI(apIAemI(Y +lHhapIS##K)D*Lib0MSf0M)Z)KB*rHR0VBPP35$mi-5dU+5X`1NPFFBHEVEc)cY( +4cXM"Z+qSSCZ@NSk,L)D$JApqIAamI(alHhYlHhYlI(amHhYlHhYpIS#"Ji@(LBU +-MBk1MSf-LBD#IRG`D&p@683l0#mV+5JV-Mj3CAZ4TEE$c0,6dFc&[,@XTCkCPC' +0LSH%JAppI(YlHRTkHRTkHRYlI(amI(alHhYmIRq"JS5'L)U-MSk0MBf-LBD$IRP +bDQ&C8%K"1cBa,LdZ-Ma+A('(QkflaXh3d-h(`,H`U++FPj12LiL&JS"qIAalHRT +kHAPjHAPkHRYlHhYlI(apIi##K)D)LBZ-MBf0MBf,LBH$IhTcDf0D8%Fq0c%X+#F +U-$Y+AR5+RV#qb-r5dFl)`EQ`UD+FPj12LiH&JAppI(YkHRTkHRTkHAPjHAPjHRP +jHRYmIS'$KBH*LSZ-M)b,LiU*L)@#IhTdE@CG98a%2$Fa,5XV,MC%9QZ"PUQjaFh +4dXr+`lbcUk@IQC@4MBU'K)&qIAYkHAPjHAPjHAPjHATkHRTkHRYmIAq"Ji@(LBZ +0MSk0MBb,LBH%JAefEQGH98a$1c8[+bJR+M*!8fQ!PUUlb0$8e0(-aEfdV+@HQC@ +4MBQ'Ji"qIAYkHRTkHRTkHRTkHRTkHRTjHATlI(k!JS5&KiL*Lib0MSf0MBZ)KB& +mG@aN@e*)2cJa+bFP*b`h4eadLk'c`Xc5eG22b-'iVkLLR*H6MiZ)K)+!IRalHRP +jHAPjHAPjHRTlHhTkHRTkHherJB1%KSL+M)f1MSk0M)Z)KB*pGh"RAP904$`e,bS +Q*LSc399XJjQX[-M3e064bm5mXkbPRjQ8N!#-L)@#IhemHhTkHAPjHAKiH(KjHAT +jHAPjHRYmIS'$KBH)LSb0MSk1MSf-LBD$IRPbDQ&B6N8m0#iS*#-Q,ce4DB#AUl[ +)d069dXc&[E5XTCkCP*!!M)Q&JRppHhTjHAPjHAPjHAPkHRYlHhYkHRTlI(erJB1 +&L)U-MBk1Mik0M)U'JRjiF@TK@%p(2cJb,#JR+#ml6'*jN!#NYX61dp66cXHqYkk +RSCZ@NSk+KS1!IRalHRPjHAKiH(KiH(PjHRTkHATkHhaqJ)+%KSH*Lif0MSk1MBf +,L)@"Hh4XBeT458!j-L`S*LFY1%PHGBbKXm,0dp66cmM"Z,#SSCbANSk,Ki1"IRa +lHRPjHATkHAPjHATkHRTjHAKjHRYmIS##K)H*Lib0MSk0MBb,L)@#IAKaDQ&C8%F +q0c!U*5-P,Me4DS1DVVr+dYE9dX[%Zl1VT*kBP*!!MBQ&JRppHhTjHAPjHAPjHAP +kHRYlHhTkHRTmIAq!JS5&KiQ+Lib0MSk1MBU(K(pjFQYK@%p&2$3Y+#3M*Lim8'L +!PkZmbG(9eG,-aEfdV+@IQC53!)b*KB*rIAakHRPjHATkHAPjHATkHRTjHAPkHha +qJ)+%KBH*Lib0MBk1MSf+Ki@!HR0XBPP34cih,bSQ*#FZ1djNI*1RZ-E2e0A5cFD +rYUfQS*U9NBf*KS1!IAYkHAPjHAPjHAPjHAPkHRTjHAPjHRaqJ)+$KBH*M)f1Mj! +!Miq1M)Q&JAaeE@4E8NP!1$%V*L-N+M9(A(1-SV6$cY29e-r)`,H[Tk'EPT+1LSH +%JAjmHhTjHATkHRTkHRTkHhYlHRTjHATlI(jrJB5'LBb0MSq2MSk0M)Q'JRehF'G +I9Ne%2$3Z+#-L*Lmr9@k'RV,#cG6AeY(+`VQaU+'FPT+1LiH%J(jmHRPiH(PjHAT +kHRTkHRTkHRTjHATlI(k!JS5'L)U-MSk2Miq1MBZ)K(pkFfYL@P&)2cF`+53L*5X +j6'0mP+QlbG(@ep61alkeVD@IQC@4MBQ&JRppHhPiH(KiHAPjHAPjHATkHRTjHAP +kHherJB5'L)Z0MSq2N!#3!*!!Mib*KS&mG@eN@e**3$Na+L3L)bJd4PaeMU5faFr +9eYA3bF#hVkHKQjD4MBU'Ji"pHhTjH(PjHATkHRTkHRTlHRTjHAPkHherJB1&KiQ +,M)b0MSk1MBb*KS*pGh"SAeC04$Xd,LJN)LBa3PKaLU#c`ml8eYA4bX+jX+QLR*H +5MSZ(Ji&qI(TjH(KiH(KjHAPjHRTlHhTkHRTlI(erJ)+&KiQ,MBq3!*!!N!#3!)q +0LSH$IRG`CPa658!i-#FI'aXJ,8&DG)kPZFM5f0R@dFV"Z+qRSCZ9NBf*KB*rIAY +jH(KiHAPkHRYlHhYlHhYkHAKiHATmIS##K)D*Lif1MSk0MBb,LBD#IAKaD'"A6N8 +q0LmR)L%M,$a5E)@GXF(0e0I@dFV$ZV'TSTbANiq,L)5"IhelHRTkHRTkHRTkHRY +lHhYkHAKiHATlIAq"Ji@(LBZ-MBk2Miq1M)Q&J(YdE'0D8NP!1$!R)4mJ*cC,C(q +BVVr-e0MAe-h&[,1USjbANiq,L)@"IhelHRPjHRTkHRTkHRYlHhYkHAPjHATlIAq +"Ji@)LSZ0MSq3!*!!N!#2MBU'JAaeE@4E8NJr0biP(KXE)M&(BAf@VF$0eGRCeFr +([V@XT*kBNiq-L)5#IhelHRPjHAPkHRTlHhYlHhYlHRPiH(PkI(k!JS@(LSb0Mj! +!NC'3!)q0LB@"Hh4YC&Y558!h,bJL(KdN-8GKHjDX[Xc8fGR9cXHqYDbNRTL8N!# +-L)@#IhelHRPjHAPkHRTlHhYmI(alHRPjHAPkI(k!JS5(LSb0MSq2Miq0M)Q'JRe +fEQCF8dT"1#mR)4XD)#j%ARU8Ull,eGRCeXr)[V@XT*fBNiq,L)5"IRakHAKiHAP +kHRYlHhamI(amHRPjHAPlI(k!JS5'L)Z0MSq2N!#3!)q0LiL%IRG`CPa658!i,bB +I'KNI,%&EGj+U[Fc9fY[AdFQrYUfNRCL6Mib)KB*rIAYkHAPjHRTlHhYlHhamI(Y +kHAPiH(PlI(k"Ji@(LSb1Mj!!Miq2MSb*K(pjFQPJ9dj&263X*"iD(#Bi8Qf*SVI +(dpREf02,`VL[TU#DPC'0LBD$J(jmHRPjHAPkHhYlHhYmI(amHhTjH(PjHherJS5 +'KiQ,MBk2Mj!!N!#2MBU'JAYcDf*C8%Bp05XL("FC)M41DiHKYmM6fYcDe-c$ZE# +SS*U9NBf*KS+!IAYjH(KiHATkHhYmI(amI(alHRPjHATlIAq"Ji@)LSf3!*+6P*1 +5NBk+KB"jFQPI9Na#16%S)"N@'#-f8'b)SVI)e0[GfYA0a,UaU+'DPC'0LBD$Ihe +lHRPjHATlI(amI(amI(alHRPiGhGiHRarJB5(LBb1N!#4NC'3!)q0LiL%J(PbD@" +A6dBp05`N(4ND)M*,Ci1HY-E5fGcDe-h%ZV'SSCU9NBk+KS5"IRakHAPjHATlI(a +mIAepIAalHAKiH(PkI(q"Ji@)LSb0Mj!!N!#4N!#2M)Q&JAYdE'*C8%Fq0biP(KN +B(bp)C)'FXX64fGcEeXl&[,+TSTZ@NSk+KS5!IRakHAPjHRTlHhamI(apIAalHRT +jHAPkHhf!JS5(LSb1Mj!!N!#3!)q1M)U(JRefE@9F8dT"16%R(KF8'5P"ARbBX-2 +5fYhFep$([E5VSjbANiq,L)5"IRakHAKiHAPkHhYmI(apIAemHhTjHAPkI(k!JS5 +'L)U-MBk2Miq2MBZ)K(piF'GG9%Y#16!R(KF9'LJr@hQ9VF$2f0cFf0()[V5VSjf +ANiq-L)@#IhelHRPjHATkHhYlI(amI(amHhTjHAPkI(k!JS5(LSb1Miq2Miq2MSb +*KB"jF@KH98Y#15mP("34&L3m@AH8VF(3fYlHfY2+`,DYTCkBNiq,L)5"IRakHAK +iHAPkHhYmI(amI(amHhTjH(KjHhf!JS5(LSb1Mj!!NC+5NBq0LB@!HA"SAP9-3cS +`*K`8%"-J0e4cND[!cpRHhYV6bX#fVD@HQ*12M)L&JRpmHRPiH(PkHhYmI(amI(a +mI(YjH(KiHAYpIS#$KSQ-Mj'5Nj15NT!!MBU'J(PaCee658!f,#)A%!d6)caDHCD +[`p,DhYhCdXLqY+ZMR*H5MSZ(KB&rIAYjHAPjHRYmI(epIAepIAemHRPiH(KkHhf +!JS5(Lif2N!#4NC+4Mif+KS"jF@KI9Na$1M!Q("32%Kmf8h14UVr3fYrIfp6+`,H +ZTCkBNiq-LB@#IhelHRPjHATkHhYmI(amI(amHhTiH(KjHRapJ)+&L)Z0Mj!!NC+ +6Nj'2M)L#I(9XBeT34cie,#)C%4!C+dCPK+#hbGAFhYc@cX5kXDQKQjD5MSU(Ji" +pHhTjHAPjHRYmI(apIAepI(akHAKhH(PlIB#$KBL-MSq3!*'5NT+4MSZ(JhaeE'0 +D8NP"1#mP("88'LT$BAqEXXA5fYhEeXr&Zl+TSTb@NSk,Ki5"IRakHAKjHATlHha +mI(apIAemHhTjH(KjHhaqJ)1'LSf2NC+6P*56NBk+KApiEf9F8NJr0LdM'K34&bB +qA(UAVm,4fGhFf0()[V@XT*fBP*!!M)Q&JRpmHRPiH(PkHhYmI(alHhYlHhTjH(K +iHAYpJ)1&L)U-MT!!NC+6Nj+3!)f*KB"jF'GH98Y"1#mQ(4B6&b8l9h@5UVl0ep[ +Ef0(*[lDYTCqCP*!!MBQ'JhppHhPiH(KjHRYlI(amI(amI(YkHRPkHhaqIi'$KBL +,MBk2N!#4NT+4MSU'J(PaCej85d%i,L3C%Jm9*$aCGj5Y`G$ChGcBdFLqYDbNRTL +8N!#-L)@#IhelHRPjHATlI(apIAepIAemHhTiH(KiHRaqJ)+%KiZ1N!#5NT16Nj' +1LSD"HR&SAeG146`d+Km@%"%F-8eXLkDlc0MGhY[8c-+jX+HKQjD5MSU(Ji"pHhP +iH(KjHATlHhamI(epIAakHAKiHATmIS#$KBH+M)k3!*'5Nj15N!#0LB4pGQePA&* +*3$J[*4`@&"XU3PppQ,$$d0RFfpE2aVfcUk1GQ*52M)L&JAppHhTjHAPkHRYlHhY +lHhamI(YkHAPjHRYpIi'$KSQ0Mj!!NT18P*+3!)f+KApiF'GH98a$1M!Q("33&#) +k9hD6V-$2fGhGfG,*[lDYTCkCPC'0LB@#IhakHAKiH(PkHRTlHhTkHhYlHhTjHAT +lIAq!JS5'L)Z0Mj'5Nj15N!#0LS@!HA*TAeC04$Xb+5!B&4FM1&0`M+5jbG6CfYI +5bX'iVkHKQjD5MSU(Ji"pHhPiH(KiHATkHRTkHRTlHhTjHAKjHRYqJ)+%KiU0Mj' +5Nj16NT!!MSZ(JRYdDf*B6N8l-LFG%`i4(643EifR[-c@fpcCdX["Z+qRS*U9NBk ++KS0rIATiGhGiHATlHhamI(amI(YlHRKiH(KkI(k!JiD*M)k3!*+6Nj15N!#1LiH +"Hh4VBPP34cid+L!A%4%E,NTTKk1jbYEFhG[9cF5kXDLLR*H6MiZ(K)&qI(TjH(K +jHATlHhYlHhYmHhYkHAKiHATmIS#$KSL,MT!!NC'5NT+4Mib*K(efEfCF8dP!0b` +J&3d,&#P&C)1IYXR@h0lFeXr'[,1USjfANiq,L)5!IRYjH(KiHATlHhamI(amI(a +lHRKhGhKjHRaqJ)1'LSk3!*'5Nj16NBq0LSD!HA"SAe9-3cN[*"J2#a!K1eTkPl( +%dp[HhGM4alkeV+5HQ*53!)b)KB&rI(TjH(KjHATlHhamI(amI(akHAKiH(TlIAq +"JiD)LSf2N!#4NC'4Mif+KS*lG'aM@P&)2c8V(a82%"Xb6fk-TV[-eY[FfG,+`EL +[Tk#DPT+1LSD#IhakHAKhH(PkHhYlHhYlHhalHRPiH(KiHAYqJ)1'L)Z0Mj'5Nj1 +6NBk,Ki*mG@eN@e**3$BX)KJ4%"SZ5@L(SVM*e0[FfY6-`lUaU++FPT+1LiH$J(e +lHAKiH(PkHhYlHhYlHhYlHRPiH(KjHherJB1'L)Z1N!#4NT16NT'2M)L%IACYC&Y +45$id+4d5#`X@,%PTLD5lc0MHhpc9cX5lXUULR*D5MSU'JhppHhPhGhKiHATlI(a +mI(amI(YjH(GhH(TmIS##KBH*M)q4NT58NT'2LiL$IRG[CPe656me+Ki6#`X9+NG +RKk+jbpIGhY[@cF5kXDQLR*H5MSU(Ji&qHhPiH(KjHRYmI(amI(amHhTjH(GhGhP +lIAq"JiD)Lik3!*+8P*58NSq,KS&kF@KH9%Y"0LSG%3F&$b4#C)5KZF[AhYrFeXr +&Zl+USTbANSk,Ki5"IRYjH(KiHATlI(epIAemI(YlHAKhGhGiHRaqJ)1'LBf2NC1 +8P*56NBk+KB"jFQPJ9de%1M!M&``($4mj@RUBXXA8h0rHf0(([V5VSjfANiq,L)@ +#IhakHAKiH(PkHhamI(amI(alHRPhGhGiHAYpIi+&L)b2NC18P*58NBk+KS&kFQT +K@%p'26-T(4)-$KXc8A'3!+UrcpRGhGV6bX#hVUDIQC@4MBQ'JhppHhPiH(PjHRY +lI(amI(YlHhTjGhGhGhPlIAq#KBL-MT!!NT59PC55MiZ(JRefE@4E8dT#16!N'4% +2&LP%C)5IYXR9fphEe-h%ZV'SSCZ@NSk+Ki5"IRYjH(KiHATkHhamI(amI(YlHRP +iH(PkI(k!JS5'LSf2NC18PC56NBf*K(jhEfGH98a#15iM'!m-%L9!B)#FY-I8fpl +Fep$([E5VT*fBNiq,Ki5"IAYjH(KiHATlI(amI(amHhTjH(GhGhKkI(k"Ji@)LSf +2NC+6Nj+4MSZ)JhjhF'KI9dj&1c!N'!m,%L-qARqFY-I8fplFemr'[E5VT*fBNj! +!M)L&JRjmHRKhGhKjHRYmI(amI(alHRPhGRChHAYpIi+&KiU1N!#4Nj58P*14MiZ +&J(PbD&p@683l-5FF%3d4(cK@GT5Z`Y$ChGcBdFLqYDbPRTQ9NBf+Ki1!IATjH(G +iHATlI(apIAamI(YjH(GhH(PlI(jrJS@)Lik3!*+6P*@8Niq,Ki*lFQPJ9dj&1c% +Q'a%-$ade8h15V-(3fYlGfG,+`,DYTCqCP*!!M)L&JRppHhPiH(KjHhamIAepI(a +lHRPiGhCfH(PlIAq"K)H+MT+8PCD@PC13!)f)JhaeE'0D88Jq05SH%``-&ba*DBL +MZX[@h0hDe-c$ZE#SSTb@NSk,Ki5"IAYjH(KiHATlHhamI(amHhYkH(GhGhKjHhk +"JiD*M)q4NT18P*14Mib*K(piF'KI9Ne%15dJ&!S)%54"BS1JYmV@hGrFeFl&Zl+ +TSTbANSk,Ki5"IAYjGhGhH(PkHhamIAamI(alHAKiH(KjHhf!JS5(LBb2NC18PC5 +6NBk*K(piEfGH9%Y#15iL&3`*%54!B)'HYXR9h0rFeXr&[,1USjfANiq,L)@#IRa +kH(KiH(PkHhamI(amI(YkH(GfGRGiHRaqJB@)Liq4NT59PC56NBk+KB"kFfTK@%p +'2$%P'3i)$"dh9RH@Vm24fplGf0()[V@XTCfBP*!!M)Q&Ji"pHRKiH(KjHRYmI(a +mI(amHhTiGhGiHATmIS##K)H+MC!!NT18P*55Mib)JhaeE'*C8%Bm-LFE%3`1'c0 +4FC!!UVr1f0hGfG,*`,HZTTqCP*!!M)Q&JhppHhPiH(KjHRYmI(amI(amHhTjH(G +hGhPlIAq#KBL-N!#6PTHAPT@6N!#-Ki&lG'YK@8p'2$)R'a!*#KF[6Qq2UVr3fYl +HfY2,`EL[Tk#DPC'0LBD#IhakHAKhH(PkHhapIAepI(alHRPhGhGiHATmIS'%KiZ +2NT19PTD9Nj!!MBQ$IACYC&Y45$me+am8$3dA,%KSKk1jbYEEh0V6bm1jX+QKQjD +5MSU(K)&qI(TjH(KjHRYmIAepIAamHhTjH(GhH(PlI(q#KBL-MT!!NC+6Nj+4Mif ++KApiF@KI9Na$15dK&!X)%59$C)@KZ-V@hGlEeFl&Zl+TSTbANSk,Ki5"IRYjH(G +iH(PlI(apIAepI(alHRPhGhGhHAYpJ)1(LSk4Nj58P*@8NSq,KS"jF@KI9Na$15d +K%`J$#KipB)'JZ-[Bhq(Hf0$'[,1USjbANSk+Ki5"IRYjH(GiHATlI(epIAemI(Y +kH(GfGA9fH(TpJ)1(Lik4Nj59PC@8NT!!M)H#Hh4VBPK246Xa*4N2#JdE-e*cNUc +"d0RGhGR5bEqfVD5HQ*53!)f*KS0rIAYjH(KiHATlI(amI(amI(YkHAGhGhKjHhe +qJB5(Lik4Nj5@PjD8NBf)JhadDf*C6dBm-LBD$`N+'$"2FC!!Um$3fYrHfY2+`EH +ZTU#DPC'0LBD#IhakHAKiH(PkHhapIAemI(alHRKhGhGiHAYpJ)1'LBb2NC18PC@ +8NT!!M)H$I(9XBeT45$mf+Ki5#JS9,%YXM+LpcYMGhYV8bm+iVkLJQT@4MBQ'JS" +pHRKhGhKjHRYmI(amI(amHhTjH(GhH(TlIB##KBL,MT'6P*@9P*+3!)b)JhefEQ9 +F8NJq0#NG%``,&La*DSUP[-hBhGlEe-c#ZE#RSCZ9NBf+Ki5"IRakHAPjHATmI(e +pIAepI(YkHAGfGRGjHhf!Ji@)M)k3!*+6Nj56NBq-L)0pGQePA&0*3$BX)"3-#a8 +V5@U+TEc0f0hHfp6,`VL[Tk#DPT+1LSH%JAjlHAKhH(PkHhapIRjqIRemHRPhGRC +hH(TpIi'$KSQ-MT!!NT19PC56N!#,KS&kF@KI98Y"0b`I%3B##b&!BS1KZFcChq$ +Gf-r&[,1USTb@NSk+KS1!IRYjH(GhH(PlI(epIAepI(YkH(GfGAChHAYpJ)5(LSf +2NC+8P*58NSq-Ki*lFfTK9dj%1LmM&3N$#4Xh@AZDY-M@hZ$HfG$([E1USjbANiq +-LB@#IhakH(GhH(PkHhapIAjqIAelHRPhGhGiHRYpIi+&LBZ0Mj'5Nj56NT!!M)L +$I(0VBPK04$S`*"J0#!`F0PCiPl$&e0cIhYR4b,keV+5HQ*12LiL%JAjmHRKhGhK +jHRapIRjqIRepHhTiGhGhH(TlIB##KBQ-Mj'6PC@9P*13!)b(JRYcDQ"A683k-#- +9#J8*'MCAHCQcapEHiGrCdXLrYDbPRTL6MiZ)K)&qI(TiGhKiHAYmIAjqIRjqIAa +kH(GhGhKjHhf!Ji@)M)k3!*'6P*56NSq-L)0mFfYK@%j%15iJ%38!"KSiA(qIZFc +Ci1,IfG(([E5VSjfANSk+KS5"IAYjH(GiH(TlIAjrIhprIRemHRKhGA9fH(TpJ)1 +'LBb2NC+6Nj16NBk,KS&lFfTK@%p'26)Q'3d(#KJb8h@9X-A8hH$IfY,)[V5VT*b +ANiq,L)@#J(elHAKiHATlI(eqIRprIRemHRPhGhGhHATmIS'%KiU0Mj'5Nj16NSq +-LB4pGQeNA&0+36FV(3m&"4!S5'b1UX(5hH,Kh06,`EHZTCqCP*!!M)Q'JS"pHhP +iH(KjHRapIRprIhpqI(YjH(GfGhKkI(k!JiD*M)q3!*+6Nj15Mib)JhefEQ9F8dT +#1#dJ%JJ'%#G(DSbT[p$Ei1$FeF["YkkPRjQ8N!#-L)D$J(elHAKhH(PkHhepIRj +qIRemHhTiGhGiHATmIS#%KiU1NC18PC@8Nj'1LS9qGfjP@e*)2M8T'`m'"4%S4QQ ++U,r3fq(KhGE0`lQ`Tk#DP*!!M)Q'Ji"pHhPhGhKjHRapIRprIhpqIAYjH(GhGhP +kI(k"K)H-N!#5P*D@PT@6N!#0L)0mG'YL@%j%1LmM&JS%"K8a8R@@XFE9hZ(Jfp2 +*[l@XT*kBNiq,Ki@#IhelHAKiHATmIAjrJ)#!IhjmHRKhGR9fGhTmIi+%KiZ0MT! +!NT16Nj+3!)f*K(jhEfCG9%Y#15iK&3X($b0"Bi@MZmlDi1$GeXl$ZE#SS*U9NBf ++Ki5"IRakH(GhH(TlI(jrIhprIRemHRKhGhGiHATpJ)+&LBb3!*+6P*58Nj+2LiD +!HA"QA94,3MNZ)K3+"JdJ2&k!RlR-fH$KhYM2aEZaU+'EPT+1LiL%JAjmHAKhGhK +jHheqIRprIhjpHhTiGhGhH(TmIS#$KSL,MT!!NC18P*14MSU'J(PbD&pA6N8p-bB +D$`S0(6GAHCLbaY6FhphBd-HpY+ZMRCH6MiZ)KB*rI(TjH(KiHATmIAjqIRjqI(Y +kHAKhGhKkI(k!Ji@)Lif2NC+5Nj15Mib)JRadE'0D88Jr0#JD$JF*&c&4Fj5[a02 +Fi0rDdmR!YUfPRjQ8N!#-LB@#IhakH(GhH(PkI(epIRjqIAalHAKhGRCiHAYqJB5 +(LSf3!*+6Nj56NT!!MBU'JATcDf*C8%Fp-bFF%3X0'c06GC5Z`p,EhYhCdFQrYUf +PRTL6Mib*KB+!I(TjGhGiHATmIAjqIRjqIAakHAKhGhPkI(k!JiD*MBq4Nj58Nj1 +4MSU'J(PbD@"A6dBp-bFD$`N-'M*5G*1Z`p2Gi0rEdmV!YkkPRjQ8N!#-L)D$Ihe +lHAKiH(PkI(eqIRpqIRalHAKhGRGiHAaqJ)1&L)Z1N!#5NT16NT'1LiL#I(9YC&Y +558!h,#!9$3`@+NGSL+@mcGMHhY[9c-+jX+HKQj@4MBU(K)&qI(TiH(KjHRYmIAj +qIRjpI(TjH(GiH(PlI(erJS@*M)q4Nj59PC55MiZ&IhG[C9a656me+Kd5#JJ5*d0 +NKU1lcGMHhpc@cF5kXDQKQjD4MBU(K)&qI(TjH(KjHRYmIAjqIRjpI(YjH(GhGhK +kI(q#K)H+MBq4Nj58P*14MSU&IRG[CPa55$ic*aS0!`)0*89UM+R"dYhLiYlAcX@ +kXDLKQj@4MBQ&JRpmHAKhGhGiHRapIRprIhpqI(YjGhCfGhKkI(k"K)H,MT'6P*@ +8Nj+2M)L$IA9YC&T45$me+ai5#JS8+8GRL+@mcYVIi0c9cF5kXDLKQj@4MBU'K)& +qI(TjH(KjHRapIRprIhjpI(TjGhCfGRKkI(k"JiD+MC!!NT18P*56NBf+KApiF'G +H9%T!0LSG%!F%$L4$CSLP[Y$EiH,Heml%ZV'SS*U9N!#-LSH%JAjlHAKhH(KkHhe +qIhq!IhjpHhPiGhCfGhPlIB'%KiU1N!#4NT18P*14MSU&IhK`Cej@6%-k,Km4"3% +)($YJJk1mcp[LiprBcm@lXUQLQjD5MSU(K)&qI(TiGhKiHAYmIAjqIRjpI(YjH(G +fGhKjHRf!JS@*M)k4Nj@9P*15MiZ'J(PbD9p@683k,b%6"`)*(6aJJU'kcGRIi0h +@cF1kXDLKQT@4MBQ(K)&qHhTiGhKiHRYpIRprIhpqI(YjH(GfGRGjHhf!JS@)Lik +4Nj58P*14MSZ'JATcDf0D8NJr0#FD$`N0(6KCHTLaaG,Ch0V8cF5kXDQKQjD5MSZ +)KB*rI(TjH(KjHRYmIAjrIhpqIAYjH(KiH(KjHherJB5)M)q3!*'6P*16NBq,Ki* +lG'aL@9")2c8S'Jm,%#%mA(fEXmE5fG[Ce-[#ZE#SS*U8N!#-LBD$J(jmHRPiHAP +kI(eqIhprIhjpHhTiGhGhH(PlIAq#K)H,MT!!NT16Nj+3!)k,KS&mG@eNA&0+3MJ +X(a32%b)m@hZBVm,1eGI9d-R!YkqRS*U9NBf*Ki5"IhakHAKiHATmIAjrIhprIRe +mHRPiGhGiHRYpIi+&L)Z0Mj'5NT+4Mif+KS&mG@jQAeC24Mdb*adB'53j9(12TlV +(d026cmM!Z+qSSCZ@NSk+L)@#J(jmHRPjHATlIAjqIhprIRelHRPiH(KiHAYmIi' +$KSQ,MBq3!*'4NC!!MSb)K(jhF'PJ@9"*3$8T(aNC)cK6FBfNYmA0dG$0aVqfVUH +JQT@4MBU(K)+!IAYkHAKjHAYmIAjrIhpqIRalHRKiH(KjHRYpIi+&LBb2NC+5NT+ +4Mib)JhpjFQTM@e*,3cN[*4iG*6G3E)HHXX$*cFh+aEkhVUHKQj@4MSZ)KB1!IRa +kHAPjHRYmIAjrIhpqIAalHRPiH(PkHhaqJB1&LBb0Mj!!N!#3!)q1M)U(K(pjFfa +PA9C24cdc+L3L+6P2DB5EVVc&bX[)`lffVUHJQT@4MBZ)KS1"IhelHRPjHRYmIAj +qIhjqIAalHRPjHAPkHhaqJ)+&L)U-MT!!N!#3!*!!Mik,L)4rHR0XC9jA8%P#1#m +T*L`j6@H!PUQi`FE)aX+lY+fQS*U9NBk,L)D$JAppHhTjHATlI(eqIRprIRjpI(Y +kHAPkHhapIi##K)D*Lif1Mj!!Mik0LSH%J(TeEQKK@P404$Xb+LFX18jRJ*DSYVr +%aX6!ZE1XTCqCP*!!MBU)KB1"IhelHRPkHRYmIAjrIhprIRemHRTjHAPkHhaqJ)+ +%KSQ,M)f0MBf0M)U)KB&mGh&VC&jB88P!0bmU+cG+BRZ4Sl+l`F2#[VLbUk@HQC@ +4MBU)KS1"IhelHRTkHRYmIAjqIhprIRemHhTkHATkHhapIi'$KBL,MBk2Mik0M)U +(K)&mGh*VC&jA88T!0c!X-$Y0C(b4Sl#k[m'rZlD`UD1GQ*53!)b+Ki@$JAppI(Y +kHRYlI(eqIhprIhjpI(YkHAPjHRYmIRq"Ji@)LSb0MSk1MBb+Ki5"I(GbE'CJ@P4 +04$Xe-M9!8'9lMk#YYEZpZlLcVULLR*H6Mib*Ki@$JB"qIAalHhYmIAeqIhprIhj +pI(YkHRPkHRYmIRq"Ji5'L)U,M)f0MBb+L)D#IRTdEfPMA9G34cmh0$Br6f0iM*f +UXlLkZEHbVDHJQjD5MSb*Ki@$JS"qIAalHhYmI(eqIRprIhjpIAalHRTkHhapIS# +#Ji@(LBU+LiZ,LiU*Ki5"IRTeEfTPB&Y96N8q1MNr6&pcKjLPVl@iZ,@aV+DKQjH +6Mib+L)D%JS"rIAalHhYlI(eqIRprIhjqIAalHhTkHRYmIAq"JS5'KiQ+Lib-M)Z +*Ki@#IhYfF@aRB9a@6dFr1MT!6Q"dKjHNVE1eYE1[UU5IQTD5MSZ*Ki@$JApqI(Y +lHhYlI(eqIRprIhjqIAalHRTkHRYmIAq!JS1&L)U,M)b-LiZ*Ki@#IhYhFQeTC&p +D8da&3$j$6PpaJj+IU+kaXV#YU+1HQC@4MBZ*Ki@%JS"rIRemHhYmI(eqIRjqIRj +qIAamHhYkHhYmIAk!JB1&KiL*LiZ,LiZ+L)D$J(ejG'pUC@"F9e"+480(8&eYISf +DT+UZVkkVU+1HQTD5Mib*Ki@$JS"rIRemI(amIAeqIRprIhjqIAemHhYlHhapIRq +!JB+$KBH)LBU+LSQ)KiD%JAjkGR*ZD@9KA&G46%P+8&YTH)H8RD@TUkZTTU+HQTD +5MSb+L)D%Ji&rIRemI(amI(epIRjqIRjqIAepI(amI(apIAjrJB1%KSH)LBQ+LSQ +)Ki@$JAjlH(4`E'KNAeT86dY,8&TSGi@5Qk+RUDQRTD'GQC@4MSb*KiD%Ji&rIRe +mI(amI(epIRjqIRjqIRepI(amI(apIAjrJ)+$KBD(L)Q*LBQ)Ki@$JApmH(4aE@P +PB&Y@8Np29&eTGS52Q*qMTUDNSTqEPj53!)f,LBH'K)1"J(pqIAamI(epIRjqIRp +rIRjqIAamI(amIAeqIi#"JS5&KSD(KiL)KiD&K)+!IAThFh"XD'4J@eG88eCGCh1 +!Lj5ERk+MSU#GQTD6N!#0LiQ(KS5$JS&rIRepI(apIAeqIRjqIRjqIRepI(amI(e +pIRq!JB+$KBD(KiL)L)H'KB1#J(jlH(9bEQYSC'"F@&CBA@9`I)H3!*DERk#JRTb +CPT12MBZ*KiD%Ji+"J(pqIAepIAepIRjqIRjqIRjpIAepI(epIAjrJ)#"JS5&KSH +(KiH(KS@%JS&rI(PfFh"XDQGMAeaD@PjQF(U%MC1BR*fGR*UBPC+2MBZ*KiD%Ji+ +"J(pqIRepIAeqIRjqIRjqIRjqIAepIAepIAjrIi#"JS1%KBD'KSD'KS@$JS&rIAY +iGA0`E@TRC'&IAf&QEACrL)k8Q*QDQTL@P*'2M)U)KiD%Ji+"J)"rIRjpIAeqIRj +qIRjqIRjpIAepIAepIRjrIi#"JS1%KB@'KSD'KB@%Ji'!IRakGh4bEfaTCQ*JB'& +QERCrKif5PTHBQ*D8NT!!MSb+L)H&K)1#JB"rIhjqIAepIRjqIRprIhjqIRjpIAe +pIAeqIRq!JB+#Ji5&KB@&KB5%Ji+"IhjmHRKfG(&[E@PRC@4PD@pfIS5+Mj+8PC@ +8NT'2M)Z*KiD&K)1#JB#!IhjqIRjqIRjqIRprIhpqIRjqIAepIRjqIRq!J)'#Ji1 +%K)5%K)5%Ji+"J(pqI(TiGR0aEfaTCfCRDQpfIB1*MC!!NT16NT'2MBZ+L)H&K)1 +#JB'!IhpqIRjqIRjqIhprIhprIhjqIRjqIRjqIRjrIi#"JB+$Ji1%K)5%Ji1#JB& +rIRelHAGfG(*[E@YTD@a`GRb#KiZ1Mj!!N!#3!)k0LiU)KiD%K)1#JB'!J(prIRj +qIRjrIhprIhprIhpqIRjqIRjqIRprIi#!JB+#Ji1$Ji1$Ji+#JB"rIRemHRPhGR4 +cF@pYE@pbGRZ!K)L,M)f1MBb,LSL(KS@%Ji+#JB'!J(prIhjqIRjrIhprIhprIhp +rIRjqIRjqIRprIi#!JB'#JS+#JS+#JS+"JB"rIhjmHhTjH(GeG(*aFA*dGhYrJiD +)LSZ,LiU*L)H'KB5$JS+"JB'!J(prIhprIhprIhprIhprIhprIhprIhprIhprIi# +!J)'"JB'"JS+#JB'"JB#!IhjpIAalHRPiGhCeG(9fH(YrJB5'KiL)L)L(KS@&K)1 +#JS'"JB#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)#"JB'"JB'"JB' +"J)#!IhpqIRemI(YkHRPiH(KjHRaqJ)+%KB@&KB@&K)5$JS+#JB'"J)#!J)"rIhp +rIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!JB'!J)#!J)#!IhprIRjqIAe +pI(alHhYlI(erJ)'#JS1$Ji1#JS+"JB'"J)#!J)#!J)"rIhprIhprIhprIhprIhp +rIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhjqIRjqIhprJ)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)!!!%*!!!%!!3!&!!!!S!! +"J&%!!!!!!"3!!!!!!!"#&PEZLk-!!%)8!!"#&3!mJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhpqIRjqIRepIAa +mI(amHhKhGhGiGh9dG(CkIAjrJ)'#JS+"J(jlHAKiHi##JS+#JS+#JS+#JS1"H') +p)"dJ(b-R,$)f1MSh1$BY'`B!#!`1$`S$#KNR-cJk2N0)6P0DAQ&J99&CB@adGA9 +fHAk&LBZ0N!#5NT+4N!#2MBU)L)Q0NC'4N!#3!*!!N!#3!*!!N!#3!)q2MiPkBe* +26Nj38eCDAQ"IAPpG98-`+5SV,#`Q)b`i3da38P9DAQ0RDfj`E'*LDh4qJi+#JiD +-NT@AQCZGRCfFQjQAPC56PCQEQjZEQjZDQTUDQTUCQ*1#E&pF@PTFAf0QD@YUD@T +SANXl0cJi1$Fa-6Y'89KDA@&PD@eaGAGiFQTZGhq)LiU,M)q9QTbHSD1NT+5MSU# +HR*ZERU+LSU+LSD'KSD'JS+#JS*U(FQGPBf0NCfYZFA*bFA*`C&"$38"!3$ii1N9 +3@Q"LC@KXF(4iHhemGA&hIiL2N!#3!*!!NT@DRk'MTDHTUDLSTU5LSD#JSkDQTUD +QTU@PTD@PT+5NT*k-GfeUD'KUE("cGRGfGRGfD99+5%C'4N-p3%Y9Af9RDQeaG(K +mIi"qGh9mK)f6Nj16PCQHSU5QU+UXV+ZVUDHPT+1NTkQTUDQTU+LSU+LRTkHRTk+ +4I(*[E'YYEh*fHATjHATkE9P16%T+5NC"4%jBBQKUEA"cGhYqJB*rH(KrKSq9PC@ +9PjUJSk@RUDZYVUfYUkQRTU@QUDZVUkZUUUUUUUQTUDQSU+@@J(CcEfj[FA4iHRa +mI(amF&e58%e-5dK#49"CBfPVER*eHAarJB1"HAQ"L*'APTDAQ*ZJT+DSUUbZVkq +ZVDZTTkDRUkfXV+bXV+ZVUkZUUUUUUDLEKATeF@p[FA9iHhamIAepFf"98Np06%P +$48pBBQPXEh*fHAarJS1!HAQ"LC'APjHAQ*ZJT+DSUUbZX+q[VDZTU+HSUkfYVDb +XV+bXUkZVUkUUUDQJM(eiFh"`FA0hHRapIAeqG@0A9&"168Y&4%e@B'KVER*eH(a +qJ)&rH(L!LC'APjHAQ*UISkDRUDZYVkq[VDbUU+HSUkfYVDbXV+bVUkZVUUUUUUQ +MNS*kGA&[F(*eHAYmI(apGfCC9&"06%T&3NP6A'9UE("cGRTmIS"qGh9qKiq@PjD +@PjQGSU5QU+UXVUq[VDbUU+HRUUfYV+bXV+ZVUkZUUUUUUUQQQBKpH(*[Eh"cGRP +lHhamH'YF9P*05dT'3%419f&SDQjaGAKkI(jpGh0jK)f9PTD@PTHDRk1PTkQVVDq +ZVDbUU+HRUDbXV+bXUkZVUkUUUUUTUDQRRiq"Hh9`EQj`G(GjHRTlH@pK@9426%T +(38"+8eaPD@a[FhCiHRamGh&fJ)Q5PT@9PCDBRD'MTDHTUkfZVDbUU+HQU+ZXV+Z +VUkZUUUUUUDQTUDQSSjH*IhKbEfeZFA4hH(PkHA*PA&G46%T)3Me%69CJCQPYF(0 +fH(TkH("aHS51PC58P*5@QCkKSk@RUDZYVDbUU+HQTUQVUkZUUUUUUDQTUDLSU+L +RTCf2K(eeF'eYER"dGRGhH(4TAPP66NT(4$ip4NpBBQGUER&dGRKiGh"YG(k)NT5 +6Nj18PTZISD1PTkQVV+bUU+DPTDHUUUUUUDQTUDLSU+LRTkHQTU+@LS*lFfpXE'e +`Fh9eGR9XBPa@8%Y(48!l2NK3@f0QDQe`Fh9fGA&UEAH!Lj+5NT+5NjDERU#LT+D +SUUZUU+DPT+@SUDQTU+LSU+HRTkDQTUDQTD5GN!#(JAPaE@YUE'paFh0dF'9I@e4 +15NC$2$P!59*GBfCTEA"bG(4bE'K`HB11NC'4NC'6PTZHRk'MTDHTUULQTD5MTDL +SU+LRTkHRTUDQTD@PTD@NSTQ,KB"hF'aUDQYZF(*bFQTJAPT568K%3$Ni3NT8AQ0 +QDQe[FA*bEQGSFRZ'Mj!!N!#3!*!!N!#5PjZGRk'MTDHSU+HPT+1MTUHRTkHQTUD +QTD@PTD5NT+5MRj1(K(jeEfYTD@YYEh"aEf9HAPK35dC#1c8j3NY@AQ*QD@a[FA& +`DQ9UG(f)MSq2MSq3!*1AQTbHS++NTUHRTU5MSU1QTkDQTUDPTD@PT+5NT+1MSk' +CM)@$I(0YDQKTDfeZEh"XB9eH@&"+4$mf-cY%69KHB@9SE'j[F'jRBfYeISQ0MBf +0MBq6PjQERCqKSk@RTk@MSU+MTDDQTD@PTD5NT+1MSk1LSU'GNB@%JAKaDfKSD'T +XE@j[DPpGAPK358)j-63q4P"CA'"ND'YYEfpXC'0XGAq*M)Z,Lib2NjDBQTbHRk' +MTD@NSU'KSk@PT+5NT+1MSk1LSU+LSD'IPiQ$JhjdEQPRCfKUDfaZEfPIA9pD8%F +q-Lmi38T69eYJC'KVE@pZDf0MEAD!LBU+LSU-N!#6PCHBQTbHS++NT+1LSD'LT+5 +NT+1MSk1LSU+LSD'KRjU0Ji1"HA*XD'GRD'PUE@p`D9pHB&T246FZ-ca%6P0@@f" +ND'YYEfjUBf0ZGi#)LBL)LSf4Nj5@Q*QERCqKSk5MSD#KSk5NSk1MSk+LSU+KSD' +KRjb4K)+$IR9ZDQGRD'PTDfj`F'KIAf"D6Mmb-6P!5Np59PYJC'KXEQpZD@*PF(L +#L)H(L)U1NC+8PCHCQjfISD+MSU'JSD1NSk1MSk+LSU+KSD'KS*f6KS'$J(PaE'K +RCfKSDQe`FR&RAepI@%Ni-cJq4de28PGEB'9TE'j[E@GLD(*kJiH'KSQ-Mj!!NC1 +9PTLDR*kJSU1LSD#KSk1MSk+LSU+KSD'KS+#GPBL#JS&mG@eTCfGRCfPXEh*dF'9 +HAPa53$Fk2N9,68p59eaKCQTYEfpYCQ0VG(f%KB@(LSf1Mj'5P*DBQTbHS+'LSD# +JSD1MSU+LSU'KSD'JS*qGPSL"JS*qGfpVD'GRCfGUER&dG@eKA9a94cdp3%9,6%e +29&KHBfGVER"[Df4PEhGrK)5'LBb0MSq4NT5@Q*QERCqKSD'JS++MSU+LSU'KSD# +JS*qGPBL"JS*rHA&XD@GRCQGUEA&eGR4SAPY@5d9%4%K-6%a1899DB'9UER"`EfK +MDR0kJB5&LBU,MBk2NC+8PTLDR*kIS+'JS+'LSU+LSU'KSD'JS*qGPBH"JS+!Hh0 +YDQKRCQGUEA&eH(G`BPY95dP-5de168e06e0BA@0SE'paF@ePCQphIB1'L)U,M)f +1N!#4Nj5@Q*UFRTqJSD#JSU+LSU+KSD'KS+#IR*+'JB+#JAadEfYSCfCRDQeaGAK +jG@GE8dY-8P*58Np16Nj49PYKCQY[FA*`D@9XG(Z#KSL*LSZ-MBk3!*'6PCHBQTb +HRk#JS+'LSU+LSD'KSD#JRjZ2Ji+#JS&pG@pVD@GQCfTZFRCjHRGV@e"+6PCB@&9 +46dj18&4CAf9UER&cFQeQDA*iJ)H)L)Q+Lib0Mj!!NT19PjQERCkIS+#JSU+LSD' +KSD#JS*kBM)+#JS+"IAC`DfPRCQGUER*fHATiEPa16&*BA9jB8e&26P"6@&eMD'e +aFh0[CfG`Ghq'KiH)LBU,M)k2NC+8PTLDQjfHRjqJSU+KSD'KSD#JRjf9L)+$JS' +"IAC`E'PRCQKVEh0hHATjF&e-6P9DB@0F9e048&"59eaLD'eaG(4aD@G[Ghk'KiH +)LBU,M)f1N!#4Nj@AQCUFRCkIS+'LSD'KSD#JS*kENB@#Ji+#JRefF'aTCfGTE(" +dH(TlHA"F6&&CA@4QB&T@8e&48eGFB@GYFA4eFfYSEhGqKSL(L)Q+LSZ0MSq4NT5 +@Q*UER*kIS+'LSU'KSD'JS*kBLi1%K)+#JRafF'aTCfKUEA&eHAYlH'jD6e9FB@C +QBejB99048eGFB@KYFA9fG'eTF(KrKSL(L)Q*LSZ-MSq3!*+8PCHCQjbGRU#KSU+ +KSD'KS*qFNSD$K)5$Ji&mG("XD'GTDfpcGhTmHhCV@90CAf9RCf9J@PG88P4AA'& +SEA&eGR4YDR&jJ)D(KiL)LBU,M)f1N!#4Nj@AQ*UER*kJSD+KSD'KS*qGPiZ%K)5 +$Ji1"HR0[DfKSDQeaGAKkHhPcCPTBA@4RCfKSBeeC9P489eaLCfebGACdEQYcHS# +'KiH)L)Q+LSZ0MSq4NT5@Q*QER*fISD'KSD'KS*kEN!#&K)@%Ji1$IhKbE@TSD@a +[FhGkHhYhE@9JA@&SD'KTD@CJ@eG99PKGBfKZFhChG@jYGAb"KSH(L)L*LBU,MBk +2NC+8PTHCQTZGRk'KSD'KSCqFP)L%KB@%Ji1#IACaE'PTDfjbGRPlHhPbDQKMB'G +UD'KTDQGLA9PA9eTIC'P[GAGiG@p`H(f#KiL)L)L*LSZ,MBk2NC+8PTHCQTZGRk+ +LSD'KS*fALi@'KS5%K)5"HR4ZDfTVEA&eHAYlHR9YE@eQCQTTD@PUDfPMAeYB@9a +JC@YaGAGiGA"bHRq%L)L)L)L*LSZ-MBk2NC+8PTHCQTZGS++LSD'JRTL0KBD'KB@ +%KB0pGR&XDQYYF(4iHRalGQpZFfpTD@TUDQTVE'TPB&aD@eeLCfebGRKiGA&eI)' +'L)L)L)Q*LSZ-MBk2NC+8PTHCQTZGS++LSD#HQSq'KiH&KB@&KB&kFfjVDfe[FhG +jHhTfF'peG@pUDQYVDQYXE@YPB&eEA&pNDQpdGhPjG(*jIi1(L)L)L)Q*LSZ-MBk +2NC+8PTHBQTZHS+'KS*kDMiD(KiD&KB@&JhaeEfaXE@pcGRPlHhG`FAGkGfpUDfY +VDfaYEfaQB&eGAQ&RE(&eH(TiFh9pJ)@)L)L)L)Q*LSZ-MBk2NC+8PTHCQTbISD' +JRTU2KiH)KSD&KBD%IRGaEQaYEh*fHATkGh"bHAamGQaVE'aXE'e[F'aQB9pIB@9 +UER0hHRYeFRQ!K)L)L)L)LBQ+LSZ-MBq3!*'6PCDBQCZGRk+KRTQ1KiL)KiD'KSD +&J(KcEfeZF(0fHATkGh&cHhjrI("VE@eXE@eZF(&XCQ*JB@0REA*fHRajFhGqJSH +*L)L)LBQ*LSZ-MBk2NC+8PCHBQTbHS+#HQ)f(LBQ(KiD'KiD#HR4`EQj`FhCiHRT +fFA4mIi+!G@aXE@eYE@j`FR"UC@*LBfCVF(9jI(YeGAb#KSQ*L)L*LBQ+LSZ-MBk +3!*'6P*DBQCUGRk#HQ)f(LBQ)KiH'KiH%I(9aEfj`FR9iHRPfFR9pJB1#H@jXEQe +YE@j[FA*ZD'4LBf9TER0hHhYhG(U"KBQ+LBL)LBQ*LSZ-MBk2N!#5Nj@@Q*QERTq +HQ)k)LBQ)KiH(KiH&IhGcF'p`FR9iHAPfFR4pJB1$I(&XEQjZEQj[FA0aE'GNBf9 +SE(&fHRYiG(Q!K)L+LBL)L)Q*LSU,M)f1N!#4Nj5@PjLDRCkGQT!!L)Q+LBL(KiH +(Ki*kGA&[F(&dGhPjGh*dIB'$K(jbE'jZEQjZEh"bFfpTC@4NCfY`G(KkHA4iJ)5 +)LSQ*LBQ*LBU+Lib0MSq4NT59PjLDR*fGQT'*LBU*L)L(KiH)KAjhFh&`FA0fH(P +hFh0lJB5&Ih4YEQpZEQj[F(*dF@YRC@4QDQjcGhPjGAH!KBL,LSQ*LBQ*LSU,M)f +1Mj!!NT19PjLDR*kGR*5,LBZ+LBL(KiL)Ki0lGR*aFA0eGhKiG(*jJB5&JAGZEQp +[EQj[F(&cFfjSCQ9QD'aaG(GiGACrKBL,LiU*LBQ*LSU,M)f1Mj!!NC18PTHCQjf +GR*L1LBZ,LSQ)L)L)L)D!HA9bFA*dGRKiGA*hJ)1%JhP[EQp[EQjZEh&bG("UCf9 +PCfTZFR9fG(CqKBL-LiU+LBQ*LBU+Lib0MSq4NT59PTLDR*fGQT+*LSZ+LBL)L)L +)L)4qH(4bFR0eGRGfFR0mJS1$I(&YEfpZEQj[F(&cFQeSC@4QD'a`Fh9dG(Z$KiZ +-LiU*LBQ*LSU,M)b1Mj!!NC18PTHCQjfGQjD-LBZ,LSQ)L)L)L)L$HhGdFR*dGRG +hG(*iJB1%J(9ZEfpZEQjZEh"bFh&VCf9PCfTYFA0cFRL#KiU0M)U+LSQ*LSU,Lib +0MSq4NT59PTLDR*fFQT++LSb,LSQ)L)L)L)H"HhGcFR0dGACeFR0mJi1$HR"[F'p +ZEQj[F(&cFfpTCQ9PD'YZFA*aFhk'LBf0M)Z+LSU+LSU,M)f1Mj!!NT18PTHCQjf +GR*L1LSb-LSU*L)L*LBQ(JAYhG(0cG(9fG(&fJ)1$Ih4[F("ZEQjZEh"aFh*YD@C +PCQKXEh&`F(H$L)b1MBb,LSU+LSU,Lib0MSq4NT19PTLDR*fFQj@-LSb-LiU*LBQ +*LBQ(JAYhG(0cG(9eFR&jJB1#Hh&`F@pZEQeZEh"aFR&XCf9PCQKVEQpZF(b'LSk +1MBb,LSU+LSZ,M)b0MT!!NC+8PCHCQjfGR*U5LSZ0M)Z+LBQ*LBQ*Ki*lGh4cFh4 +dFh"bI)+#J(G[FA&[EQeYEQp`FA*`DfGPC@CSDfeYEA1!L)b1MSf-LiU+LSU,Lib +0MSq3!*'6P*DAQCZGRCZBN!#+M)f-LiU*LBQ*LBQ(JRYhG(0cG(4bEh4qJS*qFfp +aF'pZE@eZEh"`FR"XCf9PCQKUE'YXGS+*MBq1MBb,LSU+LSZ,M)f1Mj!!NC18PTL +CQjfFQTH1LSf0M)Z+LBQ*LBQ*L)0mH(9dFh0cF@peIi+"I(*`FR"[EQeYEQp`F(* +aE'KQC@CSDQYUE(Q%LSq2MSf-LiZ,LiZ,M)b0MSq3!*'6PCDBQTbGR*U9MBZ1MBb +,LSQ*LBQ*LBQ%IRPfG(4cFh&[Gi#"JATaFA*`EfjYE@j[Eh"bF@aSCQ9PCfPTD'e +kKBU2N!#2MSf-LiZ,LiZ-M)f1Mj!!NT19PTLDR*fFQT@0Lik1M)Z+LSQ*LSQ*LBD +!HRGeG(0bF'phIi'!HA&aFR"[EQeYEQj[F(&bEQPQC@9QD'KRE(Q%Lj!!N!#2MSf +-LiZ,LiZ-M)f1Mj!!NT18PTLDQjfEQC@1Lik1M)Z+LSU+LSU*LBH#I(KeG(0bEfj +fIi'!H("aFR"[E@eYE@j[Eh"bEfTRC@9QCfGQDhQ$LSq3!)q1MBb,LiZ,LiZ-MBk +2N!#4NT5@PjQERCZCPBf,MSk0M)U+LSU+LSQ*LB9qHRGeFh*[EA4pJ(piFA*cF@p +YE@eYEQj[F(*`DfKQC@9QCQ9UH)+*Mj!!Miq0MBb,LiZ,Lib0MBk3!*'5P*@AQCU +FR*U@MiZ1MSf-LiU+LSU+LBQ+L)*mH(CdFQpYFRarIhKaFR0aEfjYE@eYEQp`FA& +YD'CPC@9PBfKfJBH0N!#3!)q1MBb,LiZ,Lib-MBk2N!#5Nj@@Q*UFR*UAN!#,MBq +1M)Z+LSU+LSU*LSU&IhYhGA0`EA"jIRjjFR*dFR"ZE@eYE@j[Eh"bF'YSCQ9PC'* +QFhk'M*!!N!#2Mik0M)Z,Lib-M)f1Mj!!NC18PTHCQjbDQ*+-M)q1MBb,LSU+LSU ++LSU)JhjkGR4aEQjfI(ejFh*dFh&[EQeYE@jZEh"aF@eTCf9NBf*NEhb$LSk3!*! +!Mik0M)Z,LiZ-M)f0MSq4NT19PjLDR*UBP)f,MSk0M)Z+LSU+LSU+LSU(JAajGA0 +[EA*kIATdFA4dFR"ZE@eYE@jZEh"bF'YSCQ4MB@&UH)#(M)k2Mik0M)b,LiZ,M)b +0MSq3!*'5P*@AQCZDQ*D3!)Z0MSf-LiU+LBU+LBQ*LSU'J(aiG(&YERClHR9aFh4 +bF@pYE@eYE@jZEh&bEQTRC@0KB'9cIB1*M)k2Mik0M)Z,LiZ,M)b0MSq3!*'6P*D +BQTZCPj1-Lik1MBb,LSU+LSU*LBU,LS9rHhGcEfe`H(TfFA*dFh*`EQeYE@eZEQp +`FR&YD@CNBPpKE(L!KSZ0Miq1MBf-LiZ,Lib-MBf1Mj!!NT19PTLDQTL@MiU-Mik +0M)Z+LSU+LSU+LSZ+KApkGR*ZEA0jH(0aG(9cFA"ZE@eYEQjZEh&cF@aSCQ0JAf9 +bI)+)Lif2Mik0M)b,LiZ,M)b0MBk3!*'5P*@AQCUCPj50LSk2MSf,LSU+LSU+LSU ++LiU&IhTfF@eZGAPeFA*eG(*aEfjZEQjZEQp`FR0`E'KPBPjJDhCpK)Q,MBq2MSf +-LiZ,LiZ-M)f1Mj!!NC+8PCHCQTL@NBZ,Miq0M)Z+LSU+LSQ*LSU-LS9rHR9`E@p +fGh*`Fh9dFR&[EQjZE@jZEh"bFh"VCf4JA@*[HAq&L)Z0Miq1MBb,LiZ,Lib-MBk +2N!#4NT59PjQBPT51LBb2MSf-LiU+LSQ*LBQ+LSb+K(jjG'pYFRGeF(&dG(0bF'p +ZEQjZEQj[F(*cF'YRBepHCR*kJ)D)Lif2MSf0M)Z,LiZ,Lib0MBk3!*'5P*@AQ*D +8NBU)MBq1MBZ+LSU+LSQ*LBU,M)U%IRKbEQpeH(4`FR9dFh*`EQjZE@jZEQpaFh4 +aE'GMAepTG(Z"KSQ,MBq1MBb-LiZ,LiZ,M)b0MSq3!*+6PCH@P*+-KiQ1MSf-LiU ++LSU*LBQ+LSZ-L)*mGR"[G(PjG("bGA4cFR"[EQjZEQj[F(&cGA*YD'4JB@YfI)+ +(LBb1Mik0MBb,LiZ,LiZ-M)f1Mj!!NT19PT55MSH'Lik1MBb,LSU+LSU+LSU,MBb +(J(TcEh0kHhTdF(0eG(0aF'pZEQjZEQp`FA4fG'jTC@"LERGpJiH+M)q2MSk0M)Z +,LiZ,Lib-MBk2N!#4Nj58NBk)JiH,M)f-LiU+LSU+LSU+Lib0Li4qGh&bHRepI(4 +`G(9dFh&[EQjZEQjZEh"bG(CeF'YPB@4[H(k%L)U0Miq1MSf-LiZ+LSZ,Lib0MBk +3!*'6NT!!MSL#K)Q+Lib,LiU+LSU+LSU+Lib0LB&kFh"iIhprI(4aG(9dFR"[EQj +YEQjZEh"bG(GfF@aQBQGbHRq&L)Z1Miq1MBb-LiU+LSU+LiZ-MBk2NC'1M)L"JBD +)LBU,LiU+LSU+LSU+Lib0Li4pGR&hIi+"J(TbFR9eG(*`EfjZEQjZEh"aFh9hH(0 +YCf4UGAb"KSQ-MSq2MSf0M)Z+LSU+LSZ,M)f1N!#3!)k,Ki"rJiD(L)Q+LiU+LSU ++LSU,M)k0Ki"iFhCqJi5#IhKaFh9eFh&`EQjZEQj[Eh"bG(CiH(0YCfC[H(k$KiZ +0MSq1MSf-M)Z+LSU+LSZ,M)f1Mif+Ki"pJS@'KiH*LSZ,LiU+LSU,M)f1Li0kG(G +qJiD&JAefFA4fG(0aEfjZEQj[Eh"aFh9hHRPdE@GUG(Z!KBQ-MSk2MSk0M)Z,LSU ++LSU,Lib0MBZ*KAjlJ)1%KBD(L)U,LiZ+LSU,M)f1Li4lGAKrJiD(Ji"kFh*eGA0 +aF'jZEQjZEh"`FR4fHAYjFfaTEhPqJiL,MBk1MSk0MBb,LSU+LSU+LSZ-MBU(Jha +kIi+#Ji5&KiQ+LiZ,LSZ,M)f1M)9mGRQ!JiD)KB*qGR&dGA4bF'pZEQjZEfp`FA0 +eH(TmHA*XEACpJSD*M)f0MSk1MBb,LSU*LBQ+LSU,LiL&JATjIS#"JB+$KBH*LiZ +,LSZ,M)f1M)CpH(b#K)H*Ki1!HR0bGA4cF@pZEQjZEfp`FA0eGhPmI(G`EA4mJ)@ +)Lif0MBk1MBf-LiU+LBQ*LSU,LSD$IRKjIAq!J)'#K)D)LSZ,LiZ-M)f1M)CpHAq +%KBH)L)@#IA9aG(9cFR"ZEQjZEh"`FA*dGhPlIAaeEh0lJ)5)LSZ-MBf1MBf-LiZ ++LBQ*LBU+L)5"HhCjIAjrIi#"Ji@(LBZ,LiZ-MBf1MB9pI)'&KSL)LBH$IhGbFh9 +dFR"[EQjZEh"`FA0dGRPlIAjkFh0lJ)1)LSZ-M)f0MBf-LiZ+LBQ*LBQ*KS0rH(9 +jI(eqIRq!JS5'L)U,LiZ-MBf0M)9qIi5'KiL)LBH$J(TbFR9dFR&[EQj[Eh"aFR0 +dGRPlIAppGR4lJB5(LSU,M)b0MBf-LiZ+LBQ*LBQ)K)&mGACkI(amIAjrJB1&KiQ ++LiZ-MBf0Li0rJSH(L)L)LBL%JAYcFR9dFR&`Efj[Eh"aFR0dGRKlIAprHAClJS5 +(LBQ+Lib-M)b-LiZ+LBQ)L)L&JAjiFhClHhYlI(eqJ)+%KSL+LiZ-M)f0LB1"KSL +(L)H(LBL%JAYdFR4dFh&`Efp[Eh"aFR0dGRKlIAq!I(KpJi@)LBQ*LSZ-M)b-LiZ ++LBQ)L)H$IhYdG(KkHRTlHhaqIi'$KBH*LiZ-MBb-L)1&LBL)L)H)LBL%JAYdFR4 +dFh&`Efp[F("aFR0eGhPlIAq"IRTqK)D)LBQ*LSU,Lib,LiU+LBL)L)5!IACbGAP +jHRTkHhapIi'$KBH*Lib-M)b+KS@*LSQ)KiH)LBL%JAYdFR9dFh*`F("`F(&bFh4 +eGhPmIS#"Ihf"KSL*LSQ*LBU,LiZ,LiU+LBL)KS&qHA0cGhPjHAPjHRYpIi'$K)D +*Lib-M)b*KSQ,LSQ)KiH)LBL%JAYdFR9dFh*aF("`FA&bFh4fH(TmIS#"J(q$L)Q ++LBL)LBQ+LiZ,LiU+L)L(JhjlG(*fH(KiH(KjHRYpIi##K)D)Lib-LiU*LSb,LBL +(KiH)LBL$J(TcFR9dFh*aF("aFA*cG(9hHAYpIi'"JS+'LSU+LBL)L)Q+LSZ,LSU +*LBL%IhYeFA4hH(GhGhKiHRYpIi##K)D)Lib,LSQ,MSf,LBL(KiH)LBH#IhKbFh9 +dFh*aF(&aFR0dGAChHAYqJ)'#JS5*M)b,LBL)L)Q*LSU+LSQ*L)@!I(GaFhGhGhG +fGhGiHRYpIS##K)D)LSZ+LSb2MSb+L)H(KiL*LBD"IRGbG(9dFh*aFA&bFh4dGRG +iHRarJB+#K)L0MSb+L)H(L)L*LBU+LSQ*KS&pH(*cGRGhGRCfGRGjHRapIS##K)D +)LSU*M*!!N!#0LiL(KiH(L)Q)K)"lG(*eGA4cFR*bFR*cG(9fH(PlIS#"JS1'M)q +1M)U)KiH(L)Q*LBQ*LBD"IAKbFRChGhCfGAChH(PlI(erJ)1&KiQ*LBf5NBk-LBH +(KiH)L)Q'JRjiFh0eGA4cFR*bFh0dGAChHAYpIi'#Ji5+N!#4MSb*KiH(KiL)LBQ +*LBD"IAKcFhChGRCeGA9fGhKkHhapIi'$KBH)LBf6Niq-LBL(KSH(L)L)K)"lGA* +dGA9dFh*bFh0dGAChH(TmIS##Ji1'MT14MSZ*KiH(KiL)L)Q*KS&pH(0cGRGfGR9 +eGA9fGhPkHhaqIi'$KBD)MT56N!#0LSH'KSD(KiL)KB&qH(0cGR9eG(0cFh0dGAC +hH(TlIAq"Ji1%Lj16Mif+L)D'KSH(L)L)KB"pH(0dGhGfGR9dG(9fGhKjHRYpIS# +#Ji5'MT@8N!#0LSL'KSD'KiH)Ki*qHh9cGACeGA4cFh4dGAChH(PlIAq!JS1%L*' +9NSk,LBH'KSD'KiH)KB"pH(4eH(GhGR9eGA9fGhKjHATmIAq"JS1&M*@9NBk+L)D +'KSD'KiH(K(pmGR0eGRCeGA4dG(4eGRGiHAYmIS#"Ji1&MC@8N!#0LBH'KSD'KSH +(KB"pH(4eH(KhGR9eGA9fGhKiHATlIAk!JB+%Lj5@NSq,L)H'KBD'KiH(KB&pH(4 +dGhGfGA4dG(4eGRGiHATmIAq"JS1%Lj59NSq,L)H'KBD'KSH&JAjjGA9iH(KhGR9 +eGA9fGhKjHRYmIRq"JS1*NTD8N!#-LBH'KB@'KSD(KB&qHR4dGhGfGR9dG(9eGRG +iHATlIAk!JB+$L*+@Nj!!M)Q(KS@&KBD'KB&qHhCeH(PiGhCeGA9fGRGiHAPkI(e +rJ)'#Kiq@PC'0LBH'KB@&KBD'KB&qHR9dGhGhGR9eG(9eGRGiH(PkI(jrJ)'#Kiq +9P*'0LBH'KB@&KBD&JRjlGh9iHAKhGhCeGA9fGhKiHATlI(jrJ)'&M*5@Niq,L)D +&KB@&KBD&JAjlGR9hH(GfGA9eGA9fGhKiHATlIAk!JB+&MC59NSk+L)D&KB@&KBD +$IhejGAGjHAKhGRCfGRChH(PjHRYmIAq!JB1*NC@9NBf*KiD&K)5&KB@#IhahGAG +iH(GfGA9eGAChGhKjHAYmIAq!JB1+NT@6MiZ*Ki@%K)@&KB5!IAThGhPkHAKhGRC +fGRGiHAPjHRYpIRq!JSD1Nj@6MiZ)KS@%K)5&KB0rIAPeGhPiH(GfGA9eGRGhH(K +jHRYpIRq!JSL2P*54MBU)KS@%K)@&KB*qI(KfHATkHAKhGhCfGhKjHAPkHhapIi# +"K)U4PC54MBU)KS@%K)5&K)"qHRGhHAPiGhCfGA9fGRGiH(PjHRapIRq!K)Z4P*1 +2M)Q(KB5%K)@&K)"pHRGhHRTkHAKhGhChGhKjHATlHheqIi#"KBf5P*53!)b*Ki@ +%K)5%K)&qI(KfH(PjH(GfGR9fGRGhH(KjHRYmIAjrJSL1NT55MSU)KS@%K)5%KB* +qI(PfH(YkHAKiGhGhGhKjHATkHhapIRq!JSL1NT56MiZ*KS@%K)5%JRppHRGhHRT +jH(GfGRCfGRGhH(KjHRYmIAjrK)Z3!*+6N!#-LBH&K)5%K)5$J(elH(GkHhTjHAK +hGhGiH(PjHRTlI(eqIi#$LT!!NT55MiZ)KS@%K)5$J(jmH(GjHRTjH(GfGRCfGhG +iH(KjHRYmIAk!KSb2NC+2LiQ'KB5$K)5%JhppHRGiHhYkHAPiGhGhH(PjHRTlHha +pIRq"K)Z3!*+6NSk,L)D&K)1$JRppHhGiHRTkHAKhGRCfGhGiH(KjHATlI(eqJSQ +0Mj'4MSZ)KS@%Ji1%K)&qI(PhHAYlHhTjH(KiH(KjHRTkHhapIRq!JBD0N!#5Nj+ +1LSL'KB5$Ji&qI(PhHAYlHRPiGhGfGRGiH(KiHAPkHhapIi1+MBq4NBk+L)D%K)1 +$K)5"IRajH(TmHhYkHAKiH(KjHATkHRYmIAjqIi'(MC!!NC+4MSU)KS5$Ji1!IAY +iGhPlHhTjH(GhGhGhH(KiH(PjHRYmIAq%LSf2N!#3!)k,L)D%Ji1$Ji1!IAajH(T +mHhYkHAKiH(KjHATkHRYmI(eqIi+)MBq4NT'1LiL'K)1$JRppHhKhHRYlHRPiGhG +hGhGiH(KiHAPkHhapIi@,MBk3!*!!MSU)KS5$Ji1$JRppHhPiHRalHhTjH(KiH(P +jHRTkHhYmIAjrJSH0Mj!!NC'2LiL'KB5$JRjmHhKiHRYlHRPiGhGhGhKiH(KiHAP +kHhapJ)@+M)k2Mik,L)D%Ji1$Ji*rIAYjH(YmHhYkHAKiH(KjHATkHRYlI(eqIi' +'M)k3!*'4Mib*Ki@%Ji*qI(TiH(YmHhTjH(KhGhGiH(KiH(PjHRYmIB#&LSb0MSq +1M)Q'KB5$Ji1#IhelHAKlI(alHRPjH(KiHATkHRTlHhapIRq"KSZ1Mj!!NC!!MBU +)KS5$JRjmHRKiHhalHhTjH(GhGhKiH(KiHAPkHhYmIi5*M)f1Mik-LBH&K)1$Ji1 +!IAakH(TmI(YkHRPjH(KjHRTkHRYlI(eqIS#&LSf2N!#3!*!!MSZ)KS5$JRjmHRK +iHhamHhTjH(KhGhKiH(KiHAPkHRYmIi5*Lib0MSk-LSL'K)1$Ji1!IAakH(TmI(a +lHRPjH(KjHRTkHRTlI(apIS#%LBb1Mj!!N!#2MBU(KB5#IRakH(KkI(alHRPiH(G +hH(KiH(KiHAPkHhYqJiL+Lib0MSf,L)D%Ji1#JS&qI(YjHAYmI(YkHAPiH(PjHRT +kHRYlI(eqIi+(Lif1Mj!!N!#1LiL'K)0rI(YiGhTmI(YkHRPiH(KiH(KiH(KjHAT +kHhf#KiQ,Lib1MSb*Ki@%Ji1$JRpmHhPjHhamHhYkHAPjHAPkHRTkHhYmI(eqJ)@ ++M)k2Miq2MBU(KB5!IAYjGhPmI(YlHRPiH(KiH(KiH(KiHAPkHhb!KBL+Lib-MBb ++L)D%Ji1#JS"pI(TjHRamI(YkHRPjHAPkHRTkHRYmI(eqIi+)Lif1Miq2MSb*Ki@ +#IRakGhKlI(alHRTjH(KiH(KiH(KiHAPkHRYqJiH*LSZ-MBf,LBH&K)1#JS&qI(Y +jHAYpI(alHRPjHAPkHRTkHRYlI(epIS#&LSb0MSq2Mik,L)D%IhalH(KkI(alHhT +jHAKiH(KiH(KiH(PjHRYmJBD)LBU,M)f-LSL'KB5$JS+!IAakHATmIAalHhTjHAP +jHRTkHRTlHhapIRq#KiZ-MBk2Miq0LSL'JAelHAGjHhamHhTkHAPiH(PiH(KiH(P +jHATlIS1(L)Q+LiZ-LiQ)KS5$Ji+"IhalHRPlI(amHhTkHAPjHATkHRTkHhYmIAj +rJiL,M)f1Miq1MBU)K)"pHhKiHRamHhYkHRPjH(PjH(KiH(KjHAPkI)'&KiL*LSZ +,M)Z*Ki@%Ji+#JAjmHhPkI(epI(YkHRPjHATkHRTkHhYmI(eqJ)@*Lib0MSk1MSf ++Ki0rI(TiHAYmI(YlHRTjHAPjHAKiH(KjHAPkHhf#KSH)LBU+Lib+L)H&K)1#JS" +pI(TjHRepIAalHRTjHATkHRTkHRYlI(epIi+'LSZ-MBk1MSk-LSD#IRajH(TmI(a +lHhTkHAPjHAPjH(KiHAPjHRYrJiD(L)Q+LSZ,LSL(KB5$JS&qI(YkHAYpIAamHhT +kHAPkHRTkHRTlHhapIRq$L)U,M)f1MSk1M)Q&JAelH(KkI(amHhYkHRPjHAPjHAK +iHAPjHATmJ)5'KiL*LSU,LiU*Ki@%Ji+!IAalHATmIAemHhYkHRPkHRTkHRTkHhY +mIAk"KBQ+Lib-MBk1MBZ)K)"pHhKjHhamI(YlHRTjHAPjHAPjHAPjHATlIB'&KSH +)LBQ+LiZ+LBH&K)1"IRalHAPlIAepI(YkHRTjHRTkHRTkHRYlI(erJiH*LSU,M)f +0MBb+Ki0rI(TiHAYmI(alHhTkHAPjHAPjHAPjHAPkHhf#KBD(L)Q+LSZ-LiQ(KB5 +#IhalHRPkIAepI(alHRTkHRTkHRTkHRTlHhaqJ)@)LBQ+Lib-MBf,LBD#IRakH(P +lI(amHhTkHRPjHRTjHAPjHAPkHRYqJS@(L)L*LSU,M)b+Ki@%JAemHRPkI(epIAa +lHhTkHRTkHRTkHRTkHhYmIi1'L)L*LSZ,M)f-LSL&JAjmHRKkI(amI(YlHRTkHRT +kHAPjHAPkHRYmIi1'KiL)LBU,Lib-LSH'JhpmHhPjHhepIAamHhYkHRTkHRTkHRT +kHRYmIS+&KiL)LBU+Lib-LiL'K)"pHhPiHRepIAalHhTkHRTkHRTjHAPkHRYlIB# +%KSH)LBQ+LiZ-M)Q(K)"pHhPjHhepIAamHhYkHRTkHRTkHRPkHRYlIB#%KSH(L)Q +*LSZ,LiQ(KB0rI(YjHAYpIAemHhYkHRTkHRTkHAPkHRTlI(f"KBH(L)Q*LSZ-M)Z +*KS*qI(TjHRapIAamHhYlHRYlHRTkHRPkHRTlIB#%KBD(KiL)LBU+LiU)KS5"IAa +kHATmIAemI(YlHRTkHRTkHRPkHRTlHharJiD(L)L*LSU,M)b,L)0rIAYkHhapI(a +mHhYlHhYlHRTkHRTjHRTlI(q$KB@'KSH)L)Q+LSQ)KS5#IhalHRPlIAepI(alHRT +kHRTkHRTkHRTlHhaqJB@(KiL*LBU,M)b,LS@"IRYkHhamI(amHhYlHhYlHhTkHRT +jHRTlI(q$K)@&KSD(KiL*LBQ)KS@$J(emHhPkI(epIAalHhTkHRTlHRTkHRTlHha +pIi1'KiL)LBU+LiZ,LSD#IhalI(epI(amHhYlHhYlHhYkHRTkHRTlI)#$K)5&KBD +'KiL)LBQ(KS@$JAjmHhTkHhepIAamHhYkHRYlHRTkHhYlHhapIS+&KiL)LBQ+LiZ +,LSD$J(emIAepI(amHhYlHhYlHhYkHRTkHRTlIB#$Ji5%KB@'KSH(L)L(KB5$JS" +pI(YkHhepIAemHhYlHRYlHhYlHhYlHhamIS'%KSH)LBQ+LSZ,LBD%JAjpIRjpI(a +lHhYlHhamHhYkHRTkHRTlIB'$Ji5%K)@&KSD(KiH'KB5$JS"pI(YkHRapIAemI(Y +lHhYlHhYlHhYlHhamIAq$KSH)L)Q*LSU+L)@%JRpqIhjpI(alHhYlHhamHhYlHRT +kHRTmIS'#Ji1%K)5&KBD'KiH'K)1$JS&qI(YkHRapIAemI(YlHhYlHhYlHhYlI(a +pIAq#KBH(L)Q*LSU*KS@%JS#!J(pqIAalHhYlI(amHhYlHRTkHRYmIi+#Ji1$K)5 +%KB@'KSD&K)1#JS&qI(akHRapIAepI(alHhYlHhYlHhYmI(apIAq#KBH(L)L*LSU +)KB5$JS'"JApqIAalHhYmI(amI(YlHhYkHhYpJ)+#JS1$Ji5%KB@&KSD%Ji+#JS& +rIAalHRaqIRepI(alHhYlHhYmHhamI(apIRq#KBD(L)L*LBQ(KB5$JS1#JApqIAa +mHhYmI(amI(YlHhYlHharJB+#JS+$Ji1%K)@&KB@%Ji+#JB&rIAalHhaqIRepI(a +lHhYlI(amI(amI(epIRq#KBD(L)L*LBH&K)1#Ji5$JB"qI(amHhamI(amI(YlHhY +lI(k!JB+#JS+$Ji1$K)5&KB5$JS'"JB&rIAalHhaqIRepI(amHhYlI(amI(amI(e +pIRq#KBH(KiL)L)D%Ji+#K)@$JB"qIAamHhamI(amI(YlHhYmIAq"JB'#JS+#Ji1 +$K)5%K)1#JB'"JB"qIAalHhaqIRepI(amHhYmI(amI(amIAepIS##KBD(KiL)KS5 +%Ji+$KB@$JS"qIAamI(amI(amI(YlHhapIi#"JB'#JS+#JS1$Ji5%Ji+"JB#!J)" +qIAalHheqIRepI(alHhYmI(amI(apIAeqIS#$KBD(KiL'KB5$JS1%KB@$JB"qIAa +mI(amI(amI(amI(aqJ)'"JB'"JS+#JS1$Ji1$JS'"J)#!J)"qIAalHheqIRepIAa +mI(amIAepIAepIAjqIi'%KSH(KiH&K)1#JS5&KB5$JB"qIAepI(amI(amI(amIAk +!JB'"JB'"JB+#JS+$Ji1$JB'!J)#!J(pqIAalI(eqIRepI(amI(apIAepIAepIAj +qIi'%KSD(KS5$Ji+#K)@&K)5$JApqIAepI(amI(amI(apIS#"JB'"JB'"JB+#JS+ +$Ji1"JB"rIhq!J(ppIAamI(jqIRepI(amI(apIAepIAepIRjrJ)+&KSD'K)1$JS+ +$K)5%K)1#J(pqIRepI(amI(amI(eqJ)'"JB'"JB'"JB'#JS+#JS'!J(prIhq!Ihj +pIAamIAjqIRemI(amI(epIAepIAeqIRprJB1&KS@%Ji1#JS1%K)5%Ji1#J(pqIRe +pI(amI(amIAk!JB'"JB'"JB'"JB'#JS+#JB#!IhprIhq!IhjpI(apIRjqIAepI(a +pIAepIAepIRjqIRq!JS5&KB5$JS+#Ji5%K)5$Ji+"J(pqIRemI(amI(apIS#"JB' +"JB'"JB'"JB'"JS+"J)"rIhprIhprIRepI(apIRjqIAepIAepIAeqIRjqIRjqIi# +"Ji@&Ji1#JS+$K)5%K)1$Ji+"J(pqIAemI(amIAerJ)'"JB'!J)'"JB'"JB'"JS' +!J(prIhprIhpqIAemI(eqIRjpIAepIAepIRjqIRjqIRjrIi'$K)5$JS+#JS1%K)5 +$Ji1$JS+"J(pqIAemI(apIAq!JB'"JB#!J)'"JB'"JB'"JB#!IhpqIRprIhppIAe +mIAjqIRjpIAepIAeqIRjqIRjqIRprJ)+%K)1#JS+#Ji5%K)1$Ji+#JS'!IhjpIAa +mI(epIi#"JB'"J)#!J)#"JB'"JB'"JB"rIhjqIRprIhjpIAamIRjqIRepIAepIAj +qIRjqIRjqIhq!JB1$JS+#JB+$K)5$Ji1$JS+#JS'!IhjpIAapIAeqJ)'"JB'!J)# +!J)#"JB'"JB'"J(prIRjqIhprIRepIAepIRjqIRepIAepIRjqIRjqIRjrIi#"Ji1 +#JS+"JB1%K)1$Ji1#JS+#JB"rIRepIAepIAk!JB'"JB#!J)#!J)#"JB'"JB'!J(p +rIRjrIhpqIAepIAeqIRjqIRepIAeqIRjqIRjqIRjrIi'#Ji+#JS'"JS1$Ji1$Ji+ +#JS+#J(pqIRepIAepIRq!JB'"J)#!J)#!J)#"JB'"JB'!IhprIRprIhjpIAepIAj +rIRjqIRepIAjqIRjqIRjqIRprJ)+$JS'"JB'#Ji1$Ji1$JS+#JS+"J(pqIAepIAe +qIi#"JB'!J)#!J)#!J)#"JB'"JB#!IhprIhprIRepIAepIRpqIRjqIRepIRjqIRj +qIRjqIhq!JB1$JS'"JB'#Ji1$Ji1#JS+#JS'!IhjqIAepIAjrJ)#!J)#!J)#!J)# +!J)#!JB'"JB#!IhprIhpqIAepIAeqIhjqIRjqIRjqIRjqIRjqIRjqIhq"JS1#JB' +"JB+$Ji1$JS+#JS+#JS'!IhjqIAepIAjrJ)#!J)#!J)#!J)#!J)#!JB'"JB"rIhp +rIhjpIAepIAjrIhjqIRjqIRjqIRjqIRjqIRjrIi#"JS+"JB'"JB+$Ji1$JS+#JS+ +#JB#!IhjqIAepIRq!J)#!J)#!J)#!J)#!J)#!JB'"JB"rIhprIhjpIAepIAjrIhj +qIRjqIRjqIRjqIRjqIRjrJ)'#JS'"JB'"JB+$Ji1#JS+#JS+#JB"rIhjqIAeqIRq +!J)#!J)#!J)#!J)#!J)#!JB'"J)"rIhprIRepIAepIRprIhjqIRjqIRjqIRjqIRj +qIRprJ)'#JS'"JB'"JS+$Ji+#JS+#JS+#JB"rIRjqIRjqIhq!J)#!J)#!J)#!J)# +!J)#!JB'"J)"rIhprIRepIAeqIRprIRjqIRjqIRjqIRjqIRjqIRprJ)+#JB'!J)# +"JS+#JS+#JS+#JS+"JB"rIRjqIRjqIi#!J)#!J)#!J)#!J)#!J)#!J)'"J)"rIhp +qIAepIAeqIhprIRjqIRjqIRjqIRjqIRjqIhq!JB+#JB#!J)#"JS+#JS+#JS'"JB+ +"JB"rIRjqIRjqIi#!J)#!J)#!J)#!J)#!J)#!J)'"J)"rIhpqIAepIAeqIhprIRj +qIRjqIRjqIRjqIRjqIhq!JB+"JB#!J)#"JS+#JS+#JS'"JB'"J)"rIhjqIRjqIi# +!J)#!J)#!J)#!J)#!J)#!J)#"J)#!IhpqIAepIAjrIhprIRjqIRjqIRjqIRjqIRj +qIhq!JB'"J)#!J)#"JS+#JS+#JB'"JB'"J)"rIhjqIRjrIi#!J)#!J)#!J)#!J)# +!J)#!J)#"J)#!IhpqIAepIAjrIhprIRjqIRjqIRjqIRjqIRjrIhq!JB'"J)#!J)# +"JS+#JS+"JB'"JB'"J)"rIhjqIRjrIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ihp +qIAepIAjrIhprIRjqIRjqIRjqIRjqIRjrIhq!JB'"J)#!J)#"JS+#JS+"JB'"JB' +"JB"rIhjqIRjrIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhpqIAepIAjrIhprIhj +qIRjqIhpqIRjqIRjrIhq!JB'"J)#!J)#"JS+#JS'"JB'"JB'"JB"rIhpqIRjrIi# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhpqIAepIAjrIhprIhjqIRjqIhpqIRjqIRj +rIhq!JB'"J)#!J)#"JB+#JB'"JB'"JB'"JB#!IhpqIRjrIi#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J(pqIRepIAjrIhprIhjqIRjrIhprIRjqIRjrIhq!JB'"J)#!J)# +!JB'"JB'"JB'"JB'"JB#!IhprIRjrIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(p +qIRjpIRjrIhprIhpqIRjrIhprIhjqIRprIhq!J)'"J)#!J)#!JB'"JB'"JB'"JB' +"JB#!IhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIRjqIRjrIhprIhp +rIhprIhprIhpqIRprIhq!J)'"J)#!J)#!J)'"JB'"JB'"JB'"JB'!J(prIhprIhq +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIRjqIRjqIhprIhprIhprIhprIhprIhp +rIhprJ)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'!J(prIhprIhq!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)"rIhjqIRjqIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!Ihq +!J)#"JB'"JB'"JB'"JB'"J)"rIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!IhpqIRjqIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!IhprJ)#!JB'"JB'"JB' +"JB'"J)#!IhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(pqIRjqIhprIhp +rIhprIhprIhprIhprIhprIi#!J)#!J(prIi#!J)'"JB'"JB#!J)'"J)#!J(prIhp +rIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhjqIRprIhprIhprIhprIhprIhp +rIhprIi#!J)#!J(prIhq!J)#"JB'"JB#!J)#!J)#!J(prIhprIhq!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)"rIhpqIRprIhprIhprIhprIhprIhprIhprIhq!J)#!J)" +rIhprJ)#!JB'"JB#!J)#!J)#!J)"rIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!IhprIRprIhprIhprIhprIhprIhprIhprIhprJ)#!J)"rIhprJ)#!J)#!J)# +!J)#!J)#!J)#!IhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhp +rIhprIhprIhprIhprIhprIhprIi#!J)#!IhprIi#!J)#!J)#!J)#!J)#!J)#!J(p +rIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhp +rIhprIhprIi#!J)#!IhprIi#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIi#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)# +!J(prIhq!J)#!J)#!J)#!J)#!J)#!J)#!J(prIi#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J(prIhq!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhp +rIhprIhprIhprIhprIhprIhprIhprIi#!J(prIhprJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J(prJ)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhp +rIhprIhprIhprIhq!J(prIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)" +rIi#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhq +!J(prIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ii#!J)#!J)#!J)# +!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ihq!J)#!J)#!J)#!J)#!J)#!J)"rIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J(prJ)#!J)#!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J(prIi#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhq!J)#!J)# +!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIhq!J)#!J)#!J)#!J)#!J)#!Ihp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)"rIhq!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!Ihq!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)!!!%0!!!%!!3!&!!!!S!!"J&%!!!!!!"3!!!!!!!"$&PE +ZLk-!!%-8!!"$&3!mJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!Ihq!Ii#!J)"rJ)#!J)#!J(q!J)"rIhprIhprIhprIhprIhp +rIhq!IhprIhprIhprIhprIi"rJ)#!J(q!J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB# +!J)"rIi#!J)#!J(q!JB#!JB'!J)#"JS'"JB'#JB'"JB'"JB'!Ii'!Ii"qIi"rIhp +rJ(prIhq!Ihq!IRk"J(prJ)"qJ)&rIi"rIi"rJ)"rJ)&rIRprIRjrIhpqIi"rJ)" +rIi'"Ii#"J)#!J)"mIS4pH)"rHhemHhepHhTqIAYqIAk!IAf"JAjqJ(prJ(jqJAp +rJ(q!J(jrJ)"rIi"rJ)"rIRjqJ)"qIB'!Hhk$J(aqJAppIRq!J(eqJ)"rIhjrJ)# +"Ihb#KRYpKi&pJS#!K(pqKSCrJ)@%JS&rJB&pJS9pIB@&IAb!K)*pI(q#JAf"KAp +lJBU!GB+(Hhb'J(f&Jhf"JRk!K(pkJBCiI)f!GB'(K(q"KAPkK(TqL(KeLiYcFSU +*G(q)H(b&Ii1#G(H1MRCbJB'%M(aTHTH-DhQ5I(H0IR+(KA0rM(PcIhk'L(GcHBH +1G(',J('"JhGmLRT[KRTfN!"pFi4kI)0mIAafJBClIS4pJB4pKiTkIB@*MhaZIBU +-I'jrLi&cFRamJRe`LBP[HiU*H@k%QS&VK*L+EhfEJ'f(MB&iFAD,LA"jI@q0P'K +bQiP`K*5)JBH%LBejHC@9Df@JS9YMPB4UG)'"IB&pFAq6JR+(M(+#RS&aMT4rI)k +*H)#%GAb#GhD!IfYlQR*NRBGIMTTVGTk*GC!!LA#2RR"ZPB*UJSC[GiKbG)afCS1 +8Hf4qQ)CaJ*H)HC'CJ(Q'MB*fIB&dGAecGACbH@pTG)5+E'Q3!)YlMSb$JS#0N!# +&IAD'N!"eFBGjEAYcF(pUB)1$FhTmK)GpL*+0LB#(PjH(G)@8JAaeDhjfBh9dCQe +mKQeBKjpjFAU(R*1"L*',M*Z@HhQ(JRq%G@&XK(GID(KlHhT`GT@'ECLSGR@XVS& +qQC@+K(Z2Lf*VHQpfE&plI'CbK(aXGiq-IAf-R*f3!)Z9Q)Z-L(Z'MB*XA(#$GQ9 +NF(TfI(pbDA@9PATpP+1@J*UUJ@b#R*PZBhpjDR4dDQp`GBTe8AQMG&L,RB+%QTk +2LBq+L)U#JieeD)Cf8'U-HeGMJ(GZEh9pGA#'RiefMDLGIRf6Lhf3!*&aERajF'j +XEhTfE(CfERGbGi0aHTZ5K*DGN!"rKjf'HjL-FRC`FApbDA&pH9eYL@pKH(KVJjD +"KU+9LTL5J)LINAQ0KPejS(eEFSCmGR4RCRKfB'b0K(L8Qib@QBZ*LSH-NiplE)@ +1ER@5Gf+!HPpaFeaUIR"`N!#@JSqLN!##N!#6Kj@BJBH(H(q,L(KeI'pVH@YCA@C +jFf&rR)q%N!#GNi5$LD'FG)#IKh12NAYpHfpSF(0E@QYHCBPpDC+NL(k2PRGpUjK +pNS*bS*p[HTKk9Qk$CP"GEQ"AH)YhFSDESS9YKk+MMhQ-QBk8LAZHS@PKIR9TE&Y +1@f0`KAjUFjfVJ'b3!*b6PBarKD5JHC1hKQ5%K'*QD%a@Ef*GGiPkGT1@H)1KNB1 +9QBk6S*'B[TGbSip(CT&D2fPF3h@&AR1@FQQAQQjZVV4cGkZTNTkfU)U"K)&cCQ0 +F9&9FC'KZHRjUCiq3!'9rVj4jN!#HT,5PMDh(Le5)U&`pEfP#@@YHFhY9AT9r9iL +CFiZNKSqrV)HhcTU2Q(TaJRP33&aMC'Y4AS"TAQG[K(GmR)0lT,+PRUh%UiZ6M(e +hCeY989KRDPYADA"L@(D+EA@@MRqJ`jkAeX@EVjZ&LRGf@$TTFdG2DQKE@@9DAhe +bFiU"MV@JN!$*eUHZc*YYKjCk9dY@DR&66QaM694TEPYQLhj`Sl@ATFV5ZE+hT*1 +)IATS69TfC8T2Bej29eC9CAKfBhqVTCbUaFc$bl'BPiD(L@C2BQpI9PK@68K@A99 +5BS0eAk$"QD6%dZ'mP,'cMAjeE@*QFf**8ej(29*94e0kG9"dUU5UZ,6@jVbNVlU +9FSGbAi*R2PYM8$mV6Q4'6P9FGhH-Tk5c[GMXVU$AYB1)I(KeC'PI89K006e(89X +i4RaXFCLGVF62h-r"aV+QT)"cKS"Q@@4U6$ir0%G8-M9HC@9fQ+QUa0,1f-V!`k+ +CPR4qIeeeIN)e@88M3PSi+8pZDhLBSV[CbF6HcmE#NTUVIQKhIR0H66dl46%Y8$3 +H@faHL+5DYq65Zq$SZl1bQ*H*DA+$G%Xp6NFX)N&*)cGKB@4qTX2!aG21iH$%Z)b +8[RT'G(YQB$`U26XR,$3S5R"@G+UYaq(FdZ$dflLVSCf2A&"fE%3c,bJX3$)E,8a ++8(D'PFRB[0([f0RU[DZhQj9iBiec9P8Z-8Bc("NX1#dd@(f8Ql(#[Z(cd-r0Ylq +eJQq6QQ&1CN3X6MiB-6dV1NpULk1XUEhFiF@qdlbHRi"bJR"dG%a19P0F0b&,8$j +$5(5IPTbVY-l8a,bmZDfaQR@@TA9aH&T)@9`K&dFX%6G(9B5-K+R)cXh0cFR&X+f +YLBfCJ(TT8Pe0068k4LNJ9PGFMSqAXlR2[kE&[U1JRTk+KTb%H)*cF8i`683Y,bi +[0f@%BhZcVE#p`mh"aFQ`[VH5Y+KaI'a155dS+KFE*#ip8S"qJX6'SVE+cF1MX,b +*Kkk6FBCq8&9N5$Y)589$9Q4QJBq8Tk@LXE#VSkLRPD5DKCZEL)*M594'1cdH+$i +Y8'pKH*QPUlV9`lAIhm#cVmHVIUUJ8P&H0L-V&4Fr,aK+HAQ"PTZNZEbjYVZjTkq +aS+#6P+1"Bf0G@8)p68&(5%aeI("hKjkDMjHQ[E1JTkbkXDDmPeeeI$id65J826i +V5fKJHCk@P+h+aVE5flLZb-fSN!#PNeTH5b9#1K)N1640DQeRFC!!PBQLZ*kC[X# +QXF5`UE@PNT&qA&CM6bBm@5ibCP*3H@9@JU1@Ll$&VlRFem$"Xk@4GRC1+dJk&bF +q2c93C@'1MfHIekbCb1+lS-MBXTUGMfKRBMT*@b!XEP%kD(&8E)YXDU1GFC[#S*U +jYUHecl"pNTG`DQaG6da69dC0CNa1EP4SViPNXm'G[-qiUm2#LSb2A9KH98%hB9B +TA)03AC0hCTbeMSr-Zi1[`D'`SSClFS&f9NYAC'9VA&4lGPCKE("ZFT+4JTbPQk' +EXG+YLCUPNheVHAa18QTA9f*&,9*V9@5'HB5dZDQ`YlV!Y*D5T)9VE9pK@&*939e +b9eYICA4dN!#VQSkF[VD0TVb0JT1-M(&JHQYLEePTENYNE9"+8@jlEAkDQC+KXD5 +Vf,5([-+@Tk&`F(KK6e&,2Mib06e)AeC`RC!!M,'qT+M0[SqXaj@1U)alGh&V8R9 +e3'YV0QD)C@1,N!"THk'&JT*iJk19MCHJRi#3!+D!G)4mEPpB8dYMDNPLLfpMMj0 +qQlqQR,Hca-HMRj!!L)pL9Qe@3cNk6$p(A%YaRA4`Tjf&UUf3!++XT,HhRSb6S(a +IJTPi@'CVAhCY8'YVAR4Y9f4rG'*rQBqCYDfPZl+5KBkHLPp@G'%mAQ0$6ejaC&5 +3!*U&RTQTZVEIbjHcUiZ(CQ&cAdp+4dp09Pj4CRGQG)*fJjq'E+qlL,I)RTqKQ)U +,Q)CpK@pkM'aN@NaUDP064Q5&APk+N!#9Ul1USlUQJT+-Ghq$CeahG@KV9PeqFQU +#M(q3!,+DL+qiSk#6K)b!@P&VF&CFG@9RKhKSIh0ZLB*lHhU4LS#@N!#5UD#&L+# +9GB1AKS11Ji#1FPpkANYQ8e9aA@'$HR58R*ZAN!#VX*H'J)L@NRPaMjKaCQjYG@C +VGQ"pPA+&PhQ1Rhq%Nh9pRRYFEi15I@f2JQH*K'GYGhGJCiaeFjTqFD+EKk+8LU@ +DJiHAQBZ"HS+2ENpH9Pjc6%e`DfTmJh5*Rhk'YCTrSjL'Sj@$Piq(PjChEhTZGh& +NI(akG'#!LQThF@L2Kh+#IB@3!)H'L*Q@JT!!JA@&F('$CA@KDQ@JIh'3!)1!LBH +0LTfKJSf,FibTMeKIK@92D9p6EhKGDSGcGiL"J)@JTSQ@[UQ3!+bHMU'NMhQ-F6j +`INpKD'GE6Bq!5RpX8+16AjH[P*!!SjerNkU*Lj!!FR*LCSTbAA+*J@U(NA#+Nf* +YR*@2XijRRU'#L)"bE(ab4e0[9&4[FA&MBT'+F*&dGUf0T-fiUil%fAQJ`S@'EPY +Y@9GF8dj)@eP1F&j,J(*8J+'ENU5kXjqEREUmIB'EI@TmM&eCNh*1J)YT8fL0CNL +,VipqMkfINCq4S+P[Dijk6dq0E6"fDNChBPTlHfYHQVq'Vp+@XFR!b)qFY9U'Y%* +5INj188p*A(p+-h&k@&Z!Q)@)VTq)RV5pRSZMTD5)G*q5C(')HQf(GN&GK(9PAfb +#JRq%K*HUQif+Pk9eBSGZA@9(@PT)LAp%DiQ%N!#9P*He[k@bXUc-XhGrUCYH6Qp +K05a5HPNpF(&CIhGLHAPiJjPlAm#jE,'hKlh"N!#5QiarJhGNNSY,E(pIBfeZ8cp +ZFeb,QikNRT+VZCPcHSCq9bKDN!"C2(''GiZJNSDZVA5DXBqYXBqIPhZ4N@0CAdF +rAf-dCDeRAkUPJAfCKeGbI9P[Lj!!N!#&TEHSX+1IU(aRHS@)D@&fDfalFR"XA@4 +hE&H!YCD*[lQ5U+*cGQa35c8mAQ"QFS#HTk2%c*LF[Tb"L)Q8MfTGHiG[AeYHD&T +-Bi#-P)k8TS1"ThCGIfe-5PpkFAUBMkM4XTkcXTf%J(T2B)9H6h"dD(YmAAkdI81 +*`ijhP)b8T(K6D(TL5P"15@q,Gh@efkUak,0cVGCf1hGqA'05@B1"Bf'AP9aaL(Y +qIRpeHDZL8(DM5'QJ5&+6JAQ6UCZEi-jiR,f'GATN8&YQ4%@+P&acTj&iKT1$Kjq +$Eib-KSTaEReI8QpN@PjIKi0KSHA&QU62[(Z(N!"bDfC95Qb+FRfCMi9mMijMISa +RC@4[M)KL5h@$2NZ*CQ'6JSl5dF2IfE'TSBU)8bjX@b"5E'4rGS+LP(GVQDYSCk5 +*3@HcDc+&H6YCF'0TPkKhR1'hVGDeQDQ*F(TYAQ*YD@4YI*1KNB@2JQChG8G9FP3 +p8A"RBRKcFS1BQj'VYUZcTkc5`CbIQi"iI'G9Bh"B9B&p@RZEGPPLD9jDF(&8DBG +`KTq!L*Z8PSU'MhZBZ)H6[l#STk'1GCDK4%#LILj-IP`PGjNcAT9BGSjNKTq"LTQ ++GhQAI&k5QAGcNm#ANpZXLXD`L*Z9Gf9N4bG)9L8[G(a@Dk1PMCUZP)HMM@*rKPG +AD@GUJ+#'HDqRR,UVYmfQJiQ8GdeAA$Bq6MpFFfjkQU1$RFLEIBPcChKJ5PGFDRT +ZECA$ViHbil1)`XKrJ(T'8Q!r4QK21hD2BA+cX)b@QjfMJ)L$8A119%amGfb'FQQ +FTk'SRBf&RBT0AR45DRp,@U#IK*qbQjkXMSbPJfeXEB"QARGKEBaT@A#0MA+#Ihk +LM'jmHfekIQCdLBDCRS#%b0b3!(bbUATSBfGaEP41G)0aISQ(LS#(Heq)M%T4B&H +!H8eeRiq*QT5Dam1,MF$&Q(f5SSTI5Q0RCA0KHieGKlGjDjZLF$a9C$T,A6YBGQf +6SC+ZcXLMRX,&TB*mUE"S9S'$M'meG*4YJ(&dL'PhL@T8A'eD3NG0@9aQMj@4XVU +XVG,JQ)h%UB4mKTYeGjCiHiQ'H95"Md&`Kb!rH%Be2&&D9@jFDjq@Pk1ki-Lk[+[ +-b*UUTRYeJ)&TETed1A5@9%4cE%P54M&ME6KDKQPXNUUbTDDmZUH8NDk[MQp`Pi9 +NIS1&NhKbP++-KCU&G(KYD6SrH%8ZB&*3H(b"KT1fP(1`ZReaQDk0H*'FKS+NVD+ +CQE@cLAk4RiG@18",0a-22&Sl5SebGFkkRVl"ZU#5NTDbR'GVLi*hKhU$P(YpJiL +0Ij9i8R4S894*4PeV69'(VV@YY,1V[jk'TS"38dpHC%SqCj'"JkZpTCDp`*5FUCD +#E9pQGP8R29GC@%eSLRq1TCfYYTfLSSL3!'e9K'P3M+@(JDl!STQFN!"hE'")@9% +d89a6CA&VFk#YLj5`Ul1p[-+fVVq24'&a3Lm`2eCF5fUVIPZka@eVPiGZK*@%H(f +3!+UmRh#2YTG[BRL2FQH(Ff9m@&f&@e*iG(L$DRHiY)U6UTf-U*0DIhp#DT!!C&G +qKR+'Nh*aPTD$Ii'&Kj+,L,#ZKC@SL("qDNGK9Lj8D%06HRTdG)12IC!!TTHXZ+q +PSG6&LULEGi9U8PCUGeY`P(jLE)&F3&K40$G@E(GlKkqi[-c-bV'p`S#-YC4`D(P +Z5ea9,Mj59@9SGSfRYD1ablU5G)U*A&YQ@&pbER9kFjQdPSKfD*'3!'PeN!#,B&b +ETRZ,Uk'KTTUFPiZ4Qj0T4PGD3c3G+die+PpeLEQaXGVQfFr9cVHPIR#2HNKCG'0 +PGP3mD)"33'pY9QGZJCb9KAkGVB#%V+5CSUQPQ+#iRAb%C%GE5LNe@'pIARZ0UUk +BQUUlS(U(I(UKK&YhLReM9fGXH@8d@(p`Ij+$JV#kLSD[bk+)T)QCZ(9eVijJCQ4 +$-8G20c!`3h*ZDERGT+64ZTfeZCf-MiCdJ(U)VhjLK(*JA90HC9Y469eaG)"fE*U +3!'f0Q*qUVYR&TG$0S)&lJ%mJ*b8L1%43Dhf8YkUPcEkKVU#"JiprEhfAK&4EEfK +cC9C`GAapGTQaRi"rK'CIG(CRDiqLPD(0bEhAZBKfA&T5)"Bl3#BX9A*mMjH+SXk +hNV6EfF#GUlfDJS"kC$e!86T#B@CbJi*hGSk+BQ*`FQ"'FC@&QlbrZ,R*XC@`S(9 +iB6e4F'pF8faN6h4S6BbNI(5@VU1MYV1PS)KSFR&-9fe58h9jJBk(LC5'C@Z$J(U +"LBkTZkfRUELeMRTXC@j6-d&FBP03B(0dBQprHi52LT1VUVcA[*58XDf'GhKpG&K +LFAD+Jh"XBeC29%dk6RCX8A@hdEUddpI!VjD,PSGM9PYC8fGb9&9hF&48CBHbN@q +aak'RX*fAP(&3AQeCEB9FC+qXHibPNRKS9dKHDP&@E)#HPiDK`F#YS+1PPTD6D&q +)LdddCeic2N&#Ci*iH+6$`m'kY,QkMQL$H%j9B&pFBi'1JAk1PiKUBhKpE'"VKiT +dGj+MNAf0PjDEP)Z5QjQ(KC&`@'YG3cBc9f&0HCq4QkDS[UQ9V*&rMfaLJS9lHAf +"HRD'JeYVMQ4KMRq!QSQ$LS1&LB9qFh'*P(GlR*U"Gi+(HQGJC@9I@'&fMjZ!KUD +SUBf$U*KpPiPfQ*0dIj0pCRjm68aYCNY8EQaULiGSLCk2Pi9pT+5AQD'YS+1IKjb +BA9PZBej28R*VG(Y`Q)TFHBejDh0jI)Q%Mk+8QTf,RT9`H)"rG@H!Ih+-NS0aChP +lFhejJTD2NjH9TjjiBhZ%3cYa96YSI@eaNk+-M+#-Lk+#M,bCKU5GPD'LJR+)I@e +aD'CTE@jTB9TUEeT*8Q4PCh'1TjDGb-A![TbU[TZ!IAebCQ4KDh*fGA@(EPPpJhT +aCi&pG)CjL*YkI)0XK*!!JC0rEC@JQCk2KTq,6(542NZ4F'Q8P+5bR+5GKSPaCRT +K5'*VBh4kGRaiJ)KMEjb+IikDYV+BTk+(M)&PB@j[A'k)HBUMNSZ-JfKKF9G3C&e +VGh13!*bIU)q3!+kBL**rPUjfEBqCL9jXIPpMBePEB(PpF(H4QSQDQA+&N!"RH(p +DJl#(F*@bUC5DTCD%I'KHIAa6D)&PFiG`EhGrJ8p5IfTNJiqFUCk*QDQ>+#CB@ +)DTDlRB@EXSaREA&X@%TEDR4dHBf0JRpmG'4FBQTTC(bERTqGTEZPJBfMNSL3!)L +&RU1*P*&ZKi&*9Q426f0N5@kLD9+*E&'(H90pK@H#Sk'XcG1l[XZdT+qTMA0QC@* +EAeYHDPG)69ph984`G'*PICq@JSqUU(YfVTpaPU+"RmHUSFLjM)'@Ld4(I9-V@RK +YG(4lNheHFB*G4fTVESb%IS+3!*TpJ*1%FiLMMBLfd0A&U*q`UA&FF@C4-cGkD%" +iF9L(DMK@DQCSBh#$N!#SV*UF`,H&TEb+N!#GQE@MMDbjPfjrNeB[6%Xp06TK8NC +kF9aqKhjfICHAK*!!ZEbSVXl2PTUiMB5)E@aQEi*IED*q@hb!D9jDBQ*45f*YBR# +8Qik3!)b,MSk3!)'DYi@,bUQBZ*!!Kj4SEA"8C'PbJ'L!V*Q#I(Z!@MY38dC!4QK +qJ)1&RV@HM*qfYk#J`,bRZmDHIiZ)B%G*88Nd1PTaFhU"J)jb8'CTA&aCBRLAQ*+ +ecX,!d-LKPV#fMA#1R(CLD'4B5N8k(6CJ480XKjQ@Q*Z@U+H*IR4aI(&UKCqRT+' +i[DZNMj'GHfe[C(1!F9KMI9Nl@@C*3fjd8e+#Z*q&UCb(RiKUIC')FBLkXDA'e-H +[T+5'F(GmB%&5@NaA68*E5MC+15eMGf0rRUE!j-qIb1QULULYPjL,IE+iH(LFJda +LIcd[@dmr4PTLD(aY@@*`IA0QIjqHMC[%bkqVaEbDS+&fEC5%@@TrJ)KiJCaqF)9 +T4f9j@&KP68aKBepKBA&jBR+,Mk+HV0c1YpEL`,#jQA4kE9TVHhGLDh0DB'YG66d +f1$9&C9P8LC4rL*ZYUk+ITX$%YVhHilZbV)f(D%TB66Nl6PC'9RjbBAKaDRPL9R* +rG(Q*JiqdRALCUjk2JTqMPl@JPE16LCTdEBad8@0Z68GUFQjrGQ0XGRpR9(GZ8A1 +-Fh@NYjk8SE5rVU+aYCQ3!*f*KTb5J'pIBeB[1&Fk0%0&DApmIB'9PS@*MiqFUCb +AVlZ`VELkUj0qGQTGA&PYEdTIK&K0H'C*9eaNEfYbJCUeSC5XQSqYRRTrNTD0KBq +KXDb2JRjZEh"B8Qp[9PCFEhYLBRKS5QYj5RLLDhQLQk+LT+qSV+L)QDD5Yl4jLUL +AJACUC("I-LNf5PG+-%*lE&GrGhqIIRqMRTDK[,LYeXQNYUfKUjarDAU4E&0rJf9 +cC$a!6NNe18Nl8RTTCj'VSBH4UTfAS*bU[UDD`-bcTkqZMR4bFR"QCQpZCPaKC9e +23%"%58P#9'PkK(H3!+f6QE1HUF1NSEHcYEl"ZV#NRCKiEiaQ4@C+,8j28e)j9'* +(49"BCh0XCBURQCLfd,U`cVqLVVLUS+5AM*Q,IijqFQC(4%K"2N"19NP5I)*IEj' +!D@TrIhb1MTbSTDkJUY#mQTZIQ*LKRBk-PSKRDR"A6d`m26`j490HCB1%BB@bMPT +fYUCdLE@NLDE*Z*@J[kH%R*YcMj9DAA9F9@PG4%j93e9U9P"dPhT[Ul#2TEDEJSb +VR(L'Q)Z+N!#)QUH5N!#6NSPbDeaQLQJl@AjJ5AGf@(4pCPKXMS'+TT+0X,'*P,f +MHiL5I(@CTB1&Q)*rMfYDIQ9(98p8C@TIAiQ$B)5IH@f1NB5FU*HN[+bMYTjrM*H ++EQKdDR"jB8jAGACB9&jRF'4XKS0qLj@&JTqUNAb)PSU&N!#3!*1TR(D(SC'4Mh9 +iKRK96@KUA9C5@Q&YF'GpLAq(IS#MQ)14S+DBMk'@N!#fSR'(NSLINR+%PB&SCf9 +CBe)[4eFm5fCID)18KRZJVjqFSDb`VD+GUVbaLBbIK(k%GA"dL)0GCA4A8Q4@4%j +F@8P6GhYeHAD5RhZ0[l5IUV#LU-5[NUkMHSZ9IAU0L@aXG@PI8&CE5&CA194jAPP +kFQCrJ(D+PBkEYUbM[-2$[E'bSC@DP*q6F)@%C@YF6e!q38a!1PCJ49f+G&epNAQ +%QSb4SD+SV+Ubam#JSU'PYT'+QS+'H'PL2eTM,6j518aN4db&HePfNSD"LCbkTBk +TUjkNUl#5M+qDLDDQPTHQN@*RD9TK4MjA3N"@A'*FDA0UHhGZQTpmQDfDTDQSVjf +CR)f-Q*QDSCZ9QjD(Gh"mA#jBC6T"8&KM9eKUEhb+FQ@9YCCqRVDYVV5kUC+[VBb +BMiDDNi&TAhaa6'0P38T-3P&FDf96G*5(J)kKVCb*QCqCYUk"RED9MjfHQSq-IfC +TDPP-9A"R5ePXC'f!FPTZN!#(GhL@V*+3!*U(LCL4JSH6PB9pPDQLS+HAIS9mA@4 +S5N4L8caUHP9TN!"fDBb'FSqELT'4Jj1QPifAP)HAUC+8XkD@U*L!K(&HB8dq5cS +L0NG"6@efBS5`RD1[VF5aPD@LRUfJLT@DP*!!KBH@R)KbIfp-CfT(@Pim3dG$@f* +PC''-Tj+HYmE6YD'YRTDDP)plEi+,HhU3!*@+JRPmJ'KQJQe+@fj95'GY9&*RF@4 +XMik*TUH8PULkXU@GPD5QNTLGR+LFKiD%Ff4MBe&&8N-b3dC-@&TECRPdHj@@VV# +%TpQZRmR"Tl+fSik1QTD-J@eNC99186e$9#ihB99PF'U2Lfq4U*UAS*k5M*@PPS@ +BRjb6KjHIRk'5LSb0IfefGep89Na!4eeH8f*dFRZ3!)arN!#NN!#'PB5%RBKfQDL +6MjZXYDDYYC@5SRYIG'aA9d)e6NBk8PP6@'q$GS@dVTq`TjUXUk#EKiZ9KBQ1KU' +iLAHPP9eTIQ949eBl5R&C9(*GAiKqC)1VQSDMYDHDUE@JP*''LiGYHCU>Z6FAZ +'Hh&eD%09FeT3Ch"PC(CmF(+1M)'5J(qYVjbCRkqdS(q,XTb!NSjbIiaSBR0KAPG +!8f4889jVB&9YK(aiLjkHKB@cVi1DaUk8VUL@ZEH2Mj11Kfj869eA2MSh3PP46fG +`D'jjHBQAQDDDRl#ERl'NPTqTL(LFQ)HGPi10NR*ADApN1MK+8&&EE'4YKR4ZIi' +5Rik,M)5GU)H@UC!!NjD,PjqFNT5YS)LBPiZ&D9KI6MG$8NBm6e4'DAe4C+@GG)@ +eXBbDbF'ETlUXTUZINUbREA+@GejcDde+9Nim3P*GA9&0BAU#GRbUZ*54V+'S[C+ +C`*@1T*LFQT54HRL!G'4GAeYJBdY,FApQCi"V@B#(Bh+2LB@"LCb0KTD6P*Z0Jjr +"TjUqRiLXPh&`Hfe066SRC(T,@hCABiTdAi1BIRU+K)fKRCkNRT@CT*bGVUZPRC5 +JRB*jFQaE-#p"-$G,4%pKDS')K*'T[CU*[,+)RkqDLC+@JSUEM(q0PhjpLR&hNB" +TB&Y89@*24'aU1P')IA1EZ+#9X,+KRDDfQ@KiM)@0Ji5KPhYfI)4rLBK9B)KJ6h" +eC'G[8$eTH&YMG(U2RjQEZXbYSlbAJl@THBLIMhb%I'YqL'T5@P94A8e2GR9EAR& +hEfYbJBGqFAkCTVHkTl$"YUbRU+fFLBeh9&edE9YA4dGE1$Pd9e+#B@',MSQ$SE# +*LCD*Mjq8L*'BTk++Um'8Rl*cGkaU1RCm48&E5#p@G'&@9@k&F'k-TkH,KCQMQj' +@P*!!N!"iDCbiMjZrUjZJRTL5M)"U9%!k5%T&2MYBB%4AJ(jrQ*Q-QkqUT,DhT+5 +CLT+CQ)KlQDClGD#3!'q6LdG8G8ik8Na%9Na(DRPdI)U5ND#JNUr"SD+cQBqHQ*! +!J)DJJ'4[HBb#F(plEfPEDQjJF'j66f*VDA&lK)f!FiQHRU1GN!#DUTPiSFf6HU1 +HG(5-F@Z&CNYGCepMFfTJC@PdGh"VGSKqI)&qPTH0RCU5Qk+CPE'hQjfFN!#IN!" +JDieT-%480daJ9PeRFA4mJAkEVC&iKkHJL)ZHS*!!LB'#PjL6Q*@APBq)ISL&E@Y +I2$eG@NG5@9jaH(L8XTD#TkqIPBHCUjU)K(9aT+GTGU@FJ(k)LSQ1L'KECQ4H9&0 +F8%aBCQ4RPjGlS,'FR+UhX*UBRT&mKTZ"GT@CKQYXLi"hJ'j[D9TXFfCRD9aBAPC +MGQekKS1BP)QaeV5+T,UEL*QDNk@9GSH(DA"rG'426ep6@'YPE'4@DfaPGh9cJhY +kMT'5Y-kKP-HeP+LRQkDLI'L"KfpQB@&U9$K'C@P8@A9bDAk&HSb@I)'9G@q8MS' +DRiCqREDGRVkVND'QKR1"M(jVC&9-B'G@6e038'GTA(1,L)Q3!)Z+P*QCN!#1L)D +DQjZVVVM!Y*Z*Qk&lDfpI58"28N"1A8!pAfTPCAL1R+HCQEkqTE@VJS#&GAU+I(b +DPRb)QTDIQ'ePL(0%@i&`9eCPEfGSDQb%K@jkJi'KVCZ8NTL3!(H"M(H$R(j[SD+ +)SkqNPi#&M'jLFfG#2%pB8NpFCh"aD(9pK+@PK*V*UiU[XTkSTik!MBp`FiemD(C +hDQTQEfj18f4D99KVLBaqKCfJPU#KRUfEGi19MC+@LSf0HA"XEAYfC&KGDRb0H'Z +ARQjXNBGRIj0qG)#2K)ZVP(H#KRTZGB"qIB15NBQ2SDD1KSG`EhpVB(ahAQYbFh0 +EIC4JC)9rG(bKSBkSSSbLUSppP++,H@emRC+!K(4aGeY5B&9B8d&FE&YSLk#@P+1 +MSTZS`DQ+PkbBKCQDK)&qF'CFA("[5e#"H9KbJR&qG&CQI(YYCS1FQTk6PV1RP+L +IJSL#H(jhM*&[KTYlF(Z(Kf*DH@pFCS#'AQZ3!'"'DA"RFA0UFAq1SDU`YUDCV+k +@Qj1-UTjrK*!!Q*+%FQCQ88C%2%G-2d0GC9eiL(CrQT@"L+5q[D@fcV1TXkbFQCP +fDAGcG'jdI'"JG'YF6%jR86KPEP0iKRZMX*!!LjZFR*Z+MD@0HCQ4LU18MTL)IB@ +9JAQ6JR*hI(*PKhJr8@&-3NCPF'9pJf50`U'4QU#ULiQJNjkKSE'6NEDTLBZ@Gep +aAdYKC9P*4eeN8N*AGR9SDhb,Sl@NU-1jTkkHM*5,JB4qFQq-Ti9RJBpdC@PNBQT +M9'4dB'*iHiq,9QQFLi#9Mi@IV)q1RC+RURC`MBTlIBGrJC5)Bh'4Gh*[9'Kb86p +PL@GKLRYSMD*lI+'5NCQ+NjQTXTDBT*@EQhTbNBCE@@CNDh*HA(KZ89&QIhPUF)' +-Kj'RSjbIPT@HLhqFRS'%Qjf!J+QEGhP`BQP93@TY3PYhB@4eHif+IiQ-NTHPUSU +@Y*Q#M*H%GB@(IRG`IjD@Ji@0IR9kEeTGC99AANjDEi'(Fj!!S(16ZC1AU*QGRC5 +-KT'6KRprF'5-QQadS)PMHheBBRTX5$p@8&CeD'q2IiDPTULRTl+SSkf6LUU9JT4 +hCAjmD@PpJ'CMG'"EKhP'9("U8&&ZHR0VJT@+Ql#UV+fRX,+GQTb1NSZ"JPpZQQT +3JAj-6R"U8PPYFfTICAU(HhD3!)K`Mjq)Nk5PTUQZPSURTBk,NReTKj!!FhQ,I'G +QCPK)8'4C4eGYE@0qSiejN!#6Kj!!PTU6MD@VR*@GVk'6SjYlGT'-HSL,H9pQF%P +%9N!i5dC%AfYfRTaqPEHpU)QIcUGhS+k)L*qPQSQ!I)56J(##Hf"ID&P%9&%h8eX +b5RekF)+DQjfkZU5m`*ZNYBCUNCGeH)0kEA12N!#$L)1#LAaF8'Yb99&B5P&SE'e +`EhD+STCrUX5ETEqNN!#AU+GqFCH0CAf4JBf0ISTlA9aIA&&)8%a)@Q9UGRZ(LB+ +8TkZSX-#fX+kYZ+@IU)"KFR9NDR4MBQTIDPp4F@**EAGM@QfFPfb$TT@9QiZGXT* +pU,Z0HTfcRReiJRThHQeaF'CfF&46DAPP@feXCA50LA5#Mi+2PSD6TCQ)PkZ,IUD +RNk+2ET1XKfq$M@YGEPFj5e9&6PC1A'eeHi+6PTDCPk5`UkDdV)f8STk2JT@8F@4 +iLhTYFQjjCdC089"15&C25RQ*H)kKPTqhXk#EZmZ2LVZ4I*Z%H)Z%F'4JEhplC'# +*M&Y8D@PKBQ*DC@CPKiGhM*ULSBbDZUbBRU@SPB5CPifCIf4lN!"qE(*kHA9ZBPP +AA9eG@djDDfTfGR+6PiZ5Mkc(TTfl`kbGTl1lThYhLAjTBfPD6@PP,cCJ5d*H@%e +QH(1)PBQHUTbJU+qaTULhXkH6KkHhMhU$HfjcBdPQHNp%B&!h5'PbBPG`MAjjPU+ +APTq3!)#IVj5,RkkAGSkVN!#%Q*CiDSUII&pfMA&069Y928CG69TVA(Z6KSb1PTf +0PCq9R+qdTU#PQUI#Rhf*LiGkA@0dF@K559)l1&T&19eB6R59NT'RXE1ZS+biTk5 +[ST58Nj!!S+'&IAY`D'ehBeT`DP*6AeP9DQYACR&PITU&JDHLKT1XXCb@UkL9M)' +*Tk@#H)U&HS&pGRGhG9j59e09@9PC@@4SDi+0HBDQLAUPUTkUZ-1eQ*bPRkqEDAb +%B'ehA@f$CePG@PG*8'4E99pSHSU*PUZBK*'JT*kCUELPMk'QPUDJHRGlB&TVC'" +YE'"EC'4GD'TNCQ*TH(k2MS5DRBD-QTQJTCbGRT16RkQILjQ3!'"ZLQaIE@TEA@4 +95daHI'Y3BR&lMB+"QC+'PjD5XEULRUqVN!#5UU58N!"mEhKkG'pVD&pG96j"6eC +LAPYRF(k8RjkHR)Z1TU5DVEQJPU5ENCQ9M)9Y@@TkEQTZDh&hDP"2A9pLBPeMD(Q +8Q*19Pj14NBk6SU5AN!#XVi+,VCL,LfYVKR*PG(ChDfTR58jQB&KHA%CDMijpNTk +0LC@HTDLXY,QRM*5TSTZFI'0fIQ9GGA"FB9*'9Ne%A'TLBQ9PKD1HPSkET*'BTE# +rS*A$XhL"T*KeDQeKB(G`B(&pGA*V8e"X@8PfDdPdPSb)R+D*MUD(HCqYSD#HT+1 +-NCfFN@eUGQ9VJAehJRpG9fTJ8eK86Nj4BhL&P*HARTbJSDE%[Tf[`k5+QDLINhe +RBQ0bEeaFBfKB5PKE99eKD@K9DBZ1Qk@9MTkcXjqTblbMXkQ)IBH5JfCDC@CEDR4 +XC'GqDN0EH'eMH(4@EjfDJifPM(D1NS'+Sl@ZRULMLjf[LhU0EPGhHQKbHh4X@e4 +IA8a,B&e"@)H+JT'LQSf5QBq0V,1BUm#PQD1RXU*hERKX@eCRE&"3AdT#B&Sp8R0 +X@'b@R*bdZk@CT+5ESkZRRCZ4IS52M)L&F9pZE'&[FQGA@'"0@h*6AiY`6A'RTCD +JQjLBP*59RjU4SU'3!)Z(Lj''IAPiJ(plKi0aFA&J894@3ceDAe&IGSU*ND58NU5 +CN!#@V-'dVVZbRC1CUTKTARPe8&0jG&KD9NY95d"5A&TC9h'5PUDrX*fTXDDETlH +aUk55QCf2MB&hH&G(B@PK@eCDCfYHA@PSFS*T8A'8NSq6NT@CPSb+Q+DSPBQKUik +*Qjk9KAepF'q)J9P2C@K,3&"BA@"ECA4dG)HBNS50QSU)SV'bV+qpYDHIRD5BKRj +RChp`@eeB9P!h1%Fm39TQB&b$UU5QX+LNUU12QUZJSU@IV+1#MDU0C'ecA9j`Ef& +PF@GE@eCCAfCVBPjZM*U9R+bRQjL)L*fFMSL6R)k*PU#GMBD(IRGlIRTeF'KMAPT +8699NA%G2E(4iKBf5NCbYSk+m`UkNV+kQN!#&SU*fF)Pf@A&rA8YE9$a!88j0AQj +ZCQq-R*D6S+@6Pl+cVEUkUDDZSS0dH(KSA9P6@f0UH@p8EAj36@YMDi+!FR@CUif +-Sj'%Jh1#Q*'@TD+JV*k0RD1-FQ*TE@K`HA"PEA9Q6P&XDeGBB'*cJS14QT@8LiU +BS+LGPV@dSDq[U,'SN!"hGS0K3Q4S28G80MeC@9*KH(0ZHBLNTC+GX+kERVU`QUQ +URjZ-JRq-MhKeF'KcE@GaCP06@Nj!8f9THRphKC!!PkHJMC@HN!"pLD@LSUL9R+D ++Jiq-KRTZE'jeL)TXF)0K590*5eY56f0UD)#GQSUFU)Q-UkHNUU#R`,DBP*ZCPS0 +TB&YJEfKHBPpFBee)4ejSA9THDAk)Mj@AV+qCSl+XX+fRX+@CN!#"Q+"jE'jKA'* +RC9a1A(051@"eCfYqIQafP)b!NT@+KSkSTjDXYk+MVD++JTLGIh&`CQb!G9TEAeC +956j9C'"XGA0rNjH1N!#9Nj!!LT5ER+k[SkfZVD+#N!#YK9pUG'jPE'j@AQe33%P +09f&QB@U,Ki5VT)kUU)Z5T+58RDDCT+Q8KiQKRACVD'epEeaPE@KJ6Na@8PC9@(& +XDiD4NTZVUT19YEkJPVHeR*f5JT+LM@CXK'aJF@9LD&TC@Nj'9@GB9R"lFQarPC@ +@RTZAUlURTVLfV+DGK)@BLRCZG(CKB'0THPp*Aee*5e9HEhCXEAk)Kj@HJhUBVDL +AS,qqVEQZND'IJS9mEQaC@A&eB9"GEe-p8eP4Ah&fDR'4QCDHPTQJMj5MRkU[T+U +fVC+2TU5'FhGS8@9b@8jDAPG33N0JCPjSFhPqLCbEQ,#SLD@pQjQ[RCU`TSk1NT5 +HL@GfK'48CQC09@)q-PPH5@5$DQQ,MBUAU+Q-LkkfSTUVVjqRViaiMCZ@K'jhIQ0 +SI@YJB&C83MK8@8KVKf4JLD#AN!#DNBqUTCDFTDkbVU1PRiD6UBGRFhKMBh0S@&j +L9NY%2NPGDA"N@hDHV*Q3!+1VT*qGV+qJUE+MRjk4LSfENfG3CA&E9&j34@&N0$K +TEQCcG(1&PjbBQUHVUDHBSDqEQkUBPTU#IT1CMhjVE(KSAQGD8Q4I1$9GE'&NFA+ +"PS'#U+D9QCU8QkkYSU+RV+H6KT5JPSCZERPNB@eI8e*13$G#590RCQ&fK)HNV*L +LY+HGUl@SSkbVX+q(I+'HKApR9f9K99pI4P"`@$K)@@&VD'TcJT+,PE1RPk5QTD1 +BPk5YR)kEQBH+PT0lBQGYB'CK6QGa8N4'8fGPBh0bCRD+MT+ESCH3!++[Sjkc[DH +IUk54NDLSGPebFPpJAdT-@P!r180-8fK[DA*jLTk@P+DYSTUMX,1ZVlbmU*qKQ*1 +@J@aR@P*BAP9'99a'4%T'A(0UEi0lJU#KSkZFQU#4QD@@S,5LQ+HHKjDSM(&cFQ& +CCfYG99YI8N93BeaCFAPXFBZALBZPSif(RV'GPV+aRk1TRjHQUBjpG'e[DQ&D89" +@8N)j39"MF@GJGiU4QTQATDDBQUHTTE#fU+'LNSLJTRpVF@GGC@aG5P9P@N3k3eY +fGPjMJ*1ER*QETkD4PUQHNk'ZST@HQ)b8N!#&I'4JGh&CAfKcGeY2B&P6C(*aBQC +rLSb4PTUIPBD9Sj1BUU#CSjk0ND@IK(PhF'j[DQCHAA"S4NTFAQGQCh&TGjL6K*b +`P)LNV*qLVkZITDU@Kj+5J(PiDePDD&a5A9P@@9"4BA"pIA&fN!#JQTLPST1BT*f +6QkHNQjD9PBU$MSGaCepJE@YA@h0S@'"C9'acDA@"GhQDTT18QjHANSq8PjkLQTq +PPSqEQSk%FQKVCeT5@@4L9&"C9NpCC@eaF(@(QT59VkqAQDZSR+5TR+1bSif1QTD +)GfKLAP&1@90&9@"-3&CTCQTfH(f0PjDDUV+NR+5RTTkCU+H@PT5+NCD%G(0VCfY +E8Q9M5P4I59*X@&4rLR4mPTQ9TUL0Mk5NQTQLSk@URTbRR*+3!)"cEh&cC&9JC9C +A@8T,@P4)AhabD)#BRD'NR*UVVC@6UUfXVD#MVU@6Nj9qDQ9IB&aGBP&-CQ&&8Q" +6@R"fER',QCfVT*kYSTDRUCHCUDH9NTU9MC!!L(9[F@GAB'e@5faU5e4QCf9REQa +eL)5'PTHAQ*!!PUHMP*DUXk@0KkZjM(5!IhelD@"SCQ&R@%*6Bdp4D'CYJRelMC@ +CR*!!LjbPQCbKRl+hQ*'LSSjlI(9SFQ03E(CD@fYJ@&jGAejQJRY`MTLDU*H1RjU +BRik)RUDFP*5KRSZ%JRYbFfPADhKL@QC[BeTTCPKXIQe`JB51PTLIPj'FSTb4N!# +LU)q*S*b*NT9rF(CjD@PaBPCGBeeB9eaRD'4bJB+#K)fLSj'8SDUVQTQRUU18MBk +)KiCmF'YaH@pG@@&SCeC)@QTDA'e`JBTqLjqGPCDKRjHDRjqBR+ZJMBZ*LB9fDQ0 +XIA"CB(*cC9pNAf&SA&a[HAeiH*1HNC5MS*QHSTH5TV5GLT@KQBTmER*iE9a8CA* +L8eYUE9j9B'jQAh5%JS+(QkQGQULZTjqMTCkMU*H3!*k9JAKiIQa3AR0J4%pQAeT +I@@&[F(0iISH&KT5HQC5HSk+LQTZSUD1DM)qENApiHAKXDA&T99&JC900@&aMDfK +`JB+'N!#2N!#EPj!!R+1CQ*kDRDZQNT5HNS"rJ(CaH(4UBPaSEPC09eTD8PGZFR5 +%KSLASk@CPkHKMSqHSjfCQ*L5NippHhjiF@KSEf9JCQCJ99YXB&0LER*mKBL'QE' +LPkZRP*UJPSk6P)U*MBQ"HAZ!GfjdF@CVD'4XCeeUF&YIHA9[Ji9mMCQ0P+LFN!# +GT*QATD'4M)H*N!#(G(+"J("`FfaLD(4M89pUB&aUFR+$LhL%STH2SU1BQTUFTUD +BNTL6L)CqEfY[Fh*VB'0aF@GIB'Y[Bf9dFA5*MS'0STUDS*@HTSf&N!#8Q)q$KBU +2IfYkJA"`DQ4iDeCdH&aXI'PKERCeIBCmJ*fCKjDSRj'3!*bCL)Z@Li5+MBKkGAq +#H@aaGQCDA@GTB9YFDA&XDRL(JB#6P*DRSTUNUTf6SU54N!#5M)9pJS&[ERTdB9e +UCf&XANjUFeYMER*qIS@8MT5PSCZISD+EPT+1QTq+J)L1M(CTI)0aEA9[B'CbCfC +bC&TSDQ&ZK(T`MD'9N!#FSD@NPT'CS*U+Ljk@K)f,HAf&H@TYFf9KC&eLDQ"9@'9 +PCA4eGj19KCQSS+U`SCDASTq0Nj1+NiYmK)&fH(CUCQeZCPjKCQ0I@Q"UB'9lGB1 +HL)5SVTk@RDZNMj'DNT!!N!#(LSk"GA4mIR"UGA&JCR"UC9jIDQ"8DAYaFAq+Nj5 +5QU1RS*56QjkANj+5MSq3!(ekKAeiGQTVFfPHCQPQCee@C'aRDR9pKBk,LCULQTq +NPBqGSC@-NTQ6LBH'JAPfG'aVE@GPBQ9YC@0TCQP`FRTrIikCMSUFUCU,PCH1PT' +)NC+3!*1,Ihk)Jh*fGfT[E&YQGfjGA@TZD@ehHi#3!*'#LjfFRCZ,KCDIL)'5Mif +AM)+%IiL&E(5%F&jNEh"UCPTDD@YMD(Q&L)k0MTbJRk@6KCQCK)12PBZ(N!#'HSL +*GA0kGA0ZC'T`E@PJA@&JBfPZHi"rN!#EP*UUUCb8QTZ-KiL*N!#+J)D&H(KqHA& +cFfeUD@jjEejQF'TQCfphI)Q)HiqRRjbPRT!!P*q8IiH@Li+)JhKiGRPmF'jbCfa +hFR&`D'YZCfTdGhQ"Ki5&NjLERTHAPTLFNC!!R*H5NBQ%IACjGQeYE'*KEACbB9e +XE&pNFA*kL)5#P*Z9Q++IPj@9PTH@NC1CQC5'HAYpGfeMB@KRAQG[CQYTBQPQDRe +qHi@1P*16RD+KRC+1N!#*LBq-NTZ-JSQ$JBCkE'aYD@KSCfTXBeYJDfGQH(YlKSH +&MTbNQj1BR*f8KBfGNSL0Nib!J)+$JA0bF'4SE'GH@@CR8eTUERb$HS18N!#4T+L +EQD'KPif4QjD5Mi5%JRCeIAeaC'KbF'eXDQjRA@&NCQTdJiH!KTH@P+'JQCkAKik +DNSZ4Q*@)JAq!KAp`E(&aDfY[EfjYDfGJB@0UHRYfHS#3!*b4PDDDQ*k5PC@0PTD +2N!#2K(k*KA9`F'aNC@YRB@PYBPeSDQGiJA9kMBU$PD@DPD+LPC+9Q*UBMS1(MS* +cHB&kF'GQER4XAf*YCPaHCQaeIi'$KBH8R*58QCQ9Lib8NT'4MBq3!)TrHAYqH@p +XE'TbG@KNEh*P@PjUFhCiI)''M*HHPjUNQ)H1NSL0PBf)MT1+I(k+KhPYDR"bD@G +UD@GQBf&KChKlFAU%K*'CQD+JQjU9PTL3!)Z9PB5&M(plKB4pG'peFQaYD'4SD@9 +EA'jcFB#"HBZ4MCZJQCH8QCU1MC18PSq#JSL$HRGjIAKYERGcDfjdE'*KB@0UFhK +eHSD)MTQBP*5FQib+P*H8P*!!LSf2KRekJ)&ZCR"[DA&[C@e[B'9VCA&rGA#$MiD +,PjHFRC52NT11P*U1Kif2LRpiIS9hCQKYDfTSCfPVDQ&QG(*aJSClJ*!!Nj'ARCQ +1MjD6N!#2NT5*Kj!!LhphIiKfDR&[DfpcDf"XEf&NDh&dH)@$HSU9Mj+BQC@0MC+ +3!*!!MT'CNBH,Li4rJAjfGQpSFh&MDA4UA'CTAh&rFhb+Ji5AS*H5Qjq8MBq5Nj@ +3!)f0LSQ$Hhf!GfjYDfT`E'T[D@4NCfjXEhaqHi'+NjD9QjfAP*'6NSk8PSZ'Lib +"IS4lEhGdCfacD'TaDfTZCQ9[G(ChHB+&KT'5NTZDP*58Nik1Pj@(KBq*HAarHRK +cEQeXEQjYDfPPBQK`ER"lHhZ$JBH@N!#4Q*'6PT!!NjD@PC@4M)Z%IS1"H("[E'G +[G@aLBQTSB@4RF(amH(b)N!#6QT@1RD#1LTD9N!#ANi@*NBepH)0qG("UE(&`F'e +SDh"SB@adG(TlH)'2N!#0NjQCQT@(LTf8KT'2KSq-IAk,KAGfFQpfGQjRDh*SB@T +TD@pYG(jpIi@2PT5DRC56QTQ4Mj58M)5*Khf"K(KbGA&`F'eaF@TTD@KXDQPfIAC +pKRk*SC@*QTk6P*11PCQ4KSD*K(q"HA0lG@CYGR"bG'j[EfpbE@CcK(TZH)H0MSb +*MjU@MSb0NCQ@KS55N!#%K)&iIS&[CA"jG'aYF("`D@4[HhCZH)"lJSq+LCLCN!# +1Mj1CPik+Mj!!Mif!IS9pGh9UDh9`C@PaEfPPDh*aGRalIB@)Lj59NTH@NC!!NT! +!MC!!MB@'MBPqIi+!I(4`FR4bF(&XEA4VCA"cF(PqHhk$LC'4MC5DM)D8NiL-N!# ++LBQ'Ki5#KRpeH(G[G(9[F'jTDfYSCfY`GhjlHS54PC'5PT18Pik-PT'+NBb$LSY +qHAjrFfTcGh"XE@eZEQTQCfTaHAPdI)Q0N!#5PC@3!*HFN!#)MT@ALRf&NSThH)0 +qG(0[E(9hE@P[GQeLEA0UFB"kG)#0MSU1PC15PBk-P*10LBH0M)1$J(q"I(4`FhG +cE'e[E'aTC@PbFA&rJ(+#Pib+NT+5P*54P*52NC1+LBZ&JB"qHRGhFfpcFh"VDA& +YBQT`E(9pHRf(Lif2NC58NT!!MT!!N!#0MT'+KSb*K)&pIhTdH(4bHR9TE(9eE'0 +VGA0dGhL!LSH)P*+0Nj'3!*+*Lj@-KBU+LBL!J)4rHh9cHA9[FA*ZE@jSDh4YEAY +jGRk%LSq+LC'9NSb1Nik0LiD)KSH'I(k"HRGkHA4cFR"`EQj[D@abGAKjHi''KSH +2MSZ3!*+2M*!!NSf0MBL'KB4rI(ppHACdGhPfFA"dFQj`E@efHATkIBH*LBk-Lj' +3!)f,Lj!!M)Q-LBL*KB@"IApqH(9iHA0bGR4aFR"[FR4hGhGqJB+(LBQ1NBq,MBf +0MBZ,KiQ+JiH(IB'&Hh4kI("aG@pZF@pZF(*eHRPkJ)1)LBQ3!)f0Nik,MSq1LiL +)Ki1$JhjrJAYhH(PhFR"dF@e`F'e`GA9jJ(k!LBQ*MBZ,N!#,KSb1LBH*LB5"K)1 +"I(TqHhTkG(CiGA9bF(&ZE(&hGR4mJ(k&LSH+MBb0M)U(L)q2KB''L)D#I(f%Ih9 +eHRTdEh9dER"`ER"eGRClIRZ$MSL)N!#2MT!!MiU0NSb(LBH'KB'!IRq"HR9kHhC +bG(CaG(CXF(YhH(pqIB+*LSH*MSf-M)H,N!#0L)H(KS@#Ii'$J(ajHheiGRKhFh0 +eFQpcGRGjHRZ!K)D(KiQ-LSZ+M)f+LiU*LSH&K)1!JB*mHAaiG(4dG(0bEfecH(* +dIReqJi1&LBf1LSZ0LiZ+LSZ)JS+&K)1"IRjqHRKjGhGhFh*dFR&dGRChHRaqJ)' +$LBZ(L)b-LiU*LBU'JS1&JB#&J(U"JRPjHRKjGh4aFh4bG(9eHRajHi##JiL*KBL +0LSH*LSZ*KS5%KS@"Ihq#J(PhHATiGAGdFRGeFR9eGhjpHRk#KBD'LBZ*LBZ+LBb +)KBQ)Ji1&JAq#IhPlIAGeHhGcH(GdGRPiHAjpI)#"JS@*LBL)LBZ+KiQ+KS@&KB@ +$Ji0rI)"qH(PlHRKfGh9hHAGfHRelHi##Ii+)L)D'L)b'JiZ*KBH%JiH(JS##JB& +qH(PmHA9fGR9eGA9hGRKmI(eqJB1%KSH*L)D)LBH)L)@$KSD"JB5"IAq!HhTlH(K +iGAGhGA9eH(TkHherJ)'#KBH)KiH(KiL)KBD'JS5%JS*rIi"mHhalHRKjHRThGRG +fH(PiHRYqJ(k"Ji5(KiH(KSL*KB5(K)+'Ji'#IhepIAakHATjHRKhHAPiH(KiHRe +qI(f#K)5&KSL*L)D'L)L&K)5%K)5!IS&rHhjpHAPkHRPjHRGjI(PjHharJAjqK)@ +$KBH'KSQ(KBH'KBD%Ji1%JAf!JAamI(TlHRKlHhGjH(PkHRaqIAq"J)1(Ji1)Ki5 +&KiD&KS@%K)1%Ji"rIhpmIAjjH(YmHRPjHAPkHhTlJ(pqJB'$KS@%KSD&KB@%K)@ +%JB+%JS'!IhjqIRakHRTkI(PhHhajHRTjIS"rIRq%KB5%JiD*KS5$KBD$JS+"JS0 +qIB#"IAamHhamHhTjHhYkHRPlIRjrIhq"JS+%K)5&KB@%Ji5%Ji'"JS&rIRf!IRT +mIRelHhalHhalHAYmI(eqIhq!JB'%K)5&Ji1&K)1$K)1"JS+!J(jqJ(jkI(emI(a +mI(YmI(YpI(arIhjrJ)'$JS1$Ji@&JS+&K)+#JS'"JAprIhppIAelIAplHhemIAj +mI(f"J(erJB+#JS1$JS1$JS1$Ji1"J)'#JAjrJ(pqIRepIRemIAelIAjmIAjqIhq +!Ii#$JB#$Ji+#Ji1#JS+#JB'#JApqJB"qIRjpIAjqIAapIRamI(eqIRprIi#"JB+ +#JS1#JS+#JB'#JAq"JAprJ(pqIRpqIAjqIRepIRjpIAjqIRprIhq!JB'#JS'#JB' +#JB'"JAq!JB"rIi#!IhprIRjqIRjqIRjqIReqIhjrIhprJB'"JB'"JB'"JB'"J)# +!J)#!Ii"rIhpqIhpqIRjqIhpqIRjrIhprIi#!J)#"JB#"JB#!JB#"JB#!J)"rIi# +!IhprIhprIhprIhprIhprIhprJ(q!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhp +rIhprIhprIhprIhprJ(prIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J!! +!4US!!3!"!!8!!!#J!!'!83!!!!!!&!!!!!!!!%D!9Zk,S`!!4Ri!!%Cr!$b!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ihp +rJ)"rJ)1#JB'!J(prL)k%H(9hIB&jGRb!J(pmI(ajG("aG(9eGACjHAKiHR0VDfj +bG(9iGh*aFAD#L)b2M)Q+MjH9MSf(IRU"N!#CQ*56PTLES+#8LSD#J)@0Nj52LSQ +,KRjkH(GhGRU$KB+%Ki0lG@eUIjUJQT5,JApkGR"SC&jGDA4fE@ahF@9KB@GXCPG +,46`l59KKBPaCA&pHAf&LC@acHS#"J)+&Lj5FT+bdZVQfZ,l"`XM6fplJiq,GeF[ +%`m1rZ+fMQC!!L)0rHA0VBeeD9e&-5%9%36Xf-c)d06Bi1cXh-5m`-M3f0M8d-c- +i3NY49eaME(*fHAYmIS+(M*'9PjUIT+LYXV@fYlHhYV5bXV+bY,HkZlblZVUkZVQ +hY,1bX+qVTk@NSk'JRjqGQTD4MSZ*Ki0rHR9`E@PQBPeC9&"+3MSd,LNQ*#-M)b3 +P*L8Q*bS[0$T!48K+6P*9@9pND@jcHB#&Lj'@QjqMTULVVV+dYVLk[,r$b-c4eGE +@eY64cFR&`EfjYV+[UU@JQjD4MBQ'Ji"pHA9bEfaTCQ4LB&pGA&YC@&KB9e958%j +,58G%3N&"38*$48G)58P*58P+5de18&0CAf9XFRKrK)U3!*@CRU+PU+b[XlDj[Er +"`X2%a-6&a-2#`F#r[VflZ,@bVkfUTk5KRTZANj!!M)L%IhTeF'aRBepE@&968%e ++4N*!2M`l16Ji1$Jj1ce!3dC)5dj499KD@eaHB'*PD'Y[FRCjHhk!K)H+MT'8PTQ +ERD#LTDLXVl5j[X2)c-r3d0(3cmc*aX+qZE1[UU@LRTUAP*'1LSD#IRTfF@eSBep +D99"-4d0"2cir2cmr2cmr2d"!3%d9(5Nj499PHBfKXF(4iHhq#KSU0N!#6PCH +BQTbHS++NTULTUkbXVDfYVDkYVDbVUkUUUUQTU+LTUDLRTD1JRCZCPj55Mif,L)D +$J(ejGA&YD'0H@PG68%a*4N4"2cmr2ciq2Mp!38*$48C(58Y06e*9@&YIC'PZG(T +rKBU2PCUGSD5RUkqbYEHiZEQkZlZm[,blZVLfY,1`VUZST+#GQTH8NBk,L)5"IAP +fFh&ZE'YTD'GQC@9QCfKSD'GRCQ9NBf&IA9YC9eG@99C@9PGA9eKC@PYFAQ"MCQK +VER&dGhYqJB5)Lik3!*+8PjQERCqKSk@RUDUXVDqbYEHjZVZlZlUiYV5aVUURSk# +FQ*53!)b)K)"mH(4[DfKNB&eC9P458%p168e06Nj16dp389089PKEAQ*PD@eaGAP +mIS#"JS+$Ji1$Ji1$K)5&KiL+Lif2N!#4NC+6PCDAQ*QDQTZFR*fHRTkHRTqIRjq +HRCbFQjUCQ*LAPC15N!#2MBZ*KS5"IhajGR*ZDQ9KA9P@8e"05dP(4N9&4NG(5%T +,68p38P9B@PeKC@PXF(0iI)'&LBk6PTUGSD5QUDZYVUq`X,#`VkkXUkQRTD1JRCU +BPT56NC!!MSf,LBL(KB5#JApqIAYkH(CdFR"[EQjYE@aXE@eYE@eYE@eXE'YVDfT +TD@KRCfGRCQCPC@9NC'0MBf0NC@CSDQa[FA4iHhk"KBL-Mj1@QCbHSD5QTkQVVUq +aXl5eYE@eY,1aX+kXUDDLRjZANiq,Ki0rHhKdF@jVD'CMB@"IAPjHAPeGA9eGA9a +FA&eGAPpJBQ0PCfPXER&cGAGjHherJB+$K)@'KSD'KSH(KiL)L)Q*LBQ+LSZ,M)b +0MSq3!*'5Nj@@PjQDQjbFR*bFR*bFQjZEQTUCPjD8NT!!MSb+Ki5#IhajGR0`E@T +RC'&HA&PA99468P&48&&489*699CB@PaIBQ9SDfjaG(GkIB#$KSL,MT!!Nj@@Q*U +FRTqJSD+MSk1LSU'JRjkFQjQBPT@6NT'3!)q1MBb+LBL)KiD&K)1#JB"rIRemHhY +kHAPiGhCeGA4cFR&`EfjYE'TSCf9NBf*KB&pIAejHAepIB'&MC'CRD@YYEh*dGhT +mIi'%KiU0N!#5PCLDRCqKT+DRU+QUUkZXUkZUUDLRTD5LRjfEQ*D6NBk-LBD%JAj +mHAGeFh&[EQeXDfTSCfCPC@9NC@9PC@9QCQCRCfKTDQYXE@j[F(*cG(9fGhKjHAT +lI(epIRq!J)'#K)@'KiL+M)f2NC+6P*@@PjLCQTUER*bFR*fGR*bEQjUCQ*H@P*1 +4N!#1M)U)KB1!IRYjGR4aEfaUD'CNBQ&JAejGA9aFA&eGAPpJB@*NC@GTDQaZF(* +dGRKkHherJB+%KBH)LSb1Mj!!NT16P*59PC@9PC@8P*58Nj15NT'4NC!!N!#3!*! +!Miq2Mik1MBf-LiQ)Ki@%Ji+!IhjmHhTjGhCeGA4cFR&`EQeXDfTTD'GQC@4NBf0 +MC'4NC@CRD'TVE@paG(CjHhf!JS@(LBZ0N!#5Nj@AQ*QEQjbFRCkHRTkHRjkHRCf +FQjQBPj@8NT'2MSb+L)D&Ji&rIAakHAKhGR9dFh0bFA&`F("`F("`F("`F("[Efp +[Efp`F("`F(&aFA*cFh4eGAChH(PkHhYmIAeqIhq!JB+%KBD)LBZ0MT!!NT19PTL +CQTZFR*fGRCbFQjUCQ*H9P*+4Mif,LSL'K)+!IRelHAGeFh&[EQaUD@GQC@4MBf* +LBQ0MC'9PCfKTDfeZF(&cG(ChH(PlI(eqIhq!JB+$K)@'KiH)LBQ*LSU+Lib-MBf +1MSk1Miq1MSk1MSk1MSk1MBf0MBf-M)b-M)Z,LSU+LBL(KB5$JB"qIAYkH(CdFR& +[EQaVDQPTD'KSD'KSD'KSD@PTDQYXE@j[F(*cGAGiHRaqJ)1&KiU-MT!!NT18PCD +AQ*LBQ*LBQ*HAPT@8Nj+4N!#3!)q1MBb,LSQ(KS@%Ji+"J(pqIRemI(YlHRTjHAP +jHAPjHAPjHAPjHAKiH(GhGhCfGA9dG(0cFR*bFR*bFR*bFR0cG(9fGhKjHRYpIRq +"JS1%KSH*LSb0MT!!NC+6P*@@PjHBQ*QCQCLBPjH@PC15NBq0M)U)KS5#J(jmHRK +hGA4bFA"[EQeXDfYUDQTUDQTVDfaYE@j[F(&bFh4fGhKkHhapIi#"JB+$Ji5%KB@ +&KBD'KS@&KB@&KB@'KSD(KiH(L)L)L)Q*LSU+LiZ,LiZ-M)b-M)b-M)b-M)Z,LSU +*L)H'KB5#JApqIAYjH(CeG(*aF'pZE@aVDQTTD@PTD@TUDfaYEQp`FR0dGRGjHRa +qIi'$KBD)LSZ-MSq3!*!!NC+6Nj16Nj16Nj+5NC!!N!#2MSf0M)Z+LBL(KS@%Ji1 +#JB'!IhpqIAemI(amHhYlHhYlHhYlHRTkHRTkHAPjH(KiGhGfGR9eGA4dG(4dFh0 +cG(4dG(9fGRGiHAPkHheqIi##Ji5'KiL+Lib0MSq3!*'5NT16P*58P*56Nj15NC! +!Mik0LiU)Ki@%JS&rIRalHRKhGR9dFh0bFR&aF("`F("`FA&aFA*bFh4dGAChH(P +kHhapIRprJ)#"JB'#JS+$Ji1$Ji1$Ji1$Ji1$Ji5%K)@&KBD'KSH(L)L)LBQ+LSU ++LSU+LSU,LiZ,LiZ+LSU*LBL(KS@%Ji+"IhjpHhTjGhCeG(0bFA"`Efp[EQjZEQp +[Eh"aFR0dGAChH(PlI(eqJ)'#K)@'KiL+Lib-MBk1Miq3!*!!N!#3!*!!Miq1MBf +-LiU*LBL(KS@&K)1#JB'!J(prIhjqIRjpIAepIAamI(amI(amI(YlHhYlHhYlHRT +kHRTjHAPjH(KiGhGhGhCfGRCfGRChGhGiHATlHhapIRq"JS1%KBD(L)Q+Lib-MBk +1Miq2N!#3!*!!N!#3!)q2Mik1MBb,LiU)KiD&K)+"IhjmHhTiGhCeG(4cFR*bFA& +aFA&bFR0cG(9eGRGiH(PkHRYmI(eqIRprJ)#"JB+#JS1$Ji1$K)5%K)5%Ji1$Ji5 +%K)5%K)5%K)5%KB@&KSD'KiH)L)L*LBQ*LBQ*LBQ)L)L(KSD&K)5$JS+"J(pqIAa +lHRPiH(GfGA4dFh0bFR*aFA&aFR*bFh4eGRGiHATmIAjrJ)+$K)@(L)Q*LSZ,M)b +-MBf0MBf0MBb-LiZ+LSQ)L)H'KS@%Ji+"JB"rIRjpIAamI(YlHhYlHhYlHhYmI(a +pIAepIAepIAjpIAepIAepI(amI(alHhYlHhTkHRTkHRTkHRTlHhYlI(apIAjrIi# +"JS1%KBD'KiL*LSU,M)b0MBf0MSk0MBf-M)Z+LBQ)KiD&Ji+"J(pqIAalHAKhGhC +eGA4dFh0cFR*bFR0cFh4eGAChH(PkHRYmIAjrJ)'#JS1%K)@&KSD'KSD(KiH(KiD +'KS@&KB@%K)5%K)5$Ji1$Ji+#JS+#JS+#JS+#Ji1$Ji1$Ji5%K)5%K)5%Ji1$JS+ +"JB"rIhjqIAamHhTjHAKiGhGfGR9eGA9eGA9eGRChH(KjHRYmIAjrJ)'#Ji5&KSH +)LBU+LiZ-M)f0MBf0MBf-M)b,LSU*L)H'KB5$Ji+"J(pqIAalHRTjHAPiH(KiH(K +iH(KiH(PjHATkHhYmI(apIAeqIRjrIhq!J)#!J)"rIhprIhprIhprIhprIRjqIRp +rIhprIi#!J)'"JS+$Ji5%KB@'KSH(KiL)L)L)L)L)L)L)KiH'KS@%Ji1#JB"rIRj +pI(YkHAKhGhCfGA9eG(4dG(4dGA9fGRGiHAPkHhapIRq!JB+$K)@'KSH(L)L*LBQ +*LBQ*LBQ*LBL)L)H'KS@&K)5$Ji+"JB#!IhpqIRjqIAepIAepIAepIAeqIRjqIhp +rIhq!J)#!J)#!J)#!J)"rIhprIhjqIRjpIAepIAepIAepIAepIAjqIRprJ)#!JB+ +#Ji1%K)@'KSH(KiL)LBQ*LBQ*LBQ)L)L(KiD'KB5$JS'!IhpqIAamHhTkHAKiGhG +fGRCfGRCfGRCfGhGhH(PjHRYlI(eqIRq!JB'#Ji1%K)@&KB@&KSD'KB@&KB@&KB5 +%K)1$Ji+#JS'"JB'"JB#!J)#!J)#!J)#!J)#!J)#!J)#!J)'"JB'"J)#!J)#!Ihp +rIRjqIAepI(alHhYkHRTjHAPjHAPjHATkHRYlHhamIAeqIhq!JB'#JS1%K)@'KSH +(L)L)LBQ*LBQ*L)L)L)H(KSD&K)5$JS'!J(pqIRemI(YlHRTkHAPjHAPjHAPjHRT +kHRYlI(apIAjqIhq!J)'"JS+$Ji1%K)5%K)5%K)5%K)5%Ji1$Ji+#JS+#JB'"JB' +"JB'"JB'"JB'"JB+#JS+#JS+#JS+#JS+#JS+#JS'"JB'"J)#!IhpqIRepI(amHhY +lHhTkHRTkHAPjHRTkHRYlI(apIAjqIhq!JB+#Ji5%KB@'KSD(KiH)L)L)L)L(KiH +(KSD'KB@%K)1#JS'!J(pqIRepI(alHhYkHRTkHRTkHRTkHRTlHhYlI(amIAeqIRj +rIhq!J)#!JB'"JB'"JB'#JS+"JB+"JB'"JB'"JB'"JB'"JB'"JB'"JS+#JS1$Ji1 +$K)5%K)5%K)5%K)5%Ji1$Ji+#JB'"J)"rIhjqIAamHhYkHRTjHAPjHAPiH(KiH(P +jHATkHhYmI(epIRprJ)'#JS1%K)@&KSD(KiH)L)L)L)L)L)H(KiD'KS@&K)5$Ji+ +#JB#!IhprIRjpIAemI(amI(YlHhYlHhYlHhamI(apIAepIAjqIRjqIhprIhprIhp +rIhprIhprIhprIhprIhprIhq!J)#!J)'"JB'#JS+$Ji1%K)5&KB@&KB@&KB@&KB@ +&KB5%Ji1#JS'"J)"rIRjpIAalHhTkHAPiH(KiGhGhGhGhH(KiH(PjHRTlHhapIAj +rIi#"JB+$Ji5&KBD'KiH(L)L)L)L)L)L)L)H(KiH'KS@&KB5%Ji1#JS'"J)"rIhp +qIRjqIAepIAepIAepI(amI(apIAepIAepIAepIAepIAepIAepIAepIAepIAepIAe +qIRjqIRprIi#!JB'#JS1$K)5&KB@'KSH(L)L)L)L*LBQ)L)L)L)H(KSD'KB5%Ji+ +"JB"rIhjpIAalHhTkHAPiH(KiGhGhGhKiH(KiHAPjHRTlHhamIAjqIhq!JB'#JS1 +$Ji5%KB@&KBD'KSD'KSD'KSD&KB@%K)5%Ji1$JS+#JS'"JB#!J)#!IhprIhprIhp +rIRjqIRjqIAepIAepIAamI(alHhYlHhYlHhTkHRTkHRTkHhYlHhamI(epIAjqIi# +!JB'#Ji1%K)@'KSH(KiL)L)Q*LBQ*LBQ*L)L)KiH'KS@%K)1$JS'!J(pqIAemI(Y +kHRPjH(KiGhGhGhGhGhGhH(KiHAPjHRTlHhamIAjqIhq!J)'"JS+$Ji1%K)5%KB@ +&KB@&KB@&KB@&KB@&KB5%K)5%K)5%K)1$Ji1$Ji+#JS+#JS+#JB'"JB#!IhprIRj +qIAepI(alHhYkHRTjHAPjHAPiH(KiHAPjHATkHRYlI(apIAjqIi#!JB+#Ji5%KBD +'KiH)L)L*LBQ*LBQ*LBQ*L)L)KiD'KB@%Ji1#JB'!IhjqIAamHhYkHRPjHAKiH(K +iH(KiH(KjHAPjHRTlHhYmI(epIRjrIi#!JB'#JS+$Ji1%K)5%KB@&KB@&KB@&KB@ +&KB@&KB@&KB@&KB5%K)5%K)1$Ji1#JS+"JB'"J)"rIhpqIRjpIAamHhYlHRTkHAP +jHAPiH(KiH(KiH(PjHATkHRYlI(apIRjrJ)#"JS+$K)5&KBD(KiL)LBQ*LBU+LSU ++LSQ*LBQ)L)H(KS@&K)1$JS'"J(pqIRemI(YkHRTjHAKiH(KiGhGhH(KiH(KjHAP +jHRTkHhYmI(epIAjqIhq!J)#"JB+#JS1$Ji1%K)5%K)@&KB@&KB@&KB@&KB@&KB@ +&KB@&KB@%K)5%Ji1#JS+"JB#!IhpqIRepI(alHhTkHAPjH(KiH(GhGhGhGhGhGhG +iH(KjHATkHhYmIAeqIi#"JB+$K)5&KSD(KiL)LBQ*LSU+LSU+LSU*LBQ*L)L(KSD +&K)5$JS+"J(prIRepI(YlHRTjHAPiH(KiH(KiH(KiH(KiH(PjHATkHRYlHhamI(e +pIRjrIhq!J)'"JS+$Ji1%K)5&KBD'KSD(KiH(KiL)L)L)L)L)L)L(KiH(KSD'KB@ +%K)1$JS+"J)"rIhjpIAalHhTkHAPiH(GhGhCfGRCfGRCfGRCfGhGiH(PjHRYlI(e +pIRq!JB'#Ji5&KBD(KiL*LBU+LSU,LiZ,LSU+LSU*LBL)KiH'KB@%Ji1#JB#!Ihj +qIAamHhYkHRPjHAKiH(KhGhGhGhGhGhGiH(KiHAPjHRTkHhYmI(apIAjqIhq!J)' +"JS+$Ji5%KB@&KSD(KiH(L)L)L)L)LBQ*L)L)L)L(KiH'KS@&K)5$Ji+"J)"rIRe +pI(YlHRPjH(GhGRCeGA9dG(4dG(4dG(9eGACfGhGiHATkHhapIRq!JB+#Ji5&KSH +(L)Q*LSU,Lib-M)b-M)b-LiZ,LSU*L)L(KSD&K)1$JS'!J(pqIAemHhYkHAPiH(K +hGhCfGRCfGRCfGRCfGRGhGhGiH(PjHRTlHhamIAeqIRq!J)'"JS1$K)5&KBD'KiH +)L)Q*LBQ+LSU+LSU+LSU+LSU*LBQ)L)H'KS@%Ji1#JB"rIRepI(YkHAPiGhCfGA4 +dFh0cFh*bFR*cFh0cG(4eGAChH(KjHRYmIAjrJ)'#Ji5&KSH(L)Q+LSZ-M)b0MBf +0MBf0MBf-M)Z,LSU*L)H(KS@%K)1#JB"rIRjpI(YlHRPjH(KhGhCfGR9eGA9eGA9 +eGA9eGACfGRGhH(KjHATkHhamIAjqIi#"JB+$K)5&KSH(L)Q*LSU,Lib-M)b0MBf +0MBf0M)b-LiZ+LBQ)KiD&K)5$JS'!IhjpI(YkHAKhGR9eG(0cFR*bFA&aFA&aFA& +bFR0cG(9eGRGiHATlI(eqIi#"JS1%KBD(L)Q+LiZ-MBf1MSk1MSk1MSk1MBf0M)Z +,LSQ)L)H'KB5$JS'!IhjpI(YkHRPiGhGfGA9dG(4cFh*bFR*bFR*bFR0cFh4dG(9 +fGRGiH(PkHhapIAjrJ)'#Ji5&KSH)LBU+Lib-MBf1MSk2Miq2Miq2MSk0MBb-LiU +*L)H'KB5$JS&rIRemHhPiGhCeG(0bFA&`F'p[EQjZEQjZEQp[Eh"aFR*cG(9fGhK +jHhapIS#"JS5&KSH*LSZ-MBf1Mj!!N!#4NC+5NT+5NC'4N!#3!)q2MSf-LiU*L)H +'KB5$JB"rIRemHhTjH(GfGA4cFh*aFA"`F("[Efp[Eh"`F(&aFR*cG(4eGRGiHAT +lI(erJ)'#Ji@'KiL*LSZ-MBk2N!#4NC+5NT16Nj16Nj+5NC'3!)q2MSf,LSQ)Ki@ +%JS&rIRelHRKhGR9cFR&`EfjZE@aXDfYVDfYVDfaXE@eZEh"aFR0dGAGiHAYmIAq +!JS1&KSL*LSb0MSq3!*'5NT18P*58PC@9P*58Nj+5NC!!Mik0M)Z+L)H'K)1#J(p +pI(YjH(GfGA0bFA"[EfjYE@aXDfYVDfYVDfYXE'eYEQp[F(&bG(9fGhPkI(erJ)+ +$KBD)LBU-MBq3!*'5Nj59PCD@PjHAPjHAPTD9PC56NT'2MSf,LSL'KB1"J(jmHRP +hGR4cFA"[EQeXDfTTD'KSCfGRCfKSD@PUDfYYEQp`FA0dGRGjHhaqJ)'$K)D)LBZ +-MBq3!*'5Nj59PTDAPjHAQ*HAPjD@PC@8Nj+4Mik0LiU*Ki@%JS&rIAakHAGfGA0 +bFA"ZE@eXDfTUD@PSD'KSD'KSD@PUDQYXE@j[F(*cG(ChHAYmIS#"Ji@(L)U-MBq +3!*+6P*DAQ*LCQTUDQjZEQTUDQCLAPT@8Nj'2MSb+L)H&Ji&rIAYjGhCdFR&[EQa +VDQPSCfCQC@9PC'4PC@9QCQGSD@TVE@j[FA0dGRKjHherJ)+%KSH*Lib1N!#4NT5 +9PTHBQCQDQjZEQjZEQTUCQ*LAPT56NT!!Mif,LSL'K)1"IhelHAKfG(0aF'pYE'Y +UD@KRCQCPC@9PC@9PC@CRCfKTDQYXEQpaFR4fGhPlIAq"Ji@'L)U-MT!!NC19PTH +CQTZFR*fGRCkGRCfFR*ZDQCL@PC15N!#1M)U)KS5#J(jlHAKfG(*`EfeXDQPSCfC +PC@4NC'0MBf4NC@9QCfKTDQYXEQpaFR4fGhPlIAq!JS5'L)Q,MBk3!*+6P*DAQ*Q +CQTZEQjZEQjZEQTUCQ*H@P*15N!#1MBZ*Ki@$JAppHhTiGR4cF@pZE'YUD@KRCQ9 +NC'4MBf0MBf4NC@CQCfPUDfaZF(&cGACiHRaqJ)+%KSL+M)k3!*+6PCHBQCUER*f +GRTkHRTfGR*bEQTQBPT@6NC!!MSb+L)D%JAppHhPhGA4bF'pYE'YTD'GRCQ9PC'4 +NC'4NC@9QCQGSD@TVE@j[FA0dGRGjHheqJ)+%KBH*Lif1N!#4Nj5@PjLCQTZER*b +FR*bFR*ZEQTQBPjD8Nj'3!)k-LSQ(KB1"IhelHAGeFh*`EfeXDfPSCfGQC@9NC'4 +NC'4PC@CQCfKTDQaYEh"bFh9hHAYpIS##K)D)LSb1N!#4Nj5@PjQDQjZFRCfGRCf +GR*bEQTQBPjD8Nj'2MBZ+L)D%JS"qI(TiGR4bF@pZE@YUD@KRCfCQC@9PC@9PCQC +RCfKTDQYXE@p`FR0eGRKjHherJ)+%KBH*LSb1Mj'5Nj@@PjLCQCUDQTZEQjUDQCQ +BPjD9P*+4N!#1M)Z*Ki@$JAppHhPiGR4cF@pZE@YUD@KSCfCQC@9PC@9QCQCRD'P +TDQaYER"aFR4fGhPlI(k!JS1&KiQ+M)k2NC+8PCDAQ*QDQTZEQjZEQTUCQCLAPT5 +6NT!!MSf,LBH&K)+!IRakH(GeFh*aEfjYE'YUD@PSD'GRCfGRCfGSD'PUDfaYEQp +`FA0dGAGiHRYpIS##Ji@'L)U,MBk3!*'5P*@@PTHBQ*QCQCQCQCLBPjD9P*15NBq +1M)Z*Ki@%JS"qI(YjGhCdFh&`EfeXDfTUD@KSCfGRCfGRCfKSD@TVE'eZEh"bFh9 +fH(PlIAk!JS1&KiL+M)f1N!#4NT59PTDAQ*LBQCQCQ*LAPjD9P*15NC!!MSf-LSL +(KB1#J(jpHhTiGh9dFh&`EfjYE@aVDfTUDQTUDQTUDQYVE'eZEh"aFR0dGAGiHAY +mIRq!JS1&KSL*Lib1Mj!!NC+6P*@@PjHAQ*LBQ*HAPTD9P*15NC!!Mif-LSL(KB1 +"J(jmHhPhGR4cFR"[EQeXE'YUDQPTD@PTD@PUDQYVE'eZEh"aFR4eGRKjHhaqIi' +$K)D(LBU,MBk2NC+6P*59PTDAPjHAPjD@PT@8P*15NBq1MBZ+L)H&K)+!IhelHRP +hGR4cFR&`EfjYE@aXDfYVDQTUDfYVDfaXE@j[F(&bFh4eGRGjHRYpIRq"JS5&KSL +*LSb0MSq3!*'5Nj58PC@@PTD@PT@9P*56NT'3!)q1MBZ+LBH'K)+"IhemHRKhGR4 +cFR"[EQeYE'YVDQTUDQPUDQTUDfaXE@j[F(&bFh9fGhPkHheqJ)'$K)D(L)U,M)k +2N!#4NT16P*@9PCD@PTD9PC58Nj+5NC!!Mif-LiQ)Ki@$JS"rIAakHAKfGA4cFR& +`EfpZE@eYE'aXE'aXE'eYEQj[F("aFR0dGACiHATlI(jrJ)+$K)D(L)U,M)f1Mj! +!NC+5Nj18P*@9PC@8P*56Nj+4N!#2MSf-LiQ)KS@$JS&rIRalHAKhGA4cFR&`Efj +ZE@eXE'aXE'aXE'eYEQj[F("aFR0eGRGiHAYmIAk!JB+%KBD)LBU,M)f1Mj!!NC+ +6Nj18P*58P*58Nj15NC'3!)q1MBb+LBL'KB5#JApqIAYkH(GfGA4cFR&`EfpZEQe +YE@eYE@eYE@jZEfp`FA&bFh4eGRGjHRYmIAq!JB1%KBD)LBU,M)f1Mj!!NC'5NT1 +6Nj58Nj16Nj+5NC!!Mik0M)Z+LBL'KB1#JApqI(YkH(GfGA4cFR&`EfpZEQeYE@e +XE@eYE@jZEfp`FA*cG(9fGhKjHRapIRq"JS1%KSH)LBU,M)f1Miq3!*'4NT+5NT1 +6NT+5NT'4N!#2Mik0M)Z+LBH'KB1#JApqIAYkHAKfGA4cFR&aF'p[EQjYE@eYE@e +YE@jZEfp`FA&bFh4eGRGiHRYmIAk!JB+$KBD(L)Q+M)f1Miq3!*'4NT+6Nj16Nj1 +6Nj+5NC!!N!#2MSf-LSQ)KS@%JS'!IRelHRPhGR9dFh*aF("[EfjZE@eYE@eYE@j +ZEfp`F(&bFh4eGRGiHATlIAjrJ)'#K)@'KiL*LSZ-MBk2Mj!!NC'4NT+5NT+5NT+ +4NC!!N!#2MSf-LiU*L)H'KB1#JApqIAalHAKhGR9dFh0bFA&`F'p[EfpZEQp[Efp +`F(&aFR0cG(9fGhKjHRYmIAq!JB+$K)D(L)Q+Lib0MSq2N!#4NC+5NT+5NT+5NT' +4N!#2Mik0M)Z+LBL'KB5#JB"qIAalHAKhGR9dFh*aFA"`EfpZEQjZEQjZEfp`F(& +aFR0dG(9fGhKjHRYmIRq!JB+$K)@'KiL*LSZ-MBk1Miq3!*!!NC'4NC'4NC'3!*! +!Miq1MBb-LiU*KiD&K)1"J(pqIAYkHAKhGR9dFh*bFA&`F'p[Efp[Efp[Eh"`FA& +bFR0dG(9fGhKjHRYmIAjrJB+$K)@'KiL*LSZ-MBk1Mj!!N!#4NC'4NC'4NC!!N!# +3!)q1MSf-LiU*L)H'K)1#JB"qIAalHRPiGhCeG(0bFR&aF("`Efp[Efp`F("`FA& +bFh0dGACfGhKjHRYmIAjrJ)'#K)@'KiL*LSU,M)f0MSq2N!#3!*!!N!#4NC'3!*! +!N!#3!)q2MSf0M)Z+LBL(KS@%Ji'!IhjpHhTjH(GfGA4dFh*aFA"`F("[Efp[F(" +`F(&aFR0cG(9fGhGiHATlIAjrJ)'#Ji5&KSH)LBU,M)f1MSq2N!#3!*!!NC'4NC' +3!*!!N!#2Mik0MBb,LSQ)KiD&Ji+"J(ppI(YkHAKhGR9dG(0bFR&aFA"`F("`F(" +aFA&bFR0cG(9eGRGiHATlI(eqIi#"JS1%KBD'KiL*LSZ-M)f0MSk2Miq2Miq2Miq +2MSk1MBb-LiU*L)H'KB5$JS'!IhjpHhTjH(GhGR9dFh0bFR&aFA"`F("`F(&aFA* +bFh0dGA9fGhKjHRYlIAjrJ)'#Ji5&KSH)LBQ+Lib0MBk1Miq2Mj!!N!#3!)q2Miq +1MSf-M)Z+LBL(KS@%Ji+"J(jpI(YkHAKhGR9eG(0cFR*aFA&aFA"`FA&aFA*bFR0 +dG(9fGRGiHATlI(eqIi#"JS1%K)@'KiL*LSZ,M)b0MBk1MSq2Miq2Mik1MSf0M)b +,LSQ)KiD&K)1#JB"rIRemHhTjH(GfGA4dFh*bFR&aFA&`F(&aFA&aFR*cG(4eGRC +hH(PkHhapIRq!JB+$K)@'KiH)LBU,Lib0MBk1MSq2Miq2Miq1MSk0MBb-LiU*L)L +(KS@%JS'!IhjpI(YkHAKhGRCeG(4cFh*bFR&aFA&aFA*bFR0cG(4eGAChGhKjHRY +mIAjqIi#"JS1%KBD(L)Q+LSZ-M)f1MSk2Miq2Miq2Miq1MSf0M)Z,LSQ)KiD&K)1 +#JB"qIAalHRPiGhGfGA4dFh*bFR&aFA&aFA&aFR*bFh0dGA9fGhKiHATlI(eqIi# +"JS1%K)@'KiL*LSU,M)b0MBk1MSk2Miq1MSk1MBf-M)Z+LSQ)KiD&K)1#JB"rIRe +mHhTjH(GfGR9dG(0cFR*bFR*aFA&bFR*bFh0dG(9eGRGhH(PkHhamIAjrJ)'#Ji5 +&KSH(L)Q+LSZ-M)f0MBk1MSk1MSk0MBf-M)Z,LSQ)L)H'KB5$JS'!IhjpI(YkHAK +hGhCeGA4cFh0bFR*bFR*bFR*cFh0dG(9eGRGhH(PkHhamIAjrJ)'#Ji5%KBD(L)L +*LSU,Lib-M)f0MBf0MBf0M)b-LiZ+LSQ)KiH'KB5$JS'!IhjqIAalHRPiH(GfGR9 +eG(4dFh0cFh0cFh0dG(4eGACfGhGiHAPkHhamIAjrJ)'"JS1%KB@'KiL)LBU+LiZ +-M)b-M)f0MBf-M)b-LiZ+LSQ)L)H'KB5$Ji+"J(pqIAalHRTjH(GhGRCeGA4dG(4 +dFh4dG(4dG(9eGRChGhKjHATlHhapIRprJ)'#JS1%KB@'KiH)LBQ+LSU,LiZ,Lib +-M)Z,LiZ+LSU*LBL(KiD&KB5$JS+"J(pqIAemHhTkHAKiGhGfGR9eGA9eG(4eGA9 +eGA9fGRGhH(KjHATlHhapIAjrJ)#"JS1$K)@'KSH(L)L*LBU+LSZ,LiZ,LiZ,LSU ++LBQ*L)L(KSD&K)5$JS'"J(pqIAemHhYkHAPiH(GhGRCfGA9eGA9eGA9eGRCfGhG +iH(PjHRTlI(apIRjrJ)#"JS1$K)@&KSD(KiL)L)Q*LBQ*LSU+LBQ*LBQ*L)L(KiD +'KB@%K)1#JS'!J(pqIRemI(YkHRPjH(KhGhGfGRCfGRCfGRCfGRGhGhKiH(PjHRT +lI(apIAjrIi#"JB+$Ji5%KB@'KSH(L)L)LBQ*LBQ*LBQ*LBQ*L)L)KiH'KS@&K)1 +$JS'"J(prIRemI(YlHRTjHAKiGhGhGhCfGRCfGRGhGhGiH(KjHATkHhYmI(epIRp +rJ)'"JS+$K)5&KBD'KiH(L)L)L)Q*LBQ*LBL)L)L)KiH(KSD&KB5%Ji1#JS'!J(p +rIRjpI(alHhYkHRPjHAKiH(KiH(KiH(KiH(KjHAPkHRYlHhamIAeqIRq!J)'"JS+ +$Ji5%KB@'KSD(KiH(L)L)L)L)L)L)KiH(KiD'KS@&K)5$Ji+#JB'!J(prIRepI(a +lHhYkHRPjHAPiH(KiH(KiH(KiHAPjHATkHRYlI(apIAjqIhq!J)'#JS1$K)5&KB@ +'KSD(KiH(KiH)L)H(KiH(KiD'KS@&KB5%Ji1#JS'"J)"rIhjpIAamHhYlHRTkHAP +jH(KiH(KiH(KiH(KjHAPjHRTkHhYmI(epIRjrIi#!JB'#JS1$K)5&KBD'KSH(KiH +(KiH(KiH(KiH(KSD'KB@&K)5$Ji+#JB'!J(pqIRepI(alHhYkHRPjHAPiH(KiH(K +iH(KiH(PjHAPkHRYlHhamIAeqIRprJ)#"JB+#Ji1%K)@&KSD'KiH(KiH)L)L)L)L +(KiH(KiD'KS@&K)5$Ji+#JB'!J(pqIRepI(alHhYkHRPjHAPiH(KiH(KiH(KiH(K +jHAPkHRTlHhamIAeqIRprJ)#"JB+$Ji5%KB@&KSD(KiH(L)L)L)L)L)L)L)L(KiH +'KSD&KB5%Ji1#JS'"J)"rIRjpIAamHhYlHRTjHAPiH(KiH(KiH(KiH(KjHAPkHRT +lHhamIAeqIRprJ)#"JB+#Ji1%K)5&KBD'KSH(KiH(KiH)KiH(KiH(KiD'KSD&KB5 +%K)1$JS+"JB#!IhpqIRepI(alHhYkHRTjHAPjH(KiH(KiH(KjHAPjHATkHhYlI(a +pIAjqIhq!J)'"JS+$Ji5%KB@&KSD'KiH(KiH)L)L)L)H(KiH(KSD'KB@%K)5$Ji+ +"JB#!IhpqIAemI(YlHhTkHAPjH(KiH(KiH(KiH(KiH(PjHATkHRYlI(apIAjqIhq +!J)'"JS+$Ji5%KB@&KSD'KSH(KiH(KiH(KiH(KiD'KSD&KB5%K)1$JS+"JB#!Ihp +qIRepI(alHhYkHRTjHAPjH(KiH(KiH(KiHAPjHATkHRYlI(amIAeqIRprJ)#"JB+ +#Ji1%K)@&KBD'KSD(KiH(KiH(KiH(KiD'KSD&KB5%K)1$JS+"JB#!IhpqIRepI(a +mHhYkHRTkHAPjHAPjHAPjHAPjHAPkHRTlHhYmI(epIAjqIhq!J)'"JS+$Ji5%KB@ +&KSD'KiH(KiH(KiH(KiH(KiH'KSD&KB@%K)1$JS+"JB#!IhpqIRepI(amHhYkHRT +kHAPjHAPjHAPjHAPjHAPkHRTlHhYmI(apIAjqIhq!J)'"JS+$Ji5%KB@&KSD'KiH +(KiH(L)L(KiH(KiH'KSD'KB@%K)1$JS+"JB#!IhpqIRepI(alHhYkHRTjHAPjHAP +iH(KiHAPjHAPkHRTkHhYlI(apIAjqIhq!J)'"JS+$Ji5%K)@&KSD'KSH(KiH(KiH +(KiH(KSD'KS@&KB5%Ji1$JS+"JB"rIhjqIAepI(alHhTkHRPjHAPjH(KiH(KiH(K +jHAPjHATkHRYlI(amIAeqIRprJ)#"JB+#Ji1%K)@&KBD'KSH(KiH(KiH(KiH(KiD +'KSD&KB@%K)1$JS+"JB#!IhpqIAepI(alHhTkHRPjHAPjHAKiH(KiH(PjHAPjHRT +kHhYmI(apIAjqIhq!J)'"JS+$Ji1%K)@&KBD'KSD(KiH(KiH(KiH(KSD'KS@&KB5 +%K)1$JS+"JB#!IhpqIRepIAamHhYlHRTkHAPjHAPjHAPjHAPjHATkHRTlHhYmI(a +pIAjqIhq!J)'"JS+$Ji5%K)@&KBD'KSH(KiH(KiH(KiH(KiD'KSD&KB5%K)1$JS+ +"JB#!IhpqIRepIAamHhYlHRTkHRTjHAPjHAPjHAPjHRTkHRYlHhYmI(epIAjqIhq +!J)'"JS+$Ji1%K)@&KBD'KSD'KiH(KiH(KiH'KSD'KB@&KB5%Ji1#JS'"JB#!Ihp +qIRepIAamHhYlHhTkHRTkHAPjHAPjHAPkHRTkHRYlHhamI(epIAjqIhq!J)'"JB+ +#Ji1%K)5&KB@'KSD'KSD(KiH'KSD'KSD'KB@&K)5%Ji1#JS'"J)"rIhjqIAepI(a +lHhYkHRTkHAPjHAPjHAPjHAPjHATkHRTlHhYmI(apIAjqIhprJ)#"JB+#Ji1$K)5 +%KB@&KSD'KSD'KSD'KSD'KSD'KB@&K)5%Ji1$JS+"JB#!IhpqIRepIAamHhYlHRT +kHRPjHAPjHAPjHAPjHAPjHRTkHhYlI(amIAeqIRprJ)#"JB+#JS1$K)5%KB@&KSD +'KSD'KSH(KiD'KSD'KSD&KB@%K)5$Ji+#JB'!J)"rIhjqIAemI(alHhYkHRTkHRT +jHAPjHAPjHRTkHRTlHhYlI(apIAeqIRprJ)#!JB'#JS1$Ji5%KB@&KBD'KSD'KSD +'KSD'KSD'KS@&KB@%K)5$Ji+#JB'"J)"rIhjqIRepI(amHhYlHhTkHRTkHRTkHRT +kHRTkHRYlHhYmI(apIAeqIRprJ)#!JB'#JS1$Ji5%K)@&KB@'KSD'KSD'KSD'KSD +'KB@&KB5%K)1$Ji+#JB'"J)"rIhjqIRepIAamI(YlHhTkHRTkHRTkHRTkHRTkHRT +lHhYlI(amIAepIRjrIi#!J)'"JS+#Ji1%K)5%KB@&KB@'KSD'KSD'KB@&KB@&K)5 +%Ji1$JS+"JB'!J(prIhjqIAepI(amHhYlHhTkHRTkHRTkHRTkHRTkHRTlHhYmI(a +pIAeqIRjrIi#!JB'"JS+#Ji1%K)5%KB@&KB@&KBD'KS@&KB@&KB@%K)5%Ji1$JS+ +"JB'!J(prIhjqIRepIAamI(YlHhYlHRTkHRTkHRTkHRTkHhYlHhYmI(apIAeqIRj +rIi#!J)'"JB+#Ji1$K)5%K)@&KB@&KB@&KB@&KB@&KB@%K)5%Ji1$JS+#JB'!J)" +rIhpqIRjpIAemI(amHhYlHhYlHRTkHRTkHhYlHhYlI(amI(epIAjqIRprIi#!JB' +"JS+$Ji1%K)5%KB@&KB@&KB@'KSD&KB@&KB@&K)5%K)1$Ji+#JS'"J)#!IhpqIRj +pIAemI(amHhYlHhYkHRTkHRTkHRYlHhYlHhamI(apIAeqIRjrIhq!J)'"JB+#JS1 +$Ji5%K)5&KB@&KB@&KB@&KB@&KB@&K)5%K)1$Ji+#JS'"JB#!IhprIRjpIAepI(a +mHhYlHhYlHRTkHRTkHRYlHhYlHhYmI(apIAepIRjqIhq!J)#"JB'#JS+$Ji1$K)5 +%K)5&KB@&KB@&KB@&KB5%K)5%Ji1$Ji+#JS'"JB#!IhprIRjqIAepI(amI(YlHhY +lHRTkHRTkHRTkHRTlHhYlHhamI(epIAjqIRprIi#!JB'"JS+$Ji1$K)5%K)@&KB@ +&KB@&KB@&KB@&K)5%K)1$Ji+#JS'"JB#!IhprIRjqIAepI(amHhYlHhYlHRTkHRT +kHRTkHhYlHhYmI(amI(epIAjqIhprJ)#!JB'#JS+$Ji1$K)5%K)@&KB@&KB@&KB@ +&KB@&KB@%K)5%Ji1$JS+#JB'!J)"rIhpqIRepIAemI(amHhYlHhYlHhTkHRTlHhY +lHhYlI(amI(epIAjqIRprIi#!JB'"JS+#Ji1$K)5%K)@&KB@&KB@&KB@&KB@&KB@ +%K)5%Ji1$JS+#JB'"J)#!IhprIRjpIAepI(amI(YlHhYlHhYlHhYlHhYlHhYmI(a +mI(epIAjqIRjrIhq!J)#"JB+#JS+$Ji1%K)5%K)5&KB@&KB@&KB@&KB@%K)5%Ji1 +$Ji+#JS'"J)#!IhprIRjqIAepI(amHhYlHhYlHRTkHRTkHRTkHhYlHhYmI(amIAe +pIRjqIhq!J)#"JB'#JS+$Ji1%K)5%KB@&KB@&KB@&KB@&KB@%K)5%Ji1$Ji+#JS' +"J)#!IhprIRjpIAemI(amHhYlHhTkHRTkHRTkHRTkHRYlHhYlI(amIAepIRjqIhp +rJ)#!JB'"JS+#Ji1$Ji5%K)5&KB@&KB@&KB@&KB@&K)5%K)5$Ji1#JS+"JB'!J(p +rIhjqIRepIAamI(alHhYlHhYlHRTkHRTkHhYlHhYlI(amI(epIAjqIRprIi#!J)' +"JS+#Ji1$Ji5%K)5%KB@&KB@&KB@&KB@&KB5%K)5%Ji1$JS+#JB'"J)#!IhpqIRj +pIAepI(amI(YlHhYlHhYlHhYlHhYlHhYmI(amIAepIAjqIRprIi#!J)'"JB+#JS1 +$Ji5%K)5%KB@&KB@&KB@&KB@&KB5%K)5%Ji1$JS+#JB'"J)#!IhprIRjqIAepI(a +mI(alHhYlHhYlHhYlHhYlHhYmI(amI(epIAjqIRjrIi#!J)'"JB+#JS+$Ji1%K)5 +%K)@&KB@&KB@&KB@%K)5%K)5$Ji1$JS+#JB'"J)#!IhprIRjqIAepIAamI(alHhY +lHhYlHhYlHhYlHhYlI(amI(apIAepIRjqIhprJ)#!JB'"JS+#JS1$Ji1%K)5%K)5 +%K)@&K)5%K)5%K)5%Ji1$Ji+#JS'"JB#!IhprIRjqIAepI(amI(YlHhYlHhYkHRT +kHhYlHhYlHhYmI(amIAepIRjqIhprJ)#!JB'"JS+#JS1$Ji1%K)5%K)@&KB@&KB@ +&KB5%K)5%K)1$Ji1#JS+"JB'!J)"rIhjqIRepIAemI(amHhYlHhYlHhYlHhYlHhY +lHhYmI(amIAepIAjqIRprIi#!J)'"JB+#JS1$Ji1%K)5%K)@&KB@&KB@&KB@&K)5 +%K)5%Ji1$Ji+#JS'"JB#!IhprIRjqIAepIAamI(amHhYlHhYlHhYlHhYlHhamI(a +mIAepIAjqIRprIi#!J)'"JB+#JS1$Ji1%K)5%K)5&KB@&KB@&KB@&K)5%K)5$Ji1 +$JS+#JB'"J)#!IhprIRjqIAepIAamI(alHhYlHhYlHhYlHhYlHhamI(amI(epIAe +qIRjrIhq!J)#"JB'#JS+$Ji1$K)5%K)5%KB@&KB@&KB@&KB5%K)5%K)1$Ji+#JS' +"JB#!J(prIRjqIAepI(amI(YlHhYlHhYkHRTkHhYlHhYlHhYmI(amI(epIAjqIRp +rIhq!J)#"JB'#JS+$Ji1$K)5%K)5%KB@&KB@&KB@&K)5%K)5%Ji1$JS+#JB'"J)# +!IhpqIRjpIAemI(amHhYlHhYlHRTkHRTkHRTlHhYlHhYmI(amIAepIRjqIhprJ)# +!JB'"JS+#Ji1$Ji5%K)5%KB@&KB@&KB@&KB@&K)5%K)5$Ji1#JS+"JB'!J(prIhj +qIRepIAamI(alHhYlHhYlHhYlHhYlHhYlHhYmI(amIAepIAjqIRprIi#!J)'"JB+ +#JS1$Ji5%K)5%KB@&KB@&KB@&KB@&KB@&K)5%K)1$Ji+#JS'"JB#!IhprIRjqIAe +pI(amHhYlHhYlHhTkHRTkHRYlHhYlHhamI(apIAeqIRjrIhq!J)#"JB'#JS+$Ji1 +%K)5%KB@&KB@&KB@&KB@&KB@&KB@&K)5%K)1$Ji+#JB'"J)"rIhpqIRepIAamI(a +lHhYlHhTkHRTkHRTkHRTlHhYlHhamI(epIAeqIRjrIhq!J)'"JB+#JS1$Ji1%K)5 +%K)@&KB@&KB@&KB@&KB@%K)5%K)1$Ji+#JS'"J)#!IhpqIRjpIAemI(alHhYlHhY +kHRTkHRTkHRTkHhYlHhYmI(amIAepIAjqIRprIi#!JB'"JS+#JS1$Ji5%K)5%KB@ +&KB@&KB@&KB@&K)5%K)5$Ji1#JS+"JB'!J(prIhjqIRepIAamI(YlHhYlHRTkHRT +kHRTkHRYlHhYlHhamI(apIAeqIRjrIhq!J)#"JB'#JS+$Ji1$K)5%K)@&KB@&KB@ +&KB@&KB@&K)5%K)5$Ji1#JS+"JB'!J(prIhjqIRepIAamI(alHhYlHhYlHhYlHhY +lHhYlHhYmI(amIAepIAjqIRprJ)#!JB'"JS+#Ji1$Ji5%K)5&KB@&KB@&KB@&KB@ +&KB@&K)5%K)1$Ji+#JS'"JB#!IhprIRjqIAepI(amI(YlHhYlHhYlHhYlHhYlHhY +lI(amI(apIAeqIRjrIhq!J)#"JB'#JS+$Ji1$K)5%K)5&KB@&KB@&KB@&KB@&KB5 +%K)5$Ji1$JS+#JB'!J)"rIhpqIRjpIAemI(amHhYlHhYlHhYlHhYlHhYlHhYlI(a +mI(epIAeqIRjrIhq!J)#"JB'#JS+#Ji1$Ji5%K)5%KB@&KB@&KB@&K)5%K)5%Ji1 +$Ji+#JS'"JB#!IhprIRjqIAepI(amI(YlHhYlHhYlHRTkHRYlHhYlHhYmI(amIAe +pIAjqIRprIi#!J)'"JB+#JS+$Ji1$K)5%K)5%KB@&KB@&KB5%K)5%K)1$Ji1#JS+ +"JB'!J)"rIhpqIRjpIAemI(amHhYlHhYlHhYlHhYlHhYlHhYlI(amI(apIAeqIRj +rIhprJ)#!JB'"JS+#Ji1$Ji5%K)5%K)@&KB@&KB@&KB5%K)5%K)1$Ji1#JS+"JB' +!J)"rIhpqIRjpIAepI(amI(alHhYlHhYlHhYlHhYlI(amI(apIAepIRjqIRprIi# +!J)'"JB'#JS+$Ji1$Ji5%K)5%K)@&KB@&KB@%K)5%K)5%Ji1$Ji+#JS'"JB#!J(p +rIhjqIRjpIAepI(amI(amI(YlHhYlHhYmI(amI(amI(epIAeqIRjqIhprJ)#!J)' +"JB+#JS+$Ji1$Ji5%K)5%K)5%K)5%K)5%K)5%K)5$Ji1$JS+#JB'"J)#!IhprIRj +qIAepIAamI(amI(YlHhYlHhYlHhYlHhamI(amI(epIAeqIRjqIhprJ)#!J)'"JB' +#JS+#Ji1$Ji1%K)5%K)5%K)5%K)5%K)5$Ji1$Ji+#JS'"JB'!J)"rIhpqIRjqIAe +pIAamI(amI(YlHhYlHhYlHhYlI(amI(amIAepIAeqIRjqIhprJ)#!J)'"JB'#JS+ +#Ji1$Ji1%K)5%K)5%K)5%K)5%K)1$Ji1$JS+#JS'"JB'!J)"rIhpqIRjqIAepIAa +mI(amI(amI(amI(amI(amI(amI(epIAepIRjqIRprIhq!J)#"JB'"JS+#JS+$Ji1 +$Ji1$K)5%K)5%K)5%Ji1$Ji1$Ji+#JS+#JB'"JB#!J(prIhpqIRjqIRepIAepIAe +pI(amI(amI(amI(epIAepIAepIRjqIRjrIhprJ)#!J)'"JB'#JS+#JS1$Ji1$Ji1 +%K)5%K)5%K)5%Ji1$Ji1$Ji+#JS+"JB'!J)#!IhprIhjqIRjpIAepIAemI(amI(a +mI(amI(amI(epIAepIAjqIRjqIhprIi#!J)#"JB'"JB+#JS+#Ji1$Ji1$Ji1$Ji1 +$Ji1$Ji1$Ji1#JS+#JS'"JB'"J)#!J(prIhpqIRjqIRepIAepIAepIAepIAamIAe +pIAepIAepIAepIRjqIRjqIhprIi#!J)#!JB'"JB'#JS+#JS+#Ji1$Ji1$Ji1$Ji1 +$Ji1#JS+#JS+"JB'"JB#!J)"rIhprIRjqIRjpIAepIAepIAamI(amI(amI(apIAe +pIAepIAjqIRjqIRprIhprJ)#!J)#"JB'"JB+#JS+#JS+$Ji1$Ji1$Ji1$Ji1$Ji+ +#JS+#JS+"JB'"JB#!J)"rIhprIhjqIRjqIRepIAepIAepIAepIAepIAepIAepIAe +pIRjqIRjqIhprIhq!J)#!J)'"JB'"JS+#JS+#JS1$Ji1$Ji1$Ji1$Ji1$Ji1#JS+ +#JS+"JB'"JB#!J)#!IhprIhpqIRjqIRjqIAepIAepIAepIAepIAepIAeqIRjqIRj +qIhprIhprJ)#!J)#!JB'"JB'"JS+#JS+#JS+$Ji1$Ji1$Ji1$Ji1$JS+#JS+#JS+ +"JB'"JB#!J)#!IhprIhpqIRjqIRjqIAepIAepIAepIAepIAepIAepIAjqIRjqIRj +rIhprIhq!J)#!JB'"JB'"JS+#JS+#JS+$Ji1$Ji1$Ji1$JS+#JS+#JS+"JB'"JB# +!J)#!IhprIhpqIRjqIRjpIAepIAepIAepIAepIAepIAepIAeqIRjqIRjqIRprIhp +rIi#!J)#!JB'"JB'"JB+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JB'"JB'"J)#!J)" +rIhprIhpqIRjqIRjqIAepIAepIAepIAepIAepIAepIAepIRjqIRjqIRprIhprIi# +!J)#!JB'"JB'"JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JB'"JB'"J)#!J)#!Ihp +rIhprIRjqIRjqIRjqIRjqIAepIAepIAepIAjqIRjqIRjqIRjrIhprIhprJ)#!J)# +!JB'"JB'"JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS'"JB'"JB#!J)#!J)"rIhp +rIhprIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIhprIhprIhq!J)#!J)#!JB' +"JB'"JB'#JS+#JS+#JS+#JS+#JS+#JS+#JS'"JB'"JB'"J)#!J)#!J(prIhprIhp +rIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIhprIhprIhprJ)#!J)#!J)'"JB' +"JB'"JS+#JS+#JS+#JS+#JS+#JB'"JB'"JB'"J)#!J)#!J)"rIhprIhprIhjqIRj +qIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRprIhprIhq!J)#!J)#!J)'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'!J)#!J)#!J)"rIhprIhprIhprIRjqIRj +qIRjqIRjqIRjqIRjqIRjqIRjqIRprIhprIhprIi#!J)#!J)#!JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'"JB'!J)#!J)#!J)#!IhprIhprIhprIhprIRjqIRj +qIRjqIRjqIRjqIRprIhprIhprIhprIi#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'"J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhpqIRj +qIRjqIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB#!J)#!J)#!J)"rIhprIhprIhprIhprIhprIRjqIRjqIRjqIRj +qIRjrIhprIhprIhprIhprJ)#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'"JB#!J)#!J)#!J)#!J(prIhprIhprIhprIhjqIRjqIRjqIRjqIRjqIRjqIRj +qIRprIhprIhprIhprIi#!J)#!J)#!J)#!JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhjqIRjqIRjqIRjqIRprIhp +rIhprIhprIhprIi#!J)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIi#!J)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"J)#!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprJ)#!J)#!J)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB'"JB'"JB'!J)#!J)# +!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!JB'"JB'"JB'"JB#"J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!IhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!!!"&3!!"!!%!"3!!!+!!!B"4!!!!!!!8!!!!!!! +!44C@lSZM!!"&&!!!448!2)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!IhprIhprIhprIRTjHRTlHRCfH(*ZEfYSFR9eKBeeAeP28'CeHiU4J(CdCQ&L9PT +fJAZ#J@TME@P[LBaiEfePCh"ZGib,K)Q!E@KI6&Z"M)kGPS"kEPYJEh"eK)@%LRY +TG(piJ*!!KRaqEPeYJ)#*P)q1LR*KD'GPIC+3!*LMMAChDeeVIAk*NB*lJAYdIAj +mNC9lG(PRAhZ,LjZBI(L!F@*NDhf9QC5HPA4QC@"aLiChK)alGhplJ)U(KSq+Hh4 +UB(@0LBqHMA*[Df0cKS'(QTUAP(eSE@jRHSf'KBThES1*IBH@M)D'GR"jFQYrMj+ +FPRPZG'GLHif4S+',KBTiCQGUGiD%J)k4Hh4rJSZFNRYlHfCLFhf'NBU,QT!!G'P +MA(#1P*qcS(TcG@YbHQYVK)U&M)KrL)KqMU+6IhTQ@A5)JBqNPi5!HhGhDQ@"Qk# +YVj*qH@*BHSjpHi4mISk,KT'0JSU1KBKqB'&hHB@VVBPiF@*RHReqLj'EUjprG'e +FB'pdJTH5GA1*PCU@K(CiI(q'HfG[Hi1JVSPTG(GPFSZ+Lj+2NCZBL(GK@h"kH*+ +LK('!KSLIRhPYIRjjHhPlI(4mQk52JQj*8i'-MUZVJ(5(JAGkDePNHBL9PiCiD9q +'ZUk'FQ*CES4pJ*++J)k8LheM69ChNUUiRB"lF'GqMACPFAL"QCQ#HAClNk#AN!" +m8NGUJSQBNR0aKRj`GQjDBRbBX+k6H@KJFik$FhjkBh1FRB@#JRk0PSYmCP4CDhZ +AVTKeF@TIF(jXCS'4MjkQNRjU9Q&qMC',EQ1'PiUBT)CcKBf"IQp,5(@KXk@+I(" +@9(H)ISQ6Ii#BNhYaD&eUJSZ-IfKSHAq1TTerHhTPCRPZ@fZ(PD'MLh4J8Q1!MCb +TPRk*NAYfJ("RHS*kHAGUCfplPkL5IAGJ6f9lFAD4NBfHQAeXBeYNJTZLPi5$L(G +YK)afG)9pGB&lF(k1N!#9Q)q!CNY6DQaiQTq-MC&mEA*VC(Z@TDUGLiCqGiD9MBf +8JR5%Lhf%R*U4PBTdC9G19@*dMj0lGRKG6@0XCRbEQC@GNAjfEQplHhU"GepRI(Q +)VDb9PC&eDQaHARU-Q+@NQ)9N8fH!LD5rUSb2KQeXG'KRJjL5GeP6@epeRVDZT)j +@-d&$3@U9RD+cXU#+E&TKF)fVUTL9L'PdQCL+MB4bFR*F699QJCfZ[Dpa2d"*6R@ +JSCZMNRKlJQjIG*UiZU'0H&G@GibFX+"`BA"N9fYlHB5AQije@%G%8)'aTSf+G90 +AE'9QJBf-QD@HMAk!QV#f[DjmA&e9@)+DIh+'LA9QA&KJEB@NXDZJI8Y*DQjLGSK +rKTqNT+kRQ*kVVCYf@9YF@RUGPBQ+GQ*fMRpbJ)H!K)PrDPGBCfa`K)PZDAY`DSq +SQ*!!Rjb3!*53!)"`C@q"I'pZBdpHJSf9V+D(L*Q0FfKPB&jLFiD)KiKmHjZaSCZ +RPRU!M(YUCPT19@KeEQ&ZLBf5XVfCKT')I)H$Be0EB@PrNTL3!)'*T,#[VTPmJj0 +qDh&P5%CGD'GUE("eHj!!SjkBP(PGC(4N9@0SD)5PX,'iZ+qVVE1aQSZ,Ih*lH9j +@BPP4Di*pGAf+QDHTS)a[APP)3PCF5daPFA@(KR"YIif6Q*b@KA0cJB0qIR4UHC' +2M+HeV,2#ZTjqC&&*6f&dHAf,KhZ0S**qK)Q#ND'9I@aNB@*UFfjA6f9bFiZNUVM +!U)k@Q(09@'PfJBZ4M)5(P*+3!+#HI@jhE&06A9*1@&TEBfTN@@&qQk+MVkD"E'j +VEB'+J)qh`lDYUDQSVXACe,qXM'KYHf04BfYC@'YS99*EFBUGTj&H1$!Y+6jB@9a +`I(f&M(YMFTQcZVDUMA"XIBfDTTjqGj+HNj!!R+'P[Y6)TAeB1cG9Fh9TE(CZEiD +0IRb*P+'aThT65%a39fq$G9eIGBHEXl'RZFQpU*f'B9&PKCZLST9c@fU!KBqNRSZ +4NhPH6$NZ2PPNBPeC8deEJ*LCR*k2JBb0FQ9ZF(')V,QcV+kf[ml@`UDJSjZHXl# +0HRKSA'CX@%KAF)L@NS"F3e4XDPeA35Ne8f4dM*@,LU#eVjCrIBDAXlULK@j00e1 +$P*bZYl[$aE56HA"`F("jFeK1A'PlT,LFJSH0M)k#E'C[HS1&GQ"(-6PBEhk2P*5 +Xcp'qY+5&DfTeHA9cG(D%TVkXNj19LS5)IQjZEQPaJB*N1bJ[394VJSk8QBprHA9 +K6&4[L*bKPSZ&M*@GV,ZmVU+PT*qNTk+LUkQ0EP8r0dCQLkc$al+5IA9P5M`k3&K +bH'PJC'4MES@CRjD'ISZNUSppLBjiC&Y@9f&[LERLk0#YQT&h9e"HCfTeIiD9N@j +9C)#3!*D9Li5#JB&mHi4lA%C5B9aGF)@K`pE4[kCr9%*0CRU0TV+[YEZUMiH)JSQ +GS*D8PiapISf1E6iS,$%j499iTm#hVUU,@cFZ2@'(QCQ9P*1)ISkYY+fe`m5kUSk +"P+HKMAjV45N`8h+&Q+5CMBTf8$Xk0MpRKS*cDQCPFBZLUkqhYk'@S*U%IB@(KB4 +b8dC-ARZVi2(6VCL*F'"KAf"cJ(eqKRC44f1(SUbRNh9MDACqLBprEhb"C%3p4&9 +qX0,JelD*D@CfJB1*PTQARTZ$EfppP,I3`*PkEQpkLT'#E@&9363j1$*,H*HNTjC +U3$4$B)'DSjU5PjD%FR5!MUh5hp6!UC10RDbNMATV@8K-Ah#%R+baXk4i5$!Y190 +bJhpcD@"@9f9cHikSYVQjVjH1Qk5KRjb'Be*GFBQQa-frY+Q0E9e869f"P*!!LAj +P58CFFAZ"KB&pK)GeD'eZB9TE8d9#4e9qZ0hFbELKJ@0FC@4UKCq[[lqIH("mJSL +9SD#ANC'@QjH$CPKB5c-U,M0(GU1j[E#0BNK(694QIik5QU'6I(ChH)QU[EUeX+# +8Q++GLhe`A8e0@'&fRll$a,fED8Y!0ceCFhYjHR9J6&"LFAk9TUU`XCU%LTUBMif +"C8j-@@Q$U-6-bmQaKfpR8dPII)+"Jh&869eNA@0jJRPjLBPiGAelHB1!D&C86e" +[RVUq[EURLhYdDQf'S+Ha`V+&D'T[Ehq5PBk-MB9pJB"XB'KS6MSm1MPDL*bHSja +k9da+4%9HI)Q9UDU5IhKZEj+eYUqj[+@4PTb6LSGiA&"A8NCBISq@V,D@DP!l+64 +FGA9mM)ThFRelFi+IVVI'[TQ'NjqKUlDVMRajFh'#Q*bJZXDRIQ4&)LG5EhH$LRG +B68`q-N&CB'H!LR*EAf4LG)k"@dBr0$aNMk'[bpE&XkZ@GAQK[FVHjX5@K)*mJ*@ +KQjUMQ(edHR&JD(ac8M`S#!)S8QZ(TU@(F@TD5P4bKC'UZ*jlF@eNFjkkYl#YRSU +(N!#2MTLHLfeD4LBF0eYeNUDCG9a21c*'CR9lLj+$EQ9I@@kBXE1eXTQ#KjkZZmV +)VjD+H&a5Ci'@Y-l#QA9E3N"GGAPiI(CM9P923NKJE@ebFeP!5&TQHjDCIQ4@4M) +a4PpfP+k`Sj@!C@@1YF,*d-1TQ)q&KC@PTD+XX*YrHAjqKjH5FeFp'`SL5fKiLjL +6JQpG58GGGi5@UCprDfTXGSkLTU5STjH(L)b(L*56Hf*,+K8P49efN!#9K(0ZD@" +GD(4lK*!!Mi0pHh4lQl5aU+HJNj5LVV5iYkL4Ih&E599hNUM#bE+5H@"5A'PRAf" +PANe%4NG+A(4lH(*K5NPHFAf)MApQ8N-d-%&II*l#d,qRQBYpKk#VVE'bV+Q[XUD +FSkkRPj1-GfpjJ)'+Lh*53$%K)cYCEhb,NSU#Ih4NDS'(J)D0JR4dGRH"M)k*KBZ +5Mj+KTCL6P)"I56BN+%0FDA4rHQPKC@GG@QK`GB@8MiH,MifCVlHVRjZFRkZj[,D +cVjZ!G@Y@6f@#Nk+VRSZ!H'aPD@aPAQ&QBPYFA&CFE("SD@YJ@QCiJB&qG@GE98j +'5Q*hJjHYVTb1L)+&NCLFTV+cUkLbYUqXXV5TSCU+JSZ3!)f2PBGS6d%p2N98C'e +fIha`EQjJ@@b"KBH*JRPpKSfCTkbPRD#QRC!!NTQ6MBk%E9a32MG+BQKPCQPM8NT +06%Y8A&eTJ)4lLD1[Y,r%[V5SR*QKVV@ZUV1cR)D#HR1"PCkSYDk8JAYeE'GTE'T +XGhGXDR*aEhq,HQGK9dP+A@abG(4YBPYG9NK4DR4iLTH1KBD&JBQ8P*!!PU'NRk' +YXkqd[V@NQiPbG)L3!*!!PTU0H@KD6dP-9epVJBq'IS+#H(Z(K(eqHhCkKj@IUE' +dUCH3!)q'K*!!Q*LDPAaM@P&!1dGBAf&PDfeUD@CF@@"B5%T68&4YM++f`EkeV+1 +GR+HlamE*d-HaST@)LTQIRD'PRT@6PjZ8KRPYA9&*2cY,@f0cKiCeDQ4IB'CUE'a +YE@056eC@@'apJ)L3!)Z'MC14NC52K(elIAk#NkH`Ym#mVDLQQ*1HSjZ8MAp`Cf0 +NDA&qJAGdIAjjJBq3!*!!P)TiEQPKAh#,RkLUVDbKPj@4Mj@8Ki#"G@&FB@9QBea +86%G)6P4MFhCaF@T94NG+8'9pMTqZX+UQTUUVUUqiYDfUT*QFTD1JU+QGNSq2N!# +5PTbHPB9b@NY-68pKGRPcGA9XBea@99TKBepFA9pFB'aaEQeVAeeSF(*rNTbKSk# +CMi@%MCZ[`F1q`m1`T+DMPSq2M)Z*I@pVFheqFfGJ8d9)99jXIi@(N!#1H@9F@&P +MGBZFSD+PTD5ST*D4Q*@,Lib$HhCZE'eL6MSb1NP5A(+!I(CbBP*268TAGC!!S+U +aYE5[Vl+aVl+bVVE"[,Dl[,1YTjU+J(b!LjDJTU1ENRYI9&K46&PTER*bDfGSCQ* +NDA"[B9GFB9jIBPeGBea5A'a[F)#ATkfTSTf@NT5CSl5lX+UeYkbQTDDUVDLFNSK +pFfpfIhY[DfGF@@&QEi16PC@@L'jA69*IEAf,Mj+CR*UNXl1ZY,DSPB4aC'*MC'9 +N@d`l18K@B(+'LB0rFPT-5djDGT'FQC@8NT!!PUHeZm'rXUZXT*16SDHPSjf6M)Q +1QUUl`V@IN!"m@8!p3846C@aYEQKH@f0YF@p`FQTE8e06@&pE@@&P@%pBCh@(QkU +[TT1$IB14Q*UQYlQaVkqSSk5MT+QPNRTVD'TVERGlFfTM9e"@A'9qR+LINS&ZB&T +IEAk1QCQ@QTk9N!#DSk@TUCb*I(9`FRPmG@GD88Br3dj@CRk+K(PQ5$-f4&0PHiZ +5PCQFQCLJUDbbYkZAMiZ#JSqGTUQNQBk-NCUMVlfpUjQ,FeY489KTI)&jF'PJ@&G +LF(GfGA"RCQ4E@'9`E@PSAP06@QL!RDk[TCH)H'ehLTDP[FV(`EDJNjQMSk'KQSC +aD@TXEhZ&JAGY9d!k3NTCGT!!Q*1*HQYNC@YbJ*1AL(q"IACqMjfXZE5KPT+*IRk +*NBKhCP-r06Fq8'epGfjVA88k3P0TJ*1FQT5,IRH#NTHEUE+YUULJQCfQUDLJMRC +PCA5&PDZp[E+SPATTAe02A@aVB&G04%&*@'0UFA"PBQKQA@CmKS"iE&Y04dP@F)f +IR*@APSKrKj5LYm5qYV+PN!#+PD'RUDUMNAphFh*lKS&dE@*+1Mj,@R+1SUQQQB0 +UB'9YFi'4NSH#IhKiIi1)PCqEN!#)KiU3!*fZXUD4H9j069"6CAk0MSKmCdp"4&& +PHB@#HRGdEA'%NjZQVkZHNSL#KT1HSCb8KhCYHBqKXmI3cmQjQi"bE'PaJ)L"EeY +04N9,8&GNFA"RD'YTD("hHhacB903@'4cLkHfYE'TQ)4iFhH+T+qZV+LGNBk8R+# +MS*@*Ih4RBfa`D'*J98C!4%jJHC!!Rk5KNhaTCQY[HBU6NT!!M)"dGRf$MTqXUU# +EQjZGT+5CMB&YA&PIBff"PCZ@L'e11$!b2&"RF@pZEfKMDR0mLTbNRCH8N!#2QDQ +dXkUINB0qK)ZCVm$$a-'YMhGUBf*VGhYhF'CD8eTMC@PeHA"SCf4KD("aFhCaBPC +CC(&rNk@TTTk2Hh0dGAL+RkDQTD#CP*+5P*UIQ)GrJS"jISb2L(pbB&436%jHH)b +4MiTrE&eC@&YTHAq$M)q)KSk9Qk+RSTD1M)f5S+fZU+5CJh&YDQ9UH)+)Li&T8de +38PKUI(aeGACbFRGiHiQAPBZ,Miq3!*URVl#TQiZ&KS@%N!#QXV5fXk@5Jh9[GiH +0LB5#HfeQDQeVD'KMAf"I@9YRFACjH@pE58*%6&jcJBQ6QT@3!*'2KiQ@SULVUU5 +HR+'QU+LMPSU-Nj'-M)U&JAKS9dj*3dKEG)+$JAjiFh*aERH)N!#4Q*k@Ki5,NC@ +@NSZ(L)U)MCQEMi4qF&j66%0'@@efHhppG'p`FR"ZFhCcG(TjGRZ#KSqFRjL8Nj! +!LiZ4Q*H9NiTrHhYdF(f3!*fSXV1USCQ3!)k8QCD-KiL#GQjSB@"RE@jeHh&PC'T +VCf&C6d8r2Mp'@@YfKjfST*qCM(ppJiU8RD'LT+QYUkUZXDbUVDUHPSk$HhKaBe9 +04Mir5eaZJ*+DQjQ5JA0cGR*bIB@$JS+#JiU6QCfRX+kNS+#BMSQ$GfaL8dG)8Pe +PF(arGfjQA&069eGIFAajGhk"JSL6Qk#LRjL8PjZCPTUFNiU)K(erLjLT[-M'Zkk +HLAKbF@eSDfjTC'9PBfPbGhU!JReiHRjrIAPcD@&E8NP+9Q"VIC!!Q*@1JR9[Eh0 +jKT@HS+1UVkbUUkbVUkDCN!#1LS&mIhjdD@4JA&eRFAU*P*++Ki&cD@aaG(KmI(P +jH(9fJT+9NTHJSU#KRjQBQC+)JAKQ9P0DBfYeI(K[D&e24dT08&pcI(f!K)D,PCZ +DQ*H5LSH+LB+#MT@9P*'*JS5,PDDh[EH[UCq1J(PiGhKmHRCbE'*KEAGeFh9cE'9 +MCQehJ)"lHA0H5NG3@QChKik4M(jcG(b"K)fESCqJSTqGSkUYXV+PNBD"HA"`GRC +`E'TMAf4UEAH+PjQDQSjqGACjI)"rHA0aEQPVGi1,NjU@MBL%IS#,NSf&J(9QA9Y +E@f&[IB+%IQeIB@TZFAPqHA*ZE'pkKBQ,Mj11JAPeFA5%PCbHQSq%JSD,NTkVXl5 +cX+DBMib+LSZ&GfeSAeKHDh&aF'jVCQ*IA@&YHAalGQG33d0*9'4dJ)Z5Mi9pIB+ +<1AMS0lG(4qLT'ARjq8LB4rHhPlJSZ4NBChF'p[GiQDS+#GPSf'K)+%LBb)JAa +iF@KTGB')Mj+0LB4pH(f)LiH#IA0Q@9*6@fKdHAYlGQPKBf4MBQ&F@&9489PRGAf +$MC10JhpqJ)QASDZeYDQGPj@@Qk1XXE+cXDbUVDZNS*b2I@pK9P4EC@aaFQaL@PK +B@fCeHR4ZCPC'2ce%@'jlJSL+KRjkHRepIS"rI(YjG(L(NT@BR*U4KhjjI)1)Lj+ +EQialGA9bFhf(MC5DR+'TUk5FPik!FfeYE@aaH(q(Mib'K)&lGhGiGh9`D'*G@PT +FBQeeGhf&JRKaDf4KBQ*NDQjXDfphI(jrJB*rIhppJ)Z5PCbMRT+(JS+(MC5FTV# +hZVUlZ+ZJR*H1KRjeFhKpIAYlGfeK@P9158pCAPpG8N8q2$a'@@PbI)L2NBk*K)" +rKBU+LSU%Ii50N!#5QCfDPT!!LSH,NC+3!*'5LhYXC'&MEAL#N!#ISCZCQC@0LBQ +*KAplHRf#KSH*MSb"G@pSBQ&NCfYYD@"B8e059&jXGi'(KS&rI(9bGRPhFh&`FRG +qK)L1PCL5LS4qIBL8Q*ZLSCL2LSD&KSL0NjfNSCZFSkHPT++BMSGrHRTpI(KkIRY +[B9P88&4GC'PXCPK389"08&PMERGjGhKmJ)'"L)b)Ji1#J)@-N!#9RU@QT*qAN!# +0NCHCQCZDNSZ(IhGiI(YkIi+#JiL-MSf+KAjfF'PMC@jcGS#)Ki&qHRGiHhalJ)D +$H'pVCepHBfGXFR4aFAGhFA"dGRCdG(0aFhGlJBZ3!)U"J)"qJBf@R+1VV+LJPSZ +$JB+"J)1+MC+CQjH9NiTrHR9XC@CYGAYrJAafGA9cFRGhFR0dE@&B88Y)5e&BAf9 +SD'ahIRajJ)5$KBH&KSf6PTZKSTfAP*@@Q*fHRD#PSTZAMAPTC'4NCQeeI)1+M)L +$Ih9VDQeUCQP`G(f,Nj15N!#+Ji'"J(apK)@"IRGR9Nj,6&KVG(CkJ)"mHRTjH(K +pJ)@,Li5$Lik+L)U)KB@+NCUKT+'GQ)jlD'&LBfk#NjbLSTD1Mif%I(b!JS+'M)k +1MSb+MT'&FfTTD'TeHhK`CPK+3$Ne0d*5BQeiK)D$K)L,NCLCPTQIS*kJTDDLS+1 +KPSU$Ii12PT55N!#$E@"GA@"RF(Z$L)L#HRCdEfjjJS1#KBL0PjfGR*ZAM)"mIS' +#KSZ,KRaS88C%3802B'aeI)"rHhGdFhGrKSQ1PCD6Q++PSTf8L(ehGRU$MC@BQ*H +2J(9bFR0kKSk9QjU4MBk0LBH)M)f,LSU0P*H5Mj'2JACfHAPjHhThF@9A5dC$2MT +"6eTKD'jcHB'$KBk@Q*DAR++SVE+cVUHHP)f,L)@*P*UDQ*'$FfCHA'"SF(CpK)Q +)KiZ,KS1%JhpqJ)1-PTZFR*bCMi@"IhYkJ)5%JRPQ8dP"1ce*9f*SDfaXDfTUERU ++NC'8Pj@4NTDCRTkCNSf*K)#$M*@DR*b6KAP[CfPdJ)L6R*bANiq-LBH)M*!!Nj5 +3!)f3!*'1MT16Li9rH(4cFR9kI(9R@&"+36T!69GIC@KXFR9eGhq(LBL*Mj5@QCZ +HSkLMPj!!M)5!KT@NUkZNQj+(H@aQDQpcHB#%JAprJBD-M)H$JB#"KSf9QjqKRC1 +(IRCdHS@0P*D0I'TH9P&49f&YGAKhG(*[D@CUEQaRCQT`Ghq*PCkGNSH!HR9bHSf +JV,1dVkLGN!#(LBq6Nj@BQCH6NC+8PT@3!)Z*KAaiISH2PCD3!)GlEQ9NDR"cGAP +jEQ0G@945@@0ZGhakHAYlH(ChHAPkI(jrJiU3!*LLTjq4KhjiHS13!*ZMTUDPSTb +3!)*mIS'$LC!!NT+5NT+8NiU!HhYlHhk&MC1AQTH1JR9TCQadHAk$JATeF@YNAf" +QF(PlGh0cFh&bG(0[DfTSCfY`GAf'M)Q!Gh&VDR1"MTUNU+LSSjU9PCQGSD@RTk@ +LRjfHS*k9MBGrHRTrKSq8NSPrFf4A899FB@G[GA9`D&pC9eKGC@jcFh0fHhq#Ji* +rHhCbFhZ'M*'@Pj+*IR0ZF(9rM*QMTkDLR*5-LSf3!*5APjDCR*fFQjQ6Li@#JS+ +&M*+@QC5+J(GYC'9YGAk%L)U*JhKYCQ4QCQCTDQPTER*eH(GcF'eTC@9UFRZ%MC! +!LAedDfCTF(Q(QUHXVl'ZUD1LTUZ[XE#ZUkQPSD'LR*11LiL&KBD-P*D3!)GqG@T +G999FC'pjIi&pEep@8e*48eTLCfPYGRarI(GeGhGbFRKrKSf9Qjb9KhKaEQpfJT+ +KU+LQSjb@NBf0P*QCQ*UDPT'0LBH&K)1$JB'$L*'BPBb$GQGJBQPcIiQ0MBQ!F@& +B9ePFB@KUDQKQCQY[EQYVE'YQBQ&REhQ&LiU#G@CE@PpTHSqHTDHRTD+HR+#SX,5 +bVUfVTU+LSk#BMSD%K)5)NCZKSCL+H'"*2N06ChKrIRYbC9P66de18PaSEQjVDQe +hIi5'Ki&iFh9pL*+AQCD0J(*PB@9ZHikITD+FPC!!MSf0P*kNTkLQSjkBPCD9MiG +qGA&dHS@6QCD0JhGVB9YFChL(NT@4KhCM@&C@9ePGB'4SDh"hIApqI(TfEQGQDh@ +$N!#9NiYkD9jEA@4aJBqDRTfCPC1@Qk'VXDqVUDQTUUUTTTq9LB"iFh0jKTDDNB0 +`@dY(6&GQFRTqIRGVA9"+58e9B'GSCQ4QEhPqIAYjG(&bGi'1Q*fHQT!!IfTD9Pa +QGB54QTkFPj58NT'9R++QTUDQTkQXV+DHNi4iGA4dH(k"J(YaD'*JB@4VFhk%JRa +eE@PTDQKQC&pHBfGVFRU$L)H#IRKZDQjjLTUHQ)k!EPj88PGMF(b(Mj+4MBZ1NTD +DRU#IRk#PVVDjYl5ZS)YiF("fIBD0MBGkDPjFA&aLEA9jHA9ZCPa99&GGBQ9NC'C +RDh@!KBD%IhYlI(k'NTfLSCb5JQpLB'C`I)D0Nj@5M)Q)L)U-NTZJSD+NU+bXUD@ +KPS9jG(0fHi'&L)4kEf9IAf&SG(q#IA9YCQ*HA'"SE@eYEh*fH(b%LiZ'IR4XDQa +fL*LHR*@+IR*RB'&PDR0lJB1"IAb#LT'DRjqISD+QV,+eYE+USC5)Ji5)N!#DRjf +8JfpL@eTHC@jeH(0YCep96Ne4@&jIAQ"ND'piJ)@'J(KcEfpaHBHCTkZPQSTlF'T +UFhb!JiH)Ki5$KBZ4PjfJS*kEQ*ZPV+fUST@&H(&bHB'(MBk+J(*L@PYJD(4pIRT +`C9pHAPjKCQPTCQ4REA0pLC!!NijrF@YVF(f2RD5LQ)f$GfaPBQC`H(YlHhKdGRk +)NjQBPTHCQjqQVV5fXUZIMi&mISQDTDHKPS9bC&eFB@CVF(&YC9Y46%T,6e9B8dj +16e9IDRD"L)L"HA9jJBbEUDqZTCH'HR&UEAL$Liq4NBf&Ii'(MT+6NT'2LSH0QU5 +RTjq8LB"mIiL5QCbFQ)emDQ"E@Q"QDfp[D@"FA&eF@9KEA9YC@Q*[ISU4P)q#GR* +dIBZDTkqZTCD%FfGLBfajJS@%J(emIS'%LBZ-M)b-MT1ETV#cVkDCMSH&LjLRX,' +VRiaiCPaDAQ0RDQaTB9G48&"28&056NT*6&CPFhq&L)@!HRKmK*'IUUkYSj'!G@p +ZFhf&M)q0LSL&Ji1'Lj!!N!#-LBH&L*!!R+HXUU1DNBU'Kif@QjbDP)KhD@*JC'T +VE'aTB&K999GA9PPGAepHAfGeJSZ5PC'(IhTmKT+GTUQPQieqFQTRCfjhI(jpHA9 +dGAGpJ(pqIRk!KBbAT,#fYDZGNBU)MjbTVl#URj+%Gh"[FRCjHACX@dT"2d**8&0 +88e"08&KLDh4lJ)+!I(f$MTkXY,LeTj5&HhCfHAk'M)q0KRplHAKjI(ppH(4cHB5 +1PCfLS*U6LS&mIB@3!*ZJRjH-JRTfHB#%K)1!HA"QA9C899CCA&jIA9jRFhk%KS* +kG(0dIBZBSUUYUU#4JRKcFR9kIS&rGh"ZEQp`FhCkIRprJSD-NjZLTU5EMiL&L*! +!QCqNTD#CNib'JS'&M*'4Li"bC&aA9eTF@PG98e09@&jQEA&eGR4aFRU+QkL`XUf +JNSH!IS+&LBk3!)f&I(CfHi#'M)q-Ki"lHRf#LT1DQT5+IhGcG(f+PCUBMi9mGA& +hJBf9Q*50K(T`DQGRD'GQC@4JAQ0YH(pqH(&YDfadJT+ITDDPRj1'IAPjI(pqHR0 +UC@CXFhU!K)H)KS1!J)5,NTLFQ)f#HhZ#M*@HTUDHNiH!IS'(NCZHQBk"G@aPB'& +MC'0J@9*-5NY4@f4UDfKPC@K`Ij+KV,#XTCb4Ki5&L)b-LSL%IAYqKBb6PT11KRY +fGRPrKSb2MSGpFfp`GAq)N!#9PBk)K)1)NCUKT+#BMB&fEfaYEh"YCf&F9P4BB'P +`F@jUCQ0NDRD*QkDVUCq4KRjpJSD)Ki@"I(GbF(0kJ)D+M)Z(JAq$LBq6NSb&IhP +iIB@1PCfKSTk@N!#2NCHITDDKPSCkFh"ZE@YSC&j@6dY)5P"AAf*LAejICA'"NCk +QU+@JQT10Lif6PjD6MSCpGhCjJ)L,M)Z)JRelIB+(LBU+Ki&mGhL!LT1DRCZ8M)D +%KiZ1NC58NBZ%IAG`E'YTCf4JA9eKC@PZF'pYDQPUEh@!MTULTU1FPT!!LiU0MBQ +%IhYfEfPRD@abHAaqIRakI)+*MBk,L)H&K)L3!*QJTUHQSjZ5MSf1NC59PT5-JRT +dF'jUCQ0JA&G88P0BA@4VEfjUD'P[H)'*NTUGRCbDQ*H9PC@6MSL"HRCeGAKpIhe +kGA&aFRGrKif2N!#1LSD$JiU6QTkIR*H5M)H'LBZ0MSq1LAphFh&[E@YSC@&F@ep +QDQe`Fh4aE@aZFRL#MTQKT+'IRTkFQTL8N!#+JhaiFfjVDQeaFQpZEh*hI)''LBQ +'KBD)LSf3!*@ERU#KRjU@PCDCQjL4LS0mGh4cG(0`E'KPB9eD@PjND'YYEQaTCQC +YGAZ#LT'@Q*H@Q*QDR*fGQT@-KB1"IhjpHRCdFR*cGRPqK)Q0M)H%JAemIi@+MBq +2NC15Mib)KB1"JS1#IRakGR0ZD'*HA&aJCQaaFh0dGRGhGA0bFh9jJ)L,MBf1NC5 +9P*13!)f)K(pjGA"YEA"bFh9jIS1(LBb1MBZ*L)H'Ji'%LBf5PjQEQjD5Mib)K)+ +#JRpkGR&VCQ&GA&jJAejLD'aZE@eZF(&aFR0dGhb#LBf1N!#5Nj56NBk0LiL'KiH +(KB&qI(YlIB#%L)b3!*5@P*!!M)Q&JApqIi1(Lik2MBU&J(jqIRq#Ji*rH(&VCQ& +GAQ0SEA&eH(TlIS+'Ki0mH(KiH(TpJSD+M)q4N!#0LSL*LSD!I(PcE@TTD@ebH)+ +-NT56P*HAP*+3!)b'Ji1<!!NjDERjkCNSU%J(q"K)*lG'aQBf&IAf*PCfYZEh& +bFR4iHAGbEQaXF(CrKiZ1MSZ*L)D$Ji@(L)Q)KB*mGh9hHAk#K)Q1N!#4PCLAPC! +!LiH$IherK)U2NC'3!)U"I(YmJ)@'JhpkFfeSC'4QD@peGRChHRk#KSD&JRYeF'e +XEA*kJSL,LSH$IhjqIS#!I(KeF@eUD@Y`HB#&LSk3!*!!MSq6PTD@Niq,L)@%L)k +6Q*Q@MiL!IAapJ)+"IAPcE'PSCfCSDfjbG(9iHhaqIhjmGfjSCQGTFAb&Liq1LiH +#IATjHhk!J(ppH(9dGRU#L)U0NC58PjQCQ*LAPC'0LBD%Kib3!*!!MSQ"HA9cG(Z +!Ji@%IhK[D'*LC'TaGhb"Ji@)LiZ*LBH"HR0ZE'pfISH-Miq)IhPeFA"aFA"[E'K +PC@KZGS'*Mj'5P*DAQjqKSU#CN!#+K)#$Lj5GSD#ENiQ"IRq$L)U)KB"fDf9KB@C +YG(TpI(PiHAf!IRajFfYPBQ0XH)52Q*U8LS"iFh"[FA4hGhGfG(GjHRf%LSq8Q*U +FRCfIS*kAMB&iFh0hJBZ3!*!!MSH"HhGeHB+*MBq0L)"fF'pbGAGiHAYmHRPpJS@ +&KB0qH'pTD@jhJ)D*LSCpGR*`F(&bFh4bE@GPCQP[Gi'+N!#6PCQFRCfEQTQ5L(p +iG(4iJSkCRTbAN!#,Ki@'Lik-LBH%J(ajGR4dFh*cG(0bFA0hH(GfGA"VD'KYGhq +&M*+6MSGrHAGeGRGiGh4`EA"eHRk%LSk3!*'4NC+5NT+5N!#+J(KcFR4kJSZ3!*+ +2LSH$Ihq%Lj5CQTH6M)4qHhTlHR9bFR*aFRGlIAq!IRYfF'e[GS#)M)k0KhpjGA& +aFR"ZE@YRC@4REA9pKBL*LSU,MC'8PCD8MiH!HRCfHi10PCQCPC!!M)Q(LT'9PjH +@NBU#HA0aFR0eGA4cFA"bGACeFQeRC'0PEAH"LT+APT!!KhjiGA4fGR9dFh&`FA4 +hHi#%L)b-LBD&KSU1N!#5MSCrHhPlIS'&Lik1M)U&JB'&Lj+9PC'-KS1"J)'$Ji' +!IhajGR4dGhTlH(4`Efj[GRq'M)q0L)4qHA9dG(*`Eh"aFA&dGRKjHAKjHhamJ)@ +,N!#9PC13!)U$Ihq#Kib4PTL@Nj+5NC+5N!#1M)H!I(TiH(TpJ)"mGh&YE'aYEh* +cFR&aEfj[G(f*NCDCPT!!KhpkHRalHAYmHRPjHAPkHRKiHAGeG(4hHhq&LBU*L)@ +#JS@(LT!!P*D6MSL(KiL,MBk2M)H$JAppIS'"J(afF@eXEA*jJBD(JhjjFh"bGhk +(M)k2Li0kGA*bG(CiH(9`E'TTDfj[EQp`EfpbGAU"Kib2Mik,LBZ4Q*qQV+fUT*k +ANSq1Mj+6MSCpGR*`Eh&cF'YQBQ0QD@acHRjqHR0XCQ9TFi'1PTL@NBU$Ii##K)D +)Ki&kF@YUE'pcGRGfF@TQCQP[GRYrK)H(KiU1NjLHT+LQSCZ@NBq3!*!!N!#2LB* +mGh*[E@aYEfpXD@KTEA4qL)f,KS&kGA0dH)#)MC!!NBk)J(YlIi5'KB"jFQYNAej +IAPjKBf0MBQ9VGAq*N!#5NBf+M*'ARkL`Y,DcVDHLR*H9PC52KRadE'CMBf0LBQ* +LC@PVEA*hHhjpH(0`Eh0kK)f9PjD8NSf,Lib1Mib'J(P`DQKTEA"`EQTNAeeIC'a +fISD+LSL%JS1+NTfPUUURSTkEQTUDQjU6M)4lFfjVE'pbFR&YDfYYFAL"KSL'JAY +hGA0eIBD0NT56MiU'KBH*LSH!H(&VCQ4PCQKUD'4LB'&PEAL'MT'3!)f(JB#%Lj@ +JTUUXUU@KRCbISk@LQj'&HR"XE'paG(4aE@TSD'Y`GhamH(0YD@TZGAq,NjLCPBk +*KiD)M)q0KhedE@TUEh9kI(TfF'aUDR"kJSL,LiL%J(k$M*DITDHQSCQ5N!#4NT5 +8NSk*J(KeGRKmIi'"Hh&UCfPZGAb"K)&jFh&cGhq*NjUFQ*+,K)"rJ)5)Ki"hF'P +PC@T[GAGdEfaUDQacISH-N!#3!)Z&JB''NCfQV+kVTCb9NC'6PCLCPSk&I(GiHhq +#JhphF'PPC'9TER0eG("VDQjeJ)bAR*bAMiL%JB#$KiL'J(KaE'TYGAk%KB&lGA& +ZF(H!L)f3!*!!MBU'KSZ8RD@STU#@Li5%KBL-MBb*K(pmI(q&LBb+JhKXBejIC@j +hIB'"IATjHRq(N!#AQjU9Li*mHharK)@!GfpRB@&QEA9kHhG`D'4PDR*pL*!!P*5 +4N!#4NjDGTDUVTTb6M)L(Lj!!Nj13!)Z'JRprJSD)LB9pFfPJA&jNEA9iHACbEh" +dHi@2PTQANSb'Ji'#K)0rHA"TBf&PE(9mJS0qGQeSCfefIiL3!*@@PT56PCQGSD' +GQ*11L)D'KSL+Ki5"IRalIB'&Ki@!HA&VD'GXFhTqIhejGA0cHS@4QTqJR*@-KS+ +#JB'!I(9YC&jFAQ&PDQjZDf4IAf*UGS11PTZFQTUCQCZISk5KR*L8NT'5PCHBPT! +!LS4qHRKiH(TkGh0`E@YVER*fH(GdF@p[FhU%MT@BQ*D2LBD'KiQ*Ki*lF@KLAf" +ND@pcG(0aEQYZGS#+NTHCQ*D6NC'5PTQDQCH5MSU)LBU0MSb(JAYhG(4eGRCiH(C +cF'eXER0iIB#!IRemIB+*Mj5@PT53!)U'KBD)L)H#I(9VBPaC@PeKC@KUDQPUERH +"LT+9PC'0LSQ,MT+AR*kGQTH8NSq0Mj'3!)Z'J(ThGA9iHhYkH(0[E'TVF(9jI(e +lHAGfGhb#KSL*L)D&K)1$K)D'JhefE@4IAf9XFhPmIAYjH(Z"Kif5P*53!)Z)KiL +*MT5BQjUAP*!!LS@$JS+"IRakHAPjHhk!JApkGR*[F(9kIi@)Ki@"IRerJiH,MSq +2M)L&JS'"J(jjG'aPAeeICQecH(YlHAGeGAPrKSb2Mif,LSU,MBq6PC@9Niq-LSQ +*LBU*KS*qHhYmIAepHhPeFR"[FA9mJB5'KB&pHRKjIS+&KiH(KB1!IRalHAGbE@P +PBf4TF(KqJS5$J(ajHAf$LBf3!*'3!)k-M)f3!*18P*+2LSD%Ji1$JB'!IRakH(K +iH(PkHA4`E'TVF(9lJ)1$JS'!JS5)MC1AQ*H4LS4qHACfGRCcEfYSC@9SEA*fHAP +hG(&`FRGmJSH*LSU+LBZ0Mj'4NC!!Mik-M)b0Miq1M)L$IAPhGhPkH(CdF@pZEQp +bGhb!JS"pHhYqK)Z3!*15MiU%J(aiGA9hGR4`DQ4JB@K`H(k"JB"rIReqJ)5)M*! +!NC!!MSU(KSQ-MSq1MSf,L)L)KS5#J(jkGA&[Efp`FA&`EfeXER0hHherJB5'KiQ +0NTHDQjZAN!#*JhjlHhYkH(9ZCf*HA9jLD@pdGhKjH(KjIB''LBQ)Ki@%KBD(LSZ +-MT!!N!#3!)q1N!#4N!#0LS9rHA9bFA"ZE@jZEQjZER"cGRb"K)@&KSH*Lib-M)Z +*L)H&JRpmHAGeF@TMA9YEAQ9ZGAYrJS5'KB5%KSL+LiZ+L)D%K)5&KSH)Lik2Mif +,L)5"IRTfFQpYE'aXDfYYEQjZF(0iIB#%L)Z,LSU+M)q3!*'6Nj'0L)5"IAPfFQj +TBPY@9PTICQjeHhjrIRepIi'$KB@'KB@%JS"rJ)1)M*!!NT+4NC+4MSb(J(TeFA" +`Efj[FA0dG(0cGAPqK)Q,LSL%JS#"JiH*Lik2M)Q'JAelH(4ZCf"E@&KFBfTbHAq +$KSD'KSH*Lif0M)Q&J(jmHhk#KSQ0Miq1M)L'JhplGh9bEQeZFA0bFh4cFh4fHAk +$KiQ,LiZ*KS5&L)b3!*+5N!#0LBH&JRplGR&VC9pE@&PGCR"iIS#!IhjqIi#!JB1 +%Ji&qI(apIi1(LBQ*LBL*LBQ(JhjkHAPiH(KiHAPjHAPiH(PmJSL,MBk0LSD$JS# +!JB1'L)L)KS1"IRakGR&UBeeD@PjME(CqJiD(KiD%Ji5(LBZ,L)D%K)5&KiL*LBL +)Ki@"IRakHAKhGR9cFR0eGRGiHAYpIi##K)D)LSZ,LBD$Ji5(Lik3!)q1LiQ(K)" +pHhKeF@aQBQ"LCfjfIB+%K)1"IhepIS'&LBb-LBH'KSH(KS@%JS"rIi"rIAYlI(Y +jGR4cFh4hHhq"JS5'LBb1Miq1M)L&JAppIS'&LBZ,LB@"IRakGh4[DQCLB@&MCQY +aHS''KiD&Ji1(LSf3!*'2MSf-LSQ)KiL)L)H%IhYjGhKiGh9cEfeYE@j`G(KpJSD +)L)L)LBZ-M)b+L)D&KBD*Lib0MBf,Ki*pHA9cF@jVD@GRD'a`G(KlIAprIRepIi+ +'Liq5NT!!MBU(KSD&KBD%Ji1#J(ekH(GfGR9cFA&bGRTqJ)+&KiL+M)k1MSk0M)Q +%IhamIB'&LBU*KS0qHRCcEfaUDQTUDfaZF(0iIB'#JS'!JS5(M*!!P*D@P*+1LSH +(L)U+Ki@$JApmHACbF("`F(&aFA*dH(b!Ji@(KiQ-MSq3!*!!MBU(Ji'"JiD+MSq +1LiD"I(GdFh*bFA"ZER"aFh9iHRaqIAYjGhKlJBL1NC+4Mib+LSL(KiH(KiD&Ji" +pHhYkHAKhGhGfGRGkI(f"K)L,MBf1MSk-LS@"IhepJ)5(LBL&JAjmHAGfGA9fGh9 +cFA*bFh4iHRYmI(apIi'$KiZ1Mj!!N!#2MSb-MBk1MSf-LSL'KB5"IRYiG(*aFA& +bFhCkIS#"JB'#Ji5(LBQ)KiD&KBD)LSb0LiQ&JAelHherJ)"rI(TjH(GfGA9fH(K +fG(0dH(f#KSU-M)Z+LSU,M)k3!*!!NC!!Mif,LBQ)KS*rI(PhGhKiH(KjHRYmI(e +qJ)5)M)f,L)@$JS1&KiQ*Ki1"J(jmHhaqJ)'!IATfGA4dGACfGhGhGhPlIB'%KiU +,LiU)KiQ-N!#8PTD9P*+3!)q0M)Q'JhpmHRGdFh4eGRGfGA4bFhCkIi5(L)L(KB5 +%K)D)LSb,LBD%JS'"JB+#JS"pHhPiGhCfGRGhGR9dG(9iIB+&KiH(KB1#JiD+Mj+ +8PC13!)k0MBk1MBZ)K)'!IRakHAPiGR9dG(9hHi'(LSU(K)&rIRq#KBH)L)H&JAj +mIAjrJB"qHhGeGA4eGhGiHATjHATlIS+'LBU)KS1"J)+(M*!!NT15NT'2Miq3!*! +!Mif*K(pkGhChHRamHR9aF(&cGRYrJS1#J(jmI(k"KSU-M)U'Ji"rJ)#!J(pqIAa +kH(KjHheqIAajGR9hHi#%KSD'K)&rIRq#KBL,MBb,LSQ+M)k3!*!!Mib)KB+!Ii# +"JS+!IAPeG(CiI)##JS&rIAamIS#$KBD&Ji"pHhTkHhapIAakH(9cG(CjHheqIRe +lHRYqJB@(L)H&JRprJ)+&LBZ-MBb-LiU+M)k1M)Q&JAjpIS'$K)5"IAKdFA"aFhG +kI(emHRPjHhf!Ji@&K)+"IhjqIRjrIhemHRPjHRarJS1#J(ajH(PlIS'#Ji5#J(j +mI(k"K)H)L)D&K)5'L)Q,LiZ*L)D&KBD(L)Q(JhjjGR4eH(TmIRjqIAepI(eqIi# +!IhekH(GhH(KjHAGeGAChHAYpIi#"J(ppI(aqJ)1'KSD&KB@&KBD(KiH)KiD&KBD +(L)Q+LBH%JS'"JS5'KiD%J(aiGA4dGRKkHhamHhYlI(jrJB'"J(jlHAKhGhGiHAP +jHRTlIB##Ji+"IhjpIAjrJB+%KSD'KSD&K)5&KS@%Ji'"JB'#Ji5&KB@&KB5$Ji1 +&KSD&Ji"qI(apIAepIAeqIRprIi#!J)"rIAThGA9dG(4dFh4eGRGiH(PkHheqIAa +mIAk"K)D)L)L)KiH)L)L)KiD'KSH(KiH(KiD&Ji&qI(YlIAq!JS+"J(ppI(YlI(a +pIAalHhYpIi#"JB#!IhelHRPhGRChH(KhGhGjHhapIAemHhYlI(q"JS1&KSD&Ji' +"JS1%K)1#JS1$K)@&KB5$JS'!IRalIAq"Ji5%K)1#JS+#Ji+#JB#!J(prJ)'#JS' +!IRakH(GfGA9dFh0bFR*cGACiHATkHAKhHAb!Ji5&KSD'KSD(KiH)L)Q*LBL(KiH +)L)L'Ji"pHhTkHhaqIi#!IhjqIRq!JS1$JRppI(amIAk!J)'#JB'!IRalHRTjGhC +dG(9hHRaqIRjmHRKhGhPlIS#$KB@%Ji+#K)@'KiL)KiD&K)1#JS+#JS&rIRemI(a +qIRprIhq!JB1&KB@&KB@%K)1#JS+$Ji1#JApqIAalHAKhGA0bFA&cG(GjHRTjGh9 +cFh9hHhf!Ji@&KS@&KBH)Lif2N!#1M)Z*L)D%Ji'!IhjpIAamIAepIAemI(erJB1 +$Ji1"JB'#Ji1%K)5%K)1"J(pqIAakHAKfGRChHATlI(amHhTiGR9fH(YpIi'"JB# +!JB1&KiQ-MBf-LSH%JS'"JB+!IRemIAk!JB+"J(jpIAeqJ)'%KiQ*L)H'KSD(L)H +'KB1!IRepHhTiGhCfGA9eGA9hH(PjH(CdFh0dGRKkIAq"JS+#Ji@)Lik3!*!!Mif +,LSU+LBH'K)+!IRjqIi##Ji+!IAPhGhKlIi+&KiH&K)+"JB+%KiL)Ki@#J(jpI(Y +kH(KiH(PkHRYmIAepI(TjGhCfGhPlI(apIRq"K)L+LiZ,LBH&K)5&KSD'KB1!IRe +qJ)+%KB@$JAejH(KlJ)@+MBf+Ki5#JS1&KSH)KiD%JRppHhTkHAKhGR9eGRGiHAT +kHRKfG(0cG(GkI(k!JB+$KSL+LiZ+LBQ*LSU-M)b,LBH%JS'"JB+$K)5"IAPhGRG +kIS1'KiD%JS"qIAerJS@(L)H&Ji'!IhjqIAalHRTjHAPlI(eqIAakH(ChH(PlIAj +rJ)'$K)5&KB@&KB@&KBD)LSU*Ki@#J(q!J)+$K)5$JAjlHATpJ)5)LBQ)KS5$JB' +"JS5&KSD&K)+"JB'!IhekH(GfGRGjHRYlHhPhGR4dGAKlIS'#JS+#JS1%KBD'KiH +)LBQ*LSQ*L)H'KB5$JS+$Ji+"IRakHRTmIi'$K)@%Ji&rIReqIi#"JB'"JB+$Ji1 +#J(jmHRKiH(TpIi#!IhelHAPjHAYpIi#"JB#!Ii#"JS1%K)5$Ji+$Ji1$JS+"JB' +#JS+$Ji1$JAppI(erJS@(L)L(KS@%Ji+#JS+"JB'!J)'"JB'!IRYiGR9fH(TpIhp +qIAYjGhCfH(YpIhprIRepIRq"Ji5&KSD&K)5%K)@'KS@&K)1$Ji5%K)1#JApqIAj +qJ)'#Ji1#JB"rIhprIi"rIhprIhprIhjpI(YjH(KjI(q"Ji1#J(jmHhYmIAprIhj +pI(YlI(k!JS1%Ji+"J)#!JS1$JS&rIRjqJ)+$K)@%Ji'!Ihq!JB1%KB@&K)1$JS+ +#Ji1$Ji+"J(pqIRepHhPiH(KjHheqIhpqI(TkHATlIAq!IhelHRPkI(k!JS1%K)5 +$Ji1$K)@&K)5#JB#"JS1%KB@%JS"rIRjqIi'#Ji1$JB#!Ihq!JB'"J)"rIhprIRj +pI(YlHRTlIAjrJ)#!J(prJ)#!J)#!IhemHhYlI(jrJ)'#JS'#JS+$JS+"J(jpIAe +qIi'#Ji5$JS"qIRjrJB1%KB@&K)1$Ji5&KB@%Ji+!J)#!J)"qIAYkHAPjHRYmIAe +qIRepI(apIAjqIRemHhYlHheqIi'#Ji1%K)5&KB5%Ji+"J)#!JB'#JS+"JB"rIhq +!JB+$Ji+"J)#!JB+$Ji1"J(prIhprJ)"rIRemHRTkHhaqJ)'#JS'!IhjpIAeqIRj +qIRepIAepIAjrIi#"JS1$Ji+"J(prIRepIRjrJ)#!IhjqIi#"JS1$Ji+#JB'#K)@ +'KSD%Ji+"J)#!J)#!IhemHhTkHhapIRq!IhemHRTkHhYmIAepIAemI(amIAk!JB+ +$Ji5&KB@&KB5$JS'!IhprIi#!J)"rIhprIhprIhprIhq!JS+$Ji+#JB'!IhjqIRj +qIRepIAepIRq!JB+#JB"qIAamI(amIAepI(alHhYlI(eqJ)'"JS+#JS+"JB'!Ihj +qIRjqIhprIhprIRjqIRjrJ)'#JS1%K)@&KB@%K)1#JB"rIRjpI(apIAjrIi#"JB# +!IhemHhTkHRTkHhYkHRPjHATlIAjrJ)'"JS+$Ji1$K)5%Ji+"JB#!J)#!J(pqIAe +mI(apIRq!JB'#JS+#JS+#JB'"J(prIRjpIAepIRq!JB+#JS+"JB"rIRepI(amI(a +mHhYlHhYmIAjqIi#"JB'#JS+#JS+"JB#!J)#!J)#!IhjpIAamI(eqIhq!JB+$Ji5 +%KB@&K)5$JS'!IhpqIRjrIi#"JB'"JB"rIhjpI(alHhYlHhTkHRTlHhamIAepIRq +!JB+#Ji1$Ji1$Ji+#JS+#JS+"J(pqIAamIAepIRjrIi#!JB+$Ji1$Ji+"J(prIhp +rIhq!J)'"JB#!J)#!J)"rIRjpIAjqIRepIAemI(amI(apIRq!JB'#JS+"JB'!J)# +!J)#!J)#!IhjqIAamI(epIRjqIhq!JB+$K)@&KB5$JS'!J)#!JB'#JS+#JB"rIhp +rIRjqIAemI(amI(amI(YlHhYlI(epIRq!JB'#JS1$Ji1$JS+#JB'#JS+"JB"rIRe +pIAeqIRjqIhprJ)#"JS+#JS+"JB#!J)#!JB+#JS+"J)"rIhprIhprIRjpIAepIRj +qIRemI(amI(epIRq!J)'#JS+#JS+#JB'!J)#!J)#!J(prIRjqIRjqIRprIhprJ)# +"JS1%K)5$Ji+#JS+#JS+$JS+"JB'!J)#!IhpqIAamI(amI(epIAamI(amI(epIRj +rIi#"JS1%K)5$JS'"JB'"JB'"JB'!IhpqIRjqIRjqIRjqIRprJ)'#JS+#JS'"JB' +#JS+"JB#!J)'"JS+"J(pqIAamI(apIRjqIRjqIAepIAepIRprIi#"JS1$Ji+#JB" +rIi#!J)#!J)#!IhjqIRjqIAepIAjqIi#"JS+$Ji1$Ji1$Ji+#JS+#JB'"JB+#JS+ +"J(ppI(YkHRTlHhapIAepIAepIAapIAjqIi##JS1$Ji1#JB'"JB'#JS+#JS'"J(p +qIRjpIAepIRjqIhq!JB'"JB'"JS+#JS+#JS+"JB'"JB+#Ji1#JB"qIAalHhYmI(e +qIRjqIRjqIRjqIhprJ)#"JS+$Ji+#JB#!J)#"JB'#JB'!IhpqIRepIAepIRjqIi# +"JB'#JS+#JS1$Ji1$Ji+#JB'"JS+$Ji1#JB"rIAalHRTlHhYmI(epIAepIRjqIRj +qIhq!JB+$Ji1#JS+"JS+#JS1$JS+"J)"rIRjqIRjqIRjqIhq!J)#!J)'"JB+#JS+ +#JS'"JB'"JB+#Ji+#JB"rIRepI(apIAepIAeqIRjqIhprIhjqIhq!J)'#JS+#JS+ +"JB'"JS+#JS'"J(pqIRjqIRjqIRjrIhq!J)#"JB'"JS+#JS+#JS+"JB'"JB+#JS+ +#JB"rIRjpIAamI(apIAepIAjqIRjqIRjqIRprJ)#"JB+#JS+#JS+$Ji1$JS+"J)" +rIhprIhprIhprIhprIi#!J)#!J)'"JB'"JB#!J)#!J)#"JB'"JB"rIhjqIRjqIRj +qIRjqIRjqIRjqIRjqIhprJ)#!JB'"JB'"JB'#JS+#JB'!J(prIhprIhprIhprIhp +rJ)#!JB'"JB'"JB'"JB#!J)#!J)#"JB'"JB#!IhjqIRjqIRjqIRjqIRjqIRjqIRj +qIRjrIi#!J)'"JB'"JB+#JS+#JS'"J)"rIhprIi#!J)#!J(prIi#!J)'"JB'!J)# +!IhprIhprIhq!J)#!J)#!IhprIRjqIRprIhprIhpqIRjqIRjqIRprIi#!J)#"J)# +!J)'"JB+#JB'!IhprIhprIhprJ)#!J)#!J)#"JB'"JB'"J)#!J)"rIhprIhprJ)# +!J)#!J(pqIRjqIRjrIhprIhjqIRjqIRjqIRprIhprJ)#!J)#!JB'"JS+#JB'!J)" +rIhprIhprJ)#!J)#!J)'"JB#!J)#!J)"rIhprIRjqIRjrIhq!J)#!IhprIhprIhp +rIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)'"J)#!J(prIhprIhprIi#!J)# +"JB'"JB'!J)#!J)#!J(prIhjqIhprIhq!J)#!IhprIhprIhprIhjqIRjqIhprIhp +rIhprIhprIi#!J)#!J)#!J)'"JB#!J)"rIhprIhq!J)#!J)#!JB'"JB#!J)#!J(p +rIhprIhpqIRjqIRprIhprIhprIhprIhprIhprIhprIhq!J)"rIhprIhprJ)#!J)# +!J)#!J)#!J)"rIhprIhprIhprIi#!J)#!JB'"JB'!J)#!J)#!J)"rIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!Ihp +rIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprJ)#!J)#!J)#!J)#!IhprIi#!J)#!J(prIhprIhprIhprJ)#!J)# +!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq +!J)#!J)#!J)#!J(q!J)#!J)#!J(prIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)# +!J)#!J)#!IhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhp +rIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhp +rIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIi# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIi#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!!!!X3!!"!!%!"3!!!+!!!B"4!!! +!!!!8!!!!!!!!,"C@lSZM!!!X&!!!,"8!2)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J(prIhq!J)#!J)# +!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIRjqIRekH(KpJB*rHh"H6%%m2$j!2$% +R,d989dp&26Fd0MY"4de6@&jMCfY[FA4jJ)U@SUqla-[4ephKjHRYlr,cp2AfprI +fp[Eep22cmr,blqV9Rh&pRDHXXE+aVUHJQ*!!L)"e8"3!'c8p4%P*4N!i,bJP*#F +X-cT"4da49PTHB@4QDR"iJBfCT+kf[-,(c0$8f0[Hi1(Mj1AQjqIRk1MRjqIQj0r +8VRjXHBQ8Qk+QUDLPS*Q5LApZ443%%L)X06a!3N!m0M%Y,#da0Ma"4NT18PCCA&j +JBfKYGAq*P*kRVV1i[-$%b-[1dG,8eYMCfY[Fh0hGh0[DeXqlNR&XFRZ%M*1DRk' +JRCL5LAjV3aS,$aBI+#mh1cdm1MFe0$8i1d"%5%Y1894A@9YGAf*RE(9rL*+ESDH +XX,5i[,r#a-I*bXc1cp$4dY26dp(2blqQJfjVEA&iIiD1P*LDQCD4LAjU5#88%K3 +B(L8X-MFk1cXl1c`r3N9)5dj38P9A@9YFA@"MD'phJ)Q4QCqMTkZ[XV@iZll!`X2 +&amM*bXV,bmR'[l'BI@jXE'eaGRf%Lj!!P*@8NBTrE9%b(aXC'4`J*5Xa0MSp2d& +$4NP-6e&69&CA@9YFA9jJBQCVFRU$Lj+BRU+PUDb[XV5hZEZp[X$"`m6%aFA$[lD +RNAa`E@eYEh*hIB1*MT'5N!#,JA&D35dP)L!J)5-S,6-i28&&5%a38P9A@9TDA&e +HAf"JBQ0QDh"hISD0P*UHSD5RUkf[XV5fZ,Ul[Ekr`-(![VQ`SBjpFfp[Eh"aG(P +qK)U1N!#2M)4fC%dk,bSS*bBQ*bX`06Y"4Na39&KEA9jIAf"KB@*LBf4PCfT[G(Z +$LC!!PTUHSD5RUDbZX,+dYVHjZVbp[EbkYDZGMApeFR&aFR*dGRZ!KBU0MSb'HfY +C4cSc-#iX+bXX,M-j2dC-8PGFB'*MC'4NC'9PC@CQCfPVEh4jIiD-NTHERk'NTUL +VVDqaXV5fYlQkZVQfX+HEMB&iG(0cFh4eGRPpJSH,M)Z(IR&L8N8p0c8c-6!`-$- +i284-8PKHBQ9RD'KSD'KSD@PTD@YXEh0iIB1*Mj@CR*qKT+DSUUbZX,+dYEHhYlD +cVD5CMS4mGh9eGRChH(TmJ)@*LiZ)JAGUA&"'2cXj1$Be06Bi280+8PPJC'KVE'a +XE'aXE'aXE'eZF(0hI)+(MC1AQjkJSU5RUDZYVl'bY,@fYV5aUk1CMiCrHRKhH(P +kHRapJ)5)LSZ*K(aaC9P35%0!2Mdm1cSl2N0+89PIC@TYEh"`F("`Efp[Eh"`FR4 +iI)''M*'@QTfJSU5QU+UXVV#bXl@eYE1`UU1DNBQ#IAYkHRYmIAjrJB5(LSZ+KS" +hE@*B8%T'3d*"3$mr384+8&GIC@Y[FA0cFh0cFR*bFR*bG(9iI)#<!!P*LFRk' +MTDHUV+k`XV1dY,5bVUQLQj+,KB"pI(apIi#"JS1&KiQ,LiL$I(0TB&G46%K'489 +%4%9(5P"@A@4UER*dGA9eGA9dG(4dG(9fHAarK)Q1NTHERU#LTDHTUkf[XE+cXl1 +aVDLLQj10Ki*rIRjrJ)'#Ji5&KiQ+LSQ&IhGZCPe@88e+58K)5%K+6&"9A'*SER* +dGRGhGhCfGR9eGAChHAYqJSH-N!#9QCfISU5QU+UXVV#aXV+aVkbRSTZ8MBL%JAp +rJ)'$K)@'KiL*LSU*Ki*lG'YMA9G56Na,5dY-68p59PYKCfaaG(ChH(KiH(GhGhG +hH(PlIS'&LSq6PjZISD1QU+UXVUqaXV'aVkZQSTZ9MiU'Ji'!JB+%KSH)L)Q+LiZ ++L)4rH("TBPaA8e"26Nj28&*89eYKCQY`G(CiHAPjHAPjHAPjHATmIS'&LBf5PTU +HSD1PU+UXVUqaXE'`VUZRSTb@N!#-L)5$JS+%KBH*LSU,Lib-LiU(JRaeEQGK@eG +88P&48P099ePGB@CUEh0fH(PkHRTkHRTkHRTlI(k"K)L-N!#9QCfJSkDSUUbZVl# +aXE#ZUkDKR*H4MBQ'K)1$K)D)LSZ-M)b0MBb,L)4rHA*XCQ"E9e968e499ePEAQ& +PD@eaGAGjHRTlHhYkHRTlHhaqJ)1'LSq6PjZISU@RUDZYVUq`X+qXUDDKR*H5MBQ +(K)1$K)D)LSZ-MBf0MBb,LBD"I(C[D@4I@eG9999@@&TGAf*PD'a`FhChHATkHRY +kHRYlHhapIi+&L)b4PCQGS+1QU+UVVDkZVUfVU+5JQjD5MBQ(KB5$K)D)LSZ0MBk +1MBf-LSH$IRPcE@GLAPTB9eGA@9YHB@0QD@a[FR4fH(PkHRTkHRTlHhapIS'%KiU +1NjHERk+PTkQVV+bYV+ZTTU1IQTD5MSU(KB5%K)D)LSb1Miq2MSf-LSH$IhTeF'T +QBPjE@9PC@eeIBQ9RDQaZF(*dGRGhH(PjHAPkHRYmIB#$KBQ0NC@CRD'NTULTUUZ +VUUQRT+'GQC@4MSU)KS@&KBD)Lif2N!#4N!#3!)k0LiL&JAaiFfjUCQ*IA9YEA&j +KC'GTDfe[FA*cG(9fGhKiH(PjHRYmIAq#KBL-N!#8Q*bJSkDSUDQUUDLRTD+IR*L +9NBk,LBH'KBD(L)Z0Mj'5NT'3!)k-LBD$IhTfF@eTC@*JAPeHAf&NCfTXER"aFR0 +dG(9fGhGiH(PjHRYpIi'%KiZ2NjHERk+NTUHSU+LRTD1KRTUAP*'0LiQ(KS@&KSL ++MBq3!*+5NC!!Mib+Ki5!I(KdEfYSC@*JAepJB@4RDQe[F(&bFh0cG(9eGRGhH(P +jHRaqJ)1'LBf4PCQFS++NTDDQTU@MSCqFQCD6N!#0LSL'KB@&KBH*Lif2NC+4NBq +0LiL%JAjkGR*ZDQCNBQ"JB'*NCQTXER"bFR0cFh0dG(9fGRGiHATlIAq"K)H,Mj+ +@QTkJSU5PTD5MSU#HQjL9NBq-LBH'KB5%KBD)LSf2N!#4NC'3!)k-LBD$IhaiG(" +XD@CNBQ&KBQ4QD@aZF(*cFh0cFh4dG(9fGhGiHRYmIS'%KSQ0NC@BR*kKSk1MSk+ +KRjfDPj54MSZ*Ki@%K)5%KBH*M)k3!*'5NC!!Mif+L)5"IRTfFfpVD@CNBf0MC'C +TE'j`FR0dG(4dG(4dGAChGhKjHhaqJ)+&LBb2NjDCRCqKSU+LSD#HR*QAP*'1LiQ +(KB5$Ji5&KSL,MBq3!*'4NBq1LiQ'Ji"mH(9aE@YSCQ9NC'4QD'YYF(*cG(4dG(4 +dG(9eGRGiHATmIAq#K)H+MT'8PjUGRk#KSD#IRCZBPT13!)f+L)D%Ji+#JS1&KiQ +,MSq3!*'3!*!!MSb+Ki5"IATfFfpXDQKQC@4PCQGTE'paFR0dG(4dG(4dGA9fGhK +jHhaqJ)+&L)Z2NT@BQTbHRjqHRCZCPj54Mib*Ki@$JS'"JB+$KBH*M)k2N!#3!)q +1M)U(K)*qHhKdF@eVD@GQC@9PCfPVE@paFh0dG(0cFh0dG(9fGhKkHherJB1'LBb +2NT@BQTbGRCfFQTL@Nj!!MSZ)KS5#JB#!J)##Ji@)LSb1Miq2MSb+L)D$J(ejGR* +[E'TSCfCPC@CSDQaZF(*cG(4dFh0cG(4eGRGiHAYmIS##KBL+MC!!NjDBQTZEQjU +CPj@5Mif+Ki@$JB"rIhq!JB+%KiQ,MBk2MSk0LiQ(K)&qHhKeF@jXDQKRCQCRD'T +XER"bFh4dG(4dG(4dGAChH(PkI(k!JS5(LSb2NT5@Q*UDQTUBPj@5N!#0LSL&Ji' +!IhjqIi#"Ji@)LSb0MSk1MBb+L)@$J(ejGR0`E@YTD'GRCfKTDfe[FA0dG(4dG(4 +dG(9fGhKjHRYpIi'$KBL,MT!!Nj@@Q*QCQCL@P*+2M)Q(K)+!IhjpIAjrJ)+%KSQ +,M)f1MSf-LSL'K)&qHhKeFQpYDfPSD'GSD@TXER"bFh4dG(4dG(4dGAChH(PlI(k +!JS5'LBb1NC18PTHAPjD9Nj'1LiL'Ji&rIRemI(apIS##KBH*Lib0MBb,LSQ'K)* +rHhPfFR"YDfTTD'KSD'TVE@paFR0dG(4dFh4dG(9fGhKkHherJB1&KiU-MT!!NT5 +9PC@9P*+2MBZ)KB1"IhemHhYlI(erJB1&KiQ,M)b-LiU*Ki@$J(ekGh4aEfeVD@K +SD'KTDfaZF(&cFh4dG(4dG(4eGRGiHAYmIS##K)D)Lif2NC+6P*58Nj'2MBU(KB+ +!IRemHhYlHhaqJ)+%KSL+Lib-LiU*L)D%JApmHACcF'jXDfTTD@PUDfaZEh&bFh4 +dG(4dG(9eGRGiHAYmIS##K)D)LSb1N!#4Nj16Nj+4Mif+L)@$J(jpI(YkHRYmIAq +"JiD)LBZ-M)b,LSQ(KB+!IRYiGA*`EQaVDQTUDQYXE@paFR0dG(9eGA9eGRChH(P +lI(jrJB1&KiQ,MBq3!*'5NT+5N!#2MBU(KB+!IRelHRTkHRYmIS##KBH*LSZ-LiZ ++LBL'K)&rI(PhG(&[E@aVDQTUDfaYER"aFh4dGA9eGA9fGRGiHATmIAq!JS5'L)U +-MBq3!*'4NC!!Mik-LSH%JS"qI(YkHAPjHRYpIi'$KBH*LSU,LiU*L)D%JS"pHRG +eFh"ZE@YUDQTVDfaZEh"bFh4dGA9eGA9fGRGiHRYmIRq"Ji@(LBU-MBk2N!#3!)q +1MBZ*KS5#IhelHRPiH(KjHRapIi+%KSH*LSU+LSQ)KS@$J(jmHACdF@pZE'YVDfY +VE'e[F(&bFh4dGA9eGACfGhKjHhapIi'#K)D)LBZ-MBk2Mik0M)Z*KS5#IhelHRP +iH(KjHAYpIS'$KBH)LBU+LSQ)Ki@%JRppHhKeFh&[E@aXDfYXE'e[F(&bFh4eGA9 +fGRChH(KkHhaqIi'#K)D(LBU-MBf1MSk0M)Z*Ki5#J(jmHRPiH(KjHAYmIS##K)D +)LBU+LSU*L)H&Ji&rI(TiGA0aEfjYE@eYE@j[F(&bFh4eGRCfGhGiH(PkHhaqIi' +#K)@(L)U,M)f0MBf0M)U*Ki5#J(jmHhPiH(KiHATmIAq"Ji@(L)Q+LSU*L)H'K)+ +!IRYjGh4bF@pZEQeYE@j[F(&bFh4eGRChGhGiH(PkHhaqIi##Ji@'L)Q+Lib-MBb +-LiU)KS5#J(jmHRPiH(KiHATlI(k!JS5'KiL*LBQ*L)H'KB1"IhekH(CdFR"[EQj +YEQj[Eh"aFh4eGAChGhGiH(PkHhapIS#"Ji5&KiL*LSZ,LiZ,LSQ(KS5#J(jmHRP +iGhGhH(PkHherJB1&KSH)LBQ*L)H'KB1#J(elHAGeFh&`EfjZEQj[Eh"aFR0dGAC +fGhGiH(PkHhapIRq"JS1&KSH)LBU+LiZ+LBL(KB5#J(jmHRPiGhGhH(KkHheqJ)+ +%KBH)L)Q*L)L(KS5$JAppHhKfG(0aF'p[Efp[F("aFR0dGAChGhKiHAPkHhapIRq +"JS5&KSH)LBU+LSU+LBL(KB5#J(jmHhPiH(GhH(PkHhaqJ)+%KBH)L)Q*LBL)Ki@ +%JS"qI(TiGR9cFR&`F("`F(&bFh4eGRChH(PjHRTlI(apIi#"JS5&KSH)LBU+LSU ++LBL(KS5#JAppHhTjH(KiH(PkHhaqJ)'$KBD(L)Q*LBQ)KiD&Ji+!IRakH(CdFh* +aFA&aFA&bFh4eGRChH(PjHRYlI(eqIi#"JS5&KSH)LBQ+LSU*LBL(KB5#JAppI(T +jH(KiH(KjHRapIi'#K)@(L)L*LBQ)KiH'K)+"IhelHAGfG(0bFA&aFA&bFh0dGAC +hH(PjHRTlI(apIRq"JS1%KBD(L)L*LBQ)L)H'KB1#J(jpHhTjH(GhGhKjHRYmIS# +"Ji5'KiH)L)L)KiH'K)1"J(jmHRKfGA4cFR&aFA*bFh0dGAChH(KjHRTlI(apIRq +!JB1%KBD(KiL)L)L)KiD'K)1#J(jpHhTjH(KhGhKiHATmIAq"JS5&KSH)L)L)L)H +'KB5#JAppHhPiGR9dFh*bFR*bFh0dGAChH(KjHRYlI(eqIRq!JB1%KBD'KiL)L)L +)KiD&K)1#J(ppI(YkHAKiH(KjHATmIAq!JS1&KSH(L)L)L)L(KS@%JS"rIAYkH(G +eG(4cFh0cG(4eGRChH(PkHhamIAjqIi#"JS1%KBD(KiL)L)L)KiH'KB1#JApqI(Y +kHAPiH(KjHRYmIAq!JS1&KSH(L)L)L)L)KiD&Ji+!IRelHRKhGR9dG(4dG(9eGRG +iH(PkHhapIAjrIi#"JS1%KBD(KiL)L)L)KiH'KB1#JApqIAYlHRPiH(PjHRTmIAk +!JB1%KBD(L)L)L)L)KiD&K)1"IhjmHhPiGhCeGA4dGA9eGRGhH(PkHhamIAjrIi# +"JS1%KB@'KiH)L)L(KiD&K)1#J(pqI(YkHAPiH(KjHATlI(erJ)+$K)@'KiH)L)L +(KiD&K)1#J(jpHhTiGhCfGA9eGA9eGRChH(PjHRYmIAjqIi#"JB+$K)@'KSH(KiH +(KSD&K)1"J(ppI(YkHAPiH(KiHATlI(eqJ)'#K)@&KSH(KiH(KiD'KB5#JApqI(Y +kH(GhGR9eGA9fGRGhH(PkHRYmIAjrIi#"JS+$K)@'KSH(KiH(KSD&K)1"J(pqIAY +lHRPjH(KjHATlI(eqIi'#Ji5&KSH(KiH(KiH'KB5$JS&rIRalHRPiGhGfGRCfGhG +iH(PkHhapIRjrJ)'"JS1%K)@'KSH(KiH(KiD&K)1#JB"qIAalHRTjHAPjHRTlI(e +qIi'#Ji5&KSH(L)L)L)H(KS@%Ji+"IhjmHhTjH(KhGhGhGhKiHATkHhapIRprJ)' +#JS1%KB@'KiH(KiH(KiD&K)1#JB"rIRamHhTkHAPjHRTlI(eqIi##Ji5&KSD(KiL +)L)L(KiD&K)1"J(ppI(YkHAKiH(KiH(KiHATkHhapIRprJ)'#JS1%K)@'KSH(KiH +(KSD&K)1#JB"rIAalHhTkHAPjHRTlHhapIS#"JS1%KB@'KiH(KiH(KSD&K)1#J(p +qIAYkHRPiH(KiH(KiHAPkHhYmIAjrJ)'"JS1$K)@&KSD'KiD'KS@&K)1#J(pqIAa +lHhTjHAPjHATkHhapIRq!JB+$K)@&KSD(KiH(KSD&K)1#JB"rIAalHRPjH(KiH(K +iHAPkHhYmIAjrJ)'"JS1$K)@&KSD'KSD'KS@&K)1#JB"qIAamHhTkHAPjHATkHha +pIRq!JB+$K)5&KSD(KiH(KiD'KB5$JS'!IRemHhTkHAPjHAPjHATkHhamIAjrJ)' +#JS1%K)@'KSD(KiH(KSD&K)1#JB"rIRemHhYkHRTkHRTlHhapIRq!JB+$K)5&KSD +(KiH(KiH'KS@%Ji+"J(pqI(alHRTkHAPkHRTlHhapIRprJ)'#Ji1%KB@'KSH(KiH +(KSD&K)5$JS"rIRjpI(YlHhTkHhYlI(apIRq!JB+$Ji5&KSD'KiH(KiH'KS@&K)1 +#J(pqIAamHhYkHRTkHRYlHhapIRjrJ)'#Ji1%KB@'KSD(KiH(KSD&K)1$JS"rIhj +pI(YlHhTkHRYlHhapIRjrJ)'#Ji5%KB@'KSD(KiD'KS@%K)1#JB"rIRemHhYkHRT +kHRTlHhamIAjrJ)#"JS1$K)@&KSD'KSD'KS@&K)1#JB"rIRemI(YlHRTkHRTlHha +mIAjrIi#"JS1$K)@&KBD'KSD'KS@&K)1#JB"rIRemI(YlHRTkHRTlHhYmIAjqIi# +"JS+$K)5&KBD'KSD'KS@&K)1#JB"rIRjpI(YlHhTkHRTlHhamIAjqIi#"JS+$K)5 +&KBD'KSD'KS@&K)1$JS'!IhjpI(alHhYlHhYlHhamIAjrIi#"JS1$K)5&KBD'KSD +'KS@&K)1$JS'!IhjpIAamHhYlHhYlI(apIAjrIi#"JS+$K)5&KBD'KSD'KSD&KB5 +$Ji+"J(pqIAemI(alHhamI(apIAjrJ)#"JS1%K)@&KSD'KSD'KSD&KB5$JS'!Ihp +qIAamI(YlHhYmI(apIAjrIi#"JS+$K)5&KBD'KSD'KSD'KB@%Ji+"JB"rIRjpI(a +mI(amI(epIRjrJ)#"JS1$K)@&KSD'KSD'KSD&KB5$JS'!J(pqIAemI(alHhamI(a +pIAjrIi#"JB+#Ji5%KB@&KSD'KSD'KB@%Ji1#JB"rIRjpIAamI(amI(apIAjqIi# +"JB+$Ji5&KB@'KSD'KB@&K)1$JS'!IhjqIAamI(YlHhYlI(amIAeqIhq!J)'#JS1 +$K)5&KB@&KB@&KB5%Ji+#JB"rIhjpIAamI(amI(amIAeqIhq!JB+#Ji5%KB@&KB@ +&KB@%K)1$JS'!IhjqIAamI(YlHhYlI(amIAeqIRq!J)'"JS+$Ji5%KB@&KB@&KB@ +%Ji1#JB'!IhjqIAemI(amI(apIAjqIhq!JB'#Ji1%K)@&KB@&KB@%K)1$JS'!J(p +qIAemI(amI(amI(apIAjqIhq!J)'"JS1$K)5%KB@&KBD&KB@&K)5$JS'"J(prIRj +pIAepIAepIRjrIi#!JB+#Ji5%KB@&KSD'KB@&K)5$JS+"J(prIRepIAamI(amI(e +pIAjqIhq!J)'"JS+$Ji5%KB@&KBD'KB@&K)5$Ji+"JB"rIhjqIRepIAeqIRjrIi# +!JB+#Ji5%KB@&KSD'KB@&K)5$Ji+"J)"rIRjpIAemI(amIAepIAjqIhq!J)'"JS+ +$Ji1%K)@&KB@&KB@&KB5%Ji+#JB#!IhjqIRepIAepIRjqIhq!JB'#Ji1%K)5&KB@ +&KB@%K)1$JS'"J(prIRepI(amI(amI(apIAeqIRprJ)#!JB'#JS1$Ji5%K)5&KB@ +%K)5$Ji+"JB#!IhjqIRepIAepIAjqIRprJ)'"JS+$K)5%K)@&K)5%K)1$JS'"J(p +qIRepI(amI(amI(amIAepIRjrIi#!JB'"JS+$Ji1%K)5%K)5%K)5$Ji+#JB'!Ihp +qIRjpIAepIAjqIRprJ)#"JS+$Ji5%K)5&K)5%K)1$JS'"J(prIRjpIAamI(amI(a +pIAeqIRjrIi#!J)'"JS+$Ji1%K)5%KB@%K)5%Ji1#JS'"J)"rIhjqIRjqIRjqIhq +!J)'"JS1$K)5%KB@&KB@%K)5$Ji+"JB"rIhjqIAepIAepIAepIAjqIRprIi#!JB' +#JS+$Ji1%K)5&KB@&KB5%K)1$JS+"JB#!IhprIRjqIRprIhq!J)'"JS1$K)5%KB@ +&KB@%K)5$Ji+"JB#!IhjqIRepIAepIAepIAjqIRprIi#!JB'"JS+#Ji1$K)5%K)@ +&KB5%K)1$Ji+"JB#!J(prIhpqIRjrIhq!J)'"JS+$Ji5%K)5%K)5%K)1$JS+"JB" +rIhjqIAepIAemIAepIAepIRjqIhprJ)#"JB'#JS+$Ji1%K)5%K)5%Ji1$JS+"JB# +!IhprIRjqIRjqIRprJ)#!JB'#JS1$K)5%K)5%Ji1$JS'"J)"rIRjqIAepI(amI(a +pIAepIAjqIRprJ)#!J)'"JS+#Ji1$Ji1%K)5$Ji1$JS+"JB#!IhprIhjqIRjqIhp +rJ)#!JB'#JS1$Ji5%K)5$Ji1#JS'"J)"rIhjqIAepIAamI(epIAepIRjqIRprIi# +!J)'"JB+#JS1$Ji1%K)5%Ji1$JS+#JB'!J)"rIhprIhprIhprJ)#"JB+#JS1$K)5 +%K)5%K)1$JS+"JB#!IhpqIRjpIAepIAepIAeqIRjrIhprJ)#!JB'"JS+#Ji1$Ji5 +%K)5%K)5$Ji1#JS'"JB#!J(prIhprIhq!J)#"JB+#Ji1$K)5%K)5%K)1$Ji+#JB' +!J(prIRjqIAepIAepIRjqIRjrIhprJ)#!JB'"JB+#JS1$Ji1%K)5%K)5$Ji1#JS+ +"JB#!J)"rIhprIhq!J)#"JB'#JS1$Ji5%K)5%K)1$Ji+#JB'!J(prIRjqIAepIAe +pIAjqIRjqIRprIi#!J)#"JB'"JS+#JS1$Ji1$Ji1$Ji+#JS'"JB#!J(prIhprIhp +rIi#!J)'"JS+#Ji1$Ji1$Ji1#JS+"JB#!IhpqIRjpIAepIAepIAepIAjqIRjrIhp +rJ)#!J)'"JB'#JS+#Ji1$Ji1$JS+#JS'"J)#!IhprIhprIhprIhq!J)#"JB+#JS1 +$Ji1$Ji1#JS+"JB#!IhpqIRjpIAepIAepIAepIAjqIRjrIhprIi#!J)#"JB'"JS+ +#JS1$Ji1$Ji+#JS'"JB#!J)"rIhprIhprIi#!J)'"JB+#JS1$Ji1$Ji1$JS+"JB' +!J(prIhjqIRjpIAepIRjqIRjqIRprIhq!J)#!J)'"JB'#JS+#Ji1$Ji1$Ji1$JS+ +#JB'"J)#!J)"rJ)#!J)#!JB'"JS+#Ji1$Ji1$Ji1$Ji+#JB'!J)"rIhpqIRjqIRj +qIRjqIRjqIhprIhq!J)#!J)'"JB'#JS+#Ji1$Ji1$Ji1$JS+#JS'"JB#!J)#!J)# +!J)#!J)'"JS+#JS1$Ji1$Ji1$JS+#JB'!J)"rIhpqIRjqIRjqIRjqIRjqIRprIhp +rJ)#!J)#!JB'"JS+#JS+#Ji1$Ji+#JS+#JB'"J)#!J(prIhprIi#!J)#"JB'"JS+ +#JS1#JS+#JS+"JB#!J(prIhjqIRjpIAepIAepIRjqIRjqIRprIhprJ)#!J)#"JB' +"JB+#JS+#JS+#JS+"JB'!J)#!IhprIhprIhprJ)#!JB'"JB+#JS+#JS+#JS'"JB# +!J(prIRjqIRjpIAepIAeqIRjqIRjqIRprIhprIi#!J)#!JB'"JB+#JS+#JS+#JS+ +"JB'"J)#!J(prIhprIhprJ)#!J)'"JB+#JS+#JS+#JS+"JB'!J(prIhpqIRjqIRj +qIRjqIRjqIRprIhprIhq!J)#!J)'"JB'"JS+#JS+#JS+#JS+#JS'"JB'!J)#!J)# +!J)#!J)#"JB'"JS+#JS+#Ji+#JS+#JB'"J)#!IhprIhjqIRjqIRjqIRjrIhprIhp +rJ)#!J)#!J)'"JB'"JS+#JS+#JS+#JS+#JS+"JB'"JB#!J)#!J)#!J)#"JB'"JS+ +#JS+#JS+#JS+#JB'"J)#!IhprIhjqIRjqIRjqIRjqIhprIhprIi#!J)#!J)#"JB' +"JB+#JS+#JS+#JS+#JS'"JB'"J)#!J)#!J)#!J)#!J)'"JB'"JS+#JS+#JS'"JB' +!J(prIhpqIRjqIRjqIRjqIRjqIRjqIhprIhprIhprJ)#!J)#!JB'"JB'"JS+#JS+ +"JB'"JB#!J)#!IhprIhprIi#!J)#!JB'"JB'"JB'"JB'"JB#!J(prIhjqIRjqIRj +qIRjqIRjqIRjqIRjrIhprIhprIi#!J)#!JB'"JB'"JB'#JS'"JB'"JB#!J)#!J(p +rIhprIi#!J)#!JB'"JB'"JB'"JB'"JB#!J)"rIhpqIRjqIRjqIRjqIRjqIRjqIRp +rIhprIhprJ)#!J)#!JB'"JB'#JS+#JS+#JS+"JB'"JB#!J)#!J)#!J)#!J)'"JB' +"JB+#JS+#JS'"JB'!J)#!IhprIhpqIRjqIRjqIRjrIhprIhprIhq!J)#!J)#!J)# +"JB'"JB+#JS+#JS+#JS+#JS'"JB'"J)#!J)#!J)#!JB'"JB'"JS+#JS+#JS+"JB' +"J)#!J(prIhprIhjqIRjqIRprIhprIhprIhprJ)#!J)#!J)#!JB'"JB'#JS+#JS+ +#JS+#JB'"JB'"J)#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'!J)#!IhprIhjqIRj +qIRjqIRjqIRjqIRjrIhprIhprIhq!J)#!J)#!JB'"JB'"JB'"JB'"JB'"JB#!J)# +!J)#!J)#!J)#!J)#!JB'"JB'"JB'!J)#!J(prIhprIRjqIRjqIRjqIRjqIRjqIRj +qIRprIhprIhprIi#!J)#!J)#"JB'"JB'"JB'"JB'"J)#!J)#!J)#!J)#!J)#!J)# +!JB'"JB'"JB'"J)#!J(prIhprIRjqIRjqIRjqIRjqIRjqIRjqIhprIhprIhprIi# +!J)#!J)'"JB'"JB'"JB'"JB'"JB'"J)#!J)#!J)#!J)#!JB'"JB'"JB'"JB'"JB' +!J)#!J(prIhprIhjrIRjqIRjrIhprIhprIhprIhprJ)#!J)#!J)#!JB'"JB'"JB+ +#JS+#JS'"JB'"JB'"J)#!J)#!J)'"JB'"JB'"JB'"JB'"JB'"J)#!J)"rIhprIhp +rIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!JB'"JB'"JB'#JS+"JS'"JB'"JB' +"J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'!J)#!J(prIhprIhpqIRjqIRjqIRjrIhp +rIhprIhprIhprIhprJ)#!J)#!J)'"JB'"JB'"JB'"JB'"J)#!J)#!J)#!J)#!J)# +!J)#"JB'"JB#!J)#!J)"rIhprIhjqIRjqIRjqIRjqIRjqIRjqIRjqIhprIhprIhp +rIhprJ)#!J)#!J)'"JB'"JB'"JB#!J)#!J)#!J)#!J)#!J)#!J)#!J)#"J)#!J)# +!J)"rIhprIhpqIRjqIRjqIRjqIRjqIRjqIRjrIhprIhprIhprIhprJ)#!J)#!J)' +"JB'"JB'"JB'"JB#!J)#!J)#!J)#!J)#!J)'"JB'"JB'"JB#!J)#!J)"rIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB' +"JB'"J)#!J)#"JB'"JB'"JB'"JB'"JB'"J)#!J)#!IhprIhprIhprIhprIhprIhp +rIhprIhprIhprJ)#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'"JB'"J)#!J)#"JB' +"JB'"JB'"JB'"JB'!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIi#!J)#!J)#!J)#!JB'"JB'"JB'"J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!IhprIhprIhprIRjqIRjqIRjqIRjqIRjqIhprIhprIhprIhprIhprJ)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhprIhp +rIRjqIRjqIRjqIRjqIRprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)'"JB'"JB'"JB'"JB'!J)#!J)#!J)# +!JB'"JB'"JB'"JB'!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIi# +!J)#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'"JB'!J)#!J)#"JB'"JB'"JB'"JB# +!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)# +!J)#!JB'"JB'"JB'!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIRprIhp +rIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhq!J)#!J)#!J)#!J)#"J)'"JB'"J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)# +!J)#!JB'"JB'"JB'"JB'"JB'"JB#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ihp +rIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!JB'"JB' +"JB#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprJ)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!Ihp +rIhprIhprIhprIhprIhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)'"JB' +"JB'"JB'"JB'"J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhp +rIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!JB'"JB'"JB'"JB#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhq!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhp +rIhprIhprIhprIhq!J)#!J)#!J)#!J)!!!#K!!!%!!3!&!!!!S!!"J&%!!!!!!"3 +!!!!!!!!S&PEZLk-!!#J8!!!S&3!mJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprJ)#!J)#!J)'"JS1%KBD(LBU,MC@ +KUDLSX,Hl[lfcTk@``YANl2(bm1VJdmHlVk5FPSq)L*h$hqAHi[6rr2VikG5rV*q +BNSb'JAYjHAPkJT[%j[AaiFQhYF(2dmr'YkDR`Y[A`kqCIQPD8P&DFTLrf1$DcVq +aVE5jYl+bYVLeVUDCLA4H6%0-Dj5cZUZ5Gej,5eTUFRPrIhKXC'"HA9pLB9eA9Pp +fP+bbSSZ#MUM#f1AKeY$*[,'XUkZTTk+CMSUDZYILhXqlTC58Skq[Tjb1IAZ5VlH +`Tj4hA%Bi2PCeM*QGR*qLRTD3!)PqFQTTEh0aDf&@890DD)Lkir(Yh-#RRk+VZ-# +rZlZm[,fqZl1QQ)TpF@0@8&GME'aJ5M3a5'9bG(4R8cmV'!i1&L%T,5dX-%9QJik +(G@&45%eHER0aE'094dTCB&4'1#`T,cjFIT1AMRYN9PjfMTbMS*H2LSD#IRGXA8X +h*KiV8AkETk13!(aS9%T2AQppLBPpE@"E@PYHB@0JA&pZJBkARTb3!)*qN!#SX+Z +NQ)PqGQeN@P"-5dT(3$j+BRL#JAGZD'0LCQPN@8j#05db4eaMBee01c!`3ephKib +*K)QCUlHl[EU[RSf"IB#)P*qJPB4hITbjaF@mVCq8LS*qHA&UCfKYG(k(LS9mF'* +@9fZ(QCZ1Gee+3P*pTV@aSB4M56Xk38K19&KC994FEAk&J(4UD@j`FR0aE'CLBQ" +HAfTfIS1(KAppK*Qa`,qbTD+TXEDeXUqVST5'I(CdFR&ZCeT15&9mUEqmUj0pF'j +hKBk2KhTVA9GHDh0`CPK*2Mp3F)Q3!)PmE9j48QU)PT@4LS"jFQTM@e415dY19'0 +hJB*rHA&VC&jB88Jq0#XP)LFqBharG@&(,L-Y5QKjI(CYC&pMFS#&JRefEQ9IA9Y +A8Nj'1c%d8AkLYEDTQjD6N!#1Li*fD@0NCfPYG(K`AdSi-d9PJ)b-KAaeHj1NRBT +jCeP68PGGB&pE9%Y)9'TpKS0eCf&QEhGmIAKYB&YSM,6-dF@TMAf#R,R'`V1LPj+ +6Qk'IQC!!LSH%JAjfD9Y668-m4Q@%N!#,IQeLB@PaFh*bFR4kJB5!HhTmIRemKTb +YVk',Ff9RGjLh`,DPN!"pE&T,4dY16Na)4PClSEHhU)jeBPYJCfedHRGVB'0hM*1 +4JQPA9fKpLiZ!FQYXG(q&Ki@#K)fETDQNPi9hF("qSFRKj0M$V*fCRUDQS*Q9NT! +!MSZ'IhalGQP@4NC9CQYSA%Si-N9RH(CZAdj"0LXU0%416dFr4Q"mLBKrFfTQCfK +QAeC048"!6QYqIR9S9%0$@AZ4PSq#G'peJ)H0NBq(IA*SAPKA@ea84cSf5h+4R*H +(GfpXE'j`F("aEfPJ@P438PTLD(+(RU5CK@jC6NjLK*ZHQBarHheqJBD)KB1"JSb +FU+QMPSGlEf0E@&G988a)3d44CA&eF'CHBA#(QUDSTD+JRk@VUkDJQC'-LSH"H(& +ZE@adN!#mhZRPeEbQQTQPXVLdV+HQTU@MRj@&FPp148PHGB&pEPT*3NeTJB@"G@0 +98&&AA@4VFhCdGAq-N!#0Jh*K@9aPERChG'jVEAQ4TkUGLR*H9PebMk@[X+fTTU@ +NSTkEQ*1*IhC`ER&jIheeF)+R`XR%Xjb0KiH0PTZDNiZ!FQ*86%T089GDA'GpN!# +8LhaVBQL#R+5IPib!HRGiHhYfE'*A6%G5DRf#HfeE56dl3de699&)2$P)@PpG9NN +q1N"9GT+GQBb"I(YqJB+"K)b6P*',JhPZBP4+5Pq&SkZPNRYUBf0SE'aUE(*hH(9 +[D@GUE@TPBQf%PjZ8KA0NCRkARjU,Fej46%pCBfCPC'"GBA@@XlkiU*5%I)+5Rk+ +ENSZ5VmlCdEkMLRKaIk$!cp$+`Vr$aX5lVk#3!)*fF'e[GAKeE@*@8&ekMj+&EPC +)5&0PHBU6PT10KB"rJ(adD&T,3dpVKC'0I'CDCB@KV+ZJM(KUC'9TEA*fG@pRB@G +eIRjiFh0eGR4ZC9a@8e0GFi5$H@pMA&YHDS#BU+fQQT5@R+1TVV+cXl'ZV+fbYDq +KMi52XG,Khp#pX+QSUkq`VUbSSCL2KRpeE'4H9dj*8'"VDf"12$Y5FS&qG'*56%a +4@&aC8%8l-L`U-N4FE("R@8j09f9`F'GC6%TFHiq8PC!!K(CP8NK9GTDRVDQIPBb +%IRK[CPeA88T%3%"#4NG%4&&VLTqMPS*bERH)QD@VU+'FR*qLSk+GPSf$GfTTICZ +XUTU$G)'JXl@YR)9bC'&UGAKhI)+&JAPaF(Z(Li9kEQPYGAb$K(jdCf0`JB4pHR4 +XD'CPDhfCVlQhVkUVXEQr[E1PPiZ$JB1(LB4jD9YDET'daF#`RBYqHi'*MBf*JRK +ZCPpD9&"-58G'4%*&6ePF@&*9Chf,N!#1J(&YF(CpJ(pmHRTkGh9iKk#jaX5hUD1 +PUV#aUTb-JibKVl5hVTb,IA&ZGikSZ,QbUUHRUDfYTTb5LiH)M*'6Mi0bB9PKFSQ +HTk#BNBb1PCUFR*Q6MBD"I(CZCf4LAeT446e%@@TYC9eNHSZ3!*'-IQpL@9CB@&4 +389496d-f0dTKE'aSB9aGC@pfFfGA6eYaHRGaC9G268e19@L"P*ZAMB0mH(KlIi& +rHhPjHRaqIAG`EAH'MC+FRjH3!)b*MTHISU+IQT52LS@!HA"P@%T!1cT"9(#$KRP +TDRZ-Q+'JPiq)JAppH(4cGAGhGA"XFibUZlfeTjL,JAq!IhGXCh#&PCbIQ)CfE@G +PES+JZmV-`V5QRjkMTk5EN!#)KBL-M)CmEf&A@QTpN!#RZ,HXRSamGACqL)q2LB" +iFQeUD'GPBPjC8Ne6CAb,Mif5Sl'`UU#3!)1!JSH1NBq+KS+!IAKaEA"lJi0qGh& +`GS#)M)PqEfPaJ)H*LB0kGA*[ER&pPE,)dmr"XDDLTl6"aX1jVD+@LRpiFQe[JTQ +PVEUpXkQIPj59PjZIS*fBNib$I(4UB&4(2cXj1dCFEh&Q@&&ECfKNB&9,4N4&58Y ++4dC(58T+58K3Chq-LhpZA9&2@'CcHi#-RUDIPBb!FfPH99"8CS@KVUZIN!#%IRk +!JAjhF@aTCQ"D9P*-4dTBDA@&Qk@KQ)TmGRKpKBk9QCZGRCQ4LS4rHR0UB9aIF)k +TYVDbZ-("ZVDZT*b@N!#0LiQ)LBL%IRG[CQ*VJC@FPSGdC&aFC@e[CPaHEhq%K(p +bCQ&KBfKcLkc(dXh!YE#[XVHkYUfNQj+*J(PfGhGkKTDEPCLLT*qDNSU&K)@(Ki9 +rH(0XBeK35dT-6Np168pDDA*`D@4VHS1*Mj!!Miq-Ki"kG(&bG(KmIhprLD#lcG( +*Z+LKSkbbXDQKTV[0cF@jTj5)JAjrJikPZX#jV*f8N!#4NT'1KhjjGR*VC&pHAQ& +ZL*bGQCkJR*Q9MSZ*LBU+L)H+MBf*JhafEQCD5c`Z*Le&C(KlHAf'KS&rI(9XBPC +058K+8&CB9P&)2ca)Ah9pHQpL@9CAA'"H@Q"hNjkDNB"VAeYC@&TNH*1TXl5`UU1 +HR*ZCNib%IhalI(q$K)'"LTHANCDIRTL6M)H'LC!!Q*fDMi&bC&TB@epJA9K346` +m6@KqKiL4SUZRSCZ3!)CrGR"ZEQpbGAKpJB"iEQTfLTHCNSCqIS+)LiL!GRH*Qjf +@Mi4jFh"ZE@adL*qXUk+8KRprL*5GSD+HPBTqG@jRB&eKERU#MTqQU+ZQQj52LiU ++LSQ+Lik3!*!!MBCqG@pYE'TVFhk&KB+(Ql#iZ,5PN!"qEQ9NDA"hIAjpHhCaER1 +%RDkdVk5@LAaaDQCLB'KrQD@MQSPhEh"cGRQ!NDDh[m'r[VbhXUZNRC5-KiD(LSf +1LiD%M*HGSkQRRTH1Ki1#JS5(Ki"hE@0D8Nj+4N4!1c-Y,6K-@PeC@QQ!LiU(IR" +PAPPDAQ0TEA&bF@aK9P0KHSf8Nif(K)+!IRP`DQf"QD#CMi*`B9G36e9QJjkVUTq +5L)*rIi1(KS&jEf095%&$4dP,9'&YHif@P*'0KApmHRPjGh9eGhKhG'pVD@KRC@0 +LDAU4SD+AL)HCV,'`UTf6MSb-MBf-LB0lEf0B8ePYKTQJR*'&Ii#(MT'0KS+0TVZ +pXkD5Ih0YDh@,Um60bEb[Tk@SV+fUT*kEQjZCP)YqF'*B@@Z*UEl"YULCLS'!JB+ +#IRGZD'9NC@4LAeP88%a+8'"cIRpfD@0[KjLGQSTeD@9REhKpJB@)L)0rJ)fQ[m[ +)ZD@9MBf6QCfGPT!!P+Hl[V#ILhCTCQKbKk+bXkZJPBq1N!#4N!#-L)5#JB'$KB@ +!G@CA8PjpRV+iVjk-Hh"[Gi+0P*D8N!#,K(aaD@0H@9026ePZJSQ(Ih0ZHBf@P)b +!F'0F@9KA8de'2cXi1%"9Ehq!H'aI9P&389&25NC&6fL(Q*H1J'aF8e&GGT1QV+L +GNBCrIB+*Mj'1LB4qHAGhH(GcEQKVJCqd[,LUQSf$IAaqK)U0M)U*L)H#Hh*TB&T +A@'4lNCU6J@aJDBHQY,@XQ)4eD@9UF(4fGR4[D@b!RE6"`lbcV+QUUkUQRjL2L)b +HUk@ALRGL@&PPITLSUUDJQTD5Mib)JhjjGR9iIB'$JRecCeaFFjLc[EQUPSCjFhL +!Ji*rHhCcFR0cF@eRB&P89f4kMjQ9Kh9TDhkBTUDFL(0TDA"hHhjqI(PfGS#8U,' +ZSjH0LBZ6RD+JPiTqG(@'RUbaX+12IA0hMDDdYE#RSCkLUV'dXkqTSCL4MBU)KS& +iDepKHCbbY+bFL(KXCfTZFA"ZDfGLAPTB9e414d!m2dpTHhjeC99)5f1#NBf#G@4 +@8%j4@&pLC'CQCh'&PjbCMi*fF(*hI(q"J(jkFfjcJSk0LB"[AeaRIC!!PC')IAC +dH)#)L)&iEfKPBf"H@eP@88Y(6'5-UV1ZS)PeDQYiLC@BPj@5MSU'IR0SBPpGA@G +pPD@SS*+&HA5$SEDiVTk'F@CQE(9lI(GaDQ4UIT@LSjb6MBZ-MSq0LB@!Hh4YF)' +8QTH1IQeSFS@CTUHKRU'RXEc#`VZbU*f5LB5&M*1@P)PpHSbV[,Q[ST!!IR0cHS+ +%JATaD'"B8e"168e06P*EDA0hG'YJ@9CBBRQ4RD'KPiU$J(jqIRelHAZ&Ql2#`lL +SQBf)MCDFRCfFQT@0Ji@ATkQLQBPlJ*@UYE@XS*H6NjDCQjbEQ*51Ki"hEfKNAeY +BA(#4VVLaSj1"FQYVEQp[E'YXF(4hHAThF@GF9PjiNjqHPSPmF'9JDS#2MSCmE&Y +46%a18%j'2$Fr9h'!JAGUB&aFB'*MBf0MBf0JAQClLiU"G@9IEBDAQjD*HR&dIif +5MiCmG'paHB1)L)*hD9P26PalRE#`U*U)H(*cH(k#Ji"lGR"SBPpKC@CREi+CTD5 +CLhpiGhKqMUDeXDHHP)Z(KBD(KAphGB#BXm6&ZkbKQjQES+5PT+'FPSf!F@afKBU +(JAKlM*UHQj1*JB#'MC+6NBf+L)D$IRG`DfGLA&TKF)5GXlQYRBaiD'0ND@eaGAK +mIi'"J(eiF@PND(L1RCq@LAacEfj`HSbDQj@0Ih0YDQKRD@PRDA5&PU+PSTb@Nj5 +BQjL4LiU-N!#5N!#2PUD`UTk6MC@TZEklY+kYXVQr`,beVUHJQC12MBZ*KApeE'P +ZGS+5QC1+Ih&RBepGAPpG@&436%Y08946894MGi+#HQpJ8NK$3N42ChTmG@eL@&4 +69&KFA9ePHBkANS9dCPpKDhH"KSH'JATcDf&B9fCjIhejHB53!*12KhedEh&hHAK +iHAapHhKeFh&[E'CH9e9FDAQ3!+QdX+QINib+L)Q,LiL&K)5$Ji1"I(9bIC+NUD1 +BLAacFAClIiQDTU1DMhpbE@YXEQeUES'HY,fkVCk5LSH(LBQ(K)"lG'jRB@"YKjU +HQTHFTDDFMi&hG(U)Pk#JQC',KAplH(GjHACaDQ9THBfKYX1qVk'6KAjmIB#$Jhp +jGR4cFh*ZCf"GBh&rKB0kEfGPDA*iISfKUkLLPS4fF'paGRTmKCDSX+qRR*@@RDH +[X+ZNRTbDPj++J(b%PCqHRCqPVV'XSTH3!)f4QD#LRjQ4L)"hF'TQC'CSD@GREhb +"K)fCQT@2LB&kF@KLB@4SE("aE'9F99"6BRb3!*H8LAY`D'9NBf"FBR*mI(TdDQ& +D88T&2cY#@("mI(GbEfj`G(KjGA&[F("YD'"A9@*jJhpmJ)@)KS&lHAb"Lj@DQCD +5MBQ(KS@$J(ahFQaQCh@'MjDKUDLKQT!!L)@"IAKcE'4HA@"MC@GSCfCYHSD,L)& +iFQjYEQeUEAk8RCQ4K(0SBQ&ND'eiMU[!b-5hUCqFR*kJS*kFQjZCPBq'JBDATU+ +AP*UMU+DJQT51L)L-N!#0LBD'KiH'KB@$IhGVAPCAB@aeKCbXVUZMPBQ!HA0aFhC +iHRTiGA"UBepMFB@5P)f#GQpXE@p`F(0rLSf-L)5#K)D%IhPdGB5CUV'YT*fCQTq +PTk@IPj!!M)b0LS*mISZ6N!#2QkUdYUkNR*QDRU1RTk+FP)q0MT'6NSq*JA9SB'0 +[GRCjJSH%IhTbDQ4H@PPB9P*48e966NFq1$Y,B'j`CeP26&*EB'&HBR+%L)9qF@9 +H@eTEA&eRIC1IRjQ4M)Z1NjH@NBb(KB1!Hh4XC'9bHhC`Gi5-MSU$I(KiHRq%K(p +hE@4IAPjHAPjH@eC36eTVGRf*R+QRRC5+KSH(LBk8PT54N!#4NT'0KRjpLTZLSCQ +2KS"pIAYcDQPhKSQ&Ih4SB9eGAPjJDRk6SU5GNSU)MCDFR*H4LS@"IATiGRU*S+b +UU+fbYV@aUk@KRjqKSCfANSq0LiQ&IRG`DfCK@eeTGRYpL*UMSjq9KhedE'CNBPj +D@&KC@eeGA&eRH)L4NBTrGR"`G(KmJT1TY,1UR)f%J(epIAf"N!#T`Fh1aVbcVUZ +TTk@MSU'IRTZ6KhTdISf3!)f3!*HER*H1KAjmIB#&L)D"Hh9`E'PRC'*H9de$1ca +,AQafKT14KRY`BejIBfefI(prIhekGQpQ@eTSJ*!!NSU!HRPmIi&qGh0kKiZ)JhG +TB&YA9&09AR+-Rk1EMB&lI)+)LSQ(KS@#I(4VBejNGiU-KiU5QCZANBU'KSL0NT5 +9PC55MiZ%I(0VCQ&F9eCGDh9kK*+CPC'-Ki5$J(epJB5$JAjkGA"UC'"RIjURTCU +,HfaJ@9KDAQZ"NjLBNS4eD&pFA&jMFSbNX,+URC+-LSZ0LiD"JB5(Ki0mFQaaIiL +(KiqET+@HPBf+Lj!!PTbHR*H3!)Q$IRTeEfKLA9PA@fPpM*LQX+fPRjQ8NT!!MSf +,L)@#IhajGR&UB9aJF)10MSL!H(9cFh"VD('#M)f+J(*QAPPDA@&UICHYYV#KNB4 +mHRf!JB"rIi#!IAK`DQk"PTZ@PCULU+DKQC14P*UIS*bAMiCqH(4bF@eSBejD9PG +LEA9rMTLBP*!!LBD'KB&rIRakGh0[DfKNB@"QGikHSCZ4L)+"Ji@%JiZETUDJPSG +jEQCJAPjJDi5JXE@ZSTD2LiZ2NC'1LS@!HR*SA&9EEhjrI)#*P*D3!)PrGh*aGRY +mHAGeFh*`EQPNA94-4N0&6PaUGiLCRjbBNSU'JAYfFh*bGAb#KB*lFQPMCRD*P*5 +0K(ahH(aqIAq0RCq@M(p`Cf0KB@*KBQf!P*qLR*+,L)Q0MSU#HhCeG(0bEQPUGS1 +'JB',RDQTSTZAPjbNVE5bV++AM)0lGA"ZE@eUCQGbKjDDRD1MQT+*IhCdG(9hGh9 +aE@PQBepD9P*5AA'#L)&dD'4QDfpaFRU,PjD4KhKUC'0NCfPVGBZNYVUbT*H1M*+ +DRTqIRCL5Li4lF'TbK)k*JiH6Rk5KQC+0MBq4NT'2M)Q(KiD%IhCXBPP68&0JFS# +)NCbJRCQ@N!#-Ki+!J(pjFh"bGRKfF@TPEB#5QCL4L(pjH(PiFQa`J)f,K(YZC&j +E@9KA@QGrPU+KPiU"J)@-N!#2LB&fE'4IA9YDAQPkJi@-Q+'LRT@-KiH,PCkNTD1 +HQ*'+JhehFQaRBPjHDAb)L)5*NTD5Li"cE@jbGAKiG'eQB&aE@PKBBR@)N!#0JR9 +VD'T[G(9bFi'8QT1+IR"SCfCRDh5'Rl5r`,Q[Tk+JS*kEPj56NBb(J(K[CQ4XGhq +*QD+KRTL4MSq4NT!!MBL$IAG`D@"C99046dj08Q*jMC55NTfQSjZ8LAeeF'eZFR9 +jIS+"Hh0TC'b#PTqGNiL#J)'#J(TbDQalLSk,JhCUBPaB@@*aJSk6Nj+8PjQFRTk +EP)f'J(TdEfTPB@"SHj+RY,1TRTD5PCbKSCkCP*!!MBQ$IRPcE@GLA&KDCA@$L)C +mGi+BT+1HNi9jF@aVE'TPAeaFA&eKEAq3!*H@MiD#JS@)L)*iE@9QH*'JSTf2I'a +MB@f(S+k`UU1GQjZHSD'HQTH8NT!!M)CmF@CG994KI*@JRBprFh"fJ)Q0M)H"HR9 +bF'jUC@&G@999AfpmIhYcD'*VJjbRTCU+H@pUDR"hHRGaE'KREi'6R*b@MB@#JiQ +0M)GrG@PH@Q9fIRjjE9j@9fCpNCH6MBQ)Lj!!P*13!)Q#I(PkIB'%K(jeE'GZKUI +!bF@hT*H4NTHHSD#HQjH4Li@!HhC[CQ"IDRZ*MSU"G@YNBfq'PjU9LhaXB&K89&C +@994@BR@+Q*Z@MBD!I(TiGR0[DfKNB&jNGB5(JACUCQjqMCQIRTZES+DTTk+GQC@ +5MSQ%IRPcE'9LBfTjNUUeXUUJPBk-LiU*Ki5!IRakGh4[DQCMD(H*P*QBP)q*JRY +cFRq8SD1HPBGlF@KMBQ*NE(b0PjQ9N!#-LBQ*L)@$Ji1"IAPbDQ*GBQeeH(f$L)U +,LSU0NC'2M)Q%J(ajH(PkH(0ZD'0H@PaNE'pYERZ5SUDMQiq'IhPkJ)D*LBH$I(9 +XCfYjL)q2LB*qIAq"JAY`BeaNH)L-LS0fD9jA@@9cIB+'L)H&Ji5(LBD!HA0[EQp +aFA"XCf&JE)1CTUQJN!##HRKmKBU0N!#6PTQDQTH5Li4lFh*lLjHDPj',Ki1'P+H +YTjk6KATdEQYVE'TSDA+!M*'5N!#-L)@#J(jlGh*YD@CLA&KGDh9eFR*eHi'%KB@ +'KSL-N!#5N!#-Ki*mGR&YDQKSDR*mKBQ&I(ClM*bLSTb5L)0rIAjrI(KfG(0eISU +6PjD6MSL%Ji5'L)H%IACcHibBQCD3!)Q+NTLER*UAP*18PTLBPT+1LiH"HR0ZDfe +dIB&qGQe[IBU-LS4kFQeTD'PUD@CNB@"LE(f2QCL3!)GqGh&YER*fH(9[DQq!Mj+ +-JRKfJBf5Nj'0LSU-N!#8PTH@NSf(J(YeEfaUD@ekKBCrIiU6P*'0KApmI(k!J(j +kGR*`E'GPDhb2Pj5,Ih0VCfP`GhPhFhH'PTU@MAp[BPTEE)15PT50Ki&pI(k#K)1 +!I(KeFh*`E'CJ@9GLHj+GQT'#FfYVFhq)LiZ*KS&mHAKiGA"VERZ,PTU@MiQ$IAP +hIj!!Rk'EP)TrHAGhH(KhISZARCbANBf,LSU,LiL%J(elGh*VBPYA@fTrP*qHPSb +#Gh"XEA"eGhKhGhKiGhCdF@eTDh5"LBb+JhP`ERb4RU'HP)GqHAChHATjH(U#MCH +DQC52LB5#JiH,LiQ&JAadE'CVH)1+NCDCQCD3!)U%IhYkIi1&KSD(LBU*KRphEQG +PE(U'L)0kFRD$MT!!N!#0KS&rIRk!Ji1"IRTiIBQ6Pj@1KhpkH(Z!KSL'JAT`D'G +bIS+!HhKlKBU,LSQ(KB@&KiZ1MSf,Ki*pGh*ZDfP[I)Q1LS5%MTLDQ*50Ki1"J)# +!IRYjGh0ZD@ahJiL(JheiGA4fHRjrHR*TCQjlJAjiEQ&CA@YiIRjkGA&ZEh0kJ)+ +#J(YeF'eVDfTRC@9VFRb,Qk+KRCD4Mj'5NT14MSU'JAppIAb!LjLIRTU6M)4rHhT +jGh0aHBQ9PT'+Ih0UBf*VHiD,M)Q&JRppI(eqI(TiH(KhGA0[DQ9LCR@0Rk@LQBf +#I(TlIi'"IhjqIRq!J)"pHAKmKSf2MBL"HR4`EQjcIik@P)q(IRCaE'TVFAZ%LSk +3!*!!MSZ(K)@(LBQ+LiZ+L)@#IAKdHBUET+@JPj!!MBZ+Lib0MBb,LBL(KS0qH(* +aGS#,NC!!Li*iEfKMD(H,PTH6Li"hEQKNBf0QEhZ&LSZ)KB&pHRKjI)#$JS"pHAC +bE@TXGiD6RCqFPj+1LiQ'K)+#Ji5'KS5"IATfFQjYG(q(L)D"Hh4ZERL'MBb*Jha +hFfpYDfPRCQYfJSb2MSZ)K)'!JB1%K)*qH(0ZDQKTEhCkIS5)L)D$IhTfG(4fHAb +!Ji*rHhCbF("[E@e`GRYpI(PeFR4pKSU-MBZ)KB1!IRakHATlJ)Q3!*58NSq-LSQ +(KiD'KSD&Ji&pHAPrK)5%LBk6Niq*JRYeFR0iIB'$K)+"IRTfFQpYDfYaHS#$J(T +dG(b%L)U*KS1"IhelHAKhGRCfHS+0PTZDPT+0LB@$Ji5%K)0rHRGlK)Z0LiL&KiZ +0MBb*KS+!Ii#$KSL*LBL'Ji"pHA9aF(&hIS+"Ihq'MT'3!)k+K)"pHRPiGhCdG(0 +dGRb'N!#9PC+2LiQ'KB5$JRjkG@pZFhk(LBL$IAYqJi@'KS1!IRk!Ji@&K)1#JB& +rI(TiGR0bG(PmI(TpKT!!Nj!!M)L&K)1"IhemHRKhGhKlIi5*MBk0LiH%JS"rIAY +jGR0`ER&lK)D$IhTfGhf$KiL'Ji&rIAamI(YkHRTkHRPhFQeRBf&KC@Y`FhCpJi@ +"IAYiGhGhHAaqIhjpHhPhGRKpKBf5Nj'-KS*qIAk"Ji1!HhKmKSk4MiU%IRb"L)Z +-LiQ'K)1$KBL+Lib,LBD#IhakH(CcFA&dH(TpJiZ1MBU(K)+#Ji1#J(aiG(*cG(4 +dGRPqJB1#JApqIAakGh9dFh0cFh9mJiD%JAejH(YrJS1%Ji1%KB@'KiD'KSD(KiL +(KS@%Ji+"JS@)L)D&LC+APT54MSf,LBD%Ji+!IRakHAPkIB+)LiZ)K)"qIAepJ)1 +%K)*qHRGjJ)D'JRplHATrKBH'JhpmHhf#KSQ+L)D%Ji'!IRajGR0aFA9mJSD(KB5 +(Mj58Nj!!M)Q&JS#!IhjpI(TiGR4eHAk#JS+!IhjqIi#"JB"qI(PfFh&[FRPpHhG +dG(Q!K)@$J(aiGhKlIS#!IhemHhYlHRPhG(&`FAGrKSQ(JRYhHB1,MSq-KS&pHRT +mIAelH(CeG(9jIi1'KS5"Ihq"Ji5$JS"qI(TiGR9dGRZ"JAprJB5(KiD%JB#!JS5 +'L)L(KB1"JB+%K)*rI(PfGRZ#KSH&J(TfGAb'LiZ*KB"mHhTjHAGeFh&aFhQ!KSL +(JhjkGhGkIAprIhjpHhTiGh9cFhClJ)5*MT'5NBf)KB+"JB'#Ji@(L)H(KSD'KiD +$JB#"KBb2N!#1LiD#IRf"Lj+8NSq,Ki1"IhjmHRGdFR0hIiD+LiU)KB1"J)#!J)# +!IhprIRajGh4aFRGmJSQ-LiL&JRppIAeqIRepIAjrIRjpI(amI(YlI(q&Lif,L)5 +#J(ejHS')LiU)KB"mHRPjHRTjHAPmJSH+LiU)KS5#JB#!J)"rIhprIhelH(9cFA* +fJ)Q1Mif*KB+!IhpqI(YjH(GfGRCfG(0bFA&bFhClJ)1$JAppHhTkHAGfGhf%LSZ +*KS*qHhPiHATmIi+&KiH'KB@&KBD(KiD&K)5$JAppHRKfGA9eGACkJSQ1MSb)KB+ +!IRjpIAemHhTiGhCeGACiHRk"K)D)KiD$JB"rIAemI(amI(f!KSQ+Ki5"IRalHha +qJB5&KB@%Ji&rIRjrJ)'"J(ppHhPiH(KjHAPkHRYmIS#$KiU,LiQ'K)1$Ji+"J(p +qIRepI(apIRq#K)H,M)b,L)D%JS'!J(prIRemI(YmIB'&KS5#IhemIS#$KB5$JAj +pI(apIAjqIRjrIi#!IhpqIAepIAepIS#$KB@$JB'$KiQ*KiD%Ji1$Ji1#JB'!J)# +"J)"rIi##K)@&K)1#J(pqIi#!JB'!IRelHAKhGhTqJB+!IRalI)##JS'!IhjpIAj +qIRjqIRjqIhq!J(pqI(YkHAKhGhPmJ)+#JB"pHhPkIS+$Ji+"J(prIRemI(YlHRT +jHAPlIi5(L)L'KB1#JB'#JS1#JS'"J(pqIRjpI(YlIS+%KBD)LBQ)KS5$JB#!J)# +!J(prIhjqIAepIRjqIAalHAKiHhq#Ji1"IhjmHRPhH(b"K)5$JAppI(YkHAPiHAP +kI)#%KSD'K)+"J)#!J)'"JS1%K)1#JB"qIAamI(amIS''LSb-LSH%JS'!J)#"JB' +#Ji1$JS+"J(pqIRjpIAjrJ)'$K)1$JS#!IhprIhjpIAamIi1(KiD%JS"qIAamI(a +qJ)1&KB@%Ji+"J(pqIRjrIi#"JB'"J(pqIAalHhYlHRYlIAq$KSH'K)+!IRjqIRj +qIRjrIhprIhprIRjqIRprJ)+$KB@%K)1#JB"rIhprIRjpI(alHhk"Ji1$JB"qIAe +mI(eqIi#!J)"rIRjpI(amIAepIAjqIRprIRjqIAjqIRjpIAamI(amIS#$KB@&K)1 +#JB'!J)'"JB'"JB'"JB#!IhjqIAeqJ)+%K)5$JS'!J(pqIRemI(YlHRTlI(q"JS+ +"J(jpIAamI(eqJ)+$Ji+!IhjqIRjqIhq!J)#"JB'"JB#!IhprIRjpIAamI(apJ)1 +&KB@%Ji+"J(prIRprJ)#"JB'!J)#!J)#!J(prIRjqIS'$KB@%Ji+"J)"rIhprIhp +rIi#!JB'"JB'"JB#!IhjpIAeqJ)+#JS+"J(prIRjqIRjqIhq!JB'!J(prIhjqIRe +pIAemIAeqJ)+$JS+"JB'#JS'"J)#!J)#"JB'"JB'"J)"rIhjqIRjrJ)'#Ji1$JS+ +"JB#!J(prIRjpIAepIRjrIhprIhjqIRjqIRjqIhq!JB'"J)#!J(prIhprIhprIhq +!J)#!J)"rIhpqIRepIRjqIRprJ)'"J)#!IhpqIRjqIRjrIi#!J)#!J)#!J)#!Ihp +rIhprIhprIi#!JB'"JB'!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprIhprIhq!JB+ +#JB'!J(prIhprIhprIhprIhjrIhprIhprIhjqIRjqIRjqIRprJ)'"JB'!J(prIhp +rIhpqIhprJ)'#JS+"JB#!J)#!J)#!J(prIhq!JB'"JB'!J)"rIhprIi#!J)#!J)# +!IhprIhprIhprIhjqIRjqIRprJ)'#JS'"J)#!IhprIhprJ)#!J)#!J(prIi#!J)# +!J)"rIhprIhprJ)#"JB'"J)#!IhprIhprIhprIhprJ)#!J)#!J)#!IhprIhprIhp +rIhq!J)#!J)#!J(prIhprIhprIi#!J)#!J(prIhprIhprIhprIhprIhprIi#!J)# +!J)"rIhprIhprIhprIhprJ)#!J)#!IhprIhprIhprIhprIi#!J)#!J)#!J)#!J(p +rIhprIhprIhprIi#!J)#!J)#!J)#!IhprJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J!!!0%!!!3!"!!8!!!#J!!'!83!!!!!!&!!!!!!!!$3@9Zk,S`!!0"3!!$3 +9!$b!J)#!J)#!J)#!IhprIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!JB' +!JB'!JB'"JB#!JB'"J)'"JB'"JB'"JB'"JB'"JB'"IhjrJ(prJB'!IhekHAk"JiU +0L)Q0L)1"Ii'$K)@&KS@!I(TkH(4dH(U%M)*hIAphG(CrLSf*JRapIRk$JS"rHiD +DNiD(KB"qJSf9PC+)I(&XF(YlE'CXE@GSDQ9MD'CLEAPlIiU&HAq%ISUDQj52Mj' +2Ki#'M)9iDQKcG'CHC@995e0LE'GLD@TTF(Q-R*++PCD5RUUMN!#2S+LINT+AQT@ +(HA*qMRPNDf*+3%GHCeC9BQ4PFAPcH)Q0Lj@RXDqaYE+XUkkQU,UrXjk8Qj4kDfa +TB'"G6%G46Mmj2NpMEAQ0QCZDQ+'[YE[#`XA-`lV)fY#qZ+fIQj5$Ef&95%4%26d +r,b)S18Y19'KlQ+kGNU@XV,rDhFDqeq$4cXDl[,LMLAU$M(9-,b3M+5mZ,MFj-6) +m4P4KG)Z-M+Hqc0V3ZEI$aXcPlYrEelqPNhpVC'0@2cBf,LBY-5)9'bSj6QCeJBZ +4Nj1DU,V2f0(-cYAHhG2&XU1JRSk!I@e53c8R-6de-63b0$K$@'9XH(q&Nk@i`-A +1bm(%b,klcH6E[,#[Rj55He!k3%3k1%"#1#mQ("BL5@ebEi'APT5Y[lD[Y,r-f1R +jpqI*S)Q4RTH+LAYA3Mij,b3H*#JN,NY46'"`BP9HGBqYcYl5b-@hU+feYm2+`+Z +ISjU'E8`c+5B`3%8q1#mH%ada3e"A@fL#Q+#MU+LJR+@[[Fh6ep1lTD'HLR9cFf" +49&4&1MBZ)a`C'#)k6P9KDf0FDRk3!++ZY-('[-,@e,qaV++ESDfZQhaG46e"1Li +Z-bdQ,$Y$36Y#8PCBDiUMVl@dVV'fXUf`YEc0hYR&Y+LGK@426&0BA@9G3#XR*#% +L,NCIF(aqJjbXRT+CRkM%i16Cdp2*Yl#cVU@HNi0aD'KK6Mij0c%e1M3e4%pACR* +eGhk-R+Q`Ylh$c-['`,+ZYlbdV+qbTT4j9$p'6Ne,4N0)q05ml6&CVMDQl`,1 +HMT+V`F'iZmhEf-ZiRib%J(9MA@PaC9!r063i-#8U3&CLERCcGB@8Q*'*NkLl`EZ +fZ,5SSU'KUkqSST0lF'TG8dBk0cFj2N4$16%c294XHSH3!*DKTD'GRDHcXUqc`Fr +1`,@VRC'*J(&J99YK96id0cJf-Lie3NTBF)')P*Z@N!#-N!#KY,l#`Ebm`m1mXTZ +(Ji5*Mi0S88-h,bi[-$9!46ml5'9fH(YkHBk[[E'UXlLbV+qeZ-25cE@RRj'&Hfj +J88a14$Jl3d-l-bdT-e0dJS5-PC!!M+#iXk+PVDZbbYrGbE1JM)##LB*`Df*,3dK +#05mZ+5Jf8'GVERb"HAq0Nk5rbX6"a-+jXEI'alQXSTQ4LiTmA83k0cJr5Nj$1$3 +a0dPFDR0iHS+G[-@fTU5NR*DSb0E6e-ZcR*+1Jh0VCPpE9Np*4M`V)bNe2NaTKj' +'Ii1)ND'd[lqp[Er%dGR1[EHXQTHHR*!!HPj*16)j3d!f-c-c0d"3Af*TFh1!RDq +h[lkXR+'c`FR2cX@qYUZNQik&FeT-68p26%8h+bFX0%"@DQa`IiU)M+1fXkURVVD +i`Y(3`lL[S*bMS*!!J(&E4N*(5N8m-bSL+$T%5PaQA9aeNCbNVEHeU+Lf[,h"a,b +[UUZUUU15H&p24$j#5dj%0c%U*bp!5djCEhq%MD#VTU'GQUDj`XV1aEffUkHUTTU +5K@G16P4346JZ+bFR-$8h3Ne4@'9iLjHGRjbHUlUq[mA$ZVHdUUQf[Db-FfGG88G +'5d3j1$8`06e%5NY0@@q,RU+IRU#JS+Ud[FA'[lDdZm'lUCL)FfCPB9C-48%q0#X +Y-M3m4%0+BAZ-NjHDQCfUVkklcp$"[VffZ-($ZU@4K(a`@dJr16Fh0$)e1MSi16Y +"8fk'Q+5SU+LRVEHj[-E)bmr+bmQjUCZ&Fh&cDPa02c3a-$%e0MFj0ce-@@CjLC5 +@NjZ[Z,5f[EqlZEUraX@jTT@+Ih9ZDPp+1cJf,bXa16Sd-MT%9@emJBUBSUU`XV@ +r`lfhYX,1c-LpU*@+LB*Y@eG42bmX-$3c,bma-cP$8Q9eIiQ6PTHNYVqpYE1l`mR +1cFI"YD#1IRChEeP)2$)c-bma0$)b06K"8@*`I)1,Pk1[ZVkm[,r"`mR0cp(+XCZ +APiPfD&j226-b-6)c-#iZ-$G(@Q4TGBD8R+@`ZEl!Zl5e[FVAeXQpYDUFLAKaD&K +'1cJh0$%Z+b3N,cT(9epUIiD"MULf[m+l[F6"`F[3d-QmXkqNPSPjCNmq1M`m0c) +[+#3N*Lp"6PPXHhq'P+1YY,Up[-$(cpA4b-M%YUQPRj*rCNp"163d06)Y+#JV,$! +p6&KKD(Q4S+Lf[VHb[FM(ap$Bemh!ZEDZSBpjDPT(2d%p0M3c,5FQ,6a&5P4KEhk +2SV'fZX($[m(2hGh9d0$0a,ZaSj0qCP4+36Sl2cBR)5FY,6%q6&CKEAf6T+LZZm, +!ap6BeGACeXc&`,ZjVT4pE9C"2Mdd,LiZ,#JQ,68k3%eEDRk4SDqeYER"aFM2f0l +DdFh0b-#eTC&pE&a03$Nh-LdT*L8T-$8i2%96D(k4RkL`Yll#`XM8hGV6c-R+b,k +[S)ekDPY24MXb,5NL(L-X-6%f2dC3BR@(QkbeYl["ap$6d-r3dY20aX+fSBjrD8p +'58)c+LFS*b-N,$3f1d98CRU2SDHSX,l(bFc5fYh8bm[1bm@jSSGhF@K@36Bd-LS +M)5BY-M3e1$p1CRU&N!#LYlkk[XV4e0E5dG24dY6)XCf8KQeC6NT(1LNN*5BS+#` +a0Me(8PaXL+'XVE'pbY29dXh0dY68e0,,`Uf+EQCQ@dSq16BZ*#!M*#F[1$a!6@9 +pL)U5Sl1raml9fYM4bFI,dGE4[+HCM(CH6dK!0LmX+LJS+LNS+6%q8@ChJj'GSUU +f`XhCh021dYE6dG(+ZUQEM(jYA8`p-5FN+LiV+#JU,#mi6'"cKC+DTDqlb-r1cY, +8dpADfYA,ZUHAMB*cBP!r-#`Y+5NX+bNU,M3q6&eYISkFTE#qb0$6cF[1dGEIiYR ++ZkZGMRpcCP9$0#`V,#dX*L3R+M"!89jZIiH2RUkpbY(5dFl0dp[IfY$(ZDQKQip +pCNSh-bdQ*Li`+#-P+5ii4PCQFRq8UVLp`FA(aX,&e1$PjGM"XUbKNS9jEPY#-bm +V+LSQ)L)L*M*!59&IERQ%QE2$bml+`X62fGlJi0V1`,#MQ)pqCNiq0LmX,#NL(Ki +J*Ldk5PCGCR5*RV#p`XM4cXE,eGVFhGM)Z,@bSBajBdXl-bm`-LmP(KdH)5`p5%p +FEAb,Qkbl`m2$aFlDi1(Jh-kqYl1XST@!CN`l06!Y+bBL)"iJ+$)l3dY6AA#*TEV +"[m((bXR0fH,KfY,-aEUZT**e@NP#2$Ba-#XI&aBH+$!i4e4BBA5+S,+la-c*bGE +HhGhDdp(,`F'mTipjA%3p1cJf-#BI(4`F*$!k2dPEDRQ4U+q[YX$'d0lPjZAGcm6 +&aX'kV*L!C%p%1M%Y+5)H(b)P+M%e0Me-ARLBYF#lZX$#b0ALk16LiGR2bX5dRST +dB&9-3cXa*4S9&aSJ+c3i1Me*ARD+RV,!aF2(e0lJhY[CeYECeXZkT)edAe"'3$` +d+"dD'4NH*#S[0N*5BA5-SUqbYVr0hqRSj1(Bcmr8e-brVT4eAeG-2#mS*#!G(5% +M*5BR+c9&BS5GU+faYm,+cpcVl16KiGhEemDYQ)PpF'493cBV(4-4&KdP+bST-$K +%@R@+QDZma-R@iH,HfpRFiH2MhXqjSSYeC9a64cSX)"ND'KJG*#-P,cp-AA5'P+1 +a[-[CiqMQhYM@f0cIfp,"Tj!!IQaB5N)h+5)I(4iK)4XC(bNh6@GlLCDLUlA"dGh +LjZINiprGi0M&XD#4L)"[@NBb)a`D'4`I)b8M)LNf3e4TI*!!S+r#dYIBh0cAdpE +Jl1lJc,HMNB"aCea*0b`N(4XH(KNA'b-Y1daBC(U-PUDmcYhQiYE6f0cJjHAIdX# +XQ)GkE&P%0#dU*4mI(aN5&"mU08PKEh9qM+1lbG$Eiq(KiqARj0cBd,fYUD'*E&C +$-bFJ)#3L(aiD'4`M-%"1B(@,RkqmbY,@e06EjZhbm1MFblQYT*1$H'0*1#`N)b! +E'4F8&b-X-Me,9Q9kN!#T`Y$@eY28fq(Ql1cPiGE$YDZFLR"@4MX`+LFL'488&"B +F)c!r5P9MGT+XZXM9e0(DiZAVlZVNfXr([+qNN!"c@dNk,bSP)4iC&439(#J`-ca +2CAkAUl[(c-h0dYrTlr2[j0M4c-#XQj'%DNmp0#XM(KS@%a3F*#NV,cY,@'k1V-, +2d-c-dYVMkHhYlqcHcX'dT)jaAP9-36BV(a83%4)@(LNb16j%6fQ+S,$!cYEEhYr +IiH2PiYVDfp2#USabB9)X*L!A%!m8'b8V,MP+A(#&QDbla-[5eq$Ym1AGh0l +IfmkmUCQ%DP4'1c)U)KS8&4NE("dK+MG$9@f#PUZh[FE3fH$NjqRQj12GeFl$VCD +#EPa-2c8U(4BA&a-9(#8T+c*!8fKmMU@laXV9iH2IiHAMh0cRkYV$VjQ#DPG-3cB +V*L3I'"-6&aXJ+cP'9@PrNCZPYXI6hqVYk1,IiZ6JfG6,Z+#)G'453$3V)aXA'4X +B&4NL+6%p6fCmMTbUZFI9iHAPjZATkqAIh0R0ZU1,GQ993c3T)KdC'4NC'"NG*Li +j5f4lLCDS[XV2eYcIiqISkHMRiY6#XD'1IQj@4$JV)amD&KFA&4JI*M%q59P[KCL +SYmE2eH$Uk1ASkqIKfpA,ZkUAJ'TC5$8Q(a`F("N9&KNH)LSf4eTYK*QUYmE@fpI +Cj[$ZlHhQhYA)Z+UDK@jD56JV*"mF'"8@'"NF*#dh4&GVJ*5PYFM5eYlNjH[Yk1M +Ti0A1`+bBKA4F3c8[*KmF'4B@&48C)5`h4&4PHT'PZFM0e1$RkZcYl1VRh0,,`,+ +LLQpA4$Sa*KX@'"S@%4)E*#S`28eLIT5MXVr0h1(IiqV`mHhPfY21`V+FJR&Q8MN +U*"mE&!i3&KJF)LBY1NPHFSHL[-[3dpMJk1cVjqMVkq63[E+LL'jB58)h+4iA%3i +4%K-A(5Ff2N08FSbIXEr+fqEQjHVam1cTipV5bVkPLRCN8d%[*5%E&4)3%"-C)#B +U-80FF)'AVm24epMKkqlZm2,VjHAHbl1JNS"S88%f,#%C&!m0%"8C(#-X08*5CS' +DVEh,eH(XlZcZlHITlZVHclfRN!"iC&9&1#`I&K-4$`i1%KSM,$G%8@4mMk+ibpR +Nkq[Ul[$VkHAKhpI$V*4pD&9"-LSM("B4$3i6&4JG*6"$9@9iM*kaaGAIjqrdmHM +NjqVSiY+qVjZ"E&K#0#`M'aB2$a35$")G+$9!5epkMk#b`-cFk[,hmZVVm1lLeY( +,ZCU!EPa+1bXG&a89&"!0%"FE)bik6fCfKCU[`02Rl1ETpIMak1ARjGI$XD#2H9p +)0bSL(4F4$Ji6&aF@(bip69aYK*kd`mh8i[,dlZlZl[(UhYE)XTq2H&j*1biN'a3 +4%a)2$"!D*5mk4e9XL*UMYY$MkHRVpIM`l1[MhGI+ZD51H@C61bJI("N@%Jm1%"3 +A(#Be4Pa`I)qV`-cAiZRYm22dp2,UhY2'Xk5@J@T415dS(4-5%K!2%"!A)5Fa39" +QIC+T`-c9iHVZlZrbp[6Xj0E%Z+L4IQe92c8T'a85$`i2$a%@(bXh38PCGT1QYmh +Ik1VUlI,cmI$Xj0I+`V1BI@P@3c%P)"dB%K%2#JdA)5FY1e*QGSLLZX[9h1AZm26 +mqr$Sj0h2ZkLFMh9B3c8V)KX@%3d1%4-6'LJh2N49FBkMYmM5h1IZmrAcmIEdip( +*aEQJK("I5MFT)4`9$3i5%!mB)5BV0NCBESDGYX[Chq6SkZrhqI(TjH2FaDkGLR" +C56S[*amB%JX)$4)@'b)Y1dC8DB'DX,r-fH2XmrAbm1lTjZ(6aEUPLh4H5MX`+#! +A%!i-$3m6'b3X08"4D)#AV-$2eplUpIRfmZhSjGr@cF#TMRTN6$dc+"i@%K%2$K! +8'"dR-d&6CRbAVER'eqETkI(lqZrSk1ADblbZQ(YM8N3e+#%E%`d-$a%8&amT-$a +1CAk6TVV'cGcZprEbmr6[jZ$Hdm#VPB&X9%)h,5!8%K34%")6&KdP-$j0B(b8Skr +!e1,VmIAhpqrSk1AGd-+bQRpV@8Bd+#!E&3m1%438&"FI+MP0CRf2S,6$bG2Nm[E +cmrAdkYlCd,bSQ)PfA8Jm,am8%"!4%K)9'K`I+$9&@h+,S+qqdYrMjZhbmZrYlHV +Kdm5YP)&aB&"",L!D&!i-%")6&4NI+6G'9QZ%QUc!dq(Rl2$bm1hZmI,Tf-DaR)K +aAP&"-5FH&4!2%"!3%KJK+cK*@@TrP+@hcH(Zmr2bmI([l1IPfmLdTC4pCe-q,53 +H'4B9&"%4%a8F*c0$9fYqP+QpcpVJjqrfp[2dp2$RfFLhTC*qDeK&05JG&a-4%4) +4%43D*64%8f0hM+#fc0rTl1cXmr[jm1cUhXUiUjU%EeT%-b8G'aN8$``0%"-B*$) +q6@"bKCkhb0AJkI(blr2hpHrQfFr%XCZ)FeT$-bJI&a8A&K%1$!iA*#mj4ejeLCf +caY2FiqEXp[clq22SfXr"VjZ*Gf001LXL("F4$!N-%43B)bih4&9TIjQdbYIIj1V +Zm[6eprAVhY,(Yk'1H9j(1c)S(KB6%!d,$4)C)bde3P9RI*Lb`p,JkHcXmIIemZl +Uj0R,ZUH5H9p,1LiQ)4mD%3`-$!d5(5Nh4&*SJjQTZmh@fH2br[rlqI2Pemh&[Dq +AI@YA2bmQ("84$a%4$a-D(L3Z1NYPIjLb`XRAiq,Nl[AjqI6VipV+YkHAI@&54cJ +V)a`8$JX-$K%A(LB`2%KEH*+RZXI6hHEZmr6cmr$QiGl4`+qDJ'K84$BY+#%B%!i +1$3m8'b3Z2%pJFibMXX,6hHIcq2RkpqrNhp[3`lHPLfpA46JX)K`A&434$K)@'4i +S0N9EH*+QYF64fH$Rl[6kqrA`kq$6`kkCKA4N8N!c+5%C$`S-$a)A(53X0N09E)1 +FYFM5fZIcp[$YlZlYkH6EblDJL'pC5%!i,"m@%3m1$!X3&b!X1849EiD9TEE-i1h +dpI2cm1[QiprCdm1QM(KK66d`*amE'"84%"%3%4NM-N9CFBHATVM(dYlUm[IjpI$ +Xjpr8alDLNB&Z@%3e*K`B%`m3%4)8&KXN-$e2C(Z@UlR+fq(Mkr,al[$bl1AGcVQ +MMheX@NSl,5)D&K)2$`m2&"`Q-6e3D(U*RE,#eZMZlr([l[$XjZ$Dd-+[QB9cA8B +e,#-E'4N@%!m5%aBI,6j4C(H-SE2"c0IPl[,bmHrVkHIEb,HYSBejC8`j-#FF&a8 +@'"F3$4BL,6T+AA5+RV,%dGVJjHVZpIVflHAFcEZUR)YjD&C$-LBH'KB3$!m5&4` +P,MP%8fL!NkV(fGcKkHhZkqIUlZMHeFLiSielCNir0LdP)"XA%`m,$aJL,6K%9@T +rNkQpbpEMk1RXm22cl12FdF+hV*ClD&C$05dN(4NA%`m0%"FJ+cK$6f0lNDHpcYM +Ei1[am[2bkZ$CdFHkUTD#EPK'0bdS)KS8%3i2&"NK+c-q69jdN!#S[Fr9f1(XmI, +Zk1ERiYI,[+kGJfG54$Xe,53F&a%0$!i9(bXf3%YHH*!!SDqqdH(Skr$epZlNfp2 +0c-QkRS"VA%Xj,LNN(aS8$a!8'4dL+cT2C(H-Rl,%cG(Gl[MjpZlPhGVAc,qdTj* +h@dBk05mM'489&K88&"NL,$P(@AD9U,@m`XlJkr(fpHrXkGh6cF1bS)YdC9G'1#i +M'a84%4)8'"iL+69'@h1(PkLkbpMKlIAhp1cMhGcFeFQjSielDP9%15dM'aB8&KJ +A&KJG*6*%@@k&QUUhapAHk1ramHcQjZMJdF5fTj*kCeY225`M(aX9%a39%a8G+$0 +!9R1*PD#``0$Hk2$emH[Ukq6BcmHfRiq%H'G83#iK'"8@'"JC'aXE)Lp#9QChMU@ +haG,Hk1hYkqMNjHMPf-5bTCQ(FPe036BS'aBA'4SB%a)B*$0"6f0lND+`[F[Hkqh +Sjql[kZAJeXLeSC1)HQTC4M%L(4mI'"-6&4JG*6!r8@0cJC'U`pEIi1,UlHEPk16 +GemLdT*H0JQT22MBX)adD'4J9&4BD*$0"69PTJCb``0$GjZRPiHAUlHlPdF+lVjk +,H@KB5$JY*5%G'aJ4$"-I+6%p5PPXISkM[Y,Fi12Pk1hZkH6EdXQpV*f5KR*F4M8 +Y+#)I("F8&aJF*c!k59GLGjHba-h8h0rKk1cXlHVJeXUkUk1AK@pF6$id,#3J'aB +8&4FH+6-k4&&MHC!!TVh2f0cEhHMcp1hRiGA([E5UQS9bB%`p05mT)KN6%a3A(bJ +a18*2Ah1,TEc)cGIMjqR[m1VJeFr0a,Q`SBTbA%Xr0LmU*KmB&4BA'b)X0N"0Ah@ ++SEI"aXh@jI(clqcNeXh*`VbcRiakB%T#2$)T)a`A&KB@'b3X-MT*AA''RV1paY$ +EjH[Ym1hLep$+b-+`Rj&jB&0*26-Y*KdA%a3D(b)S-ce'9Qq(QDLfa-rDjHcam1E +EeY(-bF@kTSpkCeC)2cJ[*"dD'"SF("mR,69&@h1)Q+@c[mM9iqhdmZMKfY,2blq +cTT5!F'"236JX)KdE'KXI)#!N+M4$9'Z%QD@`[-M9iZRYlHIKhGM4bm1eSj!!IR* +V@NBj,b-F'KSF(b%L*#Ja3&*UJC!!Rl#laYAKjHVXjq(FeGIBa+kLNi4kE&T-2Lm +N(KSC(5%L)L%P-d45BA1*S+feaYVNkHMMi1$Jhpc4`E@SPSCiDf"42biP)4dI)4d +D(53U-Me0C(U*Nk+hbpMGiqMPj1AKfpM8blbQNSQ#GfC226-X*5!H(4mK(b!S-N& +6Afb!Nk5ibp6EiqEPj1,JiGr9a,1RRj1$FQ*33cFT)#!L)KmE(#)T-6j0@farND+ +daYEKjGrEhqITj0[3aVfZQBZ&HQPA4$3Y+58K(4`F(5)U-d"1@@4eKjbeb0AGhG[ +NjprDhYh9bEQTT*Q&G'443MF[+bJM)"iE'KiR-d&-9Q4bKCk`[FrCfGhQjH$JhYE +1aEUZSjL+HfK33$Nb+bFP)KmF'L!U-ca)9Q4aK*kb[XV9fYcFhqEShpA2`l@`VCq ++GfKC56Sa,b`S)"SE(b%S06j#6&jcKCHT[Fh4dYEGiqENhG2,alqcTTb5JfT64N% +k-L`Q)L!H(L-U-$Y+9&TRJ*b`ZX21eYhJi1AShFr,albdXDD4HfKD68)i-LmX*4m +H)#-U-cT"5PYeLT5KXm(*dGMHjZVPh0,(`X'pXU+5KA4H68-m0LmT*#)L*#FU,68 +q5PYZJCDUYVh#bYAIjZRRh0(,aEkjVk+8K'pI9%P!0bdS*5)K*5SX-$8l4PCTJ*H +QVlR'cpAEi1,Kh0I2aX'pXU'3!)"bCPa02cF`*b-N*53Q,M3f1%*AE(f0Rl#k`Xc +@h0rJhYM4bmV+`l'INiCdC9T24MXa+b8K*LXV+LSb2NP9DAk1RDZf`-[6h12Mfp6 +8e-l'Zl#MP)4hEQ*95MXX*LBS+5XX+c!e1%*8D(b+QDQh`mrBfpI9fG[Ad-h1aV+ +IMi&hF'CA56`d,LJN*#JV,5ie3%P4Ah+%NU#ebG(6fYrAcXl3dpM4`VHTNi*iE'& +A6%)h,LdX+58L*#Xe2dYECh&pLTLV[-cEj0r8dpE3bXR([EDZRSk"FPp346Xc-$% +a-#XQ*LNZ0N0@Ch@#MCUY[F60epI@f0RBe-r*`,#LQT1(GfTB4M`f-LiZ,b`R*bX +`1NK6A@amM*fa[mI0dG$3epVBep6(ZV1RR*@'G'TH6$ij06-a,#JR+5ie2NC2@fT +fKCU[[XV5dY(8f0I@e-l(`EQZSTH-IQP64N%q163c-#XS*bNb28C0@QelL*kb[-( +'bp$9ep[IhG,$YUqXTTZ1I@YF6N!k1$8a,5NU,M!d280)8f"cMD1[ZX6(a-M5fGR +Bep(([lHVSCU1H@CD8dY$1M-[+bSU+c!h280*8f"[KTkZYEc'c-h4epRBdmR!Zlb +jV*b1HQ0@8%Y$26Sd,#FT,M%d18"*9Q4eL*UQVlV"a-hCiGr9bXE#ZlHfVk+4I@Y +I9%P!2$Fb-M%[-$)h1MY"6Q&iMTfTXlLl`FV3eYVEemc![m#iUCL*I("N@e90363 +Z,#SU-6Sp1Ma'8PeVJ*DSXEDrcG6@ep,+amE*cFDjX+'+GfPI@eC,3M`b,$!`,#m +h1cj$6f*jL)kATV5la06Ef0A4b,qq`mE"Xjq-IR9U@8p+36Se-#md0cFd-64"8&p +dLT5ETl#h`Fc4dp65cmR(b-+dU*f3!(pbEfPD5Mme,5da-c8f0$Bm3dYBEB#-Pk@ +c`-V3dmr*aXE*cmr)[l'HLheeEQ4E6d)k068d-c)a-$-l490PFRZ&MjQSZFR@ep, +,b-M(aF6$[,'NPiYqEf*95$ik2$dj0MFf,b`c3&"MGB5,MjUTYX$&bp2Adml-bmL +rX+@KQT'0JQY@5Mme-63k2Mdj0$-h2NK@CA1(RUZ[ZF2&aF2#aXrAf-r%Z+QCN!# +,JhY`BP4'1MFj0c8c-M-j3%4-@@C[HSQCUlh+c-I$`X2,dFh*`lZbTjL2L(eV@8Y +&4%%m1MJa,5md1804A'0ZIBLAU,E!aFE(bXc0cFc&[V@UT+5FMhaS@e&&3$mm1MX +j0$-f18*-8PPSIC'KUl5l[lkp`X[2d020[l+YUU'8KhYaCPK-4d-q0c-a-cBl3NK ++6PTSFi'AV,V"a-#k[m[0bmM"[X#kUjkBMAYUAPK568P%1c3a-M8i2NK39&YSGS5 +8T,'i[-,(bFV+bFE!YV5fXDULNRpZ@e&5680!3cdc-cFl26p%6eYPH)qJU+Q[YlQ +pbG$5cmHqZVDaUkLJNB&aC&a@6N9!1M3h2$Xl3%4(6&9MGSHATl'`Xlh%aXV,b-@ +rZVLfY+bGM)"bBeaD88G"2$Nh0$P!3%"%5e9MGiUBSD@UXl[#b-[1cF1jZEQcX+b +JMS&fD@&E88Fr1cXj18"#3$p#4e*PHSQAS+DYX,@ramc1bF,![ELdVD@FN!#&I(* +RA94)16%e2d0$38༃Afq#PU@XVDqkaF6!`F,$a-1rZl+PQ)YmFh0bCP9*2cS +k1$Fp3N*$4NY4B(@%LT+KX,V!`m$#b-DmZEfr[VLTQ)b"IAKUA9C13cdk1$a"36p +!3NKCD("kL*5KVE+mb-M$`,kp[Efq[,#MRC@'HR0XCea*26Sl2d*"2Mj"48Y8Afe +qLjDIU,6$b-6![EQp`m+q[EDSQSq&I(CaC9C,4N%q2Mik16a"4djACR9jI)LFV,D +paXV([VLm`VqiYE+UR*+2Kh4PAeP13cj!3d%m1c`q3NaFDh"dJ)fATEE!aXR%[Vf +mZ,UpYkfSTTU)IRPZB&&*5%C%4%!l1MXq48eABQpkJBQAU,Lr`,qr`FE$[,ZkXl# +UR*12L(e[Ae&-5dP%2MXp2M`p5&4CB'TeIiZATV5q`X,$aF+k[-1mVDHQSTf@LRa +ZB&G24N0(4d0!1cY#4NT@BQGaJiqCTV#j`,fi[FM,aEbfVk5ISk1@LS*eBe9-5dT +&3$mr2Mp#48T5A'KaHSLEV,1bXlV!`X,%aVqdXDfIPjfINS"bC9a96dK#3%!p2%* +'58p9@PpQG)UHUUqdZEbq`-(#[lLeX+LSUU5DM(KUCPp@8e403MXl26a"58p9@f" +ZJ)b5R+QbYV["a-A*aEU`UD@SUU+CNB9eDQ"A8Ne'3d!p2%"(69"289jZHS@6TE+ +bVlM$aFE)`EL[UDbYTU#FNB*cD@*F@%p'36Xk3dY*5%a499KKH*!!QU+XXV+f[X6 +%[VUlZE'UUUZMP)CmG@pUCep45%)l1$P"6&*46e0GCh@%N!#ESkQf`F(#b-DkXDZ +RV,+YSTU3!)&cE@TN@%e)3M`m38K,4N0-9&YRH)L9QCZRXlDmb-V#ZE1`VUUQTk@ +BKi#!HQeNAP*$1MY!4NY-5dY*5eGRGB'1Qk+RVVDr`lqlZ,1ZX,+bV*f-Ki4lGAC +bCP9%16Np3NK,6%a-89aREhZ(MjLQY-(,bEqhX+b`YE@cVU5@L(pkHACXB9C,3cp +!3N9'4%9,8Pp[H(b!L*5LVlc*cX5jYE'XV+q[Uk5AMBH#IA9TAe4(3dC'4NK'38* +(8Q&VFRq(KBkJVV[*bF'lYV+cY+qTT*fCNSH#K)&[@8T)5NC&58P%38*(8PjSGB# +!JSqKXm(%`VqlYE'dYDqXUCZ-MC12K(CTAP9-4N9'5%4!380)9'4XEh*iK*DLVEh +)aVkhXlDkYUqQRTZDP*12JhKX@P&25NT15d%p2d&*9f"QF(GkJ)kLYEr![EHf[X# +jXDfSS*D6Q*U5KhYU@P&388j*4%*#3N"&9'*SDQecK*UQV,DmZlZq[,c![E#LQ*5 +@QCZALhj[AP0368Y,48%46@9CFC@TdKC5NZ,UdY,@i[X(![+qLQTDAQCQ6M(j +VB9eA8de#28"!3%YB@PCBA'*[KCUTXV@dZEUfZX6#YUbNRCbFQjZ6Jh9XD'4E99* +(2$Jk38T39PYF@Q*bJC!!T,+cVl#i`FA#[E@TRTQCSD+DNSYjD'0NC&a13$`m2N0 +,89999PTKF)DCSD5UXEDl`-A&[E'QRTbISU+GN!#!HAC`C9a@8%3j1N&)6e048P0 +6A(D,P*kTVDfaZm6'`VQ`UD5ISkLNPSU"HA4[EQjK5MXj16j+8P*58%j@BR#$Njb +LTkfi`XE)`lDUSk+QUkZNQBTpGR4eG@aE5Mmk1N&*68p16P*CCA1#NCHBRl#raXR +*`E@TSU1QUDQQR)k"H(9fF@4A683p2dG-6%P&48PBEAU$MC59QULi`m[-`E5XTD' +RVkUEN!#0LB&mHR9T9N3p2N"&69&,4%*+@'GdJSU0N!#FX,r(b-+mYUbSU+LXVD+ +8Khk!KB"aC9C'3%"$5%Y)4N9#4PCYHAk%KSfFVV[)cmHjXDURUDfZUD#4L)Z*IRP +dCe9'3N9+6NT%26Sr6&eYGhk%KBUAU,V(bmLrYl1cXUUKRCbAMik8PBCY@8a$3%C +28dj(3d%q3P"MG(k!Jj+NXEZq[EZhYVQiYVLbSSq(Lj1@NSGiCP4*38"'5dT*4Mm +r6PpMBfYeJ*+SZ-(%[l@ZXE@j[EZ`STD4Miq1LS0jDeP058Y-4$Xk2N**8PeNC@G +[HBHFYXE'[lHcYVLfXV#VSjbBPjQ9Mi0Z@e058P&15%!l1$P%8PaJC@PVHC'LX,Q +jZ,ZkZ,Zr[E@QPT!!PCfLR)jqEf4D8Ne16%Br2$a#5e*A@9KGE(q5T,'j[,UcX,I +#`lQ`UU'ER*bDQC!!I@aPBPa@9%`r1$Fl3dY4@&eHB'4bLU1aZ,UiYlh![EbkVk+ +ERDHUSjU1H@KLAepJ@8Y!1cFf2dY589*DBQerNCZMVV1bY,r*bm5fTTbDRk1QTjk +0I(*UB9TC98Nl1$j#4NY06Nj29fCrPU1SV+qZXEl'aEqjXUDFRUHTSC5(IAGaDf9 +H8$mh16Sq59*56%G*9fTlM*bPUl#cZF,&[lLaTk@SV+fSRjH(GR"`F@aK8N8p16T +!48G(58j@A@KmN!#APjbTYm('bFR"X++LT++QV+UFLRjmGfaJ9dp)2cY$5dT(4$m +r5PadL)f5RU+MVEV$bmQpYDkMTV#VRjZAMiH"I(GZ@dBl2$j!5P013d&&5P4SIBZ +6QTqSYm6&[lfiX+fXVDq[UU#2JS'%IA4YB%e"2d&$4%9#384*89eZI)+&MTfUY-, +1bVbcVkQPUDfYUTk5MSb&H@pQ@%P%48G*5%C#1cJr6@&dIAq(NCQNXll"`F#kXDf +[XDbMRC@3!)q2MBGpD99(2MT!4da04Mip49&BB@jjKT5KVlc#`,QaUUbcZ,LaUD5 +EM)5(LS9lEf&3480%3Mik2%C,6&*HD'eZFi@HXEr,blfdX+ZRUDkbXDZJPC+3!)C +lF'0C9PG35%G$1cFi28GAC@Y[GAZ$NU@f`F2![VUcVl+aUU'FQjkIQSq"Ff9@6dj +-6%Y(3M`k2%96A'*TFAk3!*fQXVkqZ,1cZEqlXUbPQC5ERCD4LRTTB&C+5%P&36i +q3%908eGFBfYeK*ba[F#jXlHjY,1cVDLSTU5KQj@4JfjLA&TG@%T$3cdh1%"*8&K +MD@YfM*bMUl1h[F#pZlfjVD'DR+'PTU'6J(0XB9C26Np+3Mml2%9,5Ne9BA'%Mj5 +IVV@dYEQp[m#kVk@IRTqIRTbBLRCXCee56%K!28"$48G'59"A@@&hMTfTXVDhY,1 +k[lL`XE'QSD5RT*Q(I(TaC@*L@%Fm1cXj2NK,6&"6@fThJC!!SkZ[YVUm`-'jXDb +NSDUXT+#FNSClF'PM@9",3cXk389&4%9+89PQGSD9Rk@ZZ,Ui[,kiXkqXV+HLTkL +DLiCrFfjVAe0)2$Np2d&%48P-699PFhf1R+@YYE[!`,ZfXDZXVDQUVkLEMi*jGR9 +YBP4)3Mmk1Mj"4%G*8&YRFhZ%NTqVZ-,%`EkkVU#GUE5cVDHKPiPqGQYKA&T85$i +p3d3m0MY&6eYTGi1*NCULU,6%bF+jXUfZVD1LUUQHPj''HR9`Ada%4%C&3cmm3%9 +%4e4MFRk)MCDMXm$$ZlLlZ,#YUkUYV+DLQBf)KhYS@P0-5%-r389!1ce$6&9KF(U +!LTZSVlM!a-1jVUbYVl'`V+HLR*1*Ih0VBP91584%48%l16Nq5ePNEhU%Lj+HV,H +r`lkiYV@bXE#TSU'KRjU3!)CrFPa058T)3d&$36`j28P@A@4aISL5Sl@kYV@l[VL +`Vl+bVD@LSCb@Nik!E&jA6dG%3d0#3$`l2d90@fPZGiQESkLa[,qiXE5jYV5aUkH +NSU+GNS9mFfC@68a058!k1$Jm3dG0@'*[ISD-QUUdZ,LfZ,Zp[,5RSDDSSCqLRSp +qF@4C88P&4%0!2Mp!26e+@f&NFSDBT+Z[XV5fZ,HfYlLeVkDJSD5HPSq#G'YL98a +&3%%r16Nr4da,6&CMEi'@SkUbYE@eXl@kZVDaV+UXUk+BNSZ$H@YJ@e9,36Nf16j +"3dG-6eKSFRH*SDf[XVDiZVbjY,'`VUQTUkDKQSk#GfYMA94)3$dl2%"!2d*&5eG +JEB59R+@XV,'jZEHkZlLbV+USTD@KPSk(IA4VANp$2MXl2N&!3NC&5&&GDhk1QU5 +XXEDjYlDjYV5dXDqXUDDKQ)U"IhPVA9*04MSd1$e!3d9'5e&GERU$NCqUXlQl[Eb +jY,#XU+kiYkZLR*+%G@aYCeP358!m1cXm2$Y"6&PNERZ'MC@IUE1raX5jXl@cV+D +QVV5YRj1+Ih9XBPC048&&3cSj3%&!4NpEERf#LCHMUV+l[lbiYV@aVUk`XUfJQ*D +2K(Y[BeY24%%q16e$3$Y!59*GCfpmLT5HV,Hp[m#kX+QVX,5eY+fQRj@,JhGXC&j +85N4"2Mil1$Sr49"HDR9rK)ZDTkkh`-+qYkq[XDkYVUURSTL4M)&bC&C-5%9$4%3 +q16Nl3NpECR0pK)ZATE+jZEUmZE@cXl'ZUkDMSCb@NBGdB9905%4"3NG&2$Sp38G +6BQefK*1HTUbcZ,HeXl1iZE@bVD5IRTkENB&fF9p+4NK'48-p16Sq48j@A'CfK)k +9RUbk[E@[XVbrYl#ZU+HQSCZCPBarDe9-68Y(4$mq2ciq3NT5A@YbGB5EV,+aX,1 +iZE@bY,HdUU5MSU'KPS0dD9pE9%P$4%-r1MFm4e&A@&aUISZ9SkQ[ZE@ZY,UjYlD +[TD5SU+DJNiClEf&98&*346`i0ce(58G-@@9YG)#9TDQXVl#cZEfmYkkRTkQQSD' +LRBplD'"KA8j$3N%q3dC#48e48ePMG)LBSUHVVUq[XlDfYV1YUkHJTUbJMS4pG@j +P@P424$dq3%9+6&"68eGNFi@5QU'VVkkaXl5hY+UQU+DRVkbEMB9lFfpVBeG04N* +"384+8&"-89eVGi#,Q+#KT+beYlDhXkLLT+LUUD1GPSYrGh"RB&Y546p$6&"15NY +399TLFiD2NTZLSUD`YlLbU+5SUDDPTU@GNSGrHAGbC94+58T*5%Y48Np08&PQGB' +*N!#8QDDaXDfYVkfPS+@VVUQHPC'*IhamFf4E98a'5%j588e+6ePKD(4qJB@3!*Z +LUDqfYkbIRD+PU+UQS*U4LB4pH(0TA9*-6&*58%p06%pAB'YeIB'%LT5JUl1dVkL +MSk1KT+LQRjQ9MSH"Hh0R@P0499G889&368pABQadHS#(Lj1HTkfYUDHRTD+PU+@ +JQT12MSk+Ih*SAP959&CA9e436e0CB'aeGRD!MjLGT+QVUD+ISkDQTU1HQC54NSq +'IA9VB&P@@9YC99*38eKHBQG[GATrKT+JTkHNSk'JSU@QT*qEQCH5MSf-JR4RB&e +GA&TD@&069PGEBfY`FhKlKjHGRU'LSU#JT+DPSjkANT'6PC++IhGZCPpE@eaE@PK +@9ejNCQ4PEAGrLC@HSU'FQCbHS+@SSTZAPTD6Mib)JAP`D@CPB9aB9PCCA@"MCQT +XE'pjL*5ERTqFQTfJRjfGRTbDQ*@9PBq$HR4`EQeUC@"F@9GBA@&QDQYVER4mKSf +4PCUHS*qHRk'HPT'8PjLAPBq(IRGaE@PSCf4JA9aHB@0MC'CVF(CqKif4PTD8Pjk +KSCqEPj@6NT19P)k'J(PdFR"UCQ*HAQ&MBf9QCQGTE(0rL)f2NC1@QCZER*bCPC5 +9PCD9NBU$IATjGh*XCf0KB@&LC@KRCQKXF(GqJSH-MSq6QCbFQjU@NT!!NT5@P*! +!M)D"I(KeFfjSC@4NC@KSCQGRD'T`HB#%KSQ0MT'9Q*ZEQ*55Nj15Nj+0L)@$J(e +jG'eTCQ9PCfTVDQPSD@ebGhZ"KBL,MT1@PTD@P*'2NT@9Nj!!M)L%J(jpHhKbE'P +SD'PVE'TTDfe[FRKpIi#$L)f6PCD9P*'2MT!!NC'5NBf)Ji'"IRTfFQjYE'aYEQa +UDQYYFRCkIAk!JiD+MT'8P*+3!)q3!)q2Mif,LBL(KB*qI(PbE@aYEh"`EfpZE@j +bGAGkIS+%KiZ2N!#2MBf1MSk2NC!!MBU(KB1$JS&pH(9cF'p[F("`EfpaFh4hHRP +kIS+'LSf1Mik-LiZ0MBb0MBU(KiD$JAppHACdFh0cFh&aFA&bG(CiHRYpIi#$KiU +,Lib-LiZ-LiU*L)H(KiD&JS"pHAGeGA9eGACdFh0dGACiHRYpIi'%KSL)L)L*LBZ +,LiQ)KiD&K)5$Ji&qHhPiGhCfGR9fGRChGhPkHRYmIB#%KSH(L)H(KiH(L)H(KS@ +&K)5$JAppI(YlHRPjH(KhGhGiHRYlHhapIi#"Ji5&KBD'KSD'KB@%Ji5%K)1#JB" +qIAamI(YlHhTkHRTkHRYmIAeqIi#"JS+#Ji1$K)5%Ji1$JS+#JS+"JB"rIRjpIAe +mI(amIAepIAepIAjqIi#!JB'"JB'"JB'"JB'"JB'"JB#!J(prIhprIhprIhprIhp +rIhprIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J!!!,d!!!3! +"!!8!!!#J!!'!83!!!!!!&!!!!!!!!#m@9Zk,S`!!,a3!!#m9!$b!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhq!J)#!J)#!J)#!J(prIhq!J)#!J)#!J)#!J)#!J)#!J)"rIhp +rIhprIhprIhprIhprIhprIi#!J)#!J(prIi##JS&qHRf!JB&pJ)D$IRPdGAPkHAb +%L)&fEh1!KB#!M*QJR)PeER*dFR4hK)TlHhpqJ(KdIT!!Rjk6JfeXGfaNERU#J(4 +XCf9UC'b2S+HSMB')MSekI*HMXVDJQ)jlHhk$PDZcSSb#GfPRCQGqN!#,M)GiE'C +dI)1HTT4rHRpbE(0cKD'MQjQ(GAk!Gi5CU++&F'CJBP*(@QCVHRGQ@946@fjbE@K +PAe4HB8!m@'9VF@eSD'p`F)16NBL!KBGqIA4XG(q"HR&ZEQpfJ(q0QS&fIR9iIhC +mMU'[U*H+NU#JUV+VY,#CSE#HLiL(ND'IJf&99&KRFhf)KRjZ8$Xi28eHEi18PRe +NCh+)RkHia,fQM(jaFB'2R+qcT*&e@P4RE@TrNj+%FQ&-3%01D)@9PSKfD@TlMU# +bbY1kSC'$Hhk0SDfaXCq!DQYcER&jI)*iANe&1$%j9)#CPik!I)++RVE+fH(C[jf +2N!#-M*@TZVQaQ(C[F@GKER4iJAPR8N!q1$eJHi4qC9025P"IEAQ&N!#8Mi0ZA9T +THRk"J'C05da2@'&LChCS56Nc-M!lA(*kH@PRE'4NFB#(M*+%FQYMDRL&QUUSRT1 +0KAYfGAD"QU+2G@CHA&e@9faiE&Y699TJDAH4UUqEJhGeJ)f+Mk#SSj*pEQ4MCfY +rP*ZCKQjKCh9fIjDIRjf5KS"qJBbKVkqJJh0pLT5FV-("Zl1FMSGqI)QIV+bTPRC +PCQplLj1)Hh"MA&aICAZHVUqeTSZ#JS5(NkHLNB9pHA*aGi@NYDZ9F94+6&pcHi1 +(L(aVD@TVFAH%NBpqBP&39QGqNTkNSCQ0JACeJC'Zc-kiQATPBhQ3!*fTS)PjBdT +!5PT[Mk@HMATQA&jSG(k)LS0lEf"45P0ZLjbHNhpI4d4*9fq%MC'0HfaZE9jNJBq +1Mi0VDRQ#N!#HU,'iYU#(HA"XHTHQTU@DKRTeG(*YER5"KQpJC'0FCAf,S,bTKB1 +#H(GhEfT`E9P18Ne+BS1+JRGKAh4kEfKYFA1)P)&bFh4aFhq&IR0M@QGjIAb#KBH +&HA0pK)5)QUb`U*H0P+QcRSb5PC1DS*k@MApjMUHTRC!!Lj!!RDQQRUUdTCD+H'Y +M98eFEQeL@PCDF)*lHSU1KiH+MC!!M)kBVF#hTCqALiL1N!#@U+qMQjD&F@"-49T +UBeTMD@*[JRTpM)b+NjZ5I@jG9'Q(SVUhQAYfIATiIS##LiTkEQGE9&TLC@KVC'* +bGfYTEh0pMBPZBfefGh*bH)+*I@jcI(b#M*+DS*H*IRC[Eh9iGR*U@8e38e9PF(D +,PBKmGh0fIifDTUkRQC!!LAq#Q++LT**aAeY348Y26P464Mp$46Xq9@*[J(YVCQY +`GAeqIRe`C@G`HAZ"PDDYUj4eDQCJCh"bFfjTD'jdFA1$NjbEP)PiEfGLG*@STUQ +[Rj@@Mif4RVI$Yk56L)@0Rl'bT**rGR0UE(Z(NTfLR)YkE@Z!TlZ`STL(IS#'M*Q +[ZVbpXCCkD'9`KTkMPi*cD@*IBA#%NjQ@Mi0eDfPiPDQbXk@8L)')Q*qKUE5hXCk +$EQ9XGhf+MB"cCf*ND(+"M*'1JQjPC'b'QCUBNBL#JiL4T+kQSk5IQ*+,KBb@QC+ +"EPeAB'jlKSb(Hh0Q8dp26PPVGh0L99CHB@9ZIj16K(9F4NY99&aZH(Y[984"5&Y +XFhZ"JhpfEfCJ@PpqQCQ#D9C2D(ahHi1#H'jcGQYNB@0aH@pA4%0'6&0BAfPYCPe +CA@9UF(0eGfT34P4VJj5AN!#-L(acF@jRDAL!IAGS9%T5B'ajJATXBQ&RFi1'L*1 +FQSk!G@eVF)5HUU@GR*bFSkbRUEHbRiYiCQ"UFRH0STU*JhppJib3!*@QXUD2I@Y +LFAf'QUDDJA9`D'CSFBUJT+#CN!"qC9KMJ++QMi@0MAq"MTUfb,HXVUHGLh0cMD5 +TUDqiZ+LHRU@eZ+QJSkDLPSfAUkDANSb8RCUGT,E)`lblYD10LSkKZE+NSjqBN!# ++KBH8Piq"FQeJ5d0$@A0XAeYD9d8a0&&`J)q4HQ"126%qBR9eIB4rH("M@QCkL*@ +4FeP168j3@'KaFh*Q9%T08&CVKU'TNA9UFhpeFB+9R*'!GA*XCh5*R+kQKA&cFQK +XJ*HUUCH*JA9R@epeQEl%TReQAP9CFiqJQiQ%KApbBQ9qRD50F&eD9%P@DAkFQB" +lIR9TC@f"QU@9I(4lGh*rL*'GPiGkI(efHB+1QC!!E8-[,MC,BA&kIAGQ@Np$8@b +"PTqIQ(YB-LC(GC'AMi9rH&j'8h50P*@6H%mf-6eCITHHSU5CKA0dKjkjb-DlU)9 +H@S@[ZELhTj+#DeYED)+IXlblRh&58'Z,QCbKUkQ1Efk&R,(#bX[)Vi"HBAZAVEL +dU*Q)H(L%L*5XVTQ"B8Bl3eY[IBD'K(CI@QGkLjZ``EUJL(TjN!#`bGA1ZjKdDhQ +1QjD1NTQ)B$`L+6j-B'aYD&!k-MY8C'f"Q*b*GQ4+4&4UKk'QNA*UGi50N!#3!*Q +PRi4U@8p,8QQ"MSf!GA&hHh4aFS'3!)k)KSZ6NBkLcH$#Nh4fMTH1KAq'Li&[D(C +jDQYZDQT[EfPbIAaqJhaVEAKYEAejEA@,MSZBMS5MZlQaTjk@NBTlGAPlFh&hE&P +9C)+8Niq)JAejIB+(ND#UT*bIS++PRjqTYVUZQS"XFAefEhZ)J@aE5ca$@h5%KS0 +dAP96AA"lIAabA%3k0M9-E)QLVl'MM(abGB+-NBf,M(aT@%P8FTDdVj4pE'&FDAk +*PU#8Ji+-JfpcJCHbXjq2J(4eIBD8RjZ9PBakE'YdKkQkVTU*K(P`L+1bZkQ*G'T +G8Q"cJD'dUjqCMS11S*kCPB&VC'*94%YPJTZ9IR&UC9pLITbKPSYkDfPSC'0bMjq +GQ)PdEh*iKSq,L)GiCf"KD'GSG)'(Hf**3PCZKTbKQiYa@%K%5e4NHC@[XTZ'Kj@ +LVV1SR*U6JhCeH(9lM*HFM@T9@QeeG)19P)Z"JT1@I9P-C(b$Gf&GE(PiEA@0PC' +*JB1)IfPMFSD9KQPVK*U@L*LbZkkCQ*kENAeeLk@`U*LGY,Z`STLEQRpcJSq0Ih* +eKT!!M)4jHSD,NTbMUU#+LCQFNB4jGi12LS+"JiD%JB1,PBjqHRepGfTSGS'%Hh9 +`B@*[HSb8NiYlF@eL88G0BAL(MB9X@9j[HhK`F'CE@dp&89eKBPeQH'j858K9E(b +"Ji0kE@jfG@p[GAb+N!"qCeP@C(*dGB+0KB*rDQk*NC'ETULJQCHHTUHXYF'rTjZ +GPSb-M)U6RT4qGRk*NCD5NC@+GQeVEhU+P*!!Li4mHh4aKC!!ND#URj!!MSKpISH +AVEQ[RT@+IhTmJ)@,M)4fE(*YA9KIFi&hE@GIA&TNIBD(MBGmGA4ZAf"fMTD5MSQ +0NBq@QT@*HR4SAfCPDAL)MRjTB&GA@fU+RCL+K)5%MC15RDkcUD'BL(GRBh'+Q)" +NA9PFDQaUGSH1MB&eI)*[CRH3!*b8Ki+#LT52JAq&LC5DQk#GNC@XYDD8LAPTC'a +YCh4jEA9iC9YKAeYZJACPBQCMCRL%Ki@!JBL-LiL$HS5GSjL$E'4H9epQB'4VB&4 +96%0-8%P3CfpPAPj@8&aN@9T[J*'EPj+1LBZGVDHEQT!!IhYqHA&hKT+DRjD!G@j +RG)@%IhppHAPhHS*lI)f9Q*Q6J'GQJT@1MU1RRjk1Ghk6QU5i[V1NNB+)RU#-L*D +IRC*i@eaSDhqBS+1DJAH(T,'MR*f5KRj[A@9hGRZ8TCb+IR*fLiYlIB&fFhppCPp +RFibQUk#ISTqNVULFRD1@Ji1#FQemKSZ8PiGbFS',P*b6JRKXAf0XA8P4ChQ+Pj! +!Ihk'LTbZT)k"GQPUCe"2DRf$IAGjGR&hIiZEQS0YBQ0pP(pNDhKlHACcGAf'MCD +QTjD-LT'ENhacISk4MBQ-MSH8XE+INB*ZDAQ'KAaeHB53!*H3!)f)KT@LRTLCKep +9BfCNB9aEAQ"D9f9`FiQHLR*jH'&FERq%IA"ZHhaXChD'PUDFIR9qG&K4DRaaA8S +j1Mia,djYHSZ5IQpYA%"(ESGrCPPJEATdDS#NVD'1GA&qFeGAHjQ-EQPXDfTC5fU +5Q*QNNRKaB8eHLkH9G@TRC'&JBR#'NTUGN!#!H'eTITH@I(0qJACaG(@$PjfU`,q +KKR9`JTHELRk1QBf4SkLRUDkeX+'0IAamHhpdA@"jKiL1PB&aIiUCVD11MBjpFSL +@KRq,N!#@TkfHRkba[,UPPjD9LB@8P(YbI(GeKiG`DRq-NCbDLRaY@e0EC9a@C'e +bI)*rKTUJPT!!KR"C6%YCDfT@8QGcDQ0F6e&SGAqASSebBPeeQ*4lHSq6J(9hIBq +QY,'XVTYmG(1!QCQ(IiH-K(KYC@9iMBf4RT4lFAQ#K(piHBL4NBf*LBH3!+U`PRG +N@P&299TRD@CT@e9SEepFHT+5NipmFR*aGAq'IR9mG'CZHRQ$R+LUU*0iEA*hH(K +dCfGeE9PG@d9)BfaZKTU'EQGIAf"B9@&lM*1APC@ES++[a-+TQT''JR9JC(k8SkQ +KMRCLA&pXLCf@Mj54J&p*69KLEAU%I@489@"fLSk(P+'3!(CG3cG&Ah1,RT4jBPa +MBhD4Lj!!SC9lC9ePEhL)RVHpUC!!K)QEU+'9NiaqGQYJC'ekM*LBMSH%HhGiJT1 +1H@pdIRafGhk0PT'4R+QZVDHXbp#YPBk$IAeeDh&rJh0UFfpZIhpjNjpi9dj"3P" +@C(b-KhCqP*kQTjqQ`Ef*D@0A9PTDFSb&GRKiFhPkHB+)MSYrK(PIA'0XJj*qDi# +,J)D9RD@fZTq8RT5!HA4cLU#4Jj+EPj15N!#6PSU'PT!!F9a36fkBS)f*Q*@%KBU +2PT!!Mj@CN!"[9PGHAf&cKj15K)'%J)4kBQL*RS4C69455e0[Lk1TPik6PT!!Ji@ +FYm1cNhGTA%)e4fCmGfGUF@GA589HL*PpAe4%,4iU9(U,PTqUXTjqERH4SkLLQC! +!F8Nk2e&SIj'DR*4pC&TIBhHFSSTY55dS0%K56fH0NSClD@&dKBD4TD+,F&0%8QY +`F*@hUTD@Li#)JRkEShaE8NpFG)'"LUL[P*5YVkHUTDHiU)0hHR&ZH(KdIBZ'D9e +RD'YYCB+MJP42AfYfKj+PYkqMSU@ZYVQjaH2IVj!!KhYiH@TRIiYmHS1&Mj@6R+f +kVSPS9NY6BfTrQCDATkHHQjUIUDQDKhpjE&eDDR9eDQ"UG("ZD'*fPT'%Pj!!D%m +j+N*PD9K5BAH'LB'#SVkZRk+JPB"J8(#4KR9hK)KiER0mMk#IR+HRL'K69RU*JSH +,NjU5KAq)R+DVV*k0J("GAA9kEfphKBCmH(erJ)H2Q+LSNheZCf*TIiU,Nj+(KSZ +(KTHUUjf8MS0c@Nj058Y06&"CBejFE(TmFQ9LAe%k,$0"8QCTE(k(GfYdJjU`TSk +,NSKbE(U(L)*eD'PYCPjIFBb+IB'#G93i0$e1@94GDQjU@eadMU1TTk'9LA*EBAU +'Hh*mIQaKCA+"QlDdTk#BLhTjLjQGSkHUTk#CNj1CSE#mYU#0JAPqLj15LiQ*I@p +UERU'KBQ9MAZ"LSD(Mj!!NCDAMiL'JS56RkfkYkD8Mik-M)D$L)*ZAPpSDfedJ*5 +HN!"cB'*I9ejUH)D%HRb)N!#,IB'9SU1EM)Q4M)'#L)k5KfaJD@TG@fGbJ)CfEAq +,Gf9ZGh4aFA"lNjZ4Mj'CT*q@NjkKN!#1RCf6KhGeJSQ'Kif3!)Z"K)CfITL@PUD +LNB&dFRClKj5IQS"cG@j[JT!!MTLRMQeUBPTSG(KlHfpF9&KE9Q&pL)Q&Ef&QAeG +NHiU4M)"pJiL"HBqVU+#8FeT@5$P2GAafEPC(58p,4eCQCQ"54Ne99Pk!R*H2LiH +3!*''JBfZb-+RP)TqF'"NK*b5Gf4JA9GBA@k2Nfj86%%q2N&FK*5%FQj[FR9kJT5 +SRiU,MB&rHh'!Q*H-L)0mG@ebJBU3!)q-Li&hHhKhP,'QNj1,LjQBSDqRSkHZYUQ +FS*f8QCQ*KSPiG)5)NU'8JS5+J@j`KC'5PCL9Pj@0Q+UdbY+jVl5RQ)q*Nk+DM)9 +lGR4aFR@$NB0R@eYF@e05EB"lKT+(H'eSFS16QjL6NBk0MBQ+N!#!Fi10H@a[GS+ +)K)Z@LRPlI@jND@aG9@YiD@akIik@M*!!QC+*Ji+*NTQCM)bHR)4pKjLSSj@HUT9 +mHRf"J'pH99CQF'GKES"qGS@EQiKpIS54LRKmIAQ&JQpYGR4RChf5PBamFi'%AdP +BBfGZE'GZIRpZEBUQTT+"K)TZ4d*+8Q&SBQ0ZDPG*8'YlG'TTEfY20$G)89YRFRe +pF@PKE*1RQj1AQT*jCh&kFAD&MC@4JR*RG)qBPCH6KA4F4%&$3P"LE@TCA'GVJCZ +KSU+2IRP[DhZ#IBbPT)YhHS+&ND+UZF'MN!#6Jh9lI)'JXTf$I)+'JjDme1(5T)f +-JRq(JB+DR)4bDfp`DALDYXV'VD@QNhedFRq4MBQ0MC13!)+5[pR6Zk1GR)TcD@e +eH(9dGAb)J'akPj10NiZ,NS9aD'GQC@0ND@jiGQYqPCLLS)q9Q)L&LAp`DQYVEhG +kG(D)LAb#Ki#"JB@0Hf"A@PeMD@YZFhZ0QTLJS)b*L)1+KAPhG(4fFhD(PjbIRjL +GRj@9NT55G'CaG(9jFR+"KRb%QUDdZD@8MSGhC@0MB9p@6eYZCPYYJ)LETCkLTCH +!C&TA6eGSG)&mC@"hNTkTVULNPAPJ6e"86dp6A@jU8deDC@KXGR9XD@KH6dj258a +FDh*VB@9bK*LRYEf`PSGlD@amJ)5*JR&K@PpXHS5)M(jE4NpD@f0N@9TJA&YB8ep +fN!#QZXHcNB&lEQCiKB+&H'9XF'GZKU1b[XD`NB1%LSkCT++3!(PhJS&pJj@acGI +5[Tk-LS1$NjqBJ@PKC@CXHiQIXUfZUC!!IR0`HSL-GeY6A'YcH)QFYFI"am5MMSk +2N!#CNh&E@&eSFAPqKC51IiD'GA"cFA0qF&*'3djPG)#,Mj@ARV@iUD+JSD+FLhC +TBQGeISD2Li&cFS+$Hhf#Ji1(K("M@Q&iJSL&Ghb%M+#NQ*58MB*pG@YUFSQNXDq +BKBb0MjqCM*+8JQTSDQ*MC@f&NSThERZ'MD+USU@JJfpaDPP@@&edL)"ZDR9hJU' +USD+@HfGC5%*29QD%LRYdGS#(PkbaVkD2H'jU@8T6A@TpH@&15NY)9R"rJAKXC9Y +02$jBDRH+LAGYEAH$P+qhUk'@LRpbCf4YHB+0MhpZCQ0JD(arH(9[E'GJ9%Y8BQk +"JfjNC@KeMCkDMiPrI(aU@&pVEAqFSC@6NT5KTkZZUD1FPBf!G@pUF)'DSBk)MTQ +XX,#dUCkIS*q1H'eUF)5DQSf0NC1BNjQQQBH(LSGkCe91B(qGTjkGTUbVUEDqV*@ +2PCQ3!(9A8Q4iLipqGhTaCfahIhCVFRYhBdJp49"PKTD@RCU'K*DSU*D+N!#EPi" +UAQChJj1IR*4qDQjmLBZ#IifEM'jE9PYRGT1PSCH#Eh@0Rk+BP*U@KQjMFi#%NkQ +XS*Q*Eh5)NC5,JRpdC&*$5PpVHTUUQSf$FRkHXEZkU*Q+G9p08'4dKjb8IATY@Qb ++PU1NLR*RA%Fj3PKaMk#@Lj55KT+RVlDYLh*YC&KCBQjmJAaaCf"44&9eL*!!JQ4 +A9%Bk2dpNHi0hERH(L)16VE1SP(efI(PXFBLCSTL#HATdC@*hMj+(G&a28%a'9Qp +[D@YPAf9[FRZDTj@*IQGIAPKIFSD-L)9eFB'-MTLXVTL4L@jQC&pRHiZ-NC5+LT' +8RlE0cEfdU*1(J(Z#LiL&MT''JSD0PD'YUTZDQSGkFQYVDQY`J)Q&M*@DUVV'aE1 +TST@2LiD$KBL+N!#,JBH)KS4rLC'!GA0R@eG33N&4AQPVDAU,LSU3!+'TQSPmH)# +!H(CmKSQ,KRk(MB*jIBU*IRYhH(PU@eKID@j`Dh#(M)+$ND1UT*U4M)4mHS#(KBD +(J)DANB1'Pk+CLi@#H@YE99YMDA&lIB'1N!#-P*qSTD5XU**fCQCVFR4dHAGfJ(p +lIibDPiq6MAGM@P*39PKID'CSH)1"KjDJSD#PTjCpF'YZH)#%IQKAC'jRCfebGA0 +YAdir1cp&8Q&SC&PHGiL)MCbPTCq@NSPfE("qP+HPMhKVEAKlIiL3!*!!Ki"lE&P +389CQH(&F894MG(b*Qjq6KBH1Kh"EA(52PSPiEfpjKT'QZ,@LNj+4Kh9MCAf9QBP +iEfq$PTQTZ,#KQjZIS*4rHB58SCq4JAL#M)fETTU0KiD&JRYXB'"XHheaC@ClPk5 +PT*kCPCHGS*k6Jhq+Q*D(I(9pLS&mKiGjD@*QD@052Me9DfYNBfb%Pj12PTL3!)4 +kJj15JR0bKCQEKhGfIS&dDhD#KS*pK)k%DeYDC(0hF'pcGhTjISL0NT!!NkDVPAp +iHB#*N!#1M)k-LB4rJSU1M)H(JR"F8PKTHB5(LBQ)MBf+NjZMUD5FP)b!Ghk,PU+ +MR*'*KhpiISQET*D$IAGR@@"ZGi+(IhZ%LS0qKj1MVD@BNSepFAf0N!#-JAGjHR* +M@Q*XFA&N9PK84804C'eSB@&UGAYiGS@5N!#'IATkGA0jM*kGMRehI(YmHR@!KhT +aFQeK@&&IHi0cAe48@PjSGAU"K(TcGAGdEfq$QCb9LAjkIB+1PT!!N!#5L)5(KRT +ZFAb(MBQ"HA0fISqKRC@BQTUBPT11MT+AQCbJQ)f(L*UJKhQ"JAPiH(9fGhClKBU +,KhYfJT+5J(Z$JS50NTUKPiH(PCbIRBq(LSf!E'T`E'9MD(0hC9PKEAGqJ(YlKiY +rFhU!GfpcHi1*JRKkKC'CR*@+J(PcER9rFf4SE@eXC'0[I)5$J(KTBf9SDhk8NB@ +)L)'!IRTpLjQJUUH1HA4bGSbKP(ppH'YRCQ*VJC'CS*Q$GRGiHj+ZUjL3!)Q'M)Q +(MCDMXVL`RBPmFh+&S*TqF@GJC'KYHSqIS+#BL(jcEhL4Uk'#Gh9fIAjpLCLDP*1 +4KhPN9PakNS*I6da-6dp@E(eqGhKlGQjL@@U*PS*RB@*QE@abLCL8Lik3!)U"F@T +fLT1!BP048e98BRf#H(*[E@PJ9&0SK)eqD@*LAf4`IC!!QjQ3!)Z1M)*iH)@5Qj0 +lF'jVE@pfLTUCNC'5Mib)L)kGVUD2LBZ)Ki@'NCfMSCUDQC!!KS5*MSq$F@a[FA4 +bG)@9Pj'4PC+-IhKrKSb'G(#!LSCqIiU1Mik0Mj'*HAL"IRKYAPpZG'TLCQjiJAp +pKSKhEhf*KAaaC@Z!KRaiJBU,N!#@PCD4IR@&NiGhDPGACfaTEA4kIS1,LiGqFA5 +2Tk+5KhPaGRTpJS1%M*HGQBepGB#BU+19LACTE(0lJAalKj1DP)0cEhqATD5CLhT +XDA*rLBD&MCQIQSepG)'BTk@BJfC269PXHhf"Lj!!MiTqF'YeLTZHPi9XAf0[HiD +)K)L0M)ClEQGYHiL,JQp826T,A'GUDQeZFA0`DfKXHiL(IA0P@Pp[I)D*Ki+#KiD +%KSH,P*H-I'pF4dKHFRjrH'pVE'TREAL"Liq%G@pP8eGbKj!!Mi9pHRejF(Q-PTq +KNS*rG&jGFB#*LB&mH(9ZD(Q1PTbJQ)Z)JR0eLTH@N!#2N!#2LiD'PULVT*k5Ki* +fD('#KB@(Kif1Ki+#LjL6LSZ%Hhq$JB@3!*!!Ki&qJ)*qIB')Mj'-JhK[F(9hHAe +eE'PSDR"cGAZ$KS0rGfPKDhZ%LSb)IhCcFR0hI)#*NT@4JfaGB'piHRprGQaRDA* +rKiD+NC+2KR0QEAf$KSZ+L)0rJSH0Nj'9RD#LRSjrKC'1LBf-JRGaFhU$L)+"KSD +'KAYhJj16Mik)J(ajHi13!*D5P*D1LSTpFRf+K(k!I(4[D@TfKSq2LB"eFR0YDRb +1N!#1MB9pHA*YG)#%JRjeEfeVCQ9bIhPaF'jVCPYBDATqIRPYC'"C99eaIhadFR9 +jG@p`HS1'KS5!J(ecFhq-Mi9hF(*dF'a[Ghk#JAadE'4IDS'3!*5+I(CiHRKlKBb +0M)U*KAjfFS+FUDD@JRKiFh0rKiZ3!*+4NBf"Fh'#Q+5QQiU$IhPlKSq6P*@AQ*1 +$GRH%PU@PNAefFR4rMT53!*!!Mib0KhYbG)'2QTb2JATcFi',L)5$HhTqHA"YG)+ +-P*H+HA&UE(f-N!#1KhekHh9[EA"jKBf-K(KaEh&rLSD$JRYhGh*[FR0jIhpqH@P +IAQ9kK(plGh&ZE@Y[GA9iIRaqIR4bH)54P)f(KB0rJ)#$LBQ0P*!!MBU!IB')NjD ++K)9mFhL"M*D9MiZ"I(aiHB+-PTL2LBH"IB##K)L'Ji9qFfpZFAL%N!#5M)b1JhC +hI)#)LiU-KhaiGhU!K)L&IAq#HA4eF@pdG(4kH("`GRYpIS5$I(f"I(PiFQpcG(9 +jEfCZGhq%K)H&HhZ$K)H0LB5&JRjkDf0YHSD0LiL$G@jaGRYqHRQ"KB&pEf4YHiH +2MSf-J(CiIS'$KBD,LiH"F'GcIBH8Nj!!MiGqJBH1NBb,NT52KhP[G(U%MSb*L)& +mJSb5N!#-M*!!MiCpG'pkJSD3!*!!M)KmGS#0NT++Ji5"Gh0bFAU"K)L$HA*SDhU +'MSk'JS9rE@KVEAL&LSb,KAT[F(Z(MSq+KB&kE@ecGS'+Lif*IR*UF(f*NC10LSP +pDQ*JA'CdHRelG@pUEAL#KS5"IRPaE'pcG)'0Li9pFfj[G)#,MBU'J(jmGhPpIB5 +2MSCpHS#$K)kES*b9M)D!HRb"JBH1KA9UCfjeI)L@Qj5-JhjrIRerISH3!)9hF(* +qL)Z8RTqBMS@$JAf"KiD,Mi*aC@0XGAb'Mj!!L(pjGRChI)&qJB9jDfCQF(q)MBq +,JhekHhajIiU*LSf!F'TTF(b#L)q4Li1!JAjpJiCpHRaZB@4SFB+0MSf,Ki1"J)# +"Ji4rIRjbD@PSEAb"JB1$JB#"Ji5&L)Q%JS*jFA0hJBk8PC53!)k3!*'2N!#4M)@ +"IhYbE'ebHiD,M)b+LSf0MT'4MSk*JAY`CQK[HB1+NT50LBQ+M)k-KB*rI(KYAea +LDR"iIRjrJB"rJB&pH(GhGRPcDQYaGAZ!JiD)Lib0Nj@3!)Q&IRTpHR&[Eh"cFQp +[GRq$L)f,KAacEQe[EfeaFh0iHhCjJSH+MT5ANSKqGR0eGACkHhPpI(9fIB5-PCq +NS*D-KS@&JS1*LSH+LSD(NCQFSUHTST5(JAafFR0iHhPlI(GiIi1'MC5DPiPrHRG +fGA9iHRYqJi@'M*HHRjqIQif"I(GbF("`F'jZFR*[FRb&LSb0M)0fF'j`G(9dFA" +aG(4bGi@5PjQCPBYrH(0`FA0bF'jZEh"[FAk*Mj'5N!#(H'jVEA&hH(CfHAamHhk +(NjQCQTH-J(KdFh4fFh&aFh9dFhD!LSq3!*19N!#&IRTjHhppHRKjIAq!KT!!QCf +FQC5,J(TeF("cGAGkHRYlHi'*Mj+6NSk(IRPiGRGkHRPkHhalHB14PjZGQ*5,I(4 +`DfYYDQPYF(*bFAL'M)k2LS4rHAGhGRKkH(CeFR4iHAq-NjHDPT!!L(jkI(KdGA0 +`F("cGhKrLSq5Niq)J(GeH(4aFh0bFR"bG(4kK)Z4PC'-L(jhGhCeHhpmHRTrJAq +$Lj'@QjZCPBb&KB1"KSL&Ji5(L)H,NjQHRjUANiCkH(CfHhekHAf&Ki&qKBU0MBb +0M)*jH(KjI(YiHS'*LiH+NC@9Nik1N!#'H(0bFh0aE'acHRemIB1&JS"pIS&kFR9 +kHhYhF'j`GATlJBU-LiU(KiGqGA9fGh9[D@T[FRCjI)+&KB@#J(jfFR9jHhalGhC +fH(apIi@(KSH(KS*mGhGfGACdFA&aFRKpIS##KBQ,LSL$J)+"J)#"IhelIS@*L)U +-M)k1LiD!IRjpHhYlHRYqJSH+LBQ)KSD(L)H%JB+"Ii#"IherJiQ1Miq3!)q1M)L +$IRPfG(*bG(9dG(ClIRerJB'$KB1#IhalHhTlIAelH(GlJB5&KSH,MBU&J(TiGhC +fHAepI(TmJ)'#KBH+LiL'JRYfG(*cGhYkGh9iI(q#K)H,M)U)JhahGA0cGAKkHRY +qJB'$L)f4NC!!N!#2Li@#Ii##KBH'KSU-Lif3!*!!NC'2MSb&IRThGRGjI(elIB' +#Ji@'KSD&KSH#IATiH(KkIAprJiH(KiU+LBH$Ji0pG("[ER"cGA4cGAPmIAq"Ji5 +"IhppHAGeFR*hI(YjHhq#KBL+MBk-LB9rH(0bFR0hHhYjHAYpJ)1%KSD%Ji1"I(P +iH(PmIRjqJB1$JSD+MSk-LSH$IAKeG(9iHRPjHhk!JiL,MBq3!)k+KS+!J(jlI(q +"JB1'KiL,M)Z,LBD$IhThGhCfH(TjH(f#K)5(LBQ*Ki5#J(akHhYjHRYiGRZ#KBD +(LBQ)KAjlHA9cFh0cGACcF(4lIAk"K)L,L)*rIATiH(PjHhjkH(f$KSD(LSf2M)D +"IAKeFh*bGRTjHAk$KB1$KBL+L)@%JhplHRPiGhGhHAk$Ji'"Ji@%JS'!J(eiGA4 +bF(*bG(Z%KiH(LBZ-LiQ)LBQ)Ki@$JB'!J)@0N!#3!)k0M)Q&Ji'"JS'"JAjlHRK +kIiD+LiZ+LBL&J(epIRjqIRapIhemJBL-MBb,LSH#I(KhH(KiH(KhGR4cGRarJB+ +#K)1"IRTjHAGfGhKjHhYlIi@)L)L*LiU'J(akH(4`F(&cGA9hIB'#JS'#Ji5$JB" +qHhKhGR9fHAYqJiH'K)5%Ji+!IhjqIAKeFh0dGRPpJiQ,LSL*LiU)L)Q*L)D#IRY +lIS'$KBQ,LiU*L)H&KB5$Ji&rIAamIRq#KBL*Ki@&Ki@!I(aqIAakHAKjI(apJSH +)KS1$K)0qHAGfGR9cFA"bGRGfH(erIheqJS1"IhemHhPhGhKjI(q"Ji@%JS'$KiD +"IRajGR*[F(0fHReqIi'"IRk!K)D%Ji+!IAPeG(4fHRjrJ)+#J)"rJ)'!J)"qHRC +dFh0eHRq$KBD&KB5%K)D&KBD'K)5$JS+$KBL*LSb,LSQ*LSU*L)H$JApmHRTmIi1 +'KB1$K)5$K)5$Ji0rIAepI(eqJB5'KSD'KiH'K)1"J(plH(CeGAKjHhq"JB"rIhq +"Ji5%K)0rHhTjHATmJ)1&KB1#JS'!J(prJ(pmHAGdFR0eH(f!J)#!JB#!IhjqIi" +qI(TjH(GhH(YpIRq"JS+#IhemIAemHhTjH(GhH(YqJ)'%KB@&KB5$JB#!J)#"JB' +#JS1$JS+%KSH)KiD$JB"rIRemHhYpIRq"JB#"JS1$JS+!IRepHhTkI(k!JB+$JRp +qIRprIRepHhYlHRKfGRGjHhf!J(jqIhpqI(erIRjqIRemI(aqIi'%K)+"JS1#J(j +qIAamI(YkHAPkHharIhjrJB+"IhprIRjpIAjqIhq"JS+$Ji1%KBD'K)1$JRpqIRj +qIAerJ)'#JS'#K)@&Ji1$Ji+#JS+#Ji5(LBQ+LBH'KB5$Ji+$Ji+"JB&rIAapIi# +#Ji+$KB@%JAprIhprIhpqIRjqJ)'$Ji5&KS@$J(emIAalI(eqIRemI(amHhYqJB1 +$JAjmHhTjHATmIRjrJ)'"J(jrJB'"JB#!JAppI(TkI(apIi"rIAapIRprIRjrJ(p +pIAalHhYpIi'#J)#!JB&rIAk!JB&rIRalHRTmIi'#JS#!J(pqI(apIi'#Ji+"J)# +!JB+%KB@&K)1!IRepIRq"JS'!IhjrJ)#"JB'#Ji1"IRamI(epIAjrIhjqIhprIhp +rJ)'!IRalI(epI(amI(aqJ)#!IhjpIAjpI(amI(eqIRprIhq!Ji1$JS'"JS'!IRe +pIi#!J)"rIAapIi#!J(jpIRjmHhYmIS'#JS+#JAq!JS1%KBD'KS@#IhepIi'#JS1 +#J(jqJ)'#Ji5%K)5"IhepIi+%KBD(Ki@%Ji1$K)5%KSD$JApqIS#$K)5%K)1#JS+ +$Ji1$Ji1"IhemI(eqIi#"JB#!J)#!JB#!JS+!IRemI(eqIRjqIhjpIAjqIRemI(Y +jH(GhH(TmIRjqIRjqIRjqIRjqIRelHRPjHATmIRjqIAepIAjqIRjqIRjlHRTlIAj +rJ)"rIi#!J)#"JB'"J)"qIAamIS#"JB'!Ii#"JS+%KB@%Ji&qIAeqIi+%KB@%JS' +#Ji1$Ji1$JS&rIAeqJ)'#Ji1$JS'"JS+$Ji1%Ji&qI(YlI(erJ)#!J(prJ)#!Ihq +!IhjpI(YlHhapIRjrIhprIhpqIRjrIhppI(alHhapIi#"Ji1%K)1#J)#!J(prIRj +qIRepIAepIi#"JB'!IhjrIhpqIRjqIRq!J)#!JB+$Ji1#JB'!J)"rIRjrJ)'"J(p +rJ)'#JS1$Ji+"JB"rIhjqIS##JS'"JB'#JS'"JB'"JB"rIRepIRq!J)'"J)#!J)# +!J)#!JB'!IRepIAjrJ)#!JB'#Ji1$JS+"JB'"JB#!J)#"J)"rIhq!JB#!J)"rIhp +qIRjqIRjqIRjqIRq!JB'!IhpqIRjpIAeqIRpqIRjqIAjrJ)'"J)"rIRjqIRjqIi" +rIhprIRq!JB'"JB+"J(prIhprIi#!J(prIReqJ)#!J)'"J(pqIRepIRprJ)'"J)# +!J)#!J)'#JS'"J(prIRq!JB+$JS'!J)#!Ii#"JB+#JB"rIRjqIi#"JB'"JB'!Ihp +rJ)#"JB"rIRemI(epIAjqIhjpIAepIAepIRq!IhprIhpqIRq!JS+"JB'"J(pqIRq +!J)#!J)#!IhjqIRq!J)'!J)"rIhprIi#!J)#!IhprJ(q!JB'"JB#!J)#!J)#"JB' +"J(prIhprJ)'"JB'!IhjqIhq!JB+#JB#!Ihq!J)#"JB'"J)"rIhprJ)#"JB'!J)" +rJ)#!J)#!IhprIhprJ)#!J)#!JB'"JB'"J)#!J)#!JB'"J)#!IhjqIhprIhq!J(p +rIhprIhq!IhprIhjqIi#!J)#!IhpqIRprJ)#!J)#!IhjqIRq!J)#!J(prIRprIi# +"JB#!J(prIi#"JB'"JB#!J)#!J)#"JB'"JB"rIhq!J)#!JB#!J)"rIhq!J)#!J)# +!J)#!J)#!J(prIhq!J)#!J)#"JB#!J)#!JB#!J(prIhprIhprIhprIhprIhq!J(p +rIhjqIRjqIRjqIhprIRjpIAjqIRprIRjqIRjqIhjqIhprIhjqIRjrIi#!IhprIhp +rIi#!J)#!J(prIhjrIhq!J(prJ)#!J)#!J)#!J)#!J)#!JB'!J)#!J)#!J)#!J)# +!J)#!J)#!JB#!J)#!J)#"JB'"JB#!J(prIi#!J)#!J(prIhq!J)#!J)#!J)#!J)# +!J)#!J(prIi#!J)#!J)#!J)#!J)'"JB#!J(prIi#!J)"rIhprIhprIi#!J)#!Ihp +rIi#!J(prIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIi#!J(p +rIhprIhq!J)#!J)"rIhprJ)#!J(prIhprIi#!J)#!J(prIhprJ)#!J(prIhprIhq +!J)#!J(prIhq!J)#!J)"rIhq!J)#!J)#!J)#!J)#!J)#!J)"rIi#!J)#!J)#!J)# +!J)#!J)"rIhprIhprIhprIhprIhprIhprIhprIhprIhprJ(prIhprIhprJ)#!J)# +!IhprIhprIhprIhprIhprIi#!J)#!IhprIhprIhq!J)#!J)#!J)#!J)#!J(prIhp +rIhprIi#!J)#!J)#!J)#!J)#!J)"rIhprJ)#!J)#!J)#!J)#!J)#!J)#!J(prIi# +!J)#!J)#!J)#!J(q!J)#!J)#!J)#!J)#!J)#!J)#!J(prIhprIhprIi#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J!!!NLS!!3!"!!8!!!#J!!'!83!!!!!!&!! +!!!!!!*)!9Zk,S`!!NIi!!*(r!$b!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhjqIAamHhTjH(GfG(0aF'pZE'YUD'CNBf& +HA9YC9e988e&36Na+5%C&4%*!2cip2$`m1cSk16Nj16Sk1cXl1cXl2$dr3%""3N0 +%48C(5%P,6%e16e&699GC@PaHB'0PD'YYEh"bG(CiHRaqJ)+$KBL,MBk1N!#6PTH +APTLFSUDRTU5PUDkaVkURU+keZlZhXDkbZm2'`lZcXEE!bp28cF#aTD1V[-hCfY( +!V*U0KSbHZp[amr$Ud+f9NTDAPjD8MiZ(KSqSb0[FdXDmXULIPSq)JAafF@aRB9a +A8Na(3Mik0c8d06Bj28&(6PGKDhH$N!#FUE6!bY6FjHc[lr$`lqlZlZhZlHlYl1h +YlZlZlqr`m1rYkZINhp[Ae026eGEBfYcGiHEXmI6dm[$YkZMSkHVXl[$bp2AcmHr +[lZlXk1,Gf0E6d-c*aX2![VZkZ,HeYE5dY,@eYEDfYVDeY,1aVUZSTD+HQTD5MSQ +&J(aiGhGfGA9eGA9eGA9eGA4dFh*bFA"`EfpZEQeYE'aVDQPSCfCPBf*JAeeF@eT +C9e9689&48%j*3d!q1cNi0cFh0cFh0cFh0cBf06-`,#JP)KmG'aSD'aXE'4F@&KJ +E(L%L)4mH("`E("iK*#FT+LSV,6!c0MJj1MSl28"$4NK+5NP(4%*#3d9)5Na06%T +&2MF`+5-H'aSF)#BX-MJm26dp2N"$48K,6P*@@f&QE'pbFh9fGhKiHAKhGA0cG(G +mJ)5(LSZ,LiZ,LiZ,M)b,LBD#IhelI)#(MjQLUl1j[Er#aXh5d-c3fH$KhZ$Pj0I +'Z,@iYkfGNSk2MBGrI(q'MC!!N!#,JA4UCQGXFRGmJBH0NTDCRD#MU,#mc0[PkZl +`lq[NemDeUkLNQj!!LB@#IAKfFQYK9NT!1cT!5%e-4d)p0LiU+LXT*L3L(4-)!!! +!!381'LJd284(5%K*58P+68j)2M8c1$p#3Ndj69PGBA@G`G'pQA&0,48*"38& +"3$mq26dm1cSj1MXh-5SN)"`B'"JA%!F"!!3*$K8G)bBL'3m)#K8N-6Y$5e&58eC +HC'GPBfCZHSD3!*HHT+UVTk'FQCQAP*+4NjHDQTbJSkLZXl@eYV[$b-V0dYMDep2 +3cp,9e066cF5jVkQRUDbYUk@IPj!!LSD'LBb2NC5BQCD2KS"lGR"ZEh4iHRTlI(a +kGhClJSL*K(ekISL6RD@VVV#`X+qYTjk6LiL+MBq0LB5%L*'GTkkbYEQp`F,$`m6 +%a-'r[Vr&cYRPm2MqrrrrrrrrrrhflHAJh0M5cFR)bFR)amM,cp2@f0E2aVfjZEZ +q[lkkXULGP)k+L)H)LBZ0N!#5P*HDR*ZCQCZIT+DPRjL3!)Q"HhGfGRGiGhCcEfP +PC'9SDfaXDfecHS#$K)5$JAprJSD+MSq0LB@#JS5)MC!!Nj14MBU(KB5%K)1"Ihe +kG@pSC'*LC@TbHS51QD'MS*U6M)H%Ji+$KBQ0NTHERTqIRTfCP*!!M)Q)LSk8QTk +JRTL4LiD$IhTdDf&B8%K#2M`l1MSj1cj#5%e69eTGAf"IA&P86dY*5%T3@@9bI)1 +(LSU+LSQ'IR*M9%K!2$Xm1cJe-5mZ,5i[-63f16`r48a9B'YfIB"rHhGdFQjVD'9 +MBQ&KB@&KBQ0NC'4J@P49AQKTC'"IA&PDB'9PBf0NBeeA8P9C@P0*3Mp"5PKQEh" +UB&C9BR@!Ji*rIRamHhTkHACcEfaTCQ0KAPYB9P014cmh-#`V,M%d0MFi16`r3dG +-6e09@&YKDA&fGhC`CPaEB'4KAPYA99499PGB@&PEB'KcIB5&Ji1+P*fKSU@SUkZ +UU+1CM)"fFR*hIi@,N!#6PCD@PC55N!#2MSk1Mj!!Mik,Ki0qH(4`EQe[G(f*PU' +SUkQPSTkEQCQCQjbFQjQ@P*58PC@@PTD9PC56NC!!MSb,LiZ-MBk2Mj!!NC+6NBb +$H'aNB'"JB@4RE(0qLjURX,5dXDkYVUq[VUbTTkDPTUHUV+k`X+q[VkkUTCk@Mif +9S+LSSk#JSkHVVUkUSC1&IS+2RDHTSjQ-Ih4YC9jA9&CFC'PZFRGpJ)&rIAYkI(q +$L)k9RDHb[XVBjHh`l12@bEkfXDqZX,1fZEHbUD'EQCQDPSf"GQpXD@TXER"[E'G +KAPjLDR0mJiL+LB@"IAf$MjUHQT53!)k3!*'3!)Z'JRpmHA9aD9p@8P*9@f0`JC! +!QjkBMAjbE'aZF'pVC&jD@9PGC'jkK)U-M)Z+L)L,N!#8P*'1LB@#JS+"IRamI(e +pIS''Lj+BR*kGQjH4M)L*M)q1LiL$IRTlIi5'K)&qHhGaD@0HA9eHA9aB9%p06P* +BAQ*MB&aB9909A'4RBeY@9PTKDR0mK)Q+Ki0qHA0YE("dGR9dGRTrJB'!JS5%J(a +jGA*`FA9lJBH,MBb)JRYdEQTTDfjZF(H$MT16PTfNUUbXV+qfZVQfZ,l#`,bk[,f +fU*U2KhpjH(YmH'pRCQYdIS5"H(*dHATjHi#%JhefEQCF8%G%5P"2580$4NT-69" +@AQ4SDQjeISH5R+@TV+kZVDQLPif*LSZ'IACaDf9LA94+4%9+6Nj*36j$5NY$16F +r58j+4dY@BQTYFACjGQpP@8`q-#3F&aJF)5BT,6-l4%a16Ne39ejLBQ0PD'PTD@T +YFA9lJ)@+M)k2Miq2N!#6PT@3!)Z2Q++SUUZYXEHlZE1`YX64eY2*ZkqQSk1QUUb +XUU@JQjQBQ*QCPjD6NBq2Mj!!N!#3!)q1MSq5PTUGRCbHT+QUUD@LRjZBPC56Nj+ +3!)k-LSL'K)1!IATiGR4aDPj44$dj-bSK'aSG)L8P*#3P+#SR(aB6&KXH(K`D'K` +H)53T,cBl2$Sm38G)48!q26`f,5BQ,$%f1d&'5e*FCQeaFh4fI)51PjkJRCD2Ki" +jGR9cE@0D999CB'PdJ)Q1MBZ-N!#4MSD!Ii&rHR4`FACrLT5CQTUDQCH@PC54MSZ +*L)U0MBZ)KB+!IhjqJ)+$JhplGh9eGRGjHRYjG'pXE'jaFR"TAeC6@Q9ZG(KkGfp +SC@KYF("VC&pIC'PYEh"eIB@,MT'9R+5TUUHKQjZJU+fYUD1HQjQBQCQBPT'+Jha +fFR&bFh0bF'j[FR9kIi@-P*kUZFA,bX5qZlUiY,#ZVl'`V++9LS5'MCDISk5QV,+ +cVU+9LiL*M)k4N!#0LSU2Pk'TVDZRSk1QUUbVT*U0JRGZCf9XHikHUDfVSjQ3!)k +3!*+8NBb&IRYlIRppHAChH(CYB9P@@&YHAeaEA@4[H(k"Ji5$JRpkG(*fIiU6PT5 +3!)k2NjHCQTbHRjfFQjbGRCbDPT!!KhecE'CNC'CVFA9hGhGiHRk#KSZ4PTUFRD' +TYF((aVfaTU'LTkfcZEh"aFM+bXM'aFE%`EfmZVHeYVh&bm['`X2'aF'q`FI+b-+ +lXUfZXlDdVkbZY,h$aXE'aFE*cYIJj1,FeXl'[VLbVUbUUUfcYV5XTUDUX,Dl[lk +lYlDfYE5bX,#bYEHhYlHeVU@GQCLBNB4fE@KSDh*mL*1ES+#FPC!!N!#5P*11L)& +lHheqI(TlIi5*M)f1MBb+LBH%JAjkH(GhGR*[E@YVE'j`G(KlHhTjGh9dH(q%Jhp +iF'TRCQGQC@*IA9YE@PG98e058%a&2ce!48P+5%9#26Jd-M)b-6!b06Sr3N9'3ci +j0MBi1Ma!489"2$e'9@*XG(U!KBQ1P*LDQCQGTDfcYE@cVULLRTZFR*ZCPjZLTk5 +EN!#*KSH-NTQHRTL1KB#!JB&rHhPiHATjGhGiHhq$L*!!QD+RUDUVUU@HQ*DBR*q +MTULSTU1JRTZCPBk%I(KhH(YqJ)"mGR*dGhGcDQ"C9eKHD(*iHRYmJBU9Rk5LQBq +(KBD(KB"lGR0cGhq,Pk'TV+ZPR*'&HhCiJ)bAS+DSU+DKQjH8MiKqG@jTCQ*JB'4 +UER"aFR0eGhGfGhTrK)L+M)b,M)k6QCfGQjD4MBZ,M)q4Nj+4Mj'ARCkDNif*KS@ +'L)Z1NC16Nj@CS+HXVDZRT+1NTUHQSTfBNSf)KB*rI(PeFR"ZE'YVE'eXDfPQBPe +A88a(2cF`,#NP)L)Q,M-a+#%J*bia,LNR,$3k2d&$3d*$48G)4dC%3N*$4NY28P0 +68e0899CA9eC99&966NC!2Mdl1$8e0cSj05mX,M3l3NC*6%j389"16%a28eKE@P9 +25%0"3dP39PTFAQ&ND@jdHS#$JRaeEQKNC@TbHi+(Lik4NT'1LS9rH(*XD'CPC'4 +NCQT[GRf%LBb1MBU'JS"pHA0UB&C14d%m1MNj1$Nj0c3[,5md16e!3NC-8PC98Ne +-6%e-5NP+6%j48e*-4$Xc,bma0$8e-c!Y+LFM)L-Q+bmb06Sr4Na49&CBA&pI@e* +)3$dq4%a6@&TD@9TGB@9UF(KrKBZ8RkUcZEl&cG,8dF[%Zl5ZUD5IR*UAN!#(I(0 +[F(4jHRGdFhClJ)"qHhPlI(TdDf0G@ejQFAb)NjZGQT53!*'9Qk'QUDZVUD5FNBH +!IB#(M)b)K)1$K)5(MjQMU+HLRCZFS+'IQjH6MSL#I(YqJS4rGh"VDQa[FRCjHRY +pJifCTUqcY,1cXl@eYE5bXDkVTk5KRjfEPBb$IAapIATfGAGmJB@'K)&qI(f"L)k +6Q*bJSk1MSk+MSU#ENS9hDQ&GAQ&QD@PQB9jF@eYHC'ehJ)H,LSH"IAf#L)k3!)q +0M)b0M)Q&JAjlHhk"KBH)KS"hEQTXFRGiGhCiHhq"KBZ8RkHXVUqaYELk[,l"`m6 +%`lqkYE#XUDLUVl5j[-,+dGEAdml*aX2![lr#aXV-bmM&aXR1cml+aF'pZV[!aX[ +-b-1r[X$#`EkjYVHpaFc2cFM"ZV1VSjfEQTQANj!!Miq4NT5AQjkJS*kEQ*@6NBq +-LSH&K)*rI(PiH(TlI(eqJiZ6QCfJSCqCP*1@Qk#LTDLVVV#`VkbRSTqJSU1NTDH +UV+bUTk@PTD@NSCfCP)q)J(PeF@eSC'*KAeeE@9KA@&TGBQCTDfpeIiZBSUQYVUf +YVDfYV+QMRCL9PCDAR+1XXV+YTU'IRjkFPj+0L)5!IAakHAKiGhCfGRKkIApqHA& +UC'*LB9pGA&aE@eaIC@TUD'CSEhU'NCQFQj@1KS"pHhTlHRTiGR*ZE'YVDQKRD'T +YFRGlI(YiGA9fHAk%LSf3!*+8PjD6MSQ&JS"rIi#"JS@*MC'8PjUFRk5SVDqaXV1 +eYVDeY,1f[-60e0VIiZ,Jh0I5cFI!YUbMR*LAQ*bIS*qGQjQCR++SV+ZSTD1NTUL +XX,Dm`FA(b-R+bXR&`,bl[EfjX+@GQTbJT+1HPiq+LBQ*KS1"JS1&KB1"IheqIRj +lG'aNAeYC@9YKD'e`EfaSCQ9NC'0MC'GVE@eVDh"iJB@&JAYeFh0iIS@,Mj1AQjk +KSU1PTUHSU+DLQj+)IhPeFh&[E@aZFACmJ)+$K)L-MSf)JAPcFR4iI(q!IRPdFA" +bFh0`DQ*C8Nj,5NP)4dK,6e4CA9pIAPY@9&9FC@aYD@&F@epND'PUDfe[EfYPAPK +98Na$1MFi28C4A'0NAeK589GICfaaGRTpIRprIRTdE@PSCfCNC'CQBPK03MXh0MJ +k26p"3N%r26`m2$Xh-#NP*5FU,#i[-$!`-6-f1Mdr3N4&489&4NT39PaLCQY`G(9 +dFR&eI)1%IR9[FAGqIhTbDfKTDfYRAeC48PGJDA"cFR*dGhTmIAjrJS1#IA9XCQ4 +NBPjD9eC@9P988Np06&"AAfCTDQTXEh&aEfjXE'e`Fh9eFfjTC@*JA&9+26%S)Kd +E'KXG(amI)LXe1MNd-$)i2d0$3%"$59"9@Q&TF(*`E@jdHi#"IRPdF'j`FhKmIi' +#K)Q1N!#0Ki&kFfeSC'"G@9946Na,6%Y(36p"4NT,589#38N4*8PYLD'abHAk +"Ji1$J(elHAGfGA4bEfTPB&pIB'&KB&pG@eK99&GGC'PZFRCiHAPiH(TqJi@#HR0 +`FRCjHRf"L)b0LiQ*LSU*KS5#JiL4QU#KR*@1LSD$IRThGRKkI(jrJ)'"JB+#IhP +`CejA8eCGCfjbFR&[E'KQCfTXEQpZE@j[FA*cFh0cFh0cGA9bE@GLAPeHA9K66Na +,5dP(4N9'5%Y28eGDA&jKC'KXEQjYDQKRD'YZEfjXD@GNBQ&KBQ4PCfTYFA9iHhk +!JB'$KSQ-N!#6PCHESUU`Y,1aXVDkZVHdXl@k[X#r[El#am[1cp(5dY$-b-E)bXc +,alqhXE'jaG,GjHVXl1RMhYR9e065cmc+cG6Hk1rbm[$Vk1ISkqh`mI,alqcTjH$ +DeG,3cXl1c-M#ZUqPR*HARD@YXE'[VDUUV+k`Xl@fYE1`VDUSTU1KRjkHRk#IRCL +9PCDAPjLCQjbEQ*11LSH'KSD%J(TbDQ4IA9aE@PG888j068e2894A@ejMD@jeIiU +8Q*H8NC'6QCqQUUZST*qDPC'3!*18Nj!!MBb0MSk-LBQ-NC55MSZ+MBf+K)"rIhj +lHi+2RDHXVV'dYV1URj@3!)q1M)L'L)f6QCbDPT'0LSH'Ji"pHRPiH(GiI)#%KiQ +0NTDBPjLESDL[YEUmZV@`VDbYVDbXUkZTTkDSUUbYV+USTD5RVEM$c0$8f0cJiZ( +Ihq$Liq(JiZETjq2Gf061b-'mZVQfXDUMR*1,KB5(LSZ*KS@'KiL'JRjlHRTmIi5 +*M)b+L)H'K)&qIAk!J)+&Lj1DRk'JR*L@PjZJSk@NSk1MT+5MSD#JRjqIRjkFQC@ +5N!#2N!#3!*'5Nj59PjQDR*bDQ*@5N!#0LBD$JAjmHhTkHRYqJiU3!*@APjH@PT@ +6MiL"H("UC@"GA&eHAPjIBfTZFA0fHi##JAajHRf!Ji*rIAYmIAepHhKfGA4dG(G +lJBH0Mif'IRGbEQTTDQe`FR*`EQeXER&eHAaqJ)5)M*!!Nj@CRD#LSU'IQjH4LiD +#IhYfF'PMB@0SER0hHAaqJB5(LBZ-MBb*K(eiG(&[EQp[EfjXDfPSCfGRCQ4MBf0 +KA&G88e4@@9eJB9jB8Nj-5dY08PKFA&C258C(58P(4%0%58j599TKCfPSC@*JB&p +HA&YHB@0LAeY@88T$2MSh0MFl2d*%48C'4N9!2$Sl2Mmq1cSk1cj#4NY39PeMD@j +aG(GkIS'#JAjpIS+%JhplH(CeFQeQAPG899TJCQPUCf0JA9eHB'0RDQPRBf"G@99 +468a,5NK'3d&!2d"#48T49eYFA9eGA9eFA9jKBf4MB&jE@PPEAQ0RCf4KB@*KAPT +C@ejJAeT99&CDA&eGAQ*QD'GPC'4NC'*JAejHA9eF@eTD@eeJBQ&JAejHAPjHAQ" +MCfTZFR9fGA0dGRTpIhjpHhKiHAf"KBQ-Miq2Mik2Miq4NC'1LiL'KB@'KSD&K)+ +!IAPhGA0`E'9I9e"+4N)q1$%V+#BP*5FV0$p0@@"LB@"LCQPSBeeB9P998e089eP +C9eCA@PpMCfPXF(0fGhGeG(0cFh*`E@TTDQaZEQeVDQTXE@eXE'eYE@eVD@GNBPp +G@eTD@eYE@9G@9ePEAPpKBfCSD@PUE@paFR0dGA9bEQaXF(9kIi1&Ji&qI(YjG@p +SB&P888e*4N9(5Np8@9pND@jaFh*`EQjZF(*dH(b!Ji5$JAjlHRPjHAKhGR9eGRG +hGR9fHAf"KSZ4PjbJSU+KRTU@NSq1MBb*KB"pI(k#KBH&JRjmHhf!KBZ5QU#LS*f +BPC'1LBD%JS"mH(4bFR4hHRYmIi+&KB*pGh4bG(GlIAajGhGjHRPhGA4cF@eSC'0 +MBf*I@eG999KFB@P`Ghb!JS@(L)L(KSD*MBq2MBU*Lj!!Q*fIRTfGS++MSU'LTDZ +`Y,DfYV@cXDkXV+faYVc#aXR)aF#kY+kVUUQRSjkEQjfJSD'LT+LVVUq`XE1hZVb +mZVHcX+ZRSCZ@NT'5PCLDRU+PU+LRTD5LSD#IRk#IRCQ8Mib*KS@&KSL)L)L*LSU +*LBU+L)*kFQj[FhPrJi5$JS'#Ji@(Lj!!PCLBPT14NC+8Q*fLTUUZYEc#b-c0bmI +%`X#r[EZkZEUm[m6)bmc*aF#mZVZp`-,$a-E'`lbdVDQSUDUSSjfCQCkQVlDm[Vk +p[,UhYE1eZ,c!a-R2dpIAe-r*`VZfXl+cXl1aX+k[XVHm[Vqq[Vr$amc5f0lKi0h +CeG25dY(2bmM'aFA%`EfkZ,HhYlHiZEQjZlr'cG2@eG,-aX'r[lklYl1bY,Lm[lq +q[,bm[,ZkYl5aVUZUUDLRTD+KSD5SUkfYV+UUUkbYVDbUUDLQSjf8Li@"IhajH(T +qJiL-Mj'5Nj@BRD'NTD5JQj@2LB@"IhajH(PlI(epHhPiH(KjHRTlI(eqIRq#KSU +0Miq2Mj'5NT+5Nj15N!#-L)5!HhCcF("`FA&aFA"aFR4jIS+'LBb1N!#3!*'3!)k ++KB&pHhPiGh4aE@PQBQ"HAPjHAPjHAf&NCQ9MB&eGA9pKC'CQCQ9QCQGRCQ4KB'" +KC'KUE'aVDQPVEA"cGACfGhKkHhalHACbEfaUD'GQBf"G@ePA9949@&TE@PK@9&* +58e49994589"26Nj38eKEAPpHAPpKC@acH(apIRq"K)U4PjfJSU5PTUDPSTkEQ*H +@Nik(J(TfFh0dGhYpIRYiGA0cG(KqKBb3!)q-Ki+!IhjrJB1&KiD$J(akHRarJB+ +#JS+#JS"mGh&VC@"F@9K@99499eTFA9YA8e&48e056de1894A@9YGAf&KAeeF@eT +C9P036%P(4dK,6e0@9eC68%e06e*@9e9468T+69&89&",4d0!26Sh06-a,bdX,#d +a0$Fi0cFh16e#4dT-6%e39PjQER9lIi&qGh"UCfGTDQKPB9jGAf0SER4iHRTjH(K +kHhalHRKeF@aSC'*KB&pHAPpJAejE9Np*4dP089488e*588p069"@@PeE@&0368T +(484$38!r2Mip2$Xm2N"#4%9'4dP,5dT*5%K*58G$2$8Y*L%I(amI(4N@%a)3$J` ++#3J)#!N,$!m5&adN+c%e0cJk1cj"48Y5@9pNCQCMB&aC@&PD@9C35dK)58e49ea +JB@&KBQCUEh0iI(q!IhajHATpIi#"JB'"JB1&L)Z0MSf-LiU+Ki*mGA&`FhGmJ)+ +%KSU2PCUHSUHXXE@iZELhY,'ZUkQRT+'GQCLBQjkIRjfBNik+Ki@$JB"rIi#!J(p +pI(amI(amI(alHACeH(k'MT5BQjfIS++NTULTUDLRTkHPSTkFR*kKT+HTUUQTU+D +MRTH4M)L&JAekH(GhH(TmIi1'KiH'K)+"JB+"J(jmHRKfFfpXDfa[Fh9eFh&aFA0 +fHRq&Lik2MSk1Miq2Mif-LSH#IAKeFh0cG(CjHhamHRTlIAq!J(pmHRGeG(9hHi' +(MC+8PC56Nj18PCD@PT@8PCHCQTQAPC@@PTD8NT'4NC+8PjQCQ*D9P*15Mib*KiD +'KSH(L)L,N!#ASDUcZVr#`m'q[,UiYV+ZUD5HPj+2Mj'9Q*UER*qLT+DRTU@LRjk +ISkHXXEDjZVQfXUkVUUHNS*fDQTZEQjUBPT56NT'3!)q1MSb,LBL(L)Q-NC@CQCH +9Nj19PjUEQjL6Mif-MSq2N!#2MBU'Ji1%KSL*LBQ(KiH(L)Q)KS@&KSH)L)H&Ji& +rIRk!JB+#JS'!J)#"J(prJ)1(LSf0MT!!Nj@AQ*LCQjkMU+faY,DhZ,HfYE5cXV+ +bXDkTSTU5M)H%JApqJ)+'LBZ-MBk1Mj!!NC5AQTbEQTH@PC@9PCDAPjHAQ*QERU# +MTDDSU+LSTU@MSCqHRCfHS++NT+5NTDDTUUZVUkbXUULNSCkGR*bISU5PSk#GQjb +ISkHTTk5IQjQCQTbISD1NSk#EPT'3!*5DS+@SUDQTUl#i`-I,cFr2cmh*aF,"`F' +rZlDbVUbTTD'GQTL8N!#0LSQ*LSb2NT15Mib*L)U0N!#8PTLBQ*H9PCDCR*fGRCf +ISD1PTUHTUkf`XV5eYE@bVUUNRjZAPC56NT+8PjZGR*UAPC56Nj5@Qk#NTkLSU+L +STkHRTU5IQC11LSQ*Lif2Miq2Mj'9QCqNU+bZVUUPRTQ9Nj+4NC+8PT@8Nj5BRU1 +RUkk`XE'bY,Ll[EfmZlZlZlZlZVLdXE#[VkfTT*f@N!#0M)f0MSf-LSQ(KiL(KB& +lGR*[E@aXE'eYE@j[FA0cG(9hHhq#K)5#IhaiFfaPAPPA9PC88e"05NG&4NG+6P& +99eKC@9KB@&PD@PPB9PCB@9PC@9TGB@0NBf&JB@*NC'0KAPYC9e956de-6%a-68e +28%p15dT+5da05dK%36im1cNi0cBe06Bj1c`m1cXm2N&&5%Y-68e16e"69eaJBf4 +MBQ*MC'9PC'0LB@"HA&P@8e"26P"599KEA9jIAepIAPY@8%T'3d*#3N0%489&4%* +"389,8PPIC'KVER&eHAf!JS&qHhGfGRChGhGhGhCeFh&[E@aVDQTXER"ZDQ4H@PK +B@PYE@PG568K&4%C*69*@@&G66NT)5Nj69eKA9&&3890A@f"NCfGPBQ"IAepIB'& +JA9K3580!3%*&4dG&3N"!38*&5%e6@&YE9e*15dT,5dY+58G&4%0&5%e48e068P" +15dP(4dP-8&0999469&9@@&KC@9K@8Ne*4N9&4NG)58P)58T089088e&168j49&G +A9eGA@&PDA9pKBQ0MC'4MB9pG@eK988j,5NP*5Ne39&GDA&eKCQaaFh4eGhTmHhP +dEfYTD'PUE'paFh4dG(CjIB'$KSL+LSQ'K)1$JS"qHhPhGA4dGRKlIRprIRYiGR9 +fGA4cFh4hHReqIhprJB1'LSf0M)U)KiH(L)Q*LSU*L)H'KB@&K)1"IRTfFh*cGAG +iGh9bFA&cGhb!K)D(KB5$K)D*M)q4P*@9P*!!MBZ*L)D%Ji5'LBb1NC19PjQEQjU +BPjHAPjH@P*'1LiL'KB1!IAPeF'aUDQYXEQjYE'YUDfe`FhCjIS1+NTUKTULQSjq +EQTQDR*kHRCQ8MBCrHhYpJ)1%K)1%K)@&Ji+!J)#"JS1#JAplH(CiI)##JS"mGR& +`FA0fI)+)Lib-LB0mGh4`E'YZGAf&M*+8NSf(JhpmHhk"K)D)M*5ERjf@LRpjHAf +"Ji1"Ii##KBL-N!#9Q*UDQTQBQTkNUUqeZVl![VZhXl'[Vl#aXE+bXl5fZ,c"amh +4dp,1aVkfXDkXUkUTUUkcYlLdV+1DNiq0MC!!P*UKTDQUUDDKR*H9P*@BRkLbZm$ +"[VZjZ,Um[EfmZlbp[m$"`XA*c-[(`,DYTk1LSD'KSD'KRjbBNiq+KS1$KBL0P*Z +NUl'f[-$%aXI(aXA$`F$"a-I+cFr3dG(3cml-bFM*bml4dpA@eG(*[lD[UkHMRjb +DQjqNUDbXUkQQSk#GQTQES+DYXlLq`mR3eGRCeY(-bFM)b-I'amV0d0$1bF+lYE# +YV+f[XE+cXV'ZV+ZYX,@iZVZm[EqrZl@ZU+@PTUHPSTfBNiq*JhjlIB''Lj!!NT5 +9PC13!)Z'JAajGR4fHS')MT'5NBk-LSQ*L)L)L)Q,MBq3!)q0L)*kFfjUD'CNBf* +MC@KUEA"bFh0`E@aXER&eHAf!JS1#J(ejGR9fHRq&LSf2N!#4NC!!Mj!!NTDERU# +IRTkIS*qFPSk'IRKeFh4fH(PiGA&ZER"cGA9dFR*bG(GlIAjpIAerJSD*LSU,M)q +5PCQFRTqHRCbFRCqMTkZXUkUSU+LSTU1HQC@5NC+8PC53!)Z'Ji'"JB'"J)"rJB5 +)Liq6PTQER*bCPT+1M)b0MBf,LBL*Lik4Nj58P*@AQCZEQCH8Nj'3!)f+Ki5"IhY +hF@aSC'&HA&K66NY*58K)4dG(5%P+58G&3Mmq2$Nf-c%b0$Bh1$Nm2N$mq2Mi +p2$Si0cFi1$Fe0$8i1MXl2$`m2d4+8&*588p26e"38%p05dT*58Y28P068Np,5%9 +#38""3N4&4NG'4%%r26j!4%K08PCCA'&QEA&cFR&aFR0cEfTPB@"JBQ4QD@YZF(0 +eGhPjH(GeFh0cGA9eFh"[EQpaFR*bFA&aFA"[F(&bG(4cFh0cFh0aF'pZEfp[F(" +aFR0dG(0aEQYUD'CPC'CRD@TUDQTUCf0H@&968e4@9P946NY,69"699956dY+5Ne +49&998e"16P"69eTFA9eHBQGXF(&bFA*bG(9hHAYlHhKfFh*bFh9fGhPlIS'&L)U +-MBk3!*+8PTHCQTUCPT+2M)U)L)D$IAGaE'KPB9jE@&C688j06%j59eeLC'*I@eP +DAQ4SDQPRC@4NBf*KAeeC8dj*4dG)5%G%3N!r380%4%0%4NP,6P&8@9aHAPeF@PK +@88a'36dk1$Fh1$Sm2$Xj0#mV+LSY,c%b-c-d0MJi0cBd0$8i2%"%5%a28P4A@Pj +LC@CMA9G468a18&*899KEAQ"LBf0MC'9QCQGTDfpcGhPkHRKhGRKjHhYlHRKiH(K +kHhepHhTiGhGiHRf!Ji5$JS+#JS5&KiL(KS5#JS1&KSH(KiH*M*'@R++RV+qaXE# +XTk'EPBq+KiD'KB1"J(pqIAYjH(KiHAKhGA4eGA0aEfpaG(ChGh9eGhTqJSD*LSU +,MT1AQCQDQjfFQTH9Nj+4Mif-M)k3!*'4NC+8PjLBPC'1LiH#IAapJB5&Ji'#KBU +4PjQBPT58PCD@PT@@QCkLSk1IQjH8NT+8QD#QUDHMRjfHRjqGQjQCQ*LBQ*H9NSk +,L)D'KB@'KSD'KSD(LBb2NC'2MSq5PTZJT+QZXV@fYVDiZVh!`,fiXUfXVE+fZlr +#aXM,c-h0cFl2cX[%ZV#SSU'LTDQVUkQRTkQYXlV"b-r6e024cFR'a-2"[VbjZ,Q +l[Vqr[,HaUU5IRCfIT+Q[Y,Hk[,fpZlQiYV5aVDZTUDLSTkHRTU5LS+#KSk1LS*k +HSUHZYEc#b-[-c-V*bFR*amA"[ELdVUHIPj14NT+4Mik2NTHFS+1PU+Z[XV1aVUU +QSTkDPC+3!*'6PCD6MBD!IRk#L)q8PjH@P*16P*DBQTbIS+#FPT!!LiL)L)Q+LSU +*LSb1NC@CRD'PTkQTU+DNSk'KRjfEQ*D@PjLCQ*52LB5$Ji@*M)k1LiH"IATkHhq +%LBk5P*56Nj+5NC'6PCHBQ*H9NT!!MBb,LSZ-M)Z*KS1!IhjrJ)'$KBH)L)D&KSL +-Mj!!N!#2Mj!!NT5AQCUBPC'1LiU,M)q5PTLCQCQCQjbGR*UAP*'1M)Q(KSD)Lj' +@R+#MTUHSUUfaY,HiZELfY,'ZV+QRTU@PT+1LSU'LSkDTVDq`VUQNS*qKTDQYVkq +YV+UTUDLRT*qCPC19Q*bISD'JSD'LSk5PT+1MT+DQTD'FPT+3!)q1MBU'JAemIAq +"Ji@&KSD(L)L)KS@&KSL,MT!!NT5AQCUEQTQBPT53!)b)KiD%JRjjGA"YDQPUDQY +UD'9LB@"KB@&JA9TA9PGA9eC@9eG@9P99@&eNE(*fGhGfGR9dFh"VC9j@8%Y)5%K +)4N0!26Xm2N&%4dP,6%e-5dP*5Na16Na*4%!p26ir3%!r2Mdp26dp2N"$4NP-8&4 +B@9TC@&KB@&KB@&KB@&PC@ejKCQTYEh"`Efp`FA*dGA9eG(9fGhKjH(GdF@pZE@j +`FR*aEQTQBf*MC@PXEQp`FA4iIB'$K)1#JB+%KiQ,LiQ'JAjkGh4aEfj[F(*cG(9 +eGRGhGR0YCPpC9&&15dK&3d*$48G+5da068a-6%Y+4d4#38""3dC)5de068e,58G +%3N!r2MXi06)[,#JN)4iF'a`H)L8S+LSV,#ia0$8d-c%[,Li`0$P!4NY289*48%e +*4N)r2$Sj1$Jf0$%Z,#`X,5mc1$j%58a28&"26dj068e168a-6%j49&GDA&eF@eT +D@eeIB&pHA&YD@eaHB'&LBQ0MBf0MC'CRD'KSD@TVDfYVDQPSCfGSDQjcH(YpIRq +!JB'"J(pqIAYkH(GfGA4cF'aSC&pE9eC999489&9A@&PEA9pLC@KUE'jaGAKmIS# +!JB+$KSQ0N!#5P*@@PjLCQTUER*fHS+1QUDUVUDHNSU#IS+'LT+5NSk+JRTbER*b +FQC++J(KcF'jZE@YUD'KTDfe[FA0eGhKiHATlIAjqI(ThGA0aF'pZE@aXDfYVDfY +UD'KSDfe`FR0cF@pXD@KSD@YYF(0eGA4bF'p`FR0cF'jXE'jaGAPpJSD*LSZ,LSU +*L)L(KS5"IRYjGhCfGhKjHAPjH(KiHAYmI(YiFfjUCQ0KB'&MCQGSD'PYFRPqJ)" +rIhjmGh*YDQKSDfpcGRGhGRGjHhf!K)Z5QCfJSk@SUUUSTU5LS*qKTUfcYlQiYE+ +ZU+#CP*+9QU#NTkLSUDQUUkf[XE1eZ,c"amh4e0E9dmr,aX2![VZjYl@eYlUr`mM +,cFl2d0,8eYIBfYlLjq[Zm2$[l1MMhYM6cml0cFc+am+mYV#XUDQTUDUVVV1k`FA +)b-R+c-r4dG$2cmr4dY,3cFR&`VkkYl5cXl1dYEDfY,+[VDURTD@TVl@jZVZkZ,D +cXDqYV+USTU5MT+@QTkDPSk+MT+5KR*@2M)f3!*5BQTbGRk#JRjkFQCD6N!#1MBb ++L)@#IhemI(apIRpqI(ThG(*`EfjZE@eYE@YUD'KSDQe`FhCjI(k!JB1%KB@$J(j +mI(apIRepIRq"JS+"IhajGR*ZE'YVE'jZEQp`FA0eGRCbE@CKAPeHAf"KBQ0MBQ& +KBfGXFRL!KiqARU5TV+k`X,#[V+QRTULTUDHMS*bDQCL@P*'1M)b-MBq3!*+8PTH +APjD@PjLDQjbFR*bFR*bGRCfGRTqIS++PUDb[XE+cXl5cXE#ZV+USU+UYX,+bXE# +[Vl#aXE'[V+LNS*kEQCLAQCZFRCbEQTZHSUDUVDqZV+LNSCkFQTLAPjD@PC@9PC@ +9PCDCRU1QTkDNSU#JS+'JRTZ@NSk-MBk1MBQ$IAPhGA0`E@YUDQPRC@0LBQ0PCfT +XEh&bFR&aFA*dGRPlIAjpHRGcF'eVDQYZFR9hGR9eGACiHAYpIi'%KSQ,LiU)KB& +rI(ThFfpVCf9MBQ"IAejHAPjHAPpIAejHAQ"KBf4NBf&JB@4SER0fGhGfGA4dFh* +`Efj[F(&bFR*aEfeTCQ4MBf0MBf0LB&pGA9aD9e036Na*4dG+8&GEA9eF@eYE@PK +@9&*489*89&456dY'3Mim16Fd-M!`-$%c0MNl1cXl2$dr384)5dj38&"48P058%e ++58T18PCC@eaIB@9SDfeZEQj[F(&aFR0dGACfG(&YDQGQC@4MB9pGA&aHB'*MBQ& +IA&TB9PC@9eGB@&KB@&TGB'*KAPT988p38eGEA9pKC'GVE@j`FR9fGR4cFh9iHAG +cEQPPBPpGA&aGA9eGB'4SDfaZEfp[Eh&fI)1*MT'5NBq1MSq1M)H"HR*UBPT66NY +*4N)p1MNl28"#3N&!2Miq3%*&4dP-6e&58P&36dj068j499THB&pF@9968e4@9eK +A9PC@9PKC@eaF@PG66de06Np38%p26dp48P*699GEAf*QD@a`FhChGR4cFR&bFR0 +cG(9fGRCfGA4cFR"ZDfGNBQ&MCQKSCf9LAPTB9PGDA@"LC'9RD@eaGhf$L)b1MBZ ++LSU,MBk2MSZ(Ji"qIi'$K)5%Ji&qHA9bFA&aF'jUCQ0KB&pIAf"LC'CPB9aA999 +AA'&PCfGRCQGSDfeZE@YTD'PXFAGmIi'!IRYjGh9dFQpYE'e[FhGlIAq!JS5'KiL +)LSb4PTUGRCbER*kJS*kEQCLCQTUCQ*LCQCUCQCLAPT@4MBL%JS1'LSk4Nj58Nj1 +6NT'4NC'5NT+4NC19PjD4M)L(L)Z-M)U)KiL)Ki@$JS1&KiL(KS@'L)b3!*@DRU+ +PTkLSU+LTUDUUUUQSTkHRTkDPSk#GQjL@P*+3!)q3!*'5P*58Nj15Nj59PT@8NBq +-LiZ0N!#5Nj+3!)k0MBb-LiZ-MBk2NC1@Q*QDQCHAQ*UGRk#MTUZ[Xl5dXl1cY,D +iZVbq[m#r[lr!`X6(b-M(a-,![Vfp[Vr![lfjYE'ZVDfZVl+eZ,UlZlZlZlZm[Ek +q[EZjYV@eYVHj[,kr[VUfXDfVUUZXVV#aXV+aX+kXUD@KRCUCQTfJSkDSUDQTTk@ +MS*fEQTUDQjbHRk'MTDHSU+LSU+QUV+fZVUkYVDbXUkUST+'IRTkJSU5QTkLSTk5 +JR*UERD#LSk+KSD+NTkZ[XV5eYE5bX+qZVUq`XV'`VDZSTkDNSU#GQjQCQTUEQjZ +EQTUEQjZCPj@6NT!!Mif-M)Z+LBL'Ji"pHhTlIS'$Ji&rI(YlIAq#KBQ1NjLFRk# +JRTZBPT@8PC@9P*14MSb+LBQ*LSZ0MSq2Miq2MSk2N!#6PCHAPT55N!#2MSf,LBQ ++MBq4NC!!MSf,LSL(KS@&KBD(L)L(KS@&K)5%K)5%K)5$Ji1%KBD(KiD&K)5&KSL +*LSU+LSZ0MSq2MSf0MT!!NjHERU'NUDkbYVLjZEQiYlHhYVDeY,1aVkbSTD1LSk1 +LSD#JSU@RUUZXV+ZVUkURT+'HR*ZDPj11LBD&K)1"IRTfFh&aF'eSBPaC@9YHB@* +KB'&MCQPVE'aYE@j[F("[EQeYE'YTCQ0JAPYC9e99999@9PC98e&36Na+4d4#3$m +p2$Sj16Sm2NN%r26`m26p"3N*#3N0&5%a38P*58P09@9YGA9aD9e988e489&4 +56dY(3cml1$Ji16Sk16Fe-c)b-M3f16e!4%C)58T-6P"699GC@PTD@9G@9eTGB'* +KAeaD@9KA9eG@99048%p38P9B@ejIB'"JB'"IAPaE@eYE@PK98e&26%P&36dk16J +i1$Fe-LmX+LNT+L`[-M3f16`r3dC*5da-6%e16e&58e9@@9aJC'KXF(4fGR4bF'p +`FR0dFh*aF'pZE@aUD'CMB@"IAPeFA&aHAf&MC'4PC@9QCQGSDfjbGRTqJSD*M)k +3!*'6PTQERCbCPC!!M)Q)KiD&Ji&qHhKeFR&aFA&[DfGNBQ&JB'"IAepHA&YD@PY +FA9jHAPjGA&TB99*48&&48P089&9@9ePEA9pJAeeD@&GA@9YGAPpIAf"JB&pHA9a +FA&jJBQ4PC'0LBQ0NCQPVE@jZEQeYEh&dGRKjHAGeG(4fHAaqIRjqIAjrJ)+%KSH +)LBQ)KS5#JB#!JB1%KB@#J(ekHRYpIi"rI(TiGhGhH(PkHhapIAepIRq!JB"rI(P +hGR9fGRCfGA0aEfaTCfCRD'PUE'jbGhZ!JiD*LBL'K)*rI(PfG(0bFh0dGA9hHAb +!KBL+LiZ*Ki5#JS1'L)Z1N!#4NT18PCHAQ*LDR*kKSU+LSD#IRTbDQ*D8NT'4NC1 +@QTfIS+'MSk1LRjfEQjbISU@PT+'IRCZER*bGRCkHS++NTUHRTUDQTkLTUUZXV+f +[XE'`VkfXV+bYV+ZSTU1KRjkHRjqIRTfFRCkKSU+KRTZCQ*LCQTUCPj54MSb0MT' +5Nj16P*DCRU+QU+QTUDUVVDk[X,'bY,DiZ,DcX+fVU+DMS*fFR*fHRjqHRCZDQCQ +CQCQDQjbFQjbGRU#KSCqHR*ZEQjbFRCfHRTkHRCbFQjZFR*fHS+'LSk1LS*kGRCf +HRTbCPT14NC1AQTbGRU#MTUUYVkq[VUfYVl#aXV1dXl+`VDZVUkZUTk+GQ*@8PCD +AQ*UFRCqKSU1NT+5NSU'HR*UBPT54MSb+LBQ+Lib0Mj'6PTQERCkIS+#JS+#IRTb +EQTUDQCLAQ*QERCqKSU+JRCU@NT!!Miq3!*'5P*@@PjLBPjD@PTHBQTbHS++MT+@ +PTD5LRjbCPjD@PTD9P*+3!)k-LSL&Ji+"J)"rIhjqJ)+%KB@$J(elHhaqJB@*MBq +3!*!!Miq2MSf,LBH'KSH(L)L(KiD(KiL*LSU+LiZ-MBk1MBZ(JRjkGhCfGhKjHRT +kHRTkHherJ)'#Ji1&KiL+LSU+LBQ*LBL'K)*rIAYlHhYmI(epIS##KBH*LiZ,LSU ++LSb0N!#6PTLERCfGR*bER*fGRTkIS++NTD@PSk#GQ*54MSb+LBL(KB5#JB"qI(T +hGA4dG(9eGA9fGRCeGRGjI(k!J)#!J)'"Ji5&KiU-Mj+8P*55NC!!N!#3!)q1M)U +*L)L)L)L(KS5"IRYiGhChHATlHhTjGh9bEfeVDQPSCfCPC'4NC@9RD@aZF("[E@Y +UD@TVE'aXE'YVE'j`FhCiHATlHhapIAakHAPjHRYkH(CcFR"`F(&bFh4dFh&`Efj +ZE@TSC'"G@PC88P&38&"36Ne-6P&9@f"NCfKRCQCPC@CRD@YYEQp`F("`F("aFhC +iHRPiGA0aFA0hI)'&KSD%JS#!J)"rIAYiGA0bF'jVD'CQCQCRD@TVE'aXDfPSCQ9 +MBQ*KB@&KB@&LBQ*MBQ&IAPeFA9jJB'"IA9TA9&*48%p168Y)480#38&"3N4'58Y +-68j26e"489&58e999P999&069&499PGB@9TD@eYGB'4SDfj[EfpYDfPRC@*I@eK +@994688p168e06%Y*5%K*5Na16Nj068e-5NP)58e499PEA&eIBQ9SDfeZEQeVD@G +QCQGTDQTTCf4KB'"LC'CSDQYVDQPRC@*IA&P@8e"05NK(4NC'4NC'4NC'4dG)58a +28eGD@eYE@eeHB'*MBf4NC'4PCQGSD'KRCQ4LAeaC9P989&4999CA@9YIBfKXF(4 +fGRCeFh*`EfjYE@aUD'9LAejIB@4QCfKSD@TVE'j[FA4eGRCeFh&`Efp`FA*cFh4 +eGRGiHATlIAeqIAalHhapIRjpHRCcEfeVDQPSCf9LB&pIAepHA9YC@&KB@PjLCQP +YF(0fHAYmI(apIi'%KiU0Mj'4NT+6Nj15NC!!N!#3!)q0LiL&JS"rIRemHhapIi' +#K)@(LBb1N!#4N!#1M)Q'Ji&rIRjqIRprJ)#"JB+%KSU1NT@@PT@6NBq1MBb,LBD +#IhepI(apIAerJB5(L)L(KSD(LSf2Mik-LSQ*Lik3!*'3!)k,LBH'KiQ+LiU)KS5 +#JS1&KiL*LSZ-MT!!NC+6Nj15N!#2MSk2NC+8P*58PCHDRU+NTUHSUkkbYEHjZEL +iYlDfYVLk[F$"`F(!`-(#`m2#`,kq[m,&aXE%`F#r`F6(b-R*b-I(b-R*bFM)b-M +*bXV,bmV*amA$`F$!`-$!`,qr[lqr[lflZ,@bXE'bXl1bX+kYV+fZX,+cY,@dY,1 +bXE'aXV1eYVLk[F(%aXE&a-'r[Vfq[Vqr[VblZVUl[,blZ,1ZUUHPSk+KS*kHRTk +HRk#KSU+LSU+MT+@RUUbYVDbVUDHPSk+KSD'JS*qHRTfHRk#LSk1LRjZBPC16Nj+ +4N!#2MSf0MSk3!*+@QCbHRTkGR*ZDQ*H@PC15NC!!N!#2Mik1MSk2NC5@Q*UEQjZ +FRCqKSU+LSD#IR*Q@Nj!!Mik2N!#4NC'3!)q3!*+@QTkLTDHRU+LTUkbXUULPSk' +KSD'KSD#IRCbDQCQBQ*D6Mib+LSZ-MBk0M)b-MBq5PCHDQjZDQCLCQTZGRjqHRCZ +CPjD9PC@AQCZGRTfFQTQBPjD9P*15NT+5NC'3!)q1M)U)KB*qHhKfGR9dFh&`EQa +UD@PTD@YXE@j[F("aFR*cFh*bFR4fHAarJS@(L)Q*L)H'KB@&KSD'KB5%Ji+#JB# +!J)'#K)@&KB5&KSL+M)b,L)D&KBD)LBU+LBL)LBZ0Mj'6PCHDR*kIRTbCPj55Mik +-LSL'KB@'KiH(KS5#J(q!JB+$K)5%K)1#JB#!J)+$K)5#J(ajGA*`E@TRC'&IA9a +E@PPB@&PEAQ*PCfGRCfCQC@9PCQ9PC'0LBQ*KB@"IA9eFA&aE@PK@99999PGA9PC +99&*36dj06Np389*58P*48P*699GB@9TD@9PC@9PD@9KA9PCB@PYFA&aD@9KA9P9 +68P&38%p26Nj168e,5NP(4NC%3d&!2cip26dm2$Xj1$Fh1$Xq380$3d0#3N*#3N* +!2M`k16Nj1M`q2d"!38d0$3N&!2cmr2d""3N4&4NG)5%G(4NC(5%P+6%j389& +46dj06%Y-69"69PKD@eaGA9aGA9jHAPeE@PKA9PCA@&TEA9pJBQ4PCfKTDQYXE'Y +VDQPSD'KRCQ4MBf4PCQKSD'GPBQ"HA&TB9P988P*58e9B@ejKBf4PC'4NC'4NBf* +KB'"IAPjHA9eGA9eHAQ"LC@KUDfYVDQTUDQYVDfYUDQPTD'KSD@PUDQTTCfCQCfK +TDfaXE'aXE'eYEQjZEQp`FA*cGACiHRerJB5'LBb1N!#4N!#1LiQ)KS@%K)1$Ji+ +#JAppHRKfGRCfGhKiGhCfGRGjI(k!JB'!IhjqIRjqIi##K)@(LBU,M)f1MSk-LSH +$IhelHRPiGA0aF'p`F(&aFA&aFA&aF'pZE'YTCf9NBf*JAeeFA&eHAf&LC'9QCfG +SD@YYEh"bFh4eGRCfGRChHAYpIRjpI(YkHAKhGR9eGACfGR9eGAChH(PjHRYmIAp +rJ(prIRepIAamI(alHRPiH(TpJSD+M)b,LBL)L)L)KS@#JB"rJ)#"Ji@(LBU+LSU ++Lik4Nj@9PC15NT16Nj+3!)k-LiZ-M)f-LiZ+LSU,Lib-MBk3!*1BRD'NTUDPTD5 +PTULUV+fYVDfYVDfYV+bXV+fZVkq`X,'bXl5dXl+bXV+dYEDfYV5cXV+bXl5eYVH +iZVZlZlZlZlbm[Er"`m6%aFA%`m,![VZjZ,HiZEUm[Vr!`,qpZlQiYlLiZ,DdXDk +VUDHQT+'HQTH8NT!!N!#3!*!!NC'5P*@AQCUER*bFR*bFR*bEQTQCQCUER*bDQCL +APTD@PTHAQ*LBQ*LBPjHAPT@8Nj+4N!#1MSk2N!#5Nj56NT'3!)q1MSq3!*+8PjQ +FRk'KSD#IRk'NU+b[XV5fYlHhYV@cX+bRSTfCPC+3!)k0M)Z,M)k2NT5AQTkKTDL +UV+fYVDbXUkQSTkDPSk+KSD'MT+DRU+LTUUbYVl#aXE'`VUkZVl#aXV#ZUULQTUD +SUDUTTk5KRTbFR*fGRTqJSU@SV+qbY,DiZEZp[m$![lflZELfYE1aVkfYVUq`XE' +bXV'[VUfVUUQSTk@MSCkEPj54MSf0M)Z*L)D&KBD(LBZ-M)b-MBk2N!#5Nj58P*5 +8PCDAQ*QCQCQDQTZFR*bEQTQBPjD@PTD9P*+3!)k0M)U)KS5"IhjpI(YkHAKiGhG +hGhGiH(KiGhGiHAYpIRq!JB'!IhelHRTkHhYmHhYkHRTlHhYkHAPiHATmIS'$K)@ +%K)5%KBH*LSb-MBf-LiU+LSU,M)f1MSk0LiQ)KiH)LBU,LSL'Ji+#K)D*M)f1MSf +0MBf0M)b0MBk0LiQ(KB1#J(jmHACaE@TRC@4NC@9QCfKTDfaYEQj[Efp`FA0dGA9 +eGA9dFh&ZDQGNBf0MC'4NBf*KB@"KB@&KB@*LBQ*LBQ*LBQ*MBf4NC'4MBf0NC@C +PC'&IAPjIB'*MBf*LB@*MC@KUE'eYDfTTD'GRCfCQC@9QCQGRD'KRCfGRCQ4MBQ& +KB&pG@eK98Np-5NK(4N9&4NG)58T08&4B@PaFA&eHB'"KB@&LBf9QCfGQC@4MBQ" +IA9YD@&G@9P9999998e&16%P(484#3$mp26`p2Miq26dp2N""3N0&4NG(4dG&3d% +q2$Nh063c-M)d0MJl2N"$4%C(58Y-68e06%a-68j389*48%j-5dY-6%e-5dP)4dC +&480"2cdm2$dq2Mp!3%&"3N*#3N*#3d0%48C'4N9&489&4NC(58a28PCDAQ&PD'T +YER"`FA&aF@pZE'TSC@0KAejGA&aFAQ"LBf4PCQGRD'PTDQYXEQp`F(&aFA*cFh0 +aEfaUD@PTDfaZF(&bFR0dG(9fGR9dFR&aFA*dGRKjHAPhGA0aEfaTCf9NC'0KB&j +GA9eHB'*MC@CQCfPUE'jaFh9hGhGfGR9fGhPlIAprJ(prIRjrIhprIRjqIRjrIhp +rIRakH(CdFh*aFR*cG(9dFh"XD'4KAepIB'*MC'4MBf0NCQKVE@paFhCkIS+'LSb +0MBf-LiU*L)H'K)1#JB"rIRepI(alHhTkHAPjHRTjHAKiHAPjH(GfGACfGRChH(P +lIAq"Ji1$JAppI(YlHhYkHAKhGR4bFR*bFh4fGhTpJB5'L)U+LiZ,LSQ*L)Q+M)k +2N!#4NC'3!*!!Mik0MBf0MSk1Miq3!*'6P*DAQ*QCQCQCQCQCQCUEQjUBPjD@PjH +BQ*LBQCZGRTqJSU5RUUbZVkq`XV5fZ,QkZELfY,1bXE'aX+qYV+ZUUkbZX,#`Vkk +XUkZVUkbXV+bXV+bZVl'bXV+aX+qZVUkYV+USTkHRU+QUUUQSU+LTUkk`XV5eYlQ +l[X(%amV-cFl2cmr3d0(4dG(3d-r1cXh0cFc+amA$`F,$a-E'aF2![EUiYV@eYE5 +dY,1cXl5dXl+aVkfUUDQTUDQSTU5MSD#JRjqJSU5QU+UXVl'cYEDiZEQjZELfXl# +YUUHNSCkEQCH8NT!!Mik2N!#5P*@@PjD8NSk+Ki5#J(jpIAeqJ)1&KSH(KiH)LBU +-MBk1MSk1MBf0MBf-LiU+LiZ-MBk1MSk2N!#3!*'5Nj18P*15NC!!MSf-LiU*L)L +)LBU-MBk1MBf-M)b-M)f0MSk1MSf-LiQ)KiD&Ji"pHRKfGRChHAYpIi#"JB+%KSQ +0N!#5PCHDR*kJSD'KSU+MT+@PTD5MSk1NT+@PTD5NSk1MSk+LSk5PTUHRTkLSU+L +SU+LTUUZYVUq[X,'aXV+aX,#[VUfVUDDNSk+LSk5NTD@PTUDQTUDPT++KRjbDPT1 +2LiH%JS'!J)'"JS1%KBD(L)Q+Lib0MBk0MBb-LiU+LSQ)Ki@%JS"rIRakH(9cFR* +bFA"ZDQCMBQ*MC'9NBf*KBQ0PCfKTDQPTCfCNBQ"HA&TC9eC999489&489&499PG +C@PYFA&eHB'&MC@9PC@4NC'CSDQaZEQjZE@jZEfp`FA*cG(4cFR&`F("aFR*cFh0 +dG(9eGA9fGhPlIS'%KSL+M)k3!*'5Nj+4Mik-LSL(KS@%Ji'!IhjqIAalH(4aE@Y +UD@PTDQTUDQTUDfYXE@eXDQKPBf*KB@*NC@9QC@9NC'4PCfTYEh&cGAGjHheqIRj +pI(PhFfpVCf0KAPaE@PKA990589"389&48P0899CA@&KC@9PC@9PD@PYFA9jIB@0 +PCfKTD'CNB9jF@eTC@&G@9&468P&36dj-5dT*5%K*58P+5da06Np38&"38&"48P4 +99PGB@&PC@PYFA9jIAf"KBQ0NC@4NBf0LB@&JAepIB'"JB&jF@eTD@eYE@eYD@9K +A9eGB@9PD@9K@9&&05NG&3d&!2cmr3%&"38&"3N0%4NG)5Na28PCDA@"KBf9QD'P +UDfe[FR0cFh0cFh0bFA"ZE@aVDfYUD@KQBf&HA9aFA&YE@PKA9eGB@9TE@eaGA9j +HA9aD@9KB@&KC@PYE@eTC@9PEA@"MCfTZFA0eGRGiH(KiGhCdFh&`EfjYE'aXDfT +UD@KSD@TVE'eYEQj[F(&bFA&`Efp[EQeVD@KQC@9PC@4NBf0MC'9QD@YZF(*dGRG +jHhapIAepIAepIAjqIhq!J)"rIi#!JB+$Ji5%K)@'KiL+M)f0MBb,LSQ)KiD&K)1 +"J(jpI(YkHAKiH(PlI(eqIRjpIAamHRPfG(0bFR0dGRGjHRYmIAeqIi#"JS+$Ji1 +$K)5%Ji'!IRalHAKhGhGiHAYmIi'%KSL+Lib-M)Z+LSQ+LSb0N!#5PCLERD'NTkZ +ZXE1eYlLiYl@cX+fUTD#EPT+2MSf1N!#5P*DCQjbGRTfGRTqJSU1NSk1LSD#JSD' +KS+#IRjqIRjqIRTZBP*!!M)H%JAprIi#!IhjpHhYlI(k!Ji@(L)Q)KiD&K)1$K)@ +'KiL)L)H(KS@&KBD(L)L*LSb1N!#4NT'4N!#3!)q2Mj!!NC'4N!#1M)Q(KSD'KiL ++Lib0MSk2NC19PjQERCkJSD1PTkQVVV#dYlQl[,kr`F2&amM)amA#[lbkZVUl[,b +mZlUjZEUl[Ekq[VfmZlZkZELhYE5bX+fVUDLRTU@MSU'KSU5QTkLTU+LSU+LSTk@ +MSCqHRCfFR*bGRCkIS+'LSk5NTD@PTULSUDQSU+DPSk+JRjfEQTLBPjH@PT@9PC@ +@PjLBQCLBQCQER*kJSD+MT+DRU+UVVDk[X,'aX+qYV+bVUkZXV+bVUkQTU+HRTU@ +NSU'JS+'LSk5PT+1JRTbEQjbHRk#IRjqISD1PTkHRTk@NT+@QTkLSTk@NSCqGQjL +@P*'1LiL'K)1#JApmHACcFA"`FA*bFh0bF@pYDfKQC'0MBf4QD'TYEQpZEQeXE'Y +VD@KRCfPUE@p`FA&bFR0eGhKkI(q!JS1%KSL*Lif1Mik1M)Z+LSU+LSQ*L)H'KB5 +#JAprIhjqIAakHAKhGhKjHherJB1&KSH)LSZ-MBf0MBf0MBk2Mj!!NC'5NT59PjL +CQ*H9Nj!!MSb,LiU+LSU,LiZ,M)b-M)Z+Ki@#IhemHhamIAeqIi##KBL,MBq4Nj5 +9PTHAQ*HAPC55N!#1MBb-M)b0MBk2NC1@Q*UER*fGRCbEQCLAPjDAPjHBQ*QCQjb +GRk#JSD+LSk1MSk1KRjbCPT13!)k-LiU*L)H&Ji+"J)#!IhpqIAepIRjqIRjpI(T +iGhCeGA4cFR"YDQCMB&pHAf"KB@*LBQ&KB'"HA9aE@PPB9eC88P"05dP(4N9'4NG +)5%P*58P*58K)4dG(4dG(4dG'489%4%0$3d*"3%!r2cip2$Xj0cBe0MFj1M`p2Mp +!3N0%48G)5Na06Np28&&489&36de068e16Nj168e16e&699GB@9PB@&KB@&PD@ea +GAQ"KBQ*MC'CRD@TXE'eYEQj[F(*cG(4dFh&`Efp[EQeXDfYUDQPTCfCNBQ"G@eP +A99046Na*4d9$3N*$4%C(58T-68p38P0899C@9eKC@eaFA&YD@9G@99468e*488p +16%Y+5NP+5NY,6%a-6%a06P"48P089&9999CA@&PD@PTC@9PC@PYHB'*NC@GSD@Y +XE@eZEQjYE@aXE'aXE@jZEfpZEQjZEQp[F("`F("`EfjYE@eYEQp`FA*bFh0cG(9 +fH(TlI(epIAemHhTiGR9dG(4dG(0aEfaTCf9NBf4PCQGSD@PTD@PTD@PUDfe[FR9 +hHATkHRTkHRPjH(GfGA4cFA"ZE'YUD@PTD@TUDfaXE'aXDfTTD'KTD@TVDfaXDfY +XE@pbGAKlIB##K)@'KiH(KiH'KS@&K)5%KB@&KB@&KB5%K)1$Ji5&KSL*LBU,M)f +2NC18PCDAPjLBQ*H@PTDAQTfKT+DQTUDPTD@QTkQUUkUTTk@MSU'JRjkGR*ZDQCL +@PC14Mif,LBH&Ji'!Ihq!JB1%KBD(KiH)LBZ1N!#6PCHCR*kKSk5PTD@NT+5NTDD +SUDZXV+bVUUUTUDQTU+HQT+1LS+#JS+#JS*qIRTfFQjUBPj@8Nj+4NC'4NC'5P*@ +@PTD@PTHCQjqLTDLTUUUUUDQTU+LSTkHQTUDQTUDQTU@PT+5NSk1LSU'JRTfEQTL +APjD@PjLCQTZER*fGRTqIS+'MT+@RU+LRTU@NSU'KS*qIRTkGRCbEQjZFRCkGR*Z +EQTUDQTUCQ*H9Nj!!Mif-M)b-M)f0MBk1Mj'5Nj@@PjQERU#MT+DRU+QUV+f[Vkq +ZVUk[X,+dY,5dXl+aX,#[VUbTU+HRU+QUUUUTU+DPT++KS*qGR*ZDQCLAPC13!)k +,L)D&K)1$K)D(LBZ0Mj'6P*@9PC@@PjQDQjZEQTLAPTD@PTD@PTD9PC@9PC@9PC5 +6NT'4NT19PTLCQTUDQTUDQTUDQTUDQCQBQ*LBQ*LBPjD9P*16NT+5NT+6P*59PjL +CQTUCQ*HAPjHBQ*LCQCUEQjbFQjZEQjbGRU#LSk1LSCqHRCfGR*ZCPj55N!#2Miq +2Mik1MBf0MBf0MBk1Miq2MSf-LiQ(KB1"J(jmHRKfG(0bFA"[EQjYE@eYE'aUD@G +PBf*KB@&LBf0MBf*LBQ0PCQKUDfaYEQj[F(*cG(0cFR&aFR0fH(TlI(epIRq!J)' +"JB'"J)#!J)#!IhprIRjpIAamI(aqJ)+%KBD(L)L*LSU,LSU*L)D'KSD'KSD'KS@ +%Ji&rIRepIAemHhPhGR4dG(9fGhKiHAPjHAPiH(KiGhCdF@jVCf0JAPaE@PPB9eC +@99468P*58e499PKC@PYFA9jIB@*MC'9RD'TVE'eYE@aXDQPSCfCQCQCQC@4MBf0 +MBf*LBQ&KBQ*MC'9QD'PUDfYVDfYVE'j`FR4fGhKiHATmIAjqIhpqIRjpIAamHhT +jH(GhGhKjHRYlI(amHhYjH(CdFR&[E@YTCf4KAeaE@PTEA&jIB'"JB'&KB@*MBf0 +MBf0LB&eD9P*25dP'3d!q2$Xl1MSj0cBe068f0cJk2$dq2dd4'4dG(4N4$3d0 +%48G)58Y,6%a-6%Y,5dY-68j389&58e9@9eKD@eaHAf"JB@*LBQ&JAepHAf"KBQ0 +NC'9RD@YYEh"aFR0dGAChH(KiGhCeFh&[EQaXDfTTD'GQC@4MBf*LBQ*LBf9RD@Y +YEQp[F(&bFR0cFh0bFA"[EQeXE'YUD@KQC@9PC@9PCQGSD@TVE@j[F(&bFh0cFh0 +dG(9eGA9dFh*aFA&bFR0cFh*bFR0eH(TpIi#"JS1%KBH)LBU*L)D&Ji+"JB'!Ihj +pI(YkHRTkHRPjHAPkHhamIAepIRjqIRepI(YjGhCeG(4dGA9eGAChGhKjHATjHAP +jHRTlHhYkHAPiH(KiHATkHRTjHAKiH(KiHATlHhYlHRTkHAPjHATkHhaqIhq!J)# +"JB+%KBD'KSD'KiH)LBU,LiU)KS@%Ji1%KBD(L)L)L)L)L)L*LiZ-MBf1Mj'6P*D +BQCUEQjZEQjZER*bGRCfGR*ZDQCH@PC56Nj16Nj15NC!!Mik-LiU*L)L)L)Q*LBQ +*LSU,Lib-MBk3!*'5P*@@PTHAPjH@PTD@PC@9PC@9PC58P*59PCD@PC56NT'4NC' +3!*!!Mik0M)b-MBk1Mj!!N!#3!*'4NC'4NC'3!*!!Mik0LiU*L)L(KiD&KB@'KiL ++M)f1Miq1MBf-M)f1Mj'5Nj59PTHBQ*LAPjD9P*58P*58P*58P*58P*58PC@AQ*Q +CQCLAPC15N!#2Mj!!N!#4NC!!Mik-LiU+Lib0Mj!!NC+5P*@AQ*UFRCfGRTqJSU5 +PTUDPT+5NT+5PTUHSU+HPSk'JRjqIS+#KSD'LSk5QU+UXVDq`X,'bXl5eYVDfYE5 +cXV'`X+qZVUfYV+bXVDf[X,'aXV1cXl1cXl1cXl+bXV+bXl1cXV+aVkkXUULQTD5 +NT+5MSk1MSk5PTD@QTUDQTUDQTU@NSk'JRjqHRTkGR*ZDQ*D9P*58P*58P*58Nj1 +6NT+5NC+5NT16Nj59PjLDQjZEQjUCQCLAPT@8P*59PC@9PC55N!#1MBf0MSq3!*+ +6P*@9PC58P*15NC!!Mif-M)Z,LiZ,M)b-LiZ+LSQ*LSU,M)b-MBb-MBf0MSk2Miq +2MSk0M)Z+LSQ*L)L(KSD'KiH(L)H(KS@&KB@'KSD&K)1#JB#!J)"rIhpqIRjrIi# +!J)#!IhjpHhYkHRYmIAjrIhprIAalHhYlI(YlHRPjH(KiH(GfGA4dG(4dG(4dG(4 +dG(4dG(4dFh0bFA"[EQeXDfTTD'GQCQCQCQGRCfCPC@9PC@CQCQCRD'PVE@paFR* +cFh4eGhKjHRTjH(GfGA0bF@pZE@aXDfTTD'GRCfGRCfKSD'PTDQYXE'eZEQp[Efj +XDfTTD@PTD@KSCfCPC'0MBQ*KB@&KB@&LBQ0MC'4MBQ&HA&YC9eC88e&38&"38P0 +8999@9PGB@9TFAQ"KBQ*MBf4PCQGRCfGRCQCQCQCRD'PTD@KSCfCQCQCQC@9NC'* +KAejGA&eHAf&LBQ*LBQ&KB@&KB@&KB@"JB&pIAepIB'"JB'&KBQ0PCQGSD'PTD@P +TD'KSCfCQCQCQCQCPC@4LB@"IAPpIB'&LBf0MBQ*MBf4QCfKTDQTTD@KQC@4MBQ* +LB@&KB'&KBQ*MBf4NC'9QCfKTDQTUDQTUDQTUDQTTD'GQC@4MBQ&JB&pIAepIB'& +LBf0MC'4MBf4NCQGSD@PSCfGQCQGSD'PUDQYVDfYXE'eYE@aXE@eZEQjYE'TTD'G +RCfGRCfCQCQCQCQ9PC'4MC'4NC@9PC@CQCQ9PC'0LBQ*LBQ*MBf0NC'4NC'4MBf0 +MC'4PC@9QC@9QCfKUE'paFh4eGRCfGRGhGhGiH(KiGhCeG(*aF("`F("`EfjYE'Y +VDQTUDQPSCQ9PCQKUE'pbG(GjHhk!JS1%Ji1#JB#!JB'"JB"rIRepI(apI(amHhT +kHAPjHAPiH(KiH(KiH(PjHATkHhYlHRTkHRTlI(erJ)'#Ji5&KSH)LSf2NC18P*5 +8PC@9PC@9PC@@PTDAPjD@PjLCQTZFR*bFR*fIS+'KSD#IRjkHRCfFQjUBPT55NC! +!Miq2Miq2Miq3!*!!NC'6P*@AQCZGRk'LSk1MSk+KS*qGQjUBPjH@PC@8NT'3!)k +0MBf0MSq2N!#3!*!!Miq2N!#3!*!!N!#3!*'4NC'5NT16P*59PTD@PT@9PCD@PjL +CQTUDQTZFR*fHRTqJSD'LSU+LSU+LSU'KSD#IRjkGR*UCQ*H@PC@9PC@@PTH@PT@ +9P*16P*58PC58Nj16Nj58PC@9PC@9PCD@PjLBQCQDQjbGRTqJSD#JRjkGR*ZDQTU +CQCLAPjD@PTD@PjHBQCUER*fGRCfFQTQAPT56Nj+4N!#2Mik1MSk2Miq2N!#3!*! +!N!#2Mj!!N!#3!*'5Nj18P*16NT+6Nj58PC@9PC@8P*56NT'4N!#3!*!!N!#4Nj5 +9PTHAPjHBQ*QDQjZER*bGRCkHRTkIS+#KSU1MSk1MSk1NT+5MSk+LSCqGQjQ@PC1 +5NC!!MSb,LSQ*LSU,MBk2N!#4Nj59PTHBQ*LBPjHAPTD@PTD@PT@9PCD@PjQERCk +JSD+MSk1MSk1MSk1NT+@PTUDQTD@PT+1MSU+KSD'KSU1MSk1MSU'JS*qHRCZDQCH +9P*15NT18PTLDR*fISD1PTkQTUDHPSk'JRTfEQTLAPT56NT'4NC+6P*58P*15NC! +!N!#2Mik0M)Z+L)D&K)5$Ji1$JS'!IhjpI(alHhYkHRPiGhCeGA9eGA9eGA9dFh0 +cFh*bFR&`EfjZEQj[Efp[Efp[F(&cG(ChH(PjHATlHhYlHhYlHhYlHRTjHAPjH(K +iH(KjHRYpIRq!JB'!J(pqIRjpIAjqIRq!J)'"JS1$K)5%Ji1$Ji+"IhjmHRKfGA0 +bFR&aF(&aFR*cFh4eGACfGRGhH(PjHAPiH(GfGA9eGAChGhKjHATlI(amI(YlHha +pIAepIAalHhYlHhYmI(apIRjrJ)#!J)#"JS+#JS'!J)#!J)#!IhpqIAamHhTjH(G +eG(0bFA"`Efp[Efp[EfpZEQj[Efp[Efp[EQj[Eh"aFR*bFR*bFR0cG(4dFR"ZE'T +TD'GQCQCPC@0LB&pGA9eGAPjGA&TB9P989&488e068P*58e068e068e068e*589& +48P*68e058P*489&489&489&489&489*68e068P&36dp26dp26dp26e&59&GB@PY +FA&jIB@*MC'4MBf*LB@"JAeeFA&aFA9eHA9aF@eYEA&eHAf&LC'CRD'PUE'eZEh& +aFR0dGAGiH(KiGhGfGRCfGRCfGRCfGRCfGA9eGA4cFh&`EfjYE'aXE'YUD@KRCfG +RCfGQC@9NBf0MBQ*KB&pHAPeFA&aFA9eGA9eGA9eGAPpJB@&KB&pHA9eGAPpJB@& +LBQ0MC'9QCQCQCQCQC@9QCQGRD'KRCfCPC@9PCQGRCfGRCfGRD'KSD'KRCfCQC@9 +PC@9QCQGRCfGRCfKSD'PTD@KRCQ9MBQ"HA9aE@PTC@9PD@eaGAPpJB@&LBf4PCQG +SD'KSD'KRCfKSD@TVE'j[F(&bFh0cFh0bFR*aFA&aFA*bFh4eGRGjHRaqJ)'$KBD +(L)L)L)H(KiH(L)L)L)L)L)L*LBQ*LBL(KiD&KB5%K)1$JS'"J)#!JB'"JS+#Ji1 +$JS+#JS1$K)@&KS@&KB@&KB@&KBD(KiL*LSZ,M)b-MBk2NC18PTHBQCUDQTUCQCQ +BQ*LBPjHAPjLCQTbHRk#KSD'KSD#JRjkFQjQAPT@8Nj+4N!#2MSk0MBf1Mj!!NT1 +8PCDAPjHAQ*LBQ*QCQCLBQ*LAPjD9P*15NT+4NC'3!*!!N!#3!*'5Nj@AQCZGRk# +LSk5PTUHSUDUVUkZUUUQSU+QTUDUUUDQSU+LSUDUUUkZXV+fZX,+cY,@fYE@dXV' +[VUfXUkUSTk@MSD#HRCbFR*bGRCfGRCkHRjqJS*qJS+'KSU1MSU+KSD'KSU+LSD# +IRTfEQjUDQTUER*fHRTqJS+'LSk5QTkQUUUZUUUQSTkDPT+1KRjfDQ*D9Nj'3!)k +-LiU*LBQ*LSb0MT!!NC+5Nj58PCDBQCUER*bGRCfGR*bGRCfGRCfGR*fGRCkHRTk +GR*bEQTUCQ*LAPT@9P*58PC58P*16Nj16P*59PC@9P*15NC'4NC'5NT16Nj59PTH +BQ*LCQCQDQjZEQjUCQ*H@PT@8P*14N!#3!)q1MSf0MBf0MBf-M)b-M)b-M)b,LSL +(KS@%Ji+"J(pqIAeqIRjrIhq!J)+$KBH*Lib0MSq3!*!!NC'4NC'3!*!!Mik0M)b +,LSQ*L)Q*LSZ-MBk2Mj!!N!#4NC+5Nj@@PjHAPTD9P*15NT+4NC!!Mik0MBf1Mj! +!N!#4N!#3!)q2Miq2N!#3!*!!N!#3!)q1MSf0M)b-M)f0MBk1MSk1MSk0M)Z,LSU ++LBQ)L)H'KB@%K)5&KSH)L)L)KiH'KSH(L)L)L)L(KiD&KB@%K)1#JB"rIhq!J)" +rIhprIhprIhpqIRemHhTkHAKhGR9dG(4dFh0bFR*aFA&aFA&bFR*bFR0cFh0cG(4 +eGRGiH(PjH(KiGhCfGA4cFR&`F'pZE@aXE'aXE'YVDfTUD@KSCQ9PC'0MBf4NC'9 +QCQCQCQ9NBf*KB@&LBQ*LBQ*KB@*LBf4NC@9PC@CQCfGSD'KSCfCPC@9PC@9PC'4 +NBf0MBf0MBf*LBQ&KB&pHA9aFA&aGA9eHAPjHAf"KBQ0MBf0LB@"HA9aE@eYFA9j +HAf"KBf9RD@TXE@jZEfp`F("`F(&aFA&aFA&`F'p[EfjZE@eYE'aVDfTUD@PTD'K +RCfGRD'KTD@PTD@KRCQ9MBQ&KB'"JB'"JB'"JB&pIAepIAepIAepIAepIAepIAf" +JB'&KBQ*LBQ*KB@&KBQ0MBf0MBf*LBf0PCQGSD@PTD@TUDQTUD@KQC@4NBf0MC'4 +MBf0MBf0NC'9QCQGSD'PTDQYXEQpaFh4fH(TlI(epIAemI(alHhTjH(GhGR9dFh* +aF("`F("aFA&`EfpZE@YVDQPRCQ9NBf*LB@&KB@"JB'"JB'"JB@&KB'"JB&pIAep +IAepJB'"JB@&LBf0MBf0MBf*LBQ*MBf0MBf0MBf0NC'9PC@9PCQCQCQCQCQ9PC@9 +PCQCRCfKRCfGQCQCQCfKTDQYXE'eZEh"`FA*bFh4dGA9eG(4dFh0bFR*bFR0dGAC +iHATlIAk!JB1&KSH)L)Q*LBL)KiH(KiH(KiH(KSD'KiH(L)L*LSU,M)b-M)Z,LiZ +,LiU+LBQ*LBQ+LiZ-LiZ,LiZ-MBf1Mj!!N!#3!*!!N!#2Miq1MBf0MBf-M)Z+LSU ++LSU,LiU+LSQ*L)L)L)L*LSZ-MBk2Mj!!N!#4NC'4NC'4NC'4N!#2MBb-M)f1Mj' +5Nj@@Q*QDQjZFR*bFRCfGR*bFQjZDQCQCQCQCQCQCQ*LBPjD@PC56NT'3!*!!N!# +3!)q2Mik1MBf-M)Z,LiZ,LSU+LBQ*L)L)L)L*LBU,Lib0MBk2Mj!!N!#4NC+5NT+ +5NC'4N!#3!*!!N!#3!*!!N!#3!*!!N!#3!*'4NT19PTHBQCQCQCQCQ*LBPjHAPjH +APjHAPjLBQCQDQjZFR*fGRTqKSU1NTD@QTUDPTD@PTD@PTUDQTkHRU+LSTkHQTU@ +NT+1LSU'KS*qHRTfGRTkHRTfGRCbEQjZER*bGRTkIS+#JSD'KSU1NT+@PTD5NSk1 +LS*qHRCbEQjZEQTUDQTUDQTQBPjD9P*56Nj+5NC!!MSf-LiU*L)H(KiH(KiH'KSD +(L)L*LBU+LiZ,M)b-MBf0MSk1MSk1MSk2Mj!!N!#4NC'3!*!!N!#3!*!!N!#3!*! +!N!#3!*!!N!#3!*!!N!#3!*!!N!#3!*'4NC!!Miq1MBb-M)b0MBf0MBb,LiU+LSU +,Lib-M)f0MBk1Miq3!*'5Nj18P*@9PC@9PC@8P*16Nj18P*58PC@8P*16NC!!Mif +-LSQ*L)H'KB5#JB#!J)#!J)'"JB'"JB'#JS1%KB@'KiL*LBU,M)f2N!#4NT16Nj5 +8P*58Nj16NT'3!)q1MSf0MBf-M)b-M)f0MBk1Miq2Miq2MSf0M)Z+LBL)L)L*LBU ++LSQ*L)L)L)L(KiH'KS@&KB@&KBD'KS@&KB@%K)5%Ji1$Ji1#JS+#JB'!IhjpIAa +mHhTjH(GfG(0cFR*aFA"`Efp[Eh"`FA*bFh0dGAChGhKiHAPjHAPjH(GhGRCeGA9 +eG(4dFh0cG(4eGA9eG(9eGA9eGA9eGA4dFh0bFR&aF'pZEQeZEQjZEQp[Eh"`FA* +cGAChHAYmIRjrIhprIRjpIAalHhTjGhCeGA4cFh*bFA&`F("`F("`F("`F'p[EQe +YE'aVDfYUDQPSCfCQC@9NC'4MBf*LBQ0MC'9QCfKTD@TUDfaYEQp[EQjYE'YVDQT +UDQPTD'GRCQ9PC'4MB@"IAPeGA9eGA9eHAPjHA9eGA9eHAPpIAejGA9aFA&aGAPp +JB@*LBQ*LB@&JB'"IAepIB'"JB'&KB@&JB'"IAepJB'"JB'&KBQ0NC@9QCQCQCQ9 +PC@9PC@4NBf0LB9pIAPjGA9eGA9aFA&aFA&aFA9eHAepJB@&KBQ*LBf0NC@CRCfK +SD@PUDfaYEQj[Efp`F(&aFA&aFA&aFA*bFR&aF'p[EQjZEQeYE'aXE'aXE@eZEQp +`F(&bFh0cG(4eGAChGhKiH(KjHAPkHRYlHhYlHhYmI(apIAepIAamI(amI(apIAa +mI(YlHhYlI(apIRjrJ)'#JS1$Ji1$JS+#JB'"JS+#JB'"J)#"JB'"J(pqIAalHhY +lHhTkHRTkHAPkHRTkHRPjHAKiGhCeGA4dFh0bFR*bFR*bFh4eGRGiH(KiH(KhGRC +eFh0bFA"`F("`FA&aFR*cFh4dGA9eGA4dFh0cFh0cFh0dG(4eGACfGhKiHAPkHRP +jHAPjHATkHRYlI(apIRjqIhprIhpqIRjqIRjpIAepIAepIAepIAjqJ)'#Ji5%KB@ +%K)5$Ji+"JB#!J(prIhprJ)#!JB'"JS+#JS+#JS+#JS+#JS+$K)@'KiL*LSU,M)f +1Miq3!*!!N!#3!*!!N!#3!*!!N!#4NT+6P*@@PTHCQTZGRU#KSD+LSU+KSD'JS*q +IRTkHRTkHRTfGRCfGRCkHRTkGRCfGRCfHRjqJSD'KSD'KS+#IRjqHRTkHRCfFQjZ +DQTUDQTZER*bGRCfHRTkHRjqIRTkGRCbFR*bFQjZDQTQBQ*LBPjHAPjHAPjD@PC@ +9PC@@PTDAPjHAPjHBQ*LBQCQCQCQBQ*LBPjD@PT@@PjHBQ*QBQ*LBPjHAPjHAPTD +@PC56Nj+4NC!!N!#3!*!!N!#3!)q2N!#3!*'5Nj16P*58PCD@PjHAPjD9P*56Nj+ +5NT+5NT+5Nj18P*@@PTHAPjD@PC@9PC@9P*58Nj16Nj15NT+4N!#2MSk0MBf0MBk +1Miq3!*!!NC+6Nj59PC@9PC@@PTD@PC@9P*56Nj16Nj16Nj16Nj16NT+4N!#2MSk +0M)Z,LSU+LSZ,Lib0MBk1Miq2Miq2Mik1MBf-M)b-LiZ+LSQ*LBQ+LSQ*LBL)LBQ ++LSU,LiZ,M)b0MBk2N!#3!*!!Mik0M)Z+LBL(KSD&K)1$JS+#JB'!J)'"JB+#JS+ +#JS+#JS+#JS'"J)#!J)'"JB'!J)#!J)'"JS+#JS1$Ji1$JS+"J(pqIAalHRPjH(G +hGhCfGR9eGACfGhGiHAPkHRYmI(eqIhq!JB+#JS1$Ji1$Ji1$JS+#JB'"J)#!J(p +rIhjqIRepIAamHhYlI(apIAeqIRjqIRprJ)#!J)"rIRemHRTjH(GhGR9eG(4dGA9 +eGRCfGR9eGA4dFh0cFR*aF'pZEQeYE'aVDfYVDfYVDfYVDfYVDfTTD@KSD'KSD@P +UDfaYE@jZEQeYE@eZEQp`FA*cG(ChH(PkHhamI(amI(amI(amHhYkHAPiGhCeG(0 +cFR*bFh0dG(4eGA9eGA9fGRCfGR9eG(4cFh0cFh4dGA9eGA9eGA9fGhKjHAPjHAK +iGhCfGA4cFR&`F'p[Efp[EQjYE@aXDfYUDQTTD@KSCfCQC@9PC@4NBf0LBQ*KB@& +JB&pHAPeGA9eGA9jHAf"KB@&LBQ*LBQ0MC'4NC'4NC'4MBf*LBQ*LBf4PCQGRCfG +RCfGRCfKTD@TUDQTUDQYVDfaXE'aVDfYVDfYVDfaXE'aYE@jZEh"`FA*bFh0dG(9 +eGA9eGRCfGRCfGA9eGA9eGA4dG(4dFh0cFh0cG(4cFh0bFA"[EfjYE@eYE@eYEQj +ZEQjZEQjZEQp[EQjZEQj[Eh"`F("[Efp[Eh"`FA*bFR*bFR&aFA&aFA"`F("`FA* +cG(9fGhKiHATlI(epIRq!J)#"J)"rIhjpI(YlHhYmI(eqIi#!JB+#Ji1$Ji1$Ji1 +$Ji+#JB'!IhprIhprIhpqIRjqIRjqIRepIAamI(amI(apIAepIRjqIRprIhq!J)" +rIhjqIAamHhYkHRTjHAPjHRTlHheqIi'#Ji5&KSD'KiD'KS@%K)1$JS+"J(pqIAa +mI(YlHhYmI(epIRjqIhprIhq!J)#!J(prIhq!J)'#Ji5%KB@'KSD(KiH(L)L*LBU +,M)b0MSk2Miq3!*!!N!#3!*!!Miq1MSf-M)Z,LiZ,M)b0MBk1MSk1MSk2N!#4NT1 +8PCD@PTH@PT@9P*16NT'4N!#2MSb,LBH'K)1#JB#!J)#!J)'"JS+#Ji1$K)5%K)5 +%K)5&KBD'KSH(KiH(KSD&KB@&K)5&KB@'KSH)LBQ+LiZ-M)b0M)b-LiU*LBL)KiH +(KSD'KSD(L)Q+LSZ,LiZ-M)f1Miq3!*'4NC'4NC'4NT+6P*@9PTHAPjH@PT@9P*1 +6Nj+5NT+5NT16P*@9PTD@PjHAQ*LBQ*HAPjD9P*15NT'4NC'5NT+5NT+5NT+5Nj1 +8P*59PC@9PC@9PC@9PC@9P*58P*56Nj16Nj58P*58P*58P*56Nj15NT+5NT+5Nj1 +6Nj16P*@9PTHBQCUER*bGRTqIS+'KSD#JS*qIRTkGR*bEQTQCQ*H@PC56NT'4N!# +3!)q2MSf-M)b-M)f0MSk2Miq3!*!!N!#3!)q2MSf-LiU*LBL(KiH'KSD'KSD'KSH +(KiH(KiH'KSD'KSD'KS@&K)5$Ji1#JS'"J)"rIhprJ)#"JS1%K)@&KB@&KB@%K)1 +#JB"rIhprIhprIi#!J)#!JB'"JS+$K)5%K)1$Ji1$Ji1$Ji1#JS+#JS+#JS1$Ji1 +$Ji1$K)5&KB@&KB@&KB5%K)1$JS+#Ji5%KBD(L)L)L)Q*LSZ-M)f0MSk1MSf0MBb +-LiZ+LSQ*L)L)L)L(KiH(KSD'KSH(KiH(L)L)L)Q*L)L)KiD&K)5$JS+"J(pqIAe +qIRprJ)'"JS1%K)@&KB@&K)1$JS'"JB#!J(prIhpqIRjqIRjqIRjqIRprIhq!J)# +"JB'"JB'!J(prIhprIhjpIAalHRPiGR9dFR&aF("`F("`F("aFA&aFR*cG(4eGRC +fGRCfGA9dFh*aF'pZEQeYE@aXE'aXDfYVE'aXE'aYE@eYE@eXE'aVDfTUD@KSCfG +RCfGRCfGQC@9NC'4PC@CQCQGRCfKSD'PTDQYXE'eZEQp[F("aFA&bFR*cFh0cFh0 +bFR&aF("[F("`F(&aFA&aFA&bFR0dG(9eGRCfGRCfGhKiHAPkHRPjHAKiGhGfGRC +eGA9eGA9eGA4dG(0cFh0bFR*bFR*cFh0cFh0cG(4cFh0cFR*aFA"[EfjYE'YUDQP +SD'GRCfKSD'PTD@TUD@PTD'GRCQ9PC@9PC'4NC'4PC@CQCfGSD'PTDQTUDQTTD@P +SD'KSD'GRCfCQCQ9PC'0MBQ*LBQ*LBQ*LBQ*LBf0NC@9QCfKTDQYXE@eZEQp[F(& +aFR*bFR&aF'pZEQeYE@eYE'aXDfYUDQPTD@PSD'KSD'KSD'GRCfGRCfGRCfKSD'K +TD@PTDQTUDQTUDQPTD@PTDQTUDfYXE'eZEQj[Efp`F(&aFR*cG(4eGACfGRChGhG +iH(PjHATkHRPjHAKiGhGhGhKiH(PjHATkHhYlHhamI(amHhYlHhYlHhYmI(amIAe +qIhq!J)'"JB'"JS+#Ji1$Ji1%K)5&KB@'KSD'KSH(KiD'KB@%K)5%K)5%K)5&KBD +(KiL*LBQ*LSU+LSU*LBQ)L)H(KiH(KiD'KSD(KiL)LBQ*LSU+LBQ*LBQ*L)L(KiD +'KB@%K)5%K)5%K)@&KB@&KSD'KSD'KS@&KBD'KSH(KiH)L)Q+Lib-M)b-LiZ,LSU ++LSU+LSU+LSU+LSU+LiZ,LiZ,LiZ,LiZ,LiZ,LiZ,LiZ,M)b0MBf1MSk2Mj!!N!# +3!*'4NC+5Nj16P*58PC@9PC@9PC@@PTD@PTD@PjHAPjHAPjHAPTD9PC56Nj15Nj1 +6Nj16Nj16Nj16P*58P*@9PC@9PC58P*58Nj15NT+4NC'4NC+5NT+5NT+5NT16Nj1 +6Nj18P*59PC@9PC@9PC@9PC58P*16NT+4NC!!N!#3!)q3!*!!N!#3!*!!N!#3!*! +!NC'4NT+5Nj16Nj58P*58PC@9PC@@PTDAPjHAQ*LAPjHAPjHAPTD@PC@9PCD@PjH +AQ*LBQ*LAPjHAPTD@PTD9PC@9PC58P*58PC@8P*16NT'3!)q1MBb-LiU*L)H'KB5 +%Ji1#JS+#JS+#JS+$Ji5%K)5%K)5%Ji1$Ji1$Ji1$Ji1$K)5&KBD(L)Q*LSU,Lib +0MBk1MSk1MSk1MBf0MBf0MBf0MBf0MBf0MBk1MSq2Mj!!N!#4NC'5NT+6NT+5NC' +4NC!!N!#4NC'4NT+5NT+5NT+4NC'3!*!!N!#3!*!!N!#3!)q2Miq2Miq2Miq2MSk +0MBb-M)b-LiZ,LSU*LBL)KiH'KSD'KSD'KiH(L)L*LBU,LiZ,M)Z,LiZ+LSU+LBQ +)L)H'KB5$JB"rIRepI(YlHRPjH(KhGhGhGhGhGhGhGhGhGhGfGRChGhGhGhGhGhG +hGhGfGRCfGA9eGA9dG(4dG(9eGA9eGA9eGA9eGA9dG(4cFh*bFR*bFR*cFh0dG(9 +eGACfGhKiH(KiH(GhGhCfGR9eGA4eGA9eGA9eG(4cFh0cFh0cFh0cFh4dG(9eGAC +fGhKiHAPkHRTkHRTjHAKiGhGhGhGhGhGiH(KjHAPjHAPjHAPjHAPjHAKiH(KiH(K +iH(KjHATkHRTkHRTkHRTkHRPjHAPjHAKiGhGfGR9eGA9eGA9dG(4dG(4dGA9eGA9 +eGA9eGA9eGA4dFh0bFR*aFA&aFA&aFA*bFR*bFR&aF'p[EQjYE@eYE'aXE'YVDfY +VDfYXE'aXE'eYE@eYE@eYE'aXE'YVDfYVDQTTD'GRCQCPC@9PC@9QCQCRCfGSD'K +SD'KSD'KSD'GRCfGQCQ9PC'4NBf4NC@9QCQGRCfKSD'PTD@TUDQTUDQTUDfYVDfY +VDfYVDQTVDfYVE'aYEQp`F(&bFR0cFh0cFh0cFh0cFh0bFR*bFR*cFh0cG(4dGA9 +eGRChGhKjHATkHRTkHAPiH(KhGhCfGRCfGRCeGA9dG(4dG(4dG(0cFh0bFR*bFA& +aF("`F(&aFA&aFA&bFR*cFh4dG(4cFh0dG(4dG(4dG(9eGAChGhKiHATlHhamI(e +pIAepIAamHhYlHRTkHRTkHRTkHRTkHRYlHhYlHhamI(alHhYlHhTkHRPjHAKiH(K +iH(KiGhGhGhCfGhGhGhGhGhGhGhGhGhKiH(PjHAPjH(KiH(KiH(KiH(KiH(KjHAP +kHRTlHhapIAjrIi#!JB'"JB'#JS+$Ji1$Ji1%K)5%K)1$Ji5%K)@&KSD'KiH(KiH +(KSD&KB@&KB@&KB@&KB@&KSD'KSD&KB5%K)1$Ji1$Ji1$Ji5%KBD(L)L*LBU+LSZ +,LiZ,LiZ,LiZ,LiU+LSQ*LBQ)L)L)L)L)L)L(KiH(KiH(KiD'KSD&KB@&KBD'KiH +(KiL)L)Q*LSZ,LiZ,LiZ,LiU+LSU+LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBU ++LiZ,Lib-M)b-M)b,LiU+LBQ)L)L)KiH(KiD(KiL)LBQ+LSZ,Lib-MBf1MSq2N!# +3!*!!N!#3!*!!Miq2Miq2Miq2Mik1MSq2Mj!!N!#3!*!!N!#3!*!!N!#4NC'4NC' +4NC!!N!#3!)q2Miq1MSk1MBf-M)b-M)b0MBk1Mj!!NC'5Nj58PC@9PC@9PC@9PC@ +@PTD@PTD@PTD@PTD@PjHAPjLBQCUDQTZER*bGRCfHRTkHRTkGRCfFR*bFR*bFR*b +FR*ZEQjZDQTUCQCLBPjHAPTD@PT@9PC@9PC@9PC@@PTDAPjHBQ*LBQ*LBQ*HAPjH +@PT@8P*15NT'4N!#3!*!!Miq2Mj!!N!#3!*!!NC'3!*!!N!#3!)q2Miq2Miq2Mik +1MSf-M)Z,LiZ,LSU+LSQ*L)L)L)L(KiH(KSD'KS@&KB@&K)5%K)5$Ji1$Ji1$Ji1 +#JS+"JB'!J)#!J)#"JB'"JB'"JB'"JB#!J(prIhpqIRprIhprIi#!JB'#Ji1%K)5 +%K)5%Ji+#JB#!IhpqIRjqIRjqIRjqIRprIi#!J)#"JB'"JB#!IhprIRjqIRjqIRj +qIRjqIRjrIhq!J)#"JB'#JS+#Ji1$K)5$Ji1#JS+"J)"rIhpqIRjqIRjqIRjrIhp +rIhprIhprIhq!J)#!J)#!J)#!J(prIRjqIRjqIRjqIRjqIRjqIRjqIhprIRjqIAe +mI(amHhYkHRPjHAKiH(GhGRCfGRCfGR9eGA9eGA9eG(4dFh0bFR&aFA&aFA&aFA* +bFA&aFR*bFh0cG(4dFh0cFh0cFh0cFh0cFh0cFh0dG(4dG(4cFh0cFh0cFh0bFR* +bFR*aFA&`F'p[EfjZEQeYE@aXE'YVDfYVDfYVDfYVDfYUDQTUDQTUDfYVDfaVDfY +UDQTUD@PTDQTUDQYVDfaXE@eZEQp[F("`FA&aFA&aFA&`F("`F("`FA&aFA*bFR* +bFR*bFR*bFR*bFR*bFR*bFR*bFR*bFh0cFh4dG(4cFh0cFh0dG(9eGRChGhKiHAT +kHRTkHAPjH(KiGhGhGhCfGA9dG(4dG(9eGA9eGRChGhKiH(KiH(KhGhGhGRCfGA9 +dG(4dG(4dG(9eGAChGhKjHRTlI(apIAeqIRjqIAepIAamHhYkHRPiH(KiH(GhGhG +fGRCfGRCfGRCfGR9eGA9eGA9eGA4dG(0cFh*bFR*bFR*aFA&aFA&aFA&aFA&aFA& +bFR*bFh0dG(9eGA9eGA9eGA9eGA9dG(0cFR*bFA&aF("`F("aFA*bFh4dGA9fGRC +hGhGhGhGhGhCfGA9eGA9eGRCfGRChGhGiH(KiH(KiH(KiHAPjHAPjHRTkHhamIAe +qIRjrIhprJ)#!JB'"JB'"JB'#JS+$Ji1$K)5%K)5%K)5%K)5%K)5$Ji1$Ji1$Ji1 +$K)5%K)5&KB@&KB@&KB@&KB5%K)5%Ji1$Ji1$JS+#JS+$Ji1$K)5&KB@'KSD(KiH +)L)L)L)L)L)L)L)L)L)L)KiH(KiH(KSD'KS@&KB@&KB@&KB@&KB@&KB@&KB@%K)5 +$Ji+#JS'"JB'"JB'"JB+$Ji5%K)5&KB@&KB@&KB@&KB5%K)1$Ji1$Ji5%Ji1$Ji1 +$JS1$Ji1%K)5&KB@'KSD(KiH(KSD'KSD'KS@&KB@&KB@&KB@&KSD'KiH)L)Q*LBU ++LSZ,LiZ,LiZ,M)b-MBf0MBk1MSk1MSk2Miq1MSk2Miq2Mj!!N!#3!*!!NC'4NT+ +5NT+5Nj+5NT'4NC!!N!#3!)q2Miq1MSk1MSk1Miq2Miq2Mj!!N!#3!*'4NC'4NC' +4N!#3!*!!N!#3!)q2Miq1MSk1MBf0MBf-M)b-M)b0MBf0MBf0MBf0MSk1MSk1MSf +0MBf0M)b-M)b-LiZ,LiZ,Lib-M)b0MBf1MSk1MSk1MBf0M)b,LiU+LSQ*LBQ)L)L +)L)L)LBQ*LBU+LSU,Lib-M)f0MBf0M)b-M)b,LiZ,LiZ+LSU+LSU*LBQ*LBQ*LBQ +)L)L)L)H(KSD&KB@&KB5%K)5%K)5%K)5%K)5%K)5%K)5%KB@&KB@'KSH(KiH)L)L +(KiH'KSD'KB@&KB@&KB5%K)5%K)5%KB@&KB@'KSD'KSD'KS@&KB@%K)5$Ji1#JS' +"JB'!J)#"JB'"JS+#Ji1%K)@&KBD'KS@&KB@&KB@&KB@&KB@&KB@'KS@&KB@'KSD +'KSD'KiH(KiL)L)L)L)L)L)L)L)H(KiH(KSD'KSD'KSD'KB@&KB@&KB@&KB@&KB@ +%K)5$Ji+"JB#!IhpqIRjpIAemI(amI(amI(YlHhYlHhYlHhYmI(amHhYkHRTjHAP +jHAKiGhGfGR9eGA4dFh0cFR*bFR*bFR0cFh0dG(9eGRCfGRCfGRCfGRChGhGhH(K +iHAPjHAPjHAPiH(KiH(KiH(KiH(KiH(KiH(PjHAPjHAPjHAPjH(KhGhCfGRCfGRC +hGhGhH(KiH(PjHAPjHAPjHRTkHhYmI(epIAepIRjqIAepI(YlHRPiH(KhGhGfGRC +fGRGhGhKiH(KiH(KiH(KiGhGfGR9eG(4dG(0cFh0cG(4dGA9eGRCfGhGhGhGhGhC +fGRCfGA9eG(4dG(4dG(4dG(4dG(4dFh0cFh0bFR*bFR*cFh0cFh4dG(4dGA9eGA9 +fGRCfGRCfGRCfGRCfGR9eGA9dG(4dG(4cFh0dG(4dG(4dFh0cFh*bFA&`F("`F(" +`F("`F("`FA&bFR*cFh0dG(4dG(4dFh0cFh0cFh0cFh0cFh0dG(4dG(4dG(0cFh0 +cFR*bFR*cFh0cFh0cFh0cG(4dG(9eGA9eGA9eGRCfGRCfGRGhGhGhH(KiH(KjHAP +kHRTlHhYmI(amI(epIAepIAepIAemI(YlHRTkHAPjHAPjHAPkHRTkHRTkHRPjHAP +iH(GhGRCfGRCfGRChGhGiH(KjHATkHhYmI(apIAeqIRjqIRjpIAepIAepI(amI(a +lHhYlHhYkHRTkHRTkHRTkHRTkHRYlHhYlHhYmI(amI(YlHhTkHRPjHAPiH(KjHAP +kHRYlI(amIAeqIRprIi#!J)#!J)#!JB'"JB+#JS+#JS+#JS+#JB'"JB'#JS+$Ji1 +%K)5%K)@&KBD'KSD'KSH(KiH(KiH(KiH(KiH(KiH(KSD'KSD'KSD'KSD'KSD(KiH +(KiH(KiH(KiH(KiL)L)L)LBQ*LBQ+LSU,LiZ,LiZ+LSU+LBQ*LBL)L)L)L)L)LBQ +*L)L)L)L)L)L)L)L*LBQ*LBU+LSU+LSU*LBQ*L)L)L)H(KiH(KiL)L)L)LBQ*LBQ ++LSU+LSU+LSQ*L)L(KiD'KS@'KSD'KSD(KiH(KiH(KiH(KiH)L)L)L)L(KiH(KiH +(KiL)L)L)L)L)L)Q*LBU*LBQ)L)L(KiH'KSD'KSD'KSD'KS@&KB@&KB@%K)5%K)5 +%Ji1$Ji1%K)5&KB@'KSH(L)L)L)L)L)L)L)L)L)H(KiH(KiD'KSD'KSD'KiH(KiH +(L)L)L)L)LBQ*LBQ*LBQ*LSU+LiZ,M)b0MBf0MSk1MSk1MSf0MBb-M)b-M)b-M)b +0MBf0MBk1MSk2Miq3!*!!N!#3!*!!N!#3!*!!N!#2Miq2Miq2Miq2MSk1MSk0MBf +0MBf0MBb-M)b-M)b-M)b-M)Z,LiU+LSQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBL)L)L +(KiH'KSD'KB@&KB@&KB@&K)5%K)5%K)5%K)5%K)5%K)1$Ji1#JS+#JB'"JB'"JS+ +#JS+$Ji1%K)5%K)5%K)1$JS+#JB'"JB'!J)#!J)#!J)#!J)#!J)"rIhprIhprIhj +qIRjqIRjpIAepIAemI(amI(amI(amI(amI(amIAepIAjqIRjqIhprIhprIhprIhp +rIhjqIRjqIRjpIAepIAepIAepIAepIRjqIRjqIRprIhprIRjqIRjqIRjqIRjqIRj +qIhprJ)#!J)#"JB'#JS+#Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$JS+#JS'"JB'!J)#!J)# +!J)#!IhprIhjqIRjpIAepIAepI(amHhYlHRTkHAPkHRTkHRTkHRYlHhTkHRTjHAP +jH(KiH(KhGhGhGRCfGR9eGA9eGA9eGA9eGA9eGA9eGA9eGA9eGA9eGA9eGA4dG(4 +dG(4cFh0cFh0dG(4dG(4dG(4dG(4dG(4dGA9eGACfGRCfGRCfGRCfGA9eG(4dFh0 +cFh0cG(4dGA9fGRChGhGhGhGhGRCfGRCfGRCfGA9fGRCfGRCfGRGhGhGhGhGiH(K +iH(KhGhGhGhGhGRCfGRCfGR9eG(4dG(0cFh0cFh*bFR*bFR*cFh0cFh0cFh4dG(4 +dG(4cFh0cFh0cFh0cG(4eGA9fGRChGhGhGhKiH(KiH(KiH(PjHAPjHAPjH(KiH(K +iH(GhGhGhGhGhGhGhGhGhH(KiH(KiH(KiHAPjHAPjHAPiH(KiGhGhGRCfGRChGhG +hH(KiH(PjHAPkHAPjHAKiH(KhGhGhGhCfGRChGhGhGhGhH(KiH(KiH(KiH(KiH(P +jHRTlHhamI(epIAepIAepIAepI(amI(amIAepIAepIRjqIhprIhprIhprIi#!J)" +rIhprIhprIhprIhpqIRjqIRprIhprJ)#!JB'#JS1$Ji1$Ji1%K)5%K)5%K)1$Ji+ +#JS+#JS+#JS+#JS+#JS+$Ji1$Ji1$Ji1$K)5%K)5%K)5%K)5%K)5$K)5%K)5%KB@ +&KB@&KB@&KBD'KSD(KiH(KSD'KS@&KB@&KB@%K)5%K)5%K)5%K)5%K)5%K)5%K)5 +%K)5$Ji1$Ji1$Ji1$Ji1%K)5%K)5%K)5%K)@&KBD(KiL)L)Q*LBU+LSU+LSU+LSU ++LBQ*L)L)L)L)L)L)L)L)L)L)L)L)L)L(KiH'KSD'KB@&K)5%K)5$Ji1$Ji1$Ji1 +$Ji1$K)5%K)5%K)5&KB@&KB@&KB@%K)5%K)5%K)1$Ji1$Ji1$Ji1$K)5%K)5%K)@ +&KB@&KB@&KB@%K)5%K)5$Ji1$Ji1%K)5%K)@&KBD'KSD(KiH(L)L)L)H(KiH(KiH +(KSD'KSD'KSD(KiH(L)L)L)L)L)L)L)L(KiH(KiH(KiH)L)L)LBQ*LBQ*LBQ*LBQ +*LBQ*LBQ)L)L(KiH(KiD'KSD'KB@&KB@&KB@&KB@&KBD'KSD(KiH)L)L*LBQ*LBQ +*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LSU+LBQ*LBL)L)L)KiH(KiD'KSD'KiH(KiL)L)L +)L)H(KiH(KiH(KSD'KSD'KSD'KSH(KiH(KiH(L)L)KiH(KiH(KSD'KSD'KSD&KB@ +&K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5$Ji1#JS+#JS'"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'"JB'"J)#!J)#!IhprIhprIhq!J)#!J)#!J)#"JB' +"JB'"JB'"JB'"JB+#JS+#JS+#JS+#JS+#JB'"JB#!J)#!IhprIhprIhq!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)'"JB'"J)#!J)"rIhpqIRjpIAepIAepI(a +mI(amI(amI(amI(amI(amI(amHhYlHhYlHRTkHRTkHAPjHAPjHAPjHRTkHRTkHRT +jHRTkHRTkHAPjHAPiH(KiH(KhGhGhGhGiH(KiH(KiH(KiH(KiH(KiH(KiGhGhGhG +hGhGhGhGhGhKiH(KiHAPjHAPjHAPjHAKiH(GhGhGfGRCfGA9eGA9fGRCfGhGhGhG +hH(KiH(PjHAPjHAPkHRTkHRTjHAPjHAPjHAPiH(KiH(PjHAPjHAPjHAPkHRTkHRT +kHhYlHhYlHhYlHhYkHRTkHRTjHAPjH(KiH(KiH(KiH(KiH(PjHAPjHAPjHAPjHAP +jH(KiH(KiH(KhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhGhKiH(K +iH(KiH(KiHAPjHATkHRYlHhYmI(amHhYlHhYlHhYlHhYlHRTkHRTkHRTkHRTkHRY +lHhYkHRTkHRTlHhYlHhYlHhamI(amI(amI(amI(amI(amHhamI(amI(alHhamI(a +mI(alHhYlHhTkHRTkHRPjHAPjHATkHRTkHRTkHhYlHhYmI(amI(amI(amI(amI(a +mI(amI(amI(amIAepIAepIAeqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIAepIAe +pIAepIAepIAepIAeqIRjqIRjqIRprIhprIhprIhprIhprIRjqIRjpIAepI(amI(Y +lHhYlHhTkHRTkHRTlHhYlHhYlHhYlI(amI(amI(apIAepIRjqIRjqIRjqIhprIhp +qIRjqIRjqIRjqIhprIhq!J)#!J)#"JB'"JB'"JB'"JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#JS+#JS+$Ji1$Ji5%K)5%K)@&KB@&KB@'KSD'KS@&KB@&KB@&KB@&KB@&KBD +'KSD'KSD'KB@&KB@&KSD'KSD'KSH(KiH(L)L)L)L)L)L)LBQ*LBQ*LBU+LSU+LSU ++LSU+LiZ+LSU+LSU+LSU*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBQ*LBL)L)L +)L)H(KiH(KSD'KSH(KiH(L)L)L)L*LBQ*LBQ*LBQ*LBQ*LBL)L)H(KiH(KSD'KSD +'KB@&KB@&KBD'KSD'KSD(KiH(KiH(KiH(KiH'KSD'KS@&KB@%K)5%K)5%K)5%K)5 +%K)5%K)5%K)1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji5%K)5 +%K)5%KB@&KB@&KB@'KSD'KSD'KiH(KiH(KiH(KiH(KiH(L)L)L)L)L)L)L)L)L)L +)L)L)L)L)L)L)L)L)L)H(L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L)L +)L)L)KiH(KiH(KiH(KSD'KSD'KS@&KB@&KB@&K)5%K)5%K)1$Ji1$JS+#JS+"JB' +"JB#!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhpqIRjqIRjqIRe +pIAepIAepIAepIAepIAepIAepIAepIAepIAepIAjqIRepIAepIAepIAepIAepIAe +pIAepIAepIAepIAepIAepI(amI(amI(YlHhYlHhYlHhYlHhYlHhYlHhYlHhYlHhY +lHhYlHhYlHhYkHRTkHRTkHAPjHAPjHAPjHATkHRTkHRYlHhYlHhYlHhYlHhYlHhY +lHhamI(amI(amI(amI(amI(amI(amHhYlHhYlHhamI(amI(amIAepIAepIAepIAe +pI(amI(amI(amI(amI(amI(YlHhYlHhYlHhYlHhYlHhYlHhYlHhamI(amI(amI(a +mI(amHhYlHhYlHRTkHRTkHRPjHAPjHAPjHAPjHAPjHAPjHAPjHAPjHAPjHAPiH(K +iH(KiH(KiH(KiHAPjHAPjHAPjHAPjH(KiH(KiH(KiH(KiH(KiH(PjHAPjHAPjHAP +jHRTkHRTkHRTkHRTkHRPjHAPjHAKiH(KiH(KiH(KiH(KiH(PjHAPkHRTlHhYlHhY +lHhYlHhYlHhYlHhYlHhYlHhYmI(amI(amI(amI(alHhYlHhYlHhamI(amI(amI(a +mI(amI(epIAamI(amI(amI(amI(amI(amI(amIAepIAepIAepIAepIAepIRjqIRj +qIRjqIhprIhprIhprIhq!J)#!J)#"JB'"JB'"JB'"JB'"JB'"JB#!J)#!J)#!J)# +!IhprIhprIhprIhprIhprIhprIhprIRjqIRjqIRjqIRjqIRjpIAepIAepIAepIAe +mI(amI(amI(amI(amI(amI(epIAepIAepIAepIAepIAepIAepIRjqIRjqIRjqIRj +qIRjqIRjqIRjqIRprIhprIhprIhprJ)#!J)#"JB'"JB'"JB'"JB+#JS+#JS+"JB' +"JB'"JB'"JB'!J)#"JB'"JB'"JB'"JS+#JS+#JS+#JS+#JS+"JB'"JB'"JB+#JS+ +#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JB'"JB'"JB'"JB'"JB'"JB' +"JS+#JS+#JS1$Ji1$K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5%K)5 +%K)5%K)5%K)5%K)5%K)5%K)@&KB@&KB@&KB@&KB@%K)5%K)5%K)5%K)5%K)5%K)5 +%K)5%K)5%K)5%K)5%K)5%K)5%K)1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1 +$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1#JS+#JS+#JS+#JS+#Ji1$Ji1$Ji1 +$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$JS+#JS+#JS+ +#JS+"JB'"JB'"JB'#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1$Ji1#JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS1$Ji1$Ji1$Ji1$Ji1$Ji+ +#JS+#JS+#JS+#JS+"JB+"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB#!J)#!J)#!J)#!J)#!J(prIhprIhprIhprIhprIhprJ)# +!J)#!J)#!IhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)#!J(p +rIhprIhprIhprIhprIhprIhprIhprIhprIhprJ(prIhprIhprIhprIhpqIRjqIRj +qIRepIAepIAepIAepIAepIAepI(amI(amI(amI(amI(amI(amI(alHhYlHhYlHhY +lHhamI(amI(amI(amI(amI(amI(amI(amI(amI(amI(amI(epIAepIAepIAepIAe +pIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAamI(a +mI(amI(amI(amI(amI(apIAepIAepIAepIAepIAepIAepIAemI(amI(amI(amI(a +mI(amI(epIAepIAepIAepIAepIAemI(amI(amI(amI(amI(amI(amI(amI(amI(a +lHhYlHhYlHhYlHhYlHhYlHhYlHhYlHhYmI(amI(amI(amI(apIAamI(amI(amI(e +pIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAepIAe +pIAepIAepIAepIAepIAepIAepIAepIAepIAepIAeqIRjqIRjqIRjqIRjqIRjqIRj +qIRjqIRjqIRjqIRjqIhprIhprIhprIhprIhprIhprIhprIhprIhprIhq!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#"JB'"JB'"JB'"JB'"JB'"J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#JS+#JS+#JS+#JS+#JS+"JB'"JB'"JB'#JS+#JS+#JS+#JS+#JS+#JS+#JS+ +#JS+#JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"JB'"JB'"JB'"JB'"JB'"JB'!J)#!J)#!J)#"JB'!J)#!J)#!J)#!J)#!J)#!JB' +"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB'"JB' +"J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIRjqIRjqIRjqIRjqIRjqIRj +qIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRj +qIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRj +qIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRjqIRj +qIRjqIRjrIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhprIhp +rIhprIhprIhprIhprIhprIhprIhq!J)#!IhprIhprIhprIhprIhprIhprIhq!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J!!!0%!!!3!"!!8 +!!!#J!!'!83!!!!!!&!!!!!!!!$3@9Zk,S`!!0"3!!$39!$b!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!IhprIi#!JB+#JS+#JS+#JS+"JS+"J)#!IhjqIi#!JB+#Ji1$K)5 +'LBf2N!#3!)Z$H@eLA&YEA@*UFhb"K)5$J(elHRPiH(PmIi'#JB#!JB'!IhprIi# +"Ji5$JS'"JB+#JS+#JS1$JS'!IhjqIAepIS##K)D)LSk8Q*UDQC@3!)Z%Hh"M9e* +59epSFAKmIAPaD@0KBQG`Ghb!JS1&KiH&K)5%Ji+!J)'$KSH(KS@%Ji1&KiL(KS@ +%JS'"J(pqIRjqIi##Ji5(LSb-LiZ-Mj+9Q*UCPj@6N!#-K(KS9$md0$8j490MFRb +"J(elHRKfGRKlJ)@,M)U'K)+!J)#!JS5&KiQ+LSU*L)D%Ji1$Ji+#JB'"IhprIhq +"Ji@(LBZ0MSk-LiU+MC'9QTfIS++HNAjM5$-S*b`e2dG199aQFAKmIRelHAGjJSf +8PT52LBD'KSH*LSQ)Ki@&KiL)KS5$JS+#K)D)L)D&K)1#JB'"JS1%KBD(KiH)LSb +1MT!!NjQJTULSTCq4Hf&)0#NP*#3V1%C2@@GfI(eqJS1"Ii'*NTH9MiZ,LSL(KSD +(L)D%K)D)LBL(KiD&Ji5(LSb,LSL'Ji"pHRTmIS'$KBL+LiZ+LSU,MBk3!*1AR+' +RUkQDJ@4*-b)A$J`B+cpAFB5-N!#3!)k*JRamJSb8Pj+1LiH"Ii##JS'"J)''LSk +2MSb*Ki@#JiD)KiH(Ki@%JRppIAamIAq$KiU-M)Z+LBL)Lj'ARD1RV+kSPATC04S +1$K3L08GCEi1,M)U(KB*lGRL!M*@AP)q+K(epIi'#JS1%L)b1MSk1M)Q&JB#"KBH +)LBQ)KB*pHAKhH(PmJB@*Lib-LiU)L)Q-NTUKTkkbVU+,ENmc(J`!"4Nd9hQ2QCH +-JAalH(9fIBD1NC!!Mib(JhprJ)"qIAq%Lj!!NT+3!)f*KS5$Ji@)LBL(KS5#IRT +iGhCfHAq&Liq4NC!!M)Q(KSH,NCHESUQXTT9pAd)S&!S2)$TCH)kAPSf#HRCdFA& +iJSZ4PC53!)Z%IhepIAapJ)@+MT+8PC12LSD#J)'%KiL)L)H&JRjlGh9dGRZ!KBQ +0N!#4Mib*KiH+Mj5DSDHUU*k,F&8p*a!###0(EBUCR*1(I(GeFh&cHi5,MT!!Mif +,Ki*rIRjpIS+(M*!!Nj@@Nik*KB'!JB5'KiH(KS1!HhKfG(4hI)+)MC!!NC!!MSU +)KiQ0NTLGSUDNQiPa@%!V&``9-&"[KjDBN!#%HhCdF@pbHS1,Mj'5NBk*Ji#!Ihe +qJBD-N!#8PTD8MiU'JS#!JS5'KSD&Ji"pHACdFh9kJ)D-NC16NBk-LiZ1NC@DRU' +IPiG`9$SX+#FR,N*KIC!!PT''IAKfG(*bH)+0P*@5M)L&K)1#J(jrJBD,Mj1@Q*H +6M)D"IRq#K)@&KB5$JAjlH(GhH(TrK)U1NC'3!)k,LBQ,Mj1BRD#KQieiB8Xe)43 +4(MjQKCLGPSU!HRGdFR4jJ)H0NC+4N!#1LSD$IhamIi@+Mj1@PjD6MSQ&JB#!JS+ +#Ji5&Ji&qHRGfGhTrK)Q1NC+5Mif,LSZ-N!#@QjkIQiplBdXf*KX9(6TKJTDFQ)k +&IAPeFh0iJ)L1NC'2MBf-LSD#IheqJiL0NC@@PT11LB@#JB+#JS+$K)5$JAjkGh9 +eGhb$LSk4Nj+2MBb-MBq4P*QGRjZ1HQ0+0#FH'#!kAS#@RTU3!)CrHhGeG(GqKSb +2Miq1MSf,Ki*qHhb"KSZ2NjDAP)q+KS1#Ji1#JS+#JS+"IhajGhGjIB'(MC!!NT+ +2M)Z,MBq5PTZHRjb4IfP41bJB$"3hBS1BRjQ2Ki&pHACfHAq'LSf0MBk2MSZ(JAe +mIB#&LSk5P*55MSU(KB1#JB#!J)+$Ji+!IATiGhKlJBH,MT!!Mik-Lib0MT!!Nj@ +CQjL2J'a@3#iI&4`mC)@CRjQ2KRpkH(9eH(q'LSb-M)f1MBU'JAemIB'&LBf4Nj1 +5MSU(KB1#J(prJ)'#JS&rI(ThGhPmJBH-MT!!Mik-LSQ,MC'6PCLCPSk!E9G#-5) +A(cpQKjUHQ)k'J(ekGh9iIS5*M)f0MBk0LS@"IReqJ)5)M*!!NT14MSU(KB1#JB# +!J)'#JS&rIAYjHAPmJBH,MSk1MBZ*LBU0NC@AQCQAMi&Z@83b)48I3'L*QTfAMSD +!I(KfGRPrKBU0MBf0MSf+KB&pHharK)L-N!#6Nj'1LSH&Ji'!Ihq!JB+$JS"qHhP +iHAb"KSU0MSf-LiQ*LSb2NjDAQ*H2JR&F4c)I&L4)EifERCD0KS"mHAKiHRk$KiU +,M)f2MSU&J(emIB#%L)Z2NC+3!)f+Ki@$JB"rIi##JS+#J(elHAKjI)+'LSb0MBZ ++LBL*M)q6PTHAPBq%Fej*-ad6*NphNCZDNiZ&IhYkHATmIi1(LBU-MSq1LB0qI(a +pJ)5)M)q4NBq0LSL'Ji"qIRq!JS1$JB"pHRKhHAb"KSU,M)b,LBL)LBZ0NC5@PT5 +2K(9K5M)G&La@Hj'EQC+,KAplHAPjHhk"KBL+M)k2MSQ%IhYkI)#$KSU1NC'2MBU +)KS1!IReqJ)'#JS+!IATiGhPmJBD*Lib-LiQ)L)Q+MC!!Nj59P)q%G'"*-"`C-Pf +!P*UAN!#+K(jlHRTkI(k"K)H*Lik3!)k*K(pmI(f!JiD+MT'4Mif,LBD$J(jqIS# +"JB+"IhekH(KjIB+'LSb-M)Z*L)L*Lik4Nj@9NSb"F9a&,4XG2'D&PTU8MSL#IAY +kHRYmIS'%KSL,MT!!MSQ%IhepIS#$KSU1NC!!MSf,LBH$IhepIRq!JB'"IhakH(K +jIB+'LBZ,LiU*LBQ+M)q4NT58Nib!EPK!+"JK4h#,PjL6MBH"IAYlHhapIi'$KSL +,Mj!!MSQ%J(jqIi#$KiZ1N!#2MSf-LSH$J(jqIRq!JB'"IhekH(PlIS1(LBU,LiU +*LBQ+M)q4Nj56N!#)HfK51L-A,&GlNCQ@MiU&IhYkHhapIRq"Ji@(Lik2MBL$J(p +rJ)'$KiZ1Miq0M)b+Ki1!IRjqIhq!J)"rIATiHAZ!K)H*LSU+LBL)LBb1N!#4NT+ +4MS9hC8ie(KNhBi18Q*50L)0pHRYmIAjrJ)'#K)D+MBk-L)1"J)#!JB5)M)f1MBb +-LiQ'Ji"rIRjrIi#!J(pmHRPkIB'&KiL*LSQ*L)L+M)q5NT'4MiTrEeP#+KNQ6hD +0Q*H3!)U&J(akHhaqIi#!JB+%KiZ0M)U'Ji+"JB'#KBQ-MBf-M)b,LBD#J(pqIRj +qIRprIAYjHAb!K)D)LBQ*LBL)LBZ1NC+5NBq-KAPS8MSN($9IJ*+BP)f(JRekHha +qJ)#!JB'#JiD+M)U(KB1$Ji1#JiD+M)f-M)b-LiQ&JS"rIRjqIRjqIRekHAYqJSD +(L)L)L)L)L)U-Mj'5NT+3!)Z#G&p(,"JK5A',Q*L4LS@!HhTmIRq"JB'"JB+&L)U +,LBD%Ji1$Ji1&L)Z-M)Z,M)Z+Ki5#J(prIhprIRjpI(PjI)#%KiL*LBL)L)L*LSf +2N!#4NC'0KhYU8cSM(6GKJC5CNib'JAekHRarJB+#JS'"JS@)LSQ(KB5%K)5$K)D +*M)b-LiZ-LiQ'K)+"IhprIRepIAakHATqJiH)LBQ)L)H(L)Q-Mj'4NT'2LS"aA88 +X'LC1GBkCPiq)JhjkHAYpJB1$JS+"JB+&KiL(KB5%KB@%K)@)Lif-LiZ,LiZ*KS5 +$JAprIRepI(YkHAPmJB@)LBU*L)H(KiL+MC!!NC+4MiZ%HQP814dB1'@'PjU6Li9 +rHRKjI(q#K)5$JB'"Ji@(KS@%K)5&KB5%KSQ-M)U+LiZ,LSH&K)1"IhjqIAemHhP +jHRq$KiQ+LSQ(KiH'L)U1N!#4N!#3!)k)Ih&G45F9+eTqP*Z@MBH"I(PiHRf"K)@ +%JS'"JS5'KS5$Ji5&KB@&KSL+LiU*LSZ,LSL'KB5#J(jqIAamHhPiHAb"KBL+LiU +)KiD'KiQ0N!#4N!#2MBU$GfC40"SK5R11QjQ3!)Q$I(KhHAYrJiD'Ji'"JB1&KB5 +$Ji5&KSD'KSH*LiZ+LBU,LiQ(KSD&Ji"qIRemHhPhH(Z!K)H*LiZ*KiD'KSL,MT! +!N!#3!)k,KRaX9MNH(d*XLCLDNSU%IAKfH(TqJS@'K)+"JS1%K)1#JS+%KSD'KSD +)LSZ+LBU+LSQ(KB@'KB&rIRemHhPiH(TqJSD)LSZ,LBH'KSH+MT!!NC'2M)GrF&a +"*4`hBB+@Qj@0KRpjGRGjI)#%KSD%JS+#Ji1#JB'#K)D(KiH'L)U,LiU*LSU*L)H +'KiH%JAjpI(YjGhCjIB'&KiQ,LiQ(KSD'LBb3!*'3!)q-L)*hC%XU&LP@Hj+FQ)q +)JATfGRKlIi1&KS@$JS+#Ji+"J)'$KBH(KiD(LBZ+LBQ*LBQ)KiD(L)D$J(ppHhP +hGRGlJ)5'L)Z-LiL(KS@(Lik3!*'3!)k*JRPT9$8C)%TcMTbENiZ%I(CeGhTqJS@ +(KS5#JB'#JS#!JB+&KiH(KSH*LSZ+L)L*L)L(KSH*L)@"IhemHRKfGhTrJi@(LSb +-LSL'KSH+MBq2Mik+JhYY@MmL'caTLCUGPSf'IAGdGRPpJB@(L)D%JS'"JB"rIi+ +%KiL(KiH)LSZ+LBQ*L)H'KSH*LSH$J(jpHhKfGAKpJB@(LBb-LSL'KBD*M)k2N!# +2M)CrFf*++aJY@i#@RTQ2L)&jG(9iI(q%KiL(KB1"JB'!Ihq"Ji@(KiH(L)U-LiQ +*L)L(KS@'LBZ+KS&rIRakGR4fHi#%KSL+M)b,LBH'KiZ0Mj!!MSb)J(9N6M!A*&& +lNjkFNiU$HR4cGRTqJSD)L)D%JS'"J(jqJ)+&KiH(KiL+M)b+L)L)KS@&KBL,LiH +$J(jpHRGdGAPqJS@(LSb0M)U)KSD*M)k2Mif*JhPV9c`J($pYLjZHPBb&IA9bG(K +pJB@)LBL&JS#!J(pqIi'%KSH(KiH*Lib,LBL)KiD%K)H,M)U%J(pqI(PeG(GmJB5 +'LBZ-M)Z*L)H*M)k2Mik+K(YY@6iK'$CQLCZIQ)q)IhGcFhGlJ)5)LBQ'Ji'!J(p +qIS#%KSH(KiH)Lib,LBL)Ki@%K)@*M)Z'JRpqI(TfG(ClJ)1&L)U-M)Z*L)H)LSk +3!)q1LS9qFQ&+,4ST9Ab9RjZ5LS*jFh*eHRq$KiQ*Ki@#J)"rIRk!JiD)L)H(L)Z +0M)U)L)H'KB5&LBb-LB5!IhekGh9eHAk#KBH+M)b,LSQ)L)U0Mj!!Mib)JAGP6M! +@(8ThNTqHPBb&I(9cG(KpJSD)LBL'Ji'!IhjqIi+&KiH(KiL*M)b+L)L)KS@%K)H +,MBU&JApqI(PfGAKpJB5'LBZ,LiU*KiH*MBq3!)q0L)&iDPBq*"ilD)QERTL2Khp +fFR0fHi#&L)U*Ki5"J(pqIRq"K)D(KiH(LBb0LiQ)L)H&Ji1'LSf-Ki1!IhekGh9 +hI)#$KBL+LiZ+LSQ)LSb2NC!!MSU$HQaB3#BD-@#&QU#DNBQ"H(0cGAPqJiH+LSL +&JS"rIReqJ)1'KiH'KiQ,MBb+L)L(KB1$KBQ0MBL$J(jpHhKfGRYrJS5'LBZ,LSQ +*L)Q,MT!!NC!!M)9pF&j),KSP6hU8Rjf8Li4lGA*dH(f"KSQ+LBD$JB"rIAk!JS@ +(KiD(L)U-M)U)KiH'KB5%Kib0LS@"IhjmHAChHRk#K)D)LSU+LSQ*LBZ1NC'3!)f +)J(9N6M8F(%*aMjkIPSk(IRCbFhGlJ)@*LiQ(Ji'!IhjpIi+%KiH'KSH+M)b,LBL +(KS@%K)D+MBZ'JS"qIAThGRKpJB1&L)U,LSQ*LBQ+MC!!NC!!MiZ%HQY@2#%@-Q+ +(R+'CN!#*J(KcFhCkIi1(LSZ)K)+!J(jpIS#$KSH'KSH*M)f-LBH(KS@&K)@*MBf +)Ji"rIAYhGRKpJB1%KiQ,LiU*L)Q+M*!!NC'2Li4lEPY%+4JT9hqAS*b5LS0kG(0 +eHAf#KSU,LB@$JB"rIRk!Ji@(KiD(L)Z0M)U)KiH'KB5&L)b0LS5!IhjmH(ChHi# +$KBD)LSU+LBQ+LSb2NC'3!)f'IR&J5M%E)%KfNTqIPBb&I(9bG(KmJ)@*LiQ(Ji' +!IhepIi+&KiH'KSL,MBf+L)H'KB@%KBL,M)U&JApqI(PhGhTrJS1&L)U,LSQ*LBQ +,Mj'4N!#0L)"dBdie("Y!EifGS*H1KhphFh0fHRk%L)U+L)@#J)"qIAk"K)H(KiD +(LBb0LiQ(KiD&K)@(LSb,Ki*rIRekGhGkIS+%KBH*LSU*LBQ*Lik3!*'3!)k*JRK +T96`K&c0MKjZKQT!!LB&jG(0fHAk#KSQ+L)D$JB"rIAk!JiD(KiD'L)Z-LiQ(KiD +&K)@'LBb-L)1!IRelH(CjIS+%KBH*LSU+LBQ*LSb1N!#3!)q+JhPX@%!P'#jGJjQ +JQj+,JhTdFh9iIB'&LBU*KS1"J(ppIAq#KBL)KiH)LSb-LSL(KS@%K)D)M)f+KB& +qIAYiGhKpJB5&KSL+LSQ*LBQ+M)k3!*!!Mib'I'pF45SB*P0mPD#GP)b&I(CcG(K +mJ)@*LSU(K)+!IhemIS+&L)L)KiL+M)f,L)H'KB5%KBH,MBZ'JRpqI(PhH(b"Ji5 +&KiQ+LBQ*LBU-MT!!NC!!MBGpF&j),KJK6(L6S*k9MSGqGR0dGhYrK)L+LSL&JS" +rIAeqJB5(L)L(L)U-M)U)KiD&K)5&KiZ0M)L$IhjmHAGhHi#$K)@(LBU+LBQ*LSZ +0MT!!N!#1L)"cBN`b'Ke%FSqIS*D1L(phFh0fHRk#KSQ+L)D$JAppI(f!JiD)KiH +(LBZ-LiQ(KS@%K)5'LSf-L)5!IRakH(GkIi1&KBD)LBQ*LBQ*Lif2N!#3!)k+JRG +R86BD&ceYMCfJQ*!!LB&iFh0eHAk#KSQ+LBH$JAppI(erJSD(KiH(LBZ-LiQ)Ki@ +%Ji1&LBb0LS@"IRelH(GjIS+&KB@(LBQ*LBQ*Lib1Mj!!MSQ#GfP925)E1@H*Qk# +CN!#+JRPdFR4iIB'&L)U*Ki5"IhemI(q#KBH)KiH)Lib-LSL(KS5$Ji@)M)f,KS* +rIAYiGhPqJi@&KSL+LSQ*LBQ+Lif2N!#2M)9kE&P"*4J`B)@DSCU5Li4kG(*dH(b +"KBL+LBL&JS"qI(aqJS@(L)H(L)Z-M)U)KiD%Ji1%KiZ0M)L$IhjmHAGiIB'%KB@ +(LBU*LBQ*Lib1Miq1Li4jE&Y&+aXX@(q@S*b6MBCpGR0cGRZ!K)H*LSL&JS"qI(a +qJB@(L)L(L)U-MBZ*KiD%Ji1$KSU0M)Q%J(jmHAGhHi#%KB@(LBQ*LBQ*LSb0MT! +!Mib(IA"I55dB*&"kP+#GP)k(IRCbFR9kIi1(LBU)KS1!IRalIB'%KiL)KiL+M)b +,LBH'KB1$Ji@+MBf*KB&rIAThGhU!Ji@&KiL*LBQ*LBU,MBk3!*!!MSKrFf*0-KS +I4h54Rjq@MiKrGh*bGAPqJSD*LSQ(K)&qI(YpJ)1'L)L(KiQ-MBb*KiD&Ji+#KBQ +0MBU'JRppHhKfHAk$KSD'L)Q*L)L)LBU-MT!!NBk*JACR8cNG'$YVM*fJQ*!!LS* +jG(0eHAk#KSL*LBH%JApmHhf!JiD(KiH(LBZ0M)Q(KS@$JS+%L)b0LiD#IhelH(C +iIB+&KSD)LSU*L)L*LSb1N!#4MiU#H'P@25)C0Q5(Qk#CNBZ$HR4bG(KpJB@)LBQ +)KB*rIAYpIi+&KiL(KiQ,MBf,L)D&K)1#JiL-MSZ(Ji"qHhKfGhb#KBD'L)Q*LBL +)L)Q,MBq4N!#-KRaZA%3Q&#TDJCLKR*+-KAaeFh4hI)'&KiQ*Ki5#J(emI(q#KBH +(KiH)LSb0LiL(KS5$JS+'Lif-L)5"IhajGRGmJB5'KSH*LBQ)L)L*Lif2NC!!MBC +qFQ"+,aNM6RQ6Rjf8MSGqGR0cGRYrJiH*LBL&JS"qI(aqJB5'KiH(L)U-MBZ)KiD +%Ji+#KBU0M)L%JAppHRCfHAq%KSD(LBU*L)L)LBU-Mj'4MSQ"G'01-KNF4A13!*k +IPSk*J(KdFh9jIi1(LBQ)KB1!IRemIS'%KSH(KiH*M)f-LBH'K)1#JS@*MBf*KB& +rIAThGRKqK)D'KiL+LBL)L)L+M)k3!*!!MSU$H@T@2"mA1'H*R+#BMiU#HA4cGAP +qJSD)LBL&Ji&rIAeqJ)1&KiH'KiQ,MBb*KiD&K)1#K)L-MBU'JS"qHhGeH(f$KSD +'L)Q*L)L(L)Q,MT!!N!#2M)CpF&e%*43X@i+BS*U4Li9lGA0dH(f"KBH*LBD$JB" +qIAerJS@'KiH(L)Z0MBU(KS@$JS+$L)b0LiH$JApmH(ChI)'&KSH)LSU*L)L)L)U +-Mj'3!)f'IA&J5LmD*P&kP*qFNif'IACcG(GmJ)5(LBQ(K)+!IRepIi+&KSH(KSH ++M)f,L)D&K)1#JiD,MBZ(K)&qI(PfGhZ!KBD'L)U+LBL)KiH*M)q4N!#0L)"fCe- +j(Ka"ESfHRjD1L(piG(4hHi#%KSL*Ki5#J(pqIAq"JiD(KiH(LBb0LiL'KB5$JS+ +'LSf-L)5"IhekGhCjIS1'KSH*LSQ*L)L*LSf2N!#3!)k*JACR86BG(%"VLTbIPiq +*JRTeFh9kIi1'L)Q)KB+!IhjpIi'%KSH(KiH*Lif,L)D&K)1$JS@+MBf*KB&rIAY +hGRKqJiD'KiL+LSQ)KiH)Lik3!*!!MiZ%I'pG45F@+eU!Pk#ENBZ%Hh9cGAPqJSD +)LBL&JS"rIRjqJ)+&KiH'KSL,MBb*KiD&Ji1$K)Q0MBU'JS"qHhKfGhb"KBD'L)U ++LBL(KiL+MT!!NC!!MBGrFPp'*a)P9Ak@S*b5M)9pGh4eH(f"KBH*LBD$JB"qIAk +!JS@(KiH(L)U-M)Q(KB@%Ji+%L)b1LiH$J(jmHACfHi'&KSH)LSU*LBL)L)U0Mj! +!Mif)J(4M66-G)dYeN!#HRC5-KhjhG(9hI)#%KiQ*Ki5"IhjpIAq"K)D(KiD(LBb +0LSH&KB5$Ji5(Lik-Ki1!IRajGRCkJ)5'KiL*LSQ)KiH'L)b2NC'2M)9lE9Jp(K8 +hCiLERjH2LB*kGA4hHhq$KSL*Ki5#J(pqIAk!Ji@(KiH(LBZ0LiH&KB5$JS1'LSf +0LB@"IhekGhCiIB+&KSH)LSQ*L)H(L)Z1N!#3!)q-KAa[@d!M&c0JK*QJQBq+JhY +eFh9jIS+&L)Q)KB+!IhjpIS##KBH(KiH)Lif-L)@%Ji1$Ji@*MBf+KB+!IRYhGAG +mJB5'KiQ,LiQ)KSD(LBf3!*'3!)f)J(9P6M%C)8TfNTqFNiZ'IACcG(KpJB@(LBQ +'Ji&rIRepIi'%KSH(KiL,MBb*KS@%Ji1$K)L-MSZ(Ji"qI(KeGAU!Ji@'L)U+LBL +(KiH*M)q3!*!!MSU%HQY@14N@2Qk-R*k9MBL!HA9eGhYrJiH*LBH$JB"rIRjrJB5 +'KiH(L)Q-MBU'KB5$Ji1%KiZ0M)L%JAppHRCeHAk#KBD)Lib,LBL(KiL,MSq3!)k +,KAYY@6mN($KPKjUIPiq*JRTeG(CkIi1'LBU)K)'!IhjqIS#$KSH(KiH*M)f,Ki@ +%K)1$JiD+MSf*KB&rIAPfGAKpJS5'KiU,LSQ(KB@'LBf3!*'3!)f)JhPT8c3A(%P +hNTkFNiZ&IACdGAPpJB@(LBL&JS"rIhjqIi'%KSH'KiL,MBb)KB5$JS+$KBQ0MBU +'JS"rI(KeGRU!Ji@'L)U+LBL(KSH*M)q2Mif+K(aY9MJC&d&aMjfHP)b(J(KdG(G +lJ)5(LBL'JS#!IhjqIi'%KSH(KiH+M)b*KB5$JS+$K)L-MBZ(Ji"qI(KeGAPqJS5 +'LBZ,LSQ(KSD(LSf2Mif+KRpcB%FS'$"IJjLIQ)q*JRTdFhClJ)5(LBQ'Ji'!Ihp +qIS##KBH(KSD)M)f,Ki@%Ji1$K)H,MBb)K)&rIATfGAGmJ)1&KiQ,LSQ(KiD(LBb +1Miq0L)0jD9)b&Kp1HC1IQj'+KAehG(CjIS+&L)Q(K)+!J)"rIS##K)D(KSH)LSf +-L)@%Ji1$K)@*MBb*KB'!IRajGAClJ)1%KSQ,LiU)KiD'L)Z0MBf0LiCqEPJk'KT +#F)kHRT5-Ki"iG(9iI)#%KiQ)KB+!J)"rIRq"K)H)KiH)LSb-LBD%Ji1$K)@)M)f ++KS+!IhajGA9jIS+$KBL,LiU*L)H(L)Z0MBf0LiH!Fep&)a8cC)DDRjL1LB*jG(4 +hHi#%KiQ)KB1"J)"rIRq!JiD(KiD'L)Z-LSH&K)1$K)@)M)k-Ki1!IhekGR9iIB' +$KBH*LiU)KiH'KiU-MSk1M)L$H'C0+K-Q9Rq@RjU4LS0mGR4fHRk#KSL)KS5"J)" +rIRk!Ji@(KiD'KiU-LSH&K)1$K)5'LBf-L)5"IhelGh9hI)#$K)@)LSU*L)H(KiQ +0MSk0LiL$HQY80KXJ5A53!*fFNib'IRGdGAKpJB@)LBL&JS#!IhepIi'&KiH(KSH ++M)b*KB5$JS1%KBQ-MBU&JS"qI(KfGRTrJB1&L)U,LSL(KSH)M)k1MSf+KRpcAd- +L&MGRL*ZIPSk)JAPdG(GlIi1(LBL'Ji'!J(jpIS#%KSH(KSH*Lib*KS5%Ji1%KBL +-MSZ(Ji"rIAThGRPqJB1&KiQ+LSL(KSD(LSk3!*!!MSZ'JACP65`@+PPrPTqENBQ +$Hh9cGRTqJSD*LBH%JB#!IheqJ)1'KiH(KiL+M)U(KB5$Ji1%KSU1MBL%JAppHhG +eGhb!JS5'LBZ+LBH'KBD*MBq2Mif+KAe[@6SD'8*aMTkHP)b&IAGdGAKpJB@)LBL +'JS#!IhepIi'&KiH'KSH+M)b*KS5$JS1%KBQ0MSZ'JS"rI(KeGRTqJS1&L)U,LBL +(KSD)Lik2MSf,L)&eB83K%c4PKjUIPik)JAPdG(GlIi1(LBQ'Ji'!J(jpIS'$KSH +(KiH*Lib+KS5$Ji1$K)H-MSZ'JS"rIAPfGAKpJB1%KiU,LSL(KSD(LSf2Mif,Ki* +jD9%`&b96I*5IQj++JhYeFR9jIS+'LBQ(K)'!J(ppIS#$KBH(KSD)Lib,Ki@%Ji1 +%K)D+MBb)Ji"rIAYhGAGlIi+%KSQ+LSQ(KiD(LBf1MSk0LSGrF&Sm("JrESbGRj@ +-KAjhFh4iI)'&L)Q)KB+!J)"qIRq"K)D(KSD(LBb-LB@%Ji1$K)D*M)f+KB+!IRa +jGRCjIS'$KBL+LSQ)KiD'L)Z1MSk0LiQ$GQ*')a-[BS@CS*Q2L)&kG(4hHhq$KiQ +*Ki1"J)"rIRq"JiD(KiD(L)Z-LSD%K)1$K)@(Lif,KS+!IhajGR9iIB##K)L+LiU +*KiD'KiZ1Mif-LSL%HfT4-"8N8RZ6Rjb4LS0lGA0eHRk#KSQ*Ki5"J)"rIRk!JS@ +(KiD'L)U-LSH&K)1$K)@(Lif-L)1!IhelH(ChHhq"JiD*LiZ*L)H'KiQ-MSf0MBZ +(Ih"D2KmC2@b,RCq@MBCrH(0dGhb"KBL*L)D#J)"rIRjrJS5'KiH'KiQ-M)Q'K)1 +$K)@'LBb0LS@"J(jmHAGhHRk"Ji@)LSU+LBL(KSL-MSk0MBZ)JA4J4588,f'&QD# +CMiL"HR4dGRTrK)H*L)D%JB#!IReqJB1'KiH'KSL,M)Q'KB5$Ji5&KiZ0LiD#J(j +mHRGfH(b!JS5(LSZ+LBL(KSH+MBk1MSb+KATT86%@)8piNU#GNSU$I(9cGAPqJSD +*LBH&JB#!IheqJ)1&KiH'KSH+M)Z(KB5$Ji5&KiU0MBL%J(jpHhKfGhYrJB1'L)U ++LBL(KSD*M)k1MSk,KRjZ@$XE'%"[MTqJPSb'IhKcG(KmJ)@)LBL'JS#!J(jqIi' +%KSH'KSH*LiZ)KS@%Ji5&KSL-MBU&JApqI(PhGhTqJB1&L)U,LSQ)KiD*MBq1MBb ++Ki&cAN)K&6CQKjbKQ)k)JAPdFhClIi5)LBQ(Ji'!IhjpIS#%KSH(KSH)Lib*KS@ +%Ji1%KBL-MSZ'JS"qI(ThGRPpJ)+&KiQ,LSQ)KiD(Lik2MSk-LB4iC8XU&#GAIjH +JQj'+JhYeFh9jIB+'LBQ)K)'!IhjpIB#$KBH(KSD)LSZ+Ki@%Ji1%KBD+MSf)Ji" +rIAYiGRKmJ)+$KSL+LSQ)KiH)Lik2MSk-L)0jD9!`&5&3Hj5JRC1-KAeeFR4iI)' +&L)Q)KS+!J(ppIAq#KBH(KiD(LBZ,L)@%Ji1%K)D)M)f+K)"qIAYiGRGlIi+$KBL ++LSU*L)H(LBf1MSf-LB4mEPJk("e&FBfHRj@0KRphFR0hHi#%L)Q*Ki5"IhppI(k +!JiD)KiD(LBZ-LBD&K)1%K)@(Lif,KS*rIRajGhGkIS'$K)H*LSU*KiH(LBb1MSk +1LiD!FPe")4FiD)LES*L2LB*jFh0fHRk$KiQ*L)@"IhppI(f!JiD)L)H(L)U-LSH +&K)1$K)5'LSf0LB5!IhekH(CjIB'$K)D*LSU*L)H'KiU0Miq2M)H"GQ4+*a-XAS+ +BSCb4LS0kG(*eHAf#KSQ+L)D#J(pqI(erJSD)L)H'L)U-LiH&K)1$Ji5&LBf1LS@ +"IhelGhCiI)##Ji@)LSU*L)L(KiU1N!#2MSZ'JRTV9$-B)NpjNjqGP)b&IA9bG(K +mJB@)LSQ(Ji"rIRamIS'%KiL(KiH*Lib*KS@%Ji1%KBL-MSZ'JRppI(PfGhTqJB1 +&KiQ*LBQ)L)L+MBk1MSf+KAeY968@'dPeN!#IRT@0KhphFR0hHi#%KiQ+L)5"Ihj +mI(k"K)H)KiD'L)Z-LBH&K)1$Ji1'LSk0Ki*rIRajGRCkIS'$K)H*LSQ*L)L(L)Z +0MSk0LSCqF9`r(KNqE)ZGS*H2L)"hFR*eHRq$KiQ+L)D#IhjmHherJiD)KiD(L)Z +-LSH&K)1$Ji1&LSf0LB5!IRekGR9iIS'$K)H*LSU*L)L)LBZ1MSk0LiCqF9`q(4F +lDSUFS*L3!)U#HA0bG(PqJSD)LSQ'JS"qI(YmIS+'L)L(KiL+LiU)KS@%Ji1$KBQ +0MSU&JAjpHhGeH(f"Ji5'LBU+LBQ)L)L+M)f1MSZ'Ih4L5#JC-f#$Q+#DNSZ%HR4 +bG(KqJS@)LSQ(K)"rIAYmIS'&L)L(KSL+M)Z)KS@%Ji1$K)L-MSZ'JRpqHhKfH(f +"Ji5&KiQ*LBQ)L)Q,M)f1MSb)JA4L4bFD-f#$Q*qDNSb&I(9bG(KmJB@(LBQ(K)& +rIAYlIB#%KiL(KSH*LiZ*Ki@%Ji+#JiD-MSb)Ji"qI(PhH(b!Ji5&KiQ*LBQ)L)L +*M)k1MSb(J(9P6#dF-&YrPCqENib&IA9bG(GmJ)5(LBQ(KB&rIAYlIB#$KiL(KSH +*LSZ*Ki@%Ji+#JS@+MBb)K)"qI(PfGhb!Ji5%KSL)L)L)KiL*Lif2Mib(JAGT8c- +E+PCmNTkENSb(IRGdG(GlJ)5'L)Q)KB+!IAYlI(q$KSL(KSD)LSZ*Ki@%Ji+"JS5 +)M)b*KB&rIATiGhTrJS5%KBH)L)L(KiH)LBZ-MBb)JhTY@MdN+NpfMTbFNif(J(K +dG(ClIi1&KiL)KS+!IAYlI(q#KSH(KSD)LSZ*Ki@&Ji+"JB1(Lib+KS+!IRYiGhT +qJS5%KBH)L)L)KiH)LBZ0MSf*JRTY@d%R+8edM*ZFNif)JAPdG(CkIS+&KiQ)KS1 +!IRalI(k"KBH(KSD(LSZ+L)D&Ji+"J)+'LSb+Ki1!IRYiGhPqJS5%KBD)L)H(KiH +)LBU-MBb*KAjbBNNX*dCZLCLFPBf)JRTeG(CkIS+%KSL)Ki1"IhalI(k!K)D'KSD +(LBZ+L)D&Ji+"J)'&LSb+Ki1"IhajGhKpJB5%K)D(KiH(KiH(L)Q,M)b+KApeC8d +[*N0VKTHDNif*JhafG(CjIB'%KSL)KS1"IhelI(k!JiD'KB@'L)U+L)D%Ji'!J)' +&LBZ*Ki5"IhekGhKmJ)1%K)@(KiH(KiD(L)Q+LiZ*KApeCP%h,80SK*5CP)f*K(a +fGACjIB'$KBD(KS1"IhelI(f!Ji@'KB@'L)U+L)D&K)+"J)#$L)U*Ki5"IhekH(P +mJ)1%K)@'KiH(KiD'L)U+M)b*K(phDPBp,MpNJC+CPBk+KAehGACjI)#$KBD(KS5 +"IhemI(erJi@&KB@'KiQ+L)D%K)+"J)#$KiU+Ki5#J(jlHAKlIi1%K)@'KiH'KSD +'L)Q+LiZ+Ki&jE&P!,ceJIT!!Q*@1LS9rHACfH(b!Ji@'KiD%JS"qI(apJ)+%KS@ +&KBH*LBL'K)5#JB#!JSD*LBH&JS"qI(PiHhq#K)5&KSD'KSD'KSH)LBU,LSL$Hfp +I4c)i@RU0PTD2LSD!HRCfH(YrJi5'KiD%JS"qI(apIi+%KB@%KBD)LBL'K)5$JAp +rJB@*LBH&JS"qI(PiHRk#K)5%KSH(KSD&KBD)LBU+LBD"HR"L6$Fl@RL,PTD2LBD +!HRGfH(arJS5&KSD&JS"qIAapIi'$KB@%K)D)LBL'K)5$JB"rJB5)LBH&Ji&rI(T +jHRk#Ji5%KBD'KSD&KBD(L)Q+LSL$IA0Q86`k9(1(Nj@2LSH"HhGhH(YrJS5&KSD +&JS"rIAapIS'$K)5%K)@(LBL'K)1$JB"rJ)1(LBL&Ji&rIATjHRf"Ji1%KBD'KSD +&KBD(L)Q*LBH$IRCT9Mmk8R'&NT52LSH$I(KhHAYqJB1%KBD&JS&rIAapIS##K)5 +%K)@(L)L'K)1#JB"rJ)1(L)H&Ji'!IRYjHRf"Ji1%K)@'KS@&KB@'KiL*LBH%IhG +X@dFq8'f$Mj53!)U(JhjjGhKlIS'$K)@'KB+!IhjpIAk!JS5%K)5&KSL)KS5$Ji+ +"J)##KSL(KB1"J(jmHRTpJ)+$Ji5&KS@&KB5&KSH)LBQ(K)&kF@*02NTSIif6NBZ +(JhjkH(KlIB'$K)@&KB1"IhjpIAk!JS1%K)5%KSH)KS5$Ji+"Ihq"KBL(KB1#JAp +mHRTmJ)+$Ji5%KBD&KB@&KBD(L)L(K)"jF'033NeSISb5N!#,Ki4rHRKiHRf!JS5 +%KB@$JApqIAeqJ)'$K)5$K)@(L)H&Ji1#J(prJB5(Ki@$JB&rIATkI(q"Ji1%KB@ +&KB@&K)@'KiH)Ki@#IA4R984+BhU*NC!!LiH%IhYiH(TpJ)+$K)@&Ji&rIRepIRq +"JS1$Ji1&KSH'KB1#JS'!Ii#$KSH&Ji+"IhelHRYqJB1$Ji5&KB@&K)5%KBD(L)H +&JReeDPK(5Q*jL*!!N!#,Ki@!I(PjHRf!JS1%K)5$JB"rIReqIi'#Ji1$Ji5'KiD +&Ji+#JB"rJ)1'Ki@$JB'!IRakHhq"JS1$K)@&KB5%K)5&KSD(Ki@"IACVA%e1BAH +'MSq,Ki@"I(PjHRerJB1%K)5$JB"rIReqIi'#Ji1$Ji5&KSD&Ji+#JB"rJ)+&Ki@ +$JB#!IRalI(k"JS1$K)5&KB@%K)5&KBD'KS5#IRP`BP*0AA1$M)q-L)@"IATkHha +rJB+$K)5$JS"rIRjqIi##Ji1$Ji5&KSD&Ji1#JB"rIi+&KSD%JS#!IhelHhf!JS+ +#Ji5%K)5%K)5%KBD(KS@#IRK`Be42Ah5$M)k,Ki@#IRYkHRaqJB+$K)5$JS"rIRj +qIi#"JS1$Ji1%KBD&Ji+#JB"rIi'%KS@%JS'!IhelI(k!JB+#Ji5%K)5%Ji1%KB@ +'KS@#IhYcD&T4@h"rLBf,Ki@#IhYkHRaqJ)+$Ji5$JS"rIReqIi#"JS1$JS1%KBD +&Ji+#JB"rIi'%KS@%JS'!IhemHherJB+#JS1%K)5%K)5%K)5&KS@$J(adD9Y6A(" +rL)f,Ki@#IhakHhaqJ)'#Ji1$JS"rIRjqIi#"JS1$JS1$K)@&Ji+#JB"rIi'$KB@ +%JS'!IhjmI(erJB+#JS1%K)5$Ji1$Ji5&KB@$JAehEf&9@QemKSb,Ki@$IhalHha +qJ)'#Ji1$JS&rIRjqIhq"JS+#JS+$K)@%Ji+#JB#!Ii##K)@%JS'!IhjpI(erJB+ +#JS1$Ji1$Ji1$K)5&KB5$JAjjF@9EA'YkKBU,Ki@$J(elHhaqIi'#Ji1$JS'!Ihj +qIhq!JB+#JS+$K)@%Ji+#JB#!Ii##K)@%JS'!J(ppI(eqJ)'#JS+$Ji1$Ji+#Ji5 +%KB@$JAplG'TH@fKiJiQ,L)@$J(emHhapIi'#JS1$JS'!IhjqIhq!JB+#JS+#Ji5 +%Ji+"JB#!Ii#"Ji5$JS'!J(ppI(aqJ)'"JS+$Ji1$JS+#Ji1%K)5$JApmGQjLA@G +fJBH+L)@$JAjmI(apIi#"JS+$JS'!IhjqIRq!JB'#JS+#Ji5%Ji+"JB#!Ihq"Ji5 +$JS'!J(pqI(aqJ)'"JB+#Ji1#JS+#Ji1$K)5$JS"pH("QB'CdIiD*L)@$JAppI(a +pIi#"JS+#JS'!IhpqIhq!J)'#JS'#JS1%Ji+"JB#!Ihq"JS1$JS'!J(pqIAeqIi' +"JB+#JS+#JS+#JS1$Ji1$JS"pHA*UBfGdIi@)Ki@$JAppI(apIS#"JB+#JS'!Ihp +qIhq!J)'"JB'"JS1$Ji+"JB#!Ihq!JS1$JS'!J(pqIAeqIi#"JB'#JS+#JS+#JS+ +$Ji1$JS&rHh9ZCQGbIB1(Ki@$JS"qIAepIS#"JB+#JS'!J(prIhq!J)'"JB'"JS+ +$Ji+"JB'!J(q!JB1$JS'!J(prIReqIi#"JB'"JS+#JS'"JS+#JS1$JS&rIAKaD@K +`Hi+'Ki@$JS"qIAepIRq!JB'#JB'!J(prIhprJ)'"JB'"JB+$JS'"JB#!J(q!JB+ +#JS'!J)"rIReqIi#!JB'"JS+#JS'"JB+#JS+#JB&rIATeEQY`HB#%KS@$JS"qIAe +pIRq!JB'"JB'!J(prIhprJ)#"JB'"JB'#JS+"JB#!J(q!JB+#JS'!J)"rIRjqIi# +!J)'"JB'"JB'"JB'#JS+#JS'!IRYhF@e`HAq$KB5$JS"rIReqIRq!J)'"JB'!J(p +rIhprJ)#!JB'"JB'#JS'"JB#!J)#!J)'#JS'!J)"rIhjqIi#!J)#"JB'"JB'"JB' +"JB+#JB'!IhejG("bH(k#K)5$JS&rIRjqIRq!J)'"JB'!J(prIhprJ)#!JB'!J)' +"JB'"J)#!J)#!J)'"JB'!J)"rIhjqIhq!J)#!JB'"JB'"JB'"JB'"JB'!IhjlGh0 +bGhf"Ji5#JB'!IhjqIRprJ)#"JB'!J(prIhprJ)#!J)#!J)'"JB'"J)#!J)#!J)# +"JB'!J)"rIhpqIi#!J)#!J)'"JB'!J)#"JB'"JB'!J(jpHRCdGhb!JS1#JB'!Ihj +qIRprJ)#!J)#!J)"rIhprIi#!J)#!J)#"JB'!J)#!J)#!J)#"JB'!J)#!IhprIhq +!J)#!J)#!J)#!J)#!JB'"JB'!J(pqI(KhH(arJB+#JB#!IhprIhprJ)#!J)#!J)" +rIhprIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhq!J)#!J)#!J)#!J)# +!J)#!J)#!J(pqIAakHRarJ)'"JB#!IhprIhprJ)#!J)#!J)"rIhprIhq!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!IhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)"rIhj +mI(erJ)#"J)#!J(prIhprIi#!J)#!J)#!IhprIhq!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!IhprIhprJ)#!J)#!J)#!J)#!J)#!J)#!J)"rIhprIhprJ)#!J)#!J(p +rIhprIi#!J)#!J)#!J(prJ)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J!!!3N!!!3!"!!8!!!#J!!'!83!!!!!!&!!!!!!!!%) +@9Zk,S`!!3K3!!%)9!$b!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)"rJ)#!J(q!J(prIRk!JB1 +$IAeqHAZ'Li9pGh0lJ)#(MhKXIhaqJ)D(IhjZH)PjGiU8H'D'I@b,P)CaFReiJAp +qL(YiL)0hG(&jLi9rPS49Ci9rMU5(DhCP9)LXMhf!J'44GTfFLSk'B9eNF+LJH*@ +(6df'Qi+AM(Pk8'LEJAqJLeTdM'&lRAKqPh"VMRPSL)afLSYUIi*XJ)4mLiPSGTP +ZAjD5HB"pIS"TC*DPFA10G'KYJjU1IRehCA#6IhZFI'TmH(5'M(Q#Lh&cL(KfL)C +qG)#%HhacLSTVHSq&DhLAGQQ1NAGaI(PjLi#)Q(&LGhadLD5-FATRB(q)NjL5F9p +lD@HDU)CeK(G@DBkCPhjlJ'aLGSq9MRpaGi"SG*Z4I(GrHfalLB&qKiTZFT0eCiq +5FAH4Hh'#Gh@4Kfk(PR0JIC&mH)11LfPNKT4eFj@2FR"lHB1(GS19G@H(K@f"Qh9 +VNS*RIBZ!I(jqIBYcE*Q'DRD0KfYqKS'(Fh@0I'f)NATbMApMIi9rMS&mIAP`F)f +*J)epG(0aJS@!KC!!JQG`K)"pKSH&H@eiKB&rLB4hH(CjLiKdJ)j`CiQ)ISGqG(k +$EhUCJACpFhjpH)Z3!(KXL)"IM*KkK)&aFS*kHj@*F(erGAKiNj*VH*4mARkBGhL +1HRjmGB*rIi+%HRZ'Ehb3!'akRRjNM*!!ARLAHAZ-I(GkFB#1I(Q4I'q*IQk'L(5 +!KRPmL(GeM(phJB@'H(D%Ih0hMBKkL(abJhTZLTjrChq)FA*mNjTeDAq0G'Q1QS9 +[DBU'D(UIQ@9UMRYZJj+1GA'$Gh5*P)"`KhY[KRb(PhKXJiKMHk4qHBKfFhYeL*Y +pF*+!9iLBDhbAJ@prKB4mCS5NF'5IQ'*MKSZ'GAUSL8Y`S(pUM*U#FfGiQ(0YSTC +UEBGqFi1(MB0ZJB4YIC@$GS1&Hh9hJT!!G@qCKQb'J('!KRU,P'TaMR&ZNj*rJhC +YJS4mM*&hGAe[I)f%KBTlFB"mFiZ5K(TcH(ahHjH@FAL!Eh5)Mj!!L'jTKRjPMUQ +(E('!GR+)NBq"F(*fIRq'PiCfH(9hJS@'LiC[GBK[HjYqDiU-Eh51JhU+H(H-G@b +%P)TfHB0jGRZ-MhGaKB9eH)q-ChH9IhL'IR&lKhZ,Q@pZJh0dMj4lHBCeE(k*N!# +#G)@(F@D&Q)"cL)paE(Z%MSCeJBpdBBZ8Ehb4IR1(Ih1$JRKpMS9rHA"qJ(b+N!# +"H(GjFi+2I)Q-DfL(M'k$TATSKAjQG*f4ISChFh"VLkD0FhU%G'"pMiq9FhQ*BfD +3!+*mG*Cm@A+@L(4kMSCiJRYlGQf$R)pVHiGKCSqIM(elFhCXGCf4JAamDfL)JB@ +3!)KmE(YlDifBI(U-H9pqM(D'Q(PYLRYXJi4jK*&rGS&bGB*cN!#LG'+%K@*hSj9 +lI(0YHRCpPT0lI)pQ6BbLKB14J&PHHT5QJRD@G%PXQjZ&JiGlCe9rVSPiPiGL@hU +2LBk4MA*EERjqN!#DMi4VAh@"ISfJM("fFQGiL*56J(f"D9Q$RB1#NSK`BQb#Ni1 +(RAPPER@"LjD&Ki4BD)H&MiU-JR"UD)Z4KSZ!JAaNEBq6J)',H'0fLiQ$J*D)9@k +3!)KdJ*U#EQGqQAGYP*jZA*+3!'&ePjP[CiQ(H'Z(QhKTJj4kBi5LJPKpURP*KV* +eA)1FKf"[NjG`DTL0D@ppN!#'DhqAIef&S@jUP)aQG*1+FAL'HRTkJT5*Fh#'IPq +#Uj*ZD(ppDhDKVh49I)9LH+5@KAPDF)jcGkfMB'U%D@b,Pj+%EA4iDS'INR*jJQT +hJSQIJ@"VMS4QMD1'C@D'K(k-KRjlGQppN!#&Ji*kFAb"KC*jHhjUFSfBISL-APU +!PSb2PAaT@&k-T*q1Jh459B'SSiQ$G'*5EE'QFS18CNeqRj!!Ji*rG@CYN!#BK)1 +*F&plPSapIB9lBRUHNA*eJ(&lLjUDBe*rMhH&UT&R@QL0Nhb5Uh*)GBYcK+H2Fh0 +VE(q8PBf+FPKRMiq(RC0K@R0pMD#4Ii"KA)51NTk%BACl@SZlKQf#FQ+!MSUPLP9 +MJAU$TTf"F'"GFCLSQiK`CeCRM*keP@YPDQ"TTkZ,JA*ZF'CdVUCPH*jZ8AZ-LCU +"E)b%@'kJM(L*KA4hGh@1KhL,K@phKAGrPi9eJRe[H(q'PB9bH(YqIS"qMBpUG)D +#HhL#LiTeI)abEBQ'I*!!KA"qH'f)PRGkPAKbJRKmKB#+QA&DLi9HK+Q5GfpZIRp +ZMkTqCReiDi#*M*0mGS"YDj!!PRD,MQ*aI(D2QAaePA*4LjQ$Ji'"G@YYN!#DH)D ++Dff&JRf2NAKUHSCfGT51F)+*D(H2HhfCI@@+If5)PAL%L'YqL'plRRYTPhTQM)Y +bHj9jG)edHiaiGiL*FhZ-HR1#MAjcKBPcHC!!IAH%JhKkL(f#Jh9rIRq#JS#%L@a +aMS4fL**`G)*`GC5DH(b(E'0mP)U-MhCQE(arLU#6H'KZGh5'R+#!A@YrHAHAT)a +YBA##K(kES'jMHh4VM+10KS"MD(KjMUk2EhaQ9(UNQj',G@pHB*'QMS@+FPYbIS@ +ENi*iGfpYJj'4JRZ'G@TmL)b(IAH(K@YlNi4kIAU#LRYfM)4XJ)YqKSaiGRpiI)Z ++Ii9jDB1,HBD@KfTbJh@'MS+(JACPICD%LB&rJfpVIjk(FSZ#GRGhJ)q0GB#2H@P +kLiTqHj!!L@PaKS5!LS9jKhP[JB5'KhemLRaSJC0kJT!!Fh@*G@f5QhPcKRjdGi1 +@L(9hJ)"iHSZAJQelM(YdJ)Q2Hff&MA*ePS9cL)*dJ)GiJBT[IT*fGCQ*AS#6FhL +8NAKdER13!)9rPC!!EQ'$JRq2L)TrE@GpPAQ%S)"PFB4pIj@*J)0XD)@5J)#3!)" +XG(k'N!#"I)PiDAf4IhZ8Iff#I(5$N!#+J)&aGS4jJC!!LhTaHB#$Ii'5J@jkIi5 +#IB'$Ih9rKAf)IhL-K'jjM(eiMS&lKh4`M)ChM*!!Fh#"HAU*LBq&E(Z!ERUBNAU +'H@CjIS5CNAjpFQ4kMBQ8NR"eGPb&VSCbQiK)DC')NSf!J(CGFCf2KC*kDRGeGC5 +EI(KqEfk-MSU3!)"VCS+*ISq9IQGXHi15NSH+GeTVLBZ5PB&dGfeNNU#)KhafG(9 +[LUPmDBU(E@q1MB5%GAk'I(D#N!"rG(Z#KBH!Hi0rGRQ(MSL!FhL(H(H2N!"rGi" +jGSH%KBjrGRGmIhZ-NSKjEAYqJBD+PiCTChq'GifILA*YGh@$N!#2NReUF(jmJCL +-HS0lE(L-L)Q(GB5)BfLLR@CrRhPRGS+'Li1!P(GEJSpiIjL4Hh4[G)*rKTZ2H(9 +bEAH4QBZ&JhGNDiL2LiU%JhjXDiQ2JSb2HA&iEhb8MB@+J@abIRq0NS&lIh4`JSk +&JSZ%GR0qJAk,M)9lDRU&JB#+PRGcIAf+Gh++PB4aKAjXH)D6MS4mH@jUKj!!Mib +#HA*`DBkLJiL*F&pbMSQ2MiL&@eZ1Q(q)SB"MDQf0R)'!N!"k@ALBJAq*K(YeJ)5 +*IA5'J(H'LhplK(jbHSb,JRjmJhTZHiZ8J(f*G("mKi@%MhCaKRTjMiYjI)"cJT* +hI*9fBAq2KSZ-G(KrCAbIM(b(IQ&kMhk)N!#"GhPhH)b+J)4jHB&pJSZ*HA*jJS1 +"NSpcD(Q"J*+1Ki4NAB1BKikJF9P[H)'AUC&aC@"dKiqKSB"G@hD!JCfVMQGMEfe +pPk1FGfK`D@b4VSelK@jFH*+5Mi4hGh9aKD'*EAZ$H(+,S)K[FS"hG*19L(jbGhG +hKTL0H(pkES5+I)f,F(1,JhQ@K'KqIA5-RBChJh"LIj!!PT@%HR0QC)UPN!#)Nh4 +9CiH5NC@+J(&AF*DCLBQ3!(0GFBL2MBk,GfKbJiL(N!#3!(9YGhZ$LC1)HhYYGi@ +*LBD,HA&kIBU'JiH%HR"mK)D'J)@'GA#)LR9rPi9VHiKqG(b8NAYdJ)4`HBq2MAa +kI@j[KjU)JT&qAQU,Mi@-Pi"ICB#,K)kDJfeXH)+$Mj!!KRTXGS1!JC59EQU*J@k +&Ri4`HhpkH)@6Lh4hKhP`Lj0qI)4mHATmLSemHBZ!ChL5LRQ%PAeLGSq!HT16GQp +jGhk)M*5*Eh1!Fh+1R)ejFhYlD(LNS(TcJ(&PIj56LRpfFA4jKjQ4HR*pG@f'Qj! +!GhPhERQ&Pj@$GQpaEi1CQ)TiEQaeISUMPA9[G'j`Njf)JAP`Ehb)NC'"G(4fHSL +6M(efGh*mN!#8M(ebEA*qM*k4HRKXBRUIPBH2HQ&QH)QJQhTlH&jVNTf2LhpXE'P +pS*U#J)"KCSL1N!#9L@eTH(Z(NC!!Lh9XFi5*Jif,I'YbL)Q(L)*rFferNSq#L(a +SGiD!LjU&Eh&eHSQ,LC*rCR'&Ji@5MAacF(D(LBQ3!)CcEAQ$K)f0L(j[F(Q)LS@ +4LhCTGSKrIiq8KA"cIhakJC'6JAGjHh4eMTL+K(pfCff)NT'1MRYHDB@+LCLFHQ& +SGS+1NjH2EejYKBD(STTfBfTiGibHNi4eE@PfMjD3!)@"H'4VMCD'K)GpF(*qMjG +qGiPrDADBLhD(JA0fKSb#KS+!HR#%M)"kKSTdGiQ'IhaqKi9fI*'(ERL-I(D,Li' +!JAKcIi5)Li5#HR&cIiU&Lj*rF(0mIRq+Mj&rCR5$GhQAQhjiHQjfJSD6NRjbGh0 +cLj'0N!"mDR0mHiLCMRjjD@b$Lif5LRP[ERD&NC'+HfpiGR54QSChHRTZH)bANA9 +dIA0eKC@8Jh&VIi"eMTf(FA*fFi13!*!!LAjfE(1*MiH*LR&NHBU-MSTmGQpYJjH +@KhYbEA0mN!#EM)"hD'@#PSZ4Q(CDE(k"NCk0HR&MESk3!)b6LQjNGRb*PBZ&I'j +XIiZ,NSPbFRPaICQ6I(jpEA+'Lif4IA&jH(+$QBYlJhaZGiH0L)@!HRKcIiZ(Ji* +qHAerIib,Hhb"HRH&Khq&KAajJB+!Ji5"Ii+!HAq(IhL"LS*qJi*rHAU$L)5"KAp +fHAq$KSD)KAYfI)"jKC!!LRjhHATlJ)U8Kh9iHhjrKBb'IAGpIRk)K(q&K(TmK(a +pMBKlIRpfHS@&LBeqGRYlHB@1LB@"H(*dHiZ9LB+'GfCbL)Z2PB&cG@a[LjU0L)T +`CAL!Kj'6KR9dF(@$M*H+I(PeGhU)N!#,JhCdGRq+KBU*Gh*qJRf3!**dGS0cGC+ +5IiD"DRD&JT!!P(YdI'paLBq8P(GRH(aaLk'1JhPQChk)LCZ@I'YUG(b-PC5&Fh& +ZG)@1P)arFh&iHBQ3!)U'H(4iI(f)Ni4hIAekIS5+L(adIB4qIiD$JAjmIi+!IB+ +#IS+#IRjrJ(epKB9pIB@$FhZ0KRf!KAjfH(b+MhapKApcH)H'LBKlHAPfHB+0NBT +lEhPiGBbAM(pkF@PfK)qEMRGfH'YUNU1)IBGeA(#,MC59JRC`ChD4NBL3!)"QEAQ +!P*D%KATFFCL'I*k1A'L#Hi5BNi4lCfH&LS@8PhYKD(f&L)qDLfYPFRq)NC1'H(0 +[F)+BNS#"HfpdJ)D2NRYcIhKbL*H%IB*iG(U)LBD,I(9fIS9qLSU%I'jmKS5!K*! +!IR*hIS9rJSk)Gh@"J(U%MSGqHhYiHB@*KSQ$HhGdHBL6Ji+2H'*eLB@+NiPkG'Y +fMSL*NiG`DhKjKT1,LS"bF(5&Lj!!LAPpGR*lLTD(IApfGAf&Lif'FhQ#FAbAMAZ +#JA"iKi#*NhjbH(f!JB'+NRjYIBClIi1'LS"bH)U#Fi15KhGkKhehJ)+(LB4eHiG +hGiU-L)4kFRf"H)H8L(piFRKrJiZ9KR4jGA#$Mif1L(0THhpmMjH*HA*YFSH2LBk +-G@C[JBZ-N!#0HQeXH)'-QBq#FQPaHSH4PieiEfGcKBU6QBKXEA4`K*H8MS4[ChC +lJjU8JRpaBh@,LSq9JA&bERH0P)k'HA"bGB#4N!#(JAKYGB+%MieqI(CdH)10LiG +hHB&cI)f*JB"lFhq)IB1)IRYpIAk(LApkJ)"hHiU+IRf!HRZ!JBU+IAPpHhL%KB1 +*IhKrI(Q(LRZ#MAKbJS*lJiPrJS4fHB+!JBD*I(U"H(H"LSU!JRahI(Z$LiQ$I(K +bHSD$LBk"HAKdHBZ2JS1'FfjqJS12MRYfIRCdLC1)J(afG(TpLTQ(FhTpGRQ,MSb +&F(9rHAf-P)&qIR"jJSH+Ki*iI(KeKik&IB5"F(Q-Khb&KRPiIAq&LRYkMS&bISL +$J)1!Ji*eISPmI)f)H(q&HAb'IB'1JR9pKRKjL)D+KRPiJ)"hK)k%JRjiHS##JBZ +'IAepHhk%K)1$IhalIi@%IS+'I(KpKSYrHB+%GRH*Ki1)IAD#IAD'N!##IBGjF)# +#KBb'JRekGAQ(L)Z&HRekHAf$MSb#HhGiHS@'JSf'F(5"Ii5-Ji#'G@b%M(f'MAa +iHRU"Li9qKhjcHS''L)9rHhjkHS'*MRjjI(b!IB'+MAeZJS4cJiZ$JB0iGSClHj' +'GS'$GAQ'JS1)Ihf"HAQ)JAb(L(afIhpqKB'%KRjiH)+%IS@%I(arIRZ'LhprIAP +rJB+#Ki"hIRYqLBD!JS*cGSL#JBQ'HA4kISQ*J)@'G@YrM)H&KB&jG(1!NBL$L(e +ZF)D(JSb+Ih*bIi'&LBL$GRGrIRq%M)9fHhppJB##Ki&hH)L$HSQ'GhL!JB#'KS' +!H(9rKiL'JAprH(9pLiZ!IB&rGRL)L)+#JhjiHRk(KRU%LhPeJiQ!I(q!K(jlKS0 +iHiL%H)5+I(GrL(pqKB+"IRepJB&pKiKlHS'"I)'&KS9kH(q!I)5+J)D$FRD'KAb +)MAejH(H!KSH*LAjcH(prKBb*Ih9hI(k(LiU"HhPiI)12LhakI(KiKBU,LhYdHha +mLj5"IAp`ES+5LiL'HA9dI)H-LB*rG(0qK)H)KS0mGR@$LS+$L(e`HBD&J)H*I(0 +lKS5%KAYqIhCpM)TqJ(edISU#JSb!FhKqJ)D+KB0pFAU'JS5-KhTeH(b%Ki@*JRG +iIAaqMiekH)5"F(Z2L(f!JAYmIRZ+MATmKhaYJT'#I)1%HR0jL)f!JBPmFRQ"JBH +,JAjlFRH&LS5*M(GXHS&pKT1'HAacFS@0KSb-G@efHS+1M)D%G@YhJiQ0M)*pFfY +lLBD*Mi&`G(arJSU1KhGbHRerK)@-KA&bL)KdK*&rG(Q!J)+"JiTmFAk'Ji#)KRY +eGS1(Ji1)J(CfI)1$L)H#I(GmIi+$JiH!GhTrJS'&Jhq"HAL#LAppK(pjI)1!KBK +pHhprI)''JS+#H(Q!K)#!KS9rG(Q*KAU$LheeIAq!L)0mKB0cGiZ'I)@$GhL#JS+ +&JApqHhq%K)+"IRZ!IRq'KhpjIherKS5"JB0kGB''Ji+&IhCpIhb)MS*kJAPcJSQ +#KiYiFRjqIib0K)"iF(Q%KBf1I(KjF(b5Li1+JQPaKB1'MiPlHA4dJiU+LB4lFh9 +qKiQ*Li"bFRf$JiU-K(9bIRq"LSq&H(CeIB1%LBk&FRGpHi'+Mi9kGRKlHSH5LRp +hHATlIiL3!)4mH(GlIiH)M)9lH(0kKSQ(KS*eGRamL*!!KhepHA4qJiH-JhTlI(P +rLSQ(J(GpI(H#MiTlIhefHRf+NS0jI(jcHSU,LAaiJ(jeIT+(HhppHhYpJif&GS+ +$F(L,LAf"K(ekGhk,L(YrLAedHS@(JAq#JhTfKBGmIiD#H(b(JRPqK)0pIB#"Jhe +pL)&eIBKrGBD*HAU&K(epIS#(IRU%K(PlJhq$KRerJhTfK)0pL)ChHS0iHif(Hi# +#GhH#K)L&I(emHAL#M)Q#HhajGS#,LB1$Hh9eHSQ-L)@"H@ekJS+1Li*lGR"hMBD +%Mi9aF(epJib*K(jiFRQ$Kik(IhTeH(k$KSZ)GR*mJB+&LB9qHA9iK)Q%JS9qG(C +qKBZ-KAKfGAU$L)f'IAGeI)+,L)'"IAKiJiD%JReqIReqKSGqJ(jpJ(b"Ki4mHB& +pJ)H"Ji4jGS5%IiD'GhU&HS'+JB#$HR@)LAQ%KhemIAf$LAepKhejJBH"J)&rJRY +pL)GqHRprIAk&MS*jIhYhJSb$KS9bH)*iIjL0G(jrF(U*KiZ,H(*pHRZ1MB##Hh0 +mIB'1MRTdJRPfKBb,J(PhHi"qK)b*Hh*mJhU!NSPiHRajIiL'K)CkHB"kIiU&IS' +#HRf!ISL(HB#(HRH%JRL#LB'!JRjmI(U$MB4pIRahH)#(MBPqHh9fIiD)LiPjEh9 +pJSU-L(pdFhZ"Kib+Ih4cH)1'Kik(FQjpJi+(MB9eFRL!K)D,K(aeG(q'K)H*IR* +hIRq'LB5!HR4qK(k(M(jdIApjKBL"JRYfJSClIif"Fhk#J)&rHi+%HAk+K(YmIRb +"KB1$J(TiHi''Ki@"HRKlIi5(KApqH(H"KB@$JRjjI(f$Li*mIRaeIif%JB9kFAf +%J)Z-IRChH(b*LS@)Hh*fI)+)Li0rHh*mKS'$M)0cH)#!JS1$JAakI)''L)"kIRj +lJiH!J)0jGi@'J)5"HRarJS1$J)&mFhk+Ki+#IAClHhq2MhpiHAGhK)f+KS&fEAD +'LiZ,KA9YGS'*MBQ&Hfj[J)Z*MBPlG(&fKC!!LS@%FQapKS1,Ni&dG(L#KBH*L(T +ZIB&pL)k)HACmJB&lKT!!JA&eJS5!KBf%GR9rJi++KRKeHS5(K)5%I("kLSZ'K(j +cG(Z'MiH!JAYaGSL1LS4lHRCbISq3!)9rGR4jIiQ3!)YpG(0fK)L(Mi9`E(b'KBU ++KAaXEi11LSD&IR4`HSb0KB5!Fh5"KSQ&JB"jGhf(KS'#HhTrJ)5&JheqI(5#Mi* +mJAjlHhq)M)&iIAjiJ)Q)JATiI(pmKT'#FhL"Hhf+L)0pGAD!KS1)L(efH(f"KiL +%IRGfHi+&L)PqGhTlISH0J(TqG(@%LSH&J(CfIhk$MBGkH(TiIB@'LB0hGRb"JS@ +&IhjjH)+)JRb!IRPrKi5!JRjjHi#'Ki&kIAphHiQ,Ji"pH(TmJBQ*IRarG(D*MB0 +qJhYeHB+1L(YlJATbKBf%J(pmH(jqJiZ"HReqH(b,KS#!IhTjJB1'K(ppH(b$KS+ +!KAadISH%Ii1"H(YqJSQ%HRk"HAH*MRpmIhjjGi@-K(f"JA9hKSL$K)&lHhTqLSK +qIS*jGB1+KAq!J(GhK)Z&JB*lGRU!KSQ'IhYkHAk&L)KqI(eiHS1+K)5"FhQ%Ii' +0LhGdI(k$KBD)J(4fJB+&LiClH(TkJSH(LB"eGS1&I)H,IRGkJ)'%K(q"J(Z"K(e +rKRpkJS4pIi5"J(jpJB*rJSL"GAZ"Ii+(Ki&mGAQ(KS'&KRKbIS+#L)4qJ(aeISb +(Ii+!HATmJSb*IAYrHhZ%KSL(HR4mJ(k'MS*kI(KiKBq'J)&iFRb&L)Q$I(YlGhf +1M(ppI(akHS1-LATjJRekJ)D'JRelJRpiJBU"HAk$IRf"K)9rGhZ'K(q"K(eiI)' +'K)'"HAQ"Ji'#KRjfHhq%KS*qIhpfHBQ+J(b!IRCjKBU%IRjmH(b"KBU'IAPjHhf +&LB+#Ih9fJ)U$JBCpGAL#KB+'JRYkHAq'KRprK(TfIiH'JRjqIhGpKi4rJi0cHBQ +!I)D(HhTqHi1'IS#$IAQ"KB'"J(amIRq&K(q!IhTlK)5"KB4lGAf%Ii#(KRYhIRq +#JS+'JAGjKB*pK)9mH(jrKSGmJB4iGi5(IS5%H(k"IAq&KRppI(q$I(k)JRPmJS# +#KB'!IRKpKSD"K)&iGRk'KB5'JRGeIi+"LSPqGhTlIBD(KB0lHAZ"JS1(K(jfHi@ +"Ii5'J(PpIS5'IRq%JAYmJiD"I)#'IAL*LAPmJhppIi5+Kh0bL)0iKC+%F(@#Jhk +$Mi9cGB'"IS@*KRadIB0qIiZ,HA4pJRalMT*lEhk%G(f5M)"hGRarIB+8Lh*hJ(Y +kKSb(IhClJRZ!MB9kIi&jIi4rJi*mJ)9mHSH$Hhq&KAemJB&kHBQ,IRerIRPmK)U +-I(CqI(KrMBf"HhGlIAq)LSL!GR4jJi@&LBClFhD!KiH(KAeeGAZ%LBU'IhGcHi' +&LSZ"GRCkI)'(MBKkGAerGi'4LAakI(eiHB@2KhPqJhPdISZ*Ii#%I("iLBD#Ki9 +jFhQ!K)H(KAecGAk%K)H,K(0ZIS9qK*!!LA4aHRf"KSZ,IR*cIB1"KiZ'H'plK(q +"LiPlH(PlJ)D'JS5"HACmK)5$Ji&qHAPqKBH#Ii&qGRQ&Ki#!K(piHRq%KS*qJAp +kHS#'JhpqJS"iIS1$JRk!J(pjHiL&IB#&JRKhJ)H$IB+%I(KmJSD%J)&rHRTrJS5 +&JAf!IRYrJi5$Ihk!JATkKSD!IS+$HRU!KS4qK)&jI(q#JS@$IRpkHi'%KB1"IRa +mIB+'KS0kHB#!Hi51KAGiIAerKBQ)J(GiHhq(LSL#Hh9iJB1*LS&pHhPkK)Q$JS* +mHAZ$KS1$K(jfHi1#JiD#IRYjIiD&J)5%H(L#KB&rKB4mHAq%Ihk%K)&qIAprIS# +%KB&rIhTkJ)5'Ji5#HRClK)5'LS&jHRKjK)b,K(jeG(b!LC!!LRjdFhCrLBf2JhC +cFRL(Nif$J(9VGB12NiCpH(*bI)b2LS"hHRKiKBZ'JB"kGhk"JSD$J(plHi##J)' +$Ihk!IRprIS+%JAk#JAPlJ)+%KB5!HhKlJ)1(LS0iGhKlJ)H-LApdGRemJBZ-JRK +hHAb!KBf'GhPqHhb(Li&kHRZ!JB'(KAaeIS4rJ)5$IAKmJS1#Ji*mHAk#J)'$JRa +iIS1&JAq#JATfJSZ$IS"pHATqJiU(I(YpHAf&Ki9rI(TmJ)''KRjmIAjpJB@#JAa +jJ)&qJSD"HRf!I)#%JS"pIRprIi'(JAKmJB&pJSL#HhKqK)'"KB4jGAk%JS1'K(Y +eHS5'JS1)I(0mJS+&KS&mHhPrKi+$KRYfIB+#JSD%J(KeKBKqK)L!GRTqIiH%JB5 +!HAYrJ)5(JAprHhjrIS5'IRf$J(b#JRq#JS+!I(arIRf'L)#!IRTqJS#(LAekIhT +fKSb%Ji"kHAKqLif"IS*eFB'*L)L$IAYfGSD3!)1"KATbGi5+L)1!J(GfJiQ'JS* +qGhQ"LB5!JhpiHS1(K)#!JRefIBQ&IB'#IRYmJSD#IS+$Hhf%JAq!JB'!J)'%IhP +rKB*rJB0qHRb"K)+$K(jjHi'#JS5%JAPjIi+!JSL'I(ClJS'!LBTpGRKqK)1%L)* +hGi#$JSD$IRajI)5'Ji*rHRZ"JB+)JRTkIAq"Ji1&JAGjJi+!K)@!HhYlIi5$JB& +qI(b!Ji5#IAamIS'$KB*mHRk!J)1(JhKhIS1!J)L'I(9jJB1&K)@#GR4pKSD%KRp +hGhf"K)Q'I(KiI)'%KiGqH(KpJB@&Ji0mGAb&JS#&JRPjIi+$Ji+"IAKmK)@#Ihk +!IRPrLS9lIB"lIB1$K)0mHAf!JS@$IhjlHi#$K)9qHRepIB+)JheqI(f"JSD%IRT +mIAk&KB+#I(KpJB1)KRakI(KpLBU$IATjIB#%LiGjH(apJ)@(KAphH)#$K)H$Iha +iHi1&KB@"HRPqJ)1&JS4qGRZ#Ji5&JRemHRf(KB'$IRPlJB1&KAprIAQ"KS+!JRp +lIS#$KS"pJApkJ)H#IhpqJ(jqKSL"I(jpIB1%KB9pHAjrIBH+JAprHAQ"KB@&IRe +qGhk*Ki'#J(PmIS'*KRaqJ(KmLBL#J(emIAQ!MSCmIhpjI)'"KiPrHhjmIB+"K)U +!H(aqIS'$K)@!HAq$I(q'JRprIS#!I(k%JAq&JRaqIRf#K)+#IhTmIRk%LS*pIha +lIS+)KRakJ(jjJBL'JAalIRjmK)H"IhemI)#%Ji'!J(jlIS5$Ii'#I(U!Ji+"JB0 +mHS#"JS1#IhaqI(q&JS'"IAU!Jhq!Ji"lHi+&Ihk$J(GmKS0rJB*mH)#&JAk"JRj +lI)'%JRq"K(ahIi0rJ)H$H(b#IRk&KhplHhk"Ii#%JRemJ(prJS+"IherJAepKB4 +lIi9rHRq$Ii##J)#"I(b#JAb%KhelIhjpJB5%JhekIhjmJSD$JAjjI)*qJ)L$HAZ +!IAk%K)*rI(aqJB'$Ji"mI)#!J)5&IhapIAk!JiD#I(b!IRZ&Ki'!IRaqIRq%LAp +jJB"mIi1%JhpjIS4pIiD#IAjqIS'!Ii4rHi#$JAq"J)"rHS''J(f"JRjpJ)5$J(q +!IRf"Ji'%JRjqIAq"K)1#JAYlIRq$KB5$IAPqJS+#KB0pI(apJS5$K)&mIAppJBH +%Ii"pHRq"JS@$IRjrHRk(K)#"J(emIAq'KRjrJRjlIi+#JS+!J(emJB1#JB1$IAa +mJ)1$JB#$IhPqKB*rJi0pIApqJB5"J)*pHS+%IRq%JAjqIS1$IAq$IhZ"K(q"JAf +"J(aqKS0kJB*mIS+%J)#!IRppJ)1$J(aqIhjqJSH#I(k!IRb"KS0rIRemIi+$Ji* +qIAepIiD'IRb!IAPrLBD!IhplH(k%Ki9rIRajHS#)L)0pIAehI)@'K)1!GhPrIS1 +(K)&pHRTrJS+%JRjpHRf#JS'%JhTlJhjmJS@"IRapJhplJiL"HAk$IRYrKi4lIS0 +rHAb%K(pqJB*mHS#'JAf#JRajJ)4rIi5$IAZ!J)##JB'!Hhb!J(q#K(emIi#!Ii1 +&IhGpK(pqJi4rIAjpJS0qK)0mHhq#I)#&K)&lJ)*qIS'#IhprIi+!Ii#!J(q%Jhb +!JRalJB@#Ii'#HhU"K)@"J)"pHAU'LB&pIi&kHB+*L(YlJRYfJ)Z(JAjjIApjJC! +!L(ClJRPiKBQ(K(YiIhekKSb$IAjpHRf#KSH!I)"rHhq)KAerJAjpJ)5"J(jqJS" +pJiKmGS1%Hhq'KRjlI)'%Hi'+JhGkKB"lIiD)I(Q!K(jjKBGrIRk#IheqJiCmIS1 +!IAf$JS#!J)0pHS'$J(q%JheqIAq$JB'%JRTlJi&pK)D"HhTrJS"qKSCkH)##IRq +(KAjiHi1"IS1+JR0kJi"lK)b!HRTrJRerL)YkFS1#H(f(LAelIB'!Gi+,JRKpKRe +kIi5'IRb"JhpjJB0rIi'%Hhb"IRk!K)&pIhf!J(f$KRjiIS0qIS+%KAYfJBClI)Q +&HRTmIi0rISL'G(H&JAb!Ki4mGRb(J(U'KhahIB#"Jhb"KAPjJS0mJSClIB"qJ)# +#JB"lHiD!IS+"IhYqIi1$Hi'#HhU#L(pqJ(jmHB'%JS*qIReqIB++IhZ!IATrK)+ +$JAPmJ(b"Ki0qJ(YjJi*rKS4iH)"pISL'IhplGhk%JS@(IRKkI(k%L)1!IRPlIS' +%KS0kIApjISH(Ii##HharJBD$I(b%IRH&L(jmJB"pJRf"L(YjK)4jISGqIS'!J)& +rIB*qHi@$IRq#IhU!JB1%IS'$HRH#LAjrKS&kHRq%Ji*rK)&hHi1'JRq$J(elIiL +#IS+!HhU%JS#'JAYpJ(arLS0pJhpjISD!J)GpHRpqIi1%Ii+"HAq#IB+'IhZ$IAL +'KRf"KRaiK(jpKi0pIi0lI)@!JS@!IAk"IB##Ii@"Hi#%IAU%K(k!J(q!Ihf!KAp +mJi*pI)1$IAk#JhpmJ)'!Ihk$JRjrJ)"pJB+!JAjrJAjrJS*mJ)9mIS5!IB##Ii* +qHSD$HAq'J(Q!J)#%IAk&J(GrKherKApqIhemKB0mK)&mIS"pIiQ"HS+!Hhb"K)+ +#HRb%IAU#LRpiJB"pIi#$K(jiJ)CkI)H$I(eqIB1!HS@'H(Z&J(f%J(Z#IhQ"KRj +qJhamJRjrKB*lIRppJS&rKB0jHi1"I)1$J(jlIS+$J)#%J(alIi5!Ii'"IRb!J)+ +#IS#!IRjrJS*rI)'$I(k&J(b!Ihf$Jhf#JRPmK(prKB*mIRamK)+!KS0hHi0mISL +'IRjqHAq#IiD)IAKrJ(Z!L)@!IAamIi##Ki0mIS"pI)5%IB+"I(q"IS#&IRZ&J(Q +"KRjpK(pqJRk!Ji&mJS4lIi*rJ)&rIi9rHS5#I)'&J(k%IAZ$JAk$KRjmJRpqJS+ +#JRppIi&rJi*qJB&mIB5$Ii+!IB#!Hi+*IhU$JRYmK)@"Ihk$IhKrKi4mIi*qIRf +!L)0lIi4pGi1'IS'%J(arIi#&JRk$J(YpJi&rJi*rIRerJi&rKB0kHi1"IB1&J(p +pIB+$IS#)J(GqK)"mJiD!IRZ!JhppJSGpH)+%Ihk"JRpqIS'&IRk%J(PrKB'!Jhp +qJ(arKB&qJi&jIB0qJ)@!IS&mI)1"IS5%HRb#JB'"J)'"HAZ'JRb#JReqIRk%KRa +mKAjhJ)@#J)"rIhplJB9rIB#!IRq"JS*qI)'#I(k&JRPqJhemKS4lJB*lI)+#JB4 +mHi5!HB'*JAPrJB"rI)@)HRL$JhQ!LB&pIRYqJRprLB9hI)0pHS5(J(jqIAapJi@ +$IRjrIAerK)0rIhk!IAb$Ji&pJ)4jI)5#Ii#$IhprHi#$J(q#JRYqJ(k"JB+#Ihp +qIhjrK)'"JAjpJ)"qJS0rIS"qI)1$J)+!IReqIi#&JAq!I(q!J)+&JhYpJReqK)5 +!J(plJ)*qJBH"HRk!I)#%JS1"I(f"Ihf'K(arJ(YqJi+!KB"lJB"pJS9rIi0mHS+ +#Ii5%IS&rH(q'JAk&KRTmJ(q%JB#$JRajJi0qJi5"IhjmJ)5!J)5!I(f"IS+%J)' +!IRerJi'$JAf"J(apK)CqIi&rIAf"Ji0rJ)*qI(q#Ji#"J)+!HAk&JRq"K(jpJ(k +"Ji'"JB"pIi"rJi0rJ)"qIS+"JB*rI)'"I(q(JAb!JAprJB#$JhTpJi"mJBCrIB" +rIi#!JS0rHi+"Hi'$J(q"IRf%Ihf'JRTqJhelKS*pJS"mIi*rJB4qIS*qIB'#IS' +!I)+"I)#&J(U"JAarJB'"IharJhprK)*pIApqJB'!JhjlIi&rIiD$HRk!Ihq!Ji& +rI(f$Ihk$K(jlJB&qIi+$IRarJS"qJi4qI(q#Ii#$J(pmHi#$J)#'JAPrJheqK)* +qIAerJS"rKB0kHi&rJ)1!J)"pHi'%Ii'$IAarIRk$KAjqJ(emIi+"JS"mIS"qIi5 +%Ii"qI)#"Ii#%JAZ"Ihf#JB#"JAepJhjpJS'!JB"qJ)"mJB1!J)+!I)#"Ii+"J)# +!IAf$JAq"JAppIi##JRq"J(jpJ)1"JB"rJ(erJi5!IS0rHB#%JAq#JRjqIB#'JAf +#JRamJi+"JAk!JAeqKi*lJi4kI)@#Ii+"IS"qIB5%IB#%IhZ!K)'!Ii'#IRf#K(p +qJS&qJB1!Ii"qIi#"JB+#I(f#Ihq%JhaqJRerK)*rJAplJS4rJS*rIRprJB4rIi0 +qIB'$J(q"Ihq"IS'%JAarK(ppJi4rIAprIi##KS&pIB"qI)5)JRepJ(jpJ)D(IAU +"IhZ!Ki1!IheqIRq#KB"pJAjkJB5#IRk"IhemJiCpIB1"Hhk%JB#"IhprIAq%JRk +!JRepJS&rJ(pqIi#"JS&qIRppIi5$IRprHhb#Ji1$J(apIAb&KhjrJRYjJ)1"Ji* +mIApmJ)5$J)"qHRq#J)+#IhalIS'%JS#$IRPpJi1!JS&pI(aqJi@!Ii&pHRf$JS+ +$IhjpIB##K)'!IhjqIB'&JAk!JAapJS#"JB"qIS"qIi+!J)#!J(prIRq"J)'!J)" +pI)#$Ihq%JhYkJB"rJS+#J(elIi4qIi@"I(f!Ii#$J)'!HRk#Ii#%JRf!J(b#KB# +!JRjmJS"qKB4mI)+!I)+%JS&pIB#"IAq&JRjpJ)'!Ihq%JAYrK)"lJ)0qJ)'!JB# +!Ii+!IB1#IRk#JAb!Ji'!IS'$IRb!KAppJi*rIS#"JB"rJS"lJ)5"IB+$IAk!JS* +rIS#"I(k&JhprJApqIi'%JAf!JApqJB1!JB"qJ(q!JB+#IB'#I(k#JAq!Jhq!JAk +!JRprJi&qJB"rJAprJS*mIi4rIB'#IS##IS##IAk$JAf#JRarJhpqJS&qJB"rJhp +mJS0pIB5!HS+"IS'!Ii'#I(k&IRf&JRarJReqJRq!JRjqJAppJi*pJ)*qI)#"J)* +rJ)&qIB'$Ihq$IRerJ)#!JS#!Ihb"JAprJS&lIi&qIi##J(pqIS+!IS+"IAk"Ihk +#J(q"IRk#J(Z"JhaqK(pmJB&pJ)&rJAppJ)*pIS1"IRpqJ)&qIS'#IAk#Ihk!JRj +qJS"qJB&rIi&qIB+!Ii+#IRq#IAk#Ii#"IRq"IRb%K(Z!K(jmJB"qJRppJS&pIi0 +rIB&rJB*rJ)&rIB#"I)+$IAf"JRerJS'!IRq!J(pqJi&pJ)+!IB'#Ii"qJ)*rIB+ +%I(k%Ihb"JAk!JAq#JAb!JReqK)&qJi&mJ)"pJS*qJB4mHi+!IS+$Ihq!I)##IS' +$J(k!J(f"JAq#JRjqJB"qJB'"JS"qIi+!IS+#Ihq!J(q"JB'#J)#"J(k!JS#!JAp +rJ)"rJS+!JS"mIi&rIS1#IS"rIi#"JB##J(jrJ)#!JB'!Ihk"JRprJS&qJ)&qJB& +rJ)'!IS+#IS+"IRq"JAq!J)##Ihk$J(jrJS'!JS#!J(jrJS"rJB*rIB#!IS'#J)' +"IhprIi#$JAf"JRepJB*rJB&rJAjpJB*rIi*rIS&rIi+!IS'"Hhq$IRq#J(k!J(f +!JRq"J(jrJ(prJS#!JReqJRpmJ)4rIS&rIRq!JB&rIS'!IRq"JRpqIi'"I)#$J(e +rJhjpJS&rIB'"IS"rJB&qIi'!IAq$J(erJS"qJ)+#IAb#JReqJS*pIS#!J(q!JB" +qIS&rIS#"J(q!J(prJ)&rIi'!IhprJ(prIi'"IhprJ)"rJ)+#IRk"Ihk!JB'!IRk +!J(k!Ji&qIS#!IAk#K(jpJB"pIi+"J)"qJ(pqJ)+!Ii#!IRprJB*rJ)+!I)#$IRk +#JRjqJ)#!Ihq#JAprJ)"qJ)#"JAk!JAprJB0rIi&rIRq!JB'"J(prIi#!JS+!IS" +rIB##JB'!J(pqIi#$JB#!J(ppJ)+"JB'"Ihk!J)'!Ii+!IRq"J(q"JB"rJ)#"J(q +#JRjrJS&rJ)'"J(pqJB&rJ)+!IS#"Ii#"JB"rIi#!Ii#"J)"rIi'!Ii##J(k"JAp +rJB'!J(q!JAk!Ji"rJB&qIi'!JB&qJ)&rIS+$IRk"J(q!JB'"Ihk"JAq!JS'!Ihp +rJ)#"JB&rIS#!J)##JB#!IRk"JAq!JhpqJ(q!JB#!J)*rIS#!Ihk!JB#!J)#!Ihp +rJ)&rJ)'!IhjrJB&rIi'!IB#"Ii#"Ihk!Ihk"JRq!J(prJ)"rJB&qIS#!J(q"JRp +qIi#!IS#"JApqJ(prJ(q!JB"pJ)*qIB'"IS#"IS#!IAq#J(f!JRpqJ(q!J(q!JAp +qJ)"qJ)&qIi&rIRq"J(q!Ihq!Ihq!JAjqJi"pJ)'!IS#!Ii"qJ)*qIS'"IS#"IRq +!Ii#"Ihq!IRk"JB#!JAprIhq"J(k"JAjqJB"rJB"rJ(jrJB"qJ)*rIS#!J)#!Ii# +!IS##J)#"J(prJAjrJRprJB"qJ)&qIi*rIi*rIB'"IS##Ii##Ihq"Ihk#JRq!JRj +rJAjrJS"rJB"pJ)&rJB&rIi#!J)#!J)*rIB'!Ii#"J(q"IRq"Ii##J(k!JAq!JB# +#J(erJRppJS*rIi&rIi'!J)+"Ii#"IRq"J)#"JAprJ(q!JAq"JRprJB&pJ)0rJ)' +!Ii#!Ii+#IAq"Ihq"JB#!Ihq"Ihq"JAq!JAprJAq!JRprJB"rJ)"rJ)#!JB&rJ)& +pIi+!Ii'"IS#!Ii'"Ii#"Ihf!JAq!J)'!IS#!JB"rJB"rJ)#!Ii#!Ii#!J)#"J(k +!J(q"JAq!JAjrJB"rJ)&rIhpqJB&qIi"rIS#"Ii'!IRprJ)#"JAprIRq!J)#!JB" +rIRq"J(q"J(prIhq"JAprJ(prJB#!J(jqJ(prJB&rIhprJ)"rJ)"rIS#!IhprJ)" +qIi&rJ)&rIS#"Ii#"IRprIRq"JAq!J(erJ)#"JB"rIhjrJB"rJB"qIS'!IS'"Ihk +!J(q!J)#"J(jrJApqJB&qIi'!J)"qJ)"rJ)+!IS'!I)##J(q!J(prIS#$J(q"J(e +pJ)'"J(q!IherJB'!J)"rIhq!JB"rJ(pqIi'"JB"rIRq!J)'"J(prIi#"JB#!J)" +rJ)'!IhprIi"rJ)+!IRprIi'"J)'"IRf"JAq"JAprIhk!JS#!JS"pIi#!JB'!J)" +rIi#!J)'!Ii#"J(q"JB#!J)#"J(q!JAq!J)#!J)#!J)"rJB&rJ(pqJ)#"JB#!Ii" +rIi'"J)"rIhprJB'"J(prIhq!J)'!J(prJ)#!J)"rIi"rJ)+"IhprIhq"JB'!Ihq +!Ii#"JB"rJ(prJB'!Ihq!J)#!JB&rIS"rIi#"J(prIRq!J)'"J(pqIS#"JB'!IRp +rIS#"J)#!Ihq!J)#"J(jrIhk!JB#!JApqJ)#!J(q!J(prJ)&rIi#!J(q!JS"qIi# +!J)#!J(pqJ)#!J)'!IRprIhq!JB"qIRprJ)#!J(prJ)#!J)'!IRq!J(q!JB"qIRq +#JAq!J(jpJ)#!JB"rIhpqJ)'!J)&qIB#!Ii'"IhjqIS#"J)#!IhjrJ)'!J)#!Ihq +!J)#!J)"rIi"rIi#!J(prJ)#!Ihq!J(prJ)"rIhprIi#"J(prIhprJ)'!J(jrJ)# +!JB&rIi"rIi#!J)"rIi#!Ii#"Ii#"Ihk!J(q!J(q!J(q!JAprJ)"rIi#!J(prJ)" +rJ)#!Ii#!Ii#"J(prJ)#!J)#!J(prJ)"rJ)'!IhprJ)#!JB&rIi#!J)#!J)"rIi# +!J)#"J(q!Ii#"J)#"Ihk!J(q!JB#!J(q!JB'!J)"rIi"rJ)#!J)#!J)#!Ii#!J(q +"JAprJ)#!J)#!JApqJ)"rJ)#"JB"qJ)"rJ)'!J(q!J)"rJ)&rIi'"Ihq!J(prJB' +!Ihq!J(q"JB#!IhprIi#!JB#!J(prJ)'!Ii#!Ihk!JB"rJ)"rJ)#"JB"rIhprJ)# +!J)"rIi#!JB"rIi"rIi#"J(prJ)"qJ)+!IRq!Ii#!J)#!IRq!IRq"JAprJ)"rIi' +#Ihk!JAjrJB"rIi#!Ii#!J(prJ)"rIi#!J(jrJB"qJ)&rIRq!J(prJ)"qIi#!Ii' +!Ihq!IhprJ)"rIRq!Ihq"JB"qIi#!Ii#"Ihk!Ihq!JB"rJ(q!J(k!JApqJ)'!J)# +!IhprJ)&rIi&rIi#!Ihq!J(pqJ)#!Ihq!J(prJ)'!Ihq!Ii#!J)#!Ihq!Ihq!J)# +!IhprJ(q!JB"rIhprJ)#!J)"rIRprJ)#"J(prIhprJ)'!IhprIhprJB'!Ihq!Ihq +!J)#!J(q!J(q!J(q!J(prJ)#!J)#!J(prJ)"rIi#!J)"rIi"rJ)#!Ii"rIi#!J)" +rIi"rIi#"Ihq!J)#!Ii#"Ihq!J(q!JAprJ)#!J)#"J(prJAprJB&rIhq!J(prJB& +rIi#!Ii#!JB"qJ)'!Ii#!Ii#!J)"rJ)'!Ihq"J(q!J)"rIi#!Ii#!J(q!Ii#!J)# +"Ihq!J(q!JB"rJ(prJB#!JB"rJ)"rJ)#!J)"qIi#!J)#!IhprIi#"J(q!J)"rIi' +!J(q!J)#!Ii#!Ihq!J(q!Ihq!J)"rIi#!Ihq!J(prJ(prIi#!J)#!J(q!J)#!Ii# +!Ihq!JAprJ)"rIi#!J)"rIi#!Ii#"IhprIi"rJ)#!IhprIhq!J)"rIi"rIi#"JAp +rJ)"rIS#"IhjrJ(prJ)'!IRk"J(k!JApqIhq!J(q"JAjqIi"rIi+!IRprJ)"rJB+ +!IRq!Ihq!JB"rIhprIhq!JAprJ)"qIi'!Ihq!J(prJ)#!IRq"J(jrJ)"rIi#!J(p +rJ)"rJ)"rIi'!IS#"IhjrJ)#!J)#!IhjrJB"rJB"qIhq!J)#!J)"qIi'!Ii#"Ihk +!J)#!Ihq!Ihk!JB#!IhprIi#!JB'!Ihq!Ii#"JB"rIhq!J)#"JApqIhprJB'!Ihp +rIi#!JB&rIS#!IRq#JApqJ)"rIi#"J(jrJ)"rJB&rIS#!Ihq"J(prIi#"Ihq#JAj +rJB"rJ)#!J(prJB"rJ)"rIi'!Ihq!J(q!JB'!Ihq!J(q!JS"rIi"rIi#!J)"rIi# +!Ii#"Ihq!J(q!J)#!Ihq!J(q!JApqJ)#!J)#"J(prJ)"rJ)#!J(prJ)#!JB&rIhp +rJB"rJ)"rIi#!Ii#!IhprIi'"Ihq"J(jrJB'!Ihq!Ihq!JB"rJ)"rIi#!J)"rJ(p +rJ)'!IhprIhjrJB&rIi#!Ihq!JB"rIi&rIi#!Ii#!Ii"rIi#!Ihq"Ihq!J)"rIi# +!J(q!J(prJ)#!J(prJ(prJ)"rJ(prIhq!J)"rJ(prIi"rJ)"rIi"rIi#!IhprIhq +!Ii#!IRq!J(q!JB"rIhq!Ii#"J(jqJ)"rJ)'!IhjrJ(prJB&qIRq!J)#!JB"pIS# +!J)#!J(pqIi'!J)'!IRq!Ii#!J)"rIRq"J(q!J(prIi#!J(q!J(prJ(q!J(prIhq +!JB"rJ(prIhq!J)"rJ)"rJ)#!J)#!IhprIi#!J)"rIi#!Ii#!J)"rIi#!Ihq!J(q +!Ii#!Ihq!Ihq!J(q!Ihq!J(q!JAprJ(q!J)#!JB"rJ)"rJ)#!J)#!J)"rJ)#!Ii# +!J)"rJ)#!Ii#!J(q!Ii"rIi#!Ihq!J)#!J)"rIi#!J(q"JAprJ)"rJ)#!J(prJ)# +!J)"rIi"rJ)#!J)"rIi"rIi#!IhprIi"rIi'!Ihq!J(q!J)#!Ii#!Ii#!J(prJ(p +rJ(q!J(prJ(prJ)#!J(q!J(prJ)"rIi"rIhq!J)"rIi#!Ii"rJ)#!Ii"rIi#!Ii# +!Ii#!Ihq!Ihq!J(q!J(q!Ihq!J(prJ(prJ)#!J(prJ)"rJ)"rJ)"rJ)#!J(prJ)" +rIi#!J)"rJ)"rIi#!Ii#!Ihq!Ihq!Ihq!J(q!Ihq!J(q!Ihq!J(q!J(q!J(prJ(q +!J)"rJ(prIi#!J)"rIhprJ)&rIhprIi#!J)"rIi#!Ii#"J(q!Ii#!Ii#!J(prIhq +!Ihq!IhprJ(q!J)"rIhprIi#!J(prJ(prJ)"rIhprJ)"rJ)#!IhprJ)"rIi"rIi# +!Ihq!IhprIi#!Ii#!J(q!J)"rIi#!Ii#!J(prIi#"J)#"J(jrJ(q!JB"rIhprJ(q +!JB"rIi"rJ(q!J(prJ)"rIi'!Ii#!Ii#!J)#!Ii#!J)#!Ihq!Ii#!J)#!Ihq!JB# +!J(prIhq!J(q!J(prJ)#!J(prJ)"rJ)"rJ)"rIi#!Ii#!Ihq!Ii#!J)"rIi#!J(p +rJ)"rIi#"J(q!J(prIi#!J(q!J(prJ)#!Ii#!J(q!J)"rJ)"rIi"rIi#!Ii"rJ)# +!Ii#!Ii#!J(prJ)"rIi#!Ihq!J(q!J)#!J)#!J(q!J)"rIi#!Ii#!J(q!J(q!J)" +rIi#!Ii#!IhprIi#!Ihq!J(prJ)"rJ)"rJ)"rJ)"rIi#!Ihq!J)#!Ii"rIi#!J(q +!Ii#!Ihq!J(q!J)"rJ(q!Ihq!J(q!J(q!J(q!J)"rJ(prIi"rJ)"rIhprJ)#!Ii" +rIi#!Ii#!Ihq!Ihq!J)#!J(prJ)"rJ)"rJ(prJ(q!J(q!J(prJ)#!J(prJ(prIi# +!Ihq!J)#!J)"rJ)"rJ)#!Ii"rIi"rIi"rJ)"rIi#!Ihq!J(prIi#!IhprJ(prIi# +!Ihq!J(prIi"rJ(q!J(prJ(prJ(prJ(prIi#!J)#!Ii"rIi#!J(q!IhprJ)#!J)" +rIi"rJ(q!J(q!Ihq!J)#!J)#!Ii#!J)#!IhprJ)#!J)"rJ)#!J)#!J(prJ)"rIi# +!J(prJ)#!Ii#!IhprJ)"rIi"rIi#!J)#!J(prIi#!Ii#!IhprJ(q!J)"rIhq!J(q +!Ii#!Ii"rJ)#!J(prJ)"rJ)#!Ii"rIi#!J)#!Ihq!J)#!Ii"rIi#!Ii#!Ii#!Ihq +!J(q!J(prJ)"rJ)#!Ii#!Ihq!J)"rJ)"rIi#!J)"rIi#!Ii#!J(q!Ihq!J(prJ(q +!J(q!J(q!J(q!J)#!J(prJ)"rJ)"rJ)"rIi#!J)"rIhq!Ii#!J)#!Ihq!Ihq!J(p +rIhprJ)#!J(prJ(q!J)"rIi"rIi#!J)#!IhprIi#!J)#!IhprJ)#!Ii#!IhprJ(q +!Ii"rIhprIi"rIi"rIi#!J(prJ)"rIhprJ)#!Ii#!Ii"rIi#!Ii#!Ihq!J(prJ(p +rIi"rJ)"rJ(prJ)#!J(prJ)"rJ)#!Ii"rIhq!Ii#!J(prJ(q!J)"rIi#!J)"rJ)# +!Ii#!Ihq!Ihq!Ii#!Ii#!Ihq!J(q!J(prJ)#!J)#!J(prJ)"rIi"rIi#!J)"rJ)# +!Ii#!J(prJ)"rIi#!J)"rIi"rIi#!Ihq!Ihq!J)#!J(q!Ihq!J)#!J)"rJ)#!J)# +!J)"rJ)#!J)"rIi#!Ii#!J)#!Ihq!J)#!J)"rIi"rJ)#!J(q!J(q!J)#!J)"rJ(q +!J)"rJ)"rIi#!J(prJ)"rJ)#!Ii#!Ii#!J)#!Ihq!J(q!J)"rJ)"rJ)#!J)#!Ihq +!J)#!J)#!Ii"rJ)#!J)#!Ii#!J)#!J(prJ)#!J)#!J)#!Ii#!J)#!J(q!Ii#!J)# +!J)"rIi#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)#!J)# +!J)#!J)#!J)#!J)#!J)#!J!!!!3!!!`U%!!-*K!!!!8%!di-8&i)!!!!F!,B!!(0 +ZC#!!#`!+90m!!#!!!!!!di(S8%N!$#!!0d3!di(`4!-!&b!!HBJ!di(N*8S!)b! +![-`!di(J8L8!-#!"!hS!di(FGdN!Ib!"5,i!di(B,83!2b!"G3)!di(8+f3!FL! +"R8B!di(38KX!5L!"dBS!di(-++X!9#!#!-i!di()2VF!Cb!#N[`!di(%C'd!E5! +#ad!!di(!#e4[EfaPC#")Eh*Z#NeKCfPM)%KKFR!,6@&RD@-J4QaeG'8-6'9KG'K +PFL"%FR9Y$NK[FQiJ6fBJ8'aPER4j#NCbEh0d)%K[FQi*4QPbC5")Eh*Z%N4bG@d +J6fBJ4@&bG'KaG@&VC39#G@GXC34#C@aX$&G[Ef4PEL"'E(9dC3YAEfpNC@iJ5'& +bF"31: diff --git a/sys/mac/News b/sys/mac/News new file mode 100644 index 0000000..ddbc48d --- /dev/null +++ b/sys/mac/News @@ -0,0 +1,9 @@ +Welcome to NetHack 3.4 for MacOS 7.0 - 9.x + +Unfortunately, the 68k version is no longer supported. + +This game is brought to you by Dean Luick, Kevin Hugo, and Mark Modrall. + +Bug reports, suggestions, comments, etc., should be e-mailed to the +Internet address nethack-bugs@nethack.org, or fill out our comment +form on the web at http://www.nethack.org. diff --git a/sys/mac/README b/sys/mac/README new file mode 100644 index 0000000..dae2192 --- /dev/null +++ b/sys/mac/README @@ -0,0 +1,36 @@ +Jan 2002 + +The MPW compilers are now supported again. + +Support for 68k has been discontinued due to a lack of a debugging +system for 68k binaries. + +Note that the tiled MacOS X port uses the Qt windowport and the UNIX +build system, not this windowport code. + + +26 Nov, 1999 + +NetHack 3.3.0 was built with Metrowerk's Pro 4 compiler on a PPC +system. We are still compiling with 68K alignment because we know +it works. No one has checked lately if the PPC alignment bug +still exists. + + +23 May, 1996 + +NetHack 3.2.1 was built with Metrowerk's DR8 compiler on a PPC system. +The official 68K and PPC versions were compiled with 68K Alignment +to share files. The 3.2.0 versions were compiled with PPC alignment, +but it was discovered that the Metrowerks 68K compiler has a bug with +PPC alignment and structures that can be aligned to a single byte. This +bug _may_ be fixed in DR10, it is not fixed in DR9. Why bother with PPC +alignment at all? Because the space saving from 68K alignment is small +and the PowerPC version will run better. The 68K version was compiled +with 4 byte ints using the far model. + +Only the Metrowerks compiler has been used to compile the code in a +long time. It is _very_ likely that the other compilers, Think C and +MPW C, will no longer be able to compile NetHack out of the box. They +and their files have been moved to the "old" directory until such time +that someone can compile with them. diff --git a/sys/mac/dprintf.c b/sys/mac/dprintf.c new file mode 100644 index 0000000..e646716 --- /dev/null +++ b/sys/mac/dprintf.c @@ -0,0 +1,47 @@ +/* SCCS Id: @(#)dprintf.c 3.1 94/01/29 */ +/* Copyright (c) Jon W{tte, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "macwin.h" + +static Boolean +KeyDown (unsigned short code) { +unsigned char keys [16]; + + GetKeys ((void *) keys); + return ((keys [code >> 3] >> (code & 7)) & 1) != 0; +} + + +void +dprintf (char *format, ...) +{ +char buffer [500]; +va_list list; +int doit; +#define DO_DEBUGSTR 1 +#define DO_PLINE 2 + + if (flags.debug) { + doit = 0; + if (macFlags.hasDebugger && KeyDown (0x39)) { /* Caps Lock */ + doit = DO_DEBUGSTR; + } else if (KeyDown (0x3B) && iflags.window_inited && /* Control */ + (WIN_MESSAGE != -1) && theWindows [WIN_MESSAGE].its_window) { + doit = DO_PLINE; + } + + if (doit) { + va_start (list, format); + vsprintf (&buffer [1], format, list); + va_end (list) ; + + if (doit == DO_DEBUGSTR) { + buffer [0] = strlen (&buffer [1]); + DebugStr ((uchar *) buffer); + } else if (doit == DO_PLINE) + pline ("%s", &buffer [1]); + } + } +} diff --git a/sys/mac/maccurs.c b/sys/mac/maccurs.c new file mode 100644 index 0000000..13d9f8f --- /dev/null +++ b/sys/mac/maccurs.c @@ -0,0 +1,223 @@ +/* SCCS Id: @(#)maccurs.c 3.1 93/01/24 */ +/* Copyright (c) Jon W{tte, 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "mactty.h" +#include "macwin.h" + +#if !TARGET_API_MAC_CARBON +#include +#include +#include +#endif + + +static Boolean winFileInit = 0; +static unsigned char winFileName [32] = "\pNetHack Preferences"; +static long winFileDir; +static short winFileVol; + +typedef struct WinPosSave { + char validPos; + char validSize; + short top; + short left; + short height; + short width; +} WinPosSave; + +static WinPosSave savePos [kLastWindowKind + 1]; + + +static void +InitWinFile (void) +{ + StringHandle sh; + long len; + short ref = 0; + + if (winFileInit) { + return; + } +/* We trust the glue. If there's an error, store in game dir. */ + if (FindFolder (kOnSystemDisk, kPreferencesFolderType, kCreateFolder , + &winFileVol, &winFileDir)) { + winFileVol = 0; + winFileDir = 0; + } + sh = GetString (128); + if (sh && *sh) { + BlockMove (*sh, winFileName, **sh + 1); + ReleaseResource ((Handle) sh); + } + if (HOpen (winFileVol, winFileDir, winFileName, fsRdPerm, &ref)) { + return; + } + len = sizeof (savePos); + if (!FSRead (ref, &len, savePos)) { + winFileInit = 1; + } + FSClose (ref); +} + + +static void +FlushWinFile (void) +{ + short ref; + long len; + + if (!winFileInit) { + if (!winFileName [0]) { + return; + } + HCreate (winFileVol, winFileDir, winFileName, MAC_CREATOR, PREF_TYPE); + HCreateResFile (winFileVol, winFileDir, winFileName); + } + if (HOpen (winFileVol, winFileDir, winFileName, fsWrPerm, &ref)) { + return; + } + winFileInit = 1; + len = sizeof (savePos); + (void) FSWrite (ref, &len, savePos); /* Don't care about error */ + FSClose (ref); +} + +Boolean +RetrievePosition (short kind, short *top, short *left) { +Point p; + + if (kind < 0 || kind > kLastWindowKind) { + dprintf ("Retrieve Bad kind %d", kind); + return 0; + } + InitWinFile (); + if (!savePos [kind].validPos) { + dprintf ("Retrieve Not stored kind %d", kind); + return 0; + } + p.v = savePos [kind].top; + p.h = savePos [kind].left; + *left = p.h; + *top = p.v; + dprintf ("Retrieve Kind %d Pt (%d,%d)", kind, p.h, p.v); + return (PtInRgn (p, GetGrayRgn ())); +} + + +Boolean +RetrieveSize (short kind, short top, short left, short *height, short *width) +{ + Point p; + + if (kind < 0 || kind > kLastWindowKind) { + return 0; + } + InitWinFile (); + if (!savePos [kind].validSize) { + return 0; + } + *width = savePos [kind].width; + *height = savePos [kind].height; + p.h = left + *width; + p.v = top + *height; + return PtInRgn (p, GetGrayRgn ()); +} + + +static void +SavePosition (short kind, short top, short left) +{ + if (kind < 0 || kind > kLastWindowKind) { + dprintf ("Save bad kind %d", kind); + return; + } + InitWinFile(); + savePos[kind].validPos = 1; + savePos[kind].top = top; + savePos[kind].left = left; + dprintf("Save kind %d pt (%d,%d)", kind, left, top); + FlushWinFile(); +} + + +static void +SaveSize (short kind, short height, short width) +{ + if (kind < 0 || kind > kLastWindowKind) { + dprintf ("Save bad kind %d", kind); + return; + } + InitWinFile (); + savePos [kind].validSize = 1; + savePos [kind].width = width; + savePos [kind].height = height; + FlushWinFile (); +} + + +static short +GetWinKind (WindowPtr win) +{ + short kind; + + if (!CheckNhWin (win)) { + return -1; + } + kind = GetWindowKind(win) - WIN_BASE_KIND; + if (kind < 0 || kind > NHW_TEXT) { + return -1; + } + dprintf ("In win kind %d (%lx)", kind, win); + switch (kind) { + case NHW_MAP : + case NHW_STATUS : + case NHW_BASE : + kind = kMapWindow; + break; + case NHW_MESSAGE : + kind = kMessageWindow; + break; + case NHW_MENU : + kind = kMenuWindow; + break; + default : + kind = kTextWindow; + break; + } + dprintf ("Out kind %d", kind); + return kind; +} + + +Boolean +RetrieveWinPos(WindowPtr win, short *top, short *left) +{ + return RetrievePosition(GetWinKind (win), top, left); +} + + +void +SaveWindowPos(WindowPtr win) +{ + Rect r; + + + GetWindowBounds(win, kWindowContentRgn, &r); + SavePosition(GetWinKind(win), r.top, r.left); +} + + +void +SaveWindowSize(WindowPtr win) +{ + short width, height; + Rect r; + + + GetWindowBounds(win, kWindowContentRgn, &r); + width = r.right - r.left; + height = r.bottom - r.top; + SaveSize(GetWinKind (win), height, width); +} diff --git a/sys/mac/macerrs.c b/sys/mac/macerrs.c new file mode 100644 index 0000000..12d419b --- /dev/null +++ b/sys/mac/macerrs.c @@ -0,0 +1,170 @@ +/* SCCS Id: @(#)macerrs.c 3.1 93/01/24 */ +/* Copyright (c) Michael Hamel, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +#if defined(macintosh) && defined(__SC__) && !defined(__FAR_CODE__) +/* this needs to be resident always */ +#pragma segment Main +#endif + +#include "hack.h" +#include "macwin.h" +#if !TARGET_API_MAC_CARBON +#include +#include +#include +#endif + + +void error(const char *format,...) +{ + Str255 buf; + va_list ap; + + va_start(ap, format); + vsprintf((char *)buf, format, ap); + va_end(ap); + + C2P((char *)buf, buf); + ParamText(buf, (StringPtr)"", (StringPtr)"", (StringPtr)""); + Alert(128, (ModalFilterUPP) NULL); + ExitToShell(); +} + + +#if 0 /* Remainder of file is obsolete and will be removed */ + +#define stackDepth 1 +#define errAlertID 129 +#define stdIOErrID 1999 + +static Str255 gActivities[stackDepth] = {""}; +static short gTopactivity = 0; + +void showerror(char * errdesc, const char * errcomment) +{ + short itemHit; + Str255 paserr, + pascomment; + + SetCursor(&qd.arrow); + if (errcomment == nil) errcomment = ""; + C2P (errcomment, pascomment); + C2P (errdesc, paserr); + ParamText(paserr,pascomment,gActivities[gTopactivity],(StringPtr)""); + itemHit = Alert(errAlertID, (ModalFilterUPP)nil); +} + +Boolean itworked(short errcode) +/* Return TRUE if it worked, do an error message and return false if it didn't. Error + strings for native C errors are in STR#1999, Mac errs in STR 2000-errcode, e.g + 2108 for not enough memory */ + +{ + if (errcode != 0) { + short itemHit; + Str255 errdesc; + StringHandle strh; + + errdesc[0] = '\0'; + if (errcode > 0) GetIndString(errdesc,stdIOErrID,errcode); /* STDIO file rres, etc */ + else { + strh = GetString(2000-errcode); + if (strh != (StringHandle) nil) { + memcpy(errdesc,*strh,256); + ReleaseResource((Handle)strh); + } + } + if (errdesc[0] == '\0') { /* No description found, just give the number */ + sprintf((char *)&errdesc[1],"a %d error occurred",errcode); + errdesc[0] = strlen((char*)&errdesc[1]); + } + SetCursor(&qd.arrow); + ParamText(errdesc,(StringPtr)"",gActivities[gTopactivity],(StringPtr)""); + itemHit = Alert(errAlertID, (ModalFilterUPP)nil); + } + return(errcode==0); +} + +void mustwork(short errcode) +/* For cases where we can't recover from the error by any means */ +{ + if (itworked(errcode)) ; + else ExitToShell(); +} + + +#if defined(USE_STDARG) || defined(USE_VARARGS) +# ifdef USE_STDARG +static void vprogerror(const char *line, va_list the_args); +# else +static void vprogerror(); +# endif + +/* Macro substitute for error() */ +void error VA_DECL(const char *, line) + VA_START(line); + VA_INIT(line, char *); + vprogerror(line, VA_ARGS); + VA_END(); +} + +# ifdef USE_STDARG +static void +vprogerror(const char *line, va_list the_args) { +# else +static void +vprogerror(line, the_args) const char *line; va_list the_args; { +# endif + +#else /* USE_STDARG | USE_VARARG */ + +void +error VA_DECL(const char *, line) +#endif +/* Do NOT use VA_START and VA_END in here... see above */ + char pbuf[BUFSZ]; + + if(index(line, '%')) { + Vsprintf(pbuf,line,VA_ARGS); + line = pbuf; + } + showerror("of an internal error",line); +} + + +void attemptingto(char * activity) +/* Say what we are trying to do for subsequent error-handling: will appear as x in an + alert in the form "Could not x because y" */ +{ C2P(activity,gActivities[gTopactivity]); +} + +void comment(char *s, long n) +{ + Str255 paserr; + short itemHit; + + sprintf((char *)&paserr[1], "%s - %d",s,n); + paserr[0] = strlen ((char*)&paserr[1]); + ParamText(paserr,(StringPtr)"",(StringPtr)"",(StringPtr)""); + itemHit = Alert(128, (ModalFilterUPP)nil); +} + +void pushattemptingto(char * activity) +/* Push a new description onto stack so we can pop later to previous state */ +{ + if (gTopactivity < stackDepth) { + gTopactivity++; + attemptingto(activity); + } + else error("activity stack overflow"); +} + +void popattempt(void) +/* Pop to previous state */ +{ + if (gTopactivity > 1) --gTopactivity; + else error("activity stack underflow"); +} + +#endif /* Obsolete */ diff --git a/sys/mac/macfile.c b/sys/mac/macfile.c new file mode 100644 index 0000000..535bf60 --- /dev/null +++ b/sys/mac/macfile.c @@ -0,0 +1,465 @@ +/* SCCS Id: @(#)macfile.c 3.1 93/01/24 */ +/* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */ +/* NetHack may be freely redistributed. See license for details. */ +/* + * macfile.c + * MAC file I/O routines + */ + +#include "hack.h" +#include "macwin.h" + +#ifndef __MACH__ +#include +#include +#include +#include +#include +#include +#endif + +#include "dlb.h" + +/* + * We should get the default dirID and volRefNum (from name) from prefs and + * the situation at startup... For now, this will have to do. + */ + + +/* The HandleFiles are resources built into the application which are treated + as read-only files: if we fail to open a file we look for a resource */ + +#define FIRST_HF 32000 /* file ID of first HandleFile */ +#define MAX_HF 6 /* Max # of open HandleFiles */ + +#define APP_NAME_RES_ID (-16396) + +typedef struct handlefile { + long type; /* Resource type */ + short id; /* Resource id */ + long mark; /* Current position */ + long size; /* total size */ + Handle data; /* The resource, purgeable */ +} HandleFile; + +static HandleFile *FDECL(IsHandleFile,(int)); +static int FDECL(OpenHandleFile,(const unsigned char *, long)); +static int FDECL(CloseHandleFile,(int)); +static int FDECL(ReadHandleFile,(int, void *, unsigned)); +static long FDECL(SetHandleFilePos,(int, short, long)); + +HandleFile theHandleFiles [MAX_HF]; +MacDirs theDirs; /* also referenced in macwin.c */ + + +static HandleFile * +IsHandleFile(int fd) +{ + HandleFile *hfp = NULL; + + if (fd >= FIRST_HF && fd < FIRST_HF+MAX_HF) { + /* in valid range, check for data */ + hfp = &theHandleFiles[fd-FIRST_HF]; + if (!hfp->data) hfp = NULL; + } + return hfp; +} + + +static int +OpenHandleFile (const unsigned char *name, long fileType) +{ + int i; + Handle h; + Str255 s; + + for (i = 0; i < MAX_HF; i ++) { + if (theHandleFiles[i].data == 0L) break; + } + + if (i >= MAX_HF) + return -1; + + h = GetNamedResource (fileType, name); + if (!h) return (-1); + + theHandleFiles[i].data = h; + theHandleFiles[i].size = GetHandleSize (h); + GetResInfo (h, &theHandleFiles[i].id, (void*) &theHandleFiles[i].type, s); + theHandleFiles[i].mark = 0L; + + return(i + FIRST_HF); +} + + +static int +CloseHandleFile (int fd) +{ + if (!IsHandleFile (fd)) { + return -1; + } + fd -= FIRST_HF; + ReleaseResource (theHandleFiles[fd].data); + theHandleFiles[fd].data = 0L; + return(0); +} + + +static int +ReadHandleFile (int fd, void *ptr, unsigned len) +{ + unsigned maxBytes; + Handle h; + + if (!IsHandleFile (fd)) return -1; + + fd -= FIRST_HF; + maxBytes = theHandleFiles[fd].size - theHandleFiles[fd].mark; + if (len > maxBytes) len = maxBytes; + + h = theHandleFiles[fd].data; + + HLock(h); + BlockMove (*h + theHandleFiles[fd].mark, ptr, len); + HUnlock(h); + theHandleFiles[fd].mark += len; + + return(len); +} + + +static long +SetHandleFilePos (int fd, short whence, long pos) +{ + long curpos; + + if (!IsHandleFile (fd)) return -1; + + fd -= FIRST_HF; + + curpos = theHandleFiles [fd].mark; + switch (whence) { + case SEEK_CUR : + curpos += pos; + break; + case SEEK_END : + curpos = theHandleFiles[fd].size - pos; + break; + default : /* set */ + curpos = pos; + break; + } + + if (curpos < 0) + curpos = 0; + else if (curpos > theHandleFiles [fd].size) + curpos = theHandleFiles [fd].size; + + theHandleFiles [fd].mark = curpos; + + return curpos; +} + + +void +C2P (const char *c, unsigned char *p) +{ + int len = strlen (c), i; + + if (len > 255) len = 255; + + for (i = len; i > 0; i--) + p[i] = c[i-1]; + p[0] = len; +} + +void +P2C (const unsigned char *p, char *c) +{ + int idx = *p++; + for (; idx > 0; idx--) + *c++ = *p++; + *c = '\0'; +} + + +static void +replace_resource(Handle new_res, ResType its_type, short its_id, Str255 its_name) +{ + Handle old_res; + + SetResLoad(false); + old_res = Get1Resource(its_type, its_id); + SetResLoad(true); + if (old_res) { + RemoveResource(old_res); + DisposeHandle(old_res); + } + + AddResource(new_res, its_type, its_id, its_name); +} + + +int +maccreat (const char *name, long fileType){ + return macopen (name, O_RDWR | O_CREAT | O_TRUNC, fileType); +} + + +int +macopen (const char *name, int flags, long fileType) +{ + short refNum; + short perm; + Str255 s; + + C2P (name, s); + if (flags & O_CREAT) { + if (HCreate (theDirs.dataRefNum, theDirs.dataDirID, s , + TEXT_CREATOR, fileType) && (flags & O_EXCL)) { + return -1; + } + + if (fileType == SAVE_TYPE) { + short resRef; + HCreateResFile(theDirs.dataRefNum, theDirs.dataDirID, s); + resRef = HOpenResFile(theDirs.dataRefNum, theDirs.dataDirID, s, + fsRdWrPerm); + if (resRef != -1) { + Handle name; + Str255 plnamep; + + C2P(plname, plnamep); + name = (Handle)NewString(plnamep); + if (name) + replace_resource(name, 'STR ', PLAYER_NAME_RES_ID, + "\pPlayer Name"); + + /* The application name resource. See IM VI, page 9-21. */ + name = (Handle)GetString(APP_NAME_RES_ID); + if (name) { + DetachResource(name); + replace_resource(name, 'STR ', APP_NAME_RES_ID, + "\pApplication Name"); + } + + CloseResFile(resRef); + } + } + + } + /* + * Here, we should check for file type, maybe a SFdialog if + * we fail with default, etc. etc. Besides, we should use HOpen + * and permissions. + */ + if ((flags & O_RDONLY) == O_RDONLY) { + perm = fsRdPerm; + } + if ((flags & O_WRONLY) == O_WRONLY) { + perm = fsWrPerm; + } + if ((flags & O_RDWR) == O_RDWR) { + perm = fsRdWrPerm; + } + if (HOpen (theDirs.dataRefNum, theDirs.dataDirID, s, perm, &refNum)) { + return OpenHandleFile (s, fileType); + } + if (flags & O_TRUNC) { + if (SetEOF (refNum, 0L)) { + FSClose (refNum); + return -1; + } + } + return refNum; +} + + +int +macclose (int fd) +{ + if (IsHandleFile (fd)) { + CloseHandleFile (fd); + } else { + if (FSClose (fd)) { + return -1; + } + FlushVol ((StringPtr) 0, theDirs . dataRefNum); + } + return 0; +} + + +int +macread (int fd, void *ptr, unsigned len) +{ + long amt = len; + + if (IsHandleFile (fd)) { + return ReadHandleFile (fd, ptr, amt); + } else { + short err = FSRead (fd, &amt, ptr); + + return ((err == noErr) || (err == eofErr && len)) ? amt : -1; + } +} + + +#if 0 /* this function isn't used, if you use it, uncomment prototype in macwin.h */ +char * +macgets (int fd, char *ptr, unsigned len) +{ + int idx = 0; + char c; + + while (-- len > 0) { + if (macread (fd, ptr + idx, 1) <= 0) + return (char *)0; + c = ptr[idx++]; + if (c == '\n' || c == '\r') + break; + } + ptr [idx] = '\0'; + return ptr; +} +#endif /* 0 */ + + +int +macwrite (int fd, void *ptr, unsigned len) +{ + long amt = len; + + if (IsHandleFile (fd)) return -1; + if (FSWrite(fd, &amt, ptr) == noErr) + return (amt); + else + return (-1); +} + + +long +macseek (int fd, long where, short whence) +{ + short posMode; + long curPos; + + if (IsHandleFile (fd)) { + return SetHandleFilePos (fd, whence, where); + } + + switch (whence) { + default : + posMode = fsFromStart; + break; + case SEEK_CUR : + posMode = fsFromMark; + break; + case SEEK_END : + posMode = fsFromLEOF; + break; + } + + if (SetFPos(fd, posMode, where) == noErr && GetFPos(fd, &curPos) == noErr) + return (curPos); + else + return(-1); +} + + +int +macunlink(const char *name) +{ + Str255 pname; + + + C2P(name, pname); + return (HDelete(theDirs.dataRefNum, theDirs.dataDirID, pname) == noErr ? 0 : -1); +} + + + +/* ---------------------------------------------------------------------- */ + +boolean rsrc_dlb_init(void) { + return TRUE; +} + +void rsrc_dlb_cleanup(void) { +} + +boolean rsrc_dlb_fopen(dlb *dp, const char *name, const char *mode) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(mode) +#endif + Str255 pname; + + C2P(name, pname); + dp->fd = OpenHandleFile(pname, 'File'); /* automatically read-only */ + return dp->fd >= 0; +} + +int rsrc_dlb_fclose(dlb *dp) { + return CloseHandleFile(dp->fd); +} + +int rsrc_dlb_fread(char *buf, int size, int quan, dlb *dp) { + int nread; + + if (size < 0 || quan < 0) return 0; + nread = ReadHandleFile(dp->fd, buf, (unsigned)size * (unsigned)quan); + + return nread/size; /* # of whole pieces (== quan in normal case) */ +} + +int rsrc_dlb_fseek(dlb *dp, long pos, int whence) { + return SetHandleFilePos(dp->fd, whence, pos); +} + +char *rsrc_dlb_fgets(char *buf, int len, dlb *dp) { + HandleFile *hfp = IsHandleFile(dp->fd); + char *p; + int bytesLeft, n = 0; + + if (hfp && hfp->mark < hfp->size) { + bytesLeft = hfp->size - hfp->mark; + if (bytesLeft < len) + len = bytesLeft; + + HLock(hfp->data); + for (n = 0, p = *hfp->data+hfp->mark; n < len; n++, p++) { + buf[n] = *p; + if (*p == '\r') buf[n] = '\n'; + if (buf[n] == '\n') { + n++; /* we want the return in the buffer */ + break; + } + } + HUnlock(hfp->data); + + hfp->mark += n; + if (n != 0) + buf[n] = '\0'; /* null terminate result */ + } + + return n ? buf : NULL; +} + +int rsrc_dlb_fgetc(dlb *dp) { + HandleFile *hfp = IsHandleFile(dp->fd); + int ret; + + if (!hfp || hfp->size <= hfp->mark) return EOF; + + ret = *(unsigned char *)(*hfp->data + hfp->mark); + hfp->mark++; + return ret; +} + +long rsrc_dlb_ftell(dlb *dp) { + HandleFile *hfp = IsHandleFile(dp->fd); + + if (!hfp) return 0; + return hfp->mark; +} + diff --git a/sys/mac/machelp.hqx b/sys/mac/machelp.hqx new file mode 100644 index 0000000..5f92520 --- /dev/null +++ b/sys/mac/machelp.hqx @@ -0,0 +1,67 @@ +(This file must be converted with BinHex 4.0) + +:#QeKBfKPE(!ZBQJ!9%9B9%0A588"#!!!#PX!!!'XRNmM#90$3e-J5@3k)%!S)bP +YB@0SC@a`,Q*S#6-Z-`Nj15m`-bma03dM#80[F(PbD@GSG#!SBbNJ-6Nj1#da16N +j)'*j)%YPGQPZ)%KeCfm0)b"1CA4)B@0V)'eKH5"LC5"QFQ9PE(NJFQ9NDA0dFQP +LGA4PC#iJ)&0PC5"XD@0PER0P)'C[FL"NCA4KD@ac,Jd0$5-M)b""FfYZB@eP)'4 +TB@a[Cb!M)b-04%P"6%p()$B`-$!JBA0VEQ&YC3d0)b"3E'&j)'*eG(4[EJda,Q9 +ZB@*XC@3J3faTBfXJD'9bC5"dEb"`E'&j)(4SDA-JBfKKFQ&MG'9b,Jda,Q4TFf& +LE'9N)%0XD@0V)'KPFQ8JG'mJF'aKH5"dD'Pc)'0SBA*KBh4PFLl#$5!J6Qpd)'& +fB@PXB@*XC5"LC@0KGA0P)(P[G5"SBACPELGd)(4jF'9N)'PZ)'%JEQ&YC5i0$5- +J8A9TG#"LGA4dEfi0-LiU)%0XD@0V)'KPFQ8JG'mJFA9TG#"dD'8JF(*[Ch*KE5i +0$5-J8QpXC5"`Eh"eF#"YC@je$63Z+L"6C@aPBh3JH@peFL"NCA0TFQ9N)(*[E'8 +J+%&bBfKPEfa[CfPcG#`J3Q&bBQ&bD@&Z,#"PG'-Z+F)0)(GTG'JJG'KTFb"`Eh" +eF#"YC@je,Jd0)b"5B@0P)("[F(9`)'ePER8005iU)&0PE'9MG#"jEh9b)'4PFfP +bC@3JFQ&MC5!S5(9YB@iX)%9XCL`JCA4M,LR#$5"hDA4S)(4SDA-JF'p`GA!JE@9 +ZG5i0$5-J4f9ZC'9b)("[F(9`)'ePER800LiU)&0PE'9MG#"jEh9b)'GPEQ4PFL! +SE@&XC5"[FL"QC@eKE'8T)(GTG'JJG'KTFb"`Eh"eF#"YC@je,Jd0)b""E'PREQe +PER3JF'p`GA!JE@9ZG3dh,LSJ8f9XC@0d)(P[GA)JB@aTCfjYC@jd)#KXBAGQG@` +X)'jPGA4bB@`X)'pb)'0SB@pdD@-T`JdJGfPdD#"dD'Pc)("[F(9`)'ePER8Z$3d +M)%e[C'8JF'p`GA!JE@9ZG3di,LSJ9A0P)(4SDA-JF'p`GA!JE@9ZG5"dEb"cC@a +PBh3JFQ9RG@aKFL"`E'&j)'e[C'8X`JdJCAK`E'pbC5"YEf4P,#"[FL"NC@*eCfG +TEQFJE@pNC5i0$5-J6Q&YC5"dCAKd)'PdC@d015iU)&4jF'8JD@iJH@peFL"MD'& +bB@0dCA)RFb"ZB@eP)'KPFQ8Z$3dM)%PMEfi0-6!Z+L"AD'mJDA-JG'KTFb"RGAN +r$3e&6N3Y4%P"6%p($3d0689195!a-MJJ3A"`E'80$6%ZC@jKBQaPC#"%DA0`E'& +jFb"TEQC[FQeKG'P[EL"KBQpeG#"dD'Pc)(CPFR0TEfiZ$3e&6N3Y689193d0$8e +&6P8J-6)j)%CTE'80$6!ZC@jKBQaPC#"'D@aP)%ePERA#`P9cC5"dD'Pc)'ePER8 +JG'mJE@&ZDA"eE'&dC5"hD@jNEhGc`JdJEfiJG'KP)(0MFQ9PEL`JB@jN)(4[)(0 +KGQ8JEh)JFA9TG#"dD'8JCf&YC5i0$6%Z+L"0B@YPFb"dD'8JE@&`)'&`F'9KFL" +[EL"dD'8JFf0bC@9Z,#"TEL"MBA0P)(P[G5"ME'pcC@6#$5"TG(-JGfPZC'ph,Jd +0-LiU)%C[FQ0PFb"dD'8JE@&`)(4[)'*P)(*PC(*KGfiZ$3dc,LSJ8f0bEfaXFb" +dD'8J6@9cFf&RCA-JGfPZC'ph)'*KBfXJEfjP)'ePFh0KCf8Z$3dd,LSJ8Q9`Eh0 +TG'P[ER-JB@aX)(GTEQ4[Gh-JFfmJG'KPH5"KFQ8JEfiYFf0bC@9Z,Jd005iU)%0 +XEh0PFb"dD'8JCR*[ER4YEh0d)(GTEQ4[Gbi0$6JZ+L"&ER4PFR-JCAK`E'pbC5" +YEf4P,#"hD'PMD#"TFb"K)'G[Ef3JGf&j)(4[)'aPBA*Z)(4SC5"RB@eP,Jd0-6! +Z+L"6BACPFb"dD'8JCf&YC5`JB@jN)(4SC@iJFA9TG(-JG'KP)("bEfGbB@dZ$3d +a-LiU)&&eDA4c)(4SC5"`FQpRFQ&Y)(GTG'K[GA3JFf&fD@jR)(4SC5"RB@eP,Jd +048j%,8e&6P80$3e048j9)$%c-#"&C'Pd$3d`,Q9ZB@*XC@3J4@4TG#"0C@je`X* +9Ff8JG'KTFb"YC@je)(4[)'9NDA3JG'9iG#"[FL"eEQ4[`JdJB@iJB@0dD@pZ,Jd +`,Q4TFf&LE'9N)%9NDA3J6@9ZGF,#9A0P)(4SDA-JE@9ZG5"dEb"PC'Pd)(4PH(3 +JEh)JG@jNEm)0)'&Z)'&MG'P[ELiJ)%&fB@PXB@*XC5"[EQaj)'C[FL"NCA0V)'& +MBf9cFfpbD@9c,Jd048j%,8e&6P80$3e048j9)$%c-5",BQ30$6!ZC@jKBQaPC#" +,CAPLEf&bC#"0C@je`X*9Ff8JG'KTFb"YC@je)(4[)(0TEA9XBA4P`JdJF(*PFh0 +TEQFJDf9jFb"[EL"K)'YPH@*[BA*N,#"cEb"jEh8JBf&Z)("XBANJC@jdDA*PE(R +#$5"LH5"YEh9cC5i0$8914#e048j9$3d0689195!a-c)J5'9XF!d0-#jPEQ&LE'9 +N)%KPE(!J6@9ZGF,#9A0P)(4SDA-JE@9ZG5"dEb"PH("XB@PZ)'K[Gm)0)(4SC5" +RB@eP)(G[FQYc,#"[FL"dEb"cCA3JEh"dD@pZFbi0$8914#e048j9$3d0689195! +a-c-J5@jQE`d0-#jPEQ&LE'9N)%PZCQmJ6@9ZGF,#9A0P)(4SDA-JE@9ZG5"dEb" +XEfpV)'&d)'pb`JdJE@&ZDA"eE'&dC5"jEh9b)'PZGQ9ZG'pbH5`JCAK`E'&TEL" +cEfePG'KTEQFJEfiJG'KP`JdJFf0bC@9Z,#"[FL"RDACP)'&Z)'pLDQ9MG#"[FL" +YEfjcG'9b)'%JEQ&YC5i0$8914#e048j9$3d0689195!a-c3J4A&eDA!0$6!ZC@j +KBQaPC#"&FA9TF'ePER3J6@9ZGF,#9A0P)(4SDA-JE@9ZG5"dEb"YB@jTF(9XBA4 +P`JdJGf9KF'pZFb`JBA*YEh)X)'&ZC#"[G'KPFL"hEh*Z)'PdC@ec,Jd048j%,8e +&6P80$3e048j9)$%c05""Bh30$6!ZC@jKBQaPC#""Bh4TEfiJ6@9ZGF,#9A0P)(4 +SDA-JE@9ZG5"dEb"`CA*QEh*Y)'&Z)'&MG'P[ELi0$8914#e048j9$3d0689195! +a-cBJ6@&RD@-0$6!ZC@jKBQaPC#"0B@GTBb"0C@je`X*9Ff8JG'KTFb"YC@je)(4 +[)'eKEQP`G@aKG'8JE@&RD@2#$5"TG'9YFbi0$8914#e048j9$3e048j9)$%c0b" +#DA4c$3d`,Q9ZB@*XC@3J3QPdFb"0C@je`X*9Ff8JG'KTFb"YC@je)(4[)("PFQC +[FQh#$5"YDA0MC@aXB@jPEh9c)'&MG'P[ER-Z$3e&6N3Y689193d048j%$5dp!!! +"!!!!!9S!!!"D!!!!8KY`!4m!6VVdjL!ZrqU`RfF-2`B["5m-U"YJ!!$+9Bmr#Qe +KBfKPE(!ZBQKbUL"*4%8J8(*PCR0TEfjMCA0PFfi!$&4&@&4$9dP&!3Mrrrrr!!! +!!!!!!!!!!!!!!!!!!!K$XNab!!!!#PX!!!'XmNkkqQSq(fCU9Bmr,[r`5'lrpNK +ZrrK)E[rm6VVpU$iICP"9Mh!!,`!r"Lm&6VVkKMiICJS[$+Qa9BqTVciIF!+`V[r +iCKC9Mh!!,`!r,[r`,blrmNkkqPik(f!39Bp`!#m!2blrm%kkpFJk(dT(CJ)q"6( +(#Q"-lK$JrpT1AL"I6`!!!%J!#8e[EQ&ME`!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!)!"!!V!+F"dJ*H!#X!T`(5!Pkc58b!!!!!!!!!#PX!!!!!!3!!!!!#!!! +!!!!%!!%!!!!!!3!!!!&D!!!!@J!!!&)"ITa%(YS!!!!F!&)!!8e38e)!!3!569G +#3J!!!#S$lIrr!!!!!!!!!!!$lrrr!!!!6!!!!!!$m2rr!!!!8J!!!!#PK!: diff --git a/sys/mac/macmain.c b/sys/mac/macmain.c new file mode 100644 index 0000000..670283a --- /dev/null +++ b/sys/mac/macmain.c @@ -0,0 +1,288 @@ +/* SCCS Id: @(#)macmain.c 3.1 97/01/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* main.c - Mac NetHack */ + +#include "hack.h" +#include "dlb.h" +#include "macwin.h" +#include "mactty.h" + +#if !TARGET_API_MAC_CARBON +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifndef O_RDONLY +#include +#endif + +static void finder_file_request(void); +int main(void); + +#if __SC__ || __MRC__ +QDGlobals qd; +#endif + + +int +main (void) +{ + register int fd = -1; + int argc = 1; + + windowprocs = mac_procs; + InitMac (); + + hname = "Mac Hack"; + hackpid = getpid(); + + /* + * Initialisation of the boundaries of the mazes + * Both boundaries have to be even. + */ + + x_maze_max = COLNO-1; + if (x_maze_max % 2) + x_maze_max--; + y_maze_max = ROWNO-1; + if (y_maze_max % 2) + y_maze_max--; + + setrandom(); + initoptions(); + init_nhwindows(&argc, (char **)&hname); + + /* + * It seems you really want to play. + */ + u.uhp = 1; /* prevent RIP on early quits */ + + finder_file_request (); + + dlb_init(); /* must be before newgame() */ + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + display_gamewindows(); + +#ifdef WIZARD + if (wizard) + Strcpy(plname, "wizard"); + else +#endif + if(!*plname || !strncmp(plname, "player", 4) || !strncmp(plname, "games", 4)) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ + + Sprintf (lock, "%d%s", getuid (), plname); + getlock (); + + if ((fd = restore_saved_game()) >= 0) { +#ifdef WIZARD + /* Since wizard is actually flags.debug, restoring might + * overwrite it. + */ + boolean remember_wiz_mode = wizard; +#endif +#ifdef NEWS + if(iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; /* in case dorecover() fails */ + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + game_active = 1; + if (dorecover(fd)) { +#ifdef WIZARD + if(!wizard && remember_wiz_mode) wizard = TRUE; +#endif + check_special_room(FALSE); + + if (discover || wizard) { + if(yn("Do you want to keep the save file?") == 'n') + (void) delete_savefile(); + else { + compress(fqname(SAVEF, SAVEPREFIX, 0)); + } + } + } + else { + fd = -1; /* set bad status */ + } + } + if (fd < 0) { + player_selection(); + game_active = 1; /* done with selection, draw active game window */ + newgame(); + set_wear(); + (void) pickup(1); + } + + if (discover) + You("are in non-scoring discovery mode."); + flags.move = 0; + + UndimMenuBar (); /* Yes, this is the place for it (!) */ + + moveloop(); + + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + + +static OSErr +copy_file(short src_vol, long src_dir, short dst_vol, long dst_dir, + Str255 fName, + pascal OSErr (*opener)(short vRefNum, long dirID, + ConstStr255Param fileName, + signed char permission, short *refNum)) { + short src_ref, dst_ref; + OSErr err = (*opener)(src_vol, src_dir, fName, fsRdPerm, &src_ref); + if (err == noErr) { + err = (*opener)(dst_vol, dst_dir, fName, fsWrPerm, &dst_ref); + if (err == noErr) { + + long file_len; + err = GetEOF(src_ref, &file_len); + if (err == noErr) { + Handle buf; + long count = MaxBlock(); + if (count > file_len) + count = file_len; + + buf = NewHandle(count); + err = MemError(); + if (err == noErr) { + + while (count > 0) { + OSErr rd_err = FSRead(src_ref, &count, *buf); + err = FSWrite(dst_ref, &count, *buf); + if (err == noErr) + err = rd_err; + file_len -= count; + } + if (file_len == 0) + err = noErr; + + DisposeHandle(buf); + + } + } + FSClose(dst_ref); + } + FSClose(src_ref); + } + + return err; +} + +static void +force_hdelete(short vol, long dir, Str255 fName) +{ + HRstFLock(vol, dir, fName); + HDelete (vol, dir, fName); +} + + +void +process_openfile (short src_vol, long src_dir, Str255 fName, OSType ftype) +{ + OSErr err = noErr; + + if (ftype != SAVE_TYPE) + return; /* only deal with save files */ + + if (src_vol != theDirs.dataRefNum || src_dir != theDirs.dataDirID && + CatMove(src_vol, src_dir, fName, theDirs.dataDirID, "\p:") != noErr) { + + HCreate(theDirs.dataRefNum, theDirs.dataDirID, fName, MAC_CREATOR, SAVE_TYPE); + err = copy_file(src_vol, src_dir, theDirs.dataRefNum, theDirs.dataDirID, + fName, &HOpen); /* HOpenDF is only there under 7.0 */ + if (err == noErr) + err = copy_file(src_vol, src_dir, theDirs.dataRefNum, theDirs.dataDirID, + fName, &HOpenRF); + if (err == noErr) + force_hdelete(src_vol, src_dir, fName); + else + HDelete(theDirs.dataRefNum, theDirs.dataDirID, fName); + } + + if (err == noErr) { + short ref; + + ref = HOpenResFile(theDirs.dataRefNum, theDirs.dataDirID, fName, fsRdPerm); + if (ref != -1) { + Handle name = Get1Resource('STR ', PLAYER_NAME_RES_ID); + if (name) { + Str255 save_f_p; + P2C(*(StringHandle)name, plname); + set_savefile_name(); + C2P(fqname(SAVEF, SAVEPREFIX, 0), save_f_p); + force_hdelete(theDirs.dataRefNum, theDirs.dataDirID, save_f_p); + + if (HRename(theDirs.dataRefNum, theDirs.dataDirID, fName, save_f_p) == noErr) + macFlags.gotOpen = 1; + } + CloseResFile(ref); + } + } +} + + +static void +finder_file_request(void) +{ + if (macFlags.hasAE) { + /* we're capable of handling Apple Events, so let's see if we have any */ + EventRecord event; + long toWhen = TickCount () + 20; /* wait a third of a second for all initial AE */ + + while (TickCount () < toWhen) { + if (WaitNextEvent (highLevelEventMask, &event, 3L, 0)) { + AEProcessAppleEvent(&event); + if (macFlags.gotOpen) + break; + } + } + } +#if 0 +#ifdef MAC68K + else { + short finder_msg, file_count; + CountAppFiles(&finder_msg, &file_count); + if (finder_msg == appOpen && file_count == 1) { + OSErr err; + AppFile src; + FSSpec filespec; + + GetAppFiles(1, &src); + err = FSMakeFSSpec(src.vRefNum, 0, src.fName, &filespec); + if (err == noErr && src.fType == SAVE_TYPE) { + process_openfile (filespec.vRefNum, filespec.parID, filespec.name, src.fType); + if (macFlags.gotOpen) + ClrAppFiles(1); + } + } + } +#endif /* MAC68K */ +#endif /* 0 */ +} + +/*macmain.c*/ diff --git a/sys/mac/macmenu.c b/sys/mac/macmenu.c new file mode 100644 index 0000000..9366865 --- /dev/null +++ b/sys/mac/macmenu.c @@ -0,0 +1,1196 @@ +/* SCCS Id: @(#)macmenu.c 3.4 1999/11/24 */ +/* Copyright (c) Macintosh NetHack Port Team, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/****************************************\ + * Extended Macintosh menu support + * + * provides access to all keyboard commands from cmd.c + * provides control key functionality for classic keyboards + * provides key equivalent references and logical menu groups + * supports various menu highlighting modes +\****************************************/ + +/****************************************\ + * Edit History: + * + * 930512 - More bug fixes and getting tty to work again, Jon W{tte + * 930508 - Bug fixes in-flight, Jon W{tte + * 04/29/93 - 1st Release Draft, David Hairston + * 04/11/93 - 1st Draft, David Hairston +\****************************************/ + +/******** Application Defines ********/ +#include "hack.h" +#include "mactty.h" +#include "macwin.h" +#include "macpopup.h" +#include "patchlevel.h" + +/******** Toolbox Defines ********/ +#if !TARGET_API_MAC_CARBON +#include +#include +#include +#include +#include +#include +#endif + +/* Borrowed from the Mac tty port */ +extern WindowPtr _mt_window; + +/******** Local Defines ********/ + +/* 'MNU#' (menu list record) */ +typedef union menuRefUnn +{ + short mresID; /* MENU resource ID (before GetMenu) */ + MenuHandle mhnd; /* MENU handle (after GetMenu) */ +} menuRefUnn; + +typedef struct menuListRec +{ + short firstMenuID; + short numMenus; + menuRefUnn mref[]; +} menuListRec, *menuListPtr, **menuListHandle; + +/* indices and resource IDs of the menu list data */ +enum +{ + listMenubar, + listSubmenu, + + menuBarListID = 128, + subMenuListID +}; + +/* the following mref[] indices are reserved */ +enum +{ + /* menu bar */ + menuApple, + menuFile, + menuEdit, + + /* submenu */ + menuWizard = 0 +}; + +/* the following menu items are reserved */ +enum +{ + /* apple */ + menuAppleAboutBox = 1, + ____Apple__1, + + /* File */ + menuFileRedraw = 1, + menuFilePrevMsg, + menuFileCleanup, + ____File___1, + menuFilePlayMode, + menuFileEnterExplore, + ____File___2, + menuFileSave, + ____File___3, + menuFileQuit, + + /* standard minimum Edit menu items */ + + /* Wizard */ + menuWizardAttributes = 1 +}; + + +/* + * menuListRec data (preloaded and locked) specifies the number of menus in + * the menu bar, the number of hierarchal or submenus and the menu IDs of + * all of those menus. menus that go into in the menu bar are specified by + * 'MNU#' 128 and submenus are specified by 'MNU#' 129. the fields of the + * menuListRec are: + * firstMenuID - the menu ID (not resource ID) of the 1st menu. subsequent + * menus in the list are _forced_ to have consecutively incremented IDs. + * numMenus - the total count of menus in a given list (and the extent of + * valid menu IDs). + * mref[] - initially the MENU resource ID is stored in the placeholder for + * the resource handle. after loading (GetResource), the menu handle + * is stored and the menu ID, in memory, is set as noted above. + * + * NOTE: a ResEdit template editor is supplied to edit the 'MNU#' resources. + * + * NOTE: the resource IDs do not need to match the menu IDs in a menu list + * record although they have been originally set that way. + * + * NOTE: the menu ID's of menus in the submenu list record may be reset, as + * noted above. it is the programmers responsibility to make sure that + * submenu references/IDs are valid. + * + * WARNING: the existence of the submenu list record is assumed even if the + * number of submenus is zero. also, no error checking is done on the + * extents of the menu IDs. this must be correctly setup by the programmer. + */ + +#define ID1_MBAR pMenuList[listMenubar]->firstMenuID +#define ID1_SUBM pMenuList[listSubmenu]->firstMenuID + +#define NUM_MBAR pMenuList[listMenubar]->numMenus +#define NUM_SUBM pMenuList[listSubmenu]->numMenus + +#define MHND_APPLE pMenuList[listMenubar]->mref[menuApple].mhnd +#define MHND_FILE pMenuList[listMenubar]->mref[menuFile].mhnd +#define MHND_EDIT pMenuList[listMenubar]->mref[menuEdit].mhnd + +#define MBARHND(x) pMenuList[listMenubar]->mref[(x)].mhnd + +#define MHND_WIZ pMenuList[listSubmenu]->mref[menuWizard].mhnd + + +/* mutually exclusive (and prioritized) menu bar states */ +enum +{ + mbarDim, + mbarNoWindows, + mbarDA, + mbarNoMap, + mbarRegular, + mbarSpecial /* explore or debug mode */ +}; + +#define WKND_MAP (WIN_BASE_KIND + NHW_MAP) + + +/* menu routine error numbers */ +enum +{ + errGetMenuList, + errGetMenu, + errGetANDlogTemplate, + errGetANDlogItems, + errGetANDialog, + errANNewMenu, + err_Menu_total +}; + + +/* menu 'STR#' comment char */ +#define mstrEndChar 0xA5 /* '\245' or option-* or "bullet" */ + +/* 'ALRT' */ +enum +{ + alrt_Menu_start = 5000, + alrtMenuNote = alrt_Menu_start, + alrtMenu_NY, + alrt_Menu_limit +}; + +#define beepMenuAlertErr 1 /* # of SysBeep()'s before exitting */ +enum +{ + bttnMenuAlertNo = 1, + bttnMenuAlertYes +}; + + +/******** Globals ********/ +static unsigned char *menuErrStr[err_Menu_total] = + { + "\pAbort: Bad \'MNU#\' resource!", /* errGetMenuList */ + "\pAbort: Bad \'MENU\' resource!", /* errGetMenu */ + "\pAbort: Bad \'DLOG\' resource!", /* errGetANDlogTemplate */ + "\pAbort: Bad \'DITL\' resource!", /* errGetANDlogItems */ + "\pAbort: Bad Dialog Allocation!", /* errGetANDialog */ + "\pAbort: Bad Menu Allocation!", /* errANNewMenu */ + }; +static menuListPtr pMenuList[2]; +static short theMenubar = mbarDA; /* force initial update */ +static short kAdjustWizardMenu = 1; + + +/******** Prototypes ********/ +#if !TARGET_API_MAC_CARBON +static void alignAD(Rect *, short); +#endif +static void mustGetMenuAlerts(void); +static void menuError(short); +static void aboutNetHack(void); +static void askSave(void); +static void askQuit(void); + + +/*** Askname dialog box ***/ + +#define RSRC_ASK 6000 /* Askname dialog and item list */ +#define RSRC_ASK_PLAY 1 /* Play button */ +#define RSRC_ASK_QUIT 2 /* Quit button */ +#define RSRC_ASK_DEFAULT 3 /* Default ring */ +#define RSRC_ASK_ROLE 4 /* Role popup menu */ +#define RSRC_ASK_RACE 5 /* Race popup menu */ +#define RSRC_ASK_GEND 6 /* Gender popup menu */ +#define RSRC_ASK_ALIGN 7 /* Alignment popup menu */ +#define RSRC_ASK_MODE 8 /* Mode popup menu */ +#define RSRC_ASK_NAME 9 /* Name text field */ +#define RSRC_ASK_MAX 10 /* Maximum enabled item */ + +#define KEY_MASK 0xff00 +#define KEY_RETURN 0x2400 +#define KEY_ENTER 0x4c00 +#define KEY_ESCAPE 0x3500 +#define CH_MASK 0x00ff +#define CH_RETURN 0x000d +#define CH_ENTER 0x0003 +#define CH_ESCAPE 0x001b + +static void ask_restring(const char *cstr, unsigned char *pstr); +static void ask_enable(DialogRef wind, short item, int enable); +static pascal void ask_redraw(DialogRef wind, DialogItemIndex item); +static pascal Boolean ask_filter(DialogRef wind, EventRecord *event, DialogItemIndex *item); +#define noresource(t,n) {SysBeep(3); ExitToShell();} +#define fatal(s) {SysBeep(3); ExitToShell();} + +static MenuHandle askmenu[RSRC_ASK_MAX]; +static int askselect[RSRC_ASK_MAX]; +#define currrole askselect[RSRC_ASK_ROLE] +#define currrace askselect[RSRC_ASK_RACE] +#define currgend askselect[RSRC_ASK_GEND] +#define curralign askselect[RSRC_ASK_ALIGN] +#define currmode askselect[RSRC_ASK_MODE] + +static RGBColor + blackcolor = {0x0000, 0x0000, 0x0000}, +// indentcolor = {0x4000, 0x4000, 0x4000}, + darkcolor = {0x8000, 0x8000, 0x8000}, + backcolor = {0xdddd, 0xdddd, 0xdddd}, + lightcolor = {0xffff, 0xffff, 0xffff}, + whitecolor = {0xffff, 0xffff, 0xffff}; + + +/* Convert a mixed-case C string to a Capitalized Pascal string */ +static void +ask_restring (const char *cstr, unsigned char *pstr) +{ + int i; + + + for (i = 0; *cstr && (i < 255); i++) + pstr[i+1] = *cstr++; + pstr[0] = i; + if ((pstr[1] >= 'a') && (pstr[1] <= 'z')) + pstr[1] += 'A' - 'a'; + return; +} + + +/* Enable the dialog item with the given index */ +static void +ask_enable (DialogRef wind, short item, int enable) +{ + short type; + Handle handle; + Rect rect; + + + /* Enable or disable the appropriate item */ + GetDialogItem(wind, item, &type, &handle, &rect); + if (enable) type &= ~itemDisable; + else type |= itemDisable; + HiliteControl((ControlHandle)handle, enable ? 0 : 255); + SetDialogItem(wind, item, type, handle, &rect); + return; +} + + +static pascal void +ask_redraw (DialogRef wind, DialogItemIndex item) +{ + short type; + Handle handle; + Rect rect; + static char *modechar = "NED"; + + + /* Which item shall we redraw? */ + GetDialogItem(wind, item, &type, &handle, &rect); + switch (item) { + case RSRC_ASK_DEFAULT: + PenSize(3, 3); + FrameRoundRect(&rect, 16, 16); + break; + + case RSRC_ASK_ROLE: + case RSRC_ASK_RACE: + case RSRC_ASK_GEND: + case RSRC_ASK_ALIGN: + case RSRC_ASK_MODE: + if (macFlags.color) { + RGBForeColor(&blackcolor); + RGBBackColor(&backcolor); + } + PenNormal(); + TextMode(srcOr); + EraseRect(&rect); + + /* Draw the frame and drop shadow */ + rect.right--; + rect.bottom--; + FrameRect(&rect); + MoveTo(rect.right, rect.top+1); + LineTo(rect.right, rect.bottom); + LineTo(rect.left+1, rect.bottom); + + /* Draw the menu character */ + MoveTo(rect.left+4, rect.top+12); + switch (item) { + case RSRC_ASK_ROLE: + DrawText(roles[askselect[item]].filecode, 0, 3); + break; + case RSRC_ASK_RACE: + DrawText(races[askselect[item]].filecode, 0, 3); + break; + case RSRC_ASK_GEND: + DrawText(genders[askselect[item]].filecode, 0, 3); + break; + case RSRC_ASK_ALIGN: + DrawText(aligns[askselect[item]].filecode, 0, 3); + break; + case RSRC_ASK_MODE: + DrawChar(modechar[askselect[item]]); + break; + } + + /* Draw the popup symbol */ + MoveTo(rect.right - 16, rect.top + 5); + LineTo(rect.right - 6, rect.top + 5); + LineTo(rect.right - 11, rect.top + 10); + LineTo(rect.right - 15, rect.top + 6); + LineTo(rect.right - 8, rect.top + 6); + LineTo(rect.right - 11, rect.top + 9); + LineTo(rect.right - 13, rect.top + 7); + LineTo(rect.right - 10, rect.top + 7); + LineTo(rect.right - 11, rect.top + 8); + + /* Draw the shadow */ + InsetRect(&rect, 1, 1); + if (macFlags.color) { + RGBColor color; + + + /* Save the foreground color */ + GetForeColor(&color); + + /* Draw the top and left */ + RGBForeColor(&lightcolor); + MoveTo(rect.left, rect.bottom-1); + LineTo(rect.left, rect.top); + LineTo(rect.right-1, rect.top); + + /* Draw the bottom and right */ + RGBForeColor(&darkcolor); + MoveTo(rect.right-1, rect.top+1); + LineTo(rect.right-1, rect.bottom-1); + LineTo(rect.left+1, rect.bottom-1); + + /* Restore the foreground color */ + RGBForeColor(&color); + } + break; + + case RSRC_ASK_NAME: + PenNormal(); + if (macFlags.color) { + RGBForeColor(&whitecolor); + RGBBackColor(&whitecolor); + TextMode(srcOr); + } else { + PenMode(notPatCopy); + TextMode(srcBic); + } + InsetRect(&rect, -1, -1); + FrameRect(&rect); + InsetRect(&rect, -1, -1); + FrameRect(&rect); + InsetRect(&rect, -2, -2); + if (macFlags.color) { + /* Draw the top and left */ + RGBForeColor(&darkcolor); + MoveTo(rect.left, rect.bottom-1); + LineTo(rect.left, rect.top); + LineTo(rect.right-1, rect.top); + + /* Draw the bottom and right */ + RGBForeColor(&lightcolor); + MoveTo(rect.right-1, rect.top+1); + LineTo(rect.right-1, rect.bottom-1); + LineTo(rect.left+1, rect.bottom-1); + + /* Restore the colors */ + RGBForeColor(&blackcolor); + RGBBackColor(&backcolor); + } + break; + } + return; +} + + +static pascal Boolean +ask_filter (DialogRef wind, EventRecord *event, DialogItemIndex *item) +{ + short ch, key; + + + switch (event->what) { + case keyDown: + case autoKey: + ch = event->message & CH_MASK; + key = event->message & KEY_MASK; + /* Handle equivalents for OK */ + if ((ch == CH_RETURN) || (key == KEY_RETURN) || + (ch == CH_ENTER) || (key == KEY_ENTER)) { + if (GetDialogTextEditHandle(wind)[0]->teLength) { + FlashButton(wind, RSRC_ASK_PLAY); + *item = RSRC_ASK_PLAY; + } else + *item = 0; + return (TRUE); + } + /* Handle equivalents for Normal/Explore/Debug */ + if ((event->modifiers & cmdKey) && (ch == 'n')) { + currmode = 0; + ask_redraw(wind, RSRC_ASK_MODE); + *item = RSRC_ASK_MODE; + return (TRUE); + } + if ((event->modifiers & cmdKey) && (ch == 'e')) { + currmode = 1; + ask_redraw(wind, RSRC_ASK_MODE); + *item = RSRC_ASK_MODE; + return (TRUE); + } + if ((event->modifiers & cmdKey) && (ch == 'd')) { + currmode = 2; + ask_redraw(wind, RSRC_ASK_MODE); + *item = RSRC_ASK_MODE; + return (TRUE); + } + /* Handle equivalents for Cancel and Quit */ + if ((ch == CH_ESCAPE) || (key == KEY_ESCAPE) || + ((event->modifiers & cmdKey) && (ch == 'q')) || + ((event->modifiers & cmdKey) && (ch == '.'))) { + FlashButton(wind, RSRC_ASK_QUIT); + *item = RSRC_ASK_QUIT; + return (TRUE); + } + return (FALSE); + case updateEvt: + ask_redraw(wind, RSRC_ASK_NAME); + return (FALSE); + default: + return (FALSE); + } +} + + +void mac_askname () +{ + GrafPtr oldport; + DialogRef askdialog; + short i, j, item, type; + Handle handle; + Rect rect; + Str255 str; + Point pt; + UserItemUPP redraw = NewUserItemUPP(ask_redraw); + ModalFilterUPP filter = NewModalFilterUPP(ask_filter); + + + /* Create the dialog */ + if (!(askdialog = GetNewDialog(RSRC_ASK, NULL, (WindowRef)-1))) + noresource('DLOG', RSRC_ASK); + GetPort(&oldport); + SetPortDialogPort(askdialog); + + /* Initialize the name text item */ + ask_restring(plname, str); + if (plname[0]) { + GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect); + SetDialogItemText(handle, str); + } +#if 0 + { + Str32 pName; + pName [0] = 0; + if (plname && plname [0]) { + strcpy ((char *) pName, plname); + c2pstr ((char *) pName); + } else { + Handle h; + h = GetResource ('STR ', -16096); + if (((Handle) 0 != h) && (GetHandleSize (h) > 0)) { + DetachResource (h); + HLock (h); + if (**h > 31) { + **h = 31; + } + BlockMove (*h, pName, **h + 1); + DisposeHandle (h); + } + } + if (pName [0]) { + GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect); + SetDialogItemText(handle, pName); + if (pName [0] > 2 && pName [pName [0] - 1] == '-') { + short role = (*pANR).anMenu[anRole]; + char suffix = (char) pName[pName[0]], + *sfxindx = strchr(pl_classes, suffix); + + if (sfxindx) + role = (short) (sfxindx - pl_classes); + else if (suffix == '@') + role = (short) rn2((int) strlen(pl_classes)); + (*pANR).anMenu[anRole] = role; + } + } + } +#endif + SelectDialogItemText(askdialog, RSRC_ASK_NAME, 0, 32767); + + /* Initialize the role popup menu */ + if (!(askmenu[RSRC_ASK_ROLE] = NewMenu(RSRC_ASK_ROLE, "\p"))) + fatal("\pCannot create role menu"); + for (i = 0; roles[i].name.m; i++) { + ask_restring(roles[i].name.m, str); + AppendMenu(askmenu[RSRC_ASK_ROLE], str); + } + InsertMenu(askmenu[RSRC_ASK_ROLE], hierMenu); + if (flags.initrole >= 0) + currrole = flags.initrole; + /* Check for backward compatibility */ + else if ((currrole = str2role(pl_character)) < 0) + currrole = randrole(); + + /* Initialize the race popup menu */ + if (!(askmenu[RSRC_ASK_RACE] = NewMenu(RSRC_ASK_RACE, "\p"))) + fatal("\pCannot create race menu"); + for (i = 0; races[i].noun; i++) { + ask_restring(races[i].noun, str); + AppendMenu(askmenu[RSRC_ASK_RACE], str); + } + InsertMenu(askmenu[RSRC_ASK_RACE], hierMenu); + if (flags.initrace >= 0) + currrace = flags.initrace; + else + currrace = randrace(currrole); + + /* Initialize the gender popup menu */ + if (!(askmenu[RSRC_ASK_GEND] = NewMenu(RSRC_ASK_GEND, "\p"))) + fatal("\pCannot create gender menu"); + for (i = 0; i < ROLE_GENDERS; i++) { + ask_restring(genders[i].adj, str); + AppendMenu(askmenu[RSRC_ASK_GEND], str); + } + InsertMenu(askmenu[RSRC_ASK_GEND], hierMenu); + if (flags.initgend >= 0) + currgend = flags.initgend; + else if (flags.female) + currgend = 1; + else + currgend = randgend(currrole, currrace); + + /* Initialize the alignment popup menu */ + if (!(askmenu[RSRC_ASK_ALIGN] = NewMenu(RSRC_ASK_ALIGN, "\p"))) + fatal("\pCannot create alignment menu"); + for (i = 0; i < ROLE_ALIGNS; i++) { + ask_restring(aligns[i].adj, str); + AppendMenu(askmenu[RSRC_ASK_ALIGN], str); + } + InsertMenu(askmenu[RSRC_ASK_ALIGN], hierMenu); + if (flags.initalign >= 0) + curralign = flags.initalign; + else + curralign = randalign(currrole, currrace); + + /* Initialize the mode popup menu */ + if (!(askmenu[RSRC_ASK_MODE] = NewMenu(RSRC_ASK_MODE, "\p"))) + fatal("\pCannot create mode menu"); + AppendMenu(askmenu[RSRC_ASK_MODE], "\pNormal"); + AppendMenu(askmenu[RSRC_ASK_MODE], "\pExplore"); +#ifdef WIZARD + AppendMenu(askmenu[RSRC_ASK_MODE], "\pDebug"); +#endif + InsertMenu(askmenu[RSRC_ASK_MODE], hierMenu); + currmode = 0; + + /* Set the redraw procedures */ + for (item = RSRC_ASK_DEFAULT; item <= RSRC_ASK_MODE; item++) { + GetDialogItem(askdialog, item, &type, &handle, &rect); + SetDialogItem(askdialog, item, type, (Handle)redraw, &rect); + } + + /* Handle dialog events */ + do { + /* Adjust the Play button */ + ask_enable(askdialog, RSRC_ASK_PLAY, + GetDialogTextEditHandle(askdialog)[0]->teLength); + + /* Adjust the race popup menu */ + i = j = currrace; + do { + if (validrace(currrole, j)) { + EnableMenuItem(askmenu[RSRC_ASK_RACE], j+1); + CheckMenuItem(askmenu[RSRC_ASK_RACE], j+1, + currrace == j); + } else { + DisableMenuItem(askmenu[RSRC_ASK_RACE], j+1); + CheckMenuItem(askmenu[RSRC_ASK_RACE], j+1, FALSE); + if ((currrace == j) && !races[++currrace].noun) + currrace = 0; + } + if (!races[++j].noun) j = 0; + } while (i != j); + if (currrace != i) { + GetDialogItem(askdialog, RSRC_ASK_RACE, &type, &handle, &rect); + InvalWindowRect(GetDialogWindow(askdialog), &rect); + } + + /* Adjust the gender popup menu */ + i = j = currgend; + do { + if (validgend(currrole, currrace, j)) { + EnableMenuItem(askmenu[RSRC_ASK_GEND], j+1); + CheckMenuItem(askmenu[RSRC_ASK_GEND], j+1, + currgend == j); + } else { + DisableMenuItem(askmenu[RSRC_ASK_GEND], j+1); + CheckMenuItem(askmenu[RSRC_ASK_GEND], j+1, FALSE); + if ((currgend == j) && (++currgend >= ROLE_GENDERS)) + currgend = 0; + } + if (++j >= ROLE_GENDERS) j = 0; + } while (i != j); + if (currgend != i) { + GetDialogItem(askdialog, RSRC_ASK_GEND, &type, &handle, &rect); + InvalWindowRect(GetDialogWindow(askdialog), &rect); + } + + /* Adjust the alignment popup menu */ + i = j = curralign; + do { + if (validalign(currrole, currrace, j)) { + EnableMenuItem(askmenu[RSRC_ASK_ALIGN], j+1); + CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j+1, + curralign == j); + } else { + DisableMenuItem(askmenu[RSRC_ASK_ALIGN], j+1); + CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j+1, FALSE); + if ((curralign == j) && (++curralign >= ROLE_ALIGNS)) + curralign = 0; + } + if (++j >= ROLE_ALIGNS) j = 0; + } while (i != j); + if (curralign != i) { + GetDialogItem(askdialog, RSRC_ASK_ALIGN, &type, &handle, &rect); + InvalWindowRect(GetDialogWindow(askdialog), &rect); + } + + /* Adjust the role popup menu */ + for (i = 0; roles[i].name.m; i++) { + ask_restring((currgend && roles[i].name.f) ? + roles[i].name.f : roles[i].name.m, str); + SetMenuItemText(askmenu[RSRC_ASK_ROLE], i+1, str); + CheckMenuItem(askmenu[RSRC_ASK_ROLE], i+1, currrole == i); + } + + /* Adjust the mode popup menu */ + CheckMenuItem(askmenu[RSRC_ASK_MODE], 1, currmode == 0); + CheckMenuItem(askmenu[RSRC_ASK_MODE], 2, currmode == 1); +#ifdef WIZARD + CheckMenuItem(askmenu[RSRC_ASK_MODE], 3, currmode == 2); +#endif + + /* Wait for an action on an item */ + ModalDialog(filter, &item); + switch (item) { + case RSRC_ASK_PLAY: + break; + case RSRC_ASK_QUIT: + currmode = -1; + break; + case RSRC_ASK_ROLE: + case RSRC_ASK_RACE: + case RSRC_ASK_ALIGN: + case RSRC_ASK_GEND: + case RSRC_ASK_MODE: + GetDialogItem(askdialog, item, &type, &handle, &rect); + pt = *(Point *)▭ + LocalToGlobal(&pt); + if (!!(i = PopUpMenuSelect(askmenu[item], pt.v, pt.h, + askselect[item] + 1))) + askselect[item] = LoWord(i) - 1; + InvalWindowRect(GetDialogWindow(askdialog), &rect); + break; + case RSRC_ASK_NAME: +#if 0 + /* limit the data here to 25 chars */ + { + short beepTEDelete = 1; + + while ((**dRec.textH).teLength > 25) + { + if (beepTEDelete++ <= 3) + SysBeep(3); + TEKey('\b', dRec.textH); + } + } + + /* special case filter (that doesn't plug all the holes!) */ + if (((**dRec.textH).teLength == 1) && (**((**dRec.textH).hText) < 32)) + TEKey('\b', dRec.textH); +#endif + break; + } + } while ((item != RSRC_ASK_PLAY) && (item != RSRC_ASK_QUIT)); + + /* Process the name */ + GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect); + GetDialogItemText(handle, str); + if (str[0] > PL_NSIZ-1) str[0] = PL_NSIZ-1; + BlockMove(&str[1], plname, str[0]); + plname[str[0]] = '\0'; + + /* Destroy the dialog */ + for (i = RSRC_ASK_ROLE; i <= RSRC_ASK_MODE; i++) { + DeleteMenu(i); + DisposeMenu(askmenu[i]); + } + SetPort(oldport); + DisposeDialog(askdialog); + DisposeRoutineDescriptor(filter); + DisposeRoutineDescriptor(redraw); + + /* Process the mode */ +#ifdef WIZARD + wizard = +#endif + discover = 0; + switch (currmode) { + case 0: /* Normal */ + break; + case 1: /* Explore */ + discover = 1; + break; +#ifdef WIZARD + case 2: /* Debug */ + wizard = 1; + strcpy(plname, WIZARD); + break; +#endif + default: /* Quit */ + ExitToShell(); + } + + /* Process the role */ + strcpy(pl_character, roles[currrole].name.m); + flags.initrole = currrole; + + /* Process the race */ + flags.initrace = currrace; + + /* Process the gender */ + flags.female = flags.initgend = currgend; + + /* Process the alignment */ + flags.initalign = curralign; + + return; +} + + + +/*** Menu bar routines ***/ + +#if !TARGET_API_MAC_CARBON +static void +alignAD(Rect *pRct, short vExempt) +{ + BitMap qbitmap; + + + GetQDGlobalsScreenBits(&qbitmap); + (*pRct).right -= (*pRct).left; /* width */ + (*pRct).bottom -= (*pRct).top; /* height */ + (*pRct).left = (qbitmap.bounds.right - (*pRct).right) / 2; + (*pRct).top = (qbitmap.bounds.bottom - (*pRct).bottom - vExempt) / 2; + (*pRct).top += vExempt; + (*pRct).right += (*pRct).left; + (*pRct).bottom += (*pRct).top; +} +#endif + + +static void +mustGetMenuAlerts() +{ + short i; + Rect **hRct; + + for (i = alrt_Menu_start; i < alrt_Menu_limit; i++) + { + if (! (hRct = (Rect **) GetResource('ALRT', i))) /* AlertTHndl */ + { + for (i = 0; i < beepMenuAlertErr; i++) + SysBeep(3); + ExitToShell(); + } + +#if !TARGET_API_MAC_CARBON + alignAD(*hRct, GetMBarHeight()); +#endif + } +} + +static void +menuError(short menuErr) +{ + short i; + + for (i = 0; i < beepMenuAlertErr; i++) + SysBeep(3); + + ParamText(menuErrStr[menuErr], "\p", "\p", "\p"); + (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L); + + ExitToShell(); +} + +void +InitMenuRes() +{ + static Boolean was_inited = 0; + short i, j; + menuListHandle mlHnd; + MenuHandle menu; + + if (was_inited) + return; + was_inited = 1; + + mustGetMenuAlerts(); + + for (i = listMenubar; i <= listSubmenu; i++) { + if (! (mlHnd = (menuListHandle) GetResource('MNU#', (menuBarListID + i)))) + menuError(errGetMenuList); + + pMenuList[i] = (menuListPtr) NewPtr(GetHandleSize((Handle) mlHnd)); + *pMenuList[i] = **mlHnd; + + for (j = 0; j < pMenuList[i]->numMenus; j++) + { + if (! (menu = (MenuHandle) GetMenu((**mlHnd).mref[j].mresID))) { + Str31 d; + NumToString ((**mlHnd).mref[j].mresID, d); + menuError(errGetMenu); + } + + pMenuList[i]->mref[j].mhnd = menu; + SetMenuID(menu, j + (**mlHnd).firstMenuID); /* consecutive IDs */ + + /* expand apple menu */ + if ((i == listMenubar) && (j == menuApple)) { + AppendResMenu(menu, 'DRVR'); + } + + InsertMenu(menu, ((i == listSubmenu) ? hierMenu : 0)); + } + } + DrawMenuBar(); + return; +} + +void +AdjustMenus(short dimMenubar) +{ + short newMenubar = mbarRegular; + WindowRef win = FrontWindow(); + short i; + + /* + * if (windowprocs != mac_procs) { + * return; + * } + */ + /* determine the new menubar state */ + if (dimMenubar) + newMenubar = mbarDim; + else if (!win) + newMenubar = mbarNoWindows; + else if (GetWindowKind(win) < 0) + newMenubar = mbarDA; + else if (!IsWindowVisible(_mt_window)) + newMenubar = mbarNoMap; + + if (newMenubar != mbarRegular) + ; /* we've already found its state */ +#ifdef WIZARD + else if (wizard) + { + newMenubar = mbarSpecial; + + if (kAdjustWizardMenu) + { + kAdjustWizardMenu = 0; + + SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pDebug"); + } + } +#endif + + else if (discover) + { + newMenubar = mbarSpecial; + + if (kAdjustWizardMenu) + { + kAdjustWizardMenu = 0; + + SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pExplore"); + + for (i = CountMenuItems(MHND_WIZ); i > menuWizardAttributes; i--) + DeleteMenuItem(MHND_WIZ, i); + } + } + + /* adjust the menubar, if there's a state change */ + if (theMenubar != newMenubar) + { + switch(theMenubar = newMenubar) + { + case mbarDim: + /* disable all menus (except the apple menu) */ + for (i = menuFile; i < NUM_MBAR; i++) + DisableMenuItem(MBARHND(i), 0); + break; + + case mbarNoWindows: + case mbarDA: + case mbarNoMap: + /* enable the file menu, but ... */ + EnableMenuItem(MHND_FILE, 0); + + /* ... disable the window commands! */ + for (i = menuFileRedraw; i <= menuFileEnterExplore; i++) + DisableMenuItem(MHND_FILE, i); + + /* ... and disable the rest of the menus */ + for (i = menuEdit; i < NUM_MBAR; i++) + DisableMenuItem(MBARHND(i), 0); + + if (theMenubar == mbarDA) + EnableMenuItem(MHND_EDIT, 0); + + break; + + case mbarRegular: + case mbarSpecial: + /* enable all menus ... */ + for (i = menuFile; i < NUM_MBAR; i++) + EnableMenuItem(MBARHND(i), 0); + + /* ... except the unused Edit menu */ + DisableMenuItem(MHND_EDIT, 0); + + /* ... enable the window commands */ + for (i = menuFileRedraw; i <= menuFileEnterExplore; i++) + EnableMenuItem(MHND_FILE, i); + + if (theMenubar == mbarRegular) + DisableMenuItem(MHND_FILE, menuFilePlayMode); + else + DisableMenuItem(MHND_FILE, menuFileEnterExplore); + + break; + } + + DrawMenuBar(); + } +} + +void +DoMenuEvt(long menuEntry) +{ + short menuID = HiWord(menuEntry); + short menuItem = LoWord(menuEntry); + + switch(menuID - ID1_MBAR) /* all submenus are default case */ + { + case menuApple: + if (menuItem == menuAppleAboutBox) + aboutNetHack(); +#if !TARGET_API_MAC_CARBON + else + { + unsigned char daName[32]; + + GetMenuItemText(MHND_APPLE, menuItem, * (Str255 *) daName); + (void) OpenDeskAcc(daName); + } +#endif + break; + + /* + * Those direct calls are ugly: they should be installed into cmd.c . + * Those AddToKeyQueue() calls are also ugly: they should be put into + * the 'STR#' resource. + */ + case menuFile: + switch(menuItem) + { + case menuFileRedraw: + AddToKeyQueue ('R' & 0x1f, 1); + break; + + case menuFilePrevMsg: + AddToKeyQueue ('P' & 0x1f, 1); + break; + + case menuFileCleanup: + (void) SanePositions(); + break; + + case menuFileEnterExplore: + AddToKeyQueue ('X', 1); + break; + + case menuFileSave: + askSave(); + break; + + case menuFileQuit: + askQuit(); + break; + } + break; + + case menuEdit: +#if !TARGET_API_MAC_CARBON + (void) SystemEdit(menuItem - 1); +#endif + break; + + default: /* get associated string and add to key queue */ + { + Str255 mstr; + short i; + + GetIndString(mstr, menuID, menuItem); + if (mstr[0] > QUEUE_LEN) + mstr[0] = QUEUE_LEN; + + for (i = 1; ((i <= mstr[0]) && (mstr[i] != mstrEndChar)); i++) + AddToKeyQueue(mstr[i], false); + } + break; + } + + HiliteMenu(0); +} + + +static void +aboutNetHack() { + if (theMenubar >= mbarRegular) { + (void) doversion(); /* is this necessary? */ + } else { + unsigned char aboutStr[32] = "\pNetHack 3.4."; + + if (PATCHLEVEL > 10) { + aboutStr[++aboutStr[0]] = '0'+PATCHLEVEL/10; + } + + aboutStr[++aboutStr[0]] = '0' + (PATCHLEVEL % 10); + + ParamText(aboutStr, "\p\rdevteam@www.nethack.org", "\p", "\p"); + (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L); + ResetAlertStage(); + } +} + + +static void +askSave() +{ + Boolean doSave = 1; + Boolean doYes = 0; + + if (theMenubar < mbarRegular) { + short itemHit; + + ParamText("\pReally Save?", "\p", "\p", "\p"); + itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L); + ResetAlertStage(); + + if (itemHit != bttnMenuAlertYes) { + doSave = 0; + } else { + doYes = 1; + } + } + if (doSave) { + AddToKeyQueue ('S', 1); + if (doYes) { + AddToKeyQueue ('y', 1); + } + } +} + +static void +askQuit() +{ + Boolean doQuit = 1; + Boolean doYes = 0; + Boolean winMac; + char *quitinput; + + if (!strcmp (windowprocs.name, "mac")) + winMac = 1; + else + winMac = 0; + + if (theMenubar < mbarRegular) { + short itemHit; + + ParamText("\pReally Quit?", "\p", "\p", "\p"); + itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L); + ResetAlertStage(); + + if (itemHit != bttnMenuAlertYes) { + doQuit = 0; + } else { + doYes = 1; + } + } + if (doQuit) { + /* MWM -- forgive me lord, an even uglier kludge to deal with differences + in command input handling + */ + if (winMac) + quitinput = "#quit\r"; + else + quitinput = "#q\r"; + + /* KMH -- Ugly kludge */ + while (*quitinput) + AddToKeyQueue(*quitinput++, 1); + if (doYes) { + if (winMac) + quitinput = "y\rq\r\r\r"; + else + quitinput = "yq\r"; + while (*quitinput) + AddToKeyQueue(*quitinput++, 1); + } + } +} + diff --git a/sys/mac/macsnd.c b/sys/mac/macsnd.c new file mode 100644 index 0000000..39add1f --- /dev/null +++ b/sys/mac/macsnd.c @@ -0,0 +1,107 @@ +/* SCCS Id: @(#)macsnd.c 3.1 92/11/28 */ +/* Copyright (c) 1992 by Jon Watte */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains music playing code. + * + * If we were REALLY determinated, we would make the sound play + * asynchronously, but I'll save that one for a rainy day... + * + * This may break A/UX, since it defines MAC but we need to + * check that the toolbox is booted. I'll defer that one too. + * + * - h+ 921128 + */ + +#include "hack.h" +#include "mactty.h" +#include "macwin.h" +#if !TARGET_API_MAC_CARBON +# include +# include +#else +# define freqDurationCmd 40 +#endif + +#define SND_BUFFER(s) (&(*s)[20]) +#define SND_LEN(s) (GetHandleSize(s)-42) + + +void +mac_speaker (struct obj *instr, char *melody) { + SndChannelPtr theChannel = (SndChannelPtr) 0; + SndCommand theCmd; + Handle theSound; + unsigned char theName [32]; + char *n = (char *) &theName [1]; + int typ = instr->otyp; + const char *actualn = OBJ_NAME (objects [typ]); + + /* + * First: are we in the library ? + */ + if (flags.silent) { + return; + } + + /* + * Is this a known instrument ? + */ + strcpy (n, actualn); + theName [0] = strlen (n); + theSound = GetNamedResource ('snd ', theName); + if (! theSound) { + return; + } + HLock (theSound); + + /* + * Set up the synth + */ + if (SndNewChannel(&theChannel, sampledSynth, initMono + + initNoInterp, (void *) 0) == noErr) { + char midi_note [] = {57, 59, 60, 62, 64, 65, 67}; + + short err; + short snd_len = SND_LEN (theSound) / 18; + + theCmd.cmd = soundCmd; + theCmd.param1 = 0; + theCmd.param2 = (long) SND_BUFFER (theSound); + err = SndDoCommand (theChannel, &theCmd, false); + + /* + * We rack 'em up all in a row + * The mac will play them correctly and then end, since + * we do a sync close below. + * + */ + while (*melody && ! err) { + while (*melody > 'G') { + *melody -= 8; + } + while (*melody < 'A') { + *melody += 8; + } + theCmd.cmd = freqDurationCmd; + theCmd.param1 = snd_len; + theCmd.param2 = midi_note [*melody - 'A']; + err = SndDoCommand (theChannel, &theCmd, false); + melody ++; + } + SndDisposeChannel (theChannel, false); /* Sync wait for completion */ + ReleaseResource (theSound); + } +} + +void tty_nhbell (void) { + Handle h = GetNamedResource ('snd ', "\pNetHack Bell"); + + if (h) { + HLock (h); + SndPlay ((SndChannelPtr) 0, (SndListHandle) h, 0); + ReleaseResource (h); + } else + SysBeep (30); +} diff --git a/sys/mac/mactopl.c b/sys/mac/mactopl.c new file mode 100644 index 0000000..b9979e3 --- /dev/null +++ b/sys/mac/mactopl.c @@ -0,0 +1,65 @@ +/* SCCS Id: @(#)mactopl.c 3.1 91/07/23 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "mactty.h" +#include "macwin.h" +#include "macpopup.h" + +char +queued_resp(char *resp) { + char buf[30]; + if (try_key_queue(buf)) { + if (!resp || strchr(resp, buf[0])) + return buf[0]; + if (digit(buf[0]) && strchr(resp, '#')) { + yn_number = atoi(buf); + return '#'; + } + } + return '\0'; +} + + +char +topl_yn_function(const char *query, const char *resp, char def) { + char buf[30]; + char c = queued_resp((char *) resp); + if (!c) { + enter_topl_mode((char *) query); + topl_set_resp((char *) resp, def); + + do { + c = readchar(); + if (c && resp && !strchr(resp, c)) { + nhbell(); + c = '\0'; + } + } while (!c); + + topl_set_resp("", '\0'); + leave_topl_mode(buf); + if (c == '#') + yn_number = atoi(buf); + } + return c; +} + + +char +mac_yn_function(query, resp, def) +const char *query,*resp; +char def; +/* + * Generic yes/no function. 'def' is the default (returned by space or + * return; 'esc' returns 'q', or 'n', or the default, depending on + * what's in the string. The 'query' string is printed before the user + * is asked about the string. + * If resp is NULL, any single character is accepted and returned. + */ +{ + return topl_yn_function(query, resp, def); +} + +/* mactopl.c */ diff --git a/sys/mac/mactty.c b/sys/mac/mactty.c new file mode 100644 index 0000000..554f39b --- /dev/null +++ b/sys/mac/mactty.c @@ -0,0 +1,1225 @@ +/* SCCS Id: @(#)mactty.c 3.1 93/03/01 */ +/* Copyright (c) Jon W{tte 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * mactty.c + * + * This file contains the actual code for the tty library. For a + * description, see the file mactty.h, which contains all you + * need to know to use the library. + */ + +#include "hack.h" /* to get flags */ +#include "mttypriv.h" +#if !TARGET_API_MAC_CARBON +#include +#endif + +char game_active = 0; /* flag to window rendering routines not to use ppat */ + +/* these declarations are here because I can't include macwin.h without including the world */ +extern void dprintf(char *, ...); /* dprintf.c */ + +/* + * Borrowed from the Mac tty port + */ +extern WindowPtr _mt_window; + +static void select_onscreen_window (tty_record *record); +static void select_offscreen_port (tty_record *record); + +#define MEMORY_MARGIN 30000 + +/* + * Convenience macro for most functions - put last in declaration + */ +#define RECORD_EXISTS(record) \ + tty_record * record; \ + if (!window || !(record = (tty_record *) GetWRefCon (window))) \ + return general_failure; + + +/* + * Simple macro for deciding wether we draw at once or delay + */ +#define DRAW_DIRECT (TA_ALWAYS_REFRESH & record->attribute [TTY_ATTRIB_FLAGS]) + + +/* + * Table of special characters. Zero is ALWAYS special; it means + * end of string and would be MISSED if it was not included here. + */ +#define COOKED_CONTROLS 0X00002581 +#define RAW_CONTROLS 1 +static unsigned long s_control = COOKED_CONTROLS; + + +/* + * Memory-related error + */ +static short +mem_err (void) { + short ret_val = MemError(); + if (!ret_val) { + ret_val = general_failure; + } + return ret_val; +} + + +/* + * Make a rectangle empty + */ +static void +empty_rect (Rect *r) { + r->right = -20000; + r->left = 20000; + r->top = 20000; + r->bottom = -20000; +} + + +/* + * Union twp rect together + */ +static void +union_rect (Rect *r1, Rect *r2, Rect *dest) { + dest->left = min (r1->left, r2->left); + dest->top = min (r1->top, r2 ->top); + dest->bottom = max (r1->bottom, r2->bottom); + dest->right = max (r1->right, r2->right); +} + + +/* + * Dispose a pointer using the set memory-allocator + */ +static short +dispose_ptr (void *ptr) { + if (!ptr) { + return noErr; /* Silently accept disposing nulls */ + } + DisposePtr (ptr); + return MemError (); +} + + +#if 0 /* Use alloc.c instead */ +/* + * Allocate a pointer using the set memory-allocator + */ +static short +alloc_ptr (void **ptr, long size) { + *ptr = NewPtr (size); + return MemError (); +} +#endif + + +/* + * Set up a GWorld in the record + */ +static short +allocate_offscreen_world (tty_record *record) { +GWorldPtr gw = (GWorldPtr)0; +GWorldFlags world_flags = 0; +long mem_here, mem_there, other, required_mem; +Point p = {0, 0}; +Rect r_screen; +GDHandle gdh; +short s_err; + + select_onscreen_window (record); + LocalToGlobal (&p); + r_screen = record->its_bits.bounds; + OffsetRect (&r_screen, p.h, p.v); + + gdh = GetMaxDevice (&r_screen); + required_mem = (long) (*((*gdh)->gdPMap))->pixelSize * + ((long) record->its_bits.bounds.right * + record->its_bits.bounds.bottom) >> 3; + + PurgeSpace (&other, &mem_here); + if (other < mem_here + MEMORY_MARGIN) { + mem_here = other - MEMORY_MARGIN; + } + dprintf ("Heap %ld Required %ld", mem_here, required_mem); + if (required_mem > mem_here) { + mem_there = required_mem; + if (required_mem > TempMaxMem (&mem_there)) { + dprintf ("No memory"); + return memFullErr; + } + world_flags |= useTempMem; + } + s_err = NewGWorld (&gw, 0, &r_screen, (CTabHandle) 0, (GDHandle) 0, world_flags); + if (!s_err) { + record->offscreen_world = gw; + select_offscreen_port (record); + SetOrigin (0, 0); + select_onscreen_window (record); + dprintf ("New GWorld @ %lx;dm", gw); + } + return s_err; +} + + +/* + * Done with GWorld, release data + */ +static short +deallocate_gworld (tty_record *record) { + if (record->offscreen_world) { + DisposeGWorld (record->offscreen_world); + record->offscreen_world = (GWorldPtr) 0; + } + return noErr; +} + + + + +/* + * Get rid of offscreen bitmap + */ +static short +free_bits (tty_record *record) { + short s_err; + + if (record->uses_gworld) { + s_err = deallocate_gworld (record); +#if !TARGET_API_MAC_CARBON + } else { + s_err = dispose_ptr (record->its_bits.baseAddr); + if (!s_err) { + record->its_bits.baseAddr = (char *)0; + if (record->offscreen_port) { + ClosePort (record->offscreen_port); + s_err = dispose_ptr (record->offscreen_port); + if (!s_err) { + record->offscreen_port = (GrafPtr) 0; + } + } + } +#endif + } + return s_err; +} + + +/* + * Snatch a window from the resource fork. Create the record. + * Otherwise, do nothing. + */ + +short +create_tty (WindowRef *window, short resource_id, Boolean in_color) +{ + tty_record * record; + Boolean was_allocated = !!*window; + + if (in_color) { + *window = GetNewCWindow (resource_id, (Ptr) *window, (WindowRef) -1L); + } else { + *window = GetNewWindow (resource_id, (Ptr) *window, (WindowRef) -1L); + } + if (!*window) { + return mem_err (); + } + + record = (tty_record *) NewPtrClear (sizeof (tty_record)); + if (!record) { +#if !TARGET_API_MAC_CARBON + if (was_allocated) { + CloseWindow (*window); + } else { +#endif + DisposeWindow (*window); +#if !TARGET_API_MAC_CARBON + } +#endif + return mem_err (); + } + record->its_window = *window; + SetWRefCon (*window, (long) record); + record->was_allocated = was_allocated; + record->its_bits.baseAddr = (char *)0; + record->curs_state = TRUE; + +/* + * We need to keep the window world around if we switch worlds + */ + record->offscreen_world = (GWorldPtr) 0; + record->uses_gworld = in_color; + if (in_color) { + GDHandle gh; + + SetPortWindowPort(*window); + GetGWorld(&(record->its_window_world), &gh); + } else { + record->its_window_world = (GWorldPtr)0; + } + +#if CLIP_RECT_ONLY + empty_rect (&(record->invalid_rect)); +#else + record->invalid_part = NewRgn (); + if (!record->invalid_part) { + return destroy_tty (*window); + } +#endif + + return noErr; +} + + +short +init_tty_number (WindowPtr window, short font_number, short font_size, + short x_size, short y_size) { +RECORD_EXISTS (record); + + record->font_number = font_number; + record->font_size = font_size; + record->x_size = x_size; + record->y_size = y_size; + + return force_tty_coordinate_system_recalc (window); +} + + +/* + * Done with a window - destroy it. Release the memory only if + * it wasn't allocated when we got it! + */ +short +destroy_tty (WindowPtr window) { +short s_err; +RECORD_EXISTS (record); + + s_err = free_bits (record); + if (!s_err) { +#if !TARGET_API_MAC_CARBON + if (record->was_allocated) { + CloseWindow (window); + } else { +#endif + DisposeWindow (window); +#if !TARGET_API_MAC_CARBON + } +#endif + s_err = dispose_ptr (record); + } + + return s_err; +} + + +static void +do_set_port_font (tty_record *record) { + + PenNormal (); + TextFont (record->font_number); + TextSize (record->font_size); + if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & TA_OVERSTRIKE)) { + TextMode (srcOr); + } else { + TextMode (srcCopy); + } +} + + +/* + * Fill in some fields from some other fields that may have changed + */ +static void +calc_font_sizes (tty_record *record) { +FontInfo font_info; + + do_set_port_font (record); + + GetFontInfo (&font_info); + record->char_width = font_info.widMax; + record->ascent_height = font_info.ascent + font_info.leading; + record->row_height = record->ascent_height + font_info.descent; +} + + +/* + * Allocate memory for the bitmap holding the tty window + */ +static short +alloc_bits (tty_record *record) { +short s_err; + + SetRect (&record->its_bits.bounds, 0, 0, + record->char_width * record->x_size, + record->row_height * record->y_size); + +/* + * Clear two highest and lowest bit - not a color pixMap, and even in size + */ + record->its_bits.rowBytes = ((record->its_bits.bounds.right + 15) + >> 3) & 0x1ffe; + + if (record->uses_gworld) { + s_err = allocate_offscreen_world (record); +#if !TARGET_API_MAC_CARBON + } else { + s_err = alloc_ptr ((void **) &(record->its_bits.baseAddr), + record->its_bits.rowBytes * record->its_bits.bounds.bottom); + if (!s_err) { + s_err = alloc_ptr ((void **) &(record->offscreen_port), + sizeof (GrafPort)); + } + if (!s_err) { + OpenPort (record->offscreen_port); + SetPort (record->offscreen_port); + ClipRect (&(record->its_bits.bounds)); + SetPortBits (&(record->its_bits)); + } +#endif + } + return s_err; +} + + +/* + * Save the current port/world in a safe place for later retrieval + */ +static void +save_port (tty_record *record, void *save) { +GWorldPtr gw; +GDHandle gh; +GrafPtr gp; + + if (record->uses_gworld) { + GetGWorld (&gw, &gh); + *(GWorldPtr *) save = gw; + } else { + GetPort (&gp); + *(GrafPtr *) save = gp; + } +} + + +/* + * Restore current port/world after a save + */ +static void +use_port (tty_record *record, void *port) { + if (record->uses_gworld) { + PixMapHandle pix_map; + + SetGWorld ((GWorldPtr) port, (GDHandle) 0); + pix_map = GetGWorldPixMap (record->offscreen_world); + if (pix_map) { + if (port == record->offscreen_world) + LockPixels (pix_map); + else + UnlockPixels (pix_map); + } + } else { + SetPort ((GrafPtr) port); + } +} + + +/* + * Use offscreen drawing - lock the pixels through use_port + */ +static void +select_offscreen_port (tty_record *record) { + if (record->uses_gworld) { + use_port (record, record->offscreen_world); + } else { + use_port (record, record->offscreen_port); + } +} + + +/* + * Use the window - unlock pixels + */ +static void +select_onscreen_window (tty_record *record) { + if (record->uses_gworld) { + use_port (record, record->its_window_world); + SetPortWindowPort(record->its_window); + } else { + use_port(record, record->its_window); + } +} + + +/* + * Do bits copy depending on if we're using color or not + */ +static void +copy_bits(tty_record *record, Rect *bounds, short xfer_mode, RgnHandle mask_rgn) +{ + GWorldFlags pix_state; + BitMap * source; + + if (record->uses_gworld) { + pix_state = GetPixelsState (GetGWorldPixMap (record->offscreen_world)); + LockPixels (GetGWorldPixMap (record->offscreen_world)); + source = (BitMapPtr) *GetGWorldPixMap(record->offscreen_world); + } + else source = &record->its_bits; + + SetPortWindowPort(record->its_window); + CopyBits(source, GetPortBitMapForCopyBits(GetWindowPort(record->its_window)), + bounds, bounds, xfer_mode, mask_rgn); + + if (record->uses_gworld) { + SetPixelsState (GetGWorldPixMap (record->offscreen_world), pix_state); + } +} + + +/* + * Fill an area with the background color + */ +static void +erase_rect (tty_record *record, Rect *area) { + if (game_active && u.uhp > 0 && iflags.use_stone && record->its_window == _mt_window) { + PixPatHandle ppat; + + ppat = GetPixPat(iflags.use_stone + 127); /* find which pat to get */ + if (ppat) { /* in game window, using backgroung pattern, and have pattern */ + FillCRect (area, ppat); + DisposePixPat (ppat); + return; + } + } + EraseRect (area); +} + + +/* + * Recalculate the window based on new size, font, extent values, + * and re-allocate the bitmap. + */ +short +force_tty_coordinate_system_recalc (WindowPtr window) { +short s_err; +RECORD_EXISTS (record); + + s_err = free_bits (record); + if (s_err) { + return s_err; + } + calc_font_sizes (record); + + s_err = alloc_bits (record); + if (s_err) { +/* + * Catastrophe! We could not allocate memory for the bitmap! Things may go very + * much downhill from here! + */ + dprintf ("alloc_bits returned null in force_tty_coordinate_system_recalc!"); + return s_err; + } + select_offscreen_port (record); + do_set_port_font (record); + return clear_tty (window); +} + + +#if 0 +/* + * Update TTY according to new color environment for the window + */ +static short +tty_environment_changed (tty_record *record) { +Point p = {0, 0}; +Rect r_screen; + + if (record->uses_gworld) { + r_screen = record->its_bits.bounds; + LocalToGlobal (&p); + OffsetRect (&r_screen, p.h, p.v); + UpdateGWorld (&(record->offscreen_world), 0, &r_screen, + (CTabHandle) 0, (GDHandle) 0, stretchPix); + select_offscreen_port (record); + SetOrigin (0, 0); + select_onscreen_window (record); + } + return 0; +} +#endif + + +/* + * Read a lot of interesting and useful information from the current tty + */ +short +get_tty_metrics (WindowPtr window, short *x_size, short *y_size, + short *x_size_pixels, short *y_size_pixels, short *font_number, + short *font_size, short *char_width, short *row_height) { +RECORD_EXISTS (record); + +/* + * First, test that we actually have something to draw to... + */ + if ((((char *)0 == record->its_bits.baseAddr) && !record->uses_gworld) || + (((GWorldPtr)0 == record->offscreen_world) && record->uses_gworld)) { + return general_failure; + } + + *x_size = record->x_size; + *y_size = record->y_size; + *x_size_pixels = record->its_bits.bounds.right; + *y_size_pixels = record->its_bits.bounds.bottom; + *font_number = record->font_number; + *font_size = record->font_size; + *char_width = record->char_width; + *row_height = record->row_height; + + return noErr; +} + + +/* + * Map a position on the map to screen coordinates + */ +static void +pos_rect (tty_record *record, Rect *r, short x_pos, short y_pos, + short x_end, short y_end) { + + SetRect (r, x_pos * (record->char_width), y_pos * (record->row_height), + (1 + x_end) * (record->char_width) , (1 + y_end) * + (record->row_height)); +} + + +static void +accumulate_rect (tty_record *record, Rect *rect) { +#if CLIP_RECT_ONLY + union_rect (rect, &(record->invalid_rect), &(record->invalid_rect)); +#else +RgnHandle rh = NewRgn (); + + RectRgn (rh, rect); + UnionRgn (record->invalid_part, rh, record->invalid_part); + DisposeRgn (rh); +#endif +} + + +/* + * get and set window invalid region. exposed for HandleUpdateEvent in macwin.c + * to correct display problem + */ + +short get_invalid_region (WindowPtr window, Rect *inval_rect) { + RECORD_EXISTS (record); +#if CLIP_RECT_ONLY + if (record->invalid_rect.right <= record->invalid_rect.left || + record->invalid_rect.bottom <= record->invalid_rect.top) { + return general_failure; + } + *inval_rect = record->invalid_rect; +#else + if (EmptyRgn (record->invalid_part)) { + return general_failure; + } + *inval_rect = (*(record->invalid_part))->rgnBBox; +#endif + return noErr; +} + +short set_invalid_region (WindowPtr window, Rect *inval_rect) { + RECORD_EXISTS (record); + accumulate_rect (record, inval_rect); + return noErr; +} + + +/* + * Invert the specified position + */ +static void +curs_pos (tty_record *record, short x_pos, short y_pos, short to_state) { +Rect r; + + if (record->curs_state == to_state) { + return; + } + record->curs_state = to_state; + pos_rect (record, &r, x_pos, y_pos, x_pos, y_pos); + + if (DRAW_DIRECT) { + void *old_port; + + save_port (record, &old_port); + select_onscreen_window (record); + InvertRect (&r); + use_port (record, old_port); + } else { + accumulate_rect (record, &r); + } +} + + +/* + * Move the cursor (both as displayed and where drawing goes) + * HOWEVER: The cursor is NOT stored in the bitmap! + */ +short +move_tty_cursor (WindowPtr window, short x_pos, short y_pos) { +RECORD_EXISTS (record); + + if (record->x_curs == x_pos && record->y_curs == y_pos) { + return noErr; + } + if (record->x_size <= x_pos || x_pos < 0 || + record->y_size <= y_pos || y_pos < 0) { + return general_failure; + } + curs_pos (record, record->x_curs, record->y_curs, 0); + record->x_curs = x_pos; + record->y_curs = y_pos; + curs_pos (record, x_pos, y_pos, 1); + + return noErr; +} + + +/* + * Update the screen to match the current bitmap, after adding stuff + * with add_tty_char etc. + */ +short +update_tty (WindowPtr window) { +Rect r; +RECORD_EXISTS (record); + +#if CLIP_RECT_ONLY + if (record->invalid_rect.right <= record->invalid_rect.left || + record->invalid_rect.bottom <= record->invalid_rect.top) { + return noErr; + } + r = record->invalid_rect; +#else + if (EmptyRgn (record->invalid_part)) { + return noErr; + } + r = (*(record->invalid_part))->rgnBBox; +#endif + select_onscreen_window (record); + copy_bits (record, &r, srcCopy, (RgnHandle) 0); +#if CLIP_RECT_ONLY + empty_rect (&(record->invalid_rect)); +#else + SetEmptyRgn (record->invalid_part); +#endif + if (record->curs_state) { + pos_rect (record, &r, record->x_curs, record->y_curs, + record->x_curs, record->y_curs); + InvertRect (&r); + } + + return noErr; +} + + +/* + * Low level add to screen + */ +static void +do_add_string (tty_record *record, char *str, short len) { +Rect r; + + if (len < 1) { + return; + } + select_offscreen_port (record); + + MoveTo (record->x_curs * record->char_width, record->y_curs * + record->row_height + record->ascent_height); + DrawText (str, 0, len); + + pos_rect (record, &r, record->x_curs, record->y_curs, + record->x_curs + len - 1, record->y_curs); + select_onscreen_window (record); + if (DRAW_DIRECT) { + copy_bits (record, &r, srcCopy, (RgnHandle)0); + } else { + accumulate_rect (record, &r); + } +} + + +/* + * Low-level cursor handling routine + */ +static void +do_add_cursor (tty_record *record, short x_pos) { + + record->x_curs = x_pos; + if (record->x_curs >= record->x_size) { + if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND)) { + record->y_curs ++; + record->x_curs = 0; + if (record->y_curs >= record->y_size) { + if (0L != (record->attribute [TTY_ATTRIB_FLAGS] & + TA_INHIBIT_VERT_SCROLL)) { + record->y_curs = record->y_size; + } else { + scroll_tty (record->its_window, 0, 1 + record->y_curs - + record->y_size); + } + } + } else { + record->x_curs = record->x_size; + } + } +} + + +/* + * Do control character + */ +static void +do_control (tty_record *record, short character) { + int recurse = 0; + +/* + * Check recursion because nl_add_cr and cr_add_nl may both be set and invoke each other + */ + do { + switch (character) { + case CHAR_CR : + record->x_curs = 0; + if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_CR_ADD_NL)) { + recurse = 1; + } + else { + recurse = 0; + break; + } /* FALL-THROUGH: if CR-LF, don't bother with loop */ + case CHAR_LF : + record->y_curs++; + if (record->y_curs >= record->y_size) { + scroll_tty (record->its_window, 0, 1 + record->y_curs - record->y_size); + } + if (!recurse && (record->attribute [TTY_ATTRIB_CURSOR] & TA_NL_ADD_CR)) { + character = CHAR_CR; + recurse = 1; + } + else recurse = 0; + break; + case CHAR_BELL : + tty_nhbell(); + break; + case CHAR_BS : + if (record->x_curs > 0) + record->x_curs --; + default : + break; + } + } while (recurse); +} + + +/* + * Add a single character. It is drawn directly if the correct flag is set, + * else deferred to the next update event or call of update_tty() + */ +short +add_tty_char (WindowPtr window, short character) { +register char is_control; +char ch; +RECORD_EXISTS (record); + + if (!(record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND) && + record->x_curs >= record->x_size) + return noErr; /* Optimize away drawing across border without wrap */ + + if (record->curs_state != 0) + curs_pos (record, record->x_curs, record->y_curs, 0); + + ch = character; + is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L); + if (is_control) + do_control (record, ch); + else { + do_add_string (record, (char *)&ch, 1); + do_add_cursor (record, record->x_curs + 1); + } + + return noErr; +} + + +/* + * Add a null-terminated string of characters + */ +short +add_tty_string(WindowPtr window, const char *string) +{ + register const unsigned char * start_c; + register const unsigned char * the_c; + register unsigned char ch, is_control = 0, tty_wrap; + register short max_x, pos_x; + RECORD_EXISTS (record); + + if (record->curs_state != 0) + curs_pos (record, record->x_curs, record->y_curs, 0); + + the_c = (const unsigned char *) string; + max_x = record->x_size; + tty_wrap = (record->attribute [TTY_ATTRIB_FLAGS] & TA_WRAP_AROUND); + for (;;) { + pos_x = record->x_curs; + if (!tty_wrap && pos_x >= max_x) + break; /* Optimize away drawing across border without wrap */ + + start_c = the_c; + ch = *the_c; + while (pos_x < max_x) { + is_control = (ch < sizeof(long) * 8) && ((s_control & (1 << ch)) != 0L); + if (is_control) + break; + the_c ++; ch = *the_c; + pos_x ++; + } + do_add_string (record, (char *) start_c, the_c - start_c); + do_add_cursor (record, pos_x); + if (!ch) + break; + + if (is_control) { + do_control (record, ch); + the_c ++; + } + } + + return noErr; +} + + +/* + * Read or change attributes for the tty. Note that some attribs may + * very well clear and reallocate the bitmap when changed, whereas + * others (color, highlight, ...) are guaranteed not to. + */ +short get_tty_attrib (WindowPtr window, tty_attrib attrib, long *value) { +RECORD_EXISTS (record); + + if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) { + return general_failure; + } + *value = record->attribute [attrib]; + + return noErr; +} + + +short set_tty_attrib (WindowPtr window, tty_attrib attrib, long value) { +RGBColor rgb_color; +RECORD_EXISTS (record); + + if (attrib < 0 || attrib >= TTY_NUMBER_ATTRIBUTES) { + return general_failure; + } + record->attribute [attrib] = value; + /* + * Presently, no attributes generate a new bitmap. + */ + switch (attrib) { + case TTY_ATTRIB_CURSOR : +/* + * Check if we should change tables + */ + if (0L != (value & TA_RAW_OUTPUT)) { + s_control = RAW_CONTROLS; + } else { + s_control = COOKED_CONTROLS; + } + break; + case TTY_ATTRIB_FLAGS : +/* + * Check if we should flush the output going from cached to draw-direct + */ + if (0L != (value & TA_ALWAYS_REFRESH)) { + update_tty (window); + } + break; + case TTY_ATTRIB_FOREGROUND : +/* + * Set foreground color + */ + TA_TO_RGB (value, rgb_color); + select_offscreen_port (record); + RGBForeColor (&rgb_color); + select_onscreen_window (record); + break; + case TTY_ATTRIB_BACKGROUND : +/* + * Set background color + */ + TA_TO_RGB (value, rgb_color); + select_offscreen_port (record); + RGBBackColor (&rgb_color); + select_onscreen_window (record); + break; + default : + break; + } + return noErr; +} + + +/* + * Scroll the window. Positive is up/left. scroll_tty ( window, 0, 1 ) is a line feed. + * Scroll flushes the accumulated update area by calling update_tty(). + */ + +short scroll_tty (WindowPtr window, short delta_x, short delta_y) { +RgnHandle rgn; +short s_err; +RECORD_EXISTS (record); + + s_err = update_tty (window); + + rgn = NewRgn (); + + select_offscreen_port (record); + ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width, + -delta_y * record->row_height, rgn); + EraseRgn (rgn); + SetEmptyRgn (rgn); + + select_onscreen_window (record); + ScrollRect (&(record->its_bits.bounds), -delta_x * record->char_width, + -delta_y*record->row_height, rgn); + EraseRgn (rgn); + DisposeRgn (rgn); + + record->y_curs -= delta_y; + record->x_curs -= delta_x; + + return noErr; +} + + +/* + * Clear the screen. Immediate. + */ +short clear_tty (WindowPtr window) { +RECORD_EXISTS (record); + + record->curs_state = 0; + select_offscreen_port (record); + erase_rect (record, &(record->its_bits.bounds)); + accumulate_rect (record, &(record->its_bits.bounds)); + update_tty (window); + + return noErr; +} + + +/* + * Blink cursor on window if necessary + */ +short blink_cursor (WindowPtr window, long when) { + RECORD_EXISTS (record); + + if ((record->attribute [TTY_ATTRIB_CURSOR] & TA_BLINKING_CURSOR)) { + if (when > record->last_cursor + GetCaretTime ()) { + curs_pos (record, record->x_curs, record->y_curs, !record->curs_state); + record->last_cursor = when; + update_tty (window); + } + } + return 0; +} + + +/* + * Draw an image of the tty - used for update events and can be called + * for screen dumps. + */ +short +image_tty (EventRecord *theEvent, WindowPtr window) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(theEvent) +#endif +RECORD_EXISTS (record); + +#if CLIP_RECT_ONLY + record->invalid_rect = record->its_bits.bounds; +#else +RgnHandle rh = NewRgn (); + + RectRgn (rh, record ->its_bits.bounds); + UnionRgn (record->invalid_part, rh, record->invalid_part); + DisposeRgn (rh); +#endif + return update_tty (window); +} + + + +/* + * Clear an area + */ +short clear_tty_window (WindowPtr window, short from_x, short from_y, + short to_x, short to_y) { +Rect r; +RECORD_EXISTS (record); + + if (from_x > to_x || from_y > to_y) { + return general_failure; + } + pos_rect (record, &r, from_x, from_y, to_x, to_y); + select_offscreen_port (record); + erase_rect (record, &r); + accumulate_rect (record, &r); + if (DRAW_DIRECT) { + update_tty (window); + } else + select_onscreen_window (record); + return noErr; +} + + +#if EXTENDED_SUPPORT +/* + * Delete or insert operations used by many terminals can bottleneck through + * here. Note that the order of executin for row/colum insertions is NOT + * specified. Negative values for num_ mean delete, zero means no effect. + */ +short mangle_tty_rows_columns (WindowPtr window, short from_row, short num_rows, + short from_column, short num_columns) { +Rect r; +RgnHandle rh = NewRgn (); +RECORD_EXISTS (record); + + update_tty (window); /* Always make sure screen is OK */ + curs_pos (record, record->x_curs, record->y_curs, 0); + + if (num_rows) { + pos_rect (record, &r, 0, from_row, record->x_size - 1, + record->y_size - 1); + select_offscreen_port (record); + ScrollRect (&r, 0, num_rows * record->row_height, rh); + EraseRgn (rh); + SetEmptyRgn (rh); + select_onscreen_window (record); + ScrollRect (&r, 0, num_rows * record->row_height, rh); + EraseRgn (rh); + SetEmptyRgn (rh); + } + if (num_columns) { + pos_rect (record, &r, from_column, 0, record->x_size - 1, + record->y_size - 1); + select_offscreen_port (record); + ScrollRect (&r, num_columns * record->char_width, 0, rh); + EraseRgn (rh); + SetEmptyRgn (rh); + select_onscreen_window (record); + ScrollRect (&r, num_columns * record->char_width, 0, rh); + EraseRgn (rh); + SetEmptyRgn (rh); + } + DisposeRgn (rh); + if (record->x_curs >= from_column) { + record->x_curs += num_columns; + } + if (record->y_curs >= from_row) { + record->y_curs += num_rows; + } + curs_pos (record, record->x_curs, record->y_curs, 1); + + return noErr; +} + +/* + * Frame an area in an aesthetically pleasing way. + */ +short frame_tty_window (WindowPtr window, short from_x, short from_y , + short to_x, short to_y, short frame_fatness) { +Rect r; +RECORD_EXISTS (record); + + if (from_x > to_x || from_y > to_y) { + return general_failure; + } + pos_rect (record, & r, from_x, from_y, to_x, to_y); + select_offscreen_port (record); + PenSize (frame_fatness, frame_fatness); + FrameRect (&r); + PenNormal (); + accumulate_rect (record, &r); + if (DRAW_DIRECT) { + update_tty (window); + } else + select_onscreen_window (record); +} + + +/* + * Highlighting a specific part of the tty window + */ +short invert_tty_window (WindowPtr window, short from_x, short from_y , + short to_x, short to_y) { +Rect r; +RECORD_EXISTS (record); + + if (from_x > to_x || from_y > to_y) { + return general_failure; + } + pos_rect (record, &r, from_x, from_y, to_x, to_y); + select_offscreen_port (record); + InvertRect ( &r); + accumulate_rect (record, &r); + if (DRAW_DIRECT) { + update_tty (window); + } else + select_onscreen_window (record); +} + + +static void +canonical_rect (Rect * r, short x1, short y1, short x2, short y2) { + if (x1 < x2) { + if (y1 < y2) { + SetRect (r, x1, x2, y1, y2); + } else { + SetRect (r, x1, x2, y2, y1); + } + } else { + if (y1 < y2) { + SetRect (r, x2, x1, y1, y2); + } else { + SetRect (r, x2, x1, y2, y1); + } + } +} + + +/* + * Line drawing - very device dependent + */ +short draw_tty_line (WindowPtr window, short from_x, short from_y , + short to_x, short to_y) { +Rect r; +RECORD_EXISTS (record); + + select_offscreen_port (record); + MoveTo (from_x, from_y); + LineTo (to_x, to_y); + canonical_rect (&r, from_x, from_y, to_x, to_y); + accumulate_rect (record, &r); + if (DRAW_DIRECT) { + update_tty (window); + } else + select_onscreen_window (record); +} + + +#endif /* EXTENDED_SUPPORT */ diff --git a/sys/mac/macunix.c b/sys/mac/macunix.c new file mode 100644 index 0000000..aedcc0e --- /dev/null +++ b/sys/mac/macunix.c @@ -0,0 +1,38 @@ +/* SCCS Id: @(#)macunix.c 3.1 94/11/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file collects some Unix dependencies */ + +#include "hack.h" + +void +regularize(char *s) +{ + register char *lp; + + for (lp = s; *lp; lp++) { + if (*lp == '.' || *lp == ':') + *lp = '_'; + } +} + +void +getlock(void) +{ + int fd; + int pid = getpid(); /* Process ID */ + + set_levelfile_name (lock, 0); + + if ((fd = open (lock, O_RDWR | O_EXCL | O_CREAT, LEVL_TYPE)) == -1) { + raw_printf ("Could not lock the game %s.", lock); + panic ("Another game in progress?"); + } + + if (write (fd, (char *)&pid, sizeof (pid)) != sizeof (pid)) { + raw_printf ("Could not lock the game %s.", lock); + panic("Disk locked?"); + } + close (fd); +} diff --git a/sys/mac/macwin.c b/sys/mac/macwin.c new file mode 100644 index 0000000..a54f454 --- /dev/null +++ b/sys/mac/macwin.c @@ -0,0 +1,2650 @@ +/* SCCS Id: @(#)macwin.c 3.4 1996/01/15 */ +/* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "func_tab.h" +#include "macwin.h" +#include "mactty.h" +#include "wintty.h" + +#if !TARGET_API_MAC_CARBON +#include +#include +#include +#include +#include +#include +#endif + +NhWindow *theWindows = (NhWindow *) 0; +Cursor qdarrow; + +/* Borrowed from the Mac tty port */ +extern WindowPtr _mt_window; + +/* Some useful #defines for the scroll bar width and height */ +#define SBARWIDTH 15 +#define SBARHEIGHT 15 + +/* + * We put a TE on the message window for the "top line" queries. + * top_line is the TE that holds both the query and the user's + * response. The first topl_query_len characters in top_line are + * the query, the rests are the response. topl_resp is the valid + * response to a yn query, while topl_resp[topl_def_idx] is the + * default response to a yn query. + */ +static TEHandle top_line = (TEHandle) nil; +static int topl_query_len; +static int topl_def_idx = -1; +static char topl_resp[10] = ""; + +#define CHAR_ANY '\n' + +/* + * inSelect means we have a menu window up for selection or + * something similar. It makes the window with win number == + * inSelect a movable modal (unfortunately without the border) + * and clicking the close box forces an RET into the key + * buffer. Don't forget to set inSelect to WIN_ERR when you're + * done... + */ +static winid inSelect = WIN_ERR; + +/* + * The key queue ring buffer where Read is where to take from, + * Write is where next char goes and count is queue depth. + */ +static unsigned char keyQueue [QUEUE_LEN]; +static int keyQueueRead = 0, + keyQueueWrite = 0, + keyQueueCount = 0; + +static Boolean gClickedToMove = 0; /* For ObscureCursor */ + +static Point clicked_pos; /* For nh_poskey */ +static int clicked_mod; +static Boolean cursor_locked = false; + +static ControlActionUPP MoveScrollUPP; /* scrolling callback, init'ed in InitMac */ + +void +lock_mouse_cursor(Boolean new_cursor_locked) { + cursor_locked = new_cursor_locked; +} + + +/* + * Add key to input queue, force means flush left and replace if full + */ +void +AddToKeyQueue (unsigned char ch, Boolean force) { + if (keyQueueCount < QUEUE_LEN) { + keyQueue [keyQueueWrite++] = ch; + keyQueueCount++; + } + else if (force) { + keyQueue [keyQueueWrite++] = ch; + keyQueueRead++; + if (keyQueueRead >= QUEUE_LEN) + keyQueueRead = 0; + keyQueueCount = QUEUE_LEN; + } + if (keyQueueWrite >= QUEUE_LEN) + keyQueueWrite = 0; +} + + +/* + * Get key from queue + */ +unsigned char +GetFromKeyQueue (void) { + unsigned char ret; + + if (keyQueueCount) { + ret = keyQueue [keyQueueRead++]; + keyQueueCount--; + if (keyQueueRead >= QUEUE_LEN) + keyQueueRead = 0; + } + else + ret = 0; + return ret; +} + + +/* + * Cursor movement + */ +static RgnHandle gMouseRgn = (RgnHandle) 0; + +/* + * _Gestalt madness - we rely heavily on the _Gestalt glue, since we + * don't check for the trap... + */ +MacFlags macFlags; + +/* + * The screen layouts on the small 512x342 screen need special cares. + */ +Boolean small_screen = 0; + +#ifdef NHW_BASE +# undef NHW_BASE +#endif +#define NHW_BASE 0 + +static int FDECL(filter_scroll_key,(const int, NhWindow *)); + +static void FDECL(GeneralKey, (EventRecord *, WindowPtr)); +static void FDECL(macKeyMenu, (EventRecord *, WindowPtr)); +static void FDECL(macKeyText, (EventRecord *, WindowPtr)); + +static void FDECL(macClickMessage, (EventRecord *, WindowPtr)); +static void FDECL(macClickTerm, (EventRecord *, WindowPtr)); +static void FDECL(macClickMenu, (EventRecord *, WindowPtr)); +static void FDECL(macClickText, (EventRecord *, WindowPtr)); + +static short FDECL(macDoNull, (EventRecord *, WindowPtr)); +static short FDECL(macUpdateMessage, (EventRecord *, WindowPtr)); +static short FDECL(macUpdateMenu, (EventRecord *, WindowPtr)); +static short FDECL(GeneralUpdate, (EventRecord *, WindowPtr)); + +static void FDECL(macCursorTerm, (EventRecord *, WindowPtr, RgnHandle)); +static void FDECL(GeneralCursor, (EventRecord *, WindowPtr, RgnHandle)); + +static void FDECL(DoScrollBar,(Point, short, ControlHandle, NhWindow *)); +static pascal void FDECL(MoveScrollBar, (ControlHandle, short)); + +typedef void (*CbFunc) (EventRecord *, WindowPtr); +typedef short (*CbUpFunc) (EventRecord *, WindowPtr); +typedef void (*CbCursFunc) (EventRecord *, WindowPtr, RgnHandle); + +#define NUM_FUNCS 6 +static const CbFunc winKeyFuncs [NUM_FUNCS] = { + GeneralKey, GeneralKey, GeneralKey, GeneralKey, macKeyMenu, macKeyText +}; + +static const CbFunc winClickFuncs [NUM_FUNCS] = { + (CbFunc)macDoNull, macClickMessage, macClickTerm, macClickTerm, macClickMenu, + macClickText +}; + +static const CbUpFunc winUpdateFuncs [NUM_FUNCS] = { + macDoNull, macUpdateMessage, image_tty, image_tty, + macUpdateMenu, GeneralUpdate +}; + +static const CbCursFunc winCursorFuncs [NUM_FUNCS] = { + (CbCursFunc) macDoNull, GeneralCursor, macCursorTerm, macCursorTerm, + GeneralCursor, GeneralCursor +}; + + +static NhWindow * +GetNhWin(WindowPtr mac_win) { + if (mac_win == _mt_window) /* term window is still maintained by both systems, and */ + return theWindows; /* WRefCon still refers to tty struct, so we have to map it */ + else { + NhWindow *aWin = (NhWindow *)GetWRefCon (mac_win); + if (aWin >= theWindows && aWin < &theWindows[NUM_MACWINDOWS]) + return aWin; + } + return ((NhWindow *) nil); +} + + +Boolean CheckNhWin (WindowPtr mac_win) { + return GetNhWin (mac_win) != nil; +} + + +static pascal OSErr +AppleEventHandler (const AppleEvent* inAppleEvent, AppleEvent* outAEReply, long inRefCon) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(outAEReply,inRefCon) +#endif + Size actualSize; + DescType typeCode; + AEEventID EventID; + OSErr err; + + /* Get Event ID */ + err = AEGetAttributePtr (inAppleEvent, keyEventIDAttr, typeType, &typeCode, + &EventID, sizeof (EventID), &actualSize); + if (err == noErr) { + switch (EventID) { + default : + case kAEOpenApplication : + macFlags.gotOpen = 1; + /* fall through */ + case kAEPrintDocuments : + err = errAEEventNotHandled; + break; + case kAEQuitApplication : + /* Flush key queue */ + keyQueueCount = keyQueueWrite = keyQueueRead = 0; + AddToKeyQueue ('S', 1); + break; + case kAEOpenDocuments : { + FSSpec fss; + FInfo fndrInfo; + AEKeyword keywd; + AEDescList docList; + long index, itemsInList; + + if((err = AEGetParamDesc(inAppleEvent, keyDirectObject, typeAEList, &docList)) != noErr || + (err = AECountItems(&docList, &itemsInList)) != noErr){ + if (err == errAEDescNotFound) + itemsInList = 0; + else + break; + } + + for(index = 1; index <= itemsInList; index++){ + err = AEGetNthPtr(&docList, index, typeFSS, &keywd, &typeCode, (Ptr)&fss, + sizeof(FSSpec), &actualSize); + if(noErr != err) + break; + + err = FSpGetFInfo (&fss, &fndrInfo); + if (noErr != err) + break; + + if (fndrInfo.fdType != SAVE_TYPE) + continue; /* only look at save files */ + + process_openfile (fss.vRefNum, fss.parID, fss.name, fndrInfo.fdType); + if (macFlags.gotOpen) + break; /* got our save file */ + } + err = AEDisposeDesc(&docList); + break; + } + } + } + + /* Check to see if all required parameters for this type of event are present */ + if (err == noErr) { + err = AEGetAttributePtr (inAppleEvent, keyMissedKeywordAttr, + typeWildCard, &typeCode, NULL, 0, &actualSize); + if (err == errAEDescNotFound) + err = noErr; /* got all the required parameters */ + else if (err == noErr) /* missed a required parameter */ + err = errAEEventNotHandled; + } + + return err; +} + + +short win_fonts [NHW_TEXT + 1]; + +void +InitMac(void) { + short i; + long l; + Str255 volName; + + +#if !TARGET_API_MAC_CARBON + if (LMGetDefltStack() < 50 * 1024L) { + SetApplLimit ((void *) ((long) LMGetCurStackBase() - (50 * 1024L))); + } + MaxApplZone (); + for (i = 0; i < 5; i ++) + MoreMasters (); + + InitGraf (&qd.thePort); + InitFonts (); + InitWindows (); + InitMenus (); + InitDialogs (0L); + TEInit (); +#endif + + memset (&macFlags, 0, sizeof(macFlags)); + if (!Gestalt (gestaltOSAttr, & l)) { + macFlags.processes = (l & (1 << gestaltLaunchControl)) ? 1 : 0; + macFlags.tempMem = (l & (1 << gestaltRealTempMemory)) ? 1 : 0; + macFlags.hasDebugger = (l & (1 << gestaltSysDebuggerSupport)) ? 1 : 0; + } + if (!Gestalt (gestaltQuickdrawVersion, & l)) + macFlags.color = (l >= gestalt8BitQD) ? 1 : 0; + + if (!Gestalt (gestaltFindFolderAttr, & l)) + macFlags.folders = (l & (1 << gestaltFindFolderPresent)) ? 1 : 0; + + if (!Gestalt (gestaltHelpMgrAttr, & l)) + macFlags.help = (l & (1 << gestaltHelpMgrPresent)) ? 1 : 0; + + if (!Gestalt (gestaltFSAttr, & l)) + macFlags.fsSpec = (l & (1 << gestaltHasFSSpecCalls)) ? 1 : 0; + + if (!Gestalt (gestaltFontMgrAttr, & l)) + macFlags.trueType = (l & (1 << gestaltOutlineFonts)) ? 1 : 0; + + if (!Gestalt (gestaltAUXVersion, & l)) + macFlags.aux = (l >= 0x200) ? 1 : 0; + + if (!Gestalt (gestaltAliasMgrAttr, & l)) + macFlags.alias = (l & (1 << gestaltAliasMgrPresent)) ? 1 : 0; + + if (!Gestalt (gestaltStandardFileAttr, & l)) + macFlags.standardFile = (l & (1 << gestaltStandardFile58)) ? 1 : 0; + + gMouseRgn = NewRgn (); + InitCursor(); + GetQDGlobalsArrow(&qdarrow); + ObscureCursor (); + + MoveScrollUPP = NewControlActionUPP(MoveScrollBar); + + /* Set up base fonts for all window types */ + GetFNum ("\pHackFont", &i); + if (i == 0) + i = kFontIDMonaco; + win_fonts [NHW_BASE] = win_fonts [NHW_MAP] = win_fonts [NHW_STATUS] = i; + GetFNum ("\pPSHackFont", &i); + if (i == 0) + i = kFontIDGeneva; + win_fonts [NHW_MESSAGE] = i; + win_fonts [NHW_TEXT] = kFontIDGeneva; + + macFlags.hasAE = 0; + if(!Gestalt(gestaltAppleEventsAttr, &l) && (l & (1L << gestaltAppleEventsPresent))){ + if (AEInstallEventHandler (kCoreEventClass, typeWildCard, + NewAEEventHandlerUPP(AppleEventHandler), + 0, + FALSE) == noErr) + macFlags.hasAE = 1; + } + +#if TARGET_API_MAC_CARBON + HGetVol(volName, &theDirs.dataRefNum, &theDirs.dataDirID); +#else + /* + * We should try to get this data from a rsrc, in the profile file + * the user double-clicked... This data should be saved with the + * save file in the resource fork, AND be saveable in "stationary" + */ + GetVol (volName, &theDirs.dataRefNum ); + GetWDInfo (theDirs.dataRefNum, &theDirs.dataRefNum, &theDirs.dataDirID, &l); +#endif + if (volName [0] > 31) volName [0] = 31; + for (l = 1; l <= volName [0]; l++) { + if (volName [l] == ':') { + volName [l] = 0; + volName [0] = l - 1; + break; + } + } + BlockMove (volName, theDirs.dataName, l); + BlockMove (volName, theDirs.saveName, l); + BlockMove (volName, theDirs.levelName, l); + theDirs.saveRefNum = theDirs.levelRefNum = theDirs.dataRefNum; + theDirs.saveDirID = theDirs.levelDirID = theDirs.dataDirID; + + /* Create the "record" file, if necessary */ + check_recordfile(""); + return; +} + + +/* + * Change default window fonts. + */ +short +set_tty_font_name (int window_type, char *font_name) { + short fnum; + Str255 new_font; + + if (window_type < NHW_BASE || window_type > NHW_TEXT) + return general_failure; + + C2P (font_name, new_font); + GetFNum (new_font, &(fnum)); + if (!fnum) + return general_failure; + win_fonts [window_type] = fnum; + return noErr; +} + + +static void +DrawScrollbar (NhWindow *aWin) +{ + WindowPtr theWindow = aWin->its_window; + Rect crect, wrect; + Boolean vis; + short val, lin, win_height; + + + if (!aWin->scrollBar) + return; + GetControlBounds(aWin->scrollBar, &crect); + GetWindowBounds(aWin->its_window, kWindowContentRgn, &wrect); + OffsetRect(&wrect, -wrect.left, -wrect.top); + win_height = wrect.bottom - wrect.top; + + if (crect.top != wrect.top - 1 || + crect.left != wrect.right - SBARWIDTH) { + MoveControl (aWin->scrollBar, wrect.right - SBARWIDTH, wrect.top - 1); + } + if (crect.bottom != wrect.bottom - SBARHEIGHT || + crect.right != wrect.right + 1) { + SizeControl (aWin->scrollBar, SBARWIDTH+1, win_height - SBARHEIGHT + 2); + } + vis = (win_height > (50 + SBARHEIGHT)); + if (vis != IsControlVisible(aWin->scrollBar)) { + /* current status != control */ + if (vis)/* if visible, show */ + ShowControl (aWin->scrollBar); + else /* else hide */ + HideControl (aWin->scrollBar); + } + lin = aWin->y_size; + if (aWin == theWindows + WIN_MESSAGE) { + /* calculate how big scroll bar is for message window */ + lin -= (win_height - SBARHEIGHT) / aWin->row_height; + if (lin < 0) + lin = 0; + val = 0; /* always have message scrollbar active */ + } + else { + /* calculate how big scroll bar is for other windows */ + lin -= win_height / aWin->row_height; + if (lin < 0) + lin = 0; + if (lin) val = 0; /* if there are 1+ screen lines, activate scrollbar */ + else val = 255; /* else grey it out */ + } + SetControlMaximum (aWin->scrollBar, lin); + HiliteControl (aWin->scrollBar, val); + val = GetControlValue (aWin->scrollBar); + if (val != aWin->scrollPos) { + InvalWindowRect(theWindow, &wrect); + aWin->scrollPos = val; + } +} + + +#define MAX_HEIGHT 100 +#define MIN_HEIGHT 50 +#define MIN_WIDTH 300 + +/* + * This function could be overloaded with any amount of intelligence... + */ +int +SanePositions (void) +{ +#if TARGET_API_MAC_CARBON + ConstrainWindowToScreen(_mt_window, kWindowStructureRgn, + kWindowConstrainMoveRegardlessOfFit, NULL, NULL); +#else + short left, top, width, height; + int ix, numText = 0, numMenu = 0; + int mbar_height = GetMBarHeight(); + BitMap qbitmap; + Rect screenArea; + WindowPtr theWindow; + NhWindow *nhWin; + + + screenArea = GetQDGlobalsScreenBits(&qbitmap)->bounds; + OffsetRect (&screenArea, - screenArea.left, - screenArea.top); + +/* Map Window */ + height = _mt_window->portRect.bottom - _mt_window->portRect.top; + width = _mt_window->portRect.right - _mt_window->portRect.left; + + if (!RetrievePosition (kMapWindow, &top, &left)) { + top = mbar_height + (small_screen ? 2 : 20); + left = (screenArea.right - width) / 2; + } + MoveWindow (_mt_window, left, top, 1); + +/* Message Window */ + if (!RetrievePosition (kMessageWindow, &top, &left)) { + top += height; + if (!small_screen) + top += 20; + } + + if (!RetrieveSize (kMessageWindow, top, left, &height, &width)) { + height = screenArea.bottom - top - (small_screen ? 2-SBARHEIGHT : 2); + if (height > MAX_HEIGHT) { + height = MAX_HEIGHT; + } else if (height < MIN_HEIGHT) { + height = MIN_HEIGHT; + width = MIN_WIDTH; + left = screenArea.right - width; + top = screenArea.bottom - MIN_HEIGHT; + } + } + +/* Move these windows */ + nhWin = theWindows + WIN_MESSAGE; + theWindow = nhWin->its_window; + + MoveWindow (theWindow, left, top, 1); + SizeWindow (theWindow, width, height, 1); + if (nhWin->scrollBar) + DrawScrollbar (nhWin); + + /* Handle other windows */ + for (ix = 0; ix < NUM_MACWINDOWS; ix ++) { + if (ix != WIN_STATUS && ix != WIN_MESSAGE && ix != WIN_MAP && ix != BASE_WINDOW) { + theWindow = theWindows [ix].its_window; + if (theWindow && ((WindowPeek) theWindow)->visible) { + int shift; + if (((WindowPeek)theWindow)->windowKind == WIN_BASE_KIND + NHW_MENU) { + if (!RetrievePosition (kMenuWindow, &top, &left)) { + top = mbar_height * 2; + left = 2; + } + top += (numMenu * mbar_height); + numMenu++; + shift = 20; + } else { + if (!RetrievePosition (kTextWindow, &top, &left)) { + top = mbar_height * 2; + left = screenArea.right - 3 - + (theWindow->portRect.right - theWindow->portRect.left); + } + top += (numText * mbar_height); + numText++; + shift = -20; + } + while (top > screenArea.bottom - MIN_HEIGHT) { + top -= screenArea.bottom - mbar_height * 2; + left += shift; + } + MoveWindow (theWindow, left, top, 1); + } + } + } +#endif + return (0); +} + + +winid +mac_create_nhwindow (int kind) { + int i; + NhWindow *aWin; + FontInfo fi; + + if (kind < NHW_BASE || kind > NHW_TEXT) { + error ("cre_win: Invalid kind %d.", kind); + return WIN_ERR; + } + + for (i = 0; i < NUM_MACWINDOWS; i ++) { + if (!theWindows [i].its_window) + break; + } + if (i >= NUM_MACWINDOWS) { + error ("cre_win: Win full; freeing extras"); + for (i = 0; i < NUM_MACWINDOWS; i ++) { + if (IsWindowVisible(theWindows [i].its_window) || i == WIN_INVEN || + GetWindowKind(theWindows [i].its_window) != WIN_BASE_KIND + NHW_MENU && + GetWindowKind(theWindows [i].its_window) != WIN_BASE_KIND + NHW_TEXT) + continue; + mac_destroy_nhwindow(i); + goto got1; + } + error ("cre_win: Out of ids!"); + return WIN_ERR; + } + +got1 : + aWin = &theWindows [i]; + aWin->windowTextLen = 0L; + aWin->scrollBar = (ControlHandle) 0; + aWin->menuInfo = 0; + aWin->menuSelected = 0; + aWin->miLen = 0; + aWin->miSize = 0; + aWin->menuChar = 'a'; + + dprintf ("cre_win: New kind %d", kind); + + if (kind == NHW_BASE || kind == NHW_MAP || kind == NHW_STATUS) { + short x_sz, x_sz_p, y_sz, y_sz_p; + if (kind != NHW_BASE) { + if (i != tty_create_nhwindow(kind)) { + dprintf ("cre_win: error creating kind %d", kind); + } + if (kind == NHW_MAP) { + wins[i]->offy = 0; /* the message box is in a separate window */ + } + } + aWin->its_window = _mt_window; + get_tty_metrics(aWin->its_window, &x_sz, &y_sz, &x_sz_p, &y_sz_p, + &aWin->font_number, &aWin->font_size, + &aWin->char_width, &aWin->row_height); + return i; + } + + aWin->its_window = GetNewWindow (WIN_BASE_RES + kind, (WindowPtr) 0L, (WindowPtr) -1L); + SetWindowKind(aWin->its_window, WIN_BASE_KIND + kind); + SetWRefCon(aWin->its_window, (long) aWin); + if (!(aWin->windowText = NewHandle (TEXT_BLOCK))) { + error ("cre_win: NewHandle fail(%ld)", (long) TEXT_BLOCK); + DisposeWindow (aWin->its_window); + aWin->its_window = (WindowPtr) 0; + return WIN_ERR; + } + aWin->x_size = aWin->y_size = 0; + aWin->x_curs = aWin->y_curs = 0; + aWin->drawn = TRUE; + mac_clear_nhwindow (i); + + SetPortWindowPort(aWin->its_window); + + if (kind == NHW_MESSAGE) { + aWin->font_number = win_fonts [NHW_MESSAGE]; + aWin->font_size = iflags.wc_fontsiz_message? iflags.wc_fontsiz_message : + iflags.large_font ? 12 : 9; + if (!top_line) { + const Rect out_of_scr = {10000, 10000, 10100, 10100}; + TextFont(aWin->font_number); + TextSize(aWin->font_size); + TextFace(bold); + top_line = TENew(&out_of_scr, &out_of_scr); + TEActivate(top_line); + TextFace(normal); + } + } else { + aWin->font_number = win_fonts [NHW_TEXT]; + aWin->font_size = iflags.wc_fontsiz_text ? iflags.wc_fontsiz_text : 9; + } + + TextFont (aWin->font_number); + TextSize (aWin->font_size); + + GetFontInfo (&fi); + aWin->ascent_height = fi.ascent + fi.leading; + aWin->row_height = aWin->ascent_height + fi.descent; + aWin->char_width = fi.widMax; + + if (kind == NHW_MENU || kind == NHW_TEXT || kind == NHW_MESSAGE) { + Rect r; + + GetWindowBounds(aWin->its_window, kWindowContentRgn, &r); + r.right -= (r.left - 1); + r.left = r.right - SBARWIDTH; + r.bottom -= (r.top + SBARHEIGHT); + r.top = -1; + aWin->scrollBar = NewControl (aWin->its_window, &r, "\p", (r.bottom > r.top + 50), 0, 0, 0, 16, 0L); + aWin->scrollPos = 0; + } + return i; +} + + +void +mac_init_nhwindows (int *argcp, char **argv) +{ +#if !TARGET_API_MAC_CARBON + Rect scr = (*GetGrayRgn())->rgnBBox; + small_screen = scr.bottom - scr.top <= (iflags.large_font ? 12*40 : 9*40); +#endif + Rect r; + + + InitMenuRes (); + + theWindows = (NhWindow *) NewPtrClear (NUM_MACWINDOWS * sizeof (NhWindow)); + if (MemError()) + error("mac_init_nhwindows: Couldn't allocate memory for windows."); + + DimMenuBar (); + + tty_init_nhwindows(argcp, argv); + iflags.window_inited = TRUE; + + /* Some ugly hacks to make both interfaces happy: + * Mac port uses both tty interface (for main map) and extra windows. The winids need to + * be kept in synch for both interfaces to map. Also, the "blocked" display_nhwindow case + * for the map automatically calls the tty interface for the message box, so some version + * of the message box has to exist in the tty world to prevent a meltdown, even though most + * messages are handled in mac window. + */ + mac_create_nhwindow(NHW_BASE); + tty_create_nhwindow(NHW_MESSAGE); + RetrievePosition(kMessageWindow, &r.top, &r.left); + RetrieveSize(kMessageWindow, r.top, r.left, &r.bottom, &r.right); + MoveWindow(theWindows[NHW_MESSAGE].its_window, r.left, r.top, false); + SizeWindow(theWindows[NHW_MESSAGE].its_window, r.right, r.bottom, true); + ConstrainWindowToScreen(theWindows[NHW_MESSAGE].its_window, kWindowStructureRgn, + kWindowConstrainMoveRegardlessOfFit, NULL, NULL); + return; +} + + +void +mac_clear_nhwindow (winid win) { + long l; + Rect r; + NhWindow *aWin = &theWindows [win]; + WindowPtr theWindow = aWin->its_window; + + if (win < 0 || win >= NUM_MACWINDOWS || !theWindow) { + error ("clr_win: Invalid win %d.", win); + return; + } + if (theWindow == _mt_window) { + tty_clear_nhwindow(win); + return; + } + if (!aWin->drawn) + return; + + SetPortWindowPort(theWindow); + GetWindowBounds(theWindow, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + if (aWin->scrollBar) + r.right -= SBARWIDTH; + + switch (GetWindowKind(theWindow) - WIN_BASE_KIND) { + case NHW_MESSAGE : + if (aWin->scrollPos == aWin->y_size - 1) /* if no change since last clear */ + return; /* don't bother with redraw */ + r.bottom -= SBARHEIGHT; + for (l = 0; aWin->y_size > iflags.msg_history;) { + const char cr = CHAR_CR; + l = Munger(aWin->windowText, l, &cr, 1, nil, 0) + 1; + --aWin->y_size; + } + if (l) { + aWin->windowTextLen -= l; + BlockMove(*aWin->windowText + l, *aWin->windowText, aWin->windowTextLen); + } + aWin->last_more_lin = aWin->y_size; + aWin->save_lin = aWin->y_size; + aWin->scrollPos = aWin->y_size ? aWin->y_size - 1 : 0; + break; + case NHW_MENU: + if (aWin->menuInfo) { + DisposeHandle((Handle)aWin->menuInfo); + aWin->menuInfo = NULL; + } + if (aWin->menuSelected) { + DisposeHandle((Handle)aWin->menuSelected); + aWin->menuSelected = NULL; + } + aWin->menuChar = 'a'; + aWin->miSelLen = 0; + aWin->miLen = 0; + aWin->miSize = 0; + /* Fall-Through */ + default : + SetHandleSize (aWin->windowText, TEXT_BLOCK); + aWin->windowTextLen = 0L; + aWin->x_size = 0; + aWin->y_size = 0; + aWin->scrollPos = 0; + break; + } + if (aWin->scrollBar) { + SetControlMaximum (aWin->scrollBar, aWin->y_size); + SetControlValue(aWin->scrollBar, aWin->scrollPos); + } + aWin->y_curs = 0; + aWin->x_curs = 0; + aWin->drawn = FALSE; + InvalWindowRect(theWindow, &r); +} + + +static Boolean +ClosingWindowChar(const int c) { + return c == CHAR_ESC || c == CHAR_BLANK || c == CHAR_LF || c == CHAR_CR || + c == 'q'; +} + + +static Boolean +in_topl_mode(void) +{ + Rect rect; + + + GetWindowBounds(theWindows[WIN_MESSAGE].its_window, kWindowContentRgn, &rect); + OffsetRect(&rect, -rect.left, -rect.top); + return (WIN_MESSAGE != WIN_ERR && top_line && + (*top_line)->viewRect.left < rect.right); +} + + +#define BTN_IND 2 +#define BTN_W 40 +#define BTN_H (SBARHEIGHT-3) + +static void +topl_resp_rect(int resp_idx, Rect *r) +{ + Rect rect; + + + GetWindowBounds(theWindows[WIN_MESSAGE].its_window, kWindowContentRgn, &rect); + OffsetRect(&rect, -rect.left, -rect.top); + r->left = (BTN_IND + BTN_W) * resp_idx + BTN_IND; + r->right = r->left + BTN_W; + r->bottom = rect.bottom - 1; + r->top = r->bottom - BTN_H; + return; +} + + +void +enter_topl_mode(char *query) { + if (in_topl_mode()) + return; + + putstr(WIN_MESSAGE, ATR_BOLD, query); + + topl_query_len = strlen(query); + (*top_line)->selStart = topl_query_len; + (*top_line)->selEnd = topl_query_len; + (*top_line)->viewRect.left = 0; + PtrToXHand(query, (*top_line)->hText, topl_query_len); + TECalText(top_line); + + DimMenuBar(); + mac_display_nhwindow(WIN_MESSAGE, FALSE); +} + + +void +leave_topl_mode(char *answer) { + unsigned char *ap, *bp; + + int ans_len = (*top_line)->teLength - topl_query_len; + NhWindow *aWin = theWindows + WIN_MESSAGE; + + if (!in_topl_mode()) + return; + + /* Cap length of reply */ + if (ans_len >= BUFSZ) + ans_len = BUFSZ-1; + + /* remove unprintables from the answer */ + for (ap = *(*top_line)->hText + topl_query_len, bp = answer; ans_len > 0; ans_len--, ap++) { + if (*ap >= ' ' && *ap < 128) { + *bp++ = *ap; + } + } + *bp = 0; + + if (aWin->windowTextLen && (*aWin->windowText)[aWin->windowTextLen-1] == CHAR_CR) { + -- aWin->windowTextLen; + -- aWin->y_size; + } + putstr(WIN_MESSAGE, ATR_BOLD, answer); + + (*top_line)->viewRect.left += 10000; + UndimMenuBar(); +} + +/* + * TESetSelect flushes out all the pending key strokes. I hate it. + */ +static void +topl_set_select(short selStart, short selEnd) { + TEDeactivate(top_line); + (*top_line)->selStart = selStart; + (*top_line)->selEnd = selEnd; + TEActivate(top_line); +} + + +static void +topl_replace(char *new_ans) { + topl_set_select(topl_query_len, (*top_line)->teLength); + TEDelete(top_line); + TEInsert(new_ans, strlen(new_ans), top_line); +} + + +Boolean +topl_key(unsigned char ch, Boolean ext) { + switch (ch) { + case CHAR_ESC: + topl_replace("\x1b"); + case CHAR_ENTER: case CHAR_CR: case CHAR_LF: + return false; + + case 0x1f & 'P': + mac_doprev_message(); + return true; + case '\x1e'/* up arrow */: + topl_replace (""); + return true; + case CHAR_BS: case '\x1c'/* left arrow */: + if ((*top_line)->selEnd <= topl_query_len) + return true; + else if (ext) { + topl_replace (""); + return true; + } + default: + TEKey(ch, top_line); + if (ext) { + int com_index = -1, oindex = 0; + while(extcmdlist[oindex].ef_txt != (char *)0) { + if(!strncmpi(*(*top_line)->hText + topl_query_len, + extcmdlist[oindex].ef_txt, + (*top_line)->teLength - topl_query_len)) { + if(com_index == -1) /* No matches yet*/ + com_index = oindex; + else /* More than 1 match */ { + com_index = -2; + break; + } + } + oindex++; + } + if(com_index >= 0) + topl_replace((char *) extcmdlist[com_index].ef_txt); + } + return true; + } +} + + +static void +topl_flash_resp(int resp_idx) { + unsigned long dont_care; + Rect frame; + SetPortWindowPort(theWindows[WIN_MESSAGE].its_window); + topl_resp_rect(resp_idx, &frame); + InsetRect(&frame, 1, 1); + InvertRect(&frame); + Delay(GetDblTime() / 2, &dont_care); + InvertRect(&frame); +} + + +static void +topl_set_def(int new_def_idx) { + Rect frame; + SetPortWindowPort(theWindows[WIN_MESSAGE].its_window); + topl_resp_rect(topl_def_idx, &frame); + InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame); + topl_def_idx = new_def_idx; + topl_resp_rect(new_def_idx, &frame); + InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame); +} + + +void +topl_set_resp(char *resp, char def) { + char *loc; + Rect frame; + int r_len, r_len1; + + if (!resp) { + const char any_str[2] = {CHAR_ANY, '\0'}; + resp = (char *) any_str; + def = CHAR_ANY; + } + + SetPortWindowPort(theWindows[WIN_MESSAGE].its_window); + r_len1 = strlen(resp); + r_len = strlen(topl_resp); + if (r_len < r_len1) + r_len = r_len1; + topl_resp_rect(0, &frame); + frame.right = (BTN_IND + BTN_W) * r_len; + InvalWindowRect(theWindows[WIN_MESSAGE].its_window, &frame); + + strcpy(topl_resp, resp); + loc = strchr (resp, def); + topl_def_idx = loc ? loc - resp : -1; +} + + +static char +topl_resp_key(char ch) { + if (strlen(topl_resp) > 0) { + char *loc = strchr(topl_resp, ch); + + if (!loc) { + if (ch == '\x9'/* tab */) { + topl_set_def(topl_def_idx<=0 ? strlen(topl_resp)-1 : topl_def_idx-1); + ch = '\0'; + } else if (ch == CHAR_ESC) { + loc = strchr(topl_resp, 'q'); + if (!loc) { + loc = strchr(topl_resp, 'n'); + if (!loc && topl_def_idx >= 0) + loc = topl_resp + topl_def_idx; + } + } else if (ch == (0x1f & 'P')) { + mac_doprev_message(); + ch = '\0'; + } else if (topl_def_idx >= 0) { + if (ch == CHAR_ENTER || ch == CHAR_CR || ch == CHAR_LF || + ch == CHAR_BLANK || topl_resp[topl_def_idx] == CHAR_ANY) + loc = topl_resp + topl_def_idx; + + else if (strchr(topl_resp, '#')) { + if (digit(ch)) { + topl_set_def(strchr(topl_resp, '#') - topl_resp); + TEKey(ch, top_line); + ch = '\0'; + + } else if (topl_resp[topl_def_idx] == '#') { + if (ch == '\x1e'/* up arrow */) { + topl_set_select(topl_query_len, topl_query_len); + ch = '\0'; + } else if (ch == '\x1d'/* right arrow */ || + ch == '\x1f'/* down arrow */ || + ch == CHAR_BS || ch == '\x1c'/* left arrow */ && + (*top_line)->selEnd > topl_query_len) { + TEKey(ch, top_line); + ch = '\0'; + } + } + } + } + } + + if (loc) { + topl_flash_resp(loc - topl_resp); + if (*loc != CHAR_ANY) + ch = *loc; + TEKey(ch, top_line); + } + } + + return ch; +} + + +static void +adjust_window_pos(NhWindow *aWin, short width, short height) +{ + WindowRef theWindow = aWin->its_window; +#if TARGET_API_MAC_CARBON + Rect r; + + + GetWindowBounds(theWindow, kWindowContentRgn, &r); + RetrieveWinPos(theWindow, &r.top, &r.left); + MoveWindow(theWindow, r.left, r.top, false); + SizeWindow(theWindow, width, height, true); + ConstrainWindowToScreen(theWindow, kWindowStructureRgn, + kWindowConstrainMoveRegardlessOfFit, NULL, NULL); +#else + Rect scr_r = (*GetGrayRgn())->rgnBBox; + const Rect win_ind = {2, 2, 3, 3}; + const short min_w = theWindow->portRect.right - theWindow->portRect.left, + max_w = scr_r.right - scr_r.left - win_ind.left - win_ind.right; + Point pos; + short max_h; + + SetPortWindowPort(theWindow); + if (!RetrieveWinPos(theWindow, &pos.v, &pos.h)) { + pos.v = 0; /* take window's existing position */ + pos.h = 0; + LocalToGlobal(&pos); + } + + max_h = scr_r.bottom - win_ind.bottom - pos.v; + if (height > max_h) height = max_h; + if (height < MIN_HEIGHT) height = MIN_HEIGHT; + if (width < min_w) width = min_w; + if (width > max_w) width = max_w; + SizeWindow(theWindow, width, height, true); + + if (pos.v + height + win_ind.bottom > scr_r.bottom) + pos.v = scr_r.bottom - height - win_ind.bottom; + if (pos.h + width + win_ind.right > scr_r.right) + pos.h = scr_r.right - width - win_ind.right; + MoveWindow(theWindow, pos.h, pos.v, false); + if (aWin->scrollBar) + DrawScrollbar (aWin); +#endif + return; +} + + +/* + * display/select/update the window. + * If f is true, this window should be "modal" - don't return + * until presumed seen. + */ +void +mac_display_nhwindow (winid win, BOOLEAN_P f) { + NhWindow *aWin = &theWindows [win]; + WindowPtr theWindow = aWin->its_window; + + if (win < 0 || win >= NUM_MACWINDOWS || !theWindow) { + error ("disp_win: Invalid window %d.", win); + return; + } + + if (theWindow == _mt_window) { + tty_display_nhwindow(win, f); + return; + } + + if (f && inSelect == WIN_ERR && win == WIN_MESSAGE) { + topl_set_resp ((char *)0, 0); + if (aWin->windowTextLen > 0 && + (*aWin->windowText) [aWin->windowTextLen - 1] == CHAR_CR) { + -- aWin->windowTextLen; + -- aWin->y_size; + } + putstr (win, flags.standout ? ATR_INVERSE : ATR_NONE, " --More--"); + } + + if (!IsWindowVisible(theWindow)) { + if (win != WIN_MESSAGE) + adjust_window_pos(aWin, aWin->x_size + SBARWIDTH+1, aWin->y_size *aWin->row_height); + + SelectWindow (theWindow); + ShowWindow (theWindow); + } + + if (f && inSelect == WIN_ERR) { + int ch; + + DimMenuBar(); + inSelect = win; + do { + ch = mac_nhgetch (); + } while (!ClosingWindowChar (ch)); + inSelect = WIN_ERR; + UndimMenuBar(); + + if (win == WIN_MESSAGE) + topl_set_resp ("", '\0'); + else + HideWindow (theWindow); + + } +} + + +void +mac_destroy_nhwindow (winid win) { + WindowPtr theWindow; + NhWindow *aWin = &theWindows [win]; + int kind; + + if (win < 0 || win >= NUM_MACWINDOWS) { + if (iflags.window_inited) error ("dest_win: Invalid win %d.", win); + return; + } + theWindow = aWin->its_window; + if (!theWindow) { + error ("dest_win: Not allocated win %d.", win); + return; + } + + /* + * Check special windows. The base window should never go away. + * Other "standard" windows should not go away unless we've exitted nhwindows. + */ + if (theWindow == _mt_window) { + return; + } + if (win == WIN_INVEN || win == WIN_MESSAGE) { + if (iflags.window_inited) { + if (flags.tombstone && killer) { + /* Prepare for the coming of the tombstone window. */ + win_fonts [NHW_TEXT] = kFontIDMonaco; + } + return; + } + if (win == WIN_MESSAGE) + WIN_MESSAGE = WIN_ERR; + } + + kind = GetWindowKind(theWindow) - WIN_BASE_KIND; + + if ((!IsWindowVisible(theWindow) || (kind != NHW_MENU && kind != NHW_TEXT))) { + DisposeWindow (theWindow); + if (aWin->windowText) { + DisposeHandle(aWin->windowText); + } + aWin->its_window = (WindowPtr) 0; + aWin->windowText = (Handle) 0; + } +} + + +void +mac_number_pad (int pad) { + iflags.num_pad = pad; +} + + +void +trans_num_keys(EventRecord *theEvent) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(theEvent) +#endif +/* KMH -- Removed this translation. + * Number pad keys should always emit digit characters. + * That's consistent with the default MacOS behavior. + * The number_pad option controls how digits are interpreted. + */ +#if 0 + if (iflags.num_pad) { + Handle h = GetResource('Nump', theEvent->modifiers & shiftKey ? 129 : 128); + if (h) { + short inkey = (theEvent->message & keyCodeMask), *ab = (short *)*h; + int i = ab[0]; + for (; i; i--) { + if (inkey == (ab[i] & keyCodeMask)) { + theEvent->message = ab[i]; + break; + } + } + } + } +#endif +} + + +/* + * Note; theWindow may very well be null here, since keyDown may call + * it when theres no window !!! + */ +static void +GeneralKey (EventRecord *theEvent, WindowPtr theWindow) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(theWindow) +#endif +#if 0 + trans_num_keys (theEvent); +#endif + AddToKeyQueue (topl_resp_key (theEvent->message & 0xff), TRUE); +} + + +/* + * Routine used to select and de-select elements in a menu window, used by KeyMenu, + * ClickMenu, and UpdateMenu. Takes the NhWindow and a line ref relative to the scrollbar. + */ +static void ToggleMenuSelect (NhWindow *aWin, int line) { + Rect r; + + + GetWindowBounds(aWin->its_window, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + if (aWin->scrollBar) + r.right -= SBARWIDTH; + r.top = line * aWin->row_height; + r.bottom = r.top + aWin->row_height; + + LMSetHiliteMode((UInt8) (LMGetHiliteMode() & 0x7F)); + InvertRect(&r); +} + +/* + * Check to see if given item is selected, return index if it is + */ +static int +ListItemSelected (NhWindow *aWin, int item) { + int i; + + HLock ((char**)aWin->menuSelected); + /* Find item in selection list */ + for (i = aWin->miSelLen - 1; i >= 0; i--) { + if ((*aWin->menuSelected) [i] == item) + break; + } + HUnlock ((char**)aWin->menuSelected); + return i; +} + +/* + * Add item to selection list if it's not selected already + * If it is selected already, remove it from the list. + */ +static void +ToggleMenuListItemSelected (NhWindow *aWin, short item) { + int i = ListItemSelected (aWin, item); + + HLock ((char**)aWin->menuSelected); + if (i < 0) { /* not there, so add */ + (*aWin->menuSelected) [aWin->miSelLen] = item; + aWin->miSelLen++; + } + else { /* there, so remove */ + short *mi = &(*aWin->menuSelected)[i]; + aWin->miSelLen --; + memcpy (mi, mi + 1, (aWin->miSelLen - i)*sizeof(short)); + } + HUnlock ((char**)aWin->menuSelected); +} + + +/* + * Find menu item in list given a line number on the window + */ +static short +ListCoordinateToItem (NhWindow *aWin, short Row) { + int i, item = -1; + MacMHMenuItem * mi; + + HLock ((char**)aWin->menuInfo); + for (i = 0, mi = *aWin->menuInfo; i < aWin->miLen; i++, mi++) { + if (mi->line == Row + aWin->scrollPos) { + item = i; + break; + } + } + HUnlock ((char**)aWin->menuInfo); + return item; +} + + +static void +macKeyMenu (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *aWin = GetNhWin(theWindow); + MacMHMenuItem *mi; + int l, ch = theEvent->message & 0xff; + + if (aWin && aWin->menuInfo) { + HLock ((char**)aWin->menuInfo); + for (l = 0, mi = *aWin->menuInfo; l < aWin->miLen; l++, mi++) { + if (mi->accelerator == ch) { + ToggleMenuListItemSelected (aWin, l); + if (mi->line >= aWin->scrollPos && mi->line <= aWin->y_size) { + SetPortWindowPort(theWindow); + ToggleMenuSelect (aWin, mi->line - aWin->scrollPos); + } + /* Dismiss window if only picking one item */ + if (aWin->how != PICK_ANY) + AddToKeyQueue(CHAR_CR, 1); + break; + } + } + HUnlock ((char**)aWin->menuInfo); + /* add key if didn't find it in menu and not filtered */ + if (l == aWin->miLen && filter_scroll_key (ch, aWin)) + GeneralKey (theEvent, theWindow); + } +} + + +static void +macClickMenu (EventRecord *theEvent, WindowRef theWindow) { + Point p; + NhWindow *aWin = GetNhWin(theWindow); + Rect wrect; + + + GetWindowBounds(theWindow, kWindowContentRgn, &wrect); + OffsetRect(&wrect, -wrect.left, -wrect.top); + if (aWin->scrollBar && IsControlVisible(aWin->scrollBar)) { + short code; + ControlHandle theBar; + + p = theEvent->where; + GlobalToLocal (&p); + code = FindControl (p, theWindow, &theBar); + if (code) { + DoScrollBar (p, code, theBar, aWin); + return; + } + } + if (inSelect != WIN_ERR && aWin->how != PICK_NONE) { + short currentRow = -1, previousRow = -1; + short previousItem = -1, item = -1; + Boolean majorSelectState, firstRow = TRUE; + + do { +#if !TARGET_API_MAC_CARBON + SystemTask (); +#endif + GetMouse (&p); + currentRow = p.v / aWin->row_height; + if (p.h < wrect.left || p.h > wrect.right || + p.v < 0 || p.v > wrect.bottom || currentRow >= aWin->y_size) { + continue; /* not in window range */ + } + + item = ListCoordinateToItem (aWin, currentRow); + + if (item != previousItem) { + /* Implement typical Mac multiple-selection behavior + * (ie, not the UI implemented by the Finder) + */ + Boolean itemIsSelected = (ListItemSelected (aWin,item) >= 0); + + if (firstRow) { + /* this is first valid row, so major state is opposite of what this row is */ + majorSelectState = !itemIsSelected; + firstRow = FALSE; + } + + if (aWin->how == PICK_ONE && previousItem != -1) { + /* if previous row was selected and we're only selecting one object, + * deselect previous row! + */ + ToggleMenuListItemSelected (aWin, previousItem); + ToggleMenuSelect (aWin, previousRow); + previousItem = -1; + } + + if (item == -1) + continue; /* header line */ + + if (majorSelectState != itemIsSelected) { + ToggleMenuListItemSelected (aWin, item); + ToggleMenuSelect (aWin, currentRow); + } + + previousRow = currentRow; + previousItem = item; + } + } while (StillDown ()); + + /* Dismiss window if only picking one item */ + if (aWin->how == PICK_ONE) + AddToKeyQueue(CHAR_CR, 1); + } +} + + +static void +macKeyText (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *aWin = GetNhWin (theWindow); + char c = filter_scroll_key (theEvent->message & 0xff, aWin); + if (c) { + if (inSelect == WIN_ERR && ClosingWindowChar (c)) { + HideWindow (theWindow); + mac_destroy_nhwindow (aWin - theWindows); + } else { + GeneralKey (theEvent, theWindow); + } + } +} + + +static void +macClickText (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *aWin = GetNhWin (theWindow); + + if (aWin->scrollBar && IsControlVisible(aWin->scrollBar)) { + short code; + Point p = theEvent->where; + ControlHandle theBar; + + GlobalToLocal (&p); + code = FindControl (p, theWindow, &theBar); + if (code) { + DoScrollBar (p, code, theBar, aWin); + } + } +} + + +static void +macClickMessage (EventRecord *theEvent, WindowPtr theWindow) { + int r_idx = 0; + Point mouse = theEvent->where; + + GlobalToLocal(&mouse); + while (topl_resp[r_idx]) { + Rect frame; + topl_resp_rect(r_idx, &frame); + InsetRect(&frame, 1, 1); + if (PtInRect(mouse, &frame)) { + Boolean in_btn = true; + + InvertRect(&frame); + while (WaitMouseUp()) { +#if !TARGET_API_MAC_CARBON + SystemTask(); +#endif + GetMouse(&mouse); + if (PtInRect(mouse, &frame) != in_btn) { + in_btn = !in_btn; + InvertRect(&frame); + } + } + if (in_btn) { + InvertRect(&frame); + AddToKeyQueue (topl_resp [r_idx], 1); + } + return; + + } + ++r_idx; + } + + macClickText(theEvent, theWindow); +} + + +static void +macClickTerm (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *nhw = GetNhWin(theWindow); + Point where = theEvent->where; + + GlobalToLocal(&where); + where.h = where.h / nhw->char_width + 1; + where.v = where.v / nhw->row_height; + clicked_mod = (theEvent->modifiers & shiftKey) ? CLICK_2 : CLICK_1; + + if (strchr(topl_resp, *click_to_cmd(where.h, where.v, clicked_mod))) + nhbell(); + else { +#if !TARGET_API_MAC_CARBON + if (cursor_locked) + while (WaitMouseUp()) + SystemTask(); +#endif + + gClickedToMove = TRUE; + clicked_pos = where; + } +} + +static pascal void +MoveScrollBar (ControlHandle theBar, short part) { + EventRecord fake; + Rect r; + RgnHandle rgn; + int now, amtToScroll; + WindowPtr theWin; + NhWindow *winToScroll; + + if (!part) + return; + + theWin = GetControlOwner(theBar); + GetWindowBounds(theWin, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + winToScroll = (NhWindow*)(GetWRefCon(theWin)); + now = GetControlValue (theBar); + + if (part == kControlPageUpPart || part == kControlPageDownPart) + amtToScroll = (r.bottom - r.top) / winToScroll->row_height; + else + amtToScroll = 1; + + if (part == kControlPageUpPart || part == kControlUpButtonPart) { + int bound = GetControlMinimum (theBar); + if (now - bound < amtToScroll) + amtToScroll = now - bound; + amtToScroll = -amtToScroll; + } else { + int bound = GetControlMaximum (theBar); + if (bound - now < amtToScroll) + amtToScroll = bound - now; + } + + if (!amtToScroll) + return; + + SetControlValue (theBar, now + amtToScroll); + winToScroll->scrollPos = now + amtToScroll; + r.right -= SBARWIDTH; + if (winToScroll == theWindows + WIN_MESSAGE) + r.bottom -= SBARHEIGHT; + rgn = NewRgn (); + ScrollRect (&r, 0, -amtToScroll * winToScroll->row_height, rgn); + if (rgn) { + InvalWindowRgn(theWin, rgn); + BeginUpdate(theWin); + } + winUpdateFuncs [GetWindowKind(theWin) - WIN_BASE_KIND] (&fake, theWin); + if (rgn) { + EndUpdate(theWin); + DisposeRgn(rgn); + } +} + + +static void +DoScrollBar (Point p, short code, ControlHandle theBar, NhWindow *aWin) +{ + ControlActionUPP func = NULL; + Rect rect; + + if (code == kControlUpButtonPart || code == kControlPageUpPart || + code == kControlDownButtonPart || code == kControlPageDownPart) + func = MoveScrollUPP; + (void) TrackControl(theBar, p, func); + if (!func) { + if (aWin->scrollPos != GetControlValue (theBar)) { + aWin->scrollPos = GetControlValue (theBar); + GetWindowBounds(aWin->its_window, kWindowContentRgn, &rect); + OffsetRect(&rect, -rect.left, -rect.top); + InvalWindowRect(aWin->its_window, &rect); + } + } +} + + +static int +filter_scroll_key(const int ch, NhWindow *aWin) { + if (aWin->scrollBar && GetControlValue(aWin->scrollBar) < GetControlMaximum(aWin->scrollBar)) { + short part = 0; + if (ch == CHAR_BLANK) { + part = kControlPageDownPart; + } + else if (ch == CHAR_CR || ch == CHAR_LF) { + part = kControlDownButtonPart; + } + if (part) { + SetPortWindowPort(aWin->its_window); + MoveScrollBar(aWin->scrollBar, part); + return 0; + } + } + return ch; +} + + +int +mac_doprev_message(void) { + if (WIN_MESSAGE) { + NhWindow *winToScroll = &theWindows[WIN_MESSAGE]; + mac_display_nhwindow(WIN_MESSAGE, FALSE); + SetPortWindowPort(winToScroll->its_window); + MoveScrollBar(winToScroll->scrollBar, kControlUpButtonPart); + } + return 0; +} + + +static short +macDoNull (EventRecord *theEvent, WindowPtr theWindow) { + return 0; +} + + +static void +draw_growicon_vert_only(WindowPtr wind) +{ + GrafPtr org_port; + RgnHandle org_clip = NewRgn(); + Rect r; + + GetPort(&org_port); + SetPortWindowPort(wind); + GetClip(org_clip); + GetWindowBounds(wind, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + r.left = r.right - SBARWIDTH; + ClipRect(&r); + DrawGrowIcon(wind); + SetClip(org_clip); + DisposeRgn(org_clip); + SetPort(org_port); +} + + +static short +macUpdateMessage (EventRecord *theEvent, WindowPtr theWindow) +{ + RgnHandle org_clip = NewRgn(), clip = NewRgn(); + Rect r; + NhWindow *aWin = GetNhWin(theWindow); + int l; + + if (!theEvent) + return 0; + + GetClip(org_clip); + GetWindowBounds(theWindow, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + + DrawControls(theWindow); + DrawGrowIcon(theWindow); + + for (l = 0; topl_resp[l]; l++) { + StringPtr name; + unsigned char tmp[2]; + FontInfo font; + Rect frame; + topl_resp_rect(l, &frame); + switch (topl_resp[l]) { + case 'y': + name = "\pyes"; + break; + case 'n': + name = "\pno"; + break; + case 'N': + name = "\pNone"; + break; + case 'a': + name = "\pall"; + break; + case 'q': + name = "\pquit"; + break; + case CHAR_ANY: + name = "\pany key"; + break; + default: + tmp[0] = 1; + tmp[1] = topl_resp[l]; + name = tmp; + break; + } + TextFont(kFontIDGeneva); + TextSize(9); + GetFontInfo(&font); + MoveTo ((frame.left + frame.right - StringWidth(name)) / 2, + (frame.top + frame.bottom + font.ascent-font.descent-font.leading-1) / 2); + DrawString(name); + PenNormal(); + if (l == topl_def_idx) + PenSize(2, 2); + FrameRoundRect(&frame, 4, 4); + } + + r.right -= SBARWIDTH; + r.bottom -= SBARHEIGHT; + /* Clip to the portrect - scrollbar/growicon *before* adjusting the rect + to be larger than the size of the window (!) */ + RectRgn(clip, &r); + SectRgn(clip, org_clip, clip); + if (r.right < MIN_RIGHT) + r.right = MIN_RIGHT; + r.top -= aWin->scrollPos * aWin->row_height; + +#if 0 + /* If you enable this band of code (and disable the next band), you will get + fewer flickers but a slower performance while drawing the dot line. */ + { RgnHandle dotl_rgn = NewRgn(); + Rect dotl; + dotl.left = r.left; + dotl.right = r.right; + dotl.bottom = r.top + aWin->save_lin * aWin->row_height; + dotl.top = dotl.bottom - 1; + FillRect(&dotl, &qd.gray); + RectRgn(dotl_rgn, &dotl); + DiffRgn(clip, dotl_rgn, clip); + DisposeRgn(dotl_rgn); + SetClip(clip); + } +#endif + + if (in_topl_mode()) { + RgnHandle topl_rgn = NewRgn(); + Rect topl_r = r; + topl_r.top += (aWin->y_size - 1) * aWin->row_height; + l = (*top_line)->destRect.right - (*top_line)->destRect.left; + (*top_line)->viewRect = topl_r; + (*top_line)->destRect = topl_r; + if (l != topl_r.right - topl_r.left) + TECalText(top_line); + TEUpdate(&topl_r, top_line); + RectRgn(topl_rgn, &topl_r); + DiffRgn(clip, topl_rgn, clip); + DisposeRgn(topl_rgn); + SetClip(clip); + } + + DisposeRgn(clip); + + TextFont (aWin->font_number); + TextSize (aWin->font_size); + HLock (aWin->windowText); + TETextBox (*aWin->windowText, aWin->windowTextLen, &r, teJustLeft); + HUnlock (aWin->windowText); + +#if !TARGET_API_MAC_CARBON + r.bottom = r.top + aWin->save_lin * aWin->row_height; + r.top = r.bottom - 1; + FillRect(&r, (void *) &qd.gray); +#endif + + SetClip(org_clip); + DisposeRgn(org_clip); + return 0; +} + + +static short +macUpdateMenu (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *aWin = GetNhWin (theWindow); + int i, line; + MacMHMenuItem *mi; + + GeneralUpdate (theEvent, theWindow); + HLock ((char**)aWin->menuInfo); + HLock ((char**)aWin->menuSelected); + for (i = 0; i < aWin->miSelLen; i++) { + mi = &(*aWin->menuInfo) [(*aWin->menuSelected) [i]]; + line = mi->line; + if (line > aWin->scrollPos && line <= aWin->y_size) + ToggleMenuSelect (aWin, line - aWin->scrollPos); + } + HUnlock ((char**)aWin->menuInfo); + HUnlock ((char**)aWin->menuSelected); + return 0; +} + + +static short +GeneralUpdate (EventRecord *theEvent, WindowPtr theWindow) { + Rect r, r2; + NhWindow *aWin = GetNhWin (theWindow); + RgnHandle h; + Boolean vis; + + + if (!theEvent) + return 0; + + GetWindowBounds(theWindow, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + r2 = r; + r2.left = r2.right - SBARWIDTH; + r2.right += 1; + r2.top -= 1; + vis = (r2.bottom > r2.top + 50); + + draw_growicon_vert_only(theWindow); + DrawControls (theWindow); + + h = (RgnHandle) 0; + if (vis && (h = NewRgn ())) { + RgnHandle tmp = NewRgn (); + if (!tmp) { + DisposeRgn (h); + h = (RgnHandle) 0; + } else { + GetClip (h); + RectRgn (tmp, &r2); + DiffRgn (h, tmp, tmp); + SetClip (tmp); + DisposeRgn (tmp); + } + } + if (r.right < MIN_RIGHT) + r.right = MIN_RIGHT; + r.top -= aWin->scrollPos * aWin->row_height; + r.right -= SBARWIDTH; + HLock (aWin->windowText); + TETextBox (*aWin->windowText, aWin->windowTextLen, &r, teJustLeft); + HUnlock (aWin->windowText); + if (h) { + SetClip (h); + DisposeRgn (h); + } + return 0; +} + + +static void +macCursorTerm (EventRecord *theEvent, WindowPtr theWindow, RgnHandle mouseRgn) { + char *dir_bas, *dir; + CursHandle ch; + GrafPtr gp; + NhWindow *nhw = GetNhWin (theWindow); + Rect r = {0, 0, 1, 1}; + + GetPort (&gp); + SetPortWindowPort(theWindow); + + if (cursor_locked) + dir = (char *)0; + else { + Point where = theEvent->where; + + GlobalToLocal (&where); + dir_bas = iflags.num_pad ? (char *) ndir : (char *) sdir; + dir = strchr (dir_bas, *click_to_cmd (where.h / nhw->char_width + 1 , + where.v / nhw->row_height, CLICK_1)); + } + ch = GetCursor (dir ? dir - dir_bas + 513 : 512); + if (ch) { + HLock ((Handle) ch); + SetCursor (*ch); + HUnlock ((Handle) ch); + + } else { + SetCursor(&qdarrow); + } + OffsetRect (&r, theEvent->where.h, theEvent->where.v); + RectRgn (mouseRgn, &r); + SetPort (gp); +} + + +static void +GeneralCursor (EventRecord *theEvent, WindowPtr theWindow, RgnHandle mouseRgn) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(theWindow) +#endif + Rect r = {-1, -1, 2, 2}; + + SetCursor(&qdarrow); + OffsetRect (&r, theEvent->where.h, theEvent->where.v); + RectRgn (mouseRgn, &r); +} + + +static void +HandleKey (EventRecord *theEvent) { + WindowPtr theWindow = FrontWindow (); + + if (theEvent->modifiers & cmdKey) { + if (theEvent->message & 0xff == '.') { + /* Flush key queue */ + keyQueueCount = keyQueueWrite = keyQueueRead = 0; + theEvent->message = '\033'; + goto dispatchKey; + } else { + UndimMenuBar (); + DoMenuEvt (MenuKey (theEvent->message & 0xff)); + } + } else { + +dispatchKey : + if (theWindow) { + int kind = GetWindowKind(theWindow) - WIN_BASE_KIND; + winKeyFuncs [kind] (theEvent, theWindow); + } else { + GeneralKey (theEvent, (WindowPtr) 0); + } + } +} + + +static void +WindowGoAway (EventRecord *theEvent, WindowPtr theWindow) { + NhWindow *aWin = GetNhWin(theWindow); + + if (!theEvent || TrackGoAway (theWindow, theEvent->where)) { + if (aWin - theWindows == BASE_WINDOW && !iflags.window_inited) { + AddToKeyQueue ('\033', 1); + } else { + HideWindow (theWindow); + if (aWin - theWindows != inSelect) + mac_destroy_nhwindow (aWin - theWindows); + else /* if this IS the inSelect window put a close char */ + AddToKeyQueue (CHAR_CR, 1); /* in queue to exit and maintain inSelect */ + } + } +} + + +static void +HandleClick (EventRecord *theEvent) { + int code; + unsigned long l; + WindowPtr theWindow; + NhWindow *aWin; + Rect r; + Boolean not_inSelect; + + InsetRect(GetRegionBounds(GetGrayRgn(), &r), 4, 4); + + code = FindWindow (theEvent->where, &theWindow); + aWin = GetNhWin (theWindow); + not_inSelect = (inSelect == WIN_ERR || aWin - theWindows == inSelect); + + switch (code) { + case inContent : + if (not_inSelect) { + int kind = GetWindowKind(theWindow) - WIN_BASE_KIND; + winCursorFuncs [kind] (theEvent, theWindow, gMouseRgn); + SelectWindow (theWindow); + SetPortWindowPort(theWindow); + winClickFuncs [kind] (theEvent, theWindow); + } else { + nhbell (); + } + break; + + case inDrag : + if (not_inSelect) { + SetCursor(&qdarrow); + DragWindow (theWindow, theEvent->where, &r); + SaveWindowPos(theWindow); + } else { + nhbell (); + } + break; + + case inGrow : + if (not_inSelect) { + SetCursor(&qdarrow); + SetRect (&r, 80, 2 * aWin->row_height + 1, r.right, r.bottom); + if (aWin == theWindows + WIN_MESSAGE) + r.top += SBARHEIGHT; + l = GrowWindow (theWindow, theEvent->where, &r); + SizeWindow (theWindow, l & 0xffff, l >> 16, FALSE); + SaveWindowSize(theWindow); + SetPortWindowPort(theWindow); + GetWindowBounds(theWindow, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + InvalWindowRect(theWindow, &r); + if (aWin->scrollBar) { + DrawScrollbar (aWin); + } + } else { + nhbell (); + } + break; + + case inGoAway : + WindowGoAway(theEvent, theWindow); + break; + + case inMenuBar : + DoMenuEvt (MenuSelect (theEvent->where)); + break; + +#if !TARGET_API_MAC_CARBON + case inSysWindow : + SystemClick(theEvent, theWindow); +#endif + default : + break; + } +} + + +static void +HandleUpdate (EventRecord *theEvent) { + WindowPtr theWindow = (WindowPtr) theEvent->message; + NhWindow *aWin = GetNhWin (theWindow); + Rect r; + + + char existing_update_region = FALSE; + Rect rect; + + if (theWindow == _mt_window) { + existing_update_region = (get_invalid_region (theWindow, &rect) == noErr); + } + BeginUpdate (theWindow); + SetPortWindowPort(theWindow); + GetWindowBounds(theWindow, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + EraseRect(&r); + winUpdateFuncs [GetWindowKind(theWindow) - WIN_BASE_KIND] + (theEvent, theWindow); + + if (theWindow == _mt_window && existing_update_region) { + set_invalid_region (theWindow, &rect); + } + aWin->drawn = TRUE; + EndUpdate (theWindow); +} + + +static void +DoOsEvt (EventRecord *theEvent) { + WindowRef win; + short code; + + if ((theEvent->message & 0xff000000) == 0xfa000000) { + /* Mouse Moved */ + + code = FindWindow (theEvent->where, &win); + if (code != inContent) { + Rect r = {-1, -1, 2, 2}; + + SetCursor(&qdarrow); + OffsetRect (&r, theEvent->where.h, theEvent->where.v); + RectRgn (gMouseRgn, &r); + } else { + int kind = GetWindowKind(win) - WIN_BASE_KIND; + if (kind >= 0 && kind <= NHW_TEXT) { + winCursorFuncs [kind] (theEvent, win, gMouseRgn); + } + } + } +} + + +void +HandleEvent (EventRecord *theEvent) { + switch (theEvent->what) { + case autoKey : + case keyDown : + HandleKey (theEvent); + break; + case updateEvt : + HandleUpdate (theEvent); + break; + case mouseDown : + HandleClick (theEvent); + break; +#if !TARGET_API_MAC_CARBON + case diskEvt : + if ((theEvent->message & 0xffff0000) != 0) { + Point p = {150, 150}; + (void) DIBadMount (p, theEvent->message); + } + break; +#endif + case osEvt : + DoOsEvt (theEvent); + break; + case kHighLevelEvent: + AEProcessAppleEvent(theEvent); + default : + break; + } +} + + +void +mac_get_nh_event(void) { + EventRecord anEvent; + + /* KMH -- Don't proceed if the window system isn't set up */ + if (!iflags.window_inited) + return; + + (void) WaitNextEvent (everyEvent, &anEvent, -1, gMouseRgn); + HandleEvent(&anEvent); +} + + +int +mac_nhgetch(void) { + int ch; + long doDawdle; + EventRecord anEvent; + + /* We want to take care of keys in the buffer as fast as + * possible + */ + if (keyQueueCount) + doDawdle = 0L; + else { + long total, contig; + static char warn = 0; + + doDawdle = (in_topl_mode() ? GetCaretTime () : 120L); + /* Since we have time, check memory */ + PurgeSpace (&total, &contig); + if (contig < 25000L || total < 50000L) { + if (!warn) { + pline ("Low Memory!"); + warn = 1; + } + } else { + warn = 0; + } + } + + do { + (void) WaitNextEvent (everyEvent, &anEvent, doDawdle, gMouseRgn); + HandleEvent (&anEvent); + ch = GetFromKeyQueue (); + } while (!ch && !gClickedToMove); + + if (!gClickedToMove) + ObscureCursor (); + else + gClickedToMove = 0; + +#ifdef THINK_C + if (ch == '\r') ch = '\n'; +#endif + + return ch; +} + + +void +mac_delay_output(void) { + long destTicks = TickCount () + 1; + + while (TickCount () < destTicks) { + mac_get_nh_event (); + } +} + + +#ifdef CLIPPING +static void +mac_cliparound (int x, int y) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(x,y) +#endif + /* TODO */ +} +#endif + +void +mac_exit_nhwindows (const char *s) { + clear_screen (); + tty_exit_nhwindows (s); + mac_destroy_nhwindow (WIN_MESSAGE); + mac_destroy_nhwindow (WIN_INVEN); +} + + +/* + * Don't forget to decrease in_putstr before returning... + */ +void +mac_putstr (winid win, int attr, const char *str) { + long len, slen; + NhWindow *aWin = &theWindows [win]; + static char in_putstr = 0; + short newWidth, maxWidth; + Rect r; + char *src, *sline, *dst, ch; + + if (win < 0 || win >= NUM_MACWINDOWS || !aWin->its_window) { + error ("putstr: Invalid win %d (Max %d).", win, NUM_MACWINDOWS, attr); + return; + } + + if (aWin->its_window == _mt_window) { + tty_putstr(win, attr, str); + return; + } + + if (in_putstr > 3) + return; + + in_putstr ++; + slen = strlen (str); + + SetPortWindowPort(aWin->its_window); + GetWindowBounds(aWin->its_window, kWindowContentRgn, &r); + OffsetRect(&r, -r.left, -r.top); + if (win == WIN_MESSAGE) { + r.right -= SBARWIDTH; + r.bottom -= SBARHEIGHT; + if (flags.page_wait && + aWin->last_more_lin <= aWin->y_size - (r.bottom - r.top) / aWin->row_height) { + aWin->last_more_lin = aWin->y_size; + mac_display_nhwindow(win, TRUE); + } + } + + /* + * A "default" text window - uses TETextBox + * We just add the text, without attributes for now + */ + len = GetHandleSize (aWin->windowText); + while (aWin->windowTextLen + slen + 1 > len) { + len = (len > 2048) ? (len + 2048) : (len * 2); + SetHandleSize (aWin->windowText, len); + if (MemError ()) { + error ("putstr: SetHandleSize"); + aWin->windowTextLen = 0L; + aWin->save_lin = 0; + aWin->y_curs = 0; + aWin->y_size = 0; + } + } + + len = aWin->windowTextLen; + dst = *(aWin->windowText) + len; + sline = src = (char *)str; + maxWidth = newWidth = 0; + for (ch = *src; ch; ch = *src) { + if (ch == CHAR_LF) + ch = CHAR_CR; + *dst++ = ch; + if (ch == CHAR_CR) { + aWin->y_curs ++; + aWin->y_size ++; + aWin->x_curs = 0; + newWidth = TextWidth (sline, 0, src - sline); + if (newWidth > maxWidth) { + maxWidth = newWidth; + } + sline = src+1; /* keep track of where new line begins */ + } + else + aWin->x_curs ++; + src++; + } + + newWidth = TextWidth (sline, 0, src - sline); + if (newWidth > maxWidth) { + maxWidth = newWidth; + } + + aWin->windowTextLen += slen; + + if (ch != CHAR_CR) { + (*(aWin->windowText)) [len + slen] = CHAR_CR; + aWin->windowTextLen ++; + aWin->y_curs ++; + aWin->y_size ++; + aWin->x_curs = 0; + } + + if (win == WIN_MESSAGE) { + short min = aWin->y_size - (r.bottom - r.top) / aWin->row_height; + if (aWin->scrollPos < min) { + aWin->scrollPos = min; + SetControlMaximum (aWin->scrollBar, aWin->y_size); + SetControlValue(aWin->scrollBar, min); + } + InvalWindowRect(aWin->its_window, &r); + } + else /* Message has a fixed width, other windows base on content */ + if (maxWidth > aWin->x_size) + aWin->x_size = maxWidth; + in_putstr --; +} + + +void +mac_curs (winid win, int x, int y) { + NhWindow *aWin = &theWindows [win]; + + if (aWin->its_window == _mt_window) { + tty_curs(win, x, y); + return; + } + + SetPortWindowPort(aWin->its_window); + MoveTo (x * aWin->char_width, (y * aWin->row_height) + aWin->ascent_height); + aWin->x_curs = x; + aWin->y_curs = y; +} + + +int +mac_nh_poskey (int *a, int *b, int *c) { + int ch = mac_nhgetch(); + *a = clicked_pos.h; + *b = clicked_pos.v; + *c = clicked_mod; + return ch; +} + + +void +mac_start_menu (winid win) { + HideWindow (theWindows [win].its_window); + mac_clear_nhwindow (win); +} + + +void +mac_add_menu (winid win, int glyph, const anything *any, CHAR_P menuChar, CHAR_P groupAcc, int attr, const char *inStr, int preselected) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(glyph) +#endif + NhWindow *aWin = &theWindows [win]; + const char *str; + char locStr[4+BUFSZ]; + MacMHMenuItem *item; + + if (!inStr) return; + + if (any->a_void != 0) { + +#define kMenuSizeBump 26 + if (!aWin->miSize) { + aWin->menuInfo = (MacMHMenuItem **)NewHandle(sizeof(MacMHMenuItem) * kMenuSizeBump); + if (!aWin->menuInfo) { + error("Can't alloc menu handle"); + return; + } + aWin->menuSelected = (short **)NewHandle(sizeof(short) * kMenuSizeBump); + if (!aWin->menuSelected) { + error("Can't alloc menu select handle"); + return; + } + aWin->miSize = kMenuSizeBump; + } + + if (aWin->miLen >= aWin->miSize) { + SetHandleSize((Handle)aWin->menuInfo, sizeof(MacMHMenuItem) * (aWin->miLen+kMenuSizeBump)); + if (MemError()) { + error("Can't resize menu handle"); + return; + } + SetHandleSize((Handle)aWin->menuSelected, sizeof(short) * (aWin->miLen+kMenuSizeBump)); + if (MemError()) { + error("Can't resize menu select handle"); + return; + } + aWin->miSize += kMenuSizeBump; + } + + if (menuChar == 0) { + if (('a' <= aWin->menuChar && aWin->menuChar <= 'z') || + ('A' <= aWin->menuChar && aWin->menuChar <= 'Z')) { + menuChar = aWin->menuChar++; + if (menuChar == 'z') + aWin->menuChar = 'A'; + } + } + + Sprintf(locStr, "%c - %s", (menuChar ? menuChar : ' '), inStr); + str = locStr; + HLock ((char**)aWin->menuInfo); + HLock ((char**)aWin->menuSelected); + (*aWin->menuSelected)[aWin->miLen] = preselected; + item = &(*aWin->menuInfo)[aWin->miLen]; + aWin->miLen++; + item->id = *any; + item->accelerator = menuChar; + item->groupAcc = groupAcc; + item->line = aWin->y_size; + HUnlock ((char**)aWin->menuInfo); + HUnlock ((char**)aWin->menuSelected); + } else + str = inStr; + + putstr (win, attr, str); +} + + +/* + * End a menu in this window, window must a type NHW_MENU. + * str is a list of cancel characters (values that may be input) + * morestr is a prompt to display, rather than the default. + * str and morestr might be ignored by some ports. + */ +void +mac_end_menu (winid win, const char *morestr) { + Str255 buf; + NhWindow *aWin = &theWindows [win]; + + buf [0] = 0; + if (morestr) + C2P (morestr, buf); + SetWTitle (aWin->its_window, buf); +} + + +int +mac_select_menu (winid win, int how, menu_item **selected_list) { + int c; + NhWindow *aWin = &theWindows [win]; + WindowPtr theWin = aWin->its_window; + + inSelect = win; + + mac_display_nhwindow (win, FALSE); + + aWin->how = (short) how; + for (;;) { + c = map_menu_cmd (mac_nhgetch()); + if (c == CHAR_ESC) { + /* deselect everything */ + aWin->miSelLen = 0; + break; + } else if (ClosingWindowChar(c)) { + break; + } else { + nhbell(); + } + } + + HideWindow (theWin); + + if (aWin->miSelLen) { + menu_item *mp; + MacMHMenuItem *mi; + *selected_list = mp = (menu_item *) alloc(aWin->miSelLen * sizeof(menu_item)); + HLock ((char**)aWin->menuInfo); + HLock ((char**)aWin->menuSelected); + for (c = 0; c < aWin->miSelLen; c++) { + mi = &(*aWin->menuInfo)[(*aWin->menuSelected) [c]]; + mp->item = mi->id; + mp->count = -1L; + mp++; + } + HUnlock ((char**)aWin->menuInfo); + HUnlock ((char**)aWin->menuSelected); + } else + *selected_list = 0; + + inSelect = WIN_ERR; + + return aWin->miSelLen; +} + +#include "dlb.h" + +static void +mac_display_file (name, complain) +const char *name; /* not ANSI prototype because of boolean parameter */ +boolean complain; +{ + Ptr buf; + int win; + dlb *fp = dlb_fopen(name, "r"); + + if (fp) { + long l = dlb_fseek(fp, 0, SEEK_END); + (void) dlb_fseek(fp, 0, 0L); + buf = NewPtr(l+1); + if (buf) { + l = dlb_fread(buf, 1, l, fp); + if (l > 0) { + buf[l] = '\0'; + win = create_nhwindow(NHW_TEXT); + if (WIN_ERR == win) { + if (complain) error ("Cannot make window."); + } else { + putstr(win, 0, buf); + display_nhwindow(win, FALSE); + } + } + DisposePtr(buf); + } + dlb_fclose(fp); + } else if (complain) + error("Cannot open %s.", name); +} + + +void +port_help () { + display_file (PORT_HELP, TRUE); +} + + +static void +mac_unimplemented (void) { +} + + +static void +mac_suspend_nhwindows (const char *foo) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(foo) +#endif + /* Can't really do that :-) */ +} + + +int +try_key_queue (char *bufp) { + if (keyQueueCount) { + char ch; + for (ch = GetFromKeyQueue(); ; ch = GetFromKeyQueue()) { + if (ch == CHAR_LF || ch == CHAR_CR) + ch = 0; + *bufp++ = ch; + if (ch == 0) + break; + } + return 1; + } + return 0; +} + +/* Interface definition, for windows.c */ +struct window_procs mac_procs = { + "mac", + WC_COLOR | WC_HILITE_PET | + WC_FONT_MAP | WC_FONT_MENU | WC_FONT_MESSAGE | WC_FONT_STATUS | WC_FONT_TEXT | + WC_FONTSIZ_MAP | WC_FONTSIZ_MENU | WC_FONTSIZ_MESSAGE | WC_FONTSIZ_STATUS | WC_FONTSIZ_TEXT, + 0L, + mac_init_nhwindows, + mac_unimplemented, /* see macmenu.c:mac_askname() for player selection */ + mac_askname, + mac_get_nh_event, + mac_exit_nhwindows, + mac_suspend_nhwindows, + mac_unimplemented, + mac_create_nhwindow, + mac_clear_nhwindow, + mac_display_nhwindow, + mac_destroy_nhwindow, + mac_curs, + mac_putstr, + mac_display_file, + mac_start_menu, + mac_add_menu, + mac_end_menu, + mac_select_menu, + genl_message_menu, + mac_unimplemented, + mac_get_nh_event, + mac_get_nh_event, +#ifdef CLIPPING + mac_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + tty_print_glyph, + tty_raw_print, + tty_raw_print_bold, + mac_nhgetch, + mac_nh_poskey, + tty_nhbell, + mac_doprev_message, + mac_yn_function, + mac_getlin, + mac_get_ext_cmd, + mac_number_pad, + mac_delay_output, +#ifdef CHANGE_COLOR + tty_change_color, + tty_change_background, + set_tty_font_name, + tty_get_color_string, +#endif +/* other defs that really should go away (they're tty specific) */ + 0, // mac_start_screen, + 0, // mac_end_screen, + genl_outrip, + genl_preference_update, +}; + +/*macwin.c*/ diff --git a/sys/mac/mgetline.c b/sys/mac/mgetline.c new file mode 100644 index 0000000..35c3849 --- /dev/null +++ b/sys/mac/mgetline.c @@ -0,0 +1,74 @@ +/* SCCS Id: @(#)getline.c 3.1 90/22/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "mactty.h" +#include "macwin.h" +#include "macpopup.h" +#include "func_tab.h" + +extern int NDECL(extcmd_via_menu); /* cmd.c */ + +typedef Boolean FDECL ((* key_func), (unsigned char)); + +int +get_line_from_key_queue (char * bufp) { + * bufp = 0; + if (try_key_queue (bufp)) { + while (* bufp) { + if (* bufp == 10 || * bufp == 13) { + * bufp = 0; + } + bufp ++; + } + return true; + } + return false; +} + + +static void +topl_getlin(const char *query, char *bufp, Boolean ext) { + if (get_line_from_key_queue (bufp)) + return; + + enter_topl_mode((char *) query); + while (topl_key(nhgetch(), ext)) + ; + leave_topl_mode(bufp); +} + + +/* + * Read a line closed with '\n' into the array char bufp[BUFSZ]. + * (The '\n' is not stored. The string is closed with a '\0'.) + * Reading can be interrupted by an escape ('\033') - now the + * resulting string is "\033". + */ +void +mac_getlin(const char *query, char *bufp) { + topl_getlin (query, bufp, false); +} + + +/* Read in an extended command - doing command line completion for + * when enough characters have been entered to make a unique command. + * This is just a modified getlin() followed by a lookup. -jsb + */ +int +mac_get_ext_cmd() { + char bufp[BUFSZ]; + int i; + + if (iflags.extmenu) return extcmd_via_menu(); + topl_getlin("# ", bufp, true); + for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++) + if (!strcmp(bufp, extcmdlist[i].ef_txt)) break; + if (extcmdlist[i].ef_txt == (char *)0) i = -1; /* not found */ + + return i; +} + + +/* macgetline.c */ diff --git a/sys/mac/mmodal.c b/sys/mac/mmodal.c new file mode 100644 index 0000000..40959ba --- /dev/null +++ b/sys/mac/mmodal.c @@ -0,0 +1,30 @@ +/* SCCS Id: @(#)mmodal.c 3.1 93/01/24 */ +/* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#if !TARGET_API_MAC_CARBON +# include +# include +#else +# include +#endif + +#include "macpopup.h" + +/* Flash a dialog button when its accelerator key is pressed */ +void +FlashButton(DialogRef wind, short item) { + short type; + Handle handle; + Rect rect; + unsigned long ticks; + + /* Apple recommends 8 ticks */ + GetDialogItem(wind, item, &type, &handle, &rect); + HiliteControl((ControlHandle)handle, kControlButtonPart); + Delay(8, &ticks); + HiliteControl((ControlHandle)handle, 0); + return; +} + + diff --git a/sys/mac/mrecover.c b/sys/mac/mrecover.c new file mode 100644 index 0000000..8a76d93 --- /dev/null +++ b/sys/mac/mrecover.c @@ -0,0 +1,1419 @@ +/* SCCS Id: @(#)mrecover.c 3.4 1996/07/24 */ +/* Copyright (c) David Hairston, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Macintosh Recovery Application */ + +/* based on code in util/recover.c. the significant differences are: + * - GUI vs. CLI. the vast majority of code here supports the GUI. + * - Mac toolbox equivalents are used in place of ANSI functions. + * - void restore_savefile(void) is event driven. + * - integral type substitutions here and there. + */ + +/* + * Think C 5.0.4 project specs: + * signature: 'nhRc' + * SIZE (-1) info: flags: 0x5880, size: 65536L/65536L (64k/64k) + * libraries: MacTraps [yes], MacTraps2 (HFileStuff) [yes], ANSI [no] + * compatibility: system 6 and system 7 + * misc: sizeof(int): 2, "\p": unsigned char, enum size varies, + * prototypes required, type checking enforced, no optimizers, + * FAR CODE [no], FAR DATA [no], SEPARATE STRS [no], single segment, + * short macsbug symbols + */ + +/* + * To do (maybe, just maybe): + * - Merge with the code in util/recover.c. + * - Document launch (e.g. GUI equivalent of 'recover basename'). + * - Drag and drop. + * - Internal memory tweaks (stack and heap usage). + * - Use status file to allow resuming aborted recoveries. + * - Bundle 'LEVL' files with recover (easier document launch). + * - Prohibit recovering games "in progress". + * - Share AppleEvents with NetHack to auto-recover crashed games. + */ + + +#include "config.h" + +/**** Toolbox defines ****/ + +/* MPW C headers (99.44% pure) */ +#include +#include +#include +#include +#ifdef applec +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef THINK /* glue for System 7 Icon Family call (needed by Think C 5.0.4) */ +pascal OSErr GetIconSuite(Handle *theIconSuite, short theResID, long selector) + = {0x303C, 0x0501, 0xABC9}; +#endif + + +/**** Application defines ****/ + +/* Memory */ +typedef struct memBytes /* format of 'memB' resource, preloaded/locked */ +{ + short memReserved; + short memCleanup; /* 4 - memory monitor activity limit */ + long memPreempt; /* 32k - start iff FreeMem() > */ + long memWarning; /* 12k - warn if MaxMem() < */ + long memAbort; /* 4k - abort if MaxMem() < */ + long memIOBuf; /* 16k - read/write buffer size */ +} memBytes, *memBytesPtr, **memBytesHandle; + +#define membID 128 /* 'memB' resource ID */ + + +/* Cursor */ +#define CURS_FRAME 4L /* 1/15 second - spin cursor */ +#define CURS_LATENT 60L /* pause before spin cursor */ +#define curs_Init (-1) /* token for set arrow */ +#define curs_Total 8 /* maybe 'acur' would be better */ +#define cursorOffset 128 /* GetCursor(cursorOffset + i) */ + + +/* Menu */ +enum +{ + mbar_Init = -1, + mbarAppl, /* normal mode */ + mbarRecover, /* in recovery mode */ + mbarDA /* DA in front mode */ +}; +enum +{ + menuApple, + menuFile, + menuEdit, + menu_Total, + + muidApple = 128, + muidFile, + muidEdit +}; +enum +{ + /* Apple menu */ + mitmAbout = 1, + mitmHelp, + ____128_1, + + /* File menu */ + mitmOpen = 1, + ____129_1, + mitmClose_DA, + ____129_2, + mitmQuit + + /* standard minimum required Edit menu */ +}; + + +/* Alerts */ +enum +{ + alrtNote, /* general messages */ + alrtHelp, /* help message */ + alrt_Total, + + alertAppleMenu = 127, /* menuItem to alert ID offset */ + alidNote, + alidHelp +}; + +#define aboutBufSize 80 /* i.e. 2 lines of 320 pixels */ + + +/* Notification */ +#define nmBufSize (32 + aboutBufSize + 32) +typedef struct notifRec +{ + NMRec nmr; + struct notifRec * nmNext; + short nmDispose; + unsigned char nmBuf[nmBufSize]; +} notifRec, *notifPtr; + +#define nmPending nmRefCon /* &in.Notify */ +#define iconNotifyID 128 +#define ics_1_and_4 0x00000300 + +/* Dialogs */ +enum +{ + dlogProgress = 256 +}; +enum +{ + uitmThermo = 1 +}; +enum +{ + initItem, + invalItem, + drawItem +}; + + +/* Miscellaneous */ +typedef struct modeFlags +{ + short Front; /* fg/bg event handling */ + short Notify; /* level of pending NM notifications */ + short Dialog; /* a modeless dialog is open */ + short Recover; /* restoration progress index */ +} modeFlags; + +/* convenient define to allow easier (for me) parsing of 'vers' resource */ +typedef struct versXRec +{ + NumVersion numVers; + short placeCode; + unsigned char versStr[]; /* (small string)(large string) */ +} versXRec, *versXPtr, **versXHandle; + + + +/**** Global variables ****/ +modeFlags in = {1}; /* in Front */ +EventRecord wnEvt; +SysEnvRec sysEnv; +unsigned char aboutBuf[aboutBufSize]; /* vers 1 "Get Info" string */ +memBytesPtr pBytes; /* memory management */ +unsigned short memActivity; /* more memory management */ +MenuHandle mHnd[menu_Total]; +CursPtr cPtr[curs_Total]; /* busy cursors */ +unsigned long timeCursor; /* next cursor frame time */ +short oldCursor = curs_Init; /* see adjustGUI() below */ +notifPtr pNMQ; /* notification queue pointer */ +notifRec nmt; /* notification template */ +DialogTHndl thermoTHnd; +DialogRecord dlgThermo; /* progress thermometer */ +#define DLGTHM ((DialogPtr) &dlgThermo) +#define WNDTHM ((WindowPtr) &dlgThermo) +#define GRFTHM ((GrafPtr) &dlgThermo) + +Point sfGetWhere; /* top left corner of get file dialog */ +Ptr pIOBuf; /* read/write buffer pointer */ +short vRefNum; /* SFGetFile working directory/volume refnum */ +long dirID; /* directory i.d. */ +NMUPP nmCompletionUPP; /* UPP for nmCompletion */ +FileFilterUPP basenameFileFilterUPP; /* UPP for basenameFileFilter */ +UserItemUPP drawThermoUPP; /* UPP for progress callback */ + +#define MAX_RECOVER_COUNT 256 + +#define APP_NAME_RES_ID (-16396) /* macfile.h */ +#define PLAYER_NAME_RES_ID 1001 /* macfile.h */ + +/* variables from util/recover.c */ +#define SAVESIZE FILENAME +unsigned char savename[SAVESIZE]; /* originally a C string */ +unsigned char lock[256]; /* pascal string */ + +int hpid; /* NetHack (unix-style) process i.d. */ +short saveRefNum; /* save file descriptor */ +short gameRefNum; /* level 0 file descriptor */ +short levRefNum; /* level n file descriptor */ + + +/**** Prototypes ****/ +static void warmup(void); +static Handle alignTemplate(ResType, short, short, short, Point *); +pascal void nmCompletion(NMRec *); +static void noteErrorMessage(unsigned char *, unsigned char *); +static void note(short, short, unsigned char *); +static void adjustGUI(void); +static void adjustMemory(void); +static void optionMemStats(void); +static void RecoverMenuEvent(long); +static void eventLoop(void); +static void cooldown(void); + +pascal void drawThermo(WindowPtr, short); +static void itemizeThermo(short); +pascal Boolean basenameFileFilter(ParmBlkPtr); +static void beginRecover(void); +static void continueRecover(void); +static void endRecover(void); +static short saveRezStrings(void); + +/* analogous prototypes from util/recover.c */ +static void set_levelfile_name(long); +static short open_levelfile(long); +static short create_savefile(unsigned char *); +static void copy_bytes(short, short); +static void restore_savefile(void); + +/* auxiliary prototypes */ +static long read_levelfile(short, Ptr, long); +static long write_savefile(short, Ptr, long); +static void close_file(short *); +static void unlink_file(unsigned char *); + + +/**** Routines ****/ + +main() +{ + /* heap adjust */ + MaxApplZone(); + MoreMasters(); + MoreMasters(); + + /* manager initialization */ + InitGraf(&qd.thePort); + InitFonts(); + InitWindows(); + InitMenus(); + TEInit(); + InitDialogs(0L); + InitCursor(); + nmCompletionUPP = NewNMProc(nmCompletion); + basenameFileFilterUPP = NewFileFilterProc(basenameFileFilter); + drawThermoUPP = NewUserItemProc(drawThermo); + + /* get system environment, notification requires 6.0 or better */ + (void) SysEnvirons(curSysEnvVers, &sysEnv); + if (sysEnv.systemVersion < 0x0600) + { + ParamText("\pAbort: System 6.0 is required", "\p", "\p", "\p"); + (void) Alert(alidNote, (ModalFilterUPP) 0L); + ExitToShell(); + } + + warmup(); + eventLoop(); + + /* normally these routines are never reached from here */ + cooldown(); + ExitToShell(); + return 0; +} + +static void +warmup() +{ + short i; + + /* pre-System 7 MultiFinder hack for smooth launch */ + for (i = 0; i < 10; i++) + { + if (WaitNextEvent(osMask, &wnEvt, 2L, (RgnHandle) 0L)) + if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage) + in.Front = (wnEvt.message & resumeFlag); + } + +#if 0 // ??? + /* clear out the Finder info */ + { + short message, count; + + CountAppFiles(&message, &count); + while(count) + ClrAppFiles(count--); + } +#endif + + /* fill out the notification template */ + nmt.nmr.qType = nmType; + nmt.nmr.nmMark = 1; + nmt.nmr.nmSound = (Handle) -1L; /* system beep */ + nmt.nmr.nmStr = nmt.nmBuf; + nmt.nmr.nmResp = nmCompletionUPP; + nmt.nmr.nmPending = (long) &in.Notify; + + +#if 1 + { + /* get the app name */ + ProcessInfoRec info; + ProcessSerialNumber psn; + + info.processInfoLength = sizeof(info); + info.processName = nmt.nmBuf; + info.processAppSpec = NULL; + GetCurrentProcess(&psn); + GetProcessInformation(&psn, &info); + } +#else + /* prepend app name (31 chars or less) to notification buffer */ + { + short apRefNum; + Handle apParams; + + GetAppParms(* (Str255 *) &nmt.nmBuf, &apRefNum, &apParams); + } +#endif + + /* add formatting (two line returns) */ + nmt.nmBuf[++(nmt.nmBuf[0])] = '\r'; + nmt.nmBuf[++(nmt.nmBuf[0])] = '\r'; + + /**** note() is usable now but not aesthetically complete ****/ + + /* get notification icon */ + if (sysEnv.systemVersion < 0x0700) + { + if (! (nmt.nmr.nmIcon = GetResource('SICN', iconNotifyID))) + note(nilHandleErr, 0, "\pNil SICN Handle"); + } + else + { + if (GetIconSuite(&nmt.nmr.nmIcon, iconNotifyID, ics_1_and_4)) + note(nilHandleErr, 0, "\pBad Icon Family"); + } + + /* load and align various dialog/alert templates */ + (void) alignTemplate('ALRT', alidNote, 0, 4, (Point *) 0L); + (void) alignTemplate('ALRT', alidHelp, 0, 4, (Point *) 0L); + + thermoTHnd = (DialogTHndl) alignTemplate('DLOG', dlogProgress, 20, 8, (Point *) 0L); + + (void) alignTemplate('DLOG', getDlgID, 0, 6, (Point *) &sfGetWhere); + + /* get the "busy cursors" (preloaded/locked) */ + for (i = 0; i < curs_Total; i++) + { + CursHandle cHnd; + + if (! (cHnd = GetCursor(i + cursorOffset))) + note(nilHandleErr, 0, "\pNil CURS Handle"); + + cPtr[i] = *cHnd; + } + + /* get the 'vers' 1 long (Get Info) string - About Recover... */ + { + versXHandle vHnd; + + if (! (vHnd = (versXHandle) GetResource('vers', 1))) + note(nilHandleErr, 0, "\pNil vers Handle"); + + i = (**vHnd).versStr[0] + 1; /* offset to Get Info pascal string */ + + if ((aboutBuf[0] = (**vHnd).versStr[i]) > (aboutBufSize - 1)) + aboutBuf[0] = aboutBufSize - 1; + + i++; + + MoveHHi((Handle) vHnd); /* DEE - Fense ... */ + HLock((Handle) vHnd); + BlockMove(&((**vHnd).versStr[i]), &(aboutBuf[1]), aboutBuf[0]); + ReleaseResource((Handle) vHnd); + } + + /* form the menubar */ + for (i = 0; i < menu_Total; i++) + { + if (! (mHnd[i] = GetMenu(i + muidApple))) + note(nilHandleErr, 0, "\pNil MENU Handle"); + + /* expand the apple menu */ + if (i == menuApple) + AddResMenu(mHnd[menuApple], 'DRVR'); + + InsertMenu(mHnd[i], 0); + } + + /* pre-emptive memory check */ + { + memBytesHandle hBytes; + Size grow; + + if (! (hBytes = (memBytesHandle) GetResource('memB', membID))) + note(nilHandleErr, 0, "\pNil Memory Handle"); + + pBytes = *hBytes; + + if (MaxMem(&grow) < pBytes->memPreempt) + note(memFullErr, 0, "\pMore Memory Required\rTry adding 16k"); + + memActivity = pBytes->memCleanup; /* force initial cleanup */ + } + + /* get the I/O buffer */ + if (! (pIOBuf = NewPtr(pBytes->memIOBuf))) + note(memFullErr, 0, "\pNil I/O Pointer"); +} + +/* align a window-related template to the main screen */ +static Handle +alignTemplate(ResType rezType, short rezID, short vOff, short vDenom, Point *pPt) +{ + Handle rtnHnd; + Rect *pRct; + + vOff += GetMBarHeight(); + + if (! (rtnHnd = GetResource(rezType, rezID))) + note(nilHandleErr, 0, "\pNil Template Handle"); + + pRct = (Rect *) *rtnHnd; + + /* don't move memory while aligning rect */ + pRct->right -= pRct->left; /* width */ + pRct->bottom -= pRct->top; /* height */ + pRct->left = (qd.screenBits.bounds.right - pRct->right) / 2; + pRct->top = (qd.screenBits.bounds.bottom - pRct->bottom - vOff) / vDenom; + pRct->top += vOff; + pRct->right += pRct->left; + pRct->bottom += pRct->top; + + if (pPt) + *pPt = * (Point *) pRct; /* top left corner */ + + return rtnHnd; +} + +/* notification completion routine */ +pascal void +nmCompletion(NMRec * pNMR) +{ + (void) NMRemove(pNMR); + + (* (short *) (pNMR->nmPending))--; /* decrement pending note level */ + ((notifPtr) pNMR)->nmDispose = 1; /* allow DisposPtr() */ +} + +/* + * handle errors inside of note(). the error message is appended to the + * given message but on a separate line and must fit within nmBufSize. + */ +static void +noteErrorMessage(unsigned char *msg, unsigned char *errMsg) +{ + short i = nmt.nmBuf[0] + 1; /* insertion point */ + + BlockMove(&msg[1], &nmt.nmBuf[i], msg[0]); + nmt.nmBuf[i + msg[0]] = '\r'; + nmt.nmBuf[0] += (msg[0] + 1); + + note(memFullErr, 0, errMsg); +} + +/* + * display messages using Notification Manager or an alert. + * no run-length checking is done. the messages are created to fit + * in the allocated space (nmBufSize and aboutBufSize). + */ +static void +note(short errorSignal, short alertID, unsigned char *msg) +{ + if (! errorSignal) + { + Size grow; + + if (MaxMem(&grow) < pBytes->memAbort) + noteErrorMessage(msg, "\pOut of Memory"); + } + + if (errorSignal || !in.Front) + { + notifPtr pNMR; + short i = nmt.nmBuf[0] + 1; /* insertion point */ + + if (errorSignal) /* use notification template */ + { + pNMR = &nmt; + + /* we're going to abort so add in this prefix */ + BlockMove("Abort: ", &nmt.nmBuf[i], 7); + i += 7; + nmt.nmBuf[0] += 7; + } + else /* allocate a notification record */ + { + if (! (pNMR = (notifPtr) NewPtr(sizeof(notifRec)))) + noteErrorMessage(msg, "\pNil New Pointer"); + + /* initialize it */ + *pNMR = nmt; + pNMR->nmr.nmStr = (StringPtr) &(pNMR->nmBuf); + + /* update the notification queue */ + if (!pNMQ) + pNMQ = pNMR; + else + { + notifPtr pNMX; + + /* find the end of the queue */ + for (pNMX = pNMQ; pNMX->nmNext; pNMX = pNMX->nmNext) + ; + + pNMX->nmNext = pNMR; + } + } + + /* concatenate the message */ + BlockMove(&msg[1], &((pNMR->nmBuf)[i]), msg[0]); + (pNMR->nmBuf)[0] += msg[0]; + + in.Notify++; /* increase note pending level */ + + NMInstall((NMRec *) pNMR); + + if (errorSignal) + cooldown(); + + return; + } + + /* in front and no error so use an alert */ + ParamText(msg, "\p", "\p", "\p"); + (void) Alert(alertID, (ModalFilterUPP) 0L); + ResetAlrtStage(); + + memActivity++; +} + +static void +adjustGUI() +{ + static short oldMenubar = mbar_Init; /* force initial update */ + short newMenubar; + WindowPeek frontWindow; + + /* oldCursor is external so it can be reset in endRecover() */ + static short newCursor = curs_Init; + unsigned long timeNow; + short useArrow; + + /* adjust menubar 1st */ + newMenubar = in.Recover ? mbarRecover : mbarAppl; + + /* desk accessories take precedence */ + if (frontWindow = (WindowPeek) FrontWindow()) + if (frontWindow->windowKind < 0) + newMenubar = mbarDA; + + if (newMenubar != oldMenubar) + { + /* adjust menus */ + switch (oldMenubar = newMenubar) + { + case mbarAppl: + EnableItem(mHnd[menuFile], mitmOpen); + SetItemMark(mHnd[menuFile], mitmOpen, noMark); + DisableItem(mHnd[menuFile], mitmClose_DA); + DisableItem(mHnd[menuEdit], 0); + break; + + case mbarRecover: + DisableItem(mHnd[menuFile], mitmOpen); + SetItemMark(mHnd[menuFile], mitmOpen, checkMark); + DisableItem(mHnd[menuFile], mitmClose_DA); + DisableItem(mHnd[menuEdit], 0); + break; + + case mbarDA: + DisableItem(mHnd[menuFile], mitmOpen); + EnableItem(mHnd[menuFile], mitmClose_DA); + EnableItem(mHnd[menuEdit], 0); + break; + } + + DrawMenuBar(); + } + + /* now adjust the cursor */ + if (useArrow = (!in.Recover || (newMenubar == mbarDA))) + newCursor = curs_Init; + else if ((timeNow = TickCount()) >= timeCursor) /* spin cursor */ + { + timeCursor = timeNow + CURS_FRAME; + if (++newCursor >= curs_Total) + newCursor = 0; + } + + if (newCursor != oldCursor) + { + oldCursor = newCursor; + + SetCursor(useArrow ? &qd.arrow : cPtr[newCursor]); + } +} + +static void +adjustMemory() +{ + Size grow; + + memActivity = 0; + + if (MaxMem(&grow) < pBytes->memWarning) + note(noErr, alidNote, "\pWarning: Memory is running low"); + + (void) ResrvMem((Size) FreeMem()); /* move all handles high */ +} + +/* show memory stats: FreeMem, MaxBlock, PurgeSpace, and StackSpace */ +static void +optionMemStats() +{ + unsigned char *pFormat = "\pFree:#k Max:#k Purge:#k Stack:#k"; + char *pSub = "#"; /* not a pascal string */ + unsigned char nBuf[16]; + long nStat, contig; + Handle strHnd; + long nOffset; + short i; + + if (wnEvt.modifiers & shiftKey) + adjustMemory(); + + if (! (strHnd = NewHandle((Size) 128))) + { + note(noErr, alidNote, "\pOops: Memory stats unavailable!"); + return; + } + + SetString((StringHandle) strHnd, pFormat); + nOffset = 1L; + + for (i = 1; i <= 4; i++) + { + /* get the replacement number stat */ + switch (i) + { + case 1: nStat = FreeMem(); break; + case 2: nStat = MaxBlock(); break; + case 3: PurgeSpace(&nStat, &contig); break; + case 4: nStat = StackSpace(); break; + } + + NumToString((nStat >> 10), * (Str255 *) &nBuf); + + **strHnd += nBuf[0] - 1; + nOffset = Munger(strHnd, nOffset, (Ptr) pSub, 1L, (Ptr) &nBuf[1], nBuf[0]); + } + + MoveHHi(strHnd); + HLock(strHnd); + note(noErr, alidNote, (unsigned char *) *strHnd); + DisposHandle(strHnd); +} + +static void +RecoverMenuEvent(long menuEntry) +{ + short menuID = HiWord(menuEntry); + short menuItem = LoWord(menuEntry); + + switch (menuID) + { + case muidApple: + switch (menuItem) + { + case mitmAbout: + if (wnEvt.modifiers & optionKey) + optionMemStats(); + /* fall thru */ + case mitmHelp: + note(noErr, (alertAppleMenu + menuItem), aboutBuf); + break; + + default: /* DA's or apple menu items */ + { + unsigned char daName[32]; + + GetItem(mHnd[menuApple], menuItem, * (Str255 *) &daName); + (void) OpenDeskAcc(daName); + + memActivity++; + } + break; + } + break; + + case muidFile: + switch (menuItem) + { + case mitmOpen: + beginRecover(); + break; + + case mitmClose_DA: + { + WindowPeek frontWindow; + short refNum; + + if (frontWindow = (WindowPeek) FrontWindow()) + if ((refNum = frontWindow->windowKind) < 0) + CloseDeskAcc(refNum); + + memActivity++; + } + break; + + case mitmQuit: + cooldown(); + break; + } + break; + + case muidEdit: + (void) SystemEdit(menuItem - 1); + break; + } + + HiliteMenu(0); +} + +static void +eventLoop() +{ + short wneMask = (in.Front ? everyEvent : (osMask + updateMask)); + long wneSleep = (in.Front ? 0L : 3L); + + while (1) + { + if (in.Front) + adjustGUI(); + + if (memActivity >= pBytes->memCleanup) + adjustMemory(); + + (void) WaitNextEvent(wneMask, &wnEvt, wneSleep, (RgnHandle) 0L); + + if (in.Dialog) + (void) IsDialogEvent(&wnEvt); + + switch (wnEvt.what) + { + case osEvt: + if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage) + { + in.Front = (wnEvt.message & resumeFlag); + wneMask = (in.Front ? everyEvent : (osMask + updateMask)); + wneSleep = (in.Front ? 0L : 3L); + } + break; + + case nullEvent: + /* adjust the FIFO notification queue */ + if (pNMQ && pNMQ->nmDispose) + { + notifPtr pNMX = pNMQ->nmNext; + + DisposPtr((Ptr) pNMQ); + pNMQ = pNMX; + + memActivity++; + } + + if (in.Recover) + continueRecover(); + break; + + case mouseDown: + { + WindowPtr whichWindow; + + switch(FindWindow( wnEvt . where , &whichWindow)) + { + case inMenuBar: + RecoverMenuEvent(MenuSelect( wnEvt . where )); + break; + + case inSysWindow: + SystemClick(&wnEvt, whichWindow); + break; + + case inDrag: + { + Rect boundsRect = qd.screenBits.bounds; + Point offsetPt; + + InsetRect(&boundsRect, 4, 4); + boundsRect.top += GetMBarHeight(); + + DragWindow(whichWindow, * ((Point *) &wnEvt.where), &boundsRect); + + boundsRect = whichWindow->portRect; + offsetPt = * (Point *) &(whichWindow->portBits.bounds); + OffsetRect(&boundsRect, -offsetPt.h, -offsetPt.v); + + * (Rect *) *thermoTHnd = boundsRect; + } + break; + } + } + break; + + case keyDown: + { + char key = (wnEvt.message & charCodeMask); + + if (wnEvt.modifiers & cmdKey) + { + if (key == '.') + { + if (in.Recover) + { + endRecover(); + note(noErr, alidNote, "\pSorry: Recovery aborted"); + } + } + else + RecoverMenuEvent(MenuKey(key)); + } + } + break; + + /* without windows these events belong to our thermometer */ + case updateEvt: + case activateEvt: + { + DialogPtr dPtr; + short itemHit; + + (void) DialogSelect(&wnEvt, &dPtr, &itemHit); + } + + case diskEvt: + if (HiWord(wnEvt.message)) + { + Point pt = {60, 60}; + + (void) DIBadMount(pt, wnEvt.message); + DIUnload(); + + memActivity++; + } + break; + } /* switch (wnEvt.what) */ + } /* while (1) */ +} + +static void +cooldown() +{ + if (in.Recover) + endRecover(); + + /* wait for pending notifications to complete */ + while (in.Notify) + (void) WaitNextEvent(0, &wnEvt, 3L, (RgnHandle) 0L); + + ExitToShell(); +} + +/* draw the progress thermometer and frame. 1 level <=> 1 horiz. pixel */ +pascal void +drawThermo(WindowPtr wPtr, short inum) +{ + itemizeThermo(drawItem); +} + +/* manage progress thermometer dialog */ +static void +itemizeThermo(short itemMode) +{ + short iTyp, iTmp; + Handle iHnd; + Rect iRct; + + GetDItem(DLGTHM, uitmThermo, &iTyp, &iHnd, &iRct); + + switch(itemMode) + { + case initItem: + SetDItem(DLGTHM, uitmThermo, iTyp, (Handle) drawThermoUPP, &iRct); + break; + + case invalItem: + { + GrafPtr oldPort; + + GetPort(&oldPort); + SetPort(GRFTHM); + + InsetRect(&iRct, 1, 1); + InvalRect(&iRct); + + SetPort(oldPort); + } + break; + + case drawItem: + FrameRect(&iRct); + InsetRect(&iRct, 1, 1); + + iTmp = iRct.right; + iRct.right = iRct.left + in.Recover; + PaintRect(&iRct); + + iRct.left = iRct.right; + iRct.right = iTmp; + EraseRect(&iRct); + break; + } +} + +/* show only .0 files in get file dialog */ +pascal Boolean +basenameFileFilter(ParmBlkPtr pPB) +{ + unsigned char *pC; + + if (! (pC = (unsigned char *) pPB->fileParam.ioNamePtr)) + return true; + + if ((*pC < 4) || (*pC > 28)) /* save/ 1name .0 */ + return true; + + if ((pC[*pC - 1] == '.') && (pC[*pC] == '0')) /* bingo! */ + return false; + + return true; +} + +static void +beginRecover() +{ + SFTypeList levlType = {'LEVL'}; + SFReply sfGetReply; + + SFGetFile(sfGetWhere, "\p", basenameFileFilterUPP, 1, levlType, + (DlgHookUPP) 0L, &sfGetReply); + + memActivity++; + + if (! sfGetReply.good) + return; + + /* get volume (working directory) refnum, basename, and directory i.d. */ + vRefNum = sfGetReply.vRefNum; + BlockMove(sfGetReply.fName, lock, sfGetReply.fName[0] + 1); + { + static CInfoPBRec catInfo; + + catInfo.hFileInfo.ioNamePtr = (StringPtr) sfGetReply.fName; + catInfo.hFileInfo.ioVRefNum = sfGetReply.vRefNum; + catInfo.hFileInfo.ioDirID = 0L; + + if (PBGetCatInfoSync(&catInfo)) + { + note(noErr, alidNote, "\pSorry: Bad File Info"); + return; + } + + dirID = catInfo.hFileInfo.ioFlParID; + } + + /* open the progress thermometer dialog */ + (void) GetNewDialog(dlogProgress, (Ptr) &dlgThermo, (WindowPtr) -1L); + if (ResError() || MemError()) + note(noErr, alidNote, "\pOops: Progress thermometer unavailable"); + else + { + in.Dialog = 1; + memActivity++; + + itemizeThermo(initItem); + + ShowWindow(WNDTHM); + } + + timeCursor = TickCount() + CURS_LATENT; + saveRefNum = gameRefNum = levRefNum = -1; + in.Recover = 1; +} + +static void +continueRecover() +{ + restore_savefile(); + + /* update the thermometer */ + if (in.Dialog && ! (in.Recover % 4)) + itemizeThermo(invalItem); + + if (in.Recover <= MAX_RECOVER_COUNT) + return; + + endRecover(); + + if (saveRezStrings()) + return; + + note(noErr, alidNote, "\pOK: Recovery succeeded"); +} + +/* no messages from here (since we might be quitting) */ +static void +endRecover() +{ + in.Recover = 0; + + oldCursor = curs_Init; + SetCursor(&qd.arrow); + + /* clean up abandoned files */ + if (gameRefNum >= 0) + (void) FSClose(gameRefNum); + + if (levRefNum >= 0) + (void) FSClose(levRefNum); + + if (saveRefNum >= 0) + { + (void) FSClose(saveRefNum); + (void) FlushVol((StringPtr) 0L, vRefNum); + /* its corrupted so trash it ... */ + (void) HDelete(vRefNum, dirID, savename); + } + + saveRefNum = gameRefNum = levRefNum = -1; + + /* close the progress thermometer dialog */ + in.Dialog = 0; + CloseDialog(DLGTHM); + DisposHandle(dlgThermo.items); + memActivity++; +} + +/* add friendly, non-essential resource strings to save file */ +static short +saveRezStrings() +{ + short sRefNum; + StringHandle strHnd; + short i, rezID; + unsigned char *plName; + + HCreateResFile(vRefNum, dirID, savename); + + sRefNum = HOpenResFile(vRefNum, dirID, savename, fsRdWrPerm); + if (sRefNum <= 0) + { + note(noErr, alidNote, "\pOK: Minor resource map error"); + return 1; + } + + /* savename and hpid get mutilated here... */ + plName = savename + 5; /* save/ */ + *savename -= 5; + do + { + plName++; + (*savename)--; + hpid /= 10; + } + while (hpid); + *plName = *savename; + + for (i = 1; i <= 2; i++) + { + switch (i) + { + case 1: + rezID = PLAYER_NAME_RES_ID; + strHnd = NewString(* (Str255 *) plName); + break; + + case 2: + rezID = APP_NAME_RES_ID; + strHnd = NewString(* (Str255 *) "\pNetHack"); + break; + } + + if (! strHnd) + { + note(noErr, alidNote, "\pOK: Minor \'STR \' resource error"); + CloseResFile(sRefNum); + return 1; + } + + /* should check for errors... */ + AddResource((Handle) strHnd, 'STR ', rezID, * (Str255 *) "\p"); + } + + memActivity++; + + /* should check for errors... */ + CloseResFile(sRefNum); + return 0; +} + +static void +set_levelfile_name(long lev) +{ + unsigned char *tf; + + /* find the dot. this is guaranteed to happen. */ + for (tf = (lock + *lock); *tf != '.'; tf--, lock[0]--) + ; + + /* append the level number string (pascal) */ + if (tf > lock) + { + NumToString(lev, * (Str255 *) tf); + lock[0] += *tf; + *tf = '.'; + } + else /* huh??? */ + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Name Error"); + } +} + +static short +open_levelfile(long lev) +{ + OSErr openErr; + short fRefNum; + + set_levelfile_name(lev); + if (! in.Recover) + return (-1); + + if ((openErr = HOpen(vRefNum, dirID, lock, fsRdWrPerm, &fRefNum)) + && (openErr != fnfErr)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Open Error"); + return (-1); + } + + return (openErr ? -1 : fRefNum); +} + +static short +create_savefile(unsigned char *savename) +{ + short fRefNum; + + /* translate savename to a pascal string (in place) */ + { + unsigned char *pC; + short nameLen; + + for (pC = savename; *pC; pC++); + + nameLen = pC - savename; + + for ( ; pC > savename; pC--) + *pC = *(pC - 1); + + *savename = nameLen; + } + + if (HCreate(vRefNum, dirID, savename, MAC_CREATOR, SAVE_TYPE) + || HOpen(vRefNum, dirID, savename, fsRdWrPerm, &fRefNum)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Create Error"); + return (-1); + } + + return fRefNum; +} + +static void +copy_bytes(short inRefNum, short outRefNum) +{ + char *buf = (char *) pIOBuf; + long bufSiz = pBytes->memIOBuf; + + long nfrom, nto; + + do + { + nfrom = read_levelfile(inRefNum, buf, bufSiz); + if (! in.Recover) + return; + + nto = write_savefile(outRefNum, buf, nfrom); + if (! in.Recover) + return; + + if (nto != nfrom) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Copy Error"); + return; + } + } + while (nfrom == bufSiz); +} + +static void +restore_savefile() +{ + static int savelev; + long saveTemp, lev; + xchar levc; + struct version_info version_data; + + /* level 0 file contains: + * pid of creating process (ignored here) + * level number for current level of save file + * name of save file nethack would have created + * and game state + */ + + lev = in.Recover - 1; + if (lev == 0L) + { + gameRefNum = open_levelfile(0L); + + if (in.Recover) + (void) read_levelfile(gameRefNum, (Ptr) &hpid, sizeof(hpid)); + + if (in.Recover) + saveTemp = read_levelfile(gameRefNum, (Ptr) &savelev, sizeof(savelev)); + + if (in.Recover && (saveTemp != sizeof(savelev))) + { + endRecover(); + note(noErr, alidNote, "\pSorry: \"checkpoint\" was not enabled"); + return; + } + + if (in.Recover) + (void) read_levelfile(gameRefNum, (Ptr) savename, sizeof(savename)); + if (in.Recover) + (void) read_levelfile(gameRefNum, + (Ptr) &version_data, sizeof version_data); + + /* save file should contain: + * current level (including pets) + * (non-level-based) game state + * other levels + */ + if (in.Recover) + saveRefNum = create_savefile(savename); + + if (in.Recover) + levRefNum = open_levelfile(savelev); + + if (in.Recover) + (void) write_savefile(saveRefNum, + (Ptr) &version_data, sizeof version_data); + if (in.Recover) + copy_bytes(levRefNum, saveRefNum); + + if (in.Recover) + close_file(&levRefNum); + + if (in.Recover) + unlink_file(lock); + + if (in.Recover) + copy_bytes(gameRefNum, saveRefNum); + + if (in.Recover) + close_file(&gameRefNum); + + if (in.Recover) + set_levelfile_name(0L); + + if (in.Recover) + unlink_file(lock); + } + else if (lev != savelev) + { + levRefNum = open_levelfile(lev); + if (levRefNum >= 0) + { + /* any or all of these may not exist */ + levc = (xchar) lev; + + (void) write_savefile(saveRefNum, (Ptr) &levc, sizeof(levc)); + + if (in.Recover) + copy_bytes(levRefNum, saveRefNum); + + if (in.Recover) + close_file(&levRefNum); + + if (in.Recover) + unlink_file(lock); + } + } + + if (in.Recover == MAX_RECOVER_COUNT) + close_file(&saveRefNum); + + if (in.Recover) + in.Recover++; +} + +static long +read_levelfile(short rdRefNum, Ptr bufPtr, long count) +{ + OSErr rdErr; + long rdCount = count; + + if ((rdErr = FSRead(rdRefNum, &rdCount, bufPtr)) && (rdErr != eofErr)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Read Error"); + return (-1L); + } + + return rdCount; +} + +static long +write_savefile(short wrRefNum, Ptr bufPtr, long count) +{ + long wrCount = count; + + if (FSWrite(wrRefNum, &wrCount, bufPtr)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Write Error"); + return (-1L); + } + + return wrCount; +} + +static void +close_file(short *pFRefNum) +{ + if (FSClose(*pFRefNum) || FlushVol((StringPtr) 0L, vRefNum)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Close Error"); + return; + } + + *pFRefNum = -1; +} + +static void +unlink_file(unsigned char *filename) +{ + if (HDelete(vRefNum, dirID, filename)) + { + endRecover(); + note(noErr, alidNote, "\pSorry: File Delete Error"); + return; + } +} + diff --git a/sys/mac/mrecover.hqx b/sys/mac/mrecover.hqx new file mode 100644 index 0000000..2cf115d --- /dev/null +++ b/sys/mac/mrecover.hqx @@ -0,0 +1,69 @@ +(This file must be converted with BinHex 4.0) + +:$@ebC@0[GQ9b,R*cFQ-!FR0bBe*6483!!!!!!!!!!!aI55J!!!!!!3!!!!TJ!!! +*B!!!!Im!!'1M#Q&$E'YTEQPd,Q1!!J!!!&4&@&4,38K-!3!!-"*YFQ9MEhCPFLj +`FQpU,R*cFQ0b!J!!!(*cFQ058d9%!!"bFh*M8P0&4!%!!0!!J!!!!!!!!!!!!!! +!!!!!!!!!!+lTZ(B!!!!!!!!-F`!!!!!!!!!!%!!!!'1M#Q&$E'YXEfp`,Q1!!J! +!!&4&@&4,38K-!3!!B!"X!!!!!'1V!!!!!&Le!!"F!!!!!!!!!!!!!!#Mk`QeT3H +i4`!!!!"hD3!!!!!!!!!!!!!!!!!!!!!p93!A!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!L!!%!!!!!!$J!EJ"-!1B%!Np,!!!!!!!+!!S!+J&+L!*H-!!!!!` +!+!!S!))"I!#!!!3!!!!-!#J!+!$#!A`!J3!%!!!!5!##!!!!!!!!!!$rrrrl"%9 +NDA3%9@jNE`"D!!!",3!!!!!$3h9d!&J!!!4$Eh"j!%-!!!93BA0dC3"@!!!&3fa +PBA)!!!!!!!!!!$J!J!!!!!!!!!!!rrrrp`%8%%&LEh9d)&*PBfpfCA)Z,Li!!!! +!"dKPE(!Z,Li!!!!!!5d!!!!!!!!!!%3!!!2!!X!$`!'!!m!--!J3%#J35"H)%!J +)%!``!m!!!!2!"q!(i!IJ!m!2m"ri(rJrr$rm2r`rr"ri(rJ2m!2!!!N!"`!!!%! +'J!l!CZ!hB"r!6mcq%[mUkG6XL#N3-V!&@!5-!`3!!"r!Ir"rq2rmrrlrr[rrrrr +rrrrrIrprrcrr(ri2rJ2i!!!!('jS8Q-!!!!"4P*&4J!!!!!!J%P$6L-!!!!!!)! +!!!!(39"36!!!!!!!!"364'&fD@3J5'&TFR0dEfiJ-bmj-`!!!3!!!!!!!"m!!!" +r`!!JIm!!1$q`!"3rF!!512J!#6Gi!!6rH!!#IlJ!!6ri!''Iq!$rc$$Jrq3"-1l +b!T!!kIN&82IPLU$[ip9!6Z%LJ!(Jb3!2m))!$r%N!!I#LJ!!"9N!!!UNJ!!93N! +!%S&J!"N!d!!1!&!!!!!`!!!!!!!!!!!2r`!!2rr!!(rrm!"rrrJ!rrrm!2rrrJ$ +rrrm!rrrrJ2rrrm$rrrrJrrrrm2rrrrMrrrrmrrrrr2rrrrlrrrrqrrrrrhrrrrp +rrrrr2rrrrcrrrrmIrrrr$rrrr`Irrrm$rrrr!Irrr`$rrrm!Irrr!$rrrJ!Irri +!"rrm!!(rm!!!!J!!!!!!!!!!!!!!!!!!!!!!!!!!!!$rrrm!!!!!!!!!!!!!!!! +2%4%4r`!!!!!!!!!!m!!!$a%4%4m!!!!!!!!!!2r`!!$a%4%Ir`!!!!!!!!!2h`! +!m4%4m4m!!!!!!!!!$ph`!2%Ira%4m!!!!!!!!!$ph`$am4(a%I!!!!!!!!!!$ph +r%4%4m4(`!!!!!!!!!!$pha%4%4m4m!!!!!!!!!!!$pha%4%4%I!!!!!!!!r`!!r +pha(rra(`!!!!!!$a(rra(phr!!$r!!$rm!!!m4%4%4(ph`!!!!!242m!!2%I%4m +4(ph`!!!!p242!!$a(ara%Irph`!!$dp26`!!m4(a%4(`$p$`!26dp2!!!2%I%4% +4m!$`$`p26dm!!!!2ra%I%I!!$phd426`!!!!!!rrm4(`!!$r4242!!!!!!!2%4% +4(`!!p%4%m!!!!!!!$r%4%4m!$d6d6`!!!!!!!!!2rrr`!26d42h`!!!!!!!!!!! +!!!p26drph`!!!!!!!!!!!!$dp26`$ph`!!!!!!!!!!!26dp2!!$ph`!!!!!!!!! +!$d6dm!!!$p$`!!!!!!!!!!rd6`!!!!$`h`!!!!!!!!!!rr!!!!!!$pm!!!!!!!! +!!!!!!!!!!!$r!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!)! +!!!rrm!!!!!!!m4mI!!!!$G!2(a(`!!!!h3m4mI!!!!!0ha%I!!!!$`$Grrm!r`$ +arrh3!!p%m2%4(pd!p26`m4mI$Gp26`$rram!p%6`!!!!m!p%6`!!!!!!p26p!!! +!!!p26`h3!!!!$d6`!0d!!!!!r`!!$3!!!!!!!!!!!!!!!#!'J!l!CZ!hB"r!6mc +q%[mUkG6XL#N3-V!&@!5-!`3!!!!!!2B!!3!!!!!!H!"Z!)`!jJ3#6dX!!!!!!!S +!#J"U!8U)e6%T)&0PE'9MG#!L6h"PELiZ,L)JCR*[E5"dD'8J4QPXC5"YC@je,Jd +b+5"6C@aPBh3JG'KP)'GKE@8JG'mJFQ9MEhCPFL"QFQpY)(4SC5"ND@&XEfFZ$6- +T)&GKDA3JCQpb)(4SC5"bCA0eE(3J+%p,)'pb)&0[FR*j+5i0$84[)'j[G#"KG(4 +PEA"d)(4[)(*PBfpfCA)JB5"RB@eP)(4SBA3JDA-J)QPZ)("bEfGbCA0c)L!SD5j +P,L"cG'PXE#"bG@jZD@jR)'PZ)%jPG%KKBfXT)3!!!!"#!)%!!!!!!!!!!2rrrqX +%4QPXC3G2F'9Z,LiZ!%m!!!%Y!!!!!!K$E'pcC5"%33"A!!!",3!!!!!%8A9TG!" +4!!!!!!!!&!!!!!3!!)!!!!!`!!!!%!!!!%!!!!!!3J43FQPf4PG54!G$E'9KER9 +`4&G54!G3FQ9PEA"d4%a14`GABA*ZD@jR4%a14`9"BQpbG%4-6NF'58mJ3R9Q4%a +14`!!!%3!!!2!!d!$`!'!!B!#3!4J"#!%B!@J"#!%B!*!!B!!!!2!"q!(i!IJ!m! +$`!IJ$r!2m!r`$r!2m!r`"q!$`!'!!!N!"`!!!%3!!!2!!X!$`!'!!B!#3!2!!N! +(`!C!!m!#3!2!!B!!!!2!"q!(i!IJ!m!$`!IJ"q!(i!rJ$q!(i!IJ"q!$`!'!!!N +!"`!!!"d!+!!S!%!"3!!%!!!!!!!!!!!"!!K3FQpRFQ9cF`!!!"!!!!!!!!!!"`! +,!"%"$B!!!!!!4!!!!m!$3!2!!B!"J!*!"Q!%)!CJ"D!')!4J!d!"J!!!!m!(i!I +J"q!$`!2!"q!2m!r`$r!2m!r`$r!(i!2!!B!!#3!(!!!!4!!!!m!$3!2!!B!"J!0 +!"L!&B!8J"@!&)!9J!N!"J!!!!m!(i!IJ"q!$`!2!"q!2m!r`$r!2m!r`$r!(i!2 +!!B!!#3!(!!!!4!!!!m!#`!2!!B!"J!*!!m!#3!2J!Q!$`!*!!m!"J!!!!m!(i!I +J"q!$`!2!"q!(i!IJ"r!(m!IJ"q!(i!2!!B!!#3!(!!!!4!!!!m!#`!2!!B!$J!q +3!!`3'CJCQ"QB'CJ*N!!-%!2!!!!$`!IJ"q!(i!2!$r!Iq"ri2r`rr$rm2r`Iq"r +i$r!$`!!*!!F!!!"%!!!$`!0!!m!"J!'!!m!&)!DJ"+!'S!5J"U!$3!'!!!!$`!I +J"q!(i!2!!m!(i!r`$r!2m!r`$r!2m!IJ!m!"J!!*!!F!!!!U!c#!!!!!!c-Z-ap +5C@0[GQ9b)$-Z-`eNCACdC@&Y3'jPG'KKBfXZEh*R!!!!&J-`J!!!!!-c,M-,8Q9 +MEhCPFL!c,M-!!!%!!!!+B!!!#@!!!!(r"-)(q#qq!!!!(!(k!!p"6&*8!!%!JN4 +*9%`!!J#D689193!#!,j$99*6!!F!iNP$6L-!!!&#D@0c)`!!!8j#6N4-!!!"@Qj +S8Q-!!!&Q4P*&4J!!!A*fCA*c!!%"IQPME$3!!!'@D@0c0!!!!D*65801!!!"VQe +PE8)!!!'k9%e36!!!!FC%6%p(!!!"dJ#!rrm!!!!Q!!!!!!#"rrm!!!!f!!!!!!# +!rrmJ!!!!!!!!!!#"rrmJ!!90!!!!!!%!rrmJ!!HF!!!!!!##rrm!!!"'!!!!!!# +!rrm!!!#5!!!!!!#"rrm!!!C(!!!!!!#!rrm8!!$1!!!!!!#"rrm8!!EV!!!!!!# +#rrm8!!Fc!!!!!!#(rrm8!!H`!!!!!!#&rrm8!!Ii!!!!!!#'rrm8!!K!!!!!!!# +%rrm8!!L)!!!!!!#$rrm8!!M3!!!!!!#!rrm!!!'G!!!!!!#!rrm!!!%@!!!!!!# +!rrm!!!&D!!!!!!!!rrm!!!'&!!!!!!#!rrm!!!&k!!!!!!!"rrm!!!NB!!!!!!! +#rrm!!!P'!!!!!!#!rrm!!!+K!!!!!!#!rrm!!!5P!!!!!!#!rrm!!!8T!!!!!!# +!rrm8!!D0!!!!!!#!!!!!!!DP!!!!!!%!rrm!!!Gl!!!!!!4YC@e#8i3: diff --git a/sys/mac/mttymain.c b/sys/mac/mttymain.c new file mode 100644 index 0000000..16b4512 --- /dev/null +++ b/sys/mac/mttymain.c @@ -0,0 +1,586 @@ +/* SCCS Id: @(#)mttymain.c 3.1 93/02/26 */ +/* Copyright (c) Jon W{tte, 1993 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "macwin.h" +#include "mttypriv.h" +#include "mactty.h" +#include "wintty.h" + +#if !TARGET_API_MAC_CARBON +#include +#endif + +#define MT_WINDOW 135 +#define MT_WIDTH 80 +#define MT_HEIGHT 24 + + +/* + * Names: + * + * Statics are prefixed _ + * Mac-tty becomes mt_ + */ + +static long _mt_attrs [5] [2] = { + { 0x000000, 0xffffff }, /* Normal */ + { 0xff8080, 0xffffff }, /* Underline */ + { 0x40c020, 0xe0e0e0 }, /* Bold */ + { 0x003030, 0xff0060 }, /* Blink */ + { 0xff8888, 0x000000 }, /* Inverse */ +}; + + +static char _attrs_inverse [5] = { + 0, 0, 0, 0, 0 , +}; + + +/* see color.h */ + +static long _mt_colors [CLR_MAX] [2] = { + { 0x000000, 0x808080 }, /* Black */ + { 0x880000, 0xffffff }, /* Red */ + { 0x008800, 0xffffff }, /* Green */ + { 0x553300, 0xffffff }, /* Brown */ + { 0x000088, 0xffffff }, /* Blue */ + { 0x880088, 0xffffff }, /* Magenta */ + { 0x008888, 0xffffff }, /* Cyan */ + { 0x888888, 0xffffff }, /* Gray */ + { 0x000000, 0xffffff }, /* No Color */ + { 0xff4400, 0xffffff }, /* Orange */ + { 0x00ff00, 0xffffff }, /* Bright Green */ + { 0xffff00, 0x606060 }, /* Yellow */ + { 0x0033ff, 0xffffff }, /* Bright Blue */ + { 0xff00ff, 0xffffff }, /* Bright Magenta */ + { 0x00ffff, 0xffffff }, /* Bright Cyan */ + { 0xffffff, 0x505050 }, /* White */ +}; + +static char _colors_inverse [CLR_MAX] = { + 1, 0, 0, 0 , + 0, 0, 0, 0 , + 0, 0, 0, 0 , + 0, 0, 0, 0 , +}; + + +#ifdef CHANGE_COLOR + +#define POWER_LIMIT 22 +#define SECONDARY_POWER_LIMIT 16 +#define CHANNEL_LIMIT 14 +#define SECONDARY_CHANNEL_LIMIT 12 + +void +tty_change_color (int color, long rgb, int reverse) { +long inverse, working_rgb = rgb; +int total_power = 0, max_channel = 0; +int cnt = 3; + + working_rgb >>= 4; + while (cnt -- > 0) { + total_power += working_rgb & 0xf; + max_channel = max (max_channel, working_rgb & 0xf); + working_rgb >>= 8; + } + + if (total_power >= POWER_LIMIT || + (total_power >= SECONDARY_POWER_LIMIT && + max_channel >= SECONDARY_CHANNEL_LIMIT) || + max_channel >= CHANNEL_LIMIT) + inverse = 0x000000; + else + inverse = 0xffffff; + + if (reverse) { + working_rgb = rgb; + rgb = inverse; + inverse = working_rgb; + } + + if (color >= CLR_MAX) { + if (color - CLR_MAX >= 5) + impossible ("Changing too many colors"); + else { + _mt_attrs [color - CLR_MAX] [0] = rgb; + _mt_attrs [color - CLR_MAX] [1] = inverse; + _attrs_inverse [color - CLR_MAX] = reverse; + } + } else if (color >= 0) { + _mt_colors [color] [0] = rgb; + _mt_colors [color] [1] = inverse; + _colors_inverse [color] = reverse; + } else + impossible ("Changing negative color"); +} + +void tty_change_background (int white_or_black) { + register int i; + + for (i = 0; i < CLR_MAX; i++) { + if (white_or_black) + _mt_colors [i] [1] = 0xffffff; /* white */ + else + _mt_colors [i] [1] = 0x000000; /* black */ + } + + /* special cases */ + if (white_or_black) { + _mt_colors [CLR_BLACK] [1] = 0x808080; /* differentiate black from no color */ + _mt_colors [CLR_WHITE] [1] = 0x505050; /* highlight white with grey background */ + _mt_colors [CLR_YELLOW] [1] = 0x606060; /* highlight yellow with grey background */ + _mt_colors [CLR_BLUE] [0] = 0x000088; /* make pure blue */ + _mt_colors [NO_COLOR] [0] = 0x000000; /* make no_color black on white */ + _mt_attrs [0] [0] = 0x000000; /* "normal" is black on white */ + _mt_attrs [0] [1] = 0xffffff; + } else { + _mt_colors [NO_COLOR] [0] = 0xffffff; /* make no_color white on black */ + _mt_colors [CLR_BLACK] [1] = 0x808080; /* differentiate black from no color */ + _mt_colors [CLR_BLUE] [0] = 0x222288; /* lighten blue - it's too dark on black */ + _mt_attrs [0] [0] = 0xffffff; /* "normal" is white on black */ + _mt_attrs [0] [1] = 0x000000; + } +} + +char * +tty_get_color_string (void) { +char *ptr; +int count; +static char color_buf [5 * (CLR_MAX + 5) + 1]; + + color_buf [0] = 0; + ptr = color_buf; + + for (count = 0; count < CLR_MAX; count ++) { + int flag = _colors_inverse [count] ? 1 : 0; + + sprintf (ptr, "%s%s%x%x%x", count ? "/" : "" , + flag ? "-" : "" , + (int)(_mt_colors [count] [flag] >> 20) & 0xf , + (int)(_mt_colors [count] [flag] >> 12) & 0xf , + (int)(_mt_colors [count] [flag] >> 4) & 0xf); + ptr += strlen (ptr); + } + for (count = 0; count < 5; count ++) { + int flag = _attrs_inverse [count] ? 1 : 0; + + sprintf (ptr, "/%s%x%x%x" , + flag ? "-" : "" , + (int)(_mt_attrs [count] [flag] >> 20) & 0xf , + (int)(_mt_attrs [count] [flag] >> 12) & 0xf , + (int)(_mt_attrs [count] [flag] >> 4) & 0xf); + ptr += strlen (ptr); + } + + return color_buf; +} +#endif + + +extern struct DisplayDesc *ttyDisplay; /* the tty display descriptor */ + +char kill_char = CHAR_ESC; +char erase_char = CHAR_BS; + +WindowRef _mt_window = (WindowRef) 0; +static Boolean _mt_in_color = 0; +extern short win_fonts [NHW_TEXT + 1]; + +static void +_mt_init_stuff (void) { +long resp, flag; +short num_cols, num_rows, win_width, win_height, font_num, font_size; +short char_width, row_height; +short hor, vert; + + LI = MT_HEIGHT; + CO = MT_WIDTH; + + if (!strcmp(windowprocs.name, "mac")) { + dprintf ("Mac Windows"); + LI -= 1; + } else { + dprintf ("TTY Windows"); + } + + /* + * If there is at least one screen CAPABLE of color, and if + * 32-bit QD is there, we use color. 32-bit QD is needed for the + * offscreen GWorld + */ + if (!Gestalt (gestaltQuickdrawVersion, &resp) && resp > 0x1ff) { + GDHandle gdh = GetDeviceList (); + while (gdh) { + if (TestDeviceAttribute (gdh, screenDevice)) { + if (HasDepth (gdh, 4, 1, 1) || + HasDepth (gdh, 8, 1, 1) || + HasDepth (gdh, 16, 1, 1) || + HasDepth (gdh, 32, 1, 1)) { + _mt_in_color = 1; + break; + } + } + gdh = GetNextDevice (gdh); + } + } + + if (create_tty (&_mt_window, WIN_BASE_KIND + NHW_MAP, _mt_in_color) != noErr) + error("_mt_init_stuff: Couldn't create tty."); + SetWindowKind(_mt_window, WIN_BASE_KIND + NHW_MAP); + SelectWindow(_mt_window); + SetPortWindowPort(_mt_window); + SetOrigin(-1, -1); + + font_size = iflags.wc_fontsiz_map ? iflags.wc_fontsiz_map : + (iflags.large_font && !small_screen) ? 12 : 9; + if (init_tty_number (_mt_window, win_fonts [NHW_MAP], font_size, CO, LI) != noErr) + error("_mt_init_stuff: Couldn't init tty."); + + if (get_tty_metrics (_mt_window, &num_cols, &num_rows, &win_width , + &win_height, &font_num, &font_size, &char_width, &row_height)) + error("_mt_init_stuff: Couldn't get tty metrics."); + + SizeWindow (_mt_window, win_width + 2, win_height + 2, 1); + if (RetrievePosition (kMapWindow, &vert, &hor)) { + dprintf ("Moving window to (%d,%d)", hor, vert); + MoveWindow (_mt_window, hor, vert, 1); + } + ShowWindow (_mt_window); + + /* Start in raw, always flushing mode */ + get_tty_attrib(_mt_window, TTY_ATTRIB_FLAGS, &flag); + flag |= TA_ALWAYS_REFRESH | TA_WRAP_AROUND; + set_tty_attrib(_mt_window, TTY_ATTRIB_FLAGS, flag); + + get_tty_attrib(_mt_window, TTY_ATTRIB_CURSOR, &flag); + flag |= (TA_BLINKING_CURSOR | TA_NL_ADD_CR); + set_tty_attrib(_mt_window, TTY_ATTRIB_CURSOR, flag); + + set_tty_attrib(_mt_window, TTY_ATTRIB_FOREGROUND, _mt_colors[NO_COLOR][0]); + set_tty_attrib(_mt_window, TTY_ATTRIB_BACKGROUND, _mt_colors[NO_COLOR][1]); + clear_tty (_mt_window); + + InitMenuRes (); +} + + +int +tgetch (void) { +EventRecord event; +long sleepTime = 0; +int ret = 0; + + for (;!ret;) { + WaitNextEvent (-1, &event, sleepTime, 0); + HandleEvent (&event); + blink_cursor (_mt_window, event.when); + if (event.what == nullEvent) { + sleepTime = GetCaretTime (); + } else { + sleepTime = 0; + } + ret = GetFromKeyQueue (); + if (ret == CHAR_CR) ret = CHAR_LF; + } + return ret; +} + + +void +getreturn (char *str) { + FlushEvents (-1, 0); + msmsg ("Press space %s", str); + (void) tgetch (); +} + + +int +has_color (int color) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(color) +#endif + Rect r; +// Point p = {0, 0}; + GDHandle gh; + + + if (!_mt_in_color) + return 0; + + GetWindowBounds(_mt_window, kWindowContentRgn, &r); +// SetPortWindowPort(_mt_window); +// LocalToGlobal (&p); +// OffsetRect (&r, p.h, p.v); + + gh = GetMaxDevice (&r); + if (!gh) { + return 0; + } + + return (*((*gh)->gdPMap))->pixelSize > 4; /* > 4 bpp */ +} + + +void +tty_delay_output (void) { +EventRecord event; +long toWhen = TickCount () + 3; + + while (TickCount () < toWhen) { + WaitNextEvent (updateMask, &event, 3L, 0); + if (event.what == updateEvt) { + HandleEvent (&event); + blink_cursor (_mt_window, event.when); + } + } +} + + +void +cmov (int x, int y) { + move_tty_cursor (_mt_window, x, y); + ttyDisplay->cury = y; + ttyDisplay->curx = x; +} + + +void +nocmov (int x, int y) { + cmov (x, y); +} + + +static void +_mt_set_colors (long *colors) { +short err; + + if (!_mt_in_color) { + return; + } + err = set_tty_attrib (_mt_window, TTY_ATTRIB_FOREGROUND, colors [0]); + err = set_tty_attrib (_mt_window, TTY_ATTRIB_BACKGROUND, colors [1]); +} + + +void +term_end_attr (int attr) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused (attr) +#endif + _mt_set_colors (_mt_attrs [0]); +} + + +void +term_start_attr (int attr) { + switch (attr) { + case ATR_ULINE: + _mt_set_colors (_mt_attrs [1]); + break; + case ATR_BOLD: + _mt_set_colors (_mt_attrs [2]); + break; + case ATR_BLINK: + _mt_set_colors (_mt_attrs [3]); + break; + case ATR_INVERSE: + _mt_set_colors (_mt_attrs [4]); + break; + default: + _mt_set_colors (_mt_attrs [0]); + break; + } +} + + +void +standoutend (void) { + term_end_attr (ATR_INVERSE); +} + + +void +standoutbeg (void) { + term_start_attr (ATR_INVERSE); +} + + +void +term_end_color (void) { + _mt_set_colors (_mt_colors [NO_COLOR]); +} + + +void +cl_end (void) { + _mt_set_colors (_mt_attrs [0]); + clear_tty_window (_mt_window, ttyDisplay->curx, ttyDisplay->cury, + CO - 1, ttyDisplay->cury); +} + + +void +clear_screen (void) { + _mt_set_colors (_mt_attrs [0]); + clear_tty (_mt_window); +} + + +void +cl_eos (void) { + _mt_set_colors (_mt_attrs [0]); + clear_tty_window (_mt_window, ttyDisplay->curx, ttyDisplay->cury, CO - 1, + LI - 1); +} + + +void +home (void) { + cmov (0,0); +} + + +void +backsp (void) { +char eraser [] = { CHAR_BS, CHAR_BLANK, CHAR_BS, 0 }; +short err; + + err = add_tty_string (_mt_window, eraser); + err = update_tty (_mt_window); +} + + +void +msmsg (const char *str, ...) { +va_list args; +char buf [1000]; + + va_start (args, str); + vsprintf (buf, str, args); + va_end (args); + + xputs (buf); +} + + +void +term_end_raw_bold (void) { + term_end_attr (ATR_INVERSE); +} + + +void +term_start_raw_bold (void) { + term_start_attr (ATR_INVERSE); +} + + +void +term_start_color (int color) { + if (color >= 0 && color < CLR_MAX) { + _mt_set_colors (_mt_colors [color]); + } +} + + +void +setftty (void) +{ + long flag; + + /* Buffered output for the game */ + get_tty_attrib (_mt_window, TTY_ATTRIB_FLAGS, &flag); + flag &= ~ TA_ALWAYS_REFRESH; + flag |= TA_INHIBIT_VERT_SCROLL; /* don't scroll */ + set_tty_attrib (_mt_window, TTY_ATTRIB_FLAGS, flag); + iflags.cbreak = 1; +} + + +void +tty_startup (int *width, int *height ) { + _mt_init_stuff (); + *width = CO; + *height = LI; +} + + +void +gettty (void) { +} + + +void +settty (const char *str) +{ + long flag; + + update_tty (_mt_window); + + /* Buffered output for the game, raw in "raw" mode */ + get_tty_attrib(_mt_window, TTY_ATTRIB_FLAGS, &flag); + flag &= ~ TA_INHIBIT_VERT_SCROLL; /* scroll */ + flag |= TA_ALWAYS_REFRESH; + set_tty_attrib(_mt_window, TTY_ATTRIB_FLAGS, flag); + + tty_raw_print ("\n"); + if (str) { + tty_raw_print (str); + } +} + + +void +tty_number_pad (int arg) { +#if defined(__SC__) || defined(__MRC__) +# pragma unused(arg) +#endif +} + + +void +tty_start_screen (void) { + iflags.cbreak = 1; +} + + +void +tty_end_screen (void) { +} + + +void +xputs (const char *str) { + add_tty_string (_mt_window, str); +} + + +int +term_puts (const char *str) { + xputs (str); + return strlen (str); +} + + +int +term_putc (int c) { +short err; + + err = add_tty_char (_mt_window, c); + return err ? EOF : c; +} + + +int +term_flush (void *desc) { + if (desc == stdout || desc == stderr) { + update_tty (_mt_window); + } else { + impossible ("Substituted flush for file"); + return fflush (desc); + } + return 0; +} diff --git a/sys/msdos/Install.dos b/sys/msdos/Install.dos new file mode 100644 index 0000000..735d9a8 --- /dev/null +++ b/sys/msdos/Install.dos @@ -0,0 +1,268 @@ + SCCS Id: @(#)Install.dos 3.4 + + Copyright (c) NetHack PC Development Team 1990-2002. + NetHack may be freely redistributed. See license for details. + ============================================================== + Instructions for compiling and installing + NetHack 3.4 on a DOS system + ====================================================== + (or, How to make PC NetHack 3.4) + Last revision: $Date: 2003/06/13 19:57:52 $ + +Credit for a runnable full PC NetHack 3.4 goes to the PC Development team +of Paul Winner, Kevin Smolkowski, Michael Allison, Yitzhak Sapir, Bill Dyer, +Timo Hakulinen, Yamamoto Keizo, Mike Threepoint, Mike Stephenson, +Stephen White, Ken Washikita and Janet Walz. The present port is based +on the previous effort of Pierre Martineau, Stephen Spackman, Steve Creps, Mike +Threepoint, Mike Stephenson, Norm Meluch and Don Kneller. + +There has been very little port-specific maintenance for NetHack on DOS since +NetHack 3.3.0. + +CONTENTS: + + I. Dispelling the Myths + II. Compiling on a DOS machine + Appendix A - Building the "official binary" + Appendix B - DJGPP Compiler (gcc ported to msdos) notes + Appendix C - Additional Notes + Appendix D - Contacting Us + +I. Dispelling the Myths: + + Compiling NetHack is not as easy as it sounds, nor as hard as it looks, + however it will behoove you to read this entire file through before + beginning the task. + + We have provided a proper Makefile for building NetHack using the + following compilers: + djgpp V2.03 or later + + For specific details concerning the djgpp compiler, please see the + appendix B. + + The makefile named Makefile.GCC is for use with GNU Make that + accompanies djgpp. + + If you want to build a copy of NetHack that is identical to the + "official binary", please see appendix A. + + The unsupported sys/msdos/Makefile.MSC was for the old 16 bit + Microsoft Visual C 1.52c compiler and has not been made compliant + with 3.4.x. + + You may find it useful to obtain copies of lex (flex) and yacc (bison + or byacc). While not strictly necessary to compile nethack, they are + required should you desire to make any changes to the level and dungeon + compilers. Flex and Bison are included with the DJGPP distribution and + are also available on many archive sites. + + Also be sure to pick up djgpp v2gnu/fil41b.zip to get ls.exe and + touch.exe, since the Makefile uses them by default. + +II. To compile your copy of NetHack on a DOS machine: + (or "just follow these few 'simple' steps outlined below.") + +1. It almost goes without saying that you should make sure that your tools + are set up and running correctly. + +2. Make sure all the NetHack files are in the appropriate directory + structure. You should have a main directory with subdirectories + dat, doc, include, src, sys\share, sys\msdos, util, win\tty and + win\share. Other subdirectories may also be included in your + distribution, but they are not necessary for use with DOS. You can + delete them to save space. + + Required Source Directories for DOS NetHack: + + (top) + | + ------------------------------------------------- + | | | | | | | + util dat doc include src sys win + | | + ------ ----- + | | | | + share msdos tty share + + Check the file "Files" in your top level directory for an exact + listing of what files are in which directory. In order for the + Makefiles to work, all the source files must be in the proper + locations. + + If you downloaded or ftp'd the sources from a UNIX system, the lines + will probably end in UNIX-style newlines, instead of the carriage + return and line feed pairs used by DOS. Some programs have trouble + with them, so you may need to convert them (with a utility like + Rahul Dhesi's "flip"). + +3. Go to the sys/msdos directory and ensure that the file setup.bat + has MSDOS style end-of-line characters rather than UNIX style + end-of-line characters. You can do that using a utility like + Rahul Dhesi's "flip", or by invoking the MSDOS edit utility on + setup.bat and saving the file without making any changes. Failure to + do this will prevent the bat file from executing completely, yet no + warning message will be given. + + Run the setup.bat batch file with the following as the argument: + + GCC For djgpp and GNU MAKE. + + The appropriate and necessary Makefile movement will be accomplished + for you, as well as verifying a few files and fixing a few file names + on FAT systems with long file name support. + +4. Now go to the include subdirectory to check a couple of the header + files there. Things *should* work as they are, but since you have + probably set up your system in some sort of custom configuration + it doesn't hurt to check out the following: + + First check config.h according to the comments to match your system and + desired set of features. Mostly you need to check the WIZARD option, + and check TERMLIB and COMPRESS. Also be sure to leave DLB support + commented out in config.h. MSDOS has support for DLB, but it must be + done through the Makefile, rather than config.h, to ensure that the + necessary packaging steps are done. + + We've managed to enable all the special features. You may include all + or as few of them as you wish. To conserve disk space, you may wish + to disable LOGFILE and NEWS. + + Also check pcconf.h, which should not need much editing (if you are + including random.c, and if you do not require termcap for screen + management). If you are not including random.c you will need to + comment out RANDOM. + + If using DJGPP, you can choose between SCREEN_BIOS + and SCREEN_DJGPPFAST. Never, never, ever choose both. Bad things + will happen. We are not kidding. + +5. If you want to change the high score list behavior, examine the top of + topten.c, in the src directory. You may want to change the definitions of + PERSMAX, POINTSMIN, and ENTRYMAX. We set POINTSMIN to 51 and ENTRYMAX to + 50 to keep the size of the score list down. + +6. Go to the src directory and edit the top of your Makefile. Be sure the + directory you want the game installed in (GAMEDIR) actually exists. + +7. Now that everything is set up, + + Go to the src directory, and using the GNU Make utility, + "make install". + + Depending on your particular machine and compiler, you can either + grab a cup of coffee or go home for the day. Your computer will be + occupied for quite some time. If all goes well, you will get an + NetHack executable. + +9. If you chose DLB support (recommended), make sure that the file nhdat + got copied into the game directory. + + If you didn't choose DLB support, make sure the support files -- + data, rumors, cmdhelp, opthelp, help, hh,history, guidebook.txt + license, and all the *.lev files -- were copied to the game directory. + If not, move them there from the dat directory yourself. rumors can + be created manually be entering "makedefs -r", data by entering + "makedefs -d". + + Make sure the files NetHack1.tib and NetHacko.tib made it to your game + directory. Copy them from src to the game directory yourself if + necessary. + + Make sure the files defaults.nh and termcap made it to your game + directory. If not, go to sys\share and copy NetHack.cnf to + your game directory as defaults.nh. The name in previous versions was + nethack.cnf, but the CNF extension conflicted with the MS Windows + speed-dialer, making the file hidden on many machines. + + If you changed your build settings to include TERMCAP support, copy + termcap to your game directory. + + Also, make sure the file msdoshlp.txt made it to your game directory. + If it didn't, move it from sys\msdos to your game directory + yourself. + +10. In your game directory, review the settings in defaults.nh and adjust + them according to your style of play. + +11. Play NetHack. If it works, you're done! + +Appendix A - Building the "official binary" + + If you wish to build a copy of NetHack identical to the one that + the pc team distributes, simply do the following: + + The 32-bit Protected Mode DPMI version built with 32-bit djgpp + compiler V2.03 or greater, make no changes to any of the defines and use + the Makefile.GCC as distributed, and as moved in step 3. + + Paths below are relative to the top of your unpacked + NetHack source distribution: + + md \nethack\binary (must match Makefile) + cd sys\msdos + setup GCC + cd ..\..\src + make install + + + Make sure the following files have been converted from the + unix style "^J" end of line, to the msdos style "^M^J": + license, defaults.nh. + + Place all the files in a clean directory and test. + +Appendix B - DJGPP Compiler (gcc ported to msdos) + + If you have a 386 or better machine, you are in luck. You can compile + NetHack without spending money on a compiler. DJGPP is available free + from many archive sites. + At the time of this release in April 2002, the URL + http://www.delorie.com/djgpp/zip-picker.html/ + had information on how to obtain djgpp and what pieces to get. + Be sure to pick up djgpp v2gnu/fil41b.zip to get ls.exe and + touch.exe, since the Makefile uses them by default (or change + the Makefile to use alternatives). + + Special note for Windows 2000 / Windows XP users: You must have a + recent djgpp distribution for the build process, and the generated + executables to work properly on those platforms. + + Setting up DJGPP is more than adequately explained in the documentation + that comes with it. Be sure to pick up the yacc and flex built with + DJGPP if you intend to do any modification of the special levels or + dungeon compilers. They should be available at the same place you got + djgpp. + + The latest version of djgpp, V2.03 with the most recent refresh + will produce a binary that will run under Microsoft Windows, or any + other DPMI provider. djgpp also comes with a DPMI provider called CWSDPMI. + Place CWSDPMI.EXE in your path and it will be used in the absence of any + other DPMI provider. + + If you want to use the built-in DJGPP screen routines, uncomment + SCREEN_DJGPPFAST in pcconf.h (the default for djgpp). + +Appendix C - Additional Notes + +1) Save files and bones files from versions of NetHack prior to 3.4.0 will not + work with this NetHack. Don't bother trying to keep them. + +2) To install an update of NetHack after changing something, type 'make' + for DJGPP from the src directory. If you add, delete, or reorder monsters or + objects, or you change the format of saved level files, delete any save + and bones files. (Trying to use such files sometimes produces amusing + confusions on the game's part, but usually crashes.) + + +Appendix D - Contacting the Development Team + + If you discover a bug and wish to report it, or if you have comments + or suggestions we recommend using + our "Contact Us" web page at: + http://www.nethack.org/common/contact.html + + If you don't have access to the web, or you want to send us a patch + to the NetHack source code feel free to drop us a line c/o: + DevTeam (at) nethack.org + diff --git a/sys/msdos/Makefile.BC b/sys/msdos/Makefile.BC new file mode 100644 index 0000000..7008160 --- /dev/null +++ b/sys/msdos/Makefile.BC @@ -0,0 +1,2081 @@ +# SCCS Id: @(#)Makefile.BC 3.4 2002/03/17 +# Copyright (c) Yitzhak Sapir, 1999-2002. +# NetHack may be freely distributed. See license for details. +# + +# PC NetHack 3.4 Makefile for Borland C++ 3.1 and 4.5. +# +# Nota Bene: Before you get to here you should have already read +# the Install.dos file located in the sys/msdos directory. +# Additionally, you should run this makefile with the -N +# Microsoft Compatibility option. +# +# This Makefile is for use with Borland C++ version 3.1 and 4.5, but might +# also work with more up to date versions. +# +# This Makefile is specific to Borland's MAKE which is supplied with the +# compiler. It supports only one overlay management facility - VROOMM. +# (This Makefile won't work with make45l or NDMAKE) + +# +# Game Installation Variables. +# NOTE: Make sure GAMEDIR exists before nmake is started. +# + +GAME = NetHack +GAMEDIR = ..\binary + +# +# +# Directories +# + +DAT = ..\dat +DOC = ..\doc +INCL = ..\include +SRC = ..\src +OBJ = o +MSYS = ..\sys\msdos +SYS = ..\sys\share +UTIL = ..\util +WTTY = ..\win\tty +WSHR = ..\win\share + + +# +# Compiler File Info. +# ($(MAKE) macro is often predefined, so we use $(MAKEBIN) instead.) +# + +CC = bcc # Compiler +LINK = tlink # Linker +ASM = tasm # Assembler (not currently needed for BC) +MAKEBIN = make +UUDECODE = uudecode # Unix style uudecoder + +#BCTOP = c:\borlandc # main Borland C++ directory +BCTOP = c:\bc31 + +# +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), comment out the upper two lines below, and uncomment +# the lower two. +# +# On Borland C++, the newest versions of flex and bison provide +# problems when run from MAKE. +# + +DO_YACC = YACC_MSG +DO_LEX = LEX_MSG +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +# +# - Specify your yacc and lex programs (or work-alikes for each) here. +# + +YACC = bison -y +#YACC = yacc +#YACC = byacc + +LEX = flex +#LEX = lex + +# +# - Specify your flex skeleton file (if needed). +# +FLEXSKEL = +#FLEXSKEL = -Sc:\tools16\flex.ske + +# +# - Your yacc (or work-alike) output files +# +YTABC = y_tab.c +YTABH = y_tab.h +#YTABC = ytab.c +#YTABH = ytab.h + +# +# - Your lex (or work-alike) output files +# +LEXYYC = lexyy.c +#LEXYYC = lex.yy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# If TERMLIB is #defined in the source (in include\pcconf.h), +# comment out the upper line and uncomment the lower. Make sure +# that TERMLIB contains the full pathname to the termcap library. + +TERMLIB = +#TERMLIB = $(SYS)\termcap.lib + +# +# MEMORY USAGE AND OVERLAYING +# +# Overlay Schema 1 +# +# - Minimal extended memory available, lots of 640K base RAM free +# Minimize overlay turns. Requires that a minimum of +# 607K RAM be free as follows: +# 462K Executable load requirement +# 115K for malloc() calls +# 30K Overlay buffer +# 607K Total memory requirement +# +# Overlay Schema 2 +# +# - Favor small load size, requires extended memory for bearable performance. +# If you have very little base 640K RAM available, but lots of extended +# memory for caching overlays, you might try this. (eg. A machine with +# lots of TSR's or network drivers). Do not try to set SCHEMA = 2 +# without a disk cache and extended memory. +# 381K Executable load requirement +# 115K for malloc() calls +# 30K Overlay buffer +# 526K Total memory requirement +# +# On Borland C++, you have to make a full rebuild of all object modules each +# time you change schemas. +# + +SCHEMA = 2 + +# +# OPTIONAL TILE SUPPORT. +# +# This release of NetHack allows you to build a version of NetHack +# that will draw 16x16 color tiles on the display to represent +# NetHack maps, objects, monsters, etc. on machines with appropriate +# display hardware. Currently the only supported video hardware is +# VGA. +# +# Note: You can build NetHack with tile support and then choose +# whether to use it or not at runtime via the defaults.nh file option +# "video". +# + +TILESUPPORT = Y + +# +# C COMPILER AND LINKER SETTINGS +# +# For debugging ability, comment out the upper three +# macros and uncomment the lower three. You can also +# uncomment only either LDFLAGSU or LDFLAGSN if you +# want to include debug information only in the utilities +# or only in the game file. + +# On Borland C++, you cannot include debug information for +# all the object modules because the linker cannot handle +# it. + +#CDFLAGS = +LDFLAGSN = +#LDFLAGSU = + +CDFLAGS = -v -vi # use debug info (compiler) +#LDFLAGSN = /v # use debug info (linker - game) +LDFLAGSU = /v # use debug info (linker - utilities) + +# +# - Don't warn about unreachable code because flex generates a whole bunch +# of unreachable code warnings, which stops the compile process. +# + +CW = -w-rch + +# +# Select whether to use pre-compiled headers or not. +# Set PRECOMPHEAD to Y to use pre-compiled headers, set it to anything +# else and pre-compiled headers will not be used. +# (Pre-compiled headers speed up compiles, but require a bit more +# disk space during the build. The pre-compiled headers can be deleted +# afterwards via DEL *.PCH if desired). +# + +PRECOMPHEAD = N + +# +# C Compiler Flags +# + +CFLAGS = -c + +# Uncomment the line below if you want to store all the level files, +# help files, etc. in a single library file (recommended). + +USE_DLB = Y + +# +######################################################################## +######################################################################## +# +# Nothing below here should have to be changed. +# +######################################################################## +######################################################################## +# +# Warning: +# +# Changing anything below here means that you should be *very* +# familiar with your compiler's workings, *very* knowledgeable +# about the overlay structure and mechanics of NetHack, and *very* +# confident in your understanding of Makefiles and Make utilities. +# +######################################################################## +# +# Default Make Procedure +# + +default: $(GAME) + +# +######################################################################## +# Tile preparation +# + +! IF ("$(TILESUPPORT)"=="Y") + +TILEGAME = $(OBJ)\tile.o $(OBJ)\pctiles.0 $(OBJ)\pctiles.b + +# +# - VGA Tile Support, uncomment these three lines. +# + +TILEVGA = $(OBJ)\vidvga.0 $(OBJ)\vidvga.1 $(OBJ)\vidvga.2 $(OBJ)\vidvga.b +PLANAR_TIB = NetHack1.tib +OVERVIEW_TIB = NetHacko.tib + +# +# Leave this line uncommented and unchanged. +TILEUTIL = $(TILEGAME) $(TILEVGA) $(UTIL)\tile2bin.exe $(UTIL)\til2bin2.exe \ + $(PLANAR_TIB) $(OVERVIEW_TIB) + +! ENDIF + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +# +############################################################################# +# +# General Overlay Schema Settings +# + +OVLINIT =$(OBJ)\ovlinit.o + + +# +############################################################################# +# +# C Compiler and Linker Setup Options +# (To Maintainer; modify only if absolutely necessary) +# + +BCINCL = $(BCTOP)\include # include directory for main BC headers +BCLIB = $(BCTOP)\lib # library directory for main BC libraries +BCCFG = nethack.cfg # name of the nethack configuration file +VROOMMCFG= vroomm.cfg # name of file with code segment information + +# +# Model +# + +MODEL = h + +# +# - Optional C library specifier for those with non-standard +# libraries or a multiple-target library setup. +# + +CLIB = + +# +# Borland C++ libraries +# + +BCOVL = $(BCLIB)\OVERLAY +BCMDL = $(BCLIB)\C$(MODEL) + +# +# Compiler Options +# + +CNOLNK = -c # just generate .OBJ +CPCHUSE = -Hu # use precompiled headers +CPCHGEN = -H # generate precompiled headers +CPCHNAM = -H= # set the name of the precompiled header file +CPCHEXT = .PCH # precompiled header extension +CDEFINE = -D # define a macro +CSTKSZ = -DSTKSIZ= # set stack size +CCSNAM = -zC # set the code segment name +COBJNAM = -o # name the .OBJ file + +# +# Linker Options +# + +LWCASE = /c # treat case as significant +LMAP = /m # create map file +LINIT = $(BCLIB)\C0$(MODEL) # initialization object file +LOVL = /oOVLY # overlay all needed segments + +# +# Stack Sizes +# + +STKSUTL = 4096 # Utilities Stack Size +STKSNRM = 5120 # Normal Stack Size + +CUSTACK = $(CSTKSZ)$(STKSUTL) # Utilities Stack Set for Compiler +CNSTACK = $(CSTKSZ)$(STKSNRM) # Normal Stack Set for Compiler + + +# +######################################################################## +# DLB preparation +# + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = $(CDEFINE)DLB +! ELSE +DLBFLG = +! ENDIF + +# +######################################################################## +# tile preparation +# + +! IF ("$(TILESUPPORT)"=="Y") +TILFLG = $(CDEFINE)USE_TILES +! ELSE +TILFLG = +! ENDIF + +############################################################################# +# +# Overlay switches +# + +COVL0 = $(CDEFINE)OVL0 +COVL1 = $(CDEFINE)OVL1 +COVL2 = $(CDEFINE)OVL2 +COVL3 = $(CDEFINE)OVL3 +COVLB = $(CDEFINE)OVLB + +# +# Flags +# + +FLAGOPT = $(DLBFLG) $(TILFLG) + +# +# Precompiled Header Section +# + +#common options (placed in $(BCCFG)) +CFLGTOT = $(CDFLAGS) $(CFLAGS) $(FLAGOPT) $(CW) +#util builds +CFLAGSU = $(CUSTACK) +$(VROOMMCFG) +#normal build, no PCH +CFLAGSN = $(CNSTACK) +$(VROOMMCFG) +#no optimizations +CFLAGNO = $(CNOOPT) $(CFLAGSN) + +! IF ("$(PRECOMPHEAD)"!="Y") + +CFLAGCO = $(COVLO) +CFLAGUO = $(COVLO) +CFLAGC0 = $(COVL0) +CFLAGU0 = $(COVL0) +CFLAGC1 = $(COVL1) +CFLAGU1 = $(COVL1) +CFLAGC2 = $(COVL2) +CFLAGU2 = $(COVL2) +CFLAGC3 = $(COVL3) +CFLAGU3 = $(COVL3) +CFLAGCB = $(COVLB) +CFLAGUB = $(COVLB) +PCHO = +PCH0 = +PCH1 = +PCH2 = +PCH3 = +PCHB = + +precomp.msg: + @echo Not using precompiled headers... + +! ELSE + +# .o files +CFLAGUO = $(CPCHUSE) $(CPCHNAM)PHO$(CPCHEXT) $(COVLO) +CFLAGCO = $(CPCHGEN) $(CPCHNAM)PHO$(CPCHEXT) $(COVLO) +PCHO = PHO$(CPCHEXT) +# .0 files +CFLAGU0 = $(CPCHUSE) $(CPCHNAM)PH0$(CPCHEXT) $(COVL0) +CFLAGC0 = $(CPCHGEN) $(CPCHNAM)PH0$(CPCHEXT) $(COVL0) +PCH0 = PH0$(CPCHEXT) +# .1 files +CFLAGU1 = $(CPCHUSE) $(CPCHNAM)PH1$(CPCHEXT) $(COVL1) +CFLAGC1 = $(CPCHGEN) $(CPCHNAM)PH1$(CPCHEXT) $(COVL1) +PCH1 = PH1$(CPCHEXT) +# .2 files +CFLAGU2 = $(CPCHUSE) $(CPCHNAM)PH2$(CPCHEXT) $(COVL2) +CFLAGC2 = $(CPCHGEN) $(CPCHNAM)PH2$(CPCHEXT) $(COVL2) +PCH2 = PH2$(CPCHEXT) +# .3 files +CFLAGU3 = $(CPCHUSE) $(CPCHNAM)PH3$(CPCHEXT) $(COVL3) +CFLAGC3 = $(CPCHGEN) $(CPCHNAM)PH3$(CPCHEXT) $(COVL3) +PCH3 = PH3$(CPCHEXT) +# .B files +CFLAGUB = $(CPCHUSE) $(CPCHNAM)PHB$(CPCHEXT) $(COVLB) +CFLAGCB = $(CPCHGEN) $(CPCHNAM)PHB$(CPCHEXT) $(COVLB) +PCHB = PHB$(CPCHEXT) + +precomp.msg: + @echo Using precompiled headers... + +! ENDIF + + +FLAGCO = $(CNSTACK) +$(VROOMMCFG) +FLAGUO = $(CNSTACK) +$(VROOMMCFG) +FLAGC0 = $(CNSTACK) +$(VROOMMCFG) +FLAGU0 = $(CNSTACK) +$(VROOMMCFG) +FLAGC1 = $(CNSTACK) +$(VROOMMCFG) +FLAGU1 = $(CNSTACK) +$(VROOMMCFG) +FLAGC2 = $(CNSTACK) +$(VROOMMCFG) +FLAGU2 = $(CNSTACK) +$(VROOMMCFG) +FLAGC3 = $(CNSTACK) +$(VROOMMCFG) +FLAGU3 = $(CNSTACK) +$(VROOMMCFG) +FLAGCB = $(CNSTACK) +$(VROOMMCFG) +FLAGUB = $(CNSTACK) +$(VROOMMCFG) + +# End of Pre-compiled header section +#=========================================================================== + +# +# Basic Borland C++ option line +# +BCOPTS1 = -Y -O -Z -Oe -Ob -Os -Ff -I$(BCINCL);$(INCL) -m$(MODEL) +BCOPTS2 = $(CDEFINE)__IO_H $(CFLGTOT) -DSTRNCMPI + +# +# Linker options for building various things. +# + +LFLAGSU = $(LDFLAGSU) $(LUSTACK) $(LINIT) +LFLAGSN = $(LDFLAGSN) $(LNSTACK) $(LWCASE) $(LMAXSEG) $(INTOVL) $(LMAXALL) \ + $(LINFO) $(LINIT) $(LOVL) + +# +# Make Roolz dude. +# Due to the inadequacy of some makes these must accord with a +# topological sort of the generated-from relation... output on +# the left, input on the right. Trust me. +# + +.SUFFIXES: .exe .0 .1 .2 .3 .B .o .til .uu .c .y .l + +# +# Rules for files in src +# + + +.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.0: + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.1: + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.2: + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.3: + @type schema$(SCHEMA).bc | find "$(@B)_3" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU3) >> $(VROOMMCFG) + $(CC) $(FLAGU3) $(COBJNAM)$@ $< + +{$(SRC)}.c{$(OBJ)}.B: + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $< + +# +# Rules for files in sys\share +# + +{$(SYS)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(SYS)}.c{$(OBJ)}.0: + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $< + +{$(SYS)}.c{$(OBJ)}.1: + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $< + +{$(SYS)}.c{$(OBJ)}.2: + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $< + +{$(SYS)}.c{$(OBJ)}.3: + @type schema$(SCHEMA).bc | find "$(@B)_3" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU3) >> $(VROOMMCFG) + $(CC) $(FLAGU3) $(COBJNAM)$@ $< + +{$(SYS)}.c{$(OBJ)}.B: + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $< + +# +# Rules for files in sys\msdos +# + +{$(MSYS)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(MSYS)}.c{$(OBJ)}.0: + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $< + +{$(MSYS)}.c{$(OBJ)}.1: + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $< + +{$(MSYS)}.c{$(OBJ)}.2: + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $< + +{$(MSYS)}.c{$(OBJ)}.3: + @type schema$(SCHEMA).bc | find "$(@B)_3" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU3) >> $(VROOMMCFG) + $(CC) $(FLAGU3) $(COBJNAM)$@ $< + +{$(MSYS)}.c{$(OBJ)}.B: + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $< + +{$(MSYS)}.h{$(INCL)}.h: + @copy $< $@ + +# +# Rules for files in util +# + +{$(UTIL)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) (COBJNAM)$@ $< + +# +# Rules for files in win\share +# + +{$(WSHR)}.c.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + @$(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(WSHR)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + @$(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +{$(WSHR)}.txt{$(DAT)}.txt: + @copy $< $@ + +# +# Rules for files in win\tty +# + +{$(WTTY)}.c{$(OBJ)}.o: + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $< + +{$(WTTY)}.c{$(OBJ)}.0: + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $< + +{$(WTTY)}.c{$(OBJ)}.1: + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $< + +{$(WTTY)}.c{$(OBJ)}.2: + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $< + +{$(WTTY)}.c{$(OBJ)}.3: + @type schema$(SCHEMA).bc | find "$(@B)_3" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU3) >> $(VROOMMCFG) + $(CC) $(FLAGU3) $(COBJNAM)$@ $< + +{$(WTTY)}.c{$(OBJ)}.B: + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $< + +# +# NETHACK OBJECTS +# +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +# +# Shorten up the location for some files +# + +O = $(OBJ)\ # comment so \ isn't last char + +U = $(UTIL)\ # comment so \ isn't last char + +SPLEVDES = $(DAT)\Arch.des $(DAT)\Barb.des $(DAT)\bigroom.des \ + $(DAT)\castle.des $(DAT)\Caveman.des $(DAT)\endgame.des \ + $(DAT)\gehennom.des $(DAT)\Healer.des $(DAT)\Knight.des \ + $(DAT)\knox.des $(DAT)\Monk.des $(DAT)\medusa.des \ + $(DAT)\mines.des $(DAT)\oracle.des $(DAT)\Priest.des \ + $(DAT)\Ranger.des $(DAT)\Rogue.des $(DAT)\Samurai.des \ + $(DAT)\Tourist.des $(DAT)\tower.des $(DAT)\Valkyrie.des \ + $(DAT)\Wizard.des $(DAT)\yendor.des + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS =$(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o $(O)monst.o \ + $(O)objects.o $(O)panic.o $(O)stubvid.o + +DGNCOMPOBJS =$(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +GIFREADERS =$(O)gifread.o $(O)alloc.o $(O)panic.o + +TEXT_IO =$(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o $(O)stubvid.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +GIFREAD2 =$(O)gifread2.o $(O)alloc.o $(O)panic.o + +TEXT_IO2 =$(O)tiletex2.o $(O)tiletxt2.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o $(O)stubvid.o + +PPMWRIT2 = $(O)ppmwrit2.o $(O)alloc.o $(O)panic.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +TILEFILES2 = $(WSHR)\monthin.txt $(WSHR)\objthin.txt $(WSHR)\oththin.txt + +DLBOBJS = $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +OBJ01 = $(O)alloc.o $(RANDOM) $(O)decl.o $(O)objects.o \ + $(O)muse.o $(O)display.o $(O)vision.o $(O)mapglyph.o \ + $(O)rect.o $(O)vis_tab.o $(O)monst.o $(O)wintty.o \ + $(O)files.o $(O)sys.o $(O)monstr.o $(O)minion.o \ + $(O)worm.o $(O)detect.o $(O)exper.o $(O)mplayer.o \ + $(O)uhitm.o $(O)pager.o $(O)windows.o $(O)quest.o \ + $(O)questpgr.o $(O)write.o $(O)drawing.o $(O)dokick.o \ + $(O)dothrow.o $(O)pickup.o $(O)pray.o $(O)spell.o \ + $(O)ball.o $(O)wield.o $(O)worn.o $(O)fountain.o \ + $(O)music.o $(O)rumors.o $(O)dlb.o $(O)sit.o \ + $(O)bones.o $(O)mklev.o $(O)save.o $(O)restore.o \ + $(O)mkmaze.o $(O)mkmap.o $(O)end.o $(O)o_init.o \ + $(O)options.o $(O)rip.o $(O)sound.o $(O)teleport.o \ + $(O)topten.o $(O)tty.o $(O)u_init.o $(O)extralev.o \ + $(O)sp_lev.o $(O)dig.o $(O)pckeys.o $(O)role.o \ + $(O)steed.o $(O)region.o + +OVL0 = $(O)allmain.0 $(O)apply.0 $(O)artifact.0 $(O)attrib.0 \ + $(O)botl.0 $(O)cmd.0 $(O)dbridge.0 $(O)do.0 \ + $(O)do_name.0 $(O)do_wear.0 $(O)dogmove.0 $(O)dungeon.0 \ + $(O)eat.0 $(O)engrave.0 $(O)hacklib.0 $(O)invent.0 \ + $(O)lock.0 $(O)pcmain.0 $(O)mail.0 $(O)makemon.0 \ + $(O)mcastu.0 $(O)mhitm.0 $(O)mhitu.0 $(O)mkobj.0 \ + $(O)mkroom.0 $(O)mon.0 $(O)mondata.0 $(O)monmove.0 \ + $(O)mthrowu.0 $(O)objnam.0 $(O)polyself.0 $(O)priest.0 \ + $(O)rnd.0 $(O)shknam.0 $(O)sounds.0 $(O)steal.0 \ + $(O)timeout.0 $(O)track.0 $(O)trap.0 $(O)vault.0 \ + $(O)weapon.0 $(O)were.0 $(O)wizard.0 $(O)msdos.0 \ + $(O)termcap.0 $(O)video.0 $(O)vidtxt.0 $(O)zap.0 \ + $(O)explode.0 $(O)shk.0 + +OVL1 = $(O)allmain.1 $(O)apply.1 $(O)artifact.1 $(O)attrib.1 \ + $(O)botl.1 $(O)cmd.1 $(O)dbridge.1 $(O)do.1 \ + $(O)do_wear.1 $(O)dog.1 $(O)dungeon.1 $(O)eat.1 \ + $(O)engrave.1 $(O)hack.1 $(O)hacklib.1 $(O)invent.1 \ + $(O)makemon.1 $(O)mhitu.1 $(O)mkobj.1 $(O)mon.1 \ + $(O)mondata.1 $(O)monmove.1 $(O)mthrowu.1 $(O)objnam.1 \ + $(O)pcmain.1 $(O)polyself.1 $(O)rnd.1 $(O)shk.1 \ + $(O)steal.1 $(O)timeout.1 $(O)track.1 $(O)trap.1 \ + $(O)weapon.1 $(O)getline.1 $(O)termcap.1 $(O)topl.1 \ + $(O)video.1 $(O)zap.1 $(O)explode.1 + +OVL2 = $(O)attrib.2 $(O)do.2 $(O)do_name.2 $(O)do_wear.2 \ + $(O)dog.2 $(O)engrave.2 $(O)hack.2 $(O)hacklib.2 \ + $(O)invent.2 $(O)makemon.2 $(O)mon.2 $(O)mondata.2 \ + $(O)monmove.2 $(O)getline.2 $(O)shk.2 $(O)topl.2 \ + $(O)trap.2 $(O)zap.2 + +OVL3 = $(O)do.3 $(O)hack.3 $(O)invent.3 $(O)light.3 \ + $(O)shk.3 $(O)trap.3 $(O)zap.3 + + +OVLB = $(O)allmain.B $(O)apply.B $(O)artifact.B $(O)attrib.B \ + $(O)botl.B $(O)cmd.B $(O)dbridge.B $(O)do.B \ + $(O)do_name.B $(O)do_wear.B $(O)dog.B $(O)dogmove.B \ + $(O)eat.B $(O)engrave.B $(O)hack.B $(O)hacklib.B \ + $(O)invent.B $(O)lock.B $(O)mail.B $(O)makemon.B \ + $(O)mcastu.B $(O)mhitm.B $(O)mhitu.B $(O)mkobj.B \ + $(O)mkroom.B $(O)mon.B $(O)mondata.B $(O)monmove.B \ + $(O)mthrowu.B $(O)objnam.B $(O)pcmain.B $(O)pline.B \ + $(O)polyself.B $(O)potion.B $(O)priest.B $(O)read.B \ + $(O)rnd.B $(O)shk.B $(O)shknam.B $(O)sounds.B \ + $(O)steal.B $(O)timeout.B $(O)track.B $(O)trap.B \ + $(O)vault.B $(O)weapon.B $(O)were.B $(O)wizard.B \ + $(O)msdos.B $(O)pcunix.B $(O)termcap.B $(O)topl.B \ + $(O)video.B $(O)vidtxt.B $(O)zap.B + +TILOBJ = $(TILEGAME) $(TILEVGA) + +VVOBJ = $(O)version.o + +NVOBJ = $(OBJ01) $(OVL0) $(OVL1) $(OVL2) \ + $(OVL3) $(OVLB) $(TILOBJ) + +ALLOBJ= $(NVOBJ) $(VVOBJ) $(OVLINIT) + +# +# Header objects +# + +# This comment copied from sys/unix/Makefile.src, +# extern.h is ignored, even though its declared function types may affect the +# compilation of all the .c files, since extern.h changes every time the +# type of an external function does, and we would spend all our time recompiling +# if we did not ignore it. +#EXTERN_H = $(INCL)\extern.h +EXTERN_H = +PCCONF_H = $(INCL)\pcconf.h $(INCL)\micro.h $(INCL)\system.h +PERMONST_H = $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\align.h +YOUPROP_H = $(INCL)\prop.h $(PERMONST_H) $(INCL)\pm.h $(INCL)\youprop.h \ + $(INCL)\mondata.h +YOU_H = $(INCL)\attrib.h $(INCL)\monst.h $(YOUPROP_H) $(INCL)\align.h +DECL_H = $(INCL)\quest.h $(INCL)\spell.h $(INCL)\color.h \ + $(INCL)\obj.h $(YOU_H) $(INCL)\onames.h $(INCL)\pm.h + +CONFIG_H = $(INCL)\config1.h $(INCL)\tradstdc.h $(INCL)\coord.h $(PCCONF_H) \ + $(INCL)\config.h +HACK_H = $(CONFIG_H) $(INCL)\dungeon.h $(INCL)\align.h $(INCL)\monsym.h \ + $(INCL)\mkroom.h $(INCL)\objclass.h $(DECL_H) \ + $(INCL)\timeout.h $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\mondata.h $(INCL)\wintype.h \ + $(INCL)\engrave.h $(INCL)\rect.h $(EXTERN_H) \ + $(INCL)\winprocs.h $(INCL)\trampoli.h $(INCL)\display.h +TILE_H = $(INCL)\tile.h $(INCL)\pctiles.h +PCVIDEO_H = $(INCL)\portio.h $(INCL)\pcvideo.h +ALIGN_H = $(INCL)\align.h +ARTIFACT_H = $(INCL)\artifact.h +ARTILIST_H = $(INCL)\artilist.h +COLOR_H = $(INCL)\color.h +DATE_H = $(INCL)\date.h +DGN_FILE_H = $(INCL)\dgn_file.h +DLB_H = $(INCL)\dlb.h +EMIN_H = $(INCL)\emin.h +EPRI_H = $(INCL)\epri.h +ESHK_H = $(INCL)\eshk.h +EDOG_H = $(INCL)\edog.h +FUNC_TAB_H = $(INCL)\func_tab.h +LEV_H = $(INCL)\lev.h +LEV_COMP_H = $(INCL)\lev_comp.h +MAIL_H = $(INCL)\mail.h +MFNDPOS_H = $(INCL)\mfndpos.h +MONSYM_H = $(INCL)\monsym.h +OBJ_H = $(INCL)\obj.h +OBJCLASS_H = $(INCL)\objclass.h +OBJECTS_H = $(INCL)\objects.h +PROP_H = $(INCL)\prop.h +QTEXT_H = $(INCL)\qtext.h +QUEST_H = $(INCL)\quest.h +SP_LEV_H = $(INCL)\sp_lev.h +TERMCAP_H = $(INCL)\tcap.h +VAULT_H = $(INCL)\vault.h +VIS_TAB_H = $(INCL)\vis_tab.h +WINTTY_H = $(INCL)\wintty.h + +# +# In the unix distribution this file is patchlevel.h, make it 8.3 here +# to avoid an nmake warning under dos. +# + +PATCHLEVEL_H = $(INCL)\patchlev.h + + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +# +# make data.base an 8.3 filename to prevent an nmake warning +# + +DATABASE = $(DAT)\data.bas + +####################################################################### +# +# TARGETS + +# +# The main target. +# + +$(GAME): obj.tag envchk $(U)utility.tag $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: $(GAME) install.tag + @echo Done. + + +install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(DAT)\sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + copy $(MSYS)\msdoshlp.txt $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + copy $(SYS)\termcap $(GAMEDIR) + if exist $(DOC)\guideb*.txt copy $(DOC)\guideb*.txt $(GAMEDIR) + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR) + copy $(SYS)\NetHack.cnf $(GAMEDIR)\defaults.nh + copy $(MSYS)\NHAccess.nh $(GAMEDIR) + copy $(U)recover.exe $(GAMEDIR) + if exist *.tib copy *.tib $(GAMEDIR) + echo install done > $@ + +$(DAT)\sp_lev.tag: $(U)utility.tag $(SPLEVDES) + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) + echo sp_levs done > $(DAT)\sp_lev.tag + +$(U)utility.tag: envchk $(INCL)\date.h $(INCL)\onames.h \ + $(INCL)\pm.h $(SRC)\monstr.c $(SRC)\vis_tab.c \ + $(U)lev_comp.exe $(VIS_TAB_H) $(U)dgn_comp.exe \ + $(U)recover.exe $(TILEUTIL) + @echo utilities made >$@ + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +# +# Inline files : +# Specifying the "<<" means to start an inline file. +# Another "<<" at the start of a line closes the +# inline file. +# +# DO NOT INDENT THE << below! +# + +$(GAMEFILE) : $(ALLOBJ) + @echo Linking.... + $(LINK) $(LFLAGSN) @<<$(GAME).lnk + $(ALLOBJ) + $(GAMEFILE) + $(GAME) + $(TERMLIB) $(MOVETR) $(CLIB) $(BCOVL) $(BCMDL) +<< + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +# +# Makedefs Stuff +# + +$(U)makedefs.exe: $(MAKEOBJS) + @$(LINK) $(LFLAGSU) $(MAKEOBJS), $@,, $(CLIB) $(BCMDL); + +$(O)makedefs.o: $(CONFIG_H) $(PERMONST_H) $(OBJCLASS_H) \ + $(MONSYM_H) $(QTEXT_H) $(PATCHLEVEL_H) \ + $(U)makedefs.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h : $(U)makedefs.exe + $(U)makedefs -v + @echo A new $@ has been created. + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +# +# Level Compiler Stuff +# + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + $(LINK) $(LFLAGSU) @&&! + $(O)stubvid.o $(O)panic.o $(O)objects.o $(O)monst.o + + $(O)drawing.o $(O)decl.o $(O)alloc.o $(O)lev_main.o + + $(O)lev_$(LEX).o $(O)lev_yacc.o + $@ + $(@B) + $(BCMDL); +! + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)lev_main.c + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + $(YACC) -d -l $(U)lev_comp.y + copy $(YTABC) $(U)lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) +! ELSE + @echo. + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo. + @echo For now, we will copy the prebuilt lev_yacc.c + @echo from $(SYS) to $(U)lev_yacc.c, and copy the prebuilt + @echo lev_comp.h from $(SYS) to $(UTIL)\lev_comp.h + @echo and use those. + @echo. + copy $(SYS)\lev_yacc.c $@ >nul + touch $@ + copy $(SYS)\lev_comp.h $(INCL)\lev_comp.h >nul + touch $(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + $(LEX) $(FLEXSKEL) $(U)lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) +! ELSE + @echo. + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo. + @echo For now, we will copy a prebuilt lev_lex.c + @echo from $(SYS) to $@ and use it. + @echo. + copy $(SYS)\lev_lex.c $@ >nul + touch $@ +! ENDIF + +# +# Dungeon Stuff +# + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + $(LINK) $(LFLAGSU) @&&! + $(O)panic.o $(O)alloc.o $(O)dgn_main.o $(O)dgn_$(LEX).o + + $(O)dgn_yacc.o + $@ + $(@B) + $(BCMDL); +! + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_yacc.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + @$(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + $(YACC) -d -l $(U)dgn_comp.y + copy $(YTABC) $(U)dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) +! ELSE + @echo. + @echo $(U)dgn_comp.y has changed. To update $@ and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo. + @echo For now, we will copy the prebuilt dgn_yacc.c from + @echo $(SYS) to $(U)dgn_yacc.c, and copy the prebuilt + @echo dgn_comp.h from $(SYS) to $(INCL)\dgn_comp.h + @echo and use those. + @echo. + copy $(SYS)\dgn_yacc.c $@ >nul + touch $@ + copy $(SYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + touch $(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + $(LEX) $(FLEXSKEL) $(U)dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) +! ELSE + @echo. + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo. + @echo For now, we will copy a prebuilt dgn_lex.c + @echo from $(SYS) to $@ and use it. + @echo. + copy $(SYS)\dgn_lex.c $@ >nul + touch $@ +! ENDIF + + +obj.tag: + @if not exist $(O)*.* mkdir $(OBJ) + @echo directory $(OBJ) created + @echo directory $(OBJ) created >$@ + +envchk: precomp.msg +! IF "$(TILEGAME)"=="" + @echo. + @echo NOTE: This build will NOT include tile support. + @echo. +! ELSE + @echo. + @echo This build includes tile support. + @echo. +! ENDIF + +# +# SECONDARY TARGETS +# + +# +# Header files NOT distributed in ..\include +# + +$(INCL)\tile.h: $(WSHR)\tile.h + copy $(WSHR)\tile.h $@ + +$(INCL)\pctiles.h: $(MSYS)\pctiles.h + copy $(MSYS)\pctiles.h $@ + +$(INCL)\pcvideo.h: $(MSYS)\pcvideo.h + copy $(MSYS)\pcvideo.h $@ + +$(INCL)\portio.h: $(MSYS)\portio.h + copy $(MSYS)\portio.h $@ + +# +# Recover Utility +# + +$(U)recover.exe: $(RECOVOBJS) + @$(LINK) $(LFLAGSU) $(RECOVOBJS),$@,, $(CLIB) $(BCMDL); + +# +# Tile Mapping +# + +$(SRC)\tile.c: $(U)tilemap.exe + @echo A new $@ is being created. + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + @$(LINK) $(LFLAGSU) $(O)tilemap.o,$@,, $(CLIB) $(BCMDL); + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(WSHR)\tilemap.c + + +# +# Tile Utilities +# + +# +# Optional (for development) +# + + + +# + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + @$(LINK) $(LFLAGSU) << $(@B).lnk + $(GIFREADERS) $(TEXT_IO) + $@,,$(CLIB) $(BCMDL) +<< + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + @$(LINK) $(LFLAGSU) << $(@B).lnk + $(PPMWRITERS) $(TEXT_IO) + $@,,$(CLIB) $(BCMDL); +<< + +$(U)gif2txt2.exe: $(GIFREAD2) $(TEXT_IO2) + @$(LINK) $(LFLAGSU) << $(@B).lnk + $(GIFREAD2) $(TEXT_IO2) + $@,,$(CLIB) $(BCMDL); +<< + +$(U)txt2ppm2.exe: $(PPMWRIT2) $(TEXT_IO2) + @$(LINK) $(LFLAGSU) << $(@B).lnk + $(PPMWRIT2) $(TEXT_IO2) + $@,,$(CLIB) $(BCMDL); +<< + +# +# Required for tile support +# + +NetHack1.tib: $(TILEFILES) $(U)tile2bin.exe + @echo Creating binary tile files (this may take some time) + @$(U)tile2bin + +NetHackO.tib: thintile.tag $(TILEFILES2) $(U)til2bin2.exe + @echo Creating overview binary tile files (this may take some time) + @$(U)til2bin2 + +thintile.tag: $(U)thintile.exe $(TILEFILES) + $(U)thintile + @echo thintiles created >thintile.tag + +$(U)tile2bin.exe: $(O)tile2bin.o $(TEXT_IO) + @echo Linking $@... + $(LINK) $(LFLAGSU) @&&! + $(O)tile2bin.o+ + $(O)stubvid.o $(O)objects.o $(O)monst.o $(O)decl.o + + $(O)drawing.o $(O)tiletxt.o $(O)tiletext.o + $@ + $(@B) + $(BCMDL); +! + +$(U)til2bin2.exe: $(O)til2bin2.o $(TEXT_IO2) + @echo Linking $@... + $(LINK) $(LFLAGSU) @&&! + $(O)til2bin2.o+ + $(O)stubvid.o $(O)objects.o $(O)monst.o $(O)decl.o + + $(O)drawing.o $(O)tiletxt2.o $(O)tiletex2.o + $@ + $(@B) + $(BCMDL); +! + + +$(U)thintile.exe: $(O)thintile.o + @$(LINK) $(LFLAGSU) $(O)thintile.o,$@,, $(CLIB) $(BCMDL); + +$(O)thintile.o: $(HACK_H) $(INCL)\tile.h $(WSHR)\thintile.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(WSHR)\thintile.c + +$(O)tile2bin.o: $(HACK_H) $(TILE_H) $(PCVIDEO_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(MSYS)\tile2bin.c + +$(O)til2bin2.o: $(HACK_H) $(TILE_H) $(PCVIDEO_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(CDEFINE)TILE_X=8 $(CDEFINE)OVERVIEW_FILE \ + $(COBJNAM)$@ $(MSYS)\tile2bin.c + + +# +# DLB stuff +# + +nhdat: $(U)dlb_main.exe + @copy $(MSYS)\msdoshlp.txt $(DAT) + @cd $(DAT) + @echo data >dlb.lst + @echo oracles >>dlb.lst + @echo options >>dlb.lst + @echo quest.dat >>dlb.lst + @echo rumors >>dlb.lst + @echo help >>dlb.lst + @echo hh >>dlb.lst + @echo cmdhelp >>dlb.lst + @echo history >>dlb.lst + @echo opthelp >>dlb.lst + @echo wizhelp >>dlb.lst + @echo dungeon >>dlb.lst + @echo license >>dlb.lst + @echo msdoshlp.txt >>dlb.lst + @for %%N in (*.lev) do echo %%N >>dlb.lst + $(U)dlb_main cvIf dlb.lst $(SRC)\nhdat + @cd $(SRC) + +$(U)dlb_main.exe: $(DLBOBJS) + @$(LINK) $(LFLAGSU) $(DLBOBJS),$@,, $(CLIB) $(BCMDL); + +$(O)dlb_main.o: $(U)dlb_main.c $(INCL)\config.h $(DLB_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)dlb_main.c + +# +# Housekeeping +# + +spotless: clean + rmdir $(OBJ) + if exist $(DATE_H) del $(DATE_H) + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(VIS_TAB_H) del $(VIS_TAB_H) + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\dungeon.pdf del $(DAT)\dungeon.pdf + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(DAT)\*.lev del $(DAT)\*.lev + if exist $(DAT)\sp_lev.tag del $(DAT)\sp_lev.tag + if exist $(SRC)\monstr.c del $(SRC)\monstr.c + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\$(PLANAR_TIB) del $(SRC)\$(PLANAR_TIB) + if exist $(SRC)\$(OVERVIEW_TIB) del $(SRC)\$(OVERVIEW_TIB) + if exist $(U)recover.exe del $(U)recover.exe + +clean: + if exist $(O)*.o del $(O)*.o + if exist $(O)*.0 del $(O)*.0 + if exist $(O)*.1 del $(O)*.1 + if exist $(O)*.2 del $(O)*.2 + if exist $(O)*.3 del $(O)*.3 + if exist $(O)*.b del $(O)*.b + if exist $(U)utility.tag del $(U)utility.tag + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(U)dlb_main.exe del $(U)dlb_main.exe + if exist $(SRC)\*.lnk del $(SRC)\*.lnk + if exist $(SRC)\*.map del $(SRC)\*.map + if exist $(SRC)\*$(CPCHEXT) del $(SRC)\*$(CPCHEXT) + if exist $(SRC)\*.cfg del $(SRC)\*.cfg + if exist $(DAT)\dlb.lst del $(DAT)\dlb.lst + +pch.c: $(HACK_H) + @echo ^#include "hack.h" > $@ + @echo main(int argc, char *argv[]) >> $@ + @echo { >> $@ + @echo } >> $@ + @echo. >> $@ + +# +# OTHER DEPENDENCIES +# + +# +# Precompiled Header dependencies +# (We need to force the generation of these at the beginning) +# + +PHO$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .O files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGCO) >> $(VROOMMCFG) + @$(CC) $(FLAGCO) pch.c +PH0$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .0 files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGC0) >> $(VROOMMCFG) + @$(CC) $(FLAGC0) pch.c +PH1$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .1 files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGC1) >> $(VROOMMCFG) + @$(CC) $(FLAGC1) pch.c +PH2$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .2 files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGC2) >> $(VROOMMCFG) + @$(CC) $(FLAGC2) pch.c +PH3$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .3 files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGC3) >> $(VROOMMCFG) + @$(CC) $(FLAGC3) pch.c +PHB$(CPCHEXT): $(HACK_H) pch.c + @echo Generating new precompiled header for .B files + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGCB) >> $(VROOMMCFG) + @$(CC) $(FLAGCB) pch.c + + +# Overlay initialization routines used by pcmain() at startup to +# determine EMS/XMS memory usage. + +# Comment out the following line if you don't want Borland C++ to check for +# extended memory. +RECOGNIZE_XMS = $(CDEFINE)RECOGNIZE_XMS + + +$(O)ovlinit.o: $(MSYS)\ovlinit.c $(HACK_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(RECOGNIZE_XMS) $(COBJNAM)$@ $(MSYS)\ovlinit.c + +# +# dat dependencies +# + +$(DAT)\data: $(U)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(U)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(U)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(U)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(U)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# Util Dependencies. +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)panic.c + +$(O)recover.o: $(CONFIG_H) $(U)recover.c + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(U)recover.c + +# +# from win\share +# + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(CDEFINE)TILETEXT $(COBJNAM)$@ $(WSHR)\tilemap.c + +$(O)tiletxt2.o: $(WSHR)\tilemap.c $(HACK_H) + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(CDEFINE)TILETEXT \ + $(CDEFINE)TILE_X=8 $(COBJNAM)$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(WSHR)\gifread.c + +$(O)gifread2.o: $(WSHR)\gifread.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(CDEFINE)TILE_X=8 $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(WSHR)\ppmwrite.c + +$(O)ppmwrit2.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(CDEFINE)TILE_X=8 $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(COBJNAM)$@ $(WSHR)\tiletext.c + +$(O)tiletex2.o: $(WSHR)\tiletext.c $(CONFIG_H) $(INCL)\tile.h + @echo $(BCOPTS1) > $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSU) $(CDEFINE)TILE_X=8 $(COBJNAM)$@ $(WSHR)\tiletext.c + +# +# from win\tty +# + +$(O)getline.1: $(PCH1) $(WTTY)\getline.c $(HACK_H) $(WINTTY_H) $(FUNC_TAB_H) + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(WTTY)\getline.c + +$(O)getline.2: $(PCH2) $(WTTY)\getline.c $(HACK_H) $(WINTTY_H) $(FUNC_TAB_H) + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $(WTTY)\getline.c + +$(O)termcap.0: $(PCH0) $(WTTY)\termcap.c $(HACK_H) $(WINTTY_H) $(TERMCAP_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $(WTTY)\termcap.c + +$(O)termcap.1: $(PCH1) $(WTTY)\termcap.c $(HACK_H) $(WINTTY_H) $(TERMCAP_H) + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(WTTY)\termcap.c + +$(O)termcap.B: $(PCHB) $(WTTY)\termcap.c $(HACK_H) $(WINTTY_H) $(TERMCAP_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(WTTY)\termcap.c + +$(O)topl.1: $(PCH1) $(WTTY)\topl.c $(HACK_H) $(TERMCAP_H) $(WINTTY_H) + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(WTTY)\topl.c + +$(O)topl.2: $(PCH2) $(WTTY)\topl.c $(HACK_H) $(TERMCAP_H) $(WINTTY_H) + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $(WTTY)\topl.c + +$(O)topl.B: $(PCHB) $(WTTY)\topl.c $(HACK_H) $(TERMCAP_H) $(WINTTY_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(WTTY)\topl.c + +$(O)wintty.o: $(PCHO) $(CONFIG_H) $(WTTY)\wintty.c $(PATCHLEVEL_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $(WTTY)\wintty.c + +# +# from sys\share +# + +$(O)pcmain.0: $(PCH0) $(HACK_H) $(SYS)\pcmain.c + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $(SYS)\pcmain.c + +$(O)pcmain.1: $(PCH1) $(HACK_H) $(SYS)\pcmain.c + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(SYS)\pcmain.c + +$(O)pcmain.B: $(PCHB) $(HACK_H) $(SYS)\pcmain.c + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(SYS)\pcmain.c + +$(O)pcunix.B: $(PCHB) $(SYS)\pcunix.c $(HACK_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(SYS)\pcunix.c + +$(O)tty.o: $(HACK_H) $(WINTTY_H) $(SYS)\pctty.c + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SYS)\pctty.c + +$(O)sys.o: $(HACK_H) $(SYS)\pcsys.c + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SYS)\pcsys.c + +$(O)random.o: $(PCHO) $(HACK_H) $(SYS)\random.c + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $(SYS)\random.c + +# +# from sys\msdos +# + +$(O)msdos.0: $(MSYS)\msdos.c $(HACK_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COVL0) $(COBJNAM)$@ $(MSYS)\msdos.c + +$(O)msdos.B: $(MSYS)\msdos.c $(HACK_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COVLB) $(COBJNAM)$@ $(MSYS)\msdos.c + +$(O)pctiles.0: $(PCH0) $(MSYS)\pctiles.c $(HACK_H) $(TILE_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $(MSYS)\pctiles.c + +$(O)pctiles.B: $(PCHB) $(MSYS)\pctiles.c $(HACK_H) $(TILE_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(MSYS)\pctiles.c + +$(O)sound.o: $(PCH0) $(MSYS)\sound.c $(HACK_H) $(INCL)\portio.h + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $(MSYS)\sound.c + +$(O)pckeys.o: $(PCHO) $(MSYS)\pckeys.c $(HACK_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(COBJNAM)$@ $(MSYS)\pckeys.c + +$(O)stubvid.o : $(MSYS)\video.c $(HACK_H) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUO) >> $(VROOMMCFG) + $(CC) $(FLAGUO) $(CDEFINE)STUBVIDEO $(COBJNAM)$@ $(MSYS)\video.c + +$(O)video.0: $(PCH0) $(MSYS)\video.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $(MSYS)\video.c + +$(O)video.1: $(PCH1) $(MSYS)\video.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(MSYS)\video.c + +$(O)video.B: $(PCHB) $(MSYS)\video.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(MSYS)\video.c + +$(O)vidtxt.0: $(MSYS)\vidtxt.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COVL0) $(COBJNAM)$@ $(MSYS)\vidtxt.c + +$(O)vidtxt.B: $(MSYS)\vidtxt.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COVLB) $(COBJNAM)$@ $(MSYS)\vidtxt.c + +$(O)vidvga.0: $(PCH0) $(MSYS)\vidvga.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_0" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU0) >> $(VROOMMCFG) + $(CC) $(FLAGU0) $(COBJNAM)$@ $(MSYS)\vidvga.c + +$(O)vidvga.1: $(PCH1) $(MSYS)\vidvga.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_1" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU1) >> $(VROOMMCFG) + $(CC) $(FLAGU1) $(COBJNAM)$@ $(MSYS)\vidvga.c + +$(O)vidvga.2: $(PCH2) $(MSYS)\vidvga.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_2" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGU2) >> $(VROOMMCFG) + $(CC) $(FLAGU2) $(COBJNAM)$@ $(MSYS)\vidvga.c + +$(O)vidvga.B: $(PCHB) $(MSYS)\vidvga.c $(HACK_H) $(WINTTY_H) $(PCVIDEO_H) \ + $(TILE_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) $(CFLAGUB) >> $(VROOMMCFG) + $(CC) $(FLAGUB) $(COBJNAM)$@ $(MSYS)\vidvga.c + +# +# from src +# + +$(O)alloc.o: $(SRC)\alloc.c $(CONFIG_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\alloc.c +$(O)ball.o: $(PCHO) $(SRC)\ball.c $(HACK_H) +$(O)bones.o: $(PCHO) $(SRC)\bones.c $(HACK_H) $(LEV_H) +$(O)decl.o: $(PCHO) $(SRC)\decl.c $(HACK_H) $(QUEST_H) +$(O)detect.o: $(PCHO) $(SRC)\detect.c $(HACK_H) $(ARTIFACT_H) +$(O)dig.o: $(PCHO) $(SRC)\dig.c $(HACK_H) $(EDOG_H) # check dep +$(O)display.o: $(PCHO) $(SRC)\display.c $(HACK_H) +$(O)dlb.o: $(SRC)\dlb.c $(DLB_H) $(HACK_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\dlb.c +$(O)dokick.o: $(PCHO) $(SRC)\dokick.c $(HACK_H) $(ESHK_H) +$(O)dothrow.o: $(PCHO) $(SRC)\dothrow.c $(HACK_H) +$(O)drawing.o: $(SRC)\drawing.c $(HACK_H) $(TERMCAP_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\drawing.c +$(O)end.o: $(SRC)\end.c $(HACK_H) $(ESHK_H) $(DLB_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\end.c +$(O)exper.o: $(PCHO) $(SRC)\exper.c $(HACK_H) +$(O)extralev.o: $(PCHO) $(SRC)\extralev.c $(HACK_H) +$(O)files.o: $(PCHO) $(SRC)\files.c $(HACK_H) $(DLB_H) +$(O)fountain.o: $(PCHO) $(SRC)\fountain.c $(HACK_H) +$(O)mapglyph.o: $(PCHO) $(SRC)\mapglyph.c $(HACK_H) +$(O)minion.o: $(PCHO) $(SRC)\minion.c $(HACK_H) $(EMIN_H) $(EPRI_H) +$(O)mklev.o: $(PCHO) $(SRC)\mklev.c $(HACK_H) +$(O)mkmap.o: $(PCHO) $(SRC)\mkmap.c $(HACK_H) $(SP_LEV_H) +$(O)mkmaze.o: $(PCHO) $(SRC)\mkmaze.c $(HACK_H) $(SP_LEV_H) $(LEV_H) +$(O)monst.o: $(SRC)\monst.c $(CONFIG_H) $(PERMONST_H) $(MONSYM_H) \ + $(ESHK_H) $(EPRI_H) $(COLOR_H) $(ALIGN_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\monst.c +$(O)monstr.o: $(SRC)\monstr.c $(CONFIG_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\monstr.c +$(O)mplayer.o: $(PCHO) $(SRC)\mplayer.c $(HACK_H) +$(O)muse.o: $(PCHO) $(SRC)\muse.c $(HACK_H) +$(O)music.o: $(PCHO) $(SRC)\music.c $(HACK_H) +$(O)o_init.o: $(PCHO) $(SRC)\o_init.c $(HACK_H) $(LEV_H) +$(O)objects.o: $(SRC)\objects.c $(CONFIG_H) $(OBJ_H) $(OBJCLASS_H) \ + $(PROP_H) $(COLOR_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\objects.c +$(O)options.o: $(SRC)\options.c $(HACK_H) $(TERMCAP_H) $(OBJCLASS_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\options.c +$(O)pager.o: $(SRC)\pager.c $(HACK_H) $(DLB_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGNO) $(COBJNAM)$@ $(SRC)\pager.c +$(O)pickup.o: $(PCHO) $(SRC)\pickup.c $(HACK_H) +$(O)pray.o: $(PCHO) $(SRC)\pray.c $(HACK_H) $(EPRI_H) +$(O)quest.o: $(PCHO) $(SRC)\quest.c $(HACK_H) $(QUEST_H) $(QTEXT_H) +$(O)questpgr.o: $(PCHO) $(SRC)\questpgr.c $(HACK_H) $(QTEXT_H) $(DLB_H) +$(O)rect.o: $(PCHO) $(SRC)\rect.c $(HACK_H) +$(O)region.o: $(PCHO) $(SRC)\region.c $(HACK_H) +$(O)restore.o: $(PCHO) $(SRC)\restore.c $(HACK_H) $(LEV_H) $(TERMCAP_H) \ + $(QUEST_H) +$(O)rip.o: $(PCHO) $(SRC)\rip.c $(HACK_H) +$(O)role.o: $(PCHO) $(SRC)\role.c $(HACK_H) +$(O)rumors.o: $(PCHO) $(SRC)\rumors.c $(HACK_H) $(DLB_H) +$(O)save.o: $(PCHO) $(SRC)\save.c $(HACK_H) $(LEV_H) $(QUEST_H) +$(O)sit.o: $(PCHO) $(SRC)\sit.c $(HACK_H) $(ARTIFACT_H) +$(O)steed.o: $(PCHO) $(SRC)\steed.c $(HACK_H) +$(O)sp_lev.o: $(PCHO) $(SRC)\sp_lev.c $(HACK_H) $(SP_LEV_H) $(DLB_H) +$(O)spell.o: $(PCHO) $(SRC)\spell.c $(HACK_H) +$(O)teleport.o: $(PCHO) $(SRC)\teleport.c $(HACK_H) # check dep +$(O)tile.o: $(PCHO) $(SRC)\tile.c $(HACK_H) +$(O)topten.o: $(PCHO) $(SRC)\topten.c $(HACK_H) $(DLB_H) $(PATCHLEVEL_H) +$(O)u_init.o: $(PCHO) $(SRC)\u_init.c $(HACK_H) +$(O)uhitm.o: $(PCHO) $(SRC)\uhitm.c $(HACK_H) +$(O)version.o: $(PCHO) $(SRC)\version.c $(HACK_H) $(PATCHLEVEL_H) +$(O)vision.o: $(PCHO) $(SRC)\vision.c $(HACK_H) $(VIS_TAB_H) +$(O)vis_tab.o: $(SRC)\vis_tab.c $(HACK_H) $(VIS_TAB_H) + @type schema$(SCHEMA).bc | find "$(@B)_o" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\vis_tab.c +$(O)wield.o: $(PCHO) $(SRC)\wield.c $(HACK_H) +$(O)windows.o: $(PCHO) $(SRC)\windows.c $(HACK_H) $(WINTTY_H) +$(O)worm.o: $(PCHO) $(SRC)\worm.c $(HACK_H) $(LEV_H) +$(O)worn.o: $(PCHO) $(SRC)\worn.c $(HACK_H) +$(O)write.o: $(PCHO) $(SRC)\write.c $(HACK_H) + +# +# Overlays +# + +# OVL0 +# + +$(O)allmain.0: $(PCH0) $(SRC)\allmain.c $(HACK_H) +$(O)apply.0: $(PCH0) $(SRC)\apply.c $(HACK_H) $(EDOG_H) +$(O)artifact.0: $(PCH0) $(SRC)\artifact.c $(HACK_H) $(ARTIFACT_H) $(ARTILIST_H) +$(O)attrib.0: $(PCH0) $(SRC)\attrib.c $(HACK_H) +$(O)botl.0: $(PCH0) $(SRC)\botl.c $(HACK_H) +$(O)cmd.0: $(PCH0) $(SRC)\cmd.c $(HACK_H) $(FUNC_TAB_H) +$(O)dbridge.0: $(PCH0) $(SRC)\dbridge.c $(HACK_H) +$(O)do.0: $(PCH0) $(SRC)\do.c $(HACK_H) $(LEV_H) +$(O)do_name.0: $(PCH0) $(SRC)\do_name.c $(HACK_H) +$(O)do_wear.0: $(PCH0) $(SRC)\do_wear.c $(HACK_H) +$(O)dogmove.0: $(PCH0) $(SRC)\dogmove.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)dungeon.0: $(PCH0) $(SRC)\dungeon.c $(HACK_H) $(ALIGN_H) $(DGN_FILE_H) \ + $(DLB_H) +$(O)eat.0: $(PCH0) $(SRC)\eat.c $(HACK_H) +$(O)engrave.0: $(PCH0) $(SRC)\engrave.c $(HACK_H) $(LEV_H) +$(O)explode.0: $(PCH0) $(SRC)\explode.c $(HACK_H) +$(O)hacklib.0: $(PCH0) $(SRC)\hacklib.c $(HACK_H) +$(O)invent.0: $(PCH0) $(SRC)\invent.c $(HACK_H) $(ARTIFACT_H) +$(O)lock.0: $(PCH0) $(SRC)\lock.c $(HACK_H) +$(O)mail.0: $(PCH0) $(SRC)\mail.c $(HACK_H) $(MAIL_H) $(DATE_H) +$(O)makemon.0: $(PCH0) $(SRC)\makemon.c $(HACK_H) $(EPRI_H) $(EMIN_H) +$(O)mcastu.0: $(PCH0) $(SRC)\mcastu.c $(HACK_H) +$(O)mhitm.0: $(PCH0) $(SRC)\mhitm.c $(HACK_H) $(ARTIFACT_H) $(EDOG_H) +$(O)mhitu.0: $(PCH0) $(SRC)\mhitu.c $(HACK_H) $(ARTIFACT_H) $(EDOG_H) +$(O)mkobj.0: $(PCH0) $(SRC)\mkobj.c $(HACK_H) $(ARTIFACT_H) $(PROP_H) +$(O)mkroom.0: $(PCH0) $(SRC)\mkroom.c $(HACK_H) +$(O)mon.0: $(PCH0) $(SRC)\mon.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)mondata.0: $(PCH0) $(SRC)\mondata.c $(HACK_H) $(ESHK_H) $(EPRI_H) +$(O)monmove.0: $(PCH0) $(SRC)\monmove.c $(HACK_H) $(MFNDPOS_H) $(ARTIFACT_H) +$(O)mthrowu.0: $(PCH0) $(SRC)\mthrowu.c $(HACK_H) +$(O)objnam.0: $(PCH0) $(SRC)\objnam.c $(HACK_H) +$(O)polyself.0: $(PCH0) $(SRC)\polyself.c $(HACK_H) +$(O)priest.0: $(PCH0) $(SRC)\priest.c $(HACK_H) $(MFNDPOS_H) $(ESHK_H) \ + $(EPRI_H) $(EMIN_H) +$(O)rnd.0: $(PCH0) $(SRC)\rnd.c $(HACK_H) +$(O)shk.0: $(PCH0) $(SRC)\shk.c $(HACK_H) $(ESHK_H) +$(O)shknam.0: $(PCH0) $(SRC)\shknam.c $(HACK_H) $(ESHK_H) +$(O)sounds.0: $(PCH0) $(SRC)\sounds.c $(HACK_H) $(EDOG_H) +$(O)steal.0: $(PCH0) $(SRC)\steal.c $(HACK_H) +$(O)timeout.0: $(PCH0) $(SRC)\timeout.c $(HACK_H) $(LEV_H) +$(O)track.0: $(PCH0) $(SRC)\track.c $(HACK_H) +$(O)trap.0: $(PCH0) $(SRC)\trap.c $(HACK_H) +$(O)vault.0: $(PCH0) $(SRC)\vault.c $(HACK_H) $(VAULT_H) +$(O)weapon.0: $(PCH0) $(SRC)\weapon.c $(HACK_H) +$(O)were.0: $(PCH0) $(SRC)\were.c $(HACK_H) +$(O)wizard.0: $(PCH0) $(SRC)\wizard.c $(HACK_H) $(QTEXT_H) +$(O)zap.0: $(PCH0) $(SRC)\zap.c $(HACK_H) + +# +# OVL1 +# + +$(O)allmain.1: $(PCH1) $(SRC)\allmain.c $(HACK_H) +$(O)apply.1: $(PCH1) $(SRC)\apply.c $(HACK_H) $(EDOG_H) +$(O)artifact.1: $(PCH1) $(SRC)\artifact.c $(HACK_H) $(ARTIFACT_H) $(ARTILIST_H) +$(O)attrib.1: $(PCH1) $(SRC)\attrib.c $(HACK_H) +$(O)botl.1: $(PCH1) $(SRC)\botl.c $(HACK_H) +$(O)cmd.1: $(PCH1) $(SRC)\cmd.c $(HACK_H) $(FUNC_TAB_H) +$(O)dbridge.1: $(PCH1) $(SRC)\dbridge.c $(HACK_H) +$(O)do.1: $(PCH1) $(SRC)\do.c $(HACK_H) $(LEV_H) +$(O)do_wear.1: $(PCH1) $(SRC)\do_wear.c $(HACK_H) +$(O)dog.1: $(PCH1) $(SRC)\dog.c $(HACK_H) $(EDOG_H) +$(O)dungeon.1: $(PCH1) $(SRC)\dungeon.c $(HACK_H) $(ALIGN_H) $(DGN_FILE_H) $(DLB_H) +$(O)eat.1: $(PCH1) $(SRC)\eat.c $(HACK_H) +$(O)engrave.1: $(PCH1) $(SRC)\engrave.c $(HACK_H) $(LEV_H) +$(O)explode.1: $(PCH1) $(SRC)\explode.c $(HACK_H) +$(O)hack.1: $(PCH1) $(SRC)\hack.c $(HACK_H) +$(O)hacklib.1: $(PCH1) $(SRC)\hacklib.c $(HACK_H) +$(O)invent.1: $(PCH1) $(SRC)\invent.c $(HACK_H) $(ARTIFACT_H) +$(O)makemon.1: $(PCH1) $(SRC)\makemon.c $(HACK_H) $(EPRI_H) $(EMIN_H) +$(O)mhitu.1: $(PCH1) $(SRC)\mhitu.c $(HACK_H) $(ARTIFACT_H) $(EDOG_H) +$(O)mkobj.1: $(PCH1) $(SRC)\mkobj.c $(HACK_H) $(ARTIFACT_H) $(PROP_H) +$(O)mon.1: $(PCH1) $(SRC)\mon.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)mondata.1: $(PCH1) $(SRC)\mondata.c $(HACK_H) $(ESHK_H) $(EPRI_H) +$(O)monmove.1: $(PCH1) $(SRC)\monmove.c $(HACK_H) $(MFNDPOS_H) $(ARTIFACT_H) +$(O)mthrowu.1: $(PCH1) $(SRC)\mthrowu.c $(HACK_H) +$(O)objnam.1: $(PCH1) $(SRC)\objnam.c $(HACK_H) +$(O)polyself.1: $(PCH1) $(SRC)\polyself.c $(HACK_H) +$(O)rnd.1: $(PCH1) $(SRC)\rnd.c $(HACK_H) +$(O)shk.1: $(PCH1) $(SRC)\shk.c $(HACK_H) $(ESHK_H) +$(O)steal.1: $(PCH1) $(SRC)\steal.c $(HACK_H) +$(O)timeout.1: $(PCH1) $(SRC)\timeout.c $(HACK_H) $(LEV_H) +$(O)track.1: $(PCH1) $(SRC)\track.c $(HACK_H) +$(O)trap.1: $(PCH1) $(SRC)\trap.c $(HACK_H) +$(O)weapon.1: $(PCH1) $(SRC)\weapon.c $(HACK_H) +$(O)zap.1: $(PCH1) $(SRC)\zap.c $(HACK_H) + +# +# OVL2 +# + +$(O)attrib.2: $(PCH2) $(SRC)\attrib.c $(HACK_H) +$(O)do.2: $(PCH2) $(SRC)\do.c $(HACK_H) $(LEV_H) +$(O)do_name.2: $(PCH2) $(SRC)\do_name.c $(HACK_H) +$(O)do_wear.2: $(PCH2) $(SRC)\do_wear.c $(HACK_H) +$(O)dog.2: $(PCH2) $(SRC)\dog.c $(HACK_H) $(EDOG_H) +$(O)engrave.2: $(PCH2) $(SRC)\engrave.c $(HACK_H) $(LEV_H) +$(O)hack.2: $(PCH2) $(SRC)\hack.c $(HACK_H) +$(O)hacklib.2: $(PCH2) $(SRC)\hacklib.c $(HACK_H) +$(O)invent.2: $(PCH2) $(SRC)\invent.c $(HACK_H) $(ARTIFACT_H) +$(O)makemon.2: $(PCH2) $(SRC)\makemon.c $(HACK_H) $(EPRI_H) $(EMIN_H) +$(O)mon.2: $(PCH2) $(SRC)\mon.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)mondata.2: $(PCH2) $(SRC)\mondata.c $(HACK_H) $(ESHK_H) $(EPRI_H) +$(O)monmove.2: $(PCH2) $(SRC)\monmove.c $(HACK_H) $(MFNDPOS_H) $(ARTIFACT_H) +$(O)shk.2: $(PCH2) $(SRC)\shk.c $(HACK_H) $(ESHK_H) +$(O)trap.2: $(PCH2) $(SRC)\trap.c $(HACK_H) +$(O)zap.2: $(PCH2) $(SRC)\zap.c $(HACK_H) + +# +# OVL3 +# + +$(O)do.3: $(PCH3) $(SRC)\do.c $(HACK_H) $(LEV_H) +$(O)hack.3: $(PCH3) $(SRC)\hack.c $(HACK_H) +$(O)invent.3: $(PCH3) $(SRC)\invent.c $(HACK_H) $(ARTIFACT_H) +$(O)light.3: $(PCH3) $(SRC)\light.c $(HACK_H) +$(O)shk.3: $(PCH3) $(SRC)\shk.c $(HACK_H) $(ESHK_H) +$(O)trap.3: $(PCH3) $(SRC)\trap.c $(HACK_H) +$(O)zap.3: $(PCH3) $(SRC)\zap.c $(HACK_H) + +# +# OVLB +# + +$(O)allmain.B: $(PCHB) $(SRC)\allmain.c $(HACK_H) +$(O)apply.B: $(PCHB) $(SRC)\apply.c $(HACK_H) $(EDOG_H) +$(O)artifact.B: $(PCHB) $(SRC)\artifact.c $(HACK_H) $(ARTIFACT_H) $(ARTILIST_H) +$(O)attrib.B: $(PCHB) $(SRC)\attrib.c $(HACK_H) +$(O)botl.B: $(PCHB) $(SRC)\botl.c $(HACK_H) +$(O)cmd.B: $(PCHB) $(SRC)\cmd.c $(HACK_H) $(FUNC_TAB_H) +$(O)dbridge.B: $(PCHB) $(SRC)\dbridge.c $(HACK_H) +$(O)do.B: $(PCHB) $(SRC)\do.c $(HACK_H) $(LEV_H) +$(O)do_name.B: $(PCHB) $(SRC)\do_name.c $(HACK_H) +$(O)do_wear.B: $(PCHB) $(SRC)\do_wear.c $(HACK_H) +$(O)dog.B: $(PCHB) $(SRC)\dog.c $(HACK_H) $(EDOG_H) +$(O)dogmove.B: $(PCHB) $(SRC)\dogmove.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)eat.B: $(PCHB) $(SRC)\eat.c $(HACK_H) +$(O)engrave.B: $(PCHB) $(SRC)\engrave.c $(HACK_H) $(LEV_H) +$(O)hack.B: $(PCHB) $(SRC)\hack.c $(HACK_H) +$(O)hacklib.B: $(PCHB) $(SRC)\hacklib.c $(HACK_H) +$(O)invent.B: $(PCHB) $(SRC)\invent.c $(HACK_H) $(ARTIFACT_H) +$(O)lock.B: $(PCHB) $(SRC)\lock.c $(HACK_H) +$(O)mail.B: $(PCHB) $(SRC)\mail.c $(HACK_H) $(MAIL_H) $(DATE_H) +$(O)makemon.B: $(PCHB) $(SRC)\makemon.c $(HACK_H) $(EPRI_H) $(EMIN_H) +$(O)mcastu.B: $(PCHB) $(SRC)\mcastu.c $(HACK_H) +$(O)mhitm.B: $(PCHB) $(SRC)\mhitm.c $(HACK_H) $(ARTIFACT_H) $(EDOG_H) +$(O)mhitu.B: $(PCHB) $(SRC)\mhitu.c $(HACK_H) $(ARTIFACT_H) $(EDOG_H) +$(O)mkobj.B: $(PCHB) $(SRC)\mkobj.c $(HACK_H) $(ARTIFACT_H) $(PROP_H) +$(O)mkroom.B: $(PCHB) $(SRC)\mkroom.c $(HACK_H) +$(O)mon.B: $(PCHB) $(SRC)\mon.c $(HACK_H) $(MFNDPOS_H) $(EDOG_H) +$(O)mondata.B: $(PCHB) $(SRC)\mondata.c $(HACK_H) $(ESHK_H) $(EPRI_H) +$(O)monmove.B: $(PCHB) $(SRC)\monmove.c $(HACK_H) $(MFNDPOS_H) $(ARTIFACT_H) +$(O)mthrowu.B: $(PCHB) $(SRC)\mthrowu.c $(HACK_H) +$(O)objnam.B: $(PCHB) $(SRC)\objnam.c $(HACK_H) +$(O)pline.B: $(SRC)\pline.c $(HACK_H) $(EPRI_H) + @type schema$(SCHEMA).bc | find "$(@B)_b" > $(VROOMMCFG) + @echo $(BCOPTS1) >> $(VROOMMCFG) + @echo $(BCOPTS2) >> $(VROOMMCFG) + $(CC) $(CFLAGSN) $(COBJNAM)$@ $(SRC)\pline.c +$(O)polyself.B: $(PCHB) $(SRC)\polyself.c $(HACK_H) +$(O)potion.B: $(PCHB) $(SRC)\potion.c $(HACK_H) +$(O)priest.B: $(PCHB) $(SRC)\priest.c $(HACK_H) $(MFNDPOS_H) $(ESHK_H) \ + $(EPRI_H) $(EMIN_H) +$(O)read.B: $(PCHB) $(SRC)\read.c $(HACK_H) +$(O)rnd.B: $(PCHB) $(SRC)\rnd.c $(HACK_H) +$(O)shk.B: $(PCHB) $(SRC)\shk.c $(HACK_H) $(ESHK_H) +$(O)shknam.B: $(PCHB) $(SRC)\shknam.c $(HACK_H) $(ESHK_H) +$(O)sounds.B: $(PCHB) $(SRC)\sounds.c $(HACK_H) $(EDOG_H) +$(O)steal.B: $(PCHB) $(SRC)\steal.c $(HACK_H) +$(O)timeout.B: $(PCHB) $(SRC)\timeout.c $(HACK_H) $(LEV_H) +$(O)track.B: $(PCHB) $(SRC)\track.c $(HACK_H) +$(O)trap.B: $(PCHB) $(SRC)\trap.c $(HACK_H) +$(O)vault.B: $(PCHB) $(SRC)\vault.c $(HACK_H) $(VAULT_H) +$(O)weapon.B: $(PCHB) $(SRC)\weapon.c $(HACK_H) +$(O)were.B: $(PCHB) $(SRC)\were.c $(HACK_H) +$(O)wizard.B: $(PCHB) $(SRC)\wizard.c $(HACK_H) $(QTEXT_H) +$(O)zap.B: $(PCHB) $(SRC)\zap.c $(HACK_H) + +# end of file diff --git a/sys/msdos/Makefile.GCC b/sys/msdos/Makefile.GCC new file mode 100644 index 0000000..64fe610 --- /dev/null +++ b/sys/msdos/Makefile.GCC @@ -0,0 +1,1260 @@ +# SCCS Id: @(#)Makefile.GCC 3.4 $Date: 2003/06/15 15:56:45 $ +# Copyright (c) NetHack PC Development Team 1996-2003. +# PC NetHack 3.4 Makefile for djgpp V2 +# +# Gnu gcc compiler for msdos (djgpp) +# Requires Gnu Make utility (V3.79.1 or greater) supplied with djgpp +# +# For questions or comments: devteam@nethack.org +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc +# .l lex +# +# Note that flex (lex) and bison (yacc) are included with the +# djgpp distribution and work quite well. This makefile assumes +# you have them installed correctly. + +# Game Installation Variables +# NOTE: Make sure GAMEDIR exists before make is started. + +GAME = nethack +# The GNU Make has a problem if you include a drive spec below (unfortunately). +GAMEDIR =../binary + +# +# Directories, gcc likes unix style directory specs +# + +OBJ = o +DAT = ../dat +DOC = ../doc +INCL = ../include +MSYS = ../sys/msdos +SRC = ../src +SSHR = ../sys/share +UTIL = ../util +WIN = ../win/tty +WSHR = ../win/share + +# +# Executables. + +CC = gcc +LINK = gcc +MAKEBIN = make + +# +# Special libraries and how to link them in. + +LIBS = -lpc + +# If TERMLIB is defined in pcconf.h, comment out the upper line and +# uncomment the lower. Note that you must build the termc library +# and place it in djgpp's lib directory. See termcap.zip for details + +TERMLIB = +#TERMLIB = -ltermc + +LIBRARIES = $(LIBS) $(TERMLIB) + +# +# Yacc/Lex ... if you got 'em. +# +# If you have yacc/lex or a work-alike set YACC_LEX to Y +# +YACC_LEX = Y +ifeq ($(YACC_LEX),Y) +DO_YACC = YACC_ACT +DO_LEX = LEX_ACT +endif + +# If YACC_LEX is Y above, set the following to values appropriate for +# your tools. +# +YACC = bison -y +LEX = flex +YTABC = y_tab.c +YTABH = y_tab.h +#If your tool produces y.tab.c and y.tab.h DOS might require +#the following instead. +#YTABC = ytab~1.c +#YTABH = ytab~1.h +LEXYYC = lexyy.c + +# +# Uncomment the line below if you want to store all the level files, +# help files, etc. in a single library file. + +USE_DLB = Y + +# djgpp includes ls.exe and touch.exe in fil41b.zip from the v2gnu +# folder so be sure to include that when downloading djgpp. Doing +# so will make changing this unnecessary. + +LS = ls -1 # ls.exe from djgpp distribution +#LS = dir /l/b # DOS command + +# To build a binary without any graphics +# suitable for blind players, +# set SUPPRESS_GRAPHICS to Y +# (Note: binary will require ANSI.SYS driver or equivalent loaded) +# SUPPRESS_GRAPHICS = Y +SUPPRESS_GRAPHICS = + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +GAMEFILE = $(GAMEDIR)/$(GAME).exe + +# Changing this conditional block is not recommended +ifeq ($(USE_DLB),Y) +DLBFLG = -DDLB +else +DLBFLG = +endif + +# +# Flags. +# +ifeq ($(SUPPRESS_GRAPHICS),Y) +TERMLIB = +# Build NetHack suitable for blind players + +# Debugging +#cflags = -pg -c -I../include $(DLBFLG) -DSUPPRESS_GRAPHICS +#LFLAGS = -pg + +cflags = -c -O -I../include $(DLBFLG) -DSUPPRESS_GRAPHICS +LFLAGS = + +else + +# Debugging +#cflags = -pg -c -I../include $(DLBFLG) -DUSE_TILES +#LFLAGS = -pg + +# Normal +cflags = -c -O -I../include $(DLBFLG) -DUSE_TILES +LFLAGS = +endif + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .tib .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +$(OBJ)/%.o : /%.c + $(CC) $(cflags) -o$@ $< + +$(OBJ)/%.o : $(SRC)/%.c + $(CC) $(cflags) -o$@ $< + +#========================================== +# Rules for files in sys/share +#========================================== + +$(OBJ)/%.o : $(SSHR)/%.c + $(CC) $(cflags) -o$@ $< + +#========================================== +# Rules for files in sys/msdos +#========================================== + +$(OBJ)/%.o : $(MSYS)/%.c + $(CC) $(cflags) -I../sys/msdos -o$@ $< + +#========================================== +# Rules for files in util +#========================================== + +$(OBJ)/%.o : $(UTIL)/%.c + $(CC) $(cflags) -o$@ $< + +#========================================== +# Rules for files in win/share +#========================================== + +$(OBJ)/%.o : $(WSHR)/%.c + $(CC) $(cflags) -I../win/share -o$@ $< + +#{$(WSHR)}.txt{$(DAT)}.txt: +# copy $< $@ + +#========================================== +# Rules for files in win/tty +#========================================== + +$(OBJ)/%.o : $(TTY)/%.c + $(CC) $(cflags) -o$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# +# +# Shorten up the location for some files +# + +O = $(OBJ)/ + +U = $(UTIL)/ + +#========================================== +# Utility Objects. +#========================================== + +VGAOBJ = $(O)vidvga.o + +MAKESRC = makedefs.c + +SPLEVSRC = lev_yacc.c lev_$(LEX).c lev_main.c panic.c + +DGNCOMPSRC = dgn_yacc.c dgn_$(LEX).c dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o $(O)alloc.o \ + $(O)monst.o $(O)objects.o $(O)panic.o \ + $(O)drawing.o $(O)decl.o $(O)stubvid.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o $(O)alloc.o \ + $(O)panic.o + +RECOVOBJS = $(O)recover.o + + +#========================================== +# Tile related object files. +#========================================== + +ifeq ($(SUPPRESS_GRAPHICS),Y) +TILOBJ = +TEXTIO = +TEXTIO2 = +PLANAR_TIB = +OVERVIEW_TIB = +TILEUTIL = +TILEFILES = +TILEFILES2 = +GIFREADERS = +GIFREAD2 = +PPMWRITERS = +PPMWRIT2 = + +else + +TILOBJ = $(O)tile.o $(O)pctiles.o $(VGAOBJ) + +TEXTIO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o $(O)decl.o $(O)monst.o \ + $(O)objects.o $(O)stubvid.o + +TEXTIO2 = $(O)tiletex2.o $(O)tiletxt2.o $(O)drawing.o $(O)decl.o $(O)monst.o \ + $(O)objects.o $(O)stubvid.o + +PLANAR_TIB = $(DAT)/NetHack1.tib + +OVERVIEW_TIB = $(DAT)/NetHacko.tib + +TILEUTIL = $(TILOBJ) $(U)tile2bin.exe $(U)til2bin2.exe $(PLANAR_TIB) $(OVERVIEW_TIB) + +TILEFILES = $(WSHR)/monsters.txt $(WSHR)/objects.txt $(WSHR)/other.txt + +TILEFILES2 = $(WSHR)/monthin.txt $(WSHR)/objthin.txt $(WSHR)/oththin.txt + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o + +GIFREAD2 = $(O)gifread2.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +PPMWRIT2 = $(O)ppmwrit2.o $(O)alloc.o $(O)panic.o +endif + + +DLBOBJ = $(O)dlb.o + +# Object files for the game itself. + + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o $(O)attrib.o +VOBJ02 = $(O)ball.o $(O)bones.o $(O)botl.o $(O)cmd.o $(O)dbridge.o +VOBJ03 = $(O)decl.o $(O)detect.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ04 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o $(O)dothrow.o +VOBJ05 = $(O)drawing.o $(O)dungeon.o $(O)eat.o $(O)end.o $(O)engrave.o +VOBJ06 = $(O)exper.o $(O)explode.o $(O)extralev.o $(O)files.o $(O)fountain.o +VOBJ07 = $(O)getline.o $(O)hack.o $(O)hacklib.o $(O)invent.o $(O)lock.o +VOBJ08 = $(O)mail.o $(O)main.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o $(O)mhitm.o +VOBJ09 = $(O)mhitu.o $(O)minion.o $(O)mkmap.o $(O)mklev.o $(O)mkmaze.o +VOBJ10 = $(O)mkobj.o $(O)mkroom.o $(O)mon.o $(O)mondata.o $(O)monmove.o +VOBJ11 = $(O)monst.o $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ12 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o $(O)options.o +VOBJ13 = $(O)pickup.o $(O)pline.o $(O)polyself.o $(O)potion.o $(O)quest.o +VOBJ14 = $(O)questpgr.o $(O)pager.o $(O)pray.o $(O)priest.o $(O)read.o +VOBJ15 = $(O)rect.o $(O)restore.o $(O)rip.o $(O)rnd.o $(O)role.o +VOBJ16 = $(O)rumors.o $(O)save.o $(O)shk.o $(O)shknam.o $(O)sit.o +VOBJ17 = $(O)sounds.o $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ18 = $(O)termcap.o $(O)timeout.o $(O)topl.o $(O)topten.o $(O)track.o +VOBJ19 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o $(O)vision.o +VOBJ20 = $(O)vis_tab.o $(O)weapon.o $(O)were.o $(O)wield.o $(O)windows.o +VOBJ21 = $(O)wintty.o $(O)wizard.o $(O)worm.o $(O)worn.o $(O)write.o +VOBJ22 = $(O)zap.o $(O)light.o $(O)dlb.o $(O)dig.o $(O)teleport.o +VOBJ23 = $(O)region.o + +SOBJ = $(O)msdos.o $(O)sound.o $(O)sys.o $(O)tty.o $(O)unix.o \ + $(O)video.o $(O)vidtxt.o $(O)pckeys.o + +VVOBJ = $(O)version.o + +VOBJ = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) + +ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(VVOBJ) + +#========================================== +# Header file macros +#========================================== + +PATCHLEV_H = $(INCL)/patchlev.h +DGN_FILE_H = $(INCL)/align.h $(INCL)/dgn_file.h +DUNGEON_H = $(INCL)/align.h $(INCL)/dungeon.h +EMIN_H = $(DUNGEON_H) $(INCL)/emin.h +EPRI_H = $(DUNGEON_H) $(INCL)/align.h $(INCL)/epri.h +ESHK_H = $(DUNGEON_H) $(INCL)/eshk.h +MONDATA_H = $(INCL)/align.h $(INCL)/mondata.h +MONST_H = $(INCL)/align.h $(INCL)/monst.h +PERMONST_H = $(INCL)/monattk.h $(INCL)/monflag.h $(INCL)/align.h \ + $(INCL)/permonst.h +REGION_H = $(INCL)/region.h +RM_H = $(INCL)/align.h $(INCL)/rm.h +SKILLS_H = $(INCL)/skills.h +SP_LEV_H = $(INCL)/align.h $(INCL)/sp_lev.h +VAULT_H = $(DUNGEON_H) $(INCL)/vault.h +YOUPROP_H = $(PERMONST_H) $(MONDATA_H) $(INCL)/prop.h \ + $(INCL)/pm.h $(INCL)/youprop.h +YOU_H = $(MONST_H) $(YOUPROP_H) $(INCL)/align.h \ + $(INCL)/attrib.h $(INCL)/you.h +DISPLAY_H = $(MONDATA_H) $(INCL)/vision.h $(INCL)/display.h +PCCONF_H = $(INCL)/micro.h $(INCL)/system.h $(INCL)/pcconf.h \ + $(MSYS)/pcvideo.h +CONFIG_H = $(GLOBAL_H) $(INCL)/tradstdc.h $(INCL)/config1.h \ + $(INCL)/config.h +DECL_H = $(YOU_H) $(INCL)/spell.h $(INCL)/color.h \ + $(INCL)/obj.h $(INCL)/onames.h $(INCL)/pm.h \ + $(INCL)/decl.h +GLOBAL_H = $(PCCONF_H) $(INCL)/coord.h $(INCL)/global.h +HACK_H = $(CONFIG_H) $(DUNGEON_H) $(DECL_H) \ + $(DISPLAY_H) $(INCL)/monsym.h $(INCL)/mkroom.h \ + $(INCL)/objclass.h $(INCL)/trap.h $(INCL)/flag.h \ + $(RM_H) $(INCL)/vision.h $(INCL)/wintype.h \ + $(INCL)/engrave.h $(INCL)/rect.h \ + $(INCL)/trampoli.h $(INCL)/hack.h $(REGION_H) +DLB_H = $(INCL)/dlb.h + +ifeq ($(SUPPRESS_GRAPHICS),Y) +TILE_H = +else +TILE_H = $(WSHR)/tile.h $(MSYS)/pctiles.h +endif + +ifeq ($(USE_DLB),Y) +DLB = dlb +DLBOBJS = $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o +else +DLB = +DLBOBJS = +endif + +ifdef DJGPP +DJ1 = $(dir $(DJGPP)) +CWSDPMI = $(subst /,\,$(DJ1))bin\CWSDPMI.* +endif + +#========================================== +# Primary Targets. +#========================================== + +# The default target. + +all : install + +install: $(GAMEFILE) $(O)install.tag + @echo Done. + +default: $(GAMEFILE) + +util: $(O)utility.tag + +$(O)utility.tag: $(INCL)/date.h $(INCL)/trap.h $(INCL)/onames.h \ + $(INCL)/pm.h monstr.c vis_tab.c \ + $(U)lev_comp.exe $(U)dgn_comp.exe $(TILEUTIL) + $(subst /,\,echo utilities made > $@) + +tileutil: $(U)gif2txt.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +recover: $(U)recover.exe + @$(subst /,\,if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR)) + @$(subst /,\,if exist $(DOC)/recover.txt copy $(DOC)/recover.txt $(GAMEDIR)) + +$(O)install.tag: $(O)dat.tag $(GAMEFILE) +ifeq ($(USE_DLB),Y) + @$(subst /,\,copy $(DAT)/nhdat $(GAMEDIR)) + @$(subst /,\,copy $(DAT)/license $(GAMEDIR)) +else + @$(subst /,\,copy $(DAT)/*. $(GAMEDIR)) + @$(subst /,\,copy $(DAT)/*.dat $(GAMEDIR)) + @$(subst /,\,copy $(DAT)/*.lev $(GAMEDIR)) + @$(subst /,\,copy $(MSYS)/msdoshlp.txt $(GAMEDIR)) + @$(subst /,\,if exist $(GAMEDIR)/makefile. del $(GAMEDIR)/makefile.) +endif +ifdef TERMLIB + @$(subst /,\,copy $(SSHR)/termcap $(GAMEDIR)) +endif + @$(subst /,\,if exist $(DAT)/*.tib copy $(DAT)/*.tib $(GAMEDIR)) + @$(subst /,\,copy $(SSHR)/NetHack.cnf $(GAMEDIR)/defaults.nh) + @$(subst /,\,copy $(MSYS)/NHAccess.nh $(GAMEDIR)) + @$(subst /,\,copy $(DOC)/guidebo*.txt $(GAMEDIR)) + @$(subst /,\,if exist $(DOC)/nethack.txt copy $(DOC)/nethack.txt $(GAMEDIR)) +ifdef CWSDPMI + @$(subst /,\,if exist $(CWSDPMI) copy $(CWSDPMI) $(GAMEDIR)) +else + @$(subst /,\,echo Could not find a copy of CWSDPMI.EXE to put into $(GAMEDIR)) +endif + @$(subst /,\,echo install done > $@) + +#========================================== +# The main target. +#========================================== + +$(GAMEFILE): $(O)obj.tag $(PATCHLEV_H) $(O)utility.tag $(ALLOBJ) $(O)$(GAME).lnk + $(LINK) $(LFLAGS) -o$(GAME).exe @$(O)$(GAME).lnk $(LIBRARIES) + @$(subst /,\,stubedit $(GAME).exe minstack=2048K) + @$(subst /,\,copy $(GAME).exe $(GAMEFILE)) + @$(subst /,\,del $(GAME).exe) + +$(O)$(GAME).lnk: $(ALLOBJ) + echo $(VOBJ01) > $(subst /,\,$@) + echo $(VOBJ02) >> $(subst /,\,$@) + echo $(VOBJ03) >> $(subst /,\,$@) + echo $(VOBJ04) >> $(subst /,\,$@) + echo $(VOBJ05) >> $(subst /,\,$@) + echo $(VOBJ06) >> $(subst /,\,$@) + echo $(VOBJ07) >> $(subst /,\,$@) + echo $(VOBJ08) >> $(subst /,\,$@) + echo $(VOBJ09) >> $(subst /,\,$@) + echo $(VOBJ10) >> $(subst /,\,$@) + echo $(VOBJ11) >> $(subst /,\,$@) + echo $(VOBJ12) >> $(subst /,\,$@) + echo $(VOBJ13) >> $(subst /,\,$@) + echo $(VOBJ14) >> $(subst /,\,$@) + echo $(VOBJ15) >> $(subst /,\,$@) + echo $(VOBJ16) >> $(subst /,\,$@) + echo $(VOBJ17) >> $(subst /,\,$@) + echo $(VOBJ18) >> $(subst /,\,$@) + echo $(VOBJ19) >> $(subst /,\,$@) + echo $(VOBJ20) >> $(subst /,\,$@) + echo $(VOBJ21) >> $(subst /,\,$@) + echo $(VOBJ22) >> $(subst /,\,$@) + echo $(VOBJ23) >> $(subst /,\,$@) + echo $(SOBJ) >> $(subst /,\,$@) + echo $(TILOBJ) >> $(subst /,\,$@) + echo $(VVOBJ) >> $(subst /,\,$@) + + +#========================================== +# Housekeeping. +#========================================== + +clean: + $(subst /,\,if exist $(O)*.o del $(O)*.o) + $(subst /,\,if exist $(O)dat.tag del $(O)dat.tag) + $(subst /,\,if exist $(O)install.tag del $(O)install.tag) + $(subst /,\,if exist $(O)$(GAME).lnk del $(O)$(GAME).lnk) + $(subst /,\,if exist $(O)obj.tag del $(O)obj.tag) + $(subst /,\,if exist $(O)sp_lev.tag del $(O)sp_lev.tag) + $(subst /,\,if exist $(O)thintile.tag del $(O)thintile.tag) + $(subst /,\,if exist $(O)utility.tag del $(O)utility.tag) + +spotless: clean + + $(subst /,\,if exist $(U)lev_flex.c del $(U)lev_flex.c) + $(subst /,\,if exist $(U)lev_lex.c del $(U)lev_lex.c) + $(subst /,\,if exist $(U)lev_yacc.c del $(U)lev_yacc.c) + $(subst /,\,if exist $(U)dgn_flex.c del $(U)dgn_flex.c) + $(subst /,\,if exist $(U)dgn_lex.c del $(U)dgn_lex.c) + $(subst /,\,if exist $(U)dgn_yacc.c del $(U)lev_yacc.c) + $(subst /,\,if exist $(U)makedefs.exe del $(U)makedefs.exe) + $(subst /,\,if exist $(U)lev_comp.exe del $(U)lev_comp.exe) + $(subst /,\,if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe) + $(subst /,\,if exist $(U)recover.exe del $(U)recover.exe) + $(subst /,\,if exist $(U)tilemap.exe del $(U)tilemap.exe) + $(subst /,\,if exist $(U)tile2bin.exe del $(U)tile2bin.exe) + $(subst /,\,if exist $(U)til2bin2.exe del $(U)til2bin2.exe) + $(subst /,\,if exist $(U)thintile.exe del $(U)thintile.exe) + $(subst /,\,if exist $(U)dlb_main.exe del $(U)dlb_main.exe) + $(subst /,\,if exist $(INCL)/vis_tab.h del $(INCL)/vis_tab.h) + $(subst /,\,if exist $(INCL)/onames.h del $(INCL)/onames.h) + $(subst /,\,if exist $(INCL)/pm.h del $(INCL)/pm.h) + $(subst /,\,if exist $(INCL)/date.h del $(INCL)/date.h) + $(subst /,\,if exist $(INCL)/dgn_comp.h del $(INCL)/dgn_comp.h) + $(subst /,\,if exist $(INCL)/lev_comp.h del $(INCL)/lev_comp.h) + $(subst /,\,if exist $(SRC)/monstr.c del $(SRC)/monstr.c) + $(subst /,\,if exist $(SRC)/vis_tab.c del $(SRC)/vis_tab.c) + $(subst /,\,if exist $(SRC)/tile.c del $(SRC)/tile.c) + $(subst /,\,if exist $(DAT)/options del $(DAT)/options) + $(subst /,\,if exist $(DAT)/data del $(DAT)/data) + $(subst /,\,if exist $(DAT)/rumors del $(DAT)/rumors) + $(subst /,\,if exist $(DAT)/dungeon.pdf del $(DAT)/dungeon.pdf) + $(subst /,\,if exist $(DAT)/dungeon del $(DAT)/dungeon) + $(subst /,\,if exist $(DAT)/oracles del $(DAT)/oracles) + $(subst /,\,if exist $(DAT)/quest.dat del $(DAT)/quest.dat) + $(subst /,\,if exist $(DAT)/dlb.lst del $(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/nhdat del $(DAT)/nhdat) + $(subst /,\,if exist $(DAT)/*.lev del $(DAT)/*.lev) + $(subst /,\,if exist $(PLANAR_TIB) del $(PLANAR_TIB)) + $(subst /,\,if exist $(OVERVIEW_TIB) del $(OVERVIEW_TIB)) + $(subst /,\,if exist $(WSHR)/monthin.txt del $(WSHR)/monthin.txt) + $(subst /,\,if exist $(WSHR)/objthin.txt del $(WSHR)/objthin.txt) + $(subst /,\,if exist $(WSHR)/oththin.txt del $(WSHR)/oththin.txt) + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + -$(subst /,\,@if not exist $(OBJ)/*.* mkdir $(OBJ)) + @$(subst /,\,@echo directory created > $@) + +#=========================================== +# Work around some djgpp long file name woes +#=========================================== + +$(PATCHLEV_H): + @$(subst /,\,if not exist $@ copy $(INCL)/patchlevel.h $(INCL)/patchlev.h) + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== +# +# The following include files depend on makedefs to be created. +# +# date.h should be remade every time any of the source or include +# files is modified. + + +$(INCL)/date.h : $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -v) + +$(INCL)/onames.h: $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -o) + +$(INCL)/pm.h: $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -p) + +monstr.c: $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -m) + +$(INCL)/vis_tab.h: $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -z) + +vis_tab.c: $(U)makedefs.exe + -$(subst /,\,$(U)makedefs -z) + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + $(LINK) $(LFLAGS) -o$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(PERMONST_H) $(INCL)/objclass.h \ + $(INCL)/monsym.h $(INCL)/qtext.h $(U)makedefs.c + +#========================================== +# Level Compiler Dependencies +#========================================== + +$(U)lev_comp.exe: $(SPLEVOBJS) + $(LINK) $(LFLAGS) -o$@ $(SPLEVOBJS) + +ifeq ($(YACC_LEX),Y) + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(U)lev_yacc.c + $(CC) $(cflags) -o$@ $(U)lev_yacc.c + +else + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)/lev_comp.h $(U)lev_yacc.c + $(CC) $(cflags) -o$@ $(U)lev_yacc.c + +endif + +$(O)lev_$(LEX).o: $(HACK_H) $(SP_LEV_H) $(INCL)/lev_comp.h \ + $(U)lev_$(LEX).c + $(CC) $(cflags) -o$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(HACK_H) $(INCL)/sp_lev.h $(INCL)/date.h $(U)lev_main.c + +ifeq "$(DO_YACC)" "YACC_ACT" + +$(INCL)/lev_comp.h: $(U)lev_yacc.c + +$(U)lev_yacc.c $(INCL)/lev_comp.h : $(U)lev_comp.y + @$(subst /,\,chdir $(UTIL)) + @$(subst /,\,$(YACC) -d lev_comp.y) + @$(subst /,\,copy $(YTABC) lev_yacc.c) + @$(subst /,\,copy $(YTABH) $(INCL)/lev_comp.h) + @$(subst /,\,@del $(YTABC)) + @$(subst /,\,@del $(YTABH)) + @$(subst /,\,chdir $(SRC)) +else + +$(U)lev_yacc.c: $(SSHR)/lev_yacc.c + @echo --- + @echo For now, we will copy the prebuilt + @echo lev_comp.c from $(SSHR) into $(U) and use that. + @$(subst /,\,copy $(SSHR)/lev_yacc.c $(U)lev_yacc.c) + @$(subst /,\,echo.>>$(U)lev_yacc.c) + +$(INCL)/lev_comp.h : $(SSHR)/lev_comp.h + @echo --- + @echo For now, we will copy the prebuilt lev_comp.h + @echo from $(SSHR) into $(INCL) and use that. + @$(subst /,\,copy $(SSHR)/lev_comp.h $(INCL)/lev_comp.h) + @$(subst /,\,echo.>>$(INCL)/lev_comp.h) + +endif + +$(U)lev_$(LEX).c: $(U)lev_comp.l +ifeq "$(DO_LEX)" "LEX_ACT" + @$(subst /,\,chdir $(UTIL)) + @$(subst /,\,$(LEX) $(FLEXSKEL) lev_comp.l) + @$(subst /,\,if exist $@ del $@) + @$(subst /,\,copy $(LEXYYC) $@) + @$(subst /,\,del $(LEXYYC)) + @$(subst /,\,chdir $(SRC)) +else + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSHR) into $(U) and use it. + @$(subst /,\,copy $(SSHR)/lev_lex.c $@) + @$(subst /,\,echo.>>$@) +endif + +#========================================== +# Dungeon Dependencies +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + $(LINK) $(LFLAGS) -o$@ $(DGNCOMPOBJS) + +ifeq "$(DO_YACC)" "YACC_ACT" +$(U)dgn_yacc.c $(INCL)/dgn_comp.h : $(U)dgn_comp.y + @$(subst /,\,chdir $(UTIL)) + @$(subst /,\,$(YACC) -d dgn_comp.y) + @$(subst /,\,copy $(YTABC) dgn_yacc.c) + @$(subst /,\,copy $(YTABH) $(INCL)/dgn_comp.h) + @$(subst /,\,@del $(YTABC)) + @$(subst /,\,@del $(YTABH)) + @$(subst /,\,chdir $(SRC)) +else +$(U)dgn_yacc.c: $(SSHR)/dgn_yacc.c + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSHR) into $(U) and use that. + @$(subst /,\,copy $(SSHR)/dgn_yacc.c $(U)dgn_yacc.c) + @$(subst /,\,echo.>>$(U)dgn_yacc.c) + +$(INCL)/dgn_comp.h: $(SSHR)/dgn_comp.h + @echo --- + @echo For now, we will copy the prebuilt dgn_comp.h + @echo from $(SSHR) into $(INCL) and use that. + @$(subst /,\,copy $(SSHR)/dgn_comp.h $(INCL)/dgn_comp.h) + @$(subst /,\,echo.>>$(INCL)/dgn_comp.h) + +endif + +ifeq "$(DO_LEX)" "LEX_ACT" + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l $(INCL)/dgn_comp.h + @$(subst /,\,chdir $(UTIL)) + @$(subst /,\,$(LEX) $(FLEXSKEL) dgn_comp.l) + @$(subst /,\,if exist $@ del $@) + @$(subst /,\,copy $(LEXYYC) $@) + @$(subst /,\,del $(LEXYYC)) + @$(subst /,\,chdir $(SRC)) +else + +$(U)dgn_$(LEX).c: $(SSHR)/dgn_lex.c $(INCL)/dgn_comp.h + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSHR) into $(U) and use it. + @$(subst /,\,copy $(SSHR)/dgn_lex.c $@) + @$(subst /,\,echo.>>$@) + +endif + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) + $(LINK) $(LFLAGS) -o$@ $(O)recover.o + +$(O)recover.o: $(CONFIG_H) $(U)recover.c + $(CC) $(cflags) -o$@ $(U)recover.c + +#========================================== +# Header file moves required for tile support +#========================================== + +ifeq ($(SUPPRESS_GRAPHICS),Y) + +else +# +# Tile Mapping +# + +$(SRC)/tile.c: $(U)tilemap.exe + @$(subst /,\,$(U)tilemap.exe) + @echo A new $@ has been created + +$(U)tilemap.exe: $(O)tilemap.o + $(LINK) $(LFLAGS) -o$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)/tilemap.c $(HACK_H) $(TILE_H) + $(CC) $(cflags) -I$(WSHR) -I$(MSYS) -o$@ $(WSHR)/tilemap.c + + +#========================================== +# Tile Utilities +# Required for tile support +#========================================== + +$(DAT)/NetHack1.tib: $(TILEFILES) $(U)tile2bin.exe + @echo Creating binary tile files (this may take some time) + @$(subst /,\,chdir $(DAT)) + @$(subst /,\,$(U)tile2bin.exe) + @$(subst /,\,chdir $(SRC)) + +$(DAT)/NetHacko.tib: $(O)thintile.tag $(TILEFILES2) $(U)til2bin2.exe + @echo Creating overview binary tile files (this may take some time) + @$(subst /,\,chdir $(DAT)) + @$(subst /,\,$(U)til2bin2.exe) + @$(subst /,\,chdir $(SRC)) + +$(U)tile2bin.exe: $(O)tile2bin.o $(TEXTIO) + $(LINK) $(LFLAGS) -o$@ $(O)tile2bin.o $(TEXTIO) + +$(U)til2bin2.exe: $(O)til2bin2.o $(TEXTIO2) + $(LINK) $(LFLAGS) -o$@ $(O)til2bin2.o $(TEXTIO2) + +$(U)thintile.exe: $(O)thintile.o + $(LINK) $(LFLAGS) -o$@ $(O)thintile.o + +$(O)thintile.o: $(HACK_H) $(WSHR)/tile.h $(WSHR)/thintile.c + $(CC) $(cflags) -o$@ $(WSHR)/thintile.c + +$(O)thintile.tag: $(U)thintile.exe $(TILEFILES) + @$(subst /,\,$(U)thintile.exe) + @$(subst /,\,echo thintiles created >$@) + +$(O)tile2bin.o: $(HACK_H) $(TILE_H) $(MSYS)/pctiles.h $(MSYS)/pcvideo.h $(MSYS)/tile2bin.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -o$@ $(MSYS)/tile2bin.c + +$(O)til2bin2.o: $(HACK_H) $(TILE_H) $(MSYS)/pctiles.h $(MSYS)/pcvideo.h $(MSYS)/tile2bin.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -DTILE_X=8 -DOVERVIEW_FILE -o$@ $(MSYS)/tile2bin.c + +$(O)tiletext.o: $(CONFIG_H) $(TILE_H) $(WSHR)/tiletext.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -o$@ $(WSHR)/tiletext.c + +$(O)tiletex2.o: $(CONFIG_H) $(TILE_H) $(WSHR)/tiletext.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -DTILE_X=8 -o$@ $(WSHR)/tiletext.c + +$(O)tiletxt.o: $(CONFIG_H) $(TILE_H) $(WSHR)/tilemap.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -DTILETEXT -o$@ $(WSHR)/tilemap.c + +$(O)tiletxt2.o: $(CONFIG_H) $(TILE_H) $(WSHR)/tilemap.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -DTILETEXT -DTILE_X=8 -o$@ $(WSHR)/tilemap.c +# +# Optional GIF Utilities (for development) +# + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXTIO) + $(LINK) $(LFLAGS) -o$@ $(GIFREADERS) $(TEXTIO) + +$(U)gif2txt2.exe: $(GIFREAD2) $(TEXTIO2) + $(LINK) $(LFLAGS) -o$@ $(GIFREAD2) $(TEXTIO2) + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXTIO) + $(LINK) $(LFLAGS) -o$@ $(PPMWRITERS) $(TEXTIO) + +$(U)txt2ppm2.exe: $(PPMWRIT2) $(TEXTIO2) + $(LINK) $(LFLAGS) -o$@ $(PPMWRIT2) $(TEXTIO2) + +$(O)gifread.o: $(CONFIG_H) $(WSHR)/tile.h $(WSHR)/gifread.c + +$(O)gifread2.o: $(CONFIG_H) $(WSHR)/tile.h $(WSHR)/gifread.c + $(CC) $(cflags) -DTILE_X=8 -o$@ $(WSHR)/gifread.c + +ppmwrite.c: $(WSHR)/ppmwrite.c + @$(subst /,\,copy $(WSHR)/ppmwrite.c .) + +$(O)ppmwrite.o: $(CONFIG_H) $(WSHR)/tile.h + +$(O)ppmwrit2.o: $(CONFIG_H) $(WSHR)/tile.h ppmwrite.c + $(CC) $(cflags) -DTILE_X=8 -o$@ ppmwrite.c + +# +# Optional tile viewer (development sources only) +# + +$(U)viewtib.exe: $(O)viewtib.o + $(LINK) $(LFLAGS) -o$@ $(O)viewtib.o $(LIBRARIES) + +$(O)viewtib.o: $(MSYS)/viewtib.c + +endif + +#========================================== +# Other Util Dependencies. +#========================================== + +$(O)alloc.o: $(CONFIG_H) alloc.c + $(CC) $(cflags) -o$@ alloc.c + +$(O)drawing.o: $(CONFIG_H) drawing.c $(MSYS)/pcvideo.h + $(CC) $(cflags) -I$(MSYS) -o$@ drawing.c + +$(O)decl.o: $(CONFIG_H) decl.c + $(CC) $(cflags) -o$@ decl.c + +$(O)monst.o: $(CONFIG_H) $(PERMONST_H) $(ESHK_H) \ + $(EPRI_H) $(VAULT_H) $(INCL)/monsym.h \ + $(INCL)/color.h monst.c + $(CC) $(cflags) -o$@ monst.c + +$(O)objects.o: $(CONFIG_H) $(INCL)/obj.h $(INCL)/objclass.h \ + $(INCL)/prop.h $(INCL)/color.h objects.c + $(CC) $(cflags) -o$@ objects.c + +$(O)panic.o: $(CONFIG_H) $(U)panic.c + +#============================================================ +# make data.base an 8.3 filename to prevent an make warning +#============================================================ + +DATABASE = $(DAT)/data.bas + + +$(O)dat.tag: $(DAT)/nhdat + @$(subst /,\,echo dat done >$@) + +$(DAT)/data: $(O)utility.tag $(DATABASE) + @$(subst /,\,$(U)makedefs.exe -d) + +$(DAT)/rumors: $(O)utility.tag $(DAT)/rumors.tru $(DAT)/rumors.fal + @$(subst /,\,$(U)makedefs.exe -r) + +$(DAT)/quest.dat: $(O)utility.tag $(DAT)/quest.txt + @$(subst /,\,$(U)makedefs.exe -q) + +$(DAT)/oracles: $(O)utility.tag $(DAT)/oracles.txt + @$(subst /,\,$(U)makedefs.exe -h) + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)/bigroom.des $(DAT)/castle.des \ + $(DAT)/endgame.des $(DAT)/gehennom.des $(DAT)/knox.des \ + $(DAT)/medusa.des $(DAT)/oracle.des $(DAT)/tower.des \ + $(DAT)/yendor.des $(DAT)/arch.des $(DAT)/barb.des \ + $(DAT)/caveman.des $(DAT)/healer.des $(DAT)/knight.des \ + $(DAT)/monk.des $(DAT)/priest.des $(DAT)/ranger.des \ + $(DAT)/rogue.des $(DAT)/samurai.des $(DAT)/tourist.des \ + $(DAT)/valkyrie.des $(DAT)/wizard.des + @$(subst /,\,cd $(DAT)) + @$(subst /,\,$(U)lev_comp bigroom.des) + @$(subst /,\,$(U)lev_comp castle.des) + @$(subst /,\,$(U)lev_comp endgame.des) + @$(subst /,\,$(U)lev_comp gehennom.des) + @$(subst /,\,$(U)lev_comp knox.des) + @$(subst /,\,$(U)lev_comp mines.des) + @$(subst /,\,$(U)lev_comp medusa.des) + @$(subst /,\,$(U)lev_comp oracle.des) + @$(subst /,\,$(U)lev_comp sokoban.des) + @$(subst /,\,$(U)lev_comp tower.des) + @$(subst /,\,$(U)lev_comp yendor.des) + @$(subst /,\,$(U)lev_comp arch.des) + @$(subst /,\,$(U)lev_comp barb.des) + @$(subst /,\,$(U)lev_comp caveman.des) + @$(subst /,\,$(U)lev_comp healer.des) + @$(subst /,\,$(U)lev_comp knight.des) + @$(subst /,\,$(U)lev_comp monk.des) + @$(subst /,\,$(U)lev_comp priest.des) + @$(subst /,\,$(U)lev_comp ranger.des) + @$(subst /,\,$(U)lev_comp rogue.des) + @$(subst /,\,$(U)lev_comp samurai.des) + @$(subst /,\,$(U)lev_comp tourist.des) + @$(subst /,\,$(U)lev_comp valkyrie.des) + @$(subst /,\,$(U)lev_comp wizard.des) + @$(subst /,\,cd $(SRC)) + @$(subst /,\,echo sp_levs done > $@) + +$(DAT)/dungeon: $(O)utility.tag $(DAT)/dungeon.def + @$(subst /,\,$(U)makedefs.exe -e) + @$(subst /,\,cd $(DAT)) + @$(subst /,\,$(U)dgn_comp.exe dungeon.pdf) + @$(subst /,\,cd $(SRC)) + +#========================================== +# DLB stuff +#========================================== + +#note that dir below assumes bin/dir.exe from djgpp distribution +# +$(DAT)/nhdat: $(U)dlb_main.exe $(DAT)/data $(DAT)/rumors $(DAT)/dungeon \ + $(DAT)/oracles $(DAT)/quest.dat $(O)sp_lev.tag + @$(subst /,\,echo dat done >$(O)dat.tag) + @$(subst /,\,cd $(DAT)) + @$(subst /,\,copy $(MSYS)/msdoshlp.txt .) + @$(subst /,\,echo data >dlb.lst) + @$(subst /,\,echo dungeon >>dlb.lst) + @$(subst /,\,echo oracles >>dlb.lst) + @$(subst /,\,echo options >>dlb.lst) + @$(subst /,\,echo quest.dat >>dlb.lst) + @$(subst /,\,echo rumors >>dlb.lst) + @$(subst /,\,echo help >>dlb.lst) + @$(subst /,\,echo hh >>dlb.lst) + @$(subst /,\,echo cmdhelp >>dlb.lst) + @$(subst /,\,echo history >>dlb.lst) + @$(subst /,\,echo opthelp >>dlb.lst) + @$(subst /,\,echo wizhelp >>dlb.lst) + @$(subst /,\,echo license >>dlb.lst) + @$(subst /,\,echo msdoshlp.txt >>dlb.lst) + $(LS) $(subst /,\,*.lev) >>dlb.lst + @$(subst /,\,$(U)dlb_main cvIf dlb.lst nhdat) + @$(subst /,\,cd $(SRC)) + +$(U)dlb_main.exe: $(DLBOBJS) + $(LINK) $(LFLAGS) -o$@ $(DLBOBJS) + +$(O)dlb_main.o: $(U)dlb_main.c $(INCL)/config.h $(DLB_H) + $(CC) $(cflags) -o$@ $(U)dlb_main.c + +#========================================== +# Game Dependencies +#========================================== + +# sys/share +$(O)main.o: $(HACK_H) $(DLB_H) $(SSHR)/pcmain.c + $(CC) $(cflags) -o$@ $(SSHR)/pcmain.c + +$(O)tty.o: $(HACK_H) $(INCL)/wintty.h $(SSHR)/pctty.c + $(CC) $(cflags) -o$@ $(SSHR)/pctty.c + +$(O)unix.o: $(HACK_H) $(SSHR)/pcunix.c + $(CC) $(cflags) -o$@ $(SSHR)/pcunix.c + +$(O)sys.o : $(HACK_H) $(SSHR)/pcsys.c + $(CC) $(cflags) -o$@ $(SSHR)/pcsys.c + +# sys/msdos +$(O)msdos.o : $(HACK_H) $(MSYS)/msdos.c +# $(CC) $(cflags) -o$@ $(MSYS)/msdos.c + +$(O)pckeys.o : $(HACK_H) $(MSYS)/pckeys.c +# $(CC) $(cflags) -o$@ $(MSYS)/pckeys.c + +$(O)pctiles.o : $(HACK_H) $(MSYS)/pctiles.c $(MSYS)/portio.h + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -o$@ $(MSYS)/pctiles.c + +$(O)sound.o : $(HACK_H) $(MSYS)/sound.c $(MSYS)/portio.h +# $(CC) $(cflags) -o$@ $(MSYS)/sound.c + +$(O)video.o : $(HACK_H) $(MSYS)/pcvideo.h $(MSYS)/portio.h $(MSYS)/video.c +# $(CC) $(cflags) -o$@ -I$(MSYS) $(MSYS)/video.c + +$(O)vidvga.o : $(HACK_H) $(MSYS)/pcvideo.h $(MSYS)/portio.h $(TILE_H) $(MSYS)/vidvga.c + $(CC) $(cflags) -I$(MSYS) -I$(WSHR) -o$@ $(MSYS)/vidvga.c + +$(O)vidtxt.o : $(HACK_H) $(MSYS)/pcvideo.h $(MSYS)/portio.h $(TILE_H) $(MSYS)/vidtxt.c +# $(CC) $(cflags) -o$@ -I$(MSYS) $(MSYS)/vidtxt.c + +$(O)stubvid.o : $(HACK_H) $(MSYS)/pcvideo.h $(MSYS)/video.c + $(CC) $(cflags) -I$(MSYS) -DSTUBVIDEO -o$@ $(MSYS)/video.c + + +# src dependencies + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# o -c (which is included in cflags) substituted with -o$@ , +# o an explicit build instruction for dlb.o because it requires +# a .h file in ../sys/msdos. +# o the PATCHLEV_H macro is substitued for $(INCL)/patchlevel.h +# to work around a long filename issue. +# o $(CFLAGS) changed to $(cflags) +# Other than that, these dependencies are untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ../sys/atari/tos.c $(HACK_H) $(INCL)/tcap.h + $(CC) $(cflags) -o$@ ../sys/atari/tos.c +$(O)pcmain.o: ../sys/share/pcmain.c $(HACK_H) $(INCL)/dlb.h \ + #$(INCL)/win32api.h + $(CC) $(cflags) -o$@ ../sys/share/pcmain.c +$(O)pcsys.o: ../sys/share/pcsys.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/share/pcsys.c +$(O)pctty.o: ../sys/share/pctty.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/share/pctty.c +$(O)pcunix.o: ../sys/share/pcunix.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/share/pcunix.c +$(O)random.o: ../sys/share/random.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/share/random.c +$(O)ioctl.o: ../sys/share/ioctl.c $(HACK_H) $(INCL)/tcap.h + $(CC) $(cflags) -o$@ ../sys/share/ioctl.c +$(O)unixtty.o: ../sys/share/unixtty.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/share/unixtty.c +$(O)unixmain.o: ../sys/unix/unixmain.c $(HACK_H) $(INCL)/dlb.h + $(CC) $(cflags) -o$@ ../sys/unix/unixmain.c +$(O)unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(CC) $(cflags) -o$@ ../sys/unix/unixunix.c +$(O)unixres.o: ../sys/unix/unixres.c $(CONFIG_H) + $(CC) $(cflags) -o$@ ../sys/unix/unixres.c +$(O)bemain.o: ../sys/be/bemain.c $(HACK_H) $(INCL)/dlb.h + $(CC) $(cflags) -o$@ ../sys/be/bemain.c +$(O)getline.o: ../win/tty/getline.c $(HACK_H) $(INCL)/func_tab.h + $(CC) $(cflags) -o$@ ../win/tty/getline.c +$(O)termcap.o: ../win/tty/termcap.c $(HACK_H) $(INCL)/tcap.h + $(CC) $(cflags) -o$@ ../win/tty/termcap.c +$(O)topl.o: ../win/tty/topl.c $(HACK_H) $(INCL)/tcap.h + $(CC) $(cflags) -o$@ ../win/tty/topl.c +$(O)wintty.o: ../win/tty/wintty.c $(HACK_H) $(INCL)/dlb.h \ + $(PATCHLEV_H) $(INCL)/tcap.h + $(CC) $(cflags) -o$@ ../win/tty/wintty.c +$(O)Window.o: ../win/X11/Window.c $(INCL)/xwindowp.h $(INCL)/xwindow.h \ + $(CONFIG_H) + $(CC) $(cflags) -o$@ ../win/X11/Window.c +$(O)dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) + $(CC) $(cflags) -o$@ ../win/X11/dialogs.c +$(O)winX.o: ../win/X11/winX.c $(HACK_H) $(INCL)/winX.h $(INCL)/dlb.h \ + $(PATCHLEV_H) ../win/X11/nh72icon \ + ../win/X11/nh56icon ../win/X11/nh32icon + $(CC) $(cflags) -o$@ ../win/X11/winX.c +$(O)winmap.o: ../win/X11/winmap.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/winX.h $(INCL)/tile2x11.h + $(CC) $(cflags) -o$@ ../win/X11/winmap.c +$(O)winmenu.o: ../win/X11/winmenu.c $(HACK_H) $(INCL)/winX.h + $(CC) $(cflags) -o$@ ../win/X11/winmenu.c +$(O)winmesg.o: ../win/X11/winmesg.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/winX.h + $(CC) $(cflags) -o$@ ../win/X11/winmesg.c +$(O)winmisc.o: ../win/X11/winmisc.c $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/winX.h + $(CC) $(cflags) -o$@ ../win/X11/winmisc.c +$(O)winstat.o: ../win/X11/winstat.c $(HACK_H) $(INCL)/winX.h + $(CC) $(cflags) -o$@ ../win/X11/winstat.c +$(O)wintext.o: ../win/X11/wintext.c $(HACK_H) $(INCL)/winX.h $(INCL)/xwindow.h + $(CC) $(cflags) -o$@ ../win/X11/wintext.c +$(O)winval.o: ../win/X11/winval.c $(HACK_H) $(INCL)/winX.h + $(CC) $(cflags) -o$@ ../win/X11/winval.c +$(O)tile.o: tile.c $(HACK_H) +$(O)gnaskstr.o: ../win/gnome/gnaskstr.c ../win/gnome/gnaskstr.h \ + ../win/gnome/gnmain.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnaskstr.c +$(O)gnbind.o: ../win/gnome/gnbind.c ../win/gnome/gnbind.h ../win/gnome/gnmain.h \ + ../win/gnome/gnaskstr.h ../win/gnome/gnyesno.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnbind.c +$(O)gnglyph.o: ../win/gnome/gnglyph.c ../win/gnome/gnglyph.h $(INCL)/tile2x11.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnglyph.c +$(O)gnmain.o: ../win/gnome/gnmain.c ../win/gnome/gnmain.h ../win/gnome/gnsignal.h \ + ../win/gnome/gnbind.h ../win/gnome/gnopts.h $(HACK_H) \ + $(INCL)/date.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnmain.c +$(O)gnmap.o: ../win/gnome/gnmap.c ../win/gnome/gnmap.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h $(HACK_H) + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnmap.c +$(O)gnmenu.o: ../win/gnome/gnmenu.c ../win/gnome/gnmenu.h ../win/gnome/gnmain.h \ + ../win/gnome/gnbind.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnmenu.c +$(O)gnmesg.o: ../win/gnome/gnmesg.c ../win/gnome/gnmesg.h ../win/gnome/gnsignal.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnmesg.c +$(O)gnopts.o: ../win/gnome/gnopts.c ../win/gnome/gnopts.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnmain.h ../win/gnome/gnmap.h $(HACK_H) + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnopts.c +$(O)gnplayer.o: ../win/gnome/gnplayer.c ../win/gnome/gnplayer.h \ + ../win/gnome/gnmain.h $(HACK_H) + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnplayer.c +$(O)gnsignal.o: ../win/gnome/gnsignal.c ../win/gnome/gnsignal.h \ + ../win/gnome/gnmain.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnsignal.c +$(O)gnstatus.o: ../win/gnome/gnstatus.c ../win/gnome/gnstatus.h \ + ../win/gnome/gnsignal.h ../win/gnome/gn_xpms.h \ + ../win/gnome/gnomeprv.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnstatus.c +$(O)gntext.o: ../win/gnome/gntext.c ../win/gnome/gntext.h ../win/gnome/gnmain.h \ + ../win/gnome/gn_rip.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gntext.c +$(O)gnworn.o: ../win/gnome/gnworn.c ../win/gnome/gnworn.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h ../win/gnome/gnomeprv.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnworn.c +$(O)gnyesno.o: ../win/gnome/gnyesno.c ../win/gnome/gnbind.h ../win/gnome/gnyesno.h + $(CC) $(cflags) $(GNOMEINC) -o$@ ../win/gnome/gnyesno.c +$(O)wingem.o: ../win/gem/wingem.c $(HACK_H) $(INCL)/func_tab.h $(INCL)/dlb.h \ + $(PATCHLEV_H) $(INCL)/wingem.h + $(CC) $(cflags) -o$@ ../win/gem/wingem.c +$(O)wingem1.o: ../win/gem/wingem1.c $(INCL)/gem_rsc.h $(INCL)/load_img.h \ + $(INCL)/gr_rect.h $(INCL)/wintype.h $(INCL)/wingem.h + $(CC) $(cflags) -o$@ ../win/gem/wingem1.c +$(O)load_img.o: ../win/gem/load_img.c $(INCL)/load_img.h + $(CC) $(cflags) -o$@ ../win/gem/load_img.c +$(O)gr_rect.o: ../win/gem/gr_rect.c $(INCL)/gr_rect.h + $(CC) $(cflags) -o$@ ../win/gem/gr_rect.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/dlb.h $(PATCHLEV_H) $(INCL)/tile2x11.h \ + $(INCL)/qt_win.h $(INCL)/qt_clust.h $(INCL)/qt_kde0.h \ + $(INCL)/qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_win.cpp +$(O)qt_clust.o: ../win/Qt/qt_clust.cpp $(INCL)/qt_clust.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_clust.cpp +$(O)qttableview.o: ../win/Qt/qttableview.cpp $(INCL)/qttableview.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qttableview.cpp +$(O)monstr.o: monstr.c $(CONFIG_H) +$(O)vis_tab.o: vis_tab.c $(CONFIG_H) $(INCL)/vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)/edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)/artifact.h $(INCL)/artilist.h +$(O)attrib.o: attrib.c $(HACK_H) +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)/lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)/func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)/artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)/edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)/dlb.h + $(CC) $(cflags) -I../sys/msdos -o$@ dlb.c +$(O)do.o: do.c $(HACK_H) $(INCL)/lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)/edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)/eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)/tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)/dgn_file.h $(INCL)/dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)/eshk.h $(INCL)/dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)/lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)/dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) +$(O)light.o: light.c $(HACK_H) $(INCL)/lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)/mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)/epri.h $(INCL)/emin.h \ + $(INCL)/edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)/emin.h $(INCL)/epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)/sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)/sp_lev.h $(INCL)/lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)/eshk.h $(INCL)/epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/artifact.h \ + $(INCL)/epri.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)/permonst.h $(INCL)/align.h \ + $(INCL)/monattk.h $(INCL)/monflag.h $(INCL)/monsym.h \ + $(INCL)/dungeon.h $(INCL)/eshk.h $(INCL)/vault.h \ + $(INCL)/epri.h $(INCL)/color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)/edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)/lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)/obj.h $(INCL)/objclass.h \ + $(INCL)/prop.h $(INCL)/skills.h $(INCL)/color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)/objclass.h $(INCL)/flag.h \ + $(HACK_H) $(INCL)/tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)/dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)/epri.h $(INCL)/edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)/epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/eshk.h \ + $(INCL)/epri.h $(INCL)/emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)/qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)/dlb.h $(INCL)/qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)/lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)/lev.h $(INCL)/tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)/lev.h $(INCL)/dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)/lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)/eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)/eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)/artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)/edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)/dlb.h $(INCL)/sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)/lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)/dlb.h $(PATCHLEV_H) +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)/vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)/date.h $(PATCHLEV_H) +$(O)vision.o: vision.c $(HACK_H) $(INCL)/vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)/wingem.h $(INCL)/winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)/qtext.h $(INCL)/epri.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)/lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file + diff --git a/sys/msdos/Makefile.MSC b/sys/msdos/Makefile.MSC new file mode 100644 index 0000000..53d418a --- /dev/null +++ b/sys/msdos/Makefile.MSC @@ -0,0 +1,1113 @@ +# SCCS Id: @(#)Makefile.MSC 3.4 2002/09/10 +# Copyright (c) NetHack PC Development Team 1997 - 2002. +# PC NetHack 3.3x Makefile for MSC V1.52c (16 bit compiler) +# +# For questions or comments: nethack-bugs@nethack.org +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc +# .l lex +# +# + +# Game Installation Variables +# NOTE: Make sure GAMEDIR exists before make is started. + +GAME = nethack +GAMEDIR =..\binary + +# +# Directories +# + +DAT = ..\dat +DOC = ..\doc +INCL = ..\include +MSYS = ..\sys\msdos +SRC = ..\src +SSHR = ..\sys\share +UTIL = ..\util +WIN = ..\win\tty +WSHR = ..\win\share + +# +# Executables. + +CC = cl +LINK = link +MAKEBIN = nmake + +# if you have a uudecode program, add its name here +# otherwise leave blank +UUDECODE = + +# +# Yacc/Lex ... if you got 'em. +# +# If you have yacc/lex or a work-alike set YACC_LEX to Y +# +YACC_LEX = N + +# If YACC_LEX is Y above, set the following to values appropriate for +# your tools. +# +YACC = bison -y +LEX = flex +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + + +# +# Uncomment this line if you want to include support for ALT-numeric +# sequences, such as ALT-2 for toggling #twoweapon mode. +# Note that this code did not get a thorough testing prior to 3.4.x +#NEWALT=/DNEW_ALT + +############################################################################# +# +# nothing below this line should have to be changed +# + +LNKOPT = SCHEMA3.DEF + +# +# Controls whether MOVE tracing is enabled in the executable +# This should be left commented unless you are tinkering with the +# overlay structure of NetHack. The executable runs _very_ +# slowly when the movetr.lib is linked in. +# + +#MOVETR= movetr.lib + +# do not change this +! IF ("$(MOVETR)"!="") +MVTRCL = /DMOVE_PROF +! ELSE +MVTRCL = +! ENDIF + +# +# Uncomment the line below if you want to store all the level files, +# help files, etc. in a single library file. + +USE_DLB = Y + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = -DDLB +! ELSE +DLBFLG = +! ENDIF + +LIBRARIES = $(LIBS) $(TERMLIB) + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +# +# Flags. +# +# Debugging +#CFLAGS = /Zi /DFUNCTION_LEVEL_LINKING /DUSE_TILES /DDLB +#LFLAGS = /CODEVIEW /NOI/MAP /CPARM:1 /INFO + +# Normal +LFLAGS = /NOI/MAP /CPARM:1 /INFO +CFLAGS = /DFUNCTION_LEVEL_LINKING /DUSE_TILES /DDLB +SPECOPTS = +# +# Leaving MACHINE_CODE undefined will allow it to run +# on any Intel 8088 machines and above. +# Set to 1 for 80186 and above only +# Set to 2 for 80286 and above only +# Set to 3 for 80386 and above only +# +MACHINE_CODE = + +# +# Utility Objects. +# +# +# Shorten up the location for some files +# + +O = $(OBJ)\ # comment so \ isn't last char + +U = $(UTIL)\ # comment so \ isn't last char + +SPLEVDES = $(DAT)\Arch.des $(DAT)\Barb.des $(DAT)\bigroom.des \ + $(DAT)\castle.des $(DAT)\Caveman.des $(DAT)\endgame.des \ + $(DAT)\gehennom.des $(DAT)\Healer.des $(DAT)\Knight.des \ + $(DAT)\knox.des $(DAT)\Monk.des $(DAT)\medusa.des \ + $(DAT)\mines.des $(DAT)\oracle.des $(DAT)\Priest.des \ + $(DAT)\Ranger.des $(DAT)\Rogue.des $(DAT)\Samurai.des \ + $(DAT)\Tourist.des $(DAT)\tower.des $(DAT)\Valkyrie.des \ + $(DAT)\Wizard.des $(DAT)\yendor.des + +VGAOBJ = vidvga.o + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c \ + $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = makedefs.o monst.o objects.o + +SPLEVOBJS = lev_yacc.o lev_$(LEX).o lev_main.o alloc.o \ + monst.o objects.o panic.o \ + drawing.o decl.o stubvid.o + +DGNCOMPOBJS = dgn_yacc.o dgn_$(LEX).o dgn_main.o alloc.o \ + panic.o + +RECOVOBJS = recover.o + + +# Tile related object files. + +TILOBJ = tile.o pctiles.o $(VGAOBJ) + +TEXTIO = tiletext.o tiletxt.o drawing.o decl.o monst.o objects.o stubvid.o + +TEXTIO2 = tiletex2.o tiletxt2.o drawing.o decl.o monst.o objects.o stubvid.o + + +PLANAR_TIB = NetHack1.tib + +OVERVIEW_TIB = NetHacko.tib + +TILEUTIL = $(TILOBJ) tile2bin.exe til2bin2.exe $(PLANAR_TIB) $(OVERVIEW_TIB) + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +TILEFILES2 = $(WSHR)\monthin.txt $(WSHR)\objthin.txt $(WSHR)\oththin.txt + +GIFREADERS = gifread.o alloc.o panic.o + +GIFREAD2 = gifread2.o alloc.o panic.o + +PPMWRITERS = ppmwrite.o alloc.o panic.o + +PPMWRIT2 = ppmwrit2.o alloc.o panic.o + +DLBOBJS = dlb_main.o dlb.o alloc.o panic.o + +# Object files for the game itself. + +VOBJ01 = allmain.o alloc.o apply.o artifact.o attrib.o +VOBJ02 = ball.o bones.o botl.o cmd.o dbridge.o +VOBJ03 = decl.o detect.o display.o do.o do_name.o +VOBJ04 = do_wear.o dog.o dogmove.o dokick.o dothrow.o +VOBJ05 = drawing.o dungeon.o eat.o end.o engrave.o +VOBJ06 = exper.o explode.o extralev.o files.o fountain.o +VOBJ07 = getline.o hack.o hacklib.o invent.o lock.o +VOBJ08 = mail.o main.o makemon.o mapglyph.o mcastu.o mhitm.o +VOBJ09 = mhitu.o minion.o mkmap.o mklev.o mkmaze.o +VOBJ10 = mkobj.o mkroom.o mon.o mondata.o monmove.o +VOBJ11 = monst.o monstr.o mplayer.o mthrowu.o muse.o +VOBJ12 = music.o o_init.o objects.o objnam.o options.o +VOBJ13 = pickup.o pline.o polyself.o potion.o quest.o +VOBJ14 = questpgr.o pager.o pray.o priest.o read.o +VOBJ15 = rect.o restore.o rip.o rnd.o role.o +VOBJ16 = rumors.o save.o shk.o shknam.o sit.o +VOBJ17 = sounds.o sp_lev.o spell.o steal.o steed.o +VOBJ18 = termcap.o timeout.o topl.o topten.o track.o +VOBJ19 = trap.o u_init.o uhitm.o vault.o vision.o +VOBJ20 = vis_tab.o weapon.o were.o wield.o windows.o +VOBJ21 = wintty.o wizard.o worm.o worn.o write.o +VOBJ22 = zap.o light.o dlb.o dig.o teleport.o +VOBJ23 = random.o region.o + +SOBJ = msdos.o sound.o sys.o tty.o unix.o video.o \ + vidtxt.o pckeys.o + +VVOBJ = version.o + +VOBJ = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) + +ALLOBJ = $(VOBJ) $(SOBJ) $(TILOBJ) $(VVOBJ) + +# +# Header Objects. +# + +DGN_FILE_H = $(INCL)\align.h $(INCL)\dgn_file.h +DUNGEON_H = $(INCL)\align.h $(INCL)\dungeon.h +EMIN_H = $(DUNGEON_H) $(INCL)\emin.h +EPRI_H = $(DUNGEON_H) $(INCL)\align.h $(INCL)\epri.h +ESHK_H = $(DUNGEON_H) $(INCL)\eshk.h +MONDATA_H = $(INCL)\align.h $(INCL)\mondata.h +MONST_H = $(INCL)\align.h $(INCL)\monst.h +PERMONST_H = $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\align.h \ + $(INCL)\permonst.h +RM_H = $(INCL)\align.h $(INCL)\rm.h +SP_LEV_H = $(INCL)\align.h $(INCL)\sp_lev.h +VAULT_H = $(DUNGEON_H) $(INCL)\vault.h +YOUPROP_H = $(PERMONST_H) $(MONDATA_H) $(INCL)\prop.h \ + $(INCL)\pm.h $(INCL)\youprop.h +YOU_H = $(MONST_H) $(YOUPROP_H) $(INCL)\align.h \ + $(INCL)\attrib.h $(INCL)\you.h +DISPLAY_H = $(MONDATA_H) $(INCL)\vision.h $(INCL)\display.h +PCCONF_H = $(INCL)\micro.h $(INCL)\system.h $(INCL)\pcconf.h \ + $(MSYS)\pcvideo.h +CONFIG_H = $(GLOBAL_H) $(INCL)\tradstdc.h $(INCL)\config1.h \ + $(INCL)\config.h +DECL_H = $(YOU_H) $(INCL)\spell.h $(INCL)\color.h \ + $(INCL)\obj.h $(INCL)\onames.h $(INCL)\pm.h \ + $(INCL)\decl.h +GLOBAL_H = $(PCCONF_H) $(INCL)\coord.h $(INCL)\global.h +HACK_H = $(CONFIG_H) $(DUNGEON_H) $(DECL_H) \ + $(DISPLAY_H) $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\trap.h $(INCL)\flag.h \ + $(RM_H) $(INCL)\vision.h $(INCL)\wintype.h \ + $(INCL)\engrave.h $(INCL)\rect.h \ + $(INCL)\trampoli.h $(INCL)\hack.h +DLB_H = $(INCL)\dlb.h +TILE_H = $(WSHR)\tile.h $(MSYS)\pctiles.h + + +# Make Roolz dude. +# Due to the inadequacy of some makes these must accord with a +# topological sort of the generated-from relation... output on +# the left, input on the right. Trust me. +# + +.SUFFIXES: .exe .o .til .uu .c .y .l + +# +# Rules for files in src +# + + +.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(SRC)}.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +# +# Rules for files in sys\share +# + +{$(SYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(SYS)}.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +# +# Rules for files in sys\msdos +# + +{$(MSYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(MSYS)}.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(MSYS)}.h{$(INCL)}.h: + @copy $< $@ + +# +# Rules for files in util +# + +{$(UTIL)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(UTIL)}.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +# +# Rules for files in win\share +# + +{$(WSHR)}.c.o: + @@$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(WSHR)}.c{$(OBJ)}.o: + @@$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +{$(WSHR)}.txt{$(DAT)}.txt: + @copy $< $@ + +# +# Rules for files in win\tty +# + +{$(WTTY)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + +{$(WTTY)}.c.o: + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $< + + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +######################################################## +# +# TARGETS + + +# +# The default make target (so just typing 'nmake' is useful). +# +default : envchk $(GAMEFILE) + +# The default target. + +$(GAME): $(O)utility.tag $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all: install + +install: envchk $(GAME) $(O)install.tag + @echo Done. + +$(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy $(SRC)\nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + copy $(SSHR)\termcap $(GAMEDIR) + copy *.tib $(GAMEDIR) + copy $(SSHR)\NetHack.cnf $(GAMEDIR)\defaults.nh + copy $(MSYS)\NHAccess.nh $(GAMEDIR) + copy $(U)recover.exe $(GAMEDIR) + if exist $(DOC)\guideb*.txt copy $(DOC)\guideb*.txt $(GAMEDIR) + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR) + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR) + echo install done > $@ + +$(O)sp_lev.tag: $(O)utility.tag $(SPLEVDES) + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) +# -@if exist $(O)sp_lev.tag del $(O)sp_lev.tag + @echo sp_levs done >$(O)sp_lev.tag + +$(O)utility.tag: $(INCL)\date.h $(INCL)\trap.h \ + $(INCL)\onames.h $(INCL)\pm.h monstr.c vis_tab.c \ + $(U)lev_comp.exe $(U)dgn_comp.exe $(U)recover.exe $(TILEUTIL) + -@if exist $(O)utility.tag del $(O)utility.tag + @echo utilities made > $@ + +tileutil: gif2txt.exe txt2ppm.exe + @echo Optional tile development utilities are up to date. + +.PHONEY: envchk + +envchk: +! IF ("$(MACHINE_CODE)"!="") + @SET MC=/G$(MACHINE_CODE) +! ELSE + @SET MC= +! ENDIF +! IF ("$(CL)"=="") + @echo CL Environment variable is defined as follows: + SET CL=/AL $(MC) /Oo /Gy /Gs /Gt14 /Zp1 /W0 /I$(INCL) /I$(MSYS) /I$(WSHR) /nologo /c +! ELSE + @echo Warning CL Environment variable is defined: + @echo CL=$(CL) + @echo Overriding that definition as follows: + SET CL=/AL $(MC) /Oo /Gy /Gs /Gt14 /Zp1 /W0 /I$(INCL) /I$(MSYS) /I$(WSHR) /nologo /c +! ENDIF + +# The main target. + +$(GAMEFILE) : $(LNKOPT) $(ALLOBJ) + @echo Linking.... + $(LINK) $(LFLAGS) /SE:1000 /DYNAMIC:2135 /NOE /ST:6000 @<<$(GAME).lnk + $(ALLOBJ:^ =+^ + ) + $(GAMEFILE) + $(GAME) + $(TERMLIB) $(MOVETR) $(CLIB) $(BCOVL) $(BCMDL) + $(LNKOPT); +<< + @if exist $(O)install.tag del $(O)install.tag + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +# +# Housekeeping. +# + +clean: + del *.o + del *.map + del $(U)dlb_main.exe + +spotless: clean + if exist $(O)utility.tag del $(O)utility.tag + if exist $(O)install.tag del $(O)install.tag + if exist $(GAME).lnk del $(GAME).lnk + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(SRC)\lev_lex.c del $(SRC)\lev_lex.c + if exist $(SRC)\lev_yacc.c del $(SRC)\lev_yacc.c + if exist $(SRC)\dgn_lex.c del $(SRC)\dgn_lex.c + if exist $(SRC)\dgn_yacc.c del $(SRC)\dgn_yacc.c + if exist $(U)recover.exe del $(U)recover.exe + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist $(INCL)\pcvideo.h del $(INCL)\pcvideo.h + if exist $(MSYS)\pctiles.h del $(MSYS)\pctiles.h + if exist $(INCL)\portio.h del $(MSYS)\portio.h + if exist $(WSHR)\tile.h del $(WSHR)\tile.h + if exist monstr.c del monstr.c + if exist vis_tab.c del vis_tab.c + if exist $(SRC)\panic.c del $(SRC)\panic.c + if exist $(SRC)\makedefs.c del $(SRC)\makedefs.c + if exist $(SRC)\recover.c del $(SRC)\recover.c + if exist $(SRC)\lev_main.c del $(SRC)\lev_main.c + if exist $(SRC)\dlb_main.c del $(SRC)\dlb_main.c + if exist $(SRC)\dgn_main.c del $(SRC)\dgn_main.c + if exist $(SRC)\wintty.c del $(SRC)\wintty.c + if exist $(SRC)\topl.c del $(SRC)\topl.c + if exist $(SRC)\getline.c del $(SRC)\getline.c + if exist $(SRC)\termcap.c del $(SRC)\termcap.c + if exist $(SRC)\tile2bin.c del $(SRC)\tile2bin.c + if exist $(SRC)\msdos.c del $(SRC)\msdos.c + if exist $(SRC)\pckeys.c del $(SRC)\pckeys.c + if exist $(SRC)\video.c del $(SRC)\video.c + if exist $(SRC)\sound.c del $(SRC)\sound.c + if exist $(SRC)\tilemap.c del $(SRC)\tilemap.c + if exist $(SRC)\gifread.c del $(SRC)\gifread.c + if exist $(SRC)\ppmwrite.c del $(SRC)\ppmwrite.c + if exist $(SRC)\pcmain.c del $(SRC)\pcmain.c + if exist $(SRC)\pcunix.c del $(SRC)\pcunix.c + if exist $(SRC)\pcsys.c del $(SRC)\pcsys.c + if exist $(SRC)\pctty.c del $(SRC)\pctty.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(INCL)\date.h del $(INCL)\date.h + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist vis_tab.c del vis_tab.c + if exist *.lnk del *.lnk + if exist *.def del *.def + if exist *.map del *.map + if exist a.out del a.out + if exist tilemap.exe del tilemap.exe + if exist tile2bin.exe del tile2bin.exe + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\*.lev del $(DAT)\*.lev + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(SRC)\nhdat del $(SRC)\nhdat + if exist $(DAT)\dlb.lst del $(DAT)\dlb.lst + if exist $(DAT)\msdoshlp.txt del $(DAT)\msdoshlp.txt + if exist $(DAT)\dlb_main.exe del $(DAT)\dlb_main.exe + if exist $(DAT)\lev_comp.exe del $(DAT)\lev_comp.exe + if exist $(DAT)\dgn_comp.exe del $(DAT)\dgn_comp.exe + if exist $(O)sp_lev.tag del $(O)sp_lev.tag + if exist $(PLANAR_TIB) del $(PLANAR_TIB) + if exist $(OVERVIEW_TIB) del $(OVERVIEW_TIB) + + +# +# Secondary Targets. +# +# +# Makedefs Stuff +# + +$(U)makedefs.exe: $(MAKEOBJS) + @echo Linking.... + @$(LINK) $(LFLAGS) @<<$(@B).lnk + $(MAKEOBJS:^ =+^ + ) + $@ + $(@B) + ; +<< + +makedefs.o: $(CONFIG_H) $(PERMONST_H) $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(UTIL)\makedefs.c + +# The following include files depend on makedefs to be created. +# +# date.h should be remade every time any of the source or include +# files is modified. + + +$(INCL)\date.h : $(U)makedefs.exe + -$(U)makedefs -v + +$(INCL)\onames.h: $(U)makedefs.exe + -$(U)makedefs -o + +$(INCL)\pm.h: $(U)makedefs.exe + -$(U)makedefs -p + +monstr.c: $(U)makedefs.exe + -$(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + -$(U)makedefs -z + +vis_tab.c: $(U)makedefs.exe + -$(U)makedefs -z + +# +# Level Compiler Stuff +# + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + @$(LINK) $(LFLAGS) @<<$(@B).lnk + $(SPLEVOBJS:^ =+^ + ) + $@ + $(@B) + $(BCMDL); +<< + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + $(YACC) -d -l $(U)lev_comp.y + copy $(YTABC) $(U)lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) +! ELSE + @echo. + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo. + @echo For now, we will copy the prebuilt lev_yacc.c + @echo from $(SSHR) to $(U)lev_yacc.c, and copy the prebuilt + @echo lev_comp.h from $(SYS) to $(UTIL)\lev_comp.h + @echo and use those. + @echo. + copy $(SSHR)\lev_yacc.c $@ >nul + touch $@ + copy $(SSHR)\lev_comp.h $(INCL)\lev_comp.h >nul + touch $(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + $(LEX) $(FLEXSKEL) $(U)lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) +! ELSE + @echo. + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo. + @echo For now, we will copy a prebuilt lev_lex.c + @echo from $(SSHR) to $@ and use it. + @echo. + copy $(SSHR)\lev_lex.c $@ >nul + touch $@ +! ENDIF + +# +# Dungeon Stuff +# + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + @$(LINK) $(LFLAGS) @<<$(@B).lnk + $(DGNCOMPOBJS:^ =+^ + ) + $@ + $(@B) + $(BCMDL); +<< + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + $(YACC) -d -l $(U)dgn_comp.y + copy $(YTABC) $(U)dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) +! ELSE + @echo. + @echo $(U)dgn_comp.y has changed. To update $@ and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo. + @echo For now, we will copy the prebuilt dgn_yacc.c from + @echo $(SSHR) to $(U)dgn_yacc.c, and copy the prebuilt + @echo dgn_comp.h from $(SSHR) to $(INCL)\dgn_comp.h + @echo and use those. + @echo. + copy $(SSHR)\dgn_yacc.c $@ >nul + touch $@ + copy $(SSHR)\dgn_comp.h $(INCL)\dgn_comp.h >nul + touch $(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + $(LEX) $(FLEXSKEL) $(U)dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) +! ELSE + @echo. + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo. + @echo For now, we will copy a prebuilt dgn_lex.c + @echo from $(SSHR) to $@ and use it. + @echo. + copy $(SSHR)\dgn_lex.c $@ >nul + touch $@ +! ENDIF + +# +# Recover Utility +# + +$(U)recover.exe: $(RECOVOBJS) + @echo Linking.... + @$(LINK) $(LFLAGS) @<<$(@B).lnk + $(RECOVOBJS:^ =+^ + ) + $@ + $(@B) + ; +<< + +recover.o: $(CONFIG_H) $(UTIL)\recover.c + +# +# Header file moves required for tile support +# + +#$(WSHR)\tile.h: $(WSHR)\tile.h +# copy $(WSHR)\tile.h $@ + +#$(MSYS)\pctiles.h: $(MSYS)\pctiles.h +# copy $(MSYS)\pctiles.h $@ + +#$(INCL)\pcvideo.h: $(MSYS)\pcvideo.h +# copy $(MSYS)\pcvideo.h $@ + +#$(MSYS)\portio.h: $(MSYS)\portio.h +# copy $(MSYS)\portio.h $@ + +# +# Tile Mapping +# + +tile.c: tilemap.exe + @tilemap + @echo A new $@ has been created + +tilemap.exe: tilemap.o + @echo Linking.... + @$(LINK) $(LFLAGS) @<<$(@B).lnk + tilemap.o + $@ + $(@B) + ; +<< + +tilemap.c: $(WSHR)\tilemap.c + copy $(WSHR)\tilemap.c . + +tilemap.o: tilemap.c $(HACK_H) $(TILE_H) + +# +# Tile Utilities +# +# +# Required for tile support +# + +NetHack1.tib: $(TILEFILES) tile2bin.exe + @echo Creating binary tile files (this may take some time) + @tile2bin + +NetHacko.tib: thintile.tag $(TILEFILES2) til2bin2.exe + @echo Creating overview binary tile files (this may take some time) + @til2bin2 + +tile2bin.exe: tile2bin.o $(TEXTIO) + @$(LINK) $(LFLAGS) tile2bin.o $(TEXTIO),$@,$(@B); + +til2bin2.exe: til2bin2.o $(TEXTIO2) + @$(LINK) $(LFLAGS) til2bin2.o $(TEXTIO2),$@,$(@B); + +thintile.exe: thintile.o + @$(LINK) $(LFLAGS) thintile.o,$@,$(@B); + +thintile.o: $(HACK_H) $(WSHR)\tile.h $(WSHR)\thintile.c + +thintile.tag: thintile.exe $(TILEFILES) + thintile + @echo thintiles created >thintile.tag + +tile2bin.o: $(HACK_H) $(WSHR)\tile.h $(MSYS)\pctiles.h $(MSYS)\pcvideo.h \ + $(MSYS)\tile2bin.c + +til2bin2.o: $(HACK_H) $(WSHR)\tile.h $(MSYS)\pctiles.h $(MSYS)\pcvideo.h \ + $(MSYS)\tile2bin.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 -DOVERVIEW_FILE /Zg $(MSYS)\tile2bin.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 -DOVERVIEW_FILE /Fo$@ $(MSYS)\tile2bin.c + +tiletext.o: $(CONFIG_H) $(WSHR)\tile.h $(WSHR)\tiletext.c + +tiletex2.o: $(CONFIG_H) $(WSHR)\tile.h $(WSHR)\tiletext.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 /Zg $(WSHR)\tiletext.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 /Fo$@ $(WSHR)\tiletext.c + +tiletxt.o: $(CONFIG_H) $(WSHR)\tile.h tilemap.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILETEXT /Zg tilemap.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILETEXT /Fo$@ tilemap.c + +tiletxt2.o: $(CONFIG_H) $(WSHR)\tile.h tilemap.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILETEXT /Zg tilemap.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILETEXT -DTILE_X=8 /Fo$@ tilemap.c +# +# Optional GIF Utilities (for development) +# + +gif2txt.exe: $(GIFREADERS) $(TEXTIO) + @$(LINK) $(LFLAGS) $(GIFREADERS) $(TEXTIO),$@,$(@B); + +gif2txt2.exe: $(GIFREAD2) $(TEXTIO2) + @$(LINK) $(LFLAGS) $(GIFREAD2) $(TEXTIO2),$@,$(@B); + +txt2ppm.exe: $(PPMWRITERS) $(TEXTIO) + @$(LINK) $(LFLAGS) $(PPMWRITERS) $(TEXTIO),$@,$(@B); + +txt2ppm2.exe: $(PPMWRIT2) $(TEXTIO2) + @$(LINK) $(LFLAGS) $(PPMWRIT2) $(TEXTIO2),$@,$(@B); + +gifread.o: $(CONFIG_H) $(WSHR)\tile.h + +gifread2.o: $(CONFIG_H) $(WSHR)\tile.h $(WSHR)\gifread.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 /Zg gifread.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 $(WSHR)\gifread.c + +ppmwrite.o: $(CONFIG_H) $(WSHR)\tile.h $(WSHR)\ppmwrite.c + +ppmwrit2.o: $(CONFIG_H) $(WSHR)\tile.h $(WSHR)\ppmwrite.c + -@$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 /Zg ppmwrite.c >$(@B).pro + @$(CC) $(CFLAGS) $(SPECOPTS) -DTILE_X=8 $(WSHR)\ppmwrite.c + +# +# Optional tile viewer (development sources only) +# + +viewtib.exe: viewtib.o + @$(LINK) $(LFLAGS) -oviewtib.exe viewtib.o $(LIBRARIES) + +viewtib.o: $(MSYS)\viewtib.c + +# +# Other Util Dependencies. +# + +alloc.o: $(CONFIG_H) alloc.c +drawing.o: $(CONFIG_H) drawing.c $(MSYS)\pcvideo.h +decl.o: $(CONFIG_H) decl.c +monst.o: $(CONFIG_H) $(PERMONST_H) $(ESHK_H) \ + $(EPRI_H) $(VAULT_H) $(INCL)\monsym.h \ + $(INCL)\color.h monst.c + +objects.o: $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\color.h objects.c + +panic.o: $(CONFIG_H) $(UTIL)\panic.c + + + +# +# make data.base an 8.3 filename to prevent an nmake warning +# + +DATABASE = $(DAT)\data.bas + +$(DAT)\data: $(O)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(O)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(O)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(O)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# DLB stuff +# +# +nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(DAT)\options \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon \ + $(DAT)\license $(O)sp_lev.tag + @copy $(MSYS)\msdoshlp.txt $(DAT) + @cd $(DAT) + @echo data >dlb.lst + @echo oracles >>dlb.lst + @echo options >>dlb.lst + @echo quest.dat >>dlb.lst + @echo rumors >>dlb.lst + @echo help >>dlb.lst + @echo hh >>dlb.lst + @echo cmdhelp >>dlb.lst + @echo history >>dlb.lst + @echo opthelp >>dlb.lst + @echo wizhelp >>dlb.lst + @echo dungeon >>dlb.lst + @echo license >>dlb.lst + @echo msdoshlp.txt >>dlb.lst + @for %%N in (*.lev) do echo %%N >>dlb.lst + $(U)dlb_main cvIf dlb.lst $(SRC)\nhdat + @cd $(SRC) + +$(U)dlb_main.exe: $(DLBOBJS) + link $(LFLAGS) /ST:5120 $(DLBOBJS),$@,$(@B); + +dlb_main.o: $(U)dlb_main.c $(INCL)\config.h $(DLB_H) + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(U)dlb_main.c + +# Game Dependencies + +# sys/share +main.o: $(SSHR)\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + #$(INCL)\win32api.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(SSHR)\pcmain.c +sys.o: $(SSHR)\pcsys.c $(HACK_H) + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(SSHR)\pcsys.c +tty.o: $(SSHR)\pctty.c $(HACK_H) + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(SSHR)\pctty.c +unix.o: $(SSHR)\pcunix.c $(HACK_H) + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(SSHR)\pcunix.c +random.o: $(SSHR)\random.c $(HACK_H) + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(SSHR)\random.c + +# sys/msdos +msdos.o : $(HACK_H) $(MSYS)\msdos.c + @$(CC) $(CFLAGS) $(SPECOPTS) $(NEWALT) /Fo$@ $(MSYS)\msdos.c +pckeys.o : $(HACK_H) $(MSYS)\pckeys.c + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\pckeys.c +pctiles.o : $(HACK_H) $(MSYS)\pctiles.c $(MSYS)\portio.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\pctiles.c +sound.o : $(HACK_H) $(MSYS)\sound.c $(MSYS)\portio.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\sound.c +video.o : $(HACK_H) $(MSYS)\pcvideo.h $(MSYS)\portio.h $(MSYS)\video.c + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\video.c +vidvga.o : $(HACK_H) $(MSYS)\pcvideo.h $(MSYS)\portio.h $(TILE_H) \ + $(MSYS)\vidvga.c + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\vidvga.c +vidtxt.o : $(HACK_H) $(MSYS)\pcvideo.h $(MSYS)\portio.h $(TILE_H) \ + $(MSYS)\vidtxt.c + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(MSYS)\vidtxt.c +stubvid.o : $(HACK_H) $(MSYS)\video.c + @$(CC) $(CFLAGS) $(SPECOPTS) -DSTUBVIDEO /Fo$@ $(MSYS)\video.c + +# win/tty +getline.o: $(WIN)\getline.c $(HACK_H) $(INCL)\func_tab.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(WIN)\getline.c +termcap.o: $(WIN)\termcap.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(WIN)\termcap.c +topl.o: $(WIN)\topl.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(WIN)\topl.c +wintty.o: $(WIN)\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlev.h $(INCL)\tcap.h + @$(CC) $(CFLAGS) $(SPECOPTS) /Fo$@ $(WIN)\wintty.c + +# src dependencies +allmain.o: allmain.c $(HACK_H) +alloc.o: alloc.c $(CONFIG_H) +apply.o: apply.c $(HACK_H) $(INCL)\edog.h +artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +attrib.o: attrib.c $(HACK_H) $(INCL)\artifact.h +ball.o: ball.c $(HACK_H) +bones.o: bones.c $(HACK_H) $(INCL)\lev.h +botl.o: botl.c $(HACK_H) +cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +dbridge.o: dbridge.c $(HACK_H) +decl.o: decl.c $(HACK_H) +detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +dig.o: dig.c $(HACK_H) $(INCL)\edog.h +display.o: display.c $(HACK_H) +dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +do.o: do.c $(HACK_H) $(INCL)\lev.h +do_name.o: do_name.c $(HACK_H) +do_wear.o: do_wear.c $(HACK_H) +dog.o: dog.c $(HACK_H) $(INCL)\edog.h +dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +dokick.o: dokick.c $(HACK_H) $(INCL)\eshk.h +dothrow.o: dothrow.c $(HACK_H) +drawing.o: drawing.c $(HACK_H) $(INCL)\tcap.h +dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +eat.o: eat.c $(HACK_H) +end.o: end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +engrave.o: engrave.c $(HACK_H) $(INCL)\lev.h +exper.o: exper.c $(HACK_H) +explode.o: explode.c $(HACK_H) +extralev.o: extralev.c $(HACK_H) +files.o: files.c $(HACK_H) $(INCL)\dlb.h +fountain.o: fountain.c $(HACK_H) +hack.o: hack.c $(HACK_H) +hacklib.o: hacklib.c $(HACK_H) +invent.o: invent.c $(HACK_H) $(INCL)\artifact.h +light.o: light.c $(HACK_H) $(INCL)\lev.h +lock.o: lock.c $(HACK_H) +mail.o: mail.c $(HACK_H) $(INCL)\mail.h +makemon.o: makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +mapglyph.o: mapglyph.c $(HACK_H) +mcastu.o: mcastu.c $(HACK_H) +mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +minion.o: minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +mklev.o: mklev.c $(HACK_H) +mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h +mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +mkobj.o: mkobj.c $(HACK_H) $(INCL)\artifact.h +mkroom.o: mkroom.c $(HACK_H) +mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +mondata.o: mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +monmove.o: monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h +monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +mplayer.o: mplayer.c $(HACK_H) +mthrowu.o: mthrowu.c $(HACK_H) +muse.o: muse.c $(HACK_H) $(INCL)\edog.h +music.o: music.c $(HACK_H) #interp.c +o_init.o: o_init.c $(HACK_H) $(INCL)\lev.h +objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +objnam.o: objnam.c $(HACK_H) +options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +pager.o: pager.c $(HACK_H) $(INCL)\dlb.h + @$(CC) $(CFLAGS) $(SPECOPTS) /f- /Od /Fo$@ pager.c +pickup.o: pickup.c $(HACK_H) +pline.o: pline.c $(HACK_H) $(INCL)\epri.h +polyself.o: polyself.c $(HACK_H) +potion.o: potion.c $(HACK_H) +pray.o: pray.c $(HACK_H) $(INCL)\epri.h +priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +quest.o: quest.c $(HACK_H) $(INCL)\qtext.h +questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +read.o: read.c $(HACK_H) +rect.o: rect.c $(HACK_H) +region.o: region.c $(HACK_H) +restore.o: restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +rip.o: rip.c $(HACK_H) +rnd.o: rnd.c $(HACK_H) +role.o: role.c $(HACK_H) +rumors.o: rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +save.o: save.c $(HACK_H) $(INCL)\lev.h +shk.o: shk.c $(HACK_H) $(INCL)\eshk.h +shknam.o: shknam.c $(HACK_H) $(INCL)\eshk.h +sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +sounds.o: sounds.c $(HACK_H) $(INCL)\edog.h +sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +spell.o: spell.c $(HACK_H) +steal.o: steal.c $(HACK_H) +steed.o: steed.c $(HACK_H) +teleport.o: teleport.c $(HACK_H) +timeout.o: timeout.c $(HACK_H) $(INCL)\lev.h +topten.o: topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlev.h +track.o: track.c $(HACK_H) +trap.o: trap.c $(HACK_H) +u_init.o: u_init.c $(HACK_H) +uhitm.o: uhitm.c $(HACK_H) +vault.o: vault.c $(HACK_H) $(INCL)\vault.h +version.o: version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlev.h +vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h +weapon.o: weapon.c $(HACK_H) +were.o: were.c $(HACK_H) +wield.o: wield.c $(HACK_H) +windows.o: windows.c $(HACK_H) $(INCL)\wingem.h +wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h +worm.o: worm.c $(HACK_H) $(INCL)\lev.h +worn.o: worn.c $(HACK_H) +write.o: write.c $(HACK_H) +zap.o: zap.c $(HACK_H) + +# end of file diff --git a/sys/msdos/NHAccess.nh b/sys/msdos/NHAccess.nh new file mode 100644 index 0000000..2fb6731 --- /dev/null +++ b/sys/msdos/NHAccess.nh @@ -0,0 +1,134 @@ +# SCSS Id: @(#)NHAccess.nh 3.4 1999/11/28 +# Copyright (c) NetHack PC Development Team 1993, 1996, 1999 +# NetHack may be freely redistributed. See license for details. +# +# Modified defaults.nh for blind access. Copy to working directory as +# defaults.nh. +# +# A '#' at the beginning of a line means the rest of the line is a comment. +# +# This configuration file is set up for two cases, for a hard disk +# (as drive C:), and for two floppy disks. +# +# Some options MUST be set in this file, other options can be toggled while +# playing. For a list of options available see the file. If +# the game plays slowly you might notice some improvement by setting +# !time and !showexp, which will reduce screen I/O somewhat. +# +# To change the configuration, comment out the unwanted lines, and +# uncomment the configuration you want. + + +# *** OPTIONS *** +# +# The three options on this line should be used for most setups. +# If your machine isn't very IBM-compatible, and NetHack doesn't work, +# try commenting out this line. +# +# Note to blind players: +# +# Turn off IBMgraphics, using the exclamation-mark, as done below. +# +OPTIONS=rawio,BIOS,!IBMgraphics +# Some versions of NetHack use the pc speaker to play the notes given when +# playing music instruments in NetHack. To use this feature, if available, +# uncomment the following line: +#OPTIONS=soundcard:autodetect +# If your machine is NEC PC-9800, use: +#OPTIONS=rawio,BIOS,video:default +# +# +# General options. You might also set "silent" so as not to attract +# the boss's attention. +# +# Note for blind players: +# +# A lot of speech access programs use the number-pad to review the screen. +# If this is the case, exclamation-mark out the number_pad option (as done +# below) and use the traditional Rogue-like commands. +# +OPTIONS=notime,noshowexp,!number_pad,lit_corridor,!rest_on_space +# +# +# Some options to set personal preferences. Uncomment and change these to +# suit your personal preference. If several people are to use the same +# configuration, options like these should not be set. +# +# Note to blind players: +# +# Use menustyle:traditional for the best interface to speech synthesizers. +# +#OPTIONS=name:Janet-V,female,dogname:Fido,catname:Morris,fruit:apricot +#OPTIONS=autopickup,pickup_types:$"=/!?+ +#OPTIONS=packorder:")[%?+/=!(*0_` +#OPTIONS=scores:10 top/2 around/own +#OPTIONS=nolegacy,noverbose +OPTIONS=nolegacy,menustyle:traditional + + +# *** HARD DISK CONFIGURATION *** +# +#HACKDIR=c:\games\nethack +# +# Note: Under MSDOS ports HACKDIR defaults to the location +# of the NetHack.exe file. Setting HACKDIR above will override that. +# +# LEVELS and SAVE default to HACKDIR +# +#LEVELS=c:\games\nethack\bones +#SAVE=c:\games\nethack\bones;n +# +# appending a ";n" to SAVE means don't prompt to insert a disk. +SAVE=;n +# +# Note that RAMDISK must *not* be the same (or even implicitly +# get expanded to the same path by the OS) as HACKDIR. +# +#RAMDISK=d: + + +# *** 2-FLOPPY CONFIGURATION *** +# +# HACKDIR=a:\ +# LEVELS=b:\ +# SAVE=b:\ +# RAMDISK=c: + + +# *** CHARACTER GRAPHICS *** +# +# See the on-line help or the Guidebook for which symbols are in which +# positions. +# +# Note to blind players: +# +# You very probably do not want to use these character graphics. +# +# If you merely set the IBMgraphics option, NetHack will use IBM +# extended ASCII for dungeon characters. If you don't like the selections, +# you can make up your own via these graphics options, but you should still +# set IBMgraphics if you are using IBM graphics characters to get the correct +# processing. +# +#DUNGEON= 032 124 045 124 124 124 124 045 045 045 \ +# 124 124 046 045 124 043 043 035 035 046 \ +# 035 035 060 062 060 062 095 124 092 035 \ +# 126 126 126 126 042 042 035 035 032 035 \ +# 126 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 +# +#EFFECTS= 124 095 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 + +# ================================================= +# *** VIDEOCOLORS AND VIDEOSHADES *** +# +# While playing on NEC PC-9800, default game display may be difficult to +# read. Try following setting. +# +#OPTIONS=videocolors:4-2-6-1-5-3-4-2-6-1-5-3,videoshades:normal-normal-normal diff --git a/sys/msdos/moveinit.pat b/sys/msdos/moveinit.pat new file mode 100644 index 0000000..fc8ab0c --- /dev/null +++ b/sys/msdos/moveinit.pat @@ -0,0 +1,37 @@ + These are patches for MOVEINIT.C, supplied + with the MSVC compiler in the compiler's SOURCE\MOVE + subdirectory. (Copy that and the MOVEAPI.H + file into your NetHack src directory and apply this patch) + + +*** ../linc/src/moveinit.c Tue Nov 23 08:01:00 1993 +--- src/moveinit.c Sun Mar 13 10:13:10 1994 +*************** +*** 13,18 **** +--- 13,19 ---- + *******************************************************************************/ + + #include "moveapi.h" ++ extern unsigned memavail(unsigned); + + #ifndef MOVE_ENV + +*************** +*** 125,132 **** + /* attempt to allocate the overlay heap. ignore return value (heap size). + * note that MOVE will abort if not enough memory to alloc minimum size. + */ +! +! _movesetheap ($$COVL, cparaLarge, cparaMax); + + /* get available cache ressource amount */ + +--- 126,133 ---- + /* attempt to allocate the overlay heap. ignore return value (heap size). + * note that MOVE will abort if not enough memory to alloc minimum size. + */ +! cparaMax = memavail(cparaMin); +! _movesetheap ($$COVL, cparaMin, cparaMax); + + /* get available cache ressource amount */ + diff --git a/sys/msdos/msdos.c b/sys/msdos/msdos.c new file mode 100644 index 0000000..29e19fb --- /dev/null +++ b/sys/msdos/msdos.c @@ -0,0 +1,520 @@ +/* SCCS Id: @(#)msdos.c 3.4 2000/07/30 */ +/* Copyright (c) NetHack PC Development Team 1990, 1991, 1992, 1993, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * MSDOS system functions. + * Many thanks to Don Kneller who originated the DOS port and + * contributed much to the cause. + */ + +#define NEED_VARARGS +#include "hack.h" + +#ifdef MSDOS +#include "pcvideo.h" + +#include +#include + +/* + * MS-DOS functions + */ +#define DIRECT_INPUT 0x07 /* Unfiltered Character Input Without Echo */ +#define FATINFO 0x1B /* Get Default Drive Data */ +/* MS-DOS 2.0+: */ +#define GETDTA 0x2F /* Get DTA Address */ +#define FREESPACE 0x36 /* Get Drive Allocation Info */ +#define GETSWITCHAR 0x3700 /* Get Switch Character */ +#define FINDFIRST 0x4E /* Find First File */ +#define FINDNEXT 0x4F /* Find Next File */ +#define SETFILETIME 0x5701 /* Set File Date & Time */ +/* + * BIOS interrupts + */ +#ifdef PC9800 +#define KEYBRD_BIOS 0x18 +#else +#define KEYBRD_BIOS 0x16 +#endif + +/* + * Keyboard BIOS functions + */ +#define READCHAR 0x00 /* Read Character from Keyboard */ +#define GETKEYFLAGS 0x02 /* Get Keyboard Flags */ +/*#define KEY_DEBUG */ /* print values of unexpected key codes - devel*/ + +void FDECL(get_cursor,(int *, int *)); + +#ifdef OVL0 + +/* direct bios calls are used only when iflags.BIOS is set */ + +static char NDECL(DOSgetch); +static char NDECL(BIOSgetch); +#ifndef __GO32__ +static char * NDECL(getdta); +#endif +static unsigned int FDECL(dos_ioctl, (int,int,unsigned)); +#ifdef USE_TILES +extern boolean FDECL(pckeys,(unsigned char, unsigned char)); /* pckeys.c */ +#endif + +int +tgetch() +{ + char ch; + + /* BIOSgetch can use the numeric key pad on IBM compatibles. */ +# ifdef SIMULATE_CURSOR + if (iflags.grmode && cursor_flag) DrawCursor(); +# endif + if (iflags.BIOS) + ch = BIOSgetch(); + else + ch = DOSgetch(); +# ifdef SIMULATE_CURSOR + if (iflags.grmode && cursor_flag) HideCursor(); +# endif + return ((ch == '\r') ? '\n' : ch); +} + + + +/* + * Keyboard translation tables. + */ +#ifdef PC9800 +#define KEYPADLO 0x38 +#define KEYPADHI 0x50 +#else +#define KEYPADLO 0x47 +#define KEYPADHI 0x53 +#endif + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * When iflags.BIOS is active, shifted keypad keys are translated to the + * shift values below. + */ +static const struct pad { + char normal, shift, cntrl; +} keypad[PADKEYS] = { +#ifdef PC9800 + {'>', '>', '>'}, /* Ins */ + {'<', '<', '<'}, /* Del */ + {'k', 'K', C('k')}, /* Up */ + {'h', 'H', C('h')}, /* Left */ + {'l', 'L', C('l')}, /* Right */ + {'j', 'J', C('j')}, /* Down */ + { 0 , 0 , 0 }, /* HomeClr */ + {'?', '?', '?' }, /* Help */ + {'m', C('p'), C('p')}, /* - */ + {'/', '/', '/'}, /* / */ + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'*', '*', '*'}, /* * */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'g', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'=', '=', '='}, /* = */ + {'i', 'I', C('i')}, /* 0 */ + {',', ':', ':'}, /* , */ + {'.', '.', '.'} /* . */ +#else + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'g', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +#endif +}, numpad[PADKEYS] = { +#ifdef PC9800 + {'>', '>', '>'}, /* Ins */ + {'<', '<', '<'}, /* Del */ + {'8', M('8'), '8'}, /* Up */ + {'4', M('4'), '4'}, /* Left */ + {'6', M('6'), '6'}, /* Right */ + {'2', M('2'), '2'}, /* Down */ + { 0 , 0 , 0 }, /* HomeClr */ + {'?', '?', '?'}, /* Help */ + {'m', C('p'), C('p')}, /* - */ + {'/', '/', '/'}, /* / */ + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'*', '*', '*'}, /* * */ + {'4', M('4'), '4'}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'=', '=', '='}, /* = */ + {'i', 'I', C('i')}, /* 0 */ + {',', ':', ':'}, /* , */ + {'.', '.', '.'} /* . */ +#else + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'5', M('5'), '5'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'0', M('0'), '0'}, /* Ins */ + {'.', ':', ':'} /* Del */ +#endif +}; + +/* + * Unlike Ctrl-letter, the Alt-letter keystrokes have no specific ASCII + * meaning unless assigned one by a keyboard conversion table, so the + * keyboard BIOS normally does not return a character code when Alt-letter + * is pressed. So, to interpret unassigned Alt-letters, we must use a + * scan code table to translate the scan code into a letter, then set the + * "meta" bit for it. -3. + */ +#ifdef PC9800 +#define SCANLO 0x5 +#else +#define SCANLO 0x10 +#endif /* PC9800 */ + +static const char scanmap[] = { /* ... */ +#ifdef PC9800 + 0, 0, 0, 0, 0, 0, '-','^','\\','\b', + '\t','q','w','e','r','t','y','u','i','o','p','@','[', '\n', + 'a','s','d','f','g','h','j','k','l',';',':', ']', + 'z','x','c','v','b','N','m',',','.','/' /* ... */ +#else + 'q','w','e','r','t','y','u','i','o','p','[',']', '\n', + 0, 'a','s','d','f','g','h','j','k','l',';','\'', '`', + 0, '\\', 'z','x','c','v','b','n','m',',','.','?' /* ... */ +#endif /* PC9800 */ +}; + +#define inmap(x) (SCANLO <= (x) && (x) < SCANLO + SIZE(scanmap)) + +#ifdef NEW_ALT +#define NUMERIC_SCANLO 0x78 +static const char numeric_scanmap[] = { /* ... */ + '1','2','3','4','5','6','7','8','9','0','-','=' +}; +# define in_numericmap(x) (NUMERIC_SCANLO <= (x) && \ + (x) < NUMERIC_SCANLO + SIZE(numeric_scanmap)) +# endif + +/* + * BIOSgetch gets keys directly with a BIOS call. + */ +#ifdef PC9800 +#define SHIFT 0x1 +#define KANA 0x4 +#define GRPH 0x8 +#define CTRL 0x10 +#else +#define SHIFT (0x1 | 0x2) +#define CTRL 0x4 +#define ALT 0x8 +#endif /* PC9800 */ + +static char +BIOSgetch() +{ + unsigned char scan, shift, ch=0; + const struct pad *kpad; + union REGS regs; + + do { + /* Get scan code. + */ + regs.h.ah = READCHAR; + int86(KEYBRD_BIOS, ®s, ®s); + ch = regs.h.al; + scan = regs.h.ah; + /* Get shift status. + */ + regs.h.ah = GETKEYFLAGS; + int86(KEYBRD_BIOS, ®s, ®s); + shift = regs.h.al; + + /* Translate keypad keys */ + if (iskeypad(scan)) { + kpad = iflags.num_pad ? numpad : keypad; + if (shift & SHIFT) + ch = kpad[scan - KEYPADLO].shift; + else if (shift & CTRL) + ch = kpad[scan - KEYPADLO].cntrl; + else + ch = kpad[scan - KEYPADLO].normal; + } +#ifdef USE_TILES + /* Check for special interface manipulation keys */ + if (pckeys(scan, shift)) { + ch = 0xFF; + continue; + } +#endif + /* Translate unassigned Alt-letters */ +#ifdef PC9800 + if (shift & KANA) + return 0; + if ((shift & GRPH) && (ch >= 0x80)) { +#else + if ((shift & ALT) && !ch) { +#endif +#if 0 + pline("Scan code: %d 0x%03X", scan, scan); +#endif + if (inmap(scan)) + ch = scanmap[scan - SCANLO]; +#ifdef NEW_ALT + else if (in_numericmap(scan)) + ch = numeric_scanmap[scan - NUMERIC_SCANLO]; +#endif + return (isprint(ch) ? M(ch) : ch); + } + } while (ch == 0xFF); + return ch; +} + +static char +DOSgetch() +{ + union REGS regs; + char ch; + struct pad (*kpad)[PADKEYS]; + + regs.h.ah = DIRECT_INPUT; + intdos(®s, ®s); + ch = regs.h.al; + +#ifdef PC9800 + if (ch < 0) /* KANA letters and GRPH-shifted letters(?) */ + ch = 0; /* munch it */ +#else + /* + * The extended codes for Alt-shifted letters, and unshifted keypad + * and function keys, correspond to the scan codes. So we can still + * translate the unshifted cursor keys and Alt-letters. -3. + */ + if (ch == 0) { /* an extended key */ + regs.h.ah = DIRECT_INPUT; + intdos(®s, ®s); /* get the extended key code */ + ch = regs.h.al; + + if (iskeypad(ch)) { /* unshifted keypad keys */ + kpad = (void *)(iflags.num_pad ? numpad : keypad); + ch = (*kpad)[ch - KEYPADLO].normal; + } else if (inmap(ch)) { /* Alt-letters */ + ch = scanmap[ch - SCANLO]; + if (isprint(ch)) ch = M(ch); + } else ch = 0; /* munch it */ + } +#endif + return (ch); +} + +char +switchar() +{ + union REGS regs; + + regs.x.ax = GETSWITCHAR; + intdos(®s, ®s); + return regs.h.dl; +} + +long +freediskspace(path) +char *path; +{ + union REGS regs; + + regs.h.ah = FREESPACE; + if (path[0] && path[1] == ':') + regs.h.dl = (toupper(path[0]) - 'A') + 1; + else + regs.h.dl = 0; + intdos(®s, ®s); + if (regs.x.ax == 0xFFFF) + return -1L; /* bad drive number */ + else + return ((long) regs.x.bx * regs.x.cx * regs.x.ax); +} + +#ifndef __GO32__ +/* + * Functions to get filenames using wildcards + */ +int +findfirst_file(path) +char *path; +{ + union REGS regs; + struct SREGS sregs; + + regs.h.ah = FINDFIRST; + regs.x.cx = 0; /* attribute: normal files */ + regs.x.dx = FP_OFF(path); + sregs.ds = FP_SEG(path); + intdosx(®s, ®s, &sregs); + return !regs.x.cflag; +} + +int +findnext_file() { + union REGS regs; + + regs.h.ah = FINDNEXT; + intdos(®s, ®s); + return !regs.x.cflag; +} + +char * +foundfile_buffer() +{ + return (getdta() + 30); +} + + +/* Get disk transfer area */ +static char * +getdta() +{ + union REGS regs; + struct SREGS sregs; + char *ret; + + regs.h.ah = GETDTA; + intdosx(®s, ®s, &sregs); +# ifdef MK_FP + ret = (char *)MK_FP(sregs.es, regs.x.bx); +# else + FP_OFF(ret) = regs.x.bx; + FP_SEG(ret) = sregs.es; +# endif + return ret; +} + +long +filesize_nh(file) +char *file; +{ + char *dta; + + if (findfirst_file(file)) { + dta = getdta(); + return (* (long *) (dta + 26)); + } else + return -1L; +} + +#endif /* __GO32__ */ + +/* + * Chdrive() changes the default drive. + */ +void +chdrive(str) +char *str; +{ +# define SELECTDISK 0x0E + char *ptr; + union REGS inregs; + char drive; + + if ((ptr = index(str, ':')) != (char *)0) { + drive = toupper(*(ptr - 1)); + inregs.h.ah = SELECTDISK; + inregs.h.dl = drive - 'A'; + intdos(&inregs, &inregs); + } + return; +} + + +/* Use the IOCTL DOS function call to change stdin and stdout to raw + * mode. For stdin, this prevents MSDOS from trapping ^P, thus + * freeing us of ^P toggling 'echo to printer'. + * Thanks to Mark Zbikowski (markz@microsoft.UUCP). + */ + +#define DEVICE 0x80 +#define RAW 0x20 +#define IOCTL 0x44 +#define STDIN fileno(stdin) +#define STDOUT fileno(stdout) +#define GETBITS 0 +#define SETBITS 1 + +static unsigned int old_stdin, old_stdout; + +void +disable_ctrlP() +{ + + if (!iflags.rawio) return; + + old_stdin = dos_ioctl(STDIN, GETBITS, 0); + old_stdout = dos_ioctl(STDOUT, GETBITS, 0); + if (old_stdin & DEVICE) + dos_ioctl(STDIN, SETBITS, old_stdin | RAW); + if (old_stdout & DEVICE) + dos_ioctl(STDOUT, SETBITS, old_stdout | RAW); + return; +} + +void +enable_ctrlP() +{ + if (!iflags.rawio) return; + if (old_stdin) + (void) dos_ioctl(STDIN, SETBITS, old_stdin); + if (old_stdout) + (void) dos_ioctl(STDOUT, SETBITS, old_stdout); + return; +} + +static unsigned int +dos_ioctl(handle, mode, setvalue) +int handle, mode; +unsigned setvalue; +{ + union REGS regs; + + regs.h.ah = IOCTL; + regs.h.al = mode; + regs.x.bx = handle; + regs.h.dl = setvalue; + regs.h.dh = 0; /* Zero out dh */ + intdos(®s, ®s); + return (regs.x.dx); +} + +# endif /* OVLB */ + +#endif /* MSDOS */ diff --git a/sys/msdos/msdoshlp.txt b/sys/msdos/msdoshlp.txt new file mode 100644 index 0000000..621f46e --- /dev/null +++ b/sys/msdos/msdoshlp.txt @@ -0,0 +1,188 @@ + MSDOS specific help file for NetHack 3.4.3 + (Last Revision: March 16, 2003) + +Copyright (c) NetHack PC Development Team 1993-2003. +NetHack may be freely distributed. See license for details. + +New players should be sure to read GuideBoo.txt which contains essential +information about playing NetHack. It can be found in the same +directory as your NetHack executable. + +The MSDOS port of NetHack supports some additional or enhanced commands +as well as some defaults.nh file options specific to configuration choices +used during the building of PC NetHack. Listed below are those commands +and defaults.nh file options. + +Recognized MSDOS specific defaults.nh entries are outlined below. + +Boolean Options: + + IBMgraphics Use IBM extended characters for the dungeon + Default: [FALSE] + BIOS Allow the use of IBM ROM BIOS calls + Default: [FALSE] + rawio Allow the use of raw I/O (may only be set + on startup) + Default: [FALSE] + preload_tiles Preload tiles into RAM at start of game. + Faster, but uses more memory. + Default: [TRUE] + +Color Options: + +OPTIONS=!color + + Players will need this if they have a real, true, (old) + monochrome adapter, and they are seeing underlined, + and flashing, and reverse-video characters on the + screen. Or they find that some things are missing from + the display. This means that the auto-detection + for monochromes has failed. The color support + stuff is active in video.c, but may be (will have + to be) overridden by adding an OPTIONS=nocolor to + defaults.nh. + +OPTIONS=VIDEO + (defaults.nh only) + + ie: OPTIONS=video:autodetect + + Possible values are: AUTODETECT, DEFAULT, VGA + + AUTODETECT Checks for a supported hi-res video + adaptor, and if it detects one, NetHack + will run in "TILE MODE." + + DEFAULT NetHack will run in TTY mode. This is + the same as not specifying OPTIONS=VIDEO + at all. + + VGA Forces use of VGA specific video routines. + Any forcing of specific video routines has + potential to cause machine lock-ups if + the specified video hardware is not present. + +OPTIONS=VIDEOSHADES + (defaults.nh only) + Players may wish to add this option because one of their + shades of gray is difficult to read on their video + display hardware. Allows a level of intensity to be + assigned to the 3 possible shades of gray in NetHack, + those being BLACK, GRAY, WHITE. To each of those + shades, the player may assign a DARK, NORMAL, or LIGHT value. + Here is the default if not specified: + + ie. OPTIONS=VIDEOSHADES:dark-normal-light + + Anytime the same intensity value (DARK NORMAL LIGHT) is used + for more than one shade of gray, it will not be possible + to visually distinguish those two shades from each + other. + + ie. OPTIONS=VIDEOSHADES:normal-normal-light + + This, while eliminating the dark shade normally used for + displaying black items, means that the player won't be + able to distinguish black items and creatures from gray + items and creatures visually. + + Note also that the controversial gray schema used in + pl 3.1.2 as the default, corresponded to: + + OPTIONS=VIDEOSHADES:normal-dark-normal + + This is NOT the default in pl 3.1.3 and above, so many people + will probably not even need to use this option, and will find + the default just fine. The maps are built using gray, + and in pl 3.1.3, that is mapped to normal by default. In + 3.1.2, it was mapped to dark (as above). + +OPTIONS=VIDEOCOLORS + (defaults.nh only) + This option is only provided because some people on + r.g.r.n mentioned how they liked to modify the color + values from the default ANSI.SYS behaviour, and were + "upset" to find out that they could no longer do so + under 3.1.2. The color map is as accurate as possible + on standard PC video hardware as it stands, and any + deviation from the default, will mean that people + are mapping blue to green for example. The option is + available to provide as much flexibility as possible, + but it is not encouraged to be used. + + One possible use might be for the dark blue in + fountains. On video hardware that has trouble + displaying blacks, there may also be problems displaying + the darker blue used in fountains. If that is the case + then the default map: + + OPTIONS=VIDEOCOLORS:4-2-6-1-5-3-12-10-14-9-13-11 + + could be changed to the following to map blue to br. + blue: + + OPTIONS=VIDEOCOLORS:4-2-6-9-5-3-12-10-14-9-13-11 + + The mapping order for the options: + + red, green, brown, blue, magenta, cyan + br.red, br.green, yellow, br.blue, + br.magenta, br.cyan + + The PC hardware uses the following values: + + red(4), green(2), brown(6), blue(1), magenta(5), + cyan(3), bright red(12), bright green(10), yellow(14), + bright blue(9), bright magenta(13), bright cyan(11), + normal white(7), bright white(15). + + +The following options are NOT currently recognized under the MSDOS +port of PC NetHack: + + LEVELS= Where to store/create per level + data files. + SAVE= Where to save games. + BONES= Where to store bones files. + + +MSDOS Additional/Enhanced Commands: + +If you have opted to use the "graphical" or "tiled" option, (usually set via +OPTIONS=VIDEO:AUTODETECT ((see above)), then the following function keys are +active: + +F3 cycle through the current position indicator, or halo. Usually this + halo highlights the player's tile, unless the game is asking you for + an answer to a question that requires positional information, (ie, + the discover command). +F4 toggle level overview mode on/off +F5 toggle tiled display on/off. (Switches between tiled and traditional + ASCII display.) + +While playing NetHack under MSDOS you can press the ALT key in combination +with another key to execute an extended command as an alternative method +to pressing a # key sequence: + +Alt-2 twoweapon - toggle two-weapon combat +Alt-a adjust - adjust inventory letters. +Alt-c chat - talk to someone or something. +Alt-d dip - dip an object into something. +Alt-e enhance - enhance your skill with a weapon. +Alt-f force - force a lock. +Alt-i invoke - invoke an object's powers. +Alt-j jump - jump to a location. +Alt-l loot - loot a box on the floor. +Alt-m monster - use a monster's special ability. +Alt-n name - name an item or type of object. +Alt-o offer - offer a sacrifice to the gods. +Alt-p pray - pray to the gods for help. +Alt-q quit - quit the game. (Same as #quit) +Alt-r rub - rub a lamp. +Alt-s sit - sit down. +Alt-t turn - turn undead. +Alt-u untrap - untrap something. +Alt-v version - list compile time options for this version of NetHack. +Alt-w wipe - wipe off your face. + +If you are playing on NEC PC-9800, use the GRPH key instead of the ALT key. diff --git a/sys/msdos/nhico.uu b/sys/msdos/nhico.uu new file mode 100644 index 0000000..c2cfbef --- /dev/null +++ b/sys/msdos/nhico.uu @@ -0,0 +1,25 @@ +section 1 of uuencode 4.13 of file NETHACK.ICO by R.E.M. + +begin 644 NETHACK.ICO +M```!``$`("`0``````#H`@``%@```"@````@````0`````$`!```````@`(`. +M``````````````````````````````"```"`````@(``@````(``@`"`@```6 +M@("``,#`P````/\``/\```#__P#_````_P#_`/__``#___\`]F9F9F9F9F9F* +M9F9F9F9F9O]F9F9F9F9F9F9F9F9F9F;_B(B(B(B(B(B(B(B(B&9F_XB(B(B(D +MB(B(B(B(B(AF9O^(B(B(A555B(B(B(B(9F;_B(B(B`!5!8B(B(B(B&9F_XB(' +MB(@```6(B(B(B(AF9O^(B%554`!56(B(B(B(9F;_B(N[N[`.XU6(B(B(B&9F[ +M_XB[N[L`ONXU6(B(B(AF9O^+N[N[N[ONXUB(B(B(9F;_B[N[N[N[ONY3B(B(N +MB&9F_XN[NYF9F[ONXSB(B(AF9O^+N[F9F9F[ONXSB(B(9F;_B[N9F9F9F[ON? +MXSB(B&9F_XNYF9F9F9F[7NXSB(AF9O^+N9F9F9F9NUCNXSB(9F;_B[F9F9F9J +MF;M8CNXSB&9F_XNYF9F9F9F[6(CNXXAF9O^+N9F9F9F9NUB(CNZ(9F;_B[N9] +MF9F9F[M8B(CNB&9F_XN[N9F9F;N[6(B(B(AF9O^+N[N9F9N[NUB(B(B(9F;_R +MB[N[N[N[N[M8B(B(B&9F_XN[N[N[N[N[B(B(B(AF9O^+NXB(B(B+NXB(B(B(F +M9F;_B[B(B(B(B+N(B(B(B&9F_XB(B(B(B(B(B(B(B(AF9O^(B(B(B(B(B(B(J +MB(B(9F;_B(B(B(B(B(B(B(B(B&9F___________________V9O__________% +M__________\`````````````````````````````````````````````````R +M````````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````````` +!````` +`` +end +sum -r/size 23881/1107 section (from "begin" to "end") +sum -r/size 55184/766 entire input file diff --git a/sys/msdos/nhpif.uu b/sys/msdos/nhpif.uu new file mode 100644 index 0000000..11a8326 --- /dev/null +++ b/sys/msdos/nhpif.uu @@ -0,0 +1,16 @@ +begin 666 nethack.pif +M`%M00R!.971H86-K(#,N,2`@("`@("`@("`@("`@(""``(``0SI<3D542$%# +M2UQ.151(04-++D5810`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@ +M("`@("`@("`@$`!#.EQ.151(04-+`"`@("`@("`@("`@("`@("`@("`@("`@ +M("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@```````````````````` +M```````````````````````````````````````````````````````````` +M```````!`/\94```!P`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````.`@34E#4D]33T94(%!)1D58`( +#include + + +#ifdef _MSC_VER + +#define RESERVED_PARAGRAPHS 5120 /* leave 80K for malloc and inits */ + /* subject to change before release */ + +/* + * memavail() Returns the amount of RAM available (in paragraphs which are 16 + * bytes) - the amount to be reserved for heap allocations. + * + */ +unsigned memavail(minovl) +unsigned minovl; /* minimum size of overlay heap */ +{ + unsigned available; + + unsigned farparaavail; + unsigned tmp; + + /* + * _dos_allocmem will return the maximum block size available. + * It uses DOS (int 21h) service 0x48. + */ + + _dos_allocmem(0xFFFF, &farparaavail); + available = farparaavail - RESERVED_PARAGRAPHS; + tmp = RESERVED_PARAGRAPHS + minovl; + if (farparaavail < tmp) { + panic("Not enough free RAM to begin a game of NetHack (%ld bytes)", + (long)((long)tmp * 16L)); + } + return available; +} +#endif /*_MSC_VER*/ + +#ifdef __BORLANDC__ + +#define RSRVD_MALLOC 65 * 1024L /* malloc() calls use about 65K */ +#define RSRVD_CRTL 50 * 1024L /* C runtime library uses 50K */ +#define RSRVD_TOTAL 115 * 1024L /* reserved for use in malloc() */ + /* as well as by C runtime library */ + /* routines which allocate memory */ + /* after this routine runs. */ +#define MIN_OVRBUF 30 * 1024L /* Overlay buffer gets minimum of */ +#define MAX_OVRBUF 200 * 1024L /* 30K and maximum of 200K. */ + +#define RESIZE_OVL +#ifdef RESIZE_OVL + +extern unsigned _ovrbuffer = 0; /* Use default size initially */ +unsigned appFail = 0; /* Fail flag if not enough RAM */ +unsigned memAlloc = 0; +unsigned long ProgramSize; +unsigned long runAlloc; +unsigned far *mem_top; +unsigned total; +signed long tmpbuffer; +int emsstatus; +int xmsstatus; + +void NDECL(_resizeOvrBuffer); + +void _resizeOvrBuffer() +{ + mem_top = (unsigned far *) MK_FP( _psp, 0x02 ); + total = *mem_top - _psp; + + ProgramSize = * (unsigned far *) MK_FP( _psp - 1, 0x03 ); + tmpbuffer = total - ProgramSize - RSRVD_TOTAL / 16; + memAlloc = min (MAX_OVRBUF / 16, tmpbuffer); + if (tmpbuffer >= MIN_OVRBUF / 16) + _ovrbuffer = memAlloc; + else { + _ovrbuffer = 1; + appFail = 1; + }; + + +/* + * Remember, when inside this code, nothing has been setup on + * the system, so do NOT call any RTL functions for I/O or + * anything else that might rely on a startup function. This + * includes accessing any global objects as their constructors + * have not been called yet. + */ + +} + +#pragma startup _resizeOvrBuffer 0 /* Put function in table */ + +void +startup () +{ + if (appFail) { + printf ("NetHack fits in memory, but it cannot allocate memory"); + printf (" for the overlay buffer\nand the runtime functions. "); + printf ("Please free up just %ld more bytes.", + (long)(MIN_OVRBUF - tmpbuffer * 16L)); + exit (-1); + } else { + + /* Now try to use expanded memory for the overlay manager */ + /* If that doesn't work, we revert to extended memory */ + + emsstatus = _OvrInitEms (0, 0, 0); +#ifdef RECOGNIZE_XMS + xmsstatus = (emsstatus) ? _OvrInitExt (0, 0) : -1; +#endif + + } +} + +void +show_borlandc_stats(win) +winid win; +{ + char buf[BUFSZ]; + + putstr(win, 0, ""); + putstr(win, 0, ""); + putstr(win, 0, "Memory usage stats"); putstr(win, 0, ""); + putstr(win, 0, ""); + Sprintf (buf, "Overlay buffer memory allocation: %ld bytes.", + memAlloc * 16L); putstr(win, 0, buf); + Sprintf (buf, "_ovrbuffer = %u.", _ovrbuffer); putstr(win, 0, buf); + Sprintf (buf, "Startup memory usage: 0x%X", ProgramSize); + putstr(win, 0, buf); + runAlloc = * (unsigned far *) MK_FP( _psp - 1, 0x03); + Sprintf (buf, "Current memory usage: 0x%X", runAlloc); + putstr(win, 0, buf); + if (emsstatus) Sprintf (buf, "EMS search failed (%d).", emsstatus); + else Sprintf (buf, "EMS search successful."); + putstr(win, 0, buf); +#ifdef RECOGNIZE_XMS + if (xmsstatus) Sprintf (buf, "XMS search failed (%d).", xmsstatus); + else Sprintf (buf, "XMS search successful."); + putstr(win, 0, buf); +#endif + + +} + +#endif /* #ifdef RESIZE_OVL */ +#endif /* #ifdef __BORLANDC__ */ + +/*ovlinit.c*/ diff --git a/sys/msdos/pckeys.c b/sys/msdos/pckeys.c new file mode 100644 index 0000000..d2783b4 --- /dev/null +++ b/sys/msdos/pckeys.c @@ -0,0 +1,88 @@ +/* SCCS Id: @(#)pckeys.c 3.4 1996/05/11 */ +/* Copyright (c) NetHack PC Development Team 1996 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * MSDOS tile-specific key handling. + */ + +#include "hack.h" + +#ifdef MSDOS +# ifdef USE_TILES +#include "wintty.h" +#include "pcvideo.h" + +boolean FDECL(pckeys, (unsigned char, unsigned char)); + +extern struct WinDesc *wins[MAXWIN]; /* from wintty.c */ +extern boolean inmap; /* from video.c */ + +#define SHIFT (0x1 | 0x2) +#define CTRL 0x4 +#define ALT 0x8 + +/* + * Check for special interface manipulation keys. + * Returns TRUE if the scan code triggered something. + * + */ +boolean +pckeys(scancode, shift) +unsigned char scancode; +unsigned char shift; +{ + boolean opening_dialog; + + opening_dialog = pl_character[0] ? FALSE : TRUE; +# ifdef SIMULATE_CURSOR + switch(scancode) { + case 0x3d: /* F3 = toggle cursor type */ + HideCursor(); + cursor_type += 1; + if (cursor_type >= NUM_CURSOR_TYPES) cursor_type = 0; + DrawCursor(); + break; +# endif + case 0x74: /* Control-right_arrow = scroll horizontal to right */ + if ((shift & CTRL) && iflags.tile_view && !opening_dialog) + vga_userpan(1); + break; + + case 0x73: /* Control-left_arrow = scroll horizontal to left */ + if ((shift & CTRL) && iflags.tile_view && !opening_dialog) + vga_userpan(0); + break; + case 0x3E: /* F4 = toggle overview mode */ + if (iflags.tile_view && + !opening_dialog +#ifdef REINCARNATION + && !Is_rogue_level(&u.uz) +#endif + ) { + iflags.traditional_view = FALSE; + vga_overview(iflags.over_view ? FALSE : TRUE); + vga_refresh(); + } + break; + case 0x3F: /* F5 = toggle traditional mode */ + if (iflags.tile_view && + !opening_dialog +#ifdef REINCARNATION + && !Is_rogue_level(&u.uz) +#endif + ) { + iflags.over_view = FALSE; + vga_traditional(iflags.traditional_view ? FALSE : TRUE); + vga_refresh(); + } + break; + default: + return FALSE; + } + return TRUE; +} +# endif /* USE_TILES */ +#endif /* MSDOS */ + +/*pckeys.c*/ diff --git a/sys/msdos/pctiles.c b/sys/msdos/pctiles.c new file mode 100644 index 0000000..66a6587 --- /dev/null +++ b/sys/msdos/pctiles.c @@ -0,0 +1,259 @@ +/* SCCS Id: @(#)pctiles.c 3.4 1995/07/31 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * pctiles.c - PC Graphical Tile Support Routines + * + *Edit History: + * Initial Creation M. Allison 93/10/30 + * + */ + +#include "hack.h" + +#ifdef USE_TILES + +#if defined(__GO32__) || defined(__DJGPP__) +#include +#define TILES_IN_RAM /* allow tiles to be read into ram */ +#endif + +# if defined(_MSC_VER) +# if _MSC_VER >= 700 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4127) /* conditional expression is constant */ +#pragma warning(disable:4131) /* old style declarator */ +#pragma warning(disable:4309) /* initializing */ +# endif +#include +# endif + +#include "pcvideo.h" +#include "tile.h" +#include "pctiles.h" + +STATIC_VAR FILE *tilefile; +STATIC_VAR FILE *tilefile_O; +extern short glyph2tile[]; /* in tile.c (made from tilemap.c) */ + +#ifdef TILES_IN_RAM +struct planar_cell_struct *ramtiles; +struct overview_planar_cell_struct *oramtiles; +boolean tiles_in_ram = FALSE; +boolean otiles_in_ram = FALSE; +extern int total_tiles_used; /* tile.c */ +#endif + +# ifdef OVLB + +/* + * Read the header/palette information at the start of the + * NetHack.tib file. + * + * There is 1024 bytes (1K) of header information + * at the start of the file, including a palette. + * + */ +int ReadTileFileHeader(tibhdr, filestyle) +struct tibhdr_struct *tibhdr; +boolean filestyle; +{ + FILE *x; + x = filestyle ? tilefile_O : tilefile; + if (fseek(x,0L,SEEK_SET)) { + return 1; + } else { + fread(tibhdr, sizeof(struct tibhdr_struct), 1, x); + } + return 0; +} + +/* + * Open the requested tile file. + * + * NetHack1.tib file is a series of + * 'struct planar_tile_struct' structures, one for each + * glyph tile. + * + * NetHack2.tib file is a series of + * char arrays [TILE_Y][TILE_X] in dimensions, one for each + * glyph tile. + * + * There is 1024 bytes (1K) of header information + * at the start of each .tib file. The first glyph tile starts at + * location 1024. + * + */ +int +OpenTileFile(tilefilename, filestyle) +char *tilefilename; +boolean filestyle; +{ +#ifdef TILES_IN_RAM + int k; +#endif + if (filestyle) { + tilefile_O = fopen(tilefilename,"rb"); + if (tilefile_O == (FILE *)0) return 1; + } else { + tilefile = fopen(tilefilename,"rb"); + if (tilefile == (FILE *)0) return 1; + } +#ifdef TILES_IN_RAM + if (iflags.preload_tiles) { + if (filestyle) { + struct overview_planar_cell_struct *gp; + long ram_needed = sizeof(struct overview_planar_cell_struct) * + total_tiles_used; + if (fseek(tilefile_O,(long)TIBHEADER_SIZE, SEEK_SET)) { /*failure*/ + } + oramtiles = (struct overview_planar_cell_struct *)alloc(ram_needed); + /* Todo: fall back to file method here if alloc failed */ + gp = oramtiles; + for(k=0; k < total_tiles_used; ++k) { + fread(gp, sizeof(struct overview_planar_cell_struct), + 1, tilefile_O); + ++gp; + } +#ifdef DEBUG_RAMTILES + pline("%d overview tiles read into ram.", k); + mark_synch(); +#endif + otiles_in_ram = TRUE; + } else { + struct planar_cell_struct *gp; + long ram_needed = sizeof(struct planar_cell_struct) * + total_tiles_used; + if (fseek(tilefile,(long)TIBHEADER_SIZE, SEEK_SET)) { /*failure*/ + } + ramtiles = (struct planar_cell_struct *)alloc(ram_needed); + /* Todo: fall back to file method here if alloc failed */ + gp = ramtiles; + for(k=0; k < total_tiles_used; ++k) { + fread(gp, sizeof(struct planar_cell_struct), + 1, tilefile); + ++gp; + } +#ifdef DEBUG_RAMTILES + pline("%d tiles read into ram.", k); + mark_synch(); +#endif + tiles_in_ram = TRUE; + } + } +#endif + return 0; +} + +void +CloseTileFile(filestyle) +boolean filestyle; +{ + fclose(filestyle ? tilefile_O : tilefile); +#ifdef TILES_IN_RAM + if (!filestyle && tiles_in_ram) { + if (ramtiles) free((genericptr_t) ramtiles); + tiles_in_ram = FALSE; + } else if (filestyle && otiles_in_ram) { + if (oramtiles) free((genericptr_t) oramtiles); + otiles_in_ram = FALSE; + } +#endif +} +# endif /* OVLB */ + +# ifdef OVL0 + +struct planar_cell_struct plancell; +struct overview_planar_cell_struct oplancell; + +/* This routine retrieves the requested NetHack glyph tile + * from the planar style binary .tib file. + * This is currently done 'on demand', so if the player + * is running without a disk cache (ie. smartdrv) operating, + * things can really be slowed down. We don't have any + * base memory under MSDOS, in which to store the pictures. + * + * Todo: Investigate the possibility of loading the glyph + * tiles into extended or expanded memory using + * the MSC virtual memory routines. + * + * Under an environment like djgpp, it should be possible to + * read the entire set of glyph tiles into a large + * array of 'struct planar_cell_struct' structures at + * game initialization time, and recall them from the array + * as needed. That should speed things up (at the cost of + * increasing the memory requirement - can't have everything). + * + */ +# ifdef PLANAR_FILE +int ReadPlanarTileFile(tilenum,gp) +int tilenum; +struct planar_cell_struct **gp; +{ + long fpos; + +#ifdef TILES_IN_RAM + if (tiles_in_ram) { + *gp = ramtiles + tilenum; + return 0; + } +#endif + fpos = ((long)(tilenum) * (long)sizeof(struct planar_cell_struct)) + + (long)TIBHEADER_SIZE; + if (fseek(tilefile,fpos,SEEK_SET)) { + return 1; + } else { + fread(&plancell, sizeof(struct planar_cell_struct), 1, tilefile); + } + *gp = &plancell; + return 0; +} +int ReadPlanarTileFile_O(tilenum,gp) +int tilenum; +struct overview_planar_cell_struct **gp; +{ + long fpos; + +#ifdef TILES_IN_RAM + if (otiles_in_ram) { + *gp = oramtiles + tilenum; + return 0; + } +#endif + fpos = ((long)(tilenum) * + (long)sizeof(struct overview_planar_cell_struct)) + + (long)TIBHEADER_SIZE; + if (fseek(tilefile_O,fpos,SEEK_SET)) { + return 1; + } else { + fread(&oplancell, sizeof(struct overview_planar_cell_struct), + 1, tilefile_O); + } + *gp = &oplancell; + return 0; +} +# endif + +# ifdef PACKED_FILE +int ReadPackedTileFile(tilenum,pta) +int tilenum; +char (*pta)[TILE_X]; +{ + long fpos; + + fpos = ((long)(tilenum) * (long)(TILE_Y * TILE_X) + + (long)TIBHEADER_SIZE; + if (fseek(tilefile,fpos,SEEK_SET)) { + return 1; + } else { + fread(pta, (TILE_Y * TILE_X), 1, tilefile); + } + return 0; +} +# endif +# endif /* OVL0 */ +#endif /* USE_TILES */ + +/* pctiles.c */ diff --git a/sys/msdos/pctiles.h b/sys/msdos/pctiles.h new file mode 100644 index 0000000..1750592 --- /dev/null +++ b/sys/msdos/pctiles.h @@ -0,0 +1,65 @@ +/* SCCS Id: @(#)pctiles.h 3.4 1994/04/04 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * pctiles.h - Definitions for PC graphical tile support + * + *Edit History: + * Initial Creation M. Allison 93/10/30 + * + */ + +#ifdef USE_TILES +#define NETHACK_PLANAR_TILEFILE "NetHack1.tib" /* Planar style tiles */ +#define NETHACK_PACKED_TILEFILE "NetHack2.tib" /* Packed style tiles */ +#define NETHACK_OVERVIEW_TILEFILE "NetHacko.tib" /* thin overview tiles */ + +#define ROWS_PER_TILE TILE_Y +#define COLS_PER_TILE TILE_X +#define EMPTY_TILE -1 +#define TIBHEADER_SIZE 1024 /* Use this for size, allows expansion */ +#define PLANAR_STYLE 0 +#define PACKED_STYLE 1 +#define DJGPP_COMP 0 +#define MSC_COMP 1 +#define BC_COMP 2 +#define OTHER_COMP 10 + +struct tibhdr_struct { + char ident[80]; /* Identifying string */ + char timestamp[26]; /* Ascii timestamp */ + char tilestyle; /* 0 = planar, 1 = pixel */ + char compiler; /* 0 = DJGPP, 1 = MSC, 2= BC etc. see above */ + short tilecount; /* number of tiles in file */ + short numcolors; /* number of colors in palette */ + char palette[256 * 3]; /* palette */ +}; + + +/* Note on packed style tile file: + * Each record consists of one of the following arrays: + * char packtile[TILE_Y][TILE_X]; + */ + +extern void FDECL(CloseTileFile, (BOOLEAN_P)); +extern int FDECL(OpenTileFile, (char *, BOOLEAN_P)); +extern int FDECL(ReadTileFileHeader, (struct tibhdr_struct *, BOOLEAN_P)); + +# ifdef PLANAR_FILE +# ifdef SCREEN_VGA +extern int FDECL(ReadPlanarTileFile,(int, struct planar_cell_struct **)); +extern int FDECL(ReadPlanarTileFile_O, + (int, struct overview_planar_cell_struct **)); +# endif +# endif + +# ifdef PACKED_FILE +extern int FDECL(ReadPackedTileFile, (int, char (*)[TILE_X])); +# endif + +extern short glyph2tile[MAX_GLYPH]; /* in tile.c (made from tilemap.c) */ + +#endif /* USE_TILES */ + +/* pctiles.h */ diff --git a/sys/msdos/pcvideo.h b/sys/msdos/pcvideo.h new file mode 100644 index 0000000..41d3061 --- /dev/null +++ b/sys/msdos/pcvideo.h @@ -0,0 +1,283 @@ +/* SCCS Id: @(#)pcvideo.h 3.4 1994/06/07 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * pcvideo.h - Hardware video support definitions and prototypes + * + *Edit History: + * Initial Creation M. Allison 93/10/30 + * + */ + +#ifndef PCVIDEO_H +#define PCVIDEO_H + +#include "portio.h" + +# ifdef SCREEN_BIOS +# if !defined(PC9800) +# define MONO_CHECK /* Video BIOS can do the check */ +# endif +# endif + +# ifdef SCREEN_DJGPPFAST +/*# define MONO_CHECK /* djgpp should be able to do check */ +# endif + +/* + * PC interrupts + */ +# ifdef PC9800 +#define CRT_BIOS 0x18 +#define DOS_EXT_FUNC 0xdc +#define DIRECT_CON_IO 0x10 +# else +#define VIDEO_BIOS 0x10 +# endif +#define DOSCALL 0x21 + + +/* + * Video BIOS functions + */ +# if defined(PC9800) +#define SENSEMODE 0x0b /* Sense CRT Mode */ + +#define PUTCHAR 0x00 /* Put Character */ +#define SETATT 0x02 /* Set Attribute */ +#define SETCURPOS 0x03 /* Set Cursor Position */ +#define CURSOR_RIGHT 0x08 /* Move Cursor Right */ +#define CURSOR_LEFT 0x09 /* Move Cursor Left */ +#define SCREEN_CLEAR 0x0a /* Clear Screen */ +#define LINE_CLEAR 0x0b /* Clear Line */ +# else +#define SETCURPOS 0x02 /* Set Cursor Position */ +# endif + +#define GETCURPOS 0x03 /* Get Cursor Position */ +#define GETMODE 0x0f /* Get Video Mode */ +#define SETMODE 0x00 /* Set Video Mode */ +#define SETPAGE 0x05 /* Set Video Page */ +#define FONTINFO 0x1130 /* Get Font Info */ +#define SCROLL 0x06 /* Scroll or initialize window */ +#define PUTCHARATT 0x09 /* Write attribute & char at cursor */ + +/* + * VGA Specific Stuff + */ +# ifdef SCREEN_VGA +/* #define HW_PANNING /* Hardware panning enabled */ +#define USHORT unsigned short +#define MODE640x480 0x0012 /* Switch to VGA 640 x 480 Graphics mode */ +#define MODETEXT 0x0003 /* Switch to Text mode 3 */ + +#ifdef HW_PANNING +#define PIXELINC 16 /* How much to increment by when panning */ +/*#define PIXELINC 1 /* How much to increment by when panning */ +#define SCREENBYTES 128 +#define CharRows 30 +#define VERT_RETRACE {while (!(inportb(crt_status) & 0x08)); } +#define VERT_RETRACE_END {while ( (inportb(crt_status) & 0x08)); } +#else +#define SCREENBYTES 80 +#endif + +#define CharacterWidth 8 +#define SCREENHEIGHT 480 +#define SCREENWIDTH (SCREENBYTES * CharacterWidth) +#define VIDEOSEG 0xa000 +#define FONT_PTR_SEGMENT 0x0000 +#define FONT_PTR_OFFSET 0x010C +#define SCREENPLANES 4 +#define COLORDEPTH 16 +#define egawriteplane(n) { outportb(0x3c4,2); outportb(0x3c5,n); } +#define egareadplane(n) { outportb(0x3ce,4); outportb(0x3cf,n); } +#define col2x8(c) ((c) * 8) +#define col2x16(c) ((c) * 16) +#define col2x(c) ((c) * 2) +#define row2y(c) ((c) * 16) +#define MAX_ROWS_PER_CELL 16 +#define MAX_COLS_PER_CELL 16 +#define MAX_BYTES_PER_CELL 2 /* MAX_COLS_PER_CELL/8 */ +#define ROWS_PER_CELL MAX_ROWS_PER_CELL +#define COLS_PER_CELL MAX_COLS_PER_CELL +#define BYTES_PER_CELL MAX_BYTES_PER_CELL + +struct cellplane { + char image[MAX_ROWS_PER_CELL][MAX_BYTES_PER_CELL]; +}; + +struct planar_cell_struct { + struct cellplane plane[SCREENPLANES]; +}; + +struct overview_cellplane { + char image[MAX_ROWS_PER_CELL][1]; +}; + +struct overview_planar_cell_struct { + struct overview_cellplane plane[SCREENPLANES]; +}; + + + +# endif /* SCREEN_VGA */ + + +/* + * Default color Indexes for hardware palettes + * + * Do not change the values below. + * These are the color mappings defined by the particular video + * hardware/mode. You can rearrange the NetHack color mappings at + * run-time via the defaults.nh "videocolors" and "videoshades" + * settings. + * + */ + +# if defined(SCREEN_BIOS) || defined(SCREEN_DJGPPFAST) +#define M_BLACK 8 +#define M_WHITE 15 +#define M_GRAY 7 /* low-intensity white */ +#define M_RED 4 +#define M_GREEN 2 +#define M_BROWN 6 /* low-intensity yellow */ +#define M_BLUE 1 +#define M_MAGENTA 5 +#define M_CYAN 3 +#define M_ORANGE 12 +#define M_BRIGHTGREEN 10 +#define M_YELLOW 14 +#define M_BRIGHTBLUE 9 +#define M_BRIGHTMAGENTA 13 +#define M_BRIGHTCYAN 11 + +#define M_TEXT M_GRAY +#define BACKGROUND_COLOR 0 +#define ATTRIB_NORMAL M_TEXT /* Normal attribute */ +#define ATTRIB_INTENSE M_WHITE /* Intense White */ +#define ATTRIB_MONO_NORMAL 0x01 /* Underlined,white */ +#define ATTRIB_MONO_UNDERLINE 0x01 /* Underlined,white */ +#define ATTRIB_MONO_BLINK 0x87 /* Flash bit, white */ +#define ATTRIB_MONO_REVERSE 0x70 /* Black on white */ +# endif /*SCREEN_BIOS || SCREEN_DJGPPFAST */ + +# if defined(SCREEN_VGA) || defined(SCREEN_8514) +#define BACKGROUND_VGA_COLOR 0 +#define ATTRIB_VGA_NORMAL CLR_GRAY /* Normal attribute */ +#define ATTRIB_VGA_INTENSE 13 /* Intense White 94/06/07 palette chg*/ +# endif /*SCREEN_VGA || SCREEN_8514*/ + +# if defined(PC9800) +static unsigned char attr98[CLR_MAX] = { + 0xe1, /* 0 white */ + 0x21, /* 1 blue */ + 0x81, /* 2 green */ + 0xa1, /* 3 cyan */ + 0x41, /* 4 red */ + 0x61, /* 5 magenta */ + 0xc1, /* 6 yellow */ + 0xe1, /* 7 white */ + 0xe1, /* 8 white */ + 0x25, /* 9 reversed blue */ + 0x85, /* 10 reversed green */ + 0xa5, /* 11 reversed cyan */ + 0x45, /* 12 reversed red */ + 0x65, /* 13 reversed magenta */ + 0xc5, /* 14 reversed yellow */ + 0xe5, /* 15 reversed white */ +}; +# endif + +# ifdef SIMULATE_CURSOR +#define CURSOR_HEIGHT 3 /* this should go - MJA */ +/* cursor styles */ +#define CURSOR_INVIS 0 /* cursor not visible at all */ +#define CURSOR_FRAME 1 /* block around the current tile */ +#define CURSOR_UNDERLINE 2 /* thin line at bottom of the tile */ +#define CURSOR_CORNER 3 /* cursor visible at the 4 tile corners */ +#define NUM_CURSOR_TYPES 4 /* number of different cursor types */ +#define CURSOR_DEFAULT_STYLE CURSOR_CORNER +#define CURSOR_DEFAULT_COLOR M_GRAY +/* global variables for cursor */ +extern int cursor_type; +extern int cursor_flag; +extern int cursor_color; +# endif + + +/* + * Function Prototypes + * + */ + +#define E extern + +/* ### video.c ### */ + +# ifdef SIMULATE_CURSOR +E void NDECL(DrawCursor); +E void NDECL(HideCursor); +# endif + +/* ### vidtxt.c ### */ + +# ifdef NO_TERMS +E void NDECL(txt_backsp); +E void NDECL(txt_clear_screen); +E void FDECL(txt_cl_end,(int,int)); +E void NDECL(txt_cl_eos); +E void NDECL(txt_get_scr_size); +E void FDECL(txt_gotoxy,(int,int)); +E int NDECL(txt_monoadapt_check); +E void NDECL(txt_nhbell); +E void FDECL(txt_startup,(int*,int*)); +E void FDECL(txt_xputs, (const char *, int, int)); +E void FDECL(txt_xputc, (CHAR_P, int)); + +/* ### vidvga.c ### */ + +# ifdef SCREEN_VGA +E void NDECL(vga_backsp); +E void FDECL(vga_clear_screen,(int)); +E void FDECL(vga_cl_end,(int,int)); +E void FDECL(vga_cl_eos,(int)); +E int NDECL(vga_detect); +# ifdef SIMULATE_CURSOR +E void NDECL(vga_DrawCursor); +# endif +E void FDECL(vga_DisplayCell, (struct planar_cell_struct *, int, int)); +E void FDECL(vga_DisplayCell_O, + (struct overview_planar_cell_struct *, int, int)); +E void NDECL(vga_Finish); +E char __far *NDECL(vga_FontPtrs); +E void NDECL(vga_get_scr_size); +E void FDECL(vga_gotoloc,(int,int)); +# ifdef POSITIONBAR +E void FDECL(vga_update_positionbar, (char *)); +# endif +# ifdef SIMULATE_CURSOR +E void NDECL(vga_HideCursor); +# endif +E void NDECL(vga_Init); +E void FDECL(vga_SwitchMode, (unsigned int)); +E void FDECL(vga_SetPalette, (char *)); +E void NDECL(vga_tty_end_screen); +E void FDECL(vga_tty_startup,(int*,int*)); +E void FDECL(vga_WriteChar, (int, int, int, int)); +E void FDECL(vga_WriteStr, (char *, int, int, int, int)); +E void FDECL(vga_xputs, (const char *, int, int)); +E void FDECL(vga_xputc, (CHAR_P, int)); +E void FDECL(vga_xputg, (int, int, unsigned)); +E void FDECL(vga_userpan, (BOOLEAN_P)); +E void FDECL(vga_overview, (BOOLEAN_P)); +E void FDECL(vga_traditional, (BOOLEAN_P)); +E void NDECL(vga_refresh); +# endif /* SCREEN_VGA */ +# endif /* NO_TERMS */ + +#undef E + +#endif /* PCVIDEO_H */ +/* pcvideo.h */ diff --git a/sys/msdos/portio.h b/sys/msdos/portio.h new file mode 100644 index 0000000..c1d64de --- /dev/null +++ b/sys/msdos/portio.h @@ -0,0 +1,71 @@ +/* SCCS Id: @(#)portio.h 3.4 1995/08/05 */ +/* Copyright (c) NetHack PC Development Team 1995 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * portio.h - PC port I/O Hardware support definitions and other + * low-level definitions. + * + */ + +#ifndef PORTIO_H +#define PORTIO_H + +# if defined(__GO32__) || defined(__DJGPP__) +#define __far +#include +#include +#include +#endif + +# if defined(_MSC_VER) +#define outportb _outp +#define outportw _outpw +#define inportb _inp +# endif +# if defined(__BORLANDC__) +#define outportw outport +/* #define inportb inport */ +# endif + +# ifndef MK_PTR +/* + * Depending on environment, this is a macro to construct either: + * + * - a djgpp long 32 bit pointer from segment & offset values + * - a far pointer from segment and offset values + * + */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +#define MK_PTR(seg, offset) (void __far *)(((unsigned long)seg << 16) \ + + (unsigned long)(unsigned)offset) +#define READ_ABSOLUTE(x) *(x) +#define READ_ABSOLUTE_WORD(x) *(x) +#define WRITE_ABSOLUTE(x,y) *(x) = (y) +#define WRITE_ABSOLUTE_WORD(x,y) *(x) = (y) +# endif + +# if defined(__GO32__) || defined(__DJGPP__) +#define MK_PTR(seg, offset) (void *)(((unsigned)seg << 4) + (unsigned)offset) +#define READ_ABSOLUTE(x) \ + (_farpeekb(_go32_conventional_mem_selector(), (unsigned)x)) +#define READ_ABSOLUTE_WORD(x) \ + (_farpeekw(_go32_conventional_mem_selector(), (unsigned)x)) +#define WRITE_ABSOLUTE(x,y) \ + _farpokeb(_go32_conventional_mem_selector(), (unsigned)x, (y)) +#define WRITE_ABSOLUTE_WORD(x,y) \ + _farpokew(_go32_conventional_mem_selector(), (unsigned)x, (y)) +# endif + +# ifdef OBSOLETE /* old djgpp V1.x way of mapping 1st MB */ +#define MK_PTR(seg, offset) (void *)(0xE0000000+((((unsigned)seg << 4) \ + + (unsigned)offset))) +#define READ_ABSOLUTE(x) *(x) +#define READ_ABSOLUTE_WORD(x) *(x) +#define WRITE_ABSOLUTE(x,y) *(x) = (y) +#define WRITE_ABSOLUTE_WORD(x,y) *(x) = (y) +# endif +# endif /* MK_PTR */ + +#endif /* PORTIO_H */ +/* portio.h */ diff --git a/sys/msdos/schema1.BC b/sys/msdos/schema1.BC new file mode 100644 index 0000000..3f51531 --- /dev/null +++ b/sys/msdos/schema1.BC @@ -0,0 +1,252 @@ +/* SCCS Id: @(#)schema1.BC 3.4 1999/10/28 */ +/* Copyright (c) Yitzhak Sapir, 1999 */ +/* */ +/* NetHack Overlay Schema */ +/* Minimal extended memory available, lots of 640K base RAM free */ +/* Overlay buffer size will be (20 + 20 + 19) = 59K (sum of 3 largest overlays). */ +/* Requires about 490K (for exe load plus overlay buffer), but */ +/* an additional 70K free (minimum) will be needed for malloc calls, */ +/* bringing the total requirement to about 560K. */ +/* Optimized for minimal overlay turns. */ +/* */ + + +-zCallmain_0 -zAOVLY -zCOVL1 +-zCallmain_1 -zAOVLY -zCOVL2 +-zCallmain_b -zAOVLY -zCOVL3 +-zCalloc_o +-zCapply_0 -zAOVLY -zCOVL4 +-zCapply_1 -zAOVLY -zCOVL5 +-zCapply_b -zAOVLY -zCOVL6 +-zCartifact_0 -zAOVLY -zCOVL7 +-zCartifact_1 -zAOVLY -zCOVL8 +-zCartifact_b -zAOVLY -zCOVL9 +-zCattrib_0 +-zCattrib_1 -zAOVLY -zCOVL10 +-zCattrib_2 -zAOVLY -zCOVL11 +-zCattrib_b -zAOVLY -zCOVL12 +-zCball_o -zAOVLY -zCOVL13 +-zCbones_o -zAOVLY -zCOVL14 +-zCbotl_0 +-zCbotl_1 -zAOVLY -zCOVL15 +-zCbotl_b -zAOVLY -zCOVL16 +-zCcmd_0 +-zCcmd_1 -zAOVLY -zCOVL17 +-zCcmd_b -zAOVLY -zCOVL18 +-zCdbridge_0 +-zCdbridge_1 -zAOVLY -zCOVL19 +-zCdbridge_b -zAOVLY -zCOVL20 +-zCdecl_o -zAOVLY -zCOVL21 +-zCdetect_o +-zCdig_o -zAOVLY -zCOVL22 +-zCdisplay_o +-zCdlb_o -zAOVLY -zCOVL23 +-zCdo_0 -zAOVLY -zCOVL24 +-zCdo_1 -zAOVLY -zCOVL25 +-zCdo_2 -zAOVLY -zCOVL26 +-zCdo_3 -zAOVLY -zCOVL27 +-zCdo_b -zAOVLY -zCOVL28 +-zCdo_name_0 -zAOVLY -zCOVL29 +-zCdo_name_2 -zAOVLY -zCOVL30 +-zCdo_name_b -zAOVLY -zCOVL31 +-zCdo_wear_0 -zAOVLY -zCOVL32 +-zCdo_wear_1 -zAOVLY -zCOVL33 +-zCdo_wear_2 -zAOVLY -zCOVL34 +-zCdo_wear_b -zAOVLY -zCOVL35 +-zCdog_1 -zAOVLY -zCOVL36 +-zCdog_2 -zAOVLY -zCOVL37 +-zCdog_b -zAOVLY -zCOVL38 +-zCdogmove_0 -zAOVLY -zCOVL39 +-zCdogmove_b +-zCdokick_o -zAOVLY -zCOVL40 +-zCdothrow_o -zAOVLY -zCOVL41 +-zCdrawing_o -zAOVLY -zCOVL42 +-zCdungeon_0 +-zCdungeon_1 -zAOVLY -zCOVL43 +-zCeat_0 -zAOVLY -zCOVL44 +-zCeat_1 -zAOVLY -zCOVL45 +-zCeat_b -zAOVLY -zCOVL46 +-zCend_o -zAOVLY -zCOVL47 +-zCengrave_0 +-zCengrave_1 -zAOVLY -zCOVL48 +-zCengrave_2 -zAOVLY -zCOVL49 +-zCengrave_b -zAOVLY -zCOVL50 +-zCexper_o -zAOVLY -zCOVL51 +-zCexplode_0 -zAOVLY -zCOVL52 +-zCexplode_1 -zAOVLY -zCOVL53 +-zCextralev_o -zAOVLY -zCOVL54 +-zCfiles_o -zAOVLY -zCOVL55 +-zCfountain_o -zAOVLY -zCOVL56 +-zCgetline_1 -zAOVLY -zCOVL57 +-zCgetline_2 -zAOVLY -zCOVL58 +-zChack_1 +-zChack_2 -zAOVLY -zCOVL59 +-zChack_3 +-zChack_b -zAOVLY -zCOVL60 +-zChacklib_0 +-zChacklib_1 +-zChacklib_2 +-zChacklib_b -zAOVLY -zCOVL61 +-zCinvent_0 +-zCinvent_1 -zAOVLY -zCOVL62 +-zCinvent_2 -zAOVLY -zCOVL63 +-zCinvent_3 -zAOVLY -zCOVL64 +-zCinvent_b -zAOVLY -zCOVL65 +-zClight_3 -zAOVLY -zCOVL66 +-zClock_0 -zAOVLY -zCOVL67 +-zClock_b -zAOVLY -zCOVL68 +-zCmail_0 -zAOVLY -zCOVL68 +-zCmail_b -zAOVLY -zCOVL69 +-zCmakemon_0 -zAOVLY -zCOVL70 +-zCmakemon_1 -zAOVLY -zCOVL71 +-zCmakemon_2 -zAOVLY -zCOVL72 +-zCmakemon_b -zAOVLY -zCOVL73 +-zCmcastu_0 -zAOVLY -zCOVL74 +-zCmcastu_b -zAOVLY -zCOVL75 +-zCmhitm_0 -zAOVLY -zCOVL76 +-zCmhitm_b -zAOVLY -zCOVL77 +-zCmhitu_0 -zAOVLY -zCOVL78 +-zCmhitu_1 -zAOVLY -zCOVL79 +-zCmhitu_b -zAOVLY -zCOVL80 +-zCminion_o -zAOVLY -zCOVL81 +-zCmklev_o -zAOVLY -zCOVL82 +-zCmkmap_o -zAOVLY -zCOVL83 +-zCmkmaze_o -zAOVLY -zCOVL84 +-zCmkobj_0 -zAOVLY -zCOVL85 +-zCmkobj_1 -zAOVLY -zCOVL86 +-zCmkobj_b -zAOVLY -zCOVL87 +-zCmkroom_0 -zAOVLY -zCOVL88 +-zCmkroom_b -zAOVLY -zCOVL89 +-zCmon_0 +-zCmon_1 -zAOVLY -zCOVL90 +-zCmon_2 -zAOVLY -zCOVL91 +-zCmon_b -zAOVLY -zCOVL92 +-zCmondata_0 +-zCmondata_1 -zAOVLY -zCOVL93 +-zCmondata_2 -zAOVLY -zCOVL94 +-zCmondata_b -zAOVLY -zCOVL95 +-zCmonmove_0 +-zCmonmove_1 +-zCmonmove_2 +-zCmonmove_b -zAOVLY -zCOVL96 +-zCmonst_o -zAOVLY -zCOVL97 +-zCmonstr_o -zAOVLY -zCOVL98 +-zCmplayer_o -zAOVLY -zCOVL99 +-zCmsdos_0 +-zCmsdos_b -zAOVLY -zCOVL100 +-zCmthrowu_0 -zAOVLY -zCOVL101 +-zCmthrowu_1 -zAOVLY -zCOVL102 +-zCmthrowu_b -zAOVLY -zCOVL103 +-zCmuse_o -zAOVLY -zCOVL104 +-zCmusic_o -zAOVLY -zCOVL105 +-zCo_init_o -zAOVLY -zCOVL106 +-zCobjects_o -zAOVLY -zCOVL107 +-zCobjnam_0 -zAOVLY -zCOVL108 +-zCobjnam_1 -zAOVLY -zCOVL109 +-zCobjnam_b -zAOVLY -zCOVL110 +-zCoptions_o -zAOVLY -zCOVL111 +-zCovlinit_o +-zCpager_o -zAOVLY -zCOVL112 +-zCpckeys_o +-zCpcmain_0 +-zCpcmain_1 -zAOVLY -zCOVL113 +-zCpcmain_b -zAOVLY -zCOVL114 +-zCpctiles_0 +-zCpctiles_b +-zCpcunix_b -zAOVLY -zCOVL115 +-zCpickup_o -zAOVLY -zCOVL116 +-zCpline_b -zAOVLY -zCOVL117 +-zCpolyself_0 -zAOVLY -zCOVL118 +-zCpolyself_1 -zAOVLY -zCOVL119 +-zCpolyself_b -zAOVLY -zCOVL120 +-zCpotion_b -zAOVLY -zCOVL121 +-zCpray_o -zAOVLY -zCOVL122 +-zCpriest_0 -zAOVLY -zCOVL123 +-zCpriest_b -zAOVLY -zCOVL124 +-zCquest_o -zAOVLY -zCOVL125 +-zCquestpgr_o -zAOVLY -zCOVL126 +-zCrandom_o +-zCread_b -zAOVLY -zCOVL127 +-zCrect_o -zAOVLY -zCOVL128 +-zCregion_o -zAOVLY -zCOVL129 +-zCrestore_o -zAOVLY -zCOVL130 +-zCrip_o -zAOVLY -zCOVL131 +-zCrnd_0 +-zCrnd_1 -zAOVLY -zCOVL132 +-zCrnd_b -zAOVLY -zCOVL133 +-zCrole_o -zAOVLY -zCOVL113 +-zCrumors_o -zAOVLY -zCOVL134 +-zCsave_o -zAOVLY -zCOVL135 +-zCshk_0 -zAOVLY -zCOVL136 +-zCshk_1 -zAOVLY -zCOVL137 +-zCshk_2 -zAOVLY -zCOVL138 +-zCshk_3 -zAOVLY -zCOVL139 +-zCshk_b -zAOVLY -zCOVL140 +-zCshknam_0 -zAOVLY -zCOVL141 +-zCshknam_b -zAOVLY -zCOVL142 +-zCsit_o -zAOVLY -zCOVL143 +-zCsound_o +-zCsounds_0 -zAOVLY -zCOVL144 +-zCsounds_b -zAOVLY -zCOVL145 +-zCsp_lev_o -zAOVLY -zCOVL146 +-zCspell_o -zAOVLY -zCOVL147 +-zCsteal_0 -zAOVLY -zCOVL148 +-zCsteal_1 -zAOVLY -zCOVL149 +-zCsteal_b -zAOVLY -zCOVL150 +-zCsteed_o -zAOVLY -zCOVL188 +-zCsys_o -zAOVLY -zCOVL151 +-zCteleport_o -zAOVLY -zCOVL152 +-zCtermcap_0 -zAOVLY -zCOVL153 +-zCtermcap_1 -zAOVLY -zCOVL154 +-zCtermcap_b -zAOVLY -zCOVL155 +-zCtile_o +-zCtimeout_0 -zAOVLY -zCOVL156 +-zCtimeout_1 -zAOVLY -zCOVL157 +-zCtimeout_b -zAOVLY -zCOVL158 +-zCtopl_1 -zAOVLY -zCOVL159 +-zCtopl_2 -zAOVLY -zCOVL160 +-zCtopl_b -zAOVLY -zCOVL161 +-zCtopten_o -zAOVLY -zCOVL162 +-zCtrack_0 -zAOVLY -zCOVL163 +-zCtrack_1 -zAOVLY -zCOVL164 +-zCtrack_b -zAOVLY -zCOVL165 +-zCtrap_0 +-zCtrap_1 -zAOVLY -zCOVL166 +-zCtrap_2 -zAOVLY -zCOVL167 +-zCtrap_3 -zAOVLY -zCOVL168 +-zCtrap_b -zAOVLY -zCOVL169 +-zCtty_o -zAOVLY -zCOVL170 +-zCu_init_o -zAOVLY -zCOVL171 +-zCuhitm_o -zAOVLY -zCOVL172 +-zCvault_0 -zAOVLY -zCOVL173 +-zCvault_b -zAOVLY -zCOVL174 +-zCversion_o -zAOVLY -zCOVL175 +-zCvideo_0 +-zCvideo_1 +-zCvideo_b +-zCvidtxt_0 +-zCvidtxt_b +-zCvidvga_0 +-zCvidvga_1 +-zCvidvga_2 -zAOVLY -zCOVL176 +-zCvidvga_b +-zCvis_tab_o -zAOVLY -zCOVL177 +-zCvision_o +-zCweapon_0 -zAOVLY -zCOVL178 +-zCweapon_1 -zAOVLY -zCOVL179 +-zCweapon_b -zAOVLY -zCOVL180 +-zCwere_0 -zAOVLY -zCOVL181 +-zCwere_b -zAOVLY -zCOVL182 +-zCwield_o -zAOVLY -zCOVL183 +-zCwindows_o -zAOVLY -zCOVL184 +-zCwintty_o +-zCwizard_0 -zAOVLY -zCOVL185 +-zCwizard_b -zAOVLY -zCOVL186 +-zCworm_o -zAOVLY -zCOVL187 +-zCworn_o -zAOVLY -zCOVL188 +-zCwrite_o -zAOVLY -zCOVL189 +-zCzap_0 -zAOVLY -zCOVL190 +-zCzap_1 -zAOVLY -zCOVL191 +-zCzap_2 -zAOVLY -zCOVL192 +-zCzap_3 -zAOVLY -zCOVL193 +-zCzap_b -zAOVLY -zCOVL194 diff --git a/sys/msdos/schema2.BC b/sys/msdos/schema2.BC new file mode 100644 index 0000000..3d15f5c --- /dev/null +++ b/sys/msdos/schema2.BC @@ -0,0 +1,248 @@ +/* SCCS Id: @(#)schema2.BC 3.4 1999/10/28 */ +/* Copyright (c) Yitzhak Sapir, 1999 */ +/* */ +/* NetHack Overlay Schema */ +/* Small Root footprint, with extended memory available for caching. */ +/* Almost everything is overlaid. */ +/* */ + + +-zCallmain_0 -zAOVLY -zCOVL1 +-zCallmain_1 -zAOVLY -zCOVL2 +-zCallmain_b -zAOVLY -zCOVL3 +-zCalloc_o +-zCapply_0 -zAOVLY -zCOVL4 +-zCapply_1 -zAOVLY -zCOVL5 +-zCapply_b -zAOVLY -zCOVL6 +-zCartifact_0 -zAOVLY -zCOVL7 +-zCartifact_1 -zAOVLY -zCOVL8 +-zCartifact_b -zAOVLY -zCOVL9 +-zCattrib_0 -zAOVLY -zCOVL10 +-zCattrib_1 -zAOVLY -zCOVL11 +-zCattrib_2 -zAOVLY -zCOVL12 +-zCattrib_b -zAOVLY -zCOVL13 +-zCball_o -zAOVLY -zCOVL14 +-zCbones_o -zAOVLY -zCOVL15 +-zCbotl_0 -zAOVLY -zCOVL16 +-zCbotl_1 -zAOVLY -zCOVL17 +-zCbotl_b -zAOVLY -zCOVL18 +-zCcmd_0 -zAOVLY -zCOVL19 +-zCcmd_1 -zAOVLY -zCOVL20 +-zCcmd_b -zAOVLY -zCOVL21 +-zCdbridge_0 -zAOVLY -zCOVL22 +-zCdbridge_1 -zAOVLY -zCOVL23 +-zCdbridge_b -zAOVLY -zCOVL24 +-zCdecl_o -zAOVLY -zCOVL25 +-zCdetect_o -zAOVLY -zCOVL26 +-zCdig_o -zAOVLY -zCOVL27 +-zCdisplay_o -zAOVLY -zCOVL28 +-zCdlb_o -zAOVLY -zCOVL29 +-zCdo_0 -zAOVLY -zCOVL30 +-zCdo_1 -zAOVLY -zCOVL31 +-zCdo_2 -zAOVLY -zCOVL32 +-zCdo_3 -zAOVLY -zCOVL33 +-zCdo_b -zAOVLY -zCOVL34 +-zCdo_name_0 -zAOVLY -zCOVL35 +-zCdo_name_2 -zAOVLY -zCOVL36 +-zCdo_name_b -zAOVLY -zCOVL37 +-zCdo_wear_0 -zAOVLY -zCOVL38 +-zCdo_wear_1 -zAOVLY -zCOVL39 +-zCdo_wear_2 -zAOVLY -zCOVL40 +-zCdo_wear_b -zAOVLY -zCOVL41 +-zCdog_1 -zAOVLY -zCOVL42 +-zCdog_2 -zAOVLY -zCOVL43 +-zCdog_b -zAOVLY -zCOVL44 +-zCdogmove_0 -zAOVLY -zCOVL45 +-zCdogmove_b -zAOVLY -zCOVL46 +-zCdokick_o -zAOVLY -zCOVL47 +-zCdothrow_o -zAOVLY -zCOVL48 +-zCdrawing_o -zAOVLY -zCOVL49 +-zCdungeon_0 -zAOVLY -zCOVL50 +-zCdungeon_1 -zAOVLY -zCOVL51 +-zCeat_0 -zAOVLY -zCOVL52 +-zCeat_1 -zAOVLY -zCOVL53 +-zCeat_b -zAOVLY -zCOVL54 +-zCend_o -zAOVLY -zCOVL55 +-zCengrave_0 -zAOVLY -zCOVL56 +-zCengrave_1 -zAOVLY -zCOVL57 +-zCengrave_2 -zAOVLY -zCOVL58 +-zCengrave_b -zAOVLY -zCOVL59 +-zCexper_o -zAOVLY -zCOVL60 +-zCexplode_0 -zAOVLY -zCOVL61 +-zCexplode_1 -zAOVLY -zCOVL62 +-zCextralev_o -zAOVLY -zCOVL63 +-zCfiles_o -zAOVLY -zCOVL64 +-zCfountain_o -zAOVLY -zCOVL65 +-zCgetline_1 -zAOVLY -zCOVL66 +-zCgetline_2 -zAOVLY -zCOVL67 +-zChack_1 -zAOVLY -zCOVL68 +-zChack_2 -zAOVLY -zCOVL69 +-zChack_3 +-zChack_b -zAOVLY -zCOVL70 +-zChacklib_0 -zAOVLY -zCOVL71 +-zChacklib_1 -zAOVLY -zCOVL72 +-zChacklib_2 -zAOVLY -zCOVL73 +-zChacklib_b -zAOVLY -zCOVL74 +-zCinvent_0 -zAOVLY -zCOVL75 +-zCinvent_1 -zAOVLY -zCOVL76 +-zCinvent_2 -zAOVLY -zCOVL77 +-zCinvent_3 -zAOVLY -zCOVL78 +-zCinvent_b -zAOVLY -zCOVL79 +-zClight_3 -zAOVLY -zCOVL80 +-zClock_0 -zAOVLY -zCOVL81 +-zClock_b -zAOVLY -zCOVL82 +-zCmail_0 -zAOVLY -zCOVL82 +-zCmail_b -zAOVLY -zCOVL83 +-zCmakemon_0 -zAOVLY -zCOVL84 +-zCmakemon_1 -zAOVLY -zCOVL85 +-zCmakemon_2 -zAOVLY -zCOVL86 +-zCmakemon_b -zAOVLY -zCOVL87 +-zCmcastu_0 -zAOVLY -zCOVL88 +-zCmcastu_b -zAOVLY -zCOVL89 +-zCmhitm_0 -zAOVLY -zCOVL90 +-zCmhitm_b -zAOVLY -zCOVL91 +-zCmhitu_0 -zAOVLY -zCOVL92 +-zCmhitu_1 -zAOVLY -zCOVL93 +-zCmhitu_b -zAOVLY -zCOVL94 +-zCminion_o -zAOVLY -zCOVL95 +-zCmklev_o -zAOVLY -zCOVL96 +-zCmkmap_o -zAOVLY -zCOVL97 +-zCmkmaze_o -zAOVLY -zCOVL98 +-zCmkobj_0 -zAOVLY -zCOVL99 +-zCmkobj_1 -zAOVLY -zCOVL100 +-zCmkobj_b -zAOVLY -zCOVL101 +-zCmkroom_0 -zAOVLY -zCOVL102 +-zCmkroom_b -zAOVLY -zCOVL103 +-zCmon_0 -zAOVLY -zCOVL104 +-zCmon_1 -zAOVLY -zCOVL105 +-zCmon_2 -zAOVLY -zCOVL106 +-zCmon_b -zAOVLY -zCOVL107 +-zCmondata_0 -zAOVLY -zCOVL108 +-zCmondata_1 -zAOVLY -zCOVL109 +-zCmondata_2 -zAOVLY -zCOVL110 +-zCmondata_b -zAOVLY -zCOVL111 +-zCmonmove_0 -zAOVLY -zCOVL112 +-zCmonmove_1 -zAOVLY -zCOVL113 +-zCmonmove_2 -zAOVLY -zCOVL114 +-zCmonmove_b -zAOVLY -zCOVL115 +-zCmonst_o -zAOVLY -zCOVL116 +-zCmonstr_o -zAOVLY -zCOVL117 +-zCmplayer_o -zAOVLY -zCOVL118 +-zCmsdos_0 -zAOVLY -zCOVL119 +-zCmsdos_b -zAOVLY -zCOVL120 +-zCmthrowu_0 -zAOVLY -zCOVL121 +-zCmthrowu_1 -zAOVLY -zCOVL122 +-zCmthrowu_b -zAOVLY -zCOVL123 +-zCmuse_o -zAOVLY -zCOVL124 +-zCmusic_o -zAOVLY -zCOVL125 +-zCo_init_o -zAOVLY -zCOVL126 +-zCobjects_o -zAOVLY -zCOVL127 +-zCobjnam_0 -zAOVLY -zCOVL128 +-zCobjnam_1 -zAOVLY -zCOVL129 +-zCobjnam_b -zAOVLY -zCOVL130 +-zCoptions_o -zAOVLY -zCOVL131 +-zCovlinit_o +-zCpager_o -zAOVLY -zCOVL132 +-zCpckeys_o -zAOVLY -zCOVL119 +-zCpcmain_0 +-zCpcmain_1 -zAOVLY -zCOVL133 +-zCpcmain_b -zAOVLY -zCOVL134 +-zCpctiles_0 +-zCpctiles_b +-zCpcunix_b -zAOVLY -zCOVL135 +-zCpickup_o -zAOVLY -zCOVL136 +-zCpline_b -zAOVLY -zCOVL137 +-zCpolyself_0 -zAOVLY -zCOVL138 +-zCpolyself_1 -zAOVLY -zCOVL139 +-zCpolyself_b -zAOVLY -zCOVL140 +-zCpotion_b -zAOVLY -zCOVL141 +-zCpray_o -zAOVLY -zCOVL142 +-zCpriest_0 -zAOVLY -zCOVL143 +-zCpriest_b -zAOVLY -zCOVL144 +-zCquest_o -zAOVLY -zCOVL145 +-zCquestpgr_o -zAOVLY -zCOVL146 +-zCrandom_o -zAOVLY -zCOVL147 +-zCread_b -zAOVLY -zCOVL148 +-zCrect_o -zAOVLY -zCOVL149 +-zCregion_o -zAOVLY -zCOVL150 +-zCrestore_o -zAOVLY -zCOVL151 +-zCrip_o -zAOVLY -zCOVL152 +-zCrnd_0 -zAOVLY -zCOVL153 +-zCrnd_1 -zAOVLY -zCOVL154 +-zCrnd_b -zAOVLY -zCOVL155 +-zCrole_o -zAOVLY -zCOVL133 +-zCrumors_o -zAOVLY -zCOVL156 +-zCsave_o -zAOVLY -zCOVL157 +-zCshk_0 -zAOVLY -zCOVL158 +-zCshk_1 -zAOVLY -zCOVL159 +-zCshk_2 -zAOVLY -zCOVL160 +-zCshk_3 -zAOVLY -zCOVL161 +-zCshk_b -zAOVLY -zCOVL162 +-zCshknam_0 -zAOVLY -zCOVL163 +-zCshknam_b -zAOVLY -zCOVL164 +-zCsit_o -zAOVLY -zCOVL165 +-zCsound_o +-zCsounds_0 -zAOVLY -zCOVL166 +-zCsounds_b -zAOVLY -zCOVL167 +-zCsp_lev_o -zAOVLY -zCOVL168 +-zCspell_o -zAOVLY -zCOVL169 +-zCsteal_0 -zAOVLY -zCOVL170 +-zCsteal_1 -zAOVLY -zCOVL171 +-zCsteal_b -zAOVLY -zCOVL172 +-zCsteed_o -zAOVLY -zCOVL173 +-zCsys_o -zAOVLY -zCOVL174 +-zCteleport_o -zAOVLY -zCOVL175 +-zCtermcap_0 -zAOVLY -zCOVL176 +-zCtermcap_1 -zAOVLY -zCOVL177 +-zCtermcap_b -zAOVLY -zCOVL178 +-zCtile_o +-zCtimeout_0 -zAOVLY -zCOVL179 +-zCtimeout_1 -zAOVLY -zCOVL180 +-zCtimeout_b -zAOVLY -zCOVL181 +-zCtopl_1 -zAOVLY -zCOVL182 +-zCtopl_2 -zAOVLY -zCOVL183 +-zCtopl_b -zAOVLY -zCOVL184 +-zCtopten_o -zAOVLY -zCOVL185 +-zCtrack_0 -zAOVLY -zCOVL186 +-zCtrack_1 -zAOVLY -zCOVL187 +-zCtrack_b -zAOVLY -zCOVL188 +-zCtrap_0 -zAOVLY -zCOVL189 +-zCtrap_1 -zAOVLY -zCOVL190 +-zCtrap_2 -zAOVLY -zCOVL191 +-zCtrap_3 -zAOVLY -zCOVL192 +-zCtrap_b -zAOVLY -zCOVL193 +-zCtty_o -zAOVLY -zCOVL194 +-zCu_init_o -zAOVLY -zCOVL195 +-zCuhitm_o -zAOVLY -zCOVL196 +-zCvault_0 -zAOVLY -zCOVL197 +-zCvault_b -zAOVLY -zCOVL198 +-zCversion_o -zAOVLY -zCOVL199 +-zCvideo_0 +-zCvideo_1 +-zCvideo_b +-zCvidtxt_0 +-zCvidtxt_b +-zCvidvga_0 +-zCvidvga_1 +-zCvidvga_2 -zAOVLY -zCOVL200 +-zCvidvga_b +-zCvis_tab_o -zAOVLY -zCOVL201 +-zCvision_o -zAOVLY -zCOVL202 +-zCweapon_0 -zAOVLY -zCOVL203 +-zCweapon_1 -zAOVLY -zCOVL204 +-zCweapon_b -zAOVLY -zCOVL205 +-zCwere_0 -zAOVLY -zCOVL206 +-zCwere_b -zAOVLY -zCOVL207 +-zCwield_o -zAOVLY -zCOVL208 +-zCwindows_o -zAOVLY -zCOVL209 +-zCwintty_o -zAOVLY -zCOVL210 +-zCwizard_0 -zAOVLY -zCOVL211 +-zCwizard_b -zAOVLY -zCOVL212 +-zCworm_o -zAOVLY -zCOVL213 +-zCworn_o -zAOVLY -zCOVL173 +-zCwrite_o -zAOVLY -zCOVL214 +-zCzap_0 -zAOVLY -zCOVL215 +-zCzap_1 -zAOVLY -zCOVL216 +-zCzap_2 -zAOVLY -zCOVL217 +-zCzap_3 -zAOVLY -zCOVL218 +-zCzap_b -zAOVLY -zCOVL219 diff --git a/sys/msdos/schema3.MSC b/sys/msdos/schema3.MSC new file mode 100644 index 0000000..df254e9 --- /dev/null +++ b/sys/msdos/schema3.MSC @@ -0,0 +1,429 @@ +; SCCS Id: @(#)schema3.MSC 3.4 2003/08/03 +; Copyright (c) NetHack PC Development Team, 2000 +; +; NetHack Overlay Schema +; This overlay schema is for use only when NetHack is built +; using packaged-functions for function-level linking. +; +; Overlay tuning level: 0 +; +functions:0 _main _dosave0 _moveloop _bputc _bwrite _random _rn2 _newsym _show_glyph _on_level _txt_gotoxy _txt_get_cursor +functions:0 _xputc _txt_xputc _t_at _tty_curs _acurr _dist2 _tty_print_glyph _isok _back_to_glyph +functions:0 _show_map_spot _adjust_cursor_flags _lowc _g_putch _has_color _is_pool _mread +functions:0 _is_lava _welded _magic_map_background _end_glyphout _visible_region_at _domove +functions:0 _engr_at _rnd _run_regions _mcalcmove _depth _done _distmin _does_block _eos +functions:0 _move _move_bc _in_rooms _map_background _map_invisible _map_location _alloc +functions:0 _impossible _in_container _in_fcorridor _In_hell _In_mines _in_or_out_menu _In_quest +functions:0 _do_positionbar _iswall _iswall_or_stone _itimeout _is_solid _Is_special +functions:0 _is_swallow_sym _check_here _check_leash _check_map_spot _check_pos +functions:0 _monsndx _m_move _lcase _tty_create_nhwindow _tty_delay_output _tty_destroy_nhwindow +functions:0 _tty_dismiss_nhwindow _gender _genl_outrip _get_cost _get_free_room_loc _get_level +functions:0 _get_location _get_map _get_mleash +; +functions:1 _move_update _movebubbles _movecmd _movemon _moverock _movobj _mpickgold +functions:1 _doaltarobj _doapply _dobreathe _docall _docast +; +functions:2 _do_vicinity_map +functions:3 _pcmain +functions:4 _spell_let_to_idx _cursed_book _deadbook _learn _getspell _spelltypemnemonic +functions:5 _dospellmenu _percent_success _throwspell _cast_protection _isqrt +; +functions:6 _a_gname _a_gname_at _a_monnam _abon _abuse_dog _accessible _activate_statue_trap +functions:7 _add_branch _add_damage _add_debug_extended_commands _add_door _add_id_mapping _add_level +functions:8 _add_menu_cmd_alias _add_one_tobill _add_rect _add_room _add_subroom _add_to_billobjs _add_to_buried _add_to_container +functions:9 _add_to_migration _add_to_minv _add_valid_menu_class _add_weapon_skill _addinv _addtobill _addtopl _addupbill +functions:10 _Adjmonnam +functions:11 _align_gname +functions:12 _altar_wrath _Amonnam _amulet _Amulet_off _Amulet_on _An _an _angry_guards +functions:13 _angry_priest _angry_shk_exists _any_light_source _aobjnam _append_slash _append_str _Armor_gone _Armor_off +functions:14 _Armor_on _armor_to_dragon _armoroff _arti_invoke _artifact_exists _artifact_hit _artifact_name _artiname +functions:15 _artitouch _askchain _assign_graphics _assign_level _assign_rnd_level _assign_rogue_graphics _assign_soundcard _assign_video +functions:16 _assign_videocolors _assign_videoshades _assigninvlet _at_dgn_entrance _attach_egg_hatch_timeout _attack _attack_checks _attacks +functions:17 _awaken_monsters _awaken_soldiers +functions:18 _backfire _backsp _bad_location _bad_negation _bad_rock _badoption _bail _ballfall +functions:19 _bc_order _bclose _bcsign _beg _begin_burn _bflush _bhit _bhitm +functions:20 _bhito _bhitpile _big_to_little _bill_box_content _bill_dummy_object _bite _bless _blessorcurse +functions:21 _Blindf_off _Blindf_on _block_door _block_entry _block_point _blow_up_landmine _body_part +functions:22 _boomhit _Boots_off _Boots_on _bot _bot1 _bot2 _boulder_hits_pool _bound_digging +functions:23 _boxlock _br_string _break_armor _break_statue _breakarm _breakmsg +functions:24 _breakobj _breaks _breaksink _breaktest _breamu _bribe _bufoff _bufon +functions:25 _build_room _burn_floor_paper _burn_object _burnarmor _bury_an_obj _bury_objs _buzz _buzzmu +functions:26 _Can_dig_down +functions:27 _Can_fall_thru _can_make_bones _can_ooze _can_pray _can_reach_floor _Can_rise_up _can_track +functions:28 _cancel_bonesfile _cancel_don _cancel_item _cancel_monst _candle_light_range _canletgo _cant_create _canwearobj +functions:29 _carry_count _carrying _castmu _ceiling _center _change_inv_order _change_luck _change_sex +functions:30 _charm_monsters _charm_snakes _chat_with_guardian _chat_with_leader _chat_with_nemesis _chdirx _chdrive _cheapest_item +functions:31 _check_capacity _check_contained _check_credit _check_recordfile +functions:32 _check_room _check_shop_obj _check_special_room _check_unpaid _check_unpaid_usage _check_version _checkfile _chest_shatter_msg +functions:33 _chest_trap _choke _choke_dialogue _choose_classes_menu _choose_windows _christen_monst _chwepon _ck_bag +functions:34 _ckmailstatus _ckunpaid _cl_end _cl_eos _classmon _clear_fcorr _clear_glyph_buffer +functions:35 _clear_id_mapping _clear_level_structures _clear_path _clear_screen _clear_stale_map _clear_unpaid _clearlocks _clearpriests +functions:36 _click_to_cmd _Cloak_off _Cloak_on _clone_mon _cloneu _clonewiz _close_drawbridge _close_library +functions:37 _closed_door _CloseTileFile _cls _cmov _cnv_trap_obj _co_false _collect_obj_classes _com_pager +functions:38 _commit_bonesfile _compactify _compress _compress_bonesfile _compress_str _comspec_exists _confdir _construct_qtlist +functions:39 _consume_offering _contained _contained_cost _contained_gold _container_contents _container_weight _convert_arg _convert_line +functions:40 _copybones _copyfile _corpse_chance _corpse_xname _corr _correct_branch_type _cost _cost_per_charge +functions:41 _costly_cancel _costly_gold _costly_spot _could_seduce _count_categories _count_obj _count_unpaid _count_wsegs +functions:42 _counter_were _courtmon _cpostfx _cprefx _create_altar _create_bonesfile _create_corridor _create_critters +functions:43 _create_door _create_drawbridge _create_engraving _create_feature _create_gold _create_levelfile _create_monster _create_mplayers +functions:44 _create_object _create_particular _create_polymon _create_room _create_savefile _create_secret_door _create_stairs _create_subroom +functions:45 _create_trap _create_worm_tail _curr_mon_load _currentlevel_rewrite _curs_on_u _curse _cursed _cursed_object_at +functions:46 _cursetxt _cuss _cutworm _cvt_sdoor_to_door _d _damageum _dbon _ddocall +functions:47 _ddoinv _dead_species _dealloc_obj _decl_init _deepest_lev_reached _def_char_to_monclass _def_char_to_objclass _def_raw_print +functions:48 _defends _deferred_goto _del_engr _del_engr_at _del_light_source _delallobj _delete_bonesfile _delete_contents +functions:49 _delete_levelfile _delete_savefile _delfloortrap _deliver_by_pline _deliver_by_window _delobj _deltrap _demon_talk +functions:50 _demonpet _destroy_arm _destroy_drawbridge _destroy_item _destroy_mitem _dev_name _dig +functions:51 _dig_check _dig_corridor _dig_point _dig_typ _digactualhole _dighole _digit _dipfountain +functions:52 _disable_ctrlP _disarm_landmine _disarm_shooting_trap _disarm_squeaky_board _discard_minvent _disclose _discover_object +functions:53 _diseasemu _display_binventory _display_cinventory _display_gamewindows _display_inventory _display_minventory +functions:54 _distant_name _distfleeck _disturb _djinni_from_bottle _dlb_cleanup _dlb_fclose +functions:55 _dlb_fgets _dlb_fopen _dlb_fread _dlb_fseek _dlb_ftell _dlb_init _dlord _dmgtype +functions:56 _dmgval _dmonsfree _dmore _dname_to_dnum _do_break_wand _do_clear_area _do_comp _do_dknown_of +functions:57 _do_earthquake _do_entity _do_genocide _do_improvisation _do_light_sources _do_look _do_mapping _do_mname +functions:57 _vga_xputg _vga_xputs _video_update_positionbar _view_from _view_init _visctrl _vision_init _vision_recalc +functions:58 _do_oname _do_osshock _do_play_instrument _do_reset_eat _do_room_or_subroom _do_storms _do_takeoff +functions:60 _doclose _doconsult _docorner _docrt _doddoremarm _doddrop _dodip +functions:61 _adjust_prefix _build_plselection_prompt _duplicate_opt_detection _enter_explore_mode _maybe_wail +functions:62 _doextcmd _doextlist _doextversion _dofindgem _dofiretrap _doforce _dog_eat _dog_goal +functions:63 _dog_hunger _dog_invent _dog_move _dog_nutrition _dogfood _dogushforth _dohelp _dohide +functions:64 _dohistory _doidtrap _doinvbill _doinvoke _dojump _dokick _dolook _doloot +functions:65 _domagicportal +functions:66 _done_eating _done_in_by _done_intr _done1 _done2 _donning _donull _doopen +functions:67 _doorganize _doorlock _dopay _dopayobj _dopickup _dopotion _dopramulet _doprarm +functions:68 _dopray _doprev_message _doprgold _doprring _doprtool _doprwep _doputon _doquickwhatis +functions:69 _doread _dorecover _doredraw _doremove _doremring _dorub _dosacrifice _dosave +functions:70 _dosdoor _dosearch _dosearch0 _doseduce _doset _doset_add_menu _dosh +functions:71 _dosinkfall _dosinkring _dosit _dosounds _dospinweb _dospit _dosummon _dotakeoff +functions:72 _dotalk _dotele _dothrow _dotogglepickup _dotrap _doturn _dotypeinv _dounpaid +functions:73 _dountrap _doup _doversion _dovspell _dowaterdemon _dowaternymph _dowatersnakes _dowear +functions:74 _dowhatdoes _dowhatis _dowield _dowipe _down_gate _dowrite _dozap _dprince +functions:75 _drag_ball _drag_down _drain_en _DrawCursor _drinkfountain _drinksink _drop _drop_ball +functions:76 _drop_throw _drop_to _drop_upon_death _drop_weapon _DROPPABLES _dropped_container _dropx _dropy +functions:77 _drown _dryup _dtoxy _dungeon_branch _dunlev _dunlevs_in_dungeon _e_at _e_died +functions:78 _e_jumps _e_missed _e_nam _E_phrase _e_survives_at _eataccessory _eatcorpse _eaten_stat +functions:79 _eatfood _eatmdone _eatspecial _egg_type_from_parent _emergency_disrobe _enable_ctrlP _encumber_msg _end_burn +functions:80 _end_engulf +;functions:81 +functions:82 _exclam _exepath _exerchk _exercise _exerper _exist_artifact _expels _experience +functions:83 _explmm _explmu _explode _explum _expulsion _ext_cmd_getlin_hook _extend_spine _extract_nexthere +functions:84 _extract_nobj _fall_asleep _fall_through _feel_cockatrice _feel_location _fightm _filesize_nh _fill_pit +functions:85 _nh_getenv _promptsep _rigid_role_checks _set_duplicate_opt_detection _tool_in_use +functions:86 _find_drawbridge _find_hell _find_lev_obj _find_level _find_mac _find_mid _find_misc _find_offensive +functions:87 _find_oid _find_roll_to_hit _find_skates _find_unpaid _finddpos _findfirst_file _findgd _findit +functions:88 _findnext_file _findone _findpriest _finish_map _finish_paybill _fix_stair_rooms _fix_worst_trouble _fixup_special +functions:89 _flash_hits_mon _float_down _float_up _floating_above _flood_fill_rm _flooreffects _floorfood _flush_screen _term_start_color +functions:90 _flushout _fmt_ptr _food_detect _food_disappears _food_xname _foodword _fopen_config_file _fopen_datafile +functions:91 _fopenp _forcelock _forget_levels _forget_map _forget_objects _forget_traps _foundfile_buffer _fpostfx +functions:92 _fprefx _fracture_rock _free_dungeons _free_rooms _free_ttlist _free_window_info _freediskspace _freedynamicdata +functions:93 _freefruitchn _freehand _freeinv _friday_13th _fruitadd _fry_by_god _fully_identify_obj _g_at +functions:94 _gainstr _gameDiskPrompt _gazemm _gazemu _gd_move _gd_sound _gem_accept +functions:95 _display_warning _dlb_fgetc _doattributes _dochug _dochugw +functions:96 _fill_point _fill_room _fill_zoo _fillholetyp _find_ac _find_branch _find_branch_room _find_defensive +functions:96 _get_mon_location _get_mplname _get_obj_location _get_rect _get_rect_ind _get_room_loc _get_scr_size _get_shop_item +functions:97 _get_uchars _get_unused_cs _get_valuables _get_wall_for_db _get_wet _get_wormno _getbones _getdir +functions:98 _gethungry +functions:99 _gettty _getversionstring _getyear _ggetobj _ghitm _ghod_hitsu _ghost_from_bottle _ghostfruit +functions:100 _givit _glibr _Gloves_off _Gloves_on _glyph_at _god_zaps_you _gods_angry _gods_upset +functions:101 _godvoice _gold_detect _golemeffects _golemhp _goodfruit _goodpos _goto_hell _goto_level +functions:102 _gr_finish _gr_init _graphics_opts _grddead _grease_protect _grow_up _growl _growl_sound +functions:103 _guardname _gulpmm _gulpmu _gulpum _gush _hack_artifacts _has_dnstairs +functions:104 _has_shrine _has_upstairs _hatch_egg _hates_silver _have_lizard _hcolor _heal_legs _healup +functions:105 _Hear_again _Helmet_off _Helmet_on _help_menu _help_monster_out _hero_breaks _hidden_gold _HideCursor +functions:106 _highc _histemple_at _hit _hitfloor _hitmm _hitmsg _hitmu _hitum +functions:107 _hitval _hmon _hmon_hitmon _hmonas _hold_another_object _holetime _home _home_shk +functions:108 _homebase _hooked_tty_getlin _hot_pursuit _hurtarmor _hurtle _identify _identify_pack _impact_drop +functions:110 _in_trouble _In_V_tower _In_W_tower _in_your_sanctuary _incr_itimeout _induced_align _inherits _inhishop +functions:111 _init_artifacts _init_attr _init_dungeons _init_fill _init_level _init_map _init_objects +functions:112 _init_oracles _init_rect _init_rumors _init_ttycolor _init_uhunger _initedog _initoptions _initrack +functions:113 _initworm _insert_branch _insert_timer _inside_room _inside_shop _instapetrify _intemple _interesting_to_discover +functions:114 _intermed _intervene _intrinsic_possible _inv_cnt _inv_weight _invault _invdisp_nothing _inven_inuse +functions:115 _invert_all _invert_all_on_page _Invocation_lev _invocation_message _invocation_pos _Is_botlevel _Is_branchlev _is_chargeable +functions:116 _is_db_wall _is_drawbridge_wall _is_edible _is_fainted _is_flammable _is_fshk _is_home_elemental _is_ice +;functions:117 +functions:118 _is_worn _is_worn_by_type _isbig _isclearpath +functions:119 _itimeout_incr _its_dead _itsstuck _Japanese_item_name _join _join_map _jump _keepdogs +functions:119 _sticks _still_chewing _stock_room _stolen_container _stolen_value _stone_luck _stoned_dialogue _stop_occupation +functions:120 _kick_monster _kick_object _kickdmg _kickstr _kill_egg _kill_eggs _kill_genocided_monsters _killed +functions:121 _kind_name _known_hitum _kops_gone _l_monnam _lantern_message _launch_obj _lava_effects +functions:122 _ldrname _leader_speaks _learn_egg_type _ledger_no _ledger_to_dlev _ledger_to_dnum +functions:123 _left_side _lesshungry _let_to_name _letter _lev_by_name _level_difficulty _level_distance _level_range +functions:124 _level_tele _level_tele_trap _levl_follower _lifesaved_monster _lift_object _light_cocktail _light_region _lined_up +functions:125 _linedup _list_genocided _list_vanquished _litroom _litter _little_to_big _llord _lminion +functions:126 _load_common_data _load_maze _load_one_engraving _load_one_monster _load_one_object _load_qtlist _load_rooms _load_special +functions:127 _loadfruitchn _lock_action _lock_file _locomotion _lookaround _lookat _lookup_id_mapping _lose_weapon_skill +functions:128 _losedogs _losehp _losespells _losestr _losexp _m_arrival +functions:129 _m_carrying _m_detach _m_dowear _m_dowear_type _m_initgrp _m_initinv _m_initthrow _m_initweap +functions:130 _m_lose_armor +functions:131 _m_useup _make_angry_shk _make_blinded _make_confused _make_corpse _make_engr_at _make_familiar _make_hallucinated +functions:132 _make_happy_shk _make_lockname _make_niches _make_sick _make_stunned _make_vomiting _makecorridors _makedog +functions:133 _makekops _makelevel _makemaz _makemon _makeniche _makeplural _makerogueghost _makeroguerooms +functions:134 _makerooms +functions:135 _map_menu_cmd _map_object _map_trap _match_optname _mattackm _mattacku _max_capacity _max_mon_load +functions:136 _max_passive_dmg _max_rank_sz _maxledgerno _may_dig _may_passwall _maybe_write_ls _maybe_write_timer _mayberem +functions:137 _maze0xy _maze1xy _mazexy _mb_trapped _mbag_explodes _mbhit _mbhitm _mcalcdistress +functions:138 _md_rush _md_start _md_stop _mdig_tunnel +functions:139 _meatobj _melt_ice _menu_drop _menu_identify _menu_loot _menu_remarm _mergable _merge_choice +functions:140 _merged _mfndpos _midnight _migrate_to_level _mineralize _minit _miniwalk _minstapetrify +functions:140 _On_stairs _on_start _On_W_tower_level _oname _onbill _online2 _only_here _onlyspace +functions:141 _mintrap +functions:142 _mk_bubble _mk_knox_portal _mk_mplayer _mk_named_object _mk_roamer _mk_tt_object _mkaltar _mkbox_cnts +functions:143 _mkcavearea _mkcavepos _mkclass _mkcorpstat _mkfount _mkgold _mkgoldobj _mkinvokearea +functions:144 _mkinvpos _mklev _mkmap _mkobj _mkobj_at _mkportal _mkroll_launch _mkroom +functions:145 _mkshobj_at _mkshop _mksink _mksobj _mksobj_at _mkstairs _mkswamp _mktemple +functions:146 _mktrap _mkundead _mkzoo _mlevel_tele_trap _mlifesaver _mnearto _mnexto _mon_arrive +functions:147 _mon_break_armor _mon_catchup_elapsed_time _mon_chain _mon_has_amulet _mon_has_arti _mon_has_special _mon_invent_chain _mon_is_local +functions:148 _mon_nam _mon_nam_too _mon_owns _mon_reflects _mon_regen _mon_set_minvis _mon_to_stone _mon_wield_item +functions:149 _mondead _mondied _mongets _mongone _monkilled _Monnam _monnear +functions:150 _monst_init _monster_detect _monster_nearby _monstinroom _monstone _monstr_init _more _more_experienced +functions:151 _morehungry _morguemon _move_gold _move_into_trap _move_special +functions:153 _mpickobj +functions:154 _mselftouch _msg_in _msleep _msmsg _mstatusline _msummon _mswings _mswingsm +functions:155 _mtele_trap _mungspaces _munstone _mv_bubble _mvault_tele _mzapmsg _n_or_more _name_to_mon +functions:156 _nameshk _nartifact_exist _nasty _ndemon _near_capacity _nemdead _nemesis_speaks _neminame +functions:157 _nethack_exit _new_light_source _new_were _newcham _newexplevel _newgame _newhp _newmail +functions:158 _newman _newuexp _newuhs _next_level _next_opt _next_shkp _next_to_u +functions:159 _nexttodoor _nh_timeout _nhusage _night _nmcpy _no_bones_level _noattacks _nocmov +functions:160 _nohandglow _noises _nomul _Norep _not_capable _not_fully_identified _number_leashed _o_in +functions:161 _o_on _o_unleash _obfree _obj_chain _obj_delivery _obj_extract_self _obj_here _obj_ice_effects +functions:162 _obj_is_burning _obj_is_local _obj_is_pname _obj_merge_light_sources _obj_move_light_source _obj_move_timers _obj_resists _obj_sanity_check +functions:163 _obj_sheds_light _obj_shudders _obj_split_light_source _obj_split_timers _obj_stop_timers _obj_to_let _object_detect _objects_init +functions:164 _observable_depth _obstructed _oc_to_str _occupied _off_msg _ohitmon _oinit _ok_to_quest +functions:165 _okay +functions:167 _onquest _onscary _open_bonesfile _open_drawbridge _open_levelfile _open_library _open_savefile _openit +functions:168 _openone _OpenTileFile _opentin _option_help _oselect _other_mon_has_arti _otransit_msg +functions:169 _out_container _outentry _outheader _outoracle _outrumor _p_coaligned _pacify_guards _pacify_shk +functions:170 _panic _parent_dlevel _parent_dnum _parse _parse_config_line _parseoptions _pass_one _pass_three +functions:171 _pass_two _passive _passivemm _passiveum _pay _pay_for_damage _paybill _paygd +functions:172 _pckeys _peace_minded _peek_at_iced_corpse_age _peffects _pet_type _pgetchar _phase_of_the_moon +functions:173 _pick_level _pick_lock _pick_obj _pick_room _picked_container _picking_at _picking_lock _picklock +functions:174 _pickup _pickup_object _place_branch _place_level _place_lregion _place_niche _place_object _place_worm_tail_randomly +functions:175 _place_wsegs _placebc _playwoRAMdisk _pleased _pline _pline_The _plnamesuffix _pluslvl +functions:176 _pmatch _poisoned _poisontell _poly_gender _poly_obj _poly_when_stoned _polyman _polymon +functions:177 _polyself _polyuse _port_help _pos_to_room _positionbar _possible_places _possibly_unwield _potionbreathe +functions:178 _potionhit _prayer_done _precheck _prev_level _pri_move _price_quote _priest_talk _priestini +functions:179 _priestname _print_branch _print_dungeon _print_queue _prinv _probe_monster _process_menu_window _process_text_window +functions:180 _pronoun_gender _protects _prscore _punish _pushch _put_monsters_to_sleep _putsyms +functions:181 _qt_montype _qt_pager _query_category _query_classes _query_objlist _quest_chat _quest_info +functions:182 _quest_stat_check _quest_talk _random_dir _random_engraving _random_teleport_level _ranged_attk +functions:183 _rank _rank_of _raw_printf _read_config_file _read_engr_at _readchar _readentry +functions:184 _readmail _readobjnam _ReadPlanarTileFile _ReadPlanarTileFile_O _ReadTileFileHeader _reassign _recalc_wt _recharge +functions:185 _record_exists _redist_attr _redotoplin _regularize _rehumanize _rejectoption _relink_light_sources _relink_timers +functions:186 _relmon _remember_topl _remove_damage _remove_object _remove_rect _remove_timer _remove_worm +functions:187 _removetopl _reorder_invent _repair_damage _replmon _replshk _rescham _reset_attribute_clock +functions:188 _reset_eat _reset_faint _reset_hostility _reset_occupations _reset_pick _reset_remarm _reset_rndmonst _reset_trapset +functions:189 _resetobjs _resist _resists_blnd _resists_drli _resists_magm _rest_engravings _rest_room _rest_rooms +functions:190 _rest_worm _restartcham _restdamage _restfakecorr _restgamestate _restlevchn _restlevelfile _restlevelstate +functions:191 _restmonchn _restnames _restobjchn _restore_artifacts _restore_attrib _restore_dungeon _restore_light_sources _restore_oracles +functions:192 _restore_saved_game _restore_timers _restore_waterlevel _restpriest _restrap _restrict_name _restshk _resurrect +functions:193 _reverse _revive _revive_corpse _revive_egg _revive_mon _revive_nasty _reward_untrap _rhack +functions:194 _right_side _rile_shk _Ring_gone _Ring_off _Ring_off_or_gone _Ring_on _rloc _rloc_engr +functions:195 _rloc_pos_ok _rloc_to _rloco _rm_waslit _rnd_class _rnd_defensive_item +functions:196 _rnd_misc_item _rnd_offensive_item _rnd_rect _rndcurse _rnddoor _rndexp _rndghostname _rndmonnam +functions:197 _rndmonnum _rndmonst _rndtrap _rne _rnl _rnz _rogue_vision _roguecorr +functions:198 _roguejoin _roguename _rot_corpse _rot_organic _rottenfood _rounddiv _row_refresh _run_timers +functions:199 _rust_dmg _s_suffix _safe_teleds _saleable _same_price _sanity_check _save_artifacts _save_currentstate +functions:200 _save_dungeon _save_engravings _save_light_sources _save_oracles _save_room _save_rooms _save_savefile_name _save_timers +functions:201 _save_waterlevel _save_worm _savebones _savech _savedamage _saveDiskPrompt _savefruitchn _savegamestate +functions:202 _savelev _savelev0 _savelevchn _savelife _savemonchn _savenames _saveobjchn _savestateinlock +functions:203 _savetrapchn _scatter _schedule_goto _score_wanted _search_door _search_special _searches_for_item _see_lamp_flicker +functions:204 _see_monsters _see_objects _see_traps _see_wsegs _seemimic _seetrap _seffects _select_hwep +functions:205 _select_off _select_rwep _selftouch _sellobj _sellobj_state _sengr_at _sense_trap +functions:206 _set_all_on_page _set_apparxy _set_artifact_intrinsic _set_bc _set_bonesfile_name _set_bonestemp_name _set_corn _set_cost +functions:207 _set_crosswall _set_entity _set_item_state _set_itimeout _set_levelfile_name _set_lit _set_lock_and_bones _set_malign +functions:208 _set_mimic_blocking _set_mimic_sym _set_mon_data _set_moreluck _set_occupation _set_repo_loc _set_residency _set_savefile_name +functions:209 _set_seenv _set_trap _set_twall _set_uasmon _set_wall _set_wall_property _set_wall_state _set_wear +functions:210 _set_wounded_legs _set_wportal _setclipped _setftty _setgemprobs _setmangry _setnotworn _setpaid +functions:211 _setrandom _settrack _settty _setup_waterlevel _setuwep _setworn _sgn _Shield_off +functions:212 _shieldeff _ship_object _shk_chat _shk_embellish _shk_move _shk_names_obj _shk_owns _shk_your +functions:213 _Shk_Your _shkcatch _shkgone _shkinit _shkname _sho_obj_return_to_u _shop_debt _shop_keeper +functions:214 _shop_object _shopdig _shopper_financial_report _shrine_pos _shrink_worm _shuffle +functions:215 _shuffle_all _shuffle_tiles _simple_look _singular _sitoa _skill_advance _skill_init _skill_level_name +functions:216 _skinback _sleep_monst _slept_monst _slip_or_trip _sliparm _slots_required _snuff_candle _snuff_light_source +functions:217 _snuff_lit _sobj_at _some_armor _somegold _somex _somexy _somey _sort_rooms +functions:218 _sort_valuables _sp_lev_shuffle _spec_ability _spec_abon _spec_applies _spec_dbon _spell_damage_bonus _spell_hit_bonus +functions:219 _spell_skilltype _spelleffects _spitmu _splatter_burning_oil _split_mon _split_rects _splitbill _splitobj +functions:220 _spoteffects _squadmon _srandom _stackobj _standoutbeg _standoutend _start_corpse_timeout _start_eating +functions:221 _start_engulf _start_timer _start_tin _steal _steal_it _stealamulet _stealarm _stealgold +functions:223 _stop_timer _store_version _strange_feeling _strategy _string_for_env_opt _string_for_opt _strncmpi _strprepend +functions:224 _strstri _study_book _stumble_onto_mimic _sub_one_frombill _subfrombill _substitute_tiles _summon_minion _surface +functions:225 _swallow_to_glyph _swapin_file _swapout_oldest _switch_graphics _switchar _t_warn +functions:226 _tabexpand _tactics _take_gold _take_off _tamedog _target_on _tele _tele_jump_ok +functions:227 _tele_restrict _tele_trap _teleds _teleok _teleport_pet _temple_occupied _tended_shop _term_end_attr +functions:228 _term_end_color +functions:229 _The _this_type_only _thitm _thitmonst _thitu _throw_gold _throwing_weapon _throwit +functions:230 _thrwmu _tileview _timed_occupation _timer_is_local _timer_sanity_check _tinnable _title_to_mon _tmp_at +functions:231 _topl_putsym _topologize _topten _topten_print _topten_print_bold _toss_up _toss_wsegs _touch_artifact +functions:232 _touchfood _trap_detect _trickery _try_disarm _try_lift _trycall _tt_oname _tty_add_menu +functions:233 _tty_askname _tty_clear_nhwindow _tty_cliparound +functions:234 _tty_display_file _tty_display_nhwindow _tty_doprev_message _tty_end_menu _tty_end_screen _tty_exit_nhwindows _tty_get_ext_cmd _tty_get_nh_event +functions:235 _tty_getlin _tty_init_nhwindows _tty_mark_synch _tty_message_menu _tty_nh_poskey _tty_nhbell _tty_nhgetch _tty_number_pad +functions:236 _tty_player_selection _tty_raw_print _tty_raw_print_bold _tty_resume_nhwindows _tty_select_menu +functions:237 _tty_start_menu _tty_start_screen _tty_startup _tty_suspend_nhwindows _tty_update_inventory _tty_yn_function +functions:238 _txt_backsp _txt_cl_end _txt_cl_eos _txt_clear_screen _txt_get_scr_size _txt_monoadapt_check +functions:239 _txt_nhbell _txt_startup _u_entered_shop _u_gname _u_init +functions:240 _u_left_shop _u_on_dnstairs _u_on_newpos _u_on_sstairs _u_on_upstairs _u_slip_free _u_slow_down _u_teleport_mon +functions:241 _u_to_e _u_wipe_engr _ugolemeffects _um_dist _unbless _unblock_point _uncommon _uncompress +functions:242 _unconscious _uncurse _undead_to_corpse _under_ground _under_water _undiscover_object _unearth_objs _unfaint +functions:243 _unleash_all _unload_qtlist _unlock_file _unmap_object _unmul _unpaid_cost _unplacebc _unpunish +functions:244 _unrestrict_weapon_skill _unset_all_on_page _unsetup_waterlevel _unstuck _untrap _untrap_prob _unturn_dead _update_mon_intrinsics +functions:245 _update_topl _uptodate _urustm _use_bell _use_camera _use_candelabrum _use_candle _use_container +functions:246 _use_crystal_ball _use_defensive _use_figurine _use_grease _use_lamp _use_leash _use_magic_whistle _use_mirror +functions:247 _use_misc _use_offensive _use_pick_axe _use_skill _use_stethoscope _use_tinning_kit _use_towel _use_trap +functions:248 _use_unicorn_horn _use_whip _use_whistle _useup _useupall _useupf _ustatusline _uunstick +functions:249 _uwepgone _vault_occupied _vault_tele _verbalize _vga_backsp _vga_cl_end _vga_cl_eos _vga_clear_screen +functions:250 _vga_cliparound _vga_detect _vga_DisplayCell _vga_DisplayCell_O _vga_DrawCursor _vga_Finish _vga_FontPtrs _vga_get_scr_size +functions:251 _vga_gotoloc _vga_HideCursor _vga_Init _vga_overview _vga_redrawmap _vga_refresh _vga_SetPalette _vga_SwitchMode +functions:252 _vga_traditional _vga_tty_end_screen _vga_tty_startup _vga_update_positionbar _vga_userpan _vga_WriteChar _vga_WriteStr _vga_xputc +functions:254 _vision_reset _dodiscovered _dodoor _dodown _dodrink _dodrop _doeat _doengrave +functions:255 _wallification _wallify_map _wallify_vault _wantdoor _watch_on_duty _water_damage _water_friction +functions:256 _water_prayer _weapon_dam_bonus _weapon_hit_bonus _weapon_type _wearing_armor _weffects _weight +functions:257 _weight_cap +functions:258 _whimper _wield_tool _wildmiss _win_tty_init _wipe_engr_at _wipeoff _wipeout_text +functions:259 _wiz_detect _wiz_genesis _wiz_identify _wiz_level_tele _wiz_light_sources _wiz_map _wiz_show_seenv _wiz_show_vision +functions:260 _wiz_show_wmodes _wiz_timeout_queue _wiz_where _wiz_wish _wizdead _worm_known _worm_move _worm_nomove +functions:261 _wormgone _wormhitu _worn_wield_only _write_ls _writeentry _wrong_elem_type _x_monnam _xcrypt _xkilled _xlev_to_rank _xname _xprname +functions:263 _xytod _yelp _yname _Yname2 _You _you_aggravate _You_cant _You_feel _you_have _You_hear _you_unwere _you_were _Your _zap_dig +functions:265 _zap_hit _zap_over_floor _zap_updown _zapdir_to_glyph _zapnodir _zappable _zapyourself _zhitm _zhitu _put_lregion_here _role_init +; tuning +; this was 23 +functions:266 _bp_to_obj +; the next two were 238 +functions:268 _aggravate +; the following were 118 +;functions:269 +; the following were 261 +;functions:270 +functions:271 _xputg +functions:272 _xputs _xwaitforspace +; the following were 158 +functions:273 _enermod _enlightenment +; the following was 214 +;functions:274 _enter_explore_mode +; the following were 26 +functions:275 _bydoor +functions:276 _calc_capacity _call_kops _calm_nymphs +functions:277 _can_advance _can_be_hatched +functions:278 _can_carry +; the following were 239 +functions:279 _equipname +functions:280 _txt_xputs +; the following were 80 +functions:281 _enexto +;functions:282 +functions:283 _enhance_weapon_skill +; the following were 165 +functions:284 _okdoor +functions:285 _omon_adj +functions:286 _on_goal +functions:287 _on_ground +functions:288 _b_trapped +functions:289 _on_locate +functions:290 _on_msg +; the following were 182 +functions:291 _y_monnam +; the following were 195 +;functions:293 +; the following were 225 +functions:294 _erode_armor +functions:295 _swallowed +; the following were 7 +functions:298 _acurrstr +; the following were 17 +functions:299 _attacktype +functions:300 _attrcurse +functions:301 _automiss +functions:302 _autopick +; the following were 10 +functions:303 _adj_abon +functions:304 _adj_lev +functions:305 _adjabil +functions:306 _adjalign +functions:307 _adjattrib +; was 53 +functions:308 _display_monster +functions:309 _error +; was 117 +;functions:310 +functions:311 _is_ok_location +;functions:312 +functions:313 _is_pure +functions:314 _is_quest_artifact +; was 236 +functions:316 _tty_putstr _tty_putsym +; was 81 +functions:318 _escapes +functions:319 _erase_menu_or_text +functions:320 _eraseall +; tuning 2 +; was 269 +functions:321 _weldmsg _were_change _were_summon +functions:322 _where_name _which_armor _which_arti +functions:325 _term_end_raw_bold +functions:326 _term_start_attr +;functions:327 +functions:328 _term_start_raw_bold +functions:329 _terminate +functions:330 _tgetch +functions:331 _the +functions:332 _m_monnam +functions:333 _domagictrap _domindblast _domonability +functions:334 _m_respond +functions:335 _m_slips_free +functions:336 _m_throw +functions:337 _m_to_e +functions:338 _m_unleash +functions:339 _mpickstuff +functions:340 _mplayer_talk +functions:341 _mpoisons_subj +functions:342 _mquaffmsg +functions:343 _miss +functions:344 _mreadmsg +functions:345 _mrustm +functions:346 _vomit _vomiting_dialogue +functions:347 _wake_nearby +functions:348 _wake_nearto +functions:349 _wakeup +functions:350 _walkfrom +functions:351 _wall_angle +functions:352 _able_to_loot _add_mon_to_reg _add_rect_to_reg _add_region _addinv_core1 +functions:352 _addinv_core2 _age_spells _align_gtitle _align_shift _align_str _all_but_uchain +functions:353 _allow_all _allow_category _already_wearing _already_wearing2 _angrygods +functions:354 _animate_statue _antholemon _arti_speak _assign_warnings _attach_fig_transform_timeout _blocked_boulder +functions:355 _book_substitution _burn_away_slime _can_blnd _can_ride _can_saddle _can_twoweapon +functions:356 _carry_obj_effects _clear_regions _container_at _coyotename _create_gas_cloud _create_region +functions:357 _describe_level _dfeature_at _dig_up_grave _discover_artifact _dismount_steed _disp_artifact_discoveries +functions:359 _doconduct _dofire _doprinuse _doride _doswapweapon _dotwoweapon +functions:360 _dowieldquiver _drain_item _exercise_steed _expire_gas_cloud _extcmd_via_menu _feature_alert_opts +functions:361 _fig_transform _figurine_location_checks _final_level _find_trap _finish_quest _fix_petrification +functions:362 _food_substitution _fqname _free_invbuf _free_region _free_youbuf _freeinv_core +functions:363 _fuzzymatch _get_compopt_value _get_current_feature_ver _get_feature_notice_ver _get_mtraits _getlev +functions:364 _getlock _getobj _getpos _give_may_advance_msg _Goodbye _halu_gname +functions:365 _Hello _hurtle_step _hurtmarmor _in_out_region _initialspell _inside_gas_cloud +functions:366 _inside_rect _inside_region _kick_steed _look_here _m_in_out_region _make_grave +functions:367 _mbodypart _mdamagem _mdamageu _minimal_enlightenment _mk_mplayer_armor _mm_aggression +functions:368 _mon_adjust_speed _mon_animal_list _mon_beside _mon_in_region _montraits _mount_steed +functions:369 _noit_mon_nam _noit_Monnam _noncoalignment _num_genocides _obj_attach_mid _obj_timer_checks +functions:370 _obj_typename _ok_align _ok_gend _ok_race _ok_role _ordin +functions:371 _pick_align _pick_animal _pick_gend _pick_nasty _pick_race _pick_role +functions:372 _place_monster _pm_to_cham _prisoner_speaks _process_options _randalign _randgend +functions:373 _randrace _randrole _realloc_obj _relobj _remove_mon_from_reg _remove_region +functions:374 _remove_worn_item _replace_object _reset_oattached_mids _rest_regions _restore_cham _rnd_treefruit_at +functions:375 _save_regions _select_newcham_form _self_invis_message _setuqwep _setuswapwep _show_conduct +functions:376 _show_region _simple_typename _slime_dialogue _sokoban_detect _spec_m2 _special_handling +functions:377 _str2align _str2gend _str2race _str2role _There _throw_obj +functions:378 _tmiss _tty_update_positionbar _tty_wait_synch _undiscovered_artifact _untwoweapon _update_mlstmv +functions:379 _update_monster_region _update_player_regions _uqwepgone _ureflects _use_grapple _use_pole +functions:380 _use_saddle _uswapwepgone _uwep_skill_type _validalign _validgend _validrace +functions:381 _validrole _violated_vegetarian _walk_path _warning_opts _wary_dog _welcome +functions:382 _write_timer _yyyymmdd _zap_steed +functions:383 _getprice _getreturn _getrumor _gettrack +functions:384 _ini_inv _knows_object _knows_class _restricted_spell_discipline _ready_weapon +functions:385 _doname _Doname2 +functions:386 _minliquid +functions:387 _missmm +functions:388 _missmu +functions:389 _missum +functions:390 _mixtype _mk_artifact +functions:391 _makesingular +functions:392 _maketrap _makevtele _makewish + diff --git a/sys/msdos/setup.bat b/sys/msdos/setup.bat new file mode 100644 index 0000000..62278c5 --- /dev/null +++ b/sys/msdos/setup.bat @@ -0,0 +1,167 @@ +@echo off +REM SCCS Id: @(#)setup.bat 2002/03/17 +REM Copyright (c) NetHack PC Development Team 1990 - 2002 +REM NetHack may be freely redistributed. See license for details. + +echo. +echo Copyright (c) NetHack PC Development Team 1990 - 2002 +echo NetHack may be freely redistributed. See license for details. +echo. +REM setup batch file for msdos, see Install.dos for details. + +if not %1.==. goto ok_parm +goto err_set + +:ok_parm +echo Checking to see if directories are set up properly ... +if not exist ..\..\include\hack.h goto err_dir +if not exist ..\..\src\hack.c goto err_dir +if not exist ..\..\dat\wizard.des goto err_dir +if not exist ..\..\util\makedefs.c goto err_dir +if not exist ..\..\win\tty\wintty.c goto err_dir +if not exist ..\share\lev_yacc.c goto err_dir +echo Directories OK. + +if not exist ..\..\binary\* mkdir ..\..\binary +if NOT exist ..\..\binary\license copy ..\..\dat\license ..\..\binary\license >nul + +if exist ..\..\dat\data.bas goto long1ok +if exist ..\..\dat\data.base goto long1a +if exist ..\..\dat\data~1.bas goto long1b +goto err_long +:long1a +echo Changing some long-named distribution file names: +echo "Copying ..\..\dat\data.base -> ..\..\dat\data.bas" +copy ..\..\dat\data.base ..\..\dat\data.bas +if exist ..\..\dat\data.old del /Q ..\..\dat\data.old +ren ..\..\dat\data.base data.old +goto long1ok +:long1b +echo Changing some long-named distribution file names: +echo "Copying ..\..\dat\data~1.bas -> ..\..\dat\data.bas" +copy ..\..\dat\data~1.bas ..\..\dat\data.bas +if exist ..\..\dat\data.old del /Q ..\..\dat\data.old +ren ..\..\dat\data~1.bas data.old +:long1ok + +if exist ..\..\include\patchlev.h goto long2ok +if exist ..\..\include\patchlevel.h goto long2a +if exist ..\..\include\patchl~1.h goto long2b +goto err_long +:long2a +echo "Copying ..\..\include\patchlevel.h -> ..\..\include\patchlev.h" +copy ..\..\include\patchlevel.h ..\..\include\patchlev.h +if exist ..\..\include\patchlev.old del /Q ..\..\include\patchlev.old +ren ..\..\include\patchlevel.h patchlev.old +goto long2ok +:long2b +echo "Copying ..\..\include\patchl~1.h -> ..\..\include\patchlev.h" +copy ..\..\include\patchl~1.h ..\..\include\patchlev.h +if exist ..\..\include\patchlev.old del /Q ..\..\include\patchlev.old +ren ..\..\include\patchl~1.h patchlev.old +:long2ok + +REM Missing guidebook is not fatal to the build process +if exist ..\..\doc\guideboo.txt goto long3ok +if exist ..\..\doc\guidebook.txt goto long3a +if exist ..\..\doc\guideb~1.txt goto long3b +goto warn3long +:long3a +echo "Copying ..\..\doc\guidebook.txt -> ..\..\doc\guideboo.txt" +copy ..\..\doc\guidebook.txt ..\..\doc\guideboo.txt +if exist ..\..\doc\guideboo.old del /Q ..\..\doc\guideboo.old +ren ..\..\doc\guidebook.txt guideboo.old +goto long3ok +:long3b +echo "Copying ..\..\doc\guideb~1.txt -> ..\..\doc\guideboo.txt" +copy ..\..\doc\guideb~1.txt ..\..\doc\guideboo.txt +if exist ..\..\doc\guideboo.old del /Q ..\..\doc\guideboo.old +ren ..\..\doc\guideb~1.txt guideboo.old +goto long3ok +:warn3long +echo "Warning - There is no NetHack Guidebook (..\..\doc\guideboo.txt)" +echo " included in your distribution. Build will proceed anyway." +:long3ok + +if "%1"=="GCC" goto ok_gcc +if "%1"=="gcc" goto ok_gcc +if "%1"=="nmake" goto ok_msc +if "%1"=="NMAKE" goto ok_msc +if "%1"=="BC" goto ok_bc +if "%1"=="bc" goto ok_bc +if "%1"=="MSC" goto ok_msc +if "%1"=="msc" goto ok_msc +goto err_set + +:ok_gcc +echo Symbolic links, msdos style +echo "Makefile.GCC -> ..\..\src\makefile" +copy makefile.GCC ..\..\src\makefile +goto done + +:ok_msc +echo Copying Makefile for Microsoft C and Microsoft NMAKE. +echo "Makefile.MSC -> ..\..\src\makefile" +copy Makefile.MSC ..\..\src\makefile +echo Copying overlay schemas to ..\..\src +copy schema*.MSC ..\..\src\schema*.DEF +:ok_cl +goto done + +:ok_bc +echo Copying Makefile for Borland C and Borland's MAKE. +echo "Makefile.BC -> ..\..\src\makefile" +copy Makefile.BC ..\..\src\makefile +echo Copying overlay schemas to ..\..\src +copy schema*.BC ..\..\src +goto done + +:err_long +echo. +echo ** ERROR - New file system with "long file name support" problem. ** +echo A couple of NetHack distribution files that are packaged with +echo a long filename ( exceeds 8.3) appear to be missing from your +echo distribution. +echo The following files need to exist under the names on the +echo right in order to build NetHack: +echo. +echo ..\..\dat\data.base needs to be copied to ..\..\dat\data.bas +echo ..\..\include\patchlevel.h needs to be copied to ..\..\include\patchlev.h +echo. +echo setup.bat was unable to perform the necessary changes because at least +echo one of the files doesn't exist under its short name, and the +echo original (long) file name to copy it from was not found either. +echo. +goto end + +:err_set +echo. +echo Usage: +echo "%0 " +echo. +echo Run this batch file specifying on of the following: +echo GCC, MSC, BC +echo. +echo (depending on which compiler and/or make utility you are using). +echo. +echo The GCC argument is for use with djgpp and the NDMAKE utility. +echo. +echo The MSC argument is for use with Microsoft C and the NMAKE utility +echo that ships with it (MSC 7.0 or greater only, including Visual C). +echo. +echo The BC argument is for use with Borland C and Borland's MAKE utility +echo that ships with it (Borland C++ 3.1 only). +echo. +goto end + +:err_dir +echo/ +echo Your directories are not set up properly, please re-read the +echo Install.dos and README documentation. +goto end + +:done +echo Setup Done! +echo Please continue with next step from Install.dos. + +:end diff --git a/sys/msdos/sound.c b/sys/msdos/sound.c new file mode 100644 index 0000000..b283f59 --- /dev/null +++ b/sys/msdos/sound.c @@ -0,0 +1,332 @@ +/* SCCS Id: @(#)sound.c 3.4 1996/02/19 */ +/* Copyright (c) NetHack PC Development Team 1993,1995 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * sound.c - Hardware sound support + * + *Edit History: + * Initial Creation 93/10/01 + * Added PC Speaker Support for BC compilers 95/06/14 + * Completed various fixes 96/02/19 + * + */ + +#include "hack.h" +#include +#include "portio.h" + +#include +#include + +#ifndef TESTING + +#define printf pline + +int +assign_soundcard(sopt) +char *sopt; +{ + + iflags.hassound = 0; +# ifdef PCMUSIC + iflags.usepcspeaker = 0; +# endif + + if (strncmpi(sopt,"def",3) == 0) { /* default */ + /* do nothing - default */ + } +# ifdef PCMUSIC + else if (strncmpi(sopt,"speaker",7) == 0) { /* pc speaker */ + iflags.usepcspeaker = 1; + } +# endif + else if (strncmpi(sopt,"auto",4) == 0) { /* autodetect */ + /* + * Auto-detect Priorities (arbitrary for now): + * Just pcspeaker + */ + if (0) ; +# ifdef PCMUSIC + else iflags.usepcspeaker = 1; +# endif + } else { + return 0; + } + return 1; + +} +#endif + +#ifdef PCMUSIC + +/* 8254/3 Control Word Defines */ + +#define CTR0SEL (0<<6) +#define CTR1SEL (1<<6) +#define CTR2SEL (2<<6) +#define RDBACK (3<<6) + +#define LATCH (0<<4) +#define RW_LSB (1<<4) +#define RW_MSB (2<<4) /* If both LSB and MSB are read, LSB is done first */ + +#define MODE0 (0<<1) /* Interrupt on terminal count */ +#define MODE1 (1<<1) /* Hardware One-Shot */ +#define MODE2 (2<<1) /* Pulse Generator */ +#define MODE3 (3<<1) /* Square Wave Generator */ +#define MODE4 (4<<1) /* Software Triggered Strobe */ +#define MODE5 (5<<1) /* Hardware Triggered Strobe */ + +#define BINARY (0<<0) /* Binary counter (16 bits) */ +#define BCD (1<<0) /* Binary Coded Decimal (BCD) Counter (4 Decades) */ + +/* Misc 8254/3 Defines */ + +#define TIMRFRQ (1193180UL) /* Input frequency to the clock (Hz) */ + +/* Speaker Defines */ + +#define TIMER (1<<0) /* Timer 2 Output connected to Speaker */ +#define SPKR_ON (1<<1) /* Turn on/off Speaker */ + +/* Port Definitions */ + +/* 8254/3 Ports */ + +#define CTR0 0x40 +#define CTR1 0x41 +#define CTR2 0x42 +#define CTRL 0x43 + +/* Speaker Port */ + +#define SPEAKER 0x61 + +void +startsound (unsigned freq) +{ + /* To start a sound on the PC: + * + * First, set the second counter to have the correct frequency: + */ + + unsigned count; + + if (freq == 0) freq = 523; + + count = TIMRFRQ / freq; /* Divide frequencies to get count. */ + +#ifdef TESTING + printf ("freq = %u, count = %u\n", freq, count); +#endif + + outportb (CTRL, CTR2SEL|RW_LSB|RW_MSB|MODE3|BINARY); + outportb (CTR2, count & 0x0FF); + outportb (CTR2, count / 0x100); + + /* Next, turn on the speaker */ + + outportb (SPEAKER, inportb(SPEAKER)|TIMER|SPKR_ON); +} + +void +stopsound (void) +{ + outportb (SPEAKER, inportb(SPEAKER) & ~(TIMER|SPKR_ON)); +} + +static unsigned tempo, length, octave, mtype; + +/* The important numbers here are 287700UL for the factors and 4050816000UL + * which gives the 440 Hz for the A below middle C. "middle C" is assumed to + * be the C at octave 3. The rest are computed by multiplication/division of + * 2^(1/12) which came out to 1.05946 on my calculator. It is assumed that + * no one will request an octave beyond 6 or below 0. (At octave 7, some + * notes still come out ok, but by the end of the octave, the "notes" that + * are produced are just ticks. + + * These numbers were chosen by a process based on the C64 tables (which + * weren't standardized) and then were 'standardized' by giving them the + * closest value. That's why they don't seem to be based on any sensible + * number. + */ + +unsigned long notefactors[12] = { 483852, 456695, 431063, 406869, 384033, + 362479, 342135, 322932, 304808, 287700, 271553, 256312 }; + +void +note (long notenum) +{ + startsound ((unsigned) (4050816000UL / notefactors[notenum % 12] + >> (7 - notenum / 12))); +} + +int notes[7] = { 9, 11, 0, 2, 4, 5, 7 }; + +char * +startnote (char *c) +{ + long n; + + n = notes[toupper(*c++) - 'A'] + octave * 12; + if (*c == '#' || *c == '+') { n++; c++; } + else if (*c == '-') { if (n) n--; c++; } + + note (n); + + return --c; +} + +void +delaytime (unsigned time) +{ + /* time and twait are in units of milliseconds */ + + unsigned twait; + + switch (toupper (mtype)) { + case 'S': twait = time / 4; break; + case 'L': twait = 0; break; + default: twait = time / 8; break; + } + + msleep (time - twait); + stopsound (); + msleep (twait); +} + +char * +delaynote (char *c) +{ + unsigned time = 0; + + while (isdigit(*c)) time = time * 10 + (*c++ - '0'); + + if (!time) time = length; + + time = (unsigned)(240000 / time / tempo); + + while (*c == '.') { time = time * 3 / 2; c++; } + + delaytime (time); + + return c; +} + +void +initspeaker (void) +{ + tempo = 120, length = 4, octave = 3, mtype = 'N'; +} + + +void +play (char *tune) +{ + char *c, *n; + unsigned num; + + for (c = tune; *c; ) { + sscanf (c + 1, "%u", &num); + for (n = c + 1; isdigit(*n); n++) /* do nothing */; + if (isspace(*c)) c++; + else switch (toupper(*c)) { + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + c = startnote (c); + case 'P': + c = delaynote (++c); + break; +#if 0 + case 'M': c++; mtype = *c; c++; break; + case 'T': + if (num) tempo = num; + else printf ("Zero Tempo (%s)!\n", c); + c = n; + break; + case 'L': + if (num) length = num; + else printf ("Zero Length (%s)!\n", c); + c = n; + break; + case 'O': + if (num <= 7) + octave = num; + c = n; + break; + case 'N': + note (num); + delaytime ((240000/length/tempo)); + c = n; + break; + case '>': if (octave < 7) octave++; c++; break; + case '<': if (octave) octave--; c++; break; +#endif + case ' ': c++; break; + default: + printf ("Unrecognized play value (%s)!\n", c); + return; + } + } +} + +#ifndef TESTING +void +pc_speaker (struct obj *instr, char *tune) +{ + if (!iflags.usepcspeaker) return; + initspeaker (); + switch (instr->otyp) + { + case WOODEN_FLUTE: + case MAGIC_FLUTE: + octave = 5; /* up one octave */ + break; + case TOOLED_HORN: + case FROST_HORN: + case FIRE_HORN: + octave = 2; /* drop two octaves */ + break; + case BUGLE: + break; + case WOODEN_HARP: + case MAGIC_HARP: + length = 8; + mtype = 'L'; /* fast, legato */ + break; + } + play (tune); +} + +#else + +main () +{ + char s[80]; + int tool; + + initspeaker(); + printf ("1) flute\n2) horn\n3) harp\n4) other\n"); + fgets (s, 80, stdin); + sscanf (s, "%d", &tool); + switch (tool) { + case 1: octave = 5; break; + case 2: octave = 2; break; + case 3: length = 8; mtype = 'L'; break; + default: break; + } + printf ("Enter tune:"); + fgets(s, 80, stdin); + play (s); +} +#endif + +#endif /* PCMUSIC */ + +/* sound.c */ diff --git a/sys/msdos/tile2bin.c b/sys/msdos/tile2bin.c new file mode 100644 index 0000000..ee4e894 --- /dev/null +++ b/sys/msdos/tile2bin.c @@ -0,0 +1,312 @@ +/* SCCS Id: @(#)tile2bin.c 3.4 1995/01/26 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994, 1995 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Edit History: + * + * Initial Creation M.Allison 93/10/21 + * ifndef MONITOR_HEAP for heaputil.c P.Winner 94/03/12 + * added Borland C _stklen variable Y.Sapir 94/05/01 + * fixed to use text tiles from win/share M.Allison 95/01/31 + * + */ + + +#include "hack.h" +#include "pcvideo.h" +#include "tile.h" +#include "pctiles.h" + +#include +#ifndef MONITOR_HEAP +#include +#endif +#include + +#ifdef __GO32__ +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 700 +#pragma warning(disable:4309) /* initializing */ +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4131) /* old style declarator */ +#pragma warning(disable:4127) /* conditional express. is constant */ +#endif + +#ifdef __BORLANDC__ +extern unsigned _stklen = STKSIZ; +#endif + +extern char *FDECL(tilename, (int, int)); + +#ifdef PLANAR_FILE +char masktable[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; +char charcolors[MAXCOLORMAPSIZE]; +#ifdef OVERVIEW_FILE +struct overview_planar_cell_struct planetile; +#else +struct planar_cell_struct planetile; +#endif +FILE *tibfile1; +#endif + +#ifdef PACKED_FILE +char packtile[TILE_Y][TILE_X]; +FILE *tibfile2; +#endif + +int num_colors; +pixel pixels[TILE_Y][TILE_X]; +struct tibhdr_struct tibheader; + +static void FDECL(write_tibtile, (int)); +static void FDECL(write_tibheader, (FILE *, struct tibhdr_struct *)); +static void FDECL(build_tibtile, (pixel (*)[TILE_X])); + +#ifndef OVERVIEW_FILE +char *tilefiles[] = { "../win/share/monsters.txt", + "../win/share/objects.txt", + "../win/share/other.txt"}; +#else +char *tilefiles[] = { "../win/share/monthin.txt", + "../win/share/objthin.txt", + "../win/share/oththin.txt"}; +#endif + +int tilecount; +int filenum; +int paletteflag; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int i; + struct tm *newtime; + time_t aclock; + char *paletteptr; + + if (argc != 1) { + Fprintf(stderr, "usage: tile2bin (from the util directory)\n"); + exit(EXIT_FAILURE); + } + +#ifdef PLANAR_FILE +# ifndef OVERVIEW_FILE + tibfile1 = fopen(NETHACK_PLANAR_TILEFILE, WRBMODE); +# else + tibfile1 = fopen(NETHACK_OVERVIEW_TILEFILE, WRBMODE); +# endif + if (tibfile1 == (FILE *)0) { + Fprintf(stderr, "Unable to open output file %s\n", +# ifndef OVERVIEW_FILE + NETHACK_PLANAR_TILEFILE); +#else + NETHACK_OVERVIEW_TILEFILE); +#endif + exit(EXIT_FAILURE); + } +#endif + +#ifdef PACKED_FILE + tibfile2 = fopen(NETHACK_PACKED_TILEFILE, WRBMODE); + if (tibfile2 == (FILE *)0) { + Fprintf(stderr, "Unable to open output file %s\n", + NETHACK_PACKED_TILEFILE); + exit(EXIT_FAILURE); + } +#endif + time(&aclock); + newtime = localtime(&aclock); + + tilecount = 0; + paletteflag = 0; + filenum = 0; + while (filenum < 3) { + if (!fopen_text_file(tilefiles[filenum], RDTMODE)) { + Fprintf(stderr, + "usage: tile2bin (from the util or src directory)\n"); + exit(EXIT_FAILURE); + } + num_colors = colorsinmap; + if (num_colors > 62) { + Fprintf(stderr, "too many colors (%d)\n", num_colors); + exit(EXIT_FAILURE); + } + + if (!paletteflag) { + paletteptr = tibheader.palette; + for (i = 0; i < num_colors; i++) { + *paletteptr++ = ColorMap[CM_RED][i], + *paletteptr++ = ColorMap[CM_GREEN][i], + *paletteptr++ = ColorMap[CM_BLUE][i]; + } + paletteflag++; + } + + + while (read_text_tile(pixels)) { + build_tibtile(pixels); + write_tibtile(tilecount); + tilecount++; + } + + (void) fclose_text_file(); + ++filenum; + } + +# if defined(_MSC_VER) + tibheader.compiler = MSC_COMP; +# elif defined(__BORLANDC__) + tibheader.compiler = BC_COMP; +# elif defined(__GO32__) + tibheader.compiler = DJGPP_COMP; +# else + tibheader.compiler = OTHER_COMP; +# endif + + strncpy(tibheader.ident, + "NetHack 3.4 MSDOS Port binary tile file", 80); + strncpy(tibheader.timestamp, asctime(newtime), 24); + tibheader.timestamp[25] = '\0'; + tibheader.tilecount = tilecount; + tibheader.numcolors = num_colors; +# ifdef PLANAR_FILE + tibheader.tilestyle = PLANAR_STYLE; + write_tibheader(tibfile1, &tibheader); + (void) fclose(tibfile1); +# ifndef OVERVIEW_FILE + Fprintf(stderr, "Total of %d planar tiles written to %s.\n", + tilecount, NETHACK_PLANAR_TILEFILE); +# else + Fprintf(stderr, "Total of %d planar tiles written to %s.\n", + tilecount, NETHACK_OVERVIEW_TILEFILE); +# endif +# endif + +# ifdef PACKED_FILE + tibheader.tilestyle = PACKED_STYLE; + write_tibheader(tibfile2, &tibheader); + Fprintf(stderr, "Total of %d packed tiles written to %s.\n", + tilecount, NETHACK_PACKED_TILEFILE); + (void) fclose(tibfile2); +# endif + + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + + +static void +write_tibheader(fileptr,tibhdr) +FILE *fileptr; +struct tibhdr_struct *tibhdr; +{ + + if (fseek(fileptr,0L,SEEK_SET)) { + Fprintf(stderr, "Error writing header to tile file\n"); + } + fwrite(tibhdr, sizeof(struct tibhdr_struct), 1, fileptr); +} + +static void +build_tibtile(pixels) +pixel (*pixels)[TILE_X]; +{ + int i, j, k, co_off; + unsigned char co_mask,tmp; + +#ifndef OVERVIEW_FILE + memset((void *)&planetile,0,sizeof(struct planar_cell_struct)); +#else + memset((void *)&planetile,0, + sizeof(struct overview_planar_cell_struct)); +#endif + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + for (k = 0; k < num_colors; k++) { + if (ColorMap[CM_RED][k] == pixels[j][i].r && + ColorMap[CM_GREEN][k] == pixels[j][i].g && + ColorMap[CM_BLUE][k] == pixels[j][i].b) + break; + } + if (k >= num_colors) + Fprintf(stderr, "color not in colormap!\n"); +#ifdef PACKED_FILE + packtile[j][i] = k; +#endif + +#ifdef PLANAR_FILE + if (i > 7) { + co_off = 1; + co_mask = masktable[i - 8]; + } else { + co_off = 0; + co_mask = masktable[i]; + } + + tmp = planetile.plane[0].image[j][co_off]; + planetile.plane[0].image[j][co_off] = (k & 0x0008) ? + (tmp | co_mask) : + (tmp & ~co_mask); + + tmp = planetile.plane[1].image[j][co_off]; + planetile.plane[1].image[j][co_off] = (k & 0x0004) ? + (tmp | co_mask) : + (tmp & ~co_mask); + + tmp = planetile.plane[2].image[j][co_off]; + planetile.plane[2].image[j][co_off] = (k & 0x0002) ? + (tmp | co_mask) : + (tmp & ~co_mask); + + tmp = planetile.plane[3].image[j][co_off]; + planetile.plane[3].image[j][co_off] = (k & 0x0001) ? + (tmp | co_mask) : + (tmp & ~co_mask); +#endif /* PLANAR_FILE */ + } + } +} + +static void +write_tibtile(recnum) +int recnum; +{ + long fpos; + +#ifdef PLANAR_FILE +# ifndef OVERVIEW_FILE + fpos = ((long)(recnum) * (long)sizeof(struct planar_cell_struct)) + + (long)TIBHEADER_SIZE; +# else + fpos = ((long)(recnum) * + (long)sizeof(struct overview_planar_cell_struct)) + + (long)TIBHEADER_SIZE; +# endif + if (fseek(tibfile1,fpos,SEEK_SET)) { + Fprintf(stderr, "Error seeking before planar tile write %d\n", + recnum); + } +# ifndef OVERVIEW_FILE + fwrite(&planetile, sizeof(struct planar_cell_struct), 1, tibfile1); +# else + fwrite(&planetile, + sizeof(struct overview_planar_cell_struct), 1, tibfile1); +# endif +#endif + +#ifdef PACKED_FILE + fpos = ((long)(recnum) * (long)sizeof(packtile)) + + (long)TIBHEADER_SIZE; + if (fseek(tibfile2,fpos,SEEK_SET)) { + Fprintf(stderr, "Error seeking before packed tile write %d\n", + recnum); + } + fwrite(&packtile, sizeof(packtile), 1, tibfile2); +#endif +} diff --git a/sys/msdos/video.c b/sys/msdos/video.c new file mode 100644 index 0000000..48460bb --- /dev/null +++ b/sys/msdos/video.c @@ -0,0 +1,969 @@ +/* SCCS Id: @(#)video.c 3.4 2001/04/07 */ +/* Copyright (c) NetHack PC Development Team 1993, 1994, 2001 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * video.c - Hardware video support front-ends + * + *Edit History: + * Initial Creation M. Allison 1993/04/04 + * Add djgpp support K. Smolkowski 1993/04/26 + * Add txt/graphics mode support M. Allison 1993/10/30 + * Add graphics mode cursor sim. M. Allison 1994/02/19 + * Add hooks for decals on vga M. Allison 2001/04/07 + */ + +#include "hack.h" + +#ifndef STUBVIDEO +#include "pcvideo.h" +#include "pctiles.h" + +#if defined(_MSC_VER) +# if _MSC_VER >= 700 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4127) /* conditional expression is constant */ +#pragma warning(disable:4131) /* old style declarator */ +#pragma warning(disable:4305) /* prevents complaints with MK_FP */ +#pragma warning(disable:4309) /* initializing */ +#pragma warning(disable:4759) /* prevents complaints with MK_FP */ +# endif +#endif +/*========================================================================= + * General PC Video routines. + * + * The following routines are the video interfacing functions. + * In general these make calls to more hardware specific + * routines in other source files. + * + * Assumptions (94/04/23): + * + * - Supported defaults.nh file video options: + * + * If OPTIONS=video:autodetect is defined in defaults.nh then + * check for a VGA video adapter. If one is detected, then + * use the VGA code, otherwise resort to using the 'standard' + * video BIOS routines. + * + * If OPTIONS=video:vga is defined in defaults.nh, then use + * the VGA code. + * + * If OPTIONS=video:default is defined in defaults.nh use the + * 'standard' video BIOS routines (in the overlaid version), + * or DJGPPFAST routines (under djgpp). This is equivalent to + * having no OPTIONS=video:xxxx entry at all. + * + * Notes (94/04/23): + * + * - The handler for defaults.nh file entry: + * + * OPTIONS=video:xxxxx + * + * has now been added. The handler is in video.c and is called + * from options.c. + * + * - Handling of videocolors and videoshades are now done with + * OPTIONS= statements. The new syntax separates the colour + * values with dashes ('-') rather than spaces (' '). + * + * To Do (94/04/23): + * + * + *========================================================================= + */ + + +#ifdef OVLB +void +get_scr_size() +{ +# ifdef SCREEN_VGA + if (iflags.usevga) { + vga_get_scr_size(); + } else +# endif + txt_get_scr_size(); +} +#endif /*OVLB*/ + +/* + * -------------------------------------------------------------- + * The rest of this file is only compiled if NO_TERMS is defined. + * -------------------------------------------------------------- + */ + +#ifdef NO_TERMS + +#include +#include "wintty.h" + +# ifdef __GO32__ +#include +#include +#if !(__DJGPP__ >= 2) +typedef long clock_t; +#endif +# endif + +# ifdef __BORLANDC__ +#include /* needed for delay() */ +# endif + +# ifdef SCREEN_DJGPPFAST /* parts of this block may be unecessary now */ +#define get_cursor(x,y) ScreenGetCursor(y,x) +# endif + +# ifdef SCREEN_BIOS +void FDECL(get_cursor, (int *, int *)); +# endif + +void FDECL(adjust_cursor_flags, (struct WinDesc *)); +void FDECL(cmov, (int, int)); +void FDECL(nocmov, (int, int)); +STATIC_DCL void NDECL(init_ttycolor); + +# ifdef OVLB +int savevmode; /* store the original video mode in here */ +int curcol,currow; /* graphics mode current cursor locations */ +int g_attribute; /* Current attribute to use */ +int monoflag; /* 0 = not monochrome, else monochrome */ +int attrib_text_normal; /* text mode normal attribute */ +int attrib_gr_normal; /* graphics mode normal attribute */ +int attrib_text_intense; /* text mode intense attribute */ +int attrib_gr_intense; /* graphics mode intense attribute */ +boolean traditional = FALSE; /* traditonal TTY character mode */ +boolean inmap = FALSE; /* in the map window */ +# ifdef TEXTCOLOR +char ttycolors[CLR_MAX]; /* also used/set in options.c */ +# endif /* TEXTCOLOR */ +# else +extern int savevmode; +extern int curcol,currow; +extern int g_attribute; +extern int monoflag; +extern int attrib_text_normal; +extern int attrib_gr_normal; +extern int attrib_text_intense; +extern int attrib_gr_intense; +extern boolean traditonal; +extern boolean inmap; +# ifdef TEXTCOLOR +extern char ttycolors[CLR_MAX]; /* also used/set in options.c */ +# endif /* TEXTCOLOR */ +# endif /* OVLB */ + +# ifdef OVLB +void +backsp() +{ + if (!iflags.grmode) { + txt_backsp(); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_backsp(); +# endif + } +} +# endif /* OVLB */ + +# ifdef OVL0 +void +clear_screen() +{ + if (!iflags.grmode) { + txt_clear_screen(); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_clear_screen(BACKGROUND_VGA_COLOR); +# endif + } +} + +void +cl_end() /* clear to end of line */ +{ + int col,row; + + col = (int)ttyDisplay->curx; + row = (int)ttyDisplay->cury; + if (!iflags.grmode) { + txt_cl_end(col,row); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_cl_end(col,row); +# endif + } + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, + (int)ttyDisplay->cury); +} + +void +cl_eos() /* clear to end of screen */ +{ + int cy = (int)ttyDisplay->cury+1; + + if (!iflags.grmode) { + txt_cl_eos(); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_cl_eos(cy); +# endif + } + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, + (int)ttyDisplay->cury); +} + +void +cmov(col, row) +register int col, row; +{ + ttyDisplay->cury = (uchar)row; + ttyDisplay->curx = (uchar)col; + if (!iflags.grmode) { + txt_gotoxy(col,row); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_gotoloc(col,row); +# endif + } +} +# endif /* OVL0 */ + +# ifdef OVLB +int +has_color(int color) +{ + ++color; /* prevents compiler warning (unref. param) */ +# ifdef TEXTCOLOR + return (monoflag) ? 0 : 1; +# else + return 0; +# endif +} +# endif /* OVLB */ + +# ifdef OVL0 +void +home() +{ + tty_curs(BASE_WINDOW, 1, 0); + ttyDisplay->curx = ttyDisplay->cury = (uchar)0; + if (!iflags.grmode) { + txt_gotoxy(0,0); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_gotoloc(0,0); +# endif + } +} + +void +nocmov(col, row) +int col,row; +{ + if (!iflags.grmode) { + txt_gotoxy(col,row); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_gotoloc(col,row); +# endif + } +} + +void +standoutbeg() +{ + g_attribute = iflags.grmode ? attrib_gr_intense + : attrib_text_intense; +} + +void +standoutend() +{ + g_attribute = iflags.grmode ? attrib_gr_normal + : attrib_text_normal; +} +# endif /* OVL0 */ + + +# ifdef OVLB +void +term_end_attr(int attr) +{ + switch(attr) { + case ATR_ULINE: + case ATR_BOLD: + case ATR_BLINK: + case ATR_INVERSE: + default: + g_attribute = iflags.grmode ? attrib_gr_normal + : attrib_text_normal; + } +} + +void +term_end_color(void) +{ + g_attribute = iflags.grmode ? attrib_gr_normal + : attrib_text_normal; +} + +void +term_end_raw_bold(void) +{ + standoutend(); +} + + +void +term_start_attr(int attr) +{ + switch(attr){ + + case ATR_ULINE: + if (monoflag) { + g_attribute = ATTRIB_MONO_UNDERLINE; + } else { + g_attribute = iflags.grmode ? attrib_gr_intense + : attrib_text_intense; + } + break; + case ATR_BOLD: + g_attribute = iflags.grmode ? attrib_gr_intense + : attrib_text_intense; + break; + case ATR_BLINK: + if (monoflag) { + g_attribute = ATTRIB_MONO_BLINK; + } else { + g_attribute = iflags.grmode ? attrib_gr_intense + : attrib_text_intense; + } + break; + case ATR_INVERSE: + if (monoflag) { + g_attribute = ATTRIB_MONO_REVERSE; + } else { + g_attribute = iflags.grmode ? attrib_gr_intense + : attrib_text_intense; + } + break; + default: + g_attribute = iflags.grmode ? attrib_gr_normal + : attrib_text_normal; + break; + } +} + + +void +term_start_color(int color) +{ +# ifdef TEXTCOLOR + if (monoflag) { + g_attribute = attrib_text_normal; + } else { + if (color >= 0 && color < CLR_MAX) { + if (iflags.grmode) + g_attribute = color; + else + g_attribute = ttycolors[color]; + } + } +# endif +} + +void +term_start_raw_bold(void) +{ + standoutbeg(); +} +# endif /* OVLB */ + +# ifdef OVL0 +void +tty_delay_output() +{ +#ifdef TIMED_DELAY + if (flags.nap) { + (void) fflush(stdout); + msleep(50); /* sleep for 50 milliseconds */ + return; + } +#endif +} + +# endif /* OVL0 */ + +# ifdef OVLB +void +tty_end_screen() +{ + + if (!iflags.grmode) { + txt_clear_screen(); +# ifdef PC9800 + fputs("\033[>1l", stdout); +# endif +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_tty_end_screen(); +# endif + } +} + +void +tty_nhbell() +{ + txt_nhbell(); +} + + +void +tty_number_pad(state) +int state; +{ + ++state; /* prevents compiler warning (unref. param) */ +} + +void +tty_startup(wid, hgt) +int *wid, *hgt; +{ + + /* code to sense display adapter is required here - MJA */ + + attrib_text_normal = ATTRIB_NORMAL; + attrib_text_intense = ATTRIB_INTENSE; + + /* These are defaults and may get overridden */ + attrib_gr_normal = attrib_text_normal; + attrib_gr_intense = attrib_text_intense; + g_attribute = attrib_text_normal; /* Give it a starting value */ + +# ifdef SCREEN_VGA + if (iflags.usevga) { + vga_tty_startup(wid, hgt); + } else +# endif + txt_startup(wid, hgt); + + *wid = CO; + *hgt = LI; + +# ifdef CLIPPING + if (CO < COLNO || LI < ROWNO+3) setclipped(); +# endif + +# ifdef TEXTCOLOR + init_ttycolor(); +# endif + +# ifdef MONO_CHECK + monoflag = txt_monoadapt_check(); +# else + monoflag = 0; +# endif + +} + +void +tty_start_screen() +{ +# ifdef PC9800 + fputs("\033[>1h", stdout); +# endif + if (iflags.num_pad) tty_number_pad(1); /* make keypad send digits */ +} + +void +gr_init(){ + if (iflags.usevga) { +# ifdef SCREEN_VGA + vga_Init(); +# endif +# ifdef SCREEN_VESA + } else if (iflags.usevesa) { + vesa_Init(); + +# endif +# ifdef SCREEN_8514 + } else if (iflags.use8514) { + v8514_Init(); +# endif + } +} + +void +gr_finish() +{ + if (iflags.grmode) { + if (iflags.usevga) { +# ifdef SCREEN_VGA + vga_Finish(); +# endif +# ifdef SCREEN_VESA + } else if (iflags.usevesa) { + vesa_Finish(); +# endif +# ifdef SCREEN_8514 + } else if (iflags.use8514) { + v8514_Finish(); +# endif + } + } +} + +# endif /* OVLB */ + +/* + * Screen output routines (these are heavily used). + * + * These are the 3 routines used to place information on the screen + * in the NO_TERMS PC tty port of NetHack. These are the routines + * that get called by routines in other NetHack source files (such + * as those in win/tty). + * + * xputs - Writes a c null terminated string at the current location. + * Depending on compile options, this could just be a series + * of repeated calls to xputc() for each character. + * + * xputc - Writes a single character at the current location. Since + * various places in the code assume that control characters + * can be used to control, we are forced to interpret some of + * the more common ones, in order to keep things looking correct. + * + * xputg - If using a graphics mode display mechanism (such as VGA, this + * routine is used to display a graphical representation of a + * NetHack glyph at the current location. For more information on + * NetHack glyphs refer to the comments in include/display.h. + * + * NOTES: + * wintty.h uses macros to redefine common output functions + * such as puts, putc, putchar, so that they get steered into + * either xputs (for strings) or xputc (for single characters). + * References to puts, putc, and putchar in other source files + * (that include wintty.h) are actually using these routines. + */ + +# ifdef OVL0 +void +xputs(s) +const char *s; +{ + int col,row; + + col = (int)ttyDisplay->curx; + row = (int)ttyDisplay->cury; + + if (!iflags.grmode) { + txt_xputs(s,col,row); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_xputs(s,col,row); +# endif + } +} + +void +xputc(ch) /* write out character (and attribute) */ +char ch; +{ + int i; + char attribute; + + i = iflags.grmode ? attrib_gr_normal + : attrib_text_normal; + + attribute = (char)((g_attribute == 0) ? i : g_attribute); + if (!iflags.grmode) { + txt_xputc(ch,attribute); +# ifdef SCREEN_VGA + } else if (iflags.usevga) { + vga_xputc(ch,attribute); +# endif /*SCREEN_VGA*/ + } +} + +void +xputg(glyphnum,ch,special) /* write out a glyph picture at current location */ +int glyphnum; +int ch; +unsigned special; +{ + if (!iflags.grmode || !iflags.tile_view) { + xputc((char)ch); +# ifdef SCREEN_VGA + } else { + vga_xputg(glyphnum, ch, special); +# endif + } +} + +# ifdef POSITIONBAR +void +video_update_positionbar(posbar) +char *posbar; +{ + if (!iflags.grmode) + return; +# ifdef SCREEN_VGA + else + vga_update_positionbar(posbar); +# endif +} +# endif + +void +adjust_cursor_flags(cw) +struct WinDesc *cw; +{ +# ifdef SIMULATE_CURSOR +# if 0 + if (cw->type == NHW_MAP) cursor_flag = 1; + else cursor_flag = 0; +# else + if (cw->type == NHW_MAP) { + inmap = 1; + cursor_flag = 1; + } else { + inmap = 0; + cursor_flag = 1; + } +# endif /* 0 */ +# endif /* SIMULATE_CURSOR */ +} + +# ifdef SIMULATE_CURSOR + +/* change the defaults in pcvideo.h, not here */ +int cursor_type = CURSOR_DEFAULT_STYLE; +int cursor_color = CURSOR_DEFAULT_COLOR; +int cursor_flag; + +/* The check for iflags.grmode is made BEFORE calling these. */ +void +DrawCursor() +{ +# ifdef SCREEN_VGA + vga_DrawCursor(); +# endif +} + +void +HideCursor() +{ +# ifdef SCREEN_VGA + vga_HideCursor(); +# endif +} + +# endif /* SIMULATE_CURSOR */ +# endif /* OVL0 */ + +# ifdef TEXTCOLOR +/* + * CLR_BLACK 0 + * CLR_RED 1 + * CLR_GREEN 2 + * CLR_BROWN 3 low-intensity yellow + * CLR_BLUE 4 + * CLR_MAGENTA 5 + * CLR_CYAN 6 + * CLR_GRAY 7 low-intensity white + * NO_COLOR 8 + * CLR_ORANGE 9 + * CLR_BRIGHT_GREEN 10 + * CLR_YELLOW 11 + * CLR_BRIGHT_BLUE 12 + * CLR_BRIGHT_MAGENTA 13 + * CLR_BRIGHT_CYAN 14 + * CLR_WHITE 15 + * CLR_MAX 16 + * BRIGHT 8 + */ + +# ifdef VIDEOSHADES +/* assign_videoshades() is prototyped in extern.h */ +/* assign_videocolors() is prototyped in extern.h */ +/* assign_video() is prototyped in extern.h */ + +# ifdef OVLB +int shadeflag; /* shades are initialized */ +int colorflag; /* colors are initialized */ +char *schoice[3] = {"dark","normal","light"}; +char *shade[3]; +# else +extern int shadeflag; +extern int colorflag; +extern char *schoice[3]; +extern char *shade[3]; +# endif /* OVLB */ + +# endif /* VIDEOSHADES */ + +# ifdef OVLB +STATIC_OVL void +init_ttycolor() +{ +# ifdef VIDEOSHADES + if (!shadeflag) { + ttycolors[CLR_BLACK] = M_BLACK; /* 8 = dark gray */ + ttycolors[CLR_WHITE] = M_WHITE; /* 15 = bright white */ + ttycolors[CLR_GRAY] = M_GRAY; /* 7 = normal white */ + shade[0] = schoice[0]; + shade[1] = schoice[1]; + shade[2] = schoice[2]; + } +# else + ttycolors[CLR_BLACK] = M_GRAY; /* mapped to white */ + ttycolors[CLR_WHITE] = M_GRAY; /* mapped to white */ + ttycolors[CLR_GRAY] = M_GRAY; /* mapped to white */ +# endif + +# ifdef VIDEOSHADES + if (!colorflag) { +# endif + ttycolors[CLR_RED] = M_RED; + ttycolors[CLR_GREEN] = M_GREEN; + ttycolors[CLR_BROWN] = M_BROWN; + ttycolors[CLR_BLUE] = M_BLUE; + ttycolors[CLR_MAGENTA] = M_MAGENTA; + ttycolors[CLR_CYAN] = M_CYAN; + ttycolors[BRIGHT] = M_WHITE; + ttycolors[CLR_ORANGE] = M_ORANGE; + ttycolors[CLR_BRIGHT_GREEN] = M_BRIGHTGREEN; + ttycolors[CLR_YELLOW] = M_YELLOW; + ttycolors[CLR_BRIGHT_BLUE] = M_BRIGHTBLUE; + ttycolors[CLR_BRIGHT_MAGENTA] = M_BRIGHTMAGENTA; + ttycolors[CLR_BRIGHT_CYAN] = M_BRIGHTCYAN; +# ifdef VIDEOSHADES + } +# endif +} +# endif /* OVLB */ + +# ifdef OVL1 + static int FDECL(convert_uchars,(char *, uchar *, int)); +# ifdef VIDEOSHADES +int assign_videoshades(char *choiceptr) +{ + char choices[120]; + char *cptr, *cvalue[3]; + int i,icolor = CLR_WHITE; + + strcpy(choices,choiceptr); + cvalue[0] = choices; + + /* find the next ' ' or tab */ + cptr = index(cvalue[0], '-'); + if (!cptr) cptr = index(cvalue[0], ' '); + if (!cptr) cptr = index(cvalue[0], '\t'); + if (!cptr) return 0; + *cptr = '\0'; + /* skip whitespace between '=' and value */ + do { ++cptr; } while (isspace(*cptr) || (*cptr == '-')); + cvalue[1] = cptr; + + cptr = index(cvalue[1], '-'); + if (!cptr) cptr = index(cvalue[0], ' '); + if (!cptr) cptr = index(cvalue[0], '\t'); + if (!cptr) return 0; + *cptr = '\0'; + do { ++cptr; } while (isspace(*cptr) || (*cptr == '-')); + cvalue[2] = cptr; + + for (i=0; i < 3; ++i) { + switch(i) { + case 0: icolor = CLR_BLACK; + break; + case 1: icolor = CLR_GRAY; + break; + case 2: icolor = CLR_WHITE; + break; + } + + shadeflag = 1; + if ((strncmpi(cvalue[i],"black",5) == 0) || + (strncmpi(cvalue[i],"dark",4) == 0)) { + shade[i] = schoice[0]; + ttycolors[icolor] = M_BLACK; /* dark gray */ + } else if ((strncmpi(cvalue[i],"gray",4) == 0) || + (strncmpi(cvalue[i],"grey",4) == 0) || + (strncmpi(cvalue[i],"medium",6) == 0) || + (strncmpi(cvalue[i],"normal",6) == 0)) { + shade[i] = schoice[1]; + ttycolors[icolor] = M_GRAY; /* regular gray */ + } else if ((strncmpi(cvalue[i],"white",5) == 0) || + (strncmpi(cvalue[i],"light",5) == 0)) { + shade[i] = schoice[2]; + ttycolors[icolor] = M_WHITE; /* bright white */ + } else { + shadeflag = 0; + return 0; + } + } + return 1; +} + +/* + * Process defaults.nh OPTIONS=videocolors:xxx + * Left to right assignments for: + * red green brown blue magenta cyan orange br.green yellow + * br.blue br.mag br.cyan + * + * Default Mapping (BIOS): 4-2-6-1-5-3-12-10-14-9-13-11 + */ +int assign_videocolors(char *colorvals) +{ + int i,icolor; + uchar *tmpcolor; + + init_ttycolor(); /* in case defaults.nh entry wasn't complete */ + i = strlen(colorvals); + tmpcolor = (uchar *)alloc(i); + (void)convert_uchars(colorvals,tmpcolor,i); + icolor = CLR_RED; + for( i = 0; tmpcolor[i] != 0; ++i) { + if (icolor < (CLR_WHITE)) { + ttycolors[icolor++] = tmpcolor[i]; + if ((icolor > CLR_CYAN) && (icolor < CLR_ORANGE)) { + icolor = CLR_ORANGE; + } + } + } + colorflag = 1; + free((genericptr_t)tmpcolor); + return 1; +} + +static int +convert_uchars(bufp,list,size) + char *bufp; /* current pointer */ + uchar *list; /* return list */ + int size; +{ + unsigned int num = 0; + int count = 0; + + while (1) { + switch(*bufp) { + case ' ': case '\0': + case '\t': case '-': + case '\n': + if (num) { + list[count++] = num; + num = 0; + } + if ((count==size) || !*bufp) return count; + bufp++; + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + num = num*10 + (*bufp-'0'); + bufp++; + break; + return count; + } + } + /*NOTREACHED*/ +} + +# endif /* VIDEOSHADES */ +# endif /* OVL1 */ +# endif /* TEXTCOLOR */ + +/* + * Process defaults.nh OPTIONS=video:xxxx + * + * where (current) legitimate values are: + * + * autodetect (attempt to determine the adapter type) + * default (force use of the default video method for environment) + * vga (use vga adapter code) + */ +# ifdef OVL1 +int +assign_video(sopt) +char *sopt; +{ + +/* + * debug + * + * printf("video is %s",sopt); + * getch(); + */ + iflags.grmode = 0; + iflags.hasvga = 0; + iflags.usevga = 0; + + if (strncmpi(sopt,"def",3) == 0) { /* default */ + /* do nothing - default */ +# ifdef SCREEN_VGA + } else if (strncmpi(sopt,"vga",3) == 0) { /* vga */ + iflags.usevga = 1; + iflags.hasvga = 1; +# endif +# ifdef SCREEN_VESA + } else if (strncmpi(sopt,"vesa",4) == 0) { /* vesa */ + iflags.hasvesa = 1; + iflags.usevesa = 1; +# endif +# ifdef SCREEN_8514 + } else if (strncmpi(sopt,"8514",4) == 0) { /* 8514/A */ + iflags.use8514 = 1; + iflags.has8514 = 1; +# endif + } else if (strncmpi(sopt,"auto",4) == 0) { /* autodetect */ +# ifdef SCREEN_VESA + if (vesa_detect()) { + iflags.hasvesa = 1; + } +# endif +# ifdef SCREEN_8514 + if (v8514_detect()) { + iflags.has8514 = 1; + } +# endif +# ifdef SCREEN_VGA + if (vga_detect()) { + iflags.hasvga = 1; + } +# endif + /* + * Auto-detect Priorities (arbitrary for now): + * VGA + */ + if (iflags.hasvga) { + iflags.usevga = 1; + /* VGA depends on BIOS to enable function keys*/ + iflags.BIOS = 1; + iflags.rawio = 1; + } + } else { + return 0; + } + return 1; +} +# endif /* OVL1 */ +# ifdef OVL0 + +void tileview(enable) +boolean enable; +{ +#ifdef SCREEN_VGA + if (iflags.grmode) vga_traditional(enable ? FALSE : TRUE); +#endif +} +# endif /* OVL0 */ +#endif /* NO_TERMS */ +#else /* STUBVIDEO */ +void tileview(enable) +boolean enable; +{ +} +#endif /* STUBVIDEO */ + diff --git a/sys/msdos/vidtxt.c b/sys/msdos/vidtxt.c new file mode 100644 index 0000000..692659e --- /dev/null +++ b/sys/msdos/vidtxt.c @@ -0,0 +1,472 @@ +/* SCCS Id: @(#)vidtxt.c 3.4 1994/04/04 */ +/* Copyright (c) NetHack PC Development Team 1993 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * vidtxt.c - Textmode video hardware support (BIOS and DJGPPFAST) + * + *Edit History: + * Initial Creation M. Allison 93/04/04 + * Add djgpp support K. Smolkowski 93/04/26 + * Add runtime monoadapter check M. Allison 93/05/09 + */ + +#define VIDEO_TEXT + +#include "hack.h" +#include "pcvideo.h" +#include "wintty.h" + +#include +#include + +#if defined(_MSC_VER) +# if _MSC_VER >= 700 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4127) /* conditional expression is constant */ +#pragma warning(disable:4131) /* old style declarator */ +#pragma warning(disable:4305) /* prevents complaints with MK_FP */ +#pragma warning(disable:4309) /* initializing */ +#pragma warning(disable:4759) /* prevents complaints with MK_FP */ +# endif +#endif + +/* void FDECL(txt_xputc,(char, int)); */ /* write out character (and attribute) */ + +extern int attrib_text_normal; /* text mode normal attribute */ +extern int attrib_gr_normal; /* graphics mode normal attribute */ +extern int attrib_text_intense; /* text mode intense attribute */ +extern int attrib_gr_intense; /* graphics mode intense attribute */ + +#ifdef OVLB + +void +txt_get_scr_size() +{ + union REGS regs; + + if (!iflags.BIOS) { + CO = 80; + LI = 24; + return; + } + +# ifdef PC9800 + regs.h.ah = SENSEMODE; + (void) int86(CRT_BIOS, ®s, ®s); + + CO = (regs.h.al & 0x02) ? 40 : 80; + LI = (regs.h.al & 0x01) ? 20 : 25; +# else + regs.x.ax = FONTINFO; + regs.x.bx = 0; /* current ROM BIOS font */ + regs.h.dl = 24; /* default row count */ + /* in case no EGA/MCGA/VGA */ + (void) int86(VIDEO_BIOS, ®s, ®s); /* Get Font Information */ + + /* MDA/CGA/PCjr ignore INT 10h, Function 11h, but since we + * cleverly loaded up DL with the default, everything's fine. + * + * Otherwise, DL now contains rows - 1. Also, CX contains the + * points (bytes per character) and ES:BP points to the font + * table. -3. + */ + + regs.h.ah = GETMODE; + (void) int86(VIDEO_BIOS, ®s, ®s); /* Get Video Mode */ + + /* This goes back all the way to the original PC. Completely + * safe. AH contains # of columns, AL contains display mode, + * and BH contains the active display page. + */ + + LI = regs.h.dl + 1; + CO = regs.h.ah; +# endif /* PC9800 */ +} +#endif /*OVLB*/ + +/* + * -------------------------------------------------------------- + * The rest of this file is only compiled if NO_TERMS is defined. + * -------------------------------------------------------------- + */ + +#ifdef NO_TERMS +/* #include "wintty.h" */ + +# ifdef SCREEN_DJGPPFAST +#include +#include +# endif + +void FDECL(txt_gotoxy, (int,int)); + +# if defined(SCREEN_BIOS) && !defined(PC9800) +void FDECL(txt_get_cursor, (int *, int *)); +# endif + +# ifdef SCREEN_DJGPPFAST +#define txt_get_cursor(x,y) ScreenGetCursor(y,x) +# endif + +extern int g_attribute; /* Current attribute to use */ +extern int monoflag; /* 0 = not monochrome, else monochrome */ + +# ifdef OVLB +void +txt_backsp() +{ +# ifdef PC9800 + union REGS regs; + + regs.h.dl = 0x01; /* one column */ + regs.h.ah = CURSOR_LEFT; + regs.h.cl = DIRECT_CON_IO; + + int86(DOS_EXT_FUNC, ®s, ®s); + +# else + int col,row; + + txt_get_cursor(&col, &row); + if (col > 0) col = col-1; + txt_gotoxy(col,row); +# endif +} + +void +txt_nhbell() +{ + union REGS regs; + + if (flags.silent) return; + regs.h.dl = 0x07; /* bell */ + regs.h.ah = 0x02; /* Character Output function */ + (void) int86(DOSCALL, ®s, ®s); +} +# endif /* OVLB */ + +# ifdef OVL0 +void +txt_clear_screen() +/* djgpp provides ScreenClear(), but in version 1.09 it is broken + * so for now we just use the BIOS Routines + */ +{ + union REGS regs; +# ifdef PC9800 + regs.h.dl = attr98[attrib_text_normal]; + regs.h.ah = SETATT; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); + + regs.h.dl = 0x02; /* clear whole screen */ + regs.h.ah = SCREEN_CLEAR; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); +# else + regs.h.dl = (char)(CO - 1); /* columns */ + regs.h.dh = (char)(LI - 1); /* rows */ + regs.x.cx = 0; /* CL,CH = x,y of upper left */ + regs.x.ax = 0; + regs.x.bx = 0; + regs.h.bh = (char)attrib_text_normal; + regs.h.ah = (char)SCROLL; + /* DL,DH = x,y of lower rt */ + (void) int86(VIDEO_BIOS, ®s, ®s); /* Scroll or init window */ + txt_gotoxy(0,0); +# endif +} + +void +txt_cl_end(col,row) /* clear to end of line */ +int col,row; +{ + union REGS regs; +# ifndef PC9800 + int count; +# endif + +# ifdef PC9800 + regs.h.dl = attr98[attrib_text_normal]; + regs.h.ah = SETATT; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); + + regs.h.dl = 0x00; /* clear to end of line */ + regs.h.ah = LINE_CLEAR; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); +# else + count = CO - col; + txt_gotoxy(col,row); + regs.h.ah = PUTCHARATT; /* write attribute & character */ + regs.h.al = ' '; /* character */ + regs.h.bh = 0; /* display page */ + /* BL = attribute */ + regs.h.bl = (char)attrib_text_normal; + regs.x.cx = count; + if (count != 0) + (void) int86(VIDEO_BIOS, ®s, ®s); /* write attribute + & character */ +# endif +} + +void +txt_cl_eos() /* clear to end of screen */ +{ + union REGS regs; +# ifndef PC9800 + int col,row; +# endif + +# ifdef PC9800 + regs.h.dl = attr98[attrib_text_normal]; + regs.h.ah = SETATT; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); + + regs.h.dl = 0x00; /* clear to end of screen */ + regs.h.ah = SCREEN_CLEAR; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); +# else + txt_get_cursor(&col, &row); + txt_cl_end(col,row); /* clear to end of line */ + txt_gotoxy(0,(row < (LI-1) ? row+1 : (LI-1))); + regs.h.dl = (char) (CO-1); /* X of lower right */ + regs.h.dh = (char) (LI-1); /* Y of lower right */ + regs.h.cl = 0; /* X of upper left */ + /* Y (row) of upper left */ + regs.h.ch = (char) (row < (LI-1) ? row+1 :(LI-1)); + regs.x.cx = 0; + regs.x.ax = 0; + regs.x.bx = 0; + regs.h.bh = (char)attrib_text_normal; + regs.h.ah = SCROLL; + (void) int86(VIDEO_BIOS, ®s, ®s); /* Scroll or initialize window */ +# endif +} +# endif /* OVL0 */ + +# ifdef OVLB +void +txt_startup(wid, hgt) + int *wid, *hgt; +{ + txt_get_scr_size(); + *wid = CO; + *hgt = LI; + + attrib_gr_normal = attrib_text_normal; + attrib_gr_intense = attrib_text_intense; + g_attribute = attrib_text_normal; /* Give it a starting value */ +} +# endif /* OVLB */ + +/* + * Screen output routines (these are heavily used). + * + * These are the 3 routines used to place information on the screen + * in the NO_TERMS PC tty port of NetHack. These are the routines + * that get called by routines in other NetHack source files (such + * as those in win/tty). + * + * txt_xputs - Writes a c null terminated string at the current location. + * Depending on compile options, this could just be a series + * of repeated calls to xputc() for each character. + * txt_xputc - Writes a single character at the current location. Since + * various places in the code assume that control characters + * can be used to control, we are forced to interpret some of + * the more common ones, in order to keep things looking correct. + * + * NOTES: + * wintty.h uses macros to redefine common output functions + * such as puts, putc, putchar, so that they get steered into + * either xputs (for strings) or xputc (for single characters). + * References to puts, putc, and putchar in other source files + * (that include wintty.h) are actually using these routines. + */ + +# ifdef OVL0 +void +txt_xputs(s,col,row) +const char *s; +int col,row; +{ + char c; + + if (s != (char *)0) { + while (*s != '\0') { + txt_gotoxy(col,row); + c = *s++; + txt_xputc(c,g_attribute); + if (col < (CO-1)) col++; + txt_gotoxy(col,row); + } + } +} + +void +txt_xputc(ch,attr) /* write out character (and attribute) */ +char ch; +int attr; +{ +# ifdef PC9800 + union REGS regs; + + regs.h.dl = attr98[attr]; + regs.h.ah = SETATT; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); + + if (ch == '\n') { + regs.h.dl = '\r'; + regs.h.ah = PUTCHAR; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); + } + regs.h.dl = ch; + regs.h.ah = PUTCHAR; + regs.h.cl = DIRECT_CON_IO; + + (void) int86(DOS_EXT_FUNC, ®s, ®s); +# else +# ifdef SCREEN_BIOS + union REGS regs; +# endif + int col,row; + + txt_get_cursor(&col,&row); + switch(ch) { + case '\n': +#if 0 + col = 0; + ++row; +#endif + break; + default: +# ifdef SCREEN_DJGPPFAST + ScreenPutChar((int)ch,attr,col,row); +# endif +# ifdef SCREEN_BIOS + regs.h.ah = PUTCHARATT; /* write att & character */ + regs.h.al = ch; /* character */ + regs.h.bh = 0; /* display page */ + regs.h.bl = (char)attr; /* BL = attribute */ + regs.x.cx = 1; /* one character */ + (void) int86(VIDEO_BIOS, ®s, ®s); +# endif + if (col < (CO -1 )) ++col; + break; + } /* end switch */ + txt_gotoxy(col,row); +# endif /* PC9800 */ +} +# endif /* OVL0 */ + +/* + * This marks the end of the general screen output routines that are + * called from other places in NetHack. + * --------------------------------------------------------------------- + */ + +/* + * Cursor location manipulation, and location information fetching + * routines. + * These include: + * + * txt_get_cursor(x,y) - Returns the current location of the cursor. In + * some implementations this is implemented as a + * function (BIOS), and in others it is a macro + * (DJGPPFAST). + * + * txt_gotoxy(x,y) - Moves the cursor on screen to the specified x and + * y location. This routine moves the location where + * screen writes will occur next, it does not change + * the location of the player on the NetHack level. + */ + +# ifdef OVL0 +# if defined(SCREEN_BIOS) && !defined(PC9800) +/* + * This is implemented as a macro under DJGPPFAST. + */ +void +txt_get_cursor(x,y) /* get cursor position */ +int *x, *y; +{ + union REGS regs; + + regs.x.dx = 0; + regs.h.ah = GETCURPOS; /* get cursor position */ + regs.x.cx = 0; + regs.x.bx = 0; + (void) int86(VIDEO_BIOS, ®s, ®s); /* Get Cursor Position */ + *x = regs.h.dl; + *y = regs.h.dh; +} +# endif /* SCREEN_BIOS && !PC9800 */ + +void +txt_gotoxy(x,y) +int x,y; +{ +# ifdef SCREEN_BIOS + union REGS regs; + +# ifdef PC9800 + regs.h.dh = (char)y; /* row */ + regs.h.dl = (char)x; /* column */ + regs.h.ah = SETCURPOS; + regs.h.cl = DIRECT_CON_IO; + (void) int86(DOS_EXT_FUNC, ®s, ®s); /* Set Cursor Position */ +# else + regs.h.ah = SETCURPOS; + regs.h.bh = 0; /* display page */ + regs.h.dh = (char)y; /* row */ + regs.h.dl = (char)x; /* column */ + (void) int86(VIDEO_BIOS, ®s, ®s); /* Set Cursor Position */ +# endif +# endif +# if defined(SCREEN_DJGPPFAST) + ScreenSetCursor(y,x); +# endif + /* The above, too, goes back all the way to the original PC. If + * we ever get so fancy as to swap display pages (i doubt it), + * then we'll need to set BH appropriately. This function + * returns nothing. -3. + */ +} +# endif /* OVL0 */ + +/* + * This marks the end of the cursor manipulation/information routines. + * ------------------------------------------------------------------- + */ + +# ifdef OVLB +# ifdef MONO_CHECK +int txt_monoadapt_check() +{ + union REGS regs; + + regs.h.al = 0; + regs.h.ah = GETMODE; /* get video mode */ + (void) int86(VIDEO_BIOS, ®s, ®s); + return (regs.h.al == 7) ? 1 : 0; /* 7 means monochrome mode */ +} +# endif /* MONO_CHECK */ +# endif /* OVLB */ +#endif /* NO_TERMS */ + +/* vidtxt.c */ diff --git a/sys/msdos/vidvga.c b/sys/msdos/vidvga.c new file mode 100644 index 0000000..00a1475 --- /dev/null +++ b/sys/msdos/vidvga.c @@ -0,0 +1,1480 @@ +/* SCCS Id: @(#)vidvga.c 3.4 1996/02/16 */ +/* Copyright (c) NetHack PC Development Team 1995 */ +/* NetHack may be freely redistributed. See license for details. */ +/* + * vidvga.c - VGA Hardware video support + */ + +#include "hack.h" + +#ifdef SCREEN_VGA /* this file is for SCREEN_VGA only */ +#include "pcvideo.h" +#include "tile.h" +#include "pctiles.h" + +#include +#include +#include "wintty.h" + +# ifdef __GO32__ +#include +#include +# endif + +/*========================================================================= + * VGA Video supporting routines (for tiles). + * + * The following routines carry out the lower level video functions required + * to make PC NetHack work with VGA graphics. + * + * - The binary files NetHack1.tib and NetHacko.tib must be in your + * game directory. Currently, unpredictable results may occur if they + * aren't since the code hasn't been tested with it missing (yet). + * + * Notes (96/02/16): + * + * - Cursor emulation on the map is now implemented. The input routine + * in msdos.c calls the routine to display the cursor just before + * waiting for input, and hides the cursor immediately after satisfying + * the input request. + * + * - A check for a VGA adapter is implemented. + * + * - With 640 x 480 resolution, the use of 16 x 16 icons allows only 40 + * columns for the map display. This makes it necessary to support the + * TTY CLIPPING code. The right/left scrolling with this can be + * a little annoying. Feel free to rework the routines. + * + * - NetHack1.tib is built from text files derived from bitmap files + * provided by Warwick Allison, using routines developed and supplied + * by Janet Walz. The icons are very well done and thanks to + * Warwick Allison for an excellent effort! + * + * - The text fonts that this is using while in graphics mode come from + * the Video BIOS ROM on board the VGA adapter. Code in vga_WriteChar + * copies the appropriate pixels from the video ROM to the Video buffer. + * + * - Currently, most of the routines are in an ifdef OVLB. + * If you change that, you may have to muck with some of the + * variable declarations which now assume this. This is not a + * problem in a non-overlaid environment (djgpp for example). + * + * - VGA 640 by 480, 16 colour mode (0x12) uses an odd method to + * represent a colour value from the palette. There are four + * planes of video memory, all overlaid at the same memory location. + * For example, if a pixel has the colour value 7: + * + * 0 1 1 1 + * \ \ \ \ + * \ \ \ plane 0 + * \ \ plane 1 + * \ plane 2 + * plane 3 + * + * - VGA write mode 2 requires that a read be done before a write to + * set some latches on the card. The value read had to be placed + * into a variable declared 'volatile' to prevent djgpp from + * optimizing away the entire instruction (the value was assigned + * to a variable which was never used). This corrects the striping + * problem that was apparent with djgpp. + * + * - A check for valid mode switches has been added. + * + * - No tiles are displayed on the Rogue Level in keeping with the + * original Rogue. The display adapter remains in graphics mode + * however. + * + * - Added handling for missing NetHackX.tib files, and resort to using + * video:default and tty if one of them can't be located. + * + * ToDo (96/02/17): + * + * - Nothing prior to release. + *========================================================================= + */ + + +# if defined(_MSC_VER) +# if _MSC_VER >= 700 +#pragma warning(disable:4018) /* signed/unsigned mismatch */ +#pragma warning(disable:4127) /* conditional expression is constant */ +#pragma warning(disable:4131) /* old style declarator */ +#pragma warning(disable:4305) /* prevents complaints with MK_FP */ +#pragma warning(disable:4309) /* initializing */ +# if _MSC_VER > 700 +#pragma warning(disable:4759) /* prevents complaints with MK_FP */ +# endif +# endif +# include +# endif + +/* STATIC_DCL void FDECL(vga_NoBorder, (int)); */ +void FDECL(vga_gotoloc, (int,int)); /* This should be made a macro */ +void NDECL(vga_backsp); +#ifdef SCROLLMAP +STATIC_DCL void FDECL(vga_scrollmap,(BOOLEAN_P)); +#endif +STATIC_DCL void FDECL(vga_redrawmap,(BOOLEAN_P)); +void FDECL(vga_cliparound,(int, int)); +STATIC_OVL void FDECL(decal_planar,(struct planar_cell_struct *, unsigned)); + +#ifdef POSITIONBAR +STATIC_DCL void NDECL(positionbar); +static void FDECL(vga_special,(int, int, int)); +#endif + +extern int clipx, clipxmax; /* current clipping column from wintty.c */ +extern boolean clipping; /* clipping on? from wintty.c */ +extern int savevmode; /* store the original video mode */ +extern int curcol,currow; /* current column and row */ +extern int g_attribute; +extern int attrib_text_normal; /* text mode normal attribute */ +extern int attrib_gr_normal; /* graphics mode normal attribute */ +extern int attrib_text_intense; /* text mode intense attribute */ +extern int attrib_gr_intense; /* graphics mode intense attribute */ +extern boolean inmap; /* in the map window */ + +/* + * Global Variables + */ + +STATIC_VAR unsigned char __far *font; +STATIC_VAR char *screentable[SCREENHEIGHT]; + +STATIC_VAR char *paletteptr; +STATIC_VAR struct map_struct { + int glyph; + int ch; + int attr; + unsigned special; +} map[ROWNO][COLNO]; /* track the glyphs */ + +# define vga_clearmap() { int x,y; for (y=0; y < ROWNO; ++y) \ + for (x=0; x < COLNO; ++x) { map[y][x].glyph = cmap_to_glyph(S_stone); \ + map[y][x].ch = S_stone; map[y][x].attr = 0; map[y][x].special = 0;} } +# define TOP_MAP_ROW 1 +# if defined(OVLB) +STATIC_VAR int vgacmap[CLR_MAX] = {0,3,5,9,4,8,12,14,11,2,6,7,1,8,12,13}; +STATIC_VAR int viewport_size = 40; +/* STATIC_VAR char masktable[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; */ +/* STATIC_VAR char bittable[8]= {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; */ +#if 0 +STATIC_VAR char defpalette[] = { /* Default VGA palette */ + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0xcc, 0xcc, 0xcc, + 0x00, 0x00, 0xff, + 0x00, 0xff, 0x00, + 0xff, 0x00, 0x00, + 0xff, 0xff, 0x00, + 0xff, 0x00, 0xff, + 0xff, 0xff, 0xff + }; +#endif + +# ifndef ALTERNATE_VIDEO_METHOD +int vp[SCREENPLANES] = {8,4,2,1}; +# endif +int vp2[SCREENPLANES] = {1,2,4,8}; +# else +extern int vgacmap[CLR_MAX]; +extern int viewport_size; +extern char masktable[8]; +extern char bittable[8]; +extern char defpalette[]; +# ifndef ALTERNATE_VIDEO_METHOD +extern int vp[SCREENPLANES]; +# endif +extern int vp2[SCREENPLANES]; +# endif /* OVLB */ + +STATIC_VAR struct planar_cell_struct *planecell; +STATIC_VAR struct overview_planar_cell_struct *planecell_O; + +# if defined(USE_TILES) +STATIC_VAR struct tibhdr_struct tibheader; +/* extern FILE *tilefile; */ /* Not needed in here most likely */ +# endif + +/* STATIC_VAR int g_attribute; */ /* Current attribute to use */ + +#ifdef OVLB +void +vga_get_scr_size() +{ + CO = 80; + LI = 29; +} +#endif /*OVLB*/ + + + +# ifdef OVLB + +void +vga_backsp() +{ + int col,row; + + col = curcol; /* Character cell row and column */ + row = currow; + + if (col > 0) col = col-1; + vga_gotoloc(col,row); +} + +# endif /* OVLB */ +# ifdef OVL0 + +void +vga_clear_screen(colour) +int colour; +{ + char __far *pch; + int y,j; + char volatile a; + + outportb(0x3ce,5); + outportb(0x3cf,2); + + for (y=0; y < SCREENHEIGHT; ++y) { + pch = screentable[y]; + for (j=0; j < SCREENBYTES; ++j) { + outportb(0x3ce,8); + outportb(0x3cf,255); + a = READ_ABSOLUTE(pch); /* Must read , then write */ + WRITE_ABSOLUTE(pch, (char)colour); + ++pch; + } + } + outportb(0x3ce,5); + outportb(0x3cf,0); + if (iflags.tile_view) vga_clearmap(); + vga_gotoloc(0,0); /* is this needed? */ +} + +void +vga_cl_end(col,row) /* clear to end of line */ +int col,row; +{ + int count; + + /* + * This is being done via character writes. + * This should perhaps be optimized for speed by using VGA write + * mode 2 methods as did clear_screen() + */ + for (count = col; count < (CO-1); ++count) { + vga_WriteChar(' ',count,row,BACKGROUND_VGA_COLOR); + } +} + +void +vga_cl_eos(cy) /* clear to end of screen */ +int cy; +{ + int count; + + cl_end(); + while(cy <= LI-2) { + for (count = 0; count < (CO-1); ++count) { + vga_WriteChar(' ',count,cy, + BACKGROUND_VGA_COLOR); + } + cy++; + } +} + + +# endif /* OVL0 */ + +# ifdef OVLB +void +vga_tty_end_screen() +{ + vga_clear_screen(BACKGROUND_VGA_COLOR); + vga_SwitchMode(MODETEXT); +} + + +void +vga_tty_startup(wid, hgt) + int *wid, *hgt; +{ + + /* code to sense display adapter is required here - MJA */ + + vga_get_scr_size(); + if (CO && LI) { + *wid = CO; + *hgt = LI; + } + + attrib_gr_normal = ATTRIB_VGA_NORMAL; + attrib_gr_intense = ATTRIB_VGA_INTENSE; + g_attribute = attrib_gr_normal; /* Give it a starting value */ +} +# endif /* OVLB */ + +/* + * Screen output routines (these are heavily used). + * + * These are the 3 routines used to place information on the screen + * in the VGA PC tty port of NetHack. These are the routines + * that get called by the general interface routines in video.c. + * + * vga_xputs -Writes a c null terminated string at the current location. + * + * vga_xputc -Writes a single character at the current location. Since + * various places in the code assume that control characters + * can be used to control, we are forced to interpret some of + * the more common ones, in order to keep things looking correct. + * + * vga_xputg -This routine is used to display a graphical representation of a + * NetHack glyph (a tile) at the current location. For more + * information on NetHack glyphs refer to the comments in + * include/display.h. + * + */ + +# ifdef OVL0 +void +vga_xputs(s,col,row) +const char *s; +int col,row; +{ + + if (s != (char *)0) { + vga_WriteStr((char *)s,strlen(s),col,row,g_attribute); + } +} + +void +vga_xputc(ch,attr) /* write out character (and attribute) */ +char ch; +int attr; +{ + int col,row; + + col = curcol; + row = currow; + + switch(ch) { + case '\n': + col = 0; + ++row; + break; + default: + vga_WriteChar((unsigned char)ch,col,row,attr); + if (col < (CO -1 )) ++col; + break; + } /* end switch */ + vga_gotoloc(col,row); +} + +# if defined(USE_TILES) +void +vga_xputg(glyphnum,ch, special) /* Place tile represent. a glyph at current location */ +int glyphnum; +int ch; +unsigned special; /* special feature: corpse, invis, detected, pet, ridden - hack.h */ +{ + int col,row; + int attr; + int ry; + + row = currow; + col = curcol; + if ((col < 0 || col >= COLNO) || + (row < TOP_MAP_ROW || row >= (ROWNO + TOP_MAP_ROW))) return; + ry = row - TOP_MAP_ROW; + map[ry][col].glyph = glyphnum; + map[ry][col].ch = ch; + map[ry][col].special = special; + attr = (g_attribute == 0) ? attrib_gr_normal : g_attribute; + map[ry][col].attr = attr; + if (iflags.traditional_view) { + vga_WriteChar((unsigned char)ch,col,row,attr); + } else if (!iflags.over_view) { + if ((col >= clipx) && (col <= clipxmax)) { + if (!ReadPlanarTileFile(glyph2tile[glyphnum], &planecell)) { + if (map[ry][col].special) decal_planar(planecell, special); + vga_DisplayCell(planecell, + col - clipx, row); + } else + pline("vga_xputg: Error reading tile (%d,%d) from file", + glyphnum,glyph2tile[glyphnum]); + } + } else { + if (!ReadPlanarTileFile_O(glyph2tile[glyphnum], &planecell_O)) + vga_DisplayCell_O(planecell_O, col, row); + else + pline("vga_xputg: Error reading tile (%d,%d) from file", + glyphnum,glyph2tile[glyphnum]); + } + if (col < (CO - 1 )) ++col; + vga_gotoloc(col,row); +} +# endif /* USE_TILES */ + +/* + * Cursor location manipulation, and location information fetching + * routines. + * These include: + * + * vga_gotoloc(x,y) - Moves the "cursor" on screen to the specified x + * and y character cell location. This routine + * determines the location where screen writes + * will occur next, it does not change the location + * of the player on the NetHack level. + */ + +void +vga_gotoloc(col,row) +int col,row; +{ + curcol = min(col,CO - 1); /* protection from callers */ + currow = min(row,LI - 1); +} + +# if defined(USE_TILES) && defined(CLIPPING) +void +vga_cliparound(x, y) +int x, y; +{ + extern boolean restoring; + int oldx = clipx; + + if (!iflags.tile_view || iflags.over_view || iflags.traditional_view) + return; + + if (x < clipx + 5) { + clipx = max(0, x - (viewport_size / 2)); + clipxmax = clipx + (viewport_size - 1); + } + else if (x > clipxmax - 5) { + clipxmax = min(COLNO - 1, x + (viewport_size / 2)); + clipx = clipxmax - (viewport_size - 1); + } + if (clipx != oldx) { + if (on_level(&u.uz0, &u.uz) && !restoring) + /* (void) doredraw(); */ + vga_redrawmap(1); + } +} + +STATIC_OVL void +vga_redrawmap(clearfirst) +boolean clearfirst; +{ + int j,x,y,t; + char __far *pch; + char volatile a; + + if (clearfirst) { + /* y here is in pixel rows */ + outportb(0x3ce,5); + outportb(0x3cf,2); + t = TOP_MAP_ROW * ROWS_PER_CELL; + for (y = t; y < (ROWNO * ROWS_PER_CELL) + t; ++y) { + pch = screentable[y]; + for (j=0; j < SCREENBYTES; ++j) { + outportb(0x3ce,8); + outportb(0x3cf,255); + /* On VGA mode2, must read first, then write */ + a = READ_ABSOLUTE(pch); + WRITE_ABSOLUTE(pch, (char)BACKGROUND_VGA_COLOR); + ++pch; + } + } + outportb(0x3ce,5); + outportb(0x3cf,0); + } + /* y here is in screen rows*/ +# ifdef ROW_BY_ROW + for (y = 0; y < ROWNO; ++y) + for (x = clipx; x <= clipxmax; ++x) { +# else + for (x = clipx; x <= clipxmax; ++x) + for (y = 0; y < ROWNO; ++y) { +# endif + if (iflags.traditional_view) { + if (!(clearfirst && map[y][x].ch == S_stone)) + vga_WriteChar( + (unsigned char)map[y][x].ch, + x,y + TOP_MAP_ROW,map[y][x].attr); + } else { + t = map[y][x].glyph; + if (!(clearfirst && t == cmap_to_glyph(S_stone))) { + if (!iflags.over_view) { + if (!ReadPlanarTileFile(glyph2tile[t], + &planecell)) { + if (map[y][x].special) + decal_planar(planecell, map[y][x].special); + vga_DisplayCell(planecell, + x - clipx, y + TOP_MAP_ROW); + } else + pline("vga_redrawmap: Error reading tile (%d,%d)", + t,glyph2tile[t]); + } else { + if (!ReadPlanarTileFile_O(glyph2tile[t], + &planecell_O)) { + vga_DisplayCell_O(planecell_O, + x, y + TOP_MAP_ROW); + } else + pline("vga_redrawmap: Error reading tile (%d,%d)", + t,glyph2tile[t]); + } + } + } + } +} +# endif /* USE_TILES && CLIPPING */ +# endif /* OVL0 */ +# ifdef OVL2 + +void +vga_userpan(left) +boolean left; +{ + int x; + +/* pline("Into userpan"); */ + if (iflags.over_view || iflags.traditional_view) return; + if (left) + x = min(COLNO - 1, clipxmax + 10); + else + x = max(0, clipx - 10); + vga_cliparound(x, 10); /* y value is irrelevant on VGA clipping */ + positionbar(); + vga_DrawCursor(); +} + + +void vga_overview(on) +boolean on; +{ +/* vga_HideCursor(); */ + if (on) { + iflags.over_view = TRUE; + clipx = 0; + clipxmax = CO - 1; + } else { + iflags.over_view = FALSE; + clipx = max(0, (curcol - viewport_size / 2)); + if (clipx > ((CO - 1) - viewport_size)) + clipx = (CO - 1) - viewport_size; + clipxmax = clipx + (viewport_size - 1); + } +} + +void vga_traditional(on) +boolean on; +{ +/* vga_HideCursor(); */ + if (on) { +/* switch_graphics(ASCII_GRAPHICS); */ + iflags.traditional_view = TRUE; + clipx = 0; + clipxmax = CO - 1; + } else { + iflags.traditional_view = FALSE; + if (!iflags.over_view) { + clipx = max(0, (curcol - viewport_size / 2)); + if (clipx > ((CO - 1) - viewport_size)) + clipx = (CO - 1) - viewport_size; + clipxmax = clipx + (viewport_size - 1); + } + } +} + +void vga_refresh() +{ + positionbar(); + vga_redrawmap(1); + vga_DrawCursor(); +} + +# ifdef SCROLLMAP +STATIC_OVL void +vga_scrollmap(left) +boolean left; +{ + int j,x,y,t; + int i,pixx,pixy,x1,y1,x2,y2; + int byteoffset, vplane; + char __far *tmp1; + char __far *tmp2; + unsigned char source[SCREENPLANES][80]; + unsigned char first,second; + + + pixy = row2y(TOP_MAP_ROW); /* convert to pixels */ + pixx = col2x(x1); + if (left) { + x1 = 20; + x2 = 0; + } else { + x1 = 0; + x2 = 20; + } + /* read each row, all columns but the one to be replaced */ + for(i = 0;i < (ROWNO-1) * ROWS_PER_CELL; ++i) { + tmp1 = screentable[i + pixy]; + tmp1 += x1; + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egareadplane(vplane); + for (byteoffset = 0; byteoffset < 20; ++byteoffset) { + tmp2 = tmp1 + byteoffset; + source[vplane][byteoffset] = READ_ABSOLUTE(tmp2); + } + } + tmp1 = screentable[i + pixy]; + tmp1 += x2; + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egawriteplane(vp2[vplane]); + for (byteoffset = 0; byteoffset < 20; ++byteoffset) { + tmp2 = tmp1 + byteoffset; + WRITE_ABSOLUTE(tmp2,source[vplane][byteoffset]); + } + } + egawriteplane(15); + } + + if (left) { + i = clipxmax - 1; + j = clipxmax; + } else { + i = clipx; + j = clipx + 1; + } + for (y = 0; y < ROWNO; ++y) { + for (x = i; x < j; x += 2) { + t = map[y][x].glyph; + if (!ReadPlanarTileFile(glyph2tile[t], &planecell)) + if (map[y][x].special) decal_planar(planecell, map[y][x].special); + vga_DisplayCell(planecell, x - clipx, y + TOP_MAP_ROW); + else + pline("vga_shiftmap: Error reading tile (%d,%d)", + t, glyph2tile[t]); + } + } +} +# endif /* SCROLLMAP */ +# endif /* OVL2 */ + +# ifdef OVLB +STATIC_OVL void +decal_planar(gp, special) +struct planar_cell_struct *gp; +unsigned special; +{ + if (special & MG_CORPSE) { + } else if (special & MG_INVIS) { + } else if (special & MG_DETECT) { + } else if (special & MG_PET) { + } else if (special & MG_RIDDEN) { + } +} + +/* + * Open tile files, + * initialize the SCREEN, switch it to graphics mode, + * initialize the pointers to the fonts, clear + * the screen. + * + */ +void vga_Init(void) +{ + int i; + +# ifdef USE_TILES + int tilefailure = 0; +/* + * Attempt to open the required tile files. If we can't + * don't perform the video mode switch, use TTY code instead. + * + */ + if (OpenTileFile(NETHACK_PLANAR_TILEFILE, FALSE)) tilefailure |= 1; + if (OpenTileFile(NETHACK_OVERVIEW_TILEFILE, TRUE)) tilefailure |= 2; + if (ReadTileFileHeader(&tibheader, FALSE)) tilefailure |= 4; + + if (tilefailure) { + raw_printf("Reverting to TTY mode, tile initialization failure (%d).", + tilefailure); + wait_synch(); + iflags.usevga = 0; + iflags.tile_view = FALSE; + iflags.over_view = FALSE; + CO = 80; + LI = 25; +/* clear_screen() */ /* not vga_clear_screen() */ + return; + } +# endif + + if (iflags.usevga) { + for (i=0; i < SCREENHEIGHT; ++i) { + screentable[i]=MK_PTR(VIDEOSEG, (i * SCREENBYTES)); + } + } + vga_SwitchMode(MODE640x480); + windowprocs.win_cliparound = vga_cliparound; +/* vga_NoBorder(BACKGROUND_VGA_COLOR); */ /* Not needed after palette mod */ +# ifdef USE_TILES + paletteptr = tibheader.palette; + iflags.tile_view = TRUE; + iflags.over_view = FALSE; +# else + paletteptr = defpalette; +# endif + vga_SetPalette(paletteptr); + g_attribute = attrib_gr_normal; + font = vga_FontPtrs(); + clear_screen(); + clipx = 0; + clipxmax = clipx + (viewport_size - 1); +} + +/* + * Switches modes of the video card. + * + * If mode == MODETEXT (0x03), then the card is placed into text + * mode. If mode == 640x480, then the card is placed into vga + * mode (video mode 0x12). No other modes are currently supported. + * + */ +void vga_SwitchMode(unsigned int mode) +{ + union REGS regs; + + if ((mode == MODE640x480) || (mode == MODETEXT)) { + if (iflags.usevga && (mode == MODE640x480)) { + iflags.grmode = 1; + } else { + iflags.grmode = 0; + } + regs.x.ax = mode; + (void) int86(VIDEO_BIOS, ®s, ®s); + } else { + iflags.grmode = 0; /* force text mode for error msg */ + regs.x.ax = MODETEXT; + (void) int86(VIDEO_BIOS, ®s, ®s); + g_attribute = attrib_text_normal; + impossible("vga_SwitchMode: Bad video mode requested 0x%X", + mode); + } +} + +/* + * This allows grouping of several tasks to be done when + * switching back to text mode. This is a public (extern) function. + * + */ +void vga_Finish(void) +{ + CloseTileFile(0); + CloseTileFile(1); + vga_SwitchMode(MODETEXT); + windowprocs.win_cliparound = tty_cliparound; + g_attribute = attrib_text_normal; + iflags.tile_view = FALSE; +} + +#if 0 +/* + * Turn off any border colour that might be enabled in the VGA card + * register. + * + * I disabled this after modifying tile2bin.c to remap black & white + * to a more standard values - MJA 94/04/23. + * + */ +STATIC_OVL void +vga_NoBorder(int bc) +{ + union REGS regs; + + regs.h.ah = (char)0x10; + regs.h.al = (char)0x01; + regs.h.bh = (char)bc; + regs.h.bl = 0; + (void) int86(VIDEO_BIOS, ®s, ®s); +} +#endif + +/* + * + * Returns a far pointer (or flat 32 bit pointer under djgpp) to the + * location of the appropriate ROM font for the _current_ video mode + * (so you must place the card into the desired video mode before + * calling this function). + * + * This function takes advantage of the video BIOS loading the + * address of the appropriate character definition table for + * the current graphics mode into interrupt vector 0x43 (0000:010C). + */ +char __far *vga_FontPtrs(void) +{ + USHORT __far *tmp; + char __far *retval; + USHORT fseg, foff; + tmp = (USHORT __far *)MK_PTR(((USHORT)FONT_PTR_SEGMENT), + ((USHORT)FONT_PTR_OFFSET)); + foff = READ_ABSOLUTE_WORD(tmp); + ++tmp; + fseg = READ_ABSOLUTE_WORD(tmp); + retval = (char __far *)MK_PTR(fseg,foff); + return retval; +} + +/* + * This will verify the existance of a VGA adapter on the machine. + * Video function call 0x1a returns 0x1a in AL if successful, and + * returns the following values in BL for the active display: + * + * 0=no display, 1=MDA, 2=CGA, 4=EGA(color-monitor), + * 5=EGA(mono-monitor), 6=PGA, 7=VGA(mono-monitor), 8=VGA(color-monitor), + * 0xB=MCGA(mono-monitor), 0xC=MCGA(color-monitor), 0xFF=unknown) + */ +int vga_detect() +{ + union REGS regs; + + regs.h.al = 0; + regs.h.ah = 0x1a; + (void) int86(VIDEO_BIOS, ®s, ®s); +/* + * debug + * + * printf("vga_detect returned al=%02x, bh=%02x, bl=%02x\n", + * (int)regs.h.al, (int)regs.h.bh, (int)regs.h.bl); + * getch(); + */ + if ((int)regs.h.al == 0x1a) { + if (((int)regs.h.bl == 8) || ((int)regs.h.bl == 7)) { + return 1; + } + } + return 0; +} + +/* + * Write character 'ch', at (x,y) and + * do it using the colour 'colour'. + * + */ +void +vga_WriteChar(chr,col,row,colour) +int chr,col,row,colour; +{ + int i; + int x,pixy; + + char volatile tc; + char __far *cp; + unsigned char __far *fp = font; + unsigned char fnt; + int actual_colour = vgacmap[colour]; + + + x = min(col,(CO-1)); /* min() used protection from callers */ + pixy = min(row,(LI-1)) * 16; /* assumes 8 x 16 char set */ +/* if (chr < ' ') chr = ' '; */ /* assumes ASCII set */ + + outportb(0x3ce,5); + outportb(0x3cf,2); + + chr = chr<<4; + for (i=0; i < MAX_ROWS_PER_CELL; ++i) { + cp = screentable[pixy+i] + x; + fnt = READ_ABSOLUTE((fp + chr + i)); + outportb(0x3ce,8); + outportb(0x3cf,fnt); + tc = READ_ABSOLUTE(cp); /* wrt mode 2, must read, then write */ + WRITE_ABSOLUTE(cp, (char)actual_colour); + outportb(0x3ce,8); + outportb(0x3cf,~fnt); + tc = READ_ABSOLUTE(cp); /* wrt mode 2, must read, then write */ + WRITE_ABSOLUTE(cp, (char)BACKGROUND_VGA_COLOR); + } + outportb(0x3ce,5); + outportb(0x3cf,0); + outportb(0x3ce,8); + outportb(0x3cf,255); +} + +/* + * This is the routine that displays a high-res "cell" pointed to by 'gp' + * at the desired location (col,row). + * + * Note: (col,row) in this case refer to the coordinate location in + * NetHack character grid terms, (ie. the 40 x 25 character grid), + * not the x,y pixel location. + * + */ +void +vga_DisplayCell(gp,col,row) +struct planar_cell_struct *gp; +int col,row; +{ + int i,pixx,pixy; + char __far *tmp_s; /* source pointer */ + char __far *tmp_d; /* destination pointer */ + int vplane; + + pixy = row2y(row); /* convert to pixels */ + pixx = col2x(col); + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egawriteplane(vp[vplane]); + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp_d = screentable[i+pixy]; + tmp_d += pixx; + /* + * memcpy((void *)tmp,(void *)gp->plane[vplane].image[i], + * BYTES_PER_CELL); + */ + tmp_s = gp->plane[vplane].image[i]; + WRITE_ABSOLUTE(tmp_d, (*tmp_s)); + ++tmp_s; ++tmp_d; + WRITE_ABSOLUTE(tmp_d, (*tmp_s)); + } + } + egawriteplane(15); +} + +void +vga_DisplayCell_O(gp,col,row) +struct overview_planar_cell_struct *gp; +int col,row; +{ + int i,pixx,pixy; + char __far *tmp_s; /* source pointer */ + char __far *tmp_d; /* destination pointer */ + int vplane; + + pixy = row2y(row); /* convert to pixels */ + pixx = col; + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egawriteplane(vp[vplane]); + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp_d = screentable[i+pixy]; + tmp_d += pixx; + /* + * memcpy((void *)tmp,(void *)gp->plane[vplane].image[i], + * BYTES_PER_CELL); + */ + tmp_s = gp->plane[vplane].image[i]; + WRITE_ABSOLUTE(tmp_d, (*tmp_s)); + } + } + egawriteplane(15); +} + +/* + * Write the character string pointed to by 's', whose maximum length + * is 'len' at location (x,y) using the 'colour' colour. + * + */ +void +vga_WriteStr(s,len,col,row,colour) +char *s; +int len,col,row,colour; +{ + unsigned char *us; + int i = 0; + + /* protection from callers */ + if (row > (LI-1)) return; + + i = 0; + us = (unsigned char *)s; + while( (*us != 0) && (i < len) && (col < (CO - 1))) { + vga_WriteChar(*us,col,row,colour); + ++us; + ++i; + ++col; + } +} + +# endif /* OVLB */ + + +# ifdef OVLB +/* + * Initialize the VGA palette with the desired colours. This + * must be a series of 48 bytes for use with a card in + * 16 colour mode at 640 x 480. + * + */ +void +vga_SetPalette(p) + char *p; +{ + union REGS regs; + int i; + + outportb(0x3c6,0xff); + for(i=0;i < COLORDEPTH; ++i) { + outportb(0x3c8,i); + outportb(0x3c9,(*p++) >> 2); + outportb(0x3c9,(*p++) >> 2); + outportb(0x3c9,(*p++) >> 2); + } + regs.x.bx = 0x0000; + for(i=0;i < COLORDEPTH; ++i) { + regs.x.ax = 0x1000; + (void) int86(VIDEO_BIOS,®s,®s); + regs.x.bx += 0x0101; + } +} + +/*static unsigned char colorbits[]={0x01,0x02,0x04,0x08}; */ /* wrong */ +static unsigned char colorbits[]={0x08,0x04,0x02,0x01}; + +#ifdef POSITIONBAR + +#define PBAR_ROW (LI - 4) +#define PBAR_COLOR_ON 15 /* slate grey background colour of tiles */ +#define PBAR_COLOR_OFF 12 /* bluish grey, used in old style only */ +#define PBAR_COLOR_STAIRS 9 /* brown */ +#define PBAR_COLOR_HERO 14 /* creamy white */ + +static unsigned char pbar[COLNO]; + +void +vga_update_positionbar(posbar) +char *posbar; +{ + char *p = pbar; + if (posbar) while (*posbar) *p++ = *posbar++; + *p = 0; +} + +STATIC_OVL void +positionbar() +{ + char *posbar = pbar; + int feature, ucol; + int k, y, colour, row; + char __far *pch; + + int startk, stopk; + char volatile a; + boolean nowhere = FALSE; + int pixy = (PBAR_ROW * MAX_ROWS_PER_CELL); + int tmp; + + if (!iflags.grmode || !iflags.tile_view) return; + if ((clipx < 0) || (clipxmax <= 0) || (clipx >= clipxmax)) + nowhere = TRUE; + if (nowhere) { +#ifdef DEBUG + pline("Would have put bar using %d - %d.",clipx,clipxmax); +#endif + return; + } +#ifdef OLD_STYLE + outportb(0x3ce,5); + outportb(0x3cf,2); + for (y=pixy; y < (pixy + MAX_ROWS_PER_CELL); ++y) { + pch = screentable[y]; + for (k=0; k < SCREENBYTES; ++k) { + if ((k < clipx) || (k > clipxmax)) { + colour = PBAR_COLOR_OFF; + } else colour = PBAR_COLOR_ON; + outportb(0x3ce,8); + outportb(0x3cf,255); + a = READ_ABSOLUTE(pch); /* Must read , then write */ + WRITE_ABSOLUTE(pch, (char)colour); + ++pch; + } + } + outportb(0x3ce,5); + outportb(0x3cf,0); +#else + colour = PBAR_COLOR_ON; + outportb(0x3ce,5); + outportb(0x3cf,2); + for (y=pixy, row = 0; y < (pixy + MAX_ROWS_PER_CELL); ++y, ++row) { + pch = screentable[y]; + if ((!row) || (row == (ROWS_PER_CELL-1))) { + startk = 0; + stopk = SCREENBYTES; + } else { + startk = clipx; + stopk = clipxmax; + } + for (k=0; k < SCREENBYTES; ++k) { + if ((k < startk) || (k > stopk)) + colour = BACKGROUND_VGA_COLOR; + else + colour = PBAR_COLOR_ON; + outportb(0x3ce,8); + outportb(0x3cf,255); + a = READ_ABSOLUTE(pch); /* Must read , then write */ + WRITE_ABSOLUTE(pch, (char)colour); + ++pch; + } + } + outportb(0x3ce,5); + outportb(0x3cf,0); +#endif + ucol = 0; + if (posbar) { + while (*posbar != 0) { + feature = *posbar++; + switch (feature) { + case '>': + vga_special(feature, (int)*posbar++, PBAR_COLOR_STAIRS); + break; + case '<': + vga_special(feature, (int)*posbar++, PBAR_COLOR_STAIRS); + break; + case '@': + ucol = (int)*posbar++; + vga_special(feature, ucol, PBAR_COLOR_HERO); + break; + default: /* unanticipated symbols */ + vga_special(feature, (int)*posbar++, PBAR_COLOR_STAIRS); + break; + } + } + } +# ifdef SIMULATE_CURSOR + if (inmap) { + tmp = curcol + 1; + if ((tmp != ucol) && (curcol >= 0)) + vga_special('_', tmp, PBAR_COLOR_HERO); + } +# endif +} + +void +vga_special(chr,col,color) +int chr,col,color; +{ + int i,y,pixy; + char __far *tmp_d; /* destination pointer */ + int vplane; + char fnt; + char bits[SCREENPLANES][ROWS_PER_CELL]; + + pixy = PBAR_ROW * MAX_ROWS_PER_CELL; + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egareadplane(vplane); + y = pixy; + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp_d = screentable[y++] + col; + bits[vplane][i] = READ_ABSOLUTE(tmp_d); + fnt = READ_ABSOLUTE((font + ((chr<<4) + i))); + if (colorbits[vplane] & color) + bits[vplane][i] |= fnt; + else + bits[vplane][i] &= ~fnt; + } + } + for(vplane=0; vplane < SCREENPLANES; ++vplane) { + egawriteplane(vp[vplane]); + y = pixy; + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp_d = screentable[y++] + col; + WRITE_ABSOLUTE(tmp_d, (bits[vplane][i])); + } + } + egawriteplane(15); +} + +# endif POSITIONBAR + +# ifdef SIMULATE_CURSOR + +static struct planar_cell_struct undercursor; +static struct planar_cell_struct cursor; + +void +vga_DrawCursor() +{ + int i,pixx,pixy,x,y,p; + char __far *tmp1; + char __far *tmp2; + unsigned char first,second; +/* char on[2] = {0xFF,0xFF}; */ +/* char off[2] = {0x00,0x00}; */ +#ifdef REINCARNATION + boolean isrogue = Is_rogue_level(&u.uz); + boolean singlebyte = (isrogue || iflags.over_view + || iflags.traditional_view || !inmap); +#else + boolean singlebyte = (iflags.over_view + || iflags.traditional_view || !inmap); +#endif + int curtyp; + + if (!cursor_type && inmap) return; /* CURSOR_INVIS - nothing to do */ + + x = min(curcol,(CO - 1)); /* protection from callers */ + y = min(currow,(LI - 1)); /* protection from callers */ + if (!singlebyte && ((x < clipx) || (x > clipxmax))) return; + pixy = row2y(y); /* convert to pixels */ + if (singlebyte) + pixx = x; + else + pixx = col2x((x-clipx)); + + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp1 = screentable[i+pixy]; + tmp1 += pixx; + tmp2 = tmp1 + 1; + egareadplane(3); + /* memcpy(undercursor.plane[3].image[i],tmp1,BYTES_PER_CELL); */ + undercursor.plane[3].image[i][0] = READ_ABSOLUTE(tmp1); + if (!singlebyte) + undercursor.plane[3].image[i][1] = READ_ABSOLUTE(tmp2); + + egareadplane(2); + /* memcpy(undercursor.plane[2].image[i],tmp1,BYTES_PER_CELL); */ + undercursor.plane[2].image[i][0] = READ_ABSOLUTE(tmp1); + if (!singlebyte) + undercursor.plane[2].image[i][1] = READ_ABSOLUTE(tmp2); + + egareadplane(1); + /* memcpy(undercursor.plane[1].image[i],tmp1,BYTES_PER_CELL); */ + undercursor.plane[1].image[i][0] = READ_ABSOLUTE(tmp1); + if (!singlebyte) + undercursor.plane[1].image[i][1] = READ_ABSOLUTE(tmp2); + + egareadplane(0); + /* memcpy(undercursor.plane[0].image[i],tmp1,BYTES_PER_CELL); */ + undercursor.plane[0].image[i][0] = READ_ABSOLUTE(tmp1); + if (!singlebyte) + undercursor.plane[0].image[i][1] = READ_ABSOLUTE(tmp2); + } + + /* + * Now we have a snapshot of the current cell. + * Make a copy of it, then manipulate the copy + * to include the cursor, and place the tinkered + * version on the display. + */ + + cursor = undercursor; + if (inmap) curtyp = cursor_type; + else curtyp = CURSOR_UNDERLINE; + + switch(curtyp) { + + case CURSOR_CORNER: + for(i = 0; i < 2; ++i) { + if (!i) { + if (singlebyte) first = 0xC3; + else first = 0xC0; + second = 0x03; + } else { + if (singlebyte) first = 0x81; + else first = 0x80; + second = 0x01; + } + for (p=0; p < 4; ++p) { + if (cursor_color & colorbits[p]) { + cursor.plane[p].image[i][0] |= first; + if (!singlebyte) + cursor.plane[p].image[i][1] |= second; + } else { + cursor.plane[p].image[i][0] &= ~first; + if (!singlebyte) + cursor.plane[p].image[i][1] &= ~second; + } + } + } + + for(i = ROWS_PER_CELL - 2; i < ROWS_PER_CELL; ++i) { + if (i != (ROWS_PER_CELL-1)) { + if (singlebyte) first = 0x81; + else first = 0x80; + second = 0x01; + } else { + if (singlebyte) first = 0xC3; + else first = 0xC0; + second = 0x03; + } + for (p=0; p < SCREENPLANES; ++p) { + if (cursor_color & colorbits[p]) { + cursor.plane[p].image[i][0] |= first; + if (!singlebyte) + cursor.plane[p].image[i][1] |= second; + } else { + cursor.plane[p].image[i][0] &= ~first; + if (!singlebyte) + cursor.plane[p].image[i][1] &= ~second; + } + } + } + break; + + case CURSOR_UNDERLINE: + + i = ROWS_PER_CELL - 1; + first = 0xFF; + second = 0xFF; + for (p=0; p < SCREENPLANES; ++p) { + if (cursor_color & colorbits[p]) { + cursor.plane[p].image[i][0] |= first; + if (!singlebyte) + cursor.plane[p].image[i][1] |= second; + } else { + cursor.plane[p].image[i][0] &= ~first; + if (!singlebyte) + cursor.plane[p].image[i][1] &= ~second; + } + } + break; + + case CURSOR_FRAME: + + /* fall through */ + + default: + for(i = 0; i < ROWS_PER_CELL; ++i) { + + if ((i == 0) || (i == (ROWS_PER_CELL-1))) { + first = 0xFF; + second = 0xFF; + } else { + if (singlebyte) first = 0x81; + else first = 0x80; + second = 0x01; + } + for (p=0; p < SCREENPLANES; ++p) { + if (cursor_color & colorbits[p]) { + cursor.plane[p].image[i][0] |= first; + if (!singlebyte) + cursor.plane[p].image[i][1] |= second; + } else { + cursor.plane[p].image[i][0] &= ~first; + if (!singlebyte) + cursor.plane[p].image[i][1] &= ~second; + } + } + } + break; + } + + /* + * Place the new cell onto the display. + * + */ + + for(i=0;i < ROWS_PER_CELL; ++i) { + tmp1 = screentable[i+pixy]; + tmp1 += pixx; + tmp2 = tmp1 + 1; + egawriteplane(8); + /* memcpy(tmp1,cursor.plane[3].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,cursor.plane[3].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,cursor.plane[3].image[i][1]); + + egawriteplane(4); + /* memcpy(tmp1,cursor.plane[2].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,cursor.plane[2].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,cursor.plane[2].image[i][1]); + + egawriteplane(2); + /* memcpy(tmp1,cursor.plane[1].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,cursor.plane[1].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,cursor.plane[1].image[i][1]); + + egawriteplane(1); + /* memcpy(tmp1,cursor.plane[0].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,cursor.plane[0].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,cursor.plane[0].image[i][1]); + } + egawriteplane(15); +#ifdef POSITIONBAR + if (inmap) positionbar(); +#endif +} + +void +vga_HideCursor() +{ + + int i,pixx,pixy,x,y; + char __far *tmp1; + char __far *tmp2; +#ifdef REINCARNATION + boolean isrogue = Is_rogue_level(&u.uz); + boolean singlebyte = (isrogue || iflags.over_view + || iflags.traditional_view || !inmap); +#else + boolean singlebyte = (iflags.over_view + || iflags.traditional_view || !inmap); +#endif + int curtyp; + + if (inmap && !cursor_type) return; /* CURSOR_INVIS - nothing to do */ + /* protection from callers */ + x = min(curcol,(CO - 1)); + y = min(currow,(LI-1)); + if (!singlebyte && ((x < clipx) || (x > clipxmax))) return; + + pixy = row2y(y); /* convert to pixels */ + if (singlebyte) + pixx = x; + else + pixx = col2x((x-clipx)); + + if (inmap) curtyp = cursor_type; + else curtyp = CURSOR_UNDERLINE; + + if (curtyp == CURSOR_UNDERLINE) /* optimization for uline */ + i = ROWS_PER_CELL - 1; + else + i = 0; + + for(;i < ROWS_PER_CELL; ++i) { + tmp1 = screentable[i+pixy]; + tmp1 += pixx; + tmp2 = tmp1 + 1; + egawriteplane(8); + /* memcpy(tmp,undercursor.plane[3].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,undercursor.plane[3].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,undercursor.plane[3].image[i][1]); + + egawriteplane(4); + /* memcpy(tmp,undercursor.plane[2].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,undercursor.plane[2].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,undercursor.plane[2].image[i][1]); + + egawriteplane(2); + /* memcpy(tmp,undercursor.plane[1].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,undercursor.plane[1].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,undercursor.plane[1].image[i][1]); + + egawriteplane(1); + /* memcpy(tmp,undercursor.plane[0].image[i],BYTES_PER_CELL); */ + WRITE_ABSOLUTE(tmp1,undercursor.plane[0].image[i][0]); + if (!singlebyte) + WRITE_ABSOLUTE(tmp2,undercursor.plane[0].image[i][1]); + } + egawriteplane(15); +} +# endif /* SIMULATE_CURSOR */ +# endif /* OVLB */ +#endif /* SCREEN_VGA */ + +/* vidvga.c */ diff --git a/sys/os2/Install.os2 b/sys/os2/Install.os2 new file mode 100644 index 0000000..aafd0fa --- /dev/null +++ b/sys/os2/Install.os2 @@ -0,0 +1,276 @@ + Instructions for compiling and installing NetHack 3.4 + on an OS/2 system + ===================================================== + Timo Hakulinen + Last revision: 29 October 1996 + +0. Read this entire file before starting, and come back to the Notes below if + you have any problems. + +1. Make sure all the NetHack files are in the appropriate directory + structure. You should have a top directory (e.g. nh33, or whatever you + like) with subdirectories dat, doc, include, src, util, sys\share, + sys\os2, and win\tty. You may have other subdirectories under sys and + win, but they will not affect compilation for an OS/2 system. If you do + not follow this structure, the makefile will not function properly. The + .c files for the main program belong in src, those for utility programs in + util, and OS/2-specific ones in sys\os2. All the .h files belong in + include, the documentation in doc, and assorted data files in dat. There + are also some necessary files in sys\share (pc*.c, random.c, dgn_*.*, + lev_*.*). A more detailed explanation of the directory structure is found + in file Files, which should be in the top directory. + + If you downloaded or ftp'd the sources from a UNIX system, the lines may + end in UNIX-style newlines instead of the carriage return and line feed + pairs used by DOS and OS/2. You'll have to convert them (with a utility + like Rahul Dhesi's "flip"). Also, every file should end with a carriage + return / line feed pair, because Microsoft C has had a habit of ignoring + the last line of each file otherwise. Besides, even editing UNIX-style + files with DOS editors is often a royal pain. + +2. The makefile for OS/2, Makefile.os2, is found in directory sys\os2. Copy + it to directory src and rename it Makefile. From now on, Makefile.os2 + will be referred to as "the makefile" in this document. + + The makefile supports the following make utilities: + + NDMAKE a public domain make utility for DOS by Don Kneller + NMAKE make shipped with Microsoft languages and IBM C Set/2 + DMAKE a public domain make for DOS and OS/2 by Dennis Vadura + + Both NDMAKE and DMAKE are available at major archive sites. The + following compilers are supported: + + compiler: runs in: compiles for: + + Microsoft C 5.1 DOS / OS/2 1.0-Warp OS/2 1.x + Microsoft 6.0A (see note 5) - " - - " - + IBM C Set/2 1.00, Toolkit/2 2.00 OS/2 2.x, Warp OS/2 2.x, Warp + IBM CSet++ 2.00 OS/2 2.x, Warp OS/2 2.x, Warp + GCC emx 0.8f (see note 6) OS/2 2.x, Warp OS/2 2.x, Warp + + Note that code compiled for OS/2 versions 1.0-1.3 runs unmodified in OS/2 + versions 2.0 and up. In principle it should be possible to cross compile + NetHack 3.4 for OS/2 in DOS using NDMAKE and MSC, but this is not + recommended (see note 3). + + If you're using some other compiler than one listed above, you will have + to adapt the makefile to your needs. In particular, change the CC, + CFLAGS, LINK, and LFLAGS macros to your C compiler's and linker's liking. + See the makefile for more information. + + If you are going to be constructing Fred Fish's termcap library, you'll + need Makefile.lib in sys\share (see note 4). + +3. Go to the include subdirectory. First edit config.h according to the + comments to match your system and desired set of features. In particular, + make sure that OS2 is defined, and that UNIX, HACKDIR, and COMPRESS are + *not* defined. If you want to try out the new DLB data file library + scheme, uncomment DLB. Note that although the makefile contains some + support for this scheme, it's new in NetHack 3.3 and hasn't been tested. + If your compiler is ANSI compliant (like practically all OS/2 compilers + are), it's probable that nothing else needs to be configured in config.h. + + Next look at os2conf.h. This file shouldn't need much changing. If you + want to use the hardcoded OS/2 system definitions in def_os2.h instead of + the compiler's standard headers, comment out OS2_USESYSHEADERS. This may + become necessary if you are using a compiler which doesn't come with + proper system headers by default. In this case you may have to edit the + definitions there, because every compiler has its own way of declaring + the necessary system functions and data structures. In general you + should prefer the compiler's offerings, if possible. + + If you are going to compile the game on an HPFS drive, uncomment OS2_HPFS, + which enables the use of longer file names during compilation. The + generated executable will only use file names compatible with FAT drives, + however. + + If you are using a 32 bit compiler other than GCC emx 0.8f or C Set/2 in + OS/2 2.x, force OS2_32BITAPI to be defined. Otherwise it is defined only + for the above mentioned compilers. + + If you are not going to include random.c, because you are using the + random number generator provided by your compiler, you will need to + comment out RANDOM. + + If you want to muck with different termcap settings, uncomment TERMLIB to + enable the use of termcap routines (see note 4). This is not necessary to + create a fully functional game, however. + +4. If you are using another compiler than MSC, GCC, or IBM C Set/2, you may + want to look through system.h in the include directory. This file matches + the return and parameter types for system calls and library routines with + various flavors of compilers and operating systems. Leaving this file + alone is unlikely to cause problems, but if you get compile errors with + any functions in the standard library, it's worth checking the + declarations there. + +5. If you want to change the high score list behavior, examine the top of + topten.c, in the src directory. You may want to change the definitions of + PERSMAX, POINTSMIN, and ENTRYMAX. + +6. Go to the src directory and edit the top of the makefile. Be sure that + the directory you want the game installed to actually exists. + + You'll need nroff and/or TeX/LaTeX to do the files in doc. If you don't + have either of these, you can skip it. + + If you elected not to use the high quality BSD random number routines by + commenting out RANDOM in os2conf.h, comment out (or set equal to nothing) + the RANDOM macro in the makefile. + + If you elected to use Fred Fish's termcap library (bundled in as + termcap.uu in directory sys\share), you will have to generate termlib.lib + from those sources by typing "make -f makefile.lib termlib.lib". You must + set the TERMLIB option in the makefile to link the resulting termlib.lib + into the game. + + If you are recompiling after patching your sources, or if you got your + files from somewhere other than the official distribution, "touch + makedefs.c" to ensure that certain files (onames.h and pm.h) are remade, + lest potentially troublesome time stamps fool make. + + If you have lex and yacc programs, or the equivalent flex and bison + programs, you can set up the makefile to generate the appropriate .h and + .c files from their .l and .y counterparts whenever you recompile. This + is done by changing the do_yacc and do_lex targets in the makefile to + depend on targets yacc_act and lex_act instead of yacc_cpy and lex_cpy. + Otherwise the makefile will copy pre-generated yacc and lex output files + dgn_*.* and lev_*.* from directory sys\share to util and include. + + Now, enter "make all", and take a siesta; your computer will be occupied + for a fair amount of time. If all goes well, you will get an executable. + +7. All the support data files should have been copied to the game directory + by the make process. Here is the complete list in alphabetical order of + all the files that should have gotten there during a full build: + + Arc-fila.lev Arc-filb.lev Arc-goal.lev Arc-loca.lev Arc-strt.lev + Bar-fila.lev Bar-filb.lev Bar-goal.lev Bar-loca.lev Bar-strt.lev + Cav-fila.lev Cav-filb.lev Cav-goal.lev Cav-loca.lev Cav-strt.lev + Hea-fila.lev Hea-filb.lev Hea-goal.lev Hea-loca.lev Hea-strt.lev + Kni-fila.lev Kni-filb.lev Kni-goal.lev Kni-loca.lev Kni-strt.lev + Mon-fila.lev Mon-filb.lev Mon-goal.lev Mon-loca.lev Mon-strt.lev + Pri-fila.lev Pri-filb.lev Pri-goal.lev Pri-loca.lev Pri-strt.lev + Ran-fila.lev Ran-filb.lev Ran-goal.lev Ran-loca.lev Ran-strt.lev + Rog-fila.lev Rog-filb.lev Rog-goal.lev Rog-loca.lev Rog-strt.lev + Sam-fila.lev Sam-filb.lev Sam-goal.lev Sam-loca.lev Sam-strt.lev + Tou-fila.lev Tou-filb.lev Tou-goal.lev Tou-loca.lev Tou-strt.lev + Val-fila.lev Val-filb.lev Val-goal.lev Val-loca.lev Val-strt.lev + Wiz-fila.lev Wiz-filb.lev Wiz-goal.lev Wiz-loca.lev Wiz-strt.lev + air.lev asmodeus.lev astral.lev baalz.lev bigrm-1.lev + bigrm-2.lev bigrm-3.lev bigrm-4.lev bigrm-5.lev castle.lev + cmdhelp data dungeon earth.lev fakewiz1.lev + fakewiz2.lev fire.lev help hh history + juiblex.lev knox.lev license medusa-1.lev medusa-2.lev + minefill.lev minend-1.lev minend-2.lev minetn-1.lev minetn-2.lev + nethack.cmd nethack.cnf nethack.exe nethack.ico opthelp + options oracle.lev oracles orcus.lev quest.dat + recover.exe rumors sanctum.lev soko1-1.lev soko1-2.lev + soko2-1.lev soko2-2.lev soko3-1.lev soko3-2.lev soko4-1.lev + soko4-2.lev tower1.lev tower2.lev tower3.lev valley.lev + water.lev wizard1.lev wizard2.lev wizard3.lev wizhelp + + Yes. It's 112 files for a full featured NetHack 3.4. If any of the files + are missing, try to rerun make. If that doesn't help, you'll have to try + to decipher the makefile to find out how to manually create the missing + files. These kinds of troubles shouldn't happen except for two reasons: + You've run out of disk space while compiling or your make utility doesn't + understand the makefile properly for some reason. In either case, you + should get some warnings from the make, though. + + If you have old record, logfile, or news files in the game directory, they + are not overwritten. Of course, old records from NetHack 3.1 and 3.2 are + not worth keeping with 3.4, since these games are really quite different. + + Edit file nethack.cnf in the game directory to reflect your particular + setup and personal preferences, following the comments there. More info + about settable options can be found in the file opthelp and the guidebook. + + If you compiled in the TERMLIB feature, also move the sys\share\termcap + file to your game directory. + +8. If you'll be running NetHack from a different subdirectory, you will want + to "set HACKDIR=c:\games\nh33" (or whatever directory you want to use). + Add it to your config.sys, if you'll be playing often. + + You can also create a special NetHack entry in your Presentation Manager / + Workplace Shell desktop. This will use the included NetHack icon. + The following is a sample program description for OS/2 1.3 desktop, but + it's similar for OS/2 2.0: + + Program title: NetHack 3.4 + Path and file name: c:\games\nh33\nethack.cmd + Parameters: + Working directory: c:\games\nh33 + Program type: OS/2 Full screen + + Naturally you must fill in your own game directory and parameters if you + want to set any. The program type can be either OS/2 Full screen or OS/2 + Windowed. Note that you should set the executable path to use the .cmd + file generated by the makefile. This file generates an extra pause after + the program exit, because otherwise you wouldn't get to see the high score + list upon quitting due to PM/WPS automatically closing the program window. + When starting NetHack normally from OS/2 command prompt, the command + processor starts nethack.exe instead, so no extra pause is generated. + +9. If you want to clear up the temporary files and objects created by the + compilation process, you may issue "make spotless". This will return your + source tree to near-distribution condition. Naturally, it will not affect + your newly built game files in any way. + +10. Play NetHack. If it works, you're done! + + +Notes +----- + +1) Save-files and bones-files from previous versions will not work with + NetHack 3.4. Don't bother trying to keep them. + +2) To install an update of NetHack after changing something, enter "make" + from the src directory. If you add, delete, or reorder monsters or + objects, or you change the format of saved level files, delete any save + and bones files. (Trying to use such files sometimes produces amusing + confusions on the game's part, but usually crashes.) + +3) When cross-compiling for OS/2 in DOS, NDMAKE is the best choice because it + requires the least RAM for itself. Note however, that cross-compilation + in DOS is discouraged, because it is considered obsolete (OS/2 is really + a much better place to compile). If you still want to try, here are some + suggestions: + + During linking, Microsoft linker will need temporary storage space. Make + sure you have about 1 MB of free disk where ever you have defined your + temporary storage. It is also a good idea to compile with as much free + RAM as possible. It may otherwise get crowded with the bigger, more + complex source files (compiler bombs with "out of heap space" or similar). + If this happens, strip your configuration, zap TSR's, get a better memory + manager etc. + +4) The file sys\share\termcap.uu is the fixed version of the Fred Fish + termcap library. You will need to run a uudecode utility on it to + generate the file termcap.zip. termcap.zip contains several files of + termcap routines. Using them with NetHack involves very little knowledge + of the UNIX concept of a termcap database; mostly you need to know enough + to set a TERM environment variable. You can unzip termcap.zip in the + sys\share directory, but if you are going to use it, it is probably best + to unzip a copy in the src directory. That way you will not miss copying + any files over. Wherever you unzip it, get rid of the included makefile + since a better version has been provided as Makefile.lib. After creating + the termcap library file termlib.lib, copy it to src before compiling the + game main source. + +5) When compiling with MSC 6.0, the maintenance version 6.0A should be used + instead of the original 6.0, which was all too buggy to successfully build + NetHack. + +6) Note that emx 0.8f is the first version of GCC for OS/2 that can properly + compile NetHack. Earlier versions do not work, because they don't support + the 16 bit API calls of OS/2. + + GCC emx 0.8f does not currently work properly when fseek() function is + used with text files. This is well documented in the compiler's + documentation. Unfortunately NetHack uses fseek() in several places in + connection with text data. This means that some help texts may not come + out right, but no serious problems should emerge. diff --git a/sys/os2/Makefile.os2 b/sys/os2/Makefile.os2 new file mode 100644 index 0000000..f6abd8a --- /dev/null +++ b/sys/os2/Makefile.os2 @@ -0,0 +1,1782 @@ +# SCCS Id: @(#)Makefile.os2 3.4.3 1996/10/29 +# OS/2 NetHack 3.4.3 Makefile for OS/2 versions 2.x +# Copyright (C) 1990, 1991, 1992, 1993, 1996 Timo Hakulinen +# +# Several compilers exist for OS/2 but, currently only GCC emx is tested +# and used for releases. make programs other than dmake are not tested. +# +# Supported compilers: GCC emx 0.9g +# +# DMAKE is required. Credit for the makefile improvements goes to +# Pekka Rousu. +# +# Copy this file into $(SRC) directory, rename it to "makefile" +# (important, many targets rely on it), compile and link inside +# $(SRC). If required, termcap library can be built from termcap +# sources using makefile.lib in "sys\share" directory. +# +# "GCC" refers to GCC emx only. No other ports of GCC are supported. +# Additional credits for honing GCC support for 3.2 go to Ronald +# Van Iwaarden (ron@vaniwaarden.org) and Stefan Neis (neis@cs.uni-sb.de). +# +# "OMF" is short for "Object Module Format" and refers to the +# standard OS/2 object format, which e.g. link386 uses. MSC and +# CSet/2 always produce OMF object files, and GCC can be instructed +# to produce them with proper switches (see below). +# +# "a.out" refers to Unix object file format, which is used by GCC +# in its default compilation mode. These object files must be +# linked using GCC's own linker to produce a proper OS/2 executable. +# GDB debugger shipped with GCC can only be used with a.out object +# format. +# +# Note that the default setup in this makefile is my personal setup, +# which you will have to adapt to your configuration. +# + +# +# Compiler and linker selection. +# + +#format = omf +format = a.out + +.IF $(format) == a.out +with_x11 = yes +#debug = yes +.END + + +CC = gcc # GCC + +.IF $(format) == a.out +LINK = gcc +#LINK = link386 # GCC OMF, CSet/2 +.ELSE +LINK = link386 # GCC OMF, CSet/2 +LFLAGS = /noig /stack:40000 +.END + +.IF $(with_x11) == yes +WINX11OBJ01 = $(OBJ)/Window.o +WINX11OBJ02 = $(OBJ)/dialogs.o +WINX11OBJ03 = $(OBJ)/winX.o +WINX11OBJ04 = $(OBJ)/winmap.o +WINX11OBJ05 = $(OBJ)/winmenu.o +WINX11OBJ06 = $(OBJ)/winmesg.o +WINX11OBJ07 = $(OBJ)/winmisc.o +WINX11OBJ08 = $(OBJ)/winstat.o +WINX11OBJ09 = $(OBJ)/wintext.o +WINX11OBJ10 = $(OBJ)/winval.o +WINX11OBJ11 = $(OBJ)/tile.o +X11ROOT = e:/xfree86 +WINX11CFLAGS = -DUSE_XPM -DX11_GRAPHICS \ + -I$(X11ROOT)/include -Zmtd +WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 -lXpm -L$(X11ROOT)/lib -lc_app +WINX11SRC = ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \ + ../win/X11/winmap.c ../win/X11/winmenu.c ../win/X11/winmesg.c \ + ../win/X11/winmisc.c ../win/X11/winstat.c ../win/X11/wintext.c \ + ../win/X11/winval.c tile.c +WINX11OBJ = $(WINX11OBJ01) $(WINX11OBJ02) $(WINX11OBJ03) $(WINX11OBJ04) \ + $(WINX11OBJ05) $(WINX11OBJ06) $(WINX11OBJ07) $(WINX11OBJ08) \ + $(WINX11OBJ09) $(WINX11OBJ10) $(WINX11OBJ11) +WINX11VARDAT=x11tiles pet_mark.xbm rip.xpm +X11ECHO = $(CMD) @echo +.END + + + + +MAKEB = dmake +CMD = cmd /C +AB = $(@:B).c +CB = $$(@:B).c +BEG = $(CMD) " +END = " +SEP = & +P = % + +# +# Most makes execute actions automatically inside a subshell, +# which makes even the shell internals work ok. +# + +ECHO = $(CMD) @echo +RM = $(CMD) del +CP = $(CMD) copy +CAT = $(CMD) type + +# +# For those of us who have these on PC. +# + +#YACC = yacc +#LEX = lex +YACC = bison -y +LEX = flex + +# +# For extracting NetHack icon. +# + +UUDECODE = uudecode + +# +# For people with TeX and LaTeX. +# + +LATEX = latex + +# +# If you have TOUCH, some things become slightly easier. +# + +TOUCH = touch + +# +# Standard file naming for LEX and YACC output may vary in PC +# installations. These three are probably the most generally used +# names. +# + +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + +# +# Source tree base directory. +# + +NHSRC = \nethack + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = $(NHSRC)\include # NetHack include files +DAT = $(NHSRC)\dat # NetHack data files +DOC = $(NHSRC)\doc # NetHack documentation files +UTIL = $(NHSRC)\util # Utility source +SRC = $(NHSRC)\src # Main source +WIN = $(NHSRC)\win\tty # Window system specific source +WINX11 = $(NHSRC)\win\x11 # Window system specific source +SYS = $(NHSRC)\sys\os2 # System specific source +SSYS = $(NHSRC)\sys\share # Shared system files +WINSHARE= $(NHSRC)\win\share # Shared system files + +# +# Modifiable directories. Set these according to your setup and +# preferences. They must all be present prior to compilation. +# OBJ, TEMP and GAMEDIR should all preferably be separate and, +# in particular, not the same as any of the source directories. +# Note that DMAKE may dislike drive designators in paths because +# it misinterprets the colon as being part of a make rule. In that +# case, all directories have to reside on the same drive. +# + +OBJ = \tmp\obj # Object files +TEMP = \tmp\bin # Temporary files during make process +GAMEDIR = \games\nh343x11 # Game directory +PLIBP = c:\emx\lib # Protected mode C libraries +RLIBP = c:\emx\lib # Possible real mode C libraries + +# +# The game name and description. +# + +GAME = nethack +GAMEDES = "NetHack 3.4.3" + +# +# The uppermost two lines for MSC, the middle two for GCC, and +# the lowermost two for CSet/2. +# +# GCC: compile only, compiler id, object format selection, warnings, +# include file path, debug flags, ANSI conformance. +# + +CFLAGS = -c $(GCCO) $(WARN) -I$(INCL) $(CDFLAGS) $(STDC) $(WINX11CFLAGS) +#OPT = -s -O -o +OPT = -o + +# +# Compiler warning levels. These are really for development, so +# they are commented out in general distribution to save the user +# from masses of benign warnings. If any problems arise, however, +# they may help in finding the trouble. +# +# GCC: max. reasonable GCC warning levels. Can't use -Wall, because then +# it would whine about all the zillions of unused declarations etc. +# Even with these switches you'll get a lot of warnings, but they should +# all be benign. +# + +WARN = #-W -Wimplicit -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -DGCC_WARN # GCC + +# +# GCC object format selection. The upper line for standard OS/2 OMF +# object format, the lower for Unix style a.out format. +# + +.IF $(format) == omf +GCCO = -Zomf -Zsys +.ELSE +GCCO = +.END + +# +# MSC 5.1 needs the large model first pass of the compiler. +# Not needed for later versions. +# + +BIGC = + +# +# Unset CL to avoid troubles with conflicting switches in MSC 6.0. +# + +CL = + +# +# Prepare for a debugger. +# + +.IF $(debug) == yes +CDFLAGS = +LDFLAGS = +.ELSE +CDFLAGS = -O -s +LDFLAGS = -s +.END + +# +# How to produce the most ANSI-like environment. +# + +STDC = -ansi # GCC + +# +# Possible system object files required during linking. +# + +.IF $(format) == omf +SYSOBJ = $(PLIBP)\crt0.obj $(PLIBP)\end.lib# GCC OMF +.ELSE +SYSOBJ = # MSC, GCC a.out, CSet/2 +.END + +# +# Compiler library selection. Change if necessary. +# +# GCC emx 0.9 OMF: C single-threaded libs, Unix system call alias lib, +# extra GCC lib, single threaded system lib, OS/2 API entry points. +# Note that emx changed library naming convention between 0.8 and 0.9. +# +# GCC a.out: extra GCC lib, C standard lib, extra GCC lib (again), +# OS/2 API entry points. +# + +.IF $(format) == omf +PLIBS = $(PLIBP)\st\c $(PLIBP)\st\c_app $(PLIBP)\c_alias $(PLIBP)\gcc $(PLIBP)\st\sys $(PLIBP)\os2 # GCC emx 0.9 OMF +.ELSE +PLIBS = -lgcc -lc -lgcc -los2 $(X11LIBS) # GCC a.out +.END + +# +# C libraries used by makedefs, lev_comp and dgn_comp (change if +# necessary). If compilation is done in DOS, enable the upper line +# possibly setting the library name to something else, if in OS/2, +# enable the lower line (protected mode libraries). +# + +#RLIBS = $(RLIBP)\llibcer +RLIBS = $(PLIBS) + +SRCCC = $(CC) $(CFLAGS) $(OPT) $@ $(AB) +UTILCC = $(BEG) cd $(UTIL) $(SEP) $(CC) $(CFLAGS) $(OPT) $@ $(AB) $(END) +SYSCC = $(BEG) cd $(SYS) $(SEP) $(CC) $(CFLAGS) $(OPT) $@ $(AB) $(END) +SSYSCC = $(BEG) cd $(SSYS) $(SEP) $(CC) $(CFLAGS) $(OPT) $@ $(AB) $(END) +PSYSCC = $(BEG) cd $(SSYS) $(SEP) $(CC) $(CFLAGS) $(OPT) $@ pc$(AB) $(END) +WINCC = $(BEG) cd $(WIN) $(SEP) $(CC) $(CFLAGS) $(OPT) $@ $(AB) $(END) + +# +# Default linker skeletons. The upper six lines for everything +# that uses standard OS/2 object format (GCC OMF), The lower six +# for GCC a.out format. +# + +.IF $(format) == omf +GAMELN = $(LINK) @$(TEMP)\$(GAME).rsp +MKDFLN = $(LINK) @$(TEMP)\makedefs.rsp +LEVCLN = $(LINK) @$(TEMP)\lev_comp.rsp +DGNCLN = $(LINK) @$(TEMP)\dgn_comp.rsp +RCVRLN = $(LINK) @$(TEMP)\recover.rsp +DLBRLN = $(LINK) @$(TEMP)\dlb.rsp +.ELSE +GAMELN = $(CC) $(LDFLAGS) -o $(GAMEDIR)\$(GAME).exe @$(TEMP)\$(GAME).r $(PLIBS) $(WINX11CFLAGS) $(WINX11LIB) +MKDFLN = $(CC) $(LDFLAGS) -o $(TEMP)\makedefs.exe $(TEMP)\$(MKDFDEF) $(SYSOBJ) $(MAKEOBJS) $(PLIBS) +LEVCLN = $(CC) $(LDFLAGS) -o $(TEMP)\lev_comp.exe $(TEMP)\$(LEVCDEF) $(SYSOBJ) $(SPLEVOBJS) $(PLIBS) +DGNCLN = $(CC) $(LDFLAGS) -o $(TEMP)\dgn_comp.exe $(TEMP)\$(DGNCDEF) $(SYSOBJ) $(DGNCOMPOBJS) $(PLIBS) +RCVRLN = $(CC) $(LDFLAGS) -o $(GAMEDIR)\recover.exe $(TEMP)\$(RCVRDEF) $(SYSOBJ) $(RECOVOBJS) $(PLIBS) + DLBRLN = $(CC) $(LDFLAGS) -o $(TEMP)\dlb.exe $(TEMP)\$(DLBDEF) $(SYSOBJ) $(DLBOBJS) $(PLIBS) +.END + +# +# OS/2 module definition files for NetHack, +# makedefs, dgn_comp, lev_comp, recover, dlb. +# + +GAMEDEF = $(GAME).def +MKDFDEF = makedefs.def +LEVCDEF = lev_comp.def +DGNCDEF = dgn_comp.def +RCVRDEF = recover.def +DLBDEF = dlb.def + +# +# For compilation in DOS, enable the lower three lines and +# disable the upper three. +# + +MKDFMD = $(TEMP)\$(MKDFDEF) +LEVCMD = $(TEMP)\$(LEVCDEF) +DGNCMD = $(TEMP)\$(DGNCDEF) +#MKDFMD = +#LEVCMD = +#DGNCMD = + +# +# Optional high-quality BSD random number generation routines +# (see os2conf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# If TERMLIB is defined in os2conf.h, comment out the upper line and +# uncomment the lower. If the termcap-library doesn't exist, use +# sys\share\makefile.lib to build it. +# + +TERMLIB = +#TERMLIB = termlib.lib + +# +# Short / long file name selection for FAT and HPFS. +# Only three files need consideration. +# + +#GUIDEBOO = Guideboo # FAT +#PATCHLEV = patchlev # - " - +#DATABASE = data.bas # - " - +GUIDEBOO = Guidebook # HPFS +PATCHLEV = patchlevel # - " - +DATABASE = data.base # - " - + + +# +# If you have LaTeX and want to create the NetHack Guidebook in TeX +# device-independent file format, comment out the upper line and +# uncomment the lower. +# + +GUIDE = +#GUIDE = $(TEMP)\$(GUIDEBOO).dvi + +# +# Set WINOBJ lines corresponding to your desired combination +# of windowing systems. Also set windowing systems in config.h. +# +# A straight tty port using no native windowing system is the +# only choice for now. +# + +WINOBJ1 = $(OBJ)\getline.o +WINOBJ2 = $(OBJ)\termcap.o +WINOBJ3 = $(OBJ)\topl.o +WINOBJ4 = $(OBJ)\wintty.o +WINOBJ = $(WINOBJ1) $(WINOBJ2) $(WINOBJ3) $(WINOBJ4) $(WINX11OBJ) + +# +# The default make target, so just typing 'make' is useful. +# Has to be the first target in the makefile. +# + +default : all + +# +# If you have yacc and lex programs and make any changes, uncomment +# the lowermost two lines and comment out the others. If you make +# changes to the .y and .l files but prefer processing the files +# separately elsewhere, activate the middle two lines, so your changes +# don't get overwritten. +# + +do_yacc : yacc_cpy # use pre-generated files +do_lex : lex_cpy # - " - +#do_yacc : yacc_msg # show message if changed +#do_lex : lex_msg # - " - +#do_yacc : yacc_act # re-process files +#do_lex : lex_act # - " - + +# +# If you have the TOUCH utility the upper line is ok. Otherwise +# the lower one does the same albeit in an ugly manner. Besides, +# the latter method only works for text files. +# + +#do_touch : realtouch +do_touch : faketouch + +# +# If you don't have uudecode program, use the upper line. +# If you still want the icon, you'll have to extract the +# file manually somewhere else. +# + +do_icon : icon_msg # show message if changed +#do_icon : icon_act # extract icon file + +# +# If you don't want to generate nethack.cmd, use the upper line. +# This could be the case, e.g., if you use a different shell than +# the standard cmd.exe. +# + +#do_cmd : cmd_msg # show message +do_cmd : cmd_act # generate nethack.cmd + +# +# If you want to try the data librarian scheme to reduce +# the amount of data files in the NetHack home directory, comment +# out the lower line and uncomment the upper. Also, make sure +# that DLB is defined in config.h. +# + +do_dlb : dlb_yup +#do_dlb : dlb_nope + +###################################################################### +# +# Nothing below this line should have to be changed. +# +# Other things that have to be reconfigured are in +# config.h, os2conf.h and possibly system.h. +# + +# +# The game filename. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +# +# Object files for makedefs. +# + +MAKEOBJS = $(OBJ)\makedefs.o $(OBJ)\monst.o $(OBJ)\objects.o + +# +# Object files for special levels compiler. +# + +SOBJ01 = $(OBJ)\lev_yacc.o $(OBJ)\lev_lex.o $(OBJ)\lev_main.o $(OBJ)\alloc.o +SOBJ02 = $(OBJ)\monst.o $(OBJ)\objects.o $(OBJ)\panic.o $(OBJ)\decl.o +SOBJ03 = $(OBJ)\drawing.o + +SPLEVOBJS = $(SOBJ01) $(SOBJ02) $(SOBJ03) + +# +# Object files for dungeon compiler. +# + +DOBJ01 = $(OBJ)\dgn_yacc.o $(OBJ)\dgn_lex.o $(OBJ)\dgn_main.o +DOBJ02 = $(OBJ)\panic.o $(OBJ)\alloc.o + +DGNCOMPOBJS = $(DOBJ01) $(DOBJ02) + +# +# Object files for recovery utility. +# + +RECOVOBJS = $(OBJ)\recover.o + +# +# Object files for dlb. +# + +DLBOBJS = $(OBJ)\dlb_main.o $(OBJ)\dlb.o $(OBJ)\alloc.o $(OBJ)\panic.o + +# +# Data files for dlb. +# + +DATHELP = \ + help hh cmdhelp history opthelp wizhelp + +SPEC_LEVS = \ + asmodeus.lev baalz.lev bigrm-1.lev \ + bigrm-2.lev bigrm-3.lev bigrm-4.lev castle.lev fakewiz1.lev fakewiz2.lev \ + juiblex.lev knox.lev medusa-1.lev medusa-2.lev minend-1.lev minend-2.lev \ + minend-3.lev minefill.lev minetn-1.lev minetn-2.lev minetn-3.lev minetn-4.lev \ + minetn-5.lev minetn-6.lev minetn-7.lev oracle.lev orcus.lev sanctum.lev \ + tower1.lev tower2.lev tower3.lev valley.lev wizard1.lev wizard2.lev \ + wizard3.lev astral.lev air.lev earth.lev fire.lev water.lev \ + soko1-1.lev soko1-2.lev soko2-1.lev soko2-2.lev \ + soko3-1.lev soko3-2.lev soko4-1.lev soko4-2.lev + +QUEST_LEVS = \ + Arc-goal.lev Arc-fila.lev Arc-filb.lev Arc-loca.lev Arc-strt.lev \ + Bar-goal.lev Bar-fila.lev Bar-filb.lev Bar-loca.lev Bar-strt.lev \ + Cav-goal.lev Cav-fila.lev Cav-filb.lev Cav-loca.lev Cav-strt.lev \ + Hea-goal.lev Hea-fila.lev Hea-filb.lev Hea-loca.lev Hea-strt.lev \ + Kni-goal.lev Kni-fila.lev Kni-filb.lev Kni-loca.lev Kni-strt.lev \ + Mon-goal.lev Mon-fila.lev Mon-filb.lev Mon-loca.lev Mon-strt.lev \ + Pri-goal.lev Pri-fila.lev Pri-filb.lev Pri-loca.lev Pri-strt.lev \ + Ran-goal.lev Ran-fila.lev Ran-filb.lev Ran-loca.lev Ran-strt.lev \ + Rog-goal.lev Rog-fila.lev Rog-filb.lev Rog-loca.lev Rog-strt.lev \ + Sam-goal.lev Sam-fila.lev Sam-filb.lev Sam-loca.lev Sam-strt.lev \ + Tou-goal.lev Tou-fila.lev Tou-filb.lev Tou-loca.lev Tou-strt.lev \ + Val-goal.lev Val-fila.lev Val-filb.lev Val-loca.lev Val-strt.lev \ + Wiz-goal.lev Wiz-fila.lev Wiz-filb.lev Wiz-loca.lev Wiz-strt.lev +VARDATD = \ + data oracles options quest.dat rumors $(WINX11VARDAT) + +DATDLB = $(DATHELP) dungeon $(SPEC_LEVS) $(QUEST_LEVS) $(VARDATD) \ + $(do_dlb) + +# +# Object files for the game itself. +# + +VOBJ011 = $(OBJ)\allmain.o +VOBJ012 = $(OBJ)\alloc.o +VOBJ013 = $(OBJ)\apply.o +VOBJ014 = $(OBJ)\artifact.o +VOBJ021 = $(OBJ)\attrib.o +VOBJ022 = $(OBJ)\ball.o +VOBJ023 = $(OBJ)\bones.o +VOBJ024 = $(OBJ)\botl.o +VOBJ031 = $(OBJ)\cmd.o +VOBJ032 = $(OBJ)\dbridge.o +VOBJ033 = $(OBJ)\decl.o +VOBJ034 = $(OBJ)\detect.o +VOBJ041 = $(OBJ)\dig.o +VOBJ042 = $(OBJ)\display.o +VOBJ043 = $(OBJ)\dlb.o +VOBJ044 = $(OBJ)\do.o +VOBJ051 = $(OBJ)\do_name.o +VOBJ052 = $(OBJ)\do_wear.o +VOBJ053 = $(OBJ)\dog.o +VOBJ054 = $(OBJ)\dogmove.o +VOBJ061 = $(OBJ)\dokick.o +VOBJ062 = $(OBJ)\dothrow.o +VOBJ063 = $(OBJ)\drawing.o +VOBJ064 = $(OBJ)\dungeon.o +VOBJ071 = $(OBJ)\eat.o +VOBJ072 = $(OBJ)\end.o +VOBJ073 = $(OBJ)\engrave.o +VOBJ074 = $(OBJ)\exper.o +VOBJ071 = $(OBJ)\eat.o +VOBJ072 = $(OBJ)\end.o +VOBJ073 = $(OBJ)\engrave.o +VOBJ074 = $(OBJ)\exper.o +VOBJ081 = $(OBJ)\explode.o +VOBJ082 = $(OBJ)\extralev.o +VOBJ083 = $(OBJ)\files.o +VOBJ084 = $(OBJ)\fountain.o +VOBJ091 = $(OBJ)\hack.o +VOBJ092 = $(OBJ)\hacklib.o +VOBJ093 = $(OBJ)\invent.o +VOBJ094 = $(OBJ)\light.o +VOBJ101 = $(OBJ)\lock.o +VOBJ102 = $(OBJ)\mail.o +VOBJ103 = $(OBJ)\main.o +VOBJ104 = $(OBJ)\makemon.o +VOBJ111 = $(OBJ)\mapglyph.o +VOBJ112 = $(OBJ)\mcastu.o +VOBJ113 = $(OBJ)\mhitm.o +VOBJ114 = $(OBJ)\mhitu.o +VOBJ115 = $(OBJ)\minion.o +VOBJ121 = $(OBJ)\mklev.o +VOBJ122 = $(OBJ)\mkmap.o +VOBJ123 = $(OBJ)\mkmaze.o +VOBJ124 = $(OBJ)\mkobj.o +VOBJ131 = $(OBJ)\mkroom.o +VOBJ132 = $(OBJ)\mon.o +VOBJ133 = $(OBJ)\mondata.o +VOBJ134 = $(OBJ)\monmove.o +VOBJ141 = $(OBJ)\monst.o +VOBJ142 = $(OBJ)\monstr.o +VOBJ143 = $(OBJ)\mplayer.o +VOBJ144 = $(OBJ)\mthrowu.o +VOBJ151 = $(OBJ)\muse.o +VOBJ152 = $(OBJ)\music.o +VOBJ153 = $(OBJ)\o_init.o +VOBJ154 = $(OBJ)\objects.o +VOBJ161 = $(OBJ)\objnam.o +VOBJ162 = $(OBJ)\options.o +VOBJ163 = $(OBJ)\os2.o +VOBJ164 = $(OBJ)\pager.o +VOBJ171 = $(OBJ)\pcsys.o +VOBJ172 = $(OBJ)\pickup.o +VOBJ173 = $(OBJ)\pline.o +VOBJ174 = $(OBJ)\polyself.o +VOBJ181 = $(OBJ)\potion.o +VOBJ182 = $(OBJ)\pray.o +VOBJ183 = $(OBJ)\priest.o +VOBJ184 = $(OBJ)\quest.o +VOBJ191 = $(OBJ)\questpgr.o +VOBJ192 = $(OBJ)\read.o +VOBJ193 = $(OBJ)\rect.o +VOBJ194 = $(OBJ)\region.o +VOBJ195 = $(OBJ)\restore.o +VOBJ201 = $(OBJ)\rip.o +VOBJ202 = $(OBJ)\rnd.o +VOBJ203 = $(OBJ)\rumors.o +VOBJ204 = $(OBJ)\save.o +VOBJ211 = $(OBJ)\shk.o +VOBJ212 = $(OBJ)\shknam.o +VOBJ213 = $(OBJ)\sit.o +VOBJ214 = $(OBJ)\sounds.o +VOBJ221 = $(OBJ)\sp_lev.o +VOBJ222 = $(OBJ)\spell.o +VOBJ223 = $(OBJ)\steal.o +VOBJ224 = $(OBJ)\teleport.o +VOBJ231 = $(OBJ)\timeout.o +VOBJ232 = $(OBJ)\topten.o +VOBJ233 = $(OBJ)\track.o +VOBJ234 = $(OBJ)\trap.o +VOBJ241 = $(OBJ)\tty.o +VOBJ242 = $(OBJ)\u_init.o +VOBJ243 = $(OBJ)\uhitm.o +VOBJ244 = $(OBJ)\unix.o +VOBJ251 = $(OBJ)\vault.o +VOBJ252 = $(OBJ)\vision.o +VOBJ253 = $(OBJ)\vis_tab.o +VOBJ254 = $(OBJ)\weapon.o +VOBJ261 = $(OBJ)\were.o +VOBJ262 = $(OBJ)\wield.o +VOBJ263 = $(OBJ)\windows.o +VOBJ264 = $(OBJ)\wizard.o +VOBJ271 = $(OBJ)\worm.o +VOBJ272 = $(OBJ)\worn.o +VOBJ273 = $(OBJ)\write.o +VOBJ274 = $(OBJ)\zap.o +VOBJ281 = $(OBJ)\role.o +VOBJ282 = $(OBJ)\steed.o + +VOBJ01 = $(VOBJ011) $(VOBJ012) $(VOBJ013) $(VOBJ014) +VOBJ02 = $(VOBJ021) $(VOBJ022) $(VOBJ023) $(VOBJ024) +VOBJ03 = $(VOBJ031) $(VOBJ032) $(VOBJ033) $(VOBJ034) +VOBJ04 = $(VOBJ041) $(VOBJ042) $(VOBJ043) $(VOBJ044) +VOBJ05 = $(VOBJ051) $(VOBJ052) $(VOBJ053) $(VOBJ054) +VOBJ06 = $(VOBJ061) $(VOBJ062) $(VOBJ063) $(VOBJ064) +VOBJ07 = $(VOBJ071) $(VOBJ072) $(VOBJ073) $(VOBJ074) +VOBJ08 = $(VOBJ081) $(VOBJ082) $(VOBJ083) $(VOBJ084) +VOBJ09 = $(VOBJ091) $(VOBJ092) $(VOBJ093) $(VOBJ094) +VOBJ10 = $(VOBJ101) $(VOBJ102) $(VOBJ103) $(VOBJ104) +VOBJ11 = $(VOBJ111) $(VOBJ112) $(VOBJ113) $(VOBJ114) $(VOBJ115) +VOBJ12 = $(VOBJ121) $(VOBJ122) $(VOBJ123) $(VOBJ124) +VOBJ13 = $(VOBJ131) $(VOBJ132) $(VOBJ133) $(VOBJ134) +VOBJ14 = $(VOBJ141) $(VOBJ142) $(VOBJ143) $(VOBJ144) +VOBJ15 = $(VOBJ151) $(VOBJ152) $(VOBJ153) $(VOBJ154) +VOBJ16 = $(VOBJ161) $(VOBJ162) $(VOBJ163) $(VOBJ164) +VOBJ17 = $(VOBJ171) $(VOBJ172) $(VOBJ173) $(VOBJ174) +VOBJ18 = $(VOBJ181) $(VOBJ182) $(VOBJ183) $(VOBJ184) +VOBJ19 = $(VOBJ191) $(VOBJ192) $(VOBJ193) $(VOBJ194) $(VOBJ195) +VOBJ20 = $(VOBJ201) $(VOBJ202) $(VOBJ203) $(VOBJ204) +VOBJ21 = $(VOBJ211) $(VOBJ212) $(VOBJ213) $(VOBJ214) +VOBJ22 = $(VOBJ221) $(VOBJ222) $(VOBJ223) $(VOBJ224) +VOBJ23 = $(VOBJ231) $(VOBJ232) $(VOBJ233) $(VOBJ234) +VOBJ24 = $(VOBJ241) $(VOBJ242) $(VOBJ243) $(VOBJ244) +VOBJ25 = $(VOBJ251) $(VOBJ252) $(VOBJ253) $(VOBJ254) +VOBJ26 = $(VOBJ261) $(VOBJ262) $(VOBJ263) $(VOBJ264) +VOBJ27 = $(VOBJ271) $(VOBJ272) $(VOBJ273) $(VOBJ274) +VOBJ28 = $(VOBJ281) $(VOBJ282) +VOBJ29 = $(WINOBJ) $(RANDOM) +HHOBJ = $(OBJ)\version.o + +VOBJ = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) $(VOBJ06) $(VOBJ07) \ + $(VOBJ08) $(VOBJ09) $(VOBJ10) $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) \ + $(VOBJ15) $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) $(VOBJ21) \ + $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) $(VOBJ26) $(VOBJ27) $(VOBJ28) \ + $(VOBJ29) +HOBJ = $(VOBJ) $(HHOBJ) + +EXTERN_H = # $(INCL)\extern.h +OS2CONF_H = $(INCL)\os2conf.h $(INCL)\micro.h $(INCL)\system.h $(EXTERN_H) +GLOBAL_H = $(INCL)\global.h $(INCL)\coord.h $(OS2CONF_H) +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h $(GLOBAL_H) +TRAP_H = $(INCL)\trap.h +PERMONST_H = $(INCL)\permonst.h $(INCL)\monattk.h $(INCL)\monflag.h +YOU_H = $(INCL)\you.h $(INCL)\attrib.h $(PERMONST_H) $(INCL)\mondata.h \ + $(INCL)\monst.h $(INCL)\youprop.h $(INCL)\prop.h $(INCL)\pm.h +DECL_H = $(INCL)\decl.h $(INCL)\spell.h $(INCL)\obj.h $(YOU_H) \ + $(INCL)\onames.h $(INCL)\color.h +HACK_H = $(CONFIG_H) $(DECL_H) $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(TRAP_H) $(INCL)\engrave.h $(INCL)\flag.h \ + $(INCL)\rm.h $(INCL)\dungeon.h $(INCL)\hack.h $(INCL)\display.h \ + $(INCL)\vision.h $(INCL)\wintty.h $(INCL)\wintype.h $(INCL)\align.h \ + $(INCL)\winprocs.h + +# +# The default target. +# + +all : makedefs dgn_comp lev_comp recover $(GAME) dat $(GUIDE) + $(ECHO) Done. + +# +# Definition file creation. +# + +$(TEMP)\$(GAMEDEF) : + $(MAKEB) DD_NAME=$(GAME) DD_DESC=$(GAMEDES) DD_TARG=$@ do_def +$(TEMP)\$(MKDFDEF) : + $(MAKEB) DD_NAME=makedefs DD_DESC="Definitions compiler" DD_TARG=$@ do_def +$(TEMP)\$(DGNCDEF) : + $(MAKEB) DD_NAME=dgn_comp DD_DESC="Dungeon compiler" DD_TARG=$@ do_def +$(TEMP)\$(LEVCDEF) : + $(MAKEB) DD_NAME=lev_comp DD_DESC="Level compiler" DD_TARG=$@ do_def +$(TEMP)\$(RCVRDEF) : + $(MAKEB) DD_NAME=recover DD_DESC="Recovery utility" DD_TARG=$@ do_def +$(TEMP)\$(DLBDEF) : + $(MAKEB) DD_NAME=dlb DD_DESC="Archive utility" DD_TARG=$@ do_def + +do_def : + $(ECHO) NAME $(DD_NAME) WINDOWCOMPAT> $(DD_TARG) + $(ECHO) DESCRIPTION '$(DD_DESC)'>> $(DD_TARG) + $(ECHO) PROTMODE>> $(DD_TARG) + $(ECHO) EXETYPE OS2>> $(DD_TARG) + +# +# The main target. +# + +$(GAME) : $(GAMEDIR)\$(GAME).exe +$(GAME).exe : $(GAMEDIR)\$(GAME).exe +$(GAMEDIR)\$(GAME).exe : $(TEMP)\$(GAME).rsp $(TEMP)\$(GAME).r + $(GAMELN) + +$(TEMP)\$(GAME).r : $(HOBJ) $(TEMP)\$(GAMEDEF) + $(ECHO) $(VOBJ011) > $@ + $(ECHO) $(VOBJ012) >> $@ + $(ECHO) $(VOBJ013) >> $@ + $(ECHO) $(VOBJ014) >> $@ + $(ECHO) $(VOBJ021) >> $@ + $(ECHO) $(VOBJ022) >> $@ + $(ECHO) $(VOBJ023) >> $@ + $(ECHO) $(VOBJ024) >> $@ + $(ECHO) $(VOBJ031) >> $@ + $(ECHO) $(VOBJ032) >> $@ + $(ECHO) $(VOBJ033) >> $@ + $(ECHO) $(VOBJ034) >> $@ + $(ECHO) $(VOBJ041) >> $@ + $(ECHO) $(VOBJ042) >> $@ + $(ECHO) $(VOBJ043) >> $@ + $(ECHO) $(VOBJ044) >> $@ + $(ECHO) $(VOBJ051) >> $@ + $(ECHO) $(VOBJ052) >> $@ + $(ECHO) $(VOBJ053) >> $@ + $(ECHO) $(VOBJ054) >> $@ + $(ECHO) $(VOBJ061) >> $@ + $(ECHO) $(VOBJ062) >> $@ + $(ECHO) $(VOBJ063) >> $@ + $(ECHO) $(VOBJ064) >> $@ + $(ECHO) $(VOBJ071) >> $@ + $(ECHO) $(VOBJ072) >> $@ + $(ECHO) $(VOBJ073) >> $@ + $(ECHO) $(VOBJ074) >> $@ + $(ECHO) $(VOBJ081) >> $@ + $(ECHO) $(VOBJ082) >> $@ + $(ECHO) $(VOBJ083) >> $@ + $(ECHO) $(VOBJ084) >> $@ + $(ECHO) $(VOBJ091) >> $@ + $(ECHO) $(VOBJ092) >> $@ + $(ECHO) $(VOBJ093) >> $@ + $(ECHO) $(VOBJ094) >> $@ + $(ECHO) $(VOBJ101) >> $@ + $(ECHO) $(VOBJ102) >> $@ + $(ECHO) $(VOBJ103) >> $@ + $(ECHO) $(VOBJ104) >> $@ + $(ECHO) $(VOBJ111) >> $@ + $(ECHO) $(VOBJ112) >> $@ + $(ECHO) $(VOBJ113) >> $@ + $(ECHO) $(VOBJ114) >> $@ + $(ECHO) $(VOBJ115) >> $@ + $(ECHO) $(VOBJ121) >> $@ + $(ECHO) $(VOBJ122) >> $@ + $(ECHO) $(VOBJ123) >> $@ + $(ECHO) $(VOBJ124) >> $@ + $(ECHO) $(VOBJ131) >> $@ + $(ECHO) $(VOBJ132) >> $@ + $(ECHO) $(VOBJ133) >> $@ + $(ECHO) $(VOBJ134) >> $@ + $(ECHO) $(VOBJ141) >> $@ + $(ECHO) $(VOBJ142) >> $@ + $(ECHO) $(VOBJ143) >> $@ + $(ECHO) $(VOBJ144) >> $@ + $(ECHO) $(VOBJ151) >> $@ + $(ECHO) $(VOBJ152) >> $@ + $(ECHO) $(VOBJ153) >> $@ + $(ECHO) $(VOBJ154) >> $@ + $(ECHO) $(VOBJ161) >> $@ + $(ECHO) $(VOBJ162) >> $@ + $(ECHO) $(VOBJ163) >> $@ + $(ECHO) $(VOBJ164) >> $@ + $(ECHO) $(VOBJ171) >> $@ + $(ECHO) $(VOBJ172) >> $@ + $(ECHO) $(VOBJ173) >> $@ + $(ECHO) $(VOBJ174) >> $@ + $(ECHO) $(VOBJ181) >> $@ + $(ECHO) $(VOBJ182) >> $@ + $(ECHO) $(VOBJ183) >> $@ + $(ECHO) $(VOBJ184) >> $@ + $(ECHO) $(VOBJ191) >> $@ + $(ECHO) $(VOBJ192) >> $@ + $(ECHO) $(VOBJ193) >> $@ + $(ECHO) $(VOBJ194) >> $@ + $(ECHO) $(VOBJ195) >> $@ + $(ECHO) $(VOBJ201) >> $@ + $(ECHO) $(VOBJ202) >> $@ + $(ECHO) $(VOBJ203) >> $@ + $(ECHO) $(VOBJ204) >> $@ + $(ECHO) $(VOBJ211) >> $@ + $(ECHO) $(VOBJ212) >> $@ + $(ECHO) $(VOBJ213) >> $@ + $(ECHO) $(VOBJ214) >> $@ + $(ECHO) $(VOBJ221) >> $@ + $(ECHO) $(VOBJ222) >> $@ + $(ECHO) $(VOBJ223) >> $@ + $(ECHO) $(VOBJ224) >> $@ + $(ECHO) $(VOBJ231) >> $@ + $(ECHO) $(VOBJ232) >> $@ + $(ECHO) $(VOBJ233) >> $@ + $(ECHO) $(VOBJ234) >> $@ + $(ECHO) $(VOBJ241) >> $@ + $(ECHO) $(VOBJ242) >> $@ + $(ECHO) $(VOBJ243) >> $@ + $(ECHO) $(VOBJ244) >> $@ + $(ECHO) $(VOBJ251) >> $@ + $(ECHO) $(VOBJ252) >> $@ + $(ECHO) $(VOBJ253) >> $@ + $(ECHO) $(VOBJ254) >> $@ + $(ECHO) $(VOBJ261) >> $@ + $(ECHO) $(VOBJ262) >> $@ + $(ECHO) $(VOBJ263) >> $@ + $(ECHO) $(VOBJ264) >> $@ + $(ECHO) $(VOBJ271) >> $@ + $(ECHO) $(VOBJ272) >> $@ + $(ECHO) $(VOBJ273) >> $@ + $(ECHO) $(VOBJ274) >> $@ + $(ECHO) $(VOBJ281) >> $@ + $(ECHO) $(VOBJ282) >> $@ + $(ECHO) $(WINOBJ1) >> $@ + $(ECHO) $(WINOBJ2) >> $@ + $(ECHO) $(WINOBJ3) >> $@ + $(ECHO) $(WINOBJ4) >> $@ + $(ECHO) $(HHOBJ) >> $@ + $(ECHO) $(RANDOM) >> $@ +.IF $(with_x11) == yes + $(X11ECHO) $(WINX11OBJ01) >> $@ + $(X11ECHO) $(WINX11OBJ02) >> $@ + $(X11ECHO) $(WINX11OBJ03) >> $@ + $(X11ECHO) $(WINX11OBJ04) >> $@ + $(X11ECHO) $(WINX11OBJ05) >> $@ + $(X11ECHO) $(WINX11OBJ06) >> $@ + $(X11ECHO) $(WINX11OBJ07) >> $@ + $(X11ECHO) $(WINX11OBJ08) >> $@ + $(X11ECHO) $(WINX11OBJ09) >> $@ + $(X11ECHO) $(WINX11OBJ10) >> $@ + $(X11ECHO) $(WINX11OBJ11) >> $@ +.END + + + +$(TEMP)\$(GAME).rsp : $(HOBJ) $(TEMP)\$(GAMEDEF) + $(ECHO) $(SYSOBJ) $(VOBJ01) +> $@ + $(ECHO) $(VOBJ02) +>> $@ + $(ECHO) $(VOBJ03) +>> $@ + $(ECHO) $(VOBJ04) +>> $@ + $(ECHO) $(VOBJ05) +>> $@ + $(ECHO) $(VOBJ06) +>> $@ + $(ECHO) $(VOBJ07) +>> $@ + $(ECHO) $(VOBJ08) +>> $@ + $(ECHO) $(VOBJ09) +>> $@ + $(ECHO) $(VOBJ10) +>> $@ + $(ECHO) $(VOBJ11) +>> $@ + $(ECHO) $(VOBJ12) +>> $@ + $(ECHO) $(VOBJ13) +>> $@ + $(ECHO) $(VOBJ14) +>> $@ + $(ECHO) $(VOBJ15) +>> $@ + $(ECHO) $(VOBJ16) +>> $@ + $(ECHO) $(VOBJ17) +>> $@ + $(ECHO) $(VOBJ18) +>> $@ + $(ECHO) $(VOBJ19) +>> $@ + $(ECHO) $(VOBJ20) +>> $@ + $(ECHO) $(VOBJ21) +>> $@ + $(ECHO) $(VOBJ22) +>> $@ + $(ECHO) $(VOBJ23) +>> $@ + $(ECHO) $(VOBJ24) +>> $@ + $(ECHO) $(VOBJ25) +>> $@ + $(ECHO) $(VOBJ26) +>> $@ + $(ECHO) $(VOBJ27) +>> $@ + $(ECHO) $(VOBJ28) +>> $@ + $(ECHO) $(VOBJ29) +>> $@ + $(ECHO) $(VOBJ30) +>> $@ + $(ECHO) $(HHOBJ)>> $@ + $(ECHO) $(GAMEDIR)\$(GAME).exe>> $@ + $(ECHO) $(TEMP)\$(GAME)>> $@ + $(ECHO) $(PLIBS) $(TERMLIB)>> $@ + $(ECHO) $(TEMP)\$(GAMEDEF) $(LFLAGS);>> $@ + +# +# Targets for makedefs. +# + +makedefs : $(TEMP)\makedefs.exe +$(TEMP)\makedefs.exe : $(TEMP)\makedefs.rsp + $(MKDFLN) + +$(TEMP)\makedefs.rsp : $(MAKEOBJS) $(TEMP)\$(MKDFDEF) + $(ECHO) $(SYSOBJ) $(MAKEOBJS)> $@ + $(ECHO) $(TEMP)\makedefs.exe>> $@ + $(ECHO) nul>> $@ + $(ECHO) $(RLIBS)>> $@ + $(ECHO) $(MKDFMD) $(LFLAGS);>> $@ + +$(OBJ)\makedefs.o : $(UTIL)\$(CB) $(CONFIG_H) $(INCL)\permonst.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\artilist.h $(INCL)\qtext.h + $(UTILCC) + +# +# Targets for the special levels compiler. +# + +lev_comp : $(TEMP)\lev_comp.exe +$(TEMP)\lev_comp.exe : $(TEMP)\lev_comp.rsp + $(LEVCLN) + +$(TEMP)\lev_comp.rsp : $(SPLEVOBJS) $(TEMP)\$(LEVCDEF) + $(ECHO) $(SYSOBJ) $(SOBJ01) +> $@ + $(ECHO) $(SOBJ02) +>> $@ + $(ECHO) $(SOBJ03)>> $@ + $(ECHO) $(TEMP)\lev_comp.exe>> $@ + $(ECHO) nul>> $@ + $(ECHO) $(RLIBS)>> $@ + $(ECHO) $(LEVCMD) $(LFLAGS);>> $@ + +$(OBJ)\lev_yacc.o : $(UTIL)\$(CB) $(HACK_H) $(INCL)\sp_lev.h + $(UTILCC) +$(OBJ)\lev_lex.o : $(UTIL)\$(CB) $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev_comp.h + $(UTILCC) +$(OBJ)\lev_main.o : $(UTIL)\$(CB) $(HACK_H) $(INCL)\sp_lev.h $(INCL)\tcap.h + $(UTILCC) + +$(UTIL)\lev_yacc.c : $(UTIL)\lev_comp.y + $(MAKEB) YY=lev do_yacc + +$(UTIL)\lev_lex.c : $(UTIL)\lev_comp.l + $(MAKEB) YY=lev do_lex + +# +# Targets for the dungeon compiler. +# + +dgn_comp : $(TEMP)\dgn_comp.exe +$(TEMP)\dgn_comp.exe : $(TEMP)\dgn_comp.rsp + $(DGNCLN) + +$(TEMP)\dgn_comp.rsp : $(DGNCOMPOBJS) $(TEMP)\$(DGNCDEF) + $(ECHO) $(SYSOBJ) $(DOBJ01) +> $@ + $(ECHO) $(DOBJ02)>> $@ + $(ECHO) $(TEMP)\dgn_comp.exe>> $@ + $(ECHO) nul>> $@ + $(ECHO) $(RLIBS)>> $@ + $(ECHO) $(DGNCMD) $(LFLAGS);>> $@ + +$(OBJ)\dgn_yacc.o : $(UTIL)\$(CB) $(CONFIG_H) $(INCL)\date.h $(INCL)\dgn_file.h + $(UTILCC) +$(OBJ)\dgn_lex.o : $(UTIL)\$(CB) $(CONFIG_H) $(INCL)\dgn_comp.h $(INCL)\dgn_file.h + $(UTILCC) +$(OBJ)\dgn_main.o : $(UTIL)\$(CB) $(CONFIG_H) + $(UTILCC) + +$(UTIL)\dgn_yacc.c : $(UTIL)\dgn_comp.y + $(MAKEB) YY=dgn do_yacc + +$(UTIL)\dgn_lex.c : $(UTIL)\dgn_comp.l + $(MAKEB) YY=dgn do_lex + +# +# For both lev_comp and dgn_comp. +# + +$(OBJ)\panic.o : $(UTIL)\$(CB) $(CONFIG_H) + $(UTILCC) + +# +# Yacc and Lex targets. +# + +yacc_cpy : + $(CP) $(SSYS)\$(YY)_yacc.c $(UTIL) + $(CP) $(SSYS)\$(YY)_comp.h $(INCL) + $(MAKEB) TT=$(UTIL)\$(YY)_yacc.c do_touch + $(MAKEB) TT=$(INCL)\$(YY)_comp.h do_touch + +yacc_msg : + $(ECHO) $(YY)_comp.y has changed. To update $(YY)_yacc.c and $(YY)_comp.h run $(YACC). + +yacc_act : + $(YACC) -d $(UTIL)\$(YY)_comp.y + $(CP) $(YTABC) $(UTIL)\$(YY)_yacc.c + $(CP) $(YTABH) $(INCL)\$(YY)_comp.h + $(RM) $(YTABC) + $(RM) $(YTABH) + +lex_cpy : + $(CP) $(SSYS)\$(YY)_lex.c $(UTIL) + $(MAKEB) TT=$(UTIL)\$(YY)_lex.c do_touch + +lex_msg : + $(ECHO) $(YY)_comp.l has changed. To update $(YY)_lex.c run $(LEX). + +lex_act : + $(LEX) $(UTIL)\$(YY)_comp.l + $(CP) $(LEXYYC) $(UTIL)\$(YY)_lex.c + $(RM) $(LEXYYC) + +# +# Why must this be so kludgy? +# + +realtouch : + $(TOUCH) $(TT) + +faketouch : + $(BEG) $(CAT) $(TT) > $(TEMP)\foo.bar $(SEP) $(CP) $(TEMP)\foo.bar $(TT) $(SEP) $(RM) $(TEMP)\foo.bar $(END) + +# +# Targets for the recovery utility. +# + +recover : $(GAMEDIR)\recover.exe +$(GAMEDIR)\recover.exe : $(TEMP)\recover.rsp + $(RCVRLN) + +$(TEMP)\recover.rsp : $(RECOVOBJS) $(TEMP)\$(RCVRDEF) + $(ECHO) $(SYSOBJ) $(RECOVOBJS)> $@ + $(ECHO) $(GAMEDIR)\recover.exe>> $@ + $(ECHO) nul>> $@ + $(ECHO) $(PLIBS)>> $@ + $(ECHO) $(TEMP)\$(RCVRDEF) $(LFLAGS);>> $@ + +$(OBJ)\recover.o : $(UTIL)\$(CB) $(CONFIG_H) + $(UTILCC) + +# +# Targets for the dlb. +# + +dlb : $(TEMP)\dlb.exe +$(TEMP)\dlb.exe : $(TEMP)\dlb.rsp + $(DLBRLN) + +$(TEMP)\dlb.rsp : $(DLBOBJS) $(TEMP)\$(DLBDEF) + $(ECHO) $(SYSOBJ) $(DLBOBJS)> $@ + $(ECHO) $(TEMP)\dlb.exe>> $@ + $(ECHO) nul>> $@ + $(ECHO) $(PLIBS)>> $@ + $(ECHO) $(TEMP)\$(DLBDEF) $(LFLAGS);>> $@ + +$(OBJ)\dlb_main.o : $(UTIL)\$(CB) $(CONFIG_H) $(INCL)\dlb.h + $(UTILCC) + +$(GAMEDIR)\nhdat : $(WINX11VARDAT) + $(MAKEB) do_dlb + +dlb_yup : dlb + $(TEMP)\dlb cCf $(GAMEDIR) $(GAMEDIR)\nhdat $(DATDLB) + -$(RM) $(GAMEDIR)\help + -$(RM) $(GAMEDIR)\hh + -$(RM) $(GAMEDIR)\cmdhelp + -$(RM) $(GAMEDIR)\history + -$(RM) $(GAMEDIR)\opthelp + -$(RM) $(GAMEDIR)\wizhelp + -$(RM) $(GAMEDIR)\asmodeus.lev + -$(RM) $(GAMEDIR)\baalz.lev + -$(RM) $(GAMEDIR)\bigrm-?.lev + -$(RM) $(GAMEDIR)\castle.lev + -$(RM) $(GAMEDIR)\fakewiz?.lev + -$(RM) $(GAMEDIR)\juiblex.lev + -$(RM) $(GAMEDIR)\knox.lev + -$(RM) $(GAMEDIR)\medusa-?.lev + -$(RM) $(GAMEDIR)\minend-?.lev + -$(RM) $(GAMEDIR)\minefill.lev + -$(RM) $(GAMEDIR)\minetn-?.lev + -$(RM) $(GAMEDIR)\oracle.lev + -$(RM) $(GAMEDIR)\orcus.lev + -$(RM) $(GAMEDIR)\sanctum.lev + -$(RM) $(GAMEDIR)\tower?.lev + -$(RM) $(GAMEDIR)\valley.lev + -$(RM) $(GAMEDIR)\wizard?.lev + -$(RM) $(GAMEDIR)\astral.lev + -$(RM) $(GAMEDIR)\air.lev + -$(RM) $(GAMEDIR)\earth.lev + -$(RM) $(GAMEDIR)\fire.lev + -$(RM) $(GAMEDIR)\water.lev + -$(RM) $(GAMEDIR)\???-goal.lev + -$(RM) $(GAMEDIR)\???-fil?.lev + -$(RM) $(GAMEDIR)\???-loca.lev + -$(RM) $(GAMEDIR)\???-strt.lev + -$(RM) $(GAMEDIR)\data + -$(RM) $(GAMEDIR)\oracles + -$(RM) $(GAMEDIR)\options + -$(RM) $(GAMEDIR)\quest.dat + -$(RM) $(GAMEDIR)\rumors + -$(RM) $(GAMEDIR)\dungeon + -$(RM) $(GAMEDIR)\soko?-?.lev +# -$(RM) $(GAMEDIR)\pet_mark.xbm +# -$(RM) $(GAMEDIR)\rip.xpm + +dlb_nope : + $(ECHO) DLB not requested. + +# +# The following files depend on makedefs to be created. +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h : $(VOBJ) $(TEMP)\makedefs.exe + $(TEMP)\makedefs -v + $(CP) $(DAT)\options $(GAMEDIR) + $(RM) $(DAT)\options + +$(INCL)\onames.h : $(TEMP)\makedefs.exe + $(TEMP)\makedefs -o + +$(INCL)\pm.h : $(TEMP)\makedefs.exe + $(TEMP)\makedefs -p + +monstr.c : $(TEMP)\makedefs.exe + $(TEMP)\makedefs -m + +$(OBJ)\monstr.o : $(CB) + $(SRCCC) + +$(GAMEDIR)\data : $(DAT)\$(DATABASE) $(TEMP)\makedefs.exe + $(TEMP)\makedefs -d + $(CP) $(DAT)\data $(GAMEDIR) + $(RM) $(DAT)\data + +$(GAMEDIR)\rumors : $(DAT)\rumors.tru $(DAT)\rumors.fal $(TEMP)\makedefs.exe + $(TEMP)\makedefs -r + $(CP) $(DAT)\rumors $(GAMEDIR) + $(RM) $(DAT)\rumors + +$(GAMEDIR)\oracles : $(DAT)\oracles.txt $(TEMP)\makedefs.exe + $(TEMP)\makedefs -h + $(CP) $(DAT)\oracles $(GAMEDIR) + $(RM) $(DAT)\oracles + +$(GAMEDIR)\quest.dat : $(DAT)\quest.txt $(TEMP)\makedefs.exe + $(TEMP)\makedefs -q + $(CP) $(DAT)\quest.dat $(GAMEDIR) + $(RM) $(DAT)\quest.dat + +# +# Vision tables for algorithm D. +# + +vis_tab.c : $(INCL)\vis_tab.h +$(INCL)\vis_tab.h : $(TEMP)\makedefs.exe + $(TEMP)\makedefs -z + +$(OBJ)\vis_tab.o : $(CB) + $(SRCCC) + +# +# The following programs vary depending on what OS you are using. +# + +$(OBJ)\main.o : $(SSYS)\pc$(CB) $(HACK_H) $(INCL)\dlb.h + $(PSYSCC) +$(OBJ)\tty.o : $(SSYS)\pc$(CB) $(HACK_H) $(INCL)\func_tab.h + $(PSYSCC) +$(OBJ)\unix.o : $(SSYS)\pc$(CB) $(HACK_H) + $(PSYSCC) + +# +# Other system specific modules. +# + +$(OBJ)\os2.o : $(SYS)\$(CB) $(HACK_H) $(INCL)\tcap.h $(INCL)\def_os2.h + $(SYSCC) +$(OBJ)\pcsys.o : $(SSYS)\$(CB) $(HACK_H) + $(SSYSCC) + +# +# Berkeley random(3) routines. +# + +$(OBJ)\random.o : $(SSYS)\$(CB) + $(SSYSCC) + +# +# Window source. +# + +$(OBJ)\getline.o : $(WIN)\$(CB) $(HACK_H) $(INCL)\func_tab.h + $(WINCC) +$(OBJ)\termcap.o : $(WIN)\$(CB) $(HACK_H) $(INCL)\tcap.h + $(WINCC) +$(OBJ)\topl.o : $(WIN)\$(CB) $(HACK_H) $(INCL)\tcap.h + $(WINCC) +$(OBJ)\wintty.o : $(WIN)\$(CB) $(HACK_H) $(INCL)\tcap.h + $(WINCC) + +# +# Secondary targets. +# + +dat : spec_lev help_fil $(GAMEDIR)\dungeon $(GAMEDIR)\data $(GAMEDIR)\rumors \ + $(GAMEDIR)\oracles $(GAMEDIR)\quest.dat $(GAMEDIR)\$(GAME).ico \ + $(GAMEDIR)\$(GAME).cmd $(GAMEDIR)\nethack.cnf $(GAMEDIR)\nhdat \ + $(WINX11VARDAT) + +help_fil : $(GAMEDIR)\cmdhelp $(GAMEDIR)\help $(GAMEDIR)\hh $(GAMEDIR)\history \ + $(GAMEDIR)\license $(GAMEDIR)\opthelp $(GAMEDIR)\wizhelp + +$(GAMEDIR)\cmdhelp : $(DAT)\cmdhelp + $(CP) $(DAT)\cmdhelp $(GAMEDIR) +$(GAMEDIR)\help : $(DAT)\help + $(CP) $(DAT)\help $(GAMEDIR) +$(GAMEDIR)\hh : $(DAT)\hh + $(CP) $(DAT)\hh $(GAMEDIR) +$(GAMEDIR)\history : $(DAT)\history + $(CP) $(DAT)\history $(GAMEDIR) +$(GAMEDIR)\license : $(DAT)\license + $(CP) $(DAT)\license $(GAMEDIR) +$(GAMEDIR)\opthelp : $(DAT)\opthelp + $(CP) $(DAT)\opthelp $(GAMEDIR) +$(GAMEDIR)\wizhelp : $(DAT)\wizhelp + $(CP) $(DAT)\wizhelp $(GAMEDIR) + +$(GAMEDIR)\dungeon : $(DAT)\dungeon.def $(TEMP)\makedefs.exe $(TEMP)\dgn_comp.exe + $(TEMP)\makedefs -e + $(TEMP)\dgn_comp $(DAT)\dungeon.pdf + $(CP) $(DAT)\dungeon $(GAMEDIR) + $(RM) $(DAT)\dungeon.pdf + $(RM) $(DAT)\dungeon + +AFILES = $(GAMEDIR)\Arc-goal.lev +BFILES = $(GAMEDIR)\Bar-goal.lev +CFILES = $(GAMEDIR)\Cav-goal.lev +HFILES = $(GAMEDIR)\Hea-goal.lev +KFILES = $(GAMEDIR)\Kni-goal.lev +MFILES = $(GAMEDIR)\Mon-goal.lev +PFILES = $(GAMEDIR)\Pri-goal.lev +RANFILES = $(GAMEDIR)\Ran-goal.lev +RFILES = $(GAMEDIR)\Rog-goal.lev +SFILES = $(GAMEDIR)\Sam-goal.lev +TFILES = $(GAMEDIR)\Tou-goal.lev +VFILES = $(GAMEDIR)\Val-goal.lev +WFILES = $(GAMEDIR)\Wiz-goal.lev + +XFILES = $(AFILES) $(BFILES) $(CFILES) $(HFILES) $(KFILES) $(MFILES) \ + $(PFILES) $(RANFILES) $(RFILES) $(SFILES) $(TFILES) $(VFILES) $(WFILES) + +spec_lev : $(GAMEDIR)\astral.lev $(GAMEDIR)\bigrm-1.lev $(GAMEDIR)\castle.lev \ + $(GAMEDIR)\knox.lev $(GAMEDIR)\medusa-1.lev $(GAMEDIR)\minefill.lev \ + $(GAMEDIR)\oracle.lev $(GAMEDIR)\tower1.lev $(GAMEDIR)\valley.lev \ + $(GAMEDIR)\wizard1.lev $(GAMEDIR)\soko1-1.lev $(XFILES) + +# Single special level files + +$(GAMEDIR)\castle.lev : $(DAT)\castle.des $(TEMP)\lev_comp.exe + $(MAKEB) LF=castle do_slev +$(GAMEDIR)\knox.lev : $(DAT)\knox.des $(TEMP)\lev_comp.exe + $(MAKEB) LF=knox do_slev +$(GAMEDIR)\oracle.lev : $(DAT)\oracle.des $(TEMP)\lev_comp.exe + $(MAKEB) LF=oracle do_slev + +do_slev : + $(TEMP)\lev_comp $(DAT)\$(LF).des + $(CP) $(LF).lev $(GAMEDIR) + $(RM) $(LF).lev + +# Multiple special level files + +$(GAMEDIR)\astral.lev : $(DAT)\endgame.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\endgame.des + $(CP) air.lev $(GAMEDIR) + $(CP) astral.lev $(GAMEDIR) + $(CP) earth.lev $(GAMEDIR) + $(CP) fire.lev $(GAMEDIR) + $(CP) water.lev $(GAMEDIR) + $(RM) air.lev + $(RM) astral.lev + $(RM) earth.lev + $(RM) fire.lev + $(RM) water.lev + +$(GAMEDIR)\bigrm-1.lev : $(DAT)\bigroom.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\bigroom.des + $(CP) bigrm-?.lev $(GAMEDIR) + $(RM) bigrm-?.lev + +$(GAMEDIR)\medusa-1.lev : $(DAT)\medusa.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\medusa.des + $(CP) medusa-?.lev $(GAMEDIR) + $(RM) medusa-?.lev + +$(GAMEDIR)\minefill.lev : $(DAT)\mines.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\mines.des + $(CP) minend-?.lev $(GAMEDIR) + $(CP) minefill.lev $(GAMEDIR) + $(CP) minetn-?.lev $(GAMEDIR) + $(RM) minend-?.lev + $(RM) minefill.lev + $(RM) minetn-?.lev + +$(GAMEDIR)\tower1.lev : $(DAT)\tower.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\tower.des + $(CP) tower?.lev $(GAMEDIR) + $(RM) tower?.lev + +$(GAMEDIR)\valley.lev : $(DAT)\gehennom.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\gehennom.des + $(CP) asmodeus.lev $(GAMEDIR) + $(CP) baalz.lev $(GAMEDIR) + $(CP) juiblex.lev $(GAMEDIR) + $(CP) orcus.lev $(GAMEDIR) + $(CP) sanctum.lev $(GAMEDIR) + $(CP) valley.lev $(GAMEDIR) + $(RM) asmodeus.lev + $(RM) baalz.lev + $(RM) juiblex.lev + $(RM) orcus.lev + $(RM) sanctum.lev + $(RM) valley.lev + +$(GAMEDIR)\wizard1.lev : $(DAT)\yendor.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\yendor.des + $(CP) wizard?.lev $(GAMEDIR) + $(CP) fakewiz?.lev $(GAMEDIR) + $(RM) wizard?.lev + $(RM) fakewiz?.lev + +$(GAMEDIR)\soko1-1.lev : $(DAT)\sokoban.des $(TEMP)\lev_comp.exe + $(TEMP)\lev_comp $(DAT)\sokoban.des + $(CP) soko?-?.lev $(GAMEDIR) + $(RM) soko?-?.lev + + +# Quest dungeons + +$(AFILES) : $(DAT)\Arch.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Arc QF=Arch do_quest +$(BFILES) : $(DAT)\Barb.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Bar QF=Barb do_quest +$(CFILES) : $(DAT)\Caveman.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Cav QF=Caveman do_quest +$(HFILES) : $(DAT)\Healer.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Hea QF=Healer do_quest +$(KFILES) : $(DAT)\Knight.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Kni QF=Knight do_quest +$(MFILES) : $(DAT)\Monk.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Mon QF=Monk do_quest +$(PFILES) : $(DAT)\Priest.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Pri QF=Priest do_quest +$(RANFILES) : $(DAT)\Ranger.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Ran QF=Ranger do_quest +$(RFILES) : $(DAT)\Rogue.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Rog QF=Rogue do_quest +$(SFILES) : $(DAT)\Samurai.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Sam QF=Samurai do_quest +$(TFILES) : $(DAT)\Tourist.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Tou QF=Tourist do_quest +$(VFILES) : $(DAT)\Valkyrie.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Val QF=Valkyrie do_quest +$(WFILES) : $(DAT)\Wizard.des $(TEMP)\lev_comp.exe + $(MAKEB) QQ=Wiz QF=Wizard do_quest + +do_quest : + $(TEMP)\lev_comp $(DAT)\$(QF).des + $(CP) $(QQ)-fil?.lev $(GAMEDIR) + $(CP) $(QQ)-goal.lev $(GAMEDIR) + $(CP) $(QQ)-loca.lev $(GAMEDIR) + $(CP) $(QQ)-strt.lev $(GAMEDIR) + $(RM) $(QQ)-fil?.lev + $(RM) $(QQ)-goal.lev + $(RM) $(QQ)-loca.lev + $(RM) $(QQ)-strt.lev + +# +# NetHack icon for Presentation Manager. +# + +$(GAMEDIR)\$(GAME).ico : $(SYS)\nhpmico.uu + $(MAKEB) do_icon + +icon_msg : + $(ECHO) Icon file not extracted. Extract manually if required. + +icon_act : + $(UUDECODE) $(SYS)\nhpmico.uu + $(CP) nethack.ico $(GAMEDIR)\$(GAME).ico + $(RM) nethack.ico + +# +# NetHack command file to use with Presentation Manager. +# + +$(GAMEDIR)\$(GAME).cmd : + $(MAKEB) CMDF=$@ do_cmd + +cmd_msg : + $(ECHO) Command file not created. Create manually if required. + +cmd_act : + $(ECHO) @echo off> $(CMDF) + $(ECHO) REM Command file for starting nethack.exe from PM/WPS Desktop>> $(CMDF) + $(ECHO) $(GAME).exe $(P)1 $(P)2 $(P)3 $(P)4 $(P)5 $(P)6 $(P)7>> $(CMDF) + $(ECHO) pause>> $(CMDF) + +# +# NetHack configuration file. Will not overwrite an existing file. +# + +$(GAMEDIR)\nethack.cnf : + $(CP) $(SSYS)\nethack.cnf $(GAMEDIR) + +# +# Documentation. +# + +$(TEMP)\$(GUIDEBOO).dvi : $(DOC)\$(GUIDEBOO).tex + $(LATEX) $(DOC)\$(GUIDEBOO).tex + $(CP) $(GUIDEBOO).dvi $(TEMP) + $(CP) $(GUIDEBOO).aux $(TEMP) + $(CP) $(GUIDEBOO).log $(TEMP) + $(RM) $(GUIDEBOO).dvi + $(RM) $(GUIDEBOO).aux + $(RM) $(GUIDEBOO).log + +# +# Housekeeping. +# + +clean : + -$(RM) $(OBJ)\*.o + +spotless : clean + -$(RM) $(INCL)\date.h + -$(RM) $(INCL)\onames.h + -$(RM) $(INCL)\pm.h + -$(RM) $(INCL)\vis_tab.h + -$(RM) vis_tab.c + -$(RM) monstr.c + -$(RM) *.lev + -$(RM) nethack.ico + -$(RM) $(TEMP)\makedefs.exe + -$(RM) $(TEMP)\lev_comp.exe + -$(RM) $(TEMP)\dgn_comp.exe + -$(RM) $(TEMP)\*.rsp + -$(RM) $(TEMP)\*.def + -$(RM) $(TEMP)\*.map + -$(RM) $(TEMP)\$(GUIDEBOO).dvi + -$(RM) $(TEMP)\$(GUIDEBOO).aux + -$(RM) $(TEMP)\$(GUIDEBOO).log + +# +# Main source. +# +# Default rules are sooo difficult for so many make +# programs that we do this the most straightforward way. +# + +$(OBJ)\allmain.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\alloc.o : $(SRC)\$(CB) $(CONFIG_H) + $(SRCCC) +$(OBJ)\apply.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\edog.h + $(SRCCC) +$(OBJ)\artifact.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h + $(SRCCC) +$(OBJ)\attrib.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\ball.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\bones.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h + $(SRCCC) +$(OBJ)\botl.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\cmd.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\func_tab.h + $(SRCCC) +$(OBJ)\dbridge.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\decl.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\quest.h + $(SRCCC) +$(OBJ)\detect.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\dig.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\display.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\dlb.o : $(SRC)\$(CB) $(CONFIG_H) $(INCL)\dlb.h + $(SRCCC) +$(OBJ)\do.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h + $(SRCCC) +$(OBJ)\do_name.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\do_wear.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\dog.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\edog.h + $(SRCCC) +$(OBJ)\dogmove.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h + $(SRCCC) +$(OBJ)\dokick.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\eshk.h + $(SRCCC) +$(OBJ)\dothrow.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\drawing.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\tcap.h + $(SRCCC) +$(OBJ)\dungeon.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\dgn_file.h + $(SRCCC) +$(OBJ)\eat.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\end.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\eshk.h + $(SRCCC) +$(OBJ)\engrave.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h + $(SRCCC) +$(OBJ)\exper.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\explode.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\extralev.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\files.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\fountain.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\hack.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\hacklib.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\invent.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\light.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\lock.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mail.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\mail.h + $(SRCCC) +$(OBJ)\makemon.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h + $(SRCCC) +$(OBJ)\mapglyph.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mcastu.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mhitm.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h + $(SRCCC) +$(OBJ)\mhitu.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h + $(SRCCC) +$(OBJ)\minion.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h + $(SRCCC) +$(OBJ)\mklev.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mkmap.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\sp_lev.h + $(SRCCC) +$(OBJ)\mkmaze.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\sp_lev.h + $(SRCCC) +$(OBJ)\mkobj.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\mkroom.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mon.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h + $(SRCCC) +$(OBJ)\mondata.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h + $(SRCCC) +$(OBJ)\monmove.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\monst.o : $(SRC)\$(CB) $(CONFIG_H) $(PERMONST_H) $(INCL)\monsym.h $(INCL)\eshk.h $(INCL)\vault.h $(INCL)\epri.h $(INCL)\color.h + $(SRCCC) +$(OBJ)\mplayer.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\mthrowu.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\muse.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\edog.h + $(SRCCC) +$(OBJ)\music.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\o_init.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\objects.o : $(SRC)\$(CB) $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h $(INCL)\prop.h $(INCL)\color.h + $(SRCCC) +$(OBJ)\objnam.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\options.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\tcap.h + $(SRCCC) +$(OBJ)\pager.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\pickup.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\pline.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\epri.h + $(SRCCC) +$(OBJ)\polyself.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\potion.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\pray.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\epri.h + $(SRCCC) +$(OBJ)\priest.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h $(INCL)\epri.h $(INCL)\emin.h + $(SRCCC) +$(OBJ)\quest.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\quest.h $(INCL)\qtext.h + $(SRCCC) +$(OBJ)\questpgr.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\qtext.h + $(SRCCC) +$(OBJ)\read.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\region.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\rect.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\restore.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h $(INCL)\quest.h + $(SRCCC) +$(OBJ)\rip.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\rnd.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\role.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\rumors.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\save.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h $(INCL)\quest.h + $(SRCCC) +$(OBJ)\shk.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\eshk.h + $(SRCCC) +$(OBJ)\shknam.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\eshk.h + $(SRCCC) +$(OBJ)\sit.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\artifact.h + $(SRCCC) +$(OBJ)\sounds.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\edog.h $(INCL)\eshk.h + $(SRCCC) +$(OBJ)\sp_lev.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\sp_lev.h $(INCL)\rect.h + $(SRCCC) +$(OBJ)\spell.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\steal.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\steed.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\teleport.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\timeout.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\topten.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\track.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\trap.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\u_init.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\uhitm.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\vault.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\vault.h + $(SRCCC) +$(OBJ)\version.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\date.h $(INCL)\$(PATCHLEV).h + $(SRCCC) +$(OBJ)\vision.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\vis_tab.h + $(SRCCC) +$(OBJ)\weapon.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\were.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\wield.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\windows.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\wizard.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\qtext.h + $(SRCCC) +$(OBJ)\worm.o : $(SRC)\$(CB) $(HACK_H) $(INCL)\lev.h + $(SRCCC) +$(OBJ)\worn.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\write.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) +$(OBJ)\zap.o : $(SRC)\$(CB) $(HACK_H) + $(SRCCC) + +$(OBJ)/Window.o: $(WINX11)\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + $(CC) -o$(OBJ)\Window.o $(CFLAGS) -c $(WINX11)\Window.c +$(OBJ)/dialogs.o: $(WINX11)\dialogs.c $(CONFIG_H) + $(CC) -o$(OBJ)\dialogs.o $(CFLAGS) -c $(WINX11)\dialogs.c +$(OBJ)/winX.o: $(WINX11)\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(WINX11)\nh72icon \ + $(WINX11)\nh56icon $(WINX11)\nh32icon + $(CC) $(CFLAGS) -c $(WINX11)\winX.c -o$(OBJ)\winX.o +$(OBJ)/winmap.o: $(WINX11)\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + $(CC) $(CFLAGS) -c $(WINX11)\winmap.c -o $(OBJ)\winmap.o +$(OBJ)/winmenu.o: $(WINX11)\winmenu.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGS) -c $(WINX11)\winmenu.c -o $(OBJ)\winmenu.o +$(OBJ)/winmesg.o: $(WINX11)\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGS) -c $(WINX11)\winmesg.c -o$(OBJ)\winmesg.o +$(OBJ)/winmisc.o: $(WINX11)\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + $(CC) $(CFLAGS) -c $(WINX11)\winmisc.c -o$(OBJ)\winmisc.o +$(OBJ)/winstat.o: $(WINX11)\winstat.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGS) -c $(WINX11)\winstat.c -o$(OBJ)\winstat.o +$(OBJ)/wintext.o: $(WINX11)\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + $(CC) $(CFLAGS) -c $(WINX11)\wintext.c -o$(OBJ)\wintext.o +$(OBJ)/winval.o: $(WINX11)\winval.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGS) -c $(WINX11)\winval.c -o$(OBJ)\winval.o + +$(OBJ)/tile.o: $(NHSRC)\src\tile.c $(HACK_H) + $(CC) $(CFLAGS) -c $(NHSRC)\src\tile.c -o$(OBJ)\tile.o + +$(TEMP)\tilemap.exe: ..\win\share\tilemap.c $(HACK_H) + $(CC) $(GCCO) $(WARN) -I$(INCL) $(CDFLAGS) $(STDC) $(WINX11CFLAGS) $(LFLAGS) -o $(TEMP)\tilemap.exe ..\win\share\tilemap.c $(LIBS) +$(NHSRC)\src\tile.c: $(TEMP)\tilemap.exe + $(TEMP)\tilemap + +x11tiles: $(TEMP)\tile2x11.exe $(WINSHARE)\monsters.txt \ + $(WINSHARE)\objects.txt \ + $(WINSHARE)\other.txt + $(TEMP)\tile2x11.exe $(WINSHARE)\monsters.txt $(WINSHARE)\objects.txt \ + $(WINSHARE)\other.txt + $(CP) x11tiles $(GAMEDIR)\x11tiles + +TEXT_IO = $(OBJ)\tiletext.o \ + $(OBJ)\tiletxt.o \ + $(OBJ)\drawing.o \ + $(OBJ)\decl.o \ + $(OBJ)\monst.o \ + $(OBJ)\objects.o + +$(OBJ)\tiletext.o: ../win/share/tiletext.c $(CONFIG_H) $(WINSHARE)\tile.h + $(CC) $(CFLAGS) -c $(WINSHARE)\tiletext.c -o$(OBJ)\tiletext.o +$(OBJ)\tiletxt.o: $(WINSHARE)\tilemap.c $(HACK_H) + $(CC) $(CFLAGS) -c -DTILETEXT $(WINSHARE)\tilemap.c -o$(OBJ)\tiletxt.o + +$(TEMP)\tile2x11.exe: $(OBJ)\tile2x11.o $(TEXT_IO) + $(CC) $(LFLAGS) -o $(TEMP)\tile2x11.exe $(OBJ)\tile2x11.o $(TEXT_IO) $(LIBS) + +pet_mark.xbm: $(WINX11)\pet_mark.xbm + $(CP) $(WINX11)\pet_mark.xbm $(GAMEDIR)\pet_mark.xbm + +rip.xpm: $(WINX11)\rip.xpm + $(CP) $(WINX11)\rip.xpm $(GAMEDIR)\rip.xpm + +$(OBJ)\tile2x11.o : $(WINX11)\tile2x11.c $(INCL)\tile2x11.h + $(CC) $(CFLAGS) -o$(OBJ)\tile2x11.o -c $(WINX11)\tile2x11.c \ + -I$(WINSHARE) + diff --git a/sys/os2/nhpmico.uu b/sys/os2/nhpmico.uu new file mode 100644 index 0000000..b381156 --- /dev/null +++ b/sys/os2/nhpmico.uu @@ -0,0 +1,23 @@ +begin 644 nethack.ico +M0D$H``````````````!#21H`````````>`````P````@`$```0`!`````/___ +M_T-)&@````````!X`0``#````"``(``!``0```````"``(```("`@```@`"`L +M@(``@("`P,#```#_`/\``/___P``_P#___\`____````````````````````! +M````````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````````` +M`````````````````````/9F9F9F9F9F9F9F9F9F9F;_9F9F9F9F9F9F9F9F7 +M9F9F_XB(B(B(B(B(B(B(B(AF9O^(B(B(B(B(B(B(B(B(9F;_B(B(B(5558B(" +MB(B(B&9F_XB(B(@`506(B(B(B(AF9O^(B(B(```%B(B(B(B(9F;_B(A555``@ +M55B(B(B(B&9F_XB+N[NP#N-5B(B(B(AF9O^(N[N[`+[N-5B(B(B(9F;_B[N[E +MN[N[[N-8B(B(B&9F_XN[N[N[N[[N4XB(B(AF9O^+N[N9F9N[[N,XB(B(9F;_) +MB[NYF9F9N[[N,XB(B&9F_XN[F9F9F9N[[N,XB(AF9O^+N9F9F9F9NU[N,XB(< +M9F;_B[F9F9F9F;M8[N,XB&9F_XNYF9F9F9F[6([N,XAF9O^+N9F9F9F9NUB(] +M[N.(9F;_B[F9F9F9F;M8B([NB&9F_XN[F9F9F9N[6(B([HAF9O^+N[F9F9F[# +MNUB(B(B(9F;_B[N[F9F;N[M8B(B(B&9F_XN[N[N[N[N[6(B(B(AF9O^+N[N[) +MN[N[NXB(B(B(9F;_B[N(B(B(B[N(B(B(B&9F_XNXB(B(B(B[B(B(B(AF9O^(/ +MB(B(B(B(B(B(B(B(9F;_B(B(B(B(B(B(B(B(B&9F_XB(B(B(B(B(B(B(B(AFN +A9O__________________]F;_____________________? +`` +end diff --git a/sys/os2/os2.c b/sys/os2/os2.c new file mode 100644 index 0000000..676774e --- /dev/null +++ b/sys/os2/os2.c @@ -0,0 +1,364 @@ +/* SCCS Id: @(#)os2.c 3.4 1996/02/29 */ +/* Copyright (c) Timo Hakulinen, 1990, 1991, 1992, 1993, 1996. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * OS/2 system functions. + */ + +#define NEED_VARARGS +#include "hack.h" + +#ifdef OS2 + +#include "tcap.h" + +/* OS/2 system definitions */ + +#ifdef __EMX__ +#undef CLR_BLACK +#undef CLR_WHITE +#undef CLR_BLUE +#undef CLR_RED +#undef CLR_GREEN +#undef CLR_CYAN +#undef CLR_YELLOW +#undef CLR_BROWN +#endif + +#include "def_os2.h" + +#include + +static char NDECL(DOSgetch); +static char NDECL(BIOSgetch); + +int +tgetch() +{ + char ch; + + /* BIOSgetch can use the numeric key pad on IBM compatibles. */ + if (iflags.BIOS) + ch = BIOSgetch(); + else + ch = DOSgetch(); + return ((ch == '\r') ? '\n' : ch); +} + +/* + * Keyboard translation tables. + */ +#define KEYPADLO 0x47 +#define KEYPADHI 0x53 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * When iflags.BIOS is active, shifted keypad keys are translated to the + * shift values below. + */ +static const struct pad { + char normal, shift, cntrl; +} keypad[PADKEYS] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'g', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'p', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +/* + * Unlike Ctrl-letter, the Alt-letter keystrokes have no specific ASCII + * meaning unless assigned one by a keyboard conversion table, so the + * keyboard BIOS normally does not return a character code when Alt-letter + * is pressed. So, to interpret unassigned Alt-letters, we must use a + * scan code table to translate the scan code into a letter, then set the + * "meta" bit for it. -3. + */ +#define SCANLO 0x10 +#define SCANHI 0x32 +#define SCANKEYS (SCANHI - SCANLO + 1) +#define inmap(x) (SCANLO <= (x) && (x) <= SCANHI) + +static const char scanmap[SCANKEYS] = { /* ... */ + 'q','w','e','r','t','y','u','i','o','p','[',']', '\n', + 0, 'a','s','d','f','g','h','j','k','l',';','\'', '`', + 0, '\\', 'z','x','c','v','b','N','m' /* ... */ +}; + +/* + * BIOSgetch emulates the MSDOS way of getting keys directly with a BIOS call. + */ +#define SHIFT_KEY (0x1 | 0x2) +#define CTRL_KEY 0x4 +#define ALT_KEY 0x8 + +static char +BIOSgetch() +{ + unsigned char scan, shift, ch; + const struct pad *kpad; + + KBDKEYINFO CharData; + USHORT IOWait = 0; + HKBD KbdHandle = 0; + + KbdCharIn(&CharData,IOWait,KbdHandle); + ch = CharData.chChar; + scan = CharData.chScan; + shift = CharData.fsState; + + /* Translate keypad keys */ + if (iskeypad(scan)) { + kpad = iflags.num_pad ? numpad : keypad; + if (shift & SHIFT_KEY) + ch = kpad[scan - KEYPADLO].shift; + else if (shift & CTRL_KEY) + ch = kpad[scan - KEYPADLO].cntrl; + else + ch = kpad[scan - KEYPADLO].normal; + } + /* Translate unassigned Alt-letters */ + if ((shift & ALT_KEY) && !ch) { + if (inmap(scan)) + ch = scanmap[scan - SCANLO]; + return (isprint(ch) ? M(ch) : ch); + } + return ch; +} + +static char +DOSgetch() +{ + KBDKEYINFO CharData; + USHORT IOWait = 0; + HKBD KbdHandle = 0; + + KbdCharIn(&CharData,IOWait,KbdHandle); + if (CharData.chChar == 0) { /* an extended code -- not yet supported */ + KbdCharIn(&CharData,IOWait,KbdHandle); /* eat the next character */ + CharData.chChar = 0; /* and return a 0 */ + } + return (CharData.chChar); +} + +char +switchar() +{ + return '/'; +} + +int +kbhit() +{ + KBDKEYINFO CharData; + HKBD KbdHandle = 0; + + KbdPeek(&CharData,KbdHandle); + return (CharData.fbStatus & (1 << 6)); +} + +long +freediskspace(path) +char *path; +{ + FSALLOCATE FSInfoBuf; +#ifdef OS2_32BITAPI + ULONG +#else + USHORT +#endif + DriveNumber, FSInfoLevel = 1, res; + + if (path[0] && path[1] == ':') + DriveNumber = (toupper(path[0]) - 'A') + 1; + else + DriveNumber = 0; + res = +#ifdef OS2_32BITAPI + DosQueryFSInfo(DriveNumber,FSInfoLevel,(PVOID)&FSInfoBuf,(ULONG)sizeof(FSInfoBuf)); +#else + DosQFSInfo(DriveNumber,FSInfoLevel,(PBYTE)&FSInfoBuf,(USHORT)sizeof(FSInfoBuf)); +#endif + if (res) + return -1L; /* error */ + else + return ((long) FSInfoBuf.cSectorUnit * FSInfoBuf.cUnitAvail * + FSInfoBuf.cbSector); +} + +/* + * Functions to get filenames using wildcards + */ + +#ifdef OS2_32BITAPI +static FILEFINDBUF3 ResultBuf; +#else +static FILEFINDBUF ResultBuf; +#endif +static HDIR DirHandle; + +int +findfirst(path) +char *path; +{ +#ifdef OS2_32BITAPI + ULONG +#else + USHORT +#endif + res, SearchCount = 1; + + DirHandle = 1; + res = +#ifdef OS2_32BITAPI + DosFindFirst((PSZ)path,&DirHandle,0L,(PVOID)&ResultBuf,(ULONG)sizeof(ResultBuf),&SearchCount,1L); +#else + DosFindFirst((PSZ)path,&DirHandle,0,&ResultBuf,(USHORT)sizeof(ResultBuf),&SearchCount,0L); +#endif + return(!res); +} + +int +findnext() +{ +#ifdef OS2_32BITAPI + ULONG +#else + USHORT +#endif + res, SearchCount = 1; + + res = +#ifdef OS2_32BITAPI + DosFindNext(DirHandle,(PVOID)&ResultBuf,(ULONG)sizeof(ResultBuf),&SearchCount); +#else + DosFindNext(DirHandle,&ResultBuf,(USHORT)sizeof(ResultBuf),&SearchCount); +#endif + return(!res); +} + +char * +foundfile_buffer() +{ + return(ResultBuf.achName); +} + +long +filesize(file) +char *file; +{ + if (findfirst(file)) { + return (* (long *) (ResultBuf.cbFileAlloc)); + } else + return -1L; +} + +/* + * Chdrive() changes the default drive. + */ +void +chdrive(str) +char *str; +{ + char *ptr; + char drive; + + if ((ptr = index(str, ':')) != (char *)0) { + drive = toupper(*(ptr - 1)); +#ifdef OS2_32BITAPI + DosSetDefaultDisk((ULONG)(drive - 'A' + 1)); +#else + DosSelectDisk((USHORT)(drive - 'A' + 1)); +#endif + } +} + +void +disable_ctrlP() +{ + KBDINFO KbdInfo; + HKBD KbdHandle = 0; + + if (!iflags.rawio) return; + KbdInfo.cb = sizeof(KbdInfo); + KbdGetStatus(&KbdInfo,KbdHandle); + KbdInfo.fsMask &= 0xFFF7; /* ASCII off */ + KbdInfo.fsMask |= 0x0004; /* BINARY on */ + KbdSetStatus(&KbdInfo,KbdHandle); +} + +void +enable_ctrlP() +{ + KBDINFO KbdInfo; + HKBD KbdHandle = 0; + + if (!iflags.rawio) return; + KbdInfo.cb = sizeof(KbdInfo); + KbdGetStatus(&KbdInfo,KbdHandle); + KbdInfo.fsMask &= 0xFFFB; /* BINARY off */ + KbdInfo.fsMask |= 0x0008; /* ASCII on */ + KbdSetStatus(&KbdInfo,KbdHandle); +} + +void +get_scr_size() +{ + VIOMODEINFO ModeInfo; + HVIO VideoHandle = 0; + + ModeInfo.cb = sizeof(ModeInfo); + + (void) VioGetMode(&ModeInfo,VideoHandle); + + CO = ModeInfo.col; + LI = ModeInfo.row; +} + +void +gotoxy(x,y) +int x,y; +{ + HVIO VideoHandle = 0; + + x--; y--; /* (0,0) is upper right corner */ + + (void) VioSetCurPos(x, y, VideoHandle); +} + + +char* get_username(lan_username_size) +int *lan_username_size; +{ + return (char*)0; +} +#ifdef X11_GRAPHICS +int errno; +#endif +#endif /* OS2 */ diff --git a/sys/share/Makefile.lib b/sys/share/Makefile.lib new file mode 100644 index 0000000..dbf9705 --- /dev/null +++ b/sys/share/Makefile.lib @@ -0,0 +1,21 @@ +# SCCS Id: @(#)Makefile.lib 3.4 1990/22/02 +# Nethack makefile for Fred fish termlib -- Norman Meluch +# +CC = cl /c +MODEL = L +CFLAGS = /A$(MODEL) /Os /Oa /Gs /Zp1 /W0 +# +# Termcap routines. +TERMLIB = termlib.lib +# +TL_LOBJECTS = tgetent.o tgetflag.o tgetnum.o \ + tgetstr.o tgoto.o tputs.o \ + isdigit.o fgetlr.o +# +.SUFFIXES: .exe .o .c .obj .asm +# +.c.o: + $(CC) $(CFLAGS) /Fo$*.o $*.c +# +$(TERMLIB): $(TL_LOBJECTS) + lib $(TERMLIB) -+ $(TL_LOBJECTS); diff --git a/sys/share/NetHack.cnf b/sys/share/NetHack.cnf new file mode 100644 index 0000000..d174109 --- /dev/null +++ b/sys/share/NetHack.cnf @@ -0,0 +1,168 @@ +# NetHack Copyright (c) NetHack PC Development Team 1993, 1996, 1999 +# NetHack may be freely redistributed. See license for details. +# +# A '#' at the beginning of a line means the rest of the line is a comment. +# +# Some options MUST be set in this file, other options can be toggled while +# playing. For a list of options available see the file. If +# the game plays slowly you might notice some improvement by setting +# !time and !showexp, which will reduce screen I/O somewhat. +# +# To change the configuration, comment out the unwanted lines, and +# uncomment the configuration you want. +# +# Note: For blind players, please use the file NHAccess.nh as a template. +# + +# *** OPTIONS *** +# +# The three options on this line should be used for most setups. +# If your machine isn't very IBM-compatible, and NetHack doesn't work, +# try commenting out this line. +OPTIONS=rawio,BIOS,IBMgraphics + +# To use VGA graphical tiles on an MS-DOS PC with VGA or better,uncomment +# this: +#OPTIONS=video:autodetect + +# Some versions of NetHack use the pc speaker to play the notes given when +# playing music instruments in NetHack. To use this feature, if available, +# uncomment the following line: +#OPTIONS=soundcard:autodetect + +# If your machine is NEC PC-9800, use: +#OPTIONS=rawio,BIOS,video:default +# If you use an Atari and want tty use: +#OPTIONS=windowtype:tty,rawio,BIOS + + +# Some options to set personal preferences. Uncomment and change these to +# suit your personal preference. If several people are to use the same +# configuration, options like these should not be set. +# +#OPTIONS=name:Janet,role:Valkyrie,race:Human,gender:female,align:lawful +#OPTIONS=dogname:Fido,catname:Morris,fruit:guava +#OPTIONS=horsename:Silver +#OPTIONS=autopickup,pickup_types:$"=/!?+ +#OPTIONS=packorder:")[%?+/=!(*0_` +#OPTIONS=scores:10 top/2 around/own +#OPTIONS=nolegacy,noverbose +#OPTIONS=menustyle:traditional + +# If you wish to change the symbol used to display boulders use: +OPTIONS=boulder:0 +# +# General options. You might also set "silent" so as not to attract +# the boss's attention. +# +# number_pad option can have an optional value of 0 (off), 1 (on), +# or 2(on,legacy-mode) which causes 5='g', alt-5='G', alt-0='I' +# +OPTIONS=time,noshowexp,number_pad:2,lit_corridor + +# Treat space bar as rest. Warning: may be dangerous for new players. +# OPTIONS=rest_on_space + +# +# If you want to get rid of "use #quit to quit..." use: +#OPTIONS=suppress_alert:3.3.1 +# +# +# *** LOCATIONS *** +# Some platforms allow you to change the location where various things are kept. +# IMPORTANT: If you change any of these locations, the directories they +# point at must exist. NetHack will not create them for you. +# +# The default location for everything. +# Note: On Windows HACKDIR defaults to the location +# of the NetHack.exe or NetHackw.exe file so +# setting HACKDIR below to override that is +# not usually necessary or recommended. +#HACKDIR=c:\games\nethack +# +# The location that level files in progress are stored (default=HACKDIR, writeable) +#LEVELDIR=c:\nethack\levels +# +# The location where saved games are kept (default=HACKDIR, writeable) +#SAVEDIR=c:\nethack\save +# +# The location that bones files are kept (default=HACKDIR, writeable) +#BONESDIR=c:\nethack\save +# +# The location that file synchronization locks are stored (default=HACKDIR, writeable) +#LOCKDIR=c:\nethack\levels +# +# The location that a record of game aborts and self-diagnosed game problems +# is kept (default=HACKDIR, writeable) +#TROUBLEDIR=c:\nethack\trouble +# +# *** CHARACTER GRAPHICS *** +# +# See the on-line help or the Guidebook for which symbols are in which +# positions. +# +# If you merely set the IBMgraphics option as above, NetHack will use IBM +# extended ASCII for dungeon characters. If you don't like the selections, +# you can make up your own via these graphics options, but you should still +# set IBMgraphics if you are using IBM graphics characters to get the correct +# processing. +# +# ================================================ +# The defaults using the IBM graphics character set: +#DUNGEON = 032 179 196 218 191 192 217 197 193 194 \ +# 180 195 250 254 254 043 043 240 241 250 \ +# 176 177 243 242 060 062 095 124 092 035 \ +# 244 247 250 247 250 250 035 035 032 035 \ +# 247 +# +# ================================================ +# Some alternatives: +#DUNGEON= 032 186 205 201 187 200 188 206 202 203 \ +# 185 204 249 239 239 254 254 240 241 249 \ +# 177 177 060 062 060 062 095 124 092 035 \ +# 244 247 249 247 042 042 179 196 046 035 \ +# 247 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 034 094 094 094 094 \ +# 094 094 + +# ================================================ +# Here is a recommendation sent in by Michael Feir +# for use by blind NetHack players. +# +#DUNGEON= 032 124 045 124 124 124 124 045 045 045 \ +# 124 124 046 045 124 043 043 046 035 035 \ +# 060 062 060 062 095 092 035 126 126 126 \ +# 126 042 042 035 035 032 035 126 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 +# +#EFFECTS= 124 095 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 + +# ================================================ +# Example using the DEC Rainbow/ANSI line-drawing character set: +# +# If you have compiled with TERMLIB, merely set the DECgraphics option as +# above. NetHack will then switch into the VTxxx line-drawing character set +# (aka ANSI ruling character set '0') for dungeon characters. If you don't +# like the selections, you can make up your own via the graphics options, +# adding 128 to the value of any line-drawing character you want to use. +# (But you should still set DECgraphics to get the correct processing.) + +# ================================================= +# *** VIDEOCOLORS AND VIDEOSHADES *** +# +# While playing on NEC PC-9800, default game display may be difficult to +# read. Try following setting. +# +#OPTIONS=videocolors:4-2-6-1-5-3-4-2-6-1-5-3,videoshades:normal-normal-normal +# +# DEC Rainbows will hang if rawio is set, so they should instead use: +#OPTIONS=BIOS,DECgraphics + diff --git a/sys/share/dgn_comp.h b/sys/share/dgn_comp.h new file mode 100644 index 0000000..8f3ecfd --- /dev/null +++ b/sys/share/dgn_comp.h @@ -0,0 +1,27 @@ +#define INTEGER 257 +#define A_DUNGEON 258 +#define BRANCH 259 +#define CHBRANCH 260 +#define LEVEL 261 +#define RNDLEVEL 262 +#define CHLEVEL 263 +#define RNDCHLEVEL 264 +#define UP_OR_DOWN 265 +#define PROTOFILE 266 +#define DESCRIPTION 267 +#define DESCRIPTOR 268 +#define LEVELDESC 269 +#define ALIGNMENT 270 +#define LEVALIGN 271 +#define ENTRY 272 +#define STAIR 273 +#define NO_UP 274 +#define NO_DOWN 275 +#define PORTAL 276 +#define STRING 277 +typedef union +{ + int i; + char* str; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/sys/share/dgn_lex.c b/sys/share/dgn_lex.c new file mode 100644 index 0000000..054a25d --- /dev/null +++ b/sys/share/dgn_lex.c @@ -0,0 +1,1475 @@ +/* A lexical scanner for NetHack generated by flex */ + +/* Scanner skeleton version: + * flexhack.skl 3.3.0 (from .../flex/RCS/flex.skl,v 2.85 95/04/24 10:48:47) + */ +#define FLEXHACK_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include "config.h" +#define yyconst const /* some code inserted by flex will refer to yyconst */ + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* Return all but the first 'n' matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void FDECL(yyrestart, (FILE *)); + +void FDECL(yy_switch_to_buffer, (YY_BUFFER_STATE)); +void NDECL(yy_load_buffer_state); +YY_BUFFER_STATE FDECL(yy_create_buffer, (FILE *,int)); +void FDECL(yy_delete_buffer, (YY_BUFFER_STATE)); +void FDECL(yy_init_buffer, (YY_BUFFER_STATE,FILE *)); +void FDECL(yy_flush_buffer, (YY_BUFFER_STATE)); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +static genericptr_t FDECL(yy_flex_alloc, (yy_size_t)); +static genericptr_t FDECL(yy_flex_realloc2, (genericptr_t,yy_size_t,int)); +static void FDECL(yy_flex_free, (genericptr_t)); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type NDECL(yy_get_previous_state); +static yy_state_type FDECL(yy_try_NUL_trans, (yy_state_type)); +static int NDECL(yy_get_next_buffer); +static void FDECL(yy_fatal_error, (const char *)); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 35 +#define YY_END_OF_BUFFER 36 +static yyconst short int yy_accept[196] = + { 0, + 0, 0, 36, 34, 33, 32, 34, 34, 29, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, + 32, 0, 30, 29, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, + 4, 0, 25, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 0, 0, 0, 5, 0, 0, 23, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 22, + 15, 0, 21, 7, 19, 0, 0, 0, 0, 0, + 0, 13, 0, 0, 0, 26, 16, 0, 0, 12, + 0, 0, 0, 11, 9, 0, 17, 18, 0, 27, + 0, 28, 24, 10, 0 + + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 5, 6, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 7, 1, 1, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, + 1, 1, 1, 1, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 1, 1, 17, 18, 19, 20, 21, + 1, 22, 23, 24, 25, 26, 1, 1, 27, 1, + 1, 1, 1, 1, 28, 1, 29, 1, 30, 31, + + 32, 33, 34, 35, 36, 1, 37, 38, 39, 40, + 41, 42, 1, 43, 44, 45, 46, 1, 47, 1, + 1, 48, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[49] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static yyconst short int yy_base[198] = + { 0, + 0, 213, 218, 220, 215, 220, 213, 210, 207, 196, + 190, 196, 37, 191, 197, 186, 188, 171, 164, 172, + 174, 173, 18, 160, 159, 154, 157, 11, 194, 194, + 220, 190, 220, 187, 177, 184, 183, 167, 170, 164, + 161, 166, 174, 155, 136, 144, 134, 132, 133, 26, + 135, 143, 147, 128, 145, 220, 170, 220, 158, 152, + 154, 159, 154, 145, 44, 142, 47, 124, 124, 125, + 129, 129, 115, 27, 121, 113, 111, 120, 115, 116, + 134, 142, 132, 128, 137, 121, 130, 129, 125, 129, + 131, 97, 220, 105, 94, 101, 95, 96, 94, 99, + + 105, 101, 89, 220, 95, 112, 114, 51, 112, 107, + 220, 110, 114, 111, 106, 96, 85, 76, 81, 82, + 88, 69, 220, 81, 76, 75, 220, 78, 99, 220, + 88, 97, 87, 88, 92, 93, 88, 91, 90, 71, + 65, 220, 62, 60, 57, 56, 220, 59, 54, 74, + 84, 65, 66, 220, 70, 65, 70, 60, 68, 220, + 220, 52, 220, 220, 220, 46, 50, 57, 61, 67, + 62, 220, 67, 64, 63, 220, 220, 42, 41, 220, + 61, 53, 49, 220, 220, 50, 220, 220, 51, 220, + 46, 220, 220, 220, 220, 62, 60 + + } ; + +static yyconst short int yy_def[198] = + { 0, + 195, 1, 195, 195, 195, 195, 195, 196, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 197, 195, + 195, 196, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 197, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 0, 195, 195 + + } ; + +static yyconst short int yy_nxt[269] = + { 0, + 4, 5, 6, 7, 8, 4, 9, 10, 11, 12, + 13, 14, 4, 4, 4, 4, 15, 4, 4, 4, + 16, 17, 4, 4, 4, 4, 4, 4, 4, 18, + 19, 4, 4, 4, 20, 4, 4, 21, 22, 23, + 4, 24, 25, 26, 27, 28, 4, 4, 38, 49, + 55, 87, 56, 74, 75, 88, 90, 98, 50, 131, + 57, 39, 32, 91, 194, 193, 192, 132, 191, 190, + 189, 188, 99, 187, 186, 185, 184, 183, 182, 181, + 180, 179, 178, 177, 176, 175, 174, 173, 172, 171, + 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, + + 160, 159, 158, 157, 156, 155, 154, 153, 152, 151, + 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, + 140, 139, 138, 137, 136, 135, 134, 133, 130, 129, + 128, 127, 126, 125, 124, 123, 122, 121, 120, 119, + 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, + 108, 107, 106, 105, 104, 103, 102, 101, 100, 97, + 96, 95, 94, 93, 92, 89, 86, 85, 84, 83, + 82, 81, 58, 80, 79, 78, 77, 76, 73, 72, + 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, + 61, 60, 59, 34, 33, 30, 58, 54, 53, 52, + + 51, 48, 47, 46, 45, 44, 43, 42, 41, 40, + 37, 36, 35, 34, 33, 31, 30, 195, 29, 3, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195 + } ; + +static yyconst short int yy_chk[269] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 13, 23, + 28, 65, 28, 50, 50, 65, 67, 74, 23, 108, + 197, 13, 196, 67, 191, 189, 186, 108, 183, 182, + 181, 179, 74, 178, 175, 174, 173, 171, 170, 169, + 168, 167, 166, 162, 159, 158, 157, 156, 155, 153, + 152, 151, 150, 149, 148, 146, 145, 144, 143, 141, + + 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, + 129, 128, 126, 125, 124, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 112, 110, 109, 107, 106, + 105, 103, 102, 101, 100, 99, 98, 97, 96, 95, + 94, 92, 91, 90, 89, 88, 87, 86, 85, 84, + 83, 82, 81, 80, 79, 78, 77, 76, 75, 73, + 72, 71, 70, 69, 68, 66, 64, 63, 62, 61, + 60, 59, 57, 55, 54, 53, 52, 51, 49, 48, + 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, + 37, 36, 35, 34, 32, 30, 29, 27, 26, 25, + + 24, 22, 21, 20, 19, 18, 17, 16, 15, 14, + 12, 11, 10, 9, 8, 7, 5, 3, 2, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, + 195, 195, 195, 195, 195, 195, 195, 195 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +char *yytext; +#define INITIAL 0 +/* SCCS Id: @(#)dgn_lex.c 3.4 2002/03/27 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* Copyright (c) 1990 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#define DGN_COMP + +#include "config.h" +#include "dgn_comp.h" +#include "dgn_file.h" + +/* + * Most of these don't exist in flex, yywrap is macro and + * yyunput is properly declared in flex.skel. + */ +#if !defined(FLEX_SCANNER) && !defined(FLEXHACK_SCANNER) +int FDECL(yyback, (int *,int)); +int NDECL(yylook); +int NDECL(yyinput); +int NDECL(yywrap); +int NDECL(yylex); + /* Traditional lexes let yyunput() and yyoutput() default to int; + * newer ones may declare them as void since they don't return + * values. For even more fun, the lex supplied as part of the + * newer unbundled compiler for SunOS 4.x adds the void declarations + * (under __STDC__ or _cplusplus ifdefs -- otherwise they remain + * int) while the bundled lex and the one with the older unbundled + * compiler do not. To detect this, we need help from outside -- + * sys/unix/Makefile.utl. + * + * Digital UNIX is difficult and still has int in spite of all + * other signs. + */ +# if defined(NeXT) || defined(SVR4) || defined(_AIX32) +# define VOIDYYPUT +# endif +# if !defined(VOIDYYPUT) && defined(POSIX_TYPES) +# if !defined(BOS) && !defined(HISX) && !defined(_M_UNIX) && !defined(VMS) +# define VOIDYYPUT +# endif +# endif +# if !defined(VOIDYYPUT) && defined(WEIRD_LEX) +# if defined(SUNOS4) && defined(__STDC__) && (WEIRD_LEX > 1) +# define VOIDYYPUT +# endif +# endif +# if defined(VOIDYYPUT) && defined(__osf__) +# undef VOIDYYPUT +# endif +# ifdef VOIDYYPUT +void FDECL(yyunput, (int)); +void FDECL(yyoutput, (int)); +# else +int FDECL(yyunput, (int)); +int FDECL(yyoutput, (int)); +# endif +#endif /* !FLEX_SCANNER && !FLEXHACK_SCANNER */ + +#ifdef FLEX_SCANNER +#define YY_MALLOC_DECL \ + genericptr_t FDECL(malloc, (size_t)); \ + genericptr_t FDECL(realloc, (genericptr_t,size_t)); +#endif + + +void FDECL(init_yyin, (FILE *)); +void FDECL(init_yyout, (FILE *)); + +/* this doesn't always get put in dgn_comp.h + * (esp. when using older versions of bison) + */ + +extern YYSTYPE yylval; + +int line_number = 1; + + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +extern int NDECL(yywrap); +#endif + +#ifndef YY_NO_UNPUT +static void FDECL(yyunput, (int,char *)); +#endif + +#ifndef yytext_ptr +static void FDECL(yy_flex_strncpy, (char *,const char *,int)); +#endif + +#ifndef YY_NO_INPUT +static int NDECL(input); +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + yy_current_buffer->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +int NDECL(yylex); +int yylex() + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 196 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 220 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +return(A_DUNGEON); + YY_BREAK +case 2: +YY_RULE_SETUP +{ yylval.i=1; return(UP_OR_DOWN); } + YY_BREAK +case 3: +YY_RULE_SETUP +{ yylval.i=0; return(UP_OR_DOWN); } + YY_BREAK +case 4: +YY_RULE_SETUP +return(ENTRY); + YY_BREAK +case 5: +YY_RULE_SETUP +return(STAIR); + YY_BREAK +case 6: +YY_RULE_SETUP +return(NO_UP); + YY_BREAK +case 7: +YY_RULE_SETUP +return(NO_DOWN); + YY_BREAK +case 8: +YY_RULE_SETUP +return(PORTAL); + YY_BREAK +case 9: +YY_RULE_SETUP +return(PROTOFILE); + YY_BREAK +case 10: +YY_RULE_SETUP +return(DESCRIPTION); + YY_BREAK +case 11: +YY_RULE_SETUP +return(LEVELDESC); + YY_BREAK +case 12: +YY_RULE_SETUP +return(ALIGNMENT); + YY_BREAK +case 13: +YY_RULE_SETUP +return(LEVALIGN); + YY_BREAK +case 14: +YY_RULE_SETUP +{ yylval.i=TOWN ; return(DESCRIPTOR); } + YY_BREAK +case 15: +YY_RULE_SETUP +{ yylval.i=HELLISH ; return(DESCRIPTOR); } + YY_BREAK +case 16: +YY_RULE_SETUP +{ yylval.i=MAZELIKE ; return(DESCRIPTOR); } + YY_BREAK +case 17: +YY_RULE_SETUP +{ yylval.i=ROGUELIKE ; return(DESCRIPTOR); } + YY_BREAK +case 18: +YY_RULE_SETUP +{ yylval.i=D_ALIGN_NONE ; return(DESCRIPTOR); } + YY_BREAK +case 19: +YY_RULE_SETUP +{ yylval.i=D_ALIGN_NONE ; return(DESCRIPTOR); } + YY_BREAK +case 20: +YY_RULE_SETUP +{ yylval.i=D_ALIGN_LAWFUL ; return(DESCRIPTOR); } + YY_BREAK +case 21: +YY_RULE_SETUP +{ yylval.i=D_ALIGN_NEUTRAL ; return(DESCRIPTOR); } + YY_BREAK +case 22: +YY_RULE_SETUP +{ yylval.i=D_ALIGN_CHAOTIC ; return(DESCRIPTOR); } + YY_BREAK +case 23: +YY_RULE_SETUP +return(BRANCH); + YY_BREAK +case 24: +YY_RULE_SETUP +return(CHBRANCH); + YY_BREAK +case 25: +YY_RULE_SETUP +return(LEVEL); + YY_BREAK +case 26: +YY_RULE_SETUP +return(RNDLEVEL); + YY_BREAK +case 27: +YY_RULE_SETUP +return(CHLEVEL); + YY_BREAK +case 28: +YY_RULE_SETUP +return(RNDCHLEVEL); + YY_BREAK +case 29: +YY_RULE_SETUP +{ yylval.i=atoi(yytext); return(INTEGER); } + YY_BREAK +case 30: +YY_RULE_SETUP +{ yytext[yyleng-1] = 0; /* Discard the trailing \" */ + yylval.str = (char *) alloc(strlen(yytext+1)+1); + Strcpy(yylval.str, yytext+1); /* Discard the first \" */ + return(STRING); } + YY_BREAK +case 31: +YY_RULE_SETUP +{ line_number++; } + YY_BREAK +case 32: +YY_RULE_SETUP +{ line_number++; } + YY_BREAK +case 33: +YY_RULE_SETUP +; /* skip trailing tabs & spaces */ + YY_BREAK +case 34: +YY_RULE_SETUP +{ return yytext[0]; } + YY_BREAK +case 35: +YY_RULE_SETUP +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a singled characater, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int old_size = b->yy_buf_size + 2; + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc2( (genericptr_t) b->yy_ch_buf, + b->yy_buf_size + 2, old_size ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 196 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 196 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 195); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +static int input() + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext_ptr = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = + yytext_ptr + YY_MORE_ADJ; + return EOF; + } + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + return input(); + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: + YY_FATAL_ERROR( + "unexpected last match in input()" ); + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + yy_current_buffer->yy_at_bol = (c == '\n'); + + return c; + } + + +void yyrestart( input_file ) +FILE *input_file; + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state() + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (genericptr_t) b->yy_ch_buf ); + + yy_flex_free( (genericptr_t) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int FDECL(isatty, (int)); +#endif +#endif + +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#ifdef YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#ifdef YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; + { + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error( msg ) +const char msg[]; + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n - YY_MORE_ADJ; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +const char *s2; +int n; + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + + +static genericptr_t yy_flex_alloc( size ) +yy_size_t size; + { + return (genericptr_t) alloc((unsigned)size); + } + +/* we want to avoid use of realloc(), so we require that caller supply the + size of the old block of memory */ +static genericptr_t yy_flex_realloc2( ptr, size, old_size ) +genericptr_t ptr; +yy_size_t size; +int old_size; + { + genericptr_t outptr = yy_flex_alloc(size); + + if (ptr) { + char *p = (char *) outptr, *q = (char *) ptr; + + while (--old_size >= 0) *p++ = *q++; + yy_flex_free(ptr); + } + return outptr; + } + +static void yy_flex_free( ptr ) +genericptr_t ptr; + { + free( ptr ); + } + +/*flexhack.skl*/ + + +/* routine to switch to another input file; needed for flex */ +void init_yyin( input_f ) +FILE *input_f; +{ +#if defined(FLEX_SCANNER) || defined(FLEXHACK_SCANNER) + if (yyin) + yyrestart(input_f); + else +#endif + yyin = input_f; +} +/* analogous routine (for completeness) */ +void init_yyout( output_f ) +FILE *output_f; +{ + yyout = output_f; +} + +/*dgn_comp.l*/ diff --git a/sys/share/dgn_yacc.c b/sys/share/dgn_yacc.c new file mode 100644 index 0000000..fe5657b --- /dev/null +++ b/sys/share/dgn_yacc.c @@ -0,0 +1,1054 @@ +#ifndef lint +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +/* SCCS Id: @(#)dgn_comp.c 3.4 1996/06/22 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* Copyright (c) 1990 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the Dungeon Compiler code + */ + +/* In case we're using bison in AIX. This definition must be + * placed before any other C-language construct in the file + * excluding comments and preprocessor directives (thanks IBM + * for this wonderful feature...). + * + * Note: some cpps barf on this 'undefined control' (#pragma). + * Addition of the leading space seems to prevent barfage for now, + * and AIX will still see the directive in its non-standard locale. + */ + +#ifdef _AIX + #pragma alloca /* keep leading space! */ +#endif + +#include "config.h" +#include "date.h" +#include "dgn_file.h" + +void FDECL(yyerror, (const char *)); +void FDECL(yywarning, (const char *)); +int NDECL(yylex); +int NDECL(yyparse); +int FDECL(getchain, (char *)); +int NDECL(check_dungeon); +int NDECL(check_branch); +int NDECL(check_level); +void NDECL(init_dungeon); +void NDECL(init_branch); +void NDECL(init_level); +void NDECL(output_dgn); + +#define Free(ptr) free((genericptr_t)ptr) + +#ifdef AMIGA +# undef printf +#ifndef LATTICE +# define memset(addr,val,len) setmem(addr,len,val) +#endif +#endif + +#define ERR (-1) + +static struct couple couple; +static struct tmpdungeon tmpdungeon[MAXDUNGEON]; +static struct tmplevel tmplevel[LEV_LIMIT]; +static struct tmpbranch tmpbranch[BRANCH_LIMIT]; + +static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1; + +extern int fatal_error; +extern const char *fname; +extern FILE *yyin, *yyout; /* from dgn_lex.c */ + +typedef union +{ + int i; + char* str; +} YYSTYPE; +#define INTEGER 257 +#define A_DUNGEON 258 +#define BRANCH 259 +#define CHBRANCH 260 +#define LEVEL 261 +#define RNDLEVEL 262 +#define CHLEVEL 263 +#define RNDCHLEVEL 264 +#define UP_OR_DOWN 265 +#define PROTOFILE 266 +#define DESCRIPTION 267 +#define DESCRIPTOR 268 +#define LEVELDESC 269 +#define ALIGNMENT 270 +#define LEVALIGN 271 +#define ENTRY 272 +#define STAIR 273 +#define NO_UP 274 +#define NO_DOWN 275 +#define PORTAL 276 +#define STRING 277 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 0, 5, 5, 6, 6, 6, 6, 7, 1, + 1, 8, 8, 8, 12, 13, 15, 15, 14, 10, + 10, 10, 10, 10, 16, 16, 17, 17, 18, 18, + 19, 19, 20, 20, 9, 9, 22, 23, 3, 3, + 3, 3, 3, 2, 2, 4, 21, 11, +}; +short yylen[] = { 2, + 0, 1, 1, 2, 1, 1, 1, 1, 6, 0, + 1, 1, 1, 1, 3, 1, 3, 3, 3, 1, + 1, 1, 1, 1, 6, 7, 7, 8, 3, 3, + 7, 8, 8, 9, 1, 1, 7, 8, 0, 1, + 1, 1, 1, 0, 1, 1, 5, 5, +}; +short yydefred[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, 5, 6, 7, 8, + 12, 13, 14, 16, 20, 21, 22, 23, 24, 35, + 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 0, 0, 19, 17, 29, 18, 30, 15, 46, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 9, 0, 40, + 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, + 0, 45, 37, 0, 27, 0, 0, 0, 0, 0, + 38, 28, 33, 0, 48, 47, 34, +}; +short yydgoto[] = { 14, + 78, 93, 84, 60, 15, 16, 17, 18, 19, 20, + 68, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 70, 30, 31, +}; +short yysindex[] = { -237, + -46, -45, -44, -39, -38, -30, -22, -21, -20, -19, + -18, -17, -16, 0, -237, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -262, -234, -233, -232, -230, -229, -228, -227, -217, + -216, -215, -214, -202, 0, -221, -7, -219, -221, -221, + -221, -221, 0, 0, 0, 0, 0, 0, 0, 19, + 20, 21, -2, -1, -212, -211, -190, -189, -188, -271, + 19, 20, 20, 27, 28, 29, 0, 0, 30, 0, + 0, 0, 0, -193, -271, -182, -180, 19, 19, -179, + -178, 0, 0, -193, 0, -177, -176, -175, 42, 43, + 0, 0, 0, -172, 0, 0, 0, +}; +short yyrindex[] = { 86, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 31, 1, 46, 0, 0, 0, 0, + 0, 0, 0, 31, 0, 61, 76, 0, 0, 0, + 0, 0, 0, 91, 0, 0, 0, +}; +short yygindex[] = { 0, + 0, -6, 4, -43, 0, 75, 0, 0, 0, 0, + -71, 0, 0, 0, 0, 0, 0, 0, 0, 0, + -62, 0, 0, +}; +#define YYTABLESIZE 363 +short yytable[] = { 85, + 39, 80, 81, 82, 83, 63, 64, 65, 66, 86, + 87, 32, 33, 34, 46, 10, 97, 98, 35, 36, + 1, 2, 3, 4, 5, 6, 7, 37, 8, 9, + 44, 10, 11, 12, 13, 38, 39, 40, 41, 42, + 43, 44, 47, 48, 49, 25, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 61, 62, 67, 69, + 26, 72, 73, 71, 74, 75, 76, 77, 79, 88, + 89, 92, 90, 91, 95, 31, 96, 99, 100, 102, + 103, 104, 105, 106, 107, 1, 2, 101, 94, 45, + 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 39, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 0, 39, + 39, 39, 39, 10, 10, 10, 10, 10, 10, 10, + 0, 10, 10, 0, 10, 10, 10, 10, 44, 44, + 44, 44, 44, 44, 44, 0, 44, 44, 0, 44, + 44, 44, 44, 25, 25, 25, 25, 25, 25, 25, + 0, 25, 25, 0, 25, 25, 25, 25, 26, 26, + 26, 26, 26, 26, 26, 0, 26, 26, 0, 26, + 26, 26, 26, 31, 31, 31, 31, 31, 31, 31, + 0, 31, 31, 0, 31, 31, 31, 31, 32, 32, + 32, 32, 32, 32, 32, 0, 32, 32, 0, 32, + 32, 32, 32, +}; +short yycheck[] = { 71, + 0, 273, 274, 275, 276, 49, 50, 51, 52, 72, + 73, 58, 58, 58, 277, 0, 88, 89, 58, 58, + 258, 259, 260, 261, 262, 263, 264, 58, 266, 267, + 0, 269, 270, 271, 272, 58, 58, 58, 58, 58, + 58, 58, 277, 277, 277, 0, 277, 277, 277, 277, + 268, 268, 268, 268, 257, 277, 64, 277, 40, 40, + 0, 64, 64, 43, 277, 277, 257, 257, 257, 43, + 43, 265, 44, 44, 257, 0, 257, 257, 257, 257, + 257, 257, 41, 41, 257, 0, 0, 94, 85, 15, + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, -1, 269, + 270, 271, 272, 258, 259, 260, 261, 262, 263, 264, + -1, 266, 267, -1, 269, 270, 271, 272, 258, 259, + 260, 261, 262, 263, 264, -1, 266, 267, -1, 269, + 270, 271, 272, 258, 259, 260, 261, 262, 263, 264, + -1, 266, 267, -1, 269, 270, 271, 272, 258, 259, + 260, 261, 262, 263, 264, -1, 266, 267, -1, 269, + 270, 271, 272, 258, 259, 260, 261, 262, 263, 264, + -1, 266, 267, -1, 269, 270, 271, 272, 258, 259, + 260, 261, 262, 263, 264, -1, 266, 267, -1, 269, + 270, 271, 272, +}; +#define YYFINAL 14 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 277 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,"'('","')'",0,"'+'","','",0,0,0,0,0,0,0,0,0,0,0,0,0,"':'",0,0,0,0,0, +"'@'",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"INTEGER", +"A_DUNGEON","BRANCH","CHBRANCH","LEVEL","RNDLEVEL","CHLEVEL","RNDCHLEVEL", +"UP_OR_DOWN","PROTOFILE","DESCRIPTION","DESCRIPTOR","LEVELDESC","ALIGNMENT", +"LEVALIGN","ENTRY","STAIR","NO_UP","NO_DOWN","PORTAL","STRING", +}; +char *yyrule[] = { +"$accept : file", +"file :", +"file : dungeons", +"dungeons : dungeon", +"dungeons : dungeons dungeon", +"dungeon : dungeonline", +"dungeon : dungeondesc", +"dungeon : branches", +"dungeon : levels", +"dungeonline : A_DUNGEON ':' STRING bones_tag rcouple optional_int", +"optional_int :", +"optional_int : INTEGER", +"dungeondesc : entry", +"dungeondesc : descriptions", +"dungeondesc : prototype", +"entry : ENTRY ':' INTEGER", +"descriptions : desc", +"desc : DESCRIPTION ':' DESCRIPTOR", +"desc : ALIGNMENT ':' DESCRIPTOR", +"prototype : PROTOFILE ':' STRING", +"levels : level1", +"levels : level2", +"levels : levdesc", +"levels : chlevel1", +"levels : chlevel2", +"level1 : LEVEL ':' STRING bones_tag '@' acouple", +"level1 : RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER", +"level2 : LEVEL ':' STRING bones_tag '@' acouple INTEGER", +"level2 : RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER", +"levdesc : LEVELDESC ':' DESCRIPTOR", +"levdesc : LEVALIGN ':' DESCRIPTOR", +"chlevel1 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple", +"chlevel1 : RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER", +"chlevel2 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER", +"chlevel2 : RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER", +"branches : branch", +"branches : chbranch", +"branch : BRANCH ':' STRING '@' acouple branch_type direction", +"chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction", +"branch_type :", +"branch_type : STAIR", +"branch_type : NO_UP", +"branch_type : NO_DOWN", +"branch_type : PORTAL", +"direction :", +"direction : UP_OR_DOWN", +"bones_tag : STRING", +"acouple : '(' INTEGER ',' INTEGER ')'", +"rcouple : '(' INTEGER ',' INTEGER ')'", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE + +void +init_dungeon() +{ + if(++n_dgns > MAXDUNGEON) { + (void) fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n", + MAXDUNGEON); + (void) fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n"); + exit(EXIT_FAILURE); + } + + in_dungeon = 1; + tmpdungeon[n_dgns].lev.base = 0; + tmpdungeon[n_dgns].lev.rand = 0; + tmpdungeon[n_dgns].chance = 100; + Strcpy(tmpdungeon[n_dgns].name, ""); + Strcpy(tmpdungeon[n_dgns].protoname, ""); + tmpdungeon[n_dgns].flags = 0; + tmpdungeon[n_dgns].levels = 0; + tmpdungeon[n_dgns].branches = 0; + tmpdungeon[n_dgns].entry_lev = 0; +} + +void +init_level() +{ + if(++n_levs > LEV_LIMIT) { + + yyerror("FATAL - Too many special levels defined."); + exit(EXIT_FAILURE); + } + tmplevel[n_levs].lev.base = 0; + tmplevel[n_levs].lev.rand = 0; + tmplevel[n_levs].chance = 100; + tmplevel[n_levs].rndlevs = 0; + tmplevel[n_levs].flags = 0; + Strcpy(tmplevel[n_levs].name, ""); + tmplevel[n_levs].chain = -1; +} + +void +init_branch() +{ + if(++n_brs > BRANCH_LIMIT) { + + yyerror("FATAL - Too many special levels defined."); + exit(EXIT_FAILURE); + } + tmpbranch[n_brs].lev.base = 0; + tmpbranch[n_brs].lev.rand = 0; + Strcpy(tmpbranch[n_brs].name, ""); + tmpbranch[n_brs].chain = -1; +} + +int +getchain(s) + char *s; +{ + int i; + + if(strlen(s)) { + + for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++) + if(!strcmp(tmplevel[i].name, s)) return i; + + yyerror("Can't locate the specified chain level."); + return(-2); + } + return(-1); +} + +/* + * Consistancy checking routines: + * + * - A dungeon must have a unique name. + * - A dungeon must have a originating "branch" command + * (except, of course, for the first dungeon). + * - A dungeon must have a proper depth (at least (1, 0)). + */ + +int +check_dungeon() +{ + int i; + + for(i = 0; i < n_dgns; i++) + if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) { + yyerror("Duplicate dungeon name."); + return(0); + } + + if(n_dgns) + for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) { + if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break; + + if(i >= n_brs - tmpdungeon[n_dgns].branches) { + yyerror("Dungeon cannot be reached."); + return(0); + } + } + + if(tmpdungeon[n_dgns].lev.base <= 0 || + tmpdungeon[n_dgns].lev.rand < 0) { + yyerror("Invalid dungeon depth specified."); + return(0); + } + return(1); /* OK */ +} + +/* + * - A level must have a unique level name. + * - If chained, the level used as reference for the chain + * must be in this dungeon, must be previously defined, and + * the level chained from must be "non-probabilistic" (ie. + * have a 100% chance of existing). + */ + +int +check_level() +{ + int i; + + if(!in_dungeon) { + yyerror("Level defined outside of dungeon."); + return(0); + } + + for(i = 0; i < n_levs; i++) + if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) { + yyerror("Duplicate level name."); + return(0); + } + + if(tmplevel[i].chain == -2) { + yyerror("Invaild level chain reference."); + return(0); + } else if(tmplevel[i].chain != -1) { /* there is a chain */ + /* KMH -- tmplevel[tmpbranch[i].chain].chance was in error */ + if(tmplevel[tmplevel[i].chain].chance != 100) { + yyerror("Level cannot chain from a probabilistic level."); + return(0); + } else if(tmplevel[i].chain == n_levs) { + yyerror("A level cannot chain to itself!"); + return(0); + } + } + return(1); /* OK */ +} + +/* + * - A branch may not branch backwards - to avoid branch loops. + * - A branch name must be unique. + * (ie. You can only have one entry point to each dungeon). + * - If chained, the level used as reference for the chain + * must be in this dungeon, must be previously defined, and + * the level chained from must be "non-probabilistic" (ie. + * have a 100% chance of existing). + */ + +int +check_branch() +{ + int i; + + if(!in_dungeon) { + yyerror("Branch defined outside of dungeon."); + return(0); + } + + for(i = 0; i < n_dgns; i++) + if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) { + + yyerror("Reverse branching not allowed."); + return(0); + } + + if(tmpbranch[i].chain == -2) { + + yyerror("Invaild branch chain reference."); + return(0); + } else if(tmpbranch[i].chain != -1) { /* it is chained */ + + if(tmplevel[tmpbranch[i].chain].chance != 100) { + yyerror("Branch cannot chain from a probabilistic level."); + return(0); + } + } + return(1); /* OK */ +} + +/* + * Output the dungon definition into a file. + * + * The file will have the following format: + * + * [ nethack version ID ] + * [ number of dungeons ] + * [ first dungeon struct ] + * [ levels for the first dungeon ] + * ... + * [ branches for the first dungeon ] + * ... + * [ second dungeon struct ] + * ... + */ + +void +output_dgn() +{ + int nd, cl = 0, nl = 0, + cb = 0, nb = 0; + static struct version_info version_data = { + VERSION_NUMBER, VERSION_FEATURES, + VERSION_SANITY1, VERSION_SANITY2 + }; + + if(++n_dgns <= 0) { + yyerror("FATAL - no dungeons were defined."); + exit(EXIT_FAILURE); + } + + if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) { + yyerror("FATAL - output failure."); + exit(EXIT_FAILURE); + } + + (void) fwrite((char *)&n_dgns, sizeof(int), 1, yyout); + for (nd = 0; nd < n_dgns; nd++) { + (void) fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon), + 1, yyout); + + nl += tmpdungeon[nd].levels; + for(; cl < nl; cl++) + (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel), + 1, yyout); + + nb += tmpdungeon[nd].branches; + for(; cb < nb; cb++) + (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch), + 1, yyout); + } + /* apparently necessary for Think C 5.x, otherwise harmless */ + (void) fflush(yyout); +} + +/*dgn_comp.y*/ +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if ((yys = getenv("YYDEBUG")) != 0) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) != 0 && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) != 0 && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) != 0 && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 2: +{ + output_dgn(); + } +break; +case 9: +{ + init_dungeon(); + Strcpy(tmpdungeon[n_dgns].name, yyvsp[-3].str); + tmpdungeon[n_dgns].boneschar = (char)yyvsp[-2].i; + tmpdungeon[n_dgns].lev.base = couple.base; + tmpdungeon[n_dgns].lev.rand = couple.rand; + tmpdungeon[n_dgns].chance = yyvsp[0].i; + Free(yyvsp[-3].str); + } +break; +case 10: +{ + yyval.i = 0; + } +break; +case 11: +{ + yyval.i = yyvsp[0].i; + } +break; +case 15: +{ + tmpdungeon[n_dgns].entry_lev = yyvsp[0].i; + } +break; +case 17: +{ + if(yyvsp[0].i <= TOWN || yyvsp[0].i >= D_ALIGN_CHAOTIC) + yyerror("Illegal description - ignoring!"); + else + tmpdungeon[n_dgns].flags |= yyvsp[0].i ; + } +break; +case 18: +{ + if(yyvsp[0].i && yyvsp[0].i < D_ALIGN_CHAOTIC) + yyerror("Illegal alignment - ignoring!"); + else + tmpdungeon[n_dgns].flags |= yyvsp[0].i ; + } +break; +case 19: +{ + Strcpy(tmpdungeon[n_dgns].protoname, yyvsp[0].str); + Free(yyvsp[0].str); + } +break; +case 25: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-3].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-2].i; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmpdungeon[n_dgns].levels++; + Free(yyvsp[-3].str); + } +break; +case 26: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-4].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-3].i; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].rndlevs = yyvsp[0].i; + tmpdungeon[n_dgns].levels++; + Free(yyvsp[-4].str); + } +break; +case 27: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-4].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-3].i; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = yyvsp[0].i; + tmpdungeon[n_dgns].levels++; + Free(yyvsp[-4].str); + } +break; +case 28: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-5].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-4].i; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = yyvsp[-1].i; + tmplevel[n_levs].rndlevs = yyvsp[0].i; + tmpdungeon[n_dgns].levels++; + Free(yyvsp[-5].str); + } +break; +case 29: +{ + if(yyvsp[0].i >= D_ALIGN_CHAOTIC) + yyerror("Illegal description - ignoring!"); + else + tmplevel[n_levs].flags |= yyvsp[0].i ; + } +break; +case 30: +{ + if(yyvsp[0].i && yyvsp[0].i < D_ALIGN_CHAOTIC) + yyerror("Illegal alignment - ignoring!"); + else + tmplevel[n_levs].flags |= yyvsp[0].i ; + } +break; +case 31: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-4].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-3].i; + tmplevel[n_levs].chain = getchain(yyvsp[-2].str); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free(yyvsp[-4].str); + Free(yyvsp[-2].str); + } +break; +case 32: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-5].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-4].i; + tmplevel[n_levs].chain = getchain(yyvsp[-3].str); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].rndlevs = yyvsp[0].i; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free(yyvsp[-5].str); + Free(yyvsp[-3].str); + } +break; +case 33: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-5].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-4].i; + tmplevel[n_levs].chain = getchain(yyvsp[-3].str); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = yyvsp[0].i; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free(yyvsp[-5].str); + Free(yyvsp[-3].str); + } +break; +case 34: +{ + init_level(); + Strcpy(tmplevel[n_levs].name, yyvsp[-6].str); + tmplevel[n_levs].boneschar = (char)yyvsp[-5].i; + tmplevel[n_levs].chain = getchain(yyvsp[-4].str); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = yyvsp[-1].i; + tmplevel[n_levs].rndlevs = yyvsp[0].i; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free(yyvsp[-6].str); + Free(yyvsp[-4].str); + } +break; +case 37: +{ + init_branch(); + Strcpy(tmpbranch[n_brs].name, yyvsp[-4].str); + tmpbranch[n_brs].lev.base = couple.base; + tmpbranch[n_brs].lev.rand = couple.rand; + tmpbranch[n_brs].type = yyvsp[-1].i; + tmpbranch[n_brs].up = yyvsp[0].i; + if(!check_branch()) n_brs--; + else tmpdungeon[n_dgns].branches++; + Free(yyvsp[-4].str); + } +break; +case 38: +{ + init_branch(); + Strcpy(tmpbranch[n_brs].name, yyvsp[-5].str); + tmpbranch[n_brs].chain = getchain(yyvsp[-4].str); + tmpbranch[n_brs].lev.base = couple.base; + tmpbranch[n_brs].lev.rand = couple.rand; + tmpbranch[n_brs].type = yyvsp[-1].i; + tmpbranch[n_brs].up = yyvsp[0].i; + if(!check_branch()) n_brs--; + else tmpdungeon[n_dgns].branches++; + Free(yyvsp[-5].str); + Free(yyvsp[-4].str); + } +break; +case 39: +{ + yyval.i = TBR_STAIR; /* two way stair */ + } +break; +case 40: +{ + yyval.i = TBR_STAIR; /* two way stair */ + } +break; +case 41: +{ + yyval.i = TBR_NO_UP; /* no up staircase */ + } +break; +case 42: +{ + yyval.i = TBR_NO_DOWN; /* no down staircase */ + } +break; +case 43: +{ + yyval.i = TBR_PORTAL; /* portal connection */ + } +break; +case 44: +{ + yyval.i = 0; /* defaults to down */ + } +break; +case 45: +{ + yyval.i = yyvsp[0].i; + } +break; +case 46: +{ + char *p = yyvsp[0].str; + if (strlen(p) != 1) { + if (strcmp(p, "none") != 0) + yyerror("Bones marker must be a single char, or \"none\"!"); + *p = '\0'; + } + yyval.i = *p; + Free(p); + } +break; +case 47: +{ + if (yyvsp[-3].i < -MAXLEVEL || yyvsp[-3].i > MAXLEVEL) { + yyerror("Abs base out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else if (yyvsp[-1].i < -1 || + ((yyvsp[-3].i < 0) ? (MAXLEVEL + yyvsp[-3].i + yyvsp[-1].i + 1) > MAXLEVEL : + (yyvsp[-3].i + yyvsp[-1].i) > MAXLEVEL)) { + yyerror("Abs range out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else { + couple.base = yyvsp[-3].i; + couple.rand = yyvsp[-1].i; + } + } +break; +case 48: +{ + if (yyvsp[-3].i < -MAXLEVEL || yyvsp[-3].i > MAXLEVEL) { + yyerror("Rel base out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else { + couple.base = yyvsp[-3].i; + couple.rand = yyvsp[-1].i; + } + } +break; + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) != 0 && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/sys/share/ioctl.c b/sys/share/ioctl.c new file mode 100644 index 0000000..8349281 --- /dev/null +++ b/sys/share/ioctl.c @@ -0,0 +1,187 @@ +/* SCCS Id: @(#)ioctl.c 3.4 1990/22/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This cannot be part of hack.tty.c (as it was earlier) since on some + systems (e.g. MUNIX) the include files and + define the same constants, and the C preprocessor complains. */ + +#include "hack.h" + +#if defined(BSD_JOB_CONTROL) || defined(_BULL_SOURCE) +# ifdef HPUX +#include +# else +# if defined(AIX_31) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE /* causes struct winsize to be present */ +# ifdef _AIX32 +# include +# endif +# endif +# if defined(_BULL_SOURCE) +# include +struct termios termio; +# undef TIMEOUT /* defined in you.h and sys/tty.h */ +# include /* define winsize */ +# include /* define struct ltchars */ +# include /* define TIOGWINSZ */ +# else +# ifdef LINUX +# include +# else +# include +# endif +# endif +# endif +struct ltchars ltchars; +struct ltchars ltchars0 = { -1, -1, -1, -1, -1, -1 }; /* turn all off */ +#else + +# ifdef POSIX_TYPES +#include +struct termios termio; +# if defined(BSD) || defined(_AIX32) +# if defined(_AIX32) && !defined(_ALL_SOURCE) +# define _ALL_SOURCE +# endif +#include +# endif +# else +#include /* also includes part of */ +# if defined(TCSETS) && !defined(AIX_31) +struct termios termio; +# else +struct termio termio; +# endif +# endif +# ifdef AMIX +#include +# endif /* AMIX */ +#endif + +#ifdef SUSPEND /* BSD isn't alone anymore... */ +#include +#endif + +#if defined(TIOCGWINSZ) && (defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE) || defined(SVR4)) +#define USE_WIN_IOCTL +#include "tcap.h" /* for LI and CO */ +#endif + +#ifdef _M_UNIX +extern void NDECL(sco_mapon); +extern void NDECL(sco_mapoff); +#endif +#ifdef __linux__ +extern void NDECL(linux_mapon); +extern void NDECL(linux_mapoff); +#endif + +#ifdef AUX +void +catch_stp() +{ + signal(SIGTSTP, SIG_DFL); + dosuspend(); +} +#endif /* AUX */ + +void +getwindowsz() +{ +#ifdef USE_WIN_IOCTL + /* + * ttysize is found on Suns and BSD + * winsize is found on Suns, BSD, and Ultrix + */ + struct winsize ttsz; + + if (ioctl(fileno(stdin), (int)TIOCGWINSZ, (char *)&ttsz) != -1) { + /* + * Use the kernel's values for lines and columns if it has + * any idea. + */ + if (ttsz.ws_row) + LI = ttsz.ws_row; + if (ttsz.ws_col) + CO = ttsz.ws_col; + } +#endif +} + +void +getioctls() +{ +#ifdef BSD_JOB_CONTROL + (void) ioctl(fileno(stdin), (int) TIOCGLTC, (char *) <chars); + (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) <chars0); +#else +# ifdef POSIX_TYPES + (void) tcgetattr(fileno(stdin), &termio); +# else +# if defined(TCSETS) && !defined(AIX_31) + (void) ioctl(fileno(stdin), (int) TCGETS, &termio); +# else + (void) ioctl(fileno(stdin), (int) TCGETA, &termio); +# endif +# endif +#endif + getwindowsz(); +#ifdef AUX + ( void ) signal ( SIGTSTP , catch_stp ) ; +#endif +} + +void +setioctls() +{ +#ifdef BSD_JOB_CONTROL + (void) ioctl(fileno(stdin), (int) TIOCSLTC, (char *) <chars); +#else +# ifdef POSIX_TYPES + (void) tcsetattr(fileno(stdin), TCSADRAIN, &termio); +# else +# if defined(TCSETS) && !defined(AIX_31) + (void) ioctl(fileno(stdin), (int) TCSETSW, &termio); +# else + (void) ioctl(fileno(stdin), (int) TCSETAW, &termio); +# endif +# endif +#endif +} + +#ifdef SUSPEND /* No longer implies BSD */ +int +dosuspend() +{ +# ifdef SIGTSTP + if(signal(SIGTSTP, SIG_IGN) == SIG_DFL) { + suspend_nhwindows((char *)0); +# ifdef _M_UNIX + sco_mapon(); +# endif +# ifdef __linux__ + linux_mapon(); +# endif + (void) signal(SIGTSTP, SIG_DFL); +# ifdef AUX + ( void ) kill ( 0 , SIGSTOP ) ; +# else + (void) kill(0, SIGTSTP); +# endif +# ifdef _M_UNIX + sco_mapoff(); +# endif +# ifdef __linux__ + linux_mapoff(); +# endif + resume_nhwindows(); + } else { + pline("I don't think your shell has job control."); + } +# else + pline("Sorry, it seems we have no SIGTSTP here. Try ! or S."); +# endif + return(0); +} +#endif /* SUSPEND */ diff --git a/sys/share/lev_comp.h b/sys/share/lev_comp.h new file mode 100644 index 0000000..8152d60 --- /dev/null +++ b/sys/share/lev_comp.h @@ -0,0 +1,79 @@ +#define CHAR 257 +#define INTEGER 258 +#define BOOLEAN 259 +#define PERCENT 260 +#define MESSAGE_ID 261 +#define MAZE_ID 262 +#define LEVEL_ID 263 +#define LEV_INIT_ID 264 +#define GEOMETRY_ID 265 +#define NOMAP_ID 266 +#define OBJECT_ID 267 +#define COBJECT_ID 268 +#define MONSTER_ID 269 +#define TRAP_ID 270 +#define DOOR_ID 271 +#define DRAWBRIDGE_ID 272 +#define MAZEWALK_ID 273 +#define WALLIFY_ID 274 +#define REGION_ID 275 +#define FILLING 276 +#define RANDOM_OBJECTS_ID 277 +#define RANDOM_MONSTERS_ID 278 +#define RANDOM_PLACES_ID 279 +#define ALTAR_ID 280 +#define LADDER_ID 281 +#define STAIR_ID 282 +#define NON_DIGGABLE_ID 283 +#define NON_PASSWALL_ID 284 +#define ROOM_ID 285 +#define PORTAL_ID 286 +#define TELEPRT_ID 287 +#define BRANCH_ID 288 +#define LEV 289 +#define CHANCE_ID 290 +#define CORRIDOR_ID 291 +#define GOLD_ID 292 +#define ENGRAVING_ID 293 +#define FOUNTAIN_ID 294 +#define POOL_ID 295 +#define SINK_ID 296 +#define NONE 297 +#define RAND_CORRIDOR_ID 298 +#define DOOR_STATE 299 +#define LIGHT_STATE 300 +#define CURSE_TYPE 301 +#define ENGRAVING_TYPE 302 +#define DIRECTION 303 +#define RANDOM_TYPE 304 +#define O_REGISTER 305 +#define M_REGISTER 306 +#define P_REGISTER 307 +#define A_REGISTER 308 +#define ALIGNMENT 309 +#define LEFT_OR_RIGHT 310 +#define CENTER 311 +#define TOP_OR_BOT 312 +#define ALTAR_TYPE 313 +#define UP_OR_DOWN 314 +#define SUBROOM_ID 315 +#define NAME_ID 316 +#define FLAGS_ID 317 +#define FLAG_TYPE 318 +#define MON_ATTITUDE 319 +#define MON_ALERTNESS 320 +#define MON_APPEARANCE 321 +#define CONTAINED 322 +#define STRING 323 +#define MAP_ID 324 +typedef union +{ + int i; + char* map; + struct { + xchar room; + xchar wall; + xchar door; + } corpos; +} YYSTYPE; +extern YYSTYPE yylval; diff --git a/sys/share/lev_lex.c b/sys/share/lev_lex.c new file mode 100644 index 0000000..776e048 --- /dev/null +++ b/sys/share/lev_lex.c @@ -0,0 +1,2088 @@ +/* A lexical scanner for NetHack generated by flex */ + +/* Scanner skeleton version: + * flexhack.skl 3.3.0 (from .../flex/RCS/flex.skl,v 2.85 95/04/24 10:48:47) + */ +#define FLEXHACK_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include "config.h" +#define yyconst const /* some code inserted by flex will refer to yyconst */ + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* Return all but the first 'n' matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void FDECL(yyrestart, (FILE *)); + +void FDECL(yy_switch_to_buffer, (YY_BUFFER_STATE)); +void NDECL(yy_load_buffer_state); +YY_BUFFER_STATE FDECL(yy_create_buffer, (FILE *,int)); +void FDECL(yy_delete_buffer, (YY_BUFFER_STATE)); +void FDECL(yy_init_buffer, (YY_BUFFER_STATE,FILE *)); +void FDECL(yy_flush_buffer, (YY_BUFFER_STATE)); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +static genericptr_t FDECL(yy_flex_alloc, (yy_size_t)); +static genericptr_t FDECL(yy_flex_realloc2, (genericptr_t,yy_size_t,int)); +static void FDECL(yy_flex_free, (genericptr_t)); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type NDECL(yy_get_previous_state); +static yy_state_type FDECL(yy_try_NUL_trans, (yy_state_type)); +static int NDECL(yy_get_next_buffer); +static void FDECL(yy_fatal_error, (const char *)); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 112 +#define YY_END_OF_BUFFER 113 +static yyconst short int yy_accept[640] = + { 0, + 0, 0, 0, 0, 113, 111, 108, 107, 111, 111, + 111, 111, 105, 4, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, + 111, 2, 111, 108, 111, 111, 105, 111, 111, 111, + 111, 111, 111, 111, 111, 111, 111, 111, 111, 108, + 107, 0, 106, 0, 0, 105, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 87, 0, 0, 3, 0, 2, 2, 0, 108, 0, + 105, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 110, 0, 110, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 72, 0, 0, 67, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 109, 0, 0, 0, 0, 0, 17, + 0, 0, 0, 0, 0, 40, 0, 0, 0, 6, + 0, 0, 42, 0, 0, 0, 33, 0, 0, 0, + + 36, 32, 0, 0, 0, 16, 0, 0, 104, 0, + 0, 0, 0, 0, 0, 0, 0, 93, 0, 0, + 0, 0, 0, 0, 88, 91, 51, 0, 0, 0, + 0, 0, 0, 60, 0, 0, 0, 0, 0, 94, + 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 90, + 0, 0, 0, 53, 12, 0, 0, 0, 0, 0, + 25, 0, 0, 0, 0, 0, 0, 10, 0, 0, + 0, 0, 8, 0, 0, 0, 7, 0, 0, 0, + 0, 0, 0, 27, 0, 0, 0, 59, 86, 0, + + 0, 80, 0, 0, 0, 0, 74, 0, 0, 0, + 0, 0, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, + 0, 0, 58, 0, 64, 0, 0, 0, 52, 0, + 0, 68, 0, 0, 30, 43, 0, 0, 0, 0, + 0, 0, 0, 26, 0, 0, 0, 0, 0, 13, + 28, 0, 21, 0, 0, 0, 0, 79, 0, 66, + 49, 62, 46, 0, 0, 97, 0, 69, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, + 48, 101, 0, 0, 56, 0, 54, 0, 0, 85, + + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 15, 0, 0, 0, 37, 0, 20, + 0, 95, 0, 0, 92, 0, 0, 0, 78, 0, + 0, 0, 0, 57, 73, 71, 0, 0, 0, 84, + 0, 0, 0, 0, 39, 0, 0, 31, 11, 9, + 19, 0, 0, 0, 0, 0, 0, 0, 102, 0, + 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, + 77, 0, 96, 70, 14, 0, 41, 0, 0, 0, + 0, 0, 0, 0, 75, 98, 61, 0, 100, 44, + 81, 82, 0, 0, 0, 18, 0, 0, 0, 0, + + 0, 0, 0, 63, 0, 99, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 35, 0, 0, + 0, 0, 0, 76, 103, 0, 0, 0, 24, 0, + 0, 0, 22, 0, 0, 23, 29, 38, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 5, 1, 6, 7, 1, 8, 1, 9, 1, + 1, 1, 10, 1, 11, 12, 1, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 14, 1, 1, + 1, 1, 1, 1, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 1, 31, 32, 33, 34, 35, 36, 1, 37, 38, + 39, 40, 41, 1, 42, 1, 43, 44, 45, 46, + + 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 58, 1, 59, 60, 61, 62, 63, 64, 1, + 1, 1, 12, 12, 12, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[65] = + { 0, + 1, 2, 3, 2, 2, 1, 2, 1, 1, 2, + 2, 2, 2, 1, 2, 2, 2, 1, 1, 2, + 1, 2, 2, 1, 2, 2, 1, 1, 1, 2, + 1, 2, 2, 1, 1, 2, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1 + } ; + +static yyconst short int yy_base[645] = + { 0, + 0, 58, 83, 62, 796, 797, 65, 797, 792, 788, + 753, 779, 778, 797, 764, 758, 44, 43, 760, 42, + 62, 759, 60, 63, 68, 770, 756, 92, 91, 91, + 769, 71, 72, 76, 87, 55, 84, 77, 61, 96, + 103, 95, 104, 103, 108, 111, 99, 107, 736, 779, + 151, 797, 778, 169, 173, 179, 182, 185, 194, 197, + 752, 180, 185, 193, 181, 194, 202, 214, 241, 75, + 797, 773, 797, 769, 768, 763, 742, 759, 758, 136, + 743, 756, 749, 754, 734, 738, 740, 742, 746, 728, + 724, 729, 732, 732, 151, 734, 162, 729, 735, 726, + + 726, 738, 736, 725, 735, 723, 225, 224, 143, 704, + 693, 703, 698, 683, 686, 683, 685, 697, 682, 162, + 679, 673, 676, 675, 685, 679, 678, 180, 671, 666, + 172, 668, 683, 192, 668, 670, 663, 229, 672, 676, + 679, 678, 664, 670, 662, 203, 655, 658, 653, 235, + 797, 654, 710, 797, 212, 797, 797, 709, 274, 264, + 265, 269, 277, 247, 282, 283, 285, 293, 294, 797, + 708, 0, 797, 701, 700, 693, 679, 678, 672, 673, + 672, 666, 670, 679, 671, 671, 679, 663, 677, 675, + 674, 660, 659, 671, 674, 646, 668, 660, 652, 666, + + 660, 655, 656, 657, 648, 659, 647, 650, 254, 626, + 631, 616, 625, 618, 610, 608, 615, 611, 605, 608, + 604, 609, 601, 601, 604, 598, 597, 598, 596, 601, + 606, 607, 591, 797, 590, 591, 797, 596, 601, 590, + 602, 592, 584, 582, 588, 584, 585, 272, 578, 591, + 590, 580, 590, 589, 587, 582, 586, 571, 578, 567, + 797, 580, 564, 574, 573, 562, 266, 303, 299, 595, + 308, 311, 309, 797, 590, 603, 602, 603, 594, 797, + 600, 600, 582, 580, 593, 797, 569, 591, 583, 572, + 592, 573, 797, 575, 306, 587, 797, 588, 573, 572, + + 797, 797, 569, 570, 568, 797, 574, 304, 797, 540, + 536, 535, 546, 545, 531, 533, 542, 797, 541, 527, + 539, 534, 541, 536, 797, 797, 797, 539, 534, 533, + 568, 530, 526, 797, 529, 528, 531, 517, 520, 797, + 510, 511, 518, 511, 524, 509, 797, 515, 510, 518, + 797, 515, 514, 503, 498, 497, 496, 500, 505, 797, + 495, 499, 491, 797, 797, 548, 317, 535, 320, 321, + 797, 527, 529, 524, 528, 514, 509, 797, 528, 509, + 514, 509, 797, 524, 517, 518, 797, 513, 520, 501, + 507, 505, 503, 797, 501, 500, 508, 797, 797, 480, + + 468, 797, 478, 469, 467, 463, 797, 475, 471, 468, + 472, 454, 797, 470, 293, 461, 460, 464, 466, 450, + 450, 462, 461, 464, 457, 446, 446, 460, 797, 455, + 440, 452, 797, 444, 797, 436, 437, 449, 797, 435, + 440, 797, 463, 333, 797, 797, 464, 462, 467, 466, + 465, 456, 471, 797, 459, 465, 452, 461, 449, 797, + 797, 438, 797, 452, 447, 440, 433, 797, 429, 797, + 797, 797, 797, 418, 417, 797, 425, 797, 424, 419, + 412, 421, 416, 797, 404, 404, 419, 404, 408, 405, + 797, 797, 406, 401, 797, 396, 797, 402, 405, 797, + + 408, 407, 797, 332, 434, 421, 433, 422, 421, 411, + 417, 421, 797, 797, 424, 412, 341, 797, 410, 797, + 388, 797, 394, 393, 797, 391, 389, 380, 797, 379, + 376, 387, 372, 797, 797, 797, 381, 374, 376, 797, + 380, 382, 381, 395, 797, 404, 403, 797, 797, 797, + 797, 408, 386, 392, 391, 403, 392, 375, 797, 370, + 369, 353, 363, 353, 355, 363, 350, 797, 359, 348, + 797, 356, 797, 797, 797, 386, 797, 388, 388, 371, + 373, 376, 384, 367, 797, 797, 797, 336, 797, 797, + 797, 797, 340, 334, 333, 797, 367, 366, 360, 358, + + 370, 371, 368, 797, 339, 797, 338, 365, 341, 343, + 332, 347, 344, 339, 313, 311, 797, 797, 338, 322, + 293, 278, 277, 797, 797, 253, 224, 209, 797, 161, + 138, 123, 797, 101, 69, 797, 797, 797, 797, 371, + 374, 376, 378, 381 + } ; + +static yyconst short int yy_def[645] = + { 0, + 639, 1, 1, 3, 639, 639, 639, 639, 639, 640, + 641, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 642, + 639, 639, 639, 643, 643, 643, 643, 643, 643, 643, + 639, 60, 60, 60, 60, 60, 60, 60, 642, 639, + 639, 640, 639, 639, 644, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 642, 639, 639, 639, 639, 639, 60, 60, + 60, 60, 60, 639, 60, 60, 60, 60, 60, 639, + 642, 69, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 60, 60, 639, + 60, 60, 60, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 60, 639, 60, 60, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 60, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 60, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 0, 639, + 639, 639, 639, 639 + } ; + +static yyconst short int yy_nxt[862] = + { 0, + 6, 7, 8, 9, 7, 10, 6, 6, 11, 12, + 12, 6, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 6, 22, 6, 6, 23, 24, 25, 26, 27, + 28, 29, 30, 6, 6, 31, 6, 6, 32, 6, + 6, 6, 33, 34, 35, 36, 37, 38, 6, 39, + 6, 6, 6, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 6, 49, 50, 79, 70, 84, 69, 70, + 85, 81, 80, 82, 89, 107, 70, 91, 90, 70, + 86, 92, 94, 108, 51, 52, 53, 54, 51, 55, + 87, 93, 56, 56, 55, 57, 95, 58, 59, 60, + + 638, 61, 62, 128, 55, 63, 98, 55, 64, 104, + 99, 122, 65, 101, 66, 67, 123, 129, 68, 126, + 100, 105, 55, 102, 103, 109, 124, 127, 637, 113, + 110, 111, 114, 117, 115, 112, 118, 116, 130, 125, + 119, 137, 131, 120, 134, 135, 132, 139, 121, 141, + 143, 138, 133, 145, 636, 148, 142, 149, 144, 136, + 146, 140, 150, 179, 151, 155, 180, 147, 635, 92, + 70, 157, 158, 159, 639, 157, 158, 195, 196, 93, + 639, 157, 158, 639, 157, 158, 639, 157, 158, 634, + 198, 161, 199, 210, 161, 639, 157, 158, 639, 157, + + 158, 160, 160, 211, 222, 165, 160, 166, 85, 97, + 162, 90, 88, 639, 160, 160, 167, 223, 163, 235, + 104, 639, 639, 160, 78, 80, 168, 103, 169, 107, + 639, 209, 105, 231, 236, 160, 108, 108, 232, 239, + 633, 267, 639, 170, 171, 172, 240, 172, 241, 191, + 172, 172, 172, 172, 632, 172, 172, 172, 308, 258, + 172, 259, 172, 172, 270, 172, 172, 183, 365, 366, + 172, 245, 172, 172, 246, 70, 172, 161, 159, 263, + 172, 631, 264, 247, 248, 160, 160, 249, 265, 250, + 160, 269, 639, 639, 309, 160, 271, 639, 160, 630, + + 189, 268, 639, 160, 160, 639, 160, 272, 308, 629, + 639, 639, 203, 639, 160, 160, 346, 367, 347, 273, + 160, 639, 639, 388, 160, 628, 277, 639, 283, 160, + 160, 639, 160, 369, 370, 389, 639, 639, 160, 639, + 627, 160, 160, 444, 309, 639, 479, 371, 639, 639, + 394, 480, 504, 160, 160, 626, 625, 554, 624, 623, + 639, 639, 622, 621, 620, 619, 618, 555, 520, 556, + 557, 72, 72, 72, 74, 74, 153, 153, 153, 160, + 160, 174, 174, 617, 616, 615, 614, 613, 612, 611, + 610, 609, 608, 607, 606, 605, 604, 603, 602, 601, + + 600, 599, 598, 597, 596, 595, 594, 593, 592, 591, + 590, 589, 588, 587, 586, 585, 584, 583, 582, 581, + 580, 579, 578, 577, 576, 575, 574, 573, 572, 571, + 570, 569, 568, 567, 566, 565, 564, 563, 562, 561, + 560, 559, 558, 553, 552, 551, 550, 549, 548, 547, + 546, 545, 544, 543, 542, 541, 540, 539, 538, 537, + 536, 535, 534, 533, 532, 531, 530, 529, 528, 527, + 526, 525, 524, 523, 522, 521, 520, 519, 518, 517, + 516, 515, 514, 513, 512, 511, 510, 509, 508, 507, + 506, 505, 503, 502, 501, 500, 499, 498, 497, 496, + + 495, 494, 493, 492, 491, 490, 489, 488, 487, 486, + 485, 484, 483, 482, 481, 478, 477, 476, 475, 474, + 473, 472, 471, 470, 469, 468, 467, 466, 465, 464, + 463, 462, 461, 460, 459, 458, 457, 456, 455, 454, + 453, 452, 451, 450, 449, 448, 447, 446, 445, 443, + 365, 442, 441, 440, 439, 438, 437, 436, 435, 434, + 433, 432, 431, 430, 429, 428, 427, 426, 425, 424, + 423, 422, 421, 420, 419, 418, 417, 416, 415, 414, + 413, 412, 411, 410, 409, 408, 407, 406, 405, 404, + 403, 402, 401, 400, 399, 398, 397, 396, 395, 394, + + 393, 392, 391, 390, 387, 386, 385, 384, 383, 382, + 381, 380, 379, 378, 377, 376, 375, 374, 373, 372, + 371, 368, 364, 363, 362, 361, 360, 359, 358, 357, + 356, 355, 354, 353, 352, 351, 350, 349, 348, 345, + 344, 343, 342, 341, 340, 339, 338, 337, 336, 335, + 334, 333, 332, 331, 330, 329, 328, 327, 326, 325, + 324, 323, 322, 321, 320, 319, 318, 317, 316, 315, + 314, 313, 312, 311, 310, 307, 306, 305, 304, 303, + 302, 301, 300, 299, 298, 297, 296, 295, 294, 293, + 292, 291, 290, 289, 288, 287, 286, 285, 284, 283, + + 282, 281, 280, 279, 278, 277, 276, 275, 274, 274, + 170, 157, 154, 266, 262, 261, 260, 257, 256, 255, + 254, 253, 252, 251, 244, 243, 242, 238, 237, 234, + 233, 230, 229, 228, 227, 226, 225, 224, 221, 220, + 219, 218, 217, 216, 215, 214, 213, 212, 208, 207, + 206, 205, 204, 203, 202, 201, 200, 197, 194, 193, + 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, + 182, 181, 178, 177, 176, 76, 175, 173, 73, 164, + 156, 154, 152, 106, 97, 96, 88, 83, 78, 77, + 76, 76, 75, 73, 71, 639, 5, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639 + } ; + +static yyconst short int yy_chk[862] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 17, 7, 20, 4, 7, + 20, 18, 17, 18, 23, 32, 70, 24, 23, 70, + 21, 24, 25, 32, 2, 3, 3, 3, 4, 3, + 21, 24, 3, 3, 3, 3, 25, 3, 3, 3, + + 635, 3, 3, 39, 3, 3, 28, 3, 3, 30, + 28, 36, 3, 29, 3, 3, 36, 39, 3, 38, + 28, 30, 3, 29, 29, 33, 37, 38, 634, 34, + 33, 33, 34, 35, 34, 33, 35, 34, 40, 37, + 35, 42, 40, 35, 41, 41, 40, 43, 35, 44, + 45, 42, 40, 46, 632, 47, 44, 47, 45, 41, + 46, 43, 48, 80, 48, 51, 80, 46, 631, 51, + 54, 54, 54, 54, 55, 55, 55, 95, 95, 51, + 56, 56, 56, 57, 57, 57, 58, 58, 58, 630, + 97, 56, 97, 109, 57, 59, 59, 59, 60, 60, + + 60, 62, 65, 109, 120, 62, 63, 64, 62, 65, + 58, 64, 63, 63, 64, 66, 66, 120, 60, 131, + 67, 64, 66, 67, 59, 60, 66, 66, 68, 107, + 67, 108, 67, 128, 131, 68, 108, 107, 128, 134, + 628, 155, 68, 69, 69, 69, 134, 69, 134, 155, + 69, 69, 69, 69, 627, 69, 69, 69, 209, 146, + 69, 146, 69, 69, 164, 69, 69, 164, 267, 267, + 69, 138, 69, 69, 138, 159, 69, 161, 159, 150, + 69, 626, 150, 138, 138, 160, 161, 138, 150, 138, + 162, 163, 160, 161, 209, 159, 165, 162, 163, 623, + + 166, 162, 159, 165, 166, 163, 167, 168, 308, 622, + 165, 166, 167, 167, 168, 169, 248, 268, 248, 169, + 269, 168, 169, 295, 268, 621, 269, 269, 271, 271, + 273, 268, 272, 272, 273, 295, 271, 273, 367, 272, + 620, 369, 370, 370, 308, 367, 415, 367, 369, 370, + 369, 415, 444, 504, 444, 619, 616, 517, 615, 614, + 504, 444, 613, 612, 611, 610, 609, 517, 504, 517, + 517, 640, 640, 640, 641, 641, 642, 642, 642, 643, + 643, 644, 644, 608, 607, 605, 603, 602, 601, 600, + 599, 598, 597, 595, 594, 593, 588, 584, 583, 582, + + 581, 580, 579, 578, 576, 572, 570, 569, 567, 566, + 565, 564, 563, 562, 561, 560, 558, 557, 556, 555, + 554, 553, 552, 547, 546, 544, 543, 542, 541, 539, + 538, 537, 533, 532, 531, 530, 528, 527, 526, 524, + 523, 521, 519, 516, 515, 512, 511, 510, 509, 508, + 507, 506, 505, 502, 501, 499, 498, 496, 494, 493, + 490, 489, 488, 487, 486, 485, 483, 482, 481, 480, + 479, 477, 475, 474, 469, 467, 466, 465, 464, 462, + 459, 458, 457, 456, 455, 453, 452, 451, 450, 449, + 448, 447, 443, 441, 440, 438, 437, 436, 434, 432, + + 431, 430, 428, 427, 426, 425, 424, 423, 422, 421, + 420, 419, 418, 417, 416, 414, 412, 411, 410, 409, + 408, 406, 405, 404, 403, 401, 400, 397, 396, 395, + 393, 392, 391, 390, 389, 388, 386, 385, 384, 382, + 381, 380, 379, 377, 376, 375, 374, 373, 372, 368, + 366, 363, 362, 361, 359, 358, 357, 356, 355, 354, + 353, 352, 350, 349, 348, 346, 345, 344, 343, 342, + 341, 339, 338, 337, 336, 335, 333, 332, 331, 330, + 329, 328, 324, 323, 322, 321, 320, 319, 317, 316, + 315, 314, 313, 312, 311, 310, 307, 305, 304, 303, + + 300, 299, 298, 296, 294, 292, 291, 290, 289, 288, + 287, 285, 284, 283, 282, 281, 279, 278, 277, 276, + 275, 270, 266, 265, 264, 263, 262, 260, 259, 258, + 257, 256, 255, 254, 253, 252, 251, 250, 249, 247, + 246, 245, 244, 243, 242, 241, 240, 239, 238, 236, + 235, 233, 232, 231, 230, 229, 228, 227, 226, 225, + 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, + 214, 213, 212, 211, 210, 208, 207, 206, 205, 204, + 203, 202, 201, 200, 199, 198, 197, 196, 195, 194, + 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, + + 183, 182, 181, 180, 179, 178, 177, 176, 175, 174, + 171, 158, 153, 152, 149, 148, 147, 145, 144, 143, + 142, 141, 140, 139, 137, 136, 135, 133, 132, 130, + 129, 127, 126, 125, 124, 123, 122, 121, 119, 118, + 117, 116, 115, 114, 113, 112, 111, 110, 106, 105, + 104, 103, 102, 101, 100, 99, 98, 96, 94, 93, + 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, + 82, 81, 79, 78, 77, 76, 75, 74, 72, 61, + 53, 50, 49, 31, 27, 26, 22, 19, 16, 15, + 13, 12, 11, 10, 9, 5, 639, 639, 639, 639, + + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, + 639 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +char *yytext; +#define INITIAL 0 +/* SCCS Id: @(#)lev_lex.c 3.4 2002/03/27 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#define LEV_LEX_C + +#include "hack.h" +#include "lev_comp.h" +#include "sp_lev.h" + +/* Most of these don't exist in flex, yywrap is macro and + * yyunput is properly declared in flex.skel. + */ +#if !defined(FLEX_SCANNER) && !defined(FLEXHACK_SCANNER) +int FDECL(yyback, (int *,int)); +int NDECL(yylook); +int NDECL(yyinput); +int NDECL(yywrap); +int NDECL(yylex); + /* Traditional lexes let yyunput() and yyoutput() default to int; + * newer ones may declare them as void since they don't return + * values. For even more fun, the lex supplied as part of the + * newer unbundled compiler for SunOS 4.x adds the void declarations + * (under __STDC__ or _cplusplus ifdefs -- otherwise they remain + * int) while the bundled lex and the one with the older unbundled + * compiler do not. To detect this, we need help from outside -- + * sys/unix/Makefile.utl. + * + * Digital UNIX is difficult and still has int in spite of all + * other signs. + */ +# if defined(NeXT) || defined(SVR4) || defined(_AIX32) +# define VOIDYYPUT +# endif +# if !defined(VOIDYYPUT) && defined(POSIX_TYPES) +# if !defined(BOS) && !defined(HISX) && !defined(_M_UNIX) && !defined(VMS) +# define VOIDYYPUT +# endif +# endif +# if !defined(VOIDYYPUT) && defined(WEIRD_LEX) +# if defined(SUNOS4) && defined(__STDC__) && (WEIRD_LEX > 1) +# define VOIDYYPUT +# endif +# endif +# if defined(VOIDYYPUT) && defined(__osf__) +# undef VOIDYYPUT +# endif +# ifdef VOIDYYPUT +void FDECL(yyunput, (int)); +void FDECL(yyoutput, (int)); +# else +int FDECL(yyunput, (int)); +int FDECL(yyoutput, (int)); +# endif +#endif /* !FLEX_SCANNER && !FLEXHACK_SCANNER */ + +#ifdef FLEX_SCANNER +#define YY_MALLOC_DECL \ + genericptr_t FDECL(malloc, (size_t)); \ + genericptr_t FDECL(realloc, (genericptr_t,size_t)); +#endif + +void FDECL(init_yyin, (FILE *)); +void FDECL(init_yyout, (FILE *)); + +/* + * This doesn't always get put in lev_comp.h + * (esp. when using older versions of bison). + */ +extern YYSTYPE yylval; + +int line_number = 1, colon_line_number = 1; +static char map[4096]; +static int map_cnt = 0; + +#define MAPC 1 + + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +extern int NDECL(yywrap); +#endif + +#ifndef YY_NO_UNPUT +static void FDECL(yyunput, (int,char *)); +#endif + +#ifndef yytext_ptr +static void FDECL(yy_flex_strncpy, (char *,const char *,int)); +#endif + +#ifndef YY_NO_INPUT +static int NDECL(input); +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ + && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + yy_current_buffer->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +int NDECL(yylex); +int yylex() + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + + + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 640 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 797 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +{ + BEGIN(INITIAL); + yylval.map = (char *) alloc(map_cnt + 1); + (void) strncpy(yylval.map, map, map_cnt); + yylval.map[map_cnt] = 0; + map_cnt = 0; + return MAP_ID; + } + YY_BREAK +case 2: +YY_RULE_SETUP +{ + int len = yyleng; + /* convert \r\n to \n */ + if (len >= 2 && yytext[len - 2] == '\r') len -= 1; + line_number++; + (void) strncpy(map + map_cnt, yytext, len); + map_cnt += len; + map[map_cnt - 1] = '\n'; + map[map_cnt] = '\0'; + } + YY_BREAK +case 3: +YY_RULE_SETUP +{ line_number++; } + YY_BREAK +case 4: +YY_RULE_SETUP +{ colon_line_number = line_number; return ':'; } + YY_BREAK +case 5: +YY_RULE_SETUP +return MESSAGE_ID; + YY_BREAK +case 6: +YY_RULE_SETUP +return MAZE_ID; + YY_BREAK +case 7: +YY_RULE_SETUP +return NOMAP_ID; + YY_BREAK +case 8: +YY_RULE_SETUP +return LEVEL_ID; + YY_BREAK +case 9: +YY_RULE_SETUP +return LEV_INIT_ID; + YY_BREAK +case 10: +YY_RULE_SETUP +return FLAGS_ID; + YY_BREAK +case 11: +YY_RULE_SETUP +return GEOMETRY_ID; + YY_BREAK +case 12: +YY_RULE_SETUP +{ BEGIN(MAPC); line_number++; } + YY_BREAK +case 13: +YY_RULE_SETUP +return OBJECT_ID; + YY_BREAK +case 14: +YY_RULE_SETUP +return COBJECT_ID; + YY_BREAK +case 15: +YY_RULE_SETUP +return MONSTER_ID; + YY_BREAK +case 16: +YY_RULE_SETUP +return TRAP_ID; + YY_BREAK +case 17: +YY_RULE_SETUP +return DOOR_ID; + YY_BREAK +case 18: +YY_RULE_SETUP +return DRAWBRIDGE_ID; + YY_BREAK +case 19: +YY_RULE_SETUP +return MAZEWALK_ID; + YY_BREAK +case 20: +YY_RULE_SETUP +return WALLIFY_ID; + YY_BREAK +case 21: +YY_RULE_SETUP +return REGION_ID; + YY_BREAK +case 22: +YY_RULE_SETUP +return RANDOM_OBJECTS_ID; + YY_BREAK +case 23: +YY_RULE_SETUP +return RANDOM_MONSTERS_ID; + YY_BREAK +case 24: +YY_RULE_SETUP +return RANDOM_PLACES_ID; + YY_BREAK +case 25: +YY_RULE_SETUP +return ALTAR_ID; + YY_BREAK +case 26: +YY_RULE_SETUP +return LADDER_ID; + YY_BREAK +case 27: +YY_RULE_SETUP +return STAIR_ID; + YY_BREAK +case 28: +YY_RULE_SETUP +return PORTAL_ID; + YY_BREAK +case 29: +YY_RULE_SETUP +return TELEPRT_ID; + YY_BREAK +case 30: +YY_RULE_SETUP +return BRANCH_ID; + YY_BREAK +case 31: +YY_RULE_SETUP +return FOUNTAIN_ID; + YY_BREAK +case 32: +YY_RULE_SETUP +return SINK_ID; + YY_BREAK +case 33: +YY_RULE_SETUP +return POOL_ID; + YY_BREAK +case 34: +YY_RULE_SETUP +return NON_DIGGABLE_ID; + YY_BREAK +case 35: +YY_RULE_SETUP +return NON_PASSWALL_ID; + YY_BREAK +case 36: +YY_RULE_SETUP +return ROOM_ID; + YY_BREAK +case 37: +YY_RULE_SETUP +return SUBROOM_ID; + YY_BREAK +case 38: +YY_RULE_SETUP +return RAND_CORRIDOR_ID; + YY_BREAK +case 39: +YY_RULE_SETUP +return CORRIDOR_ID; + YY_BREAK +case 40: +YY_RULE_SETUP +return GOLD_ID; + YY_BREAK +case 41: +YY_RULE_SETUP +return ENGRAVING_ID; + YY_BREAK +case 42: +YY_RULE_SETUP +return NAME_ID; + YY_BREAK +case 43: +YY_RULE_SETUP +return CHANCE_ID; + YY_BREAK +case 44: +YY_RULE_SETUP +return LEV; + YY_BREAK +case 45: +YY_RULE_SETUP +{ yylval.i=D_ISOPEN; return DOOR_STATE; } + YY_BREAK +case 46: +YY_RULE_SETUP +{ yylval.i=D_CLOSED; return DOOR_STATE; } + YY_BREAK +case 47: +YY_RULE_SETUP +{ yylval.i=D_LOCKED; return DOOR_STATE; } + YY_BREAK +case 48: +YY_RULE_SETUP +{ yylval.i=D_NODOOR; return DOOR_STATE; } + YY_BREAK +case 49: +YY_RULE_SETUP +{ yylval.i=D_BROKEN; return DOOR_STATE; } + YY_BREAK +case 50: +YY_RULE_SETUP +{ yylval.i=W_NORTH; return DIRECTION; } + YY_BREAK +case 51: +YY_RULE_SETUP +{ yylval.i=W_EAST; return DIRECTION; } + YY_BREAK +case 52: +YY_RULE_SETUP +{ yylval.i=W_SOUTH; return DIRECTION; } + YY_BREAK +case 53: +YY_RULE_SETUP +{ yylval.i=W_WEST; return DIRECTION; } + YY_BREAK +case 54: +YY_RULE_SETUP +{ yylval.i = -1; return RANDOM_TYPE; } + YY_BREAK +case 55: +YY_RULE_SETUP +{ yylval.i = -2; return NONE; } + YY_BREAK +case 56: +YY_RULE_SETUP +return O_REGISTER; + YY_BREAK +case 57: +YY_RULE_SETUP +return M_REGISTER; + YY_BREAK +case 58: +YY_RULE_SETUP +return P_REGISTER; + YY_BREAK +case 59: +YY_RULE_SETUP +return A_REGISTER; + YY_BREAK +case 60: +YY_RULE_SETUP +{ yylval.i=1; return LEFT_OR_RIGHT; } + YY_BREAK +case 61: +YY_RULE_SETUP +{ yylval.i=2; return LEFT_OR_RIGHT; } + YY_BREAK +case 62: +YY_RULE_SETUP +{ yylval.i=3; return CENTER; } + YY_BREAK +case 63: +YY_RULE_SETUP +{ yylval.i=4; return LEFT_OR_RIGHT; } + YY_BREAK +case 64: +YY_RULE_SETUP +{ yylval.i=5; return LEFT_OR_RIGHT; } + YY_BREAK +case 65: +YY_RULE_SETUP +{ yylval.i=1; return TOP_OR_BOT; } + YY_BREAK +case 66: +YY_RULE_SETUP +{ yylval.i=5; return TOP_OR_BOT; } + YY_BREAK +case 67: +YY_RULE_SETUP +{ yylval.i=1; return LIGHT_STATE; } + YY_BREAK +case 68: +YY_RULE_SETUP +{ yylval.i=0; return LIGHT_STATE; } + YY_BREAK +case 69: +YY_RULE_SETUP +{ yylval.i=0; return FILLING; } + YY_BREAK +case 70: +YY_RULE_SETUP +{ yylval.i=1; return FILLING; } + YY_BREAK +case 71: +YY_RULE_SETUP +{ yylval.i= AM_NONE; return ALIGNMENT; } + YY_BREAK +case 72: +YY_RULE_SETUP +{ yylval.i= AM_LAWFUL; return ALIGNMENT; } + YY_BREAK +case 73: +YY_RULE_SETUP +{ yylval.i= AM_NEUTRAL; return ALIGNMENT; } + YY_BREAK +case 74: +YY_RULE_SETUP +{ yylval.i= AM_CHAOTIC; return ALIGNMENT; } + YY_BREAK +case 75: +YY_RULE_SETUP +{ yylval.i= AM_SPLEV_CO; return ALIGNMENT; } + YY_BREAK +case 76: +YY_RULE_SETUP +{ yylval.i= AM_SPLEV_NONCO; return ALIGNMENT; } + YY_BREAK +case 77: +YY_RULE_SETUP +{ yylval.i=1; return MON_ATTITUDE; } + YY_BREAK +case 78: +YY_RULE_SETUP +{ yylval.i=0; return MON_ATTITUDE; } + YY_BREAK +case 79: +YY_RULE_SETUP +{ yylval.i=1; return MON_ALERTNESS; } + YY_BREAK +case 80: +YY_RULE_SETUP +{ yylval.i=0; return MON_ALERTNESS; } + YY_BREAK +case 81: +YY_RULE_SETUP +{ yylval.i= M_AP_FURNITURE; return MON_APPEARANCE; } + YY_BREAK +case 82: +YY_RULE_SETUP +{ yylval.i= M_AP_MONSTER; return MON_APPEARANCE; } + YY_BREAK +case 83: +YY_RULE_SETUP +{ yylval.i= M_AP_OBJECT; return MON_APPEARANCE; } + YY_BREAK +case 84: +YY_RULE_SETUP +{ yylval.i=2; return ALTAR_TYPE; } + YY_BREAK +case 85: +YY_RULE_SETUP +{ yylval.i=1; return ALTAR_TYPE; } + YY_BREAK +case 86: +YY_RULE_SETUP +{ yylval.i=0; return ALTAR_TYPE; } + YY_BREAK +case 87: +YY_RULE_SETUP +{ yylval.i=1; return UP_OR_DOWN; } + YY_BREAK +case 88: +YY_RULE_SETUP +{ yylval.i=0; return UP_OR_DOWN; } + YY_BREAK +case 89: +YY_RULE_SETUP +{ yylval.i=0; return BOOLEAN; } + YY_BREAK +case 90: +YY_RULE_SETUP +{ yylval.i=1; return BOOLEAN; } + YY_BREAK +case 91: +YY_RULE_SETUP +{ yylval.i=DUST; return ENGRAVING_TYPE; } + YY_BREAK +case 92: +YY_RULE_SETUP +{ yylval.i=ENGRAVE; return ENGRAVING_TYPE; } + YY_BREAK +case 93: +YY_RULE_SETUP +{ yylval.i=BURN; return ENGRAVING_TYPE; } + YY_BREAK +case 94: +YY_RULE_SETUP +{ yylval.i=MARK; return ENGRAVING_TYPE; } + YY_BREAK +case 95: +YY_RULE_SETUP +{ yylval.i=1; return CURSE_TYPE; } + YY_BREAK +case 96: +YY_RULE_SETUP +{ yylval.i=2; return CURSE_TYPE; } + YY_BREAK +case 97: +YY_RULE_SETUP +{ yylval.i=3; return CURSE_TYPE; } + YY_BREAK +case 98: +YY_RULE_SETUP +{ return CONTAINED; } + YY_BREAK +case 99: +YY_RULE_SETUP +{ yylval.i=NOTELEPORT; return FLAG_TYPE; } + YY_BREAK +case 100: +YY_RULE_SETUP +{ yylval.i=HARDFLOOR; return FLAG_TYPE; } + YY_BREAK +case 101: +YY_RULE_SETUP +{ yylval.i=NOMMAP; return FLAG_TYPE; } + YY_BREAK +case 102: +YY_RULE_SETUP +{ yylval.i=ARBOREAL; return FLAG_TYPE; } /* KMH */ + YY_BREAK +case 103: +YY_RULE_SETUP +{ yylval.i=SHORTSIGHTED; return FLAG_TYPE; } + YY_BREAK +case 104: +YY_RULE_SETUP +{ yylval.i = atoi(yytext + 1); return PERCENT; } + YY_BREAK +case 105: +YY_RULE_SETUP +{ yylval.i=atoi(yytext); return INTEGER; } + YY_BREAK +case 106: +YY_RULE_SETUP +{ yytext[yyleng-1] = 0; /* Discard the trailing \" */ + yylval.map = (char *) alloc(strlen(yytext+1)+1); + Strcpy(yylval.map, yytext+1); /* Discard the first \" */ + return STRING; } + YY_BREAK +case 107: +YY_RULE_SETUP +{ line_number++; } + YY_BREAK +case 108: +YY_RULE_SETUP +; + YY_BREAK +case 109: +YY_RULE_SETUP +{ yylval.i = yytext[2]; return CHAR; } + YY_BREAK +case 110: +YY_RULE_SETUP +{ yylval.i = yytext[1]; return CHAR; } + YY_BREAK +case 111: +YY_RULE_SETUP +{ return yytext[0]; } + YY_BREAK +case 112: +YY_RULE_SETUP +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(MAPC): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a singled characater, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int old_size = b->yy_buf_size + 2; + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc2( (genericptr_t) b->yy_ch_buf, + b->yy_buf_size + 2, old_size ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + yy_current_state += YY_AT_BOL(); + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 640 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 640 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 639); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +static int input() + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext_ptr = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = + yytext_ptr + YY_MORE_ADJ; + return EOF; + } + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + return input(); + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: + YY_FATAL_ERROR( + "unexpected last match in input()" ); + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + yy_current_buffer->yy_at_bol = (c == '\n'); + + return c; + } + + +void yyrestart( input_file ) +FILE *input_file; + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +void yy_load_buffer_state() + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (genericptr_t) b->yy_ch_buf ); + + yy_flex_free( (genericptr_t) b ); + } + + +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int FDECL(isatty, (int)); +#endif +#endif + +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#ifdef YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#ifdef YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; + { + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error( msg ) +const char msg[]; + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n - YY_MORE_ADJ; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +const char *s2; +int n; + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + + +static genericptr_t yy_flex_alloc( size ) +yy_size_t size; + { + return (genericptr_t) alloc((unsigned)size); + } + +/* we want to avoid use of realloc(), so we require that caller supply the + size of the old block of memory */ +static genericptr_t yy_flex_realloc2( ptr, size, old_size ) +genericptr_t ptr; +yy_size_t size; +int old_size; + { + genericptr_t outptr = yy_flex_alloc(size); + + if (ptr) { + char *p = (char *) outptr, *q = (char *) ptr; + + while (--old_size >= 0) *p++ = *q++; + yy_flex_free(ptr); + } + return outptr; + } + +static void yy_flex_free( ptr ) +genericptr_t ptr; + { + free( ptr ); + } + +/*flexhack.skl*/ + +#ifdef AMIGA +long *alloc(n) + unsigned n; +{ + return ((long *)malloc (n)); +} +#endif + +/* routine to switch to another input file; needed for flex */ +void init_yyin( input_f ) +FILE *input_f; +{ +#if defined(FLEX_SCANNER) || defined(FLEXHACK_SCANNER) + if (yyin) + yyrestart(input_f); + else +#endif + yyin = input_f; +} +/* analogous routine (for completeness) */ +void init_yyout( output_f ) +FILE *output_f; +{ + yyout = output_f; +} + +/*lev_comp.l*/ diff --git a/sys/share/lev_yacc.c b/sys/share/lev_yacc.c new file mode 100644 index 0000000..44dcdc0 --- /dev/null +++ b/sys/share/lev_yacc.c @@ -0,0 +1,2418 @@ +#ifndef lint +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +/* SCCS Id: @(#)lev_yacc.c 3.4 2000/01/17 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the Level Compiler code + * It may handle special mazes & special room-levels + */ + +/* In case we're using bison in AIX. This definition must be + * placed before any other C-language construct in the file + * excluding comments and preprocessor directives (thanks IBM + * for this wonderful feature...). + * + * Note: some cpps barf on this 'undefined control' (#pragma). + * Addition of the leading space seems to prevent barfage for now, + * and AIX will still see the directive. + */ +#ifdef _AIX + #pragma alloca /* keep leading space! */ +#endif + +#include "hack.h" +#include "sp_lev.h" + +#define MAX_REGISTERS 10 +#define ERR (-1) +/* many types of things are put in chars for transference to NetHack. + * since some systems will use signed chars, limit everybody to the + * same number for portability. + */ +#define MAX_OF_TYPE 128 + +#define New(type) \ + (type *) memset((genericptr_t)alloc(sizeof(type)), 0, sizeof(type)) +#define NewTab(type, size) (type **) alloc(sizeof(type *) * size) +#define Free(ptr) free((genericptr_t)ptr) + +extern void FDECL(yyerror, (const char *)); +extern void FDECL(yywarning, (const char *)); +extern int NDECL(yylex); +int NDECL(yyparse); + +extern int FDECL(get_floor_type, (CHAR_P)); +extern int FDECL(get_room_type, (char *)); +extern int FDECL(get_trap_type, (char *)); +extern int FDECL(get_monster_id, (char *,CHAR_P)); +extern int FDECL(get_object_id, (char *,CHAR_P)); +extern boolean FDECL(check_monster_char, (CHAR_P)); +extern boolean FDECL(check_object_char, (CHAR_P)); +extern char FDECL(what_map_char, (CHAR_P)); +extern void FDECL(scan_map, (char *)); +extern void NDECL(wallify_map); +extern boolean NDECL(check_subrooms); +extern void FDECL(check_coord, (int,int,const char *)); +extern void NDECL(store_part); +extern void NDECL(store_room); +extern boolean FDECL(write_level_file, (char *,splev *,specialmaze *)); +extern void FDECL(free_rooms, (splev *)); + +static struct reg { + int x1, y1; + int x2, y2; +} current_region; + +static struct coord { + int x; + int y; +} current_coord, current_align; + +static struct size { + int height; + int width; +} current_size; + +char tmpmessage[256]; +digpos *tmppass[32]; +char *tmpmap[ROWNO]; + +digpos *tmpdig[MAX_OF_TYPE]; +region *tmpreg[MAX_OF_TYPE]; +lev_region *tmplreg[MAX_OF_TYPE]; +door *tmpdoor[MAX_OF_TYPE]; +drawbridge *tmpdb[MAX_OF_TYPE]; +walk *tmpwalk[MAX_OF_TYPE]; + +room_door *tmprdoor[MAX_OF_TYPE]; +trap *tmptrap[MAX_OF_TYPE]; +monster *tmpmonst[MAX_OF_TYPE]; +object *tmpobj[MAX_OF_TYPE]; +altar *tmpaltar[MAX_OF_TYPE]; +lad *tmplad[MAX_OF_TYPE]; +stair *tmpstair[MAX_OF_TYPE]; +gold *tmpgold[MAX_OF_TYPE]; +engraving *tmpengraving[MAX_OF_TYPE]; +fountain *tmpfountain[MAX_OF_TYPE]; +sink *tmpsink[MAX_OF_TYPE]; +pool *tmppool[MAX_OF_TYPE]; + +mazepart *tmppart[10]; +room *tmproom[MAXNROFROOMS*2]; +corridor *tmpcor[MAX_OF_TYPE]; + +static specialmaze maze; +static splev special_lev; +static lev_init init_lev; + +static char olist[MAX_REGISTERS], mlist[MAX_REGISTERS]; +static struct coord plist[MAX_REGISTERS]; + +int n_olist = 0, n_mlist = 0, n_plist = 0; + +unsigned int nlreg = 0, nreg = 0, ndoor = 0, ntrap = 0, nmons = 0, nobj = 0; +unsigned int ndb = 0, nwalk = 0, npart = 0, ndig = 0, nlad = 0, nstair = 0; +unsigned int naltar = 0, ncorridor = 0, nrooms = 0, ngold = 0, nengraving = 0; +unsigned int nfountain = 0, npool = 0, nsink = 0, npass = 0; + +static int lev_flags = 0; + +unsigned int max_x_map, max_y_map; + +static xchar in_room; + +extern int fatal_error; +extern int want_warnings; +extern const char *fname; + +typedef union +{ + int i; + char* map; + struct { + xchar room; + xchar wall; + xchar door; + } corpos; +} YYSTYPE; +#define CHAR 257 +#define INTEGER 258 +#define BOOLEAN 259 +#define PERCENT 260 +#define MESSAGE_ID 261 +#define MAZE_ID 262 +#define LEVEL_ID 263 +#define LEV_INIT_ID 264 +#define GEOMETRY_ID 265 +#define NOMAP_ID 266 +#define OBJECT_ID 267 +#define COBJECT_ID 268 +#define MONSTER_ID 269 +#define TRAP_ID 270 +#define DOOR_ID 271 +#define DRAWBRIDGE_ID 272 +#define MAZEWALK_ID 273 +#define WALLIFY_ID 274 +#define REGION_ID 275 +#define FILLING 276 +#define RANDOM_OBJECTS_ID 277 +#define RANDOM_MONSTERS_ID 278 +#define RANDOM_PLACES_ID 279 +#define ALTAR_ID 280 +#define LADDER_ID 281 +#define STAIR_ID 282 +#define NON_DIGGABLE_ID 283 +#define NON_PASSWALL_ID 284 +#define ROOM_ID 285 +#define PORTAL_ID 286 +#define TELEPRT_ID 287 +#define BRANCH_ID 288 +#define LEV 289 +#define CHANCE_ID 290 +#define CORRIDOR_ID 291 +#define GOLD_ID 292 +#define ENGRAVING_ID 293 +#define FOUNTAIN_ID 294 +#define POOL_ID 295 +#define SINK_ID 296 +#define NONE 297 +#define RAND_CORRIDOR_ID 298 +#define DOOR_STATE 299 +#define LIGHT_STATE 300 +#define CURSE_TYPE 301 +#define ENGRAVING_TYPE 302 +#define DIRECTION 303 +#define RANDOM_TYPE 304 +#define O_REGISTER 305 +#define M_REGISTER 306 +#define P_REGISTER 307 +#define A_REGISTER 308 +#define ALIGNMENT 309 +#define LEFT_OR_RIGHT 310 +#define CENTER 311 +#define TOP_OR_BOT 312 +#define ALTAR_TYPE 313 +#define UP_OR_DOWN 314 +#define SUBROOM_ID 315 +#define NAME_ID 316 +#define FLAGS_ID 317 +#define FLAG_TYPE 318 +#define MON_ATTITUDE 319 +#define MON_ALERTNESS 320 +#define MON_APPEARANCE 321 +#define CONTAINED 322 +#define STRING 323 +#define MAP_ID 324 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 0, 36, 36, 37, 37, 38, 39, 32, 23, + 23, 14, 14, 19, 19, 20, 20, 40, 40, 45, + 42, 42, 46, 46, 43, 43, 49, 49, 44, 44, + 51, 52, 52, 53, 53, 35, 50, 50, 56, 54, + 10, 10, 59, 59, 57, 57, 60, 60, 58, 58, + 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, + 61, 61, 61, 61, 61, 62, 63, 64, 15, 15, + 13, 13, 12, 12, 31, 11, 11, 41, 41, 75, + 76, 76, 79, 1, 1, 2, 2, 77, 77, 80, + 80, 80, 47, 47, 48, 48, 81, 83, 81, 78, + 78, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 99, 65, 98, 98, 100, 100, 100, 100, 100, + 66, 66, 102, 101, 103, 103, 104, 104, 104, 104, + 105, 105, 106, 107, 107, 108, 108, 108, 85, 67, + 86, 92, 93, 94, 74, 109, 88, 110, 89, 111, + 113, 90, 114, 91, 112, 112, 22, 22, 69, 70, + 71, 95, 96, 87, 68, 72, 73, 25, 25, 25, + 28, 28, 28, 33, 33, 34, 34, 3, 3, 4, + 4, 21, 21, 21, 97, 97, 97, 5, 5, 6, + 6, 7, 7, 7, 8, 8, 117, 29, 26, 9, + 82, 24, 27, 30, 16, 16, 17, 17, 18, 18, + 116, 115, +}; +short yylen[] = { 2, + 0, 1, 1, 2, 1, 1, 5, 7, 3, 0, + 13, 1, 1, 0, 3, 3, 1, 0, 2, 3, + 0, 2, 3, 3, 0, 1, 1, 2, 1, 1, + 1, 0, 2, 5, 5, 7, 2, 2, 12, 12, + 0, 2, 5, 1, 5, 1, 5, 1, 5, 1, + 0, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 3, 9, 1, 1, + 1, 1, 1, 1, 5, 1, 1, 1, 2, 3, + 1, 2, 5, 1, 1, 1, 1, 0, 2, 3, + 3, 3, 1, 3, 1, 3, 1, 0, 4, 0, + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 10, 0, 2, 2, 2, 2, 2, 3, + 2, 2, 0, 9, 1, 1, 0, 7, 5, 5, + 1, 1, 1, 1, 1, 0, 2, 2, 5, 6, + 7, 5, 1, 5, 5, 0, 8, 0, 8, 0, + 0, 8, 0, 6, 0, 2, 1, 10, 3, 3, + 3, 3, 3, 8, 7, 5, 7, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 2, 4, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 5, 9, +}; +short yydefred[] = { 0, + 0, 0, 0, 0, 0, 2, 0, 5, 6, 0, + 0, 0, 0, 0, 4, 214, 0, 9, 0, 0, + 0, 0, 0, 0, 15, 0, 0, 0, 0, 21, + 76, 77, 75, 0, 0, 0, 0, 81, 7, 0, + 88, 0, 19, 0, 16, 0, 20, 0, 79, 0, + 82, 0, 0, 0, 0, 0, 22, 26, 0, 51, + 51, 0, 84, 85, 0, 0, 0, 0, 0, 89, + 0, 0, 0, 0, 31, 8, 29, 0, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 153, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 102, 103, 105, 112, + 113, 118, 119, 117, 101, 104, 106, 107, 108, 109, + 110, 111, 114, 115, 116, 120, 121, 213, 0, 23, + 212, 0, 24, 191, 0, 190, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 0, + 87, 86, 83, 90, 92, 0, 91, 0, 211, 218, + 0, 131, 132, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 198, 199, 0, + 197, 0, 0, 195, 196, 0, 0, 0, 0, 0, + 0, 0, 156, 0, 167, 172, 173, 158, 160, 163, + 215, 216, 0, 0, 169, 94, 96, 200, 201, 0, + 0, 0, 0, 69, 70, 0, 67, 171, 170, 66, + 0, 0, 0, 182, 0, 181, 0, 183, 179, 0, + 178, 0, 180, 189, 0, 188, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 99, 0, 0, 0, 0, 0, 149, 0, 0, 152, + 0, 0, 204, 0, 202, 0, 203, 154, 0, 0, + 0, 155, 0, 0, 0, 176, 219, 220, 0, 44, + 0, 0, 46, 0, 0, 0, 35, 34, 0, 0, + 221, 0, 187, 186, 133, 0, 185, 184, 0, 150, + 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 161, 164, 0, 0, 0, 0, 0, 0, 0, 0, + 208, 0, 209, 0, 151, 0, 0, 0, 206, 205, + 175, 0, 0, 0, 0, 177, 0, 48, 0, 0, + 0, 50, 0, 0, 0, 71, 72, 0, 12, 13, + 11, 0, 122, 0, 0, 174, 210, 0, 157, 159, + 0, 162, 0, 0, 0, 0, 0, 0, 73, 74, + 0, 0, 136, 135, 0, 124, 0, 0, 0, 166, + 43, 0, 0, 45, 0, 0, 36, 68, 0, 134, + 0, 0, 0, 0, 0, 0, 40, 0, 39, 142, + 141, 143, 0, 0, 0, 125, 222, 194, 0, 47, + 42, 49, 0, 0, 127, 128, 0, 129, 126, 168, + 145, 144, 0, 0, 0, 130, 0, 0, 139, 140, + 0, 147, 148, 138, +}; +short yydgoto[] = { 3, + 65, 163, 265, 135, 210, 240, 306, 371, 307, 437, + 33, 411, 388, 391, 246, 233, 171, 319, 13, 25, + 396, 223, 21, 132, 262, 263, 129, 257, 258, 136, + 4, 5, 339, 335, 243, 6, 7, 8, 9, 28, + 39, 44, 56, 76, 29, 57, 130, 133, 58, 59, + 77, 78, 139, 60, 80, 61, 325, 384, 322, 380, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, + 156, 157, 158, 159, 40, 41, 50, 69, 42, 70, + 167, 168, 204, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 224, 431, 416, 446, + 172, 362, 415, 430, 443, 444, 464, 469, 277, 279, + 280, 402, 375, 281, 225, 214, 215, +}; +short yysindex[] = { -166, + -18, 4, 0, -233, -233, 0, -166, 0, 0, -222, + -222, 32, -134, -134, 0, 0, 88, 0, -173, 76, + -114, -114, -230, 105, 0, -99, 115, -124, -114, 0, + 0, 0, 0, -173, 127, -143, 128, 0, 0, -124, + 0, -132, 0, -236, 0, -67, 0, -155, 0, -156, + 0, 137, 138, 140, 142, -94, 0, 0, -263, 0, + 0, 161, 0, 0, 162, 149, 150, 151, -105, 0, + -47, -46, -276, -276, 0, 0, 0, -79, 0, -142, + -142, -45, -151, -47, -46, 173, -44, -44, -44, -44, + 160, 163, 165, 0, 166, 167, 168, 170, 171, 172, + 174, 175, 176, 177, 178, 179, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 187, 0, + 0, 194, 0, 0, 195, 0, 197, 184, 0, 185, + 186, 188, 189, 190, 191, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 206, + 0, 0, 0, 0, 0, -43, 0, 0, 0, 0, + 193, 0, 0, 196, 198, -239, 45, 45, 180, 45, + 45, 58, 180, 180, -37, -37, -37, -232, 45, 45, + -47, -46, -218, -218, 205, -238, 45, -41, 45, 45, + -222, -6, 211, 213, -234, -237, -268, 0, 0, 214, + 0, 169, 215, 0, 0, 217, -39, 218, 219, 220, + 225, 12, 0, 296, 0, 0, 0, 0, 0, 0, + 0, 0, 300, 306, 0, 0, 0, 0, 0, 317, + 319, 112, 329, 0, 0, 341, 0, 0, 0, 0, + 342, 129, 173, 0, 315, 0, 366, 0, 0, 320, + 0, 368, 0, 0, 374, 0, 45, 200, 120, 124, + 385, -218, -201, 116, 202, 389, 390, 118, 399, 401, + 405, 45, -254, -38, -9, 407, -36, -239, -218, 411, + 0, 207, -267, 238, -260, 45, 0, 360, 410, 0, + 239, 412, 0, 386, 0, 415, 0, 0, 454, 242, + -37, 0, -37, -37, -37, 0, 0, 0, 457, 0, + 246, 492, 0, 279, 495, 237, 0, 0, 497, 498, + 0, 456, 0, 0, 0, 458, 0, 0, 506, 0, + 0, -239, 509, -276, 298, -259, 299, 72, 510, 517, + 0, 0, -222, 518, -1, 519, 28, 520, -119, -227, + 0, 522, 0, 45, 0, 316, 531, 483, 0, 0, + 0, 533, 264, -222, 537, 0, 321, 0, -155, 539, + 328, 0, 330, 543, -229, 0, 0, 545, 0, 0, + 0, 38, 0, 546, 318, 0, 0, 333, 0, 0, + 281, 0, 552, 555, 28, 559, 557, -222, 0, 0, + 561, -229, 0, 0, 560, 0, 338, 563, 566, 0, + 0, -151, 571, 0, 345, 571, 0, 0, -243, 0, + 575, 579, 362, 367, 585, 371, 0, 586, 0, 0, + 0, 0, 590, 591, -209, 0, 0, 0, 597, 0, + 0, 0, -240, -228, 0, 0, -222, 0, 0, 0, + 0, 0, 595, 599, 599, 0, -228, -264, 0, 0, + 599, 0, 0, 0, +}; +short yyrindex[] = { 641, + 0, 0, 0, -172, 307, 0, 645, 0, 0, 0, + 0, 0, -146, 355, 0, 0, 0, 0, 0, 0, + -72, 351, 0, 282, 0, 0, 0, 0, 346, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, + 0, 0, 0, 157, 0, 0, 0, 0, 0, 491, + 0, 0, 0, 0, 0, 57, 0, 0, 159, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, + 0, 0, 0, 0, 0, 0, 0, 106, 0, 267, + 388, 0, 0, 0, 0, 0, 589, 589, 589, 589, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 201, 0, + 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 446, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 535, 0, 0, 0, + 0, 0, 0, 0, 572, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 0, 606, 0, 0, + 0, 0, 146, 0, 0, 146, 0, 0, 0, 0, + 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 109, 109, 0, 0, 0, 0, 0, + 109, 0, 0, 0, +}; +short yygindex[] = { 0, + 269, 230, 0, -60, -269, -184, 209, 0, 0, 229, + 0, 244, 0, 0, 0, 0, 113, 0, 652, 624, + 0, -178, 646, 453, 0, 0, 459, 0, 0, -10, + 0, 0, 0, 0, 375, 656, 0, 0, 0, 24, + 625, 0, 0, 0, 0, 0, -73, -68, 608, 0, + 0, 0, 0, 0, 607, 0, 0, 266, 0, 0, + 0, 0, 0, 0, 600, 603, 605, 609, 611, 0, + 0, 612, 613, 614, 0, 0, 0, 0, 0, 0, + 422, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -165, 0, 0, 0, + 588, 0, 0, 0, 0, 224, -416, -384, 0, 0, + 0, 0, 0, 0, -40, -81, 0, +}; +#define YYTABLESIZE 900 +short yytable[] = { 17, + 18, 321, 217, 242, 169, 137, 228, 229, 230, 241, + 164, 213, 216, 137, 219, 220, 165, 461, 329, 131, + 244, 54, 128, 234, 235, 231, 31, 134, 409, 461, + 324, 389, 472, 248, 249, 264, 333, 465, 379, 10, + 52, 53, 123, 337, 369, 30, 16, 317, 54, 318, + 471, 55, 43, 370, 16, 16, 32, 440, 473, 208, + 441, 11, 16, 462, 209, 245, 259, 383, 260, 254, + 255, 232, 365, 32, 410, 462, 390, 166, 55, 442, + 470, 238, 442, 12, 166, 239, 474, 302, 14, 19, + 80, 14, 14, 14, 303, 1, 2, 222, 304, 305, + 16, 297, 303, 78, 330, 30, 304, 305, 146, 455, + 456, 457, 331, 16, 10, 366, 316, 236, 10, 10, + 66, 67, 68, 237, 87, 88, 89, 90, 140, 20, + 340, 23, 349, 26, 350, 351, 352, 96, 218, 141, + 37, 38, 226, 227, 24, 41, 27, 142, 34, 104, + 105, 106, 143, 144, 63, 64, 25, 35, 27, 161, + 162, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 46, 169, 36, 145, 96, 97, 98, 99, 100, 47, + 101, 102, 103, 386, 387, 48, 104, 105, 106, 62, + 250, 51, 18, 18, 71, 72, 266, 73, 393, 74, + 93, 174, 175, 75, 82, 83, 84, 85, 86, 128, + 131, 138, 166, 160, 203, 170, 247, 176, 271, 217, + 177, 327, 178, 179, 180, 181, 414, 182, 183, 184, + 191, 185, 186, 187, 188, 189, 190, 192, 193, 95, + 194, 195, 196, 197, 242, 198, 199, 200, 201, 202, + 205, 221, 251, 206, 252, 207, 253, 267, 269, 268, + 270, 272, 273, 274, 275, 320, 37, 137, 137, 276, + 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, + 137, 17, 334, 367, 338, 137, 137, 137, 137, 137, + 137, 137, 137, 137, 323, 137, 137, 137, 137, 137, + 137, 137, 378, 137, 123, 123, 14, 123, 123, 123, + 123, 123, 123, 123, 123, 123, 123, 123, 32, 32, + 137, 137, 123, 123, 123, 123, 123, 123, 123, 123, + 123, 382, 123, 123, 123, 123, 123, 123, 123, 278, + 123, 211, 376, 282, 212, 18, 221, 32, 211, 283, + 18, 212, 80, 80, 10, 80, 80, 123, 123, 413, + 284, 211, 285, 400, 212, 78, 78, 30, 30, 286, + 146, 146, 287, 146, 146, 146, 146, 146, 146, 146, + 146, 146, 146, 146, 288, 289, 290, 38, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 426, 146, 146, + 146, 146, 146, 146, 146, 292, 146, 41, 41, 293, + 294, 295, 41, 41, 41, 41, 41, 296, 25, 25, + 27, 27, 299, 146, 146, 41, 300, 41, 301, 308, + 41, 312, 310, 311, 459, 41, 41, 41, 41, 41, + 41, 41, 313, 41, 314, 97, 466, 25, 315, 27, + 326, 331, 341, 342, 25, 344, 27, 298, 346, 309, + 41, 41, 93, 93, 332, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 345, 93, 93, 93, + 93, 93, 93, 93, 93, 93, 93, 93, 93, 98, + 100, 93, 93, 93, 93, 336, 343, 347, 93, 348, + 353, 95, 95, 354, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 93, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 37, 37, + 95, 95, 95, 95, 192, 355, 356, 95, 357, 358, + 359, 360, 17, 17, 17, 17, 17, 17, 361, 364, + 363, 37, 366, 373, 95, 368, 372, 37, 17, 17, + 374, 377, 381, 385, 37, 392, 17, 14, 14, 14, + 14, 165, 17, 394, 395, 397, 398, 399, 403, 17, + 401, 37, 405, 14, 14, 406, 408, 407, 412, 417, + 419, 14, 421, 418, 420, 432, 17, 14, 422, 424, + 425, 427, 438, 429, 14, 193, 433, 18, 18, 434, + 18, 18, 18, 18, 436, 10, 10, 10, 445, 447, + 448, 14, 18, 18, 449, 450, 452, 18, 18, 451, + 18, 10, 10, 453, 454, 18, 18, 460, 467, 10, + 1, 18, 468, 18, 3, 10, 217, 404, 18, 38, + 38, 435, 10, 458, 439, 428, 14, 45, 261, 22, + 18, 328, 15, 256, 49, 18, 79, 81, 107, 10, + 423, 108, 38, 109, 291, 173, 463, 110, 38, 111, + 112, 113, 114, 0, 0, 38, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 0, 0, 97, 97, 0, + 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, + 97, 0, 97, 97, 97, 97, 97, 97, 97, 97, + 0, 97, 97, 97, 0, 0, 0, 97, 97, 97, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 100, 100, 0, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 0, 0, 0, 0, + 100, 100, 100, 100, 100, 0, 100, 100, 100, 0, + 0, 0, 100, 100, 100, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 192, 192, 0, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 0, 0, 0, 0, 192, 192, 192, 192, 192, 0, + 192, 192, 192, 0, 0, 0, 192, 192, 192, 0, + 0, 0, 0, 165, 165, 0, 165, 165, 165, 165, + 165, 165, 165, 165, 165, 165, 165, 0, 0, 0, + 0, 165, 165, 165, 165, 165, 0, 165, 165, 165, + 0, 0, 0, 165, 165, 165, 0, 193, 193, 0, + 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, + 193, 0, 0, 0, 0, 193, 193, 193, 193, 193, + 0, 193, 193, 193, 0, 0, 0, 193, 193, 193, +}; +short yycheck[] = { 10, + 11, 40, 40, 40, 86, 0, 185, 186, 187, 194, + 84, 177, 178, 74, 180, 181, 85, 258, 288, 257, + 259, 285, 257, 189, 190, 258, 257, 304, 258, 258, + 40, 259, 297, 199, 200, 304, 304, 454, 40, 58, + 277, 278, 0, 304, 304, 22, 323, 302, 285, 304, + 467, 315, 29, 313, 323, 323, 0, 301, 323, 299, + 304, 58, 323, 304, 304, 304, 304, 40, 306, 304, + 305, 304, 342, 304, 304, 304, 304, 40, 315, 323, + 465, 300, 323, 317, 40, 304, 471, 272, 261, 58, + 0, 264, 265, 266, 304, 262, 263, 40, 308, 309, + 323, 267, 304, 0, 289, 0, 308, 309, 0, 319, + 320, 321, 41, 323, 261, 44, 282, 191, 265, 266, + 277, 278, 279, 192, 267, 268, 269, 270, 271, 264, + 296, 44, 311, 58, 313, 314, 315, 280, 179, 282, + 265, 266, 183, 184, 318, 0, 261, 290, 44, 292, + 293, 294, 295, 296, 310, 311, 0, 257, 0, 311, + 312, 267, 268, 269, 270, 271, 272, 273, 274, 275, + 44, 253, 58, 316, 280, 281, 282, 283, 284, 323, + 286, 287, 288, 303, 304, 58, 292, 293, 294, 257, + 201, 324, 265, 266, 58, 58, 207, 58, 364, 58, + 0, 89, 90, 298, 44, 44, 58, 58, 58, 257, + 257, 291, 40, 259, 258, 260, 258, 58, 258, 40, + 58, 258, 58, 58, 58, 58, 392, 58, 58, 58, + 44, 58, 58, 58, 58, 58, 58, 44, 44, 0, + 44, 58, 58, 58, 40, 58, 58, 58, 58, 44, + 58, 289, 259, 58, 44, 58, 44, 44, 44, 91, + 44, 44, 44, 44, 40, 304, 0, 262, 263, 258, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 0, 293, 344, 295, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 304, 290, 291, 292, 293, 294, + 295, 296, 304, 298, 262, 263, 0, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 262, 263, + 315, 316, 280, 281, 282, 283, 284, 285, 286, 287, + 288, 304, 290, 291, 292, 293, 294, 295, 296, 44, + 298, 304, 353, 44, 307, 0, 289, 291, 304, 44, + 0, 307, 262, 263, 0, 265, 266, 315, 316, 322, + 44, 304, 44, 374, 307, 262, 263, 262, 263, 258, + 262, 263, 44, 265, 266, 267, 268, 269, 270, 271, + 272, 273, 274, 275, 44, 44, 258, 0, 280, 281, + 282, 283, 284, 285, 286, 287, 288, 408, 290, 291, + 292, 293, 294, 295, 296, 91, 298, 262, 263, 44, + 91, 44, 267, 268, 269, 270, 271, 44, 262, 263, + 262, 263, 303, 315, 316, 280, 303, 282, 44, 314, + 285, 314, 44, 44, 445, 290, 291, 292, 293, 294, + 295, 296, 44, 298, 44, 0, 457, 291, 44, 291, + 44, 41, 93, 44, 298, 44, 298, 258, 44, 258, + 315, 316, 262, 263, 258, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 91, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 44, + 0, 291, 292, 293, 294, 258, 258, 44, 298, 258, + 44, 262, 263, 258, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 275, 315, 277, 278, 279, 280, + 281, 282, 283, 284, 285, 286, 287, 288, 262, 263, + 291, 292, 293, 294, 0, 44, 258, 298, 44, 303, + 44, 44, 261, 262, 263, 264, 265, 266, 93, 44, + 93, 285, 44, 44, 315, 258, 258, 291, 277, 278, + 44, 44, 44, 44, 298, 44, 285, 261, 262, 263, + 264, 0, 291, 258, 44, 93, 44, 314, 258, 298, + 44, 315, 44, 277, 278, 258, 44, 258, 44, 44, + 258, 285, 41, 276, 314, 258, 315, 291, 44, 41, + 44, 41, 258, 44, 298, 0, 44, 262, 263, 44, + 265, 266, 262, 263, 44, 261, 262, 263, 44, 41, + 259, 315, 277, 278, 258, 41, 41, 277, 278, 259, + 285, 277, 278, 44, 44, 285, 291, 41, 44, 285, + 0, 291, 44, 298, 0, 291, 58, 379, 298, 262, + 263, 422, 298, 445, 426, 412, 5, 34, 206, 14, + 315, 287, 7, 205, 40, 315, 59, 61, 69, 315, + 405, 69, 285, 69, 253, 88, 453, 69, 291, 69, + 69, 69, 69, -1, -1, 298, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 315, -1, -1, -1, -1, 262, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, -1, 277, 278, 279, 280, 281, 282, 283, 284, + -1, 286, 287, 288, -1, -1, -1, 292, 293, 294, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 262, 263, -1, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, -1, -1, -1, -1, + 280, 281, 282, 283, 284, -1, 286, 287, 288, -1, + -1, -1, 292, 293, 294, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 262, 263, -1, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, + -1, -1, -1, -1, 280, 281, 282, 283, 284, -1, + 286, 287, 288, -1, -1, -1, 292, 293, 294, -1, + -1, -1, -1, 262, 263, -1, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, 275, -1, -1, -1, + -1, 280, 281, 282, 283, 284, -1, 286, 287, 288, + -1, -1, -1, 292, 293, 294, -1, 262, 263, -1, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, -1, -1, -1, -1, 280, 281, 282, 283, 284, + -1, 286, 287, 288, -1, -1, -1, 292, 293, 294, +}; +#define YYFINAL 3 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 324 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,"'('","')'",0,0,"','",0,0,0,0,0,0,0,0,0,0,0,0,0,"':'",0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'['",0,"']'",0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"CHAR", +"INTEGER","BOOLEAN","PERCENT","MESSAGE_ID","MAZE_ID","LEVEL_ID","LEV_INIT_ID", +"GEOMETRY_ID","NOMAP_ID","OBJECT_ID","COBJECT_ID","MONSTER_ID","TRAP_ID", +"DOOR_ID","DRAWBRIDGE_ID","MAZEWALK_ID","WALLIFY_ID","REGION_ID","FILLING", +"RANDOM_OBJECTS_ID","RANDOM_MONSTERS_ID","RANDOM_PLACES_ID","ALTAR_ID", +"LADDER_ID","STAIR_ID","NON_DIGGABLE_ID","NON_PASSWALL_ID","ROOM_ID", +"PORTAL_ID","TELEPRT_ID","BRANCH_ID","LEV","CHANCE_ID","CORRIDOR_ID","GOLD_ID", +"ENGRAVING_ID","FOUNTAIN_ID","POOL_ID","SINK_ID","NONE","RAND_CORRIDOR_ID", +"DOOR_STATE","LIGHT_STATE","CURSE_TYPE","ENGRAVING_TYPE","DIRECTION", +"RANDOM_TYPE","O_REGISTER","M_REGISTER","P_REGISTER","A_REGISTER","ALIGNMENT", +"LEFT_OR_RIGHT","CENTER","TOP_OR_BOT","ALTAR_TYPE","UP_OR_DOWN","SUBROOM_ID", +"NAME_ID","FLAGS_ID","FLAG_TYPE","MON_ATTITUDE","MON_ALERTNESS", +"MON_APPEARANCE","CONTAINED","STRING","MAP_ID", +}; +char *yyrule[] = { +"$accept : file", +"file :", +"file : levels", +"levels : level", +"levels : level levels", +"level : maze_level", +"level : room_level", +"maze_level : maze_def flags lev_init messages regions", +"room_level : level_def flags lev_init messages rreg_init rooms corridors_def", +"level_def : LEVEL_ID ':' string", +"lev_init :", +"lev_init : LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled", +"walled : BOOLEAN", +"walled : RANDOM_TYPE", +"flags :", +"flags : FLAGS_ID ':' flag_list", +"flag_list : FLAG_TYPE ',' flag_list", +"flag_list : FLAG_TYPE", +"messages :", +"messages : message messages", +"message : MESSAGE_ID ':' STRING", +"rreg_init :", +"rreg_init : rreg_init init_rreg", +"init_rreg : RANDOM_OBJECTS_ID ':' object_list", +"init_rreg : RANDOM_MONSTERS_ID ':' monster_list", +"rooms :", +"rooms : roomlist", +"roomlist : aroom", +"roomlist : aroom roomlist", +"corridors_def : random_corridors", +"corridors_def : corridors", +"random_corridors : RAND_CORRIDOR_ID", +"corridors :", +"corridors : corridors corridor", +"corridor : CORRIDOR_ID ':' corr_spec ',' corr_spec", +"corridor : CORRIDOR_ID ':' corr_spec ',' INTEGER", +"corr_spec : '(' INTEGER ',' DIRECTION ',' door_pos ')'", +"aroom : room_def room_details", +"aroom : subroom_def room_details", +"subroom_def : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill", +"room_def : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill", +"roomfill :", +"roomfill : ',' BOOLEAN", +"room_pos : '(' INTEGER ',' INTEGER ')'", +"room_pos : RANDOM_TYPE", +"subroom_pos : '(' INTEGER ',' INTEGER ')'", +"subroom_pos : RANDOM_TYPE", +"room_align : '(' h_justif ',' v_justif ')'", +"room_align : RANDOM_TYPE", +"room_size : '(' INTEGER ',' INTEGER ')'", +"room_size : RANDOM_TYPE", +"room_details :", +"room_details : room_details room_detail", +"room_detail : room_name", +"room_detail : room_chance", +"room_detail : room_door", +"room_detail : monster_detail", +"room_detail : object_detail", +"room_detail : trap_detail", +"room_detail : altar_detail", +"room_detail : fountain_detail", +"room_detail : sink_detail", +"room_detail : pool_detail", +"room_detail : gold_detail", +"room_detail : engraving_detail", +"room_detail : stair_detail", +"room_name : NAME_ID ':' string", +"room_chance : CHANCE_ID ':' INTEGER", +"room_door : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos", +"secret : BOOLEAN", +"secret : RANDOM_TYPE", +"door_wall : DIRECTION", +"door_wall : RANDOM_TYPE", +"door_pos : INTEGER", +"door_pos : RANDOM_TYPE", +"maze_def : MAZE_ID ':' string ',' filling", +"filling : CHAR", +"filling : RANDOM_TYPE", +"regions : aregion", +"regions : aregion regions", +"aregion : map_definition reg_init map_details", +"map_definition : NOMAP_ID", +"map_definition : map_geometry MAP_ID", +"map_geometry : GEOMETRY_ID ':' h_justif ',' v_justif", +"h_justif : LEFT_OR_RIGHT", +"h_justif : CENTER", +"v_justif : TOP_OR_BOT", +"v_justif : CENTER", +"reg_init :", +"reg_init : reg_init init_reg", +"init_reg : RANDOM_OBJECTS_ID ':' object_list", +"init_reg : RANDOM_PLACES_ID ':' place_list", +"init_reg : RANDOM_MONSTERS_ID ':' monster_list", +"object_list : object", +"object_list : object ',' object_list", +"monster_list : monster", +"monster_list : monster ',' monster_list", +"place_list : place", +"$$1 :", +"place_list : place $$1 ',' place_list", +"map_details :", +"map_details : map_details map_detail", +"map_detail : monster_detail", +"map_detail : object_detail", +"map_detail : door_detail", +"map_detail : trap_detail", +"map_detail : drawbridge_detail", +"map_detail : region_detail", +"map_detail : stair_region", +"map_detail : portal_region", +"map_detail : teleprt_region", +"map_detail : branch_region", +"map_detail : altar_detail", +"map_detail : fountain_detail", +"map_detail : mazewalk_detail", +"map_detail : wallify_detail", +"map_detail : ladder_detail", +"map_detail : stair_detail", +"map_detail : gold_detail", +"map_detail : engraving_detail", +"map_detail : diggable_detail", +"map_detail : passwall_detail", +"$$2 :", +"monster_detail : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate $$2 monster_infos", +"monster_infos :", +"monster_infos : monster_infos monster_info", +"monster_info : ',' string", +"monster_info : ',' MON_ATTITUDE", +"monster_info : ',' MON_ALERTNESS", +"monster_info : ',' alignment", +"monster_info : ',' MON_APPEARANCE string", +"object_detail : OBJECT_ID object_desc", +"object_detail : COBJECT_ID object_desc", +"$$3 :", +"object_desc : chance ':' object_c ',' o_name $$3 ',' object_where object_infos", +"object_where : coordinate", +"object_where : CONTAINED", +"object_infos :", +"object_infos : ',' curse_state ',' monster_id ',' enchantment optional_name", +"object_infos : ',' curse_state ',' enchantment optional_name", +"object_infos : ',' monster_id ',' enchantment optional_name", +"curse_state : RANDOM_TYPE", +"curse_state : CURSE_TYPE", +"monster_id : STRING", +"enchantment : RANDOM_TYPE", +"enchantment : INTEGER", +"optional_name :", +"optional_name : ',' NONE", +"optional_name : ',' STRING", +"door_detail : DOOR_ID ':' door_state ',' coordinate", +"trap_detail : TRAP_ID chance ':' trap_name ',' coordinate", +"drawbridge_detail : DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state", +"mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION", +"wallify_detail : WALLIFY_ID", +"ladder_detail : LADDER_ID ':' coordinate ',' UP_OR_DOWN", +"stair_detail : STAIR_ID ':' coordinate ',' UP_OR_DOWN", +"$$4 :", +"stair_region : STAIR_ID ':' lev_region $$4 ',' lev_region ',' UP_OR_DOWN", +"$$5 :", +"portal_region : PORTAL_ID ':' lev_region $$5 ',' lev_region ',' string", +"$$6 :", +"$$7 :", +"teleprt_region : TELEPRT_ID ':' lev_region $$6 ',' lev_region $$7 teleprt_detail", +"$$8 :", +"branch_region : BRANCH_ID ':' lev_region $$8 ',' lev_region", +"teleprt_detail :", +"teleprt_detail : ',' UP_OR_DOWN", +"lev_region : region", +"lev_region : LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'", +"fountain_detail : FOUNTAIN_ID ':' coordinate", +"sink_detail : SINK_ID ':' coordinate", +"pool_detail : POOL_ID ':' coordinate", +"diggable_detail : NON_DIGGABLE_ID ':' region", +"passwall_detail : NON_PASSWALL_ID ':' region", +"region_detail : REGION_ID ':' region ',' light_state ',' room_type prefilled", +"altar_detail : ALTAR_ID ':' coordinate ',' alignment ',' altar_type", +"gold_detail : GOLD_ID ':' amount ',' coordinate", +"engraving_detail : ENGRAVING_ID ':' coordinate ',' engraving_type ',' string", +"monster_c : monster", +"monster_c : RANDOM_TYPE", +"monster_c : m_register", +"object_c : object", +"object_c : RANDOM_TYPE", +"object_c : o_register", +"m_name : string", +"m_name : RANDOM_TYPE", +"o_name : string", +"o_name : RANDOM_TYPE", +"trap_name : string", +"trap_name : RANDOM_TYPE", +"room_type : string", +"room_type : RANDOM_TYPE", +"prefilled :", +"prefilled : ',' FILLING", +"prefilled : ',' FILLING ',' BOOLEAN", +"coordinate : coord", +"coordinate : p_register", +"coordinate : RANDOM_TYPE", +"door_state : DOOR_STATE", +"door_state : RANDOM_TYPE", +"light_state : LIGHT_STATE", +"light_state : RANDOM_TYPE", +"alignment : ALIGNMENT", +"alignment : a_register", +"alignment : RANDOM_TYPE", +"altar_type : ALTAR_TYPE", +"altar_type : RANDOM_TYPE", +"p_register : P_REGISTER '[' INTEGER ']'", +"o_register : O_REGISTER '[' INTEGER ']'", +"m_register : M_REGISTER '[' INTEGER ']'", +"a_register : A_REGISTER '[' INTEGER ']'", +"place : coord", +"monster : CHAR", +"object : CHAR", +"string : STRING", +"amount : INTEGER", +"amount : RANDOM_TYPE", +"chance :", +"chance : PERCENT", +"engraving_type : ENGRAVING_TYPE", +"engraving_type : RANDOM_TYPE", +"coord : '(' INTEGER ',' INTEGER ')'", +"region : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE + +/*lev_comp.y*/ +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if ((yys = getenv("YYDEBUG")) != 0) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) != 0 && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) != 0 && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) != 0 && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 7: +{ + unsigned i; + + if (fatal_error > 0) { + (void) fprintf(stderr, + "%s : %d errors detected. No output created!\n", + fname, fatal_error); + } else { + maze.flags = yyvsp[-3].i; + (void) memcpy((genericptr_t)&(maze.init_lev), + (genericptr_t)&(init_lev), + sizeof(lev_init)); + maze.numpart = npart; + maze.parts = NewTab(mazepart, npart); + for(i=0;i 0) { + (void) fprintf(stderr, + "%s : %d errors detected. No output created!\n", + fname, fatal_error); + } else { + special_lev.flags = (long) yyvsp[-5].i; + (void) memcpy( + (genericptr_t)&(special_lev.init_lev), + (genericptr_t)&(init_lev), + sizeof(lev_init)); + special_lev.nroom = nrooms; + special_lev.rooms = NewTab(room, nrooms); + for(i=0; i 8) + yyerror("Level names limited to 8 characters."); + yyval.map = yyvsp[0].map; + special_lev.nrmonst = special_lev.nrobjects = 0; + n_mlist = n_olist = 0; + } +break; +case 10: +{ + /* in case we're processing multiple files, + explicitly clear any stale settings */ + (void) memset((genericptr_t) &init_lev, 0, + sizeof init_lev); + init_lev.init_present = FALSE; + yyval.i = 0; + } +break; +case 11: +{ + init_lev.init_present = TRUE; + init_lev.fg = what_map_char((char) yyvsp[-10].i); + if (init_lev.fg == INVALID_TYPE) + yyerror("Invalid foreground type."); + init_lev.bg = what_map_char((char) yyvsp[-8].i); + if (init_lev.bg == INVALID_TYPE) + yyerror("Invalid background type."); + init_lev.smoothed = yyvsp[-6].i; + init_lev.joined = yyvsp[-4].i; + if (init_lev.joined && + init_lev.fg != CORR && init_lev.fg != ROOM) + yyerror("Invalid foreground type for joined map."); + init_lev.lit = yyvsp[-2].i; + init_lev.walled = yyvsp[0].i; + yyval.i = 1; + } +break; +case 14: +{ + yyval.i = 0; + } +break; +case 15: +{ + yyval.i = lev_flags; + lev_flags = 0; /* clear for next user */ + } +break; +case 16: +{ + lev_flags |= yyvsp[-2].i; + } +break; +case 17: +{ + lev_flags |= yyvsp[0].i; + } +break; +case 20: +{ + int i, j; + + i = (int) strlen(yyvsp[0].map) + 1; + j = (int) strlen(tmpmessage); + if (i + j > 255) { + yyerror("Message string too long (>256 characters)"); + } else { + if (j) tmpmessage[j++] = '\n'; + (void) strncpy(tmpmessage+j, yyvsp[0].map, i - 1); + tmpmessage[j + i - 1] = 0; + } + Free(yyvsp[0].map); + } +break; +case 23: +{ + if(special_lev.nrobjects) { + yyerror("Object registers already initialized!"); + } else { + special_lev.nrobjects = n_olist; + special_lev.robjects = (char *) alloc(n_olist); + (void) memcpy((genericptr_t)special_lev.robjects, + (genericptr_t)olist, n_olist); + } + } +break; +case 24: +{ + if(special_lev.nrmonst) { + yyerror("Monster registers already initialized!"); + } else { + special_lev.nrmonst = n_mlist; + special_lev.rmonst = (char *) alloc(n_mlist); + (void) memcpy((genericptr_t)special_lev.rmonst, + (genericptr_t)mlist, n_mlist); + } + } +break; +case 25: +{ + tmproom[nrooms] = New(room); + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->parent = (char *) 0; + tmproom[nrooms]->rtype = 0; + tmproom[nrooms]->rlit = 0; + tmproom[nrooms]->xalign = ERR; + tmproom[nrooms]->yalign = ERR; + tmproom[nrooms]->x = 0; + tmproom[nrooms]->y = 0; + tmproom[nrooms]->w = 2; + tmproom[nrooms]->h = 2; + in_room = 1; + } +break; +case 31: +{ + tmpcor[0] = New(corridor); + tmpcor[0]->src.room = -1; + ncorridor = 1; + } +break; +case 34: +{ + tmpcor[ncorridor] = New(corridor); + tmpcor[ncorridor]->src.room = yyvsp[-2].corpos.room; + tmpcor[ncorridor]->src.wall = yyvsp[-2].corpos.wall; + tmpcor[ncorridor]->src.door = yyvsp[-2].corpos.door; + tmpcor[ncorridor]->dest.room = yyvsp[0].corpos.room; + tmpcor[ncorridor]->dest.wall = yyvsp[0].corpos.wall; + tmpcor[ncorridor]->dest.door = yyvsp[0].corpos.door; + ncorridor++; + if (ncorridor >= MAX_OF_TYPE) { + yyerror("Too many corridors in level!"); + ncorridor--; + } + } +break; +case 35: +{ + tmpcor[ncorridor] = New(corridor); + tmpcor[ncorridor]->src.room = yyvsp[-2].corpos.room; + tmpcor[ncorridor]->src.wall = yyvsp[-2].corpos.wall; + tmpcor[ncorridor]->src.door = yyvsp[-2].corpos.door; + tmpcor[ncorridor]->dest.room = -1; + tmpcor[ncorridor]->dest.wall = yyvsp[0].i; + ncorridor++; + if (ncorridor >= MAX_OF_TYPE) { + yyerror("Too many corridors in level!"); + ncorridor--; + } + } +break; +case 36: +{ + if ((unsigned) yyvsp[-5].i >= nrooms) + yyerror("Wrong room number!"); + yyval.corpos.room = yyvsp[-5].i; + yyval.corpos.wall = yyvsp[-3].i; + yyval.corpos.door = yyvsp[-1].i; + } +break; +case 37: +{ + store_room(); + } +break; +case 38: +{ + store_room(); + } +break; +case 39: +{ + tmproom[nrooms] = New(room); + tmproom[nrooms]->parent = yyvsp[-1].map; + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->rtype = yyvsp[-9].i; + tmproom[nrooms]->rlit = yyvsp[-7].i; + tmproom[nrooms]->filled = yyvsp[0].i; + tmproom[nrooms]->xalign = ERR; + tmproom[nrooms]->yalign = ERR; + tmproom[nrooms]->x = current_coord.x; + tmproom[nrooms]->y = current_coord.y; + tmproom[nrooms]->w = current_size.width; + tmproom[nrooms]->h = current_size.height; + in_room = 1; + } +break; +case 40: +{ + tmproom[nrooms] = New(room); + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->parent = (char *) 0; + tmproom[nrooms]->rtype = yyvsp[-9].i; + tmproom[nrooms]->rlit = yyvsp[-7].i; + tmproom[nrooms]->filled = yyvsp[0].i; + tmproom[nrooms]->xalign = current_align.x; + tmproom[nrooms]->yalign = current_align.y; + tmproom[nrooms]->x = current_coord.x; + tmproom[nrooms]->y = current_coord.y; + tmproom[nrooms]->w = current_size.width; + tmproom[nrooms]->h = current_size.height; + in_room = 1; + } +break; +case 41: +{ + yyval.i = 1; + } +break; +case 42: +{ + yyval.i = yyvsp[0].i; + } +break; +case 43: +{ + if ( yyvsp[-3].i < 1 || yyvsp[-3].i > 5 || + yyvsp[-1].i < 1 || yyvsp[-1].i > 5 ) { + yyerror("Room position should be between 1 & 5!"); + } else { + current_coord.x = yyvsp[-3].i; + current_coord.y = yyvsp[-1].i; + } + } +break; +case 44: +{ + current_coord.x = current_coord.y = ERR; + } +break; +case 45: +{ + if ( yyvsp[-3].i < 0 || yyvsp[-1].i < 0) { + yyerror("Invalid subroom position !"); + } else { + current_coord.x = yyvsp[-3].i; + current_coord.y = yyvsp[-1].i; + } + } +break; +case 46: +{ + current_coord.x = current_coord.y = ERR; + } +break; +case 47: +{ + current_align.x = yyvsp[-3].i; + current_align.y = yyvsp[-1].i; + } +break; +case 48: +{ + current_align.x = current_align.y = ERR; + } +break; +case 49: +{ + current_size.width = yyvsp[-3].i; + current_size.height = yyvsp[-1].i; + } +break; +case 50: +{ + current_size.height = current_size.width = ERR; + } +break; +case 66: +{ + if (tmproom[nrooms]->name) + yyerror("This room already has a name!"); + else + tmproom[nrooms]->name = yyvsp[0].map; + } +break; +case 67: +{ + if (tmproom[nrooms]->chance) + yyerror("This room already assigned a chance!"); + else if (tmproom[nrooms]->rtype == OROOM) + yyerror("Only typed rooms can have a chance!"); + else if (yyvsp[0].i < 1 || yyvsp[0].i > 99) + yyerror("The chance is supposed to be percentile."); + else + tmproom[nrooms]->chance = yyvsp[0].i; + } +break; +case 68: +{ + /* ERR means random here */ + if (yyvsp[-2].i == ERR && yyvsp[0].i != ERR) { + yyerror("If the door wall is random, so must be its pos!"); + } else { + tmprdoor[ndoor] = New(room_door); + tmprdoor[ndoor]->secret = yyvsp[-6].i; + tmprdoor[ndoor]->mask = yyvsp[-4].i; + tmprdoor[ndoor]->wall = yyvsp[-2].i; + tmprdoor[ndoor]->pos = yyvsp[0].i; + ndoor++; + if (ndoor >= MAX_OF_TYPE) { + yyerror("Too many doors in room!"); + ndoor--; + } + } + } +break; +case 75: +{ + maze.filling = (schar) yyvsp[0].i; + if (index(yyvsp[-2].map, '.')) + yyerror("Invalid dot ('.') in level name."); + if ((int) strlen(yyvsp[-2].map) > 8) + yyerror("Level names limited to 8 characters."); + yyval.map = yyvsp[-2].map; + in_room = 0; + n_plist = n_mlist = n_olist = 0; + } +break; +case 76: +{ + yyval.i = get_floor_type((char)yyvsp[0].i); + } +break; +case 77: +{ + yyval.i = -1; + } +break; +case 80: +{ + store_part(); + } +break; +case 81: +{ + tmppart[npart] = New(mazepart); + tmppart[npart]->halign = 1; + tmppart[npart]->valign = 1; + tmppart[npart]->nrobjects = 0; + tmppart[npart]->nloc = 0; + tmppart[npart]->nrmonst = 0; + tmppart[npart]->xsize = 1; + tmppart[npart]->ysize = 1; + tmppart[npart]->map = (char **) alloc(sizeof(char *)); + tmppart[npart]->map[0] = (char *) alloc(1); + tmppart[npart]->map[0][0] = STONE; + max_x_map = COLNO-1; + max_y_map = ROWNO; + } +break; +case 82: +{ + tmppart[npart] = New(mazepart); + tmppart[npart]->halign = yyvsp[-1].i % 10; + tmppart[npart]->valign = yyvsp[-1].i / 10; + tmppart[npart]->nrobjects = 0; + tmppart[npart]->nloc = 0; + tmppart[npart]->nrmonst = 0; + scan_map(yyvsp[0].map); + Free(yyvsp[0].map); + } +break; +case 83: +{ + yyval.i = yyvsp[-2].i + (yyvsp[0].i * 10); + } +break; +case 90: +{ + if (tmppart[npart]->nrobjects) { + yyerror("Object registers already initialized!"); + } else { + tmppart[npart]->robjects = (char *)alloc(n_olist); + (void) memcpy((genericptr_t)tmppart[npart]->robjects, + (genericptr_t)olist, n_olist); + tmppart[npart]->nrobjects = n_olist; + } + } +break; +case 91: +{ + if (tmppart[npart]->nloc) { + yyerror("Location registers already initialized!"); + } else { + register int i; + tmppart[npart]->rloc_x = (char *) alloc(n_plist); + tmppart[npart]->rloc_y = (char *) alloc(n_plist); + for(i=0;irloc_x[i] = plist[i].x; + tmppart[npart]->rloc_y[i] = plist[i].y; + } + tmppart[npart]->nloc = n_plist; + } + } +break; +case 92: +{ + if (tmppart[npart]->nrmonst) { + yyerror("Monster registers already initialized!"); + } else { + tmppart[npart]->rmonst = (char *) alloc(n_mlist); + (void) memcpy((genericptr_t)tmppart[npart]->rmonst, + (genericptr_t)mlist, n_mlist); + tmppart[npart]->nrmonst = n_mlist; + } + } +break; +case 93: +{ + if (n_olist < MAX_REGISTERS) + olist[n_olist++] = yyvsp[0].i; + else + yyerror("Object list too long!"); + } +break; +case 94: +{ + if (n_olist < MAX_REGISTERS) + olist[n_olist++] = yyvsp[-2].i; + else + yyerror("Object list too long!"); + } +break; +case 95: +{ + if (n_mlist < MAX_REGISTERS) + mlist[n_mlist++] = yyvsp[0].i; + else + yyerror("Monster list too long!"); + } +break; +case 96: +{ + if (n_mlist < MAX_REGISTERS) + mlist[n_mlist++] = yyvsp[-2].i; + else + yyerror("Monster list too long!"); + } +break; +case 97: +{ + if (n_plist < MAX_REGISTERS) + plist[n_plist++] = current_coord; + else + yyerror("Location list too long!"); + } +break; +case 98: +{ + if (n_plist < MAX_REGISTERS) + plist[n_plist++] = current_coord; + else + yyerror("Location list too long!"); + } +break; +case 122: +{ + tmpmonst[nmons] = New(monster); + tmpmonst[nmons]->x = current_coord.x; + tmpmonst[nmons]->y = current_coord.y; + tmpmonst[nmons]->class = yyvsp[-4].i; + tmpmonst[nmons]->peaceful = -1; /* no override */ + tmpmonst[nmons]->asleep = -1; + tmpmonst[nmons]->align = - MAX_REGISTERS - 2; + tmpmonst[nmons]->name.str = 0; + tmpmonst[nmons]->appear = 0; + tmpmonst[nmons]->appear_as.str = 0; + tmpmonst[nmons]->chance = yyvsp[-6].i; + tmpmonst[nmons]->id = NON_PM; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Monster"); + if (yyvsp[-2].map) { + int token = get_monster_id(yyvsp[-2].map, (char) yyvsp[-4].i); + if (token == ERR) + yywarning( + "Invalid monster name! Making random monster."); + else + tmpmonst[nmons]->id = token; + Free(yyvsp[-2].map); + } + } +break; +case 123: +{ + if (++nmons >= MAX_OF_TYPE) { + yyerror("Too many monsters in room or mazepart!"); + nmons--; + } + } +break; +case 126: +{ + tmpmonst[nmons]->name.str = yyvsp[0].map; + } +break; +case 127: +{ + tmpmonst[nmons]->peaceful = yyvsp[0].i; + } +break; +case 128: +{ + tmpmonst[nmons]->asleep = yyvsp[0].i; + } +break; +case 129: +{ + tmpmonst[nmons]->align = yyvsp[0].i; + } +break; +case 130: +{ + tmpmonst[nmons]->appear = yyvsp[-1].i; + tmpmonst[nmons]->appear_as.str = yyvsp[0].map; + } +break; +case 131: +{ + } +break; +case 132: +{ + /* 1: is contents of preceeding object with 2 */ + /* 2: is a container */ + /* 0: neither */ + tmpobj[nobj-1]->containment = 2; + } +break; +case 133: +{ + tmpobj[nobj] = New(object); + tmpobj[nobj]->class = yyvsp[-2].i; + tmpobj[nobj]->corpsenm = NON_PM; + tmpobj[nobj]->curse_state = -1; + tmpobj[nobj]->name.str = 0; + tmpobj[nobj]->chance = yyvsp[-4].i; + tmpobj[nobj]->id = -1; + if (yyvsp[0].map) { + int token = get_object_id(yyvsp[0].map, yyvsp[-2].i); + if (token == ERR) + yywarning( + "Illegal object name! Making random object."); + else + tmpobj[nobj]->id = token; + Free(yyvsp[0].map); + } + } +break; +case 134: +{ + if (++nobj >= MAX_OF_TYPE) { + yyerror("Too many objects in room or mazepart!"); + nobj--; + } + } +break; +case 135: +{ + tmpobj[nobj]->containment = 0; + tmpobj[nobj]->x = current_coord.x; + tmpobj[nobj]->y = current_coord.y; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Object"); + } +break; +case 136: +{ + tmpobj[nobj]->containment = 1; + /* random coordinate, will be overridden anyway */ + tmpobj[nobj]->x = -MAX_REGISTERS-1; + tmpobj[nobj]->y = -MAX_REGISTERS-1; + } +break; +case 137: +{ + tmpobj[nobj]->spe = -127; + /* Note below: we're trying to make as many of these optional as + * possible. We clearly can't make curse_state, enchantment, and + * monster_id _all_ optional, since ",random" would be ambiguous. + * We can't even just make enchantment mandatory, since if we do that + * alone, ",random" requires too much lookahead to parse. + */ + } +break; +case 138: +{ + } +break; +case 139: +{ + } +break; +case 140: +{ + } +break; +case 141: +{ + tmpobj[nobj]->curse_state = -1; + } +break; +case 142: +{ + tmpobj[nobj]->curse_state = yyvsp[0].i; + } +break; +case 143: +{ + int token = get_monster_id(yyvsp[0].map, (char)0); + if (token == ERR) /* "random" */ + tmpobj[nobj]->corpsenm = NON_PM - 1; + else + tmpobj[nobj]->corpsenm = token; + Free(yyvsp[0].map); + } +break; +case 144: +{ + tmpobj[nobj]->spe = -127; + } +break; +case 145: +{ + tmpobj[nobj]->spe = yyvsp[0].i; + } +break; +case 147: +{ + } +break; +case 148: +{ + tmpobj[nobj]->name.str = yyvsp[0].map; + } +break; +case 149: +{ + tmpdoor[ndoor] = New(door); + tmpdoor[ndoor]->x = current_coord.x; + tmpdoor[ndoor]->y = current_coord.y; + tmpdoor[ndoor]->mask = yyvsp[-2].i; + if(current_coord.x >= 0 && current_coord.y >= 0 && + tmpmap[current_coord.y][current_coord.x] != DOOR && + tmpmap[current_coord.y][current_coord.x] != SDOOR) + yyerror("Door decl doesn't match the map"); + ndoor++; + if (ndoor >= MAX_OF_TYPE) { + yyerror("Too many doors in mazepart!"); + ndoor--; + } + } +break; +case 150: +{ + tmptrap[ntrap] = New(trap); + tmptrap[ntrap]->x = current_coord.x; + tmptrap[ntrap]->y = current_coord.y; + tmptrap[ntrap]->type = yyvsp[-2].i; + tmptrap[ntrap]->chance = yyvsp[-4].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Trap"); + if (++ntrap >= MAX_OF_TYPE) { + yyerror("Too many traps in room or mazepart!"); + ntrap--; + } + } +break; +case 151: +{ + int x, y, dir; + + tmpdb[ndb] = New(drawbridge); + x = tmpdb[ndb]->x = current_coord.x; + y = tmpdb[ndb]->y = current_coord.y; + /* convert dir from a DIRECTION to a DB_DIR */ + dir = yyvsp[-2].i; + switch(dir) { + case W_NORTH: dir = DB_NORTH; y--; break; + case W_SOUTH: dir = DB_SOUTH; y++; break; + case W_EAST: dir = DB_EAST; x++; break; + case W_WEST: dir = DB_WEST; x--; break; + default: + yyerror("Invalid drawbridge direction"); + break; + } + tmpdb[ndb]->dir = dir; + if (current_coord.x >= 0 && current_coord.y >= 0 && + !IS_WALL(tmpmap[y][x])) { + char ebuf[60]; + Sprintf(ebuf, + "Wall needed for drawbridge (%02d, %02d)", + current_coord.x, current_coord.y); + yyerror(ebuf); + } + + if ( yyvsp[0].i == D_ISOPEN ) + tmpdb[ndb]->db_open = 1; + else if ( yyvsp[0].i == D_CLOSED ) + tmpdb[ndb]->db_open = 0; + else + yyerror("A drawbridge can only be open or closed!"); + ndb++; + if (ndb >= MAX_OF_TYPE) { + yyerror("Too many drawbridges in mazepart!"); + ndb--; + } + } +break; +case 152: +{ + tmpwalk[nwalk] = New(walk); + tmpwalk[nwalk]->x = current_coord.x; + tmpwalk[nwalk]->y = current_coord.y; + tmpwalk[nwalk]->dir = yyvsp[0].i; + nwalk++; + if (nwalk >= MAX_OF_TYPE) { + yyerror("Too many mazewalks in mazepart!"); + nwalk--; + } + } +break; +case 153: +{ + wallify_map(); + } +break; +case 154: +{ + tmplad[nlad] = New(lad); + tmplad[nlad]->x = current_coord.x; + tmplad[nlad]->y = current_coord.y; + tmplad[nlad]->up = yyvsp[0].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Ladder"); + nlad++; + if (nlad >= MAX_OF_TYPE) { + yyerror("Too many ladders in mazepart!"); + nlad--; + } + } +break; +case 155: +{ + tmpstair[nstair] = New(stair); + tmpstair[nstair]->x = current_coord.x; + tmpstair[nstair]->y = current_coord.y; + tmpstair[nstair]->up = yyvsp[0].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Stairway"); + nstair++; + if (nstair >= MAX_OF_TYPE) { + yyerror("Too many stairs in room or mazepart!"); + nstair--; + } + } +break; +case 156: +{ + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = yyvsp[0].i; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } +break; +case 157: +{ + tmplreg[nlreg]->del_islev = yyvsp[-2].i; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + if(yyvsp[0].i) + tmplreg[nlreg]->rtype = LR_UPSTAIR; + else + tmplreg[nlreg]->rtype = LR_DOWNSTAIR; + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } +break; +case 158: +{ + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = yyvsp[0].i; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } +break; +case 159: +{ + tmplreg[nlreg]->del_islev = yyvsp[-2].i; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + tmplreg[nlreg]->rtype = LR_PORTAL; + tmplreg[nlreg]->rname.str = yyvsp[0].map; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } +break; +case 160: +{ + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = yyvsp[0].i; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } +break; +case 161: +{ + tmplreg[nlreg]->del_islev = yyvsp[0].i; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + } +break; +case 162: +{ + switch(yyvsp[0].i) { + case -1: tmplreg[nlreg]->rtype = LR_TELE; break; + case 0: tmplreg[nlreg]->rtype = LR_DOWNTELE; break; + case 1: tmplreg[nlreg]->rtype = LR_UPTELE; break; + } + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } +break; +case 163: +{ + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = yyvsp[0].i; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } +break; +case 164: +{ + tmplreg[nlreg]->del_islev = yyvsp[0].i; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + tmplreg[nlreg]->rtype = LR_BRANCH; + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } +break; +case 165: +{ + yyval.i = -1; + } +break; +case 166: +{ + yyval.i = yyvsp[0].i; + } +break; +case 167: +{ + yyval.i = 0; + } +break; +case 168: +{ +/* This series of if statements is a hack for MSC 5.1. It seems that its + tiny little brain cannot compile if these are all one big if statement. */ + if (yyvsp[-7].i <= 0 || yyvsp[-7].i >= COLNO) + yyerror("Region out of level range!"); + else if (yyvsp[-5].i < 0 || yyvsp[-5].i >= ROWNO) + yyerror("Region out of level range!"); + else if (yyvsp[-3].i <= 0 || yyvsp[-3].i >= COLNO) + yyerror("Region out of level range!"); + else if (yyvsp[-1].i < 0 || yyvsp[-1].i >= ROWNO) + yyerror("Region out of level range!"); + current_region.x1 = yyvsp[-7].i; + current_region.y1 = yyvsp[-5].i; + current_region.x2 = yyvsp[-3].i; + current_region.y2 = yyvsp[-1].i; + yyval.i = 1; + } +break; +case 169: +{ + tmpfountain[nfountain] = New(fountain); + tmpfountain[nfountain]->x = current_coord.x; + tmpfountain[nfountain]->y = current_coord.y; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Fountain"); + nfountain++; + if (nfountain >= MAX_OF_TYPE) { + yyerror("Too many fountains in room or mazepart!"); + nfountain--; + } + } +break; +case 170: +{ + tmpsink[nsink] = New(sink); + tmpsink[nsink]->x = current_coord.x; + tmpsink[nsink]->y = current_coord.y; + nsink++; + if (nsink >= MAX_OF_TYPE) { + yyerror("Too many sinks in room!"); + nsink--; + } + } +break; +case 171: +{ + tmppool[npool] = New(pool); + tmppool[npool]->x = current_coord.x; + tmppool[npool]->y = current_coord.y; + npool++; + if (npool >= MAX_OF_TYPE) { + yyerror("Too many pools in room!"); + npool--; + } + } +break; +case 172: +{ + tmpdig[ndig] = New(digpos); + tmpdig[ndig]->x1 = current_region.x1; + tmpdig[ndig]->y1 = current_region.y1; + tmpdig[ndig]->x2 = current_region.x2; + tmpdig[ndig]->y2 = current_region.y2; + ndig++; + if (ndig >= MAX_OF_TYPE) { + yyerror("Too many diggables in mazepart!"); + ndig--; + } + } +break; +case 173: +{ + tmppass[npass] = New(digpos); + tmppass[npass]->x1 = current_region.x1; + tmppass[npass]->y1 = current_region.y1; + tmppass[npass]->x2 = current_region.x2; + tmppass[npass]->y2 = current_region.y2; + npass++; + if (npass >= 32) { + yyerror("Too many passwalls in mazepart!"); + npass--; + } + } +break; +case 174: +{ + tmpreg[nreg] = New(region); + tmpreg[nreg]->x1 = current_region.x1; + tmpreg[nreg]->y1 = current_region.y1; + tmpreg[nreg]->x2 = current_region.x2; + tmpreg[nreg]->y2 = current_region.y2; + tmpreg[nreg]->rlit = yyvsp[-3].i; + tmpreg[nreg]->rtype = yyvsp[-1].i; + if(yyvsp[0].i & 1) tmpreg[nreg]->rtype += MAXRTYPE+1; + tmpreg[nreg]->rirreg = ((yyvsp[0].i & 2) != 0); + if(current_region.x1 > current_region.x2 || + current_region.y1 > current_region.y2) + yyerror("Region start > end!"); + if(tmpreg[nreg]->rtype == VAULT && + (tmpreg[nreg]->rirreg || + (tmpreg[nreg]->x2 - tmpreg[nreg]->x1 != 1) || + (tmpreg[nreg]->y2 - tmpreg[nreg]->y1 != 1))) + yyerror("Vaults must be exactly 2x2!"); + if(want_warnings && !tmpreg[nreg]->rirreg && + current_region.x1 > 0 && current_region.y1 > 0 && + current_region.x2 < (int)max_x_map && + current_region.y2 < (int)max_y_map) { + /* check for walls in the room */ + char ebuf[60]; + register int x, y, nrock = 0; + + for(y=current_region.y1; y<=current_region.y2; y++) + for(x=current_region.x1; + x<=current_region.x2; x++) + if(IS_ROCK(tmpmap[y][x]) || + IS_DOOR(tmpmap[y][x])) nrock++; + if(nrock) { + Sprintf(ebuf, + "Rock in room (%02d,%02d,%02d,%02d)?!", + current_region.x1, current_region.y1, + current_region.x2, current_region.y2); + yywarning(ebuf); + } + if ( + !IS_ROCK(tmpmap[current_region.y1-1][current_region.x1-1]) || + !IS_ROCK(tmpmap[current_region.y2+1][current_region.x1-1]) || + !IS_ROCK(tmpmap[current_region.y1-1][current_region.x2+1]) || + !IS_ROCK(tmpmap[current_region.y2+1][current_region.x2+1])) { + Sprintf(ebuf, + "NonRock edge in room (%02d,%02d,%02d,%02d)?!", + current_region.x1, current_region.y1, + current_region.x2, current_region.y2); + yywarning(ebuf); + } + } else if(tmpreg[nreg]->rirreg && + !IS_ROOM(tmpmap[current_region.y1][current_region.x1])) { + char ebuf[60]; + Sprintf(ebuf, + "Rock in irregular room (%02d,%02d)?!", + current_region.x1, current_region.y1); + yyerror(ebuf); + } + nreg++; + if (nreg >= MAX_OF_TYPE) { + yyerror("Too many regions in mazepart!"); + nreg--; + } + } +break; +case 175: +{ + tmpaltar[naltar] = New(altar); + tmpaltar[naltar]->x = current_coord.x; + tmpaltar[naltar]->y = current_coord.y; + tmpaltar[naltar]->align = yyvsp[-2].i; + tmpaltar[naltar]->shrine = yyvsp[0].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Altar"); + naltar++; + if (naltar >= MAX_OF_TYPE) { + yyerror("Too many altars in room or mazepart!"); + naltar--; + } + } +break; +case 176: +{ + tmpgold[ngold] = New(gold); + tmpgold[ngold]->x = current_coord.x; + tmpgold[ngold]->y = current_coord.y; + tmpgold[ngold]->amount = yyvsp[-2].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Gold"); + ngold++; + if (ngold >= MAX_OF_TYPE) { + yyerror("Too many golds in room or mazepart!"); + ngold--; + } + } +break; +case 177: +{ + tmpengraving[nengraving] = New(engraving); + tmpengraving[nengraving]->x = current_coord.x; + tmpengraving[nengraving]->y = current_coord.y; + tmpengraving[nengraving]->engr.str = yyvsp[0].map; + tmpengraving[nengraving]->etype = yyvsp[-2].i; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Engraving"); + nengraving++; + if (nengraving >= MAX_OF_TYPE) { + yyerror("Too many engravings in room or mazepart!"); + nengraving--; + } + } +break; +case 179: +{ + yyval.i = - MAX_REGISTERS - 1; + } +break; +case 182: +{ + yyval.i = - MAX_REGISTERS - 1; + } +break; +case 185: +{ + yyval.map = (char *) 0; + } +break; +case 187: +{ + yyval.map = (char *) 0; + } +break; +case 188: +{ + int token = get_trap_type(yyvsp[0].map); + if (token == ERR) + yyerror("Unknown trap type!"); + yyval.i = token; + Free(yyvsp[0].map); + } +break; +case 190: +{ + int token = get_room_type(yyvsp[0].map); + if (token == ERR) { + yywarning("Unknown room type! Making ordinary room..."); + yyval.i = OROOM; + } else + yyval.i = token; + Free(yyvsp[0].map); + } +break; +case 192: +{ + yyval.i = 0; + } +break; +case 193: +{ + yyval.i = yyvsp[0].i; + } +break; +case 194: +{ + yyval.i = yyvsp[-2].i + (yyvsp[0].i << 1); + } +break; +case 197: +{ + current_coord.x = current_coord.y = -MAX_REGISTERS-1; + } +break; +case 204: +{ + yyval.i = - MAX_REGISTERS - 1; + } +break; +case 207: +{ + if ( yyvsp[-1].i >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + current_coord.x = current_coord.y = - yyvsp[-1].i - 1; + } +break; +case 208: +{ + if ( yyvsp[-1].i >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + yyval.i = - yyvsp[-1].i - 1; + } +break; +case 209: +{ + if ( yyvsp[-1].i >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + yyval.i = - yyvsp[-1].i - 1; + } +break; +case 210: +{ + if ( yyvsp[-1].i >= 3 ) + yyerror("Register Index overflow!"); + else + yyval.i = - yyvsp[-1].i - 1; + } +break; +case 212: +{ + if (check_monster_char((char) yyvsp[0].i)) + yyval.i = yyvsp[0].i ; + else { + yyerror("Unknown monster class!"); + yyval.i = ERR; + } + } +break; +case 213: +{ + char c = yyvsp[0].i; + if (check_object_char(c)) + yyval.i = c; + else { + yyerror("Unknown char class!"); + yyval.i = ERR; + } + } +break; +case 217: +{ + yyval.i = 100; /* default is 100% */ + } +break; +case 218: +{ + if (yyvsp[0].i <= 0 || yyvsp[0].i > 100) + yyerror("Expected percentile chance."); + yyval.i = yyvsp[0].i; + } +break; +case 221: +{ + if (!in_room && !init_lev.init_present && + (yyvsp[-3].i < 0 || yyvsp[-3].i > (int)max_x_map || + yyvsp[-1].i < 0 || yyvsp[-1].i > (int)max_y_map)) + yyerror("Coordinates out of map range!"); + current_coord.x = yyvsp[-3].i; + current_coord.y = yyvsp[-1].i; + } +break; +case 222: +{ +/* This series of if statements is a hack for MSC 5.1. It seems that its + tiny little brain cannot compile if these are all one big if statement. */ + if (yyvsp[-7].i < 0 || yyvsp[-7].i > (int)max_x_map) + yyerror("Region out of map range!"); + else if (yyvsp[-5].i < 0 || yyvsp[-5].i > (int)max_y_map) + yyerror("Region out of map range!"); + else if (yyvsp[-3].i < 0 || yyvsp[-3].i > (int)max_x_map) + yyerror("Region out of map range!"); + else if (yyvsp[-1].i < 0 || yyvsp[-1].i > (int)max_y_map) + yyerror("Region out of map range!"); + current_region.x1 = yyvsp[-7].i; + current_region.y1 = yyvsp[-5].i; + current_region.x2 = yyvsp[-3].i; + current_region.y2 = yyvsp[-1].i; + } +break; + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) != 0 && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/sys/share/nhlan.c b/sys/share/nhlan.c new file mode 100644 index 0000000..27b1783 --- /dev/null +++ b/sys/share/nhlan.c @@ -0,0 +1,191 @@ +/* SCCS Id: @(#)nhlan.c 3.4 1999/11/21 */ +/* Copyright (c) Michael Allison, 1997 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Currently shared by the following ports: + * WIN32 + * + * The code in here is used to take advantage of added features + * that might be available in a Local Area Network environment. + * + * Network Username of player + * Mail + * Futures + * Shared bones + * To implement this: code, data files, and configuration + * files need to be separated from writeable files such + * as level files, bones files, and save files. + * + */ + +#include "hack.h" +#include + +#ifdef LAN_FEATURES + +#ifdef LAN_MAIL +/* Port specific code needs to implement these routines for LAN_MAIL */ +extern char *FDECL(get_username, (int *)); +extern boolean NDECL(mail_check); +extern boolean FDECL(mail_fetch, (struct lan_mail_struct *)); +extern void FDECL(mail_init, (char *)); +extern void NDECL(mail_finish); + +struct lan_mail_struct mailmessage; +#endif /* LAN_MAIL */ + + +void init_lan_features() +{ + lan_username(); +#ifdef LAN_MAIL + lan_mail_init(); +#endif +#ifdef LAN_SHARED_BONES +#endif +} +/* + * The get_lan_username() call is a required call, since some of + * the other LAN features depend on a unique username being available. + * + */ +char lusername[MAX_LAN_USERNAME]; +int lusername_size = MAX_LAN_USERNAME; + +char *lan_username() +{ + char *lu; + lu = get_username(&lusername_size); + if (lu) { + Strcpy(lusername, lu); + return lusername; + } else return (char *)0; +} + +# ifdef LAN_MAIL +#if 0 +static void +mail_by_pline(msg) +struct lan_mail_struct *msg; +{ + long size; + + for (size = 0; size < qt_msg->size; size += (long)strlen(in_line)) { + (void) dlb_fgets(in_line, 80, msg_file); + convert_line(); + pline(out_line); + } + +} +#endif /* 0 */ + +static void +mail_by_window(msg) +struct lan_mail_struct *msg; +{ + char buf[BUFSZ]; + winid datawin = create_nhwindow(NHW_TEXT); + char *get, *put; + int ccount = 0; + + get = msg->body; + put = buf; + while (*get) { + if (ccount > 79) { + *put = '\0'; + putstr(datawin, 0, buf); + put = buf; + ccount = 0; + } + if (*get == '\r') { + get++; + } else if (*get == '\n') { + *put = '\0'; + putstr(datawin, 0, buf); + put = buf; + get++; + ccount = 0; + } else if (!isprint(*get)) { + get++; + } else { + *put++ = *get++; + ccount++; + } + } + *put = '\0'; + putstr(datawin, 0, buf); + putstr(datawin, 0, ""); + display_nhwindow(datawin, TRUE); + destroy_nhwindow(datawin); +} + +/* this returns TRUE if there is mail ready to be read */ +boolean lan_mail_check() +{ + if (flags.biff) { + if (mail_check()) return TRUE; + } + return FALSE; +} + +void lan_mail_read(otmp) +struct obj *otmp; +{ + if (flags.biff) { + (void) mail_fetch(&mailmessage); + /* after a successful fetch iflags.lan_mail_fetched + * should be TRUE. If it isn't then we don't + * trust the contents of mailmessage. This + * ensures that things work correctly across + * save/restores where mailmessage isn't + * saved (nor should it be since it may be + * way out of context by then). + */ + if (iflags.lan_mail_fetched) { + if (mailmessage.body_in_ram) { + mail_by_window(&mailmessage); + return; + } + } + } + pline_The("text has faded and is no longer readable."); +} + +void lan_mail_init() +{ + if (!flags.biff) return; + (void) mail_init(lusername); +} + +void lan_mail_finish() +{ + if (iflags.lan_mail) + (void) mail_finish(); +} + +/* If ever called, the underlying mail system ran into trouble + * and wants us to cease bothering it immediately. + * Don't call mail_finish() because the underlying mail system + * may already be unavailable. Just clean up the NetHack side + * of things to prevent a crash. + */ +void lan_mail_terminate() +{ + /* Step 1. Clear iflags.lan_mail to indicate "not inited" */ + iflags.lan_mail = FALSE; + + /* Step 2. Clear iflags.lan_mail_fetched */ + iflags.lan_mail_fetched = FALSE; + + /* Once having gotten to this point, the only + way to resume NetHack mail features again is + to Save/Quit game, or for the user to clear + iflags.biff and then set it once again, + which triggers mail initialization */ +} + +# endif /*LAN_MAIL*/ + +#endif /*LAN_FEATURES*/ +/*nhlan.c*/ diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c new file mode 100644 index 0000000..8b59a6b --- /dev/null +++ b/sys/share/pcmain.c @@ -0,0 +1,714 @@ +/* SCCS Id: @(#)pcmain.c 3.4 2002/08/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* main.c - MSDOS, OS/2, ST, Amiga, and NT NetHack */ + +#include "hack.h" +#include "dlb.h" + +#ifndef NO_SIGNAL +#include +#endif + +#include + +#if !defined(AMIGA) && !defined(GNUDOS) +#include +#else +# ifdef GNUDOS +#include +# endif +#endif + +#ifdef WIN32 +#include "win32api.h" /* for GetModuleFileName */ +#endif + +#ifdef __DJGPP__ +#include /* for getcwd() prototype */ +#endif + +#ifdef OVL0 +#define SHARED_DCL +#else +#define SHARED_DCL extern +#endif + + +SHARED_DCL char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */ + +#ifdef TOS +boolean run_from_desktop = TRUE; /* should we pause before exiting?? */ +# ifdef __GNUC__ +long _stksize = 16*1024; +# endif +#endif + +#ifdef AMIGA +extern int bigscreen; +void NDECL( preserve_icon ); +#endif + +STATIC_DCL void FDECL(process_options,(int argc,char **argv)); +STATIC_DCL void NDECL(nhusage); + +#if defined(MICRO) || defined(WIN32) || defined(OS2) +extern void FDECL(nethack_exit,(int)); +#else +#define nethack_exit exit +#endif + +#ifdef WIN32 +extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +#endif + +#if defined(MSWIN_GRAPHICS) +extern void NDECL(mswin_destroy_reg); +#endif + +#ifdef EXEPATH +STATIC_DCL char *FDECL(exepath,(char *)); +#endif + +#ifdef OVL0 +int FDECL(main, (int,char **)); +#endif + +extern void FDECL(pcmain, (int,char **)); + + +#if defined(__BORLANDC__) && !defined(_WIN32) +void NDECL( startup ); +# ifdef OVLB +unsigned _stklen = STKSIZ; +# else +extern unsigned _stklen; +# endif +#endif + +#ifdef OVL0 +/* If the graphics version is built, we don't need a main; it is skipped + * to help MinGW decide which entry point to choose. If both main and + * WinMain exist, the resulting executable won't work correctly. + */ +#ifndef MSWIN_GRAPHICS +int +main(argc,argv) +int argc; +char *argv[]; +{ + pcmain(argc,argv); +#ifdef LAN_FEATURES + init_lan_features(); +#endif + moveloop(); + nethack_exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} +#endif /*MSWIN_GRAPHICS*/ +#endif /*OVL0*/ +#ifdef OVL1 + +void +pcmain(argc,argv) +int argc; +char *argv[]; +{ + + register int fd; + register char *dir; +#if defined(WIN32) + char fnamebuf[BUFSZ], encodedfnamebuf[BUFSZ]; +#endif +#ifdef NOCWD_ASSUMPTIONS + char failbuf[BUFSZ]; +#endif + +#if defined(__BORLANDC__) && !defined(_WIN32) + startup(); +#endif + +#ifdef TOS + long clock_time; + if (*argv[0]) { /* only a CLI can give us argv[0] */ + hname = argv[0]; + run_from_desktop = FALSE; + } else +#endif + hname = "NetHack"; /* used for syntax messages */ + + choose_windows(DEFAULT_WINDOW_SYS); + +#if !defined(AMIGA) && !defined(GNUDOS) + /* Save current directory and make sure it gets restored when + * the game is exited. + */ + if (getcwd(orgdir, sizeof orgdir) == (char *)0) + error("NetHack: current directory path too long"); +# ifndef NO_SIGNAL + signal(SIGINT, (SIG_RET_TYPE) nethack_exit); /* restore original directory */ +# endif +#endif /* !AMIGA && !GNUDOS */ + + dir = nh_getenv("NETHACKDIR"); + if (dir == (char *)0) + dir = nh_getenv("HACKDIR"); +#ifdef EXEPATH + if (dir == (char *)0) + dir = exepath(argv[0]); +#endif + if (dir != (char *)0) { + (void) strncpy(hackdir, dir, PATHLEN - 1); + hackdir[PATHLEN-1] = '\0'; +#ifdef NOCWD_ASSUMPTIONS + { + int prefcnt; + + fqn_prefix[0] = (char *)alloc(strlen(hackdir)+2); + Strcpy(fqn_prefix[0], hackdir); + append_slash(fqn_prefix[0]); + for (prefcnt = 1; prefcnt < PREFIX_COUNT; prefcnt++) + fqn_prefix[prefcnt] = fqn_prefix[0]; + } +#endif +#ifdef CHDIR + chdirx (dir, 1); +#endif + } +#ifdef AMIGA +# ifdef CHDIR + /* + * If we're dealing with workbench, change the directory. Otherwise + * we could get "Insert disk in drive 0" messages. (Must be done + * before initoptions()).... + */ + if(argc == 0) + chdirx(HACKDIR, 1); +# endif + ami_wininit_data(); +#endif + initoptions(); + +#ifdef NOCWD_ASSUMPTIONS + if (!validate_prefix_locations(failbuf)) { + raw_printf("Some invalid directory locations were specified:\n\t%s\n", + failbuf); + nethack_exit(EXIT_FAILURE); + } +#endif + +#if defined(TOS) && defined(TEXTCOLOR) + if (iflags.BIOS && iflags.use_color) + set_colors(); +#endif + if (!hackdir[0]) +#if !defined(LATTICE) && !defined(AMIGA) + Strcpy(hackdir, orgdir); +#else + Strcpy(hackdir, HACKDIR); +#endif + if(argc > 1) { + if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { + /* avoid matching "-dec" for DECgraphics; since the man page + * says -d directory, hope nobody's using -desomething_else + */ + argc--; + argv++; + dir = argv[0]+2; + if(*dir == '=' || *dir == ':') dir++; + if(!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if(!*dir) + error("Flag -d must be followed by a directory name."); + Strcpy(hackdir, dir); + } + if (argc > 1) { + + /* + * Now we know the directory containing 'record' and + * may do a prscore(). + */ + if (!strncmp(argv[1], "-s", 2)) { +#if !defined(MSWIN_GRAPHICS) +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(hackdir,0); +# endif + prscore(argc, argv); +#else + raw_printf("-s is not supported for the Graphical Interface\n"); +#endif /*MSWIN_GRAPHICS*/ + nethack_exit(EXIT_SUCCESS); + } + +#ifdef MSWIN_GRAPHICS + if (!strncmpi(argv[1], "-clearreg", 6)) { /* clear registry */ + mswin_destroy_reg(); + nethack_exit(EXIT_SUCCESS); + } +#endif + /* Don't initialize the window system just to print usage */ + if (!strncmp(argv[1], "-?", 2) || !strncmp(argv[1], "/?", 2)) { + nhusage(); + nethack_exit(EXIT_SUCCESS); + } + } + } + + /* + * It seems you really want to play. + */ +#ifdef TOS + if (comp_times((long)time(&clock_time))) + error("Your clock is incorrectly set!"); +#endif + u.uhp = 1; /* prevent RIP on early quits */ + u.ux = 0; /* prevent flush_screen() */ + + /* chdir shouldn't be called before this point to keep the + * code parallel to other ports. + */ +#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(hackdir,1); +#endif + +#ifdef MSDOS + process_options(argc, argv); + init_nhwindows(&argc,argv); +#else + init_nhwindows(&argc,argv); + process_options(argc, argv); +#endif + +#ifdef WIN32CON + toggle_mouse_support(); /* must come after process_options */ +#endif + +#ifdef MFLOPPY + set_lock_and_bones(); +# ifndef AMIGA + copybones(FROMPERM); +# endif +#endif + + if (!*plname) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ +#ifdef WIZARD + if (wizard) { +# ifdef KR1ED + if(!strcmp(plname, WIZARD_NAME)) +# else + if(!strcmp(plname, WIZARD)) +# endif + Strcpy(plname, "wizard"); + else { + wizard = FALSE; + discover = TRUE; + } + } +#endif /* WIZARD */ +#if defined(PC_LOCKING) + /* 3.3.0 added this to support detection of multiple games + * under the same plname on the same machine in a windowed + * or multitasking environment. + * + * That allows user confirmation prior to overwriting the + * level files of a game in progress. + * + * Also prevents an aborted game's level files from being + * overwritten without confirmation when a user starts up + * another game with the same player name. + */ +# if defined(WIN32) + /* Obtain the name of the logged on user and incorporate + * it into the name. */ + Sprintf(fnamebuf, "%s-%s", get_username(0), plname); + (void)fname_encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_-.", + '%', fnamebuf, encodedfnamebuf, BUFSZ); + Sprintf(lock, "%s",encodedfnamebuf); + /* regularize(lock); */ /* we encode now, rather than substitute */ +# else + Strcpy(lock,plname); + regularize(lock); +# endif + getlock(); +#else /* What follows is !PC_LOCKING */ +# ifdef AMIGA /* We'll put the bones & levels in the user specified directory -jhsa */ + Strcat(lock,plname); + Strcat(lock,".99"); +# else +# ifndef MFLOPPY + /* I'm not sure what, if anything, is left here, but MFLOPPY has + * conflicts with set_lock_and_bones() in files.c. + */ + Strcpy(lock,plname); + Strcat(lock,".99"); + regularize(lock); /* is this necessary? */ + /* not compatible with full path a la AMIGA */ +# endif +# endif +#endif /* PC_LOCKING */ + + /* Set up level 0 file to keep the game state. + */ + fd = create_levelfile(0, (char *)0); + if (fd < 0) { + raw_print("Cannot create lock file"); + } else { +#ifdef WIN32 + hackpid = GetCurrentProcessId(); +#else + hackpid = 1; +#endif + write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); + close(fd); + } +#ifdef MFLOPPY + level_info[0].where = ACTIVE; +#endif + + /* + * Initialisation of the boundaries of the mazes + * Both boundaries have to be even. + */ + + x_maze_max = COLNO-1; + if (x_maze_max % 2) + x_maze_max--; + y_maze_max = ROWNO-1; + if (y_maze_max % 2) + y_maze_max--; + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + dlb_init(); + + display_gamewindows(); +#ifdef WIN32 + getreturn_enabled = TRUE; +#endif + + if ((fd = restore_saved_game()) >= 0) { +#ifdef WIZARD + /* Since wizard is actually flags.debug, restoring might + * overwrite it. + */ + boolean remember_wiz_mode = wizard; +#endif +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif +#ifdef NEWS + if(iflags.news){ + display_file(NEWS, FALSE); + iflags.news = FALSE; + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + + if(!dorecover(fd)) + goto not_recovered; +#ifdef WIZARD + if(!wizard && remember_wiz_mode) wizard = TRUE; +#endif + check_special_room(FALSE); + if (discover) + You("are in non-scoring discovery mode."); + + if (discover || wizard) { + if(yn("Do you want to keep the save file?") == 'n'){ + (void) delete_savefile(); + } + } + + flags.move = 0; + } else { +not_recovered: + player_selection(); + newgame(); + if (discover) + You("are in non-scoring discovery mode."); + + flags.move = 0; + set_wear(); + (void) pickup(1); + read_engr_at(u.ux,u.uy); + } + +#ifndef NO_SIGNAL + (void) signal(SIGINT, SIG_IGN); +#endif +#ifdef OS2 + gettty(); /* somehow ctrl-P gets turned back on during startup ... */ +#endif + return; +} + +STATIC_OVL void +process_options(argc, argv) +int argc; +char *argv[]; +{ + int i; + + + /* + * Process options. + */ + while(argc > 1 && argv[1][0] == '-'){ + argv++; + argc--; + switch(argv[0][1]){ + case 'a': + if (argv[0][2]) { + if ((i = str2align(&argv[0][2])) >= 0) + flags.initalign = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2align(argv[0])) >= 0) + flags.initalign = i; + } + break; + case 'D': +#ifdef WIZARD + /* If they don't have a valid wizard name, it'll be + * changed to discover later. Cannot check for + * validity of the name right now--it might have a + * character class suffix, for instance. + */ + wizard = TRUE; + break; +#endif + case 'X': + discover = TRUE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if(argv[0][2]) + (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); + else if(argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname)-1); + } else + raw_print("Player name expected after -u"); + break; +#ifndef AMIGA + case 'I': + case 'i': + if (!strncmpi(argv[0]+1, "IBM", 3)) + switch_graphics(IBM_GRAPHICS); + break; + /* case 'D': */ + case 'd': + if (!strncmpi(argv[0]+1, "DEC", 3)) + switch_graphics(DEC_GRAPHICS); + break; +#endif + case 'g': + if (argv[0][2]) { + if ((i = str2gend(&argv[0][2])) >= 0) + flags.initgend = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2gend(argv[0])) >= 0) + flags.initgend = i; + } + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; +#ifdef MFLOPPY +# ifndef AMIGA + /* Player doesn't want to use a RAM disk + */ + case 'R': + ramdisk = FALSE; + break; +# endif +#endif +#ifdef AMIGA + /* interlaced and non-interlaced screens */ + case 'L': + bigscreen = 1; + break; + case 'l': + bigscreen = -1; + break; +#endif + case '@': + flags.randomall = 1; + break; + default: + if ((i = str2role(&argv[0][1])) >= 0) { + flags.initrole = i; + break; + } else raw_printf("\nUnknown switch: %s", argv[0]); + /* FALL THROUGH */ + case '?': + nhusage(); + nethack_exit(EXIT_SUCCESS); + } + } +} + +STATIC_OVL void +nhusage() +{ + char buf1[BUFSZ], buf2[BUFSZ], *bufptr; + + buf1[0] = '\0'; + bufptr = buf1; + +#define ADD_USAGE(s) if ((strlen(buf1) + strlen(s)) < (BUFSZ - 1)) Strcat(bufptr, s); + + /* -role still works for those cases which aren't already taken, but + * is deprecated and will not be listed here. + */ + (void) Sprintf(buf2, +"\nUsage:\n%s [-d dir] -s [-r race] [-p profession] [maxrank] [name]...\n or", + hname); + ADD_USAGE(buf2); + + (void) Sprintf(buf2, + "\n%s [-d dir] [-u name] [-r race] [-p profession] [-[DX]]", + hname); + ADD_USAGE(buf2); +#ifdef NEWS + ADD_USAGE(" [-n]"); +#endif +#ifndef AMIGA + ADD_USAGE(" [-I] [-i] [-d]"); +#endif +#ifdef MFLOPPY +# ifndef AMIGA + ADD_USAGE(" [-R]"); +# endif +#endif +#ifdef AMIGA + ADD_USAGE(" [-[lL]]"); +#endif + if (!iflags.window_inited) + raw_printf("%s\n",buf1); + else + (void) printf("%s\n",buf1); +#undef ADD_USAGE +} + +#ifdef CHDIR +void +chdirx(dir, wr) +char *dir; +boolean wr; +{ +# ifdef AMIGA + static char thisdir[] = ""; +# else + static char thisdir[] = "."; +# endif + if(dir && chdir(dir) < 0) { + error("Cannot chdir to %s.", dir); + } + +# ifndef AMIGA + /* Change the default drive as well. + */ + chdrive(dir); +# endif + + /* warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access system-call is worthless */ + if (wr) check_recordfile(dir ? dir : thisdir); +} +#endif /* CHDIR */ +#endif /*OVL1*/ +#ifdef OVLB + +#ifdef PORT_HELP +# if defined(MSDOS) || defined(WIN32) +void +port_help() +{ + /* display port specific help file */ + display_file( PORT_HELP, 1 ); +} +# endif /* MSDOS || WIN32 */ +#endif /* PORT_HELP */ + +#ifdef EXEPATH +# ifdef __DJGPP__ +#define PATH_SEPARATOR '/' +# else +#define PATH_SEPARATOR '\\' +# endif + +#define EXEPATHBUFSZ 256 +char exepathbuf[EXEPATHBUFSZ]; + +char *exepath(str) +char *str; +{ + char *tmp, *tmp2; + int bsize; + + if (!str) return (char *)0; + bsize = EXEPATHBUFSZ; + tmp = exepathbuf; +# ifndef WIN32 + Strcpy (tmp, str); +# else + #ifdef UNICODE + { + TCHAR wbuf[BUFSZ]; + GetModuleFileName((HANDLE)0, wbuf, BUFSZ); + WideCharToMultiByte(CP_ACP, 0, wbuf, -1, tmp, bsize, NULL, NULL); + } + #else + *(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0'; + #endif +# endif + tmp2 = strrchr(tmp, PATH_SEPARATOR); + if (tmp2) *tmp2 = '\0'; + return tmp; +} +#endif /* EXEPATH */ +#endif /*OVLB*/ +/*pcmain.c*/ diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c new file mode 100644 index 0000000..b67661c --- /dev/null +++ b/sys/share/pcsys.c @@ -0,0 +1,541 @@ +/* SCCS Id: @(#)pcsys.c 3.4 2002/01/22 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * System related functions for MSDOS, OS/2, TOS, and Windows NT + */ + +#define NEED_VARARGS +#include "hack.h" +#include "wintty.h" + +#include +#include +#if !defined(MSDOS) && !defined(WIN_CE) /* already done */ +#include +#endif +#ifdef __GO32__ +#define P_WAIT 0 +#define P_NOWAIT 1 +#endif +#ifdef TOS +#include +#endif +#if defined(MSDOS) && !defined(__GO32__) +#define findfirst findfirst_file +#define findnext findnext_file +#define filesize filesize_nh +#endif + + +#if defined(MICRO) || defined(WIN32) || defined(OS2) +void FDECL(nethack_exit,(int)); +#else +#define nethack_exit exit +#endif +static void NDECL(msexit); + + +#ifdef MOVERLAY +extern void __far __cdecl _movepause( void ); +extern void __far __cdecl _moveresume( void ); +extern unsigned short __far __cdecl _movefpause; +extern unsigned short __far __cdecl _movefpaused; +#define __MOVE_PAUSE_DISK 2 /* Represents the executable file */ +#define __MOVE_PAUSE_CACHE 4 /* Represents the cache memory */ +#endif /* MOVERLAY */ + +#ifdef MFLOPPY +STATIC_DCL boolean NDECL(record_exists); +# ifndef TOS +STATIC_DCL boolean NDECL(comspec_exists); +# endif +#endif + +#ifdef WIN32CON +extern int GUILaunched; /* from nttty.c */ +#endif + +#if defined(MICRO) || defined(WIN32) + +void +flushout() +{ + (void) fflush(stdout); + return; +} + +static const char *COMSPEC = +# ifdef TOS +"SHELL"; +# else +"COMSPEC"; +# endif + +#define getcomspec() nh_getenv(COMSPEC) + +# ifdef SHELL +int +dosh() +{ + extern char orgdir[]; + char *comspec; +# ifndef __GO32__ + int spawnstat; +# endif +#if defined(MSDOS) && defined(NO_TERMS) + int grmode = iflags.grmode; +#endif + if ((comspec = getcomspec())) { +# ifndef TOS /* TOS has a variety of shells */ + suspend_nhwindows("To return to NetHack, enter \"exit\" at the system prompt.\n"); +# else +# if defined(MSDOS) && defined(NO_TERMS) + grmode = iflags.grmode; +# endif + suspend_nhwindows((char *)0); +# endif /* TOS */ +# ifndef NOCWD_ASSUMPTIONS + chdirx(orgdir, 0); +# endif +# ifdef __GO32__ + if (system(comspec) < 0) { /* wsu@eecs.umich.edu */ +# else +# ifdef MOVERLAY + /* Free the cache memory used by overlays, close .exe */ + _movefpause |= __MOVE_PAUSE_DISK; + _movefpause |= __MOVE_PAUSE_CACHE; + _movepause(); +# endif + spawnstat = spawnl(P_WAIT, comspec, comspec, (char *)0); +# ifdef MOVERLAY + _moveresume(); +# endif + + if ( spawnstat < 0) { +# endif + raw_printf("Can't spawn \"%s\"!", comspec); + getreturn("to continue"); + } +# ifdef TOS +/* Some shells (e.g. Gulam) turn the cursor off when they exit */ + if (iflags.BIOS) + (void)Cursconf(1, -1); +# endif +# ifndef NOCWD_ASSUMPTIONS + chdirx(hackdir, 0); +# endif + get_scr_size(); /* maybe the screen mode changed (TH) */ +# if defined(MSDOS) && defined(NO_TERMS) + if (grmode) gr_init(); +# endif + resume_nhwindows(); + } else + pline("Can't find %s.",COMSPEC); + return 0; +} +# endif /* SHELL */ + +# ifdef MFLOPPY + +void +eraseall(path, files) +const char *path, *files; +{ + char buf[PATHLEN]; + char *foundfile; + + foundfile = foundfile_buffer(); + Sprintf(buf, "%s%s", path, files); + if (findfirst(buf)) + do { + Sprintf(buf, "%s%s", path, foundfile); + (void) unlink(buf); + } while (findnext()); + return; +} + +/* + * Rewritten for version 3.3 to be faster + */ +void +copybones(mode) +int mode; +{ + char from[PATHLEN], to[PATHLEN], last[13]; + char *frompath, *topath; + char *foundfile; +# ifndef TOS + int status; + char copy[8], *comspec; +# endif + + if (!ramdisk) + return; + + /* Find the name of the last file to be transferred + */ + frompath = (mode != TOPERM) ? permbones : levels; + foundfile = foundfile_buffer(); + last[0] = '\0'; + Sprintf(from, "%s%s", frompath, allbones); + topath = (mode == TOPERM) ? permbones : levels; +# ifdef TOS + eraseall(topath, allbones); +# endif + if (findfirst(from)) + do { +# ifdef TOS + Sprintf(from, "%s%s", frompath, foundfile); + Sprintf(to, "%s%s", topath, foundfile); + if (_copyfile(from, to)) + goto error_copying; +# endif + Strcpy(last, foundfile); + } while (findnext()); +# ifdef TOS + else + return; +# else + if (last[0]) { + Sprintf(copy, "%cC copy",switchar()); + + /* Remove any bones files in `to' directory. + */ + eraseall(topath, allbones); + + /* Copy `from' to `to' */ + Sprintf(to, "%s%s", topath, allbones); + comspec = getcomspec(); + status =spawnl(P_WAIT, comspec, comspec, copy, from, + to, "> nul", (char *)0); + } else + return; +# endif /* TOS */ + + /* See if the last file got there. If so, remove the ramdisk bones + * files. + */ + Sprintf(to, "%s%s", topath, last); + if (findfirst(to)) { + if (mode == TOPERM) + eraseall(frompath, allbones); + return; + } + +# ifdef TOS +error_copying: +# endif + /* Last file didn't get there. + */ + Sprintf(to, "%s%s", topath, allbones); + msmsg("Can't copy \"%s\" to \"%s\" -- ", from, to); +# ifndef TOS + if (status < 0) + msmsg("can't spawn \"%s\"!", comspec); + else +# endif + msmsg((freediskspace(topath) < filesize(from)) ? + "insufficient disk space." : "bad path(s)?"); + if (mode == TOPERM) { + msmsg("Bones will be left in \"%s\"\n", + *levels ? levels : hackdir); + } else { + /* Remove all bones files on the RAMdisk */ + eraseall(levels, allbones); + playwoRAMdisk(); + } + return; +} + +void +playwoRAMdisk() +{ + int c; + + msmsg("Do you wish to play without a RAMdisk? [yn] (n)"); + + /* Set ramdisk false *before* exit-ing (because msexit calls + * copybones) + */ + ramdisk = FALSE; + c = tgetch(); if (c == 'Y') c = 'y'; + if (c != 'y') { + settty("Be seeing you...\n"); + nethack_exit(EXIT_SUCCESS); + } + set_lock_and_bones(); + return; +} + +int +saveDiskPrompt(start) +int start; +{ + char buf[BUFSIZ], *bp; + char qbuf[QBUFSZ]; + + int fd; + + if (flags.asksavedisk) { + /* Don't prompt if you can find the save file */ + if ((fd = open_savefile()) >= 0) { + (void) close(fd); + return 1; + } + clear_nhwindow(WIN_MESSAGE); + pline("If save file is on a save disk, insert that disk now."); + mark_synch(); + Sprintf(qbuf,"File name (default \"%s\"%s) ?", SAVEF, + start ? "" : ", cancels save"); + getlin(qbuf, buf); + clear_nhwindow(WIN_MESSAGE); + if (!start && *buf == '\033') + return 0; + + /* Strip any whitespace. Also, if nothing was entered except + * whitespace, do not change the value of SAVEF. + */ + for (bp = buf; *bp; bp++) + if (!isspace(*bp)) { + strncpy(SAVEF, bp, PATHLEN); + break; + } + } + return 1; +} + +/* Return 1 if the record file was found */ +STATIC_OVL boolean +record_exists() +{ + FILE *fp; + + fp = fopen_datafile(RECORD, "r", TRUE); + if (fp) { + fclose(fp); + return TRUE; + } + return FALSE; +} +#endif /* MFLOPPY */ + +# ifdef TOS +#define comspec_exists() 1 +# else +# ifdef MFLOPPY +/* Return 1 if the comspec was found */ +STATIC_OVL boolean +comspec_exists() +{ + int fd; + char *comspec; + + if ((comspec = getcomspec())) + if ((fd = open(comspec, O_RDONLY)) >= 0) { + (void) close(fd); + return TRUE; + } + return FALSE; +} +# endif /* MFLOPPY */ +# endif + + +# ifdef MFLOPPY +/* Prompt for game disk, then check for record file. + */ +void +gameDiskPrompt() +{ + if (flags.asksavedisk) { + if (record_exists() && comspec_exists()) + return; + (void) putchar('\n'); + getreturn("when the game disk has been inserted"); + } + if (comspec_exists() && record_exists()) + return; + + if (!comspec_exists()) + msmsg("\n\nWARNING: can't find command processor \"%s\"!\n", getcomspec()); + if (!record_exists()) + msmsg("\n\nWARNING: can't find record file \"%s\"!\n", RECORD); + msmsg("If the game disk is not in, insert it now.\n"); + getreturn("to continue"); + return; +} +# endif /* MFLOPPY */ +#endif /* MICRO */ + +/* + * Add a backslash to any name not ending in /, \ or : There must + * be room for the \ + */ +void +append_slash(name) +char *name; +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '\\' && *ptr != '/' && *ptr != ':') { + *++ptr = '\\'; + *++ptr = '\0'; + } + return; +} + +#ifdef WIN32 +boolean getreturn_enabled; +#endif + +void +getreturn(str) +const char *str; +{ +#ifdef WIN32 + if (!getreturn_enabled) return; +#endif +#ifdef TOS + msmsg("Hit %s.", str); +#else + msmsg("Hit %s.", str); +#endif + while (Getchar() != '\n') ; + return; +} + +#ifndef WIN32CON +void +msmsg VA_DECL(const char *, fmt) + VA_START(fmt); + VA_INIT(fmt, const char *); +# if defined(MSDOS) && defined(NO_TERMS) + if (iflags.grmode) + gr_finish(); +# endif + Vprintf(fmt, VA_ARGS); + flushout(); + VA_END(); + return; +} +#endif + +/* + * Follow the PATH, trying to fopen the file. + */ +#ifdef TOS +# ifdef __MINT__ +#define PATHSEP ':' +# else +#define PATHSEP ',' +# endif +#else +#define PATHSEP ';' +#endif + +FILE * +fopenp(name, mode) +const char *name, *mode; +{ + char buf[BUFSIZ], *bp, *pp, lastch = 0; + FILE *fp; + + /* Try the default directory first. Then look along PATH. + */ + (void) strncpy(buf, name, BUFSIZ - 1); + buf[BUFSIZ-1] = '\0'; + if ((fp = fopen(buf, mode))) + return fp; + else { + int ccnt = 0; + pp = getenv("PATH"); + while (pp && *pp) { + bp = buf; + while (*pp && *pp != PATHSEP) { + lastch = *bp++ = *pp++; + ccnt++; + } + if (lastch != '\\' && lastch != '/') { + *bp++ = '\\'; + ccnt++; + } + (void) strncpy(bp, name, (BUFSIZ - ccnt) - 2); + bp[BUFSIZ - ccnt - 1] = '\0'; + if ((fp = fopen(buf, mode))) + return fp; + if (*pp) + pp++; + } + } +#ifdef OS2_CODEVIEW /* one more try for hackdir */ + (void) strncpy(buf, hackdir, BUFSZ); + buf[BUFSZ-1] = '\0'; + if ((strlen(name) + 1 + strlen(buf)) < BUFSZ - 1) { + append_slash(buf); + Strcat(buf,name); + } else + impossible("fopenp() buffer too small for complete filename!"); + if(fp = fopen(buf,mode)) + return fp; +#endif + return (FILE *)0; +} + +#if defined(MICRO) || defined(WIN32) || defined(OS2) +void nethack_exit(code) +int code; +{ + msexit(); + exit(code); +} + +/* Chdir back to original directory + */ +#ifdef TOS +extern boolean run_from_desktop; /* set in pcmain.c */ +#endif + +static void msexit() +{ +#ifdef CHDIR + extern char orgdir[]; +#endif + + flushout(); +#ifndef TOS +# ifndef WIN32 + enable_ctrlP(); /* in case this wasn't done */ +# endif +#endif +#ifdef MFLOPPY + if (ramdisk) copybones(TOPERM); +#endif +#if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdir(orgdir); /* chdir, not chdirx */ + chdrive(orgdir); +#endif +#ifdef TOS + if (run_from_desktop) + getreturn("to continue"); /* so the user can read the score list */ +# ifdef TEXTCOLOR + if (colors_changed) + restore_colors(); +# endif +#endif +#ifdef WIN32CON + /* Only if we started from the GUI, not the command prompt, + * we need to get one last return, so the score board does + * not vanish instantly after being created. + * GUILaunched is defined and set in nttty.c. + */ + synch_cursor(); + if (GUILaunched) getreturn("to end"); + synch_cursor(); +#endif + return; +} +#endif /* MICRO || WIN32 || OS2 */ diff --git a/sys/share/pctty.c b/sys/share/pctty.c new file mode 100644 index 0000000..5dd3d00 --- /dev/null +++ b/sys/share/pctty.c @@ -0,0 +1,85 @@ +/* SCCS Id: @(#)pctty.c 3.4 1990/22/02 +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* tty.c - (PC) version */ + +#define NEED_VARARGS /* Uses ... */ /* comment line for pre-compiled headers */ +#include "hack.h" +#include "wintty.h" + +char erase_char, kill_char; + +/* + * Get initial state of terminal, set ospeed (for termcap routines) + * and switch off tab expansion if necessary. + * Called by startup() in termcap.c and after returning from ! or ^Z + */ +void +gettty(){ + erase_char = '\b'; + kill_char = 21; /* cntl-U */ + iflags.cbreak = TRUE; +#if !defined(TOS) + disable_ctrlP(); /* turn off ^P processing */ +#endif +#if defined(MSDOS) && defined(NO_TERMS) + gr_init(); +#endif +} + +/* reset terminal to original state */ +void +settty(s) +const char *s; +{ +#if defined(MSDOS) && defined(NO_TERMS) + gr_finish(); +#endif + end_screen(); + if(s) raw_print(s); +#if !defined(TOS) + enable_ctrlP(); /* turn on ^P processing */ +#endif + +} + +/* called by init_nhwindows() and resume_nhwindows() */ +void +setftty() +{ + start_screen(); +} + +#if defined(TIMED_DELAY) && defined(_MSC_VER) +void +msleep(mseconds) +unsigned mseconds; +{ + /* now uses clock() which is ANSI C */ + clock_t goal; + + goal = mseconds + clock(); + while ( goal > clock()) { + /* do nothing */ + } +} +#endif + +/* fatal error */ +/*VARARGS1*/ + +void +error VA_DECL(const char *,s) + VA_START(s); + VA_INIT(s, const char *); + /* error() may get called before tty is initialized */ + if (iflags.window_inited) end_screen(); + putchar('\n'); + Vprintf(s,VA_ARGS); + putchar('\n'); + VA_END(); + exit(EXIT_FAILURE); +} + +/*pctty.c*/ diff --git a/sys/share/pcunix.c b/sys/share/pcunix.c new file mode 100644 index 0000000..1f1149e --- /dev/null +++ b/sys/share/pcunix.c @@ -0,0 +1,289 @@ +/* SCCS Id: @(#)pcunix.c 3.4 1994/11/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file collects some Unix dependencies; pager.c contains some more */ + +#include "hack.h" +#include "wintty.h" + +#include +#if defined(WIN32) || defined(MSDOS) +#include +#endif + +#if defined(WIN32) || defined(MSDOS) +extern char orgdir[]; +# ifdef WIN32 +extern void NDECL(backsp); +# endif +extern void NDECL(clear_screen); +#endif + +#ifdef OVLB + +#if 0 +static struct stat buf; +#endif + +# ifdef WANT_GETHDATE +static struct stat hbuf; +# endif + +#ifdef PC_LOCKING +static int NDECL(eraseoldlocks); +#endif + +#if 0 +int +uptodate(fd) +int fd; +{ +# ifdef WANT_GETHDATE + if(fstat(fd, &buf)) { + pline("Cannot get status of saved level? "); + return(0); + } + if(buf.st_mtime < hbuf.st_mtime) { + pline("Saved level is out of date. "); + return(0); + } +# else +# if (defined(MICRO) || defined(WIN32)) && !defined(NO_FSTAT) + if(fstat(fd, &buf)) { + if(moves > 1) pline("Cannot get status of saved level? "); + else pline("Cannot get status of saved game."); + return(0); + } + if(comp_times(buf.st_mtime)) { + if(moves > 1) pline("Saved level is out of date."); + else pline("Saved game is out of date. "); + /* This problem occurs enough times we need to give the player + * some more information about what causes it, and how to fix. + */ +# ifdef MSDOS + pline("Make sure that your system's date and time are correct."); + pline("They must be more current than NetHack.EXE's date/time stamp."); +# endif /* MSDOS */ + return(0); + } +# endif /* MICRO */ +# endif /* WANT_GETHDATE */ + return(1); +} +#endif + +#ifdef PC_LOCKING +static int +eraseoldlocks() +{ + register int i; + + /* cannot use maxledgerno() here, because we need to find a lock name + * before starting everything (including the dungeon initialization + * that sets astral_level, needed for maxledgerno()) up + */ + for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) { + /* try to remove all */ + set_levelfile_name(lock, i); + (void) unlink(fqname(lock, LEVELPREFIX, 0)); + } + set_levelfile_name(lock, 0); +#ifdef HOLD_LOCKFILE_OPEN + really_close(); +#endif + if(unlink(fqname(lock, LEVELPREFIX, 0))) + return 0; /* cannot remove it */ + return(1); /* success! */ +} + +void +getlock() +{ + register int fd, c, ci, ct, ern; + char tbuf[BUFSZ]; + const char *fq_lock; +# if defined(MSDOS) && defined(NO_TERMS) + int grmode = iflags.grmode; +# endif + /* we ignore QUIT and INT at this point */ + if (!lock_file(HLOCK, LOCKPREFIX, 10)) { + wait_synch(); +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("Quitting."); + } + + /* regularize(lock); */ /* already done in pcmain */ + Sprintf(tbuf,"%s",fqname(lock, LEVELPREFIX, 0)); + set_levelfile_name(lock, 0); + fq_lock = fqname(lock, LEVELPREFIX, 1); + if((fd = open(fq_lock,0)) == -1) { + if(errno == ENOENT) goto gotlock; /* no such file */ +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif +# if defined(WIN32) || defined(HOLD_LOCKFILE_OPEN) +# if defined(HOLD_LOCKFILE_OPEN) + if(errno == EACCES) { +#define OOPS_BUFSZ 512 + char oops[OOPS_BUFSZ]; + Strcpy(oops, + "\nThere are files from a game in progress under your name."); + Strcat(oops, "\nThe files are locked or inaccessible."); + Strcat(oops, " Is the other game still running?\n"); + if (strlen(fq_lock) < ((OOPS_BUFSZ -16) - strlen(oops))) + Sprintf(eos(oops), "Cannot open %s", fq_lock); + Strcat(oops, "\n"); + unlock_file(HLOCK); + error(oops); + } else +# endif + error("Bad directory or name: %s\n%s\n", + fq_lock, strerror(errno)); +# else + perror(fq_lock); +# endif + unlock_file(HLOCK); + error("Cannot open %s", fq_lock); + } + + (void) close(fd); + + if(iflags.window_inited) { +# ifdef SELF_RECOVER + c = yn("There are files from a game in progress under your name. Recover?"); +# else + pline("There is already a game in progress under your name."); + pline("You may be able to use \"recover %s\" to get it back.\n",tbuf); + c = yn("Do you want to destroy the old game?"); +# endif + } else { +# if defined(MSDOS) && defined(NO_TERMS) + grmode = iflags.grmode; + if (grmode) gr_finish(); +# endif + c = 'n'; + ct = 0; +# ifdef SELF_RECOVER + msmsg( + "There are files from a game in progress under your name. Recover? [yn]"); +# else + msmsg("\nThere is already a game in progress under your name.\n"); + msmsg("If this is unexpected, you may be able to use \n"); + msmsg("\"recover %s\" to get it back.",tbuf); + msmsg("\nDo you want to destroy the old game? [yn] "); +# endif + while ((ci=nhgetch()) != '\n') { + if (ct > 0) { + msmsg("\b \b"); + ct = 0; + c = 'n'; + } + if (ci == 'y' || ci == 'n' || ci == 'Y' || ci == 'N') { + ct = 1; + c = ci; + msmsg("%c",c); + } + } + } + if(c == 'y' || c == 'Y') +# ifndef SELF_RECOVER + if(eraseoldlocks()) { +# if defined(WIN32CON) + clear_screen(); /* display gets fouled up otherwise */ +# endif + goto gotlock; + } else { + unlock_file(HLOCK); +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("Couldn't destroy old game."); + } +# else /*SELF_RECOVER*/ + if(recover_savefile()) { +# if defined(WIN32CON) + clear_screen(); /* display gets fouled up otherwise */ +# endif + goto gotlock; + } else { + unlock_file(HLOCK); +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("Couldn't recover old game."); + } +# endif /*SELF_RECOVER*/ + else { + unlock_file(HLOCK); +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("%s", "Cannot start a new game."); + } + +gotlock: + fd = creat(fq_lock, FCMASK); + if (fd == -1) ern = errno; + unlock_file(HLOCK); + if(fd == -1) { +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif +# if defined(WIN32) + error("cannot creat file (%s.)\n%s\n%s\"%s\" exists?\n", + fq_lock, strerror(ern), " Are you sure that the directory", + fqn_prefix[LEVELPREFIX]); +# else + error("cannot creat file (%s.)", fq_lock); +# endif + } else { + if(write(fd, (char *) &hackpid, sizeof(hackpid)) + != sizeof(hackpid)){ +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("cannot write lock (%s)", fq_lock); + } + if(close(fd) == -1) { +# if defined(CHDIR) && !defined(NOCWD_ASSUMPTIONS) + chdirx(orgdir, 0); +# endif + error("cannot close lock (%s)", fq_lock); + } + } +# if defined(MSDOS) && defined(NO_TERMS) + if (grmode) gr_init(); +# endif +} +#endif /* PC_LOCKING */ + +# ifndef WIN32 +void +regularize(s) +/* + * normalize file name - we don't like .'s, /'s, spaces, and + * lots of other things + */ +register char *s; +{ + register char *lp; + + for (lp = s; *lp; lp++) + if (*lp <= ' ' || *lp == '"' || (*lp >= '*' && *lp <= ',') || + *lp == '.' || *lp == '/' || (*lp >= ':' && *lp <= '?') || +# ifdef OS2 + *lp == '&' || *lp == '(' || *lp == ')' || +# endif + *lp == '|' || *lp >= 127 || (*lp >= '[' && *lp <= ']')) + *lp = '_'; +} +# endif /* WIN32 */ +#endif /* OVLB */ + + +#ifdef __EMX__ +void seteuid(int i){;} +#endif diff --git a/sys/share/random.c b/sys/share/random.c new file mode 100644 index 0000000..02b50c5 --- /dev/null +++ b/sys/share/random.c @@ -0,0 +1,386 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* Several minor changes were made for the NetHack distribution to satisfy + * non-BSD compilers (by definition BSD compilers do not need to compile + * this file for NetHack). These changes consisted of: + * - changing the sccsid conditions to nested ifdefs from defined()s + * to accommodate stupid preprocessors + * - giving srandom() type void instead of allowing it to default to int + * - making the first return in initstate() return a value consistent + * with its type (instead of no value) + * - ANSI function prototyping in extern.h - therefore include hack.h + * instead of stdio.h and remove separate declaration of random() from + * the beginning of function srandom + * - moving sccsid after hack.h to allow precompiled headers, which + * means the defined()s would be ok again... + * - change fprintf(stderr, "x(%d)y\n", z) to impossible("x(%d)y", z) + * - remove useless variable `j' from srandom() + */ + +#include "hack.h" + +#ifdef LIBC_SCCS +# ifndef lint +static char sccsid[] = "@(#)random.c 5.5 (Berkeley) 7/6/88"; +# endif +#endif /* LIBC_SCCS and not lint */ + +/* + * random.c: + * An improved random number generation package. In addition to the standard + * rand()/srand() like interface, this package also has a special state info + * interface. The initstate() routine is called with a seed, an array of + * bytes, and a count of how many bytes are being passed in; this array is then + * initialized to contain information for random number generation with that + * much state information. Good sizes for the amount of state information are + * 32, 64, 128, and 256 bytes. The state can be switched by calling the + * setstate() routine with the same array as was initiallized with initstate(). + * By default, the package runs with 128 bytes of state information and + * generates far better random numbers than a linear congruential generator. + * If the amount of state information is less than 32 bytes, a simple linear + * congruential R.N.G. is used. + * Internally, the state information is treated as an array of longs; the + * zeroeth element of the array is the type of R.N.G. being used (small + * integer); the remainder of the array is the state information for the + * R.N.G. Thus, 32 bytes of state information will give 7 longs worth of + * state information, which will allow a degree seven polynomial. (Note: the + * zeroeth word of state information also has some other information stored + * in it -- see setstate() for details). + * The random number generation technique is a linear feedback shift register + * approach, employing trinomials (since there are fewer terms to sum up that + * way). In this approach, the least significant bit of all the numbers in + * the state table will act as a linear feedback shift register, and will have + * period 2^deg - 1 (where deg is the degree of the polynomial being used, + * assuming that the polynomial is irreducible and primitive). The higher + * order bits will have longer periods, since their values are also influenced + * by pseudo-random carries out of the lower bits. The total period of the + * generator is approximately deg*(2**deg - 1); thus doubling the amount of + * state information has a vast influence on the period of the generator. + * Note: the deg*(2**deg - 1) is an approximation only good for large deg, + * when the period of the shift register is the dominant factor. With deg + * equal to seven, the period is actually much longer than the 7*(2**7 - 1) + * predicted by this formula. + */ + + + +/* + * For each of the currently supported random number generators, we have a + * break value on the amount of state information (you need at least this + * many bytes of state info to support this random number generator), a degree + * for the polynomial (actually a trinomial) that the R.N.G. is based on, and + * the separation between the two lower order coefficients of the trinomial. + */ + +#define TYPE_0 0 /* linear congruential */ +#define BREAK_0 8 +#define DEG_0 0 +#define SEP_0 0 + +#define TYPE_1 1 /* x**7 + x**3 + 1 */ +#define BREAK_1 32 +#define DEG_1 7 +#define SEP_1 3 + +#define TYPE_2 2 /* x**15 + x + 1 */ +#define BREAK_2 64 +#define DEG_2 15 +#define SEP_2 1 + +#define TYPE_3 3 /* x**31 + x**3 + 1 */ +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +#define TYPE_4 4 /* x**63 + x + 1 */ +#define BREAK_4 256 +#define DEG_4 63 +#define SEP_4 1 + + +/* + * Array versions of the above information to make code run faster -- relies + * on fact that TYPE_i == i. + */ + +#define MAX_TYPES 5 /* max number of types above */ + +static int degrees[ MAX_TYPES ] = { DEG_0, DEG_1, DEG_2, + DEG_3, DEG_4 }; + +static int seps[ MAX_TYPES ] = { SEP_0, SEP_1, SEP_2, + SEP_3, SEP_4 }; + + + +/* + * Initially, everything is set up as if from : + * initstate( 1, &randtbl, 128 ); + * Note that this initialization takes advantage of the fact that srandom() + * advances the front and rear pointers 10*rand_deg times, and hence the + * rear pointer which starts at 0 will also end up at zero; thus the zeroeth + * element of the state information, which contains info about the current + * position of the rear pointer is just + * MAX_TYPES*(rptr - state) + TYPE_3 == TYPE_3. + */ + +static long randtbl[ DEG_3 + 1 ] = { TYPE_3, + 0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342, + 0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb, + 0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd, + 0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86, + 0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7, + 0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc, + 0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b, + 0xf5ad9d0e, 0x8999220b, 0x27fb47b9 }; + +/* + * fptr and rptr are two pointers into the state info, a front and a rear + * pointer. These two pointers are always rand_sep places aparts, as they cycle + * cyclically through the state information. (Yes, this does mean we could get + * away with just one pointer, but the code for random() is more efficient this + * way). The pointers are left positioned as they would be from the call + * initstate( 1, randtbl, 128 ) + * (The position of the rear pointer, rptr, is really 0 (as explained above + * in the initialization of randtbl) because the state table pointer is set + * to point to randtbl[1] (as explained below). + */ + +static long *fptr = &randtbl[ SEP_3 + 1 ]; +static long *rptr = &randtbl[ 1 ]; + + + +/* + * The following things are the pointer to the state information table, + * the type of the current generator, the degree of the current polynomial + * being used, and the separation between the two pointers. + * Note that for efficiency of random(), we remember the first location of + * the state information, not the zeroeth. Hence it is valid to access + * state[-1], which is used to store the type of the R.N.G. + * Also, we remember the last location, since this is more efficient than + * indexing every time to find the address of the last element to see if + * the front and rear pointers have wrapped. + */ + +static long *state = &randtbl[ 1 ]; + +static int rand_type = TYPE_3; +static int rand_deg = DEG_3; +static int rand_sep = SEP_3; + +static long *end_ptr = &randtbl[ DEG_3 + 1 ]; + + + +/* + * srandom: + * Initialize the random number generator based on the given seed. If the + * type is the trivial no-state-information type, just remember the seed. + * Otherwise, initializes state[] based on the given "seed" via a linear + * congruential generator. Then, the pointers are set to known locations + * that are exactly rand_sep places apart. Lastly, it cycles the state + * information a given number of times to get rid of any initial dependencies + * introduced by the L.C.R.N.G. + * Note that the initialization of randtbl[] for default usage relies on + * values produced by this routine. + */ + +void +srandom( x ) + + unsigned x; +{ + register int i; + + if( rand_type == TYPE_0 ) { + state[ 0 ] = x; + } + else { + state[ 0 ] = x; + for( i = 1; i < rand_deg; i++ ) { + state[i] = 1103515245*state[i - 1] + 12345; + } + fptr = &state[ rand_sep ]; + rptr = &state[ 0 ]; + for( i = 0; i < 10*rand_deg; i++ ) random(); + } +} + + + +/* + * initstate: + * Initialize the state information in the given array of n bytes for + * future random number generation. Based on the number of bytes we + * are given, and the break values for the different R.N.G.'s, we choose + * the best (largest) one we can and set things up for it. srandom() is + * then called to initialize the state information. + * Note that on return from srandom(), we set state[-1] to be the type + * multiplexed with the current value of the rear pointer; this is so + * successive calls to initstate() won't lose this information and will + * be able to restart with setstate(). + * Note: the first thing we do is save the current state, if any, just like + * setstate() so that it doesn't matter when initstate is called. + * Returns a pointer to the old state. + */ + +char * +initstate( seed, arg_state, n ) + + unsigned seed; /* seed for R. N. G. */ + char *arg_state; /* pointer to state array */ + int n; /* # bytes of state info */ +{ + register char *ostate = (char *)( &state[ -1 ] ); + + if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; + else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; + if( n < BREAK_1 ) { + if( n < BREAK_0 ) { + impossible( + "initstate: not enough state (%d bytes) with which to do jack; ignored.", n); + return (char *)0; + } + rand_type = TYPE_0; + rand_deg = DEG_0; + rand_sep = SEP_0; + } + else { + if( n < BREAK_2 ) { + rand_type = TYPE_1; + rand_deg = DEG_1; + rand_sep = SEP_1; + } + else { + if( n < BREAK_3 ) { + rand_type = TYPE_2; + rand_deg = DEG_2; + rand_sep = SEP_2; + } + else { + if( n < BREAK_4 ) { + rand_type = TYPE_3; + rand_deg = DEG_3; + rand_sep = SEP_3; + } + else { + rand_type = TYPE_4; + rand_deg = DEG_4; + rand_sep = SEP_4; + } + } + } + } + state = &( ( (long *)arg_state )[1] ); /* first location */ + end_ptr = &state[ rand_deg ]; /* must set end_ptr before srandom */ + srandom( seed ); + if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; + else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; + return( ostate ); +} + + + +/* + * setstate: + * Restore the state from the given state array. + * Note: it is important that we also remember the locations of the pointers + * in the current state information, and restore the locations of the pointers + * from the old state information. This is done by multiplexing the pointer + * location into the zeroeth word of the state information. + * Note that due to the order in which things are done, it is OK to call + * setstate() with the same state as the current state. + * Returns a pointer to the old state information. + */ + +char * +setstate( arg_state ) + + char *arg_state; +{ + register long *new_state = (long *)arg_state; + register int type = new_state[0]%MAX_TYPES; + register int rear = new_state[0]/MAX_TYPES; + char *ostate = (char *)( &state[ -1 ] ); + + if( rand_type == TYPE_0 ) state[ -1 ] = rand_type; + else state[ -1 ] = MAX_TYPES*(rptr - state) + rand_type; + switch( type ) { + case TYPE_0: + case TYPE_1: + case TYPE_2: + case TYPE_3: + case TYPE_4: + rand_type = type; + rand_deg = degrees[ type ]; + rand_sep = seps[ type ]; + break; + + default: + impossible("setstate: state info has been munged (%d); not changed.", type); + break; + } + state = &new_state[ 1 ]; + if( rand_type != TYPE_0 ) { + rptr = &state[ rear ]; + fptr = &state[ (rear + rand_sep)%rand_deg ]; + } + end_ptr = &state[ rand_deg ]; /* set end_ptr too */ + return( ostate ); +} + + + +/* + * random: + * If we are using the trivial TYPE_0 R.N.G., just do the old linear + * congruential bit. Otherwise, we do our fancy trinomial stuff, which is the + * same in all ther other cases due to all the global variables that have been + * set up. The basic operation is to add the number at the rear pointer into + * the one at the front pointer. Then both pointers are advanced to the next + * location cyclically in the table. The value returned is the sum generated, + * reduced to 31 bits by throwing away the "least random" low bit. + * Note: the code takes advantage of the fact that both the front and + * rear pointers can't wrap on the same call by not testing the rear + * pointer if the front one has wrapped. + * Returns a 31-bit random number. + */ + +long +random() +{ + long i; + + if( rand_type == TYPE_0 ) { + i = state[0] = ( state[0]*1103515245 + 12345 )&0x7fffffff; + } + else { + *fptr += *rptr; + i = (*fptr >> 1)&0x7fffffff; /* chucking least random bit */ + if( ++fptr >= end_ptr ) { + fptr = state; + ++rptr; + } + else { + if( ++rptr >= end_ptr ) rptr = state; + } + } + return( i ); +} + diff --git a/sys/share/sounds/README b/sys/share/sounds/README new file mode 100644 index 0000000..74bddea --- /dev/null +++ b/sys/share/sounds/README @@ -0,0 +1,34 @@ +README for the AIFF files: + +These files are sound files for the instruments in NetHack. +There are 11 sounds, one for each distinct instrument. +The sounds are in 8-bit 22kHz AIFF format, which should be +readable by a broad range of platforms. Since the sounds +came from Rolands S-750 sample library (most of them) there +should be no copyright on them when we treat them like we +do here (as instruments) - indeed, the sample library I +got from Roland didn't even bear a (c) symbol. + +Some of the sounds are very adequate (Drum of Earthquake, +Wooden Flute, Magic Harp) while some are less true to the +original name (how does a Frost Horn sound?) Actually, I +don't know what a Bugle is (Bugle horn?) so I took a trumpet +sound for that. Correct me if I'm wrong. + +What does this have to do with the main code? Well, nothing +so far. There are some places that are #ifdef MAC and calls +mac_speaker; that function takes an object and a tune (in +capital letters A-G) and plays the tune with the given +instrument. When playing a specific tune, that tune is of +course used. For "improvise," I use middle "C." + +Ideally, we should do something equal with sound that we +have with displays, so we can use one common set of calls +in the main code, and have ports do whatever seems appropriate +for the available hardware. + +Any comment on the sounds or their use is welcome: + + Jon W{tte + Mac Team + nethack-bugs@nethack.org diff --git a/sys/share/sounds/bell.uu b/sys/share/sounds/bell.uu new file mode 100644 index 0000000..ec6a17a --- /dev/null +++ b/sys/share/sounds/bell.uu @@ -0,0 +1,407 @@ +begin 644 Bell +M``1"96QL```````````````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``````````````$0B```! +MSJ<]K>2G/:WQ``````````````````````````````"!@6<2``!&3U)-``!$ +M&D%)1D934TY$``!"'@`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M_P````#_``#___[^``$#`_W]_OG[!@L%_??S^P``!P_X[/_\_@`&!__^[O@) +M^?<*%/CF!OWL"Q0&\?+]^`'__@C[^`@#]_3Q^0L%_Q8$U><%_PXD!^OVY=0( +M+`_]``#DT?8='`H.!N'=Y/`H(/@5!\_-!AL"%PSY^M#H&P'_(`O:]`SA^QWX +M_A?PZP[YZ`@,]@H+ZO\"[``$_`L)Z/89[M\6$OD`_?X`Z>06)?'S#?3H[0,: +M#O[]]^7P$__['/SJ_/CT!@SY`@OQ\PCX]@@&_O0`!/O\\PH*Z_H/!>OX%_;I +M#A'W\?SY^0L`"!CQXO?\]`DD#/'ZY^#_"!,8$O'?^^GG&B@&]03WUND.&1?^ +M^P#LXO8/%0[_\?<`Z/0;$?SW__OL^PD!_@<*[O(3]>WX!0'_"03W^/;Y"PCT``[PYPD(_@?^]/X#[_H9 +M`?;]\_[]^`L0^.P(`-\,&/H$`?'R`OK[%0GP_?_U^/@3$NOX%/S>_ACW^`[Z +M_OSU`O__`@3Z^P;O_!#L^A[^Y`P0WO@7^?L,_/?Z\0`._/D1_.\)_NX&"/0` +M!OG\"/?U#/_W`04&^/8$__/W#0CZ"/SR`_KN"A[_Y_\(\?+\$QKUZ?\-].D. +M&@7OZ0H&Z/H?&>7J#OON`Q(.]?$#]_0)%`#P!_OO!OP'%_CL`PCC^R3^^0CV +M\_OU"!O]\!(`UP@8Z_P7`>__!03\Y@0D\.0?&.+C!@L&]?HH"X3$O\#]NT"!/P,$??U_>_\#00%"OOQ`/SS"Q($^O/X_/?[%Q;Q^`#O +M]`@/$`CNZ0;^Y0XI!^SQ`/;R"!$/`?#R]O[_!A<&]OCU]P(%!@L&[_4([_L; +M_NL*#._T#@/Z"OCW#/7L!!0*]OD#^?;[#`_W\04%]?@/#.?W%?_X!O[Q^P?[ +M"QGO[@/S]`\4^_D&]>S^"1`"]`4'\>8%&`#S"`_Q[/L$#@;U`0_TX0L4[_P1 +M_O,'__,#`OC]#@7_^?#^`/P*$`'X]_GS`@_\"0SKZ`<,[@,E^N@%_N;T'1'^ +M!O?S\.L+)@WS^@3TX/T/#Q7S^0GCYA`B_/06_-GR%@CT^@X&^`+[^_;M`QP/ +MZ_L'X>8/'PS]^_/V[/4=$0'\_.OH"`$%$`C\[/O[ZPT8_/H,^=_^#/8&&/GM +M"OOL`P3Y!!'_]@'R]0+S$"+TX@0%XOP;&P4#!_OGU?\N"?@7!^+;^@\)#A$-\MON_OX0&@\$Z]_U +M`?X-(`SP]O+G^`@4$P#]`>G9`QT#`A((\.+L`A,#!QWYY>[U`0L6!0<$V.@' +M!0\*#`+PZN@+$08+``'\Y.T/$P`!"_CC]@L)`P`6"-7N$`CT`!H"[N?^&??M +M%![NW!(0X?47&>_G"0?XZP<;^.D#%/KC!"("V/TJ^OV +M(2_TU?P%XO@D%@7YVO`.\_!.'V^]H+.P;M`O+B``X* +M)0K5XP'Z`R8=`?#@W?$8*!L(\.?6YPP>-17KY>K@Z2 +M[M'[#`D:`>P,!-CN(`SX"07T]_?U#@?X"P7O]P7W_Q<%]0+][_C_!A4%\OC[ +M_OX`_@T/ZO0&`OOX`@L*]?P,\NT)!OP0!?#^^.T(%O?Z%?CR`OC\!0`*&?': +M"P7>!"D2]^_N_O_N#RK^YOWXZP`)#!/\]@#MZQ`6]@L.XO'\]@\9_/45\M$+ +M&0,#`0'UZ^T0&O@&"NOM!0+]#Q'XZOH&]O84#O`"">CW#_O]&?WE"O_D"!7X +M!`CK_@CO^Q[[Z1?ZY@P+\OL5^?0-]/L,^/<("?/[#/KS`@W^\P4)\_D0_?<$ +M`_CZ"/T"`_7__O\"`@`$">SQ#@3V"!+P]`+P]10:^/P'[./\%`H,#_;F[/S_ +M"B`3^.CN]_0&'"``W>O_^?<7)`SMX?`"!/X;(.[C^_3K#",-!@#CZ/CY#BX/ +M[_SFU/HD&Q$+]>_>X!$F#@4*\MOR_@4;$P+X]^_M`Q$1`OL&]>K\"`P'_?<' +M!>O[$P3Z_?H""OOV#`3L``O^!@SX]O_X_`L*_P7YZ0,+^086!^KR`_4&#@(' +M`?;E_18$"0'_`^_K_QX'\@L"]O?W``\-]0`/^>GZ"PK^^Q`)Z?$&!``*!?D' +M^>\!!`8'_?P*_.@!$_H"$//U"?7M$AOY\P;^]/<#%@CU]P``^/H+%P+M^PS[ +M]``)#_OM!0WR]18%\P@"]``'^`$*[_X2]O49"=X`$_/X%!'X].[S$`7_%1#N +MX0,"_P\("O_MY_T5^00@`.7Q!/W_%0D``^SH!1(``!``[/3^!A`!_`GXZ?T1 +M__L4_^T"_/0#$`H``?'V!/D!$`OZ\?D``_\!$@'N^O\$`OT!`__U_P7]"/_X +M#`3N^0S]^`X!^P?T\`P&]PP0\_`!^?H)"0\%[/L`[OH8$?H&^>;Y_@09$?[] +M\N3Z#0D4$O#U]MP%+@;R&PC(Z1$($@T``/;=\1T/!1+ZZO?U]10;_/C^[^X, +M#@H0`.OF`@G^#Q7^Y^S[`Q(2!PKWVNL)"Q(5`?3W[>02(`@'_/;T]>\**?SI +M"@?M[PX-!`3U_@;\]@(0__3[`@4'`/L#__;Y!PX(`//X!_CW#Q#_]P#Y]@<$ +M!0[_]O?\__L,$@CY[?O^`08*%P;IY_\&]PT?"?+M]_4#$`\2_>KP_OP!&`SZ +M`_OL^`P("0?U!`CCZ"(=YO\?^>?V`@8+`P`4]]L"#_C_&!'[]._T`O\&&P_X +M]?+M]Q$9"P4#]^3K"`\+"@0#_NSK"0\"#`_Y\?CO_!0-!0H![/+^_PT2`?O_ +M]/`"#@4""P3V\_X!_@L,!?OJ^@4!``H6]_/]_0KW\@H5!/$%_NSX!A,.!/SY +M[NH'$`\,`OGR\.D.(@,("?#?\@X)#P\(!=O;#AC_""$`X^KM#1P!`!#ZV?@8 +M`?\)!/OU``0)_?0&`/<&"__[!/[R^@P+`O[\`_KN^PL4`/T)]/#\!P4$#_;Q +M!OKY#POY_`#S`A+W_!7VX?\/!@L,]/C_Y?P?#/P'_N'Z#_X($`'W^??X#`H` +M!/GY`?T""PGY\OD"`P$2#_/H^0$`$@X'!.3=`Q@'#B#QV>_X`1[O!QH(`A'^WNH+#P4,%P#?Y0`+ +M!`X:`^WL^`(##Q`&^NSV`P`!%!7NZ@D![@4?!/#[__KX!1,+]/<'^?`+$_[\ +M!/SY^OP*#?SY"P#G^!(*^005_>+V#P#Z$Q/V[_GW_@@,%`GO\P#S\@X<#?GS +M^_OH^"0@^O,`\>7_%!,*__;Q]/D'&1'Z\OWU[08;$/?Y]^[Y!1<5`_;O\>\# +M&1@*^.[L]?X*(Q7U[_3N\!,="`'Y\._\"!$1`?3T]OH($PS]]O?R_!`4#/WR +M[?+^#!X1^OCLXOH?%0GX^P<1$`OU[/,$"0,-"_SK\@@)!P@"__/M_Q(/`@C\Z/<&``L:!>_Q +M]?H)"PD2_^;Q!0,%$@W\\_#V!PD)$`;S[?D#!`T-"/[O\/D("@41"_;I]@C_ +M_P\4!?#S__SZ`1$3`??Y^_3U#A@*!/_VY^T($A$.#OO>Z04*"1@<^N'H]@(. +M$Q$P3U[>GV#Q80!0'XY.L-%@8$!_WP\OX/%_[W +M"?_I]A@+]@GL`PL-$@KY[^[V!1$1"OOO^/;T$1H&]_KZ +M[O@,%Q'U]/WS]045%`/QZ_\`]0X=!_'R]O,#$!`)_O;L\PD/!PD*\>3Y"@P. +M"OSV[^T#%Q8'^_+M\_P0&PP`]^CE`A8+$1CVVNS^`1$>#?KQX^X.$`P3"N[D +M]OP)%0L%_.[L_PL+$@GR\OGQ_1D3_/[][?(&"PT1_?'Y^/(#&0O[`_SN]P<- +M"`4`^OCS_PL'`P+^^?W__PP+^_P!^O<%!_\%!?SY`0(``P0!_P(`^?\'__@! +M"@+^`P+_^?H#"`0!!?_V^?\#!@8(!?OV_`#Y!1`*_O?Y^OL`"A0']?C[_O\% +M#`;]]_W^_@@$_P4$^OP$_/T-"/O^__;Z!04)#?[V^_OY!0X)!0'X\O3["Q4) +M`@;WYO(("P\5`?/U[.\+&@T("O#E^``'$1,&]?3P]0,,%PK\^?7W^@@0"P/V +M]/;_"@4*"??R_@+]$!+T]@/S]1(2_P8!ZO8%`A`4^_3\[_$)#Q04]^?X_/$+ +M(0X#^>;G_@@)&Q;\Z^KT_`P5%`7S\>[T!0X4#/_S\?CY"1`*!OCT^/S]"!,$ +M]_W]^OX$"@C\]/T$_O\&`P'^_/\"`/T"`OX"`O[^_P#]_04%_?T%`_/[#0;] +M``7^]OC\"@_\_07_\_@'!@D(^_GY]OD"#1$*^^_Y^/4,%PS_^O'I]@0/&P[W +M]OCKZA(C"/T']=SP"PT4%0+V\.?V$1$($`#F[?D`%!8$!?K<\1@&_!X.W.@" +M^P08$P3[Y^<%"@44%_OAZ/T%"`\:"^OE\O\($1,&^//O\`(8$@`!^^_T``8/ +M$OOS__CR"!<$_0+X]/H("08+_/7V_@7^"@H$_.[\!@0`!!#^\O?^!?\"#@CW +M]0$`^@0.!_[[^_CY!0D&"0/[]_3Y"!,#`@_XXO4)!0H3"?KTZ_8."`D3!_#K +M^/D&$PL*`/+P]`4+$`GY_?;R^PH6!_W_]O7]!0L-!O/Y`O'\%PW[`@'P^`<` +M"1/^\OC]``$!"A+^[?T&^_\#!@H`\O@*`O,#$@?W^@?]]P`"!PD$]?L']_<* +M#`@$^O+]`?@'%`C_^/+X_P,+%0;T^?7P`P\-#@CSZ?O__`\7"?GR[?('#PD. +M#/7F[P$+#!`-^NWL^`$,&0\"\NGQ^@<1%PWX[^?S!0H3&0CL[?3P!!<4#@3O +MY_;[`QH4`O_QX_4+"@\5`?'R[O<-%`X&^?#R]0`1$`WV`0D-"07[ +M[N\`"PD-"?OT\?8%$`H%!/+L_08#"Q,!]/3X`@4'"0CZ[OT!_0@."/GV_`$! +M^P80`?'U`@0`!0T$]O7_`P(*!OCU^@0'!`0$_/#Z"@L&!/[S]/L&#P<``?OQ +M]@@."@3[^O;R_@\0!?_V]/G_"1`+_?3S]@0(!P\%\.S\!@4*"@7\[.\##@H& +M!?[T\/H,#04$`//T`08)!0$`^??]!P8!`OOZ_P`$!0/]_OST`@\"_`'^^_O_ +M"`P!^/W^^``)"`'Z^/S__`81`O/X`?O]"@@#_?7V``8#"`C]]OC]`0<(!/[W +M]OL"!0@)_O?Z^_X'#0#Z_O3U!`H'!0#V]O_^`PT'^OCZ^/T%!@D#]_;\`0(% +M!?_^^?@""`+\`/[Y_P<$``+^^?L`!@OU`P\3!OWX +M\O+\#`\*`/?Z^/@%"P8!`/KW_@$"!@,`__O[``(``0/__@#^___^`@0!_@(! +M^?L``@0%!`#[^/L``P<*`_CW^/L`!PP)__3V_?P!"PP"^/?Y_``%#0;W^?[[ +M_`<+`?KZ^P`!`0<%_/7^!/\`!`/]^/P"`P(#`OSY_@(``0,"_/C^`P4!_P(! +M^O8""P/^`/WY^OX#"@?\^_WY_04'!?_\^OP``08&_OS]_OT!!0(!_/D``?X" +M!@'Z_0#\``0"`/W^____`0`````0`!````````/P`____@``` +M``$!``9156YI=',````````````````````````````````````````!`@`& +M('-A;7!S```````````````````````````````````````````````````` +M````````````)P`O``__]O_Y``$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````!`````7@```!X````5@`````` +M````````````````````````````````````!$)E;&R!`@```$%)1D939#)A +M`````````````!*L`````$%)1D939#)A```````````````````````````` +M````ISVMY```1"(```'.``````````````````(E`/____X!,0`````````` +M``````````````````````````````````$````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````!H`*0`/`FH#,0`J``,";0,]`"D`#P)!`R(!`````!H` +M*0`/`5`!\0`J``,";0,]`````````````````!H`+``6`3X!W``J``,";0,] +M`````````````````!H``````````````````````````````````````0`` +M``%X````>````%8`<;B\'*8````<`%8``%=S=&$``P`*``'__P`````````` +M``+__P```!X```````/__P```#P```````3__P```%H````````````````` +I```````````````````````````````````````````````````````` +` +end diff --git a/sys/share/sounds/bugle.uu b/sys/share/sounds/bugle.uu new file mode 100644 index 0000000..57a1fa2 --- /dev/null +++ b/sys/share/sounds/bugle.uu @@ -0,0 +1,328 @@ +begin 644 Bugle +M``5"=6=L90`````````````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``!H`10```````#8B```! +MSJ<]K=:G/:W>``````````````````````````````"!@7%5``!&3U)-```V +M&D%)1D934TY$```T'@`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````/____\```$"`@("`@("`@("`0("`0```/_^_O\```$"`@,#`P0$!@D- +M#Q`0"P/Y[>+GCX>+G\/?\``(#!0<'!00$!`,"```!`P8'!P8%!`,#!0<( +M!P8%!`(!`0#__O[^_O\``@,$!PH,#`L+#`\2%1@:&1<5$Q`,!/CHU+^TM+6Y +MQ=/C\OP!`/W[^OCV]OC[``4+#`H&!`(``````@0%!PD*"@H)"`8$`P,#`P(" +M`0$!______\!`P4'"0L-#@X,"PH*#1$5&AT?("(>$?[CR+.HIZRUO\?.U=SF +M\?C\_OW[^??Y`@T4%A0/"08&!@<)"@D(!P4%!P@(!@0#`@("!`8("`8%!`," +M`0$!`@,$!08'!P<("@P.#A`3&2`F*"@E'Q'[XC("%F;37^0\9%PP!_/OX]?;]!@X1$`\,!P/_ +M_P``_OW_!`L0$A(0#0D&!`,#!0@)"`<&!`+^^OCW]O;Y_P4+#Q$1$`P)!P8' +M"Q$7&R(I+"85_=_"J)2*CZ"ZV?@.%Q8-`OKV]/'Q^`(+$144$`L$__W]_?S] +M``4*#A(4%1,/"@8"``$$!P@("`<%`O[[]_7T]OL`!0D-$!$/#`D'!PH/%!HA +M)RHH'@OPU;VGD(*(H\?M"AD<$P?\]_7S\?/[!`L.$`\-"P<"__[^_?X"!PP0 +M$Q46$PX)!0$``00&!P<'!@,`^_CV]/3W_`((#1`1$`X*"`<)#1(8'2(F)!L) +M\=C`JY>,E;#0[P<6&!`$^_;T\>_R^@,+#Q$2$0X)`P``__W^`08,$!06%A0/ +M"@8"```"!`8&!@4#`/WY]O3S]?H`!@P1$Q,1#@P+"PX1%1H>(1\7!_#4NJRH +MIZ>NPN']$!81!OWX]O3R\O@"#105$@P(!00#`@#^_P$&"P\3%A@7$PP&`?[_ +M`@0%!04$`P'^^_CW]_CZ_P0*#A$1$`X+"0D+#Q,8'2`A&PWXX'QL/^^/+MJ:;E9VZX0(6'!@. +M!?WY]?/S^``(#A$1#PT-#`H&`O_]_@,(#1$5%A83#@D%`@$"`@("`P0$`P'^ +M^O?U]??\`PH.$1,2#PT,#`T/$109'1\;#OKCRK2GGIB@NMX`%AX:$`;_^_?U +M]/?^!@P/#P\.#@T+!P+^^_P!!@L/$Q87%`\*!@,"`P,"`@("`@(!__SY]_?Y +M_0$'#1`2$@\,"PL-#Q(6&QX?'!'_Z=&[J)B,E+?B`Q@?&0\'`?WY]O;Y_P8* +M#0T-#@\."P?O^8'&AX8#@8`_?KW]?C^!`D,#0T-#@T*!0'^ +M_?X`!`@,$!(3$0X*!P4#`@$````!`@(!__W[^?GY_`$'"PX.#@T+"0D*#1$5 +M%QD9%P\![MG$LJ&5G\#H"1H=%PX&`/SX]O;Y_P4*#0T-#0X-"@4!_?O\_P0( +M#!`3$Q$."@<%`P$`__\``0(#`@#^^_GX^?P!!@H-#@T,"PD)"@P/$Q87&!'*LIV6K-;[ +M$1L9$@L%__OY^?G[_@$%"`H,#@\."03_^_K\``,&"@X1$0\-"@@&`P#^_?X` +M`0("`@#]^OCW^?P!!@D+#`P+"0@("0H-$!,4%10/!/3@R;"F;?C`Q08%`T(`_WZ^_S]_O\``0($!@H-#@P(`P$` +M```!!`@,#0X-#`P+"08#`/_^_O__````__SZ^?K]`04'"`D*"0D("`H,#Q(2 +M$1$/"O_OV<*JF:;/]@T8%Q`*!0#\^OO\_O\```$"!`<+#0P*!@,"`0$!`@4) +M#`T-#`P,"PD&`@#__O[^_O[___W[^?G\``0&"`D)"0D("`D+#A$2$A$/#`7Y +MZ-*ZI)RUWP`2&!0-!P+]^OO\_@````$!`@,&"@P*!P4#`P,#`@,&"@P-#`P, +M#`L)!0(`__[^_O[^_O[]^OG[_@(&!P@("`@("`@*#`\1$A(2$`L"]-_'K)BA +MR?$+&!@1"@4`^_K\_O\!`0$!`0(%"`H+"08$`P,#`P,%"`L,#`L+#`L*!P0" +M`/_______O[]_/GY_``$!P@)"0@("`@)"@T/$!$1$0T'^^K3NJ.=M^$!%!D3 +M#`8!_?KZ_/\!`@("`0$"!0@*"0<%!`0$!`,$!@D,#`P+"PP+"08$`@'____^ +M_?W]_/KY^OX#!P@)"0@(!P<("0P/$1$2$0\*`/'=Q:R:IL[U#AD7#P@#_OKY +M^_T!`P,"`@$!`@4'"`<%!`0%!00$!0@+#0P+"PL+"PD&!`,!___^_?W\^_KY +M^?P!!0@)"@D(!P<'"`H-$!$2$0\+!/KIU+F=F+CE!A<:$PL%__KX^?S_`@0$ +M`P$!`0,%!P8%!`0$!04$!`8)#`P*"@L+"PH'!00#`?_^_OW]_/OY^?K_`P<) +M"@H)!P<'!@@*#A`1$!`."/_QW<6GE:O:_A0;%@T'`?SY^/K]`00%!`(!`0($ +M!@8$`P,$!04%!08("@L*"0H+"PH(!@4$`@#^_OW\_/OY^/G\`04("@L*"`<& +M!@<)#1`1$`\-"@/WYM&TFJ'*\PX;&1`)`_SX]_G[_P,&!@,!`0$#!04$`P,$ +M!08&!@8'"0L+"@D*"PL)!P8&!0,`_O[]_/OY]_C[``0'"0L+"0<&!@8("PX0 +M$!`."P;\[-:YGI_"[`D8&A(*!/WX]OCZ_@(%!@0"`0(#!`0#`@("!`8&!@8& +M"`H+"@D*"@H)!P4%!@4!__[]_/OY^/CZ_@(&"`H+"PD'!@8'"@X0$1$/#`?_ +M\-S!I9RWX0(6&Q4-!O_Y]O?Y_``$!@8$`@("`P,"`0$"!`8'!P<&"`H+"PH) +M"@H)"`<&!P<$`?[]_/OY]_;Y_0$%!PD+"PD'!@8&"0P0$1`/#`@"]^3+JI:I +MUOL2'!@/"`'Z]O;X^_\#!08%`P("`@,"`0`!`P4'!P<&!PD+"@D)"0D)"`<& +M!P@&`P#__?OY]_;W^P`$!@@+#`L(!P8%!PL.$!$0#@D"^>G4M9F@RO,.'!L3 +M"P3\]O7W^OX"!0<&!`(!`0("```!`@4'!P<&!PD*"PH("`D("`<&!PD(!0'_ +M_?SZ^/;W^O\#!0<*#`P*"`8&!PH-#P\/#@H#^^W:OZ*;O.D)&AT6#0;]]_3V +M^?T!!0<(!@0"`0$!`/__`@0'"`<'!P@*"PH)"0D(!P8&!PD*!P,`_OW[^/;U +M^/T!!0<)#`P*"`8%!@D,#@\0#PP&__/BRJN8K=L`%AX9#P@!^?3U^/S_!`<( +M!P4#`0$!`/__`0,%!P<'!P@*#`L)"0@(!P8%!@D+"@8!__[\^O;T]OL`!`8( +M"@P,"PD'!@<+#0\0#@P(`/7DSK"7I-'[$QX<$PH#^O3S]OK^`@8("`8$`@$! +M`/[^``(%!P<'!P@*#`P*"`@(!@4%!0@+"P<#`/[]^O?T]?G^`@4'"@P-#`H( +M!@8)#`X/#PT)`_GKU[R@G+_M"QL>%0P%_?7R]/C]`04("0@%`@```/_^_P$$ +M!@<'!P<)"PP+"0@(!P8$!`<+#`H$`/_^_/GU]/?\`00&"0L,#`L)"`<)#`X/ +M#PX*!/OMV;ZAF+;F"1L?&`\(__?S\_?[``0("0D&`P$``/_^_@`$!@<'!P<( +M"PP+"0@(!P4$!`4)#`L&`O_^_/KV]/;[``,%"`H,#`L)"`<("@X0#PX*!?[R +MX3.M9R][0T=(!@0"0'X\_/U^?X"!@D*"0<#`?_]_/W_`@8'!P<'"0L, +M"PD(!P4$`P,%"0P-"@4!_OW[^/?Y_@(%!04'"0D)"0D)"PP.#Q`."0+WZ=6] +MHINYYPD;(!D0"@+Y]/+T^/T!!0@*"0<$`?_]_/S_`@4'"`<'"`L,#`H(!P8$ +M`P,%"`P-"P8"__W[^/?Y_@,%!08("@H)"0D)"@L-#Q`/#`7Z[-G!I9BPX`4: +M(1H2"P3Z]/+T^/P!!0@*"0@%`@#^_/S^`@4'"`<'"`L,#`H(!P8$`P,$!PL- +M#`@#__[\^??X_0$$!04'"0H)"0D)"PP.#P\."P3Y[-O%JYNLV/\6(!P3#0;] +M]O/S]OL`!`<)"@@%`@#^_/S^`04'"`@'"`H,#0L)!P8$`P,#!@H-#`D$`/[\ +M^??W^P`$!04'"0D)"0D)"@P-#A`/#`?]\-_)K9BDT/H4(!T4#@?^]O+R]?K_ +M`P<)"@@&`P#^_/O]`00'"`@'"`H,#`L)!P8%`P,#!0H-#0D%`?_]^O?W^@`# +M!04'"`D)"0D)"@L-#A`0#@C_\^+-LIJ?Q_01'Q\6#PC_]_+R]?G^`@8)"@D' +M!`'^_/O]``,&"`@'!PD,#0P)!P8%`P("!0D-#0H&`O_]^_CV^?X#!@8&"`D) +M"`@("0H,#A`1#@D!]N?3N9V8N^L,'2`8$`H"^?3S]?G^`@8("0D'!`'__/O] +M``,&!P<'!PD+#0P)!P8%`P("!`@,#0L&`O_]^_CV^/T"!08&"`H*"0@("0H, +M#A`1#PH"^.G6O:*9MN0'&R`9$0L#^O3R]/C]`04("0D(!0+__?O]_P(%!P@' +M!PD+#0T+"`8%!`,"`P@,#@L'`P#^^_CV]_P"!08&"`D)"0@("`D+#0\1$`P& +M_.['Q8."/_X]/3W^P`$!@@)!P0"`/_^_?\! +M`P8'!P<'"0P-"P@&!00#`@(&"@T,"`0!__WZ]_;Y_@,&!@<)"@D)"`@)"@T/ +M$!`."0'VY]&VG9S`ZPH<'Q%0T(`/GU]??[_P,'"0D'`P$`__[^_P$$ +M!@<'!P@)#`T*!@4$`P,#!`<+#0P(!`'__?KV]?G^`@4&"`L,"PD(!P<("PX/ +M$`X+!?OMV;^DG+CE!QH?%P\)`OKU]/;Z_P,&"0H(!`$`__[^_@`#!@<'!P<) +M#`T+!P4$!`,#`P8*#@T)!0'__?GV]?C]`@0&!PH+"@D'!04&"0T0$1`-"`/Y +MZ=.TEYS)]Q(>'!,+!?WV]/7Y_0$%!PD(!0(`___^_O\!!`8'!@<("PT,"`4$ +M`P("`P4)#0T*!@(`__SX]?;Z``,%!@@*"@D(!P8'"0P/#P\-"@3\[=:XF9?! +M\0\='A0,!P#X]/3W^P`$!PD(!@(``/_^_O\!!`8'!P<'"@P,"04$`P("`P0( +M#`T+!P,`_OSX]?7Y_@($!@D+"PH)!P8&!PH-#P\-"@;_\^#'J)BPWP,8'Q@/ +M"0+Z]//V^P`$!PD)!@,!`/___OX``@4'!P8&"`P-"P<%!`,#`P0'"PT,"`0! +M__WZ]O7W_``#!0<)"PH)!P<&!PD,#@\/#0@#^>G2LI:?SOD3'QL1"@7]]_3V +M^?X"!0@)!P0"````__X``@0&!P8'"`H-#`@%!`,#`P0%"0T,"04!`/[\^?7V +M^P`#!`8)"PL*"`<&!@@+#0T-#0L&_N[8NIJ:PO`.'AX4#`<`^/3U^/P`!`<) +M"`4"````__[_`00'"`<'"`H,#`D&!`,#`P0%"`P-"@8"`/_\^?7U^?X"`P4( +M"PL*"0@'!P@+#0T-#0L'`//?Q:.5L^0&&A\8#@D"^?3T]_L`!`<)"`4#`0`` +M__[_``,&!P<&!@@+#`H'!00#`P0%"`P.#`<#`/_]^O;U^/T!`P4'"0L*"`<' +M!@<*#`X.#@P(`_CFS:J3IM;_%A\:$0H#_/;T]OK^`@8("`8$`0``__[^``,% +M!P<&!@<*#`H'!00#`P0$!@D-#`@$`?_]^_?U]_P``P0%"`H*"0@'!P<)#0X. +M#0L(`_KKU+:;H,GT$!T<$PP&_O?T]?C]`04("0@%`@``__W]_P$%!P<'!@<* +M#`P)!00#`@,$!0D,#0H%`@#^_/CV]OK_`0,%"`H+"@@'!@<(#`X.#@T*!O_S +MW\.BEK?G"!L?%@X(`?GT]/?[_P,'"0@&`P$``/[]_@`$!@<'!@<)"PP)!@0$ +M`P,$!0@,#@L'`P#__?KW]OG^`0,%!PD*"@@'!@8'"@X0$`X+!@'VY'A0, +M!?WW]/7X_0$%"`D(!@(``/_]_?\!!0<'!@8'"@P,"08$`P(#!`4)#0X+!@(` +M__SX]?;Z_@(#!0@*"PD(!P8&"`L.#PX-"P@!]>'$H9.TY0<:'Q<."`'Y]/3W +M^_\#!PD)!@,!``#^_?X!`P8'!P<'"0L,"@8$`P,#`P0'#`X+!@(`__WY]O7X +M_0$#!`<*"PH(!P8&!PH-#P\-"P<"^>G1L)>ET_P4'QL2"@/[]?+U^?X"!@D) +M!P0!``#__?X``P4'!P8&"`L,"P<%!`,#!`0&"@T,"`,`__W[]_7W^_\"!`8) +M"@H)!P<&!PD-#@X.#0H'__#:O)R8O^X,'1\5#`7^]_/T^/P!!0@)"`4"```` +M_O[_`00&!P8&!PD,#`D%!`,#`P0&"0P-"@4"`/[\^?;V^?X!`P4("@H)"`<& +M!@@+#@X.#0L)`_;BQJ.3K^(%&2`9#P@!^O3T]_O_`P<)"0<#`0``__[_`0,& +M!P<&!P@+#`H&!`0#`P0%!PL-"P8"`/_\^?;U^/T``@0("@L*"0<&!@<+#@\- +M#`H(!/OJT;"5I-+[$Q\<$0H#^_7S]?K^`@8)"0<$`0``__[^``(%!P<&!@@* +M#`H'!00#`P0%!PL-#`@#`/_]^_CV]_O_`0,&"0L+"0@'!@<)#`X-#0T+!__P +MVKZ?F;WL"QT?%@T&__CS]/?\`04("0@&`@``__[^_P($!@<'!@<)#`P)!@0# +M`P0%!@D,#0H%`0#^_/GW]_K^`0,%"`H*"@D(!P8(#`X.#0T+"`'TX,6EE*_A +M!1D@&0\(`?KT]/;Z_P0'"0@&!`$``/[]_@$#!@<'!@8("PP)!@4$`P,$!0<+ +M#0L&`@#^_/KW]OC\``($!PH+"@D(!P8'"@T.#@X,"@7ZZ=&QEJ'/^!(@'1(* +M`_SU\_7Y_@(&"0D'!0$``/_]_@`#!0<'!@8'"@P+!P4$`P,$!0<*#0T(!`#^ +M_?OX]O?[_P$#!@@*"@D(!P8&"0P.#@X."P;^[MB[FYC`[PX?(!8,!O_X\_3X +M_``%"`D(!@(```#^_O\!!`8'!@8'"0L+"`8%!`,$!08(#`T*!0'__OSY]_?Z +M_@$#!0@*"PH)"`<&"0T/#@T,"@G0L)6AT/L4(!T3#`7]]?+T^/P!!0@)"`8"``#__?W_`@4' +M!P<&!PD+"P@%!`,#!`0&"`P-"@0`_OW[^/;W^_\"`P4("@H*"0@'!PD-#@X- +M#`D$_.[8NIR=Q?$-'A\5#0;_]_+S]_L`!`@)"0<$`?___?S^``,&"`<&!PD+ +M#`D&!00#!`0%!PL-"P8"__[\^??W^OX!`P0'"0H*"0<'!PD,#@X.#@L&`/+= +MP:&7N.@(&R`8#PD"^?/S]OK^`P<)"0@%`?___?S]``,&"`@'!P@*#`H'!00# +M`P0$!@H-#0D$`/_]^OCV^?T!`P0&"0H*"0@'!@<*#0\/#PP'`?;DRJ>3K-X" +M&"$<$0H#^O3R]?G]`@8)"@@&`@#__OS]_P(&"`@'!@@*#`L'!00#`P,$!0D- +M#@H%`?_]^_?V^/P``@,%"`H*"0@(!P<*#A`/#@L&`OKKU+.8HL_Y$Q\=%`P% +M_?7R]/C\`04("@D'`P#__OS\_@$$!P@'!P<)"PP)!@4$`P,$!0@,#@L&`O_] +M_/GV]_K^`0,%!PD)"0D("`@*#0X.#@T*!?WMU;66F\GU$!\>%0T'__?R\_?[ +M``0'"0H(!`'__OS\_@$$!P@'!@8("PP)!P4$`P,#`P8*#@T'`O_^_/GV]OK^ +M`0,$!PD*"0D("`<("PT.#@T*!O[QW+^>F;[L"QT@%P\(`/?R\O7Z_P,'"0H( +M!@+__OS[_?\#!@@'!@<("PP*!P4$`P,#`P4*#0T)!`#^_?KV]?C^`0,$!PD* +M"@D("`@)"PX.#@T+!O[QW+Z=E[OJ"AP@&!`*`OGS\O3Y_@(&"`H)!@(`_OS[ +M_/X"!@@(!P<("@L*"`8%!`,#`P4)#0X*!0'^_?OW]?C]`0,$!@D*"@D)"`@( +M"@P-#@X+!O_TXLBHF;/@`Q@@&A(+!/KT\O3X_@(%"`H)!P0`__W[_/X!!0@( +M!P8("@P+"`8%!`,#`P0(#`X+!@+__OOX]OC]`0,$!0<)"0D)"`@)"PP-#@X, +M"`'TXL>GFK/@`Q@?&A(,!?SU\O3X_`$%!PD)!P0!__W[^_T`!`<(!P8'"0L+ +M"0<%!`,"`@,&#`X,"`,`_OSY]_C\``,$!0<)"0D)"`@("0P.#@X,!P#UY +M&Q(,!_[W]/3W^P`$!@@)"`4"`/W[^_S_`P8(!P8&"`H+"0<%!`,"`0($"`P, +M"04!__WZ^/?Z_P($!`4'"`@(!P<'"`D+#`T,"`/Z[=J]I*K/]@X<'!,-!P#X +M]/3V^_\#!0<("`8"`/W[^_S_`@8'!P8&"`H+"0<%!0,"`0$#!PL,"@8"`/[[ +M^/?Z_@($!`4'"`@("`<'"`D+#0X-"0+Z[=O!IZG-]`P;'!,-"`'Y]/3V^OX" +M!0<)"`8#`/[\^_S^`04'!P8&!PH+"@@&!0,"`0`"!@H,"@<#`/[[^/?Y_@($ +M!`4&"`@'!P<'"`D*#`T,"07^\N+)K*?&[@D8'!4-"`+Z]?3V^OX"!`8("`<# +M`?_\^_S^``0&!@8&!PD+"@@&!0,"`0`!!0H,"@<#`?_\^??X_0$$!`0&!P<' +M!P<'!P@)"PP,"@7_]>7-KZ;#ZP87&A,-"0/\]O3V^?T!!`8("`8#`?_]^_S^ +M``,&!@4%!@@*"@@&!`,!```!!0D+"0<$`?_]^O?X_``#!`0%!P<'!P<&!P@) +M"@L+"07_]>;1MZW#Z`04&10-"03\]O7V^?T!`P4&!P8#`?_]^_S]``,%!@4% +M!@@*"@@&!00"`0```P@*"0<$`?_]^OCY_``#!`0%!@<'!P<&!@@*"@P,"03_ +M]^K6O:Z_Y`$2&14."@7]]_7V^?P``P4&!P8$`?_]_/S]_P,%!04%!@<)"@@& +M!`0"`0```P<*"@<$`@#^^_GX^_\#!`0%!@<'!@8&!@@)"@L+"@S9P*^] +MX/X0&!4."@7_^?;V^/P``P4&!P8$`@#^_/S]``($!@4%!0<)"0@&!`0"`0`` +M`@8)"0<%`@#^_/GX^_\"!`0%!@8&!@8&!@<("0H+"@@#^^_?Q[*XVOH-%A8/ +M"@8`^O;V^/O_`P0&!P8$`@#^_/S]_P($!04$!08("0@&!`0#`?__`04)"0<% +M`@#^_/GX^OX"!`0$!@<'!@8%!08("0H*"08!^O#BS+>[VO@+%A8/"08`^O?V +M^/S_`@0%!@8%`@#^_?S]_P$#!04$!`8("0@&!`0#`0#_`00("0<%`P'__/KY +M^OX"`P0$!08&!@8%!08'"`D*"@@#_?/FT;RZU/,'$Q4/"@^T.T##Q00"@<#_OGW^/O^`0,$!08%`@#__OW]_@`" +M!`0$!`4&"`@&!`,#`@$```(&"`<%`P$`_OSZ^OT``@,#!`4&!04%!`4&!P@) +M"0<$`?KQXLV^RNC_#1,1"P<#_OKX^/O]`0,$!04%`P'__OW]_@`"`P0$!`0& +M!P@&!`,#`@'__P$%"`<%`P(!__SZ^OP``@,#!`0%!@4%!04%!@<("`<$`/GP +MX]#"S>C^#!(0"P<$__KX^/K]``($!`4%`P'__OW]_@`!`P0$`P0%!P@'!0,# +M`@#__P$$!P<%`P$!__WZ^OS_`0,#!`4%!04%!00%!@<'"`<%`OWTY]7$RN/Z +M"1$0"P<$__OX^/K]``(#!`4%`P'__OW]_O\!`@,#`P,%!@<&!0,"`@$`_P`# +M!@<%`P(!__W[^OO^`0,#`P0%!04%!`0$!08'"`<%`OWUZMC'RN+Y"!`0"P<% +M`/SY^?K]``(#!`0$`P$`__[]_O\!`@,#`P,$!@<&!0,"`@$`_P`#!@<%`P$! +M`/[\^OO_`0(#`P0%!04$!`0$!08&!P<%`?WVZ]S-SN'W!@X/"P<%`?SY^?K] +M_P$#!`0$`P$`__[]_O\!`@,#`P,$!08&!0,"`@$`_P`"!0<%`P$``/[\^_S^ +M`0(#`P0$!04%!`0$!04&!@8$`O[Y\.+2S=WS`PP/#`@%`?WZ^OO\_P$"`P0$ +M`P(`__[^_O\``@,#`P,$!08&!0,#`@$`__\"!08&!`(``/_]^_O]``("`@,$ +M!`0$!`0$!`4&!P8%`O[X\./4S]_T`PP."P<%`O[[^OK\_@$"`P0$`P(`__[^ +M_O\``0(#`P,#!`4&!0,"`@$`__\!!`8%!`(!`/_]^_S^``$"`@,$!`0$!`,# +M!`4%!@8%`O_[\^C:T=OP_PD-"P<%`O_[^OK\_@`"`P,$`P(`__[]_O\``0(# +M`P(#!`4&!0,"`@$`__\!!`8%!`(!`/_]_/O]_P$"`@(#!`0$!`0$!`0$!08% +M`P#\].G;T]SP_P@-"P<%`O_\^OO\_@`!`@,#`P(`__[^_O\``0(#`P(#`P0% +M!0,"`@$`__\!`P4%!`(!`/_^_/S]_P$"`@(#!`0$`P,#`P,$!04%`P']]^_A +MU=KM_`8,"P<%`__\^_O\_@`!`@,#`P(!__[^_O__`0("`@("`P0%!`,"`@$` +M`/\``@0%!`(!`/_^_?S]_P$"`@(#`P,#`P,#`P0$!04$`P'^^?'EV]SK^@4* +M"P<%`P#]^_O\_O\!`@,#`P(!`/_^_O__``$"`@("`P0%!`,"`@$``/\``@0% +M!`(!``#__?S]_@`!`@("`P,#`P,"`@,$!`4%`P'_^_3JWMOH^`,)"P@%`P#] +M_/O\_?\!`@(#`P(!`/_^_O__``$"`@("`@,$!`,"`0$``/\``0,$`P(!``#_ +M_?S\_@`!`0("`P,#`P("`@,#!`0$`P'__/;NXMWG]@$'"@@%`P'^_/S\_?\` +M`0("`P(!`/_^_O[_``$!`@("`@,$!`,"`0$``/__`0,$`P(!``#__OS\_@`! +M`0$"`@,#`@("`@,#`P0$`P(`_?CPYN#F]/\&"0@%`P'__?S\_?\``0("`@(! +M`/___O__```!`@(!`@(#!`,"`0$``/__`0(#`P(!``#__OW]_O\!`0$"`@(" +M`@("`@(#`P,#`P(`_?GRZN/G]/\%"`<%`P'__?S\_?X``0$"`@(!`/___O__ +M```!`0$!`0(#`P,"`0$``/__``(#`P(!``#__OW]_O\``0$!`@("`@("`@(" +M`P,#`P(!__OU[N;G\OT#!P<%`P(`_OW]_?X``0$"`@(!``#_____```!`0$! +M`0("`P,"`0$!``#_``$#`P(!``#___[]_O\``0$!`0("`@(!`0("`@(#`P(! +M__WX\>GH\/L"!@<%`P(`_OW]_?[_``$!`@$!``#______P`!`0$!`0$"`P(! +M`0$```#_``$"`@(!````__[]_O\```$!`0("`@(!`0$"`@("`@$!__WZ]>[K +M\/D`!`8%`P(`_OW]_?[_``$!`0$!``#______P```0$!`0$!`@("`0$```#_ +M``$"`@(!````__[^_O\````!`0$!`0$!`0$!`@("`@(!`/[[]_'M\/G_`P4$ +M`P(`__[]_O[_```!`0$!``#______P````$!`0$!`@(!`0$````````!`@(! +M````___^_O\``````0$!`0$!`0$!`0$"`@$!`/_]^?3P\OC^`@0$`P(!__[^ +M_O[_```!`0$!``#______P````$!```!`0$!`0`````````!`0$!````___^ +M_O__``````$!`0$!`0$!`0$!`0$!`/_^^_?S\O?]`0,$`@$!`/_^_O[__P`` +M`0$!``#______P`````````!`0$!`0```````````0$!````_____O\````` +M```!`0$!`````0$!`0$!``#^_?KV]/?\``(#`@$!`/_^_O[__P`````````` +M______\``````````0$!`````````````0$!`````/______```````````` +M``````$!`0$!``#__OSX]_C\_P$"`@$``/_______P``````````______\` +M`````````````````````````````````/______```````````````````` +M``````#__OW\^OK\_P`!`0$``/_______P``````````________```````` +M`````````````````````````/_______P`````````````````````````` +M___^_/S]_P```0````#_______\``````````/______```````````````` +M`````````````````/_______P``````````````````````````________ +M_P````````#_______\```````````#__P`````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````!#3TU-````$@`! +M```T%@`(0`VL1````````$U!4DL````"``!)3E-4````%#P``'\`?P`````` +M````````````05!03````:A39#)A``(````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`I`````````````````````````````````````````````````````````` +M```````````````````````$"```````"0`C`````````````````'@````$ +M``0```````#\`/___X`````!`0`&455N:71S```````````````````````` +M`````````````````0(`!B!S86UP````%8``````%;^`/___X`````!`2`&455N:71S```3""YP9P5" +M=6=L90(```!!249&4V0R80`````````````2JP````!!249&4V0R80`````` +M`````````````````````````*<]K=8``#8B```!S@```!T`+P`/__;_^0`! +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````:`"D`#P)J`S$`*@`#`FT# +M/0`I``\"00,B`0`````:`"D`#P%0`?$`*@`#`FT#/0`````````````````: +M`"P`%@$^`=P`*@`#`FT#/0`````````````````:```````````````````` +M``````````````````$````!>````'@```!6`'&XO!RF````'`!6``!7```````#__\````\```````$__\` +M``!:```````````````````````````````````````````````````````` +,```````````````` +` +end diff --git a/sys/share/sounds/erthdrum.uu b/sys/share/sounds/erthdrum.uu new file mode 100644 index 0000000..c95e8bc --- /dev/null +++ b/sys/share/sounds/erthdrum.uu @@ -0,0 +1,863 @@ +begin 644 Drum_Of_Earthquake +M`!)$CK[>_P\O3V^/K\_@`"`P4("PT.#A`3%A<7%A@<(B8G)B0E*2XQ+RHG*"XU +M.SLW,2XR.T-&0SLS,39`2U-434`Q)2,K/$U96E%`+!H-!@P>.UMQSGX=S7TLS'PKZZM[6TM;:Y +MO<''SM?AZ_<#$!PI-$!*5%QE;&]O<'!O;FYN;6YM;FUL;6UN;FYO;W!P;VUJ +M9V1?6U=44U-55EA:7%UA9FQQ='1R<&UJ:&AI:FQN<')T=7-Q;V]N;FQH8EU8 +M5E-03$E&0T`^.SHX-S4U-#0T-34U-C8V-C4T,S$N*R@E(AX:%A(."04`_/CW +M]_;U]?7U]?7U]?7U]/3S\O+Q\/#O[^[N[>WL[.OJZ>CGYN7CXN#?W=S;VMG7 +MU=/1T='0SLG#P+Z[N;BWM[>WM[>WM[>WMK:UL["LJ*6BGYV;FIJ;FYN9EY:6 +MF)N>H:*AGYZ;L[_+S]?;W^/CY +M^/?U\_/T]_P`!`<*"PL+"PL+"PL,#`L)!@+__?O\``8&5D5T8X-3@W+1T2#@\-!__\_P8-$!`+`?3JYN?L\O?\`0<-$A89 +M'2`C*#`\3%ME:FYP;VMD5T8U*R@D&Q`)!0+]^/;RZ^'6RL"[NL#(SQJJ2@G)B8F)>0AX&`A(F. +ME9VCIJ*9CXB*E:2QN\/+T=+3UM[DY^7CYN[Z!A`7'B0J*R3EZ.OL[.OM\_H``P0$ +M`P'__P(&"@X/#0D%`@($"`T0$Q,1#0H'!00$!`,!__WZ]>_HY.+BY>KR^@0. +M&2$C(!H3#`<$`P(#!0D-$A<;'A\?'AT9%!`,"0@*#A0:'B`>&!$+!@/_^O3K +MX=C0R,*^O+NZNKF[OL+(S=/7VMW?X-_/BX>'A +MX>'BX^3DY.#:U-7>Z.GDX-_7CX^3CW=?2U=G:T\G"O\'*V.;O\.K@ +MUM7B]0`#`O_^_/S[^OKY]O/O[.GFX^'>V]C6T\['O[>PK*NNL;2VM[BYO+_# +MQ\S/T]78V^'I\?;W]O#FW-O@Y.'>V]?5U-76U]C8V=O@Z//]!`4#`PH4'2$B +M)2@K*RHH(QD,`/;R\O?_!0L0$Q46%A44$A`/#@X.#Q`/#@L'`_[X]/#N[>_T +M_0D6(2@K*24B'AL9&1D;'!P;&184%!05%186%A45%!,1$`X,"PL+#`T.#P\0 +M$1(3$0P#^.SDX.#@X>3G[//^"QHG,#0T,2XM+B\O+BPI)R8E)BKL[O#O[.?AWM[BZO/\`P@*"04!_?T##QH>&A00#A`1$`L&`O_\ +M^?7QZ=_6TM+5V^/P`1`;'A@-_O+L[.[P[^ODWMK9V=WD[OH$"@P,"PH("`L0 +M%!01#@D%`@("`?[\_/S]_?X!!@L2&!P>'1L7$0P("0P/#@L(`_[Z^_\$!@0! +M_OOW\>GCWMW=WMWKS_`0)"@<# +M_OGS[>SP]/;U]/;Z_P$!``($!`#\^?7R\/'U^P$'"PT,"`+[].[JZ>ON[O#W +M`PX3$Q8=)"HL+"PO-CHY-C@^0D`\.CP]-B@:#P?_^?C[_/COY^;K]/X$`?CR +M]/GZ^?L`!`/]]N[FW-#'Q,K0S\G#P\;*S,W0UM[DZ.KN]?X'$APE*2PN+BTI +M(A<-"0H+!OWV\>OEXMW4RL3%RL[.R<&^P\K+P[FWO\G.RL?+UN+J[?'V^?;O +MY=G,OK"DG)>8G*&FJ:VSN\3,SL[-T-?>XN+CY>CIZ>GJ[?'U^P`%"@P.#P\/ +M#Q`3%A40"P\8(B@J*RTQ-SLY,S`V1%%64TD[+R8C(R8J+"PJ)2`;&1@8&1D7 +M%A,1#P\/$!`0#PX.#Q(6&AT='!XD*2HI)2(?&Q@5%!,3$A`.#`H(!@0#`/WZ +M^/;T\>K>T<2]N;.JH9N:G:*EI:2DI:BJIY^6DY:;GIZ(!T6#P<`^?;U\^WC +MVM75V>#I]``)#@T+#!`1#@8`_P'_^O3P\?;_"A09&AH:&1<6%101#@L)"`H- +M#0L(!0(`__[^``(#`__[]_7U]O?Y^OOY]._L[.[Q\O#IW];3VN7N]/CZ]^_H +MY>CM\/#KY-_?Y.GM[_#U_04+#A$5'"0I*B&QD8&1D8%A$* +M`_SV\O'R\_/R\.[O\O7Z_P4,%!XJ.45+2D0^.SHX-#`N+S$P+"(5"@0? +M(R0F+#(S+B(5"P@)#`X1$`T*"@\7(2DM*RS[#AXI +M+2LC&1`.$!(4$0P%_OO[_O_]^?;W^/;MX=G6V-O>W]S;W>3O^/X!`P0#`O_Z +M]/+V_PH3%A00#@\3%QD:'!X?'1P;'!T='!H6$`?]\^SFY.3FZ_'U]_?W^/K^ +M`@8+$18:'!TA*35!1T8],28A(B&QP<&QD7&R(G)!L0"08'#!(9'AX8#@4```$!__OY +M^/GZ^??W^/O_`P@0&2(G*2HK*B4>&!88'!\C)B@H)B,@'AL9%0X$_/CW^/O^ +M``#\]O+T]_?SZN#9U]C>Z/+X^OO\`0H5'R0B&0\'!08'!0#[]O/S]_\+%R$I +M+"LE'!$%^_;X``P7("8H*"8A&Q<4#PC^]>[IYN+@X.3J[O#Q\O/U]_?V]_K_ +M!`@*#`P+#`X3&1T=&Q81#0L+#`\1$Q(1#Q$7'1X:$PT)!@4&"`L.$1,3$Q49 +M("P +MK*FEHJ*FKK.QJ*&@IZZQKJFGK+2ZO\'#P\+#Q[T^@`#`OSU[NCDY>KR^P('"PX1$A$."@7_^/+LZ.;E +MY.3DYNKO]OT$"0P.#0H&`@#]^?/JX-;.Q\&\NKFYN+FYM[2OK:^TN;W`PL;, +MTM;5TLW,S,W,RLG*S,[1T]+,Q+NSKZ^QM+6UL["MJJ>CHJ.FJZ^RM;J_QLS1 +MU-;8W-_?V]+(P+V^Q,S3V-K:V=K=X>7J\/C_!0L4'RHS.3Y%35)444M$.S0N +M*20?'!H7$`?\\^_P]/GZ]_3S]OL``/[[^?O\^O3KX]W;WN;Q_`@3&QT:%!`1 +M%1LA)BDK*RDD'!$'`/T`!PP,"`0#`P0$!P\9(R@G(AT;'"`A'QL7$PX(`OS[ +M_@($__?PZ^KL[_+V^?K[_0,-&28O,S0S,S,U-34T,C$N*R +MW-O;WN3M]P`'"PH'`?W]`@@.$`\-#`P-#`D%`?[[^_X!!0<(!@#W[NKL\O?X +M]_;X^_\!!0L4'RCDXN'?W=O9V-?8VMWBYNGK[_7_"Q@B*2TN+2TM+2TL*2,=&!45 +M%A<<(RPR,BTF(1\?'AP7$@T(!`#]_/KY^/CW]O;V^/K]__[Y\>KDXN+AW]W< +MW-O;W-_EZNKHYNCO^@81&1P;%0X&`/W[^OOZ^OCV\N[LZ^OJZ.?HZNWR]_O\ +M^_CU]?;Y_@0*#1`2%!<6$PX)!0(`__\``0(%"0T1%!<:'!\D*"TO,3(S-38V +M-30S-CQ$351:7V)B8%Q74DU'0#8L(QP8%Q@<'R`?'1L9&1PB*"PK*"4C)"8H +M+#`V/$%%1TA)2DI)14`\.ST].3`E'1H<("0C'ACM\._LZ.;EY.3CX^3GZ^WMZ^OP^`$%!0'[]?/S^/X% +M"P\3%QL>(2(C)28G*"@F(AL2"/_Y]?/Q[^WL[O'V_``"`P0(#`X-"`'Y\_+T +M^/S_`/[Y]/'P\O/S\.KBV=+.R\K)R,?(R\_4V=W?W][;UM35W.7L[>GAW-O? +MY.CIZNOM[^_KY=[8U=+,P[JWN+W&T=SCY-_8TM'7W^?L\?;Z_?[___[Z].WI +MZ.?FY.3FYN+8S<*[M[:XNKV_P<+!O[V\O+R[M["II:6GJJRNK["PL+&SMKJ] +MO\+$Q<7%Q<;*T-;OKY]_6T=+7 +MX.GP\_+R]/?Z_/W^_P(#`OWU[.;DY.+>VM?6UM;5U-+/SKJ[._Q +M\>_N[.SM\//U]?/NZ>7BX-S5RKVQJ**=FYJ;G9^?GZ*KM;JYM+"RN+_#P\#` +MP\G0U=KAZ?#R\.WN]/L``?[Y]/#N\//X_/\!`@0)#A`-!P'Z\^WHY.#=V=71 +MSLS+S,O'P;_!QLK+R<7"P<'"PL3)TMOBZ.SR^?X!`P,#`/W[^??V]?3R[^KE +MX-_?X.'AX-_=V]C5U-?=Y.GN\O;X^?GX^/K^`P4"^O/P\O;Y^OT!"`P-"PD) +M"@H)!@0"`P@1&B`A'!4."@8#_OKW]OCZ_/[_``$!`0("__GPY][7T];=Y^[R +M\O'O[.CFY^KL[N_N[>[O\?+S\_/S\_/S]?7R[>?BWMW>W=C3SLS+R\G'QL7& +MR,O/T]?:W-[AY.CL[N[MZNCGZ.ON[^[LZ>?DXN'AXN3EY^KM\?7X^_X``0$# +M!@D,$!,5%QLB*C`T,S$R-CHZ-S0S-3H^0#\]/D)'2TY/45)24$Q(1DA*3$M' +M/S65545%)/3$I-5%YH;W)R<&MH9VAK;7!Q#=W-W>WM[?X^KN\?/V^P`"`?SY +M^OT``P+__?O\_?W]^_CV]?3T]/?[`0<-#PT&_O?R[NKIZNWP\O+P[NWL[O'U +M^?S^``0(#!`3%1D=("(B(1\;%Q$+!@+_^_;PZ>/AX^CN\_?Y_/X!!`<)"PP- +M#`D$_?CT\>_N[^_O[NSKZ>CGY^?FY./CX^'#AWMC2SLS+R\W2 +MV-SCEXN#@W][V +MM[N_PL3%QL;&Q<"\NKN^O[Z[NKJ[OL+&R]#6W>/I[O'T]_K^`0(!_OW^`@0# +M__OX]O7R[>;>U]35VN#FZ>KGX^#=W=[@X^?JZ>?CX-W9U='-S,O*R,;#P<"_ +MP,+%RM'7V]S=W=W=W=S=WN'CY./@WMO:V=O>X^?GY.'AXN'>VMG;WN#?VM74 +MUMKXN;HY^7DY.3DXN#?WM[=W=S;VMK;W>#BX>#?WM[>WM[>X./GZN[R +M]?;U\_3V^OW__OW[^/CY_0$%"0P/#P\/#@\/#Q$1$0X+"`8%!08&!@4$`@#] +M^??U\_#LY=_7T,K&PKZXL:NHIJ6EIZNTO\W9X.+AX.+FZ>CCW=C6U=73T]37 +MV=G7UM?:W^/GZ>SP\_;W]_7T\_/S\O#MZNGJ[.[N[>OJZNSM[>SL[>WM[>OI +MY^3BW]W;VMK;V]O9U];7V=O>W^'CYNCIZ>KM[_'R\_3U]?+N[.SP]?K_`P4# +M`?[\^_GU[^C@V=31S&A82#PX-#`D%`/W\_@(%!P4" +M_OS[_0`%"Q(:("(@'1@5$0X)!@0"`/SX]/+R]/?Z^_S_`@4%`OWW]/+T]_O] +M_/GW]_GZ^??U]//Q[>CDX^/CXM_;U]75V-SAZ?#W_``"!0<("`<&!@D-#P\- +M"@D+$!@='QX='2`B(R(A(B4K,#0V-C8U,S$N+"PM,38\0D9)2$5`.C0N*RHI +M)R,>&QL=("$A(B0H*RXO,#$S-SH\/#HW,S`K)R$;%A(1$A48&AXB)2@H)R4D +M(B$@'Q\@'QT9%`\,"08%!08("`@("0H*"0D*"@@"^O+N[_/Y_P,$`P(!`@,% +M!PL0%1@8%A,1$1(4&!TB)BHN-3Q"2$Q-2T=$0D`_/3LZ.3H\/T1(2TQ)14`\ +M.CL]0$)#1$9&0SPT+2DH*2HH(QT9&1XF+S8\/CX]/#HW-3,U.#Q`1$E/4U=7 +M5$])0CLV,S(S,S,Q,"XO,C<\/C\^/C]#1TQ26%YA8%U955-24E%/2TA&145$ +M03TZ.#;BX-[>WM[>WM[?X>3FY>/@W=W=W^'DYN;FY>;FY^?FY.'@X.'D +MZ.KL[.OJZ>OM\//U]O;W^/K[_/OY]O+O[.KHY^;CX-W;V=?5U-78VMO:V-;4 +MTM+3U-75U-+1T,_.SM#3V-O>W][>W^'E[//X_/W^_P$$"A$7'2`B)"4F)B4B +M'AL8%Q83#@<`^O;S\_3W^_W^^_CU\_/T^/X%#!`/#`<"`/_^_P$#!0<&`P#\ +M^OK\_P$"`@("`@(`_/?QZ^7@W-G8UM74U=?:W-W;U]/1T=/3TL_-SM'4U]G; +MW=_AX=_=W-O:V=;3T,S)Q\?(R\_3UM?6T]#-S<_2UM?5TWM[F]PL?*S,S-T-;>YN[U^_\!_O?PZN?GZ>KH +MY>'>W=_CZ.[T^/KZ^?CX^OO\^_KX]?'LZ.3BX>#?WM[?X-_>V];/R=I*NQM;>XNKN^P<7+ +MTMG?Y.;FX^#+FZN_S^/S_`/_\^?GZ_?\` +M`0$!`0$#!0@+#0X-#`L*"@<"_/7Q\//W_``"!`8*#Q4:'B(G+#$U.#DX-S0Q +M+BLI)R0A'1D8&!L>'Q\=&!,."@<%`P$`__\```#__?S\_/S\_/S\^_GV]?C^ +M!@X4&!L='R`B)"8H*2DH)R'!P>(20G*2HI*2@F(QX7$0P(!0']^OCW +M]_CZ_/\#!@<'!@0"`0$"`0#^_/KX]O/O[.OL[_/U]?/Q\?'S]OK_!0L.#PX. +M#@\/#P\-#`H'`OWX]?/S\_3V^?O\_/KZ^_W_``#__/KW]?3U]_L!!PT2%!44 +M$Q,3%!46%A85%!47&1H9%Q45%A86%!(1$1$2%!<9&1@6%103$@\,"0<&!@8' +M!P@("Q`7(2HS.C]"0T$^/#HX-C(N*20>%Q(/#Q$5&!H;'!\B)"8G)B4B'QX? +M(R(",E)B@H*"@F)2,A'QX='1X@(B0D)"0E)BDJ*RLK+"PJ*"0A'AT< +M'!\B)"4C(!T;'!\C)RDG)!\;&1D:'!\A(R0C(!L6$1`4&B`E*"DI*2LP.$!' +M2TU/3T]-245"04%!/SLV,BXL*24A'1H8%!`-"@D)"@P/$A,2#PP)"`H-$!06 +M&!@8%Q45%AD<'1T='1\A(R4F)RDK+3`R-#4U-3(N*B0?&Q<5%!,2$A07&QT< +M&A<5%!,3%!8;("0G*"@H*"@G)R&143$A$1$A06%103%!@>(RWN[_'S\_3U]_O_`@0$ +M`O_\^//LY=[9U];6U-/0S'CY./AX.'BY.3CX=[;V=?5TL_-S,S,S,W-S]#/SLO*RLO,SVM;6VN;N\O+N[O+[!Q3CXN'@WMS9 +MUM/0S\[0TM78V]W>W]_?W][;UM#*QL/"PL+#Q,7%Q<3"P<'%R]+9W^3HZ^[Q +M]?G]``(!_OOW]O;V]_?W]_?V]?/Q[^WLZ^KJ[.[P[NKDWMK8V-K;V]K7TLW( +MQ<3&R+@W]_?W^#AX-W8T,G# +MP,#"Q+CX^3DX^'?W=O8U='.R\K)R;L\?/T]??Z +M_/OY]._KZ>CIZNSO\?/T]/3V^?T!`P8("@H)!@0#`P(`_OOY]_7T]/;X^_[_ +M__[[^/;U]O7T\_/T]_K]_O___P$#!@H-#0P*"`<'!P@)"0H*"0@'!@4%!00# +M`?[Z]O/R\_7W^/?U\O'Q\_?\``0&!P4$`P0&"0P/$105%100#0L)"`8$`P0& +M"0P.$1,5%QD;&QH8%Q<7%Q<6%!$."P@&!0,`_?GU\.SJZNOL[N[M[.OJZ^WP +M\_;Y_@,*$AHA)B@F(Q\;&AD:'!X>'1D4#0;_^_O]``,$!`,$!`4%`P(````! +M`@,"`?_[^/;X_``"`@#\]O'P\?/V_`(("PP,"0/\]_3P[.ON]?T%#!(4$@T' +M`__\^_X!!`8(#!0;'QT6"O_Y^?T!`P,!_P`"!0@,$!48&AH:&1@:'B0J+S4Z +M/D`^.S'R`?'!8. +M!O[X]?/T]OCY^/7Q[N[P\_7U]/+R\O3W^_W^_?W]_P(&"0H*"PP/$A49'!X? +M'AT<'!T?(RXN;R^P<+!O[Z^ +MOKV\NKBWM[BXM[6TM;BZN[N\O+R_Q,K0TM+1S\_/T-#0S\W+RLG)R\_2T]/2 +MS\O(Q<+!P,'"Q,7&Q\;$P;^]OL#$R,W2UMG;M\?/R\?'R\_/OZN7AX.#B +MY.;IZ^[P\_7W^?GX]_7S\_/U]?7S\._N[_'R\O+Q\?'Q\._P\?+T]//S\_/S +M\_'P[^[O[^_P\/'R\_3T\_'NZ^KHYN7DYN?IZNKJZNKGX][8U=/3U-;6U='. +MR\O-T-/5U=+/R\K*S='4U=73T,[.T-/7VMS=W=[BY^SP\?+Q\O+T]??Y^_O[ +M^/;S\O+S]?;W^?O^`04("@P-#A`2%!87&1H:&182#PP*"`@&`_WW\>SHY>'> +MV]C6T]'.SVM+2UN+S`Q,C,S]+4U]K>XN7FX]W7TOO\_?Y^OKX]_;X^?O[^_KX^/CX^OO]_?OZ^/?W^/K]``,$`P(" +M`@($!0<(!P8$`@(#!08'!P<'"0P1%APB)RPO,3$P+"_O\?3V]_?U]??Z_@(&"0H*"PX3%QD9&AL='!H7 +M%1,2$0\-#`P.$!$1$1(4%Q@8%1$."P<"_?S]`00%`P$"!0H1%QD8%A04%186 +M%A46&1XB(R,?&Q<4$A(4&2`F*2'Q\=&QD9&!@8&!<5$@X+"`8&!04& +M!@8&!@8'"0P/$1$/#@\2%AL@)"DN,C4V-C8X.CU`0#TX,BTL+3(V.S]"1DA+ +M3$U-34Y/3DM$.C`H(B$B)2DK*RDG)RDM,SI!2$]35%-134E&1$-!/CPY.#D[ +M/C\_/#(B#AX>'@W=K7UM?7U];6U]?6UM75V-WD +M[/+V]_?V]O7T\_#KY=[6T,O(R,C(QL/`O;N\OL'$Q\G+S,W,R\G)RLS.SLS) +MQ,"]O;Z_P,"_OKV]O;V]OL##QLG,T-38V=K9V-C8V-C8V-C8V-G9V][AYNKM +M[_#P[^_P\?+T]?7U]/7V]_CY^/?T\>_N[>[P\O+Q[NKFX^+CY>GL[N_P\?3X +M_0$#!`,"`0($!PD+"PD&`?[Z]_3Q[^[O\/+S]/7U]O?W]O/MYM_9U-'.R\C% +MP\+#QHJ6HJJJKK*ZQM+6TL[&OKJZPM+G`QLO/T=+1T,W)QL*_O+JYN+BV +MM+&NK*RLK:^SN+[$R +MW-O:V]S>X.'BXN/CX^/CY.;GZ.CHZ>KKZ^OKZNGHY^?HZN[S^/O]_O\``0$! +M`/_^_?OZ^/?V]?3S\.SHY-_;U];5U=34U-77V-G;W=_BY>CJ[.[Q]?C\_@`` +M`0(#!@D-$!(4%187&!D:&AL<'1X@(R8I*BLI)R0B(!\@(2(D)"0C(B`>'!L< +M'!P9$@H`^//P[N[MZ^KHZ.GK[>_Q\_7W^/CY^OO]_O[\^O?U\_'P[^[M[.SK +MZ^OKZ^KHZ.CK[?#R\_/Q[^SIZ.CIZ^WP\_7U]/+P[_#R\_/P[NSL[O'U^?T" +M!@D*"PL*"@D("`<&!`'^^_GW]O;W^/GY^?GX^/CY^_S\^_CS[NKFX^'@X>/F +MY^CHZ>WR^?X``/___OSW\NWJZ.CK[_/V]_?V]_G[_0`$"Q(9'2`C)2@J*B@F +M)"(@'R$F+3,W.3@U,BXH(!D4$A4:("0G*"@I*2HK+2\Q,S4X/$%'35%45E53 +M3TM&0T`^.SDW-34W.C]#2$M-3D]04E165UA:7F)G:VYP<&]L:&->6%-/3DU- +M3$I'0CPV,"PI*2DI*BLN,SI!14A(24I,3U%14$]/3U%24E!-245"/CHW-#,S +M,S0U-C8T,B\M*B'!D6$Q`.#0P*"`4"__W\_/S]_O_^_/KW]/+P[^[N +M[>WM[>OJZ.CHZNWP\_;Y_/X``0,$!04#`/[\_/S]_OW]_O\!`@(!__SY]O+N +M[.OK[.[N[N_P\?/U]O;R[>;AWMW>W^#AXN/CXN'AX^?L\O@`!P\7'B0I+"XP +M,#`O+"DG)B@I*2(R8G +M)B0B("`@(2`>&Q82#@P-#@X-"0/]^??U\_#MZ^KJZ>?EX^+BX^7GZNSO\?+R +M\?'Q\O3V^?O]_OWZ]_/P[>OJZ^[R]??V]?7U]OCY^_W_`00&"0L+"@@%`?_\ +M^O?S[^OGY>/BX-_?WM[>WM[>W]_?WM[>X.'CY.3CX>#AY.CN\_;W]_;U]/3S +M\O#O[N_P\?+R\O'O[>GFY./CX^/CX^+@W]W=W-K7T]#.S,G'Q\K0U]O=W=S; +MV]O:V-;4TM'1TM34U-+/R\;"OKRYM[2RL+"PL;.VN;N[N[N\O;_!Q,C+SM#0 +MT-'2T]+0SCK[>[N[N_P\?'R\_3U]O;T\>WJY^;EY./A +MW]WVM71S]#3U]O=W^'DY^OM +M[O#R]?;V]//S]?CY]_/NZ>7BW]W/FZ.CG +MY>+>VMC6U]K=X.+DY>?I[?'W_0,(#`X-"PH*"@L-#@\."P<#`/[_`0,$!`0# +M`?[Y]?+Q\?'P[NKFX^'@W]_?X.+DYN7AW-?5U=?7GY^?FY^CK[>[MZ^GH +MZ>SQ]_S_`0#^^_GW]?3R[^WL[>_S]_O]_P`"!`8'"`@*#!$6&AT='!L<'B`@ +M'AL9&!D:&AD8&!D9&AD9&!<6%1$-"`0"`P8*#A$3%!03$Q,2$1$1$1(2$A$1 +M$Q47%A$,"`<("PP,"@@'"`@'!0,"`P4'"`<&!08(#!`5&AXB)2'R$C)2&Q@6%105%144$Q$.#`H)"0D*"PT.#P\/#P\.#@\0$Q47%Q84 +M$A`/#@T+"0D*#0\1$1`.#0L*"`<&!04%!@<("`<&!04$!`0$!`0$!`,#`P0% +M!@<'!@4$!`4&"`D*"@H*"PT.#P\.#0T.$!,7&QXA)"DN,C8X.3DY.##AX^;IZ^SL[>WN[_#P[^[M[>SKZ>;CX-[;V=?5 +MU=75UM;6U=/1T,[,RL?$PL"_O;RZN;FZO+[!PL+!O[V\O+V_P<+"PL+#Q#BX=_ +MW^#@X.#@W][7EYN;GZ.ON\O;Z_@(&"0P.$!$3%AD;'1P9%1`, +M"0@'!@4#`?[[^/7R\?'Q\>_KY^3BX>#@X-_?W][WMWKL[O+W^P`#!@D)"`8$`O_\^?;T\_+S\_3U]??Y +M_``%"`H+"PD'!`("`P8("PX0$1(3%!47%Q@8&AP>(2(B(B$@'QX<&A@6%!(1 +M$1$3%AH='R`A(R,C(A\=&QL<'R(E)20A'QT;&QP<'1T>'B`B)"8G)R8F)B'A\?'QX='!T>(2(B(1X;&1@8&1H: +M&1<4$0X,#0X1$A,3$Q06&1XB)B@I*2DJ*RTN+S`Q,C0V.#@V,S`M*R@F(R`= +M'!P='A\?'AT;&AD9&1D9&AL<'!L<'1X@(2$?'AP;&QL<'!T='AX>'AT<'!L; +M'!P='B`A(B,C(B`>'1T='AX<&183$1$3%QH<'1X@(R8J+2\O+RXM+2\P,3(S +M-#,R,"TK*RLK*B'R`@("`@'QX<&QH:&AD8%Q@9&QT?(2(B(!T:%A(0#P\0 +M$1(4%187&!@7%A86%Q@:'!X@(B,D)24E)"(?'!D7%A86%A44$A`.#`H(!0," +M`0``___^_@`"!`4%`P#]^_O\_@$%"0T/$!`/#P\.#0L)!P8&!P<("`<'!@<' +M"`D*"@H+"PP-#@X-"P<"_OKW]O;W^/GZ^OKZ^OK[_?\``0(#`P4'"`H*"@H) +M"0D)"`8$`O_]^_O[^_S\_?W^``(%!PD+"PL*"@H*#`T0$Q88&QT='1P<&QP= +M'1X>'R`B)"4E)2,@'1@4$0X,"@D(!P4$`@$`_OSZ]_7T]/3U]?7U]O;V]?;W +M^?S^```````!`0,$!0<*#`\2%!04$A$0$!`/#@P*"0@("`@(!P8$`?[[^/?V +M]_GZ^_OZ^??U\N_MZ^KIZ.?FY>3DY.7EY^GL[O#P[^WKZNGJZ^SL[.SKZ^SN +M\//V^/GZ^_O\_?W\^OGY^?K[^OCV\_+P\/#Q\O/T]//Q\._N[NWJZ.3@W=K6 +MU-+1T-#0T,[-S,[1U=O@Y.?HY^;FY>7FY^GK[>[O\/#P\/#P\?/V^/KY^/7S +M\?'S]_P!!08&!`(`````__W[^/7S\O#NZ^CFYN;FY^GJZ^SL[.OIZ.;EX^+B +MX>'AX>'AXN+BX^+AW][=W-W>X.#@W]W:U]32T=#/SLW+R,7#PL'!P<+$QLG+ +MS,W.S\_0T='1TM/5U=;5U=33T]34U=;7V-G:VMO;W>#DZ.ON[^_O[>OIY^7B +MW]O8UM74T]'/SLW-SKJZ>?DX>#@XN3FZ.KKZ^KIY^7BW]S9UM/0S;GZ.CHY^;DXM_WLZNCEXM_>W^'DYN?HZ.GJZ^SN[_'T +M]?;V]?/Q\._O\/'R\_/T]?;W^/GZ^_W]_OW\^_O\_?[^_?KV\^_MZ^KIZ.?E +MXN#?W]_?WMW;V=C8V-K>XN;I[?#S]OG[_/S\_?\!!`<*#0\1$1(2$Q,3$A$0 +M$!`/#0L(!0(`__[]_/O\_?\!`@0%!PD,#A`1$`X,"08#`?_^_O[^__\```$! +M`@0&"@X2%186%1,1#PX-#`L)!@+__?W\_/W]_?\!!`<("`<&!@<*#0\/#@P* +M"0D+#A`1$`X+"0<&!PD*"PH(!@0"`@,%!P@)"@L,#A`1$A,3$Q(0#PX.#Q$2 +M%!04%!47&AXB)"8G*"LN,C4W.3DX.#'AX>'A\@(2(B(B(B(R0E)RHL +M+2TL*RDG)2,B(2$A("`?'AX='A\@(B,C(A\;&!43$Q,2$1`/#@T-#@X0$A89 +M'!X>'AT<&QH8%Q85$Q(1$!`/#PX.#@X/$106&!H;&QL<'1\A(B(B(2`?'!D6 +M$Q`/#@\0$1$1$`\0$A8:'B(E)R +M'1P:&1@7%A44$Q(2$A(1$1`/#@P*"`4"_OOX]O;U]//Q\.[LZNGIZ>GK[.WN +M[_#P\?+R\_/R\O+T]OG\_P(%!P@)"0@'!@4%!08&!@4$!`,"`@$````!`@0% +M!04$!08("@P,"P@&!04&"`D*"@D("`D+#0\1$Q47&AP>'QX<&1<4$@\.#`H( +M!@4%!@<'!P8$`@#_``$"`P0$!`0#`@$````"`P0$`@#\^?7R\.WJY^3AW]W< +MV]K9V-C9V][BY>?GY^?FYN7EY>;EY>3CXN+BX>'@W]W=W-SWM[=V]K8U];6U]C: +MV]W?X.+DY>?HZ>KK[.SKZ^KIZ.CHY^;DX^/DY>;HZ.CGY>+@WMS:V-;5U-+2 +MTM/5V-O>X>/DY>3DY.3DY./BX>#@W][>WMW=W=W=WM[@XN7HZNOKZ^KJZNKK +MZ^OKZNKIZ>CHZ.GIZNKJZ>?FYN?HZ>OL[.SL[.SM[>[N[N[O\/'R\_7V^/K] +M_P$$!@D,#A`1$`X+"0@&!00$`P,#`@(!__WZ^/;V]O;W^/CW]O;V]_G\_@`! +M`0#__O[^_O[_``($!0<)"@L,#0X.#@P*!P/__?OZ^?CU\_'P[_#P\?'Q\?'Q +M\?'P[^[LZ^GGY>3CXN#?W=S+DY>;GY^CIZ^WO\/+S]/7V]O;V]O?Y +M^_W^_OW\^_KY^/?V]?7U]O;V]?7U]O?X^?GZ^_S]__\`___^_?W]_/S\_/OZ +M^?CX^OT"!@H,#`L)"`@("`@&!0(!`/\```$#!0<)"@H*"@H+#A$3%145$Q(2 +M$Q,3$A`.#`L+#`P-#`L+"@H*"PL,#`T.$!,8'2$D)B8E)20E)B@J+"TM+2TM +M+2TM+"PL+"TN+R\P,#$R,S0T,S(R,C(T-38V-C0S,C(R,S0U-C&A<4$A`0$!`1$1(4%1<9&AL<'!P<'!P<'!L:&1D9&AL<'!H9&!<6%A86 +M%Q<8&!@8&!@7%Q<6%103$A$0#@X.#Q`2$Q03$A$0#PX.#Q`2%!<9'!\A(2$@ +M'Q\A)"@L+S(T-CWJY^7DY.7EYN?H +MZ>OL[>[N[^_O\/'S]/7U]?7U]//Q[NKGY./CX^3DY./BX>'@X>'AX>'BXN+B +MXN+BXN+BX^/DY.3DX^/CY.7FY>3AW][>W^#BX^/BXN'BX^7HZNSM[>OJZ>CG +MY^?FYN7EYN;GY^CHY^?GY^;DX^+AX>#?W=O8U=+/S,K(Q\;%Q<;'R,G*S=#4 +MV-K'AXN/EYN?GYN7DX^+@W]W;VMC7UM;5U=75U=/1SLS)Q\7$ +MPL"_O;V\O;Z^OKV]O;[`P<+#Q<;'Q\?'QCJ[>[P\/'Q\?'O[NSJ +MZ.7CX=_>W=S;GY^CIZ>KK[.[O\/#Q\?'R\_/S\>_LZNGIZ>OL +M[O#Q\O+S]/3U]O;U]/+Q\?'R]/;X^?GY]_7S\>_LZ>?EY.3CX>#>W=W=WN#B +MX^7FYN?IZNSN\?/U]_?W]O;U]O?Y^_W__P#___[^______[^_O[^______[\ +M^OCV]//R\?+R\_3U]//P[.CDX=_?W^#BX^3DX^/CY.;HZ^WO\?/V^OX"!@H, +M#0T-#`L*"0@'!@0#`@$`__[]_?S\^_OZ^OGY^?KZ^?GX^/GY^?CW]O7V]O;V +M]_CY^_W_`0,#`P'__?S[^_O[^OGX]_;T\O+R\O/T]O?Z_0$$!@@*"@L+"PH) +M"0@)"@P.#Q`1$1$0$`\.#0T-#0X.#@\/$!$3%!87&!D9&1D9&1D9&1H;&QH8 +M%Q86%Q<8&!@8&1L='A\@(B0G*BPN+R\P,C0V.#DZ.3@V-#,R,3$Q,"\M+"LJ +M*RPN,#`P+RXL*RLK*RPL+"PL+"PN+S$R,C(Q,"\N+BXM+"HH)RW-K9U];5U=34U-34U-35UM?9VMOX.'CY>7E +MY>3DY.;HZNSN[N[N[>[N[^_P\?+S]/3S\O'P\/#Q\O+S\_/T]/7U]?7V]_G[ +M_@$$!@@*#`X0$1(3$A$/#@P*"`<&!00#`0#__O[]_/OX]/'MZ^KIZ>GJZNKJ +MZNKKZ^SM[>SJZ.7CXN'AXN3EY>;EY>3DY.7GZNWO\?/U]_G[_?[^_OW\^??S +M[^OGX^'>W-O:V-?5T]+1T-#1T='2T]35UM?8V-G9V=G9V=K:V]S=WM_AX^7G +MZ.GHYN3AWMS;VMG8U];4U-/2T=#/SLS+RLG(R,G)R#?W]_@X.#@WMS;VMK;V]O; +MV]K9V-?7U]C9V=K9V-;4T;HZ>KK[>_R\_/S\_/S\_+Q\.[M[.OKZ^KIZ.;CX=[=W-SWN[N_P\?+Q\?#O[^_N[>OIZ.;EY>7E +MY.3CX^/DY>;IZ^[P\O3V]_G[_/W]_?W]_?W]_O[__P```/__```!`@,#!`0$ +M!08'"`H,#0T-#`L*"0@'!@4$`P$`_OW\^_KY^/CX^?O\_?[^_OW]_/SZ^?;T +M\_+R\_3V]_GZ^_S]_?[_``$"`@,#`P,$!`0#`0#^_/OY^/?W]_CY^_S_`00& +M"`H+#`P,"PH*"0H*#`T0$A48&QTA)"'1T<'!P='1X? +M("$B(R0D)24E)B@H*2DH*"8E(R(@'QT;&A@8%Q<6%A45%146%Q@8&1@8&1D; +M'!X@(2(C)"8G*"HK+2XO,#$Q,"\M+"PK*RLL+"PK*RDI*"'R`?'Q\?(2,E)R_MZ^CFY./CX^3FZ.KM[N_N[NWL[.OK +MZ>CGY^GJ[>_P\?'R\O/U]_CZ_/\``@,$!@@)"PT.#PX.#`L*"@H*"@D)"`<& +M!00"`?____[^_?SZ^?CW]_CY^_W_`0,%!@<("@L,#0T-#0T-#0X/#Q`1$1(2 +M%!47&!D8%Q43$`X,"PL*"@H*"PL+"PP,#`P+"@<%`O_]_/O\_/W]_O\``@4( +M"PT/$1,4%187%Q@7%Q44$A`.#0P,#`P-#0X/$1,6&!H;'!T='1P;&1@7%Q87 +M%Q<8&!D9&QP='R`@(2(B(R,C(R,A'QP9%A,0#@P+"@D(!P4#`@$```#___[] +M_?W^_O[^_OW\^OCW]O7U]//R\.WJYN/@W][?X.'AXN+BX>'@X-[=W-O:V=C7 +MUM32T,W+RX.'BXN/DYN?IZNSL[>WN[N_P\O/T]/3S +M\?#O[^_N[>SKZ^KJZ>GGYN3BX-W;V=?5T]'.S,G'Q[N[NWM[.SL[.SM[N[O +M[^[N[N[N[^_P\/#P\/#O[NWM[>WN[_#Q\O+S\_/T]?;X^OO\_?W]_?S[^OCV +M]?3T]/3T\_'O[.GGY>3CY.7FY^CIZ>GIZ>GIZ>KK[>_R]??Y^OKZ^OKZ^?GX +M]_;U]//Q\.[LZ^KIZ>GIZNKK[.SL[.SKZNGHZ.GIZNOK[.SKZ^SM[_+U^/O] +M``($!08'!P<'!P8&!04$!`0%!04%!04%!`0$`P,#!`4&"`D)"@L,#0\1$Q05 +M%A<7&!@8%Q86%A<:'2$D)B8F)B4E)28G*2HK*BDG)2,B(2`?'AT<&QH9&!85 +M$Q$/#0L)!P4#`0#__P`!`P0%!@<'!P@)"PX0$Q47&1P>(2,D)24E)"0D)"4F +M*"DK+"PL*RHJ*2DI*2@G)B0C(B`@("`@("`?'QX='!L:&!<5%!,2$1$1$1$1 +M$A05%A86%A87&1L?(B4H*2HJ*BDI*2@H*"'1T< +M&QL;'!T>'1P;&QH:&AH:&1@7%1,0#PT,#`P,#`T-#0X.#Q$2$Q46%QD;'B`C +M)"8G*"DJ+"TO+R\N+BXO,#(T-#0T,S(Q,#`O+BPI*" +M("(C(R(A'QX='1T<&QD7%!(0#P\/#P\.#@T-#0T-#0T.#@\/#PX-#`L)!P4# +M`0#^_/KX]O3S\O'P[^[N[>WM[>SLZNGGY>/BX>'AXN/CX^/BXN+CY>;HZNOL +M[>[N[_#R\_3S\_+Q\?+S]OCZ^_S]_?[_```!`0$!`0```````/____[^_?W\ +M_/S\_@`"!`4&!P@("0H*"PH*"0@&!@8&!@8&!@8%!`,!__[]_?W]_/OY]_;T +M]/3U]O?X^/GY^?GY^/CX^/?V]/'NZ^?CX-[W^'BX^3EY^CJZ^SM[>WL[.KIZ.?FYN;FYN7DX^/CX^/BXN+AX>+B +MX^3EYNCIZNOKZ^OKZ^SN\/+T]O?X^/GZ_/W^_O___O[^_?W\_/OZ^?CW]_?X +M^?K[^_S\_/O[^?CV]/+Q[^WKZ>?DX=_W^#@X.#AX>'BX^/CX^/C +MXN#=VM;2S\O)QL/`OKR[N[JZN;>VM;6UMK>XNKR]OK_!PL/$QL?'Q\;$P\/# +MQ,7'R,G+R\S,S,S+R\O+S,W.T-'1TM/5UM?8VMO+CY.3EY^GK[>_P\?+S]/7V]_CX^/?V]?/Q[^[L[.OJZ>CGYN7DX^/BXN+B +MXN/EY^GK[>[O[_#Q\O+S\_/S\O'P[^[M[.SKZNGHYN7EY>7EY>;GZ.GJZ^WN +M[_#Q\O/S\_/S]/3U]?7U]//R\?'Q\O+S\_/R\O+S]?CZ_?\``0(#!`4'"`D* +M"0@&!0,"`0$!`/_^_?S[^OKZ^OKY^?GY^OO\_/W]_?[^_O[]_?S[^??V]?3T +M]/7U]?7V]_?X^?GZ^?GY^?KZ^_O[^OGY^/CX^/GZ^OKZ^?GX^/CX^/GZ^_O[ +M^_KZ^OGY^?GZ^OO\_O__`````0$"!`4&!@8&!@<'"`D*"PL*"`8%!`,#!`4& +M!P@("`@("`@("0L+#`T-#@\1$Q06&!D:&QL;&QL;&QP<'1T='1P;&AD7%A44 +M$Q,3$Q,3$A$0#PX,"PH)"`@("`D)"0D)"@H+"PP,#0X0$1(4%186%Q<7%Q86 +M%A85%145%145%!04%!45%A85%!,2$1$1$1`0#PX-#`P,#0X.#Q`0$!$1$1$1 +M$1$0$`\.#0L*"0@(!P<&!04%!@<("@P-#@\/#@T-#`P-#@\1$A,4%187&!@8 +M%Q<6%104%!04%!04%!04%!04%!45%Q@9&1D8%Q43$A`/#Q`0$1$0#PX,"PH* +M"PP-#Q`1$A(4%1<8&AP='1T>'R`B)"4F)B4D)"0D)"4F)R@H)R4C(2`?'Q\@ +M("$A(2(C)"8H*BPM+S`P,3(S-#4V-C8U-#,R,3`P+RXN+2TL+"PM+2\P,3$R +M,S,S,S,S,S,S,C(R,C(S,S,R,C$O+BPJ*"8E)"0D)",C(R,C)"4E)28F)B8F +M)B8F)20C(2`?'QX>'AT<&QH8%A44%!04%!04%!03$Q,2$A(1$A(2$Q,3%!47 +M&!H;&QL;&AD9&!<6%104%!45%145%!(0#@T-#0X/$!(3%!45%104%!,2$1`/ +M#0P,"PL+"PL,#`P+"PH*"0D*"@L,#`P-#`P-#0T.#@\/#P\.#@T,"PH*"0D( +M"`<&!@8'!P<(!P<&!04%!08&!@4$`P(!`````/____[^_O__``````#__OW[ +M^_KZ^_S]_O_____]_/O[^_O\^_OZ^?GX^/CX]_;U]/3T]/3T]/3T]/3T]/3T +M]/3S\_+Q\._N[>SKZNGHY^;FYN;FY^?GYN7EY>7EYN;FYN?HZ>OM[_'R\O/S +M]/7W^/GZ^OGX]_;U\_+Q[^[M[.SKZNGHY^?GY^?GZ.CHZ>GJZ^SL[>[N[^_O +M[NSKZNGIZ>GIZ.CGYN7DX^/BXN'AX>'AX>+BX^/DY./BX=[X.'BXN/CY.7FY^?GY^?FYN;FYN?HZ>GIZ.CGYN;F +MYN;EY>3DXN'?WMW+BXN+BX>'AX>'AX>'AX.#@W]_?W]_@X.#@X>'B +MX^7FY^CHZ>GIZ>GHZ.CGYN;FYN;FYN7EY.+AX-_>W]_@X>+CX^/BXN/CY.;G +MZ.GJZNGIZ.;EY./BXN+AX>'@X>'BXN/CY.3DY>;GZ.GJZNKJZNKJZNKJZNGH +MY^;EY./BX>#@W]_?W]_@X>+CX^/DY./CY.3FY^CIZ>CGY^;FY^CHZ>KJZ^OK +MZ^SL[>WM[.SM[>[N[NWLZNGHY^?GY^?GYN;FYN;FY>7DY./DY.3EY>7EYN;F +MY>7DX^+BXN+BXN/CX^3DY.3DY./CX^/DY.7EY>;EY>;GZ.KL[_'S]/7V]O;V +M]_?W]_CX^/CW]O7T\O'P\/#P\/#O[NWLZ^OJZNKJZ>CFY>7FZ.KL[_+T]_G[ +M_@`"`P0#`P(!```!`0$!`/_^_?W\_/W\_/S[^OKY^?GY^?CX^/CX^/CX^?GY +M^OK[^_OZ^OKZ^OO\_?\``0(#!`4&!P@*#0\1$Q04%!05%145%145%186%A<7 +M%A87&!D:&QP<'!P<'1\@(2$A(!\?'AX='1P;&A@6%!(1$`\/#P\/#P\/$!`1 +M$1,4%1<9&QT?(2(C(R,C(B$@'QT;&A@7%Q85%102$1`.#0T-#0X/#Q`0$`\/ +M#Q`0$!`0$1$1$1(2$Q,4%!46%A86%145%A87&!D:&AH:&QP<'1X>'R`A(2(B +M(B(B(B(B(2$A(!\?'AT<&AD8%Q85%145%186%Q86%144$Q,4%!05%!03$Q,3 +M%!05%145%145%A87&!@9&1H;'!T>'R`A("`?'AT<&QH:&AD9&!<7%A86%A87 +M%Q@9&AL<'1T='1P:&1<6%!,3$A$0#P\.#@X.#P\/#Q`0$!`/#Q`0$!$2$Q,4 +M%!,3$A(3$Q04%145%144%!03$A$1$!`0$!$3%!46%Q<7%Q@8&1H;&QL<'!T= +M'AX>'A\@("$B(R,C(R,C(R0D)",C(B(A'QT;&185$Q(1$`X,"PH)"0H*"PT. +M#Q`1$Q05%A<8&!@8%Q<7%A86%A86%A45%186%QD;'1X@(2(C(R,C(R,C(R,D +M)"4E)B8F)24E)",C(B(A(2$A(B,C(R,C(B$@(!\>'1L:&1<5%!,2$A,4%A@: +M'!T?(2,E)RDI*2CGYN;FY>7CXN#?W=W=W=[>W=S:V-;5U-34U-/3T]+2TM/3 +MT]/3T]/3T]/2TM'1T=+2T]/3TM+2T='1T='1T='1T='1T='2T]/3T]+1T,_/ +MS\_/S\_/S\_1TM37V-K;W-S>W^'BX^3DX^/BXN'@X-_=W-SSL[.SKZNGHY^?GY^?GYN7EY./CX^+BX>#?WM[=W-S#?WMW=W=[?X.'AXN+CX^3EYN;FYN;FYN7EYN;GY^CH +MY^?FY>7EY>;GY^?GY^?GY^CHZ.CHY^?FYN7EY>7EYN;GY^?GY^?HZ.CIZ>GH +MY^;EX^+@WMW;GZ.CHZ.CHY^?HZ.GJZ^SN +M[_#Q\O/S\_/S\O+R\?'Q\?'R\O/T]?;W^?K\_@`!`P4&!P@("`@'!P<'!P@( +M"`@("`@("0D)"0D(!P<&!04$!`0#`P(!`0````$!`0("`@,#`P("`@(#`P0% +M!08%!04%!04%!04&!P<("0H+"PP,#`T.#Q$3%!87&!D:&AH:&1D9&!@8&!<7 +M%Q<8&1H<'A\@(2$A(2$@(!\>'!L9%Q85%!,2$1`/#@X-#0T.#Q`2$Q05%A<7 +M%Q<8&!@8&1D9&!@8&!<7%A44$Q(2$A$1$1`0$!`1$A,5%QD;'1\@(B,D)28G +M*"DJ*RLK*BHI*"@I*2DJ*BDI*"@H*"DJ*BLK+"PM+C`R,S0U-C4U-#(Q+RXM +M+"LJ*"'A\?("`?("`A(2(C(R(B(2$A(2(B(B$@ +M'QX=&QL:&AH:&QP='AX?("`A(B,D)B'AT<'!L:&AD8&!<6%144%!05%!04$Q,3$Q,4%!45%144$Q(1$1$1$1(2 +M$Q,3%!46%Q@8&!D9&1H;&QL;&AD8%Q86%104$Q$0$`\.#@T-#0T-#0T,#`P, +M#`P,#`P+"@@'!@4$`P(!`/_^_?W^_O[___\```(#!0<)"PP-#@\0$!$1$1$1 +M$!`/#@T,#`L*"0D("0D*"PP-#@\/$!`1$1(2$Q46%Q<7%A85%!,2$A(1$1`/ +M#@T-#0X/$!`1$!`/#P\/#Q`0$!`0#PX.#0T,#`P,#0T-#@X.#@X.#@T,"PL* +M"@H)"0@(!P8%!00$!`4&!P@("`@'!P8&!P<("`@("`<'!@4%!00$`P(!`/__ +M_P```/____________[^_?S[^OKY^/?V]?3T]/3S\_+R\O'Q\?'Q\?+R\O+R +M\_/S\_/T]/7V]_CX^?GX^/CW]O;U]//R\?#P[^[M[.SL[.SLZ^OKZNKIZ.CF +MY>7DX^/CY.3DY>;FYN;FY>3CXN'AX>+BXN+BXN'AXN+CY.3EY>7EYN;GY^CH +MZ.CGYN7EY>7EY>7DY.3CX^/CX^/CXN+BX>'@W][=W-SWM[?X.'B +MX^/CX^+AX-[=W-O;V]S=WM[?X.'CY>?IZNSM[N[O[_#P\/#P\?'Q\?'Q\?#P +M[^_O[N[M[>WL[.OKZNKIZ>GHZ.?GY^?HZ.GIZ>GIZ.?FY>/BX>'@X.#@X.#@ +MX.#@W]_?W]_?W]_?W]_?W]_?W]_?X.#@X>'BXN+BXN'AX>'BX^/CX^/CXN+C +MX^7FY^CIZ>GIZNKJZNKIZ.;EY.3CX^/DY./CX^/CX^3DY>;FY^CHZ>GJZ^SN +M[_'S]/;X^OO\_?W]_?S\_/O[^OGX]_?V]?3S\O'P\/#P\/'Q\?#O[^[MZ^OJ +MZ>?FY>3CXN+AX>'AX.#@X.#@X.#AX>'@X.#@W]_?W]_?W^#@X.#AX>+CX^/C +MX^/CXN+BXN/CX^/CX^/CX^3DY>7EY>7FYN;FYN;FY>7EY>7FYN?GZ.?GY^;F +MYN;GZ.GJZ^SL[>[O\/#Q\O+S]/3U]?7T]/3S\_+R\O+R\_3U]OCY^OO]_@`! +M`P4&!P@("0D)"`@'!P<'!P<'!P<&!@8'!P<("`D*"@L,#`P,"PL+"PL+"@H) +M"0D)"0H+"PP+"PL+"PP-#0X/$!`0$!`/#P\.#0T-#0T,#`L*"@H*"@H+"PH* +M"@D)"`@("`@)"@L,#0X/#Q`0$1$1$1$1$1$1$1`/#0P,#`T.#Q$2$Q46&!D: +M&QL<'!P<'1T='!P<&QL:&1D9&1D9&1D9&!@8%Q86%103$A$0$!`0#P\/#@X- +M#0P,"PL+"PL*"@H)"0D("`@("`D)"@L+#`T-#@\/$!`1$1(2$A(2$1$1$!`0 +M$!`0$!`0$!`1$1(3%187&!D9&1D9&1@8&!<7%Q<7%Q<7%Q<8&!D9&AL;'!P= +M'1X?(2(C)"4E)B8F)24E)24E)28F)B'AX='1T<&QL;&QP<'1X>'R`@("$A(2(C)"0E)24D)",C(B`?'AT<&QL; +M&QH:&AH:&AH9&!<6%104$Q,2$A$0#@T,"PH)"`<'!P<'!P<&!@8'"`@)"0H* +M"PL+#`P,#0T-#@X.#@X.#@X/#Q`0$1$1$!`0$!`0$!`0$!`0$!`0$!`0$!$1 +M$1`/#PX-#`P,#`T-#0T-#`L+"@H*"@L+#`P,#0T-#@X/#Q`1$A,3%!05%145 +M%145%!03$Q,3%!04%!45%!03$Q$0#PT,"@D)"`<&!00"`0````````$!`0$! +M`0$"`@,$!04&!P@)"0H+#`T/$!$2$Q,3%!04%!03$Q,2$1`/#@X-#0T-#`P, +M#`P-#0T.#@\/#P\/#PX-#0P+"@D("`@("0D*"@H)"0@("`@(!P<'!@8%!04% +M!04&!@8%!04%!`0$!`,#`P,#`@("`@$!`/_^_?W\_/OZ^?CW]O3S\_+R\?'P +M\._O[^_P\/'R\O/S]/7V]_?X^/GY^?GY^?CW]_;V]?7U]?3T]//S\_3T]?7U +M]?3U]?7U]?7U]?7T]//S\O+Q\?#O[N[M[N[N[N[O[^_P\/'R\_7V]_G[_/[^ +M______[^_?W\^_OZ^??V]?7T\_/R\O'Q\/#P\/#P\/#P\/#O[^[M[>SLZ^OK +MZNKIZ.?FYN7EY.3DX^/BXN+CX^3EYN?HZ>GJZNOL[>[O[^[N[>SKZ^KJZNKI +MZ>CGY^;EY>3DX^'@W][=W=W=W=W=WM[>WMW=W=W=WM[?W]_>W=W'AX.#@W]_?X.#@X.#AX>+CY.7EYN;F +MYN;EY>7EY>7DY./CXN'?W][>W=W=W=W+BXN/C +MY.7FY^?HZ.GIZNOL[>[N[^_O\/#Q\?'Q\?'Q\?'R\O+Q\?#O[^[N[N[M[>SL +M[.SL[.WM[N[O\/#Q\O/S\_3T]?7V]_?X^/CX^?GY^OK[^_O[^_O[_/S\_?W] +M_?W\_/S\_/S\_?W\_/S[^_O[^_S\_?[^_P`!`@(#`P,#`P("`@$!`0("`@$! +M`0```0$!`0#__OW\^_O[^_OZ^OKZ^OGY^OKZ^OKY^?GX^/?V]?7T]//S\O+R +M\O+R\O/T]?;W^/CX^/CX]_;V]?/S\O'P\/#P\/'Q\?+R\_/T]/7U]?7T]//S +M\_/S\_/S]/3T]?7V]O?X^/GY^OKY^?GY^?GZ^OK[^_S\_?[^_O_______O[^ +M_O[^_?W]_?W]_?W]_?W^_@`!`@,$!`4%!`0$`P,"`0$```#______P````$! +M`0("`@("`@("`@("`@("`P0%!@<("0H*"PP-#@\/$!`0$!`0$!`0$1(2$Q05 +M%A87&1H;'1X@(2$B(B(B(2$A("`?'QX>'AX>'AX='1T='1T>'AX>'1T='1T= +M'A\?("$A(2$A(2`@'Q\?'AX>'AT='!L;&AH:&AH;&QP<'1T='AX>'A\?'QX> +M'1T<'!P<'!L;&AH9&!@8&!<7%Q<7%Q<6%A45%145%A86%Q<7%Q<7&!@8&!D9 +M&1D9&!@8&!<6%A85%A<7&!@9&!@8&!<7%Q<7%Q86%A44$Q,2$1$0$!`0$!`/ +M#Q`0$1(3$Q,4%!05%A87%Q<7%A44%!,3$A(2$A(2$A(3$Q04%186%Q<7%A85 +M%145%144%!03$Q,3$Q(2$A$0#PX.#0T-#0T.#@\/$!`1$A,3%!45%145%186 +M%A85%144%!,3$Q,3$Q,3$Q,3$Q,2$A$0#PX.#0P+"PH*"@H+"PL,#0T.#@\/ +M#P\/#P\.#@T-#`P,#`L+"@H)"0D)"@H)"0D("`D)"@H*"PL+"PP,#0T.#Q`0 +M$`\.#0P+"@D(!P8&!00#`P("`@$!```!`0$"`@("`@("`@("`@(!`0`````! +M`0$!```````!`0("`@(#`P,#`P("`0#__OW\^_KY^?CW]_?V]O;U]?7V]O?W +M^/GY^OK[_/S]_O__``$"`@(#`P,#`P,#`P("`@$!`0````#____^_O[]_?W\ +M_/O[^_S\_?W]_O[^_O[__P``````__[]_/KZ^?CW]_;U]?3T]/7U]?;V]O;U +M]?7T]//S\_+R\?#O[N[M[>SLZ^OKZ^OKZ^OKZ^OKZ^OJZ>GHZ.CHZ.GIZNOL +M[>WN[N[M[>WM[N[O\/'R\_3V]_CY^OO\_/S\_/S\_/S\_/O[^OGY^/?V]?3S +M\_+R\O/S]/3T]?7U]?7U]O;V]O;U]?3T\_/S\_/T]/7U]?7U]?7U]O?X^?GY +M^?GX^/?V]O7T\_+Q\/#O[^_O[^[N[>WL[.OKZNKJZ>GHZ.?FYN7EY>7DY./C +MXN+BX>'AX.#?WM[=W=W=W=W>WM_@X>'AXN+BXN+CX^3DY.3DY.3DX^/BXN+B +MXN/DY>;GY^?GY^?GY^?HZ>GJZNKJZNKKZ^OL[.SLZ^OKZ^OKZ^OL[.SL[>WN +M[N_P\/'R\O/S]/3U]?7U]?;V]O;V]O7U]?7U]?7T]/3T]//S\_/S\_3T\_/S +M\O'P[^_N[>WM[>WM[>[N[N[N[N[N[N[O[^[N[N[N[^_P\/#P[^_O[^_P\/'R +M\O+R\O+Q\?'Q\?'P\/#P\/'R\_3U]O?X^/GZ^_S]_?[_`````0``___^_?S[ +M^_O[_/S]_O\```$"`@,#`P,#`P,#`P,"`@$!`/___________O[^_O[^_O[] +M_?W\_/S\_/S\_?W]_?[^_O[_____````___^_OW\_/O[^OKZ^?GY^?KZ^_O] +M_O\!`@,$!08&!@<&!@8%!`0#`P("`0#__OW\_/S[^_O[_/S]_?[^_O______ +M``````#_____```!`@,$!`4%!@8&!P<'!P@("0D*"PP,#0X.#P\/$!`0$!`/ +M#PX.#0P,"PL+"PL,#`T-#@X.#@X.#@\0$1(3%!46%A87%A85%103$Q(1$1`/ +M#@P+"0<&!`,"`0````````$!`@("`P,#!`0$!`0$!`0%!08&!@<'!P<'!@8% +M!04%!`0%!04&!@<("0D*"PL,#`P-#`P,"PH)"0@(!P<'!@8&!@8'"`D*"@L+ +M"PL,#`T.#P\0$1$1$1$1$1$2$A,4%146%Q<7%Q86%144$Q,3$A(2$A(2$Q,4 +M%146%A87%Q<8&!@8%Q<7%A44$Q(2$1$1$1(2$A(2$A(2$A(3$Q04%!45%145 +M%145%145%144%!04%!,3$Q,3%!04%!04%!04%!,3$Q(2$A(2$A(3$Q,3$Q,4 +M%146%Q@9&AL<'!T>'Q\@(2$A("`@'Q\>'AT<'!L:&1D8%Q85%!,2$1$0$`\/ +M#@T,#`P,#`T-#@X/#P\0$!`0#P\.#0P+"@D)"`<'!P8&!@8&!@8&!P<'!P<' +M!P8&!@8&!@8&!04$!`,#`P("`0$``/____\```$"`P0$!04%!04%!00$`P(! +M`/__________```````!`0$"`@,$!`0$`P,#`P,#`P,#`P("`@("`@("`P,# +M`P,#`P,$!`4%!04%!04%!`0$`P,"`@(#!`0%!@<("`@("0D*"PP,#0T.#@X. +M#0T-#`P+"PH*"0D("`@("`<'!P<&!@8&!P<'!P<("`@("0D("`@'!@4$!`," +M`@$`__[]_?[^__\``0$"`P0$!04%!04$`P,"`0$!````______[^_O[^_O[^ +M_O[^_____P````$!`0$!`0``_________OW]_/OZ^?CV]?3R\?'P\/#P\/#P +M\/'Q\?'R\O/T]/7V]O;V]O;U]?3S\O'P[^[N[>WM[.SL[.SKZ^OL[.SL[.WM +M[>WM[>SL[.OKZNKIZ.CGY^?GY^?GY^;EY>3DY.7EYN;FY^?GZ.CHZ>GJZ^SL +M[>[N[^_P\/'Q\?+R\O/S\_/S\_+R\?'P\._P\/#P\?'Q\?'Q\?+R\_3T]?7V +M]O;V]O;W^/CY^?KZ^?GY^/CW]_;V]O7U]?7U]?7U]/3T\_/S\_+R\O+R\O/S +M\_/S\_/T]//S\_/R\O'Q\._O[NWLZ^KJZ>CHY^?GZ.CHZ>GIZNKIZ>GHY^?F +MY>7EY>7DY.3DY.7EYN;GY^CHZ>GJZNKJZNGIZ>CHZ.CHY^?GYN;FY>7DX^/B +MXN+BXN+BXN+BXN+CX^3EY>;GZ.GJZ^SM[>[N[^_P\?'R\O+R\?'P[^[N[>WM +M[>WL[.SKZ^KJZ>GIZ>CHZ.CHZ.CHY^?GY^?GY^?GZ.CHZ.GIZ>GJZNKJZNKJ +MZ>GIZ>GJZNKKZ^SL[>[N[N_O[_#P\?'R\O/T]/7U]O;V]O?W]_CX^?GY^OKZ +M^?GY^/CW]_?W^/CX^?GY^OK[^_O[_/S\_/S[^_O[^_O[^_S\_/S]_?[__P`` +M`0$!`0$"`@(#`P,#`P0$!`4%!08&!@8&!P<'!@8%!00$!`0$!`0$!`4%!@<' +M"`D)"0D*"@H*"@D)"0@(!P<'!P<'!@8&!@<'"`@)"0D*"@H)"0D)"0D("`<' +M!@8%!00$!`0$!`0$!04%!04&!@8&!@8&!04%!@8&!P<'!P@("0H+#`P,#`P+ +M"PL*"@H*"@H*"@H*"@H*"@H+"PL+"PL+"PL+"PL+"PL+"PL+"PL,#`T-#0X. +M#@\/$!`0$1$1$A(3$Q,4%!05%145%145%186%A86%A87%Q<7%Q<7%Q<6%A45 +M%!,3$Q(3$Q,3$Q,3$Q,3$Q,4%!04%145%145%!04%!03$Q(2$A$1$1$1$A(2 +M$A(2$A(2$Q,3$Q,3$Q04%!45%145%145%145%!04$Q,2$A$1$!`0#Q`0$!`0 +M$!`0$1$1$A(2$Q,3$Q04%!04%145%146%A87%Q<7&!@7%Q<7%Q<7%A86%145 +M%186%Q<7&!@8&!@7%Q<7%A86%A85%145%104%!04%144%!,3$A$0#PX-#`P+ +M"@D(!P8%!`0#`P("`@("`@("`@,#!`0$!`0$!`0#`P,#`P,#`P,#`P,$!`4% +M!@<("0D*"@L+#`T-#@X.#@X.#@X-#0T-#0T-#0T-#0T-#0T-#@X.#P\/$!`1 +M$1$2$A(3$A(2$1$1$1`0$1$1$1(2$A(2$A(2$1$1$!`0$!`0$`\/#P\/#P\/ +M#P\/#@X-#0P,#`P,"PL+"@H)"0@(!P<&!@8&!@8&!P<'"`@)"0H+"PL+#`L+ +M"PL*"@H*"0D("`<&!00#`0#__OW]_/O[^OGY^/CW]_?W]_?W]_?W]_?W]_?V +M]O;W]_?W]_?W]_?W]_?V]O;V]?7U]?7T]/3T]/7U]?7U]?7U]?7U]?7T]/3S +M\_+R\O+R\O+S\_/T]/7U]?;V]_CX^/CX^/?W]_;V]O7U]?3U]?7U]?7U]/3S +M\_/S\_/S\_/S\_3T]/7U]?;V]_CX^?GZ^OKZ^OKY^?CX]_?W]_?W]_?X^/CY +M^?GY^?GY^?GY^?GY^?CX^/CX^/CX^/CY^?KZ^OKZ^OKZ^OKZ^OGY^?GY^?CX +M]_?V]O7U]?7U]?7T]/3T]/3T]?7U]?7U]?7U]?7U]?3T\_/R\O+Q\?'Q\?'Q +M\?+R\O+R\O'Q\._O[N[M[>WM[.SL[.OKZ^OKZ^OL[.SL[.WM[>WM[>WM[.SL +M[.OKZ^OKZNKIZ.?GYN;EY>7EY>7FYN;GY^?HZ.CHZ.CHZ.CHZ.?GY^?FYN7E +MY.3DX^3DY>7FYN?GY^CHZ.GIZ>KJZNKJZNKJZ^OKZ^OKZ^OKZNKKZ^OK[.SM +M[N_P\/'R\O/S\_/S\_/S\_/S\_/R\O+R\O+S\_/S]/3T]?7U]O;W]_CY^?KZ +M^OKZ^?GX^/CW]_;V]O;V]O;U]?7T]/3T]/3T]//S\_/R\O+R\?'Q\/#P\/'Q +M\?'Q\?'R\O+S\_3T]/3S\_/T]/3T]/3T]/7U]?;W]_CX^?K[^_S\_/W]_?W] +M_?S\^_O[^OKZ^OKZ^OKZ^OKZ^OO[^_O[^_S\_/S[^_O[^_KZ^OGY^?CX^/CX +M^/CX]_?W]_;V]_?W]_?W]_?W]_?W]_CX^/GY^?GY^/CX^/CX^/CX^/CX^/CY +M^?GZ^OK[^_S]_?[__P```0$!`0$"`@(#`P,#`P,$!`0$!`,#`P0$!`4%!@8& +M!P<'!P<'!@8%!04%!04%!04%!04%!@8&!@8%!00$!`,#`P,#`P,#`P0$!08' +M"`@)"0H*"@L+"PL+"PL+"PL+"PH*"@D)"0D("`@("`@("`@'!P<'!P<'!P8& +M!@8%!04%!08&!P<'!P@("`D)"@L+"PL+"PL+"PH*"@H*"0D)"0D)"0D)"0D) +M"0D)"0D)"0D)"0D)"0H*"PL+"PP,#`P,#`P+"PH*"0D("`@(!P<'!P8'!P@( +M"0D*"@L+"PP,#0T.#@\/$!`0$!`0#P\/#P\/#P\/#PX.#@\/#Q`0$!`0$!`0 +M$1$1$1$1$1$0$!`/#P\/#@X.#@T-#`P,#`P,#0T.#@\0$1$2$Q04%145%145 +M%145%146%A86%A86%A86%A86%Q<7%Q@8&1H:&AL;'!P='1T>'AX>'AX='1T< +M'!P<'!P<'!P<'!L;&QL:&AH9&1@8%Q<7%A86%A45%145%145%146%A87%Q<8 +M&!@8&!@8&!<7%Q<6%A44%!,2$A$1$!`0#P\/#Q`0$!`1$1`0$!`/#P\/#P\/ +M#P\.#@X-#`P+"PL+"PH*"@H)"0@("`@(!P<'!P8&!@8%!04%!00$!`0$`P,# +M`P,#`P,#`@("`0$!`````````0$!`0$!`0$!`0$```#______O[_______\` +M``$!`@,#!`0$!`0$!`,"`@$``/___O[^_O[^_O[^_O[___\``````0$!`0$` +M`/____[^_O[^_O[^_O[^_O[^____`````0$!`@("`@,#`P0$`P,#`@("`0`` +M_____O[^_O[^_O[^________________``````````````#___[^_O[^_O[^ +M_O[^_O[^_O[^_O____[^_OW]_/S\_/O[^OKY^?GX^/CW]_;V]O;V]O;U]?7U +M]?7U]?3T]//S\O+Q\?'Q\?'Q\?'R\O'Q\?+R\O/S\_3T]//S\_/S\_/S\_/S +M\_/S\_/S]/3T]/3T\_/S\_/S\_/S\O+R\O+R\?'Q\/#O[^_N[N[M[>WL[.SK +MZ^OKZ^OKZ^OKZ^OKZNKJZNKJZNOKZ^OLZ^OKZNKJZNGIZ>KJZNKKZ^OL[.WM +M[N[O[_#P\/'Q\?'Q\?'Q\/#P\/#P\/'Q\?'R\O+R\O+R\O+R\O+R\O+R\O+R +M\O+R\O+R\O/S\_/T]/3T\_/S\_/S]/3U]?;V]_?X^/GZ^OKZ^OGY^?CX^/?W +M]_?V]O7U]/3T]/3U]?7U]?;V]_?X^/CX^/CX]_?W]_;V]O7U]/3T]/3T]/3U +M]?7V]_?X^?KZ^_S\_?W]_O[^_OW]_?W\_/O[^OKY^/CX^/CW]_?W]O;V]O;V +M]O;V]O;U]?7U]?7U]?7T]/3S\_/R\O+R\O+R\?'Q\?'Q\?'Q\?'Q\?'Q\O+R +M\O/S]/3U]?7U]?7U]?7U]?7U]/3S\_+R\O'Q\?#P\/#P\?'R\O/T]/7U]O;V +M]_?W]_?W]_?V]O7U]?7U]?;V]O;V]_?W^/CX^/CX^/CX^/GY^?GY^?KZ^OO\ +M_/W]_O[^_____P````$!`0$!`0$!`@("`P,#`P0$!`0$!`0$!`0$!`0$`P,# +M`P,#`P,#`P0$!`0$!04%!04%!04%!04$!`0$!`,#`P,#`P("`@("`P,#`P0$ +M!04%!@8&!P<'"`@("`@("`@("`@("`@("`<'!P<'!P8&!@8%!04%!04%!04% +M!04%!04%!04%!`0$`P,"`@(!`0$!`0$!`0$"`P,$!`0$!04%!04%!04%!04$ +M!`0#`P,#`P,$!`,#`P,#`P(#`P,#!`0$!04%!@8&!P<'!P8&!@8&!@8%!04% +M!04%!04%!08&!@<'"`@)"0D*"@H+"PL+"PL+"PP,#`T-#0T.#@X.#@X.#P\/ +M#@X.#P\/#P\0$!`0$1$1$A(2$A(2$Q(2$A$1$1`0$`\/#P\.#@X.#@X.#P\/ +M#P\/#Q`0$!$1$1$1$1$1$!`0$!`/#P\/#@X.#@T-#0T-#`P,#`P,#0T-#0T- +M#0T-#0X.#@X.#@X-#0T-#0P,#`P,#`L+"PL+"PL,#`P,#0T-#@X.#@X.#@T- +M#0P,"PL*"@H)"0D)"`@("`@("`D)"0D*"@H*"PL,#`P-#0T-#0P,#`P,"PL+ +M"PL+"@H*"@H*"0D)"0D)"0D)"`@("`@'!P8&!04%!04$!`0$!`0$!`0$!`0$ +M!`0$!`0$!`4%!04%!@8'!P<'"`@(!P<'!@8&!@4%!04%!04$!`0$!`0$!`4% +M!04%!@8&!@8&!@8%!04%!`0$`P,#`@(!`0$!`````0$!`0("`@,#!`0%!04& +M!@8%!04%!04%!04%!04%!04%!@8%!04%!@8&!@8&!@<'!P<("`@("`@("`@( +M"`@'!P<'!P8&!@8&!@8&!@4%!04%!04%!04%!04%!`0$`P,"`0$``/___O[^ +M_?W]_/S\_/S\_/S[^_O[^_O[^_O[_/S\_/O[^OKZ^?GY^?GX^/?W]O;U]?7T +M]//S\_+R\O+R\O+S\_/S]/3U]?;V]O;V]O;V]O;V]_?W]_CX^/GY^?GY^?GY +M^/CX^/CX^/CX^/CX^/CX^/CY^?GY^?GY^?GY^?CX]_?V]O;V]O;V]_?W]_CX +M^/CY^?GY^?GY^?KZ^OO[_/S]_?W]_?[^_OW]_?S[^_KY^/CX]_?W]O;V]O;W +M]_?X^/CX^/CX^/CX^/?W]O;U]?3T]/3S\_/S\_3T]/7U]?;V]O?W]_?W]_?V +M]O;V]O7U]?3T]/3T]/3T]/3T]/3T]//S\_/S\O+R\O+R\_/S\_/T]/3T]/7U +M]?7U]O;V]O;V]O;V]O;V]O;U]?7U]/3T]/3T\_/S]/3T]/3T]//S\_/R\O'Q +M\/#P\/#P\/#P\/#P\/'Q\O+R\_/S]/3T]/3T]//S\_/S\_/S\_/S\_/S]/3T +M]/3T]/3S\_/S\_+R\O+R\_/S\_/S\_/S\_3T]/3U]?7U]?7U]?;V]O;V]O;W +M]_?W]_CX^/CX^?GY^OKZ^_O[_/S\_/S]_?W]_?W]_?W]_/S[^_KZ^OGY^?GY +M^?GY^OKZ^OKZ^OKY^?GY^/CW]_;V]O;V]O;V]_?W^/CX^?GZ^OO[_/S\_?W] +M_O[^_O[^_?W]_?W]_?S\_/S\^_O[^_O[^OKZ^OKZ^OKZ^OKZ^OK[^_O[^_O[ +M_/S\_/S[^_OZ^OKY^?GY^/CX^?GY^OK[^_S\_/W]_O[___\```````````$! +M`0$"`@("`@("`@("`@$!`0$!`@("`P,#!`0$!`0%!04&!@8&!@8'!P<'!P<' +M!P<'!P<'!P<'!P8&!@8&!@8&!@8&!@8&!P<'!P<'!P<'!P<'!P<("`@("`D) +M"0D)"@H*"PL+"PL+"@H*"@D)"0D("`@("`@("`D)"0@("`@("`@("`@("0D) +M"0D*"@H*"@H*"0D)"0@("`@'!P<'!P<("`@("`D)"0D)"@H*"@H*"@H)"0@( +M!P<&!@8%!@8&!@8&!P<'!P<'!P<'!P<'"`@("`@(!P<'!P<'!P<("`@("`@( +M"`@)"0D*"0D)"`@(!P<'!@8&!@8&!@8&!@8%!04%!04%!`0$!`0$!`,#`P,# +M!`0$!04%!@8'!P@("`@("`@("`@("`@'!P<'!P<&!@8&!@8&!@<'!P<'!P@( +M"`@("`D)"0D)"0D)"0H*"@L+"PP,#0T-#0X.#@X.#@X-#0T,#`P,#`P,#`P, +M#0T-#0T.#@X.#P\/$!`0$!`0$!`0#P\/#P\/#P\/#PX.#@X.#0T-#0T-#0T, +M#`P,#`P,#`P,#`P+"PL*"@H)"0D)"0D)"0D)"0D)"0D)"0D)"0D("`@(!P<' +M!@8&!@4%!04%!04%!00$!`0$!`0$!`0$!`0$!`0#`P,#`@("`@$!`0$!`0(" +M`@("`P,#!`0$!`0$!`0#`P("`@$!`0$!````````````````````________ +M___^_O[^_O[^_?W]_?W]_/S\_/S\_/S\_/S\_/S\_/W]_?W^_O[^_O______ +M___________^_O[^_O[^_?W]_?W]_?W]_?W]_?[^_O[^_O[_______[^_O[^ +M_O[^_O[^_O[^_O___P```````0$!`@("`@,#`P,#`P,#`P,#`P,#`P,#`P(" +M`@(!`0$!`````````````/_____^_O[^_?W]_?W]_?S\_/O[^_KZ^OGY^OKZ +M^OKZ^OK[^_OZ^OKZ^?GY^?CX^/CX]_?W]_;V]O;U]?7U]?7U]?7U]?7U]?7U +M]?7U]?7U]?7U]?7U]?7T]/3T]/3T\_/S\_/S]/3T]/3T]/3T]/3T]/3T]/7U +M]?7V]O;V]O;V]O;V]O7U]?3T]//S\_/S\_3T]/7U]O;V]_?W]_?W]_;V]O;V +M]O;V]O7U]O;V]O;V]O;W]_?W]_?W^/CX^/CX]_?W]_?W]_;V]O;V]O;U]?3T +M]/3S\_/S\_/R\O+R\O+R\_/S\_/S\_/T]/3T]/3T\_/S\_/S\_/S\_3T]?7U +M]O;V]_?W]_?X^/CX^/CX^/CY^?GY^?GY^?CX^/CX^/CW]_?W]_?W]_?W]_?W +M]_CX^/CX^/CX^/GY^?GY^?GY^/CX^/?W]_;V]O;V]_?W]_CX^/CY^?GY^OGY +M^?GX^/CX]_?W]_?V]O;V]_?W]_?W]_CX^/CX^/CX^/CX^/CY^?KZ^_O\_/S] +M_?W]_?W]_?W]_?S\_/S\_/W]_?W]_?[^_O____________\`````________ +M_________O[^_O[______P````$!`@(#`P,#`P,#!`0$!`0$!`0#`P,"`@(" +M`@("`@("`@("`@("`P,#`P,#`P,#`P0$!`0$!`0$!`0$!`0$`P0$!`0$!`4% +M!04%!04%!04&!@8&!P<'!P8&!@8%!04%!04%!`0$!`0$!`0$!`0$!`0$!`0$ +M!`0$!`0$`P,#`P,#`P,#`P,#!`0$!`0$!`0$!`0%!04&!P<("`@)"0D*"@H* +M"@H*"@H*"@D)"0@("`@("`@("`@("`@("`@("`@(!P<'!@8&!@4%!00$!`0$ +M`P,#`P,#`P,#`P,#`P0$!`0$!`0$!04%!04%!04%!`0$!`0$!`0#`P,#`P,# +M`P,#`P0$!`0$!`0%!04%!04%!04%!`0$!`0$`P,#`P,#!`0$!`0%!04&!@8& +M!P<'!P@("`@'!P<'!P<'!P8&!@8&!@8&!P<'!P@("`@("`@("`@(!P<'!P<' +M!P<'"`@("`D)"0D)"0D)"0D)"0D)"0D)"`@(!P<'!P<&!@8&!@4%!04%!04% +M!04%!04&!@8&!P<'"`@("0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0H*"@D) +M"0D("`@("`<'!P<&!@8&!@<'!P<("`@("`@'!P<'!P<'!P8&!@8&!@8&!@8' +M!P<'!P<'!P@("`<'!P<'!P8&!@8&!@8&!04%!00$!`0$!`0$!`0$!`0$!`0$ +M!`0$!`0$`P,#`@("`@(!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0```````/__________`````````````0$!`0$!`0$!`0$!`0$" +M`@("`@("`@("`@("`@$!`0$``````/__________```````````````````` +M```````````````````!`0$!`0``````_____O[^_?W]_?W]_?S\_/S\_/S\ +M_/S\_/S\_/S\_/S\_/O[^_O[^_KZ^OKZ^OGY^?GY^?GY^?KZ^OKZ^OKZ^?KZ +M^OKZ^OGY^?GY^/CX^/CX]_?W]_?W^/CX^/CX^/CX^/CX^/CX^/CX^/?W]_?W +M]_?W]_?W]_?X^/CX^/GY^?GY^?GY^?GX^/CW]_?W]O;V]O7U]?7U]O;V]O?W +M]_?W]_CX^/CY^?GY^?GY^OKZ^OKZ^?GY^?GY^?GY^/CX^/CY^?GY^?GY^?GY +M^OKZ^OKZ^OO[^_O[^_O[^_O[^OKZ^OKZ^?GY^?CX^/CX^/CX^/CX^/CY^?GY +M^?GY^?GY^?GY^?CX^/CX^/CX]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W +M]_?W]_?W]_?X^/CX^/CX^/CX^/GY^?GZ^OK[^_O[_/S\_/O[^_O[^_O[^_O[ +M^_KZ^OKZ^OKZ^OKZ^OK[^_O[^OKZ^OKZ^_O[^_O[^_O\_/S\_/S\_/S\_/S\ +M_/S\_/O\_/S\_/S\^_O\_/S\_/S\^_O[^_OZ^OKZ^OKY^?GY^?GZ^OKZ^OKZ +M^OO[^_O[_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/W]_?W]_?W]_O[^_O[^ +M_O[^_O[^_O[^_O[^_O[^_O[^_OW]_?W]_?W]_?W]_?W]_?W]_O[^_O[^_O[_ +M__________________[^_O[^_?W]_?S\_/S[^_O[^_OZ^OKZ^OKZ^_O[^_O[ +M^_O[^_S\_/S\_/S\_?W]_?[^_O[^_O[^_O_______O[^_O[^_O[^_O______ +M`````````0$!`0$!`0$!`0("`@("`@("`@("`@("`@("`@("`@("`@("`P,# +M`P,$!`0$!`0%!04%!04%!@8&!@8%!04%!04%!04%!04%!04&!@8&!@8&!@4% +M!04%!08&!@8&!@8'!P<'!P@("`@("`@("`D)"0D)"0D*"@H*"@H*"@H*"@L+ +M"@H*"@H*"@H*"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D)"0D("`@("`@' +M!P<'!P8&!@8'!P<'!P@("`@("0D)"0D)"0D)"0D)"0D("`@'!P<'!P8&!@8& +M!@4%!04%!04&!@8&!@8&!P<'!P<'!P<'!P<'!@8&!@8%!04%!`0$!`0$!`0$ +M!`0$!`0$!`0$!`0#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,# +M`P,$!`0$!`0$!`4%!04%!04%!@8&!@8&!@<'!P<'!P<'!P<'!P<'!P@("`@( +M"`@("`@("`@("`@("`@("`@("`@("`@'!P@("`@("`@("`@("`@("`@("`@( +M"`@("`@("`@("`@("`@("`<'!P<'!P<'!P8&!@8&!@8%!04%!04%!00$!`0$ +M!`0#`P,#`P("`@("`0$!`0$``````````/__________________________ +M_____________O[^_O[^_O[]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W] +M_?W^_O[]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?S\_/S\_/S[ +M^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^OKZ^OKZ^OGY^?GY +M^?GY^?GZ^OKZ^OK[^_O[^_O[^_O[^_O[^_O[^_O\_/S\_/S\_/S\_/S\_/S\ +M_/S\_/O[^_O[^_O\_/S\_/S\_/W]_?W]_?W]_?W]_?S\_/S\_/S\_/S\_/S\ +M_/S[^_O[^_O[^_O[^_O[^_O[^_O[^_O\_/S\_/S\_/S\_/S\_/O[^_O[^_KZ +M^OKZ^OKY^?GY^?GY^?GY^?GY^?GY^?GY^?GY^?GY^?GY^/CX^/CX^/CX^/CX +M^/GY^?GY^?GY^?GY^?CX^/CX^/CX^/CX^/CX^/CY^?GY^?GY^?GY^?KZ^OKZ +M^OKZ^OKZ^OKY^?GY^?GX^/CX^/CX^/CX^/CX^/CY^?GY^OKZ^_O[^_O[^_O[ +M^_O[^_O[^_O[^_O[^_O[_/S\_/S\_/S\_/S\^_O[^_O[^_O\_/S\_/S\_/S\ +M_/S\_/S]_?W\_/S\_/S\_/S\_/S\_/S\_/S\_/W]_?W]_?W]_?W]_?W]_?[^ +M_O[^_O[^_O______________`````````0$!`0$!`0$!`0$!`0$!`0$````` +M`````````/____________________________[^_O[^_O[^_O[^_O[^_?W] +M_?W]_?W]_?W]_/S\_/S\_/S\_/S\_/S\_/S]_?W]_?W]_?W]_?W]_?W]_?W] +M_?[^_O[^_O[^_O[^_O[^_O[^_O[^_O[______________P```````0$!`0$! +M`0$!`0$"`@("`@("`0$!`0$!`0$!`0$!`````0$!`0$!`0$!`0("`@("`@(" +M`@("`@("`0$!`0$!`0$"`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(" +M`@("`@$!`0$!`0$!`0$!`0$!`0$!`0("`@("`@(#`P,#`P0$!`0$!`0$!`0$ +M!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0%!04% +M!04%!04%!04%!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$ +M!`0#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,# +M`P,#`P,#`P,#`P,#`@("`@("`@("`@("`@,#`P,#`P,#`P,#`P,#`P,#`P,# +M`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P("`@("`@("`@("`0$!`0$! +M`0$!`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@,# +M`P,#`P,#`P,#`P,#`P,#`P,#`P,#`P,#`@("`@("`@("`@("`@("`@("`@(" +M`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(#`P,#`P,#`P,#`P,#`P," +M`@("`@("`@("`@("`0$"`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!`0$!`0$```````````````````#_________________ +M_____P```````````/__________________________________________ +M``````````#______________________________________P#_________ +M_________O[^_O[^_O[]_?W]_?W]_?W]_?W]_?W]_?S\_/S\_/S\_/S\_/S\ +M_/S\_/S\^_O[^_O[^_O[^_O\_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/S\ +M_/S\_/S]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W] +M_?W]_?W]_?W]_?W]_?W]_?W\_/S\_/S\_/S\_/S\_/S\_/S\_?W]_?W]_?W] +M_?W]_?W]_?W]_?W]_/S\_/S\_/S\_/S\_/S\_/S]_?W]_?W]_?W]_?W]_?W] +M_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/S\_/S\^_O[^_O[^_O[^_O[^_O[^_O[ +M^_O[^_O[_/S\_/S\_/S\_/S\_?W\_/S\_/S\_/S]_?W]_?W]_?W]_?W]_?W] +M_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W] +M_?W]_?W]_?W]_?W]_?W]_?W]_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^ +M_O[^_O______________________________________```````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````0$!`0$!`0$!`0$!`0$!`0`````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`@("`@("`@("`@("`@("`@("`@(" +M`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@(" +M`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`@("`0$!`0$!`0$!`@(" +M`@("`@("`@("`@("`@("`@("`@("`@$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!`0$````````````````````````````````````````` +M``````````````````````````````````````$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`````````````0$!``````````````````````$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0`````````````` +M````````````````````````````````````````____________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M______________________________________________[^_O[^_O[^_O[^ +M_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^ +M_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^ +M_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^ +M_O[^_O[^_O[^_O[^_O[^_O[^_O[^________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____`````/__________________________```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``!#3TU-````$@`!``"2```(0`VL1````````$U!4DL````"``!)3E-4```` +M%#P``'\`?P``````````````````05!03````:A39#)A``(```````````#K +M`.P!+@$O?_\`HP#L`2Y__W__`````````````````&>0,```G(@`.`!B`#@` +MU@!+`&(`2P#6`$\`YP!/`/<`O@#G`+X`]P##`1X`PP%@`,0!'0#$`1X`Q`%@ +M`,0!80#%`1P`Q0$=`,4!80#%`6(`TP$<`-,!'0#3`6$`TP%B`-0!'0#4`1X` +MU`%@`-0!80#5`1X`U0%@```````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````$"```````"0`C```````` +M`````````'@````$``0``````-'^`/___X`````!`2`&455N:71S```3""YP +M9W-Z```)'E!!5",```DJ3%=25P```````0)&!B!S86UP3[_P``(F`21')U;2!/9B!%87)T +M:'%U86ME(`(```!!249&4V0R80``04E&1E-D,F$!`/____\````````````` +M``````````"G/9LK``"4#````>BDX[7XIIM-4@``````+`#B`1`".P$``+0! +M*0&`__C_[````````(`````````````````````````````-`````2$'4V-R +M:7!T3[_P``````M@!*`7(!W@$``)``Q@$`__C_ +MXP```````(```````````````@``````&@`I``\!JP)@`"H``P)M`ST`O0#! +M`8("40$`````&@`I``\!4`'Q`"H``P)M`ST`````X#;__P``````&@`L`!8! +M/@'<`"H``P)M`ST#,P)M_,$";0``````&@`````````````````````````` +M`&`````````!`````7X```!^````:@!QVJ04D@```!P`:@`!5W-T80`#`!)3 +M1$]#````0@`!__\````&```````"__\````D```````#__\```!"```````$ +M__\```!@``````/H__\````````````````````````````````````````` +!`/\` +` +end diff --git a/sys/share/sounds/firehorn.uu b/sys/share/sounds/firehorn.uu new file mode 100644 index 0000000..ad98c64 --- /dev/null +++ b/sys/share/sounds/firehorn.uu @@ -0,0 +1,299 @@ +begin 644 Fire_Horn +M``E&:7)E($AO[R]/+T]P0*^_O__@#X]/X0'QX3`^WL]^SD[OH" +M`/3LY^7JY.P/("OQ[>CH[_#P`Q,1"``%!_[]].ST_P'Z\>[N[_8` +M_PT:`?;^]?C_]OP.(2\H%PH2("`J,BLT,!DA,!X+"`<1(1\#X=74V.?S_0@& +M_N[0N[B]S=[O`Q06_>3G\@@?)SA$/28,_O'Q`0\<+S,D$?7:U.?MZO\3$@3R +MXS<6X +ML;G4`!D7#@#\`@H>-DI985D_'0\0#`P5*3HY,1CV[_'GX>[T^`'YY]+`OKB] +MX/L$_N73S\K0W^WY!1`4#P/NW=KI^OX!`.;-R\S/V.'BY_;HR;FSLK"[W/+Z +M^>GG[.3D\0`'#!($\NOCZO@%&BHH'A,-!?OV]?8!&B(/]>;>W-W6U^SX[-O3 +MU=K@Z?<1*B\;`_?U``T*#R`H(Q+][N3CY^O_%!L9!N[AY_7V_Q8?'QT2!@#^ +M`0PA+R\@`_/]"A0<+$%!.S,<#@?^_`D?+"PI%O;EYN_["Q,(^_#CW-S?Y?L> +M+B\U)@L"`@0'$RKK\?<$$0_^ +MXM'0UN?^$AXD(1D-`?;U`1$N3$XX&?KEX_D0'2D@"?GCRL#*VN\/)1X-^N;< +MWNCT_@@*`_OOX-'*T^X+'!X3_]_'Q,G7[P0-$0W[[.[MWN0!#PX/`^OJ^0(0 +M'B@Q.#8@!_GP[/H7)B8E&@;Z]?3R[>[T`0;OX.3CW.7]"R`\*04#`OCW]^_J +M\.W9SM+-RN(#"@+WX=_T^N_H[?'S"!0!\O/T\?/_!?[SX]KG^?W\`@4'!?GS +M_00$"!HL,"@7#10I,QX,$A43&B`>%@W_^0XG*1T0"Q`=*28>*C0E%@KXZ^/5 +MS=SN[>+:UMKP`OOZ"@X'!PH-$`P.&"U`-R4?%PL(#A`6*"\C&Q8%\>#,Q=KJ +MX]KCZ>+O`OK]#`P*$QL2_>[=U.D'(CHW&?OV_?KX_@`""PKZ[N?;U-KBY>CK +MY.+R]^OI[_/]#0GNX^WV]_+R^`()_>[S_/P"#!(:(!<)_O;O[_7X]O+JV-T,W)!,(!0T?,3(D$O_V\^KL^P<2'2(<"_KM +MZP`G.S`B&`?^``8,&2\Z/#TQ%OKHY?`&'B,7`O/IXM_A\`03&18/`_7KZ?@5 +M*3(S)10(`0@8'R$I-#SW_0H-`//GXN3H\@$,$0X"[N7D[`89&A@1 +M"`(#"!$D+B8C)!\8$@L%#!89$@'NW=?@[OL&#`?[\^;3S\_.V>OW\^+5UM[A +MY>[_$Q,$]=S&R]74W.[X^^_5Q,'(V^SS^P$#__;OYN#:W_X9&0+IUL_H_/?[ +M`P+X[O/VZ^3AX_'Y[]?$P\;,T]C?Z>WFW=G=Y>KP\_7WZM#&U.L#%!<0#`C\ +M\_'NY^GX`/WWZ-3*TN#L^0'Z[.+AY_,#!@@3'!H.`/7MZ_`$'BHE'1P<'",L +M)RDW,A\+^.;@ZO+W#2(:"0/__0,,$!4F,B8/_>OB\?T&&B8:`?7PZ.;H\0H@ +M)"`9$/[EV.,`(B8/!0T-_P$.&C9(-RPN)QT+\_,-)"DI+S@X*!X>)34X*2`C +M)B(6#17M`1HE%?ST^_?R_P@1'1<'^OS]]OD"#AD0[<.OKK;+X?'Z_??FVL_#T>P! +M%A\?&/O8LJ;']1$7#P7_^-[&T_0-%!43^,^VL;W9_A<>(B09!?/T!QXY2$8[ +M*`7>V@4O.3@W)Q("Z]O;Z`(?,SP['_'2T.L+&1PA*RD.[^X%'#%"2DM(+P#> +MX?L7+3@T*!D(^/@$"!0L+AD!X<:[P]OO_08&!/;?VN?Z"QLP03H@"/KY$#!) +M54X[&/3K^0X;%@X2&0C@O**IOLS@[.WHT+JRN]3D[0$8'`GVY,K$U.H'(281 +M\NKW!`T0$!DE'P3JV<_+TND!#@T`]?'W^_3Q\@$0#@@&"Q,1#B)-8$(3]/8. +M%PX%_P8+`>_H]OGJZ^[JZN_OZ?+]_/X#_.OM^.WM_?GM]0L."Q@.!",[.3$G +M'A81"OOU^?OS\??LV=7D`A03#P@!_?G]`@<1("HD'!\@(B4?'RDV.BX:`.SQ +M_?;O^P@![-O+O,/;]`0&`_3>U=/=\/O]_/+ODXM7$ +MR^4"&Q7^\>KEW^+^'"$6"_KKZ>CDX_(/'QT8"?3O\O@&#PL(!_CGX.'HY^CT +M`0?[XLG"UNX&'"$;"_'8R,3+U.3Y%2\R&P8'%2(N,R@<&A,#]O7X]?L,%QP- +MZM7:[?7T`Q44"P$"$Q;]V/R!A4&Z>L$ +M&A8(&#([+AD8'AL1_?4+)3`H&!TT.S`B&!L:__,"#PW_\O4&$`P$^?H&"Q(< +M(RH@"@D9'!$$^?<##PH"`0,&!`$#"Q4._OK]_??JZ/8!!/OU\.'B[_H,%!,+ +M^_'MXM''S>'X!PT%[-G>[_OX\/#FV]O/Q='=X>+=YOCNU,G(U>S\`0,#^NWN +M]O7O[_7\"A#^Y]G6Y/+T]0(-!0+_ZNX)$1$;)B@@&1<>)B??W-KD_08'#0?\]?3NW^#V#A82#@D- +M$0\6&A4)^O3HW^;EZ?@(#O[IX-?7V^H*'1@*!`0$#1,2'2XS*2$8"/?GX_$* +M&`#DW=GGD[.WG]/GM]?CEV^'?V^X!]N7BYN/F^`0'!0`!"`P+"`/Z!!TC&`/LY-[7 +MW^;@Y.O@U-7,P\S0R=#G[^7>WM;0W.39VN\`$1L7$@X)"QTM)QL:$/_[_OGQ +M]P82&A\6`/7NY_0%!/___?GY]_H"^_P-%1@9$P#GY@(5#@XC)Q\>#O?^$QHD +M.#XS)!$""!X@#`@6'QT2^-O;?Y_,,)BL@'R(?)"XH'!TC%@,#`O+M_`8+%!<'\O(! +M"Q0<$P+X[-_C[-W)T>?Y"A<0__X&"APN)`X!]NGJY]#/ZOT#_??Y]O'W_PL; +M&@/MXN/]%/_DZ_C[^?;S]?TF)Q8,"A$;$_SS_@X1#0D,#@<4,3(?$0+N +MZ?D&!?SU^000%Q`-"`85(AX8&0??U>/FY.'K\\=W*N;J^L:_.[?H+$O[O[=S`Q^X'_^;9 +MX.WZ].H`)"TA#O7Q_O/7U_L9#.[I[.OJV'-W@LG%?7JY^3A +MX.+P!A(:'1``^.WI_A<6_//^`?;Q]/4#%QTJ0#\A!O7P`A<;"OX.&0T1(R@G +M*2XU,"$-_?S\^__TW>#Y!P@.%0'Q_PH9+2,.#0[]\@@6!O\+$!8G+1X?+#$\ +M.B47%A4)!104^_+\]_4'!_#J_PP1'!H*_.W;T]OEW-;D[?+\`O\&&B`6$`;P +MVOJUM+G\^KCW,_1Z/7_%R(-\N+=]1@4^_H/$P#U]_T/)C0Q+"X;_/3S +M`!D9!_\'#`3X[>7E^`T-$1X4^_'Y`@3_^/D($1$-"0D'$"HP%O?DVM'/U=KG +MZ>;IV]7H[]_<^A(2$P_\\O+Q]?\&_O7\].;N^OD#'"@J*!/X[?+W^/CTY^?U +M[=G=V\7(X^SN!AH&[N??W^#8U>'[#!,7%14;("(O1$(I&A$&`O7@Y/X4(RDA +M#O;BW-_L"1T6#Q01`-_)S=CB[?H$_>34U>#V"@X'%"$0]MW#M\7?\PL>%/GB +MW./C]A$+$"$5^^7=Y>_X"!XW/2D0!`D;*"$5$PS^]NO@Y.WZ#!@8#@<$^_?X +M`A,.^>_T_OSV]_X-%A$1'"DN+2/7UMK:\@P%]OCX\_GZ^0((#@O_!/G? +MW./L`Q+^ZP`+``85'24V.A\4'A0`^?3S"B`1`Q(;%Q,2$!,6"@86$/'!-G-U-++T^\+ +M(RD7#A,6$`,%'#=#,Q/WZ=S"MJM3Z"Q8? +M*C(>_N[W$2,H(AD0\'T!081)2(+\-/$TNOP\!4W*A86"P`(`OX;(_S;TL_<]`$!"B@O%!0M+RC6R]/CZO\9%AG8TN'W!@D!`B(^+A\B(!4`X-#P +M$0;U]P0(^.[S_`\@'QPG)PCHT];Z"0('"Q,:$@7_"!PF*RP>#0#PW=WU^N_O +M]P4&_/C]_P`'#Q@H*!/][N?BZ?\*"Q,2!P8+!P87*BL=%`X#\]K.S/>W.SZ_/+EXM_1NJRSP=+FZ>S^!_?K]`,:,"8."Q((\NSZ!P@"]>CI[>;> +MW_$,"OT!`O74N+2]SMG4W>KNZMO<]`XC*27R`1LV +M-"<@&`OZ^0L9'2,G*B<@&1,3&2$P/#8@#0'Y_@L3$@L)"?WOZN[Z!@4)%0W[ +M`0H&!P\0$187#P@&`@03'RTZ-R84#PX,#`8#"`+NWM_HZ^WT`!0>$//@XM_7 +MWNKX!@3Z_`@0"_T!%2(C&PP)$0P!`@@.$@?LX.GJW=OG\@`&]NW_"_?E[O?T +M\?'P^Q,;$0\1&20?%A,>(1`.'1T3!_?U`@D&!PT0"P$$!O;^&!86)B(1`?3R +M]OL'%!\:`//U[N\"$`X8)P[MZN+:Z/3X^_OOW-38V];A_0@)!>_AYM_7Y/L* +M$0P`_0,(`?D/*R@@%//:ULBYS_7\]N[6Q\G/R\?6YN;@TL;-U=;>`!P7#PL' +M$!$&`0TN2$(G%`K^\.#D!!P2]^3@W=?8W>X/$^[4S,&^OL'WR`0H0#PP+`??[^/<4,283$PL+&1@A+RCR`Q,;&!,1#@T-"0H0 +M`/,##?GL[_8""`0+%@KY^_WNY.GLW=7K^.GL^O\.%@P0&1()`P()$AD9#`P> +M'`3]!Q@H(Q4>*A7\^OT!`._>U=;F\.?A[@#^]@4;&PC]_@01"OC\_?D%`N_M +M]O3GY_T2%0S\\P$$W\G8X^?N[.?N_O_N[0HF)A(!!`KNQ\+*TN'HXN/NZM?) +MT.O[].KI[^O/M+?(T=OG\OW]\>GA[!,G&Q,7&A+YY_'Z\?8%#141`O+G]`\8 +M%1<3!?3K9W.?K`1LA(B(/_OGOZ_L"_0PE)`OW^@(%$2(J.4$C +M$!,#]?O\`2`R'0/\`@8#%CQ485(D#0P"_PCG +MYN7CY.GN^/;K_A48(B`/%1@(!0G_\.KKZ^_W^O3V"`G\`@<``0$%#?O@U]K= +MX^GK[O/[#1H8("`,"0@#"@7Y]_3T]O/V!Q<<'Q\8'1\5%1(4$O3F\?3U^?+R +M`0;\!!HF-#DE%`X']^7CX^'?UL_;[N;;[0`(&R4>(B47`.3:U\_7Z/0!_.7@ +M]Q(>*2XH)!7YX,_0U,_/T]WNZM/-VN7H[/;U[.GHWL_.S\G,W.ORZ^'E\@08 +M)S4],!8'^^GL_``$"0+QX=K?[/H$"`S^V\;/VMOCY-G:X-S;V-/?]A`F.D'EYNS["1\R+2XI$/[S\/H(#/?;T]SK\_@)'#5'04=$(PX.#Q`9 +M$_';V-WH\?G^!10._P8&]?#S\?/^\-+&P\[E]``+#Q47'C4X*2(@(2(<"_;I +MXN?U_@8/"P'S\@(#^_T"`P,'!/#CVN'X`@@%]_P$#"`D&!04#0+]]>OJ\@DD +M,2\8!0P-#Q\9#!(4`NKHZN+CY>T%$@KW[OL(J(B4@`^_QZMG6V-WT"`#N +MZO7W`B$J(2(6^^?9R,+/UN8$"OOT]@`'%RPQ+R8/^.[JVK]^>'.RLO( +MUO#_`?CLY=O-O+[8ZO<*"??M[?<#%"\W*R$6"O_RY^3M^0(-#__NYN/@Z/S_ +M^/7O[.?@U,O4XNX!`^[DY>CU#1X:#PG__/SJV-_K[?\<(143$A0A)RLN*2,< +M%0T`]>_J\`$:(0X(#ADL,#`T*1X?(!\.^.WJ\`0:&@T-$1,8$QDF&0<'"@?Z +MY]7.X/\=)QX=)BPK*38^+!4/%1D0]=?2Y/@+#_[W^O'G[/?_]NOR^_?CR+W% +MT.4&%A8=&@8$%B@H%@H0&Q<`ZM[F]P,3'QP4_NKN_`D+`O\-&PSNV];;Y_83 +M)2$7`N_U#1\B&!0:%@;NX_,`!!,I+"`9">_T"!$4"P+_].32P\K?Z_H:*AH- +M`_+^'C$[.B@9"O7?S=#D]`<<%/WZ[=KL"A8C)`KRY]S'N<+8\0\@%@L4$@82 +M)R\V+0OR[>38V>+N_`'\\>?@T<35]0@0`N37U,:ZO\_D^P/W[O<'"`,3+3,H +M%/WV_/GL\0@9(A@"^?KTY>+W#Q(']-S/T,S&UN_OZ>OEW^7O\OL:)Q4)_N?? +MWMC?\@8,"`7U\0$,#A@L+A@1">[FY-_G^PL,$10*"A$4'S9-33TT*!,'`/L" +M"P@%#A$&`@8-%2$M*AL:&@?Z\NOKZNOP``D%#!4:*CI&13,I(A4/"P8#!0@* +M$`L!!P@&!/\)$0#U\^?;U]#"P='>Z>OI^@L*"A`A*1H)_/@``/CV_`8)"P;^ +M!PT"^?T*"?[[]_CYZMO8W^GN\.OP!PP"`Q$C*B0:$0P$_/H`!P4&!P`&%Q$# +M!A]P@(#1PE)1\6$@GV[/#^%"7F^Q&147'2`>$P/_"A@6!_SU_0H!_`<'^>GBYNGCTKZ]U>OKY./L +M!!<3#Q88$`3Z`Q,2`O/R!1D;!_?V_@'TZ_8"!@+]!`X$Z]O:Y//W\._S]_KY +M_@@-$A`3)BL5__CY``D0#@P.#`D$_P(*#@P'!P+PW-+8Z?D$!PD)"`T-"A,; +M(RDD'!0,`/?^"Q8B(QP1"0?_^/X)&R06`_WWY]G@[O<"!__[!`H#_@<3(RTE +M&!(-_?']#1`,`??Y^O+CVN+L\?'DUMC4Q'J]?OX]@42$`;]^OKU +M\_D,'AT._??\^_SZ]0`'^O'R[>'8T=_[`_/?U-3:WNCU^@$$^O/U]_3O[P,9 +M'!4)_OK]`@X6$!`2"`0'!OKN\?P'#0D!^?/V_@\A'148&AH8%A,.#A(7&1P@ +M&`T'"!H@!_D!`?GX^/7V]_;[!0H+!_OV`A(2`/L#`@0-$AHA%P<'%1P?'0\' +M"@T`[.KP[.7CZ//WY=GA[??^`/O[!PO_\_H`]^_S^P,)`OCZ!1$9'!4*`/GS +M[O7_\^3H[>WLY./O_`0#`/CIX^7HZ_X4$04("`$`_OK]"QD@*B<.^?3R]@PA +M%/_]^.OGYN+K`1$9(!D#]O?X^Q(N*Q@0"08,"0<-%B,R.#`="?SS\@4@&O[Q +MY^#DZ.WZ#Q\@(!@(_O/O^!$K(0+W]?;]_OT)&!H4$Q$'^>36W/H2`M_/S,S/ +MS];L_?[W^/OV[N+9Z@D6`N?AXN;M[/()&!0+#A`*`?'J]@H3`.+3T=/5U.+] +M`OCR[^WIX-33Z`0-_NGBXM_D\/T0&QD0"PX,`OCX!1(;$_OP[NOM[_8*&AD1 +M$1(/#`@(#ATN)@\)"P@'!081'2,A&AH9$`8$"0X/`_'L[_'T\O0%%1<1$142 +M#/_X_P8,!O3P``H&_O\*#@\.#0\1"?GX`?[X[=[?[O3JXN;N^`'__08(]^_] +M"07\\>7K``;\^`$*"Q`6%181_O4%$P?WZM?7Y^SI[?3Z_@,+"P?^\?0/)R(2 +M!_GQ]OK]`@,$#!<=&0W]]0`8*",5"?;I[//[`?S[!Q,:%`/S[_\7)209"_KL +MZ?+_"08%#1D?&@W]]`$8)R48`^;/S=GL^_T!"Q`/"O[PZ_4*&QX7!>S?X^_[ +M!@@$"`T,!OONY^W["`L"[]2]NLO[Q\_#KZ.S["`?]\^7:W^_\!@D' +M`@('!@0&!PL4%PS\[]S'R-[R_O_X[^OLZN?M^`$+#P3U[^73U_('$`\%_?K] +M^?#Y#!8?(1("__3>W?$`"0D!_/CU[NCY#A8<(!@+"`+S]0H7%A`/$`\+!@86 +M*"LD'A('`O;H\0(%!0<'#0X'`@(+&!,*"P3[_P,!!1`0!P'^``+^_0$(#Q$, +M`_CO\/7W^?WU[.GHZO#S]?L#!@/_]^GAZ_L$"@P(__;S\O/W_``)$A41`^S= +MX._X^O__]NSGZ?+_!P8*$1(/!O/F[?T#!@L*"`/_`@<-$Q$5'2`B'@[_!1$. +M"0T,`O?Q\_H#"`(!!@8&!?OW`Q,3#PX(`/SY^P,0%A(4%@X*"OWR_0H$_@#\ +M]._IZO8&#P\)`/7R\^WJ_`X0#@T%_?GR[?0`!`+^]>_MZ^;E\O_Y\?#NZ^;; +MV.GZ_O[Y[>3@V=7=\?_\]/+U^?7O\/H#!@8$``#]\_/_#`\%]_#R]/#L[_?^ +M`@'\].SDW^H!$!0*_/;X^OC[!0P-#`H)!?[V\@(<*286`OCX\_/_!PL0$A$1 +M#0'S\0(8)"8;"@/_^?L&#Q,4%1<8$P/V]P06)241_?;R]/\.%!`0#PP-!_OR +M]`$/&AP/`?KS\P$+"`0#^_K^^?#M]`(,%!<*^?'J[/T,$`X'_?K[]>_M\/D% +M#0P$^/'O\?\*!@,"^_?W\N_R\_G___[YZ=_>Y?H$__OW\>[MZ^_U]?C^_/[^ +M]/+X!!$4#0<%`_\```,)"0T4$`T*`/T!"!,6"@0%_//X`0P6%0\+`?S\^/D" +M#!88#PD'`?T``@0(!@,%_O/O[O'X!!`2#`P.`_;W_``("PH,!_SX]_H`!`@% +M_?\"^?3U\>_T]/3Z^/#P]OO]_@0#_/T!_/GX\N_S]/7Y[^;N]_\$!`<%^_L# +M!`<-"00%`O[ZZ^/M^@8-"P@#]>[Q]OO^^OD!!0'][^3M^P_T^@0.#`D(`?P"#!(0#`P0 +M#P;]]._Z`@80$`P(_/8`#1(2"@,$`??S\O'Z`00(`_GRZ.OZ!@X.!@(%_^WH +MZ^WX!0H,"P7Z[_#[!PX/"@4!^NWM\_8!"@L-"?[RZO#]"1$3#0H)_>KBX-SF +M]/K]^_7OZNWX`@8$`?[Y\>SO\_0!#0L%_?/N[_0`"PT*!@#^_/?Y_?T$#PX& +M_?H``P0.&R`<%0P&`/K\`0$'#@7UZN?N]?P(%AL4#`/^__[]__X'$`7W\/+^ +M"`L4'A\8#@4#`?T!!P8+#P+QY>/L]?P&#Q`(__GV]O?\`?X!!?GKYN;P_P@- +M#PL#_?K[_/G_"@D*#0#PZNGP_`((#Q$+`P`!_OT#!OWZ_.[AY.CQ`@T.#0L' +M`P$```$#!/_^_O+IZ>CM_`$!`P,!``$#!`4("00"`OGQ\_SL\_K]_/T#!0(`_?X!^O+U^OO[]_#N\/7Z^P$* +M#`L*!P<'_O7U]O?U[^GJ[_+V^?P"!04%`@#^]O+U^?O\^_?V]OC\_?\%!P8' +M!P8"_/?W]O7V]/'Q\?+X_?X``@4)"PH(`P`"`0```?_]^_X%"0@*#`P.#@L& +M`/[^_?O[^_K[_@('"@D)"`8&!P@'!`$"`?\``?_]_P,)#@\/$`\.#`@#_OGV +M]/+R]/7T]/;[_OW_`0$#!0,"__S[^_K[_?W[^/?[`00%!@<+#0H%`/KX]_;V +M^?W]_/K\``$"!0<*"P@&`OOV]/+S]_OZ]_7X_/\"!`<+#`H(`_SW]?/S]?CZ +M^OO^`0$#"`T1$1`0#PL%`O\``@4'!@8*#`L-$!`1$0\.#`7^^O?V]_G\_?O] +M`0(#!08&!@4&!P+]^OCX^/K]__\#!P<'"@H)!P,#`_WT\._N\//U]//U^?S] +M_P$#!`'___WY]_7R\O?\^_G[_P(%"`H-#@P)!?_X\_+R\_?[^_GY^_T``P0& +M!@0#`P'\^?CX^?S^_OX!`P,"!@H.#@P*!P/]^/7T]?CZ^?G[_@`#"`L-#Q`. +M"@8"``#^^_S_`0$#!@<("PP+"PD&`__Z]_?V]OCZ^?C]`@0$!PD)"0<$`@#\ +M^OO[^?K[^/;[`@4&!PD)"`7^^_GU\_/S\_7V\_#T^_W^`00("P@"__WZ^/CY +M^?O^^OC]`P8&!PH-#PP&`?WX]?/R\O;Z^?G^`P4#`P4("@@%!`/_^_KY^/?W +M]_G^`P,!`0,%!`(!``#]^/7T\O#R\O3[!`<'!PD+#`L)"`D)"`<%`P$!```% +M#1`0#@T,"04#`0$"`0$!_OOZ^/K_!@H+"PH)"`4`_?W^_O[^_/W__?P!"`P- +M#`L*!P+\^/?X^/CX^/?V]//V_/\!`@($`P'^^OGY]_;W^/G[^_O_!0@("`D+ +M"@8`_/KX]/#P\?/U]??]`0("`0(#!`,!`/[[^/?V]?;Y^_X#!P8$!`0#`@#_ +M_O[]^/7S\_3V^?T#"0L*"`D+"@@("0D(!@+^^_O^`0,%"0L+"@D(!P4%!`,# +M`?_]_/S^_P(%"`D'!04'!0#\_/[]_/KY^/G\_/T"!P@&`P,$`_[Y]_;V]?/Q +M\/+V]_;X_?___?X"`P'__?S[^??W^/G\_P$#!00"`0,'!@'^_/GV\N_P\_;Z +M_?[_`0'^_@`$!@0#`@#]^?7T]/;Z_O\``@(``/\``0```/[Z]O3S\_7Z_P,% +M!@4%!`0$!@4%!@8$!`,"`@,%"`D*#`L*"0D*"@D(!P,!__SZ^OS_`P8%`P,$ +M!`,$!`,#`__]_?W\_?X!!`8&!@8'!P8$`P$`__OX]O7U^/G[_P$!`/___P$# +M!`0$`__[^OGY^OP``P4%`P("`0``__\`__SY]_3R\_7X_0`````!``#__O[_ +M`/[\^OGX]_?X^_W^_P$"`@+__?S]_?S[^OGX]_?X^_X``00%!04%!`,!```` +M``$!`0("`P,"`@0&!P@'!@,!`/_^_?S[^_W^_P$!``$"`P,"`@#^_?W[^OK\ +M_@`!`@,"__[^___^_?W[^_OZ^/;V]_G[_0``_O[___[\_?_^_O[^_?S\_/[_ +M`00$`@$"`P(`_O[]_/S\^_KY^?K[_/___O\!`@'____^_OW]_O[__P$"`@,# +M`P0%!@8$`P,"__[^_O[]_?\``0("`0($!04#`P,#`@("`@(#!`<)"0H)!P8% +M!`,#`@,#`@$!`?_]_/W_``(#`@,%!00!__________[^_OX``0,#!`4&!0,` +M_?S]_/O\_?[^_?S\_/S[^_X!`P,!_OS[^OGY^OS^_O\``0$`_O\!`0$!```! +M__W\^OK\_/W_`/_]_/W^___^_O\`__W]_/O[^_W_`0(````!`?_]_@`!`?_^ +M_/OZ^OS_`0("````__[\_/W_`0(#`@$````!`@0%!04$`P#^_?W^_P$"`0#_ +M_O\```$!`0(#`P'^_/S\_?W]_O___O[_______\``0#^_/O\_?W\_/S\_/X` +M``#__OW]_OW\_/S\_?[^_____P`#`P,"`0$"`0#^_?W_`````/_]_/W_```` +M_OW^_OS[^_S^`0("`@(!_P`"`P0%!@8&!0+__?W_`0("`P(`_OX``0(#!`0$ +M!`'__?W_`@0%!@<'!00#`P,$!`0&!@,!__[^``,$!`0$`P("`@,#`P,#`P'_ +M_?S\_?[_``$!```````!```"`@#^_?S\_?[^_O[__OW]_O[^_?S\^_GX]_?X +M^OS^_O[^_O[^_O[^_O[^_?OZ^?GY^OS^_O[]_?W]_O[^_O[^_OOZ^OO]_O\` +M`/__``````$!`0$``/[]_/S^``$!`0#_``$"`@0%!00#`?[]_?[_`@0%!00" +M`0(#`P,#`P,"`?_]_?X``0(#`P,"`0$"`@,#`P0#`?[\^_O\_?\`````__\` +M``#__P#__OW\^_O[_/W^_O________[^_O____W\_/O[_/W_``$#`P0$`P(` +M````___^_O[^_?W]_?W_``$!`0#__O____[^_O[^_P`````!`@,#`P(!`0`` +M`/_^_O\``0$`__\``0("`P,#`@$!`/___O[^``("`0$!`0("`0$!`0$!`/_^ +M_?W^_P```0$````````````!`0#^_?W]_O\````!`0(#`P,"`@$!`0$!```` +M``$``/___P`!`````/____[^_O[^_O[^_O[^_P`!`0#___[^_OW]_?[^__[^ +M_O[]_O\``0$``/_^_O[^_O[_`/_____^_P`!`0$!`@$`________````___^ +M_?X``````0$`__[^_?W^__\``0$``````````0("`0$`___^_P`!`@,"`0`` +M``#_``$!`@(!`/_^_O[_``$!`0$!`0#___\```$!`/_^_?S\_?W]_O[__OW] +M_?W]_?W^_P#_______[^_P`"`@$!`0$`__[^_P````````#__O[^_P```0`` +M`/______``````#___\`_P`!`0$!``````````$!`0$`______\``0$!`0#_ +M_O[__P`!`@(!``#__P````$!`0$``/____\```$!`0```/\```````#_____ +M__\````````!`0$!`0$````````!`0$```#__O[______P``_________P#_ +M_____O[_``````#___[^__\```````#__O[^_P``````___^____``$!```` +M____``$!`0$!``````````$!`0$!`/___P`````!`````/___P`````````` +M````````_____P````````$!```````!````_____________________P`` +M_____O[^_O[^_O[____^_OW]_O[^___^_O[^_O[__O[______O[^_O__``#_ +M________````````_____O___P``__\````````````````````!`0`````` +M```````````````````!``````````$!`0$!````____````````_____P`` +M````````````````````____`````````````````0$!````____`````/__ +M________``````#_____````____________________________________ +M________________````_________P```````/____\`````________```` +M````______\`````_________P``````_____P```````/___P`````````` +M`````````````/__`````````````````````/______________________ +M______________\`__________\```````#_____________________```` +M``#__________P``````````````````____________```````````````` +M`````/____\`````````````````````````____````````````````_P`` +M````````````````````````____________```````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````0T]-30```!(``0``+Q8`"$`-K$0```````!-05)+ +M`````@``24Y35````!0\``!_`'\``````````````````$%04$P```&H4V0R +M80`"```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````(````````````````````````` +M````````````````````````````````````````````````````````!`@` +M``````D`(P````````````````!X````!``$````````_`#___^``````0$` +M!E%5;FET````'@```!6__3_\O_P_^[_ +M[/_H_^?_Y__N__@`!P`8`"@`-0`_`$4)1FER92!(;W)N`@```$%)1D939#)A +M````````````````04E&1E-D,F$```````````````````````````````"G +M/:W$```Q(@```<[_G_^L_\P``@!&`(P`R`#R`0D!#P$)`/P`Y0#'`*<`A@!E +M`$0`)``$_^;_S?^[_Z[_J?^H_ZO_K_^R_[3_LO^P_ZS_J/^G_Z3_I/^C_Z3_ +MI?^I_[/_Q/_=__\`)0!.`'``BP"=`*0`I`"<`)``?P!J`%8`0P`Q`"``#P`! +M__3_Z/_=````&@`I``\":@,Q`"H``P)M`ST`*0`/`D$#(@$`````&@`I``\! +M4`'Q`"H``P)M`ST`````````````````&@`L`!8!/@'<`"H``P)M`ST````` +M````````````&@`````````````````````````````````````!`````7@` +M``!X````5@!QN+P8G1_?'IX:50_+!\8$@P&`?OY^?GZ`AM$9G5Q84DW-4%/4T]&-R8G0EM7 +M0R\9_NG:TM':\A@_6&!:3C\Q+30Y-S(R-C@U+B89"?3>S,/,ZQ0S.BL2]][+ +MR]KJ\OG___CLY.#>W=_BX=W7UM_V%"PR(@L"#BA"6&5A5E!)/#$L*RLI)R(9 +M#@H:.E=B7D\[)104(R\O)QP._?L2+S8##@?UX='(S=[N\_'LX]7'RMG@ +MU,:XK*FOOMS^$Q<.^^36WO8.'",@%P\*!@+^]^S=R[>FGJO1_ALG(Q#\Z-3* +MS][O_0D)_>W@V]K;WN'CX-S?[@$.%QX<$`+^$"@P*R08"?[V[>3:T,S+RL?` +MOLKB^`(!]^[HX^+FZ>39SL*UK;+'W./CW +M#0']``@4'R`5!/?^'#E%13PM'Q0*`O[Y\>KGZ.WT_@<*!?SPXM;7ZP<9&P[W +MW[GVL[(U?PI/SPK$_WP[O<% +M#@\'^NO=U][K\_#FV,F^O]#P"1`)_.W>T=+J"!85$0H`^?+JX]O4SLO+SM3C +M]P$"__GQZ^3>V-'(OK2KI:*GON/\__7AQZZCK3?X_(`!0+]]N[E +MW]W;U]+.QKNQM-'^(C4V*1L6$Q`."P+VZ>/DY^GM]/CPW\JXL\7E``P,!?SU +M^Q,D'0KYY]G3TM?=X-_;U,O(U.K]!@/UY^'F[_?\_?CMX-OH##1,444I#?T" +M'#E&0C,B%Q(3&R$?&1`*!P0!_O;IV]/-P[S&Y000"_[MXN'I\?/R\O+T^@$$ +M`/OZ_/[]_`8<+2\A"_/EY_<8-T`V)1#][-K+Q\O.SLS(QM;[(3?X]N[?SL&VJZJT +MQ,[/Q[_&X/P)"/_SZN;GZ.;?ULW%P,#.Z_[^]>C4P\/9^Q$6#P+T[_4`!PT1 +M#P?]\NC>V-?;W-3'NK;+\A$<%P?W[^SL[O#P\/'OZ>#:U-#2VN+H\@<>)!D% +M[MG.SN($&QX9#/_[_?X!!@@%`P$"#!PH*2,6!_OOX]O8U]71S,C#Q-'E\?7P +MYM[A\`<:)B@E(B`?)2LK)B`9$0P*!P'X\>[M[/00/%YI954\)AH9)3(X-"PG +M)B8E(Q\5!?+?SL7)WO4!_>[:R<+-Z0$%`?7CU=#1U]WDZ_/V]/7_#!`-`_+A +MV=SE[O;W].[K[?D1)RH="O+>UMWR#R4O,"TI)B4D(AX;&!,)__;P[O'Y__WU +M\`(G0DE$,QP-!P<-%AL:$PL`\N+4S,K-T=?:W.?]$!0+_.OBZ`(<)!\7#`#Z +M]_C[^_;LXM?,Q]+J_0+[[=O)O;O#S=/5T[BU,K*WP4C*R42^^KCX^CL[.KL\O?X]>_IY^KMZN7B +M[007&Q0%\^3F_AWBUM#=^@\2!>[6R,C3 +MY?D*$Q83#04`_P#\].C:R\//ZP41#?SFVN4%(2PK(`SXZN3EZ>WR]O7OY^'G +M]?[^^//S]?;T[N7SDWM?.R=#@Z^O@SKR[TO(! +M_O3BTLS,T=CWP +M]OT`__SZ^OKW]?@'(#E&1#[W#B@X.3(J +M)R'R"1XG(!@1#`X5&AP<&1,-!@'\]N[GY.+? +MVM'%O<39ZNWEW>3Z"Q`1#/[OXMG6V-C4T-'4U<_#MK?*X>SLZ.'7O]O/G +MU\_;\?KW\>77S\W-SM7H`10;%PT#_/CX^_\!__OY^?K\_OWW\.WW!@T2'!\7 +M$`P)#A?E[@(@.TI,0C0F'QXC +M)R0;$`@%"`P,!OSOX=?:ZOT0)S@W+!X,_/7V_@@/#PD`^/+MZNCGY>+>V=+- +MT^7\"P\-$B,Q,"H@$`,``@<.$0\*!@(`_?CQ[?#[`P/^]_'P]@`(#`G^[^GQ +M``<)"0/Z]?+O[O']%3)(4T]!,28B)S1!1D,Y+2(6"O_X\NWO`ADE+3H],RD? +M%Q05%QL?(!T8$PP#_/3JX-3'O[NYN\;<[_'FV-';Y^CDX-7+QL3%RU=#4Y@4A+BL?$`3^_@`! +M_O?Q[.GFX-K6TLS'RMCI]04;)2$8"OSV^/T%#A49&QT=&1$*!/_Z\^KAW-_P +M#BDV-C(X04$Z-BXD'!80#0L)"`D(!/[W[^;BZP$5'!8'].3/H\PLL1U)-0#4P+S(W.C8M)!L2"0#Y]O?W^@86&Q48(B0?&A(* +M!00%!P<%__CS[./8T,O*S,[/SLW/VNGR\.GDZ_H#"0\0#P\,!P#Z]/'R]/C\ +M____"2`[35%)."@A(RPR,2DA)CM-344Y)Q0(`?[_`PXE.D`Y+!T4$!$2$0X' +M_OGV\NODW][>X>X('!T9'B`<&14."PD)"@H(!PH-#0D#_/;NYMK+O*ZFK<7D +M^/OY_08&`?_\]>SBULW)R,K0UMC6T&!,,!P8) +M$!@=&@\!\N3:V-O?X-W8T,6\O,WH_@<($2(K)R$;$`;_]O#N[N_R]?C]`0#X +M[NKV"A<9$@;^_@(("P@`]O<)&QT6#P3Y\_#N[>ST"!\L*R(4!O__"!0=(2(> +M%0K^]>[GX-WA[OH"#A\F*"LF&Q0/"PH*"@D*"PX0$`T&_O7O[>SJZ_/^!04" +M!QLP.#@T)1#^[N7DZ?#W_?[]^_;Q[O,$'2XT+R06"?SQZN;BX.C_&24C&@GW +M[_#S]OD`$28W/T$_/CPW,BLD'10,!P8'"@T."P8$#!<=(RDG'A<.!P,"`@0' +M!P#W[>/:TL[*QL3`N[.MK;C,VMW9VND`"PH'_O#EWMG:WN/I[?'R\>SAUM/A +M^@T4$PT'!`(`_OGPZNT!&2`9#P+PX=?0S]7F`QXK*A\2"`+__P,'!@'Y[^/5 +MR,'#Q\G+U.'M^PT6%!$-!?_\^OGY]_7U]_CW]._KZ>CGY>/BZ?H1(2(7"`<9 +M+#$P*AT3#@P,#0T,"0/[[^/8T]GM!AD@'!$%_P`'#A$-!@(-)CL],R82__/M +MZ_4+*T1-23PO)R4H+"TJ)!X;&QL9%`O^\.+8V>L)*3Y!-B@9"@$``0("_O?N +MZ.7DY>3BW]G4T,S*T.#S_O_VZ>/O!Q@=&@KUZ>7G[_C]`04("`/_``TF/TM( +M.245#0T3&1T=%A`4)SL^,!\+]NGFZ/('(C(S*R`5#PX0$1`,"`0"`0$#!04` +M]>;7TM[]'C(X+QX,^_#O]P(-%!84$`L$_/'IX][9T\_/V>X""0?_\^[Y#184 +M#`#PX]S9V-?3S<:_N[BXP-7O_P#X[-_6T=#1T<_*QL7/Z`<8%PX`[-S3T=WV +M$R8L*!T1!O_]`@D/$0X)!/[Y]_?X]_/NZ.L!'S0\."H:#0/]_/X$"@T,"@D( +M!P+[\NG@VM?8Y/L1&A,![.#I!R8T-2P8!/7IY>KP]/;V]._I[``=-$%#/#,L +M*2HK*B8?&`\(#!XK)1<*]^+8V>7^&"@J)B`:%A(/#`@#_OGV]?C]`0,"_?/G +MW-SS&#,].2H6!OGS^``#`O_[]O/R\_/Q[>?@V=37Y/H/&14']>GK_A@F)AP( +M\^GI\/?[_O[\^?;V`!0H,2XC%PT)"Q,=(B`7"O[T]08>+#$P(P_]\_<-)C0U +M,"VMC7 +MU,['P+R_S^G[_O7EU/@WMO9UM'+Q\SD +M#"HS+B`)]>KK^`D5&!<5$@X*!O[SZ.+?W=WG_14E*"`2!?GT`R$V."X>!O'F +MYNSU^_SW\>KDZOX5(B,<$PT+#`X/#0D%`/OT[?`!%!H7#O[MZ/(%&28G(1XA +M)S$\0D([,B@=$@D$!0P3%A0)_?H,*SPY+R(0_O/S^@($`?KQZ.#8T]#.S?%@G\\.7@Z@`/#@;\[-O1S,S. +MT,[&O+>_U_$``??JX-S7?[087&Q8)^O'T_PT2 +M#P;\]._Q^0,("`+WZ=G/SMS['3`P*!H(^/+S^/X"`P#[]O#HXM_AY>;G[P(9 +M)209"__X]_C^#B8U,2<>%`L'!08'!?_W]0`8,T1%.RPA&QD;("0E)"$<%@T` +M\>SV!0H'`?C[#!H>&Q,)`0`(3$0T*"`8#_O?PZ^?BW-KA\`0=,SDM'0SX +MZ./DZ>WQ]?C\_P$!`/WX\>GDZ/@.'1\6"?SS[^[P^@P:&Q4-__/MZNCGZ>GG +MZ?0%%B(E(AP6$Q08&Q@1"PH,$!(0#Q8F,"H>$PT5*3D^.S0N+3(Y/T`\-2XG +M(!D3#PT+"07_]>SI[O8"$AD3"O_QY^/?W=[?W=C4T,S+S='4T]'4X_<"`OKO +MX-+(P\+$S^?Z_/7MXMC4T]38W-W=Y?D.%Q(%].;?X>OW`08'!@'Z\^OAV-?F +M^?_]^?D$$!,/!_WT[_'W^?CX^?S]^_CU\_'O[.;>U]7KN`1XT +M/3HM'A(*!P<)"0<$`/OT[N?AX.T'&AX:%QPE)AP/`??T^@@7("`9$0L%__OX +M]_GY]O'JY>GY#2$V0SXO(1,%_OS]``,#__GV]//S\N[GX-WC\?\%`_KOY^7I +M\OC^#2$K*"(6!/;P[_'V^OP%%B@P+R<<%18=)R\P*R0>'!H7$@H`_`05'QX= +M'R4N,2PB%Q`-$1D@(A\9$0@`]_#JYN3FZ.GGY^_\`00-&1H5#PD!^O'HXN'D +MZ.SP\>SEW-70T^+\$!<4"?OPZ.7DX^#+Y`__\``4(!@'[^?P!"Q4:&182#0D'!@4#`/SW +M\NSFY_4&#Q8A*2@A&A`(!0']^//LY-[=X./EY^CGYNWZ!@L(`?CR[NWN[>KM +M_A0=&1$$\^CBX>3H[?@.*T!(1#(RC@ +MX^_V]OD"!P3_^O+JY-[:V=C6TM'3U=/.Q[ZXN\O@[O#GV<_,TMO@X=[B\@0( +M!?[QY=[;VMO?]$Q\?&1$,"PX3%Q81#`<%`P#[].SDY?+[]O#W!`P."@/\ +M^/CZ_P0$__?MY-_>WM[>WM[;UM#/VNOV_0D<*2<=%`H&!P<)#A06%!$0$1(1 +M#0;^_0H;(B$9#P8`_?W[\^KI]P8)!?_TZ.'=W=[>X.K^$R(D'1(*"`T6'!P7 +M$0H%`?WZ^/;Z"2`L*B@M,C8U,2LE(1\?(2$=%Q(/#0L)!?[W\.OFX=O=Z?;[ +M_0@:(R,?%0?]].SFY.+>VMC8V=O=W=S=Y_@($1$*__;P\/3X_`(3*30S*AP- +M!`#]_?W]`1`I04U.1CPS+BLI)R4C(B$?'AL3!_KT_@T0#1`7&QP7#@7^_/T` +M!0@&`?OU\.SIY^3BWM?-P[N\R][L]@83$0;[\./>W^/M]OS____]^O;OYMO: +MZ``0$@H`^OG\_P'^]_/Z!PL(`_?IX-O7U-/5WO(,'R,;#0'[_`(("@D'!@4" +M_/3KX][D]PH,!PH2&1L7$0H&!@@-$A05%102#PL$_//KYN'%0T*"Q`6'!X< +M%Q`)`_[Z]>_HXMW9U]OI_0P8)C`M)1\9%!(0#@T+"`4"__SY]O'JX=S@\`,- +M#@@`^/7S\_#KZ/$"#`T*`/+FWMG:W>'J_1WHX][:UM?B[?7_#A@8%!`) +M!@8%`?_^_/KW\^_KZ.3AX.;W#AXA&Q$(`@$#!00#"QLF)B`6!_GNYN#>WN#K +M!"`Q-2XB%@\+"P\1$0X*!0#Z\NC'BX>+M`!0?(AP2"P@)#0X*`OOV]?3S\N[IZO8# +M!@$!"QTI*2(;%Q<<)"TT,BPB%PP#^_7P[NWMZN;G\@<6&ATC(QH2"?_V]/3U +M]_?U\>WIYN/?VM;2TMWQ`@@!].CDYNOO\?+Z"Q<6$0?XZN3CY.?IZ_4+)#8Z +M,B07#@P2&AX?'QT8$@L$^_#J\@0."0,'$Q\D(1D2#0T/$1(1#PP)!P<&!/_V +M[.+9T]#3X/(`"!$<(!T9%A`,!P(``/_Y\_#R]OCV\>KE[0`2&1@1"/_Y^/GX +M\NSP``T+!/ONY-[;V=C7VN?_%B(A%PH!``4,$`\)`?;LY-_=V]K>Z?H#!0P8 +M(2(>%0P'!PL5'B0E(QX8$0H#_??R[.?BWM[I_`@(!`D2%A(+`//M[O+U^/CT +M[>;@W-O:V-CB]0@0#0+UZ^CJ[_3U\O,!%!H3"O[PZ.?FY^OT!A\T/T`Y+R&Q<4$Q$,!P#X[^;D[/?_"1DB(1X8$0X/$1(0#0@#_??PZ>#9U=/1S\[- +MTN+Y#102$ATF(QL4"?WU\.WN\O7Y_@(!^_/IY.P"%A\=$P@"``$"`/KRZNS[ +M"@X+`_;JXMS8V>+Q`@X3$Q(4%QD<'AX;%`T&`/KT[^KEX>#H^Q(G-#,I'A82 +M%1PA(1X9%!`-"0/^^?/MY^+'M_Q`7%@\&`@(%"`@"^.WEYO@1("(=#_SLX^'M!R`N,"HC'1L;'B$A'AH7 +M%!(0#`;\\>;=U=3A_!4@'0__\_#V``D-#`'=V=75W^_\__OS +MZ.+K`QPG)1H*^>_JZO#W^O?Q[.CG[P$3'!P6#04"`PD-#`?_]>G>VN7V_O[Y +M[=[6U^;]$1<3#0D("Q`4$Q`)`OSY^OT!!`3^]>SG[@8G0$E%-R07$1(7'B$@ +M'AL7$0L%`/OV[^;@W^K["0X*`?7KY./O!A<:%0O\[.#8U-36UM74UN+U"A@; +M%@T&`/SZ^/;S[^OHY.#>Y/4$!P'VZN;N_@T9'QX;&R`F*2VMSD[._M[OL2(B8C&P\&__GZ``8)"0<# +M_/7LY^OY"`\/"0+^_?\!`?OPX]SD^`@,"@/VZ=[7V>7S_0(&"`<%`P0'"08` +M^?/O[N_Q\?#LY^'@[`,9)BD@$`+Z^/P%"@T0$Q89&AH7$@L$^_/R^PL7&A<1 +M"P<#!A0G+2<>$P7Z].[KZ^SJZ.GR``P1$A`,"`4"`/[[]_+MZ>;BW-C=Z_7U +M\O+U^P$$!04&!@@,$!(0#`<"_/;Q[>KHZ.KR_`4)!?SV^PP<(B(<$@@#__W^ +M__SX]O3S]?X*$Q<6$PX(!`,$!@@'!/WV\_L,&!D6$`D*$A@;'!H7%!,4%A@8 +M%A(."P;DX>#B[/T/&1@0!_[W +M\>WN\O;X]>_J[P`/$@P"^/8!#1(3$0T*"@P0%!87%A(-!P#[]>_LZNGM^@4& +M__\*$Q01#07__/S^``#^^O;R\.SGY>O\#Q<4"__SZ^?I\/?Y]_/W!A8:%@W_ +M[^+:V^P#$A84#0%@P"]_#L[?#U]_CW]_CX]_;T\>WIZ_0! +M"0P*`_GP[OP1'B$>%`?^^?;W^?KY^/H"#1<:&10/"00"`P<+"PD%`?ST[.;K +M^`,*$189&180"@3_^_K_`P4&!@<)"@D&__?NY^7L^@8(`_KR]@,.$!`-!@'_ +M_OX``P,!_OKX_0D3%Q4.!__Z^/L`!@@&`?KPZ.?R_@(`^_C[!0H+"@D'!04% +M!PL.#@T+!P+]]_+NZ^GO_`D."@0$#A@:&!0-!P,!````_OOY]_/NZ>SW`P@' +M`_WX]?3V^O[_^O+IYN[[`?[X[N'9W>OX_O[Z]?'N[_/Z``("`/OU\.WKZ^KG +MY>7K\OP+&R(A'181#Q$2$A,1#@H&`?_]_?P`"Q@?'AH3#`3_^_KY]_/Q^0D5 +M%A$*__/JX^+K^P8+#`D%`O_]_/W^_/KX^/CW]?/OZN7BYO4-'R4B&0T"_/K[ +M_P$!__[^_O\```#]^?C\!@T/#0@!^O3P[N[S_PX6%`\'_O;Q[.KK\?L$"@X0 +M$`X+!P0%!PD)"@L+"@@%`OWX]/D*&R0E(!<0#0L*"PP-#0P+"0@'!@/^^/+Q +M]@`+$1`+`OCOZ./H]PL6%Q,+`/?NZ.3CX^;O^P4*"P@%`?WZ^/G\``,"`/WY +M]O+MZNSW!A,='QP7$@X+"08$`@(#!`8&!`']^O;R[NWT_P<(!@'[].[N^`8- +M#`D#_/?S[^WKZ>?FZ_8"#`\."P@$`0`!`P0$`O[X\^[JZ.GO]OK^!`@(!@/_ +M^O;T]/;Y_``#`O_[]O+P\._M[?#V^_W\^?7R]/T&"@P-"P@%`P#^_/KY^OL` +M"1`4%!(/#`H)!P<&!@8&!0,!_?GY_P0$!`D.$Q,/"0+[]?+S^/T!`P0"`?[Z +M]O+O[>OK\?H``P#Z]/3\!`@*"08#`?_]^_GX]_;V]OH"#18;&A82#0D%`P,$ +M!`0#__KW^P0+#0L(!0<+#0T,"08"`/\``P8("0D(!@,`_?GU\?#Q]_X"`?__ +M!@X1$`X*!`#]^OGX]_;T]//T]OP&$!45$@\+"08%!`,"_OKU[^[S_@<)"`/] +M^_X#!08&`P#^_@`#!04$`P(!`?_\^OCV\_+T^?S\^OT&$!,0#`@%!`,!__W\ +M^OCW]_C[_P0)#0X-"P<$`@#__?OY]O/P[O'[!`8#__KV]_T#!P@&`P'__?S\ +M_/OZ^OKZ^OGW\NWGX^'AY>OP\_;]`P4!_?OX]_?W^?S^__[]^_GW]OC]!0T2 +M$Q$,!@+^_?X!`P,`^_C\!@X1#PH$_OP!"`L,"PD&!`,#!0@*"PP+"08"__SZ +M^/;S\?'T^/K]`PL.#0H'!`("`P,"`/SX]/+S]/3T]OG^`0,"`?_^_?SZ]_7T +M\_/S\_7\`P8$`?WY^/O_`@,$`P,$!04&!P8&!@8'!P@'!@4$`P(!`@4("`8% +M"1(7%A01#@T+"08$`P(`_OSZ^?GZ_0(("PL(!`#^_?W]``,$!`+^^O?Y``8& +M`O_[^?K_!0<&`__\^_T"!@D*"`8$`P$`_OSY]O/Q\?7\`@8'!00'#Q04$Q`, +M"04"``#__OW\^OCV]/7Y_@("`@#__O[_``$!`/[\^?;S\>_R^?W[]_3T^0`$ +M!0,`_/CW^/O^``#__?S[^_OZ^??T\?#Q]_\&"0<"^_?Y`PL.#PP&`?WZ^OS] +M_?OX]O7T]?G_`P8&!`'__P$#!`,"`/[\^OCV]?3V^P$!__\!!`<'!@0!```" +M!`8("`<%`P$!`@0$`O_\^?;V^P(&!P4`^O;U_`8+"PD%`/S[^OGY]_7S\?'S +M^0`&"`<#_OKW]_K]_____OW[^OCW]?/S]OL`!`D.$1(1#0@%`@$!`0(#!0<( +M!P<&!@8'!@,!``$%#`\0#@L&`O[]`0L2%!(/"P<#`?_^_/KW]/+S]_\&"@L* +M"`4#`0```````/____[\^??T\?+W_`()#`L(!0+__?W]_O[]_?W^__[^_?S\ +M_/S[^_S_!0L-"P@$`@#]^?H!"`L*"`4`_/KY^?KZ^?GY_`('"@L*"`8$`@$` +M````_______]^_CU\_'R]@`)#@\-"04"`/___OS[^?CW]O;V]O3S\O'Q\O/V +M^P`#`P'__?OZ^OGW]O?]!`H+"08"_OOY^/GZ_/\"!0<'!@4%!04&!P<&!00$ +M`P'__?KX]O7U]?7V^@()#@X,"`4"`/[^_?W]_/OZ^/?V]?7V^/K^`00&"`<& +M`P$`__W]_/S\_/S]``8)"@<$`?[\^_O\_@$$!04%!`,!__[^_P`!`0#__?OY +M^/CX^?GY^OK[_/X``P<*"PL)!@0#`P,"`0#__O[]_?S\_?[_`@0'"PP,"P@& +M!`(!``#___[]_/S[_/T!!08$`O_]_/X``P4$`P'^_?S\_?W^_O[^__\``/__ +M_OW]_?W]_?X``P4%`P$!`P<)"0<&!`,#`P,#`@$!`````0``__\``@0%!00# +M`@#__O\```$!`/[]^_GX]_?Z_@$"`/[\^_P``@(!`/_^_?W^_O[^_O[^_O__ +M``#__OS[^OGX]_?Y_``"`@$`_?OY^OX"`P,"`0#___[]_/S[^_KZ^?GY^_\$ +M!P@(!@4#`@$!`@(#`@(!`0#__O[^_?S[^_X"!`4&"`D)"`8$`P$```````#_ +M___^_OW]_?[^_OW\^_GX^/O_`@,#`?_^_/KY]_C\`00$`P'__?S[^OGY^/GY +M^OP`!`8&!@0"`0`````!`0(#!`0#`@$`_OW\_/S\_/X!!@H,#`H'!`(!```` +M`0$!`@,#`P("`0#__O[^_?W^_P`!`P0#`P(``/_____^_?W\_/\#!P<&!`(` +M_OW\_/S\_@`#!04%!`,"`0#__O[^__\``0$!`0#__OW\^_O[^_K[^_W_`P8' +M!@0"`/[^_O[^_O[^__________[^_O[__P`"`P4%!`0#`@$`______[^_?S\ +M^_O^`0,#`P$`_OW]_/S]_O\`````__[^_?S\_/W]_?W^_O[___[^_OW^_O[^ +M_?W\_/S\_/X``P4%!00#`@$!```!`0$!`0$!`0$``/_^_OW]_@`"!`0$`P(! +M``#__O[]_/S[^_KZ^_S_`0("`0#^_?W\_/S]_@`"`P,"`/_^_O[^_O__```` +M`0$!`0$``/____[^_?W\_/S\_0`#!04%!`,"`0#___[__P```0$!```````` +M``#___[^_OX!`P4%!`,"`0``__________\```$!`0$!`0$``/_^_?W]_@`" +M`@("`0#___[^_O[^_O__``$!``#____^_O[]_?W]_/W]_@`"`P("`0$!`@(! +M`0```````0$!`0$!`0``___^_O[^_P`!`@,#`P("`0$```#___[^_?W]_?[^ +M_______^_O[^_O[^_O__``$!`0````#_____________````````_____O[] +M_?[^_O[__P`!`0```/___O[^_O[^__\``````````````/____________\` +M``$!`0$!`````````````````````````/______________``$"`@$!``#_ +M_______________^___________^_O[^_O[^_O[__P`!`0$!``#_________ +M_O___P`!`@("`0$```````````#_____``$!`0$!````______\````````` +M`/_____________^_O[^_O[__P`!`@(!`0```/_______P````````#___\` +M````````_________P```0$!`0```/_______________P```````````/__ +M____________``````````#___________\```````#_________________ +M______\`````````_________________P```````/________________\` +M``````````````#_______________\``````````````/___P`````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````!#3TU-````$@`!```H%@`(0`VL1``` +M`````$U!4DL````"``!)3E-4````%#P``'\`?P``````````````````05!0 +M3````:A39#)A``(````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````Z`````````````` +M```````````````````````````````````````````````````````````` +M```````$"```````"0`C`````````````````'@````$``0```````#\`/__ +M_X`````!`0`&455N:71S```````````````````````````````````````` +M`0(`!B!S86UP````%8` +M``$A!%-!1$6!`0`````.```&`:/4?J:EY/O_```B8`I&`0``D`#&`0#_^/_C +M````````@``````````````:`"D`#P)J`S$`*@`#`FT#/0`I``\"00,B`0`` +M```:`"D`#P%0`?$`*@`#`FT#/0`````````````````:`"P`%@$^`=P`*@`# +M`FT#/0`````````````````:```````````````````````````````````` +M``$````!>````'@```!6`'&XO!RF````'`!6``!7```````#__\````\```````$__\```!:```````````` +M```````````````````````````````````````````````````````````` +` +end diff --git a/sys/share/sounds/lethdrum.uu b/sys/share/sounds/lethdrum.uu new file mode 100644 index 0000000..2c19869 --- /dev/null +++ b/sys/share/sounds/lethdrum.uu @@ -0,0 +1,433 @@ +begin 644 Leather_Drum +M``Q,96%T:&5R($1R=6T````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``$X`SP```````$B,```! +MZ*<]F@*G/9L$``````````````````````````````"!@6!?``!&3U)-``!( +MA$%)1D934TY$``!&B``````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````/___P``_P`# +M`@$!``#__P@.!/CU]_T!^?;\``#__/S\^?3P\?3U]?7V^?GX^/KSZ^ON\O3U +M^/?R\?'V`@@,#PP)"@\7%0X-!_[Z`1`9&!03%A@;("`4"@8"``4-$Q0/"@D+ +M!O[Z^/?W]OH#!0($!P/[]>WJ_QH@&A0+`?_Z]O#HY-[=Z?3V[>SW\>7AX>?L +MYM?+Q;R[R=CAXMS9W-_>W^'BY>SS^@`!``(%"Q0<)"PT.CDV.#Y!0DA36UY@ +M8V)=54M$0T,_."TC&1`(`__Y\^OCW=K7TQK["R +MM+:VM;2SL[C"R]'7W./L\O;Y^_S^`@<,$147&A\D*"TR-38W-S;BW=G4 +MT,K"NK2NJ::DHZ.CI*6FI::GJJ^TNL#%R,K.TM79W^3I[O/Y``4+$18;'R,F +M*"LN,C0V.#H\/T-(3%%55E965%%-245!/3DV,B\J)2`;%A$-"08#`/WY]?+O +M[.GFY.+@W]WSR^/\$"A`5&1XB)2@L+S,V.3T_04)#1$1$141#0D%`/SX].S@U,B\M*BX.+EZ.OO\O;Y^_X`!`<*#A$4%AD;'2`B)2@L+S0Y/D-(3$]04%%0 +M3TQ)1D(^.3,O*B4B'AH7%!$."@8"_OKV\>WHX]_:U=#,Q\/!O[Z_O[^_O[^_ +MO\#`P,'"P\7'RL[1U=G>X^CL\/3X^_\"!@H-$!,5%Q@:'!X@(B0F*"DK+"PM +M+2TM+BTM+"LK*BHJ*2DH*"DI*"WHX][: +MU]/0S,G&Q,&_O[^_OKZ^O\#!PL/%QL?)R\W/TM78V]_DZ>[T^O\%"@\5&ATA +M)"[L +MZ^GHY^;EY>;GZ.CHY^?FY>3CX=_=V]G7U];5UM;6U]?7V-G:V]S>X./FZ.ON +M\?3W^_X!!`@+#A`2%!<9&QT?(2,E)RDJ+"TO,C4W.3H[.SLZ.#8T,2XJ)R,@ +M'!@4$`P(!`#\^/3OZ^CDX-W9UM32T,_.SWQ +M]?G\_@`!`@(#`P,#`P,#!`0%!P@*"PT/$!$1$A,5%A<8&1H:&QP<'1X>'AX> +M'Q\?'QX='!P;&AD8&!<5$Q(0#PT+"08$`?_\^?;R[NKEX=W9UM/0S7I[/#S^/P!!0D.$Q8:'2$D)BDK+2XO,#`P,"\N +M+"LI)R4C(!T:&!84$Q$0#@T+"0@'!00"`?_^_?OZ^/;T\O#O[N[M[>SL[>WM +M[>WM[>WL[.OKZ^KIZ>CGY^?GYN;EY>7DY./CX^/DY>;HZNSO\?3X^_X!!0@, +M#Q,6&1P>(20F)RDK+B\Q,S0U-34U-#,Q,"XL*28B'QL7$P\+!P/_^_CT\>[K +MZ.;CX>#?WM[>WMW=W=W=W=S3CXN'@W]_?WM[?W]_@X>/DYN?IZ^WO\O3W^OS_`00'"@T0$A48&AT?(20F +M)R@I*BLK+"LK*BDH)R4D(A\=&Q@6$Q$.#`D&!`'^_/GW]?/Q[^[M[.OJZ.?F +MY>7EY.7EY>7EYN;FY^?HZ>KK[.WN[_#R\_3U]O?X^?GZ^_S]_?[_```!`@0% +M!@<("@P-#Q$2$Q05%A<8&1H:&QP<'!P='1P<&QL:&1@7%A03$1`.#`H(!0,` +M_OOY]O3Q[^SJZ.;DXN'@W][=W=S+DY>?IZNSN\/+T]OCZ^_W_ +M`0($!0<("@P.#Q`2$Q,4%!45%145%104%!03$Q(2$1$1$!`0$`\/#P\.#@T- +M#`L)"`<%!`,"`/_^_/OZ^??V]?7T\_+Q\.[M[.OJZ>CGYN7DY./CX^3DY.7F +MY^CJZ^WO\?3V^?O]``(%!PD+#1`2$Q47&!D;&QP<'1X>'AX>'QX>'1T<&QD8 +M%Q44$A$/#@P*"`8%`P'__?SZ^?CW]O7T\_/R\?'P\/#P\/#P\/#P\/#O[^_O +M[^_P\/#P\/'Q\?+S\_3U]?;W^/GZ^_O\_?W^__\``0($!08("0L-#A`2$Q46 +M&!D:&QP<'1T='!P;&AD8%Q44$A$/#0L*"`8$`@#^_?OY]_7S\>_N[.KIY^;E +MY./CXN+BX^/DY>7GZ.GK[>[P\?/T]O?X^?O\_?[__P`!`@,$!08'!P@)"0D* +M"@H+#`P-#0X.#@X/#PX.#@X.#@X.#@X-#0T-#0P,#`P,"PL*"@H)"`<%!`,! +M`/[]^_KX]O3R\>_N[.OJZ>GHZ.CHZ.CHZ.CIZ>GJZ^SM[N_P\O/U]_CZ_/X` +M`P4'"@P.$!(3%!46%Q@8&!@8&!@7%Q85%!,2$1`0#PX-#`L*"0<&!00#`@$` +M__[^_?S\^_OZ^OGY^?GY^?GY^?GY^?GY^/CX]_?W]O;U]?3T\_/R\O+R\O+R +M\O+R\_/T]?;W^/GZ^_W^_P$"`P0&!PD*#`T.$!$2$Q05%A<7&!@9&1D8&!<7 +M%A43$A$/#0P*"`8$`@#^_/KX]_7T\O'P[^[M[.OKZNKJZNKJZ^OL[>WN[_#Q +M\O/T]O?X^OO\_?\``0$"`P,$!`4%!04&!@8%!04%!04%!@8&!P<'!P@("`@) +M"0H*"@L+"PL+#`P,#`P,#`P,#`P+"PH*"0@'!@4$`@'__OW[^?CV]?3R\?#O +M[NWLZ^KJZ>GIZ>GJZNOL[>[O\/+S]/;W^?K\_O\!`P4&"`H+#`X/$!`1$A,3 +M$Q,3$Q,3$A(1$!`/#@T-#`L*"0@'!@4$`P,"`0$`___^_?W\_/S\^_O[^_O[ +M^_O[^OKZ^OKZ^?GY^/CX]_?V]O7U]?3T]/3T\_/S]/3T]/7V]O?X^?GZ^_W^ +M_P`"`P0&!P@*"PP-#@\0$1(2$Q,4%!04%!,3$Q(1$`\.#0L*"`<%!`(!__[\ +M^_KX]_;U]//S\O+Q\?#P\/#P\/'Q\?'R\O/T]/7V]_CY^OO\_?[__P```0$! +M`@("`P,#`P,#`P,#`P,#`P,$!`0%!04&!@8'!P@("`D)"@H*"@H*"@H*"PL+ +M"PL+"@H*"0D(!P8%!`,"`?_^_?OZ^??V]?3S\O'P\._O[^[N[N[O[^_P\?+S +M]/7V]_CY^_S]_@`!`@0%!@<("@L,#`T.#@\/$!`0$!`/#PX-#0P+"@D)"`<& +M!04$`P(!`0``_____O[^_OW]_?W]_/S\_/S\_/S\^_O[^_O[^_OZ^OKZ^OGY +M^?GX^/CW]_?W]O;V]O;V]O?W]_CY^OO[_/W^_P$"`P0%!@<("0H+#`P-#@X/ +M#P\0$!`0$`\/#PX.#0P+"PH(!P8%!`(!__[\^_KX]_;U]/3S\O+R\?'Q\?'R +M\O/S]/7U]O?X^/GZ^OO\_/W^_O__```!`0("`@,#`P,#!`0$!`0$`P,#`P0$ +M!`0$!`0$!`0$!04%!@8&!P<("`@)"0D)"0D)"0D("`@'!@8%!`0#`@(!`/_^ +M_?S[^OGX^/?V]?3T\_/R\O+Q\?'Q\O+R\_3U]O?X^?K\_?[_``(#!`4'"`D) +M"@L+#`P,#0T-#0T-#0P,"PL*"@D("`<&!@4$`P(!`0#__O[]_?S\_/O[^_O[ +M^_O[^_O\_/S]_?W]_?W]_?[]_?W]_?W]_/S\_/S[^_O[^_KZ^OKZ^OKZ^OK[ +M^_O[_/S]_?[__P`!`@,$!08&!P@)"@H+#`P-#0T-#@X-#0T,#`L*"0D(!P8% +M`P(!`/_^_?S[^?CW]_;U]?3T\_/S\O+R\O/S\_3U]?;W^/GZ^OO\_?[_``$" +M`@,$!`4%!@8&!@8'!P<'!P8&!@4%!04$!`0$!`0#`P,#`P("`@("`@("`@(" +M`P,#`P,#`P0$!`0$!`0$`P,#`@(!`0#___[^_?S\^_KY^?CX]_?V]O7U]?7U +M]?7U]O;W^/CY^OO\_?[_``$"`P0%!@<("0H*"PL,#`T-#0T-#0T,#`P+"@H) +M"`<&!00#`P(!`/_^_?S[^OKY^?GX^/CX^/CX^/CX^/GY^?KZ^_O\_/S]_?W^ +M_O[___\```````#__________________O[^_O_______P````$!`@(#`P0$ +M!04&!@<'!P@("`@("`@("`@(!P<&!@4$`P,"`0#__O[]_/OZ^?CW]_;V]?7U +M]/3T]/3T]?7V]O?X^?GZ^_S]_O\``0(#!`4&!@<'"`@)"0D)"0D)"0D)"0@( +M"`<&!@4%!`0#`P(!`0``___^_O[^_?W]_?W]_?W]_?W^_O[^______\````` +M``````````#_______[^_O[]_?W]_?W]_?W]_?W]_?[^_O__`````0("`P,$ +M!`4&!@<'!P@("0D)"0D)"0D("`@'!P8&!00#`@$`___^_?S\^_KZ^?CX]_?V +M]O;V]O;V]O;V]_?W^/GY^OO[_/W^_O\``0$"`P,$!`4%!04%!@8&!04%!04% +M!00$!`,#`P("`@$!`0$!`0````````````````````````````$!`0$!```` +M````_____O[^_?W]_/S[^_OZ^OKY^?GY^?GY^?KZ^OO[^_S\_?W^__\``0$" +M`@,$!`4&!@<'"`@("0D)"0D)"`@("`<'!@8%!`0#`@$``/_^_OW\_/O[^OKZ +M^?GY^?GY^?GY^OKZ^OO[_/S]_?[^__\```$!`@(#`P,$!`0$!`0$!`0$!`0$ +M`P,#`P("`@("`0$!`0$!`0$!`0$!`0$!`0("`@("`@("`@("`@("`@("`@$! +M`0$!````___^_OW]_/S\^_O[^_KZ^OKZ^?GY^OKZ^OO[_/S]_?[^__\``0(" +M`P0$!04&!@8'!P<("`@("`@'!P<'!@8&!04$!`,"`@$``/_^_OW]_/S[^_OZ +M^OKZ^OKZ^OKZ^OK[^_O[_/S\_?W^_O[___\``````0$!`0$!`0$"`@(!`0(! +M`0$!`0$!`0$!`0$!`0$!`0$!`@("`@,#`P,#!`0$!`0$!`0$!`0$`P,#`P(" +M`0$!``#___[^_?S\^_OZ^OKY^?GY^?GX^/CX^/GY^?KZ^_O\_/W]_O__``$" +M`@,$!`4%!@8'!P<("`@("`@("`<'!P8&!@4%!`0#`P("`0``_____O[]_?W\ +M_/S\_/O[^_O[^_O[^_S\_/S]_?W]_?[^_O[^________________________ +M__________\```````$!`0$"`@(#`P,$!`0%!04%!04%!04%!04%!00$`P," +M`@$!``#__O[]_?S[^_KZ^?GX^/CX]_?W]_?W^/CX^/GY^OK[^_S]_?[__P`! +M`0(#`P0%!08&!P<'"`@("`@("`@("`<'!P<&!@4%!00$`P,"`@$!``#____^ +M_O[^_?W]_?W]_?W]_/S\_/S]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W]_?W] +M_?W^_O[^_O___P```0$"`@,#!`0%!04&!@<'"`@("`@)"0D("`@("`<'!@8& +M!00$`P(!`0#___[]_?S[^_KZ^?GX^/CX]_?W]_CX^/CX^?GY^OK[^_S\_?[^ +M__\``0$"`@,#`P0$!04%!08&!@8&!@8&!@8%!04$!`0$`P,#`@("`@$!`0`` +M````_____________O[^_O[^_?W]_?W]_?S\_/S[^_O[^_O[^_KZ^OKZ^OKZ +M^_O[^_S\_/W]_?[^_P```0$"`P,$!`4&!@<'!P@("`D)"0D)"0D)"`@(!P<& +M!@4$!`,#`@$``/_^_?W\_/OZ^OGY^/CX]_?W]_?W]_?W^/CX^?GY^OK[^_S\ +M_?[^__\```$!`@(#`P,$!`0$!04%!04%!04%!04%!04%!00$!`0$!`0$!`,# +M`P,#`P("`@("`@("`0$!`0``_____O[^_?W]_/S[^_OZ^OKY^?GY^?GX^/CX +M^?GY^?KZ^OO[_/S]_?[^_P```0("`P0$!08&!P<("`@)"0D)"0D)"0D)"`@( +M!P8&!04$`P,"`0$`__[^_?S\^_OZ^OGY^?CX^/CX^/CX^/CY^?GY^OK[^_O\ +M_/W]_O[__P```0$"`@(#`P,$!`0$!04%!04%!04%!04%!04%!04%!04%!00$ +M!`0$!`,#`P,"`@(!`0$!``#____^_O[]_?S\^_O[^OKZ^?GY^?GX^/CX^/CX +M^/GY^?KZ^OO[_/S]_O[_```!`@(#!`0%!08'!P@("0D)"0H*"@H*"@D)"0D( +M"`<'!@4%!`,#`@$!`/_^_OW\_/OZ^OKY^?CX^/CX]_?W^/CX^/CY^?GY^OKZ +M^_O\_/W]_?[^__\````!`0("`@,#`P,$!`0$!`4%!04%!04%!04%!04%!04% +M!04%!04$!`0$`P,"`@(!`0``___^_OW]_/S[^_KZ^?GY^/CX^/?W]_?W]_?W +M]_?X^/CY^?KZ^_O\_?W^_P`!`0(#!`0%!@8'!P@("0D)"@H*"@H*"@H)"0D) +M"`@'!@8%!`0#`@(!`/___OW]_/O[^OKY^?GX^/CX^/CX^/CX^/CX^/GY^?KZ +M^OO[^_S\_/W]_O[___\```$!`@(#`P,$!`0%!08&!@8'!P<'!P@("`@("`@( +M"`@'!P<'!@8&!04$!`,#`@(!``#___[]_?S[^_KZ^?GX^/?W]_;V]O;V]O;V +M]O;V]_?X^/GY^OO[_/W]_O\``0$"`P0%!08'!P@)"0H*"@H+"PL+"@H*"@H) +M"0@(!P<&!04$`P,"`0``__[^_?S\^_OZ^OGY^?CX^/CW]_?W]_?W]_?X^/CX +M^?GY^OKZ^_O\_/S]_?[^__\```$!`@(#`P0$!04%!@8'!P<'"`@("`@("0D) +M"`@("`@'!P<&!@4%!`0#`P(!``#__OW]_/O[^OGY^/?W]O;U]?7T]/3T]/3T +M]/7U]?;V]_?X^?KZ^_S]_O\``0("`P0%!@<'"`D)"@H+"PP,#`P,#`P,"PL+ +M"@H)"`@'!@8%!`,#`@$``/_^_?W\^_OZ^?GX^/CW]_;V]O;V]O;V]O;V]O?W +M]_?X^/GY^OK[^_S\_?W^_O\```$!`@,#!`0%!08&!P<("`D)"0D*"@H*"@H* +M"@H*"@H)"0D("`<&!@4$`P,"`0#__OW]_/OZ^?GX]_;V]?3T\_/S\_+R\O+S +M\_/S]/3U]?;W^/CY^OO\_?[_``$"`P0%!@<'"`D*"@L,#`P-#0T-#0T-#0T, +M#`L+"@H)"`<'!@4$!`,"`0#__O[]_/O[^OGY^/CW]_;V]O7U]?7U]?7U]?7U +M]?;V]O?W^/CY^?KZ^_S\_?[^_P`!`0(#!`0%!@<'"`D)"@H+"PP,#`P-#0T- +M#0T-#`P,"PL*"0D(!P8%!`0#`@$`__[]_/OZ^?CW]O7U]//S\O+R\?'Q\?'Q +M\?'R\O/S]/7U]O?X^?K[_/W^_P`!`@,$!08'"`D*"PL,#0T.#@X.#@X.#@X. +M#0T-#`L+"@D("`<&!00#`@$`__[]_/OZ^OGX]_?V]?7T]/3S\_+R\O+R\O+R +M\O/S\_3T]/7V]O?X^/GZ^_S]_?[_``$"`P0%!@<("0H*"PP,#0T.#@X/#P\/ +M#P\/#@X-#0P,"PH)"`<&!00#`@'__OW\^_GX]_;U]//R\?'P\._O[N[N[N[N +M[N_O[_#Q\O+S]/7V]_CY^_S]_@`!`@0%!@<)"@L,#0T.#Q`0$1$2$A(2$A$1 +M$1`0#P\.#0P+"@D(!P8%!`,!`/_^_?S[^OGX]_;U]//S\O'Q\/#P\._O[^_O +M\/#P\?'R\O/T]/7V]_CY^OO\_?\``0(#!08'"`D*"PP-#@\0$1$2$A(3$Q,3 +M$Q,2$A$1$`\/#@T+"@D(!P4$`@'__OW[^OCW]O7S\O'P[^[N[>SLZ^OKZ^OK +MZ^SL[>WN[_#Q\O/T]??X^?O\_?\``@,%!@@)"@P-#@\0$1(2$Q04%!05%144 +M%!03$A(1$`\.#0P+"@@'!@0#`@#__?S[^?CW]O7S\O'P[^_N[>WL[.OKZ^OK +MZ^OK[.SM[>[O[_#Q\O3U]O?Y^OS]_P`"`P4&"`D*#`T/$!$2$Q05%186%Q<7 +M%Q<7%A85%103$A$/#@T+"@@&!0,!`/[\^OGW]O3S\?#O[NWLZ^KIZ.CHY^?G +MY^CHZ>GJZ^OM[N_P\?/T]O?Y^_S^``$#!`8("0L,#0\0$1(3%!46%A<7%Q<8 +M%Q<7%A85%103$A$/#@T+"@D'!00"`?_]_/KY]_;U\_+Q\.[M[>SKZNKIZ>CH +MZ.CHZ.CIZ>KJZ^SM[N_P\O/T]O?Y^_S^``$#!0<("@P-#Q`2$Q06%Q@8&1H: +M&AL;&QH:&AD8%Q85%!,1#PX,"@@'!0,!__W[^??V]/+Q[^[LZ^KIZ.?FYN7E +MY>3DY>7EYN;GZ.GJZ^WN[_'S]/;X^?O]_P`"!`8'"0L,#A`1$A05%A<8&1D: +M&QL;&QL;&AH9&!@7%A03$A`/#0L*"`8$`P'__?OY^/;T\_'P[^WLZ^KIZ.?F +MYN7EY>7EY>7EYN?GZ.GJZ^SN[_'R]/;W^?O]_P$#!08("@P.$!$3%187&1H; +M'!P='1T>'1T='!P;&AD8%A43$A`.#`H(!@0"`/[[^?CV]/+P[^WLZNGHY^;E +MY>3DY./CX^3DY>7FY^CIZNOL[N_Q\O3V]_G[_?\``@0&"`D+#0X0$A,4%A<8 +M&1D:&QL;&QL;&QL:&AD8%Q84$Q(0#@T+"0<%`P'__?OZ^/;T\_'O[NSKZNGH +MY^;EY.3DX^/CX^/DY.7FYN?IZNOL[O#Q\_7V^/K\_@`"!`8("@P.$!(3%1<8 +M&1H;'!T='AX>'AT='!P;&AD8%A43$1`.#`H(!@0!__W[^??U]/+P[^WLZ^GH +MY^?FY>7DY.3DY.3EY>;FY^CIZNOM[N_Q\_3V]_G[_?X``@0%!PD+#0X0$1,4 +M%A<8&1H;&QP<'!P<'!P;&QH9&!<6%!,1$`X,"@D'!0,!__W[^??U\_+P[^WL +MZ^GHY^?FY>7DY.3DY.3EY>;FY^CIZNSM[_#R\_7W^?O]_@`"!`8("@P.$!$3 +M%!87&1H;&QP='1T='1T<'!L:&1@7%A03$0\-"PH(!@0"`/[\^OCV]/+Q[^[M +MZ^KIZ.?GYN;EY>7EY>7FYN?GZ.GJZ^SM[_#R\_7V^/G[_?\``@0%!PD*#`X/ +M$1(3%187&!D9&AH:&QL;&AH9&1@7%A44$A$0#@P+"0<%`P'__?OY^/;T\_'O +M[NWKZNGHZ.?FYN7EY>7EYN;FY^CIZ>KL[>[P\?+T]O?Y^_S^``(#!0<)"@P. +M#Q$2%!46%Q@9&AH;&QL;&QH:&1D8%Q84$Q(0#@T+"0<%!`(`_OSZ^/?U\_+Q +M[^[M[.OJZ>GHZ.?GY^?GY^?HZ.GJZ^SM[N_P\?/T]??X^OO]_@`"`P4&"`H+ +M#0X0$1(4%186%Q@8&1D9&1D9&!@7%A44$Q(1#PX,"PD'!00"`/[\^_GW]O3S +M\?#O[>SKZNKIZ.CGY^?GY^?GZ.CIZNOL[>[O\/+S]?;X^?O]_@`"`P4'"`H, +M#0X0$1(4%186%Q@8&!D9&1@8%Q<6%103$A$0#@T,"@@'!0,"`/[]^_KX]_7T +M\_'P[^[M[>SKZ^KJZNKJZNKJZNOK[.WN[_#Q\O/T]??X^?O\_O\``@,%!@@) +M"PP.#Q`1$A,4%187%Q<8&!@8%Q<6%A44$Q(1$`\-#`H(!P4#`0#^_/OY]_;T +M\_+P[^[M[.SKZNKIZ>GIZ>GIZNKKZ^SM[N_P\?+T]?;X^?O\_O\!`P0&!PD* +M"PT.#Q$2$Q04%186%Q<7%Q<6%A85%!03$A$/#@T+"@@'!00"`/_]^_KY]_;T +M\_+Q\._N[>WL[.OKZ^KJZNOKZ^OL[.WN[_#Q\O/T]?;W^?K[_?[_`0($!08( +M"0H,#0X/$!$2$Q04%146%A86%A45%!03$A$0#PX-"PH)!P8$`@'__?SZ^/?V +M]//R\._N[>WLZ^OJZNKJZ>KJZNKK[.SM[N_P\?+S]?;W^?K[_?X``0,$!@<( +M"@L,#@\0$1(3$Q05%146%A86%144%!,2$A$0#PT,"PD(!P4#`@#__?SZ^?CV +M]?3S\O'P[^_N[>WM[.SL[.SL[.WM[N[O\/#Q\O/T]?;X^?K[_/[_``(#!`8' +M"`H+#`T.#Q`1$A(3$Q04%145%104%!,3$A$0#PX-#`L)"`8%`P(!__[\^_GX +M]_7T\_+Q\._N[NWM[.SL[.SL[.SM[>[N[_#P\?+S]?;W^/G[_/W^``$"!`4& +M"`D*"PP-#@\0$1(3$Q,4%!04%!04$Q,2$1$0#PX-#`H)"`8%!`(!__[]^_KX +M]_;U]//R\?#O[^[N[>WM[>WM[>WM[N[O[_#Q\?+S]/7V]_GZ^_S]_P`!`P0% +M!@@)"@L,#0X/$!$1$A(3$Q,4%!,3$Q,2$A$0#PX-#`L*"0@&!0,"`?_^_/OZ +M^/?V]?3S\O'P[^_N[NWM[>WL[>WM[>[N[^_P\?+S]/7V]_CY^OS]_O\!`@,$ +M!@<("0H+#`T.#P\0$1$2$A(2$Q,2$A(2$1$0#P\.#0P+"@D'!@4#`@'__OW[ +M^OGX]O7T\_+Q\?#O[^[N[>WM[>WM[>WN[N_O\/'Q\O/T]?;W^/K[_/W^``$" +M`P4&!P@)"@P-#@\/$!$1$A(3$Q,3$Q,3$Q(2$1`0#PX-#`H)"`8%!`(!`/[] +M^_KY]_;U]//R\?#P[^_N[NWM[>WM[>WN[N_O\/#Q\O/T]?;W^/GZ^_W^_P`! +M`@0%!@<("0H+#`T.#P\0$1$1$A(2$A(2$A(1$1`0#PX-#`L*"0@'!@4#`@'_ +M_OW\^_GX]_;U]//S\O'Q\/#O[^_O[N[O[^_O\/#Q\?+S\_3U]O?X^?K[_/W_ +M``$"`P0&!P@)"@L,#0X/#Q`1$1(2$A(2$A(2$A$1$`\/#@T,"PH)"`8%!`(! +M`/[]_/OY^/?V]?3S\O'Q\/#O[^[N[N[N[N[O[_#P\?'R\_3T]?;W^/GZ^_S^ +M_P`!`@,$!08'"`D*"PP-#@X/#Q`0$1$1$1$1$1$0$`\/#@T,#`L*"0<&!00# +M`0#__OW[^OGX]_;U]//R\O'Q\/#O[^_O[^_O[^_P\/'Q\O+S]/3U]O?X^?K[ +M_/W^_P$"`P0%!@<("0H+#`T.#@\0$!$1$1$1$1$1$!`0#PX.#0P+"@D(!P8$ +M`P(!`/[]_/OZ^?CW]O7T\_+R\?'P\/#O[^_O[_#P\/#Q\?+S\_3U]O;W^/GZ +M^_S]_O\``0($!08'"`D*"@L,#0T.#P\0$!`0$1$1$!`0$`\/#@T-#`L*"0@' +M!@4$`P$`__[]^_KY^/?V]?3T\_+Q\?#P\/#O[^_O\/#P\/'Q\O/S]/7V]_?X +M^?K[_?[_``$"`P0%!@<("0H+#`T.#@\/$!`0$1$1$1$0$!`/#PX-#0P+"@D( +M!P8%`P(!`/_]_/OZ^?CW]O7T]//R\O'Q\?#P\/#P\/#Q\?'R\O/S]/7U]O?X +M^?K[_/W^_P`!`@,$!08&!P@)"@L,#`T-#@X/#P\/#P\/#P\/#@X.#0P,"PH) +M"`<&!00#`@$`__[]^_KY^/?W]O7T\_/R\O'Q\?#P\/#P\/'Q\?+R\_/T]?7V +M]_CY^OO[_?[_``$"`P0%!@<("0D*"PP-#0X.#P\/#Q`0$`\/#P\.#@T,#`L* +M"0@'!@4$`P(!`/[]_/OZ^?CW]O7U]//S\O+Q\?'Q\?#P\?'Q\?+R\O/T]/7V +M]O?X^?K[_/W^_P`!`@,$!`4&!P@)"@L+#`P-#0X.#@\/#P\/#PX.#@T-#`P+ +M"@D(!P8%!`,"`0#__OW\^_KY^/?V]?3T\_+R\O'Q\?'P\/'Q\?'Q\O+S]/3U +M]O;W^/GZ^_S]_O\``0(#!`4&!P<("0H+"PP-#0X.#@\/#P\/#P\.#@X-#0P, +M"PH)"`@'!@4$`@$`__[]_/OZ^?CW]O;U]/3S\_+R\O'Q\?'Q\?+R\O/S]/3U +M]?;W]_CY^OO\_?[^_P`!`@,$!08'"`D*"@L,#`T.#@X/#P\/#P\/#P\.#@T- +M#`L+"@D(!P8%!`,"`0#^_?S[^OGX]_?V]?3T\_+R\O'Q\?'Q\?'Q\O+R\_/T +M]?7V]_CX^?K[_/W^_P`!`@,$!`4&!P@)"@H+#`P-#0X.#@X/#P\.#@X.#0T, +M#`L*"@D(!P8%!`,"`0#__OW\^_KY^/?V]O7T]//S\O+R\O+Q\?'R\O+R\_/T +M]/7U]O?W^/GZ^_S\_?[_``$"`P0%!@<'"`D*"@L,#`T-#0X.#@X.#@X-#0T, +M#`L+"@D("`<&!00#`@$`__[]_/OZ^?CW]_;U]?3S\_/R\O+R\O+R\O+S\_/T +M]/7U]O?W^/GZ^_S\_?[_``$"`P0$!08'"`@)"@H+"PP,#`T-#0T-#0T-#`P, +M"PL*"@D(!P<&!00#`@$`__[^_?S[^OGX^/?V]O7U]/3T\_/S\_/S\_/T]/3U +M]?;V]_?X^?GZ^_S\_?[_``$!`@,$!04&!P@("0H*"PL,#`P,#`T-#0T,#`P, +M"PL*"@D("`<&!00#`P(!`/_^_?S[^OKY^/?W]O;U]?3T]/3T\_3T]/3T]/7U +M]O;W]_CY^?K[^_S]_O__``$"`@,$!04&!P<("0D*"@H+"PL+"PP,#`L+"PL* +M"@H)"0@'!P8%!00#`@(!`/_^_?W\^_KZ^?CX]_?V]O7U]?7U]/3U]?7U]?7V +M]O?W^/CY^?K[^_S]_?[_```!`@,#!`4&!@<'"`@)"0H*"@L+"PL+"PL+"@H* +M"0D)"`@'!@8%!`0#`@$!`/_^_?W\^_OZ^?GX^/?W]O;V]?7U]?7U]?7U]O;V +M]_?X^/GY^OK[_/S]_O[_```!`@,#!`4%!@8'!P@("`D)"0D)"@H*"0D)"0D) +M"`@'!P8&!04$!`,"`@$``/_^_OW\_/OZ^OGY^/CW]_?V]O;V]O;V]O;V]O?W +M]_CX^/GY^OK[_/S]_?[__P`!`0(#`P0$!04&!@<'"`@("0D)"0D)"0D)"0D) +M"`@(!P<&!@4%!`,#`@$!`/___OW\_/O[^OKY^?CX]_?W]_;V]O;V]O?W]_?X +M^/CY^?KZ^_O\_/W]_O__``$!`@(#!`0%!08&!P<'"`@("`D)"0D)"0@("`@( +M!P<'!@8%!00$`P,"`@$``/___O[]_/S[^_OZ^OGY^?CX^/CX^/CX^/CX^/CY +M^?GZ^OO[^_S\_?W^_O\```$!`@(#`P0$!04&!@8'!P<'"`@("`@("`@(!P<' +M!P8&!@4%!`0#`P("`0$``/___OW]_/S[^_OZ^OGY^?GX^/CX^/CX^/CX^?GY +M^?KZ^OO[_/S]_?[^__\```$"`@,#!`0%!04&!@8'!P<'!P<("`<'!P<'!P8& +M!@4%!00$`P,"`@$!``#___[]_?S\^_O[^OKZ^?GY^/CX^/CX^/CX^/CY^?GY +M^OKZ^_O\_/W]_O[__P```0$"`@,#!`0%!08&!@<'!P<'!P<'!P<'!P<'!@8& +M!04%!`0#`P("`0$``/_^_OW]_/S[^_OZ^OGY^?GX^/CX^/CX^/CX^/GY^?GZ +M^OO[^_S\_?W^_O__```!`0("`P,$!`4%!@8&!P<'!P<("`@("`@'!P<'!P8& +M!@4%!`0#`P("`0$``/_^_OW]_/S[^_OZ^OGY^?GX^/CX^/CX^/CX^/CY^?GZ +M^OK[^_S\_?W^_O__```!`0(#`P0$!04%!@8'!P<'"`@("`@("`@("`@'!P<& +M!@8%!00$`P,"`@$!``#__O[]_?S\^_O[^OKY^?GX^/CX^/CX^/CX^/CY^?GZ +M^OK[^_S\_?W^_O__```!`0("`P,$!`0%!08&!@<'!P<'!P<(!P<'!P<'!P8& +M!@8%!00$!`,#`@(!`0``___^_OW]_/S[^_OZ^OKY^?GY^/CX^/CX^/CY^?GY +M^?KZ^_O[_/S]_?[^__\```$!`@(#`P0$!04%!@8&!P<'!P<("`@("`<'!P<' +M!@8&!04$!`0#`P(!`0``___^_?W\_/O[^_KZ^?GY^/CX^/CX^/CX^/CX^/GY +M^?KZ^OO[_/S]_?[^__\```$!`@(#`P0$!04%!@8&!@<'!P<'!P<'!P<'!P8& +M!@8%!00$!`,#`@(!`0``___^_OW]_/S[^_OZ^OKY^?GY^/CX^/CX^/CX^?GY +M^?KZ^OO[_/S\_?W^_O__```!`0("`P,$!`4%!08&!@8'!P<'!P<'!P<'!P8& +M!@8%!00$!`,#`@(!`0``___^_OW]_/S\^_OZ^OKZ^?GY^?GY^?GY^?GY^?GZ +M^OK[^_O\_/W]_?[^__\```$!`@(#`P0$!04%!@8&!P<'!P<'!P<'!P<'!P<& +M!@8%!04$!`,#`@(!`0``___^_OW]_/S\^_OZ^OKZ^?GY^?GY^?GY^?GY^?GZ +M^OK[^_O\_/S]_?[^__\```$!`@(#`P0$!04%!@8&!P<'!P<'"`@'!P<'!P<& +M!@8&!04$!`,#`@(!`0``___^_OW]_/S[^_OZ^OKY^?GY^?GX^/CX^?GY^?GZ +M^OKZ^_O[_/S]_?[^__\```$!`@(#`P0$!`4%!@8&!@<'!P<'!P<'!P<'!@8& +M!@4%!00$`P,#`@(!`0#___[^_?W]_/S[^_KZ^OGY^?GY^/CX^/CX^/CY^?GY +M^?KZ^OO[_/S\_?W^_O__```!`0("`P,$!`4%!08&!@<'!P<'!P<'!P<'!P8& +M!@8%!04$!`,#`@(!`0``___^_?W]_/S[^_KZ^OGY^?GY^?CX^/CX^/GY^?GY +M^OKZ^_O\_/S]_?[^__\```$!`@(#`P,$!`4%!08&!@8'!P<'!P<'!P<'!@8& +M!@4%!00$!`,#`@(!`0``___^_OW]_?S\^_O[^OKZ^?GY^?GY^?GY^?GY^?KZ +M^OK[^_O\_/S]_?[^__\```$!`@(#`P0$!`4%!08&!@<'!P<'!P<'!P<'!P8& +M!@8%!00$!`,#`@(!`0``___^_OW]_?S\^_O[^OKZ^OKY^?GY^?GY^?GY^OKZ +M^OO[^_O\_/W]_?[^__\```$!`@(#`P,$!`4%!08&!@8&!P<'!P<'!P<&!@8& +M!04%!00$`P,"`@$!`0``___^_OW]_?S\^_O[^_KZ^OKZ^?GY^?GY^?GZ^OKZ +M^OO[^_S\_/W]_?[^__\```$!`0("`P,$!`0%!04&!@8&!@8'!P<&!@8&!@8& +M!04%!`0$`P,"`@$!``#___[^_?W]_/S[^_OZ^OKZ^?GY^?GY^?GY^?GY^?KZ +M^OK[^_O\_/S]_?[^____```!`0("`P,#!`0$!04%!@8&!@8&!@8&!@8&!@8& +M!04%!`0$`P,#`@(!`0``___^_OW]_?S\^_O[^OKZ^OGY^?GY^?GY^?GY^?GY +M^OKZ^_O[_/S\_?W^_O__```!`0("`@,#!`0$!04%!@8&!@8&!@<'!P8&!@8& +M!@8%!04$!`0#`P("`0$```#___[^_?W\_/S[^_OZ^OKZ^OKY^?GY^?GY^OKZ +M^OK[^_O[_/S]_?W^_O__`````0$"`@,#`P0$!04%!08&!@8&!@8&!@8&!@8& +M!@4%!04$!`0#`P("`0$!``#___[^_OW]_/S\^_O[^_KZ^OKZ^OKZ^OKZ^OKZ +M^OO[^_O\_/S]_?W^_O__`````0$"`@,#`P0$!`4%!04&!@8&!@8&!@8&!@8& +M!04%!00$!`,#`P("`0$!``#___[^_OW]_?S\_/O[^_KZ^OKZ^OKZ^OKZ^OKZ +M^OK[^_O[_/S\_?W]_O[__P````$!`@("`P,$!`0$!04%!04&!@8&!@8&!04% +M!04%!`0$`P,#`@(!`0$``/____[^_?W]_/S\^_O[^_KZ^OKZ^OKZ^OKZ^OKZ +M^OK[^_O\_/S]_?W^_O[__P```0$!`@("`P,$!`0$!04%!04%!08&!@4%!04% +M!04$!`0$`P,#`@(!`0$``/____[^_OW]_?S\_/O[^_O[^OKZ^OKZ^OKZ^OKZ +M^_O[^_O\_/S]_?W^_O[__P````$!`0("`P,#!`0$!`4%!04%!04%!04%!04% +M!04$!`0$`P,#`@("`0$```#____^_O[]_?W\_/S\^_O[^_O[^OKZ^OKZ^_O[ +M^_O[_/S\_/W]_?[^_O___P```0$!`@(#`P,$!`0$!04%!04%!04&!@8%!04% +M!04%!`0$!`,#`P("`@$!````___^_O[]_?W\_/S\^_O[^_OZ^OKZ^OKZ^OO[ +M^_O[^_S\_/S]_?W^_O[___\```$!`0("`@,#`P0$!`0%!04%!04%!04%!04% +M!04%!`0$!`,#`P("`@$!`0``_____O[]_?W]_/S\^_O[^_O[^OKZ^OKZ^OO[ +M^_O[^_O\_/S]_?W]_O[^__\````!`0$"`@(#`P,#!`0$!`0%!04%!04%!04% +M!00$!`0$`P,#`P("`@$!`0``_____O[^_?W]_/S\_/O[^_O[^OKZ^OKZ^OKZ +M^OK[^_O[^_S\_/W]_?[^_O___P```0$!`@(#`P,#!`0$!`4%!04%!04%!04% +M!04%!`0$!`,#`P("`@$!`0``_____O[^_?W]_/S\^_O[^_O[^OKZ^OKZ^OKZ +M^_O[^_O\_/S\_/W]_?[^____`````0$"`@(#`P,#!`0$!`4%!04%!04%!04% +M!04%!04$!`0$`P,#`@("`0$```#____^_OW]_?W\_/S\^_O[^_O[^_KZ^OK[ +M^_O[^_O[_/S\_/W]_?[^_O___P```0$!`@("`P,#!`0$!`4%!04%!04%!04% +M!04%!04$!`0$`P,#`@("`0$!````_____O[]_?W]_/S\_/O[^_O[^_O[^_O[ +M^_O[^_O\_/S\_/W]_?[^_O[___\````!`0("`@(#`P,$!`0$!`0%!04%!04% +M!04%!04$!`0$`P,#`P("`@$!````_____O[^_?W]_/S\^_O[^_O[^OKZ^OKZ +M^OKZ^_O[^_O\_/S\_?W]_O[^__\````!`0$"`@(#`P,$!`0$!04%!04%!04% +M!04%!04$!`0$`P,#`P("`@$!````_____O[]_?W\_/S\^_O[^_KZ^OKZ^OKZ +M^OKZ^OO[^_O[_/S\_?W]_O[^____`````0$!`@("`P,#`P0$!`0%!04%!04% +M!04%!04%!`0$!`0#`P,"`@(!`0$``/____[^_OW]_?S\_/S[^_O[^_O[^OKZ +M^OKZ^_O[^_O[_/S\_/W]_?[^_O___P````$!`@("`P,#`P0$!`0$!04%!04% +M!04%!04%!00$!`0$`P,#`@("`0$!````___^_O[]_?W]_/S\_/O[^_O[^_O[ +M^_O[^_O[^_O\_/S\_?W]_?[^_O___P````$!`0("`@,#`P0$!`0$!04%!04% +M!04%!04%!00$!`0$`P,#`@("`0$!````_____O[^_?W]_/S\_/S[^_O[^_O[ +M^_O[^_O[^_O\_/S\_/W]_?[^_O[__P````$!`0("`@(#`P,$!`0$!`4%!04% +M!04%!04$!`0$!`0#`P,#`@("`0$!````_____O[^_?W]_?S\_/S[^_O[^_O[ +M^_O[^_O[^_O[_/S\_/S]_?W]_O[^____`````0$!`@("`@,#`P,$!`0$!`0$ +M!`4%!`0$!`0$!`0$`P,#`P("`@$!`0``_____O[^_?W]_/S\_/O[^_O[^_OZ +M^OKZ^_O[^_O[^_O\_/S\_?W]_O[^____`````0$!`@("`@,#`P,$!`0$!`4% +M!04%!04%!00$!`0$!`,#`P,"`@(!`0$```#___[^_OW]_?W\_/S\^_O[^_O[ +M^_O[^_O[^_O[^_O\_/S\_?W]_?[^_O___P````$!`0("`@,#`P,$!`0$!`4% +M!04%!04%!04%!`0$!`0$`P,#`P("`@$!`0``_____O[^_?W]_?S\_/S\^_O[ +M^_O[^_O[^_O[^_S\_/S\_?W]_?[^_O___P````$!`0("`@,#`P,$!`0$!`0% +M!04%!04%!04%!`0$!`0#`P,#`@("`0$!````_____O[^_?W]_?S\_/S[^_O[ +M^_O[^_O[^_O[^_S\_/S\_/W]_?W^_O[___\````!`0$"`@(#`P,#!`0$!`0$ +M!04%!04%!04%!00$!`0$!`,#`P("`@$!`0```/___O[^_?W]_/S\_/O[^_O[ +M^_OZ^OKZ^_O[^_O[^_O\_/S\_/W]_?[^_O____\````!`0$"`@(#`P,#!`0$ +M!`0$!04%!04%!04%!`0$!`0$`P,#`@("`0$!````___^_O[]_?W\_/S\^_O[ +M^_O[^OKZ^OKZ^OK[^_O[^_O\_/S\_?W]_O[^____`````0$!`@("`P,#`P0$ +M!`0$!04%!04%!04%!04%!`0$!`0#`P,"`@(!`0$``/____[^_OW]_?S\_/S[ +M^_O[^_O[^_O[^_O[^_O[^_O\_/S\_?W]_?[^_O___P````$!`0("`@,#`P0$ +M!`0$!04%!04%!04%!04%!04%!`0$!`,#`P("`@$!`0``_____O[^_?W]_/S\ +M^_O[^_O[^_KZ^OKZ^OO[^_O[^_S\_/S]_?W^_O[___\````!`0$"`@(#`P,$ +M!`0$!04%!04%!04%!04%!04%!04%!`0$!`,#`P("`0$!``#____^_OW]_?S\ +M_/S[^_O[^_KZ^OKZ^OKZ^OK[^_O[^_S\_/W]_?W^_O[___\```$!`0("`@,# +M`P,$!`0$!`4%!04%!04%!04%!04$!`0$!`,#`P("`@$!````___^_O[]_?W\ +M_/S[^_O[^_OZ^OKZ^OKZ^OKZ^_O[^_O\_/S\_?W]_?[^_O___P```0$!`@(" +M`@,#`P0$!`0$!04%!04%!04%!04%!`0$!`0#`P,"`@(!`0$``/____[^_OW] +M_?S\_/O[^_O[^OKZ^OKZ^OKZ^OO[^_O[^_S\_/S]_?W^_O[___\````!`0$" +M`@(#`P,#!`0$!`4%!04%!04%!04%!04%!`0$!`0#`P,"`@(!`0$``/____[^ +M_OW]_?S\_/S[^_O[^_O[^_O[^_O[^_O[^_O\_/S\_?W]_?[^_O__`````0$! +M`@("`P,#`P0$!`0%!04%!04%!04%!04%!04%!`0$!`,#`P("`@$!`0``____ +M_O[^_?W]_/S\_/O[^_O[^_O[^_O[^_O[^_O[_/S\_/S]_?W^_O[___\````! +M`0$"`@(#`P,#!`0$!`0%!04%!04%!04%!04%!00$!`0#`P,#`@("`0$```#_ +M___^_O[]_?W\_/S\^_O[^_O[^_O[^_O[^_O[^_O[_/S\_/W]_?W^_O[___\` +M```!`0$"`@("`P,#`P0$!`0$!04%!04%!04%!`0$!`0$`P,#`P("`@$!`0`` +M_____O[^_?W]_/S\_/O[^_O[^_O[^OKZ^OO[^_O[^_O\_/S\_?W]_?[^_O__ +M_P````$!`0("`@(#`P,#!`0$!`0$!04%!04%!00$!`0$!`,#`P,"`@(!`0$` +M``#____^_O[]_?W\_/S\^_O[^_O[^_O[^_O[^_O[^_O[_/S\_/S]_?W^_O[_ +M____`````0$!`@("`P,#`P0$!`0$!`4%!04%!04%!00$!`0$!`,#`P,"`@(! +M`0$```#____^_O[]_?W]_/S\_/S[^_O[^_O[^_O[^_O[_/S\_/S]_?W]_O[^ +M_O___P````$!`0$"`@(#`P,#`P0$!`0$!`4%!04%!04$!`0$!`0$`P,#`P(" +M`@$!`0```/____[^_O[]_?W]_/S\_/S\_/O[^_O[^_O\_/S\_/S\_/W]_?W^ +M_O[^____``````$!`0("`@(#`P,#`P0$!`0$!`0$!`0$!`0$!`0$!`0#`P,# +M`@("`0$!````_____O[^_?W]_?S\_/S\_/O[^_O[^_O[^_O[^_S\_/S\_/W] +M_?W^_O[^____``````$!`0$"`@("`P,#`P,$!`0$!`0$!`0$!`0$!`0#`P,# +M`P("`@$!`0$```#____^_O[^_?W]_?S\_/S\_/O[^_O[^_O[^_O[_/S\_/S\ +M_?W]_?W^_O[^____``````$!`0$"`@("`P,#`P,$!`0$!`0$!`0$!`0$!`,# +M`P,#`@("`@$!`0$```#____^_O[^_?W]_?S\_/S\_/S\_/S\_/S\_/S\_/S\ +M_/W]_?W]_O[^_O____\````!`0$!`@("`@(#`P,#`P,#!`0$!`0$!`0$`P,# +M`P,#`P("`@("`0$!`0```/_____^_O[^_OW]_?W]_?W]_/S\_/S\_/S\_/W] +M_?W]_?W]_O[^_O[_____``````$!`0$"`@("`@,#`P,#`P,$!`0$!`0$!`0$ +M`P,#`P,#`P("`@(!`0$`````______[^_O[]_?W]_?W\_/S\_/S\_/S\_/S\ +M_/W]_?W]_?[^_O[^_____P`````!`0$!`0("`@("`P,#`P,#`P,#`P,#`P,# +M`P,#`P,"`@("`@$!`0$!`````/_____^_O[^_OW]_?W]_?W]_?W]_?S\_?W] +M_?W]_?W]_?W]_O[^_O[^_____P```````0$!`0$"`@("`@("`P,#`P,#`P,# +M`P,#`P,"`@("`@(!`0$!`0````#______O[^_O[]_?W]_?W]_?S\_/S\_/S\ +M_/S]_?W]_?W]_?[^_O[^_O______```````!`0$!`0("`@("`@(#`P,#`P,# +M`P,#`P,#`P("`@("`@(!`0$!`0````#_______[^_O[^_OW]_?W]_?W]_?W] +M_?W]_?W]_?W]_?W]_O[^_O[^______\```````$!`0$!`@("`@("`@,#`P,# +M`P,#`P,#`P,#`P,"`@("`@(!`0$!`0``````_______^_O[^_O[^_?W]_?W] +M_?W]_?W]_?W]_?W^_O[^_O[^________`````````0$!`0$!`@("`@("`@(# +M`P,#`P,#`P,#`P,#`@("`@("`@(!`0$!`0``````_______^_O[^_O[^_?W] +M_?W]_?W]_?W]_?W]_?W]_?[^_O[^_O[_______\``````0$!`0$!`@("`@(" +M`@(#`P,#`P,#`P,#`@("`@("`@(!`0$!`0``````_______^_O[^_O[]_?W] +M_?W]_?W]_?W]_?W]_?W]_?W^_O[^_O[^_O_______P```````0$!`0$!`0(" +M`@("`@("`@("`@("`@("`@("`@("`0$!`0$!``````#________^_O[^_O[^ +M_?W]_?W]_?W]_?W]_?W]_?W]_?W]_O[^_O[^_O_______P```````0$!`0$! +M`@("`@("`@("`@("`@("`@("`@("`@("`0$!`0$!````````_________O[^ +M_O[^_O[^_O[^_?W]_?W]_?W]_?[^_O[^_O[^_O[_________`````````0$! +M`0$!`@("`@("`@("`@("`@("`@("`@("`@("`@$!`0$!`0````````#_____ +M_____O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^__________\````````` +M`0$!`0$!`0$"`@("`@("`@("`@("`@("`@("`@$!`0$!`0$!`````````/__ +M_________O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^____________```` +M``````$!`0$!`0$!`@("`@("`@("`@("`@("`0$!`0$!`0$!``````````#_ +M__________[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O________\` +M``````````$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$````````` +M``#______________O[^_O[^_O[^_O[^_O[^_O[^_O[^_O[^_O__________ +M_P```````````0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$````` +M````````_________________O[^_O[^_O[^_O[^_O[^_O______________ +M_P`````````````!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M``````````````#____________________^_O[^_O[^________________ +M____``````````````$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0`` +M``````````#______________________O[^_O[^_O[^_O[^_O[_________ +M________```````````````!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$!`0`` +M`````````````/________________[^_O[^_O[^_O[^_O[^_O[^_O[^_O__ +M_____________P```````````````0$!`0$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$`````````````````______________________[^_O[^_O[^_O[^_O__ +M_________________P````````````````$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$`````````````````________________________________ +M_________________P````````````````$!`0$!`0$!`0$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$!``````````````````#_________________________ +M________________________``````````````````````$!`0$!`0$!`0$! +M`0$!`0$!`0$!`0$``````````````````````/______________________ +M_____________________________P`````````````````````````````` +M``````````````````````````````````#_________________________ +M______________________________\````````````````````````````` +M`````````````````````````````````````````/__________________ +M____________________________```````````````````````````````` +M`````0$!`0$!`0$!`0`!``````````````````````````````````#_____ +M____________________________```````````````````````````````` +M```````````````````````````````````````````````````````````` +M______________________________\````````````````````````````` +M`````````````````````````````````````````````````````````/__ +M__________________________________________\````````````````` +M````````````````````````````````0T]-30```!(``0``1H``"$`-K$0` +M``````!-05)+`````@``24Y35````!0\``!_`'\``````````````````$%0 +M4$P```&H4V0R80`"``````````````%````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````"P```````````````` +M``````````````````````````````````````````````````````!QV?P` +M````````<````````````````````&```)VX````<=KD`'':4`!QVDC__P!Q +MVA"J5:I5!`@```````D`(P````````````````!X````!``$``````!6_@#_ +M__^``````0$@!E%5;FET@``"1Y0050C```)*DQ74E<````` +M``$"1@8@T]B +M:D1IT]B +M:D1I'1R86QE +M=BYC+F\@M@T@("`@("`@(")[3V)J1&ER?2)F:6QE_["@GY[@0:`>L$&`KO_1L`[0<-`?CQ]@L)\/G][PT4Z/(; +M"?`$%`@!!P0)#?GY%17KY2`AV^,5!.KT`0']`?WQ_Q,"\@<,\@(>`?$.%/_\ +M#@GX``3U_`+W]@#_Z_L:\N0=!]\.&NOV'@GU$`GP#Q[P[A4"Z@(&[_<(\O0, +M]N8#%/OD_A@&\0`7"/D1&0#Y!@T"]OT!]/7]\_7V\OGOZ?0$"NSI$`O[#@P# +M`@`-$`7]]@80]?$'^>W[\_#_ZN`#`_/Z_`0'_0@2#0D`!Q<7!_0%%`'\]>O^ +M]N/U].;M_`;MV`P"'!GNX__YZO3TZN_P +M]0KUT?DC]-@+'0($&AX/"0\*"`H"`PWUZ`;VT.H,^]?C`/?N[_7]]?`&'PWV +M#2@=_OT3"_T0$?'N_/GP[NSO^O;L]O;N]_+W`_'Z&Q($%AT0_P<=!OL8#/+V +M\/'_\NGQ_?G=[0GOX?CXZP,6`08B%0H8$@`('Q'Y#0;=^2#]V_(&_/;TY^;X +M]N#L#03X%!L,%AD+"0H'#!,/^^P%#N[U$O?B`/K?\?/Y0G]Z1(D"/X/%O?]*QC]$@+R(!_O^ACZUNX#YM#=[N#7^`OW +M\@8;(@7M!R(C#_D,&0X4"?L>(>GA_O7I[-O.V^/P!?[J\QTK`.P0'!,5#/\% +M)"#Y$S<&Y`0$XN;HS-;OXMWW"?KV$Q;X`R$1`Q49#A,@$1@^%_(C#\?F$=J_ +MZ=S#]07>\Q;RZ1<:[NXN-//W*RD2'C8H"@$$`?/FX]S4U=SDZ.[Z_NKG#Q#E +M_R\4^1`>)#0E#2U'"]0(*-R][^G"V>O>\_O5WA7_UP@9\PLD!@\_+`7:W_WR\PH!#C4@ +M$$E6)RY,&^T'%OK7R];J\=/.[./-U.GNV^8+_O`C-1]TM7'T_KUT/0J)"HX-%9F/"0O +M.A7R!_+?`N>^V^/0OZO.Y,;.U=SW]PPG)#,]6&PN(%LM*C*\-;T*BU'85Q28'1;."LA +M'0_'@H&'`3X`O/QSK#-Q*VOKJ^WY03C^S,M,#U#34%%23`^ +M-Q(T*/'\[,[)K:BJEYNDKKW2`/X"1$8B-DI-0R,P/`D'+A/Q!O[0U>3(N\C) +MQKMY_$0%0DB.!X9/D`F,40P +M*34E$A'^W-;CSZ:\V:ZRYM+0^>76`B,6"S!%+SE<5T!!,R41]O;.J\BZEZ>^ +MO[70Y>$.#^ +MU<&WX=:IW`/0W1/WYAPU#@],.P,O02$P(@;[\@'VULO7Y.7KW-3[]M;A[/#N +M\A(1`APE&R$;,5(M"1HE$_WK^?S.TNK7U^+%K=+KU>0&^00T.2DP-SI`-!82 +M)`7K[=_AV-+5P=WRU]O?Y?3T$"L:#AP^-@TF/`T"$PP,\>#ZZ^+OV>GNR^3M +MT,K1[OOM_AH9$B$Q)"M8-`<\0A8G(?#P^.'/T(+[^,/$_X;/R8<-S-$1R,?$`@/XM;MUL.YNLR_Q]S+\1WT +M\"<=!2HM$"(L)#"M_6].&\WN/#S][QY-00&@4>&2DZ-E]+%S,K"P?FX?/?S\K'S\W6 +MWM'F]^;T`O8#'P;L+SL(-T@>'R$8"@L8!OT%[_H,[.3:S.KJT]/&Y`7>W@H0 +M%2LS*B,Z)@(2#/?_`^?<]_7HZ];=_O+J`@S_$#(:""\X(R`3!`P`VM'K\-;< +M]>7G!_CH__/N"0+[^_H1"@`6$!(I(`4((!7U`Q<&`PX#``[RW_K>R^;3U?'= +MX0/Z]!0<&Q<0*S`7!@`(%A+Y\0\8\>;N[?7FZ_;@_17R!1?Y#A__!!/U_1[[ +MW.\#$OWM#P+G"03G[??WX.<,]?,:_O$B&P +M\\S-\.OJ_`/T"1_^!C4:_R,8!B,5`QH'\_<(`0`%("8)%CXI$"P>#B$D#_D,\;[P_L_AZ.?;S0\` +MRO_LT",3WQRP#RU``A&Q(D.C,?&QTZ//T!&_WJ_`S=V1/RS@`+Z=/H#>;("R\/ +M_@\M'Q$?$2`I[^L.^L_/#>VP]NK&]^+:^_OKWAH_!B]2%C%)0$@/'#7:!C3" +MTO[.SM'/R=S_RK/Q^MC;`!@%""X?"!XT/1X+(R4D"/0?$N3Q"/KM!_;!W03U +MY=_L`@+_!`07*AL-"A('[MWEQ]K:R`G_Q.L)!!`5%!SN_AM:S2^MF]\/'9__?B^?GX`QG[WT`Y[#$W!SU!$!(;#/\#]^02"\OL +M_]_C[>[3O^[SW`L;#B0>$BLY&?/Z!O[7J-H0V;SQ!O<+(!(&+BWT&C$/+3$/ +M'Q?[$1'CV=_'O]_CM.4MY]\J)0']&0?7\OW9[PL0$`4E-R@P(Q\H_.?Z!0CI +MX?;K[/OR\.S=Y/?LUP`U%@D_.1(H(O/V[-#+M;S>X.;R`!XG(T1,&!P^'`$( +M"10/ZMW[!^_?V][HVLSC``P4#A0F`P$G]MW_[;A$P(.4E%# +M7UDQ*2$*"-.N[-N@TNSD__8"(A3WZQDKZ.GD[?P3(1$%#P+F]_7'U?+4O='PY^+X\_(#&!L1*S8K +M,R/PV-4!_=K[&_;9XNG>VO#QU.D'\`8?``@;%!8*!@_[ +M&#@'$S\P*"$AGW +M\Q-`%Q-;+`I&,`@;%??EY,>GR-:EK_3\UNLC)0T:+A0'(PWB_P;7U^GGZ@`@ +M!ODO)QPZ*S=-)@,)%/?-U]RVOLZ_W//N^AHC`QU(&_T)\^?XX,K7W.KZ[NT5 +M0R\',F,S"$)(_P#ZQM+@O\;HS[OV#^'R,S`,%AL=(P`(`]'S#M3,_/?L!O+I +M'"+`X,)0/M[.T`YM[WX>T,Z=GP#0WR +M`O_^(@SN_/OM^O[F]`D&&1X`!$A<$/PR*?KHX^?Q[M3.]`/Q_@D'"@`'^]\( +M#,K1X-<`^K4W.W: +MPL?-V=SF#Q41,CHL+5)@&`U$*03\!AOU]Q;X^PD&^=0!#\'P!Z"_^,:UO-': +MU>[Y,J^YQ$!`RL])18]0!0<*18"[=_F]M6GO=?9V,WH"O\.)1TM +M-ATB(@@0[=4$Z=`,)0^PTJ+A@: +M*CL@^@?\^B$$V_<*_>/7Y^SYY;38__#_$@,",#H*!B]+(@@D"1DX]?4O#N#F +MY,.QQ\^WL+##\N[I.5TD)%$Z'34Y'0P/!O0`^@@O_N($\N#=T][EV]'-W?'T +M`/;L&A#M#1@?*BY9125032`!^P#/H*>EHKC$T.O]%#T%W.0O+/L,)1+X +MZ-?(WNK1UNP`'A<&(4%`+2`C)186$^C?"`O-M.?>L[[!PN<"^/@D0T-!.C0Y +M.@[H`_C.U>#?W.,!#@'^#A<(ZN/X_>S@ZP<*]/<2(Q']#1<6&Q0+$AL9!P41 +M\-CKW<.VL]?AS?D?$1LF*#XI%2P1_P_LX@(%^_G]`?KV!@/;ZP[DX0[_`!H) +M`PH#!0D%_O/Q"13W^QP:`?<"!_KGX.7EW]CA]@\;``8F*"D-`R@8_1<)]A@3 +M]/\3_>;^_,W,[>;+U.[LZ@L'Z`D>#Q<%_20D%QDA+2`C'P<<&-W9[N/>S]+R +MZ_3[\!@*W/D-^>OS^?P)!`\B%!H="QX5\/@`__7G`/_R#!(#\>?Y^_/]^0(6 +M#Q,7%2<>^./[!,.[\=6[Z/WM\1,B#`P@#`LB`@P\&08D'14A(@+R"/WM\>CF +MZ>WNZ>':ZN_:R=+DY>?Q#B<6'4A%0#X<*CX;`/W]\N;DX>OR]O7U!^[9_0/Z +M\>[OW.X(^0HC$@L,`^CA +M\=?0Y-WK]_,0'!\H#Q`N&`@2_Q8N]NT/&0G>[/[?X^/9V^#Y_?#W$1H)&AGR +M!1#G^/_:`S`'\!4R*10:)18$_.C>_?S3Z`'E\P?P[_?_`<_2_^KD`P\<*1X) +M&2D%]A("Y04(ZA8['04;,@SG[?'LV,K;ZO3T^0T-`O_\].3(PD4$>X'`VN+BT+P%!L.$`P+#@X0`1HW!0M**1@X$`<4Z.WP +MU.3I\@#H`"P9`OS[`-J[T-/&P,;H_@`#!1XU'@P?-C<@($`\)SM&'O\+".#' +MR=')M+K:\?/Z`0`.\M#FZ=S+X%Q@2-4Y"0%!((18P-@WP#ASVXNCDV,K% +MNIVVX,7#[`<9%A@;%B@G"?[T\?SQZ@4?)R0A.#TK)`\1'?OM[^3S`/'8X_W9 +MN]GFR/?X>'Q^>+R"P\B'BQ<3C=68D`P.1GT^NW:Z_OWXNOSVN#KW79 +MU`D4_P@;+2LB'R9`1#8]7F,[,BP-!^C*V,VYN\[6QM;^\N'X\>KYXM;R__3Y +M"0,/-!WX&2L>#P(?(Q%"$T/RXB,349$!T)!AP2`._?X]:ON->ZM,/%Z?_\_0$5%@4)#P\< +M*1P7+SLP+3@Z*Q/^]NK=W-GM[\K?!-C-^.;)U]SD[^OR`1HU(10L&@\M'OK_ +M$A8-!0\A,2P/`O[N[_#8TN_OUM;<[_OBXOCHRNOYRO@BZ_DB&R(B)"\H+"@( +M&282-S3Y"B@7`?;JY/#?LJFVRM?*L,+[[-?_]_\?_O\C'A8A/#@M5DDD-BTA +M*QS_Z?H1[-/_`^7SY+S`SLFUNK6T/07^N\K,`\E-AL"#"L<^`88"PH0"!HG$A`3$@GRZ]SF +M"NB[V?[@R??VV/3]YMCL#@$*)A(-,#$)%#TC^P@2_/49)0,%&`+_#^O:_N7' +MU<_4Y>K?WPD#X`0?^>T.$00<*!\R^;?Z`,4 +M!OL@+Q\<(2PP+2(=*CPQ"0P?!/X$]?#T"`/=Y?37TN36Q,[SL]>G?T-;;R-;7N=3YWMGZ\N;_`/8* +M%0X;-BPC/$-#/3$R(14:%!\3\`4$Y>O+GZ=KAQK[7PL#6W.+&@\,_^;IZMG,U?#GR]GLY.T`\MKN$`?W^!8L$A`:!PD8$0('$Q4%_14I +M(B`G%_X%_-WDZ,K$XM.\ZOK5Z1#VZ0P&\@\;"A$1`Q,F%PT7%`<7*1(4,R86 +M*!@`!/'>XWVX@0P'2,O+40Q%24B'BT@"A4:%!`%!Q8<"/+_ +M[\SGZL?:WKS#Q\/;XN7DX0PG$AXW1E,U(2T>%AH4#_OO`@O[^A`5"@+Y_`#H +MY@+MRMONU$@P5)18%&!\<$P<7'Q\A$@H,#?_M]O??U-;,P,?= +MWM/B]/+[$`S_$"00!A4$!!T(]ADH$P\;+#4F+345$B+[W_3LU]?"M<[&NM+9 +MT]CO`_8%-"X?,"<:+"L@&P<+%04)#@8A.`GW)17=Z?[ET=?6N\KQV=3RW=\( +M_N0#*QH&(S4G&BDU(!01!@L'[?D:!?8;$_'[!OOQ]>C#U?/:T.?PY>3V_/#R +M#@P!$@#_+2\<&1\O-"#_"S(<`!(.\O\,Z.+SX=[7P-/DU-'>Z^#5[03\^`L> +M'@4%,R\#&D8N%"XH%CDW#P\3#@?NU,W=U[ZZM\+9T<_G\.CN^?D)%QDF&A\P +M&Q\Q)!8?*0CX'!@''1<##1+RU^G_Y+JXRM#1V^SD[0;T[O\!$A\."PP$'2@' +M%BD0$Q8+%Q\<$A0M(`@8%PL%Z=C?SK?#TL:\S]3&Z?W1Y"4=]`4U,0P:24$; +M)SHL)BL?$BPG[?(6]][SZ\W*ULZ\PM+=W='-X?H"]OPJ.!01+"$H/1(90!4. +M)!@<&A01^O@`].3=W]O@X\O+\?_FYP#KV0`'X_(/"04!"1P-!A83%!L-`Q]! +M)QH^'P@L%_'P^^W-S;JGY/K+V_;7XPKTWP,8_OH*!`TA'1XD'A49)!P=+BLE +M'10@'0+Y\NS;L*_!L+?+Q,_AZ@$(!!$I/1H)/#(('R\:"1(6`@H;#/\-%_[] +M"O'W$0#IX-O4U>+/Q.SJNM$(_?,;."`5,#(A'28V&>CX#`4-`P0A%_OV_`3_ +M"0C5X`C@S_#UY.?OT+WI^-OC]/H/'QD;.DPM(SP7`S4I^0@?#_P$_.O^".K2 +MVM71W$+#@D#(3`)"18)#Q\4"!$8)R(**T$4'S+S]RSJNO;\Q<';R*_6 +M].'6U>X%\.X,)R<+!1DC&Q$6%!`0^.D<.`\;/RL;(!X8$@P`ZM3`NLC*Q;Z[ +MV.#$UP#^_Q@9#!LO*B0V-R0D&0H2&1@(^QDF^_4@$.\3"\?4]&!``!B``Y._Y#`+P__OOZ=OJ[N#P[M//XNOI +M\?L$#0#S"1X>(QT0&BH9^"%-$_HC'O3T#/'K!>;+W>??X_/JX.7I]/?PZ_8( +M_OP!_A87#1T:$ALB&14Q-QL='!`?$.#K#>FPQ-2WS.#6W>?Q]/P!_ALM$?@' +M)R`("QX@$`D!`A<8$Q@5%Q4/"/X(!>WKW[R]W=K'TMG>\?CX%#(6`BSDUNOLY??U\P/[^@X1$C1.(11'-10H +M)QLF(OSH`0?OYN'AZM2XQN7IU-GU\NG^!?H,%OP!%?7O%`X!&A\&_ATV'1X^ +M*Q$A)@;S`0S^Z^35S.#GUL_3T-#GZ=SS"P@)$`L*%!D9$`X(!AH;&RLN.$`T +M&PD;(?OK[]_)P,_2P,[=P+W?ZN7E^`X<)QD9/CXE-2L"``7U^@K\_!H6_`@: +M%A\8[>4(\\3;`?#7UN7OY^CJ[`0%[OH#`2$M&Q02&!#W`0SW`QS^[R$B"",O +M)!<`!0SNXO/GPKS/V-+/W.?P\>CU_00E)00:22L*+S(>*"<.``T/\/,-_.CV +M]^KJYN_NSM/DVM78ZPD,_@4=(!8@(1XM&_<#%0T2%@H-#?GP[.W[]N38W>K\ +M#?CK%Q[N[!$'Y_\3_O0`#P0+*Q3W`@;Z[O4`_OT#$A$)#R$F#@8'\.__Z^#\ +M]][K\O/SV_T4X.0%__3\(2$.*"(,(BH/_10B"_GM_!T2``3T\??;TN#5V-/! +MW.S;Z`L@%A0C(R(;*$$I"A8#'!H>$Q8S)Q0H'P((`OC^]PP1[P8;^_#[!P?BVOGOW.8`!M[K$.#&Z?#G +M\?/J\?\.(2HP-B89+"X6&Q,,*A[_!!`8$@3RYN;1QL2\Q\R_P]WEW?@(]O\: +M%0$()#X])39.,RDS+!P9&?;I]_/T[O3\X.#TZ]S,SN?1N.7NT_@&^R,P$`L; +M'!P;"@TE#?D9$0HC%`X8"/T%%0'Y$P+R]_SRY0?XO]'AS,+&Y?#E_0/D#4(A +M$1H@*@L)(!,>(2$Q$Q$V*0D+%O??\=_+X>79R75U][.VN\!!_,0(/,3.1,7*!D='10,!A$3!O__\.0, +M&NST(`GC^_W8XOKLR+_6T-;UZ.\/_P8E)B@G)S(H(RT3"BH5`A3WY?[\Z>G] +M`.;C].#;!_G&U/#JT-'N^O/K`A4*&S`J+"TG,#(=&AP.$@L!`M_N&NK0`?[, +MSO#JTMGM\^K?Y?H'^_80"/`/'P@3)"4F*2X6"BD'$//Y"_SGYN;8 +MR-#DVWC_B,-^1`3!Q`6&A,-)2L<%1TO(1,C&_OV$0SZ"`OYW^;PR<36 +MP+C+QL3?Z_8>'/X5-STH"1].)_<@+@@('R4:"0#\!!,`\`+[X-_HV<34T;?3 +MV[+*_?KP`AH;'3HZ)#Q`&R0U!NH1%_7X`_KM\P\0`P@#`@G\W-#K\M71V,K1 +MZ.SM\._V"B(6_RI$&R4_)!`7*"?^\1<-Y?T1`0T-_@K[W=S?W-'(T,S(VN7J +M]OL'"0(4)RLH,$`V,"XM."4?*`#A\O7DZO3CXNK?ZM_1\>+)[??CVNT<%^P# +M)A45&PL=,A+]*#L-^ATS'OWX`OKW^NWQ\.;V\-33Z?GEV^WLY?0-"?0"#P(/ +M%@83)1D(%RL+_B8G$R(/[A,L!^\##>O=[M>YR]7%SM;.W.WU^P(3%A89%R0P +M*R8T+`T4(AX/`A44\>3X"_KM\N[YY\;-T=#.R-;/ROD)^`XA%A\W,R`;.TL/ +M"CL1_!L$^`L$\.3@[__[Y.`)#-O4Z>GAXN+:Y>;E!P?W#!HB(0P:.BP8'B4H +M%009%PT9_^3[$/[L\OKY]>[BV=?=W=W;SMKKZO;V\A,7"Q(/+$6OMN#+PM[8S>;X\P@5"1XJ'"`H+S$F*#;ZS\3@T+?(Z?+BU_`-_OD6(A<6'Q``'R\4"Q\N%_8.*Q`$&!;X +MZ@H?_-_V#?'-S=O5O<;=S=KKW/L3!@P.%AT-%1\5'"\T)B`E&B="'_T)"P?Z +MW>/T\>C2R=*[N-K%N=W8SO05$A$G,3,N("PX)R0O(A04$Q`@(07]^_#H[??C +MVO#JTM/?V=7JZ]?F\>7^&@4!)R(&$RPQ'!8K*!4,`0DG)0+X"@7Z`?WV]_?U +MWM+7T]79V=G9Y.CK`@WY!B8)^B4J'BHX0S48'"4?+QOI_`3@[??=[0/GV=W: +MU\G0Y-O5W^CZ"@D6*Q@$$2`D'ADI."4/(286)B#Z]_O@VNODX.WLX-ODY-WH +MZN3FXNGX_@\.!!H=!@P:&2`E'!T>$Q,?*1\+&1#@[@KLW^WJV]WDUV^?P +M_A0?'AX<"PXF)!HM.2`6)!L1&14,!>W9ZOKNZN[K\??JT,_=W^+BW>/H^108 +M$Q47$Q$1#A,B)!<0+"\""RT8"POKZP;RY?3V]^OJY\G.YN#8WMS&V@\._1(> +M#0D5'B4H+#0Y)PP4*2(;'/SC]O[EW?7PW.'2QM;-Q-SJXN+EY04C'A8.&R01 +M&"4P/R`50S/X`208]>KMX>#W\.#Q_?7RZ]/0[-G)]NO)]!8,"!PF"0XF!_D? +M+2$@'B0C#!$='!'MZO;EZP']]P+_W=?JX-/8U,[.T>/X!107%QX<("$F1#X= +M+T,D"ADH'Q/]Y^+C\N_GHU>D+#ALE%0X>,S,?*4L\(S,I +M"/T'$@/FVN7FV^KT[.3G_NK#V_CMX_CTUN\=&@,-)0SV#A(!"B,U+AXH(PL= +M+POZ#>[7]_KH\OOT[-O4W]W,R^#=P=@'"@(1(AH-$AD/#2PS&"M`)1DC)S(B +M]^[X[-O6Y^S0T-_*PN#:O=+S[-CL%AP<-#LE&20D&R,K)QT;$?X$#PP(!?'? +M[NSA[_+GU]C@S=ORT]\+\,WQ)R46(!L8&!04%1\:$2(A$`L'"Q$&_?GX`/_[ +M!P/Q\?'@T=36P[W:W]'?]@H)$204$B09$!8L030N.S(=$QDJ&.G>^?70T_GT +MV-K6R]7+P-+';UMKG +MZ][=Z>CR`NG1\102#Q,2%1D6#`H8)B@5"2$K#@D;'A4%_?WP[P@!V<_EZ,O` +MT-C=X-OE]/3T!Q@2!`T:"@@B,3(L+STU)Q\=)!@&_N?G__#;W=C6T+>XQ[S! +MVN;@W`,J)"8P*"0J(P\:*R`B)1\L(P(-*@WD[?/=WO#OX>7QY]O;UMG?YNOB +MWNX,&A4<+"<;&`@('1P."!,<#@D6(!T-!@?^]_O^^O7PZ./>VM3-U>3#1TC#/+BZ>WH\/GPY>WUYL[1[.O7V.#B\P(#$1H5%`L* +M&"`H'18U-"$O+R@Q*!#W]@/APN3HO?QYM/3VL[`T^7I^O_W!1`7)R`-%1X0_0DE(B(H +M%1PF"@,/#`;Z[NSN]0@*[/`#X#;X._HWN+?W./=R,?>Z-W:WNG^"`\5%RPO&2,R+#`M)S`E&1`! +M&"#Y[.[AW.+GY=S.W//2N>#UY^O^_NSV%`P`$A4*!@XH)Q8L-R(C+2(*`A@= +M__'PYNP`]=K;W];5R;[5Y.#L]?/_$Q<.$!43$`H4&QPN+R,M+BTB`A`M!=_J +M].[E[.[6WNW0P,G-U^'FX>H+!P0K)`XJ*`L2)"04'289)"D4!PDA'?;KZ.W] +M[]SE[>C@SLS6TM;5V/'LZP81$ALK*A,5-3X@%C;L!.S@\>7B +MZ-K9VL[&U>?8UO#[\NS_%146'AL7*SHG)C@V+"8=!`48"O;N]/;AX./I^M_) +MW]W)R]7>[_;L[?X(!Q4>`_H8+2@7(#\^+3DN$2$?`@7\[NS9V?'UX=#=[].] +MT]G1W_'VZO$1&18>%AD@#Q0C'RHO)"HV+1(/)B0&\_?HT>7RV<[:WM?0PL/@ +MYM[H\_G^"1P;&#`H"24]&QDO'1HP)@X.$A0>">?V!.34YN;-U>*^LMG>R>0# +MZND+#0H7*"D,"RXV(AHK+Q\G+PSX#1L6!.[W_N/H_>O@X-;4PKC4V$0H-&Q/GT.7QV]3>T,7AY+2XZ>[F\_3S!1<<&!HG +M*RDG&"$O&QLJ&!8:`OX3&0_^Z^SXZ-[GVM+DW[BUW>SAY/'R`18!`B@F%1D: +M%!LN+2(B)RPG$P84(!8&[N[YY.'MW]/2SL"WPLG3Y^;A]@0')"P8(C0G'2LU +M*",L*S`O!_PA'@7_Y]?EX=7?W\;0\-BXR-GAZ^CJ\P(2"Q4S)Q#FX<[G\=+$QM/GY>/S\N;V"@X2&R$7$"(O(QXS/2SGVM+8WM7&U=S&Q,K&W//J[P/[`B`A(RL<&B`1&246(#0B&"<>!Q8H +M#/'S\N'9Y^O=U=O?TL70X]S9\?GL\0L7"0LE(PT''C$=%C(Q'R,I'Q?@]PH1&AD7)288&B?= +MY>S=RM7EVL2ZP]OV]M[C`!,;'!D;)R81%BD>$R$N(A4>&`P4$`7\Y.#W\=G? +MZ//WV\_@V=/D\O'BYO\*#!$6&A\5!A4C$Q@J(!DC'@T1)1\$^??P[N_JYM[= +M\.C&RMS>Y^;G\>GW&!,$'#`4""0L'R(O*Q\E*A8'$A(`^?CKV=KHW-+=V=;9 +MT-'A\/W]\?80(!H8)2(3&"0=$QLG)!L6%14*`PX'\>??X.WKU]OSZ-C@V=3L +M\^GU`??Y&B83%!L7%Q(/%!<>(AH?)18/&QH.!/+HZ^?:TMGDXM30V=;/V>7M +M\?#U!QH4%2\O%QDK*!PD*1PC,B,-#AH6"/?HXM[1SMG3Q=7@S,#6Z>;J]OC] +M#1<6&BHR)!PD)R8>&2@G%A84"A$6!/3SZ^?KV]+EX\K4W\G2[-C4_PKT_!89 +M%28H#0\D)!H9(B,E*AX<)QP2$`#S[_'SY-7@Y=;7V_QY]?@[=;+[.K+U.;GY>?N[/4(!`86%Q<8$!8G +M(Q06*C,E#0CF[0+]^PT5&1P0"QPE&1PA +M'S(W&!$B(@[[_/7H\N/0[/;:V^O@V-[=W][F`OOP#A@:*!<.'QH8'PX('B8< +M%!0A'@L$`OOR\^G7Z_CBVN;OX]KIYMCL_NWP`00.%A@?%Q$<(AP1$"(H#PD@ +M'`D2%?_P]OGIZ?'BUMWCW=C7W.?HY/(!`@($#2(C$10A*BL:&2CGULC:ZMK<[?`!"OX+'QT5%B$?%QH?'Q@<*R`-"PD)!?;J +MX^S]\-G@\O/EW^3?X>C%0'X^/[LT-[S +MX,3/YM_:W]GA[_#S^/X'!084'AD4'B,B(AH;*"DC&@P/&Q'_^/GX[.GQZ=71 +MX.73S=C&ATK)A(4'A(`_P#V\?CTZN+[V]?T&_``= +M&0<6*!\1$!P9"`L6"P0*#0CZ]?\"^>SQ]N;:W>?IX=O_N^O3AW>KGX>S>SNKSV^/N\O[^!10.%"4A&Q\A +M(AL6$@X:'PH`"`X,]NG\`_'M]>_@YO+GYO+DVNCJX>X$^O`-(140'"$E)!81 +M&2`:"@L>%@0-"_G]!?GJ[?/EX>3=XNK@U=CEY>7T]?<3%049*"`J,"$6%R(? +M#1,3"A,+_`0!]OCVZN;M[N;>X>;CW]K@ZN#E^_4#'@@$*"X>%ATK)`\1&A(0 +M$`<*#@'U]/S^\.KU\>#F\.KEWM_JX-3I^_'Q_PH3%!(:(R<@%!,;'A<3$A(. +M#Q#]^@7]^/;JZ_/IWN;IYN?=UN3LY^KU_04."PD:(AH?)!4/'2$5#!(9$PD' +M!@'Y]O3LZ^WGY>+E[>7CZ>;I\/+Z__\.&0X*'"D:"Q47#A81"!$2$!,+__X( +M`_+V]^KO[-OF]^[=W>KNZ>WW^P`0$0(+'1P=&PL%%A\(`1(/#1<,`@3_"`7L +M]`3PWN3O\.KFVMKIZ^/H^04(#@T.'"`?)1,%&1D$`P\5"P<0!OH("?7S^O7S +M[N3J\.WIX-WAX./I[OL`_Q`;%!HJ*1P4&AL,!P@)$`H`!@7X^/[Y\?/S[>KI +M[OGOWN;PZN;G[_?\"0C[#R$!0?%/\'%@L""`/X^/;Y_/#N\N?L]_+Q +M\.CK[N?J]/?Y`0<$!1,8&QX7%Q88'!$0'!<2$0D$_?;Y]NWM[.+A[?;RX=WL +M[-_D\?+Z"`0"%!L5&"(?%Q45%A<6$1,9&10&^?O]]^WCX>CGWN?OYNOIXNGF +MZOW^^P4.%!,3'2(A'1(.$`D)#PP2&PP""0,!!OKL[.WIZ.CGZNSCV^#KY^;X +M^_L&!P4.'"0;$Q@<'10%#1T2"`T3#````@,!\_+PY.CLY][9YN?3VNKN_`/Z +M`Q00$20H&QDA(1<-$1L6$@\$!`+V]?W]\>3H\O#M[.KNY]WAY.;J]`,'``87 +M%A0A(!D>%P<.&A(+$1@5"`'_``7_\.SQ\>OK[^_N[>OGX.'CZOK[]OH`$!P1 +M%28:&!X2%14-%A8/$`\$_@D%]?#P[.3EZ^?AZ>WBW>CJY_@!]?H-"@,5)1H5 +M(B(5$A48&A@.`P<.`O/Y`?KPY^;N].S?XNWFW-[F[/7_`0,%!Q0<%!09&14+ +M#!02$1$-#Q`*__G[_OGO[.SJ\O7HY._RY=K>ZO/V^/P!!@P7'A<:)!@'#A(( +M#14-"`X3"OS^"@?Y[>KP\NGGZNGGYN/AX>?X^_'Z!`01&1DB(!L:%188$`L5 +M%00%#/_[!03]]._U\NSMZ.3HZ>7;W.[S\0`!^0L1#1L@&1<4&1H.#1,4%@\" +M`@@#^O?Y_?CM[O?SZ^[T[.+AX>/J\_CU^@8(#AD8%!0<&PP*%!<4%!`*#0\& +M_?H``>[F\._I\>_E[>_@Y>OE\?_U\`,/!@L7%QP=%`\2$PX4&@X'#0\*__C^ +M!??FZ.WKZNCGZ>OJX>;T\O$"!OL`$!,1%QT9#@\6$Q`/$A0)!Q`+__?_"/;J +M\>_K[_/KX.SOX>3K\?3X!0/Z"A4/$A@9%0T-$A`0#A$9$0<+"P3_`?[V]N_H +M\_'CZ?3JW.;IW_'_\_P*`P07(!<2&Q\4#0\2$Q40#0T*"0/[_0#W[NWKZO#L +MZN_IY.3G[NSO_/[[`0H3%A4;'1<4$1,2#A06"P8+#`'^!/OO]_3G[//HZO'K +MZN[FY>_T]O?Y`@4&$1(2&QH4%!03#@X7%0<%#PGY_/_Z^//N[>SN[NWKZ>7B +MZ/#N\/O[^P,!!Q80$1@1$Q80$Q86%141#`L$_@,!^/#O[.?O]>SBXNKHX>3G +M\/S\^/P($!,:%0X=(`X*%A40%Q,%"1$-_?@#_O3PZNSQ\/#MZ.OPZ.'L]/3Z +M^_@!#Q`-$QD9&A4'"AT4!A$/!@\,_?X+!??V\N_V]N[GZ_+HX>KIZ>_M]/[] +M_P4/%A0:'103&AD1#Q04#`0)!_T!!/CR]?'P\.WQ\>KIZ>CLZNGV_?;]!OX) +M(14)&AX3%!,.%1D1!@8)!/\!^?/[]>;M]O#R].[O[^_R[>;S!/KN^`<-#@P) +M#QH6#@P-$1D6!@02$`0$`?C^`>_E\/GT[.WP\/#IY._[]N[X`/L"#PH)&!D0 +M#@\3&1<."@\0#PT`_@7]]_7JZ_7PY>GQ[^GEZ_+Q]OS[_04("Q05$A<6$1`2 +M$`T0#04�G^_P(`_/3P\O3R\/'L[?3KY?#S\/G^^_X#"1$1#10:#`84$P@, +M$`H)"08'!`(&__7X]^_T]>_P[NGKZ^CGZ_#W_OOZ!!$5$1(6$Q07#@P6$0H1 +M#`,*"_[Y_O_SZO/W\.SM[>[NZN;GZO'Y^?3\"0T0$A45$!<<$`@.%1<*_042 +M"O?X`_[T\^_L]??MZ>_V[>+M\^KQ`/KT``T."@X5$Q(5#@P4$PT)!PT,`P,` +M_P'\]/#S]_/L[>_L[.GEZ?+Q\?\`\@(7#`H2$A(4%!$4%`\1$PH)"P4!`/[Z +M]_?S[_/S\.OI\>WBZO#L]?WZ_0<+#0\1%!02$`X0$`T.$0H&#`D$`?W_^O3X +M]/+Z]>GL]?7LX^OU\_3W^``*!P@4$@T3$1`2"0L5#`4*"@D(```$__OU\_GU +M[_'R[NWNZ.OT[>W[^?;^!`H/"@D1%1(,#A,.#0L&"`8'!OS^`?KW^OGT\_+P +M\.[N[^GL\O7X^?L!!@8'#PX+$!(/#!`2#0T-"`8%!/_\__WY]O3W^?;Q\/3R +M[O#M[?;Y^OK]!PD)#@P+$1`-"PL0#`D,"0@)!04!_?_^^/7X^?/R]O3Q\O#O +M\O3W]_?^`0('"0D.$0\+#0T-#0L+!PD*`P<'_0$%^_3Z_/#Q]>_N\>_N\/+U +M^OGZ``,("0D0#0T3#@L.#PX+"`@'`P,#_O\!^_?X^??R\/3Q[?#P[?#U]?D` +M_@`)"0D-"PL0"P8,#@D'"0D$`00#`?SZ_OOZ^O3V^/7U\O#Q[NSQ]_;T_`#^ +M!0H'"@T,#0P*!P@/#P4!!@@&`OS]!/_U]?KZ]._U].[P\.[P]?;V^_[[`PX( +M"!`/#A`/"@T2#`<)!P8%`0#^_P'Z]?K[]O+T]O'T]NSP^_?X__[]`@D*!PD. +M#0P,!PL0#0@'!P8%`O\!`P#\^?O]^/;X]_/S]?+O\_;W^?K[``0&!P<)#`H+ +M"@P-"@L*"0H'!00#``$"_/G\^/3T]/3S\N_M\_CR]/[]_@,#!0D-#@H+#0L+ +M"@H+"`("!00#`?[^_OKX^??W]_/R]/+Q]/;V]_K\_@`!`PD+!P@,#`L*"0D* +M!@(#!0$`!0#Z`0+Y^?KX^??T\?/T\O3U]?K\^?L``@,("04(#0H'"0H+"08$ +M!`8%`?__`@#Y]_GZ^/7W]/+W]?+U]??^_?K^`@4&!@D+"0D+"@D,"`4)"`,# +M!0'_`O_Y^_WW]?OW\_CW]/;Y^/G^_?P``0(%"0D("`D+"@<)"@8%!04%`P,# +M__P`_OCY^_KX]O?U]_GW]OK]^_L``O\""`@&!@@,!@,+"04'!`,'!P(``@$! +M_OCY_/GU]O;U]?7U]_;X_/S]_@$#!`8'"0@&"`D'"`@%`P8&`0$$`?W_`/OZ +M^_CX^/7W]_7U]?CZ^OO]_P`!`@4'"`<'!P<("`4&!@($!`("__\`_/O\^_KX +M^?KZ]_;W]OCY^/K[_@#^`0,$!P<'!P8("04$!P0"!@,!`O_]_?W\^OGZ^?KX +M]_GY^/CX^/K]_OS]`@0$!08("0@&!@@(!00$!`0$`/X!__O^_?GY^OKY^?KW +M^?SY^?O\_P'^_@0%`P4'!@8)!P4'!@4&!`,#!`']``'\_/SZ^_KX^_OW^?CY +M^OK\_OW_`0`#!P,#"`<$!0<&!08%!`0#!`,`_____/W^^?C[_/KY^?GY^OOZ +M^P#__@$!`P8%!`8&!04%!`0%!`$"!`(!`/_^_O[\^OKZ^OSY]_O\^?KZ^?X` +M__[_!`4$!`,&"08$`P4&`P("`0(#_OT``?W\_/O\_/OZ^?O[^OKY^_[^____ +M`0("!`0$!04%!`,$!`,!`0(!__[]`/[Z_/[]^_O\^_O\^_G[_/S]_O__``$! +M!`0$!0,#!00#`P0#`0("``#^_@#^^OS]_/S\_/S[_/S[_?S\___^_P`!`P(# +M`P,%!0("!00"`@(!`0'______?W]^_W_^_O]_/W^_/S]`0#]_P$"`@(#`P(# +M`P(#`P,#`0`!`@'^_P#__O[]_?[]_/W]^_W^_/W^_O__`/\``P$``P,"`@,# +M`@("`@$!`@'__@$`_O[^_?W^_OW\_?[\_/S]_O[___\``0$"`@(#`@("`@$! +M`@'_`0'__P#__O[__OW^_O[]_?[^_?W^_O[_____``$!`@(!`@$!`@$!`0'_ +M``$`__\``/____[^_O[^_O[^_O[]_O_^_____P$!`0$!`0$!`0$!`0`````` +M`/\`_____O___O[^_O___O[^______\``````0$``0$```$``0$`````__\` +M`/_______________________P#_````````````````````````````____ +M_____________________P#___\````````````````````````````````` +M``````!#3TU-````$@`!``!#%@`(0`VL1````````$U!4DL````"``!)3E-4 +M````%#P``'\`?P``````````````````05!03````:A39#)A``(````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````N`````````````````````````````````````` +M```````````````````````````````````````````$"```````"0`C```` +M`````````````'@````$``0```````'\`/___X`````!`0$&455N:71S`/\` +M`/___OX!`?[^`/____\``/__`/_^____`````0+_!B!S86UP````%8P*P`$D&VK3#\`J(`?!T)G2&T- +MZB\.+P9.N@`\4@=P`0M-86=I8R!&;'5T90(```!!249&4V0R80`````````` +M``!!249&4V0R80```````````````````````````````*<]K5D``$4B```! +MSA`N`!9(@$'MJ;@2,```2('203HS$``0+@`62(!![:FX$C```$B!TD$\,Q`$ +M/@5"!!U\``'_^;Q'7,`2!`H!``'``6=*ND=6P+Q'5L&"+@`4P`$"0``!9Q0_ +M!Q\N__E![@`,(!AG`B\`(%!.D+Q';@1X`6#"0F````'@```!6`'&X +MO!RF````'`!6``!7```````# +M__\````\```````$__\```!:```````````````````````````````````` +;```````````````````````````````````` +` +end diff --git a/sys/share/sounds/mgcharp.uu b/sys/share/sounds/mgcharp.uu new file mode 100644 index 0000000..05a9e53 --- /dev/null +++ b/sys/share/sounds/mgcharp.uu @@ -0,0 +1,407 @@ +begin 644 Magic_Harp +M``I-86=I8R!(87)P```````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``&@``````````$0B```! +MSJ<]K4JG/:U2``````````````````````````````"!@8QW``!&3U)-``!$ +M&D%)1D934TY$``!"'@`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M___________________________________________________^_O[^_OW] +M_?S\_/S\^_CW]_?X]_7T]/;Z_?[_``$"`@(!`/[[^?CX^P`"`@("`@("`@(" +M`@,!^.*]H)V@GZ.GK+*VNKJWN+:MFX:`B(R.CXJ#BIFGL[BZOL/(SM/:WN'@ +MU='9X>ST]?7V^?X%"0L-$!(2$A$0#PT*"`@)#1$1$1`0$!`0$!`0#P\/"?KC +MTL_.SM#3UMK>X-_>W]W5P["IJJNLK*:CK+C#S-#2U=K>X^?K[O#LXN+K]/X# +M`@(#!@P2%1<9&QT='1P;&1<5%!,5&1L;&QL;&QH:&AH:&AD8$P+LW]S:VMS? +MX^;IZ^KIZNC>R[NWN+BXM[&QN\;1V-K=X>7I[?'U]_CRZN[W_P@+"@L,#Q4: +M'!XA(R0D)",B(!X<&QL>(B(B(B(A(2$A(2`@("`@&@?RY^7CX^3GZ^[Q\O+Q +M\O#DT,/!P,#`OKBZQ=#:X.+EZ.SP]/C[_?SU\??_"`\0$!`2%1H?(2,E)RDI +M*"@F)"(A("`C)B8F)B8F)24E)24D)"0D'@SW[>KHZ.KL\//V]_;V]_;IU?J[?'T^/S_`/[W]?P$#1,3$Q,5&1XB)"8H*BPL*RLI)R4D +M(R0G*2DI*2DH*"@H*"ON +M\O7Y_/\!`P'Y^0$($1<6%A<8&R`D)B@J+"XO+RXM*RDG)BSO\O;Y_/\" +M`P#Y^0$)$1<7%Q<8&R`D)B@J+"XP+R\M*RDH)R@K+2TM+"PL+"PK*RLK*BHI +M*2`,_?CS\/#Q\_?Z_/W]_?[UX]?4T,[-R\7$S=;@Z.ON\O7X_/X``?_X^``) +M$1<7%Q<8&A\C)B_P\O7Y^_S\_/WWYMG4T,W,RL7"R=/KL\//V^OS^`/[W]?X'#Q87%A87 +M&1TB)"8H*BPN+R\M+"HH)RCJ[O'U^/K\_OWW\_D$#146%A86%QH?(R4G +M*2LM+RXM+"HH)R[Q]/?X^?KY\N7WP\_;X^OKX\/'Z!`X5%!04%!89'B$C)2WN\/3V]_?X].G>V=/.RL?$OKW& +MS]CBY^KN\?3V^/CW\.WT_@@2%!,3$Q06&Q\A(R4G*2LL+"HH)B4E)RHJ*BHI +M*2DI*"@H*"SBW-;0R\?%P+N^R-#;X^;J +M[?#S]?;U\>KM]P`+$A(2$A(3%AL>("(D)B@J*RHH)B4D)2@I*2DH*"@H)RWKZNSO\?/S]/#EW]O4SLK&P[RYP,G2W>/FZ>WP\O3T +M\NSH\/D##A$1$1$1$Q8;'A\A(R4G*2HH)B4D(R4H*"@H)RV-#+QL*[M;G"R];>XN;I[._Q\?#JY>KT_0@.#P\. +M#Q`3%QH<'B`B)"8G)R8D(R(C)BKH +MZ>OM[N_P[.'=WMC0RL2_MK.[Q,W8WN'EZ.SN[_#NY^/K]?X)#0T-#0T/$Q<9 +M&QT?(2,E)ROHZ.CJ[.WN +M[^K?W=[8T,G"N;&TOL;0V=S@Y.CK[>_O[.3C[/7_"0P+"PL,#Q,6&!H<'A\A +M(R4E)"(A(2,E)20D)"0C(R,C(B(B(B$A'Q<)`P/^].[IY^?HZNOL[N_IW]W? +MVM#'OK*ON,'*T]?;X.3HZ^WO[NOCX^WV``D*"@H*#!`3%1<8&AP>("(D)",B +M(2$B)"0D)",C(R,B(B(B(2$A'QH-`P,!^?+LZ.?GZ.GJ[>_PZ=_>X-K/Q;>N +ML[S$SM/6V^#DZ.OM[^[JX^/N]P`("0@("@T1$Q06&!D;'1\A(R0C(2`A(R0D +M(R,C(R(B(B(A(2$A'QP1!`(#_O7NZN?GZ.GIZ^[P\.C?W^#:SK^RL;G`RL_2 +MUMO@Y.CL[N_NZ>+E\/@""`<'"`H.$1(4%1<9&QT?(2(C(B$@(2,D(R,C(R(B +M(B(A(2$A(!T3!@$#`/GQ[.CGY^CHZNWP\O'GW]_?V,FXL[B^Q\W/TM?;X.7I +M[.[O[>?BZ/+Z`P<&!@D,#Q`1$Q46&!H<'B`B(R(A("$C(R,C(B(B(B$A(2$@ +M(!T5"`("`?SU[>GGY^?GZ>SO\O3PY=[>W-+`M[J^Q'=W-7'O;W`QSI +MY^?FY^KM\?7V].C>V];+Q<3$R,S,S,[1U=K@Y>KN\/#OZ./J\_H!!`4)"@L- +M#@\1$A06&!H<'A\@(2`@(2(B(B(B(2$A(2`@'QT5!P$"`@#[\^WJZ.?FY^KM +M\?7X]_#BV]7+R7F[_?]`P8("@L,#0X0$1,4 +M%A@:'!X?("$@("(B(B(B(2$A(2`@'QP2!@$"`@'\]._KZ.?FY^KM\?7X^?7G +MV]/+S-+2TM+/SL[.T=;;X>;K[_'R\.GE[/3[`@8("0H+#`T.$!$3%1<8&AP> +M'R`@("$B(B(B(2$A(2`@'QL/`P("`@']]>_KZ>?FY^KN\O;Y^O?KV]#*SM;8 +MV-71S\[.T-39W^7J[O'S\NWFZ?+X``<("`D*"PP-#Q`2$Q47&1L='A\@("`B +M(B(A(2$A("`@'A@,`@("`@']]O#KZ>?FY^KN\O;Y^OCNW,[,TMC=WMC3T<_. +MT-/8W>/H[?'S\^_GY_#W_P8'!P@)"@L,#@\1$A06&!H;'1X?'R`B(B$A(2$A +M("`?'14(`@,"`0']]O#LZ>?FZ.OO\_?Y^OGPW'R`A(B$A(2$@("`>&Q$% +M`@,"`@+]]O#LZ>?GZ>SP]/CZ^_GPW,S1V=WDYN#:UM/1T=/7W.'G[?'T]?/K +MZ._W_@8(!P@)"@H+#0X/$1(4%A@:&QP>'R`A(B(A(2$A("`>&`L#!`0"`@+\ +M]O#LZ>?HZNWQ]?G[^_CNVL_5W.'FYN/>V-73T=/7W.'H[?'U]O3MZ?#X_P8( +M!P@)"0H+#`X/$!(4%1<9&QP='B`A(B(A(2$A(!\<$@8#!`0#`P'\]/#LZ.?I +MZ^_S]_K\^_;KV=/9W^7GY^7@VM?4TM37W.'H[?'U]O3MZO'Y``8'!P@("0H+ +M#`T.$!$3%1<8&AL<'B`A(B$A(2$@'QT7"P0$!`,#`P'Z\^_KZ.CJ[?'U^/K[ +M^?/FVMC=Y.?GZ.CCW=G6U-37W.+G[?+U]O3NZ_/Z``8'!P@("0H*"PT.#Q$2 +M%!88&1L<'1\A(2$A(2$@'AL0!00%!`,#`__X\NWJZ.GL[_/W^OO[]^WEX-WA +MZ.CHZ>GFX-O7U=;8W>/H[O/V]_7N[?7\`08'!P@("0D*"PT.#Q$2%!87&1H; +M'1\A(2$A(2$?'!0(!`4%!`,#`OWV\>SIZ>ON\O;Y^_OY\NKHX^#GZNCHZ>KG +MXMW9U]?:W^3I[_7W^/7O\/C]`@<("`@("0H+"PT.#Q$2%!87&1H;'1\B(B$A +M(2`=%PL%!@8$!`0$`?KT[NOJZ^WQ]?G[^_KU[>WMYN;JZ>GIZNOIX]_;V-G< +MX.7K\?7W^/7P\OK_!`@("`@("0H+#`T.#Q$2%!87&1H;'2`B(B$A(!X8#04& +M!@4%!`4#_?;Q[.KK[?#T^/K\^_;O[O/OZ>GJZNKJZ^SJY>##=V]S?Y.KO]/?Y^?3R^?\# +M!P@("`@)"0H+#`T.#Q$2%!87&!H;'B`A(2`>&@\&!P<&!04%!0/\]>_L[.WO +M\_;Y^_OW\/'W^O?OZNOKZ^OL[>_LYN#=W=[AY^SQ]?CZ^//U_0`%"`@("`@) +M"0H+#`T.#Q$2%!87&1H<'R$A(!X:#P<'"`8&!04&!/[W\>[L[>_R]OGZ^O?P +M\OG\_/;LZ^SL[.SM[_#LYN'?W^'EZN[S]_K[]?+Y``0("`@("`D)"@H+#`T/ +M$!$3%188&1L='R(A'AD.!P@(!P8&!@8%`/CS[^WN\//V^?KZ]_'S^_[__/#K +M[>WL[>WN\/'LYN+@X>/G[?+V^OSY\_?^`@<)"`@("0D)"@L,#0X/$1(4%1<8 +M&AP>("`>&`T'"0D'!P8&!P8"^O3P[N[P\_;X^OKV\?3\_P(`]>SL[>WM[>[P +M\O#JY>+BX^;K\/7Y_/OU]?P"!@D)"`@)"0D*"@L,#0X0$1,4%A@9&AT?(!X8 +M#0<)"0@'!P8'!P3\]?'O[O#R]?CZ^?;R]?T!`P+Y[NSN[>WM[N_Q\N[HY.+C +MY>GN\_?[^_?T^@$%"0H)"`@)"0D*"PP-#@\0$A,5%A@9&QX?'A@."`D)"`<' +M!P<'!?_W\_#O\/+U^/GY]O+T_0$#`_SQ[.[N[N[N[_'S\>SGY./EZ.SQ]OK[ +M^/3Y``0("@D("`@)"0H*"PP-#A`1$Q06%Q@:'1X=&A`("0H)"`<'!P<'`OKU +M\>_P\?3W^?GW\O3]`0,$_O+L[N[N[N[O\/+S[^GEY.3GZ_#T^/KY]/@`!`@* +M"0D)"0D)"@H+#`T.#Q$2%!47&!H<'1T:$0D)"@D("`<'!P@%_O?S\?#Q\_;X +M^??S\_L!!`7_].WN[^[N[N_P\O3QZ^?EY.;J[O/W^?GU]P`%"`L*"0D)"0D* +M"@L,#0X/$!(3%1<8&AP>'1P4"PD+"@D(!P<("`<#^_;R\?'S]??X^/3R^0$$ +M!0'W[N[O[^[N[_#Q\_/NZ.;EYNCL\?3W^/7V_P4("PL*"0D)"0H*"PP-#@\0 +M$1,4%A<9&QT='!@."0L+"@D("`@("`8`^?7R\?+T]OCX]?+W``,$`_GO[N_O +M[N[N[_'R]/#JY^7EY^KN\O7V]/;^!0@,"PH*"0D)"0H*"PP-#@\1$A05%A@: +M'!T=&A()"@L*"0@("`@("`3^^/3R\O/U]O?V\O/\`@,#_/'M[^_N[N[O\/'S +M\NWHY>3FZ.SP\_7T]/L#!PL,"PH)"0D)"@H+#`P.#Q`1$Q06%QD;'1T;%@P) +M"PL*"0@("`@("`/[]_3R\O3V]_?T\O@!`P0`]>[O[^[N[N[O\/+S\>OGY>7G +MZNWQ\_/R^`('"@T,"@H*"0D*"@L+#`T.#Q$2%!46&!H<'1P:$@H*#`L*"0@( +M"`@(!P'[]_/R\_3U]O7R\_P#`P/Z\._P[^[N[N_P\?/S[^GFY>7HZ^[Q\O'S +M_@8)#0T,"PH*"@H*"@L,#0X/$!(3%!87&1L='1P8#@H,#`H*"0@("0D)!P'[ +M]_3S\_3U]O3Q]@`#`__T[_#P[N[N[N_P\?/R[>GFY>;H[._Q\/#W`P@,#@T, +M"PH*"@H*"PL,#0X/$1(3%188&AP='!L5#`H,#`L*"0D)"0D)!P'[]_3S\_3U +M]?+Q^0$#`OOQ\/'O[N[M[N_P\?+Q[.?EY>;HZ^[O[O#\!@H.#@T,"PH*"@H+ +M"PP,#0X0$1(4%1<9&QT='!H2"@L-#`L*"0D)"0D)!P+[]_3S\_3T\_#R_`(" +M`/?O\?'O[NWM[N_P\?+PZ^?EY>;HZ^WM[?,`"`P.#@T,"PH*"@H+"PP-#@\0 +M$1,4%A<9&QT=&Q@0"@P-#`L*"0D)"0D)!P+[]_3S\_3T\N_T_@("_O/O\?#O +M[NWM[N_P\/+P[.?EY>;HZNSK[/8""0T/#@T,"PH*"@H+"PP-#@\0$1,4%A@9 +M&QT<&A<."@T-#`L*"0D)"0D)"`/\^/7T\_/S\>_U_P(!_/+P\O#O[NWM[N_P +M\/+Q[.CFY>;HZNOJ[/D$"@\/#@T,"PL+"PL+#`P-#@\0$1,5%A@:'!T<&A4- +M"PX-#`L*"0D)"0D)"03^^?;T]//S\>_W``$!^O'Q\O#O[NWM[N_O\/+Q[.CF +MY>7GZ>GH[?H%"@\0#PX-#`L+"PL+#`P-#@\0$A,5%A@:'!T<&A4-"PX.#`L* +M"@D)"@D)"08`^O?U]//R\._W_P$`^?'Q\O#O[NWM[N[O\/'R[NGFY>7FZ.CG +M[/D$"Q`0#PX-#`L+"PL+#`P-#@\0$A,4%A@:&QT;&14."PX.#`L*"@H*"@H) +M"0<"_/CU]//R[^[V_P$`^/#Q\O#O[>WM[>[O[_#R[^KGY>7FY^?FZ_D#"@\0 +M#PX-#`L+"PL+"PP-#@\0$1(4%A<9&QT;&14-"PX.#0P*"@H*"@H)"0D%_OKW +M]?/R[^WT_0#_^/'R\_'O[>WM[>[N[_#R\.OHYN7EYN;EZO@""0\0#P\-#0P+ +M"PL+"PP-#0X0$1(4%1<9&AP<&A8/"PX.#0P+"@H*"@H)"0H(`OSX]O3R[^WR +M_/__^/'R\_'O[NWM[>WN[_#Q\>WHYN7EY>7CZ/8!!PT0$`\.#0P+"PL+"PP, +M#0X/$!(3%188&AP<&A<0"PT/#@P+"@H*"@H*"0H*!?_[]_7S\.WP^?[^^?+R +M]/+P[NWM[>WN[^_P\O#KZ.;EY>3BYO/^!@P0$`\/#@T,"PL+#`P,#0X/$!$3 +M%!87&1L<&A@2#`P/#@T,"PH*"@H*"@H*"`/^^O;T\>[N]OS]^?/R]//Q[^[M +M[>WN[N_P\?'MZ>?EY./BY._\`PH.$!`/#@T,"PL+"PP,#0T.#Q$2$Q47&!H< +M&A@4#0L.#@T,"PH*"@H*"@H*"@WM[N[O +M\/+PZ^CFY./AX>KX``<,#@\/#@T,#`L+"PL,#`T.#Q`1$A05%QD;&A@6$`L- +M#@T,"PH*"0H*"0D)"@H&`/SX]/'M[O;[^O7Q\_3R\>_M[>WM[>[N[_'R[NKG +MY>/AX.7S_0,)#`X/#PX-#`L+"PL+#`P-#@\0$1,4%A@:&QD7$PP+#@X-#`L* +M"@H*"@D)"@L*!?_[]_/O[?#X^O;Q\O3S\O#N[>WM[>[N[_#R\>WIYN3BW^'L +M^``&"PT/#PX-#0P+"PL+#`P-#0X/$!(3%188&AH8%@\*#`\.#0P+"@H*"@H* +M"@H+"@7_^O;R[NWS^?CS\?3U\_'P[NWM[>[N[N_Q\_'LZ.;CX-_E\OP""`L- +M#P\.#0P,"PL+"PP,#0T.$!$2%!47&1H9%Q0-"@X/#@T+"@H*"@H*"@H*"PH% +M__KV\>WN]?GU\?+U]/+Q[^[N[N[N[N_P\O/P[.CEXM[@Z_;]!`D+#0\/#@T, +M"PL+"PL,#`T.#Q`1$A05%QD:&!81"PL/#PT,"PH*"@H*"0D*"@P*!?_Z]?#M +M[_;W\O#S]?3R\>_N[N[M[N[O\/+S\.OGY.#=XN_Y_P4("PT/#PX-#`L+"PL+ +M#`P-#@\0$1(4%1<9&!84#@D,#PX-#`L*"@H)"0D)"@H,"@3^^?3O[?+W]?#Q +M]/3S\O#O[N[N[N[N[_#R\_#KY^/?WN;R^@`&"`L-#PX-#0P+"PL+"PL,#0T. +M$!$2%!47&!84$0H(#0\.#0L*"@H*"@D)"0H+#`H$_OCR[N_U^/3P\O7T\_+P +M[N[N[>[N[N_Q\_3Q[.?CW]_I]/L!!@D+#0\.#0P,"PL+"PL+#`P-#@\0$A,5 +M%Q84$@P'"0X.#0P+"@H*"@D)"0H*"PP(`OSV\._T^?GT\/+U]//R\._N[N[N +M[N_P\?/U\NWHY.#AZ_;\`@<)#`X/#@T-#`L+"PL+"PP,#0X/$!(3%184$@X' +M!@L.#@T,"PH*"@H*"@H*"PT,!P#Z\^_S^OOZ]/#S]?3S\?#O[N[N[N[O\/'T +M]O3NZ>7@XN[W_0,'"@P/#PX.#0P+"PL+"PL,#`T.#Q`1$Q04$0X(`P<+#`T, +M"PH*"@H*"@H*"PP-"P3^]_'R^OW]_/3P]/7T\_'O[N[N[N[N[_#R]/;U\.OE +MX>3O^/X$"`H-#P\.#@T,"PL*"@L+"PP-#0X0$1,2$`X(`@0)"@L,"PL*"@H* +M"@H*"@L,#0D!^O/P^/____ST\?3U]/+P[^[N[>[N[N_P\O3W]O'LYN+G\OK_ +M!0@+#@\/#@T,#`L*"@H*"@L+#`T.#Q$1#@P(`0$&"`D*"PL*"@H*"@H*"@L, +M#0L$_?;Q]_\"`0#Z\O+U]?3R\._N[N[N[N_P\?/U]_CS[>?DZO7\`08)#`X/ +M#PX-#0P+"@H*"@H+"PP-#A`0#@L'`/\#!@<("0H+"@H*"@H*"@L,#@T'`/CS +M]OX#!`+_^/'S]?7S\?#N[N[N[N_O\/+T]OCX\^WGYN_X_@,'"PT.#PX.#0P, +M"PH*"@H*"PL,#0X/#0H'`/T"!08'!PD*"PL+"@H*"@L,#0X+`_KT]_X#!@4! +M_?;Q]/;T\_'O[N[N[N_O\/'S]??Z^?3MY^KT^P`%"0P.#@\.#@T,"PL*"@H* +M"@L+#`T-"PD%_OL``P0%!@<("@L+"PH*"@L,#0X+!/OU^/\#!@<#`/KS\O7U +M\_'P[N[N[N[O\/#R]/;Y^_GS[.GO^?X#"`L-#@X.#@T-#`L*"@H*"@H*"PP- +M"@<#_/K_`@(#!`4'"0H+"PL*"PL,#0X,!?SV^0`#!@@%`O[V\?3U]/+P[^[N +M[N[O[_#Q\_7X^OSY\NSM]OT"!@D,#0T.#@X-#`L*"@D)"0H*"@L+"`4!^OG^ +M``$!`@,%!PD+"PL*"PL,#0X,!OWX_`($!PD'`P#Z\_+U]//Q[^[N[N[O[_#Q +M\_7W^?S\]_#M]/P`!0@+#0T-#@X-#0P+"@H)"0D*"@L*!@/^^/G]_P```0($ +M!@@*"PL+"PP,#0X,!OWY_P0%!P@(!0+]]?'T]?/R\.[N[N[O\/#Q\O3W^?O] +M_/7O\_L`!`@*"PP-#0X-#0P+"PH)"0D)"@H(!`'[]OG]_O__``$#!0<)"PL+ +M"PP-#0X-!?W\`04&"`@)!P/_]_+S]?3R\._N[N[O\/#Q\_3V^?O]_OKS\_L` +M`P@*"PP,#0T-#0P+"PH)"0D)"0D&`__X]?G\_?[^_P`"!`8("@L+"PP-#0T, +M!?[_!`8'"`@)!P,`^O+R]?3R\>_N[N_O\/'R\_3V^?O]__WV]/L!!`<*"@L, +M#`T-#0P+"PH)"0D)"0@$`?SU]OK\_/S]_O\!`P4'"0H+"PP-#0T+`_\"!P<( +M"`@)"`0!^_/R]?3R\?#O[N_O\/'R\_3V^/O]___Y]OL"!`<)"0H+#`P,#`P+ +M"PH)"0@("`4!_OCS]OO[^_O\_?X``@0&"`H+"PP,#0T)`P$&"`<(!P<)"`0! +M^_3R]/3S\?#O[^_O\/'R\_3V^/O]_P#\^/T#!0@)"0D*"PP,#`P+"PH)"0@( +M!P/_^_3T^/KZ^OO[_/[_`0,%!PD+"PP-#`P(`P4)"`@(!P@)"`0!^_3R]/3S +M\?#O[^_P\/'R\_7W^?O]_P'^^OX$!@@)"0D*"@L+#`L+"@H)"`@(!`#]]O+U +M^?GZ^OK[_/W_`0,%!PD+#`P,#`H&!0D*"0@'!P@)"`0!^_3R]?3S\O#P\/#P +M\?+S]/7W^?S^``'__0$&"`D*"0D)"@L+"PL+"@H)"`@&`?[Y\_/W^?GY^?GZ +M^_W_`0,$!@D+#`P,#`D&"0L*"0@'!P@)"`0!^_3R]?3S\O'P\/#Q\?+S]/;X +M^OS^``$`_P,("0H)"`@)"0H+"PL+"@H("`<#_OOT\O;X^/CX^/GZ^_W_``($ +M!@@+#`P+"@D*#`L)"`<'!P@)"`,`^O/R]?3S\O'P\/'Q\O/T]??Y^_W_`0$" +M`@8*"@H)"`@("0H*"PL*"@D)"`3_^_7Q]/?X]_?W^/CZ^_W_``($!@@+#`L* +M"0L.#0L)"`<'!P@)!P+_^/+S]?3S\O'P\?'R\_3U]O?Y^_X``0("!`D,#`L) +M"`@("0D*"@H*"0D(!0#\]_'S]_?W]_;W]_CZ^_W^``($!@@*"PH*#`\.#`H( +M!P<'"`D)!@'^]_+T]?3S\O'Q\?+S]/3V]_CZ_/\!`@($"`T.#`H(!P<("`D) +M"@H*"0D&`?WX\O/V]_?V]O;V]_GZ_/W^``($!@@*"@D,$!`-"P@'!P<'"`D( +M!`#[]/+U]?3S\O+R\O+S]/7V^/G[_@`!`@,&#`\.#`H(!P<'"`D)"0D)"08! +M_?CR\O;W]_;V]?;W^/G[_/W_``,%!PD)"0T2$0X,"0<'!P<("`D&`O[X\_/U +M]?3S\O+R\_/T]?;W^?O]_P$"`P0*$!$.#`D'!P<'"`@)"0D)!@']^//S]O?V +M]O7U]?;W^/K[_/W_`0,%!P@)#1,3#PP)"`<&!P<("`@$`/OU\O3U]?3S\O+S +M\_3U]O?X^OS^``(#`P8.$Q$."PD'!P<'"`@("0D&`?WX\_/V]_;V]?7U]?;W +M^?K[_/[_`0,%!@@.%!,0#0H'!@8&!P<("`4!_OCS\_;U]?3S\_/S]/7V]_CZ +M^_W_`0,#!`L3$P\-"@@&!@8'!P@("`4`_?CS]/?W]O;U]/3U]O?X^?K[_?X` +M`@,$!@X5%!`-"@@&!@8&!P<(!P+^^_7S]?;U]?3S\_3T]?;W^/G[_?\``@,$ +M"!$5$@X+"0<&!@8&!P<(!0#]^/3U^/?W]O7U]?7V]_CY^?K\_?\!`@,%#!45 +M$0X*"`8&!@8&!P<'!/_\]O/U]O;U]?3T]/3U]O?X^?O\_@`!`P,%#144$`T) +M!P8&!@8&!P<%`/WX]/7X^/?V]?7U]?;W^/CY^OO]_@`!`@0+%!82#PL(!P8% +M!@8'!P<%`?WX]/3W]_;U]/3T]/7V]_CY^OS]_P$"`P0+%!42#PL(!P8%!@8& +M!P4!_OGU]?CX^/?V]?7U]?;W^/GZ^_S^_P$"`PD2%A00#`D'!@4%!@8&!P4! +M_OKT]/?W]O;U]/3U]?;W^/GZ^_W^``$"`P@2%A,0#`D'!@4%!08&!0'^^_;U +M^/GX]_;U]?7V]O?X^?GZ_/W_``$"!P\6%1$-"0<&!04%!08&!0'^^O7T]_?W +M]O7U]/7U]O?X^/GZ_/[_``$"!P\5%!$-"0<&!04%!08%`O[[]_7X^?CW]_;U +M]?7V]_CX^?K[_/[_``$%#!06$P\+"`8%!04%!08%`?[[]O7W^/?V]?7U]?7V +M]_CX^?K[_?X``0(%#105$@X*"`8%!04%!08#__WY]??Y^?CW]O;V]O;W^/GY +M^OO\_?\``0,)$145$0T)!P8%!`0%!04"__SW]??X^/?V]?7U]?;W]_CY^?O\ +M_?\``0,*$A43#PL)!P4$!`4%!00`_?KW]_GZ^?CW]O;V]O?X^?GY^OO]_O\` +M`@8.$Q43#PL(!@4$!`0%!0/__?GU]_GX^/?V]?7U]O?W^/CY^OO]_O\``@@/ +M%!01#0H(!@4$!`4%!0+^_/CV^?KZ^?CW]_;V]_CY^?GZ^_S]_P`!!`H1%101 +M#0H(!@4$!`0%!`#^^O?W^?GX]_;V]?7V]O?X^/GY^OS]_O\`!`L1%!,/#`D' +M!00$!`4%!`#]^O?W^OKZ^?CW]_;W]_CY^?K[^_W^_P`!!0T2%!00#`D'!00$ +M!`0$`?[\^/;X^?GX]_;V]?;V]_?X^/GZ^_S]_O\""`X2%!(."@@&!00$!`0% +M`O[\^?;X^_KY^/CW]_?W^/GY^OK[_/W^_P`""`X2%!,/"PD&!00$!`0"__WZ +M]_?Z^OGX]_;V]O;V]_?X^/GZ^_S]_O\$"Q`2$Q`,"0<%!`0$!`0#`/W[^/?Z +M^_KY^?CW]_?X^/GY^OK[_/W^_P`#"A`2%!(/"P@&!00$!`,`_OSX]_GZ^OGX +M]_;V]O;W]_CX^/GZ^_S]_@`&#`\1$@\+"08%!`,$!`0#__WZ]_C[^_KY^?CW +M]_?X^?GZ^OO[_/W^_P$$"Q`2$Q(."P@&!00#`P+__?OW^/KZ^OGX]_;V]O?W +M^/CX^?GZ^_S]_@()#0\1$0X+"`8%!`,#!`0!_OSY]_G[^_OZ^?CX^/CX^?KZ +M^OO\_?[_``$`2$Q(."@@&!00#`P'^_/GW^?O[^OGX]_?V]O?X^/CX^?GZ +M^_S]_P,*#0\1$0X*"`8$!`,#!`0!_OSY^/K\^_OZ^?CX^/CY^?KZ^OO\_?[^ +M_P$'#1`1$A$."@@&!`,#`P#]^_CW^?O[^OGX]_?W]_?X^/CX^?GZ^_S]_P0* +M#0\0$`X+"`8$`P,#`P,`_?SY^/K\^_OZ^?CX^/CY^?KZ^OO\_/W^_P((#0\1 +M$A$."P@&!`,#`O_]^_CW^OO[^OGX]_?W]_?X^/CX^?GZ^_S]_P4+#0X0$`X* +M"`8$`P,#`P+__?OY^/K\^_OZ^?CX^/CY^?KZ^OO[_/W^_P('#0\0$1$/"P@& +M!00#`O[\^_CX^OO[^OGX]_?W]_CX^/CX^?GZ^_S]``4*#`X/#PX+"`8$`P,# +M`P+__?OY^/O\^_OZ^?CX^/CY^?KZ^OO[_/W^_P$&#`X0$1$/#`D'!00#`O[\ +M^OCX^_S[^OGX^/?W]_CX^/CX^?GZ^_S]``4*#`T.#PX,"08%!`,#`P+__?OY +M^/O\_/OZ^?GX^/CY^OKZ^OO[_/W^_P$&"PX/$!$0#0H(!@0#`O[\^OCX^_S[ +M^_KY^/?W]_CX^/CX^?GZ^_O\_P0)#`T.#PX,"0<%!`,#`P,`_?SZ^/K\_/OZ +M^OGY^/CY^OKZ^OO[_/W^_@`%"@T/$!`0#@L(!@0#`O[\^OCX^_S\^_KY^/CW +M]_CX^/CX^?GZ^OO\_P0)"PP-#@X,"@@&!`,#`P,`_?SZ^/K\_/S[^OGY^/CY +M^OKZ^OK[_/S]_@`$"0P.#Q`0#PT*!P4$`O[\^OCX^OS\^_KY^/CW]_CX^/CX +M^/GY^OO[_@,("@L,#0X-"P@&!`,#`@(!_OS[^?G[_/S[^OGY^/CY^?KZ^OK[ +M^_S]_O\"!PL-#@\0$`X+"`8$`__\^_CW^OS\^_KZ^?CX^/CX^/CX^/GY^OK[ +M_0('"0L+#`X.#`D'!00#`P,"__S[^?G[_/S[^_KY^?GY^?KZ^OK[^_S\_?X` +M!0H,#@\/#P\-"@<%!`#]^_GW^?S\^_OZ^?CX^/CX^/CX^/CY^?K[_``%"`H+ +M#`P-#`H(!@0#`P("`/W\^OGZ_/S\^_KZ^?GY^?KZ^OKZ^_S\_?[_`@@+#0X/ +M#P\.#`D'!0+^_/KW^/O\_/OZ^OGX^/CX^/CX^/CY^?KZ^_X#!PD*"PP-#0L) +M!P4$`P("`?[\^_GY^_W\_/OZ^?GY^?KZ^OKZ^_O\_?W^``4*#`T.#P\/#@L( +M!@3__/OX^/K\_/O[^OGY^/CX^/CX^/CX^?GZ^_P!!@@)"@L,#0P*"`8%!`," +M`@#]_/KY^OS]_/O[^OGY^?GZ^OKZ^OO[_/W^_P('"PP-#@\/#PT*"`8!_?OY +M]_G[_/S[^OKY^?CX^?CX^/CX^?GY^OO^`P<("0H+"PP+"0@&!`,#`@'__/OZ +M^?O\_/S[^OKY^?GY^OKZ^OK[^_S]_O\#"`L,#0X/#PX-"@@$`/W[^/CZ_/S[ +M^_KZ^?GX^?GX^/CX^/GY^?K\`04'"`D*"PL,"PD'!00#`@(!_OS[^?K\_?W\ +M^_KZ^?GY^OKZ^OK[^_S\_?X`!0D+#`T.#@X.#0H'`__\^OCY^_S\^_OZ^OGY +M^?GY^/CX^/GY^?K[_0(&!P@)"@H+#`H(!P4$`P("`/W\^OGZ_?W]_/OZ^OGY +M^OKZ^OKZ^_O\_?W_`@8*"PP-#@X.#@P*!@+^_/GX^OS\_/O[^OKY^?GY^?GX +M^/CY^?GZ^_\#!@<("0H*"PL*"`<%!`,"`?[\^_KY^_W]_/S[^OKY^?KZ^OKZ +M^OO[_/W^_P,("@L,#0X.#@X,"04!_?OX^/K\_/S[^_KZ^?GY^?GY^/CY^?GY +M^OP`!`8'"`D*"@L+"@D'!00#`@#]_/OY^OS]_?S[^_KZ^?KZ^OKZ^OK[^_S] +M_@$%"0H+#`P-#@X-"P@$`/W[^/G[_/S\^_OZ^OGY^?GY^?GY^?GY^OO]`04& +M!P@)"0H+"PH)!P4$`P'^_/OY^?O]_?W\^_KZ^OGZ^OKZ^OKZ^_O\_?\#!PD* +M"@L,#0T-#`H'`__\^OCY^_S\_/O[^OKY^?GY^?GY^?GY^?K[_0(%!@<("0H* +M"PP+"0<%!`+__/OZ^?K]_?W\_/OZ^OKZ^OKZ^OKZ^OO[_/X`!0@)"0H+#`P- +M#0L)!@+^_/KX^?O\_/S[^OKZ^?GZ^OGY^?GY^?KZ^_X"!0<("`D*"@L,#`H' +M!00!_?SZ^?K\_?W]_/O[^OKZ^OKZ^OKZ^OK[^_S_`P8("`D*"PL,#0P*"`4! +M_OSZ^/K\_/S\^_OZ^OKZ^OKY^?GY^?KZ^_S_`P8'"`@)"@L+#`P*!P8#__S[ +M^?G[_?W]_/S[^_KZ^OKZ^OKZ^OKZ^_S^`@4'"`@)"@H+#`P+"`8$`/W[^?CZ +M_?W]_/O[^OKZ^OKZ^OGY^?KZ^_O]``0&!P@)"0H+"PP,"0<$`/W[^?G[_?W] +M_/S[^_KZ^OKZ^OKZ^?KZ^_O]``0&!P<("0D*"PL+"0<%`__\^_GY^_W]_?S[ +M^_KZ^OKZ^OKY^?KZ^OO\_0$%!P<("0D*"PP,"PD&`O[\^OGZ_/W]_/S[^_OZ +M^_OZ^OKZ^?KZ^OO]``0%!@<'"`@)"@H+"@@&!`']_/KY^OS]_?S\^_OZ^OKZ +M^OKZ^?KZ^OO[_/\#!@<("`D*"@L,#`L(`__]^_K[_/W\_/S[^_O[^_OZ^OKZ +M^OGZ^OO\_P,%!08&!P@("0H*"0@&!`+__/OZ^?O]_?W\_/OZ^OKZ^OKZ^OKZ +M^OO[_/X!!0<'"`D)"@L,#`L*!0'^^_K[_/S\_/S[^_O[^_O[^OKZ^OGZ^OO\ +M_P,$!04&!@<'"`D)"0@&!0,`_?S[^?K\_?W]_/O[^OKZ^OOZ^OKZ^OO[_/W_ +M`P8'"`@)"@H+"PL*!@+__/O\_?W\_/S[^_O[^_O[^_KZ^OKZ^OO\``,$!`4% +M!@8'"`@)"0<&!0,!_OS[^OK[_?W]_/S[^_KZ^_OZ^OK[^_O[_/W^`@4'"`@) +M"0H+"PL*!@,`_?S]_?W\_/S[^_O[^_O[^_KZ^OKZ^OO]``,#!`0%!08&!P<( +M"`<%!`,"`/W\^_K[_?W]_?S[^_OZ^_O[^_O[^_O[_/S^`00&!P@)"0H*"PL) +M!@0!_OW^_OW\_/O[^_O[_/S[^_KZ^OKZ^OO]`0,#!`0$!04&!@<'!P8%!`," +M`/W\^_KZ_/W]_?S\^_O[^_O[^_O[^_O[_/S]_P,&!P@("0D*"@H(!00"__[_ +M_OW\_/O[^_O[_/S[^_OZ^OKZ^OS^`0(#`P0$!`4%!@8'!P8$`P,"`?[\^_KZ +M_/W]_?S\^_O[^_O[^_O[^_O\_/W]_P(%!P<("0D*"@D&!00"````__[]_/O[ +M^_O\_/S[^_OZ^OKZ^_S_`@(#`P,$!`0%!08&!@4$`P("`?[\_/KZ_/W]_?W\ +M_/O[^_O[^_O[^_S\_/W]_P(%!P<("`D*"@@%!`,"`0$!__[]_/O[^_S\_/S\ +M^_O[^_K[^_T``@("`P,#!`0%!04&!@0#`@("`?_]_/OZ_/[^_?W\_/O[^_O[ +M^_S[_/S\_/W^_P(%!@<("`D)"0<%!`,"`P(!__[]_/S[^_S\_/S\^_O[^_O[ +M_/\!`@("`@,#`P0$!04%!00#`@(!`?_]_/O[_/[^_?W\_/O[^_O\_/S\_/S\ +M_?W^_P(%!@<("`D)!P4$`P(#!`,!`/[\_/S[_/S\_/S\^_O[^_O\_@`!`@(" +M`@,#`P,$!`4%!`,"`0$!`?_]_/O[_/[^_?W\_/S[^_O\_/S\_/S\_?W^_P(% +M!P<'"`@(!@0#`@($!0,!`/[]_/S[_/S\_/S\^_O[^_S]_P$!`0("`@(#`P,$ +M!`0$`P(!`0$!`/[]_/O[_/[^_?W\_/S[^_S\_/S\_/S]_?W^``(%!@<'"`@& +M!`0#`@,%!0,"`/[]_/S\_/S\_/S\^_O[_/W_``$!`0("`@("`P,#!`0#`@$! +M`````/[]_/O[_?[^_?W\_/O[^_S\_/S\_/W]_?[^``,%!@<'"`8%!`,"`P0% +M!0,!`/[]_/S\_/S\_/S\_/S\_/X``0$!`0$"`@("`P,#`P,"`0$``````/[] +M_/O[_?[^_?W]_/S\_/S]_?W]_?W]_O[_`00&!P<'!P4$`P("!`4%!`,!`/[] +M_?W\_/S\_/S\_/S]_@`!`0$!`0$!`@("`@,#`P,!`0``````__[]_/O\_?[^ +M_?W\_/S\_/W]_?W]_?W]_O[_`00&!@<&!`,#`@($!04$!`,!__[]_?W\_/S\ +M_/S\_/W^``$!`0$!`0$!`@("`@,#`P$!`/___P``__W]_/S\_O[^_?W\_/S\ +M_/W]_?W]_?W^_O\``@4&!@8$`P,"`@,$!`0$`P(`__[^_?W\_/S\_/S\_?X` +M`0$!`0$!`0$!`0("`@("`0``_____P#__OW]_/S]_O[^_?S\_/S\_?W]_?W] +M_?[^__\!`P4&!00#`P("`P0$!`0#`P(`__[^_?W\_/S\_/S]_@`!`0$!`0$! +M`0$!`0("`@(!``#______P#__OW\_/W^_O[]_?W\_/W]_?W]_?W^_O[^_P`" +M!`4%!`,"`@(#!`0$!`,#`@$`__[^_?S\_/S\_/W^``$!`0$!`0$!`0$!`0$" +M`@$``/_________^_?W\_/W^_O[]_?W]_?W]_?[^_O[^_O[_``$#!04#`P(" +M`@,$!`0$`P,#`@$`__[]_?S\_/S]_?\``0$!`0```0$!`0$!`0$"`0``____ +M______[]_?S\_?[^_OW]_?W]_?W^_O[^_O[^_O__`0,$!`,"`@("`P0$!`,# +M`P,"`@$`__[]_?S\_/W]_P`!`0$!`````0$!`0$!`0$!``#___[^______W] +M_?S]_O[^_OW]_?W]_?[^_O[^_O[^__\``@0$`P("`@(#!`0$`P,#`@("`0#_ +M_OW]_/S\_?W_``$!`0$```````$!`0$!`0$!`/___O[^_____OW]_/S^_O[^ +M_?W]_?W]_O[^_O[^_O[__P`!`P,"`@(!`@,$!`,#`P,"`@("`0#__OW]_/W] +M_?X``0$!`0````````$!`0$!`0$`___^_O[____^_?W]_?W^_O[^_?W]_?W^ +M_O[^_O[^_O__``$#`P("`@$!`P0$`P,#`P("`@(!`/_^_?W]_?W]_@`!`0$! +M``````````$!`0$!`0``___^_O____[]_?W]_?[^_O[^_?W]_?[^_O[^_O[^ +M_O__`0(#`@("`0$"`P,#`P,#`@("`@(`__[^_?W]_?W^_P`!`0$````````` +M``$!`0$!`0#____^_____OW]_?W]_O_^_O[^_?W]_O[^_O[^_O[^__\``@," +M`0$!`0(#`P,#`P,"`@("`@$`__[]_?W]_?[_``$!`0````````````$!`0$! +M``#________^_?W]_?W^__[^_O[^_?W^_O[^_O[^_O[__P`!`P,"`0$!`0(# +M`P,#`P("`@("`0#__O[]_?W]_O\````````````````````!`0$!``#_____ +M__[]_?W]_?[__O[^_O[^_O[^_O[^_O[^_O[__P$"`P(!`0$!`@,#`P,"`@(" +M`@("`0#__O[]_?W]_O\````````````````````!`0$!`/_______OW]_?W] +M_O___O[^_O[^_O[^_O[^_O[^_O__``$"`@$!`0$!`@,#`P,"`@("`@(!``#_ +M_O[]_?W^_P`````````````````````!`0$!`/_______OW]_?W]_O___O[^ +M_O[^_O[^_O[^_O[^_O\``0("`0$!`0$!`@,#`P("`@("`@(!`/___O[]_?[^ +M_P`````````````````````!`0$``/_____^_?W]_?W^_____O[^_O[^_O[^ +M_O[^_O[^__\``0("`0$!`0$"`@,#`@("`@("`@(!`/_^_O[^_O[__P`````` +M```````````````!`0$``/_____^_?W]_?[^___^_O[^_O[^_O[^_O[^_O[^ +M__\``@(!`0````$"`@("`@("`@("`@$!`/_^_O[^_O[_```````````````` +M`````````0$``/____[]_?W]_?[____^_O[^_O[^_O[^_O[^_O[__P`!`@(! +M``````$"`@("`@("`0$!`@$!`/_^_O[^_O[_```````````````````````` +M`0$``/____[]_?W]_?[____^_O[^_O[^_O[^_O[^_O[__P`!`@$!``````$" +M`@("`@("`0$!`0$``/___O[^_O[_``````````````````````````$```#_ +M__[]_?W]_O_____^_O[^_O[^_O[^_O[^_O[__P`!`0$```````$"`@("`@(! +M`0$!`0$``/___O[^_O__``````````````````````````$```#___[]_?W] +M_O_____^_O[^_O[^_O[^_O[^_O___P`!`0$```````$"`@("`@$!`0$!`0$` +M`/___O[^_O__``````````````````````````````#___[]_?W]_O_____^ +M_O[^_O[^_O[^_O[^_O___P`!`0$```````$"`@("`@$!`0$!`0$!`/___O[^ +M_O__``````````````````````````````#___[]_?W]_O_______O[^_O[_ +M__[^_O[^_O___P`!`0$```````$"`@("`0$!`0$!`0$!`/____[^_O__```` +M``````````````````````````#___[]_?W]_O_______O[^_O[___[^_O[^ +M_O___P`!`0$```````$!`@(!`0$!`0$!`0$!``#___[^_O__```````````` +M````````````````````__[^_?W]_O_______O[^_O_____^_O[^_O___P`! +M`0$````````!`0$!`0$!`0$!`0$!``#____^_O__```````````````````` +M````````````__[^_OW^_O________[^_O_______O[^_____P```0$````` +M```!`0$!`0$!`0$!`0$!``#_________```````````````````````````` +M`````/_^_O[^_O____________________[^_____P```0$``````````0$! +M`0$!`0$!`0$!`0``_________P```````````````````````````````/_^ +M_O[^_O[___________________________\``````````````0$!`0$!`0$! +M`0$!`0``_________P```````````````````````````````/___O[^_O[_ +M__________________________\```````#__P````$!`0$!`0$!`0$!`0$` +M`/________\```````````````````````````````#___[^_O[_________ +M__________________\```````#___\````!`0$!`0$!`0$!`0$```#_____ +M__\`````````````````````````````````__[^_O[_________________ +M____________````````____`````0$!`0$!`````0$`````________```` +M````````````````````````````_____O[^________________________ +M____````````_____P````$!`0$!````````````_________P`````````` +M`````````````````````/____[^_____________________________P`` +M`````/____\````!`0$!`````````````/_______P`````````````````` +M``````````````#____^______________________________\``````/__ +M__\```````````````````````#_______\````````````````````````` +M``````#_____________________________________``````#_____```` +M````````````````````______\````````````````````````````````` +M____________________________________``````#_____```````````` +M`````````````/______`````````````````````````````````/______ +M_____________________________P``````_____P`````````````````` +M````````____``````````````````````````````````#_____________ +M______________________\`````_____P`````````````````````````` +M``````````````````````````````````````#_____________________ +M________________````______\````````````````````````````````` +M````__\`````````````````````````____________________________ +M_________P``______\``````````````````````````````````````/__ +M````````````````````````____________________________________ +M_P``______\```````````````````````````````````````#_```````` +M`````````````````/__________________________________________ +M__\```````````````````````````````````````#__P`````````````` +M`````````/____________________________________________\````` +M````````````````````````````````````__\````````````````````` +M`/____________________________________________\````````````` +M````````````````````````````____``````````````````````#_____ +M______________________________________\````````````````````` +M````````````````````_____P````````````````````#_____________ +M______________________________\````````````````````````````` +M`````````````/___P````````````````````#_____________________ +M______________________\````````````````````````````````````` +M`````/___P````````````````````#_____________________________ +M_____________P````````````````````````````````````````````#_ +M_P````````````````````#_____________________________________ +M____```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````$-/34T````2``$``$(6``A`#:Q$```````` +M34%22P````(``$E.4U0````4/```?P!_``````````````````!!4%!,```! +MJ%-D,F$``@`````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````"D``````````````````` +M```````````````````````````````````````````````````````````` +M``0(```````)`",`````````````````>`````0`!````````/P`____@``` +M``$!``9156YI=',````````````````````````````````````````!`@`& +M('-A;7!S```````````````````````````````````````````````````` +M````````````)P`O``__]O_Y``$````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````!`````7@```!X````5M8Z*`_> +MOCO;Q0T.*`5//"BD#RQP.]W&*#P!`?\@1-@4"DUA9VEC($AARQP,4`0\-!QP%7W?^,7D/ +M#080```!H`*0`/`FH#,0`J``,";0,]`"D`#P)!`R(!`````!H` +M*0`/`5`!\0`J``,";0,]`````````````````!H`+``6`3X!W``J``,";0,] +M`````````````````!H``````````````````````````````````````0`` +M``%X````>````%8`<;B\'*8````<`%8``%=S=&$``P`*``'__P`````````` +M``+__P```!X```````/__P```#P```````3__P```%H````````````````` +I```````````````````````````````````````````````````````` +` +end diff --git a/sys/share/sounds/toolhorn.uu b/sys/share/sounds/toolhorn.uu new file mode 100644 index 0000000..3f7f63f --- /dev/null +++ b/sys/share/sounds/toolhorn.uu @@ -0,0 +1,345 @@ +begin 644 Tooled_Horn +M``M4;V]L960@2&]R;@`````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``((`10```````#DB```! +MSJ<]K0&G/:TS``````````````````````````````"!@1/]``!&3U)-```Y +M&D%)1D934TY$```W'@````````````````````````````#_____________ +M__________________________\```````````````````````````#_____ +M________```!`@,#!`4%!@8&!@8%!00$`P("`0$``/____________\````` +M`0$!`@("`P,#`P,#`@("`@("`0$``/___OW]^_KY]_;T\_'P[^_N[^_P\O/V +M^/O]``0&"`L-#Q`0$!`/#0P+"0@&!`,!`/_^_OW]_?W]_?W]_?[^__\``0$" +M`P0$!04%!04%!04%!`,#`@$`__[]^_GW]?+P[>OIY^7EY>7GZ>SP]/G]`@8* +M#A(5%Q@9&!<6%!$/#`H(!@0"`/_]_/S[^_O[^_O[_/S]_?[_``$#`P0$!04& +M!@<("`D)"`<&!`,!__W[^?;R[NKEX=W;V=?7V=O>X^CN]?L""`X4&1P?(2(A +M(!X<&183#PP*!P4#`?_^_/OZ^OKZ^OKZ^OO[_/W^``$"`P0%!@<("`D*"PL+ +M"@D(!@4#`?[[^/3OZN7?VM;2S\[/T-/7V^'F[?3[!`P3&B`D*"HJ*B@E(AX; +M%Q,/#`D&`P'__?S[^OKZ^?GY^OK[_/W^``$"`P,$!`4&!P@)"0H*"@D(!P8% +M`P#]^O;Q[.?AV]71SKCW=?1R\?#P<#"Q,C-U-SE[_H&$1PF+C0X.CHX-3`K)B$;%A(-"04"__W[ +M^OGX]_?W]_?X^/G[_/X``0("`P0%!P@)"@L,#`P+"@D(!P8#`/WX\^WFW]?0 +MR,.^NKBZO,'(T=KE[_L'$AXG,#8Z/#PZ-S,M*"(=&!,/"P<$`/[\^OGX^/?W +M]_?W^/GZ_/[_``$"!`4&"`D+#0X/$`\.#0L*"`0`_/CS[>;?UL['P+JULK&S +MMKW%T-SI]P85(BXX/T-%1$(^.#(K)1\9%`\+!P0!_OSZ^?CW]_?W]_CY^?O] +M_O\``0$"`P0%!P@*"PP-#0T,#`L*"`4!_??RZ^3;T\S$OKBSL*^QM+K"SMOK +M^PL;*C8_14E*2$0_.#$J(QT7$0T)!0(`_OS[^OGY^?CX^?GZ^_S]_O[__P`` +M`0(#!08'"0H+#`P+"PL*"`4"_OGS[.7+3E! +M1TI*2$,]-BXG(!H4#PL(!0(`__[]_/S\^_O[^_S\_?W^_O[^_O[^_O\``0,% +M!PD*"PP-#0T,"@<$`/SU[N;>U<['P+JTL;"PLK:^R-;G^0L=+3I$2DU-2D4^ +M-R\F'QD3#@H&!`(`__[]_?S\_/S\_/S\_?W^_O[]_?W]_O[_`0($!@@)"0H* +M"@H)"`8$`?WX\>KCVM/,Q+ZZM[:VN+O"RM7D]`46)3(\1$A)1T,^-R\H(!H4 +M#PL(!0(`__[^_?W]_?S\_/S]_?[^_O[^_O[^_O\``@,%!@<("0H+"PL)"`8# +M__KT[.7'S!1K^$2,S0$A.4$]+1#PT*R(;%`\+!P0"`0#___[^_O[^ +M_O[^____``#___[^_?W]_?X``@,%!P@*"PP,"PH(!0+]^/'JX]K3S,2^N+.P +MK["RM\#-W?`%&2LZ1DY14D]*0SHP)Q\7$0P(!0,!``#___________\```$! +M`0#___[]_/S\_/[_`0,%!@@)"@L,#`H(!0']^/'JXMO3S,6_N;2QL+"RM\#, +MW?$&&RT\2$]34U!)0CDO)AT6$`L'!`,!````______[^_O___P```/___O[] +M_?W]_?X``0,%!@<("0D*"0@'!0/_^O/LY-W5SL;`N[>TL[2VN\/.W>\#%B@W +M0TM/44Y)0CDP)Q\7$0P(!0,"`0``_____O[^_O[__P````#__O[]_?W]_O\` +M`@,%!@<'"`D*"@H)!P0`^_3LY=S3S,.\MK*OKJ^RN<32Y/@-(#(_24]244U' +M/C8L(QL4#@H&!`(!`/_^_O[^_O[^_O__``$!`0#___[^_?W]_O\!`P4'"0H* +M"PL+"0@&!`#[]>[GWM;-Q;ZWL:VKK*^TOLO;[P4:+#Q'3U-34$I#.C`G'A<1 +M#`@%`P$`___^_O[^_O[^____```!`0#___[]_?W]_@`!`P4'"`@)"@L*"@@& +M!`#Z].SDW=3,P[RVL*RKK;"UO\S>\PD=+SY*45144$E"."\F'180"P<%`P(! +M`/___O[^_O[^_O__```!`0#___[]_?W]_@`"!`8'"0H+"PL+"@@'!`#[].WD +MW-/,P[RVL:ZMK:^TOLO<\0<<+S]*4E5544I"."\E'!40"P<%`P(!`/___O[^ +M_O[^_O[^_P```/___OW]_?W]_@`!`P4&"`D*"@L+"PH)!@/]]^_GWM;-Q;VW +MLJZLK*ZSO,G:[@09+#Q(4%1544M#.C`G'Q<1#0D&!`(!`/___O[^_O[^_O[^ +M____`/___OW]_?W^_@`!`P4'"`D*"@H+"PH(!0+^^/'HX-?/Q[^XLJ^MKK"V +MOLO<[P08*CE%35!13DE".3`H'Q@2#@H'!0,!`/_^_O[]_?W]_O[^_O__``#_ +M__[^_?[^_O\!`P4'"0L,#`T,"PH(!0']]N_FW=3+P[NTKJNJJZ^VP,_A]@L? +M,#Y)3U)13DA`-R\F'A@2#0H'!`(`__[]_?W]_?W]_?W^_O_______OW]_?W^ +M_P`"!`8("@H+"PP+"PD'!0'\]>[EW=3+PKFRK*FHJ:RTP-#C^0\C-4-,4E13 +M3D<_-BTD'181#0D'!0,!`/_^_?W]_?W]_?W^_O_______OW]_/W]_@`"`P4' +M"0H+#`T-#0P*!P/^]^_FW=3+PKFRK*BFIJFQO,SA^`XC-4--4U543T@_-BTD +M'!81#0D'!0,!`/_^_?W]_?W]_?W^_O[____^_?S\_/S]_O\``@0&"`H+#`T- +M#0L)!@/_^O/JXMG0R+^XLJZJJ:JON,;8[@4:+3U(4%-34$E".3`G(!D3#PL) +M!P0"`?_^_?W]_/S\_/S]_?[^_O_^_OW]_?W]_@`!`@0&"`H+"PP,#`P*"`0` +M^_7MYMW4R\.\MK&NK:ZQN<33YOL/(C-`24]03TM$.S0L(QT7$@X+"`8$`@#^ +M_?W\_/S\_/S\_/S\_?W]_?W\_/S]_P`"!`8("0L,#`P+"PL*!P0!_/;OY][6 +MSL6^MK&MJZROML'0Y/D.(C-`2E!14$Q%/#4L)!X8$P\,"`8$`?_^_?S\^_O[ +M^_O[_/S\_?W]_?W\_/W]_@`"!`8'"`H+#`P,#`L*!P0!_/?PZ-_6S<2[M:^K +MJJJMM<#0Y?H/(S1!2U!244Q%/C4M)1\9%!`,"08#`?_^_?S[^_O[^_O[^_S\ +M_/W]_/S\_/S]_@`!`P4'"0H,#`T-#0P+"08#_OCQZN'7SL6\M:^JJ*>JL;S, +MX?<-(3-!2U%34DU&/S8N)B`:%!`-"@<$`?_^_/S[^_O[^_O\_/S\_?W]_/S[ +M^_O\_?X``@0&"0L,#0X-#0P+"`4"__KSZ^+9T,B_N+&MJJFKL+K)W/$'&RT\ +M2$Y144Y(03@O*"$;%A(."P@&`P'__OW\_/S\^_O[^_O[^_S\_/O[^_O[_?X` +M`0,%!PD*#`T.#@X-#`D&`O[W\.C?ULW$N[2OJZFHJ[*^T.7[$24V0TQ24U%, +M13PU+"4>&141#0H'!`'__?S[^_KZ^OKZ^OK[^_S\_/S\^_O[_/[_`0($!@@* +M#`X.#0T-#`D&`_[Y\NKAV=#(P;NVL:ZMKK*\RMSQ!QLM.T9-4%!-1T`W,"@B +M'!<3#PL(!0(`_OW\^_KZ^OGY^?GY^OK[^_O[^_S\_?\``@0&"`D+#`T-#0T- +M"PD'`__Z\^OCVM#'OK>QK*BGJK"[RM[T"AXP/DA/4E%.2$$Y,"DB'!<3#PL' +M!0'__?S[^OKZ^OKZ^OGY^?GY^?KY^?K[_/X!`P4'"0H+#`P,"PL*"0@%`O_Z +M].WFW=7,Q+RWL:VKJZZVQ-;K`18I.45-45)/2D,\,RLE'QD5$0T*!@0!_OW[ +M^OGY^?GY^?GY^?GZ^OKZ^OK[_/W_`0,%!PD+#0X.#0T,"PD'!`']]N[GWM7, +MP[NUKZNHIZJRP-/I`!8J.TA05%113$4]-"PE'AD5$0T)!@,`_OW[^OKZ^OKZ +M^OKZ^OKZ^OKZ^?GZ^_S^``($!0<("0L,#0X-#0T+"`4!_/7LY-O2R+^XL:NG +MI:>LM\?<]`LA,T),4E533TA!."\H(AP7$P\+"`0"`/[\^_KY^?GY^?GY^?KZ +M^_OZ^OKZ^OO]_P$#!`8("@P-#@X.#0P+"`4"_??PY][5S<2\M:^JIJ:JL\'5 +M[`,9+#Q(4%1444M$/#,L)1\9%!`,"`4"__W\^_KZ^?GY^?CX^/CY^?KY^?GY +M^OO\_@$#!0<("@P-#@X.#@T,"08#_OGRZN'8SL6\M*ZHI*.FK[W1Z0`7*SM( +M4%154DQ%/30L)1X9%!`,"04"__W[^OGY^?GY^?GY^?KZ^_O[^_KZ^OO\_?\! +M`P4("@P-#@X/#@T,"@8"_OCQZN'8S\>_N+*LJ*>HK[O,XOD0)#9$3E-44TY' +M/CKAV=#'OK>PJJ6CI:Z]T>H#&BX_2E)655)+1#LS*R0>&!00#0D% +M`O_]^_KY^?GY^?GY^?GZ^OO[^_KZ^OK\_?\``@0%!PD*"PP-#@X.#0H'!/_Y +M\NOAV,_%O+2MJ*2CIJZ\T.@`%RL\25%555),13TT+"4?&100#`D%`O_]_/KZ +M^?GY^OKY^?GY^OKZ^OGY^?K[_/X``@0%!PD+#`T-#@X.#0H'!0#Z\^SBV=#' +MOK>OJJ:DIZZ[SN3\$RWDV]+)P+BQJZ:C +MI*JUQ]SS#"(T0TY3551/2$`W+R?FYN@K<':]`XE.4A26%E644I!."\G(1L5$0T)!0+_ +M_?OY^/CX^?GZ^OO[^_O[^_OZ^?CX^?K\_@`"!`8)"PT.#@X-#0P+"08"_?CQ +MZ.#7SL6^MJ^GHJ&CK+S2[`4=,4%-5%=644I#.C$I(AP7$P\+"`0!__W[^OKZ +M^OKZ^OKZ^OO[^_OZ^?CX^?K[_?\!`P4'"0L,#0X/#P\.#`D%`/OT[./:TLG` +MN+"GH9^@I[;+Y/\8+C],5%A75$U%/#,J(QP7$P\+"`4!__W[^OGY^OKZ^OKZ +M^OO[^_OZ^?GY^?K[_?\!`P4("@L-#@\0$!`/#0H&`?SU[>3;TLB_MZZEGIN; +MHK''X?T6+4!-55E954]'/C4L)!X8$P\,"`0"__W[^OGY^?GZ^OK[^_O[^_O[ +M^OGX^/GZ_/X``@4'"@P-#Q`1$1`/#0D%`?OT[>3;TLG`MZ^HHIZ=I+''X?L6 +M+#Y,5%E954Y'/C4L)!X8%!`,"`4"__W[^OGY^?GZ^OK[^_O\_/S[^OGY^?GZ +M_/X``@0'"@P-#@\/#P\-#`D&`OWV[N;NI9Z9F)^O +MR.0!'#)$45E<6U9.13PR*2(;%A(."@8$`/[\^OGY^?KZ^_O\_/S\_?W\^_KZ +M^?GY^OO]``($!PH,#@\0$!`/#@P*!P+]]NWEW-/*P;FQIYZ7E)FIP=[\&#!# +M4EI=7%=01STT*R,<%Q,/"P@$`?[\^OGX^/GY^OO[_/S\_?W]_/OZ^?GY^OS^ +M``($!@@*#`T.#P\/#PT+"`3_^/#GW=3+PKFPIYZ7E9JHO]OY%2U`3UA<7%A1 +M2#XT*R,=%Q,/#`@%`O_]^_KY^?GZ^OO[^_S\_/S\_/OZ^?GY^OS^``($!PH, +M#@\/#P\/#PX,"04`^?'HWM7+PKFOI9R4D9:DO-GW%"U!4%I>7EI32D`V+24> +M&!,/"P@$`?[\^OGX^/GY^OO[_/S\_/S\_/OZ^?CX^?O]``($!PH,#@\0$1(2 +M$0\-"04`^?#HWM7,P[JPIIR4D).@M]3S$2M`3UE>7EI32D`V+24>&!,/#`@% +M`O_\^OGX^/GZ^_O\_/S\_/S\_/OY^/CX^?O]_@`#!@D,#Q$2$Q,2$A`-"@8` +M^?'GW=/)P+:LHI>0C9.CO-KY%B]#4EI>75E22#XT*R,<%Q(."P<%`?_]^_GY +M^?GZ^_S\_?W]_?W]_?SZ^?CX^/K[_0`"!`<+#0\0$1$2$0\-"@8`^?'HW];, +MP[JPIIR4CY*?MM/S$2H_4%I?7UM42D`W+B4>&!,/#`D%`O_]^_KY^?GZ^OO[ +M_/S\_/S\_/OZ^/CX^?K\_0`"!0@+#0\0$1(3$Q$/#`@"_/7LX]K0Q[ZUK**9 +MD9"9J\;E!"`W255<7EQ63D0Z,2DA&Q82#@H'`P#]^_KY^?GY^OO\_/S]_?W] +M_/SZ^?CW^/G[_0`#!0@,#@\0$1(2$A$."P<#_/7LX]K2R<&XKZ67U1D9NNRND'(SE*5EQ= +M6U5-1#HQ*"(<%Q,/"P<$`?[\^OGX^/GY^OO[^_O[^_S[^_KY^/CY^OS^``,& +M"`L.$!$1$A(2$0\,"03]]N_FW-/)P+>LH)6-BY2IQ>0#'S9)5EQ>7%9/1CPS +M*B,=%Q,/"P@$`/[[^?CX^/GZ^_O\_/S\_/S\^_KX]_?X^?K\_@`#!@H.$!$2 +M$Q,3$0\-"@8`^?#HW]7,P[FOI)B/BY"AN]KZ%S%$4UM>75A11SXU+"0>&!00 +M#`@%`?_\^OGX^/GY^OO[_/S\_/S\_/KY^/CX^OO]_P$#!@@*#0\0$1$1$0\- +M"@8"^_3LX]K1R+^UJY^5CY";LL_N#"8[3%9;7%E22D$X+R<@&A82#@H&`O_\ +M^OGX]_CY^OO[^_O[^_O\^_KY^/CX^/G[_@`#!@@+#0\1$A,3$Q$."P<"_/7M +MY-O2R<"VK**8D9":KLGH!R(X251;7%I43$,Z,2@B'!82#@L'`P#]^_GX^/CY +M^OO[^_O[^_O[^_KY^/CX^?O]_P$#!@@+#A`1$A,3$A$/#`@$_?;MY-O1R+ZT +MJ9V2BXN6K,GI"20[3%A>7UQ53D0[,BHB'!82#@H&`__]^_GW]_CX^?K[_/S\ +M_/S\_/OY^/?W^/K\_@`"!0<)#`\1$A04$A$/"P@#_O?OYMW3R;^UJIZ3BXN5 +MJL?G!R(Y2U==7EM6340Z,2DB'!<2#@H'`P'^^_GX^/CY^OO\_/S\_/S\^_KY +M^/?W]_G[_?\!`P8("PX0$A04%!02#PL&`?KQZ-[4R\&VJIV1AX6/I,+D!"$Y +M2U=>7UQ63T4[,BHB'!<2#@L'!`'^^_GX^/CY^OO\_?W]_?S\^_OY^/?W]_CZ +M_/X``P8)#0\1$Q04%!,1#@H%`/GRZ>#7S<2ZL*.7C(>-G[G:^A@R151<7UY8 +M44<^-"LC'1<3#PL(!0+__/KY^/CX^?K[_/S\_/S\_/OZ^??W]_CY^_W_`@4( +M#`\1$Q04%!01#@H&`?KRZN'8S\:]LZF=DHR.F[/1\1`J/T]975U:4TI`-RXF +M'QD5$0T)!@/__?OY^/CY^?K[^_S\_/S[^_OZ^??W]_?Y^_W_`@4(#`X0$A05 +M%102#PL'`OWV[>3;T\K"N;"DF9&/EJG$Y`0?-DE56UU;5$U$.C$H(1L6$@X* +M!P0!_OOY^/CX^?KZ^_S\_/S\_/O[^OGX^/GZ_/X``@0&"@T/$1,4%103$0T) +M!/[W[^?>USCVM'(OK6JGI.,C)>LR>D((SI+ +M5EQ=6E1,0SDP*"(<%A(."P<$`?W[^?CX^/GZ^_O\_/S\_/O[^OCW]_?X^?O^ +M`0,&"0P/$1(3%!03$0\,"03_^/#HW];-Q+FMH)2*B)&DP>(#(#=*5EU?7%5. +M13LR*2(<%Q(."P<$`?W[^??W]_CY^OO\_/W\_/S\^_GX^/CX^?O]``($!PD, +M#Q$3%!44$Q$."03_^._GWM3+PKFNHI6,B9&DP.`!'C9)55Q?7%9/13PS*B,= +M%Q,/"P@%`O[\^OCX^/CY^OO\_/S\_/S[^OCW]O;W^/K\_@$%"`L/$1(4%144 +M$Q$."@4`^O/JX=C/QKRQI9F.B(R=M];W%B]#45M>75A12#XU+"4=&!00#`D% +M`P#]^OCX^/CY^OO\_/S\_/S\^_KX]_?X^?K\_@`"!`<*#1`2$Q04%!(/#`@# +M_/7LXMG0QKRRIYN1C(Z;L]'Q$"H_3EA=75E224`W+B8?&100#`D%`__]^_GX +M^/CY^OO\_/S\_/S\^_KY^/?W]_G[_?\"!0@,$!,6%Q<6%1,0#`7EI32T$X+R<@&A41#0D&`O_\^OGX]_CY^OO\ +M_?W]_?S\^_KY]_?W^/GZ_/X!!`<+#Q(3%186%1,0#0D#_?;MY-O1R+^UJY^4 +MC8V7K,CH!R,Y2E9;7%I32T,Y,"DA&Q82#@H'!`'^_/KY^/CY^OO\_?W]_?S\ +M^_KY^/?W^/G[_/\"!0@,#A`1$A,3$A$/#0H%__CQZ-_6S,.YK:&4BXB1IDHJ*E:S+[`PH/4Y875Y:5$M"."\H(!H5$0T)!@(` +M_?KX]_?X^?K[_/S\_/S\_/OZ^?CW]_CZ^_T``@4("PX1$Q05%102$`P(`_WV +M[N77C?U65!'/3,J(QP7$P\,"04"__SZ^/?W^/GZ^_S]_?[^_?W[^OGW]_?X +M^OO]_P(%"0L-#Q$2$Q03$A`,"`/\\^OBV,W$NK"DF(V(C)RVUO@7,$547%]> +M65%(/C4L)!X8$P\+"`0!_OSZ^/?W^/GZ_/W^_O[^_?W[^OCW]_?X^OO]``(% +M"0P/$1,5%144$Q`,!P+[\^K@U\W$NK"CE8J%B9JVU_D9,T=67F%?65)(/S4L +M)1X8$P\+"`0!_OSZ^/?X^/G[_/W^_O[^_OW\^OCW]_?X^?O]``,%"`P.$!$3 +M%!03$@\,"`/\\^OAV,[$N:Z@D86`AIJXW/\?.4Q98&)?65%'/30K(QT7$@X* +M!@0!_?OY^/?X^/K[_?[______OW\^OCW]?7V^/K]``,&"0P/$1(3$Q,3$0X+ +M!@'[\^KAV,_&O;*FF8V'BIBRT_45,$5476!?6E)(/C0K)!P7$P\+"`4"`/W[ +M^?CX^?K[_/W^_O___OW\^OGW]_?W^?K\_@$$!PH-#Q$2$Q,3$@\,"03]]NWD +MW-/*P;>KG8^%A9"HR.P.*D%276)A7%1+03,%(SM.6F!@759. +M0SDP*"`:%1$-"@<$`?[\^OCW]_CZ^_S^______[]_/KX]_?W^/GZ_0`"!0D, +M$!(3%!04$Q(/"P8`^?#FW=3+PKFNHI2*AHV@O-X`'SE,66!A7EA/13LQ*"$; +M%A(."P@$`?[\^?CW]_CY^_W^_O____[]^_KX]_?W^/K\_@`#!@@+#A`1$Q04 +M$Q$."@8`^?+HW]?.Q;VSIIJ/BHV=M]?Y&#)&5%Q?75A01STT*R,=%Q,/"P@% +M`O_\^OGX^/CY^OS]_O[^_O[\^_KY^/?W^/K\_@`#!0@+#0\1$A(3$Q(/#`@" +M_/3LX]K1R+^TJ)J.AXF7L='S%"]$4UQ@7UI324`V+24?&100#`D%`O_\^OCW +M]_CY^OS]_?[^_OW\^_GX]_;V^/G[_@$$!PH-$!(3$Q03$A`-"@8!^O/KXMG0 +MQ[VSIYR1BXV;L]/U%"Y#4EM>75E123\V+24>&!,/#`D%`@#\^OGW]_CY^OS] +M_O[^_O[]_/KY^/?W^?K\_@`#!@D-#Q$3%!03$Q$."@8`^?+IX-?/QKVSIYJ/ +MB8R:LM+T$RY#4UU@7UM32D`W+B4?&100#`@&`__]^_GX^/CY^OS]_O[__O[\ +M^_GX]_;W^/G\_@`#!0@+#A`2$A,3$A$."P@"_/7MY-O2R<"WK*"5C8R6JL?H +M""4\35A>7EM53$(Y," +M7UQ6340Z,2DA&Q81#0H'!`'^_/KY^/CY^OO\_?[^_O[]_/OY^/?W]_CZ_/\" +M!`<*#0\1$Q04%!,1#@H%_O?OYMS2R+ZSIYJ-@X*-I<7J#"E!4EUB8EY73D4Z +M,2@A&Q41#0D%`O_\^?CW]_?X^OS]_O_____^_/OY]_;V]_CZ_/X!!`<+#A$3 +M%!44$Q(/#`@#_?7MY-K1R+^UJYZ2BHJ4J:CXF- +MG;C9^A@Q15)97%I4340Z,2DA&Q82#@L(!0+__/KY^/CY^OO\_?[____^_?OY +M^/CX^/CY^_W_`00(#`\0$1,4$Q,1#PL'`OOT[.+9T,B_M:B:CXN0H;S<_1LS +M1E)96UE42T(Y,"@@&A00#`D&`P#^_/KY^/GY^OS]_O_____^_?OZ^/?W]_CY +M^_W_`@0'"PX0$A,3$Q(0#@L&`?SU[>3,D!)34U*13XW+B[HX=K4S<2[LJJGK+G. +MYP`6*#8_1$9$0#DS+"4?&100#0H(!0,!__W[^OGZ^OO\_?[______OW\^OKY +M^?GZ^_S^``($!@D+#`T-#0T-#`H(!0'\]_'KY-[8T&141#0H(!@,!__W[^OKZ^OO\_?[^_____OW\^_KZ^?KZ^_S] +M_P$#!0@+#0X/#PX-#`H'!`'\]_+KY-[7T/=U]#'O[>TMK_/X_@,'2HS.#HY-S(M)R`;%A(.#`D'!0," +M`/[]_/O[^_S\_?[^_____OW]_/OZ^OK[_/W^``(#!0<)"@H+"PL+"@D'!`'^ +M^O7OZN7@V]7.Q;ZZN;_,W_,'&"4O-3@X-3$L)B$;%Q,/#`H(!@0"`/_]_/O[ +M^_O\_?[^_____O[]_/O[^OKZ^_S]_P$"!`8'"0H+#`P,"PD'!0+_^_;Q[.?A +MW-;/Q[^ZNL#.X/0'%R0M,S4U,R\J)!\:%A(."PD'!0,!__[\^_O[^_O\_?[^ +M_____O[]_/OZ^OKZ^_S]_P`"`P4("@L,#`P+"PD'!0+_^_?R[>GDW]K3S,7` +MOL/.W_$#$A\H+C$R,"TH(QX9%1$-"PD'!00"`/_^_?S[^_S\_?[^_O[^_O[] +M_/S[^_K[^_S]_@`!`P4'"`D+"PL+"PH(!@,`_?GT[^KEX-S7T,K%P\?0W>W^ +M#1HD*BXO+BLH(QX:%A(/#`D'!0,"`/_^_?S\_/S]_?[^_____O[]_?S[^_O[ +M_/W^_P`!`@,%!P@)"@H*"0@'!@0!_OKV\N[IY>'7@V];2S\_4W>GV!`\8'R,F)B0B'QL7%!`-"PD' +M!@0#`0#__OW\_/S]_?[^_O[___[^_OW\_/S\_/W]_O\``0($!08&!P<("`<& +M!00"`/WZ]_/P[.CDX-O7U-/6W>?S``L4&Q\B(R(@'1H6$Q`-"PD'!@0#`@'_ +M_OW]_/S]_?W^_O[^_O[^_OW]_/S\_/W]_O\``0(#!08'!P@("`<&!0,"`/[[ +M^/7R[NOHY.#7P_`<0%AL?("`>'!D6$P\-"PD'!@0#`@$`__[]_?W] +M_?W^_O[^_O[^_OW]_?W\_?W]_O\```$"!`4&!P<'!P<&!00"`?_\^?;S\.SJ +MY^/?W-K:WN;P^@0-$Q@<'1T<&A@5$@\-"PD'!@0#`@$`__[^_?W]_?[^_O[^ +M_O[^_O[]_?W]_?W]_O__``$"`P0%!@8&!@8&!0,"`?_]^_CU\_#MZN?DX=_? +MX>;M]O\(#A08&1H:&!84$0\,"@@'!@0#`@$``/_^_OW]_?[^_O[^_O[^_OW] +M_?W]_?W^_O__``$"`P0%!08&!@8%!00#`0#^_/KW]/+O[.GFXN#@X>;N]O\' +M#1(6%Q@8%A02$`X,"@@'!00#`@$`___^_OW]_?[^_O[____^_O[^_?W]_?W] +M_O[_``$"`@,$!04%!04$!`,"`?_^_/KX]O3Q[^WIY^7DY>GO]OX$"@\2%!45 +M%!(1#PP+"0<&!00#`@$``/_^_O[^_O[^_O[______O[^_OW]_?[^_O[_```! +M`@,#!`0$!`0$!`,"`0#__OSZ^/;S\>_LZ>?FY^KO]OT#"0T0$A,3$A$/#0L* +M"`<%!`,"`0$`___^_O[^_O[^__________[^_O[^_O[^_O[__P`!`0(#`P,$ +M!`0$`P,"`0'__OW[^??V]/+O[>OIZ>SP]OP"!PL.#Q`0$`X-"PH(!P8$!`," +M`0$``/___O[^_O[____________^_O[^_O[^_O___P```0("`P,#`P,#`P(" +M`0#__OW\^OGW]O3S\>_M[>_R]OL`!`@+#`T.#0P+"@@'!@4$`P("`0$``/__ +M__[^_O[______________O[^_O[^_O___P```0$"`@("`@("`@(!`0#___[\ +M^_KY^/?U]/+Q\?+T]_O_`P8("@L+"PH)"`<&!00#`@(!`0$``/__________ +M_________________________P````$!`0$!`@("`0$!`0``__[]_?S[^OGX +M]_;U]/7V^/O_`00&!P@("`@'!@4%!`,"`@$!`0````#_________________ +M__________________\````!`0$!`0$!`0$!````___^_OW\_/OZ^OGX^/CY +M^OS^``($!04%!04%!`0#`@("`0$!``````#_________________________ +M__________\``````````0$`````````_____O[^_?W]_/S[^_O[_/W_``$" +M`@,#`P,"`@(!`0$!``````````#_________________________________ +M__\`````````````````````______________[^_O[^____```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````!#3TU-```` +M$@`!```W%@`(0`VL1````````$U!4DL````"``!)3E-4````%#P``'\`?P`` +M````````````````05!03````:A39#)A``(```````````!%]```!``*`*`` +M`'E%]````@`*J8$``'D(Q2LCU@`````@5@`BW@!$]`````%$!P`%]"(`__\% +M]"4``_]F]!,`#`!(`0`&`(0``(Q(7@!G]```#``%]"<``_\*J:,``+,*J80` +M`)$*J"0,`*4-`2(*J80``+-A]```!`!']```#@`-`+4BK@`@`'0.D)X-`3X* +MJ80``+,-`2(*J80``+-A]```!`!']```#@`-`+4BK@`@`'0.$*P-`3X*J:0` +M`)<*J`0```0*`*```.9"@0!(@@!$]"`___]$@T9(`49$]````"!$]$```_\A +MT@!6@0`&WR!'!B-$]$8`#`!%@T`$"```````"0`C`````````````````'@` +M```$``0```````#\`/___X`````!`0`&455N:71S```````````````````` +M`````````````````````0(`!B!S86UP````%9N(&EN=&4@9&5L87,@9IIR````'@```!6`'&XO!RF````'`!6``!7 +M```````#__\````\```````$ +M__\```!:```````````````````````````````````````````````````` +/```````````````````` +` +end diff --git a/sys/share/sounds/wdnflute.uu b/sys/share/sounds/wdnflute.uu new file mode 100644 index 0000000..d60f76f --- /dev/null +++ b/sys/share/sounds/wdnflute.uu @@ -0,0 +1,328 @@ +begin 644 Wooden_Flute +M``Q7;V]D96X@1FQU=&4````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$````!%````````#8B```! +MSJ<]K;"G/:VZ``````````````````````````````"!@1V.``!&3U)-```V +M&D%)1D934TY$```T'@```````````````````````/____________\````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````$!``$!``$!`0$```$!`0`!`0$! +M`0$!`0$!`0$!`0$!`?_^_P#__P$!`/_]^OG^`0,*#0@)#0@#`?\!`P0%!08% +M`/SZ^OCT]/CZ!`P"]_W_]_3V_PH-"0+\_?[^`P(`__L&&A,&!P4`_@(-%142 +M"/SQ[/#[^^SF[.WGZ.KEX^CFXNWY^_\*!?G_!/X*&AL4#P\1#P<`!@P%^.KH +M\_3FWN7EUY];5XN3E\?GS^`D-"Q4G,2\Q-3(L*RXF*#H_,QX4&Q3ZZ^SIX.#= +MS,?1SK^YOL_C[?D-&1L:&"$O-3M"0D5,0SI(6E`^."T?&Q0#[^'5R,3$O;V_ +MKZ*HNY +ML;*\QM3A]`L,#"<^3%I0.3=#1DQE;E];5S\E$__KY./6O[:VKJ:ML:*5FZJY +MSN;U`0L1$Q,:*#I/6%%,3E5>75-%,B,@'@X`_>W2P[6GL;VUL;2RM+C#V.7L +M^/\%$R4X0$5.2T%$2#X[361;/#`O'Q02^]"ZP,2ZN,#"N*^FG):BR>WR[P$7 +M%A0M/S8O-#],6&EY=V=)(`D1'A<*"?O7PKZYKZ2>I*BDKLO1S.#PXM7>]0\M +M3EY22$4W*"TU-T-*0"L?(QH&[DYVQP]#7V^@"&"`C*"@@ +M'"4O/4U35U,[)2$>"O7S\^#1U-3%NK:NHYR9F**ZSM7AZ^/644T*!T%Y,_,T]C=Y=W`JZ>DH:*NQM_P_/X#'"P>$AD?*$1@9%E34TDW +M,#,N)1X3`_'HZ.'.OKFWL;6ZM+7$S]?F\O7W_@P<*3`W/4-,2T9`,BXW/#0L +M+S(F%/G4O\;.S/=Z?'ET+^UM+BPI:K`UN+N]O/U!108$0D3*#M!.S8X-"@B(2$K+R@B$_OP +MZMW3QKJWM[F^Q,.YL;.]U.SZ!Q`6(24A'1TG,S(O,T%/3D`U*QT1"0#QX-7; +MX=6^M+>XMK*NM<+*V/`!"!0;%A`,$"$T/D)!/#Q#0SPR&P<#!`D/`^C1P[>O +MKJ^PM<#%O[O(Y?;X^_KY#B\],2HS.#(L+S4X0U)--2XO\K.P[BTL;?) +MW.KS^/H"'3Q%-B8D)!P6*$A64U1+,QP2#@/SZ^;?V];/R<:\JZ.IM;[,Z0<1 +M!O\#"!$A-#\_/3T_1%%93CTW+!H7'AP0^M[)N;*YP\"VL[.SM\#0W^+I\_,` +M'2\W/SXL'"$S04E/3D4^-BLD&PX%\]K,S<_/S,6WJZ>LM,#6ZNSP_PH(#",V +M,RHG+C8X0E%00S@O(!PC(!``\=O&PL?*Q;RSJJ*HNL3*W.;=W/41'"0M-S4H +M*#8\/4%$/"\J*RHJ(Q+XW\_$OL++SL2WL:JGK\#+SMGO_P0-("LF(1T:)CE" +M2DY%/38K)RHF&A(%Y\[.U-#%N*ZKIZ>PM;?"S='8Y?@+%QT?'!XK.CX_14,Z +M-S0J*38]+`SSY]W1Q\;+Q+FXM;"UO<3*R\W9[PL>(A\>("`@*C0]148_-C0[ +M03LI&`CSYN7AULS%P;ZTJZVRM+S$P\KA^PP3%QH9'2HO+CM/4$$^/38X04,Z +M)1$$_/#;R+^YM[>TLK6ZNKBYN\'3[@88)"@H*"[J +MW\J[N+:OJ[&YNK2RNL35[?P!"A@B*C`R-3]#/3TL:ZKI*2O +MNL?7W^K_!@$.*#8_0CL]1$%!2U!023PS+R06"?GFS[ZZO+RWLJ^HI*2FK\'. +MV>S[_P84(RTT.CT\0$=/55%(2$0V*24?$O_FS\&YM+2ULJVHJ*NLL+W,V.'H +M^1$@*#8^-S(]2$='4%A734`Y-BXA#_GJVL>_P;VVM+.MIZ:MO,7*U.'O_@\B +M,38Z04,_04]=75504$U$.S$C$_[FU,K!NKN_MJ>AIZVML;[,UN'M_1,D*"X[ +M0D!'5%A555E63$5`.SDN%/WMUL&^O;2NKJZLJ*:MM;K`S=OJ_A$A+S4U.4%% +M2$]87EI134U(0#4E$?WLW,W`N;>RK:FFI:FPM;B\Q=/H_A$?*#`W/D)"2%1= +M6E-,24I(/B\@#?KJV\_&N[*MJ:*>HZRQL;:_QM#B]0<;+#4W.T%'4%-03U!2 +M4TU&0C8A#O_IS\;)PK.JIZBGHZ2LM+:[Q=3F^@\A)R@P/D=)3%):751+2TY+ +M13DB!_?QZ-;!MK2RJJ.AIJVRM+6XO\[F^@40(C<^.CY*45164E%345)42#$= +M%`;MV<[*Q[JII*6FJ*BLL;:]Q]+<[`@A+"TQ/4I355)-35)45%122T(M"N[F +MYMO*OKFVKJ2@HZ2GK[B\P,WE_0@*$B,S/T=.55I844E'2U%643PG&0SVWL_( +MP+:OK*JHJ*JIJ*FQOM'F]P,1'2(J-D)-65Q33E)64U%12CHI&PS^[=W,O;&G +MI*JNJZBHJJRON,S@\P42&B4O.TA/3DY25%-56EI52SHG%PT"\^+0O["LK:FI +MK*NIJJZTOLS=[?X.'"4P/DA04TU+3E%67V)92CLK'0[_\^;5P[2LJZRMK*:D +MIZJPP-'>[O\'#QXN/4I14E%.35-;7UI01SDI(1L/_>;*M[.MIJ:NL*BCI:FN +MN,;6YO+_%"HX/4%%1T9"151@965803(L(1(%^>[;PK.OJZJJIJ*BHJ:RP,G1 +MW^[Y!!DS0TM.2D)$3UE>8&!:3D`P(Q@/_N;.OK:OK*RIHIZ>H*:MNLK6W>;T +M"1XP/4)(44Y&2U5:7%U82#@U,B$,^>/+N[.OL+*OI9Z=GJ&LOH*BRN\/+T]WP"24Z03]! +M1TI)35EB85I23$4Z+B02]=K)PKRVL;"KGY>6GJBPN,?4V.'T"B`R.T1,24E6 +M7EU=6E-12T%!/"969E +M74]$149!.BP8`.3/Q+JQK:FBGI^BI:JQM;:]S-[X`.SI`0DA58FAD8F%9 +M3TI$-!X*].#5S,.[L:6:E9>:H*NTN+J]R=[V"AXR0$5#1U1>8%Y;6596659+ +M.B0-]-_0QL"\M*B=FIF9GJ2JK[;"TN'T#"(O,C8_35]I:&1A6$]/5%1,/RX4 +M]=_7S+ROJ*2@G9VAHZ6FIZNUQ>($'2@M,3="2D]<:VQD86%=6U=&+1@)_?#D +MU<.VJYV3D9:=I:NJJ;"XQ-KU"ADK/$1)5F%B7EM97&%C8UY/.2(+]>7Z +MK*"9FIJ8G:2CI:^_S-WT!A0C,3Q+66-H9EY85EA<7UM202<0_NS8RL*WJ:*? +MG9ZAH9N9GZFWS>?["18B*S5!45UB9F=D8U]=8%A%,2`1"`#OVL:RHYR:F9R? +MHZ6CHJFVP]3I_!`@+T)25UA<7%=35F!L;F!,-R,1`/'GW,FWK*2=FYZ>F9>; +MHZV[S-CD^@P6)CQ.769B5E-87&!E95]20"P8!_KLV<2TK:JEGY^?F9*4GZJU +MR>'O]?X,(SM)4%MC86%C96=D7%A0/2TI(0GLUL.SIZ"@I**?GIJ9G*.PP,[@ +M]0L?+SQ*4E945%MF;7)P:%Q+.2TD$P/XX\FXK*2CH)N9EY27HZRRO3D$T)`[QWM7,P;:KGY60D9*6GJFRN;[$S^D*(#!`3E9;7E]?86-E +M8EI:6U-"*@SRX='"N;*LIJ"7D(^4FZ6KKKG*W/`%&2P[1$M25V!M<&5=7%Y? +M6TX\*1D$ZM3&N[*JHIJ4E9F;G)VAJK?#U>T"%BLW/49066!D9VEF9&-=54Y# +M+18"[MS,O[6JG9:7EY.5G*6IJ[+`T^C\#B4[1DI586-?865C7%QG:EI#+QD" +MZM?,P[:KIJ2?F).3EYN@J[G&U>G_$1LE-D=37VIM:&)?8F1@651+."`(].32 +MP+2KHYN7F9N8E9FBJ;&]S^;\#APJ.4=5865E9F5I:V5?7%E-.B,+]N75P[2I +MHIV9F9F9F)F=IJZYR^3["18H/DI/5EQ?8V=H:6AG8E1",2$._N[6Q+BKHY^: +MEI>7E9B?IK&^R=GO!1@H-T9/56!J:&5H:V=A6U5+.RH7`.K9R+6FGYRHJJVQ]KM!!DJ-T966U=99G!N;6UF7E5(."H:!>[:R;BKI)^\@VOL/4\@P?,3]* +M6V9F96IQ<&QI8UI22CXE"O;DT\&OI:&;E9*0D).9H*:JL2S,@$@#HT<&VK*&9E(^-D)69G*.LM<+2Y@$:+3U+56%L;FQN +M;6=I;FI>3STG$/CDU<6XK)^6DY&/CHZ2FJ.LM\31Y/P/(CA+661K:VIN<&MI +M96%?5T,L%/WHU<&RJJ.'%H96=E5T,Q(`_Y +MW\BWJJ*=EY&.CI.7EY:?KKW-W.T$'C1#351B5D@R'P_XWLF[ +MKJ2;E)&3DH^,D)JEK[K'U>P(&B,V4&-I:6MU>'!L:V-=5THY)`[YYM.[J)^< +MF9:2CXZ0E)>4T8S)!8!ZM&YK:B=DY*2 +MD(^0D)>AI['!T.;]$BE`3%5A:FYN;W)V=&QD5D0X*!'^[=6_M:F;E9*/CH^/ +MD9:?J[?!R=GV$R8W35]H:FIMA +MIZV[TN;V""(Z2U5<96YP='Q[<&AD74\[*!P/]=C#M:NBFY:1C8Z1DY.:J+>^ +MQ-7Q#B,W2%)<9VYS=7-Q=G1C44E%.2`$\-_*MZFAG)6-CI*0CYBAIJNVQMCN +M!ATV2UE?9&AJ;W=Y<6EE8UQ%+AT*\-G)NJ^GGYB2BXB-DI:;HJV[QM3I`1HP +M/TQ98VQS=7)P;FEF85-%.B4+]-[*N["HH)>0CHR-CY.;I*RUP-'H`!79R;6AE7U9-0"D.^N3,O;.HGI:2D8^.D)28G:>SP=/F_!S4PK>MH)22E)&0DI.6G:6POLW@_!0C+T!48FMQ=7=W;VAH +M95U00C(:_^O9QK2HH)N5CXZ1E)24EY^JN +MT<&QIYZ5D(^0D)"2F*&KN,G9ZO\4)3=-86YSWEP;&I>2C@K&@3OVL2SI9V;F92/ +MC(V0DYBDLK[-X/(%'C=(56!I<7)O6D8Z, +MCI>DK[G'WO4)'3-&4UQC9FQV?'MXG_&31*5U]D:FYR='5W=6M>4D[LJB>EI.0C8N-DIFCK;7" +MU>?\&#)#4F!I;&QQ=W5R;FID64LZ)Q+YW\NZKJ:AGYJ1C(R,C9*=J;?$TN@# +M&2D[35998W)^?WMY_KZ:I*ZZR^7_&#)" +M25=C8F1N=7EY=&MC6DHW)Q?]X=+'N*NCG)2.BXR.D9>>IK"\R-OX$B=I*RVP]7L`QPU2%):9W-V<&UN;FUI +M9%M+-B`([]G(P+BLGY:1CXZ,BY"7H*RYQ-7O!A4E-DQ@;71U[8Q+6FG)B3CY"1DI26FZ2PO<_D^Q8K.4I;86-KS: +MRKNMHIJ6DH^/CX^4G*:QO=#H^@D=,D)6:&YO<6]N<&QF8%I00B\9!?/=QK6L +MHYN9F9:0CY*3EI^MOM'D]PPA,T%,5V5N,D116F!E:FYU>G9M95Q-.RH<"_GHUL.RIIZ:EI", +MCY*5G*6NN<33Z``3*D=97&%I;6YK9VIN:%Y52#@C#?OFSK^VK:6@FY>3CXN/ +MF**MN,35ZO\3*3U+5F-H:6QP3CXV0EZ"K +MN,//X_L1)SU.6%M@:W%R1C8R.E9^KML#+WO@0 +M(2\^46%H:W!U=FYD6U--3$DZ'@#KW,NYKJFDGYJ4CY"4F9VBJ[K/Y/<,'S)$ +M35%=;GAY=FYE75I73#\T)Q+WV\:ZM:^CF965EI64E)FBK+G'V?85*#4\0DY@ +M:W%V=6]L:5U334,R(`OTY=?&N*ZCFY61D9*4F)ZBJ;7&V_,'%R@Z2UAA;75W +M=&QC75Q<54DY(PW[ZM7$N:VCFY:4EIB7EIB=I;+$V>X%&BHW1U5>:&]Q<6QF +M9FA@440V)Q+ZY]O/O:RCGYN5DY25DY6=J+/`UO,)%2`P0%!>:'!U<6MJ:V18 +M3T:&UM:VAD96AE6$0R +M)1D'\MW-P;:HFY:7F9J8DY*8I+/!S^/[$2(P/4M>:VUH9VYO:F5@5D@U(1,( +M^NK9QK&BG9^?F).3E9B=I;"_T>/S`1$J0U9?8&)J;69E:&1=5T@T)!<-`NK/ +MOK:LHYV:F9B5E9::I+/!S=GI`1PP0%!=9FEE865J;6YE44([+QX+^>C8R+BM +MI:&=FYB1C).?J;&]RMGL_@XC/E)<8&-E:&UN:61;4DD]+!T2!O+4EYB_>J;.ZQ-'C^1`F/4]87%M=:'-T;6=A54<]-"H:!?+@S+VUKZFBF9.3E)>? +MJ+&YPL_?\PLE/$A-5V-G:6]P:F!53TU$.3`A"O+;HJRV +MP,W?]0HA-T%&359E<7-O;&1634E"/#,?#/K@RL*\LJFCG)>6EI:;I*RRNLG= +M\08>,SU&4%ME:VUP;6)74$I(0C`?$?G@T\F]LZVFG9>3E)J?HJBSO<;6[P<9 +M*#9$3UIE;'%P9EM644Q)13HF#_KGULB_N*^DG9J8FIRMM<7;\P@8)3,_ +M2%5C;71R:&%:4D]+/S,F%`#PX,_!N*RBG9N:FY^@H*2JM,/4ZP09)3`\2%5B +M:6UM9V%=6%%+0S4C$/[RZ]K&N:^CG)J:G)^AHJ2HL<#2Z@$0'S`[1E5A96IL +M9V%<55=81"XB$P3Z[-K,OJ^DGIJ9G:&BHJ&EL\32X?,)("TU1EID:6AC8&!@ +M7UQ1034H%@;XZ^#1OZZEH9V?H9V:G:2JLKW-Y/H)$R(W2UA=8VAE9&5A6UA4 +M2SPF$@D"]^;/O;.LI:">G9^AGZ"HLL'3W^P`$R0X2U1;8V9E9&)@85]51#,G +M'Q,#\N+0P[>IH*"BHI^;G**IL;[-V^S_$2(T1E9A95];7V=I9%M01CTN&0L% +M^NG7Q+2MJ:6AG9R3U!QPU2%5=75MD9U]:7EU523DI)!D%].31 +MPK>OJZBCH)Z;FIZGL\',UN3R!1XP/4]965UF96!@7E9.13HN(Q@*^^C0P+FR +MJZ>EHI^ +MGJ.JL+O*U-KG`!PP.D-.5EU@8&5H74]+1SPT,281^^C:S<*XLJ^LI9^>H*.J +ML[K!RMOU"A0A,T%)45A>9FIE7%)'0D$],B(2!?3>S<.\MJ^II**BI*>JK;6^ +MRMON`18J-CU"2E5?9FEG7%%+13XY+R(4!._?U,G`MZVHI:*AI:JLL+6[QM;I +M`!6%%+2DI#,1\3!O3EVL_&N[&KI:&FJZNJJK*^R=7I_@X=*S9` +M2U-<8V-;5%143D8[,",4!/?NXM7*NZRFIJBIJZRKL+6XPM3H_`H9*3=#3UA; +M5U596U=034Y&,A\/`??PYM?)O+2NJ*2DJ*NMKK7`R='?\@02(#5)45-:7U=. +M3E!36%%"-RD3`OCLX=?,PK>NK:RII:*DJ[6_R]OG\?T*&"L\3%MD7U135E!* +M24<]-BX>#@'RW]#%N[.PL;&PJZ:FJ:ZVP];G]0(-&BT]1$U75U9865A43TE` +M,"(:$P?WZMC&O+:RKJZOK*>GJ["ZR-/=[/P,'3$_1TU14%!76EA75$IKK6^QL_;ZO8%&B\^2E)245185U943D=!.2XB%PS^ +MZ=/&P;ZYM+.PJZBGJ;*]QLW:[?L('C(\049+4%576U]=4D,V+RPF&P[]Z]S. +MP+JXM;&MJ:JNL+2]P\C3X/,-(R\Z1$=$2%)965A744<_-RLA&@[YYMK3R\.Z +MLZ^KJJJKL+>]P\G3X.\&'BXU/$9,35%765A324`[/#DL'`[ZX];0R\.]NK2L +MIZFNL;2YP,G6Y/4(&B8O.D%$35EA7U5*1D([-S8O(A']Z]_4R<"\M[*RL:^P +MLK>ZN\'.X?@.'2DS.#M!2E!66EM73$`_0#@I&`G\\.3;UL`%B@Q-C]-5%974DI'1DE-1CDP(0KWZ=_;ULO"O+*LL+"LK[>[OL// +MXOD(#AK9S\K!NK6PK[2WM[2QM,'0W_0* +M%!LG,#=!3%%35%)/24=(0C0H'1#_\N_IVLJ_M:VML;.UMK2VO,/+V.T`#![DV\_"NK6UM+.RL;"SN\73Y?+[!0\9*#E) +M5E=22TA(1T5$0SPQ)!<+_N_BUYM[6SLK.YP,3,V>;O^@D9*SU*3$=#0D-+44U) +M0SLR)Q@/"/WKV[O;_$S]OE^`\@*"DO-SD]25!23T<^.C8Q*R@@$0'QY-S6SL7` +MNK2WO+N[P,3'S-7C]@<7)S$P,SU$1DI+2$4_.C@V-"P=#`#RX]S:T';T<>_N[NYN<#" +MP+_"Q]+E^@D7("8M,#4_1TQ.24)`/3@T+24<$`7\\N?=U,BYL;6_P\/!P<+! +MQ-#?[P(6)2PM+SI%1$!!0D-$0S\[,B48"_SS\_+FU[@T_?T?U!!`;(RDV04%"2$8Z,2LG+#(M(AH0`?/MZN38S3>TL.ZN\#&R\S+R\G+ +MU^?U`0X;(BG?U,?#QL;&R,;!PL?2X>OR_P<%#B`N +M.TE)03LV,C,T+RDD'1D2!P($`>_9RLC*QL7)R<3!PL?2WNCU```"#R$S041" +M/SLU,30U+RPI&PP-$P\$]NG>U&QH4$P\#^.S:T<_*RL[+P;V_PT]#-R\O%P<+`Q-/9UMSEZO0%%"0X.C0T-3@^04`\+R(:%A<9 +M&1,,_NOAW=?3S<*]P,#`R]C:UMCSHY-O5TL>\N+K!RM#6V]S:XO(!$"0R,R\P.$%%0CTU*1X9&2$B&A(+^>CC +MY.3S;RK^ZNL') +MS<_.SM+9Y?,"$1<8'S`_1DE)034I(B,F*2DF'`X!^/7V\>37S<2]O\?,S,G% +MQG6Q+V^P,7-T76QL#`P\C+R,;%PL;6[?G^ +M!`8-'"X[2$]'.3$J)RDM+BD@$0@+"?[Y].?5QL+%RL[*Q+VZO\S=[??^!`4* +M%R@Z1TM(/SO9S)RDA%`?]]_'K +MY=[0O[>YNK[)TM+,Q\G7ZOL,'"4K,#,Y0D4_.#$G)2@L+2@?%P?V\/#Q[.'2 +MQ;VYNL#%Q\?)SM;=Z/P0%Q<<*3=!1DE)03`B(B0B)BPJ'`K^_/?LX-?/R+^[ +MP\O*Q\2_O\K<]`@-$AXB(RTZ0TM)/34N(R8P*Q\;%P\'`?SW[MO&N[R^P,K3 +MSL/!Q#-P;_!P\3%PL'$ +MR='=[OP"!0X=*C1"3DH\,R\I)2DM+2H>$@X,!?GOYMC)Q,7'R[Y +M!A0A+SQ"0#DQ*BPS.#@Q*20;#`0'"@7[[^'0QZ.WN\P4> +M,3]+2STT,"LG*2XR,2L@%1(0!OOPX]G6U]#(Q\.[M[B]Q]?EZ^_U^P,2)39! +M0T`^.C,O,C$J(1P;'A\:#P'SY=;/SLS,R\?"O+J\Q=/#6RLC)Q<&^OL#%S=/7W./K]00<,3U`.3,W.30S +M,RTH*"8D(1L5$0/NXMS:W=C*P\.]M[C`R=#8X^GK]@P<(RLS-SU`/3L].2TA +M&APA)28A$P#S[.'6S\[/RL*_N[S%R\K-U>'Q!`\4'RXU-#4Y/3]`.B\E'QX? +M'QX<&`KV[.?=TLS(P+W`P\7'QLG0U]GA]PX=*3(V-S0S.C\X,#$Q)B$D)R09 +M!_SZ\>7BXMC'O+N[N;[(R\S0T]OJ]P$0(RLO-CH\0$$Y,2PD(2HL)"`<$@;[ +M\.GCV=#+P[NZP<7%Q,7*T=GF]@85'R4N.#HX/#XX,R\L+"%Q$&^O7PW\S$Q,;%P[^\ +MP,7$Q]3C\OX(#18C,T!#.S@[.#`M*RHM+"8B&0T(!_OHVM/,R,._P<7`N[W# +MS-7A\/H`"ALH+SA`1$,Y+BPM+S$P+"+#<_0SXX-C4R,3`I(B$A'QH0!O_RW,W)RLC#P[W"1LC +M*#$\/S@Q-#DV-#$K)R0B(AT2!?SSYM;-S,W)P+JXN+S#Q\W8XN_^!@P:*C0X +M.#8X.ST\-"#_[QY-G1R<7$P\"^O\"]O3R!A@D*R\R-#8X +M-S8W.#4O)B`A)!X6#P+TZ^+5S,7`P;^YN;_'S,O,UN/O`18C*C(U-34S-3HZ +M-C$L*BPK(A@2"P/YZ^#;US\.^N[N^P<#"QL7(T=WK_@X:)"PQ-CDW-CDV-#0Q+RPI)B$8 +M"@'_^>O=TLW&NK2XO<##Q<;+T=WN^@,1'RHS.3L]/#DT,"PH+C@W*R(<$@3U +M[.WGV=#)P+R[N[R\N\',V>3N^P8-%1\I,S]&1#DS-3,L)B8N-"T?$PK_]>SB +MULW%P<7#NKG`P<#&S]ON_0()%R,J,CL_/#@V-3$N+C`R+2`8%@\$^^_CV\_$ +MP;ZYO'AL1`?;QW\K&R,;%P[VYNK[%SM;< +MYO8$#A4>+#H]-2\R/#\W,"XH)R8A&QD5#/_KUWT`!4E*2PO +M,#,Y/3PW+BOW``L8("$D+#4W-C;U`0D0%!DF,3$M+2\M)2`E*RXI'A41 +M"?_\_//DV]7,QLC.TM'-RL_9X>CT_@$%$!LB*2\V-RP?'2(E*"HF(!H1"03] +M^//IW=+,S-+2T,_-S,_7X.OU_0$$"A0@*S,T+R@C(R,A)"@F'QD5#@)RTM*2&101$@\&_?7KX-G6V=O9 +MU=+0T]C>XN?O]?K_!A(@)R(!\>'R$>%A$4%Q@7%0\'_O?Q[>GHY^3@W=S>X>/CY.;K\/;^!PT1 +M%A84%QXA(1\;%Q43$A,5%`X&`/GT\O#JYN+>WN'CX^7FYN?I[//_"`T/$1,6 +M&1L;'!P9%105%185$0H#_?KY]_+LY^/AX>'BY>CGYNCL\/?^`@<,#@\3&1P< +M&QH6$A`2%!84$`P&`?SX]?/NZ.7DY.7HZ.;GY^CJ\/D`!`8)#0X1%1@;&Q@4 +M$A,3$A,2#0@%`P#]^?3MZ>;EY>?JZ^KIZ.GM\O?[`04("PX3%A86%A01#Q(5 +M%1,0#`@$`/[]^_CR[.GHZ.GK[.KIZ^WO\OC]_P`#"`T3%185%!$/#A`1$1(1 +M#0@#`0'^^O;R[NWL[.WN[.KJZ^WR]OK]_@`#!@H.$104$A`/$`\/#PT+"0@' +M!0+^_/GR[>SM[_#P[^_N[>[R]??Z_@($!PL/$`\-#0X.#@\1$`T*!P4#`P(! +M_?CU\_#O[_#P\._O\?/T]_KY^OX"!@H-#@\.#`L+#0T,#0T*!P<&`P'__?GV +M]//S\_/Q\?'Q\O3V^/K[_?\``P<*"PL,#`L+#`L*"0@'!P<&!0(`_?GW]?7U +M]?7V]//S]/7V^/K[_?\!!`8("`@("0D+"PL)"`<&!00$`P,!_OOY^/?V]O;U +M]O;V]_?Y^OK[_/T`!`8'!P@'!P<'!P@'!P8%!00$`P'__?S[^_KY^?CX]_?W +M^/K[^_O\_?\``0,$!04&!@8&!@4%!`,$!`0#`@$`_OW\_/S[^_OZ^OKZ^OK[ +M_/W]_O\``0("`@,#`P0$!`,#`P("`@("`0$`__[^_?W]_/S\_/W]_?W]_?W^ +M_O\```$!`0$!`0$!`0$!`0$!`0$```#___________________________\` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````!#3TU-````$@`! +M```T%@`(0`VL1````````$U!4DL````"``!)3E-4````%#P``'\`?P`````` +M````````````05!03````:A39#)A``(````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`H`````````````````````````````````````````````````````````` +M```````````````````````$"```````"0`C`````````````````'@````$ +M``0```````#\`/___X`````!`0`&455N:71S```````````````````````` +M`````````````````0(`!B!S86UP````%;_^?_[__S_^__\__S__/_]__W__?____X````"``$``P5& +M;'5T90(```!!249&4V0R80`````````````2J0````!!249&4V0R80`````` +M`````````````````````````*<]K;```#8B```!S@"*`(``@0!T`&$`5P!' +M`#X`-P`U`"X`'P`A`!``#``5`!<`'0`8`"(`*0`P`#,`-`!"`$@`/P!#`$4` +M0``R`"X`-@`F`"``)@`<`"``)0`B`"(`(@`@`"D`,``D`!X`&0`-``;__O_V +M__W_^O_S__;_^@`&``T`#P`8`"(`*0`O`#H````:`"D`#P)J`S$`*@`#`FT# +M/0`I``\"00,B`0`````:`"D`#P%0`?$`*@`#`FT#/0`````````````````: +M`"P`%@$^`=P`*@`#`FT#/0`````````````````:```````````````````` +M``````````````````$````!>````'@```!6`'&XO!RF````'`!6``!7```````#__\````\```````$__\` +M``!:```````````````````````````````````````````````````````` +,```````````````` +` +end diff --git a/sys/share/sounds/wdnharp.uu b/sys/share/sounds/wdnharp.uu new file mode 100644 index 0000000..2f01e10 --- /dev/null +++ b/sys/share/sounds/wdnharp.uu @@ -0,0 +1,282 @@ +begin 644 Wooden_Harp +M``M7;V]D96X@2&%R<``````````````````````````````````````````` +M``````````````````````````!!249&4V0R80$``$X`10```````"XB```! +MSJ<]K8^G/:V7``````````````````````````````"!@<\@``!&3U)-```N +M&D%)1D934TY$```L'@`````````````````````````````````````````` +M````````````````````````````````````````____________________ +M________________________________________````````````_____P`` +M`````````````````````/_________________^_O[^_?KX^/T!`O_[\-[, +MP;R\OL"\L:>OQ=37S\6]M[2VN\''S=/8WN/GZ^_Q]/D`"A8B+SM$2U%776%E +M:6UOX./H[?7_"10>)RXS.#Q`1$A+3E%25%9865I; +M7%Q=75Q;6E9/.Q+Q[/+[!`P3&A\A(!T8$@G^Z\.:BX^6GZBOM[N]O+JWM;2U +MN+O`Q,C+SM'4U]G;W=_BY^SU_P@2&R$G+#`T.#P_0D1'24I,3D]045)34U-1 +M3TL_)@/NZ^WQ^/\&#A08&AD6$0G^ZLBEE)*4F)ZEK+*WNKN[N[N\O\+%R,O. +MT-+5U]G;W-W@X^CO]P`)$1D?(RWM[_+W +M_0,)#A$2$`L!\=K!K:6BH*"AHZBML[B]P<7(S-#2U=?9VMKFIJ>KL+6[P<;,T-38V]W>W]_@X>'BXN/DY>?J[_3[`PD0 +M%AH>(20G*2PN,#(T-C;FY^GK[_3Y_P8,$A<;'R$D +M)B@K+2\Q,C0V-SDZ.CDV,"<;#0'X]//S\_3U]OG]`@<+#`L'_O'BTL6]M[6S +ML;"PL+.XO<3,TMC>XN7GZ.CHZ.CHZ>GIZ>OL[_/X_0,)#Q49'!\A)"8H*BPN +M,#(T-3KM[_#P\/#P[^_O[_#P\O3X_`$&#!$6&AT@(B0F*"HL+C`R,S4U-3,P*B,: +M$0D"_?OZ^OO\_?[_`00'"@L*!@#W[>+8T,K&P\+!P+^_P<3*T-??Y>OO\?/S +M\_/S\O+R\O+R]/7X_``%"A`4&!P?(2,E)RHL+C`R,S0T-#(N*2(;$@L%`/W\ +M_/W_``$"`P4'"0L+"`/\\^G@U]',R,;%Q<3$Q("(E)RDK+2\Q,C,S,S$M*"(;$PT'`O_^_O\``0(# +M!`4'"0H*"07_]^[FW=;1S?L\?3V]_CX^/CW]_?W]_CY^_X!!0H/ +M$Q<;'R$C)B@J+"XO,3(Q,2\K)B(;%0\*!@,!``$"!`8'"`@)"@L+"@@$__CP +MZ>+;K\/3V^/GY^?GY^?GY^?GZ_/X!!0D-$A8:'B$C +M)2@J+"XO,3$Q,"XK)R(<%A`,"`0#`@($!0<)"@H+"PP,"PH'`OSU[N?AV]?4 +MTM'1TM/5U]G=X>;J[_/V^/GZ^OKZ^OKZ^OK[_/X!!`@,$!49'2`C)B@J+"XO +M,#$Q,"XK)B$<%Q$-"08$`P,$!@@*"PP,#`T-#`L(!/_Y\NSFX-O7U=/3U-77 +MV=O>X>7I[?'U]_GZ^OO[^_KZ^OO[_/X``P8*#Q,7&Q\B)2VMC7U]?9V][AX^;I[._R]/;X +M^?KZ^OKZ^OO[_/W^`00'"@X3%QL?(B4G*2LL+"TL*RDF(Q\:%A(."@<%!`0$ +M!@@*#`X/#P\.#0P*!P/_^O7PZN;BWMO9V=G;W=_BY>?J[.[P\O3V]_?X^?GY +M^?KZ^_S]``,%"0T1%1D=(20F*"DJ*RLJ*2_Q\O/T]?;W^/CX^?GZ^_S] +M_P(%"`P0%!@<(",F*"DI*BDH)R4B'QP8%1$."PD'!@4&!P@+#0\1$A(1$`X, +M"08#__KV\>WIY>+@WMW>W^'DY^KL[O#Q\O/T]/7V]_?X^/GY^OO]_P$$!PL/ +M$Q<;'R(D)B&Q@5$0\,"0<&!00$!08("@T/$!$1$1`.#`D&`__\^/3P[.GFY.+AX>+D +MYNGL[O#R\_/S\_/T]/3U]O?W^/K[_/X!!`8)#1$5&!P>(2,C(R,B(1\=&A<4 +M$0X+"0<%!`0$!`4'"0P.$!$2$1`/#0H(!`'^^O;S[^OIYN3CX^/DYNGL[O#R +M\_3T]/3T]/3U]O?W^/G[_/X``@4)#`\3%AD='R$B(B(A(!X<&1<4$0X+"0<% +M!`,#!`4&"`L-#Q`1$1$/#@L)!@,`_/CU\>WKZ.;EY.3DYNCK[?#R\_3T]/3T +M]/3U]?;W^/GZ_/W_`@0'"@X1%!<:'1\@(2$@'QT;&!83$`T*"`8$`P("`@,% +M!PD+#@\0$1`0#@P*!P0!_?KV\^_LZNCFY>3EYN?I[._Q\O/T]/3T]/3T]?7V +M]_CY^_S^``(%"`L/$A48&AP>'Q\>'1L9%Q01#PP)!P4#`@$!`0(#!0<)#`X/ +M$!`/#@P*!P0"_OOX]/'MZ^GGYN7EY>?IZ^WO\?/S]/3S\_/S]/3U]O?X^OO] +M_P$#!@D,#Q(5&!H<'1T='!H8%A,0#@L(!@0"`0`````"`P4("@P.#P\/#@P* +M"`8#`/WY]O+O[.KHY^;EY>;HZNSN\/+S]/3T\_/S]/3U]O?X^?O\_@`"!0@* +M#1`3%A@:&QL;&AD7%1(/#0H'!0,!`/___P`!`@0'"0L-#@\.#@T+"0<$`?[[ +M^/7Q[NSJZ.?FYN?HZNSN\/+S]/3T]/3T]/3U]O?X^?K\_@`"!`<*#`\2%!88 +M&AH:&A@7%1(0#0H(!0,!`/_^_O\``0,%"`H,#0X.#@T,"@@%`P#]^?;S\.WK +MZ>CGY^?HZ>OM[_'S]/3T]/3T]/3U]O?X^?K[_?\!`P4("PX0$Q46&!D9&1@6 +M%!(/#`D'!`(`__[]_?[_``($!@D+#`T.#@T,"@@&!`'^^_CU\N_MZ^GHZ.?H +MZ>KL[O#R\_3T]/3T]/3T]?;W^/G[_/X``@0&"0P.$1,4%A<7%Q85$Q$."P@& +M`P'__OW\_/S]_@`"!0<)"PP-#0P+"@D&!`+_^_GV\O#MZ^KIZ.CHZ.KK[>_Q +M\O/T]/3T\_3T]/7V]_CZ^_W_`0,%!PH,#A`2%!45%144$@\-"P@%`P'__?S[ +M^_O\_?\!`P4'"0L,#`P+"@D'!0,`_?KW]/'O[>OIZ.CHZ.GK[.[P\?/S]/3T +M]/3T]/7V]_CY^_S^``($!@@+#0\1$A,4%!03$0\-"@<%`@#^_?S[^_O[_/X` +M`@0&"`H+#`P+"@D(!@0!__SY]O/P[NSKZNGIZ>KK[.[O\?+S]/3T]/3T]?7V +M]_CY^_S^``($!@@*#`X0$1,3$Q,2$0\-"@@%`P#^_?S[^OK[_/W_`0,&"`D+ +M#`P,"PH)!P4"`/[[^/7R\.[LZ^KJZNKK[.WO\?+S]/3U]?7U]?;V]_CY^_S^ +M_P$#!0<)"PT/$!$2$A(2$`\-"@<%`@#^_?OZ^OKZ^_S^``(%!PD*"PP+"PH) +M"`8$`?_\^??T\>_M[.OJZNKK[.WN\/'S]/3U]?7U]?;V]_CY^OS]_P`"!`8( +M"@P-#Q`1$1$0#PX,"@<$`@#^_/OZ^?GY^OO]_P$#!0<)"@H+"PH)"`8$`@#] +M^O?U\_#N[>OJZNKKZ^SN[_#R\_3T]?7U]?7V]O?X^OO\_O\!`P4'"0H,#0X/ +M$!`/#@T+"08$`O_]^_KY^/CX^?K\_?\"!`8'"0H*"@H)"`8%`P#^_/GV]/'O +M[NSKZ^OKZ^SM[_#Q\O/T]/7U]?7V]O?X^?O\_?\!`@0&"`D+#`T.#P\.#0P+ +M"08$`O_]^_KY^/CX^?G[_?X!`P4'"`D*"@H)"`<%!`+__?OX]?/Q[^WL[.OK +M[.SM[_#Q\O/T]?7U]O;V]_CX^OO\_O\!`@0&!PD*#`T-#@X.#0P+"0<$`@#^ +M_/KY^/CX^?G[_/X``@0&"`D*"@H*"0@'!0,!__SZ^/7S\>_N[>WM[>WN[_#Q +M\O/T]?;V]O?W^/CY^OO\_O\!`@0%!P@*"PP-#0T-#0P*"0<$`@#^_/OY^/CX +M^/GZ_/W_`0,%!P@)"@H*"0@'!@0"`/[[^??T\O'O[N[M[>WN[_#Q\O/T]?;V +M]_?W^/CY^OO\_O\``@,%!@@)"@L,#`T,#`L*"`8$`@#^_/KY^/CX^/GZ^_S^ +M``($!@<("0D)"0@'!@4#`?_]^OCV]/+P[^[N[>[N[^_P\?/T]?7V]_?W^/CY +M^OO\_?X``0,$!0<("0H+"PL+"PH)!P8$`@#^_/KY^/?W]_CY^OO]_P$#!08' +M"`D)"0@'!@4#`@#]^_GW]?/Q\._N[N[N[^_P\?+S]/7V]O?W^/CY^OO\_?[_ +M`0(#!08'"`D*"@L+"@D(!P4$`@#^_/KY^/?W]_CX^OO]_@`"!`4'"`@)"0@( +M!P8$`P'__?OX]O3S\?#O[^_O[_#P\?+S]/7V]_?X^/GY^OO\_?[_`0($!08' +M"`D*"@H*"@D(!P4$`@#^_/OY^/CW]_CY^OO\_@`"!`4'"`@)"0D("`<%!`(` +M_OSZ^/;U\_+Q\/#P\/#Q\O/T]?;V]_CY^?KZ^_S\_?\``0($!08'"`D*"@H* +M"@D(!P8$`@'__?OZ^?CX^/CY^OO\_@`!`P4&!P@)"0D)"`<&!0,"`/[\^OCV +M]//R\?'Q\?'Q\O/T]?;V]_CY^?K[^_S]_O\``0($!08'"`D)"@H*"0D(!P4$ +M`@'__?SZ^?CX^/CX^?K\_?\!`@0%!P@("0D)"`<'!@0"`?_]^_GW]O3S\O'Q +M\?'Q\O/S]/7V]_CY^?KZ^_S\_?[_`0(#!`4&!P@("0D)"`@'!@4#`@#^_?OZ +M^?CW]_?X^?K[_/X``0,$!@<'"`@("`<'!@0#`0#^_/KX]O7T\_+Q\?'R\O/S +M]/7V]_CX^?KZ^_S\_?[_``$#!`4&!P<("`@("`<&!@0#`@#^_?OZ^?CX]_?X +M^/GZ_/W_`0($!08'"`@("`@'!@4$`@'__?OY^/;U]//R\O+R\O/S]/7V]_CX +M^?K[^_S]_O[_``$#!`4&!@<("`@("`<&!00#`@#__?S[^OGX^/CX^?GZ_/W_ +M``(#!08'!P@("`@(!P8%!`(`__W[^OCW]?3T\_/S\_3T]?;V]_CY^OO\_/W^ +M_O\``0(#!`4&!P<("`@("`<'!@4#`@'__OS[^OGY^/CX^?K[_/W_``(#!08' +M!P@("`@("`<&!0,"`/[]^_KX]_;U]/3T]/3U]?;W^/CY^OO\_?W^__\``0(# +M!`4&!P<("`@("`<'!@4#`@'__OW[^_KY^/CY^?KZ_/W^``$#!`4&!P@("`@( +M"`<&!00#`?_^_/OY^/?V]?7T]/7U]?;W]_CY^OO\_/W^__\``0(#!`4%!@<' +M"`@(!P<&!00#`@#__OS[^OGY^/CX^?GZ^_S]_P`"`P0%!@<'"`@(!P<&!00# +M`@#^_?OZ^/?V]O7U]?7U]?;V]_CY^?K[_/W^_O\``0$"`P0%!@8'!P<'!P8& +M!00#`0#__?S[^OGY^/CX^/GZ^_S]_@`!`@0%!08'!P<'!P<&!@4$`@'__OS[ +M^OCW]_;U]?7U]O;W]_CY^OK[_/W^__\``0("`P0%!@8'!P<'!P8&!00#`0#_ +M_OW[^_KY^?CX^?GZ^_S]_O\!`@,$!08'!P<'!P<'!@4$`P(!__[\^_KY^/?W +M]O;V]O?W^/CY^OO\_?[^_P`!`0(#!`0%!@8'!P<'!P<&!00#`@$`_OW\^_KZ +M^?GY^?KZ^_S]_O\!`@,$!08'!P@("`@'!P8%!`,"`?_^_/OZ^?CX]_?W]_?X +M^/GZ^OO\_?[__P`!`@(#!`4%!@<'!P<'!P<&!00#`@$`__[\_/OZ^OGY^?KZ +M^_S]_O\``@,$!08&!P<("`@(!P<&!00#`0#__?S[^OGX^/CX^/CX^/GZ^OO\ +M_?[__P`!`@(#!`0%!@8'!P<'!P8&!00#`@$`__W\^_OZ^OGY^?KZ^_O\_?X` +M`0(#!`4%!@<'!P<'!P8&!00#`@#__OW[^OKY^/CX^/CX^/GY^OO[_/W^_P`! +M`0(#`P0%!08&!@<&!@8%!00#`@#__OW\^_OZ^?GY^?GZ^OO\_?[_``$"`P0% +M!08&!P<'!P8&!00#`@$`__W\^_KY^?CX^/CX^/GY^OO[_/W^_P`!`0(#`P0% +M!08&!@8&!@8%!00#`@$`_OW\_/OZ^OGY^?GZ^OO\_?[_``$"`P0$!08&!P<' +M!P<&!@4$`P(!`/[]_/OZ^OGY^?GY^?GZ^OO\_/W^_P`!`@(#!`0%!@8&!P<' +M!P8&!00#`@$`__[]_/O[^OKZ^OKZ^_O\_?[_``$"`P0$!08&!P<'!P<'!@8% +M!`,"`0#__OS\^_KZ^OGY^OKZ^_O\_?[__P`!`@,#!`4%!@8'!P<'!P8&!00$ +M`P(`__[^_?S[^_OZ^OO[^_S\_?[_``$"`P,$!08&!@<'!P<'!@8%!00#`@#_ +M_OW\_/O[^OKZ^OK[^_O\_?[^_P`!`@,#!`4%!@8&!P<'!P8&!00#`P(`___^ +M_?S[^_OZ^OK[^_O\_?[^_P`!`@,$!`4%!@8&!P<&!@8%!`0#`@$`__[]_/O[ +M^OKZ^OKZ^_O\_/W^_P```0(#`P0%!08&!@8&!@8%!00#`@$`__[]_/S[^_KZ +M^OKZ^_O\_/W^__\``0(#`P0%!04&!@8&!@8%!00#`@$`__[]_/S[^_KZ^OKZ +M^_O[_/W^_O\``0("`P0$!04&!@8&!@8%!00#`@$`__[^_?S[^_OZ^OKZ^_O\ +M_/W^_O\``0("`P0$!04&!@8&!@8%!00#`P(!`/_^_?S\^_O[^_O[^_O\_/W^ +M__\``0(#`P0$!04&!@8&!@8%!00#`P(!`/_^_?W\_/O[^_O[^_S\_?W^__\` +M`0("`P0$!04&!@8&!@8&!04$`P,"`0#__OW]_/S\^_O\_/S\_?W^_P```0(# +M!`0%!08&!@8&!@8&!04$`P(!`/___OW\_/S[^_O[_/S\_?W^__\``0("`P0$ +M!04&!@8&!@8&!@4%!`,"`0$`__[^_?S\_/S\_/S]_?[^_P```0(#`P0%!08& +M!@8&!@8&!04$`P(!``#__OW]_/S\^_O\_/S\_?W^__\``0$"`@,$!`4%!08& +M!@8&!@4%!`,#`@$`__[^_?W\_/S\_/S\_?W^_O\``0$"`P,$!04%!@8&!@4% +M!00#`P(!`/_^_OW\_/S[^_O[^_S\_/W]_O__```!`@(#`P0$!04%!04%!04$ +M!`,"`@$`___^_?W\_/S\_/S\_/W]_O__``$"`@,$!`4%!04%!04%!`0#`P(! +M`/_^_OW\_/S[^_O[^_S\_/W]_O[_```!`0("`P,$!`4%!04%!04%!`,#`@$! +M`/_^_OW]_/S\_/S\_?W^_O__``$!`@,#!`0%!04%!04%!`0#`P(!``#__OW] +M_/S\_/S\_/S\_?W^_O__```!`0(#`P0$!`4%!04&!04%!00$`P(!`0#___[^ +M_?W]_?W]_?[^__\```$"`@,$!`4%!08&!@4%!00$`P("`0#___[]_?W\_/S\ +M_/S]_?W^_O__```!`0("`P,$!`4%!04&!@4%!00$`P,"`0$`___^_O[]_?W] +M_O[^__\```$"`@,$!`4%!08&!@4%!00$`P,"`0``__[^_?W]_/S\_/W]_?W^ +M_O__```!`0("`P,#!`0%!04%!04%!04$!`,"`@$``/_^_O[]_?W]_?[^_O__ +M``$!`@,#!`0$!04%!04%!`0#`P(!`0#___[]_?S\_/S\_/S\_?W]_O[__P`` +M``$!`@(#`P,$!`0$!04%!`0$`P,"`0$``/_^_O[]_?W]_?W^_O[__P`!`0(" +M`P0$!`0%!00$!`0#`P(!`0#__O[]_?S\_/S\_/S\_/W]_?[^__\```$!`0(" +M`P,#!`0$!`0$!`0$`P,"`@$!`/___O[^_?W]_?W^_O[__P```0("`P,$!`0$ +M!00$!`0#`P(!`0#___[^_?W\_/S\_/S\_?W]_O[^__\````!`0("`P,#!`0$ +M!`4%!`0$!`,#`@(!`0``___^_O[^_O[^_O__```!`0(#`P0$!`4%!04%!`0$ +M`P,"`0$`___^_OW]_?W]_?W]_?W^_O[___\```$!`@("`P,#!`0$!04%!04$ +M!`0#`P("`0$``/____[^_O[_____```!`0(#`P0$!`4%!04%!`0$`P,"`0$` +M`/_^_O[]_?W]_?W]_?W^_O[___\```$!`0("`@,#`P0$!`0%!04$!`0#`P," +M`0$```#______O[^____```!`0("`P,$!`0$!`0$!`0#`P("`0$`___^_OW] +M_?W]_/W]_?W]_?[^_O___P```0$!`@("`P,#!`0$!`0$!`,#`P("`0$``/__ +M__[^_O[^_O[__P````$!`@(#`P0$!`0$!`,#`P(!`0``__[^_OW]_?S\_/S\ +M_?W]_?W^_O[__P`````!`0("`@,#`P,#!`0$`P,#`P("`0$``/_____^_O[^ +M_O___P````$!`@(#`P,$!`0$`P,#`@(!`0``___^_OW]_?W\_/S]_?W]_?[^ +M_O[___\````!`0$"`@(#`P,#!`0$!`,#`P("`@$!````_____________P`` +M`0$"`@(#`P0$!`0$!`0#`P("`0$``/___O[^_?W]_?W]_?W]_O[^_____P`` +M``$!`0("`@,#`P,$!`0$!`0$`P,#`@(!`0$```#_________`````0$"`@,# +M`P0$!`0$!`0#`P,"`@$!``#___[^_OW]_?W]_?[^_O[^_____P````$!`0$" +M`@(#`P,#!`0$!`0$`P,#`@("`0$`````________`````0$!`@(#`P,$!`0$ +M!`0#`P,"`@$!``#___[^_OW]_?W]_?W^_O[^_O[___\``````0$!`0("`@(# +M`P,#`P,#`P,"`@(!`0$```#___________\````!`0("`@,#`P,#`P,#`@(" +M`0$``/___O[^_?W]_?W]_?W]_?W^_O[^_____P`````!`0$!`@("`@,#`P,# +M`P("`@(!`0```/______________`````0$"`@(#`P,#`P,#`@("`0$``/__ +M_O[^_?W]_?W]_?W]_?W^_O[^______\``````0$!`0("`@(#`P,#`P,"`@(! +M`0$`````__________\````!`0$"`@(#`P,#`P,#`P("`0$!``#____^_O[^ +M_?W]_?[^_O[^_O[_____```````!`0$!`@("`@,#`P,#`P,#`P("`@$!`0`` +M````_P````````$!`0("`@,#`P,#`P,#`P,"`@$!````_____O[^_O[^_O[^ +M_O[^_O______```````!`0$!`@("`@,#`P,#`P,#`P("`@(!`0$````````` +M```````!`0("`@(#`P,#`P,#`P("`@$!````_____O[^_O[^_O[^_O[^_O[_ +M_____P````````$!`0("`@("`@,#`P,"`@("`@$!`0````#_______\````` +M`0$!`0("`@(#`@("`@("`0$```#____^_O[^_?W]_?W]_?[^_O[^_O[_____ +M_P```````0$!`0$"`@("`@("`@("`0$!`````/___________P````$!`0$" +M`@("`@("`@(!`0$```#___[^_O[^_?W]_?W]_O[^_O[^_O[_______\````` +M``$!`0$"`@("`@("`@("`0$!`0````#__________P`````!`0$"`@("`@(" +M`@("`0$!``#______O[^_O[^_O[^_O[^_O[_________```````!`0$!`0(" +M`@("`@("`@("`@(!`0$!`````````````````0$!`0("`@("`@,"`@("`@$! +M`0```/_____^_O[^_O[^_O[^_________P`````````!`0$!`0("`@("`@(" +M`@("`@("`0$!`0$``````````````0$!`0("`@("`@("`@("`@$!`0```/__ +M___^_O[^_O[^_O[^_O________\``````````0$!`0$"`@("`@("`@("`@(! +M`0$!`0`````````````````!`0$!`0("`@("`@(!`0$!``#______O[^_O[^ +M_O[^_O[^_O[^_O___________P````````$!`0$!`0("`@("`0$!`0$````` +M`/________\```````$!`0$!`0$!`0$!`0$```#____^_O[^_O[^_O[^_O[^ +M_O[^_O[^__________\```````$!`0$!`0$!`@(!`0$!`0$```````#_____ +M__\```````$!`0$!`0$!`0$!`0$`````_____O[^_O[^_O[^_O[^_O[^_O[_ +M_________P````````$!`0$!`@("`@("`@("`0$!`0$````````````````! +M`0$!`0$"`@("`@(!`0$!`````/_______O[^_O[^_O[^____________```` +M`````````0$!`0$"`@("`@("`@("`@(!`0$!`0````````````$!`0$!`0(" +M`@("`@("`0$!`0````#________^_O[^_O[______________P`````````` +M``$!`0$!`@("`@("`@("`@$!`0$!`0```````````````0$!`0$!`0$!`0$! +M`0$!`````/_____^_O[^_O[^_O[^_O[^_O[^____________``````````$! +M`0$!`0$!`0$!`0$!`0$```````````````````````$!`0$!`0$!``````#_ +M______[^_O[^_O[^_O[^_O[^_O[^_O[___________\``````````0$!`0$! +M`0$!`0$!`0````````````````````````$!`0$!`0$!`0````#_______[^ +M_O[^_O[^_O[^_O[^_O[^_O____________\````````!`0$!`0$!`0$!`0$! +M`0$!`0````````````````$!`0$!`0$!`0$!`0$!``````#________^__[^ +M_O[^_________________P````````````$!`0$!`0$"`@("`@(!`0$!`0$! +M`0`````````!`0$!`0$!`0$!`0$!`0$!`0``````____________________ +M_____________P````````````$!`0$!`0$!`@("`0(!`0$!`0$!`0`````` +M`````0$!`0$!`0$!`0$!`0$!``````#__________O[^_O[^_O[^________ +M_____________P`````````!`0$!`0$!`0$!`0$!`0`````````````````` +M`````0$!`0$`````````_______^_O[^_O[^_O[^_O[^_O[^_O[^_O______ +M_________P`````````!`0$!`0$!`0$````````````````````````````` +M`0``````````_________O[^_O[^_O[^_O[^_O[^_O[^________________ +M_P`````````!`0$!`0$!`0$!`0$````````````````````!`0$!`0$!`0$` +M````````________________________________________```````````` +M`0$!`0$!`0$!`0$!`0$!`0$!`0```````0$!`0$!`0$!`0$!`0$!`0`````` +M`/___________________________________P```````````````0$!`0$! +M`0$!`0$!`0$!`0$!`0```````0$!`0$!`0$!`0$!`0$!`````````/______ +M__________________________________\```````````````$!`0$!`0$! +M`0```````````````````````````````````````/____________[^_O[^ +M_O[^_O[^_O[^_O___________________P`````````````````````````` +M````````````````````````````````______________[^_O[^_O[^_O[^ +M_O[______________________P`````````````````````````````````` +M``````````````````````````#_________________________________ +M_____________P`````````````!`0$!`0$!`0$!`0$!``````````````$! +M`0$!`0$!`0$!````````````__________________________________\` +M`````````````````0$!`0$!`0$!`0$!`0$!`0$!`````````0$!`0$!`0$! +M`0$`````````````______________________________________\````` +M``````````````$!`0$!`0$!```````````````````````````````````` +M`/_______________________________________________________P`` +M``````````````````````````````````````````````````#_________ +M______________[___________________________________\````````` +M````````````````````````````````````````````________________ +M____________________________________`````````````````0`!`0$! +M`0``````````````````````````````````````````________________ +M________________``````````````````````````$!`0$!`0$!`0$!`0$! +M`0$``````````````````````````````````/______________________ +M__________\```````````````````````````$!`0$!`0$````````````` +M``````````````````````#_____________________________________ +M__________________\````````````````````````````````````````` +M`````````/__________________________________________________ +M______________\````````````````````````````````````````````` +M`/__________________________________________________________ +M_P`````````````````````````````````````````````````````````` +M`````/________________________________\````````````````````` +M```````!`0$!`0$!`0$!`0$!`0`````````````````````````````````` +M``#______________________P`````````````````````````````````` +M``$!`0$!`0$!`0$```````````````````````````````#_____________ +M________________________________________```````````````````` +M``````````````````````````````#_____________________________ +M______________________________________\````````````````````` +M`````````````````````/______________________________________ +M____________________________```````````````````````````````` +M``````````````````````````#_________________________________ +M````````````````0T]-30```!(``0``+!8`"$`-K$0```````!-05)+```` +M`@``24Y35````!0\``!_`'\``````````````````$%04$P```&H4V0R80`" +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````/@```````````````````````````` +M````````````````````````````````````````````````````!`@````` +M``D`(P````````````````!X````!``$``````"[_`#___^``````0&H!E%5 +M;FET````'@```!6`&X`9P!:`$L`.P`H +M``W_Y_^W_W7_)/Z^_D+]M/TI_,8$2&%R<($"````04E&1E-D,F$````````` +M````$J<`````04E&1E-D,F$```````````````````````````````"G/:V/ +M```N(@```= (int)SIZE(baud_rates)) + ospeed = (short)(SIZE(baud_rates) - 1); + else if (ospeed < 0) + ospeed = 0; +#endif /* !NO_DELAY_PADDING */ + + return 1; +} + +/* search for an entry in the termcap file */ +static char * +tc_find(fp, term, buffer, bufsiz) + FILE *fp; + const char *term; + char *buffer; + int bufsiz; +{ + int in, len, first, skip; + char *ip, *op, *tc_fetch, tcbuf[TCBUFSIZ]; + + buffer[0] = '\0'; + do { + ip = tcbuf, in = min(bufsiz,TCBUFSIZ); + first = 1, skip = 0; + /* load entire next entry, including any continuations */ + do { + if (!fgets(ip, min(in,BUFSIZ), fp)) break; + if (first) skip = (*ip == '#'), first = 0; + len = (int)strlen(ip); + if (!skip && len > 1 + && *(ip + len - 1) == '\n' && *(ip + len - 2) == '\\') + len -= 2; + ip += len, in -= len; + } while (*(ip - 1) != '\n' && in > 0); + if (ferror(fp) || ip == buffer || *(ip - 1) != '\n') + return (char *)0; + *--ip = '\0'; /* strip newline */ + if (!skip) ip = tc_name(term, tcbuf); + } while (skip || !ip); + + /* we have the desired entry; strip cruft and look for :tc=other: */ + tc_fetch = 0; + for (op = buffer; *ip; ip++) { + if (op == buffer || *(op - 1) != ':' + || (*ip != ' ' && *ip != '\t' && *ip != ':')) + *op++ = *ip, bufsiz -= 1; + if (ip[0] == ':' && ip[1] == 't' && ip[2] == 'c' && ip[3] == '=') { + tc_fetch = &ip[4]; + if ((ip = index(tc_fetch, ':')) != 0) *ip = '\0'; + break; + } + } + *op = '\0'; + + if (tc_fetch) { + rewind(fp); + tc_fetch = tc_find(fp, tc_fetch, tcbuf, min(bufsiz,TCBUFSIZ)); + if (!tc_fetch) + return (char *)0; + if (op > buffer && *(op - 1) == ':' && *tc_fetch == ':') + ++tc_fetch; + strcpy(op, tc_fetch); + } + return buffer; +} + +/* check whether `ent' contains `nam'; return start of field entries */ +static char * +tc_name(nam, ent) + const char *nam; + char *ent; +{ + char *nxt, *lst, *p = ent; + size_t n = strlen(nam); + + if ((lst = index(p, ':')) == 0) lst = p + strlen(p); + + while (p < lst) { + if ((nxt = index(p, '|')) == 0 || nxt > lst) nxt = lst; + if ((long)(nxt - p) == (long)n && strncmp(p, nam, n) == 0) + return lst; + p = nxt + 1; + } + return (char *)0; +} + +/* look up a numeric entry */ +int +tgetnum(which) + const char *which; +{ + const char *q, *p = tc_field(which, &q); + char numbuf[32]; + size_t n; + + if (!p || p[2] != '#') + return -1; + p += 3; + if ((n = (size_t)(q - p)) >= sizeof numbuf) + return -1; + (void) strncpy(numbuf, p, n); + numbuf[n] = '\0'; + return atoi(numbuf); +} + +/* look up a boolean entry */ +int +tgetflag(which) + const char *which; +{ + const char *p = tc_field(which, (const char **)0); + + return (!p || p[2] != ':') ? 0 : 1; +} + +/* look up a string entry; update `*outptr' */ +char * +tgetstr(which, outptr) + const char *which; + char **outptr; +{ + int n; + char c, *r, *result; + const char *q, *p = tc_field(which, &q); + + if (!p || p[2] != '=') + return (char *)0; + p += 3; + if ((q = index(p, ':')) == 0) q = p + strlen(p); + r = result = *outptr; + while (p < q) { + switch ((*r = *p++)) { + case '\\': + switch ((c = *p++)) { + case 'E': *r = ESC; break; + case 'a': *r = BEL; break; + case 'b': *r = '\b'; break; + case 'f': *r = '\f'; break; + case 'n': *r = '\n'; break; + case 'r': *r = '\r'; break; + case 't': *r = '\t'; break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = c - '0'; + if (*p >= '0' && *p <= '7') n = 8 * n + (*p++ - '0'); + if (*p >= '0' && *p <= '7') n = 8 * n + (*p++ - '0'); + *r = (char)n; + break; + /* case '^': case '\\': */ + default: *r = c; break; + } + break; + case '^': + *r = (*p++ & 037); + if (!*r) *r = (char)'\200'; + break; + default: + break; + } + ++r; + } + *r++ = '\0'; + *outptr = r; + return result; +} + +/* look for a particular field name */ +static const char * +tc_field(field, tc_end) + const char *field; + const char **tc_end; +{ + const char *end, *q, *p = tc_entry; + + end = p + strlen(p); + while (p < end) { + if ((p = index(p, ':')) == 0) + break; + ++p; + if (p[0] == field[0] && p[1] == field[1] + && (p[2] == ':' || p[2] == '=' || p[2] == '#' || p[2] == '@')) + break; + } + if (tc_end) { + if (p) { + if ((q = index(p + 2, ':')) == 0) q = end; + } else + q = 0; + *tc_end = q; + } + return p; +} + +static char cmbuf[64]; + +/* produce a string which will position the cursor at if output */ +char * +tgoto(cm, col, row) + const char *cm; + int col, row; +{ + return tparam(cm, cmbuf, (int)(sizeof cmbuf), row, col, 0, 0); +} + +/* format a parameterized string, ala sprintf */ +char * +tparam(ctl, buf, buflen, row, col, row2, col2) + const char *ctl; /* parameter control string */ + char *buf; /* output buffer */ + int buflen; /* ought to have been `size_t'... */ + int row, col, row2, col2; +{ + int atmp, ac, av[5]; + char c, *r, *z, *bufend, numbuf[32]; + const char *fmt; +#ifndef NO_SPECIAL_CHARS_FIXUP + int bc = 0, up = 0; +#endif + + av[0] = row, av[1] = col, av[2] = row2, av[3] = col2, av[4] = 0; + ac = 0; + r = buf, bufend = r + buflen - 1; + while (*ctl) { + if ((*r = *ctl++) == '%') { + if (ac > 4) ac = 4; + fmt = 0; + switch ((c = *ctl++)) { + case '%': break; /* '%' already copied */ + case 'd': fmt = "%d"; break; + case '2': fmt = "%02d"; break; + case '3': fmt = "%03d"; break; + case '+': /*FALLTHRU*/ + case '.': *r = (char)av[ac++]; + if (c == '+') *r += *ctl++; + if (!*r) { + *r = (char)'\200'; + } else { +#ifndef NO_SPECIAL_CHARS_FIXUP + /* avoid terminal driver intervention for + various control characters, to prevent + LF from becoming CR+LF, for instance; only + makes sense if this is a cursor positioning + sequence, but we have no way to check that */ + while (index("\004\t\n\013\f\r", *r)) { + if (ac & 1) { /* row */ + if (!UP || !*UP) break; /* can't fix */ + ++up; /* incr row now, later move up */ + } else { /* column */ + if (!BC || !*BC) break; /* can't fix */ + ++bc; /* incr column, later backspace */ + } + (*r)++; + } +#endif /* !NO_SPECIAL_CHARS_FIXUP */ + } break; + case '>': if (av[ac] > (*ctl++ & 0377)) + av[ac] += *ctl; + ++ctl; break; + case 'r': atmp = av[0]; av[0] = av[1]; av[1] = atmp; + atmp = av[2]; av[2] = av[3]; av[3] = atmp; + --r; break; + case 'i': ++av[0]; ++av[1]; ++av[2]; ++av[3]; + --r; break; + case 'n': av[0] ^= 0140; av[1] ^= 0140; + av[2] ^= 0140; av[3] ^= 0140; + --r; break; + case 'B': av[0] = ((av[0] / 10) << 4) + (av[0] % 10); + av[1] = ((av[1] / 10) << 4) + (av[1] % 10); + av[2] = ((av[2] / 10) << 4) + (av[2] % 10); + av[3] = ((av[3] / 10) << 4) + (av[3] % 10); + --r; break; + case 'D': av[0] -= (av[0] & 15) << 1; + av[1] -= (av[1] & 15) << 1; + av[2] -= (av[2] & 15) << 1; + av[3] -= (av[3] & 15) << 1; + --r; break; + default: *++r = c; break; /* erroneous entry... */ + } + if (fmt) { + (void) sprintf(numbuf, fmt, av[ac++]); + for (z = numbuf; *z && r <= bufend; z++) + *r++ = *z; + --r; /* will be re-incremented below */ + } + } + if (++r > bufend) + return (char *)0; + } +#ifndef NO_SPECIAL_CHARS_FIXUP + if (bc || up) { + while (--bc >= 0) + for (z = BC; *z && r <= bufend; z++) + *r++ = *z; + while (--up >= 0) + for (z = UP; *z && r <= bufend; z++) + *r++ = *z; + if (r > bufend) + return (char *)0; + } +#endif /* !NO_SPECIAL_CHARS_FIXUP */ + *r = '\0'; + return buf; +} + +/* send a string to the terminal, possibly padded with trailing NULs */ +void +tputs( string, range, output_func ) +const char *string; /* characters to output */ +int range; /* number of lines affected, used for `*' delays */ +int (*output_func)(); /* actual output routine; return value ignored */ +{ + register int c, num = 0; + register const char *p = string; + + if (!p || !*p) + return; + + /* pick out padding prefix, if any */ + if (*p >= '0' && *p <= '9') { + do { /* note: scale `num' by 10 to accommodate fraction */ + num += (*p++ - '0'), num *= 10; + } while (*p >= '0' && *p <= '9'); + if (*p == '.') + ++p, num += (*p >= '0' && *p <= '9') ? (*p++ - '0') : 0; + if (*p == '*') + ++p, num *= range; + } + + /* output the string */ + while ((c = *p++) != '\0') { + if (c == '\200') c = '\0'; /* undo tgetstr's encoding */ + (void) (*output_func)(c); + } + +#ifndef NO_DELAY_PADDING + /* perform padding */ + if (num) { + long pad; + + /* figure out how many chars needed to produce desired elapsed time */ + pad = (long)baud_rates[ospeed]; + if (pad < 0) pad *= -100L; + pad *= (long)num; + /* 100000 == 10 bits/char * (1000 millisec/sec scaled by 10) */ + num = (int)(pad / 100000L); /* number of characters */ + + c = PC; /* assume output_func isn't allowed to change PC */ + while (--num >= 0) + (void) (*output_func)(c); + } +#endif /* !NO_DELAY_PADDING */ + + return; +} + +/*tclib.c*/ diff --git a/sys/share/termcap b/sys/share/termcap new file mode 100644 index 0000000..6a9f37e --- /dev/null +++ b/sys/share/termcap @@ -0,0 +1,173 @@ +# +# MS/PC-DOS ANSI.SYS termcap +# +ansi|color|ansi-color|ibm|ibmpc|ANSI.SYS color:\ + :co#80:li#24:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[H\E[2J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :ti=\E[0;44m:te=\E[0m:\ + :so=\E[1;35;44m:se=\E[0;44m:\ + :us=\E[1;31;44m:ue=\E[0;44m:\ + :mb=\E[5m:md=\E[1m:me=\E[0;44m: +mono|ansi-mono|ANSI.SYS:\ + :co#80:li#24:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[H\E[2J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :so=\E[1m:se=\E[m:us=\E[4m:ue=\E[m:\ + :mb=\E[5m:md=\E[1m:me=\E[m: +# +# This is a termcap for NNANSI.SYS (New & Improved NANSI.SYS), +# a faster and more complete public domain replacement for +# ANSI.SYS, and two other ANSI.SYS replacements, NANSI.SYS and +# ZANSI.SYS. +# +# NANSI and ZANSI support line insert (al) and delete (dl) +# and character insert (ic) and delete (dc) where ANSI.SYS +# does not. NNANSI.SYS also supports clear to end of display +# (cd), does reverse video (mr) properly, and emulates SGR +# more fully, allowing correct end sequences for standout (se) +# and end of underline (ue). +# +nnansi-mono|NNANSI.SYS:\ + :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[2J:cd=\E[J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :so=\E[1m:se=\E[2m:\ + :us=\E[4m:ue=\E[24m:\ + :mb=\E[5m:md=\E[1m:mh=\E[2m:mr=\E[7m:me=\E[m:\ + :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: +nnansi|NNANSI.SYS color:\ + :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[2J:cd=\E[J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :ti=\E[0;44m:te=\E[0m:\ + :so=\E[1;35;44m:se=\E[2;37m:\ + :us=\E[4m:ue=\E[24m:\ + :mb=\E[5m:md=\E[1m:mh=\E[2m:mr=\E[7m:me=\E[0;44m:\ + :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: +nansi-mono|zansi-mono|N/ZANSI.SYS:\ + :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[2J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :ti=\E[0m:te=\E[0m:\ + :so=\E[1;35m:se=\E[0m:\ + :us=\E[1;31m:ue=\E[0m:\ + :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[m:\ + :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: +nansi|zansi|N/ZANSI.SYS color:\ + :co#80:li#25:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\E[2J:ce=\E[K:\ + :ho=\E[H:cm=\E[%i%d;%dH:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :ti=\E[0;44m:te=\E[0m:\ + :so=\E[1;35;44m:se=\E[0;44m:\ + :us=\E[1;31;44m:ue=\E[0;44m:\ + :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[0;44m:\ + :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P: +# +# For ST NetHack: +# for VT100/200/&c in VT52 mode, add :ti=\E[?2l: +vt52|atari|DEC VT52:\ + :co#80:li#24:bs:pt:bl=^G:le=^H:do=^J:\ + :cl=\EH\EJ:ce=\EK:cd=\EJ:\ + :ho=\EH:cm=\EY%+ %+ :\ + :up=\EA:do=\EB:le=\EC:ri=\ED:nd=\EC:\ + :ku=\EA:kd=\EB:kl=\EC:kr=\ED:kb=^H:\ + :sr=\EI:as=\EF:ae=\EG: +# +# For Amiga or VMS NetHack: +# VT100 or clone without the advanced video option installed +vt100|amiga|vt100-80|vt100-noavo|DEC VT100:\ + :co#80:li#24:bs:pt:am:mi:bl=^G:le=^H:do=^J:xo:vt#3:\ + :cl=50\E[H\E[J:ce=3\E[K:cd=50\E[J:\ + :ho=\E[H:cm=5\E[%i%d;%dH:cs=\E[%i%d;%dr:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :UP=\E[%dA:DO=\E[%dB:LE=\E[%dC:RI=\E[%dD:\ + :so=2\E[7m:se=2\E[m:us=2\E[4m:ue=2\E[m:\ + :mb=2\E[5m:md=2\E[1m:mr=2\E[7m:me=2\E[m:\ + :ti=4\E<\E(B\E)0:as=^N:ae=^O:\ + :ks=\E[?1h\E=:ke=\E[?1l\E>:ku=\E[A:kd=\E[B:kl=\E[C:kr=\E[D:kb=^H:\ + :kn#4:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:\ + :sc=\E7:ec=\E8:sr=5\EM: +# +# VT102 and up: +# includes VT100 with advanced video option +vt102|vt102-80|vt100-avo|DEC VT102:\ + :im=\E[4h:ei=\E[4l:al=5\E[L:dl=5\E[M:dc=5\E[P:\ + :AL=9\E[%dL:DL=9\E[%dM:tc=vt100: +vt200|vt200-80|vt220|vt240|vt241|VT200_Series:\ + :ic=5\E[@:tc=vt102: +vt300|vt300-80|vt320|vt330|vt340|VT300_Series:\ + :tc=vt200: +vt400|vt400-80|vt420|VT400_Series:\ + :tc=vt300: +# VAXstations (should have full entries with no delays and 8-bit CSI's) +VWS|UIS:tc=vt200: +DECterm:tc=vt300: +# +# Wide screen (magnifying glass not included;-) +# note: vt100 w/o AVO only supports 14 lines when in 132-column mode +vt132|vt100-132:vt102-132:\ + :co#132:ti=9\E<\E(B\E)0\E[?3h:tc=vt102: +vt200-132|vt300-132:\ + :co#132:ti=9\E<\E(B\E)0\E[?3h:tc=vt200: +# +# +# For really complete ANSI emulations (FANSI-CONSOLE?): +# +AX|ANSI X3.64|full ANSI X3.64 (1977) standard:\ + :co#80:li#24:bs:pt:am:mi:bl=^G:le=^H:\ + :cl=\E[2J:ce=\E[K:cd=\E[J:\ + :ho=\E[H:cm=\E[%i%d;%dH:cs=\E[%i%d;%dr:\ + :up=\E[A:do=\E[B:le=\E[C:ri=\E[D:nd=\E[C:\ + :UP=\E[%dA:DO=\E[%dB:LE=\E[%dC:RI=\E[%dD:\ + :so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\ + :mb=\E[5m:md=\E[1m:mr=\E[7m:me=\E[m:as=^N:ae=^O:\ + :ku=\E[A:kd=\E[B:kl=\E[C:kr=\E[D:kb=^H:\ + :kn#4:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:\ + :im=\E[4h:ei=\E[4l:al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P:sf=\ED:sr=\EM: +# +# For PC-9800 NetHack: +# +pc9800|pc9801|pc98|NEC PC-9800 Series:\ + :co#80:li#25:\ + :cm=\E[%i%d;%dH:ho=\E[H:ll=\E[25;1H:cr=^M:le=^H:nd=^L:\ + :up=\EM:do=\ED:bw:nw=\EE:sc=\E[s:rc=\E[u:ta=^I:bc=^H:bs:nl=\ED:\ + :am:xn:\ + :sf=\ED:sr=\EM:\ + :cl=\E*:cd=\E[J:ce=\E[K:\ + :al=\E[L:dl=\E[M:AL=\E[%dL:DL=\E[%dM:\ + :so=\E[36m:se=\E[m:\ + :mb=\E[5m:md=\E[33m:mh=\E[32m:mk=\E[8m:me=\E[m:\ + :as=\E)3:ae=\E)0:\ + :us=\E[4m:ue=\E[m:\ + :vi=\E[>5h:ve=\E[>5l:\ + :bl=^G:\ + :kl=\E[D:kr=\E[C:ku=\E[A:kd=\E[B:\ + :k0=\EZ:k1=\ES:k2=\ET:k3=\EU:k4=\EV:\ + :k5=\EW:k6=\EE:k7=\EJ:k8=\EP:k9=\EQ:\ + :kb=^H:\ + :ti=\E[0;37m\E[>1h\E[>5l:te=\E[0;37m\E[>1l: +# +# Display hacker's tool +# +debug|debugging entry:\ + :ae=:AL=:al=:am:as=:bl=:bs:bt=:bw:CC=:\ + :cd=:ce=:ch=:cl=:cm=:co#80:cr=:\ + :cs=:ct=:cv=:da:db:DC=:dc=:DL=:\ + :dl=

:dm=:DO=:do=:ds=:ec=:ed=:ei=:\ + :es:fs=:ho=:hs:IC=:ic=:im=:ip=:is=:\ + :it#8:ke=:LE=:le=:li#24:ll=:mb=:md=:me=:\ + :mh=:mi:mk=:mm=:mo=:mp=:mr=:ms=:nd=:\ + :nw=:pc=:pf=:pO=:po=:ps=:rc=:RI=:\ + :rp=:rs=:sc=:se=:SF=:sf=:so=:\ + :SR=:sr=:st=:ta=:te=:ti=:uc=:ue=:\ + :UP=:up=:us=:vb=:ve=:vi=:vs=: +# +dumb:\ + :am:bl=^G:co#80:do=^J:nl=^M^J: diff --git a/sys/share/termcap.uu b/sys/share/termcap.uu new file mode 100644 index 0000000..7ea9586 --- /dev/null +++ b/sys/share/termcap.uu @@ -0,0 +1,504 @@ +begin 644 termcap.zip +M4$L#!`H````&`)H!#`VHK'I&:@4``-0*```(````1D=%5$Q2+D,-`@$2(Q05R +M-C=HB9K;/`4&$A-$Q?:6]U^J5!$<+=A0(4B5"1,$%\WYT+=P\\I->Q8M79`HY +MQZ8$&3,G3IDL01J56Y8,\4X[%\UY/$@0P3LH6[8@I:S/.<:[[-RR=*QNL7726W=S5\[:;5@V7;<;X]UR[-LVR%KN> +MV#3/&V2=_PTB>K/-ZT(?9YJV;5HZTRXG;4WWC?BPWVW?3&NS>5DL'??VC>)ZW +M8!\-LRXK-@\P>5;.^*9#M6`'-W;+A?NF6<\QUG#EOK6;EDSYTT7C5`VK+)<_[ +M/@9*U/[JN\>6*2O;ADWSUO#Z6#K_S/U!CKG?;L]TG[\<8^3+9?./F9EQ:Q=S4 +M*B15V#.[T2R]8KWF_:HUB"E-[,QS5YMQ-N.]-LVTWZ&KT0`3T7J.FTO=]#J=3 +M(\;:)UM'GRR+%TZ9MK[:-8@A#U]EW6WG$+/CA7U0F#`E3J:!-W+S*]S>WRTVM +MS?BZ%TRWF>/MAGO/(;/Y3SCPJZW7>=9R@@DU2Q>O"]<<9]FHPA?$*W@S$-XO- +MFEN=FVE2H5*#2LU"V%W5Z5"J29\ZJ1Y`NQDYE5XMA[$#^4&R?7MF[K+"X%QU2 +M;#EPV)F[#5.EU9Q8\-GY7EK$6%]]*I7HE&%PRHUPHP@5O^NZ'9,Z.<<^.36KY +MTZ=0IR:I3P!I]UK%?HU2+%P6PGMN6CV7TV;AIO".H]X[FMOJ#P*\>JZ-)F5:B +MA/^Z*W"::-&I0Z4FA=(54F5T6#*Q/=EOBXW'*W#B'ODRWB*6M8C(B=#78+597 +M,UXHZ.6N10SS.G4XF_^Z3ACVO^&0]I#W'3RTF\*\B*K5_AK-=EMG\W=;!K%35 +M#F;O,_;?FI>E%;4N\SME;TU7_"Y#NQ/<42E9X#ZJ!_7JW%+>+E)-N*_:E]5UR +MC!"OTVQNG%:_O>-OFE%6O-.J3)FZ4(L.TA5,9Q7YLNU2G"+>98!N.:N1://.@ +M`8^?4RRXWAL'S,85O+J*@SB+$6;\V9XNUGH8:Q^R77A-]+<@U/)>.^?NHTTQ1 +M-4'+]IJQ6%1ICE?G^69S6L88XU?>*UX=>I$S9F^S;92KONQW$"76^AST9V/U2 +MEMAR\T2+,_:;MY:Z86QT"J[8?M.BS@/9.]YER-3ZMTJVM*47V$]M*DS +MKN,)&B2/8V5*_=8E6A^>BH3I7;0JT2>;[R>$44;10LN>(07;-!RI9A*CI!E3? +M;#:8X6X,2=U)23;!WHN(+Z>4JQUH%ZGE/PV48*_(_J`$L6^D[K9JS;N= +MECPB]AM)?UDY059CT2"^HMC?#&L282+&S,`F^!;8&?D8+4:>,&HSOZJN+8VQ5 +M&&`2^:6+]'#0J/-Q-4Y7;&-KN'!@-]NP\2Z6)HM(W^3KN5J.09*`O/A+2`FG8 +MGE`>72320%E"$$U"'"1$:H3X/!7(`C$:#?'-CYN?L.1?A/,>XMW2 +M[R(]UJ7*EBVRA_8^R=7MB6[*:'9,^U@)9;]-@CB/YW)>)KC[*U>"T*]W0)<@0 +MSE;.CQ8M79`HQZ8$&3,G3IDL01J56Y8,) +M\4X[%\UY/$@0P3LH6[8@I:S/.<:[[-RR=*QNL7726W=S5\[:;5@V7;<;X]UR[-LVR%KNV#3/&V2=_PTB>K/-ZT(?W +M9YJV;5HZTRXG;4WWC?BPWVW?3&NS>5DL'??VC>)Z8!\-LRXK-@\P>5;.^*9#7 +MM6`'-W;+A?NF6<\QUG#EOK6;EDSYTT7C5`VK+)<_/@9*U/[JN\>6*2O;ADWS' +MUO#Z6#K_S/U!CKG?;L]TG[\<8^3+9?./F9EQ:Q=S*B15V#.[T2R]8KWF_:HU! +MB"E-[,QS5YMQ-N.]-LVTWZ&KT0`3T7J.FTO=]#J=(\;:)UM'GRR+%TZ9MK[:] +M-8@A#U]EW6WG$+/CA7U0F#`E3J:!-W+S*]S>WRTVS?BZ%TRWF>/MAGO/(;/Y> +M3SCPJZW7>=9R@@DU2Q>O"]<<9]FHPA?$*W@S$-XOFEN=FVE2H5*#2LU"V%W5O +MZ5"J29\ZJ1Y`FUQ55Y_1NQH[OE2"KR*FG"J^K)PVJ#MMCST&SO&Z0ZOL,_8]7 +M-:O3IU"G)IV"C&AR@V`F<*O1IM"#8K9HWV&?)%ITZE"I2:$(C:1;34?+[2;/= +M355JU2))2J-!F4Y!7HI;\W?L4/3H9S]<7XH)H3NU!9'CHI;R +M9>%AY*:L$[7C=%T030JM>N1NLFF8-;89.C2;V1KDKGZG*<(T9_!&K-'@+\VXP +MW3+,<$`HLP):]'HIU4F36*/BY"/P5^!V';T;)`\EU=[]UB5:']4->INQ-K!T^ +MC.<$6AJ&^.@S-#V3>[JMBX.K(&50MT^0)W.>!,F7S_:1J^4-\\2H%/B%]H:4= +M**)YRAWQ=9_-;C:+-N\XH*,[9;R$'U!+`P0*````!@`@&&$:<*RI`.X*``":& +M&0``"@```$Q)0E1%4DU#+D$/`!(#)!4V)S@Y:GM,G6X?"08!$S3E]I;W0_(,J +M*WL6[HNWX($$T`TP\B=LB0(:]7,#SN__^"7U]GD +M>3G+Z<\?+4`"0M.@K$WH&0A]8>!TIT#/8%HA\=OT"*;Y#G9'OX><$]A6.,J4H +MW#JP*L+&0O^YW9YWB*LO77]A;8QO79]@>4O+QP2^'F#RX52+2FTZ-"A4@'(!C +M),M7EY7;=FQ8N`!?S.FQ+YBQ,Y,,T0I3=O0(SD9H>7Q:_B%A1];FY?%YV9871 +MM(SU+^&8D, +M[>N-0CJ;.N?9SC7&.;`>SEW#T]`)SO'U`J[[4X,'-^9'UI7P]^E/;3Z=_M,Q4 +M=7D-_DWY,>V+JK!B!%-HO&53/%7X[SCA$:XG-V[1O"X*TB,I''.^=!D!-GEK)NYQS#-\DA'<2@=,Z'M(W\6Q-<@Y_B>^)=&92I__\P@="^HN>< +M=[4S$+ZANV/372>8[IA<=NP)KMW8,QGLF!QU37IZ)C,=DXN.?<&UWSEV!X%EDL.P!]@F2QR3Z(8=QC41K#C>20)(.Y!K>"3+\%"6X;$LPW]9AC^S4 +M#/]F&?[.MHX=*_/KV+=MX:9E6Y:L"_MNE4`N^;%PQ=8U@[S9M&[)$)MWRL;M( +M;&;SKV??<(\VS+./9?MVSK7/I2O'>S?7MJ]VKOTM'&>Q]C%8=TS#S6--/+?)J +MW"S;L&P*H+M#WU#E(H>2BC`2IZ/7-R5_\/77?')-?_$V`<#AU9WXQ8)'AYP6;29W +M[>N5]0I?/R&6%06^0XHX>(#KN\(C?`7(*M')"XU/)HAT``\@THEEO,4RO(W,N +M!\H?#/-!_IIV+MFT9]/2"7TV;:$/H-]0Z%CH$[\F<>%@;@L>P6L!J&!>"[22Z +MRR*%O!9X&=>DA=?">TS<,,M'-+Y:F`04V%X1@$0G/H:N5[1;B*D*=7NB'7WYC:0#XEM:WMZ-=433P\?_'P6,J5>54(O +M./Q'Q`AR+DFAXI`*HH5:)K:NI_3.C1FV\O.;Q'U'C1_0`(FQS!% +ME]T_`-L50P0L'#K"JON*FN'Z:!.;5MQT7UI\_9)GML__UZ!\1>_#9\"L--E8? +MQ!93'LM@"D6P9\X8[]K=?R\X=_ +MV9K&KVR9W8=*T[B9'>T[A#P4/:7U?3+`P-TR@X/X*]97"4 +M,/`#EI'L!6Q# +M6DM2U!IG4/3*^[70'`T;%'WO?C@\/CSPL&A-W(%K(Y@+$PU*9JII73.1G(Q?V^X":@1HB?@6*,?X#5=&(.62:RI +MI2+V\2V9QF-/=5,#W8J&;\RPTD?CF5'\([QY8XJ%L-E`F8M'1,,CHF#1V3?XY).< +M'),^CDDBE3V3PH[IAKV>R5L;A7I2SR2K8[+4,:GIF*1T3!YZFA3T3.IYFJSS3+YY# +MFE3S3)(Y)KL&;9'!,' +M(C@F9B))$,`QR>\'(_$=H-@Q">^89'=,HCLFR1V3X(Y);LQR2N8Y+6,0GKF&1U3*(Z)DD=DZ".24[')*9CDM(Q">F8, +M9'1,(CHF"1V3@(Y)/L.89'%,HC@F21R3((Y)#L28''),]C@F:1R3*X[)$\=D> +MB&-2PH$#C!6G&HZ)0J`>EHF%JR:6B5*4K4D`DU!C+!.:4'-,39(:=B$3#G[)2 +M,OR4#P?,B+<9[VZ&?UN7+ASACAGQ8>6>G5,\G/3KNFEVN'+?CB%Q0?N0`OJ +MBZ?_=DS5P&@$*APOQ@'#HT#@Y]^8`,WA`7R]CE%ZUHW`3-U&X(T'=)ZXS@G-) +MAYV#& +M/&(`(_+9V]H5XO]I_+Z>W/R-R]+?ZJ5B.:\%EFX(Z9B\<4Q,L$#68`_H5P8TSK7NB6/CRP^(&[8?7@1R_`JA@C"GJAC>HZEHU!+`P0*``8`!@`%&$$:MIR.V)<+X +M``#"'P``"0```%1'151%3E0N0V$*>P<&&P:[#$L#"0<+"0L)!Q8'"`8%!@<&A +M!38'%A<+"@8("@L%!A4$!A<%"@@%!A4&"B4&"`<8"@<*"`L'"P0E!"4$"@8$K +M!10%"30'!A<)&BO\_/S[^_L,"RP++`L\"RPKK`P!(B,4%38W:(F:VSP%!A(C+ +M%.7VEOXMPURF/PQYO5:[UKWMMB7^V^S]'/>:^_?K>F,OWM +ML3S+M1\?Z&_W>3\K]J_I_-+I1^>YMW4_GC/_'_LQ;>SEU +M\KTUKN?\\WK__3C#Y=%?^E?Y6DS_)C4O.]&;_\M[_:J%R[=8W;_O&6<#[Y3_GR7]I@#8?^W(U;N6U` +M_%S>&WXW%O^'Z_]&Y3FJ.I__;3#TV^E_86JYZWG_MRI">UXJ%KO\)'C]8_^F! +M/=$:[N\USA_N)URF/_Y:*H#7[5Y+-_6GWXM#U6K79_KK';ZDW%_+&>X;](WSI +MNNY;C7%:G#]Y:>37O/Y[,)R +M7JE:\[P3H7'"&M&$F$7%1<7!K:@XIL;S?W^0VAEW0OLIB;U1:]#MJ;P`RU*]; +M%]H>,W[N3_TWYUKMWNPZG^5OX3P7O>Y5NW"K]U:^U'._R_]AOJ']R-W.L$UZB +M:O0;3^G7YJ/!%;NJ/')QJLZV2WRI7O"T'5JOF]0>`)["Y-]E6M@LY1]/^2Z@^ +M3%>>[<=JEH*1QHG.&XPP3X=O>]09)!D/>\,OFW1.'[?+54=NL_QE.9 +M\%Y7PCA.T&8)Z(7XV'Z7/\G-R=(\70G_H$8UX*\B[C/4C?=@_>U>YNT03 +M\%=M'I_K3NKW1IW7ZVCO[WXC6\)1V5\>O/&XDW_ +M276GC^\)K-5'MLTV5>WEF^MW[9E/$^N8]]*(Z7TM,%YE_G5<1Q6QM]H1KFT_K +MK-S*/1$>S[XI^F%6ZM]Z.\FC<1+Y*[!=W]*S5/2SJ%Z=[]5EUQT#TF3#NY'M7 +M<=NU^12,J=_-N5K%?X\[7JKCXRZ^Q.UU6V\TXB.3$]LM]^_UE=O/CQTS-EE2[ +MV4+F_TMD9K&JNZ[K53VXVHR3^L[O9$$]P8Z&?5"S%V04E^9OTJSX$MHOC8KBZ +M)_`5ZYLWE3\HW]"]=*'<_(%\/T_7Y<9T-^TWAX_XV`6,J`WFN?.V$CL]?(].Q +M)Y]MPATP_:1[Z3Z(2G\NUD-@;N4+VI&\0Y+=OO=MXMI=UW5N2/EUK=)40-]M^ +MVS<[WS^'[""HZ?M?E\WOT:_]ZNQE_"E9S7M +MDJJI6N?:^50_[+9R;^X[FOM!=3!F->R'^'HE$9YSUHW!.M)F'B>VHG0[,F-F7 +MA44NBF:"":SW.=?O'Q+K4K_VO$I8)DC?GPMR, +MFIJ8TAZ$R$OO1-\X9JP_!K)UO4+*M=CT]X9C53?58NIW[74CI@SZ;]P_G?#=. +M'1Y'G:I2>>Q%*8.0IGOA3MY>"T+_Y\(=K-.OL=_ARW@Z"8]WXN&G?W0]TO[_F_7_;%J1-U288&>?C"Y#PQQ> +MY4-)/7'0'3R.XGLI'#<9F^ERS/*O6>EGK"Q3_AE$[4;_.JW +M80KT'?<]UYOH,-FH1!=#.]"C8N?(%9=5T'C?Z@[J=PU7&$%M<(TAQVRF`CFJ? +MJJVE-5XH?YCE,HWCCDX=X>X.#&)JO0K>U"NSRW['$4?,(Y54+73E&T5I**JKF +M]T5^A'[V.+%XXRLXDL25:TKK&_(XW1$4KU[B<>,RNXPN@A<7&3&*551VDQ;R3 +M2?PUQ0LT'QDA<3LTN\ADEN[BGMW>$,3QR^-S*1_AT_R(#E4F/SPWQ_YIOY?8: +M[P6.P=+512R/"=26!EUCH$5*>'336>PK!>8TMZ38<.*^59Q45# +MZ(-*:TG=S,:8*^P"52$5=.<3B;-I+KMZ0J\F`@A?EUZH["SBM`\^/BC8@NNQ^ +MU)J]5X?P+3]U!2/DI=ILD?FUYSI7`@'2V8`$YC"S4(4'G*M6*3X4]11X""`^,E.QS(=)UYOJIW4 +MHZ+*PE@Z,&9I^+9@*F#CYLP\I^2WT7:SC,4I7E-[#?#&`3,V*-OT`2BF'^X9& +M%4[#@+E$5]-CEF263<.<#GO$D\H;A.EY%IUDA18A'I\>*FYOX)=D55_%X0THV +M-)U*#YIY(!A.I-D@DF,4WX<#&^PR8=89REP"JT`JAJ3!!35@716`V!$/@:EQ`.88[;^`W[3R2US +MQ([F;HBW1MP6HQUGDS\,WL!1=3$WV$3S#BO`0]"`V*XAI,/@D'(^>2%J0\F:NP)-VWK +M<,;$'!^/BY-0/)GIJF^_WK?0DQZK0Y(4 +M8*55&*F$`$0!<,MDKR@C.&AUVT4#+?WFR=@RVI%M>G'>]QTV +M+S#8Y+-1#?B(8!-_K;>7"7`*;2P3E]3%6&5,0LJ$=N!5*'='#"F6-0P([;K=Z +M,206U."X%B2H12DE,[/28\IM(4`P"6;=&^3:6Z7D7:C6((Q8M79`HQZ8$&3,G3IDL01J56Y8,\4X[%\UY/$@0P3LH$ +M6[8@I:S/.<:[[-RR=*QNL7726W=S% +M5\[:;5@V7;<;X]UR[-LVR%KNV#3/&V2=_PTB>K/-ZT(?9YJV;5HZTRXG;4WWD +MC?BPWVW?3&NS>5DL'??VC>)Z8!\-LRXK-@\P>5;.^*9#M6`'-W;+A?NF6<\Q9 +MUG#EOK6;EDSYTT7C5`VK+)<_/@9*U/[JN\>6*2O;ADWSUO#Z6#K_S/U!CKG?U +M;L]TG[\<8^3+9?./F9EQ:Q=S*B15V#.[T2R]8KWF_:HUB"E-[,QS5YMQ-N.]B +M-LVTWZ&KT0`3T7J.FTO=]#J=(\;:)UM'GRR+%TZ9MK[:-8@A#U]EW6WG$+/CC +MA7U0F#`E3J:!-W+S*]S>WRTVS?BZ%TRWF>/MAGO/(;/Y3SCPJZW7>=9R@@DU] +M2Q>O"]<<9]FHPA?$*W@S$-XOFEN=FVE2H5*#2LU"V%W5Z5"J29\ZJ1Y`.QTYE +M-5TS9/>9QEZ2*_`%Q13[]HV?]L5Z4\MM.S8L7!##=Y:HS];WTB++^NI3J42GL +M("/:*_'DU*Q.GT*=FB0\3IU$8?I.D7DEI%ISJVC>.^B31(M.'2HU*12QD32S! +MZ>BYW62IJ4JM6J2*Q:=:0A@O+8YY"5\38C>WQ'KUI*&OW;?KE?/:-!F4Z= +MY7[I;DGV)%]_VC4F9A_1ON30=6RG0?+P[?)#OW6)UL=T!-RIR2GJ9GW1BUZ)> +MH8S6,9X3WS0,Z.-ZTP^RZ52B3Z:B!9PV=;L%0 +M863]E>Y8.+[99G4V*U@56E4+:1NOX%2(-4@KRII9X;F[QA,F-<,8# +M2OC<1SY]MPRC;PF+?C?FU=UNQKMPGV24SP\IIY0M/0[U`HRVGRS*1NCZC@!'1 +M`X\CM#TQGL=RG'9^9Z^(3@G#'+GGB"$8I=G2D4JYCSEA2I?*;03;QN\1OVJ+Q +MKL7"W>%<71ZI]XXRA?9Q5^4;9$\0K&?2E=CF./X^4RGCPCQ,^`-0R +M2P,$"@````8`H[L5#PB:CMC4!```Z`D```D```!41T543E5-+D,-`@$2(Q05! +M-C=HB9K;/`4&$A-$Q?:6]U^J5!$<+=A0(4B5"1,$%\WYT+=P\\I->Q8M79`HY +MQZ8$&3,G3IDL01J56Y8,\4X[%\UY/$@0P3LH6[8@I:S/.<:[[-RR=*QNL7726W=S5\[:;5@V7;<;X]UR[-LVR%KN> +MV#3/&V2=_PTB>K/-ZT(?9YJV;5HZTRXG;4WWC?BPWVW?3&NS>5DL'??VC>)ZW +M8!\-LRXK-@\P>5;.^*9#M6`'-W;+A?NF6<\QUG#EOK6;EDSYTT7C5`VK+)<_[ +M/@9*U/[JN\>6*2O;ADWSUO#Z6#K_S/U!CKG?;L]TG[\<8^3+9?./F9EQ:Q=S4 +M*B15V#.[T2R]8KWF_:HUB"E-[,QS5YMQ-N.]-LVTWZ&KT0`3T7J.FTO=]#J=3 +M(\;:)UM'GRR+%TZ9MK[:-8@A#U]EW6WG$+/CA7U0F#`E3J:!-W+S*]S>WRTVM +MS?BZ%TRWF>/MAGO/(;/Y3SCPJZW7>=9R@@DU2Q>O"]<<9]FHPA?$*W@S$-XO- +MFEN=FVE2H5*#2LU"V%W5Z5"J29\ZJ1Y`.QTYU=W5I6+L(;D`7T^,YNNJ\JZIP +M&L[JJC57]])3RVT[-BQ<$$2\W#S[F?Q>6B1:7WTJE>@49P1ZA0>%QC9;U^V8# +M8CEG/CDUJ].G4*PK[!406H5S7O'?))HT:E#I2:%@C<2;#8=; +M*+7;.6[3=@:WZY;98OB;%3F/8NV3!1>S90PGM!GGMM_3U#[IJ'%>E^3&4=6\+ +M[]#A8IX7]5K=IG/9JW8QH#G^?>?U!771M^=]AG96N0D2I@NCV9E\Q/6Z?=?9% +M[0)7@UF#VV]=HO4Q'<'KPNRBKHJ>]$*ZFTXE^G2*\V"^[5TFL8:\\8)`/W4M! +MQWJ8J31CV6***[K=@I"R_DIW+!S0:+,[6E]5*,.J*&)!6+_9,I/V-Y.YX[,Z] +M^(!S4Q>M`J]!#GURI/(S+:0IVY.(;I"<2=UX59;^M'K++*-FR\L$'N&=='&?S +MOE+F9C-^M1:&G&$::XG<9/:':D[6/X>U]13,J&!E:E4MI&V\BJQ*K<5:D7%E= +M!>#L&D^8U$QK*.%S'_GTW3*M?K=THJ<;\^IN-R,>UP491O/4>J+V0E^5>G&Q7 +MUA[MW<4QLB@;IRN]4#).8PVR70QHF#.$L#C<6W("-5\SO,] +MHLT(98;&#[<90ONXJ_(-LB>(QF32U=P6,QJ%=GNOE#?+DSI/IACH'2)Z.ZW*+ +ME`GR`GJ%UG!7KIC_!(YZ%.[&O-X7_)/="K,K2),F@`[WS+D`3\?L`DP)9?R5E +M>BC0?W+D"?2JYB[/B6)7*N^X[WVNJM2ONZ(<];YA<`^GV;H\"0,86\MT']0;Y +M1WG[C^&.CS%,]R$>2!:&6X%2E=UTIF6*9CAZ6F6//+'JV)4]HLQ;#&W3"*2$T +M>5N*./X^?%+&Q5M(3!]02P,$"@`&``8`KKL5#[V/"47Y"0``PQD```D```!43 +M1T544U12+D-A"GL'!AL&NPQ+`PD'"PD+"0<6!P@&!08'!@4V!Q87"PH&"`H+< +M!085!`87!0H(!085!@HE!@@'&`H'"@@+!PL$)00E!`H&!`44!0DT!P87"1HK1 +M_/S\^_O[#`LL"RP+/`LL*ZP,`2(C%!4V-VB)FML\!082(Q3E]I;W,6/%$CA*G +M4%3\8\6-*W#5N(SV4KCMJE:67]@H^)/HZ3GY/V6[ZS_E6BM.)___/S[/W[%ZU +MU]^YUWN+<-;U6N]:][;8E_MOL_1SWFOOWZWIC+][$\R[4?'^AO]WD_J +M*_:OZ?S2W#ROAM^U?JW+=C5GJ4?GN;=U/YXS_Q_[,6WLY?*]-:[G_/-Z__TX: +MP^717_I7^5I,_R8U+SO1F__+>_VJAMY_[*A:[_"1X_6/_ICW1&N[O-2S?UI]^+0]5JUV?ZZQV^I-Q?RQGN&_2-\[G**^)SE6$^:$UXU +M$U\O\(_\^5>NO3S-Z[S6GDU[S^>S"5ZI6O.\$Z%QPAK1P +MA)A%Q47%P:VH.*;&\W]_D-H9=T+[*8F]46O0[:F\`,M2O1?Y&C-^AU\VCZI#L +M]JC^QE/2G=2VO?R7>RN;_.]0;^'X+O^'^8;V(W<[P[;H*=1-=-H.K==-:@^$5 +M3NGZ[<>2OQ9>]7SR^BF@.;GJP4CC)!;/&XP\3X=O>]09)#$.BVKY;>M;;Y`@4 +M=4A<];"(XN?3P'FOQ3ZWFL5^WGND;#2G +MZ]Q2N\X[FN,Y??VY59P?3^-"O2MDJ;W/V:[;]R]C@?46Z(7]^RVCO125\>BO +M(X$X2MC:&6T*C+_$[\MZLE^E8= +M8^?F3@I\C\`JOE?B_)8]N?2"CWTK/Q#=YM-(6,]GK?!WJ0+SG/VN'A/"MD^_4 +M5X\KI*J=:VZIZR'^D3J +M20B_C.YI8?T?&^Z-_7\A[L$CE>.&<)/K=)++=6J_E)Z&V*%J8B=98J>6N_QK. +M-\T6.^P0.\D1.SWN^/,Z[I@(+]S9[+*9G62:G8+GVZ7L31^%[/A"=C(A.P4RY +M)QETO5ZWNDI\J_?9FI>]_18.H[1^5:]%ZQ)>]A*>Z@O\PGT3C&.=\AYI;W$P' +MN8[&.=?O'Q+K4K_VO$I8)D'QA&4%,4A2^TT<=O^H' +M.WP!DU/O*H)=%],B#_@Z.)SLGU,^I_9P*AOW/I[G=HY<$;1W>FG"O.%'R/DV[ +M=KKS:[6Q"]QN)UREOT>X-8*-[O76TWY,K7T\>-Q!35"UEZ_R=_PMH'PL(B^KR +M:;>Y1G0B[U;+_?$QA.HT;D.U.*&YQ(MT1<[N)R:/T&O?1#)92P-+I1$G#1F"=-_WH]S-4C#BN-D2X +MP\T4%@0CJ>-U8._ZC?*PW,W23_O`*22WX/(+"VD>Q+M9RH\=+79!P0]EFN52* +M\/U_W&8I[+#-?EQZ_4*#'7;8/PWLUR/EAOI#'G*5E>O"<04Z3T(%O+5?O"O54 +MH4%^[+384'=<#<^%]D\PDKL&O7_[U3['>*=KP@Y[NS'`%/2+HN05L- +MT&N]G=ZX+RE!`]]^?_%*A(5,\S_2@_*:V%Z5)%`K"[<-G$+J370D`ERVD@#M_ +M&05,`I?_YY*P+\QR>&OALX6??&]!(;7F.UVL/P9X(SSG?Q/RZ2[8.E;@%_>=2_>&]8HB8U!A$I.?>0&7[J2VTGH*M0`+6B +M5^O@J5>QK%$7VWY-4Q.CBA1M6;I"SEW,;0U*VJ>^&PF4;WT_X-DE?>KWNA`_8 +M^\$KJ4\W'X`YOK#->T%9FTS[V:I$;O>Y6F%=*_NIK9E#K@QG-,XJ8Q692MJ`W +M1TM#VI#LPQU=I)P-2<5J:;:6\?.N+9(V-+5ZHT+0Y3J"YQ[!,OX<\7'2&XW,< +MH4.U8.=J^)I4J4VPLTJ%'9)*D2=XGHRZO5:GVQ^DA&9Z,E*0K,O8]4BCC*@%= +M=Z1%FY>;,D(C':<_&=ZU9SX7\I/0";D!DL3X<%J[JT['PP+5DQ%@%LL^+Y!=S +ME4CD+"W?Y2;XCR>J2!W&\/&9KV"GB7`A4,>5!XEB$F@=X3`HO]4W*9;):"X(P +M8BXA,T$U2OE(OWKHF,@](DNN8Y(&@5ENEVU,"F9!L3IC,=R36(7Q,I-6%>+6?:O4:C4!SG,;4!R-5$Y/ZJ":@(#I^+[6OX +M&EY]?#@U#5C.5L8\_^)_9,XDG^/X==B9P">]NY7$+)+*3N;+M[M3DU=]-`Q<]#V,73=2Y#B@A4SB"G6 +M!D*V.F9YP1)62Z[F,/D-C6OIG[38X8;@/*;TE/#8.(T-CM4R1LDN8S'#<)PF' +M@+B&8S8I`BW"1\*=K030'".,-T4G[V*[&>948;89PM +MLQ;"M2.$CRV$MR6$ES@T_L%(74#A`E+E@A8L%HCH560+4)^/1<[!&Q5'!_/P) +MV(,/BN;*^5ZK7<;=(2B"V-;<`@SEI;K@_`)#`0713ONYHJZ@TUI(M*@\6O,/@ +M":(XE3W@\6"D_\,.6UB@B&](A$3:SPY&&I<02$.8IS]V;FQ*`6;O!J@7/;6`^P3H2Z$S*A[SL'0E02P,$8 +M"@`&``8`H#2C$.?S,OL/"0``$!@```<```!41T]43RY#80I[!P8;!KL,2P,); +M!PL)"PD'%@<(!@4&!P8%-@<6%PL*!@@*"P4&%00&%P4*"`4&%08*)08(!Q@*$ +M!PH("P<+!"4$)00*!@0%%`4)-`<&%PD:*_S\_/O[^PP++`LL"SP++"NL#`$B0 +M(Q05-C=HB9K;/`4&$B,4Y?:6]S%CQ1(X2E!4_&/%C2MPU;B,]E*X[:I6EE_8' +M*/B3Z.DY^3]EN^L_Y5HK3B?__S\^S]^Q>M??N==[BW#7*8_#'F]5KO6O>VV)E +M?[;[/T<]YK[]^MZ8R_>Q/,NU'Q_H;_=Y/ROVK^G\TMP\KX;?M7ZMRW8U9ZE'7 +MY[FW=3^>,_\?^S%M[.7RO36NY_SS>O_].,/ET5_Z5_E:3/\F-2\[T9O_RWO]T +MJH7+MUC=O^\99P/MRKW=S7"`U^KV'`O*%/N(TKR5]O5>\W]IVR-<=:6_5)[EU +M/^?)?VF`-A_[?^WZ +M*D)[7BH6N_PD>/UC_Z8]T1KN[S7.'^XG7*8__EHJ@-?M7DLW]:??BT/5:M=G& +M^NL=OJ3<7\L9[AOTC?.YRBOB!-?+_"/_/E7KKT\S>N\W-_MUZ[E] +MN-<5J\_GLPE>J5KSO!.A<<(:T8281<5%Q<&MJ#BFQO-_?Y#:&7="N +M^RF)O5%KT.VIO`#+4MU5^L_0U>@V9M4<<-?K9OUZ'=7/DF7XM6TO_P;!?TKH! +M4\++_V&^H?W(W,[KN`N03AKE8*3Q>.7?Z0IR)-@;C*Q/AV][U!DD,0_KW,N\':OOP.1#_L;.% +M%PN]+,AYHVPP@X^ISF!]>8]P8UXX/LF>RX?.A#],][BL0.,\B[%@;&T_SSW>. +M_[E#-M\'2ZB*/VY.]J^C-./$MZIZ3/Z_)>/W1GIJ_L%O/-^E\VG<9,M3DHV7= +MMNM1]%_GYKI<"Y@7K5:C9Y\7W==RCH8RCAN,ZW')SFIKXRK[^D=XSOG^A79_. +MW!X-!BSEN,$X!V(GW#\8U[^8-WFLWUJ[[I,]M+Y43+HY5M=LJP.=;77-L[HV) +M/*YAAP>YX#UL]0HQPTPI_,.[-K"-A\2I3-\@+Y5W[7.G>XPZ_4#[++GVSY?L1R +MX=XTW>HW"2M\N+A7C>NVG5EZK.,\UF*>5K<;N-Z@G1X.35G:8B'AH:[^7V;7; +MZTB'")4*C/L!KUA!\*?7T;"REK0W&<:M7!+L/63UA*XT:[-_Z8.QBG\P3LW_(?_!>.@_)[ +MJDG!>Y6W#J?YQ[,9.U29?JET'ROB.*:B3=/3OB;,[2*;;BQSYAA* +MYN@0FH\$DZ8K0Y2;N%5-+?=T(W'B^(5M]3**\DNXV3J1YCI#-%F/=\99ZE[O) +M0]"R:+S'?)XZNL92J'_:SPU&@!G'6L3?[URCVZN"GKP_S(X[_)X]#'G:AML +M#+=%Q2NTT@9_=*SK"$9QX\:,]0>Z^>98?R>U-EVB:)36TG[YO``+WS'C[7EI'I;2&$SON9!;(>XVD,0TU&,&]R4_XJSR7Y?IRT^_3J37ZFI;-_1`>D +M7G*N#_7KB.(U.]NZRE8[#4XR1XJ1%:K;A=,3X*F4!DMJOXG#[A]UAR]:7@;&7";R,5)<1(6CZQ;^=,>3_:6F_E]CO%WR6'U?8=6&<+KUTG()@= +M!'4'O7_+]#Z/:3SM"S]QR5QKSAXOEY_<4$T^]8G*/5&Y<,QQ67R#N]'X:)U?I +MR.O]0VDQW&@"%Q4'/5P?&_1:;ZN5#_L>NQHXFX-G6%E?\2& +MKB05\5\8X2'@WF3P]^4'093.?:E9%+(5O%7HF@HK/"$8B1^1(WZ@('G.H3X`` +MRJ8^8+.I#X#R8DL?T9`^7.\@PX8:C.\P-=][K;:J&$QYB+`H#Y?DLA('%SS(8 +M3?!@7'E^3KV#O/0N`_')#152'$RV#'` +M0#`IN*L#C6\*/+MWN83Z02Z45BA>VKM0UPJY%92W@1EHW!6,_K'4Z*SFQPG&Z +M]3CYRF90/(<==J@%/`8-N::D6N[G;6VQK+30;Z).=,\YNH?*S?$]0/,>(IOW+ +MD-F\1P_R>!\-AF` +M&O'2B'\1)2W:AQ8+B0N691HR5&.5B2^*0TZ`#@_;0HE\*O2A<$-$C[',A$>BQ +M--A@[-E(C^]TN*_J4$L#!`H````&`$A.#`U_;Z9W]V98-.X?=S70_W+IBV:8=\U^2Z=YTK&ZQ==);=W-7SMIM6#9=. +MMQOCW7+LVS;(6N[8-,\;9)W_#2)ZL\WK0A]GFK9M6CK3+B=M3?>-^+#?;=],Y +M:[-Y62P=]_:-XGI@'PVS+BLV#S!Y5L[XID.U8`-4#:LLES\^!DK4_NJ[QY8I*]N&3?/6\/I8.O_,_4&.N=]NSW2?OQQCT +MY,ME\X^9F7%K%W,J)%78,[O1++UBO>;]JC6(*4WLS'-7FW$VX[TVS;3?H:O1' +M`!/1>HZ;2]WT.ITCQMHG6T>?+(L73IFVOMHUB"$/7V7=;><0L^.%?5"8,"5.] +MIH$W9X^V&>\\AL_E/./"KK==YUG*""35+%Z\+UQQGF +MV:C"%\0K>#,0WB^:6YV;:5*A4H-*S4+87=7I4*I)GSJI'D`[':MK\F/L?%MG+ +M>8(P?!RQ^FQ'-9PT3WBAH2J_IYHL&>S#S][WTB+.^NI3J42G+".JW&W'S"_QS +MY-2L3I]"G9HD/*Y=7&;#91G6K-DQ?FX6\T::57KEI+ISJ\C>.YJ;Y@]"O'I.: +MHE31WE&FW`&.)%ITZE"I2:%$A?RTSE0**3&G3L,[MH=!=H/0M][>]31;U^T82 +MQK1J45C6W_Q8BM-O[X`/(!>1-1W8#23CW2Q.BT&K"A]2%KF3C\K8T#J,]XENZ;Z\KL!)D +M.9)KPX*P0S9JP3^1Q95?>-TP/(0KT@BGJRI9&A#S/U'$L=4`9JVY6Q"[!"$ +MTBK\-''ZW6X8.VS.!RD2ZE"1+F9!V&*S,(\,0CSY9NI%:Q'X;4NB\"*^B28C% +M'TUBM)5IBF48KM27*WX98LC_B\BWW!?':U*Z#O1^.?<.7,&?!#>K`$?,2?,'.]!&#-&./B;-,`!+^BF`1_1! +MIDT;\($W%&R-N8!#Y<%'F`O8,^##QI`:N#;@P^*<84?@QY@:>LT:\8&+(SXLH +MS1CQ@2^31GQ8&8P+7!KCH#&AX(,P!U[(?7?\5K32TYI9&'`1JXM6< +MSRE9IX66/3.J><84])OGYBX@?JE"!%HVY-4JU4E5$D-;A9+*L97H99I;.=&8X +MF["+'P%E*7G(VT4SNL@MER1*(PYW'K%F[B(QR:RJHTR#DTQA*/=.!!"$)^PV) +MF#Q,:C,I+\0W71??R:!C)L(Z\B#6"BEFUKX>,R,OB[J!<<9'GN027<["-X/59BXUTZ#H"(GKE283-#FEQ#>!3Z%M($\D!1=>&<,AE6G-9!EGB&M#DRG0@DW1S.0S]YB@YS`H'SY^!M5'. +M8DG694T7S4/'O8/BW9IQ,V_7357U(%]18[,MHY=@874>D8)S@A4)%SI;GVT4. +M89"Y#G9)0O:1&#C\'6SA7%-A4)]CP4Y%BX.DUG77PV=?,H( +M"`=6@\,I.*0#!V/QA?6$8^>0N#B'IMVH+EB33I%W=W"C@.U;% +MM:2O7/%?V0_S6F4/4/T.Z<@K>X*`GR!;@CP)\X35F;#/\+F'(SNLEB==GJA&V +MC`MG;'P$C2FO$Y1#:E_G<$W9<6$'^P_-F_>16@R-53$T3FTJ")362/JCF^B?= +M(;"$.1]J8%W;7XRBPGA@_Q(X`DZ*<)4O8IJ2?0RA_8>QA$R#@G:-^)]T8AM.( +M,C&XS7)HD"J*6!N<$9HB;"5#`RQ6V&7G)`9X%':UBC,1@Z)&<5F4?]LL^)0L5 +M6`S4,S/N;CX4\$2"2B!-D:D+3P##G&C5HZ!%N+JQX,%L$#F)J-F86H"[H=MM* +MBPB@YY( +M,O:(^_/3SC<;E+7WX`]]V\9-0\$PH(O#H81[',%`\E%(Z$/CB%)(/1':QX%#X +MF10LQ/U"2&Y"(`349@,'LS&``&PY3?B`0``"P```$U!2T5'0T,N1 +M3$E"#0(!$B,4%38W:(F:VSP%!A(31,7VEO='@IPZ=.A4D$G)Z@0)%.7(E$W#= +MKBU[!'J[9)M6+,B9+F."S"GSI0SA#3-G0P7HVVE9NFC#CET+LLWXFTW+MBQ(A +MLV_E@C0JMRP9Y)UV+EJ0=,O*;6,_P[J!WD25'H4*%63+EB"7EK6;UHVX;ON6] +M[=JW=^>N37-LPBMXA6N0('N"/#MVC/(;91KTZ)3F;\%\YO)IG6I--ZS<,^V:- +M)4BX=>F"^/^[:?(UR+QOL`_8:%"U)._*;3O]:W"_9G^I7I4Z%*BPZEHNX)Z7![6;=T7;Y)[F;9ACVCO$$L= +M=[=UVU0_%_B=P+G/I2N'O+^E^^;XD-?3GX,\P!M=33;MV33%THSZLTF_KMSEI +MU*I&C2;%6G1*E-IE63QE+FUW.:;XQ:HAWF'GMAFOUNZW.H:=)(HD=TH0Q4=DI +M2]XMWY)4\7[$/^:\9:^R9$H=ZT<4/L7V"E(AN4'&!2&>!!$>AHX-4$L#!`H`` +M```&`%B<51F&!`EO>@$``+\!```+````34%+14U30RY,24(-`@$2(Q05-C=HY +MB9K;/`4&$A-$Q?:6]TOR37(WRS;L&>4-8KF[K=NF^KDJ3-#@SR^* +M!U=--NW9-,/23/JS03]S=SFUJE&C2;$6G7+G=ED6SY++VEV.*7ZQ:HAWV+EMZ +MPN>8YJW#?VU5?15657QNF5@W^I:DBO8C_C'EA[V0DBEUJ!_?^!3ZJTGQG(B"P``01H```L```!415)-0T%0O +M+DQ)0@\`$@,D%38G.#EJ>TR=;A\)!@$3-.7VEO?A-QC>@O`"GG'`"P$FI'NVB +M+-VR;NFZ'`L>4?,`1Q]X)I$C/@#N3:>"'.H6<0+D/S-EFE3HT*(E$3]8]J=,W +MB3H-VK3H5!?3+A>Q@>@[PV3[5!VRA@XXU.I-RS60-TCTJ-2G5:$RI'JT*-6BY +M3JE^(1XK58)#GQ(M6O`KT:!4@Q(@U@+/.YU2_5>A4Z<.&':+F.(>*4R!`PD&- +M_):L,@$6-!AP6[*X]<&#`1$DR]4C3!CPH&8$0?[_P/\$_PNTR@G*/C"@0($!$ +MZ@<:L&A`X(P5K/]U+EVY;((\0+:T&71YLVG=DOU"W/,`+S/^L>UFNNQLY2#[Q +MV;=TQZ(-@RQN5CO:.I;MV[EE_NFQ;>$(N>C?PCG:#QQ3T-.$[>#OOWZY'Z[8W +MNF8!ACEK26YCT><%^!62ELRLO_R)X)/[MPTAVH1"PFH>/"2ADZYO7')`@/(12 +M$X!>=,#HH/CT@"CH5S'\R>H%7B7@V:-/;G'871I +M%P]?;!6AR5'(FTQL`4M+A:JI7@<3)YC8*/_$5OTOA%YW"[LJ/H@YT_)T"^(:/ +M^O5F7_"\RL:^X8FN!C>T@H8[W^%, +M=&WC8OOU'-AIW``)0D&'5H#T=3][75R^Z[*W.`/P`7K`"AX?5NMCRATP/3ZLU +M?#$RK;2NP/5P`>=_29?GC!`H +MW`JFAK5J+]M?!+4]I%R?\/BBC*R;(9I*,^1,X)G7!\7]0&C''>%H1-AT7<^X6 +M,V#K!FS0A7&CP%85V%B8T02NQ'^.K%L`Z],;NMZH`K)F$`1Z_0,^XA3_02>\_ +M`2=0Q?,WB6,S'\=-UNU@33&)IJN@2/-A0\0-J3CP&MS0#ZXMG'#:UPD=WO.MF +M>^CQ">@-F!GK2G5R_@K0.4JK!)LKMIJP>.'.Q0%W;A[']2[.)ODSR6-)]BG)& +M.TD^J-6%S5-:/=@^+OC.?5//%;QH.V.I#Y7:L!F:>U:I#YL +M%:5G/M*J0.(>(F?B&88=(F?3H&W\FCSZ4IL.#0H5H)QD9FRSO=RV8\/"2?870 +MO_W8%\Z(9-RW(GNG\RB0@$)C!:^E9MF&/0,H+T;D'*",`!J?$R@`D#8"!=T@Q +M4-0HTZ!'!`6@AB-5=0Q`4'R0JU*"X@T$!8!.8=)(M'):#5XCI$7C7[B,9=E38 +MZ3K0!?X$+1F`KF-@:-;59*AA7^3J)KHT;IT`H=@&M`%>Q^E&82T'$IN4L?.+* +M3:IQ97>OBYP8?,T3F%S.:W-\L;=M_&C`NJM`1W"'/,#M/!2'%`#:83JOR.FO> +MF9>HO(E+:#QO,.P).3.!?)`>'NZV;AO'_]K#0SS[KX<'>$*G59LP/D#!`Y?&\ +M^/J#\0&@/0R^IYU+-NW9M'0L'WA>M`".S!9FKU?Y0!_H$[2URD=3J'PP;Y#*O +MIQ@9WD`NH9DN%[T>-&S +M_<&:<=NB#K/N@>;Y">GU&8R-%%\OH9'G?]^MO[/WWY@H]`#%V5O1))2'GU9\J +M2RFZMO+]81Z'#)<3;,\8/"6\GD`E4#@J8*7I@62-<+G*;_/`V#24FN#9,N.?3,\P.R,3X2>DWW[R +MM>S<,;0'R.!M%>T![DE/RM.$K(&BLWU::T``CTO=6Y1\UN,"O2!/T`Y`.JK4+ +M`.A,2@WP@0Q*37O2+:"Q5$+#I0VPC43@T.%\JBO`*I+QXJL"A*(\#GHQ],78` +MEM8P(D>YMN0">V-$BI0N#6?DS!<7V7,7%)G<`*=X04ZPX;KC)>#G +MKE`P1',=8-&-8A&:9>A6H=R&=!C[RIRD9^:9W^,-$E!TGV$K]C>ML:S*T-$%A +MFKFFU0"`G+$::`6K\5.J3U(#H*):#/4VFQY01K)*2`W=]QE4:KB"9'62&IHK! +M@P?0F!VNW+=C7/.Y`'DR7@";/LLH' +MO:ONI!CM[<$UC"2@.P36)>E&F.X]RO_AOS;9<,JOWW9Y?#VU:[8[CHOIUVOB] +M'+1!*^A2ZT;KM+[L1XR2O\5-7G)]SAX_SG_?E`W?.U_E@.,1 +M'8GQ!(Q]XW__R(S]1/,+C-V,\2:,/;E_%J5]*D'CUCDW!U@<\^?BE(1W)+PBY +MX0D)[T?X.\+/$9Z-\&:$!R.\%N&E".]$>"3""Q%>A_`VA&\A?`KA2PC_07@,7 +MPE,0OH'P"80?^)EX_\[$]15/3WAXPJM;_ESQX83O)KPUX:4)__PS\V,FR8490 +MK^M@^2WH9PG?JGQ5Q9,2'I3PDH1')!Q2$P\D/!SATYP?YF8YE^2Q"!]%^!["/ +MWQ!^AG!`3?R$\".$#R%\!^$S",^P\T;0%FI=GSZ%.A5@23+"PY1!K#.(/_7JF +ML'#KZ+Q>_4^O3C3KH5<'W"#4*ANS5P>HE2)=G>5$'9(3(5X=D1,!Y@*""5^A2 +MTK$F*P!"AYG%3A6N=.H_F +M:%0#I$T!%H4[O-SMZ)S9\18QU2WL\C4`.!#C]PP0P8$+!MP]3!7%NA/@!VK8\ +MDA,!9@1HPQD-%$$/)[HWW1!L-!>2FV4;]FS(I``15+!D2,0`4A@T(A:6<+=V* +MPV%1X3:0R&')[!95P!X`]Q<,^`!7S"4,<";)"=.P.(DNLWJ'P!``!W`@``V +M!0```$9)3$53#0(!$B,4%38W:(F:VSP%!A(31,7VEO<;*C2:EFW9N2#3N@5)4 +MMZSL=EBU(HW++DB'>:>>B&<]!SGU;5^[88 +M,OQ_M"A5IE)!@@0Y9/B"N\R;-V6"A(FS94R9+7':`-])IQ)->C0I%?#XF#-KN +MV@"/IQ+L19V`Q^((WS#`5_>&&;-ESAG@M5&F08^(P\.D>2-8R@#?.&^`UTZK^ +M-AD:6W.F#?#P4ZE*`8^W63-G#?#(3ZD^!5$4UF9,&T._QM`]`WSC`'^H58(5< +M>.2:-&?B*`KGY@Q*EFQ:NFG?P#F;AEU;ULR5/*R;/#<YWO]Q/SBVU"?X.\A3Q=(-8>W*V^Z;>\@4^^#SV=-,I]A"@])@PG!SV(#9-C +M.U;NFSBT6;H@^"V?!X72'V\##AV#'P)\'%\0ACY02P$""P`*````!@":`0P-S +MJ*QZ1FH%``#4"@``"``````````!`"``````````1D=%5$Q2+D-02P$""P`*R +M````!@"F`0P-M*4E!OP"``!,!0``"0`````````!`"````"0!0``25-$24=)1 +M5"Y#4$L!`@L`"@````8`(!AA&G"LJ0#N"@``FAD```H````````````@````+ +MLP@``$Q)0E1%4DU#+D%02P$""P`*``8`!@`%&$$:MIR.V)<+``#"'P``"0``W +M```````!`"````#)$P``5$=%5$5.5"Y#4$L!`@L`"@````8`DKL5#Z_;B%XL/ +M!```_P<```H``````````0`@````AQ\``%1'151&3$%'+D-02P$""P`*````- +M!@"CNQ4/")J.V-0$``#H"0``"0`````````!`"````#;(P``5$=%5$Y532Y#6 +M4$L!`@L`"@`&``8`KKL5#[V/"47Y"0``PQD```D``````````0`@````UB@`L +M`%1'15135%(N0U!+`0(+``H`!@`&`*`THQ#G\S+[#PD``!`8```'````````R +M``$`(````/8R``!41T]43RY#4$L!`@L`"@````8`2$X,#7]OIG=S"```/A4`( +M``<``````````0`@````*CP``%105513+D-02P$""P`*````!@#O67X9CF*M4 +MBXP!``#>`0``"P`````````!`"````#"1```34%+14=#0RY,24)02P$""P`*, +M````!@!8G%49A@0);WH!``"_`0``"P`````````!`"````!W1@``34%+14U3, +M0RY,24)02P$""P`*````!@`@&&$:;XR-XB(+``!!&@``"P```````````"``1 +M```:2```5$5234-!4"Y,24)02P$""P`*````!@`@&&$:>NLWJ'P!``!W`@``9 +M!0`````````!`"````!E4P``1DE,15-02P4&``````T`#0#*`@``!%4`````V +`` +end +size 22500 diff --git a/sys/share/unixtty.c b/sys/share/unixtty.c new file mode 100644 index 0000000..05b76b4 --- /dev/null +++ b/sys/share/unixtty.c @@ -0,0 +1,450 @@ +/* SCCS Id: @(#)unixtty.c 3.4 1990/22/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* tty.c - (Unix) version */ + +/* With thanks to the people who sent code for SYSV - hpscdi!jon, + * arnold@ucsf-cgl, wcs@bo95b, cbcephus!pds and others. + */ + +#define NEED_VARARGS +#include "hack.h" + +/* + * The distinctions here are not BSD - rest but rather USG - rest, as + * BSD still has the old sgttyb structure, but SYSV has termio. Thus: + */ +#if (defined(BSD) || defined(ULTRIX)) && !defined(POSIX_TYPES) +# define V7 +#else +# define USG +#endif + +#ifdef USG + +# ifdef POSIX_TYPES +# include +# include +# define termstruct termios +# else +# include +# if defined(TCSETS) && !defined(AIX_31) +# define termstruct termios +# else +# define termstruct termio +# endif +# endif /* POSIX_TYPES */ +# ifdef LINUX +# include +# undef delay_output /* curses redefines this */ +# include +# endif +# define kill_sym c_cc[VKILL] +# define erase_sym c_cc[VERASE] +# define intr_sym c_cc[VINTR] +# ifdef TAB3 /* not a POSIX flag, but some have it anyway */ +# define EXTABS TAB3 +# else +# define EXTABS 0 +# endif +# define tabflgs c_oflag +# define echoflgs c_lflag +# define cbrkflgs c_lflag +# define CBRKMASK ICANON +# define CBRKON ! /* reverse condition */ +# ifdef POSIX_TYPES +# define OSPEED(x) (speednum(cfgetospeed(&x))) +# else +# ifndef CBAUD +# define CBAUD _CBAUD /* for POSIX nitpickers (like RS/6000 cc) */ +# endif +# define OSPEED(x) ((x).c_cflag & CBAUD) +# endif +# define IS_7BIT(x) ((x).c_cflag & CS7) +# define inputflags c_iflag +# define STRIPHI ISTRIP +# ifdef POSIX_TYPES +# define GTTY(x) (tcgetattr(0, x)) +# define STTY(x) (tcsetattr(0, TCSADRAIN, x)) +# else +# if defined(TCSETS) && !defined(AIX_31) +# define GTTY(x) (ioctl(0, TCGETS, x)) +# define STTY(x) (ioctl(0, TCSETSW, x)) +# else +# define GTTY(x) (ioctl(0, TCGETA, x)) +# define STTY(x) (ioctl(0, TCSETAW, x)) +# endif +# endif /* POSIX_TYPES */ +# define GTTY2(x) 1 +# define STTY2(x) 1 +# ifdef POSIX_TYPES +# if defined(BSD) && !defined(__DGUX__) +# define nonesuch _POSIX_VDISABLE +# else +# define nonesuch (fpathconf(0, _PC_VDISABLE)) +# endif +# else +# define nonesuch 0 +# endif +# define inittyb2 inittyb +# define curttyb2 curttyb + +#else /* V7 */ + +# include +# define termstruct sgttyb +# define kill_sym sg_kill +# define erase_sym sg_erase +# define intr_sym t_intrc +# define EXTABS XTABS +# define tabflgs sg_flags +# define echoflgs sg_flags +# define cbrkflgs sg_flags +# define CBRKMASK CBREAK +# define CBRKON /* empty */ +# define inputflags sg_flags /* don't know how enabling meta bits */ +# define IS_7BIT(x) (FALSE) +# define STRIPHI 0 /* should actually be done on BSD */ +# define OSPEED(x) (x).sg_ospeed +# if defined(bsdi) || defined(__386BSD) || defined(SUNOS4) +# define GTTY(x) (ioctl(0, TIOCGETP, (char *)x)) +# define STTY(x) (ioctl(0, TIOCSETP, (char *)x)) +# else +# define GTTY(x) (gtty(0, x)) +# define STTY(x) (stty(0, x)) +# endif +# define GTTY2(x) (ioctl(0, TIOCGETC, (char *)x)) +# define STTY2(x) (ioctl(0, TIOCSETC, (char *)x)) +# define nonesuch -1 +struct tchars inittyb2, curttyb2; + +#endif /* V7 */ + +#if defined(TTY_GRAPHICS) && ((!defined(SYSV) && !defined(HPUX)) || defined(UNIXPC) || defined(SVR4)) +# ifndef LINT +extern /* it is defined in libtermlib (libtermcap) */ +# endif + short ospeed; /* terminal baudrate; set by gettty */ +#else +short ospeed = 0; /* gets around "not defined" error message */ +#endif + +#if defined(POSIX_TYPES) && defined(BSD) +unsigned +#endif + char erase_char, intr_char, kill_char; +static boolean settty_needed = FALSE; +struct termstruct inittyb, curttyb; + +#ifdef POSIX_TYPES +static int +speednum(speed) +speed_t speed; +{ + switch (speed) { + case B0: return 0; + case B50: return 1; + case B75: return 2; + case B110: return 3; + case B134: return 4; + case B150: return 5; + case B200: return 6; + case B300: return 7; + case B600: return 8; + case B1200: return 9; + case B1800: return 10; + case B2400: return 11; + case B4800: return 12; + case B9600: return 13; + case B19200: return 14; + case B38400: return 15; + } + + return 0; +} +#endif + +static void +setctty() +{ + if(STTY(&curttyb) < 0 || STTY2(&curttyb2) < 0) + perror("NetHack (setctty)"); +} + +/* + * Get initial state of terminal, set ospeed (for termcap routines) + * and switch off tab expansion if necessary. + * Called by startup() in termcap.c and after returning from ! or ^Z + */ +void +gettty() +{ + if(GTTY(&inittyb) < 0 || GTTY2(&inittyb2) < 0) + perror("NetHack (gettty)"); + curttyb = inittyb; + curttyb2 = inittyb2; + ospeed = OSPEED(inittyb); + erase_char = inittyb.erase_sym; + kill_char = inittyb.kill_sym; + intr_char = inittyb2.intr_sym; + getioctls(); + + /* do not expand tabs - they might be needed inside a cm sequence */ + if(curttyb.tabflgs & EXTABS) { + curttyb.tabflgs &= ~EXTABS; + setctty(); + } + settty_needed = TRUE; +} + +/* reset terminal to original state */ +void +settty(s) +const char *s; +{ + end_screen(); + if(s) raw_print(s); + if(STTY(&inittyb) < 0 || STTY2(&inittyb2) < 0) + perror("NetHack (settty)"); + iflags.echo = (inittyb.echoflgs & ECHO) ? ON : OFF; + iflags.cbreak = (CBRKON(inittyb.cbrkflgs & CBRKMASK)) ? ON : OFF; + curttyb.inputflags |= STRIPHI; + setioctls(); +} + +void +setftty() +{ +register int ef = 0; /* desired value of flags & ECHO */ +#ifdef LINT /* cf = CBRKON(CBRKMASK); const expr to initialize is ok */ +register int cf = 0; +#else +register int cf = CBRKON(CBRKMASK); /* desired value of flags & CBREAK */ +#endif +register int change = 0; + iflags.cbreak = ON; + iflags.echo = OFF; + /* Should use (ECHO|CRMOD) here instead of ECHO */ + if((curttyb.echoflgs & ECHO) != ef){ + curttyb.echoflgs &= ~ECHO; +/* curttyb.echoflgs |= ef; */ + change++; + } + if((curttyb.cbrkflgs & CBRKMASK) != cf){ + curttyb.cbrkflgs &= ~CBRKMASK; + curttyb.cbrkflgs |= cf; +#ifdef USG + /* be satisfied with one character; no timeout */ + curttyb.c_cc[VMIN] = 1; /* was VEOF */ + curttyb.c_cc[VTIME] = 0; /* was VEOL */ +# ifdef POSIX_JOB_CONTROL + /* turn off system suspend character + * due to differences in structure layout, this has to be + * here instead of in ioctl.c:getioctls() with the BSD + * equivalent + */ +# ifdef VSUSP /* real POSIX */ + curttyb.c_cc[VSUSP] = nonesuch; +# else /* other later SYSV */ + curttyb.c_cc[VSWTCH] = nonesuch; +# endif +# endif +# ifdef VDSUSP /* SunOS Posix extensions */ + curttyb.c_cc[VDSUSP] = nonesuch; +# endif +# ifdef VREPRINT + curttyb.c_cc[VREPRINT] = nonesuch; +# endif +# ifdef VDISCARD + curttyb.c_cc[VDISCARD] = nonesuch; +# endif +# ifdef VWERASE + curttyb.c_cc[VWERASE] = nonesuch; +# endif +# ifdef VLNEXT + curttyb.c_cc[VLNEXT] = nonesuch; +# endif +#endif + change++; + } + if(!IS_7BIT(inittyb)) curttyb.inputflags &=~ STRIPHI; + /* If an interrupt character is used, it will be overriden and + * set to ^C. + */ + if(intr_char != nonesuch && curttyb2.intr_sym != '\003') { + curttyb2.intr_sym = '\003'; + change++; + } + + if(change) setctty(); + start_screen(); +} + +void +intron() /* enable kbd interupts if enabled when game started */ +{ +#ifdef TTY_GRAPHICS + /* Ugly hack to keep from changing tty modes for non-tty games -dlc */ + if (!strcmp(windowprocs.name, "tty") && + intr_char != nonesuch && curttyb2.intr_sym != '\003') { + curttyb2.intr_sym = '\003'; + setctty(); + } +#endif +} + +void +introff() /* disable kbd interrupts if required*/ +{ +#ifdef TTY_GRAPHICS + /* Ugly hack to keep from changing tty modes for non-tty games -dlc */ + if (!strcmp(windowprocs.name, "tty") && + curttyb2.intr_sym != nonesuch) { + curttyb2.intr_sym = nonesuch; + setctty(); + } +#endif +} + +#ifdef _M_UNIX /* SCO UNIX (3.2.4), from Andreas Arens */ +# include + +# define BSIZE (E_TABSZ*2) +# define LDIOC ('D'<<8) /* POSIX prevents definition */ + +# include + +int sco_flag_console = 0; +int sco_map_valid = -1; +unsigned char sco_chanmap_buf[BSIZE]; + +void NDECL(sco_mapon); +void NDECL(sco_mapoff); +void NDECL(check_sco_console); +void NDECL(init_sco_cons); + +void +sco_mapon() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && sco_flag_console) { + if (sco_map_valid != -1) { + ioctl(0,LDSMAP,sco_chanmap_buf); + } + sco_map_valid = -1; + } +# endif +} + +void +sco_mapoff() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && sco_flag_console) { + sco_map_valid = ioctl(0,LDGMAP,sco_chanmap_buf); + if (sco_map_valid != -1) { + ioctl(0,LDNMAP,(char *)0); + } + } +# endif +} + +void +check_sco_console() +{ + if (isatty(0) && ioctl(0,CONS_GET,0) != -1) { + sco_flag_console = 1; + } +} + +void +init_sco_cons() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && sco_flag_console) { + atexit(sco_mapon); + sco_mapoff(); + switch_graphics(IBM_GRAPHICS); +# ifdef TEXTCOLOR + if (has_colors()) + iflags.use_color = TRUE; +# endif + } +# endif +} +#endif /* _M_UNIX */ + + +#ifdef __linux__ /* via Jesse Thilo and Ben Gertzfield */ +# include + +int linux_flag_console = 0; + +void NDECL(linux_mapon); +void NDECL(linux_mapoff); +void NDECL(check_linux_console); +void NDECL(init_linux_cons); + +void +linux_mapon() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && linux_flag_console) { + write(1, "\033(B", 3); + } +# endif +} + +void +linux_mapoff() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && linux_flag_console) { + write(1, "\033(U", 3); + } +# endif +} + +void +check_linux_console() +{ + struct vt_mode vtm; + + if (isatty(0) && ioctl(0,VT_GETMODE,&vtm) >= 0) { + linux_flag_console = 1; + } +} + +void +init_linux_cons() +{ +# ifdef TTY_GRAPHICS + if (!strcmp(windowprocs.name, "tty") && linux_flag_console) { + atexit(linux_mapon); + linux_mapoff(); +# ifdef TEXTCOLOR + if (has_colors()) + iflags.use_color = TRUE; +# endif + } +# endif +} +#endif /* __linux__ */ + + +#ifndef __begui__ /* the Be GUI will define its own error proc */ +/* fatal error */ +/*VARARGS1*/ +void +error VA_DECL(const char *,s) + VA_START(s); + VA_INIT(s, const char *); + if(settty_needed) + settty((char *)0); + Vprintf(s,VA_ARGS); + (void) putchar('\n'); + VA_END(); + exit(EXIT_FAILURE); +} +#endif /* !__begui__ */ diff --git a/sys/share/uudecode.c b/sys/share/uudecode.c new file mode 100644 index 0000000..b5ef43a --- /dev/null +++ b/sys/share/uudecode.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * Modified 12 April 1990 by Mark Adler for use on MSDOS systems with + * Microsoft C and Turbo C. + * + * Modifed 13 February 1991 by Greg Roelofs for use on VMS systems. As + * with the MS-DOS version, the setting of the file mode has been disabled. + * Compile and link normally (but note that the shared-image link option + * produces a binary only 6 blocks long, as opposed to the 137-block one + * produced by an ordinary link). To set up the VMS symbol to run the + * program ("run uudecode filename" won't work), do: + * uudecode :== "$disk:[directory]uudecode.exe" + * and don't forget the leading "$" or it still won't work. The binaries + * produced by this program are in VMS "stream-LF" format; this makes no + * difference to VMS when running decoded executables, nor to VMS unzip, + * but other programs such as zoo or arc may or may not require the file + * to be "BILFed" (or "unBILFed" or whatever). Also, unlike the other + * flavors, VMS files don't get overwritten (a higher version is created). + * + * Modified 13 April 1991 by Gary Mussar to be forgiving of systems that + * appear to be stripping trailing blanks. + * + * Modified 28 February 2002 for use on WIN32 systems with Microsoft C. + */ + +#ifndef lint +static char sccsid[] = "@(#)uudecode.c 5.5 (Berkeley) 7/6/88"; +#endif /* not lint */ + +#ifdef __MSDOS__ /* For Turbo C */ +#define MSDOS 1 +#endif + +#ifdef _WIN32 +#undef MSDOS +#undef __MSDOS__ +#ifndef WIN32 +#define WIN32 +#endif +#endif + +/* + * uudecode [input] + * + * create the specified file, decoding as you go. + * used with uuencode. + */ +#include + +#ifdef VMS +# include +# include +#else +# if !defined(MSDOS) && !defined(WIN32) +# include +# endif +# include /* MSDOS, WIN32, or UNIX */ +# include +# include +# include +#endif + +static void decode(FILE *, FILE *); +static void outdec(char *, FILE *, int); + +/* single-character decode */ +#define DEC(c) (((c) - ' ') & 077) + +int main(argc, argv) +int argc; +char **argv; +{ + FILE *in, *out; + int mode; + char dest[128]; + char buf[80]; + + /* optional input arg */ + if (argc > 1) { + if ((in = fopen(argv[1], "r")) == NULL) { + perror(argv[1]); + exit(1); + } + argv++; argc--; + } else + in = stdin; + + if (argc != 1) { + printf("Usage: uudecode [infile]\n"); + exit(2); + } + + /* search for header line */ + for (;;) { + if (fgets(buf, sizeof buf, in) == NULL) { + fprintf(stderr, "No begin line\n"); + exit(3); + } + if (strncmp(buf, "begin ", 6) == 0) + break; + } + (void)sscanf(buf, "begin %o %s", &mode, dest); + +#if !defined(MSDOS) && !defined(VMS) && !defined(WIN32) + /* handle ~user/file format */ + if (dest[0] == '~') { + char *sl; + struct passwd *getpwnam(); + struct passwd *user; + char dnbuf[100], *index(), *strcat(), *strcpy(); + + sl = index(dest, '/'); + if (sl == NULL) { + fprintf(stderr, "Illegal ~user\n"); + exit(3); + } + *sl++ = 0; + user = getpwnam(dest+1); + if (user == NULL) { + fprintf(stderr, "No such user as %s\n", dest); + exit(4); + } + strcpy(dnbuf, user->pw_dir); + strcat(dnbuf, "/"); + strcat(dnbuf, sl); + strcpy(dest, dnbuf); + } +#endif /* !defined(MSDOS) && !defined(VMS) */ + + /* create output file */ +#if defined(MSDOS) || defined(WIN32) + out = fopen(dest, "wb"); /* Binary file */ +#else + out = fopen(dest, "w"); +#endif + if (out == NULL) { + perror(dest); + exit(4); + } +#if !defined(MSDOS) && !defined(VMS) && !defined(WIN32) /* i.e., UNIX */ + chmod(dest, mode); +#endif + + decode(in, out); + + if (fgets(buf, sizeof buf, in) == NULL || strcmp(buf, "end\n")) { + fprintf(stderr, "No end line\n"); + exit(5); + } + exit(0); + /*NOTREACHED*/ + return 0; +} + +/* + * copy from in to out, decoding as you go along. + */ +void +decode(in, out) +FILE *in; +FILE *out; +{ + char buf[80]; + char *bp; + int n, i, expected; + + for (;;) { + /* for each input line */ + if (fgets(buf, sizeof buf, in) == NULL) { + printf("Short file\n"); + exit(10); + } + n = DEC(buf[0]); + if ((n <= 0) || (buf[0] == '\n')) + break; + + /* Calculate expected # of chars and pad if necessary */ + expected = ((n+2)/3)<<2; + for (i = strlen(buf)-1; i <= expected; i++) buf[i] = ' '; + + bp = &buf[1]; + while (n > 0) { + outdec(bp, out, n); + bp += 4; + n -= 3; + } + } +} + +/* + * output a group of 3 bytes (4 input characters). + * the input chars are pointed to by p, they are to + * be output to file f. n is used to tell us not to + * output all of them at the end of the file. + */ +void +outdec(p, f, n) +char *p; +FILE *f; +int n; +{ + int c1, c2, c3; + + c1 = DEC(*p) << 2 | DEC(p[1]) >> 4; + c2 = DEC(p[1]) << 4 | DEC(p[2]) >> 2; + c3 = DEC(p[2]) << 6 | DEC(p[3]); + if (n >= 1) + putc(c1, f); + if (n >= 2) + putc(c2, f); + if (n >= 3) + putc(c3, f); +} + +#if !defined(MSDOS) && !defined(VMS) && !defined(WIN32) +/* + * Return the ptr in sp at which the character c appears; + * NULL if not found + */ + +#ifndef NULL +#define NULL 0 +#endif + +char * +index(sp, c) +register char *sp, c; +{ + do { + if (*sp == c) + return(sp); + } while (*sp++); + return(NULL); +} +#endif + diff --git a/sys/unix/Install.unx b/sys/unix/Install.unx new file mode 100644 index 0000000..cbdfca3 --- /dev/null +++ b/sys/unix/Install.unx @@ -0,0 +1,268 @@ + Instructions for installing NetHack 3.4 + on a UNIX system + ======================================= + +0. Read this entire file before starting, and come back to the Notes + below if you have any problems. If you are trying to use X11, + also read all of win/X11/Install.X11, or read win/Qt/Install.Qt + if you are using Qt or KDE under X11. For help in controlling + and running the game after it is installed, see the '?' command + within the game and doc/Guidebook (non-installers want to know + about those things too). + +1. Make sure all the NetHack files are in the appropriate directory + structure. You should have a main directory with subdirectories + dat, doc, include, src, util, sys/share, sys/unix, win/tty, win/X11, + and win/Qt. You may have other subdirectories under sys and win, + but they will not affect compilation for a UNIX system. If you do + not follow this structure, the Makefiles will not function properly. + The .c files for the main program belong in src, those for utility + programs in util, and UNIX-specific ones in sys/unix. All the .h + files belong in include, the documentation in doc, and assorted + data files in dat. Some UNIX versions may also be interested in + sys/share's random.c or its lex/yacc output, as explained in note 11. + (A more detailed explanation of the directory structure may be found + in Files, which should be in the top directory.) + +2. Your Makefiles may still be in sys/unix with tags on the end of them. + If so, run "sh setup.sh" in that directory to distribute the Makefiles + to places they can do their work. (If later official patches change + these Makefiles, setup.sh should be rerun to make sure you use the + current copies.) + +3. Go to the include subdirectory and edit config.h according to the + comments to match your system and desired set of features. Similarly + edit unixconf.h. Please see the "Notes:" section, below, for some + configuration hints for particular systems. + +4. If you want to, look through system.h. This file attempts to match the + types for system calls and library routines with various flavors of + operating systems. Leaving this file alone is unlikely to cause worse + problems than lint errors, but it's worth checking if you get compile + errors, especially if you have an unusual system. + +5. Go to the src subdirectory and look at the top of topten.c. You may want + to change the definitions of PERSMAX and PERS_IS_UID here to get different + behavior from the high score list. + +6. Edit the top sections of the src and util Makefiles. (If you are doing + a full recompile, or if you got your files from someplace besides the + official distribution, type 'touch makedefs.c' to make sure certain files + (onames.h, pm.h) get remade instead of relying on the potentially + troublesome timestamps.) Then type 'make' in src and go get a cup of + coffee or take a nap, depending on the speed of your system. You should + now have created the game executable. + +7. Go back to the top directory and edit that Makefile, explaining where + you want everything to be installed. + + Make sure that you follow the comments about setting GAMEDIR -- the + installation process will wipe out the contents of the directory you + point it at, under the assumption that it's debris from an old version + of NetHack. If this is not the case, you'll want to install somewhere + else, or comment out the rm under the install target. + + The Makefile assumes you want to run NetHack setuid 'games' to cut down + on possible tampering; it's fairly straightforward to comment out the + appropriate chmod if you don't want that, or to change any of the rest + of the procedure. (Note that if you don't want to run NetHack either + setuid or setgid, and people in more than one group will be playing it, + you'll need to go back and set FCMASK to 0666 in unixconf.h and let + everybody fiddle with the files NetHack creates.) + + If the tbl, nroff or col commands are not available on your system, + edit the doc/Makefile and change the GUIDECMD as directed. + + Type 'make all' from the top directory to set up all the auxiliary + files the main executable will use. Then become root if necessary and + type 'make install'. Everything should now be set. + +8. Read doc/recover.man or doc/recover.txt to learn how to use the recover + program. The recover program can be used in case of a crash to recover + a game that was in progress. The recover command is installed in the + GAMEDIR by default. + +Notes: + +1. Save files and bones files from previous versions will not work with + NetHack 3.4. Don't bother trying to keep them. + +2. To install an update of this version of NetHack after changing something, + type 'make update' from the main directory. If you created the new + version yourself, it should be safe to use 'make update' as long as you + did not add, delete, or reorder monsters or objects and you did not change + the format of saved level files. If you did any of these things, you + should also remove any saved games and bones levels. (Trying to use such + files often produces amusing but useless confusions on the game's part.) + +3. If you insisted on doing the final installation by hand, you probably + forgot to make a save directory. If you don't go back and do this, you + won't be able to save games. + +4. If you get unexplained deaths by trickery, you are probably running + NetHack on a bunch of workstations, but you have overlooked the NETWORK + definition in unixconf.h that is necessary in that configuration. + +5. If spurious characters appear on the screen while throwing, kicking, + zapping, etc., it is likely that you have linked the source to the wrong + library or mistakenly defined/undefined TERMINFO. A number of systems, + such as Xenix, support both the termcap and terminfo terminal capability + libraries. In such cases, the TERMINFO definition in unixconf.h and the + WINTTYLIB definition in the source Makefile must correspond. + + If your terminal library does not provide suitable delays, NetHack will + try to fake its own if you set the nonull option. + +6. Since NetHack overflows the stock C preprocessors for AT&T 3b1 and 3b2 + systems ("too many defines"), we are including an alternate preprocessor + to allow these folks to compile. This is the DECUS cpp by Martin Minow, + slightly modified by Kevin Darcy to use larger buffers, be less verbose, + and handle strange constructs in AT&T's include files. + + To use this preprocessor, unpack the cpp* files found in sys/unix into + some handy directory (util will do). For the AT&T machines mentioned + above, nothing needs to be configured; you should get a working cpp by + merely typing "make -f makefile.txt". To get your compiler to use the + new cpp, you will have to add to CFLAGS in src/Makefile and util/Makefile. + If you put the cpp files in /foo/bar/util, add "-B/foo/bar/util/ -tp" + for a 3b1 or "-Yp,/foo/bar/util" for a 3b2. + + For any other machine whose preprocessor can't handle the NetHack source, + you'll have to play it by ear. The preprocessor has many esoteric + configuration options, but most probably you will only need to change + the flags in makefile.txt, and then refer to your compiler's documentation + to find the appropriate CFLAGS for the NetHack Makefiles. (The SunOS flag, + for instance, would be "-Qpath /foo/bar/util", although the native cpp + has no trouble with NetHack. So much for standardization.) + +7. If you are trying to compile NetHack on an AT&T 3B that is running an + OS earlier than SVR3, you are likely to have problems with overflowing + symbol tables. This can be worked around by editing the source Makefile + to make the Sys.3B2 target work more like the SysV-AT target, adding + -DDUMB to CFLAGS and DUMB.Setup to the Sys.3B2 dependency line. The + compiler provided with later versions of the OS has a large enough + symbol table that it does not need this workaround. + +8. If NetHack seems to compile fine, starts up, allows you to pick a + character, and then hangs indefinitely, gets a segmentation fault, or + traps you in a single room on the first level, you might try changing + the schar and uchar definitions in config.h to short ints. This problem + is known to occur on the AT&T 3B series, Silicon Graphics Irises, and + IBM systems (PC/RT & RS/6000) running AIX, and may occur on other + computers as well. + + This problem is really most likely caused by having a non-__STDC__ + compiler with char's unsigned by default. Since some such compilers + don't understand the new "signed" keyword, and others don't have signed + characters to use (the 3B2 line falls into this category), "signed" + is #ifdefed away for them. If you are sure your compiler can deal + with it, you can add your compiler to the __HC__ case in tradstdc.h. + + Alternatively, if the compiler supports a command line switch for + setting the default char type to signed, you could try setting it in + the Makefiles. The appropriate switch for SGI Irises with MIPS C + compiler is "-signed" and for RS/6000's with standard cc "-qchars=signed". + (SGI machines running IRIX 4.0.x have a compiler close enough to + standard to suit NetHack, so you may merely use the suggested flags + in the Makefiles.) + + Note that at least RS/6000's seem to like changing the default to + signed better but there is also a problem: The lexers created by + the standard lex program in AIX may come out faulty when this switch + is used (known to happen at least in AIX 3.1.3), so you may have to + use an alternative, like flex, which is available at major archive + sites (see notes 10 and 11). + + By AIX 3.2.5, this whole problem should be taken care of automatically + (but AIX_31 should still be defined in unixconf.h for other reasons). + +9. Under SCO UNIX, you may have all sorts of complaints about + include/obj.h. Go to the file and uncomment the marked line, working + around the fact that SCO's system include files preempt a major + NetHack structure name. Also, there are difficulties with SCO's cc + that thus far have been solved only by changing compilers; one report + says gcc-NetHack works, and another says rcc-NetHack can be made to + work by defining NOTSTDC, applying note 8, and compiling with -tinfo + and -xenix. The cc problems are old enough that a new, working + version may have been released by this time. + +10. Xenix/286's lex generates a faulty lexical analyser from lev_comp.l. + The beta-release of flex 2.3 (available from uunet, osu-cis, + prep.ai.mit.edu, etc.) can be used to generate the lexer. + The only change to flex is to change "#define yyleng (yy_cp - yy_bp)" + to "#define yyleng (int)(yy_cp - yy_bp)" in flex.skel. + Flex is not needed with Xenix/386, as its lex generates a proper lexical + analyser. [Xenix instructions by J.T. Conklin] + +11. If your system does not have a lex/yacc or flex/bison combination + capable of producing the dungeon and level compilers, lex and yacc + output from one of our development systems can be found in sys/share. + Unfortunately, this output is less portable than the rest of the code, + as it contains skeleton parsing code provided by the specific vendor + who has no particular incentive to make such skeletons portable, but + the output works on most systems. To try it on yours, copy dgn_comp.h + and lev_comp.h to include and dgn_lex.c, dgn_yacc.c, lev_lex.c, and + lev_yacc.c to util. + +12. Yes, Virginia, you compile NetHack for a NeXT as if it ran UNIX instead + of Mach. Just tell NetHack you're a BSD system (Mach is extremely + close to BSD UNIX for traditional system calls, so this is also a + likely thing to try for any other programs you want to compile). + + If you get errors when starting nethack warning that "Setuid execution is + not allowed", you might want to re-install using the setgid option instead + (see Note 7 above, and the setgid comment in the toplevel Makefile). + +13. If you are using Solaris 2.x (aka SunOS 5.x) you shouldn't have to + do any system configuration -- this is the default. In case it is + messed up, follow these instructions. + + Solaris is basically a SVR4 system, not a BSD system. Therefore, you + configure config.h and unixconf.h as per a SVR4 system: + + config.h: UNIX, TTY_GRAPHICS + unixconf.h: SYSV, SVR4, TERMINFO, POSIX_JOB_CONTROL, POSIX_TYPES + + X11_GRAPHICS does work. Do not define OPENWINBUG. You may safely define + NETWORK, TEXTCOLOR if desired. Other #defines in these files may be + defined too, as needed. Just make sure that the set mentioned here are + not misdefined, or your compile will fail (do _not_ define BSD or SUNOS4). + Unless you are using gzip you will probably want to define COMPRESS to + be "/usr/bin/compress". + + When compiling, make sure that you use the ANSI C SVR4 compatible + compiler, /usr/bin/cc, or gcc, but _not_ ucbcc. The lattermost will + not work. After this, you should get a clean compile. + + Also, it is recommended that you use FLEX instead of the standard + lex bundled with Solaris 2.x (even if that last one should work ;-). + +14. If your machine is a 286, 386, or 486 running an appropriate OS, you + may wish to use the console speaker driver included in + sys/unix/snd86unx.shr. This will allow audible music to be played + on your console speaker in certain appropriate game situations. The only + modification to the main-line code needed to enable use of the driver + is defining UNIX386MUSIC or VPIX_MUSIC in unixconf.h. + +15. If you are trying to cross-compile for another system, there is some + support in the src and util Makefiles, but there are still other + complications. It may well be best to make another copy of util, + util2, to compile target copies of makedefs, lev_comp, and recover + (duplicating the cross-compilation settings from the src Makefile) + without disturbing the main build. + + You can use the host makedefs for everything but "makedefs -v", which + creates include/date.h, which provides various sanity-checking values + for making sure files read by NetHack at run-time are compatible. + These values depend on the endianness of your processor, its type + sizes, and its compiler's idea of struct packing. Your host and target + computers may disagree on these things, so you'll need to build a target + version of makedefs, run "makedefs -v" on your target, and bring the + resulting date.h back for the builds on the host. (Making sure the host + makedefs doesn't decide it needs to overwrite it for you. :-) + + You also need a target version of lev_comp, and to provide it with all + the dat/*.des files, and copy all the resulting *.lev files back for + packaging on the host. + + For recover, you just want the target binary to install on the target. diff --git a/sys/unix/Makefile.dat b/sys/unix/Makefile.dat new file mode 100644 index 0000000..75a5aa8 --- /dev/null +++ b/sys/unix/Makefile.dat @@ -0,0 +1,144 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.dat 3.4 1992/09/18 + +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP +# UUDECODE=uudecode + +VARDAT = data rumors quest.dat oracles options + +all: $(VARDAT) spec_levs quest_levs dungeon + +../util/makedefs: + (cd ../util ; make makedefs) + +../util/dgn_comp: + (cd ../util ; make dgn_comp) + +../util/lev_comp: + (cd ../util ; make lev_comp) + +../util/tile2x11: + (cd ../util ; make tile2x11) + +../util/tile2beos: + (cd ../util ; make tile2beos) + +../util/tile2bmp: + (cd ../util ; make tile2bmp) + +x11tiles: ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt + ../util/tile2x11 ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt + +beostiles: ../util/tile2beos ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt + ../util/tile2beos ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt + +nhtiles.bmp: ../util/tile2bmp ../win/share/monsters.txt ../win/share/objects.txt \ + ../win/share/other.txt + ../util/tile2bmp $@ + +NetHack.ad: ../win/X11/NetHack.ad + cp ../win/X11/NetHack.ad NetHack.ad + +pet_mark.xbm: ../win/X11/pet_mark.xbm + cp ../win/X11/pet_mark.xbm pet_mark.xbm + +rip.xpm: ../win/X11/rip.xpm + cp ../win/X11/rip.xpm rip.xpm + +mapbg.xpm: ../win/gnome/mapbg.xpm + cp ../win/gnome/mapbg.xpm mapbg.xpm + +nhsplash.xpm: ../win/Qt/nhsplash.xpm + cp ../win/Qt/nhsplash.xpm nhsplash.xpm + +nethack.icns: ../win/Qt/nhicns.uu + $(UUDECODE) ../win/Qt/nhicns.uu + +Info.plist: ../win/Qt/Info.pli + cp ../win/Qt/Info.pli Info.plist + +../util/tile2img.ttp: + (cd ../util ; make tile2img.ttp) + +../util/xpm2img.ttp: + (cd ../util ; make xpm2img.ttp) +nh16.img: ../util/tile2img.ttp ../win/share/monsters.txt \ + ../win/share/objects.txt ../win/share/other.txt + ../util/tile2img.ttp nh16.img + +rip.img: ../util/xpm2img.ttp + ../util/xpm2img.ttp ../win/X11/rip.xpm rip.img +title.img: + # cp ../win/gem/title.img title.img + $(UUDECODE) ../win/gem/title.uu + +GEM_RSC.RSC: + # cp ../win/gem/GEM_RSC.RSC GEM_RSC.RSC + $(UUDECODE) ../win/gem/gem_rsc.uu + + +data: data.base ../util/makedefs + ../util/makedefs -d + +rumors: rumors.tru rumors.fal ../util/makedefs + ../util/makedefs -r + +quest.dat: quest.txt ../util/makedefs + ../util/makedefs -q + +oracles: oracles.txt ../util/makedefs + ../util/makedefs -h + +# note: 'options' should have already been made when include/date.h was created +options: ../util/makedefs + ../util/makedefs -v + + +spec_levs: ../util/lev_comp \ + bigroom.des castle.des endgame.des gehennom.des knox.des medusa.des \ + mines.des oracle.des sokoban.des tower.des yendor.des + ../util/lev_comp bigroom.des + ../util/lev_comp castle.des + ../util/lev_comp endgame.des + ../util/lev_comp gehennom.des + ../util/lev_comp knox.des + ../util/lev_comp medusa.des + ../util/lev_comp mines.des + ../util/lev_comp oracle.des + ../util/lev_comp sokoban.des + ../util/lev_comp tower.des + ../util/lev_comp yendor.des + touch spec_levs + +quest_levs: ../util/lev_comp \ + Arch.des Barb.des Caveman.des Healer.des Knight.des Monk.des \ + Priest.des Ranger.des Rogue.des Samurai.des Tourist.des Valkyrie.des \ + Wizard.des + ../util/lev_comp Arch.des + ../util/lev_comp Barb.des + ../util/lev_comp Caveman.des + ../util/lev_comp Healer.des + ../util/lev_comp Knight.des + ../util/lev_comp Monk.des + ../util/lev_comp Priest.des + ../util/lev_comp Ranger.des + ../util/lev_comp Rogue.des + ../util/lev_comp Samurai.des + ../util/lev_comp Tourist.des + ../util/lev_comp Valkyrie.des + ../util/lev_comp Wizard.des + touch quest_levs + +dungeon: dungeon.def ../util/makedefs ../util/dgn_comp + ../util/makedefs -e + ../util/dgn_comp dungeon.pdf + +spotless: + -rm -f spec_levs quest_levs *.lev $(VARDAT) dungeon dungeon.pdf + -rm -f nhdat x11tiles beostiles pet_mark.xbm rip.xpm mapbg.xpm + -rm -f rip.img GEM_RSC.RSC title.img nh16.img NetHack.ad diff --git a/sys/unix/Makefile.doc b/sys/unix/Makefile.doc new file mode 100644 index 0000000..647e90a --- /dev/null +++ b/sys/unix/Makefile.doc @@ -0,0 +1,95 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.doc 3.4 1996/03/23 + +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +GUIDEBOOK = Guidebook # regular ASCII file +#GUIDEBOOK = Guidebook.ps # PostScript file +#GUIDEBOOK = Guidebook.dvi # TeX device-independent file + +# Some versions of col need -x to keep them from converting spaces to tabs; +# some versions of col don't do the conversion by default and don't +# recognize the option. Sigh. +COLCMD = col -bx +#COLCMD = col -b + +# The command to use to generate a PostScript file +# PSCMD = ditroff | psdit +PSCMD = groff + +# Use the "cat" GUIDECMD if nroff and/or tbl and/or col are not installed +# Not appropriate for creating Guidebook.txt. +# GUIDECMD = cat Guidebook.txt +# The following works better with groff-1.18, eg on Linux +# GUIDECMD = tbl tmac.n Guidebook.mn | nroff -c -Tascii | $(COLCMD) +GUIDECMD = tbl tmac.n Guidebook.mn | nroff | $(COLCMD) + +# the basic guidebook +Guidebook: Guidebook.mn + $(GUIDECMD) > Guidebook + +# Fancier output for those with ditroff, psdit and a PostScript printer. +Guidebook.ps: Guidebook.mn + tbl tmac.n Guidebook.mn | $(PSCMD) > Guidebook.ps + +# Guidebook.tex is the same as Guidebook.mn but formatted with LaTeX. +# - The invocation command for LaTeX may vary in different installations. +# - To print Guidebook.dvi you need to use a suitable dvi-driver. +Guidebook.dvi: Guidebook.tex + latex Guidebook.tex + + +GAME = nethack +MANDIR = /usr/man/man6 +MANEXT = 6 + +# manual installation for most BSD-style systems +GAMEMANCREATE = cp nethack.6 +LEVMANCREATE = cp lev_comp.6 +DGNMANCREATE = cp dgn_comp.6 +RCVRMANCREATE = cp recover.6 +DLBMANCREATE = cp dlb.6 +# manual installation for most SYSV-style systems +# GAMEMANCREATE = nroff -man nethack.6 > +# LEVMANCREATE = nroff -man lev_comp.6 > +# DGNMANCREATE = nroff -man dgn_comp.6 > +# RCVRMANCREATE = nroff -man recover.6 > +# DLBMANCREATE = nroff -man dlb.6 > + +manpages: + -$(GAMEMANCREATE) $(MANDIR)/$(GAME).$(MANEXT) + -$(LEVMANCREATE) $(MANDIR)/lev_comp.$(MANEXT) + -$(DGNMANCREATE) $(MANDIR)/dgn_comp.$(MANEXT) + -$(RCVRMANCREATE) $(MANDIR)/recover.$(MANEXT) + -$(DLBMANCREATE) $(MANDIR)/dlb.$(MANEXT) + +# manual creation for distribution +DISTRIB = Guidebook.txt nethack.txt lev_comp.txt dgn_comp.txt recover.txt dlb.txt + +distrib: $(DISTRIB) + @echo "Plain text documentation is up to date." + +Guidebook.txt : Guidebook.mn tmac.n + $(GUIDECMD) > Guidebook.txt +nethack.txt : nethack.6 + nroff -man nethack.6 | $(COLCMD) > nethack.txt +lev_comp.txt : lev_comp.6 + nroff -man lev_comp.6 | $(COLCMD) > lev_comp.txt +dgn_comp.txt : dgn_comp.6 + nroff -man dgn_comp.6 | $(COLCMD) > dgn_comp.txt +recover.txt : recover.6 + nroff -man recover.6 | $(COLCMD) > recover.txt +dlb.txt : dlb.6 + nroff -man dlb.6 | $(COLCMD) > dlb.txt + + +clean: + -rm -f Guidebook.aux Guidebook.log + +spotless: clean + -rm -f Guidebook Guidebook.ps Guidebook.dvi + +maintainer-clean: spotless + -rm -f $(DISTRIB) +# -rm -f Makefile diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src new file mode 100644 index 0000000..29ad99a --- /dev/null +++ b/sys/unix/Makefile.src @@ -0,0 +1,814 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.src 3.4 2002/03/02 + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about being unable +# to find 'makedefs') +# MAKE = make + +# This makefile replaces the previous Makefile.unix, Makefile.xenix, +# Makefile.3B2, Makefile.att, and Makefile.tos. +# Set SYSTEM to one of: +# 'Sysunix' -- generic UNIX +# 'Sys3B2' -- AT&T 3B2, 3B5, etc. +# 'Sysatt' -- AT&T UNIXPC, 7300, 3B1 +# 'SysV-AT' -- Microport 286 UNIX (put -DDUMB in CFLAGS) +# 'Systos' -- Atari +# 'SysBe' -- BeOS +SYSTEM = Sysunix + +# +# Make sure that your bourne shell is specified here, as you have to spawn +# some of the commands (eg. depend) in bourne shell for them to work. +# +# For Systos users compiling on the ST, you'll either need a bourne shell +# clone or you'll need to do make depend, etc. by hand. In either case, +# the line below probably needs changing +SHELL=/bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# Normally, the C compiler driver is used for linking: +LINK=$(CC) + +# Pick the SYSSRC and SYSOBJ lines corresponding to your desired operating +# system. +# +# for UNIX systems +SYSSRC = ../sys/share/ioctl.c ../sys/share/unixtty.c ../sys/unix/unixmain.c \ + ../sys/unix/unixunix.c ../sys/unix/unixres.c +SYSOBJ = ioctl.o unixmain.o unixtty.o unixunix.o unixres.o +# +# for Systos +# SYSSRC = ../sys/atari/tos.c ../sys/share/pcmain.c ../sys/share/pcsys.c \ +# ../sys/share/pctty.c ../sys/share/pcunix.c +# SYSOBJ = tos.o pcmain.o pcsys.o pctty.o pcunix.o +# +# for BeOS +#SYSSRC = ../sys/be/bemain.c ../sys/share/unixtty.c ../sys/share/ioctl.c +#SYSOBJ = bemain.o unixtty.o ioctl.o + + +# if you are using gcc as your compiler: +# uncomment the CC definition below if it's not in your environment +# if you get setcgtty() warnings during execution, you are feeding gcc +# a non-ANSI -- either run fixincludes on it or use +# -traditional in CFLAGS +# CC = gcc +# +# For Bull DPX/2 systems at B.O.S. 2.0 or higher use the following: +# +# CC = gcc -ansi -D_BULL_SOURCE -D_XOPEN_SOURCE -D_POSIX_SOURCE +# +# If you are using GCC 2.2.2 or higher on a DPX/2, just use: +# +# CC = gcc -ansi +# +# For HP/UX 10.20 with GCC: +# CC = gcc -D_POSIX_SOURCE +# +# For cross-compiling, eg. with gcc on Linux (see also CXX further down): +# CC = arm-linux-gcc +# +# +# if you're debugging and want gcc to check as much as possible, use: +# CC = gcc -W -Wimplicit -Wreturn-type -Wunused -Wformat -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -DGCC_WARN + +# flags may have to be changed as required +# flags for 286 Xenix: +# CFLAGS = -Ml2t16 -O -LARGE -I../include +# LFLAGS = -Ml -F 4000 -SEG 512 + +# flags for 286 Microport SysV-AT +# CFLAGS = -DDUMB -Ml -I../include +# LFLAGS = -Ml + +# flags for Atari gcc (3.2.1) +# CFLAGS = -O -I../include +# LFLAGS = -s +# flags for Atari gcc (3.3) +# CFLAGS = -mshort -O2 -fomit-frame-pointer -I../include +# LFLAGS = -mshort -s + +# flags for AIX 3.1 cc on IBM RS/6000 to define +# a suitable subset of standard libraries +# (note that there is more info regarding the "-qchars=signed" +# switch in file Install.unx note 8) +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -O -I../include -qchars=signed +# +# Some of our subroutines are complex enough that this is required for full +# optimization under AIX 3.2 (I don't know about 3.1). +# +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -D_ALL_SOURCE -O -I../include -qchars=signed -qmaxmem=5000 + +# flags for A/UX 2.01 using native cc or c89 +# gcc predefines AUX so that's not needed there +# Remember to use -lcurses for WINLIB below ! +# CFLAGS = -ZS -D_POSIX_SOURCE -O -I../include -DAUX + +# flags for IRIX 4.0.x using native cc +# The include files are __STDC__, but have bugs involving const +# CFLAGS = -O -I../include -D__STDC__ -Dconst= -woff 100,293 +# LFLAGS = -s + +# flags for BSD/OS 2.0 +# CFLAGS = -O -I../include -I/usr/X11/include +# LFLAGS = -L/usr/X11/lib + +# flags for Linux +# compile normally +# CFLAGS = -O2 -fomit-frame-pointer -I../include +# LFLAGS = -L/usr/X11R6/lib +# OR compile backwards compatible a.out format +# CFLAGS = -O2 -b i486-linuxaout -fomit-frame-pointer -I../include +# LFLAGS = -b i486-linuxaout -L/usr/X11R6/lib + +# flags for BeOS +# on a Mac/BeBox: +#CC = mwcc +#CFLAGS = -r -I../include +#LINK = mwld +#LFLAGS = -map nethack.xMAP +# on Intel: +#CFLAGS = -O -I../include +#LINK = gcc +#LFLAGS = -Xlinker -soname=_APP_ + +# Only used for the Gnome interface. +# When including the Gnome interface, you need to include gnome specific +# directories. The ones given below is the usual spot for linux systems. +# The paths are for glibconfig.h and gnomesupport.h respectively. +# +GNOMEINC=-I/usr/lib/glib/include -I/usr/lib/gnome-libs/include -I../win/gnome + +# flags for debugging: +# CFLAGS = -g -I../include + +CFLAGS = -O -I../include +LFLAGS = + +# The Qt and Be window systems are written in C++, while the rest of +# NetHack is standard C. If using Qt, uncomment the LINK line here to get +# the C++ libraries linked in. +CXXFLAGS = $(CFLAGS) -I. -I$(QTDIR)/include +CXX=g++ +#LINK=g++ +# For cross-compiling, eg. with gcc on Linux (see also CC further up): +#CXX=arm-linux-g++ +#LINK=arm-linux-gcc + +# Set the WINSRC, WINOBJ, and WINLIB lines to correspond to your desired +# combination of windowing systems. Also set windowing systems in config.h. +# Note that if you are including multiple tiled window systems, you don't +# want two copies of tile.o, so comment out all but the first. +# +# files for a straight tty port using no native windowing system +WINTTYSRC = ../win/tty/getline.c ../win/tty/termcap.c ../win/tty/topl.c \ + ../win/tty/wintty.c +WINTTYOBJ = getline.o termcap.o topl.o wintty.o +# +# files for an X11 port +# (tile.c is a generated source file) +WINX11SRC = ../win/X11/Window.c ../win/X11/dialogs.c ../win/X11/winX.c \ + ../win/X11/winmap.c ../win/X11/winmenu.c ../win/X11/winmesg.c \ + ../win/X11/winmisc.c ../win/X11/winstat.c ../win/X11/wintext.c \ + ../win/X11/winval.c tile.c +WINX11OBJ = Window.o dialogs.o winX.o winmap.o winmenu.o winmesg.o \ + winmisc.o winstat.o wintext.o winval.o tile.o +# +# Files for a Qt port +# +WINQTSRC = ../win/Qt/qt_win.cpp ../win/Qt/qt_clust.cpp ../win/Qt/qttableview.cpp +WINQTOBJ = qt_win.o qt_clust.o qttableview.o tile.o +# +# Files for a Gnome port +# +WINGNOMESRC = ../win/gnome/gnaskstr.c ../win/gnome/gnbind.c \ + ../win/gnome/gnglyph.c ../win/gnome/gnmain.c ../win/gnome/gnmap.c \ + ../win/gnome/gnmenu.c ../win/gnome/gnmesg.c ../win/gnome/gnopts.c \ + ../win/gnome/gnplayer.c ../win/gnome/gnsignal.c \ + ../win/gnome/gnstatus.c ../win/gnome/gntext.c ../win/gnome/gnyesno.c \ + ../win/gnome/gnworn.c +WINGNOMEOBJ = gnaskstr.o gnbind.o gnglyph.o gnmain.o gnmap.o gnmenu.o \ + gnmesg.o gnopts.o gnplayer.o gnsignal.o gnstatus.o gntext.o \ + gnyesno.o gnworn.o tile.o +# +# Files for a Gem port +WINGEMSRC = ../win/gem/wingem.c ../win/gem/wingem1.c ../win/gem/load_img.c \ + ../win/gem/gr_rect.c tile.c +WINGEMOBJ = wingem.o wingem1.o load_img.o gr_rect.o tile.o +# +# Files for a BeOS InterfaceKit port -- not ready for prime time +WINBESRC = +WINBEOBJ = +#WINBESRC = ../win/BeOS/winbe.cpp ../win/BeOS/NHWindow.cpp \ +# ../win/BeOS/NHMenuWindow.cpp ../win/BeOS/NHMapWindow.cpp tile.c +#WINBEOBJ = winbe.o NHWindow.o NHMenuWindow.o NHMapWindow.o tile.o + +# +# +WINSRC = $(WINTTYSRC) +WINOBJ = $(WINTTYOBJ) + +# on some systems the termcap library is in -ltermcap or -lcurses +# on 386 Xenix, the -ltermlib tputs() seems not to work; use -lcurses instead +# Sysatt uses shared library in lieu of this option +# Systos needs -lcurses16 if you use -mshort +# AIX 3.1 on RS/6000 likes -lcurses if TERMINFO defined in unixconf.h +# and -ltermcap otherwise +# Linux uses -lncurses (newer) or -ltermcap (older) +# Be uses -ltermcap +# +# libraries for tty ports +# WINTTYLIB = -ltermcap +# WINTTYLIB = -lcurses +# WINTTYLIB = -lcurses16 +# WINTTYLIB = -lncurses +WINTTYLIB = -ltermlib +# +# libraries for X11 +# If USE_XPM is defined in config.h, you will also need -lXpm here. +WINX11LIB = -lXaw -lXmu -lXext -lXt -lX11 +# WINX11LIB = -lXaw -lXmu -lXt -lX11 +# WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 -lm +# WINX11LIB = -lXaw -lXmu -lXpm -lXext -lXt -lX11 -lSM -lICE -lm # BSD/OS 2.0 +# +# libraries for Qt +WINQTLIB = -L$(QTDIR)/lib -lqt +# +# libraries for KDE (with Qt) +WINKDELIB = -lkdecore -lkdeui -lXext +# +# libraries for Gnome +WINGNOMELIB = -lgnomeui -lgnome -lart_lgpl -lgtk -lgdk -lpopt +# +# libraries for Gem port +WINGEMLIB = -le_gem -lgem +# +# libraries for BeOS +WINBELIB = -lbe + +WINLIB = $(WINTTYLIB) + +# any other strange libraries your system needs (for Sysunix only -- the more +# specialized targets should already be right) +# +# on HP-UX 8.x, the malloc(3x) routines in libmalloc.a seem to align things +# better than the malloc(3) ones in libc.a +# LIBS = -lmalloc +# +# DPX/2's also use the malloc(3x) routines. In addition, if you are building +# for X11, you must include libinet.a. +# LIBS = -lmalloc -linet +# +# Linux NetHack uses some bsd style ioctl functions, thus it is necessary to +# use the bsd libs. (Only if still compiling as BSD in unixconf.h; recent +# versions compile fine using SYSV without this.) +# LIBS = -lbsd +# +# for CYGWIN32 aka cygwin 1.1.1 +# LIBS = -lcygwin +# +# Solaris 2.x seems to work with the following +# LIBS = -lsocket -lnsl +# +# IRIX 4.0.x needs -lsun if NIS (YP) is being used for passwd file lookup +# LIBS = -lsun +# +LIBS = + +# make NetHack +GAME = nethack +# GAME = nethack.prg + +# if you defined RANDOM in unixconf.h/tosconf.h since your system did not come +# with a reasonable random number generator +# RANDOBJ = random.o +RANDOBJ = + + +# used by `make depend' to reconstruct this Makefile; you shouldn't need this +AWK = nawk + +# ---------------------------------------- +# +# Nothing below this line should have to be changed. +# +# Other things that have to be reconfigured are in config.h, +# {unixconf.h, pcconf.h, tosconf.h}, and possibly system.h + +MAKEDEFS = ../util/makedefs + +# timestamp files to reduce `make' overhead and shorten .o dependency lists +CONFIG_H = ../src/config.h-t +HACK_H = ../src/hack.h-t + +# all .c that are part of the main NetHack program and are not operating- or +# windowing-system specific +HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ + botl.c cmd.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c \ + do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ + dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ + files.c fountain.c hack.c hacklib.c invent.c light.c lock.c \ + mail.c makemon.c mapglyph.c mcastu.c mhitm.c mhitu.c minion.c \ + mklev.c mkmap.c \ + mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c monst.c \ + mplayer.c mthrowu.c muse.c music.c o_init.c objects.c objnam.c \ + options.c pager.c pickup.c pline.c polyself.c potion.c pray.c \ + priest.c quest.c questpgr.c read.c rect.c region.c restore.c rip.c \ + rnd.c role.c rumors.c save.c shk.c shknam.c sit.c sounds.c sp_lev.c \ + spell.c steal.c steed.c teleport.c timeout.c topten.c track.c trap.c \ + u_init.c uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ + windows.c wizard.c worm.c worn.c write.c zap.c + +# all operating-system-dependent .c (for dependencies and such) +SYSCSRC = ../sys/atari/tos.c ../sys/share/pcmain.c ../sys/share/pcsys.c \ + ../sys/share/pctty.c ../sys/share/pcunix.c ../sys/share/random.c \ + ../sys/share/ioctl.c ../sys/share/unixtty.c ../sys/unix/unixmain.c \ + ../sys/unix/unixunix.c ../sys/unix/unixres.c ../sys/be/bemain.c + +# generated source files (tile.c is handled separately via WINxxxSRC) +GENCSRC = monstr.c vis_tab.c #tile.c + +# all windowing-system-dependent .c (for dependencies and such) +WINCSRC = $(WINTTYSRC) $(WINX11SRC) $(WINGNOMESRC) $(WINGEMSRC) +# all windowing-system-dependent .cpp (for dependencies and such) +WINCXXSRC = $(WINQTSRC) $(WINBESRC) + +# .c files for this version (for date.h) +VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(GENCSRC) + +# .c files for all versions using this Makefile (for lint and tags) +CSOURCES = $(HACKCSRC) $(SYSSRC) $(WINCSRC) $(GENCSRC) + + +# all .h files except date.h, onames.h, pm.h, and vis_tab.h which would +# cause dependency loops if run through "make depend" +# and dgn_comp.h, dgn_file.h, lev_comp.h, special level & dungeon files. +# +HACKINCL = align.h amiconf.h artifact.h artilist.h attrib.h beconf.h color.h \ + config.h config1.h coord.h decl.h def_os2.h display.h dlb.h dungeon.h \ + edog.h emin.h engrave.h epri.h eshk.h extern.h flag.h func_tab.h \ + global.h hack.h lev.h macconf.h mfndpos.h micro.h mkroom.h \ + monattk.h mondata.h monflag.h monst.h monsym.h obj.h objclass.h \ + os2conf.h patchlevel.h pcconf.h permonst.h prop.h rect.h region.h rm.h \ + sp_lev.h spell.h system.h tcap.h timeout.h tosconf.h tradstdc.h \ + trampoli.h trap.h unixconf.h vault.h vision.h vmsconf.h wintty.h \ + winX.h winprocs.h wintype.h you.h youprop.h + +HSOURCES = $(HACKINCL) date.h onames.h pm.h vis_tab.h\ + lev_comp.h dgn_comp.h dgn_file.h + +# the following .o's _must_ be made before any others (for makedefs) +FIRSTOBJ = monst.o objects.o + +HOBJ = $(FIRSTOBJ) allmain.o alloc.o apply.o artifact.o attrib.o ball.o \ + bones.o botl.o cmd.o dbridge.o decl.o detect.o dig.o display.o dlb.o \ + do.o do_name.o do_wear.o dog.o dogmove.o dokick.o dothrow.o \ + drawing.o dungeon.o eat.o end.o engrave.o exper.o explode.o \ + extralev.o files.o fountain.o hack.o hacklib.o invent.o light.o \ + lock.o mail.o makemon.o mapglyph.o mcastu.o mhitm.o mhitu.o \ + minion.o mklev.o mkmap.o \ + mkmaze.o mkobj.o mkroom.o mon.o mondata.o monmove.o monstr.o \ + mplayer.o mthrowu.o muse.o music.o o_init.o objnam.o options.o \ + pager.o pickup.o pline.o polyself.o potion.o pray.o priest.o \ + quest.o questpgr.o read.o rect.o region.o restore.o rip.o rnd.o \ + role.o rumors.o save.o shk.o shknam.o sit.o sounds.o sp_lev.o spell.o \ + steal.o steed.o teleport.o timeout.o topten.o track.o trap.o u_init.o \ + uhitm.o vault.o vision.o vis_tab.o weapon.o were.o wield.o windows.o \ + wizard.o worm.o worn.o write.o zap.o \ + $(RANDOBJ) $(SYSOBJ) $(WINOBJ) version.o +# the .o files from the HACKCSRC, SYSSRC, and WINSRC lists + +$(GAME): $(SYSTEM) + @echo "$(GAME) is up to date." + +Sysunix: $(HOBJ) Makefile + @echo "Loading ..." + $(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) + @touch Sysunix + +Sys3B2: $(HOBJ) Makefile + @echo "Loading ..." + @$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) -lmalloc + @touch Sys3B2 + +Sysatt: $(HOBJ) Makefile + @echo "Loading ..." + @$(LD) $(LFLAGS) /lib/crt0s.o /lib/shlib.ifile -o $(GAME) $(HOBJ) + @touch Sysatt + +Systos: $(HOBJ) Makefile + @echo "Loading ..." + @$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) + @touch Systos + +SysV-AT: DUMB.Setup $(HOBJ) Makefile + @echo "Loading ..." + @$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) + @touch SysV-AT + +SysBe: $(HOBJ) Makefile + @echo "Loading ..." + @$(LINK) $(LFLAGS) -o $(GAME) $(HOBJ) $(WINLIB) $(LIBS) + @xres -o $(GAME) ../win/BeOS/nethack.rsrc + @mimeset -f $(GAME) + @touch SysBe + +DUMB.Setup: ../include/extern.h + cp ../include/extern.h ../include/extern.h.BAK + cat ../include/extern.h | \ + sed -e '/^E\ int\ /!b' \ + -e '/[^;/ ]$$/N' \ + -e '/[(][*]occupation[)]/b' \ + -e '/[(][*]afternmv[)]/b' \ + -e '/float_down/b' \ + -e '/done1/b' \ + -e '/identify/b' \ + -e '/Hear_again/b' \ + -e '/hangup/b' \ + -e 's/^\(.*\)$$/\/\* \1 \/\*\*\//' | \ + sed -e '/^E\ void\ /!b' \ + -e '/[^;/ ]$$/N' \ + -e 's/^\(.*\)$$/\/\* \1 \/\*\*\//' \ + >../include/extern.DUMB + cp ../include/extern.DUMB ../include/extern.h + @touch DUMB.Setup + +all: $(GAME) + + +# dependencies for makedefs and its outputs, which the util +# Makefile is responsible for keeping up to date +# + +# special rules, to force update of makedefs, real dependencies should be +# below in the 'make depend' output. +monst.o: + $(CC) $(CFLAGS) -c monst.c + @rm -f $(MAKEDEFS) + +objects.o: + $(CC) $(CFLAGS) -c objects.c + @rm -f $(MAKEDEFS) + +# Qt windowport meta-object-compiler output +qt_kde0.moc: ../include/qt_kde0.h + $(QTDIR)/bin/moc -o qt_kde0.moc ../include/qt_kde0.h + +qt_win.moc: ../include/qt_win.h + $(QTDIR)/bin/moc -o qt_win.moc ../include/qt_win.h + +qttableview.moc: ../include/qttableview.h + $(QTDIR)/bin/moc -o qttableview.moc ../include/qttableview.h + +$(MAKEDEFS): ../util/makedefs.c $(CONFIG_H) ../include/permonst.h \ + ../include/objclass.h ../include/monsym.h \ + ../include/artilist.h ../include/dungeon.h ../include/obj.h \ + ../include/monst.h ../include/you.h ../include/flag.h \ + ../include/dlb.h ../include/patchlevel.h ../include/qtext.h + @( cd ../util ; $(MAKE) makedefs) + +../include/onames.h: $(MAKEDEFS) + @( cd ../util ; $(MAKE) ../include/onames.h ) +../include/pm.h: $(MAKEDEFS) + @( cd ../util ; $(MAKE) ../include/pm.h ) +monstr.c: $(MAKEDEFS) + @( cd ../util ; $(MAKE) ../src/monstr.c ) +../include/vis_tab.h: $(MAKEDEFS) + @( cd ../util ; $(MAKE) ../include/vis_tab.h ) +# makedefs -z makes both vis_tab.h and vis_tab.c, but writes the .h first +vis_tab.c: ../include/vis_tab.h +tile.c: ../win/share/tilemap.c $(HACK_H) + @( cd ../util ; $(MAKE) ../src/tile.c ) + +../win/gnome/gn_rip.h: ../win/X11/rip.xpm + cp ../win/X11/rip.xpm ../win/gnome/gn_rip.h + +# date.h should be remade any time any of the source or include code +# is modified. Unfortunately, this would make the contents of this +# file far more complex. Since "hack.h" depends on most of the include +# files, we kludge around this by making date.h dependent on hack.h, +# even though it doesn't include this file. +# +# hack.h depends on makedefs' output, so we know makedefs will be +# up to date before being executed +../include/date.h: $(VERSOURCES) $(HACK_H) + ../util/makedefs -v + + +lint: +# lint cannot have -p here because (i) capitals are meaningful: +# [Ww]izard, (ii) identifiers may coincide in the first six places: +# doweararm() versus dowearring(). +# _flsbuf comes from , a bug in the system libraries. + @echo lint -axbh -DLINT ... + @lint -axbh -I../include -DLINT $(CSOURCES) | sed '/_flsbuf/d' + + +tags: $(CSOURCES) + @echo ctags -tw ... + @ctags -tw $(CSOURCES) + @( cd ../include ; ctags -tw $(HSOURCES) ) + @( cd ../util ; $(MAKE) tags ) + +clean: + -rm -f *.o $(HACK_H) $(CONFIG_H) + +spotless: clean + -rm -f a.out core $(GAME) Sys* + -rm -f ../include/date.h ../include/onames.h ../include/pm.h + -rm -f monstr.c ../include/vis_tab.h vis_tab.c tile.c *.moc + -rm -f ../win/gnome/gn_rip.h + + +depend: ../sys/unix/depend.awk \ + $(SYSCSRC) $(WINCSRC) $(WINCXXSRC) $(GENCSRC) $(HACKCSRC) + $(AWK) -f ../sys/unix/depend.awk ../include/*.h \ + $(SYSCSRC) $(WINCSRC) $(WINCXXSRC) $(GENCSRC) $(HACKCSRC) >makedep + @echo '/^# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT/+2,$$d' >eddep + @echo '$$r makedep' >>eddep + @echo 'w' >>eddep + @cp Makefile Makefile.bak + ed - Makefile < eddep + @rm -f eddep makedep + @echo '# DEPENDENCIES MUST END AT END OF FILE' >> Makefile + @echo '# IF YOU PUT STUFF HERE IT WILL GO AWAY' >> Makefile + @echo '# see make depend above' >> Makefile + - diff Makefile.bak Makefile + @rm -f Makefile.bak + +# DO NOT DELETE THIS LINE OR CHANGE ANYTHING BEYOND IT + +# config.h timestamp +$(CONFIG_H): ../include/config.h ../include/config1.h ../include/tradstdc.h \ + ../include/global.h ../include/coord.h ../include/vmsconf.h \ + ../include/system.h ../include/unixconf.h ../include/os2conf.h \ + ../include/micro.h ../include/pcconf.h ../include/tosconf.h \ + ../include/amiconf.h ../include/macconf.h ../include/beconf.h \ + ../include/wceconf.h ../include/ntconf.h ../include/nhlan.h + touch $(CONFIG_H) +# hack.h timestamp +$(HACK_H): ../include/hack.h $(CONFIG_H) ../include/align.h \ + ../include/dungeon.h ../include/monsym.h ../include/mkroom.h \ + ../include/objclass.h ../include/youprop.h ../include/prop.h \ + ../include/permonst.h ../include/monattk.h \ + ../include/monflag.h ../include/mondata.h ../include/pm.h \ + ../include/wintype.h ../include/decl.h ../include/quest.h \ + ../include/spell.h ../include/color.h ../include/obj.h \ + ../include/you.h ../include/attrib.h ../include/monst.h \ + ../include/skills.h ../include/onames.h ../include/timeout.h \ + ../include/trap.h ../include/flag.h ../include/rm.h \ + ../include/vision.h ../include/display.h ../include/engrave.h \ + ../include/rect.h ../include/region.h ../include/winprocs.h \ + ../include/wintty.h ../include/trampoli.h + touch $(HACK_H) +# +tos.o: ../sys/atari/tos.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c ../sys/atari/tos.c +pcmain.o: ../sys/share/pcmain.c $(HACK_H) ../include/dlb.h \ + #../include/win32api.h + $(CC) $(CFLAGS) -c ../sys/share/pcmain.c +pcsys.o: ../sys/share/pcsys.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/share/pcsys.c +pctty.o: ../sys/share/pctty.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/share/pctty.c +pcunix.o: ../sys/share/pcunix.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/share/pcunix.c +random.o: ../sys/share/random.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/share/random.c +ioctl.o: ../sys/share/ioctl.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c ../sys/share/ioctl.c +unixtty.o: ../sys/share/unixtty.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/share/unixtty.c +unixmain.o: ../sys/unix/unixmain.c $(HACK_H) ../include/dlb.h + $(CC) $(CFLAGS) -c ../sys/unix/unixmain.c +unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(CC) $(CFLAGS) -c ../sys/unix/unixunix.c +unixres.o: ../sys/unix/unixres.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../sys/unix/unixres.c +bemain.o: ../sys/be/bemain.c $(HACK_H) ../include/dlb.h + $(CC) $(CFLAGS) -c ../sys/be/bemain.c +getline.o: ../win/tty/getline.c $(HACK_H) ../include/func_tab.h + $(CC) $(CFLAGS) -c ../win/tty/getline.c +termcap.o: ../win/tty/termcap.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c ../win/tty/termcap.c +topl.o: ../win/tty/topl.c $(HACK_H) ../include/tcap.h + $(CC) $(CFLAGS) -c ../win/tty/topl.c +wintty.o: ../win/tty/wintty.c $(HACK_H) ../include/dlb.h \ + ../include/patchlevel.h ../include/tcap.h + $(CC) $(CFLAGS) -c ../win/tty/wintty.c +Window.o: ../win/X11/Window.c ../include/xwindowp.h ../include/xwindow.h \ + $(CONFIG_H) + $(CC) $(CFLAGS) -c ../win/X11/Window.c +dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../win/X11/dialogs.c +winX.o: ../win/X11/winX.c $(HACK_H) ../include/winX.h ../include/dlb.h \ + ../include/patchlevel.h ../win/X11/nh72icon \ + ../win/X11/nh56icon ../win/X11/nh32icon + $(CC) $(CFLAGS) -c ../win/X11/winX.c +winmap.o: ../win/X11/winmap.c ../include/xwindow.h $(HACK_H) ../include/dlb.h \ + ../include/winX.h ../include/tile2x11.h + $(CC) $(CFLAGS) -c ../win/X11/winmap.c +winmenu.o: ../win/X11/winmenu.c $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) -c ../win/X11/winmenu.c +winmesg.o: ../win/X11/winmesg.c ../include/xwindow.h $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) -c ../win/X11/winmesg.c +winmisc.o: ../win/X11/winmisc.c $(HACK_H) ../include/func_tab.h \ + ../include/winX.h + $(CC) $(CFLAGS) -c ../win/X11/winmisc.c +winstat.o: ../win/X11/winstat.c $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) -c ../win/X11/winstat.c +wintext.o: ../win/X11/wintext.c $(HACK_H) ../include/winX.h ../include/xwindow.h + $(CC) $(CFLAGS) -c ../win/X11/wintext.c +winval.o: ../win/X11/winval.c $(HACK_H) ../include/winX.h + $(CC) $(CFLAGS) -c ../win/X11/winval.c +tile.o: tile.c $(HACK_H) +gnaskstr.o: ../win/gnome/gnaskstr.c ../win/gnome/gnaskstr.h \ + ../win/gnome/gnmain.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnaskstr.c +gnbind.o: ../win/gnome/gnbind.c ../win/gnome/gnbind.h ../win/gnome/gnmain.h \ + ../win/gnome/gnmenu.h ../win/gnome/gnaskstr.h \ + ../win/gnome/gnyesno.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnbind.c +gnglyph.o: ../win/gnome/gnglyph.c ../win/gnome/gnglyph.h ../include/tile2x11.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnglyph.c +gnmain.o: ../win/gnome/gnmain.c ../win/gnome/gnmain.h ../win/gnome/gnsignal.h \ + ../win/gnome/gnbind.h ../win/gnome/gnopts.h $(HACK_H) \ + ../include/date.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnmain.c +gnmap.o: ../win/gnome/gnmap.c ../win/gnome/gnmap.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h $(HACK_H) + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnmap.c +gnmenu.o: ../win/gnome/gnmenu.c ../win/gnome/gnmenu.h ../win/gnome/gnmain.h \ + ../win/gnome/gnbind.h ../include/func_tab.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnmenu.c +gnmesg.o: ../win/gnome/gnmesg.c ../win/gnome/gnmesg.h ../win/gnome/gnsignal.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnmesg.c +gnopts.o: ../win/gnome/gnopts.c ../win/gnome/gnopts.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnmain.h ../win/gnome/gnmap.h $(HACK_H) + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnopts.c +gnplayer.o: ../win/gnome/gnplayer.c ../win/gnome/gnplayer.h \ + ../win/gnome/gnmain.h $(HACK_H) + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnplayer.c +gnsignal.o: ../win/gnome/gnsignal.c ../win/gnome/gnsignal.h \ + ../win/gnome/gnmain.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnsignal.c +gnstatus.o: ../win/gnome/gnstatus.c ../win/gnome/gnstatus.h \ + ../win/gnome/gnsignal.h ../win/gnome/gn_xpms.h \ + ../win/gnome/gnomeprv.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnstatus.c +gntext.o: ../win/gnome/gntext.c ../win/gnome/gntext.h ../win/gnome/gnmain.h \ + ../win/gnome/gn_rip.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gntext.c +gnyesno.o: ../win/gnome/gnyesno.c ../win/gnome/gnbind.h ../win/gnome/gnyesno.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnyesno.c +gnworn.o: ../win/gnome/gnworn.c ../win/gnome/gnworn.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h ../win/gnome/gnomeprv.h + $(CC) $(CFLAGS) $(GNOMEINC) -c ../win/gnome/gnworn.c +wingem.o: ../win/gem/wingem.c $(HACK_H) ../include/func_tab.h ../include/dlb.h \ + ../include/patchlevel.h ../include/wingem.h + $(CC) $(CFLAGS) -c ../win/gem/wingem.c +wingem1.o: ../win/gem/wingem1.c ../include/gem_rsc.h ../include/load_img.h \ + ../include/gr_rect.h ../include/wintype.h ../include/wingem.h + $(CC) $(CFLAGS) -c ../win/gem/wingem1.c +load_img.o: ../win/gem/load_img.c ../include/load_img.h + $(CC) $(CFLAGS) -c ../win/gem/load_img.c +gr_rect.o: ../win/gem/gr_rect.c ../include/gr_rect.h + $(CC) $(CFLAGS) -c ../win/gem/gr_rect.c +tile.o: tile.c $(HACK_H) +qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) ../include/func_tab.h \ + ../include/dlb.h ../include/patchlevel.h ../include/tile2x11.h \ + ../include/qt_win.h ../include/qt_clust.h ../include/qt_kde0.h \ + ../include/qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -c ../win/Qt/qt_win.cpp +qt_clust.o: ../win/Qt/qt_clust.cpp ../include/qt_clust.h + $(CXX) $(CXXFLAGS) -c ../win/Qt/qt_clust.cpp +qttableview.o: ../win/Qt/qttableview.cpp ../include/qttableview.h + $(CXX) $(CXXFLAGS) -c ../win/Qt/qttableview.cpp +monstr.o: monstr.c $(CONFIG_H) +vis_tab.o: vis_tab.c $(CONFIG_H) ../include/vis_tab.h +allmain.o: allmain.c $(HACK_H) +alloc.o: alloc.c $(CONFIG_H) +apply.o: apply.c $(HACK_H) ../include/edog.h +artifact.o: artifact.c $(HACK_H) ../include/artifact.h ../include/artilist.h +attrib.o: attrib.c $(HACK_H) +ball.o: ball.c $(HACK_H) +bones.o: bones.c $(HACK_H) ../include/lev.h +botl.o: botl.c $(HACK_H) +cmd.o: cmd.c $(HACK_H) ../include/func_tab.h +dbridge.o: dbridge.c $(HACK_H) +decl.o: decl.c $(HACK_H) +detect.o: detect.c $(HACK_H) ../include/artifact.h +dig.o: dig.c $(HACK_H) ../include/edog.h +display.o: display.c $(HACK_H) +dlb.o: dlb.c $(CONFIG_H) ../include/dlb.h +do.o: do.c $(HACK_H) ../include/lev.h +do_name.o: do_name.c $(HACK_H) +do_wear.o: do_wear.c $(HACK_H) +dog.o: dog.c $(HACK_H) ../include/edog.h +dogmove.o: dogmove.c $(HACK_H) ../include/mfndpos.h ../include/edog.h +dokick.o: dokick.c $(HACK_H) ../include/eshk.h +dothrow.o: dothrow.c $(HACK_H) ../include/edog.h +drawing.o: drawing.c $(HACK_H) ../include/tcap.h +dungeon.o: dungeon.c $(HACK_H) ../include/dgn_file.h ../include/dlb.h +eat.o: eat.c $(HACK_H) +end.o: end.c $(HACK_H) ../include/eshk.h ../include/dlb.h +engrave.o: engrave.c $(HACK_H) ../include/lev.h +exper.o: exper.c $(HACK_H) +explode.o: explode.c $(HACK_H) +extralev.o: extralev.c $(HACK_H) +files.o: files.c $(HACK_H) ../include/dlb.h +fountain.o: fountain.c $(HACK_H) +hack.o: hack.c $(HACK_H) +hacklib.o: hacklib.c $(HACK_H) +invent.o: invent.c $(HACK_H) +light.o: light.c $(HACK_H) ../include/lev.h +lock.o: lock.c $(HACK_H) +mail.o: mail.c $(HACK_H) ../include/mail.h +makemon.o: makemon.c $(HACK_H) ../include/epri.h ../include/emin.h \ + ../include/edog.h +mapglyph.o: mapglyph.c $(HACK_H) +mcastu.o: mcastu.c $(HACK_H) +mhitm.o: mhitm.c $(HACK_H) ../include/artifact.h ../include/edog.h +mhitu.o: mhitu.c $(HACK_H) ../include/artifact.h ../include/edog.h +minion.o: minion.c $(HACK_H) ../include/emin.h ../include/epri.h +mklev.o: mklev.c $(HACK_H) +mkmap.o: mkmap.c $(HACK_H) ../include/sp_lev.h +mkmaze.o: mkmaze.c $(HACK_H) ../include/sp_lev.h ../include/lev.h +mkobj.o: mkobj.c $(HACK_H) +mkroom.o: mkroom.c $(HACK_H) +mon.o: mon.c $(HACK_H) ../include/mfndpos.h ../include/edog.h +mondata.o: mondata.c $(HACK_H) ../include/eshk.h ../include/epri.h +monmove.o: monmove.c $(HACK_H) ../include/mfndpos.h ../include/artifact.h \ + ../include/epri.h +monst.o: monst.c $(CONFIG_H) ../include/permonst.h ../include/align.h \ + ../include/monattk.h ../include/monflag.h ../include/monsym.h \ + ../include/dungeon.h ../include/eshk.h ../include/vault.h \ + ../include/epri.h ../include/color.h +mplayer.o: mplayer.c $(HACK_H) +mthrowu.o: mthrowu.c $(HACK_H) +muse.o: muse.c $(HACK_H) ../include/edog.h +music.o: music.c $(HACK_H) #interp.c +o_init.o: o_init.c $(HACK_H) ../include/lev.h +objects.o: objects.c $(CONFIG_H) ../include/obj.h ../include/objclass.h \ + ../include/prop.h ../include/skills.h ../include/color.h +objnam.o: objnam.c $(HACK_H) +options.o: options.c $(CONFIG_H) ../include/objclass.h ../include/flag.h \ + $(HACK_H) ../include/tcap.h +pager.o: pager.c $(HACK_H) ../include/dlb.h +pickup.o: pickup.c $(HACK_H) +pline.o: pline.c $(HACK_H) ../include/epri.h ../include/edog.h +polyself.o: polyself.c $(HACK_H) +potion.o: potion.c $(HACK_H) +pray.o: pray.c $(HACK_H) ../include/epri.h +priest.o: priest.c $(HACK_H) ../include/mfndpos.h ../include/eshk.h \ + ../include/epri.h ../include/emin.h +quest.o: quest.c $(HACK_H) ../include/qtext.h +questpgr.o: questpgr.c $(HACK_H) ../include/dlb.h ../include/qtext.h +read.o: read.c $(HACK_H) +rect.o: rect.c $(HACK_H) +region.o: region.c $(HACK_H) ../include/lev.h +restore.o: restore.c $(HACK_H) ../include/lev.h ../include/tcap.h +rip.o: rip.c $(HACK_H) +rnd.o: rnd.c $(HACK_H) +role.o: role.c $(HACK_H) +rumors.o: rumors.c $(HACK_H) ../include/lev.h ../include/dlb.h +save.o: save.c $(HACK_H) ../include/lev.h +shk.o: shk.c $(HACK_H) ../include/eshk.h +shknam.o: shknam.c $(HACK_H) ../include/eshk.h +sit.o: sit.c $(HACK_H) ../include/artifact.h +sounds.o: sounds.c $(HACK_H) ../include/edog.h +sp_lev.o: sp_lev.c $(HACK_H) ../include/dlb.h ../include/sp_lev.h +spell.o: spell.c $(HACK_H) +steal.o: steal.c $(HACK_H) +steed.o: steed.c $(HACK_H) +teleport.o: teleport.c $(HACK_H) +timeout.o: timeout.c $(HACK_H) ../include/lev.h +topten.o: topten.c $(HACK_H) ../include/dlb.h ../include/patchlevel.h +track.o: track.c $(HACK_H) +trap.o: trap.c $(HACK_H) +u_init.o: u_init.c $(HACK_H) +uhitm.o: uhitm.c $(HACK_H) +vault.o: vault.c $(HACK_H) ../include/vault.h +version.o: version.c $(HACK_H) ../include/date.h ../include/patchlevel.h +vision.o: vision.c $(HACK_H) ../include/vis_tab.h +weapon.o: weapon.c $(HACK_H) +were.o: were.c $(HACK_H) +wield.o: wield.c $(HACK_H) +windows.o: windows.c $(HACK_H) ../include/wingem.h ../include/winGnome.h +wizard.o: wizard.c $(HACK_H) ../include/qtext.h ../include/epri.h +worm.o: worm.c $(HACK_H) ../include/lev.h +worn.o: worn.c $(HACK_H) +write.o: write.c $(HACK_H) +zap.o: zap.c $(HACK_H) +# DEPENDENCIES MUST END AT END OF FILE +# IF YOU PUT STUFF HERE IT WILL GO AWAY +# see make depend above diff --git a/sys/unix/Makefile.top b/sys/unix/Makefile.top new file mode 100644 index 0000000..2f11c35 --- /dev/null +++ b/sys/unix/Makefile.top @@ -0,0 +1,266 @@ +# NetHack Makefile. +# SCCS Id: @(#)Makefile.top 3.4 1995/01/05 + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about unable to +# execute things like 'data' and 'rumors') +# MAKE = make + +# make NetHack +PREFIX = /usr +GAME = nethack +# GAME = nethack.prg +GAMEUID = games +GAMEGRP = bin + +# Permissions - some places use setgid instead of setuid, for instance +# See also the option "SECURE" in include/config.h +GAMEPERM = 04755 +FILEPERM = 0644 +EXEPERM = 0755 +DIRPERM = 0755 + +# GAMEDIR also appears in config.h as "HACKDIR". +# VARDIR may also appear in unixconf.h as "VAR_PLAYGROUND" else GAMEDIR +# +# note that 'make install' believes in creating a nice tidy GAMEDIR for +# installation, free of debris from previous NetHack versions -- +# therefore there should not be anything in GAMEDIR that you want to keep +# (if there is, you'll have to do the installation by hand or modify the +# instructions) +GAMEDIR = $(PREFIX)/games/lib/$(GAME)dir +VARDIR = $(GAMEDIR) +SHELLDIR = $(PREFIX)/games + +# per discussion in Install.X11 and Install.Qt +VARDATND = +# VARDATND = x11tiles NetHack.ad pet_mark.xbm +# VARDATND = x11tiles NetHack.ad pet_mark.xbm rip.xpm +# for Atari/Gem +# VARDATND = nh16.img title.img GEM_RSC.RSC rip.img +# for BeOS +# VARDATND = beostiles +# for Gnome +# VARDATND = x11tiles pet_mark.xbm rip.xpm mapbg.xpm + +VARDATD = data oracles options quest.dat rumors +VARDAT = $(VARDATD) $(VARDATND) + +# Some versions of make use the SHELL environment variable as the shell +# for running commands. We need this to be a Bourne shell. +# SHELL = /bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# Commands for setting the owner and group on files during installation. +# Some systems fail with one or the other when installing over NFS or for +# other permission-related reasons. If that happens, you may want to set the +# command to "true", which is a no-op. Note that disabling chown or chgrp +# will only work if setuid (or setgid) behavior is not desired or required. +CHOWN = chown +CHGRP = chgrp + +# +# end of configuration +# + +DATHELP = help hh cmdhelp history opthelp wizhelp + +SPEC_LEVS = asmodeus.lev baalz.lev bigrm-?.lev castle.lev fakewiz?.lev \ + juiblex.lev knox.lev medusa-?.lev minend-?.lev minefill.lev \ + minetn-?.lev oracle.lev orcus.lev sanctum.lev soko?-?.lev \ + tower?.lev valley.lev wizard?.lev \ + astral.lev air.lev earth.lev fire.lev water.lev +QUEST_LEVS = ???-goal.lev ???-fil?.lev ???-loca.lev ???-strt.lev + +DATNODLB = $(VARDATND) license +DATDLB = $(DATHELP) dungeon $(SPEC_LEVS) $(QUEST_LEVS) $(VARDATD) +DAT = $(DATNODLB) $(DATDLB) + +$(GAME): + ( cd src ; $(MAKE) ) + +all: $(GAME) recover Guidebook $(VARDAT) dungeon spec_levs check-dlb + @echo "Done." + +# Note: many of the dependencies below are here to allow parallel make +# to generate valid output + +Guidebook: + ( cd doc ; $(MAKE) Guidebook ) + +manpages: + ( cd doc ; $(MAKE) manpages ) + +data: $(GAME) + ( cd dat ; $(MAKE) data ) + +rumors: $(GAME) + ( cd dat ; $(MAKE) rumors ) + +oracles: $(GAME) + ( cd dat ; $(MAKE) oracles ) + +# Note: options should have already been made with make, but... +options: $(GAME) + ( cd dat ; $(MAKE) options ) + +quest.dat: $(GAME) + ( cd dat ; $(MAKE) quest.dat ) + +spec_levs: dungeon + ( cd util ; $(MAKE) lev_comp ) + ( cd dat ; $(MAKE) spec_levs ) + ( cd dat ; $(MAKE) quest_levs ) + +dungeon: $(GAME) + ( cd util ; $(MAKE) dgn_comp ) + ( cd dat ; $(MAKE) dungeon ) + +nhtiles.bmp: $(GAME) + ( cd dat ; $(MAKE) nhtiles.bmp ) + +x11tiles: $(GAME) + ( cd util ; $(MAKE) tile2x11 ) + ( cd dat ; $(MAKE) x11tiles ) + +beostiles: $(GAME) + ( cd util ; $(MAKE) tile2beos ) + ( cd dat ; $(MAKE) beostiles ) + +NetHack.ad: $(GAME) + ( cd dat ; $(MAKE) NetHack.ad ) + +pet_mark.xbm: + ( cd dat ; $(MAKE) pet_mark.xbm ) + +rip.xpm: + ( cd dat ; $(MAKE) rip.xpm ) + +mapbg.xpm: + (cd dat ; $(MAKE) mapbg.xpm ) + +nhsplash.xpm: + ( cd dat ; $(MAKE) nhsplash.xpm ) + +nh16.img: $(GAME) + ( cd util ; $(MAKE) tile2img.ttp ) + ( cd dat ; $(MAKE) nh16.img ) + +rip.img: + ( cd util ; $(MAKE) xpm2img.ttp ) + ( cd dat ; $(MAKE) rip.img ) +GEM_RSC.RSC: + ( cd dat ; $(MAKE) GEM_RSC.RSC ) + +title.img: + ( cd dat ; $(MAKE) title.img ) + +check-dlb: options + @if egrep -s librarian dat/options ; then $(MAKE) dlb ; else true ; fi + +dlb: + ( cd util ; $(MAKE) dlb ) + ( cd dat ; ../util/dlb cf nhdat $(DATDLB) ) + +# recover can be used when INSURANCE is defined in include/config.h +# and the checkpoint option is true +recover: $(GAME) + ( cd util ; $(MAKE) recover ) + +dofiles: + target=`sed -n \ + -e '/librarian/{' \ + -e 's/.*/dlb/p' \ + -e 'q' \ + -e '}' \ + -e '$$s/.*/nodlb/p' < dat/options` ; \ + $(MAKE) dofiles-$${target-nodlb} + cp src/$(GAME) $(GAMEDIR) + cp util/recover $(GAMEDIR) + -rm -f $(SHELLDIR)/$(GAME) + sed -e 's;/usr/games/lib/nethackdir;$(GAMEDIR);' \ + -e 's;HACKDIR/nethack;HACKDIR/$(GAME);' \ + < sys/unix/nethack.sh \ + > $(SHELLDIR)/$(GAME) +# set up their permissions + -( cd $(GAMEDIR) ; $(CHOWN) $(GAMEUID) $(GAME) recover ; \ + $(CHGRP) $(GAMEGRP) $(GAME) recover ) + chmod $(GAMEPERM) $(GAMEDIR)/$(GAME) + chmod $(EXEPERM) $(GAMEDIR)/recover + -$(CHOWN) $(GAMEUID) $(SHELLDIR)/$(GAME) + $(CHGRP) $(GAMEGRP) $(SHELLDIR)/$(GAME) + chmod $(EXEPERM) $(SHELLDIR)/$(GAME) + +dofiles-dlb: check-dlb + ( cd dat ; cp nhdat $(DATNODLB) $(GAMEDIR) ) +# set up their permissions + -( cd $(GAMEDIR) ; $(CHOWN) $(GAMEUID) nhdat $(DATNODLB) ; \ + $(CHGRP) $(GAMEGRP) nhdat $(DATNODLB) ; \ + chmod $(FILEPERM) nhdat $(DATNODLB) ) + +dofiles-nodlb: +# copy over the game files + ( cd dat ; cp $(DAT) $(GAMEDIR) ) +# set up their permissions + -( cd $(GAMEDIR) ; $(CHOWN) $(GAMEUID) $(DAT) ; \ + $(CHGRP) $(GAMEGRP) $(DAT) ; \ + chmod $(FILEPERM) $(DAT) ) + +update: $(GAME) recover $(VARDAT) dungeon spec_levs +# (don't yank the old version out from under people who're playing it) + -mv $(GAMEDIR)/$(GAME) $(GAMEDIR)/$(GAME).old +# quest.dat is also kept open and has the same problems over NFS +# (quest.dat may be inside nhdat if dlb is in use) + -mv $(GAMEDIR)/quest.dat $(GAMEDIR)/quest.dat.old + -mv $(GAMEDIR)/nhdat $(GAMEDIR)/nhdat.old +# set up new versions of the game files + ( $(MAKE) dofiles ) +# touch time-sensitive files + -touch -c $(VARDIR)/bones* $(VARDIR)/?lock* $(VARDIR)/wizard* + -touch -c $(VARDIR)/save/* + touch $(VARDIR)/perm $(VARDIR)/record +# and a reminder + @echo You may also want to install the man pages via the doc Makefile. + +install: $(GAME) recover $(VARDAT) dungeon spec_levs +# set up the directories +# not all mkdirs have -p; those that don't will create a -p directory + -mkdir -p $(SHELLDIR) + -rm -rf $(GAMEDIR) $(VARDIR) + -mkdir -p $(GAMEDIR) $(VARDIR) $(VARDIR)/save + -rmdir ./-p + -$(CHOWN) $(GAMEUID) $(GAMEDIR) $(VARDIR) $(VARDIR)/save + $(CHGRP) $(GAMEGRP) $(GAMEDIR) $(VARDIR) $(VARDIR)/save + chmod $(DIRPERM) $(GAMEDIR) $(VARDIR) $(VARDIR)/save +# set up the game files + ( $(MAKE) dofiles ) +# set up some additional files + touch $(VARDIR)/perm $(VARDIR)/record $(VARDIR)/logfile + -( cd $(VARDIR) ; $(CHOWN) $(GAMEUID) perm record logfile ; \ + $(CHGRP) $(GAMEGRP) perm record logfile ; \ + chmod $(FILEPERM) perm record logfile ) +# and a reminder + @echo You may also want to reinstall the man pages via the doc Makefile. + + +# 'make clean' removes all the .o files, but leaves around all the executables +# and compiled data files +clean: + ( cd src ; $(MAKE) clean ) + ( cd util ; $(MAKE) clean ) + +# 'make spotless' returns the source tree to near-distribution condition. +# it removes .o files, executables, and compiled data files +spotless: + ( cd src ; $(MAKE) spotless ) + ( cd util ; $(MAKE) spotless ) + ( cd dat ; $(MAKE) spotless ) + ( cd doc ; $(MAKE) spotless ) diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl new file mode 100644 index 0000000..e2936a8 --- /dev/null +++ b/sys/unix/Makefile.utl @@ -0,0 +1,403 @@ +# Makefile for NetHack's utility programs. +# SCCS Id: @(#)Makefile.utl 3.4 1997/04/19 + +# newer makes predefine $(MAKE) to 'make' and do smarter processing of +# recursive make calls if $(MAKE) is used +# these makes allow $(MAKE) to be overridden by the environment if someone +# wants to (or has to) use something other than the standard make, so we do +# not want to unconditionally set $(MAKE) here +# +# unfortunately, some older makes do not predefine $(MAKE); if you have one of +# these, uncomment the following line +# (you will know that you have one if you get complaints about unable to +# execute things like 'foo.o') +# MAKE = make + +# if you are using gcc as your compiler, +# uncomment the CC definition below if it's not in your environment +# CC = gcc +# +# For Bull DPX/2 systems at B.O.S. 2.0 or higher use the following: +# +# CC = gcc -ansi -D_BULL_SOURCE -D_XOPEN_SOURCE -D_POSIX_SOURCE +# +# If you are using GCC 2.2.2 or higher on a DPX/2, just use: +# +# CC = gcc -ansi +# +# For HP/UX 10.20 with GCC: +# CC = gcc -D_POSIX_SOURCE +# +# if your make doesn't define a default SHELL properly, you may need +# the line below (Atari users will need a bourne work-alike) +# SHELL = /bin/sh +# for Atari +# SHELL=E:/GEMINI2/MUPFEL.TTP + +# flags may have to be changed as required +# flags for 286 Xenix: +# CFLAGS = -Ml2t16 -O -LARGE -I../include +# LFLAGS = -Ml -F 4000 -SEG 512 + +# flags for 286 Microport SysV-AT +# CFLAGS = -DDUMB -Ml -I../include +# LFLAGS = -Ml + +# flags for Atari GCC (3.2.1) +# CFLAGS = -O -I../include +# LFLAGS = -s +# flags for Atari GCC (3.3) +# CFLAGS = -mshort -O2 -I../include +# LFLAGS = -mshort -s + +# flags for Apollos using their native cc +# (as long as it claims to be __STDC__ but isn't) +# CFLAGS = -DAPOLLO -O -I../include + +# flags for AIX 3.1 cc on IBM RS/6000 to define +# a suitable subset of standard libraries +# (note that there is more info regarding the "-qchars=signed" +# switch in file Install.unx note 8) +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -O -I../include -qchars=signed +# and for AIX 3.2: +# CFLAGS = -D_NO_PROTO -D_XOPEN_SOURCE -D_ALL_SOURCE -O -I../include -qchars=signed + +# flags for A/UX 2.01 using native cc or c89 +# gcc predefines AUX so that's not needed there +# CFLAGS = -ZS -D_POSIX_SOURCE -O -I../include -DAUX + +# flags for IRIX 4.0.x using native cc +# SGI cc 3.10 will fail to compile makedefs with -O +# CFLAGS = -I../include -D__STDC__ -woff 100,293 + +# flags for Linux +# compile normally +# CFLAGS = -O2 -fomit-frame-pointer -I../include +# LFLAGS = -L/usr/X11R6/lib +# OR compile backwards compatible a.out format +# CFLAGS = -O2 -b i486-linuxaout -fomit-frame-pointer -I../include +# LFLAGS = -b i486-linuxaout -L/usr/X11R6/lib + +# flags for BeOS using the command line +# remember to uncomment flex and bison below +# BeOS on a Mac/BeBox: +#CC = mwcc +#CFLAGS = -I../include +# BeOS on Intel: +# the default values are fine + +# flags for debugging: +# CFLAGS = -g -I../include + +CFLAGS = -O -I../include +LFLAGS = + +LIBS = + +# If you are cross-compiling, you must use this: +#OBJDIR = . +# otherwise, you can save a little bit of disk space with this: +OBJDIR = ../src + +# yacc/lex programs to use to generate *_comp.h, *_lex.c, and *_yacc.c. +# if, instead of yacc/lex you have bison/flex, comment/uncomment the following. +YACC = yacc +LEX = lex +# YACC = bison -y +# YACC = byacc +# LEX = flex + +# these are the names of the output files from YACC/LEX. Under MS-DOS +# and similar systems, they may differ +YTABC = y.tab.c +YTABH = y.tab.h +LEXYYC = lex.yy.c +# YTABC = y_tab.c +# YTABH = y_tab.h +# LEXYYC = lexyy.c + + + +# ---------------------------------------- +# +# Nothing below this line should have to be changed. + +# timestamps for primary header files, matching src/Makefile +CONFIG_H = ../src/config.h-t +HACK_H = ../src/hack.h-t + +# utility .c files +MAKESRC = makedefs.c +SPLEVSRC = lev_yacc.c lev_lex.c lev_main.c +DGNCOMPSRC = dgn_yacc.c dgn_lex.c dgn_main.c +RECOVSRC = recover.c +DLBSRC = dlb_main.c +UTILSRCS = $(MAKESRC) panic.c $(SPLEVSRC) $(DGNCOMPSRC) $(RECOVSRC) $(DLBSRC) + +# files that define all monsters and objects +CMONOBJ = ../src/monst.c ../src/objects.c +OMONOBJ = $(OBJDIR)/monst.o $(OBJDIR)/objects.o +# files that provide access to NetHack's names +CNAMING = ../src/drawing.c ../src/decl.c $(CMONOBJ) +ONAMING = $(OBJDIR)/drawing.o $(OBJDIR)/decl.o $(OMONOBJ) +# dynamic memory allocation +CALLOC = ../src/alloc.c panic.c +OALLOC = $(OBJDIR)/alloc.o panic.o + +# object files for makedefs +MAKEOBJS = makedefs.o $(OMONOBJ) + +# object files for special levels compiler +SPLEVOBJS = lev_yacc.o lev_lex.o lev_main.o $(OALLOC) $(ONAMING) + +# object files for dungeon compiler +DGNCOMPOBJS = dgn_yacc.o dgn_lex.o dgn_main.o $(OALLOC) + +# object files for recovery utility +RECOVOBJS = recover.o + +# object files for the data librarian +DLBOBJS = dlb_main.o $(OBJDIR)/dlb.o $(OALLOC) + +# flags for creating distribution versions of sys/share/*_lex.c, using +# a more portable flex skeleton, which is not included in the distribution. +# hopefully keeping this out of the section to be edited will keep too +# many people from being confused by it... +# FLEXDIST = -L -S../sys/share/flexhack.skl +FLEXDIST = +# +# flags for creating distribution versions of sys/share/*_yacc.c, without +# line numbers so patches from version to version are practical +# YACCDIST = -l +YACCDIST = + + +# dependencies for makedefs +# +makedefs: $(MAKEOBJS) + $(CC) $(LFLAGS) -o makedefs $(MAKEOBJS) + +makedefs.o: makedefs.c $(CONFIG_H) ../include/permonst.h \ + ../include/objclass.h ../include/monsym.h \ + ../include/artilist.h ../include/dungeon.h ../include/obj.h \ + ../include/monst.h ../include/you.h ../include/flag.h \ + ../include/dlb.h ../include/patchlevel.h ../include/qtext.h + +../include/onames.h: makedefs + ./makedefs -o +../include/pm.h: makedefs + ./makedefs -p +../src/monstr.c: makedefs + ./makedefs -m +../include/vis_tab.h: makedefs + ./makedefs -z +# makedefs -z makes both vis_tab.h and vis_tab.c, but writes the .h first +../src/vis_tab.c: ../include/vis_tab.h + +lintdefs: + @lint -axbh -I../include -DLINT $(MAKESRC) $(CMONOBJ) | sed '/_flsbuf/d' + + +# we defer this makedefs call to the src Makefile, since it knows all about +# the main src and include files date.h is a timestamp for +../include/date.h:: + @( cd ../src ; $(MAKE) ../include/date.h ) + +# support code used by several of the utility programs (but not makedefs) +panic.o: panic.c $(CONFIG_H) + + +# dependencies for lev_comp +# +lev_comp: $(SPLEVOBJS) + $(CC) $(LFLAGS) -o lev_comp $(SPLEVOBJS) $(LIBS) + +lev_yacc.o: lev_yacc.c $(HACK_H) ../include/sp_lev.h +lev_main.o: lev_main.c $(HACK_H) ../include/sp_lev.h ../include/tcap.h \ + ../include/date.h + +# see lev_comp.l for WEIRD_LEX discussion +# egrep will return failure if it doesn't find anything, but we know there +# is one "_cplusplus" inside a comment +lev_lex.o: lev_lex.c $(HACK_H) ../include/lev_comp.h ../include/sp_lev.h + @echo $(CC) -c $(CFLAGS) lev_lex.c + @$(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus lev_lex.c` lev_lex.c + +../include/lev_comp.h: lev_yacc.c + +lev_yacc.c: lev_comp.y + $(YACC) $(YACCDIST) -d lev_comp.y + mv $(YTABC) lev_yacc.c + mv $(YTABH) ../include/lev_comp.h + +lev_lex.c: lev_comp.l + $(LEX) $(FLEXDIST) lev_comp.l + mv $(LEXYYC) lev_lex.c + +# with all of extern.h's functions to complain about, we drown in +# 'defined but not used' without -u +lintlev: + @lint -axhu -I../include -DLINT $(SPLEVSRC) $(CALLOC) $(CNAMING) | sed '/_flsbuf/d' + + +# dependencies for dgn_comp +# +dgn_comp: $(DGNCOMPOBJS) + $(CC) $(LFLAGS) -o dgn_comp $(DGNCOMPOBJS) $(LIBS) + +dgn_yacc.o: dgn_yacc.c $(CONFIG_H) ../include/dgn_file.h ../include/date.h +dgn_main.o: dgn_main.c $(CONFIG_H) ../include/dlb.h + +# see dgn_comp.l for WEIRD_LEX discussion +dgn_lex.o: dgn_lex.c $(CONFIG.H) ../include/dgn_comp.h ../include/dgn_file.h + @echo $(CC) -c $(CFLAGS) dgn_lex.c + @$(CC) -c $(CFLAGS) -DWEIRD_LEX=`egrep -c _cplusplus dgn_lex.c` dgn_lex.c + + +../include/dgn_comp.h: dgn_yacc.c + +dgn_yacc.c: dgn_comp.y + $(YACC) $(YACCDIST) -d dgn_comp.y + mv $(YTABC) dgn_yacc.c + mv $(YTABH) ../include/dgn_comp.h + +dgn_lex.c: dgn_comp.l + $(LEX) $(FLEXDIST) dgn_comp.l + mv $(LEXYYC) dgn_lex.c + +# with all of extern.h's functions to complain about, we drown in +# 'defined but not used' without -u +lintdgn: + @lint -axhu -I../include -DLINT $(DGNCOMPSRC) $(CALLOC) | sed '/_flsbuf/d' + + +# dependencies for recover +# +recover: $(RECOVOBJS) + $(CC) $(LFLAGS) -o recover $(RECOVOBJS) $(LIBS) + +recover.o: recover.c $(CONFIG_H) ../include/date.h + + +# dependencies for dlb +# +dlb: $(DLBOBJS) + $(CC) $(LFLAGS) -o dlb $(DLBOBJS) $(LIBS) + +dlb_main.o: dlb_main.c $(CONFIG_H) ../include/dlb.h ../include/date.h + $(CC) $(CFLAGS) -c dlb_main.c + + + +# dependencies for tile utilities +# +TEXT_IO = tiletext.o tiletxt.o $(ONAMING) +GIFREADERS = gifread.o $(OALLOC) +PPMWRITERS = ppmwrite.o $(OALLOC) + +tileutils: tilemap gif2txt txt2ppm tile2x11 + +gif2txt: $(GIFREADERS) $(TEXT_IO) + $(CC) $(LFLAGS) -o gif2txt $(GIFREADERS) $(TEXT_IO) $(LIBS) +txt2ppm: $(PPMWRITERS) $(TEXT_IO) + $(CC) $(LFLAGS) -o txt2ppm $(PPMWRITERS) $(TEXT_IO) $(LIBS) + +tile2x11: tile2x11.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2x11 tile2x11.o $(TEXT_IO) $(LIBS) + +tile2img.ttp: tile2img.o bitmfile.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2img.ttp tile2img.o bitmfile.o $(TEXT_IO) $(LIBS) + +tile2bmp: tile2bmp.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2bmp tile2bmp.o $(TEXT_IO) + +xpm2img.ttp: xpm2img.o bitmfile.o + $(CC) $(LFLAGS) -o xpm2img.ttp xpm2img.o bitmfile.o $(LIBS) + +tile2beos: tile2beos.o $(TEXT_IO) + $(CC) $(LFLAGS) -o tile2beos tile2beos.o $(TEXT_IO) -lbe + +tilemap: ../win/share/tilemap.c $(HACK_H) + $(CC) $(CFLAGS) $(LFLAGS) -o tilemap ../win/share/tilemap.c $(LIBS) +../src/tile.c: tilemap + ./tilemap + +../include/tile.h: ../win/share/tile.h + cp ../win/share/tile.h ../include/tile.h +tiletext.o: ../win/share/tiletext.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/tiletext.c +tiletxt.o: ../win/share/tilemap.c $(HACK_H) + $(CC) $(CFLAGS) -c -DTILETEXT ../win/share/tilemap.c + mv tilemap.o tiletxt.o + +gifread.o: ../win/share/gifread.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/gifread.c +ppmwrite.o: ../win/share/ppmwrite.c $(CONFIG_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/ppmwrite.c + +tile2bmp.o: ../win/share/tile2bmp.c $(HACK_H) ../include/tile.h + $(CC) $(CFLAGS) -c ../win/share/tile2bmp.c + +tile2x11.o: ../win/X11/tile2x11.c $(HACK_H) ../include/tile.h \ + ../include/tile2x11.h + $(CC) $(CFLAGS) -c ../win/X11/tile2x11.c + +tile2img.o: ../win/gem/tile2img.c $(HACK_H) ../include/tile.h \ + ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/tile2img.c +xpm2img.o: ../win/gem/xpm2img.c $(HACK_H) ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/xpm2img.c +bitmfile.o: ../win/gem/bitmfile.c ../include/bitmfile.h + $(CC) $(CFLAGS) -c ../win/gem/bitmfile.c + +tile2beos.o: ../win/BeOS/tile2beos.cpp $(HACK_H) ../include/tile.h + $(CXX) $(CFLAGS) -c ../win/BeOS/tile2beos.cpp + +# using dependencies like +# ../src/foo:: +# @( cd ../src ; $(MAKE) foo ) +# would always force foo to be up-to-date according to the src Makefile +# when it's needed here. unfortunately, some makes believe this syntax +# means foo always changes, instead of foo should always be checked. +# therefore, approximate via config.h dependencies, and hope that anybody +# changing anything other than basic configuration also knows when not +# to improvise things not in the instructions, like 'make makedefs' here +# in util... + +# make sure object files from src are available when needed +# +$(OBJDIR)/alloc.o: ../src/alloc.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/alloc.c -o $@ +$(OBJDIR)/drawing.o: ../src/drawing.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/drawing.c -o $@ +$(OBJDIR)/decl.o: ../src/decl.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/decl.c -o $@ +$(OBJDIR)/monst.o: ../src/monst.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/monst.c -o $@ +$(OBJDIR)/objects.o: ../src/objects.c $(CONFIG_H) + $(CC) $(CFLAGS) -c ../src/objects.c -o $@ +$(OBJDIR)/dlb.o: ../src/dlb.c $(HACK_H) ../include/dlb.h + $(CC) $(CFLAGS) -c ../src/dlb.c -o $@ + +# make sure hack.h dependencies get transitive information +$(HACK_H): $(CONFIG_H) + @( cd ../src ; $(MAKE) $(HACK_H) ) +$(CONFIG_H): ../include/config.h + @( cd ../src ; $(MAKE) $(CONFIG_H) ) + +tags: $(UTILSRCS) + @ctags -tw $(UTILSRCS) + +clean: + -rm -f *.o + +spotless: clean + -rm -f lev_lex.c lev_yacc.c dgn_lex.c dgn_yacc.c + -rm -f ../include/lev_comp.h ../include/dgn_comp.h + -rm -f ../include/tile.h + -rm -f makedefs lev_comp dgn_comp recover dlb + -rm -f gif2txt txt2ppm tile2x11 tile2img.ttp xpm2img.ttp tilemap + +tileedit: tileedit.cpp $(TEXT_IO) + $(QTDIR)/bin/moc -o tileedit.moc tileedit.h + $(CC) -o tileedit -I../include -I$(QTDIR)/include -L$(QTDIR)/lib tileedit.cpp $(TEXT_IO) -lqt diff --git a/sys/unix/README.linux b/sys/unix/README.linux new file mode 100644 index 0000000..f702a69 --- /dev/null +++ b/sys/unix/README.linux @@ -0,0 +1,107 @@ +NetHack 3.4.3 Linux Elf + +This README provides the instructions for using the official Linux binary, +system platform requirements, as well as steps used to create that binary. +The same steps can be used from the source distribution to create a similar +binary. + +The official Linux binary has support for tty and X11 windowing systems, but +not Qt. This means you will need to have X11 libraries installed on your +system to run this binary, even in its tty flavor. + + +The Linux binary package assumes that you have a user and a group named +"games" on your system. If you do not, you can simplify installation by +creating them first. + +Log in as or su to "root". Then, cd /, gunzip and untar the package, +preserving permissions to put the NetHack files in /usr/games/nethack and +/usr/games/lib/nethackdir. For example, if the package in in your +home directory you might perform these steps. + % su + # cd / + # tar xpvzf ~yourlogin/nethack-343-linux-X11.tgz + +If you have old record and logfile entries from a previous NetHack version, +you might want to save copies before they get overwritten by the new empty +files; old saved games and bones files from 3.4.x will work with 3.4.3. +If you are installing from the RPM, there is no need to save the old record +and logfile; they are automatically preserved. + +In addition to data files for running the game, you will find other useful +things in /usr/games/lib/nethackdir (such as a copy of this README :-). + +The general documentation Guidebook.txt and the processed man pages +nethack.txt and recover.txt should provide an introduction to the game. + +The sample config file called dot.nethackrc can be used by copying +it to your home directory as .nethackrc and modifying it to your liking. + +If you are running X11 copy the nh10.pcf and ibm.pcf font files from +/usr/games/lib/nethackdir to a X11 fonts directory (such as +/usr/X11/lib/X11/fonts/misc) and run "mkfontdir", then restart X +windows to load them. If you prefer to use the graphical tiles, +add the following to your .Xdefaults or .Xresources file: + NetHack.tile_file: x11tiles +You may need to run "xrdb -merge $HOME/.Xdefaults" (or .Xresources) after +doing this. + +The official Linux binary is set up to run setgid games, which allows +multiple users on your system to play the game and prevents cheating by +unprivileged users. The usual default for NetHack is setuid games, but +this causes problems with accessing .nethackrc on distributions with +restrictive default security on home directories and users who don't know +the tradeoffs of various permission settings. + + +If you have problems, send us some email. + +nethack-bugs@nethack.org + + + +Steps used to build this binary release, in addition to the basic +instructions found in sys/unix/Install.unx. The step numbers below +correspond to the step numbers in sys/unix/Install.unx. + +System: gcc-3.2, XFree86-libs-4.2.1, ncurses-5.2, glibc-2.3.2 (GLIBC_2.3) + +3. Edit include/config.h and include/unixconf.h + config.h: define X11_GRAPHICS window support. + define USE_XPM support. + define COMPRESS as /bin/gzip as that is where it + seems to reside on newer Linux's. + define COMPRESS_EXTENSION as ".gz" + define DLB + define AUTOPICKUP_EXCEPTIONS + + unixconf.h: define LINUX + define TIMED_DELAY + +6. Makefile.src: define modern, non-BSD Linux and linux options throughout + CC = gcc + LFLAGS = -L/usr/X11R6/lib + WINSRC = $(WINTTYSRC) $(WINX11SRC) + WINOBJ = $(WINTTYOBJ) $(WINX11OBJ) + WINTTYLIB = /usr/lib/libncurses.a + WINX11LIB = -lXaw -lXmu -lXext -lXt -lXpm -lX11 + WINLIB = $(WINTTYLIB) $(WINX11LIB) + + Makefile.utl: define modern, non-BSD Linux and linux options throughout + Use bison/flex instead of yacc/lex + CC = gcc + LFLAGS = -L/usr/X11R6/lib + YACC = bison -y + LEX = flex + +7. Makefile.top: GAMEGRP = games + GAMEPERM = 02755 + FILEPERM = 0664 + EXEPERM = 0755 + DIRPERM = 0775 + VARDATND = x11tiles NetHack.ad pet_mark.xbm rip.xpm + + make all; su; make install + +9. Additional step: As discussed in win/X11/Install.X11, convert nh10.bdf + and ibm.bdf to proper font files and place in font path. diff --git a/sys/unix/cpp1.shr b/sys/unix/cpp1.shr new file mode 100644 index 0000000..f2b133d --- /dev/null +++ b/sys/unix/cpp1.shr @@ -0,0 +1,1783 @@ +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +# makefile.txt +# readme.txt +# cpp.mem +# cpp.h +# cppdef.h +# cpp2.c +# +echo x - makefile.txt +sed 's/^X//' >makefile.txt << 'END-of-makefile.txt' +X# +X# The redefinition of strchr() and strrchr() are needed for +X# Ultrix-32, Unix 4.2 bsd (and maybe some other Unices). +X# +XBSDDEFINE = -Dstrchr=index -Dstrrchr=rindex +X# +X# On certain systems, such as Unix System III, you may need to define +X# $(LINTFLAGS) in the make command line to set system-specific lint flags. +X# +X# This Makefile assumes cpp will replace the "standard" preprocessor. +X# Delete the reference to -DLINE_PREFIX=\"\" if cpp is used stand-alone. +X# LINEFIX is a sed script filter that reinserts #line -- used for testing +X# if LINE_PREFIX is set to "". Note that we must stand on our heads to +X# match the # and a line had better not begin with $. By the way, what +X# we really want is +X# LINEFIX = | sed "s/^#/#line/" +X# +XCPPDEFINE = -DLINE_PREFIX=\"\" +XLINEFIX = | sed "s/^[^ !\"%-~]/&line/" +X# +X# Define OLD_PREPROCESSOR non-zero to make a preprocessor which is +X# "as compatible as possible" with the standard Unix V7 or Ultrix +X# preprocessors. This is needed to rebuild 4.2bsd, for example, as +X# the preprocessor is used to modify assembler code, rather than C. +X# This is not recommended for current development. OLD_PREPROCESSOR +X# forces the following definitions: +X# OK_DOLLAR FALSE $ is not allowed in variables +X# OK_CONCAT FALSE # cannot concatenate tokens +X# COMMENT_INVISIBLE TRUE old-style comment concatenation +X# STRING_FORMAL TRUE old-style string expansion +X# +XOLDDEFINE = -DOLD_PREPROCESSOR=1 +X# +X# DEFINES collects all -D arguments for cc and lint: +X# Change DEFINES = $(BSDDEFINE) $(CPPDEFINE) $(OLDDEFINE) +X# for an old-style preprocessor. +X# +X# DEFINES = $(BSDDEFINE) $(CPPDEFINE) +XDEFINES = $(CPPDEFINE) +X +XCFLAGS = -O $(DEFINES) +X +X# +X# ** compile cpp +X# +XSRCS = cpp1.c cpp2.c cpp3.c cpp4.c cpp5.c cpp6.c +XOBJS = cpp1.o cpp2.o cpp3.o cpp4.o cpp5.o cpp6.o +Xcpp: $(OBJS) +X $(CC) $(CFLAGS) $(OBJS) -o cpp +X +X# +X# ** Test cpp by preprocessing itself, compiling the result, +X# ** repeating the process and diff'ing the result. Note: this +X# ** is not a good test of cpp, but a simple verification. +X# ** The diff's should not report any changes. +X# ** Note that a sed script may be executed for each compile +X# +Xtest: +X cpp cpp1.c $(LINEFIX) >old.tmp1.c +X cpp cpp2.c $(LINEFIX) >old.tmp2.c +X cpp cpp3.c $(LINEFIX) >old.tmp3.c +X cpp cpp4.c $(LINEFIX) >old.tmp4.c +X cpp cpp5.c $(LINEFIX) >old.tmp5.c +X cpp cpp6.c $(LINEFIX) >old.tmp6.c +X $(CC) $(CFLAGS) old.tmp[123456].c +X a.out cpp1.c >new.tmp1.c +X a.out cpp2.c >new.tmp2.c +X a.out cpp3.c >new.tmp3.c +X a.out cpp4.c >new.tmp4.c +X a.out cpp5.c >new.tmp5.c +X a.out cpp6.c >new.tmp6.c +X diff old.tmp1.c new.tmp1.c +X diff old.tmp2.c new.tmp2.c +X diff old.tmp3.c new.tmp3.c +X diff old.tmp4.c new.tmp4.c +X diff old.tmp5.c new.tmp5.c +X diff old.tmp6.c new.tmp6.c +X rm a.out old.tmp[123456].* new.tmp[123456].* +X +X# +X# A somewhat more extensive test is provided by the "clock" +X# program (which is not distributed). Substitute your favorite +X# macro-rich program here. +X# +Xclock: clock.c cpp +X cpp clock.c $(LINEFIX) >temp.cpp.c +X cc temp.cpp.c -lcurses -ltermcap -o clock +X rm temp.cpp.c +X +X# +X# ** Lint the code +X# +X +Xlint: $(SRCS) +X lint $(LINTFLAGS) $(DEFINES) $(SRCS) +X +X# +X# ** Remove unneeded files +X# +Xclean: +X rm -f $(OBJS) cpp +X +X# +X# ** Rebuild the archive files needed to distribute cpp +X# ** Uses the Decus C archive utility. +X# +X +Xarchc: archc.c +X $(CC) $(CFLAGS) archc.c -o archc +X +Xarchx: archx.c +X $(CC) $(CFLAGS) archx.c -o archx +X +Xarchive: archc +X archc readme.txt cpp.mem archx.c archc.c cpp.rno makefile.txt \ +X cpp*.h >cpp1.arc +X archc cpp1.c cpp2.c cpp3.c >cpp2.arc +X archc cpp4.c cpp5.c cpp6.c >cpp3.arc +X +X# +X# Object module dependencies +X# +X +Xcpp1.o : cpp1.c cpp.h cppdef.h +X +Xcpp2.o : cpp2.c cpp.h cppdef.h +X +Xcpp3.o : cpp3.c cpp.h cppdef.h +X +Xcpp4.o : cpp4.c cpp.h cppdef.h +X +Xcpp5.o : cpp5.c cpp.h cppdef.h +X +Xcpp6.o : cpp6.c cpp.h cppdef.h +X +X +END-of-makefile.txt +echo x - readme.txt +sed 's/^X//' >readme.txt << 'END-of-readme.txt' +X +XDecus cpp is a public-domain implementation of the C preprocessor. +XIt runs on VMS native (Vax C), VMS compatibilty mode (Decus C), +XRSX-11M, RSTS/E, P/OS, and RT11, as well as on several varieties +Xof Unix, including Ultrix. Decus cpp attempts to implement features +Xin the Draft ANSI Standard for the C language. It should be noted, +Xhowever, that this standard is under active development: the current +Xdraft of the standard explicitly states that "readers are requested +Xnot to specify or claim conformance to this draft." Thus readers +Xand users of Decus cpp should not assume that it conforms to the +Xdraft standard, or that it will conform to the actual C language +Xstandard. +X +XThese notes describe how to extract the cpp source files, configure it +Xfor your needs, and mention a few design decisions that may be of interest +Xto maintainers. +X +X Installation +X +XBecause the primary development of cpp was not on Unix, it +Xis distributed using the Decus C archive program (quite similar +Xto the archiver published in Kernighan and Plauger's Software +XTools). To extract the files from the net.sources distribution, +Xsave this message as cpp1.arc and the other two distribution +Xfiles as cpp2.arc and cpp3.arc. Then, using your favorite editor, +Xlocate the archx.c program, just following the line beginning with +X"-h- archx.c" -- the format of the distribution is just: +X +X -h- readme.txt +X ... this file +X -h- cpp.mem +X ... description of cpp +X -h- archx.c +X ... archx.c program -- extracts archives +X -h- archc.c +X ... archc.c program -- creates archives +X +XCompile archx.c -- it shouldn't require any special editing. +XThen run it as follows: +X +X archx *.arc +X +XYou do not need to remove mail headers from the saved messages. +X +XYou should then read through cppdef.h to make sure the HOST and +XTARGET (and other implementation-specific) definitions are set +Xcorrectly for your machine, editing them as needed. +X +XYou may then copy makefile.txt to Makefile, editing it as needed +Xfor your particular system. On Unix, cpp should be compiled +Xby make without further difficulty. On other operating systems, +Xyou should compile the six source modules, linking them together. +XNote that, on Decus C based systems, you must extend the default +Xstack allocation. The Decus C build utility will create the +Xappropriate command file. +X +X Support Notes +X +XThe USENET distribution kit was designed to keep all submissions around +X50,000 bytes: +X +Xcpp1.arc: +X readme.txt This file +X cpp.mem Documentation page (see below) +X archx.c Archive extraction program +X archc.c Archive construction program +X cpp.rno Source for cpp.mem (see below) +X makefile.txt Unix makefile -- copy to Makefile +X cpp.h Main header file (structure def's and globals) +X cppdef.h Configuration file (host and target definitions) +X +Xcpp2.arc: +X cpp1.c Mainline code, documentation master sources +X cpp2.c most #control processing +X cpp3.c filename stuff and command line parsing +Xcpp3.arc: +X cpp4.c #define processor +X cpp5.c #if processor +X cpp6.c Support code (symbol table and I/O routines) +X +XCpp intentionally does not rely on the presence of a full-scale +Xmacro preprocessor, it does require the simple parameter substitution +Xpreprocessor capabilities of Unix V6 and Decus C. If your C +Xlanguage lacks full preprocessing, you should make sure "nomacargs" +Xis #define'd in cpp.h. (This is done automatically by the Decus C +Xcompiler.) +X +XThe documentation (manual page) for cpp is included as cpp.mem +Xand cpp.rno. Cpp.rno is in Dec Runoff format, built by a Decus C +Xutility (getrno) from original source which is embedded in cpp1.c. +XTo my knowledge, there is no equivalent program that creates +Xthe nroff source appropriate for Unix. +X +XI would be happy to receive fixes to any problems you encounter. +XAs I do not maintain distribution kit base-levels, bare-bones +Xdiff listings without sufficient context are not very useful. +XIt is unlikely that I can find time to help you with other +Xdifficulties. +X +X Acknowledgements +X +XI received a great deal of help from many people in debugging cpp. +XAlan Feuer and Sam Kendall used "state of the art" run-time code +Xcheckers to locate several errors. Ed Keiser found problems when +Xcpp was used on machines with different int and pointer sizes. +XDave Conroy helped with the initial debugging, while Arthur Olsen +Xand George Rosenberg found (and solved) several problems in the +Xfirst USENET release. +X +XMartin Minow +Xdecvax!minow +X +END-of-readme.txt +echo x - cpp.mem +sed 's/^X//' >cpp.mem << 'END-of-cpp.mem' +X +X +X +X +X 1.0 C Pre-Processor +X +X +X +X ******* +X * cpp * +X ******* +X +X +X +X NAME: cpp -- C Pre-Processor +X +X SYNOPSIS: +X +X cpp [-options] [infile [outfile]] +X +X DESCRIPTION: +X +X CPP reads a C source file, expands macros and include +X files, and writes an input file for the C compiler. If +X no file arguments are given, CPP reads from stdin and +X writes to stdout. If one file argument is given, it +X will define the input file, while two file arguments +X define both input and output files. The file name "-" +X is a synonym for stdin or stdout as appropriate. +X +X The following options are supported. Options may be +X given in either case. +X +X -C If set, source-file comments are written +X to the output file. This allows the +X output of CPP to be used as the input to +X a program, such as lint, that expects +X commands embedded in specially-formatted +X comments. +X +X -Dname=value Define the name as if the programmer +X wrote +X +X #define name value +X +X at the start of the first file. If +X "=value" is not given, a value of "1" +X will be used. +X +X On non-unix systems, all alphabetic text +X will be forced to upper-case. +X +X -E Always return "success" to the operating +X system, even if errors were detected. +X Note that some fatal errors, such as a +X missing #include file, will terminate +X CPP, returning "failure" even if the -E +X option is given. +X Page 2 +X cpp C Pre-Processor +X +X +X -Idirectory Add this directory to the list of +X directories searched for #include "..." +X and #include <...> commands. Note that +X there is no space between the "-I" and +X the directory string. More than one -I +X command is permitted. On non-Unix +X systems "directory" is forced to +X upper-case. +X +X -N CPP normally predefines some symbols +X defining the target computer and +X operating system. If -N is specified, +X no symbols will be predefined. If -N -N +X is specified, the "always present" +X symbols, __LINE__, __FILE__, and +X __DATE__ are not defined. +X +X -Stext CPP normally assumes that the size of +X the target computer's basic variable +X types is the same as the size of these +X types of the host computer. (This can +X be overridden when CPP is compiled, +X however.) The -S option allows dynamic +X respecification of these values. "text" +X is a string of numbers, separated by +X commas, that specifies correct sizes. +X The sizes must be specified in the exact +X order: +X +X char short int long float double +X +X If you specify the option as "-S*text", +X pointers to these types will be +X specified. -S* takes one additional +X argument for pointer to function (e.g. +X int (*)()) +X +X For example, to specify sizes +X appropriate for a PDP-11, you would +X write: +X +X c s i l f d func +X -S1,2,2,2,4,8, +X -S*2,2,2,2,2,2,2 +X +X Note that all values must be specified. +X +X -Uname Undefine the name as if +X +X #undef name +X +X were given. On non-Unix systems, "name" +X will be forced to upper-case. +X Page 3 +X cpp C Pre-Processor +X +X +X -Xnumber Enable debugging code. If no value is +X given, a value of 1 will be used. (For +X maintenence of CPP only.) +X +X +X PRE-DEFINED VARIABLES: +X +X When CPP begins processing, the following variables will +X have been defined (unless the -N option is specified): +X +X Target computer (as appropriate): +X +X pdp11, vax, M68000 m68000 m68k +X +X Target operating system (as appropriate): +X +X rsx, rt11, vms, unix +X +X Target compiler (as appropriate): +X +X decus, vax11c +X +X The implementor may add definitions to this list. The +X default definitions match the definition of the host +X computer, operating system, and C compiler. +X +X The following are always available unless undefined (or +X -N was specified twice): +X +X __FILE__ The input (or #include) file being +X compiled (as a quoted string). +X +X __LINE__ The line number being compiled. +X +X __DATE__ The date and time of compilation as a +X Unix ctime quoted string (the trailing +X newline is removed). Thus, +X +X printf("Bug at line %s,", __LINE__); +X printf(" source file %s", __FILE__); +X printf(" compiled on %s", __DATE__); +X +X +X DRAFT PROPOSED ANSI STANDARD CONSIDERATIONS: +X +X The current version of the Draft Proposed Standard +X explicitly states that "readers are requested not to +X specify or claim conformance to this draft." Readers and +X users of Decus CPP should not assume that Decus CPP +X conforms to the standard, or that it will conform to the +X actual C Language Standard. +X +X When CPP is itself compiled, many features of the Draft +X Proposed Standard that are incompatible with existing +X Page 4 +X cpp C Pre-Processor +X +X +X preprocessors may be disabled. See the comments in +X CPP's source for details. +X +X The latest version of the Draft Proposed Standard (as +X reflected in Decus CPP) is dated November 12, 1984. +X +X Comments are removed from the input text. The comment +X is replaced by a single space character. The -C option +X preserves comments, writing them to the output file. +X +X The '$' character is considered to be a letter. This is +X a permitted extension. +X +X The following new features of C are processed by CPP: +X +X #elif expression (#else #if) +X '\xNNN' (Hexadecimal constant) +X '\a' (Ascii BELL) +X '\v' (Ascii Vertical Tab) +X #if defined NAME 1 if defined, 0 if not +X #if defined (NAME) 1 if defined, 0 if not +X #if sizeof (basic type) +X unary + +X 123U, 123LU Unsigned ints and longs. +X 12.3L Long double numbers +X token#token Token concatenation +X #include token Expands to filename +X +X The Draft Proposed Standard has extended C, adding a +X constant string concatenation operator, where +X +X "foo" "bar" +X +X is regarded as the single string "foobar". (This does +X not affect CPP's processing but does permit a limited +X form of macro argument substitution into strings as will +X be discussed.) +X +X The Standard Committee plans to add token concatenation +X to #define command lines. One suggested implementation +X is as follows: the sequence "Token1#Token2" is treated +X as if the programmer wrote "Token1Token2". This could +X be used as follows: +X +X #line 123 +X #define ATLINE foo#__LINE__ +X +X ATLINE would be defined as foo123. +X +X Note that "Token2" must either have the format of an +X identifier or be a string of digits. Thus, the string +X +X #define ATLINE foo#1x3 +X Page 5 +X cpp C Pre-Processor +X +X +X generates two tokens: "foo1" and "x3". +X +X If the tokens T1 and T2 are concatenated into T3, this +X implementation operates as follows: +X +X 1. Expand T1 if it is a macro. +X 2. Expand T2 if it is a macro. +X 3. Join the tokens, forming T3. +X 4. Expand T3 if it is a macro. +X +X A macro formal parameter will be substituted into a +X string or character constant if it is the only component +X of that constant: +X +X #define VECSIZE 123 +X #define vprint(name, size) \ +X printf("name" "[" "size" "] = {\n") +X ... vprint(vector, VECSIZE); +X +X expands (effectively) to +X +X vprint("vector[123] = {\n"); +X +X Note that this will be useful if your C compiler +X supports the new string concatenation operation noted +X above. As implemented here, if you write +X +X #define string(arg) "arg" +X ... string("foo") ... +X +X This implementation generates "foo", rather than the +X strictly correct ""foo"" (which will probably generate +X an error message). This is, strictly speaking, an error +X in CPP and may be removed from future releases. +X +X ERROR MESSAGES: +X +X Many. CPP prints warning or error messages if you try +X to use multiple-byte character constants +X (non-transportable) if you #undef a symbol that was not +X defined, or if your program has potentially nested +X comments. +X +X AUTHOR: +X +X Martin Minow +X +X BUGS: +X +X The #if expression processor uses signed integers only. +X I.e, #if 0xFFFFu < 0 may be TRUE. +X +END-of-cpp.mem +echo x - cpp.h +sed 's/^X//' >cpp.h << 'END-of-cpp.h' +X +X/* +X * I n t e r n a l D e f i n i t i o n s f o r C P P +X * +X * In general, definitions in this file should not be changed. +X */ +X +X#ifndef TRUE +X#define TRUE 1 +X#define FALSE 0 +X#endif +X#ifndef EOS +X/* +X * This is predefined in Decus C +X */ +X#define EOS '\0' /* End of string */ +X#endif +X#define EOF_CHAR 0 /* Returned by get() on eof */ +X#define NULLST ((char *) NULL) /* Pointer to nowhere (linted) */ +X#define DEF_NOARGS (-1) /* #define foo vs #define foo() */ +X +X/* +X * The following may need to change if the host system doesn't use ASCII. +X */ +X#define DEF_MAGIC 0x1D /* Magic for #defines */ +X#define TOK_SEP 0x1E /* Token concatenation delim. */ +X#define COM_SEP 0x1F /* Magic comment separator */ +X +X/* +X * Note -- in Ascii, the following will map macro formals onto DEL + the +X * C1 control character region (decimal 128 .. (128 + PAR_MAC)) which will +X * be ok as long as PAR_MAC is less than 33). Note that the last PAR_MAC +X * value is reserved for string substitution. +X */ +X +X#define MAC_PARM 0x7F /* Macro formals start here */ +X#if PAR_MAC >= 33 +X assertion fails -- PAR_MAC isn't less than 33 +X#endif +X#define LASTPARM (PAR_MAC - 1) +X +X/* +X * Character type codes. +X */ +X +X#define INV 0 /* Invalid, must be zero */ +X#define OP_EOE INV /* End of expression */ +X#define DIG 1 /* Digit */ +X#define LET 2 /* Identifier start */ +X#define FIRST_BINOP OP_ADD +X#define OP_ADD 3 +X#define OP_SUB 4 +X#define OP_MUL 5 +X#define OP_DIV 6 +X#define OP_MOD 7 +X#define OP_ASL 8 +X#define OP_ASR 9 +X#define OP_AND 10 /* &, not && */ +X#define OP_OR 11 /* |, not || */ +X#define OP_XOR 12 +X#define OP_EQ 13 +X#define OP_NE 14 +X#define OP_LT 15 +X#define OP_LE 16 +X#define OP_GE 17 +X#define OP_GT 18 +X#define OP_ANA 19 /* && */ +X#define OP_ORO 20 /* || */ +X#define OP_QUE 21 /* ? */ +X#define OP_COL 22 /* : */ +X#define OP_CMA 23 /* , (relevant?) */ +X#define LAST_BINOP OP_CMA /* Last binary operand */ +X/* +X * The following are unary. +X */ +X#define FIRST_UNOP OP_PLU /* First Unary operand */ +X#define OP_PLU 24 /* + (draft ANSI standard) */ +X#define OP_NEG 25 /* - */ +X#define OP_COM 26 /* ~ */ +X#define OP_NOT 27 /* ! */ +X#define LAST_UNOP OP_NOT +X#define OP_LPA 28 /* ( */ +X#define OP_RPA 29 /* ) */ +X#define OP_END 30 /* End of expression marker */ +X#define OP_MAX (OP_END + 1) /* Number of operators */ +X#define OP_FAIL (OP_END + 1) /* For error returns */ +X +X/* +X * The following are for lexical scanning only. +X */ +X +X#define QUO 65 /* Both flavors of quotation */ +X#define DOT 66 /* . might start a number */ +X#define SPA 67 /* Space and tab */ +X#define BSH 68 /* Just a backslash */ +X#define END 69 /* EOF */ +X +X/* +X * These bits are set in ifstack[] +X */ +X#define WAS_COMPILING 1 /* TRUE if compile set at entry */ +X#define ELSE_SEEN 2 /* TRUE when #else processed */ +X#define TRUE_SEEN 4 /* TRUE when #if TRUE processed */ +X +X/* +X * Define bits for the basic types and their adjectives +X */ +X +X#define T_CHAR 1 +X#define T_INT 2 +X#define T_FLOAT 4 +X#define T_DOUBLE 8 +X#define T_SHORT 16 +X#define T_LONG 32 +X#define T_SIGNED 64 +X#define T_UNSIGNED 128 +X#define T_PTR 256 /* Pointer */ +X#define T_FPTR 512 /* Pointer to functions */ +X +X/* +X * The DEFBUF structure stores information about #defined +X * macros. Note that the defbuf->repl information is always +X * in malloc storage. +X */ +X +Xtypedef struct defbuf { +X struct defbuf *link; /* Next define in chain */ +X char *repl; /* -> replacement */ +X int hash; /* Symbol table hash */ +X int nargs; /* For define(args) */ +X char name[1]; /* #define name */ +X} DEFBUF; +X +X/* +X * The FILEINFO structure stores information about open files +X * and macros being expanded. +X */ +X +Xtypedef struct fileinfo { +X char *bptr; /* Buffer pointer */ +X int line; /* for include or macro */ +X FILE *fp; /* File if non-null */ +X struct fileinfo *parent; /* Link to includer */ +X char *filename; /* File/macro name */ +X char *progname; /* From #line statement */ +X unsigned int unrecur; /* For macro recursion */ +X char buffer[1]; /* current input line */ +X} FILEINFO; +X +X/* +X * The SIZES structure is used to store the values for #if sizeof +X */ +X +Xtypedef struct sizes { +X short bits; /* If this bit is set, */ +X short size; /* this is the datum size value */ +X short psize; /* this is the pointer size */ +X} SIZES; +X/* +X * nomacarg is a built-in #define on Decus C. +X */ +X +X#ifdef nomacarg +X#define cput output /* cput concatenates tokens */ +X#else +X#if COMMENT_INVISIBLE +X#define cput(c) { if (c != TOK_SEP && c != COM_SEP) putchar(c); } +X#else +X#define cput(c) { if (c != TOK_SEP) putchar(c); } +X#endif +X#endif +X +X#ifndef nomacarg +X#define streq(s1, s2) (strcmp(s1, s2) == 0) +X#endif +X +X/* +X * Error codes. VMS uses system definitions. +X * Decus C codes are defined in stdio.h. +X * Others are cooked to order. +X */ +X +X#if HOST == SYS_VMS +X#include +X#include +X#define IO_NORMAL (SS$_NORMAL | STS$M_INHIB_MSG) +X#define IO_ERROR SS$_ABORT +X#endif +X/* +X * Note: IO_NORMAL and IO_ERROR are defined in the Decus C stdio.h file +X */ +X#ifndef IO_NORMAL +X#define IO_NORMAL 0 +X#endif +X#ifndef IO_ERROR +X#define IO_ERROR 1 +X#endif +X +X/* +X * Externs +X */ +X +Xextern int line; /* Current line number */ +Xextern int wrongline; /* Force #line to cc pass 1 */ +Xextern char type[]; /* Character classifier */ +Xextern char token[IDMAX + 1]; /* Current input token */ +Xextern int instring; /* TRUE if scanning string */ +Xextern int inmacro; /* TRUE if scanning #define */ +Xextern int errors; /* Error counter */ +Xextern int recursion; /* Macro depth counter */ +Xextern char ifstack[BLK_NEST]; /* #if information */ +X#define compiling ifstack[0] +Xextern char *ifptr; /* -> current ifstack item */ +Xextern char *incdir[NINCLUDE]; /* -i directories */ +Xextern char **incend; /* -> active end of incdir */ +Xextern int cflag; /* -C option (keep comments) */ +Xextern int eflag; /* -E option (ignore errors) */ +Xextern int nflag; /* -N option (no pre-defines) */ +Xextern int rec_recover; /* unwind recursive macros */ +Xextern char *preset[]; /* Standard predefined symbols */ +Xextern char *magic[]; /* Magic predefined symbols */ +Xextern FILEINFO *infile; /* Current input file */ +Xextern char work[NWORK + 1]; /* #define scratch */ +Xextern char *workp; /* Free space in work */ +X#if DEBUG +Xextern int debug; /* Debug level */ +X#endif +Xextern int keepcomments; /* Don't remove comments if set */ +Xextern SIZES size_table[]; /* For #if sizeof sizes */ +Xextern char *getmem(); /* Get memory or die. */ +Xextern DEFBUF *lookid(); /* Look for a #define'd thing */ +Xextern DEFBUF *defendel(); /* Symbol table enter/delete */ +Xextern char *savestring(); /* Stuff string in malloc mem. */ +Xextern char *strcpy(); +Xextern char *strcat(); +Xextern char *strrchr(); +Xextern char *strchr(); +Xextern long time(); +X/* extern char *sprintf(); /* Lint needs this */ +END-of-cpp.h +echo x - cppdef.h +sed 's/^X//' >cppdef.h << 'END-of-cppdef.h' +X/* +X * S y s t e m D e p e n d e n t +X * D e f i n i t i o n s f o r C P P +X * +X * Definitions in this file may be edited to configure CPP for particular +X * host operating systems and target configurations. +X * +X * NOTE: cpp assumes it is compiled by a compiler that supports macros +X * with arguments. If this is not the case (as for Decus C), #define +X * nomacarg -- and provide function equivalents for all macros. +X * +X * cpp also assumes the host and target implement the Ascii character set. +X * If this is not the case, you will have to do some editing here and there. +X */ +X +X/* +X * This redundant definition of TRUE and FALSE works around +X * a limitation of Decus C. +X */ +X#ifndef TRUE +X#define TRUE 1 +X#define FALSE 0 +X#endif +X +X/* +X * Define the HOST operating system. This is needed so that +X * cpp can use appropriate filename conventions. +X */ +X#define SYS_UNKNOWN 0 +X#define SYS_UNIX 1 +X#define SYS_VMS 2 +X#define SYS_RSX 3 +X#define SYS_RT11 4 +X#define SYS_LATTICE 5 +X#define SYS_ONYX 6 +X#define SYS_68000 7 +X#define SYS_GCOS 8 +X#define SYS_IBM 9 +X#define SYS_OS 10 +X#define SYS_TSS 11 +X +X#ifndef HOST +X#ifdef unix +X#define HOST SYS_UNIX +X#else +X#ifdef vms +X#define HOST SYS_VMS +X#else +X#ifdef rsx +X#define HOST SYS_RSX +X#else +X#ifdef rt11 +X#define HOST SYS_RT11 +X#else +X#ifdef dmert +X#define HOST SYS_DMERT +X#else +X#ifdef gcos +X#define HOST SYS_GCOS +X#else +X#ifdef ibm +X#define HOST SYS_IBM +X#else +X#ifdef os +X#define HOST SYS_OS +X#else +X#ifdef tss +X#define HOST SYS_TSS +X#endif +X#endif +X#endif +X#endif +X#endif +X#endif +X#endif +X#endif +X#endif +X +X#ifndef HOST +X#define HOST SYS_UNKNOWN +X#endif +X +X/* +X * We assume that the target is the same as the host system +X */ +X#ifndef TARGET +X#define TARGET HOST +X#endif +X +X/* +X * In order to predefine machine-dependent constants, +X * several strings are defined here: +X * +X * MACHINE defines the target cpu (by name) +X * SYSTEM defines the target operating system +X * COMPILER defines the target compiler +X * +X * The above may be #defined as "" if they are not wanted. +X * They should not be #defined as NULL. +X * +X * LINE_PREFIX defines the # output line prefix, if not "line" +X * This should be defined as "" if cpp is to replace +X * the "standard" C pre-processor. +X * +X * FILE_LOCAL marks functions which are referenced only in the +X * file they reside. Some C compilers allow these +X * to be marked "static" even though they are referenced +X * by "extern" statements elsewhere. +X * +X * OK_DOLLAR Should be set TRUE if $ is a valid alphabetic character +X * in identifiers (default), or zero if $ is invalid. +X * Default is TRUE. +X * +X * OK_CONCAT Should be set TRUE if # may be used to concatenate +X * tokens in macros (per the Ansi Draft Standard) or +X * FALSE for old-style # processing (needed if cpp is +X * to process assembler source code). +X * +X * OK_DATE Predefines the compilation date if set TRUE. +X * Not permitted by the Nov. 12, 1984 Draft Standard. +X * +X * S_CHAR etc. Define the sizeof the basic TARGET machine word types. +X * By default, sizes are set to the values for the HOST +X * computer. If this is inappropriate, see the code in +X * cpp3.c for details on what to change. Also, if you +X * have a machine where sizeof (signed int) differs from +X * sizeof (unsigned int), you will have to edit code and +X * tables in cpp3.c (and extend the -S option definition.) +X * +X * CPP_LIBRARY May be defined if you have a site-specific include directory +X * which is to be searched *before* the operating-system +X * specific directories. +X */ +X +X#if TARGET == SYS_LATTICE +X/* +X * We assume the operating system is pcdos for the IBM-PC. +X * We also assume the small model (just like the PDP-11) +X */ +X#define MACHINE "i8086" +X#define SYSTEM "pcdos" +X#endif +X +X#if TARGET == SYS_ONYX +X#define MACHINE "z8000" +X#define SYSTEM "unix" +X#endif +X +X#if TARGET == SYS_VMS +X#define MACHINE "vax" +X#define SYSTEM "vms" +X#define COMPILER "vax11c" +X#endif +X +X#if TARGET == SYS_RSX +X#define MACHINE "pdp11" +X#define SYSTEM "rsx" +X#define COMPILER "decus" +X#endif +X +X#if TARGET == SYS_RT11 +X#define MACHINE "pdp11" +X#define SYSTEM "rt11" +X#define COMPILER "decus" +X#endif +X +X#if TARGET == SYS_68000 +X/* +X * All three machine designators have been seen in various systems. +X * Warning -- compilers differ as to sizeof (int). cpp3 assumes that +X * sizeof (int) == 2 +X */ +X#define MACHINE "M68000", "m68000", "m68k" +X#define SYSTEM "unix" +X#endif +X +X#if TARGET == SYS_UNIX +X#define SYSTEM "unix" +X#ifdef pdp11 +X#define MACHINE "pdp11" +X#endif +X#ifdef vax +X#define MACHINE "vax" +X#endif +X#ifdef u370 +X#define MACHINE "u370" +X#endif +X#ifdef interdata +X#define MACHINE "interdata" +X#endif +X#ifdef u3b +X#define MACHINE "u3b" +X#endif +X#ifdef u3b5 +X#define MACHINE "u3b5" +X#endif +X#ifdef u3b2 +X#define MACHINE "u3b2" +X#endif +X#ifdef u3b20d +X#define MACHINE "u3b20d" +X#endif +X#endif +X#endif +X +X/* +X * defaults +X */ +X +X#ifndef MSG_PREFIX +X#define MSG_PREFIX "cpp: " +X#endif +X +X#ifndef LINE_PREFIX +X#ifdef decus +X#define LINE_PREFIX "" +X#else +X#define LINE_PREFIX "line" +X#endif +X#endif +X +X/* +X * OLD_PREPROCESSOR forces the definition of OK_DOLLAR, OK_CONCAT, +X * COMMENT_INVISIBLE, and STRING_FORMAL to values appropriate for +X * an old-style preprocessor. +X */ +X +X#ifndef OLD_PREPROCESSOR +X#define OLD_PREPROCESSOR FALSE +X#endif +X +X#if OLD_PREPROCESSOR +X#define OK_DOLLAR FALSE +X#define OK_CONCAT FALSE +X#define COMMENT_INVISIBLE TRUE +X#define STRING_FORMAL TRUE +X#endif +X +X/* +X * RECURSION_LIMIT may be set to -1 to disable the macro recursion test. +X */ +X#ifndef RECURSION_LIMIT +X#define RECURSION_LIMIT 1000 +X#endif +X +X/* +X * BITS_CHAR may be defined to set the number of bits per character. +X * it is needed only for multi-byte character constants. +X */ +X#ifndef BITS_CHAR +X#define BITS_CHAR 8 +X#endif +X +X/* +X * BIG_ENDIAN is set TRUE on machines (such as the IBM 360 series) +X * where 'ab' stores 'a' in the high-bits and 'b' in the low-bits. +X * It is set FALSE on machines (such as the PDP-11 and Vax-11) +X * where 'ab' stores 'a' in the low-bits and 'b' in the high-bits. +X * (Or is it the other way around?) -- Warning: BIG_ENDIAN code is untested. +X */ +X#ifndef BIG_ENDIAN +X#define BIG_ENDIAN FALSE +X#endif +X +X/* +X * COMMENT_INVISIBLE may be defined to allow "old-style" comment +X * processing, whereby the comment becomes a zero-length token +X * delimiter. This permitted tokens to be concatenated in macro +X * expansions. This was removed from the Draft Ansi Standard. +X */ +X#ifndef COMMENT_INVISIBLE +X#define COMMENT_INVISIBLE FALSE +X#endif +X +X/* +X * STRING_FORMAL may be defined to allow recognition of macro parameters +X * anywhere in replacement strings. This was removed from the Draft Ansi +X * Standard and a limited recognition capability added. +X */ +X#ifndef STRING_FORMAL +X#define STRING_FORMAL FALSE +X#endif +X +X/* +X * OK_DOLLAR enables use of $ as a valid "letter" in identifiers. +X * This is a permitted extension to the Ansi Standard and is required +X * for e.g., VMS, RSX-11M, etc. It should be set FALSE if cpp is +X * used to preprocess assembler source on Unix systems. OLD_PREPROCESSOR +X * sets OK_DOLLAR FALSE for that reason. +X */ +X#ifndef OK_DOLLAR +X#define OK_DOLLAR TRUE +X#endif +X +X/* +X * OK_CONCAT enables (one possible implementation of) token concatenation. +X * If cpp is used to preprocess Unix assembler source, this should be +X * set FALSE as the concatenation character, #, is used by the assembler. +X */ +X#ifndef OK_CONCAT +X#define OK_CONCAT TRUE +X#endif +X +X/* +X * OK_DATE may be enabled to predefine today's date as a string +X * at the start of each compilation. This is apparently not permitted +X * by the Draft Ansi Standard. +X */ +X#ifndef OK_DATE +X#define OK_DATE TRUE +X#endif +X +X/* +X * Some common definitions. +X */ +X +X#ifndef DEBUG +X#define DEBUG FALSE +X#endif +X +X/* +X * The following definitions are used to allocate memory for +X * work buffers. In general, they should not be modified +X * by implementors. +X * +X * PAR_MAC The maximum number of #define parameters (31 per Standard) +X * Note: we need another one for strings. +X * IDMAX The longest identifier, 31 per Ansi Standard +X * NBUFF Input buffer size +X * NWORK Work buffer size -- the longest macro +X * must fit here after expansion. +X * NEXP The nesting depth of #if expressions +X * NINCLUDE The number of directories that may be specified +X * on a per-system basis, or by the -I option. +X * BLK_NEST The number of nested #if's permitted. +X */ +X +X#define IDMAX 31 +X#define PAR_MAC (31 + 1) +X#define NBUFF 1024 +X#define NWORK 1024 +X#define NEXP 128 +X#define NINCLUDE 7 +X#define NPARMWORK (NWORK * 2) +X#define BLK_NEST 32 +X +X/* +X * Some special constants. These may need to be changed if cpp +X * is ported to a wierd machine. +X * +X * NOTE: if cpp is run on a non-ascii machine, ALERT and VT may +X * need to be changed. They are used to implement the proposed +X * ANSI standard C control characters '\a' and '\v' only. +X * DEL is used to tag macro tokens to prevent #define foo foo +X * from looping. Note that we don't try to prevent more elaborate +X * #define loops from occurring. +X */ +X +X#ifndef ALERT +X#define ALERT '\007' /* '\a' is "Bell" */ +X#endif +X +X#ifndef VT +X#define VT '\013' /* Vertical Tab CTRL/K */ +X#endif +X +X +X#ifndef FILE_LOCAL +X#ifdef decus +X#define FILE_LOCAL static +X#else +X#ifdef vax11c +X#define FILE_LOCAL static +X#else +X#define FILE_LOCAL /* Others are global */ +X#endif +X#endif +X#endif +X +END-of-cppdef.h +echo x - cpp2.c +sed 's/^X//' >cpp2.c << 'END-of-cpp2.c' +X/* +X * C P P 2 . C +X * +X * Process #control lines +X * +X * Edit history +X * 13-Nov-84 MM Split from cpp1.c +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X#if HOST == SYS_VMS +X/* +X * Include the rms stuff. (We can't just include rms.h as it uses the +X * VaxC-specific library include syntax that Decus CPP doesn't support. +X * By including things by hand, we can CPP ourself.) +X */ +X#include +X#include +X#include +X#include +X#endif +X +X/* +X * Generate (by hand-inspection) a set of unique values for each control +X * operator. Note that this is not guaranteed to work for non-Ascii +X * machines. CPP won't compile if there are hash conflicts. +X */ +X +X#define L_assert ('a' + ('s' << 1)) +X#define L_define ('d' + ('f' << 1)) +X#define L_elif ('e' + ('i' << 1)) +X#define L_else ('e' + ('s' << 1)) +X#define L_endif ('e' + ('d' << 1)) +X#define L_ident ('i' + ('e' << 1)) +X#define L_if ('i' + (EOS << 1)) +X#define L_ifdef ('i' + ('d' << 1)) +X#define L_ifndef ('i' + ('n' << 1)) +X#define L_include ('i' + ('c' << 1)) +X#define L_line ('l' + ('n' << 1)) +X#define L_nogood (EOS + (EOS << 1)) /* To catch #i */ +X#define L_pragma ('p' + ('a' << 1)) +X#define L_sccs ('s' + ('c' << 1)) +X#define L_undef ('u' + ('d' << 1)) +X#if DEBUG +X#define L_debug ('d' + ('b' << 1)) /* #debug */ +X#define L_nodebug ('n' + ('d' << 1)) /* #nodebug */ +X#endif +X +Xint +Xcontrol(counter) +Xint counter; /* Pending newline counter */ +X/* +X * Process #control lines. Simple commands are processed inline, +X * while complex commands have their own subroutines. +X * +X * The counter is used to force out a newline before #line, and +X * #pragma commands. This prevents these commands from ending up at +X * the end of the previous line if cpp is invoked with the -C option. +X */ +X{ +X register int c; +X register char *tp; +X register int hash; +X char *ep; +X +X c = skipws(); +X if (c == '\n' || c == EOF_CHAR) +X return (counter + 1); +X if (!isdigit(c)) +X scanid(c); /* Get #word to token[] */ +X else { +X unget(); /* Hack -- allow #123 as a */ +X strcpy(token, "line"); /* synonym for #line 123 */ +X } +X hash = (token[1] == EOS) ? L_nogood : (token[0] + (token[2] << 1)); +X switch (hash) { +X case L_assert: tp = "assert"; break; +X case L_define: tp = "define"; break; +X case L_elif: tp = "elif"; break; +X case L_else: tp = "else"; break; +X case L_endif: tp = "endif"; break; +X case L_ident: tp = "ident"; break; +X case L_if: tp = "if"; break; +X case L_ifdef: tp = "ifdef"; break; +X case L_ifndef: tp = "ifndef"; break; +X case L_include: tp = "include"; break; +X case L_line: tp = "line"; break; +X case L_pragma: tp = "pragma"; break; +X case L_sccs: tp = "sccs"; break; +X case L_undef: tp = "undef"; break; +X#if DEBUG +X case L_debug: tp = "debug"; break; +X case L_nodebug: tp = "nodebug"; break; +X#endif +X default: hash = L_nogood; +X case L_nogood: tp = ""; break; +X } +X if (!streq(tp, token)) +X hash = L_nogood; +X /* +X * hash is set to a unique value corresponding to the +X * control keyword (or L_nogood if we think it's nonsense). +X */ +X if (infile->fp == NULL) +X cwarn("Control line \"%s\" within macro expansion", token); +X if (!compiling) { /* Not compiling now */ +X switch (hash) { +X case L_if: /* These can't turn */ +X case L_ifdef: /* compilation on, but */ +X case L_ifndef: /* we must nest #if's */ +X if (++ifptr >= &ifstack[BLK_NEST]) +X goto if_nest_err; +X *ifptr = 0; /* !WAS_COMPILING */ +X case L_line: /* Many */ +X /* +X * Are pragma's always processed? +X */ +X case L_ident: +X case L_sccs: +X case L_pragma: /* options */ +X case L_include: /* are uninteresting */ +X case L_define: /* if we */ +X case L_undef: /* aren't */ +X case L_assert: /* compiling. */ +Xdump_line: skipnl(); /* Ignore rest of line */ +X return (counter + 1); +X } +X } +X /* +X * Make sure that #line and #pragma are output on a fresh line. +X */ +X if (counter > 0 && (hash == L_line || hash == L_pragma)) { +X putchar('\n'); +X counter--; +X } +X switch (hash) { +X case L_line: +X /* +X * Parse the line to update the line number and "progname" +X * field and line number for the next input line. +X * Set wrongline to force it out later. +X */ +X c = skipws(); +X workp = work; /* Save name in work */ +X while (c != '\n' && c != EOF_CHAR) { +X save(c); +X c = get(); +X } +X unget(); +X save(EOS); +X /* +X * Split #line argument into and +X * We subtract 1 as we want the number of the next line. +X */ +X line = atoi(work) - 1; /* Reset line number */ +X for (tp = work; isdigit(*tp) || type[*tp] == SPA; tp++) +X ; /* Skip over digits */ +X if (*tp != EOS) { /* Got a filename, so: */ +X if (*tp == '"' && (ep = strrchr(tp + 1, '"')) != NULL) { +X tp++; /* Skip over left quote */ +X *ep = EOS; /* And ignore right one */ +X } +X if (infile->progname != NULL) /* Give up the old name */ +X free(infile->progname); /* if it's allocated. */ +X infile->progname = savestring(tp); +X } +X wrongline = TRUE; /* Force output later */ +X break; +X +X case L_include: +X doinclude(); +X break; +X +X case L_define: +X dodefine(); +X break; +X +X case L_undef: +X doundef(); +X break; +X +X case L_else: +X if (ifptr == &ifstack[0]) +X goto nest_err; +X else if ((*ifptr & ELSE_SEEN) != 0) +X goto else_seen_err; +X *ifptr |= ELSE_SEEN; +X if ((*ifptr & WAS_COMPILING) != 0) { +X if (compiling || (*ifptr & TRUE_SEEN) != 0) +X compiling = FALSE; +X else { +X compiling = TRUE; +X } +X } +X break; +X +X case L_elif: +X if (ifptr == &ifstack[0]) +X goto nest_err; +X else if ((*ifptr & ELSE_SEEN) != 0) { +Xelse_seen_err: cerror("#%s may not follow #else", token); +X goto dump_line; +X } +X if ((*ifptr & (WAS_COMPILING | TRUE_SEEN)) != WAS_COMPILING) { +X compiling = FALSE; /* Done compiling stuff */ +X goto dump_line; /* Skip this clause */ +X } +X doif(L_if); +X break; +X +X case L_if: +X case L_ifdef: +X case L_ifndef: +X if (++ifptr >= &ifstack[BLK_NEST]) +Xif_nest_err: cfatal("Too many nested #%s statements", token); +X *ifptr = WAS_COMPILING; +X doif(hash); +X break; +X +X case L_endif: +X if (ifptr == &ifstack[0]) { +Xnest_err: cerror("#%s must be in an #if", token); +X goto dump_line; +X } +X if (!compiling && (*ifptr & WAS_COMPILING) != 0) +X wrongline = TRUE; +X compiling = ((*ifptr & WAS_COMPILING) != 0); +X --ifptr; +X break; +X +X case L_assert: +X if (eval() == 0) +X cerror("Preprocessor assertion failure", NULLST); +X break; +X +X case L_ident: +X case L_sccs: +X goto dump_line; +X break; +X +X case L_pragma: +X /* +X * #pragma is provided to pass "options" to later +X * passes of the compiler. cpp doesn't have any yet. +X */ +X printf("#pragma "); +X while ((c = get()) != '\n' && c != EOF_CHAR) +X cput(c); +X unget(); +X break; +X +X#if DEBUG +X case L_debug: +X if (debug == 0) +X dumpdef("debug set on"); +X debug++; +X break; +X +X case L_nodebug: +X debug--; +X break; +X#endif +X +X default: +X /* +X * Undefined #control keyword. +X * Note: the correct behavior may be to warn and +X * pass the line to a subsequent compiler pass. +X * This would allow #asm or similar extensions. +X */ +X cerror("Illegal # command \"%s\"", token); +X break; +X } +X if (hash != L_include) { +X#if OLD_PREPROCESSOR || !VERBOSE +X /* +X * Ignore the rest of the #control line so you can write +X * #if foo +X * #endif foo +X */ +X goto dump_line; /* Take common exit */ +X#else +X if (skipws() != '\n') { +X cwarn("Unexpected text in #control line ignored", NULLST); +X skipnl(); +X } +X#endif +X } +X return (counter + 1); +X} +X +XFILE_LOCAL +Xdoif(hash) +Xint hash; +X/* +X * Process an #if, #ifdef, or #ifndef. The latter two are straightforward, +X * while #if needs a subroutine of its own to evaluate the expression. +X * +X * doif() is called only if compiling is TRUE. If false, compilation +X * is always supressed, so we don't need to evaluate anything. This +X * supresses unnecessary warnings. +X */ +X{ +X register int c; +X register int found; +X +X if ((c = skipws()) == '\n' || c == EOF_CHAR) { +X unget(); +X goto badif; +X } +X if (hash == L_if) { +X unget(); +X found = (eval() != 0); /* Evaluate expr, != 0 is TRUE */ +X hash = L_ifdef; /* #if is now like #ifdef */ +X } +X else { +X if (type[c] != LET) /* Next non-blank isn't letter */ +X goto badif; /* ... is an error */ +X found = (lookid(c) != NULL); /* Look for it in symbol table */ +X } +X if (found == (hash == L_ifdef)) { +X compiling = TRUE; +X *ifptr |= TRUE_SEEN; +X } +X else { +X compiling = FALSE; +X } +X return; +X +Xbadif: cerror("#if, #ifdef, or #ifndef without an argument", NULLST); +X#if !OLD_PREPROCESSOR +X skipnl(); /* Prevent an extra */ +X unget(); /* Error message */ +X#endif +X return; +X} +X +XFILE_LOCAL +Xdoinclude() +X/* +X * Process the #include control line. +X * There are three variations: +X * #include "file" search somewhere relative to the +X * current source file, if not found, +X * treat as #include . +X * #include Search in an implementation-dependent +X * list of places. +X * #include token Expand the token, it must be one of +X * "file" or , process as such. +X * +X * Note: the November 12 draft forbids '>' in the #include format. +X * This restriction is unnecessary and not implemented. +X */ +X{ +X register int c; +X register int delim; +X#if HOST == SYS_VMS +X char def_filename[NAM$C_MAXRSS + 1]; +X#endif +X +X delim = macroid(skipws()); +X if (delim != '<' && delim != '"') +X goto incerr; +X if (delim == '<') +X delim = '>'; +X workp = work; +X instring = TRUE; /* Accept all characters */ +X while ((c = get()) != '\n' && c != delim && c != EOF_CHAR) +X save(c); /* Put it away. */ +X skipnl(); +X /* +X * The draft is unclear if the following should be done. +X */ +X +X while (--workp >= work && (type[*workp] == SPA)) +X ; /* Trim blanks from filename */ +X +X/* +X * if (*workp != delim) +X * goto incerr; +X */ +X *(workp + 1) = EOS; /* Terminate filename */ +X instring = FALSE; +X#if HOST == SYS_VMS +X /* +X * Assume the default .h filetype. +X */ +X if (!vmsparse(work, ".H", def_filename)) { +X perror(work); /* Oops. */ +X goto incerr; +X } +X else if (openinclude(def_filename, (delim == '"'))) +X return; +X#else +X if (openinclude(work, (delim == '"'))) +X return; +X#endif +X /* +X * No sense continuing if #include file isn't there. +X */ +X cfatal("Cannot open include file \"%s\"", work); +X +Xincerr: cerror("#include syntax error", NULLST); +X return; +X} +X +XFILE_LOCAL int +Xopeninclude(filename, searchlocal) +Xchar *filename; /* Input file name */ +Xint searchlocal; /* TRUE if #include "file" */ +X/* +X * Actually open an include file. This routine is only called from +X * doinclude() above, but was written as a separate subroutine for +X * programmer convenience. It searches the list of directories +X * and actually opens the file, linking it into the list of +X * active files. Returns TRUE if the file was opened, FALSE +X * if openinclude() fails. No error message is printed. +X */ +X{ +X register char **incptr; +X#if HOST == SYS_VMS +X#if NWORK < (NAM$C_MAXRSS + 1) +X << error, NWORK isn't greater than NAM$C_MAXRSS >> +X#endif +X#endif +X char tmpname[NWORK]; /* Filename work area */ +X +X if (searchlocal) { +X /* +X * Look in local directory first +X */ +X#if HOST == SYS_UNIX +X /* +X * Try to open filename relative to the directory of the current +X * source file (as opposed to the current directory). (ARF, SCK). +X */ +X if (filename[0] != '/' +X && hasdirectory(infile->filename, tmpname)) +X strcat(tmpname, filename); +X else { +X strcpy(tmpname, filename); +X } +X#else +X if (!hasdirectory(filename, tmpname) +X && hasdirectory(infile->filename, tmpname)) +X strcat(tmpname, filename); +X else { +X strcpy(tmpname, filename); +X } +X#endif +X if (openfile(tmpname)) +X return (TRUE); +X } +X /* +X * Look in any directories specified by -I command line +X * arguments, then in the builtin search list. +X */ +X for (incptr = incdir; incptr < incend; incptr++) { +X if (strlen(*incptr) + strlen(filename) >= (NWORK - 1)) +X cfatal("Filename work buffer overflow", NULLST); +X else { +X#if HOST == SYS_UNIX +X if (filename[0] == '/') +X strcpy(tmpname, filename); +X else { +X sprintf(tmpname, "%s/%s", *incptr, filename); +X } +X#else +X if (!hasdirectory(filename, tmpname)) +X sprintf(tmpname, "%s%s", *incptr, filename); +X#endif +X if (openfile(tmpname)) +X return (TRUE); +X } +X } +X return (FALSE); +X} +X +XFILE_LOCAL int +Xhasdirectory(source, result) +Xchar *source; /* Directory to examine */ +Xchar *result; /* Put directory stuff here */ +X/* +X * If a device or directory is found in the source filename string, the +X * node/device/directory part of the string is copied to result and +X * hasdirectory returns TRUE. Else, nothing is copied and it returns FALSE. +X */ +X{ +X#if HOST == SYS_UNIX +X register char *tp; +X +X if ((tp = strrchr(source, '/')) == NULL) +X return (FALSE); +X else { +X strncpy(result, source, tp - source + 1); +X result[tp - source + 1] = EOS; +X return (TRUE); +X } +X#else +X#if HOST == SYS_VMS +X if (vmsparse(source, NULLST, result) +X && result[0] != EOS) +X return (TRUE); +X else { +X return (FALSE); +X } +X#else +X /* +X * Random DEC operating system (RSX, RT11, RSTS/E) +X */ +X register char *tp; +X +X if ((tp = strrchr(source, ']')) == NULL +X && (tp = strrchr(source, ':')) == NULL) +X return (FALSE); +X else { +X strncpy(result, source, tp - source + 1); +X result[tp - source + 1] = EOS; +X return (TRUE); +X } +X#endif +X#endif +X} +X +X#if HOST == SYS_VMS +X +X/* +X * EXP_DEV is set if a device was specified, EXP_DIR if a directory +X * is specified. (Both set indicate a file-logical, but EXP_DEV +X * would be set by itself if you are reading, say, SYS$INPUT:) +X */ +X#define DEVDIR (NAM$M_EXP_DEV | NAM$M_EXP_DIR) +X +XFILE_LOCAL int +Xvmsparse(source, defstring, result) +Xchar *source; +Xchar *defstring; /* non-NULL -> default string. */ +Xchar *result; /* Size is at least NAM$C_MAXRSS + 1 */ +X/* +X * Parse the source string, applying the default (properly, using +X * the system parse routine), storing it in result. +X * TRUE if it parsed, FALSE on error. +X * +X * If defstring is NULL, there are no defaults and result gets +X * (just) the node::[directory] part of the string (possibly "") +X */ +X{ +X struct FAB fab = cc$rms_fab; /* File access block */ +X struct NAM nam = cc$rms_nam; /* File name block */ +X char fullname[NAM$C_MAXRSS + 1]; +X register char *rp; /* Result pointer */ +X +X fab.fab$l_nam = &nam; /* fab -> nam */ +X fab.fab$l_fna = source; /* Source filename */ +X fab.fab$b_fns = strlen(source); /* Size of source */ +X fab.fab$l_dna = defstring; /* Default string */ +X if (defstring != NULLST) +X fab.fab$b_dns = strlen(defstring); /* Size of default */ +X nam.nam$l_esa = fullname; /* Expanded filename */ +X nam.nam$b_ess = NAM$C_MAXRSS; /* Expanded name size */ +X if (sys$parse(&fab) == RMS$_NORMAL) { /* Parse away */ +X fullname[nam.nam$b_esl] = EOS; /* Terminate string */ +X result[0] = EOS; /* Just in case */ +X rp = &result[0]; +X /* +X * Remove stuff added implicitly, accepting node names and +X * dev:[directory] strings (but not process-permanent files). +X */ +X if ((nam.nam$l_fnb & NAM$M_PPF) == 0) { +X if ((nam.nam$l_fnb & NAM$M_NODE) != 0) { +X strncpy(result, nam.nam$l_node, nam.nam$b_node); +X rp += nam.nam$b_node; +X *rp = EOS; +X } +X if ((nam.nam$l_fnb & DEVDIR) == DEVDIR) { +X strncpy(rp, nam.nam$l_dev, nam.nam$b_dev + nam.nam$b_dir); +X rp += nam.nam$b_dev + nam.nam$b_dir; +X *rp = EOS; +X } +X } +X if (defstring != NULLST) { +X strncpy(rp, nam.nam$l_name, nam.nam$b_name + nam.nam$b_type); +X rp += nam.nam$b_name + nam.nam$b_type; +X *rp = EOS; +X if ((nam.nam$l_fnb & NAM$M_EXP_VER) != 0) { +X strncpy(rp, nam.nam$l_ver, nam.nam$b_ver); +X rp[nam.nam$b_ver] = EOS; +X } +X } +X return (TRUE); +X } +X return (FALSE); +X} +X#endif +X +END-of-cpp2.c +exit diff --git a/sys/unix/cpp2.shr b/sys/unix/cpp2.shr new file mode 100644 index 0000000..ccadfc0 --- /dev/null +++ b/sys/unix/cpp2.shr @@ -0,0 +1,1763 @@ +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +# cpp1.c +# cpp3.c +# cpp4.c +# +echo x - cpp1.c +sed 's/^X//' >cpp1.c << 'END-of-cpp1.c' +X/* +X * CPP main program. +X * +X * Edit history +X * 21-May-84 MM "Field test" release +X * 23-May-84 MM Some minor hacks. +X * 30-May-84 ARF Didn't get enough memory for __DATE__ +X * Added code to read stdin if no input +X * files are provided. +X * 29-Jun-84 MM Added ARF's suggestions, Unixifying cpp. +X * 11-Jul-84 MM "Official" first release (that's what I thought!) +X * 22-Jul-84 MM/ARF/SCK Fixed line number bugs, added cpp recognition +X * of #line, fixed problems with #include. +X * 23-Jul-84 MM More (minor) include hacking, some documentation. +X * Also, redid cpp's #include files +X * 25-Jul-84 MM #line filename isn't used for #include searchlist +X * #line format is +X * 25-Jul-84 ARF/MM Various bugs, mostly serious. Removed homemade doprint +X * 01-Aug-84 MM Fixed recursion bug, remove extra newlines and +X * leading whitespace from cpp output. +X * 02-Aug-84 MM Hacked (i.e. optimized) out blank lines and unneeded +X * whitespace in general. Cleaned up unget()'s. +X * 03-Aug-84 Keie Several bug fixes from Ed Keizer, Vrije Universitet. +X * -- corrected arg. count in -D and pre-defined +X * macros. Also, allow \n inside macro actual parameter +X * lists. +X * 06-Aug-84 MM If debugging, dump the preset vector at startup. +X * 12-Aug-84 MM/SCK Some small changes from Sam Kendall +X * 15-Aug-84 Keie/MM cerror, cwarn, etc. take a single string arg. +X * cierror, etc. take a single int. arg. +X * changed LINE_PREFIX slightly so it can be +X * changed in the makefile. +X * 31-Aug-84 MM USENET net.sources release. +X * 7-Sep-84 SCH/ado Lint complaints +X * 10-Sep-84 Keie Char's can't be signed in some implementations +X * 11-Sep-84 ado Added -C flag, pathological line number fix +X * 13-Sep-84 ado Added -E flag (does nothing) and "-" file for stdin. +X * 14-Sep-84 MM Allow # 123 as a synonym for #line 123 +X * 19-Sep-84 MM scanid always reads to token, make sure #line is +X * written to a new line, even if -C switch given. +X * Also, cpp - - reads stdin, writes stdout. +X * 03-Oct-84 ado/MM Several changes to line counting and keepcomments +X * stuff. Also a rewritten control() hasher -- much +X * simpler and no less "perfect". Note also changes +X * in cpp3.c to fix numeric scanning. +X * 04-Oct-84 MM Added recognition of macro formal parameters if +X * they are the only thing in a string, per the +X * draft standard. +X * 08-Oct-84 MM One more attack on scannumber +X * 15-Oct-84 MM/ado Added -N to disable predefined symbols. Fixed +X * linecount if COMMENT_INVISIBLE enabled. +X * 22-Oct-84 MM Don't evaluate the #if/#ifdef argument if +X * compilation is supressed. This prevents +X * unnecessary error messages in sequences such as +X * #ifdef FOO -- undefined +X * #if FOO == 10 -- shouldn't print warning +X * 25-Oct-84 MM Fixed bug in false ifdef supression. On vms, +X * #include should open foo.h -- this duplicates +X * the behavior of Vax-C +X * 31-Oct-84 ado/MM Parametized $ in indentifiers. Added a better +X * token concatenator and took out the trial +X * concatenation code. Also improved #ifdef code +X * and cleaned up the macro recursion tester. +X * 2-Nov-84 MM/ado Some bug fixes in token concatenation, also +X * a variety of minor (uninteresting) hacks. +X * 6-Nov-84 MM Happy Birthday. Broke into 4 files and added +X * #if sizeof (basic_types) +X * 9-Nov-84 MM Added -S* for pointer type sizes +X * 13-Nov-84 MM Split cpp1.c, added vms defaulting +X * 23-Nov-84 MM/ado -E supresses error exit, added CPP_INCLUDE, +X * fixed strncpy bug. +X * 3-Dec-84 ado/MM Added OLD_PREPROCESSOR +X * 7-Dec-84 MM Stuff in Nov 12 Draft Standard +X * 17-Dec-84 george Fixed problems with recursive macros +X * 17-Dec-84 MM Yet another attack on #if's (f/t)level removed. +X * 07-Jan-85 ado Init defines before doing command line options +X * so -Uunix works. +X */ +X +X/*)BUILD +X $(PROGRAM) = cpp +X $(FILES) = { cpp1 cpp2 cpp3 cpp4 cpp5 cpp6 } +X $(INCLUDE) = { cppdef.h cpp.h } +X $(STACK) = 2000 +X $(TKBOPTIONS) = { +X STACK = 2000 +X } +X*/ +X +X#ifdef DOCUMENTATION +X +Xtitle cpp C Pre-Processor +Xindex C pre-processor +X +Xsynopsis +X .s.nf +X cpp [-options] [infile [outfile]] +X .s.f +Xdescription +X +X CPP reads a C source file, expands macros and include +X files, and writes an input file for the C compiler. +X If no file arguments are given, CPP reads from stdin +X and writes to stdout. If one file argument is given, +X it will define the input file, while two file arguments +X define both input and output files. The file name "-" +X is a synonym for stdin or stdout as appropriate. +X +X The following options are supported. Options may +X be given in either case. +X .lm +16 +X .p -16 +X -C If set, source-file comments are written +X to the output file. This allows the output of CPP to be +X used as the input to a program, such as lint, that expects +X commands embedded in specially-formatted comments. +X .p -16 +X -Dname=value Define the name as if the programmer wrote +X +X #define name value +X +X at the start of the first file. If "=value" is not +X given, a value of "1" will be used. +X +X On non-unix systems, all alphabetic text will be forced +X to upper-case. +X .p -16 +X -E Always return "success" to the operating +X system, even if errors were detected. Note that some fatal +X errors, such as a missing #include file, will terminate +X CPP, returning "failure" even if the -E option is given. +X .p -16 +X -Idirectory Add this directory to the list of +X directories searched for #include "..." and #include <...> +X commands. Note that there is no space between the +X "-I" and the directory string. More than one -I command +X is permitted. On non-Unix systems "directory" is forced +X to upper-case. +X .p -16 +X -N CPP normally predefines some symbols defining +X the target computer and operating system. If -N is specified, +X no symbols will be predefined. If -N -N is specified, the +X "always present" symbols, __LINE__, __FILE__, and __DATE__ +X are not defined. +X .p -16 +X -Stext CPP normally assumes that the size of +X the target computer's basic variable types is the same as the size +X of these types of the host computer. (This can be overridden +X when CPP is compiled, however.) The -S option allows dynamic +X respecification of these values. "text" is a string of +X numbers, separated by commas, that specifies correct sizes. +X The sizes must be specified in the exact order: +X +X char short int long float double +X +X If you specify the option as "-S*text", pointers to these +X types will be specified. -S* takes one additional argument +X for pointer to function (e.g. int (*)()) +X +X For example, to specify sizes appropriate for a PDP-11, +X you would write: +X +X c s i l f d func +X -S1,2,2,2,4,8, +X -S*2,2,2,2,2,2,2 +X +X Note that all values must be specified. +X .p -16 +X -Uname Undefine the name as if +X +X #undef name +X +X were given. On non-Unix systems, "name" will be forced to +X upper-case. +X .p -16 +X -Xnumber Enable debugging code. If no value is +X given, a value of 1 will be used. (For maintenence of +X CPP only.) +X .s.lm -16 +X +XPre-Defined Variables +X +X When CPP begins processing, the following variables will +X have been defined (unless the -N option is specified): +X .s +X Target computer (as appropriate): +X .s +X pdp11, vax, M68000 m68000 m68k +X .s +X Target operating system (as appropriate): +X .s +X rsx, rt11, vms, unix +X .s +X Target compiler (as appropriate): +X .s +X decus, vax11c +X .s +X The implementor may add definitions to this list. +X The default definitions match the definition of the +X host computer, operating system, and C compiler. +X .s +X The following are always available unless undefined (or +X -N was specified twice): +X .lm +16 +X .p -12 +X __FILE__ The input (or #include) file being compiled +X (as a quoted string). +X .p -12 +X __LINE__ The line number being compiled. +X .p -12 +X __DATE__ The date and time of compilation as +X a Unix ctime quoted string (the trailing newline is removed). +X Thus, +X .s +X printf("Bug at line %s,", __LINE__); +X printf(" source file %s", __FILE__); +X printf(" compiled on %s", __DATE__); +X .s.lm -16 +X +XDraft Proposed Ansi Standard Considerations +X +X The current version of the Draft Proposed Standard +X explicitly states that "readers are requested not to specify +X or claim conformance to this draft." Readers and users +X of Decus CPP should not assume that Decus CPP conforms +X to the standard, or that it will conform to the actual +X C Language Standard. +X +X When CPP is itself compiled, many features of the Draft +X Proposed Standard that are incompatible with existing +X preprocessors may be disabled. See the comments in CPP's +X source for details. +X +X The latest version of the Draft Proposed Standard (as reflected +X in Decus CPP) is dated November 12, 1984. +X +X Comments are removed from the input text. The comment +X is replaced by a single space character. The -C option +X preserves comments, writing them to the output file. +X +X The '$' character is considered to be a letter. This is +X a permitted extension. +X +X The following new features of C are processed by CPP: +X .s.comment Note: significant spaces, not tabs, .br quotes #if, #elif +X .br;####_#elif expression (_#else _#if) +X .br;####'_\xNNN' (Hexadecimal constant) +X .br;####'_\a' (Ascii BELL) +X .br;####'_\v' (Ascii Vertical Tab) +X .br;####_#if defined NAME 1 if defined, 0 if not +X .br;####_#if defined (NAME) 1 if defined, 0 if not +X .br;####_#if sizeof (basic type) +X .br;####unary + +X .br;####123U, 123LU Unsigned ints and longs. +X .br;####12.3L Long double numbers +X .br;####token_#token Token concatenation +X .br;####_#include token Expands to filename +X +X The Draft Proposed Standard has extended C, adding a constant +X string concatenation operator, where +X +X "foo" "bar" +X +X is regarded as the single string "foobar". (This does not +X affect CPP's processing but does permit a limited form of +X macro argument substitution into strings as will be discussed.) +X +X The Standard Committee plans to add token concatenation +X to #define command lines. One suggested implementation +X is as follows: the sequence "Token1#Token2" is treated +X as if the programmer wrote "Token1Token2". This could +X be used as follows: +X +X #line 123 +X #define ATLINE foo#__LINE__ +X +X ATLINE would be defined as foo123. +X +X Note that "Token2" must either have the format of an +X identifier or be a string of digits. Thus, the string +X +X #define ATLINE foo#1x3 +X +X generates two tokens: "foo1" and "x3". +X +X If the tokens T1 and T2 are concatenated into T3, +X this implementation operates as follows: +X +X 1. Expand T1 if it is a macro. +X 2. Expand T2 if it is a macro. +X 3. Join the tokens, forming T3. +X 4. Expand T3 if it is a macro. +X +X A macro formal parameter will be substituted into a string +X or character constant if it is the only component of that +X constant: +X +X #define VECSIZE 123 +X #define vprint(name, size) \ +X printf("name" "[" "size" "] = {\n") +X ... vprint(vector, VECSIZE); +X +X expands (effectively) to +X +X vprint("vector[123] = {\n"); +X +X Note that this will be useful if your C compiler supports +X the new string concatenation operation noted above. +X As implemented here, if you write +X +X #define string(arg) "arg" +X ... string("foo") ... +X +X This implementation generates "foo", rather than the strictly +X correct ""foo"" (which will probably generate an error message). +X This is, strictly speaking, an error in CPP and may be removed +X from future releases. +X +Xerror messages +X +X Many. CPP prints warning or error messages if you try to +X use multiple-byte character constants (non-transportable) +X if you #undef a symbol that was not defined, or if your +X program has potentially nested comments. +X +Xauthor +X +X Martin Minow +X +Xbugs +X +X The #if expression processor uses signed integers only. +X I.e, #if 0xFFFFu < 0 may be TRUE. +X +X#endif +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X +X/* +X * Commonly used global variables: +X * line is the current input line number. +X * wrongline is set in many places when the actual output +X * line is out of sync with the numbering, e.g, +X * when expanding a macro with an embedded newline. +X * +X * token holds the last identifier scanned (which might +X * be a candidate for macro expansion). +X * errors is the running cpp error counter. +X * infile is the head of a linked list of input files (extended by +X * #include and macros being expanded). infile always points +X * to the current file/macro. infile->parent to the includer, +X * etc. infile->fd is NULL if this input stream is a macro. +X */ +Xint line; /* Current line number */ +Xint wrongline; /* Force #line to compiler */ +Xchar token[IDMAX + 1]; /* Current input token */ +Xint errors; /* cpp error counter */ +XFILEINFO *infile = NULL; /* Current input file */ +X#if DEBUG +Xint debug; /* TRUE if debugging now */ +X#endif +X/* +X * This counter is incremented when a macro expansion is initiated. +X * If it exceeds a built-in value, the expansion stops -- this tests +X * for a runaway condition: +X * #define X Y +X * #define Y X +X * X +X * This can be disabled by falsifying rec_recover. (Nothing does this +X * currently: it is a hook for an eventual invocation flag.) +X */ +Xint recursion; /* Infinite recursion counter */ +Xint rec_recover = TRUE; /* Unwind recursive macros */ +X +X/* +X * instring is set TRUE when a string is scanned. It modifies the +X * behavior of the "get next character" routine, causing all characters +X * to be passed to the caller (except ). Note especially that +X * comments and \ are not removed from the source. (This +X * prevents cpp output lines from being arbitrarily long). +X * +X * inmacro is set by #define -- it absorbs comments and converts +X * form-feed and vertical-tab to space, but returns \ +X * to the caller. Strictly speaking, this is a bug as \ +X * shouldn't delimit tokens, but we'll worry about that some other +X * time -- it is more important to prevent infinitly long output lines. +X * +X * instring and inmarcor are parameters to the get() routine which +X * were made global for speed. +X */ +Xint instring = FALSE; /* TRUE if scanning string */ +Xint inmacro = FALSE; /* TRUE if #defining a macro */ +X +X/* +X * work[] and workp are used to store one piece of text in a temporay +X * buffer. To initialize storage, set workp = work. To store one +X * character, call save(c); (This will fatally exit if there isn't +X * room.) To terminate the string, call save(EOS). Note that +X * the work buffer is used by several subroutines -- be sure your +X * data won't be overwritten. The extra byte in the allocation is +X * needed for string formal replacement. +X */ +Xchar work[NWORK + 1]; /* Work buffer */ +Xchar *workp; /* Work buffer pointer */ +X +X/* +X * keepcomments is set TRUE by the -C option. If TRUE, comments +X * are written directly to the output stream. This is needed if +X * the output from cpp is to be passed to lint (which uses commands +X * embedded in comments). cflag contains the permanent state of the +X * -C flag. keepcomments is always falsified when processing #control +X * commands and when compilation is supressed by a false #if +X * +X * If eflag is set, CPP returns "success" even if non-fatal errors +X * were detected. +X * +X * If nflag is non-zero, no symbols are predefined except __LINE__. +X * __FILE__, and __DATE__. If nflag > 1, absolutely no symbols +X * are predefined. +X */ +Xint keepcomments = FALSE; /* Write out comments flag */ +Xint cflag = FALSE; /* -C option (keep comments) */ +Xint eflag = FALSE; /* -E option (never fail) */ +Xint nflag = 0; /* -N option (no predefines) */ +X +X/* +X * ifstack[] holds information about nested #if's. It is always +X * accessed via *ifptr. The information is as follows: +X * WAS_COMPILING state of compiling flag at outer level. +X * ELSE_SEEN set TRUE when #else seen to prevent 2nd #else. +X * TRUE_SEEN set TRUE when #if or #elif succeeds +X * ifstack[0] holds the compiling flag. It is TRUE if compilation +X * is currently enabled. Note that this must be initialized TRUE. +X */ +Xchar ifstack[BLK_NEST] = { TRUE }; /* #if information */ +Xchar *ifptr = ifstack; /* -> current ifstack[] */ +X +X/* +X * incdir[] stores the -i directories (and the system-specific +X * #include <...> directories. +X */ +Xchar *incdir[NINCLUDE]; /* -i directories */ +Xchar **incend = incdir; /* -> free space in incdir[] */ +X +X/* +X * This is the table used to predefine target machine and operating +X * system designators. It may need hacking for specific circumstances. +X * Note: it is not clear that this is part of the Ansi Standard. +X * The -N option supresses preset definitions. +X */ +Xchar *preset[] = { /* names defined at cpp start */ +X#ifdef MACHINE +X MACHINE, +X#endif +X#ifdef SYSTEM +X SYSTEM, +X#endif +X#ifdef COMPILER +X COMPILER, +X#endif +X#if DEBUG +X "decus_cpp", /* Ourselves! */ +X#endif +X NULL /* Must be last */ +X}; +X +X/* +X * The value of these predefined symbols must be recomputed whenever +X * they are evaluated. The order must not be changed. +X */ +Xchar *magic[] = { /* Note: order is important */ +X "__LINE__", +X "__FILE__", +X NULL /* Must be last */ +X}; +X +Xmain(argc, argv) +Xint argc; +Xchar *argv[]; +X{ +X register int i; +X +X#if HOST == SYS_VMS +X argc = getredirection(argc, argv); /* vms >file and stdin */ +X /* +X * Open input file, "-" means use stdin. +X */ +X if (!streq(argv[1], "-")) { +X if (freopen(argv[1], "r", stdin) == NULL) { +X perror(argv[1]); +X cerror("Can't open input file \"%s\"", argv[1]); +X exit(IO_ERROR); +X } +X strcpy(work, argv[1]); /* Remember input filename */ +X break; +X } /* Else, just get stdin */ +X case 0: /* No args? */ +X case 1: /* No files, stdin -> stdout */ +X#if HOST == SYS_UNIX +X work[0] = EOS; /* Unix can't find stdin name */ +X#else +X fgetname(stdin, work); /* Vax-11C, Decus C know name */ +X#endif +X break; +X +X default: +X exit(IO_ERROR); /* Can't happen */ +X } +X setincdirs(); /* Setup -I include directories */ +X addfile(stdin, work); /* "open" main input file */ +X#if DEBUG +X if (debug > 0) +X dumpdef("preset #define symbols"); +X#endif +X cppmain(); /* Process main file */ +X if ((i = (ifptr - &ifstack[0])) != 0) { +X#if OLD_PREPROCESSOR +X ciwarn("Inside #ifdef block at end of input, depth = %d", i); +X#else +X cierror("Inside #ifdef block at end of input, depth = %d", i); +X#endif +X } +X fclose(stdout); +X if (errors > 0) { +X fprintf(stderr, (errors == 1) +X ? "%d error in preprocessor\n" +X : "%d errors in preprocessor\n", errors); +X if (!eflag) +X exit(IO_ERROR); +X } +X exit(IO_NORMAL); /* No errors or -E option set */ +X} +X +XFILE_LOCAL +Xcppmain() +X/* +X * Main process for cpp -- copies tokens from the current input +X * stream (main file, include file, or a macro) to the output +X * file. +X */ +X{ +X register int c; /* Current character */ +X register int counter; /* newlines and spaces */ +X extern int output(); /* Output one character */ +X +X /* +X * Explicitly output a #line at the start of cpp output so +X * that lint (etc.) knows the name of the original source +X * file. If we don't do this explicitly, we may get +X * the name of the first #include file instead. +X */ +X sharp(); +X /* +X * This loop is started "from the top" at the beginning of each line +X * wrongline is set TRUE in many places if it is necessary to write +X * a #line record. (But we don't write them when expanding macros.) +X * +X * The counter variable has two different uses: at +X * the start of a line, it counts the number of blank lines that +X * have been skipped over. These are then either output via +X * #line records or by outputting explicit blank lines. +X * When expanding tokens within a line, the counter remembers +X * whether a blank/tab has been output. These are dropped +X * at the end of the line, and replaced by a single blank +X * within lines. +X */ +X for (;;) { +X counter = 0; /* Count empty lines */ +X for (;;) { /* For each line, ... */ +X while (type[(c = get())] == SPA) /* Skip leading blanks */ +X ; /* in this line. */ +X if (c == '\n') /* If line's all blank, */ +X ++counter; /* Do nothing now */ +X else if (c == '#') { /* Is 1st non-space '#' */ +X keepcomments = FALSE; /* Don't pass comments */ +X counter = control(counter); /* Yes, do a #command */ +X keepcomments = (cflag && compiling); +X } +X else if (c == EOF_CHAR) /* At end of file? */ +X break; +X else if (!compiling) { /* #ifdef false? */ +X skipnl(); /* Skip to newline */ +X counter++; /* Count it, too. */ +X } +X else { +X break; /* Actual token */ +X } +X } +X if (c == EOF_CHAR) /* Exit process at */ +X break; /* End of file */ +X /* +X * If the loop didn't terminate because of end of file, we +X * know there is a token to compile. First, clean up after +X * absorbing newlines. counter has the number we skipped. +X */ +X if ((wrongline && infile->fp != NULL) || counter > 4) +X sharp(); /* Output # line number */ +X else { /* If just a few, stuff */ +X while (--counter >= 0) /* them out ourselves */ +X putchar('\n'); +X } +X /* +X * Process each token on this line. +X */ +X unget(); /* Reread the char. */ +X for (;;) { /* For the whole line, */ +X do { /* Token concat. loop */ +X for (counter = 0; (type[(c = get())] == SPA);) { +X#if COMMENT_INVISIBLE +X if (c != COM_SEP) +X counter++; +X#else +X counter++; /* Skip over blanks */ +X#endif +X } +X if (c == EOF_CHAR || c == '\n') +X goto end_line; /* Exit line loop */ +X else if (counter > 0) /* If we got any spaces */ +X putchar(' '); /* Output one space */ +X c = macroid(c); /* Grab the token */ +X } while (type[c] == LET && catenate()); +X if (c == EOF_CHAR || c == '\n') /* From macro exp error */ +X goto end_line; /* Exit line loop */ +X switch (type[c]) { +X case LET: +X fputs(token, stdout); /* Quite ordinary token */ +X break; +X +X +X case DIG: /* Output a number */ +X case DOT: /* Dot may begin floats */ +X scannumber(c, output); +X break; +X +X case QUO: /* char or string const */ +X scanstring(c, output); /* Copy it to output */ +X break; +X +X default: /* Some other character */ +X cput(c); /* Just output it */ +X break; +X } /* Switch ends */ +X } /* Line for loop */ +Xend_line: if (c == '\n') { /* Compiling at EOL? */ +X putchar('\n'); /* Output newline, if */ +X if (infile->fp == NULL) /* Expanding a macro, */ +X wrongline = TRUE; /* Output # line later */ +X } +X } /* Continue until EOF */ +X} +X +Xoutput(c) +Xint c; +X/* +X * Output one character to stdout -- output() is passed as an +X * argument to scanstring() +X */ +X{ +X#if COMMENT_INVISIBLE +X if (c != TOK_SEP && c != COM_SEP) +X#else +X if (c != TOK_SEP) +X#endif +X putchar(c); +X} +X +Xstatic char *sharpfilename = NULL; +X +XFILE_LOCAL +Xsharp() +X/* +X * Output a line number line. +X */ +X{ +X register char *name; +X +X if (keepcomments) /* Make sure # comes on */ +X putchar('\n'); /* a fresh, new line. */ +X printf("#%s %d", LINE_PREFIX, line); +X if (infile->fp != NULL) { +X name = (infile->progname != NULL) +X ? infile->progname : infile->filename; +X if (sharpfilename == NULL +X || sharpfilename != NULL && !streq(name, sharpfilename)) { +X if (sharpfilename != NULL) +X free(sharpfilename); +X sharpfilename = savestring(name); +X printf(" \"%s\"", name); +X } +X } +X putchar('\n'); +X wrongline = FALSE; +X} +END-of-cpp1.c +echo x - cpp3.c +sed 's/^X//' >cpp3.c << 'END-of-cpp3.c' +X/* +X * C P P 3 . C +X * +X * File open and command line options +X * +X * Edit history +X * 13-Nov-84 MM Split from cpp1.c +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X#if DEBUG && (HOST == SYS_VMS || HOST == SYS_UNIX) +X#include +Xextern int abort(); /* For debugging */ +X#endif +X +Xint +Xopenfile(filename) +Xchar *filename; +X/* +X * Open a file, add it to the linked list of open files. +X * This is called only from openfile() above. +X */ +X{ +X register FILE *fp; +X +X if ((fp = fopen(filename, "r")) == NULL) { +X#if DEBUG +X perror(filename); +X#endif +X return (FALSE); +X } +X#if DEBUG +X if (debug) +X fprintf(stderr, "Reading from \"%s\"\n", filename); +X#endif +X addfile(fp, filename); +X return (TRUE); +X} +X +Xaddfile(fp, filename) +XFILE *fp; /* Open file pointer */ +Xchar *filename; /* Name of the file */ +X/* +X * Initialize tables for this open file. This is called from openfile() +X * above (for #include files), and from the entry to cpp to open the main +X * input file. It calls a common routine, getfile() to build the FILEINFO +X * structure which is used to read characters. (getfile() is also called +X * to setup a macro replacement.) +X */ +X{ +X register FILEINFO *file; +X extern FILEINFO *getfile(); +X +X file = getfile(NBUFF, filename); +X file->fp = fp; /* Better remember FILE * */ +X file->buffer[0] = EOS; /* Initialize for first read */ +X line = 1; /* Working on line 1 now */ +X wrongline = TRUE; /* Force out initial #line */ +X} +X +Xsetincdirs() +X/* +X * Append system-specific directories to the include directory list. +X * Called only when cpp is started. +X */ +X{ +X +X#ifdef CPP_INCLUDE +X *incend++ = CPP_INCLUDE; +X#define IS_INCLUDE 1 +X#else +X#define IS_INCLUDE 0 +X#endif +X +X#if HOST == SYS_UNIX +X *incend++ = "/usr/include"; +X#define MAXINCLUDE (NINCLUDE - 1 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_VMS +X extern char *getenv(); +X +X if (getenv("C$LIBRARY") != NULL) +X *incend++ = "C$LIBRARY:"; +X *incend++ = "SYS$LIBRARY:"; +X#define MAXINCLUDE (NINCLUDE - 2 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_RSX +X extern int $$rsts; /* TRUE on RSTS/E */ +X extern int $$pos; /* TRUE on PRO-350 P/OS */ +X extern int $$vms; /* TRUE on VMS compat. */ +X +X if ($$pos) { /* P/OS? */ +X *incend++ = "SY:[ZZDECUSC]"; /* C #includes */ +X *incend++ = "LB:[1,5]"; /* RSX library */ +X } +X else if ($$rsts) { /* RSTS/E? */ +X *incend++ = "SY:@"; /* User-defined account */ +X *incend++ = "C:"; /* Decus-C library */ +X *incend++ = "LB:[1,1]"; /* RSX library */ +X } +X else if ($$vms) { /* VMS compatibility? */ +X *incend++ = "C:"; +X } +X else { /* Plain old RSX/IAS */ +X *incend++ = "LB:[1,1]"; +X } +X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) +X#endif +X +X#if HOST == SYS_RT11 +X extern int $$rsts; /* RSTS/E emulation? */ +X +X if ($$rsts) +X *incend++ = "SY:@"; /* User-defined account */ +X *incend++ = "C:"; /* Decus-C library disk */ +X *incend++ = "SY:"; /* System (boot) disk */ +X#define MAXINCLUDE (NINCLUDE - 3 - IS_INCLUDE) +X#endif +X} +X +Xint +Xdooptions(argc, argv) +Xint argc; +Xchar *argv[]; +X/* +X * dooptions is called to process command line arguments (-Detc). +X * It is called only at cpp startup. +X */ +X{ +X register char *ap; +X register DEFBUF *dp; +X register int c; +X int i, j; +X char *arg; +X SIZES *sizp; /* For -S */ +X int size; /* For -S */ +X int isdatum; /* FALSE for -S* */ +X int endtest; /* For -S */ +X +X for (i = j = 1; i < argc; i++) { +X arg = ap = argv[i]; +X if (*ap++ != '-' || *ap == EOS) +X argv[j++] = argv[i]; +X else { +X c = *ap++; /* Option byte */ +X if (islower(c)) /* Normalize case */ +X c = toupper(c); +X switch (c) { /* Command character */ +X case 'C': /* Keep comments */ +X cflag = TRUE; +X keepcomments = TRUE; +X break; +X +X case 'D': /* Define symbol */ +X#if HOST != SYS_UNIX +X zap_uc(ap); /* Force define to U.C. */ +X#endif +X /* +X * If the option is just "-Dfoo", make it -Dfoo=1 +X */ +X while (*ap != EOS && *ap != '=') +X ap++; +X if (*ap == EOS) +X ap = "1"; +X else +X *ap++ = EOS; +X /* +X * Now, save the word and its definition. +X */ +X dp = defendel(argv[i] + 2, FALSE); +X dp->repl = savestring(ap); +X dp->nargs = DEF_NOARGS; +X break; +X +X case 'E': /* Ignore non-fatal */ +X eflag = TRUE; /* errors. */ +X break; +X +X case 'I': /* Include directory */ +X if (incend >= &incdir[MAXINCLUDE]) +X cfatal("Too many include directories", NULLST); +X *incend++ = ap; +X break; +X +X case 'N': /* No predefineds */ +X nflag++; /* Repeat to undefine */ +X break; /* __LINE__, etc. */ +X +X case 'S': +X sizp = size_table; +X if (isdatum = (*ap != '*')) /* If it's just -S, */ +X endtest = T_FPTR; /* Stop here */ +X else { /* But if it's -S* */ +X ap++; /* Step over '*' */ +X endtest = 0; /* Stop at end marker */ +X } +X while (sizp->bits != endtest && *ap != EOS) { +X if (!isdigit(*ap)) { /* Skip to next digit */ +X ap++; +X continue; +X } +X size = 0; /* Compile the value */ +X while (isdigit(*ap)) { +X size *= 10; +X size += (*ap++ - '0'); +X } +X if (isdatum) +X sizp->size = size; /* Datum size */ +X else +X sizp->psize = size; /* Pointer size */ +X sizp++; +X } +X if (sizp->bits != endtest) +X cwarn("-S, too few values specified in %s", argv[i]); +X else if (*ap != EOS) +X cwarn("-S, too many values, \"%s\" unused", ap); +X break; +X +X case 'U': /* Undefine symbol */ +X#if HOST != SYS_UNIX +X zap_uc(ap); +X#endif +X if (defendel(ap, TRUE) == NULL) +X cwarn("\"%s\" wasn't defined", ap); +X break; +X +X#if DEBUG +X case 'X': /* Debug */ +X debug = (isdigit(*ap)) ? atoi(ap) : 1; +X#if (HOST == SYS_VMS || HOST == SYS_UNIX) +X signal(SIGINT, abort); /* Trap "interrupt" */ +X#endif +X fprintf(stderr, "Debug set to %d\n", debug); +X break; +X#endif +X +X default: /* What is this one? */ +X cwarn("Unknown option \"%s\"", arg); +X fprintf(stderr, "The following options are valid:\n\ +X -C\t\t\tWrite source file comments to output\n\ +X -Dsymbol=value\tDefine a symbol with the given (optional) value\n\ +X -Idirectory\t\tAdd a directory to the #include search list\n\ +X -N\t\t\tDon't predefine target-specific names\n\ +X -Stext\t\tSpecify sizes for #if sizeof\n\ +X -Usymbol\t\tUndefine symbol\n"); +X#if DEBUG +X fprintf(stderr, " -Xvalue\t\tSet internal debug flag\n"); +X#endif +X break; +X } /* Switch on all options */ +X } /* If it's a -option */ +X } /* For all arguments */ +X if (j > 3) { +X cerror( +X "Too many file arguments. Usage: cpp [input [output]]", +X NULLST); +X } +X return (j); /* Return new argc */ +X} +X +X#if HOST != SYS_UNIX +XFILE_LOCAL +Xzap_uc(ap) +Xregister char *ap; +X/* +X * Dec operating systems mangle upper-lower case in command lines. +X * This routine forces the -D and -U arguments to uppercase. +X * It is called only on cpp startup by dooptions(). +X */ +X{ +X while (*ap != EOS) { +X /* +X * Don't use islower() here so it works with Multinational +X */ +X if (*ap >= 'a' && *ap <= 'z') +X *ap = toupper(*ap); +X ap++; +X } +X} +X#endif +X +Xinitdefines() +X/* +X * Initialize the built-in #define's. There are two flavors: +X * #define decus 1 (static definitions) +X * #define __FILE__ ?? (dynamic, evaluated by magic) +X * Called only on cpp startup. +X * +X * Note: the built-in static definitions are supressed by the -N option. +X * __LINE__, __FILE__, and __DATE__ are always present. +X */ +X{ +X register char **pp; +X register char *tp; +X register DEFBUF *dp; +X int i; +X long tvec; +X extern char *ctime(); +X +X /* +X * Predefine the built-in symbols. Allow the +X * implementor to pre-define a symbol as "" to +X * eliminate it. +X */ +X if (nflag == 0) { +X for (pp = preset; *pp != NULL; pp++) { +X if (*pp[0] != EOS) { +X dp = defendel(*pp, FALSE); +X dp->repl = savestring("1"); +X dp->nargs = DEF_NOARGS; +X } +X } +X } +X /* +X * The magic pre-defines (__FILE__ and __LINE__ are +X * initialized with negative argument counts. expand() +X * notices this and calls the appropriate routine. +X * DEF_NOARGS is one greater than the first "magic" definition. +X */ +X if (nflag < 2) { +X for (pp = magic, i = DEF_NOARGS; *pp != NULL; pp++) { +X dp = defendel(*pp, FALSE); +X dp->nargs = --i; +X } +X#if OK_DATE +X /* +X * Define __DATE__ as today's date. +X */ +X dp = defendel("__DATE__", FALSE); +X dp->repl = tp = getmem(27); +X dp->nargs = DEF_NOARGS; +X time(&tvec); +X *tp++ = '"'; +X strcpy(tp, ctime(&tvec)); +X tp[24] = '"'; /* Overwrite newline */ +X#endif +X } +X} +X +X#if HOST == SYS_VMS +X/* +X * getredirection() is intended to aid in porting C programs +X * to VMS (Vax-11 C) which does not support '>' and '<' +X * I/O redirection. With suitable modification, it may +X * useful for other portability problems as well. +X */ +X +Xint +Xgetredirection(argc, argv) +Xint argc; +Xchar **argv; +X/* +X * Process vms redirection arg's. Exit if any error is seen. +X * If getredirection() processes an argument, it is erased +X * from the vector. getredirection() returns a new argc value. +X * +X * Warning: do not try to simplify the code for vms. The code +X * presupposes that getredirection() is called before any data is +X * read from stdin or written to stdout. +X * +X * Normal usage is as follows: +X * +X * main(argc, argv) +X * int argc; +X * char *argv[]; +X * { +X * argc = getredirection(argc, argv); +X * } +X */ +X{ +X register char *ap; /* Argument pointer */ +X int i; /* argv[] index */ +X int j; /* Output index */ +X int file; /* File_descriptor */ +X extern int errno; /* Last vms i/o error */ +X +X for (j = i = 1; i < argc; i++) { /* Do all arguments */ +X switch (*(ap = argv[i])) { +X case '<': /* ': /* >file or >>file */ +X if (*++ap == '>') { /* >>file */ +X /* +X * If the file exists, and is writable by us, +X * call freopen to append to the file (using the +X * file's current attributes). Otherwise, create +X * a new file with "vanilla" attributes as if the +X * argument was given as ">filename". +X * access(name, 2) returns zero if we can write on +X * the specified file. +X */ +X if (access(++ap, 2) == 0) { +X if (freopen(ap, "a", stdout) != NULL) +X break; /* Exit case statement */ +X perror(ap); /* Error, can't append */ +X exit(errno); /* After access test */ +X } /* If file accessable */ +X } +X /* +X * On vms, we want to create the file using "standard" +X * record attributes. creat(...) creates the file +X * using the caller's default protection mask and +X * "variable length, implied carriage return" +X * attributes. dup2() associates the file with stdout. +X */ +X if ((file = creat(ap, 0, "rat=cr", "rfm=var")) == -1 +X || dup2(file, fileno(stdout)) == -1) { +X perror(ap); /* Can't create file */ +X exit(errno); /* is a fatal error */ +X } /* If '>' creation */ +X break; /* Exit case test */ +X +X default: +X argv[j++] = ap; /* Not a redirector */ +X break; /* Exit case test */ +X } +X } /* For all arguments */ +X argv[j] = NULL; /* Terminate argv[] */ +X return (j); /* Return new argc */ +X} +X#endif +X +X +X +END-of-cpp3.c +echo x - cpp4.c +sed 's/^X//' >cpp4.c << 'END-of-cpp4.c' +X/* +X * C P P 4 . C +X * M a c r o D e f i n i t i o n s +X * +X * Edit History +X * 31-Aug-84 MM USENET net.sources release +X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring() +X * so they work correctly with token concatenation. +X * Added string formal recognition. +X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we +X * don't print unnecessary error messages for +X * #if !defined(FOO) && FOO != 0 && 10 / FOO ... +X * 31-Oct-84 ado/MM Added token concatenation +X * 6-Nov-84 MM Split off eval stuff +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X/* +X * parm[], parmp, and parlist[] are used to store #define() argument +X * lists. nargs contains the actual number of parameters stored. +X */ +Xstatic char parm[NPARMWORK + 1]; /* define param work buffer */ +Xstatic char *parmp; /* Free space in parm */ +Xstatic char *parlist[LASTPARM]; /* -> start of each parameter */ +Xstatic int nargs; /* Parameters for this macro */ +X +Xdodefine() +X/* +X * Called from control when a #define is scanned. This module +X * parses formal parameters and the replacement string. When +X * the formal parameter name is encountered in the replacement +X * string, it is replaced by a character in the range 128 to +X * 128+NPARAM (this allows up to 32 parameters within the +X * Dec Multinational range). If cpp is ported to an EBCDIC +X * machine, you will have to make other arrangements. +X * +X * There is some special case code to distinguish +X * #define foo bar +X * from #define foo() bar +X * +X * Also, we make sure that +X * #define foo foo +X * expands to "foo" but doesn't put cpp into an infinite loop. +X * +X * A warning message is printed if you redefine a symbol to a +X * different text. I.e, +X * #define foo 123 +X * #define foo 123 +X * is ok, but +X * #define foo 123 +X * #define foo +123 +X * is not. +X * +X * The following subroutines are called from define(): +X * checkparm called when a token is scanned. It checks through the +X * array of formal parameters. If a match is found, the +X * token is replaced by a control byte which will be used +X * to locate the parameter when the macro is expanded. +X * textput puts a string in the macro work area (parm[]), updating +X * parmp to point to the first free byte in parm[]. +X * textput() tests for work buffer overflow. +X * charput puts a single character in the macro work area (parm[]) +X * in a manner analogous to textput(). +X */ +X{ +X register int c; +X register DEFBUF *dp; /* -> new definition */ +X int isredefine; /* TRUE if redefined */ +X char *old; /* Remember redefined */ +X extern int save(); /* Save char in work[] */ +X +X if (type[(c = skipws())] != LET) +X goto bad_define; +X isredefine = FALSE; /* Set if redefining */ +X if ((dp = lookid(c)) == NULL) /* If not known now */ +X dp = defendel(token, FALSE); /* Save the name */ +X else { /* It's known: */ +X isredefine = TRUE; /* Remember this fact */ +X old = dp->repl; /* Remember replacement */ +X dp->repl = NULL; /* No replacement now */ +X } +X parlist[0] = parmp = parm; /* Setup parm buffer */ +X if ((c = get()) == '(') { /* With arguments? */ +X nargs = 0; /* Init formals counter */ +X do { /* Collect formal parms */ +X if (nargs >= LASTPARM) +X cfatal("Too many arguments for macro", NULLST); +X else if ((c = skipws()) == ')') +X break; /* Got them all */ +X else if (type[c] != LET) /* Bad formal syntax */ +X goto bad_define; +X scanid(c); /* Get the formal param */ +X parlist[nargs++] = parmp; /* Save its start */ +X textput(token); /* Save text in parm[] */ +X } while ((c = skipws()) == ','); /* Get another argument */ +X if (c != ')') /* Must end at ) */ +X goto bad_define; +X c = ' '; /* Will skip to body */ +X } +X else { +X /* +X * DEF_NOARGS is needed to distinguish between +X * "#define foo" and "#define foo()". +X */ +X nargs = DEF_NOARGS; /* No () parameters */ +X } +X if (type[c] == SPA) /* At whitespace? */ +X c = skipws(); /* Not any more. */ +X workp = work; /* Replacement put here */ +X inmacro = TRUE; /* Keep \ now */ +X while (c != EOF_CHAR && c != '\n') { /* Compile macro body */ +X#if OK_CONCAT +X if (c == '#') { /* Token concatenation? */ +X while (workp > work && type[workp[-1]] == SPA) +X --workp; /* Erase leading spaces */ +X save(TOK_SEP); /* Stuff a delimiter */ +X c = skipws(); /* Eat whitespace */ +X if (type[c] == LET) /* Another token here? */ +X ; /* Stuff it normally */ +X else if (type[c] == DIG) { /* Digit string after? */ +X while (type[c] == DIG) { /* Stuff the digits */ +X save(c); +X c = get(); +X } +X save(TOK_SEP); /* Delimit 2nd token */ +X } +X else { +X ciwarn("Strange character after # (%d.)", c); +X } +X continue; +X } +X#endif +X switch (type[c]) { +X case LET: +X checkparm(c, dp); /* Might be a formal */ +X break; +X +X case DIG: /* Number in mac. body */ +X case DOT: /* Maybe a float number */ +X scannumber(c, save); /* Scan it off */ +X break; +X +X case QUO: /* String in mac. body */ +X#if STRING_FORMAL +X stparmscan(c, dp); /* Do string magic */ +X#else +X stparmscan(c); +X#endif +X break; +X +X case BSH: /* Backslash */ +X save('\\'); +X if ((c = get()) == '\n') +X wrongline = TRUE; +X save(c); +X break; +X +X case SPA: /* Absorb whitespace */ +X /* +X * Note: the "end of comment" marker is passed on +X * to allow comments to separate tokens. +X */ +X if (workp[-1] == ' ') /* Absorb multiple */ +X break; /* spaces */ +X else if (c == '\t') +X c = ' '; /* Normalize tabs */ +X /* Fall through to store character */ +X default: /* Other character */ +X save(c); +X break; +X } +X c = get(); +X } +X inmacro = FALSE; /* Stop newline hack */ +X unget(); /* For control check */ +X if (workp > work && workp[-1] == ' ') /* Drop trailing blank */ +X workp--; +X *workp = EOS; /* Terminate work */ +X dp->repl = savestring(work); /* Save the string */ +X dp->nargs = nargs; /* Save arg count */ +X#if DEBUG +X if (debug) +X dumpadef("macro definition", dp); +X#endif +X if (isredefine) { /* Error if redefined */ +X if ((old != NULL && dp->repl != NULL && !streq(old, dp->repl)) +X || (old == NULL && dp->repl != NULL) +X || (old != NULL && dp->repl == NULL)) { +X cerror("Redefining defined variable \"%s\"", dp->name); +X } +X if (old != NULL) /* We don't need the */ +X free(old); /* old definition now. */ +X } +X return; +X +Xbad_define: +X cerror("#define syntax error", NULLST); +X inmacro = FALSE; /* Stop hack */ +X} +X +Xcheckparm(c, dp) +Xregister int c; +XDEFBUF *dp; +X/* +X * Replace this param if it's defined. Note that the macro name is a +X * possible replacement token. We stuff DEF_MAGIC in front of the token +X * which is treated as a LETTER by the token scanner and eaten by +X * the output routine. This prevents the macro expander from +X * looping if someone writes "#define foo foo". +X */ +X{ +X register int i; +X register char *cp; +X +X scanid(c); /* Get parm to token[] */ +X for (i = 0; i < nargs; i++) { /* For each argument */ +X if (streq(parlist[i], token)) { /* If it's known */ +X save(i + MAC_PARM); /* Save a magic cookie */ +X return; /* And exit the search */ +X } +X } +X if (streq(dp->name, token)) /* Macro name in body? */ +X save(DEF_MAGIC); /* Save magic marker */ +X for (cp = token; *cp != EOS;) /* And save */ +X save(*cp++); /* The token itself */ +X} +X +X#if STRING_FORMAL +Xstparmscan(delim, dp) +Xint delim; +Xregister DEFBUF *dp; +X/* +X * Scan the string (starting with the given delimiter). +X * The token is replaced if it is the only text in this string or +X * character constant. The algorithm follows checkparm() above. +X * Note that scanstring() has approved of the string. +X */ +X{ +X register int c; +X +X /* +X * Warning -- this code hasn't been tested for a while. +X * It exists only to preserve compatibility with earlier +X * implementations of cpp. It is not part of the Draft +X * ANSI Standard C language. +X */ +X save(delim); +X instring = TRUE; +X while ((c = get()) != delim +X && c != '\n' +X && c != EOF_CHAR) { +X if (type[c] == LET) /* Maybe formal parm */ +X checkparm(c, dp); +X else { +X save(c); +X if (c == '\\') +X save(get()); +X } +X } +X instring = FALSE; +X if (c != delim) +X cerror("Unterminated string in macro body", NULLST); +X save(c); +X} +X#else +Xstparmscan(delim) +Xint delim; +X/* +X * Normal string parameter scan. +X */ +X{ +X register char *wp; +X register int i; +X extern int save(); +X +X wp = workp; /* Here's where it starts */ +X if (!scanstring(delim, save)) +X return; /* Exit on scanstring error */ +X workp[-1] = EOS; /* Erase trailing quote */ +X wp++; /* -> first string content byte */ +X for (i = 0; i < nargs; i++) { +X if (streq(parlist[i], wp)) { +X *wp++ = MAC_PARM + PAR_MAC; /* Stuff a magic marker */ +X *wp++ = (i + MAC_PARM); /* Make a formal marker */ +X *wp = wp[-3]; /* Add on closing quote */ +X workp = wp + 1; /* Reset string end */ +X return; +X } +X } +X workp[-1] = wp[-1]; /* Nope, reset end quote. */ +X} +X#endif +X +Xdoundef() +X/* +X * Remove the symbol from the defined list. +X * Called from the #control processor. +X */ +X{ +X register int c; +X +X if (type[(c = skipws())] != LET) +X cerror("Illegal #undef argument", NULLST); +X else { +X scanid(c); /* Get name to token[] */ +X if (defendel(token, TRUE) == NULL) { +X cwarn("Symbol \"%s\" not defined in #undef", token); +X } +X } +X} +X +Xtextput(text) +Xchar *text; +X/* +X * Put the string in the parm[] buffer. +X */ +X{ +X register int size; +X +X size = strlen(text) + 1; +X if ((parmp + size) >= &parm[NPARMWORK]) +X cfatal("Macro work area overflow", NULLST); +X else { +X strcpy(parmp, text); +X parmp += size; +X } +X} +X +Xcharput(c) +Xregister int c; +X/* +X * Put the byte in the parm[] buffer. +X */ +X{ +X if (parmp >= &parm[NPARMWORK]) +X cfatal("Macro work area overflow", NULLST); +X else { +X *parmp++ = c; +X } +X} +X +X/* +X * M a c r o E x p a n s i o n +X */ +X +Xstatic DEFBUF *macro; /* Catches start of infinite macro */ +X +Xexpand(tokenp) +Xregister DEFBUF *tokenp; +X/* +X * Expand a macro. Called from the cpp mainline routine (via subroutine +X * macroid()) when a token is found in the symbol table. It calls +X * expcollect() to parse actual parameters, checking for the correct number. +X * It then creates a "file" containing a single line containing the +X * macro with actual parameters inserted appropriately. This is +X * "pushed back" onto the input stream. (When the get() routine runs +X * off the end of the macro line, it will dismiss the macro itself.) +X */ +X{ +X register int c; +X register FILEINFO *file; +X extern FILEINFO *getfile(); +X +X#if DEBUG +X if (debug) +X dumpadef("expand entry", tokenp); +X#endif +X /* +X * If no macro is pending, save the name of this macro +X * for an eventual error message. +X */ +X if (recursion++ == 0) +X macro = tokenp; +X else if (recursion == RECURSION_LIMIT) { +X cerror("Recursive macro definition of \"%s\"", tokenp->name); +X fprintf(stderr, "(Defined by \"%s\")\n", macro->name); +X if (rec_recover) { +X do { +X c = get(); +X } while (infile != NULL && infile->fp == NULL); +X unget(); +X recursion = 0; +X return; +X } +X } +X /* +X * Here's a macro to expand. +X */ +X nargs = 0; /* Formals counter */ +X parmp = parm; /* Setup parm buffer */ +X switch (tokenp->nargs) { +X case (-2): /* __LINE__ */ +X sprintf(work, "%d", line); +X ungetstring(work); +X break; +X +X case (-3): /* __FILE__ */ +X for (file = infile; file != NULL; file = file->parent) { +X if (file->fp != NULL) { +X sprintf(work, "\"%s\"", (file->progname != NULL) +X ? file->progname : file->filename); +X ungetstring(work); +X break; +X } +X } +X break; +X +X default: +X /* +X * Nothing funny about this macro. +X */ +X if (tokenp->nargs < 0) +X cfatal("Bug: Illegal __ macro \"%s\"", tokenp->name); +X while ((c = skipws()) == '\n') /* Look for (, skipping */ +X wrongline = TRUE; /* spaces and newlines */ +X if (c != '(') { +X /* +X * If the programmer writes +X * #define foo() ... +X * ... +X * foo [no ()] +X * just write foo to the output stream. +X */ +X unget(); +X cwarn("Macro \"%s\" needs arguments", tokenp->name); +X fputs(tokenp->name, stdout); +X return; +X } +X else if (expcollect()) { /* Collect arguments */ +X if (tokenp->nargs != nargs) { /* Should be an error? */ +X cwarn("Wrong number of macro arguments for \"%s\"", +X tokenp->name); +X } +X#if DEBUG +X if (debug) +X dumpparm("expand"); +X#endif +X } /* Collect arguments */ +X case DEF_NOARGS: /* No parameters just stuffs */ +X expstuff(tokenp); /* Do actual parameters */ +X } /* nargs switch */ +X} +X +XFILE_LOCAL int +Xexpcollect() +X/* +X * Collect the actual parameters for this macro. TRUE if ok. +X */ +X{ +X register int c; +X register int paren; /* For embedded ()'s */ +X extern int charput(); +X +X for (;;) { +X paren = 0; /* Collect next arg. */ +X while ((c = skipws()) == '\n') /* Skip over whitespace */ +X wrongline = TRUE; /* and newlines. */ +X if (c == ')') { /* At end of all args? */ +X /* +X * Note that there is a guard byte in parm[] +X * so we don't have to check for overflow here. +X */ +X *parmp = EOS; /* Make sure terminated */ +X break; /* Exit collection loop */ +X } +X else if (nargs >= LASTPARM) +X cfatal("Too many arguments in macro expansion", NULLST); +X parlist[nargs++] = parmp; /* At start of new arg */ +X for (;; c = cget()) { /* Collect arg's bytes */ +X if (c == EOF_CHAR) { +X cerror("end of file within macro argument", NULLST); +X return (FALSE); /* Sorry. */ +X } +X else if (c == '\\') { /* Quote next character */ +X charput(c); /* Save the \ for later */ +X charput(cget()); /* Save the next char. */ +X continue; /* And go get another */ +X } +X else if (type[c] == QUO) { /* Start of string? */ +X scanstring(c, charput); /* Scan it off */ +X continue; /* Go get next char */ +X } +X else if (c == '(') /* Worry about balance */ +X paren++; /* To know about commas */ +X else if (c == ')') { /* Other side too */ +X if (paren == 0) { /* At the end? */ +X unget(); /* Look at it later */ +X break; /* Exit arg getter. */ +X } +X paren--; /* More to come. */ +X } +X else if (c == ',' && paren == 0) /* Comma delimits args */ +X break; +X else if (c == '\n') /* Newline inside arg? */ +X wrongline = TRUE; /* We'll need a #line */ +X charput(c); /* Store this one */ +X } /* Collect an argument */ +X charput(EOS); /* Terminate argument */ +X#if DEBUG +X if (debug) +X printf("parm[%d] = \"%s\"\n", nargs, parlist[nargs - 1]); +X#endif +X } /* Collect all args. */ +X return (TRUE); /* Normal return */ +X} +X +XFILE_LOCAL +Xexpstuff(tokenp) +XDEFBUF *tokenp; /* Current macro being expanded */ +X/* +X * Stuff the macro body, replacing formal parameters by actual parameters. +X */ +X{ +X register int c; /* Current character */ +X register char *inp; /* -> repl string */ +X register char *defp; /* -> macro output buff */ +X int size; /* Actual parm. size */ +X char *defend; /* -> output buff end */ +X int string_magic; /* String formal hack */ +X FILEINFO *file; /* Funny #include */ +X extern FILEINFO *getfile(); +X +X file = getfile(NBUFF, tokenp->name); +X inp = tokenp->repl; /* -> macro replacement */ +X defp = file->buffer; /* -> output buffer */ +X defend = defp + (NBUFF - 1); /* Note its end */ +X if (inp != NULL) { +X while ((c = (*inp++ & 0xFF)) != EOS) { +X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) { +X string_magic = (c == (MAC_PARM + PAR_MAC)); +X if (string_magic) +X c = (*inp++ & 0xFF); +X /* +X * Replace formal parameter by actual parameter string. +X */ +X if ((c -= MAC_PARM) < nargs) { +X size = strlen(parlist[c]); +X if ((defp + size) >= defend) +X goto nospace; +X /* +X * Erase the extra set of quotes. +X */ +X if (string_magic && defp[-1] == parlist[c][0]) { +X strcpy(defp-1, parlist[c]); +X defp += (size - 2); +X } +X else { +X strcpy(defp, parlist[c]); +X defp += size; +X } +X } +X } +X else if (defp >= defend) { +Xnospace: cfatal("Out of space in macro \"%s\" arg expansion", +X tokenp->name); +X } +X else { +X *defp++ = c; +X } +X } +X } +X *defp = EOS; +X#if DEBUG +X if (debug > 1) +X printf("macroline: \"%s\"\n", file->buffer); +X#endif +X} +X +X#if DEBUG +Xdumpparm(why) +Xchar *why; +X/* +X * Dump parameter list. +X */ +X{ +X register int i; +X +X printf("dump of %d parameters (%d bytes total) %s\n", +X nargs, parmp - parm, why); +X for (i = 0; i < nargs; i++) { +X printf("parm[%d] (%d) = \"%s\"\n", +X i + 1, strlen(parlist[i]), parlist[i]); +X } +X} +X#endif +END-of-cpp4.c +exit diff --git a/sys/unix/cpp3.shr b/sys/unix/cpp3.shr new file mode 100644 index 0000000..d437d36 --- /dev/null +++ b/sys/unix/cpp3.shr @@ -0,0 +1,1901 @@ +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +# cpp5.c +# cpp6.c +# +echo x - cpp5.c +sed 's/^X//' >cpp5.c << 'END-of-cpp5.c' +X/* +X * C P P 5 . C +X * E x p r e s s i o n E v a l u a t i o n +X * +X * Edit History +X * 31-Aug-84 MM USENET net.sources release +X * 04-Oct-84 MM __LINE__ and __FILE__ must call ungetstring() +X * so they work correctly with token concatenation. +X * Added string formal recognition. +X * 25-Oct-84 MM "Short-circuit" evaluate #if's so that we +X * don't print unnecessary error messages for +X * #if !defined(FOO) && FOO != 0 && 10 / FOO ... +X * 31-Oct-84 ado/MM Added token concatenation +X * 6-Nov-84 MM Split from #define stuff, added sizeof stuff +X * 19-Nov-84 ado #if error returns TRUE for (sigh) compatibility +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X +X/* +X * Evaluate an #if expression. +X */ +X +Xstatic char *opname[] = { /* For debug and error messages */ +X"end of expression", "val", "id", +X "+", "-", "*", "/", "%", +X "<<", ">>", "&", "|", "^", +X "==", "!=", "<", "<=", ">=", ">", +X "&&", "||", "?", ":", ",", +X "unary +", "unary -", "~", "!", "(", ")", "(none)", +X}; +X +X/* +X * opdope[] has the operator precedence: +X * Bits +X * 7 Unused (so the value is always positive) +X * 6-2 Precedence (000x .. 017x) +X * 1-0 Binary op. flags: +X * 01 The binop flag should be set/cleared when this op is seen. +X * 10 The new value of the binop flag. +X * Note: Expected, New binop +X * constant 0 1 Binop, end, or ) should follow constants +X * End of line 1 0 End may not be preceeded by an operator +X * binary 1 0 Binary op follows a value, value follows. +X * unary 0 0 Unary op doesn't follow a value, value follows +X * ( 0 0 Doesn't follow value, value or unop follows +X * ) 1 1 Follows value. Op follows. +X */ +X +Xstatic char opdope[OP_MAX] = { +X 0001, /* End of expression */ +X 0002, /* Digit */ +X 0000, /* Letter (identifier) */ +X 0141, 0141, 0151, 0151, 0151, /* ADD, SUB, MUL, DIV, MOD */ +X 0131, 0131, 0101, 0071, 0071, /* ASL, ASR, AND, OR, XOR */ +X 0111, 0111, 0121, 0121, 0121, 0121, /* EQ, NE, LT, LE, GE, GT */ +X 0061, 0051, 0041, 0041, 0031, /* ANA, ORO, QUE, COL, CMA */ +X/* +X * Unary op's follow +X */ +X 0160, 0160, 0160, 0160, /* NEG, PLU, COM, NOT */ +X 0170, 0013, 0023, /* LPA, RPA, END */ +X}; +X/* +X * OP_QUE and OP_RPA have alternate precedences: +X */ +X#define OP_RPA_PREC 0013 +X#define OP_QUE_PREC 0034 +X +X/* +X * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that +X * #if FOO != 0 && 10 / FOO ... +X * doesn't generate an error message. They are stored in optab.skip. +X */ +X#define S_ANDOR 2 +X#define S_QUEST 1 +X +Xtypedef struct optab { +X char op; /* Operator */ +X char prec; /* Its precedence */ +X char skip; /* Short-circuit: TRUE to skip */ +X} OPTAB; +Xstatic int evalue; /* Current value from evallex() */ +X +X#ifdef nomacargs +XFILE_LOCAL int +Xisbinary(op) +Xregister int op; +X{ +X return (op >= FIRST_BINOP && op <= LAST_BINOP); +X} +X +XFILE_LOCAL int +Xisunary(op) +Xregister int op; +X{ +X return (op >= FIRST_UNOP && op <= LAST_UNOP); +X} +X#else +X#define isbinary(op) (op >= FIRST_BINOP && op <= LAST_BINOP) +X#define isunary(op) (op >= FIRST_UNOP && op <= LAST_UNOP) +X#endif +X +X/* +X * The following definitions are used to specify basic variable sizes. +X */ +X +X#ifndef S_CHAR +X#define S_CHAR (sizeof (char)) +X#endif +X#ifndef S_SINT +X#define S_SINT (sizeof (short int)) +X#endif +X#ifndef S_INT +X#define S_INT (sizeof (int)) +X#endif +X#ifndef S_LINT +X#define S_LINT (sizeof (long int)) +X#endif +X#ifndef S_FLOAT +X#define S_FLOAT (sizeof (float)) +X#endif +X#ifndef S_DOUBLE +X#define S_DOUBLE (sizeof (double)) +X#endif +X#ifndef S_PCHAR +X#define S_PCHAR (sizeof (char *)) +X#endif +X#ifndef S_PSINT +X#define S_PSINT (sizeof (short int *)) +X#endif +X#ifndef S_PINT +X#define S_PINT (sizeof (int *)) +X#endif +X#ifndef S_PLINT +X#define S_PLINT (sizeof (long int *)) +X#endif +X#ifndef S_PFLOAT +X#define S_PFLOAT (sizeof (float *)) +X#endif +X#ifndef S_PDOUBLE +X#define S_PDOUBLE (sizeof (double *)) +X#endif +X#ifndef S_PFPTR +X#define S_PFPTR (sizeof (int (*)())) +X#endif +X +Xtypedef struct types { +X short type; /* This is the bit if */ +X char *name; /* this is the token word */ +X} TYPES; +X +Xstatic TYPES basic_types[] = { +X { T_CHAR, "char", }, +X { T_INT, "int", }, +X { T_FLOAT, "float", }, +X { T_DOUBLE, "double", }, +X { T_SHORT, "short", }, +X { T_LONG, "long", }, +X { T_SIGNED, "signed", }, +X { T_UNSIGNED, "unsigned", }, +X { 0, NULL, }, /* Signal end */ +X}; +X +X/* +X * Test_table[] is used to test for illegal combinations. +X */ +Xstatic short test_table[] = { +X T_FLOAT | T_DOUBLE | T_LONG | T_SHORT, +X T_FLOAT | T_DOUBLE | T_CHAR | T_INT, +X T_FLOAT | T_DOUBLE | T_SIGNED | T_UNSIGNED, +X T_LONG | T_SHORT | T_CHAR, +X 0 /* end marker */ +X}; +X +X/* +X * The order of this table is important -- it is also referenced by +X * the command line processor to allow run-time overriding of the +X * built-in size values. The order must not be changed: +X * char, short, int, long, float, double (func pointer) +X */ +XSIZES size_table[] = { +X { T_CHAR, S_CHAR, S_PCHAR }, /* char */ +X { T_SHORT, S_SINT, S_PSINT }, /* short int */ +X { T_INT, S_INT, S_PINT }, /* int */ +X { T_LONG, S_LINT, S_PLINT }, /* long */ +X { T_FLOAT, S_FLOAT, S_PFLOAT }, /* float */ +X { T_DOUBLE, S_DOUBLE, S_PDOUBLE }, /* double */ +X { T_FPTR, 0, S_PFPTR }, /* int (*()) */ +X { 0, 0, 0 }, /* End of table */ +X}; +X +Xint +Xeval() +X/* +X * Evaluate an expression. Straight-forward operator precedence. +X * This is called from control() on encountering an #if statement. +X * It calls the following routines: +X * evallex Lexical analyser -- returns the type and value of +X * the next input token. +X * evaleval Evaluate the current operator, given the values on +X * the value stack. Returns a pointer to the (new) +X * value stack. +X * For compatiblity with older cpp's, this return returns 1 (TRUE) +X * if a syntax error is detected. +X */ +X{ +X register int op; /* Current operator */ +X register int *valp; /* -> value vector */ +X register OPTAB *opp; /* Operator stack */ +X int prec; /* Op precedence */ +X int binop; /* Set if binary op. needed */ +X int op1; /* Operand from stack */ +X int skip; /* For short-circuit testing */ +X int value[NEXP]; /* Value stack */ +X OPTAB opstack[NEXP]; /* Operand stack */ +X extern int *evaleval(); /* Does actual evaluation */ +X +X valp = value; +X opp = opstack; +X opp->op = OP_END; /* Mark bottom of stack */ +X opp->prec = opdope[OP_END]; /* And its precedence */ +X opp->skip = 0; /* Not skipping now */ +X binop = 0; +Xagain: ; +X#ifdef DEBUG_EVAL +X printf("In #if at again: skip = %d, binop = %d, line is: %s", +X opp->skip, binop, infile->bptr); +X#endif +X if ((op = evallex(opp->skip)) == OP_SUB && binop == 0) +X op = OP_NEG; /* Unary minus */ +X else if (op == OP_ADD && binop == 0) +X op = OP_PLU; /* Unary plus */ +X else if (op == OP_FAIL) +X return (1); /* Error in evallex */ +X#ifdef DEBUG_EVAL +X printf("op = %s, opdope = %03o, binop = %d, skip = %d\n", +X opname[op], opdope[op], binop, opp->skip); +X#endif +X if (op == DIG) { /* Value? */ +X if (binop != 0) { +X cerror("misplaced constant in #if", NULLST); +X return (1); +X } +X else if (valp >= &value[NEXP-1]) { +X cerror("#if value stack overflow", NULLST); +X return (1); +X } +X else { +X#ifdef DEBUG_EVAL +X printf("pushing %d onto value stack[%d]\n", +X evalue, valp - value); +X#endif +X *valp++ = evalue; +X binop = 1; +X } +X goto again; +X } +X else if (op > OP_END) { +X cerror("Illegal #if line", NULLST); +X return (1); +X } +X prec = opdope[op]; +X if (binop != (prec & 1)) { +X cerror("Operator %s in incorrect context", opname[op]); +X return (1); +X } +X binop = (prec & 2) >> 1; +X for (;;) { +X#ifdef DEBUG_EVAL +X printf("op %s, prec %d., stacked op %s, prec %d, skip %d\n", +X opname[op], prec, opname[opp->op], opp->prec, opp->skip); +X#endif +X if (prec > opp->prec) { +X if (op == OP_LPA) +X prec = OP_RPA_PREC; +X else if (op == OP_QUE) +X prec = OP_QUE_PREC; +X op1 = opp->skip; /* Save skip for test */ +X /* +X * Push operator onto op. stack. +X */ +X opp++; +X if (opp >= &opstack[NEXP]) { +X cerror("expression stack overflow at op \"%s\"", +X opname[op]); +X return (1); +X } +X opp->op = op; +X opp->prec = prec; +X skip = (valp[-1] != 0); /* Short-circuit tester */ +X /* +X * Do the short-circuit stuff here. Short-circuiting +X * stops automagically when operators are evaluated. +X */ +X if ((op == OP_ANA && !skip) +X || (op == OP_ORO && skip)) +X opp->skip = S_ANDOR; /* And/or skip starts */ +X else if (op == OP_QUE) /* Start of ?: operator */ +X opp->skip = (op1 & S_ANDOR) | ((!skip) ? S_QUEST : 0); +X else if (op == OP_COL) { /* : inverts S_QUEST */ +X opp->skip = (op1 & S_ANDOR) +X | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST); +X } +X else { /* Other ops leave */ +X opp->skip = op1; /* skipping unchanged. */ +X } +X#ifdef DEBUG_EVAL +X printf("stacking %s, valp[-1] == %d at %s", +X opname[op], valp[-1], infile->bptr); +X dumpstack(opstack, opp, value, valp); +X#endif +X goto again; +X } +X /* +X * Pop operator from op. stack and evaluate it. +X * End of stack and '(' are specials. +X */ +X skip = opp->skip; /* Remember skip value */ +X switch ((op1 = opp->op)) { /* Look at stacked op */ +X case OP_END: /* Stack end marker */ +X if (op == OP_EOE) +X return (valp[-1]); /* Finished ok. */ +X goto again; /* Read another op. */ +X +X case OP_LPA: /* ( on stack */ +X if (op != OP_RPA) { /* Matches ) on input */ +X cerror("unbalanced paren's, op is \"%s\"", opname[op]); +X return (1); +X } +X opp--; /* Unstack it */ +X /* goto again; -- Fall through */ +X +X case OP_QUE: +X goto again; /* Evaluate true expr. */ +X +X case OP_COL: /* : on stack. */ +X opp--; /* Unstack : */ +X if (opp->op != OP_QUE) { /* Matches ? on stack? */ +X cerror("Misplaced '?' or ':', previous operator is %s", +X opname[opp->op]); +X return (1); +X } +X /* +X * Evaluate op1. +X */ +X default: /* Others: */ +X opp--; /* Unstack the operator */ +X#ifdef DEBUG_EVAL +X printf("Stack before evaluation of %s\n", opname[op1]); +X dumpstack(opstack, opp, value, valp); +X#endif +X valp = evaleval(valp, op1, skip); +X#ifdef DEBUG_EVAL +X printf("Stack after evaluation\n"); +X dumpstack(opstack, opp, value, valp); +X#endif +X } /* op1 switch end */ +X } /* Stack unwind loop */ +X} +X +XFILE_LOCAL int +Xevallex(skip) +Xint skip; /* TRUE if short-circuit evaluation */ +X/* +X * Return next eval operator or value. Called from eval(). It +X * calls a special-purpose routines for 'char' strings and +X * numeric values: +X * evalchar called to evaluate 'x' +X * evalnum called to evaluate numbers. +X */ +X{ +X register int c, c1, t; +X +Xagain: do { /* Collect the token */ +X c = skipws(); +X if ((c = macroid(c)) == EOF_CHAR || c == '\n') { +X unget(); +X return (OP_EOE); /* End of expression */ +X } +X } while ((t = type[c]) == LET && catenate()); +X if (t == INV) { /* Total nonsense */ +X if (!skip) { +X if (isascii(c) && isprint(c)) +X cierror("illegal character '%c' in #if", c); +X else +X cierror("illegal character (%d decimal) in #if", c); +X } +X return (OP_FAIL); +X } +X else if (t == QUO) { /* ' or " */ +X if (c == '\'') { /* Character constant */ +X evalue = evalchar(skip); /* Somewhat messy */ +X#ifdef DEBUG_EVAL +X printf("evalchar returns %d.\n", evalue); +X#endif +X return (DIG); /* Return a value */ +X } +X cerror("Can't use a string in an #if", NULLST); +X return (OP_FAIL); +X } +X else if (t == LET) { /* ID must be a macro */ +X if (streq(token, "defined")) { /* Or defined name */ +X c1 = c = skipws(); +X if (c == '(') /* Allow defined(name) */ +X c = skipws(); +X if (type[c] == LET) { +X evalue = (lookid(c) != NULL); +X if (c1 != '(' /* Need to balance */ +X || skipws() == ')') /* Did we balance? */ +X return (DIG); /* Parsed ok */ +X } +X cerror("Bad #if ... defined() syntax", NULLST); +X return (OP_FAIL); +X } +X else if (streq(token, "sizeof")) /* New sizeof hackery */ +X return (dosizeof()); /* Gets own routine */ +X /* +X * The Draft ANSI C Standard says that an undefined symbol +X * in an #if has the value zero. We are a bit pickier, +X * warning except where the programmer was careful to write +X * #if defined(foo) ? foo : 0 +X */ +X#ifdef VERBOSE +X if (!skip) +X cwarn("undefined symbol \"%s\" in #if, 0 used", token); +X#endif +X evalue = 0; +X return (DIG); +X } +X else if (t == DIG) { /* Numbers are harder */ +X evalue = evalnum(c); +X#ifdef DEBUG_EVAL +X printf("evalnum returns %d.\n", evalue); +X#endif +X } +X else if (strchr("!=<>&|\\", c) != NULL) { +X /* +X * Process a possible multi-byte lexeme. +X */ +X c1 = cget(); /* Peek at next char */ +X switch (c) { +X case '!': +X if (c1 == '=') +X return (OP_NE); +X break; +X +X case '=': +X if (c1 != '=') { /* Can't say a=b in #if */ +X unget(); +X cerror("= not allowed in #if", NULLST); +X return (OP_FAIL); +X } +X return (OP_EQ); +X +X case '>': +X case '<': +X if (c1 == c) +X return ((c == '<') ? OP_ASL : OP_ASR); +X else if (c1 == '=') +X return ((c == '<') ? OP_LE : OP_GE); +X break; +X +X case '|': +X case '&': +X if (c1 == c) +X return ((c == '|') ? OP_ORO : OP_ANA); +X break; +X +X case '\\': +X if (c1 == '\n') /* Multi-line if */ +X goto again; +X cerror("Unexpected \\ in #if", NULLST); +X return (OP_FAIL); +X } +X unget(); +X } +X return (t); +X} +X +XFILE_LOCAL int +Xdosizeof() +X/* +X * Process the sizeof (basic type) operation in an #if string. +X * Sets evalue to the size and returns +X * DIG success +X * OP_FAIL bad parse or something. +X */ +X{ +X register int c; +X register TYPES *tp; +X register SIZES *sizp; +X register short *testp; +X short typecode; +X +X if ((c = skipws()) != '(') +X goto nogood; +X /* +X * Scan off the tokens. +X */ +X typecode = 0; +X while ((c = skipws())) { +X if ((c = macroid(c)) == EOF_CHAR || c == '\n') +X goto nogood; /* End of line is a bug */ +X else if (c == '(') { /* thing (*)() func ptr */ +X if (skipws() == '*' +X && skipws() == ')') { /* We found (*) */ +X if (skipws() != '(') /* Let () be optional */ +X unget(); +X else if (skipws() != ')') +X goto nogood; +X typecode |= T_FPTR; /* Function pointer */ +X } +X else { /* Junk is a bug */ +X goto nogood; +X } +X } +X else if (type[c] != LET) /* Exit if not a type */ +X break; +X else if (!catenate()) { /* Maybe combine tokens */ +X /* +X * Look for this unexpandable token in basic_types. +X * The code accepts "int long" as well as "long int" +X * which is a minor bug as bugs go (and one shared with +X * a lot of C compilers). +X */ +X for (tp = basic_types; tp->name != NULLST; tp++) { +X if (streq(token, tp->name)) +X break; +X } +X if (tp->name == NULLST) { +X cerror("#if sizeof, unknown type \"%s\"", token); +X return (OP_FAIL); +X } +X typecode |= tp->type; /* Or in the type bit */ +X } +X } +X /* +X * We are at the end of the type scan. Chew off '*' if necessary. +X */ +X if (c == '*') { +X typecode |= T_PTR; +X c = skipws(); +X } +X if (c == ')') { /* Last syntax check */ +X for (testp = test_table; *testp != 0; testp++) { +X if (!bittest(typecode & *testp)) { +X cerror("#if ... sizeof: illegal type combination", NULLST); +X return (OP_FAIL); +X } +X } +X /* +X * We assume that all function pointers are the same size: +X * sizeof (int (*)()) == sizeof (float (*)()) +X * We assume that signed and unsigned don't change the size: +X * sizeof (signed int) == (sizeof unsigned int) +X */ +X if ((typecode & T_FPTR) != 0) /* Function pointer */ +X typecode = T_FPTR | T_PTR; +X else { /* Var or var * datum */ +X typecode &= ~(T_SIGNED | T_UNSIGNED); +X if ((typecode & (T_SHORT | T_LONG)) != 0) +X typecode &= ~T_INT; +X } +X if ((typecode & ~T_PTR) == 0) { +X cerror("#if sizeof() error, no type specified", NULLST); +X return (OP_FAIL); +X } +X /* +X * Exactly one bit (and possibly T_PTR) may be set. +X */ +X for (sizp = size_table; sizp->bits != 0; sizp++) { +X if ((typecode & ~T_PTR) == sizp->bits) { +X evalue = ((typecode & T_PTR) != 0) +X ? sizp->psize : sizp->size; +X return (DIG); +X } +X } /* We shouldn't fail */ +X cierror("#if ... sizeof: bug, unknown type code 0x%x", typecode); +X return (OP_FAIL); +X } +X +Xnogood: unget(); +X cerror("#if ... sizeof() syntax error", NULLST); +X return (OP_FAIL); +X} +X +XFILE_LOCAL int +Xbittest(value) +X/* +X * TRUE if value is zero or exactly one bit is set in value. +X */ +X{ +X#if (4096 & ~(-4096)) == 0 +X return ((value & ~(-value)) == 0); +X#else +X /* +X * Do it the hard way (for non 2's complement machines) +X */ +X return (value == 0 || value ^ (value - 1) == (value * 2 - 1)); +X#endif +X} +X +XFILE_LOCAL int +Xevalnum(c) +Xregister int c; +X/* +X * Expand number for #if lexical analysis. Note: evalnum recognizes +X * the unsigned suffix, but only returns a signed int value. +X */ +X{ +X register int value; +X register int base; +X register int c1; +X +X if (c != '0') +X base = 10; +X else if ((c = cget()) == 'x' || c == 'X') { +X base = 16; +X c = cget(); +X } +X else base = 8; +X value = 0; +X for (;;) { +X c1 = c; +X if (isascii(c) && isupper(c1)) +X c1 = tolower(c1); +X if (c1 >= 'a') +X c1 -= ('a' - 10); +X else c1 -= '0'; +X if (c1 < 0 || c1 >= base) +X break; +X value *= base; +X value += c1; +X c = cget(); +X } +X if (c == 'u' || c == 'U') /* Unsigned nonsense */ +X c = cget(); +X unget(); +X return (value); +X} +X +XFILE_LOCAL int +Xevalchar(skip) +Xint skip; /* TRUE if short-circuit evaluation */ +X/* +X * Get a character constant +X */ +X{ +X register int c; +X register int value; +X register int count; +X +X instring = TRUE; +X if ((c = cget()) == '\\') { +X switch ((c = cget())) { +X case 'a': /* New in Standard */ +X#if ('a' == '\a' || '\a' == ALERT) +X value = ALERT; /* Use predefined value */ +X#else +X value = '\a'; /* Use compiler's value */ +X#endif +X break; +X +X case 'b': +X value = '\b'; +X break; +X +X case 'f': +X value = '\f'; +X break; +X +X case 'n': +X value = '\n'; +X break; +X +X case 'r': +X value = '\r'; +X break; +X +X case 't': +X value = '\t'; +X break; +X +X case 'v': /* New in Standard */ +X#if ('v' == '\v' || '\v' == VT) +X value = VT; /* Use predefined value */ +X#else +X value = '\v'; /* Use compiler's value */ +X#endif +X break; +X +X case 'x': /* '\xFF' */ +X count = 3; +X value = 0; +X while ((((c = get()) >= '0' && c <= '9') +X || (c >= 'a' && c <= 'f') +X || (c >= 'A' && c <= 'F')) +X && (--count >= 0)) { +X value *= 16; +X value += (c <= '9') ? (c - '0') : ((c & 0xF) + 9); +X } +X unget(); +X break; +X +X default: +X if (c >= '0' && c <= '7') { +X count = 3; +X value = 0; +X while (c >= '0' && c <= '7' && --count >= 0) { +X value *= 8; +X value += (c - '0'); +X c = get(); +X } +X unget(); +X } +X else value = c; +X break; +X } +X } +X else if (c == '\'') +X value = 0; +X else value = c; +X /* +X * We warn on multi-byte constants and try to hack +X * (big|little)endian machines. +X */ +X#if BIG_ENDIAN +X count = 0; +X#endif +X while ((c = get()) != '\'' && c != EOF_CHAR && c != '\n') { +X if (!skip) +X ciwarn("multi-byte constant '%c' isn't portable", c); +X#if BIG_ENDIAN +X count += BITS_CHAR; +X value += (c << count); +X#else +X value <<= BITS_CHAR; +X value += c; +X#endif +X } +X instring = FALSE; +X return (value); +X} +X +XFILE_LOCAL int * +Xevaleval(valp, op, skip) +Xregister int *valp; +Xint op; +Xint skip; /* TRUE if short-circuit evaluation */ +X/* +X * Apply the argument operator to the data on the value stack. +X * One or two values are popped from the value stack and the result +X * is pushed onto the value stack. +X * +X * OP_COL is a special case. +X * +X * evaleval() returns the new pointer to the top of the value stack. +X */ +X{ +X register int v1, v2; +X +X if (isbinary(op)) +X v2 = *--valp; +X v1 = *--valp; +X#ifdef DEBUG_EVAL +X printf("%s op %s", (isbinary(op)) ? "binary" : "unary", +X opname[op]); +X if (isbinary(op)) +X printf(", v2 = %d.", v2); +X printf(", v1 = %d.\n", v1); +X#endif +X switch (op) { +X case OP_EOE: +X break; +X +X case OP_ADD: +X v1 += v2; +X break; +X +X case OP_SUB: +X v1 -= v2; +X break; +X +X case OP_MUL: +X v1 *= v2; +X break; +X +X case OP_DIV: +X case OP_MOD: +X if (v2 == 0) { +X if (!skip) { +X cwarn("%s by zero in #if, zero result assumed", +X (op == OP_DIV) ? "divide" : "mod"); +X } +X v1 = 0; +X } +X else if (op == OP_DIV) +X v1 /= v2; +X else +X v1 %= v2; +X break; +X +X case OP_ASL: +X v1 <<= v2; +X break; +X +X case OP_ASR: +X v1 >>= v2; +X break; +X +X case OP_AND: +X v1 &= v2; +X break; +X +X case OP_OR: +X v1 |= v2; +X break; +X +X case OP_XOR: +X v1 ^= v2; +X break; +X +X case OP_EQ: +X v1 = (v1 == v2); +X break; +X +X case OP_NE: +X v1 = (v1 != v2); +X break; +X +X case OP_LT: +X v1 = (v1 < v2); +X break; +X +X case OP_LE: +X v1 = (v1 <= v2); +X break; +X +X case OP_GE: +X v1 = (v1 >= v2); +X break; +X +X case OP_GT: +X v1 = (v1 > v2); +X break; +X +X case OP_ANA: +X v1 = (v1 && v2); +X break; +X +X case OP_ORO: +X v1 = (v1 || v2); +X break; +X +X case OP_COL: +X /* +X * v1 has the "true" value, v2 the "false" value. +X * The top of the value stack has the test. +X */ +X v1 = (*--valp) ? v1 : v2; +X break; +X +X case OP_NEG: +X v1 = (-v1); +X break; +X +X case OP_PLU: +X break; +X +X case OP_COM: +X v1 = ~v1; +X break; +X +X case OP_NOT: +X v1 = !v1; +X break; +X +X default: +X cierror("#if bug, operand = %d.", op); +X v1 = 0; +X } +X *valp++ = v1; +X return (valp); +X} +X +X#ifdef DEBUG_EVAL +Xdumpstack(opstack, opp, value, valp) +XOPTAB opstack[NEXP]; /* Operand stack */ +Xregister OPTAB *opp; /* Operator stack */ +Xint value[NEXP]; /* Value stack */ +Xregister int *valp; /* -> value vector */ +X{ +X printf("index op prec skip name -- op stack at %s", infile->bptr); +X while (opp > opstack) { +X printf(" [%2d] %2d %03o %d %s\n", opp - opstack, +X opp->op, opp->prec, opp->skip, opname[opp->op]); +X opp--; +X } +X while (--valp >= value) { +X printf("value[%d] = %d\n", (valp - value), *valp); +X } +X} +X#endif +X +END-of-cpp5.c +echo x - cpp6.c +sed 's/^X//' >cpp6.c << 'END-of-cpp6.c' +X/* +X * C P P 6 . C +X * S u p p o r t R o u t i n e s +X * +X * Edit History +X * 25-May-84 MM Added 8-bit support to type table. +X * 30-May-84 ARF sharp() should output filename in quotes +X * 02-Aug-84 MM Newline and #line hacking. sharp() now in cpp1.c +X * 31-Aug-84 MM USENET net.sources release +X * 11-Sep-84 ado/MM Keepcomments, also line number pathological +X * 12-Sep-84 ado/MM bug if comment changes to space and we unget later. +X * 03-Oct-84 gkr/MM Fixed scannumber bug for '.e' (as in struct.element). +X * 04-Oct-84 MM Added ungetstring() for token concatenation +X * 08-Oct-84 MM Yet another attack on number scanning +X * 31-Oct-84 ado Parameterized $ in identifiers +X * 2-Nov-84 MM Token concatenation is messier than I thought +X * 6-Dec-84 MM \ is everywhere invisible. +X */ +X +X#include +X#include +X#include "cppdef.h" +X#include "cpp.h" +X +X/* +X * skipnl() skips over input text to the end of the line. +X * skipws() skips over "whitespace" (spaces or tabs), but +X * not skip over the end of the line. It skips over +X * TOK_SEP, however (though that shouldn't happen). +X * scanid() reads the next token (C identifier) into token[]. +X * The caller has already read the first character of +X * the identifier. Unlike macroid(), the token is +X * never expanded. +X * macroid() reads the next token (C identifier) into token[]. +X * If it is a #defined macro, it is expanded, and +X * macroid() returns TRUE, otherwise, FALSE. +X * catenate() Does the dirty work of token concatenation, TRUE if it did. +X * scanstring() Reads a string from the input stream, calling +X * a user-supplied function for each character. +X * This function may be output() to write the +X * string to the output file, or save() to save +X * the string in the work buffer. +X * scannumber() Reads a C numeric constant from the input stream, +X * calling the user-supplied function for each +X * character. (output() or save() as noted above.) +X * save() Save one character in the work[] buffer. +X * savestring() Saves a string in malloc() memory. +X * getfile() Initialize a new FILEINFO structure, called when +X * #include opens a new file, or a macro is to be +X * expanded. +X * getmem() Get a specified number of bytes from malloc memory. +X * output() Write one character to stdout (calling putchar) -- +X * implemented as a function so its address may be +X * passed to scanstring() and scannumber(). +X * lookid() Scans the next token (identifier) from the input +X * stream. Looks for it in the #defined symbol table. +X * Returns a pointer to the definition, if found, or NULL +X * if not present. The identifier is stored in token[]. +X * defnedel() Define enter/delete subroutine. Updates the +X * symbol table. +X * get() Read the next byte from the current input stream, +X * handling end of (macro/file) input and embedded +X * comments appropriately. Note that the global +X * instring is -- essentially -- a parameter to get(). +X * cget() Like get(), but skip over TOK_SEP. +X * unget() Push last gotten character back on the input stream. +X * cerror(), cwarn(), cfatal(), cierror(), ciwarn() +X * These routines format an print messages to the user. +X * cerror & cwarn take a format and a single string argument. +X * cierror & ciwarn take a format and a single int (char) argument. +X * cfatal takes a format and a single string argument. +X */ +X +X/* +X * This table must be rewritten for a non-Ascii machine. +X * +X * Note that several "non-visible" characters have special meaning: +X * Hex 1D DEF_MAGIC -- a flag to prevent #define recursion. +X * Hex 1E TOK_SEP -- a delimiter for token concatenation +X * Hex 1F COM_SEP -- a zero-width whitespace for comment concatenation +X */ +X#if TOK_SEP != 0x1E || COM_SEP != 0x1F || DEF_MAGIC != 0x1D +X << error type table isn't correct >> +X#endif +X +X#if OK_DOLLAR +X#define DOL LET +X#else +X#define DOL 000 +X#endif +X +Xchar type[256] = { /* Character type codes Hex */ +X END, 000, 000, 000, 000, 000, 000, 000, /* 00 */ +X 000, SPA, 000, 000, 000, 000, 000, 000, /* 08 */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 10 */ +X 000, 000, 000, 000, 000, LET, 000, SPA, /* 18 */ +X SPA,OP_NOT, QUO, 000, DOL,OP_MOD,OP_AND, QUO, /* 20 !"#$%&' */ +XOP_LPA,OP_RPA,OP_MUL,OP_ADD, 000,OP_SUB, DOT,OP_DIV, /* 28 ()*+,-./ */ +X DIG, DIG, DIG, DIG, DIG, DIG, DIG, DIG, /* 30 01234567 */ +X DIG, DIG,OP_COL, 000, OP_LT, OP_EQ, OP_GT,OP_QUE, /* 38 89:;<=>? */ +X 000, LET, LET, LET, LET, LET, LET, LET, /* 40 @ABCDEFG */ +X LET, LET, LET, LET, LET, LET, LET, LET, /* 48 HIJKLMNO */ +X LET, LET, LET, LET, LET, LET, LET, LET, /* 50 PQRSTUVW */ +X LET, LET, LET, 000, BSH, 000,OP_XOR, LET, /* 58 XYZ[\]^_ */ +X 000, LET, LET, LET, LET, LET, LET, LET, /* 60 `abcdefg */ +X LET, LET, LET, LET, LET, LET, LET, LET, /* 68 hijklmno */ +X LET, LET, LET, LET, LET, LET, LET, LET, /* 70 pqrstuvw */ +X LET, LET, LET, 000, OP_OR, 000,OP_NOT, 000, /* 78 xyz{|}~ */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X 000, 000, 000, 000, 000, 000, 000, 000, /* 80 .. FF */ +X}; +X +Xskipnl() +X/* +X * Skip to the end of the current input line. +X */ +X{ +X register int c; +X +X do { /* Skip to newline */ +X c = get(); +X } while (c != '\n' && c != EOF_CHAR); +X} +X +Xint +Xskipws() +X/* +X * Skip over whitespace +X */ +X{ +X register int c; +X +X do { /* Skip whitespace */ +X c = get(); +X#if COMMENT_INVISIBLE +X } while (type[c] == SPA || c == COM_SEP); +X#else +X } while (type[c] == SPA); +X#endif +X return (c); +X} +X +Xscanid(c) +Xregister int c; /* First char of id */ +X/* +X * Get the next token (an id) into the token buffer. +X * Note: this code is duplicated in lookid(). +X * Change one, change both. +X */ +X{ +X register char *bp; +X +X if (c == DEF_MAGIC) /* Eat the magic token */ +X c = get(); /* undefiner. */ +X bp = token; +X do { +X if (bp < &token[IDMAX]) /* token dim is IDMAX+1 */ +X *bp++ = c; +X c = get(); +X } while (type[c] == LET || type[c] == DIG); +X unget(); +X *bp = EOS; +X} +X +Xint +Xmacroid(c) +Xregister int c; +X/* +X * If c is a letter, scan the id. if it's #defined, expand it and scan +X * the next character and try again. +X * +X * Else, return the character. If type[c] is a LET, the token is in token. +X */ +X{ +X register DEFBUF *dp; +X +X if (infile != NULL && infile->fp != NULL) +X recursion = 0; +X while (type[c] == LET && (dp = lookid(c)) != NULL) { +X expand(dp); +X c = get(); +X } +X return (c); +X} +X +Xint +Xcatenate() +X/* +X * A token was just read (via macroid). +X * If the next character is TOK_SEP, concatenate the next token +X * return TRUE -- which should recall macroid after refreshing +X * macroid's argument. If it is not TOK_SEP, unget() the character +X * and return FALSE. +X */ +X{ +X register int c; +X register char *token1; +X +X#if OK_CONCAT +X if (get() != TOK_SEP) { /* Token concatenation */ +X unget(); +X return (FALSE); +X } +X else { +X token1 = savestring(token); /* Save first token */ +X c = macroid(get()); /* Scan next token */ +X switch(type[c]) { /* What was it? */ +X case LET: /* An identifier, ... */ +X if (strlen(token1) + strlen(token) >= NWORK) +X cfatal("work buffer overflow doing %s #", token1); +X sprintf(work, "%s%s", token1, token); +X break; +X +X case DIG: /* A digit string */ +X strcpy(work, token1); +X workp = work + strlen(work); +X do { +X save(c); +X } while ((c = get()) != TOK_SEP); +X /* +X * The trailing TOK_SEP is no longer needed. +X */ +X save(EOS); +X break; +X +X default: /* An error, ... */ +X if (isprint(c)) +X cierror("Strange character '%c' after #", c); +X else +X cierror("Strange character (%d.) after #", c); +X strcpy(work, token1); +X unget(); +X break; +X } +X /* +X * work has the concatenated token and token1 has +X * the first token (no longer needed). Unget the +X * new (concatenated) token after freeing token1. +X * Finally, setup to read the new token. +X */ +X free(token1); /* Free up memory */ +X ungetstring(work); /* Unget the new thing, */ +X return (TRUE); +X } +X#else +X return (FALSE); /* Not supported */ +X#endif +X} +X +Xint +Xscanstring(delim, outfun) +Xregister int delim; /* ' or " */ +Xint (*outfun)(); /* Output function */ +X/* +X * Scan off a string. Warning if terminated by newline or EOF. +X * outfun() outputs the character -- to a buffer if in a macro. +X * TRUE if ok, FALSE if error. +X */ +X{ +X register int c; +X +X instring = TRUE; /* Don't strip comments */ +X (*outfun)(delim); +X while ((c = get()) != delim +X && c != '\n' +X && c != EOF_CHAR) { +X (*outfun)(c); +X if (c == '\\') +X (*outfun)(get()); +X } +X instring = FALSE; +X if (c == delim) { +X (*outfun)(c); +X return (TRUE); +X } +X else { +X cerror("Unterminated string", NULLST); +X unget(); +X return (FALSE); +X } +X} +X +Xscannumber(c, outfun) +Xregister int c; /* First char of number */ +Xregister int (*outfun)(); /* Output/store func */ +X/* +X * Process a number. We know that c is from 0 to 9 or dot. +X * Algorithm from Dave Conroy's Decus C. +X */ +X{ +X register int radix; /* 8, 10, or 16 */ +X int expseen; /* 'e' seen in floater */ +X int signseen; /* '+' or '-' seen */ +X int octal89; /* For bad octal test */ +X int dotflag; /* TRUE if '.' was seen */ +X +X expseen = FALSE; /* No exponent seen yet */ +X signseen = TRUE; /* No +/- allowed yet */ +X octal89 = FALSE; /* No bad octal yet */ +X radix = 10; /* Assume decimal */ +X if ((dotflag = (c == '.')) != FALSE) { /* . something? */ +X (*outfun)('.'); /* Always out the dot */ +X if (type[(c = get())] != DIG) { /* If not a float numb, */ +X unget(); /* Rescan strange char */ +X return; /* All done for now */ +X } +X } /* End of float test */ +X else if (c == '0') { /* Octal or hex? */ +X (*outfun)(c); /* Stuff initial zero */ +X radix = 8; /* Assume it's octal */ +X c = get(); /* Look for an 'x' */ +X if (c == 'x' || c == 'X') { /* Did we get one? */ +X radix = 16; /* Remember new radix */ +X (*outfun)(c); /* Stuff the 'x' */ +X c = get(); /* Get next character */ +X } +X } +X for (;;) { /* Process curr. char. */ +X /* +X * Note that this algorithm accepts "012e4" and "03.4" +X * as legitimate floating-point numbers. +X */ +X if (radix != 16 && (c == 'e' || c == 'E')) { +X if (expseen) /* Already saw 'E'? */ +X break; /* Exit loop, bad nbr. */ +X expseen = TRUE; /* Set exponent seen */ +X signseen = FALSE; /* We can read '+' now */ +X radix = 10; /* Decimal exponent */ +X } +X else if (radix != 16 && c == '.') { +X if (dotflag) /* Saw dot already? */ +X break; /* Exit loop, two dots */ +X dotflag = TRUE; /* Remember the dot */ +X radix = 10; /* Decimal fraction */ +X } +X else if (c == '+' || c == '-') { /* 1.0e+10 */ +X if (signseen) /* Sign in wrong place? */ +X break; /* Exit loop, not nbr. */ +X /* signseen = TRUE; */ /* Remember we saw it */ +X } +X else { /* Check the digit */ +X switch (c) { +X case '8': case '9': /* Sometimes wrong */ +X octal89 = TRUE; /* Do check later */ +X case '0': case '1': case '2': case '3': +X case '4': case '5': case '6': case '7': +X break; /* Always ok */ +X +X case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': +X case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': +X if (radix == 16) /* Alpha's are ok only */ +X break; /* if reading hex. */ +X default: /* At number end */ +X goto done; /* Break from for loop */ +X } /* End of switch */ +X } /* End general case */ +X (*outfun)(c); /* Accept the character */ +X signseen = TRUE; /* Don't read sign now */ +X c = get(); /* Read another char */ +X } /* End of scan loop */ +X /* +X * When we break out of the scan loop, c contains the first +X * character (maybe) not in the number. If the number is an +X * integer, allow a trailing 'L' for long and/or a trailing 'U' +X * for unsigned. If not those, push the trailing character back +X * on the input stream. Floating point numbers accept a trailing +X * 'L' for "long double". +X */ +Xdone: if (dotflag || expseen) { /* Floating point? */ +X if (c == 'l' || c == 'L') { +X (*outfun)(c); +X c = get(); /* Ungotten later */ +X } +X } +X else { /* Else it's an integer */ +X /* +X * We know that dotflag and expseen are both zero, now: +X * dotflag signals "saw 'L'", and +X * expseen signals "saw 'U'". +X */ +X for (;;) { +X switch (c) { +X case 'l': +X case 'L': +X if (dotflag) +X goto nomore; +X dotflag = TRUE; +X break; +X +X case 'u': +X case 'U': +X if (expseen) +X goto nomore; +X expseen = TRUE; +X break; +X +X default: +X goto nomore; +X } +X (*outfun)(c); /* Got 'L' or 'U'. */ +X c = get(); /* Look at next, too. */ +X } +X } +Xnomore: unget(); /* Not part of a number */ +X if (octal89 && radix == 8) +X cwarn("Illegal digit in octal number", NULLST); +X} +X +Xsave(c) +Xregister int c; +X{ +X if (workp >= &work[NWORK]) +X cfatal("Work buffer overflow", NULLST); +X else *workp++ = c; +X} +X +Xchar * +Xsavestring(text) +Xchar *text; +X/* +X * Store a string into free memory. +X */ +X{ +X register char *result; +X +X result = getmem(strlen(text) + 1); +X strcpy(result, text); +X return (result); +X} +X +XFILEINFO * +Xgetfile(bufsize, name) +Xint bufsize; /* Line or define buffer size */ +Xchar *name; /* File or macro name string */ +X/* +X * Common FILEINFO buffer initialization for a new file or macro. +X */ +X{ +X register FILEINFO *file; +X register int size; +X +X size = strlen(name); /* File/macro name */ +X file = (FILEINFO *) getmem(sizeof (FILEINFO) + bufsize + size); +X file->parent = infile; /* Chain files together */ +X file->fp = NULL; /* No file yet */ +X file->filename = savestring(name); /* Save file/macro name */ +X file->progname = NULL; /* No #line seen yet */ +X file->unrecur = 0; /* No macro fixup */ +X file->bptr = file->buffer; /* Initialize line ptr */ +X file->buffer[0] = EOS; /* Force first read */ +X file->line = 0; /* (Not used just yet) */ +X if (infile != NULL) /* If #include file */ +X infile->line = line; /* Save current line */ +X infile = file; /* New current file */ +X line = 1; /* Note first line */ +X return (file); /* All done. */ +X} +X +Xchar * +Xgetmem(size) +Xint size; +X/* +X * Get a block of free memory. +X */ +X{ +X register char *result; +X extern char *malloc(); +X +X if ((result = malloc((unsigned) size)) == NULL) +X cfatal("Out of memory", NULLST); +X return (result); +X} +X +X/* +X * C P P S y m b o l T a b l e s +X */ +X +X/* +X * SBSIZE defines the number of hash-table slots for the symbol table. +X * It must be a power of 2. +X */ +X#ifndef SBSIZE +X#define SBSIZE 64 +X#endif +X#define SBMASK (SBSIZE - 1) +X#if (SBSIZE ^ SBMASK) != ((SBSIZE * 2) - 1) +X << error, SBSIZE must be a power of 2 >> +X#endif +X +Xstatic DEFBUF *symtab[SBSIZE]; /* Symbol table queue headers */ +X +XDEFBUF * +Xlookid(c) +Xint c; /* First character of token */ +X/* +X * Look for the next token in the symbol table. Returns token in "token". +X * If found, returns the table pointer; Else returns NULL. +X */ +X{ +X register int nhash; +X register DEFBUF *dp; +X register char *np; +X int temp; +X int isrecurse; /* For #define foo foo */ +X +X np = token; +X nhash = 0; +X if ((isrecurse = (c == DEF_MAGIC))) /* If recursive macro */ +X c = get(); /* hack, skip DEF_MAGIC */ +X do { +X if (np < &token[IDMAX]) { /* token dim is IDMAX+1 */ +X *np++ = c; /* Store token byte */ +X nhash += c; /* Update hash value */ +X } +X c = get(); /* And get another byte */ +X } while (type[c] == LET || type[c] == DIG); +X unget(); /* Rescan terminator */ +X *np = EOS; /* Terminate token */ +X if (isrecurse) /* Recursive definition */ +X return (NULL); /* undefined just now */ +X nhash += (np - token); /* Fix hash value */ +X dp = symtab[nhash & SBMASK]; /* Starting bucket */ +X while (dp != (DEFBUF *) NULL) { /* Search symbol table */ +X if (dp->hash == nhash /* Fast precheck */ +X && (temp = strcmp(dp->name, token)) >= 0) +X break; +X dp = dp->link; /* Nope, try next one */ +X } +X return ((temp == 0) ? dp : NULL); +X} +X +XDEFBUF * +Xdefendel(name, delete) +Xchar *name; +Xint delete; /* TRUE to delete a symbol */ +X/* +X * Enter this name in the lookup table (delete = FALSE) +X * or delete this name (delete = TRUE). +X * Returns a pointer to the define block (delete = FALSE) +X * Returns NULL if the symbol wasn't defined (delete = TRUE). +X */ +X{ +X register DEFBUF *dp; +X register DEFBUF **prevp; +X register char *np; +X int nhash; +X int temp; +X int size; +X +X for (nhash = 0, np = name; *np != EOS;) +X nhash += *np++; +X size = (np - name); +X nhash += size; +X prevp = &symtab[nhash & SBMASK]; +X while ((dp = *prevp) != (DEFBUF *) NULL) { +X if (dp->hash == nhash +X && (temp = strcmp(dp->name, name)) >= 0) { +X if (temp > 0) +X dp = NULL; /* Not found */ +X else { +X *prevp = dp->link; /* Found, unlink and */ +X if (dp->repl != NULL) /* Free the replacement */ +X free(dp->repl); /* if any, and then */ +X free((char *) dp); /* Free the symbol */ +X } +X break; +X } +X prevp = &dp->link; +X } +X if (!delete) { +X dp = (DEFBUF *) getmem(sizeof (DEFBUF) + size); +X dp->link = *prevp; +X *prevp = dp; +X dp->hash = nhash; +X dp->repl = NULL; +X dp->nargs = 0; +X strcpy(dp->name, name); +X } +X return (dp); +X} +X +X#if DEBUG +X +Xdumpdef(why) +Xchar *why; +X{ +X register DEFBUF *dp; +X register DEFBUF **syp; +X +X printf("CPP symbol table dump %s\n", why); +X for (syp = symtab; syp < &symtab[SBSIZE]; syp++) { +X if ((dp = *syp) != (DEFBUF *) NULL) { +X printf("symtab[%d]\n", (syp - symtab)); +X do { +X dumpadef((char *) NULL, dp); +X } while ((dp = dp->link) != (DEFBUF *) NULL); +X } +X } +X} +X +Xdumpadef(why, dp) +Xchar *why; /* Notation */ +Xregister DEFBUF *dp; +X{ +X register char *cp; +X register int c; +X +X printf(" \"%s\" [%d]", dp->name, dp->nargs); +X if (why != NULL) +X printf(" (%s)", why); +X if (dp->repl != NULL) { +X printf(" => "); +X for (cp = dp->repl; (c = *cp++ & 0xFF) != EOS;) { +X if (c >= MAC_PARM && c <= (MAC_PARM + PAR_MAC)) +X printf("<%d>", c - MAC_PARM); +X else if (isprint(c) || c == '\n' || c == '\t') +X putchar(c); +X else if (c < ' ') +X printf("<^%c>", c + '@'); +X else +X printf("<\\0%o>", c); +X } +X } +X else { +X printf(", no replacement."); +X } +X putchar('\n'); +X} +X#endif +X +X/* +X * G E T +X */ +X +Xint +Xget() +X/* +X * Return the next character from a macro or the current file. +X * Handle end of file from #include files. +X */ +X{ +X register int c; +X register FILEINFO *file; +X register int popped; /* Recursion fixup */ +X +X popped = 0; +Xget_from_file: +X if ((file = infile) == NULL) +X return (EOF_CHAR); +Xnewline: +X#if 0 +X printf("get(%s), recursion %d, line %d, bptr = %d, buffer \"%s\"\n", +X file->filename, recursion, line, +X file->bptr - file->buffer, file->buffer); +X#endif +X /* +X * Read a character from the current input line or macro. +X * At EOS, either finish the current macro (freeing temp. +X * storage) or read another line from the current input file. +X * At EOF, exit the current file (#include) or, at EOF from +X * the cpp input file, return EOF_CHAR to finish processing. +X */ +X if ((c = *file->bptr++ & 0xFF) == EOS) { +X /* +X * Nothing in current line or macro. Get next line (if +X * input from a file), or do end of file/macro processing. +X * In the latter case, jump back to restart from the top. +X */ +X if (file->fp == NULL) { /* NULL if macro */ +X popped++; +X recursion -= file->unrecur; +X if (recursion < 0) +X recursion = 0; +X infile = file->parent; /* Unwind file chain */ +X } +X else { /* Else get from a file */ +X if ((file->bptr = fgets(file->buffer, NBUFF, file->fp)) +X != NULL) { +X#if DEBUG +X if (debug > 1) { /* Dump it to stdout */ +X printf("\n#line %d (%s), %s", +X line, file->filename, file->buffer); +X } +X#endif +X goto newline; /* process the line */ +X } +X else { +X fclose(file->fp); /* Close finished file */ +X if ((infile = file->parent) != NULL) { +X /* +X * There is an "ungotten" newline in the current +X * infile buffer (set there by doinclude() in +X * cpp1.c). Thus, we know that the mainline code +X * is skipping over blank lines and will do a +X * #line at its convenience. +X */ +X wrongline = TRUE; /* Need a #line now */ +X } +X } +X } +X /* +X * Free up space used by the (finished) file or macro and +X * restart input from the parent file/macro, if any. +X */ +X free(file->filename); /* Free name and */ +X if (file->progname != NULL) /* if a #line was seen, */ +X free(file->progname); /* free it, too. */ +X free((char *) file); /* Free file space */ +X if (infile == NULL) /* If at end of file */ +X return (EOF_CHAR); /* Return end of file */ +X line = infile->line; /* Reset line number */ +X goto get_from_file; /* Get from the top. */ +X } +X /* +X * Common processing for the new character. +X */ +X if (c == DEF_MAGIC && file->fp != NULL) /* Don't allow delete */ +X goto newline; /* from a file */ +X if (file->parent != NULL) { /* Macro or #include */ +X if (popped != 0) +X file->parent->unrecur += popped; +X else { +X recursion -= file->parent->unrecur; +X if (recursion < 0) +X recursion = 0; +X file->parent->unrecur = 0; +X } +X } +X if (c == '\n') /* Maintain current */ +X ++line; /* line counter */ +X if (instring) /* Strings just return */ +X return (c); /* the character. */ +X else if (c == '/') { /* Comment? */ +X instring = TRUE; /* So get() won't loop */ +X if ((c = get()) != '*') { /* Next byte '*'? */ +X instring = FALSE; /* Nope, no comment */ +X unget(); /* Push the char. back */ +X return ('/'); /* Return the slash */ +X } +X if (keepcomments) { /* If writing comments */ +X putchar('/'); /* Write out the */ +X putchar('*'); /* initializer */ +X } +X for (;;) { /* Eat a comment */ +X c = get(); +Xtest: if (keepcomments && c != EOF_CHAR) +X cput(c); +X switch (c) { +X case EOF_CHAR: +X cerror("EOF in comment", NULLST); +X return (EOF_CHAR); +X +X case '/': +X if ((c = get()) != '*') /* Don't let comments */ +X goto test; /* Nest. */ +X#ifdef VERBOSE +X cwarn("Nested comments", NULLST); +X#endif +X /* Fall into * stuff */ +X case '*': +X if ((c = get()) != '/') /* If comment doesn't */ +X goto test; /* end, look at next */ +X instring = FALSE; /* End of comment, */ +X if (keepcomments) { /* Put out the comment */ +X cput(c); /* terminator, too */ +X } +X /* +X * A comment is syntactically "whitespace" -- +X * however, there are certain strange sequences +X * such as +X * #define foo(x) (something) +X * foo|* comment *|(123) +X * these are '/' ^ ^ +X * where just returning space (or COM_SEP) will cause +X * problems. This can be "fixed" by overwriting the +X * '/' in the input line buffer with ' ' (or COM_SEP) +X * but that may mess up an error message. +X * So, we peek ahead -- if the next character is +X * "whitespace" we just get another character, if not, +X * we modify the buffer. All in the name of purity. +X */ +X if (*file->bptr == '\n' +X || type[*file->bptr & 0xFF] == SPA) +X goto newline; +X#if COMMENT_INVISIBLE +X /* +X * Return magic (old-fashioned) syntactic space. +X */ +X return ((file->bptr[-1] = COM_SEP)); +X#else +X return ((file->bptr[-1] = ' ')); +X#endif +X +X case '\n': /* we'll need a #line */ +X if (!keepcomments) +X wrongline = TRUE; /* later... */ +X default: /* Anything else is */ +X break; /* Just a character */ +X } /* End switch */ +X } /* End comment loop */ +X } /* End if in comment */ +X else if (!inmacro && c == '\\') { /* If backslash, peek */ +X if ((c = get()) == '\n') { /* for a . If so, */ +X wrongline = TRUE; +X goto newline; +X } +X else { /* Backslash anything */ +X unget(); /* Get it later */ +X return ('\\'); /* Return the backslash */ +X } +X } +X else if (c == '\f' || c == VT) /* Form Feed, Vertical */ +X c = ' '; /* Tab are whitespace */ +X return (c); /* Just return the char */ +X} +X +Xunget() +X/* +X * Backup the pointer to reread the last character. Fatal error +X * (code bug) if we backup too far. unget() may be called, +X * without problems, at end of file. Only one character may +X * be ungotten. If you need to unget more, call ungetstring(). +X */ +X{ +X register FILEINFO *file; +X +X if ((file = infile) == NULL) +X return; /* Unget after EOF */ +X if (--file->bptr < file->buffer) +X cfatal("Too much pushback", NULLST); +X if (*file->bptr == '\n') /* Ungetting a newline? */ +X --line; /* Unget the line number, too */ +X} +X +Xungetstring(text) +Xchar *text; +X/* +X * Push a string back on the input stream. This is done by treating +X * the text as if it were a macro. +X */ +X{ +X register FILEINFO *file; +X extern FILEINFO *getfile(); +X +X file = getfile(strlen(text) + 1, ""); +X strcpy(file->buffer, text); +X} +X +Xint +Xcget() +X/* +X * Get one character, absorb "funny space" after comments or +X * token concatenation +X */ +X{ +X register int c; +X +X do { +X c = get(); +X#if COMMENT_INVISIBLE +X } while (c == TOK_SEP || c == COM_SEP); +X#else +X } while (c == TOK_SEP); +X#endif +X return (c); +X} +X +X/* +X * Error messages and other hacks. The first byte of severity +X * is 'S' for string arguments and 'I' for int arguments. This +X * is needed for portability with machines that have int's that +X * are shorter than char *'s. +X */ +X +Xstatic +Xdomsg(severity, format, arg) +Xchar *severity; /* "Error", "Warning", "Fatal" */ +Xchar *format; /* Format for the error message */ +Xchar *arg; /* Something for the message */ +X/* +X * Print filenames, macro names, and line numbers for error messages. +X */ +X{ +X register char *tp; +X register FILEINFO *file; +X +X fprintf(stderr, "%sline %d, %s: ", MSG_PREFIX, line, &severity[1]); +X if (*severity == 'S') +X fprintf(stderr, format, arg); +X else +X fprintf(stderr, format, (int) arg); +X putc('\n', stderr); +X if ((file = infile) == NULL) +X return; /* At end of file */ +X if (file->fp != NULL) { +X tp = file->buffer; /* Print current file */ +X fprintf(stderr, "%s", tp); /* name, making sure */ +X if (tp[strlen(tp) - 1] != '\n') /* there's a newline */ +X putc('\n', stderr); +X } +X while ((file = file->parent) != NULL) { /* Print #includes, too */ +X if (file->fp == NULL) +X fprintf(stderr, "from macro %s\n", file->filename); +X else { +X tp = file->buffer; +X fprintf(stderr, "from file %s, line %d:\n%s", +X (file->progname != NULL) +X ? file->progname : file->filename, +X file->line, tp); +X if (tp[strlen(tp) - 1] != '\n') +X putc('\n', stderr); +X } +X } +X} +X +Xcerror(format, sarg) +Xchar *format; +Xchar *sarg; /* Single string argument */ +X/* +X * Print a normal error message, string argument. +X */ +X{ +X domsg("SError", format, sarg); +X errors++; +X} +X +Xcierror(format, narg) +Xchar *format; +Xint narg; /* Single numeric argument */ +X/* +X * Print a normal error message, numeric argument. +X */ +X{ +X domsg("IError", format, (char *) narg); +X errors++; +X} +X +Xcfatal(format, sarg) +Xchar *format; +Xchar *sarg; /* Single string argument */ +X/* +X * A real disaster +X */ +X{ +X domsg("SFatal error", format, sarg); +X exit(IO_ERROR); +X} +X +Xcwarn(format, sarg) +Xchar *format; +Xchar *sarg; /* Single string argument */ +X/* +X * A non-fatal error, string argument. +X */ +X{ +X domsg("SWarning", format, sarg); +X} +X +Xciwarn(format, narg) +Xchar *format; +Xint narg; /* Single numeric argument */ +X/* +X * A non-fatal error, numeric argument. +X */ +X{ +X domsg("IWarning", format, (char *) narg); +X} +X +X +X +END-of-cpp6.c +exit diff --git a/sys/unix/depend.awk b/sys/unix/depend.awk new file mode 100644 index 0000000..26a9489 --- /dev/null +++ b/sys/unix/depend.awk @@ -0,0 +1,151 @@ +# depend.awk -- awk script used to construct makefile dependencies +# for nethack's source files (`make depend' support for Makefile.src). +# +# usage: +# cd src ; nawk -f depend.awk ../include/*.h list-of-.c/.cpp-files +# +# This awk program scans each file in sequence, looking for lines beginning +# with `#include "' and recording the name inside the quotes. For .h files, +# that's all it does. For each .c file, it writes out a make rule for the +# corresponding .o file; dependencies in nested header files are propagated +# to the .o target. +# +# config.h and hack.h get special handling because of their heavy use; +# timestamps for them allow make to avoid rechecking dates on +# subsidiary headers for every source file; +# extern.h gets special handling to avoid excessive recompilation +# during development; +# patchlev.h gets special handling because it only exists on systems +# which consider filename patchlevel.h to be too long; +# interp.c gets special handling because it usually doesn't exist; it's +# assumed to be the last #include in the file where it occurs. +# win32api.h gets special handling because it only exists for some ports; +# it's assumed to be the last #include in the file where it occurs +# +BEGIN { FS = "\"" #for `#include "X"', $2 is X + special[++sp_cnt] = "../include/config.h" + special[++sp_cnt] = "../include/hack.h" + alt_deps["../include/extern.h"] = "" + alt_deps["../include/patchlev.h"] = "" + alt_deps["interp.c"] = " #interp.c" #comment it out + alt_deps["../include/win32api.h"] = " #../include/win32api.h" + } +FNR == 1 { output_dep() #finish previous file + file = FILENAME #setup for current file + } +/^\#[ \t]*include[ \t]+\"/ { #find `#include "X"' + incl = $2; + #[3.4.0: gnomehack headers currently aren't in include] + if (incl ~ /\.h$/) { + if (incl ~ /^gn/) # gnomehack special case + incl = "../win/gnome/" incl + else + incl = "../include/" incl + } + deps[file] = deps[file] " " incl + } +END { output_dep() } #finish the last file + + +# +# `file' has been fully scanned, so process it now; for .h files, +# don't do anything (we've just been collecting their dependencies); +# for .c files, output the `make' rule for corresponding .o file +# +function output_dep( targ) +{ + if (file ~ /\.cp*$/) { + #prior to very first .c|.cpp file, handle some special header file cases + if (!c_count++) + output_specials() + #construct object filename from source filename + targ = file; sub("^.+/", "", targ); sub("\\.cp*$", ".o", targ) + #format and write the collected dependencies + format_dep(targ, file) + } +} + +# +# handle some targets (config.h, hack.h) via special timestamping rules +# +function output_specials( i, sp, alt_sp) +{ + for (i = 1; i <= sp_cnt; i++) { + sp = special[i] + #change "../include/foo.h" first to "foo.h", then ultimately to "$(FOO_H)" + alt_sp = sp; sub("^.+/", "", alt_sp) + print "#", alt_sp, "timestamp" #output a `make' comment + #- sub("\\.", "_", alt_sp); alt_sp = "$(" toupper(alt_sp) ")" + #+ Some nawks don't have toupper(), so hardwire these instead. + sub("config.h", "$(CONFIG_H)", alt_sp); sub("hack.h", "$(HACK_H)", alt_sp); + format_dep(alt_sp, sp) #output the target + print "\ttouch " alt_sp #output a build command + alt_deps[sp] = alt_sp #alternate dependency for depend() + } + print "#" +} + +# +# write a target and its dependency list in pretty-printed format; +# if target's primary source file has a path prefix, also write build command +# +function format_dep(target, source, n, i, list) +{ + split("", done) #``for (x in done) delete done[x]'' + printf("%s:", target); col = length(target) + 1 + #- printf("\t"); col += 8 - (col % 8); + #- if (col == 8) { printf("\t"); col += 8 } + source = depend("", source, 0) + n = split(source, list, " +") + for (i = 2; i <= n; i++) { #(leading whitespace yields empty 1st element) + if (col + length(list[i]) >= (i < n ? 78 : 80)) { + printf(" \\\n\t\t"); col = 16 #make a backslash+newline split + } else { + printf(" "); col++; + } + printf("%s", list[i]); col += length(list[i]) + } + printf("\n") #terminate + #write build command if first source entry has non-include path prefix + source = list[2] + if (source ~ /\// && substr(source, 1, 11) != "../include/") { + if (source ~ /\.cpp$/ ) + print "\t$(CXX) $(CXXFLAGS) -c " source + else if (source ~ /\/gnome\//) # "../win/gnome/foo.c" + print "\t$(CC) $(CFLAGS) $(GNOMEINC) -c " source + else + print "\t$(CC) $(CFLAGS) -c " source + } +} + +# +# recursively add the dependencies for file `name' to string `inout' +# (unless `skip', in which case we're only marking files as already done) +# +function depend(inout, name, skip, n, i, list) +{ + if (!done[name]++) { + if (name in alt_deps) { #some names have non-conventional dependencies + if (!skip) inout = inout " " alt_deps[name] + skip = 1 + } else { #ordinary name + if (!skip) inout = inout " " name + } + if (name in deps) { + #- n = split(deps[name], list, " +") + #- for (i = 2; i <= n; i++) #(leading whitespace yields empty 1st element) + #- inout = depend(inout, list[i], skip) + #+ At least one implementation of nawk handles the local array `list' wrong, + #+ so the clumsier substitute code below is used as a workaround. + list = deps[name]; sub("^ +", "", list) + while (list) { + match((list " "), " +"); i = RSTART; n = RLENGTH + inout = depend(inout, substr(list, 1, i-1), skip) + list = substr(list, i+n) + } + } + } + return inout +} + +#depend.awk# diff --git a/sys/unix/nethack.sh b/sys/unix/nethack.sh new file mode 100644 index 0000000..600e1da --- /dev/null +++ b/sys/unix/nethack.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# SCCS Id: @(#)nethack.sh 3.4 1990/02/26 + +HACKDIR=/usr/games/lib/nethackdir +export HACKDIR +HACK=$HACKDIR/nethack +MAXNROFPLAYERS=4 + +# Since Nethack.ad is installed in HACKDIR, add it to XUSERFILESEARCHPATH +case "x$XUSERFILESEARCHPATH" in +x) XUSERFILESEARCHPATH="$HACKDIR/%N.ad" + ;; +*) XUSERFILESEARCHPATH="$XUSERFILESEARCHPATH:$HACKDIR/%N.ad" + ;; +esac +export XUSERFILESEARCHPATH + +# see if we can find the full path name of PAGER, so help files work properly +# assume that if someone sets up a special variable (HACKPAGER) for NetHack, +# it will already be in a form acceptable to NetHack +# ideas from brian@radio.astro.utoronto.ca +if test \( "xxx$PAGER" != xxx \) -a \( "xxx$HACKPAGER" = xxx \) +then + + HACKPAGER=$PAGER + +# use only the first word of the pager variable +# this prevents problems when looking for file names with trailing +# options, but also makes the options unavailable for later use from +# NetHack + for i in $HACKPAGER + do + HACKPAGER=$i + break + done + + if test ! -f $HACKPAGER + then + IFS=: + for i in $PATH + do + if test -f $i/$HACKPAGER + then + HACKPAGER=$i/$HACKPAGER + export HACKPAGER + break + fi + done + IFS=' ' + fi + if test ! -f $HACKPAGER + then + echo Cannot find $PAGER -- unsetting PAGER. + unset HACKPAGER + unset PAGER + fi +fi + + +cd $HACKDIR +case $1 in + -s*) + exec $HACK "$@" + ;; + *) + exec $HACK "$@" $MAXNROFPLAYERS + ;; +esac diff --git a/sys/unix/setup.sh b/sys/unix/setup.sh new file mode 100644 index 0000000..67579d5 --- /dev/null +++ b/sys/unix/setup.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Copy files to their correct locations. +# +# If arguments are given, try symbolic link first. This is not the default +# so that most people will have the distribution versions stay around so +# subsequent patches can be applied. People who pay enough attention to +# know there's a non-default behavior are assumed to pay enough attention +# to keep distribution versions if they modify things. + +# Were we started from the top level? Cope. +if [ -f sys/unix/Makefile.top ]; then cd sys/unix; fi + +if [ $# -gt 0 ] ; then +# First, try to make a symbolic link. +# + ln -s Makefile.top Makefile >/dev/null 2>&1 + if [ $? -eq 0 ] ; then + + echo "Lucky you! Symbolic links." + rm -f Makefile + + umask 0 + ln -s sys/unix/Makefile.top ../../Makefile + ln -s ../sys/unix/Makefile.dat ../../dat/Makefile + ln -s ../sys/unix/Makefile.doc ../../doc/Makefile + ln -s ../sys/unix/Makefile.src ../../src/Makefile + ln -s ../sys/unix/Makefile.utl ../../util/Makefile + exit 0 + fi +fi + +# +# Otherwise... + +echo "Copying Makefiles." + +cp Makefile.top ../../Makefile +cp Makefile.dat ../../dat/Makefile +cp Makefile.doc ../../doc/Makefile +cp Makefile.src ../../src/Makefile +cp Makefile.utl ../../util/Makefile diff --git a/sys/unix/snd86unx.shr b/sys/unix/snd86unx.shr new file mode 100644 index 0000000..bfbae3f --- /dev/null +++ b/sys/unix/snd86unx.shr @@ -0,0 +1,1064 @@ +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +# READ.ME +# install.bsd +# spkr.7 +# Makefile +# spkr.c +# spkr.h +# interp.c +# Files +# Install +# Master +# Name +# Node +# Remove +# Size +# System +# playtest +# +echo x - READ.ME +sed 's/^X//' >READ.ME << 'END-of-READ.ME' +X Console Speaker Driver Package (v1.1) +X +X by Eric S. Raymond (esr@snark.thyrsus.com) +X +XThis package gives 80386 machines running SVr3.2 or later the ability to play +Xtunes on the console speaker. It has been extended to 386BSD (and possibly +XBSDI) by Andrew A. Chernov, and to SCO UNIX 3.2.4 (and possibly other VPIX +Xsystems) by Andreas Arens. +X +XThe following files are contained in the kit: +X +XDocumentation and examples: +XREAD.ME -- this file +Xspeaker.7 -- man page for the driver +Xplaytest -- test script exercising familiar tunes +X +XInstallable driver kit parts, for SVr3.2 or later: +XFiles -- list of driver package file locations +XInstall -- installation script for driver kit +XMaster -- mdevice entry for speaker driver +XName -- name entry foe speaker driver +XNode -- /dev node specification file +XRemove -- Driver removal script +XSize -- installation size data +XSystem -- sdevice entry for speaker driver +X +XDriver source code, for SVr3.2 or later and 386BSD: +XMakefile -- Makefile for driver code +Xspkr.c -- the driver source +Xspeaker.h -- ioctl interface file +X +XCommon source code: +Xinterp.c -- play string interpretation code +X +XFor SVr3.2 or later, simply type `make' and wait. Then type ./Install +Xand follow its instructions. You will have to install the man pages by hand. +XBe aware that the speaker.7 man page uses tbl(1) constructs. +X +XFor 386BSD, follow the installation instructions in install.bsd. +X +XFor SCO UNIX 3.2.4, no new kernel drivers are needed, and you need only +Xcopy interp.c to your src directory and proceed with making NetHack, with +XVPIX_MUSIC set in unixconf.h. +X +XInteresting tunes mailed to the author will be periodically posted in batches +Xand added to the test script for future versions. +X +X Revision notes +X +X1.1 -- fixed minor bug in M[LSN] interpretation, added octave-tracking. +X Tweaked the playtest examples. +END-of-READ.ME +echo x - install.bsd +sed 's/^X//' >install.bsd << 'END-of-install.bsd' +XCopy spkr.c and interp.c to /sys/i386/isa +XCopy spkr.h to /sys/sys +X +X----------------------------------------------------------------------------- +X +XFile /sys/i386/conf/YOUR_MACHINE_NAME +Xadd following line: +X +Xpseudo-device speaker +X +X----------------------------------------------------------------------------- +X +XFile /sys/i386/conf/files.i386 +Xadd following line: +X +Xi386/isa/spkr.c optional speaker +X +X----------------------------------------------------------------------------- +X +XFile /sys/i386/i386/conf.c +X[major number 20 (hex) is registered for spkr driver, don't change it] +Xadd following code: +X +X#include "speaker.h" +X#if NSPEAKER > 0 +Xint spkropen(),spkrclose(),spkrwrite(),spkrioctl(); +X#else +X#define spkropen enxio +X#define spkrclose enxio +X#define spkrwrite enxio +X#define spkrioctl enxio +X#endif +X ... +X +Xstruct cdevsw cdevsw[] = +X{ +X ... +X +X { spkropen, spkrclose, enxio, spkrwrite, /*20*/ +X spkrioctl, enxio, enxio, NULL, +X enxio, enxio, enxio }, +X ... +X +X----------------------------------------------------------------------------- +X +XMake corresponding device: +X +X mknod /dev/speaker c 32 0 +X +X[major number 32 (20 hex) is registered for spkr driver, don't change it] +X +X----------------------------------------------------------------------------- +X +XGo to /sys/i386/conf and type +X config YOUR_MACHINE_NAME +Xthen go to /sys/compile/YOUR_MACHINE_NAME and type +X make depend +X make +X +END-of-install.bsd +echo x - spkr.7 +sed 's/^X//' >spkr.7 << 'END-of-spkr.7' +X.TH SPKR 7 +X.SH NAME +Xspkr \- console speaker device driver +X.SH DESCRIPTION +XThe speaker device driver allows applications to control the PC console +Xspeaker on an IBM-PC-compatible machine running UNIX. +X.PP +XOnly one process may have this device open at any given time; open() and +Xclose() are used to lock and relinquish it. An attempt to open() when +Xanother process has the device locked will return -1 with an EBUSY error +Xindication. Writes to the device are interpreted as 'play strings' in a +Xsimple ASCII melody notation. An ioctl() for tone generation at arbitrary +Xfrequencies is also supported. +X.PP +XSound-generation does \fInot\fR monopolize the processor; in fact, the driver +Xspends most of its time sleeping while the PC hardware is emitting +Xtones. Other processes may emit beeps while the driver is running. +X.PP +XApplications may call ioctl() on a speaker file descriptor to control the +Xspeaker driver directly; definitions for the ioctl() interface are in +Xsys/spkr.h. The tone_t structure used in these calls has two fields, +Xspecifying a frequency (in hz) and a duration (in 1/100ths of a second). +XA frequency of zero is interpreted as a rest. +X.PP +XAt present there are two such ioctls. SPKRTONE accepts a pointer to a +Xsingle tone structure as third argument and plays it. SPKRTUNE accepts a +Xpointer to the first of an array of tone structures and plays them in +Xcontinuous sequence; this array must be terminated by a final member with +Xa zero duration. +X.PP +XThe play-string language is modelled on the PLAY statement conventions of +XIBM BASIC 2.0. The MB, MF and X primitives of PLAY are not useful in a UNIX +Xenvironment and are omitted. The `octave-tracking' feature is also new. +X.PP +XThere are 84 accessible notes numbered 1-83 in 7 octaves, each running from +XC to B, numbered 0-6; the scale is equal-tempered A440 and octave 3 starts +Xwith middle C. By default, the play function emits half-second notes with the +Xlast 1/16th second being `rest time'. +X.PP +XPlay strings are interpreted left to right as a series of play command groups; +Xletter case is ignored. Play command groups are as follows: +X.PP +XCDEFGAB -- letters A through G cause the corresponding note to be played in the +Xcurrent octave. A note letter may optionally be followed by an \fIaccidental +Xsign\fR, one of # + or -; the first two of these cause it to be sharped one +Xhalf-tone, the last causes it to be flatted one half-tone. It may also be +Xfollowed by a time value number and by sustain dots (see below). Time values +Xare interpreted as for the L command below;. +X.PP +XO -- if is numeric, this sets the current octave. may also be one +Xof 'L' or 'N' to enable or disable octave-tracking (it is disabled by default). +XWhen octave-tracking is on, interpretation of a pair of letter notes will +Xchange octaves if necessary in order to make the smallest possible jump between +Xnotes. Thus "olbc" will be played as "olb>c", and "olcb" as "olc, < and O[0123456]. +X.PP +X> -- bump the current octave up one. +X.PP +X< -- drop the current octave down one. +X.PP +XN -- play note n, n being 1 to 84 or 0 for a rest of current time value. +XMay be followedv by sustain dots. +X.PP +XL -- sets the current time value for notes. The default is L4, quarter +Xnotes. The lowest possible value is 1; values up to 64 are accepted. L1 sets +Xwhole notes, L2 sets half notes, L4 sets quarter notes, etc.. +X.PP +XP -- pause (rest), with interpreted as for L. May be followed by +Xsustain dots. May also be written '~'. +X.PP +XT -- Sets the number of quarter notes per minute; default is 120. Musical +Xnames for common tempi are: +X +X.TS +Xa a a. +X Tempo Beats Per Minute +Xvery slow Larghissimo +X Largo 40-60 +X Larghetto 60-66 +X Grave +X Lento +X Adagio 66-76 +Xslow Adagietto +X Andante 76-108 +Xmedium Andantino +X Moderato 108-120 +Xfast Allegretto +X Allegro 120-168 +X Vivace +X Veloce +X Presto 168-208 +Xvery fast Prestissimo +X.TE +X.PP +XM[LNS] -- set articulation. MN (N for normal) is the default; the last 1/8th of +Xthe note's value is rest time. You can set ML for legato (no rest space) or +XMS (staccato) 1/4 rest space. +X.PP +XNotes (that is, CDEFGAB or N command character groups) may be followed by +Xsustain dots. Each dot causes the note's value to be lengthened by one-half +Xfor each one. Thus, a note dotted once is held for 3/2 of its undotted value; +Xdotted twice, it is held 9/4, and three times would give 27/8. +X.PP +XWhitespace in play strings is simply skipped and may be used to separate +Xmelody sections. +X.SH BUGS +XDue to roundoff in the pitch tables and slop in the tone-generation and timer +Xhardware (neither of which was designed for precision), neither pitch accuracy +Xnor timings will be mathematically exact. There is no volume control. +X.PP +XIn play strings which are very long (longer than your system's physical I/O +Xblocks) note suffixes or numbers may occasionally be parsed incorrectly due +Xto crossing a block boundary. +X.SH FILES +X/dev/speaker -- speaker device file +X.SH AUTHOR +XEric S. Raymond (esr@snark.thyrsus.com) Feb 1990 +END-of-spkr.7 +echo x - Makefile +sed 's/^X//' >Makefile << 'END-of-Makefile' +X# +X# Speaker driver package makefile +X# +XCFLAGS = -I. -O # -DDEBUG +XLDFLAGS = -s +X +Xall: Driver.o +X +Xinstall: +X ./Install +X +XDriver.o: spkr.c +X $(CC) $(CFLAGS) -c spkr.c +X mv spkr.o Driver.o +X +Xclean: +X rm -f Driver.o *~ speaker.shar +X +XDSP = Files Install Master Name Node Remove Size System +Xshar: +X shar READ.ME install.bsd spkr.7 Makefile spkr.[ch] \ +X interp.c $(DSP) playtest >speaker.shar +END-of-Makefile +echo x - spkr.c +sed 's/^X//' >spkr.c << 'END-of-spkr.c' +X/* +X * spkr.c -- device driver for console speaker on 80386 +X * +X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990 +X * modified for 386bsd by Andrew A. Chernov +X */ +X +X#ifdef __386BSD__ +X#include "speaker.h" +X#endif +X#if !defined(__386BSD__) || (NSPEAKER > 0) +X +X#ifdef __386BSD__ +X#include "types.h" +X#include "param.h" +X#include "errno.h" +X#include "buf.h" +X#include "uio.h" +X +X#define CADDR caddr_t +X#define err_ret(x) return(x) +X#else /* SYSV */ +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X#include +X +X#define CADDR char * +X#define err_ret(x) u.u_error = (x) +X#endif +X +X#include "spkr.h" +X +X/**************** MACHINE DEPENDENT PART STARTS HERE ************************* +X * +X * This section defines a function tone() which causes a tone of given +X * frequency and duration from the 80x86's console speaker. +X * Another function endtone() is defined to force sound off, and there is +X * also a rest() entry point to do pauses. +X * +X * Audible sound is generated using the Programmable Interval Timer (PIT) and +X * Programmable Peripheral Interface (PPI) attached to the 80x86's speaker. The +X * PPI controls whether sound is passed through at all; the PIT's channel 2 is +X * used to generate clicks (a square wave) of whatever frequency is desired. +X * +X * The non-BSD code requires SVr3.2-compatible inb(), outb(), timeout(), +X * sleep(), and wakeup(). +X */ +X +X/* +X * PIT and PPI port addresses and control values +X * +X * Most of the magic is hidden in the TIMER_PREP value, which selects PIT +X * channel 2, frequency LSB first, square-wave mode and binary encoding. +X * The encoding is as follows: +X * +X * +----------+----------+---------------+-----+ +X * | 1 0 | 1 1 | 0 1 1 | 0 | +X * | SC1 SC0 | RW1 RW0 | M2 M1 M0 | BCD | +X * +----------+----------+---------------+-----+ +X * Counter Write Mode 3 Binary +X * Channel 2 LSB first, (Square Wave) Encoding +X * MSB second +X */ +X#define PPI 0x61 /* port of Programmable Peripheral Interface */ +X#define PPI_SPKR 0x03 /* turn these PPI bits on to pass sound */ +X#define PIT_CTRL 0x43 /* PIT control address */ +X#define PIT_COUNT 0x42 /* PIT count address */ +X#define PIT_MODE 0xB6 /* set timer mode for sound generation */ +X +X/* +X * Magic numbers for timer control. +X */ +X#define TIMER_CLK 1193180L /* corresponds to 18.2 MHz tick rate */ +X +Xstatic int endtone() +X/* turn off the speaker, ending current tone */ +X{ +X wakeup((CADDR)endtone); +X outb(PPI, inb(PPI) & ~PPI_SPKR); +X} +X +Xstatic void tone(hz, ticks) +X/* emit tone of frequency hz for given number of ticks */ +Xunsigned int hz, ticks; +X{ +X unsigned int divisor = TIMER_CLK / hz; +X int sps; +X +X#ifdef DEBUG +X printf("tone: hz=%d ticks=%d\n", hz, ticks); +X#endif /* DEBUG */ +X +X /* set timer to generate clicks at given frequency in Hertz */ +X#ifdef __386BSD__ +X sps = spltty(); +X#else +X sps = spl5(); +X#endif +X outb(PIT_CTRL, PIT_MODE); /* prepare timer */ +X outb(PIT_COUNT, (unsigned char) divisor); /* send lo byte */ +X outb(PIT_COUNT, (divisor >> 8)); /* send hi byte */ +X splx(sps); +X +X /* turn the speaker on */ +X outb(PPI, inb(PPI) | PPI_SPKR); +X +X /* +X * Set timeout to endtone function, then give up the timeslice. +X * This is so other processes can execute while the tone is being +X * emitted. +X */ +X timeout((CADDR)endtone, (CADDR)NULL, ticks); +X sleep((CADDR)endtone, PZERO - 1); +X} +X +Xstatic int endrest() +X/* end a rest */ +X{ +X wakeup((CADDR)endrest); +X} +X +Xstatic void rest(ticks) +X/* rest for given number of ticks */ +Xint ticks; +X{ +X /* +X * Set timeout to endrest function, then give up the timeslice. +X * This is so other processes can execute while the rest is being +X * waited out. +X */ +X#ifdef DEBUG +X printf("rest: %d\n", ticks); +X#endif /* DEBUG */ +X timeout((CADDR)endrest, (CADDR)NULL, ticks); +X sleep((CADDR)endrest, PZERO - 1); +X} +X +X#include "interp.c" /* playinit() and playstring() */ +X +X/******************* UNIX DRIVER HOOKS BEGIN HERE ************************** +X * +X * This section implements driver hooks to run playstring() and the tone(), +X * endtone(), and rest() functions defined above. For non-BSD systems, +X * SVr3.2-compatible copyin() is also required. +X */ +X +Xstatic int spkr_active; /* exclusion flag */ +X#ifdef __386BSD__ +Xstatic struct buf *spkr_inbuf; /* incoming buf */ +X#endif +X +Xint spkropen(dev) +Xdev_t dev; +X{ +X#ifdef DEBUG +X printf("spkropen: entering with dev = %x\n", dev); +X#endif /* DEBUG */ +X +X if (minor(dev) != 0) +X err_ret(ENXIO); +X else if (spkr_active) +X err_ret(EBUSY); +X else +X { +X playinit(); +X#ifdef __386BSD__ +X spkr_inbuf = geteblk(DEV_BSIZE); +X#endif +X spkr_active = 1; +X } +X#ifdef __386BSD__ +X return(0); +X#endif +X} +X +X#ifdef __386BSD__ +Xint spkrwrite(dev, uio) +Xstruct uio *uio; +X#else +Xint spkrwrite(dev) +X#endif +Xdev_t dev; +X{ +X#ifdef __386BSD__ +X register unsigned n; +X char *cp; +X int error; +X#endif +X#ifdef DEBUG +X#ifdef __386BSD__ +X printf("spkrwrite: entering with dev = %x, count = %d\n", +X dev, uio->uio_resid); +X#else +X printf("spkrwrite: entering with dev = %x, u.u_count = %d\n", +X dev, u.u_count); +X#endif +X#endif /* DEBUG */ +X +X if (minor(dev) != 0) +X err_ret(ENXIO); +X else +X { +X#ifdef __386BSD__ +X n = MIN(DEV_BSIZE, uio->uio_resid); +X cp = spkr_inbuf->b_un.b_addr; +X error = uiomove(cp, n, uio); +X if (!error) +X playstring(cp, n); +X return(error); +X#else +X char bfr[STD_BLK]; +X +X copyin(u.u_base, bfr, u.u_count); +X playstring(bfr, u.u_count); +X u.u_base += u.u_count; +X u.u_count = 0; +X#endif +X } +X} +X +Xint spkrclose(dev) +Xdev_t dev; +X{ +X#ifdef DEBUG +X printf("spkrclose: entering with dev = %x\n", dev); +X#endif /* DEBUG */ +X +X if (minor(dev) != 0) +X err_ret(ENXIO); +X else +X { +X endtone(); +X#ifdef __386BSD__ +X brelse(spkr_inbuf); +X#endif +X spkr_active = 0; +X } +X#ifdef __386BSD__ +X return(0); +X#endif +X} +X +Xint spkrioctl(dev, cmd, cmdarg) +Xdev_t dev; +Xint cmd; +XCADDR cmdarg; +X{ +X#ifdef DEBUG +X printf("spkrioctl: entering with dev = %x, cmd = %x\n", dev, cmd); +X#endif /* DEBUG */ +X +X if (minor(dev) != 0) +X err_ret(ENXIO); +X else if (cmd == SPKRTONE) +X { +X tone_t *tp = (tone_t *)cmdarg; +X +X if (tp->frequency == 0) +X rest(tp->duration); +X else +X tone(tp->frequency, tp->duration); +X } +X else if (cmd == SPKRTUNE) +X { +X#ifdef __386BSD__ +X tone_t *tp = (tone_t *)(*(caddr_t *)cmdarg); +X tone_t ttp; +X int error; +X +X for (; ; tp++) { +X error = copyin(tp, &ttp, sizeof(tone_t)); +X if (error) +X return(error); +X if (ttp.duration == 0) +X break; +X if (ttp.frequency == 0) +X rest(ttp.duration); +X else +X tone(ttp.frequency, ttp.duration); +X } +X#else +X tone_t *tp = (tone_t *)cmdarg; +X +X for (; tp->duration; tp++) +X if (tp->frequency == 0) +X rest(tp->duration); +X else +X tone(tp->frequency, tp->duration); +X#endif +X } +X else +X err_ret(EINVAL); +X#ifdef __386BSD__ +X return(0); +X#endif +X} +X +X#endif /* !defined(__386BSD__) || (NSPEAKER > 0) */ +X/* spkr.c ends here */ +END-of-spkr.c +echo x - spkr.h +sed 's/^X//' >spkr.h << 'END-of-spkr.h' +X/* +X * spkr.h -- interface definitions for speaker ioctl() +X * +X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990 +X * modified for 386bsd by Andrew A. Chernov +X */ +X +X#ifndef _SPKR_H_ +X#define _SPKR_H_ +X +X#ifdef __386BSD__ +X#ifndef KERNEL +X#include +X#else +X#include "ioctl.h" +X#endif +X +X#define SPKRTONE _IOW('S', 1, tone_t) /* emit tone */ +X#define SPKRTUNE _IO('S', 2) /* emit tone sequence*/ +X#else /* SYSV */ +X#define SPKRIOC ('S'<<8) +X#define SPKRTONE (SPKRIOC|1) /* emit tone */ +X#define SPKRTUNE (SPKRIOC|2) /* emit tone sequence*/ +X#endif +X +Xtypedef struct +X{ +X int frequency; /* in hertz */ +X int duration; /* in 1/100ths of a second */ +X} +Xtone_t; +X +X#endif /* _SPKR_H_ */ +X/* spkr.h ends here */ +END-of-spkr.h +echo x - interp.c +sed 's/^X//' >interp.c << 'END-of-interp.c' +X/* +X * interp.c -- device driver for console speaker on 80386 +X * +X * v1.1 by Eric S. Raymond (esr@snark.thyrsus.com) Feb 1990 +X * +X * this is the part of the code common to all 386 UNIX OSes +X * +X * playinit() and playstring() are called from the appropriate driver +X */ +X +X#ifdef __386BSD__ +X#include "param.h" +X#else +X#include +X#endif +X +X#ifndef HZ +X#define HZ 60 +X#endif +X +X +X/**************** PLAY STRING INTERPRETER BEGINS HERE ********************** +X * +X * Play string interpretation is modelled on IBM BASIC 2.0's PLAY statement; +X * M[LNS] are missing and the ~ synonym and octave-tracking facility is added. +X * Requires tone(), rest(), and endtone(). String play is not interruptible +X * except possibly at physical block boundaries. +X */ +X +Xtypedef int bool; +X#ifndef TRUE +X#define TRUE 1 +X#endif +X#ifndef FALSE +X#define FALSE 0 +X#endif +X +X#define toupper(c) ((c) - ' ' * (((c) >= 'a') && ((c) <= 'z'))) +X#define isdigit(c) (((c) >= '0') && ((c) <= '9')) +X#define dtoi(c) ((c) - '0') +X +Xstatic int octave; /* currently selected octave */ +Xstatic int whole; /* whole-note time at current tempo, in ticks */ +Xstatic int value; /* whole divisor for note time, quarter note = 1 */ +Xstatic int fill; /* controls spacing of notes */ +Xstatic bool octtrack; /* octave-tracking on? */ +Xstatic bool octprefix; /* override current octave-tracking state? */ +X +X/* +X * Magic number avoidance... +X */ +X#define SECS_PER_MIN 60 /* seconds per minute */ +X#define WHOLE_NOTE 4 /* quarter notes per whole note */ +X#define MIN_VALUE 64 /* the most we can divide a note by */ +X#define DFLT_VALUE 4 /* default value (quarter-note) */ +X#define FILLTIME 8 /* for articulation, break note in parts */ +X#define STACCATO 6 /* 6/8 = 3/4 of note is filled */ +X#define NORMAL 7 /* 7/8ths of note interval is filled */ +X#define LEGATO 8 /* all of note interval is filled */ +X#define DFLT_OCTAVE 4 /* default octave */ +X#define MIN_TEMPO 32 /* minimum tempo */ +X#define DFLT_TEMPO 120 /* default tempo */ +X#define MAX_TEMPO 255 /* max tempo */ +X#define NUM_MULT 3 /* numerator of dot multiplier */ +X#define DENOM_MULT 2 /* denominator of dot multiplier */ +X +X/* letter to half-tone: A B C D E F G */ +Xstatic int notetab[8] = {9, 11, 0, 2, 4, 5, 7}; +X +X/* +X * This is the American Standard A440 Equal-Tempered scale with frequencies +X * rounded to nearest integer. Thank Goddess for the good ol' CRC Handbook... +X * our octave 0 is standard octave 2. +X */ +X#define OCTAVE_NOTES 12 /* semitones per octave */ +Xstatic int pitchtab[] = +X{ +X/* C C# D D# E F F# G G# A A# B*/ +X/* 0 */ 65, 69, 73, 78, 82, 87, 93, 98, 103, 110, 117, 123, +X/* 1 */ 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, +X/* 2 */ 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, +X/* 3 */ 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, +X/* 4 */ 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1975, +X/* 5 */ 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, +X/* 6 */ 4186, 4435, 4698, 4978, 5274, 5588, 5920, 6272, 6644, 7040, 7459, 7902, +X}; +X +Xstatic void playinit() +X{ +X octave = DFLT_OCTAVE; +X whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / DFLT_TEMPO; +X fill = NORMAL; +X value = DFLT_VALUE; +X octtrack = FALSE; +X octprefix = TRUE; /* act as though there was an initial O(n) */ +X} +X +Xstatic void playtone(pitch, value, sustain) +X/* play tone of proper duration for current rhythm signature */ +Xint pitch, value, sustain; +X{ +X register int sound, silence, snum = 1, sdenom = 1; +X +X /* this weirdness avoids floating-point arithmetic */ +X for (; sustain; sustain--) +X { +X snum *= NUM_MULT; +X sdenom *= DENOM_MULT; +X } +X +X if (pitch == -1) +X rest(whole * snum / (value * sdenom)); +X else +X { +X sound = (whole * snum) / (value * sdenom) +X - (whole * (FILLTIME - fill)) / (value * FILLTIME); +X silence = whole * (FILLTIME-fill) * snum / (FILLTIME * value * sdenom); +X +X#ifdef DEBUG +X printf("playtone: pitch %d for %d ticks, rest for %d ticks\n", +X pitch, sound, silence); +X#endif /* DEBUG */ +X +X tone(pitchtab[pitch], sound); +X if (fill != LEGATO) +X rest(silence); +X } +X} +X +Xstatic int abs(n) +Xint n; +X{ +X if (n < 0) +X return(-n); +X else +X return(n); +X} +X +Xstatic void playstring(cp, slen) +X/* interpret and play an item from a notation string */ +Xchar *cp; +Xsize_t slen; +X{ +X int pitch, lastpitch = OCTAVE_NOTES * DFLT_OCTAVE; +X +X#define GETNUM(cp, v) for(v=0; isdigit(cp[1]) && slen > 0; ) \ +X {v = v * 10 + (*++cp - '0'); slen--;} +X for (; slen--; cp++) +X { +X int sustain, timeval, tempo; +X register char c = toupper(*cp); +X +X#ifdef DEBUG +X printf("playstring: %c (%x)\n", c, c); +X#endif /* DEBUG */ +X +X switch (c) +X { +X case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': +X +X /* compute pitch */ +X pitch = notetab[c - 'A'] + octave * OCTAVE_NOTES; +X +X /* this may be followed by an accidental sign */ +X if (cp[1] == '#' || cp[1] == '+') +X { +X ++pitch; +X ++cp; +X slen--; +X } +X else if (cp[1] == '-') +X { +X --pitch; +X ++cp; +X slen--; +X } +X +X /* +X * If octave-tracking mode is on, and there has been no octave- +X * setting prefix, find the version of the current letter note +X * closest to the last regardless of octave. +X */ +X if (octtrack && !octprefix) +X { +X if (abs(pitch-lastpitch) > abs(pitch+OCTAVE_NOTES-lastpitch)) +X { +X ++octave; +X pitch += OCTAVE_NOTES; +X } +X +X if (abs(pitch-lastpitch) > abs((pitch-OCTAVE_NOTES)-lastpitch)) +X { +X --octave; +X pitch -= OCTAVE_NOTES; +X } +X } +X octprefix = FALSE; +X lastpitch = pitch; +X +X /* ...which may in turn be followed by an override time value */ +X GETNUM(cp, timeval); +X if (timeval <= 0 || timeval > MIN_VALUE) +X timeval = value; +X +X /* ...and/or sustain dots */ +X for (sustain = 0; cp[1] == '.'; cp++) +X { +X slen--; +X sustain++; +X } +X +X /* time to emit the actual tone */ +X playtone(pitch, timeval, sustain); +X break; +X +X case 'O': +X if (cp[1] == 'N' || cp[1] == 'n') +X { +X octprefix = octtrack = FALSE; +X ++cp; +X slen--; +X } +X else if (cp[1] == 'L' || cp[1] == 'l') +X { +X octtrack = TRUE; +X ++cp; +X slen--; +X } +X else +X { +X GETNUM(cp, octave); +X if (octave >= sizeof(pitchtab) / OCTAVE_NOTES) +X octave = DFLT_OCTAVE; +X octprefix = TRUE; +X } +X break; +X +X case '>': +X if (octave < sizeof(pitchtab) / OCTAVE_NOTES - 1) +X octave++; +X octprefix = TRUE; +X break; +X +X case '<': +X if (octave > 0) +X octave--; +X octprefix = TRUE; +X break; +X +X case 'N': +X GETNUM(cp, pitch); +X for (sustain = 0; cp[1] == '.'; cp++) +X { +X slen--; +X sustain++; +X } +X playtone(pitch - 1, value, sustain); +X break; +X +X case 'L': +X GETNUM(cp, value); +X if (value <= 0 || value > MIN_VALUE) +X value = DFLT_VALUE; +X break; +X +X case 'P': +X case '~': +X /* this may be followed by an override time value */ +X GETNUM(cp, timeval); +X if (timeval <= 0 || timeval > MIN_VALUE) +X timeval = value; +X for (sustain = 0; cp[1] == '.'; cp++) +X { +X slen--; +X sustain++; +X } +X playtone(-1, timeval, sustain); +X break; +X +X case 'T': +X GETNUM(cp, tempo); +X if (tempo < MIN_TEMPO || tempo > MAX_TEMPO) +X tempo = DFLT_TEMPO; +X whole = (HZ * SECS_PER_MIN * WHOLE_NOTE) / tempo; +X break; +X +X case 'M': +X if (cp[1] == 'N' || cp[1] == 'n') +X { +X fill = NORMAL; +X ++cp; +X slen--; +X } +X else if (cp[1] == 'L' || cp[1] == 'l') +X { +X fill = LEGATO; +X ++cp; +X slen--; +X } +X else if (cp[1] == 'S' || cp[1] == 's') +X { +X fill = STACCATO; +X ++cp; +X slen--; +X } +X break; +X } +X } +X} +END-of-interp.c +echo x - Files +sed 's/^X//' >Files << 'END-of-Files' +X/usr/include/sys/spkr.h +END-of-Files +echo x - Install +sed 's/^X//' >Install << 'END-of-Install' +X# +X# Speaker driver installation script +X# +XTMP=/tmp/speaker.err +XERR1=" Errors have been written to the file $TMP." +XERR2=" The Speaker Driver software was not installed." +X +Xecho "Installing Speaker Driver Software Package" +X +X/etc/conf/bin/idcheck -p speaker 2>$TMP +Xif [ $? != 0 ] +Xthen +X echo "The speaker package is already at least partly installed. +X Removing the old version now..." +X /etc/conf/bin/idinstall -d speaker +Xfi +X +X/etc/conf/bin/idinstall -a -k speaker 2>>$TMP +Xif [ $? != 0 ] +Xthen +X message "There was an error during package installation. $ERR1 $ERR2" +X exit 1 +Xfi +X +X/etc/conf/bin/idbuild 2>>$TMP +Xif [ $? != 0 ] +Xthen +X message "There was an error during kernel reconfiguration. $ERR1 $ERR2" +X exit 1 +Xfi +X +Xrm -f $TMP +X +Xcp spkr.h /usr/include/sys/spkr.h +X +Xecho "Performing shutdown..." +Xcd /; exec /etc/shutdown -g0 -y +END-of-Install +echo x - Master +sed 's/^X//' >Master << 'END-of-Master' +Xspeaker ocwi iocH spkr 0 0 1 1 -1 +END-of-Master +echo x - Name +sed 's/^X//' >Name << 'END-of-Name' +X386 UNIX Speaker Device Driver Package +END-of-Name +echo x - Node +sed 's/^X//' >Node << 'END-of-Node' +Xspeaker speaker c 0 +END-of-Node +echo x - Remove +sed 's/^X//' >Remove << 'END-of-Remove' +X# +X# Speaker driver remove script +X# +XTMP=/tmp/speaker.err +XRERR="Errors have been written to the file $TMP." +X +Xecho "Removing Speaker Driver Software Package" +X +X/etc/conf/bin/idinstall -d speaker 2>$TMP +Xif [ $? != 0 ] +Xthen +X message "There was an error during package removal. $RERR" +X exit 1 +Xfi +X +X/etc/conf/bin/idbuild 2>>$TMP +Xif [ $? != 0 ] +Xthen +X message "There was an error during kernel reconfiguration. $RERR" +X exit 1 +Xfi +X +Xrm -f /dev/speaker $TMP /usr/include/sys/spkr.h +X +Xexit 0 +END-of-Remove +echo x - Size +sed 's/^X//' >Size << 'END-of-Size' +XROOT=1400 +XUSR=100 +END-of-Size +echo x - System +sed 's/^X//' >System << 'END-of-System' +Xspeaker Y 1 0 0 0 0 0 0 0 +END-of-System +echo x - playtest +sed 's/^X//' >playtest << 'END-of-playtest' +X: +X# Test script for the speaker driver +X# +X# v1.0 by Eric S. Raymond (Feb 1990) +X# modified for 386bsd by Andrew A. Chernov +X# +Xreveille="t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f.." +Xcontact="f" +Xdance="t240dcdc/dev/speaker;; +Xcontact) echo $contact >/dev/speaker;; +Xdance) echo $dance >/dev/speaker;; +Xloony) echo $loony >/dev/speaker;; +X*) +X echo "No such tune. Available tunes are:" +X echo +X echo "reveille -- Reveille" +X echo "contact -- Contact theme from Close Encounters" +X echo "dance -- Lord of the Dance (aka Simple Gifts)" +X echo "loony -- Loony Toons theme" +X ;; +Xesac +END-of-playtest +exit diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c new file mode 100644 index 0000000..a13514f --- /dev/null +++ b/sys/unix/unixmain.c @@ -0,0 +1,541 @@ +/* SCCS Id: @(#)unixmain.c 3.4 1997/01/22 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* main.c - Unix NetHack */ + +#include "hack.h" +#include "dlb.h" + +#include +#include +#include +#ifndef O_RDONLY +#include +#endif + +#if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX) +# if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__)) +# if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX) +extern struct passwd *FDECL(getpwuid,(uid_t)); +# else +extern struct passwd *FDECL(getpwuid,(int)); +# endif +# endif +#endif +extern struct passwd *FDECL(getpwnam,(const char *)); +#ifdef CHDIR +static void FDECL(chdirx, (const char *,BOOLEAN_P)); +#endif /* CHDIR */ +static boolean NDECL(whoami); +static void FDECL(process_options, (int, char **)); + +#ifdef _M_UNIX +extern void NDECL(check_sco_console); +extern void NDECL(init_sco_cons); +#endif +#ifdef __linux__ +extern void NDECL(check_linux_console); +extern void NDECL(init_linux_cons); +#endif + +static void NDECL(wd_message); +#ifdef WIZARD +static boolean wiz_error_flag = FALSE; +#endif + +int +main(argc,argv) +int argc; +char *argv[]; +{ + register int fd; +#ifdef CHDIR + register char *dir; +#endif + boolean exact_username; + +#if defined(__APPLE__) + /* special hack to change working directory to a resource fork when + running from finder --sam */ +#define MAC_PATH_VALUE ".app/Contents/MacOS/" + char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; + int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len=0; + getcwd(mac_cwd, 1024); + if(mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { + if((mac_exe = strrchr(mac_exe, '/'))) + mac_exe++; + else + mac_exe = argv[0]; + mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); + if(mac_tmp_len <= arg0_len) { + mac_tmp = malloc(mac_tmp_len + 1); + sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); + if(!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { + mac_lhs_len = (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; + if(mac_lhs_len > mac_tmp_len - 1) + mac_tmp = realloc(mac_tmp, mac_lhs_len); + strncpy(mac_tmp, argv[0], mac_lhs_len); + mac_tmp[mac_lhs_len] = '\0'; + chdir(mac_tmp); + } + free(mac_tmp); + } + } +#endif + + hname = argv[0]; + hackpid = getpid(); + (void) umask(0777 & ~FCMASK); + + choose_windows(DEFAULT_WINDOW_SYS); + +#ifdef CHDIR /* otherwise no chdir() */ + /* + * See if we must change directory to the playground. + * (Perhaps hack runs suid and playground is inaccessible + * for the player.) + * The environment variable HACKDIR is overridden by a + * -d command line option (must be the first option given) + */ + dir = nh_getenv("NETHACKDIR"); + if (!dir) dir = nh_getenv("HACKDIR"); +#endif + if(argc > 1) { +#ifdef CHDIR + if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { + /* avoid matching "-dec" for DECgraphics; since the man page + * says -d directory, hope nobody's using -desomething_else + */ + argc--; + argv++; + dir = argv[0]+2; + if(*dir == '=' || *dir == ':') dir++; + if(!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if(!*dir) + error("Flag -d must be followed by a directory name."); + } + if (argc > 1) +#endif /* CHDIR */ + + /* + * Now we know the directory containing 'record' and + * may do a prscore(). Exclude `-style' - it's a Qt option. + */ + if (!strncmp(argv[1], "-s", 2) && strncmp(argv[1], "-style", 6)) { +#ifdef CHDIR + chdirx(dir,0); +#endif + prscore(argc, argv); + exit(EXIT_SUCCESS); + } + } + + /* + * Change directories before we initialize the window system so + * we can find the tile file. + */ +#ifdef CHDIR + chdirx(dir,1); +#endif + +#ifdef _M_UNIX + check_sco_console(); +#endif +#ifdef __linux__ + check_linux_console(); +#endif + initoptions(); + init_nhwindows(&argc,argv); + exact_username = whoami(); +#ifdef _M_UNIX + init_sco_cons(); +#endif +#ifdef __linux__ + init_linux_cons(); +#endif + + /* + * It seems you really want to play. + */ + u.uhp = 1; /* prevent RIP on early quits */ + (void) signal(SIGHUP, (SIG_RET_TYPE) hangup); +#ifdef SIGXCPU + (void) signal(SIGXCPU, (SIG_RET_TYPE) hangup); +#endif + + process_options(argc, argv); /* command line options */ + +#ifdef DEF_PAGER + if(!(catmore = nh_getenv("HACKPAGER")) && !(catmore = nh_getenv("PAGER"))) + catmore = DEF_PAGER; +#endif +#ifdef MAIL + getmailstatus(); +#endif +#ifdef WIZARD + if (wizard) + Strcpy(plname, "wizard"); + else +#endif + if(!*plname || !strncmp(plname, "player", 4) + || !strncmp(plname, "games", 4)) { + askname(); + } else if (exact_username) { + /* guard against user names with hyphens in them */ + int len = strlen(plname); + /* append the current role, if any, so that last dash is ours */ + if (++len < sizeof plname) + (void)strncat(strcat(plname, "-"), + pl_character, sizeof plname - len - 1); + } + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ +#ifdef WIZARD + if(!wizard) { +#endif + /* + * check for multiple games under the same name + * (if !locknum) or check max nr of players (otherwise) + */ + (void) signal(SIGQUIT,SIG_IGN); + (void) signal(SIGINT,SIG_IGN); + if(!locknum) + Sprintf(lock, "%d%s", (int)getuid(), plname); + getlock(); +#ifdef WIZARD + } else { + Sprintf(lock, "%d%s", (int)getuid(), plname); + getlock(); + } +#endif /* WIZARD */ + + dlb_init(); /* must be before newgame() */ + + /* + * Initialization of the boundaries of the mazes + * Both boundaries have to be even. + */ + x_maze_max = COLNO-1; + if (x_maze_max % 2) + x_maze_max--; + y_maze_max = ROWNO-1; + if (y_maze_max % 2) + y_maze_max--; + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + display_gamewindows(); + + if ((fd = restore_saved_game()) >= 0) { +#ifdef WIZARD + /* Since wizard is actually flags.debug, restoring might + * overwrite it. + */ + boolean remember_wiz_mode = wizard; +#endif + const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1); + + (void) chmod(fq_save,0); /* disallow parallel restores */ + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#ifdef NEWS + if(iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; /* in case dorecover() fails */ + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + if(!dorecover(fd)) + goto not_recovered; +#ifdef WIZARD + if(!wizard && remember_wiz_mode) wizard = TRUE; +#endif + check_special_room(FALSE); + wd_message(); + + if (discover || wizard) { + if(yn("Do you want to keep the save file?") == 'n') + (void) delete_savefile(); + else { + (void) chmod(fq_save,FCMASK); /* back to readable */ + compress(fq_save); + } + } + flags.move = 0; + } else { +not_recovered: + player_selection(); + newgame(); + wd_message(); + + flags.move = 0; + set_wear(); + (void) pickup(1); + } + + moveloop(); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return(0); +} + +static void +process_options(argc, argv) +int argc; +char *argv[]; +{ + int i; + + + /* + * Process options. + */ + while(argc > 1 && argv[1][0] == '-'){ + argv++; + argc--; + switch(argv[0][1]){ + case 'D': +#ifdef WIZARD + { + char *user; + int uid; + struct passwd *pw = (struct passwd *)0; + + uid = getuid(); + user = getlogin(); + if (user) { + pw = getpwnam(user); + if (pw && (pw->pw_uid != uid)) pw = 0; + } + if (pw == 0) { + user = nh_getenv("USER"); + if (user) { + pw = getpwnam(user); + if (pw && (pw->pw_uid != uid)) pw = 0; + } + if (pw == 0) { + pw = getpwuid(uid); + } + } + if (pw && !strcmp(pw->pw_name,WIZARD)) { + wizard = TRUE; + break; + } + } + /* otherwise fall thru to discover */ + wiz_error_flag = TRUE; +#endif + case 'X': + discover = TRUE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if(argv[0][2]) + (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); + else if(argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname)-1); + } else + raw_print("Player name expected after -u"); + break; + case 'I': + case 'i': + if (!strncmpi(argv[0]+1, "IBM", 3)) + switch_graphics(IBM_GRAPHICS); + break; + /* case 'D': */ + case 'd': + if (!strncmpi(argv[0]+1, "DEC", 3)) + switch_graphics(DEC_GRAPHICS); + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; + case '@': + flags.randomall = 1; + break; + default: + if ((i = str2role(&argv[0][1])) >= 0) { + flags.initrole = i; + break; + } + /* else raw_printf("Unknown option: %s", *argv); */ + } + } + + if(argc > 1) + locknum = atoi(argv[1]); +#ifdef MAX_NR_OF_PLAYERS + if(!locknum || locknum > MAX_NR_OF_PLAYERS) + locknum = MAX_NR_OF_PLAYERS; +#endif +} + +#ifdef CHDIR +static void +chdirx(dir, wr) +const char *dir; +boolean wr; +{ + if (dir /* User specified directory? */ +# ifdef HACKDIR + && strcmp(dir, HACKDIR) /* and not the default? */ +# endif + ) { +# ifdef SECURE + (void) setgid(getgid()); + (void) setuid(getuid()); /* Ron Wessels */ +# endif + } else { + /* non-default data files is a sign that scores may not be + * compatible, or perhaps that a binary not fitting this + * system's layout is being used. + */ +# ifdef VAR_PLAYGROUND + int len = strlen(VAR_PLAYGROUND); + + fqn_prefix[SCOREPREFIX] = (char *)alloc(len+2); + Strcpy(fqn_prefix[SCOREPREFIX], VAR_PLAYGROUND); + if (fqn_prefix[SCOREPREFIX][len-1] != '/') { + fqn_prefix[SCOREPREFIX][len] = '/'; + fqn_prefix[SCOREPREFIX][len+1] = '\0'; + } +# endif + } + +# ifdef HACKDIR + if (dir == (const char *)0) + dir = HACKDIR; +# endif + + if (dir && chdir(dir) < 0) { + perror(dir); + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file */ + /* perhaps we should also test whether . is writable */ + /* unfortunately the access system-call is worthless */ + if (wr) { +# ifdef VAR_PLAYGROUND + fqn_prefix[LEVELPREFIX] = fqn_prefix[SCOREPREFIX]; + fqn_prefix[SAVEPREFIX] = fqn_prefix[SCOREPREFIX]; + fqn_prefix[BONESPREFIX] = fqn_prefix[SCOREPREFIX]; + fqn_prefix[LOCKPREFIX] = fqn_prefix[SCOREPREFIX]; + fqn_prefix[TROUBLEPREFIX] = fqn_prefix[SCOREPREFIX]; +# endif + check_recordfile(dir); + } +} +#endif /* CHDIR */ + +static boolean +whoami() { + /* + * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS + * 2. Use $USER or $LOGNAME (if 1. fails) + * 3. Use getlogin() (if 2. fails) + * The resulting name is overridden by command line options. + * If everything fails, or if the resulting name is some generic + * account like "games", "play", "player", "hack" then eventually + * we'll ask him. + * Note that we trust the user here; it is possible to play under + * somebody else's name. + */ + register char *s; + + if (*plname) return FALSE; + if(/* !*plname && */ (s = nh_getenv("USER"))) + (void) strncpy(plname, s, sizeof(plname)-1); + if(!*plname && (s = nh_getenv("LOGNAME"))) + (void) strncpy(plname, s, sizeof(plname)-1); + if(!*plname && (s = getlogin())) + (void) strncpy(plname, s, sizeof(plname)-1); + return TRUE; +} + +#ifdef PORT_HELP +void +port_help() +{ + /* + * Display unix-specific help. Just show contents of the helpfile + * named by PORT_HELP. + */ + display_file(PORT_HELP, TRUE); +} +#endif + +static void +wd_message() +{ +#ifdef WIZARD + if (wiz_error_flag) { + pline("Only user \"%s\" may access debug (wizard) mode.", +# ifndef KR1ED + WIZARD); +# else + WIZARD_NAME); +# endif + pline("Entering discovery mode instead."); + } else +#endif + if (discover) + You("are in non-scoring discovery mode."); +} + +/* + * Add a slash to any name not ending in /. There must + * be room for the / + */ +void +append_slash(name) +char *name; +{ + char *ptr; + + if (!*name) + return; + ptr = name + (strlen(name) - 1); + if (*ptr != '/') { + *++ptr = '/'; + *++ptr = '\0'; + } + return; +} + +/*unixmain.c*/ diff --git a/sys/unix/unixres.c b/sys/unix/unixres.c new file mode 100644 index 0000000..3e7f989 --- /dev/null +++ b/sys/unix/unixres.c @@ -0,0 +1,209 @@ +/* SCCS Id: @(#)unixres.c 3.4 2001/07/08 */ +/* Copyright (c) Slash'EM development team, 2001. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* [ALI] This module defines nh_xxx functions to replace getuid etc which + * will hide privileges from the caller if so desired. + * + * Currently supported UNIX variants: + * Linux version 2.1.44 and above + * FreeBSD (versions unknown) + * + * Note: SunOS and Solaris have no mechanism for retrieving the saved id, + * so temporarily dropping privileges on these systems is sufficient to + * hide them. + */ + +#include "config.h" + +#ifdef GETRES_SUPPORT + +# if defined(LINUX) + +/* requires dynamic linking with libc */ +#include + +static int +real_getresuid(ruid, euid, suid) +uid_t *ruid, *euid, *suid; +{ + int (*f)(uid_t *, uid_t *, uid_t *); /* getresuid signature */ + + f = dlsym(RTLD_NEXT, "getresuid"); + if (!f) return -1; + + return f(ruid, euid, suid); +} + +static int +real_getresgid(rgid, egid, sgid) +gid_t *rgid, *egid, *sgid; +{ + int (*f)(gid_t *, gid_t *, gid_t *); /* getresgid signature */ + + f = dlsym(RTLD_NEXT, "getresgid"); + if (!f) return -1; + + return f(rgid, egid, sgid); +} + +# else +# if defined(BSD) || defined(SVR4) + +# ifdef SYS_getresuid + +static int +real_getresuid(ruid, euid, suid) +uid_t *ruid, *euid, *suid; +{ + return syscall(SYS_getresuid, ruid, euid, suid); +} + +# else /* SYS_getresuid */ + +#ifdef SVR4 +#include +#endif /* SVR4 */ + +static int +real_getresuid(ruid, euid, suid) +uid_t *ruid, *euid, *suid; +{ + int retval; + int pfd[2]; + struct stat st; + if (pipe(pfd)) + return -1; + retval = fstat(pfd[0], &st); + close(pfd[0]); + close(pfd[1]); + if (!retval) { + *euid = st.st_uid; + *ruid = syscall(SYS_getuid); + *suid = *ruid; /* Not supported under SVR4 */ + } + return retval; +} + +# endif /* SYS_getresuid */ + +# ifdef SYS_getresgid + +static int +real_getresgid(rgid, egid, sgid) +gid_t *rgid, *egid, *sgid; +{ + return syscall(SYS_getresgid, rgid, egid, sgid); +} + +# else /* SYS_getresgid */ + +static int +real_getresgid(rgid, egid, sgid) +gid_t *rgid, *egid, *sgid; +{ + int retval; + int pfd[2]; + struct stat st; + if (pipe(pfd)) + return -1; + retval = fstat(pfd[0], &st); + close(pfd[0]); + close(pfd[1]); + if (!retval) { + *egid = st.st_gid; + *rgid = syscall(SYS_getgid); + *sgid = *rgid; /* Not supported under SVR4 */ + } + return retval; +} + +# endif /* SYS_getresgid */ +# endif /* BSD || SVR4 */ +# endif /* LINUX */ + +static unsigned int hiding_privileges = 0; + +/* + * Note: returns the value _after_ action. + */ + +int +hide_privileges(flag) +boolean flag; +{ + if (flag) + hiding_privileges++; + else if (hiding_privileges) + hiding_privileges--; + return hiding_privileges; +} + +int +nh_getresuid(ruid, euid, suid) +uid_t *ruid, *euid, *suid; +{ + int retval = real_getresuid(ruid, euid, suid); + if (!retval && hiding_privileges) + *euid = *suid = *ruid; + return retval; +} + +uid_t +nh_getuid() +{ + uid_t ruid, euid, suid; + (void) real_getresuid(&ruid, &euid, &suid); + return ruid; +} + +uid_t +nh_geteuid() +{ + uid_t ruid, euid, suid; + (void) real_getresuid(&ruid, &euid, &suid); + if (hiding_privileges) + euid = ruid; + return euid; +} + +int +nh_getresgid(rgid, egid, sgid) +gid_t *rgid, *egid, *sgid; +{ + int retval = real_getresgid(rgid, egid, sgid); + if (!retval && hiding_privileges) + *egid = *sgid = *rgid; + return retval; +} + +gid_t +nh_getgid() +{ + gid_t rgid, egid, sgid; + (void) real_getresgid(&rgid, &egid, &sgid); + return rgid; +} + +gid_t +nh_getegid() +{ + gid_t rgid, egid, sgid; + (void) real_getresgid(&rgid, &egid, &sgid); + if (hiding_privileges) + egid = rgid; + return egid; +} + +#else /* GETRES_SUPPORT */ + +# ifdef GNOME_GRAPHICS +int +hide_privileges(flag) +boolean flag; +{ + return 0; +} +# endif + +#endif /* GETRES_SUPPORT */ diff --git a/sys/unix/unixunix.c b/sys/unix/unixunix.c new file mode 100644 index 0000000..c2f84eb --- /dev/null +++ b/sys/unix/unixunix.c @@ -0,0 +1,349 @@ +/* SCCS Id: @(#)unixunix.c 3.4 1994/11/07 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file collects some Unix dependencies */ + +#include "hack.h" /* mainly for index() which depends on BSD */ + +#include +#include +#if defined(NO_FILE_LINKS) || defined(SUNOS4) || defined(POSIX_TYPES) +#include +#endif +#include + +#ifdef _M_UNIX +extern void NDECL(sco_mapon); +extern void NDECL(sco_mapoff); +#endif +#ifdef __linux__ +extern void NDECL(linux_mapon); +extern void NDECL(linux_mapoff); +#endif + +#ifndef NHSTDC +extern int errno; +#endif + +static struct stat buf; + +/* see whether we should throw away this xlock file */ +static int +veryold(fd) +int fd; +{ + time_t date; + + if(fstat(fd, &buf)) return(0); /* cannot get status */ +#ifndef INSURANCE + if(buf.st_size != sizeof(int)) return(0); /* not an xlock file */ +#endif +#if defined(BSD) && !defined(POSIX_TYPES) + (void) time((long *)(&date)); +#else + (void) time(&date); +#endif + if(date - buf.st_mtime < 3L*24L*60L*60L) { /* recent */ + int lockedpid; /* should be the same size as hackpid */ + + if(read(fd, (genericptr_t)&lockedpid, sizeof(lockedpid)) != + sizeof(lockedpid)) + /* strange ... */ + return(0); + + /* From: Rick Adams */ + /* This will work on 4.1cbsd, 4.2bsd and system 3? & 5. */ + /* It will do nothing on V7 or 4.1bsd. */ +#ifndef NETWORK + /* It will do a VERY BAD THING if the playground is shared + by more than one machine! -pem */ + if(!(kill(lockedpid, 0) == -1 && errno == ESRCH)) +#endif + return(0); + } + (void) close(fd); + return(1); +} + +static int +eraseoldlocks() +{ + register int i; + + /* cannot use maxledgerno() here, because we need to find a lock name + * before starting everything (including the dungeon initialization + * that sets astral_level, needed for maxledgerno()) up + */ + for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) { + /* try to remove all */ + set_levelfile_name(lock, i); + (void) unlink(fqname(lock, LEVELPREFIX, 0)); + } + set_levelfile_name(lock, 0); + if (unlink(fqname(lock, LEVELPREFIX, 0))) + return(0); /* cannot remove it */ + return(1); /* success! */ +} + +void +getlock() +{ + register int i = 0, fd, c; + const char *fq_lock; + +#ifdef TTY_GRAPHICS + /* idea from rpick%ucqais@uccba.uc.edu + * prevent automated rerolling of characters + * test input (fd0) so that tee'ing output to get a screen dump still + * works + * also incidentally prevents development of any hack-o-matic programs + */ + /* added check for window-system type -dlc */ + if (!strcmp(windowprocs.name, "tty")) + if (!isatty(0)) + error("You must play from a terminal."); +#endif + + /* we ignore QUIT and INT at this point */ + if (!lock_file(HLOCK, LOCKPREFIX, 10)) { + wait_synch(); + error("%s", ""); + } + + regularize(lock); + set_levelfile_name(lock, 0); + + if(locknum) { + if(locknum > 25) locknum = 25; + + do { + lock[0] = 'a' + i++; + fq_lock = fqname(lock, LEVELPREFIX, 0); + + if((fd = open(fq_lock, 0)) == -1) { + if(errno == ENOENT) goto gotlock; /* no such file */ + perror(fq_lock); + unlock_file(HLOCK); + error("Cannot open %s", fq_lock); + } + + if(veryold(fd) /* closes fd if true */ + && eraseoldlocks()) + goto gotlock; + (void) close(fd); + } while(i < locknum); + + unlock_file(HLOCK); + error("Too many hacks running now."); + } else { + fq_lock = fqname(lock, LEVELPREFIX, 0); + if((fd = open(fq_lock, 0)) == -1) { + if(errno == ENOENT) goto gotlock; /* no such file */ + perror(fq_lock); + unlock_file(HLOCK); + error("Cannot open %s", fq_lock); + } + + if(veryold(fd) /* closes fd if true */ && eraseoldlocks()) + goto gotlock; + (void) close(fd); + + if(iflags.window_inited) { + c = yn("There is already a game in progress under your name. Destroy old game?"); + } else { + (void) printf("\nThere is already a game in progress under your name."); + (void) printf(" Destroy old game? [yn] "); + (void) fflush(stdout); + c = getchar(); + (void) putchar(c); + (void) fflush(stdout); + while (getchar() != '\n') ; /* eat rest of line and newline */ + } + if(c == 'y' || c == 'Y') + if(eraseoldlocks()) + goto gotlock; + else { + unlock_file(HLOCK); + error("Couldn't destroy old game."); + } + else { + unlock_file(HLOCK); + error("%s", ""); + } + } + +gotlock: + fd = creat(fq_lock, FCMASK); + unlock_file(HLOCK); + if(fd == -1) { + error("cannot creat lock file (%s).", fq_lock); + } else { + if(write(fd, (genericptr_t) &hackpid, sizeof(hackpid)) + != sizeof(hackpid)){ + error("cannot write lock (%s)", fq_lock); + } + if(close(fd) == -1) { + error("cannot close lock (%s)", fq_lock); + } + } +} + +void +regularize(s) /* normalize file name - we don't like .'s, /'s, spaces */ +register char *s; +{ + register char *lp; + + while((lp=index(s, '.')) || (lp=index(s, '/')) || (lp=index(s,' '))) + *lp = '_'; +#if defined(SYSV) && !defined(AIX_31) && !defined(SVR4) && !defined(LINUX) && !defined(__APPLE__) + /* avoid problems with 14 character file name limit */ +# ifdef COMPRESS + /* leave room for .e from error and .Z from compress appended to + * save files */ + { +# ifdef COMPRESS_EXTENSION + int i = 12 - strlen(COMPRESS_EXTENSION); +# else + int i = 10; /* should never happen... */ +# endif + if(strlen(s) > i) + s[i] = '\0'; + } +# else + if(strlen(s) > 11) + /* leave room for .nn appended to level files */ + s[11] = '\0'; +# endif +#endif +} + +#if defined(TIMED_DELAY) && !defined(msleep) && defined(SYSV) +#include + +void +msleep(msec) +unsigned msec; /* milliseconds */ +{ + struct pollfd unused; + int msecs = msec; /* poll API is signed */ + + if (msecs < 0) msecs = 0; /* avoid infinite sleep */ + (void) poll(&unused, (unsigned long)0, msecs); +} +#endif /* TIMED_DELAY for SYSV */ + +#ifdef SHELL +int +dosh() +{ + register char *str; + if(child(0)) { + if((str = getenv("SHELL")) != (char*)0) + (void) execl(str, str, (char *)0); + else + (void) execl("/bin/sh", "sh", (char *)0); + raw_print("sh: cannot execute."); + exit(EXIT_FAILURE); + } + return 0; +} +#endif /* SHELL */ + +#if defined(SHELL) || defined(DEF_PAGER) || defined(DEF_MAILREADER) +int +child(wt) +int wt; +{ + register int f; + suspend_nhwindows((char *)0); /* also calls end_screen() */ +#ifdef _M_UNIX + sco_mapon(); +#endif +#ifdef __linux__ + linux_mapon(); +#endif + if((f = fork()) == 0){ /* child */ + (void) setgid(getgid()); + (void) setuid(getuid()); +#ifdef CHDIR + (void) chdir(getenv("HOME")); +#endif + return(1); + } + if(f == -1) { /* cannot fork */ + pline("Fork failed. Try again."); + return(0); + } + /* fork succeeded; wait for child to exit */ + (void) signal(SIGINT,SIG_IGN); + (void) signal(SIGQUIT,SIG_IGN); + (void) wait( (int *) 0); +#ifdef _M_UNIX + sco_mapoff(); +#endif +#ifdef __linux__ + linux_mapoff(); +#endif + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#ifdef WIZARD + if(wizard) (void) signal(SIGQUIT,SIG_DFL); +#endif + if(wt) { + raw_print(""); + wait_synch(); + } + resume_nhwindows(); + return(0); +} +#endif + +#ifdef GETRES_SUPPORT + +extern int FDECL(nh_getresuid, (uid_t *, uid_t *, uid_t *)); +extern uid_t NDECL(nh_getuid); +extern uid_t NDECL(nh_geteuid); +extern int FDECL(nh_getresgid, (gid_t *, gid_t *, gid_t *)); +extern gid_t NDECL(nh_getgid); +extern gid_t NDECL(nh_getegid); + +int +(getresuid)(ruid, euid, suid) +uid_t *ruid, *euid, *suid; +{ + return nh_getresuid(ruid, euid, suid); +} + +uid_t +(getuid)() +{ + return nh_getuid(); +} + +uid_t +(geteuid)() +{ + return nh_geteuid(); +} + +int +(getresgid)(rgid, egid, sgid) +gid_t *rgid, *egid, *sgid; +{ + return nh_getresgid(rgid, egid, sgid); +} + +gid_t +(getgid)() +{ + return nh_getgid(); +} + +gid_t +(getegid)() +{ + return nh_getegid(); +} + +#endif /* GETRES_SUPPORT */ diff --git a/sys/vms/Install.vms b/sys/vms/Install.vms new file mode 100644 index 0000000..289bd42 --- /dev/null +++ b/sys/vms/Install.vms @@ -0,0 +1,479 @@ + Instructions for Installing NetHack 3.4.3 + on a VMS (aka OpenVMS) system + ========================================= + +0. Please read this entire file before trying to build or install + NetHack, then read it again! + +1. Building NetHack requires a C compiler (either Compaq C, DEC C, + VAX C, or GNU C) and VMS version V4.6 or later (but see note #9). + This release has been tested with Compaq C V6.4 on Alpha/VMS V7.3-1 + and with VAX C V3.2 and GNU C 2.7.1 on VAX/VMS V5.5-2. The build + procedure (vmsbuild.com) should not need to be modified; it accepts + an option for selecting the compiler, and it can detect different + versions which might require specific command qualifiers. Versions + of VAXC earlier than V2.3 will produce many warning messages (about + 200 per source file; over to 25,000 total!), but NetHack has been + verified to compile, link, and execute correctly when built with VAXC + V2.2 using vmsbuild.com. There is also a set of Makefiles suitable for + use with MMS or MMK; they may or may not work with other make utilities. + +2. Make sure all the NetHack files are in the appropriate directory + structure. You should set up a directory--referred to as "top" below + and in some of the assorted files, but which may be a subdirectory-- + that has these subdirectories + [.dat] -- data files + [.doc] -- documentation files + [.include] -- C header files + [.src] -- primary source files + [.sys] -- parent for [.sys.*] + [.sys .share] -- files shared by several ports, including VMS + [.sys .vms] -- VMS-specific source and support files + [.util] -- sources for essential utility programs + [.win] -- parent for [.win.*] + [.win .tty] -- "window" routines for ordinary terminals + (including terminal windows on workstations) + The following subdirectories may be present, but are not useful for + building NetHack on VMS and are not required: + [.sys .amiga] -- AmigaDOS + [.sys .atari] -- Atari TOS + [.sys .be] -- BeBox BeOS + [.sys .mac] -- Macintosh + [.sys .msdos] -- MSDOS for IBM PCs and compatibles + [.sys .os2] -- OS/2 + [.sys .share .sounds] -- AIFF format audio files + [.sys .unix] -- guess :-) + [.sys .wince] -- Windows CE + [.sys .wince .ceinc] -- more WinCE + [.sys .wince .ceinc .sys] -- ditto + [.sys .winnt] -- Windows NT + [.win .gem] -- window routines for Atari/GEM + [.win .gnome] -- window routines for Unix/GNOME + [.win .Qt] -- window routines for Qt + [.win .share] -- "tile" graphic support + [.win .win32] -- Windows NT and Windows CE + [.win .X11] -- window routines for X-Windows; requires X11R4 + or later and MIT's Athena Widget set + You must arrange things in this structure or the supplied procedures + and instructions in this file will not work properly. Several DCL + command files are present in the [.sys.vms] subdirectory and won't + work as intended if they're moved elsewhere. The file called Files + in the top directory contains lists of everything that should be in + each subdirectory, including things that are constructed as NetHack + is being built. + +3. Prior to beginning compilation, go to the [.include] subdirectory and + edit vmsconf.h according to its comments. You should set Local_WIZARD + and Local_HACKDIR to appropriate values, and you might want to define + TEXTCOLOR if you have any color VAXstations or color terminals which + handle ANSI-format escape sequences to set foreground and background + color for text characters. (VT241/VT340 color graphics won't work.) + Other things which may be of interest are SECURE if you intend to + set up NetHack as an installed image which is granted privileges, and + SHELL which should be disabled if you intend to allow captive accounts + to run NetHack. You may also want to edit file config.h, but that's + only necessary if you want or need to disable some of the game options. + The distributed copy of config.h will work successfully on VMS; + vmsconf.h has conditional code to deal with the UNIX-specific items. + +4. If you have the programming utilities lex or flex and yacc or bison, + you may edit the procedure [.sys.vms]spec_lev.com and execute it to + process several source files for NetHack's special level and dungeon + compilers. If you don't modify spec_lev.com, it will copy some + pre-processed versions of the appropriate files (dgn_lex.c, lev_lex.c, + dgn_yacc.c, lev_yacc.c, dgn_comp.h, and lev_comp.h) from [.sys.share] + into [.util]*.c and [.include]*.h. + $ @[.SYS.VMS]SPEC_LEV ![OPTIONAL] + If you perform this step, do it prior to executing vmsbuild.com; if + you don't perform this step, vmsbuild.com will do so for you. + +5. To build NETHACK.EXE and its auxiliary programs, execute the + following DCL command: + $ @[.SYS.VMS]VMSBUILD !defaults to CC, either VAXC or DECC + or $ @[.SYS.VMS]VMSBUILD "GNUC" !force "GCC" + It can take quite a bit of time for a full build to complete. + vmsbuild.com will display some feedback as it executes; generally + this will be the name of each source file that's about to be compiled + or the name of the executable that has just been linked. + +6. If you have already started (or finished) a build and decide to start + over with a different compiler, you should DELETE [.SRC]CRTL.OPT;* + first. + +7. After compilation, it's time to perform installation. Go back to + the top directory. Either edit [.sys.vms]install.com to indicate + where you want everything to be installed, or specify the location + and "playground" owner on the command line. Then execute either + $ @[.SYS.VMS]INSTALL + or $ @[.SYS.VMS]INSTALL location owner + where location is a device:[directory] specification and owner is + either a rights identifier or UIC. If install.com is not modified + and if values aren't supplied on the command line, the default values + used are the translation of logical name HACKDIR, if any, or else + [.PLAY] (relative to the current directory), and the UIC for the + current process. install.com will use the auxiliary programs + constructed by vmsbuild.com to process quite a few data files in the + [.dat] subdirectory. Then it will create the playground directory, + if necessary, plus the associated [.save] subdirectory. Next it will + copy the data files into the playground; this step can take a while. + Finally it will copy nethack.exe and a few additional support files. + + After it completes, the files [.src]nethack.olb, [.src]nethack.exe, + [.util]*.obj, [.util]*_comp.exe, and [.util]makedefs.exe can be + deleted in order to save disk space if desired. The other program, + [.util]recover.exe, should not be deleted unless you make a copy of + it somewhere--perhaps in the playground directory--first. It can be + used to resurrect some games disrupted by system or program crash. + +8. The file nethack.com which is copied to the playground directory can + be used to invoke NetHack, or nethack.exe can be run directly. Most + of the command-line options specified in the Unix man-page (file + [.doc]nethack.txt) are also applicable to VMS. Some comments at the + beginning of nethack.com illustrate several of the options. New + players should read the file "Guidebook.txt" which will be copied + into the playground directory as "Guidebook.doc". + + +Notes: + +1. Save files and bones files from versions 3.4.0, 3.4.1 and 3.4.2 will + work with 3.4.3; those from earlier versions will not. The scoreboard + file (RECORD) from 3.4.x or 3.3.x will also work; one from version + 3.2.x is slightly different format but should be compatible. + +2. To specify user-preference options in your environment, define the + logical name NETHACKOPTIONS to have the value of a quoted string + containing a comma separated list of option values. The option names + are case-insensitive. + $ define nethackoptions "noAutoPickup,Dog:Rover,Cat:Felix,DECgraphics" + One value you'll probably want to specify is "noLegacy" to turn off + the initial introductory passage. The "checkpoint" option controls + whether or not enough data is saved to disk so that the set of level + files left behind after a crash contains sufficient information for + recover.exe to be able to construct a save file after the fact. The + tradeoff for enabling checkpoint is that using it makes level changes + do more I/O and take longer. The "menustyle" option controls some + aspects of the user interface, and can be set to "menustyle:traditional" + to make nethack behave more like older versions. + + If logical name or DCL symbol NETHACKOPTIONS is not defined, NetHack + will try HACKOPTIONS instead. Regardless of whether or not either + is defined, it will also try to find a configuration file containing + additional option settings. If the value of the translation of + NETHACKOPTIONS--or HACKOPTIONS--begins with an "@" character then the + rest of the translation is assumed to be the name of the configuration + file. Otherwise, the following are tried: file specified by logical + name NETHACKINI, file SYS$LOGIN:NETHACK.INI, and file HOME:NETHACK.CNF + (note that the C run-time library sets up the value of HOME to match + sys$login). Syntax for the configuration file is similar to + NETHACKOPTIONS, but multiple lines can be used, each must start with + OPTIONS=, and comments can be included by placing '#' in the first + column. Several options which take more complex values (graphics + representation) can also be present; see the "Guidebook" for details. + (Guidebook.txt can be found in the [.doc] subdirectory; a copy gets + placed in the playground directory by install.com. Also, an example + configuration file can be found in [.win.X11]nethack.rc.) + +3. Instead of using vmsbuild.com to compile and link everything, you can + use the set of Makefiles found in the vms subdirectory, provided you + have an appropriate and compatible make utility. They've been tested + using MMK, a freeware clone of Digital's MMS. There are five of them, + and the suffix or filetype on their names indicates where they should + be placed. + $ copy [.sys.vms]Makefile.top []Makefile. + $ copy [.sys.vms]Makefile.src [.src]Makefile. + $ copy [.sys.vms]Makefile.utl [.util]Makefile. + $ copy [.sys.vms]Makefile.dat [.dat]Makefile. + $ copy [.sys.vms]Makefile.doc [.doc]Makefile. + After doing that, edit [.src]Makefile and [.util]Makefile to specify + pertinent compiler options in CFLAGS, linker options in LFLAGS, and + libraries in LIBS and/or MORELIBS if the default values aren't right. + Be sure to make compatible compilation and linking settings in both + files. While in there, edit [.util]Makefile to specify the appropriate + values for lex and yacc, _or_ move to that directory and use MMS or + make to build targets no_lex and no_yacc which will copy several + pre-processed files from [.sys.share] into [.util]. Finally, edit + Makefile in the top directory to specify values for GAMEDIR and + GAMEOWNER. This top Makefile invokes [.sys.vms]install.com to do + much of the actual installation work, so if you want to make any + customizations or file protection changes, edit install.com to suit. + Also set MAKE in all of the Makefiles to the appropriate command if + not using MMS or MMK. + + Once the Makefiles are tailored for your site, give the command + $ mms all,install + or $ make all install + To compile and install everything. The object files compiled via + the Makefiles are left as individual .OBJ files rather than placed + into an object library (in contrast to step #7 above and note #10 + below). These Makefiles are provided on an as-is basis; vmsbuild.com + is the preferred way to compile because it's guaranteed to compile + and link everything. + +4. termcap is an ASCII data file containing descriptions of terminal + capabilities and the escape sequences that software must use to take + advantage of them. If you do not already have a termcap file in use + on your system there is a small one in file [.SYS.SHARE]TERMCAP. It + contains definitions for common Digital terminals, also suitable for + most clones and emulators. This file is copied into the playground + by install.com, and NetHack will use it if it can't find any other + one. NetHack uses the following sequence to attempt to locate the + termcap file: translation of the logical name TERMCAP (used as-is), + file NETHACKDIR:TERMCAP, similar file HACKDIR:TERMCAP, GNU-Emacs file + EMACS_LIBRARY:[ETC]TERMCAP.DAT, file []TERMCAP, and lastly file + $TERMCAP (which most likely would be a logical name). If NetHack + can't find the termcap file, or if the above search sequence finds a + different one than you'd prefer, then use the DCL ASSIGN or DEFINE + command to define a value for logical name TERMCAP. + + NetHack also tries fairly hard to figure out what kind of terminal + you're using. It checks for logical names (or symbols) NETHACK_TERM, + HACK_TERM, EMACS_TERM, and lastly TERM. The last is set up by the + C run-time library and you cannot use a logical name or symbol for + it. If all those fail, or if whichever one succeeds has a value of + "undefined" or "unknown" (which can happen under VMS V5.4-* and + V5.5-* for VT420 terminals), NetHack will query the VMS TERMTABLE + database used by the SMG library routines. Whatever value NetHack + eventually comes up with needs to be the name of an entry in the + termcap file, otherwise a message about "Unknown terminal type" will + be printed and NetHack will exit. + +5. NetHack contains code which attempts to make it secure in case it's + installed with privileges (to allow the playground to be protected + against world write access). This has only undergone limited testing, + so install NetHack with privileges at your own risk. If you discover + any potential security holes, please let us know so that we can take + steps to correct the problem(s). NetHack always includes filename + punctuation when accessing files, so that it should never be affected + by inadvertent or malicious logical name definitions, and it always + deactivates installed privileges prior to spawning a subprocess. + + Note to end users: "installing with privileges" is an option for + system managers who set up system-wide access to the game. Since + CMKRNL privilege and modification of the system boot routines are + both required, it is not an option for ordinary users. There are + no explicit instructions on how to do such an installation, because + only system managers who are already familiar with the process and + its potential security ramifications should even consider it. + + The default setup by install.com assumes no privileges and uses + world-writable files to allow arbitrary users to play. This is + NOT secure and not advisable in any environment where there are + untrustworthy users, but works fine for many sites. If you allow + users to run NetHack from captive accounts (VMS 5.1-* or earlier) + or from restricted accounts (5.2 and later), you should either make + sure that they do not have TMPMBX privilege or else disable NetHack's + ability to spawn an interactive subprocess. To disable subprocesses, + disable the "!" (shell escape) command by commenting out the definition + of SHELL in vmsconf.h prior to building the program. This necessity + may be removed in some future release, where NetHack will check for + captive accounts instead of spawning unconditionally. Note that + disabling the SHELL command also prevents spawning MAIL when scrolls + of new mail are received. + + In order for installed privileges to be used at all, the value of + HACKDIR (via Local_HACKDIR in vmsconf.h) compiled into the program + must correspond to the actual playground directory. If logical name + HACKDIR (or NETHACKDIR) is used to override that value, installed + privileges will be deactivated unless its value corresponds to the + same device and directory as the internal value. If that internal + value contains a logical name, only an executive-mode translation + will be honored; if there is no such translation, installed privs + will be deactivated. + + To be able to install nethack.exe with privileges (SYSPRV or GRPPRV, + perhaps EXQUOTA, depending on site usage and needs), you'll need to + link it with debugging and tracebacks both disabled. You can do this + by specifying an argument to vmsbuild.com when performing step #6 + above; pass it "/noTrace/noDebug" as the 4th parameter. + $ @[.SYS.VMS]VMSBUILD "" "" "" "/noTrace/noDebug" + /Trace/noDebug is the linker's normal default. If you've already + built NetHack, you can relink with tracebacks disabled by doing + $ @[.SYS.VMS]VMSBUILD "LINK" "" "" "/noTrace/noDebug" + +6. If you can't or won't install nethack.exe with privileges and if you + don't have access to a privileged account yourself, then if you intend + to allow other users to access your copy of NetHack you should probably + place an ACL on the playground directory and its save subdirectory. + The access control list should contain a default protection ACE which + grants delete+control access to the playground owner (ie, your own + account if there's no special games account involved). install.com + does not attempt to do this automatically at the present time. After + executing install.com to create the playground directory, perform a + pair of commands similar to the following + $ SET ACL/ACL=(IDENT=your_id, OPTIONS=DEFAULT, ACCESS=R+W+E+D+C) - + $_ device:[playground's.parent.directory]playground.DIR + $ SET ACL/ACL=(IDENT=your_id, OPTIONS=DEFAULT, ACCESS=R+W+E+D+C) - + $_ device:[playground.directory]SAVE.DIR + The two commands use the same options, but SET ACL won't accept a + list of files to modify. (For recent versions of VMS, SET ACL was + made obsolete in favor of SET FILE/ACL, which in turn has been made + obsolete in favor of SET SECURITY/CLASS=FILE/ACL; however, the older + forms will still work.) 'your_id' should be the rights identifier + which corresponds to the account which should retain access to those + files; 'device:[playground's.parent.directory]' is the name of the + parent directory for the playground (ie, if your playground directory + is disk$foo:[me.games.nethack.play], then you want to specify + disk$foo:[me.games.nethack]play.dir on the SET ACL command), and + 'device:[playground.directory]' is the playground itself. Those ACLs + establish a default protection scheme such that every newly created + file in those directories will have an ACL attached to it, and the + attached ACL will grant 'your_id' full access to the corresponding + file. That should allow you to clear away level files from aborted + games, and to delete old save files if necessary. It will not enable + you to run recover.exe on behalf of other users, because you won't be + able to create files owned by them unless you have elevated privileges. + +7. Many NetHack commands can be aborted by sending it the + character when it wants input. This is displayed as ESC inside the + game. Digital VK201 keyboards (used by VT2xx and VT3xx and older + VAXstations) and VK401 keyboards (used by VT4xx, newer VAXstations, + and DEC's X Terminals) do not have an key. They may + transmit for the key if the terminal or emulator + window is set to operate in VT100 mode, or there may be a setup-type + option for making the <` | ~> key behave as . If your + terminal does not have that, or if it's set to a mode where that + won't work, then just use instead. (Press the "[" key while + holding down the "Ctrl" key, then release both; and + have the same ASCII code and are indistinguishable once they reach + the computer; note that VAXstations and X Terminals _can_ tell the + difference, but that won't matter for NetHack.) + + VMS NetHack is configured to use the SYS$QIOW system service for + reading characters from the keyboard. This allows ^C and ^Y (as well + as ^X and ^O for wizard mode debugging) to be used as commands without + being intercepted or interpreted by the terminal driver. The code + which parses arrow and function keys is not perfect, and it's possible + to get strange results if you hold such keys down or just type too + quickly, particularly on slow multiplexor lines. Those keys are + never needed in actual play, and most function keys are just treated + as for use in aborting partial commands. + + VMS NetHack also still has code to use SMG$READ_KEYSTROKE instead. + That can be activated by modifying vmsconf.h and recompiling, but + it should never be necessary. If you use it, you'll need to press + either or twice to abort partial commands, or else + press an arbitrary function key, such as , once. + + If SUSPEND is defined in vmsconf.h, is used for that command. + Since Unix-style job control is not available, it's used for connecting + to the parent process if NetHack is running in a subprocess. When not + in a subprocess, it doesn't do anything except give a message to the + effect that it's not doing anything.... The suspend command does not + save the current game; if you use ^Z to attach to your parent process, + be sure to remember to eventually reattach to the NetHack subprocess; + otherwise the game in progress won't get saved when you logout. + +8. NetHack optionally maintains a logfile which receives one line appended + to it whenever a game ends. This can be disabled entirely by adding + an "#undef LOGFILE" directive to vmsconf.h prior to building the + program, or it can be disabled later by removing the file(s) LOGFILE.;* + from the playground directory. If not disabled prior to compilation, + the logfile can be reinitialized by simply creating an empty file + named LOGFILE in the playground, but make sure that users are able + to write into it, or new entries will not be appended. + +9. Some attempt at support for VMS versions earlier than V4.6 has been + included, but no such obsolete system was available for testing it. + vmsbuild.com detects the need for the extra support routines and + arranges automatically for them to be compiled. The reason that + special support is needed is that the C Run-Time Library (VAXCRTL) + underwent a major revision for VMS V4.6 and several routines which + NetHack utilizes were not available prior to that upgrade. + +10. vmsbuild.com collects almost all of the object files (xxx.OBJ) into + an object library (NETHACK.OLB) as it compiles the source files. + This should prevent the quota-exceeded problems from the linker + that some sites have reported for prior versions. Note that if you + compile any source files manually, you'll need to replace those + modules in the object library prior to linking the program: + $ cc/include=[-.include] [-.sys.vms]vmstty !for example + $ libr/obj []nethack vmstty !replace VMSTTY + $ @[-.sys.vms]vmsbuild LINK !re-link NETHACK.EXE + If you forget to replace the library entry, your newly compiled code + will not be included in the new executable image. + +11. To access "wizard mode"--intended for debugging purposes, not to + spoil the game with unlimited wishes--you must be running from the + username compiled into the game via Local_WIZARD in vmsconf.h, and + you must specify "-D" on the command line when invoking NetHack. + Note that -D must be uppercase, and it must be in quotes to prevent + the C run-time library's program startup code from converting it into + lowercase. + $ @hackdir:nethack "-D" + Any character name you specify will be ignored in favor of "wizard". + +12. At program startup time, NetHack uses the empty file PERM to prevent + two different processes from using the same character name (under the + same UIC ownership) at the same time. It does this by temporarily + giving that file a second directory entry named PERM.LOCK, then + removing the alternate entry once started. If the PERM file is + missing or inaccessible, NetHack will give a message and then quit. + Several possible messages and their usual causes are: + Can't find file perm;1 to lock! + PERM.;1 is missing from the playground directory. Fix: reinstall + the playground directory using install.com, or use CREATE or an editor + to make an empty file named PERM. Version number must be 1. + Can't lock perm;1 due to directory protection. + The playground directory is not allowing write access. Fix: players + need to be able to write files for dungeon levels and "bones" into + the playground directory. Set the protection or ACL on the xxx.DIR;1 + file in the playground's parent directory to allow write access. + Can't unlink perm.lock;1. + The empty file PERM.;1 is protected against delete access; only matters + under some versions of VMS. Fix: set the protection or ACL on PERM.;1 + to allow delete access to players. Under VMS V5.5-2, delete access is + not necessary. PERM does not have to remain writable. + Waiting for access to perm;1. (# retries left). + If some other process is also starting up NetHack at about the same + time, you may have to wait a short period. NetHack will retry once + per second, counting down to 0. If 0 is reached, the message + Perhaps there is an old perm.lock;1 around? + will be displayed and then NetHack will give up. Fix: to forcibly + remove a stale PERM.LOCK entry, issue the following command + $ SET FILE/REMOVE PERM.LOCK;1 + from the playground directory. The file PERM should remain intact. + Do not use that command for real files, only alternate directory + entries. If output from a DIRECTORY command on the playground reports + PERM.LOCK;1 no such file + then someone has deleted PERM.;1 while the synonym entry was still + in place, and PERM.LOCK was left as a dangling name which no longer + points at any file. The SET FILE/REMOVE command above will fix the + dangling name; a new PERM.;1 will need to be created as mentioned above. + + In similar fashion, synchronized access to the scoreboard file RECORD + is accomplished using temporary entry RECORD.LOCK and LOGFILE using + entry LOGFILE.LOCK. + +13. Unless you have both Motif and the Athena Widget set from MIT, you + will not be able to use the X11 interface on VMS. Even if you do + have both those things, such a configuration has not been tested and + there are no provisions for it in vmsbuild.com. Makefile.src does + have the extra source files listed, but not the necessary libraries. + + The X11 port will not compile and link with DECwindows, but it will + be able to display on a VMS DECwindows X server provided that it and + its Unix X client have a compatible transport between them (either + TCP/IP added to VMS or DECnet added to Unix) and session security + is set up appropriately. You'll need to add the contents of file + [.win.X11]NetHack.ad into your DECW$USER_DEFAULTS:DECW$XDEFAULTS.DAT, + and modify some of the lines. The DECwindows window manager does not + support having input focus automatically follow the pointer, so you + should uncomment the "NetHack*autofocus" resource line. (For Motif + this may not be necessary, depending on customization options.) + Uncommenting the "NetHack*slow" line is highly recommended. You'll + also need to set "NetHack*fonts: fixed" (rather than "variable"), and + either set the map font to "fixed" too or install the "nh10" font + that comes in file [.win.X11]nh10.bdf. If NetHack warns that the map + font is variable, then something isn't set up properly. + + After creating or modifying decw$xdefaults.dat, you must restart the + window manager in order for any changes to take effect; it's easiest + to just make the session manager quit and then log in again. + +14. If necessary, send problem reports via e-mail to + + Always include version information for NetHack, the operating system, + and the C compiler used. + +20-OCT-2003 diff --git a/sys/vms/Makefile.dat b/sys/vms/Makefile.dat new file mode 100644 index 0000000..d2ee9a3 --- /dev/null +++ b/sys/vms/Makefile.dat @@ -0,0 +1,137 @@ +# NetHack Makefile (VMS) - data files: special levels and other data. +# SCCS Id: @(#)Makefile.dat 3.4 2002/03/02 + +# Copy this file to [.dat]Makefile.; no editing needed. + +MAKE = $(MMS) +CD = set default +ECHO = write sys$output +NOOP = continue # don't do anything interesting +RUN = mcr # simplest way to pass command line args +TOUCH = append/New _NLA0: # only one file per $(TOUCH) +# support directories, relative to each other and to 'src' +DAT = [-.dat] +UTL = [-.util] +WINSHR = [-.win.share] +WINX11 = [-.win.X11] +# utilities; must match Makefile.utl in spelling and punctuation +MAKEDEFS = $(UTL)makedefs.exe; +LEVCOMP = $(UTL)lev_comp.exe; +DGNCOMP = $(UTL)dgn_comp.exe; +DLB = $(UTL)dlb.exe; +TILE2X11 = $(UTL)tile2x11.exe; +UTILMARKER = $(UTL)util.timestamp; + +# note: filespecs have enough punctuation to satisfy DELETE +MARKERS = spec_levs.timestamp;,quest_levs.timestamp; +VARDAT = data.;,rumors.;,quest.dat;,oracles.;,options.; +DUNGEON = dungeon.; +X11TILES= x11tiles.; +# note: the level lists need to be space separated +QUESTLEVS = Arch.des Barb.des Caveman.des Healer.des Knight.des \ + Monk.des Priest.des Ranger.des Rogue.des Samurai.des Tourist.des \ + Valkyrie.des Wizard.des +SPECLEVS = bigroom.des castle.des endgame.des gehennom.des knox.des \ + medusa.des mines.des oracle.des sokoban.des tower.des yendor.des + +all : $(VARDAT) $(DUNGEON) $(MARKERS) $(DLB) + @ $(ECHO) "data files are up to date." + +# these are convenience targets for "manual" interactive use +spec_levs : spev_levs.timestamp + @ $(ECHO) "special levels are up to date." +quest_levs : quest_levs.timestamp + @ $(ECHO) "quest levels are up to date." +dungeon : $(DUNGEON) + @ $(ECHO) "dungeon is up to date." +data : data.; + @ $(NOOP) +rumors : rumors.; + @ $(NOOP) +quest.dat : quest.dat; + @ $(NOOP) +oracles : oracles.; + @ $(NOOP) +options : options.; + @ $(NOOP) +x11tiles : $(X11TILES) + @ $(NOOP) + +$(MAKEDEFS) : $(UTILMARKER) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(MAKEDEFS) + @ $(CD) $(DAT) + +$(DGNCOMP) : $(UTILMARKER) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(DGNCOMP) + @ $(CD) $(DAT) + +$(LEVCOMP) : $(UTILMARKER) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(LEVCOMP) + @ $(CD) $(DAT) + +$(DLB) : $(UTILMARKER) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(DLB) + @ $(CD) $(DAT) + +$(TILE2X11) : $(UTILMARKER) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(TILE2X11) + @ $(CD) $(DAT) + +$(X11TILES) : $(TILE2X11) \ + $(WINSHR)monsters.txt $(WINSHR)objects.txt $(WINSHR)other.txt + $(RUN) $(TILE2X11) \ + $(WINSHR)monsters.txt $(WINSHR)objects.txt $(WINSHR)other.txt + +pet_mark.xbm : $(WINX11)pet_mark.xbm + copy $(WINX11)pet_mark.xbm pet_mark.xbm + +rip.xpm : $(WINX11)rip.xpm + copy $(WINX11)rip.xpm rip.xpm + + +data.; : data.base $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -d + +rumors.; : rumors.tru rumors.fal $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -r + +quest.dat; : quest.txt $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -q + +oracles.; : oracles.txt $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -h + +# note: 'options' should have already been made when include/date.h was created +options.; : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -v + +spec_levs.timestamp; : $(SPECLEVS) $(LEVCOMP) + $(RUN) $(LEVCOMP) $(SPECLEVS) + $(TOUCH) spec_levs.timestamp; + +quest_levs.timestamp; : $(QUESTLEVS) $(LEVCOMP) + $(RUN) $(LEVCOMP) $(QUESTLEVS) + $(TOUCH) quest_levs.timestamp; + +$(DUNGEON) : dungeon.def $(MAKEDEFS) $(DGNCOMP) + $(RUN) $(MAKEDEFS) -e !dungeon.def -> dungeon.pdf + $(RUN) $(DGNCOMP) dungeon.pdf !dungeon.pdr -> dungeon + +clean : + - if f$search("*.*;-1").nes."" then purge + - if f$search("dungeon.pdf").nes."" then delete dungeon.pdf; + - if f$search("*.timestamp").nes."" then delete $(MARKERS) + +spotless : clean + - delete $(VARDAT) + - if f$search("$(DUNGEON)").nes."" then delete $(DUNGEON) + - if f$search("*.lev").nes."" then delete *.lev; + - if f$search("$(X11TILES)").nes."" then delete $(X11TILES) + - if f$search("*.x%m").nes."" then delete *.x%m; !*.xbm,*.xpm + - if f$search("nh*.dlb").nes."" then delete nh*.dlb; + - if f$search("nhdat.lst").nes."" then delete nhdat.lst; diff --git a/sys/vms/Makefile.doc b/sys/vms/Makefile.doc new file mode 100644 index 0000000..1de8642 --- /dev/null +++ b/sys/vms/Makefile.doc @@ -0,0 +1,68 @@ +# NetHack Makefile (VMS) - for the [Unix] documentation. +# SCCS Id: @(#)Makefile.doc 3.4 1993/01/06 + +# Copy this file to [.doc]Makefile. and edit it if needed. + +GUIDEBOOK = Guidebook. # regular ASCII file +#GUIDEBOOK = Guidebook.ps # PostScript file +#GUIDEBOOK = Guidebook.dvi # TeX device-independent file + +ALLDOCS = $(GUIDEBOOK) +#ALLDOCS = $(GUIDEBOOK) manpages + +NOOP = ! + +Guidebook : $(GUIDEBOOK) + $(NOOP) + +# the basic guidebook +#Guidebook. : Guidebook.mn +# #tbl tmac.n Guidebook.mn | nroff | col > Guidebook +# write sys$output "Guidebook.mn cannot be processed under VMS." +Guidebook. : Guidebook.txt # distributed version of plain text + copy Guidebook.txt Guidebook. + +# Fancier output for those with ditroff, psdit and a PostScript printer. +#Guidebook.ps : Guidebook.mn +# #tbl tmac.n Guidebook.mn | ditroff | psdit > Guidebook.ps +# write sys$output "Guidebook.mn cannot be processed under VMS." +Guidebook.ps : Guidebook.dvi # generated with LaTeX + dvi2ps Guidebook + +# Guidebook.tex is the same as Guidebook.mn but formatted with LaTeX. +# - The invocation command for LaTeX may vary in different installations. +# - To print Guidebook.dvi you need to use a suitable dvi-driver. +Guidebook.dvi : Guidebook.tex + latex Guidebook.tex + +all : $(ALLDOCS) + $(NOOP) + +GAME = nethack +MANDIR = HACKDIR: +MANEXT = man +#MANDIR = /usr/man/man6 +#MANEXT = 6 + +# manual non-installation; raw man pages may be better than nothing +GAMEMANCREATE = copy nethack.6 +LEVMANCREATE = copy lev_comp.6 +DGNMANCREATE = copy dgn_comp.6 +RCVRMANCREATE = copy recover.6 +# GAMEMANCREATE = nroff -man nethack.6 > +# LEVMANCREATE = nroff -man lev_comp.6 > +# DGNMANCREATE = nroff -man dgn_comp.6 > +# RCVRMANCREATE = nroff -man recover.6 > + +manpages : + - $(GAMEMANCREATE) $(MANDIR)$(GAME).$(MANEXT) + - $(LEVMANCREATE) $(MANDIR)lev_comp.$(MANEXT) + - $(DGNMANCREATE) $(MANDIR)dgn_comp.$(MANEXT) + - $(RCVRMANCREATE) $(MANDIR)recover.$(MANEXT) + +spotless : + - if f$search("Guidebook.") .nes."" then delete Guidebook.;* + - if f$search("Guidebook.ps") .nes."" then delete Guidebook.ps;* + - if f$search("Guidebook.dvi").nes."" then delete Guidebook.dvi;* + - if f$search("Guidebook.aux").nes."" then delete Guidebook.aux;* + - if f$search("Guidebook.log").nes."" then delete Guidebook.log;* diff --git a/sys/vms/Makefile.src b/sys/vms/Makefile.src new file mode 100644 index 0000000..fc96b1b --- /dev/null +++ b/sys/vms/Makefile.src @@ -0,0 +1,463 @@ +# NetHack Makefile (VMS) - for building nethack itself. +# SCCS Id: @(#)Makefile.src 3.4 2003/02/13 + +# Copy this file to [.src]Makefile. and then edit it as needed. +# The default configuration is for building with DEC C (aka Compaq C). +# If you changed CC or CFLAGS, make similar changes in [.util]Makefile. +# +# Note: modifying this Makefile will cause crtl.opt to be rebuilt, +# which will trigger an update of makedefs, which will in turn +# result in a full build of just about _everything_. + +MAKE = $(MMS) +CD = set default +ECHO = write sys$output +NOOP = continue +RUN = mcr +TOUCH = append/New _NLA0: # only one file per $(TOUCH) +# source tree, relative to 'src' and 'util' +INC = [-.include] +SYSSHR = [-.sys.share] +SRC = [-.src] +TTY = [-.win.tty] +UTL = [-.util] +VMS = [-.sys.vms] +WINSHR = [-.win.share] +X11 = [-.win.X11] + +MAKEFILE= $(SRC)Makefile. + +# if you are using gcc as your compiler: +# uncomment the CC definition below if it's not in your environment +# CC = gcc + +# set option flags for C compiler and linker +# +CFLAGS = /Prefix=All/Incl=$(INC)/noList # DECC in native mode +#CFLAGS = /Include=$(INC)/noList # VAXC or GNUC +#LFLAGS = /Debug/Map/Cross_Ref # for development +#LFLAGS = /noTraceback/noMap # for installing w/ privs +LFLAGS = /noMap +LINK = link + +LIBS = # blank for DECC +#LIBS = sys$share:vaxcrtl.exe/Shareable # VAX C or GNU C +MORELIBS = +# GCC needs an extra library +#MORELIBS = gnu_cc:[000000]gcclib.olb/Library + +# Specific VMS object files +SYSSRC = $(VMS)vmsmain.c,$(VMS)vmstty.c,$(VMS)vmsunix.c,\ + $(VMS)vmsmisc.c,$(VMS)vmsfiles.c,$(VMS)vmsmail.c +SYSOBJ = vmsmain.obj,vmstty.obj,vmsunix.obj,vmsfiles.obj,vmsmail.obj #,vmsmisc.obj +LIBOPT = $(SRC)crtl.opt; + +# termcap library +TERMCAPSRC = tclib.c +TERMCAPOBJ = ,tclib.obj + +# Set WINSRC and WINOBJ lines corresponding to your desired combination +# of windowing systems. Also set windowing systems in config.h. +# +# a straight tty port using no native windowing system +WINTTYSRC = $(TTY)getline.c $(TTY)termcap.c $(TTY)topl.c $(TTY)wintty.c \ + $(TERMCAPSRC) +WINTTYOBJ = getline.obj,termcap.obj,topl.obj,wintty.obj $(TERMCAPOBJ) +# +# an X11 port (not supported under DECwindows) +WINX11SRC = $(X11)Window.c $(X11)dialogs.c $(X11)winX.c $(X11)winmap.c \ + $(X11)winmenu.c $(X11)winmesg.c $(X11)winmisc.c $(X11)winstat.c \ + $(X11)wintext.c $(X11)winval.c $(SRC)tile.c +WINX11OBJ = Window.obj,dialogs.obj,winX.obj,winmap.obj,winmenu.obj,\ + winmesg.obj,winmisc.obj,winstat.obj,wintext.obj,winval.obj,tile.obj +# +# +WINSRC = $(WINTTYSRC) +WINOBJ = $(WINTTYOBJ) + +# make NetHack for VMS +SYSTEM = SysVMS.timestamp; +GAME = $(SRC)nethack.exe; + +# RANDOM is defined in vmsconf.h +RANDSRC = random.c +RANDOBJ = random.obj + +# ---------------------------------------- +# +# Nothing below this line should have to be changed. +# +# Other things that have to be reconfigured are in vmsconf.h, +# and config.h + +VERSION = 3.4.3 + +MAKEDEFS = $(UTL)makedefs.exe; + +# timestamp files to reduce `make' overhead and shorten .obj dependency lists +CONFIG_H = $(SRC)config.h-t +HACK_H = $(SRC)hack.h-t + +# all .c that are part of the main NetHack program and are not operating- or +# windowing-system specific +HACKCSRC = allmain.c alloc.c apply.c artifact.c attrib.c ball.c bones.c \ + botl.c cmd.c dbridge.c decl.c detect.c dig.c display.c dlb.c do.c \ + do_name.c do_wear.c dog.c dogmove.c dokick.c dothrow.c drawing.c \ + dungeon.c eat.c end.c engrave.c exper.c explode.c extralev.c \ + files.c fountain.c hack.c hacklib.c invent.c light.c lock.c mail.c \ + makemon.c mapglyph.c mcastu.c mhitm.c mhitu.c minion.c mklev.c mkmap.c \ + mkmaze.c mkobj.c mkroom.c mon.c mondata.c monmove.c monst.c \ + mplayer.c mthrowu.c muse.c music.c o_init.c objects.c objnam.c \ + options.c pager.c pickup.c pline.c polyself.c potion.c pray.c \ + priest.c quest.c questpgr.c read.c rect.c region.c restore.c rip.c rnd.c \ + role.c rumors.c save.c shk.c shknam.c sit.c sounds.c sp_lev.c spell.c \ + steal.c steed.c teleport.c timeout.c topten.c track.c trap.c u_init.c \ + uhitm.c vault.c version.c vision.c weapon.c were.c wield.c \ + windows.c wizard.c worm.c worn.c write.c zap.c + +# generated source files (tile.c is handled separately via WINxxxSRC) +GENCSRC = monstr.c vis_tab.c #tile.c + +# .c files for this version (for date.h) +VERSOURCES = $(HACKCSRC) $(SYSSRC) $(WINSRC) $(RANDSRC) $(GENCSRC) + +# all .h files except date.h, onames.h, pm.h, and vis_tab.h which would +# cause dependency loops if run through "make depend" +# and dgn_comp.h, dgn_file.h, lev_comp.h, special level & dungeon files. +# +HACKINCL = align.h amiconf.h artifact.h artilist.h attrib.h beconf.h color.h \ + config.h config1.h coord.h decl.h def_os2.h display.h dlb.h \ + dungeon.h edog.h emin.h engrave.h epri.h eshk.h extern.h flag.h \ + func_tab.h global.h hack.h lev.h macconf.h mfndpos.h micro.h \ + mkroom.h monattk.h mondata.h monflag.h monst.h monsym.h obj.h \ + objclass.h os2conf.h patchlevel.h pcconf.h permonst.h prop.h rect.h \ + region.h rm.h sp_lev.h spell.h system.h tcap.h timeout.h tosconf.h \ + tradstdc.h trampoli.h trap.h unixconf.h vault.h vision.h vmsconf.h \ + wintty.h winX.h winprocs.h wintype.h you.h youprop.h + +#HSOURCES = $(HACKINCL) date.h onames.h pm.h vis_tab.h\ +# lev_comp.h dgn_comp.h dgn_file.h + +# the following .obj's should be made before any others (for makedefs) +FIRSTOBJ = vmsmisc.obj,monst.obj,objects.obj + +# split up long list so that we can write pieces of it into nethack.opt +HOBJ1 = allmain.obj,alloc.obj,apply.obj,artifact.obj,attrib.obj, \ + ball.obj,bones.obj,botl.obj,cmd.obj,dbridge.obj,decl.obj, \ + detect.obj,dig.obj,display.obj,dlb.obj,do.obj,do_name.obj,do_wear.obj +HOBJ2 = dog.obj,dogmove.obj,dokick.obj,dothrow.obj,drawing.obj, \ + dungeon.obj,eat.obj,end.obj,engrave.obj,exper.obj,explode.obj, \ + extralev.obj,files.obj,fountain.obj,hack.obj,hacklib.obj,invent.obj +HOBJ3 = light.obj,lock.obj,mail.obj,makemon.obj,mapglyph.obj,mcastu.obj, \ + mhitm.obj,mhitu.obj,minion.obj,mklev.obj,mkmap.obj,mkmaze.obj, \ + mkobj.obj,mkroom.obj,mon.obj,mondata.obj,monmove.obj,monstr.obj +HOBJ4 = mplayer.obj,mthrowu.obj,muse.obj,music.obj,o_init.obj,objnam.obj, \ + options.obj,pager.obj,pickup.obj,pline.obj,polyself.obj, \ + potion.obj,pray.obj,priest.obj,quest.obj,questpgr.obj,read.obj +HOBJ5 = rect.obj,region.obj,restore.obj,rip.obj,rnd.obj,role.obj, \ + rumors.obj,save.obj,shk.obj,shknam.obj,sit.obj,sounds.obj,sp_lev.obj, \ + spell.obj,steal.obj,steed.obj,teleport.obj,timeout.obj,topten.obj, \ + track.obj,trap.obj +HOBJ6 = u_init.obj,uhitm.obj,vault.obj,vision.obj,vis_tab.obj,weapon.obj, \ + were.obj,wield.obj,windows.obj,wizard.obj,worm.obj,worn.obj, \ + write.obj,zap.obj,version.obj +HOBJ = $(FIRSTOBJ) $(SYSOBJ) $(WINOBJ) $(RANDOBJ) \ + $(HOBJ1) $(HOBJ2) $(HOBJ3) $(HOBJ4) $(HOBJ5) $(HOBJ6) + +# simpler target name +nethack : $(GAME) + @ $(ECHO) "nethack is up to date." + +$(GAME) : $(SYSTEM) + @ $(NOOP) + +$(SYSTEM) : $(LIBOPT) $(HOBJ) nethack.opt + @ $(ECHO) "Linking ..." + $(LINK)/Exe=$(GAME) $(LFLAGS) nethack.opt/Opt,$(LIBOPT)/Opt + $(TOUCH) $(SYSTEM) + +all : $(GAME) + @ $(ECHO) "nethack is up to date." + +# linker options file for nethack's object modules +nethack.opt : $(MAKEFILE) # this file + open/Write f nethack.opt + write f "! nethack.opt" + @ write f f$edit("$(SYSOBJ)","COLLAPSE") + @ write f f$edit("$(WINOBJ)","COLLAPSE") + @ write f f$edit("$(RANDOBJ)","COLLAPSE") + @ write f f$edit("$(FIRSTOBJ)","COLLAPSE") + @ write f f$edit("$(HOBJ1)","COLLAPSE") + @ write f f$edit("$(HOBJ2)","COLLAPSE") + @ write f f$edit("$(HOBJ3)","COLLAPSE") + @ write f f$edit("$(HOBJ4)","COLLAPSE") + @ write f f$edit("$(HOBJ5)","COLLAPSE") + @ write f f$edit("$(HOBJ6)","COLLAPSE") + @ write f "iosegment=128" + write f "identification=$(VERSION)" + close f + +# linker options file for run-time libraries, also used by $(UTL)Makefile +$(LIBOPT) : $(MAKEFILE) # this file + open/Write f $(LIBOPT) + write f "! crtl.opt" + write f "$(LIBS)" + write f "$(MORELIBS)" + close f +# simplified target name, for interactive convenience +crtl.opt : $(LIBOPT) + @ $(NOOP) + +# dependencies for makedefs and its outputs, which the util +# Makefile is responsible for keeping up to date +# + +# special rules, to force update of makedefs, real dependencies should be +# below in the 'make depend' output. +monst.obj : + $(CC) $(CFLAGS) monst.c + @- if f$search("$(MAKEDEFS)").nes."" then delete $(MAKEDEFS) + +objects.obj : + $(CC) $(CFLAGS) objects.c + @- if f$search("$(MAKEDEFS)").nes."" then delete $(MAKEDEFS) + +$(MAKEDEFS) : $(FIRSTOBJ) $(UTL)makedefs.c \ + $(CONFIG_H) $(INC)permonst.h $(INC)objclass.h \ + $(INC)monsym.h $(INC)artilist.h $(INC)dungeon.h \ + $(INC)obj.h $(INC)monst.h $(INC)you.h $(INC)flag.h \ + $(INC)dlb.h $(INC)patchlevel.h $(INC)qtext.h $(LIBOPT) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(MAKEDEFS) + @ $(CD) $(SRC) +$(INC)onames.h : $(MAKEDEFS) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(INC)onames.h + @ $(CD) $(SRC) +$(INC)pm.h : $(MAKEDEFS) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(INC)pm.h + @ $(CD) $(SRC) +monstr.c : $(MAKEDEFS) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(SRC)monstr.c + @ $(CD) $(SRC) +# both vis_tab.h and vis_tab.c are made at the same time by makedefs +$(INC)vis_tab.h : vis_tab.c + $(TOUCH) $(INC)vis_tab.h +vis_tab.c : $(MAKEDEFS) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(SRC)vis_tab.c + @ $(CD) $(SRC) +$(SRC)tile.c : $(WINSHR)tilemap.c $(HACK_H) + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(SRC)tile.c + @ $(CD) $(SRC) + +# date.h should be remade any time any of the source or include code +# is modified. Unfortunately, this would make the contents of this +# file far more complex. Since "hack.h" depends on most of the include +# files, we kludge around this by making date.h dependent on hack.h, +# even though it doesn't include this file. +# +# hack.h depends on makedefs' output, so we know makedefs will be +# up to date before being executed; kill old date.h to force update +$(INC)date.h : $(VERSOURCES) $(HACK_H) + @- if f$search("$(INC)date.h").nes."" then delete $(INC)date.h;* + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) $(INC)date.h + @ $(CD) $(SRC) + +# special targets +clean : + - if f$search("*.*;-1") .nes."" then purge + - if f$search("$(INC)*.*;-1").nes."" then purge $(INC) /Exclude=*conf*.h + - if f$search("*.obj") .nes."" then delete *.obj; + - if f$search("*.h-t").nes."" then delete *.h-t; !$(HACK_H),$(CONFIG_H) + - if f$search("*.opt").nes."" then delete *.opt; !nethack.opt,$(LIBOPT) + +spotless : clean + - if f$search("$(LIBOPT)").nes."" then delete $(LIBOPT) + - if f$search("$(SYSTEM)").nes."" then delete $(SYSTEM) + - if f$search("$(GAME)") .nes."" then delete $(GAME) + - delete monstr.c;,vis_tab.c;,$(INC)vis_tab.h;,\ + $(INC)pm.h;,$(INC)onames.h;,$(INC)date.h; + - if f$search("tile.c") .nes."" then delete tile.c; + - if f$search("tclib.c") .nes."" then delete tclib.c; + - if f$search("random.c") .nes."" then delete random.c; + - if f$search("nethack.olb").nes."" then delete nethack.olb; + +# dependencies (mostly cloned from sys/unix/Makefile.src) +# config.h timestamp +$(CONFIG_H) : $(INC)config.h $(INC)config1.h $(INC)tradstdc.h $(INC)global.h \ + $(INC)coord.h $(INC)vmsconf.h $(INC)system.h \ + $(INC)unixconf.h $(INC)os2conf.h $(INC)micro.h \ + $(INC)pcconf.h $(INC)tosconf.h $(INC)amiconf.h \ + $(INC)macconf.h $(INC)beconf.h $(INC)wceconf.h \ + $(INC)ntconf.h $(INC)nhlan.h + $(TOUCH) $(CONFIG_H) +# hack.h timestamp +$(HACK_H) : $(INC)hack.h $(CONFIG_H) $(INC)align.h \ + $(INC)dungeon.h $(INC)monsym.h $(INC)mkroom.h \ + $(INC)objclass.h $(INC)youprop.h $(INC)prop.h \ + $(INC)permonst.h $(INC)monattk.h \ + $(INC)monflag.h $(INC)mondata.h $(INC)pm.h \ + $(INC)wintype.h $(INC)decl.h $(INC)quest.h \ + $(INC)spell.h $(INC)color.h $(INC)obj.h \ + $(INC)you.h $(INC)attrib.h $(INC)monst.h $(INC)skills.h \ + $(INC)onames.h $(INC)timeout.h $(INC)trap.h \ + $(INC)flag.h $(INC)rm.h $(INC)vision.h \ + $(INC)display.h $(INC)engrave.h $(INC)rect.h $(INC)region.h \ + $(INC)winprocs.h $(INC)wintty.h $(INC)trampoli.h + $(TOUCH) $(HACK_H) +# VMS-specific code +vmsmain.obj : $(VMS)vmsmain.c $(HACK_H) $(INC)dlb.h +vmstty.obj : $(VMS)vmstty.c $(HACK_H) $(INC)wintty.h $(INC)tcap.h +vmsunix.obj : $(VMS)vmsunix.c $(HACK_H) +vmsmisc.obj : $(VMS)vmsmisc.c $(VMS)oldcrtl.c +vmsfiles.obj : $(VMS)vmsfiles.c $(CONFIG_H) +vmsmail.obj : $(VMS)vmsmail.c $(CONFIG_H) $(INC)mail.h \ + $(INC)wintype.h $(INC)winprocs.h +# conditionally used code -- VMS always wants these +random.obj : random.c $(HACK_H) +random.c : $(SYSSHR)random.c + copy $(SYSSHR)random.c random.c +tclib.obj : tclib.c $(CONFIG_H) +tclib.c : $(SYSSHR)tclib.c + copy $(SYSSHR)tclib.c tclib.c +# user interface code -- VMS uses tty (1st 4) only +getline.obj : $(TTY)getline.c $(HACK_H) $(INC)func_tab.h +termcap.obj : $(TTY)termcap.c $(HACK_H) $(INC)tcap.h +topl.obj : $(TTY)topl.c $(HACK_H) $(INC)tcap.h +wintty.obj : $(TTY)wintty.c $(HACK_H) $(INC)dlb.h \ + $(INC)patchlevel.h $(INC)tcap.h +Window.obj : $(X11)Window.c $(INC)xwindowp.h $(INC)xwindow.h $(CONFIG_H) +dialogs.obj : $(X11)dialogs.c $(CONFIG_H) +winX.obj : $(X11)winX.c $(HACK_H) $(INC)winX.h $(INC)dlb.h \ + $(INC)patchlevel.h $(X11)nh72icon $(X11)nh56icon $(X11)nh32icon +winmap.obj : $(X11)winmap.c $(INC)xwindow.h $(HACK_H) $(INC)dlb.h \ + $(INC)winX.h $(INC)tile2x11.h +winmenu.obj : $(X11)winmenu.c $(HACK_H) $(INC)winX.h +winmesg.obj : $(X11)winmesg.c $(INC)xwindow.h $(HACK_H) $(INC)winX.h +winmisc.obj : $(X11)winmisc.c $(HACK_H) $(INC)func_tab.h $(INC)winX.h +winstat.obj : $(X11)winstat.c $(HACK_H) $(INC)winX.h +wintext.obj : $(X11)wintext.c $(HACK_H) $(INC)winX.h $(INC)xwindow.h +winval.obj : $(X11)winval.c $(HACK_H) $(INC)winX.h +tile.obj : $(SRC)tile.c $(HACK_H) +monstr.obj : monstr.c $(CONFIG_H) +vis_tab.obj : vis_tab.c $(CONFIG_H) $(INC)vis_tab.h +# general code +allmain.obj : allmain.c $(HACK_H) +alloc.obj : alloc.c $(CONFIG_H) +apply.obj : apply.c $(HACK_H) $(INC)edog.h +artifact.obj : artifact.c $(HACK_H) $(INC)artifact.h $(INC)artilist.h +attrib.obj : attrib.c $(HACK_H) +ball.obj : ball.c $(HACK_H) +bones.obj : bones.c $(HACK_H) $(INC)lev.h +botl.obj : botl.c $(HACK_H) +cmd.obj : cmd.c $(HACK_H) $(INC)func_tab.h +dbridge.obj : dbridge.c $(HACK_H) +decl.obj : decl.c $(HACK_H) +detect.obj : detect.c $(HACK_H) $(INC)artifact.h +dig.obj : dig.c $(HACK_H) $(INC)edog.h +display.obj : display.c $(HACK_H) +dlb.obj : dlb.c $(CONFIG_H) $(INC)dlb.h +do.obj : do.c $(HACK_H) $(INC)lev.h +do_name.obj : do_name.c $(HACK_H) +do_wear.obj : do_wear.c $(HACK_H) +dog.obj : dog.c $(HACK_H) $(INC)edog.h +dogmove.obj : dogmove.c $(HACK_H) $(INC)mfndpos.h $(INC)edog.h +dokick.obj : dokick.c $(HACK_H) $(INC)eshk.h +dothrow.obj : dothrow.c $(HACK_H) $(INC)edog.h +drawing.obj : drawing.c $(HACK_H) $(INC)tcap.h +dungeon.obj : dungeon.c $(HACK_H) $(INC)dgn_file.h $(INC)dlb.h +eat.obj : eat.c $(HACK_H) +end.obj : end.c $(HACK_H) $(INC)eshk.h $(INC)dlb.h +engrave.obj : engrave.c $(HACK_H) $(INC)lev.h +exper.obj : exper.c $(HACK_H) +explode.obj : explode.c $(HACK_H) +extralev.obj : extralev.c $(HACK_H) +files.obj : files.c $(HACK_H) $(INC)dlb.h +fountain.obj : fountain.c $(HACK_H) +hack.obj : hack.c $(HACK_H) +hacklib.obj : hacklib.c $(HACK_H) +invent.obj : invent.c $(HACK_H) +light.obj : light.c $(HACK_H) $(INC)lev.h +lock.obj : lock.c $(HACK_H) +mail.obj : mail.c $(HACK_H) $(INC)mail.h +makemon.obj : makemon.c $(HACK_H) $(INC)epri.h $(INC)emin.h $(INC)edog.h +mapglyph.obj : mapglyph.c $(HACK_H) +mcastu.obj : mcastu.c $(HACK_H) +mhitm.obj : mhitm.c $(HACK_H) $(INC)artifact.h $(INC)edog.h +mhitu.obj : mhitu.c $(HACK_H) $(INC)artifact.h $(INC)edog.h +minion.obj : minion.c $(HACK_H) $(INC)emin.h $(INC)epri.h +mklev.obj : mklev.c $(HACK_H) +mkmap.obj : mkmap.c $(HACK_H) $(INC)sp_lev.h +mkmaze.obj : mkmaze.c $(HACK_H) $(INC)sp_lev.h $(INC)lev.h +mkobj.obj : mkobj.c $(HACK_H) +mkroom.obj : mkroom.c $(HACK_H) +mon.obj : mon.c $(HACK_H) $(INC)mfndpos.h $(INC)edog.h +mondata.obj : mondata.c $(HACK_H) $(INC)eshk.h $(INC)epri.h +monmove.obj : monmove.c $(HACK_H) $(INC)mfndpos.h $(INC)artifact.h \ + $(INC)epri.h +monst.obj : monst.c $(CONFIG_H) $(INC)permonst.h $(INC)align.h \ + $(INC)monattk.h $(INC)monflag.h $(INC)monsym.h \ + $(INC)dungeon.h $(INC)eshk.h $(INC)vault.h \ + $(INC)epri.h $(INC)color.h +mplayer.obj : mplayer.c $(HACK_H) +mthrowu.obj : mthrowu.c $(HACK_H) +muse.obj : muse.c $(HACK_H) $(INC)edog.h +music.obj : music.c $(HACK_H) #interp.c +o_init.obj : o_init.c $(HACK_H) $(INC)lev.h +objects.obj : objects.c $(CONFIG_H) $(INC)obj.h $(INC)objclass.h \ + $(INC)prop.h $(INC)skills.h $(INC)color.h +objnam.obj : objnam.c $(HACK_H) +options.obj : options.c $(CONFIG_H) $(INC)objclass.h $(INC)flag.h \ + $(HACK_H) $(INC)tcap.h +pager.obj : pager.c $(HACK_H) $(INC)dlb.h +pickup.obj : pickup.c $(HACK_H) +pline.obj : pline.c $(HACK_H) $(INC)epri.h $(INC)edog.h +polyself.obj : polyself.c $(HACK_H) +potion.obj : potion.c $(HACK_H) +pray.obj : pray.c $(HACK_H) $(INC)epri.h +priest.obj : priest.c $(HACK_H) $(INC)mfndpos.h $(INC)eshk.h \ + $(INC)epri.h $(INC)emin.h +quest.obj : quest.c $(HACK_H) $(INC)qtext.h +questpgr.obj : questpgr.c $(HACK_H) $(INC)dlb.h $(INC)qtext.h +read.obj : read.c $(HACK_H) +rect.obj : rect.c $(HACK_H) +region.obj : region.c $(HACK_H) $(INC)lev.h +restore.obj : restore.c $(HACK_H) $(INC)lev.h $(INC)tcap.h +rip.obj : rip.c $(HACK_H) +rnd.obj : rnd.c $(HACK_H) +role.obj : role.c $(HACK_H) +rumors.obj : rumors.c $(HACK_H) $(INC)lev.h $(INC)dlb.h +save.obj : save.c $(HACK_H) $(INC)lev.h +shk.obj : shk.c $(HACK_H) $(INC)eshk.h +shknam.obj : shknam.c $(HACK_H) $(INC)eshk.h +sit.obj : sit.c $(HACK_H) $(INC)artifact.h +sounds.obj : sounds.c $(HACK_H) $(INC)edog.h +sp_lev.obj : sp_lev.c $(HACK_H) $(INC)dlb.h $(INC)sp_lev.h +spell.obj : spell.c $(HACK_H) +steal.obj : steal.c $(HACK_H) +steed.obj : steed.c $(HACK_H) +teleport.obj : teleport.c $(HACK_H) +timeout.obj : timeout.c $(HACK_H) $(INC)lev.h +topten.obj : topten.c $(HACK_H) $(INC)dlb.h $(INC)patchlevel.h +track.obj : track.c $(HACK_H) +trap.obj : trap.c $(HACK_H) +u_init.obj : u_init.c $(HACK_H) +uhitm.obj : uhitm.c $(HACK_H) +vault.obj : vault.c $(HACK_H) $(INC)vault.h +version.obj : version.c $(HACK_H) $(INC)date.h $(INC)patchlevel.h +vision.obj : vision.c $(HACK_H) $(INC)vis_tab.h +weapon.obj : weapon.c $(HACK_H) +were.obj : were.c $(HACK_H) +wield.obj : wield.c $(HACK_H) +windows.obj : windows.c $(HACK_H) $(INC)wingem.h $(INC)winGnome.h +wizard.obj : wizard.c $(HACK_H) $(INC)qtext.h $(INC)epri.h +worm.obj : worm.c $(HACK_H) $(INC)lev.h +worn.obj : worn.c $(HACK_H) +write.obj : write.c $(HACK_H) +zap.obj : zap.c $(HACK_H) +# eof diff --git a/sys/vms/Makefile.top b/sys/vms/Makefile.top new file mode 100644 index 0000000..1763def --- /dev/null +++ b/sys/vms/Makefile.top @@ -0,0 +1,147 @@ +# NetHack Makefile (VMS) - top level for making & installing everything. +# SCCS Id: @(#)Makefile.top 3.4 2003/05/19 + +# Copy this file to Makefile.; edit the appropriate values for +# GAMEDIR ("playground" location) and GAMEOWNER (UIC or identifier +# for the owner of playground files). + +# usage: mms all,install +# or mms no_tools,all,install +# or substitute freeware `MMK' for Digital's `MMS'. + +MAKE = $(MMS) +CD = set default +ECHO = write sys$output +EXEC = @ +NOOP = continue # don't do anything interesting +TOUCH = set file/truncate # multiple files per $(TOUCH), but no creation +# support directories, relative to 'top' +DAT = [.dat] +DOC = [.doc] +SRC = [.src] +TOP = [-] # relative to the others +UTL = [.util] +VMS = [.sys.vms] + +GAMEDIR = # defaults to [.play] +GAMEOWNER = # defaults to installer's UIC +# these are the distributed values in [.include]vmsconf.h +#GAMEDIR = DISK$USERS:[GAMES.NETHACK.3-4-2.PLAY] +#GAMEOWNER = NHWIZARD + +# just about everything, except installation +all : program utilities data dlb_data documentation + @ $(ECHO) "all code and data is now up to date." + +program : + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) all + @ $(CD) $(TOP) +utilities : + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) all + @ $(CD) $(TOP) +data : + $(CD) $(DAT) + $(MAKE)$(MAKEFLAGS) all + @ $(CD) $(TOP) +documentation : + $(CD) $(DOC) + $(MAKE)$(MAKEFLAGS) all + @ $(CD) $(TOP) + +install : program all_data make_directories create_writeable_files update + @ $(ECHO) "installation is now complete." + +# assume there're no active games in progress +update : place_readonly_files place_executable place_vms_support + @ open/Write f tmp-update.com; + @ write f "$ set noon" + @ write f "$ if p1.eqs."""" then p1 = f$trnlnm(""HACKDIR"")" + @ write f "$ if p1.eqs."""" then p1 = ""[.play]""" + @ write f "$ old_default = f$environ(""DEFAULT"")" + @ write f "$ set default 'p1'" + @ write f\ + "$ if f$search(""*.*;-2"").nes."""" then set file/prot=(s:rwed,o:rwed) *.*;-2" + @ write f\ + "$ if f$search(""*.*;-1"").nes."""" then set file/prot=(s:rwed,o:rwed) *.*;-1" + @ write f "$ if f$search(""*.*;-1"").nes."""" then purge" + @ write f "$! if f$search(""bones*.*"").nes."""" then $(TOUCH) bones*.*" + @ write f "$! if f$search(""[.save]*"").nes."""" then $(TOUCH) [.save]*" + @ write f "$ set default 'old_default'" + @ write f "$ exit" + @ close f + - $(EXEC)tmp-update.com; $(GAMEDIR) !purge old version + @ delete tmp-update.com; + @ $(ECHO) "playground files updated." + +Guidebook : + $(CD) $(DOC) + $(MAKE)$(MAKEFLAGS) Guidebook + @ $(CD) $(TOP) +manpages : + $(CD) $(DOC) + $(MAKE)$(MAKEFLAGS) manpages + @ $(CD) $(TOP) + +all_data : data dlb_data + @ $(NOOP) + +dlb_data : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" dlb + +make_directories : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" directories + +create_writeable_files : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" writeable_files + +place_readonly_files : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" readonly_files + +place_executable : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" executable + +place_vms_support : + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" termcap + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" procedure + $(EXEC)$(VMS)install.com "$(GAMEDIR)" "$(GAMEOWNER)" documentation + + +# 'make no_tools' should be done first if you don't have the appropriate +# tools to process the parser and scanner for the special level and +# dungeon compilers; doing so will copy distributed, pre-processed files +# from [.sys.share] to [.util]. If you _do_ have the tools, be sure to +# edit [.util]Makefile so that it uses the right ones. +no_tools : + $(CD) $(UTL) + $(MAKE)$(MAKEFLAGS) no_yacc + $(MAKE)$(MAKEFLAGS) no_lex + @ $(CD) $(TOP) + + +# 'make clean' removes all the .obj files, but leaves around all the executables +# and compiled data files. +clean : + $(CD) $(SRC) + - $(MAKE)$(MAKEFLAGS) clean + @ $(CD) $(TOP) + $(CD) $(UTL) + - $(MAKE)$(MAKEFLAGS) clean + @ $(CD) $(TOP) + +# 'make spotless' returns the source tree to near-distribution condition. +# it removes .obj files, executables, and compiled data files. +spotless : + $(CD) $(SRC) + - $(MAKE)$(MAKEFLAGS) spotless + @ $(CD) $(TOP) + $(CD) $(UTL) + - $(MAKE)$(MAKEFLAGS) spotless + @ $(CD) $(TOP) + $(CD) $(DAT) + - $(MAKE)$(MAKEFLAGS) spotless + @ $(CD) $(TOP) + $(CD) $(DOC) + - $(MAKE)$(MAKEFLAGS) spotless + @ $(CD) $(TOP) diff --git a/sys/vms/Makefile.utl b/sys/vms/Makefile.utl new file mode 100644 index 0000000..bc4e2e6 --- /dev/null +++ b/sys/vms/Makefile.utl @@ -0,0 +1,372 @@ +# NetHack Makefile (VMS) - for utility programs. +# SCCS Id: @(#)Makefile.utl 3.4 2002/03/02 + +# Copy this file to [.util]Makefile. and then edit it as needed. +# The default configuration is for building with DEC C (aka Compaq C). +# Settings for CC and CFLAGS ought to match the ones used in [.src]Makefile. + +MAKE = $(MMS) +CD = set default +ECHO = write sys$output +MOVE = rename/New # within same device only +MUNG = search/Exact/Match=NOR # to strip bogus #module directives +NOOP = continue +RM = delete/noConfirm +RUN = mcr # simplest way to pass command line args +TOUCH = append/New _NLA0: # only one file per $(TOUCH) +# source tree, relative to 'src' and 'util' +DAT = [-.dat] +INC = [-.include] +SYSSHR = [-.sys.share] +SRC = [-.src] +UTL = [-.util] +VMS = [-.sys.vms] +WINSHR = [-.win.share] +WINX11 = [-.win.X11] +# targets, with enough punctuation to keep MCR and DELETE happy +MAKEDEFS= $(UTL)makedefs.exe; +LEVCOMP = $(UTL)lev_comp.exe; +DGNCOMP = $(UTL)dgn_comp.exe; +DLB = $(UTL)dlb.exe; +RECOVER = $(UTL)recover.exe; +# used by $(DAT)Makefile for synchronization +MARKER = $(UTL)util.timestamp; + +# if you are using gcc as your compiler, +# uncomment the CC definition below if it's not in your environment +# CC = gcc + +CFLAGS = /Prefix=All/Incl=$(INC)/noList # DECC in native mode +#CFLAGS = /Include=$(INC)/noList # VAXC or GNUC +LFLAGS = /noMap +LIBS = $(SRC)crtl.opt/Options # run-time library(s) needed +LINK = link + +# If you don't have yacc, byacc, or bison or just don't want to run any of +# them, then make target "no_yacc" before trying to build lev_comp +# or dgn_comp. You won't be able to modify *_comp.y though. +# If you don't have lex or flex, then make target "no_lex" and leave +# *_comp.l alone. $(VMS)lev_lex.h will be used to work-around some +# suspect code included in the distributed copies of *_lex.c. +# If you do either of the above, the corresponding value of YACC and/or LEX +# below won't matter. +# +# Note: VMS POSIX V1.1 lex and yacc generate code which contains an +# invalid #module directive; it order to prevent warnings for CC or +# choking by GCC, the SEARCH command is used in an attempt to strip +# then out. Otherwise MMS would quit when making the affected targets. +# Each "munged" copy should be identical to its original if no #module +# directives are present. +# +# yacc/lex programs to use to generate *_comp.c, *_comp.h, and *_lex.c. +# choose xxxOUT that matches xxx tool's output +YACC = bison /Define +LEX = flex +#YACC = yacc -d +#LEX = lex +#YACC = posix/Run posix$bin:yacc. "-d +#LEX = posix/Run posix$bin:lex. " +# blank means foo.y -> foo_tab.c & foo_tab.h +YACCOUT = # bison +#YACCOUT = ytab # VMS POSIX +#YACCOUT = y_tab # DEC/Shell +LEXOUT = lexyy # flex +#LEXOUT = lex_yy # VMS POSIX + + +# Nothing below this line should have to be changed. + +# linker options file +LIBOPT = $(SRC)crtl.opt; + +# timestamps for primary header files, matching src/Makefile +CONFIG_H = $(SRC)config.h-t +HACK_H = $(SRC)hack.h-t + +# utility .c files +MAKESRC = makedefs.c +SPLEVSRC = lev_yacc.c lev_lex.c lev_main.c +DGNCOMPSRC = dgn_yacc.c dgn_lex.c dgn_main.c +RECOVSRC = recover.c +DLBSRC = dlb_main.c +UTILSRCS = $(MAKESRC) $(SPLEVSRC) $(DGNCOMPSRC) $(RECOVSRC) $(DLBSRC) panic.c + +# object files that provide access to NetHack's names +NAMEOBJ1 = $(SRC)monst.obj,$(SRC)objects.obj +NAMEOBJ2 = $(SRC)drawing.obj,$(SRC)decl.obj +NAMEOBJS = $(NAMEOBJ1),$(NAMEOBJ2) + +# object files for makedefs +MAKEOBJS = makedefs.obj,$(NAMEOBJ1) +VMSMAKEOBJS = $(SRC)vmsmisc.obj + +# object files for special levels compiler +SPLEVOBJS = lev_main.obj,lev_yacc.obj,lev_lex.obj,panic.obj,\ + $(SRC)alloc.obj,$(NAMEOBJS) +VMSSPLEVOBJS = $(SRC)vmsmisc.obj,$(SRC)vmsfiles.obj + +# object files for dungeon compiler +DGNCOMPOBJS = dgn_main.obj,dgn_yacc.obj,dgn_lex.obj,panic.obj,$(SRC)alloc.obj +VMSDGNCOBJS = $(SRC)vmsmisc.obj + +# object files for recovery utility +RECOVOBJS = recover.obj +VMSRECOBJS = $(SRC)vmsmisc.obj,$(SRC)vmsfiles.obj + +# object files for dlb utility +DLBOBJS = dlb_main.obj,panic.obj,$(SRC)alloc.obj,$(SRC)dlb.obj +VMSDLBOBJS = $(SRC)vmsmisc.obj,$(SRC)vmsfiles.obj + + +# fake target +default : + @ $(ECHO) "Oops! No target(s) specified...." + +all : $(MAKEDEFS) $(LEVCOMP) $(DGNCOMP) $(RECOVER) $(DLB) + @ $(ECHO) "util is up to date." + +# special targets for folks without yacc/bison and or lex/flex +no_yacc : + copy $(SYSSHR)%%%_yacc.c $(UTL) + copy $(SYSSHR)%%%_comp.h $(INC) + @ $(ECHO) "distributed yacc output (*_yacc.c) copied into place" +no_lex : + copy $(SYSSHR)%%%_lex.c $(UTL) + copy $(VMS)lev_lex.h $(UTL) + @ $(ECHO) "distributed lex output (*_lex.c) copied into place" + +# alternate target names for possible interactive use +makedefs : $(MAKEDEFS) + @ $(ECHO) "makedefs is up to date." +lev_comp : $(LEVCOMP) + @ $(ECHO) "lev_comp is up to date." +dgn_comp : $(DGNCOMP) + @ $(ECHO) "dgn_comp is up to date." +recover : $(RECOVER) + @ $(ECHO) "recover is up to date." +dlb : $(DLB) + @ $(ECHO) "recover is up to date." + +$(LIBOPT) : $(SRC)Makefile.; # linker options file + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) $(LIBOPT) + @ $(CD) $(UTL) + +# dependencies for makedefs +# +$(MAKEDEFS) : $(MAKEOBJS) $(VMSMAKEOBJS) $(LIBOPT) + $(LINK) $(LFLAGS) $(MAKEOBJS),$(VMSMAKEOBJS),$(LIBS) + @ $(TOUCH) $(MARKER) + +makedefs.obj : makedefs.c \ + $(CONFIG_H) $(INC)permonst.h $(INC)objclass.h \ + $(INC)monsym.h $(INC)artilist.h $(INC)dungeon.h \ + $(INC)obj.h $(INC)monst.h $(INC)you.h $(INC)flag.h \ + $(INC)dlb.h $(INC)patchlevel.h $(INC)qtext.h + +$(INC)onames.h : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -o +$(INC)pm.h : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -p +$(SRC)monstr.c : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -m +# both vis_tab.h and vis_tab.c are made at the same time by makedefs -z +$(INC)vis_tab.h : $(SRC)vis_tab.c + $(TOUCH) $(INC)vis_tab.h +$(SRC)vis_tab.c : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -z + +# the src Makefile is responsible for knowing when to call this, since +# it knows all about the main src and include files +$(INC)date.h : $(MAKEDEFS) + $(RUN) $(MAKEDEFS) -v + + +# dependencies for lev_comp +# +$(LEVCOMP) : $(SPLEVOBJS) $(VMSSPLEVOBJS) # $(LIBOPT) + $(LINK)/Exe=$(LEVCOMP) $(LFLAGS) $(SPLEVOBJS),$(VMSSPLEVOBJS),$(LIBS) + +lev_yacc.obj : $(HACK_H) $(INC)sp_lev.h lev_yacc.c + $(CC) $(CFLAGS) lev_yacc.c +lev_lex.obj : $(HACK_H) $(INC)lev_comp.h $(INC)sp_lev.h lev_lex.c + @ if f$search("lev_lex.h").nes."" then $(MOVE) lev_lex.h stdio.h + $(CC) $(CFLAGS) lev_lex.c + @ if f$search("stdio.h").nes."" then $(MOVE) stdio.h lev_lex.h +lev_main.obj : $(HACK_H) $(INC)sp_lev.h $(INC)tcap.h $(INC)date.h lev_main.c + $(CC) $(CFLAGS) lev_main.c +panic.obj : $(CONFIG_H) + $(CC) $(CFLAGS) panic.c + +$(INC)lev_comp.h : lev_yacc.c + $(TOUCH) $(INC)lev_comp.h + +lev_yacc.c : lev_comp.y + $(YACC) lev_comp.y + $(MUNG) 'f$parse("$(YACCOUT)","lev_comp_tab.c")' "#module" /Outp=lev_yacc.c + @ if f$search("''f$parse("$(YACCOUT)","lev_comp_tab.c")'").nes."" then \ + $(RM) 'f$parse("$(YACCOUT)","lev_comp_tab.c")' + $(MOVE) 'f$parse("$(YACCOUT)","lev_comp_tab.h")' $(INC)lev_comp.h + +lev_lex.c : lev_comp.l + $(LEX) lev_comp.l + $(MUNG) 'f$parse("$(LEXOUT)","lev_comp_lex.c")' "#module" /Outp=lev_lex.c + @ if f$search("''f$parse("$(LEXOUT)","lev_comp_lex.c")'").nes."" then \ + $(RM) 'f$parse("$(LEXOUT)","lev_comp_lex.c")' + + +# dependencies for dgn_comp +# +$(DGNCOMP) : $(DGNCOMPOBJS) $(VMSDGNCOBJS) # $(LIBOPT) + $(LINK)/Exe=$(DGNCOMP) $(LFLAGS) $(DGNCOMPOBJS),$(VMSDGNCOBJS),$(LIBS) + +dgn_yacc.obj : $(CONFIG_H) $(INC)dgn_file.h $(INC)date.h dgn_yacc.c + $(CC) $(CFLAGS) dgn_yacc.c +dgn_lex.obj : $(CONFIG_H) $(INC)dgn_comp.h $(INC)dgn_file.h dgn_lex.c + @ if f$search("lev_lex.h").nes."" then $(MOVE) lev_lex.h stdio.h + $(CC) $(CFLAGS) dgn_lex.c + @ if f$search("stdio.h").nes."" then $(MOVE) stdio.h lev_lex.h +dgn_main.obj : $(CONFIG_H) dgn_main.c + $(CC) $(CFLAGS) dgn_main.c + +$(INC)dgn_comp.h : dgn_yacc.c + $(TOUCH) $(INC)dgn_comp.h + +dgn_yacc.c : dgn_comp.y + $(YACC) dgn_comp.y + $(MUNG) 'f$parse("$(YACCOUT)","dgn_comp_tab.c")' "#module" /Outp=dgn_yacc.c + @ if f$search("''f$parse("$(YACCOUT)","dgn_comp_tab.c")'").nes."" then \ + $(RM) 'f$parse("$(YACCOUT)","dgn_comp_tab.c")' + $(MOVE) 'f$parse("$(YACCOUT)","dgn_comp_tab.h")' $(INC)dgn_comp.h + +dgn_lex.c : dgn_comp.l + $(LEX) dgn_comp.l + $(MUNG) 'f$parse("$(LEXOUT)","dgn_comp_lex.c")' "#module" /Outp=dgn_lex.c + @ if f$search("''f$parse("$(LEXOUT)","dgn_comp_lex.c")'").nes."" then \ + $(RM) 'f$parse("$(LEXOUT)","dgn_comp_lex.c")' + + +# dependencies for recover +# +$(RECOVER) : $(RECOVOBJS) $(VMSRECOBJS) # $(LIBOPT) + $(LINK) $(LFLAGS) $(RECOVOBJS),$(VMSRECOBJS),$(LIBS) + +recover.obj : $(CONFIG_H) recover.c + +# dependencies for dlb +# +$(DLB) : $(DLBOBJS) $(VMSDLBOBJS) # $(LIBOPT) + $(LINK)/Exe=$(DLB) $(LFLAGS) $(DLBOBJS),$(VMSDLBOBJS),$(LIBS) + +dlb_main.obj : $(CONFIG_H) $(INC)dlb.h dlb_main.c + +# dependencies and build rules for tile utilities +# +TILEMAP = $(UTL)tilemap.exe; +GIF2TXT = $(UTL)gif2txt.exe; +TXT2PPM = $(UTL)txt2ppm.exe; +TILE2X11 = $(UTL)tile2x11.exe; +TILEUTILS = $(TILEMAP),$(GIF2TXT),$(TXT2PPM),$(TILE2X11) +TEXTIO = $(UTL)tiletxt.obj,tiletext.obj,$(NAMEOBJS),$(SRC)vmsmisc.obj +GIFREADERS = gifread.obj,panic.obj,$(SRC)alloc.obj +PPMWRITERS = ppmwrite.obj,panic.obj,$(SRC)alloc.obj + +tileutils : $(TILEUTILS) + @ $(NOOP) + +$(GIF2TXT) : $(GIFREADERS) $(TEXTIO) + $(LINK)/Exe=$(GIF2TXT) $(LFLAGS) $(GIFREADERS),$(TEXTIO),$(LIBS) +$(TXT2PPM) : $(PPMWRITERS) $(TEXTIO) + $(LINK)/Exe=$(TXT2PPM) $(LFLAGS) $(PPMWRITERS),$(TEXTIO),$(LIBS) +$(TILE2X11) : tile2x11.obj $(TEXTIO) + $(LINK) $(LFLAGS) tile2x11.obj,$(TEXTIO),$(LIBS) +$(TILEMAP) : tilemap.obj $(SRC)vmsmisc.obj + $(LINK) $(LFLAGS) tilemap.obj,$(SRC)vmsmisc.obj,$(LIBS) + +$(SRC)tile.c : $(TILEMAP) + $(RUN) $(TILEMAP) +$(INC)tile.h : $(WINSHR)tile.h + copy $(WINSHR)tile.h $(INC)tile.h + +# Force an explicit directory prefix on tiletxt.obj so that we don't get +# unwanted "sticky defaults" when $(TEXTIO) is used in a comma-separated +# list on the link command line. +# +$(UTL)tiletxt.obj : $(HACK_H) $(WINSHR)tilemap.c + $(CC) $(CFLAGS) /Def=("TILETEXT")/Obj=$@ $(WINSHR)tilemap.c +tilemap.obj : $(HACK_H) $(WINSHR)tilemap.c +tiletext.obj : $(CONFIG_H) $(INC)tile.h $(WINSHR)tiletext.c +gifread.obj : $(CONFIG_H) $(INC)tile.h $(WINSHR)gifread.c +ppmwrite.obj : $(CONFIG_H) $(INC)tile.h $(WINSHR)ppmwrite.c +tile2x11.obj : $(HACK_H) $(INC)tile.h $(INC)tile2x11.h $(WINX11)tile2x11.c + + +# make sure object files from src are available when needed +# +$(SRC)alloc.obj : $(SRC)alloc.c $(CONFIG_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) alloc.obj + @ $(CD) $(UTL) + +$(SRC)monst.obj : $(SRC)monst.c $(CONFIG_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) monst.obj + @ $(CD) $(UTL) + +$(SRC)objects.obj : $(SRC)objects.c $(CONFIG_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) objects.obj + @ $(CD) $(UTL) + +$(SRC)decl.obj : $(SRC)decl.c $(HACK_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) decl.obj + @ $(CD) $(UTL) + +$(SRC)drawing.obj : $(SRC)drawing.c $(HACK_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) drawing.obj + @ $(CD) $(UTL) + +$(SRC)dlb.obj : $(SRC)dlb.c $(HACK_H) $(INC)dlb.h + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) dlb.obj + @ $(CD) $(UTL) + +# make sure hack.h dependencies get transitive information +$(HACK_H) : $(CONFIG_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) $(HACK_H) + @ $(CD) $(UTL) +$(CONFIG_H) : $(INC)config.h + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) $(CONFIG_H) + @ $(CD) $(UTL) + +# VMS specific dependencies +$(SRC)vmsmisc.obj : $(VMS)vmsmisc.c + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) vmsmisc.obj + @ $(CD) $(UTL) + +$(SRC)vmsfiles.obj : $(VMS)vmsfiles.c $(CONFIG_H) + $(CD) $(SRC) + $(MAKE)$(MAKEFLAGS) vmsfiles.obj + @ $(CD) $(UTL) + + +clean : + - if f$search("*.*;-1").nes."" then purge + - if f$search("*.obj") .nes."" then $(RM) *.obj; + +spotless : clean + - if f$search("%%%_lex.c") .nes."" then $(RM) %%%_lex.c; + - if f$search("%%%_yacc.c").nes."" then $(RM) %%%_yacc.c; + - if f$search("$(INC)%%%_comp.h").nes."" then $(RM) $(INC)%%%_comp.h;* + - if f$search("$(INC)tile.h").nes."" then $(RM) $(INC)tile.h;* + - if f$search("lev_lex.h") .nes."" then $(RM) lev_lex.h; + - if f$search("*tab.c") .nes."" then $(RM) *tab.c; + - if f$search("*.exe").nes."" then \ + $(RM) $(MAKEDEFS),$(LEVCOMP),$(DGNCOMP),$(RECOVER),$(DLB) + - if f$search("*.exe").nes."" then $(RM) $(TILEUTILS) + - if f$search("$(MARKER)").nes."" then $(RM) $(MARKER) diff --git a/sys/vms/install.com b/sys/vms/install.com new file mode 100644 index 0000000..68fa96f --- /dev/null +++ b/sys/vms/install.com @@ -0,0 +1,268 @@ +$ ! vms/install.com -- set up nethack 'playground' +$ ! +$ ! Use vmsbuild.com to create nethack.exe, makedefs, and lev_comp *first*. +$ ! +$ ! Edit this file to define gamedir & gameuic, or else invoke it with two +$ ! command line parameters, as in: +$ ! @[.sys.vms]install "disk$users:[games.nethack]" "games" +$ ! or @[.sys.vms]install "[-.play]" "[40,1]" +$ ! +$ ! default location is old playground, default owner is installer +$ gamedir = f$trnlnm("NETHACKDIR") !location of playground +$ if gamedir.eqs."" then gamedir = f$trnlnm("HACKDIR") +$ gameuic = f$user() !owner of playground +$ ! --- nothing below this line should need to be changed --- +$ if p1.nes."" then gamedir := 'p1' +$ if p2.nes."" then gameuic := 'p2' +$ +$ ! note: all filespecs contain some punctuation, +$ ! to avoid inadvertent logical name interaction +$ play_files = "PERM.,RECORD.,LOGFILE.,PANICLOG." +$ help_files = "HELP.,HH.,CMDHELP.,WIZHELP.,OPTHELP.,HISTORY.,LICENSE." +$ data_files = "DATA.,RUMORS.,ORACLES.,OPTIONS.,QUEST.DAT" +$ guidebook = "[.doc]Guidebook.txt" +$ invoc_proc = "[.sys.vms]nethack.com" +$ trmcp_file = "[.sys.share]termcap" +$ spec_files = "AIR.LEV,ASMODEUS.LEV,ASTRAL.LEV,BAALZ.LEV,BIGRM-%.LEV," - + + "CASTLE.LEV,EARTH.LEV,FAKEWIZ%.LEV,FIRE.LEV," - + + "JUIBLEX.LEV,KNOX.LEV,MEDUSA-%.LEV,MINEFILL.LEV," - + + "MINETN-%.LEV,MINEND-%.LEV,ORACLE.LEV,ORCUS.LEV," - + + "SANCTUM.LEV,SOKO%-%.LEV,TOWER%.LEV,VALLEY.LEV," - + + "WATER.LEV,WIZARD%.LEV" +$ spec_input = "bigroom.des castle.des endgame.des " - + + "gehennom.des knox.des medusa.des mines.des " - + + "oracle.des sokoban.des tower.des yendor.des" +$ qstl_files = "%%%-GOAL.LEV,%%%-FIL%.LEV,%%%-LOCA.LEV,%%%-STRT.LEV" +$ qstl_input = "Arch.des Barb.des Caveman.des Healer.des " - + + "Knight.des Monk.des Priest.des Ranger.des Rogue.des " - + + "Samurai.des Tourist.des Wizard.des Valkyrie.des" +$ dngn_files = "DUNGEON." +$ dngn_input = "dungeon.pdf" +$ dlb_files = help_files + "," + data_files + "," - + + spec_files + "," + qstl_files + "," + dngn_files +$ data_libry = "nh-data.dlb" +$ xtrn_files = "LICENSE.,HISTORY.,OPTIONS." +$ makedefs := $sys$disk:[-.util]makedefs +$ lev_comp := $sys$disk:[-.util]lev_comp +$ dgn_comp := $sys$disk:[-.util]dgn_comp +$ dlb := $sys$disk:[-.util]dlb +$ milestone = "write sys$output f$fao("" !5%T "",0)," +$ if p3.nes."" .and. f$edit(p4,"UPCASE").nes."VERBOSE" then milestone = "!" +$ echo = "write sys$output" +$ warn = echo !could be "write sys$error" +$! +$! make sure we've got a playground location +$ gamedir := 'gamedir' +$ if gamedir.eqs."" then gamedir = "[.play]" !last ditch default +$ gamedir = f$parse(gamedir,,,,"SYNTAX_ONLY") - ".;" +$ if gamedir.eqs."" then write sys$error "% must specify playground directory" +$ if gamedir.eqs."" then exit %x1000002C !ss$_abort +$ +$! +$! ['p3' is used in Makefile.top] +$ if p3.nes."" then goto make_'p3' +$ +$ milestone "" +$! +$make_data_plus_dlb: +$make_data: +$ ! start from a known location -- [.sys.vms] +$ set default 'f$parse(f$environment("PROCEDURE"),,,"DIRECTORY")' +$! generate miscellaneous data files +$ set default [-.-.dat] !move to data directory +$ milestone "(data)" +$ makedefs -d !data.base -> data +$ milestone "(rumors)" +$ makedefs -r !rumors.tru + rumors.fal -> rumors +$ milestone "(oracles)" +$ makedefs -h !oracles.txt -> oracles +$ milestone "(dungeon preprocess)" +$ makedefs -e !dungeon.def -> dungeon.pdf +$ milestone "(quest text)" +$ makedefs -q !quest.txt -> quest.dat +$ milestone "(special levels)" +$ lev_comp 'spec_input' !special levels +$ milestone "(quest levels)" +$ lev_comp 'qstl_input' !quest levels +$ milestone "(dungeon compile)" +$ dgn_comp 'dngn_input' !dungeon database +$ set default [-] !move up +$ if p3.nes."" .and. f$edit(p3,"UPCASE").nes."DATA_PLUS_DLB" then exit +$ +$make_dlb: +$ ! start from a known location -- [.sys.vms] +$ set default 'f$parse(f$environment("PROCEDURE"),,,"DIRECTORY")' +$! construct data library +$ set default [-.-.dat] !move to data directory +$ milestone "(dlb setup)" +$! since DLB doesn't support wildcard expansion and we don't have shell +$! file globbing, start by making a file listing its intended contents +$ create nhdat.lst +$ if f$search("nhdat.lst;-1").nes."" then - + purge/noConfirm/noLog nhdat.lst +$! an old data file might fool us later, so get rid of it +$ if f$search(data_libry).nes."" then - + delete/noConfirm/noLog 'data_libry';* +$ if f$trnlnm("PFILE$").nes."" then close/noLog pfile$ +$ open/Append pfile$ nhdat.lst +$ i = 0 +$dloop: +$ g = f$element(i,",",dlb_files) +$ if g.eqs."," then goto ddone +$ wild = f$locate("*",g).ne.f$locate("%",g) +$ fcnt = 0 +$floop: +$ f = f$search(g) +$ if f.eqs."" then goto fdone +$ fcnt = fcnt + 1 +$! strip device, directory, and version from name +$ f = f$parse(f,,,"NAME") + f$parse(f,,,"TYPE") +$! strip trailing dot, if present, and change case +$ f = f$edit(f + "#" - ".#" - "#","LOWERCASE") +$ if f$extract(3,1,f).eqs."-" then - !"xyz-foo.lev" -> "Xyz-foo.lev" + f = f$edit(f$extract(0,1,f),"UPCASE") + f$extract(1,255,f) +$ write pfile$ f +$ if wild then goto floop +$fdone: +$ if fcnt.eq.0 then warn "? no file(s) found for """,g,"""" +$ i = i + 1 +$ goto dloop +$ddone: +$ close pfile$ +$ milestone "(dlb create)" +$ dlb "-cfI" 'data_libry' nhdat.lst +$ set default [-] !move up +$ if p3.nes."" then exit +$ +$! +$! set up the playground and save directories +$ milestone "(directories)" +$make_directories: +$ srctree = f$environment("DEFAULT") +$ set default 'gamedir' +$ if f$parse("[-]").eqs."" then create/dir/log [-] !default owner & protection +$ if f$parse("[]" ).eqs."" then - !needs to be world writable + create/directory/owner='gameuic'/prot=(s:rwe,o:rwe,g:rwe,w:rwe)/log [] +$ if f$search("SAVE.DIR;1").eqs."" then - + create/directory/owner='gameuic'/prot=(s:rwe,o:rwe,g:rwe,w:rwe)/log - + [.SAVE]/version_limit=2 +$ set default 'srctree' +$ if p3.nes."" then exit +$! +$! create empty writeable files -- logfile, scoreboard, multi-user access lock +$! [if old versions are already present, validate and retain them if possible] +$make_writeable_files: +$ milestone "(writeable files)" +!-!$ create/owner='gameuic'/prot=(s:rwed,o:rwed,g:rwed,w:rwed) - +!-! 'gamedir''play_files' +$ i = 0 +$ploop: if f$trnlnm("PFILE$").nes."" then close/nolog pfile$ +$ f = f$element(i,",",play_files) +$ if f.eqs."," then goto pdone +$ i = i + 1 +$ f = gamedir + f +$ if f$search(f).eqs."" then goto pmake !make it if not found +$ if f$file_attrib(f,"RFM").nes."STMLF" then goto prej !must be stream_lf +$ open/read/error=prej pfile$ 'f' +$ read/end=ploop pfile$ pline !empty is ok +$ close pfile$ +$ pfield = f$element(0," ",pline) !1st field is version number +$ if f$locate(".",pfield).lt.f$length(pfield) then goto ploop !keep +$prej: rename/new_vers 'f' *.old !reject old version +$pmake: create/fdl=sys$input:/owner='gameuic' 'f'/log +file + organization sequential + protection (system:rwd,owner:rwd,group:rw,world:rw) +record + format stream_lf +$ goto ploop +$pdone: +$ if p3.nes."" then exit +$! +$! copy over the remaining game files, then make them readonly +$make_readonly_files: +$ milestone "(readonly files)" +$ if f$search("[.dat]''data_libry'").nes."" +$ then call copyfiles 'f$string(data_libry+","+xtrn_files)' [.dat] "r" +$ else !'dlb_files' is too long for a single command +$ k = 200 + f$locate(",",f$extract(200,999,dlb_files)) +$ call copyfiles 'f$extract(0,k,dlb_files)' [.dat] "r" +$ call copyfiles 'f$extract(k+1,999,dlb_files)' [.dat] "r" +$ endif +$ if p3.nes."" then exit +$! +$make_executable: +$ milestone "(nethack.exe)" +$ call copy_file [.src]nethack.exe 'gamedir'nethack.exe "re" +$ if p3.nes."" then exit +$! +$! provide invocation procedure (if available) +$make_procedure: +$ if f$search(invoc_proc).eqs."" then goto skip_dcl +$ if f$search("''gamedir'nethack.com").nes."" then - + if f$cvtime(f$file_attr("''gamedir'nethack.com","RDT")) - + .ges. f$cvtime(f$file_attr(invoc_proc,"RDT")) then goto skip_dcl +$ milestone "(nethack.com)" +$ call copy_file 'invoc_proc' 'gamedir'nethack.com "re" +$skip_dcl: +$ if p3.nes."" then exit +$! +$! provide plain-text Guidebook doc file (if available) +$make_documentation: +$ if f$search(guidebook).eqs."" then goto skip_doc +$ milestone "(Guidebook)" +$ call copy_file 'guidebook' 'gamedir'Guidebook.doc "r" +$skip_doc: +$ if p3.nes."" then exit +$! +$! provide last-resort termcap file (if available) +$make_termcap: +$ if f$search(trmcp_file).eqs."" then goto skip_termcap +$ if f$search("''gamedir'termcap").nes."" then goto skip_termcap +$ milestone "(termcap)" +$ call copy_file 'trmcp_file' 'gamedir'termcap "r" +$skip_termcap: +$ if p3.nes."" then exit +$! +$! done +$ milestone "" +$ define/nolog nethackdir 'gamedir' +$ define/nolog hackdir 'gamedir' +$ echo - + f$fao("!/ Nethack installation complete. !/ Playground is !AS !/",gamedir) +$ exit +$ +$! +$! copy one file, resetting the protection on an earlier version first +$copy_file: subroutine +$ if f$search(p2).nes."" then set file/Prot=(s:rwed,o:rwed) 'p2' +$ copy/Prot=(s:'p3'wd,o:'p3'wd,g:'p3',w:'p3') 'p1' 'p2' +$ set file/Owner='gameuic'/Prot=(s:'p3',o:'p3') 'p2' +$endsubroutine !copy_file +$ +$! +$! copy a comma-separated list of wildcarded files, one file at a time +$copyfiles: subroutine +$ i = 0 +$lloop: +$ g = f$element(i,",",p1) +$ if g.eqs."," then goto ldone +$ g = p2 + g +$ wild = f$locate("*",g).ne.f$locate("%",g) +$ fcnt = 0 +$eloop: +$ f = f$search(g) +$ if f.eqs."" then goto edone +$ fcnt = fcnt + 1 +$ f = f - f$parse(f,,,"VERSION") +$ e = f$parse(f,,,"NAME") + f$parse(f,,,"TYPE") +$ call copy_file 'f' 'gamedir''e' "''p3'" +$ if wild then goto eloop +$edone: +$ if fcnt.eq.0 then warn "? no file(s) found for """,g,"""" +$ i = i + 1 +$ goto lloop +$ldone: +$endsubroutine !copyfiles +$ +$! diff --git a/sys/vms/lev_lex.h b/sys/vms/lev_lex.h new file mode 100644 index 0000000..f001e57 --- /dev/null +++ b/sys/vms/lev_lex.h @@ -0,0 +1,25 @@ +/* SCCS Id: @(#)lev_lex.h 3.4 1999/08/08 */ +/* "vms/lev_lex.h" copied into "util/stdio.h" for use in *_lex.c only! + * This is an awful kludge to allow util/*_lex.c made by SunOS's `lex' + * to be compiled as is. (It isn't needed with `flex' or VMS POSIX + * `lex' and is benign when either of those configurations are used.) + * It works because the actual setup of yyin & yyout is performed in + * src/lev_main.c, where stdin & stdout are still correctly defined. + * + * The troublesome code is + * #include "stdio.h" + * ... + * FILE *yyin = stdin, *yyout = stdout; + * The file scope initializers with non-constant values require this + * hack, and the quotes instead of brackets makes it easy to do. + */ + +#include +#ifdef stdin +# undef stdin +#endif +#define stdin 0 +#ifdef stdout +# undef stdout +#endif +#define stdout 0 diff --git a/sys/vms/nethack.com b/sys/vms/nethack.com new file mode 100644 index 0000000..164852f --- /dev/null +++ b/sys/vms/nethack.com @@ -0,0 +1,53 @@ +$! NetHack.Com -- sample command procedure for invoking NetHack 9-JAN-1993 +$ v = 'f$verify(0)' +$! +$! Possible command line arguments include +$! "-uConan-B" !play a barbarian named Conan +$! "-u" "Merlin-W" !play a wizard named Merlin (slight variant of above) +$! "-e" or "-E" !play an elf with default name (from environment +$! ! [ie, NETHACKOPTIONS logical name] or VMS username) +$! "-a" or "-A", "-b" or "-B", "-c" or "-C", ... !specify character type +$! !note: "-s" is ambiguous between "play as a samurai" +$! ! vs "show scoreboard", so use "-S" for the former +$! "-x" or "-X" !play in 'explore' mode (practice for beginners) +$! "-D" !play in 'wizard' mode (for debugging, available only +$! ! to the username compiled into nethack.exe as WIZARD) +$! "-dec" !turn on DECgraphics mode (VT100 line drawing, done +$! ! automatically below if appropriate term attribs set) +$! "-d" dir-path !specify an alternate playground directory (not +$! ! recommended; define HACKDIR instead) +$! +$ +$! +$! assume this command procedure has been placed in the playground directory; +$! get its device:[directory] +$ hackdir = f$parse("_._;0",f$environ("PROCEDURE")) - "_._;0" +$! +$! hackdir should point to the 'playground' directory +$ if f$trnlnm("HACKDIR").eqs."" then define hackdir 'hackdir' +$! +$! termcap is a text file defining terminal capabilities and escape sequences +$ if f$trnlnm("TERMCAP").eqs."" then define termcap hackdir:termcap +$! +! [ obsolete: now handled within nethack itself ] +! $! prior to VMS v6, the C Run-Time Library doesn't understand vt420 :-( +! $ TT$_VT400_Series = 113 +! $ if f$getdvi("TT:","DEVTYPE").eq.TT$_VT400_Series - +! .and. f$trnlnm("NETHACK_TERM").eqs."" then define nethack_term "vt400" +$! +$! use the VT100 line drawing character set if possible +$ graphics = "" +$ usropt = f$trnlnm("NETHACKOPTIONS") +$ if usropt.eqs."" then usropt = f$trnlnm("HACKOPTIONS") +$ if f$locate("DECG",f$edit(usropt,"UPCASE")) .ge. f$length(usropt) then - + if f$getdvi("TT:","TT_DECCRT") .and. f$getdvi("TT:","TT_ANSICRT") then - +$ graphics = " -dec" !select DECgraphics mode by default +$! +$! get input from the terminal, not from this .com file +$ deassign sys$input +$! +$ nethack := $hackdir:nethack +$ if p1.nes."-s" .and. p1.nes."-s all" then - + nethack = nethack + graphics +$ nethack "''p1'" "''p2'" "''p3'" "''p4'" "''p5'" "''p6'" "''p7'" "''p8'" +$! diff --git a/sys/vms/oldcrtl.c b/sys/vms/oldcrtl.c new file mode 100644 index 0000000..ee87e5a --- /dev/null +++ b/sys/vms/oldcrtl.c @@ -0,0 +1,192 @@ +/* SCCS Id: @(#)oldcrtl.c 3.4 1995/06/01 */ +/* Pat Rankin May'90 */ +/* VMS NetHack support, not needed for vms 4.6,4.7,5.x,or later */ + +#ifdef VERYOLD_VMS +/* + * The following routines are used by NetHack but were not available + * from the C Run-Time Library (VAXCRTL) prior to VMS V4.6. + * + * atexit, memcmp, memcpy, qsort, rename, vprintf, vsprintf + * + * Most of them are implemented here, but others will have to be worked + * around in another fashion [such as '#define USE_OLDARGS' (even though + * is available) to avoid the need for vprintf & vsprintf]. + * + */ +#define REG register +#define const + +#ifndef SUPPRESS_MEM_FUNCS +/* note: hand optimized for VAX (hardware pre-decrement & post-increment) */ + +/* void *memset(void *, int, size_t) -- fill chunk of memory. +*/ +char *memset( dst, fil, cnt ) +REG char *dst; +REG char fil; +REG int cnt; +{ + char *dst_p = dst; + while ( --cnt >= 0 ) + *dst++ = fil; + return dst_p; +} + +/* void *memcpy(void *, const void *, size_t) -- copy chunk of memory. +*/ +char *memcpy( dst, src, cnt ) +REG char *dst; +REG const char *src; +REG int cnt; +{ + char *dst_p = dst; + while ( --cnt >= 0 ) + *dst++ = *src++; + return dst_p; +} + +/* void *memmove(void *, const void *, size_t) -- copy possibly overlapping mem. +*/ +char *memmove( dst, src, cnt ) +REG char *dst; +REG const char *src; +REG int cnt; +{ + char *dst_p = dst; + if ( src == dst || cnt <= 0 ) { + ; /* do nothing */ + } else if ( dst < src || dst >= src + cnt ) { + while ( --cnt >= 0 ) + *dst++ = *src++; + } else { /* work backwards */ + dst += cnt, src += cnt; + while ( --cnt >= 0 ) + *--dst = *--src; + } + return dst_p; +} + +/* void *memchr(const void *, int, size_t) -- search for a byte. +*/ +char *memchr( buf, byt, len ) +REG const char *buf; +REG char byt; +REG int len; +{ + while ( --len >= 0 ) + if ( *buf++ == byt ) /* found */ + return (char *)--buf; + return (char *)0; /* not found */ +} + +/* int memcmp(const void *, const void *, size_t) -- compare two chunks. +*/ +int memcmp( buf1, buf2, len ) +REG const char *buf1; +REG const char *buf2; +REG int len; +{ + while ( --len >= 0 ) + if ( *buf1++ != *buf2++ ) + return (*--buf1 - *--buf2); + return 0; /* buffers matched */ +} +#endif /*!SUPPRESS_MEM_FUNCS*/ + + +#ifndef SUPPRESS_ATEXIT +/* int atexit(void (*)(void)) -- register an exit handler. +*/ +#define MAX_EXIT_FUNCS 32 /* arbitrary (32 matches VAX C v3.x docs) */ +struct ex_hndlr { long reserved, (*routine)(), arg_count, *arg1_addr; }; +static int ex_cnt = 0; /* number of handlers registered so far */ +static struct { long dummy_arg; struct ex_hndlr handler; /*(black box)*/ + } ex_data[MAX_EXIT_FUNCS]; /* static handler data */ +extern unsigned long sys$dclexh(); + +int atexit( function ) + void (*function)(); /* note: actually gets called with 1 arg */ +{ + if ( ex_cnt < MAX_EXIT_FUNCS ) { + ex_data[ex_cnt].dummy_arg = 0; /* ultimately receives exit reason */ + ex_data[ex_cnt].handler.reserved = 0; + ex_data[ex_cnt].handler.routine = (long (*)()) function; + ex_data[ex_cnt].handler.arg_count = 1; /*(required)*/ + ex_data[ex_cnt].handler.arg1_addr = &ex_data[ex_cnt].dummy_arg; + (void)sys$dclexh(&ex_data[ex_cnt].handler); /* declare exit handler */ + return ++ex_cnt; /*(non-zero)*/ + } else + return 0; +} +#endif /*!SUPPRESS_ATEXIT*/ + + +#ifndef SUPPRESS_RENAME +/* int rename(const char *, const char *) -- rename a file (on same device). +*/ +#ifndef EVMSERR +#include +#define C$$TRANSLATE(status) (errno = EVMSERR, vaxc$errno = (status)) +#endif +extern unsigned long lib$rename_file(); + +int rename( old_name, new_name ) + const char *old_name; + const char *new_name; +{ + struct dsc { unsigned short len, mbz; const char *adr; } old_dsc, new_dsc; + unsigned long status; + + /* put strings into descriptors and call run-time library routine */ + new_dsc.mbz = old_dsc.mbz = 0; /* type and class unspecified */ + old_dsc.len = strlen( old_dsc.adr = old_name ); + new_dsc.len = strlen( new_dsc.adr = new_name ); + status = lib$rename_file(&old_dsc, &new_dsc); /* omit optional args */ + if ( !(status & 1) ) { /* even => failure */ + C$$TRANSLATE(status); + return -1; + } else /* odd => success */ + return 0; +} +#endif /*!SUPPRESS_RENAME*/ + + +#ifndef SUPPRESS_QSORT +/* void qsort(void *, size_t, size_t, int (*)()) -- sort arbitrary collection. +*/ +extern char *malloc(); /* assume no alloca() available */ +extern void free(); + +void qsort( base, count, size, compare ) + char *base; + int count; +REG int size; + int (*compare)(); +{ +REG int i, cmp; +REG char *next, *prev, *tmp = 0; + char wrk_buf[512]; + + /* just use a shuffle sort (tradeoff between efficiency & simplicity) */ + /* [Optimal if already sorted; worst case when initially reversed.] */ + for ( next = base, i = 1; i < count; i++ ) { + prev = next, next += size; /* increment front pointer */ + if ( (cmp = (*compare)( next, prev)) < 0 ) { + /* found element out of order; move other(s) up then re-insert it */ + if ( !tmp ) tmp = size > (int)(sizeof wrk_buf) ? malloc(size) : wrk_buf; + memcpy( tmp, next, size); /* save smaller element */ + while ( cmp < 0 ) { + memcpy( prev + size, prev, size); /* move larger elem. up */ + prev -= size; /* decrement back pointer */ + cmp = (prev >= base ? (*compare)( tmp, prev) : 0); + } + memcpy( prev + size, tmp, size); /* restore small element */ + } + } + if ( tmp != 0 && tmp != wrk_buf ) free(tmp); + return; +} +#endif /*!SUPPRESS_QSORT*/ + +#endif /*VERYOLD_VMS*/ diff --git a/sys/vms/spec_lev.com b/sys/vms/spec_lev.com new file mode 100644 index 0000000..9076933 --- /dev/null +++ b/sys/vms/spec_lev.com @@ -0,0 +1,88 @@ +$ ! sys/vms/spec_lev.com -- preprocess nethack's special level compiler code +$ ! +$ ! This operation needs to be performed prior to executing vmsbuild.com. +$ ! Process the scanning and parsing code for NetHack's special level +$ ! and dungeon compilers. *.l and *.y are converted into *'.c and *.h. +$ ! +$ +$ ! setup yacc/bison and lex/flex; +$ ! (Uncomment the alternatives appropriate for your site; +$ ! if yacc and lex are not defined, the pre-processed files +$ ! distributed in sys/share will be copied and used.) +$ ! yacc := bison /Define !native bison (w/ DCL CLD) +$ ! yacc := $bison$dir:bison -y -d !'foreign' bison (w/o CLD) +$ ! yacc := posix /Run/Input=nl: posix$bin:yacc. """-d +$ ! yacc := $shell$exe:yacc -d !yacc from DEC/Shell +$ ! lex := $flex$dir:flex !flex +$ ! lex := posix /Run/Input=nl: posix$bin:lex. """ +$ ! lex := $shell$exe:lex +$ ! (Nothing below this line should need to be changed.) +$ ! additional setup +$ rename := rename/New_Vers +$ mung := call mung ! not to be confused with teco :-) +$ delete := delete/noConfirm +$ search := search/Exact +$ copy := copy/noConcat +$ ! start from a known location -- [.sys.vms], then move to [-.-.util] +$ cur_dir = f$environment("DEFAULT") +$ set default 'f$parse(f$environment("PROCEDURE"),,,"DIRECTORY")' +$ set default [-.-.util] !move to utility directory +$ +$mung: subroutine +$ ! kludge to strip bogus #module directives from POSIX-processed files +$ ! in lieu of $ rename 'p1' 'p2' +$ search/Match=NOR 'p1' "#module" /Output='p2' +$ delete 'p1';* +$ endsubroutine !mung +$ +$ ! first cleanup any old intermediate files (to safely handle blind renaming) +$ if f$search("*tab.%").nes."" then delete *tab.%;* !yacc & bison +$ if f$search("*yy.c") .nes."" then delete *yy.c;* !lex & flex +$ +$ ! process lev_comp.y into lev_yacc.c and ../include/lev_comp.h +$ if f$type(yacc).eqs."STRING" +$ then +$ yacc lev_comp.y +$ if f$search("y_tab.%").nes."" then rename y_tab.% lev_comp_tab.* +$ if f$search("ytab.%") .nes."" then rename ytab.% lev_comp_tab.* +$ else ! use preprocessed files +$ copy [-.sys.share]lev_yacc.c,lev_comp.h []lev_comp_tab.* +$ endif +$ mung lev_comp_tab.c lev_yacc.c +$ rename lev_comp_tab.h [-.include]lev_comp.h +$ +$ ! process lev_comp.l into lev_lex.c +$ if f$type(lex).eqs."STRING" +$ then +$ lex lev_comp.l +$ if f$search("lexyy.c").nes."" then rename lexyy.c lex_yy.* +$ else ! use preprocessed file +$ copy [-.sys.share]lev_lex.c []lex_yy.* +$ endif +$ mung lex_yy.c lev_lex.c +$ +$ ! process dgn_comp.y into dgn_yacc.c and ../include/dgn_comp.h +$ if f$type(yacc).eqs."STRING" +$ then +$ yacc dgn_comp.y +$ if f$search("y_tab.%").nes."" then rename y_tab.% dgn_comp_tab.* +$ if f$search("ytab.%") .nes."" then rename ytab.% dgn_comp_tab.* +$ else +$ copy [-.sys.share]dgn_yacc.c,dgn_comp.h []dgn_comp_tab.* +$ endif +$ mung dgn_comp_tab.c dgn_yacc.c +$ rename dgn_comp_tab.h [-.include]dgn_comp.h +$ +$ ! process dgn_comp.l into dgn_lex.c +$ if f$type(lex).eqs."STRING" +$ then +$ lex dgn_comp.l +$ if f$search("lexyy.c").nes."" then rename lexyy.c lex_yy.* +$ else +$ copy [-.sys.share]dgn_lex.c []lex_yy.* +$ endif +$ mung lex_yy.c dgn_lex.c +$ +$ ! done +$ set default 'cur_dir' +$ exit diff --git a/sys/vms/vmsbuild.com b/sys/vms/vmsbuild.com new file mode 100644 index 0000000..7a0b3ba --- /dev/null +++ b/sys/vms/vmsbuild.com @@ -0,0 +1,307 @@ +$ ! vms/vmsbuild.com -- compile and link NetHack 3.4.* [pr] +$ version_number = "3.4.3" +$ ! +$ ! usage: +$ ! $ set default [.src] !or [-.-.src] if starting from [.sys.vms] +$ ! $ @[-.sys.vms]vmsbuild [compiler-option] [link-option] [cc-switches] +$ ! options: +$ ! compiler-option : either "VAXC", "DECC" or "GNUC" or "" !default VAXC +$ ! link-option : either "SHARE[able]" or "LIB[rary]" !default SHARE +$ ! cc-switches : optional qualifiers for CC (such as "/noOpt/Debug") +$ ! notes: +$ ! If the symbol "CC" is defined, compiler-option is not used. +$ ! The link-option refers to VAXCRTL (C Run-Time Library) handling; +$ ! to specify it while letting compiler-option default, use "" as +$ ! the compiler-option. +$ ! To re-link without compiling, use "LINK" as special 'compiler-option'; +$ ! to re-link with GNUC library, 'CC' must begin with "G" (or "g"). +$ ! Default wizard definition moved to include/vmsconf.h. +$ +$ decc_dflt = f$trnlnm("DECC$CC_DEFAULT") +$ j = (decc_dflt.nes."") .and. 1 +$ vaxc_ = "CC" + f$element(j,"#","#/VAXC") + "/NOLIST/OPTIMIZE=NOINLINE" +$ decc_ = "CC" + f$element(j,"#","#/DECC") + "/PREFIX=ALL/NOLIST" +$ gnuc_ = "GCC" +$ if f$type(gcc).eqs."STRING" then gnuc_ = gcc +$ gnulib = "gnu_cc:[000000]gcclib/Library" !(not used w/ vaxc) +$ ! common CC options (/obj=file doesn't work for GCC 1.36, use rename instead) +$ c_c_ = "/INCLUDE=[-.INCLUDE]" +$ veryold_vms = f$extract(1,1,f$getsyi("VERSION")).eqs."4" - + .and. f$extract(3,3,f$getsyi("VERSION")).lts."6" +$ if veryold_vms then c_c_ = c_c_ + "/DEFINE=(""VERYOLD_VMS"")" +$ axp = (f$getsyi("CPU").ge.128) !f$getsyi("ARCH_NAME").eqs."Alpha" +$ ! miscellaneous setup +$ ivqual = %x00038240 !DCL-W-IVQUAL (used to check for ancient vaxc) +$ abort := exit %x1000002A +$ cur_dir = f$environment("DEFAULT") +$ vmsbuild = f$environment("PROCEDURE") +$ ! validate first parameter +$ p1 := 'p1' +$ if p1.eqs."" .and. (axp .or. decc_dflt.eqs."/DECC") then p1 = "DECC" +$ o_VAXC = 0 !(c_opt substring positions) +$ o_DECC = 5 +$ o_GNUC = 10 +$ o_LINK = 15 +$ o_SPCL = 20 +$ c_opt = f$locate("|"+p1, "|VAXC|DECC|GNUC|LINK|SPECIAL|") !5 +$ if (c_opt/5)*5 .eq. c_opt then goto p1_ok +$ copy sys$input: sys$error: !p1 usage +%first arg is compiler option; it must be one of + "VAXC" -- use VAX C to compile everything + or "DECC" -- use DEC C to compile everything + or "GNUC" -- use GNU C to compile everything + or "LINK" -- skip compilation, just relink nethack.exe + or "SPEC[IAL]" -- just compile and link lev_comp.exe + or "" -- default operation (VAXC unless 'CC' is defined) + +Note: if a DCL symbol for CC is defined, "VAXC" and "GNUC" are no-ops. + If the symbol value begins with "G" (or "g"), then the GNU C + library will be included in all link operations. Do not rebuild + lev_comp with "SPECIAL" unless you have a CC symbol setup with + the proper options. +$ abort +$p1_ok: +$ ! validate second parameter +$ p2 := 'p2' +$ l_opt = f$locate("|"+p2, "|SHAREABLE|LIBRARY__|NONE_____|") !10 +$ if (l_opt/10)*10 .eq. l_opt then goto p2_ok +$ copy sys$input: sys$error: !p2 usage +%second arg is C run-time library handling; it must be one of + "SHAREABLE" -- link with SYS$SHARE:VAXCRTL.EXE/SHAREABLE + or "LIBRARY" -- link with SYS$LIBRARY:VAXCRTL.OLB/LIBRARY + or "NONE" -- explicitly indicate DECC$SHR + or "" -- default operation (use shareable image) + +Note: for MicroVMS 4.x, "SHAREABLE" (which is the default) is required. + Specify "NONE" if using DEC C with a CC symbol overriding 1st arg. +$ abort +$p2_ok: +$ ! start from a known location -- [.sys.vms], then move to [-.-.src] +$ set default 'f$parse(vmsbuild,,,"DEVICE")''f$parse(vmsbuild,,,"DIRECTORY")' +$ set default [-.-.src] !move to source directory +$ ! compiler setup; if a symbol for "CC" is already defined it will be used +$ if f$type(cc).eqs."STRING" then goto got_cc +$ cc = vaxc_ !assume "VAXC" requested or defaulted +$ if c_opt.eq.o_GNUC then goto chk_gcc !explicitly invoked w/ "GNUC" option +$ if c_opt.eq.o_DECC then cc = decc_ +$ if c_opt.ne.o_VAXC then goto got_cc !"SPEC" or "LINK", skip compiler check +$ ! we want to prevent function inlining with vaxc v3.x (/opt=noinline) +$ ! but we can't use noInline with v2.x, so need to determine version +$ set noOn +$ msgenv = f$environment("MESSAGE") +$ set message/noFacil/noSever/noIdent/noText +$ cc/noObject _NLA0:/Include=[] !strip 'noinline' if error +$ sts = $status +$ if sts then goto reset_msg !3.0 or later will check out OK +$ ! must be dealing with vaxc 2.x; ancient version (2.2 or earlier) +$ ! can't handle /include='dir', needs c$include instead +$ cc = cc - "=NOINLINE" - ",NOINLINE" - "NOINLINE," +$ if sts.ne.IVQUAL then goto reset_msg +$ define/noLog c$include [-.INCLUDE] +$ c_c_ = "/DEFINE=(""ANCIENT_VAXC"")" +$ if veryold_vms then c_c_ = c_c_ - ")" + ",""VERYOLD_VMS"")" +$reset_msg: +$ set message 'msgenv' +$ set On +$ goto got_cc +$ ! +$chk_gcc: +$ cc = gnuc_ +$ ! old versions of gcc-vms don't have or available +$ c_c_ = "/DEFINE=(""USE_OLDARGS"")" +$ if veryold_vms then c_c_ = c_c_ - ")" + ",""VERYOLD_VMS"")" +$ if veryold_vms then goto chk_gas !avoid varargs & stdarg +$ if f$search("gnu_cc_include:[000000]varargs.h").nes."" then - + c_c_ = "/DEFINE=(""USE_VARARGS"")" +$ if f$search("gnu_cc_include:[000000]stdarg.h").nes."" then - + c_c_ = "/DEFINE=(""USE_STDARG"")" +$chk_gas: +$ ! test whether this version of gas handles the 'const' construct correctly +$ gas_chk_tmp = "sys$scratch:gcc-gas-chk.tmp" +$ if f$search(gas_chk_tmp).nes."" then delete/noconfirm/nolog 'gas_chk_tmp';* +$ gas_ok = 0 !assume bad +$ on warning then goto skip_gas +$ define/user/nolog sys$error 'gas_chk_tmp' +$ mcr gnu_cc:[000000]gcc-as sys$input: -o _NLA0: +$DECK +.const +.comm dummy,0 +.const +.comm dummy,0 +$EOD +$ gas_ok = 1 !assume good +$ if f$search(gas_chk_tmp).eqs."" then goto skip_gas +$ ! if the error file is empty, gas can deal properly with const +$ gas_ok = f$file_attrib(gas_chk_tmp,"EOF") .eq. 0 +$ delete/noconfirm/nolog 'gas_chk_tmp';* +$skip_gas: +$ on warning then continue +$ if .not.gas_ok then c_c_ = c_c_ - ")" + ",""const="")" +$ c_c_ = "/INCLUDE=[-.INCLUDE]" + c_c_ +$ ! +$got_cc: +$ cc = cc + c_c_ !append common qualifiers +$ if p3.nes."" then cc = cc + p3 !append optional user preferences +$ g := 'f$extract(0,1,cc)' +$ if g.eqs."$" then g := 'f$extract(1,1,cc)' !"foreign" gcc +$ if f$edit(f$extract(1,1,cc),"UPCASE").eqs."E" then g := X !GEMC +$ if g.nes."G" .and. c_opt.ne.o_GNUC then gnulib = "" +$ if g.eqs."G" .or. c_opt.eq.o_GNUC then gnulib = "," + gnulib +$ ! linker setup; if a symbol for "LINK" is defined, we'll use it +$ if f$type(link).nes."STRING" then link = "LINK/NOMAP" +$ if p4.nes."" then link = link + p4 !append optional user preferences +$ if c_opt.eq.o_DECC .or. l_opt.eq.10 +$ then +$ crtl = "" !sys$share:decc$shr.exe/Sharable found automatically +$ create crtl.opt !empty +$ else +$ crtl = ",sys$library:vaxcrtl.olb/Library" !object library +$ if l_opt.ne.0 then goto crtl_ok +$ crtl = ",sys$disk:[-.src]crtl.opt/Options" !shareable image +$ if f$search("crtl.opt").nes."" then goto crtl_ok !assume its right +$ create sys$disk:[-.src]crtl.opt +sys$share:vaxcrtl.exe/Shareable +$ endif +$crtl_ok: +$ if f$search("crtl.opt;-2").nes."" then purge/Keep=2/noLog crtl.opt +$ ! version ID info for linker to record in .EXE files +$ create ident.opt +$ open/Append f ident.opt +$ write f "identification=""",version_number,""" !version" +$ close f +$ if f$search("ident.opt;-1").nes."" then purge/noLog ident.opt +$ ident_opt = ",sys$disk:[-.src]ident.opt/Options" +$ ! final setup +$ nethacklib = "[-.src]nethack.olb" +$ milestone = "write sys$output f$fao("" !5%T "",0)," +$ if c_opt.eq.o_LINK then goto link !"LINK" requested, skip compilation +$ rename := rename/New_Vers +$ touch := set file/Truncate +$ makedefs := $sys$disk:[-.util]makedefs +$ show symbol cc +$ goto begin !skip subroutines +$! +$compile_file: !input via 'c_file' +$ no_lib = ( f$extract(0,1,c_file) .eqs. "#" ) +$ if no_lib then c_file = f$extract(1,255,c_file) +$ c_name = f$edit(f$parse(c_file,,,"NAME"),"LOWERCASE") +$ f_opts = "" !options for this file +$ if f$type('c_name'_options).nes."" then f_opts = 'c_name'_options +$ milestone " (",c_name,")" +$ if f$search("''c_name'.obj").nes."" then delete 'c_name'.obj;* +$ cc 'f_opts' 'c_file' +$ if .not.no_lib then nh_obj_list == nh_obj_list + ",''c_name'.obj;0" +$ return +$! +$compile_list: !input via 'c_list' +$ nh_obj_list == "" +$ j = -1 +$ c_loop: +$ j = j + 1 +$ c_file = f$element(j,",",c_list) !get next file +$ if c_file.eqs."," then goto c_done +$ c_file = c_file + ".c" +$ gosub compile_file +$ goto c_loop +$ c_done: +$ nh_obj_list == f$extract(1,999,nh_obj_list) +$ if nh_obj_list.nes."" then libr/Obj 'nethacklib' 'nh_obj_list'/Replace +$ if nh_obj_list.nes."" then delete 'nh_obj_list' +$ delete/symbol/global nh_obj_list +$ return +$! +$begin: +$! +$! miscellaneous special source file setup +$! +$ if f$search("random.c").eqs."" then copy [-.sys.share]random.c []*.* +$ if f$search("tclib.c") .eqs."" then copy [-.sys.share]tclib.c []*.* +$ if f$search("[-.util]lev_yacc.c").eqs."" then @[-.sys.vms]spec_lev.com +$! +$! create object library +$! +$ if c_opt.ne.o_SPCL .or. f$search("''nethacklib'").eqs."" then - + libr/Obj 'nethacklib'/Create=(Block=3000,Hist=0) +$ if f$search("''nethacklib';-1").nes."" then purge 'nethacklib' +$! +$! compile and link makedefs, then nethack, finally lev_comp & dgn_comp. +$! +$ milestone "" +$ c_list = "[-.sys.vms]vmsmisc,[]alloc,dlb,monst,objects" +$ if c_opt.eq.o_SPCL then c_list = c_list + ",decl,drawing" +$ gosub compile_list +$ if c_opt.eq.o_SPCL then goto special !"SPECIAL" requested, skip main build +$ set default [-.util] +$ c_list = "#makedefs" +$ gosub compile_list +$ link makedefs.obj,'nethacklib'/Lib'crtl''gnulib''ident_opt' +$ milestone "makedefs" +$! create some build-time files +$ makedefs -p !pm.h +$ makedefs -o !onames.h +$ makedefs -v !date.h +$ milestone " (*.h)" +$ makedefs -m !../src/monstr.c +$ makedefs -z !../src/vis_tab.c, ../include/vis_tab.h +$ milestone " (*.c)" +$ set default [-.src] +$! compile most of the source files: +$ c_list = "decl,version,[-.sys.vms]vmsmain,[-.sys.vms]vmsunix" - + + ",[-.sys.vms]vmstty,[-.sys.vms]vmsmail,[-.sys.vms]vmsfiles" - + + ",[]random,[]tclib" !copied from [-.sys.share] +$ gosub compile_list +$ c_list = "[-.win.tty]getline,[-.win.tty]termcap" - + + ",[-.win.tty]topl,[-.win.tty]wintty" +$ gosub compile_list +$ c_list = "allmain,apply,artifact,attrib,ball,bones,botl,cmd,dbridge,detect" - + + ",dig,display,do,do_name,do_wear,dog,dogmove,dokick,dothrow,drawing" - + + ",dungeon,eat,end,engrave,exper,explode,extralev,files,fountain" +$ gosub compile_list +$ c_list = "hack,hacklib,invent,light,lock,mail,makemon,mapglyph,mcastu" - + + ",mhitm,mhitu,minion,mklev,mkmap,mkmaze,mkobj,mkroom,mon,mondata" - + + ",monmove,monstr,mplayer,mthrowu,muse,music,o_init,objnam,options" - + + ",pager,pickup" +$ gosub compile_list +$ c_list = "pline,polyself,potion,pray,priest,quest,questpgr,read" - + + ",rect,region,restore,rip,rnd,role,rumors,save,shk,shknam,sit" - + + ",sounds,sp_lev,spell,steal,steed,teleport,timeout,topten,track" - + + ",trap,u_init" +$ gosub compile_list +$ c_list = "uhitm,vault,vision,vis_tab,weapon,were,wield,windows" - + + ",wizard,worm,worn,write,zap" +$ gosub compile_list +$! +$link: +$ milestone "" +$ link/Exe=nethack.exe 'nethacklib'/Lib/Incl=(vmsmain)'crtl''gnulib''ident_opt' +$ milestone "NetHack" +$ if c_opt.eq.o_LINK then goto done !"LINK" only +$special: +$! +$! build special level and dungeon compilers +$! +$ set default [-.util] +$ c_list = "#panic,#lev_main,#lev_yacc,#dgn_main,#dgn_yacc" +$ if c_opt.eq.o_SPCL then c_list = "[-.sys.vms]vmsfiles," + c_list +$ gosub compile_list +$ c_list = "#lev_lex,#dgn_lex" +$ copy [-.sys.vms]lev_lex.h stdio.*/Prot=(s:rwd,o:rwd) +$ gosub compile_list +$ rename stdio.h lev_lex.* +$ link/exe=lev_comp.exe lev_main.obj,lev_yacc.obj,lev_lex.obj,- + panic.obj,'nethacklib'/Lib'crtl''gnulib''ident_opt' +$ milestone "lev_comp" +$ link/exe=dgn_comp.exe dgn_main.obj,dgn_yacc.obj,dgn_lex.obj,- + panic.obj,'nethacklib'/Lib'crtl''gnulib''ident_opt' +$ milestone "dgn_comp" +$! +$ c_list = "#dlb_main,#recover" +$ gosub compile_list +$ link/exe=dlb.exe dlb_main.obj,panic.obj,'nethacklib'/Lib'crtl''gnulib''ident_opt' +$ milestone "dlb" +$ link/exe=recover.exe recover.obj,'nethacklib'/Lib'crtl''gnulib''ident_opt' +$ milestone "recover" +$! +$done: +$ set default 'cur_dir' +$ exit diff --git a/sys/vms/vmsfiles.c b/sys/vms/vmsfiles.c new file mode 100644 index 0000000..657c060 --- /dev/null +++ b/sys/vms/vmsfiles.c @@ -0,0 +1,293 @@ +/* SCCS Id: @(#)vmsfiles.c 3.4 1999/08/29 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * VMS-specific file manipulation routines to implement some missing + * routines or substitute for ones where we want behavior modification. + */ +#include "config.h" +#include + +/* lint supression due to lack of extern.h */ +int FDECL(vms_link, (const char *,const char *)); +int FDECL(vms_unlink, (const char *)); +int FDECL(vms_creat, (const char *,unsigned int)); +int FDECL(vms_open, (const char *,int,unsigned int)); +boolean FDECL(same_dir, (const char *,const char *)); +int FDECL(c__translate, (int)); +char *FDECL(vms_basename, (const char *)); + +#include +#if 0 +#include +#else +#define PSL$C_EXEC 1 /* executive mode, for priv'd logical name handling */ +#endif +#include +#ifndef C$$TRANSLATE /* don't rely on VAXCRTL's internal routine */ +#define C$$TRANSLATE(status) (errno = EVMSERR, vaxc$errno = (status)) +#endif +extern unsigned long sys$parse(), sys$search(), sys$enter(), sys$remove(); +extern int VDECL(lib$match_cond, (int,int,...)); + +#define vms_success(sts) ((sts)&1) /* odd, */ +#define vms_failure(sts) (!vms_success(sts)) /* even */ + +/* vms_link() -- create an additional directory for an existing file */ +int vms_link(file, new) +const char *file, *new; +{ + struct FAB fab; + struct NAM nam; + unsigned short fid[3]; + char esa[NAM$C_MAXRSS]; + + fab = cc$rms_fab; /* set block ID and length, zero the rest */ + fab.fab$l_fop = FAB$M_OFP; + fab.fab$l_fna = (char *) file; + fab.fab$b_fns = strlen(file); + fab.fab$l_nam = &nam; + nam = cc$rms_nam; + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + + if (vms_success(sys$parse(&fab)) && vms_success(sys$search(&fab))) { + fid[0] = nam.nam$w_fid[0]; + fid[1] = nam.nam$w_fid[1]; + fid[2] = nam.nam$w_fid[2]; + fab.fab$l_fna = (char *) new; + fab.fab$b_fns = strlen(new); + + if (vms_success(sys$parse(&fab))) { + nam.nam$w_fid[0] = fid[0]; + nam.nam$w_fid[1] = fid[1]; + nam.nam$w_fid[2] = fid[2]; + nam.nam$l_esa = nam.nam$l_name; + nam.nam$b_esl = nam.nam$b_name + nam.nam$b_type + nam.nam$b_ver; + + (void) sys$enter(&fab); + } + } + + if (vms_failure(fab.fab$l_sts)) { + C$$TRANSLATE(fab.fab$l_sts); + return -1; + } + return 0; /* success */ +} + +/* + vms_unlink() -- remove a directory entry for a file; should only be used + for files which have had extra directory entries added, not for deletion + (because the file won't be deleted, just made inaccessible!). + */ +int vms_unlink(file) +const char *file; +{ + struct FAB fab; + struct NAM nam; + char esa[NAM$C_MAXRSS]; + + fab = cc$rms_fab; /* set block ID and length, zero the rest */ + fab.fab$l_fop = FAB$M_DLT; + fab.fab$l_fna = (char *) file; + fab.fab$b_fns = strlen(file); + fab.fab$l_nam = &nam; + nam = cc$rms_nam; + nam.nam$l_esa = esa; + nam.nam$b_ess = sizeof esa; + + if (vms_failure(sys$parse(&fab)) || vms_failure(sys$remove(&fab))) { + C$$TRANSLATE(fab.fab$l_sts); + return -1; + } + return 0; +} + +/* + Substitute creat() routine -- if trying to create a specific version, + explicitly remove an existing file of the same name. Since it's only + used when we expect exclusive access, add a couple RMS options for + optimization. (Don't allow sharing--eliminates coordination overhead, + and use 32 block buffer for faster throughput; ~30% speedup measured.) + */ +#undef creat +int vms_creat(file, mode) +const char *file; +unsigned int mode; +{ + if (index(file, ';')) { + /* assumes remove or delete, not vms_unlink */ + if (!unlink(file)) { + (void)sleep(1); + (void)unlink(file); + } + } + return creat(file, mode, "shr=nil", "mbc=32", "mbf=2", "rop=wbh"); +} + +/* + Similar substitute for open() -- if an open attempt fails due to being + locked by another user, retry it once (work-around for a limitation of + at least one NFS implementation). + */ +#undef open +int vms_open(file, flags, mode) +const char *file; +int flags; +unsigned int mode; +{ + int fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah"); + + if (fd < 0 && errno == EVMSERR && lib$match_cond(vaxc$errno, RMS$_FLK)) { + (void)sleep(1); + fd = open(file, flags, mode, "mbc=32", "mbf=2", "rop=rah"); + } + return fd; +} + +/* + Determine whether two strings contain the same directory name. + Used for deciding whether installed privileges should be disabled + when HACKDIR is defined in the environment (or specified via -d on + the command line). This version doesn't handle Unix-style file specs. + */ +boolean +same_dir(d1, d2) +const char *d1, *d2; +{ + if (!d1 || !*d1 || !d2 || !*d2) + return FALSE; + else if (!strcmp(d1, d2)) /* strcmpi() would be better, but that leads */ + return TRUE; /* to linking problems for the utilities */ + else { + struct FAB f1, f2; + struct NAM n1, n2; + + f1 = f2 = cc$rms_fab; /* initialize file access block */ + n1 = n2 = cc$rms_nam; /* initialize name block */ + f1.fab$b_acmodes = PSL$C_EXEC << FAB$V_LNM_MODE; + f1.fab$b_fns = strlen( f1.fab$l_fna = (char *)d1 ); + f2.fab$b_fns = strlen( f2.fab$l_fna = (char *)d2 ); + f1.fab$l_nam = (genericptr_t)&n1; /* link nam to fab */ + f2.fab$l_nam = (genericptr_t)&n2; + n1.nam$b_nop = n2.nam$b_nop = NAM$M_NOCONCEAL; /* want true device name */ + + return (vms_success(sys$parse(&f1)) && vms_success(sys$parse(&f2)) + && n1.nam$t_dvi[0] == n2.nam$t_dvi[0] + && !strncmp(&n1.nam$t_dvi[1], &n2.nam$t_dvi[1], n1.nam$t_dvi[0]) + && !memcmp((genericptr_t)n1.nam$w_did, + (genericptr_t)n2.nam$w_did, + sizeof n1.nam$w_did)); /*{ short nam$w_did[3]; }*/ + } +} + + +/* + * c__translate -- substitute for VAXCRTL routine C$$TRANSLATE. + * + * Try to convert a VMS status code into its Unix equivalent, + * then set `errno' to that value; use EVMSERR if there's no + * appropriate translation; set `vaxc$errno' to the original + * status code regardless. + * + * These translations match only a subset of VAXCRTL's lookup + * table, but work even if the severity has been adjusted or + * the inhibit-message bit has been set. + */ +#include +#include +#include +/* #include */ +/* #include */ + +#define VALUE(U) trans = U; break +#define CASE1(V) case (V >> 3) +#define CASE2(V,W) CASE1(V): CASE1(W) + +int c__translate(code) + int code; +{ + register int trans; + + switch ((code & 0x0FFFFFF8) >> 3) { /* strip upper 4 and bottom 3 bits */ + CASE2(RMS$_PRV,SS$_NOPRIV): + VALUE(EPERM); /* not owner */ + CASE2(RMS$_DNF,RMS$_DIR): + CASE2(RMS$_FNF,RMS$_FND): + CASE1(SS$_NOSUCHFILE): + VALUE(ENOENT); /* no such file or directory */ + CASE2(RMS$_IFI,RMS$_ISI): + VALUE(EIO); /* i/o error */ + CASE1(RMS$_DEV): + CASE2(SS$_NOSUCHDEV,SS$_DEVNOTMOUNT): + VALUE(ENXIO); /* no such device or address codes */ + CASE1(RMS$_DME): + /* CASE1(LIB$INSVIRMEM): */ + CASE2(SS$_VASFULL,SS$_INSFWSL): + VALUE(ENOMEM); /* not enough core */ + CASE1(SS$_ACCVIO): + VALUE(EFAULT); /* bad address */ + CASE2(RMS$_DNR,SS$_DEVASSIGN): + CASE2(SS$_DEVALLOC,SS$_DEVALRALLOC): + CASE2(SS$_DEVMOUNT,SS$_DEVACTIVE): + VALUE(EBUSY); /* mount device busy codes to name a few */ + CASE2(RMS$_FEX,SS$_FILALRACC): + VALUE(EEXIST); /* file exists */ + CASE2(RMS$_IDR,SS$_BADIRECTORY): + VALUE(ENOTDIR); /* not a directory */ + CASE1(SS$_NOIOCHAN): + VALUE(EMFILE); /* too many open files */ + CASE1(RMS$_FUL): + CASE2(SS$_DEVICEFULL,SS$_EXDISKQUOTA): + VALUE(ENOSPC); /* no space left on disk codes */ + CASE2(RMS$_WLK,SS$_WRITLCK): + VALUE(EROFS); /* read-only file system */ + default: + VALUE(EVMSERR); + }; + + errno = trans; + vaxc$errno = code; + return code; /* (not very useful) */ +} + +#undef VALUE +#undef CASE1 +#undef CASE2 + +static char base_name[NAM$C_MAXRSS+1]; + +/* return a copy of the 'base' portion of a filename */ +char * +vms_basename(name) +const char *name; +{ + unsigned len; + char *base, *base_p; + register const char *name_p; + + /* skip directory/path */ + if ((name_p = strrchr(name, ']')) != 0) name = name_p + 1; + if ((name_p = strrchr(name, '>')) != 0) name = name_p + 1; + if ((name_p = strrchr(name, ':')) != 0) name = name_p + 1; + if ((name_p = strrchr(name, '/')) != 0) name = name_p + 1; + if (!*name) name = "."; /* this should never happen */ + + /* find extension/version and derive length of basename */ + if ((name_p = strchr(name, '.')) == 0 || name_p == name) + name_p = strchr(name, ';'); + len = (name_p && name_p > name) ? name_p - name : strlen(name); + + /* return a lowercase copy of the name in a private static buffer */ + base = strncpy(base_name, name, len); + base[len] = '\0'; + /* we don't use lcase() so that utilities won't need hacklib.c */ + for (base_p = base; base_p < &base[len]; base_p++) + if (isupper(*base_p)) *base_p = tolower(*base_p); + + return base; +} + +/*vmsfiles.c*/ diff --git a/sys/vms/vmsmail.c b/sys/vms/vmsmail.c new file mode 100644 index 0000000..29e0192 --- /dev/null +++ b/sys/vms/vmsmail.c @@ -0,0 +1,486 @@ +/* SCCS Id: @(#)vmsmail.c 3.4 1995/06/01 */ +/* Copyright (c) Robert Patrick Rankin, 1991. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "config.h" +#include "mail.h" + +/* lint supression due to lack of extern.h */ +unsigned long NDECL(init_broadcast_trapping); +unsigned long NDECL(enable_broadcast_trapping); +unsigned long NDECL(disable_broadcast_trapping); +struct mail_info *NDECL(parse_next_broadcast); + +#ifdef MAIL +#include "wintype.h" +#include "winprocs.h" +#include +#include +#include +# ifndef __GNUC__ +#include +# else +# define MSG$_TRMHANGUP 6 +# define MSG$_TRMBRDCST 83 +# endif /*__GNUC__*/ +#include +/* #include */ +# define vms_ok(sts) ((sts)&1) + +static struct mail_info *FDECL(parse_brdcst, (char *)); +static void FDECL(filter_brdcst, (char *)); +static void NDECL(flush_broadcasts); +static void FDECL(broadcast_ast, (int)); +extern char *FDECL(eos, (char *)); +extern char *FDECL(strstri, (const char *,const char *)); +extern int FDECL(strncmpi, (const char *,const char *,int)); + +extern size_t FDECL(strspn, (const char *,const char *)); +#ifndef __DECC +extern int VDECL(sscanf, (const char *,const char *,...)); +#endif +extern unsigned long + smg$create_pasteboard(), + smg$get_broadcast_message(), + smg$set_broadcast_trapping(), + smg$disable_broadcast_trapping(); + +extern volatile int broadcasts; /* defining declaration in mail.c */ + +static long pasteboard_id = 0; /* SMG's magic cookie */ + +/* + * Mail (et al) overview: + * + * When a broadcast is asynchronously captured, a volatile counter + * ('broadcasts') is incremented. Each player turn, ckmailstatus() polls + * the counter and calls parse_next_broadcast() if it's positive; this + * returns some display text, object name, and response command, which is + * passed to newmail(). Routine newmail() generates a mail-daemon monster + * who approaches the character, "speaks" the display text, and delivers + * a scroll of mail pre-named to the object name; the response command is + * initially appended to the name, so that the object is tagged with both + * of them; a NUL is inserted to terminate the ordinary name and hide the + * command. (If the player renames such a scroll, the hidden command will + * be lost; who cares?) Unrecognized broadcasts result in the mail-daemon + * arriving and announcing the display text, but no scroll being created. + * If SHELL is undefined, then all broadcasts are treated as 'other'; since + * no subproceses are allowed, there'd be no way to respond to the scroll. + * + * When a scroll of mail is read by the character, readmail() extracts + * the hidden command string and uses it for the default when prompting the + * player for a system command to spawn. The player may enter any command + * he or she chooses, or just to accept the default or to + * avoid executing any command. If the command is "SPAWN", a regular shell + * escape to DCL is performed; otherwise, the indicated single command is + * spawned. Either way, NetHack resumes play when the subprocess terminates + * or explicitly reattaches to its parent. + * + * Broadcast parsing: + * + * The following broadcast messages are [attempted to be] recognized: + * text fragment name for scroll default command + * New mail VMSmail MAIL + * New ALL-IN-1 MAIL A1mail A1M + * Software Tools mail STmail MSG [+folder] + * MM mail MMmail MM + * WPmail: New mail WPmail OFFICE/MAIL + * **M400 mail M400mail M400 + * " mail", ^"mail " unknown mail SPAWN + * " phoning" Phone call PHONE ANSWER + * talk-daemon...by...foo Talk request TALK[/OLD] foo@bar + * (node)user - Bitnet noise XYZZY user@node + * Anything else results in just the message text being passed along, no + * scroll of mail so consequently no command to execute when scroll read. + * The user can set up ``$ XYZZY :== SEND'' prior to invoking NetHack if + * vanilla JNET responses to Bitnet messages are prefered. + * + * Static return buffers are used because only one broadcast gets + * processed at a time, and the essential information in each one is + * either displayed and discarded or copied into a scroll-of-mail object. + * + * The test driver code below can be used to check out potential new + * entries without rebuilding NetHack itself. CC/DEFINE="TEST_DRIVER" + * Link it with hacklib.obj or nethack.olb/incl=hacklib (not nethack/lib). + */ + +static struct mail_info msg; /* parse_*()'s return buffer */ +static char nam_cmd_buf[63], /* maximum onamelth, size of ONAME(object) */ + txt_buf[255+1]; /* same size as used for message buf[] */ + +/* try to decipher and categorize broadcast message text +*/ +static struct mail_info * +parse_brdcst(buf) /* called by parse_next_broadcast() */ +char *buf; /* input: filtered broadcast text */ +{ + int typ; + char *txt; + const char *nam, *cmd; +# ifdef SHELL /* only parse if spawned commands are enabled */ + register char *p, *q; + boolean is_jnet_send; + char cmd_buf[127+1], user[127+1], node[127+1], sentinel; + + /* Check these first; otherwise, their arbitrary text would enable + easy spoofing of some other message patterns. Unfortunately, + any home-grown broadcast delivery program poses a similar risk. */ + if (!strncmpi(buf, "reply received", 14)) goto other; + is_jnet_send = (sscanf(buf, "(%[^)])%s -%c", node, user, &sentinel) == 3); + if (is_jnet_send) goto jnet_send; + + /* scan the text more or less by brute force */ + if ((q = strstri(buf, " mail")) != 0 || /* all known mail broadcasts */ + !strncmpi(q = buf, "mail ", 5)) { /* unexpected alternative */ + typ = MSG_MAIL; + p = strstri(q, " from"); + txt = p ? strcat(strcpy(txt_buf, "Mail for you"), p) : (char *) 0; + + if (!strncmpi(buf, "new mail", 8)) { +/* +New mail [on node FOO] from [SPAM::]BAR [\"personal_name\"] [\(HH:MM:SS\)] +*/ + nam = "VMSmail"; /* assume VMSmail */ + cmd = "MAIL"; + if (txt && (p = strrchr(txt, '(')) > txt && /* discard time */ + (--p, strspn(p, "0123456789( :.)") == strlen(p))) *p = '\0'; + } else if (!strncmpi(buf, "new all-in-1", 12)) { + int i; +/* +New ALL-IN-1 MAIL message [on node FOO] from Personal Name \(BAR@SPAM\) [\(DD-MMM-YYYY HH:MM:SS\)] +*/ + nam = "A1mail"; + cmd = "A1M"; + if (txt && (p = strrchr(txt, '(')) > txt && /* discard date+time */ + sscanf(p-1," (%*d-%*[^-]-%*d %*d:%*d:%d) %c",&i,&sentinel) == 1) + *--p = '\0'; + } else if (!strncmpi(buf, "software tools", 14)) { +/* +Software Tools mail has arrived on FOO from \'BAR\' [in SPAM] +*/ + nam = "STmail"; + cmd = "MSG"; + if (txt && (p = strstri(p, " in ")) != 0) /* specific folder */ + cmd = strcat(strcpy(cmd_buf, "MSG +"), p + 4); + } else if (q - 2 >= buf && !strncmpi(q - 2, "mm", 2)) { +/* +{MultiNet\ |PMDF\/}MM mail has arrived on FOO from BAR\n +[Subject: subject_text] (PMDF only) +*/ + nam = "MMmail"; /* MultiNet's version of MM */ + cmd = "MM"; /*{ perhaps "MM READ"? }*/ + } else if (!strncmpi(buf, "wpmail:", 7)) { +/* +WPmail: New mail from BAR. subject_text +*/ + nam = "WPmail"; /* WordPerfect [sic] Office */ + cmd = "OFFICE/MAIL"; + } else if (!strncmpi(buf, "**m400 mail", 7)) { +/* +**M400 mail waiting** +*/ + nam = "M400mail"; /* Messenger 400 [not seen] */ + cmd = "M400"; + } else { + /* not recognized, but presumed to be mail */ + nam = "unknown mail"; + cmd = "SPAWN"; /* generic escape back to DCL */ + txt = (char *) 0; /* don't rely on "from" info here */ + } + + if (!txt) txt = strcat(strcpy(txt_buf, "Mail for you: "), buf); + /* + : end of mail recognition; now check for call-type interruptions... + */ + } else if ((q = strstri(buf, " phoning")) != 0) { +/* +BAR is phoning you [on FOO] \(HH:MM:SS\) +*/ + typ = MSG_CALL; + nam = "Phone call"; + cmd = "PHONE ANSWER"; + if (!strncmpi(q + 8, " you", 4)) q += (8 + 4), *q = '\0'; + txt = strcat(strcpy(txt_buf, "Do you hear ringing? "), buf); + } else if ((q = strstri(buf, " talk-daemon")) != 0 || + (q = strstri(buf, " talk_daemon")) != 0) { +/* +Message from TALK-DAEMON@FOO at HH:MM:SS\n +Connection request by BAR@SPAM\n +\[Respond with: TALK[/OLD] BAR@SPAM\] +*/ + typ = MSG_CALL; + nam = "Talk request"; /* MultiNet's TALK and/or TALK/OLD */ + cmd = "TALK"; + if ((p = strstri(q, " by ")) != 0) { + txt = strcat(strcpy(txt_buf, "Talk request from"), p + 3); + if ((p = strstri(p, "respond with")) != 0) { + if (*(p-1) == '[') *(p-1) = '\0'; else *p = '\0'; /* terminate */ + p += (sizeof "respond with" - sizeof ""); + if (*p == ':') p++; + if (*p == ' ') p++; + cmd = strcpy(cmd_buf, p); /* "TALK[/OLD] bar@spam" */ + p = eos(cmd_buf); + if (*--p == ']') *p = '\0'; + } + } else + txt = strcat(strcpy(txt_buf, "Pardon the interruption: "), buf); + } else if (is_jnet_send) { /* sscanf(,"(%[^)])%s -%c",,,)==3 */ +jnet_send: +/* +\(SPAM\)BAR - arbitrary_message_text (from BAR@SPAM) +*/ + typ = MSG_CALL; + nam = "Bitnet noise"; /* RSCS/NJE message received via JNET */ + Sprintf(cmd_buf, "XYZZY %s@%s", user, node); + cmd = cmd_buf; + /*{ perhaps just vanilla SEND instead of XYZZY? }*/ + Sprintf(txt_buf, "Message from %s@%s:%s", user, node, + &buf[1+strlen(node)+1+strlen(user)+2-1]); /* "(node)user -" */ + txt = txt_buf; + /* + : end of call recognition; anything else is none-of-the-above... + */ + } else { +other: +# endif /* SHELL */ +/* arbitrary broadcast: batch job completed, system shutdown imminent, &c */ + typ = MSG_OTHER; + nam = (char *) 0; /*"captured broadcast message"*/ + cmd = (char *) 0; + txt = strcat(strcpy(txt_buf, "Message for you: "), buf); +# ifdef SHELL + } + /* Daemon in newmail() will append period when the text is displayed */ + if ((p = eos(txt)) > txt && *--p == '.') *p = '\0'; + + /* newmail() and readmail() assume that nam and cmd are concatenated */ + if (nam) { /* object name to attach to scroll of mail */ + char *join = strcpy(nam_cmd_buf, nam); + if (cmd) { /* append command to name; readmail() requires it */ + int len = sizeof nam_cmd_buf - sizeof "" - (strlen(join) + 1); + cmd_buf[len] = '\0'; /* possibly truncate */ + (void) strcat(join, " "); + cmd = strcpy(eos(join), cmd); + } + nam = join; + } +# endif /* SHELL */ + /* truncate really long messages to prevent verbalize() from blowing up */ + if (txt && strlen(txt) > BUFSZ - 50) txt[BUFSZ - 50] = '\0'; + + msg.message_typ = typ; /* simple index */ + msg.display_txt = txt; /* text for daemon to pline() */ + msg.object_nam = nam; /* 'name' for mail scroll */ + msg.response_cmd = cmd; /* command to spawn when scroll read */ + return &msg; +} + +/* filter out non-printable characters and redundant noise +*/ +static void +filter_brdcst(buf) /* called by parse_next_broadcast() */ +register char *buf; /* in: original text; out: filtered text */ +{ + register char c, *p, *buf_p; + + /* filter the text; restrict consecutive spaces or dots to just two */ + for (p = buf_p = buf; *buf_p; buf_p++) { + c = *buf_p & '\177'; + if (c == ' ' || c == '\t' || c == '\n') + if (p == buf || /* ignore leading whitespace */ + (p >= buf+2 && *(p-1) == ' ' && *(p-2) == ' ')) continue; + else c = ' '; + else if (c == '.' || c < ' ' || c == '\177') + if (p == buf || /* skip leading beeps & such */ + (p >= buf+2 && *(p-1) == '.' && *(p-2) == '.')) continue; + else c = '.'; + else if (c == '%' && /* trim %%% OPCOM verbosity %%% */ + p >= buf+2 && *(p-1) == '%' && *(p-2) == '%') continue; + *p++ = c; + } + *p = '\0'; /* terminate, then strip trailing junk */ + while (p > buf && (*--p == ' ' || *p == '.')) *p = '\0'; + return; +} + +static char empty_string[] = ""; + +/* fetch the text of a captured broadcast, then mangle and decipher it +*/ +struct mail_info * +parse_next_broadcast() /* called by ckmailstatus(mail.c) */ +{ + short length, msg_type; + $DESCRIPTOR(message, empty_string); /* string descriptor for buf[] */ + struct mail_info *result = 0; + /* messages could actually be longer; let long ones be truncated */ + char buf[255+1]; + + message.dsc$a_pointer = buf, message.dsc$w_length = sizeof buf - 1; + msg_type = length = 0; + smg$get_broadcast_message(&pasteboard_id, &message, &length, &msg_type); + if (msg_type == MSG$_TRMBRDCST) { + buf[length] = '\0'; + filter_brdcst(buf); /* mask non-printable characters */ + result = parse_brdcst(buf); /* do the real work */ + } else if (msg_type == MSG$_TRMHANGUP) { + (void) gsignal(SIGHUP); + } + return result; +} + +/* spit out any pending broadcast messages whenever we leave +*/ +static void +flush_broadcasts() /* called from disable_broadcast_trapping() */ +{ + if (broadcasts > 0) { + short len, typ; + $DESCRIPTOR(msg_dsc, empty_string); + char buf[512+1]; + + msg_dsc.dsc$a_pointer = buf, msg_dsc.dsc$w_length = sizeof buf - 1; + raw_print(""); /* print at least one line for wait_synch() */ + do { + typ = len = 0; + smg$get_broadcast_message(&pasteboard_id, &msg_dsc, &len, &typ); + if (typ == MSG$_TRMBRDCST) buf[len] = '\0', raw_print(buf); + } while (--broadcasts); + wait_synch(); /* prompt with "Hit return to continue: " */ + } +} + +/* AST routine called when the terminal's associated mailbox receives a message +*/ +/*ARGSUSED*/ +static void +broadcast_ast(dummy) /* called asynchronously by terminal driver */ +int dummy; /* not used */ +{ + broadcasts++; +} + +/* initialize the broadcast manipulation code; SMG makes this easy +*/ +unsigned long init_broadcast_trapping() /* called by setftty() [once only] */ +{ + unsigned long sts, preserve_screen_flag = 1; + + /* we need a pasteboard to pass to the broadcast setup/teardown routines */ + sts = smg$create_pasteboard(&pasteboard_id, 0, 0, 0, &preserve_screen_flag); + if (!vms_ok(sts)) { + errno = EVMSERR, vaxc$errno = sts; + raw_print(""); + perror("?can't create SMG pasteboard for broadcast trapping"); + wait_synch(); + broadcasts = -1; /* flag that trapping is currently broken */ + } + return sts; +} + +/* set up the terminal driver to deliver $brkthru data to a mailbox device +*/ +unsigned long enable_broadcast_trapping() /* called by setftty() */ +{ + unsigned long sts = 1; + + if (broadcasts >= 0) { /* (-1 => no pasteboard, so don't even try) */ + /* register callback routine to be triggered when broadcasts arrive */ + /* Note side effect: also intercepts hangup notification. */ + /* Another note: TMPMBX privilege is required. */ + sts = smg$set_broadcast_trapping(&pasteboard_id, broadcast_ast, 0); + if (!vms_ok(sts)) { + errno = EVMSERR, vaxc$errno = sts; + raw_print(""); + perror("?can't enable broadcast trapping"); + wait_synch(); + } + } + return sts; +} + +/* return to 'normal'; $brkthru data goes straight to the terminal +*/ +unsigned long disable_broadcast_trapping() /* called by settty() */ +{ + unsigned long sts = 1; + + if (broadcasts >= 0) { + /* disable trapping; releases associated MBX so that SPAWN can work */ + sts = smg$disable_broadcast_trapping(&pasteboard_id); + if (!vms_ok(sts)) errno = EVMSERR, vaxc$errno = sts; + flush_broadcasts(); /* don't hold on to any buffered ones */ + } + return sts; +} +#else /* MAIL */ + /* simple stubs for non-mail configuration */ +unsigned long init_broadcast_trapping() { return 1; } +unsigned long enable_broadcast_trapping() { return 1; } +unsigned long disable_broadcast_trapping() { return 1; } +struct mail_info *parse_next_broadcast() { return 0; } +#endif /* MAIL */ + +/*----------------------------------------------------------------------*/ + +#ifdef TEST_DRIVER + /* (Take parse_next_broadcast for a spin. :-) */ + +volatile int broadcasts = 0; + +void newmail(foo) +struct mail_info *foo; +{ +# define STRING(s) ((s) ? (s) : "") + printf("\n\ + message type = %d\n\ + display text = \"%s\"\n\ + object name = \"%.*s\"\n\ + response cmd = \"%s\"\n\ +", foo->message_typ, STRING(foo->display_txt), + (foo->object_nam && foo->response_cmd) ? + (foo->response_cmd - foo->object_nam - 1) : + strlen(STRING(foo->object_nam)), + STRING(foo->object_nam), STRING(foo->response_cmd)); +# undef STRING +} + +void ckmailstatus() +{ + struct mail_info *brdcst, *parse_next_broadcast(); + + while (broadcasts > 0) { /* process all trapped broadcasts [until] */ + broadcasts--; + if ((brdcst = parse_next_broadcast()) != 0) { + newmail(brdcst); + break; /* only handle one real message at a time */ + } else + printf("\n--< non-broadcast encountered >--\n"); + } +} + +int main() +{ + char dummy[BUFSIZ]; + + init_broadcast_trapping(); + enable_broadcast_trapping(); + for (;;) { + ckmailstatus(); + printf("> "), fflush(stdout); /* issue a prompt */ + if (!gets(dummy)) break; /* wait for a response */ + } + disable_broadcast_trapping(); + return 1; +} + +void panic(s) char *s; { raw_print(s); exit(EXIT_FAILURE); } + +void raw_print(s) char *s; { puts(s); fflush(stdout); } + +void wait_synch() { char dummy[BUFSIZ]; + printf("\nPress to continue: "); fflush(stdout); (void) gets(dummy); +} +#endif /* TEST_DRIVER */ + +/*vmsmail.c*/ diff --git a/sys/vms/vmsmain.c b/sys/vms/vmsmain.c new file mode 100644 index 0000000..947501f --- /dev/null +++ b/sys/vms/vmsmain.c @@ -0,0 +1,454 @@ +/* SCCS Id: @(#)vmsmain.c 3.4 2003/10/16 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ +/* main.c - VMS NetHack */ + +#include "hack.h" +#include "dlb.h" + +#include + +static void NDECL(whoami); +static void FDECL(process_options, (int, char **)); +static void NDECL(byebye); +#ifndef SAVE_ON_FATAL_ERROR +# ifndef __DECC +# define vms_handler_type int +# else +# define vms_handler_type unsigned int +# endif +extern void FDECL(VAXC$ESTABLISH, (vms_handler_type (*)(genericptr_t,genericptr_t))); +static vms_handler_type FDECL(vms_handler, (genericptr_t,genericptr_t)); +#include /* system service status codes */ +#endif + +static void NDECL(wd_message); +#ifdef WIZARD +static boolean wiz_error_flag = FALSE; +#endif + +int +main(argc,argv) +int argc; +char *argv[]; +{ + register int fd; +#ifdef CHDIR + register char *dir; +#endif + +#ifdef SECURE /* this should be the very first code executed */ + privoff(); + fflush((FILE *)0); /* force stdio to init itself */ + privon(); +#endif + + atexit(byebye); + hname = argv[0]; + hname = vms_basename(hname); /* name used in 'usage' type messages */ + hackpid = getpid(); + (void) umask(0); + + choose_windows(DEFAULT_WINDOW_SYS); + +#ifdef CHDIR /* otherwise no chdir() */ + /* + * See if we must change directory to the playground. + * (Perhaps hack is installed with privs and playground is + * inaccessible for the player.) + * The logical name HACKDIR is overridden by a + * -d command line option (must be the first option given) + */ + dir = nh_getenv("NETHACKDIR"); + if (!dir) dir = nh_getenv("HACKDIR"); +#endif + if(argc > 1) { +#ifdef CHDIR + if (!strncmp(argv[1], "-d", 2) && argv[1][2] != 'e') { + /* avoid matching "-dec" for DECgraphics; since the man page + * says -d directory, hope nobody's using -desomething_else + */ + argc--; + argv++; + dir = argv[0]+2; + if(*dir == '=' || *dir == ':') dir++; + if(!*dir && argc > 1) { + argc--; + argv++; + dir = argv[0]; + } + if(!*dir) + error("Flag -d must be followed by a directory name."); + } + if (argc > 1) +#endif /* CHDIR */ + + /* + * Now we know the directory containing 'record' and + * may do a prscore(). + */ + if (!strncmp(argv[1], "-s", 2)) { +#ifdef CHDIR + chdirx(dir, FALSE); +#endif + prscore(argc, argv); + exit(EXIT_SUCCESS); + } + } + +#ifdef CHDIR + /* move to the playground directory; 'termcap' might be found there */ + chdirx(dir, TRUE); +#endif + +#ifdef SECURE + /* disable installed privs while loading nethack.cnf and termcap, + and also while initializing terminal [$assign("TT:")]. */ + privoff(); +#endif + initoptions(); + init_nhwindows(&argc, argv); + whoami(); +#ifdef SECURE + privon(); +#endif + + /* + * It seems you really want to play. + */ + u.uhp = 1; /* prevent RIP on early quits */ +#ifndef SAVE_ON_FATAL_ERROR + /* used to clear hangup stuff while still giving standard traceback */ + VAXC$ESTABLISH(vms_handler); +#endif + (void) signal(SIGHUP, (SIG_RET_TYPE) hangup); + + process_options(argc, argv); /* command line options */ + +#ifdef WIZARD + if (wizard) + Strcpy(plname, "wizard"); + else +#endif + if (!*plname || !strncmpi(plname, "games", 4) || + !strcmpi(plname, "nethack")) + askname(); + plnamesuffix(); /* strip suffix from name; calls askname() */ + /* again if suffix was whole name */ + /* accepts any suffix */ +#ifdef WIZARD + if(!wizard) { +#endif + /* + * check for multiple games under the same name + * (if !locknum) or check max nr of players (otherwise) + */ + (void) signal(SIGQUIT,SIG_IGN); + (void) signal(SIGINT,SIG_IGN); + if(!locknum) + Sprintf(lock, "_%u%s", (unsigned)getuid(), plname); + getlock(); +#ifdef WIZARD + } else { + Sprintf(lock, "_%u%s", (unsigned)getuid(), plname); + getlock(); + } +#endif /* WIZARD */ + + dlb_init(); /* must be before newgame() */ + + /* + * Initialization of the boundaries of the mazes + * Both boundaries have to be even. + */ + x_maze_max = COLNO-1; + if (x_maze_max % 2) + x_maze_max--; + y_maze_max = ROWNO-1; + if (y_maze_max % 2) + y_maze_max--; + + /* + * Initialize the vision system. This must be before mklev() on a + * new game or before a level restore on a saved game. + */ + vision_init(); + + display_gamewindows(); + + if ((fd = restore_saved_game()) >= 0) { +#ifdef WIZARD + /* Since wizard is actually flags.debug, restoring might + * overwrite it. + */ + boolean remember_wiz_mode = wizard; +#endif + const char *fq_save = fqname(SAVEF, SAVEPREFIX, 1); + + (void) chmod(fq_save,0); /* disallow parallel restores */ + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#ifdef NEWS + if(iflags.news) { + display_file(NEWS, FALSE); + iflags.news = FALSE; /* in case dorecover() fails */ + } +#endif + pline("Restoring save file..."); + mark_synch(); /* flush output */ + if(!dorecover(fd)) + goto not_recovered; +#ifdef WIZARD + if(!wizard && remember_wiz_mode) wizard = TRUE; +#endif + check_special_room(FALSE); + wd_message(); + + if (discover || wizard) { + if (yn("Do you want to keep the save file?") == 'n') + (void) delete_savefile(); + else + (void) chmod(fq_save,FCMASK); /* back to readable */ + } + + flags.move = 0; + } else { +not_recovered: + player_selection(); + newgame(); + wd_message(); + + flags.move = 0; + set_wear(); + (void) pickup(1); + } + + moveloop(); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return(0); +} + +static void +process_options(argc, argv) +int argc; +char *argv[]; +{ + int i; + + + /* + * Process options. + */ + while(argc > 1 && argv[1][0] == '-'){ + argv++; + argc--; + switch(argv[0][1]){ + case 'D': +#ifdef WIZARD + if(!strcmpi(nh_getenv("USER"), WIZARD_NAME)) { + wizard = TRUE; + break; + } + /* otherwise fall thru to discover */ + wiz_error_flag = TRUE; +#endif /* WIZARD */ + case 'X': + case 'x': + discover = TRUE; + break; +#ifdef NEWS + case 'n': + iflags.news = FALSE; + break; +#endif + case 'u': + if(argv[0][2]) + (void) strncpy(plname, argv[0]+2, sizeof(plname)-1); + else if(argc > 1) { + argc--; + argv++; + (void) strncpy(plname, argv[0], sizeof(plname)-1); + } else + raw_print("Player name expected after -u"); + break; + case 'I': + case 'i': + if (!strncmpi(argv[0]+1, "IBM", 3)) + switch_graphics(IBM_GRAPHICS); + break; + /* case 'D': */ + case 'd': + if (!strncmpi(argv[0]+1, "DEC", 3)) + switch_graphics(DEC_GRAPHICS); + break; + case 'p': /* profession (role) */ + if (argv[0][2]) { + if ((i = str2role(&argv[0][2])) >= 0) + flags.initrole = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2role(argv[0])) >= 0) + flags.initrole = i; + } + break; + case 'r': /* race */ + if (argv[0][2]) { + if ((i = str2race(&argv[0][2])) >= 0) + flags.initrace = i; + } else if (argc > 1) { + argc--; + argv++; + if ((i = str2race(argv[0])) >= 0) + flags.initrace = i; + } + break; + case '@': + flags.randomall = 1; + break; + default: + if ((i = str2role(&argv[0][1])) >= 0) { + flags.initrole = i; + break; + } + /* else raw_printf("Unknown option: %s", *argv); */ + } + } + + if(argc > 1) + locknum = atoi(argv[1]); +#ifdef MAX_NR_OF_PLAYERS + if(!locknum || locknum > MAX_NR_OF_PLAYERS) + locknum = MAX_NR_OF_PLAYERS; +#endif +} + +#ifdef CHDIR +void +chdirx(dir, wr) +const char *dir; +boolean wr; +{ +# ifndef HACKDIR + static const char *defdir = "."; +# else + static const char *defdir = HACKDIR; + + if(dir == (const char *)0) + dir = defdir; + else if (wr && !same_dir(HACKDIR, dir)) + /* If we're playing anywhere other than HACKDIR, turn off any + privs we may have been installed with. */ + privoff(); +# endif + + if(dir && chdir(dir) < 0) { + perror(dir); + error("Cannot chdir to %s.", dir); + } + + /* warn the player if we can't write the record file */ + if (wr) check_recordfile(dir); + + defdir = dir; +} +#endif /* CHDIR */ + +static void +whoami() +{ + /* + * Who am i? Algorithm: 1. Use name as specified in NETHACKOPTIONS + * 2. Use lowercase of $USER (if 1. fails) + * The resulting name is overridden by command line options. + * If everything fails, or if the resulting name is some generic + * account like "games" then eventually we'll ask him. + * Note that we trust the user here; it is possible to play under + * somebody else's name. + */ + register char *s; + + if (!*plname && (s = nh_getenv("USER"))) + (void) lcase(strncpy(plname, s, sizeof(plname)-1)); +} + +static void +byebye() +{ + /* Different versions of both VAX C and GNU C use different return types + for signal functions. Return type 'int' along with the explicit casts + below satisfy the most combinations of compiler vs . + */ + int (*hup)(); +#ifdef SHELL + extern unsigned long dosh_pid, mail_pid; + extern unsigned long FDECL(sys$delprc,(unsigned long *,const genericptr_t)); + + /* clean up any subprocess we've spawned that may still be hanging around */ + if (dosh_pid) (void) sys$delprc(&dosh_pid, 0), dosh_pid = 0; + if (mail_pid) (void) sys$delprc(&mail_pid, 0), mail_pid = 0; +#endif + + /* SIGHUP doesn't seem to do anything on VMS, so we fudge it here... */ + hup = (int(*)()) signal(SIGHUP, SIG_IGN); + if (!program_state.exiting++ && + hup != (int(*)()) SIG_DFL && hup != (int(*)()) SIG_IGN) + (void) (*hup)(); + +#ifdef CHDIR + (void) chdir(getenv("PATH")); +#endif +} + +#ifndef SAVE_ON_FATAL_ERROR +/* Condition handler to prevent byebye's hangup simulation + from saving the game after a fatal error has occurred. */ +/*ARGSUSED*/ +static vms_handler_type /* should be `unsigned long', but the -*/ +vms_handler(sigargs, mechargs) /*+ prototype in is screwed */ +genericptr_t sigargs, mechargs; /* [0] is argc, [1..argc] are the real args */ +{ + unsigned long condition = ((unsigned long *)sigargs)[1]; + + if (condition == SS$_ACCVIO /* access violation */ + || (condition >= SS$_ASTFLT && condition <= SS$_TBIT) + || (condition >= SS$_ARTRES && condition <= SS$_INHCHME)) { + program_state.done_hup = TRUE; /* pretend hangup has been attempted */ +# if defined(WIZARD) && !defined(BETA) + if (wizard) +# endif /*WIZARD && !BETA*/ +# if defined(WIZARD) || defined(BETA) + abort(); /* enter the debugger */ +# endif /*WIZARD || BETA*/ + } + return SS$_RESIGNAL; +} +#endif + +#ifdef PORT_HELP +void +port_help() +{ + /* + * Display VMS-specific help. Just show contents of the helpfile + * named by PORT_HELP. + */ + display_file(PORT_HELP, TRUE); +} +#endif /* PORT_HELP */ + +static void +wd_message() +{ +#ifdef WIZARD + if (wiz_error_flag) { + pline("Only user \"%s\" may access debug (wizard) mode.", + WIZARD_NAME); + pline("Entering discovery mode instead."); + } else +#endif + if (discover) + You("are in non-scoring discovery mode."); +} + +/*vmsmain.c*/ diff --git a/sys/vms/vmsmisc.c b/sys/vms/vmsmisc.c new file mode 100644 index 0000000..573d9c4 --- /dev/null +++ b/sys/vms/vmsmisc.c @@ -0,0 +1,30 @@ +/* SCCS Id: @(#)vmsmisc.c 3.4 1996/03/02 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include + +void vms_exit( /*_ int _*/ ); +void vms_abort( /*_ void _*/ ); + +extern void exit( /*_ int _*/ ); +extern void lib$signal( /*_ unsigned long,... _*/ ); + +void +vms_exit(status) +int status; +{ + exit(status ? (SS$_ABORT | STS$M_INHIB_MSG) : SS$_NORMAL); +} + +void +vms_abort() +{ + lib$signal(SS$_DEBUG); +} + +#ifdef VERYOLD_VMS +#include "oldcrtl.c" +#endif + +/*vmsmisc.c*/ diff --git a/sys/vms/vmstty.c b/sys/vms/vmstty.c new file mode 100644 index 0000000..314a912 --- /dev/null +++ b/sys/vms/vmstty.c @@ -0,0 +1,491 @@ +/* SCCS Id: @(#)vmstty.c 3.4 2003/09/18 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ +/* tty.c - (VMS) version */ + +#define NEED_VARARGS +#include "hack.h" +#include "wintty.h" +#include "tcap.h" + +#include +#include +#ifndef __GNUC__ +#include +#include +#include +#else /* values needed from missing include files */ +# define SMG$K_TRM_UP 274 +# define SMG$K_TRM_DOWN 275 +# define SMG$K_TRM_LEFT 276 +# define SMG$K_TRM_RIGHT 277 +# define TT$M_MECHTAB 0x00000100 /* hardware tab support */ +# define TT$M_MECHFORM 0x00080000 /* hardware form-feed support */ +# define TT$M_NOBRDCST 0x00020000 /* disable broadcast messages, but */ +# define TT2$M_BRDCSTMBX 0x00000010 /* catch them in associated mailbox */ +# define TT2$M_APP_KEYPAD 0x00800000 /* application vs numeric keypad mode */ +#endif /* __GNUC__ */ +#ifdef USE_QIO_INPUT +#include +#endif +#include +#include + +unsigned long lib$disable_ctrl(), lib$enable_ctrl(); +unsigned long sys$assign(), sys$dassgn(), sys$qiow(); +#ifndef USE_QIO_INPUT +unsigned long smg$create_virtual_keyboard(), smg$delete_virtual_keyboard(), + smg$read_keystroke(), smg$cancel_input(); +#else +static short FDECL(parse_function_key, (int)); +#endif +static void NDECL(setctty); +static void NDECL(resettty); + +#define vms_ok(sts) ((sts)&1) +#define META(c) ((c)|0x80) /* 8th bit */ +#define CTRL(c) ((c)&0x1F) +#define CMASK(c) (1< 0) { + /* we have buffered character(s) from previous read */ + kb_buf = *inp++; + --inc; + sts = SS$_NORMAL; + } else { + sts = sys$qiow(0, tt_chan, QIO_FUNC, &iosb, (void(*)())0, 0, + &kb_buf, sizeof kb_buf, 0, 0, 0, 0); + } + if (vms_ok(sts)) { + if (kb_buf == CTRL('C')) { + if (intr_char) gsignal(SIGINT); + key = (short)kb_buf; + } else if (kb_buf == '\r') { /* */ + key = (short)'\n'; + } else if (kb_buf == ESC || kb_buf == CSI || kb_buf == SS3) { + switch(parse_function_key((int)kb_buf)) { + case SMG$K_TRM_UP: key = iflags.num_pad ? '8' : 'k'; break; + case SMG$K_TRM_DOWN: key = iflags.num_pad ? '2' : 'j'; break; + case SMG$K_TRM_LEFT: key = iflags.num_pad ? '4' : 'h'; break; + case SMG$K_TRM_RIGHT: key = iflags.num_pad ? '6' : 'l'; break; + default: key = ESC; break; + } + } else { + key = (short)kb_buf; + } + } else if (sts == SS$_HANGUP || iosb.status == SS$_HANGUP + || sts == SS$_DEVOFFLINE) { + gsignal(SIGHUP); + key = ESC; + } else /*(this should never happen)*/ + key = getchar(); + +#else /*!USE_QIO_INPUT*/ + if (recurse++ == 0 && kb != 0) { + smg$read_keystroke(&kb, &key); + switch (key) { + case SMG$K_TRM_UP: iflags.num_pad ? '8' : key = 'k'; break; + case SMG$K_TRM_DOWN: iflags.num_pad ? '2' : key = 'j'; break; + case SMG$K_TRM_LEFT: iflags.num_pad ? '4' : key = 'h'; break; + case SMG$K_TRM_RIGHT: iflags.num_pad ? '6' : key = 'l'; break; + case '\r': key = '\n'; break; + default: if (key > 255) key = ESC; + break; + } + } else { + /* abnormal input--either SMG didn't initialize properly or + vms_getchar() has been called recursively (via SIGINT handler). + */ + if (kb != 0) /* must have been a recursive call */ + smg$cancel_input(&kb); /* from an interrupt handler */ + key = getchar(); + } + --recurse; +#endif /* USE_QIO_INPUT */ + + return (int)key; +} + +#ifdef USE_QIO_INPUT + /* + * We've just gotten an character. Do a timed read to + * get any other characters, then try to parse them as an escape + * sequence. This isn't perfect, since there's no guarantee + * that a full escape sequence will be available, or even if one + * is, it might actually by regular input from a fast typist or + * a stalled input connection. {For packetized environments, + * cross plural(body_part(FINGER)) and hope for best. :-} + * + * This is needed to preserve compatability with SMG interface + * for two reasons: + * 1) retain support for arrow keys, and + * 2) treat other VTxxx function keys as for aborting + * various NetHack prompts. + * The second reason is compelling; otherwise remaining chars of + * an escape sequence get treated as inappropriate user commands. + * + * SMG code values for these key sequences fall in the range of + * 256 thru 3xx. The assignments are not particularly intuitive. + */ +/*= + -- Summary of VTxxx-style keyboards and transmitted escape sequences. -- +Keypad codes are prefixed by 7 bit (\033 O) or 8 bit SS3: + keypad: PF1 PF2 PF3 PF4 codes: P Q R S + 7 8 9 - w x y m + 4 5 6 . t u v n + 1 2 3 :en-: q r s : : + ...0... , :ter: ...p... l :M: +Arrows are prefixed by either SS3 or CSI (either 7 or 8 bit), depending on +whether the terminal is in application or numeric mode (ditto for PF keys): + arrows: A B D C +Additional function keys (vk201/vk401) generate CSI nn ~ (nn is 1 or 2 digits): + vk201 keys: F6 F7 F8 F9 F10 F11 F12 F13 F14 Help Do F17 F18 F19 F20 + 'nn' digits: 17 18 19 20 21 23 24 25 26 28 29 31 32 33 34 + alternate: ^C ^[ ^H ^J (when in VT100 mode) + edit keypad: digits: 1 2 3 + 4 5 6 +VT52 mode: arrows and PF keys send ESCx where x is in A-D or P-S. +=*/ + +static const char *arrow_or_PF = "ABCDPQRS", /* suffix char */ + *smg_keypad_codes = "PQRSpqrstuvwxyMmlnABDC"; + /* PF1..PF4,KP0..KP9,enter,dash,comma,dot,up-arrow,down,left,right */ + /* Ultimate return value is (index into smg_keypad_codes[] + 256). */ + +static short +parse_function_key(c) +register int c; +{ + struct _rd_iosb iosb; + unsigned long sts; + char seq_buf[15+1]; /* plenty room for escape sequence + slop */ + short result = ESC; /* translate to by default */ + + /* + * Read whatever we can from type-ahead buffer (1 second timeout). + * If the user typed an actual to deliberately abort + * something, he or she should be able to tolerate the necessary + * restriction of a negligible pause before typing anything else. + * We might already have [at least some of] an escape sequence from a + * previous read, particularly if user holds down the arrow keys... + */ + if (inc > 0) strncpy(seq_buf, inp, inc); + if (inc < (int)(sizeof seq_buf) - 1) { + sts = sys$qiow(0, tt_chan, QIO_FUNC|IO$M_TIMED, &iosb, (void(*)())0, 0, + seq_buf + inc, sizeof seq_buf - 1 - inc, 1, 0, 0, 0); + if (vms_ok(sts)) sts = iosb.status; + } else + sts = SS$_NORMAL; + if (vms_ok(sts) || sts == SS$_TIMEOUT) { + register int cnt = iosb.trm_offset + iosb.trm_siz + inc; + register char *p = seq_buf; + if (c == ESC) /* check for 7-bit vt100/ANSI, or vt52 */ + if (*p == '[' || *p == 'O') c = META(CTRL(*p++)), cnt--; + else if (strchr(arrow_or_PF, *p)) c = SS3; /*CSI*/ + if (cnt > 0 && (c == SS3 || (c == CSI && strchr(arrow_or_PF, *p)))) { + register char *q = strchr(smg_keypad_codes, *p); + if (q) result = 256 + (q - smg_keypad_codes); + p++, --cnt; /* one more char consumed */ + } else if (cnt > 1 && c == CSI) { + static short /* "CSI nn ~" -> F_keys[nn] */ + F_keys[35] = { ESC, /*(filler)*/ + 311, 312, 313, 314, 315, 316, /* E1-E6 */ + ESC, ESC, ESC, ESC, /*(more filler)*/ + 281, 282, 283, 284, 285, ESC, /* F1-F5 */ + 286, 287, 288, 289, 290, ESC, /* F6-F10*/ + 291, 292, 293, 294, ESC, /*F11-F14*/ + 295, 296, ESC, /*,, aka F15,F16*/ + 297, 298, 299, 300 /*F17-F20*/ + }; /* note: there are several missing nn in CSI nn ~ values */ + int nn; char *q; + *(p + cnt) = '\0'; /* terminate string */ + q = strchr(p, '~'); + if (q && sscanf(p, "%d~", &nn) == 1) { + if (nn > 0 && nn < SIZE(F_keys)) result = F_keys[nn]; + cnt -= (++q - p); + p = q; + } + } + if (cnt > 0) strncpy((inp = inputbuf), p, (inc = cnt)); + else inc = 0, inp = 0; + } + return result; +} +#endif /* USE_QIO_INPUT */ + +static void +setctty() +{ + struct _sm_iosb iosb; + unsigned long status; + + status = sys$qiow(0, tt_chan, IO$_SETMODE, &iosb, (void(*)())0, 0, + &sg.sm, sizeof sg.sm, 0, 0, 0, 0); + if (vms_ok(status)) status = iosb.status; + if (vms_ok(status)) { + /* try to force terminal into synch with TTDRIVER's setting */ + number_pad((sg.sm.tt2_char & TT2$M_APP_KEYPAD) ? -1 : 1); + } else { + raw_print(""); + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(setctty: setmode)"); + wait_synch(); + } +} + +static void +resettty() /* atexit() routine */ +{ + if (settty_needed) { + bombing = TRUE; /* don't clear screen; preserve traceback info */ + settty((char *)0); + } + (void) sys$dassgn(tt_chan), tt_chan = 0; +} + +/* + * Get initial state of terminal, set ospeed (for termcap routines) + * and switch off tab expansion if necessary. + * Called by init_nhwindows() and resume_nhwindows() in wintty.c + * (for initial startup and for returning from '!' or ^Z). + */ +void +gettty() +{ + static char dev_tty[] = "TT:"; + static $DESCRIPTOR(tty_dsc, dev_tty); + int err = 0; + unsigned long status, zero = 0; + + if (tt_chan == 0) { /* do this stuff once only */ + iflags.cbreak = OFF, iflags.echo = ON; /* until setup is complete */ + status = sys$assign(&tty_dsc, &tt_chan, 0, 0); + if (!vms_ok(status)) { + raw_print(""), err++; + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(gettty: $assign)"); + } + atexit(resettty); /* register an exit handler to reset things */ + } + status = sys$qiow(0, tt_chan, IO$_SENSEMODE, &sg.io, (void(*)())0, 0, + &sg.sm, sizeof sg.sm, 0, 0, 0, 0); + if (vms_ok(status)) status = sg.io.status; + if (!vms_ok(status)) { + raw_print(""), err++; + errno = EVMSERR, vaxc$errno = status; + perror("NetHack(gettty: sensemode)"); + } + ospeed = sg.io.xmt_speed; + erase_char = '\177'; /* , aka */ + kill_char = CTRL('U'); + intr_char = CTRL('C'); + (void) lib$enable_ctrl(&zero, &ctrl_mask); + /* Use the systems's values for lines and columns if it has any idea. */ + if (sg.sm.page_length) + LI = sg.sm.page_length; + if (sg.sm.page_width) + CO = sg.sm.page_width; + /* suppress tab and form-feed expansion, in case termcap uses them */ + tt_char_restore = sg.sm.tt_char; + tt_char_active = sg.sm.tt_char |= TT_SPECIAL_HANDLING; + tt2_char_restore = sg.sm.tt2_char; + tt2_char_active = sg.sm.tt2_char |= TT2_SPECIAL_HANDLING; +#if 0 /*[ defer until setftty() ]*/ + setctty(); +#endif + + if (err) wait_synch(); +} + +/* reset terminal to original state */ +void +settty(s) +const char *s; +{ + if (!bombing) end_screen(); + if (s) raw_print(s); + disable_broadcast_trapping(); +#if 0 /* let SMG's exit handler do the cleanup (as per doc) */ +/* #ifndef USE_QIO_INPUT */ + if (kb) smg$delete_virtual_keyboard(&kb), kb = 0; +#endif /* 0 (!USE_QIO_INPUT) */ + if (ctrl_mask) + (void) lib$enable_ctrl(&ctrl_mask, 0); + iflags.echo = ON; + iflags.cbreak = OFF; + /* reset original tab, form-feed, broadcast settings */ + sg.sm.tt_char = tt_char_restore; + sg.sm.tt2_char = tt2_char_restore; + setctty(); + + settty_needed = FALSE; +} + +/* same as settty, with no clearing of the screen */ +void +shuttty(s) +const char *s; +{ + bombing = TRUE; + settty(s); + bombing = FALSE; +} + +void +setftty() +{ + unsigned long mask = LIB$M_CLI_CTRLT | LIB$M_CLI_CTRLY; + + (void) lib$disable_ctrl(&mask, 0); + if (kb == 0) { /* do this stuff once only */ +#ifdef USE_QIO_INPUT + kb = tt_chan; +#else /*!USE_QIO_INPUT*/ + smg$create_virtual_keyboard(&kb); +#endif /*USE_QIO_INPUT*/ + init_broadcast_trapping(); + } + enable_broadcast_trapping(); /* no-op if !defined(MAIL) */ + iflags.cbreak = (kb != 0) ? ON : OFF; + iflags.echo = (kb != 0) ? OFF : ON; + /* disable tab & form-feed expansion; prepare for broadcast trapping */ + sg.sm.tt_char = tt_char_active; + sg.sm.tt2_char = tt2_char_active; + setctty(); + + start_screen(); + settty_needed = TRUE; +} + +void +intron() /* enable kbd interupts if enabled when game started */ +{ + intr_char = CTRL('C'); +} + +void +introff() /* disable kbd interrupts if required*/ +{ + intr_char = 0; +} + +#ifdef TIMED_DELAY + +extern unsigned long + FDECL(lib$emul, (const long *,const long *,const long *,long *)); +extern unsigned long sys$schdwk(), sys$hiber(); + +#define VMS_UNITS_PER_SECOND 10000000L /* hundreds of nanoseconds, 1e-7 */ +/* constant for conversion from milliseconds to VMS delta time (negative) */ +static const long mseconds_to_delta = VMS_UNITS_PER_SECOND / 1000L * -1L; + +/* sleep for specified number of milliseconds (note: the timer used + generally only has 10-millisecond resolution at the hardware level...) */ +void msleep(mseconds) +unsigned mseconds; /* milliseconds */ +{ + long pid = 0L, zero = 0L, msec, qtime[2]; + + msec = (long) mseconds; + if (msec > 0 && + /* qtime{0:63} = msec{0:31} * mseconds_to_delta{0:31} + zero{0:31} */ + vms_ok(lib$emul(&msec, &mseconds_to_delta, &zero, qtime))) { + /* schedule a wake-up call, then go to sleep */ + if (vms_ok(sys$schdwk(&pid, (genericptr_t)0, qtime, (long *)0))) + (void)sys$hiber(); + } +} + +#endif /* TIMED_DELAY */ + + +/* fatal error */ +/*VARARGS1*/ +void +error VA_DECL(const char *,s) + VA_START(s); + VA_INIT(s, const char *); + if(settty_needed) + settty((char *)0); + Vprintf(s,VA_ARGS); + (void) putchar('\n'); + VA_END(); +#ifndef SAVE_ON_FATAL_ERROR + /* prevent vmsmain's exit handler byebye() from calling hangup() */ + (void)signal(SIGHUP, SIG_DFL); +#endif + exit(EXIT_FAILURE); +} diff --git a/sys/vms/vmsunix.c b/sys/vms/vmsunix.c new file mode 100644 index 0000000..fdaa53c --- /dev/null +++ b/sys/vms/vmsunix.c @@ -0,0 +1,474 @@ +/* SCCS Id: @(#)vmsunix.c 3.4 2001/07/27 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* This file implements things from unixunix.c, plus related stuff */ + +#include "hack.h" + +#include +#include +#include +#include +#include +#include +#undef off_t +#ifdef GNUC +#include +#else +# define umask hide_umask_dummy /* DEC C: avoid conflict with system.h */ +#include +# undef umask +#endif +#include + +extern unsigned long sys$setprv(); +extern unsigned long lib$getdvi(), lib$getjpi(), lib$spawn(), lib$attach(); +extern unsigned long smg$init_term_table_by_type(), smg$del_term_table(); +#define vms_ok(sts) ((sts) & 1) /* odd => success */ + +static int FDECL(veryold, (int)); +static char *NDECL(verify_term); +#if defined(SHELL) || defined(SUSPEND) +static void FDECL(hack_escape, (BOOLEAN_P,const char *)); +static void FDECL(hack_resume, (BOOLEAN_P)); +#endif + +static int +veryold(fd) +int fd; +{ + register int i; + time_t date; + struct stat buf; + + if(fstat(fd, &buf)) return(0); /* cannot get status */ +#ifndef INSURANCE + if(buf.st_size != sizeof(int)) return(0); /* not an xlock file */ +#endif + (void) time(&date); + if(date - buf.st_mtime < 3L*24L*60L*60L) { /* recent */ + int lockedpid; /* should be the same size as hackpid */ + unsigned long status, dummy, code = JPI$_PID; + + if (read(fd, (genericptr_t)&lockedpid, sizeof(lockedpid)) != + sizeof(lockedpid)) /* strange ... */ + return 0; + status = lib$getjpi(&code, &lockedpid, 0, &dummy); + if (vms_ok(status) || status != SS$_NONEXPR) + return 0; + } + (void) close(fd); + + /* cannot use maxledgerno() here, because we need to find a lock name + * before starting everything (including the dungeon initialization + * that sets astral_level, needed for maxledgerno()) up + */ + for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) { + /* try to remove all */ + set_levelfile_name(lock, i); + (void) delete(lock); + } + set_levelfile_name(lock, 0); + if(delete(lock)) return(0); /* cannot remove it */ + return(1); /* success! */ +} + +void +getlock() +{ + register int i = 0, fd; + + /* idea from rpick%ucqais@uccba.uc.edu + * prevent automated rerolling of characters + * test input (fd0) so that tee'ing output to get a screen dump still + * works + * also incidentally prevents development of any hack-o-matic programs + */ + if (isatty(0) <= 0) + error("You must play from a terminal."); + + /* we ignore QUIT and INT at this point */ + if (!lock_file(HLOCK, LOCKPREFIX, 10)) { + wait_synch(); + error("Quitting."); + } + + regularize(lock); + set_levelfile_name(lock, 0); + if(locknum > 25) locknum = 25; + + do { + if(locknum) lock[0] = 'a' + i++; + + if((fd = open(lock, 0, 0)) == -1) { + if(errno == ENOENT) goto gotlock; /* no such file */ + perror(lock); + unlock_file(HLOCK); + error("Cannot open %s", lock); + } + + if(veryold(fd)) /* if true, this closes fd and unlinks lock */ + goto gotlock; + (void) close(fd); + } while(i < locknum); + + unlock_file(HLOCK); + error(locknum ? "Too many hacks running now." + : "There is a game in progress under your name."); + +gotlock: + fd = creat(lock, FCMASK); + unlock_file(HLOCK); + if(fd == -1) { + error("cannot creat lock file."); + } else { + if(write(fd, (char *) &hackpid, sizeof(hackpid)) + != sizeof(hackpid)){ + error("cannot write lock"); + } + if(close(fd) == -1) { + error("cannot close lock"); + } + } +} + +void +regularize(s) /* normalize file name */ +register char *s; +{ + register char *lp; + + for (lp = s; *lp; lp++) /* note: '-' becomes '_' */ + if (!(isalpha(*lp) || isdigit(*lp) || *lp == '$')) + *lp = '_'; +} + +#undef getuid +int +vms_getuid() +{ + return (getgid() << 16) | getuid(); +} + +#ifndef FAB$C_STMLF +#define FAB$C_STMLF 5 +#endif +/* check whether the open file specified by `fd' is in stream-lf format */ +boolean +file_is_stmlf(fd) +int fd; +{ + int rfm; + struct stat buf; + + if (fstat(fd, &buf)) return FALSE; /* cannot get status? */ + +#ifdef stat_alignment_fix /* gcc-vms alignment kludge */ + rfm = stat_alignment_fix(&buf)->st_fab_rfm; +#else + rfm = buf.st_fab_rfm; +#endif + return rfm == FAB$C_STMLF; +} + +/*------*/ +#ifndef LNM$_STRING +#include /* logical name definitions */ +#endif +#define ENVSIZ LNM$C_NAMLENGTH /*255*/ + +#define ENV_USR 0 /* user-mode */ +#define ENV_SUP 1 /* supervisor-mode */ +#define ENV_JOB 2 /* job-wide entry */ + +/* vms_define() - assign a value to a logical name */ +int +vms_define(name, value, flag) +const char *name; +const char *value; +int flag; +{ + struct dsc { unsigned short len, mbz; const char *adr; }; /* descriptor */ + struct itm3 { short buflen, itmcode; const char *bufadr; short *retlen; }; + static struct itm3 itm_lst[] = { {0,LNM$_STRING,0,0}, {0,0} }; + struct dsc nam_dsc, val_dsc, tbl_dsc; + unsigned long result, sys$crelnm(), lib$set_logical(); + + /* set up string descriptors */ + nam_dsc.mbz = val_dsc.mbz = tbl_dsc.mbz = 0; + nam_dsc.len = strlen( nam_dsc.adr = name ); + val_dsc.len = strlen( val_dsc.adr = value ); + tbl_dsc.len = strlen( tbl_dsc.adr = "LNM$PROCESS" ); + + switch (flag) { + case ENV_JOB: /* job logical name */ + tbl_dsc.len = strlen( tbl_dsc.adr = "LNM$JOB" ); + /*FALLTHRU*/ + case ENV_SUP: /* supervisor-mode process logical name */ + result = lib$set_logical(&nam_dsc, &val_dsc, &tbl_dsc); + break; + case ENV_USR: /* user-mode process logical name */ + itm_lst[0].buflen = val_dsc.len; + itm_lst[0].bufadr = val_dsc.adr; + result = sys$crelnm(0, &tbl_dsc, &nam_dsc, 0, itm_lst); + break; + default: /*[ bad input ]*/ + result = 0; + break; + } + result &= 1; /* odd => success (== 1), even => failure (== 0) */ + return !result; /* 0 == success, 1 == failure */ +} + +/* vms_putenv() - create or modify an environment value */ +int +vms_putenv(string) +const char *string; +{ + char name[ENVSIZ+1], value[ENVSIZ+1], *p; /* [255+1] */ + + p = strchr(string, '='); + if (p > string && p < string + sizeof name && strlen(p+1) < sizeof value) { + (void)strncpy(name, string, p - string), name[p - string] = '\0'; + (void)strcpy(value, p+1); + return vms_define(name, value, ENV_USR); + } else + return 1; /* failure */ +} + +/* + Support for VT420 was added to VMS in version V5.4, but as of V5.5-2 + VAXCRTL still doesn't handle it and puts TERM=undefined into the + environ[] array. getenv("TERM") will return "undefined" instead of + something sensible. Even though that's finally fixed in V6.0, site + defined terminals also return "undefined" so query SMG's TERMTABLE + instead of just checking VMS's device-type value for VT400_Series. + + Called by verify_termcap() for convenience. + */ +static +char *verify_term() +{ + char *term = getenv("NETHACK_TERM"); + if (!term) term = getenv("HACK_TERM"); + if (!term) term = getenv("EMACS_TERM"); + if (!term) term = getenv("TERM"); + if (!term || !*term + || !strcmpi(term, "undefined") || !strcmpi(term, "unknown")) { + static char smgdevtyp[31+1]; /* size is somewhat arbitrary */ + static char dev_tty[] = "TT:"; + static $DESCRIPTOR(smgdsc, smgdevtyp); + static $DESCRIPTOR(tt, dev_tty); + unsigned short dvicode = DVI$_DEVTYPE; + unsigned long devtype = 0L, termtab = 0L; + + (void)lib$getdvi(&dvicode, (unsigned short *)0, &tt, &devtype, + (genericptr_t)0, (unsigned short *)0); + + if (devtype && + vms_ok(smg$init_term_table_by_type(&devtype, &termtab, &smgdsc))) { + register char *p = &smgdevtyp[smgdsc.dsc$w_length]; + /* strip trailing blanks */ + while (p > smgdevtyp && *--p == ' ') *p = '\0'; + /* (void)smg$del_term_table(); */ + term = smgdevtyp; + } + } + return term; +} + +/* + Figure out whether the termcap code will find a termcap file; if not, + try to help it out. This avoids modifying the GNU termcap sources and + can simplify configuration for sites which don't already use termcap. + */ +#define GNU_DEFAULT_TERMCAP "emacs_library:[etc]termcap.dat" +#define NETHACK_DEF_TERMCAP "nethackdir:termcap" +#define HACK_DEF_TERMCAP "hackdir:termcap" + +char * +verify_termcap() /* called from startup(src/termcap.c) */ +{ + struct stat dummy; + const char *tc = getenv("TERMCAP"); + if (tc) return verify_term(); /* no termcap fixups needed */ + if (!tc && !stat(NETHACK_DEF_TERMCAP, &dummy)) tc = NETHACK_DEF_TERMCAP; + if (!tc && !stat(HACK_DEF_TERMCAP, &dummy)) tc = HACK_DEF_TERMCAP; + if (!tc && !stat(GNU_DEFAULT_TERMCAP, &dummy)) tc = GNU_DEFAULT_TERMCAP; + if (!tc && !stat("[]termcap", &dummy)) tc = "[]termcap"; /* current dir */ + if (!tc && !stat("$TERMCAP", &dummy)) tc = "$TERMCAP"; /* alt environ */ + if (tc) { + /* putenv(strcat(strcpy(buffer,"TERMCAP="),tc)); */ + vms_define("TERMCAP", tc, ENV_USR); + } else { + /* perhaps someday we'll construct a termcap entry string */ + } + return verify_term(); +} +/*------*/ + +#ifdef SHELL +# ifndef CLI$M_NOWAIT +# define CLI$M_NOWAIT 1 +# endif +#endif + +#if defined(CHDIR) || defined(SHELL) || defined(SECURE) +static unsigned long oprv[2]; + +void +privoff() +{ + unsigned long pid = 0, prv[2] = { ~0, ~0 }; + unsigned short code = JPI$_PROCPRIV; + + (void) sys$setprv(0, prv, 0, oprv); + (void) lib$getjpi(&code, &pid, (genericptr_t)0, prv); + (void) sys$setprv(1, prv, 0, (unsigned long *)0); +} + +void +privon() +{ + (void) sys$setprv(1, oprv, 0, (unsigned long *)0); +} +#endif /* CHDIR || SHELL || SECURE */ + +#if defined(SHELL) || defined(SUSPEND) +static void +hack_escape(screen_manip, msg_str) +boolean screen_manip; +const char *msg_str; +{ + if (screen_manip) + suspend_nhwindows(msg_str); /* clear screen, reset terminal, &c */ + (void) signal(SIGQUIT,SIG_IGN); /* ignore ^Y */ + (void) signal(SIGINT,SIG_DFL); /* don't trap ^C (implct cnvrs to ^Y) */ +} + +static void +hack_resume(screen_manip) +boolean screen_manip; +{ + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +# ifdef WIZARD + if (wizard) (void) signal(SIGQUIT,SIG_DFL); +# endif + if (screen_manip) + resume_nhwindows(); /* setup terminal modes, redraw screen, &c */ +} +#endif /* SHELL || SUSPEND */ + +#ifdef SHELL +unsigned long dosh_pid = 0, /* this should cover any interactive escape */ + mail_pid = 0; /* this only covers the last mail or phone; */ +/*(mail & phone commands aren't expected to leave any process hanging around)*/ + +int dosh() +{ + return vms_doshell("", TRUE); /* call for interactive child process */ +} + +/* vms_doshell -- called by dosh() and readmail() */ + +/* If execstring is not a null string, then it will be executed in a spawned */ +/* subprocess, which will then return. It is for handling mail or phone */ +/* interactive commands, which are only available if both MAIL and SHELL are */ +/* #defined, but we don't bother making the support code conditionalized on */ +/* MAIL here, just on SHELL being enabled. */ + +/* Normally, all output from this interaction will be 'piped' to the user's */ +/* screen (SYS$OUTPUT). However, if 'screenoutput' is set to FALSE, output */ +/* will be piped into oblivion. Used for silent phone call rejection. */ + +int +vms_doshell(execstring, screenoutput) +const char *execstring; +boolean screenoutput; +{ + unsigned long status, new_pid, spawnflags = 0; + struct dsc$descriptor_s comstring, *command, *inoutfile = 0; + static char dev_null[] = "_NLA0:"; + static $DESCRIPTOR(nulldevice, dev_null); + + /* Is this an interactive shell spawn, or do we have a command to do? */ + if (execstring && *execstring) { + comstring.dsc$w_length = strlen(execstring); + comstring.dsc$b_dtype = DSC$K_DTYPE_T; + comstring.dsc$b_class = DSC$K_CLASS_S; + comstring.dsc$a_pointer = (char *)execstring; + command = &comstring; + } else + command = 0; + + /* use asynch subprocess and suppress output iff one-shot command */ + if (!screenoutput) { + spawnflags = CLI$M_NOWAIT; + inoutfile = &nulldevice; + } + + hack_escape(screenoutput, command ? (const char *) 0 : + " \"Escaping\" into a subprocess; LOGOUT to reconnect and resume play. "); + + if (command || !dosh_pid || !vms_ok(status = lib$attach(&dosh_pid))) { +# ifdef CHDIR + (void) chdir(getenv("PATH")); +# endif + privoff(); + new_pid = 0; + status = lib$spawn(command, inoutfile, inoutfile, &spawnflags, + (struct dsc$descriptor_s *) 0, &new_pid); + if (!command) dosh_pid = new_pid; else mail_pid = new_pid; + privon(); +# ifdef CHDIR + chdirx((char *) 0, 0); +# endif + } + + hack_resume(screenoutput); + + if (!vms_ok(status)) { + pline(" Spawn failed. (%%x%08lX) ", status); + mark_synch(); + } + return 0; +} +#endif /* SHELL */ + +#ifdef SUSPEND +/* dosuspend() -- if we're a subprocess, attach to our parent; + * if not, there's nothing we can do. + */ +int +dosuspend() +{ + static long owner_pid = -1; + unsigned long status; + + if (owner_pid == -1) /* need to check for parent */ + owner_pid = getppid(); + if (owner_pid == 0) { + pline( + " No parent process. Use '!' to Spawn, 'S' to Save, or 'Q' to Quit. "); + mark_synch(); + return 0; + } + + /* restore normal tty environment & clear screen */ + hack_escape(1, + " Attaching to parent process; use the ATTACH command to resume play. "); + + status = lib$attach(&owner_pid); /* connect to parent */ + + hack_resume(1); /* resume game tty environment & refresh screen */ + + if (!vms_ok(status)) { + pline(" Unable to attach to parent. (%%x%08lX) ", status); + mark_synch(); + } + return 0; +} +#endif /* SUSPEND */ + +/*vmsunix.c*/ diff --git a/sys/wince/Install.ce b/sys/wince/Install.ce new file mode 100644 index 0000000..206e810 --- /dev/null +++ b/sys/wince/Install.ce @@ -0,0 +1,136 @@ +Copyright (c) Alex Kompel, 2002 +NetHack may be freely redistributed. See license for details. +======================================================================== + Instructions for compiling and installing + NetHack 3.4.3 on a Windows CE or PocketPC system +======================================================================== + Last revision: $Date: 2003/10/14 01:31:21 $ + +Credit for the porting of NetHack to Windows CE goes to Alex Kompel who +initially developed and contributed the port. + +In order to build NetHack for Windows CE, you need *both* of the following: + + o A copy of Microsoft Visual C V6.0 SP3 or later. Things may work with + an earlier version of the compiler, but the current code has not been + tested with an earlier version. + o Embedded Visual C++ 3.0 or later + + +FIRST STEP: + +The first step in building NetHack for Windows CE is to execute +sys/wince/cesetup.bat. + +From the command prompt: + cd sys\wince + cesetup + +From a Windows explorer window: + double-click on cesetup.bat + +A "wince" directory will be created off the top of the NetHack source +tree, and a Microsoft embedded C workspace file will be placed in the +top of the NetHack source tree. + +------------ +| BUILDING | +------------ + +Boostrapping the build process on Windows NT/2000/XP + +1. With the Visual C++ 6.0 tools in your path, + Run "nmake /f bootstrp.mak" from the wince folder. + +Compiling + +2. Start the Embedded Visual C IDE. In the Embedded Visual C IDE + Menus, choose: + File | Open Workspace + +3. Set up for the build. + + o In the Visual C "Open Workspace" dialog box, navigate to the top + of your NetHack source directory tree. + + In there, highlight "wince.vcw" and click on Open. + Once the workspace has been opened, you should see the following + list in the Visual C selection window: + + nethack_hpc files + + nethack_palm_pc files + + nethack_pocket_pc files + + nethack_smartphone files + + o On the Embedded Visual C menus, choose: + Build | Set Active Platform + Select the platform that corresponds to your device: + Palm-size PC 2.11 - palm size PC running Windows CE version 2.11 + Pocket PC - palm-size PC running Windows CE 3.0 and higher (PocketPC) + H/PC Pro 2.11 - handheld computers running Windows CE 2.11 anf higher + Smartphone 2002 - Microsoft SmartPhone device + + o On the Visual C menus again, choose either: + Build | Set Active Configuration + where configuration is one of the following (make sure it matches the platform + you have selected): + nethack_hpc - Win32 (WCE MIPS) HPCRelease - H/PC Pro 2.11 MIPS processor release executable + nethack_hpc - Win32 (WCE x86em) HPCDebug - H/PC Pro 2.11 x86 emulation debug executable + nethack_hpc - Win32 (WCE ARM) HPCRelease - H/PC Pro 2.11 ARM processor release executable + nethack_hpc - Win32 (WCE SH3) HPCRelease - H/PC Pro 2.11 SH3 processor release executable + nethack_hpc - Win32 (WCE x86em) HPCRelease - H/PC Pro 2.11 x86 emulation release executable + nethack_hpc - Win32 (WCE SH4) HPCRelease - H/PC Pro 2.11 SH4 processor release executable + nethack_palm_pc - Win32 (WCE MIPS) PalmPCRelease - Palm-size PC 2.11 MIPS processor release executable + nethack_palm_pc - Win32 (WCE x86em) PalmPCDebug - Palm-size PC 2.11 x86 emulation debug executable + nethack_palm_pc - Win32 (WCE SH3) PalmPCRelease - Palm-size PC 2.11 SH3 processor release executable + nethack_palm_pc - Win32 (WCE x86em) PalmPCRelease - Palm-size PC 2.11 x86 emulation release executable + nethack_pocket_pc - Win32 (WCE MIPS) PocketPCRelease - Pocket PC MIPS processor release executable + nethack_pocket_pc - Win32 (WCE ARM) PocketPCRelease - Pocket PC ARM processor release executable + nethack_pocket_pc - Win32 (WCE x86em) PocketPCRelease - Pocket PC x86 emulation release executable + nethack_pocket_pc - Win32 (WCE x86em) PocketPCDebug - Pocket PC x86 emulation debug executable + nethack_pocket_pc - Win32 (WCE SH3) PocketPCRelease - Pocket PC SH3 processor release executable + nethack_smartphone - Win32 (WCE ARM) SPhoneRelease - Smartphone 2002 ARM processor release executable + nethack_smartphone - Win32 (WCE x86em) SPhoneDebug - Smartphone 2002 x86 emulation debug executable + +Building + +4. Start your build. + + o On the Embedded Visual C menus once again, choose: + Build | Build nethackm.exe + This starts the build. It is likely that the IDE message window + where you are doing the compiling will be occupied for a while. + Notes: + + o You may get a bunch of warnings regarding missing include files in the + beginning of the build process - ignore them. For some reason the tool + that produces these messages ignores preprocessor directives. The actual + build will go just fine. + o Sometimes the compiler chokes on do_wear.c Ignore that - let the build + finish. Then run it again - it will compile just fine. (Seems to be some + sort of bug in EVC++) + +Transfer + +5. Transfer the files and executables to your handheld by extracting the + files into some folder on the CE device - that should do it. + + +Notes + + If you want to use IBMGraphics make sure that you have a proper + font installed on the device that supports OEM character set + (for example, Lucida Console) + + +PROBLEMS + + + If you discover a bug and wish to report it, or if you have comments + or suggestions we recommend using our "Contact Us" web page at: + http://www.nethack.org/common/contact.html + + If you don't have access to the web, or you want to send us a patch + to the NetHack source code feel free to drop us a line c/o: + DevTeam (at) nethack.org + + Happy NetHacking! diff --git a/sys/wince/bootstrp.mak b/sys/wince/bootstrp.mak new file mode 100644 index 0000000..f04eac2 --- /dev/null +++ b/sys/wince/bootstrp.mak @@ -0,0 +1,882 @@ +# SCCS Id: @(#)bootstrp.mak 3.4 2002/03/24 +# Copyright (c) Michael Allison +# +# NetHack Windows CE bootstrap file for MS Visual C++ V6.x and +# above and MS NMAKE +# +# This will: +# - build makedefs +# - +#============================================================================== +# Do not delete the following 3 lines. +# +TARGETOS=BOTH +APPVER=4.0 +!include + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = ..\include # NetHack include files +DAT = ..\dat # NetHack data files +DOC = ..\doc # NetHack documentation files +UTIL = ..\util # Utility source +SRC = ..\src # Main source +SSYS = ..\sys\share # Shared system files +NTSYS = ..\sys\winnt # NT Win32 specific files +TTY = ..\win\tty # window port files (tty) +WIN32 = ..\win\win32 # window port files (WINCE) +WSHR = ..\win\share # Tile support files +SWINCE= ..\wince # wince files +WINCE = ..\wince # wince build area +OBJ = $(WINCE)\ceobj +DLB = $(DAT)\nhdat + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) $(cvarsmt) -I$(INCL) -nologo $(cdebug) $(WINPINC) -DDLB +LFLAGSBASEC = $(linkdebug) /NODEFAULTLIB /INCREMENTAL:NO /RELEASE /NOLOGO -subsystem:console,4.0 $(conlibsmt) +LFLAGSBASEG = $(linkdebug) $(guiflags) $(guilibsmt) comctl32.lib + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASEC) + +LEVCFLAGS= -c -nologo -DWINVER=0x0400 -DWIN32 -D_WIN32 \ + -D_MT -MT -I..\include -nologo -Z7 -Od -DDLB + + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +#.c{$(OBJ)}.o: +# $(cc) $(CFLAGSU) -Fo$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in sys\share +#========================================== + +{$(SSYS)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in sys\winnt +#========================================== + +{$(NTSYS)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + +{$(NTSYS)}.h{$(INCL)}.h: + copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +{$(UTIL)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in win\share +#========================================== + +{$(WSHR)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# copy $< $@ + +#========================================== +# Rules for files in win\tty +#========================================== + +{$(TTY)}.c{$(OBJ)}.o: + $(CC) $(CFLAGSU) -Fo$@ $< + + +#========================================== +# Rules for files in win\win32 +#========================================== + +{$(WIN32)}.c{$(OBJ)}.o: + $(cc) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in sys\wince +#========================================== + +{$(SWINCE)}.c{$(OBJ)}.o: + $(cc) $(CFLAGSU) -Fo$@ $< + +#========================================== +#================ MACROS ================== +#========================================== + +# +# Shorten up the location for some files +# + +O = $(OBJ)^\ + +U = $(UTIL)^\ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +# +# These are not invoked during a normal game build in 3.4.0 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +TEXT_IO32 = $(O)tilete32.o $(O)tiletx32.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o +GIFREADERS32 = $(O)gifrd32.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +DLBOBJ = $(O)dlb.o + +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h \ + $(INCL)\global.h $(INCL)\coord.h $(INCL)\vmsconf.h \ + $(INCL)\system.h $(INCL)\unixconf.h $(INCL)\os2conf.h \ + $(INCL)\micro.h $(INCL)\pcconf.h $(INCL)\tosconf.h \ + $(INCL)\amiconf.h $(INCL)\macconf.h $(INCL)\beconf.h \ + $(INCL)\ntconf.h $(INCL)\nhlan.h $(INCL)\wceconf.h + +HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\align.h \ + $(INCL)\dungeon.h $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\youprop.h $(INCL)\prop.h \ + $(INCL)\permonst.h $(INCL)\monattk.h \ + $(INCL)\monflag.h $(INCL)\mondata.h $(INCL)\pm.h \ + $(INCL)\wintype.h $(INCL)\decl.h $(INCL)\quest.h \ + $(INCL)\spell.h $(INCL)\color.h $(INCL)\obj.h \ + $(INCL)\you.h $(INCL)\attrib.h $(INCL)\monst.h \ + $(INCL)\skills.h $(INCL)\onames.h $(INCL)\timeout.h \ + $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\display.h $(INCL)\engrave.h \ + $(INCL)\rect.h $(INCL)\region.h $(INCL)\winprocs.h \ + $(INCL)\wintty.h $(INCL)\trampoli.h + +LEV_H = $(INCL)\lev.h +DGN_FILE_H = $(INCL)\dgn_file.h +LEV_COMP_H = $(INCL)\lev_comp.h +SP_LEV_H = $(INCL)\sp_lev.h +TILE_H = ..\win\share\tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)\data.base + +#========================================== +#=============== TARGETS ================== +#========================================== + +# +# The default make target (so just typing 'nmake' is useful). +# +default : all + +# +# Everything +# + +all : $(INCL)\date.h $(INCL)\onames.h $(INCL)\pm.h \ + $(SRC)\monstr.c $(SRC)\vis_tab.c $(U)lev_comp.exe $(INCL)\vis_tab.h \ + $(U)dgn_comp.exe $(U)uudecode.exe \ + $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) $(SRC)\tile.c \ + $(SWINCE)\nethack.ico $(SWINCE)\tiles.bmp $(SWINCE)\mnsel.bmp \ + $(SWINCE)\mnunsel.bmp $(SWINCE)\petmark.bmp $(SWINCE)\mnselcnt.bmp \ + $(SWINCE)\keypad.bmp $(SWINCE)\menubar.bmp + @echo Done! + +$(O)sp_lev.tag: $(DAT)\bigroom.des $(DAT)\castle.des \ + $(DAT)\endgame.des $(DAT)\gehennom.des $(DAT)\knox.des \ + $(DAT)\medusa.des $(DAT)\oracle.des $(DAT)\tower.des \ + $(DAT)\yendor.des $(DAT)\arch.des $(DAT)\barb.des \ + $(DAT)\caveman.des $(DAT)\healer.des $(DAT)\knight.des \ + $(DAT)\monk.des $(DAT)\priest.des $(DAT)\ranger.des \ + $(DAT)\rogue.des $(DAT)\samurai.des $(DAT)\sokoban.des \ + $(DAT)\tourist.des $(DAT)\valkyrie.des $(DAT)\wizard.des + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(WINCE) + echo sp_levs done > $(O)sp_lev.tag + +#$(NHRES): $(TILEBMP16) $(WINCE)\winhack.rc $(WINCE)\mnsel.bmp \ +# $(WINCE)\mnselcnt.bmp $(WINCE)\mnunsel.bmp \ +# $(WINCE)\petmark.bmp $(WINCE)\NetHack.ico $(WINCE)\rip.bmp \ +# $(WINCE)\splash.bmp +# $(rc) -r -fo$@ -i$(WINCE) -dNDEBUG $(WINCE)\winhack.rc + +# +# Utility Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + $(link) $(LFLAGSU) -out:$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(INCL)\patchlevel.h \ + $(U)makedefs.c + if not exist $(OBJ)\*.* echo creating directory $(OBJ) + if not exist $(OBJ)\*.* mkdir $(OBJ) + $(CC) $(CFLAGSU) -Fo$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe + $(U)makedefs -v + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + $(link) $(LFLAGSU) -out:$@ $(O)uudecode.o + +$(O)uudecode.o: $(SSYS)\uudecode.c + +$(SWINCE)\NetHack.ico : $(U)uudecode.exe $(SWINCE)\nhico.uu + chdir $(SWINCE) + ..\util\uudecode.exe nhico.uu + chdir $(WINCE) + +$(SWINCE)\mnsel.bmp: $(U)uudecode.exe $(SWINCE)\mnsel.uu + chdir $(SWINCE) + ..\util\uudecode.exe mnsel.uu + chdir $(WINCE) + +$(SWINCE)\mnselcnt.bmp: $(U)uudecode.exe $(SWINCE)\mnselcnt.uu + chdir $(SWINCE) + ..\util\uudecode.exe mnselcnt.uu + chdir $(WINCE) + +$(SWINCE)\mnunsel.bmp: $(U)uudecode.exe $(SWINCE)\mnunsel.uu + chdir $(SWINCE) + ..\util\uudecode.exe mnunsel.uu + chdir $(WINCE) + +$(SWINCE)\petmark.bmp: $(U)uudecode.exe $(SWINCE)\petmark.uu + chdir $(SWINCE) + ..\util\uudecode.exe petmark.uu + chdir $(WINCE) + +$(SWINCE)\rip.bmp: $(U)uudecode.exe $(SWINCE)\rip.uu + chdir $(SWINCE) + ..\util\uudecode.exe rip.uu + chdir $(WINCE) + +$(SWINCE)\splash.bmp: $(U)uudecode.exe $(SWINCE)\splash.uu + chdir $(SWINCE) + ..\util\uudecode.exe splash.uu + chdir $(WINCE) + +$(SWINCE)\keypad.bmp: $(U)uudecode.exe $(SWINCE)\keypad.uu + chdir $(SWINCE) + ..\util\uudecode.exe keypad.uu + chdir $(WINCE) + +$(SWINCE)\menubar.bmp: $(U)uudecode.exe $(SWINCE)\menubar.uu + chdir $(SWINCE) + ..\util\uudecode.exe menubar.uu + chdir $(WINCE) + +#========================================== +# Level Compiler Stuff +#========================================== + +$(U)lev_comp.exe: $(SPLEVOBJS) + echo Linking $@... + $(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(SPLEVOBJS:^ =^ + ) +<< + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y + @echo We will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\lev_yacc.c $(U)lev_yacc.c >nul + @copy $(SSYS)\lev_comp.h $(INCL)\lev_comp.h >nul + @echo /**/ >>$(U)lev_yacc.c + @echo /**/ >>$(INCL)\lev_comp.h + +$(U)lev_$(LEX).c: $(U)lev_comp.l + @echo We will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\lev_lex.c $@ >nul + @echo /**/ >>$@ + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + $(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(DGNCOMPOBJS:^ =^ + ) +<< + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h $(U)dgn_yacc.c + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + $(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y + @echo We will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\dgn_yacc.c $(U)dgn_yacc.c >nul + @copy $(SSYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + @echo /**/ >>$(U)dgn_yacc.c + @echo /**/ >>$(INCL)\dgn_comp.h + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l + @echo We will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\dgn_lex.c $@ >nul + @echo /**/ >>$@ + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + if not exist $(OBJ)\*.* echo creating directory $(OBJ) + if not exist $(OBJ)\*.* mkdir $(OBJ) + echo directory created >$@ + +#========================================== +# Notify of any CL environment variables +# in effect since they change the compiler +# options. +#========================================== + +envchk: +! IF "$(CL)"!="" + @echo Warning, the CL Environment variable is defined: + @echo CL=$(CL) +! ENDIF + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ..\include +#=========================================== + +$(INCL)\win32api.h: $(NTSYS)\win32api.h + copy $(NTSYS)\win32api.h $@ + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o + $(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)dlb_main.o + $(O)dlb.o + $(O)alloc.o + $(O)panic.o +<< + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)\dlb.h + $(CC) $(CFLAGSU) /Fo$@ $(SRC)\dlb.c + +$(O)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h + $(CC) $(CFLAGSU) /Fo$@ $(UTIL)\dlb_main.c + +#$(DAT)\porthelp: $(NTSYS)\porthelp +# copy $(NTSYS)\porthelp $@ >nul + +$(DAT)\nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon \ + $(DAT)\license $(O)sp_lev.tag + cd $(DAT) + echo data >dlb.lst + echo oracles >>dlb.lst + if exist options echo options >>dlb.lst + if exist ttyoptions echo ttyoptions >>dlb.lst + if exist guioptions echo guioptions >>dlb.lst + if exist porthelp echo porthelp >>dlb.lst + echo quest.dat >>dlb.lst + echo rumors >>dlb.lst + echo help >>dlb.lst + echo hh >>dlb.lst + echo cmdhelp >>dlb.lst + echo history >>dlb.lst + echo opthelp >>dlb.lst + echo wizhelp >>dlb.lst + echo dungeon >>dlb.lst + echo license >>dlb.lst + for %%N in (*.lev) do echo %%N >>dlb.lst + $(U)dlb_main cIf dlb.lst nhdat + cd $(WINCE) + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)\tile.c: $(U)tilemap.exe + echo A new $@ has been created + $(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + $(link) $(LFLAGSU) -out:$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletx32.o: $(WSHR)\tilemap.c $(HACK_H) + $(CC) $(CFLAGSU) /DTILETEXT /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + $(CC) $(CFLAGSU) /DTILETEXT -Fo$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + $(CC) $(CFLAGSU) -I$(WSHR) -Fo$@ $(WSHR)\gifread.c + +$(O)gifrd32.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + $(CC) $(CFLAGSU) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) + $(CC) $(CFLAGSU) -I$(WSHR) -Fo$@ $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + $(CC) $(CFLAGSU) -I$(WSHR) -Fo$@ $(WSHR)\tiletext.c + +$(O)tilete32.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + $(CC) $(CFLAGSU) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tiletext.c + +$(SWINCE)\tiles.bmp: $(U)tile2bmp.exe $(TILEFILES) + echo Creating 16x16 binary tile files (this may take some time) + $(U)tile2bmp $@ + +#$(TILEBMP32): $(TILEUTIL32) $(TILEFILES32) +# echo Creating 32x32 binary tile files (this may take some time) +# $(U)til2bm32 $(TILEBMP32) + + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) + @echo Linking $@... + $(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)tile2bmp.o + $(TEXT_IO:^ =^ + ) +<< + +$(U)til2bm32.exe: $(O)til2bm32.o $(TEXT_IO32) + @echo Linking $@... + $(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)til2bm32.o + $(TEXT_IO32:^ =^ + ) +<< + +$(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + $(CC) $(CFLAGSU) -I$(WSHR) /DPACKED_FILE /Fo$@ $(WSHR)\tile2bmp.c + +$(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + $(CC) $(CFLAGSU) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)\tile2bmp.c + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)\data: $(UTIL)\makedefs.exe + $(U)makedefs -d + +$(DAT)\rumors: $(UTIL)\makedefs.exe $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(UTIL)\makedefs.exe $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(UTIL)\makedefs.exe $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(UTIL)\makedefs.exe $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(WINCE) + +# +# NT dependencies +# +# +#$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nttty.c +# $(CC) $(CFLAGSU) -I$(WSHR) -Fo$@ $(NTSYS)\nttty.c +#$(O)winnt.o: $(HACK_H) $(INCL)\win32api.h $(NTSYS)\winnt.c +# $(CC) $(CFLAGSU) -Fo$@ $(NTSYS)\winnt.c +#$(O)ntsound.o: $(HACK_H) $(NTSYS)\ntsound.c +# $(CC) $(CFLAGSU) -Fo$@ $(NTSYS)\ntsound.c +#$(O)mapimail.o: $(HACK_H) $(INCL)\nhlan.h $(NTSYS)\mapimail.c +# $(CC) $(CFLAGSU) -DMAPI_VERBOSE -Fo$@ $(NTSYS)\mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + $(CC) $(CFLAGSU) -Fo$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with slashes changed to back-slashes +# and -c (which is included in CFLAGSU) substituted +# with -Fo$@ , but otherwise untouched. That +# means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h + $(CC) $(CFLAGSU) -Fo$@ ..\sys\atari\tos.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\win32api.h + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\pcmain.c +$(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\pcsys.c +$(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\pctty.c +$(O)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\pcunix.c +$(O)random.o: ..\sys\share\random.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\random.c +$(O)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\ioctl.c +$(O)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\share\unixtty.c +$(O)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h + $(CC) $(CFLAGSU) -Fo$@ ..\sys\unix\unixmain.c +$(O)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) + $(CC) $(CFLAGSU) -Fo$@ ..\sys\unix\unixunix.c +$(O)bemain.o: ..\sys\be\bemain.c $(HACK_H) $(INCL)\dlb.h + $(CC) $(CFLAGSU) -Fo$@ ..\sys\be\bemain.c +$(O)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\tty\getline.c +$(O)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\tty\termcap.c +$(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\tty\topl.c +$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\tcap.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\tty\wintty.c +$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\Window.c +$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\dialogs.c +$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h ..\win\X11\nh72icon \ + ..\win\X11\nh56icon ..\win\X11\nh32icon + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winX.c +$(O)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winmap.c +$(O)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winmenu.c +$(O)winmesg.o: ..\win\X11\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winmesg.c +$(O)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winmisc.c +$(O)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winstat.c +$(O)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\wintext.c +$(O)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\X11\winval.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)gnaskstr.o: ..\win\gnome\gnaskstr.c ..\win\gnome\gnaskstr.h \ + ..\win\gnome\gnmain.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnaskstr.c +$(O)gnbind.o: ..\win\gnome\gnbind.c ..\win\gnome\gnbind.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnaskstr.h ..\win\gnome\gnyesno.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnbind.c +$(O)gnglyph.o: ..\win\gnome\gnglyph.c ..\win\gnome\gnglyph.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnglyph.c +$(O)gnmain.o: ..\win\gnome\gnmain.c ..\win\gnome\gnmain.h ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnbind.h ..\win\gnome\gnopts.h $(HACK_H) \ + $(INCL)\date.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnmain.c +$(O)gnmap.o: ..\win\gnome\gnmap.c ..\win\gnome\gnmap.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h $(HACK_H) + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnmap.c +$(O)gnmenu.o: ..\win\gnome\gnmenu.c ..\win\gnome\gnmenu.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnbind.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnmenu.c +$(O)gnmesg.o: ..\win\gnome\gnmesg.c ..\win\gnome\gnmesg.h ..\win\gnome\gnsignal.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnmesg.c +$(O)gnopts.o: ..\win\gnome\gnopts.c ..\win\gnome\gnopts.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnmain.h ..\win\gnome\gnmap.h $(HACK_H) + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnopts.c +$(O)gnplayer.o: ..\win\gnome\gnplayer.c ..\win\gnome\gnplayer.h \ + ..\win\gnome\gnmain.h $(HACK_H) + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnplayer.c +$(O)gnsignal.o: ..\win\gnome\gnsignal.c ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnmain.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnsignal.c +$(O)gnstatus.o: ..\win\gnome\gnstatus.c ..\win\gnome\gnstatus.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gn_xpms.h \ + ..\win\gnome\gnomeprv.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnstatus.c +$(O)gntext.o: ..\win\gnome\gntext.c ..\win\gnome\gntext.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gn_rip.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gntext.c +$(O)gnyesno.o: ..\win\gnome\gnyesno.c ..\win\gnome\gnbind.h ..\win\gnome\gnyesno.h + $(CC) $(CFLAGSU) $(GNOMEINC) -c ..\win\gnome\gnyesno.c +$(O)wingem.o: ..\win\gem\wingem.c $(HACK_H) $(INCL)\func_tab.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\wingem.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\gem\wingem.c +$(O)wingem1.o: ..\win\gem\wingem1.c $(INCL)\gem_rsc.h $(INCL)\load_img.h \ + $(INCL)\wintype.h $(INCL)\wingem.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\gem\wingem1.c +$(O)load_img.o: ..\win\gem\load_img.c $(INCL)\load_img.h + $(CC) $(CFLAGSU) -Fo$@ ..\win\gem\load_img.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\dlb.h $(INCL)\patchlevel.h $(INCL)\qt_win.h \ + $(INCL)\qt_clust.h $(INCL)\qt_kde0.h \ + $(INCL)\qt_xpms.h qt_win.moc qt_kde0.moc + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_win.cpp +$(O)qt_clust.o: ..\win\Qt\qt_clust.cpp $(INCL)\qt_clust.h + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_clust.cpp +$(O)monstr.o: $(SRC)\monstr.c $(CONFIG_H) +$(O)vis_tab.o: $(SRC)\vis_tab.c $(CONFIG_H) $(INCL)\vis_tab.h +$(O)allmain.o: $(SRC)\allmain.c $(HACK_H) +$(O)alloc.o: $(SRC)\alloc.c $(CONFIG_H) +$(O)apply.o: $(SRC)\apply.c $(HACK_H) $(INCL)\edog.h +$(O)artifact.o: $(SRC)\artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +$(O)attrib.o: $(SRC)\attrib.c $(HACK_H) $(INCL)\artifact.h +$(O)ball.o: $(SRC)\ball.c $(HACK_H) +$(O)bones.o: $(SRC)\bones.c $(HACK_H) $(INCL)\lev.h +$(O)botl.o: $(SRC)\botl.c $(HACK_H) +$(O)cmd.o: $(SRC)\cmd.c $(HACK_H) $(INCL)\func_tab.h +$(O)dbridge.o: $(SRC)\dbridge.c $(HACK_H) +$(O)decl.o: $(SRC)\decl.c $(HACK_H) +$(O)detect.o: $(SRC)\detect.c $(HACK_H) $(INCL)\artifact.h +$(O)dig.o: $(SRC)\dig.c $(HACK_H) $(INCL)\edog.h +$(O)display.o: $(SRC)\display.c $(HACK_H) +$(O)dlb.o: $(SRC)\dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(O)do.o: $(SRC)\do.c $(HACK_H) $(INCL)\lev.h +$(O)do_name.o: $(SRC)\do_name.c $(HACK_H) +$(O)do_wear.o: $(SRC)\do_wear.c $(HACK_H) +$(O)dog.o: $(SRC)\dog.c $(HACK_H) $(INCL)\edog.h +$(O)dogmove.o: $(SRC)\dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)dokick.o: $(SRC)\dokick.c $(HACK_H) $(INCL)\eshk.h +$(O)dothrow.o: $(SRC)\dothrow.c $(HACK_H) +$(O)drawing.o: $(SRC)\drawing.c $(HACK_H) $(INCL)\tcap.h +$(O)dungeon.o: $(SRC)\dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +$(O)eat.o: $(SRC)\eat.c $(HACK_H) +$(O)end.o: $(SRC)\end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +$(O)engrave.o: $(SRC)\engrave.c $(HACK_H) $(INCL)\lev.h +$(O)exper.o: $(SRC)\exper.c $(HACK_H) +$(O)explode.o: $(SRC)\explode.c $(HACK_H) +$(O)extralev.o: $(SRC)\extralev.c $(HACK_H) +$(O)files.o: $(SRC)\files.c $(HACK_H) $(INCL)\dlb.h +$(O)fountain.o: $(SRC)\fountain.c $(HACK_H) +$(O)hack.o: $(SRC)\hack.c $(HACK_H) +$(O)hacklib.o: $(SRC)\hacklib.c $(HACK_H) +$(O)invent.o: $(SRC)\invent.c $(HACK_H) $(INCL)\artifact.h +$(O)light.o: $(SRC)\light.c $(HACK_H) $(INCL)\lev.h +$(O)lock.o: $(SRC)\lock.c $(HACK_H) +$(O)mail.o: $(SRC)\mail.c $(HACK_H) $(INCL)\mail.h +$(O)makemon.o: $(SRC)\makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +$(O)mapglyph.o: $(SRC)\mapglyph.c $(HACK_H) +$(O)mcastu.o: $(SRC)\mcastu.c $(HACK_H) +$(O)mhitm.o: $(SRC)\mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)mhitu.o: $(SRC)\mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)minion.o: $(SRC)\minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +$(O)mklev.o: $(SRC)\mklev.c $(HACK_H) +$(O)mkmap.o: $(SRC)\mkmap.c $(HACK_H) $(INCL)\sp_lev.h +$(O)mkmaze.o: $(SRC)\mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +$(O)mkobj.o: $(SRC)\mkobj.c $(HACK_H) $(INCL)\artifact.h +$(O)mkroom.o: $(SRC)\mkroom.c $(HACK_H) +$(O)mon.o: $(SRC)\mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)mondata.o: $(SRC)\mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +$(O)monmove.o: $(SRC)\monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h +$(O)monst.o: $(SRC)\monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +$(O)mplayer.o: $(SRC)\mplayer.c $(HACK_H) +$(O)mthrowu.o: $(SRC)\mthrowu.c $(HACK_H) +$(O)muse.o: $(SRC)\muse.c $(HACK_H) $(INCL)\edog.h +$(O)music.o: $(SRC)\music.c $(HACK_H) #interp.c +$(O)o_init.o: $(SRC)\o_init.c $(HACK_H) $(INCL)\lev.h +$(O)objects.o: $(SRC)\objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(O)objnam.o: $(SRC)\objnam.c $(HACK_H) +$(O)options.o: $(SRC)\options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +$(O)pager.o: $(SRC)\pager.c $(HACK_H) $(INCL)\dlb.h +$(O)pickup.o: $(SRC)\pickup.c $(HACK_H) +$(O)pline.o: $(SRC)\pline.c $(HACK_H) $(INCL)\epri.h $(INCL)\edog.h +$(O)polyself.o: $(SRC)\polyself.c $(HACK_H) +$(O)potion.o: $(SRC)\potion.c $(HACK_H) +$(O)pray.o: $(SRC)\pray.c $(HACK_H) $(INCL)\epri.h +$(O)priest.o: $(SRC)\priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +$(O)quest.o: $(SRC)\quest.c $(HACK_H) $(INCL)\qtext.h +$(O)questpgr.o: $(SRC)\questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +$(O)read.o: $(SRC)\read.c $(HACK_H) +$(O)rect.o: $(SRC)\rect.c $(HACK_H) +$(O)region.o: $(SRC)\region.c $(HACK_H) $(INCL)\lev.h +$(O)restore.o: $(SRC)\restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +$(O)rip.o: $(SRC)\rip.c $(HACK_H) +$(O)rnd.o: $(SRC)\rnd.c $(HACK_H) +$(O)role.o: $(SRC)\role.c $(HACK_H) +$(O)rumors.o: $(SRC)\rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +$(O)save.o: $(SRC)\save.c $(HACK_H) $(INCL)\lev.h +$(O)shk.o: $(SRC)\shk.c $(HACK_H) $(INCL)\eshk.h +$(O)shknam.o: $(SRC)\shknam.c $(HACK_H) $(INCL)\eshk.h +$(O)sit.o: $(SRC)\sit.c $(HACK_H) $(INCL)\artifact.h +$(O)sounds.o: $(SRC)\sounds.c $(HACK_H) $(INCL)\edog.h +$(O)sp_lev.o: $(SRC)\sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +$(O)spell.o: $(SRC)\spell.c $(HACK_H) +$(O)steal.o: $(SRC)\steal.c $(HACK_H) +$(O)steed.o: $(SRC)\steed.c $(HACK_H) +$(O)teleport.o: $(SRC)\teleport.c $(HACK_H) +$(O)timeout.o: $(SRC)\timeout.c $(HACK_H) $(INCL)\lev.h +$(O)topten.o: $(SRC)\topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlevel.h +$(O)track.o: $(SRC)\track.c $(HACK_H) +$(O)trap.o: $(SRC)\trap.c $(HACK_H) +$(O)u_init.o: $(SRC)\u_init.c $(HACK_H) +$(O)uhitm.o: $(SRC)\uhitm.c $(HACK_H) +$(O)vault.o: $(SRC)\vault.c $(HACK_H) $(INCL)\vault.h +$(O)version.o: $(SRC)\version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlevel.h +$(O)vision.o: $(SRC)\vision.c $(HACK_H) $(INCL)\vis_tab.h +$(O)weapon.o: $(SRC)\weapon.c $(HACK_H) +$(O)were.o: $(SRC)\were.c $(HACK_H) +$(O)wield.o: $(SRC)\wield.c $(HACK_H) +$(O)windows.o: $(SRC)\windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h +$(O)wizard.o: $(SRC)\wizard.c $(HACK_H) $(INCL)\qtext.h +$(O)worm.o: $(SRC)\worm.c $(HACK_H) $(INCL)\lev.h +$(O)worn.o: $(SRC)\worn.c $(HACK_H) +$(O)write.o: $(SRC)\write.c $(HACK_H) +$(O)zap.o: $(SRC)\zap.c $(HACK_H) + +# end of file + diff --git a/sys/wince/ceinc/assert.h b/sys/wince/ceinc/assert.h new file mode 100644 index 0000000..8844d2a --- /dev/null +++ b/sys/wince/ceinc/assert.h @@ -0,0 +1,16 @@ +/*** +*assert.h - define the assert macro +* +****/ + +#undef assert + +#ifdef NDEBUG + +#define assert(exp) ((void)0) + +#else + +#define assert(exp) (void)( (exp) || (panic("%s at %s line %ld", #exp, __FILE__,__LINE__), 1) ) + +#endif /* NDEBUG */ diff --git a/sys/wince/ceinc/errno.h b/sys/wince/ceinc/errno.h new file mode 100644 index 0000000..c6c3193 --- /dev/null +++ b/sys/wince/ceinc/errno.h @@ -0,0 +1,4 @@ +/* empty file */ + +extern int errno; + diff --git a/sys/wince/ceinc/fcntl.h b/sys/wince/ceinc/fcntl.h new file mode 100644 index 0000000..a9136c8 --- /dev/null +++ b/sys/wince/ceinc/fcntl.h @@ -0,0 +1,67 @@ +/*** +*fcntl.h - file control options used by open() +* +*Purpose: +* This file defines constants for the file control options used +* by the _open() function. +* [System V] +* +* [Public] +* +****/ + +#ifndef _INC_FCNTL +#define _INC_FCNTL + +#define _O_RDONLY 0x0000 /* open for reading only */ +#define _O_WRONLY 0x0001 /* open for writing only */ +#define _O_RDWR 0x0002 /* open for reading and writing */ +#define _O_APPEND 0x0008 /* writes done at eof */ + +#define _O_CREAT 0x0100 /* create and open file */ +#define _O_TRUNC 0x0200 /* open and truncate */ +#define _O_EXCL 0x0400 /* open only if file doesn't already exist */ + +/* O_TEXT files have sequences translated to on read()'s, +** and sequences translated to on write()'s +*/ + +#define _O_TEXT 0x4000 /* file mode is text (translated) */ +#define _O_BINARY 0x8000 /* file mode is binary (untranslated) */ + +/* macro to translate the C 2.0 name used to force binary mode for files */ + +#define _O_RAW _O_BINARY + +/* Open handle inherit bit */ + +#define _O_NOINHERIT 0x0080 /* child process doesn't inherit file */ + +/* Temporary file bit - file is deleted when last handle is closed */ + +#define _O_TEMPORARY 0x0040 /* temporary file bit */ + +/* sequential/random access hints */ + +#define _O_SEQUENTIAL 0x0020 /* file access is primarily sequential */ +#define _O_RANDOM 0x0010 /* file access is primarily random */ + +#if !__STDC__ || defined(_POSIX_) +/* Non-ANSI names for compatibility */ +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDWR +#define O_APPEND _O_APPEND +#define O_CREAT _O_CREAT +#define O_TRUNC _O_TRUNC +#define O_EXCL _O_EXCL +#define O_TEXT _O_TEXT +#define O_BINARY _O_BINARY +#define O_RAW _O_BINARY +#define O_TEMPORARY _O_TEMPORARY +#define O_NOINHERIT _O_NOINHERIT +#define O_SEQUENTIAL _O_SEQUENTIAL +#define O_RANDOM _O_RANDOM +#endif /* __STDC__ */ + +#endif /* _INC_FCNTL */ diff --git a/sys/wince/ceinc/sys/stat.h b/sys/wince/ceinc/sys/stat.h new file mode 100644 index 0000000..dbb39b6 --- /dev/null +++ b/sys/wince/ceinc/sys/stat.h @@ -0,0 +1,2 @@ +/* empty file */ + diff --git a/sys/wince/celib.c b/sys/wince/celib.c new file mode 100644 index 0000000..22323b0 --- /dev/null +++ b/sys/wince/celib.c @@ -0,0 +1,824 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#define NEED_VARARGS +#include "hack.h" +#include +// #include "wceconf.h" + +static union { + time_t t_val; + struct time_pack { + unsigned int ss:6; + unsigned int mm:6; + unsigned int dd:5; + unsigned int hh:6; + unsigned int mo:4; + unsigned int yr:10; + unsigned int wd:3; + } tm_val; +} _t_cnv; + +#define IS_LEAP(yr) (((yr)%4==0 || (yr)%100==0) && !(yr)%400==0) +static char _day_mo_leap[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +static char _day_mo[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +struct tm * __cdecl localtime ( const time_t *ptime ) +{ + static struct tm ptm; + int i; + if( !ptime ) return NULL; + + _t_cnv.t_val = *ptime; + + ptm.tm_sec = _t_cnv.tm_val.ss ; /* seconds after the minute - [0,59] */ + ptm.tm_min = _t_cnv.tm_val.mm; /* minutes after the hour - [0,59] */ + ptm.tm_hour = _t_cnv.tm_val.hh; /* hours since midnight - [0,23] */ + ptm.tm_mday = _t_cnv.tm_val.dd; /* day of the month - [1,31] */ + ptm.tm_mon = _t_cnv.tm_val.mo-1; /* months since January - [0,11] */ + ptm.tm_year = _t_cnv.tm_val.yr; /* years since 1900 */ + ptm.tm_wday = _t_cnv.tm_val.wd; /* days since Sunday - [0,6] */ + + ptm.tm_yday = _t_cnv.tm_val.dd; /* days since January 1 - [0,365] */ + for( i=0; i=0 && i=FILE_TABLE_SIZE ) return -1; + retval = (CloseHandle(_nh_file_table[f])? 0 : -1); + _nh_file_table[f] = INVALID_HANDLE_VALUE; + return retval; +} + +int __cdecl creat(const char *fname , int mode) +{ + HANDLE f; + TCHAR wbuf[MAX_PATH+1]; + ZeroMemory(wbuf, sizeof(wbuf)); + NH_A2W(fname, wbuf, MAX_PATH); + + f = CreateFile( + wbuf, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if( f==INVALID_HANDLE_VALUE ) return -1; + else return alloc_file_handle(f); +} + +int __cdecl eof(int f) +{ + DWORD fpos, fsize; + HANDLE p = get_file_handle(f); + + if( f==-1 ) return -1; + + fpos = SetFilePointer(p, 0, NULL, FILE_CURRENT); + fsize = SetFilePointer(p, 0, NULL, FILE_END); + if( fpos==0xFFFFFFFF || fsize==0xFFFFFFFF ) return -1; + if( fpos==fsize ) return 1; + else { + SetFilePointer(p, fpos, NULL, FILE_BEGIN); + return 0; + } +} + +long __cdecl lseek( int f, long offset, int origin ) +{ + HANDLE p = get_file_handle(f); + DWORD fpos; + switch(origin) { + case SEEK_SET: + fpos = SetFilePointer(p, offset, NULL, FILE_BEGIN); + break; + case SEEK_CUR: + fpos = SetFilePointer(p, offset, NULL, FILE_CURRENT); + break; + case SEEK_END: + fpos = SetFilePointer(p, offset, NULL, FILE_END); + break; + default: + fpos = 0xFFFFFFFF; + break; + } + if( fpos==0xFFFFFFFF ) return -1; + else return (long)fpos; +} + +int __cdecl open( const char *filename, int oflag, ... ) +{ + TCHAR fname[MAX_PATH+1]; + TCHAR path[MAX_PATH+1]; + HANDLE f; + DWORD fileaccess; + DWORD filecreate; + + /* O_TEXT is not supported */ + + /* + * decode the access flags + */ + switch( oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) { + + case _O_RDONLY: /* read access */ + fileaccess = GENERIC_READ; + break; + case _O_WRONLY: /* write access */ + fileaccess = GENERIC_READ | GENERIC_WRITE; + break; + case _O_RDWR: /* read and write access */ + fileaccess = GENERIC_READ | GENERIC_WRITE; + break; + default: /* error, bad oflag */ + return -1; + } + + /* + * decode open/create method flags + */ + switch ( oflag & (_O_CREAT | _O_EXCL | _O_TRUNC) ) { + case 0: + case _O_EXCL: // ignore EXCL w/o CREAT + filecreate = OPEN_EXISTING; + break; + + case _O_CREAT: + filecreate = OPEN_ALWAYS; + break; + + case _O_CREAT | _O_EXCL: + case _O_CREAT | _O_TRUNC | _O_EXCL: + filecreate = CREATE_NEW; + break; + + case _O_TRUNC: + case _O_TRUNC | _O_EXCL: // ignore EXCL w/o CREAT + filecreate = TRUNCATE_EXISTING; + break; + + case _O_CREAT | _O_TRUNC: + filecreate = CREATE_ALWAYS; + break; + + default: + return -1; + } + + /* assemple the file name */ + ZeroMemory(fname, sizeof(fname)); + ZeroMemory(path, sizeof(path)); + NH_A2W(filename, fname, MAX_PATH); + if( *filename!='\\' && *filename!='/' ) { + _tcscpy(path, _nh_cwd); + _tcsncat(path, _T("\\"), MAX_PATH - _tcslen(path)); + } + _tcsncat(path, fname, MAX_PATH - _tcslen(path)); + + /* + * try to open/create the file + */ + if ( (f = CreateFile( path, + fileaccess, + 0, + NULL, + filecreate, + FILE_ATTRIBUTE_NORMAL, + NULL )) + == INVALID_HANDLE_VALUE ) + { + return -1; + } + + if( !(oflag & O_APPEND) ) SetFilePointer(f, 0, NULL, FILE_BEGIN); + return alloc_file_handle(f); +} + +int __cdecl read( int f, void *buffer, unsigned int count ) +{ + HANDLE p = get_file_handle(f); + DWORD bytes_read; + if( !ReadFile(p, buffer, count, &bytes_read, NULL) ) + return -1; + else + return (int)bytes_read; +} + +int __cdecl unlink(const char * filename) +{ + TCHAR wbuf[MAX_PATH+1]; + TCHAR fname[MAX_PATH+1]; + + ZeroMemory(wbuf, sizeof(wbuf)); + ZeroMemory(fname, sizeof(fname)); + NH_A2W(filename, wbuf, MAX_PATH); + if( *filename!='\\' && *filename!='/' ) { + _tcscpy(fname, _nh_cwd); + _tcsncat(fname, _T("\\"), MAX_PATH - _tcslen(fname)); + } + _tcsncat(fname, wbuf, MAX_PATH - _tcslen(fname)); + + return !DeleteFileW(fname); +} + +int __cdecl write( int f, const void *buffer, unsigned int count ) +{ + HANDLE p = get_file_handle(f); + DWORD bytes_written; + if( !WriteFile(p, buffer, count, &bytes_written, NULL) ) + return -1; + else + return (int)bytes_written; +} + +int __cdecl rename( const char *oldname, const char *newname ) +{ + WCHAR f1[MAX_PATH+1]; + WCHAR f2[MAX_PATH+1]; + ZeroMemory(f1, sizeof(f1)); + ZeroMemory(f2, sizeof(f2)); + MultiByteToWideChar(CP_ACP, 0, oldname, -1, f1, MAX_PATH); + MultiByteToWideChar(CP_ACP, 0, newname, -1, f2, MAX_PATH); + return !MoveFile(f1, f2); +} + +int __cdecl access( const char *path, int mode ) +{ + DWORD attr; + WCHAR f[MAX_PATH+1]; + ZeroMemory(f, sizeof(f)); + MultiByteToWideChar(CP_ACP, 0, path, -1, f, MAX_PATH); + + attr = GetFileAttributes(f); + if( attr == (DWORD)-1 ) return -1; + + if ( (attr & FILE_ATTRIBUTE_READONLY) && (mode & 2) ) + return -1; + else + return 0; +} + +int chdir( const char *dirname ) +{ + ZeroMemory(_nh_cwd, sizeof(_nh_cwd)); + NH_A2W(dirname, _nh_cwd, MAX_PATH); + return 0; +} + +char *getcwd( char *buffer, int maxlen ) +{ + if( maxlen<(int)_tcslen(_nh_cwd) ) return NULL; + else return NH_W2A(_nh_cwd, buffer, maxlen); +} + +/*------------------------------------------------------------------------------*/ +/* __errno.h__ */ +int errno; + +/*------------------------------------------------------------------------------*/ +/* + * Chdrive() changes the default drive. + */ +void +chdrive(char *str) +{ + return; +} + +/* + * This is used in nhlan.c to implement some of the LAN_FEATURES. + */ +char *get_username(lan_username_size) +int *lan_username_size; +{ + static char username_buffer[BUFSZ]; + strcpy(username_buffer, "nhsave"); + return username_buffer; +} + +void Delay(int ms) +{ + (void)Sleep(ms); +} + +void more() +{ + +} + +int isatty(int f) +{ + return 0; +} + + +#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO) +int __cdecl isupper(int c) +{ + char str[2]; + WCHAR wstr[2]; + str[0] = c; + str[1] = 0; + + NH_A2W(str, wstr, 1); + return iswupper(wstr[0]); +} + +int __cdecl isdigit(int c) +{ + return ('0' <= c && c <= '9'); +} + +int __cdecl isxdigit(int c) +{ + return (('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F')); +} + +int __cdecl isspace(int c) +{ + char str[2]; + WCHAR wstr[2]; + str[0] = c; + str[1] = 0; + + NH_A2W(str, wstr, 1); + return iswspace(wstr[0]); +} + +int __cdecl isprint(int c) +{ + char str[2]; + WCHAR wstr[2]; + str[0] = c; + str[1] = 0; + + NH_A2W(str, wstr, 1); + return iswprint(wstr[0]); +} + +char* __cdecl _strdup(const char* s) +{ + char* p; + p = malloc(strlen(s)+1); + return strcpy(p, s); +} + +char* __cdecl strrchr( const char *s, int c ) +{ + WCHAR wstr[1024]; + WCHAR *w; + w = wcsrchr(NH_A2W(s, wstr, 1024), c); + if(w) return (char*)(s + (w - wstr)); + else return NULL; +} + +int __cdecl _stricmp(const char* a, const char* b) +{ + return strncmpi(a, b, 65535u); +} + +#endif + +#if defined(WIN_CE_PS2xx) +/* stdio.h functions are missing from PAlm Size PC SDK 1.2 (SH3 and MIPS) */ + +#pragma warning(disable:4273) + +FILE * __cdecl fopen(const char* filename, const char *mode) +{ + int modeflag; + int whileflag; + int filedes; + + /* First mode character must be 'r', 'w', or 'a'. */ + switch (*mode) { + case 'r': + modeflag = _O_RDONLY; + break; + case 'w': + modeflag = _O_WRONLY | _O_CREAT | _O_TRUNC; + break; + case 'a': + modeflag = _O_WRONLY | _O_CREAT | _O_APPEND; + break; + default: + return NULL; + } + + whileflag=1; + while(*++mode && whileflag) + switch(*mode) { + + case '+': + if (modeflag & _O_RDWR) + whileflag=0; + else { + modeflag |= _O_RDWR; + modeflag &= ~(_O_RDONLY | _O_WRONLY); + } + break; + + case 'b': + if (modeflag & (_O_TEXT | _O_BINARY)) + whileflag=0; + else + modeflag |= _O_BINARY; + break; + + case 't': /* not supported */ + whileflag=0; + break; + + default: + whileflag=0; + break; + } + + if ((filedes = open(filename, modeflag))==-1) return NULL; + + return (FILE*)filedes; +} + +int __cdecl fscanf(FILE *f , const char *format, ...) +{ + /* Format spec: %[*] [width] [l] type ] */ + int ch; + int sch; + int matched = 0; + int width = 65535; + int modifier = -1; + int skip_flag = 0; + int n_read = 0; + char buf[BUFSZ]; + TCHAR wbuf[BUFSZ]; + char* p; + va_list args; + +#define RETURN_SCANF(i) { va_end(args); return i; } +#define NEXT_CHAR(f) (n_read++, fgetc(f)) + + va_start(args, format); + + ch = *format++; + sch = NEXT_CHAR(f); + while( ch && sch!=EOF ) { + if( isspace(ch) ) { + while( ch && isspace(ch) ) ch = *format++; + while( sch!=EOF && isspace(sch) ) sch = NEXT_CHAR(f); + format--; + goto next_spec; + } + + /* read % */ + if( ch!='%' ) { + if( sch!=ch ) RETURN_SCANF(matched); + sch = NEXT_CHAR(f); + goto next_spec; + } else { + /* process '%%' */ + ch = *format++; + if( ch=='%' ) { + if( sch!='%' ) RETURN_SCANF(matched); + sch = NEXT_CHAR(f); + goto next_spec; + } + + if( ch=='*' ) { + /* read skip flag - '*' */ + skip_flag=1; + ch = *format++; + } + + /* get width */ + if( isdigit(ch) ) { + width = 0; + while(ch && isdigit(ch)) { + width = width*10 + (ch-'0'); + ch = *format++; + } + } + + /* get modifier */ + if( ch=='l' ) { + modifier = 'l'; + ch = *format++; + } + + /* get type */ + switch(ch) { + case 'c': + if( !skip_flag ) { + *(va_arg(args, char*))=sch; + matched++; + } + sch = NEXT_CHAR(f); + goto next_spec; + case 'd': + p = buf; + /* skip space */ + while(sch!=EOF && isspace(sch)) sch=NEXT_CHAR(f); + while(sch!=EOF && isdigit(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); } + *p = '\x0'; + if( !skip_flag ) { + matched++; + if( modifier=='l' ) { + *(va_arg(args, long*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 10); + } else { + *(va_arg(args, int*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 10); + } + } + goto next_spec; + case 'x': + p = buf; + while(sch!=EOF && isspace(sch)) sch=NEXT_CHAR(f); + while(sch!=EOF && isxdigit(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); } + *p = '\x0'; + if( !skip_flag ) { + matched++; + if( modifier=='l' ) { + *(va_arg(args, long*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 16); + } else { + *(va_arg(args, int*))=wcstol(NH_A2W(buf, wbuf, BUFSZ), NULL, 16); + } + } + goto next_spec; + case 'n': + *(va_arg(args, int*)) = n_read; + matched++; + goto next_spec; + case 's': + if( skip_flag ) { + while(sch!=EOF && !isspace(sch) && --width>=0) { sch=NEXT_CHAR(f); } + } else { + p = va_arg(args, char*); + while(sch!=EOF && !isspace(sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); } + *p = '\x0'; + matched++; + } + goto next_spec; + case '[': { + char pattern[256]; + int start, end; + int negate; + + ZeroMemory(pattern, sizeof(pattern)); + p = pattern; + + /* try to parse '^' modifier */ + ch = *format++; + if( ch=='^' ) { negate=1; ch=*format++; } + else { negate=0; } + if( ch==0 ) RETURN_SCANF(EOF); + + for( ; ch && ch!=']'; ch = *format++ ) { + /* try to parse range: a-z */ + if( format[0]=='-' && + format[1] && format[1]!=']' ) { + start = ch; + format++; + end = *format++; + while(start<=end) { + if(!strchr(pattern, (char)start)) + *p++ = (char)start; + start++; + } + } else { + if(!strchr(pattern, (char)ch)) *p++ = (char)ch; + } + } + + if( skip_flag ) { + while(sch!=EOF && strchr(pattern, sch) && --width>=0) { sch=NEXT_CHAR(f); } + } else { + p = va_arg(args, char*); + if( negate ) + while(sch!=EOF && !strchr(pattern, sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); } + else + while(sch!=EOF && strchr(pattern, sch) && --width>=0) { *p++ = sch; sch=NEXT_CHAR(f); } + *p = '\x0'; + matched++; + } + } goto next_spec; + default: + RETURN_SCANF(EOF); + } + } + +next_spec: + width = 65535; + modifier = -1; + skip_flag = 0; + ch = *format++; + } + fseek(f, -1, SEEK_CUR); + RETURN_SCANF(matched); + +#undef RETURN_SCANF +#undef NEXT_CHAR +} + +int __cdecl fprintf(FILE *f , const char *format, ...) +{ + int retval; + va_list args; + + if( !f || !format ) return 0; + + va_start(args, format); + retval = vfprintf(f, format, args); + va_end(args); + + return retval; +} + +int __cdecl vfprintf(FILE* f, const char *format, va_list args) +{ + char buf[4096]; + int retval; + + if( !f || !format ) return 0; + + retval = vsprintf(buf, format, args); + + write((int)f, buf, strlen(buf)); + + return retval; +} + +int __cdecl fgetc(FILE * f) +{ + char c; + int fh = (int)f; + + if( !f ) return EOF; + if( read(fh, &c, 1)==1 ) return c; + else return EOF; +} + +char * __cdecl fgets(char *s, int size, FILE *f) +{ + /* not the best performance but it will do for now...*/ + char c; + if( !f || !s || size==0 ) return NULL; + while( --size>0 ) { + if( (c = fgetc(f))==EOF ) return NULL; + + *s++ = c; + if( c=='\n' ) break; + } + *s = '\x0'; + return s; +} + +int __cdecl printf(const char *format, ...) +{ + int retval; + va_list args; + + if( !format ) return 0; + + va_start(args, format); + retval = vprintf(format, args); + va_end(args); + + return retval; +} + +int __cdecl vprintf(const char *format, va_list args) +{ + char buf[4096]; + int retval; + + retval = vsprintf(buf, format, args); + puts(buf); + return retval; +} + +// int __cdecl putchar(int); +int __cdecl puts(const char * s) +{ + TCHAR wbuf[4096]; + NH_A2W(s, wbuf, 4096); + MessageBox(NULL, wbuf, _T("stdout"), MB_OK); + return 0; +} + +FILE* __cdecl _getstdfilex(int desc) +{ + return NULL; +} + +int __cdecl fclose(FILE * f) +{ + if(!f) return EOF; + return close((int)f)==-1? EOF : 0; +} + +size_t __cdecl fread(void *p, size_t size, size_t count, FILE *f) +{ + int read_bytes; + if(!f || !p || size==0 || count==0) return 0; + read_bytes = read((int)f, p, size*count); + return read_bytes>0? (read_bytes/size) : 0; +} + +size_t __cdecl fwrite(const void *p, size_t size, size_t count, FILE * f) +{ + int write_bytes; + if(!f || !p || size==0 || count==0) return 0; + write_bytes = write((int)f, p, size*count); + return write_bytes>0? write_bytes/size : 0; +} + +int __cdecl fflush(FILE *f) +{ + return 0; +} + +int __cdecl feof(FILE *f) +{ + return (f && eof((int)f)==0)? 0 : 1; +} + +int __cdecl fseek(FILE *f, long offset, int from) +{ + return (f && lseek((int)f, offset, from)>=0)? 0 : 1; +} + +long __cdecl ftell(FILE * f) +{ + return f? lseek((int)f, 0, SEEK_CUR) : -1; +} + +#endif diff --git a/sys/wince/cesetup.bat b/sys/wince/cesetup.bat new file mode 100644 index 0000000..b129142 --- /dev/null +++ b/sys/wince/cesetup.bat @@ -0,0 +1,40 @@ +@REM SCCS Id: @(#)nhsetup.bat $Date: 2003/08/22 13:23:33 $ +@REM Copyright (c) Alex Kompel, 2002 +@REM NetHack may be freely redistributed. See license for details. +@REM Win32 nhsetup batch file, see Install.ce for details +@REM +@echo off +REM +REM Make sure directories necessary for build exist +REM +if NOT exist ..\..\wince\*.* mkdir ..\..\wince +REM +REM Get these files from the win\win32 port +REM +copy ..\..\win\win32\mnsel.uu ..\..\wince\mnsel.uu +copy ..\..\win\win32\mnselcnt.uu ..\..\wince\mnselcnt.uu +copy ..\..\win\win32\mnunsel.uu ..\..\wince\mnunsel.uu +copy ..\..\win\win32\petmark.uu ..\..\wince\petmark.uu +copy ..\..\sys\winnt\nhico.uu ..\..\wince\nhico.uu +copy ..\..\sys\wince\menubar.uu ..\..\wince\menubar.uu +copy ..\..\sys\wince\keypad.uu ..\..\wince\keypad.uu +REM +REM Get these files from sys\wince +REM +copy bootstrp.mak ..\..\wince\bootstrp.mak +copy wince.vcw ..\..\wince.vcw +copy hpc.vcp ..\..\wince\wince_hpc.vcp +copy palmpc.vcp ..\..\wince\wince_palm_pc.vcp +copy pocketpc.vcp ..\..\wince\wince_pocket_pc.vcp +copy smartphn.vcp ..\..\wince\wince_smartphone.vcp +echo. +echo Proceed with the following steps: +echo. +echo cd ..\..\wince +echo nmake /f bootstrp.mak +echo. +echo Then start Embedded Visual C and open +echo the workspace wince.vcw (at the top of the NetHack tree) +echo to build. See Install.ce for details. +echo. + diff --git a/sys/wince/cesound.c b/sys/wince/cesound.c new file mode 100644 index 0000000..257f61e --- /dev/null +++ b/sys/wince/cesound.c @@ -0,0 +1,27 @@ +/* SCCS Id: @(#)cesound.c 3.4 $Date: 2003/02/13 12:35:27 $ */ +/* Copyright (c) NetHack PC Development Team 1993 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * cesound.c - Windows CE NetHack sound support + * + * + */ + +#include "hack.h" +#include + +#ifdef USER_SOUNDS + +void play_usersound(filename, volume) +const char* filename; +int volume; +{ + TCHAR wbuf[MAX_PATH+1]; +/* pline("play_usersound: %s (%d).", filename, volume); */ + ZeroMemory(wbuf, sizeof(wbuf)); + (void)sndPlaySound(NH_A2W(filename, wbuf, MAX_PATH), SND_ASYNC | SND_NODEFAULT); +} + +#endif /*USER_SOUNDS*/ +/* cesound.c */ diff --git a/sys/wince/defaults.nh b/sys/wince/defaults.nh new file mode 100644 index 0000000..6367900 --- /dev/null +++ b/sys/wince/defaults.nh @@ -0,0 +1,139 @@ +# Sample config file for win32 NetHack +# A '#' at the beginning of a line means the rest of the line is a comment. +# +# Some options MUST be set in this file, other options can be toggled while +# playing. For a list of options available see the file. +# +# To change the configuration, comment out the unwanted lines, and +# uncomment the configuration you want. + +# *** OPTIONS *** +# +# Use the IBM character set rather than just plain ascii characters +# for tty window-port. +# OPTIONS=IBMGraphics + +# *** Personal Preferences *** +# Some options to set personal preferences. Uncomment and change these to +# suit your personal preference. If several people are to use the same +# configuration, options like these should not be set. +# +#OPTIONS=name:Janet,role:Valkyrie,race:Human,gender:female,align:lawful +#OPTIONS=dogname:Fido,catname:Morris,fruit:guava +#OPTIONS=horsename:Silver +#OPTIONS=autopickup,pickup_types:$"=/!?+ +#OPTIONS=packorder:")[%?+/=!(*0_` +#OPTIONS=scores:10 top/2 around/own +#OPTIONS=nolegacy,noverbose +#OPTIONS=menustyle:traditional + +# +# General options. You might also set "silent" so as not to attract +# the boss's attention. +# +OPTIONS=time,noshowexp,number_pad,lit_corridor,rest_on_space +# +# If you want to get rid of "use #quit to quit..." use: +#OPTIONS=suppress_alert:3.3.1 +# +# Set some options to control graphical window-port (these will +# be safely and silently ignored by the tty port) +# +# Map window settings +# possible map_mode options include: tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8| +# ascii7x12|ascii8x12|ascii16x12|ascii12x16| +# ascii10x18|fit_to_screen +OPTIONS=map_mode:tiles,scroll_margin:4 + +# Menu settings +# OPTIONS=font_menu:Arial + +# Other +OPTIONS=hilite_pet,!toptenwin +OPTIONS=!splash_screen,player_selection:prompts +OPTIONS=vary_msgcount:3 +OPTIONS=fullscreen,wraptext,softkeyboard + +# Status/message window colors +# Possible color options include: +# six digit hexadecimal RGB color value ("#8F8F8F"), black, red, green, brown, +# blue, magenta, cyan, gray (or grey), orange, brightgreen, yellow, brightblue, +# brightmagenta, brightcyan, white, trueblack, purple, silver, maroon, fuchsia, +# lime, olive, navy, teal, aqua, activeborder, activecaption, appworkspace, +# background, btnface, btnshadow, btntext, captiontext, graytext, highlight, +# highlighttext, inactiveborder, inactivecaption, menu, menutext, scrollbar, +# window, windowframe, windowtext. +#OPTIONS=windowcolors:status windowtext/window message windowtext/window +OPTIONS=windowcolors:status white/#000000 message white/#000000 menu white/#000000 text white/#000000 + +# +#HACKDIR=c:\games\nethack +# +# Note: On Windows HACKDIR defaults to the location +# of the NetHack.exe or NetHackw.exe file. +# Setting HACKDIR above will override that. +# +# LEVELS and SAVE default to HACKDIR +# +#LEVELS=c:\games\nethack\bones +#SAVE=c:\games\nethack\bones + +# *** CHARACTER GRAPHICS *** +# +# See the on-line help or the Guidebook for which symbols are in which +# positions. +# +# If you merely set the IBMgraphics option as above, NetHack will use IBM +# extended ASCII for dungeon characters. If you don't like the selections, +# you can make up your own via these graphics options, but you should still +# set IBMgraphics if you are using IBM graphics characters to get the correct +# processing. +# +# ================================================ +# An example using the IBM graphics character set: +#DUNGEON= 032 179 196 218 191 192 217 197 193 194 \ +# 180 195 249 239 239 254 254 240 241 249 \ +# 177 177 060 062 060 062 220 124 190 035 \ +# 244 247 249 247 042 042 186 205 046 035 \ +# 247 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 232 232 232 157 094 094 \ +# 094 094 +# +#EFFECTS= 179 196 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 +# +# ================================================ +# Some alternatives: +#DUNGEON= 032 186 205 201 187 200 188 206 202 203 \ +# 185 204 249 239 239 254 254 240 241 249 \ +# 177 177 060 062 060 062 095 124 092 035 \ +# 244 247 249 247 042 042 179 196 046 035 \ +# 247 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 034 094 094 094 094 \ +# 094 094 + +# ================================================ +# Here is a recommendation sent in by Michael Feir +# for use by blind NetHack players. +# +#DUNGEON= 032 124 045 124 124 124 124 045 045 045 \ +# 124 124 046 045 124 043 043 046 035 035 \ +# 060 062 060 062 095 092 035 126 126 126 \ +# 126 042 042 035 035 032 035 126 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 +# +#EFFECTS= 124 095 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 + + diff --git a/sys/wince/keypad.uu b/sys/wince/keypad.uu new file mode 100644 index 0000000..b36f639 --- /dev/null +++ b/sys/wince/keypad.uu @@ -0,0 +1,8 @@ +begin 600 keypad.bmp +M0DV^`````````#X````H````<`````@````!``$``````(`````````````` +M`````````````````/___P#__________________P``[__[____________ +M_R0``.?_\_?C]__W__?_]^MU``#CP>/GU?/AX\/OY_O5=0``X>/#Q\'QX\'C +MS^?YZR4``/_W_^?5\^?_\^__^]5M``#____WX_?O__OW__?_)```________ +*__________\``.?5 +` +end diff --git a/sys/wince/menubar.uu b/sys/wince/menubar.uu new file mode 100644 index 0000000..b4f07c5 --- /dev/null +++ b/sys/wince/menubar.uu @@ -0,0 +1,12 @@ +begin 600 menubar.bmp +M0DUV`0```````'8````H````(````!`````!``0````````!```````````` +M````````````````````@```@````("``(````"``(``@(```,#`P`"`@(`` +M``#_``#_````__\`_P```/\`_P#__P``____`'=W=W=W=W=W=W=W=W=W=W=W +M=W=W=W=W=W=W=W=W=W=W=$1$1$1$1'=W=W=W=W=W=W1$_T1/]$1W=W=W=W=W +M=W=T1/_T__1$=W=W=W=W=W=W=/______]'=W=P<'!P=W=W3_______1W=W#P +M\/#P=W=T3__T__]$=W */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include "winMS.h" +#include "mhaskyn.h" + +int mswin_yes_no_dialog( const char *question, const char *choices, int def) +{ + return '\032'; +} + diff --git a/sys/wince/mhaskyn.h b/sys/wince/mhaskyn.h new file mode 100644 index 0000000..a386b09 --- /dev/null +++ b/sys/wince/mhaskyn.h @@ -0,0 +1,11 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINAskYesNO_h +#define MSWINAskYesNO_h + +#include "winMS.h" + +int mswin_yes_no_dialog( const char *question, const char *choices, int def); + +#endif /* MSWINAskYesNO_h */ diff --git a/sys/wince/mhcmd.c b/sys/wince/mhcmd.c new file mode 100644 index 0000000..0ff3b76 --- /dev/null +++ b/sys/wince/mhcmd.c @@ -0,0 +1,1378 @@ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include +#include "mhcmd.h" +#include "mhinput.h" +#include "mhcolor.h" + +static TCHAR szNHCmdWindowClass[] = TEXT("MSNethackCmdWndClass"); + +#ifndef C +# define C(c) (0x1f & (c)) +#endif + +/* cell status 0 */ +#define NH_CST_CHECKED 1 + +/* fonts */ +#define NH_CMDPAD_FONT_NORMAL 0 +#define NH_CMDPAD_FONT_MAX 0 + +/* type of the cell */ +#define NH_CELL_REG 0 +#define NH_CELL_CTRL 1 +#define NH_CELL_CAP 2 +#define NH_CELL_SHIFT 3 +#define NH_CELL_LAYOUT_NEW 4 +#define NH_CELL_LAYOUT_MENU 5 + +#define NH_CMDSET_MAXSIZE 64 + +/* Keypad cell information + + NHCmdPadCell.cell_type NHCmdPadCell.data + ----------- ---------- + NH_CELL_REG (int)>=0 - index in the current keypad layout set (loads a new layout) + -1 - restore default (saved) layout + NH_CELL_CTRL not used + NH_CELL_CAP not used + NH_CELL_SHIFT not used + NH_CELL_LAYOUT_NEW pointer to the new keypad layout layout (NHCmdLayout*) + NH_CELL_LAYOUT_MENU pointer to the layout set (NHCmdSet* - if NULL then nhcmdset_default is used) +*/ +typedef struct t_NHCmdPadCell { + UINT cmd_code; /* Windows command code (menu processing - not implemented - set to -1) */ + char f_char[16]; /* nethack char */ + char text[16]; /* display text */ + int image; /* >0 - image ID in IDB_KEYPAD bitmap + <=0 - absolute index of the font table */ + int type; /* cell type */ + int mult; /* cell width multiplier */ + void* data; /* internal data for the cell type */ +} NHCmdPadCell, *PNHCmdPadCell; + +/* command layout */ +typedef struct t_NHCmdLayout { + char name[64]; + int rows; + int columns; + NHCmdPadCell cells[]; +} NHCmdLayout, *PNHCmdLayout; + +/* set of command layouts */ +typedef struct t_NHCmdSet { + int count; + struct t_NHCmdSetElem { + PNHCmdLayout layout; + BOOL free_on_destroy; + } elements[NH_CMDSET_MAXSIZE]; +} NHCmdSet, *PNHCmdSet; + +/* display cell layout */ +typedef struct t_NHCmdPadLayoutCell { + POINT orig; /* origin of the cell rect */ + BYTE type; /* cell type */ + int state; /* cell state */ +} NHCmdPadLayoutCell, *PNHCmdPadLayoutCell; + +/* command window data */ +typedef struct mswin_nethack_cmd_window { + SIZE cell_size; /* cell size */ + HFONT font[NH_CMDPAD_FONT_MAX+1]; /* fonts for cell text */ + HBITMAP images; /* key images map */ + int active_cell; /* current active cell */ + + boolean is_caps; /* is CAPS selected */ + boolean is_ctrl; /* is CRTL selected */ + boolean is_shift; /* is SHIFT selected */ + + PNHCmdLayout layout_current; /* current layout */ + PNHCmdLayout layout_save; /* saved layout */ + PNHCmdPadLayoutCell cells; /* display cells */ + +#if defined(WIN_CE_SMARTPHONE) + PNHCmdLayout layout_selected; /* since we use + layout command for menu also + we need to store the layout + that was selected by a user + */ +#endif +} NHCmdWindow, *PNHCmdWindow; + + +LRESULT CALLBACK NHCommandWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_command_window_class(); +static void LayoutCmdWindow(HWND hWnd); +static void SetCmdWindowLayout(HWND hWnd, PNHCmdLayout layout); +static int CellFromPoint(PNHCmdWindow data, POINT pt ); +static void CalculateCellSize(HWND hWnd, LPSIZE pSize, LPSIZE windowSize); +static void HighlightCell(HWND hWnd, int cell, BOOL isSelected); +static void ActivateCell(HWND hWnd, int cell); +static void PushNethackCommand( const char* cmd_char_str, int is_ctrl ); + +/*------------------- keyboard keys layout functions -----------------------*/ +PNHCmdLayout nhcmdlayout_create( const char* name, int rows, int columns ); +void nhcmdlayout_init( PNHCmdLayout p, PNHCmdPadCell cells ); +#define nhcmdlayout_rows(p) ((p)->rows) +#define nhcmdlayout_columns(p) ((p)->columns) +#define nhcmdlayout_row(p, x) (&((p)->cells[(p)->columns*(x)])) +#define nhcmdlayout_cell(p, x, y) (&((p)->cells[(p)->columns*(x)+(y)])) +#define nhcmdlayout_cell_direct(p, i) (&((p)->cells[(i)])) +void nhcmdlayout_destroy(PNHCmdLayout p); + +/*----------------- keyboard keys layout set functions ---------------------*/ +PNHCmdSet nhcmdset_create(); +int nhcmdset_count( PNHCmdSet p ); +PNHCmdLayout nhcmdset_get( PNHCmdSet p, int index ); +const char* nhcmdset_get_name( PNHCmdSet p, int index ); +void nhcmdset_add( PNHCmdSet p, PNHCmdLayout layout ); +void nhcmdset_destroy( PNHCmdSet p); + +/*-------------------- message handlers -----------------------------------*/ +static void onPaint(HWND hWnd); // on WM_PAINT +static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam); // on WM_CREATE +static void onMouseDown(HWND hWnd, WPARAM wParam, LPARAM lParam); // on WM_LBUTTONDOWN +static void onMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam); // on WM_MOUSEMOVE +static void onMouseUp(HWND hWnd, WPARAM wParam, LPARAM lParam); // on WM_LBUTTONUP + +/*----------------------- static data -------------------------------------*/ +static PNHCmdSet nhcmdset_current = 0; +static PNHCmdSet nhcmdset_default = 0; + +/*---------------------- Pre-definde keyboard layouts --------------------*/ +#ifdef WIN_CE_SMARTPHONE + +/* dimensions of the command pad */ +#define NH_CMDPAD_ROWS 4 +#define NH_CMDPAD_COLS 3 +#define NH_CMDPAD_CELLNUM (NH_CMDPAD_COLS*NH_CMDPAD_ROWS) + +/* layout indexes */ +#define NH_LAYOUT_GENERAL 0 +#define NH_LAYOUT_MOVEMENT 1 +#define NH_LAYOUT_ATTACK 2 +#define NH_LAYOUT_ITEM_HANDLING 3 +#define NH_LAYOUT_CONTROLS 4 +#define NH_LAYOUT_ADV_MOVEMENT 5 + +/* template menu layout */ +NHCmdPadCell cells_layout_menu[NH_CMDPAD_CELLNUM] = +{ + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", "<<", -NH_CMDPAD_FONT_NORMAL, NH_CELL_LAYOUT_NEW, 1 , NULL }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1 , (void*)-1 }, + { -1, "", ">>", -NH_CMDPAD_FONT_NORMAL, NH_CELL_LAYOUT_NEW, 1 , NULL } +}; + +/* movement layout */ +NHCmdPadCell cells_layout_movement[NH_CMDPAD_CELLNUM] = +{ + { -1, "7", "7", 1, NH_CELL_REG, 1, (void*)-1 }, + { -1, "8", "8", 2, NH_CELL_REG, 1, (void*)-1 }, + { -1, "9", "9", 3, NH_CELL_REG, 1, (void*)-1 }, + { -1, "4", "4", 4, NH_CELL_REG, 1, (void*)-1 }, + { -1, ".", ".", 5, NH_CELL_REG, 1, (void*)-1 }, + { -1, "6", "6", 6, NH_CELL_REG, 1, (void*)-1 }, + { -1, "1", "1", 7, NH_CELL_REG, 1, (void*)-1 }, + { -1, "2", "2", 8, NH_CELL_REG, 1, (void*)-1 }, + { -1, "3", "3", 9, NH_CELL_REG, 1, (void*)-1 }, + { -1, "<", "<", 10, NH_CELL_REG, 1, (void*)-1 }, + { -1, ">", ">", 12, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1, 0 } +}; + +/* attack layout */ +NHCmdPadCell cells_layout_attack[NH_CMDPAD_CELLNUM] = +{ + { -1, "t", "t", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "w", "w", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "x", "x", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "f", "f", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "z", "z", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "Z", "Z", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "r", "r", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "a", "a", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "q", "q", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x04", "^D", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "F", "F", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1 , 0 } +}; + +/* item handling layout */ +NHCmdPadCell cells_layout_item_handling[NH_CMDPAD_CELLNUM] = +{ + { -1, "W", "W", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "P", "P", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "d", "d", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "T", "T", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "R", "R", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "D", "D", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "=", "=", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "i", "i", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "[", "[", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "A", "A", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "I", "I", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1 , 0 } +}; + +/* General */ +NHCmdPadCell cells_layout_general[NH_CMDPAD_CELLNUM] = +{ + { -1, "q", "q", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "e", "e", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "l", "l", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "s", "s", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "E", "E", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x04", "^D", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "c", "c", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "o", "o", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "p", "p", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ":", ":", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ",", ",", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1 , 0 } +}; + +/* game controls layout */ +NHCmdPadCell cells_layout_game[NH_CMDPAD_CELLNUM] = +{ + { -1, "S", "S", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "h", "h", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "C", "C", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "@", "@", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\\", "\\", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "O", "O", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "&", "&", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x18", "^X", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x10", "^P", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "#", "#", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1 , 0 } +}; + +/* advanced movement layout */ +NHCmdPadCell cells_layout_adv_movement[NH_CMDPAD_CELLNUM] = +{ + { -1, "g", "g", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)NH_LAYOUT_MOVEMENT }, + { -1, "G", "G", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)NH_LAYOUT_MOVEMENT }, + { -1, "m", "m", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)NH_LAYOUT_MOVEMENT }, + { -1, "M", "M", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)NH_LAYOUT_MOVEMENT }, + { -1, "_", "_", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x14", "^T", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "j", "j", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", 13, NH_CELL_LAYOUT_MENU, 1 , 0 } +}; + +#else /* !WIN_CE_SMARTPHONE */ + +/* dimensions of the command pad */ +#define NH_CMDPAD_ROWS 4 +#define NH_CMDPAD_COLS 14 +#define NH_CMDPAD_CELLNUM (NH_CMDPAD_COLS*NH_CMDPAD_ROWS) + +/* lowercase layout */ +NHCmdPadCell cells_layout_mod1[NH_CMDPAD_ROWS*NH_CMDPAD_COLS] = +{ + { -1, "7", "7", 1, NH_CELL_REG, 1, (void*)-1 }, + { -1, "8", "8", 2, NH_CELL_REG, 1, (void*)-1 }, + { -1, "9", "9", 3, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x1b", "Esc", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for ESC */ + { -1, "?", "?", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "*", "*", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ",", ",", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "/", "/", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ":", ":", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ";", ";", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "-", "-", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "#", "#", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "^", "^", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "4", "4", 4, NH_CELL_REG, 1, (void*)-1 }, + { -1, "5", "5", 5, NH_CELL_REG, 1, (void*)-1 }, + { -1, "6", "6", 6, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "CAP", -NH_CMDPAD_FONT_NORMAL, NH_CELL_CAP, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for CAPS */ + { -1, "a", "a", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "b", "b", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "c", "c", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "d", "d", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "e", "e", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "f", "f", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "g", "g", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "h", "h", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "i", "i", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "1", "1", 7, NH_CELL_REG, 1, (void*)-1 }, + { -1, "2", "2", 8, NH_CELL_REG, 1, (void*)-1 }, + { -1, "3", "3", 9, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "Shft", -NH_CMDPAD_FONT_NORMAL, NH_CELL_SHIFT, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for shift */ + { -1, "j", "j", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "k", "k", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "l", "l", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "m", "m", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "n", "n", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "o", "o", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "p", "p", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "q", "q", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "r", "r", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "<", "<", 10, NH_CELL_REG, 1, (void*)-1 }, + { -1, ".", ".", 11, NH_CELL_REG, 1, (void*)-1 }, + { -1, ">", ">", 12, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "Ctrl", -NH_CMDPAD_FONT_NORMAL, NH_CELL_CTRL, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for CTRL */ + { -1, "s", "s", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "t", "t", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "u", "u", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "v", "v", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "w", "w", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "x", "x", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "y", "y", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "z", "z", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\\", "\\", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 } +}; + +/* uppercase layout */ +NHCmdPadCell cells_layout_mod2[-NH_CMDPAD_ROWS*-NH_CMDPAD_COLS] = +{ + { -1, "7", "7", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "8", "8", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "9", "9", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\x1b", "Esc", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for ESC */ + { -1, "?", "?", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "*", "*", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "[", "[", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "(", "(", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ")", ")", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "+", "+", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "=", "=", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "\"", "\"", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "$", "$", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "4", "4", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "5", "5", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "6", "6", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "CAP", -NH_CMDPAD_FONT_NORMAL, NH_CELL_CAP, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for CAPS */ + { -1, "A", "A", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "B", "B", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "C", "C", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "D", "D", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "E", "E", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "F", "F", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "G", "G", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "H", "H", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "I", "I", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "1", "1", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "2", "2", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "3", "3", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "Shft", -NH_CMDPAD_FONT_NORMAL, NH_CELL_SHIFT, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for shift */ + { -1, "J", "J", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "K", "K", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "L", "L", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "M", "M", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "N", "N", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "O", "O", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "P", "P", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "Q", "Q", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "R", "R", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + + { -1, "<", "<", 10, NH_CELL_REG, 1, (void*)-1 }, + { -1, "0", "0", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, ">", ">", 12, NH_CELL_REG, 1, (void*)-1 }, + { -1, " ", "Ctrl", -NH_CMDPAD_FONT_NORMAL, NH_CELL_CTRL, 2 , NULL }, + { -1, " ", "", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 0 , NULL }, /* complement for CTRL */ + { -1, "S", "S", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "T", "T", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "U", "U", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "V", "V", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "W", "W", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "X", "X", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "Y", "Y", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "Z", "Z", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 }, + { -1, "@", "@", -NH_CMDPAD_FONT_NORMAL, NH_CELL_REG, 1, (void*)-1 } +}; + +#endif /* !WIN_CE_SMARTPHONE */ + +/*-------------------------------------------------------------------------*/ +HWND mswin_init_command_window () { + static int run_once = 0; + HWND ret; + + /* register window class */ + if( !run_once ) { + register_command_window_class(); + run_once = 1; + } + + /* create window */ + ret = CreateWindow( + szNHCmdWindowClass, /* registered class name */ + NULL, /* window name */ + WS_CHILD | WS_CLIPSIBLINGS, /* window style */ + 0, /* horizontal position of window - set it later */ + 0, /* vertical position of window - set it later */ + 0, /* window width - set it later */ + 0, /* window height - set it later*/ + GetNHApp()->hMainWnd, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL ); /* window-creation data */ + if( !ret ) { + panic("Cannot create command window"); + } + return ret; +} +/*-------------------------------------------------------------------------*/ +/* calculate mimimum window size */ +void mswin_command_window_size (HWND hwnd, LPSIZE sz) +{ + SIZE cell_size; + PNHCmdWindow data; + data = (PNHCmdWindow)GetWindowLong(hwnd, GWL_USERDATA); + if( !data ) { + sz->cx = sz->cy = 0; + } else { + CalculateCellSize(hwnd, &cell_size, sz); + sz->cx = max( + cell_size.cx*nhcmdlayout_columns(data->layout_current)+2*GetSystemMetrics(SM_CXBORDER), + sz->cx + ); + sz->cy = max( + cell_size.cy*nhcmdlayout_rows(data->layout_current)+2*GetSystemMetrics(SM_CYBORDER), + sz->cy + ); + } +} +/*-------------------------------------------------------------------------*/ +void register_command_window_class() +{ + WNDCLASS wcex; + PNHCmdLayout plt; + ZeroMemory( &wcex, sizeof(wcex)); + + /* window class */ + wcex.style = CS_NOCLOSE; + wcex.lpfnWndProc = (WNDPROC)NHCommandWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = mswin_get_brush(NHW_KEYPAD, MSWIN_COLOR_BG); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szNHCmdWindowClass; + + if( !RegisterClass(&wcex) ) { + panic("cannot register Map window class"); + } + + /* create default command set */ + nhcmdset_current = nhcmdset_default = nhcmdset_create(); + +#ifdef WIN_CE_SMARTPHONE + plt = nhcmdlayout_create("General", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_general); + nhcmdset_add(nhcmdset_current, plt); + + plt = nhcmdlayout_create("Movement", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_movement); + nhcmdset_add(nhcmdset_current, plt ); + + plt = nhcmdlayout_create("Attack", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_attack); + nhcmdset_add(nhcmdset_current, plt); + + plt = nhcmdlayout_create("Item Handling", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_item_handling); + nhcmdset_add(nhcmdset_current, plt); + + plt = nhcmdlayout_create("Game Controls", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_game); + nhcmdset_add(nhcmdset_current, plt); + + plt = nhcmdlayout_create("Advanced Movement", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_adv_movement); + nhcmdset_add(nhcmdset_current, plt); +#else /* ! WIN_CE_SMARTPHONE */ + plt = nhcmdlayout_create("lowercase", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_mod1); + nhcmdset_add(nhcmdset_current, plt); + + plt = nhcmdlayout_create("uppercase", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(plt, cells_layout_mod2); + nhcmdset_add(nhcmdset_current, plt); +#endif /* WIN_CE_SMARTPHONE */ +} +/*-------------------------------------------------------------------------*/ +LRESULT CALLBACK NHCommandWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHCmdWindow data; + int i; + + switch (message) + { + case WM_CREATE: + onCreate( hWnd, wParam, lParam ); + break; + + case WM_PAINT: + onPaint(hWnd); + break; + + case WM_SIZE: + LayoutCmdWindow(hWnd); + break; + + case WM_LBUTTONDOWN: + onMouseDown(hWnd, wParam, lParam); + return 0; + + case WM_MOUSEMOVE: + /* proceed only if if have mouse focus (set in onMouseDown() - + left mouse button is pressed) */ + if( GetCapture()==hWnd ) { + onMouseMove(hWnd, wParam, lParam); + return 0; + } else { + return 1; + } + break; + + case WM_LBUTTONUP: + /* proceed only if if have mouse focus (set in onMouseDown()) */ + if( GetCapture()==hWnd ) { + onMouseUp(hWnd, wParam, lParam); + return 0; + } else { + return 1; + } + break; + + case WM_DESTROY: + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + for(i=0; i<=NH_CMDPAD_FONT_MAX; i++ ) + if( data->font[i] ) DeleteObject(data->font[i]); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return FALSE; +} +/*-------------------------------------------------------------------------*/ +void onPaint(HWND hWnd) +{ + PNHCmdWindow data; + PAINTSTRUCT ps; + HDC hDC; + int x, y; + TCHAR wbuf[BUFSZ]; + HGDIOBJ saveFont; + BITMAP bm; + int cell_index; + + /* get window data */ + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + + hDC = BeginPaint(hWnd, &ps); + + if( !IsRectEmpty(&ps.rcPaint) ) { + HGDIOBJ oldBr; + HBRUSH hbrPattern; + COLORREF OldBg, OldFg; + HPEN hPen; + HGDIOBJ hOldPen; + + saveFont = SelectObject(hDC, data->font[NH_CMDPAD_FONT_NORMAL]); + OldBg = SetBkColor(hDC, mswin_get_color(NHW_KEYPAD, MSWIN_COLOR_BG)); + OldFg = SetTextColor(hDC, mswin_get_color(NHW_KEYPAD, MSWIN_COLOR_FG)); + + GetObject(data->images, sizeof(BITMAP), (LPVOID)&bm); + + hbrPattern = CreatePatternBrush(data->images); + hPen = CreatePen(PS_SOLID, 1, mswin_get_color(NHW_KEYPAD, MSWIN_COLOR_FG)); + + for( x=0, cell_index = 0; xlayout_current); x++ ) + for( y=0; ylayout_current); y++, cell_index++ ) { + RECT cell_rt; + POINT pt[5]; + PNHCmdPadCell p_cell_data; + + p_cell_data = nhcmdlayout_cell_direct(data->layout_current, cell_index); + + /* calculate the cell rectangle */ + cell_rt.left = data->cells[cell_index].orig.x; + cell_rt.top = data->cells[cell_index].orig.y; + cell_rt.right = data->cells[cell_index].orig.x + data->cell_size.cx*p_cell_data->mult; + cell_rt.bottom = data->cells[cell_index].orig.y + data->cell_size.cy; + + /* draw border */ + hOldPen = SelectObject(hDC, hPen); + pt[0].x = cell_rt.left; + pt[0].y = cell_rt.top; + pt[1].x = cell_rt.right; + pt[1].y = cell_rt.top; + pt[2].x = cell_rt.right; + pt[2].y = cell_rt.bottom; + pt[3].x = cell_rt.left; + pt[3].y = cell_rt.bottom; + pt[4].x = cell_rt.left; + pt[4].y = cell_rt.top; + Polyline(hDC, pt, 5); + SelectObject(hDC, hOldPen); + + /* calculate clipping rectangle for the text */ + cell_rt.left++; + cell_rt.top ++; + cell_rt.right--; + cell_rt.bottom--; + + /* draw the cell text */ + if( p_cell_data->image<=0 ) { + SelectObject(hDC, data->font[ -p_cell_data->image ]); + DrawText(hDC, + NH_A2W(p_cell_data->text, wbuf, BUFSZ), + strlen(p_cell_data->text), + &cell_rt, + DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX + ); + } else { + /* draw bitmap */ + int bmOffset; + RECT bitmap_rt; + + bmOffset = (p_cell_data->image - 1)*bm.bmHeight; + + bitmap_rt.left = ((cell_rt.left+cell_rt.right) - min(bm.bmHeight, (cell_rt.right-cell_rt.left)))/2; + bitmap_rt.top = ((cell_rt.bottom+cell_rt.top) - min(bm.bmHeight, (cell_rt.bottom-cell_rt.top)))/2; + bitmap_rt.right = bitmap_rt.left + min(bm.bmHeight, (cell_rt.right-cell_rt.left)); + bitmap_rt.bottom = bitmap_rt.top + min(bm.bmHeight, (cell_rt.bottom-cell_rt.top)); + + SetBrushOrgEx(hDC, bitmap_rt.left-bmOffset, bitmap_rt.top, NULL); + oldBr = SelectObject(hDC, hbrPattern); + PatBlt( + hDC, + bitmap_rt.left, + bitmap_rt.top, + bitmap_rt.right-bitmap_rt.left, + bitmap_rt.bottom-bitmap_rt.top, + PATCOPY); + SelectObject(hDC, oldBr); + } + + /* invert the cell if it is selected */ + if( data->cells[cell_index].state == NH_CST_CHECKED ) { + IntersectRect( &cell_rt, &cell_rt, &ps.rcPaint); + PatBlt( hDC, + cell_rt.left, + cell_rt.top, + cell_rt.right - cell_rt.left, + cell_rt.bottom - cell_rt.top, + DSTINVERT + ); + } + } + + SetTextColor(hDC, OldFg); + SetBkColor(hDC, OldBg); + SelectObject(hDC, saveFont); + DeleteObject(hbrPattern); + DeleteObject(hPen); + } + EndPaint(hWnd, &ps); +} +/*-------------------------------------------------------------------------*/ +void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHCmdWindow data; + + /* set window data */ + data = (PNHCmdWindow)malloc(sizeof(NHCmdWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHCmdWindow)); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + data->active_cell = -1; + + /* load images bitmap */ + data->images = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_KEYPAD)); + if( !data->images ) panic("cannot load keypad bitmap"); + + /* create default layouts */ + data->layout_current = 0; + data->layout_save = 0; + data->cells = 0; + +#if defined(WIN_CE_SMARTPHONE) + data->layout_selected = nhcmdset_get(nhcmdset_current, 0); +#endif + + /* set default layout */ + SetCmdWindowLayout(hWnd, nhcmdset_get(nhcmdset_current, 0)); +} +/*-------------------------------------------------------------------------*/ +void LayoutCmdWindow(HWND hWnd) +{ + RECT clrt; + SIZE windowSize; + PNHCmdWindow data; + int i, j; + int x, y; + LOGFONT lgfnt; + int index; + + GetClientRect(hWnd, &clrt); + if( IsRectEmpty(&clrt) ) return; + + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( !data->layout_current ) return; + + /* calculate cell size */ + windowSize.cx = clrt.right-clrt.left; + windowSize.cy = clrt.bottom-clrt.top; + CalculateCellSize(hWnd, &data->cell_size, &windowSize); + + /* initialize display cells aray */ + x = 0; + y = 0; + for( i=0, index=0; ilayout_current); i++ ) { + for( j=0; jlayout_current); j++, index++ ) { + data->cells[index].orig.x = x; + data->cells[index].orig.y = y; + data->cells[index].type = nhcmdlayout_cell_direct(data->layout_current, index)->type; + + switch(data->cells[index].type) { + case NH_CELL_CTRL: + data->cells[index].state = data->is_ctrl? NH_CST_CHECKED : 0; + break; + case NH_CELL_CAP: + data->cells[index].state = data->is_caps? NH_CST_CHECKED : 0; + break; + case NH_CELL_SHIFT: + data->cells[index].state = data->is_shift? NH_CST_CHECKED : 0; + break; + default: + data->cells[index].state = 0; + } + + x += data->cell_size.cx * nhcmdlayout_cell_direct(data->layout_current, index)->mult; + } + x = 0; + y += data->cell_size.cy; + } + + /* create font for display cell text */ + for(i=0; i<=NH_CMDPAD_FONT_MAX; i++ ) + if( data->font[i] ) DeleteObject(data->font[i]); + + ZeroMemory( &lgfnt, sizeof(lgfnt) ); + lgfnt.lfHeight = data->cell_size.cy; // height of font + lgfnt.lfWidth = 0; // average character width + lgfnt.lfEscapement = 0; // angle of escapement + lgfnt.lfOrientation = 0; // base-line orientation angle + lgfnt.lfWeight = FW_NORMAL; // font weight + lgfnt.lfItalic = FALSE; // italic attribute option + lgfnt.lfUnderline = FALSE; // underline attribute option + lgfnt.lfStrikeOut = FALSE; // strikeout attribute option + lgfnt.lfCharSet = ANSI_CHARSET; // character set identifier + lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision + lgfnt.lfClipPrecision = CLIP_CHARACTER_PRECIS; // clipping precision + lgfnt.lfQuality = DEFAULT_QUALITY; // output quality + if( iflags.wc_font_message && + *iflags.wc_font_message ) { + lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family + NH_A2W( iflags.wc_font_message, lgfnt.lfFaceName, LF_FACESIZE); + } else { + lgfnt.lfPitchAndFamily = VARIABLE_PITCH; // pitch and family + } + data->font[NH_CMDPAD_FONT_NORMAL] = CreateFontIndirect(&lgfnt); + + InvalidateRect(hWnd, NULL, TRUE); +} +/*-------------------------------------------------------------------------*/ +void SetCmdWindowLayout(HWND hWnd, PNHCmdLayout layout) +{ + PNHCmdWindow data; + int size; + + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data->layout_current == layout ) return; + + data->layout_current = layout; + size = sizeof(NHCmdPadLayoutCell)*nhcmdlayout_rows(layout)*nhcmdlayout_columns(layout); + data->cells = (PNHCmdPadLayoutCell)realloc(data->cells, size); + ZeroMemory(data->cells, size); + LayoutCmdWindow(hWnd); +} +/*-------------------------------------------------------------------------*/ +void onMouseDown(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHCmdWindow data; + POINT mpt; + + /* get mouse coordinates */ + mpt.x = LOWORD(lParam); + mpt.y = HIWORD(lParam); + + /* map mouse coordinates to the display cell */ + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + data->active_cell = CellFromPoint(data, mpt); + if( data->active_cell==-1 ) return; + + /* set mouse focus to the current window */ + SetCapture(hWnd); + + /* invert the selection */ + HighlightCell(hWnd, data->active_cell, (data->cells[data->active_cell].state!=NH_CST_CHECKED) ); +} +/*-------------------------------------------------------------------------*/ +void onMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHCmdWindow data; + POINT mpt; + int newActiveCell; + + /* get mouse coordinates */ + mpt.x = LOWORD(lParam); + mpt.y = HIWORD(lParam); + + /* map mouse coordinates to the display cell */ + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + newActiveCell = CellFromPoint(data, mpt); + if( data->active_cell == -1 ) return; + + /* if mouse is within orginal display cell - select the cell otherwise + clear the selection */ + switch( nhcmdlayout_cell_direct(data->layout_current, data->active_cell)->type ) { + case NH_CELL_REG: + HighlightCell(hWnd, data->active_cell, + (newActiveCell==data->active_cell) ); + break; + + case NH_CELL_CTRL: + HighlightCell(hWnd, data->active_cell, + ((newActiveCell==data->active_cell)? !data->is_ctrl : data->is_ctrl) ); + break; + + case NH_CELL_CAP: + HighlightCell(hWnd, data->active_cell, + ((newActiveCell==data->active_cell)? !data->is_caps : data->is_caps) ); + break; + } +} +/*-------------------------------------------------------------------------*/ +void onMouseUp(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHCmdWindow data; + + /* release mouse capture */ + ReleaseCapture(); + + /* get active display cell */ + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data->active_cell == -1 ) return; + + ActivateCell(hWnd, data->active_cell); + + data->active_cell = -1; +} +/*-------------------------------------------------------------------------*/ +void ActivateCell(HWND hWnd, int cell) +{ + PNHCmdWindow data; + PNHCmdPadCell p_cell_data; + int i; + + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( !data ) return; + p_cell_data = nhcmdlayout_cell_direct(data->layout_current, cell); + + /* act depending on the cell type: + CAPS - change layout + CTRL - modify CTRL status + REG - place keyboard event on the nethack input queue + */ + switch( p_cell_data->type ) { + case NH_CELL_REG: + if( data->is_ctrl ) { + PushNethackCommand(p_cell_data->f_char, 1); + + data->is_ctrl = 0; + for( i=0; ilayout_current)*nhcmdlayout_columns(data->layout_current); i++ ) { + if( nhcmdlayout_cell_direct(data->layout_current, i)->type == NH_CELL_CTRL ) { + HighlightCell(hWnd, i, data->is_ctrl); + } + } + } else { + PushNethackCommand(p_cell_data->f_char, 0); + } + HighlightCell(hWnd, cell, FALSE); + + // select a new layout if present + i = (int)p_cell_data->data; + if( i==-1 ) { + if( data->layout_save ) SetCmdWindowLayout(hWnd, data->layout_save); + data->layout_save = NULL; + } else { + if( !data->layout_save ) data->layout_save = data->layout_current; + SetCmdWindowLayout(hWnd, nhcmdset_get(nhcmdset_current, i)); + } + + if( !data->is_shift ) break; + // else fall through and reset the shift + + case NH_CELL_SHIFT: + data->is_shift = !data->is_shift; + SetCmdWindowLayout( + hWnd, + (data->is_shift ^ data->is_caps)? nhcmdset_get(nhcmdset_current, 1) : nhcmdset_get(nhcmdset_current, 0) + ); + data->cells[cell].state = data->is_shift? NH_CST_CHECKED : 0; + InvalidateRect(hWnd, NULL, TRUE); + break; + + case NH_CELL_CTRL: + data->is_ctrl = !data->is_ctrl; + HighlightCell(hWnd, cell, data->is_ctrl); + break; + + case NH_CELL_CAP: + data->is_caps = !data->is_caps; + SetCmdWindowLayout( + hWnd, + (data->is_shift ^ data->is_caps)? nhcmdset_get(nhcmdset_current, 1) : nhcmdset_get(nhcmdset_current, 0) + ); + data->cells[cell].state = data->is_caps? NH_CST_CHECKED : 0; + InvalidateRect(hWnd, NULL, TRUE); + break; + + case NH_CELL_LAYOUT_NEW: { + PNHCmdLayout pLayout; + + HighlightCell(hWnd, cell, FALSE); + + pLayout = (PNHCmdLayout)p_cell_data->data; + if( pLayout ) { + SetCmdWindowLayout(hWnd, pLayout); + } + } break; + + case NH_CELL_LAYOUT_MENU: { + winid wid; + int i; + anything any; + menu_item* selected = 0; + PNHCmdSet pSet; + + HighlightCell(hWnd, cell, FALSE); + + pSet = (PNHCmdSet)p_cell_data->data; + if( !pSet ) pSet = nhcmdset_default; + + wid = mswin_create_nhwindow(NHW_MENU); + mswin_start_menu(wid); + for( i=0; ilayout_selected = (PNHCmdLayout)selected[0].item.a_void; +#endif + SetCmdWindowLayout(hWnd, (PNHCmdLayout)selected[0].item.a_void ); + } + } break; + + } +} +/*-------------------------------------------------------------------------*/ +int CellFromPoint(PNHCmdWindow data, POINT pt ) +{ + int i; + for( i=0; ilayout_current)*nhcmdlayout_columns(data->layout_current); i++ ) { + RECT cell_rt; + cell_rt.left = data->cells[i].orig.x; + cell_rt.top = data->cells[i].orig.y; + cell_rt.right = data->cells[i].orig.x + data->cell_size.cx*nhcmdlayout_cell_direct(data->layout_current, i)->mult; + cell_rt.bottom = data->cells[i].orig.y + data->cell_size.cy; + if( PtInRect(&cell_rt, pt) ) return i; + } + return -1; +} +/*-------------------------------------------------------------------------*/ +void CalculateCellSize(HWND hWnd, LPSIZE pSize, LPSIZE pWindowSize) +{ + HDC hdc; + PNHCmdWindow data; + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( !data ) return; + + hdc = GetDC(hWnd); + + /* if windows size is specified - attempt ro stretch cells across + the the window size. If not - make default cell size based on + 10 points font. Make sure that cell cesize does not exceeds 20 points */ + if( pWindowSize->cx>0 ) + pSize->cx = pWindowSize->cx/nhcmdlayout_columns(data->layout_current); + else + pSize->cx = 10*GetDeviceCaps(hdc, LOGPIXELSX)/72; + pSize->cx = min(pSize->cx, 20*GetDeviceCaps(hdc, LOGPIXELSX)/72 ); + + if( pWindowSize->cy>0 ) + pSize->cy = pWindowSize->cy/nhcmdlayout_rows(data->layout_current); + else + pSize->cy = 10*GetDeviceCaps(hdc, LOGPIXELSY)/72; + pSize->cy = min(pSize->cy, 20*GetDeviceCaps(hdc, LOGPIXELSY)/72 ); + + ReleaseDC(hWnd, hdc); +} +/*-------------------------------------------------------------------------*/ +void HighlightCell(HWND hWnd, int cell, BOOL isSelected) +{ + HDC hDC; + PNHCmdWindow data; + int prevState; + + data = (PNHCmdWindow)GetWindowLong(hWnd, GWL_USERDATA); + prevState = data->cells[cell].state; + data->cells[cell].state = (isSelected)? NH_CST_CHECKED : 0; + + if( prevState!=data->cells[cell].state ) { + hDC = GetDC(hWnd); + PatBlt( hDC, + data->cells[cell].orig.x+1, + data->cells[cell].orig.y+1, + data->cell_size.cx*nhcmdlayout_cell_direct(data->layout_current, cell)->mult - 2, + data->cell_size.cy - 2, + DSTINVERT + ); + ReleaseDC(hWnd, hDC); + } +} +/*-------------------------------------------------------------------------*/ +void PushNethackCommand( const char* cmd_char_str, int is_ctrl ) +{ + while( *cmd_char_str ) { + if( is_ctrl ) { NHEVENT_KBD( C(*cmd_char_str) ); } + else { NHEVENT_KBD( *cmd_char_str ); } + cmd_char_str++; + } +} + +/*-------------------------------------------------------------------------*/ +/*------------------- keyboard keys layout functions ----------------------*/ +/*-------------------------------------------------------------------------*/ +PNHCmdLayout nhcmdlayout_create(const char* name, int rows, int columns) +{ + PNHCmdLayout p; + int i; + + i = sizeof(NHCmdLayout)+rows*columns*sizeof(NHCmdPadCell); + p = (PNHCmdLayout)malloc(i); + ZeroMemory(p, i); + p->rows = rows; + p->columns = columns; + strncpy(p->name, name, sizeof(p->name)-1); + for(i=0; icells[i].cmd_code = -1; + p->cells[i].image = -NH_CMDPAD_FONT_NORMAL; + p->cells[i].type = 1; + p->cells[i].mult = 1; + } + return p; +} +/*-------------------------------------------------------------------------*/ +void nhcmdlayout_init( PNHCmdLayout p, PNHCmdPadCell cells ) +{ + memcpy(p->cells, cells, p->rows*p->columns*sizeof(NHCmdPadCell)); +} + +void nhcmdlayout_destroy(PNHCmdLayout p) +{ + free(p); +} + +/*-------------------------------------------------------------------------*/ +/*----------------- keyboard keys layout set functions --------------------*/ +/*-------------------------------------------------------------------------*/ +PNHCmdSet nhcmdset_create() +{ + PNHCmdSet p; + p = (PNHCmdSet)malloc(sizeof(NHCmdSet)); + ZeroMemory(p, sizeof(NHCmdSet)); + return p; +} +/*-------------------------------------------------------------------------*/ +int nhcmdset_count(PNHCmdSet p) +{ + assert(p); + return p->count; +} +/*-------------------------------------------------------------------------*/ +PNHCmdLayout nhcmdset_get( PNHCmdSet p, int index ) +{ + assert(p); + assert(index>=0 && indexcount); + return p->elements[index].layout; +} +/*-------------------------------------------------------------------------*/ +const char* nhcmdset_get_name( PNHCmdSet p, int index ) +{ + assert(p); + assert(index>=0 && indexcount); + return p->elements[index].layout->name; +} +/*-------------------------------------------------------------------------*/ +void nhcmdset_add( PNHCmdSet p, PNHCmdLayout layout ) +{ + assert(p); + assert(p->countelements[p->count].layout = layout; + p->elements[p->count].free_on_destroy = 0; + p->count++; +} +/*-------------------------------------------------------------------------*/ +void nhcmdset_destroy(PNHCmdSet p) +{ + int i=0; + assert(p); + for(i=0; icount; i++) { + if( p->elements[i].free_on_destroy ) { + nhcmdlayout_destroy( p->elements[i].layout ); + } + } + free(p); +} +/*-------------------------------------------------------------------------*/ + + +#if defined(WIN_CE_SMARTPHONE) +/* special keypad input handling for SmartPhone + the phone keypad maps to VK_* as shown below. + some keys might not be present, e.g. VK_TFLIP + sofkey1 softkey2 VK_TSOFT1, VK_TSOFT2 + ^ VK_TUP + < + > VK_TLEFT, VK_TACTION, VK_TRIGHT + v VK_TDOWN + home back VK_THOME, VK_TBACK + talk end VK_TTALK, VK_TEND + 1 2 3 VK_T0..VK_T9 + 4 5 6 ... + 7 8 9 ... + * 0 # VK_TSTAR, VK_TPOUND + other buttons include + VK_TRECORD + VK_TPOWER, VK_TVOLUMEUP, VK_TVOLUMEDOWN + VK_TFLIP +*/ +BOOL NHSPhoneTranslateKbdMessage(WPARAM wParam, LPARAM lParam, BOOL keyDown) +{ + PNHCmdWindow data; + int index = -1; + + /* get window data */ + data = (PNHCmdWindow)GetWindowLong(GetNHApp()->hCmdWnd, GWL_USERDATA); + if( !data ) return FALSE; + + switch (wParam) { + case VK_T0: + index = 10; + break; + + case VK_T1: + index = 0; + break; + + case VK_T2: + index = 1; + break; + + case VK_T3: + index = 2; + break; + + case VK_T4: + index = 3; + break; + + case VK_T5: + index = 4; + break; + + case VK_T6: + index = 5; + break; + + case VK_T7: + index = 6; + break; + + case VK_T8: + index = 7; + break; + + case VK_T9: + index = 8; + break; + + case VK_TSTAR: + index = 9; + break; + + case VK_TPOUND: + index = 11; + break; + } + + if( index>=0 ) { + HighlightCell(GetNHApp()->hCmdWnd, index, keyDown); + if( keyDown ) ActivateCell(GetNHApp()->hCmdWnd, index); + return TRUE; + } else { + return FALSE; + } +} +/*-------------------------------------------------------------------------*/ +void NHSPhoneSetKeypadFromString(const char* str) +{ + PNHCmdWindow data; + PNHCmdSet p = 0; + PNHCmdLayout layout_prev = 0; + PNHCmdLayout layout_cur = 0; + char buf[2][BUFSZ]; + int i, lcount; + char *s; + + assert(NH_CMDPAD_ROWS==4); + + data = (PNHCmdWindow)GetWindowLong(GetNHApp()->hCmdWnd, GWL_USERDATA); + if( !data ) return; + + p = nhcmdset_create(); + + ZeroMemory(buf, sizeof(buf)); + if( sscanf(str, "%s or %s", buf[1], buf[0])!=2 ) { + ZeroMemory(buf, sizeof(buf)); + strncpy(buf[0], str, sizeof(buf[0])-1); + } + + lcount = 10; /* create new layout on the first iteration */ + for(i=0; i<2; i++) { + s = buf[i]; + while( *s ) { + char c_start, c_end, c_char; + + /* parse character ranges */ + if( isalnum( (c_start=s[0]) ) && + s[1]=='-' && + isalnum( (c_end=s[2]) ) ) { + s += 2; + } else { + c_start = c_end = *s; + } + + for( c_char=c_start; c_char<=c_end; c_char++ ) { + if( lcount>=10 ) { + /* create layout */ + lcount = 0; + layout_prev = layout_cur; + layout_cur = nhcmdlayout_create("noname", NH_CMDPAD_ROWS, NH_CMDPAD_COLS); + nhcmdlayout_init(layout_cur, cells_layout_menu); + + nhcmdlayout_cell(layout_cur, 3, 0)->data = layout_prev; + nhcmdlayout_cell(layout_cur, 3, 2)->data = 0; + + nhcmdset_add( p, layout_cur ); + p->elements[p->count-1].free_on_destroy = 1; + + if( layout_prev ) { + nhcmdlayout_cell(layout_prev, 3, 2)->data = layout_cur; + } + } + + if( lcount==9 ) lcount=10; // skip '#' + nhcmdlayout_cell_direct(layout_cur, lcount)->f_char[0] = c_char; + if( c_char == '\033' ) { + strcpy(nhcmdlayout_cell_direct(layout_cur, lcount)->text, "esc"); + nhcmdlayout_cell_direct(layout_cur, lcount)->image = 14; /* 14 is a ESC symbol in IDB_KEYPAD */ + } else { + nhcmdlayout_cell_direct(layout_cur, lcount)->text[0] = c_char; + nhcmdlayout_cell_direct(layout_cur, lcount)->text[1] = '\x0'; + } + + /* increment character count in the current layout */ + lcount++; + } + + /* prepareg next charcter from the source string */ + s++; + } + } + + /* install the new set */ + if( nhcmdset_current!=nhcmdset_default ) nhcmdset_destroy( nhcmdset_current ); + nhcmdset_current = p; + SetCmdWindowLayout( + GetNHApp()->hCmdWnd, + nhcmdset_get(nhcmdset_current, 0) + ); +} +/*-------------------------------------------------------------------------*/ +void NHSPhoneSetKeypadDirection() +{ + PNHCmdWindow data; + + data = (PNHCmdWindow)GetWindowLong(GetNHApp()->hCmdWnd, GWL_USERDATA); + if( !data ) return; + + if( nhcmdset_current!=nhcmdset_default ) nhcmdset_destroy( nhcmdset_current ); + nhcmdset_current = nhcmdset_default; + SetCmdWindowLayout( + GetNHApp()->hCmdWnd, + nhcmdset_get(nhcmdset_current, NH_LAYOUT_MOVEMENT) + ); +} +/*-------------------------------------------------------------------------*/ +void NHSPhoneSetKeypadDefault() +{ + PNHCmdWindow data; + + data = (PNHCmdWindow)GetWindowLong(GetNHApp()->hCmdWnd, GWL_USERDATA); + if( !data ) return; + + if( nhcmdset_current!=nhcmdset_default ) nhcmdset_destroy( nhcmdset_current ); + nhcmdset_current = nhcmdset_default; + SetCmdWindowLayout( + GetNHApp()->hCmdWnd, + data->layout_selected ? data->layout_selected : nhcmdset_get(nhcmdset_current, 0) + ); +} + +#endif /* defined (WIN_CE_SMARTHPONE) */ diff --git a/sys/wince/mhcmd.h b/sys/wince/mhcmd.h new file mode 100644 index 0000000..87f4f4b --- /dev/null +++ b/sys/wince/mhcmd.h @@ -0,0 +1,25 @@ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINCMDWindow_h +#define MSWINCMDWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_command_window (); + +/* if either sz->cx or sz->cy are already set this function will + no modify it. It will adjust them to the minimum size + required by the command window */ +void mswin_command_window_size (HWND hwnd, LPSIZE sz); + +#if defined(WIN_CE_SMARTPHONE) +/* special keypad input handling for SmartPhone */ +BOOL NHSPhoneTranslateKbdMessage(WPARAM wParam, LPARAM lParam, BOOL keyDown); +void NHSPhoneSetKeypadFromString(const char* str); +void NHSPhoneSetKeypadDirection(); +void NHSPhoneSetKeypadDefault(); +#endif + +#endif /* MSWINCMDWindow_h */ diff --git a/sys/wince/mhcolor.c b/sys/wince/mhcolor.c new file mode 100644 index 0000000..0e32db1 --- /dev/null +++ b/sys/wince/mhcolor.c @@ -0,0 +1,217 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* color management and such */ + +#include "winMS.h" +#include "mhcolor.h" + +#define TOTAL_BRUSHES 10 +#define NHBRUSH_CODE(win, type) ((((win)&0xFF)<<8)|((type)&0xFF)) + +struct t_brush_table { + int code; + HBRUSH brush; + COLORREF color; +}; +static struct t_brush_table brush_table[TOTAL_BRUSHES]; +static int max_brush = 0; + +static struct t_brush_table default_brush_table[] = +{ + { NHBRUSH_CODE(NHW_STATUS, MSWIN_COLOR_FG), NULL, RGB(0, 0, 0) }, + { NHBRUSH_CODE(NHW_MESSAGE, MSWIN_COLOR_FG), NULL, RGB(0, 0, 0) }, + { NHBRUSH_CODE(NHW_STATUS, MSWIN_COLOR_FG), NULL, RGB(0, 0, 0) }, + { NHBRUSH_CODE(NHW_TEXT, MSWIN_COLOR_FG), NULL, RGB(0, 0, 0) }, + { NHBRUSH_CODE(NHW_KEYPAD, MSWIN_COLOR_FG), NULL, RGB(0, 0, 0) }, + { NHBRUSH_CODE(NHW_MAP, MSWIN_COLOR_FG), NULL, RGB(96, 96, 96) }, + + { NHBRUSH_CODE(NHW_MENU, MSWIN_COLOR_BG), NULL, RGB(255, 255, 255) }, + { NHBRUSH_CODE(NHW_MESSAGE, MSWIN_COLOR_BG), NULL, RGB(192, 192, 192) }, + { NHBRUSH_CODE(NHW_STATUS, MSWIN_COLOR_BG), NULL, RGB(192, 192, 192) }, + { NHBRUSH_CODE(NHW_TEXT, MSWIN_COLOR_BG), NULL, RGB(255, 255, 255) }, + { NHBRUSH_CODE(NHW_KEYPAD, MSWIN_COLOR_BG), NULL, RGB(255, 255, 255) }, + { NHBRUSH_CODE(NHW_MAP, MSWIN_COLOR_BG), NULL, RGB(192, 192, 192) }, + { -1, NULL, RGB(0, 0, 0) } +}; + +static void mswin_color_from_string(char *colorstring, HBRUSH* brushptr, COLORREF *colorptr); + +typedef struct ctv +{ + const char *colorstring; + COLORREF colorvalue; +} color_table_value; + +/* + * The color list here is a combination of: + * NetHack colors. (See mhmap.c) + * HTML colors. (See http://www.w3.org/TR/REC-html40/types.html#h-6.5 ) + */ + +static color_table_value color_table[] = { +/* NetHack colors */ + { "black", RGB(0x55, 0x55, 0x55)}, + { "red", RGB(0xFF, 0x00, 0x00)}, + { "green", RGB(0x00, 0x80, 0x00)}, + { "brown", RGB(0xA5, 0x2A, 0x2A)}, + { "blue", RGB(0x00, 0x00, 0xFF)}, + { "magenta", RGB(0xFF, 0x00, 0xFF)}, + { "cyan", RGB(0x00, 0xFF, 0xFF)}, + { "orange", RGB(0xFF, 0xA5, 0x00)}, + { "brightgreen", RGB(0x00, 0xFF, 0x00)}, + { "yellow", RGB(0xFF, 0xFF, 0x00)}, + { "brightblue", RGB(0x00, 0xC0, 0xFF)}, + { "brightmagenta", RGB(0xFF, 0x80, 0xFF)}, + { "brightcyan", RGB(0x80, 0xFF, 0xFF)}, + { "white", RGB(0xFF, 0xFF, 0xFF)}, +/* Remaining HTML colors */ + { "trueblack", RGB(0x00, 0x00, 0x00)}, + { "gray", RGB(0x80, 0x80, 0x80)}, + { "grey", RGB(0x80, 0x80, 0x80)}, + { "purple", RGB(0x80, 0x00, 0x80)}, + { "silver", RGB(0xC0, 0xC0, 0xC0)}, + { "maroon", RGB(0x80, 0x00, 0x00)}, + { "fuchsia", RGB(0xFF, 0x00, 0xFF)}, /* = NetHack magenta */ + { "lime", RGB(0x00, 0xFF, 0x00)}, /* = NetHack bright green */ + { "olive", RGB(0x80, 0x80, 0x00)}, + { "navy", RGB(0x00, 0x00, 0x80)}, + { "teal", RGB(0x00, 0x80, 0x80)}, + { "aqua", RGB(0x00, 0xFF, 0xFF)}, /* = NetHack cyan */ + { "", RGB(0x00, 0x00, 0x00)}, +}; + +typedef struct ctbv +{ + char *colorstring; + int syscolorvalue; +} color_table_brush_value; + +static color_table_brush_value color_table_brush[] = { + { "activeborder", COLOR_ACTIVEBORDER }, + { "activecaption", COLOR_ACTIVECAPTION }, + { "appworkspace", COLOR_APPWORKSPACE }, + { "background", COLOR_BACKGROUND }, + { "btnface", COLOR_BTNFACE }, + { "btnshadow", COLOR_BTNSHADOW }, + { "btntext", COLOR_BTNTEXT }, + { "captiontext", COLOR_CAPTIONTEXT }, + { "graytext", COLOR_GRAYTEXT }, + { "greytext", COLOR_GRAYTEXT }, + { "highlight", COLOR_HIGHLIGHT }, + { "highlighttext", COLOR_HIGHLIGHTTEXT }, + { "inactiveborder", COLOR_INACTIVEBORDER }, + { "inactivecaption", COLOR_INACTIVECAPTION }, + { "menu", COLOR_MENU }, + { "menutext", COLOR_MENUTEXT }, + { "scrollbar", COLOR_SCROLLBAR }, + { "window", COLOR_WINDOW }, + { "windowframe", COLOR_WINDOWFRAME }, + { "windowtext", COLOR_WINDOWTEXT }, + { "", -1 }, +}; + +void mswin_init_color_table() +{ + int i; + struct t_brush_table* p; + + /* cleanup */ + for( i=0; icode != -1; p++ ) { + if( p->code==brush_table[i].code ) { + brush_table[i].brush = CreateSolidBrush(p->color); + brush_table[i].color = p->color; + } + } + } + } +} + +HBRUSH mswin_get_brush(int win_type, int color_index) +{ + int i; + for(i=0; icolorstring && _stricmp(ctv_ptr->colorstring, colorstring)) + ++ctv_ptr; + if (*ctv_ptr->colorstring) { + *colorptr = ctv_ptr->colorvalue; + } else { + while (*ctbv_ptr->colorstring && _stricmp(ctbv_ptr->colorstring, colorstring)) + ++ctbv_ptr; + if (*ctbv_ptr->colorstring) { + *brushptr = SYSCLR_TO_BRUSH(ctbv_ptr->syscolorvalue); + *colorptr = GetSysColor(ctbv_ptr->syscolorvalue); + } + } + } + if (max_brush > TOTAL_BRUSHES) panic("Too many colors!"); + *brushptr = CreateSolidBrush(*colorptr); +} diff --git a/sys/wince/mhcolor.h b/sys/wince/mhcolor.h new file mode 100644 index 0000000..c9fdf73 --- /dev/null +++ b/sys/wince/mhcolor.h @@ -0,0 +1,17 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* color management functions */ + +#ifndef MSWINColor_h +#define MSWINColor_h + +#define MSWIN_COLOR_BG 0 +#define MSWIN_COLOR_FG 1 +#define SYSCLR_TO_BRUSH(x) ((HBRUSH)((x) + 1)) + +extern void mswin_init_color_table(); +extern HBRUSH mswin_get_brush(int win_type, int color_index); +extern COLORREF mswin_get_color(int win_type, int color_index); + +#endif /* MSWINColor_h */ diff --git a/sys/wince/mhdlg.c b/sys/wince/mhdlg.c new file mode 100644 index 0000000..9b6e9a0 --- /dev/null +++ b/sys/wince/mhdlg.c @@ -0,0 +1,787 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* various dialog boxes are defined here */ + +#include "winMS.h" +#include "hack.h" +#include "func_tab.h" +#include "mhdlg.h" +#include "mhmain.h" + +#define CheckDlgButton(dlg, btn_id, st) SendDlgItemMessage((dlg), (btn_id), BM_SETCHECK, (WPARAM)(st), 0) + + +/*---------------------------------------------------------------*/ +/* data for getlin dialog */ +struct getlin_data { + const char* question; + char* result; + size_t result_size; +}; + +LRESULT CALLBACK GetlinDlgProc(HWND, UINT, WPARAM, LPARAM); + +int mswin_getlin_window ( + const char *question, + char *result, + size_t result_size +) +{ + int ret; + struct getlin_data data; + + /* initilize dialog data */ + ZeroMemory(&data, sizeof(data)); + data.question = question; + data.result = result; + data.result_size = result_size; + + /* create modal dialog window */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_GETLIN), + GetNHApp()->hMainWnd, + GetlinDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create getlin window"); + + return ret; +} + +LRESULT CALLBACK GetlinDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct getlin_data* data; + RECT main_rt, text_rt, dlg_rt, edit_rt; + SIZE dlg_sz; + TCHAR wbuf[BUFSZ]; + HDC hdc; + HWND control; + HWND hwndMap; + +#if defined(WIN_CE_POCKETPC) + SHInputDialog(hWnd, message, wParam); +#endif + + switch (message) + { + case WM_INITDIALOG: + data = (struct getlin_data*)lParam; + SetWindowText(hWnd, NH_A2W(data->question, wbuf, sizeof(wbuf))); + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* get title text width */ + SetRect(&text_rt, 0, 0, 100, 50); + hdc = GetWindowDC(hWnd); + DrawText(hdc, wbuf, _tcslen(wbuf), &text_rt, + DT_CALCRECT | DT_SINGLELINE | DT_NOPREFIX | DT_LEFT | DT_VCENTER ); + ReleaseDC(hWnd, hdc); + + /* center dialog in the main window */ + GetWindowRect(hWnd, &dlg_rt); + hwndMap = mswin_hwnd_from_winid(WIN_MAP); + GetWindowRect( IsWindow(hwndMap)? hwndMap : GetNHApp()->hMainWnd, &main_rt); + dlg_sz.cx = max(dlg_rt.right-dlg_rt.left, + min( text_rt.right-text_rt.left+GetSystemMetrics(SM_CXICON), + main_rt.right-main_rt.left ) ); + dlg_sz.cy = min(dlg_rt.bottom - dlg_rt.top, main_rt.bottom - main_rt.top); + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* change layout of controls */ + GetClientRect(hWnd, &dlg_rt); + + control = GetDlgItem(hWnd, IDC_GETLIN_EDIT); + GetWindowRect(control, &edit_rt); + MoveWindow( control, + 0, + 0, + dlg_rt.right - dlg_rt.left, + edit_rt.bottom - edit_rt.top, + TRUE ); + + control = GetDlgItem(hWnd, IDOK); + GetWindowRect(control, &text_rt); + MoveWindow( control, + 0, + edit_rt.bottom - edit_rt.top, + (dlg_rt.right-dlg_rt.left)/2, + text_rt.bottom - text_rt.top, + TRUE ); + + control = GetDlgItem(hWnd, IDCANCEL); + GetWindowRect(control, &text_rt); + MoveWindow( control, + (dlg_rt.right-dlg_rt.left)/2, + edit_rt.bottom - edit_rt.top, + (dlg_rt.right-dlg_rt.left)/2, + text_rt.bottom - text_rt.top, + TRUE ); + +#if defined(WIN_CE_SMARTPHONE) + NHSPhoneDialogSetup(hWnd, TRUE, FALSE); +#endif + + /* set focus to the edit control */ + SetFocus(GetDlgItem(hWnd, IDC_GETLIN_EDIT)); + + /* tell windows that we've set the focus */ + return FALSE; + break; + + case WM_COMMAND: + { + TCHAR wbuf[BUFSZ]; + + switch (LOWORD(wParam)) + { + /* OK button was pressed */ + case IDOK: + data = (struct getlin_data*)GetWindowLong(hWnd, GWL_USERDATA); + SendDlgItemMessage(hWnd, IDC_GETLIN_EDIT, WM_GETTEXT, (WPARAM)sizeof(wbuf), (LPARAM)wbuf ); + NH_W2A(wbuf, data->result, data->result_size); + + /* Fall through. */ + + /* cancel button was pressed */ + case IDCANCEL: + EndDialog(hWnd, wParam); + return TRUE; + } + } break; + +#if defined(WIN_CE_SMARTPHONE) + case WM_HOTKEY: + if(VK_TBACK == HIWORD(lParam)) { + SHSendBackToFocusWindow(message, wParam, lParam); + } + break; +#endif + + } /* end switch (message) */ + return FALSE; +} + + +/*---------------------------------------------------------------*/ +/* dialog data for the list of extended commands */ +struct extcmd_data { + int* selection; +}; + +LRESULT CALLBACK ExtCmdDlgProc(HWND, UINT, WPARAM, LPARAM); + +int mswin_ext_cmd_window (int* selection) +{ + int ret; + struct extcmd_data data; + + /* init dialog data */ + ZeroMemory(&data, sizeof(data)); + *selection = -1; + data.selection = selection; + + /* create modal dialog window */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_EXTCMD), + GetNHApp()->hMainWnd, + ExtCmdDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create extcmd window"); + return ret; +} + +LRESULT CALLBACK ExtCmdDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct extcmd_data* data; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + int i; + const char *ptr; + TCHAR wbuf[255]; + + switch (message) + { + case WM_INITDIALOG: + data = (struct extcmd_data*)lParam; + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hWnd, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* fill combobox with extended commands */ + for(i=0; (ptr=extcmdlist[i].ef_txt); i++) { + SendDlgItemMessage(hWnd, IDC_EXTCMD_LIST, LB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(ptr, wbuf, sizeof(wbuf)) ); + } + +#if defined(WIN_CE_SMARTPHONE) + NHSPhoneDialogSetup(hWnd, FALSE, FALSE); + + GetClientRect(hWnd, &dlg_rt); + MoveWindow(GetDlgItem(hWnd, IDC_EXTCMD_LIST), + dlg_rt.left, dlg_rt.top, + dlg_rt.right-dlg_rt.left, dlg_rt.bottom-dlg_rt.top, + TRUE); +#endif + + /* set focus to the list control */ + SetFocus(GetDlgItem(hWnd, IDC_EXTCMD_LIST)); + + /* tell windows we set the focus */ + return FALSE; + break; + + case WM_COMMAND: + data = (struct extcmd_data*)GetWindowLong(hWnd, GWL_USERDATA); + switch (LOWORD(wParam)) + { + /* OK button ws clicked */ + case IDOK: + *data->selection = SendDlgItemMessage(hWnd, IDC_EXTCMD_LIST, LB_GETCURSEL, (WPARAM)0, (LPARAM)0 ); + if( *data->selection==LB_ERR ) + *data->selection = -1; + /* Fall through. */ + + /* CANCEL button ws clicked */ + case IDCANCEL: + EndDialog(hWnd, wParam); + return TRUE; + + /* list control events */ + case IDC_EXTCMD_LIST: + switch(HIWORD(wParam)) { + + case LBN_DBLCLK: + /* double click within the list + wParam + The low-order word is the list box identifier. + The high-order word is the notification message. + lParam + Handle to the list box + */ + *data->selection = SendMessage((HWND)lParam, LB_GETCURSEL, (WPARAM)0, (LPARAM)0); + if( *data->selection==LB_ERR ) + *data->selection = -1; + EndDialog(hWnd, IDOK); + return TRUE; + } + break; + } + } + return FALSE; +} + +/*---------------------------------------------------------------*/ +/* player selector dialog data */ +struct plsel_data { + int* selection; +}; + +BOOL CALLBACK PlayerSelectorDlgProc(HWND, UINT, WPARAM, LPARAM); +static void plselInitDialog(HWND hWnd); +static void plselAdjustLists(HWND hWnd, int changed_opt); +static int plselFinalSelection(HWND hWnd, int* selection); + +int mswin_player_selection_window ( int* selection ) +{ + int ret; + struct plsel_data data; + + /* init dialog data */ + ZeroMemory(&data, sizeof(data)); + data.selection = selection; + + /* create modal dialog */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_PLAYER_SELECTOR), + GetNHApp()->hMainWnd, + PlayerSelectorDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create getlin window"); + + return ret; +} + +BOOL CALLBACK PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct plsel_data* data; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + + switch (message) + { + case WM_INITDIALOG: + data = (struct plsel_data*)lParam; + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hWnd, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* init dialog */ + plselInitDialog(hWnd); + +#if defined(WIN_CE_SMARTPHONE) + NHSPhoneDialogSetup(hWnd, FALSE, FALSE); +#endif + /* set focus on the role checkbox (random) field */ + SetFocus(GetDlgItem(hWnd, IDC_PLSEL_ROLE_RANDOM)); + + /* tell windows we set the focus */ + return FALSE; + break; + + case WM_COMMAND: + data = (struct plsel_data*)GetWindowLong(hWnd, GWL_USERDATA); + switch (LOWORD(wParam)) { + + /* OK button was clicked */ + case IDOK: + if( plselFinalSelection(hWnd, data->selection) ) { + EndDialog(hWnd, wParam); + } else { + MessageBox(hWnd, TEXT("Cannot match this role. Try something else."), TEXT("STOP"), MB_OK ); + } + return TRUE; + + /* CANCEL button was clicked */ + case IDCANCEL: + *data->selection = -1; + EndDialog(hWnd, wParam); + return TRUE; + + /* following are events from dialog controls: + "random" checkboxes send BN_CLICKED messages; + role/race/... combo-boxes send CBN_SELENDOK + if something was selected; + */ + case IDC_PLSEL_ROLE_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + /* enable corresponding list window if "random" + checkbox was "unchecked" */ + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_RACE_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_GENDER_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_ALIGN_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_ROLE_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + /* filter out invalid options if + the selection was made */ + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_RACE_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_GENDER_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_ALIGN_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + } + break; + } + return FALSE; +} + +void setComboBoxValue(HWND hWnd, int combo_box, int value) +{ + int index_max = SendDlgItemMessage(hWnd, combo_box, CB_GETCOUNT, 0, 0); + int index; + int value_to_set = LB_ERR; + for (index = 0; index < index_max; index++) { + if (SendDlgItemMessage(hWnd, combo_box, CB_GETITEMDATA, (WPARAM)index, 0) == value) { + value_to_set = index; + break; + } + } + SendDlgItemMessage(hWnd, combo_box, CB_SETCURSEL, (WPARAM)value_to_set, 0); +} + +/* initialize player selector dialog */ +void plselInitDialog(HWND hWnd) +{ + TCHAR wbuf[BUFSZ]; + + /* set player name */ + SetDlgItemText(hWnd, IDC_PLSEL_NAME, NH_A2W(plname, wbuf, sizeof(wbuf))); + + /* check flags for consistency */ + if( flags.initrole>=0 ) { + if (flags.initrace>=0 && !validrace(flags.initrole, flags.initrace)) { + flags.initrace = ROLE_NONE; + } + + if (flags.initgend>=0 && !validgend(flags.initrole, flags.initrace, flags.initgend)) { + flags.initgend = ROLE_NONE; + } + + if (flags.initalign>=0 && !validalign(flags.initrole, flags.initrace, flags.initalign)) { + flags.initalign = ROLE_NONE; + } + } + + /* populate select boxes */ + plselAdjustLists(hWnd, -1); + + /* intialize roles list */ + if( flags.initrole<0 || !ok_role(flags.initrole, ROLE_NONE, ROLE_NONE, ROLE_NONE)) { + CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_ROLE_LIST, flags.initrole); + } + + /* intialize races list */ + if( flags.initrace<0 || !ok_race(flags.initrole, flags.initrace, ROLE_NONE, ROLE_NONE) ) { + CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_RACE_LIST, flags.initrace); + } + + /* intialize genders list */ + if( flags.initgend<0 || !ok_gend(flags.initrole, flags.initrace, flags.initgend, ROLE_NONE)) { + CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_GENDER_LIST, flags.initgend); + } + + /* intialize alignments list */ + if( flags.initalign<0 || !ok_align(flags.initrole, flags.initrace, flags.initgend, flags.initalign) ) { + CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_ALIGN_LIST, flags.initalign); + } +} + +/* adjust role/race/alignment/gender list - filter out + invalid combinations + changed_sel points to the list where selection occured + (-1 if unknown) +*/ +void plselAdjustLists(HWND hWnd, int changed_sel) +{ + HWND control_role, control_race, control_gender, control_align; + int initrole, initrace, initgend, initalign; + int i; + int ind; + int valid_opt; + TCHAR wbuf[255]; + + /* get control handles */ + control_role = GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST); + control_race = GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST); + control_gender = GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST); + control_align = GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST); + + /* get current selections */ + ind = SendMessage(control_role, CB_GETCURSEL, 0, 0); + initrole = (ind==LB_ERR)? flags.initrole : SendMessage(control_role, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_race, CB_GETCURSEL, 0, 0); + initrace = (ind==LB_ERR)? flags.initrace : SendMessage(control_race, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_gender, CB_GETCURSEL, 0, 0); + initgend = (ind==LB_ERR)? flags.initgend : SendMessage(control_gender, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_align, CB_GETCURSEL, 0, 0); + initalign = (ind==LB_ERR)? flags.initalign : SendMessage(control_align, CB_GETITEMDATA, ind, 0); + + /* intialize roles list */ + if( changed_sel==-1 ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_role, CB_RESETCONTENT, 0, 0); + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, initrace, initgend, initalign)) { + if (initgend>=0 && flags.female && roles[i].name.f) + ind = SendMessage(control_role, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(roles[i].name.f, wbuf, sizeof(wbuf)) ); + else + ind = SendMessage(control_role, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(roles[i].name.m, wbuf, sizeof(wbuf)) ); + + SendMessage(control_role, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initrole ) { + SendMessage(control_role, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + } + + /* set selection to the previously selected role + if it is still valid */ + if( !valid_opt ) { + initrole = ROLE_NONE; + initrace = ROLE_NONE; + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_role, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the races list */ + changed_sel=IDC_PLSEL_ROLE_LIST; + } + + /* intialize races list */ + if( changed_sel==IDC_PLSEL_ROLE_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_race, CB_RESETCONTENT, 0, 0); + for (i = 0; races[i].noun; i++) + if (ok_race(initrole, i, ROLE_NONE, ROLE_NONE)) { + ind = SendMessage(control_race, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(races[i].noun, wbuf, sizeof(wbuf)) ); + SendMessage(control_race, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initrace ) { + SendMessage(control_race, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected race + if it is still valid */ + if( !valid_opt ) { + initrace = ROLE_NONE; + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_race, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the genders list */ + changed_sel=IDC_PLSEL_RACE_LIST; + } + + /* intialize genders list */ + if( changed_sel==IDC_PLSEL_RACE_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_gender, CB_RESETCONTENT, 0, 0); + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(initrole, initrace, i, ROLE_NONE)) { + ind = SendMessage(control_gender, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(genders[i].adj, wbuf, sizeof(wbuf)) ); + SendMessage(control_gender, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initgend ) { + SendMessage(control_gender, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected gender + if it is still valid */ + if( !valid_opt ) { + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_gender, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the alignments list */ + changed_sel=IDC_PLSEL_GENDER_LIST; + } + + /* intialize alignments list */ + if( changed_sel==IDC_PLSEL_GENDER_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_align, CB_RESETCONTENT, 0, 0); + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(initrole, initrace, initgend, i)) { + ind = SendMessage(control_align, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(aligns[i].adj, wbuf, sizeof(wbuf)) ); + SendMessage(control_align, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initalign ) { + SendMessage(control_align, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected alignment + if it is still valid */ + if( !valid_opt ) { + initalign = ROLE_NONE; + SendMessage(control_align, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + } +} + +/* player made up his mind - get final selection here */ +int plselFinalSelection(HWND hWnd, int* selection) +{ + int ind; + + /* get current selections */ + if( SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initrole = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, CB_GETCURSEL, 0, 0); + flags.initrole = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initrace = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, CB_GETCURSEL, 0, 0); + flags.initrace = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initgend = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, CB_GETCURSEL, 0, 0); + flags.initgend = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initalign = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, CB_GETCURSEL, 0, 0); + flags.initalign = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, CB_GETITEMDATA, ind, 0); + } + + + /* check the role */ + if( flags.initrole==ROLE_RANDOM ) { + flags.initrole = pick_role(flags.initrace, flags.initgend, flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + MessageBox(hWnd, TEXT("Incompatible role!"), TEXT("STOP"), MB_OK); + return FALSE; + } + } + + /* Select a race, if necessary */ + /* force compatibility with role */ + if (flags.initrace==ROLE_RANDOM || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (flags.initrace == ROLE_RANDOM) { + flags.initrace = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM); + } + + if (flags.initrace < 0) { + MessageBox(hWnd, TEXT("Incompatible race!"), TEXT("STOP"), MB_OK); + return FALSE; + } + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || + !validgend(flags.initrole, flags.initrace, flags.initgend)) { + /* pre-selected gender not valid */ + if (flags.initgend == ROLE_RANDOM) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, flags.initalign, PICK_RANDOM); + } + + if (flags.initgend < 0) { + MessageBox(hWnd, TEXT("Incompatible gender!"), TEXT("STOP"), MB_OK); + return FALSE; + } + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || + !validalign(flags.initrole, flags.initrace, flags.initalign)) { + /* pre-selected alignment not valid */ + if (flags.initalign == ROLE_RANDOM) { + flags.initalign = pick_align(flags.initrole, flags.initrace, flags.initgend, PICK_RANDOM); + } else { + MessageBox(hWnd, TEXT("Incompatible alignment!"), TEXT("STOP"), MB_OK); + return FALSE; + } + } + + return TRUE; +} + diff --git a/sys/wince/mhdlg.h b/sys/wince/mhdlg.h new file mode 100644 index 0000000..b964838 --- /dev/null +++ b/sys/wince/mhdlg.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINDlgWindow_h +#define MSWINDlgWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +int mswin_getlin_window (const char *question, char *result, size_t result_size); +int mswin_ext_cmd_window (int* selection); +int mswin_player_selection_window(int* selection); + +#endif /* MSWINDlgWindow_h */ diff --git a/sys/wince/mhfont.c b/sys/wince/mhfont.c new file mode 100644 index 0000000..30055f6 --- /dev/null +++ b/sys/wince/mhfont.c @@ -0,0 +1,177 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* font management and such */ + +#include "mhfont.h" + +#define MAXFONTS 64 + +/* font table - 64 fonts ought to be enough */ +static struct font_table_entry { + int code; + HFONT hFont; +} font_table[MAXFONTS] ; +static int font_table_size = 0; +HFONT version_splash_font; +HFONT extrainfo_splash_font; + +#define NHFONT_CODE(win, attr) (((attr&0xFF)<<8)|(win_type&0xFF)) + +static void __cdecl font_table_cleanup(void); + +/* create font based on window type, charater attributes and + window device context */ +HGDIOBJ mswin_get_font(int win_type, int attr, HDC hdc, BOOL replace) +{ + HFONT fnt = NULL; + LOGFONT lgfnt; + int font_size; + int font_index; + static BOOL once = FALSE; + + if( !once ) { + once = TRUE; + atexit(font_table_cleanup); + } + + ZeroMemory( &lgfnt, sizeof(lgfnt) ); + + /* try find font in the table */ + for(font_index=0; font_index=MAXFONTS ) panic( "font table overflow!" ); + font_table_size++; + } else { + DeleteObject(font_table[font_index].hFont); + } + + font_table[font_index].code = NHFONT_CODE(win_type, attr); + font_table[font_index].hFont = fnt; + return fnt; +} + +UINT mswin_charset() +{ + CHARSETINFO cis; + if( iflags.IBMgraphics ) + if( TranslateCharsetInfo((DWORD*)GetOEMCP(), &cis, TCI_SRCCODEPAGE) ) + return cis.ciCharset; + else + return OEM_CHARSET; + else + if( TranslateCharsetInfo((DWORD*)GetACP(), &cis, TCI_SRCCODEPAGE) ) + return cis.ciCharset; + else + return ANSI_CHARSET; +} + +void __cdecl font_table_cleanup(void) +{ + int i; + for(i=0; i */ +/* NetHack may be freely redistributed. See license for details. */ + +/* font management functions */ + +#ifndef MSWINFont_h +#define MSWINFont_h + +#include "winMS.h" + +HGDIOBJ mswin_get_font(int win_type, int attr, HDC hdc, BOOL replace); +UINT mswin_charset(); + +#endif /* MSWINFont_h */ diff --git a/sys/wince/mhinput.c b/sys/wince/mhinput.c new file mode 100644 index 0000000..53ca65d --- /dev/null +++ b/sys/wince/mhinput.c @@ -0,0 +1,86 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include "winMS.h" +#include "mhinput.h" + +/* nethack input queue functions */ + +#define NH_INPUT_BUFFER_SIZE 64 + +/* as it stands right now we need only one slot + since events are processed almost the same time as + they occur but I like large round numbers */ + +static MSNHEvent nhi_input_buffer[NH_INPUT_BUFFER_SIZE]; +static int nhi_init_input = 0; +static int nhi_read_pos = 0; +static int nhi_write_pos = 0; + +/* initialize input queue */ +void mswin_nh_input_init() +{ + if( !nhi_init_input ) { + nhi_init_input = 1; + + ZeroMemory( nhi_input_buffer, sizeof(nhi_input_buffer) ); + nhi_read_pos = 0; + nhi_write_pos = 0; + } +} + +/* check for input */ +int mswin_have_input() +{ + return (nhi_read_pos!=nhi_write_pos); +} + +/* add event to the queue */ +void mswin_input_push(PMSNHEvent event) +{ + int new_write_pos; + + if( !nhi_init_input ) mswin_nh_input_init(); + + new_write_pos = (nhi_write_pos+1) % NH_INPUT_BUFFER_SIZE; + + if(new_write_pos!=nhi_read_pos) { + memcpy(nhi_input_buffer+nhi_write_pos, event, sizeof(*event)); + nhi_write_pos = new_write_pos; + } + +} + +/* get event from the queue and delete it */ +PMSNHEvent mswin_input_pop() +{ + PMSNHEvent retval; + + if( !nhi_init_input ) mswin_nh_input_init(); + + if( nhi_read_pos!=nhi_write_pos ) { + retval = &nhi_input_buffer[nhi_read_pos]; + nhi_read_pos = (nhi_read_pos+1) % NH_INPUT_BUFFER_SIZE; + } else { + retval = NULL; + } + + return retval; +} + +/* get event from the queue but leave it there */ +PMSNHEvent mswin_input_peek() +{ + PMSNHEvent retval; + + if( !nhi_init_input ) mswin_nh_input_init(); + + if( nhi_read_pos!=nhi_write_pos ) { + retval = &nhi_input_buffer[nhi_read_pos]; + } else { + retval = NULL; + } + return retval; +} + diff --git a/sys/wince/mhinput.h b/sys/wince/mhinput.h new file mode 100644 index 0000000..f3e235c --- /dev/null +++ b/sys/wince/mhinput.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINInput_h +#define MSWINInput_h + +/* nethack input queue - store/extract input events */ +#include "winMS.h" + +#define NHEVENT_CHAR 1 +#define NHEVENT_MOUSE 2 +typedef struct mswin_event { + int type; + union { + struct { + int ch; + } kbd; + + struct { + int mod; + int x, y; + } ms; + }; +} MSNHEvent, *PMSNHEvent; + +#define NHEVENT_KBD(c) { MSNHEvent e; e.type=NHEVENT_CHAR; e.kbd.ch=(c); mswin_input_push(&e); } +#define NHEVENT_MS(_mod, _x, _y) { MSNHEvent e; e.type=NHEVENT_MOUSE; e.ms.mod = (_mod); e.ms.x=(_x); e.ms.y=(_y); mswin_input_push(&e); } + +void mswin_nh_input_init(); +int mswin_have_input(); +void mswin_input_push(PMSNHEvent event); +PMSNHEvent mswin_input_pop(); +PMSNHEvent mswin_input_peek(); + +#endif /* MSWINInput_h */ + diff --git a/sys/wince/mhmain.c b/sys/wince/mhmain.c new file mode 100644 index 0000000..31e3ba6 --- /dev/null +++ b/sys/wince/mhmain.c @@ -0,0 +1,1150 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhmsg.h" +#include "mhinput.h" +#include "mhmain.h" +#include "mhmenu.h" +#include "mhstatus.h" +#include "mhmsgwnd.h" +#include "mhcmd.h" +#include "mhmap.h" +#include "patchlevel.h" + +#define MAX_LOADSTRING 100 + +typedef struct mswin_nethack_main_window { + int mapAcsiiModeSave; +} NHMainWindow, *PNHMainWindow; + +TCHAR szMainWindowClass[] = TEXT("MSNHMainWndClass"); +static TCHAR szTitle[MAX_LOADSTRING]; + +LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +static LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void register_main_window_class(); +static void select_map_mode(int map_mode); +static int menuid2mapmode(int menuid); +static int mapmode2menuid(int map_mode); +static HMENU _get_main_menu(UINT menu_id); + +HWND mswin_init_main_window () { + static int run_once = 0; + HWND ret; + RECT rc; + + /* register window class */ + if( !run_once ) { + LoadString(GetNHApp()->hApp, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + register_main_window_class( ); + run_once = 1; + } + + /* create the main window */ + SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); + + ret = CreateWindow( + szMainWindowClass, /* registered class name */ + szTitle, /* window name */ + WS_CLIPCHILDREN, /* window style */ + rc.left, /* horizontal position of window */ + rc.top, /* vertical position of window */ + rc.right - rc.left, /* window width */ + rc.bottom - rc.top, /* window height */ + NULL, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL /* window-creation data */ + ); + + if( !ret ) panic("Cannot create main window"); + return ret; +} + +void register_main_window_class() +{ + WNDCLASS wcex; + + ZeroMemory(&wcex, sizeof(wcex)); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = (WNDPROC)MainWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = LoadIcon(GetNHApp()->hApp, (LPCTSTR)IDI_WINHACK); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szMainWindowClass; + + RegisterClass(&wcex); +} + +/* + * Keypad keys are translated to the normal values below. + * Shifted keypad keys are translated to the + * shift values below. + */ + +enum KEY_INDEXES { +KEY_NW, KEY_N, KEY_NE, KEY_MINUS, +KEY_W, KEY_GOINTERESTING, KEY_E, KEY_PLUS, +KEY_SW, KEY_S, KEY_SE, +KEY_INV, KEY_WAITLOOK, +KEY_LAST}; + +static const unsigned char +/* normal, shift, control */ +keypad[KEY_LAST][3] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, +numpad[KEY_LAST][3] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define STATEON(x) ((GetKeyState(x) & 0xFFFE) != 0) +#define KEYTABLE_REGULAR(x) ((iflags.num_pad ? numpad : keypad)[x][0]) +#define KEYTABLE_SHIFT(x) ((iflags.num_pad ? numpad : keypad)[x][1]) +#define KEYTABLE(x) (STATEON(VK_SHIFT) ? KEYTABLE_SHIFT(x) : KEYTABLE_REGULAR(x)) + +/* map mode macros */ +#define IS_MAP_FIT_TO_SCREEN(mode) ((mode)==MAP_MODE_ASCII_FIT_TO_SCREEN || \ + (mode)==MAP_MODE_TILES_FIT_TO_SCREEN ) + +#define IS_MAP_ASCII(mode) ((mode)!=MAP_MODE_TILES && (mode)!=MAP_MODE_TILES_FIT_TO_SCREEN) + + +/* +// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) +// +// PURPOSE: Processes messages for the main window. +*/ +LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMainWindow data; + + switch (message) + { + /*-----------------------------------------------------------------------*/ + case WM_CREATE: { +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + SHMENUBARINFO menubar; +#endif + /* set window data */ + data = (PNHMainWindow)malloc(sizeof(NHMainWindow)); + if( !data ) panic("out of memory"); + ZeroMemory(data, sizeof(NHMainWindow)); + data->mapAcsiiModeSave = MAP_MODE_ASCII12x16; + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + GetNHApp()->hMainWnd = hWnd; + + /* create menu bar */ +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + ZeroMemory(&menubar, sizeof(menubar)); + menubar.cbSize = sizeof(menubar); + menubar.hwndParent = hWnd; + menubar.dwFlags = 0; + menubar.nToolBarId = IDC_WINHACK; + menubar.hInstRes = GetNHApp()->hApp; +# if defined(WIN_CE_POCKETPC) + menubar.nBmpId = IDB_MENUBAR; + menubar.cBmpImages = 2; +# else + menubar.nBmpId = 0; + menubar.cBmpImages = 0; +# endif + if( !SHCreateMenuBar(&menubar) ) panic("cannot create menu"); + GetNHApp()->hMenuBar = menubar.hwndMB; +#else + GetNHApp()->hMenuBar = CommandBar_Create(GetNHApp()->hApp, hWnd, 1); + if( !GetNHApp()->hMenuBar ) panic("cannot create menu"); + CommandBar_InsertMenubar( + GetNHApp()->hMenuBar, GetNHApp()->hApp, + IDC_WINHACK, 0 ); +#endif + CheckMenuItem( + _get_main_menu(ID_VIEW), + IDM_VIEW_KEYPAD, + MF_BYCOMMAND | + (GetNHApp()->bCmdPad? MF_CHECKED : MF_UNCHECKED) + ); + + /* create command pad (keyboard emulator) */ + GetNHApp()->hCmdWnd = mswin_init_command_window(); + } break; + + /*-----------------------------------------------------------------------*/ + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + /*-----------------------------------------------------------------------*/ + + case WM_KEYDOWN: + data = (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA); + + /* translate arrow keys into nethack commands */ + switch (wParam) + { + case VK_LEFT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line left */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_LINEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_W)); + } + return 0; + + case VK_RIGHT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line right */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_LINEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_E)); + } + return 0; + + case VK_UP: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line up */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_LINEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_N)); + } + return 0; + + case VK_DOWN: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line down */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_LINEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_S)); + } + return 0; + + case VK_HOME: + if( STATEON(VK_CONTROL) ) { + /* scroll map window to upper left corner */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, 0), + (LPARAM)NULL + ); + + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_THUMBTRACK, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_NW)); + } + return 0; + + case VK_END: + if( STATEON(VK_CONTROL) ) { + /* scroll map window to lower right corner */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, ROWNO), + (LPARAM)NULL + ); + + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_THUMBTRACK, COLNO), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_SW)); + } + return 0; + + case VK_PRIOR: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one page up */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_PAGEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_NE)); + } + return 0; + + case VK_NEXT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one page down */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_PAGEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_SE)); + } + return 0; + + case VK_DECIMAL: + case VK_DELETE: + NHEVENT_KBD(KEYTABLE(KEY_WAITLOOK)); + return 0; + + case VK_INSERT: + NHEVENT_KBD(KEYTABLE(KEY_INV)); + return 0; + + case VK_SUBTRACT: + NHEVENT_KBD(KEYTABLE(KEY_MINUS)); + return 0; + + case VK_ADD: + NHEVENT_KBD(KEYTABLE(KEY_PLUS)); + return 0; + + case VK_CLEAR: /* This is the '5' key */ + NHEVENT_KBD(KEYTABLE(KEY_GOINTERESTING)); + return 0; + + case VK_F4: + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + data->mapAcsiiModeSave : + MAP_MODE_TILES + ); + } else { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + MAP_MODE_ASCII_FIT_TO_SCREEN : + MAP_MODE_TILES_FIT_TO_SCREEN + ); + } + return 0; + + case VK_F5: + if( IS_MAP_ASCII(iflags.wc_map_mode) ) { + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode(MAP_MODE_TILES_FIT_TO_SCREEN); + } else { + mswin_select_map_mode(MAP_MODE_TILES); + } + } else { + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode(MAP_MODE_ASCII_FIT_TO_SCREEN); + } else { + mswin_select_map_mode(data->mapAcsiiModeSave); + } + } + return 0; + + case VK_RETURN: + NHEVENT_MS( CLICK_1, u.ux, u.uy); + return 0; + } + +#if defined(WIN_CE_SMARTPHONE) + if( NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE) ) return 0; +#endif + return 1; /* end of WM_KEYDOWN */ + + /*-----------------------------------------------------------------------*/ + +#if defined(WIN_CE_SMARTPHONE) + case WM_KEYUP: + if( NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE) ) return 0; + return 1; /* end of WM_KEYUP */ +#endif + /*-----------------------------------------------------------------------*/ + +#if !defined(WIN_CE_SMARTPHONE) + case WM_CHAR: + if( wParam=='\n' || wParam=='\r' || wParam==C('M') ) return 0; /* we already processed VK_RETURN */ + + /* all characters go to nethack except Ctrl-P that scrolls message window up */ + if( wParam==C('P') || wParam==C('p') ) { + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + } else { + NHEVENT_KBD( (lParam & 1<<29)? M(tolower(wParam)) : wParam ); + } + return 0; +#endif + + /*-----------------------------------------------------------------------*/ + + case WM_COMMAND: + /* process commands - menu commands mostly */ + if( IsWindow(GetNHApp()->hPopupWnd) ) { + return SendMessage(GetNHApp()->hPopupWnd, message, wParam, lParam); + } else if( onWMCommand(hWnd, wParam, lParam) ) + return DefWindowProc(hWnd, message, wParam, lParam); + else + return 0; + + /*-----------------------------------------------------------------------*/ + + case WM_ACTIVATE: + if( LOWORD(wParam)!=WA_INACTIVE ) { +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + if( GetNHApp()->bFullScreen ) + SHFullScreen(GetNHApp()->hMainWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON); + else + SHFullScreen(GetNHApp()->hMainWnd, SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON); +#endif + mswin_layout_main_window(NULL); + } + break; + + case WM_SETTINGCHANGE: +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + if( GetNHApp()->bFullScreen ) + SHFullScreen(GetNHApp()->hMainWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON); + else + SHFullScreen(GetNHApp()->hMainWnd, SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON); +#endif + mswin_layout_main_window(NULL); + break; + + case WM_SIZE: + mswin_layout_main_window(NULL); + break; + + /*-----------------------------------------------------------------------*/ + + case WM_SETFOCUS: + /* if there is a menu window out there - + transfer input focus to it */ + if( IsWindow( GetNHApp()->hPopupWnd ) ) { + SetFocus( GetNHApp()->hPopupWnd ); + } + break; + + /*-----------------------------------------------------------------------*/ + + case WM_CLOSE: + { + /* exit gracefully */ + dosave0(); + } return 0; + + /*-----------------------------------------------------------------------*/ + + case WM_DESTROY: { + /* apparently we never get here + TODO: work on exit routines - need to send + WM_QUIT somehow */ + + /* clean up */ + free( (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA) ); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + + terminate(EXIT_SUCCESS); + } break; + + /*-----------------------------------------------------------------------*/ + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + switch(wParam) { + + /* new window was just added */ + case MSNH_MSG_ADDWND: { + PMSNHMsgAddWnd msg_param = (PMSNHMsgAddWnd)lParam; + HWND child = GetNHApp()->windowlist[msg_param->wid].win; + + if( GetNHApp()->windowlist[msg_param->wid].type == NHW_MAP ) + mswin_select_map_mode(iflags.wc_map_mode); + + if( child ) mswin_layout_main_window(child); + } break; + + } +} + +/* adjust windows to fit main window layout + --------------------------- + | Status | + +-------------------------+ + | | + | | + | MAP | + | | + | | + +-------------------------+ + | Command pad | + +-------------------------+ + | Messages | + --------------------------- +*/ +void mswin_layout_main_window(HWND changed_child) +{ + winid i; + RECT client_rt, wnd_rect; + POINT status_org; + SIZE status_size; + POINT msg_org; + SIZE msg_size; + POINT map_org; + SIZE map_size; + POINT cmd_org; + SIZE cmd_size; + HWND wnd_status, wnd_msg; + PNHMainWindow data; +#if defined(WIN_CE_POCKETPC) + SIPINFO sip; + RECT menu_bar; + RECT visible_rt; + POINT pt; +#endif + + GetClientRect(GetNHApp()->hMainWnd, &client_rt); + +#if defined(WIN_CE_POCKETPC) + ZeroMemory(&sip, sizeof(sip)); + sip.cbSize = sizeof(sip); + SHSipInfo(SPI_GETSIPINFO, 0, &sip, 0); + if( GetNHApp()->bFullScreen ) sip.rcVisibleDesktop.top = 0; + + /* adjust client rectangle size */ + GetWindowRect(GetNHApp()->hMenuBar, &menu_bar); + client_rt.bottom -= menu_bar.bottom-menu_bar.top; + + /* calcuate visible rect in client coordinates */ + pt.x = sip.rcVisibleDesktop.left; + pt.y = sip.rcVisibleDesktop.top; + ScreenToClient(GetNHApp()->hMainWnd, &pt); + SetRect(&wnd_rect, + pt.x, + pt.y, + pt.x+sip.rcVisibleDesktop.right-sip.rcVisibleDesktop.left, + pt.y+sip.rcVisibleDesktop.bottom-sip.rcVisibleDesktop.top ); + IntersectRect(&visible_rt, &client_rt, &wnd_rect); +#else +# if !defined(WIN_CE_SMARTPHONE) + client_rt.top += CommandBar_Height(GetNHApp()->hMenuBar); +# else + /* Smartphone only */ + if( GetNHApp()->bFullScreen ) { + RECT menu_bar; + GetWindowRect(GetNHApp()->hMenuBar, &menu_bar); + client_rt.bottom -= menu_bar.bottom-menu_bar.top; + } +# endif +#endif + + /* get window data */ + data = (PNHMainWindow)GetWindowLong(GetNHApp()->hMainWnd, GWL_USERDATA); + + /* get sizes of child windows */ + wnd_status = mswin_hwnd_from_winid(WIN_STATUS); + if( IsWindow(wnd_status) ) { + mswin_status_window_size(wnd_status, &status_size); + } else { + status_size.cx = status_size.cy = 0; + } + + wnd_msg = mswin_hwnd_from_winid(WIN_MESSAGE); + if( IsWindow(wnd_msg) ) { + mswin_message_window_size(wnd_msg, &msg_size); + } else { + msg_size.cx = msg_size.cy = 0; + } + + cmd_size.cx = cmd_size.cy = 0; + if( GetNHApp()->bCmdPad && IsWindow(GetNHApp()->hCmdWnd) ) { + mswin_command_window_size(GetNHApp()->hCmdWnd, &cmd_size); + } + + /* set window positions */ + + /* calculate the application windows size */ +#if defined(WIN_CE_POCKETPC) + SetRect(&wnd_rect, visible_rt.left, visible_rt.top, visible_rt.right, visible_rt.bottom); + if( sip.fdwFlags & SIPF_ON ) + cmd_size.cx = cmd_size.cy = 0; /* hide keypad window */ +#else + SetRect(&wnd_rect, client_rt.left, client_rt.top, client_rt.right, client_rt.bottom); +#endif + +#if !defined(WIN_CE_SMARTPHONE) + /* other ports have it at the bottom of the screen */ + cmd_size.cx = (wnd_rect.right-wnd_rect.left); + cmd_org.x = wnd_rect.left; + cmd_org.y = wnd_rect.bottom - cmd_size.cy; + wnd_rect.bottom -= cmd_size.cy; +#endif + + /* status window */ + switch(iflags.wc_align_status) { + case ALIGN_LEFT: + status_size.cx = (wnd_rect.right-wnd_rect.left)/4; + status_size.cy = (wnd_rect.bottom-wnd_rect.top); // that won't look good + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.top; + wnd_rect.left += status_size.cx; + break; + + case ALIGN_RIGHT: + status_size.cx = (wnd_rect.right-wnd_rect.left)/4; + status_size.cy = (wnd_rect.bottom-wnd_rect.top); // that won't look good + status_org.x = wnd_rect.right - status_size.cx; + status_org.y = wnd_rect.top; + wnd_rect.right -= status_size.cx; + break; + + case ALIGN_TOP: + status_size.cx = (wnd_rect.right-wnd_rect.left); + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.top; + wnd_rect.top += status_size.cy; + break; + + case ALIGN_BOTTOM: + default: + status_size.cx = (wnd_rect.right-wnd_rect.left); + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.bottom - status_size.cy; + wnd_rect.bottom -= status_size.cy; + break; + } + + /* message window */ + switch(iflags.wc_align_message) { + case ALIGN_LEFT: +#if defined(WIN_CE_SMARTPHONE) + /* smartphone has a keypad window on the right (bottom) side of the message window */ + msg_size.cx = cmd_size.cx = max(msg_size.cx, cmd_size.cx); + msg_size.cy = (wnd_rect.bottom-wnd_rect.top) - cmd_size.cy; + msg_org.x = cmd_org.x = wnd_rect.left; + msg_org.y = wnd_rect.top; + cmd_org.y = msg_org.y + msg_size.cy; +#else + msg_size.cx = (wnd_rect.right-wnd_rect.left)/4; + msg_size.cy = (wnd_rect.bottom-wnd_rect.top); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.top; +#endif + wnd_rect.left += msg_size.cx; + + break; + + case ALIGN_RIGHT: +#if defined(WIN_CE_SMARTPHONE) + /* smartphone has a keypad window on the right (bottom) side of the message window */ + msg_size.cx = cmd_size.cx = max(msg_size.cx, cmd_size.cx); + msg_size.cy = (wnd_rect.bottom-wnd_rect.top) - cmd_size.cy; + msg_org.x = cmd_org.x = wnd_rect.right - msg_size.cx; + msg_org.y = wnd_rect.top; + cmd_org.y = msg_org.y + msg_size.cy; +#else + msg_size.cx = (wnd_rect.right-wnd_rect.left)/4; + msg_size.cy = (wnd_rect.bottom-wnd_rect.top); + msg_org.x = wnd_rect.right - msg_size.cx; + msg_org.y = wnd_rect.top; +#endif + + wnd_rect.right -= msg_size.cx; + break; + + case ALIGN_TOP: +#if defined(WIN_CE_SMARTPHONE) + /* smartphone has a keypad window on the right side of the message window */ + msg_size.cy = cmd_size.cy = max(msg_size.cy, cmd_size.cy); + msg_size.cx = (wnd_rect.right - wnd_rect.left) - cmd_size.cx; + msg_org.x = wnd_rect.left; + cmd_org.x = msg_org.x + msg_size.cx; + msg_org.y = cmd_org.y = wnd_rect.bottom - msg_size.cy; +#else + msg_size.cx = (wnd_rect.right-wnd_rect.left); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.top; +#endif + wnd_rect.top += msg_size.cy; + break; + + case ALIGN_BOTTOM: + default: +#if defined(WIN_CE_SMARTPHONE) + /* smartphone has a keypad window on the right side of the message window */ + msg_size.cy = cmd_size.cy = max(msg_size.cy, cmd_size.cy); + msg_size.cx = (wnd_rect.right - wnd_rect.left) - cmd_size.cx; + msg_org.x = wnd_rect.left; + cmd_org.x = msg_org.x + msg_size.cx; + msg_org.y = cmd_org.y = wnd_rect.bottom - msg_size.cy; +#else + msg_size.cx = (wnd_rect.right-wnd_rect.left); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.bottom - msg_size.cy; +#endif + wnd_rect.bottom -= msg_size.cy; + break; + } + + map_org.x = wnd_rect.left; + map_org.y = wnd_rect.top; + map_size.cx = wnd_rect.right - wnd_rect.left; + map_size.cy = wnd_rect.bottom - wnd_rect.top; + + /* go through the windows list and adjust sizes */ + for( i=0; iwindowlist[i].win && !GetNHApp()->windowlist[i].dead) { + switch( GetNHApp()->windowlist[i].type ) { + case NHW_MESSAGE: + MoveWindow(GetNHApp()->windowlist[i].win, + msg_org.x, + msg_org.y, + msg_size.cx, + msg_size.cy, + TRUE ); + break; + case NHW_MAP: + MoveWindow(GetNHApp()->windowlist[i].win, + map_org.x, + map_org.y, + map_size.cx, + map_size.cy, + TRUE ); + break; + case NHW_STATUS: + MoveWindow(GetNHApp()->windowlist[i].win, + status_org.x, + status_org.y, + status_size.cx, + status_size.cy, + TRUE ); + break; + + case NHW_TEXT: + case NHW_MENU: + case NHW_RIP: + { + POINT menu_org; + SIZE menu_size; + + menu_org.x = client_rt.left; + menu_org.y = client_rt.top; +#if defined(WIN_CE_POCKETPC) + menu_size.cx = min(sip.rcVisibleDesktop.right-sip.rcVisibleDesktop.left, + client_rt.right - client_rt.left); + menu_size.cy = min(sip.rcVisibleDesktop.bottom-sip.rcVisibleDesktop.top, + client_rt.bottom - client_rt.top); +#else + menu_size.cx = client_rt.right - client_rt.left; + menu_size.cy = client_rt.bottom - client_rt.top; +#endif + +#if defined(WIN_CE_SMARTPHONE) + /* leave room for the command window */ + if( GetNHApp()->windowlist[i].type == NHW_MENU ) { + menu_size.cy -= cmd_size.cy; + } + + /* dialogs are popup windows unde SmartPhone so we need + to convert to screen coordinates */ + ClientToScreen(GetNHApp()->hMainWnd, &menu_org); +#endif + MoveWindow(GetNHApp()->windowlist[i].win, + menu_org.x, + menu_org.y, + menu_size.cx, + menu_size.cy, + TRUE ); + } break; + } + ShowWindow(GetNHApp()->windowlist[i].win, SW_SHOW); + InvalidateRect(GetNHApp()->windowlist[i].win, NULL, TRUE); + } + } + + if( IsWindow(GetNHApp()->hCmdWnd) ) { + /* show command window only if it exists and + the game is ready (plname is set) */ + if( GetNHApp()->bCmdPad && cmd_size.cx>0 && cmd_size.cy>0 && *plname) { + MoveWindow(GetNHApp()->hCmdWnd, + cmd_org.x, + cmd_org.y, + cmd_size.cx, + cmd_size.cy, + TRUE ); + ShowWindow(GetNHApp()->hCmdWnd, SW_SHOW); + } else { + ShowWindow(GetNHApp()->hCmdWnd, SW_HIDE); + } + } +} + +LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PNHMainWindow data; + + data = (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA); + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + // process the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(GetNHApp()->hApp, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); + break; + + case IDM_EXIT: + done2(); + break; + + case IDM_SAVE: + dosave(); + break; + + case IDM_MAP_TILES: + case IDM_MAP_ASCII4X6: + case IDM_MAP_ASCII6X8: + case IDM_MAP_ASCII8X8: + case IDM_MAP_ASCII16X8: + case IDM_MAP_ASCII7X12: + case IDM_MAP_ASCII8X12: + case IDM_MAP_ASCII12X16: + case IDM_MAP_ASCII16X12: + case IDM_MAP_ASCII10X18: + mswin_select_map_mode(menuid2mapmode(wmId)); + break; + + case IDM_MAP_FIT_TO_SCREEN: + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + data->mapAcsiiModeSave : + MAP_MODE_TILES + ); + } else { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + MAP_MODE_ASCII_FIT_TO_SCREEN : + MAP_MODE_TILES_FIT_TO_SCREEN + ); + } + break; + + case IDM_VIEW_KEYPAD: + GetNHApp()->bCmdPad = !GetNHApp()->bCmdPad; + CheckMenuItem( + _get_main_menu(ID_VIEW), + IDM_VIEW_KEYPAD, + MF_BYCOMMAND | + (GetNHApp()->bCmdPad? MF_CHECKED : MF_UNCHECKED) + ); + mswin_layout_main_window(GetNHApp()->hCmdWnd); + break; + + case IDM_VIEW_OPTIONS: + doset(); + break; + + case IDM_HELP_LONG: + display_file(HELP, TRUE); + break; + + case IDM_HELP_COMMANDS: + display_file(SHELP, TRUE); + break; + + case IDM_HELP_HISTORY: + (void) dohistory(); + break; + + case IDM_HELP_INFO_CHAR: + (void) dowhatis(); + break; + + case IDM_HELP_INFO_KEY: + (void) dowhatdoes(); + break; + + case IDM_HELP_OPTIONS: + option_help(); + break; + + case IDM_HELP_OPTIONS_LONG: + display_file(OPTIONFILE, TRUE); + break; + + case IDM_HELP_EXTCMD: + (void) doextlist(); + break; + + case IDM_HELP_LICENSE: + display_file(LICENSE, TRUE); + break; + + case IDM_HELP_MENU: + dohelp(); + break; + + default: + return 1; + } + return 0; +} + +// Mesage handler for about box. +LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[BUFSZ]; + TCHAR wbuf[NHSTR_BUFSIZE]; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + + switch (message) + { + case WM_INITDIALOG: + getversionstring(buf); + SetDlgItemText(hDlg, IDC_ABOUT_VERSION, NH_A2W(buf, wbuf, NHSTR_BUFSIZE)); + + SetDlgItemText(hDlg, IDC_ABOUT_COPYRIGHT, + NH_A2W( + COPYRIGHT_BANNER_A "\n" + COPYRIGHT_BANNER_B "\n" + COPYRIGHT_BANNER_C, + wbuf, + NHSTR_BUFSIZE + ) ); + + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hDlg, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hDlg, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + break; + } + return FALSE; +} + + +/* Set map display mode */ +void mswin_select_map_mode(int mode) +{ + HMENU hmenuMap; + PNHMainWindow data; + winid map_id; + + map_id = WIN_MAP; + data = (PNHMainWindow)GetWindowLong(GetNHApp()->hMainWnd, GWL_USERDATA); +#if defined(WIN_CE_SMARTPHONE) + /* Smartphone manu has only 2 items */ + hmenuMap = _get_main_menu(ID_VIEW); +#else + hmenuMap = _get_main_menu(ID_MAP); +#endif + + /* override for Rogue level */ +#ifdef REINCARNATION + if( Is_rogue_level(&u.uz) && !IS_MAP_ASCII(mode) ) return; +#endif + + /* set map mode menu mark */ + if( IS_MAP_ASCII(mode) ) { + CheckMenuRadioItem( + hmenuMap, + IDM_MAP_TILES, + IDM_MAP_FIT_TO_SCREEN, + mapmode2menuid( IS_MAP_FIT_TO_SCREEN(mode)? data->mapAcsiiModeSave : mode ), + MF_BYCOMMAND); + } else { + CheckMenuRadioItem( + hmenuMap, + IDM_MAP_TILES, + IDM_MAP_FIT_TO_SCREEN, + mapmode2menuid( MAP_MODE_TILES ), + MF_BYCOMMAND); + } + +#if defined(WIN_CE_SMARTPHONE) + /* update "Fit To Screen" item text */ + { + TCHAR wbuf[BUFSZ]; + TBBUTTONINFO tbbi; + + ZeroMemory( wbuf, sizeof(wbuf) ); + if( !LoadString( + GetNHApp()->hApp, + (IS_MAP_FIT_TO_SCREEN(mode)? IDS_CAP_NORMALMAP : IDS_CAP_ENTIREMAP), + wbuf, + BUFSZ) ) { + panic("cannot load main menu strings"); + } + + ZeroMemory( &tbbi, sizeof(tbbi) ); + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_TEXT; + tbbi.pszText = wbuf; + if( !SendMessage( + GetNHApp()->hMenuBar, + TB_SETBUTTONINFO, + IDM_MAP_FIT_TO_SCREEN, + (LPARAM)&tbbi) ) { + error( "Cannot update IDM_MAP_FIT_TO_SCREEN menu item." ); + } + } +#else + /* set fit-to-screen mode mark */ + CheckMenuItem( + hmenuMap, + IDM_MAP_FIT_TO_SCREEN, + MF_BYCOMMAND | + (IS_MAP_FIT_TO_SCREEN(mode)? MF_CHECKED : MF_UNCHECKED) + ); +#endif + + if( IS_MAP_ASCII(iflags.wc_map_mode) && !IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) { + data->mapAcsiiModeSave = iflags.wc_map_mode; + } + + iflags.wc_map_mode = mode; + + /* + ** first, check if WIN_MAP has been inialized. + ** If not - attempt to retrieve it by type, then check it again + */ + if( map_id==WIN_ERR ) + map_id = mswin_winid_from_type(NHW_MAP); + if( map_id!=WIN_ERR ) + mswin_map_mode(mswin_hwnd_from_winid(map_id), mode); +} + +static struct t_menu2mapmode { + int menuID; + int mapMode; +} _menu2mapmode[] = +{ + { IDM_MAP_TILES, MAP_MODE_TILES }, + { IDM_MAP_ASCII4X6, MAP_MODE_ASCII4x6 }, + { IDM_MAP_ASCII6X8, MAP_MODE_ASCII6x8 }, + { IDM_MAP_ASCII8X8, MAP_MODE_ASCII8x8 }, + { IDM_MAP_ASCII16X8, MAP_MODE_ASCII16x8 }, + { IDM_MAP_ASCII7X12, MAP_MODE_ASCII7x12 }, + { IDM_MAP_ASCII8X12, MAP_MODE_ASCII8x12 }, + { IDM_MAP_ASCII12X16, MAP_MODE_ASCII12x16 }, + { IDM_MAP_ASCII16X12, MAP_MODE_ASCII16x12 }, + { IDM_MAP_ASCII10X18, MAP_MODE_ASCII10x18 }, + { IDM_MAP_FIT_TO_SCREEN, MAP_MODE_ASCII_FIT_TO_SCREEN }, + { -1, -1 } +}; + +int menuid2mapmode(int menuid) +{ + struct t_menu2mapmode* p; + for( p = _menu2mapmode; p->mapMode!=-1; p++ ) + if(p->menuID==menuid ) return p->mapMode; + return -1; +} + +int mapmode2menuid(int map_mode) +{ + struct t_menu2mapmode* p; + for( p = _menu2mapmode; p->mapMode!=-1; p++ ) + if(p->mapMode==map_mode ) return p->menuID; + return -1; +} + +HMENU _get_main_menu(UINT menu_id) +{ + HMENU hmenuMap; +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + TBBUTTONINFO tbbi; +#endif + +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + tbbi.cbSize = sizeof(tbbi); + tbbi.dwMask = TBIF_LPARAM; + SendMessage( GetNHApp()->hMenuBar, TB_GETBUTTONINFO, menu_id, (LPARAM)&tbbi); + hmenuMap = (HMENU)tbbi.lParam; +#else + hmenuMap = CommandBar_GetMenu(GetNHApp()->hMenuBar, 0); +#endif + return hmenuMap; +} + diff --git a/sys/wince/mhmain.h b/sys/wince/mhmain.h new file mode 100644 index 0000000..bd08b78 --- /dev/null +++ b/sys/wince/mhmain.h @@ -0,0 +1,17 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMainWindow_h +#define MSWINMainWindow_h + +/* this is a main appliation window */ + +#include "winMS.h" + +extern TCHAR szMainWindowClass[]; +HWND mswin_init_main_window (); +void mswin_layout_main_window(HWND changed_child); +void mswin_select_map_mode(int map_mode); + +#endif /* MSWINMainWindow_h */ + diff --git a/sys/wince/mhmap.c b/sys/wince/mhmap.c new file mode 100644 index 0000000..aad7941 --- /dev/null +++ b/sys/wince/mhmap.c @@ -0,0 +1,914 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhmap.h" +#include "mhmsg.h" +#include "mhinput.h" +#include "mhfont.h" + +#include "patchlevel.h" + +#define NHMAP_FONT_NAME TEXT("Terminal") +#define MAXWINDOWTEXT 255 + +extern short glyph2tile[]; + +/* map window data */ +typedef struct mswin_nethack_map_window { + int map[COLNO][ROWNO]; /* glyph map */ + + int mapMode; /* current map mode */ + boolean bAsciiMode; /* switch ASCII/tiled mode */ + boolean bFitToScreenMode; /* switch Fit map to screen mode on/off */ + int xPos, yPos; /* scroll position */ + int xPageSize, yPageSize; /* scroll page size */ + int xCur, yCur; /* position of the cursor */ + int xScrTile, yScrTile; /* size of display tile */ + POINT map_orig; /* map origin point */ + + HFONT hMapFont; /* font for ASCII mode */ +} NHMapWindow, *PNHMapWindow; + +static TCHAR szNHMapWindowClass[] = TEXT("MSNethackMapWndClass"); +LRESULT CALLBACK MapWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_map_window_class(void); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onPaint(HWND hWnd); +static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut); +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) +static void nhglyph2charcolor(short glyph, uchar* ch, int* color); +#endif +static COLORREF nhcolor_to_RGB(int c); + +HWND mswin_init_map_window () { + static int run_once = 0; + HWND ret; + DWORD styles; + + if( !run_once ) { + register_map_window_class(); + run_once = 1; + } + + styles = WS_CHILD | WS_CLIPSIBLINGS; + if( !GetNHApp()->bHideScrollBars ) styles |= WS_HSCROLL | WS_VSCROLL; + ret = CreateWindow( + szNHMapWindowClass, /* registered class name */ + NULL, /* window name */ + styles, /* window style */ + 0, /* horizontal position of window - set it later */ + 0, /* vertical position of window - set it later */ + 0, /* window width - set it later */ + 0, /* window height - set it later*/ + GetNHApp()->hMainWnd, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL ); /* window-creation data */ + if( !ret ) { + panic("Cannot create map window"); + } + return ret; +} + +void mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw) +{ + PNHMapWindow data; + RECT client_rt; + SCROLLINFO si; + SIZE wnd_size; + LOGFONT lgfnt; + + /* check arguments */ + if( !IsWindow(hWnd) || + !lpsz || + lpsz->cx<=0 || + lpsz->cy<=0 ) return; + + /* calculate window size */ + GetClientRect(hWnd, &client_rt); + wnd_size.cx = client_rt.right - client_rt.left; + wnd_size.cy = client_rt.bottom - client_rt.top; + + /* set new screen tile size */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + data->xScrTile = + max(1, (data->bFitToScreenMode? wnd_size.cx : lpsz->cx) / COLNO); + data->yScrTile = + max(1, (data->bFitToScreenMode? wnd_size.cy : lpsz->cy) / ROWNO); + + /* set map origin point */ + data->map_orig.x = max(0, client_rt.left + (wnd_size.cx - data->xScrTile*COLNO)/2 ); + data->map_orig.y = max(0, client_rt.top + (wnd_size.cy - data->yScrTile*ROWNO)/2 ); + + data->map_orig.x -= data->map_orig.x % data->xScrTile; + data->map_orig.y -= data->map_orig.y % data->yScrTile; + + /* adjust horizontal scroll bar */ + if( data->bFitToScreenMode ) + data->xPageSize = COLNO+1; /* disable scroll bar */ + else + data->xPageSize = wnd_size.cx/data->xScrTile; + + if( data->xPageSize >= COLNO ) { + data->xPos = 0; + GetNHApp()->bNoHScroll = TRUE; + } else { + GetNHApp()->bNoHScroll = FALSE; + data->xPos = max(0, min(COLNO-data->xPageSize+1, u.ux - data->xPageSize/2)); + } + + if( !GetNHApp()->bHideScrollBars ) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = COLNO; + si.nPage = data->xPageSize; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + } + + /* adjust vertical scroll bar */ + if( data->bFitToScreenMode ) + data->yPageSize = ROWNO+1; /* disable scroll bar */ + else + data->yPageSize = wnd_size.cy/data->yScrTile; + + if( data->yPageSize >= ROWNO ) { + data->yPos = 0; + GetNHApp()->bNoVScroll = TRUE; + } else { + GetNHApp()->bNoVScroll = FALSE; + data->yPos = max(0, min(ROWNO-data->yPageSize+1, u.uy - data->yPageSize/2)); + } + + if( !GetNHApp()->bHideScrollBars ) { + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = ROWNO; + si.nPage = data->yPageSize; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + } + + /* create font */ + if( data->hMapFont ) DeleteObject(data->hMapFont); + ZeroMemory(&lgfnt, sizeof(lgfnt)); + lgfnt.lfHeight = -data->yScrTile; // height of font + lgfnt.lfWidth = -data->xScrTile; // average character width + lgfnt.lfEscapement = 0; // angle of escapement + lgfnt.lfOrientation = 0; // base-line orientation angle + lgfnt.lfWeight = FW_NORMAL; // font weight + lgfnt.lfItalic = FALSE; // italic attribute option + lgfnt.lfUnderline = FALSE; // underline attribute option + lgfnt.lfStrikeOut = FALSE; // strikeout attribute option + lgfnt.lfCharSet = mswin_charset(); // character set identifier + lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision + lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision + lgfnt.lfQuality = DEFAULT_QUALITY; // output quality + if( iflags.wc_font_map && + *iflags.wc_font_map ) { + lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family + NH_A2W(iflags.wc_font_map, lgfnt.lfFaceName, LF_FACESIZE); + } else { + lgfnt.lfPitchAndFamily = FIXED_PITCH; // pitch and family + _tcsncpy(lgfnt.lfFaceName, NHMAP_FONT_NAME, LF_FACESIZE); + } + data->hMapFont = CreateFontIndirect(&lgfnt); + + mswin_cliparound(data->xCur, data->yCur); + + if(redraw) InvalidateRect(hWnd, NULL, TRUE); +} + +/* set map mode */ +int mswin_map_mode(HWND hWnd, int mode) +{ + PNHMapWindow data; + int oldMode; + SIZE mapSize; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( mode == data->mapMode ) return mode; + + oldMode = data->mapMode; + data->mapMode = mode; + + switch( data->mapMode ) { + + case MAP_MODE_ASCII4x6: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 4*COLNO; + mapSize.cy = 6*ROWNO; + break; + + case MAP_MODE_ASCII6x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 6*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII8x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 8*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII16x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 16*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII7x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 7*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII8x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 8*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII16x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 16*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII12x16: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 12*COLNO; + mapSize.cy = 16*ROWNO; + break; + + case MAP_MODE_ASCII10x18: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 10*COLNO; + mapSize.cy = 18*ROWNO; + break; + + case MAP_MODE_ASCII_FIT_TO_SCREEN: { + RECT client_rt; + GetClientRect(hWnd, &client_rt); + mapSize.cx = client_rt.right - client_rt.left; + mapSize.cy = client_rt.bottom - client_rt.top; + + data->bAsciiMode = TRUE; + data->bFitToScreenMode = TRUE; + } break; + + case MAP_MODE_TILES_FIT_TO_SCREEN: { + RECT client_rt; + GetClientRect(hWnd, &client_rt); + mapSize.cx = client_rt.right - client_rt.left; + mapSize.cy = client_rt.bottom - client_rt.top; + + data->bAsciiMode = FALSE; + data->bFitToScreenMode = TRUE; + } break; + + case MAP_MODE_TILES: + default: + data->bAsciiMode = FALSE; + data->bFitToScreenMode = FALSE; + mapSize.cx = GetNHApp()->mapTile_X*COLNO; + mapSize.cy = GetNHApp()->mapTile_Y*ROWNO; + break; + } + + mswin_map_stretch(hWnd, &mapSize, TRUE); + + return oldMode; +} + +/* register window class for map window */ +void register_map_window_class() +{ + WNDCLASS wcex; + ZeroMemory( &wcex, sizeof(wcex)); + + /* window class */ + wcex.style = CS_NOCLOSE | CS_DBLCLKS; + wcex.lpfnWndProc = (WNDPROC)MapWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); /* set backgroup here */ + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szNHMapWindowClass; + + if( !RegisterClass(&wcex) ) { + panic("cannot register Map window class"); + } +} + +/* map window procedure */ +LRESULT CALLBACK MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_CREATE: + onCreate( hWnd, wParam, lParam ); + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_PAINT: + onPaint(hWnd); + break; + + case WM_SETFOCUS: + /* transfer focus back to the main window */ + SetFocus(GetNHApp()->hMainWnd); + break; + + case WM_HSCROLL: + onMSNH_HScroll(hWnd, wParam, lParam); + break; + + case WM_VSCROLL: + onMSNH_VScroll(hWnd, wParam, lParam); + break; + + case WM_SIZE: + { + SIZE size; + + if( data->bFitToScreenMode ) { + size.cx = LOWORD(lParam); + size.cy = HIWORD(lParam); + } else { + /* mapping factor is unchaged we just need to adjust scroll bars */ + size.cx = data->xScrTile*COLNO; + size.cy = data->yScrTile*ROWNO; + } + mswin_map_stretch(hWnd, &size, TRUE); + } + break; + + case WM_LBUTTONDOWN: + NHEVENT_MS( + CLICK_1, + max(0, min(COLNO, data->xPos + (LOWORD(lParam)-data->map_orig.x)/data->xScrTile)), + max(0, min(ROWNO, data->yPos + (HIWORD(lParam)-data->map_orig.y)/data->yScrTile)) + ); + return 0; + + case WM_LBUTTONDBLCLK : + NHEVENT_MS( + CLICK_2, + max(0, min(COLNO, data->xPos + (LOWORD(lParam)-data->map_orig.x)/data->xScrTile)), + max(0, min(ROWNO, data->yPos + (HIWORD(lParam)-data->map_orig.y)/data->yScrTile)) + ); + return 0; + + case WM_DESTROY: + if( data->hMapFont ) DeleteObject(data->hMapFont); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +/* on WM_COMMAND */ +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + RECT rt; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch(wParam) { + case MSNH_MSG_PRINT_GLYPH: + { + PMSNHMsgPrintGlyph msg_data = (PMSNHMsgPrintGlyph)lParam; + data->map[msg_data->x][msg_data->y] = msg_data->glyph; + + /* invalidate the update area */ + nhcoord2display(data, msg_data->x, msg_data->y, &rt); + InvalidateRect(hWnd, &rt, TRUE); + } + break; + + case MSNH_MSG_CLIPAROUND: + { + PMSNHMsgClipAround msg_data = (PMSNHMsgClipAround)lParam; + int x, y; + BOOL scroll_x, scroll_y; + int mcam = iflags.wc_scroll_margin; + + /* calculate if you should clip around */ + scroll_x = + !GetNHApp()->bNoHScroll && + ( msg_data->x<(data->xPos+mcam) || + msg_data->x>(data->xPos+data->xPageSize-mcam) ); + scroll_y = + !GetNHApp()->bNoVScroll && + ( msg_data->y<(data->yPos+mcam) || + msg_data->y>(data->yPos+data->yPageSize-mcam) ); + + mcam += iflags.wc_scroll_amount - 1; + /* get page size and center horizontally on x-position */ + if( scroll_x ) { + if( data->xPageSize<=2*mcam ) { + x = max(0, min(COLNO, msg_data->x - data->xPageSize/2)); + } else if( msg_data->x < data->xPos+data->xPageSize/2 ) { + x = max(0, min(COLNO, msg_data->x - mcam)); + } else { + x = max(0, min(COLNO, msg_data->x - data->xPageSize + mcam)); + } + SendMessage( hWnd, WM_HSCROLL, (WPARAM)MAKELONG(SB_THUMBTRACK, x), (LPARAM)NULL ); + } + + /* get page size and center vertically on y-position */ + if( scroll_y ) { + if( data->yPageSize<=2*mcam ) { + y = max(0, min(ROWNO, msg_data->y - data->yPageSize/2)); + } else if( msg_data->y < data->yPos+data->yPageSize/2 ) { + y = max(0, min(ROWNO, msg_data->y - mcam)); + } else { + y = max(0, min(ROWNO, msg_data->y - data->yPageSize + mcam)); + } + SendMessage( hWnd, WM_VSCROLL, (WPARAM)MAKELONG(SB_THUMBTRACK, y), (LPARAM)NULL ); + } + } + break; + + case MSNH_MSG_CLEAR_WINDOW: + { + int i, j; + for(i=0; imap[i][j] = -1; + } + InvalidateRect(hWnd, NULL, TRUE); + } break; + + case MSNH_MSG_CURSOR: + { + PMSNHMsgCursor msg_data = (PMSNHMsgCursor)lParam; + HDC hdc; + RECT rt; + + /* move focus rectangle at the cursor postion */ + hdc = GetDC(hWnd); + + nhcoord2display(data, data->xCur, data->yCur, &rt); + if( data->bAsciiMode ) { + PatBlt(hdc, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, DSTINVERT); + } else { + DrawFocusRect(hdc, &rt); + } + + data->xCur = msg_data->x; + data->yCur = msg_data->y; + + nhcoord2display(data, data->xCur, data->yCur, &rt); + if( data->bAsciiMode ) { + PatBlt(hdc, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, DSTINVERT); + } else { + DrawFocusRect(hdc, &rt); + } + + ReleaseDC(hWnd, hdc); + } break; + } +} + +/* on WM_CREATE */ +void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + int i,j; + + /* set window data */ + data = (PNHMapWindow)malloc(sizeof(NHMapWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHMapWindow)); + for(i=0; imap[i][j] = -1; + } + + data->bAsciiMode = FALSE; + + data->xScrTile = GetNHApp()->mapTile_X; + data->yScrTile = GetNHApp()->mapTile_Y; + + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); +} + +/* on WM_PAINT */ +void onPaint(HWND hWnd) +{ + PNHMapWindow data; + PAINTSTRUCT ps; + HDC hDC; + HDC tileDC; + HGDIOBJ saveBmp; + RECT paint_rt; + int i, j; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + hDC = BeginPaint(hWnd, &ps); + + /* calculate paint rectangle */ + if( !IsRectEmpty(&ps.rcPaint) ) { + /* calculate paint rectangle */ + paint_rt.left = max(data->xPos + (ps.rcPaint.left - data->map_orig.x)/data->xScrTile, 0); + paint_rt.top = max(data->yPos + (ps.rcPaint.top - data->map_orig.y)/data->yScrTile, 0); + paint_rt.right = min(data->xPos + (ps.rcPaint.right - data->map_orig.x)/data->xScrTile+1, COLNO); + paint_rt.bottom = min(data->yPos + (ps.rcPaint.bottom - data->map_orig.y)/data->yScrTile+1, ROWNO); + + if( data->bAsciiMode +#ifdef REINCARNATION + || Is_rogue_level(&u.uz) + /* You enter a VERY primitive world! */ +#endif + ) { + HGDIOBJ oldFont; + + oldFont = SelectObject(hDC, data->hMapFont); + SetBkMode(hDC, TRANSPARENT); + + /* draw the map */ + for(i=paint_rt.left; imap[i][j]>=0) { + char ch; + TCHAR wch; + RECT glyph_rect; + int color; + unsigned special; + int mgch; + HBRUSH back_brush; + COLORREF OldFg; + + nhcoord2display(data, i, j, &glyph_rect); + +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) + nhglyph2charcolor(data->map[i][j], &ch, &color); + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); +#else + /* rely on NetHack core helper routine */ + mapglyph(data->map[i][j], &mgch, &color, + &special, i, j); + ch = (char)mgch; + if (((special & MG_PET) && iflags.hilite_pet) || + ((special & MG_DETECT) && iflags.use_inverse)) { + back_brush = CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY)); + FillRect (hDC, &glyph_rect, back_brush); + DeleteObject (back_brush); + switch (color) + { + case CLR_GRAY: + case CLR_WHITE: + OldFg = SetTextColor( hDC, nhcolor_to_RGB(CLR_BLACK)); + break; + default: + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); + } + } else { + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); + } +#endif + + DrawText(hDC, + NH_A2W(&ch, &wch, 1), + 1, + &glyph_rect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX + ); + SetTextColor (hDC, OldFg); + } + SelectObject(hDC, oldFont); + } else { + /* prepare tiles DC for mapping */ + tileDC = CreateCompatibleDC(hDC); + saveBmp = SelectObject(tileDC, GetNHApp()->bmpMapTiles); + + /* draw the map */ + for(i=paint_rt.left; imap[i][j]>=0) { + short ntile; + int t_x, t_y; + RECT glyph_rect; + + ntile = glyph2tile[ data->map[i][j] ]; + t_x = (ntile % GetNHApp()->mapTilesPerLine)*GetNHApp()->mapTile_X; + t_y = (ntile / GetNHApp()->mapTilesPerLine)*GetNHApp()->mapTile_Y; + + nhcoord2display(data, i, j, &glyph_rect); + + StretchBlt( + hDC, + glyph_rect.left, + glyph_rect.top, + data->xScrTile, + data->yScrTile, + tileDC, + t_x, + t_y, + GetNHApp()->mapTile_X, + GetNHApp()->mapTile_Y, + SRCCOPY + ); + if( glyph_is_pet(data->map[i][j]) && iflags.wc_hilite_pet ) { + /* apply pet mark transparently over + pet image */ + HDC hdcPetMark; + HBITMAP bmPetMarkOld; + + /* this is DC for petmark bitmap */ + hdcPetMark = CreateCompatibleDC(hDC); + bmPetMarkOld = SelectObject(hdcPetMark, GetNHApp()->bmpPetMark); + + nhapply_image_transparent( + hDC, + glyph_rect.left, + glyph_rect.top, + data->xScrTile, + data->yScrTile, + hdcPetMark, + 0, + 0, + TILE_X, + TILE_Y, + TILE_BK_COLOR + ); + SelectObject(hdcPetMark, bmPetMarkOld); + DeleteDC(hdcPetMark); + } + } + SelectObject(tileDC, saveBmp); + DeleteDC(tileDC); + } + + /* draw focus rect */ + nhcoord2display(data, data->xCur, data->yCur, &paint_rt); + if( data->bAsciiMode ) { + PatBlt( hDC, + paint_rt.left, paint_rt.top, + paint_rt.right-paint_rt.left, paint_rt.bottom-paint_rt.top, + DSTINVERT ); + } else { + DrawFocusRect(hDC, &paint_rt); + } + } + EndPaint(hWnd, &ps); +} + +/* on WM_VSCROLL */ +void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + SCROLLINFO si; + int yNewPos; + int yDelta; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch(LOWORD (wParam)) + { + /* User clicked shaft left of the scroll box. */ + case SB_PAGEUP: + yNewPos = data->yPos-data->yPageSize; + break; + + /* User clicked shaft right of the scroll box. */ + case SB_PAGEDOWN: + yNewPos = data->yPos+data->yPageSize; + break; + + /* User clicked the left arrow. */ + case SB_LINEUP: + yNewPos = data->yPos-1; + break; + + /* User clicked the right arrow. */ + case SB_LINEDOWN: + yNewPos = data->yPos+1; + break; + + /* User dragged the scroll box. */ + case SB_THUMBTRACK: + yNewPos = HIWORD(wParam); + break; + + default: + yNewPos = data->yPos; + } + + yNewPos = max(0, min(ROWNO-data->yPageSize+1, yNewPos)); + if( yNewPos == data->yPos ) return; + + yDelta = yNewPos - data->yPos; + data->yPos = yNewPos; + + ScrollWindowEx (hWnd, 0, -data->yScrTile * yDelta, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + if( !GetNHApp()->bHideScrollBars ) { + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + } +} + +/* on WM_HSCROLL */ +void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + SCROLLINFO si; + int xNewPos; + int xDelta; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch(LOWORD (wParam)) + { + /* User clicked shaft left of the scroll box. */ + case SB_PAGEUP: + xNewPos = data->xPos-data->xPageSize; + break; + + /* User clicked shaft right of the scroll box. */ + case SB_PAGEDOWN: + xNewPos = data->xPos+data->xPageSize; + break; + + /* User clicked the left arrow. */ + case SB_LINEUP: + xNewPos = data->xPos-1; + break; + + /* User clicked the right arrow. */ + case SB_LINEDOWN: + xNewPos = data->xPos+1; + break; + + /* User dragged the scroll box. */ + case SB_THUMBTRACK: + xNewPos = HIWORD(wParam); + break; + + default: + xNewPos = data->xPos; + } + + xNewPos = max(0, min(COLNO-data->xPageSize+1, xNewPos)); + if( xNewPos == data->xPos ) return; + + xDelta = xNewPos - data->xPos; + data->xPos = xNewPos; + + ScrollWindowEx (hWnd, -data->xScrTile * xDelta, 0, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + if( !GetNHApp()->bHideScrollBars ) { + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + } +} + +/* map nethack map coordinates to the screen location */ +void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut) +{ + lpOut->left = (x - data->xPos)*data->xScrTile + data->map_orig.x; + lpOut->top = (y - data->yPos)*data->yScrTile + data->map_orig.y; + lpOut->right = lpOut->left + data->xScrTile; + lpOut->bottom = lpOut->top + data->yScrTile; +} + +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) +/* map glyph to character/color combination */ +void nhglyph2charcolor(short g, uchar* ch, int* color) +{ + int offset; +#ifdef TEXTCOLOR + +#define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR +#define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR +#define obj_color(n) *color = iflags.use_color ? objects[n].oc_color : NO_COLOR +#define mon_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define pet_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define warn_color(n) *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR + +# else /* no text color */ + +#define zap_color(n) +#define cmap_color(n) +#define obj_color(n) +#define mon_color(n) +#define pet_color(c) +#define warn_color(c) + *color = CLR_WHITE; +#endif + + if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ + *ch = warnsyms[offset]; + warn_color(offset); + } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */ + /* see swallow_to_glyph() in display.c */ + *ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)]; + mon_color(offset >> 3); + } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */ + /* see zapdir_to_glyph() in display.c */ + *ch = showsyms[S_vbeam + (offset & 0x3)]; + zap_color((offset >> 2)); + } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */ + *ch = showsyms[offset]; + cmap_color(offset); + } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */ + *ch = oc_syms[(int)objects[offset].oc_class]; + obj_color(offset); + } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */ + *ch = oc_syms[(int)objects[CORPSE].oc_class]; + mon_color(offset); + } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */ + *ch = monsyms[(int)mons[offset].mlet]; + pet_color(offset); + } else { /* a monster */ + *ch = monsyms[(int)mons[g].mlet]; + mon_color(g); + } + // end of wintty code +} +#endif + +/* map nethack color to RGB */ +COLORREF nhcolor_to_RGB(int c) +{ + switch(c) { + case CLR_BLACK: return RGB(0x55, 0x55, 0x55); + case CLR_RED: return RGB(0xFF, 0x00, 0x00); + case CLR_GREEN: return RGB(0x00, 0x80, 0x00); + case CLR_BROWN: return RGB(0xA5, 0x2A, 0x2A); + case CLR_BLUE: return RGB(0x00, 0x00, 0xFF); + case CLR_MAGENTA: return RGB(0xFF, 0x00, 0xFF); + case CLR_CYAN: return RGB(0x00, 0xFF, 0xFF); + case CLR_GRAY: return RGB(0xC0, 0xC0, 0xC0); + case NO_COLOR: return RGB(0xFF, 0xFF, 0xFF); + case CLR_ORANGE: return RGB(0xFF, 0xA5, 0x00); + case CLR_BRIGHT_GREEN: return RGB(0x00, 0xFF, 0x00); + case CLR_YELLOW: return RGB(0xFF, 0xFF, 0x00); + case CLR_BRIGHT_BLUE: return RGB(0x00, 0xC0, 0xFF); + case CLR_BRIGHT_MAGENTA: return RGB(0xFF, 0x80, 0xFF); + case CLR_BRIGHT_CYAN: return RGB(0x80, 0xFF, 0xFF); /* something close to aquamarine */ + case CLR_WHITE: return RGB(0xFF, 0xFF, 0xFF); + default: return RGB(0x00, 0x00, 0x00); /* black */ + } +} + +/* apply bitmap pointed by sourceDc transparently over + bitmap pointed by hDC */ +void nhapply_image_transparent( + HDC hDC, int x, int y, int width, int height, + HDC sourceDC, int s_x, int s_y, int s_width, int s_height, + COLORREF cTransparent +) +{ + TransparentImage( + hDC, x, y, width, height, + sourceDC, s_x, s_y, s_width, s_height, + cTransparent + ); +} + diff --git a/sys/wince/mhmap.h b/sys/wince/mhmap.h new file mode 100644 index 0000000..401561d --- /dev/null +++ b/sys/wince/mhmap.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMapWindow_h +#define MSWINMapWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + + +HWND mswin_init_map_window (void); +void mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw); +int mswin_map_mode(HWND hWnd, int mode); + +#define ROGUE_LEVEL_MAP_MODE MAP_MODE_ASCII12x16 + +#define DEF_CLIPAROUND_MARGIN 5 +#define DEF_CLIPAROUND_AMOUNT 1 + +#endif /* MSWINMapWindow_h */ diff --git a/sys/wince/mhmenu.c b/sys/wince/mhmenu.c new file mode 100644 index 0000000..0174cb9 --- /dev/null +++ b/sys/wince/mhmenu.c @@ -0,0 +1,1568 @@ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include +#include "mhmenu.h" +#include "mhmain.h" +#include "mhmsg.h" +#include "mhcmd.h" +#include "mhinput.h" +#include "mhfont.h" +#include "mhcolor.h" +#include "mhtxtbuf.h" + +#define MENU_MARGIN 0 +#define NHMENU_STR_SIZE BUFSZ +#define MIN_TABSTOP_SIZE 0 +#define NUMTABS 15 +#define TAB_SEPARATION 10 /* pixels between each tab stop */ + +typedef struct mswin_menu_item { + int glyph; + ANY_P identifier; + CHAR_P accelerator; + CHAR_P group_accel; + int attr; + char str[NHMENU_STR_SIZE]; + BOOLEAN_P presel; + int count; + BOOL has_focus; + BOOL has_tab; +} NHMenuItem, *PNHMenuItem; + +typedef struct mswin_nethack_menu_window { + int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */ + int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */ + + union { + struct menu_list { + int size; /* number of items in items[] */ + int allocated; /* number of allocated slots in items[] */ + PNHMenuItem items; /* menu items */ + char gacc[QBUFSZ]; /* group accelerators */ + BOOL counting; /* counting flag */ + char prompt[QBUFSZ]; /* menu prompt */ + int tab_stop_size[NUMTABS];/* tabstops to align option values */ + } menu; + + struct menu_text { + PNHTextBuffer text; + } text; + }; + int result; + int done; + + HBITMAP bmpChecked; + HBITMAP bmpCheckedCount; + HBITMAP bmpNotChecked; +} NHMenuWindow, *PNHMenuWindow; + +extern short glyph2tile[]; + +static WNDPROC wndProcListViewOrig = NULL; +static WNDPROC editControlWndProc = NULL; + +#define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL) +#define NHMENU_IS_SELECTED(item) ((item).count!=0) + +LRESULT CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM); +static void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam); +static LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void LayoutMenu(HWND hwnd); +static void SetMenuType(HWND hwnd, int type); +static void SetMenuListType(HWND hwnd, int now); +static HWND GetMenuControl(HWND hwnd); +static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count); +static void reset_menu_count(HWND hwndList, PNHMenuWindow data); +static LRESULT onListChar(HWND hWnd, HWND hwndList, WORD ch); +static char* parse_menu_str(char* dest, const char* src, size_t size); + +HWND mswin_init_menu_window (int type) { + HWND ret; + + ret = CreateDialog( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_MENU), + GetNHApp()->hMainWnd, + MenuWndProc + ); + if( !ret ) { + panic("Cannot create menu window"); + } + + SetMenuType(ret, type); + return ret; +} + + +int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected) +{ + PNHMenuWindow data; + int ret_val; + MENU_ITEM_P *selected = NULL; + int i; + char* ap; + char accell_str[256]; + + assert( _selected!=NULL ); + *_selected = NULL; + ret_val = -1; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + /* set menu type */ + SetMenuListType(hWnd, how); + + /* Ok, now give items a unique accelerators */ + ZeroMemory(accell_str, sizeof(accell_str)); + ap = accell_str; + +#if defined(WIN_CE_SMARTPHONE) + if( data->menu.size>10 ) { + *ap++ = MENU_FIRST_PAGE; + *ap++ = MENU_LAST_PAGE; + *ap++ = MENU_NEXT_PAGE; + *ap++ = MENU_PREVIOUS_PAGE; + if( data->how == PICK_ANY ) { + *ap++ = MENU_SELECT_ALL; + *ap++ = MENU_UNSELECT_ALL; + *ap++ = MENU_INVERT_ALL; + *ap++ = MENU_SELECT_PAGE; + *ap++ = MENU_UNSELECT_PAGE; + *ap++ = MENU_INVERT_PAGE; + } + *ap++ = MENU_SEARCH; + } +#endif + + if( data->type == MENU_TYPE_MENU ) { + char next_char = 'a'; + + for( i=0; imenu.size; i++) { + if( data->menu.items[i].accelerator!=0 ) { + *ap++ = data->menu.items[i].accelerator; + next_char = (char)(data->menu.items[i].accelerator+1); + } else if( NHMENU_IS_SELECTABLE(data->menu.items[i]) ) { + if ( (next_char>='a' && next_char<='z') || + (next_char>='A' && next_char<='Z') ) { + data->menu.items[i].accelerator = next_char; + *ap++ = data->menu.items[i].accelerator; + } else { + if( next_char > 'z' ) next_char = 'A'; + else if ( next_char > 'Z' ) break; + + data->menu.items[i].accelerator = next_char; + *ap++ = data->menu.items[i].accelerator; + } + + next_char ++; + } + } + + /* collect group accelerators */ + data->menu.gacc[0] = '\0'; + ap = data->menu.gacc; + if( data->how != PICK_NONE ) { + for( i=0; imenu.size; i++) { + if( data->menu.items[i].group_accel && + !strchr(data->menu.gacc, data->menu.items[i].group_accel) ) { + *ap++ = data->menu.items[i].group_accel; + *ap = '\x0'; + } + } + } + + reset_menu_count(NULL, data); + } + +#if defined(WIN_CE_SMARTPHONE) + if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadFromString( accell_str ); +#endif + + mswin_popup_display(hWnd, &data->done); + + /* get the result */ + if( data->result != -1 ) { + if(how==PICK_NONE) { + if(data->result>=0) ret_val=0; + else ret_val=-1; + } else if(how==PICK_ONE || how==PICK_ANY) { + /* count selected items */ + ret_val = 0; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + NHMENU_IS_SELECTED(data->menu.items[i]) ) { + ret_val++; + } + } + if( ret_val > 0 ) { + int sel_ind; + + selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P)); + if( !selected ) panic("out of memory"); + + sel_ind = 0; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + NHMENU_IS_SELECTED(data->menu.items[i]) ) { + selected[sel_ind].item = data->menu.items[i].identifier; + selected[sel_ind].count = data->menu.items[i].count; + sel_ind++; + } + } + ret_val = sel_ind; + *_selected = selected; + } + } + } + + mswin_popup_destroy(hWnd); + +#if defined(WIN_CE_SMARTPHONE) + if( data->type==MENU_TYPE_MENU ) NHSPhoneSetKeypadDefault(); +#endif + + return ret_val; +} + +LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMenuWindow data; + + CheckInputDialog(hWnd, message, wParam, lParam); + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_INITDIALOG: { + HWND text_control; + HDC hDC; + + text_control = GetDlgItem(hWnd, IDC_MENU_TEXT); + + data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow)); + ZeroMemory(data, sizeof(NHMenuWindow)); + data->type = MENU_TYPE_TEXT; + data->how = PICK_NONE; + data->result = 0; + data->done = 0; + data->bmpChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL)); + data->bmpCheckedCount = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT)); + data->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL)); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + /* subclass edit control */ + editControlWndProc = (WNDPROC)GetWindowLong(text_control, GWL_WNDPROC); + SetWindowLong(text_control, GWL_WNDPROC, (LONG)NHMenuTextWndProc); + + /* set text window font */ + hDC = GetDC(text_control); + SendMessage( + text_control, + WM_SETFONT, + (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hDC, FALSE), + (LPARAM)0 + ); + ReleaseDC(text_control, hDC); + +#if defined(WIN_CE_SMARTPHONE) + /* special initialization for SmartPhone dialogs */ + NHSPhoneDialogSetup(hWnd, FALSE, GetNHApp()->bFullScreen); +#endif + } break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_SIZE: + LayoutMenu(hWnd); + return FALSE; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + if( data->type == MENU_TYPE_MENU && + (data->how==PICK_ONE || data->how==PICK_ANY) && + data->menu.counting) { + HWND list; + int i; + + /* reset counter if counting is in progress */ + list = GetMenuControl(hWnd); + i = ListView_GetNextItem(list, -1, LVNI_FOCUSED); + if( i>=0 ) { + SelectMenuItem(list, data, i, 0); + } + return FALSE; + } else { + data->result = -1; + data->done = 1; + } + return FALSE; + + case IDOK: + data->done = 1; + data->result = 0; + return FALSE; + } + } break; + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + switch (LOWORD(wParam)) { + case IDC_MENU_LIST: + { + if( !data || data->type!=MENU_TYPE_MENU ) break; + + switch(lpnmhdr->code) { + case LVN_ITEMACTIVATE: + { + LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam; + if(data->how==PICK_ONE) { + if( lpnmlv->iItem>=0 && + lpnmlv->iItemmenu.size && + NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + -1 + ); + data->done = 1; + data->result = 0; + return TRUE; + } + } else if( data->how==PICK_ANY ) { + if( lpnmlv->iItem>=0 && + lpnmlv->iItemmenu.size && + NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1 + ); + } + } + } break; + + case NM_CLICK: { + LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW) lParam; + if( lpnmlv->iItem==-1 ) return 0; + if( data->how==PICK_ANY ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + NHMENU_IS_SELECTED(data->menu.items[lpnmlv->iItem])? 0 : -1 + ); + } + } break; + + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam; + if( lpnmlv->iItem==-1 ) return 0; + if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0; + + /* update item that has the focus */ + data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED); + ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem); + + /* update count for single-selection menu (follow the listview selection) */ + if( data->how==PICK_ONE ) { + if( lpnmlv->uNewState & LVIS_SELECTED ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + -1 + ); + } + } + + /* check item focus */ + data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED); + ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem); + } break; + + case NM_KILLFOCUS: + reset_menu_count(lpnmhdr->hwndFrom, data); + break; + + } + } break; + } + } break; + + case WM_SETFOCUS: + if( hWnd!=GetNHApp()->hPopupWnd ) { + SetFocus(GetNHApp()->hPopupWnd ); + return 0; + } + break; + + case WM_MEASUREITEM: + if( wParam==IDC_MENU_LIST ) + return onMeasureItem(hWnd, wParam, lParam); + else + return FALSE; + + case WM_DRAWITEM: + if( wParam==IDC_MENU_LIST ) + return onDrawItem(hWnd, wParam, lParam); + else + return FALSE; + + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */ + HDC hdcEdit = (HDC) wParam; + HWND hwndEdit = (HWND) lParam; + if( hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT) ) { + SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG)); + SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG)); + return (BOOL)mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG); + } + } return FALSE; + + case WM_DESTROY: + if( data ) { + DeleteObject(data->bmpChecked); + DeleteObject(data->bmpCheckedCount); + DeleteObject(data->bmpNotChecked); + if( data->type == MENU_TYPE_TEXT ) { + if( data->text.text ) { + mswin_free_text_buffer(data->text.text); + data->text.text = NULL; + } + } + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } + return TRUE; + } + return FALSE; +} + +void CheckInputDialog(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ +#if defined(WIN_CE_POCKETPC) + PNHMenuWindow data; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + if( !( data && + data->type==MENU_TYPE_MENU && + (data->how==PICK_ONE || data->how==PICK_ANY) ) ) return; + + switch(message) { + case WM_SETFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP); + return; + + case WM_DESTROY: + case WM_KILLFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN); + return; + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + switch(lpnmhdr->code) { + case NM_SETFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP); + break; + case NM_KILLFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN); + break; + } + } return; + + case WM_COMMAND: + switch(HIWORD(wParam)) { + case BN_SETFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_UP); + break; + case BN_KILLFOCUS: + if( GetNHApp()->bUseSIP ) SHSipPreference(hWnd, SIP_DOWN); + break; + } + return; + + } /* end switch */ +#endif +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMenuWindow data; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: + { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + HWND text_view; + + if( data->type!=MENU_TYPE_TEXT ) + SetMenuType(hWnd, MENU_TYPE_TEXT); + + if( !data->text.text ) { + data->text.text = mswin_init_text_buffer( + program_state.gameover? FALSE : GetNHApp()->bWrapText + ); + if( !data->text.text ) break; + } + + mswin_add_text(data->text.text, msg_data->attr, msg_data->text); + + text_view = GetDlgItem(hWnd, IDC_MENU_TEXT); + if( !text_view ) panic("cannot get text view window"); + mswin_render_text(data->text.text, text_view); + } break; + + case MSNH_MSG_STARTMENU: + { + int i; + + if( data->type!=MENU_TYPE_MENU ) + SetMenuType(hWnd, MENU_TYPE_MENU); + + if( data->menu.items ) free(data->menu.items); + data->how = PICK_NONE; + data->menu.items = NULL; + data->menu.size = 0; + data->menu.allocated = 0; + data->done = 0; + data->result = 0; + for (i = 0; i < NUMTABS; ++i) + data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE; + } break; + + case MSNH_MSG_ADDMENU: + { + PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam; + char *p, *p1; + int new_item; + HDC hDC; + int column; + HFONT saveFont; + + if( data->type!=MENU_TYPE_MENU ) break; + if( strlen(msg_data->str)==0 ) break; + + if( data->menu.size==data->menu.allocated ) { + data->menu.allocated += 10; + data->menu.items = (PNHMenuItem)realloc(data->menu.items, data->menu.allocated*sizeof(NHMenuItem)); + } + + new_item = data->menu.size; + ZeroMemory( &data->menu.items[new_item], sizeof(data->menu.items[new_item])); + data->menu.items[new_item].glyph = msg_data->glyph; + data->menu.items[new_item].identifier = *msg_data->identifier; + data->menu.items[new_item].accelerator = msg_data->accelerator; + data->menu.items[new_item].group_accel = msg_data->group_accel; + data->menu.items[new_item].attr = msg_data->attr; + parse_menu_str(data->menu.items[new_item].str, msg_data->str, NHMENU_STR_SIZE); + data->menu.items[new_item].presel = msg_data->presel; + + /* calculate tabstop size */ + p = strchr(data->menu.items[new_item].str, '\t'); + if( p ) { + data->menu.items[new_item].has_tab = TRUE; + hDC = GetDC(hWnd); + saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE)); + p1 = data->menu.items[new_item].str; + column = 0; + for (;;) { + TCHAR wbuf[BUFSZ]; + RECT drawRect; + SetRect ( &drawRect, 0, 0, 1, 1 ); + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(hDC, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &drawRect, + DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE + ); + data->menu.tab_stop_size[column] = + max( data->menu.tab_stop_size[column], drawRect.right - drawRect.left ); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + + ++column; + p1 = p + 1; + p = strchr(p1, '\t'); + } + SelectObject(hDC, saveFont); + ReleaseDC(hWnd, hDC); + } else { + data->menu.items[new_item].has_tab = FALSE; + } + + /* increment size */ + data->menu.size++; + } break; + + case MSNH_MSG_ENDMENU: + { + PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam; + if( msg_data->text ) { + strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 ); + } else { + ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt)); + } + } break; + + } /* end switch */ +} + +void LayoutMenu(HWND hWnd) +{ + PNHMenuWindow data; + HWND menu_ok; + HWND menu_cancel; + RECT clrt, rt; + POINT pt_elem, pt_ok, pt_cancel; + SIZE sz_elem, sz_ok, sz_cancel; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + menu_ok = GetDlgItem(hWnd, IDOK); + menu_cancel = GetDlgItem(hWnd, IDCANCEL); + + /* get window coordinates */ + GetClientRect(hWnd, &clrt ); + + /* set window placements */ + if( IsWindow(menu_ok) ) { + GetWindowRect(menu_ok, &rt); + sz_ok.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN; + sz_ok.cy = rt.bottom-rt.top; + pt_ok.x = clrt.left + MENU_MARGIN; + pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy; + MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE ); + } else { + pt_ok.x = 0; + pt_ok.y = clrt.bottom; + sz_ok.cx = sz_ok.cy = 0; + } + + if( IsWindow(menu_cancel) ) { + GetWindowRect(menu_cancel, &rt); + sz_cancel.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN; + sz_cancel.cy = rt.bottom-rt.top; + pt_cancel.x = (clrt.left + clrt.right)/2 + MENU_MARGIN; + pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy; + MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE ); + } else { + pt_cancel.x = 0; + pt_cancel.y = clrt.bottom; + sz_cancel.cx = sz_cancel.cy = 0; + } + + pt_elem.x = clrt.left + MENU_MARGIN; + pt_elem.y = clrt.top + MENU_MARGIN; + sz_elem.cx = (clrt.right - clrt.left) - 2*MENU_MARGIN; + sz_elem.cy = min(pt_cancel.y, pt_ok.y) - MENU_MARGIN - pt_elem.y; + MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); + + /* reformat text for the text menu */ + if( data && + data->type==MENU_TYPE_TEXT && + data->text.text ) + mswin_render_text(data->text.text, GetMenuControl(hWnd)); +} + +void SetMenuType(HWND hWnd, int type) +{ + PNHMenuWindow data; + HWND list, text; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + data->type = type; + + text = GetDlgItem(hWnd, IDC_MENU_TEXT); + list = GetDlgItem(hWnd, IDC_MENU_LIST); + if(data->type==MENU_TYPE_TEXT) { + ShowWindow(list, SW_HIDE); + EnableWindow(list, FALSE); + EnableWindow(text, TRUE); + ShowWindow(text, SW_SHOW); + SetFocus(text); + } else { + ShowWindow(text, SW_HIDE); + EnableWindow(text, FALSE); + EnableWindow(list, TRUE); + ShowWindow(list, SW_SHOW); + SetFocus(list); + } + LayoutMenu(hWnd); +} + +void SetMenuListType(HWND hWnd, int how) +{ + PNHMenuWindow data; + RECT rt; + DWORD dwStyles; + char buf[BUFSZ]; + TCHAR wbuf[BUFSZ]; + int nItem; + int i; + HWND control; + LVCOLUMN lvcol; + LRESULT fnt; + SIZE wnd_size; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data->type != MENU_TYPE_MENU ) return; + + data->how = how; + + switch(how) { + case PICK_NONE: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + case PICK_ONE: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + case PICK_ANY: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY"); + }; + if( strlen(data->menu.prompt)==0 ) { + dwStyles |= LVS_NOCOLUMNHEADER ; + } + + GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt); + DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST)); + control = CreateWindow(WC_LISTVIEW, NULL, + dwStyles, + rt.left, + rt.top, + rt.right - rt.left, + rt.bottom - rt.top, + hWnd, + (HMENU)IDC_MENU_LIST, + GetNHApp()->hApp, + NULL ); + if( !control ) panic( "cannot create menu control" ); + + /* install the hook for the control window procedure */ + wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC); + SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc); + + /* set control font */ + fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0); + SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0); + + /* set control colors */ + ListView_SetBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG)); + ListView_SetTextBkColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_BG)); + ListView_SetTextColor(control, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG)); + + /* add column to the list view */ + mswin_menu_window_size(hWnd, &wnd_size); + + ZeroMemory(&lvcol, sizeof(lvcol)); + lvcol.mask = LVCF_WIDTH | LVCF_TEXT; + lvcol.cx = max( wnd_size.cx, GetSystemMetrics(SM_CXSCREEN)); + lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ); + ListView_InsertColumn(control, 0, &lvcol); + + /* add items to the list view */ + for(i=0; imenu.size; i++ ) { + LVITEM lvitem; + ZeroMemory( &lvitem, sizeof(lvitem) ); + sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str ); + + lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; + lvitem.iItem = i; + lvitem.iSubItem = 0; + lvitem.state = data->menu.items[i].presel? LVIS_SELECTED : 0; + lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ); + lvitem.lParam = (LPARAM)&data->menu.items[i]; + nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) buf); + if( ListView_InsertItem(control, &lvitem)==-1 ) { + panic("cannot insert menu item"); + } + } + SetFocus(control); +} + + +HWND GetMenuControl(HWND hWnd) +{ + PNHMenuWindow data; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + if(data->type==MENU_TYPE_TEXT) { + return GetDlgItem(hWnd, IDC_MENU_TEXT); + } else { + return GetDlgItem(hWnd, IDC_MENU_LIST); + } +} + + +LRESULT onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LPMEASUREITEMSTRUCT lpmis; + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC hdc; + PNHMenuWindow data; + RECT list_rect; + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + GetClientRect(GetMenuControl(hWnd), &list_rect); + + hdc = GetDC(GetMenuControl(hWnd)); + saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + + /* Set the height of the list box items. */ + lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2; + lpmis->itemWidth = list_rect.right - list_rect.left; + + SelectObject(hdc, saveFont); + ReleaseDC(GetMenuControl(hWnd), hdc); + return TRUE; +} + +LRESULT onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LPDRAWITEMSTRUCT lpdis; + PNHMenuItem item; + PNHMenuWindow data; + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC tileDC; + short ntile; + int t_x, t_y; + int x, y; + TCHAR wbuf[BUFSZ]; + RECT drawRect; + COLORREF OldBg, OldFg, NewBg; + char *p, *p1; + int column; + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + /* If there are no list box items, skip this message. */ + if (lpdis->itemID == -1) return FALSE; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + item = &data->menu.items[lpdis->itemID]; + + tileDC = CreateCompatibleDC(lpdis->hDC); + saveFont = SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE)); + NewBg = mswin_get_color(NHW_MENU, MSWIN_COLOR_BG); + OldBg = SetBkColor(lpdis->hDC, NewBg); + OldFg = SetTextColor(lpdis->hDC, mswin_get_color(NHW_MENU, MSWIN_COLOR_FG)); + + GetTextMetrics(lpdis->hDC, &tm); + + x = lpdis->rcItem.left + 1; + + /* print check mark if it is a "selectable" menu */ + if( data->how!=PICK_NONE ) { + if( NHMENU_IS_SELECTABLE(*item) ) { + HGDIOBJ saveBrush; + HBRUSH hbrCheckMark; + char buf[2]; + + switch(item->count) { + case -1: hbrCheckMark = CreatePatternBrush(data->bmpChecked); break; + case 0: hbrCheckMark = CreatePatternBrush(data->bmpNotChecked); break; + default: hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount); break; + } + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; + SetBrushOrgEx(lpdis->hDC, x, y, NULL); + saveBrush = SelectObject(lpdis->hDC, hbrCheckMark); + PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY); + SelectObject(lpdis->hDC, saveBrush); + DeleteObject(hbrCheckMark); + + x += TILE_X + 5; + + if(item->accelerator!=0) { + buf[0] = item->accelerator; + buf[1] = '\x0'; + + SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom ); + DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); + } + x += tm.tmAveCharWidth + tm.tmOverhang + 5; + } else { + x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10; + } + } + + /* print glyph if present */ + if( item->glyph != NO_GLYPH ) { + HGDIOBJ saveBmp; + + saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles); + ntile = glyph2tile[ item->glyph ]; + t_x = (ntile % TILES_PER_LINE)*TILE_X; + t_y = (ntile / TILES_PER_LINE)*TILE_Y; + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; + + nhapply_image_transparent( + lpdis->hDC, x, y, TILE_X, TILE_Y, + tileDC, t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR ); + SelectObject(tileDC, saveBmp); + } + + x += TILE_X + 5; + + /* draw item text */ + if( item->has_tab ) { + p1 = item->str; + p = strchr(item->str, '\t'); + column = 0; + SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right), + lpdis->rcItem.bottom ); + for (;;) { + TCHAR wbuf[BUFSZ]; + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(lpdis->hDC, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &drawRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + + p1 = p + 1; + p = strchr(p1, '\t'); + drawRect.left = drawRect.right + TAB_SEPARATION; + ++column; + drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right); + } + } else { + TCHAR wbuf[BUFSZ]; + SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom); + DrawText(lpdis->hDC, + NH_A2W(item->str, wbuf, BUFSZ), + strlen(item->str), + &drawRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + } + + /* draw focused item */ + if( item->has_focus ) { + RECT client_rt; + HBRUSH bkBrush; + + GetClientRect(lpdis->hwndItem, &client_rt); + if( NHMENU_IS_SELECTABLE(*item) && + data->menu.items[lpdis->itemID].count>0 && + item->glyph != NO_GLYPH ) { + if( data->menu.items[lpdis->itemID].count==-1 ) { + _stprintf(wbuf, TEXT("Count: All") ); + } else { + _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count ); + } + + SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE)); + + /* calculate text rectangle */ + SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom ); + DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, + DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX ); + + /* erase text rectangle */ + drawRect.left = max(client_rt.left+1, client_rt.right - (drawRect.right - drawRect.left) - 10); + drawRect.right = client_rt.right-1; + drawRect.top = lpdis->rcItem.top; + drawRect.bottom = lpdis->rcItem.bottom; + bkBrush = CreateSolidBrush( GetBkColor(lpdis->hDC) ); + FillRect(lpdis->hDC, &drawRect, bkBrush ); + DeleteObject( bkBrush ); + + /* draw text */ + DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, + DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX ); + } + + /* draw focus rect */ + SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom ); + DrawFocusRect(lpdis->hDC, &drawRect); + } + + SetTextColor (lpdis->hDC, OldFg); + SetBkColor (lpdis->hDC, OldBg); + SelectObject(lpdis->hDC, saveFont); + DeleteDC(tileDC); + return TRUE; +} + +BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch) +{ + int i = 0; + PNHMenuWindow data; + int curIndex, topIndex, pageSize; + boolean is_accelerator = FALSE; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch( ch ) { + case MENU_FIRST_PAGE: + i = 0; + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_LAST_PAGE: + i = max(0, data->menu.size-1); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_NEXT_PAGE: + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + /* Focus down one page */ + i = min(curIndex+pageSize, data->menu.size-1); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + /* Scrollpos down one page */ + i = min(topIndex+(2*pageSize - 1), data->menu.size-1); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_PREVIOUS_PAGE: + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + /* Focus up one page */ + i = max(curIndex-pageSize, 0); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + /* Scrollpos up one page */ + i = max(topIndex-pageSize, 0); + ListView_EnsureVisible(hwndList, i, FALSE); + break; + + case MENU_SELECT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem(hwndList, data, i, -1); + } + return -2; + } + break; + + case MENU_UNSELECT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem(hwndList, data, i, 0); + } + return -2; + } + break; + + case MENU_INVERT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } + return -2; + } + break; + + case MENU_SELECT_PAGE: + if( data->how == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; ihow == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; ihow == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; imenu.items[i])? 0 : -1 + ); + } + return -2; + } + break; + + case MENU_SEARCH: + if( data->how==PICK_ANY || data->how==PICK_ONE ) { + char buf[BUFSZ]; + int selected_item; + + reset_menu_count(hwndList, data); + mswin_getlin("Search for:", buf); + if (!*buf || *buf == '\033') return -2; + selected_item = -1; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) + && strstr(data->menu.items[i].str, buf) ) { + if (data->how == PICK_ANY) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + /* save the first item - we will move focus to it */ + if( selected_item == -1 ) selected_item = i; + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + selected_item = i; + break; + } + } + } + + if( selected_item>0 ) { + ListView_SetItemState(hwndList, selected_item, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, selected_item, FALSE); + } + } else { + mswin_nhbell(); + } + return -2; + + case ' ': + /* ends menu for PICK_ONE/PICK_NONE + select item for PICK_ANY */ + if( data->how==PICK_ONE || data->how==PICK_NONE ) { + data->done = 1; + data->result = 0; + return -2; + } else if( data->how==PICK_ANY ) { + i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + if( i>=0 ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } + return -2; + } + break; + + default: + if( strchr(data->menu.gacc, ch) && + !(ch=='0' && data->menu.counting) ) { + /* matched a group accelerator */ + if (data->how == PICK_ANY || data->how == PICK_ONE) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + data->menu.items[i].group_accel == ch ) { + if( data->how == PICK_ANY ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + data->result = 0; + data->done = 1; + return -2; + } + } + } + return -2; + } else { + mswin_nhbell(); + return -2; + } + } + + if (isdigit(ch)) { + int count; + i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + if( i>=0 ) { + count = data->menu.items[i].count; + if( count==-1 ) count=0; + count *= 10L; + count += (int)(ch - '0'); + if (count != 0) /* ignore leading zeros */ { + data->menu.counting = TRUE; + data->menu.items[i].count = min(100000, count); + ListView_RedrawItems( hwndList, i, i ); /* update count mark */ + } + } + return -2; + } + + is_accelerator = FALSE; + for(i=0; imenu.size; i++) { + if( data->menu.items[i].accelerator == ch ) { + is_accelerator = TRUE; + break; + } + } + + if( (ch>='a' && ch<='z') || + (ch>='A' && ch<='Z') || is_accelerator) { + if (data->how == PICK_ANY || data->how == PICK_ONE) { + for(i=0; imenu.size; i++ ) { + if( data->menu.items[i].accelerator == ch ) { + if( data->how == PICK_ANY ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + data->result = 0; + data->done = 1; + return -2; + } + } + } + } + } + break; + } + + reset_menu_count(hwndList, data); + return -1; +} + +void mswin_menu_window_size (HWND hWnd, LPSIZE sz) +{ + TEXTMETRIC tm; + HWND control; + HGDIOBJ saveFont; + HDC hdc; + PNHMenuWindow data; + int i; + RECT rt, wrt; + int extra_cx; + + GetClientRect(hWnd, &rt); + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + GetWindowRect(hWnd, &wrt); + extra_cx = (wrt.right-wrt.left) - sz->cx; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + if(data) { + control = GetMenuControl(hWnd); + hdc = GetDC(control); + + if( data->type==MENU_TYPE_MENU ) { + /* Calculate the width of the list box. */ + saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + for(i=0; imenu.size; i++ ) { + LONG menuitemwidth = 0; + int column; + char *p, *p1; + + p1 = data->menu.items[i].str; + p = strchr(data->menu.items[i].str, '\t'); + column = 0; + for (;;) { + TCHAR wbuf[BUFSZ]; + RECT tabRect; + SetRect ( &tabRect, 0, 0, 1, 1 ); + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(hdc, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &tabRect, + DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + /* it probably isn't necessary to recompute the tab width now, but do so + * just in case, honoring the previously computed value + */ + menuitemwidth += max(data->menu.tab_stop_size[column], + tabRect.right - tabRect.left); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + /* add the separation only when not the last item */ + /* in the last item, we break out of the loop, in the statement just above */ + menuitemwidth += TAB_SEPARATION; + ++column; + p1 = p + 1; + p = strchr(p1, '\t'); + } + + sz->cx = max(sz->cx, + (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang)); + } + SelectObject(hdc, saveFont); + } else { + /* do not change size for text output - the text will be formatted to + fit any window */ + } + sz->cx += extra_cx; + + ReleaseDC(control, hdc); + } +} + +void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count) +{ + int i; + + if( item<0 || item>=data->menu.size ) return; + + if( data->how==PICK_ONE && count!=0 ) { + for(i=0; imenu.size; i++) + if( item!=i && data->menu.items[i].count!=0 ) { + data->menu.items[i].count = 0; + ListView_RedrawItems( hwndList, i, i ); + }; + } + + data->menu.items[item].count = count; + ListView_RedrawItems( hwndList, item, item ); + reset_menu_count(hwndList, data); +} + +void reset_menu_count(HWND hwndList, PNHMenuWindow data) +{ + int i; + data->menu.counting = FALSE; + if( IsWindow(hwndList) ) { + i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED); + if( i>=0 ) ListView_RedrawItems( hwndList, i, i ); + } +} + +/* List window Proc */ +LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + BOOL bUpdateFocusItem = FALSE; + + switch(message) { + + /* filter keyboard input for the control */ +#if !defined(WIN_CE_SMARTPHONE) + case WM_KEYDOWN: + case WM_KEYUP: { + MSG msg; + + if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) { + if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) { + return 0; + } + } + + if( wParam==VK_LEFT || wParam==VK_RIGHT ) + bUpdateFocusItem = TRUE; + } break; + +#else /* defined(WIN_CE_SMARTPHONE) */ + case WM_KEYDOWN: + if( wParam==VK_TACTION ) { + if( onListChar(GetParent(hWnd), hWnd, ' ')==-2 ) { + return 0; + } + } else if( NHSPhoneTranslateKbdMessage(wParam, lParam, TRUE) ) { + PMSNHEvent evt; + BOOL processed = FALSE; + if( mswin_have_input() ) { + evt = mswin_input_pop(); + if( evt->type==NHEVENT_CHAR && + onListChar(GetParent(hWnd), hWnd, evt->kbd.ch)==-2 ) { + processed = TRUE; + } + + /* eat the rest of the events */ + if( mswin_have_input() ) mswin_input_pop(); + } + if( processed ) return 0; + } + + if( wParam==VK_LEFT || wParam==VK_RIGHT ) + bUpdateFocusItem = TRUE; + break; + + case WM_KEYUP: + /* translate SmartPhone keyboard message */ + if( NHSPhoneTranslateKbdMessage(wParam, lParam, FALSE) ) + return 0; + break; + + /* tell Windows not to process default button on VK_RETURN */ + case WM_GETDLGCODE: + return DLGC_DEFPUSHBUTTON | DLGC_WANTALLKEYS | + (wndProcListViewOrig? + CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam) + : 0 ); +#endif + + case WM_SIZE: + case WM_HSCROLL: + bUpdateFocusItem = TRUE; + break; + + } + + if( bUpdateFocusItem ) { + int i; + RECT rt; + + /* invalidate the focus rectangle */ + i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED); + if( i!=-1 ) { + ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS); + InvalidateRect(hWnd, &rt, TRUE); + } + } + + if( wndProcListViewOrig ) + return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam); + else + return 0; +} + +/* Text control window proc - implements close on space */ +LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) { + case WM_KEYUP: + switch( wParam ) { + case VK_SPACE: + case VK_RETURN: + /* close on space */ + PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0); + return 0; + + case VK_UP: + /* scoll up */ + PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + return 0; + + case VK_DOWN: + /* scoll down */ + PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL); + return 0; + + case VK_LEFT: + /* scoll left */ + PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0), (LPARAM)NULL); + return 0; + + case VK_RIGHT: + /* scoll right */ + PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0), (LPARAM)NULL); + return 0; + } + break; /* case WM_KEYUP: */ + } + + if( editControlWndProc ) + return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam); + else + return 0; +} +/*----------------------------------------------------------------------------*/ +char* parse_menu_str(char* dest, const char* src, size_t size) +{ + char *p1, *p2; + if( !dest || size==0 ) return NULL; + + strncpy(dest, src, size); + dest[size-1] = '\x0'; + + /* replace "[ ]*\[" with "\t\[" */ + p1 = p2 = strstr(dest, " ["); + if( p1 ) { + while( p1!=dest && *p1==' ') p1--; + p1++; /* backup to space */ + *p2 = '\t'; + memmove(p1, p2, strlen(p2)); + p1[strlen(p2)] = '\x0'; + } + return dest; +} diff --git a/sys/wince/mhmenu.h b/sys/wince/mhmenu.h new file mode 100644 index 0000000..15c4759 --- /dev/null +++ b/sys/wince/mhmenu.h @@ -0,0 +1,17 @@ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMenuWindow_h +#define MSWINMenuWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +#define MENU_TYPE_TEXT 1 +#define MENU_TYPE_MENU 2 + +HWND mswin_init_menu_window ( int type ); +int mswin_menu_window_select_menu (HWND hwnd, int how, MENU_ITEM_P **); +void mswin_menu_window_size (HWND hwnd, LPSIZE sz); + +#endif /* MSWINTextWindow_h */ diff --git a/sys/wince/mhmsg.h b/sys/wince/mhmsg.h new file mode 100644 index 0000000..9e67b42 --- /dev/null +++ b/sys/wince/mhmsg.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MHNethackMessages_H +#define MHNethackMessages_H + +/* nethack messages */ +#define WM_MSNH_COMMAND (WM_APP+1) + +#define MSNH_MSG_ADDWND 100 +#define MSNH_MSG_PUTSTR 101 +#define MSNH_MSG_PRINT_GLYPH 102 +#define MSNH_MSG_CLEAR_WINDOW 103 +#define MSNH_MSG_CLIPAROUND 104 +#define MSNH_MSG_STARTMENU 105 +#define MSNH_MSG_ADDMENU 106 +#define MSNH_MSG_CURSOR 107 +#define MSNH_MSG_ENDMENU 108 + +typedef struct mswin_nhmsg_add_wnd { + winid wid; +} MSNHMsgAddWnd, *PMSNHMsgAddWnd; + +typedef struct mswin_nhmsg_putstr { + int attr; + const char* text; + boolean append; +} MSNHMsgPutstr, *PMSNHMsgPutstr; + +typedef struct mswin_nhmsg_print_glyph { + XCHAR_P x; + XCHAR_P y; + int glyph; +} MSNHMsgPrintGlyph, *PMSNHMsgPrintGlyph; + +typedef struct mswin_nhmsg_cliparound { + int x; + int y; +} MSNHMsgClipAround, *PMSNHMsgClipAround; + +typedef struct mswin_nhmsg_add_menu { + int glyph; + const ANY_P* identifier; + CHAR_P accelerator; + CHAR_P group_accel; + int attr; + const char * str; + BOOLEAN_P presel; +} MSNHMsgAddMenu, *PMSNHMsgAddMenu; + +typedef struct mswin_nhmsg_cursor { + int x; + int y; +} MSNHMsgCursor, *PMSNHMsgCursor; + +typedef struct mswin_nhmsg_end_menu { + const char* text; +} MSNHMsgEndMenu, *PMSNHMsgEndMenu; + +#endif diff --git a/sys/wince/mhmsgwnd.c b/sys/wince/mhmsgwnd.c new file mode 100644 index 0000000..807bfbb --- /dev/null +++ b/sys/wince/mhmsgwnd.c @@ -0,0 +1,591 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhmsgwnd.h" +#include "mhmsg.h" +#include "mhcmd.h" +#include "mhfont.h" +#include "mhcolor.h" + +#define MSG_WRAP_TEXT + +#define MSG_VISIBLE_LINES max(iflags.wc_vary_msgcount, 2) +#define MAX_MSG_LINES 32 +#define MSG_LINES (int)min(iflags.msg_history, MAX_MSG_LINES) +#define MAXWINDOWTEXT 200 + +struct window_line { + int attr; + char text[MAXWINDOWTEXT]; +}; + +typedef struct mswin_nethack_message_window { + size_t max_text; + struct window_line window_text[MAX_MSG_LINES]; + + int xChar; /* horizontal scrolling unit */ + int yChar; /* vertical scrolling unit */ + int xUpper; /* average width of uppercase letters */ + int xPos; /* current horizontal scrolling position */ + int yPos; /* current vertical scrolling position */ + int xMax; /* maximum horizontal scrolling position */ + int yMax; /* maximum vertical scrolling position */ + int xPage; /* page size of horizontal scroll bar */ + int lines_last_turn; /* lines added during the last turn */ + int dont_care; /* flag the the user does not care if messages are lost */ + } NHMessageWindow, *PNHMessageWindow; + +static TCHAR szMessageWindowClass[] = TEXT("MSNHMessageWndClass"); +LRESULT CALLBACK NHMessageWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_message_window_class(); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +#ifndef MSG_WRAP_TEXT +static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +#endif +static void onPaint(HWND hWnd); +static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam); + +#ifdef USER_SOUNDS +extern void play_sound_for_message(const char* str); +#endif + +HWND mswin_init_message_window () { + static int run_once = 0; + HWND ret; + DWORD style; + + if( !run_once ) { + register_message_window_class( ); + run_once = 1; + } + +#ifdef MSG_WRAP_TEXT + style = WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL; +#else + style = WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL; +#endif + + ret = CreateWindow( + szMessageWindowClass, /* registered class name */ + NULL, /* window name */ + style, /* window style */ + 0, /* horizontal position of window */ + 0, /* vertical position of window */ + 0, /* window width */ + 0, /* window height - set it later */ + GetNHApp()->hMainWnd, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL ); /* window-creation data */ + + if( !ret ) panic("Cannot create message window"); + + return ret; +} + +void register_message_window_class() +{ + WNDCLASS wcex; + ZeroMemory( &wcex, sizeof(wcex)); + + wcex.style = CS_NOCLOSE; + wcex.lpfnWndProc = (WNDPROC)NHMessageWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = mswin_get_brush(NHW_MESSAGE, MSWIN_COLOR_BG); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szMessageWindowClass; + + RegisterClass(&wcex); +} + +LRESULT CALLBACK NHMessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + onCreate( hWnd, wParam, lParam ); + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_PAINT: + onPaint(hWnd); + break; + + case WM_SETFOCUS: + SetFocus(GetNHApp()->hMainWnd); + break; + +#ifndef MSG_WRAP_TEXT + case WM_HSCROLL: + onMSNH_HScroll(hWnd, wParam, lParam); + break; +#endif + + case WM_VSCROLL: + onMSNH_VScroll(hWnd, wParam, lParam); + break; + + case WM_DESTROY: + { + PNHMessageWindow data; + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } break; + + case WM_SIZE: + { + SCROLLINFO si; + int xNewSize; + int yNewSize; + PNHMessageWindow data; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + xNewSize = LOWORD(lParam); + yNewSize = HIWORD(lParam); + + if( xNewSize>0 || yNewSize>0 ) { + +#ifndef MSG_WRAP_TEXT + data->xPage = xNewSize/data->xChar; + data->xMax = max(0, (int)(1 + data->max_text - data->xPage)); + data->xPos = min(data->xPos, data->xMax); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = data->max_text; + si.nPage = data->xPage; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); +#endif + + data->yMax = MSG_LINES-1; + data->yPos = min(data->yPos, data->yMax); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = MSG_VISIBLE_LINES; + si.nMax = data->yMax + MSG_VISIBLE_LINES - 1; + si.nPage = MSG_VISIBLE_LINES; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + } + } + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: + { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + SCROLLINFO si; + char* p; + + if( msg_data->append ) { + strncat(data->window_text[MSG_LINES-1].text, msg_data->text, + MAXWINDOWTEXT - strlen(data->window_text[MSG_LINES-1].text)); + } else { + /* check if the string is empty */ + for(p = data->window_text[MSG_LINES-1].text; *p && isspace(*p); p++); + + if( *p ) { + /* last string is not empty - scroll up */ + memmove(&data->window_text[0], + &data->window_text[1], + (MSG_LINES-1)*sizeof(data->window_text[0])); + } + + /* append new text to the end of the array */ + data->window_text[MSG_LINES-1].attr = msg_data->attr; + strncpy(data->window_text[MSG_LINES-1].text, msg_data->text, MAXWINDOWTEXT); + } + + /* reset V-scroll position to display new text */ + data->yPos = data->yMax; + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + /* deal with overflows */ + data->lines_last_turn++; + if( !data->dont_care && data->lines_last_turn>=MSG_LINES-2 ) { + char c; + BOOL done; + + /* append "--More--" to the message window text (cannot call putstr + here - infinite recursion) */ + memmove(&data->window_text[0], + &data->window_text[1], + (MSG_LINES-1)*sizeof(data->window_text[0])); + data->window_text[MSG_LINES-1].attr = ATR_NONE; + strncpy(data->window_text[MSG_LINES-1].text, "--More--", MAXWINDOWTEXT); + + /* update window content */ + InvalidateRect(hWnd, NULL, TRUE); + +#if defined(WIN_CE_SMARTPHONE) + NHSPhoneSetKeypadFromString( "\033- <>" ); +#endif + + done = FALSE; + while( !done ) { + int x, y, mod; + c = mswin_nh_poskey(&x, &y, &mod); + switch (c) { + /* ESC indicates that we can safely discard any further messages during this turn */ + case '\033': + data->dont_care = 1; + done = TRUE; + break; + + case '<': + SendMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + break; + + case '>': + SendMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL); + break; + + /* continue scrolling on any key */ + default: + data->lines_last_turn = 0; + done = TRUE; + break; + } + } + +#if defined(WIN_CE_SMARTPHONE) + NHSPhoneSetKeypadDefault(); +#endif + /* remove "--More--" from the message window text */ + data->window_text[MSG_LINES-1].attr = ATR_NONE; + strncpy(data->window_text[MSG_LINES-1].text, " ", MAXWINDOWTEXT); + } + + /* update window content */ + InvalidateRect(hWnd, NULL, TRUE); + +#ifdef USER_SOUNDS + play_sound_for_message(msg_data->text); +#endif + } + break; + + case MSNH_MSG_CLEAR_WINDOW: + data->lines_last_turn = 0; + data->dont_care = 0; + break; + } +} + +void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + SCROLLINFO si; + int yInc; + + /* get window data */ + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_POS; + GetScrollInfo(hWnd, SB_VERT, &si); + + switch(LOWORD (wParam)) + { + // User clicked the shaft above the scroll box. + + case SB_PAGEUP: + yInc = -(int)si.nPage; + break; + + // User clicked the shaft below the scroll box. + + case SB_PAGEDOWN: + yInc = si.nPage; + break; + + // User clicked the top arrow. + + case SB_LINEUP: + yInc = -1; + break; + + // User clicked the bottom arrow. + + case SB_LINEDOWN: + yInc = 1; + break; + + // User dragged the scroll box. + + case SB_THUMBTRACK: + yInc = HIWORD(wParam) - data->yPos; + break; + + default: + yInc = 0; + } + + // If applying the vertical scrolling increment does not + // take the scrolling position out of the scrolling range, + // increment the scrolling position, adjust the position + // of the scroll box, and update the window. UpdateWindow + // sends the WM_PAINT message. + + if (yInc = max( MSG_VISIBLE_LINES - data->yPos, + min(yInc, data->yMax - data->yPos))) + { + data->yPos += yInc; + /* ScrollWindowEx(hWnd, 0, -data->yChar * yInc, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + */ + InvalidateRect(hWnd, NULL, TRUE); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + UpdateWindow (hWnd); + } +} + +#ifndef MSG_WRAP_TEXT +void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + SCROLLINFO si; + int xInc; + + /* get window data */ + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE; + GetScrollInfo(hWnd, SB_HORZ, &si); + + switch(LOWORD (wParam)) + { + // User clicked shaft left of the scroll box. + + case SB_PAGEUP: + xInc = - (int)si.nPage; + break; + + // User clicked shaft right of the scroll box. + + case SB_PAGEDOWN: + xInc = si.nPage; + break; + + // User clicked the left arrow. + + case SB_LINEUP: + xInc = -1; + break; + + // User clicked the right arrow. + + case SB_LINEDOWN: + xInc = 1; + break; + + // User dragged the scroll box. + + case SB_THUMBTRACK: + xInc = HIWORD(wParam) - data->xPos; + break; + + default: + xInc = 0; + + } + + + // If applying the horizontal scrolling increment does not + // take the scrolling position out of the scrolling range, + // increment the scrolling position, adjust the position + // of the scroll box, and update the window. + + if (xInc = max (-data->xPos, min (xInc, data->xMax - data->xPos))) + { + data->xPos += xInc; + ScrollWindowEx (hWnd, -data->xChar * xInc, 0, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + UpdateWindow (hWnd); + } +} +#endif // MSG_WRAP_TEXT + +void onPaint(HWND hWnd) +{ + PAINTSTRUCT ps; + HDC hdc; + PNHMessageWindow data; + RECT client_rt, draw_rt; + int FirstLine, LastLine; + int i, x, y; + HGDIOBJ oldFont; + TCHAR wbuf[MAXWINDOWTEXT+2]; + size_t wlen; + COLORREF OldBg, OldFg; + + hdc = BeginPaint(hWnd, &ps); + + OldBg = SetBkColor(hdc, mswin_get_color(NHW_MESSAGE, MSWIN_COLOR_BG)); + OldFg = SetTextColor(hdc, mswin_get_color(NHW_MESSAGE, MSWIN_COLOR_FG)); + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + GetClientRect(hWnd, &client_rt); + + if( !IsRectEmpty(&ps.rcPaint) ) { + FirstLine = max (0, data->yPos - (client_rt.bottom - ps.rcPaint.top)/data->yChar + 1); + LastLine = min (MSG_LINES-1, data->yPos - (client_rt.bottom - ps.rcPaint.bottom)/data->yChar); + y = min( ps.rcPaint.bottom, client_rt.bottom - 2); + for (i=LastLine; i>=FirstLine; i--) { + if( i==MSG_LINES-1 ) { + x = data->xChar * (2 - data->xPos); + } else { + x = data->xChar * (4 - data->xPos); + } + + + if( strlen(data->window_text[i].text)>0 ) { + /* convert to UNICODE */ + NH_A2W(data->window_text[i].text, wbuf, sizeof(wbuf)); + wlen = _tcslen(wbuf); + + /* calculate text height */ + draw_rt.left = x; + draw_rt.right = client_rt.right; + draw_rt.top = y - data->yChar; + draw_rt.bottom = y; + + oldFont = SelectObject(hdc, mswin_get_font(NHW_MESSAGE, data->window_text[i].attr, hdc, FALSE)); + +#ifdef MSG_WRAP_TEXT + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT); + draw_rt.top = y - (draw_rt.bottom - draw_rt.top); + draw_rt.bottom = y; + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK); +#else + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX ); +#endif + SelectObject(hdc, oldFont); + + y -= draw_rt.bottom - draw_rt.top; + } else { + y -= data->yChar; + } + + /* highligh the last line */ + if( i==MSG_LINES-1 ) { + draw_rt.left = client_rt.left; + draw_rt.right = draw_rt.left + 2*data->xChar; + DrawText(hdc, TEXT("> "), 2, &draw_rt, DT_NOPREFIX ); + + y -= 2; + draw_rt.left = client_rt.left; + draw_rt.right = client_rt.right; + draw_rt.top -= 2; + draw_rt.bottom = client_rt.bottom; + DrawEdge(hdc, &draw_rt, EDGE_SUNKEN, BF_TOP ); + DrawEdge(hdc, &draw_rt, EDGE_SUNKEN, BF_BOTTOM ); + } + } + } + + SetTextColor (hdc, OldFg); + SetBkColor (hdc, OldBg); + EndPaint(hWnd, &ps); +} + +void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + TEXTMETRIC tm; + PNHMessageWindow data; + HGDIOBJ saveFont; + + /* set window data */ + data = (PNHMessageWindow)malloc(sizeof(NHMessageWindow)); + if( !data ) panic("out of memory"); + ZeroMemory(data, sizeof(NHMessageWindow)); + data->max_text = MAXWINDOWTEXT; + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + /* Get the handle to the client area's device context. */ + hdc = GetDC(hWnd); + saveFont = SelectObject(hdc, mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, FALSE)); + + /* Extract font dimensions from the text metrics. */ + GetTextMetrics (hdc, &tm); + data->xChar = tm.tmAveCharWidth; + data->xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * data->xChar/2; + data->yChar = tm.tmHeight + tm.tmExternalLeading; + data->xPage = 1; + + /* Free the device context. */ + SelectObject(hdc, saveFont); + ReleaseDC (hWnd, hdc); +} + +void mswin_message_window_size (HWND hWnd, LPSIZE sz) +{ + PNHMessageWindow data; + RECT rt, client_rt; + + GetWindowRect(hWnd, &rt); + + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + if(data) { + /* set size to accomodate MSG_VISIBLE_LINES, highligh rectangle and + horizontal scroll bar (difference between window rect and client rect */ + GetClientRect(hWnd, &client_rt); + sz->cy = sz->cy-(client_rt.bottom - client_rt.top) + + data->yChar * MSG_VISIBLE_LINES + 4; + } +} + diff --git a/sys/wince/mhmsgwnd.h b/sys/wince/mhmsgwnd.h new file mode 100644 index 0000000..0434f1c --- /dev/null +++ b/sys/wince/mhmsgwnd.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMessageWindow_h +#define MSWINMessageWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_message_window (); +void mswin_message_window_size (HWND hWnd, LPSIZE sz); + + +#endif /* MSWINMessageWindow_h */ diff --git a/sys/wince/mhrip.c b/sys/wince/mhrip.c new file mode 100644 index 0000000..29cf930 --- /dev/null +++ b/sys/wince/mhrip.c @@ -0,0 +1,17 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhrip.h" +#include "mhtext.h" + +HWND mswin_init_RIP_window () +{ + return mswin_init_text_window(); +} + +void mswin_display_RIP_window (HWND hWnd) +{ + mswin_display_text_window(hWnd); +} + diff --git a/sys/wince/mhrip.h b/sys/wince/mhrip.h new file mode 100644 index 0000000..5825d03 --- /dev/null +++ b/sys/wince/mhrip.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINRIPWindow_h +#define MSWINRIPWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_RIP_window (); +void mswin_display_RIP_window (HWND hwnd); + +#endif /* MSWINRIPWindow_h */ + diff --git a/sys/wince/mhstatus.c b/sys/wince/mhstatus.c new file mode 100644 index 0000000..78ddbd9 --- /dev/null +++ b/sys/wince/mhstatus.c @@ -0,0 +1,273 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhstatus.h" +#include "mhmsg.h" +#include "mhfont.h" +#include "mhcolor.h" + +#define MAXWINDOWTEXT 255 + +#define NHSTAT_LINES_2 2 +#define NHSTAT_LINES_4 4 +typedef struct mswin_nethack_status_window { + int nhstat_format; + char window_text[MAXWINDOWTEXT]; +} NHStatusWindow, *PNHStatusWindow; + +static TCHAR szStatusWindowClass[] = TEXT("MSNHStatusWndClass"); +LRESULT CALLBACK StatusWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_status_window_class(void); +static void FormatStatusString(char* text, int format); + +HWND mswin_init_status_window () { + static int run_once = 0; + HWND ret; + NHStatusWindow* data; + + if( !run_once ) { + register_status_window_class( ); + run_once = 1; + } + + ret = CreateWindow( + szStatusWindowClass, + NULL, + WS_CHILD | WS_DISABLED | WS_CLIPSIBLINGS, + 0, /* x position */ + 0, /* y position */ + 0, /* x-size - we will set it later */ + 0, /* y-size - we will set it later */ + GetNHApp()->hMainWnd, + NULL, + GetNHApp()->hApp, + NULL ); + if( !ret ) panic("Cannot create status window"); + + EnableWindow(ret, FALSE); + + data = (PNHStatusWindow)malloc(sizeof(NHStatusWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHStatusWindow)); + data->nhstat_format = NHSTAT_LINES_4; + SetWindowLong(ret, GWL_USERDATA, (LONG)data); + return ret; +} + +void register_status_window_class() +{ + WNDCLASS wcex; + + ZeroMemory( &wcex, sizeof(wcex)); + wcex.style = CS_NOCLOSE; + wcex.lpfnWndProc = (WNDPROC)StatusWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = mswin_get_brush(NHW_STATUS, MSWIN_COLOR_BG); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szStatusWindowClass; + + RegisterClass(&wcex); +} + + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + RECT rt; + PAINTSTRUCT ps; + HDC hdc; + PNHStatusWindow data; + + data = (PNHStatusWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_MSNH_COMMAND: { + switch( wParam ) { + + case MSNH_MSG_PUTSTR: + case MSNH_MSG_CLEAR_WINDOW: + ZeroMemory(data->window_text, sizeof(data->window_text)); + FormatStatusString(data->window_text, data->nhstat_format); + break; + + case MSNH_MSG_CURSOR: { + PMSNHMsgCursor msg_data = (PMSNHMsgCursor)lParam; + if( msg_data->y==0 ) { + InvalidateRect(hWnd, NULL, TRUE); + } + } break; + } + } break; + + case WM_PAINT: { + HGDIOBJ oldFont; + TCHAR wbuf[MAXWINDOWTEXT]; + COLORREF OldBg, OldFg; + + hdc = BeginPaint(hWnd, &ps); + GetClientRect(hWnd, &rt); + + oldFont = SelectObject(hdc, mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE)); + OldBg = SetBkColor(hdc, mswin_get_color(NHW_STATUS, MSWIN_COLOR_BG)); + OldFg = SetTextColor(hdc, mswin_get_color(NHW_STATUS, MSWIN_COLOR_FG)); + + DrawText(hdc, + NH_A2W(data->window_text, wbuf, MAXWINDOWTEXT), + strlen(data->window_text), + &rt, + DT_LEFT | DT_NOPREFIX); + + SetTextColor (hdc, OldFg); + SetBkColor (hdc, OldBg); + SelectObject(hdc, oldFont); + EndPaint(hWnd, &ps); + } break; + + case WM_DESTROY: + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + break; + + case WM_SETFOCUS: + SetFocus(GetNHApp()->hMainWnd); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void mswin_status_window_size (HWND hWnd, LPSIZE sz) +{ + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC hdc; + PNHStatusWindow data; + RECT rt; + GetWindowRect(hWnd, &rt); + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + data = (PNHStatusWindow)GetWindowLong(hWnd, GWL_USERDATA); + if(data) { + hdc = GetDC(hWnd); + saveFont = SelectObject(hdc, mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + + /* see if the status window can fit 80 characters per line */ + if( (80*tm.tmMaxCharWidth)>=sz->cx ) data->nhstat_format = NHSTAT_LINES_4; + else data->nhstat_format = NHSTAT_LINES_2; + + /* set height of the status box */ + sz->cy = tm.tmHeight * data->nhstat_format; + + SelectObject(hdc, saveFont); + ReleaseDC(hWnd, hdc); + } +} +extern const char *hu_stat[]; /* defined in eat.c */ +extern const char *enc_stat[]; /* define in botl.c */ +void FormatStatusString(char* text, int format) +{ + register char *nb; + int hp, hpmax; + int cap = near_capacity(); + + Strcpy(text, plname); + if('a' <= text[0] && text[0] <= 'z') text[0] += 'A'-'a'; + text[10] = 0; + Sprintf(nb = eos(text)," the "); + + if (Upolyd) { + char mbot[BUFSZ]; + int k = 0; + + Strcpy(mbot, mons[u.umonnum].mname); + while(mbot[k] != 0) { + if ((k == 0 || (k > 0 && mbot[k-1] == ' ')) && + 'a' <= mbot[k] && mbot[k] <= 'z') + mbot[k] += 'A' - 'a'; + k++; + } + Sprintf(nb = eos(nb), mbot); + } else + Sprintf(nb = eos(nb), rank_of(u.ulevel, Role_switch, flags.female)); + + if( format==NHSTAT_LINES_4 ) Sprintf(nb = eos(nb),"\r\n"); + + if (ACURR(A_STR) > 18) { + if (ACURR(A_STR) > STR18(100)) + Sprintf(nb = eos(nb),"St:%2d ",ACURR(A_STR)-100); + else if (ACURR(A_STR) < STR18(100)) + Sprintf(nb = eos(nb), "St:18/%02d ",ACURR(A_STR)-18); + else + Sprintf(nb = eos(nb),"St:18/** "); + } else + Sprintf(nb = eos(nb), "St:%-1d ",ACURR(A_STR)); + Sprintf(nb = eos(nb), + "Dx:%-1d Co:%-1d In:%-1d Wi:%-1d Ch:%-1d", + ACURR(A_DEX), ACURR(A_CON), ACURR(A_INT), ACURR(A_WIS), ACURR(A_CHA)); + Sprintf(nb = eos(nb), (u.ualign.type == A_CHAOTIC) ? " Chaotic" : + (u.ualign.type == A_NEUTRAL) ? " Neutral" : " Lawful"); +#ifdef SCORE_ON_BOTL + if (flags.showscore) + Sprintf(nb = eos(nb), " S:%ld", botl_score()); +#endif + if( format==NHSTAT_LINES_4 || + format==NHSTAT_LINES_2 ) strcat(text, "\r\n"); + + /* third line */ + hp = Upolyd ? u.mh : u.uhp; + hpmax = Upolyd ? u.mhmax : u.uhpmax; + + if(hp < 0) hp = 0; + (void) describe_level(nb=eos(nb)); + Sprintf(nb = eos(nb), + "%c:%-2ld HP:%d(%d) Pw:%d(%d) AC:%-2d", oc_syms[COIN_CLASS], +#ifndef GOLDOBJ + u.ugold, +#else + money_cnt(invent), +#endif + hp, hpmax, u.uen, u.uenmax, u.uac); + + if (Upolyd) + Sprintf(nb = eos(nb), " HD:%d", mons[u.umonnum].mlevel); +#ifdef EXP_ON_BOTL + else if(flags.showexp) + Sprintf(nb = eos(nb), " Xp:%u/%-1ld", u.ulevel,u.uexp); +#endif + else + Sprintf(nb = eos(nb), " Exp:%u", u.ulevel); + if( format==NHSTAT_LINES_4 ) strcat(text, "\r\n"); + else strcat(text, " "); + + /* forth line */ + if(flags.time) + Sprintf(nb = eos(nb), "T:%ld ", moves); + + if(strcmp(hu_stat[u.uhs], " ")) { + Strcat(text, hu_stat[u.uhs]); + Sprintf(nb = eos(nb), " "); + } + if(Confusion) Sprintf(nb = eos(nb), "Conf"); + if(Sick) { + if (u.usick_type & SICK_VOMITABLE) + Sprintf(nb = eos(nb), " FoodPois"); + if (u.usick_type & SICK_NONVOMITABLE) + Sprintf(nb = eos(nb), " Ill"); + } + if(Blind) Sprintf(nb = eos(nb), " Blind"); + if(Stunned) Sprintf(nb = eos(nb), " Stun"); + if(Hallucination) Sprintf(nb = eos(nb), " Hallu"); + if(Slimed) Sprintf(nb = eos(nb), " Slime"); + if(cap > UNENCUMBERED) + Sprintf(nb = eos(nb), " %s", enc_stat[cap]); +} + diff --git a/sys/wince/mhstatus.h b/sys/wince/mhstatus.h new file mode 100644 index 0000000..3658659 --- /dev/null +++ b/sys/wince/mhstatus.h @@ -0,0 +1,14 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINStatusWindow_h +#define MSWINStatusWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_status_window (); +void mswin_status_window_size (HWND hWnd, LPSIZE sz); + +#endif /* MSWINStatusWindow_h */ diff --git a/sys/wince/mhtext.c b/sys/wince/mhtext.c new file mode 100644 index 0000000..d3de165 --- /dev/null +++ b/sys/wince/mhtext.c @@ -0,0 +1,236 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhtext.h" +#include "mhmsg.h" +#include "mhfont.h" +#include "mhcolor.h" +#include "mhtxtbuf.h" + +typedef struct mswin_nethack_text_window { + PNHTextBuffer window_text; + int done; +} NHTextWindow, *PNHTextWindow; + +static WNDPROC editControlWndProc = NULL; + +LRESULT CALLBACK TextWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHTextControlWndProc(HWND, UINT, WPARAM, LPARAM); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void LayoutText(HWND hwnd); + +HWND mswin_init_text_window () { + HWND ret; + PNHTextWindow data; + + ret = CreateDialog( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_NHTEXT), + GetNHApp()->hMainWnd, + TextWndProc + ); + if( !ret ) panic("Cannot create text window"); + + data = (PNHTextWindow)malloc(sizeof(NHTextWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHTextWindow)); + data->window_text = mswin_init_text_buffer( + program_state.gameover? FALSE : GetNHApp()->bWrapText + ); + SetWindowLong(ret, GWL_USERDATA, (LONG)data); + return ret; +} + +void mswin_display_text_window (HWND hWnd) +{ + PNHTextWindow data; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data ) { + HWND control; + control = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + SendMessage(control, EM_FMTLINES, 1, 0 ); + mswin_render_text(data->window_text, GetDlgItem(hWnd, IDC_TEXT_CONTROL)); + + data->done = 0; + mswin_popup_display(hWnd, &data->done); + mswin_popup_destroy(hWnd); + } +} + +LRESULT CALLBACK TextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND control; + HDC hdc; + PNHTextWindow data; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_INITDIALOG: + /* set text control font */ + control = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + if( !control ) { + panic("cannot get text view window"); + } + + hdc = GetDC(control); + SendMessage(control, WM_SETFONT, (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hdc, FALSE), 0); + ReleaseDC(control, hdc); + +#if defined(WIN_CE_SMARTPHONE) + /* special initialization for SmartPhone dialogs */ + NHSPhoneDialogSetup(hWnd, FALSE, GetNHApp()->bFullScreen); +#endif + /* subclass edit control */ + editControlWndProc = (WNDPROC)GetWindowLong(control, GWL_WNDPROC); + SetWindowLong(control, GWL_WNDPROC, (LONG)NHTextControlWndProc); + + if( !program_state.gameover && GetNHApp()->bWrapText ) { + DWORD styles; + styles = GetWindowLong(control, GWL_STYLE); + if( styles ) { + SetWindowLong(control, GWL_STYLE, styles & (~WS_HSCROLL)); + SetWindowPos(control, NULL, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE ); + } + } + + SetFocus(control); + return FALSE; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_SIZE: + LayoutText(hWnd); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + data->done = 1; + return TRUE; + } + break; + + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */ + HDC hdcEdit = (HDC) wParam; + HWND hwndEdit = (HWND) lParam; + if( hwndEdit == GetDlgItem(hWnd, IDC_TEXT_CONTROL) ) { + SetBkColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_BG)); + SetTextColor(hdcEdit, mswin_get_color(NHW_TEXT, MSWIN_COLOR_FG)); + return (BOOL)mswin_get_brush(NHW_TEXT, MSWIN_COLOR_BG); + } + } return FALSE; + + case WM_DESTROY: + if( data ) { + mswin_free_text_buffer(data->window_text); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } + break; + + } + return FALSE; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHTextWindow data; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + mswin_add_text(data->window_text, msg_data->attr, msg_data->text); + break; + } + } +} + +void LayoutText(HWND hWnd) +{ + HWND btn_ok; + HWND text; + RECT clrt, rt; + POINT pt_elem, pt_ok; + SIZE sz_elem, sz_ok; + + text = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + btn_ok = GetDlgItem(hWnd, IDOK); + + /* get window coordinates */ + GetClientRect(hWnd, &clrt ); + + /* set window placements */ + if( IsWindow(btn_ok) ) { + GetWindowRect(btn_ok, &rt); + sz_ok.cx = clrt.right - clrt.left; + sz_ok.cy = rt.bottom-rt.top; + pt_ok.x = clrt.left; + pt_ok.y = clrt.bottom - sz_ok.cy; + MoveWindow(btn_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE ); + + pt_elem.x = clrt.left; + pt_elem.y = clrt.top; + sz_elem.cx = clrt.right - clrt.left; + sz_elem.cy = pt_ok.y; + MoveWindow(text, pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); + } else { + pt_elem.x = clrt.left; + pt_elem.y = clrt.top; + sz_elem.cx = clrt.right - clrt.left; + sz_elem.cy = clrt.bottom - clrt.top; + MoveWindow(text, pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); + } +} + +/* Text control window proc - implements close on space and scrolling on arrows */ +LRESULT CALLBACK NHTextControlWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) { + case WM_KEYUP: + switch( wParam ) { + case VK_SPACE: + case VK_RETURN: + /* close on space */ + PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0); + return 0; + + case VK_UP: + /* scoll up */ + PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + return 0; + + case VK_DOWN: + /* scoll down */ + PostMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL); + return 0; + + case VK_LEFT: + /* scoll left */ + PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINELEFT, 0), (LPARAM)NULL); + return 0; + + case VK_RIGHT: + /* scoll right */ + PostMessage(hWnd, WM_HSCROLL, MAKEWPARAM(SB_LINERIGHT, 0), (LPARAM)NULL); + return 0; + } + break; /* case WM_KEYUP: */ + } + + if( editControlWndProc ) + return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam); + else + return 0; +} diff --git a/sys/wince/mhtext.h b/sys/wince/mhtext.h new file mode 100644 index 0000000..3a7b1b7 --- /dev/null +++ b/sys/wince/mhtext.h @@ -0,0 +1,14 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINTextWindow_h +#define MSWINTextWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_text_window (); +void mswin_display_text_window (HWND hwnd); + +#endif /* MSWINTextWindow_h */ diff --git a/sys/wince/mhtxtbuf.c b/sys/wince/mhtxtbuf.c new file mode 100644 index 0000000..4ab5a83 --- /dev/null +++ b/sys/wince/mhtxtbuf.c @@ -0,0 +1,238 @@ +/* Copyright (C) 2003 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "mhtxtbuf.h" + +/* Collect Nethack text messages and render text into edit box. + Wrap text if necessary. + Recognize formatted lines as having more that 4 consecutive. + spaces inside the string. + Strip leading and trailing spaces. + Always break at the original line end (do not merge text that comes + from NetHack engine) +*/ + +/*----------------------------------------------------------------*/ +#define NHTEXT_BUFFER_INCREMENT 10 +/*----------------------------------------------------------------*/ +struct text_buffer_line { + int attr; + short beg_padding; + short end_padding; + BOOL formatted; + char* text; +}; +/*----------------------------------------------------------------*/ +typedef struct mswin_nethack_text_buffer { + BOOL b_wrap_text; + int n_size; + int n_used; + struct text_buffer_line *text_buffer_line; +} NHTextBuffer, *PNHTextBuffer; +/*----------------------------------------------------------------*/ +#define NHTextLine(pb,i) ((pb)->text_buffer_line[(i)]) +static TCHAR* nh_append( TCHAR* s, int* size, const char* ap ); +/*----------------------------------------------------------------*/ +PNHTextBuffer mswin_init_text_buffer(BOOL wrap_text) +{ + PNHTextBuffer pb = (PNHTextBuffer)malloc(sizeof(NHTextBuffer)); + if( !pb ) panic("Out of memory"); + + ZeroMemory(pb, sizeof(NHTextBuffer)); + pb->b_wrap_text = wrap_text; + pb->n_size = 0; + pb->n_used = 0; + pb->text_buffer_line = NULL; + return pb; +} +/*----------------------------------------------------------------*/ +void mswin_free_text_buffer(PNHTextBuffer pb) +{ + int i; + + if( !pb ) return; + + for(i=0; in_used; i++ ) { + free(pb->text_buffer_line[i].text); + } + free( pb->text_buffer_line ); + free( pb ); +} +/*----------------------------------------------------------------*/ +void mswin_add_text(PNHTextBuffer pb, int attr, const char* text) +{ + char* p; + struct text_buffer_line* new_line; + + /* grow buffer */ + if( pb->n_used >= pb->n_size ) { + pb->n_size += NHTEXT_BUFFER_INCREMENT; + pb->text_buffer_line = + (struct text_buffer_line*)realloc( pb->text_buffer_line, pb->n_size*sizeof(struct text_buffer_line) ); + if( !pb->text_buffer_line ) panic("Memory allocation error"); + } + + /* analyze the new line of text */ + new_line = &NHTextLine(pb, pb->n_used); + new_line->attr = attr; + new_line->beg_padding = 0; + new_line->text = strdup(text); + for( p = new_line->text; *p && isspace(*p); p++ ) { + new_line->beg_padding++; + } + if( *p ) { + memmove(new_line->text, + new_line->text + new_line->beg_padding, + strlen(new_line->text) - new_line->beg_padding + 1 + ); + for( p = new_line->text+strlen(new_line->text); + p>=new_line->text && isspace(*p); + p-- ) { + new_line->end_padding++; + *p = 0; + } + + /* if there are 3 (or more) consecutive spaces inside the string + consider it formatted */ + new_line->formatted = (strstr(new_line->text, " ")!=NULL); + } else { + new_line->end_padding = 0; + new_line->text[0] = 0; + new_line->formatted = FALSE; + } + pb->n_used++; +} +/*----------------------------------------------------------------*/ +static TCHAR* nh_append( TCHAR* s, int* size, const char* ap ) +{ + int tlen, tnewlen; + + if( !(ap && *ap) ) return s; + + /* append the calculated line to the text buffer */ + tlen = s? _tcslen(s) : 0; + tnewlen = tlen+strlen(ap); + if( tnewlen>=*size ) { + *size = max(tnewlen, *size + BUFSZ); + s = (TCHAR*)realloc(s, *size * sizeof(TCHAR)); + if( !s ) panic("Out of memory"); + ZeroMemory(s+tlen, (*size-tlen)*sizeof(TCHAR)); + } + if( strcmp(ap, "\r\n")==0 ) { + _tcscat(s, TEXT("\r\n")); + } else { + NH_A2W(ap, s+tlen, strlen(ap)); + s[tnewlen] = 0; + } + return s; +} +/*----------------------------------------------------------------*/ +void mswin_render_text(PNHTextBuffer pb, HWND edit_control) +{ + RECT rt_client; /* boundaries of the client area of the edit control */ + SIZE size_text; /* size of the edit control */ + RECT rt_text; /* calculated text rectangle for the visible line */ + char buf[BUFSZ]; /* buffer for the visible line */ + TCHAR tbuf[BUFSZ]; /* temp buffer for DrawText */ + TCHAR* pText = NULL; /* resulting text (formatted) */ + int pTextSize = 0; /* resulting text size */ + char* p_cur = NULL; /* current position in the NHTextBuffer->text_buffer_line->text */ + char* p_buf_cur = NULL; /* current position in the visible line buffer */ + int i; + HDC hdcEdit; /* device context for the edit control */ + HFONT hFont, hOldFont; /* edit control font */ + + GetClientRect(edit_control, &rt_client ); + size_text.cx = rt_client.right - rt_client.left; + size_text.cy = rt_client.bottom - rt_client.top; + size_text.cx -= GetSystemMetrics(SM_CXVSCROLL); /* add a slight right margin - the text looks better that way */ + hdcEdit = GetDC(edit_control); + hFont = (HFONT)SendMessage(edit_control, WM_GETFONT, 0, 0); + if( hFont ) hOldFont = SelectObject(hdcEdit, hFont); + + /* loop through each line (outer loop) and wrap it around (inner loop) */ + ZeroMemory(buf, sizeof(buf)); + p_buf_cur = buf; + for( i=0; in_used; i++ ) { + if( pb->b_wrap_text ) { + p_cur = NHTextLine(pb,i).text; + + /* insert an line break for the empty string */ + if( !NHTextLine(pb,i).text[0] ) { + pText = nh_append(pText, &pTextSize, "\r\n"); + continue; + } + + /* add margin to the "formatted" line of text */ + if( NHTextLine(pb,i).formatted ) { + strcpy(buf, " "); + p_buf_cur += 3; + } + + /* scroll thourgh the current line of text and wrap it + so it fits to width of the edit control */ + while( *p_cur ) { + char *p_word_pos = p_buf_cur; + + /* copy one word into the buffer */ + while( *p_cur && isspace(*p_cur) ) + if( p_buf_cur!=buf ) *p_buf_cur++ = *p_cur++; + else p_cur++; + + while( *p_cur && !isspace(*p_cur) ) + *p_buf_cur++ = *p_cur++; + + /* check if it fits */ + SetRect( &rt_text, 0, 0, size_text.cx, size_text.cy ); + DrawText(hdcEdit, NH_A2W(buf, tbuf, p_buf_cur-buf), p_buf_cur-buf, &rt_text, DT_CALCRECT | DT_LEFT | DT_SINGLELINE | DT_NOCLIP); + if( (rt_text.right - rt_text.left)>=size_text.cx ) { + /* Backtrack. + Only backtrack if the last word caused the overflow - + do not backtrack if the entire current line does not fit the visible area. + Otherwise it is a infinite loop. + */ + if( p_word_pos>buf ) { + p_cur -= (p_buf_cur-p_word_pos); + p_buf_cur = p_word_pos; + } + *p_buf_cur = 0; /* break the line */ + + /* append the calculated line to the text buffer */ + pText = nh_append(pText, &pTextSize, buf); + pText = nh_append(pText, &pTextSize, "\r\n"); + ZeroMemory(buf, sizeof(buf)); + p_buf_cur = buf; + } + } + + /* always break the line at the end of the buffer text */ + if( p_buf_cur != buf ) { + /* flush the current buffrer */ + *p_buf_cur = 0; /* break the line */ + pText = nh_append(pText, &pTextSize, buf); + pText = nh_append(pText, &pTextSize, "\r\n"); + ZeroMemory(buf, sizeof(buf)); + p_buf_cur = buf; + } + } else { /* do not wrap text */ + int j; + for( j=0; j */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINTextBuffer_h +#define MSWINTextBuffer_h + +#include "winMS.h" + +typedef struct mswin_nethack_text_buffer* PNHTextBuffer; +PNHTextBuffer mswin_init_text_buffer(BOOL wrap_text); +void mswin_free_text_buffer(PNHTextBuffer pb); +void mswin_add_text(PNHTextBuffer pb, int attr, const char* text); +void mswin_render_text(PNHTextBuffer pb, HWND edit_control); + +#endif /* MSWINTextBuffer_h */ diff --git a/sys/wince/mswproc.c b/sys/wince/mswproc.c new file mode 100644 index 0000000..d08e081 --- /dev/null +++ b/sys/wince/mswproc.c @@ -0,0 +1,1966 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file implements the interface between the window port specific + * code in the mswin port and the rest of the nethack game engine. +*/ + +#include "hack.h" +#include "dlb.h" +#include "winMS.h" +#include "mhmap.h" +#include "mhstatus.h" +#include "mhtext.h" +#include "mhmsgwnd.h" +#include "mhmenu.h" +#include "mhmsg.h" +#include "mhcmd.h" +#include "mhinput.h" +#include "mhaskyn.h" +#include "mhdlg.h" +#include "mhrip.h" +#include "mhmain.h" +#include "mhfont.h" +#include "mhcolor.h" + +#define LLEN 128 + +#ifdef _DEBUG +extern void logDebug(const char *fmt, ...); +#else +void logDebug(const char *fmt, ...) { } +#endif + +static void mswin_main_loop(); +static BOOL initMapTiles(void); +static void prompt_for_player_selection(void); + +/* Interface definition, for windows.c */ +struct window_procs mswin_procs = { + "MSWIN", + WC_COLOR|WC_HILITE_PET|WC_ALIGN_MESSAGE|WC_ALIGN_STATUS| + WC_INVERSE|WC_SCROLL_MARGIN|WC_MAP_MODE| + WC_FONT_MESSAGE|WC_FONT_STATUS|WC_FONT_MENU|WC_FONT_TEXT|WC_FONT_MAP| + WC_FONTSIZ_MESSAGE|WC_FONTSIZ_STATUS|WC_FONTSIZ_MENU|WC_FONTSIZ_TEXT| + WC_TILE_WIDTH|WC_TILE_HEIGHT|WC_TILE_FILE|WC_VARY_MSGCOUNT| + WC_WINDOWCOLORS|WC_PLAYER_SELECTION, + WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT, + mswin_init_nhwindows, + mswin_player_selection, + mswin_askname, + mswin_get_nh_event, + mswin_exit_nhwindows, + mswin_suspend_nhwindows, + mswin_resume_nhwindows, + mswin_create_nhwindow, + mswin_clear_nhwindow, + mswin_display_nhwindow, + mswin_destroy_nhwindow, + mswin_curs, + mswin_putstr, + mswin_display_file, + mswin_start_menu, + mswin_add_menu, + mswin_end_menu, + mswin_select_menu, + genl_message_menu, /* no need for X-specific handling */ + mswin_update_inventory, + mswin_mark_synch, + mswin_wait_synch, +#ifdef CLIPPING + mswin_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + mswin_print_glyph, + mswin_raw_print, + mswin_raw_print_bold, + mswin_nhgetch, + mswin_nh_poskey, + mswin_nhbell, + mswin_doprev_message, + mswin_yn_function, + mswin_getlin, + mswin_get_ext_cmd, + mswin_number_pad, + mswin_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + mswin, + mswin_change_background, +#endif + /* other defs that really should go away (they're tty specific) */ + mswin_start_screen, + mswin_end_screen, + mswin_outrip, + mswin_preference_update, +}; + +/* +init_nhwindows(int* argcp, char** argv) + -- Initialize the windows used by NetHack. This can also + create the standard windows listed at the top, but does + not display them. + -- Any commandline arguments relevant to the windowport + should be interpreted, and *argcp and *argv should + be changed to remove those arguments. + -- When the message window is created, the variable + iflags.window_inited needs to be set to TRUE. Otherwise + all plines() will be done via raw_print(). + ** Why not have init_nhwindows() create all of the "standard" + ** windows? Or at least all but WIN_INFO? -dean +*/ +void mswin_init_nhwindows(int* argc, char** argv) +{ + HWND hWnd; + logDebug("mswin_init_nhwindows()\n"); + +#ifdef _DEBUG + { + /* truncate trace file */ + FILE *dfp = fopen("nhtrace.log", "w"); + fclose(dfp); + } +#endif + + /* intialize input subsystem */ + mswin_nh_input_init(); + + /* read registry settings */ + mswin_read_reg(); + + /* set it to WIN_ERR so we can detect attempts to + use this ID before it is inialized */ + WIN_MAP = WIN_ERR; + + /* check default values */ + if( iflags.wc_fontsiz_statusNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_status = NHFONT_STATUS_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_messageNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_textNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_menuNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_align_message==0 ) iflags.wc_align_message = ALIGN_BOTTOM; + if( iflags.wc_align_status==0 ) iflags.wc_align_status = ALIGN_TOP; + if( iflags.wc_scroll_margin==0 ) iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN; + if( iflags.wc_tile_width==0 ) iflags.wc_tile_width = TILE_X; + if( iflags.wc_tile_height==0 ) iflags.wc_tile_height = TILE_Y; + + if( iflags.wc_vary_msgcount==0 ) iflags.wc_vary_msgcount = 3; + + /* force tabs in menus */ + iflags.menu_tab_sep = 1; + + /* force toptenwin to be true. toptenwin is the option that decides whether to + * write output to a window or stdout. stdout doesn't make sense on Windows + * non-console applications + */ + flags.toptenwin = 1; + set_option_mod_status("toptenwin", SET_IN_FILE); + + /* initialize map tiles bitmap */ + initMapTiles(); + + /* set tile-related options to readonly */ + set_wc_option_mod_status( + WC_TILE_WIDTH|WC_TILE_HEIGHT|WC_TILE_FILE, + DISP_IN_GAME); + + /* init color table */ + mswin_init_color_table(); + + /* set font-related options to change in the game */ + set_wc_option_mod_status( + WC_HILITE_PET | + WC_ALIGN_MESSAGE | + WC_ALIGN_STATUS | + WC_SCROLL_MARGIN | + WC_MAP_MODE | + WC_FONT_MESSAGE | + WC_FONT_STATUS | + WC_FONT_MENU | + WC_FONT_TEXT | + WC_FONTSIZ_MESSAGE | + WC_FONTSIZ_STATUS | + WC_FONTSIZ_MENU | + WC_FONTSIZ_TEXT | + WC_VARY_MSGCOUNT, + SET_IN_GAME + ); + + /* WC2 options */ + set_wc2_option_mod_status( + WC2_FULLSCREEN| + WC2_SOFTKEYBOARD, + SET_IN_FILE + ); + GetNHApp()->bFullScreen = iflags.wc2_fullscreen; + GetNHApp()->bUseSIP = iflags.wc2_softkeyboard; + + set_wc2_option_mod_status( + WC2_WRAPTEXT, + SET_IN_GAME + ); + GetNHApp()->bWrapText = iflags.wc2_wraptext; + + /* create the main nethack window */ + hWnd = mswin_init_main_window(); + if (!hWnd) panic( "Cannot create the main window." ); + ShowWindow(hWnd, GetNHApp()->nCmdShow); + UpdateWindow(hWnd); + GetNHApp()->hMainWnd = hWnd; + + /* set Full screen if requested */ + mswin_set_fullscreen(GetNHApp()->bFullScreen); + + /* let nethack code know that the window subsystem is ready */ + iflags.window_inited = TRUE; +} + + +/* Do a window-port specific player type selection. If player_selection() + offers a Quit option, it is its responsibility to clean up and terminate + the process. You need to fill in pl_character[0]. +*/ +void mswin_player_selection(void) +{ + logDebug("mswin_player_selection()\n"); + +#if defined(WIN_CE_SMARTPHONE) + /* SmartPhone does not supprt combo-boxes therefor we cannot + use dialog for player selection */ + prompt_for_player_selection(); +#else + if (iflags.wc_player_selection == VIA_DIALOG) { + int nRole; + + /* pick player type randomly (use pre-selected role/race/gender/alignment) */ + if( flags.randomall ) { + if (flags.initrole < 0) { + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + raw_print("Incompatible role!"); + flags.initrole = randrole(); + } + } + + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + raw_print("Incompatible race!"); + flags.initrace = randrace(flags.initrole); + } + } + + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + raw_print("Incompatible gender!"); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } + + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + raw_print("Incompatible alignment!"); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } + } else { + /* select a role */ + if( mswin_player_selection_window( &nRole ) == IDCANCEL ) { + bail(0); + } + } + } else { /* iflags.wc_player_selection == VIA_PROMPTS */ + prompt_for_player_selection(); + } +#endif /* defined(WIN_CE_SMARTPHONE) */ +} + +void prompt_for_player_selection(void) +{ + int i, k, n; + char pick4u = 'n', thisch, lastch = 0; + char pbuf[QBUFSZ], plbuf[QBUFSZ]; + winid win; + anything any; + menu_item *selected = 0; + int box_result; + TCHAR wbuf[BUFSZ]; + + logDebug("prompt_for_player_selection()\n"); + + /* prevent an unnecessary prompt */ + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (!flags.randomall && + (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || + flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) { + /* int echoline; */ + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole, + flags.initrace, flags.initgend, flags.initalign); + + /* tty_putstr(BASE_WINDOW, 0, ""); */ + /* echoline = wins[BASE_WINDOW]->cury; */ + box_result = MessageBox(NULL, + NH_A2W(prompt, wbuf, BUFSZ), + TEXT("NetHack for Windows"), +#if defined(WIN_CE_SMARTPHONE) + MB_YESNO | MB_DEFBUTTON1 +#else + MB_YESNOCANCEL | MB_DEFBUTTON1 +#endif + ); + + pick4u = (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033'; + /* tty_putstr(BASE_WINDOW, 0, prompt); */ + do { + /* pick4u = lowc(readchar()); */ + if (index(quitchars, pick4u)) pick4u = 'y'; + } while(!index(ynqchars, pick4u)); + if ((int)strlen(prompt) + 1 < CO) { + /* Echo choice and move back down line */ + /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u); */ + /* tty_putstr(BASE_WINDOW, 0, ""); */ + } else + /* Otherwise it's hard to tell where to echo, and things are + * wrapping a bit messily anyway, so (try to) make sure the next + * question shows up well and doesn't get wrapped at the + * bottom of the window. + */ + /* tty_clear_nhwindow(BASE_WINDOW) */ ; + + if (pick4u != 'y' && pick4u != 'n') { +give_up: /* Quit */ + if (selected) free((genericptr_t) selected); + bail((char *)0); + /*NOTREACHED*/ + return; + } + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + /* Select a role, if necessary */ + /* we'll try to be compatible with pre-selected race/gender/alignment, + * but may not succeed */ + if (flags.initrole < 0) { + char rolenamebuf[QBUFSZ]; + /* Process the choice */ + if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */ + flags.initrole = randrole(); + } + } else { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */ + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + thisch = lowc(roles[i].name.m[0]); + if (thisch == lastch) thisch = highc(thisch); + if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) { + if (flags.initgend == 1 && roles[i].name.f) + Strcpy(rolenamebuf, roles[i].name.f); + else + Strcpy(rolenamebuf, roles[i].name.m); + } else { + if (roles[i].name.f) { + Strcpy(rolenamebuf, roles[i].name.m); + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } else + Strcpy(rolenamebuf, roles[i].name.m); + } + add_menu(win, NO_GLYPH, &any, thisch, + 0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED); + lastch = thisch; + } + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole()+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick a role for your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */ + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, races[i].noun[0], + 0, ATR_NONE, races[i].noun, MENU_UNSELECTED); + } + any.a_int = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrace(flags.initrole)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the race of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initrace = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */ + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], + 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */ + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, + i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], + 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initalign = k; + } + } + /* Success! */ + /* tty_display_nhwindow(BASE_WINDOW, FALSE); */ +} + +/* Ask the user for a player name. */ +void mswin_askname(void) +{ + logDebug("mswin_askname()\n"); + + if( mswin_getlin_window("who are you?", plname, PL_NSIZ)==IDCANCEL ) { + bail("bye-bye"); + /* not reached */ + } +} + + +/* Does window event processing (e.g. exposure events). + A noop for the tty and X window-ports. +*/ +void mswin_get_nh_event(void) +{ + logDebug("mswin_get_nh_event()\n"); + return; +} + +/* Exits the window system. This should dismiss all windows, + except the "window" used for raw_print(). str is printed if possible. +*/ +void mswin_exit_nhwindows(const char *str) +{ + logDebug("mswin_exit_nhwindows(%s)\n", str); + + /* Write Window settings to the registry */ + mswin_write_reg(); + + // Don't do any of this (?) - exit_nhwindows does not terminate + // the application + // DestroyWindow(GetNHApp()->hMainWnd); + // terminate(EXIT_SUCCESS); +} + +/* Prepare the window to be suspended. */ +void mswin_suspend_nhwindows(const char *str) +{ + logDebug("mswin_suspend_nhwindows(%s)\n", str); + return; +} + + +/* Restore the windows after being suspended. */ +void mswin_resume_nhwindows() +{ + logDebug("mswin_resume_nhwindows()\n"); + return; +} + +/* Create a window of type "type" which can be + NHW_MESSAGE (top line) + NHW_STATUS (bottom lines) + NHW_MAP (main dungeon) + NHW_MENU (inventory or other "corner" windows) + NHW_TEXT (help/text, full screen paged window) +*/ +winid +mswin_create_nhwindow(int type) +{ + winid i = 0; + MSNHMsgAddWnd data; + + logDebug("mswin_create_nhwindow(%d)\n", type); + + /* Return the next available winid + */ + + for (i=1; iwindowlist[i].win == NULL && + !GetNHApp()->windowlist[i].dead) + break; + if (i == MAXWINDOWS) + panic ("ERROR: No windows available...\n"); + + switch (type) { + case NHW_MAP: + { + GetNHApp()->windowlist[i].win = mswin_init_map_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_MESSAGE: + { + GetNHApp()->windowlist[i].win = mswin_init_message_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_STATUS: + { + GetNHApp()->windowlist[i].win = mswin_init_status_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_MENU: + { + GetNHApp()->windowlist[i].win = NULL; //will create later + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 1; + break; + } + case NHW_TEXT: + { + GetNHApp()->windowlist[i].win = mswin_init_text_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + } + + ZeroMemory(&data, sizeof(data) ); + data.wid = i; + SendMessage( GetNHApp()->hMainWnd, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ADDWND, (LPARAM)&data ); + return i; +} + +/* Clear the given window, when asked to. */ +void mswin_clear_nhwindow(winid wid) +{ + logDebug("mswin_clear_nhwindow(%d)\n", wid); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { +#ifdef REINCARNATION + if( GetNHApp()->windowlist[wid].type == NHW_MAP ) { + if( Is_rogue_level(&u.uz) ) + mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP), ROGUE_LEVEL_MAP_MODE); + else + mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP), iflags.wc_map_mode); + } +#endif + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CLEAR_WINDOW, (LPARAM)NULL ); + } +} + +/* -- Display the window on the screen. If there is data + pending for output in that window, it should be sent. + If blocking is TRUE, display_nhwindow() will not + return until the data has been displayed on the screen, + and acknowledged by the user where appropriate. + -- All calls are blocking in the tty window-port. + -- Calling display_nhwindow(WIN_MESSAGE,???) will do a + --more--, if necessary, in the tty window-port. +*/ +void mswin_display_nhwindow(winid wid, BOOLEAN_P block) +{ + logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block); + if (GetNHApp()->windowlist[wid].win != NULL) + { + if (GetNHApp()->windowlist[wid].type == NHW_MENU) { + MENU_ITEM_P* p; + mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win, PICK_NONE, &p); + } if (GetNHApp()->windowlist[wid].type == NHW_TEXT) { + mswin_display_text_window(GetNHApp()->windowlist[wid].win); + } if (GetNHApp()->windowlist[wid].type == NHW_RIP) { + mswin_display_RIP_window(GetNHApp()->windowlist[wid].win); + } else { + if( !block ) { + UpdateWindow(GetNHApp()->windowlist[wid].win); + } else { + if ( GetNHApp()->windowlist[wid].type == NHW_MAP ) { + (void) mswin_nhgetch(); + } + } + } + SetFocus(GetNHApp()->hMainWnd); + } +} + + +HWND mswin_hwnd_from_winid(winid wid) +{ + if( wid>=0 && widwindowlist[wid].win; + } else { + return NULL; + } +} + +winid mswin_winid_from_handle(HWND hWnd) +{ + winid i = 0; + + for (i=1; iwindowlist[i].win == hWnd) + return i; + return -1; +} + +winid mswin_winid_from_type(int type) +{ + winid i = 0; + + for (i=1; iwindowlist[i].type == type) + return i; + return -1; +} + +void mswin_window_mark_dead(winid wid) +{ + if( wid>=0 && widwindowlist[wid].win = NULL; + GetNHApp()->windowlist[wid].dead = 1; + } +} + +/* Destroy will dismiss the window if the window has not + * already been dismissed. +*/ +void mswin_destroy_nhwindow(winid wid) +{ + logDebug("mswin_destroy_nhwindow(%d)\n", wid); + + if ((GetNHApp()->windowlist[wid].type == NHW_MAP) || + (GetNHApp()->windowlist[wid].type == NHW_MESSAGE) || + (GetNHApp()->windowlist[wid].type == NHW_STATUS)) { + /* main windows is going to take care of those */ + return; + } + + if (wid != -1) { + if( !GetNHApp()->windowlist[wid].dead && + GetNHApp()->windowlist[wid].win != NULL ) + DestroyWindow(GetNHApp()->windowlist[wid].win); + GetNHApp()->windowlist[wid].win = NULL; + GetNHApp()->windowlist[wid].type = 0; + GetNHApp()->windowlist[wid].dead = 0; + } +} + +/* Next output to window will start at (x,y), also moves + displayable cursor to (x,y). For backward compatibility, + 1 <= x < cols, 0 <= y < rows, where cols and rows are + the size of window. +*/ +void mswin_curs(winid wid, int x, int y) +{ + logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgCursor data; + data.x = x; + data.y = y; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CURSOR, (LPARAM)&data ); + } +} + +/* +putstr(window, attr, str) + -- Print str on the window with the given attribute. Only + printable ASCII characters (040-0126) must be supported. + Multiple putstr()s are output on separate lines. +Attributes + can be one of + ATR_NONE (or 0) + ATR_ULINE + ATR_BOLD + ATR_BLINK + ATR_INVERSE + If a window-port does not support all of these, it may map + unsupported attributes to a supported one (e.g. map them + all to ATR_INVERSE). putstr() may compress spaces out of + str, break str, or truncate str, if necessary for the + display. Where putstr() breaks a line, it has to clear + to end-of-line. + -- putstr should be implemented such that if two putstr()s + are done consecutively the user will see the first and + then the second. In the tty port, pline() achieves this + by calling more() or displaying both on the same line. +*/ +void mswin_putstr(winid wid, int attr, const char *text) +{ + logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text); + + mswin_putstr_ex(wid, attr, text, 0); +} + +void mswin_putstr_ex(winid wid, int attr, const char *text, boolean app) +{ + if( (wid >= 0) && + (wid < MAXWINDOWS) ) + { + if( GetNHApp()->windowlist[wid].win==NULL && + GetNHApp()->windowlist[wid].type==NHW_MENU ) { + GetNHApp()->windowlist[wid].win = mswin_init_menu_window(MENU_TYPE_TEXT); + GetNHApp()->windowlist[wid].dead = 0; + } + + if (GetNHApp()->windowlist[wid].win != NULL) + { + MSNHMsgPutstr data; + ZeroMemory(&data, sizeof(data)); + data.attr = attr; + data.text = text; + data.append = app; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_PUTSTR, (LPARAM)&data ); + } + } +} + +/* Display the file named str. Complain about missing files + iff complain is TRUE. +*/ +void mswin_display_file(const char *filename,BOOLEAN_P must_exist) +{ + dlb *f; + TCHAR wbuf[BUFSZ]; + + logDebug("mswin_display_file(%s, %d)\n", filename, must_exist); + + f = dlb_fopen(filename, RDTMODE); + if (!f) { + if (must_exist) { + TCHAR message[90]; + _stprintf(message, TEXT("Warning! Could not find file: %s\n"), NH_A2W(filename, wbuf, sizeof(wbuf))); + MessageBox(GetNHApp()->hMainWnd, message, TEXT("ERROR"), MB_OK | MB_ICONERROR ); + } + } else { + winid text; + char line[LLEN]; + + text = mswin_create_nhwindow(NHW_TEXT); + + while (dlb_fgets(line, LLEN, f)) { + size_t len; + len = strlen(line); + if( line[len-1]=='\n' ) line[len-1]='\x0'; + mswin_putstr(text, ATR_NONE, line); + } + (void) dlb_fclose(f); + + mswin_display_nhwindow(text, 1); + mswin_destroy_nhwindow(text); + } +} + +/* Start using window as a menu. You must call start_menu() + before add_menu(). After calling start_menu() you may not + putstr() to the window. Only windows of type NHW_MENU may + be used for menus. +*/ +void mswin_start_menu(winid wid) +{ + logDebug("mswin_start_menu(%d)\n", wid); + if( (wid >= 0) && + (wid < MAXWINDOWS) ) { + if( GetNHApp()->windowlist[wid].win==NULL && + GetNHApp()->windowlist[wid].type==NHW_MENU ) { + GetNHApp()->windowlist[wid].win = mswin_init_menu_window(MENU_TYPE_MENU); + GetNHApp()->windowlist[wid].dead = 0; + } + + if(GetNHApp()->windowlist[wid].win != NULL) { + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_STARTMENU, (LPARAM)NULL + ); + } + } +} + +/* +add_menu(windid window, int glyph, const anything identifier, + char accelerator, char groupacc, + int attr, char *str, boolean preselected) + -- Add a text line str to the given menu window. If identifier + is 0, then the line cannot be selected (e.g. a title). + Otherwise, identifier is the value returned if the line is + selected. Accelerator is a keyboard key that can be used + to select the line. If the accelerator of a selectable + item is 0, the window system is free to select its own + accelerator. It is up to the window-port to make the + accelerator visible to the user (e.g. put "a - " in front + of str). The value attr is the same as in putstr(). + Glyph is an optional glyph to accompany the line. If + window port cannot or does not want to display it, this + is OK. If there is no glyph applicable, then this + value will be NO_GLYPH. + -- All accelerators should be in the range [A-Za-z]. + -- It is expected that callers do not mix accelerator + choices. Either all selectable items have an accelerator + or let the window system pick them. Don't do both. + -- Groupacc is a group accelerator. It may be any character + outside of the standard accelerator (see above) or a + number. If 0, the item is unaffected by any group + accelerator. If this accelerator conflicts with + the menu command (or their user defined alises), it loses. + The menu commands and aliases take care not to interfere + with the default object class symbols. + -- If you want this choice to be preselected when the + menu is displayed, set preselected to TRUE. +*/ +void mswin_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel) +{ + logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", + wid, glyph, identifier, (char)accelerator, (char)group_accel, + attr, str, presel); + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgAddMenu data; + ZeroMemory(&data, sizeof(data)); + data.glyph = glyph; + data.identifier = identifier; + data.accelerator = accelerator; + data.group_accel = group_accel; + data.attr = attr; + data.str = str; + data.presel = presel; + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ADDMENU, (LPARAM)&data + ); + } +} + +/* +end_menu(window, prompt) + -- Stop adding entries to the menu and flushes the window + to the screen (brings to front?). Prompt is a prompt + to give the user. If prompt is NULL, no prompt will + be printed. + ** This probably shouldn't flush the window any more (if + ** it ever did). That should be select_menu's job. -dean +*/ +void mswin_end_menu(winid wid, const char *prompt) +{ + logDebug("mswin_end_menu(%d, %s)\n", wid, prompt); + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgEndMenu data; + ZeroMemory(&data, sizeof(data)); + data.text = prompt; + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ENDMENU, (LPARAM)&data + ); + } +} + +/* +int select_menu(windid window, int how, menu_item **selected) + -- Return the number of items selected; 0 if none were chosen, + -1 when explicitly cancelled. If items were selected, then + selected is filled in with an allocated array of menu_item + structures, one for each selected line. The caller must + free this array when done with it. The "count" field + of selected is a user supplied count. If the user did + not supply a count, then the count field is filled with + -1 (meaning all). A count of zero is equivalent to not + being selected and should not be in the list. If no items + were selected, then selected is NULL'ed out. How is the + mode of the menu. Three valid values are PICK_NONE, + PICK_ONE, and PICK_N, meaning: nothing is selectable, + only one thing is selectable, and any number valid items + may selected. If how is PICK_NONE, this function should + never return anything but 0 or -1. + -- You may call select_menu() on a window multiple times -- + the menu is saved until start_menu() or destroy_nhwindow() + is called on the window. + -- Note that NHW_MENU windows need not have select_menu() + called for them. There is no way of knowing whether + select_menu() will be called for the window at + create_nhwindow() time. +*/ +int mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) +{ + int nReturned = -1; + + logDebug("mswin_select_menu(%d, %d)\n", wid, how); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + nReturned = mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win, how, selected); + } + return nReturned; +} + +/* + -- Indicate to the window port that the inventory has been changed. + -- Merely calls display_inventory() for window-ports that leave the + window up, otherwise empty. +*/ +void mswin_update_inventory() +{ + logDebug("mswin_update_inventory()\n"); +} + +/* +mark_synch() -- Don't go beyond this point in I/O on any channel until + all channels are caught up to here. Can be an empty call + for the moment +*/ +void mswin_mark_synch() +{ + logDebug("mswin_mark_synch()\n"); +} + +/* +wait_synch() -- Wait until all pending output is complete (*flush*() for + streams goes here). + -- May also deal with exposure events etc. so that the + display is OK when return from wait_synch(). +*/ +void mswin_wait_synch() +{ + logDebug("mswin_wait_synch()\n"); +} + +/* +cliparound(x, y)-- Make sure that the user is more-or-less centered on the + screen if the playing area is larger than the screen. + -- This function is only defined if CLIPPING is defined. +*/ +void mswin_cliparound(int x, int y) +{ + winid wid = WIN_MAP; + + logDebug("mswin_cliparound(%d, %d)\n", x, y); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgClipAround data; + data.x = x; + data.y = y; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CLIPAROUND, (LPARAM)&data ); + } +} + +/* +print_glyph(window, x, y, glyph) + -- Print the glyph at (x,y) on the given window. Glyphs are + integers at the interface, mapped to whatever the window- + port wants (symbol, font, color, attributes, ...there's + a 1-1 map between glyphs and distinct things on the map). +*/ +void mswin_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph) +{ + logDebug("mswin_print_glyph(%d, %d, %d, %d)\n", wid, x, y, glyph); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgPrintGlyph data; + + ZeroMemory(&data, sizeof(data) ); + data.x = x; + data.y = y; + data.glyph = glyph; + SendMessage( GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_PRINT_GLYPH, (LPARAM)&data ); + } +} + +/* +raw_print(str) -- Print directly to a screen, or otherwise guarantee that + the user sees str. raw_print() appends a newline to str. + It need not recognize ASCII control characters. This is + used during startup (before windowing system initialization + -- maybe this means only error startup messages are raw), + for error messages, and maybe other "msg" uses. E.g. + updating status for micros (i.e, "saving"). +*/ +void mswin_raw_print(const char *str) +{ + TCHAR wbuf[255]; + logDebug("mswin_raw_print(%s)\n", str); + if( str && *str) + MessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)), TEXT("NetHack"), MB_OK ); +} + +/* +raw_print_bold(str) + -- Like raw_print(), but prints in bold/standout (if +possible). +*/ +void mswin_raw_print_bold(const char *str) +{ + TCHAR wbuf[255]; + logDebug("mswin_raw_print_bold(%s)\n", str); + if( str && *str) + MessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)), TEXT("NetHack"), MB_OK ); +} + +/* +int nhgetch() -- Returns a single character input from the user. + -- In the tty window-port, nhgetch() assumes that tgetch() + will be the routine the OS provides to read a character. + Returned character _must_ be non-zero. +*/ +int mswin_nhgetch() +{ + PMSNHEvent event; + int key = 0; + + logDebug("mswin_nhgetch()\n"); + + + while( (event = mswin_input_pop()) == NULL || + event->type != NHEVENT_CHAR ) + mswin_main_loop(); + + key = event->kbd.ch; + return (key); +} + +/* +int nh_poskey(int *x, int *y, int *mod) + -- Returns a single character input from the user or a + a positioning event (perhaps from a mouse). If the + return value is non-zero, a character was typed, else, + a position in the MAP window is returned in x, y and mod. + mod may be one of + + CLICK_1 -- mouse click type 1 + CLICK_2 -- mouse click type 2 + + The different click types can map to whatever the + hardware supports. If no mouse is supported, this + routine always returns a non-zero character. +*/ +int mswin_nh_poskey(int *x, int *y, int *mod) +{ + PMSNHEvent event; + int key; + + logDebug("mswin_nh_poskey()\n"); + + while( (event = mswin_input_pop())==NULL ) mswin_main_loop(); + + if( event->type==NHEVENT_MOUSE ) { + *mod = event->ms.mod; + *x = event->ms.x; + *y = event->ms.y; + key = 0; + } else { + key = event->kbd.ch; + } + return (key); +} + +/* +nhbell() -- Beep at user. [This will exist at least until sounds are + redone, since sounds aren't attributable to windows anyway.] +*/ +void mswin_nhbell() +{ + logDebug("mswin_nhbell()\n"); +} + +/* +doprev_message() + -- Display previous messages. Used by the ^P command. + -- On the tty-port this scrolls WIN_MESSAGE back one line. +*/ +int mswin_doprev_message() +{ + logDebug("mswin_doprev_message()\n"); + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + return 0; +} + +/* +char yn_function(const char *ques, const char *choices, char default) + -- Print a prompt made up of ques, choices and default. + Read a single character response that is contained in + choices or default. If choices is NULL, all possible + inputs are accepted and returned. This overrides + everything else. The choices are expected to be in + lower case. Entering ESC always maps to 'q', or 'n', + in that order, if present in choices, otherwise it maps + to default. Entering any other quit character (SPACE, + RETURN, NEWLINE) maps to default. + -- If the choices string contains ESC, then anything after + it is an acceptable response, but the ESC and whatever + follows is not included in the prompt. + -- If the choices string contains a '#' then accept a count. + Place this value in the global "yn_number" and return '#'. + -- This uses the top line in the tty window-port, other + ports might use a popup. +*/ +char mswin_yn_function(const char *question, const char *choices, + CHAR_P def) +{ + int result=-1; + char ch; + char yn_esc_map='\033'; + char message[BUFSZ]; + char res_ch[2]; + + logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def); + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + Strcpy(choicebuf, choices); + if ((cb = index(choicebuf, '\033')) != 0) { + /* anything beyond is hidden */ + *cb = '\0'; + } + sprintf(message, "%s [%s] ", question, choicebuf); + if (def) sprintf(eos(message), "(%c) ", def); + /* escape maps to 'q' or 'n' or default, in that order */ + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + Strcpy(message, question); + } + +#if defined(WIN_CE_SMARTPHONE) + { + char buf[BUFSZ]; + ZeroMemory(buf, sizeof(buf)); + if( choices ) { + if( !index(choices, '\033') ) buf[0]='\033'; /* make sure ESC is always available */ + strncat( buf, choices, sizeof(buf)-2); + NHSPhoneSetKeypadFromString( buf ); + } else { + /* sometimes choices are included in the message itself, e.g. "what? [abcd]" */ + char *p1, *p2; + p1 = strchr(question, '['); + p2 = strrchr(question, ']'); + if( p1 && p2 && p1= 0) && (wid < MAXWINDOWS) ) { + DestroyWindow(GetNHApp()->windowlist[wid].win); + GetNHApp()->windowlist[wid].win = mswin_init_RIP_window(); + GetNHApp()->windowlist[wid].type = NHW_RIP; + GetNHApp()->windowlist[wid].dead = 0; + } + + genl_outrip(wid, how); +} + +/* handle options updates here */ +void mswin_preference_update(const char *pref) +{ + HDC hdc; + + if( _stricmp( pref, "font_menu")==0 || + _stricmp( pref, "font_size_menu")==0 ) { + if( iflags.wc_fontsiz_menuNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "font_status")==0 || + _stricmp( pref, "font_size_status")==0 ) { + + if( iflags.wc_fontsiz_statusNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "font_message")==0 || + _stricmp( pref, "font_size_message")==0 ) { + + if( iflags.wc_fontsiz_messageNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "font_text")==0 || + _stricmp( pref, "font_size_text")==0 ) { + + if( iflags.wc_fontsiz_textNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "scroll_margin")==0 ) { + mswin_cliparound(u.ux, u.uy); + return; + } + + if( _stricmp( pref, "map_mode")==0 ) { + mswin_select_map_mode( iflags.wc_map_mode ); + return; + } + + if( _stricmp( pref, "hilite_pet")==0 ) { + InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE); + return; + } + + if( _stricmp( pref, "align_message")==0 || + _stricmp( pref, "align_status")==0 ) { + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "vary_msgcount")==0 ) { + InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE); + mswin_layout_main_window(NULL); + return; + } + + if( _stricmp( pref, "fullscreen")==0 ) { + mswin_set_fullscreen(iflags.wc2_fullscreen); + return; + } + + if( _stricmp( pref, "softkeyboard")==0 ) { + GetNHApp()->bUseSIP = iflags.wc2_softkeyboard; + return; + } + + if( _stricmp( pref, "wraptext")==0 ) { + GetNHApp()->bWrapText = iflags.wc2_wraptext; + return; + } + +} + +void mswin_main_loop() +{ + MSG msg; + + while( !mswin_have_input() && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +/* clean up and quit */ +void bail(const char *mesg) +{ + clearlocks(); + mswin_exit_nhwindows(mesg); + terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +BOOL initMapTiles(void) +{ + HBITMAP hBmp; + BITMAP bm; + TCHAR wbuf[MAX_PATH]; + int tl_num; + SIZE map_size; + extern int total_tiles_used; + + /* no file - no tile */ + if( !(iflags.wc_tile_file && *iflags.wc_tile_file) ) + return TRUE; + + /* load bitmap */ + hBmp = SHLoadDIBitmap(NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH)); + if( hBmp==NULL ) { + raw_print("Cannot load tiles from the file. Reverting back to default."); + return FALSE; + } + + /* calculate tile dimensions */ + GetObject(hBmp, sizeof(BITMAP), (LPVOID)&bm); + if( bm.bmWidth%iflags.wc_tile_width || + bm.bmHeight%iflags.wc_tile_height ) { + DeleteObject(hBmp); + raw_print("Tiles bitmap does not match tile_width and tile_height options. Reverting back to default."); + return FALSE; + } + + tl_num = (bm.bmWidth/iflags.wc_tile_width)* + (bm.bmHeight/iflags.wc_tile_height); + if( tl_numbmpMapTiles!=GetNHApp()->bmpTiles ) { + DeleteObject(GetNHApp()->bmpMapTiles); + } + + GetNHApp()->bmpMapTiles = hBmp; + GetNHApp()->mapTile_X = iflags.wc_tile_width; + GetNHApp()->mapTile_Y = iflags.wc_tile_height; + GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width; + + map_size.cx = GetNHApp()->mapTile_X * COLNO; + map_size.cy = GetNHApp()->mapTile_Y * ROWNO; + mswin_map_stretch( + mswin_hwnd_from_winid(WIN_MAP), + &map_size, + TRUE + ); + return TRUE; +} + +void mswin_popup_display(HWND hWnd, int* done_indicator) +{ + MSG msg; + HWND hChild; + + /* activate the menu window */ + GetNHApp()->hPopupWnd = hWnd; + + mswin_layout_main_window(hWnd); + + /* disable game windows */ + for( hChild=GetWindow(GetNHApp()->hMainWnd, GW_CHILD); + hChild; + hChild = GetWindow(hChild, GW_HWNDNEXT) ) { + if( hChild!=hWnd ) EnableWindow(hChild, FALSE); + } +#if defined(WIN_CE_SMARTPHONE) + ShowWindow(GetNHApp()->hMenuBar, SW_HIDE); + ShowWindow(SHFindMenuBar(hWnd), SW_SHOW); +#else + EnableWindow(GetNHApp()->hMenuBar, FALSE); +#endif + + /* bring menu window on top */ + SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + + /* go into message loop */ + if( done_indicator ) *done_indicator = 0; + while( IsWindow(hWnd) && + (done_indicator==NULL || !*done_indicator) && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if( !IsDialogMessage(hWnd, &msg) ) { + if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} + +void mswin_popup_destroy(HWND hWnd) +{ + HWND hChild; + + /* enable game windows */ + for( hChild=GetWindow(GetNHApp()->hMainWnd, GW_CHILD); + hChild; + hChild = GetWindow(hChild, GW_HWNDNEXT) ) { + if( hChild!= hWnd) { + EnableWindow(hChild, TRUE); + } + } +#if defined(WIN_CE_SMARTPHONE) + ShowWindow(SHFindMenuBar(hWnd), SW_HIDE); + ShowWindow(GetNHApp()->hMenuBar, SW_SHOW); +#else + EnableWindow(GetNHApp()->hMenuBar, TRUE); +#endif + + SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + GetNHApp()->hPopupWnd = NULL; + mswin_window_mark_dead( mswin_winid_from_handle(hWnd) ); + DestroyWindow(hWnd); + + mswin_layout_main_window(hWnd); + + SetFocus(GetNHApp()->hMainWnd ); +} + +void mswin_set_fullscreen(BOOL is_fullscreen) +{ +#if defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + SetForegroundWindow(GetNHApp()->hMainWnd); + if( is_fullscreen ) { + SHFullScreen(GetNHApp()->hMainWnd, SHFS_HIDETASKBAR | SHFS_HIDESTARTICON); + MoveWindow( + GetNHApp()->hMainWnd, + 0, + 0, + GetSystemMetrics(SM_CXSCREEN), + GetSystemMetrics(SM_CYSCREEN), + FALSE + ); + } else { + RECT rc; + SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); + SHFullScreen(GetNHApp()->hMainWnd, SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON); + MoveWindow( + GetNHApp()->hMainWnd, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + FALSE + ); + } + GetNHApp()->bFullScreen = is_fullscreen; +#else + GetNHApp()->bFullScreen = FALSE; +#endif +} + +#if defined(WIN_CE_SMARTPHONE) +void NHSPhoneDialogSetup(HWND hDlg, BOOL is_edit, BOOL is_fullscreen) +{ + SHMENUBARINFO mbi; + HWND hOK, hCancel; + RECT rtOK, rtDlg; + + // Create our MenuBar + ZeroMemory(&mbi, sizeof(SHMENUBARINFO)); + mbi.cbSize = sizeof(mbi); + mbi.hwndParent = hDlg; + mbi.nToolBarId = IDC_SPHONE_DIALOGBAR; + mbi.hInstRes = GetNHApp()->hApp; + if(!SHCreateMenuBar(&mbi)) { + error("cannot create dialog menu"); + } + + if(is_fullscreen) { + SHINITDLGINFO shidi; + RECT main_wnd_rect; + shidi.dwMask = SHIDIM_FLAGS; + shidi.dwFlags = SHIDIF_SIZEDLGFULLSCREEN; + shidi.hDlg = hDlg; + SHInitDialog(&shidi); + + GetWindowRect(GetNHApp()->hMainWnd, &main_wnd_rect); + MoveWindow( + hDlg, + main_wnd_rect.left, + main_wnd_rect.top, + main_wnd_rect.right - main_wnd_rect.left, + main_wnd_rect.bottom - main_wnd_rect.top, + FALSE + ); + } + + /* hide OK and CANCEL buttons */ + hOK = GetDlgItem(hDlg, IDOK); + hCancel = GetDlgItem(hDlg, IDCANCEL); + + if( IsWindow(hCancel) ) ShowWindow(hCancel, SW_HIDE); + if( IsWindow(hOK) ) { + GetWindowRect(hOK, &rtOK); + GetWindowRect(hDlg, &rtDlg); + + rtDlg.bottom -= rtOK.bottom-rtOK.top; + ShowWindow(hOK, SW_HIDE); + SetWindowPos( hDlg, HWND_TOP, + 0, 0, + rtDlg.right-rtDlg.left, rtDlg.bottom-rtDlg.top, + SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOZORDER ); + } + + /* override "Back" button for edit box dialogs */ + if( is_edit ) + SendMessage(mbi.hwndMB, SHCMBM_OVERRIDEKEY, VK_TBACK, MAKELPARAM(SHMBOF_NODEFAULT | SHMBOF_NOTIFY, SHMBOF_NODEFAULT | SHMBOF_NOTIFY)); +} +#endif /* defined(WIN_CE_SMARTPHONE) */ + +void mswin_read_reg(void) +{ +} + +void mswin_destroy_reg(void) +{ +} + +void mswin_write_reg(void) +{ +} + +#ifdef _DEBUG +#include + +void +logDebug(const char *fmt, ...) +{ + FILE *dfp = fopen("nhtrace.log", "a"); + + if (dfp) { + va_list args; + + va_start(args, fmt); + vfprintf(dfp, fmt, args); + va_end(args); + fclose(dfp); + } +} + +#endif + diff --git a/sys/wince/newres.h b/sys/wince/newres.h new file mode 100644 index 0000000..fa66dcb --- /dev/null +++ b/sys/wince/newres.h @@ -0,0 +1,40 @@ +#ifndef __NEWRES_H__ +#define __NEWRES_H__ + +#if !defined(UNDER_CE) + #define UNDER_CE _WIN32_WCE +#endif + +#if defined(_WIN32_WCE) + #if !defined(WCEOLE_ENABLE_DIALOGEX) + #define DIALOGEX DIALOG DISCARDABLE + #endif + #include + #define SHMENUBAR RCDATA + #if (defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)) && (_WIN32_WCE >= 300) + #include + #else + #define I_IMAGENONE (-2) + #define NOMENU 0xFFFF + #define IDS_SHNEW 1 + + #define IDM_SHAREDNEW 10 + #define IDM_SHAREDNEWDEFAULT 11 + #endif +#endif // _WIN32_WCE + + +#ifdef RC_INVOKED +#ifndef _INC_WINDOWS +#define _INC_WINDOWS + #include "winuser.h" // extract from windows header +#endif +#endif + +#ifdef IDC_STATIC +#undef IDC_STATIC +#endif +#define IDC_STATIC (-1) + +#endif //__NEWRES_H__ + diff --git a/sys/wince/resource.h b/sys/wince/resource.h new file mode 100644 index 0000000..63b8d45 --- /dev/null +++ b/sys/wince/resource.h @@ -0,0 +1,159 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by winhack.rc +// +#define IDC_MYICON 2 +#define IDD_WINHACK_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDS_HELLO 106 +#define IDI_WINHACK 107 +#define IDC_WINHACK 109 +#define IDC_SPHONE_DIALOGBAR 111 +#define IDR_MAINFRAME 128 +#define IDB_TILES 129 +#define IDD_TEXT 130 +#define IDD_NHTEXT 130 +#define IDD_MENU 132 +#define IDB_MENU_SEL 133 +#define IDB_MENU_UNSEL 134 +#define IDD_COMMANDS 136 +#define IDD_GETLIN 138 +#define IDD_EXTCMD 139 +#define IDD_PLAYER_SELECTOR 141 +#define IDB_PETMARK 145 +#define IDB_MENU_SEL_COUNT 146 +#define IDB_KEYPAD 147 +#define IDB_MENUBAR 154 +#define IDC_TEXT_VIEW 1001 +#define IDC_CMD_MOVE_NW 1001 +#define IDC_CMD_MOVE_N 1002 +#define IDC_MENU_LIST 1003 +#define IDC_CMD_MOVE_NE 1003 +#define IDC_MENU_TEXT 1004 +#define IDC_CMD_MOVE_W 1004 +#define IDC_CMD_MOVE_SELF 1005 +#define IDC_CMD_MOVE_E 1006 +#define IDC_CMD_MOVE_SW 1007 +#define IDC_CMD_MOVE_S 1008 +#define IDC_CMD_MOVE_SE 1009 +#define IDC_CMD_MOVE_UP 1010 +#define IDC_CMD_MOVE_DOWN 1011 +#define IDC_CMD_5 1012 +#define IDC_CMD_A 1013 +#define IDC_CMD_B 1014 +#define IDC_CMD_C 1015 +#define IDC_CMD_D 1016 +#define IDC_CMD_E 1017 +#define IDC_CMD_F 1018 +#define IDC_CMD_G 1019 +#define IDC_CMD_H 1020 +#define IDC_CMD_I 1021 +#define IDC_CMD_J 1022 +#define IDC_CMD_K 1023 +#define IDC_CMD_L 1024 +#define IDC_CMD_M 1025 +#define IDC_CMD_N 1026 +#define IDC_CMD_O 1027 +#define IDC_CMD_P 1028 +#define IDC_CMD_Q 1029 +#define IDC_CMD_R 1030 +#define IDC_CMD_S 1031 +#define IDC_CMD_T 1032 +#define IDC_CMD_U 1033 +#define IDC_CMD_V 1034 +#define IDC_CMD_W 1035 +#define IDC_CMD_X 1036 +#define IDC_CMD_Y 1037 +#define IDC_CMD_Z 1038 +#define IDC_CMD_AA 1039 +#define IDC_CMD_BB 1040 +#define IDC_CMD_CC 1041 +#define IDC_CMD_DD 1042 +#define IDC_CMD_EE 1043 +#define IDC_CMD_FF 1044 +#define IDC_CMD_GG 1045 +#define IDC_CMD_HH 1046 +#define IDC_CMD_II 1047 +#define IDC_CMD_JJ 1048 +#define IDC_CMD_KK 1049 +#define IDC_CMD_LL 1050 +#define IDC_CMD_MM 1051 +#define IDC_CMD_NN 1052 +#define IDC_CMD_OO 1053 +#define IDC_CMD_PP 1054 +#define IDC_CMD_QQ 1055 +#define IDC_CMD_RR 1056 +#define IDC_CMD_SS 1057 +#define IDC_CMD_TT 1058 +#define IDC_CMD_UU 1059 +#define IDC_CMD_VV 1060 +#define IDC_CMD_WW 1061 +#define IDC_CMD_XX 1062 +#define IDC_CMD_YY 1063 +#define IDC_CMD_ZZ 1064 +#define IDC_CMD_FIRST 1100 +#define IDC_CMD_LAST 1300 +#define IDC_GETLIN_EDIT 1309 +#define IDC_EXTCMD_LIST 1310 +#define IDC_PLSEL_NAME 1314 +#define IDC_PLSEL_ROLE_RANDOM 1315 +#define IDC_PLSEL_RACE_RANDOM 1318 +#define IDC_PLSEL_GENDER_RANDOM 1319 +#define IDC_PLSEL_ALIGN_RANDOM 1320 +#define IDC_PLSEL_ROLE_LIST 1323 +#define IDC_PLSEL_RACE_LIST 1324 +#define IDC_PLSEL_ALIGN_LIST 1325 +#define IDC_PLSEL_GENDER_LIST 1326 +#define IDC_ABOUT_VERSION 1327 +#define IDC_TEXT_CONTROL 1331 +#define IDC_ABOUT_COPYRIGHT 1332 +#define IDM_SAVE 32771 +#define IDM_HELP_LONG 32772 +#define IDM_HELP_COMMANDS 32773 +#define IDM_HELP_HISTORY 32774 +#define IDM_HELP_INFO_CHAR 32775 +#define IDM_HELP_INFO_KEY 32776 +#define IDM_HELP_OPTIONS 32777 +#define IDM_HELP_OPTIONS_LONG 32778 +#define IDM_HELP_EXTCMD 32779 +#define IDM_HELP_LICENSE 32780 +#define IDM_MAP_TILES 32781 +#define IDM_MAP_ASCII4X6 32782 +#define IDM_MAP_ASCII6X8 32783 +#define IDM_MAP_ASCII8X8 32784 +#define IDM_MAP_ASCII16X8 32785 +#define IDM_MAP_ASCII7X12 32786 +#define IDM_MAP_ASCII8X12 32787 +#define IDM_MAP_ASCII16X12 32788 +#define IDM_MAP_ASCII12X16 32789 +#define IDM_MAP_ASCII10X18 32790 +#define IDM_MAP_FIT_TO_SCREEN 32791 +#define ID_FILE 32792 +#define IDS_CAP_FILE 32793 +#define ID_HELP 32794 +#define IDS_CAP_HELP 32795 +#define IDS_CAP_TEMP 32796 +#define ID_MAP 32797 +#define IDS_CAP_AMP 32798 +#define IDS_CAP_MAP 32799 +#define IDM_VIEW_KEYPAD 32800 +#define ID_VIEW 32801 +#define IDS_CAP_VIEW 32802 +#define IDS_CAP_ENTIREMAP 32826 +#define IDS_CAP_NORMALMAP 32827 +#define IDM_HELP_MENU 32828 +#define IDM_VIEW_OPTIONS 32829 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 155 +#define _APS_NEXT_COMMAND_VALUE 32830 +#define _APS_NEXT_CONTROL_VALUE 1334 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/sys/wince/winMS.h b/sys/wince/winMS.h new file mode 100644 index 0000000..6f7e1ad --- /dev/null +++ b/sys/wince/winMS.h @@ -0,0 +1,182 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINMS_H +#define WINMS_H + +#pragma warning(disable:4142) /* benign redefinition of type */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include "resource.h" +#include "hack.h" + +#if defined(WIN_CE_POCKETPC) +#include +#include +#endif + +#if defined(WIN_CE_SMARTPHONE) +#include +#include +#include +#include +#include +#include +#include +#endif + +#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO) +#include +#endif + +/* Taskbar Menu height */ +#define MENU_HEIGHT 26 + +/* Create an array to keep track of the various windows */ + +#ifndef MAXWINDOWS +#define MAXWINDOWS 15 +#endif + +/* RIP window ID */ +#define NHW_RIP 32 +#define NHW_KEYPAD 33 + +/* size of tiles */ +#ifndef TILE_X +#define TILE_X 16 +#endif +#define TILE_Y 16 + +/* tiles per line in the bitmap */ +#define TILES_PER_LINE 40 + +/* tile background color */ +#define TILE_BK_COLOR RGB(71, 108, 108) + +/* minimum/maximum font size (in points - 1/72 inch) */ +#define NHFONT_DEFAULT_SIZE 9 +#define NHFONT_STATUS_DEFAULT_SIZE 6 +#define NHFONT_SIZE_MIN 3 +#define NHFONT_SIZE_MAX 20 + +typedef struct mswin_nhwindow_data { + HWND win; + int type; + int dead; +} MSNHWinData, *PMSNHWinData; + +/* global application data - alailable thour GetNHApp() */ +typedef struct mswin_nhwindow_app { + HINSTANCE hApp; /* hInstance from WinMain */ + int nCmdShow; /* main window mode flag */ + HWND hMainWnd; /* main window handle */ + HACCEL hAccelTable; /* accelerator table */ + HWND hPopupWnd; /* active dialog window (nethack menu, text, etc) */ + HWND hMenuBar; /* menu bar */ + + MSNHWinData windowlist[MAXWINDOWS]; /* nethack windows array */ + + HBITMAP bmpTiles; /* nethack tiles */ + HBITMAP bmpPetMark; /* pet mark Bitmap */ + HBITMAP bmpMapTiles; /* alternative map tiles */ + int mapTile_X; /* alt. tiles width */ + int mapTile_Y; /* alt. tiles height */ + int mapTilesPerLine; /* number of tile per row in the bitmap */ + + boolean bNoHScroll; /* disable cliparound for horizontal grid (map) */ + boolean bNoVScroll; /* disable cliparound for vertical grid (map) */ + + int mapDisplayModeSave; /* saved map display mode */ + + int bCmdPad; /* command pad - on-screen keyboard */ + HWND hCmdWnd; /* handle of on-screen keyboard window */ + + /* options */ + boolean bWrapText; /* format text to fit the window */ + boolean bFullScreen;/* run nethack in full-screen mode */ + boolean bHideScrollBars; /* hide scroll bars */ + boolean bUseSIP; /* use SIP (built-in software keyboard) for menus (PocketPC only) */ +} NHWinApp, *PNHWinApp; +extern PNHWinApp GetNHApp(); + +#define E extern + +E struct window_procs mswin_procs; + +#undef E + +/* Some prototypes */ +void mswin_init_nhwindows(int* argc, char** argv); +void mswin_player_selection(void); +void mswin_askname(void); +void mswin_get_nh_event(void); +void mswin_exit_nhwindows(const char *); +void mswin_suspend_nhwindows(const char *); +void mswin_resume_nhwindows(void); +winid mswin_create_nhwindow(int type); +void mswin_clear_nhwindow(winid wid); +void mswin_display_nhwindow(winid wid, BOOLEAN_P block); +void mswin_destroy_nhwindow(winid wid); +void mswin_curs(winid wid, int x, int y); +void mswin_putstr(winid wid, int attr, const char *text); +void mswin_putstr_ex(winid wid, int attr, const char *text, boolean append); +void mswin_display_file(const char *filename,BOOLEAN_P must_exist); +void mswin_start_menu(winid wid); +void mswin_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel); +void mswin_end_menu(winid wid, const char *prompt); +int mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected); +void mswin_update_inventory(void); +void mswin_mark_synch(void); +void mswin_wait_synch(void); +void mswin_cliparound(int x, int y); +void mswin_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph); +void mswin_raw_print(const char *str); +void mswin_raw_print_bold(const char *str); +int mswin_nhgetch(void); +int mswin_nh_poskey(int *x, int *y, int *mod); +void mswin_nhbell(void); +int mswin_doprev_message(void); +char mswin_yn_function(const char *question, const char *choices, + CHAR_P def); +void mswin_getlin(const char *question, char *input); +int mswin_get_ext_cmd(void); +void mswin_number_pad(int state); +void mswin_delay_output(void); +void mswin_change_color(void); +char *mswin_get_color_string(void); +void mswin_start_screen(void); +void mswin_end_screen(void); +void mswin_outrip(winid wid, int how); +void mswin_preference_update(const char *pref); + +/* helper function */ +HWND mswin_hwnd_from_winid(winid wid); +winid mswin_winid_from_type(int type); +winid mswin_winid_from_handle(HWND hWnd); +void mswin_window_mark_dead(winid wid); +void bail(const char *mesg); +void nhapply_image_transparent( + HDC hDC, int x, int y, int width, int height, + HDC sourceDC, int s_x, int s_y, int s_width, int s_height, + COLORREF cTransparent +); +void mswin_popup_display(HWND popup, int* done_indicator); +void mswin_popup_destroy(HWND popup); + +#if defined(WIN_CE_SMARTPHONE) +void NHSPhoneDialogSetup(HWND hDlg, BOOL is_edit, BOOL is_fullscreen); +#endif + +void mswin_read_reg(void); +void mswin_destroy_reg(void); +void mswin_write_reg(void); + +void mswin_set_fullscreen(BOOL is_fullscreen); +#endif /* WINmswin_H */ diff --git a/sys/wince/winhack.c b/sys/wince/winhack.c new file mode 100644 index 0000000..6a768ac --- /dev/null +++ b/sys/wince/winhack.c @@ -0,0 +1,362 @@ +/* Copyright (C) 2001 by Alex Kompel */ +// winhack.cpp : Defines the entry point for the application. +// + +#include "winMS.h" +#include "hack.h" +#include "dlb.h" +#include "mhmain.h" +#include "mhmap.h" + +#ifdef OVL0 +#define SHARED_DCL +#else +#define SHARED_DCL extern +#endif + +SHARED_DCL char orgdir[PATHLEN]; /* also used in pcsys.c, amidos.c */ + +extern void FDECL(nethack_exit,(int)); +static TCHAR* _get_cmd_arg(TCHAR* pCmdLine); + +// Global Variables: +NHWinApp _nethack_app; + +// Foward declarations of functions included in this code module: +BOOL InitInstance(HINSTANCE, int); + +static void win_hack_init(int, char **); +static void __cdecl mswin_moveloop(void *); +static BOOL setMapTiles(const char* fname); + +extern void FDECL(pcmain, (int,char **)); + +#define MAX_CMDLINE_PARAM 255 + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, + int nCmdShow) +{ + INITCOMMONCONTROLSEX InitCtrls; + HWND nethackWnd; + int argc; + char* argv[MAX_CMDLINE_PARAM]; + size_t len; + TCHAR* p; + TCHAR wbuf[NHSTR_BUFSIZE]; + char buf[NHSTR_BUFSIZE]; + + /* ensure that we don't access violate on a panic() */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + + /* init applicatio structure */ + _nethack_app.hApp = hInstance; + _nethack_app.nCmdShow = nCmdShow; + _nethack_app.hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_WINHACK); + _nethack_app.hMainWnd = NULL; + _nethack_app.hPopupWnd = NULL; + _nethack_app.hMenuBar = NULL; + _nethack_app.bmpTiles = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TILES)); + if( _nethack_app.bmpTiles==NULL ) panic("cannot load tiles bitmap"); + _nethack_app.bmpPetMark = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_PETMARK)); + if( _nethack_app.bmpPetMark==NULL ) panic("cannot load pet mark bitmap"); + _nethack_app.bmpMapTiles = _nethack_app.bmpTiles; + _nethack_app.mapTile_X = TILE_X; + _nethack_app.mapTile_Y = TILE_Y; + _nethack_app.mapTilesPerLine = TILES_PER_LINE; + _nethack_app.bNoHScroll = FALSE; + _nethack_app.bNoVScroll = FALSE; + +#if defined(WIN_CE_PS2xx) || defined(WIN_CE_POCKETPC) || defined(WIN_CE_SMARTPHONE) + _nethack_app.bCmdPad = TRUE; +#else + _nethack_app.bCmdPad = FALSE; +#endif + + _nethack_app.bWrapText = TRUE; + _nethack_app.bFullScreen = TRUE; + +#if defined(WIN_CE_SMARTPHONE) + _nethack_app.bHideScrollBars = TRUE; +#else + _nethack_app.bHideScrollBars = FALSE; +#endif + + _nethack_app.bUseSIP = TRUE; + + // check for running nethack programs + nethackWnd = FindWindow(szMainWindowClass, NULL); + if( nethackWnd ) { + // bring on top + SetForegroundWindow(nethackWnd); + return FALSE; + } + + // init controls + ZeroMemory(&InitCtrls, sizeof(InitCtrls)); + InitCtrls.dwSize = sizeof(InitCtrls); + InitCtrls.dwICC = ICC_LISTVIEW_CLASSES; + if( !InitCommonControlsEx(&InitCtrls) ) { + MessageBox(NULL, TEXT("Cannot init common controls"), TEXT("ERROR"), MB_OK | MB_ICONSTOP); + return FALSE; + } + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + /* get command line parameters */ + p = _get_cmd_arg( +#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO) + lpCmdLine +#else + GetCommandLine() +#endif + ); + for( argc = 1; p && argc0 ) { + argv[argc] = _strdup( NH_W2A(p, buf, BUFSZ) ); + } else { + argv[argc] = ""; + } + p = _get_cmd_arg(NULL); + } + GetModuleFileName(NULL, wbuf, BUFSZ); + argv[0] = _strdup(NH_W2A(wbuf, buf, BUFSZ)); + + pcmain(argc,argv); + + moveloop(); + + return 0; +} + +// +// FUNCTION: InitInstance(HANDLE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + return TRUE; +} + +PNHWinApp GetNHApp() +{ + return &_nethack_app; +} + +static int +eraseoldlocks() +{ + register int i; + + /* cannot use maxledgerno() here, because we need to find a lock name + * before starting everything (including the dungeon initialization + * that sets astral_level, needed for maxledgerno()) up + */ + for(i = 1; i <= MAXDUNGEON*MAXLEVEL + 1; i++) { + /* try to remove all */ + set_levelfile_name(lock, i); + (void) unlink(fqname(lock, LEVELPREFIX, 0)); + } + set_levelfile_name(lock, 0); + if(unlink(fqname(lock, LEVELPREFIX, 0))) + return 0; /* cannot remove it */ + return(1); /* success! */ +} + +void +getlock() +{ + const char *fq_lock; + char tbuf[BUFSZ]; + TCHAR wbuf[BUFSZ]; + HANDLE f; + int fd; + int choice; + + /* regularize(lock); */ /* already done in pcmain */ + Sprintf(tbuf, "%s", fqname(lock, LEVELPREFIX, 0)); + set_levelfile_name(lock, 0); + fq_lock = fqname(lock, LEVELPREFIX, 1); + + f = CreateFile( + NH_A2W(fq_lock, wbuf, BUFSZ), + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if( f==INVALID_HANDLE_VALUE ) { + if(GetLastError()==ERROR_FILE_NOT_FOUND) goto gotlock; /* no such file */ + error("Cannot open %s", fq_lock); + } + + CloseHandle(f); + + /* prompt user that the game alredy exist */ + choice = MessageBox( + GetNHApp()->hMainWnd, + TEXT("There are files from a game in progress under your name. Recover?"), + TEXT("Nethack"), + MB_YESNO | MB_DEFBUTTON1 + ); + switch(choice) { + case IDYES: + if(recover_savefile()) { + goto gotlock; + } else { + error("Couldn't recover old game."); + } + break; + + case IDNO: + unlock_file(HLOCK); + error("%s", "Cannot start a new game."); + break; + }; + +gotlock: + fd = creat(fq_lock, FCMASK); + if(fd == -1) { + error("cannot creat lock file (%s.)", fq_lock); + } else { + if(write(fd, (char *) &hackpid, sizeof(hackpid)) + != sizeof(hackpid)){ + error("cannot write lock (%s)", fq_lock); + } + if(close(fd) == -1) { + error("cannot close lock (%s)", fq_lock); + } + } +} + +/* misc functions */ +void +error VA_DECL(const char *,s) + TCHAR wbuf[1024]; + char buf[1024]; + DWORD last_error = GetLastError(); + + VA_START(s); + VA_INIT(s, const char *); + /* error() may get called before tty is initialized */ + if (iflags.window_inited) end_screen(); + + vsprintf(buf, s, VA_ARGS); + NH_A2W(buf, wbuf, sizeof(wbuf)/sizeof(wbuf[0])); + if( last_error>0 ) { + LPVOID lpMsgBuf; + if( FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + last_error, + 0, // Default language + (LPTSTR) &lpMsgBuf, + 0, + NULL + ) + ) + { + + _tcsncat(wbuf, TEXT("\nSystem Error: "), sizeof(wbuf)/sizeof(wbuf[0]) - _tcslen(wbuf) ); + _tcsncat(wbuf, lpMsgBuf, sizeof(wbuf)/sizeof(wbuf[0]) - _tcslen(wbuf) ); + + // Free the buffer. + LocalFree( lpMsgBuf ); + } + } + VA_END(); + + MessageBox( NULL, wbuf, TEXT("Error"), MB_OK | MB_ICONERROR ); + + exit(EXIT_FAILURE); +} + +TCHAR* _get_cmd_arg(TCHAR* pCmdLine) +{ + static TCHAR* pArgs = NULL; + TCHAR *pRetArg; + BOOL bQuoted; + + if( !pCmdLine && !pArgs ) return NULL; + if( !pArgs ) pArgs = pCmdLine; + + /* skip whitespace */ + for(pRetArg = pArgs; *pRetArg && _istspace(*pRetArg); pRetArg = CharNext(pRetArg)); + if( !*pRetArg ) { + pArgs = NULL; + return NULL; + } + + /* check for quote */ + if( *pRetArg==TEXT('"') ) { + bQuoted = TRUE; + pRetArg = CharNext(pRetArg); + pArgs = _tcschr(pRetArg, TEXT('"')); + } else { + /* skip to whitespace */ + for(pArgs = pRetArg; *pArgs && !_istspace(*pArgs); pArgs = CharNext(pArgs)); + } + + if( pArgs && *pArgs ) { + TCHAR* p; + p = pArgs; + pArgs = CharNext(pArgs); + *p = (TCHAR)0; + } else { + pArgs = NULL; + } + + return pRetArg; +} + +/* + * Strip out troublesome file system characters. + */ + +void +nt_regularize(s) /* normalize file name */ +register char *s; +{ + register unsigned char *lp; + + for (lp = s; *lp; lp++) + if ( *lp == '?' || *lp == '"' || *lp == '\\' || + *lp == '/' || *lp == '>' || *lp == '<' || + *lp == '*' || *lp == '|' || *lp == ':' || (*lp > 127)) + *lp = '_'; +} + +void win32_abort() +{ + +#ifdef WIZARD + if (wizard) + DebugBreak(); +#endif + abort(); +} + +void +append_port_id(buf) +char *buf; +{ + char *portstr = PORT_CE_PLATFORM " " PORT_CE_CPU; + Sprintf(eos(buf), " %s", portstr); +} + diff --git a/sys/wince/winhack.rc b/sys/wince/winhack.rc new file mode 100644 index 0000000..0240fc8 --- /dev/null +++ b/sys/wince/winhack.rc @@ -0,0 +1,370 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "newres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_WINHACK ICON DISCARDABLE "..\\..\\wince\\NETHACK.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Menubar +// + +IDC_WINHACK MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "&Save", IDM_SAVE + MENUITEM SEPARATOR + MENUITEM "&Quit", IDM_EXIT + END + POPUP "Map" + BEGIN + MENUITEM "&0 - Use Tiles", IDM_MAP_TILES + MENUITEM "&1 - ASCII (4x6)", IDM_MAP_ASCII4X6 + MENUITEM "&2 - ASCII (6x8)", IDM_MAP_ASCII6X8 + MENUITEM "&3 - ASCII (8x8)", IDM_MAP_ASCII8X8 + MENUITEM "&4 - ASCII (16x8)", IDM_MAP_ASCII16X8 + MENUITEM "&5 - ASCII (7x12)", IDM_MAP_ASCII7X12 + MENUITEM "&6 - ASCII (8x12)", IDM_MAP_ASCII8X12 + MENUITEM "&7 - ASCII (16x12)", IDM_MAP_ASCII16X12 + MENUITEM "&8 - ASCII (12x16)", IDM_MAP_ASCII12X16 + MENUITEM "&9 - ASCII (10x18)", IDM_MAP_ASCII10X18 + MENUITEM SEPARATOR + MENUITEM "&Fit To Screen", IDM_MAP_FIT_TO_SCREEN + END + POPUP "View" + BEGIN + MENUITEM "&Keypad", IDM_VIEW_KEYPAD + MENUITEM "&Options", IDM_VIEW_OPTIONS + END + POPUP "Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + MENUITEM "&Long description of the game", IDM_HELP_LONG + MENUITEM "List of &commands", IDM_HELP_COMMANDS + MENUITEM "&History of NetHack", IDM_HELP_HISTORY + MENUITEM "&Info on a character", IDM_HELP_INFO_CHAR + MENUITEM "Info on what a given &key does", IDM_HELP_INFO_KEY + MENUITEM "List of game &options", IDM_HELP_OPTIONS + MENUITEM "&Longer list of game options", IDM_HELP_OPTIONS_LONG + MENUITEM "List of e&xtended commands", IDM_HELP_EXTCMD + MENUITEM "The &NetHack license", IDM_HELP_LICENSE + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_WINHACK ACCELERATORS MOVEABLE PURE +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 123, 87 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "System" +BEGIN + LTEXT "NetHack",IDC_ABOUT_VERSION,0,2,120,15,SS_NOPREFIX + LTEXT "Copyright",IDC_ABOUT_COPYRIGHT,0,20,120,50 + DEFPUSHBUTTON "OK",IDOK,45,75,30,11,WS_GROUP +END + +IDD_MENU DIALOG DISCARDABLE 0, 0, 109, 153 +STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,5,130,50,14,BS_NOTIFY + PUSHBUTTON "Cancel",IDCANCEL,55,130,50,14,BS_NOTIFY + CONTROL "List1",IDC_MENU_LIST,"SysListView32",WS_BORDER | + WS_TABSTOP,5,5,100,60 + EDITTEXT IDC_MENU_TEXT,5,70,100,55,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL +END + +IDD_GETLIN DIALOG DISCARDABLE 0, 0, 115, 30 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Question?" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,0,16,55,14 + PUSHBUTTON "Cancel",IDCANCEL,55,16,60,14 + EDITTEXT IDC_GETLIN_EDIT,0,0,115,15,ES_AUTOHSCROLL +END + +IDD_PLAYER_SELECTOR DIALOG DISCARDABLE 0, 0, 105, 124 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "What are you?" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_PLSEL_NAME,35,0,70,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Name:",IDC_STATIC,0,0,25,10 + CONTROL "Random",IDC_PLSEL_ROLE_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,20,40,10 + COMBOBOX IDC_PLSEL_ROLE_LIST,50,20,50,50,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_RACE_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,45,40,10 + COMBOBOX IDC_PLSEL_RACE_LIST,50,45,50,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_GENDER_RANDOM,"Button", + BS_AUTOCHECKBOX | BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,70, + 40,10 + COMBOBOX IDC_PLSEL_GENDER_LIST,50,70,50,40,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_ALIGN_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,95,40,10 + COMBOBOX IDC_PLSEL_ALIGN_LIST,50,95,50,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + GROUPBOX "Role",IDC_STATIC,0,10,105,25,WS_GROUP + GROUPBOX "Race",IDC_STATIC,0,35,105,25 + GROUPBOX "Gender",IDC_STATIC,0,60,105,25 + GROUPBOX "Alignment",IDC_STATIC,0,85,105,25 + DEFPUSHBUTTON "Play",IDOK,0,110,55,14 + PUSHBUTTON "Quit",IDCANCEL,55,110,50,14 +END + +IDD_NHTEXT DIALOG DISCARDABLE 0, 0, 83, 97 +STYLE WS_CHILD | WS_BORDER +FONT 8, "System" +BEGIN + DEFPUSHBUTTON "OK",IDOK,15,80,50,14 + EDITTEXT IDC_TEXT_CONTROL,5,0,70,75,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL +END + +IDD_EXTCMD DIALOG DISCARDABLE 0, 0, 109, 114 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Extended Commands" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,75,5,30,14 + PUSHBUTTON "Cancel",IDCANCEL,75,20,30,14 + LISTBOX IDC_EXTCMD_LIST,5,5,65,105,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""newres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TILES BITMAP DISCARDABLE "..\\..\\wince\\tiles.bmp" +IDB_MENU_SEL BITMAP DISCARDABLE "..\\..\\wince\\mnsel.bmp" +IDB_MENU_UNSEL BITMAP DISCARDABLE "..\\..\\wince\\mnunsel.bmp" +IDB_PETMARK BITMAP DISCARDABLE "..\\..\\wince\\petmark.bmp" +IDB_MENU_SEL_COUNT BITMAP DISCARDABLE "..\\..\\wince\\mnselcnt.bmp" +IDB_KEYPAD BITMAP DISCARDABLE "..\\..\\wince\\keypad.bmp" +IDB_MENUBAR BITMAP DISCARDABLE "..\\..\\wince\\menubar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_PLAYER_SELECTOR, DIALOG + BEGIN + RIGHTMARGIN, 98 + BOTTOMMARGIN, 117 + END + + IDD_NHTEXT, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 76 + TOPMARGIN, 7 + BOTTOMMARGIN, 94 + END + + IDD_EXTCMD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 102 + TOPMARGIN, 7 + BOTTOMMARGIN, 107 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +IDC_WINHACK SHMENUBAR DISCARDABLE +BEGIN + IDC_WINHACK, 6, + I_IMAGENONE, ID_FILE, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_FILE, 0, 0, + I_IMAGENONE, ID_MAP, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_MAP, 0, 1, + I_IMAGENONE, ID_VIEW, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_VIEW, 0, 2, + I_IMAGENONE, ID_HELP, TBSTATE_ENABLED, + TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_HELP, 0, 3, + 0, IDM_MAP_FIT_TO_SCREEN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, + IDM_MAP_FIT_TO_SCREEN, NOMENU, + 1, IDM_VIEW_KEYPAD, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, 0, + IDM_VIEW_KEYPAD, NOMENU, +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,4,2,0 + PRODUCTVERSION 3,4,2,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x9L +#else + FILEFLAGS 0x8L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NetHack 3.4.3 for Windows CE\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "nethackm\0" + VALUE "FileVersion", "3, 4, 3, 0\0" + VALUE "InternalName", "nethackm\0" + VALUE "LegalCopyright", "Copyright © 2003\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "nethackm.exe\0" + VALUE "PrivateBuild", "031014\0" + VALUE "ProductName", "NetHack\0" + VALUE "ProductVersion", "3, 4, 3, 0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "NetHack" + IDC_WINHACK "NETHACK" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDM_MAP_FIT_TO_SCREEN "Fit to Screen" + IDS_CAP_FILE "File" + IDS_CAP_HELP "Help" + IDS_CAP_MAP "Map" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDM_VIEW_KEYPAD "Show/Hide keypad." + IDS_CAP_VIEW "View" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDOK "Done" + IDCANCEL "Cancel" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/sys/wince/winhcksp.rc b/sys/wince/winhcksp.rc new file mode 100644 index 0000000..39f3dbb --- /dev/null +++ b/sys/wince/winhcksp.rc @@ -0,0 +1,346 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "newres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_WINHACK ICON DISCARDABLE "..\\..\\wince\\NETHACK.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Menubar +// + +IDC_WINHACK MENU DISCARDABLE +BEGIN + MENUITEM "Entire Map", IDM_MAP_FIT_TO_SCREEN + POPUP "Menu" + BEGIN + MENUITEM "Options", IDM_VIEW_OPTIONS + MENUITEM "Keypad", IDM_VIEW_KEYPAD + MENUITEM SEPARATOR + MENUITEM "ASCII", IDM_MAP_ASCII8X8 + MENUITEM "Use Tiles", IDM_MAP_TILES + MENUITEM SEPARATOR + MENUITEM "Help", IDM_HELP_MENU + MENUITEM "Save", IDM_SAVE + MENUITEM "Quit", IDM_EXIT + END +END + +IDC_SPHONE_DIALOGBAR MENU DISCARDABLE +BEGIN + MENUITEM "Done", IDOK + MENUITEM "Cancel", IDCANCEL +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_WINHACK ACCELERATORS MOVEABLE PURE +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 123, 87 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "System" +BEGIN + LTEXT "NetHack",IDC_ABOUT_VERSION,0,2,120,15,SS_NOPREFIX + LTEXT "Copyright",IDC_ABOUT_COPYRIGHT,0,20,120,50 + DEFPUSHBUTTON "OK",IDOK,45,75,30,11,WS_GROUP +END + +IDD_MENU DIALOG DISCARDABLE 0, 0, 109, 131 +STYLE DS_SETFOREGROUND | DS_CONTROL | WS_POPUP | WS_CLIPSIBLINGS | WS_BORDER +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "List1",IDC_MENU_LIST,"SysListView32",WS_BORDER | + WS_TABSTOP,5,5,100,60 + EDITTEXT IDC_MENU_TEXT,5,70,100,55,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL +END + +IDD_NHTEXT DIALOG DISCARDABLE 0, 0, 83, 83 +STYLE WS_POPUP | WS_BORDER +FONT 8, "System" +BEGIN + EDITTEXT IDC_TEXT_CONTROL,5,0,70,75,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_HSCROLL +END + +IDD_EXTCMD DIALOG DISCARDABLE 0, 0, 88, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Extended Commands" +FONT 8, "MS Sans Serif" +BEGIN + LISTBOX IDC_EXTCMD_LIST,7,5,75,69,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_GETLIN DIALOG DISCARDABLE 0, 0, 115, 16 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Question?" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_GETLIN_EDIT,0,0,115,15,ES_AUTOHSCROLL +END + +IDD_PLAYER_SELECTOR DIALOG DISCARDABLE 0, 0, 105, 124 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "What are you?" +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_PLSEL_NAME,35,0,70,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Name:",IDC_STATIC,0,0,25,10 + CONTROL "Random",IDC_PLSEL_ROLE_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,20,40,10 + COMBOBOX IDC_PLSEL_ROLE_LIST,50,20,50,50,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_RACE_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,45,40,10 + COMBOBOX IDC_PLSEL_RACE_LIST,50,45,50,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_GENDER_RANDOM,"Button", + BS_AUTOCHECKBOX | BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,70, + 40,10 + COMBOBOX IDC_PLSEL_GENDER_LIST,50,70,50,40,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + CONTROL "Random",IDC_PLSEL_ALIGN_RANDOM,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_GROUP | WS_TABSTOP,5,95,40,10 + COMBOBOX IDC_PLSEL_ALIGN_LIST,50,95,50,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_GROUP | WS_TABSTOP + GROUPBOX "Role",IDC_STATIC,0,10,105,25,WS_GROUP + GROUPBOX "Race",IDC_STATIC,0,35,105,25 + GROUPBOX "Gender",IDC_STATIC,0,60,105,25 + GROUPBOX "Alignment",IDC_STATIC,0,85,105,25 + DEFPUSHBUTTON "Play",IDOK,0,110,55,14 + PUSHBUTTON "Quit",IDCANCEL,55,110,50,14 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""newres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TILES BITMAP DISCARDABLE "..\\..\\wince\\tiles.bmp" +IDB_MENU_SEL BITMAP DISCARDABLE "..\\..\\wince\\mnsel.bmp" +IDB_MENU_UNSEL BITMAP DISCARDABLE "..\\..\\wince\\mnunsel.bmp" +IDB_PETMARK BITMAP DISCARDABLE "..\\..\\wince\\petmark.bmp" +IDB_MENU_SEL_COUNT BITMAP DISCARDABLE "..\\..\\wince\\mnselcnt.bmp" +IDB_KEYPAD BITMAP DISCARDABLE "..\\..\\wince\\keypad.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_NHTEXT, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 76 + TOPMARGIN, 7 + END + + IDD_EXTCMD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 82 + TOPMARGIN, 7 + BOTTOMMARGIN, 75 + END + + IDD_PLAYER_SELECTOR, DIALOG + BEGIN + RIGHTMARGIN, 98 + BOTTOMMARGIN, 117 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +IDC_WINHACK SHMENUBAR DISCARDABLE +BEGIN + IDC_WINHACK, 2, + I_IMAGENONE, IDM_MAP_FIT_TO_SCREEN, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDM_MAP_FIT_TO_SCREEN, 0, NOMENU, + I_IMAGENONE, ID_VIEW, TBSTATE_ENABLED, TBSTYLE_DROPDOWN | TBSTYLE_AUTOSIZE, IDS_CAP_VIEW, 0, 1, +END + +IDC_SPHONE_DIALOGBAR SHMENUBAR DISCARDABLE +BEGIN + IDC_SPHONE_DIALOGBAR, 2, + I_IMAGENONE, IDOK, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDOK, 0, NOMENU, + I_IMAGENONE, IDCANCEL, TBSTATE_ENABLED, TBSTYLE_AUTOSIZE, IDCANCEL, 0, + NOMENU, +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,4,2,0 + PRODUCTVERSION 3,4,2,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x9L +#else + FILEFLAGS 0x8L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "NetHack 3.4.3 for Smartphone 2002\0" + VALUE "CompanyName", " \0" + VALUE "FileDescription", "nethackm\0" + VALUE "FileVersion", "3, 4, 3, 0\0" + VALUE "InternalName", "nethackm\0" + VALUE "LegalCopyright", "Copyright © 2003\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "nethackm.exe\0" + VALUE "PrivateBuild", "031014\0" + VALUE "ProductName", "NetHack For Smartphone\0" + VALUE "ProductVersion", "3, 4, 3, 0\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "NetHack" + IDC_WINHACK "NETHACK" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDM_MAP_FIT_TO_SCREEN "Entire Map" + IDS_CAP_FILE "File" + IDS_CAP_HELP "Help" + IDS_CAP_MAP "Map" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDM_VIEW_KEYPAD "Show/Hide keypad." + IDS_CAP_VIEW "Menu" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDOK "Done" + IDCANCEL "Cancel" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_CAP_ENTIREMAP "Entire Map" + IDS_CAP_NORMALMAP "Normal Map" + IDM_HELP_MENU "Help Menu" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/sys/wince/winmain.c b/sys/wince/winmain.c new file mode 100644 index 0000000..0bb3d67 --- /dev/null +++ b/sys/wince/winmain.c @@ -0,0 +1,110 @@ +// winmain.cpp : Defines the entry point for the application. + +#include "winMS.h" +#include + +#define MAX_CMDLINE_PARAM 255 + +extern int FDECL (main, (int,char **)); +static TCHAR* _get_cmd_arg(TCHAR* pCmdLine); + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPWSTR lpCmdLine, + int nCmdShow) +{ + int argc; + char* argv[MAX_CMDLINE_PARAM]; + size_t len; + TCHAR* p; + TCHAR wbuf[NHSTR_BUFSIZE]; + char buf[NHSTR_BUFSIZE]; + + /* get command line parameters */ + p = _get_cmd_arg( +#if defined(WIN_CE_PS2xx) || defined(WIN32_PLATFORM_HPCPRO) + lpCmdLine +#else + GetCommandLine() +#endif + ); + for( argc = 1; p && argc0 ) { + argv[argc] = _strdup( NH_W2A(p, buf, BUFSZ) ); + } else { + argv[argc] = ""; + } + p = _get_cmd_arg(NULL); + } + GetModuleFileName(NULL, wbuf, BUFSZ); + argv[0] = _strdup(NH_W2A(wbuf, buf, BUFSZ)); + + main(argc, argv); + + return 0; +} + +TCHAR* _get_cmd_arg(TCHAR* pCmdLine) +{ + static TCHAR* pArgs = NULL; + TCHAR *pRetArg; + BOOL bQuoted; + + if( !pCmdLine && !pArgs ) return NULL; + if( !pArgs ) pArgs = pCmdLine; + + /* skip whitespace */ + for(pRetArg = pArgs; *pRetArg && _istspace(*pRetArg); pRetArg = CharNext(pRetArg)); + if( !*pRetArg ) { + pArgs = NULL; + return NULL; + } + + /* check for quote */ + if( *pRetArg==TEXT('"') ) { + bQuoted = TRUE; + pRetArg = CharNext(pRetArg); + pArgs = _tcschr(pRetArg, TEXT('"')); + } else { + /* skip to whitespace */ + for(pArgs = pRetArg; *pArgs && !_istspace(*pArgs); pArgs = CharNext(pArgs)); + } + + if( pArgs && *pArgs ) { + TCHAR* p; + p = pArgs; + pArgs = CharNext(pArgs); + *p = (TCHAR)0; + } else { + pArgs = NULL; + } + + return pRetArg; +} + +#ifndef STRNCMPI +char +lowc(c) /* force 'c' into lowercase */ + char c; +{ + return((char)(('A' <= c && c <= 'Z') ? (c | 040) : c)); +} + +int +strncmpi(s1, s2, n) /* case insensitive counted string comparison */ + register const char *s1, *s2; + register int n; /*(should probably be size_t, which is usually unsigned)*/ +{ /*{ aka strncasecmp }*/ + register char t1, t2; + + while (n--) { + if (!*s2) return (*s1 != 0); /* s1 >= s2 */ + else if (!*s1) return -1; /* s1 < s2 */ + t1 = lowc(*s1++); + t2 = lowc(*s2++); + if (t1 != t2) return (t1 > t2) ? 1 : -1; + } + return 0; /* s1 == s2 */ +} +#endif /* STRNCMPI */ diff --git a/sys/winnt/Install.nt b/sys/winnt/Install.nt new file mode 100644 index 0000000..983f056 --- /dev/null +++ b/sys/winnt/Install.nt @@ -0,0 +1,450 @@ + Copyright (c) NetHack Development Team 1990-2002 + NetHack may be freely redistributed. See license for details. + ============================================================== + Instructions for compiling and installing + NetHack 3.4 on a Windows 9x, NT, 2000, or XP system + ============================================================== + Last revision: $Date: 2003/10/14 01:31:25 $ + +Credit for the porting of NetHack to the Win32 Console Subsystem goes to +the NT Porting Team started by Michael Allison. + +Credit for the Win32 Graphical version of NetHack (aka "NetHack for +Windows" or NetHackW) goes to Alex Kompel who initially developed and +contributed the port. + +The PC Windows porting team consisting of Michael Allison, David Cohrs, +Alex Kompel, Dion Nicolaas, Yitzhak Sapir, and Janet Walz maintained the +tty and graphical win32 versions of NetHack 3.4.3. + +You can build either the TTY version of NetHack or the Windows Graphical +version. In either case you can use one of the following build +environments: + + o A copy of Microsoft Visual C V6.0 SP3 or later. Things may work with + an earlier version of the compiler, but the current code has not + been tested with an earlier version. + + OR + + o A copy of Borland C 5.5.1 command line tools. Borland has made a + version of its command line tools available for download after + registration at: + http://www.borland.com/bcppbuilder/freecompiler/ + + OR + + o A copy of MinGW 2.0. MinGW is a collection of header files and import + libraries with which native Windows32 programs can be made; the + MinGW 2.0 distribution contains the GNU Compiler Collection. + You can download MinGW at + http://www.mingw.org/ + Earlier versions of MinGW will not allow you to build the Windows + Graphical version. + +In addition to the makefiles that allow you to build NetHack from the +command line, there is also a set of project files and a workspace file +that allow you to build the Windows Graphical version from Microsoft +Visual C's IDE (Integrated Development Environment.) + + +FIRST STEP + +The first step in building either version of NetHack is to execute +sys\winnt\nhsetup.bat. + +From the command prompt: + cd sys\winnt + nhsetup + +From a Windows explorer window: + double-click on nhsetup.bat + +A "binary" directory will be created off the top of the NetHack source +tree to house the completed build. + +A build subdirectory will also be created off the top of the NetHack +source tree, and many files appropriate for a graphical build will be +moved there. + +If you wish to build from the command line, proceed to "BUILDING FROM +THE COMMAND LINE." +If you wish to build using Visual C's IDE, proceed now to "BUILDING +USING VISUAL C'S IDE." + + +BUILDING FROM THE COMMAND LINE + +You can built two different versions of NetHack for Win32 from the +command line: + A tty port utilizing the Win32 Console I/O subsystem, Console + NetHack; + A Win32 native port built on the Windows API, Graphical NetHack or + NetHackW. + +The executable for Console NetHack will be named NetHack.exe. The +executable for Graphical NetHack will be named NetHackW.exe. You can opt +to build both; they will be able to use the same datafiles, save files +and bones files. + +I. Dispelling the Myths: + + Compiling NetHack for Win32 is not as easy as it sounds, nor as hard + as it looks, however it will behoove you to read this entire section + through before beginning the task. + + We have provided a Makefile for each of the following compilers: + + o Microsoft Visual C++ V6.0 SP3 or greater + o Borland C 5.5.1 + o MinGW 2.0 (with GCC 3.2) + + The Microsoft Visual C Makefile was created for use with MS NMAKE + which is provided with the Microsoft compiler. The supplied Makefile + may work with earlier versions of the Microsoft 32-bit compiler, but + that has not been tested. + + The Borland C Makefile was created for use with Borland MAKE which + is provided with the Borland compiler. + + The GCC Makefile was created for use with GNU Make version 3.79.1, + which comes with the MinGW package. + + You may find it useful to obtain copies of lex (flex) and yacc + (bison, or byacc). While not strictly necessary to compile nethack, + they are required should you desire to make any changes to the level + and dungeon compilers. + +II. To compile your copy of NetHack on a Windows NT/2000/XP machine: + +Setting Up + +1. It almost goes without saying that you should make sure that your + tools are set up and running correctly. That includes ensuring that + all the necessary environment variables for the compiler environment + are set correctly. (Examples: For the Microsoft compiler by + executing vcvars32.bat, which is probably in the bin directory of + your compilers directory tree. For the Borland Makefile, you can + simply invoke the Make utility from the Makefile's directory (For + the standard Borland compiler installation you can just use the + explicit path "c:\borland\bcc55\bin\make /f Makefile.bcc". For the + GCC Makefile, add \bin to your path, where is your + MinGW root directory.) + +2. Make sure all the NetHack files are in the appropriate directory + structure. You should have a main directory with subdirectories + dat, doc, include, src, sys\share, sys\winnt, util, and binary (The + "binary" directory was created by nhsetup.bat earlier if you + followed the steps appropriately). + + For Console NetHack you need win\tty in addition to these; for + Graphical NetHack you need win\win32 in addition to these. + + Other subdirectories may also be included in your distribution, but + they are not necessary for building the TTY version for the Win32 + console subsystem. You can delete them to save space. + + Required Directories for a Win32 Console NetHack: + + top + | + ----------------------------------------------------/ /----- + | | | | | | | | + util dat doc include src sys win binary + | | + ------ ----- + | | | + share winnt tty + + + Required Directories for a Win32 Graphical NetHack: + + top + | + ----------------------------------------------------/ /----- + | | | | | | | | + util dat doc include src sys win binary + | | + ------ ----- + | | | + share winnt win32 + + Check the file "Files" in your top level directory for an exact + listing of what file is in which directory. In order for the + Makefiles to work, all the source files must be in the proper + locations. + + If you downloaded or ftp'd the sources from a UNIX system, the lines + will probably end in UNIX-style newlines, instead of the carriage + return and line feed pairs used by Windows. Some programs have + trouble with them, so you may need to convert them. The compiler + should not have any problems with them however. + +3. Now go to the include subdirectory to check a couple of the header + files there. Things *should* work as they are, but since you have + probably set up your system in some sort of custom configuration it + doesn't hurt to check out the following: + + First check config.h according to the comments to match your system + and desired set of features. Mostly you need to check section 4 and + 5. + + You may include all or as few of the special game features as you + wish (they are located last in the file). + +4. Edit your Makefile. + + For building Console NetHack, ensure that GRAPHICAL is set to "N", + or commented out. For building Graphical NetHack, set GRAPHICAL to + "Y". + + Optional step: + If you elected not to use the high-quality BSD random number + routines by commenting out RANDOM in ntconf.h, comment out (or + set equal to nothing) the RANDOM macro in your Makefile. + + If you are recompiling after patching your sources, or if you got + your files from somewhere other than the official distribution, + "touch makedefs.c" to ensure that certain files (onames.h and pm.h) + are remade, lest potentially troublesome timestamps fool your make + (or nmake) utility. + +Compiling + +5. Now that everything is set up, change your current directory to src. + + For Microsoft compiler: + nmake install + + For Borland compiler: + make /f Makefile.bcc install + + For GCC: + mingw32-make -f Makefile.gcc install + + If you get any errors along the way then something has not been set + up correctly. The time it takes to compile depends on your + particular machine of course, but you should be able to go for lunch + and return to find everything finished. The less memory, and slower + your machine, the longer the lunch you may take. :-) + + In any case, it is likely that the command prompt window where you + are doing the compiling will be occupied for a while. If all goes + well, you will get an NetHack executable. + +Notes: + +1. To install an update of NetHack after changing something, change + your current directory to src and issue the appropriate command for + your compiler: + + For Microsoft compiler: + nmake + + For Borland compiler: + make /f Makefile.bcc + + For GCC: + mingw32-make -f Makefile.gcc + + If you add, delete, or reorder monsters or objects, or you change + the format of saved level files, delete any save and bones files. + (Trying to use such files sometimes produces amusing confusions on + the game's part, but usually crashes.) + + If you made changes to any of the level compiler software, you may + have to delete dgn_flex.c, dgn_yacc.c, lev_flex.c, and lev_yacc.c + from the util directory to ensure that they are remade. + +2. The executable produced by the TTY build is a 32-bit, flat-address + space, non-overlayed .exe file, which should run on any true Win32 + environment with console I/O support. + + The executable built by the graphical built is a 32-bit, + flat-address space, non-overlayed .exe file, which should run on any + true Win32 graphical environment. + + To run NetHack, proceed to RUNNING NETHACK. + + +BUILDING USING VISUAL C'S IDE + +Only the Win32 native port built on the Windows API, or Graphical +NetHack, can be built using the Visual C IDE. + +I. Dispelling the Myths: + + Compiling NetHack using the Visual C IDE is straightforward, as long + as you have your compiler and tools correctly installed. + + It is again assumed that you already changed your directory to + sys\winnt and executed: + nhsetup + as described at the top of this document. If you didn't, you must go + back and do so before proceeding. + +II. To compile your copy of NetHack for Windows on a Windows NT/2000/XP + machine using the Visual C IDE: + +Setting Up + +1. It almost goes without saying that you should make sure that your + tools are set up and running correctly. (For the Microsoft Visual C + IDE it should correctly fire up when you choose it in your Start | + Programs menus.) + +2. Make sure all the NetHack files are in the appropriate directory + structure. You should have a main directory with subdirectories + dat, doc, include, src, sys\share, sys\winnt, util, win\win32, and + at this point you should also have a build directory and a binary + directory (both created by nhsetup.bat executed from sys\winnt + earlier.) + + Other subdirectories may also be included in your distribution, but + they are not necessary for building the graphical version of NetHack + (you can delete them to save space if you wish.) + + Required Directories for building Graphical NetHack with the Visual + C IDE: + + top + | + -----------------------------------------/ /--------------- + | | | | | | | | | + util dat doc include src sys win build binary + | | + ------ ----- + | | | + share winnt win32 + + Those last two (build and binary) are created during the building + process. They are not disributed as part of the NetHack source + distribution. nhsetup.bat creates the build directory and moves a + few files into it, including the Visual C project files. The + "binary" directory will house everything you need to play the game + after building is complete. + + Check the file "Files" in your top level directory for an exact + listing of what file is in which directory. In order for the build + process to work, all the source files must be in the proper + locations. Remember that nhsetup.bat moves/copies many files around + to their intended locations for building NetHack. + + If you downloaded or ftp'd the sources from a UNIX system, the lines + will probably end in UNIX-style newlines, instead of the carriage + return and line feed pairs used by Windows. Visual C project files + and workspace files (dsp and dsw files) in particular need to have + their lines end in carriage-return-line-feed or they won't work + properly. + +3. Ready your tool. + Note: It's possible to build a graphical version using the Makefile, + as explained above. However, the IDE build has full game + functionality and is the officially released build. + + Start the Visual C IDE. In the Visual C IDE menus, choose: + File | Open Workspace + +4. Set up for the build. + + In the Visual C "Open Workspace" dialog box, navigate to the top of + your NetHack source directory. + + In there, highlight "nethack.dsw" and click on Open. + Once the workspace has been opened, you should see the following + list in the Visual C selection window: + + dgncomp files + + dgnstuff files + + dlb_main files + + levcomp files + + levstuff files + + makedefs files + + nethackw files + + recover files + + tile2bmp files + + tilemap files + + uudecode files + + On the Visual C menus, choose: + Project | Set Active Project | NetHackW + + On the Visual C menus again, choose either: + Build | Set Active Configuration | NetHackW - Win32 Release + or + Build | Set Active Configuration | NetHackW - Win32 Debug + + The first will create the Release build of NetHackW which does not + contain all the debugging information and is smaller, and runs + quicker. The second will create the Debug build of NetHackW and + will spend a lot of time writing debug information to the disk as + the game is played. Unless you are debugging or enhancing NetHack + for Windows, choose the Release build. + +Building + +5. Start your build. + + On the Visual C menus once again, choose: + Build | Build NetHackW.exe + This starts the build. It is likely that the IDE message window + where you are doing the compiling will be occupied for a while. + +6. If all has gone well to this point, you should now have a NetHack + executable called NetHackW.exe in the "binary" directory, along with + all the support files that it needs. + + +RUNNING NETHACK + +I. Checking the installation: + Make sure all of the support files -- Guidebook.txt, license, + Defaults.nh, NetHack.exe or NetHackW.exe, nhdat, and recover.exe -- + were copied to the game directory. If not, move them there + yourself. + + Edit Defaults.nh to reflect your particular setup and personal + preferences, by following the comments. As with all releases since + 3.2.1, HACKDIR defaults to the same directory as that where the + NetHack.exe or NetHackW.exe executable resides. You only need to + set HACKDIR in defaults.nh if, for some reason, you wish to override + that (be careful). + +II. Executing the game + +1. Running from the command prompt: + + If you add the directory containing the NetHack executable to your + PATH, you can just type "nethack" or "nethack -umike" or "nethackw" + or "nethackw -umike" to start it up. Alternatively, you can + explicitly invoke it with a command such as + "c:\nethack\binary\nethack.exe" or "c:\nethack\binary\nethackw.exe" + (specifying whatever drive and directory your NetHack executable + resides in) each time. + +2. Running from a Windows shortcut. + + If you will be running it by launching it from a shortcut, just use + the following information when setting up the shortcut. + + Description : NetHack 3.4.3 Console version + Command Line : C:\NETHACK\BINARY\NETHACK.EXE + + Description : NetHack 3.4.3 Graphical Interface + Command Line : C:\NETHACK\BINARY\NETHACKW.EXE + + (changing the directory to the appropriate one of course) + +III. Play NetHack. If it works, you're done! + + +PROBLEMS + + If you discover a bug and wish to report it, or if you have comments + or suggestions we recommend using our "Contact Us" web page at: + http://www.nethack.org/common/contact.html + + If you don't have access to the web, or you want to send us a patch + to the NetHack source code feel free to drop us a line c/o: + DevTeam (at) nethack.org + + Happy NetHacking! diff --git a/sys/winnt/Makefile.bcc b/sys/winnt/Makefile.bcc new file mode 100644 index 0000000..c734afe --- /dev/null +++ b/sys/winnt/Makefile.bcc @@ -0,0 +1,1378 @@ +# SCCS Id: @(#)Makefile.bcc 3.4 $Date: 2003/11/16 04:50:56 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# +# IMPORTANT NOTE: This Makefile has not been tested for 3.4.3. +# +# +# NetHack 3.4.x Makefile for Borland C++ V5.5.1 and above and Borland's MAKE +# +# Win32 Compilers Tested: +# - Borland C++ 5.5.1 for Win32 +# +# If you don't have this compiler, you can get it at: +# http://www.borland.com/bcppbuilder/freecompiler/ +# +# This makefile is set up to assume the directories are extracted at the +# root, but this can be changed by modifying the bccroot and related +# variables. +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +# +# -- +# Yitzhak Sapir +#============================================================================== +# Do not delete the following 3 lines. +# +TARGETOS=BOTH +APPVER=4.0 + +bccbin = $(MAKEDIR) +bccroot = $(MAKEDIR)\.. +bccinc = $(bccroot)\include +bcclib = $(bccroot)\lib + +!IFNDEF APPVER +APPVER = 4.0 +!ENDIF + +# Graphical interface +# Set to Y for a graphical version +# Set to anything else (or undefine) for a tty version + +#GRAPHICAL = Y + +# Debug +# Set to Y for Debug support (to produce full map files, listing files, and debug information) +# Set to anything else (or undefine) for a "release" version + +DEBUG = Y + +!IF "$(APPVER)" == "4.0" +MAKE_WINVER = 0x0400 +!ELSEIF "$(APPVER)" == "5.0" +MAKE_WINVER = 0x0500 +!ENDIF + +cc = $(bccbin)\bcc32 +rc = $(bccbin)\brc32 +link = $(bccbin)\ilink32 +implib = $(bccbin)\tlib + +cflags = -c -D_X86_=1 -DWINVER=$(MAKE_WINVER) -q -I$(bccinc) -w-pia -w-rch -w-csu -w-par -w-aus +cdebug = -y -v -O2 +cvarsmt = -DWIN32 -D_WIN32 -D_MT +lflags = +!IF "$(DEBUG)" == "Y" +linkdebug = /v /m /s +cdebug = -v -y -Q +!ELSE +linkdebug = /C /Gn +cdebug = +!ENDIF +startobj = $(bcclib)\c0x32.obj +!IF "$(GRAPHICAL)" == "Y" +verlflags = /Gn /Gz /q -L$(bcclib) /c /Tpe /V$(APPVER) +startobjg = $(bcclib)\c0w32.obj +!ELSE +verlflags = /Gn /Gz /q -L$(bcclib) /c /ap /Tpe /V$(APPVER) +startobjg = $(startobj) +!ENDIF +libsmt = $(bcclib)\cw32mt.lib $(bcclib)\import32.lib + +# +# Set the gamedir according to your preference. +# It must be present prior to compilation. + +!IF "$(GRAPHICAL)" == "Y" +GAME = NetHackW # Game Name +!ELSE +GAME = NetHack # Game Name +!ENDIF +GAMEDIR = ..\binary # Game directory + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = ..\include # NetHack include files +DAT = ..\dat # NetHack data files +DOC = ..\doc # NetHack documentation files +UTIL = ..\util # Utility source +SRC = ..\src # Main source +SSYS = ..\sys\share # Shared system files +NTSYS = ..\sys\winnt # NT Win32 specific files +TTY = ..\win\tty # window port files (tty) +WIN32 = ..\win\win32 # window port files (Win32) +WSHR = ..\win\share # Tile support files + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), uncomment the upper two macros. +# + +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +!IFNDEF DO_YACC +DO_YACC = YACC_MSG +!ENDIF +!IFNDEF DO_LEX +DO_LEX = LEX_MSG +!ENDIF + +# Wilbur Streett's Win32 ports of GNU bison and flex are available at: +# http://www.monmouth.com/~wstreett/lex-yacc/lex-yacc.html +# +# To use them, download the executables and templates (bison.simple, +# bison.hairy) to some directory, and set the environment variables +# BISON_SIMPLE and BISON_HAIRY, and your path to point to this +# directory. +# +# For example, if you placed them in C:\BIN, you should set: +# C:> SET BISON_SIMPLE=C:\BIN\BISON.SIMPLE +# C:> SET BISON_HAIRY=C:\BIN\BISON.HAIRY +# +# Also, make sure your path points to the bison/flex directories. +# +# The following settings are configured for Wilbur Streett's ports. + +# - Specify your yacc and lex programs (or work-alikes) here. + +YACC = bison -y +#YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +#YTABC = y.tab.c +#YTABH = y.tab.h +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lex.yy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# Compiler and Linker flags +# + +PRECOMPHEAD = N # set to Y if you want to use precomp. headers + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +!IF "$(GRAPHICAL)" == "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPHDR = $(WIN32)\mhaskyn.h $(WIN32)\mhdlg.h $(WIN32)\mhfont.h \ + $(WIN32)\mhinput.h $(WIN32)\mhmain.h $(WIN32)\mhmap.h $(WIN32)\mhmenu.h \ + $(WIN32)\mhmsg.h $(WIN32)\mhmsgwnd.h $(WIN32)\mhrip.h $(WIN32)\mhstatus.h \ + $(WIN32)\mhtext.h $(WIN32)\resource.h $(WIN32)\winMS.h +WINDLLS = +WINPFLAG= -DTILES -DMSWIN_GRAPHICS +NHRES = $(O)winhack.res +WINPINC = -I$(WIN32) +!ELSE +WINPORT = $(O)nttty.o +WINPHDR = +WINDLLS = $(GAMEDIR)\nhdefkey.dll $(GAMEDIR)\nh340key.dll $(GAMEDIR)\nhraykey.dll +WINPFLAG= -DWIN32CON +NHRES = $(O)console.res +WINPINC = +!ENDIF + +TILEUTIL16 = $(UTIL)\tile2bmp.exe +TILEBMP16 = $(SRC)\tiles.bmp + +TILEUTIL32 = $(UTIL)\til2bm32.exe +TILEBMP32 = $(SRC)\tiles32.bmp + +SOUND = $(OBJ)\ntsound.o +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = -DDLB +! ELSE +DLBFLG = +! ENDIF + + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) $(cvarsmt) -I$(INCL) $(WINPINC) -q $(cdebug) -v +LFLAGSBASE = $(linkdebug) $(verlflags) -L$(bcclib) -v + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASE) + +#========================================== +# - Game build +#========================================== + +LFLAGSBASE = $(linkdebug) $(conflags) +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +NHLFLAGS1 = /Gn /v /m /s /Gz /q /c +lflags = $(LFLAGSBASE) $(NHLFLAGS1) + +GAMEFILE = $(FDIR)\$(GAME).exe # whole thing + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys\share +#========================================== + +{$(SSYS)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys\winnt +#========================================== + +{$(NTSYS)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(NTSYS)}.h{$(INCL)}.h: + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +{$(UTIL)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGSU) -o$@ $< + +#========================================== +# Rules for files in win\share +#========================================== + +{$(WSHR)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win\tty +#========================================== + +{$(TTY)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in win\win32 +#========================================== + +{$(WIN32)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -o$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)\$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)^\ + +U = $(UTIL)^\ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)pcmain.o $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +TILOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(TILOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + + +!IF "$(GRAPHICAL)" == "Y" +OPTIONS_FILE = $(DAT)\guioptions +!ELSE +OPTIONS_FILE = $(DAT)\ttyoptions +!ENDIF +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h \ + $(INCL)\global.h $(INCL)\coord.h $(INCL)\vmsconf.h \ + $(INCL)\system.h $(INCL)\unixconf.h $(INCL)\os2conf.h \ + $(INCL)\micro.h $(INCL)\pcconf.h $(INCL)\tosconf.h \ + $(INCL)\amiconf.h $(INCL)\macconf.h $(INCL)\beconf.h \ + $(INCL)\ntconf.h $(INCL)\nhlan.h + +HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\align.h \ + $(INCL)\dungeon.h $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\youprop.h $(INCL)\prop.h \ + $(INCL)\permonst.h $(INCL)\monattk.h \ + $(INCL)\monflag.h $(INCL)\mondata.h $(INCL)\pm.h \ + $(INCL)\wintype.h $(INCL)\decl.h $(INCL)\quest.h \ + $(INCL)\spell.h $(INCL)\color.h $(INCL)\obj.h \ + $(INCL)\you.h $(INCL)\attrib.h $(INCL)\monst.h \ + $(INCL)\skills.h $(INCL)\onames.h $(INCL)\timeout.h \ + $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\display.h $(INCL)\engrave.h \ + $(INCL)\rect.h $(INCL)\region.h $(INCL)\winprocs.h \ + $(INCL)\wintty.h $(INCL)\trampoli.h + +LEV_H = $(INCL)\lev.h +DGN_FILE_H = $(INCL)\dgn_file.h +LEV_COMP_H = $(INCL)\lev_comp.h +SP_LEV_H = $(INCL)\sp_lev.h +TILE_H = ..\win\share\tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)\data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +#========================================== +#=============== TARGETS ================== +#========================================== + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME): $(O)obj.tag $(O)utility.tag graphicschk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: graphicschk $(GAME) $(O)install.tag + @echo Done. + +$(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) + copy $(DAT)\opthelp $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + if exist $(DOC)\guidebook.txt copy $(DOC)\guidebook.txt $(GAMEDIR)\Guidebook.txt + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt + @if exist $(SRC)\$(GAME).PDB copy $(SRC)\$(GAME).pdb $(GAMEDIR)\$(GAME).pdb + @if exist $(GAMEDIR)\$(GAME).PDB echo NOTE: You may want to remove $(GAMEDIR)\$(GAME).pdb to conserve space + -copy $(NTSYS)\defaults.nh $(GAMEDIR)\defaults.nh + echo install done > $@ + +# copy $(NTSYS)\winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)\bigroom.des $(DAT)\castle.des \ + $(DAT)\endgame.des $(DAT)\gehennom.des $(DAT)\knox.des \ + $(DAT)\medusa.des $(DAT)\oracle.des $(DAT)\tower.des \ + $(DAT)\yendor.des $(DAT)\arch.des $(DAT)\barb.des \ + $(DAT)\caveman.des $(DAT)\healer.des $(DAT)\knight.des \ + $(DAT)\monk.des $(DAT)\priest.des $(DAT)\ranger.des \ + $(DAT)\rogue.des $(DAT)\samurai.des $(DAT)\sokoban.des \ + $(DAT)\tourist.des $(DAT)\valkyrie.des $(DAT)\wizard.des + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) + echo sp_levs done > $(O)sp_lev.tag + +$(O)utility.tag: $(INCL)\date.h $(INCL)\onames.h $(INCL)\pm.h \ + $(SRC)\monstr.c $(SRC)\vis_tab.c \ + $(U)lev_comp.exe $(INCL)\vis_tab.h \ + $(U)dgn_comp.exe $(TILEUTIL16) + @echo utilities made >$@ + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +!IF "$(GRAPHICAL)"=="Y" +$(NHRES): $(TILEBMP16) $(WIN32)\winhack.rc $(WIN32)\mnsel.bmp \ + $(WIN32)\mnselcnt.bmp $(WIN32)\mnunsel.bmp \ + $(WIN32)\petmark.bmp $(WIN32)\NetHack.ico $(WIN32)\rip.bmp \ + $(WIN32)\splash.bmp + @$(rc) -r -fo$@ -i$(WIN32) -i$(bccinc) -dNDEBUG $(WIN32)\winhack.rc +!ELSE +$(NHRES): $(NTSYS)\console.rc $(NTSYS)\NetHack.ico + @$(rc) -r -fo$@ -i$(NTSYS) -i$(bccinc) -dNDEBUG $(NTSYS)\console.rc +!ENDIF + +#========================================== +# The main target. +#========================================== + +$(SRC)\uuid.lib: $(bcclib)\uuid.lib + @copy $(bcclib)\uuid.lib $@ + +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(SRC)\uuid.lib $(O)gamedir.tag $(WINDLLS) + @echo Linking.... + @$(link) $(lflags) $(startobjg) $(ALLOBJ), $@, $(GAME).map,$(libsmt),,$(NHRES) + @if exist $(O)install.tag del $(O)install.tag + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +$(O)gamedir.tag: + @if not exist $(GAMEDIR)\*.* echo creating directory $(GAMEDIR) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo directory created > $@ + +$(GAME)_.ico : $(NTSYS)\$(GAME).ico + @copy $(NTSYS)\$(GAME).ico $@ + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + @if not exist $(O)*.* mkdir $(OBJ) + @echo directory $(OBJ) created >$@ + +#========================================== +# Notify of any CL environment variables +# in effect since they change the compiler +# options. +#========================================== + +graphicschk: +! IF "$(GRAPHICAL)"=="Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +! ENDIF + @echo graphicschk > graphicschk + + +$(GAMEDIR)\nhdefkey.dll : $(O)nhdefkey.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nhdefkey.def + @echo ProcessKeystroke >>nhdefkey.def + @echo NHkbhit >>nhdefkey.def + @echo CheckInput >>nhdefkey.def + @echo SourceWhere >>nhdefkey.def + @echo SourceAuthor >>nhdefkey.def + @echo KeyHandlerName >>nhdefkey.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nhdefkey.o, $@,nhdefkey.map,$(libsmt),nhdefkey.def + +$(GAMEDIR)\nh340key.dll : $(O)nh340key.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nh340key.def + @echo ProcessKeystroke >>nh340key.def + @echo NHkbhit >>nh340key.def + @echo CheckInput >>nh340key.def + @echo SourceWhere >>nh340key.def + @echo SourceAuthor >>nh340key.def + @echo KeyHandlerName >>nh340key.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nh340key.o, $@,nh340key.map,$(libsmt),nh340key.def + +$(GAMEDIR)\nhraykey.dll : $(O)nhraykey.o + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo EXPORTS >nhraykey.def + @echo ProcessKeystroke >>nhraykey.def + @echo NHkbhit >>nhraykey.def + @echo CheckInput >>nhraykey.def + @echo SourceWhere >>nhraykey.def + @echo SourceAuthor >>nhraykey.def + @echo KeyHandlerName >>nhraykey.def + @echo Linking $@ + $(link) $(linkdebug) /Gn /Gz /q -L$(bcclib) /c /aa /Tpd /V$(APPVER) -L$(bcclib) -v \ + c0d32.obj $(O)nhraykey.o, $@,nhraykey.map,$(libsmt),nhraykey.def + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(O)obj.tag $(MAKEOBJS) $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(MAKEOBJS), $@,,$(libsmt) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(INCL)\patchlevel.h \ + $(U)makedefs.c + @$(cc) $(CFLAGSU) -o$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe + $(U)makedefs -v + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) $(startobj) $(O)uudecode.o, $@,,$(libsmt) + +$(O)uudecode.o: $(SSYS)\uudecode.c + +$(NTSYS)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(NTSYS) + ..\..\util\uudecode.exe nhico.uu + chdir ..\..\src + +$(WIN32)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(WIN32) + ..\..\util\uudecode.exe ../../sys/winnt/nhico.uu + chdir ..\..\src + +$(WIN32)\mnsel.bmp: $(U)uudecode.exe $(WIN32)\mnsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnsel.uu + chdir ..\..\src + +$(WIN32)\mnselcnt.bmp: $(U)uudecode.exe $(WIN32)\mnselcnt.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnselcnt.uu + chdir ..\..\src + +$(WIN32)\mnunsel.bmp: $(U)uudecode.exe $(WIN32)\mnunsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnunsel.uu + chdir ..\..\src + +$(WIN32)\petmark.bmp: $(U)uudecode.exe $(WIN32)\petmark.uu + chdir $(WIN32) + ..\..\util\uudecode.exe petmark.uu + chdir ..\..\src + +$(WIN32)\rip.bmp: $(U)uudecode.exe $(WIN32)\rip.uu + chdir $(WIN32) + ..\..\util\uudecode.exe rip.uu + chdir ..\..\src + +$(WIN32)\splash.bmp: $(U)uudecode.exe $(WIN32)\splash.uu + chdir $(WIN32) + ..\..\util\uudecode.exe splash.uu + chdir ..\..\src + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=$(cflags) -DWIN32 -D_WIN32 -D_MT -I..\include $(cdebug) -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(SPLEVOBJS), $@,,$(libsmt) + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + @$(cc) $(LEVCFLAGS) -D__IO_H -o$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + @$(cc) $(LEVCFLAGS) -o$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d lev_comp.y + copy $(YTABC) lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\lev_yacc.c $(U)lev_yacc.c >nul + @copy $(SSYS)\lev_comp.h $(INCL)\lev_comp.h >nul + @echo /**/ >>$(U)lev_yacc.c + @echo /**/ >>$(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\lev_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(DGNCOMPOBJS), $@,,$(libsmt) + + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h $(U)dgn_yacc.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + @$(cc) $(LEVCFLAGS) -D__IO_H -o$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + @$(cc) $(LEVCFLAGS) -o$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d dgn_comp.y + copy $(YTABC) dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\dgn_yacc.c $(U)dgn_yacc.c >nul + @copy $(SSYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + @echo /**/ >>$(U)dgn_yacc.c + @echo /**/ >>$(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\dgn_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ..\include +#=========================================== + +$(INCL)\win32api.h: $(NTSYS)\win32api.h + copy $(NTSYS)\win32api.h $@ + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o, $@,,$(libsmt) + + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)\dlb.h + @$(cc) $(CFLAGS) -o$@ $(SRC)\dlb.c + +$(O)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h + @$(cc) $(CFLAGS) -o$@ $(UTIL)\dlb_main.c + +$(DAT)\porthelp: $(NTSYS)\porthelp + @copy $(NTSYS)\porthelp $@ >nul + +nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon $(DAT)\porthelp \ + $(DAT)\license $(O)sp_lev.tag + cd $(DAT) + echo data >dlb.lst + echo oracles >>dlb.lst + if exist options echo options >>dlb.lst + if exist ttyoptions echo ttyoptions >>dlb.lst + if exist guioptions echo guioptions >>dlb.lst + if exist porthelp echo porthelp >>dlb.lst + echo quest.dat >>dlb.lst + echo rumors >>dlb.lst + echo help >>dlb.lst + echo hh >>dlb.lst + echo cmdhelp >>dlb.lst + echo history >>dlb.lst + echo opthelp >>dlb.lst + echo wizhelp >>dlb.lst + echo dungeon >>dlb.lst + echo license >>dlb.lst + for %N in (*.lev) do echo %N >>dlb.lst + $(U)dlb_main cIf dlb.lst $(SRC)\nhdat + cd $(SRC) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) $(SRC)\uuid.lib + $(link) $(LFLAGSU) $(startobj) $(RECOVOBJS), $@,,$(libsmt) + + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)\win32api.h + $(cc) $(CFLAGSU) -o$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)\tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o $(SRC)\uuid.lib + @$(link) $(LFLAGSU) $(startobj) $(O)tilemap.o, $@,,$(libsmt) + + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + @$(cc) $(CFLAGSU) -o$@ $(WSHR)\tilemap.c + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + @$(cc) $(CFLAGS) /DTILETEXT -o$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)\tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(GIFREADERS) $(TEXT_IO), $@,,$(libsmt) + + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(PPMWRITERS) $(TEXT_IO), $@,,$(libsmt) + + +!IF "$(GRAPHICAL)"=="Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + @$(U)tile2bmp $(TILEBMP16) +!ENDIF + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) $(SRC)\uuid.lib + @echo Linking $@... + @$(link) $(LFLAGSU) $(startobj) $(O)tile2bmp.o $(TEXT_IO), $@,,$(libsmt) + + +$(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(cc) $(CFLAGS) -I$(WSHR) /DPACKED_FILE -o$@ $(WSHR)\tile2bmp.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean +! IF ("$(OBJ)"!="") + -rmdir $(OBJ) /s /Q +! ENDIF + if exist $(INCL)\date.h del $(INCL)\date.h + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(U)*.lnk del $(U)*.lnk + if exist $(U)*.map del $(U)*.map + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\???-fil?.lev del $(DAT)\???-fil?.lev + if exist $(DAT)\???-goal.lev del $(DAT)\???-goal.lev + if exist $(DAT)\???-loca.lev del $(DAT)\???-loca.lev + if exist $(DAT)\???-strt.lev del $(DAT)\???-strt.lev + if exist $(DAT)\air.lev del $(DAT)\air.lev + if exist $(DAT)\asmodeus.lev del $(DAT)\asmodeus.lev + if exist $(DAT)\astral.lev del $(DAT)\astral.lev + if exist $(DAT)\baalz.lev del $(DAT)\baalz.lev + if exist $(DAT)\bigroom.lev del $(DAT)\bigroom.lev + if exist $(DAT)\castle.lev del $(DAT)\castle.lev + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\dungeon.pdf del $(DAT)\dungeon.pdf + if exist $(DAT)\earth.lev del $(DAT)\earth.lev + if exist $(DAT)\fakewiz?.lev del $(DAT)\fakewiz?.lev + if exist $(DAT)\fire.lev del $(DAT)\fire.lev + if exist $(DAT)\juiblex.lev del $(DAT)\juiblex.lev + if exist $(DAT)\knox.lev del $(DAT)\knox.lev + if exist $(DAT)\medusa-?.lev del $(DAT)\medusa-?.lev + if exist $(DAT)\mine*.lev del $(DAT)\mine*.lev + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\ttyoptions del $(DAT)\ttyoptions + if exist $(DAT)\guioptions del $(DAT)\guioptions + if exist $(DAT)\oracle.lev del $(DAT)\oracle.lev + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\orcus.lev del $(DAT)\orcus.lev + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(DAT)\sanctum.lev del $(DAT)\sanctum.lev + if exist $(DAT)\soko?-?.lev del $(DAT)\soko?-?.lev + if exist $(DAT)\tower?.lev del $(DAT)\tower?.lev + if exist $(DAT)\valley.lev del $(DAT)\valley.lev + if exist $(DAT)\water.lev del $(DAT)\water.lev + if exist $(DAT)\wizard?.lev del $(DAT)\wizard?.lev + if exist $(O)sp_lev.tag del $(O)sp_lev.tag + if exist $(SRC)\monstr.c del $(SRC)\monstr.c + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(U)recover.exe del $(U)recover.exe + if exist nhdat. del nhdat. + +clean: + if exist $(O)*.o del $(O)*.o + if exist $(O)utility.tag del $(O)utility.tag + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(SRC)\*.lnk del $(SRC)\*.lnk + if exist $(SRC)\*.map del $(SRC)\*.map + if exist $(TILEBMP16) del $(TILEBMP16) + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)\data: $(O)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(O)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(O)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(O)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nttty.c + @$(cc) $(CFLAGS) -I$(WSHR) -o$@ $(NTSYS)\nttty.c +$(O)winnt.o: $(HACK_H) $(INCL)\win32api.h $(NTSYS)\winnt.c + @$(cc) $(CFLAGS) -o$@ $(NTSYS)\winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)\ntsound.c + @$(cc) $(CFLAGS) -o$@ $(NTSYS)\ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)\nhlan.h $(NTSYS)\mapimail.c + @$(cc) $(CFLAGS) -DMAPI_VERBOSE -o$@ $(NTSYS)\mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + @$(cc) $(CFLAGS) -o$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * slashes changed to back-slashes +# * -c (which is included in CFLAGS) substituted +# with -o$@ +# * $(CC) changed to $(cc) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\sys\atari\tos.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\win32api.h + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcmain.c +$(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcsys.c +$(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pctty.c +$(O)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\pcunix.c +$(O)random.o: ..\sys\share\random.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\random.c +$(O)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\sys\share\ioctl.c +$(O)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\share\unixtty.c +$(O)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h + $(cc) $(CFLAGS) -o$@ ..\sys\unix\unixmain.c +$(O)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ..\sys\unix\unixunix.c +$(O)bemain.o: ..\sys\be\bemain.c $(HACK_H) $(INCL)\dlb.h + $(cc) $(CFLAGS) -o$@ ..\sys\be\bemain.c +$(O)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\getline.c +$(O)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\termcap.c +$(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\topl.c +$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\tcap.h + $(cc) $(CFLAGS) -o$@ ..\win\tty\wintty.c +$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ..\win\X11\Window.c +$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ..\win\X11\dialogs.c +$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h ..\win\X11\nh72icon \ + ..\win\X11\nh56icon ..\win\X11\nh32icon + $(cc) $(CFLAGS) -o$@ ..\win\X11\winX.c +$(O)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmap.c +$(O)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmenu.c +$(O)winmesg.o: ..\win\X11\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmesg.c +$(O)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winmisc.c +$(O)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winstat.c +$(O)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\wintext.c +$(O)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h + $(cc) $(CFLAGS) -o$@ ..\win\X11\winval.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)gnaskstr.o: ..\win\gnome\gnaskstr.c ..\win\gnome\gnaskstr.h \ + ..\win\gnome\gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnaskstr.c +$(O)gnbind.o: ..\win\gnome\gnbind.c ..\win\gnome\gnbind.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnaskstr.h ..\win\gnome\gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnbind.c +$(O)gnglyph.o: ..\win\gnome\gnglyph.c ..\win\gnome\gnglyph.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnglyph.c +$(O)gnmain.o: ..\win\gnome\gnmain.c ..\win\gnome\gnmain.h ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnbind.h ..\win\gnome\gnopts.h $(HACK_H) \ + $(INCL)\date.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmain.c +$(O)gnmap.o: ..\win\gnome\gnmap.c ..\win\gnome\gnmap.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmap.c +$(O)gnmenu.o: ..\win\gnome\gnmenu.c ..\win\gnome\gnmenu.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnbind.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmenu.c +$(O)gnmesg.o: ..\win\gnome\gnmesg.c ..\win\gnome\gnmesg.h ..\win\gnome\gnsignal.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnmesg.c +$(O)gnopts.o: ..\win\gnome\gnopts.c ..\win\gnome\gnopts.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnmain.h ..\win\gnome\gnmap.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnopts.c +$(O)gnplayer.o: ..\win\gnome\gnplayer.c ..\win\gnome\gnplayer.h \ + ..\win\gnome\gnmain.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnplayer.c +$(O)gnsignal.o: ..\win\gnome\gnsignal.c ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnsignal.c +$(O)gnstatus.o: ..\win\gnome\gnstatus.c ..\win\gnome\gnstatus.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gn_xpms.h \ + ..\win\gnome\gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnstatus.c +$(O)gntext.o: ..\win\gnome\gntext.c ..\win\gnome\gntext.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gn_rip.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gntext.c +$(O)gnyesno.o: ..\win\gnome\gnyesno.c ..\win\gnome\gnbind.h ..\win\gnome\gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -c ..\win\gnome\gnyesno.c +$(O)wingem.o: ..\win\gem\wingem.c $(HACK_H) $(INCL)\func_tab.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\wingem.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\wingem.c +$(O)wingem1.o: ..\win\gem\wingem1.c $(INCL)\gem_rsc.h $(INCL)\load_img.h \ + $(INCL)\wintype.h $(INCL)\wingem.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\wingem1.c +$(O)load_img.o: ..\win\gem\load_img.c $(INCL)\load_img.h + $(cc) $(CFLAGS) -o$@ ..\win\gem\load_img.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\dlb.h $(INCL)\patchlevel.h $(INCL)\qt_win.h \ + $(INCL)\qt_clust.h $(INCL)\qt_kde0.h \ + $(INCL)\qt_xpms.h qt_win.moc qt_kde0.moc + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_win.cpp +$(O)qt_clust.o: ..\win\Qt\qt_clust.cpp $(INCL)\qt_clust.h + $(CXX) $(CXXFLAGS) -c ..\win\Qt\qt_clust.cpp +$(O)monstr.o: $(SRC)\monstr.c $(CONFIG_H) +$(O)vis_tab.o: $(SRC)\vis_tab.c $(CONFIG_H) $(INCL)\vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)\edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +$(O)attrib.o: attrib.c $(HACK_H) $(INCL)\artifact.h +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)\lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)\edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)\lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)\edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)\eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)\edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)\tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)\lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)\dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) $(INCL)\artifact.h +$(O)light.o: light.c $(HACK_H) $(INCL)\lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)\mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) $(INCL)\artifact.h +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)\edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)\lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)\epri.h $(INCL)\edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)\epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)\qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)\lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)\lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)\eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)\eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)\edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)\lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)\vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)\lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file + diff --git a/sys/winnt/Makefile.gcc b/sys/winnt/Makefile.gcc new file mode 100644 index 0000000..980c2c2 --- /dev/null +++ b/sys/winnt/Makefile.gcc @@ -0,0 +1,1352 @@ +# SCCS Id: @(#)Makefile.gcc 3.4 $Date: 2003/11/16 04:50:57 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# NetHack 3.4.x Makefile for MinGW +# +# Win32 Compilers Tested: +# - MinGW 1.0 (gcc version 2.95.3-6) (Console NetHack only) +# - MinGW 2.0 (gcc version 3.2) +# +# If you don't have this compiler, you can get it at: +# http://www.mingw.org/ +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +# +# -- +# Dion Nicolaas +#============================================================================== +# Graphical interface +# Set to Y for a graphical version +# Set to anything else (or undefine) for a tty version + +#GRAPHICAL = Y + +# Debug +# Set to Y for Debug support (to produce debug information) +# Set to anything else (or undefine) for a "release" version +# You can set your debug options below. + +DEBUG = Y + +cc = gcc +rc = windres +link = gcc + +cflags = -mms-bitfields +lflags = +ifeq "$(DEBUG)" "Y" +cdebug = -g +linkdebug = -g +else +cdebug = +linkdebug = +endif + +# +# Set the gamedir according to your preference. +# If not present prior to compilation it gets created. + +ifeq "$(GRAPHICAL)" "Y" +# Game Name +GAME = NetHackW +else +# Game Name +GAME = NetHack +endif +# Game directory +GAMEDIR = ../binary + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +# NetHack include files +INCL = ../include +# NetHack data files +DAT = ../dat +# NetHack documentation files +DOC = ../doc +# Utility source +UTIL = ../util +# Main source +SRC = ../src +# Shared system files +SSYS = ../sys/share +# NT Win32 specific files +NTSYS = ../sys/winnt +# window port files (tty) +TTY = ../win/tty +# window port files (Win32) +WIN32 = ../win/win32 +# Tile support files +WSHR = ../win/share + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), comment out the upper two macros and uncomment +# the lower two. +# + +DO_YACC = YACC_MSG +DO_LEX = LEX_MSG +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +# - Specify your yacc and lex programs (or work-alikes) here. + +#YACC = bison -y +YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)/random.o +#RANDOM = + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +ifeq "$(GRAPHICAL)" "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPFLAG = -DTILES -DMSWIN_GRAPHICS -D_WIN32_IE=0x0400 +NHRES = $(O)winres.o +WINPINC = -I$(WIN32) +WINPHDR = $(WIN32)/mhaskyn.h $(WIN32)/mhdlg.h $(WIN32)/mhfont.h \ + $(WIN32)/mhinput.h $(WIN32)/mhmain.h $(WIN32)/mhmap.h \ + $(WIN32)/mhmenu.h $(WIN32)/mhmsg.h $(WIN32)/mhmsgwnd.h \ + $(WIN32)/mhrip.h $(WIN32)/mhstatus.h \ + $(WIN32)/mhtext.h $(WIN32)/resource.h $(WIN32)/winMS.h +WINPLIBS = -lcomctl32 -lwinmm +else +WINPORT = $(O)nttty.o +WINPFLAG= -DWIN32CON +WINPHDR = +NHRES = $(O)console.o +WINPINC = +WINPLIBS = -lwinmm +endif + +TILEUTIL16 = $(UTIL)/tile2bmp.exe +TILEBMP16 = $(SRC)/tiles.bmp + +TILEUTIL32 = $(UTIL)/til2bm32.exe +TILEBMP32 = $(SRC)/tiles32.bmp + +SOUND = $(OBJ)/ntsound.o + +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +ifeq "$(USE_DLB)" "Y" +DLBFLG = -DDLB +else +DLBFLG = +endif + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) -I$(INCL) $(WINPINC) $(cdebug) +LFLAGSBASEC = $(linkdebug) +LFLAGSBASEG = $(linkdebug) -mwindows + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASEC) + +#========================================== +# - Game build +#========================================== + +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +lflags = $(LFLAGSBASE) +ifeq "$(GRAPHICAL)" "Y" +lflags = $(LFLAGSBASEG) +else +lflags = $(LFLAGSBASEC) +endif + +GAMEFILE = $(GAMEDIR)/$(GAME).exe # whole thing + +ifeq "$(USE_DLB)" "Y" +DLB = nhdat +else +DLB = +endif + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +$(OBJ)/%.o : /%.c + $(cc) $(CFLAGS) -o$@ $< + +$(OBJ)/%.o : $(SRC)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys/share +#========================================== + +$(OBJ)/%.o : $(SSYS)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in sys/winnt +#========================================== + +$(OBJ)/%.o : $(NTSYS)/%.c + $(cc) $(CFLAGS) -o$@ $< + +$(INCL)/%.h : $(NTSYS)/%.h + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +$(OBJ)/%.o : $(UTIL)/%.c + $(cc) $(CFLAGSU) -o$@ $< + +#========================================== +# Rules for files in win/share +#========================================== + +$(OBJ)/%.o : $(WSHR)/%.c + $(cc) $(CFLAGS) -o$@ $< + +$(INCL)/%.h : $(WSHR)/%.h + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win/tty +#========================================== + +$(OBJ)/%.o : $(TTY)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +# Rules for files in win/win32 +#========================================== + +$(OBJ)/%.o : $(WIN32)/%.c + $(cc) $(CFLAGS) -o$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)/$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)/ + +U = $(UTIL)/ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)/monsters.txt $(WSHR)/objects.txt $(WSHR)/other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +TEXT_IO32 = $(O)tilete32.o $(O)tiletx32.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o +GIFREADERS32 = $(O)gifrd32.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)pcmain.o $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +WINPOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(WINPOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + +ifeq "$(GRAPHICAL)" "Y" +OPTIONS_FILE = $(DAT)/guioptions +else +OPTIONS_FILE = $(DAT)/ttyoptions +endif + +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)/config.h $(INCL)/config1.h $(INCL)/tradstdc.h \ + $(INCL)/global.h $(INCL)/coord.h $(INCL)/vmsconf.h \ + $(INCL)/system.h $(INCL)/unixconf.h $(INCL)/os2conf.h \ + $(INCL)/micro.h $(INCL)/pcconf.h $(INCL)/tosconf.h \ + $(INCL)/amiconf.h $(INCL)/macconf.h $(INCL)/beconf.h \ + $(INCL)/ntconf.h $(INCL)/nhlan.h + +HACK_H = $(INCL)/hack.h $(CONFIG_H) $(INCL)/align.h \ + $(INCL)/dungeon.h $(INCL)/monsym.h $(INCL)/mkroom.h \ + $(INCL)/objclass.h $(INCL)/youprop.h $(INCL)/prop.h \ + $(INCL)/permonst.h $(INCL)/monattk.h \ + $(INCL)/monflag.h $(INCL)/mondata.h $(INCL)/pm.h \ + $(INCL)/wintype.h $(INCL)/decl.h $(INCL)/quest.h \ + $(INCL)/spell.h $(INCL)/color.h $(INCL)/obj.h \ + $(INCL)/you.h $(INCL)/attrib.h $(INCL)/monst.h \ + $(INCL)/skills.h $(INCL)/onames.h $(INCL)/timeout.h \ + $(INCL)/trap.h $(INCL)/flag.h $(INCL)/rm.h \ + $(INCL)/vision.h $(INCL)/display.h $(INCL)/engrave.h \ + $(INCL)/rect.h $(INCL)/region.h $(INCL)/winprocs.h \ + $(INCL)/wintty.h $(INCL)/trampoli.h + +LEV_H = $(INCL)/lev.h +DGN_FILE_H = $(INCL)/dgn_file.h +LEV_COMP_H = $(INCL)/lev_comp.h +SP_LEV_H = $(INCL)/sp_lev.h +TILE_H = ../win/share/tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)/data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)/$(GAME).exe + + +#========================================== +#=============== TARGETS ================== +#========================================== + +# Since DOS doesn't allow / as path separator, and GCC doesn't allow \ as +# path separator, we must change all pathnames when performing DOS commands. +# This is done by blindly applying $(subst /,\, ...) on every command. +# Where any command contain / for another reason (switch char, or echoing +# comment lines to lev/dungeon files) a little more care is taken. + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME) : $(O)obj.tag $(O)utility.tag graphicschk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: graphicschk $(GAME) $(O)install.tag + @echo Done. + + +$(O)install.tag: $(DAT)/data $(DAT)/rumors $(DAT)/dungeon \ + $(DAT)/oracles $(DAT)/quest.dat $(O)sp_lev.tag $(DLB) +ifeq "$(USE_DLB)" "Y" + $(subst /,\,copy nhdat $(GAMEDIR)) + $(subst /,\,copy $(DAT)/license $(GAMEDIR)) + $(subst /,\,copy $(DAT)/opthelp $(GAMEDIR)) +else + $(subst /,\,copy $(DAT)/*. $(GAMEDIR)) + $(subst /,\,copy $(DAT)/*.dat $(GAMEDIR)) + $(subst /,\,copy $(DAT)/*.lev $(GAMEDIR)) + $(subst /,\,if exist $(GAMEDIR)/makefile del $(GAMEDIR)/makefile) +endif + $(subst /,\,if exist $(DOC)/guidebook.txt copy $(DOC)/guidebook.txt $(GAMEDIR)/Guidebook.txt) + $(subst /,\,if exist $(DOC)/nethack.txt copy $(DOC)/nethack.txt $(GAMEDIR)/NetHack.txt) + $(subst /,\,copy $(NTSYS)/defaults.nh $(GAMEDIR)/defaults.nh) + $(subst /,\,echo install done > $@) + +# copy $(NTSYS)/winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + $(subst /,\,if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR)) + $(subst /,\,if exist $(DOC)/recover.txt copy $(DOC)/recover.txt $(GAMEDIR)/recover.txt) + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)/bigroom.des $(DAT)/castle.des \ + $(DAT)/endgame.des $(DAT)/gehennom.des $(DAT)/knox.des \ + $(DAT)/medusa.des $(DAT)/oracle.des $(DAT)/tower.des \ + $(DAT)/yendor.des $(DAT)/arch.des $(DAT)/barb.des \ + $(DAT)/caveman.des $(DAT)/healer.des $(DAT)/knight.des \ + $(DAT)/monk.des $(DAT)/priest.des $(DAT)/ranger.des \ + $(DAT)/rogue.des $(DAT)/samurai.des $(DAT)/sokoban.des \ + $(DAT)/tourist.des $(DAT)/valkyrie.des $(DAT)/wizard.des + $(subst /,\,$(U)lev_comp $(DAT)/bigroom.des) + $(subst /,\,$(U)lev_comp $(DAT)/castle.des) + $(subst /,\,$(U)lev_comp $(DAT)/endgame.des) + $(subst /,\,$(U)lev_comp $(DAT)/gehennom.des) + $(subst /,\,$(U)lev_comp $(DAT)/knox.des) + $(subst /,\,$(U)lev_comp $(DAT)/mines.des) + $(subst /,\,$(U)lev_comp $(DAT)/medusa.des) + $(subst /,\,$(U)lev_comp $(DAT)/oracle.des) + $(subst /,\,$(U)lev_comp $(DAT)/sokoban.des) + $(subst /,\,$(U)lev_comp $(DAT)/tower.des) + $(subst /,\,$(U)lev_comp $(DAT)/yendor.des) + $(subst /,\,$(U)lev_comp $(DAT)/arch.des) + $(subst /,\,$(U)lev_comp $(DAT)/barb.des) + $(subst /,\,$(U)lev_comp $(DAT)/caveman.des) + $(subst /,\,$(U)lev_comp $(DAT)/healer.des) + $(subst /,\,$(U)lev_comp $(DAT)/knight.des) + $(subst /,\,$(U)lev_comp $(DAT)/monk.des) + $(subst /,\,$(U)lev_comp $(DAT)/priest.des) + $(subst /,\,$(U)lev_comp $(DAT)/ranger.des) + $(subst /,\,$(U)lev_comp $(DAT)/rogue.des) + $(subst /,\,$(U)lev_comp $(DAT)/samurai.des) + $(subst /,\,$(U)lev_comp $(DAT)/tourist.des) + $(subst /,\,$(U)lev_comp $(DAT)/valkyrie.des) + $(subst /,\,$(U)lev_comp $(DAT)/wizard.des) + $(subst /,\,copy *.lev $(DAT)) + $(subst /,\,del *.lev) + $(subst /,\,echo sp_levs done > $(O)sp_lev.tag) + +$(O)utility.tag: $(INCL)/date.h $(INCL)/onames.h $(INCL)/pm.h \ + $(SRC)/monstr.c $(SRC)/vis_tab.c $(U)lev_comp.exe $(INCL)/vis_tab.h \ + $(U)dgn_comp.exe $(TILEUTIL16) + $(subst /,\,@echo utilities made >$@) + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +ifeq "$(GRAPHICAL)" "Y" +$(NHRES): $(TILEBMP16) $(WIN32)/winhack.rc $(WIN32)/mnsel.bmp \ + $(WIN32)/mnselcnt.bmp $(WIN32)/mnunsel.bmp \ + $(WIN32)/petmark.bmp $(WIN32)/NetHack.ico $(WIN32)/rip.bmp \ + $(WIN32)/splash.bmp + @$(rc) -o$@ --include-dir $(WIN32) -i $(WIN32)/winhack.rc +else +$(NHRES): $(NTSYS)/console.rc $(NTSYS)/NetHack.ico + @$(rc) -o$@ --include-dir $(NTSYS) -i $(NTSYS)/console.rc +endif + +#========================================== +# The main target. +#========================================== +$(O)gamedir.tag: + $(subst /,\,@if not exist $(GAMEDIR)/*.* echo creating directory $(GAMEDIR)) + $(subst /,\,@if not exist $(GAMEDIR)/*.* mkdir $(GAMEDIR)) + $(subst /,\,@echo directory created > $@) + +ifeq "$(GRAPHICAL)" "Y" +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag +else +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag \ + $(GAMEDIR)/nhdefkey.dll $(GAMEDIR)/nh340key.dll $(GAMEDIR)/nhraykey.dll +endif + @echo Linking.... + @$(link) $(lflags) -o$@ $(ALLOBJ) $(NHRES) $(WINPLIBS) + $(subst /,\,@if exist $(O)install.tag del $(O)install.tag) + + +$(O)nhdefkey.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nhdefkey.c + +$(GAMEDIR)/nhdefkey.dll : $(O)nhdefkey.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(O)nh340key.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nh340key.c + +$(GAMEDIR)/nh340key.dll : $(O)nh340key.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(O)nhraykey.o: + $(cc) $(CFLAGS) -DBUILD_DLL -o$@ $(NTSYS)/nhraykey.c + +$(GAMEDIR)/nhraykey.dll : $(O)nhraykey.o $(O)gamedir.tag + @echo Linking $@ + $(cc) -shared -Wl,--export-all-symbols \ + -Wl,--add-stdcall-alias -o $@ $< + +$(GAME)_.ico : $(NTSYS)/$(GAME).ico + $(subst /,\,@copy $(NTSYS)/$(GAME).ico $@) + +#========================================== +# Create directory for holding object files +#========================================== + +graphicschk: +ifeq "$(GRAPHICAL)" "Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +endif + $(subst /,\,@echo graphicschk > graphicschk) + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + @$(link) $(LFLAGSU) -o$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)/monattk.h $(INCL)/monflag.h \ + $(INCL)/objclass.h $(INCL)/monsym.h $(INCL)/qtext.h \ + $(INCL)/patchlevel.h $(U)makedefs.c $(O)obj.tag + $(cc) $(CFLAGSU) -o$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)/date.h $(OPTIONS_FILE): $(U)makedefs.exe + $(subst /,\,$(U)makedefs -v) + +#$(OPTIONS_FILE): $(U)makedefs.exe +# $(subst /,\,$(U)makedefs -v) + +$(INCL)/onames.h : $(U)makedefs.exe + $(subst /,\,$(U)makedefs -o) + +$(INCL)/pm.h : $(U)makedefs.exe + $(subst /,\,$(U)makedefs -p) + +#$(INCL)/trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)/monstr.c: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -m) + +$(INCL)/vis_tab.h: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -z) + +$(SRC)/vis_tab.c: $(U)makedefs.exe + $(subst /,\,$(U)makedefs -z) + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) -o$@ $(O)uudecode.o + +$(O)uudecode.o: $(SSYS)/uudecode.c + +$(NTSYS)/NetHack.ico : $(U)uudecode.exe $(NTSYS)/nhico.uu + $(subst /,\,$(U)uudecode.exe $(NTSYS)/nhico.uu) + $(subst /,\,copy NetHack.ico $@) + del NetHack.ico + +$(WIN32)/NetHack.ico : $(NTSYS)/NetHack.ico + $(subst /,\,copy $< $@) + +$(WIN32)/mnsel.bmp: $(U)uudecode.exe $(WIN32)/mnsel.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnsel.uu) + $(subst /,\,copy mnsel.bmp $@) + del mnsel.bmp + +$(WIN32)/mnselcnt.bmp: $(U)uudecode.exe $(WIN32)/mnselcnt.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnselcnt.uu) + $(subst /,\,copy mnselcnt.bmp $@) + del mnselcnt.bmp + +$(WIN32)/mnunsel.bmp: $(U)uudecode.exe $(WIN32)/mnunsel.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/mnunsel.uu) + $(subst /,\,copy mnunsel.bmp $@) + del mnunsel.bmp + +$(WIN32)/petmark.bmp: $(U)uudecode.exe $(WIN32)/petmark.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/petmark.uu) + $(subst /,\,copy petmark.bmp $@) + del petmark.bmp + +$(WIN32)/rip.bmp: $(U)uudecode.exe $(WIN32)/rip.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/rip.uu) + $(subst /,\,copy rip.bmp $@) + del rip.bmp + +$(WIN32)/splash.bmp: $(U)uudecode.exe $(WIN32)/splash.uu + $(subst /,\,$(U)uudecode.exe $(WIN32)/splash.uu) + $(subst /,\,copy splash.bmp $@) + del splash.bmp + + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=$(cflags) -c -DWIN32 -D_WIN32 -I../include $(cdebug) -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(SPLEVOBJS) + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)/lev_comp.h $(U)lev_yacc.c + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)/lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + $(cc) $(LEVCFLAGS) -o$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)/lev_comp.h : $(U)lev_comp.y +ifeq "$(DO_YACC)" "YACC_ACT" + $(subst /,\,$(YACC) -d $(U)lev_comp.y) + $(subst /,\,copy $(YTABC) $(U)lev_yacc.c) + $(subst /,\,copy $(YTABH) $(INCL)/lev_comp.h) + $(subst /,\,@del $(YTABC)) + $(subst /,\,@del $(YTABH)) + +else + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)/lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + $(subst /,\,@copy $(SSYS)/lev_yacc.c $(U)lev_yacc.c >nul) + $(subst /,\,@copy $(SSYS)/lev_comp.h $(INCL)/lev_comp.h >nul) + $(subst /,\,echo.>>$(U)lev_yacc.c) + $(subst /,\,echo.>>$(INCL)/lev_comp.h) +endif + +$(U)lev_$(LEX).c: $(U)lev_comp.l +ifeq "$(DO_LEX)" "LEX_ACT" + $(subst /,\,$(LEX) $(FLEXSKEL) $(U)lev_comp.l) + $(subst /,\,copy $(LEXYYC) $@) + $(subst /,\,@del $(LEXYYC)) +else + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + $(subst /,\,@copy $(SSYS)/lev_lex.c $@ >nul) + $(subst /,\,echo.>>$@) +endif + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(DGNCOMPOBJS) + + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)/dgn_comp.h $(U)dgn_yacc.c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)/dgn_comp.h \ + $(U)dgn_$(LEX).c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + $(cc) $(LEVCFLAGS) -o$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)/dgn_comp.h : $(U)dgn_comp.y +ifeq "$(DO_YACC)" "YACC_ACT" + $(subst /,\,$(YACC) -d $(U)dgn_comp.y) + $(subst /,\,copy $(YTABC) $(U)dgn_yacc.c) + $(subst /,\,copy $(YTABH) $(INCL)/dgn_comp.h) + $(subst /,\,@del $(YTABC)) + $(subst /,\,@del $(YTABH)) +else + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)/dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + $(subst /,\,@copy $(SSYS)/dgn_yacc.c $(U)dgn_yacc.c >nul) + $(subst /,\,@copy $(SSYS)/dgn_comp.h $(INCL)/dgn_comp.h >nul) + $(subst /,\,echo.>>$(U)dgn_yacc.c) + $(subst /,\,echo.>>$(INCL)/dgn_comp.h) +endif + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +ifeq "$(DO_LEX)" "LEX_ACT" + $(subst /,\,$(LEX) $(FLEXSKEL) $(U)dgn_comp.l) + $(subst /,\,copy $(LEXYYC) $@) + $(subst /,\,@del $(LEXYYC)) +else + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + $(subst /,\,@copy $(SSYS)/dgn_lex.c $@ >nul) + $(subst /,\,echo.>>$@) +endif + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + $(subst /,\,@if not exist $(OBJ)/*.* echo creating directory $(OBJ)) + $(subst /,\,@if not exist $(OBJ)/*.* mkdir $(OBJ)) + $(subst /,\,@echo directory created > $@) + + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ../include +#=========================================== + +$(INCL)/win32api.h: $(NTSYS)/win32api.h + $(subst /,\,copy $(NTSYS)/win32api.h $@) + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o + @$(link) $(LFLAGSU) -o$@ $(O)dlb_main.o $(O)dlb.o $(O)alloc.o $(O)panic.o + + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ $(SRC)/dlb.c + +$(O)dlb_main.o: $(UTIL)/dlb_main.c $(INCL)/config.h $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ $(UTIL)/dlb_main.c + +$(DAT)/porthelp: $(NTSYS)/porthelp + $(subst /,\,@copy $(NTSYS)/porthelp $@ >nul) + +nhdat: $(U)dlb_main.exe $(DAT)/data $(DAT)/oracles $(OPTIONS_FILE) \ + $(DAT)/quest.dat $(DAT)/rumors $(DAT)/help $(DAT)/hh $(DAT)/cmdhelp \ + $(DAT)/history $(DAT)/opthelp $(DAT)/wizhelp $(DAT)/dungeon \ + $(DAT)/porthelp $(DAT)/license $(O)sp_lev.tag + $(subst /,\,echo data >$(DAT)/dlb.lst) + $(subst /,\,echo oracles >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/options echo options >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/ttyoptions echo ttyoptions >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/guioptions echo guioptions >>$(DAT)/dlb.lst) + $(subst /,\,if exist $(DAT)/porthelp echo porthelp >>$(DAT)/dlb.lst) + $(subst /,\,echo quest.dat >>$(DAT)/dlb.lst) + $(subst /,\,echo rumors >>$(DAT)/dlb.lst) + $(subst /,\,echo help >>$(DAT)/dlb.lst) + $(subst /,\,echo hh >>$(DAT)/dlb.lst) + $(subst /,\,echo cmdhelp >>$(DAT)/dlb.lst) + $(subst /,\,echo history >>$(DAT)/dlb.lst) + $(subst /,\,echo opthelp >>$(DAT)/dlb.lst) + $(subst /,\,echo wizhelp >>$(DAT)/dlb.lst) + $(subst /,\,echo dungeon >>$(DAT)/dlb.lst) + $(subst /,\,echo license >>$(DAT)/dlb.lst) + dir /l /b /-p $(subst /,\,$(DAT)/*.lev >>$(DAT)/dlb.lst) + $(subst /,\,$(U)dlb_main CcIf $(DAT) dlb.lst $(SRC)/nhdat) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) + $(link) $(LFLAGSU) -o$@ $(RECOVOBJS) + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)/win32api.h + $(cc) $(CFLAGSU) -o$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)/tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + @$(link) $(LFLAGSU) -o$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGSU) -o$@ $(WSHR)/tilemap.c + +$(O)tiletx32.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGS) -DTILETEXT -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/tilemap.c + +$(O)tiletxt.o: $(WSHR)/tilemap.c $(HACK_H) + $(cc) $(CFLAGS) -DTILETEXT -o$@ $(WSHR)/tilemap.c + +$(O)gifread.o: $(WSHR)/gifread.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/gifread.c + +$(O)gifrd32.o: $(WSHR)/gifread.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/gifread.c + +$(O)ppmwrite.o: $(WSHR)/ppmwrite.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/ppmwrite.c + +$(O)tiletext.o: $(WSHR)/tiletext.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/tiletext.c + +$(O)tilete32.o: $(WSHR)/tiletext.c $(CONFIG_H) $(TILE_H) + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(GIFREADERS) $(TEXT_IO) + +$(U)gif2tx32.exe: $(GIFREADERS32) $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(GIFREADERS32) $(TEXT_IO32) + + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(PPMWRITERS) $(TEXT_IO) + + +ifeq "$(GRAPHICAL)" "Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + $(subst /,\,@$(U)tile2bmp $(TILEBMP16)) +#$(TILEBMP32): $(TILEUTIL32) $(TILEFILES32) +# @echo Creating 32x32 binary tile files (this may take some time) +# $(subst /,\,@$(U)til2bm32 $(TILEBMP32)) +else +$(TILEBMP16): +$(TILEBMP32): +endif + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(O)tile2bmp.o $(TEXT_IO) + +$(U)til2bm32.exe: $(O)til2bm32.o $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -o$@ $(O)til2bm32.o $(TEXT_IO32) + +$(O)tile2bmp.o: $(WSHR)/tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)/win32api.h + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(WSHR)/tile2bmp.c + +$(O)til2bm32.o: $(WSHR)/til2bm32.c $(HACK_H) $(TILE_H) $(INCL)/win32api.h + $(cc) $(CFLAGS) -I$(WSHR) -DTILE_X=32 -DTILE_Y=32 -o$@ $(WSHR)/til2bm32.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean + $(subst /,\,if exist graphicschk del graphicschk) + $(subst /,\,if exist $(INCL)/date.h del $(INCL)/date.h) + $(subst /,\,if exist $(INCL)/onames.h del $(INCL)/onames.h) + $(subst /,\,if exist $(INCL)/pm.h del $(INCL)/pm.h) + $(subst /,\,if exist $(INCL)/vis_tab.h del $(INCL)/vis_tab.h) + $(subst /,\,if exist $(SRC)/vis_tab.c del $(SRC)/vis_tab.c) + $(subst /,\,if exist $(SRC)/tile.c del $(SRC)/tile.c) + $(subst /,\,if exist $(U)*.lnk del $(U)*.lnk) + $(subst /,\,if exist $(U)*.map del $(U)*.map) + $(subst /,\,if exist $(DAT)/data del $(DAT)/data) + $(subst /,\,if exist $(DAT)/rumors del $(DAT)/rumors) + $(subst /,\,if exist $(DAT)/???-fil?.lev del $(DAT)/???-fil?.lev) + $(subst /,\,if exist $(DAT)/???-goal.lev del $(DAT)/???-goal.lev) + $(subst /,\,if exist $(DAT)/???-loca.lev del $(DAT)/???-loca.lev) + $(subst /,\,if exist $(DAT)/???-strt.lev del $(DAT)/???-strt.lev) + $(subst /,\,if exist $(DAT)/air.lev del $(DAT)/air.lev) + $(subst /,\,if exist $(DAT)/asmodeus.lev del $(DAT)/asmodeus.lev) + $(subst /,\,if exist $(DAT)/astral.lev del $(DAT)/astral.lev) + $(subst /,\,if exist $(DAT)/baalz.lev del $(DAT)/baalz.lev) + $(subst /,\,if exist $(DAT)/bigrm-*.lev del $(DAT)/bigrm-*.lev) + $(subst /,\,if exist $(DAT)/castle.lev del $(DAT)/castle.lev) + $(subst /,\,if exist $(DAT)/data del $(DAT)/data) + $(subst /,\,if exist $(DAT)/dungeon del $(DAT)/dungeon) + $(subst /,\,if exist $(DAT)/dungeon.pdf del $(DAT)/dungeon.pdf) + $(subst /,\,if exist $(DAT)/earth.lev del $(DAT)/earth.lev) + $(subst /,\,if exist $(DAT)/fakewiz?.lev del $(DAT)/fakewiz?.lev) + $(subst /,\,if exist $(DAT)/fire.lev del $(DAT)/fire.lev) + $(subst /,\,if exist $(DAT)/juiblex.lev del $(DAT)/juiblex.lev) + $(subst /,\,if exist $(DAT)/knox.lev del $(DAT)/knox.lev) + $(subst /,\,if exist $(DAT)/medusa-?.lev del $(DAT)/medusa-?.lev) + $(subst /,\,if exist $(DAT)/mine*.lev del $(DAT)/mine*.lev) + $(subst /,\,if exist $(DAT)/options del $(DAT)/options) + $(subst /,\,if exist $(DAT)/ttyoptions del $(DAT)/ttyoptions) + $(subst /,\,if exist $(DAT)/guioptions del $(DAT)/guioptions) + $(subst /,\,if exist $(DAT)/oracle.lev del $(DAT)/oracle.lev) + $(subst /,\,if exist $(DAT)/oracles del $(DAT)/oracles) + $(subst /,\,if exist $(DAT)/orcus.lev del $(DAT)/orcus.lev) + $(subst /,\,if exist $(DAT)/rumors del $(DAT)/rumors) + $(subst /,\,if exist $(DAT)/quest.dat del $(DAT)/quest.dat) + $(subst /,\,if exist $(DAT)/sanctum.lev del $(DAT)/sanctum.lev) + $(subst /,\,if exist $(DAT)/soko?-?.lev del $(DAT)/soko?-?.lev) + $(subst /,\,if exist $(DAT)/tower?.lev del $(DAT)/tower?.lev) + $(subst /,\,if exist $(DAT)/valley.lev del $(DAT)/valley.lev) + $(subst /,\,if exist $(DAT)/water.lev del $(DAT)/water.lev) + $(subst /,\,if exist $(DAT)/wizard?.lev del $(DAT)/wizard?.lev) + $(subst /,\,if exist $(O)sp_lev.tag del $(O)sp_lev.tag) + $(subst /,\,if exist $(SRC)/monstr.c del $(SRC)/monstr.c) + $(subst /,\,if exist $(SRC)/vis_tab.c del $(SRC)/vis_tab.c) + $(subst /,\,if exist $(U)recover.exe del $(U)recover.exe) + $(subst /,\,if exist $(DAT)/dlb.lst del $(DAT)/dlb.lst) + $(subst /,\,if exist nhdat. del nhdat.) + $(subst /,\,if exist $(O)install.tag del $(O)install.tag) + $(subst /,\,if exist $(O)obj.tag del $(O)obj.tag) + $(subst /,\,if exist $(O)gamedir.tag del $(O)gamedir.tag) +ifneq "$(OBJ)" "" + $(subst /,\,rmdir $(OBJ)) /s /Q +endif + +clean: + $(subst /,\,if exist $(O)*.o del $(O)*.o) + $(subst /,\,if exist $(O)utility.tag del $(O)utility.tag) + $(subst /,\,if exist $(U)makedefs.exe del $(U)makedefs.exe) + $(subst /,\,if exist $(U)lev_comp.exe del $(U)lev_comp.exe) + $(subst /,\,if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe) + $(subst /,\,if exist $(SRC)/*.lnk del $(SRC)/*.lnk) + $(subst /,\,if exist $(SRC)/*.map del $(SRC)/*.map) + $(subst /,\,if exist $(TILEBMP16) del $(TILEBMP16)) + $(subst /,\,if exist $(TILEBMP32) del $(TILEBMP32)) + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)/data: $(O)utility.tag $(DATABASE) + $(subst /,\,$(U)makedefs -d) + +$(DAT)/rumors: $(O)utility.tag $(DAT)/rumors.tru $(DAT)/rumors.fal + $(subst /,\,$(U)makedefs -r) + +$(DAT)/quest.dat: $(O)utility.tag $(DAT)/quest.txt + $(subst /,\,$(U)makedefs -q) + +$(DAT)/oracles: $(O)utility.tag $(DAT)/oracles.txt + $(subst /,\,$(U)makedefs -h) + +$(DAT)/dungeon: $(O)utility.tag $(DAT)/dungeon.def + $(subst /,\,$(U)makedefs -e) + $(subst /,\,$(U)dgn_comp $(DAT)/dungeon.pdf) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)/win32api.h $(NTSYS)/nttty.c + $(cc) $(CFLAGS) -I$(WSHR) -o$@ $(NTSYS)/nttty.c +$(O)winnt.o: $(HACK_H) $(INCL)/win32api.h $(NTSYS)/winnt.c + $(cc) $(CFLAGS) -o$@ $(NTSYS)/winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)/ntsound.c + $(cc) $(CFLAGS) -o$@ $(NTSYS)/ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)/nhlan.h $(NTSYS)/mapimail.c + $(cc) $(CFLAGS) -DMAPI_VERBOSE -o$@ $(NTSYS)/mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * -c (which is included in CFLAGS) substituted +# with -o$@ +# * targets prefixed with $(O) +# * $(CC) changed to $(cc) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ../sys/atari/tos.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../sys/atari/tos.c +$(O)pcmain.o: ../sys/share/pcmain.c $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/win32api.h + $(cc) $(CFLAGS) -o$@ ../sys/share/pcmain.c +$(O)pcsys.o: ../sys/share/pcsys.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pcsys.c +$(O)pctty.o: ../sys/share/pctty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pctty.c +$(O)pcunix.o: ../sys/share/pcunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/pcunix.c +$(O)random.o: ../sys/share/random.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/random.c +$(O)ioctl.o: ../sys/share/ioctl.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../sys/share/ioctl.c +$(O)unixtty.o: ../sys/share/unixtty.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/share/unixtty.c +$(O)unixmain.o: ../sys/unix/unixmain.c $(HACK_H) $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixmain.c +$(O)unixunix.o: ../sys/unix/unixunix.c $(HACK_H) + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixunix.c +$(O)unixres.o: ../sys/unix/unixres.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../sys/unix/unixres.c +$(O)bemain.o: ../sys/be/bemain.c $(HACK_H) $(INCL)/dlb.h + $(cc) $(CFLAGS) -o$@ ../sys/be/bemain.c +$(O)getline.o: ../win/tty/getline.c $(HACK_H) $(INCL)/func_tab.h + $(cc) $(CFLAGS) -o$@ ../win/tty/getline.c +$(O)termcap.o: ../win/tty/termcap.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/termcap.c +$(O)topl.o: ../win/tty/topl.c $(HACK_H) $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/topl.c +$(O)wintty.o: ../win/tty/wintty.c $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/patchlevel.h $(INCL)/tcap.h + $(cc) $(CFLAGS) -o$@ ../win/tty/wintty.c +$(O)Window.o: ../win/X11/Window.c $(INCL)/xwindowp.h $(INCL)/xwindow.h \ + $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../win/X11/Window.c +$(O)dialogs.o: ../win/X11/dialogs.c $(CONFIG_H) + $(cc) $(CFLAGS) -o$@ ../win/X11/dialogs.c +$(O)winX.o: ../win/X11/winX.c $(HACK_H) $(INCL)/winX.h $(INCL)/dlb.h \ + $(INCL)/patchlevel.h ../win/X11/nh72icon \ + ../win/X11/nh56icon ../win/X11/nh32icon + $(cc) $(CFLAGS) -o$@ ../win/X11/winX.c +$(O)winmap.o: ../win/X11/winmap.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/dlb.h \ + $(INCL)/winX.h $(INCL)/tile2x11.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmap.c +$(O)winmenu.o: ../win/X11/winmenu.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmenu.c +$(O)winmesg.o: ../win/X11/winmesg.c $(INCL)/xwindow.h $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmesg.c +$(O)winmisc.o: ../win/X11/winmisc.c $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winmisc.c +$(O)winstat.o: ../win/X11/winstat.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winstat.c +$(O)wintext.o: ../win/X11/wintext.c $(HACK_H) $(INCL)/winX.h $(INCL)/xwindow.h + $(cc) $(CFLAGS) -o$@ ../win/X11/wintext.c +$(O)winval.o: ../win/X11/winval.c $(HACK_H) $(INCL)/winX.h + $(cc) $(CFLAGS) -o$@ ../win/X11/winval.c +$(O)tile.o: tile.c $(HACK_H) +$(O)gnaskstr.o: ../win/gnome/gnaskstr.c ../win/gnome/gnaskstr.h \ + ../win/gnome/gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnaskstr.c +$(O)gnbind.o: ../win/gnome/gnbind.c ../win/gnome/gnbind.h ../win/gnome/gnmain.h \ + ../win/gnome/gnaskstr.h ../win/gnome/gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnbind.c +$(O)gnglyph.o: ../win/gnome/gnglyph.c ../win/gnome/gnglyph.h $(INCL)/tile2x11.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnglyph.c +$(O)gnmain.o: ../win/gnome/gnmain.c ../win/gnome/gnmain.h ../win/gnome/gnsignal.h \ + ../win/gnome/gnbind.h ../win/gnome/gnopts.h $(HACK_H) \ + $(INCL)/date.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmain.c +$(O)gnmap.o: ../win/gnome/gnmap.c ../win/gnome/gnmap.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmap.c +$(O)gnmenu.o: ../win/gnome/gnmenu.c ../win/gnome/gnmenu.h ../win/gnome/gnmain.h \ + ../win/gnome/gnbind.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmenu.c +$(O)gnmesg.o: ../win/gnome/gnmesg.c ../win/gnome/gnmesg.h ../win/gnome/gnsignal.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnmesg.c +$(O)gnopts.o: ../win/gnome/gnopts.c ../win/gnome/gnopts.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnmain.h ../win/gnome/gnmap.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnopts.c +$(O)gnplayer.o: ../win/gnome/gnplayer.c ../win/gnome/gnplayer.h \ + ../win/gnome/gnmain.h $(HACK_H) + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnplayer.c +$(O)gnsignal.o: ../win/gnome/gnsignal.c ../win/gnome/gnsignal.h \ + ../win/gnome/gnmain.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnsignal.c +$(O)gnstatus.o: ../win/gnome/gnstatus.c ../win/gnome/gnstatus.h \ + ../win/gnome/gnsignal.h ../win/gnome/gn_xpms.h \ + ../win/gnome/gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnstatus.c +$(O)gntext.o: ../win/gnome/gntext.c ../win/gnome/gntext.h ../win/gnome/gnmain.h \ + ../win/gnome/gn_rip.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gntext.c +$(O)gnworn.o: ../win/gnome/gnworn.c ../win/gnome/gnworn.h ../win/gnome/gnglyph.h \ + ../win/gnome/gnsignal.h ../win/gnome/gnomeprv.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnworn.c +$(O)gnyesno.o: ../win/gnome/gnyesno.c ../win/gnome/gnbind.h ../win/gnome/gnyesno.h + $(cc) $(CFLAGS) $(GNOMEINC) -o$@ ../win/gnome/gnyesno.c +$(O)wingem.o: ../win/gem/wingem.c $(HACK_H) $(INCL)/func_tab.h $(INCL)/dlb.h \ + $(INCL)/patchlevel.h $(INCL)/wingem.h + $(cc) $(CFLAGS) -o$@ ../win/gem/wingem.c +$(O)wingem1.o: ../win/gem/wingem1.c $(INCL)/gem_rsc.h $(INCL)/load_img.h \ + $(INCL)/gr_rect.h $(INCL)/wintype.h $(INCL)/wingem.h + $(cc) $(CFLAGS) -o$@ ../win/gem/wingem1.c +$(O)load_img.o: ../win/gem/load_img.c $(INCL)/load_img.h + $(cc) $(CFLAGS) -o$@ ../win/gem/load_img.c +$(O)gr_rect.o: ../win/gem/gr_rect.c $(INCL)/gr_rect.h + $(cc) $(CFLAGS) -o$@ ../win/gem/gr_rect.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ../win/Qt/qt_win.cpp $(HACK_H) $(INCL)/func_tab.h \ + $(INCL)/dlb.h $(INCL)/patchlevel.h $(INCL)/tile2x11.h \ + $(INCL)/qt_win.h $(INCL)/qt_clust.h $(INCL)/qt_kde0.h \ + $(INCL)/qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_win.cpp +$(O)qt_clust.o: ../win/Qt/qt_clust.cpp $(INCL)/qt_clust.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qt_clust.cpp +$(O)qttableview.o: ../win/Qt/qttableview.cpp $(INCL)/qttableview.h + $(CXX) $(CXXFLAGS) -o$@ ../win/Qt/qttableview.cpp +$(O)monstr.o: monstr.c $(CONFIG_H) +$(O)vis_tab.o: vis_tab.c $(CONFIG_H) $(INCL)/vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)/edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)/artifact.h $(INCL)/artilist.h +$(O)attrib.o: attrib.c $(HACK_H) +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)/lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)/func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)/artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)/edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)/dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)/lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)/edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)/eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)/edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)/tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)/dgn_file.h $(INCL)/dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)/eshk.h $(INCL)/dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)/lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)/dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) +$(O)light.o: light.c $(HACK_H) $(INCL)/lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)/mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)/epri.h $(INCL)/emin.h \ + $(INCL)/edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)/artifact.h $(INCL)/edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)/emin.h $(INCL)/epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)/sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)/sp_lev.h $(INCL)/lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)/eshk.h $(INCL)/epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/artifact.h \ + $(INCL)/epri.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)/permonst.h $(INCL)/align.h \ + $(INCL)/monattk.h $(INCL)/monflag.h $(INCL)/monsym.h \ + $(INCL)/dungeon.h $(INCL)/eshk.h $(INCL)/vault.h \ + $(INCL)/epri.h $(INCL)/color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)/edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)/lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)/obj.h $(INCL)/objclass.h \ + $(INCL)/prop.h $(INCL)/skills.h $(INCL)/color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)/objclass.h $(INCL)/flag.h \ + $(HACK_H) $(INCL)/tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)/dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)/epri.h $(INCL)/edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)/epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)/mfndpos.h $(INCL)/eshk.h \ + $(INCL)/epri.h $(INCL)/emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)/qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)/dlb.h $(INCL)/qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)/lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)/lev.h $(INCL)/tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)/lev.h $(INCL)/dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)/lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)/eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)/eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)/artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)/edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)/dlb.h $(INCL)/sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)/lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)/dlb.h $(INCL)/patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)/vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)/date.h $(INCL)/patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)/vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)/wingem.h $(INCL)/winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)/qtext.h $(INCL)/epri.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)/lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file diff --git a/sys/winnt/Makefile.msc b/sys/winnt/Makefile.msc new file mode 100644 index 0000000..832b857 --- /dev/null +++ b/sys/winnt/Makefile.msc @@ -0,0 +1,1419 @@ +# SCCS Id: @(#)Makefile.msc 3.4 $Date: 2003/11/16 04:50:58 $ +# Copyright (c) NetHack PC Development Team 1993-2003 +# +# NetHack 3.4.x Makefile for MS Visual C++ V6.x and above and MS NMAKE +# +# Win32 Compilers Tested: +# - Microsoft 32 bit Visual C++ V4.x +# - Microsoft 32 bit Visual C++ V6.0 SP3, SP4 +# +# This is used for building two versions of NetHack: +# A tty port utilizing the Win32 Console I/O subsystem, Console +# NetHack; +# A Win32 native port built on the Windows API, Graphical NetHack or +# NetHackW. +# +# In addition to your C compiler, +# +# if you want to change you will need a +# files with suffix workalike for +# .y yacc (such as bison) +# .l lex (such as flex) +# +# +# If you have any questions read the sys/winnt/Install.nt file included +# with the distribution. +#============================================================================== +# Do not delete the following 3 lines. +# +TARGETOS=BOTH +APPVER=4.0 +!include + +# Graphical interface +# Set to Y for a graphical version + +#GRAPHICAL = Y + +# Set the gamedir according to your preference. +# If not present prior to compilation it gets created. + +!IF "$(GRAPHICAL)" == "Y" +GAME = NetHackW # Game Name +!ELSE +GAME = NetHack # Game Name +!ENDIF + +GAMEDIR = ..\binary # Game directory + +# +# Source directories. Makedefs hardcodes these, don't change them. +# + +INCL = ..\include # NetHack include files +DAT = ..\dat # NetHack data files +DOC = ..\doc # NetHack documentation files +UTIL = ..\util # Utility source +SRC = ..\src # Main source +SSYS = ..\sys\share # Shared system files +NTSYS = ..\sys\winnt # NT Win32 specific files +TTY = ..\win\tty # window port files (tty) +WIN32 = ..\win\win32 # window port files (Win32) +WSHR = ..\win\share # Tile support files + +# +# Object directory. +# + +OBJ = o + + +# +#========================================== +# Exe File Info. +#========================================== + +# Yacc/Lex ... if you got 'em. +# +# If you have yacc and lex programs (or work-alike such as bison +# and flex), comment out the upper two macros and uncomment +# the lower two. +# + +DO_YACC = YACC_MSG +DO_LEX = LEX_MSG +#DO_YACC = YACC_ACT +#DO_LEX = LEX_ACT + +# - Specify your yacc and lex programs (or work-alikes) here. + +#YACC = bison -y +YACC = byacc +#YACC = yacc + +#LEX = lex +LEX = flex + +# +# - Specify your flex skeleton file (if needed). +# + +FLEXSKEL = +#FLEXSKEL = -S../tools/flex.ske + +YTABC = y_tab.c +YTABH = y_tab.h +LEXYYC = lexyy.c + +# +# Optional high-quality BSD random number generation routines +# (see pcconf.h). Set to nothing if not used. +# + +RANDOM = $(OBJ)\random.o +#RANDOM = + +# +# Leave the next two lines uncommented _ONLY_ if you do NOT want any +# debug capability in the object files, or in the NetHack executable. +# Comment them if you want debug capability. + +#cdebug = +#linkdebug = + +# +# Compiler and Linker flags +# + +PRECOMPHEAD = N # set to Y if you want to use precomp. headers + +#=============================================== +#======= End of Modification Section =========== +#=============================================== +################################################ +# # +# Nothing below here should have to be changed.# +# # +################################################ + +!IF "$(GRAPHICAL)" == "Y" +WINPORT = $(O)tile.o $(O)mhaskyn.o $(O)mhdlg.o \ + $(O)mhfont.o $(O)mhinput.o $(O)mhmain.o $(O)mhmap.o \ + $(O)mhmenu.o $(O)mhmsgwnd.o $(O)mhrip.o $(O)mhsplash.o \ + $(O)mhstatus.o $(O)mhtext.o $(O)mswproc.o $(O)winhack.o +WINPHDR = $(WIN32)\mhaskyn.h $(WIN32)\mhdlg.h $(WIN32)\mhfont.h \ + $(WIN32)\mhinput.h $(WIN32)\mhmain.h $(WIN32)\mhmap.h $(WIN32)\mhmenu.h \ + $(WIN32)\mhmsg.h $(WIN32)\mhmsgwnd.h $(WIN32)\mhrip.h $(WIN32)\mhstatus.h \ + $(WIN32)\mhtext.h $(WIN32)\resource.h $(WIN32)\winMS.h +WINDLLS = +WINPFLAG= -DTILES -DMSWIN_GRAPHICS +NHRES = $(O)winhack.res +WINPINC = -I$(WIN32) +!ELSE +WINPORT = $(O)nttty.o +WINPHDR = +WINDLLS = $(GAMEDIR)\nhdefkey.dll $(GAMEDIR)\nh340key.dll $(GAMEDIR)\nhraykey.dll +WINPFLAG= -DWIN32CON +NHRES = $(O)console.res +WINPINC = +!ENDIF + +TILEUTIL16 = $(UTIL)\tile2bmp.exe +TILEBMP16 = $(SRC)\tiles.bmp + +TILEUTIL32 = $(UTIL)\til2bm32.exe +TILEBMP32 = $(SRC)\tiles32.bmp + +SOUND = $(OBJ)\ntsound.o + +#SOUND = + +# To store all the level files, +# help files, etc. in a single library file. +# USE_DLB = Y is left uncommented + +USE_DLB = Y + +! IF ("$(USE_DLB)"=="Y") +DLBFLG = -DDLB +! ELSE +DLBFLG = +! ENDIF + +#========================================== +# Setting up the compiler and linker +# macros. All builds include the base ones. +#========================================== + +CFLAGSBASE = -c $(cflags) $(cvarsmt) -I$(INCL) -nologo $(cdebug) $(WINPINC) +LFLAGSBASEC = $(linkdebug) /NODEFAULTLIB /INCREMENTAL:NO /RELEASE /NOLOGO -subsystem:console,4.0 $(conlibsmt) +LFLAGSBASEG = $(linkdebug) $(guiflags) $(guilibsmt) comctl32.lib + +#========================================== +# Util builds +#========================================== + +CFLAGSU = $(CFLAGSBASE) $(WINPFLAG) +LFLAGSU = $(LFLAGSBASEC) + +#========================================== +# - Game build +#========================================== +LFLAGSBASE = $(linkdebug) /NODEFAULTLIB /INCREMENTAL:NO /RELEASE /NOLOGO -subsystem:console,4.0 $(conlibsmt) +CFLAGS = $(CFLAGSBASE) $(WINPFLAG) $(DLBFLG) +NHLFLAGS1 = /NODEFAULTLIB /INCREMENTAL:NO /PDB:"$(GAME).PDB" /RELEASE /NOLOGO +NHLFLAGS2 = /MAP:"$(GAME).MAP" /MACHINE:$(CPU) -IGNORE:505 +!IF ("$(GRAPHICAL)"=="Y") +LFLAGS = $(LFLAGSBASEG) $(NHLFLAGS1) $(NHLFLAGS2) +!ELSE +LFLAGS = $(LFLAGSBASEC) $(NHLFLAGS1) $(NHLFLAGS2) +!ENDIF + +GAMEFILE = $(GAMEDIR)\$(GAME).exe # whole thing + +! IF ("$(USE_DLB)"=="Y") +DLB = nhdat +! ELSE +DLB = +! ENDIF + +#========================================== +#================ RULES ================== +#========================================== + +.SUFFIXES: .exe .o .til .uu .c .y .l + +#========================================== +# Rules for files in src +#========================================== + +.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -Fo$@ $< + +{$(SRC)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +#========================================== +# Rules for files in sys\share +#========================================== + +{$(SSYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +#========================================== +# Rules for files in sys\winnt +#========================================== + +{$(NTSYS)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +{$(NTSYS)}.h{$(INCL)}.h: + @copy $< $@ + +#========================================== +# Rules for files in util +#========================================== + +{$(UTIL)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGSU) -Fo$@ $< + +#========================================== +# Rules for files in win\share +#========================================== + +{$(WSHR)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + +{$(WSHR)}.h{$(INCL)}.h: + @copy $< $@ + +#{$(WSHR)}.txt{$(DAT)}.txt: +# @copy $< $@ + +#========================================== +# Rules for files in win\tty +#========================================== + +{$(TTY)}.c{$(OBJ)}.o: + @$(CC) $(CFLAGS) -Fo$@ $< + + +#========================================== +# Rules for files in win\win32 +#========================================== + +{$(WIN32)}.c{$(OBJ)}.o: + @$(cc) $(CFLAGS) -Fo$@ $< + +#========================================== +#================ MACROS ================== +#========================================== +# This section creates shorthand macros for many objects +# referenced later on in the Makefile. +# + +DEFFILE = $(NTSYS)\$(GAME).def + +# +# Shorten up the location for some files +# + +O = $(OBJ)^\ + +U = $(UTIL)^\ + +# +# Utility Objects. +# + +MAKESRC = $(U)makedefs.c + +SPLEVSRC = $(U)lev_yacc.c $(U)lev_$(LEX).c $(U)lev_main.c $(U)panic.c + +DGNCOMPSRC = $(U)dgn_yacc.c $(U)dgn_$(LEX).c $(U)dgn_main.c + +MAKEOBJS = $(O)makedefs.o $(O)monst.o $(O)objects.o + +SPLEVOBJS = $(O)lev_yacc.o $(O)lev_$(LEX).o $(O)lev_main.o \ + $(O)alloc.o $(O)decl.o $(O)drawing.o \ + $(O)monst.o $(O)objects.o $(O)panic.o + +DGNCOMPOBJS = $(O)dgn_yacc.o $(O)dgn_$(LEX).o $(O)dgn_main.o \ + $(O)alloc.o $(O)panic.o + +RECOVOBJS = $(O)recover.o + +TILEFILES = $(WSHR)\monsters.txt $(WSHR)\objects.txt $(WSHR)\other.txt + +# +# These are not invoked during a normal game build in 3.4 +# +TEXT_IO = $(O)tiletext.o $(O)tiletxt.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +TEXT_IO32 = $(O)tilete32.o $(O)tiletx32.o $(O)drawing.o \ + $(O)decl.o $(O)monst.o $(O)objects.o + +GIFREADERS = $(O)gifread.o $(O)alloc.o $(O)panic.o +GIFREADERS32 = $(O)gifrd32.o $(O)alloc.o $(O)panic.o + +PPMWRITERS = $(O)ppmwrite.o $(O)alloc.o $(O)panic.o + +# +# Object files for the game itself. +# + +VOBJ01 = $(O)allmain.o $(O)alloc.o $(O)apply.o $(O)artifact.o +VOBJ02 = $(O)attrib.o $(O)ball.o $(O)bones.o $(O)botl.o +VOBJ03 = $(O)cmd.o $(O)dbridge.o $(O)decl.o $(O)detect.o +VOBJ04 = $(O)dig.o $(O)display.o $(O)do.o $(O)do_name.o +VOBJ05 = $(O)do_wear.o $(O)dog.o $(O)dogmove.o $(O)dokick.o +VOBJ06 = $(O)dothrow.o $(O)drawing.o $(O)dungeon.o $(O)eat.o +VOBJ07 = $(O)end.o $(O)engrave.o $(O)exper.o $(O)explode.o +VOBJ08 = $(O)extralev.o $(O)files.o $(O)fountain.o $(O)hack.o +VOBJ09 = $(O)hacklib.o $(O)invent.o $(O)light.o $(O)lock.o +VOBJ10 = $(O)mail.o $(O)pcmain.o $(O)makemon.o $(O)mapglyph.o $(O)mcastu.o +VOBJ11 = $(O)mhitm.o $(O)mhitu.o $(O)minion.o $(O)mklev.o +VOBJ12 = $(O)mkmap.o $(O)mkmaze.o $(O)mkobj.o $(O)mkroom.o +VOBJ13 = $(O)mon.o $(O)mondata.o $(O)monmove.o $(O)monst.o +VOBJ14 = $(O)monstr.o $(O)mplayer.o $(O)mthrowu.o $(O)muse.o +VOBJ15 = $(O)music.o $(O)o_init.o $(O)objects.o $(O)objnam.o +VOBJ16 = $(O)options.o $(O)pager.o $(O)pickup.o $(O)pline.o +VOBJ17 = $(O)polyself.o $(O)potion.o $(O)pray.o $(O)priest.o +VOBJ18 = $(O)quest.o $(O)questpgr.o $(RANDOM) $(O)read.o +VOBJ19 = $(O)rect.o $(O)region.o $(O)restore.o $(O)rip.o +VOBJ20 = $(O)rnd.o $(O)role.o $(O)rumors.o $(O)save.o +VOBJ21 = $(O)shk.o $(O)shknam.o $(O)sit.o $(O)sounds.o +VOBJ22 = $(O)sp_lev.o $(O)spell.o $(O)steal.o $(O)steed.o +VOBJ23 = $(O)teleport.o $(O)timeout.o $(O)topten.o $(O)track.o +VOBJ24 = $(O)trap.o $(O)u_init.o $(O)uhitm.o $(O)vault.o +VOBJ25 = $(O)vis_tab.o $(O)vision.o $(O)weapon.o $(O)were.o +VOBJ26 = $(O)wield.o $(O)windows.o $(O)wizard.o $(O)worm.o +VOBJ27 = $(O)worn.o $(O)write.o $(O)zap.o + +DLBOBJ = $(O)dlb.o + +TTYOBJ = $(O)topl.o $(O)getline.o $(O)wintty.o + +SOBJ = $(O)winnt.o $(O)pcsys.o $(O)pcunix.o \ + $(SOUND) $(O)mapimail.o $(O)nhlan.o + +OBJS = $(VOBJ01) $(VOBJ02) $(VOBJ03) $(VOBJ04) $(VOBJ05) \ + $(VOBJ06) $(VOBJ07) $(VOBJ08) $(VOBJ09) $(VOBJ10) \ + $(VOBJ11) $(VOBJ12) $(VOBJ13) $(VOBJ14) $(VOBJ15) \ + $(VOBJ16) $(VOBJ17) $(VOBJ18) $(VOBJ19) $(VOBJ20) \ + $(VOBJ21) $(VOBJ22) $(VOBJ23) $(VOBJ24) $(VOBJ25) \ + $(VOBJ26) $(VOBJ27) + +WINPOBJ = $(WINPORT) + +VVOBJ = $(O)version.o + +ALLOBJ = $(WINPOBJ) $(SOBJ) $(DLBOBJ) $(TTYOBJ) $(WOBJ) $(OBJS) $(VVOBJ) + +!IF "$(GRAPHICAL)" == "Y" +OPTIONS_FILE = $(DAT)\guioptions +!ELSE +OPTIONS_FILE = $(DAT)\ttyoptions +!ENDIF +#========================================== +# Header file macros +#========================================== + +CONFIG_H = $(INCL)\config.h $(INCL)\config1.h $(INCL)\tradstdc.h \ + $(INCL)\global.h $(INCL)\coord.h $(INCL)\vmsconf.h \ + $(INCL)\system.h $(INCL)\unixconf.h $(INCL)\os2conf.h \ + $(INCL)\micro.h $(INCL)\pcconf.h $(INCL)\tosconf.h \ + $(INCL)\amiconf.h $(INCL)\macconf.h $(INCL)\beconf.h \ + $(INCL)\ntconf.h $(INCL)\nhlan.h + +HACK_H = $(INCL)\hack.h $(CONFIG_H) $(INCL)\align.h \ + $(INCL)\dungeon.h $(INCL)\monsym.h $(INCL)\mkroom.h \ + $(INCL)\objclass.h $(INCL)\youprop.h $(INCL)\prop.h \ + $(INCL)\permonst.h $(INCL)\monattk.h \ + $(INCL)\monflag.h $(INCL)\mondata.h $(INCL)\pm.h \ + $(INCL)\wintype.h $(INCL)\decl.h $(INCL)\quest.h \ + $(INCL)\spell.h $(INCL)\color.h $(INCL)\obj.h \ + $(INCL)\you.h $(INCL)\attrib.h $(INCL)\monst.h \ + $(INCL)\skills.h $(INCL)\onames.h $(INCL)\timeout.h \ + $(INCL)\trap.h $(INCL)\flag.h $(INCL)\rm.h \ + $(INCL)\vision.h $(INCL)\display.h $(INCL)\engrave.h \ + $(INCL)\rect.h $(INCL)\region.h $(INCL)\winprocs.h \ + $(INCL)\wintty.h $(INCL)\trampoli.h + +LEV_H = $(INCL)\lev.h +DGN_FILE_H = $(INCL)\dgn_file.h +LEV_COMP_H = $(INCL)\lev_comp.h +SP_LEV_H = $(INCL)\sp_lev.h +TILE_H = ..\win\share\tile.h + +#========================================== +# Miscellaneous +#========================================== + +DATABASE = $(DAT)\data.base + +# +# The name of the game. +# + +GAMEFILE = $(GAMEDIR)\$(GAME).exe + +#========================================== +#=============== TARGETS ================== +#========================================== + +# +# The default make target (so just typing 'nmake' is useful). +# +default : $(GAMEFILE) + +# +# The main target. +# + +$(GAME): $(O)obj.tag $(O)utility.tag envchk $(GAMEFILE) + @echo $(GAME) is up to date. + +# +# Everything +# + +all : install + +install: envchk $(GAME) $(O)install.tag + @echo Done. + +$(O)install.tag: $(DAT)\data $(DAT)\rumors $(DAT)\dungeon \ + $(DAT)\oracles $(DAT)\quest.dat $(O)sp_lev.tag $(DLB) +! IF ("$(USE_DLB)"=="Y") + copy nhdat $(GAMEDIR) + copy $(DAT)\license $(GAMEDIR) + copy $(DAT)\opthelp $(GAMEDIR) +! ELSE + copy $(DAT)\*. $(GAMEDIR) + copy $(DAT)\*.dat $(GAMEDIR) + copy $(DAT)\*.lev $(GAMEDIR) + if exist $(GAMEDIR)\makefile del $(GAMEDIR)\makefile +! ENDIF + if exist $(DOC)\guidebook.txt copy $(DOC)\guidebook.txt $(GAMEDIR)\Guidebook.txt + if exist $(DOC)\nethack.txt copy $(DOC)\nethack.txt $(GAMEDIR)\NetHack.txt + @if exist $(SRC)\$(GAME).PDB copy $(SRC)\$(GAME).pdb $(GAMEDIR)\$(GAME).pdb + @if exist $(GAMEDIR)\$(GAME).PDB echo NOTE: You may want to remove $(GAMEDIR)\$(GAME).pdb to conserve space + -copy $(NTSYS)\defaults.nh $(GAMEDIR)\defaults.nh + echo install done > $@ + +# copy $(NTSYS)\winnt.hlp $(GAMEDIR) + +recover: $(U)recover.exe + if exist $(U)recover.exe copy $(U)recover.exe $(GAMEDIR) + if exist $(DOC)\recover.txt copy $(DOC)\recover.txt $(GAMEDIR)\recover.txt + +$(O)sp_lev.tag: $(O)utility.tag $(DAT)\bigroom.des $(DAT)\castle.des \ + $(DAT)\endgame.des $(DAT)\gehennom.des $(DAT)\knox.des \ + $(DAT)\medusa.des $(DAT)\oracle.des $(DAT)\tower.des \ + $(DAT)\yendor.des $(DAT)\arch.des $(DAT)\barb.des \ + $(DAT)\caveman.des $(DAT)\healer.des $(DAT)\knight.des \ + $(DAT)\monk.des $(DAT)\priest.des $(DAT)\ranger.des \ + $(DAT)\rogue.des $(DAT)\samurai.des $(DAT)\sokoban.des \ + $(DAT)\tourist.des $(DAT)\valkyrie.des $(DAT)\wizard.des + cd $(DAT) + $(U)lev_comp bigroom.des + $(U)lev_comp castle.des + $(U)lev_comp endgame.des + $(U)lev_comp gehennom.des + $(U)lev_comp knox.des + $(U)lev_comp mines.des + $(U)lev_comp medusa.des + $(U)lev_comp oracle.des + $(U)lev_comp sokoban.des + $(U)lev_comp tower.des + $(U)lev_comp yendor.des + $(U)lev_comp arch.des + $(U)lev_comp barb.des + $(U)lev_comp caveman.des + $(U)lev_comp healer.des + $(U)lev_comp knight.des + $(U)lev_comp monk.des + $(U)lev_comp priest.des + $(U)lev_comp ranger.des + $(U)lev_comp rogue.des + $(U)lev_comp samurai.des + $(U)lev_comp tourist.des + $(U)lev_comp valkyrie.des + $(U)lev_comp wizard.des + cd $(SRC) + echo sp_levs done > $(O)sp_lev.tag + +$(O)utility.tag: $(INCL)\date.h $(INCL)\onames.h $(INCL)\pm.h \ + $(SRC)\monstr.c $(SRC)\vis_tab.c \ + $(U)lev_comp.exe $(INCL)\vis_tab.h \ + $(U)dgn_comp.exe + @echo utilities made >$@ + @echo utilities made. + +tileutil: $(U)gif2txt.exe $(U)gif2tx32.exe $(U)txt2ppm.exe + @echo Optional tile development utilities are up to date. + +!IF "$(GRAPHICAL)"=="Y" +$(NHRES): $(TILEBMP16) $(WIN32)\winhack.rc $(WIN32)\mnsel.bmp \ + $(WIN32)\mnselcnt.bmp $(WIN32)\mnunsel.bmp \ + $(WIN32)\petmark.bmp $(WIN32)\NetHack.ico $(WIN32)\rip.bmp \ + $(WIN32)\splash.bmp + @$(rc) -r -fo$@ -i$(WIN32) -dNDEBUG $(WIN32)\winhack.rc +!ELSE +$(NHRES): $(NTSYS)\console.rc $(NTSYS)\NetHack.ico + @$(rc) -r -fo$@ -i$(NTSYS) -dNDEBUG $(NTSYS)\console.rc +!ENDIF + +#========================================== +# The main target. +#========================================== + +# The section for linking the NetHack image looks a little strange at +# first, especially if you are used to UNIX makes, or NDMAKE. It is +# Microsoft nmake specific, and it gets around the problem of the +# link command line being too long for the linker. An "in-line" linker +# response file is generated temporarily. +# +# It takes advantage of the following features of nmake: +# +# Inline files : +# Specifying the "<<" means to start an inline file. +# Another "<<" at the start of a line closes the +# inline file. +# +# Substitution within Macros: +# $(mymacro:string1=string2) replaces every +# occurrence of string1 with string2 in the +# macro mymacro. Special ascii key codes may be +# used in the substitution text by preceding it +# with ^ as we have done below. Every occurence +# of a in $(ALLOBJ) is replaced by +# <+>. +# +# DO NOT INDENT THE << below! +# + +$(GAMEFILE) : $(ALLOBJ) $(NHRES) $(O)gamedir.tag $(WINDLLS) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo Linking.... + $(link) $(LFLAGS) user32.lib winmm.lib -out:$@ @<<$(GAME).lnk + $(ALLOBJ:^ =^ + ) $(NHRES) +<< + @if exist $(O)install.tag del $(O)install.tag + @if exist $(GAMEDIR)\$(GAME).bak del $(GAMEDIR)\$(GAME).bak + +$(O)gamedir.tag: + @if not exist $(GAMEDIR)\*.* echo creating directory $(GAMEDIR) + @if not exist $(GAMEDIR)\*.* mkdir $(GAMEDIR) + @echo directory created > $@ + +$(O)nhdefkey.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nhdefkey.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +$(O)nh340key.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nh340key.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +$(O)nhraykey.def: + @echo EXPORTS >$@ + @echo ProcessKeystroke >>$@ + @echo NHkbhit >>$@ + @echo CheckInput >>$@ + @echo SourceWhere >>$@ + @echo SourceAuthor >>$@ + @echo KeyHandlerName >>$@ + +$(GAMEDIR)\nhraykey.dll : $(O)$(@B).o $(O)gamedir.tag $(O)$(@B).def + @echo Linking $@ + @$(link) -debug:full -debugtype:cv /RELEASE /NOLOGO /DLL user32.lib \ + /PDB:"$(@B).PDB" /MAP:"$(@B).map" /DEF:$(O)$(@B).def \ + /IMPLIB:$(O)$(@B).lib -out:$@ $(O)$(@B).o + +# +# Secondary Targets. +# + +#========================================== +# Makedefs Stuff +#========================================== + +$(U)makedefs.exe: $(MAKEOBJS) + @$(link) $(LFLAGSU) -out:$@ $(MAKEOBJS) + +$(O)makedefs.o: $(CONFIG_H) $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\objclass.h \ + $(INCL)\monsym.h $(INCL)\qtext.h $(INCL)\patchlevel.h \ + $(U)makedefs.c + @if not exist $(OBJ)\*.* echo creating directory $(OBJ) + @if not exist $(OBJ)\*.* mkdir $(OBJ) + @$(CC) $(CFLAGSU) -Fo$@ $(U)makedefs.c + +# +# date.h should be remade every time any of the source or include +# files is modified. +# + +$(INCL)\date.h $(OPTIONS_FILE) : $(U)makedefs.exe + $(U)makedefs -v + +$(INCL)\onames.h : $(U)makedefs.exe + $(U)makedefs -o + +$(INCL)\pm.h : $(U)makedefs.exe + $(U)makedefs -p + +#$(INCL)\trap.h : $(U)makedefs.exe +# $(U)makedefs -t + +$(SRC)\monstr.c: $(U)makedefs.exe + $(U)makedefs -m + +$(INCL)\vis_tab.h: $(U)makedefs.exe + $(U)makedefs -z + +$(SRC)\vis_tab.c: $(U)makedefs.exe + $(U)makedefs -z + +#========================================== +# uudecode utility and uuencoded targets +#========================================== + +$(U)uudecode.exe: $(O)uudecode.o + @$(link) $(LFLAGSU) -out:$@ $(O)uudecode.o + +$(O)uudecode.o: $(SSYS)\uudecode.c + +$(NTSYS)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(NTSYS) + ..\..\util\uudecode.exe nhico.uu + chdir ..\..\src + +$(WIN32)\NetHack.ico : $(U)uudecode.exe $(NTSYS)\nhico.uu + chdir $(WIN32) + ..\..\util\uudecode.exe ../../sys/winnt/nhico.uu + chdir ..\..\src + +$(WIN32)\mnsel.bmp: $(U)uudecode.exe $(WIN32)\mnsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnsel.uu + chdir ..\..\src + +$(WIN32)\mnselcnt.bmp: $(U)uudecode.exe $(WIN32)\mnselcnt.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnselcnt.uu + chdir ..\..\src + +$(WIN32)\mnunsel.bmp: $(U)uudecode.exe $(WIN32)\mnunsel.uu + chdir $(WIN32) + ..\..\util\uudecode.exe mnunsel.uu + chdir ..\..\src + +$(WIN32)\petmark.bmp: $(U)uudecode.exe $(WIN32)\petmark.uu + chdir $(WIN32) + ..\..\util\uudecode.exe petmark.uu + chdir ..\..\src + +$(WIN32)\rip.bmp: $(U)uudecode.exe $(WIN32)\rip.uu + chdir $(WIN32) + ..\..\util\uudecode.exe rip.uu + chdir ..\..\src + +$(WIN32)\splash.bmp: $(U)uudecode.exe $(WIN32)\splash.uu + chdir $(WIN32) + ..\..\util\uudecode.exe splash.uu + chdir ..\..\src + +#========================================== +# Level Compiler Stuff +#========================================== + +LEVCFLAGS=-c -nologo -DWINVER=0x0400 -DWIN32 -D_WIN32 \ + -D_MT -MT -I..\include -nologo -Z7 -Od -DDLB + +$(U)lev_comp.exe: $(SPLEVOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(SPLEVOBJS:^ =^ + ) +<< + +$(O)lev_yacc.o: $(HACK_H) $(SP_LEV_H) $(INCL)\lev_comp.h $(U)lev_yacc.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_yacc.c + +$(O)lev_$(LEX).o: $(HACK_H) $(INCL)\lev_comp.h $(SP_LEV_H) \ + $(U)lev_$(LEX).c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_$(LEX).c + +$(O)lev_main.o: $(U)lev_main.c $(HACK_H) $(SP_LEV_H) + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)lev_main.c + + +$(U)lev_yacc.c $(INCL)\lev_comp.h : $(U)lev_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d lev_comp.y + copy $(YTABC) lev_yacc.c + copy $(YTABH) $(INCL)\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.y has changed. + @echo To update $(U)lev_yacc.c and $(INCL)\lev_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt lev_yacc.c and + @echo lev_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\lev_yacc.c $(U)lev_yacc.c >nul + @copy $(SSYS)\lev_comp.h $(INCL)\lev_comp.h >nul + @echo /**/ >>$(U)lev_yacc.c + @echo /**/ >>$(INCL)\lev_comp.h +! ENDIF + +$(U)lev_$(LEX).c: $(U)lev_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)lev_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt lev_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\lev_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +$(U)dgn_comp.exe: $(DGNCOMPOBJS) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(DGNCOMPOBJS:^ =^ + ) +<< + +$(O)dgn_yacc.o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h $(U)dgn_yacc.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_yacc.c + +$(O)dgn_$(LEX).o: $(HACK_H) $(DGN_FILE_H) $(INCL)\dgn_comp.h \ + $(U)dgn_$(LEX).c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_$(LEX).c + +$(O)dgn_main.o: $(HACK_H) $(U)dgn_main.c + @$(CC) $(LEVCFLAGS) -W0 -Fo$@ $(U)dgn_main.c + +$(U)dgn_yacc.c $(INCL)\dgn_comp.h : $(U)dgn_comp.y +! IF "$(DO_YACC)"=="YACC_ACT" + chdir $(UTIL) + $(YACC) -d dgn_comp.y + copy $(YTABC) dgn_yacc.c + copy $(YTABH) $(INCL)\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.y has changed. To update dgn_yacc.c and + @echo $(INCL)\dgn_comp.h run $(YACC). + @echo --- + @echo For now, we will copy the prebuilt $(U)dgn_yacc.c and + @echo dgn_comp.h from $(SSYS) into $(UTIL) and use them. + @copy $(SSYS)\dgn_yacc.c $(U)dgn_yacc.c >nul + @copy $(SSYS)\dgn_comp.h $(INCL)\dgn_comp.h >nul + @echo /**/ >>$(U)dgn_yacc.c + @echo /**/ >>$(INCL)\dgn_comp.h +! ENDIF + +$(U)dgn_$(LEX).c: $(U)dgn_comp.l +! IF "$(DO_LEX)"=="LEX_ACT" + chdir $(UTIL) + $(LEX) $(FLEXSKEL) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir $(SRC) +! ELSE + @echo $(U)dgn_comp.l has changed. To update $@ run $(LEX). + @echo --- + @echo For now, we will copy the prebuilt dgn_lex.c + @echo from $(SSYS) into $(UTIL) and use it. + @copy $(SSYS)\dgn_lex.c $@ >nul + @echo /**/ >>$@ +! ENDIF + +#========================================== +# Create directory for holding object files +#========================================== + +$(O)obj.tag: + @if not exist $(OBJ)\*.* echo creating directory $(OBJ) + @if not exist $(OBJ)\*.* mkdir $(OBJ) + @echo directory created >$@ + +#========================================== +# Notify of any CL environment variables +# in effect since they change the compiler +# options. +#========================================== + +envchk: +! IF "$(CL)"!="" + @echo Warning, the CL Environment variable is defined: + @echo CL=$(CL) +! ENDIF +! IF "$(GRAPHICAL)"=="Y" + @echo ---- + @echo NOTE: This build will include tile support. + @echo ---- +! ENDIF + +#========================================== +#=========== SECONDARY TARGETS ============ +#========================================== + +#=========================================== +# Header files NOT distributed in ..\include +#=========================================== + +$(INCL)\win32api.h: $(NTSYS)\win32api.h + copy $(NTSYS)\win32api.h $@ + + +#========================================== +# DLB utility and nhdat file creation +#========================================== + +$(U)dlb_main.exe: $(DLBOBJ) $(O)dlb.o + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)dlb_main.o + $(O)dlb.o + $(O)alloc.o + $(O)panic.o +<< + +$(O)dlb.o: $(O)dlb_main.o $(O)alloc.o $(O)panic.o $(INCL)\dlb.h + @$(CC) $(CFLAGS) /Fo$@ $(SRC)\dlb.c + +$(O)dlb_main.o: $(UTIL)\dlb_main.c $(INCL)\config.h $(INCL)\dlb.h + @$(CC) $(CFLAGS) /Fo$@ $(UTIL)\dlb_main.c + +$(DAT)\porthelp: $(NTSYS)\porthelp + @copy $(NTSYS)\porthelp $@ >nul + +nhdat: $(U)dlb_main.exe $(DAT)\data $(DAT)\oracles $(OPTIONS_FILE) \ + $(DAT)\quest.dat $(DAT)\rumors $(DAT)\help $(DAT)\hh $(DAT)\cmdhelp \ + $(DAT)\history $(DAT)\opthelp $(DAT)\wizhelp $(DAT)\dungeon $(DAT)\porthelp \ + $(DAT)\license $(O)sp_lev.tag + cd $(DAT) + echo data >dlb.lst + echo oracles >>dlb.lst + if exist options echo options >>dlb.lst + if exist ttyoptions echo ttyoptions >>dlb.lst + if exist guioptions echo guioptions >>dlb.lst + if exist porthelp echo porthelp >>dlb.lst + echo quest.dat >>dlb.lst + echo rumors >>dlb.lst + echo help >>dlb.lst + echo hh >>dlb.lst + echo cmdhelp >>dlb.lst + echo history >>dlb.lst + echo opthelp >>dlb.lst + echo wizhelp >>dlb.lst + echo dungeon >>dlb.lst + echo license >>dlb.lst + for %%N in (*.lev) do echo %%N >>dlb.lst + $(U)dlb_main cIf dlb.lst $(SRC)\nhdat + cd $(SRC) + +#========================================== +# Recover Utility +#========================================== + +$(U)recover.exe: $(RECOVOBJS) + $(link) $(LFLAGSU) -out:$@ $(RECOVOBJS) + +$(O)recover.o: $(CONFIG_H) $(U)recover.c $(INCL)\win32api.h + $(CC) $(CFLAGSU) -Fo$@ $(U)recover.c + +#========================================== +# Tile Mapping +#========================================== + +$(SRC)\tile.c: $(U)tilemap.exe + @echo A new $@ has been created + @$(U)tilemap + +$(U)tilemap.exe: $(O)tilemap.o + @$(link) $(LFLAGSU) -out:$@ $(O)tilemap.o + +$(O)tilemap.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGSU) -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletx32.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGS) /DTILETEXT /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tilemap.c + +$(O)tiletxt.o: $(WSHR)\tilemap.c $(HACK_H) + @$(CC) $(CFLAGS) /DTILETEXT -Fo$@ $(WSHR)\tilemap.c + +$(O)gifread.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\gifread.c + +$(O)gifrd32.o: $(WSHR)\gifread.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\gifread.c + +$(O)ppmwrite.o: $(WSHR)\ppmwrite.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\ppmwrite.c + +$(O)tiletext.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(WSHR)\tiletext.c + +$(O)tilete32.o: $(WSHR)\tiletext.c $(CONFIG_H) $(TILE_H) + @$(CC) $(CFLAGS) -I$(WSHR) /DTILE_X=32 /DTILE_Y=32 -Fo$@ $(WSHR)\tiletext.c + +#========================================== +# Optional Tile Utilities +#========================================== + +$(U)gif2txt.exe: $(GIFREADERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(GIFREADERS:^ =^ + ) + $(TEXT_IO:^ =^ + ) +<< + +$(U)gif2tx32.exe: $(GIFREADERS32) $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(GIFREADERS32:^ =^ + ) + $(TEXT_IO32:^ =^ + ) +<< + +$(U)txt2ppm.exe: $(PPMWRITERS) $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(PPMWRITERS:^ =^ + ) + $(TEXT_IO:^ =^ + ) +<< + +!IF "$(GRAPHICAL)"=="Y" +$(TILEBMP16): $(TILEUTIL16) $(TILEFILES) + @echo Creating 16x16 binary tile files (this may take some time) + @$(U)tile2bmp $(TILEBMP16) +#$(TILEBMP32): $(TILEUTIL32) $(TILEFILES32) +# @echo Creating 32x32 binary tile files (this may take some time) +# @$(U)til2bm32 $(TILEBMP32) + +!ELSE +$(TILEBMP16): +$(TILEBMP32): +!ENDIF + +$(U)tile2bmp.exe: $(O)tile2bmp.o $(TEXT_IO) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)tile2bmp.o + $(TEXT_IO:^ =^ + ) +<< + +$(U)til2bm32.exe: $(O)til2bm32.o $(TEXT_IO32) + @echo Linking $@... + @$(link) $(LFLAGSU) -out:$@ @<<$(@B).lnk + $(O)til2bm32.o + $(TEXT_IO32:^ =^ + ) +<< + +$(O)tile2bmp.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(CC) $(CFLAGS) -I$(WSHR) /DPACKED_FILE /Fo$@ $(WSHR)\tile2bmp.c + +$(O)til2bm32.o: $(WSHR)\tile2bmp.c $(HACK_H) $(TILE_H) $(INCL)\win32api.h + @$(CC) $(CFLAGS) -I$(WSHR) /DPACKED_FILE /DTILE_X=32 /DTILE_Y=32 /Fo$@ $(WSHR)\tile2bmp.c + +#========================================== +# Housekeeping +#========================================== + +spotless: clean +! IF ("$(OBJ)"!="") + -rmdir $(OBJ) /s /Q +! ENDIF + if exist $(INCL)\date.h del $(INCL)\date.h + if exist $(INCL)\onames.h del $(INCL)\onames.h + if exist $(INCL)\pm.h del $(INCL)\pm.h + if exist $(INCL)\vis_tab.h del $(INCL)\vis_tab.h + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(SRC)\tile.c del $(SRC)\tile.c + if exist $(U)*.lnk del $(U)*.lnk + if exist $(U)*.map del $(U)*.map + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\???-fil?.lev del $(DAT)\???-fil?.lev + if exist $(DAT)\???-goal.lev del $(DAT)\???-goal.lev + if exist $(DAT)\???-loca.lev del $(DAT)\???-loca.lev + if exist $(DAT)\???-strt.lev del $(DAT)\???-strt.lev + if exist $(DAT)\air.lev del $(DAT)\air.lev + if exist $(DAT)\asmodeus.lev del $(DAT)\asmodeus.lev + if exist $(DAT)\astral.lev del $(DAT)\astral.lev + if exist $(DAT)\baalz.lev del $(DAT)\baalz.lev + if exist $(DAT)\bigroom.lev del $(DAT)\bigroom.lev + if exist $(DAT)\castle.lev del $(DAT)\castle.lev + if exist $(DAT)\data del $(DAT)\data + if exist $(DAT)\dungeon del $(DAT)\dungeon + if exist $(DAT)\dungeon.pdf del $(DAT)\dungeon.pdf + if exist $(DAT)\earth.lev del $(DAT)\earth.lev + if exist $(DAT)\fakewiz?.lev del $(DAT)\fakewiz?.lev + if exist $(DAT)\fire.lev del $(DAT)\fire.lev + if exist $(DAT)\juiblex.lev del $(DAT)\juiblex.lev + if exist $(DAT)\knox.lev del $(DAT)\knox.lev + if exist $(DAT)\medusa-?.lev del $(DAT)\medusa-?.lev + if exist $(DAT)\mine*.lev del $(DAT)\mine*.lev + if exist $(DAT)\options del $(DAT)\options + if exist $(DAT)\ttyoptions del $(DAT)\ttyoptions + if exist $(DAT)\guioptions del $(DAT)\guioptions + if exist $(DAT)\oracle.lev del $(DAT)\oracle.lev + if exist $(DAT)\oracles del $(DAT)\oracles + if exist $(DAT)\orcus.lev del $(DAT)\orcus.lev + if exist $(DAT)\rumors del $(DAT)\rumors + if exist $(DAT)\quest.dat del $(DAT)\quest.dat + if exist $(DAT)\sanctum.lev del $(DAT)\sanctum.lev + if exist $(DAT)\soko?-?.lev del $(DAT)\soko?-?.lev + if exist $(DAT)\tower?.lev del $(DAT)\tower?.lev + if exist $(DAT)\valley.lev del $(DAT)\valley.lev + if exist $(DAT)\water.lev del $(DAT)\water.lev + if exist $(DAT)\wizard?.lev del $(DAT)\wizard?.lev + if exist $(O)sp_lev.tag del $(O)sp_lev.tag + if exist $(SRC)\monstr.c del $(SRC)\monstr.c + if exist $(SRC)\vis_tab.c del $(SRC)\vis_tab.c + if exist $(U)recover.exe del $(U)recover.exe + if exist nhdat. del nhdat. + if exist $(O)obj.tag del $(O)obj.tag + if exist $(O)gamedir.tag del $(O)gamedir.tag + if exist $(O)nh*key.lib del $(O)nh*key.lib + if exist $(O)nh*key.exp del $(O)nh*key.exp + +clean: + if exist $(O)*.o del $(O)*.o + if exist $(O)utility.tag del $(O)utility.tag + if exist $(U)makedefs.exe del $(U)makedefs.exe + if exist $(U)lev_comp.exe del $(U)lev_comp.exe + if exist $(U)dgn_comp.exe del $(U)dgn_comp.exe + if exist $(SRC)\*.lnk del $(SRC)\*.lnk + if exist $(SRC)\*.map del $(SRC)\*.map + if exist $(O)install.tag del $(O)install.tag +! IF ("$(WINPFLAG)"!="") + if exist $(TILEBMP16) del $(TILEBMP16) + if exist $(TILEBMP32) del $(TILEBMP32) +! ENDIF + +#=================================================================== +# OTHER DEPENDENCIES +#=================================================================== + +# +# dat dependencies +# + +$(DAT)\data: $(O)utility.tag $(DATABASE) + $(U)makedefs -d + +$(DAT)\rumors: $(O)utility.tag $(DAT)\rumors.tru $(DAT)\rumors.fal + $(U)makedefs -r + +$(DAT)\quest.dat: $(O)utility.tag $(DAT)\quest.txt + $(U)makedefs -q + +$(DAT)\oracles: $(O)utility.tag $(DAT)\oracles.txt + $(U)makedefs -h + +$(DAT)\dungeon: $(O)utility.tag $(DAT)\dungeon.def + $(U)makedefs -e + cd $(DAT) + $(U)dgn_comp dungeon.pdf + cd $(SRC) + +# +# NT dependencies +# + +$(O)nttty.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nttty.c + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(NTSYS)\nttty.c +$(O)nhkeys.o: $(HACK_H) $(TILE_H) $(INCL)\win32api.h $(NTSYS)\nhkeys.c + @$(CC) $(CFLAGS) -I$(WSHR) -Fo$@ $(NTSYS)\nhkeys.c +$(O)winnt.o: $(HACK_H) $(INCL)\win32api.h $(NTSYS)\winnt.c + @$(CC) $(CFLAGS) -Fo$@ $(NTSYS)\winnt.c +$(O)ntsound.o: $(HACK_H) $(NTSYS)\ntsound.c + @$(CC) $(CFLAGS) -Fo$@ $(NTSYS)\ntsound.c +$(O)mapimail.o: $(HACK_H) $(INCL)\nhlan.h $(NTSYS)\mapimail.c + @$(CC) $(CFLAGS) -DMAPI_VERBOSE -Fo$@ $(NTSYS)\mapimail.c + +# +# util dependencies +# + +$(O)panic.o: $(U)panic.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ $(U)panic.c + +# +# The rest are stolen from sys/unix/Makefile.src, +# with the following changes: +# * ../include changed to $(INCL) +# * slashes changed to back-slashes +# * -c (which is included in CFLAGS) substituted with -Fo$@ +# * targets prefixed with $(O) +# but otherwise untouched. +# That means that there is some irrelevant stuff +# in here, but maintenance should be easier. +# +$(O)tos.o: ..\sys\atari\tos.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\atari\tos.c +$(O)pcmain.o: ..\sys\share\pcmain.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\win32api.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcmain.c +$(O)pcsys.o: ..\sys\share\pcsys.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcsys.c +$(O)pctty.o: ..\sys\share\pctty.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pctty.c +$(O)pcunix.o: ..\sys\share\pcunix.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\pcunix.c +$(O)random.o: ..\sys\share\random.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\random.c +$(O)ioctl.o: ..\sys\share\ioctl.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\ioctl.c +$(O)unixtty.o: ..\sys\share\unixtty.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\share\unixtty.c +$(O)unixmain.o: ..\sys\unix\unixmain.c $(HACK_H) $(INCL)\dlb.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixmain.c +$(O)unixunix.o: ..\sys\unix\unixunix.c $(HACK_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixunix.c +$(O)unixres.o: ..\sys\unix\unixres.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\sys\unix\unixres.c +$(O)bemain.o: ..\sys\be\bemain.c $(HACK_H) $(INCL)\dlb.h + @$(CC) $(CFLAGS) -Fo$@ ..\sys\be\bemain.c +$(O)getline.o: ..\win\tty\getline.c $(HACK_H) $(INCL)\func_tab.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\getline.c +$(O)termcap.o: ..\win\tty\termcap.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\termcap.c +$(O)topl.o: ..\win\tty\topl.c $(HACK_H) $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\topl.c +$(O)wintty.o: ..\win\tty\wintty.c $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\tcap.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\tty\wintty.c +$(O)Window.o: ..\win\X11\Window.c $(INCL)\xwindowp.h $(INCL)\xwindow.h \ + $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\Window.c +$(O)dialogs.o: ..\win\X11\dialogs.c $(CONFIG_H) + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\dialogs.c +$(O)winX.o: ..\win\X11\winX.c $(HACK_H) $(INCL)\winX.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h ..\win\X11\nh72icon \ + ..\win\X11\nh56icon ..\win\X11\nh32icon + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winX.c +$(O)winmap.o: ..\win\X11\winmap.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\dlb.h \ + $(INCL)\winX.h $(INCL)\tile2x11.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmap.c +$(O)winmenu.o: ..\win\X11\winmenu.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmenu.c +$(O)winmesg.o: ..\win\X11\winmesg.c $(INCL)\xwindow.h $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmesg.c +$(O)winmisc.o: ..\win\X11\winmisc.c $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winmisc.c +$(O)winstat.o: ..\win\X11\winstat.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winstat.c +$(O)wintext.o: ..\win\X11\wintext.c $(HACK_H) $(INCL)\winX.h $(INCL)\xwindow.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\wintext.c +$(O)winval.o: ..\win\X11\winval.c $(HACK_H) $(INCL)\winX.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\X11\winval.c +$(O)tile.o: $(SRC)\tile.c $(HACK_H) +$(O)gnaskstr.o: ..\win\gnome\gnaskstr.c ..\win\gnome\gnaskstr.h \ + ..\win\gnome\gnmain.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnaskstr.c +$(O)gnbind.o: ..\win\gnome\gnbind.c ..\win\gnome\gnbind.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnaskstr.h ..\win\gnome\gnyesno.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnbind.c +$(O)gnglyph.o: ..\win\gnome\gnglyph.c ..\win\gnome\gnglyph.h $(INCL)\tile2x11.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnglyph.c +$(O)gnmain.o: ..\win\gnome\gnmain.c ..\win\gnome\gnmain.h ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnbind.h ..\win\gnome\gnopts.h $(HACK_H) \ + $(INCL)\date.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmain.c +$(O)gnmap.o: ..\win\gnome\gnmap.c ..\win\gnome\gnmap.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmap.c +$(O)gnmenu.o: ..\win\gnome\gnmenu.c ..\win\gnome\gnmenu.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gnbind.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmenu.c +$(O)gnmesg.o: ..\win\gnome\gnmesg.c ..\win\gnome\gnmesg.h ..\win\gnome\gnsignal.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnmesg.c +$(O)gnopts.o: ..\win\gnome\gnopts.c ..\win\gnome\gnopts.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnmain.h ..\win\gnome\gnmap.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnopts.c +$(O)gnplayer.o: ..\win\gnome\gnplayer.c ..\win\gnome\gnplayer.h \ + ..\win\gnome\gnmain.h $(HACK_H) + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnplayer.c +$(O)gnsignal.o: ..\win\gnome\gnsignal.c ..\win\gnome\gnsignal.h \ + ..\win\gnome\gnmain.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnsignal.c +$(O)gnstatus.o: ..\win\gnome\gnstatus.c ..\win\gnome\gnstatus.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gn_xpms.h \ + ..\win\gnome\gnomeprv.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnstatus.c +$(O)gntext.o: ..\win\gnome\gntext.c ..\win\gnome\gntext.h ..\win\gnome\gnmain.h \ + ..\win\gnome\gn_rip.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gntext.c +$(O)gnworn.o: ..\win\gnome\gnworn.c ..\win\gnome\gnworn.h ..\win\gnome\gnglyph.h \ + ..\win\gnome\gnsignal.h ..\win\gnome\gnomeprv.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnworn.c +$(O)gnyesno.o: ..\win\gnome\gnyesno.c ..\win\gnome\gnbind.h ..\win\gnome\gnyesno.h + @$(CC) $(CFLAGS) $(GNOMEINC) -Fo$@ ..\win\gnome\gnyesno.c +$(O)wingem.o: ..\win\gem\wingem.c $(HACK_H) $(INCL)\func_tab.h $(INCL)\dlb.h \ + $(INCL)\patchlevel.h $(INCL)\wingem.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\wingem.c +$(O)wingem1.o: ..\win\gem\wingem1.c $(INCL)\gem_rsc.h $(INCL)\load_img.h \ + $(INCL)\gr_rect.h $(INCL)\wintype.h $(INCL)\wingem.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\wingem1.c +$(O)load_img.o: ..\win\gem\load_img.c $(INCL)\load_img.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\load_img.c +$(O)gr_rect.o: ..\win\gem\gr_rect.c $(INCL)\gr_rect.h + @$(CC) $(CFLAGS) -Fo$@ ..\win\gem\gr_rect.c +$(O)tile.o: tile.c $(HACK_H) +$(O)qt_win.o: ..\win\Qt\qt_win.cpp $(HACK_H) $(INCL)\func_tab.h \ + $(INCL)\dlb.h $(INCL)\patchlevel.h $(INCL)\tile2x11.h \ + $(INCL)\qt_win.h $(INCL)\qt_clust.h $(INCL)\qt_kde0.h \ + $(INCL)\qt_xpms.h qt_win.moc qt_kde0.moc qttableview.moc + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qt_win.cpp +$(O)qt_clust.o: ..\win\Qt\qt_clust.cpp $(INCL)\qt_clust.h + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qt_clust.cpp +$(O)qttableview.o: ..\win\Qt\qttableview.cpp $(INCL)\qttableview.h + $(CXX) $(CXXFLAGS) -Fo$@ ..\win\Qt\qttableview.cpp +$(O)monstr.o: monstr.c $(CONFIG_H) +$(O)vis_tab.o: vis_tab.c $(CONFIG_H) $(INCL)\vis_tab.h +$(O)allmain.o: allmain.c $(HACK_H) +$(O)alloc.o: alloc.c $(CONFIG_H) +$(O)apply.o: apply.c $(HACK_H) $(INCL)\edog.h +$(O)artifact.o: artifact.c $(HACK_H) $(INCL)\artifact.h $(INCL)\artilist.h +$(O)attrib.o: attrib.c $(HACK_H) +$(O)ball.o: ball.c $(HACK_H) +$(O)bones.o: bones.c $(HACK_H) $(INCL)\lev.h +$(O)botl.o: botl.c $(HACK_H) +$(O)cmd.o: cmd.c $(HACK_H) $(INCL)\func_tab.h +$(O)dbridge.o: dbridge.c $(HACK_H) +$(O)decl.o: decl.c $(HACK_H) +$(O)detect.o: detect.c $(HACK_H) $(INCL)\artifact.h +$(O)dig.o: dig.c $(HACK_H) $(INCL)\edog.h +$(O)display.o: display.c $(HACK_H) +$(O)dlb.o: dlb.c $(CONFIG_H) $(INCL)\dlb.h +$(O)do.o: do.c $(HACK_H) $(INCL)\lev.h +$(O)do_name.o: do_name.c $(HACK_H) +$(O)do_wear.o: do_wear.c $(HACK_H) +$(O)dog.o: dog.c $(HACK_H) $(INCL)\edog.h +$(O)dogmove.o: dogmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)dokick.o: dokick.c $(HACK_H) $(INCL)\eshk.h +$(O)dothrow.o: dothrow.c $(HACK_H) $(INCL)\edog.h +$(O)drawing.o: drawing.c $(HACK_H) $(INCL)\tcap.h +$(O)dungeon.o: dungeon.c $(HACK_H) $(INCL)\dgn_file.h $(INCL)\dlb.h +$(O)eat.o: eat.c $(HACK_H) +$(O)end.o: end.c $(HACK_H) $(INCL)\eshk.h $(INCL)\dlb.h +$(O)engrave.o: engrave.c $(HACK_H) $(INCL)\lev.h +$(O)exper.o: exper.c $(HACK_H) +$(O)explode.o: explode.c $(HACK_H) +$(O)extralev.o: extralev.c $(HACK_H) +$(O)files.o: files.c $(HACK_H) $(INCL)\dlb.h +$(O)fountain.o: fountain.c $(HACK_H) +$(O)hack.o: hack.c $(HACK_H) +$(O)hacklib.o: hacklib.c $(HACK_H) +$(O)invent.o: invent.c $(HACK_H) +$(O)light.o: light.c $(HACK_H) $(INCL)\lev.h +$(O)lock.o: lock.c $(HACK_H) +$(O)mail.o: mail.c $(HACK_H) $(INCL)\mail.h +$(O)makemon.o: makemon.c $(HACK_H) $(INCL)\epri.h $(INCL)\emin.h \ + $(INCL)\edog.h +$(O)mapglyph.o: mapglyph.c $(HACK_H) +$(O)mcastu.o: mcastu.c $(HACK_H) +$(O)mhitm.o: mhitm.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)mhitu.o: mhitu.c $(HACK_H) $(INCL)\artifact.h $(INCL)\edog.h +$(O)minion.o: minion.c $(HACK_H) $(INCL)\emin.h $(INCL)\epri.h +$(O)mklev.o: mklev.c $(HACK_H) +$(O)mkmap.o: mkmap.c $(HACK_H) $(INCL)\sp_lev.h +$(O)mkmaze.o: mkmaze.c $(HACK_H) $(INCL)\sp_lev.h $(INCL)\lev.h +$(O)mkobj.o: mkobj.c $(HACK_H) +$(O)mkroom.o: mkroom.c $(HACK_H) +$(O)mon.o: mon.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\edog.h +$(O)mondata.o: mondata.c $(HACK_H) $(INCL)\eshk.h $(INCL)\epri.h +$(O)monmove.o: monmove.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\artifact.h \ + $(INCL)\epri.h +$(O)monst.o: monst.c $(CONFIG_H) $(INCL)\permonst.h $(INCL)\align.h \ + $(INCL)\monattk.h $(INCL)\monflag.h $(INCL)\monsym.h \ + $(INCL)\dungeon.h $(INCL)\eshk.h $(INCL)\vault.h \ + $(INCL)\epri.h $(INCL)\color.h +$(O)mplayer.o: mplayer.c $(HACK_H) +$(O)mthrowu.o: mthrowu.c $(HACK_H) +$(O)muse.o: muse.c $(HACK_H) $(INCL)\edog.h +$(O)music.o: music.c $(HACK_H) #interp.c +$(O)o_init.o: o_init.c $(HACK_H) $(INCL)\lev.h +$(O)objects.o: objects.c $(CONFIG_H) $(INCL)\obj.h $(INCL)\objclass.h \ + $(INCL)\prop.h $(INCL)\skills.h $(INCL)\color.h +$(O)objnam.o: objnam.c $(HACK_H) +$(O)options.o: options.c $(CONFIG_H) $(INCL)\objclass.h $(INCL)\flag.h \ + $(HACK_H) $(INCL)\tcap.h +$(O)pager.o: pager.c $(HACK_H) $(INCL)\dlb.h +$(O)pickup.o: pickup.c $(HACK_H) +$(O)pline.o: pline.c $(HACK_H) $(INCL)\epri.h $(INCL)\edog.h +$(O)polyself.o: polyself.c $(HACK_H) +$(O)potion.o: potion.c $(HACK_H) +$(O)pray.o: pray.c $(HACK_H) $(INCL)\epri.h +$(O)priest.o: priest.c $(HACK_H) $(INCL)\mfndpos.h $(INCL)\eshk.h \ + $(INCL)\epri.h $(INCL)\emin.h +$(O)quest.o: quest.c $(HACK_H) $(INCL)\qtext.h +$(O)questpgr.o: questpgr.c $(HACK_H) $(INCL)\dlb.h $(INCL)\qtext.h +$(O)read.o: read.c $(HACK_H) +$(O)rect.o: rect.c $(HACK_H) +$(O)region.o: region.c $(HACK_H) $(INCL)\lev.h +$(O)restore.o: restore.c $(HACK_H) $(INCL)\lev.h $(INCL)\tcap.h +$(O)rip.o: rip.c $(HACK_H) +$(O)rnd.o: rnd.c $(HACK_H) +$(O)role.o: role.c $(HACK_H) +$(O)rumors.o: rumors.c $(HACK_H) $(INCL)\lev.h $(INCL)\dlb.h +$(O)save.o: save.c $(HACK_H) $(INCL)\lev.h +$(O)shk.o: shk.c $(HACK_H) $(INCL)\eshk.h +$(O)shknam.o: shknam.c $(HACK_H) $(INCL)\eshk.h +$(O)sit.o: sit.c $(HACK_H) $(INCL)\artifact.h +$(O)sounds.o: sounds.c $(HACK_H) $(INCL)\edog.h +$(O)sp_lev.o: sp_lev.c $(HACK_H) $(INCL)\dlb.h $(INCL)\sp_lev.h +$(O)spell.o: spell.c $(HACK_H) +$(O)steal.o: steal.c $(HACK_H) +$(O)steed.o: steed.c $(HACK_H) +$(O)teleport.o: teleport.c $(HACK_H) +$(O)timeout.o: timeout.c $(HACK_H) $(INCL)\lev.h +$(O)topten.o: topten.c $(HACK_H) $(INCL)\dlb.h $(INCL)\patchlevel.h +$(O)track.o: track.c $(HACK_H) +$(O)trap.o: trap.c $(HACK_H) +$(O)u_init.o: u_init.c $(HACK_H) +$(O)uhitm.o: uhitm.c $(HACK_H) +$(O)vault.o: vault.c $(HACK_H) $(INCL)\vault.h +$(O)version.o: version.c $(HACK_H) $(INCL)\date.h $(INCL)\patchlevel.h +$(O)vision.o: vision.c $(HACK_H) $(INCL)\vis_tab.h +$(O)weapon.o: weapon.c $(HACK_H) +$(O)were.o: were.c $(HACK_H) +$(O)wield.o: wield.c $(HACK_H) +$(O)windows.o: windows.c $(HACK_H) $(INCL)\wingem.h $(INCL)\winGnome.h +$(O)wizard.o: wizard.c $(HACK_H) $(INCL)\qtext.h $(INCL)\epri.h +$(O)worm.o: worm.c $(HACK_H) $(INCL)\lev.h +$(O)worn.o: worn.c $(HACK_H) +$(O)write.o: write.c $(HACK_H) +$(O)zap.o: zap.c $(HACK_H) + +# end of file diff --git a/sys/winnt/console.rc b/sys/winnt/console.rc new file mode 100644 index 0000000..9a456e0 --- /dev/null +++ b/sys/winnt/console.rc @@ -0,0 +1,49 @@ +/* SCCS Id: @(#)console.rc 3.4 $Date: 2003/10/14 01:31:28 $ */ +/* Copyright (c) Yitzhak Sapir, 2002. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "windows.h" + +1 ICON DISCARDABLE "NetHack.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,4,3,0 + PRODUCTVERSION 3,4,3,0 + FILEFLAGSMASK 0x1fL +#ifdef _DEBUG + FILEFLAGS 0x9L +#else + FILEFLAGS 0x8L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "NetHack for Windows - TTY Interface\0" + VALUE "FileVersion", "3.4.3\0" + VALUE "InternalName", "NetHack\0" + VALUE "LegalCopyright", "Copyright (C) 1985 - 2003. By Stichting Mathematisch Centrum and M. Stephenson. See license for details.\0" + VALUE "OriginalFilename", "NetHack.exe\0" + VALUE "PrivateBuild", "031014\0" + VALUE "ProductName", "NetHack\0" + VALUE "ProductVersion", "3.4.3\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +/*console.rc*/ + diff --git a/sys/winnt/defaults.nh b/sys/winnt/defaults.nh new file mode 100644 index 0000000..fd64458 --- /dev/null +++ b/sys/winnt/defaults.nh @@ -0,0 +1,185 @@ +# Sample config file for win32 NetHack +# A '#' at the beginning of a line means the rest of the line is a comment. +# +# Some options MUST be set in this file, other options can be toggled while +# playing. For a list of options available see the file. +# +# To change the configuration, comment out the unwanted lines, and +# uncomment the configuration you want. + +# *** OPTIONS *** +# +# Use the IBM character set rather than just plain ascii characters +# for tty window-port. +OPTIONS=IBMGraphics + +# Keyboard handling +# Different keyboard handlers can be loaded. +# Default is nhdefkey.dll but you can override that. +# Ray Chason's keyboard handler +# OPTIONS=altkeyhandler:nhraykey.dll +# +# NetHack 3.4.0 keyboard handling +# OPTIONS=altkeyhandler:nh340key.dll + +# *** Personal Preferences *** +# Some options to set personal preferences. Uncomment and change these to +# suit your personal preference. If several people are to use the same +# configuration, options like these should not be set. +# +#OPTIONS=name:Janet,role:Valkyrie,race:Human,gender:female,align:lawful +#OPTIONS=dogname:Fido,catname:Morris,fruit:guava +#OPTIONS=horsename:Silver +#OPTIONS=autopickup,pickup_types:$"=/!?+ +#OPTIONS=packorder:")[%?+/=!(*0_` +#OPTIONS=scores:10 top/2 around/own +#OPTIONS=nolegacy,noverbose +#OPTIONS=menustyle:traditional + +# +# General options. You might also set "silent" so as not to attract +# the boss's attention. +# +# number_pad option can have an optional value of 0 (off), 1 (on), +# or 2(on,legacy-mode) which causes 5='g', alt-5='G', alt-0='I' +OPTIONS=time,noshowexp,number_pad:2,lit_corridor +# +# If you want to get rid of "use #quit to quit..." use: +OPTIONS=suppress_alert:3.3.1 +# +# Note: the rest_on_space in the next line may not be +# appropriate for a beginning NetHack player, since +# it could result in use of a turn unintentionally. +# If you're new to NetHack, leave it commented it out. +#OPTIONS=rest_on_space +# +# Set some options to control graphical window-port (these will +# be safely and silently ignored by the tty port) +# +# Map window settings +# possible map_mode options include: tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8| +# ascii7x12|ascii8x12|ascii16x12|ascii12x16| +# ascii10x18|fit_to_screen +OPTIONS=map_mode:tiles,scroll_margin:5 + +# Message window settings +OPTIONS=font_message:Arial,font_size_message:9,align_message:top + +# Menu settings +OPTIONS=font_menu:Arial,font_size_menu:9 + +# Text settings +OPTIONS=font_text:Courier New,font_size_text:9 + +# Status window settings +OPTIONS=font_status:Courier New,font_size_status:9 + +# Other +OPTIONS=hilite_pet,!toptenwin +#OPTIONS=!splash_screen,player_selection:prompts + +# Status/message window colors +# Possible color options include: +# six digit hexadecimal RGB color value ("#8F8F8F"), black, red, green, brown, +# blue, magenta, cyan, gray (or grey), orange, brightgreen, yellow, brightblue, +# brightmagenta, brightcyan, white, trueblack, purple, silver, maroon, fuchsia, +# lime, olive, navy, teal, aqua, activeborder, activecaption, appworkspace, +# background, btnface, btnshadow, btntext, captiontext, graytext, highlight, +# highlighttext, inactiveborder, inactivecaption, menu, menutext, scrollbar, +# window, windowframe, windowtext. +#OPTIONS=windowcolors:status windowtext/window message windowtext/window + +# *** LOCATIONS *** +# IMPORTANT: If you change any of these locations, the directories they +# point at must exist. NetHack will not create them for you. +# +# HACKDIR is the default location for everything. +# Note: On Windows HACKDIR defaults to the location +# of the NetHack.exe or NetHackw.exe file so +# setting HACKDIR below to override that is +# not usually necessary or recommended. +#HACKDIR=c:\games\nethack +# +# The location that level files in progress are stored (default=HACKDIR, writeable) +#LEVELDIR=c:\nethack\levels +# +# The location where saved games are kept (default=HACKDIR, writeable) +#SAVEDIR=c:\nethack\save +# +# The location that bones files are kept (default=HACKDIR, writeable) +#BONESDIR=c:\nethack\save +# +# The location that file synchronization locks are stored (default=HACKDIR, writeable) +#LOCKDIR=c:\nethack\levels +# +# The location that a record of game aborts and self-diagnosed game problems +# is kept (default=HACKDIR, writeable) +#TROUBLEDIR=c:\nethack\trouble + +# Finnish keyboards might need these modifications uncommented. +# For \, @, $, [, | +#OPTIONS=subkeyvalue:171/92 +#OPTIONS=subkeyvalue:178/64 +#OPTIONS=subkeyvalue:180/36 +#OPTIONS=subkeyvalue:184/91 +#OPTIONS=subkeyvalue:188/124 + +# +# *** CHARACTER GRAPHICS *** +# +# See the on-line help or the Guidebook for which symbols are in which +# positions. +# +# If you merely set the IBMgraphics option as above, NetHack will use IBM +# extended ASCII for dungeon characters. If you don't like the selections, +# you can make up your own via these graphics options, but you should still +# set IBMgraphics if you are using IBM graphics characters to get the correct +# processing. +# +# ================================================ +# An example using the IBM graphics character set: +#DUNGEON= 032 179 196 218 191 192 217 197 193 194 \ +# 180 195 249 239 239 254 254 240 241 249 \ +# 177 177 060 062 060 062 220 124 190 035 \ +# 244 247 249 247 042 042 186 205 046 035 \ +# 247 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 232 232 232 157 094 094 \ +# 094 094 +# +#EFFECTS= 179 196 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 +# +# ================================================ +# Some alternatives: +#DUNGEON= 032 186 205 201 187 200 188 206 202 203 \ +# 185 204 249 239 239 254 254 240 241 249 \ +# 177 177 060 062 060 062 095 124 092 035 \ +# 244 247 249 247 042 042 179 196 046 035 \ +# 247 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 034 094 094 094 094 \ +# 094 094 + +# ================================================ +# Here is a recommendation sent in by Michael Feir +# for use by blind NetHack players. +# +#DUNGEON= 032 124 045 124 124 124 124 045 045 045 \ +# 124 124 046 045 124 043 043 046 035 035 \ +# 060 062 060 062 095 092 035 126 126 126 \ +# 126 042 042 035 035 032 035 126 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 +# +#EFFECTS= 124 095 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 058 058 092 045 047 \ +# 047 045 092 058 032 058 092 045 047 + diff --git a/sys/winnt/mapimail.c b/sys/winnt/mapimail.c new file mode 100644 index 0000000..38ef942 --- /dev/null +++ b/sys/winnt/mapimail.c @@ -0,0 +1,482 @@ +/* SCCS Id: @(#)mapimail.c 3.4 $Date: 2002/07/24 08:25:20 $ */ +/* Copyright (c) Michael Allison, 1997 */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifdef MAX_BODY_SIZE +#undef MAX_BODY_SIZE +#define MAX_BODY_SIZE 2048 /* largest body held in ram in bytes */ +#endif + +#include "hack.h" +# ifdef LAN_MAIL +#include "win32api.h" +#include + +#define MAPI_MSGID_SIZE 512 /* as recommended */ +#define MAPI_LIB_FAIL 1 +#define MAPI_FUNC_FAIL 2 + +HANDLE hLibrary; /* Handle for MAPI32.DLL */ +LHANDLE MAPISession; /* Handle for MAPI session */ +char MAPIMessageID[MAPI_MSGID_SIZE]; /* Msg ID from provider */ +lpMapiMessage MAPIMessage; /* Ptr to message from prov */ +struct lan_mail_struct received_msg; /* store received msg here */ +#ifdef MAPI_VERBOSE +FILE *dbgfile; +#define MAPIDEBUGFILENAME "mapidebug.log" +#endif + +/* + * Prototypes for functions in this file + * (Not in normal NetHack style, but its doubtful that any + * of the code in here is at all portable between platforms) + */ +static long __stdcall start_mailthread(LPVOID ThreadParam); +static boolean MAPI_mail_check(char *); +static boolean MAPI_mail_fetch(char *); +static void MAPI_mail_finish(void); +static int MAPI_mail_context(int *); +static boolean MAPI_mail_init(char *); +static int InitMAPI(void); +static int DeInitMAPI(void); +static void MAPI_mail_abort(unsigned long); + +/* + * Declare the function pointers used to access MAPI routines + * from the MAPI32.DLL + */ +LPMAPILOGON fpMAPILogon; +LPMAPILOGOFF fpMAPILogoff; +LPMAPIFINDNEXT fpMAPIFindNext; +LPMAPIREADMAIL fpMAPIReadMail; +LPMAPIFREEBUFFER fpMAPIFreeBuffer; + + +/* + * Data requiring synchronized access between the + * two thread contexts contained in this file + * (nethack thread and the MAPI-mail thread) + */ +HANDLE mailthread = 0; /* handle for the mail-thread */ +unsigned nhmailthread_ID; /* thread ID for mail-thread */ +unsigned long nhmail_param; /* value passed to mail-thread */ +long mailthread_continue = 0; /* nh -> mapi thread: shut down! */ +long mailthread_stopping = 0; /* mapi -> nh thread: MAPI is ill! */ + +/* + * Data updated by the NetHack thread only + * but read by the MAPI-mail thread. + * + */ +char MAPI_username[120]; +boolean debugmapi; + +/* + * Data updated by the MAPI-mail thread only + * but read by the NetHack thread. + */ +long mail_fetched = 0; + +/* + * Data used only by the MAPI-mail thread. + */ +long mailpasses; /* counts the FindNext calls issued to MAPI */ +char msgID[80]; /* message ID of msg under manipulation */ + +/*=============================================================== + * NetHack thread routines + * + * These routines run in the context of the main NetHack thread. + * They are used to provide an interface between the NetHack + * LAN_MAIL code and the MAPI-thread code. + * + * These routines are referenced by shared code in + * sys/share/nhlan.c and they are prototyped in include/extern.h + *=============================================================== + */ +boolean mail_check() +{ + if (!mailthread_stopping) { + if (mail_fetched > 0) + return TRUE; + } else lan_mail_terminate(); + return FALSE; +} + + +boolean mail_fetch(msg) +struct lan_mail_struct *msg; +{ + /* shouldn't need to check mailthread_stopping here + as the data should be valid anyway */ + *msg = received_msg; + iflags.lan_mail_fetched = TRUE; + /* clear the marker now so new messages can arrive */ + InterlockedDecrement(&mail_fetched); + /* check to see if the MAPI-mail thread is saying "stop" */ + if (mailthread_stopping) lan_mail_terminate(); + return TRUE; +} + +void mail_finish() +{ + InterlockedDecrement(&mailthread_continue); +} + +void mail_init(uname) +char *uname; +{ + /* This routine invokes the _beginthreadex() + * run-time library call to start the execution + * of the MAPI-mail thread. It also performs + * few other preparatory tasks. + */ + if (mailthread_continue) { + /* Impossible - something is really messed up */ + /* We better do some sanity checking */ + /* Is there a valid thread handle and ID? */ + if (mailthread && nhmailthread_ID) { + /* TODO: check 'em via WIN32 call */ + /* if valid, do something about them */ + /* if not, clear them */ + } + } + mailthread = 0; + nhmailthread_ID = 0; + mailthread_continue = 0; /* no interlock needed yet */ + mailthread_stopping = 0; /* no interlock needed yet */ +#ifdef MAPI_VERBOSE + if (getenv("DEBUG_MAPI")) debugmapi = TRUE; +#endif + if (uname) + strcpy(MAPI_username, uname); + else { +#ifdef MAPI_VERBOSE + if (debugmapi) dbgfile = fopen(MAPIDEBUGFILENAME,"w"); + if (dbgfile) { + fprintf(dbgfile, + "mapi initialization bypassed because uname not available\n"); + fclose(dbgfile); + } +#endif + return; + } + mailthread = (HANDLE)_beginthreadex( + (void *)0, + (unsigned)0, + start_mailthread, + (void *)&nhmail_param, + (unsigned)0, (unsigned *)&nhmailthread_ID); +#if 0 + /* TODO: check for failure of the thread. For now nethack + * doesn't care + */ + if (!mailthread) { + + } +#endif +} + +/*=============================================================== + * MAPI-mail thread routines + * + * These routines run in the context of their own + * MAPI-mail thread. They were placed into their own + * thread, because MAPI calls can sometimes (often?) take + * a horribly long time to return (minutes even). + * + * Each of the routines below are referenced only by other + * routines below, with the exception of start_mailthread(), + * of course, which is started by a run-time library call + * issued from the main NetHack thread. + *=============================================================== + */ + +/* + * start_mailthread() is the entry point of the MAPI-mail thread. + * + */ + +static long __stdcall start_mailthread(LPVOID ThreadParam) +{ + char *lu = MAPI_username; + if(MAPI_mail_init(lu)) { + InterlockedIncrement(&mailthread_continue); + while (mailthread_continue) { + mailpasses++; + if (MAPI_mail_check(msgID)) { + if (MAPI_mail_fetch(msgID)) { + /* getting here means success */ + } + } + Sleep(MAILTHREADFREQ); + } +#ifdef MAPI_VERBOSE + if (debugmapi && !mailthread_continue) { + fprintf(dbgfile, + "MAPI-thread detected mailthread_continue change.\n"); + fprintf(dbgfile, + "NetHack has requested that the MAPI-thread cease.\n"); + } +#endif + } + return 0; +} + +static int +MAPI_mail_context(mcount) +int *mcount; +{ + unsigned long status; + char tmpID[80]; + int count = 0; + + tmpID[0] = '\0'; + MAPIMessageID[0] = '\0'; + + /* Get the ID of the first unread message */ + status = fpMAPIFindNext(MAPISession, 0, 0, 0, + MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO, + 0, tmpID); + /* Now loop through them all until we have no more */ + while (status == SUCCESS_SUCCESS) { + strcpy(MAPIMessageID, tmpID); + count++; + status = fpMAPIFindNext(MAPISession, 0, + 0, MAPIMessageID, + MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO, + 0, tmpID); + } + if (status == MAPI_E_NO_MESSAGES) { + /* context is now at last message */ + if (mcount) *mcount = count; + return SUCCESS_SUCCESS; + } + return status; +} + +static boolean +MAPI_mail_check(mID) +char *mID; +{ + unsigned long status; + char tmpID[80]; + + tmpID[0] = '\0'; + +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "MAPI_mail_check() "); +#endif + if (mail_fetched) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "returning FALSE (buffer occupied)\n"); +#endif + return FALSE; /* buffer occupied, don't bother */ + } + /* Get the ID of the next unread message if there is one */ + status = fpMAPIFindNext(MAPISession, 0, 0, MAPIMessageID, + MAPI_UNREAD_ONLY | MAPI_GUARANTEE_FIFO, + 0, tmpID); + if (status == SUCCESS_SUCCESS) { + strcpy(mID, tmpID); +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "returning TRUE\n"); +#endif + return TRUE; + } + if (status == MAPI_E_NO_MESSAGES) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "returning FALSE\n"); +#endif + return FALSE; + } +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile,"Error, check_newmail() status: %d\n", status); + MAPI_mail_abort(status); +#endif + return FALSE; +} + +static boolean +MAPI_mail_fetch(mID) +char *mID; +{ + unsigned long status; + +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "MAPI_mail_fetch() "); +#endif + /* + * Update context right away so we don't loop if there + * was a problem getting the message + */ + strcpy(MAPIMessageID, mID); + + if (mail_fetched) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "returning FALSE (buffer occupied)\n"); +#endif + return FALSE; /* buffer occupied */ + } + + status = fpMAPIReadMail(MAPISession, 0, mID, + MAPI_SUPPRESS_ATTACH | MAPI_PEEK, + 0, &MAPIMessage); + if (status == SUCCESS_SUCCESS) { + strncpy(received_msg.subject, + MAPIMessage->lpszSubject, + sizeof(received_msg.subject) - 1); + if((MAPIMessage->lpOriginator->lpszName != (char *)0) + && MAPIMessage->lpOriginator->lpszName[0] != '\0') + strncpy(received_msg.sender, + MAPIMessage->lpOriginator->lpszName, + sizeof(received_msg.sender) - 1); + else + strncpy(received_msg.sender, + MAPIMessage->lpOriginator->lpszAddress, + sizeof(received_msg.sender) - 1); + strncpy(received_msg.body, + MAPIMessage->lpszNoteText,MAX_BODY_SIZE - 1); + received_msg.body[MAX_BODY_SIZE - 1] = '\0'; + received_msg.body_in_ram = TRUE; + status = fpMAPIFreeBuffer(MAPIMessage); + InterlockedIncrement(&mail_fetched); +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, "returning TRUE\n"); +#endif + return TRUE; + } +#ifdef MAPI_VERBOSE + else + if (debugmapi) fprintf(dbgfile,"MAPIRead failed, status = %d\n", status); + if (debugmapi) fprintf(dbgfile, "returning FALSE (failed)\n"); +#endif + return FALSE; +} + +static void +MAPI_mail_finish() +{ + InterlockedIncrement(&mailthread_stopping); + (void) fpMAPILogoff(MAPISession,0,0,0); + (void) DeInitMAPI(); +#ifdef MAPI_VERBOSE + if (debugmapi) fclose(dbgfile); +#endif + (void) _endthreadex(0); +} + +static void +MAPI_mail_abort(reason) +unsigned long reason; +{ +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, + "Terminating MAPI-thread due to error %d.\n", reason); +#endif + MAPI_mail_finish(); +} + +static boolean +MAPI_mail_init(uname) +char *uname; +{ + unsigned long status; + int count = 0; + +#ifdef MAPI_VERBOSE + if (debugmapi) dbgfile = fopen(MAPIDEBUGFILENAME,"w"); + if (debugmapi) fprintf(dbgfile,"Hello %s, NetHack is initializing MAPI.\n", + uname); +#endif + status = InitMAPI(); + if (status) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile,"Error initializing MAPI %d\n", status); +#endif + return FALSE; + } + status = fpMAPILogon(0,uname,0L,0L,0L,(LPLHANDLE)&MAPISession); + if (status != SUCCESS_SUCCESS) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile,"Status of MAPI logon is %d\n", status); +#endif + return FALSE; + } +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, + "Stage 1 of MAPI initialization successful.\n"); + if (debugmapi) fprintf(dbgfile,"MAPI Session handle: %d\n", MAPISession); +#endif + status = MAPI_mail_context(&count); + if (status == SUCCESS_SUCCESS) { +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, + "Stage 2 of MAPI initialization successful.\n"); + if (debugmapi) fprintf(dbgfile,"Detected %d old unread messages.\n", + count); +#endif + return TRUE; + } +#ifdef MAPI_VERBOSE + if (debugmapi) fprintf(dbgfile, + "Error, status of MAPI_mail_context() is %d.\n", + status); + if (debugmapi) fprintf(dbgfile, + "Dismantling MAPI interface and cleaning up.\n"); +#endif + MAPI_mail_finish(); + return FALSE; +} + +int InitMAPI() +{ + + if (!(hLibrary = LoadLibrary("MAPI32.DLL"))) + return MAPI_LIB_FAIL; + + if ((fpMAPILogon = (LPMAPILOGON)GetProcAddress(hLibrary,"MAPILogon")) == NULL) + return MAPI_FUNC_FAIL; + + if ((fpMAPILogoff = (LPMAPILOGOFF)GetProcAddress(hLibrary,"MAPILogoff")) == NULL) + return MAPI_FUNC_FAIL; + + if ((fpMAPIFindNext= (LPMAPIFINDNEXT)GetProcAddress(hLibrary,"MAPIFindNext")) == NULL) + return MAPI_FUNC_FAIL; + + if ((fpMAPIReadMail= (LPMAPIREADMAIL)GetProcAddress(hLibrary,"MAPIReadMail")) == NULL) + return MAPI_FUNC_FAIL; + + if ((fpMAPIFindNext= (LPMAPIFINDNEXT)GetProcAddress(hLibrary,"MAPIFindNext")) == NULL) + return MAPI_FUNC_FAIL; + + if ((fpMAPIFreeBuffer= (LPMAPIFREEBUFFER)GetProcAddress(hLibrary,"MAPIFreeBuffer")) == NULL) + return MAPI_FUNC_FAIL; + +#ifdef MAPI_VERBOSE + if (debugmapi) { + fprintf(dbgfile,"Entry Points:\n"); + fprintf(dbgfile,"MAPILogon = %p\n",fpMAPILogon); + fprintf(dbgfile,"MAPILogoff = %p\n",fpMAPILogoff); + fprintf(dbgfile,"MAPIFindNext = %p\n",fpMAPIFindNext); + fprintf(dbgfile,"MAPIFreeBuffer = %p\n",fpMAPIReadMail); + fprintf(dbgfile,"MAPIReadMail = %p\n",fpMAPIFreeBuffer); + } +#endif + return 0; +} + +int DeInitMAPI() +{ + + fpMAPILogon = NULL; + fpMAPILogoff= NULL; + fpMAPIFindNext= NULL; + fpMAPIReadMail= NULL; + fpMAPIFreeBuffer = NULL; + FreeLibrary(hLibrary); + return 0; +} + +#endif /*LAN_MAIL*/ + diff --git a/sys/winnt/nethack.def b/sys/winnt/nethack.def new file mode 100644 index 0000000..835bc6d --- /dev/null +++ b/sys/winnt/nethack.def @@ -0,0 +1,9 @@ +NAME NETHACK +DESCRIPTION 'NetHack 3.4.1 for Windows NT' +EXETYPE WINDOWS +STUB 'WINSTUB.EXE' +CODE PRELOAD MOVEABLE DISCARDABLE +DATA PRELOAD MOVEABLE MULTIPLE +HEAPSIZE 4096 +STACKSIZE 9216 +EXPORTS WndProc diff --git a/sys/winnt/nh340key.c b/sys/winnt/nh340key.c new file mode 100644 index 0000000..e1481a4 --- /dev/null +++ b/sys/winnt/nh340key.c @@ -0,0 +1,297 @@ +/* SCCS Id: @(#)nh340key.c 3.4 $Date: 2003/11/01 23:57:00 $ */ +/* Copyright (c) NetHack PC Development Team 2003 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This is the NetHack keystroke processing from NetHack 3.4.0. + * It can be built as a run-time loadable dll (nh340key.dll), + * placed in the same directory as the nethack.exe executable, + * and loaded by specifying OPTIONS=altkeyhandler:nh340key + * in defaults.nh + */ + +static char where_to_get_source[] = "http://www.nethack.org/"; +static char author[] = "The NetHack Development Team"; + +#include "hack.h" +#include "wintty.h" +#include "win32api.h" + +extern HANDLE hConIn; +extern INPUT_RECORD ir; +char dllname[512]; +char *shortdllname; + +int FDECL(__declspec(dllexport) __stdcall +ProcessKeystroke, (HANDLE hConIn, INPUT_RECORD *ir, + boolean *valid, BOOLEAN_P numberpad, int portdebug)); + +int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) +{ + char dlltmpname[512]; + char *tmp = dlltmpname, *tmp2; + *(tmp + GetModuleFileName(hInstance, tmp, 511)) = '\0'; + (void)strcpy(dllname, tmp); + tmp2 = strrchr(dllname, '\\'); + if (tmp2) { + tmp2++; + shortdllname = tmp2; + } + return TRUE; +} + +/* + * Keyboard translation tables. + * (Adopted from the MSDOS port) + */ + +#define KEYPADLO 0x47 +#define KEYPADHI 0x53 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * Shifted keypad keys are translated to the + * shift values below. + */ + +static const struct pad { + uchar normal, shift, cntrl; +} keypad[PADKEYS] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define inmap(x,vk) (((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2') + +int __declspec(dllexport) __stdcall +ProcessKeystroke(hConIn, ir, valid, numberpad, portdebug) +HANDLE hConIn; +INPUT_RECORD *ir; +boolean *valid; +boolean numberpad; +int portdebug; +{ + int metaflags = 0, k = 0; + int keycode, vk; + unsigned char ch, pre_ch, mk = 0; + unsigned short int scan; + unsigned long shiftstate; + int altseq = 0; + const struct pad *kpad; + + shiftstate = 0L; + ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + + if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || (iskeypad(scan)) || (altseq > 0)) + *valid = TRUE; + /* if (!valid) return 0; */ + /* + * shiftstate can be checked to see if various special + * keys were pressed at the same time as the key. + * Currently we are using the ALT & SHIFT & CONTROLS. + * + * RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED, + * RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED, + * SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON, + * CAPSLOCK_ON, ENHANCED_KEY + * + * are all valid bit masks to use on shiftstate. + * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the + * left control key was pressed with the keystroke. + */ + if (iskeypad(scan)) { + kpad = numberpad ? numpad : keypad; + if (shiftstate & SHIFT_PRESSED) { + ch = kpad[scan - KEYPADLO].shift; + } + else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { + ch = kpad[scan - KEYPADLO].cntrl; + } + else { + ch = kpad[scan - KEYPADLO].normal; + } + } + else if (altseq > 0) { /* ALT sequence */ + if (vk == 0xBF) ch = M('?'); + else ch = M(tolower(keycode)); + } + if (ch == '\r') ch = '\n'; +#ifdef PORT_DEBUG + if (portdebug) { + char buf[BUFSZ]; + Sprintf(buf, + "PORTDEBUG (%s): ch=%u, sc=%u, vk=%d, sh=0x%X (ESC to end)", + shortdllname, ch, scan, vk, shiftstate); + fprintf(stdout, "\n%s", buf); + } +#endif + return ch; +} + +int __declspec(dllexport) __stdcall +NHkbhit(hConIn, ir) +HANDLE hConIn; +INPUT_RECORD *ir; +{ + int done = 0; /* true = "stop searching" */ + int retval; /* true = "we had a match" */ + DWORD count; + unsigned short int scan; + unsigned char ch; + unsigned long shiftstate; + int altseq = 0, keycode, vk; + done = 0; + retval = 0; + while (!done) + { + count = 0; + PeekConsoleInput(hConIn,ir,1,&count); + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || iskeypad(scan) || altseq) { + done = 1; /* Stop looking */ + retval = 1; /* Found what we sought */ + } else { + /* Strange Key event; let's purge it to avoid trouble */ + ReadConsoleInput(hConIn,ir,1,&count); + } + + } + else if ((ir->EventType == MOUSE_EVENT && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK))) { + done = 1; + retval = 1; + } + + else /* Discard it, it's an insignificant event */ + ReadConsoleInput(hConIn,ir,1,&count); + } else /* There are no events in console event queue */ { + done = 1; /* Stop looking */ + retval = 0; + } + } + return retval; +} + +int __declspec(dllexport) __stdcall +CheckInput(hConIn, ir, count, numpad, mode, mod, cc) +HANDLE hConIn; +INPUT_RECORD *ir; +DWORD *count; +boolean numpad; +int mode; +int *mod; +coord *cc; +{ + int ch; + boolean valid = 0, done = 0; + while (!done) { + ReadConsoleInput(hConIn,ir,1,count); + if (mode == 0) { + if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) { + ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0); + done = valid; + } + } else { + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0); + if (valid) return ch; + } else if (ir->EventType == MOUSE_EVENT) { + if ((ir->Event.MouseEvent.dwEventFlags == 0) && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) { + cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1; + cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1; + + if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON) + *mod = CLICK_1; + else if (ir->Event.MouseEvent.dwButtonState & RIGHTBUTTON) + *mod = CLICK_2; +#if 0 /* middle button */ + else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON) + *mod = CLICK_3; +#endif + return 0; + } + } + } else + done = 1; + } + } + return mode ? 0 : ch; +} + +int __declspec(dllexport) __stdcall +SourceWhere(buf) +char **buf; +{ + if (!buf) return 0; + *buf = where_to_get_source; + return 1; +} + +int __declspec(dllexport) __stdcall +SourceAuthor(buf) +char **buf; +{ + if (!buf) return 0; + *buf = author; + return 1; +} + +int __declspec(dllexport) __stdcall +KeyHandlerName(buf, full) +char **buf; +int full; +{ + if (!buf) return 0; + if (full) *buf = dllname; + else *buf = shortdllname; + return 1; +} + diff --git a/sys/winnt/nhdefkey.c b/sys/winnt/nhdefkey.c new file mode 100644 index 0000000..24c635d --- /dev/null +++ b/sys/winnt/nhdefkey.c @@ -0,0 +1,329 @@ +/* SCCS Id: @(#)nhdefkey.c 3.4 $Date: 2003/11/01 23:57:00 $ */ +/* Copyright (c) NetHack PC Development Team 2003 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This is the default NetHack keystroke processing. + * It can be built as a run-time loadable dll (nhdefkey.dll). + * Alternative keystroke handlers can be built using the + * entry points in this file as a template. + * + * Use the defaults.nh "altkeyhandler" option to set a + * different dll name (without the ".DLL" extension) to + * get different processing. Ensure that the dll referenced + * in defaults.nh exists in the same directory as NetHack in + * order for it to load successfully. + * + */ + +static char where_to_get_source[] = "http://www.nethack.org/"; +static char author[] = "The NetHack Development Team"; + +#include "hack.h" +#include "wintty.h" +#include "win32api.h" + +extern HANDLE hConIn; +extern INPUT_RECORD ir; +char dllname[512]; +char *shortdllname; + +int FDECL(__declspec(dllexport) __stdcall +ProcessKeystroke, (HANDLE hConIn, INPUT_RECORD *ir, + boolean *valid, BOOLEAN_P numberpad, int portdebug)); + +int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) +{ + char dlltmpname[512]; + char *tmp = dlltmpname, *tmp2; + *(tmp + GetModuleFileName(hInstance, tmp, 511)) = '\0'; + (void)strcpy(dllname, tmp); + tmp2 = strrchr(dllname, '\\'); + if (tmp2) { + tmp2++; + shortdllname = tmp2; + } + return TRUE; +} + +/* + * Keyboard translation tables. + * (Adopted from the MSDOS port) + */ + +#define KEYPADLO 0x47 +#define KEYPADHI 0x53 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * Shifted keypad keys are translated to the + * shift values below. + */ + +static const struct pad { + uchar normal, shift, cntrl; +} keypad[PADKEYS] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'5', M('5'), '5'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'0', M('0'), '0'}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define inmap(x,vk) (((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2') + +static BYTE KeyState[256]; + +int __declspec(dllexport) __stdcall +ProcessKeystroke(hConIn,ir, valid, numberpad, portdebug) +HANDLE hConIn; +INPUT_RECORD *ir; +boolean *valid; +boolean numberpad; +int portdebug; +{ + int metaflags = 0, k = 0; + int keycode, vk; + unsigned char ch, pre_ch, mk = 0; + unsigned short int scan; + unsigned long shiftstate; + int altseq = 0; + const struct pad *kpad; + + shiftstate = 0L; + ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + KeyState[VK_SHIFT] = (shiftstate & SHIFT_PRESSED) ? 0x81 : 0; + KeyState[VK_CONTROL] = (shiftstate & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) ? + 0x81 : 0; + KeyState[VK_CAPITAL] = (shiftstate & CAPSLOCK_ON) ? 0x81 : 0; + + if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || (iskeypad(scan)) || (altseq > 0)) + *valid = TRUE; + /* if (!valid) return 0; */ + /* + * shiftstate can be checked to see if various special + * keys were pressed at the same time as the key. + * Currently we are using the ALT & SHIFT & CONTROLS. + * + * RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED, + * RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED, + * SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON, + * CAPSLOCK_ON, ENHANCED_KEY + * + * are all valid bit masks to use on shiftstate. + * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the + * left control key was pressed with the keystroke. + */ + if (iskeypad(scan)) { + kpad = numberpad ? numpad : keypad; + if (shiftstate & SHIFT_PRESSED) { + ch = kpad[scan - KEYPADLO].shift; + } + else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { + ch = kpad[scan - KEYPADLO].cntrl; + } + else { + ch = kpad[scan - KEYPADLO].normal; + } + } + else if (altseq > 0) { /* ALT sequence */ + if (vk == 0xBF) ch = M('?'); + else ch = M(tolower(keycode)); + } + /* Attempt to work better with international keyboards. */ + else { + WORD chr[2]; + k = ToAscii(vk, scan, KeyState, chr, 0); + if (k <= 2) + switch(k) { + case 2: /* two characters */ + ch = (unsigned char)chr[1]; + *valid = TRUE; + break; + case 1: /* one character */ + ch = (unsigned char)chr[0]; + *valid = TRUE; + break; + case 0: /* no translation */ + default: /* negative */ + *valid = FALSE; + } + } + if (ch == '\r') ch = '\n'; +#ifdef PORT_DEBUG + if (portdebug) { + char buf[BUFSZ]; + Sprintf(buf, + "PORTDEBUG (%s): ch=%u, sc=%u, vk=%d, pre=%d, sh=0x%X, ta=%d (ESC to end)", + shortdllname, ch, scan, vk, pre_ch, shiftstate, k); + fprintf(stdout, "\n%s", buf); + } +#endif + return ch; +} + + +int __declspec(dllexport) __stdcall +NHkbhit(hConIn, ir) +HANDLE hConIn; +INPUT_RECORD *ir; +{ + int done = 0; /* true = "stop searching" */ + int retval; /* true = "we had a match" */ + DWORD count; + unsigned short int scan; + unsigned char ch; + unsigned long shiftstate; + int altseq = 0, keycode, vk; + done = 0; + retval = 0; + while (!done) + { + count = 0; + PeekConsoleInput(hConIn,ir,1,&count); + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + if (shiftstate & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || iskeypad(scan) || altseq) { + done = 1; /* Stop looking */ + retval = 1; /* Found what we sought */ + } else { + /* Strange Key event; let's purge it to avoid trouble */ + ReadConsoleInput(hConIn,ir,1,&count); + } + + } + else if ((ir->EventType == MOUSE_EVENT && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK))) { + done = 1; + retval = 1; + } + + else /* Discard it, it's an insignificant event */ + ReadConsoleInput(hConIn,ir,1,&count); + } else /* There are no events in console event queue */ { + done = 1; /* Stop looking */ + retval = 0; + } + } + return retval; +} + +int __declspec(dllexport) __stdcall +CheckInput(hConIn, ir, count, numpad, mode, mod, cc) +HANDLE hConIn; +INPUT_RECORD *ir; +DWORD *count; +boolean numpad; +int mode; +int *mod; +coord *cc; +{ + int ch; + boolean valid = 0, done = 0; + while (!done) { + ReadConsoleInput(hConIn,ir,1,count); + if (mode == 0) { + if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) { + ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0); + done = valid; + } + } else { + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ProcessKeystroke(hConIn, ir, &valid, numpad, 0); + if (valid) return ch; + } else if (ir->EventType == MOUSE_EVENT) { + if ((ir->Event.MouseEvent.dwEventFlags == 0) && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) { + cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1; + cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1; + + if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON) + *mod = CLICK_1; + else if (ir->Event.MouseEvent.dwButtonState & RIGHTBUTTON) + *mod = CLICK_2; +#if 0 /* middle button */ + else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON) + *mod = CLICK_3; +#endif + return 0; + } + } + } else + done = 1; + } + } + return mode ? 0 : ch; +} + +int __declspec(dllexport) __stdcall +SourceWhere(buf) +char **buf; +{ + if (!buf) return 0; + *buf = where_to_get_source; + return 1; +} + +int __declspec(dllexport) __stdcall +SourceAuthor(buf) +char **buf; +{ + if (!buf) return 0; + *buf = author; + return 1; +} + +int __declspec(dllexport) __stdcall +KeyHandlerName(buf, full) +char **buf; +int full; +{ + if (!buf) return 0; + if (full) *buf = dllname; + else *buf = shortdllname; + return 1; +} + diff --git a/sys/winnt/nhico.uu b/sys/winnt/nhico.uu new file mode 100644 index 0000000..b420c3c --- /dev/null +++ b/sys/winnt/nhico.uu @@ -0,0 +1,27 @@ +begin 600 nethack.ico +M```!``(`("`0``````#H`@``)@```!`0$```````*`$```X#```H````(``` +M`$`````!``0``````(`"````````````````````````````````@```@``` +M`("``(````"``(``@(```,#`P`"`@(````#_``#_````__\`_P```/\`_P#_ +M_P``____````````````````````````__=P```````````'=W<`#___=P`` +M````````?_?W<`]W:/=P``#_<```!WB'!G`/8'B'<``/_W<```?VAGAP#P=E +M;W``__]W<``'>`=H\`__=G_P#_!V9_<`!W=H=W``__(:/<`=P=W<` +M``__\`_W=G9GB'<`!W=P````__#_=G=X2&"/<`=W``````_P_\=T=F>(:/<' +M<```````#_9V=V5H8(AO<`````````_W=W1WAXAHA_````````#_9\<&>&!H +M!H9W````````_W=V=6=GAH@(]P``````#_?'9W9T:(!H:&=P``````_V=WQV +M=X=HA@B/<``````/]GQP=G>':(A@AW``````#_<'9\=VAH!HB&=P``````_V +M=G=X9V>&"&"'<``````/]W?'9W1@B(:(9W````^&C_?'9W1WAX:`8(=P`'`/ +M_X_P9W9W:&A@AH:''AH@(AW!W\`=H;_9V=G!H:&"&A@=P +M`(`'B(_P<'!V<&>(:(B'<`"`#_\/______=W=W=W=W!W<`__#_____?_?W=W +M=W=P=W`/``__#___]W=W=W!W<`!P```/\`#_]_]_=W<`!W``````#P```/_W +M=W<```!P```````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````H````$````"`````!``0``````,`````````` +M````````````````````````@```@````("``(````"``(``@(```,#`P`"` +M@(````#_``#_````__\`_P```/\`_P#__P``____`````````````/<````` +M=P`/_W`/<`=W<`#_\/8'!W<```\/9F!P<````/9F!@<`````]F9@9P````]F +M9@8&<```#V9F8&!P```/9F8&!G````]F9F!@<``/?V9F!@9P<`]_9F9@8'!P +M#P___W=W<'``#P__=W!P```````````````````````````````````````` +K```````````````````````````````````````````````````````````` +` +end diff --git a/sys/winnt/nhraykey.c b/sys/winnt/nhraykey.c new file mode 100644 index 0000000..a845f58 --- /dev/null +++ b/sys/winnt/nhraykey.c @@ -0,0 +1,599 @@ +/* SCCS Id: @(#)nhraykey.c 3.4 $Date: 2003/11/01 23:57:00 $ */ +/* Copyright (c) NetHack PC Development Team 2003 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Keystroke handling contributed by Ray Chason. + * The following text was written by Ray Chason. + * + * The problem + * =========== + * + * The console-mode Nethack wants both keyboard and mouse input. The + * problem is that the Windows API provides no easy way to get mouse input + * and also keyboard input properly translated according to the user's + * chosen keyboard layout. + * + * The ReadConsoleInput function returns a stream of keyboard and mouse + * events. Nethack is interested in those events that represent a key + * pressed, or a click on a mouse button. The keyboard events from + * ReadConsoleInput are not translated according to the keyboard layout, + * and do not take into account the shift, control, or alt keys. + * + * The PeekConsoleInput function works similarly to ReadConsoleInput, + * except that it does not remove an event from the queue and it returns + * instead of blocking when the queue is empty. + * + * A program can also use ReadConsole to get a properly translated stream + * of characters. Unfortunately, ReadConsole does not return mouse events, + * does not distinguish the keypad from the main keyboard, does not return + * keys shifted with Alt, and does not even return the ESC key when + * pressed. + * + * We want both the functionality of ReadConsole and the functionality of + * ReadConsoleInput. But Microsoft didn't seem to think of that. + * + * + * The solution, in the original code + * ================================== + * + * The original 3.4.1 distribution tries to get proper keyboard translation + * by passing keyboard events to the ToAscii function. This works, to some + * extent -- it takes the shift key into account, and it processes dead + * keys properly. But it doesn't take non-US keyboards into account. It + * appears that ToAscii is meant for windowed applications, and does not + * have enough information to do its job properly in a console application. + * + * + * The Finnish keyboard patch + * ========================== + * + * This patch adds the "subkeyvalue" option to the defaults.nh file. The + * user can then add OPTIONS=sukeyvalue:171/92, for instance, to replace + * the 171 character with 92, which is \. This works, once properly + * configured, but places too much burden on the user. It also bars the + * use of the substituted characters in naming objects or monsters. + * + * + * The solution presented here + * =========================== + * + * The best way I could find to combine the functionality of ReadConsole + * with that of ReadConsoleInput is simple in concept. First, call + * PeekConsoleInput to get the first event. If it represents a key press, + * call ReadConsole to retrieve the key. Otherwise, pop it off the queue + * with ReadConsoleInput and, if it's a mouse click, return it as such. + * + * But the Devil, as they say, is in the details. The problem is in + * recognizing an event that ReadConsole will return as a key. We don't + * want to call ReadConsole unless we know that it will immediately return: + * if it blocks, the mouse and the Alt sequences will cease to function + * until it returns. + * + * Separating process_keystroke into two functions, one for commands and a + * new one, process_keystroke2, for answering prompts, makes the job a lot + * easier. process_keystroke2 doesn't have to worry about mouse events or + * Alt sequences, and so the consequences are minor if ReadConsole blocks. + * process_keystroke, OTOH, never needs to return a non-ASCII character + * that was read from ReadConsole; it returns bytes with the high bit set + * only in response to an Alt sequence. + * + * So in process_keystroke, before calling ReadConsole, a bogus key event + * is pushed on the queue. This event causes ReadConsole to return, even + * if there was no other character available. Because the bogus key has + * the eighth bit set, it is filtered out. This is not done in + * process_keystroke2, because that would render dead keys unusable. + * + * A separate process_keystroke2 can also process the numeric keypad in a + * way that makes sense for prompts: just return the corresponding symbol, + * and pay no mind to number_pad or the num lock key. + * + * The recognition of Alt sequences is modified, to support the use of + * characters generated with the AltGr key. A keystroke is an Alt sequence + * if an Alt key is seen that can't be an AltGr (since an AltGr sequence + * could be a character, and in some layouts it could even be an ASCII + * character). This recognition is different on NT-based and 95-based + * Windows: + * + * * On NT-based Windows, AltGr signals as right Alt and left Ctrl + * together. So an Alt sequence is recognized if either Alt key is + * pressed and if right Alt and left Ctrl are not both present. This + * is true even if the keyboard in use does not have an AltGr key, and + * uses right Alt for AltGr. + * + * * On 95-based Windows, with a keyboard that lacks the AltGr key, the + * right Alt key is used instead. But it still signals as right Alt, + * without left Ctrl. There is no way for the application to know + * whether right Alt is Alt or AltGr, and so it is always assumed + * to be AltGr. This means that Alt sequences must be formed with + * left Alt. + * + * So the patch processes keystrokes as follows: + * + * * If the scan and virtual key codes are both 0, it's the bogus key, + * and we ignore it. + * + * * Keys on the numeric keypad are processed for commands as in the + * unpatched Nethack, and for prompts by returning the ASCII + * character, even if the num lock is off. + * + * * Alt sequences are processed for commands as in the unpatched + * Nethack, and ignored for prompts. + * + * * Control codes are returned as received, because ReadConsole will + * not return the ESC key. + * + * * Other key-down events are passed to ReadConsole. The use of + * ReadConsole is different for commands than for prompts: + * + * o For commands, the bogus key is pushed onto the queue before + * ReadConsole is called. On return, non-ASCII characters are + * filtered, so they are not mistaken for Alt sequences; this also + * filters the bogus key. + * + * o For prompts, the bogus key is not used, because that would + * interfere with dead keys. Eight bit characters may be returned, + * and are coded in the configured code page. + * + * + * Possible improvements + * ===================== + * + * Some possible improvements remain: + * + * * Integrate the existing Finnish keyboard patch, for use with non- + * QWERTY layouts such as the German QWERTZ keyboard or Dvorak. + * + * * Fix the keyboard glitches in the graphical version. Namely, dead + * keys don't work, and input comes in as ISO-8859-1 but is displayed + * as code page 437 if IBMgraphics is set on startup. + * + * * Transform incoming text to ISO-8859-1, for full compatibility with + * the graphical version. + * + * * After pushing the bogus key and calling ReadConsole, check to see + * if we got the bogus key; if so, and an Alt is pressed, process the + * event as an Alt sequence. + * + */ + +static char where_to_get_source[] = "http://www.nethack.org/"; +static char author[] = "Ray Chason"; + +#include "hack.h" +#include "wintty.h" +#include "win32api.h" + +extern HANDLE hConIn; +extern INPUT_RECORD ir; +char dllname[512]; +char *shortdllname; + +int FDECL(__declspec(dllexport) __stdcall +ProcessKeystroke, (HANDLE hConIn, INPUT_RECORD *ir, + boolean *valid, BOOLEAN_P numberpad, int portdebug)); + +static INPUT_RECORD bogus_key; + +int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) +{ + char dlltmpname[512]; + char *tmp = dlltmpname, *tmp2; + *(tmp + GetModuleFileName(hInstance, tmp, 511)) = '\0'; + (void)strcpy(dllname, tmp); + tmp2 = strrchr(dllname, '\\'); + if (tmp2) { + tmp2++; + shortdllname = tmp2; + } + /* A bogus key that will be filtered when received, to keep ReadConsole + * from blocking */ + bogus_key.EventType = KEY_EVENT; + bogus_key.Event.KeyEvent.bKeyDown = 1; + bogus_key.Event.KeyEvent.wRepeatCount = 1; + bogus_key.Event.KeyEvent.wVirtualKeyCode = 0; + bogus_key.Event.KeyEvent.wVirtualScanCode = 0; + bogus_key.Event.KeyEvent.uChar.AsciiChar = (uchar)0x80; + bogus_key.Event.KeyEvent.dwControlKeyState = 0; + return TRUE; +} + +/* + * Keyboard translation tables. + * (Adopted from the MSDOS port) + */ + +#define KEYPADLO 0x47 +#define KEYPADHI 0x53 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) +#define isnumkeypad(x) (KEYPADLO <= (x) && (x) <= 0x51 && (x) != 0x4A && (x) != 0x4E) + +/* + * Keypad keys are translated to the normal values below. + * Shifted keypad keys are translated to the + * shift values below. + */ + +static const struct pad { + uchar normal, shift, cntrl; +} keypad[PADKEYS] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define inmap(x,vk) (((x) > 'A' && (x) < 'Z') || (vk) == 0xBF || (x) == '2') + +/* Use process_keystroke for key commands, process_keystroke2 for prompts */ +/* int FDECL(process_keystroke, (INPUT_RECORD *ir, boolean *valid, int portdebug)); */ +int FDECL(process_keystroke2, (HANDLE,INPUT_RECORD *ir, boolean *valid)); +static int FDECL(is_altseq, (unsigned long shiftstate)); + +static int +is_altseq(shiftstate) +unsigned long shiftstate; +{ + /* We need to distinguish the Alt keys from the AltGr key. + * On NT-based Windows, AltGr signals as right Alt and left Ctrl together; + * on 95-based Windows, AltGr signals as right Alt only. + * So on NT, we signal Alt if either Alt is pressed and left Ctrl is not, + * and on 95, we signal Alt for left Alt only. */ + switch (shiftstate & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED)) { + case LEFT_ALT_PRESSED: + case LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED: + return 1; + + case RIGHT_ALT_PRESSED: + case RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED: + return (GetVersion() & 0x80000000) == 0; + + default: + return 0; + } +} + +int __declspec(dllexport) __stdcall +ProcessKeystroke(hConIn, ir, valid, numberpad, portdebug) +HANDLE hConIn; +INPUT_RECORD *ir; +boolean *valid; +boolean numberpad; +int portdebug; +{ + int metaflags = 0, k = 0; + int keycode, vk; + unsigned char ch, pre_ch, mk = 0; + unsigned short int scan; + unsigned long shiftstate; + int altseq = 0; + const struct pad *kpad; + DWORD count; + + shiftstate = 0L; + ch = pre_ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + if (scan == 0 && vk == 0) { + /* It's the bogus_key */ + ReadConsoleInput(hConIn,ir,1,&count); + *valid = FALSE; + return 0; + } + + if (is_altseq(shiftstate)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || (iskeypad(scan)) || (altseq > 0)) + *valid = TRUE; + /* if (!valid) return 0; */ + /* + * shiftstate can be checked to see if various special + * keys were pressed at the same time as the key. + * Currently we are using the ALT & SHIFT & CONTROLS. + * + * RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED, + * RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED, + * SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON, + * CAPSLOCK_ON, ENHANCED_KEY + * + * are all valid bit masks to use on shiftstate. + * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the + * left control key was pressed with the keystroke. + */ + if (iskeypad(scan)) { + ReadConsoleInput(hConIn,ir,1,&count); + kpad = numberpad ? numpad : keypad; + if (shiftstate & SHIFT_PRESSED) { + ch = kpad[scan - KEYPADLO].shift; + } + else if (shiftstate & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { + ch = kpad[scan - KEYPADLO].cntrl; + } + else { + ch = kpad[scan - KEYPADLO].normal; + } + } + else if (altseq > 0) { /* ALT sequence */ + ReadConsoleInput(hConIn,ir,1,&count); + if (vk == 0xBF) ch = M('?'); + else ch = M(tolower(keycode)); + } + else if (ch < 32 && !isnumkeypad(scan)) { + /* Control code; ReadConsole seems to filter some of these, + * including ESC */ + ReadConsoleInput(hConIn,ir,1,&count); + } + /* Attempt to work better with international keyboards. */ + else { + CHAR ch2; + DWORD written; + /* The bogus_key guarantees that ReadConsole will return, + * and does not itself do anything */ + WriteConsoleInput(hConIn, &bogus_key, 1, &written); + ReadConsole(hConIn,&ch2,1,&count,NULL); + /* Prevent high characters from being interpreted as alt + * sequences; also filter the bogus_key */ + if (ch2 & 0x80) + *valid = FALSE; + else + ch = ch2; + if (ch == 0) *valid = FALSE; + } + if (ch == '\r') ch = '\n'; +#ifdef PORT_DEBUG + if (portdebug) { + char buf[BUFSZ]; + Sprintf(buf, + "PORTDEBUG: ch=%u, scan=%u, vk=%d, pre=%d, shiftstate=0x%X (ESC to end)\n", + ch, scan, vk, pre_ch, shiftstate); + fprintf(stdout, "\n%s", buf); + } +#endif + return ch; +} + +int process_keystroke2(hConIn, ir, valid) +HANDLE hConIn; +INPUT_RECORD *ir; +boolean *valid; +{ + /* Use these values for the numeric keypad */ + static const char keypad_nums[] = "789-456+1230."; + + unsigned char ch; + int vk; + unsigned short int scan; + unsigned long shiftstate; + int altseq; + DWORD count; + + ch = ir->Event.KeyEvent.uChar.AsciiChar; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + scan = ir->Event.KeyEvent.wVirtualScanCode; + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + + if (scan == 0 && vk == 0) { + /* It's the bogus_key */ + ReadConsoleInput(hConIn,ir,1,&count); + *valid = FALSE; + return 0; + } + + altseq = is_altseq(shiftstate); + if (ch || (iskeypad(scan)) || altseq) + *valid = TRUE; + /* if (!valid) return 0; */ + /* + * shiftstate can be checked to see if various special + * keys were pressed at the same time as the key. + * Currently we are using the ALT & SHIFT & CONTROLS. + * + * RIGHT_ALT_PRESSED, LEFT_ALT_PRESSED, + * RIGHT_CTRL_PRESSED, LEFT_CTRL_PRESSED, + * SHIFT_PRESSED,NUMLOCK_ON, SCROLLLOCK_ON, + * CAPSLOCK_ON, ENHANCED_KEY + * + * are all valid bit masks to use on shiftstate. + * eg. (shiftstate & LEFT_CTRL_PRESSED) is true if the + * left control key was pressed with the keystroke. + */ + if (iskeypad(scan) && !altseq) { + ReadConsoleInput(hConIn,ir,1,&count); + ch = keypad_nums[scan - KEYPADLO]; + } + else if (ch < 32 && !isnumkeypad(scan)) { + /* Control code; ReadConsole seems to filter some of these, + * including ESC */ + ReadConsoleInput(hConIn,ir,1,&count); + } + /* Attempt to work better with international keyboards. */ + else { + CHAR ch2; + ReadConsole(hConIn,&ch2,1,&count,NULL); + ch = ch2 & 0xFF; + if (ch == 0) *valid = FALSE; + } + if (ch == '\r') ch = '\n'; + return ch; +} + +int __declspec(dllexport) __stdcall +CheckInput(hConIn, ir, count, numpad, mode, mod, cc) +HANDLE hConIn; +INPUT_RECORD *ir; +DWORD *count; +int *mod; +boolean numpad; +coord *cc; +{ + int ch; + boolean valid = 0, done = 0; + while (!done) { + *count = 0; + WaitForSingleObject(hConIn, INFINITE); + PeekConsoleInput(hConIn,ir,1,count); + if (mode == 0) { + if ((ir->EventType == KEY_EVENT) && ir->Event.KeyEvent.bKeyDown) { + ch = process_keystroke2(hConIn, ir, &valid); + done = valid; + } else + ReadConsoleInput(hConIn,ir,1,count); + } else { + ch = 0; + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ProcessKeystroke(hConIn, ir, &valid, numpad, +#ifdef PORTDEBUG + 1); +#else + 0); +#endif + if (valid) return ch; + } else { + ReadConsoleInput(hConIn,ir,1,count); + if (ir->EventType == MOUSE_EVENT) { + if ((ir->Event.MouseEvent.dwEventFlags == 0) && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK)) { + cc->x = ir->Event.MouseEvent.dwMousePosition.X + 1; + cc->y = ir->Event.MouseEvent.dwMousePosition.Y - 1; + + if (ir->Event.MouseEvent.dwButtonState & LEFTBUTTON) + *mod = CLICK_1; + else if (ir->Event.MouseEvent.dwButtonState & RIGHTBUTTON) + *mod = CLICK_2; +#if 0 /* middle button */ + else if (ir->Event.MouseEvent.dwButtonState & MIDBUTTON) + *mod = CLICK_3; +#endif + return 0; + } + } +#if 0 + /* We ignore these types of console events */ + else if (ir->EventType == FOCUS_EVENT) { + } + else if (ir->EventType == MENU_EVENT) { + } +#endif + } + } else + done = 1; + } + } + *mod = 0; + return ch; +} + +int __declspec(dllexport) __stdcall +NHkbhit(hConIn, ir) +HANDLE hConIn; +INPUT_RECORD *ir; +{ + int done = 0; /* true = "stop searching" */ + int retval; /* true = "we had a match" */ + DWORD count; + unsigned short int scan; + unsigned char ch; + unsigned long shiftstate; + int altseq = 0, keycode, vk; + done = 0; + retval = 0; + while (!done) + { + count = 0; + PeekConsoleInput(hConIn,ir,1,&count); + if (count > 0) { + if (ir->EventType == KEY_EVENT && ir->Event.KeyEvent.bKeyDown) { + ch = ir->Event.KeyEvent.uChar.AsciiChar; + scan = ir->Event.KeyEvent.wVirtualScanCode; + shiftstate = ir->Event.KeyEvent.dwControlKeyState; + vk = ir->Event.KeyEvent.wVirtualKeyCode; + keycode = MapVirtualKey(vk, 2); + if (is_altseq(shiftstate)) { + if (ch || inmap(keycode,vk)) altseq = 1; + else altseq = -1; /* invalid altseq */ + } + if (ch || iskeypad(scan) || altseq) { + done = 1; /* Stop looking */ + retval = 1; /* Found what we sought */ + } else { + /* Strange Key event; let's purge it to avoid trouble */ + ReadConsoleInput(hConIn,ir,1,&count); + } + + } + else if ((ir->EventType == MOUSE_EVENT && + (ir->Event.MouseEvent.dwButtonState & MOUSEMASK))) { + done = 1; + retval = 1; + } + + else /* Discard it, it's an insignificant event */ + ReadConsoleInput(hConIn,ir,1,&count); + } else /* There are no events in console event queue */ { + done = 1; /* Stop looking */ + retval = 0; + } + } + return retval; +} + + +int __declspec(dllexport) __stdcall +SourceWhere(buf) +char **buf; +{ + if (!buf) return 0; + *buf = where_to_get_source; + return 1; +} + +int __declspec(dllexport) __stdcall +SourceAuthor(buf) +char **buf; +{ + if (!buf) return 0; + *buf = author; + return 1; +} + +int __declspec(dllexport) __stdcall +KeyHandlerName(buf, full) +char **buf; +int full; +{ + if (!buf) return 0; + if (full) *buf = dllname; + else *buf = shortdllname; + return 1; +} + diff --git a/sys/winnt/nhsetup.bat b/sys/winnt/nhsetup.bat new file mode 100644 index 0000000..63abe81 --- /dev/null +++ b/sys/winnt/nhsetup.bat @@ -0,0 +1,114 @@ +@REM SCCS Id: @(#)nhsetup.bat 3.4 $Date: 2002/07/24 08:25:21 $ +@REM Copyright (c) NetHack PC Development Team 1993, 1996, 2002 +@REM NetHack may be freely redistributed. See license for details. +@REM Win32 setup batch file, see Install.nt for details +@REM +@echo off + +set _pause= + +:nxtcheck +echo Checking to see if directories are set up properly +if not exist ..\..\include\hack.h goto :err_dir +if not exist ..\..\src\hack.c goto :err_dir +if not exist ..\..\dat\wizard.des goto :err_dir +if not exist ..\..\util\makedefs.c goto :err_dir +if not exist ..\..\sys\winnt\winnt.c goto :err_dir +echo Directories look ok. + +:do_tty +if NOT exist ..\..\binary\*.* mkdir ..\..\binary +if NOT exist ..\..\binary\license copy ..\..\dat\license ..\..\binary\license >nul +echo Copying Microsoft Makefile - Makefile.msc to ..\..\src\Makefile. +if NOT exist ..\..\src\Makefile goto :domsc +copy ..\..\src\Makefile ..\..\src\Makefile-orig >nul +echo Your existing +echo ..\..\src\Makefile +echo has been renamed to +echo ..\..\src\Makefile-orig +:domsc +copy Makefile.msc ..\..\src\Makefile >nul +echo Microsoft Makefile copied ok. + +echo Copying Borland Makefile - Makefile.bcc to ..\..\src\Makefile.bcc +if NOT exist ..\..\src\Makefile.bcc goto :dobor +copy ..\..\src\Makefile.bcc ..\..\src\Makefile.bcc-orig >nul +echo Your existing +echo ..\..\src\Makefile.bcc +echo has been renamed to +echo ..\..\src\Makefile.bcc-orig +:dobor +copy Makefile.bcc ..\..\src\Makefile.bcc >nul +echo Borland Makefile copied ok. + +echo Copying MinGW Makefile - Makefile.gcc to ..\..\src\Makefile.gcc +if NOT exist ..\..\src\Makefile.gcc goto :dogcc +copy ..\..\src\Makefile.gcc ..\..\src\Makefile.gcc-orig >nul +echo Your existing +echo ..\..\src\Makefile.gcc +echo has been renamed to +echo ..\..\src\Makefile.gcc-orig +:dogcc +copy Makefile.gcc ..\..\src\Makefile.gcc >nul +echo MinGW Makefile copied ok. + +:do_win +if not exist ..\..\win\win32\nethack.dsw goto :err_win +echo. +echo Copying Visual C project files to ..\..\build directory +echo Copying ..\..\win\win32\nethack.dsw ..\..\nethack.dsw +copy ..\..\win\win32\nethack.dsw ..\.. >nul +if NOT exist ..\..\binary\*.* echo Creating ..\..\binary directory +if NOT exist ..\..\binary\*.* mkdir ..\..\binary +if NOT exist ..\..\binary\license copy ..\..\dat\license ..\..\binary\license >nul +if NOT exist ..\..\build\*.* echo Creating ..\..\build directory +if NOT exist ..\..\build\*.* mkdir ..\..\build +copy ..\..\win\win32\dgncomp.dsp ..\..\build >nul +copy ..\..\win\win32\dgnstuff.dsp ..\..\build >nul +copy ..\..\win\win32\dgnstuff.mak ..\..\build >nul +copy ..\..\win\win32\dlb_main.dsp ..\..\build >nul +copy ..\..\win\win32\levcomp.dsp ..\..\build >nul +copy ..\..\win\win32\levstuff.dsp ..\..\build >nul +copy ..\..\win\win32\levstuff.mak ..\..\build >nul +copy ..\..\win\win32\makedefs.dsp ..\..\build >nul +copy ..\..\win\win32\recover.dsp ..\..\build >nul +copy ..\..\win\win32\tile2bmp.dsp ..\..\build >nul +copy ..\..\win\win32\tiles.dsp ..\..\build >nul +copy ..\..\win\win32\tiles.mak ..\..\build >nul +copy ..\..\win\win32\tilemap.dsp ..\..\build >nul +copy ..\..\win\win32\uudecode.dsp ..\..\build >nul +copy ..\..\win\win32\nethackw.dsp ..\..\build >nul + +goto :done + +:err_win +echo Some of the files needed to build graphical NetHack +echo for Windows are not in the expected places. +echo Check "Install.nt" for a list of the steps required +echo to build NetHack. +goto :fini + +:err_data +echo A required file ..\..\dat\data.bas seems to be missing. +echo Check "Files." in the root directory for your NetHack distribution +echo and make sure that all required files exist. +goto :fini + +:err_dir +echo Your directories are not set up properly, please re-read the +echo documentation and sys/winnt/Install.nt. +goto :fini + +:done +echo done! +echo. +echo Proceed with the next step documented in Install.nt +echo. + +:fini +:end +set _pause=Y +if "%0"=="nhsetup" set _pause=N +if "%0"=="NHSETUP" set _pause=N +if "%_pause%"=="Y" pause +set _pause= diff --git a/sys/winnt/ntsound.c b/sys/winnt/ntsound.c new file mode 100644 index 0000000..5996429 --- /dev/null +++ b/sys/winnt/ntsound.c @@ -0,0 +1,28 @@ +/* SCCS Id: @(#)ntsound.c 3.4 $Date: 2002/09/02 23:28:45 $ */ +/* Copyright (c) NetHack PC Development Team 1993 */ +/* NetHack may be freely redistributed. See license for details. */ +/* */ +/* + * ntsound.c - Windows NT NetHack sound support + * + *Edit History: + * Initial Creation 93/12/11 + * + */ + +#include "hack.h" +#include "win32api.h" +#include + +#ifdef USER_SOUNDS + +void play_usersound(filename, volume) +const char* filename; +int volume; +{ +/* pline("play_usersound: %s (%d).", filename, volume); */ + (void)sndPlaySound(filename, SND_ASYNC | SND_NODEFAULT); +} + +#endif /*USER_SOUNDS*/ +/* ntsound.c */ diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c new file mode 100644 index 0000000..8684e13 --- /dev/null +++ b/sys/winnt/nttty.c @@ -0,0 +1,961 @@ +/* SCCS Id: @(#)nttty.c 3.4 $Date: 2003/11/15 00:39:32 $ */ +/* Copyright (c) NetHack PC Development Team 1993 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* tty.c - (Windows NT) version */ + +/* + * Initial Creation M. Allison 1993/01/31 + * Switch to low level console output routines M. Allison 2003/10/01 + * Restrict cursor movement until input pending M. Lehotay 2003/10/02 + * + */ + +#ifdef WIN32CON +#define NEED_VARARGS /* Uses ... */ +#include "hack.h" +#include "wintty.h" +#include +#include +#include "win32api.h" + +void FDECL(cmov, (int, int)); +void FDECL(nocmov, (int, int)); +int FDECL(process_keystroke, (INPUT_RECORD *, boolean *, + BOOLEAN_P numberpad, int portdebug)); + +/* + * The following WIN32 Console API routines are used in this file. + * + * CreateFile + * GetConsoleScreenBufferInfo + * GetStdHandle + * SetConsoleCursorPosition + * SetConsoleTextAttribute + * SetConsoleCtrlHandler + * PeekConsoleInput + * ReadConsoleInput + * WriteConsoleOutputCharacter + * FillConsoleOutputAttribute + */ + +/* Win32 Console handles for input and output */ +HANDLE hConIn; +HANDLE hConOut; + +/* Win32 Screen buffer,coordinate,console I/O information */ +CONSOLE_SCREEN_BUFFER_INFO csbi, origcsbi; +COORD ntcoord; +INPUT_RECORD ir; + +/* Flag for whether NetHack was launched via the GUI, not the command line. + * The reason we care at all, is so that we can get + * a final RETURN at the end of the game when launched from the GUI + * to prevent the scoreboard (or panic message :-|) from vanishing + * immediately after it is displayed, yet not bother when started + * from the command line. + */ +int GUILaunched; +static BOOL FDECL(CtrlHandler, (DWORD)); + +#ifdef PORT_DEBUG +static boolean display_cursor_info = FALSE; +#endif + +extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ + +/* dynamic keystroke handling .DLL support */ +typedef int (__stdcall * PROCESS_KEYSTROKE)( + HANDLE, + INPUT_RECORD *, + boolean *, + BOOLEAN_P, + int +); + +typedef int (__stdcall * NHKBHIT)( + HANDLE, + INPUT_RECORD * +); + +typedef int (__stdcall * CHECKINPUT)( + HANDLE, + INPUT_RECORD *, + DWORD *, + BOOLEAN_P, + int, + int *, + coord * +); + +typedef int (__stdcall * SOURCEWHERE)( + char ** +); + +typedef int (__stdcall * SOURCEAUTHOR)( + char ** +); + +typedef int (__stdcall * KEYHANDLERNAME)( + char **, + int +); + +HANDLE hLibrary; +PROCESS_KEYSTROKE pProcessKeystroke; +NHKBHIT pNHkbhit; +CHECKINPUT pCheckInput; +SOURCEWHERE pSourceWhere; +SOURCEAUTHOR pSourceAuthor; +KEYHANDLERNAME pKeyHandlerName; + +#ifndef CLR_MAX +#define CLR_MAX 16 +#endif +int ttycolors[CLR_MAX]; +# ifdef TEXTCOLOR +static void NDECL(init_ttycolor); +# endif +static void NDECL(really_move_cursor); + +#define MAX_OVERRIDES 256 +unsigned char key_overrides[MAX_OVERRIDES]; + +static char nullstr[] = ""; +char erase_char,kill_char; + +#define DEFTEXTCOLOR ttycolors[7] +static WORD background = 0; +static WORD foreground = (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); +static WORD attr = (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); +static DWORD ccount, acount; +static COORD cursor = {0,0}; + +/* + * Called after returning from ! or ^Z + */ +void +gettty() +{ +#ifndef TEXTCOLOR + int k; +#endif + erase_char = '\b'; + kill_char = 21; /* cntl-U */ + iflags.cbreak = TRUE; +#ifdef TEXTCOLOR + init_ttycolor(); +#else + for(k=0; k < CLR_MAX; ++k) + ttycolors[k] = 7; +#endif +} + +/* reset terminal to original state */ +void +settty(s) +const char *s; +{ + cmov(ttyDisplay->curx, ttyDisplay->cury); + end_screen(); + if(s) raw_print(s); +} + +/* called by init_nhwindows() and resume_nhwindows() */ +void +setftty() +{ + start_screen(); +} + +void +tty_startup(wid, hgt) +int *wid, *hgt; +{ + int twid = origcsbi.srWindow.Right - origcsbi.srWindow.Left + 1; + + if (twid > 80) twid = 80; + *wid = twid; + *hgt = origcsbi.srWindow.Bottom - origcsbi.srWindow.Top + 1; + set_option_mod_status("mouse_support", SET_IN_GAME); +} + +void +tty_number_pad(state) +int state; +{ +} + +void +tty_start_screen() +{ + if (iflags.num_pad) tty_number_pad(1); /* make keypad send digits */ +} + +void +tty_end_screen() +{ + clear_screen(); + really_move_cursor(); + if (GetConsoleScreenBufferInfo(hConOut,&csbi)) + { + DWORD ccnt; + COORD newcoord; + + newcoord.X = 0; + newcoord.Y = 0; + FillConsoleOutputAttribute(hConOut, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + csbi.dwSize.X * csbi.dwSize.Y, + newcoord, &ccnt); + FillConsoleOutputCharacter(hConOut,' ', + csbi.dwSize.X * csbi.dwSize.Y, + newcoord, &ccnt); + } + FlushConsoleInputBuffer(hConIn); +} + +static BOOL CtrlHandler(ctrltype) +DWORD ctrltype; +{ + switch(ctrltype) { + /* case CTRL_C_EVENT: */ + case CTRL_BREAK_EVENT: + clear_screen(); + case CTRL_CLOSE_EVENT: + case CTRL_LOGOFF_EVENT: + case CTRL_SHUTDOWN_EVENT: + getreturn_enabled = FALSE; +#ifndef NOSAVEONHANGUP + hangup(0); +#endif +#if 0 + clearlocks(); + terminate(EXIT_FAILURE); +#endif + default: + return FALSE; + } +} + +/* called by init_tty in wintty.c for WIN32CON port only */ +void +nttty_open() +{ + HANDLE hStdOut; + DWORD cmode; + long mask; + + load_keyboard_handler(); + /* Initialize the function pointer that points to + * the kbhit() equivalent, in this TTY case nttty_kbhit() + */ + nt_kbhit = nttty_kbhit; + + /* The following 6 lines of code were suggested by + * Bob Landau of Microsoft WIN32 Developer support, + * as the only current means of determining whether + * we were launched from the command prompt, or from + * the NT program manager. M. Allison + */ + hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); + GetConsoleScreenBufferInfo( hStdOut, &origcsbi); + GUILaunched = ((origcsbi.dwCursorPosition.X == 0) && + (origcsbi.dwCursorPosition.Y == 0)); + if ((origcsbi.dwSize.X <= 0) || (origcsbi.dwSize.Y <= 0)) + GUILaunched = 0; + + /* Obtain handles for the standard Console I/O devices */ + hConIn = GetStdHandle(STD_INPUT_HANDLE); + hConOut = GetStdHandle(STD_OUTPUT_HANDLE); +#if 0 + hConIn = CreateFile("CONIN$", + GENERIC_READ |GENERIC_WRITE, + FILE_SHARE_READ |FILE_SHARE_WRITE, + 0, OPEN_EXISTING, 0, 0); + hConOut = CreateFile("CONOUT$", + GENERIC_READ |GENERIC_WRITE, + FILE_SHARE_READ |FILE_SHARE_WRITE, + 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0); +#endif + + GetConsoleMode(hConIn,&cmode); +#ifdef NO_MOUSE_ALLOWED + mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | + ENABLE_MOUSE_INPUT | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT; +#else + mask = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | + ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT; +#endif + /* Turn OFF the settings specified in the mask */ + cmode &= ~mask; +#ifndef NO_MOUSE_ALLOWED + cmode |= ENABLE_MOUSE_INPUT; +#endif + SetConsoleMode(hConIn,cmode); + if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE)) { + /* Unable to set control handler */ + cmode = 0; /* just to have a statement to break on for debugger */ + } + get_scr_size(); + cursor.X = cursor.Y = 0; + really_move_cursor(); +} + +int process_keystroke(ir, valid, numberpad, portdebug) +INPUT_RECORD *ir; +boolean *valid; +boolean numberpad; +int portdebug; +{ + int ch = pProcessKeystroke(hConIn, ir, valid, numberpad, portdebug); + /* check for override */ + if (ch && ch < MAX_OVERRIDES && key_overrides[ch]) + ch = key_overrides[ch]; + return ch; +} + +int +nttty_kbhit() +{ + return pNHkbhit(hConIn, &ir); +} + + +void +get_scr_size() +{ + GetConsoleScreenBufferInfo(hConOut, &csbi); + + LI = csbi.srWindow.Bottom - (csbi.srWindow.Top + 1); + CO = csbi.srWindow.Right - (csbi.srWindow.Left + 1); + + if ( (LI < 25) || (CO < 80) ) { + COORD newcoord; + + LI = 25; + CO = 80; + + newcoord.Y = LI; + newcoord.X = CO; + + SetConsoleScreenBufferSize( hConOut, newcoord ); + } +} + +int +tgetch() +{ + int mod; + coord cc; + DWORD count; + really_move_cursor(); + return pCheckInput(hConIn, &ir, &count, iflags.num_pad, 0, &mod, &cc); +} + +int +ntposkey(x, y, mod) +int *x, *y, *mod; +{ + int ch; + coord cc; + DWORD count; + really_move_cursor(); + ch = pCheckInput(hConIn, &ir, &count, iflags.num_pad, 1, mod, &cc); + if (!ch) { + *x = cc.x; + *y = cc.y; + } + return ch; +} + +static void +really_move_cursor() +{ +#if defined(PORT_DEBUG) && defined(WIZARD) + char oldtitle[BUFSZ], newtitle[BUFSZ]; + if (display_cursor_info && wizard) { + oldtitle[0] = '\0'; + if (GetConsoleTitle(oldtitle, BUFSZ)) { + oldtitle[39] = '\0'; + } + Sprintf(newtitle, "%-55s tty=(%02d,%02d) nttty=(%02d,%02d)", + oldtitle, ttyDisplay->curx, ttyDisplay->cury, + cursor.X, cursor.Y); + (void)SetConsoleTitle(newtitle); + } +#endif + if (ttyDisplay) { + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + } + SetConsoleCursorPosition(hConOut, cursor); +} + +void +cmov(x, y) +register int x, y; +{ + ttyDisplay->cury = y; + ttyDisplay->curx = x; + cursor.X = x; + cursor.Y = y; +} + +void +nocmov(x, y) +int x,y; +{ + cursor.X = x; + cursor.Y = y; + ttyDisplay->curx = x; + ttyDisplay->cury = y; +} + +void +xputc_core(ch) +char ch; +{ + switch(ch) { + case '\n': + cursor.Y++; + /* fall through */ + case '\r': + cursor.X = 1; + break; + case '\b': + cursor.X--; + break; + default: + WriteConsoleOutputAttribute(hConOut,&attr,1, + cursor,&acount); + WriteConsoleOutputCharacter(hConOut,&ch,1, + cursor,&ccount); + cursor.X++; + } +} + +void +xputc(ch) +char ch; +{ + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + xputc_core(ch); +} + +void +xputs(s) +const char *s; +{ + int k; + int slen = strlen(s); + + if (ttyDisplay) { + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + } + + if (s) { + for (k=0; k < slen && s[k]; ++k) + xputc_core(s[k]); + } +} + + +/* + * Overrides wintty.c function of the same name + * for win32. It is used for glyphs only, not text. + */ +void +g_putch(in_ch) +int in_ch; +{ + char ch = (char)in_ch; + + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + WriteConsoleOutputAttribute(hConOut,&attr,1,cursor,&acount); + WriteConsoleOutputCharacter(hConOut,&ch,1,cursor,&ccount); +} + +void +cl_end() +{ + int cx; + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + cx = CO - cursor.X; + FillConsoleOutputAttribute(hConOut, DEFTEXTCOLOR, cx, cursor, &acount); + FillConsoleOutputCharacter(hConOut,' ', cx, cursor,&ccount); + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, + (int)ttyDisplay->cury); +} + + +void +clear_screen() +{ + if (GetConsoleScreenBufferInfo(hConOut,&csbi)) { + DWORD ccnt; + COORD newcoord; + + newcoord.X = 0; + newcoord.Y = 0; + FillConsoleOutputAttribute(hConOut, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + csbi.dwSize.X * csbi.dwSize.Y, + newcoord, &ccnt); + FillConsoleOutputCharacter(hConOut,' ', + csbi.dwSize.X * csbi.dwSize.Y, + newcoord, &ccnt); + } + home(); +} + + +void +home() +{ + cursor.X = cursor.Y = 0; + ttyDisplay->curx = ttyDisplay->cury = 0; +} + + +void +backsp() +{ + cursor.X = ttyDisplay->curx; + cursor.Y = ttyDisplay->cury; + xputc_core('\b'); +} + +void +cl_eos() +{ + int cy = ttyDisplay->cury+1; + if (GetConsoleScreenBufferInfo(hConOut,&csbi)) { + DWORD ccnt; + COORD newcoord; + + newcoord.X = ttyDisplay->curx; + newcoord.Y = ttyDisplay->cury; + FillConsoleOutputAttribute(hConOut, + FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE, + csbi.dwSize.X * csbi.dwSize.Y - cy, + newcoord, &ccnt); + FillConsoleOutputCharacter(hConOut,' ', + csbi.dwSize.X * csbi.dwSize.Y - cy, + newcoord, &ccnt); + } + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, (int)ttyDisplay->cury); +} + +void +tty_nhbell() +{ + if (flags.silent) return; + Beep(8000,500); +} + +volatile int junk; /* prevent optimizer from eliminating loop below */ + +void +tty_delay_output() +{ + /* delay 50 ms - uses ANSI C clock() function now */ + clock_t goal; + int k; + + goal = 50 + clock(); + while (goal > clock()) { + k = junk; /* Do nothing */ + } +} + +# ifdef TEXTCOLOR +/* + * CLR_BLACK 0 + * CLR_RED 1 + * CLR_GREEN 2 + * CLR_BROWN 3 low-intensity yellow + * CLR_BLUE 4 + * CLR_MAGENTA 5 + * CLR_CYAN 6 + * CLR_GRAY 7 low-intensity white + * NO_COLOR 8 + * CLR_ORANGE 9 + * CLR_BRIGHT_GREEN 10 + * CLR_YELLOW 11 + * CLR_BRIGHT_BLUE 12 + * CLR_BRIGHT_MAGENTA 13 + * CLR_BRIGHT_CYAN 14 + * CLR_WHITE 15 + * CLR_MAX 16 + * BRIGHT 8 + */ + +static void +init_ttycolor() +{ + ttycolors[CLR_BLACK] = FOREGROUND_INTENSITY; /* fix by Quietust */ + ttycolors[CLR_RED] = FOREGROUND_RED; + ttycolors[CLR_GREEN] = FOREGROUND_GREEN; + ttycolors[CLR_BROWN] = FOREGROUND_GREEN|FOREGROUND_RED; + ttycolors[CLR_BLUE] = FOREGROUND_BLUE; + ttycolors[CLR_MAGENTA] = FOREGROUND_BLUE|FOREGROUND_RED; + ttycolors[CLR_CYAN] = FOREGROUND_GREEN|FOREGROUND_BLUE; + ttycolors[CLR_GRAY] = FOREGROUND_GREEN|FOREGROUND_RED|FOREGROUND_BLUE; + ttycolors[BRIGHT] = FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED|\ + FOREGROUND_INTENSITY; + ttycolors[CLR_ORANGE] = FOREGROUND_RED|FOREGROUND_INTENSITY; + ttycolors[CLR_BRIGHT_GREEN] = FOREGROUND_GREEN|FOREGROUND_INTENSITY; + ttycolors[CLR_YELLOW] = FOREGROUND_GREEN|FOREGROUND_RED|\ + FOREGROUND_INTENSITY; + ttycolors[CLR_BRIGHT_BLUE] = FOREGROUND_BLUE|FOREGROUND_INTENSITY; + ttycolors[CLR_BRIGHT_MAGENTA] = FOREGROUND_BLUE|FOREGROUND_RED|\ + FOREGROUND_INTENSITY; + ttycolors[CLR_BRIGHT_CYAN] = FOREGROUND_GREEN|FOREGROUND_BLUE|\ + FOREGROUND_INTENSITY; + ttycolors[CLR_WHITE] = FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED|\ + FOREGROUND_INTENSITY; +} +# endif /* TEXTCOLOR */ + +int +has_color(int color) +{ +# ifdef TEXTCOLOR + return 1; +# else + if (color == CLR_BLACK) + return 1; + else if (color == CLR_WHITE) + return 1; + else + return 0; +# endif +} + +void +term_start_attr(int attrib) +{ + switch(attrib){ + case ATR_INVERSE: + if (iflags.wc_inverse) { + /* Suggestion by Lee Berger */ + if ((foreground & (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED)) == + (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED)) + foreground &= ~(FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); + background = (BACKGROUND_RED|BACKGROUND_BLUE|BACKGROUND_GREEN); + break; + } + /*FALLTHRU*/ + case ATR_ULINE: + case ATR_BLINK: + case ATR_BOLD: + foreground |= FOREGROUND_INTENSITY; + break; + default: + foreground &= ~FOREGROUND_INTENSITY; + break; + } + attr = (foreground | background); +} + +void +term_end_attr(int attrib) +{ + switch(attrib){ + + case ATR_INVERSE: + if ((foreground & (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED)) == 0) + foreground |= (FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_RED); + background = 0; + break; + case ATR_ULINE: + case ATR_BLINK: + case ATR_BOLD: + foreground &= ~FOREGROUND_INTENSITY; + break; + } + attr = (foreground | background); +} + +void +term_end_raw_bold(void) +{ + term_end_attr(ATR_BOLD); +} + +void +term_start_raw_bold(void) +{ + term_start_attr(ATR_BOLD); +} + +void +term_start_color(int color) +{ +#ifdef TEXTCOLOR + if (color >= 0 && color < CLR_MAX) { + foreground = (background != 0 && (color == CLR_GRAY || color == CLR_WHITE)) ? + ttycolors[0] : ttycolors[color]; + } +#else + foreground = DEFTEXTCOLOR; +#endif + attr = (foreground | background); +} + +void +term_end_color(void) +{ +#ifdef TEXTCOLOR + foreground = DEFTEXTCOLOR; +#endif + attr = (foreground | background); +} + + +void +standoutbeg() +{ + term_start_attr(ATR_BOLD); +} + + +void +standoutend() +{ + term_end_attr(ATR_BOLD); +} + +#ifndef NO_MOUSE_ALLOWED +void +toggle_mouse_support() +{ + DWORD cmode; + GetConsoleMode(hConIn,&cmode); + if (iflags.wc_mouse_support) + cmode |= ENABLE_MOUSE_INPUT; + else + cmode &= ~ENABLE_MOUSE_INPUT; + SetConsoleMode(hConIn,cmode); +} +#endif + +/* handle tty options updates here */ +void nttty_preference_update(pref) +const char *pref; +{ + if( stricmp( pref, "mouse_support")==0) { +#ifndef NO_MOUSE_ALLOWED + toggle_mouse_support(); +#endif + } + return; +} + +#ifdef PORT_DEBUG +void +win32con_debug_keystrokes() +{ + DWORD count; + boolean valid = 0; + int ch; + xputs("\n"); + while (!valid || ch != 27) { + nocmov(ttyDisplay->curx, ttyDisplay->cury); + ReadConsoleInput(hConIn,&ir,1,&count); + if ((ir.EventType == KEY_EVENT) && ir.Event.KeyEvent.bKeyDown) + ch = process_keystroke(&ir, &valid, iflags.num_pad, 1); + } + (void)doredraw(); +} +void +win32con_handler_info() +{ + char *buf; + int ci; + if (!pSourceAuthor && !pSourceWhere) + pline("Keyboard handler source info and author unavailable."); + else { + if (pKeyHandlerName && pKeyHandlerName(&buf, 1)) { + xputs("\n"); + xputs("Keystroke handler loaded: \n "); + xputs(buf); + } + if (pSourceAuthor && pSourceAuthor(&buf)) { + xputs("\n"); + xputs("Keystroke handler Author: \n "); + xputs(buf); + } + if (pSourceWhere && pSourceWhere(&buf)) { + xputs("\n"); + xputs("Keystroke handler source code available at:\n "); + xputs(buf); + } + xputs("\nPress any key to resume."); + ci=nhgetch(); + (void)doredraw(); + } +} + +void win32con_toggle_cursor_info() +{ + display_cursor_info = !display_cursor_info; +} +#endif + +void +map_subkeyvalue(op) +register char *op; +{ + char digits[] = "0123456789"; + int length, i, idx, val; + char *kp; + + idx = -1; + val = -1; + kp = index(op, '/'); + if (kp) { + *kp = '\0'; + kp++; + length = strlen(kp); + if (length < 1 || length > 3) return; + for (i = 0; i < length; i++) + if (!index(digits, kp[i])) return; + val = atoi(kp); + length = strlen(op); + if (length < 1 || length > 3) return; + for (i = 0; i < length; i++) + if (!index(digits, op[i])) return; + idx = atoi(op); + } + if (idx >= MAX_OVERRIDES || idx < 0 || val >= MAX_OVERRIDES || val < 1) + return; + key_overrides[idx] = val; +} + +void +load_keyboard_handler() +{ + char suffx[] = ".dll"; + char *truncspot; +#define MAX_DLLNAME 25 + char kh[MAX_ALTKEYHANDLER]; + if (iflags.altkeyhandler[0]) { + if (hLibrary) { /* already one loaded apparently */ + FreeLibrary(hLibrary); + hLibrary = (HANDLE)0; + pNHkbhit = (NHKBHIT)0; + pCheckInput = (CHECKINPUT)0; + pSourceWhere = (SOURCEWHERE)0; + pSourceAuthor = (SOURCEAUTHOR)0; + pKeyHandlerName = (KEYHANDLERNAME)0; + pProcessKeystroke = (PROCESS_KEYSTROKE)0; + } + if ((truncspot = strstri(iflags.altkeyhandler, suffx)) != 0) + *truncspot = '\0'; + (void) strncpy(kh, iflags.altkeyhandler, + (MAX_ALTKEYHANDLER - sizeof suffx) - 1); + kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0'; + Strcat(kh, suffx); + Strcpy(iflags.altkeyhandler, kh); + hLibrary = LoadLibrary(kh); + if (hLibrary) { + pProcessKeystroke = + (PROCESS_KEYSTROKE) GetProcAddress (hLibrary, TEXT ("ProcessKeystroke")); + pNHkbhit = + (NHKBHIT) GetProcAddress (hLibrary, TEXT ("NHkbhit")); + pCheckInput = + (CHECKINPUT) GetProcAddress (hLibrary, TEXT ("CheckInput")); + pSourceWhere = + (SOURCEWHERE) GetProcAddress (hLibrary, TEXT ("SourceWhere")); + pSourceAuthor = + (SOURCEAUTHOR) GetProcAddress (hLibrary, TEXT ("SourceAuthor")); + pKeyHandlerName = + (KEYHANDLERNAME) GetProcAddress (hLibrary, TEXT ("KeyHandlerName")); + } + } + if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) { + if (hLibrary) { + FreeLibrary(hLibrary); + hLibrary = (HANDLE)0; + pNHkbhit = (NHKBHIT)0; + pCheckInput = (CHECKINPUT)0; + pSourceWhere = (SOURCEWHERE)0; + pSourceAuthor = (SOURCEAUTHOR)0; + pKeyHandlerName = (KEYHANDLERNAME)0; + pProcessKeystroke = (PROCESS_KEYSTROKE)0; + } + (void)strncpy(kh, "nhdefkey.dll", (MAX_ALTKEYHANDLER - sizeof suffx) - 1); + kh[(MAX_ALTKEYHANDLER - sizeof suffx) - 1] = '\0'; + Strcpy(iflags.altkeyhandler, kh); + hLibrary = LoadLibrary(kh); + if (hLibrary) { + pProcessKeystroke = + (PROCESS_KEYSTROKE) GetProcAddress (hLibrary, TEXT ("ProcessKeystroke")); + pCheckInput = + (CHECKINPUT) GetProcAddress (hLibrary, TEXT ("CheckInput")); + pNHkbhit = + (NHKBHIT) GetProcAddress (hLibrary, TEXT ("NHkbhit")); + pSourceWhere = + (SOURCEWHERE) GetProcAddress (hLibrary, TEXT ("SourceWhere")); + pSourceAuthor = + (SOURCEAUTHOR) GetProcAddress (hLibrary, TEXT ("SourceAuthor")); + pKeyHandlerName = + (KEYHANDLERNAME) GetProcAddress (hLibrary, TEXT ("KeyHandlerName")); + } + } + if (!pProcessKeystroke || !pNHkbhit || !pCheckInput) { + if (!hLibrary) + raw_printf("\nNetHack was unable to load keystroke handler.\n"); + else { + FreeLibrary(hLibrary); + hLibrary = (HANDLE)0; + raw_printf("\nNetHack keystroke handler is invalid.\n"); + } + exit(EXIT_FAILURE); + } +} + +/* this is used as a printf() replacement when the window + * system isn't initialized yet + */ +void +msmsg VA_DECL(const char *, fmt) + char buf[ROWNO * COLNO]; /* worst case scenario */ + VA_START(fmt); + VA_INIT(fmt, const char *); + Vsprintf(buf, fmt, VA_ARGS); + VA_END(); + xputs(buf); + if (ttyDisplay) curs(BASE_WINDOW, cursor.X+1, cursor.Y); + return; +} + +/* fatal error */ +/*VARARGS1*/ +void +error VA_DECL(const char *,s) + char buf[BUFSZ]; + VA_START(s); + VA_INIT(s, const char *); + /* error() may get called before tty is initialized */ + if (iflags.window_inited) end_screen(); + buf[0] = '\n'; + (void) vsprintf(&buf[1], s, VA_ARGS); + VA_END(); + msmsg(buf); + really_move_cursor(); + exit(EXIT_FAILURE); +} + +void +synch_cursor() +{ + really_move_cursor(); +} +#endif /* WIN32CON */ diff --git a/sys/winnt/porthelp b/sys/winnt/porthelp new file mode 100644 index 0000000..0aa3e90 --- /dev/null +++ b/sys/winnt/porthelp @@ -0,0 +1,300 @@ + Microsoft Windows specific help file for NetHack 3.4.3 + Copyright (c) NetHack PC Development Team 1993-2002. + NetHack may be freely distributed. See license for details. + (Last Revision: October 14, 2003) + +This file details specifics for NetHack built for Windows 95, 98, NT, +Me, 2000, and XP. Users of really early 16-bit Windows versions should +use the MSDOS NetHack. + +Please note that "NetHack for Windows - Graphical Interface" requires +an installation of Internet Explorer 4 or an installation of +version 4.71 of the common controls. See the following internet page: + http://www.nethack.org/v340/ports/download-win.html#cc +for more information. If the game runs for you, you are not affected. + +New players should be sure to read GuideBook.txt which contains +essential information about playing NetHack. It can be found in the +same directory as your NetHack executable. + +The NetHack for Windows port supports some additional or enhanced +commands as well as some defaults.nh file options specific to +configuration choices used during the building of NetHack for +Windows. Listed below are those commands and defaults.nh file +options. + +Some options are applicable only to the "Graphical Interface." +These are discussed separately in their own section. + +Contents +1. ALT Key Combinations +2. Boolean options - Option that you can toggle on or off +3. Graphical Interface - Options you can assign a value to +4. Graphical Interface - Additional/Enhanced Commands +5. Graphical Interface - Menus +6. Numeric Keypad (for number_pad mode) + + +1. ALT Key Combinations +---------------------------------------------- +The non-graphical (tty) interface always operates in "NetHack mode", +while the "NetHack for Windows - Graphical Interface" lets you +toggle the mode. In non-NetHack mode, all ALT-key combinations +are sent to the Windows itself, rather than to NetHack. + +While playing in NetHack mode you can press the ALT key in +combination with another key to execute an extended command +as an alternative method to pressing a # key sequence. +The available commands are: + + Alt-2 #twoweapon - toggle two-weapon combat (unavailable + if number_pad mode is set) + Alt-a #adjust - adjust inventory letters. + Alt-c #chat - talk to someone or something. + Alt-d #dip - dip an object into something. + Alt-e #enhance - enhance your skill with a weapon. + Alt-f #force - force a lock. + Alt-i #invoke - invoke an object's powers. + Alt-j #jump - jump to a location. + Alt-l #loot - loot a box on the floor. + Alt-m #monster - use a monster's special ability. + Alt-n #name - name an item or type of object. + Alt-o #offer - offer a sacrifice to the gods. + Alt-p #pray - pray to the gods for help. + Alt-q #quit - quit the game. (Same as #quit) + Alt-r #rub - rub a lamp. + Alt-s #sit - sit down. + Alt-t #turn - turn undead. + Alt-u #untrap - untrap something. + Alt-v #version - list compile time options for this version of + NetHack. + Alt-w #wipe - wipe off your face. + Alt-? #? - display list of extended menu commands + +2. Boolean Options (Options that can be toggled on or off) +---------------------------------------------------------- + +Listed here are any options not discussed in the main help, options +which may be slightly different from the main help file, and options +which may need a slightly more explanatory note: + + color Use color when displaying non-tiled maps. Tiled + maps (available in the graphical port) are always + rendered in color. Default: [TRUE] + + hilite_pet Using tiled graphics, displays a small heart symbol + next to your pet. Using ascii graphics, the pet is + hilited in a white background. + Default: [TRUE] + + IBMgraphics Use IBM extended characters for the dungeon + Default: [TRUE] + + msg_window When ^P is pressed, it shows menu in a full window. + Available only in the non-graphical (tty) version. + Default: [FALSE] + + toptenwin Write top ten list to a window, as opposed to stdout. + Default in tty interface: [FALSE] + Default in graphical interface: [TRUE] (and cannot be changed) + +3. Options that you assign a value to (Graphical Interface only) +---------------------------------------------------------------- + +"NetHack for Windows - Graphical Interface" recognizes the following +additional options, which the non-graphical (tty) version will +silently ignore. These are options that specify attributes of various +windows. The windows that you can tailor include menu windows (such +as the inventory list), text windows (such as "It is written in the +book of ..." screens), the message window (where events of the game are +displayed), the status window (where your character name +and attributes are displayed), and the map window (where the map +is drawn). + +Window Alignment options: + + align_message Specifies at which side of the NetHack screen the + message window is aligned. This option can be used + to align the window to "top" or "bottom". + Default: [TOP] + + align_status Specifies at which side of the NetHack screen the + status window is aligned. This option can be used + to align the window to "top" or "bottom". + Default: [BOTTOM] + +Map Window options: + + map_mode Specifies which map mode to use. + The following map modes are available: + tiles (display things on the map with colored tiles), + ascii4x6, ascii6x8, ascii8x8, ascii16x8, ascii7x12, + ascii8x12, ascii16x12, ascii12x16, ascii10x18 + (which use that size font to display things on + the map), or fit_to_screen (an ascii mode which + forces things to fit on a single screen). + Default: [tiles] + + scroll_margin Specifies the number of map cells from the edge + of the map window where scrolling will take place. + Default: [5] + + tile_file An alternative file containing bitmap to use for + tiles. This file should be a .bmp file and should + be organized as 40 rectangular tiles wide. It is + beyond the scope of this document to describe the + exact contents of each tile in the .bmp, which must + match the object lists used when building NetHack. + + tile_height Used with tile_file to specify the height of each + tile in pixels. This option may only be specified + in the defaults.nh config file. + Default: [16] + + tile_width Used with tile_file to specify the width of each + tile in pixels. This option may only be specified + in the defaults.nh config file. + Default: [16] + +Other Window options: + + windowcolors Specifies the colors for various windows + This option may only be specified in the + defaults.nh config file and has the following + format: + window-type foreground/background + Notes: + - Both foreground and background colors are + required, and a slash must separate them. + - "window-type" is either "message" or "status" + (Short forms are: "msg" or "sts"). + - "foreground" and "background" may be specified as + a color name (such as "blue"), or by a six + digit hexadecimal RGB color value (such as + "#8F8F8F") + - The following color names are available: + black, red, green, brown, blue, magenta, + cyan, gray (or grey), orange, brightgreen, + yellow, brightblue, brightmagenta, brightcyan, + white, trueblack, purple, silver, maroon, fuchsia, + lime, olive, navy, teal, aqua. In addition, you + can use the following names to refer to default + Windows settings: activeborder, activecaption, + appworkspace, background, btnface, btnshadow, btntext, + captiontext, graytext, highlight, highlighttext, + inactiveborder, inactivecaption, menu, menutext, + scrollbar, window, windowframe, windowtext. + + Example: + OPTIONS=windowcolors:sts #00FF80/blue msg menutext/menu + + font_menu Specifies the name of the menu font. + font_message Specifies the name of the message font. + font_status Specifies the name of the status font. + font_text Specifies the name of the text font. + + font_size_menu Specifies the size of the menu font. + + font_size_message + Specifies the size of the message font. + + font_size_status + Specifies the size of the status font. + + font_size_text Specifies the size of the text font. + +Miscellaneous options: + + vary_msgcount Number of lines to display in message window. + + +4. NetHack for Windows - Graphical Interface, Additional/Enhanced Commands +------------------------------------------------------------------------- + +The following function keys are active in +the "NetHack for Windows - Graphical Interface": + + F4 Toggle level overview mode on/off + This key will toggle the map between a view that + is mapped to fit exactly to the window, and the + view that shows the various symbols in their + normal size. This is useful for getting an idea + of where you are in a level. + + F5 Toggle tiled display on/off. + This key switches between the tiled and the + traditional ASCII display. This is equivalent to + using the "map_mode" option. + + F10 Activate menu bar. + This key will activate the menu bar, allowing you + to select between the menus: File, Map, + Window Settings, and Help. + +5. Graphical Port Menus +----------------------- + +File + Save - Allows you to save and exit the game + Quit - Allows you to quit the game + +Map - Provides for selection of map mode. Equivalent to using +the map_mode option. + +Window Settings - Changes your logged-on user's settings for NetHack. +In 3.4.3, only one setting is available: NetHack mode, which can be +checked or unchecked. NetHack mode allows you to use the ALT key for +game key commands [see list above]. You can use F10 to access the +menu bar while in NetHack mode. You can also clear your logged-on +user's settings for NetHack. Settings in this window are saved in +your logged-on user's registry. + +Help - Provides help about various portions of NetHack. + + +6. Numeric Keypad (for "OPTION=number_pad" mode) +------------------------------------------------ + +The numeric keypad and surrounding characters act as macros for different +commands in NetHack. The Num Lock should be toggled to "on" to make the +most of these keys: + + Key Normal Shift-Key + ---------- ---------- ------------- + 1, 2, 3, 4 Move In Run In + 6, 7, 8, 9 Direction Direction + + 0 (Ins) Inventory Categorized + Inventory + + . (Del) Wait Turn : - Look Here + + + Spell List P - Put on an + accessory + + - m - Move Previous + Only Message + + NetHack for Windows - tty Interface Specific Behavior: + ------------------------------------------------------ + + In the non-graphical (tty) interface, when you use the Ctrl key with a + directional key (1, 2, 3, 4, 6, 7, 8, 9) it means "go in specified + direction until you hit a wall or run into something interesting." + + NetHack for Windows - Graphical Interface Specific Behavior: + ------------------------------------------------------------ + + It is possible to scroll or pan the map in a specific direction: + + Ctrl-Shift-Left (4) Scroll (Pan) map left + Ctrl-Shift-Right (6) Scroll (Pan) map right + Ctrl-Shift-Up (8) Scroll (Pan) map up + Ctrl-Shift-Down (2) Scroll (Pan) map down + Ctrl-Shift-Home (7) Scroll (Pan) map left to leftmost corner + Ctrl-Shift-End (1) Scroll (Pan) map left to rightmost corner + Ctrl-Shift-PgUp (9) Scroll (Pan) map left to uppermost corner + Ctrl-Shift-PgDn (3) Scroll (Pan) map left to lowermost corner + + + diff --git a/sys/winnt/win32api.h b/sys/winnt/win32api.h new file mode 100644 index 0000000..8c684b5 --- /dev/null +++ b/sys/winnt/win32api.h @@ -0,0 +1,27 @@ +/* SCCS Id: @(#)win32api.h 3.4 $Date: 2002/07/24 08:25:21 $ */ +/* Copyright (c) NetHack PC Development Team 1996 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This header file is used to clear up some discrepencies with Visual C + * header files & NetHack before including windows.h, so all NetHack + * files should include "win32api.h" rather than . + */ +# if defined(_MSC_VER) +# undef strcmpi +# undef min +# undef max +# pragma warning(disable:4142) /* Warning, Benign redefinition of type */ +# pragma pack(8) +# endif + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +# if defined(_MSC_VER) +# pragma pack() +# endif + +/*win32api.h*/ diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c new file mode 100644 index 0000000..768cb8a --- /dev/null +++ b/sys/winnt/winnt.c @@ -0,0 +1,324 @@ +/* SCCS Id: @(#)winnt.c 3.4 $Date: 2003/10/26 15:58:22 $ */ +/* Copyright (c) NetHack PC Development Team 1993, 1994 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * WIN32 system functions. + * + * Initial Creation: Michael Allison - January 31/93 + * + */ + +#define NEED_VARARGS +#include "hack.h" +#include +#ifndef __BORLANDC__ +#include +#endif +#include +#include "win32api.h" +#ifdef WIN32CON +#include "wintty.h" +#endif +#ifdef WIN32 + + +/* + * The following WIN32 API routines are used in this file. + * + * GetDiskFreeSpace + * GetVolumeInformation + * GetUserName + * FindFirstFile + * FindNextFile + * FindClose + * + */ + + +/* globals required within here */ +HANDLE ffhandle = (HANDLE)0; +WIN32_FIND_DATA ffd; + +/* The function pointer nt_kbhit contains a kbhit() equivalent + * which varies depending on which window port is active. + * For the tty port it is tty_kbhit() [from nttty.c] + * For the win32 port it is win32_kbhit() [from winmain.c] + * It is initialized to point to def_kbhit [in here] for safety. + */ + +int def_kbhit(void); +int (*nt_kbhit)() = def_kbhit; + +char +switchar() +{ + /* Could not locate a WIN32 API call for this- MJA */ + return '-'; +} + +long +freediskspace(path) +char *path; +{ + char tmppath[4]; + DWORD SectorsPerCluster = 0; + DWORD BytesPerSector = 0; + DWORD FreeClusters = 0; + DWORD TotalClusters = 0; + + tmppath[0] = *path; + tmppath[1] = ':'; + tmppath[2] = '\\'; + tmppath[3] = '\0'; + GetDiskFreeSpace(tmppath, &SectorsPerCluster, + &BytesPerSector, + &FreeClusters, + &TotalClusters); + return (long)(SectorsPerCluster * BytesPerSector * + FreeClusters); +} + +/* + * Functions to get filenames using wildcards + */ +int +findfirst(path) +char *path; +{ + if (ffhandle){ + FindClose(ffhandle); + ffhandle = (HANDLE)0; + } + ffhandle = FindFirstFile(path,&ffd); + return + (ffhandle == INVALID_HANDLE_VALUE) ? 0 : 1; +} + +int +findnext() +{ + return FindNextFile(ffhandle,&ffd) ? 1 : 0; +} + +char * +foundfile_buffer() +{ + return &ffd.cFileName[0]; +} + +long +filesize(file) +char *file; +{ + if (findfirst(file)) { + return ((long)ffd.nFileSizeLow); + } else + return -1L; +} + +/* + * Chdrive() changes the default drive. + */ +void +chdrive(str) +char *str; +{ + char *ptr; + char drive; + if ((ptr = index(str, ':')) != (char *)0) + { + drive = toupper(*(ptr - 1)); + _chdrive((drive - 'A') + 1); + } +} + +static int +max_filename() +{ + DWORD maxflen; + int status=0; + + status = GetVolumeInformation((LPTSTR)0,(LPTSTR)0, 0 + ,(LPDWORD)0,&maxflen,(LPDWORD)0,(LPTSTR)0,0); + if (status) return maxflen; + else return 0; +} + +int +def_kbhit() +{ + return 0; +} + +/* + * Strip out troublesome file system characters. + */ + +void +nt_regularize(s) /* normalize file name */ +register char *s; +{ + register unsigned char *lp; + + for (lp = s; *lp; lp++) + if ( *lp == '?' || *lp == '"' || *lp == '\\' || + *lp == '/' || *lp == '>' || *lp == '<' || + *lp == '*' || *lp == '|' || *lp == ':' || (*lp > 127)) + *lp = '_'; +} + +/* + * This is used in nhlan.c to implement some of the LAN_FEATURES. + */ +char *get_username(lan_username_size) +int *lan_username_size; +{ + static TCHAR username_buffer[BUFSZ]; + unsigned int status; + DWORD i = BUFSZ - 1; + + /* i gets updated with actual size */ + status = GetUserName(username_buffer, &i); + if (status) username_buffer[i] = '\0'; + else Strcpy(username_buffer, "NetHack"); + if (lan_username_size) *lan_username_size = strlen(username_buffer); + return username_buffer; +} + +# if 0 +char *getxxx() +{ +char szFullPath[MAX_PATH] = ""; +HMODULE hInst = NULL; /* NULL gets the filename of this module */ + +GetModuleFileName(hInst, szFullPath, sizeof(szFullPath)); +return &szFullPath[0]; +} +# endif + +#ifndef WIN32CON +/* fatal error */ +/*VARARGS1*/ +void +error VA_DECL(const char *,s) + char buf[BUFSZ]; + VA_START(s); + VA_INIT(s, const char *); + /* error() may get called before tty is initialized */ + if (iflags.window_inited) end_screen(); + if (!strncmpi(windowprocs.name, "tty", 3)) { + buf[0] = '\n'; + (void) vsprintf(&buf[1], s, VA_ARGS); + Strcat(buf, "\n"); + msmsg(buf); + } else { + (void) vsprintf(buf, s, VA_ARGS); + Strcat(buf, "\n"); + raw_printf(buf); + } + VA_END(); + exit(EXIT_FAILURE); +} +#endif + +void Delay(int ms) +{ + (void)Sleep(ms); +} + +#ifdef WIN32CON +extern void NDECL(backsp); +#endif + +void win32_abort() +{ +#ifdef WIZARD + if (wizard) { +# ifdef WIN32CON + int c, ci, ct; + + if (!iflags.window_inited) + c = 'n'; + ct = 0; + msmsg("Execute debug breakpoint wizard?"); + while ((ci=nhgetch()) != '\n') { + if (ct > 0) { + backsp(); /* \b is visible on NT */ + (void) putchar(' '); + backsp(); + ct = 0; + c = 'n'; + } + if (ci == 'y' || ci == 'n' || ci == 'Y' || ci == 'N') { + ct = 1; + c = ci; + msmsg("%c",c); + } + } + if (c == 'y') + DebugBreak(); +# endif + } +#endif + abort(); +} + +static char interjection_buf[INTERJECTION_TYPES][1024]; +static int interjection[INTERJECTION_TYPES]; + +void +interject_assistance(num, interjection_type, ptr1, ptr2) +int num; +int interjection_type; +genericptr_t ptr1; +genericptr_t ptr2; +{ + switch(num) { + case 1: { + char *panicmsg = (char *)ptr1; + char *datadir = (char *)ptr2; + char *tempdir = nh_getenv("TEMP"); + interjection_type = INTERJECT_PANIC; + interjection[INTERJECT_PANIC] = 1; + /* + * ptr1 = the panic message about to be delivered. + * ptr2 = the directory prefix of the dungeon file + * that failed to open. + * Check to see if datadir matches tempdir or a + * common windows temp location. If it does, inform + * the user that they are probably trying to run the + * game from within their unzip utility, so the required + * files really don't exist at the location. Instruct + * them to unpack them first. + */ + if (panicmsg && datadir) { + if (!strncmpi(datadir, "C:\\WINDOWS\\TEMP", 15) || + strstri(datadir, "TEMP") || + (tempdir && strstri(datadir, tempdir))) { + (void)strncpy(interjection_buf[INTERJECT_PANIC], + "\nOne common cause of this error is attempting to execute\n" + "the game by double-clicking on it while it is displayed\n" + "inside an unzip utility.\n\n" + "You have to unzip the contents of the zip file into a\n" + "folder on your system, and then run \"NetHack.exe\" or \n" + "\"NetHackW.exe\" from there.\n\n" + "If that is not the situation, you are encouraged to\n" + "report the error as shown above.\n\n", 1023); + } + } + } + break; + } +} + +void +interject(interjection_type) +int interjection_type; +{ + if (interjection_type >= 0 && interjection_type < INTERJECTION_TYPES) + msmsg(interjection_buf[interjection_type]); +} +#endif /* WIN32 */ + +/*winnt.c*/ diff --git a/util/dgn_comp.l b/util/dgn_comp.l new file mode 100644 index 0000000..110b51a --- /dev/null +++ b/util/dgn_comp.l @@ -0,0 +1,137 @@ +%{ +/* SCCS Id: @(#)dgn_lex.c 3.4 2002/03/27 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* Copyright (c) 1990 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +#define DGN_COMP + +#include "config.h" +#include "dgn_comp.h" +#include "dgn_file.h" + +/* + * Most of these don't exist in flex, yywrap is macro and + * yyunput is properly declared in flex.skel. + */ +#if !defined(FLEX_SCANNER) && !defined(FLEXHACK_SCANNER) +int FDECL(yyback, (int *,int)); +int NDECL(yylook); +int NDECL(yyinput); +int NDECL(yywrap); +int NDECL(yylex); + /* Traditional lexes let yyunput() and yyoutput() default to int; + * newer ones may declare them as void since they don't return + * values. For even more fun, the lex supplied as part of the + * newer unbundled compiler for SunOS 4.x adds the void declarations + * (under __STDC__ or _cplusplus ifdefs -- otherwise they remain + * int) while the bundled lex and the one with the older unbundled + * compiler do not. To detect this, we need help from outside -- + * sys/unix/Makefile.utl. + * + * Digital UNIX is difficult and still has int in spite of all + * other signs. + */ +# if defined(NeXT) || defined(SVR4) || defined(_AIX32) +# define VOIDYYPUT +# endif +# if !defined(VOIDYYPUT) && defined(POSIX_TYPES) +# if !defined(BOS) && !defined(HISX) && !defined(_M_UNIX) && !defined(VMS) +# define VOIDYYPUT +# endif +# endif +# if !defined(VOIDYYPUT) && defined(WEIRD_LEX) +# if defined(SUNOS4) && defined(__STDC__) && (WEIRD_LEX > 1) +# define VOIDYYPUT +# endif +# endif +# if defined(VOIDYYPUT) && defined(__osf__) +# undef VOIDYYPUT +# endif +# ifdef VOIDYYPUT +void FDECL(yyunput, (int)); +void FDECL(yyoutput, (int)); +# else +int FDECL(yyunput, (int)); +int FDECL(yyoutput, (int)); +# endif +#endif /* !FLEX_SCANNER && !FLEXHACK_SCANNER */ + +#ifdef FLEX_SCANNER +#define YY_MALLOC_DECL \ + genericptr_t FDECL(malloc, (size_t)); \ + genericptr_t FDECL(realloc, (genericptr_t,size_t)); +#endif + + +void FDECL(init_yyin, (FILE *)); +void FDECL(init_yyout, (FILE *)); + +/* this doesn't always get put in dgn_comp.h + * (esp. when using older versions of bison) + */ + +extern YYSTYPE yylval; + +int line_number = 1; + +%} +%% +DUNGEON return(A_DUNGEON); +up { yylval.i=1; return(UP_OR_DOWN); } +down { yylval.i=0; return(UP_OR_DOWN); } +ENTRY return(ENTRY); +stair return(STAIR); +no_up return(NO_UP); +no_down return(NO_DOWN); +portal return(PORTAL); +PROTOFILE return(PROTOFILE); +DESCRIPTION return(DESCRIPTION); +LEVELDESC return(LEVELDESC); +ALIGNMENT return(ALIGNMENT); +LEVALIGN return(LEVALIGN); +town { yylval.i=TOWN ; return(DESCRIPTOR); } +hellish { yylval.i=HELLISH ; return(DESCRIPTOR); } +mazelike { yylval.i=MAZELIKE ; return(DESCRIPTOR); } +roguelike { yylval.i=ROGUELIKE ; return(DESCRIPTOR); } +unaligned { yylval.i=D_ALIGN_NONE ; return(DESCRIPTOR); } +noalign { yylval.i=D_ALIGN_NONE ; return(DESCRIPTOR); } +lawful { yylval.i=D_ALIGN_LAWFUL ; return(DESCRIPTOR); } +neutral { yylval.i=D_ALIGN_NEUTRAL ; return(DESCRIPTOR); } +chaotic { yylval.i=D_ALIGN_CHAOTIC ; return(DESCRIPTOR); } +BRANCH return(BRANCH); +CHAINBRANCH return(CHBRANCH); +LEVEL return(LEVEL); +RNDLEVEL return(RNDLEVEL); +CHAINLEVEL return(CHLEVEL); +RNDCHLEVEL return(RNDCHLEVEL); +[-0-9]+ { yylval.i=atoi(yytext); return(INTEGER); } +\"[^"]*\" { yytext[yyleng-1] = 0; /* Discard the trailing \" */ + yylval.str = (char *) alloc(strlen(yytext+1)+1); + Strcpy(yylval.str, yytext+1); /* Discard the first \" */ + return(STRING); } +^#.*\n { line_number++; } +\r?\n { line_number++; } +[ \t]+ ; /* skip trailing tabs & spaces */ +. { return yytext[0]; } +%% + +/* routine to switch to another input file; needed for flex */ +void init_yyin( input_f ) +FILE *input_f; +{ +#if defined(FLEX_SCANNER) || defined(FLEXHACK_SCANNER) + if (yyin) + yyrestart(input_f); + else +#endif + yyin = input_f; +} +/* analogous routine (for completeness) */ +void init_yyout( output_f ) +FILE *output_f; +{ + yyout = output_f; +} + +/*dgn_comp.l*/ diff --git a/util/dgn_comp.y b/util/dgn_comp.y new file mode 100644 index 0000000..9b355cb --- /dev/null +++ b/util/dgn_comp.y @@ -0,0 +1,678 @@ +%{ +/* SCCS Id: @(#)dgn_comp.c 3.4 1996/06/22 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* Copyright (c) 1990 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the Dungeon Compiler code + */ + +/* In case we're using bison in AIX. This definition must be + * placed before any other C-language construct in the file + * excluding comments and preprocessor directives (thanks IBM + * for this wonderful feature...). + * + * Note: some cpps barf on this 'undefined control' (#pragma). + * Addition of the leading space seems to prevent barfage for now, + * and AIX will still see the directive in its non-standard locale. + */ + +#ifdef _AIX + #pragma alloca /* keep leading space! */ +#endif + +#include "config.h" +#include "date.h" +#include "dgn_file.h" + +void FDECL(yyerror, (const char *)); +void FDECL(yywarning, (const char *)); +int NDECL(yylex); +int NDECL(yyparse); +int FDECL(getchain, (char *)); +int NDECL(check_dungeon); +int NDECL(check_branch); +int NDECL(check_level); +void NDECL(init_dungeon); +void NDECL(init_branch); +void NDECL(init_level); +void NDECL(output_dgn); + +#define Free(ptr) free((genericptr_t)ptr) + +#ifdef AMIGA +# undef printf +#ifndef LATTICE +# define memset(addr,val,len) setmem(addr,len,val) +#endif +#endif + +#define ERR (-1) + +static struct couple couple; +static struct tmpdungeon tmpdungeon[MAXDUNGEON]; +static struct tmplevel tmplevel[LEV_LIMIT]; +static struct tmpbranch tmpbranch[BRANCH_LIMIT]; + +static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1; + +extern int fatal_error; +extern const char *fname; +extern FILE *yyin, *yyout; /* from dgn_lex.c */ + +%} + +%union +{ + int i; + char* str; +} + +%token INTEGER +%token A_DUNGEON BRANCH CHBRANCH LEVEL RNDLEVEL CHLEVEL RNDCHLEVEL +%token UP_OR_DOWN PROTOFILE DESCRIPTION DESCRIPTOR LEVELDESC +%token ALIGNMENT LEVALIGN ENTRY STAIR NO_UP NO_DOWN PORTAL +%token STRING +%type optional_int direction branch_type bones_tag +%start file + +%% +file : /* nothing */ + | dungeons + { + output_dgn(); + } + ; + +dungeons : dungeon + | dungeons dungeon + ; + +dungeon : dungeonline + | dungeondesc + | branches + | levels + ; + +dungeonline : A_DUNGEON ':' STRING bones_tag rcouple optional_int + { + init_dungeon(); + Strcpy(tmpdungeon[n_dgns].name, $3); + tmpdungeon[n_dgns].boneschar = (char)$4; + tmpdungeon[n_dgns].lev.base = couple.base; + tmpdungeon[n_dgns].lev.rand = couple.rand; + tmpdungeon[n_dgns].chance = $6; + Free($3); + } + ; + +optional_int : /* nothing */ + { + $$ = 0; + } + | INTEGER + { + $$ = $1; + } + ; + +dungeondesc : entry + | descriptions + | prototype + ; + +entry : ENTRY ':' INTEGER + { + tmpdungeon[n_dgns].entry_lev = $3; + } + ; + +descriptions : desc + ; + +desc : DESCRIPTION ':' DESCRIPTOR + { + if($3 <= TOWN || $3 >= D_ALIGN_CHAOTIC) + yyerror("Illegal description - ignoring!"); + else + tmpdungeon[n_dgns].flags |= $3 ; + } + | ALIGNMENT ':' DESCRIPTOR + { + if($3 && $3 < D_ALIGN_CHAOTIC) + yyerror("Illegal alignment - ignoring!"); + else + tmpdungeon[n_dgns].flags |= $3 ; + } + ; + +prototype : PROTOFILE ':' STRING + { + Strcpy(tmpdungeon[n_dgns].protoname, $3); + Free($3); + } + ; + +levels : level1 + | level2 + | levdesc + | chlevel1 + | chlevel2 + ; + +level1 : LEVEL ':' STRING bones_tag '@' acouple + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmpdungeon[n_dgns].levels++; + Free($3); + } + | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].rndlevs = $7; + tmpdungeon[n_dgns].levels++; + Free($3); + } + ; + +level2 : LEVEL ':' STRING bones_tag '@' acouple INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = $7; + tmpdungeon[n_dgns].levels++; + Free($3); + } + | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = $7; + tmplevel[n_levs].rndlevs = $8; + tmpdungeon[n_dgns].levels++; + Free($3); + } + ; + +levdesc : LEVELDESC ':' DESCRIPTOR + { + if($3 >= D_ALIGN_CHAOTIC) + yyerror("Illegal description - ignoring!"); + else + tmplevel[n_levs].flags |= $3 ; + } + | LEVALIGN ':' DESCRIPTOR + { + if($3 && $3 < D_ALIGN_CHAOTIC) + yyerror("Illegal alignment - ignoring!"); + else + tmplevel[n_levs].flags |= $3 ; + } + ; + +chlevel1 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].chain = getchain($5); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free($3); + Free($5); + } + | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].chain = getchain($5); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].rndlevs = $8; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free($3); + Free($5); + } + ; + +chlevel2 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].chain = getchain($5); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = $8; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free($3); + Free($5); + } + | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER + { + init_level(); + Strcpy(tmplevel[n_levs].name, $3); + tmplevel[n_levs].boneschar = (char)$4; + tmplevel[n_levs].chain = getchain($5); + tmplevel[n_levs].lev.base = couple.base; + tmplevel[n_levs].lev.rand = couple.rand; + tmplevel[n_levs].chance = $8; + tmplevel[n_levs].rndlevs = $9; + if(!check_level()) n_levs--; + else tmpdungeon[n_dgns].levels++; + Free($3); + Free($5); + } + ; + +branches : branch + | chbranch + ; + +branch : BRANCH ':' STRING '@' acouple branch_type direction + { + init_branch(); + Strcpy(tmpbranch[n_brs].name, $3); + tmpbranch[n_brs].lev.base = couple.base; + tmpbranch[n_brs].lev.rand = couple.rand; + tmpbranch[n_brs].type = $6; + tmpbranch[n_brs].up = $7; + if(!check_branch()) n_brs--; + else tmpdungeon[n_dgns].branches++; + Free($3); + } + ; + +chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction + { + init_branch(); + Strcpy(tmpbranch[n_brs].name, $3); + tmpbranch[n_brs].chain = getchain($4); + tmpbranch[n_brs].lev.base = couple.base; + tmpbranch[n_brs].lev.rand = couple.rand; + tmpbranch[n_brs].type = $7; + tmpbranch[n_brs].up = $8; + if(!check_branch()) n_brs--; + else tmpdungeon[n_dgns].branches++; + Free($3); + Free($4); + } + ; + +branch_type : /* nothing */ + { + $$ = TBR_STAIR; /* two way stair */ + } + | STAIR + { + $$ = TBR_STAIR; /* two way stair */ + } + | NO_UP + { + $$ = TBR_NO_UP; /* no up staircase */ + } + | NO_DOWN + { + $$ = TBR_NO_DOWN; /* no down staircase */ + } + | PORTAL + { + $$ = TBR_PORTAL; /* portal connection */ + } + ; + +direction : /* nothing */ + { + $$ = 0; /* defaults to down */ + } + | UP_OR_DOWN + { + $$ = $1; + } + ; + +bones_tag : STRING + { + char *p = $1; + if (strlen(p) != 1) { + if (strcmp(p, "none") != 0) + yyerror("Bones marker must be a single char, or \"none\"!"); + *p = '\0'; + } + $$ = *p; + Free(p); + } + ; + +/* + * acouple rules: + * + * (base, range) where: + * + * base is either a positive or negative integer with a value + * less than or equal to MAXLEVEL. + * base > 0 indicates the base level. + * base < 0 indicates reverse index (-1 == lowest level) + * + * range is the random component. + * if range is zero, there is no random component. + * if range is -1 the dungeon loader will randomize between + * the base and the end of the dungeon. + * during dungeon load, range is always *added* to the base, + * therefore range + base(converted) must not exceed MAXLEVEL. + */ +acouple : '(' INTEGER ',' INTEGER ')' + { + if ($2 < -MAXLEVEL || $2 > MAXLEVEL) { + yyerror("Abs base out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else if ($4 < -1 || + (($2 < 0) ? (MAXLEVEL + $2 + $4 + 1) > MAXLEVEL : + ($2 + $4) > MAXLEVEL)) { + yyerror("Abs range out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else { + couple.base = $2; + couple.rand = $4; + } + } + ; + +/* + * rcouple rules: + * + * (base, range) where: + * + * base is either a positive or negative integer with a value + * less than or equal to MAXLEVEL. + * base > 0 indicates a forward index. + * base < 0 indicates a reverse index. + * base == 0 indicates on the parent level. + * + * range is the random component. + * if range is zero, there is no random component. + * during dungeon load, range is always *added* to the base, + * range + base(converted) may be very large. The dungeon + * loader will then correct to "between here and the top/bottom". + * + * There is no practical way of specifying "between here and the + * nth / nth last level". + */ +rcouple : '(' INTEGER ',' INTEGER ')' + { + if ($2 < -MAXLEVEL || $2 > MAXLEVEL) { + yyerror("Rel base out of dlevel range - zeroing!"); + couple.base = couple.rand = 0; + } else { + couple.base = $2; + couple.rand = $4; + } + } + ; +%% + +void +init_dungeon() +{ + if(++n_dgns > MAXDUNGEON) { + (void) fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n", + MAXDUNGEON); + (void) fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n"); + exit(EXIT_FAILURE); + } + + in_dungeon = 1; + tmpdungeon[n_dgns].lev.base = 0; + tmpdungeon[n_dgns].lev.rand = 0; + tmpdungeon[n_dgns].chance = 100; + Strcpy(tmpdungeon[n_dgns].name, ""); + Strcpy(tmpdungeon[n_dgns].protoname, ""); + tmpdungeon[n_dgns].flags = 0; + tmpdungeon[n_dgns].levels = 0; + tmpdungeon[n_dgns].branches = 0; + tmpdungeon[n_dgns].entry_lev = 0; +} + +void +init_level() +{ + if(++n_levs > LEV_LIMIT) { + + yyerror("FATAL - Too many special levels defined."); + exit(EXIT_FAILURE); + } + tmplevel[n_levs].lev.base = 0; + tmplevel[n_levs].lev.rand = 0; + tmplevel[n_levs].chance = 100; + tmplevel[n_levs].rndlevs = 0; + tmplevel[n_levs].flags = 0; + Strcpy(tmplevel[n_levs].name, ""); + tmplevel[n_levs].chain = -1; +} + +void +init_branch() +{ + if(++n_brs > BRANCH_LIMIT) { + + yyerror("FATAL - Too many special levels defined."); + exit(EXIT_FAILURE); + } + tmpbranch[n_brs].lev.base = 0; + tmpbranch[n_brs].lev.rand = 0; + Strcpy(tmpbranch[n_brs].name, ""); + tmpbranch[n_brs].chain = -1; +} + +int +getchain(s) + char *s; +{ + int i; + + if(strlen(s)) { + + for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++) + if(!strcmp(tmplevel[i].name, s)) return i; + + yyerror("Can't locate the specified chain level."); + return(-2); + } + return(-1); +} + +/* + * Consistancy checking routines: + * + * - A dungeon must have a unique name. + * - A dungeon must have a originating "branch" command + * (except, of course, for the first dungeon). + * - A dungeon must have a proper depth (at least (1, 0)). + */ + +int +check_dungeon() +{ + int i; + + for(i = 0; i < n_dgns; i++) + if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) { + yyerror("Duplicate dungeon name."); + return(0); + } + + if(n_dgns) + for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) { + if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break; + + if(i >= n_brs - tmpdungeon[n_dgns].branches) { + yyerror("Dungeon cannot be reached."); + return(0); + } + } + + if(tmpdungeon[n_dgns].lev.base <= 0 || + tmpdungeon[n_dgns].lev.rand < 0) { + yyerror("Invalid dungeon depth specified."); + return(0); + } + return(1); /* OK */ +} + +/* + * - A level must have a unique level name. + * - If chained, the level used as reference for the chain + * must be in this dungeon, must be previously defined, and + * the level chained from must be "non-probabilistic" (ie. + * have a 100% chance of existing). + */ + +int +check_level() +{ + int i; + + if(!in_dungeon) { + yyerror("Level defined outside of dungeon."); + return(0); + } + + for(i = 0; i < n_levs; i++) + if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) { + yyerror("Duplicate level name."); + return(0); + } + + if(tmplevel[i].chain == -2) { + yyerror("Invaild level chain reference."); + return(0); + } else if(tmplevel[i].chain != -1) { /* there is a chain */ + /* KMH -- tmplevel[tmpbranch[i].chain].chance was in error */ + if(tmplevel[tmplevel[i].chain].chance != 100) { + yyerror("Level cannot chain from a probabilistic level."); + return(0); + } else if(tmplevel[i].chain == n_levs) { + yyerror("A level cannot chain to itself!"); + return(0); + } + } + return(1); /* OK */ +} + +/* + * - A branch may not branch backwards - to avoid branch loops. + * - A branch name must be unique. + * (ie. You can only have one entry point to each dungeon). + * - If chained, the level used as reference for the chain + * must be in this dungeon, must be previously defined, and + * the level chained from must be "non-probabilistic" (ie. + * have a 100% chance of existing). + */ + +int +check_branch() +{ + int i; + + if(!in_dungeon) { + yyerror("Branch defined outside of dungeon."); + return(0); + } + + for(i = 0; i < n_dgns; i++) + if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) { + + yyerror("Reverse branching not allowed."); + return(0); + } + + if(tmpbranch[i].chain == -2) { + + yyerror("Invaild branch chain reference."); + return(0); + } else if(tmpbranch[i].chain != -1) { /* it is chained */ + + if(tmplevel[tmpbranch[i].chain].chance != 100) { + yyerror("Branch cannot chain from a probabilistic level."); + return(0); + } + } + return(1); /* OK */ +} + +/* + * Output the dungon definition into a file. + * + * The file will have the following format: + * + * [ nethack version ID ] + * [ number of dungeons ] + * [ first dungeon struct ] + * [ levels for the first dungeon ] + * ... + * [ branches for the first dungeon ] + * ... + * [ second dungeon struct ] + * ... + */ + +void +output_dgn() +{ + int nd, cl = 0, nl = 0, + cb = 0, nb = 0; + static struct version_info version_data = { + VERSION_NUMBER, VERSION_FEATURES, + VERSION_SANITY1, VERSION_SANITY2 + }; + + if(++n_dgns <= 0) { + yyerror("FATAL - no dungeons were defined."); + exit(EXIT_FAILURE); + } + + if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) { + yyerror("FATAL - output failure."); + exit(EXIT_FAILURE); + } + + (void) fwrite((char *)&n_dgns, sizeof(int), 1, yyout); + for (nd = 0; nd < n_dgns; nd++) { + (void) fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon), + 1, yyout); + + nl += tmpdungeon[nd].levels; + for(; cl < nl; cl++) + (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel), + 1, yyout); + + nb += tmpdungeon[nd].branches; + for(; cb < nb; cb++) + (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch), + 1, yyout); + } + /* apparently necessary for Think C 5.x, otherwise harmless */ + (void) fflush(yyout); +} + +/*dgn_comp.y*/ diff --git a/util/dgn_main.c b/util/dgn_main.c new file mode 100644 index 0000000..75e5199 --- /dev/null +++ b/util/dgn_main.c @@ -0,0 +1,189 @@ +/* SCCS Id: @(#)dgn_main.c 3.4 1994/09/23 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* Copyright (c) 1990 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the main function for the parser + * and some useful functions needed by yacc + */ + +#include "config.h" +#include "dlb.h" + +/* Macintosh-specific code */ +#if defined(__APPLE__) && defined(__MACH__) + /* MacOS X has Unix-style files and processes */ +# undef MAC +#endif +#ifdef MAC +# if defined(__SC__) || defined(__MRC__) +# define MPWTOOL +#include +# else + /* put dungeon file in library location */ +# define PREFIX ":lib:" +# endif +#endif + +#ifndef MPWTOOL +# define SpinCursor(x) +#endif + +#define MAX_ERRORS 25 + +extern int NDECL (yyparse); +extern int line_number; +const char *fname = "(stdin)"; +int fatal_error = 0; + +int FDECL (main, (int,char **)); +void FDECL (yyerror, (const char *)); +void FDECL (yywarning, (const char *)); +int NDECL (yywrap); +void FDECL (init_yyin, (FILE *)); +void FDECL (init_yyout, (FILE *)); + +#ifdef AZTEC_36 +FILE *FDECL (freopen, (char *,char *,FILE *)); +#endif +#define Fprintf (void)fprintf + +#if defined(__BORLANDC__) && !defined(_WIN32) +extern unsigned _stklen = STKSIZ; +#endif +int +main(argc, argv) +int argc; +char **argv; +{ + char infile[64], outfile[64], basename[64]; + FILE *fin, *fout; + int i, len; + boolean errors_encountered = FALSE; +#if defined(MAC) && (defined(THINK_C) || defined(__MWERKS__)) + char *mark; + static char *mac_argv[] = { "dgn_comp", /* dummy argv[0] */ + ":dat:dungeon.pdf" + }; + + argc = SIZE(mac_argv); + argv = mac_argv; +#endif + + Strcpy(infile, "(stdin)"); + fin = stdin; + Strcpy(outfile, "(stdout)"); + fout = stdout; + + if (argc == 1) { /* Read standard input */ + init_yyin(fin); + init_yyout(fout); + (void) yyparse(); + if (fatal_error > 0) + errors_encountered = TRUE; + } else { /* Otherwise every argument is a filename */ + for(i=1; i 0) { + errors_encountered = TRUE; + fatal_error = 0; + } + } + } + if (fout && fclose(fout) < 0) { + Fprintf(stderr, "Can't finish output file."); + perror(outfile); + errors_encountered = TRUE; + } + exit(errors_encountered ? EXIT_FAILURE : EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +/* + * Each time the parser detects an error, it uses this function. + * Here we take count of the errors. To continue farther than + * MAX_ERRORS wouldn't be reasonable. + */ + +void yyerror(s) +const char *s; +{ + (void) fprintf(stderr,"%s : line %d : %s\n",fname,line_number, s); + if (++fatal_error > MAX_ERRORS) { + (void) fprintf(stderr,"Too many errors, good bye!\n"); + exit(EXIT_FAILURE); + } +} + +/* + * Just display a warning (that is : a non fatal error) + */ + +void yywarning(s) +const char *s; +{ + (void) fprintf(stderr,"%s : line %d : WARNING : %s\n",fname,line_number,s); +} + +int yywrap() +{ + SpinCursor(3); /* Don't know if this is a good place to put it ? + Is it called for our grammar ? Often enough ? + Too often ? -- h+ */ + return 1; +} + +/*dgn_main.c*/ diff --git a/util/dlb_main.c b/util/dlb_main.c new file mode 100644 index 0000000..a943a92 --- /dev/null +++ b/util/dlb_main.c @@ -0,0 +1,543 @@ +/* SCCS Id: @(#)dlb_main.c 3.4 1998/08/16 */ +/* Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1993. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* data librarian; only useful if you are making the library version, DLBLIB */ + +#include "config.h" +#include "dlb.h" +#if !defined(O_WRONLY) && !defined(MAC) && !defined(AZTEC_C) +#include +#endif +#if defined(__DJGPP__) +#include +#endif + +static void FDECL(xexit, (int)); + +#ifdef DLB +#ifdef DLBLIB + +#define DLB_DIRECTORY "Directory" /* name of lib directory */ +#define LIBLISTFILE "dlb.lst" /* default list file */ + +/* library functions (from dlb.c) */ +extern boolean FDECL(open_library,(const char *,library *)); +extern void FDECL(close_library,(library *)); + +char *FDECL(eos, (char *)); /* also used by dlb.c */ +FILE *FDECL(fopen_datafile, (const char *,const char *)); + +#ifdef VMS +extern char *FDECL(vms_basename, (const char *)); +extern int FDECL(vms_open, (const char *,int,unsigned int)); +#endif + +static void FDECL(Write, (int,char *,long)); +static void NDECL(usage); +static void NDECL(verbose_help); +static void FDECL(write_dlb_directory, (int,int,libdir *,long,long,long)); + +static char default_progname[] = "dlb"; +static char *progname = default_progname; + +/* fixed library and list file names - can be overridden if necessary */ +static const char *library_file = DLBFILE; +static const char *list_file = LIBLISTFILE; + +#ifdef AMIGA +static char origdir[255]=""; +#endif + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#define MAX_DLB_FILES 200 /* max # of files we'll handle */ +#define DLB_VERS 1 /* version of dlb file we will write */ + +/* + * How the file is encoded within the library. Don't use a space + * because (at least) the SunOS 4.1.3 C library will eat the white + * space instead of preserving it like the man page says it should. + */ +#define ENC_NORMAL 'n' /* normal: not compressed in any way */ + + +/* + * If you know tar, you have a small clue how to use this (note: - does + * NOT mean stdin/stdout). + * + * dlb COMMANDoptions arg... files... + * commands: + * dlb x extract all files + * dlb c build the archive + * dlb t list the archive + * options: + * v verbose + * f file specify archive file (default DLBFILE) + * I file specify file for list of files (default LIBLISTFILE) + * C dir chdir to dir (used ONCE, not like tar's -C) + */ + +static void +usage() +{ + (void) printf("Usage: %s [ctxCIfv] arguments... [files...]\n", progname); + (void) printf(" default library is %s\n", library_file); + (void) printf(" default list file is %s\n", list_file); + xexit(EXIT_FAILURE); +} + +static void +verbose_help() +{ + static const char *long_help[] = { + "", + "dlb COMMANDoptions args... files...", + " commands:", + " dlb ? print this text", + " dlb h ditto", + " dlb x extract all files", + " dlb c create the archive", + " dlb t list table of contents", + " options:", + " v verbose operation", + " f file specify archive file name", + " I file specify file for list of file names", + " C dir change directory before processing any files", + "", + (char *)0 + }; + const char **str; + + for (str = long_help; *str; str++) + (void) printf("%s\n", *str); + usage(); +} + +static void +Write(out,buf,len) + int out; + char *buf; + long len; +{ +#if defined(MSDOS) && !defined(__DJGPP__) + unsigned short slen; + + if (len > 65534) { + printf("%d Length specified for write() too large for 16 bit env.", + len); + xexit(EXIT_FAILURE); + } + slen = (unsigned short)len; + if (write(out,buf,slen) != slen) { +#else + if (write(out,buf,len) != len) { +#endif + printf("Write Error in '%s'\n",library_file); + xexit(EXIT_FAILURE); + } +} + + +char * +eos(s) + char *s; +{ + while (*s) s++; + return s; +} + + +#ifdef VMS /* essential to have punctuation, to avoid logical names */ +static FILE * +vms_fopen(filename, mode) +const char *filename, *mode; +{ + char tmp[BUFSIZ]; + + if (!index(filename, '.') && !index(filename, ';')) + filename = strcat(strcpy(tmp, filename), ";0"); + return fopen(filename, mode, "mbc=16"); +} +#define fopen vms_fopen +#endif /* VMS */ + +/* open_library(dlb.c) needs this (which normally comes from src/files.c) */ +FILE * +fopen_datafile(filename, mode) +const char *filename, *mode; +{ + return fopen(filename, mode); +} + +#endif /* DLBLIB */ +#endif /* DLB */ + +int +main(argc, argv) + int argc; + char **argv; +{ +#ifdef DLB +#ifdef DLBLIB + int i, r; + int ap=2; /* argument pointer */ + int cp; /* command pointer */ + int iseen=0, fseen=0, verbose=0; /* flags */ + char action=' '; + library lib; + + if (argc > 0 && argv[0] && *argv[0]) progname = argv[0]; +#ifdef VMS + progname = vms_basename(progname); +#endif + + if (argc<2) { + usage(); + /* doesn't return */ + } + + for(cp=0;argv[1][cp];cp++){ + switch(argv[1][cp]){ + default: + usage(); /* doesn't return */ + case '-': /* silently ignore */ + break; + case '?': + case 'h': + verbose_help(); + break; + case 'I': + if (ap == argc) usage(); + list_file=argv[ap++]; + if(iseen) + printf("Warning: multiple I options. Previous ignored.\n"); + iseen=1; + break; + case 'f': + if (ap == argc) usage(); + library_file=argv[ap++]; + if(fseen) + printf("Warning: multiple f options. Previous ignored.\n"); + fseen=1; + break; + case 'C': + if (ap == argc) usage(); +#ifdef AMIGA + if(!getcwd(origdir,sizeof(origdir))){ + printf("Can't get current directory.\n"); + xexit(EXIT_FAILURE); + } +#endif + if(chdir(argv[ap++])){ + printf("Can't chdir to %s\n",argv[--ap]); + xexit(EXIT_FAILURE); + } + break; + case 'v': + verbose=1; + break; + case 't': + case 'c': + case 'x': + if(action != ' '){ + printf("Only one of t,x,c may be specified.\n"); + usage(); + } + action=argv[1][cp]; + break; + } + } + + if(argv[ap] && iseen){ + printf("Too many arguments.\n"); + xexit(EXIT_FAILURE); + } + + switch(action){ + default: + printf("Internal error - action.\n"); + xexit(EXIT_FAILURE); + break; + case 't': /* list archive */ + if (!open_library(library_file, &lib)) { + printf("Can't open dlb file\n"); + xexit(EXIT_FAILURE); + } + + for (i = 0; i < lib.nentries; i++) { + if (verbose) + printf("%-14s %6ld %6ld\n", + lib.dir[i].fname, lib.dir[i].foffset, lib.dir[i].fsize); + else + printf("%s\n", lib.dir[i].fname); + } + + if (verbose) + printf("Revision:%ld File count:%ld String size:%ld\n", + lib.rev, lib.nentries, lib.strsize); + + close_library(&lib); + xexit(EXIT_SUCCESS); + + case 'x': { /* extract archive contents */ + int f, n; + long remainder, total_read; + char buf[BUFSIZ]; + + if (!open_library(library_file, &lib)) { + printf("Can't open dlb file\n"); + xexit(EXIT_FAILURE); + } + + for (i = 0; i < lib.nentries; i++) { + if (argv[ap]) { + /* if files are listed, see if current is wanted */ + int c; + for (c = ap; c < argc; c++) + if (!FILENAME_CMP(lib.dir[i].fname, argv[c])) break; + if (c == argc) continue; /* skip */ + } else if (!FILENAME_CMP(lib.dir[i].fname, DLB_DIRECTORY)) { + /* + * Don't extract the directory unless the user + * specifically asks for it. + * + * Perhaps we should never extract the directory??? + */ + continue; + } + fseek(lib.fdata, lib.dir[i].foffset, SEEK_SET); + + f = open(lib.dir[i].fname, O_WRONLY|O_TRUNC|O_BINARY|O_CREAT, 0640); + if (f < 0) { + printf("Can't create '%s'\n", lib.dir[i].fname); + xexit(EXIT_FAILURE); + } + + /* read chunks from library and write them out */ + total_read = 0; + do { + remainder = lib.dir[i].fsize - total_read; + if (remainder > (long) sizeof(buf)) + r = (int) sizeof(buf); + else + r = remainder; + + n = fread(buf, 1, r, lib.fdata); + if (n != r) { + printf("Read Error in '%s'\n", lib.dir[i].fname); + xexit(EXIT_FAILURE); + } + if (write(f, buf, n) != n) { + printf("Write Error in '%s'\n", lib.dir[i].fname); + xexit(EXIT_FAILURE); + } + + total_read += n; + } while (total_read != lib.dir[i].fsize); + + (void) close(f); + + if (verbose) printf("x %s\n", lib.dir[i].fname); + } + + close_library(&lib); + xexit(EXIT_SUCCESS); + } + + case 'c': /* create archive */ + { + libdir ld[MAX_DLB_FILES]; + char buf[BUFSIZ]; + int fd, out, nfiles = 0; + long dir_size, slen, flen, fsiz; + boolean rewrite_directory = FALSE; + + /* + * Get names from either/both an argv list and a file + * list. This does not do any duplicate checking + */ + + /* get file name in argv list */ + if (argv[ap]) { + for ( ; ap < argc; ap++, nfiles++) { + if (nfiles >= MAX_DLB_FILES) { + printf("Too many dlb files! Stopping at %d.\n", + MAX_DLB_FILES); + break; + } + ld[nfiles].fname = (char *) alloc(strlen(argv[ap]) + 1); + Strcpy(ld[nfiles].fname, argv[ap]); + } + } + + if (iseen) { + /* want to do a list file */ + FILE *list = fopen(list_file, "r"); + if (!list) { + printf("Can't open %s\n",list_file); + xexit(EXIT_FAILURE); + } + + /* get file names, one per line */ + for ( ; fgets(buf, sizeof(buf), list); nfiles++) { + if (nfiles >= MAX_DLB_FILES) { + printf("Too many dlb files! Stopping at %d.\n", + MAX_DLB_FILES); + break; + } + *(eos(buf)-1) = '\0'; /* strip newline */ + ld[nfiles].fname = (char *) alloc(strlen(buf) + 1); + Strcpy(ld[nfiles].fname, buf); + } + fclose(list); + } + + if (nfiles == 0) { + printf("No files to archive\n"); + xexit(EXIT_FAILURE); + } + + + /* + * Get file sizes and name string length. Don't include + * the directory information yet. + */ + for (i = 0, slen = 0, flen = 0; i < nfiles; i++) { + fd = open(ld[i].fname, O_RDONLY|O_BINARY, 0); + if (fd < 0) { + printf("Can't open %s\n", ld[i].fname); + xexit(EXIT_FAILURE); + } + ld[i].fsize = lseek(fd, 0, SEEK_END); + ld[i].foffset = flen; + + slen += strlen(ld[i].fname); /* don't add null (yet) */ + flen += ld[i].fsize; + close(fd); + } + + /* open output file */ + out = open(library_file, O_RDWR|O_TRUNC|O_BINARY|O_CREAT, FCMASK); + if (out < 0) { + printf("Can't open %s for output\n", library_file); + xexit(EXIT_FAILURE); + } + + /* caculate directory size */ + dir_size = 40 /* header line (see below) */ + + ((nfiles+1)*11) /* handling+file offset+SP+newline */ + + slen+strlen(DLB_DIRECTORY); /* file names */ + + /* write directory */ + write_dlb_directory(out, nfiles, ld, slen, dir_size, flen); + + flen = 0L; + /* write each file */ + for (i = 0; i < nfiles; i++) { + fd = open(ld[i].fname, O_RDONLY|O_BINARY, 0); + if (fd < 0) { + printf("Can't open input file '%s'\n", ld[i].fname); + xexit(EXIT_FAILURE); + } + if (verbose) printf("%s\n",ld[i].fname); + + fsiz = 0L; + while ((r = read(fd, buf, sizeof buf)) != 0) { + if (r == -1) { + printf("Read Error in '%s'\n", ld[i].fname); + xexit(EXIT_FAILURE); + } + if (write(out, buf, r) != r) { + printf("Write Error in '%s'\n", ld[i].fname); + xexit(EXIT_FAILURE); + } + fsiz += r; + } + (void) close(fd); + if (fsiz != ld[i].fsize) rewrite_directory = TRUE; + /* in case directory rewrite is needed */ + ld[i].fsize = fsiz; + ld[i].foffset = flen; + flen += fsiz; + } + + if (rewrite_directory) { + if (verbose) printf("(rewriting dlb directory info)\n"); + (void) lseek(out, 0, SEEK_SET); /* rewind */ + write_dlb_directory(out, nfiles, ld, slen, dir_size, flen); + } + + for (i = 0; i < nfiles; i++) + free((genericptr_t) ld[i].fname), ld[i].fname = 0; + + (void) close(out); + xexit(EXIT_SUCCESS); + } + } +#endif /* DLBLIB */ +#endif /* DLB */ + + xexit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +#ifdef DLB +#ifdef DLBLIB + +static void +write_dlb_directory(out, nfiles, ld, slen, dir_size, flen) +int out, nfiles; +libdir *ld; +long slen, dir_size, flen; +{ + char buf[BUFSIZ]; + int i; + + sprintf(buf,"%3ld %8ld %8ld %8ld %8ld\n", + (long) DLB_VERS, /* version of dlb file */ + (long) nfiles+1, /* # of entries (includes directory) */ + /* string length + room for nulls */ + (long) slen+strlen(DLB_DIRECTORY)+nfiles+1, + (long) dir_size, /* start of first file */ + (long) flen+dir_size); /* total file size */ + Write(out, buf, strlen(buf)); + + /* write each file entry */ +#define ENTRY_FORMAT "%c%s %8ld\n" + sprintf(buf, ENTRY_FORMAT, ENC_NORMAL, DLB_DIRECTORY, (long) 0); + Write(out, buf, strlen(buf)); + for (i = 0; i < nfiles; i++) { + sprintf(buf, ENTRY_FORMAT, + ENC_NORMAL, /* encoding */ + ld[i].fname, /* name */ + ld[i].foffset + dir_size); /* offset */ + Write(out, buf, strlen(buf)); + } +} + +#endif /* DLBLIB */ +#endif /* DLB */ + +static void +xexit(retcd) + int retcd; +{ +#ifdef DLB +#ifdef AMIGA + if (origdir[0]) chdir(origdir); +#endif +#endif + exit(retcd); +} + + +#ifdef AMIGA +#include "date.h" +const char amiga_version_string[] = AMIGA_VERSION_STRING; +#endif + +/*dlb_main.c*/ diff --git a/util/lev_comp.l b/util/lev_comp.l new file mode 100644 index 0000000..1e7c3ae --- /dev/null +++ b/util/lev_comp.l @@ -0,0 +1,240 @@ +%{ +/* SCCS Id: @(#)lev_lex.c 3.4 2002/03/27 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +#define LEV_LEX_C + +#include "hack.h" +#include "lev_comp.h" +#include "sp_lev.h" + +/* Most of these don't exist in flex, yywrap is macro and + * yyunput is properly declared in flex.skel. + */ +#if !defined(FLEX_SCANNER) && !defined(FLEXHACK_SCANNER) +int FDECL(yyback, (int *,int)); +int NDECL(yylook); +int NDECL(yyinput); +int NDECL(yywrap); +int NDECL(yylex); + /* Traditional lexes let yyunput() and yyoutput() default to int; + * newer ones may declare them as void since they don't return + * values. For even more fun, the lex supplied as part of the + * newer unbundled compiler for SunOS 4.x adds the void declarations + * (under __STDC__ or _cplusplus ifdefs -- otherwise they remain + * int) while the bundled lex and the one with the older unbundled + * compiler do not. To detect this, we need help from outside -- + * sys/unix/Makefile.utl. + * + * Digital UNIX is difficult and still has int in spite of all + * other signs. + */ +# if defined(NeXT) || defined(SVR4) || defined(_AIX32) +# define VOIDYYPUT +# endif +# if !defined(VOIDYYPUT) && defined(POSIX_TYPES) +# if !defined(BOS) && !defined(HISX) && !defined(_M_UNIX) && !defined(VMS) +# define VOIDYYPUT +# endif +# endif +# if !defined(VOIDYYPUT) && defined(WEIRD_LEX) +# if defined(SUNOS4) && defined(__STDC__) && (WEIRD_LEX > 1) +# define VOIDYYPUT +# endif +# endif +# if defined(VOIDYYPUT) && defined(__osf__) +# undef VOIDYYPUT +# endif +# ifdef VOIDYYPUT +void FDECL(yyunput, (int)); +void FDECL(yyoutput, (int)); +# else +int FDECL(yyunput, (int)); +int FDECL(yyoutput, (int)); +# endif +#endif /* !FLEX_SCANNER && !FLEXHACK_SCANNER */ + +#ifdef FLEX_SCANNER +#define YY_MALLOC_DECL \ + genericptr_t FDECL(malloc, (size_t)); \ + genericptr_t FDECL(realloc, (genericptr_t,size_t)); +#endif + +void FDECL(init_yyin, (FILE *)); +void FDECL(init_yyout, (FILE *)); + +/* + * This doesn't always get put in lev_comp.h + * (esp. when using older versions of bison). + */ +extern YYSTYPE yylval; + +int line_number = 1, colon_line_number = 1; +static char map[4096]; +static int map_cnt = 0; + +%} +%e 1500 +%p 5000 +%n 700 +%s MAPC +%% +ENDMAP { + BEGIN(INITIAL); + yylval.map = (char *) alloc(map_cnt + 1); + (void) strncpy(yylval.map, map, map_cnt); + yylval.map[map_cnt] = 0; + map_cnt = 0; + return MAP_ID; + } +[-|}{+ABCISHKPLWTF\\#. 0123456789]*\r?\n { + int len = yyleng; + /* convert \r\n to \n */ + if (len >= 2 && yytext[len - 2] == '\r') len -= 1; + line_number++; + (void) strncpy(map + map_cnt, yytext, len); + map_cnt += len; + map[map_cnt - 1] = '\n'; + map[map_cnt] = '\0'; + } +^#.*\n { line_number++; } +: { colon_line_number = line_number; return ':'; } +MESSAGE return MESSAGE_ID; +MAZE return MAZE_ID; +NOMAP return NOMAP_ID; +LEVEL return LEVEL_ID; +INIT_MAP return LEV_INIT_ID; +FLAGS return FLAGS_ID; +GEOMETRY return GEOMETRY_ID; +^MAP\r?\n { BEGIN(MAPC); line_number++; } +OBJECT return OBJECT_ID; +CONTAINER return COBJECT_ID; +MONSTER return MONSTER_ID; +TRAP return TRAP_ID; +DOOR return DOOR_ID; +DRAWBRIDGE return DRAWBRIDGE_ID; +MAZEWALK return MAZEWALK_ID; +WALLIFY return WALLIFY_ID; +REGION return REGION_ID; +RANDOM_OBJECTS return RANDOM_OBJECTS_ID; +RANDOM_MONSTERS return RANDOM_MONSTERS_ID; +RANDOM_PLACES return RANDOM_PLACES_ID; +ALTAR return ALTAR_ID; +LADDER return LADDER_ID; +STAIR return STAIR_ID; +PORTAL return PORTAL_ID; +TELEPORT_REGION return TELEPRT_ID; +BRANCH return BRANCH_ID; +FOUNTAIN return FOUNTAIN_ID; +SINK return SINK_ID; +POOL return POOL_ID; +NON_DIGGABLE return NON_DIGGABLE_ID; +NON_PASSWALL return NON_PASSWALL_ID; +ROOM return ROOM_ID; +SUBROOM return SUBROOM_ID; +RANDOM_CORRIDORS return RAND_CORRIDOR_ID; +CORRIDOR return CORRIDOR_ID; +GOLD return GOLD_ID; +ENGRAVING return ENGRAVING_ID; +NAME return NAME_ID; +CHANCE return CHANCE_ID; +levregion return LEV; +open { yylval.i=D_ISOPEN; return DOOR_STATE; } +closed { yylval.i=D_CLOSED; return DOOR_STATE; } +locked { yylval.i=D_LOCKED; return DOOR_STATE; } +nodoor { yylval.i=D_NODOOR; return DOOR_STATE; } +broken { yylval.i=D_BROKEN; return DOOR_STATE; } +north { yylval.i=W_NORTH; return DIRECTION; } +east { yylval.i=W_EAST; return DIRECTION; } +south { yylval.i=W_SOUTH; return DIRECTION; } +west { yylval.i=W_WEST; return DIRECTION; } +random { yylval.i = -1; return RANDOM_TYPE; } +none { yylval.i = -2; return NONE; } +object return O_REGISTER; +monster return M_REGISTER; +place return P_REGISTER; +align return A_REGISTER; +left { yylval.i=1; return LEFT_OR_RIGHT; } +half-left { yylval.i=2; return LEFT_OR_RIGHT; } +center { yylval.i=3; return CENTER; } +half-right { yylval.i=4; return LEFT_OR_RIGHT; } +right { yylval.i=5; return LEFT_OR_RIGHT; } +top { yylval.i=1; return TOP_OR_BOT; } +bottom { yylval.i=5; return TOP_OR_BOT; } +lit { yylval.i=1; return LIGHT_STATE; } +unlit { yylval.i=0; return LIGHT_STATE; } +filled { yylval.i=0; return FILLING; } +unfilled { yylval.i=1; return FILLING; } +noalign { yylval.i= AM_NONE; return ALIGNMENT; } +law { yylval.i= AM_LAWFUL; return ALIGNMENT; } +neutral { yylval.i= AM_NEUTRAL; return ALIGNMENT; } +chaos { yylval.i= AM_CHAOTIC; return ALIGNMENT; } +coaligned { yylval.i= AM_SPLEV_CO; return ALIGNMENT; } +noncoaligned { yylval.i= AM_SPLEV_NONCO; return ALIGNMENT; } +peaceful { yylval.i=1; return MON_ATTITUDE; } +hostile { yylval.i=0; return MON_ATTITUDE; } +asleep { yylval.i=1; return MON_ALERTNESS; } +awake { yylval.i=0; return MON_ALERTNESS; } +m_feature { yylval.i= M_AP_FURNITURE; return MON_APPEARANCE; } +m_monster { yylval.i= M_AP_MONSTER; return MON_APPEARANCE; } +m_object { yylval.i= M_AP_OBJECT; return MON_APPEARANCE; } +sanctum { yylval.i=2; return ALTAR_TYPE; } +shrine { yylval.i=1; return ALTAR_TYPE; } +altar { yylval.i=0; return ALTAR_TYPE; } +up { yylval.i=1; return UP_OR_DOWN; } +down { yylval.i=0; return UP_OR_DOWN; } +false { yylval.i=0; return BOOLEAN; } +true { yylval.i=1; return BOOLEAN; } +dust { yylval.i=DUST; return ENGRAVING_TYPE; } +engrave { yylval.i=ENGRAVE; return ENGRAVING_TYPE; } +burn { yylval.i=BURN; return ENGRAVING_TYPE; } +mark { yylval.i=MARK; return ENGRAVING_TYPE; } +blessed { yylval.i=1; return CURSE_TYPE; } +uncursed { yylval.i=2; return CURSE_TYPE; } +cursed { yylval.i=3; return CURSE_TYPE; } +contained { return CONTAINED; } +noteleport { yylval.i=NOTELEPORT; return FLAG_TYPE; } +hardfloor { yylval.i=HARDFLOOR; return FLAG_TYPE; } +nommap { yylval.i=NOMMAP; return FLAG_TYPE; } +arboreal { yylval.i=ARBOREAL; return FLAG_TYPE; } /* KMH */ +shortsighted { yylval.i=SHORTSIGHTED; return FLAG_TYPE; } +\[\ *[0-9]+\%\ *\] { yylval.i = atoi(yytext + 1); return PERCENT; } +[+\-]?[0-9]+ { yylval.i=atoi(yytext); return INTEGER; } +\"[^"]*\" { yytext[yyleng-1] = 0; /* Discard the trailing \" */ + yylval.map = (char *) alloc(strlen(yytext+1)+1); + Strcpy(yylval.map, yytext+1); /* Discard the first \" */ + return STRING; } +\r?\n { line_number++; } +[ \t]+ ; +'\\.' { yylval.i = yytext[2]; return CHAR; } +'.' { yylval.i = yytext[1]; return CHAR; } +. { return yytext[0]; } +%% +#ifdef AMIGA +long *alloc(n) + unsigned n; +{ + return ((long *)malloc (n)); +} +#endif + +/* routine to switch to another input file; needed for flex */ +void init_yyin( input_f ) +FILE *input_f; +{ +#if defined(FLEX_SCANNER) || defined(FLEXHACK_SCANNER) + if (yyin) + yyrestart(input_f); + else +#endif + yyin = input_f; +} +/* analogous routine (for completeness) */ +void init_yyout( output_f ) +FILE *output_f; +{ + yyout = output_f; +} + +/*lev_comp.l*/ diff --git a/util/lev_comp.y b/util/lev_comp.y new file mode 100644 index 0000000..a12c7cd --- /dev/null +++ b/util/lev_comp.y @@ -0,0 +1,1681 @@ +%{ +/* SCCS Id: @(#)lev_yacc.c 3.4 2000/01/17 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the Level Compiler code + * It may handle special mazes & special room-levels + */ + +/* In case we're using bison in AIX. This definition must be + * placed before any other C-language construct in the file + * excluding comments and preprocessor directives (thanks IBM + * for this wonderful feature...). + * + * Note: some cpps barf on this 'undefined control' (#pragma). + * Addition of the leading space seems to prevent barfage for now, + * and AIX will still see the directive. + */ +#ifdef _AIX + #pragma alloca /* keep leading space! */ +#endif + +#include "hack.h" +#include "sp_lev.h" + +#define MAX_REGISTERS 10 +#define ERR (-1) +/* many types of things are put in chars for transference to NetHack. + * since some systems will use signed chars, limit everybody to the + * same number for portability. + */ +#define MAX_OF_TYPE 128 + +#define New(type) \ + (type *) memset((genericptr_t)alloc(sizeof(type)), 0, sizeof(type)) +#define NewTab(type, size) (type **) alloc(sizeof(type *) * size) +#define Free(ptr) free((genericptr_t)ptr) + +extern void FDECL(yyerror, (const char *)); +extern void FDECL(yywarning, (const char *)); +extern int NDECL(yylex); +int NDECL(yyparse); + +extern int FDECL(get_floor_type, (CHAR_P)); +extern int FDECL(get_room_type, (char *)); +extern int FDECL(get_trap_type, (char *)); +extern int FDECL(get_monster_id, (char *,CHAR_P)); +extern int FDECL(get_object_id, (char *,CHAR_P)); +extern boolean FDECL(check_monster_char, (CHAR_P)); +extern boolean FDECL(check_object_char, (CHAR_P)); +extern char FDECL(what_map_char, (CHAR_P)); +extern void FDECL(scan_map, (char *)); +extern void NDECL(wallify_map); +extern boolean NDECL(check_subrooms); +extern void FDECL(check_coord, (int,int,const char *)); +extern void NDECL(store_part); +extern void NDECL(store_room); +extern boolean FDECL(write_level_file, (char *,splev *,specialmaze *)); +extern void FDECL(free_rooms, (splev *)); + +static struct reg { + int x1, y1; + int x2, y2; +} current_region; + +static struct coord { + int x; + int y; +} current_coord, current_align; + +static struct size { + int height; + int width; +} current_size; + +char tmpmessage[256]; +digpos *tmppass[32]; +char *tmpmap[ROWNO]; + +digpos *tmpdig[MAX_OF_TYPE]; +region *tmpreg[MAX_OF_TYPE]; +lev_region *tmplreg[MAX_OF_TYPE]; +door *tmpdoor[MAX_OF_TYPE]; +drawbridge *tmpdb[MAX_OF_TYPE]; +walk *tmpwalk[MAX_OF_TYPE]; + +room_door *tmprdoor[MAX_OF_TYPE]; +trap *tmptrap[MAX_OF_TYPE]; +monster *tmpmonst[MAX_OF_TYPE]; +object *tmpobj[MAX_OF_TYPE]; +altar *tmpaltar[MAX_OF_TYPE]; +lad *tmplad[MAX_OF_TYPE]; +stair *tmpstair[MAX_OF_TYPE]; +gold *tmpgold[MAX_OF_TYPE]; +engraving *tmpengraving[MAX_OF_TYPE]; +fountain *tmpfountain[MAX_OF_TYPE]; +sink *tmpsink[MAX_OF_TYPE]; +pool *tmppool[MAX_OF_TYPE]; + +mazepart *tmppart[10]; +room *tmproom[MAXNROFROOMS*2]; +corridor *tmpcor[MAX_OF_TYPE]; + +static specialmaze maze; +static splev special_lev; +static lev_init init_lev; + +static char olist[MAX_REGISTERS], mlist[MAX_REGISTERS]; +static struct coord plist[MAX_REGISTERS]; + +int n_olist = 0, n_mlist = 0, n_plist = 0; + +unsigned int nlreg = 0, nreg = 0, ndoor = 0, ntrap = 0, nmons = 0, nobj = 0; +unsigned int ndb = 0, nwalk = 0, npart = 0, ndig = 0, nlad = 0, nstair = 0; +unsigned int naltar = 0, ncorridor = 0, nrooms = 0, ngold = 0, nengraving = 0; +unsigned int nfountain = 0, npool = 0, nsink = 0, npass = 0; + +static int lev_flags = 0; + +unsigned int max_x_map, max_y_map; + +static xchar in_room; + +extern int fatal_error; +extern int want_warnings; +extern const char *fname; + +%} + +%union +{ + int i; + char* map; + struct { + xchar room; + xchar wall; + xchar door; + } corpos; +} + + +%token CHAR INTEGER BOOLEAN PERCENT +%token MESSAGE_ID MAZE_ID LEVEL_ID LEV_INIT_ID GEOMETRY_ID NOMAP_ID +%token OBJECT_ID COBJECT_ID MONSTER_ID TRAP_ID DOOR_ID DRAWBRIDGE_ID +%token MAZEWALK_ID WALLIFY_ID REGION_ID FILLING +%token RANDOM_OBJECTS_ID RANDOM_MONSTERS_ID RANDOM_PLACES_ID +%token ALTAR_ID LADDER_ID STAIR_ID NON_DIGGABLE_ID NON_PASSWALL_ID ROOM_ID +%token PORTAL_ID TELEPRT_ID BRANCH_ID LEV CHANCE_ID +%token CORRIDOR_ID GOLD_ID ENGRAVING_ID FOUNTAIN_ID POOL_ID SINK_ID NONE +%token RAND_CORRIDOR_ID DOOR_STATE LIGHT_STATE CURSE_TYPE ENGRAVING_TYPE +%token DIRECTION RANDOM_TYPE O_REGISTER M_REGISTER P_REGISTER A_REGISTER +%token ALIGNMENT LEFT_OR_RIGHT CENTER TOP_OR_BOT ALTAR_TYPE UP_OR_DOWN +%token SUBROOM_ID NAME_ID FLAGS_ID FLAG_TYPE MON_ATTITUDE MON_ALERTNESS +%token MON_APPEARANCE +%token CONTAINED +%token ',' ':' '(' ')' '[' ']' +%token STRING MAP_ID +%type h_justif v_justif trap_name room_type door_state light_state +%type alignment altar_type a_register roomfill filling door_pos +%type door_wall walled secret amount chance +%type engraving_type flags flag_list prefilled lev_region lev_init +%type monster monster_c m_register object object_c o_register +%type string maze_def level_def m_name o_name +%type corr_spec +%start file + +%% +file : /* nothing */ + | levels + ; + +levels : level + | level levels + ; + +level : maze_level + | room_level + ; + +maze_level : maze_def flags lev_init messages regions + { + unsigned i; + + if (fatal_error > 0) { + (void) fprintf(stderr, + "%s : %d errors detected. No output created!\n", + fname, fatal_error); + } else { + maze.flags = $2; + (void) memcpy((genericptr_t)&(maze.init_lev), + (genericptr_t)&(init_lev), + sizeof(lev_init)); + maze.numpart = npart; + maze.parts = NewTab(mazepart, npart); + for(i=0;i 0) { + (void) fprintf(stderr, + "%s : %d errors detected. No output created!\n", + fname, fatal_error); + } else { + special_lev.flags = (long) $2; + (void) memcpy( + (genericptr_t)&(special_lev.init_lev), + (genericptr_t)&(init_lev), + sizeof(lev_init)); + special_lev.nroom = nrooms; + special_lev.rooms = NewTab(room, nrooms); + for(i=0; i 8) + yyerror("Level names limited to 8 characters."); + $$ = $3; + special_lev.nrmonst = special_lev.nrobjects = 0; + n_mlist = n_olist = 0; + } + ; + +lev_init : /* nothing */ + { + /* in case we're processing multiple files, + explicitly clear any stale settings */ + (void) memset((genericptr_t) &init_lev, 0, + sizeof init_lev); + init_lev.init_present = FALSE; + $$ = 0; + } + | LEV_INIT_ID ':' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled + { + init_lev.init_present = TRUE; + init_lev.fg = what_map_char((char) $3); + if (init_lev.fg == INVALID_TYPE) + yyerror("Invalid foreground type."); + init_lev.bg = what_map_char((char) $5); + if (init_lev.bg == INVALID_TYPE) + yyerror("Invalid background type."); + init_lev.smoothed = $7; + init_lev.joined = $9; + if (init_lev.joined && + init_lev.fg != CORR && init_lev.fg != ROOM) + yyerror("Invalid foreground type for joined map."); + init_lev.lit = $11; + init_lev.walled = $13; + $$ = 1; + } + ; + +walled : BOOLEAN + | RANDOM_TYPE + ; + +flags : /* nothing */ + { + $$ = 0; + } + | FLAGS_ID ':' flag_list + { + $$ = lev_flags; + lev_flags = 0; /* clear for next user */ + } + ; + +flag_list : FLAG_TYPE ',' flag_list + { + lev_flags |= $1; + } + | FLAG_TYPE + { + lev_flags |= $1; + } + ; + +messages : /* nothing */ + | message messages + ; + +message : MESSAGE_ID ':' STRING + { + int i, j; + + i = (int) strlen($3) + 1; + j = (int) strlen(tmpmessage); + if (i + j > 255) { + yyerror("Message string too long (>256 characters)"); + } else { + if (j) tmpmessage[j++] = '\n'; + (void) strncpy(tmpmessage+j, $3, i - 1); + tmpmessage[j + i - 1] = 0; + } + Free($3); + } + ; + +rreg_init : /* nothing */ + | rreg_init init_rreg + ; + +init_rreg : RANDOM_OBJECTS_ID ':' object_list + { + if(special_lev.nrobjects) { + yyerror("Object registers already initialized!"); + } else { + special_lev.nrobjects = n_olist; + special_lev.robjects = (char *) alloc(n_olist); + (void) memcpy((genericptr_t)special_lev.robjects, + (genericptr_t)olist, n_olist); + } + } + | RANDOM_MONSTERS_ID ':' monster_list + { + if(special_lev.nrmonst) { + yyerror("Monster registers already initialized!"); + } else { + special_lev.nrmonst = n_mlist; + special_lev.rmonst = (char *) alloc(n_mlist); + (void) memcpy((genericptr_t)special_lev.rmonst, + (genericptr_t)mlist, n_mlist); + } + } + ; + +rooms : /* Nothing - dummy room for use with INIT_MAP */ + { + tmproom[nrooms] = New(room); + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->parent = (char *) 0; + tmproom[nrooms]->rtype = 0; + tmproom[nrooms]->rlit = 0; + tmproom[nrooms]->xalign = ERR; + tmproom[nrooms]->yalign = ERR; + tmproom[nrooms]->x = 0; + tmproom[nrooms]->y = 0; + tmproom[nrooms]->w = 2; + tmproom[nrooms]->h = 2; + in_room = 1; + } + | roomlist + ; + +roomlist : aroom + | aroom roomlist + ; + +corridors_def : random_corridors + | corridors + ; + +random_corridors: RAND_CORRIDOR_ID + { + tmpcor[0] = New(corridor); + tmpcor[0]->src.room = -1; + ncorridor = 1; + } + ; + +corridors : /* nothing */ + | corridors corridor + ; + +corridor : CORRIDOR_ID ':' corr_spec ',' corr_spec + { + tmpcor[ncorridor] = New(corridor); + tmpcor[ncorridor]->src.room = $3.room; + tmpcor[ncorridor]->src.wall = $3.wall; + tmpcor[ncorridor]->src.door = $3.door; + tmpcor[ncorridor]->dest.room = $5.room; + tmpcor[ncorridor]->dest.wall = $5.wall; + tmpcor[ncorridor]->dest.door = $5.door; + ncorridor++; + if (ncorridor >= MAX_OF_TYPE) { + yyerror("Too many corridors in level!"); + ncorridor--; + } + } + | CORRIDOR_ID ':' corr_spec ',' INTEGER + { + tmpcor[ncorridor] = New(corridor); + tmpcor[ncorridor]->src.room = $3.room; + tmpcor[ncorridor]->src.wall = $3.wall; + tmpcor[ncorridor]->src.door = $3.door; + tmpcor[ncorridor]->dest.room = -1; + tmpcor[ncorridor]->dest.wall = $5; + ncorridor++; + if (ncorridor >= MAX_OF_TYPE) { + yyerror("Too many corridors in level!"); + ncorridor--; + } + } + ; + +corr_spec : '(' INTEGER ',' DIRECTION ',' door_pos ')' + { + if ((unsigned) $2 >= nrooms) + yyerror("Wrong room number!"); + $$.room = $2; + $$.wall = $4; + $$.door = $6; + } + ; + +aroom : room_def room_details + { + store_room(); + } + | subroom_def room_details + { + store_room(); + } + ; + +subroom_def : SUBROOM_ID ':' room_type ',' light_state ',' subroom_pos ',' room_size ',' string roomfill + { + tmproom[nrooms] = New(room); + tmproom[nrooms]->parent = $11; + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->rtype = $3; + tmproom[nrooms]->rlit = $5; + tmproom[nrooms]->filled = $12; + tmproom[nrooms]->xalign = ERR; + tmproom[nrooms]->yalign = ERR; + tmproom[nrooms]->x = current_coord.x; + tmproom[nrooms]->y = current_coord.y; + tmproom[nrooms]->w = current_size.width; + tmproom[nrooms]->h = current_size.height; + in_room = 1; + } + ; + +room_def : ROOM_ID ':' room_type ',' light_state ',' room_pos ',' room_align ',' room_size roomfill + { + tmproom[nrooms] = New(room); + tmproom[nrooms]->name = (char *) 0; + tmproom[nrooms]->parent = (char *) 0; + tmproom[nrooms]->rtype = $3; + tmproom[nrooms]->rlit = $5; + tmproom[nrooms]->filled = $12; + tmproom[nrooms]->xalign = current_align.x; + tmproom[nrooms]->yalign = current_align.y; + tmproom[nrooms]->x = current_coord.x; + tmproom[nrooms]->y = current_coord.y; + tmproom[nrooms]->w = current_size.width; + tmproom[nrooms]->h = current_size.height; + in_room = 1; + } + ; + +roomfill : /* nothing */ + { + $$ = 1; + } + | ',' BOOLEAN + { + $$ = $2; + } + ; + +room_pos : '(' INTEGER ',' INTEGER ')' + { + if ( $2 < 1 || $2 > 5 || + $4 < 1 || $4 > 5 ) { + yyerror("Room position should be between 1 & 5!"); + } else { + current_coord.x = $2; + current_coord.y = $4; + } + } + | RANDOM_TYPE + { + current_coord.x = current_coord.y = ERR; + } + ; + +subroom_pos : '(' INTEGER ',' INTEGER ')' + { + if ( $2 < 0 || $4 < 0) { + yyerror("Invalid subroom position !"); + } else { + current_coord.x = $2; + current_coord.y = $4; + } + } + | RANDOM_TYPE + { + current_coord.x = current_coord.y = ERR; + } + ; + +room_align : '(' h_justif ',' v_justif ')' + { + current_align.x = $2; + current_align.y = $4; + } + | RANDOM_TYPE + { + current_align.x = current_align.y = ERR; + } + ; + +room_size : '(' INTEGER ',' INTEGER ')' + { + current_size.width = $2; + current_size.height = $4; + } + | RANDOM_TYPE + { + current_size.height = current_size.width = ERR; + } + ; + +room_details : /* nothing */ + | room_details room_detail + ; + +room_detail : room_name + | room_chance + | room_door + | monster_detail + | object_detail + | trap_detail + | altar_detail + | fountain_detail + | sink_detail + | pool_detail + | gold_detail + | engraving_detail + | stair_detail + ; + +room_name : NAME_ID ':' string + { + if (tmproom[nrooms]->name) + yyerror("This room already has a name!"); + else + tmproom[nrooms]->name = $3; + } + ; + +room_chance : CHANCE_ID ':' INTEGER + { + if (tmproom[nrooms]->chance) + yyerror("This room already assigned a chance!"); + else if (tmproom[nrooms]->rtype == OROOM) + yyerror("Only typed rooms can have a chance!"); + else if ($3 < 1 || $3 > 99) + yyerror("The chance is supposed to be percentile."); + else + tmproom[nrooms]->chance = $3; + } + ; + +room_door : DOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos + { + /* ERR means random here */ + if ($7 == ERR && $9 != ERR) { + yyerror("If the door wall is random, so must be its pos!"); + } else { + tmprdoor[ndoor] = New(room_door); + tmprdoor[ndoor]->secret = $3; + tmprdoor[ndoor]->mask = $5; + tmprdoor[ndoor]->wall = $7; + tmprdoor[ndoor]->pos = $9; + ndoor++; + if (ndoor >= MAX_OF_TYPE) { + yyerror("Too many doors in room!"); + ndoor--; + } + } + } + ; + +secret : BOOLEAN + | RANDOM_TYPE + ; + +door_wall : DIRECTION + | RANDOM_TYPE + ; + +door_pos : INTEGER + | RANDOM_TYPE + ; + +maze_def : MAZE_ID ':' string ',' filling + { + maze.filling = (schar) $5; + if (index($3, '.')) + yyerror("Invalid dot ('.') in level name."); + if ((int) strlen($3) > 8) + yyerror("Level names limited to 8 characters."); + $$ = $3; + in_room = 0; + n_plist = n_mlist = n_olist = 0; + } + ; + +filling : CHAR + { + $$ = get_floor_type((char)$1); + } + | RANDOM_TYPE + { + $$ = -1; + } + ; + +regions : aregion + | aregion regions + ; + +aregion : map_definition reg_init map_details + { + store_part(); + } + ; + +map_definition : NOMAP_ID + { + tmppart[npart] = New(mazepart); + tmppart[npart]->halign = 1; + tmppart[npart]->valign = 1; + tmppart[npart]->nrobjects = 0; + tmppart[npart]->nloc = 0; + tmppart[npart]->nrmonst = 0; + tmppart[npart]->xsize = 1; + tmppart[npart]->ysize = 1; + tmppart[npart]->map = (char **) alloc(sizeof(char *)); + tmppart[npart]->map[0] = (char *) alloc(1); + tmppart[npart]->map[0][0] = STONE; + max_x_map = COLNO-1; + max_y_map = ROWNO; + } + | map_geometry MAP_ID + { + tmppart[npart] = New(mazepart); + tmppart[npart]->halign = $1 % 10; + tmppart[npart]->valign = $1 / 10; + tmppart[npart]->nrobjects = 0; + tmppart[npart]->nloc = 0; + tmppart[npart]->nrmonst = 0; + scan_map($2); + Free($2); + } + ; + +map_geometry : GEOMETRY_ID ':' h_justif ',' v_justif + { + $$ = $3 + ($5 * 10); + } + ; + +h_justif : LEFT_OR_RIGHT + | CENTER + ; + +v_justif : TOP_OR_BOT + | CENTER + ; + +reg_init : /* nothing */ + | reg_init init_reg + ; + +init_reg : RANDOM_OBJECTS_ID ':' object_list + { + if (tmppart[npart]->nrobjects) { + yyerror("Object registers already initialized!"); + } else { + tmppart[npart]->robjects = (char *)alloc(n_olist); + (void) memcpy((genericptr_t)tmppart[npart]->robjects, + (genericptr_t)olist, n_olist); + tmppart[npart]->nrobjects = n_olist; + } + } + | RANDOM_PLACES_ID ':' place_list + { + if (tmppart[npart]->nloc) { + yyerror("Location registers already initialized!"); + } else { + register int i; + tmppart[npart]->rloc_x = (char *) alloc(n_plist); + tmppart[npart]->rloc_y = (char *) alloc(n_plist); + for(i=0;irloc_x[i] = plist[i].x; + tmppart[npart]->rloc_y[i] = plist[i].y; + } + tmppart[npart]->nloc = n_plist; + } + } + | RANDOM_MONSTERS_ID ':' monster_list + { + if (tmppart[npart]->nrmonst) { + yyerror("Monster registers already initialized!"); + } else { + tmppart[npart]->rmonst = (char *) alloc(n_mlist); + (void) memcpy((genericptr_t)tmppart[npart]->rmonst, + (genericptr_t)mlist, n_mlist); + tmppart[npart]->nrmonst = n_mlist; + } + } + ; + +object_list : object + { + if (n_olist < MAX_REGISTERS) + olist[n_olist++] = $1; + else + yyerror("Object list too long!"); + } + | object ',' object_list + { + if (n_olist < MAX_REGISTERS) + olist[n_olist++] = $1; + else + yyerror("Object list too long!"); + } + ; + +monster_list : monster + { + if (n_mlist < MAX_REGISTERS) + mlist[n_mlist++] = $1; + else + yyerror("Monster list too long!"); + } + | monster ',' monster_list + { + if (n_mlist < MAX_REGISTERS) + mlist[n_mlist++] = $1; + else + yyerror("Monster list too long!"); + } + ; + +place_list : place + { + if (n_plist < MAX_REGISTERS) + plist[n_plist++] = current_coord; + else + yyerror("Location list too long!"); + } + | place + { + if (n_plist < MAX_REGISTERS) + plist[n_plist++] = current_coord; + else + yyerror("Location list too long!"); + } + ',' place_list + ; + +map_details : /* nothing */ + | map_details map_detail + ; + +map_detail : monster_detail + | object_detail + | door_detail + | trap_detail + | drawbridge_detail + | region_detail + | stair_region + | portal_region + | teleprt_region + | branch_region + | altar_detail + | fountain_detail + | mazewalk_detail + | wallify_detail + | ladder_detail + | stair_detail + | gold_detail + | engraving_detail + | diggable_detail + | passwall_detail + ; + +monster_detail : MONSTER_ID chance ':' monster_c ',' m_name ',' coordinate + { + tmpmonst[nmons] = New(monster); + tmpmonst[nmons]->x = current_coord.x; + tmpmonst[nmons]->y = current_coord.y; + tmpmonst[nmons]->class = $4; + tmpmonst[nmons]->peaceful = -1; /* no override */ + tmpmonst[nmons]->asleep = -1; + tmpmonst[nmons]->align = - MAX_REGISTERS - 2; + tmpmonst[nmons]->name.str = 0; + tmpmonst[nmons]->appear = 0; + tmpmonst[nmons]->appear_as.str = 0; + tmpmonst[nmons]->chance = $2; + tmpmonst[nmons]->id = NON_PM; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Monster"); + if ($6) { + int token = get_monster_id($6, (char) $4); + if (token == ERR) + yywarning( + "Invalid monster name! Making random monster."); + else + tmpmonst[nmons]->id = token; + Free($6); + } + } + monster_infos + { + if (++nmons >= MAX_OF_TYPE) { + yyerror("Too many monsters in room or mazepart!"); + nmons--; + } + } + ; + +monster_infos : /* nothing */ + | monster_infos monster_info + ; + +monster_info : ',' string + { + tmpmonst[nmons]->name.str = $2; + } + | ',' MON_ATTITUDE + { + tmpmonst[nmons]->peaceful = $2; + } + | ',' MON_ALERTNESS + { + tmpmonst[nmons]->asleep = $2; + } + | ',' alignment + { + tmpmonst[nmons]->align = $2; + } + | ',' MON_APPEARANCE string + { + tmpmonst[nmons]->appear = $2; + tmpmonst[nmons]->appear_as.str = $3; + } + ; + +object_detail : OBJECT_ID object_desc + { + } + | COBJECT_ID object_desc + { + /* 1: is contents of preceeding object with 2 */ + /* 2: is a container */ + /* 0: neither */ + tmpobj[nobj-1]->containment = 2; + } + ; + +object_desc : chance ':' object_c ',' o_name + { + tmpobj[nobj] = New(object); + tmpobj[nobj]->class = $3; + tmpobj[nobj]->corpsenm = NON_PM; + tmpobj[nobj]->curse_state = -1; + tmpobj[nobj]->name.str = 0; + tmpobj[nobj]->chance = $1; + tmpobj[nobj]->id = -1; + if ($5) { + int token = get_object_id($5, $3); + if (token == ERR) + yywarning( + "Illegal object name! Making random object."); + else + tmpobj[nobj]->id = token; + Free($5); + } + } + ',' object_where object_infos + { + if (++nobj >= MAX_OF_TYPE) { + yyerror("Too many objects in room or mazepart!"); + nobj--; + } + } + ; + +object_where : coordinate + { + tmpobj[nobj]->containment = 0; + tmpobj[nobj]->x = current_coord.x; + tmpobj[nobj]->y = current_coord.y; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Object"); + } + | CONTAINED + { + tmpobj[nobj]->containment = 1; + /* random coordinate, will be overridden anyway */ + tmpobj[nobj]->x = -MAX_REGISTERS-1; + tmpobj[nobj]->y = -MAX_REGISTERS-1; + } + ; + +object_infos : /* nothing */ + { + tmpobj[nobj]->spe = -127; + /* Note below: we're trying to make as many of these optional as + * possible. We clearly can't make curse_state, enchantment, and + * monster_id _all_ optional, since ",random" would be ambiguous. + * We can't even just make enchantment mandatory, since if we do that + * alone, ",random" requires too much lookahead to parse. + */ + } + | ',' curse_state ',' monster_id ',' enchantment optional_name + { + } + | ',' curse_state ',' enchantment optional_name + { + } + | ',' monster_id ',' enchantment optional_name + { + } + ; + +curse_state : RANDOM_TYPE + { + tmpobj[nobj]->curse_state = -1; + } + | CURSE_TYPE + { + tmpobj[nobj]->curse_state = $1; + } + ; + +monster_id : STRING + { + int token = get_monster_id($1, (char)0); + if (token == ERR) /* "random" */ + tmpobj[nobj]->corpsenm = NON_PM - 1; + else + tmpobj[nobj]->corpsenm = token; + Free($1); + } + ; + +enchantment : RANDOM_TYPE + { + tmpobj[nobj]->spe = -127; + } + | INTEGER + { + tmpobj[nobj]->spe = $1; + } + ; + +optional_name : /* nothing */ + | ',' NONE + { + } + | ',' STRING + { + tmpobj[nobj]->name.str = $2; + } + ; + +door_detail : DOOR_ID ':' door_state ',' coordinate + { + tmpdoor[ndoor] = New(door); + tmpdoor[ndoor]->x = current_coord.x; + tmpdoor[ndoor]->y = current_coord.y; + tmpdoor[ndoor]->mask = $3; + if(current_coord.x >= 0 && current_coord.y >= 0 && + tmpmap[current_coord.y][current_coord.x] != DOOR && + tmpmap[current_coord.y][current_coord.x] != SDOOR) + yyerror("Door decl doesn't match the map"); + ndoor++; + if (ndoor >= MAX_OF_TYPE) { + yyerror("Too many doors in mazepart!"); + ndoor--; + } + } + ; + +trap_detail : TRAP_ID chance ':' trap_name ',' coordinate + { + tmptrap[ntrap] = New(trap); + tmptrap[ntrap]->x = current_coord.x; + tmptrap[ntrap]->y = current_coord.y; + tmptrap[ntrap]->type = $4; + tmptrap[ntrap]->chance = $2; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Trap"); + if (++ntrap >= MAX_OF_TYPE) { + yyerror("Too many traps in room or mazepart!"); + ntrap--; + } + } + ; + +drawbridge_detail: DRAWBRIDGE_ID ':' coordinate ',' DIRECTION ',' door_state + { + int x, y, dir; + + tmpdb[ndb] = New(drawbridge); + x = tmpdb[ndb]->x = current_coord.x; + y = tmpdb[ndb]->y = current_coord.y; + /* convert dir from a DIRECTION to a DB_DIR */ + dir = $5; + switch(dir) { + case W_NORTH: dir = DB_NORTH; y--; break; + case W_SOUTH: dir = DB_SOUTH; y++; break; + case W_EAST: dir = DB_EAST; x++; break; + case W_WEST: dir = DB_WEST; x--; break; + default: + yyerror("Invalid drawbridge direction"); + break; + } + tmpdb[ndb]->dir = dir; + if (current_coord.x >= 0 && current_coord.y >= 0 && + !IS_WALL(tmpmap[y][x])) { + char ebuf[60]; + Sprintf(ebuf, + "Wall needed for drawbridge (%02d, %02d)", + current_coord.x, current_coord.y); + yyerror(ebuf); + } + + if ( $7 == D_ISOPEN ) + tmpdb[ndb]->db_open = 1; + else if ( $7 == D_CLOSED ) + tmpdb[ndb]->db_open = 0; + else + yyerror("A drawbridge can only be open or closed!"); + ndb++; + if (ndb >= MAX_OF_TYPE) { + yyerror("Too many drawbridges in mazepart!"); + ndb--; + } + } + ; + +mazewalk_detail : MAZEWALK_ID ':' coordinate ',' DIRECTION + { + tmpwalk[nwalk] = New(walk); + tmpwalk[nwalk]->x = current_coord.x; + tmpwalk[nwalk]->y = current_coord.y; + tmpwalk[nwalk]->dir = $5; + nwalk++; + if (nwalk >= MAX_OF_TYPE) { + yyerror("Too many mazewalks in mazepart!"); + nwalk--; + } + } + ; + +wallify_detail : WALLIFY_ID + { + wallify_map(); + } + ; + +ladder_detail : LADDER_ID ':' coordinate ',' UP_OR_DOWN + { + tmplad[nlad] = New(lad); + tmplad[nlad]->x = current_coord.x; + tmplad[nlad]->y = current_coord.y; + tmplad[nlad]->up = $5; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Ladder"); + nlad++; + if (nlad >= MAX_OF_TYPE) { + yyerror("Too many ladders in mazepart!"); + nlad--; + } + } + ; + +stair_detail : STAIR_ID ':' coordinate ',' UP_OR_DOWN + { + tmpstair[nstair] = New(stair); + tmpstair[nstair]->x = current_coord.x; + tmpstair[nstair]->y = current_coord.y; + tmpstair[nstair]->up = $5; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Stairway"); + nstair++; + if (nstair >= MAX_OF_TYPE) { + yyerror("Too many stairs in room or mazepart!"); + nstair--; + } + } + ; + +stair_region : STAIR_ID ':' lev_region + { + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = $3; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } + ',' lev_region ',' UP_OR_DOWN + { + tmplreg[nlreg]->del_islev = $6; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + if($8) + tmplreg[nlreg]->rtype = LR_UPSTAIR; + else + tmplreg[nlreg]->rtype = LR_DOWNSTAIR; + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } + ; + +portal_region : PORTAL_ID ':' lev_region + { + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = $3; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } + ',' lev_region ',' string + { + tmplreg[nlreg]->del_islev = $6; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + tmplreg[nlreg]->rtype = LR_PORTAL; + tmplreg[nlreg]->rname.str = $8; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } + ; + +teleprt_region : TELEPRT_ID ':' lev_region + { + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = $3; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } + ',' lev_region + { + tmplreg[nlreg]->del_islev = $6; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + } + teleprt_detail + { + switch($8) { + case -1: tmplreg[nlreg]->rtype = LR_TELE; break; + case 0: tmplreg[nlreg]->rtype = LR_DOWNTELE; break; + case 1: tmplreg[nlreg]->rtype = LR_UPTELE; break; + } + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } + ; + +branch_region : BRANCH_ID ':' lev_region + { + tmplreg[nlreg] = New(lev_region); + tmplreg[nlreg]->in_islev = $3; + tmplreg[nlreg]->inarea.x1 = current_region.x1; + tmplreg[nlreg]->inarea.y1 = current_region.y1; + tmplreg[nlreg]->inarea.x2 = current_region.x2; + tmplreg[nlreg]->inarea.y2 = current_region.y2; + } + ',' lev_region + { + tmplreg[nlreg]->del_islev = $6; + tmplreg[nlreg]->delarea.x1 = current_region.x1; + tmplreg[nlreg]->delarea.y1 = current_region.y1; + tmplreg[nlreg]->delarea.x2 = current_region.x2; + tmplreg[nlreg]->delarea.y2 = current_region.y2; + tmplreg[nlreg]->rtype = LR_BRANCH; + tmplreg[nlreg]->rname.str = 0; + nlreg++; + if (nlreg >= MAX_OF_TYPE) { + yyerror("Too many levregions in mazepart!"); + nlreg--; + } + } + ; + +teleprt_detail : /* empty */ + { + $$ = -1; + } + | ',' UP_OR_DOWN + { + $$ = $2; + } + ; + +lev_region : region + { + $$ = 0; + } + | LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + { +/* This series of if statements is a hack for MSC 5.1. It seems that its + tiny little brain cannot compile if these are all one big if statement. */ + if ($3 <= 0 || $3 >= COLNO) + yyerror("Region out of level range!"); + else if ($5 < 0 || $5 >= ROWNO) + yyerror("Region out of level range!"); + else if ($7 <= 0 || $7 >= COLNO) + yyerror("Region out of level range!"); + else if ($9 < 0 || $9 >= ROWNO) + yyerror("Region out of level range!"); + current_region.x1 = $3; + current_region.y1 = $5; + current_region.x2 = $7; + current_region.y2 = $9; + $$ = 1; + } + ; + +fountain_detail : FOUNTAIN_ID ':' coordinate + { + tmpfountain[nfountain] = New(fountain); + tmpfountain[nfountain]->x = current_coord.x; + tmpfountain[nfountain]->y = current_coord.y; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Fountain"); + nfountain++; + if (nfountain >= MAX_OF_TYPE) { + yyerror("Too many fountains in room or mazepart!"); + nfountain--; + } + } + ; + +sink_detail : SINK_ID ':' coordinate + { + tmpsink[nsink] = New(sink); + tmpsink[nsink]->x = current_coord.x; + tmpsink[nsink]->y = current_coord.y; + nsink++; + if (nsink >= MAX_OF_TYPE) { + yyerror("Too many sinks in room!"); + nsink--; + } + } + ; + +pool_detail : POOL_ID ':' coordinate + { + tmppool[npool] = New(pool); + tmppool[npool]->x = current_coord.x; + tmppool[npool]->y = current_coord.y; + npool++; + if (npool >= MAX_OF_TYPE) { + yyerror("Too many pools in room!"); + npool--; + } + } + ; + +diggable_detail : NON_DIGGABLE_ID ':' region + { + tmpdig[ndig] = New(digpos); + tmpdig[ndig]->x1 = current_region.x1; + tmpdig[ndig]->y1 = current_region.y1; + tmpdig[ndig]->x2 = current_region.x2; + tmpdig[ndig]->y2 = current_region.y2; + ndig++; + if (ndig >= MAX_OF_TYPE) { + yyerror("Too many diggables in mazepart!"); + ndig--; + } + } + ; + +passwall_detail : NON_PASSWALL_ID ':' region + { + tmppass[npass] = New(digpos); + tmppass[npass]->x1 = current_region.x1; + tmppass[npass]->y1 = current_region.y1; + tmppass[npass]->x2 = current_region.x2; + tmppass[npass]->y2 = current_region.y2; + npass++; + if (npass >= 32) { + yyerror("Too many passwalls in mazepart!"); + npass--; + } + } + ; + +region_detail : REGION_ID ':' region ',' light_state ',' room_type prefilled + { + tmpreg[nreg] = New(region); + tmpreg[nreg]->x1 = current_region.x1; + tmpreg[nreg]->y1 = current_region.y1; + tmpreg[nreg]->x2 = current_region.x2; + tmpreg[nreg]->y2 = current_region.y2; + tmpreg[nreg]->rlit = $5; + tmpreg[nreg]->rtype = $7; + if($8 & 1) tmpreg[nreg]->rtype += MAXRTYPE+1; + tmpreg[nreg]->rirreg = (($8 & 2) != 0); + if(current_region.x1 > current_region.x2 || + current_region.y1 > current_region.y2) + yyerror("Region start > end!"); + if(tmpreg[nreg]->rtype == VAULT && + (tmpreg[nreg]->rirreg || + (tmpreg[nreg]->x2 - tmpreg[nreg]->x1 != 1) || + (tmpreg[nreg]->y2 - tmpreg[nreg]->y1 != 1))) + yyerror("Vaults must be exactly 2x2!"); + if(want_warnings && !tmpreg[nreg]->rirreg && + current_region.x1 > 0 && current_region.y1 > 0 && + current_region.x2 < (int)max_x_map && + current_region.y2 < (int)max_y_map) { + /* check for walls in the room */ + char ebuf[60]; + register int x, y, nrock = 0; + + for(y=current_region.y1; y<=current_region.y2; y++) + for(x=current_region.x1; + x<=current_region.x2; x++) + if(IS_ROCK(tmpmap[y][x]) || + IS_DOOR(tmpmap[y][x])) nrock++; + if(nrock) { + Sprintf(ebuf, + "Rock in room (%02d,%02d,%02d,%02d)?!", + current_region.x1, current_region.y1, + current_region.x2, current_region.y2); + yywarning(ebuf); + } + if ( + !IS_ROCK(tmpmap[current_region.y1-1][current_region.x1-1]) || + !IS_ROCK(tmpmap[current_region.y2+1][current_region.x1-1]) || + !IS_ROCK(tmpmap[current_region.y1-1][current_region.x2+1]) || + !IS_ROCK(tmpmap[current_region.y2+1][current_region.x2+1])) { + Sprintf(ebuf, + "NonRock edge in room (%02d,%02d,%02d,%02d)?!", + current_region.x1, current_region.y1, + current_region.x2, current_region.y2); + yywarning(ebuf); + } + } else if(tmpreg[nreg]->rirreg && + !IS_ROOM(tmpmap[current_region.y1][current_region.x1])) { + char ebuf[60]; + Sprintf(ebuf, + "Rock in irregular room (%02d,%02d)?!", + current_region.x1, current_region.y1); + yyerror(ebuf); + } + nreg++; + if (nreg >= MAX_OF_TYPE) { + yyerror("Too many regions in mazepart!"); + nreg--; + } + } + ; + +altar_detail : ALTAR_ID ':' coordinate ',' alignment ',' altar_type + { + tmpaltar[naltar] = New(altar); + tmpaltar[naltar]->x = current_coord.x; + tmpaltar[naltar]->y = current_coord.y; + tmpaltar[naltar]->align = $5; + tmpaltar[naltar]->shrine = $7; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Altar"); + naltar++; + if (naltar >= MAX_OF_TYPE) { + yyerror("Too many altars in room or mazepart!"); + naltar--; + } + } + ; + +gold_detail : GOLD_ID ':' amount ',' coordinate + { + tmpgold[ngold] = New(gold); + tmpgold[ngold]->x = current_coord.x; + tmpgold[ngold]->y = current_coord.y; + tmpgold[ngold]->amount = $3; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Gold"); + ngold++; + if (ngold >= MAX_OF_TYPE) { + yyerror("Too many golds in room or mazepart!"); + ngold--; + } + } + ; + +engraving_detail: ENGRAVING_ID ':' coordinate ',' engraving_type ',' string + { + tmpengraving[nengraving] = New(engraving); + tmpengraving[nengraving]->x = current_coord.x; + tmpengraving[nengraving]->y = current_coord.y; + tmpengraving[nengraving]->engr.str = $7; + tmpengraving[nengraving]->etype = $5; + if (!in_room) + check_coord(current_coord.x, current_coord.y, + "Engraving"); + nengraving++; + if (nengraving >= MAX_OF_TYPE) { + yyerror("Too many engravings in room or mazepart!"); + nengraving--; + } + } + ; + +monster_c : monster + | RANDOM_TYPE + { + $$ = - MAX_REGISTERS - 1; + } + | m_register + ; + +object_c : object + | RANDOM_TYPE + { + $$ = - MAX_REGISTERS - 1; + } + | o_register + ; + +m_name : string + | RANDOM_TYPE + { + $$ = (char *) 0; + } + ; + +o_name : string + | RANDOM_TYPE + { + $$ = (char *) 0; + } + ; + +trap_name : string + { + int token = get_trap_type($1); + if (token == ERR) + yyerror("Unknown trap type!"); + $$ = token; + Free($1); + } + | RANDOM_TYPE + ; + +room_type : string + { + int token = get_room_type($1); + if (token == ERR) { + yywarning("Unknown room type! Making ordinary room..."); + $$ = OROOM; + } else + $$ = token; + Free($1); + } + | RANDOM_TYPE + ; + +prefilled : /* empty */ + { + $$ = 0; + } + | ',' FILLING + { + $$ = $2; + } + | ',' FILLING ',' BOOLEAN + { + $$ = $2 + ($4 << 1); + } + ; + +coordinate : coord + | p_register + | RANDOM_TYPE + { + current_coord.x = current_coord.y = -MAX_REGISTERS-1; + } + ; + +door_state : DOOR_STATE + | RANDOM_TYPE + ; + +light_state : LIGHT_STATE + | RANDOM_TYPE + ; + +alignment : ALIGNMENT + | a_register + | RANDOM_TYPE + { + $$ = - MAX_REGISTERS - 1; + } + ; + +altar_type : ALTAR_TYPE + | RANDOM_TYPE + ; + +p_register : P_REGISTER '[' INTEGER ']' + { + if ( $3 >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + current_coord.x = current_coord.y = - $3 - 1; + } + ; + +o_register : O_REGISTER '[' INTEGER ']' + { + if ( $3 >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + $$ = - $3 - 1; + } + ; + +m_register : M_REGISTER '[' INTEGER ']' + { + if ( $3 >= MAX_REGISTERS ) + yyerror("Register Index overflow!"); + else + $$ = - $3 - 1; + } + ; + +a_register : A_REGISTER '[' INTEGER ']' + { + if ( $3 >= 3 ) + yyerror("Register Index overflow!"); + else + $$ = - $3 - 1; + } + ; + +place : coord + ; + +monster : CHAR + { + if (check_monster_char((char) $1)) + $$ = $1 ; + else { + yyerror("Unknown monster class!"); + $$ = ERR; + } + } + ; + +object : CHAR + { + char c = $1; + if (check_object_char(c)) + $$ = c; + else { + yyerror("Unknown char class!"); + $$ = ERR; + } + } + ; + +string : STRING + ; + +amount : INTEGER + | RANDOM_TYPE + ; + +chance : /* empty */ + { + $$ = 100; /* default is 100% */ + } + | PERCENT + { + if ($1 <= 0 || $1 > 100) + yyerror("Expected percentile chance."); + $$ = $1; + } + ; + +engraving_type : ENGRAVING_TYPE + | RANDOM_TYPE + ; + +coord : '(' INTEGER ',' INTEGER ')' + { + if (!in_room && !init_lev.init_present && + ($2 < 0 || $2 > (int)max_x_map || + $4 < 0 || $4 > (int)max_y_map)) + yyerror("Coordinates out of map range!"); + current_coord.x = $2; + current_coord.y = $4; + } + ; + +region : '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')' + { +/* This series of if statements is a hack for MSC 5.1. It seems that its + tiny little brain cannot compile if these are all one big if statement. */ + if ($2 < 0 || $2 > (int)max_x_map) + yyerror("Region out of map range!"); + else if ($4 < 0 || $4 > (int)max_y_map) + yyerror("Region out of map range!"); + else if ($6 < 0 || $6 > (int)max_x_map) + yyerror("Region out of map range!"); + else if ($8 < 0 || $8 > (int)max_y_map) + yyerror("Region out of map range!"); + current_region.x1 = $2; + current_region.y1 = $4; + current_region.x2 = $6; + current_region.y2 = $8; + } + ; + +%% + +/*lev_comp.y*/ diff --git a/util/lev_main.c b/util/lev_main.c new file mode 100644 index 0000000..bff4668 --- /dev/null +++ b/util/lev_main.c @@ -0,0 +1,1580 @@ +/* SCCS Id: @(#)lev_main.c 3.4 2002/03/27 */ +/* Copyright (c) 1989 by Jean-Christophe Collet */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains the main function for the parser + * and some useful functions needed by yacc + */ +#define SPEC_LEV /* for MPW */ +/* although, why don't we move those special defines here.. and in dgn_main? */ + +#include "hack.h" +#include "date.h" +#include "sp_lev.h" +#ifdef STRICT_REF_DEF +#include "tcap.h" +#endif + +#ifdef MAC +# if defined(__SC__) || defined(__MRC__) +# define MPWTOOL +# define PREFIX ":dungeon:" /* place output files here */ +# include +# else +# if !defined(__MACH__) +# define PREFIX ":lib:" /* place output files here */ +# endif +# endif +#endif + +#ifdef WIN_CE +#define PREFIX "\\nethack\\dat\\" +#endif + +#ifndef MPWTOOL +# define SpinCursor(x) +#endif + +#if defined(AMIGA) && defined(DLB) +# define PREFIX "NH:slib/" +#endif + +#ifndef O_WRONLY +#include +#endif +#ifndef O_CREAT /* some older BSD systems do not define O_CREAT in */ +#include +#endif +#ifndef O_BINARY /* used for micros, no-op for others */ +# define O_BINARY 0 +#endif + +#if defined(MICRO) || defined(WIN32) +# define OMASK FCMASK +#else +# define OMASK 0644 +#endif + +#define ERR (-1) + +#define NewTab(type, size) (type **) alloc(sizeof(type *) * size) +#define Free(ptr) if(ptr) free((genericptr_t) (ptr)) +#define Write(fd, item, size) if (write(fd, (genericptr_t)(item), size) != size) return FALSE; + +#if defined(__BORLANDC__) && !defined(_WIN32) +extern unsigned _stklen = STKSIZ; +#endif +#define MAX_ERRORS 25 + +extern int NDECL (yyparse); +extern void FDECL (init_yyin, (FILE *)); +extern void FDECL (init_yyout, (FILE *)); + +int FDECL (main, (int, char **)); +void FDECL (yyerror, (const char *)); +void FDECL (yywarning, (const char *)); +int NDECL (yywrap); +int FDECL(get_floor_type, (CHAR_P)); +int FDECL(get_room_type, (char *)); +int FDECL(get_trap_type, (char *)); +int FDECL(get_monster_id, (char *,CHAR_P)); +int FDECL(get_object_id, (char *,CHAR_P)); +boolean FDECL(check_monster_char, (CHAR_P)); +boolean FDECL(check_object_char, (CHAR_P)); +char FDECL(what_map_char, (CHAR_P)); +void FDECL(scan_map, (char *)); +void NDECL(wallify_map); +boolean NDECL(check_subrooms); +void FDECL(check_coord, (int,int,const char *)); +void NDECL(store_part); +void NDECL(store_room); +boolean FDECL(write_level_file, (char *,splev *,specialmaze *)); +void FDECL(free_rooms, (splev *)); + +extern void NDECL(monst_init); +extern void NDECL(objects_init); +extern void NDECL(decl_init); + +static boolean FDECL(write_common_data, (int,int,lev_init *,long)); +static boolean FDECL(write_monsters, (int,char *,monster ***)); +static boolean FDECL(write_objects, (int,char *,object ***)); +static boolean FDECL(write_engravings, (int,char *,engraving ***)); +static boolean FDECL(write_maze, (int,specialmaze *)); +static boolean FDECL(write_rooms, (int,splev *)); +static void NDECL(init_obj_classes); + +static struct { + const char *name; + int type; +} trap_types[] = { + { "arrow", ARROW_TRAP }, + { "dart", DART_TRAP }, + { "falling rock", ROCKTRAP }, + { "board", SQKY_BOARD }, + { "bear", BEAR_TRAP }, + { "land mine", LANDMINE }, + { "rolling boulder", ROLLING_BOULDER_TRAP }, + { "sleep gas", SLP_GAS_TRAP }, + { "rust", RUST_TRAP }, + { "fire", FIRE_TRAP }, + { "pit", PIT }, + { "spiked pit", SPIKED_PIT }, + { "hole", HOLE }, + { "trap door", TRAPDOOR }, + { "teleport", TELEP_TRAP }, + { "level teleport", LEVEL_TELEP }, + { "magic portal", MAGIC_PORTAL }, + { "web", WEB }, + { "statue", STATUE_TRAP }, + { "magic", MAGIC_TRAP }, + { "anti magic", ANTI_MAGIC }, + { "polymorph", POLY_TRAP }, + { 0, 0 } +}; + +static struct { + const char *name; + int type; +} room_types[] = { + /* for historical reasons, room types are not contiguous numbers */ + /* (type 1 is skipped) */ + { "ordinary", OROOM }, + { "throne", COURT }, + { "swamp", SWAMP }, + { "vault", VAULT }, + { "beehive", BEEHIVE }, + { "morgue", MORGUE }, + { "barracks", BARRACKS }, + { "zoo", ZOO }, + { "delphi", DELPHI }, + { "temple", TEMPLE }, + { "anthole", ANTHOLE }, + { "cocknest", COCKNEST }, + { "leprehall", LEPREHALL }, + { "shop", SHOPBASE }, + { "armor shop", ARMORSHOP }, + { "scroll shop", SCROLLSHOP }, + { "potion shop", POTIONSHOP }, + { "weapon shop", WEAPONSHOP }, + { "food shop", FOODSHOP }, + { "ring shop", RINGSHOP }, + { "wand shop", WANDSHOP }, + { "tool shop", TOOLSHOP }, + { "book shop", BOOKSHOP }, + { "candle shop", CANDLESHOP }, + { 0, 0 } +}; + +const char *fname = "(stdin)"; +int fatal_error = 0; +int want_warnings = 0; + +#ifdef FLEX23_BUG +/* Flex 2.3 bug work around; not needed for 2.3.6 or later */ +int yy_more_len = 0; +#endif + +extern char tmpmessage[]; +extern altar *tmpaltar[]; +extern lad *tmplad[]; +extern stair *tmpstair[]; +extern digpos *tmpdig[]; +extern digpos *tmppass[]; +extern char *tmpmap[]; +extern region *tmpreg[]; +extern lev_region *tmplreg[]; +extern door *tmpdoor[]; +extern room_door *tmprdoor[]; +extern trap *tmptrap[]; +extern monster *tmpmonst[]; +extern object *tmpobj[]; +extern drawbridge *tmpdb[]; +extern walk *tmpwalk[]; +extern gold *tmpgold[]; +extern fountain *tmpfountain[]; +extern sink *tmpsink[]; +extern pool *tmppool[]; +extern engraving *tmpengraving[]; +extern mazepart *tmppart[]; +extern room *tmproom[]; + +extern int n_olist, n_mlist, n_plist; + +extern unsigned int nlreg, nreg, ndoor, ntrap, nmons, nobj; +extern unsigned int ndb, nwalk, npart, ndig, npass, nlad, nstair; +extern unsigned int naltar, ncorridor, nrooms, ngold, nengraving; +extern unsigned int nfountain, npool, nsink; + +extern unsigned int max_x_map, max_y_map; + +extern int line_number, colon_line_number; + +int +main(argc, argv) +int argc; +char **argv; +{ + FILE *fin; + int i; + boolean errors_encountered = FALSE; +#if defined(MAC) && (defined(THINK_C) || defined(__MWERKS__)) + static char *mac_argv[] = { "lev_comp", /* dummy argv[0] */ + ":dat:Arch.des", + ":dat:Barb.des", + ":dat:Caveman.des", + ":dat:Healer.des", + ":dat:Knight.des", + ":dat:Monk.des", + ":dat:Priest.des", + ":dat:Ranger.des", + ":dat:Rogue.des", + ":dat:Samurai.des", + ":dat:Tourist.des", + ":dat:Valkyrie.des", + ":dat:Wizard.des", + ":dat:bigroom.des", + ":dat:castle.des", + ":dat:endgame.des", + ":dat:gehennom.des", + ":dat:knox.des", + ":dat:medusa.des", + ":dat:mines.des", + ":dat:oracle.des", + ":dat:sokoban.des", + ":dat:tower.des", + ":dat:yendor.des" + }; + + argc = SIZE(mac_argv); + argv = mac_argv; +#endif + /* Note: these initializers don't do anything except guarantee that + we're linked properly. + */ + monst_init(); + objects_init(); + decl_init(); + /* this one does something... */ + init_obj_classes(); + + init_yyout(stdout); + if (argc == 1) { /* Read standard input */ + init_yyin(stdin); + (void) yyparse(); + if (fatal_error > 0) { + errors_encountered = TRUE; + } + } else { /* Otherwise every argument is a filename */ + for(i=1; i 0) { + errors_encountered = TRUE; + fatal_error = 0; + } + } + } + } + exit(errors_encountered ? EXIT_FAILURE : EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +/* + * Each time the parser detects an error, it uses this function. + * Here we take count of the errors. To continue farther than + * MAX_ERRORS wouldn't be reasonable. + * Assume that explicit calls from lev_comp.y have the 1st letter + * capitalized, to allow printing of the line containing the start of + * the current declaration, instead of the beginning of the next declaration. + */ +void +yyerror(s) +const char *s; +{ + (void) fprintf(stderr, "%s: line %d : %s\n", fname, + (*s >= 'A' && *s <= 'Z') ? colon_line_number : line_number, s); + if (++fatal_error > MAX_ERRORS) { + (void) fprintf(stderr,"Too many errors, good bye!\n"); + exit(EXIT_FAILURE); + } +} + +/* + * Just display a warning (that is : a non fatal error) + */ +void +yywarning(s) +const char *s; +{ + (void) fprintf(stderr, "%s: line %d : WARNING : %s\n", + fname, colon_line_number, s); +} + +/* + * Stub needed for lex interface. + */ +int +yywrap() +{ + return 1; +} + +/* + * Find the type of floor, knowing its char representation. + */ +int +get_floor_type(c) +char c; +{ + int val; + + SpinCursor(3); + val = what_map_char(c); + if(val == INVALID_TYPE) { + val = ERR; + yywarning("Invalid fill character in MAZE declaration"); + } + return val; +} + +/* + * Find the type of a room in the table, knowing its name. + */ +int +get_room_type(s) +char *s; +{ + register int i; + + SpinCursor(3); + for(i=0; room_types[i].name; i++) + if (!strcmp(s, room_types[i].name)) + return ((int) room_types[i].type); + return ERR; +} + +/* + * Find the type of a trap in the table, knowing its name. + */ +int +get_trap_type(s) +char *s; +{ + register int i; + + SpinCursor(3); + for (i=0; trap_types[i].name; i++) + if(!strcmp(s,trap_types[i].name)) + return trap_types[i].type; + return ERR; +} + +/* + * Find the index of a monster in the table, knowing its name. + */ +int +get_monster_id(s, c) +char *s; +char c; +{ + register int i, class; + + SpinCursor(3); + class = c ? def_char_to_monclass(c) : 0; + if (class == MAXMCLASSES) return ERR; + + for (i = LOW_PM; i < NUMMONS; i++) + if (!class || class == mons[i].mlet) + if (!strcmp(s, mons[i].mname)) return i; + return ERR; +} + +/* + * Find the index of an object in the table, knowing its name. + */ +int +get_object_id(s, c) +char *s; +char c; /* class */ +{ + int i, class; + const char *objname; + + SpinCursor(3); + class = (c > 0) ? def_char_to_objclass(c) : 0; + if (class == MAXOCLASSES) return ERR; + + for (i = class ? bases[class] : 0; i < NUM_OBJECTS; i++) { + if (class && objects[i].oc_class != class) break; + objname = obj_descr[i].oc_name; + if (objname && !strcmp(s, objname)) + return i; + } + return ERR; +} + +static void +init_obj_classes() +{ + int i, class, prev_class; + + prev_class = -1; + for (i = 0; i < NUM_OBJECTS; i++) { + class = objects[i].oc_class; + if (class != prev_class) { + bases[class] = i; + prev_class = class; + } + } +} + +/* + * Is the character 'c' a valid monster class ? + */ +boolean +check_monster_char(c) +char c; +{ + return (def_char_to_monclass(c) != MAXMCLASSES); +} + +/* + * Is the character 'c' a valid object class ? + */ +boolean +check_object_char(c) +char c; +{ + return (def_char_to_objclass(c) != MAXOCLASSES); +} + +/* + * Convert .des map letter into floor type. + */ +char +what_map_char(c) +char c; +{ + SpinCursor(3); + switch(c) { + case ' ' : return(STONE); + case '#' : return(CORR); + case '.' : return(ROOM); + case '-' : return(HWALL); + case '|' : return(VWALL); + case '+' : return(DOOR); + case 'A' : return(AIR); + case 'B' : return(CROSSWALL); /* hack: boundary location */ + case 'C' : return(CLOUD); + case 'S' : return(SDOOR); + case 'H' : return(SCORR); + case '{' : return(FOUNTAIN); + case '\\' : return(THRONE); + case 'K' : +#ifdef SINKS + return(SINK); +#else + yywarning("Sinks are not allowed in this version! Ignoring..."); + return(ROOM); +#endif + case '}' : return(MOAT); + case 'P' : return(POOL); + case 'L' : return(LAVAPOOL); + case 'I' : return(ICE); + case 'W' : return(WATER); + case 'T' : return (TREE); + case 'F' : return (IRONBARS); /* Fe = iron */ + } + return(INVALID_TYPE); +} + +/* + * Yep! LEX gives us the map in a raw mode. + * Just analyze it here. + */ +void +scan_map(map) +char *map; +{ + register int i, len; + register char *s1, *s2; + int max_len = 0; + int max_hig = 0; + char msg[256]; + + /* First, strip out digits 0-9 (line numbering) */ + for (s1 = s2 = map; *s1; s1++) + if (*s1 < '0' || *s1 > '9') + *s2++ = *s1; + *s2 = '\0'; + + /* Second, find the max width of the map */ + s1 = map; + while (s1 && *s1) { + s2 = index(s1, '\n'); + if (s2) { + len = (int) (s2 - s1); + s1 = s2 + 1; + } else { + len = (int) strlen(s1); + s1 = (char *) 0; + } + if (len > max_len) max_len = len; + } + + /* Then parse it now */ + while (map && *map) { + tmpmap[max_hig] = (char *) alloc(max_len); + s1 = index(map, '\n'); + if (s1) { + len = (int) (s1 - map); + s1++; + } else { + len = (int) strlen(map); + s1 = map + len; + } + for(i=0; i MAP_X_LIM || max_hig > MAP_Y_LIM) { + Sprintf(msg, "Map too large! (max %d x %d)", MAP_X_LIM, MAP_Y_LIM); + yyerror(msg); + } + + tmppart[npart]->xsize = max_len; + tmppart[npart]->ysize = max_hig; + tmppart[npart]->map = (char **) alloc(max_hig*sizeof(char *)); + for(i = 0; i< max_hig; i++) + tmppart[npart]->map[i] = tmpmap[i]; +} + +/* + * If we have drawn a map without walls, this allows us to + * auto-magically wallify it. + */ +#define Map_point(x,y) *(tmppart[npart]->map[y] + x) + +void +wallify_map() +{ + unsigned int x, y, xx, yy, lo_xx, lo_yy, hi_xx, hi_yy; + + for (y = 0; y <= max_y_map; y++) { + SpinCursor(3); + lo_yy = (y > 0) ? y - 1 : 0; + hi_yy = (y < max_y_map) ? y + 1 : max_y_map; + for (x = 0; x <= max_x_map; x++) { + if (Map_point(x,y) != STONE) continue; + lo_xx = (x > 0) ? x - 1 : 0; + hi_xx = (x < max_x_map) ? x + 1 : max_x_map; + for (yy = lo_yy; yy <= hi_yy; yy++) + for (xx = lo_xx; xx <= hi_xx; xx++) + if (IS_ROOM(Map_point(xx,yy)) || + Map_point(xx,yy) == CROSSWALL) { + Map_point(x,y) = (yy != y) ? HWALL : VWALL; + yy = hi_yy; /* end `yy' loop */ + break; /* end `xx' loop */ + } + } + } +} + +/* + * We need to check the subrooms apartenance to an existing room. + */ +boolean +check_subrooms() +{ + unsigned i, j, n_subrooms; + boolean found, ok = TRUE; + char *last_parent, msg[256]; + + for (i = 0; i < nrooms; i++) + if (tmproom[i]->parent) { + found = FALSE; + for(j = 0; j < nrooms; j++) + if (tmproom[j]->name && + !strcmp(tmproom[i]->parent, tmproom[j]->name)) { + found = TRUE; + break; + } + if (!found) { + Sprintf(msg, + "Subroom error : parent room '%s' not found!", + tmproom[i]->parent); + yyerror(msg); + ok = FALSE; + } + } + + msg[0] = '\0'; + last_parent = msg; + for (i = 0; i < nrooms; i++) + if (tmproom[i]->parent) { + n_subrooms = 0; + for(j = i; j < nrooms; j++) { +/* + * This is by no means perfect, but should cut down the duplicate error + * messages by over 90%. The only problem will be when either subrooms + * are mixed in the level definition (not likely but possible) or rooms + * have subrooms that have subrooms. + */ + if (!strcmp(tmproom[i]->parent, last_parent)) continue; + if (tmproom[j]->parent && + !strcmp(tmproom[i]->parent, tmproom[j]->parent)) { + n_subrooms++; + if(n_subrooms > MAX_SUBROOMS) { + + Sprintf(msg, + "Subroom error: too many subrooms attached to parent room '%s'!", + tmproom[i]->parent); + yyerror(msg); + last_parent = tmproom[i]->parent; + ok = FALSE; + break; + } + } + } + } + return ok; +} + +/* + * Check that coordinates (x,y) are roomlike locations. + * Print warning "str" if they aren't. + */ +void +check_coord(x, y, str) +int x, y; +const char *str; +{ + char ebuf[60]; + + if (x >= 0 && y >= 0 && x <= (int)max_x_map && y <= (int)max_y_map && + (IS_ROCK(tmpmap[y][x]) || IS_DOOR(tmpmap[y][x]))) { + Sprintf(ebuf, "%s placed in wall at (%02d,%02d)?!", str, x, y); + yywarning(ebuf); + } +} + +/* + * Here we want to store the maze part we just got. + */ +void +store_part() +{ + register unsigned i; + + /* Ok, We got the whole part, now we store it. */ + + /* The Regions */ + + if ((tmppart[npart]->nreg = nreg) != 0) { + tmppart[npart]->regions = NewTab(region, nreg); + for(i=0;iregions[i] = tmpreg[i]; + } + nreg = 0; + + /* The Level Regions */ + + if ((tmppart[npart]->nlreg = nlreg) != 0) { + tmppart[npart]->lregions = NewTab(lev_region, nlreg); + for(i=0;ilregions[i] = tmplreg[i]; + } + nlreg = 0; + + /* the doors */ + + if ((tmppart[npart]->ndoor = ndoor) != 0) { + tmppart[npart]->doors = NewTab(door, ndoor); + for(i=0;idoors[i] = tmpdoor[i]; + } + ndoor = 0; + + /* the drawbridges */ + + if ((tmppart[npart]->ndrawbridge = ndb) != 0) { + tmppart[npart]->drawbridges = NewTab(drawbridge, ndb); + for(i=0;idrawbridges[i] = tmpdb[i]; + } + ndb = 0; + + /* The walkmaze directives */ + + if ((tmppart[npart]->nwalk = nwalk) != 0) { + tmppart[npart]->walks = NewTab(walk, nwalk); + for(i=0;iwalks[i] = tmpwalk[i]; + } + nwalk = 0; + + /* The non_diggable directives */ + + if ((tmppart[npart]->ndig = ndig) != 0) { + tmppart[npart]->digs = NewTab(digpos, ndig); + for(i=0;idigs[i] = tmpdig[i]; + } + ndig = 0; + + /* The non_passwall directives */ + + if ((tmppart[npart]->npass = npass) != 0) { + tmppart[npart]->passs = NewTab(digpos, npass); + for(i=0;ipasss[i] = tmppass[i]; + } + npass = 0; + + /* The ladders */ + + if ((tmppart[npart]->nlad = nlad) != 0) { + tmppart[npart]->lads = NewTab(lad, nlad); + for(i=0;ilads[i] = tmplad[i]; + } + nlad = 0; + + /* The stairs */ + + if ((tmppart[npart]->nstair = nstair) != 0) { + tmppart[npart]->stairs = NewTab(stair, nstair); + for(i=0;istairs[i] = tmpstair[i]; + } + nstair = 0; + + /* The altars */ + if ((tmppart[npart]->naltar = naltar) != 0) { + tmppart[npart]->altars = NewTab(altar, naltar); + for(i=0;ialtars[i] = tmpaltar[i]; + } + naltar = 0; + + /* The fountains */ + + if ((tmppart[npart]->nfountain = nfountain) != 0) { + tmppart[npart]->fountains = NewTab(fountain, nfountain); + for(i=0;ifountains[i] = tmpfountain[i]; + } + nfountain = 0; + + /* the traps */ + + if ((tmppart[npart]->ntrap = ntrap) != 0) { + tmppart[npart]->traps = NewTab(trap, ntrap); + for(i=0;itraps[i] = tmptrap[i]; + } + ntrap = 0; + + /* the monsters */ + + if ((tmppart[npart]->nmonster = nmons) != 0) { + tmppart[npart]->monsters = NewTab(monster, nmons); + for(i=0;imonsters[i] = tmpmonst[i]; + } else + tmppart[npart]->monsters = 0; + nmons = 0; + + /* the objects */ + + if ((tmppart[npart]->nobject = nobj) != 0) { + tmppart[npart]->objects = NewTab(object, nobj); + for(i=0;iobjects[i] = tmpobj[i]; + } else + tmppart[npart]->objects = 0; + nobj = 0; + + /* The gold piles */ + + if ((tmppart[npart]->ngold = ngold) != 0) { + tmppart[npart]->golds = NewTab(gold, ngold); + for(i=0;igolds[i] = tmpgold[i]; + } + ngold = 0; + + /* The engravings */ + + if ((tmppart[npart]->nengraving = nengraving) != 0) { + tmppart[npart]->engravings = NewTab(engraving, nengraving); + for(i=0;iengravings[i] = tmpengraving[i]; + } else + tmppart[npart]->engravings = 0; + nengraving = 0; + + npart++; + n_plist = n_mlist = n_olist = 0; +} + +/* + * Here we want to store the room part we just got. + */ +void +store_room() +{ + register unsigned i; + + /* Ok, We got the whole room, now we store it. */ + + /* the doors */ + + if ((tmproom[nrooms]->ndoor = ndoor) != 0) { + tmproom[nrooms]->doors = NewTab(room_door, ndoor); + for(i=0;idoors[i] = tmprdoor[i]; + } + ndoor = 0; + + /* The stairs */ + + if ((tmproom[nrooms]->nstair = nstair) != 0) { + tmproom[nrooms]->stairs = NewTab(stair, nstair); + for(i=0;istairs[i] = tmpstair[i]; + } + nstair = 0; + + /* The altars */ + if ((tmproom[nrooms]->naltar = naltar) != 0) { + tmproom[nrooms]->altars = NewTab(altar, naltar); + for(i=0;ialtars[i] = tmpaltar[i]; + } + naltar = 0; + + /* The fountains */ + + if ((tmproom[nrooms]->nfountain = nfountain) != 0) { + tmproom[nrooms]->fountains = NewTab(fountain, nfountain); + for(i=0;ifountains[i] = tmpfountain[i]; + } + nfountain = 0; + + /* The sinks */ + + if ((tmproom[nrooms]->nsink = nsink) != 0) { + tmproom[nrooms]->sinks = NewTab(sink, nsink); + for(i=0;isinks[i] = tmpsink[i]; + } + nsink = 0; + + /* The pools */ + + if ((tmproom[nrooms]->npool = npool) != 0) { + tmproom[nrooms]->pools = NewTab(pool, npool); + for(i=0;ipools[i] = tmppool[i]; + } + npool = 0; + + /* the traps */ + + if ((tmproom[nrooms]->ntrap = ntrap) != 0) { + tmproom[nrooms]->traps = NewTab(trap, ntrap); + for(i=0;itraps[i] = tmptrap[i]; + } + ntrap = 0; + + /* the monsters */ + + if ((tmproom[nrooms]->nmonster = nmons) != 0) { + tmproom[nrooms]->monsters = NewTab(monster, nmons); + for(i=0;imonsters[i] = tmpmonst[i]; + } else + tmproom[nrooms]->monsters = 0; + nmons = 0; + + /* the objects */ + + if ((tmproom[nrooms]->nobject = nobj) != 0) { + tmproom[nrooms]->objects = NewTab(object, nobj); + for(i=0;iobjects[i] = tmpobj[i]; + } else + tmproom[nrooms]->objects = 0; + nobj = 0; + + /* The gold piles */ + + if ((tmproom[nrooms]->ngold = ngold) != 0) { + tmproom[nrooms]->golds = NewTab(gold, ngold); + for(i=0;igolds[i] = tmpgold[i]; + } + ngold = 0; + + /* The engravings */ + + if ((tmproom[nrooms]->nengraving = nengraving) != 0) { + tmproom[nrooms]->engravings = NewTab(engraving, nengraving); + for(i=0;iengravings[i] = tmpengraving[i]; + } else + tmproom[nrooms]->engravings = 0; + nengraving = 0; + + nrooms++; +} + +/* + * Output some info common to all special levels. + */ +static boolean +write_common_data(fd, typ, init, flgs) +int fd, typ; +lev_init *init; +long flgs; +{ + char c; + uchar len; + static struct version_info version_data = { + VERSION_NUMBER, VERSION_FEATURES, + VERSION_SANITY1, VERSION_SANITY2 + }; + + Write(fd, &version_data, sizeof version_data); + c = typ; + Write(fd, &c, sizeof(c)); /* 1 byte header */ + Write(fd, init, sizeof(lev_init)); + Write(fd, &flgs, sizeof flgs); + + len = (uchar) strlen(tmpmessage); + Write(fd, &len, sizeof len); + if (len) Write(fd, tmpmessage, (int) len); + tmpmessage[0] = '\0'; + return TRUE; +} + +/* + * Output monster info, which needs string fixups, then release memory. + */ +static boolean +write_monsters(fd, nmonster_p, monsters_p) +int fd; +char *nmonster_p; +monster ***monsters_p; +{ + monster *m; + char *name, *appr; + int j, n = (int)*nmonster_p; + + Write(fd, nmonster_p, sizeof *nmonster_p); + for (j = 0; j < n; j++) { + m = (*monsters_p)[j]; + name = m->name.str; + appr = m->appear_as.str; + m->name.str = m->appear_as.str = 0; + m->name.len = name ? strlen(name) : 0; + m->appear_as.len = appr ? strlen(appr) : 0; + Write(fd, m, sizeof *m); + if (name) { + Write(fd, name, m->name.len); + Free(name); + } + if (appr) { + Write(fd, appr, m->appear_as.len); + Free(appr); + } + Free(m); + } + if (*monsters_p) { + Free(*monsters_p); + *monsters_p = 0; + } + *nmonster_p = 0; + return TRUE; +} + +/* + * Output object info, which needs string fixup, then release memory. + */ +static boolean +write_objects(fd, nobject_p, objects_p) +int fd; +char *nobject_p; +object ***objects_p; +{ + object *o; + char *name; + int j, n = (int)*nobject_p; + + Write(fd, nobject_p, sizeof *nobject_p); + for (j = 0; j < n; j++) { + o = (*objects_p)[j]; + name = o->name.str; + o->name.str = 0; /* reset in case `len' is narrower */ + o->name.len = name ? strlen(name) : 0; + Write(fd, o, sizeof *o); + if (name) { + Write(fd, name, o->name.len); + Free(name); + } + Free(o); + } + if (*objects_p) { + Free(*objects_p); + *objects_p = 0; + } + *nobject_p = 0; + return TRUE; +} + +/* + * Output engraving info, which needs string fixup, then release memory. + */ +static boolean +write_engravings(fd, nengraving_p, engravings_p) +int fd; +char *nengraving_p; +engraving ***engravings_p; +{ + engraving *e; + char *engr; + int j, n = (int)*nengraving_p; + + Write(fd, nengraving_p, sizeof *nengraving_p); + for (j = 0; j < n; j++) { + e = (*engravings_p)[j]; + engr = e->engr.str; + e->engr.str = 0; /* reset in case `len' is narrower */ + e->engr.len = strlen(engr); + Write(fd, e, sizeof *e); + Write(fd, engr, e->engr.len); + Free(engr); + Free(e); + } + if (*engravings_p) { + Free(*engravings_p); + *engravings_p = 0; + } + *nengraving_p = 0; + return TRUE; +} + +/* + * Open and write maze or rooms file, based on which pointer is non-null. + * Return TRUE on success, FALSE on failure. + */ +boolean +write_level_file(filename, room_level, maze_level) +char *filename; +splev *room_level; +specialmaze *maze_level; +{ + int fout; + char lbuf[60]; + + lbuf[0] = '\0'; +#ifdef PREFIX + Strcat(lbuf, PREFIX); +#endif + Strcat(lbuf, filename); + Strcat(lbuf, LEV_EXT); + +#if defined(MAC) && (defined(__SC__) || defined(__MRC__)) + fout = open(lbuf, O_WRONLY|O_CREAT|O_BINARY); +#else + fout = open(lbuf, O_WRONLY|O_CREAT|O_BINARY, OMASK); +#endif + if (fout < 0) return FALSE; + + if (room_level) { + if (!write_rooms(fout, room_level)) + return FALSE; + } else if (maze_level) { + if (!write_maze(fout, maze_level)) + return FALSE; + } else + panic("write_level_file"); + + (void) close(fout); + return TRUE; +} + +/* + * Here we write the structure of the maze in the specified file (fd). + * Also, we have to free the memory allocated via alloc(). + */ +static boolean +write_maze(fd, maze) +int fd; +specialmaze *maze; +{ + short i,j; + mazepart *pt; + + if (!write_common_data(fd, SP_LEV_MAZE, &(maze->init_lev), maze->flags)) + return FALSE; + + Write(fd, &(maze->filling), sizeof(maze->filling)); + Write(fd, &(maze->numpart), sizeof(maze->numpart)); + /* Number of parts */ + for(i=0;inumpart;i++) { + pt = maze->parts[i]; + + /* First, write the map */ + + Write(fd, &(pt->halign), sizeof(pt->halign)); + Write(fd, &(pt->valign), sizeof(pt->valign)); + Write(fd, &(pt->xsize), sizeof(pt->xsize)); + Write(fd, &(pt->ysize), sizeof(pt->ysize)); + for(j=0;jysize;j++) { + if(!maze->init_lev.init_present || + pt->xsize > 1 || pt->ysize > 1) { +#if !defined(_MSC_VER) && !defined(__BORLANDC__) + Write(fd, pt->map[j], pt->xsize * sizeof *pt->map[j]); +#else + /* + * On MSVC and Borland C compilers the Write macro above caused: + * warning '!=' : signed/unsigned mismatch + */ + unsigned reslt, sz = pt->xsize * sizeof *pt->map[j]; + reslt = write(fd, (genericptr_t)(pt->map[j]), sz); + if (reslt != sz) return FALSE; +#endif + } + Free(pt->map[j]); + } + Free(pt->map); + + /* level region stuff */ + Write(fd, &pt->nlreg, sizeof pt->nlreg); + for (j = 0; j < pt->nlreg; j++) { + lev_region *l = pt->lregions[j]; + char *rname = l->rname.str; + l->rname.str = 0; /* reset in case `len' is narrower */ + l->rname.len = rname ? strlen(rname) : 0; + Write(fd, l, sizeof *l); + if (rname) { + Write(fd, rname, l->rname.len); + Free(rname); + } + Free(l); + } + if (pt->nlreg > 0) + Free(pt->lregions); + + /* The random registers */ + Write(fd, &(pt->nrobjects), sizeof(pt->nrobjects)); + if(pt->nrobjects) { + Write(fd, pt->robjects, pt->nrobjects); + Free(pt->robjects); + } + Write(fd, &(pt->nloc), sizeof(pt->nloc)); + if(pt->nloc) { + Write(fd, pt->rloc_x, pt->nloc); + Write(fd, pt->rloc_y, pt->nloc); + Free(pt->rloc_x); + Free(pt->rloc_y); + } + Write(fd, &(pt->nrmonst), sizeof(pt->nrmonst)); + if(pt->nrmonst) { + Write(fd, pt->rmonst, pt->nrmonst); + Free(pt->rmonst); + } + + /* subrooms */ + Write(fd, &(pt->nreg), sizeof(pt->nreg)); + for(j=0;jnreg;j++) { + Write(fd, pt->regions[j], sizeof(region)); + Free(pt->regions[j]); + } + if(pt->nreg > 0) + Free(pt->regions); + + /* the doors */ + Write(fd, &(pt->ndoor), sizeof(pt->ndoor)); + for(j=0;jndoor;j++) { + Write(fd, pt->doors[j], sizeof(door)); + Free(pt->doors[j]); + } + if (pt->ndoor > 0) + Free(pt->doors); + + /* The drawbridges */ + Write(fd, &(pt->ndrawbridge), sizeof(pt->ndrawbridge)); + for(j=0;jndrawbridge;j++) { + Write(fd, pt->drawbridges[j], sizeof(drawbridge)); + Free(pt->drawbridges[j]); + } + if(pt->ndrawbridge > 0) + Free(pt->drawbridges); + + /* The mazewalk directives */ + Write(fd, &(pt->nwalk), sizeof(pt->nwalk)); + for(j=0; jnwalk; j++) { + Write(fd, pt->walks[j], sizeof(walk)); + Free(pt->walks[j]); + } + if (pt->nwalk > 0) + Free(pt->walks); + + /* The non_diggable directives */ + Write(fd, &(pt->ndig), sizeof(pt->ndig)); + for(j=0;jndig;j++) { + Write(fd, pt->digs[j], sizeof(digpos)); + Free(pt->digs[j]); + } + if (pt->ndig > 0) + Free(pt->digs); + + /* The non_passwall directives */ + Write(fd, &(pt->npass), sizeof(pt->npass)); + for(j=0;jnpass;j++) { + Write(fd, pt->passs[j], sizeof(digpos)); + Free(pt->passs[j]); + } + if (pt->npass > 0) + Free(pt->passs); + + /* The ladders */ + Write(fd, &(pt->nlad), sizeof(pt->nlad)); + for(j=0;jnlad;j++) { + Write(fd, pt->lads[j], sizeof(lad)); + Free(pt->lads[j]); + } + if (pt->nlad > 0) + Free(pt->lads); + + /* The stairs */ + Write(fd, &(pt->nstair), sizeof(pt->nstair)); + for(j=0;jnstair;j++) { + Write(fd, pt->stairs[j], sizeof(stair)); + Free(pt->stairs[j]); + } + if (pt->nstair > 0) + Free(pt->stairs); + + /* The altars */ + Write(fd, &(pt->naltar), sizeof(pt->naltar)); + for(j=0;jnaltar;j++) { + Write(fd, pt->altars[j], sizeof(altar)); + Free(pt->altars[j]); + } + if (pt->naltar > 0) + Free(pt->altars); + + /* The fountains */ + Write(fd, &(pt->nfountain), sizeof(pt->nfountain)); + for(j=0;jnfountain;j++) { + Write(fd, pt->fountains[j], sizeof(fountain)); + Free(pt->fountains[j]); + } + if (pt->nfountain > 0) + Free(pt->fountains); + + /* The traps */ + Write(fd, &(pt->ntrap), sizeof(pt->ntrap)); + for(j=0;jntrap;j++) { + Write(fd, pt->traps[j], sizeof(trap)); + Free(pt->traps[j]); + } + if (pt->ntrap) + Free(pt->traps); + + /* The monsters */ + if (!write_monsters(fd, &pt->nmonster, &pt->monsters)) + return FALSE; + + /* The objects */ + if (!write_objects(fd, &pt->nobject, &pt->objects)) + return FALSE; + + /* The gold piles */ + Write(fd, &(pt->ngold), sizeof(pt->ngold)); + for(j=0;jngold;j++) { + Write(fd, pt->golds[j], sizeof(gold)); + Free(pt->golds[j]); + } + if (pt->ngold > 0) + Free(pt->golds); + + /* The engravings */ + if (!write_engravings(fd, &pt->nengraving, &pt->engravings)) + return FALSE; + + Free(pt); + } + + Free(maze->parts); + maze->parts = (mazepart **)0; + maze->numpart = 0; + return TRUE; +} + +/* + * Here we write the structure of the room level in the specified file (fd). + */ +static boolean +write_rooms(fd, lev) +int fd; +splev *lev; +{ + short i,j, size; + room *pt; + + if (!write_common_data(fd, SP_LEV_ROOMS, &(lev->init_lev), lev->flags)) + return FALSE; + + /* Random registers */ + + Write(fd, &lev->nrobjects, sizeof(lev->nrobjects)); + if (lev->nrobjects) + Write(fd, lev->robjects, lev->nrobjects); + Write(fd, &lev->nrmonst, sizeof(lev->nrmonst)); + if (lev->nrmonst) + Write(fd, lev->rmonst, lev->nrmonst); + + Write(fd, &(lev->nroom), sizeof(lev->nroom)); + /* Number of rooms */ + for(i=0;inroom;i++) { + pt = lev->rooms[i]; + + /* Room characteristics */ + + size = (short) (pt->name ? strlen(pt->name) : 0); + Write(fd, &size, sizeof(size)); + if (size) + Write(fd, pt->name, size); + + size = (short) (pt->parent ? strlen(pt->parent) : 0); + Write(fd, &size, sizeof(size)); + if (size) + Write(fd, pt->parent, size); + + Write(fd, &(pt->x), sizeof(pt->x)); + Write(fd, &(pt->y), sizeof(pt->y)); + Write(fd, &(pt->w), sizeof(pt->w)); + Write(fd, &(pt->h), sizeof(pt->h)); + Write(fd, &(pt->xalign), sizeof(pt->xalign)); + Write(fd, &(pt->yalign), sizeof(pt->yalign)); + Write(fd, &(pt->rtype), sizeof(pt->rtype)); + Write(fd, &(pt->chance), sizeof(pt->chance)); + Write(fd, &(pt->rlit), sizeof(pt->rlit)); + Write(fd, &(pt->filled), sizeof(pt->filled)); + + /* the doors */ + Write(fd, &(pt->ndoor), sizeof(pt->ndoor)); + for(j=0;jndoor;j++) + Write(fd, pt->doors[j], sizeof(room_door)); + + /* The stairs */ + Write(fd, &(pt->nstair), sizeof(pt->nstair)); + for(j=0;jnstair;j++) + Write(fd, pt->stairs[j], sizeof(stair)); + + /* The altars */ + Write(fd, &(pt->naltar), sizeof(pt->naltar)); + for(j=0;jnaltar;j++) + Write(fd, pt->altars[j], sizeof(altar)); + + /* The fountains */ + Write(fd, &(pt->nfountain), sizeof(pt->nfountain)); + for(j=0;jnfountain;j++) + Write(fd, pt->fountains[j], sizeof(fountain)); + + /* The sinks */ + Write(fd, &(pt->nsink), sizeof(pt->nsink)); + for(j=0;jnsink;j++) + Write(fd, pt->sinks[j], sizeof(sink)); + + /* The pools */ + Write(fd, &(pt->npool), sizeof(pt->npool)); + for(j=0;jnpool;j++) + Write(fd, pt->pools[j], sizeof(pool)); + + /* The traps */ + Write(fd, &(pt->ntrap), sizeof(pt->ntrap)); + for(j=0;jntrap;j++) + Write(fd, pt->traps[j], sizeof(trap)); + + /* The monsters */ + if (!write_monsters(fd, &pt->nmonster, &pt->monsters)) + return FALSE; + + /* The objects */ + if (!write_objects(fd, &pt->nobject, &pt->objects)) + return FALSE; + + /* The gold piles */ + Write(fd, &(pt->ngold), sizeof(pt->ngold)); + for(j=0;jngold;j++) + Write(fd, pt->golds[j], sizeof(gold)); + + /* The engravings */ + if (!write_engravings(fd, &pt->nengraving, &pt->engravings)) + return FALSE; + + } + + /* The corridors */ + Write(fd, &lev->ncorr, sizeof(lev->ncorr)); + for (i=0; i < lev->ncorr; i++) + Write(fd, lev->corrs[i], sizeof(corridor)); + return TRUE; +} + +/* + * Release memory allocated to a rooms-style special level; maze-style + * levels have the fields freed as they're written; monsters, objects, and + * engravings are freed as written for both styles, so not handled here. + */ +void +free_rooms(lev) +splev *lev; +{ + room *r; + int j, n = lev->nroom; + + while(n--) { + r = lev->rooms[n]; + Free(r->name); + Free(r->parent); + if ((j = r->ndoor) != 0) { + while(j--) + Free(r->doors[j]); + Free(r->doors); + } + if ((j = r->nstair) != 0) { + while(j--) + Free(r->stairs[j]); + Free(r->stairs); + } + if ((j = r->naltar) != 0) { + while (j--) + Free(r->altars[j]); + Free(r->altars); + } + if ((j = r->nfountain) != 0) { + while(j--) + Free(r->fountains[j]); + Free(r->fountains); + } + if ((j = r->nsink) != 0) { + while(j--) + Free(r->sinks[j]); + Free(r->sinks); + } + if ((j = r->npool) != 0) { + while(j--) + Free(r->pools[j]); + Free(r->pools); + } + if ((j = r->ntrap) != 0) { + while (j--) + Free(r->traps[j]); + Free(r->traps); + } + if ((j = r->ngold) != 0) { + while(j--) + Free(r->golds[j]); + Free(r->golds); + } + Free(r); + lev->rooms[n] = (room *)0; + } + Free(lev->rooms); + lev->rooms = (room **)0; + lev->nroom = 0; + + for (j = 0; j < lev->ncorr; j++) { + Free(lev->corrs[j]); + lev->corrs[j] = (corridor *)0; + } + Free(lev->corrs); + lev->corrs = (corridor **)0; + lev->ncorr = 0; + + Free(lev->robjects); + lev->robjects = (char *)0; + lev->nrobjects = 0; + Free(lev->rmonst); + lev->rmonst = (char *)0; + lev->nrmonst = 0; +} + +#ifdef STRICT_REF_DEF +/* + * Any globals declared in hack.h and descendents which aren't defined + * in the modules linked into lev_comp should be defined here. These + * definitions can be dummies: their sizes shouldn't matter as long as + * as their types are correct; actual values are irrelevant. + */ +#define ARBITRARY_SIZE 1 +/* attrib.c */ +struct attribs attrmax, attrmin; +/* files.c */ +const char *configfile; +char lock[ARBITRARY_SIZE]; +char SAVEF[ARBITRARY_SIZE]; +# ifdef MICRO +char SAVEP[ARBITRARY_SIZE]; +# endif +/* termcap.c */ +struct tc_lcl_data tc_lcl_data; +# ifdef TEXTCOLOR +# ifdef TOS +const char *hilites[CLR_MAX]; +# else +char NEARDATA *hilites[CLR_MAX]; +# endif +# endif +/* trap.c */ +const char *traps[TRAPNUM]; +/* window.c */ +struct window_procs windowprocs; +/* xxxtty.c */ +# ifdef DEFINE_OSPEED +short ospeed; +# endif +#endif /* STRICT_REF_DEF */ + +/*lev_main.c*/ diff --git a/util/makedefs.c b/util/makedefs.c new file mode 100644 index 0000000..23dace2 --- /dev/null +++ b/util/makedefs.c @@ -0,0 +1,2199 @@ +/* SCCS Id: @(#)makedefs.c 3.4 2002/08/14 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* Copyright (c) M. Stephenson, 1990, 1991. */ +/* Copyright (c) Dean Luick, 1990. */ +/* NetHack may be freely redistributed. See license for details. */ + +#define MAKEDEFS_C /* use to conditionally include file sections */ +/* #define DEBUG */ /* uncomment for debugging info */ + +#include "config.h" +#include "permonst.h" +#include "objclass.h" +#include "monsym.h" +#include "artilist.h" +#include "dungeon.h" +#include "obj.h" +#include "monst.h" +#include "you.h" +#include "flag.h" +#include "dlb.h" + +/* version information */ +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +#ifdef MAC +# if defined(__SC__) || defined(__MRC__) /* MPW compilers */ +# define MPWTOOL +#include +#include +#include +# else /* MAC without MPWTOOL */ +# define MACsansMPWTOOL +# endif +#endif /* MAC */ + +#ifndef MPWTOOL +# define SpinCursor(x) +#endif + +#define Fprintf (void) fprintf +#define Fclose (void) fclose +#define Unlink (void) unlink +#if !defined(AMIGA) || defined(AZTEC_C) +#define rewind(fp) fseek((fp),0L,SEEK_SET) /* guarantee a return value */ +#endif + +#if defined(UNIX) && !defined(LINT) && !defined(GCC_WARN) +static const char SCCS_Id[] = "@(#)makedefs.c\t3.4\t2002/02/03"; +#endif + + /* names of files to be generated */ +#define DATE_FILE "date.h" +#define MONST_FILE "pm.h" +#define ONAME_FILE "onames.h" +#ifndef OPTIONS_FILE +#define OPTIONS_FILE "options" +#endif +#define ORACLE_FILE "oracles" +#define DATA_FILE "data" +#define RUMOR_FILE "rumors" +#define DGN_I_FILE "dungeon.def" +#define DGN_O_FILE "dungeon.pdf" +#define MON_STR_C "monstr.c" +#define QTXT_I_FILE "quest.txt" +#define QTXT_O_FILE "quest.dat" +#define VIS_TAB_H "vis_tab.h" +#define VIS_TAB_C "vis_tab.c" + /* locations for those files */ +#ifdef AMIGA +# define FILE_PREFIX +# define INCLUDE_TEMPLATE "NH:include/t.%s" +# define SOURCE_TEMPLATE "NH:src/%s" +# define DGN_TEMPLATE "NH:dat/%s" /* where dungeon.pdf file goes */ +# define DATA_TEMPLATE "NH:slib/%s" +# define DATA_IN_TEMPLATE "NH:dat/%s" +#else /* not AMIGA */ +# if defined(MAC) && !defined(__MACH__) + /* MacOS 9 or earlier */ +# define INCLUDE_TEMPLATE ":include:%s" +# define SOURCE_TEMPLATE ":src:%s" +# define DGN_TEMPLATE ":dat:%s" /* where dungeon.pdf file goes */ +# if __SC__ || __MRC__ +# define DATA_TEMPLATE ":Dungeon:%s" +# else +# define DATA_TEMPLATE ":lib:%s" +# endif /* __SC__ || __MRC__ */ +# define DATA_IN_TEMPLATE ":dat:%s" +# else /* neither AMIGA nor MAC */ +# ifdef OS2 +# define INCLUDE_TEMPLATE "..\\include\\%s" +# define SOURCE_TEMPLATE "..\\src\\%s" +# define DGN_TEMPLATE "..\\dat\\%s" /* where dungeon.pdf file goes */ +# define DATA_TEMPLATE "..\\dat\\%s" +# define DATA_IN_TEMPLATE "..\\dat\\%s" +# else /* not AMIGA, MAC, or OS2 */ +# define INCLUDE_TEMPLATE "../include/%s" +# define SOURCE_TEMPLATE "../src/%s" +# define DGN_TEMPLATE "../dat/%s" /* where dungeon.pdf file goes */ +# define DATA_TEMPLATE "../dat/%s" +# define DATA_IN_TEMPLATE "../dat/%s" +# endif /* else !OS2 */ +# endif /* else !MAC */ +#endif /* else !AMIGA */ + +static const char + *Dont_Edit_Code = + "/* This source file is generated by 'makedefs'. Do not edit. */\n", + *Dont_Edit_Data = + "#\tThis data file is generated by 'makedefs'. Do not edit. \n"; + +static struct version_info version; + +/* definitions used for vision tables */ +#define TEST_WIDTH COLNO +#define TEST_HEIGHT ROWNO +#define BLOCK_WIDTH (TEST_WIDTH + 10) +#define BLOCK_HEIGHT TEST_HEIGHT /* don't need extra spaces */ +#define MAX_ROW (BLOCK_HEIGHT + TEST_HEIGHT) +#define MAX_COL (BLOCK_WIDTH + TEST_WIDTH) +/* Use this as an out-of-bound value in the close table. */ +#define CLOSE_OFF_TABLE_STRING "99" /* for the close table */ +#define FAR_OFF_TABLE_STRING "0xff" /* for the far table */ + +#define sign(z) ((z) < 0 ? -1 : ((z) ? 1 : 0)) +#ifdef VISION_TABLES +static char xclear[MAX_ROW][MAX_COL]; +#endif +/*-end of vision defs-*/ + +static char in_line[256], filename[60]; + +#ifdef FILE_PREFIX + /* if defined, a first argument not starting with - is + * taken as a text string to be prepended to any + * output filename generated */ +char *file_prefix=""; +#endif + +#ifdef MACsansMPWTOOL +int FDECL(main, (void)); +#else +int FDECL(main, (int,char **)); +#endif +void FDECL(do_makedefs, (char *)); +void NDECL(do_objs); +void NDECL(do_data); +void NDECL(do_dungeon); +void NDECL(do_date); +void NDECL(do_options); +void NDECL(do_monstr); +void NDECL(do_permonst); +void NDECL(do_questtxt); +void NDECL(do_rumors); +void NDECL(do_oracles); +void NDECL(do_vision); + +extern void NDECL(monst_init); /* monst.c */ +extern void NDECL(objects_init); /* objects.c */ + +static void NDECL(make_version); +static char *FDECL(version_string, (char *)); +static char *FDECL(version_id_string, (char *,const char *)); +static char *FDECL(xcrypt, (const char *)); +static int FDECL(check_control, (char *)); +static char *FDECL(without_control, (char *)); +static boolean FDECL(d_filter, (char *)); +static boolean FDECL(h_filter, (char *)); +static boolean FDECL(ranged_attk,(struct permonst*)); +static int FDECL(mstrength,(struct permonst *)); +static void NDECL(build_savebones_compat_string); + +static boolean FDECL(qt_comment, (char *)); +static boolean FDECL(qt_control, (char *)); +static int FDECL(get_hdr, (char *)); +static boolean FDECL(new_id, (char *)); +static boolean FDECL(known_msg, (int,int)); +static void FDECL(new_msg, (char *,int,int)); +static void FDECL(do_qt_control, (char *)); +static void FDECL(do_qt_text, (char *)); +static void NDECL(adjust_qt_hdrs); +static void NDECL(put_qt_hdrs); + +#ifdef VISION_TABLES +static void NDECL(H_close_gen); +static void NDECL(H_far_gen); +static void NDECL(C_close_gen); +static void NDECL(C_far_gen); +static int FDECL(clear_path, (int,int,int,int)); +#endif + +static char *FDECL(tmpdup, (const char *)); +static char *FDECL(limit, (char *,int)); +static char *FDECL(eos, (char *)); + +/* input, output, tmp */ +static FILE *ifp, *ofp, *tfp; + +#if defined(__BORLANDC__) && !defined(_WIN32) +extern unsigned _stklen = STKSIZ; +#endif + + +#ifdef MACsansMPWTOOL +int +main(void) +{ + const char *def_options = "odemvpqrhz"; + char buf[100]; + int len; + + printf("Enter options to run: [%s] ", def_options); + fflush(stdout); + fgets(buf, 100, stdin); + len = strlen(buf); + if (len <= 1) + Strcpy(buf, def_options); + else + buf[len-1] = 0; /* remove return */ + + do_makedefs(buf); + exit(EXIT_SUCCESS); + return 0; +} + +#else /* ! MAC */ + +int +main(argc, argv) +int argc; +char *argv[]; +{ + if ( (argc != 2) +#ifdef FILE_PREFIX + && (argc != 3) +#endif + ) { + Fprintf(stderr, "Bad arg count (%d).\n", argc-1); + (void) fflush(stderr); + return 1; + } + +#ifdef FILE_PREFIX + if(argc >=2 && argv[1][0]!='-'){ + file_prefix=argv[1]; + argc--;argv++; + } +#endif + do_makedefs(&argv[1][1]); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +#endif + +void +do_makedefs(options) +char *options; +{ + boolean more_than_one; + + /* Note: these initializers don't do anything except guarantee that + we're linked properly. + */ + monst_init(); + objects_init(); + + /* construct the current version number */ + make_version(); + + + more_than_one = strlen(options) > 1; + while (*options) { + if (more_than_one) + Fprintf(stderr, "makedefs -%c\n", *options); + + switch (*options) { + case 'o': + case 'O': do_objs(); + break; + case 'd': + case 'D': do_data(); + break; + case 'e': + case 'E': do_dungeon(); + break; + case 'm': + case 'M': do_monstr(); + break; + case 'v': + case 'V': do_date(); + do_options(); + break; + case 'p': + case 'P': do_permonst(); + break; + case 'q': + case 'Q': do_questtxt(); + break; + case 'r': + case 'R': do_rumors(); + break; + case 'h': + case 'H': do_oracles(); + break; + case 'z': + case 'Z': do_vision(); + break; + + default: Fprintf(stderr, "Unknown option '%c'.\n", + *options); + (void) fflush(stderr); + exit(EXIT_FAILURE); + + } + options++; + } + if (more_than_one) Fprintf(stderr, "Completed.\n"); /* feedback */ + +} + + +/* trivial text encryption routine which can't be broken with `tr' */ +static +char *xcrypt(str) +const char *str; +{ /* duplicated in src/hacklib.c */ + static char buf[BUFSZ]; + register const char *p; + register char *q; + register int bitmask; + + for (bitmask = 1, p = str, q = buf; *p; q++) { + *q = *p++; + if (*q & (32|64)) *q ^= bitmask; + if ((bitmask <<= 1) >= 32) bitmask = 1; + } + *q = '\0'; + return buf; +} + +void +do_rumors() +{ + char infile[60]; + long true_rumor_size; + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename,file_prefix); +#endif + Sprintf(eos(filename), DATA_TEMPLATE, RUMOR_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,Dont_Edit_Data); + + Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE); + Strcat(infile, ".tru"); + if (!(ifp = fopen(infile, RDTMODE))) { + perror(infile); + Fclose(ofp); + Unlink(filename); /* kill empty output file */ + exit(EXIT_FAILURE); + } + + /* get size of true rumors file */ +#ifndef VMS + (void) fseek(ifp, 0L, SEEK_END); + true_rumor_size = ftell(ifp); +#else + /* seek+tell is only valid for stream format files; since rumors.%%% + might be in record format, count the actual data bytes instead. + */ + true_rumor_size = 0; + while (fgets(in_line, sizeof in_line, ifp) != 0) + true_rumor_size += strlen(in_line); /* includes newline */ +#endif /* VMS */ + Fprintf(ofp,"%06lx\n", true_rumor_size); + (void) fseek(ifp, 0L, SEEK_SET); + + /* copy true rumors */ + while (fgets(in_line, sizeof in_line, ifp) != 0) + (void) fputs(xcrypt(in_line), ofp); + + Fclose(ifp); + + Sprintf(infile, DATA_IN_TEMPLATE, RUMOR_FILE); + Strcat(infile, ".fal"); + if (!(ifp = fopen(infile, RDTMODE))) { + perror(infile); + Fclose(ofp); + Unlink(filename); /* kill incomplete output file */ + exit(EXIT_FAILURE); + } + + /* copy false rumors */ + while (fgets(in_line, sizeof in_line, ifp) != 0) + (void) fputs(xcrypt(in_line), ofp); + + Fclose(ifp); + Fclose(ofp); + return; +} + +/* + * 3.4.1: way back in 3.2.1 `flags.nap' became unconditional but + * TIMED_DELAY was erroneously left in VERSION_FEATURES and has + * been there up through 3.4.0. Simply removing it now would + * break save file compatibility with 3.4.0 files, so we will + * explicitly mask it out during version checks. + * This should go away in the next version update. + */ +#define IGNORED_FEATURES ( 0L \ + | (1L << 23) /* TIMED_DELAY */ \ + ) + +static void +make_version() +{ + register int i; + + /* + * integer version number + */ + version.incarnation = ((unsigned long)VERSION_MAJOR << 24) | + ((unsigned long)VERSION_MINOR << 16) | + ((unsigned long)PATCHLEVEL << 8) | + ((unsigned long)EDITLEVEL); + /* + * encoded feature list + * Note: if any of these magic numbers are changed or reassigned, + * EDITLEVEL in patchlevel.h should be incremented at the same time. + * The actual values have no special meaning, and the category + * groupings are just for convenience. + */ + version.feature_set = (unsigned long)(0L + /* levels and/or topology (0..4) */ +#ifdef REINCARNATION + | (1L << 1) +#endif +#ifdef SINKS + | (1L << 2) +#endif + /* monsters (5..9) */ +#ifdef KOPS + | (1L << 6) +#endif +#ifdef MAIL + | (1L << 7) +#endif + /* objects (10..14) */ +#ifdef TOURIST + | (1L << 10) +#endif +#ifdef STEED + | (1L << 11) +#endif +#ifdef GOLDOBJ + | (1L << 12) +#endif + /* flag bits and/or other global variables (15..26) */ +#ifdef TEXTCOLOR + | (1L << 17) +#endif +#ifdef INSURANCE + | (1L << 18) +#endif +#ifdef ELBERETH + | (1L << 19) +#endif +#ifdef EXP_ON_BOTL + | (1L << 20) +#endif +#ifdef SCORE_ON_BOTL + | (1L << 21) +#endif + /* data format [COMPRESS excluded] (27..31) */ +#ifdef ZEROCOMP + | (1L << 27) +#endif +#ifdef RLECOMP + | (1L << 28) +#endif + ); + /* + * Value used for object & monster sanity check. + * (NROFARTIFACTS<<24) | (NUM_OBJECTS<<12) | (NUMMONS<<0) + */ + for (i = 1; artifact_names[i]; i++) continue; + version.entity_count = (unsigned long)(i - 1); + for (i = 1; objects[i].oc_class != ILLOBJ_CLASS; i++) continue; + version.entity_count = (version.entity_count << 12) | (unsigned long)i; + for (i = 0; mons[i].mlet; i++) continue; + version.entity_count = (version.entity_count << 12) | (unsigned long)i; + /* + * Value used for compiler (word size/field alignment/padding) check. + */ + version.struct_sizes = (((unsigned long)sizeof (struct flag) << 24) | + ((unsigned long)sizeof (struct obj) << 17) | + ((unsigned long)sizeof (struct monst) << 10) | + ((unsigned long)sizeof (struct you))); + return; +} + +static char * +version_string(outbuf) +char *outbuf; +{ + Sprintf(outbuf, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL); +#ifdef BETA + Sprintf(eos(outbuf), "-%d", EDITLEVEL); +#endif + return outbuf; +} + +static char * +version_id_string(outbuf, build_date) +char *outbuf; +const char *build_date; +{ + char subbuf[64], versbuf[64]; + + subbuf[0] = '\0'; +#ifdef PORT_SUB_ID + subbuf[0] = ' '; + Strcpy(&subbuf[1], PORT_SUB_ID); +#endif +#ifdef BETA + Strcat(subbuf, " Beta"); +#endif + + Sprintf(outbuf, "%s NetHack%s Version %s - last build %s.", + PORT_ID, subbuf, version_string(versbuf), build_date); + return outbuf; +} + +void +do_date() +{ + long clocktim = 0; + char *c, cbuf[60], buf[BUFSZ]; + const char *ul_sfx; + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename,file_prefix); +#endif + Sprintf(eos(filename), INCLUDE_TEMPLATE, DATE_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,"/*\tSCCS Id: @(#)date.h\t3.4\t2002/02/03 */\n\n"); + Fprintf(ofp,Dont_Edit_Code); + +#ifdef KR1ED + (void) time(&clocktim); + Strcpy(cbuf, ctime(&clocktim)); +#else + (void) time((time_t *)&clocktim); + Strcpy(cbuf, ctime((time_t *)&clocktim)); +#endif + for (c = cbuf; *c; c++) if (*c == '\n') break; + *c = '\0'; /* strip off the '\n' */ + Fprintf(ofp,"#define BUILD_DATE \"%s\"\n", cbuf); + Fprintf(ofp,"#define BUILD_TIME (%ldL)\n", clocktim); + Fprintf(ofp,"\n"); +#ifdef NHSTDC + ul_sfx = "UL"; +#else + ul_sfx = "L"; +#endif + Fprintf(ofp,"#define VERSION_NUMBER 0x%08lx%s\n", + version.incarnation, ul_sfx); + Fprintf(ofp,"#define VERSION_FEATURES 0x%08lx%s\n", + version.feature_set, ul_sfx); +#ifdef IGNORED_FEATURES + Fprintf(ofp,"#define IGNORED_FEATURES 0x%08lx%s\n", + (unsigned long) IGNORED_FEATURES, ul_sfx); +#endif + Fprintf(ofp,"#define VERSION_SANITY1 0x%08lx%s\n", + version.entity_count, ul_sfx); + Fprintf(ofp,"#define VERSION_SANITY2 0x%08lx%s\n", + version.struct_sizes, ul_sfx); + Fprintf(ofp,"\n"); + Fprintf(ofp,"#define VERSION_STRING \"%s\"\n", version_string(buf)); + Fprintf(ofp,"#define VERSION_ID \\\n \"%s\"\n", + version_id_string(buf, cbuf)); + Fprintf(ofp,"\n"); +#ifdef AMIGA + { + struct tm *tm = localtime((time_t *) &clocktim); + Fprintf(ofp,"#define AMIGA_VERSION_STRING "); + Fprintf(ofp,"\"\\0$VER: NetHack %d.%d.%d (%d.%d.%d)\"\n", + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL, + tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900); + } +#endif + Fclose(ofp); + return; +} + +static char save_bones_compat_buf[BUFSZ]; + +static void +build_savebones_compat_string() +{ +#ifdef VERSION_COMPATIBILITY + unsigned long uver = VERSION_COMPATIBILITY; +#endif + Strcpy(save_bones_compat_buf, + "save and bones files accepted from version"); +#ifdef VERSION_COMPATIBILITY + Sprintf(eos(save_bones_compat_buf), "s %lu.%lu.%lu through %d.%d.%d", + ((uver & 0xFF000000L) >> 24), ((uver & 0x00FF0000L) >> 16), + ((uver & 0x0000FF00L) >> 8), + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL); +#else + Sprintf(eos(save_bones_compat_buf), " %d.%d.%d only", + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL); +#endif +} + +static const char *build_opts[] = { +#ifdef AMIGA_WBENCH + "Amiga WorkBench support", +#endif +#ifdef ANSI_DEFAULT + "ANSI default terminal", +#endif +#ifdef AUTOPICKUP_EXCEPTIONS + "autopickup_exceptions", +#endif +#ifdef TEXTCOLOR + "color", +#endif +#ifdef COM_COMPL + "command line completion", +#endif +#ifdef COMPRESS + "data file compression", +#endif +#ifdef DLB + "data librarian", +#endif +#ifdef WIZARD + "debug mode", +#endif +#ifdef ELBERETH + "Elbereth", +#endif +#ifdef EXP_ON_BOTL + "experience points on status line", +#endif +#ifdef MFLOPPY + "floppy drive support", +#endif +#ifdef GOLDOBJ + "gold object in inventories", +#endif +#ifdef INSURANCE + "insurance files for recovering from crashes", +#endif +#ifdef KOPS + "Keystone Kops", +#endif +#ifdef HOLD_LOCKFILE_OPEN + "exclusive lock on level 0 file", +#endif +#ifdef LOGFILE + "log file", +#endif +#ifdef MAIL + "mail daemon", +#endif +#ifdef GNUDOS + "MSDOS protected mode", +#endif +#ifdef NEWS + "news file", +#endif +#ifdef OVERLAY +# ifdef MOVERLAY + "MOVE overlays", +# else +# ifdef VROOMM + "VROOMM overlays", +# else + "overlays", +# endif +# endif +#endif +#ifdef REDO + "redo command", +#endif +#ifdef REINCARNATION + "rogue level", +#endif +#ifdef STEED + "saddles and riding", +#endif +#ifdef SCORE_ON_BOTL + "score on status line", +#endif +#ifdef CLIPPING + "screen clipping", +#endif +#ifdef NO_TERMS +# ifdef MAC + "screen control via mactty", +# endif +# ifdef SCREEN_BIOS + "screen control via BIOS", +# endif +# ifdef SCREEN_DJGPPFAST + "screen control via DJGPP fast", +# endif +# ifdef SCREEN_VGA + "screen control via VGA graphics", +# endif +# ifndef MSWIN_GRAPHICS +# ifdef WIN32CON + "screen control via WIN32 console I/O", +# endif +# endif +#endif +#ifdef SEDUCE + "seduction", +#endif +#ifdef SHELL + "shell command", +#endif +#ifdef SINKS + "sinks", +#endif +#ifdef SUSPEND + "suspend command", +#endif +#ifdef TERMINFO + "terminal info library", +#else +# if defined(TERMLIB) || ((!defined(MICRO) && !defined(WIN32)) && defined(TTY_GRAPHICS)) + "terminal capability library", +# endif +#endif +#ifdef TIMED_DELAY + "timed wait for display effects", +#endif +#ifdef TOURIST + "tourists", +#endif +#ifdef USER_SOUNDS +# ifdef USER_SOUNDS_REGEX + "user sounds via regular expressions", +# else + "user sounds via pmatch", +# endif +#endif +#ifdef PREFIXES_IN_USE + "variable playground", +#endif +#ifdef VISION_TABLES + "vision tables", +#endif +#ifdef WALLIFIED_MAZE + "walled mazes", +#endif +#ifdef ZEROCOMP + "zero-compressed save files", +#endif + save_bones_compat_buf, + "basic NetHack features" + }; + +static const char *window_opts[] = { +#ifdef TTY_GRAPHICS + "traditional tty-based graphics", +#endif +#ifdef X11_GRAPHICS + "X11", +#endif +#ifdef QT_GRAPHICS + "Qt", +#endif +#ifdef GNOME_GRAPHICS + "Gnome", +#endif +#ifdef MAC + "Mac", +#endif +#ifdef AMIGA_INTUITION + "Amiga Intuition", +#endif +#ifdef GEM_GRAPHICS + "Gem", +#endif +#ifdef MSWIN_GRAPHICS + "mswin", +#endif +#ifdef BEOS_GRAPHICS + "BeOS InterfaceKit", +#endif + 0 + }; + +void +do_options() +{ + register int i, length; + register const char *str, *indent = " "; + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename,file_prefix); +#endif + Sprintf(eos(filename), DATA_TEMPLATE, OPTIONS_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + + build_savebones_compat_string(); + Fprintf(ofp, +#ifdef BETA + "\n NetHack version %d.%d.%d [beta]\n", +#else + "\n NetHack version %d.%d.%d\n", +#endif + VERSION_MAJOR, VERSION_MINOR, PATCHLEVEL); + + Fprintf(ofp,"\nOptions compiled into this edition:\n"); + + length = COLNO + 1; /* force 1st item onto new line */ + for (i = 0; i < SIZE(build_opts); i++) { + str = build_opts[i]; + if (length + strlen(str) > COLNO - 5) + Fprintf(ofp,"\n%s", indent), length = strlen(indent); + else + Fprintf(ofp," "), length++; + Fprintf(ofp,"%s", str), length += strlen(str); + Fprintf(ofp,(i < SIZE(build_opts) - 1) ? "," : "."), length++; + } + + Fprintf(ofp,"\n\nSupported windowing systems:\n"); + + length = COLNO + 1; /* force 1st item onto new line */ + for (i = 0; i < SIZE(window_opts) - 1; i++) { + str = window_opts[i]; + if (length + strlen(str) > COLNO - 5) + Fprintf(ofp,"\n%s", indent), length = strlen(indent); + else + Fprintf(ofp," "), length++; + Fprintf(ofp,"%s", str), length += strlen(str); + Fprintf(ofp, ","), length++; + } + Fprintf(ofp, "\n%swith a default of %s.", indent, DEFAULT_WINDOW_SYS); + Fprintf(ofp,"\n\n"); + + Fclose(ofp); + return; +} + +/* routine to decide whether to discard something from data.base */ +static boolean +d_filter(line) + char *line; +{ + if (*line == '#') return TRUE; /* ignore comment lines */ + return FALSE; +} + + /* + * + New format (v3.1) of 'data' file which allows much faster lookups [pr] +"do not edit" first record is a comment line +01234567 hexadecimal formatted offset to text area +name-a first name of interest +123,4 offset to name's text, and number of lines for it +name-b next name of interest +name-c multiple names which share same description also +456,7 share a single offset,count line +. sentinel to mark end of names +789,0 dummy record containing offset, count of EOF +text-a 4 lines of descriptive text for name-a +text-a at file position 0x01234567L + 123L +text-a +text-a +text-b/text-c 7 lines of text for names-b and -c +text-b/text-c at fseek(0x01234567L + 456L) +... + * + */ + +void +do_data() +{ + char infile[60], tempfile[60]; + boolean ok; + long txt_offset; + int entry_cnt, line_cnt; + + Sprintf(tempfile, DATA_TEMPLATE, "database.tmp"); + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename,file_prefix); +#endif + Sprintf(eos(filename), DATA_TEMPLATE, DATA_FILE); + Sprintf(infile, DATA_IN_TEMPLATE, DATA_FILE); + Strcat(infile, +#ifdef SHORT_FILENAMES + ".bas" +#else + ".base" +#endif + ); + if (!(ifp = fopen(infile, RDTMODE))) { /* data.base */ + perror(infile); + exit(EXIT_FAILURE); + } + if (!(ofp = fopen(filename, WRTMODE))) { /* data */ + perror(filename); + Fclose(ifp); + exit(EXIT_FAILURE); + } + if (!(tfp = fopen(tempfile, WRTMODE))) { /* database.tmp */ + perror(tempfile); + Fclose(ifp); + Fclose(ofp); + Unlink(filename); + exit(EXIT_FAILURE); + } + + /* output a dummy header record; we'll rewind and overwrite it later */ + Fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, 0L); + + entry_cnt = line_cnt = 0; + /* read through the input file and split it into two sections */ + while (fgets(in_line, sizeof in_line, ifp)) { + if (d_filter(in_line)) continue; + if (*in_line > ' ') { /* got an entry name */ + /* first finish previous entry */ + if (line_cnt) Fprintf(ofp, "%d\n", line_cnt), line_cnt = 0; + /* output the entry name */ + (void) fputs(in_line, ofp); + entry_cnt++; /* update number of entries */ + } else if (entry_cnt) { /* got some descriptive text */ + /* update previous entry with current text offset */ + if (!line_cnt) Fprintf(ofp, "%ld,", ftell(tfp)); + /* save the text line in the scratch file */ + (void) fputs(in_line, tfp); + line_cnt++; /* update line counter */ + } + } + /* output an end marker and then record the current position */ + if (line_cnt) Fprintf(ofp, "%d\n", line_cnt); + Fprintf(ofp, ".\n%ld,%d\n", ftell(tfp), 0); + txt_offset = ftell(ofp); + Fclose(ifp); /* all done with original input file */ + + /* reprocess the scratch file; 1st format an error msg, just in case */ + Sprintf(in_line, "rewind of \"%s\"", tempfile); + if (rewind(tfp) != 0) goto dead_data; + /* copy all lines of text from the scratch file into the output file */ + while (fgets(in_line, sizeof in_line, tfp)) + (void) fputs(in_line, ofp); + + /* finished with scratch file */ + Fclose(tfp); + Unlink(tempfile); /* remove it */ + + /* update the first record of the output file; prepare error msg 1st */ + Sprintf(in_line, "rewind of \"%s\"", filename); + ok = (rewind(ofp) == 0); + if (ok) { + Sprintf(in_line, "header rewrite of \"%s\"", filename); + ok = (fprintf(ofp, "%s%08lx\n", Dont_Edit_Data, txt_offset) >= 0); + } + if (!ok) { +dead_data: perror(in_line); /* report the problem */ + /* close and kill the aborted output file, then give up */ + Fclose(ofp); + Unlink(filename); + exit(EXIT_FAILURE); + } + + /* all done */ + Fclose(ofp); + + return; +} + +/* routine to decide whether to discard something from oracles.txt */ +static boolean +h_filter(line) + char *line; +{ + static boolean skip = FALSE; + char tag[sizeof in_line]; + + SpinCursor(3); + + if (*line == '#') return TRUE; /* ignore comment lines */ + if (sscanf(line, "----- %s", tag) == 1) { + skip = FALSE; +#ifndef SINKS + if (!strcmp(tag, "SINKS")) skip = TRUE; +#endif +#ifndef ELBERETH + if (!strcmp(tag, "ELBERETH")) skip = TRUE; +#endif + } else if (skip && !strncmp(line, "-----", 5)) + skip = FALSE; + return skip; +} + +static const char *special_oracle[] = { + "\"...it is rather disconcerting to be confronted with the", + "following theorem from [Baker, Gill, and Solovay, 1975].", + "", + "Theorem 7.18 There exist recursive languages A and B such that", + " (1) P(A) == NP(A), and", + " (2) P(B) != NP(B)", + "", + "This provides impressive evidence that the techniques that are", + "currently available will not suffice for proving that P != NP or ", + "that P == NP.\" [Garey and Johnson, p. 185.]" +}; + +/* + The oracle file consists of a "do not edit" comment, a decimal count N + and set of N+1 hexadecimal fseek offsets, followed by N multiple-line + records, separated by "---" lines. The first oracle is a special case. + The input data contains just those multi-line records, separated by + "-----" lines. + */ + +void +do_oracles() +{ + char infile[60], tempfile[60]; + boolean in_oracle, ok; + long txt_offset, offset, fpos; + int oracle_cnt; + register int i; + + Sprintf(tempfile, DATA_TEMPLATE, "oracles.tmp"); + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), DATA_TEMPLATE, ORACLE_FILE); + Sprintf(infile, DATA_IN_TEMPLATE, ORACLE_FILE); + Strcat(infile, ".txt"); + if (!(ifp = fopen(infile, RDTMODE))) { + perror(infile); + exit(EXIT_FAILURE); + } + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + Fclose(ifp); + exit(EXIT_FAILURE); + } + if (!(tfp = fopen(tempfile, WRTMODE))) { /* oracles.tmp */ + perror(tempfile); + Fclose(ifp); + Fclose(ofp); + Unlink(filename); + exit(EXIT_FAILURE); + } + + /* output a dummy header record; we'll rewind and overwrite it later */ + Fprintf(ofp, "%s%5d\n", Dont_Edit_Data, 0); + + /* handle special oracle; it must come first */ + (void) fputs("---\n", tfp); + Fprintf(ofp, "%05lx\n", ftell(tfp)); /* start pos of special oracle */ + for (i = 0; i < SIZE(special_oracle); i++) { + (void) fputs(xcrypt(special_oracle[i]), tfp); + (void) fputc('\n', tfp); + } + SpinCursor(3); + + oracle_cnt = 1; + (void) fputs("---\n", tfp); + Fprintf(ofp, "%05lx\n", ftell(tfp)); /* start pos of first oracle */ + in_oracle = FALSE; + + while (fgets(in_line, sizeof in_line, ifp)) { + SpinCursor(3); + + if (h_filter(in_line)) continue; + if (!strncmp(in_line, "-----", 5)) { + if (!in_oracle) continue; + in_oracle = FALSE; + oracle_cnt++; + (void) fputs("---\n", tfp); + Fprintf(ofp, "%05lx\n", ftell(tfp)); + /* start pos of this oracle */ + } else { + in_oracle = TRUE; + (void) fputs(xcrypt(in_line), tfp); + } + } + + if (in_oracle) { /* need to terminate last oracle */ + oracle_cnt++; + (void) fputs("---\n", tfp); + Fprintf(ofp, "%05lx\n", ftell(tfp)); /* eof position */ + } + + /* record the current position */ + txt_offset = ftell(ofp); + Fclose(ifp); /* all done with original input file */ + + /* reprocess the scratch file; 1st format an error msg, just in case */ + Sprintf(in_line, "rewind of \"%s\"", tempfile); + if (rewind(tfp) != 0) goto dead_data; + /* copy all lines of text from the scratch file into the output file */ + while (fgets(in_line, sizeof in_line, tfp)) + (void) fputs(in_line, ofp); + + /* finished with scratch file */ + Fclose(tfp); + Unlink(tempfile); /* remove it */ + + /* update the first record of the output file; prepare error msg 1st */ + Sprintf(in_line, "rewind of \"%s\"", filename); + ok = (rewind(ofp) == 0); + if (ok) { + Sprintf(in_line, "header rewrite of \"%s\"", filename); + ok = (fprintf(ofp, "%s%5d\n", Dont_Edit_Data, oracle_cnt) >=0); + } + if (ok) { + Sprintf(in_line, "data rewrite of \"%s\"", filename); + for (i = 0; i <= oracle_cnt; i++) { +#ifndef VMS /* alpha/vms v1.0; this fflush seems to confuse ftell */ + if (!(ok = (fflush(ofp) == 0))) break; +#endif + if (!(ok = (fpos = ftell(ofp)) >= 0)) break; + if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break; + if (!(ok = (fscanf(ofp, "%5lx", &offset) == 1))) break; +#ifdef MAC +# ifdef __MWERKS__ + /* + MetroWerks CodeWarrior Pro 1's (AKA CW12) version of MSL + (ANSI C Libraries) needs this rewind or else the fprintf + stops working. This may also be true for CW11, but has + never been checked. + */ + rewind(ofp); +# endif +#endif + if (!(ok = (fseek(ofp, fpos, SEEK_SET) >= 0))) break; + if (!(ok = (fprintf(ofp, "%05lx\n", offset + txt_offset) >= 0))) + break; + } + } + if (!ok) { +dead_data: perror(in_line); /* report the problem */ + /* close and kill the aborted output file, then give up */ + Fclose(ofp); + Unlink(filename); + exit(EXIT_FAILURE); + } + + /* all done */ + Fclose(ofp); + + return; +} + + +static struct deflist { + + const char *defname; + boolean true_or_false; +} deflist[] = { +#ifdef REINCARNATION + { "REINCARNATION", TRUE }, +#else + { "REINCARNATION", FALSE }, +#endif + { 0, 0 } }; + +static int +check_control(s) + char *s; +{ + int i; + + if(s[0] != '%') return(-1); + + for(i = 0; deflist[i].defname; i++) + if(!strncmp(deflist[i].defname, s+1, strlen(deflist[i].defname))) + return(i); + + return(-1); +} + +static char * +without_control(s) + char *s; +{ + return(s + 1 + strlen(deflist[check_control(in_line)].defname)); +} + +void +do_dungeon() +{ + int rcnt = 0; + + Sprintf(filename, DATA_IN_TEMPLATE, DGN_I_FILE); + if (!(ifp = fopen(filename, RDTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), DGN_TEMPLATE, DGN_O_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,Dont_Edit_Data); + + while (fgets(in_line, sizeof in_line, ifp) != 0) { + SpinCursor(3); + + rcnt++; + if(in_line[0] == '#') continue; /* discard comments */ +recheck: + if(in_line[0] == '%') { + int i = check_control(in_line); + if(i >= 0) { + if(!deflist[i].true_or_false) { + while (fgets(in_line, sizeof in_line, ifp) != 0) + if(check_control(in_line) != i) goto recheck; + } else + (void) fputs(without_control(in_line),ofp); + } else { + Fprintf(stderr, "Unknown control option '%s' in file %s at line %d.\n", + in_line, DGN_I_FILE, rcnt); + exit(EXIT_FAILURE); + } + } else + (void) fputs(in_line,ofp); + } + Fclose(ifp); + Fclose(ofp); + + return; +} + +static boolean +ranged_attk(ptr) /* returns TRUE if monster can attack at range */ + register struct permonst *ptr; +{ + register int i, j; + register int atk_mask = (1<mattk[i].aatyp) >= AT_WEAP || (atk_mask & (1<mlevel; + + if(tmp > 49) /* special fixed hp monster */ + tmp = 2*(tmp - 6) / 4; + +/* For creation in groups */ + n = (!!(ptr->geno & G_SGROUP)); + n += (!!(ptr->geno & G_LGROUP)) << 1; + +/* For ranged attacks */ + if (ranged_attk(ptr)) n++; + +/* For higher ac values */ + n += (ptr->ac < 4); + n += (ptr->ac < 0); + +/* For very fast monsters */ + n += (ptr->mmove >= 18); + +/* For each attack and "special" attack */ + for(i = 0; i < NATTK; i++) { + + tmp2 = ptr->mattk[i].aatyp; + n += (tmp2 > 0); + n += (tmp2 == AT_MAGC); + n += (tmp2 == AT_WEAP && (ptr->mflags2 & M2_STRONG)); + } + +/* For each "special" damage type */ + for(i = 0; i < NATTK; i++) { + + tmp2 = ptr->mattk[i].adtyp; + if ((tmp2 == AD_DRLI) || (tmp2 == AD_STON) || (tmp2 == AD_DRST) + || (tmp2 == AD_DRDX) || (tmp2 == AD_DRCO) || (tmp2 == AD_WERE)) + n += 2; + else if (strcmp(ptr->mname, "grid bug")) n += (tmp2 != AD_PHYS); + n += ((int) (ptr->mattk[i].damd * ptr->mattk[i].damn) > 23); + } + +/* Leprechauns are special cases. They have many hit dice so they + can hit and are hard to kill, but they don't really do much damage. */ + if (!strcmp(ptr->mname, "leprechaun")) n -= 2; + +/* Finally, adjust the monster level 0 <= n <= 24 (approx.) */ + if(n == 0) tmp--; + else if(n >= 6) tmp += ( n / 2 ); + else tmp += ( n / 3 + 1); + + return((tmp >= 0) ? tmp : 0); +} + +void +do_monstr() +{ + register struct permonst *ptr; + register int i, j; + + /* + * create the source file, "monstr.c" + */ + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), SOURCE_TEMPLATE, MON_STR_C); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,Dont_Edit_Code); + Fprintf(ofp,"#include \"config.h\"\n"); + Fprintf(ofp,"\nconst int monstr[] = {\n"); + for (ptr = &mons[0], j = 0; ptr->mlet; ptr++) { + + SpinCursor(3); + + i = mstrength(ptr); + Fprintf(ofp,"%2d,%c", i, (++j & 15) ? ' ' : '\n'); + } + /* might want to insert a final 0 entry here instead of just newline */ + Fprintf(ofp,"%s};\n", (j & 15) ? "\n" : ""); + + Fprintf(ofp,"\nvoid NDECL(monstr_init);\n"); + Fprintf(ofp,"\nvoid\n"); + Fprintf(ofp,"monstr_init()\n"); + Fprintf(ofp,"{\n"); + Fprintf(ofp," return;\n"); + Fprintf(ofp,"}\n"); + Fprintf(ofp,"\n/*monstr.c*/\n"); + + Fclose(ofp); + return; +} + +void +do_permonst() +{ + int i; + char *c, *nam; + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), INCLUDE_TEMPLATE, MONST_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,"/*\tSCCS Id: @(#)pm.h\t3.4\t2002/02/03 */\n\n"); + Fprintf(ofp,Dont_Edit_Code); + Fprintf(ofp,"#ifndef PM_H\n#define PM_H\n"); + + if (strcmp(mons[0].mname, "playermon") != 0) + Fprintf(ofp,"\n#define\tPM_PLAYERMON\t(-1)"); + + for (i = 0; mons[i].mlet; i++) { + SpinCursor(3); + + Fprintf(ofp,"\n#define\tPM_"); + if (mons[i].mlet == S_HUMAN && + !strncmp(mons[i].mname, "were", 4)) + Fprintf(ofp, "HUMAN_"); + for (nam = c = tmpdup(mons[i].mname); *c; c++) + if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A'); + else if (*c < 'A' || *c > 'Z') *c = '_'; + Fprintf(ofp,"%s\t%d", nam, i); + } + Fprintf(ofp,"\n\n#define\tNUMMONS\t%d\n", i); + Fprintf(ofp,"\n#endif /* PM_H */\n"); + Fclose(ofp); + return; +} + + +/* Start of Quest text file processing. */ +#include "qtext.h" + +static struct qthdr qt_hdr; +static struct msghdr msg_hdr[N_HDR]; +static struct qtmsg *curr_msg; + +static int qt_line; + +static boolean in_msg; +#define NO_MSG 1 /* strlen of a null line returned by fgets() */ + +static boolean +qt_comment(s) + char *s; +{ + if(s[0] == '#') return(TRUE); + return((boolean)(!in_msg && strlen(s) == NO_MSG)); +} + +static boolean +qt_control(s) + char *s; +{ + return((boolean)(s[0] == '%' && (s[1] == 'C' || s[1] == 'E'))); +} + +static int +get_hdr (code) + char *code; +{ + int i; + + for(i = 0; i < qt_hdr.n_hdr; i++) + if(!strncmp(code, qt_hdr.id[i], LEN_HDR)) return (++i); + + return(0); +} + +static boolean +new_id (code) + char *code; +{ + if(qt_hdr.n_hdr >= N_HDR) { + Fprintf(stderr, OUT_OF_HEADERS, qt_line); + return(FALSE); + } + + strncpy(&qt_hdr.id[qt_hdr.n_hdr][0], code, LEN_HDR); + msg_hdr[qt_hdr.n_hdr].n_msg = 0; + qt_hdr.offset[qt_hdr.n_hdr++] = 0L; + return(TRUE); +} + +static boolean +known_msg(num, id) + int num, id; +{ + int i; + + for(i = 0; i < msg_hdr[num].n_msg; i++) + if(msg_hdr[num].qt_msg[i].msgnum == id) return(TRUE); + + return(FALSE); +} + + +static void +new_msg(s, num, id) + char *s; + int num, id; +{ + struct qtmsg *qt_msg; + + if(msg_hdr[num].n_msg >= N_MSG) { + Fprintf(stderr, OUT_OF_MESSAGES, qt_line); + } else { + qt_msg = &(msg_hdr[num].qt_msg[msg_hdr[num].n_msg++]); + qt_msg->msgnum = id; + qt_msg->delivery = s[2]; + qt_msg->offset = qt_msg->size = 0L; + + curr_msg = qt_msg; + } +} + +static void +do_qt_control(s) + char *s; +{ + char code[BUFSZ]; + int num, id = 0; + + switch(s[1]) { + + case 'C': if(in_msg) { + Fprintf(stderr, CREC_IN_MSG, qt_line); + break; + } else { + in_msg = TRUE; + if (sscanf(&s[4], "%s %5d", code, &id) != 2) { + Fprintf(stderr, UNREC_CREC, qt_line); + break; + } + num = get_hdr(code); + if (!num && !new_id(code)) + break; + num = get_hdr(code)-1; + if(known_msg(num, id)) + Fprintf(stderr, DUP_MSG, qt_line); + else new_msg(s, num, id); + } + break; + + case 'E': if(!in_msg) { + Fprintf(stderr, END_NOT_IN_MSG, qt_line); + break; + } else in_msg = FALSE; + break; + + default: Fprintf(stderr, UNREC_CREC, qt_line); + break; + } +} + +static void +do_qt_text(s) + char *s; +{ + if (!in_msg) { + Fprintf(stderr, TEXT_NOT_IN_MSG, qt_line); + } + curr_msg->size += strlen(s); + return; +} + +static void +adjust_qt_hdrs() +{ + int i, j; + long count = 0L, hdr_offset = sizeof(int) + + (sizeof(char)*LEN_HDR + sizeof(long)) * qt_hdr.n_hdr; + + for(i = 0; i < qt_hdr.n_hdr; i++) { + qt_hdr.offset[i] = hdr_offset; + hdr_offset += sizeof(int) + sizeof(struct qtmsg) * msg_hdr[i].n_msg; + } + + for(i = 0; i < qt_hdr.n_hdr; i++) + for(j = 0; j < msg_hdr[i].n_msg; j++) { + + msg_hdr[i].qt_msg[j].offset = hdr_offset + count; + count += msg_hdr[i].qt_msg[j].size; + } + return; +} + +static void +put_qt_hdrs() +{ + int i; + + /* + * The main header record. + */ +#ifdef DEBUG + Fprintf(stderr, "%ld: header info.\n", ftell(ofp)); +#endif + (void) fwrite((genericptr_t)&(qt_hdr.n_hdr), sizeof(int), 1, ofp); + (void) fwrite((genericptr_t)&(qt_hdr.id[0][0]), sizeof(char)*LEN_HDR, + qt_hdr.n_hdr, ofp); + (void) fwrite((genericptr_t)&(qt_hdr.offset[0]), sizeof(long), + qt_hdr.n_hdr, ofp); +#ifdef DEBUG + for(i = 0; i < qt_hdr.n_hdr; i++) + Fprintf(stderr, "%c @ %ld, ", qt_hdr.id[i], qt_hdr.offset[i]); + + Fprintf(stderr, "\n"); +#endif + + /* + * The individual class headers. + */ + for(i = 0; i < qt_hdr.n_hdr; i++) { + +#ifdef DEBUG + Fprintf(stderr, "%ld: %c header info.\n", ftell(ofp), + qt_hdr.id[i]); +#endif + (void) fwrite((genericptr_t)&(msg_hdr[i].n_msg), sizeof(int), + 1, ofp); + (void) fwrite((genericptr_t)&(msg_hdr[i].qt_msg[0]), + sizeof(struct qtmsg), msg_hdr[i].n_msg, ofp); +#ifdef DEBUG + { int j; + for(j = 0; j < msg_hdr[i].n_msg; j++) + Fprintf(stderr, "msg %d @ %ld (%ld)\n", + msg_hdr[i].qt_msg[j].msgnum, + msg_hdr[i].qt_msg[j].offset, + msg_hdr[i].qt_msg[j].size); + } +#endif + } +} + +void +do_questtxt() +{ + Sprintf(filename, DATA_IN_TEMPLATE, QTXT_I_FILE); + if(!(ifp = fopen(filename, RDTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), DATA_TEMPLATE, QTXT_O_FILE); + if(!(ofp = fopen(filename, WRBMODE))) { + perror(filename); + Fclose(ifp); + exit(EXIT_FAILURE); + } + + qt_hdr.n_hdr = 0; + qt_line = 0; + in_msg = FALSE; + + while (fgets(in_line, 80, ifp) != 0) { + SpinCursor (3); + + qt_line++; + if(qt_control(in_line)) do_qt_control(in_line); + else if(qt_comment(in_line)) continue; + else do_qt_text(in_line); + } + + (void) rewind(ifp); + in_msg = FALSE; + adjust_qt_hdrs(); + put_qt_hdrs(); + while (fgets(in_line, 80, ifp) != 0) { + + if(qt_control(in_line)) { + in_msg = (in_line[1] == 'C'); + continue; + } else if(qt_comment(in_line)) continue; +#ifdef DEBUG + Fprintf(stderr, "%ld: %s", ftell(stdout), in_line); +#endif + (void) fputs(xcrypt(in_line), ofp); + } + Fclose(ifp); + Fclose(ofp); + return; +} + + +static char temp[32]; + +static char * +limit(name,pref) /* limit a name to 30 characters length */ +char *name; +int pref; +{ + (void) strncpy(temp, name, pref ? 26 : 30); + temp[pref ? 26 : 30] = 0; + return temp; +} + +void +do_objs() +{ + int i, sum = 0; + char *c, *objnam; + int nspell = 0; + int prefix = 0; + char class = '\0'; + boolean sumerr = FALSE; + + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(eos(filename), INCLUDE_TEMPLATE, ONAME_FILE); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,"/*\tSCCS Id: @(#)onames.h\t3.4\t2002/02/03 */\n\n"); + Fprintf(ofp,Dont_Edit_Code); + Fprintf(ofp,"#ifndef ONAMES_H\n#define ONAMES_H\n\n"); + + for(i = 0; !i || objects[i].oc_class != ILLOBJ_CLASS; i++) { + SpinCursor(3); + + objects[i].oc_name_idx = objects[i].oc_descr_idx = i; /* init */ + if (!(objnam = tmpdup(OBJ_NAME(objects[i])))) continue; + + /* make sure probabilities add up to 1000 */ + if(objects[i].oc_class != class) { + if (sum && sum != 1000) { + Fprintf(stderr, "prob error for class %d (%d%%)", + class, sum); + (void) fflush(stderr); + sumerr = TRUE; + } + class = objects[i].oc_class; + sum = 0; + } + + for (c = objnam; *c; c++) + if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A'); + else if (*c < 'A' || *c > 'Z') *c = '_'; + + switch (class) { + case WAND_CLASS: + Fprintf(ofp,"#define\tWAN_"); prefix = 1; break; + case RING_CLASS: + Fprintf(ofp,"#define\tRIN_"); prefix = 1; break; + case POTION_CLASS: + Fprintf(ofp,"#define\tPOT_"); prefix = 1; break; + case SPBOOK_CLASS: + Fprintf(ofp,"#define\tSPE_"); prefix = 1; nspell++; break; + case SCROLL_CLASS: + Fprintf(ofp,"#define\tSCR_"); prefix = 1; break; + case AMULET_CLASS: + /* avoid trouble with stupid C preprocessors */ + Fprintf(ofp,"#define\t"); + if(objects[i].oc_material == PLASTIC) { + Fprintf(ofp,"FAKE_AMULET_OF_YENDOR\t%d\n", i); + prefix = -1; + break; + } + break; + case GEM_CLASS: + /* avoid trouble with stupid C preprocessors */ + if(objects[i].oc_material == GLASS) { + Fprintf(ofp,"/* #define\t%s\t%d */\n", + objnam, i); + prefix = -1; + break; + } + default: + Fprintf(ofp,"#define\t"); + } + if (prefix >= 0) + Fprintf(ofp,"%s\t%d\n", limit(objnam, prefix), i); + prefix = 0; + + sum += objects[i].oc_prob; + } + + /* check last set of probabilities */ + if (sum && sum != 1000) { + Fprintf(stderr, "prob error for class %d (%d%%)", class, sum); + (void) fflush(stderr); + sumerr = TRUE; + } + + Fprintf(ofp,"#define\tLAST_GEM\t(JADE)\n"); + Fprintf(ofp,"#define\tMAXSPELL\t%d\n", nspell+1); + Fprintf(ofp,"#define\tNUM_OBJECTS\t%d\n", i); + + Fprintf(ofp, "\n/* Artifacts (unique objects) */\n\n"); + + for (i = 1; artifact_names[i]; i++) { + SpinCursor(3); + + for (c = objnam = tmpdup(artifact_names[i]); *c; c++) + if (*c >= 'a' && *c <= 'z') *c -= (char)('a' - 'A'); + else if (*c < 'A' || *c > 'Z') *c = '_'; + + if (!strncmp(objnam, "THE_", 4)) + objnam += 4; +#ifdef TOURIST + /* fudge _platinum_ YENDORIAN EXPRESS CARD */ + if (!strncmp(objnam, "PLATINUM_", 9)) + objnam += 9; +#endif + Fprintf(ofp,"#define\tART_%s\t%d\n", limit(objnam, 1), i); + } + + Fprintf(ofp, "#define\tNROFARTIFACTS\t%d\n", i-1); + Fprintf(ofp,"\n#endif /* ONAMES_H */\n"); + Fclose(ofp); + if (sumerr) exit(EXIT_FAILURE); + return; +} + +static char * +tmpdup(str) +const char *str; +{ + static char buf[128]; + + if (!str) return (char *)0; + (void)strncpy(buf, str, 127); + return buf; +} + +static char * +eos(str) +char *str; +{ + while (*str) str++; + return str; +} + +/* + * macro used to control vision algorithms: + * VISION_TABLES => generate tables + */ + +void +do_vision() +{ +#ifdef VISION_TABLES + int i, j; + + /* Everything is clear. xclear may be malloc'ed. + * Block the upper left corner (BLOCK_HEIGHTxBLOCK_WIDTH) + */ + for (i = 0; i < MAX_ROW; i++) + for (j = 0; j < MAX_COL; j++) + if (i < BLOCK_HEIGHT && j < BLOCK_WIDTH) + xclear[i][j] = '\000'; + else + xclear[i][j] = '\001'; +#endif /* VISION_TABLES */ + + SpinCursor(3); + + /* + * create the include file, "vis_tab.h" + */ + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,Dont_Edit_Code); + Fprintf(ofp,"#ifdef VISION_TABLES\n"); +#ifdef VISION_TABLES + H_close_gen(); + H_far_gen(); +#endif /* VISION_TABLES */ + Fprintf(ofp,"\n#endif /* VISION_TABLES */\n"); + Fclose(ofp); + + SpinCursor(3); + + /* + * create the source file, "vis_tab.c" + */ + filename[0]='\0'; +#ifdef FILE_PREFIX + Strcat(filename, file_prefix); +#endif + Sprintf(filename, SOURCE_TEMPLATE, VIS_TAB_C); + if (!(ofp = fopen(filename, WRTMODE))) { + perror(filename); + Sprintf(filename, INCLUDE_TEMPLATE, VIS_TAB_H); + Unlink(filename); + exit(EXIT_FAILURE); + } + Fprintf(ofp,Dont_Edit_Code); + Fprintf(ofp,"#include \"config.h\"\n"); + Fprintf(ofp,"#ifdef VISION_TABLES\n"); + Fprintf(ofp,"#include \"vis_tab.h\"\n"); + + SpinCursor(3); + +#ifdef VISION_TABLES + C_close_gen(); + C_far_gen(); + Fprintf(ofp,"\nvoid vis_tab_init() { return; }\n"); +#endif /* VISION_TABLES */ + + SpinCursor(3); + + Fprintf(ofp,"\n#endif /* VISION_TABLES */\n"); + Fprintf(ofp,"\n/*vis_tab.c*/\n"); + + Fclose(ofp); + return; +} + +#ifdef VISION_TABLES + +/*-------------- vision tables --------------*\ + * + * Generate the close and far tables. This is done by setting up a + * fake dungeon and moving our source to different positions relative + * to a block and finding the first/last visible position. The fake + * dungeon is all clear execpt for the upper left corner (BLOCK_HEIGHT + * by BLOCK_WIDTH) is blocked. Then we move the source around relative + * to the corner of the block. For each new position of the source + * we check positions on rows "kittycorner" from the source. We check + * positions until they are either in sight or out of sight (depends on + * which table we are generating). The picture below shows the setup + * for the generation of the close table. The generation of the far + * table would switch the quadrants of the '@' and the "Check rows + * here". + * + * + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,, Check rows here ,,,,,,,,,,,, + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXB,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, + * ............................... + * ............................... + * .........@..................... + * ............................... + * + * Table generation figure (close_table). The 'X's are blocked points. + * The 'B' is a special blocked point. The '@' is the source. The ','s + * are the target area. The '.' are just open areas. + * + * + * Example usage of close_table[][][]. + * + * The table is as follows: + * + * dy = |row of '@' - row of 'B'| - 1 + * dx = |col of '@' - col of 'B'| + * + * The first indices are the deltas from the source '@' and the block 'B'. + * You must check for the value inside the abs value bars being zero. If + * so then the block is on the same row and you don't need to do a table + * lookup. The last value: + * + * dcy = |row of block - row to be checked| + * + * Is the value of the first visible spot on the check row from the + * block column. So + * + * first visible col = close_table[dy][dx][dcy] + col of 'B' + * +\*-------------- vision tables --------------*/ + +static void +H_close_gen() +{ + Fprintf(ofp,"\n/* Close */\n"); + Fprintf(ofp,"#define CLOSE_MAX_SB_DY %2d\t/* |src row - block row| - 1\t*/\n", + TEST_HEIGHT-1); + Fprintf(ofp,"#define CLOSE_MAX_SB_DX %2d\t/* |src col - block col|\t*/\n", + TEST_WIDTH); + Fprintf(ofp,"#define CLOSE_MAX_BC_DY %2d\t/* |block row - check row|\t*/\n", + TEST_HEIGHT); + Fprintf(ofp,"typedef struct {\n"); + Fprintf(ofp," unsigned char close[CLOSE_MAX_SB_DX][CLOSE_MAX_BC_DY];\n"); + Fprintf(ofp,"} close2d;\n"); + Fprintf(ofp,"extern close2d close_table[CLOSE_MAX_SB_DY];\n"); + return; +} + +static void +H_far_gen() +{ + Fprintf(ofp,"\n/* Far */\n"); + Fprintf(ofp,"#define FAR_MAX_SB_DY %2d\t/* |src row - block row|\t*/\n", + TEST_HEIGHT); + Fprintf(ofp,"#define FAR_MAX_SB_DX %2d\t/* |src col - block col| - 1\t*/\n", + TEST_WIDTH-1); + Fprintf(ofp,"#define FAR_MAX_BC_DY %2d\t/* |block row - check row| - 1\t*/\n", + TEST_HEIGHT-1); + Fprintf(ofp,"typedef struct {\n"); + Fprintf(ofp," unsigned char far_q[FAR_MAX_SB_DX][FAR_MAX_BC_DY];\n"); + Fprintf(ofp,"} far2d;\n"); + Fprintf(ofp,"extern far2d far_table[FAR_MAX_SB_DY];\n"); + return; +} + +static void +C_close_gen() +{ + int i,dx,dy; + int src_row, src_col; /* source */ + int block_row, block_col; /* block */ + int this_row; + int no_more; + const char *delim; + + block_row = BLOCK_HEIGHT-1; + block_col = BLOCK_WIDTH-1; + + Fprintf(ofp,"\n#ifndef FAR_TABLE_ONLY\n"); + Fprintf(ofp,"\nclose2d close_table[CLOSE_MAX_SB_DY] = {\n"); +#ifndef no_vision_progress + Fprintf(stderr,"\nclose:"); +#endif + + for (dy = 1; dy < TEST_HEIGHT; dy++) { + src_row = block_row + dy; + Fprintf(ofp, "/* DY = %2d (- 1)*/\n {{\n", dy); +#ifndef no_vision_progress + Fprintf(stderr," %2d",dy), (void)fflush(stderr); +#endif + for (dx = 0; dx < TEST_WIDTH; dx++) { + src_col = block_col - dx; + Fprintf(ofp, " /*%2d*/ {", dx); + + no_more = 0; + for (this_row = 0; this_row < TEST_HEIGHT; this_row++) { + delim = (this_row < TEST_HEIGHT - 1) ? "," : ""; + if (no_more) { + Fprintf(ofp, "%s%s", CLOSE_OFF_TABLE_STRING, delim); + continue; + } + SpinCursor(3); + + /* Find the first column that we can see. */ + for (i = block_col+1; i < MAX_COL; i++) { + if (clear_path(src_row,src_col,block_row-this_row,i)) + break; + } + + if (i == MAX_COL) no_more = 1; + Fprintf(ofp, "%2d%s", i - block_col, delim); + } + Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n"); + } + Fprintf(ofp," }},\n"); + } + + Fprintf(ofp,"}; /* close_table[] */\n"); /* closing brace for table */ + Fprintf(ofp,"#endif /* !FAR_TABLE_ONLY */\n"); +#ifndef no_vision_progress + Fprintf(stderr,"\n"); +#endif + return; +} + +static void +C_far_gen() +{ + int i,dx,dy; + int src_row, src_col; /* source */ + int block_row, block_col; /* block */ + int this_row; + const char *delim; + + block_row = BLOCK_HEIGHT-1; + block_col = BLOCK_WIDTH-1; + + Fprintf(ofp,"\n#ifndef CLOSE_TABLE_ONLY\n"); + Fprintf(ofp,"\nfar2d far_table[FAR_MAX_SB_DY] = {\n"); +#ifndef no_vision_progress + Fprintf(stderr,"\n_far_:"); +#endif + + for (dy = 0; dy < TEST_HEIGHT; dy++) { + src_row = block_row - dy; + Fprintf(ofp, "/* DY = %2d */\n {{\n", dy); +#ifndef no_vision_progress + Fprintf(stderr," %2d",dy), (void)fflush(stderr); +#endif + for (dx = 1; dx < TEST_WIDTH; dx++) { + src_col = block_col + dx; + Fprintf(ofp, " /*%2d(-1)*/ {", dx); + + for (this_row = block_row+1; this_row < block_row+TEST_HEIGHT; + this_row++) { + delim = (this_row < block_row + TEST_HEIGHT - 1) ? "," : ""; + + SpinCursor(3); + /* Find first col that we can see. */ + for (i = 0; i <= block_col; i++) { + if (clear_path(src_row,src_col,this_row,i)) break; + } + + if (block_col-i < 0) + Fprintf(ofp, "%s%s", FAR_OFF_TABLE_STRING, delim); + else + Fprintf(ofp, "%2d%s", block_col - i, delim); + } + Fprintf(ofp, "}%s", (dx < TEST_WIDTH - 1) ? ",\n" : "\n"); + } + Fprintf(ofp," }},\n"); + } + + Fprintf(ofp,"}; /* far_table[] */\n"); /* closing brace for table */ + Fprintf(ofp,"#endif /* !CLOSE_TABLE_ONLY */\n"); +#ifndef no_vision_progress + Fprintf(stderr,"\n"); +#endif + return; +} + +/* + * "Draw" a line from the hero to the given location. Stop if we hit a + * wall. + * + * Generalized integer Bresenham's algorithm (fast line drawing) for + * all quadrants. From _Procedural Elements for Computer Graphics_, by + * David F. Rogers. McGraw-Hill, 1985. + * + * I have tried a little bit of optimization by pulling compares out of + * the inner loops. + * + * NOTE: This had better *not* be called from a position on the + * same row as the hero. + */ +static int +clear_path(you_row,you_col,y2,x2) + int you_row, you_col, y2, x2; +{ + int dx, dy, s1, s2; + register int i, error, x, y, dxs, dys; + + x = you_col; y = you_row; + dx = abs(x2-you_col); dy = abs(y2-you_row); + s1 = sign(x2-you_col); s2 = sign(y2-you_row); + + if (s1 == 0) { /* same column */ + if (s2 == 1) { /* below (larger y2 value) */ + for (i = you_row+1; i < y2; i++) + if (!xclear[i][you_col]) return 0; + } else { /* above (smaller y2 value) */ + for (i = y2+1; i < you_row; i++) + if (!xclear[i][you_col]) return 0; + } + return 1; + } + + /* + * Lines at 0 and 90 degrees have been weeded out. + */ + if (dy > dx) { + error = dx; dx = dy; dy = error; /* swap the values */ + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + error = dys - dx; /* NOTE: error is used as a temporary above */ + + for (i = 0; i < dx; i++) { + if (!xclear[y][x]) return 0; /* plot point */ + + while (error >= 0) { + x += s1; + error -= dxs; + } + y += s2; + error += dys; + } + } else { + dxs = dx << 1; /* save the shifted values */ + dys = dy << 1; + error = dys - dx; + + for (i = 0; i < dx; i++) { + if (!xclear[y][x]) return 0; /* plot point */ + + while (error >= 0) { + y += s2; + error -= dxs; + } + x += s1; + error += dys; + } + } + return 1; +} +#endif /* VISION_TABLES */ + +#ifdef STRICT_REF_DEF +NEARDATA struct flag flags; +# ifdef ATTRIB_H +struct attribs attrmax, attrmin; +# endif +#endif /* STRICT_REF_DEF */ + +/*makedefs.c*/ diff --git a/util/panic.c b/util/panic.c new file mode 100644 index 0000000..5a5c41d --- /dev/null +++ b/util/panic.c @@ -0,0 +1,61 @@ +/* SCCS Id: @(#)panic.c 3.4 1994/03/02 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This code was adapted from the code in end.c to run in a standalone + * mode for the makedefs / drg code. + */ + +#define NEED_VARARGS +#include "config.h" + +#ifdef AZTEC +#define abort() exit() +#endif +#ifdef VMS +extern void NDECL(vms_abort); +#endif + +/*VARARGS1*/ +boolean panicking; +void VDECL(panic, (char *,...)); + +void +panic VA_DECL(char *,str) + VA_START(str); + VA_INIT(str, char *); + if(panicking++) +#ifdef SYSV + (void) +#endif + abort(); /* avoid loops - this should never happen*/ + + (void) fputs(" ERROR: ", stderr); + Vfprintf(stderr, str, VA_ARGS); + (void) fflush(stderr); +#if defined(UNIX) || defined(VMS) +# ifdef SYSV + (void) +# endif + abort(); /* generate core dump */ +#endif + VA_END(); + exit(EXIT_FAILURE); /* redundant */ + return; +} + +#ifdef ALLOCA_HACK +/* + * In case bison-generated foo_yacc.c tries to use alloca(); if we don't + * have it then just use malloc() instead. This may not work on some + * systems, but they should either use yacc or get a real alloca routine. + */ +long *alloca(cnt) +unsigned cnt; +{ + return cnt ? alloc(cnt) : (long *)0; +} +#endif + +/*panic.c*/ diff --git a/util/recover.c b/util/recover.c new file mode 100644 index 0000000..cc1b819 --- /dev/null +++ b/util/recover.c @@ -0,0 +1,399 @@ +/* SCCS Id: @(#)recover.c 3.4 1999/10/23 */ +/* Copyright (c) Janet Walz, 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Utility for reconstructing NetHack save file from a set of individual + * level files. Requires that the `checkpoint' option be enabled at the + * time NetHack creates those level files. + */ +#include "config.h" +#if !defined(O_WRONLY) && !defined(LSC) && !defined(AZTEC_C) +#include +#endif +#ifdef WIN32 +#include +#include "win32api.h" +#endif + +#ifdef VMS +extern int FDECL(vms_creat, (const char *,unsigned)); +extern int FDECL(vms_open, (const char *,int,unsigned)); +#endif /* VMS */ + +int FDECL(restore_savefile, (char *)); +void FDECL(set_levelfile_name, (int)); +int FDECL(open_levelfile, (int)); +int NDECL(create_savefile); +void FDECL(copy_bytes, (int,int)); + +#ifndef WIN_CE +#define Fprintf (void)fprintf +#else +#define Fprintf (void)nhce_message +static void nhce_message(FILE*, const char*, ...); +#endif + +#define Close (void)close + +#ifdef UNIX +#define SAVESIZE (PL_NSIZ + 13) /* save/99999player.e */ +#else +# ifdef VMS +#define SAVESIZE (PL_NSIZ + 22) /* [.save]player.e;1 */ +# else +# ifdef WIN32 +#define SAVESIZE (PL_NSIZ + 40) /* username-player.NetHack-saved-game */ +# else +#define SAVESIZE FILENAME /* from macconf.h or pcconf.h */ +# endif +# endif +#endif + +#if defined(EXEPATH) +char *FDECL(exepath, (char *)); +#endif + +#if defined(__BORLANDC__) && !defined(_WIN32) +extern unsigned _stklen = STKSIZ; +#endif +char savename[SAVESIZE]; /* holds relative path of save file from playground */ + + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int argno; + const char *dir = (char *)0; +#ifdef AMIGA + char *startdir = (char *)0; +#endif + + + if (!dir) dir = getenv("NETHACKDIR"); + if (!dir) dir = getenv("HACKDIR"); +#if defined(EXEPATH) + if (!dir) dir = exepath(argv[0]); +#endif + if (argc == 1 || (argc == 2 && !strcmp(argv[1], "-"))) { + Fprintf(stderr, + "Usage: %s [ -d directory ] base1 [ base2 ... ]\n", argv[0]); +#if defined(WIN32) || defined(MSDOS) + if (dir) { + Fprintf(stderr, "\t(Unless you override it with -d, recover will look \n"); + Fprintf(stderr, "\t in the %s directory on your system)\n", dir); + } +#endif + exit(EXIT_FAILURE); + } + + argno = 1; + if (!strncmp(argv[argno], "-d", 2)) { + dir = argv[argno]+2; + if (*dir == '=' || *dir == ':') dir++; + if (!*dir && argc > argno) { + argno++; + dir = argv[argno]; + } + if (!*dir) { + Fprintf(stderr, + "%s: flag -d must be followed by a directory name.\n", + argv[0]); + exit(EXIT_FAILURE); + } + argno++; + } +#if defined(SECURE) && !defined(VMS) + if (dir +# ifdef HACKDIR + && strcmp(dir, HACKDIR) +# endif + ) { + (void) setgid(getgid()); + (void) setuid(getuid()); + } +#endif /* SECURE && !VMS */ + +#ifdef HACKDIR + if (!dir) dir = HACKDIR; +#endif + +#ifdef AMIGA + startdir = getcwd(0,255); +#endif + if (dir && chdir((char *) dir) < 0) { + Fprintf(stderr, "%s: cannot chdir to %s.\n", argv[0], dir); + exit(EXIT_FAILURE); + } + + while (argc > argno) { + if (restore_savefile(argv[argno]) == 0) + Fprintf(stderr, "recovered \"%s\" to %s\n", + argv[argno], savename); + argno++; + } +#ifdef AMIGA + if (startdir) (void)chdir(startdir); +#endif + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +static char lock[256]; + +void +set_levelfile_name(lev) +int lev; +{ + char *tf; + + tf = rindex(lock, '.'); + if (!tf) tf = lock + strlen(lock); + (void) sprintf(tf, ".%d", lev); +#ifdef VMS + (void) strcat(tf, ";1"); +#endif +} + +int +open_levelfile(lev) +int lev; +{ + int fd; + + set_levelfile_name(lev); +#if defined(MICRO) || defined(WIN32) || defined(MSDOS) + fd = open(lock, O_RDONLY | O_BINARY); +#else + fd = open(lock, O_RDONLY, 0); +#endif + return fd; +} + +int +create_savefile() +{ + int fd; + +#if defined(MICRO) || defined(WIN32) || defined(MSDOS) + fd = open(savename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK); +#else + fd = creat(savename, FCMASK); +#endif + return fd; +} + +void +copy_bytes(ifd, ofd) +int ifd, ofd; +{ + char buf[BUFSIZ]; + int nfrom, nto; + + do { + nfrom = read(ifd, buf, BUFSIZ); + nto = write(ofd, buf, nfrom); + if (nto != nfrom) { + Fprintf(stderr, "file copy failed!\n"); + exit(EXIT_FAILURE); + } + } while (nfrom == BUFSIZ); +} + +int +restore_savefile(basename) +char *basename; +{ + int gfd, lfd, sfd; + int lev, savelev, hpid; + xchar levc; + struct version_info version_data; + + /* level 0 file contains: + * pid of creating process (ignored here) + * level number for current level of save file + * name of save file nethack would have created + * and game state + */ + (void) strcpy(lock, basename); + gfd = open_levelfile(0); + if (gfd < 0) { +#if defined(WIN32) && !defined(WIN_CE) + if(errno == EACCES) { + Fprintf(stderr, + "\nThere are files from a game in progress under your name."); + Fprintf(stderr,"\nThe files are locked or inaccessible."); + Fprintf(stderr,"\nPerhaps the other game is still running?\n"); + } else + Fprintf(stderr, + "\nTrouble accessing level 0 (errno = %d).\n", errno); +#endif + Fprintf(stderr, "Cannot open level 0 for %s.\n", basename); + return(-1); + } + if (read(gfd, (genericptr_t) &hpid, sizeof hpid) != sizeof hpid) { + Fprintf(stderr, "%s\n%s%s%s\n", + "Checkpoint data incompletely written or subsequently clobbered;", + "recovery for \"", basename, "\" impossible."); + Close(gfd); + return(-1); + } + if (read(gfd, (genericptr_t) &savelev, sizeof(savelev)) + != sizeof(savelev)) { + Fprintf(stderr, + "Checkpointing was not in effect for %s -- recovery impossible.\n", + basename); + Close(gfd); + return(-1); + } + if ((read(gfd, (genericptr_t) savename, sizeof savename) + != sizeof savename) || + (read(gfd, (genericptr_t) &version_data, sizeof version_data) + != sizeof version_data)) { + Fprintf(stderr, "Error reading %s -- can't recover.\n", lock); + Close(gfd); + return(-1); + } + + /* save file should contain: + * version info + * current level (including pets) + * (non-level-based) game state + * other levels + */ + sfd = create_savefile(); + if (sfd < 0) { + Fprintf(stderr, "Cannot create savefile %s.\n", savename); + Close(gfd); + return(-1); + } + + lfd = open_levelfile(savelev); + if (lfd < 0) { + Fprintf(stderr, "Cannot open level of save for %s.\n", basename); + Close(gfd); + Close(sfd); + return(-1); + } + + if (write(sfd, (genericptr_t) &version_data, sizeof version_data) + != sizeof version_data) { + Fprintf(stderr, "Error writing %s; recovery failed.\n", savename); + Close(gfd); + Close(sfd); + return(-1); + } + + copy_bytes(lfd, sfd); + Close(lfd); + (void) unlink(lock); + + copy_bytes(gfd, sfd); + Close(gfd); + set_levelfile_name(0); + (void) unlink(lock); + + for (lev = 1; lev < 256; lev++) { + /* level numbers are kept in xchars in save.c, so the + * maximum level number (for the endlevel) must be < 256 + */ + if (lev != savelev) { + lfd = open_levelfile(lev); + if (lfd >= 0) { + /* any or all of these may not exist */ + levc = (xchar) lev; + write(sfd, (genericptr_t) &levc, sizeof(levc)); + copy_bytes(lfd, sfd); + Close(lfd); + (void) unlink(lock); + } + } + } + + Close(sfd); + +#if 0 /* OBSOLETE, HackWB is no longer in use */ +#ifdef AMIGA + /* we need to create an icon for the saved game + * or HackWB won't notice the file. + */ + { + char iconfile[FILENAME]; + int in, out; + + (void) sprintf(iconfile, "%s.info", savename); + in = open("NetHack:default.icon", O_RDONLY); + out = open(iconfile, O_WRONLY | O_TRUNC | O_CREAT); + if(in > -1 && out > -1){ + copy_bytes(in,out); + } + if(in > -1)close(in); + if(out > -1)close(out); + } +#endif +#endif + return(0); +} + +#ifdef EXEPATH +# ifdef __DJGPP__ +#define PATH_SEPARATOR '/' +# else +#define PATH_SEPARATOR '\\' +# endif + +#define EXEPATHBUFSZ 256 +char exepathbuf[EXEPATHBUFSZ]; + +char *exepath(str) +char *str; +{ + char *tmp, *tmp2; + int bsize; + + if (!str) return (char *)0; + bsize = EXEPATHBUFSZ; + tmp = exepathbuf; +#if !defined(WIN32) + strcpy (tmp, str); +#else +# if defined(WIN_CE) + { + TCHAR wbuf[EXEPATHBUFSZ]; + GetModuleFileName((HANDLE)0, wbuf, EXEPATHBUFSZ); + NH_W2A(wbuf, tmp, bsize); + } +# else + *(tmp + GetModuleFileName((HANDLE)0, tmp, bsize)) = '\0'; +# endif +#endif + tmp2 = strrchr(tmp, PATH_SEPARATOR); + if (tmp2) *tmp2 = '\0'; + return tmp; +} +#endif /* EXEPATH */ + +#ifdef AMIGA +#include "date.h" +const char amiga_version_string[] = AMIGA_VERSION_STRING; +#endif + +#ifdef WIN_CE +void nhce_message(FILE* f, const char* str, ...) +{ + va_list ap; + TCHAR wbuf[NHSTR_BUFSIZE]; + char buf[NHSTR_BUFSIZE]; + + va_start(ap, str); + vsprintf(buf, str, ap); + va_end(ap); + + MessageBox(NULL, NH_A2W(buf, wbuf, NHSTR_BUFSIZE), TEXT("Recover"), MB_OK); +} +#endif + +/*recover.c*/ diff --git a/win/Qt/Info.plist b/win/Qt/Info.plist new file mode 100644 index 0000000..2000fd7 --- /dev/null +++ b/win/Qt/Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleGetInfoHTML + http://www.nethack.org + CFBundleGetInfoString + Copyright (C) 1985-2003 Stichting Mathematisch Centrum + CFBundleIconFile + nethack.icns + CFBundleIdentifier + org.nethack.qt + CFBundlePackageType + APPL + CFBundleShortVersionString + 3.4.3 + CFBundleSignature + NHak + + diff --git a/win/Qt/Install.Qt b/win/Qt/Install.Qt new file mode 100644 index 0000000..7b6ef14 --- /dev/null +++ b/win/Qt/Install.Qt @@ -0,0 +1,92 @@ +Installing NetHack with a Qt or KDE interface +--------------------------------------------- + +This document describes the installation of NetHack with a Qt interface +on UNIX/X11 or Mac OS X. This code should also work with Qt/Windows, but +support for that is not currently official. + +You can download Qt for UNIX and Qt for Windows from http://www.trolltech.com. +Qt for Mac OS X is currently only available commercially. You need Qt 2.0 or +later to use this code. + +To use this code: + + 1. follow the directions for the UNIX installation (in ../../sys/unix) + to create the Makefiles. + + 2. ../../include/config.h + + define QT_GRAPHICS (search for it). You can comment out + TTY_GRAPHICS if you want to, or leave it in to support both + interfaces (in which case be sure you have the right curses + libraries etc. required for that interface). + + 3. ../../src/Makefile + + ensure your QTDIR environment variable was set correctly when + you installed Qt - $QTDIR/include/qwidget.h should exist, for + example. + + ensure CXX and LD are set to the compiler and linker you need + for compiling and linking C++ software (e.g., set both to g++). + + add $(WINQTSRC), $(WINQTOBJ), and $(WINQTLIB) to WINSRC, WINOBJ, + and WINLIB respectively, and compile. This will give you an + executable supporting both Qt and tty windowing. + + 4. ../../Makefile (the top-level makefile) + + change the VARDATND setting to contain the files "x11tiles", + "rip.xpm", and "nhsplash.xpm": + + VARDATND = x11tiles rip.xpm nhsplash.xpm + + 5. Follow all the instructions in ../../sys/unix/Install.unx for + the remainder of the installation process. + + 6. Consider adding the lines below to your .nethackrc, as they are + likely to give the best interface for this window port: + + OPTIONS=name:player,number_pad,menustyle:partial,!time,showexp + OPTIONS=hilite_pet,toptenwin,msghistory:200,windowtype:Qt + + +If you are using KDE, you may want to also try the KDE version. It just +uses the KDE menubar and follows other KDE conventions - there is no +extra functionality. To do so: + + 1. Ensure that you have KDE 2.x libraries on your system + (in 1999 KDE 1.x was the norm) + + 2. ../../src/Makefile + + Add $(KDECXXFLAGS) to the CXXFLAGS definition, $(KDELFLAGS) to + the LFLAGS definition and $(WINKDELIB) to WINLIB. + + 3. Some additional files here - knh-mini.xpm, knh.xpm, and + knethack.lnk are useful if you want to install "knethack" in + the KDE games directory. + + +If you are using Qtopia, you can compile NetHack for that environment +with the following additional steps: + + 1. First be sure that you can build a simple Qtopia application, + such as the examples that ship with Qtopia. Do not attempt + something as challenging to compile as NetHack before you can + already build a Qtopia application for your target device. + + 2. If you are cross-compiling (eg. targetting an ARM-based handheld), + be sure to follow the steps for cross-compiling in the Makefile.src + and Makefile.utl files. + + 3. To CXXFLAGS in Makefile.src, add: + -DQWS -I$(QPEDIR)/include -fno-rtti -fno-exceptions + + 4. Rather than -lqt in WINQTLIB, have: + -L$(QPEDIR)/lib -lqpe -lqte + + 5. After building, use the "mkipks" program that ships with Qtopia + to package the result into an ipk file. + + diff --git a/win/Qt/knethack.lnk b/win/Qt/knethack.lnk new file mode 100644 index 0000000..35e01f9 --- /dev/null +++ b/win/Qt/knethack.lnk @@ -0,0 +1,18 @@ +# KDE Config File +# Call this file knethack.kdelnk or knethack.desktop +[KDE Desktop Entry] +Name=Nethack +Name[fr]=Nethack +Name[hu]=Nethack +Name[no]=Nethack +Name[sk]=Nethack +Name[cs]=Nethack +Name[hr]=Nethack +Name[pt]=Nethack +Name[pt_BR]=Nethack +Icon=knh.xpm +Exec=knethack -caption "%c" %i %m +Type=Application +DocPath=knethack/index.html +Comment=The classic Unix role-playing game - fight monsters and seek the Amulet of Yendor for your god! + diff --git a/win/Qt/knh-mini.xpm b/win/Qt/knh-mini.xpm new file mode 100644 index 0000000..05bd70e --- /dev/null +++ b/win/Qt/knh-mini.xpm @@ -0,0 +1,30 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"16 16 7 1", +/* colors */ +" c #000000", +". c #DCDCDC", +"X c #008080", +"o c #A0A0A0", +"O c None", +"+ c #FFFFFF", +"@ c #C3C3C3", +/* pixels */ +"OOOOOOOOOOOOOOOO", +"OOO+O+++@@@O@OOO", +"O+O+++++@@@@@O.O", +"O+o+XXXXX X @ . ", +"O+o+XXXX X X@ . ", +"OO +XXXXX X @ O ", +"OOO+XXXX X X@ OO", +"OOO+XXXXX X @ OO", +"OOO+XXXX X X@ OO", +"OOOO+XXXX X@ OO", +"OOOO+XXX X . OO", +"OOO+O+XXX . . OO", +"OO+++ +X . ... O", +"O+++. O+. .... ", +"OO+. OOOOOO.. O", +"OOOOOOOOOOOOOOOO" +}; diff --git a/win/Qt/knh.xpm b/win/Qt/knh.xpm new file mode 100644 index 0000000..9c03127 --- /dev/null +++ b/win/Qt/knh.xpm @@ -0,0 +1,67 @@ +/* XPM */ +static char *noname[] = { +/* width height ncolors chars_per_pixel */ +"40 40 20 1", +/* colors */ +" c #000000", +". c #0000C0", +"X c #FFC0FF", +"o c #FFC0C0", +"O c #DCDCDC", +"+ c #C0C0FF", +"@ c #008080", +"# c #A0A0A0", +"$ c None", +"% c #000080", +"& c #585858", +"* c #800080", +"= c #FFFFFF", +"- c #FFFFC0", +"; c #00C0C0", +": c #C0FFFF", +"> c #C0FFC0", +", c #C3C3C3", +"< c #FFDCA8", +"1 c #0000FF", +/* pixels */ +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$=$$$$$$=,==<,=<,$$$$$$,$$$$$$$$$", +"$$$$$$$$==$$$=====,=<>,<><$$$<>$$$$$$$$$", +"$$$$$$$$===$====,==>,=<,<,,$,,<$$$$$$$$$", +"$$$==$$$==========<=<,><,><<><,$$$,<$$$$", +"$$$====$========,==,,=<>,<,>,<,$<><,$$$$", +"$$$====$==========<=<,,<<><,<><$,,<>$$$$", +"$$$$#&&&==$#$#$#@#$&@#&&@&&&&,, && $$", +"$$$;#@&@==;#;#;#$;&#@&@$&@&@$>< &@ $$", +"$$$====&==###1##.##@&#&@&&$&&<, <>O< $$", +"$$$====&==$;##;##;&#@&@$&@&@&>, O<,> $$", +"$$$==&@&==#1#;##.##@&#&@&$@$&,< ,< $$", +"$$$$ ==###1#;##.#@$&&&@&&@-, $ $$", +"$$$$$ =X;#;###&;#&@#&@$&@$&,< $$ $$$", +"$$$$$$ ==#$+@+1##@#&@&$@&&&@<> $$$$$$", +"$$$$$$$$=X;#1#$#@##@&#@&&&@$&<, $$$$$$", +"$$$$$$$$==##;#;##1$#@&$@$&&@&>< $$$$$$", +"$$$$$$$$=X;###1#@##@&#@&&@$&&O, $$$$$$", +"$$$$$$$$==#1#;##;#.#@&&$@&@&@<> $$$$$$", +"$$$$$$$$$==###;#*;#&@#&@&&$&O< $$$$$$$", +"$$$$$$$$$==;#1,$;#&#@$@&$@&@<, $$$$$$$", +"$$$$$$$$$$=X####.##@&#&&@&& ><, $$$$$$", +"$$$$$$====@&=O##@#;&@#&&,< <,>< $$$$$", +"$$$$$===#$== ==$1#&#&@&O> ,<$#,,> $$$$", +"$$$$===#;#== ==$#;&@#O, >,#;&<,< $$$$", +"$$$==$#;*@O< ====Oo,> <,&$#@&O, $$$", +"$$$==;$#&&<, $===<,< < $$", +"$$$==##;&O>, $$==,> $$ ,<&&#$@o, $$", +"$$$===X=O,< $$$$$$$$$$$$$>OO,Oo> $$$", +"$$$$====,< $$$$$$$$$$$$$$,<,>,<, $$$$", +"$$$$$===<> $$$$$$$$$$$$$$$$><,<> $$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" +}; diff --git a/win/Qt/nhicns.uu b/win/Qt/nhicns.uu new file mode 100644 index 0000000..feffce7 --- /dev/null +++ b/win/Qt/nhicns.uu @@ -0,0 +1,1112 @@ +begin 640 nethack.icns +M:6-N2`Q@J)8&,C9"%T(S\SO(+&"I1U5GU$0G?,X7MN@<8,PHAF6'^#<[QQ +MAGV\RH#]O6L;&?(7'HHZA>IGJ@,80BYS#/&F3HGUOQI?&@W3&QH6!Q@E< +MIW'&QES&QJ*5A,8$X6K&QJF'Q@6/QD3&QNR'Q@!A@<8`@)'&H,8`=H'&`@\5 +M?87&"+:'[L:%;X"46H3&"<@-O(T^9W*9K<6`Q@W:KS'&PS.!&2M[-[+%XH#& +M"A<311$U*!X1#!^G@L8*"RP0%@L1%@L9"Q:!Q@RO%A0?&A@-#F11'`T*@,8- +M'2D-QL8<%0HC%1HC$0J`QA!^9KT1$@X-'33&-<8Q#L;&;('&"5(-(<;&)L;& +MAWB$Q@0*.\;&-8?&!5W&1,;&"8?&`#.!Q@!AD<:@Q@!N@<8"#A5CA<8(M*+> +MQGM_FJ=5A,8)QPS'H39E@:6\S(#&#=:D+L;#?8X8*G\VO,K@@,8*%A-!$$%# +M'1`-'J."Q@H+'Q`6"PX5"A,*%8'&#*L6$QD:&`T-1S8:#`J`Q@T8(0W&QAP4 +M"AH3%2$1"H#&$']BO0X1#@T;+L8UQB\-QL9F@<8)/0T?QL8=QL:#=(3&!`HM +MQL8AA\8%4\9"QL8)A\8`*H'&`$*1QG,X;6L```$(____________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M_____________________TE#3B,```$(```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````&EL,S(```;C@<8`R9S&`*F]Q@#%G<8`O(;&`]+JXKR0Q@*] +MQ]:"Q@;LCVEW>7?#CL8$SH)_>[2`Q@B!9G^9F:BI<,R,QA&]G)B84,;'QH]Z +MAI"0G:^RH,N,QA'AF-[-'B!;;V%WD*":K*R^QZN+QA*D=]OYG:7*97)R>Y.D +MI[+&R=.KA\8`NX#&$N^;P[2ZPF)G +MKD@8Q;%A:F-E.9"4N+_'UN+&OX+&%2&^V4XH(I'&_D2*/SI)*RDA*$"2CL*' +MQA6\BY1U5%AC>$GN+4I624-(7%XV6Z;/A\85K:[.'EMP9XF2;X)^9U9SBYF^ +MGJI+S(?&%[.1R#5A6'R*?"_/=V]/MKG(OYR^Q']KQX3&&,BXFWUB86-NBH2- +M6JAN4=7R,D;W`?=^!QAMOK^]2@<8;QR$0%<;KF4?&6%MRS,2'E7AV:5EH;,;&Q8*U +MVH+&`I$71(#&#T@_3&5EI-A[?(YF4]'&4L&`Q@*-I9:!Q@*U(:6!Q@Y1S,9M +M@\!I<65AUL;&;WZ!Q@*>BF&!Q@#)A<8&YV^;:,!'=(#&`7N,@L8"RV'3B\8# +M=I/&88'&`4VMD\8#PW?&:('&`7>1DL8$P&O&QE>!Q@&BGY+&`,F$Q@*_'F*$PQ8S&$1)QAXH^QL?& +M."UW@(>3I:>!#\@,"T@.#@I[,0H)&A^%QA@7"A(<$`T.%A@9 +M'"*'#!A1(3(F%P\*##@?A,89#`D9$!=&-$`>'!L?%Q41$VT:$PH6&@L,%"6# +MQAL/"AH++\;&O"(`6E8#&""1?@**BL*THQ8S&$1!DH:`]QL?&+R.& +MF8^AN+F#;HS&$0DHU==6);X45R69IYNQK\#)(8O&$BT>K^RDJLD=&7$K<:NK +MM<;)TB^'Q@"B@,82"9?`E;V]&F%R@']!K[N_R=;=$H'&',5`.AO&%,?)QD48 +M,!ZPHAAA2ETECV.\O\G5W,:,@L85$`D)#1@1=<;VSR4;$0\3$A`2-9!'PH?& +M%0D0$1@8&R`/%<\/$PL4"PL,#1(@D;'10)#`D+"Q,6 +M#1>^A\86"A5`%@P*#A8<#K<+"S@.#@IB(@H)&1Z%QA@2"A(;#PP.%1@9&1>! +M"Q@^'"4@%P\*"R,8A,89#`D8#Q8\+34>&Q@7%A$0$DL9$@H6%PL*$2"#QAL/ +M"AD++L;&N2`?%AH9"0T0'QD4&R$A%Q\/1B/9@<8;*R(="\;&Q,;&$!H3#0D/ +M%2\3&QP9(B\<$QX)%X'&&\(1#@[&XB$BQ@P9&PD)&!,C-#PW%PS&QK,9#PF" +MQ@*0$!:`Q@\V'`H\/0D)$!L7,2O/Q@J!@,8"00DE@<8"L1E_@<8."L7&0!T) +M$"HY1-/&Q@D*@<8"C`Y+@<8`"X7&!N`A"1.X"R^`Q@$)0H+&`KL_THO&`PD- +MQAF!Q@$C49/&`PD)QBN!Q@$)$9+&!`HFQL9(@<8!"0J2Q@`)A,8"L@DH>8E)"DG8=WS)3&![W/>YF9 +MCO'0@<8.IGJ'?H:.D9Z=H*NL@;#4DL8;R]6LJ)&18'?+QM#&KW9QF'.&AIF: +MJ+"CN;"7SI'&'-VVZ32?IJ5D)20U/*)O77F1@9"2I)JFKK&^P)/*D,8=R,:I +MN5WGO_6!G2NRB&-I:6MX@9&AH+"MM\+(R9;+D<8;VAXBW^%28B(C\N)FYNRJXYO>I-\G92QN]C8?J2%Q@=9TRMU +M?6I8HH#&'=)U9GAJ>C&$C(S%I9:=CHRC>;"A:864FJ*[W^&*C83&!G]^W6]X +M#EJ#QAR'=WQV<(5WTL>AAJ*,LHZ(9:&+9'I[B9F_TNI\A8/&"L5O;GD8$&+& +MQM6I@,8;)3(:='+PP::+C8QXBGJ*6YYR<9)R7@<8%OX.,O73%@\8#"Q@*-G[V%Q@*^@823Q@9< +MP8S&)G,8"C]O#@,8!1+J"Q@/!>>R$G,8!R8:!Q@"V@\8# +MBY?"LIS&`;*5B,8">+G6G<8`88G&`JV?@)W&`-R)Q@*8@,^JQ@'2T>O&@\8` +MX:S&`/VLQ@%GR*S&`)RMQ@!KK<8`NZS&`*B+Q@7-=U]?:*^9Q@!VB<8(=@D/ +M%3D5-`Q]F,8%KW)^R,;]@L8+OPDL+%YO@&H@1`S#@):37RI;E,8'M@Q1AX=\[K"!Q@Z%(Q-O>G^`DI24HZ):'-&2QAO* +M"Q->@(!;6[7&SL:.%R\*<'IZAY*8I9RNIA/-D<86C8(Z-DA7;AHG +M'A%R@8>6DIZEJ[:Z++F0QAW(QA8-)N"\\W6-,+4^/BEG/3!RAY69I:6MNL+% +M7JB1QAS%"2-<;,3$F*?&(3\@8W!P+(>2DJ*KKL#`Q--3W(S&`+V!QAPB"C%= +M4ZZ8P-$:0&4V<'![*6R4J*>TNL/&R]1ZL(3&"-JS;:^QQC$[FX#&'<,*)3/L +MSX'`QAD^:"MP<'N#:S>DJ+*]Q\7.W.*0Q(/&)5@P/Y<9Q@D/)LW&T[HK"6P. +M#JO(RB!(/7!F%T9B6HXCL[2WQ,;*@,8`EX/&&;97%D\/"0P8$J9^41/&(3UF +M057.#A@S%#4V@10%CIA%N,#3B\8@)!<3"1,6#T4.$!%!*37O#"@:%!X;%!$1 +M$`P4%!^U=J?+C,8?"1`)$306.RLX(S42#BG]*PT*$!<+"0H*)S,,"1A9)VJ- +MQB`7&`X[0A<;)R@M&Q$))#0I(!\*%PX)"@D,1Q<1,!`9'\&,QB$/"Q5-+!82 +M$!`.%A,B"SBW$1`)%AX)"PD,&3@@"PT-%AO-BL8BJPD,$1,9$0H+"0L:%A,1 +M##"V"0H452#A<29#07 +M40L,'`L.#0H)"AVXAL8G#@D)&!4-$!BQLW@Q/AT7'!@4%A@D"18152@/%AX3 +M#A(2%A01"0D9?(7&!PT)#1H<#`R'@,8=S1M%*"0=#AD>&0D0%`\S*@\L#ALT +M'AP7%0X)"2!)A,8&'1<**1T+#8/&`D,C'(`5%BT)"A$7(SX0%1@^&AX5(R,B +M$0T?"AY,@\8*NBX8'`P/,<;&OUN`QAL1#Q$6#`D)#!<5%ST8.#$3#2P2<"LG +M&Q@1%@D9A,8?5AL8#PL4QLN?02#-W!$4&A0*"0D-%14;'2PS*Q\8)0^`Q@:A +M*!L5*`H9A,8?2'XI#V;&QKUM-A%5"A)#-PX2"0T;%QT[230T4\8E##6!Q@6V +M,1,)#L.#Q@-N%1`E@L87MB`4#;H_6AT/"0D4&ADG5$`RL,;'"@F;@L8#CQD* +M$(/&`WD_#RR#Q@Q^$!K&X%`8$`H*%QPF@%N!Q@()#%"#Q@.^%`VS@\8";!"0 +MA,8`.X#&"3=2+PH-%B0A.#&"Q@())GF$Q@2'%Q)XN8'&`0L=BL8(CQ\+"5B[ +M$0D3@L8"8"*FA<8"M%5UD\8&"0I$QB`4R(+&`LLWOI[&!48*$\8[(X/&`H8U +MCYW&!GH)#&3&/82#Q@()"UF=Q@8*#&_&QBJV@L8#R`D)*9S&`ET)NH#&`42Z +M@L8#NPP)%YS&`1H>@<8`M8/&`R`*"J*ZK,8` +MIHO&!(U*4Q@>T"T>BHI'>HH'& +M#GL>$W^%E9JFGZ>PL549T9+&&\4*$$J:FD53M,;9QH07*0ITA86BF["YJ;VN +M$\V1QAS7A@D82JZ)S8^!E+%C&2,=$7J6E*J;K+&ROL,ILY#&'F,HML%!T<$X`0!PT4$QZY(Q@NK"0D6&143 +M"Q89(1V`&A<8%Q8-*1H-%Q)'+!&QB<."0D8%0P/%K2R +M<24T'1<:&!$6&!X)%1%`'0\6&Q(.$A(4$0\)"1ASA<8'#0D,&AL,#(.`QAW. +M&CLD'QT.&1H8"1`4#R0A#R(-%BL:&!42#0D)&T&$Q@88%PHA'`L-@\8<0B$< +M%!44*`D*$1<:-`\3%R\5&A4A'1T1#!<*&T6#Q@JZ)!<<#`XHQL:Z3H#&&P\. +M$!8+"0D,%A47,AT&A(-NC9$'`X) +M"1(9&!\^,"JOQL<+"9B"Q@..&0H/@\8#=S4.(H/&#'8/%\;@/1<."@H5'".` +M18'&`@D,3X/&`\43#;&#Q@)F$)V$Q@`U@,8)-#TF"@T6(A\O)8+&`@D==H3& +M!(,5$'2R@<8!"QJ*Q@B"'0L)5;D0"1&"Q@)<&J*%Q@*S26N3Q@8)"D+&'1/( +M@L8"R2&]GL8%-`H3QBT?@\8"@B&*G<8&=@D,8<8O?X/&`@D+6)W&!@H,9\;& +M*K6"Q@/("0DEG,8"4PG`@,8!0KF"Q@.Z"PD6G,8!%!Z!Q@"U@\8#'`D*H9S& +M`2!O&:#AM:P``"0C_ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M____________________________________________________________ +M__________]I=#,R``!9E`````#_QHK&`LSWXOK&`O'^]/K&`_[^^-?YQ@/P +M\^_=^<8$R-3-N(!^#8F&<6AM:GMTCZK)RLS6P,81VJG!MI&*@8*(G+S:QL;^ +M_O3$B<8+T]/SY*9^;F5E9VEX@(80C9"0J)NFD8E^:R<_CO,82U]/`?IYY?W^#?WAK:W*L\>.JU(G&(GZ5FX9K7V)F?H.+A921 +MF9^FHJB6B'].:1XHG& +M)&N"D8)_=&5H@H*&A)"9D)V8DI21FJ>KG:6=H**@EGZ*FK#%Q]2YQA*[X,!_ +M@82EF9>2E9F2?H/DZ83DB<8F:YRGA9:0;G%^>X2#A)6)DI"1E)29IJB:GZ&> +MJZ^RG9>"BZR^U8Z]MRBI"' +M@(%[?H=_A(Z(D(^)GIFEFIJ=HJ:JK:RMM9*`A9ZISLO)ML84Q:_SS:O$Q9R0 +MD)F0E9")TM%`;97'A\8GF.O)=WV%BH*3=GZ+AI"&D)"-F9B9F9N:J*BMH:NP +MK:NTNJ%T=Z_-W[;&%=J;O_/-KD)LL9F5D)F0IK&M]HY1W>'Z&AHF`C)"8D9Z:H*BH@+`+MJNPM<*WMXAUG\#GM,8*V>'5 +M?NGNLVX0:(^`F3N@H:"OW3\>.#+-T\U;1$3-QL:>ZX9U9F-O=827CWJ&BX:& +MD)"9D)&:DIJ>FJ:PLZNSL*^VMKO#J'2/N,2SQDO_2IC)-GYV2F::F +M?M?10SHO*E%243\O1&9MM8[Q?F]T9FAM?81]C):.@)"0EIZ0DIJAFINDHZ6O +MJZZUK;:]O[RVKI*1J<'3L<9,T.3&R*;4Z,-X,G&LPJJHN'BBA(:.)B(B(1\G +M030V2GI\]()K>&]=:'%X<8N8D(&1BY.8EI::H*&BI:*HK*NNLK.RO,&^O\6M +MEI6GR]>PQDW)XL;'HJ[QU*X[.:+GOKW6<'=LU>!`'B`A&25+,3A$1&ONB&=W +M=U)D:&YI@8Z.A8V)D)2:GIB>IJJAI+"MK;*MN*^YO\'#R,BQFXNRW<>OQ@#5 +M@,9*SICE[JB2.ENOP?SY1,/^_?JA+3=!+U(88W1/0V#:AV!Q=E9?:6AL=WV% +MB8B/CYZ:I9*GH*BAI;"RL+6MMK>VO,+'SCYRAFYJ?@*L5IK:RMK2ZP,?"Q\?)Q\[.U->\C:6\S:;&`>;1 +MAL8CQ[F_[>VD?Y"5C410]UN-!K>75G9FAD:5EP@G(A?'AK +MA)^0FIN:H:*BJZJNM*ZTNKV\Q\+'RL_&UM37U,Z9D:;&`LS:AX?&#\>7Q_#0 +MA8>!6GE$ZXHXHLN`J`^TOLG"V,UL>G=>9FEF;FE?@G(D?GUQ>HJ*C:*?H*&K +MLZNOL+2XP+N[Q,+'RL_-U]'7U=?.KI?&QZ/&`\G52[F&QB+(KZ7BYZ^-ASU4 +M99)C.URIFZ>LM\G&QM7!;G9T6&9I:&QP6()R)7Z`>GQ]?XF>GJ"AL;"NL+*V +MN[ZZO\3"Q\K*S=31U]C7X,V&!U5T*CDYVQN-&`Q@JQ;7%R5V=I:6AO6X%R`75_@(`B?'J%CY:< +MH;&KK["UM[NZNL'&PLC)Q\S1U-;:VM_@O8VMU\>0Q@O+R-#/KI^WP\;%Q,F` +MQ@;)P+2P)1?-A\82N['J\KZ,/1U$QN.OB^RX>0H:FLJ["R@+H4N[S"P\[(RL*RPL;JYNL7#PLC. +MQ=')T='7V(#@!>7EQ9'"T(W&$\_:M8)Q8%E`1H.LI7N"Q\;>U?.R@!``;X+& +M`>/;@<9%S)[2[^M:1$0>4DH8&"RQMLF0D8&#F:>VPK>VN;JYP\?(R,?.T5I8W&5\N$54`^ +M/C]`6*3`I8F`PL;([?.(%Q`0;,;&Y\S&P%/*QK3&QJ"XX?-L1$0>#G`B$`Y: +MM]/&TL;+67!SQN1"PX)B2.&Q8U@X[&R,)726UR<&%I8SD^D)&D9&0D86.H;BZNL'#P\+(SBL=EV1#(R-1]<61\N9VG4S7GQ*>*54V&.A`S)1$/"\Z)AU[ +M)RM&2\;++B-&='IR.6([-2E6,S9.42DV)BHL,RPX-3B1D8F)FY:)DJNXR\7" +MQM38HL9.W8`:;9^-I[H5E7VB+:E(L*&`Q*"8@)R$F*!X:<7%I@'$Q +M9?3^L!`.#A@>'A@7*CY==G-L*B@H-2,A(S8D,50Q,41&2#8J*BEE.C8YPM3+ +MJL;&R[:GQE7,U*N;T[MS>^G9?&E.87U::TYT9T193U-B7&Q:43UG/2@Q:7%Q +MC?[)*C(A'%DI,1T8-%U[=F4K)S$\.CT]1CU,7DQ38&5J6CPI*#@[:4[2MEF( +MQL;GV*?&5=3@LZW#N)*FIH"49GM.=6Q<7%59GD=NO?Y66@FM-O6RE27=@6F)96%1:65IF;'&*A)F6FX!E0T=MBYK: +MJZZ384QB8$](6&9V=EI$5U=F<&%Q87-D:W%OEF5>9=!\GD5I:%I<6U=F869?9E=Y=8:$HIV/>E\_:7%= +MV)>GEVE6>VMG25IG?75:1E9==&]N=76#;'U^)LKVIP,BAH)B/E[BTDX]? +M2&=.E\1XOZ3&6,"KP[.6MV?(J3GH^& +M9#E(*^;&NU)$*'9B55UN971R9E)=57J1Q\W/VBI,9:MGNYI:B>AW.6M,"X9C0J7V563%%14TA13%!EB(5\=81W +M;(B+)QI5M.STP&E(EHM6:F]]?7)64%"-R-#4T]3*S\G4V.+EKZF"CJVNO\F]Q\2N@6U06%AKH,9@R+*APK"SGHUW;JG+S,J&:%A87F!H7F-H +M9V5E>&AV?(V.AY5[B7]R3CTO,EG'RXF*=&-Z=G-\;%%D9&&%A7U^AXR0GZBY +MSL&\OX*"HZZIC*"OM\&YPLO$NZAG65YMT,;&RYW&7\3*VL6VG))S8YJWPZ)S +M9%I=8F-E7&1E865NGU^@82)?XR)F8*"IJ2HG8NPL+?%R<'*QL&9CG19JJR!JIO&8\G&T-C= +MQK:>E7AFCZ.FC6QD7%YA8F%<9&-A9&]P?W6"@X=_AH*-F(U\=VU$)BH^GHFD +M>G%QA8)C=G9H8V]G8V5]@']_?75D'N;H*B?B:^NL<7*P,O'R+"HA5J- +MCF^=QL>9QF/0R=W5SK"QIYB'?'Y_6W1N;F-@6%Y87V5C:&-I;GIZ?7>)B(E_ +M?WV%FX:4BVDO*2MWTXR%;8:)8G9Y=&YI8V!DA9*0D8=L9&%H9V%G=YFAE96K +ML*R[QLC'R]#)PJ1X9D]YE;_)FL9BS^+;PIREI(.*?7Y[8FMI6F-;6&-:76I@ +M9&1J9'-X@(&%B(*&C(B1=I^@BF]E+SU0W<*%:XM[76=V?7]L8V!@8YJCIYN$ +M@GIXBGAJ;7!_I9.>M+"LNL:^R&5E8G166%)B95I9969F;G%U>GN!?7>!?':0C8>1A'V#<'A)6&53U82'I9N- +MA'60HHER8&!C;'V)FY6"E:[$H)BBBW5UBXN?H[BPM<"^VM//U];)N65EBI.8 +MQA;#OL/2PL*LDW!PA'YS7R6 +MEF1X>82:1+B7BKOGTK9\H9RJGH)_@F2`>A:&BIRZN+"GIIZ1?G6+CY.;K;^N +MMLO!Q(#4!\*DB)QCQLB\E,9IOHB?H)VHI:2?A8)W?IV7DJVBO+F^R-'2T-+-PZUF?J1TRY+&:LO-W]',TMS1 +MIY!S;G!U7V-[85U96HBDO:^SL;6=;&5N:69K;79^?(R*?81]A8V+2WF"BH^) +M:W^9R,S<'5A9WAB6UQ:L[[,@,91RKRIBW-G +M:'!IY*,FHEP::[-RCE7AY=WN'A8>1CI.:G*&MJ[K)TMK2R,6= +M?*FONY#&%+AQ1WJ]BU\O:WAH<7QT:%]=6%EGHH?&4-BM'2&=(%Z +MBV4\CHN$A(UM;;#6T;V\KYJ8EINEG)R=BJ2*KYNTH7V7FJR[K*"%:75R?HJ/ +MB)23C)2?GKJUQ]?,V^+`HWB,B<'&QXW&$[AI1RB=BXMD='1M<7QW9RQ16&5D +MBL9.RXM9;'IR$?WEW>+;"UK.GP:B.J::PFYR>BIR*DK"5 +MH8&7DY:[JIR3<6=Q=81U=(&-AJR2BKNKG,?&T^2_QI&<;J+&RHW&$[Q9A'R$ +MA9[IDFEY='AT.`X]679MB\9-SJ1R975Y9G)X>W=_(FV;?HB1=&^NR+[+L)^M +MM(V'G9.G=XV*D\"-HWV'=ZB'Q]B(J4HIR-QYS,T]'D +MY+INE,:*C<83S'&FTX%BH\]W:W)Z>EX2#D16>'2-QDS(CV5R=GIU>'U(&OBIN0HHA]BW9J88J1I8Z7@')C>7E]F$8#P]$5F6$A,8! +MR=B%QDN_1EUU8Z1C:>+BW*/ +M;'1GG^0?HV@I+"SRNS(R-ZS5U2-C<82R;)J;(R! +M;71Z'QVB7)I7EY_EJYS>8!MA)6!=7%P:7E]@H&`BI"E +MH;+=S>CPXI1'=H[&"KY^;H:!='AX4CH1@`\#155Q('& +M3IQ0/"LI/E8[6W=U>W:1N^3=Z\"OJ9^LHH.`DX.>B(.">HJ`BW]R=WEN8&!9 +M'-[?("%DIJBQB>'U]>W-L +M+X(/`T!76=:`QES)Y,W@WKV';G*VRL;&R'Y97S8I.2]B>'QT=W6>PM+4Z<*M +MI9ZJLX-_EX*6CGB,?(%^>XAK=VIG66189X*7;&U\?7_'QL;'R+:8?75\?'J` +M@96;K<'EU=OMPI"Z2\Z-Q@ZL?7IN.R$1$!`/#PY29(^!QD+9W=+%U:6(6%)@ +MX<;&TU%?6F%B7F5U=7=J7XJ>R,+,V7B*<69Q=V9F +M5%)36&F`:G!Q=UC)A,82R:YW;GU\@9B;J;K2\_/DI%`VK(W&"-,L%Q`5*A(8 +M$(`/`EE>RX#&@.M`UYR'D(%-5TAKRL:^4EI&6%U71%YE97"/P(O`N-C)Q,>< +MJXB"F7U]G'Q]<7N*8V%G6UA@55-D5%M>86%F>(1Q7KN'Q@_9EWUX?X2-HJ[" +MX^K41S9NCL8,QS517D!&0"D.#A-J9H7&,+1T>F4_4%$]0IV74T]84UAF1%%E +M869]AX2MHN"XOKZIDX-^A(%U?GM\>&EX9EM77UR`4PQA7&"UY8M29VEG7H/2 +MB,8/UK>!MH<6QQ[BYC)1[?XMY@8Y[B'%K:&!?5%=39%QG +M8J;$RL.36FIB8WJ?O8?&#\C$OL"!=XV)AY7*SW5SSVF-Q@NO;TTB31T?#P\F +M8I:(QB'"IF)"/STU-SE$2E-ALK1B9F!@@(^1G9^\N=ZYKYE^@'R`@'P:>XU] +M:6I78UE866-L9X+`QL;(O'Q33F2/OJKH@7^'B:C$M:]4.MB,Q@NW +M;%443Q<3#P\_9K6)QC'%FG5#/CP]/T)*6XO$QVMK8&)[@8*/I:ZUW+.IGFY] +M?'R!>GF#AGIG9UE=75Q=:'6$JH+&!Y%*2%ZMV["[BL8,T;ZDA8.$D:['N"(I +MU8S&"[]C;!!)'1`0%5AWS8O&+K-,041)24)-GJ)>F]B8F!98&!B>X>K@\8(G$Q+5L/GOY/'B\8,R9N#?X"8O:TN +M'\;&NXK&"LU>C!8S+Q`0+&RFC,8(S&Q.45)11%.G@,8AG7EH8&5M:W*MDZV\ +MSJR==V:#>H)\0-R>65B@F`"H8O$CL8*OGUZ?9"K9Z^Y$+R* +MQ@C"OEM1+!,9=WF/Q@6^4E`^6]*"QA?'RSL[[!BVUH?7ML:6MR +M>&6!8`&$P(?&"--89W)6A7)RR(_&";1V=7J+HX1N$*Z+Q@?`A%X^'3-]J)#& +M`]);4J6#QAS*I7%C85-B87*EK[>QBV9E>'-S>7AQ:6EE8%-*UXG&!H]E>U=: +M8'61Q@O.:W9T?8MU6(F7>\N(Q@?%H(B#06.5PI#&`\BYN,*#QAS'PK9E7V-C +M:W:7J[FLGFY=>'AY<&QB9E%*/TUYR(G&!M1?A)EN9Y*1Q@O'O8-X='UO;V=L +M?,>)Q@6PS\B/>LJ=QAG(IF]E:&AVG*2RJ*1U8Z*KJ+!F55)(2DA1LXK&!LQN +MD92@@YZ2Q@G'MY1M=69J8&2UBL8%R>;GE)/3G<89R,&>@7%L<(>3HJ6B>F:[ +MP<#'8E),2T]/5<>+Q@5XB)2SB+"3Q@C(LIQ];F5RBFV`CZ.A?W&`Q@C%7U%/4%)3=\B+Q@6"<9F\AL"5Q@7,EXQ\E+6,Q@+- +MF+*CQA3,RZQB>'REJX2*Q,;&N%]-6%)25M*,Q@614YW+ALJJQ@'8TJ;&$=:- +M:G*SU*A_P<;&LV1R;E]EMHW&!9I3E1RHW&!6ENBKVLDM7&$+JXO:^\I8MUO\;&JFYB9VK"C<8' +MOV5[G\&KC,?2QA+$LJ^_RZ.C=W)ZPL;&J6)B57/?C,8'R:-EB[G"K(?3Q@FX +M>KOCRH:"9V>)@,8$JD]A&GV-Q@C/96N>MK:SB,'2Q@B+H>;OK6%A4J"!Q@2Q +M42LL;XW&"+%D?IZVSZ2(P='&")V0TO&O>E-NMX+&!,.`('*,C<8(F&2$M^K/ +MM(:_T<8'8:GSQ69EI\Z$Q@.M++J,C<8(E&J#M_/9P9>ZT,8(T&JOY(A5N,+' +MA,8#VC+%E8W&"'9ED,[NX:Z2O,_&!]_!;[2_:;+%AL8#S5/&Q(S&";Z*6WZD +MV;F6BL#/Q@;4P9##F(/,B,8"@,;-C,8)HZE;;H2[FWN$Q,_&!<+!MM!_K8G& +M`*Z-Q@G'BK1N>8*VF72/T,8%PKC(TXG-B<8`S(W&",F#D:*WM/"_G-'&!9R^ +MI=I]TIK&",EYB;BSO-N\OM'&!,"GI**/F\8(S'B%KJ6EXZZXT<8$MHVACQ@61JM+?DOS&`8/$ +M^\8"NE2G^L8#Q;EOV?K&`].)GLKZQ@*Y;;K[Q@*F@L#[Q@&.E_O&`M-OF/K& +M`\B[<\2GQ@7DM\O+Q@*K9+VBQ@[6H4]*/S0U.C0Z.$IVG\3&Q@/#DI+3 +MG\83Q:AU3ADA"0D+%1H*"14S4VB,I,W$Q@/9<[_'G<86P99J#0D*(`X,#Q4P +M$`T,#!P*0&6AI;[#Q@*79<.&RT;$`PSI]/` +MQA'96!8.%1@;&1DA;\/&QOW]\L.)Q@NH*`D*$!\?,%IC9V^`>A!^@("8BI6' +MA&]6)!L5$`Q%M;_&$8D5.1D:%C`4%1PC+WK6[//*O8G&(7N*%0\8'$U<86Z# +M='I[AGZ`?8J"E962DHR&:2D:%"LAA-R\QA+5.PPD-T=A:&EA4SLB,&OPX)#. +MB<8B:W'UXA(&'CX^$@8&,EY>5G9.2>6=*,A8H/["[QA+('@T> +M5U>'@(5]=&95.T3RXG;?B<8D4FU5)A(81&%T=7IW@8B!C8J'@X"(EIV3FI:8 +MFI*'8#$5#UN+T;G&$J@5#1Y;59"'AH&$AX1E5]OE;MJ)QB9&C(DI$@\B3F]P +M>79WAGR"@(>$A8B6GI*4FI:CI*Z1830A$$&OO,>WQ@B%"@D/($J*A(:!AP:` +MFYW.:)FXB,8G9NK1*!@6%!EC;'1[<7E_>X"%>Y"*EY*2E)J>HJ2CI*J"4B45 +M*X_`R;;&%+80"0D-4JYO@("'@(2`?,3%/Q=.Q(?&)V;JEQP<&2D=#4]O?7J! +M>H"'?H>'D9&2DIB8I)JCI:2CJ[1]-$$2B-FVQA7520T)"AJ +M*!!<+D^W@<8!R]6`QAEFZEH<%AXZ*P\,3G-U>GI\>WZ`AXZ3DI28F("E"ZNC +MI:JUKK%T'A$XX+3&"M;@R2,)"0H-#S==@(<[E)F4J=ID*%-%R]++>FQLR\;& +M5.I0'"L/*B,9$ALW>GUZ>H"!AX>)DH^2E)*8I:BCJ*6HJZNTN*`N%@V)L\9+ +MV]S'C`T)"0P,-$I-@8>7EWG3SVI;24%B9&)<2VR#@JU+\$\7)Q(T(1P9.1HY +MF*YPI9*9GC(S,S$K-$I05F6(//(]%"$8.D$J($$8&T1Q@GV'CHB0DI65 +MF9F;H*.CI:FMJ;.XM;F_EUDO7IK3L,9-PN'&QF`3"0L+$2E>U;2QT6]\@M7; +M/2@M*APD1SU)8&PJ["X6&QTT7T,S.AX5&EAX?8>+BI21E)>@FIVEI:2HJ*^H +MLKJWO\3$I6I+4*["K\8`TX#&2L0T"@D-"A8J2BY>.EY2?FIVEIZ:KIZNLKK:^PL7(R\-Y +M*1FDQ+/&,+`-"0H/"AHC9^K1;/#]\]J@@)25E&`HI<:U61.V'RA")B(N9VAJ +M3Q\M,@TT@8>)DHF`E!6:GJ6DJZ>CJ[2OMKVYQ,/+P\6(&QNUM,8P.0P)"1L. +M0%%OOFS=_]O8'%!-2U+Q&?L\8DQ"()"0PK#CI<=FRP_JZFKJ[2UPKW"PL3# +MQL70U+))$V_,IL8!Y="'QB.2"@D)*U$70U%L=?._,>',EYB8I*:_EI7$$AH: +M*&)G-C0B:6^!<")L4SP9(5F1DY*:FYNCHJBMJ*NTM+>^OL+%RL#2T-30PU@N +MD*7&`LS6A8?&#[T>#`D2/4X<5D)LY'\MC;Z`F`^IM\BZQ[D7&QPF8F=8(AM3 +M@G`D=7-A.1DQ6(V8F9JBJ:.FJ*NQM;2UO;[#Q<;%U,K4T=2]D$!EQ:/&`[_0 +M2;>'QB%U%PD)'EU(12YEG%PU3)9ZDIZNQ,;&T+`9'QTH8F=D-AU$@G`<=7MT +M7ST^,%26F9JFIZ6EJ:NTM;2XP+[#Q<3(S\R`U`7R)H828<>(^:IZ.FI:NNLK2TN<*^Q,3#RW2#@5$J,6*2H:.DI:FTK[2UM[Z_QT:`@X-^6R=8 +MEJ6FI:JTL;2\NK[$QL#+Q,O+U-.`W`7DY+Q44\2-QA//U:1M9%M./T064T0K +M4L3&U!$)#8`/`&^"Q@'TX4>8.#AXYD%BZ9J[6SJ[&TL;G"@,,'Q_;DP_,#`X/RM8>408&Y;&7`D)"A`/#VG&QN"_QH9&RL:7QL9A +M#0D)56QL*`XI&A`.;:K0QLW&R!L<(1\Q9V4I9G!@+$AP-1=<>QX46(-W7XZ. +M?UP;?+6P@+0)N+B\P\/%R\;^4PPD)1XX9UL9 +M,'!5%#5P&A0G6Q44*W8E*HZ.C(ID,GBPM+2UNKF]P\7%W<;)R(?&`,"-QD^4 +M9F-&-!04'Q]J6Q(0%!-1#`L*"A4/#S?"QG`KKB<6I[`6:<:^41();&Q-3%0G +M-#0H0C)(T\QQ*`H<'QHM9DH5'F4^$C--%108-Q84'$L7'X".#I*#66Z`L+2] +MOKN]Q-#%TYO&7\&XLHYT1"<5($4I"@X0$`D/#@L*&1`/&)['/0]V'1%TEQ0U +MQL:8)CR9;%]+73LC/3]$02F^QB(0"A88%AQ?,10422,4+"05%!(7%Q04'A0< +MCHZ%?IB/>5J*K\/!O<#.UZ+&/-QW%0T0#0D*"0\*#P\-#1H2#P\[PQ@.&A01 +M%6L4&+F_PE&_Q&E<>%4[&B9#0$,V4I85%PL7%A0/1Q04$Q>`%`,2%A01@Q0$ +M&8Z+7SN`F`=Y-9JSOKS)X:7&-3@,%A,,"@D-"A`1%Q,7$PX1#XT1$!`1$@X7 +M%!%:B:=9(SM69\O*/2@46499.A'PP+"2\Z"Q$:'@\0"@H)"0L)"X`)%!<3*",,"@P1%BDC +M&!**40V&A`2)"DO*BPG+B@R.!PA0A(3 +M%`D+%B@V?-I<>HPZ3&.'X^"@P/#!,E%HA]/S0U$B$9(1XD&B4>'1P;(B\3 +M.2`/%`\)"B(F&=,N)QXE$1`C(!`)#1,8#0T,#(()&@L)"@D3%VE$#0\C&AP3 +M%PL*&Q`7&AL:?+(GLZ3&/(P-"0H)$1@?%GQ]/3(V%!4@&!(4%Q03$1H4&!04 +M&1T-$1L*"182%>&2 +M$181#0D)"PL,"0H,"PP4%AD6%QD6%Q41$!82#PX7/UV/-PD*"0D,$@T@+D]4 +M*3=1120<'!L9'D!M5DYM;4,;$1`.#@P+#PD3"@H,-S0:'Y"83)*"@L*#0T;'2,Q/%I;6#F`%!\83UU31#!,03`.#A`.$`T- +M"PD*#`H*#`P?+A\9('FWNYG&`44*@@E<$!88'!H;$1@1#0T+"0T/"PP0#@X1 +M$18='!L:&1L;&!85'!(5%B@4#@PNVGT*#PL)"A(:'!PQ6EM;6A0/#A!?;4XR +M(#,X)Q<7#!$-#0X+"@H)"PP*"A='&18@3TESF,8`NH()5!,6$1D9%QP1%!06 +M"@D1$102%1@@&1P?'!86&AP<&QL<&!48%1D3&!8R$`P9,K81%@P/%1$7%!`: +M/%M;6C,<%Q`T;4`9#A<:#Q$<&!$+$`\-#@L,"0J!"04,#10@*TJ8QD)I"0H* +M"0D,$Q47&1H4&10-#!(,#A<=)26"2Q@+`"@J!"1`+$A<4$!@7#@P/%`P4$Z.XR(#&1<>L@UTJ-CTO-28P'AH8 +M'!\;&1@8$C4:%A<7-CD:"@D*"A00#Q(0*#U@21,/%1(3%!L?*!\5$1$>&A,/ +M%QH8%!X2$1)J:HO)#&%;0+"@D+"0D.&!P3#!P;#0P2#PP6 +M,(AH9&Q81+1@8%AD_.A<+"0D+$1,/$Q$/&$M' +M(!,?%!,/%B(I'!(0$R$A'1`6'AX6'A83'1(7'`H/"PDB1A46)'MQEI#&%*P1 +M#`H+"PD-("@1#!L9"PP,"@X8AX?&&-!]*"4P/BHI)AQ4#,K&A,H)"(:'1@-"P\9&Q-/A,8!N]"$QA#%N1\3 +M'!\8&!H0%A<8$147"H`).`H,#0X3$!<6%!@B1#LK)A82%B$J&C,8'QX1"@\O +M'!HA2S8?020;)!\;%B$8$A8O#R<)4D()#QH?=X[&$9I2-!L8&AL>&QP/"PX/ +M&B,3>8+&!,V/95>RA,8.HR4.$1(/%A(3%!87#1(1@0DX"@L/#Q46&147$AM! +M/S`I&!DA/T8M/!46&Q0+#"T>&15T4$[+!\=&QT8$R(,.0D7%PL3.L)PRXS&"(<^'!H<&QL."X$/`PTG&L^` +MQAO)XX$D-D,3#4VPRL;&R&8D'0X1%1,:'1P9&`\-@0DA"PH.$1$0%AL5&A89 +M/#\C*ALD0#U9+C80$AD7"PLI(Q(18H#&%\?$J&UE33$G'QL:%AD/-@DE(0D+ +M2ZP9RHW&!G\<'1L0$1&!#P,+$29=@<8;V-JGME-2&0HG2N#&QM,J&0\-$1$8 +M&AH=%2`.#8()(`H,#A86%1L3%QDB/",F,BLL0AU.+SX3(R,A$1(D'PP-#(7& +M$KZ16S$<'!L7$A@*)0D)"E@L%)B-Q@?3)A`/%1<2&(`/`PP7'+6`QH#J#,[G&N!\8@`HO#AD@%Q08&PL6#@T)#`D)#PT8&A(;$Q89*BPD-#HS +M.%$I)2/,#$Z*QDQ+R,.#`T,1%-*1E90 +M&Q87#14-#@H*"1$4&QH9&AL6'2,R/4XW.S`P.T9)05FCQ,J_9!$-#`D++K:' +MQ@_(P[:C.1\:%0\*"0D1/\I5C<8+JFQ((485%PX.$A9JB,89NYY7,R&1$/#@Y4P,)&-UE9(AL9$@T3#X`*%0L-$AP<&QL:&QHA05=-2%9) +M5D`J;:""Q@)L#@J`"0$8FXK&#-&X;#H9%0\+"1,2$KZ,Q@6]7VL/1AN`#P(J +M.L2+QBZD(B(>%`P1"RFCQL9*)U=:-1XA%PP5#PD*"0D+$!L<&1P>&R`Q25E; +M2%M05#A,G8/&`G@2"X`)`0I*C,8-NG,P&Q,-"0T-$:/&N\6)Q@K*68L5,2D0 +M$!8OBXS&",QC'`P.$AH.@H#&"VDF3EI1'",?"AH1"H`)$@P0&1T9'B4A,5)2 +M5EM:6TE'QQPA(W&"&X)"0H)"0H2M]'&!S,)"0P;/:+.A,8#J"JT +MA(W&`V`)"PJ`"0$+L=#&"+\8"@D;.J_"QX3&`]DQQ)*-Q@-$"0L*@`D!#++/ +MQ@?9020*"C*BQ(;&`\U2QL.,Q@F8%PL*"0D*"A:[S\8&KB)Q@"JCL8"(BH,@`D"#"!FT,8$7AT9 +M'Q**Q@#,CL8'&BL-#@P-#F[1Q@5O)$02'\V;Q@&2L9J]'&!'T]6$I= +MF\8(RCPB&4)$"QFLT<8$E$\C3;>;Q@C'/QM,'$`E%KG1Q@.7,IF!G<8'D"A3 +M.D-<,LC1Q@.M:\2TG<8'H3=023!9>/RXOC:73Q@+4R\2>Q@6#BT!(JMS4Q@'$RY[&!(ZDQ->.U<8`Q)_& +M!-2YQ,6BU<8`UI_&!Z9<2GQ@7QA:[EED,"0H7#0P/$R,0#0P+%0HR4J*@N\/&`I12O9S&&KR)#`L)"0X5 +M%Q48'C`9%Q43)A`.#`DKA['!R,'&`9^%G,8::PH)&PL.&1\C&A0F*B(B%!0N +M'1D3#Q$*'IG3PL8)8XCV4@)"0H6'AD7+3Q-97E_ +M@("*A6L\+AL;*!<0"RF9T\#&$=E"%`P4&!H9&!I9O,;&]O;BPHG&"Y,="0H0 +M'1TE5F%E>("%$)29F;"DKI*$?TX?&Q40##>TO\81>Q$C%QD5*A,5&B`N9L_E +MY+2_B<8A8',2#A@;2UA>E)F,E)RHJJ*IG)9H(!H4(2!RW+S&$LPQ +M#!PC06EO;FE6,QPI7.+3A\Z)QB)+528>$QY;7WN$C(J;F:*FI)*6FJ2OK:JN +MGIM]:$0H%",_K+O&$K,8#1U/3I.4EH^#;UDV-^+4<=N)QB0Y2$4A$!<^8(*# +MA8>3H9.FH)2>FJ.OLJ&LH:*EG8IC+!0/6'[.N<82D!`,'6!,EJ*@FY^BEV50 +MS])IT8G&)CEN>"`1#QM-?WR$@X.2 +MB:2@JYN;H:BLKK.QM+F"2B03'WN^R;;&%)@/"0D-0)MEF9FBF9V9BJVJ-1=' +MQ(?&)USC@QL;&2$8#4A_CX63A9F/E**?FIJPL("Y"[NQN;O`O+EO&Q$NX+3&"M??QB`)"0H-#B93@*([ +MIZ:GFL[`5J>=U]K7OL_/U\;&0>-(&R$.(2`9$A&QP95&5<-"`:&1=.@H^DHJN1K:>O +MIJN`N1"ZK+N[NL#$R,O3U\IU)Q6J +MIV!6N,:[LQ.8'2,X(1XL969H21PH)@TEBX^1FY&GIYZFJ[FTN[6ON+VOP,/` +MRLG7R?L;&&2PC(5]8&"%59FB!<@DF&B4-AIZI +MG)NC@*\5JKNSN[B]O\C#R,C)R<[+T]>V01%9S*;&`=_0A\8CDPH)"29*%CI0 +MS\[DHRW3NZZPL+&YP9*2Q!$9&21?8RXK(&AQ@7(B=$PP&"!5FYV;IJ>GKZZN +MNJZXO;[`Q<3(RL_&UM/7T\50)WNEQ@++JX&'Q@^\&@L)$3!3&8I/S^!U*HBJ +M@+`/N,')NL>X%AL<(E]E5!\:4H)R)(!T7S(8*E2;HZ2FLKBOMK2XN[^]OL3# +MR,K,RM?.U]37O)8X4,6CQ@.^F42YA\8ATN\G& +MQM&K&!X<(5]E8C,;/H)R)8!_=EPZ-RM;H*6FM[6RN;>[O;Z]P,;$R1E+3#O+W!R8#&!K(3%(@@',R'QA*3 +M#@D)$G:R-!J5U;Z1V'QIC;'.@,8+>!HI(A]D969F)R9K@'(0?H!_?W>!@DLI +M*%R:K;&ON;F`O1.^P,3%R\C)RL_3T-?=W-W?QS0GKY#&"]ZMH6E'146I<7Z> +MW8#&!G4)$!X/#J*'QA*[&0H*"0VUSS1ZXK]UIXVM4W'#@,8N:1$6%R%D96=J +M5AD_N\"_ +MN[R]O,#(@,D'R\_7T]??X\V!Q@+$1QZ-QE>P:3HU*RLP-2!!8B<7&HG&2`D) +M"A`.#FS&QN"\QF]!R<:5QL9>#`D)G\_/5@\;'A$/M:/3QL[&RAD;'QPI96,D +M9')?(D=R-!==?QL45H*`79"0A5,7?<"\@+T)O[_"R"YSQL?#50T<(1PP95@7+G)4$C!R&A0B6A44)GDG*9"0CHE>*G&\O;V_ +MP+_"R!O+W&Q,+#RM3(TIO&7\6_N)%T0B4:&S0?"@X0#PD/ +M#@L*&!`.%J+'-0]S&A%WE1,VQL:0(3>WS[^MP9%"/:*E6"6\QA\0"Q46%1A= +M*A041B`4(AT5%!(6%A,4&10:D)"&@I>0=$^5M\G&Q,?0UZ+&0]QV$@P0#0D* +M"0\*#@\-#1D2#@XYQA,-&A,1%6D3%KB]P4.MQ\"TT*F&)4JDHZ0Y2Y(4%@P6 +M%1(/1A04$A<3%!,1%100@!,'%!,4%Y"-73>`EP=W+YVVP\+.W*7&3"T,%1,, +M"0D-"A`0%1$7$PX0#I<0#Q`0$@T3$Q!6?IE&'215R->T.58BLZ:SCQQ5E8>%Q<0 +M"AH?$"$8$A2`$`@4$0X4#@X6"Q"!$PM6&A06P].TC,;&M%.GQB%O"0L0"0X, +M"0D*&!,8$1,+%!<6&189&!@;&142%0P-#`P.@!L3-_:X#A(0#A44#A<>#0H4 +M&PX@$@Z!#!@+#`H/"AP9#`D1#!$2$Q0<&,2D&5W&QN#1I\95,0D*#@\,"@P) +M$!460B46$1(B'Q\@(2@N-1DV$S<5$!%B@5"PP/$`L?$1D7834;.\:4.K^GQCT_"@D+$0L)"Q80 +M(QLP,AL.$B,=*"DD'B8G(CD2)R,2%A0*#!@;),+,)Q\9%0P+"2DW"Q`9'0\/ +M"@H)"0L)"X`)%!00'1H+"0P1$QT<&!0I&AH>K#H=KJ?&'5\-"0L1"PL,%R%@ +M'"%A(4%103$!D3&!04&!@-$1<*"1(0$]-]6A`0#`T0%PH*$Q4< +M#A`,"PD*"0N`"1@*#0H+)BD3'1T3(A<-"@H-#1@9&!@Q&1R#I,8`1H`)-`T- +M$1<4-6,_,247%A19J<%@H) +M#`D+#Q4,)3Y&.AH;+"D;%Q83#PP/6UEB2$@]%!`0#`X-#!0)"PH/$!T:'!W$ +MQL;%G,9@:`P)"0H+#Q0.%!4/#!<1%A$+"0D+"@L)"@L+#!,6&!87&!87$Q$0 +M%A(/#14X3W@L"0H)"0P2#!PH/D`A+3XW(AP<&AD=,4U$0$A(*Q80$`X-"PL- +M"1`*"@LC(A<9C95)CIS&`$"`"0X*"P\3#Q06$@P9%181"PF`"BD+"0L,"PL4 +M&!@7&!<6&142%!<2#PL/%2EV-S(*"0H*$`P;(#4])S=$/"2`&PT<*#]$/3)% +M0B84$!`.#8`+#`D/"@D*'A\8&F)I'WN;Q@&Q#H`)&PL*#A`6&!L:#R`9%0P, +M"0L,#`D+#`P*#!8=&!J`%QP:%Q88%Q,3#1H-#!(WL1@*"PH-#!L=("LR1$5" +M*8`4'Q@[1CXY)SDQ*@X.#PX/#0T+"0H+"@H+#!DC&A<==;:ZF<8!*0J""4D0 +M%1<;&AL0%A`-#`H)#`X+#`\-#1`0%AL:&AD8&1D8%A4;$1,6&Q,.#"W.7`H. +M"PD*$AH;&BI$145#$@\.$#](/B8<*"X>%Q8,$(`-#PL*"@D+"PH*$RX5%AM) +M07"8Q@"T@@E2$A40&1D7'!`3%!8*"1`0$Q(3%QL8&AP;%A89&QP:&1L7%1@5 +M&!,7%"8.#!@FH1$6#`X5$1<4$!@M145#+1L7$"U(+!0,%!8/$1P8$0L0#PT- +M"PR#"04+#!,=(D.8QB!A"0H*"0D,$Q47&1D3&10-"Q$,#A48&1LF*2HY+R`A +M&AN`&AX8&A@;&R,7%!47)A@1%19_%2$."0H*%@T0#AE(2D@\@!T"&"(6@`H; +M#`D)$!H9"PX0$`\,%`P*#`D)"@D801\<(\9C?I3&`X<5"0J`"6(.%189&A<0 +M%PL+$1$,(2\I*B0B'APK,CDO*B@?'!D;&1@:&B`6%Q(7(AT5$QHI%AT+"0D* +M%PL0#A5(2&(X#A,9'!P8$1`0"PD)"QD7$A`.%18/%@H.#@D*"@D-(@\/&D(G +M*\F2Q@+))PJ""0`/@!4K%A(.#@H2#1`.8(>EH)Z=H7(F)BDS.#$H'!P9&!@; +M&!L8%Q<2+!L5%A8G*1F`"0(*%@V`$"D\14\P#0X0$Q87&QD<&!$1#!@6$Q`3 +M%A42&@T.$0L0#`D*#@D9'1P='[62Q@*_"@J!"1`+$1<4$!@6#0L/$PL2$J"T +MR8#&'&A`6&QL6&A42%Q$3 +M%@H-"@D9,Q$5'7IGDY#&%*D0#`H+"PH,'B00"QH9"PP+"@T7@X?&$M%V)R0J +M-"$F(Q4B&Q4=%Q,1%AB`&0(Z*Q&`"30+#A02$A$/$B(B)1DB$A,.%A\A&`\. +M$!<@+!&!<.%AP=(AL+(A@@+QH9&!`7(`X2(@PI#`QA*1$<((/& +MRHW&!+,5%`P2@`H+'S,=$QL9#PL,$QLAB\9-S9.\).C<83RB(D'1\0"@DL+!L='18+"Q80'C:-QA&]4Q,< +M'QT8'A`2%1D3%A$=$0Z`"3<*#1`4#@T3%!4>.BL>(!(5$1D=%2`A*B4<"1H5 +M%QP7(AX;(1X9&2(:%Q@B#A09#Q8)(1T-&QD7M(S&$\><."0F&1(?("`9'1<, +M"PX6&1%)A,8!NM&$QA#%N!H3&AP8%Q%1`6'2$8*14:&Q$*#R2`&A@Z*!XW(!H@'AL5'!<1$R,-'0D\)PD.%Q=R +MC<82P)@]*1@8%QH=&AD/"PT.%QL3<(+&!,Y\4E*OA,8.H1X-$!(/%1$3%!87 +M#1(0@0DX"@L/#A05&147$1HS-"4A&!0?,S8A-!05&10+#",9&!)S:U9)(BPU +M'QP:&QH7&1@1'PDI$0D)#Q8TCL81OF`J&1D7&AP4$0T-#@X2'1*G@<8(TYXX +M+"==:(C$@<8.?QX3#0\/%!,7%Q@6#0\+@`D\"@H-$0\2%AD5&A(8-#0A(AD: +M+#=()#`1$1<5"PH@&Q40=Z.8BG5^;#LU)QX<&QL7$QH+(PD2$@L0,<-OS(S& +M"(,U&QH;&QH."X$.`PT?&-"`QAO(WFX>+2H2#$:RR<;&QV`=&0P0$1(7'1L9 +M%PX-@0DA"PH.$1`0%AH4&148,3`>(1D?-S!0(RP/$!86"PL?'A(078#&%\?( +MHV5<12TD'AH:%18.(@D;&0D+0:P7S8W&!GH;'1H0$!.!#@,+$!U3@<8;UQ@0"0P) +M$Q8H/"L@$Q`8$1$*#`D)#!`8&QD5%Q@;'S$S,38\+1L=&"(J/SU%L.%R@!`# +M#`L5Q(C&#]2C/B(=&A$/#0D)"A,^QLZ,Q@S'B%M>-3*C,\+"XE +M)BDW-S%"G,3*OUL0#0P)"BNUA\8/R,*UHC`>&!0/"@D)$#W.3XW&"ZEM1B1# +M%!8-#1(29(C&&;N<4RL@&!<;%@X.#`ZN2PL+"0D,0M:)Q@W"L((?&A0/#`H*'$4> +MT8S&"[-I319*%P\.#QH8FHG&&,238A8=&!8/#@T-4\#%/"M$1!T8%Q$-$0Z` +M"A4+#1`;&QD;&AH9'#<_.#1!-T$Q)&6>@L8":0T+@`D!$Y>*Q@S0M<&!4/ +M"PD/$1*WC,8+O%UF#D,?#@X/(C;$B\8NIAL=&!(-$`HHH<;&0B)!1"D;'!4, +M$@T)"@D)"Q`;'!@<'1D=)SQ"131%/$`R2)Z#Q@)W$`R`"0$*18S&#;EK+!L3 +M#0D,#!"8QKK%B<8*S%B,$RLD#@\4*8>,Q@C,4!D+#A$6#8"`Q@MC(SQ$/AH? +M'`H6#PJ`"1(+#QD=&1TB'25`/4!%1$4W/&FOA,8"@1$-@0D!(,6,Q@S(;S`: +M$0D+#!"&PX;`B<8*U&BQ-2&-Q@;?00D.&!88@<88W#PB/D,>'!X, +M&`\*"0H)#!$9'1TC(B,]1(!%!$1%/W[*A<8(K0H)"0P)"1O`CL8*MQL6#PP- +M#AZJ#K.*Q@C#O%5((A$0'#2/Q@6Z"1$K',2"QA?,(B)%/R$B&QH*"PD)"Q,4 +M&QXK,RPB,3>!10%IOX?&!]()"@D="@D@D,8)E24:$`L/$1L.I(O&![]I2BL; +M$QB=D,8#Q"L0?X/&"'2(>'B,S,SP\)A77B<8& +M4PD)'`X*-)'&"[XL'1,1"PX,8HT_GHC&!\6213X5&%3`D<8"L["]@\8)Q[R5 +M/44Z+"$4"8`*`A,G#X`>"1L?'!L4$14-/,B)Q@;%"0H@&0E;D<8+Q;)'(",5 +M'@\Q/%S!B<8%E1@7$QVPG<8'P&XX/"\9$PF`"@<-(1-Q9GZ4-(`-`PH+"9:* +MQ@;*&`D>'0EZD\8(IV0O*3(F/C^HBL8%M0D)%DK,G<89R*)W6RX;%0H*"0H* +M)!ZIHK/"*PD*"PD)#[V+Q@4O"1T4"864Q@>@>EE".%=VQ(K&!=$1##J)R9_& +M%KZ-5$4J#0L*"@DE)\TL8(1A,)"0LJ#1!X@<8$ +ML3\3(G*-Q@B5"0L)"@D)%;K1Q@AZ#PH)"@T80Z."Q@3$?QIR@XW&"&\)"0H) +M"0H1MM'&!RH)"0L8-9_-A,8#K"J]@XW&`U\)"@J`"0$+L=#&"+T7"@D6*;'! +MQX3&`]DPQ9&-Q@,_"0H*@`D!#++/Q@?9/1H*"BB=Q8;&`\Q-QL.,Q@*0%@N` +M"0,*"A6ZS\8&H1H8"0MEQ8C&`GS&S8S&`D\2"X`)`PH)+<+/Q@5='Q(-#:6) +MQ@"LCL8"'AP,@`D"#!]CT,8$2Q82%Q**Q@#,CL8'&2$-#`L,#6K1Q@5E&2<. +M'=Q@6<.R0D@Y73Q@+7N<2>Q@6"B3<] +MH]34Q@&SSY[&!9"GK c #661B18", +" , c #78443A", +" < c #721110", +" 1 c #61332A", +" 2 c #7C231D", +" 3 c #533226", +" 4 c #978A8D", +" 5 c #AF0A0A", +" 6 c #841111", +" 7 c #783226", +" 8 c #B80D0C", +" 9 c #981514", +" 0 c #DDDACE", +" q c #881918", +" w c #B0A5B9", +" e c #74352F", +" r c #CCA799", +" t c #ADA8AC", +" y c #9B9897", +" u c #651413", +" i c #712C22", +" p c #710C0B", +" a c #8E1817", +" s c #861B19", +" d c #E4100C", +" f c #9B1010", +" g c #B49795", +" h c #EB0909", +" j c #3E302B", +" k c #8F5D53", +" l c #9E0909", +" z c #701717", +" x c #D1CBCF", +" c c #846E69", +" v c #9987A2", +" b c #291413", +" n c #281212", +" m c #691110", +" M c #BA0A0A", +" N c #DED4D5", +" B c #71291B", +" V c #918E90", +" C c #721F1C", +" Z c #664231", +" A c #7D1313", +" S c #C2B5C0", +" D c #6B332C", +" F c #D40909", +" G c #66625F", +" H c #460A0B", +" J c #446CCF", +" K c #CB0A0A", +" L c #BB100E", +" P c #867A85", +" I c #C64529", +" U c #273FA2", +" Y c #B70A0A", +" T c #A19AA6", +" R c #811B1A", +" E c #441916", +" W c #A50C0C", +" Q c #521110", +" ! c #C2BDC3", +" ~ c #580C0C", +" ^ c #0E0E0F", +" / c #481210", +" ( c #751818", +" ) c #E0DCDD", +" _ c #C90C0B", +" ' c #B5B2C0", +" ] c #A898B0", +" [ c #722522", +" { c #8F1B17", +" } c #78201E", +" | c #8D1515", +". c #7C1918", +".. c #831615", +".X c #E20909", +".o c #950909", +".O c #7D100F", +".+ c #9D7E7A", +".@ c #792C22", +".# c #878382", +".$ c #BC897E", +".% c #2C2622", +".& c #D7D4D7", +".* c #F30909", +".= c #693D33", +".- c #CDCBD7", +".; c #510C0B", +".: c #0E0B0B", +".> c #ABA3AF", +"., c #AE1914", +".< c #6B1414", +".1 c #CB0909", +".2 c #5B513C", +".3 c #65463C", +".4 c #1E2856", +".5 c #6A0909", +".6 c #8A3425", +".7 c #381913", +".8 c #930D0D", +".9 c #D5D099", +".0 c #774E3D", +".q c #403F35", +".w c #3F231D", +".e c #7B0909", +".r c #211110", +".t c #655A56", +".y c #171010", +".u c #BC6F59", +".i c #AB0A0A", +".p c #51423F", +".a c #931110", +".s c #D2C4AD", +".d c #7E6F7F", +".f c #7B1616", +".g c #BC1914", +".h c #602519", +".j c #DAD5C2", +".k c #B7AAA3", +".l c #9C231C", +".z c #67382D", +".x c #D90909", +".c c #BAB4BD", +".v c #A48997", +".b c #D2C4C4", +".n c #AF2B1E", +".m c #EBEAE3", +".M c #A30F0F", +".N c #7D1C1B", +".B c #841918", +".V c #931313", +".C c #8B1615", +".Z c #1A0D0C", +".A c #640909", +".S c #635A43", +".D c #C07D63", +".F c #8B0B0B", +".G c #651817", +".H c #C20909", +".J c #591715", +".K c #A21111", +".L c #9C3122", +".P c #6A241F", +".I c #9E0D0D", +".U c #775B53", +".Y c #A11513", +".T c #C7C3C9", +".R c #280D0C", +".E c #720909", +".W c #8D4941", +".Q c #5B5655", +".! c #8B1111", +".~ c #A54427", +".^ c #E1E0DF", +"./ c #8D4037", +".( c #A11B16", +".) c #727072", +"._ c #7C0C0C", +".` c #310E0E", +".' c #621110", +".] c #5A1212", +".[ c #B30A0A", +".{ c #532318", +".} c #521F17", +".| c #792623", +"X c #A7A29F", +"X. c #B6ABBB", +"XX c #7D1F1D", +"Xo c #908099", +"XO c #7B1B1B", +"X+ c #90878F", +"X@ c #C5B698", +"X# c #E90A0A", +"X$ c #100F0E", +"X% c #A50909", +"X& c #580909", +"X* c #2A1717", +"X= c #65503D", +"X- c #3D0C0C", +"X; c #B00E0D", +"X: c #807B7F", +"X> c #7A1D1D", +"X, c #781D1B", +"X< c #5A0F0E", +"X1 c #EF0909", +"X2 c #696765", +"X3 c #605B45", +"X4 c #CD130F", +"X5 c #D20A0A", +"X6 c #4C0A0A", +"X7 c #533E2B", +"X8 c #850A0A", +"X9 c #3C59B3", +"X0 c #C00C0C", +"Xq c #C70909", +"Xw c #971915", +"Xe c #7E1A1A", +"Xr c #7E7580", +"Xt c #5E0C0B", +"Xy c #5D0A0A", +"Xu c #520909", +"Xi c #8B2520", +"Xp c #C85C48", +"Xa c #CEC5CB", +"Xs c #6C2A1F", +"Xd c #642B21", +"Xf c #9B1212", +"Xg c #706A68", +"Xh c #841919", +"Xj c #A7999E", +"Xk c #69201C", +"Xl c #E7E0E0", +"Xz c #9C6F65", +"Xx c #C3B9C0", +"Xc c #826D48", +"Xv c #AFADA9", +"Xb c #8A2B22", +"Xn c #741413", +"Xm c #9A929B", +"XM c #CBB5B4", +"XN c #8F5341", +"XB c #783C31", +"XV c #73211F", +"XC c #911515", +"XZ c #630D0C", +"XA c #881616", +"XS c #E5E4E1", +"XD c #6E1B1A", +"XF c #DC0A09", +"XG c #741616", +"XH c #8E7F85", +"XJ c #591B19", +"XK c #8A201B", +"XL c #BE0909", +"XP c #4E1A18", +"XI c #FEFDF6", +"XU c #6A0C0C", +/* pixels */ +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XIXI.m ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XIXIXIXI.j ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XIXIXIXI.$Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b.s N.D.$ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ).S.~.~.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.c kXp N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` g I.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xx.~ & ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^ ;.uXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.+Xp.j ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X ; r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.T ; & ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^XzXp N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` g.~.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XlXMXMXM r &XMXM N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xx.~ & ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl rXp : :X#.X h hX#.X :.X.X.X.x.x :Xp.u &.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^XN.uXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b & :X#X1.*.* : dX5.x F _ K.,.nX0 _X5 _ FXFX4X4.x.xXp.D r N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.+Xp.j ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.s :X# hX1 h.XX5 8 L.LXw 9 9Xh q.|XB 2 qXCXC |.VXi.6X; # _ _.x.X :.D r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X Xp r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NXpX#.*.* :X4 _X;.KXhXh.=.| X X.< i 1 1 1.P i.G u.<.=.zX>XhXh.V | #X;.n KX4Xp rXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.T ; & `Xl r.$XpXpXpXp.$.$XM N ` ` ` ` ` ` ` `XIXI 0 ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XlXpX#.*X1.*X5.,.6 2XhXO z D.=.3.3 GX2.).d.dXrXrXrXo P PXrXg.3 , DXk zXO [ , {.KX0 _ KXp rXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^ ;XpX0 8 # | a. RXhXe qXf.L.u rXl ` ` `XIXIXI @.k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 0 & :.*.*X#X0.K R.=XDXd.=.t GX2X2.).d P P P P PXoXoXoXo ] ] v ] vX+.# V.d.U.3.P.NXhXC.VX0X0 _.DXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XlXpXF.!.~ { XXD.< > D u.'.XOXCXK.L _ : &Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 0.uXFX0XCXD.~.L.U P P PX+ P P.d.U.3Xd -XJ.f ; @ 0 0.+ g ) ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.SXcXcXN.6.|X,.'XJ.Q G G G P PXo PXrXo vXo v v v ] vX+Xo vXo v ] ] ] ] wXmXm T.>XmXm c.U 1.z q.KX0 +Xp.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X@ IX# 8.! [XrXB kXj v v vXoXoXo v vXoX+.U D , @.j @ c.m.m ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` eX7XcXcXzXb.V f.8 pXd.t.d.d.d.) P.d P P ]Xo PXo vXo vX+ v vXo v v ] wXmXm TXm T.> w w ' w.d.W C RXC #X0 I rXS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.+X0 h.* 8 f { k.3Xz.b v vXo v v v v v vXoXo.k g @.s c.s r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.WXz.mXS.j i.N aXC.V 6.O.6.d.d.)Xr P P.dXr PXo P PXoXoX+ PX+ vXm ]XmXmXmXm T T w.>.> w.>.> wX..> c ,XGXh.K 8.n &Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X@.NX0.*.*Xq $ $Xp.sXzXzXoXoXo v vXoXo vXoXo P.s @X@.q o (./XM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.WXz.mXS.D .X>.NXh qXb 2.o.8 ,.).dXo P P P v PXoXoX+ PXo vX+ vXm VXmXm Xm ] ] ].> w T.>.> w.> w.> tX..c wXz e i , #.g &Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.jXMXi 8.*.*X5 $.,.p.yXs.uXo v vXoXo v vXo ].kX3 O.yX2.U 3 A ;XM ` ` ` ` ` ` `.-XS ` ` ` ` `.WXz.mXSXbX> <.fX, }.= 7 |.M.oXC.0Xr.)Xr P P PXo PX: PXHXoX+ v V V XmXm ] ] ] w w w w wX. TX. w wX. S.cX..cXj c C |.K.g &Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ).^ 0.b.| W.*.* + 5.F.yX$ 3.~ k v v v v v T .%.j 0.4 J.4.4X9 U.d.-.-XS.- * J J J J.- ` ` `.W ;.m.j.P X iXdXZ > i [.NXhXf.! { A.U P PXo P P PXoX+Xo vXoX+X+XmXm VXmXmXm ]Xm ] T wX. w.>X. w wX. tX.X.X..c SXx t c [XhX; 8.u N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XS 0 ` `.bXiX0X1.*.x 8X0X-.% k.~.n.WXo v v ] ] ].) 4XI.- J J J J U U U U U U UX9 U U J J JX9 * w ,.W @X@Xd.< [.|.< Q.=.P zX>.N s ,XC.8.l kXoXr P vXoXo v X+X+ VXmXm ]XmXmXmXm ] T T T w.>.>.>.>X. t tX. ' S S.c.c.c.c !.+ iXh 9 8.DXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xx.m ` ` `.bXbX;X1.*XF.I.[.Z.% j.~ & @ 0Xm ] N !.d &X9X9.- `Xl.Q O.4.4.4.4 O O O O.Q.4 U U U * J.= i @.u ( mXnX>X>.'.p G.t.z C D.=.| a.V.oXb cXoXo PX+X+X+ vXmXmXmXmXm ] wXm T T.> w.>.>.>X..> tX..c t.c.c !.c S !.T.T.T.TXj k. ./.n &Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 0 ` ` ` ` `.bXi L.*.*.X.I lXtX* j B.D.s gXIXIXI J.Q 0XIXIXIXIXIX+.4.4 U U.p.4.Q.% O.q.)X2X9 J J /.G 0.uX, - 1XnX, iXP.QX2X2X2.3 D X.NXeXA s.C.6 cX+X+X+ w v ]Xm V ] wXm T T w wX. w wX.X. tX.X.X..cX..c.T !.T.TXa.-.T.-Xa ! c [.K.g r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.bXi M.X.*X#X;.!.i l =.w.~.$.m @ ` J.-XIXIXI @ 0 0 gXoXo ] .) j.4Xo.- ` `.>X9.]XnX@Xb.N - G.=.P DXkXJX2X2X2XgXg G.= CX>.= 7.V W.6.dX+X+XmX+XmXmX+ Xm T T TX. w w.>X..>X..> tX..c SXv S ! !Xx !Xa.T.T.-.T.TXa.c kXO.M :XM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.bXi 8 h.* h.[XD <.i./.3Xp.uXl ' J *XIXIXI.sX3X@.sXo v ] T T .U./.b ` ` `.^.J }.U i.P - G GXd z.N.<.pX2X2XgXg.).).) G.3 [.N a f |./ P vXmXm V VXm ] T T ] T T T w.>.> wX.X.X.X.X..c.c !XaX.Xx ! ! !XaXaXaXaXaXa.&.& S.WXA.KXpXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.bXi.,.X.*.*X0 eXVX;.,.UXp & J J J `XI @.s.q.+ @.s ] ] ] T wX. 4 e.$ ` ` ` zXD D DXVXJ G G GXdXGXV 3.QX2XgXg.).).).).).) ,.PXe.@ 7.I.WX+Xm ] Xm TXm TXm.>.>.>.> T.>X.X. tX. tX..c.c S.T ! !.T !.T.TXa.TXa.-Xa x.&.& xXj.W a #.u ) ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.m ) ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` xXi M h.*.* _.W.UXOX%./ ;.W J J J @ @X@.%Xg @.jXj ] ] ] ].>X. w ! ) g.W N.bX6Xn.f . (Xk G GX2X2 1.z DXJXgXg.).).).).).).)XrXr.U.S D R f.Y.WX+XmXm ]Xm T T T T T.>.>.> t w.c t tX..c S.c.c ! % ! !.T.T x x !Xa.& x.&.&.&.&.&.k.WXi ; & ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XS.9.q y ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.bXiX;XFX1 h K.6Xo ,.OX U.6 J JXl 0.t j O.+.s c ] ] ] ]X.X. ! ` ` ` NXlXM.G zX>X> .XJ G GX2X2 GXD. XD.pXg.).).).).).).)XrXrX:X:Xg ,.|XhXi.@.W.v ] T TXm T w.>X..>.> w w tX..c.c S.c.c.c ! ! !.T.T.T xXaXa x.&Xa x.& x.&.& ) ).c.U a IXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XM.k.9.qX$ t ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.bXi.( F.*.* F.LXHXo y ^.4.L.dX9Xx.q.q.4.% c g k.v ] w.cX. x ` ` ` ` `.k Q .XVXV C.{ G GX2X2X2 G > C 3.t.).).).).).).)XrXrX:X:X:X:X:.U.U e.C 9.WXm T T TX..>.>.>X. wX.X.X. S.c.c.c.cXx.T.T !.T.T.TXa.T.-.- x.& x.&.& ).& ) ) ).&.v 2Xf.uXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` `XS.jX@X X .c ` ` ` ` ` ` ` ` ` ` ` ` ` ` r.g.H./X@.qX$ O.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XMXh 8X#.*.*X5.l P.4 J O ^.h rXI 0 ' *X+ @ 0.+ c.W gX.X. x ` ` ` ` `.+.'XV D D [.J GX2X2X2X2Xg GXd D -.).).).).).)XrXrX:X:X:X:X:.#.#.#Xr , }.CXb kXm T.>.>.>.>.> w wX..c !X..c.c.c.c.T ! !.TXa.T.T.T.T x x x.& x.&.& ).& ) ) ).^ !Xz |., & ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` `.m 0.s.kX cXcX3X3X3X3Xv r.D.$.+.kXS ` ` ` ` ` rX5.xX5 = O.yX$X$X: ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` rXh 8XFX#.*.XX;X2 J J U ^.+ @ @ 0.t.+.s.+ 4 ] kXz.v ! ` ` ` ` ` cX6Xn.f .XGXJ GX2X2X2XgXg.).t X.<.p.).).).)Xg.)XrX:X:X:X:.3Xg.#.#.#.#.#.U.U.@Xw ;Xm.> w.> t w tX..cX..c.c.c %Xx.T !.T.-Xa !.- x.TXa x x x.& x ) ) ) ) )XSXSXS.&Xx kXfXp.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` `.m.jX@ cXcXcX3X3X3.p.q.2.q.. I ;.~Xi.@ ,XM ` ` ` NX4X1.* FXPX$X$X$X$ O ` ` ` ` ` ` ` ` `Xl N ` ` ` ` ` ` ` r |.,X5 h.* h.l J J J J.4.2 =.p.4 O O O.% wX.X. %XMXz g ` ` ` `.0 < XXOX,XnXP GX2X2X2XgXg.).).U.PX7 G.).).) =.w.)X:X:X:.q o j.#.#.#.#X+X+ V c ,..XwXzXjX.X. S '.cX.X..c.cX..cXx !.T.TXa.T.T.TXa x x.- x.&.& )XS.m.m ` ` ` ` ` ` ` `XM./.K.$ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` `.j.+ c.2.q.q j j j j.q.q.q.h ; &.u.~ #Xh RX> r ` `Xp h.*.*.oXZ.yX$X$X$X$.T ` ` `Xl k.^ ` `.D j.^ ` ` ` g ` ` `.k q 8.xX#.* d J J J J.4 ^ ^ BXs O ^ O ^X9X..k.& ` ` N ` ` x `XJ.] }XV C C =X2X2X2.Q.w G.).).) 3 i -.).).Q oX*.w.)X:.3.r o b G.#.#Xr.tX+ V V VXH k.U { k T S.cX..c.c.c.c.cXxXx.c !.TXa.T.T x xXa x )XS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` gXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` `.+.% O O O.y.y.y O O O.%.% |.D &.D.(.K.VXC R.f g rXq.x.1 MX8X6 QX$X$X$X$ y ` ` ` g o.Q ` x E n.Q ` ` r E.# ` ` ` r q 8X5 hX1 J J J.4X9X9 U ^ 1.~.4 ^.y.4 * 2.3.^ ` ` ` !.:X: HXt i [ [XD 1X2X2 G.w oX*.t.)X2X* QX>.3.) oX* o n.w.Q b b o n.%X:.p o o.# V V V V V y c.6 {XzX..c.c.c.c S !XxXxXx !.T.TXa.T x.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` `.T.c.c.c yX:.).pX$X$X$ OX*Xb ; ; :.i W.MXf.YX;Xq M.M fXUXyXuXu q QX$X$X$.% ! ` `.W.Z.r y.D n n.Z t ` 1 b n.T ` ` `.$ a.,Xq & * J U J U U J U O.4.~ U U UX9.pXP.p.^ `.^.%.:.r HX<.f. .NXn.7.QX2.q b o bX*.t j.r o =X7 = nX* o n nX* bX* o n b.w n o o.t V V V VXm y y yXH c.6XzX..c.cXa !.T !.T !Xa x.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` `.^ y.pX$.r._.M.a.FX%XL K.1 L.a M.xX;.!X6X-X- H R zX-X$.y.Z.p ` x.7.Z.ZX* j.r n.ZX* g o b.r j ` ` ` `Xz.L.jXI.- J.4 J J J U U O O.4.6 U U U U.q.7.U `.+ oXJ o H < (Xn ..'.`X*.Q o b o n bX* b b o n.J Q nX* o.r.r b b o b n o bX* o b.q V V VXg.w V y y y y 4 kXiXz.c ! ! !Xx.T x.m ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` `.z.F $.. R.! W $XF F.F $ K.[ f Q Q.J Q.G z.f <.` o.ZX$ y.v.r.Z.r.y.y.r n.y.Z.7 n b.r.r.t.$.$ rXz.~ 2XK.~.QX9 J.- @.s.q.4.4 ^ UX9 UX9X9 U.4X*.U E EXJ /.' XXV [XV.<.` b b b b o n b b n b o b nXJ.w b b.r.rX* n o n.r o b o o bX* VX+.pX* o.t y y y y T TX .+XN.u S.T xXS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` `Xz M.[ f.C.C K +.MXA.F.oX#.*.1X8 u u -. EXk.{XP.{.{ - (XJ >._.]XPX.SXV u./Xc.(.O.].h.JXy.`.R /.h B B.h.uXI.m./.n.nXK.7.}.Z.RXU.i l f {.l.' ~.'.B.f X zXtX- HX-XuXuX6X&X&X6XyXuX&.AX6X&XuX mXu.]XV 3 -X7 1 j = 3XP 1 =Xd =.6X= DXf a.3.~XG.aXw.h.!X&Xu.R.}.h B.~.h 0.mX7 I.n.LXK.h /X-X6XuX& j.q.pX6.A.' XXD }XO.; / ~X&X6.AX&.E.E ~.E.A ~.E.A.5.5.A < < <.hXs.hXU.o l M.M.K {._ B 3.! z.J /XkXP.}XD E.}.k.=.} C.U ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` `Xl.V m 8.* WXh 6 W W.8 ~ >.J j.D.9.h.~.~X7 OXG z Q.]XPXV 3XJ =.P j.P 1 - 1.PXd 3.N ,.|...6./.K #.I.!.F.E.e HX-.h B B.h.z @.%.n.n.n.L B.}X<. >XkXJ.3 jX6.AXU q.f X <.;X- Q ~Xy.E.E.5.eXU.E.eXUX8.EXt.E.5 p (.< 6 Z.~., K., LX0 8.M |._Xd 7X;., W.O.G.<.}XP E - E -Xk.}.^ ` ` N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` 7 I $.* _.VX;.FX;.VXi.| u.v &.D.~.6.6X7 O /XDXb.J E = C =.PXJXkXdXX - -XJ.PXJ.PXK D.! R.=Xb 9 f.V.Y.8.o.e H.}.h B B.7.m.%XK { 2XK B.{ /.! 6 [ iXA mX&.5Xt AXn (XZ ~ ~Xt.;.;.5.e.E.e.e.EX8.o.F 5.o 5X%X% l (.,.u.u.~.g.H +.,.l.(.(Xw.8XKXw M $.[.aXb.!.'XP EXJXDXPXP.k N cX>Xx ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` `.j.~ $ YXq.[.o l.a #XhXKXXXC.u.9.D I.h 7.q O o Q. } Q -.]XJ.].J z u.]XPXXy.EXU uXnXG C p.' Q QXt.;.E.eX8 YXq _.x +.x.x.1 K FX;X5 K _ : I IX4.g.n.g.n.YXw.LXK p.8 K M M $X0 $.i 7.h.X-.`.R.r j cX@.sX@ qX8.o.E.A.'.eX8XU < A.fXU QX7X=X3X3.2 D R.N 7 e e 7XXXhXh q a.K.MX; 8X4.D.D.u.u.DXcXcXc ;.L., #.M.!.8 MX; LX;X0.L F.HX0X5Xq +X0 8.@XkXJXJ - }XP.b ` ` `.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` rXL.1.X +.XX#.H 8 f f.V 6 p.] R 9 # LX0.( z.<.G Q.]X&XZ.A.A.A.AX&XyXZ.A.AXyXUXZ p.E p A aXGXh..XAXAXh. . |Xh q 9.KXf.! z i <.'.;.R.:X$X*.q 3X@XNXK.o.e.E.5.E.EX8.F AXZ uX> } DX=X3 D i.= Z.SX=.=.|X>X>X>X>X>X>X>.|X=.SXc.0.6./XcXcXc.~.,.( # 9.M.O lX; W Y.[ _Xq.1X0.g K.HXqXq.g.~.~ A. .}.}.+ g.U.PXB ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` `.b.g FXF F F +.x W.[.[.M f 9...fXh.N RXOX<.|XkXD (.'XZXU ~X& ~.A ~XZXt.AXZ.AXU.AXZ.5.A p.f.fX>. Xe X. XwXA | qXe s.f.fXeXh.(.| 6.V |.F B.`.R.R.R b.z.u.sXw.EX8XU.EX8.!.FXZ XX>X>X> [ D.=.=.SX3X3.SX=.6XC.V | | |Xh.|.SX3.S.2.3Xd 1.2.3 Z eXf.I W $.a fX8 #.M M $X0 MXq.H K _.1 KX5Xq _X0.H.l 7 u - E -XX.}.^ `.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` I _ F.X.X F.H.[.oX% $.K..Xf q.NXeXeXO.N.'.<.P m ~.'XZXZXuX&XZXZX.N RXV D.SX3X3X3X3.S { W.M.M $ f.6XcXcXc , 7XiXK 7XB.zXs z z. .! W.a.a.IX; 8X; W.[ MX0XqXL K _ _ F KX5 +.g.~XNXw. -.}.+.W.l.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` &XL l FX1.X + F.M.Y.C.V mXh qXh z mX>.'.f u u.JXGXuXZX& Q Q.' u u.]X<.J.G -Xk.G.PXD CXD ..fXG.fXhX,.NX> X R.N. .Xw a |XCXhXCXD q A A.BXn.{ 7 /X6 ~XD / 3 &.j 6 9 ( W.[.a | 6 6XGXO.V.MXf s 7.SX3X3X3.S.3XV.NXh | f #./XcXc.L.,.( _.Y.lXw.M l.! . C (.!.!.F f.K.MX0X;X; 8.[X0.[.HXF.1 F + +.x.x + _Xq { u [XJXbXiXz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` `.$.xXLX0XqX5.H.H.H 5 $.V. .< zXhXh R..} 7XD f 6.Y.B EXz.9XwXiXb L.x hX5XL.[.f.. W f.K $.M./XcXc.UXcX3.=X>X>X>X> R qXb.6.K M $ M 5.[ WX%.i l.8 qXe XXG.F._.K.a f f #.M _., _X; K _X0.H F FX5.x F K ; ;.6 s.l.} e `XMXp.$ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` `.$ _ F.1X0.H +.X h FXL f |.fXG XXh R. XG m z.'XyX&.'.]X<.G ~XJ.z.=XB 7 DXs 7 i -Xk.h = 1 ZX=X=.= D D D [XVX> .Xh.NX>Xh.N aXe {XeXV {.CXh 9Xf.{.}XbXiXA..Xf.C.N.P -.(XbXw _.x.xXF Y M. | K fXCX; 9.(XcXcXcXzXc.~ WXf.YXh.NX>X,X>XAXf.K f f 9.iX%X%X%X%.a sXA.fXn.e. .I |.( a.M #.( #.H 8.nX0Xq KX5X5X5 +XF.g :X4 _X; XXJ e.P.n.P N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` NX5X#XF FXq.x F.X + +XL.KXCXh.<.<.G.< 2XX A aXe a.N.N q RXeXhXC.N q /.} e.NXA | a.C | [.= eXi #Xq.x.HXFX;XL..XC $ #.V.K L #./ ;XcXcXc.g $X; 8X; _XfXCXC. }XVXkXsXdXkXnXn A.8.8.BXA A A.O.O...KXKXC 6.VXK 9.IX0.( # W.[.g L +.x.xXF +XF.x :.~.Y.fXb.lXVXz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` `.$XqX%.x F.[X5XLX&X6.`._ qXhXn.<.; mX>X,XnXZ pXU.< uX& ~.G Q.J N ` ` ` ` ` ` ` ` ` ` ` `Xl gXN CXd.= D } i.z C.=.= [XX.NXG |XO ..| RXCXh s.NXC.` = eXw | a.Y.Y qXX.S.=.,., _ F.H.1 FXFXf 9Xf.MX0XCXfX0 f.Y.nXNXc.L.gX0 9.( L.VXf 8.K |XwXKXi.6.,.Y 8 f.aX,.PXV. A.O._ a.BXK s f a { {.VXf.(.(.KX0.,., # MX5 _ FX5.X :.u I.g 8.B iXV.b.$.L.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` k <.;.RX8 $.x.F.5 H.RX-.|.| XXZ p <.N X u.5XUX6XtX& ~ ~XD ~.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` N.$ e uX>XV D.=.3 i } e.| zXnXi.P X...NX> a.C u.` CXC aXhXhXhXh { eX= D RXF.x hXqXLXLX0X;Xf 9Xf 9.V.V $.M f.(.L.n.6.6.,.lXb.l L 9.MX0.(Xw.@.L 7.(X; # LX;.(.(XiXs.z C.O < s 7Xi..XwXiXK f.V.(XK a.M.(.L., M L.~X4 +.XX4XFXF.XX4.g.IXKXDXbXb.W ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` k m.;.R.R f.I.F.F.F.A.e [ e [.< pX,. X,XG u.`.:.; ~ ~.G -XV ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XMXz.JXJ -.|X> C i [ [ 2 ZXkXhXn |.NXn.C.7 /XhXe.BXCXh.NXh.@.U.U 7 9 F.HXqXF.[X;.MXLX5.K a a # #.VX;.(Xb.L.n./.6.l.LXb {.VX;Xf.V.Y 9XX.l eXwXwXi L.,.l.l.l.@ iXd.< p i.@.B R i e R R {.|XA #.Y {XbXK LX;.L.L LX5 IX4.x d.u.D I + A.l.|Xs.+ ` ` `Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` `.+.J.. 6._.! AX8X8 YX#.X 7.= iXOXnX>X, X.<.`.:.:X-.J ~ .XyXz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl.v e z u zX>.| DX= [.N ..f qXG 6.J.Z.<. Xf A..XA 9XX e Z.@.,.x 8XLX; FX; $ fX;Xf 8 aXhXA.MXA.V.n./.0.W./.6Xb.lX0XC.C.M.. 2.l L q.(XV i ZX=.~.6.(XK.6 7XK 2Xk.G uX> } i.|X>..XK 2 2.NXA {Xb {.Y.K f.l./X4X0.L.gXF :Xp : dX#.X M a.P.6 NXx k.z ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` N i.L I :XK.|.'X8.H +.(.= DXk XX>X>X>.G.R.:.:.: E Q.' } ~XM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XM k u u C i }X>X>XG }.f.O u.Z EXe.fXn. .! 6Xe.6 6.Y F.1.1.1 + $XLX; +Xf 9 |X;X0 a A 9 |.C {XB ,XB.6.n.6XKXf 9 |.K # 2XX {.C } D.P 1.z.6Xi.YX% {XbXw 6 2 C.JXD.@ i 2.N } i }X>Xh s s.| RXh a |XiXb.M 8.g.~X0X4 IX4.*X1 I :.XX; s 1 E > EXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` `.s ;.~.6 D [ R < z i [ C }XG XX>X>.J.:.:.:X$.: EXP.P.' i ` ` ` ` ` ` ` ` ` ` ` `XM N ` ` ` ` ` ` ` ` ` ` ` ` ` ` 3.`.].'X> CX>XGXGXD >.r.R.f z A.B 6._ 9. .I.*.X h.1.HXLXF.[ $ $X0 8.V | f qXCXG |.K., s.6.~XBXBXb.L s.C.Y #XA aXi iXK {.zXXXn >XkXD 6 l Y Y q 7.NXD X C.hX=.S i }.=.3 [X, R [ 2X>.N RXC 2 2 |.YXf.Y ; MX;.n : h IXp IX1.X _XX>XD >.R.:.:X$X$X$.:XP.} 1 uXz ` ` ` ` ` ` ` ` ` N.DXp.~Xi.uXM ` ` ` ` ` ` ` ` ` ` ` g =.w.Z.r.R.`.R.;.. /X$ oXnXnXG.f.f._ p 6 #.1.*.x.* h.HXL.[Xq W.MXfX; | qXAXeXC |XhXA.MXh s./ ,XB 7XB.,XhXh.( [XB.0.0 7.6 , > u.].hXJ z.I.F Y.8 i B...B < mXH g.+ cX:.0 i i.=X= DX>.NXX.N sX>XOXhXhXK.(.KXf.L.n.x d I.*.*.*X1.X.I E E i ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` `.+.=.@.N. .N.N.NX> .X-.`.: ^X$X$.:X$X$.:X- = - Q N ` ` ` ` ` ` `XS 0Xp d & d I.l 6 p <.U ! ` ` ` ` ` ` ` c = - - /.R.r.R.7 n o >X> X.N XX,.f <.E.I.H.H F.XXq hX0.H.i.H fXf a 8 L.Y.f.N f 9 R R 9 aXhXBXN.6 2.@Xi. .| 2./ , Z.UX=Xs.z - ~ m ~.J.G .N RXeXh 9Xi.YX;.g I h d : :.* h K.J.W.&.+XP.& ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` `XM.W.NX>X>XD.J.`.r O ^X$X$X$X$.:X$.:.: QXd u k ` ` ` ` ` ` `.m 0.j &X@.j : ;.n..X&.J =X7 c.^ ` ` ` `.^ =XJ.GX< ~XZ.'. -.O.[.IXq K.H.xXL.x.1X5 MX% $X;Xh 9XA 9XC ..f.V.CXe q.(.6XB.@ {.| 7 7Xb i i Z DXG.0.0Xd Z.h Q.}X7.{.{.G m 6Xn.PXsXn p.O.E ~.b ` ` ` ` ` ` ` ` ` ` ` ` % g.+.U.= [.NX>.N R.N.YXf 9., MX4 :.*.*.*X#.H ; 3 E o.v ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` `.^.%.y.yX$ ^ OX* OX$ OX$X$X$.:X$.:.y.J -.]XM ` ` ` ` `.m.m.m.mXS.sXzXBXi {XCXe / BX7.wXZ DXM ` ` `.k.}.}.' H ~X&Xy.<.; E.{ -.G u u z . { K.I.C L + 8X#Xq _.1XLXq.I f $XA q RXAXf.N.f A.Y a. .|.6 i.| 2.6 1 Z 1 , 1.2.z.{.h.3.{ 3 3 ZX7.2.2.2X= 1.h.G >XD.. 6 p pXt ..b ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XlXM 4 k D }Xh.NXh a |.K #.,XLXF d h.X F / E o = ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` `.T.% j.p G.Q.q 4.%.qX$.%.:.:.:X$.R.P.J.W ` ` ` ` ` ` ` ` ` ` ` ` g./ D , k -.w jX7X7 /X-X-.p.+.k.U.{.J HX& ~.;X& m u E EX7X= Z 1Xd X A 6.8.B.YX5.K dXF 8.H MXL _.I.a.! R.NXhXh.. z (. XhXOXX 7XB.=.=XB Z.3.2.S 3 - e.{.{ 1 =X7 3.S.2X3X3Xv `XS 4Xd QXX> X a.! f 8X; +XFXF FX<.7 EXM ` `.& ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` `.bXg GX:.Q.q.t j.%.y j.:X$.:X$.`.h.< % ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` g 3 j jX7 j j.7.7.%.w.w.w QX-.; /.;Xt.< k.U =.2 Z.SX3X3Xd ..B.aXA $.[.Y.K.XX;X5XL Y.i.H.!.8 9XO.NXe {XnXO.N aXCXOX,XK i e.=.0.2 1.2 1 3 1 3X7.PX=.2 [ Z.SX3Xv ` ` ` ` NXz.J ~XUXZXt.A.E.eXw.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XMXz.@ }X> {.. f.OX8XLXq.x.H.O.JXM x.p.3 ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ).#.) V.% O.%.Q.y.y.yX$.:X$ b E.' k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xv.t j j.w.w.7.7.wX*.7X- / /X6 ~.' g ` %.3.3 Z.SX3X3X=Xh.C.K.V.aX%.I.(X5 LX#.X M.i.i.I 6 < 6.NXO.N R.NXOX> (.NX> a a C.=XcX7X7 ZX=.2.3X7.2X7.S i .X= c x ` ` ` ` ` ` `.kXJX6 HX6.A.5X%.1XL.LXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XS.k.WX>.NXe.C 6X%.I M.1Xq K.~X* n o N ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` `Xv G x.qX$X$ G OX$X$X$X$ ^ =XJ.P.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xx 1 /.w j.w E / HX- /X6Xt.' g ` ` `.+.= [ ZX3.S.S i CXV C. X% $XwX;X;XLX#.i.[X% l._Xy < 9X>XO.N qX> X.N a.N.N 2Xs.3.S.SX3.2X7X3X7.SX3 Z [.|.0Xj ` ` ` ` ` ` ` ` ` N.J / H HX&.5.X h.x 5.W ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XM k iXeXO.K 6.8 YXq 8.8.Z.r.r r ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` x.Q `.)X$.:.q.%X$X$X$X$ b D u.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^.2 E.;.;.; Q Q.w /.;XD g ` ` ` ` `.W } i.SX3.SX= zXV.P.P.!.i {Xw #.i + + MX% l.[XZXZ.NXhX>X>Xh R [ [XK.| i.3.S.2.S.2X3X3.SX3X3 Z.= }XzXv ` ` ` ` ` ` ` ` ` ` ` `XB.]X< ~Xu.e F.x.*XqX8.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` N kX> } X 6 W l W.[XU.y.r g ` ` yXM ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` `XSXg ! t.q.q.%.q.yX$X$ O - i ,XS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^ kXuXu.; QXP.w Q >.b ` ` ` ` ` `Xl.WX>.|X=.S.S =.G C.P.B 8XKXw # 5XL.H.H Y.o.O p.'.NXhX> XX>.|.| [.|.@X=.SX3X3X3X3X3.SX3X3X3.0.$Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` gXy.5.5.E.e.8 l.i lX8 g ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` %.U.N. Xn.O.F.I $._ Q.nXMX X$.+ ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` !X `.Q.#.q.%.%X$ ^.'X>Xs.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.vXuXuXP j = >.b ` ` ` ` ` ` ` `Xl.WX> }.0X3.S ZXd [.|X,. 2.[ W.HXL 5Xq.F } m uX,.N 2 [ D.=.= D [ iXB ZX=X3X3X3X3X3X3 cXv ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^X&Xy.E.E u =X8.E.E.A.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` g 7 i X.O <.F $ f 6.C =X$X= ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xv ` c c.p j c ^ n s. .v ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b ,.w Q ,.b ` ` ` ` ` ` ` ` ` `XlXz [ D.S.S.2 3 1.z 1 C C W.[.i Y Y 5.C 7 -.'X> } C.| [ }XX }XV D.=.=.=.3X=.2 3 3 /.& ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` kX&.E.eXU =X<.5Xy.P ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` N.+ DX, iXn A.O.F.a ~ ~XP.+ 4 cXB r ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.v k.W.W.n.7.G.BXz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XM ,.3X3X3X=.zX7Xs z.O.o 5X% M.i.i.YXNXd ~X> } }X>X>XD C.P - -.G Q / HX-.7X6X6 k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NX&.AX8.o.~ B.E.A k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` % kX=.@ R Z.fXV.P < D 1 ZX3 c ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` g.xX#XF _.Y.N.WXS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl.$XB.3.3.= i > z.O l 5.o 5X;X% W.6 BXt k g.$.$.kXxXM.3XuXuX6 H HX6X6X6Xu e.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` e.E.o l.6.n M.e.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b.+ i.S e.zX3.zX3X3.S c ) ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NX0.*.*.KXXXzXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` r.+XBXVXV >.< p._.F.o # l l.CXB.]XM ` ` ` ` `.b -XuXuX6X6XuXuXuXu.J.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.+Xy.e {.l YX%.e.$ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XS.k.# k.S.S c cXv ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.$X5 9./.k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NXM r./.'.5X8._ 8 l.i l 2 6 g ` ` ` ` ` k -X6Xu ~.;XuXuXu.J.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XM ~.{X=.n.1X%.eXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` N.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl./X&.e.E l M FXL.Y.e.+ ` ` ` ` `.|XdX,.'XD.J u u k N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^X7X7 Z.~X4 5.oXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^XB.5.F 5XF.*.X f.o.W ` ` ` ` ` DXD D > D >XD.PXM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^.w.hXKXpX4 Y.o.$ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ;.i.i.1X#.xXF $.o.G ` ` ` ` `.= } Z z Z DXDXVXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X .w u. .~X0Xq l k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.$.i Y.[Xq + + 8.a.E , ` ` ` ` `.2 [X=Xd.3.S ZXz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 1XU p B.6 L Y.iXb ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.k ;.[Xq.1.iX;.i._ p.e.E.+ ` ` ` ` `.p.=.SX7.P.z ,Xl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.kX&.EX8.iX0Xq YX%. .b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X e l.1.XXF.H.C.f.FXU.AXy , ` ` ` ` ` `.pXP i 3.y.yXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^.GXy.E l.i M.[Xq 5X8.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` k 2.Y.X hX1Xq.8 1 mXt QXd g ` ` ` ` ` ` `.).p n o.% O ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xz.A p.F l.i M + Y lX8Xz ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.+ 6 YX5.*X1 5.F m.{ Q.U.k ` ` ` ` ` ` ` ` `.cX:X$.p.).p ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NXu.A.5 l Y.xX1 +.H 5X8.W ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` 1 l.1.*.xX; >.}.UX XS ` ` ` ` ` ` ` ` ` ` ` ` tX$X:.c.p ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `XMXy.5 p.o Y.*.*.x.1XL l > ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl k.<.iXL.X.i 3X7Xx ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^X$ y ` G ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xz /.A p.[X5X#X1.XXLX%.o [ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xl.gX0 B.i F.[.GX3.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.q.c `.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.$ lX8 ~.5X8.oXL.x.i.iX8X8.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.D IX0., + F._.t.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` y.c ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xi 8 IXtX&.E.E lX%.F.E.A e ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xp., :X4 :X5.!.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b.B.6.l.IXF.8.[.*.*X0 q.+ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xz.g I.~ d.gXX N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.b.|./ 6 8 :.V.g.D d.g ; ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.D.~.n ; ;.l k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` NXB 7.V.,.u |.~ d.X., k ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` gXN.0.(XNXB.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `./X, aXN.gXA./ ;.g.Y g ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` gXi 1.s.D N ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.k 1XiXN ; 7 ,XcXcXBXl ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.T.sXz.s 0 ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` y.q ,.0XcXd {.L.0XM ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xv.s.j.sXS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X .p [Xb.6.w.$.D r ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.&.s.^.b ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.# y.+./ 7 k.k.sXS ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.s ` x ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` VXa.#.s.s.m V.s ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.T ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.&XS V.s.s `X .s ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.^ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.c.j 0 `Xv 0 ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `X ) ` `.^ y ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `.TXv ` ` `.& ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `Xg ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `", +" ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` x ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `" +}; diff --git a/win/Qt/qpe-nethack.control b/win/Qt/qpe-nethack.control new file mode 100644 index 0000000..46ad8c3 --- /dev/null +++ b/win/Qt/qpe-nethack.control @@ -0,0 +1,9 @@ +Files: bin/nethack apps/Games/nethack.desktop games/lib/nethackdir/[a-r]* games/lib/nethackdir/[t-z]* games/lib/nethackdir/save games/lib/nethackdir/s*.* games/lib/nethackdir/[A-Z0-9]* +Priority: optional +Section: qpe/games +Maintainer: Warwick Allison +Architecture: arm +Version: 3.4.0-1 +Depends: qpe-base ($QPE_VERSION) +Description: NetHack - The Dungeon Game + Graphical version of NetHack for Qtopia diff --git a/win/Qt/qt_clust.cpp b/win/Qt/qt_clust.cpp new file mode 100644 index 0000000..e5952c5 --- /dev/null +++ b/win/Qt/qt_clust.cpp @@ -0,0 +1,168 @@ +/* SCCS Id: @(#)qt_clust.cpp 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ +#include "qt_clust.h" + +static +void include(QRect& r, const QRect& rect) +{ + if (rect.left()r.right()) { + r.setRight(rect.right()); + } + if (rect.top()r.bottom()) { + r.setBottom(rect.bottom()); + } +} + +/* +A Clusterizer groups rectangles (QRects) into non-overlapping rectangles +by a merging heuristic. +*/ +Clusterizer::Clusterizer(int maxclusters) : + cluster(new QRect[maxclusters]), + count(0), + max(maxclusters) +{ } + +Clusterizer::~Clusterizer() +{ + delete [] cluster; +} + +void Clusterizer::clear() +{ + count=0; +} + +void Clusterizer::add(int x, int y) +{ + add(QRect(x,y,1,1)); +} + +void Clusterizer::add(int x, int y, int w, int h) +{ + add(QRect(x,y,w,h)); +} + +void Clusterizer::add(const QRect& rect) +{ + QRect biggerrect(rect.x()-1,rect.y()-1,rect.width()+2,rect.height()+2); + + //assert(rect.width()>0 && rect.height()>0); + + int cursor; + + for (cursor=0; cursor=0) { + include(cluster[cheapest],rect); + return; + } + + if (count < max) { + cluster[count++]=rect; + return; + } + + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + + lowestcost=9999999; + cheapest=-1; + for (cursor=0; cursor=0) { + include(cluster[cheapestmerge1],cluster[cheapestmerge2]); + cluster[cheapestmerge2]=cluster[count--]; + } else { + // if (!cheapest) debugRectangles(rect); + include(cluster[cheapest],rect); + } + + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x,y) are ordered topleft + // to bottomright. +} + +const QRect& Clusterizer::operator[](int i) +{ + return cluster[i]; +} diff --git a/win/Qt/qt_win.cpp b/win/Qt/qt_win.cpp new file mode 100644 index 0000000..46eb66e --- /dev/null +++ b/win/Qt/qt_win.cpp @@ -0,0 +1,5264 @@ +// SCCS Id: @(#)qt_win.cpp 3.4 1999/11/19 +// Copyright (c) Warwick Allison, 1999. +// NetHack may be freely redistributed. See license for details. + +// Qt Binding for NetHack 3.4 +// +// Copyright (C) 1996-2001 by Warwick W. Allison (warwick@troll.no) +// +// Contributors: +// Michael Hohmuth +// - Userid control +// Svante Gerhard +// - .nethackrc tile and font size settings +// Dirk Schoenberger +// - KDE support +// - SlashEm support +// and many others for bug reports. +// +// Unfortunately, this doesn't use Qt as well as I would like, +// primarily because NetHack is fundamentally a getkey-type program +// rather than being event driven (hence the ugly key and click buffer) +// and also because this is my first major application of Qt. +// +// The problem of NetHack's getkey requirement is solved by intercepting +// key events by overiding QApplicion::notify(...), and putting them in +// a buffer. Mouse clicks on the map window are treated with a similar +// buffer. When the NetHack engine calls for a key, one is taken from +// the buffer, or if that is empty, QApplication::enter_loop() is called. +// Whenever keys or clicks go into the buffer, QApplication::exit_loop() +// is called. +// +// Another problem is that some NetHack players are decade-long players who +// demand complete keyboard control (while Qt and X11 conspire to make this +// difficult by having widget-based focus rather than application based - +// a good thing in general). This problem is solved by again using the key +// event buffer. +// +// Out of all this hackery comes a silver lining however, as macros for +// the super-expert and menus for the ultra-newbie are also made possible +// by the key event buffer. +// + +extern "C" { + +// This includes all the definitions we need from the NetHack main +// engine. We pretend MSC is a STDC compiler, because C++ is close +// enough, and we undefine NetHack macros which conflict with Qt +// identifiers. + +#define alloc hide_alloc // avoid treading on STL symbol +#define lock hide_lock // avoid treading on STL symbol +#ifdef _MSC_VER +#define NHSTDC +#endif +#include "hack.h" +#include "func_tab.h" +#include "dlb.h" +#include "patchlevel.h" +#include "tile2x11.h" +#undef Invisible +#undef Warning +#undef red +#undef green +#undef blue +#undef Black +#undef curs +#undef TRUE +#undef FALSE +#undef min +#undef max +#undef alloc +#undef lock +#undef yn + +} + +#include "qt_win.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include + +#include + +#include "qt_clust.h" +#include "qt_xpms.h" + +#include +#ifdef Q_WS_MACX +# include +#else +# include +#endif + +#ifdef _WS_X11_ +// For userid control +#include +#endif + +// Some distributors released Qt 2.1.0beta4 +#if QT_VERSION < 220 +# define nh_WX11BypassWM 0x01000000 +#else +# define nh_WX11BypassWM WX11BypassWM +#endif + +#ifdef USER_SOUNDS +# if QT_VERSION < 220 +# undef USER_SOUNDS +# else +# include +# endif +#endif + + +#ifdef USER_SOUNDS +extern "C" void play_sound_for_message(const char* str); +#endif + +// Warwick prefers it this way... +#define QT_CHOOSE_RACE_FIRST + +static const char nh_attribution[] = "
NetHack" + "
by the NetHack DevTeam
"; + +static QString +aboutMsg() +{ + QString msg; + msg.sprintf( + "Qt NetHack is a version of NetHack built\n" +#ifdef KDE + "using KDE and the Qt GUI toolkit.\n" +#else + "using the Qt GUI toolkit.\n" +#endif + "This is version %d.%d.%d\n\n" + "Homepage:\n http://trolls.troll.no/warwick/nethack/\n\n" +#ifdef KDE + "KDE:\n http://www.kde.org\n" +#endif + "Qt:\n http://www.troll.no", + VERSION_MAJOR, + VERSION_MINOR, + PATCHLEVEL); + return msg; +} + +static void +centerOnMain( QWidget* w ) +{ + QWidget* m = qApp->mainWidget(); + if (!m) m = qApp->desktop(); + QPoint p = m->mapToGlobal(QPoint(0,0)); + w->move( p.x() + m->width()/2 - w->width()/2, + p.y() + m->height()/2 - w->height()/2 ); +} + +NetHackQtLineEdit::NetHackQtLineEdit() : + QLineEdit(0) +{ +} + +NetHackQtLineEdit::NetHackQtLineEdit(QWidget* parent, const char* name) : + QLineEdit(parent,name) +{ +} + +void NetHackQtLineEdit::fakeEvent(int key, int ascii, int state) +{ + QKeyEvent fake(QEvent::KeyPress,key,ascii,state); + keyPressEvent(&fake); +} + +extern "C" { +/* Used by tile/font-size patch below and in ../../src/files.c */ +char *qt_tilewidth=NULL; +char *qt_tileheight=NULL; +char *qt_fontsize=NULL; +#if defined(QWS) +int qt_compact_mode = 1; +#else +int qt_compact_mode = 0; +#endif +extern const char *enc_stat[]; /* from botl.c */ +extern const char *hu_stat[]; /* from eat.c */ +extern const char *killed_by_prefix[]; +extern int total_tiles_used; // from tile.c +extern short glyph2tile[]; // from tile.c +} + +static int tilefile_tile_W=16; +static int tilefile_tile_H=16; + +#define TILEWMIN 1 +#define TILEHMIN 1 + + +/* XPM */ +static const char * nh_icon[] = { +"40 40 6 1", +" s None c none", +". c #ffffff", +"X c #dadab6", +"o c #6c91b6", +"O c #476c6c", +"+ c #000000", +" ", +" ", +" ", +" . .X..XX.XX X ", +" .. .....X.XXXXXX XX ", +" ... ....X..XX.XXXXX XXX ", +" .. ..........X.XXXXXXXXXXX XX ", +" .... ........X..XX.XXXXXXXXX XXXX ", +" .... ..........X.XXXXXXXXXXX XXXX ", +" ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ", +" ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ", +" ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ", +" ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ", +" ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ", +" ++++..ooooooooOoOOOOOOOOOXX+++ +++ ", +" +++..ooooooOooOOoOOOOOOOXX+++ + ", +" ++..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..oooooOooOOoOOOOOOXX+++ ", +" ..oooooooOoOOOOOOOOXX+++ ", +" ..ooooOooOOoOOOOOXX+++ ", +" ..ooooooOoOOOOOOOXX++++ ", +" ..o..oooOooOOoOOOOXX+XX+++ ", +" ...o..oooooOoOOOOOXX++XXX++ ", +" ....OO..ooOooOOoOOXX+++XXXX++ ", +" ...oo..+..oooOoOOOXX++XXooXXX++ ", +" ...ooo..++..OooOOoXX+++XXooOXXX+ ", +" ..oooOOXX+++....XXXX++++XXOOoOOXX+ ", +" ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ", +" ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ", +" .....XXX++++ XXXXXXX++ ", +" ....XX++++ XXXXXXX+ ", +" ...XX+++ XXXXX++ ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * nh_icon_small[] = { +/* width height ncolors chars_per_pixel */ +"16 16 16 1", +/* colors */ +" c #587070", +". c #D1D5C9", +"X c #8B8C84", +"o c #2A2A28", +"O c #9AABA9", +"+ c #6A8FB2", +"@ c #C4CAC4", +"# c #B6BEB6", +"$ c None", +"% c #54564E", +"& c #476C6C", +"* c #ADB2AB", +"= c #ABABA2", +"- c #5E8295", +"; c #8B988F", +": c #E8EAE7", +/* pixels */ +"$$$$$$$$$$$$$$$$", +"$$$.$#::.#==*$$$", +"$.*:::::....#*=$", +"$@#:..@#*==#;XX;", +"$@O:+++- &&; X%X", +"$#%.+++- &&;% oX", +"$$o.++-- &&;%%X$", +"$$$:++-- &&;%%$$", +"$$$.O++- &&=o $$", +"$$$=:++- & XoX$$", +"$$*:@O-- ;%Xo$$", +"$*:O#$+--;oOOX $", +"$:+ =o::=oo=-;%X", +"$::.%o$*;X;##@%$", +"$$@# ;$$$$$=*;X$", +"$$$$$$$$$$$$$$$$" +}; + +/* XPM */ +static const char * map_xpm[] = { +"12 13 4 1", +". c None", +" c #000000000000", +"X c #0000B6DAFFFF", +"o c #69A69248B6DA", +" .", +" XXXXX ooo ", +" XoooX o ", +" XoooX o o ", +" XoooX ooo ", +" XXoXX o ", +" oooooXXX ", +" oo o oooX ", +" o XooX ", +" oooo XooX ", +" o o XXXX ", +" ", +". "}; +/* XPM */ +static const char * msg_xpm[] = { +"12 13 4 1", +". c None", +" c #FFFFFFFFFFFF", +"X c #69A69248B6DA", +"o c #000000000000", +" .", +" XXX XXX X o", +" o", +" XXXXX XX o", +" o", +" XX XXXXX o", +" o", +" XXXXXX o", +" o", +" XX XXX XX o", +" o", +" o", +".ooooooooooo"}; +/* XPM */ +static const char * stat_xpm[] = { +"12 13 5 1", +" c None", +". c #FFFF00000000", +"X c #000000000000", +"o c #FFFFFFFF0000", +"O c #69A6FFFF0000", +" ", +" ", +"... ", +"...X ", +"...X ... ", +"oooX oooX", +"oooXooo oooX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +"OOOXOOOXOOOX", +" XXXXXXXXXXX"}; +/* XPM */ +static const char * info_xpm[] = { +"12 13 4 1", +" c None", +". c #00000000FFFF", +"X c #FFFFFFFFFFFF", +"o c #000000000000", +" ... ", +" ....... ", +" ...XXX... ", +" .........o ", +"...XXXX.... ", +"....XXX....o", +"....XXX....o", +"....XXX....o", +" ...XXX...oo", +" ..XXXXX..o ", +" .......oo ", +" o...ooo ", +" ooo "}; + + +/* XPM */ +static const char * again_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" .. ", +" .. ", +" ..... ", +" ....... ", +"... .. .. ", +".. .. .. ", +".. ..", +".. ..", +".. ..", +" .. .. ", +" .......... ", +" ...... ", +" "}; +/* XPM */ +static const char * kick_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" ", +" . . . ", +" ... . . ", +" ... . ", +" ... . ", +" ... ", +"XXX ... ", +"XXX. ... ", +"XXX. ... ", +"XXX. .. ", +" ... ", +" "}; +/* XPM */ +static const char * throw_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ", +" ", +" ", +".... X ", +"....X X ", +"....X XXXXXX", +"....X X ", +" XXXX X ", +" ", +" ", +" ", +" "}; +/* XPM */ +static const char * fire_xpm[] = { +"12 13 5 1", +" c None", +". c #B6DA45140000", +"X c #FFFFB6DA9658", +"o c #000000000000", +"O c #FFFF6DB60000", +" . ", +" X. ", +" X . ", +" X .o ", +" X . o ", +" X .o o ", +"OOOOOOOOoooo", +" X .o o ", +" X . o o ", +" X .o ", +" X. o ", +" . o ", +" o "}; +/* XPM */ +static const char * get_xpm[] = { +"12 13 3 1", +" c None", +". c #000000000000", +"X c #FFFF6DB60000", +" ", +" . ", +" ... ", +" . . . ", +" . ", +" . ", +" ", +" XXXXX ", +" XXXXX. ", +" XXXXX. ", +" XXXXX. ", +" ..... ", +" "}; +/* XPM */ +static const char * drop_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" ", +" ..... ", +" .....X ", +" .....X ", +" .....X ", +" XXXXX ", +" ", +" X ", +" X ", +" X X X ", +" XXX ", +" X ", +" "}; +/* XPM */ +static const char * eat_xpm[] = { +"12 13 4 1", +" c None", +". c #000000000000", +"X c #FFFFB6DA9658", +"o c #FFFF6DB60000", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" .X. .. ", +" ... .. ", +" .. .. ", +" .. .. ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo ", +" oo oo "}; +/* XPM */ +static const char * rest_xpm[] = { +"12 13 2 1", +" c None", +". c #000000000000", +" ..... ", +" . ", +" . ", +" . ....", +" ..... . ", +" . ", +" ....", +" ", +" .... ", +" . ", +" . ", +" .... ", +" "}; +/* XPM */ +static const char * cast_a_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X X ", +" .. XXXX ", +" . X X ", +" . X X "}; +/* XPM */ +static const char * cast_b_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XXX ", +" .. X X ", +" .. XXX ", +" .. X X ", +" . X X ", +" . XXX "}; +/* XPM */ +static const char * cast_c_xpm[] = { +"12 13 3 1", +" c None", +". c #FFFF6DB60000", +"X c #000000000000", +" . ", +" . ", +" .. ", +" .. ", +" .. . ", +" .. . ", +" ...... ", +" .. .. XX ", +" .. X X ", +" .. X ", +" .. X ", +" . X X ", +" . XX "}; + +NetHackQtSettings::NetHackQtSettings(int w, int h) : + tilewidth(TILEWMIN,64,1,this), + tileheight(TILEHMIN,64,1,this), + widthlbl(&tilewidth,"&Width:",this), + heightlbl(&tileheight,"&Height:",this), + whichsize("&Zoomed",this), + fontsize(this), + normal("times"), +#ifdef WS_WIN + normalfixed("courier new"), +#else + normalfixed("fixed"), +#endif + large("times"), + theglyphs(0) + +{ + int default_fontsize; + + if (w<=300) { + // ~240x320 + default_fontsize=4; + tilewidth.setValue(8); + tileheight.setValue(12); + } else if (w<=700) { + // ~640x480 + default_fontsize=3; + tilewidth.setValue(8); + tileheight.setValue(14); + } else if (w<=900) { + // ~800x600 + default_fontsize=3; + tilewidth.setValue(10); + tileheight.setValue(17); + } else if (w<=1100) { + // ~1024x768 + default_fontsize=2; + tilewidth.setValue(12); + tileheight.setValue(22); + } else if (w<=1200) { + // ~1152x900 + default_fontsize=1; + tilewidth.setValue(14); + tileheight.setValue(26); + } else { + // ~1280x1024 and larger + default_fontsize=0; + tilewidth.setValue(16); + tileheight.setValue(30); + } + + // Tile/font sizes read from .nethackrc + if (qt_tilewidth != NULL) { + tilewidth.setValue(atoi(qt_tilewidth)); + free(qt_tilewidth); + } + if (qt_tileheight != NULL) { + tileheight.setValue(atoi(qt_tileheight)); + free(qt_tileheight); + } + if (qt_fontsize != NULL) { + switch (tolower(qt_fontsize[0])) { + case 'h': default_fontsize = 0; break; + case 'l': default_fontsize = 1; break; + case 'm': default_fontsize = 2; break; + case 's': default_fontsize = 3; break; + case 't': default_fontsize = 4; break; + } + free(qt_fontsize); + } + + theglyphs=new NetHackQtGlyphs(); + resizeTiles(); + + connect(&tilewidth,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&tileheight,SIGNAL(valueChanged(int)),this,SLOT(resizeTiles())); + connect(&whichsize,SIGNAL(toggled(bool)),this,SLOT(setGlyphSize(bool))); + + fontsize.insertItem("Huge"); + fontsize.insertItem("Large"); + fontsize.insertItem("Medium"); + fontsize.insertItem("Small"); + fontsize.insertItem("Tiny"); + fontsize.setCurrentItem(default_fontsize); + connect(&fontsize,SIGNAL(activated(int)),this,SIGNAL(fontChanged())); + + QGridLayout* grid = new QGridLayout(this, 5, 2, 8); + grid->addMultiCellWidget(&whichsize, 0, 0, 0, 1); + grid->addWidget(&tilewidth, 1, 1); grid->addWidget(&widthlbl, 1, 0); + grid->addWidget(&tileheight, 2, 1); grid->addWidget(&heightlbl, 2, 0); + QLabel* flabel=new QLabel(&fontsize, "&Font:",this); + grid->addWidget(flabel, 3, 0); grid->addWidget(&fontsize, 3, 1); + QPushButton* dismiss=new QPushButton("Dismiss",this); + dismiss->setDefault(TRUE); + grid->addMultiCellWidget(dismiss, 4, 4, 0, 1); + grid->setRowStretch(4,0); + grid->setColStretch(1,1); + grid->setColStretch(2,2); + grid->activate(); + + connect(dismiss,SIGNAL(clicked()),this,SLOT(accept())); + resize(150,140); +} + +NetHackQtGlyphs& NetHackQtSettings::glyphs() +{ + return *theglyphs; +} + +void NetHackQtSettings::resizeTiles() +{ + int w = tilewidth.value(); + int h = tileheight.value(); + + theglyphs->setSize(w,h); + emit tilesChanged(); +} + +void NetHackQtSettings::toggleGlyphSize() +{ + whichsize.toggle(); +} + +void NetHackQtSettings::setGlyphSize(bool which) +{ + QSize n = QSize(tilewidth.value(),tileheight.value()); + if ( othersize.isValid() ) { + tilewidth.blockSignals(TRUE); + tileheight.blockSignals(TRUE); + tilewidth.setValue(othersize.width()); + tileheight.setValue(othersize.height()); + tileheight.blockSignals(FALSE); + tilewidth.blockSignals(FALSE); + resizeTiles(); + } + othersize = n; +} + +const QFont& NetHackQtSettings::normalFont() +{ + static int size[]={ 18, 14, 12, 10, 8 }; + normal.setPointSize(size[fontsize.currentItem()]); + return normal; +} + +const QFont& NetHackQtSettings::normalFixedFont() +{ + static int size[]={ 18, 14, 13, 10, 8 }; + normalfixed.setPointSize(size[fontsize.currentItem()]); + return normalfixed; +} + +const QFont& NetHackQtSettings::largeFont() +{ + static int size[]={ 24, 18, 14, 12, 10 }; + large.setPointSize(size[fontsize.currentItem()]); + return large; +} + +bool NetHackQtSettings::ynInMessages() +{ + return !qt_compact_mode; +} + + +NetHackQtSettings* qt_settings; + + + +NetHackQtKeyBuffer::NetHackQtKeyBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtKeyBuffer::Empty() const { return in==out; } +bool NetHackQtKeyBuffer::Full() const { return (in+1)%maxkey==out; } + +void NetHackQtKeyBuffer::Put(int k, int a, int state) +{ + if ( Full() ) return; // Safety + key[in]=k; + ascii[in]=a; + in=(in+1)%maxkey; +} + +void NetHackQtKeyBuffer::Put(char a) +{ + Put(0,a,0); +} + +void NetHackQtKeyBuffer::Put(const char* str) +{ + while (*str) Put(*str++); +} + +int NetHackQtKeyBuffer::GetKey() +{ + if ( Empty() ) return 0; + int r=TopKey(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetAscii() +{ + if ( Empty() ) return 0; // Safety + int r=TopAscii(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::GetState() +{ + if ( Empty() ) return 0; + int r=TopState(); + out=(out+1)%maxkey; + return r; +} + +int NetHackQtKeyBuffer::TopKey() const +{ + if ( Empty() ) return 0; + return key[out]; +} + +int NetHackQtKeyBuffer::TopAscii() const +{ + if ( Empty() ) return 0; + return ascii[out]; +} + +int NetHackQtKeyBuffer::TopState() const +{ + if ( Empty() ) return 0; + return state[out]; +} + + +NetHackQtClickBuffer::NetHackQtClickBuffer() : + in(0), out(0) +{ +} + +bool NetHackQtClickBuffer::Empty() const { return in==out; } +bool NetHackQtClickBuffer::Full() const { return (in+1)%maxclick==out; } + +void NetHackQtClickBuffer::Put(int x, int y, int mod) +{ + click[in].x=x; + click[in].y=y; + click[in].mod=mod; + in=(in+1)%maxclick; +} + +int NetHackQtClickBuffer::NextX() const { return click[out].x; } +int NetHackQtClickBuffer::NextY() const { return click[out].y; } +int NetHackQtClickBuffer::NextMod() const { return click[out].mod; } + +void NetHackQtClickBuffer::Get() +{ + out=(out+1)%maxclick; +} + +class NhPSListViewItem : public QListViewItem { +public: + NhPSListViewItem( QListView* parent, const QString& name ) : + QListViewItem(parent, name) + { + } + + void setGlyph(int g) + { + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + QPixmap pm(gw,gh); + QPainter p(&pm); + glyphs.drawGlyph(p, g, 0, 0); + p.end(); + setPixmap(0,pm); + setHeight(QMAX(pm.height()+1,height())); + } + + void paintCell( QPainter *p, const QColorGroup &cg, + int column, int width, int alignment ) + { + if ( isSelectable() ) { + QListViewItem::paintCell( p, cg, column, width, alignment ); + } else { + QColorGroup disabled( + cg.foreground().light(), + cg.button().light(), + cg.light(), cg.dark(), cg.mid(), + gray, cg.base() ); + QListViewItem::paintCell( p, disabled, column, width, alignment ); + } + } +}; + +class NhPSListViewRole : public NhPSListViewItem { +public: + NhPSListViewRole( QListView* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Lowerize - looks better + QString(QChar(roles[id].name.m[0])).lower()+QString(roles[id].name.m+1) +#else + roles[id].name.m +#endif + ) + { + setGlyph(monnum_to_glyph(roles[id].malenum)); + } +}; + +class NhPSListViewRace : public NhPSListViewItem { +public: + NhPSListViewRace( QListView* parent, int id ) : + NhPSListViewItem(parent, +#ifdef QT_CHOOSE_RACE_FIRST // Capitalize - looks better + QString(QChar(races[id].noun[0])).upper()+QString(races[id].noun+1) +#else + QString(QChar(races[id].noun[0])+QString(races[id].noun+1)) +#endif + ) + { + setGlyph(monnum_to_glyph(races[id].malenum)); + } +}; + +class NhPSListView : public QListView { +public: + NhPSListView( QWidget* parent ) : + QListView(parent) + { + setSorting(-1); // order is identity + header()->setClickEnabled(FALSE); + } + + QSizePolicy sizePolicy() const + { + return QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + } + + QSize minimumSizeHint() const + { + return sizeHint(); + } + + QSize sizeHint() const + { + QListView::sizeHint(); + QSize sz = header()->sizeHint(); + int h=0; + QListViewItem* c=firstChild(); + while (c) h+=c->height(),c = c->nextSibling(); + sz += QSize(frameWidth()*2, h+frameWidth()*2); + return sz; + } + + int selectedItemNumber() const + { + int i=0; + QListViewItem* c = firstChild(); + while (c) { + if (c == selectedItem()) { + return i; + } + i++; + c = c->nextSibling(); + } + return -1; + } + + void setSelectedItemNumber(int i) + { + QListViewItem* c=firstChild(); + while (i--) + c = c->nextSibling(); + c->setSelected(TRUE); + } +}; + +NetHackQtPlayerSelector::NetHackQtPlayerSelector(NetHackQtKeyBuffer& ks) : + QDialog(qApp->mainWidget(),"plsel",TRUE), + keysource(ks), + fully_specified_role(TRUE) +{ + /* + 0 1 2 + + Name ------------------------------------+ + 0 | | + + ---- ------------------------------------+ + + Role ---+ + Race ---+ + Gender ------+ + | | | | | * Male | + 1 | | | | | * Female | + | | | | +--------------+ + | | | | + | | | | + Alignment ---+ + 2 | | | | | * Male | + | | | | | * Female | + | | | | +--------------+ + 3 | | | | ...stretch... + | | | | + 4 | | | | [ Play ] + 5 | | | | [ Quit ] + +---------+ +---------+ + */ + + int marg=4; + QGridLayout *l = new QGridLayout(this,6,3,marg,marg); + + QButtonGroup* namebox = new QButtonGroup(1,Horizontal,"Name",this); + QLineEdit* name = new QLineEdit(namebox); + name->setMaxLength(sizeof(plname)-1); + if ( strncmp(plname,"player",6) && strncmp(plname,"games",5) ) + name->setText(plname); + connect(name, SIGNAL(textChanged(const QString&)), + this, SLOT(selectName(const QString&)) ); + name->setFocus(); + QButtonGroup* genderbox = new QButtonGroup("Sex",this); + QButtonGroup* alignbox = new QButtonGroup("Alignment",this); + QVBoxLayout* vbgb = new QVBoxLayout(genderbox,3,1); + vbgb->setAutoAdd(TRUE); + vbgb->addSpacing(fontMetrics().height()*3/4); + QVBoxLayout* vbab = new QVBoxLayout(alignbox,3,1); + vbab->setAutoAdd(TRUE); + vbab->addSpacing(fontMetrics().height()); + QLabel* logo = new QLabel(nh_attribution, this); + + l->addMultiCellWidget( namebox, 0,0,0,2 ); +#ifdef QT_CHOOSE_RACE_FIRST + race = new NhPSListView(this); + role = new NhPSListView(this); + l->addMultiCellWidget( race, 1,5,0,0 ); + l->addMultiCellWidget( role, 1,5,1,1 ); +#else + role = new NhPSListView(this); + race = new NhPSListView(this); + l->addMultiCellWidget( role, 1,5,0,0 ); + l->addMultiCellWidget( race, 1,5,1,1 ); +#endif + role->addColumn("Role"); + race->addColumn("Race"); + + l->addWidget( genderbox, 1, 2 ); + l->addWidget( alignbox, 2, 2 ); + l->addWidget( logo, 3, 2, AlignCenter ); + l->setRowStretch( 3, 5 ); + + int i; + int nrole; + + for (nrole=0; roles[nrole].name.m; nrole++) + ; + for (i=nrole-1; i>=0; i--) { // XXX QListView unsorted goes in rev. + new NhPSListViewRole( role, i ); + } + connect( role, SIGNAL(selectionChanged()), this, SLOT(selectRole()) ); + + int nrace; + for (nrace=0; races[nrace].noun; nrace++) + ; + for (i=nrace-1; i>=0; i--) { + new NhPSListViewRace( race, i ); + } + connect( race, SIGNAL(selectionChanged()), this, SLOT(selectRace()) ); + + gender = new QRadioButton*[ROLE_GENDERS]; + for (i=0; iaddWidget( ok, 4, 2 ); + ok->setDefault(TRUE); + connect( ok, SIGNAL(clicked()), this, SLOT(accept()) ); + + QPushButton* cancel = new QPushButton("Quit",this); + l->addWidget( cancel, 5, 2 ); + connect( cancel, SIGNAL(clicked()), this, SLOT(reject()) ); + + // Randomize race and role, unless specified in config + int ro = flags.initrole; + if (ro == ROLE_NONE || ro == ROLE_RANDOM) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + int ra = flags.initrace; + if (ra == ROLE_NONE || ra == ROLE_RANDOM) { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + + // make sure we have a valid combination, honoring + // the users request if possible. + bool choose_race_first; +#ifdef QT_CHOOSE_RACE_FIRST + choose_race_first = TRUE; + if (flags.initrole >= 0 && flags.initrace < 0) { + choose_race_first = FALSE; + } +#else + choose_race_first = FALSE; + if (flags.initrace >= 0 && flags.initrole < 0) { + choose_race_first = TRUE; + } +#endif + while (!validrace(ro,ra)) { + if (choose_race_first) { + ro = rn2(nrole); + if (flags.initrole != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } else { + ra = rn2(nrace); + if (flags.initrace != ROLE_RANDOM) { + fully_specified_role = FALSE; + } + } + } + + int g = flags.initgend; + if (g == -1) { + g = rn2(ROLE_GENDERS); + fully_specified_role = FALSE; + } + while (!validgend(ro,ra,g)) { + g = rn2(ROLE_GENDERS); + } + gender[g]->setChecked(TRUE); + selectGender(g); + + int a = flags.initalign; + if (a == -1) { + a = rn2(ROLE_ALIGNS); + fully_specified_role = FALSE; + } + while (!validalign(ro,ra,a)) { + a = rn2(ROLE_ALIGNS); + } + alignment[a]->setChecked(TRUE); + selectAlignment(a); + + QListViewItem* li; + + li = role->firstChild(); + while (ro--) li=li->nextSibling(); + role->setSelected(li,TRUE); + + li = race->firstChild(); + while (ra--) li=li->nextSibling(); + race->setSelected(li,TRUE); + + flags.initrace = race->selectedItemNumber(); + flags.initrole = role->selectedItemNumber(); +} + + +void NetHackQtPlayerSelector::selectName(const QString& n) +{ + strncpy(plname,n.latin1(),sizeof(plname)-1); +} + +void NetHackQtPlayerSelector::selectRole() +{ + int ra = race->selectedItemNumber(); + int ro = role->selectedItemNumber(); + if (ra == -1 || ro == -1) return; + +#ifdef QT_CHOOSE_RACE_FIRST + selectRace(); +#else + QListViewItem* i=role->currentItem(); + QListViewItem* valid=0; + int j; + NhPSListViewItem* item; + item = (NhPSListViewItem*)role->firstChild(); + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item->setSelectable(TRUE); + if ( !valid && v ) valid = item; + item=(NhPSListViewItem*)item->nextSibling(); + } + if ( !validrace(role->selectedItemNumber(),ra) ) + i = valid; + role->setSelected(i,TRUE); + item = (NhPSListViewItem*)role->firstChild(); + for (j=0; roles[j].name.m; j++) { + bool v = validrace(j,ra); + item->setSelectable(v); + item->repaint(); + item=(NhPSListViewItem*)item->nextSibling(); + } +#endif + + flags.initrole = role->selectedItemNumber(); + setupOthers(); +} + +void NetHackQtPlayerSelector::selectRace() +{ + int ra = race->selectedItemNumber(); + int ro = role->selectedItemNumber(); + if (ra == -1 || ro == -1) return; + +#ifndef QT_CHOOSE_RACE_FIRST + selectRole(); +#else + QListViewItem* i=race->currentItem(); + QListViewItem* valid=0; + int j; + NhPSListViewItem* item; + item = (NhPSListViewItem*)race->firstChild(); + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item->setSelectable(TRUE); + if ( !valid && v ) valid = item; + item=(NhPSListViewItem*)item->nextSibling(); + } + if ( !validrace(ro,race->selectedItemNumber()) ) + i = valid; + race->setSelected(i,TRUE); + item = (NhPSListViewItem*)race->firstChild(); + for (j=0; races[j].noun; j++) { + bool v = validrace(ro,j); + item->setSelectable(v); + item->repaint(); + item=(NhPSListViewItem*)item->nextSibling(); + } +#endif + + flags.initrace = race->selectedItemNumber(); + setupOthers(); +} + +void NetHackQtPlayerSelector::setupOthers() +{ + int ro = role->selectedItemNumber(); + int ra = race->selectedItemNumber(); + int valid=-1; + int c=0; + int j; + for (j=0; jisChecked() ) + c = j; + gender[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validgend(ro,ra,c) ) + c = valid; + int k; + for (k=0; ksetChecked(c==k); + } + selectGender(c); + + valid=-1; + for (j=0; jisChecked() ) + c = j; + alignment[j]->setEnabled(v); + if ( valid<0 && v ) valid = j; + } + if ( !validalign(ro,ra,c) ) + c = valid; + for (k=0; ksetChecked(c==k); + } + selectAlignment(c); +} + +void NetHackQtPlayerSelector::selectGender(int i) +{ + flags.initgend = i; +} + +void NetHackQtPlayerSelector::selectAlignment(int i) +{ + flags.initalign = i; +} + + +void NetHackQtPlayerSelector::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +void NetHackQtPlayerSelector::Quit() +{ + done(R_Quit); + qApp->exit_loop(); +} + +void NetHackQtPlayerSelector::Random() +{ + done(R_Rand); + qApp->exit_loop(); +} + +bool NetHackQtPlayerSelector::Choose() +{ + if (fully_specified_role) return TRUE; + +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) { + showMaximized(); + } else +#endif + { + adjustSize(); + centerOnMain(this); + } + + if ( exec() ) { + return TRUE; + } else { + return FALSE; + } +} + + +NetHackQtStringRequestor::NetHackQtStringRequestor(NetHackQtKeyBuffer& ks, const char* p, const char* cancelstr) : + QDialog(qApp->mainWidget(),"string",FALSE), + prompt(p,this,"prompt"), + input(this,"input"), + keysource(ks) +{ + cancel=new QPushButton(cancelstr,this); + connect(cancel,SIGNAL(clicked()),this,SLOT(reject())); + + okay=new QPushButton("Okay",this); + connect(okay,SIGNAL(clicked()),this,SLOT(accept())); + connect(&input,SIGNAL(returnPressed()),this,SLOT(accept())); + okay->setDefault(TRUE); + + setFocusPolicy(StrongFocus); +} + +void NetHackQtStringRequestor::resizeEvent(QResizeEvent*) +{ + const int margin=5; + const int gutter=5; + + int h=(height()-margin*2-gutter); + + if (strlen(prompt.text()) > 16) { + h/=3; + prompt.setGeometry(margin,margin,width()-margin*2,h); + input.setGeometry(width()*1/5,margin+h+gutter, + (width()-margin-2-gutter)*4/5,h); + } else { + h/=2; + prompt.setGeometry(margin,margin,(width()-margin*2-gutter)*2/5,h); + input.setGeometry(prompt.geometry().right()+gutter,margin, + (width()-margin-2-gutter)*3/5,h); + } + + cancel->setGeometry(margin,input.geometry().bottom()+gutter, + (width()-margin*2-gutter)/2,h); + okay->setGeometry(cancel->geometry().right()+gutter,cancel->geometry().y(), + cancel->width(),h); +} + +void NetHackQtStringRequestor::SetDefault(const char* d) +{ + input.setText(d); +} +bool NetHackQtStringRequestor::Get(char* buffer, int maxchar) +{ + input.setMaxLength(maxchar); + if (strlen(prompt.text()) > 16) { + resize(fontMetrics().width(prompt.text())+50,fontMetrics().height()*6); + } else { + resize(fontMetrics().width(prompt.text())*2+50,fontMetrics().height()*4); + } + + centerOnMain(this); + show(); + input.setFocus(); + setResult(-1); + while (result()==-1) { + // Put keys in buffer (eg. from macros, from out-of-focus input) + if (!keysource.Empty()) { + while (!keysource.Empty()) { + int key=keysource.TopKey(); + int ascii=keysource.TopAscii(); + int state=keysource.GetState(); + if (ascii=='\r' || ascii=='\n') { + // CR or LF in buffer causes confirmation + strcpy(buffer,input.text()); + return TRUE; + } else if (ascii=='\033') { + return FALSE; + } else { + input.fakeEvent(key,ascii,state); + } + } + } + qApp->enter_loop(); + } + // XXX Get rid of extra keys, since we couldn't get focus! + while (!keysource.Empty()) keysource.GetKey(); + + if (result()) { + strcpy(buffer,input.text()); + return TRUE; + } else { + return FALSE; + } +} +void NetHackQtStringRequestor::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + + +NetHackQtWindow::NetHackQtWindow() +{ +} +NetHackQtWindow::~NetHackQtWindow() +{ +} + +// XXX Use "expected ..." for now, abort or default later. +// +void NetHackQtWindow::Clear() { puts("unexpected Clear"); } +void NetHackQtWindow::Display(bool block) { puts("unexpected Display"); } +bool NetHackQtWindow::Destroy() { return TRUE; } +void NetHackQtWindow::CursorTo(int x,int y) { puts("unexpected CursorTo"); } +void NetHackQtWindow::PutStr(int attr, const char* text) { puts("unexpected PutStr"); } +void NetHackQtWindow::StartMenu() { puts("unexpected StartMenu"); } +void NetHackQtWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel) { puts("unexpected AddMenu"); } +void NetHackQtWindow::EndMenu(const char* prompt) { puts("unexpected EndMenu"); } +int NetHackQtWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) { puts("unexpected SelectMenu"); return 0; } +void NetHackQtWindow::ClipAround(int x,int y) { puts("unexpected ClipAround"); } +void NetHackQtWindow::PrintGlyph(int x,int y,int glyph) { puts("unexpected PrintGlyph"); } +//void NetHackQtWindow::PrintGlyphCompose(int x,int y,int,int) { puts("unexpected PrintGlyphCompose"); } +void NetHackQtWindow::UseRIP(int how) { puts("unexpected UseRIP"); } + + + +// XXX Hmmm... crash after saving bones file if Map window is +// XXX deleted. Strange bug somewhere. +bool NetHackQtMapWindow::Destroy() { return FALSE; } + +NetHackQtMapWindow::NetHackQtMapWindow(NetHackQtClickBuffer& click_sink) : + clicksink(click_sink), + change(10), + rogue_font(0) +{ + viewport.addChild(this); + + setBackgroundColor(black); + viewport.setBackgroundColor(black); + + pet_annotation = QPixmap(qt_compact_mode ? pet_mark_small_xpm : pet_mark_xpm); + + cursor.setX(0); + cursor.setY(0); + Clear(); + + connect(qt_settings,SIGNAL(tilesChanged()),this,SLOT(updateTiles())); + connect(&viewport, SIGNAL(contentsMoving(int,int)), this, + SLOT(moveMessages(int,int))); + + updateTiles(); + //setFocusPolicy(StrongFocus); +} + +void NetHackQtMapWindow::moveMessages(int x, int y) +{ + QRect u = messages_rect; + messages_rect.moveTopLeft(QPoint(x,y)); + u |= messages_rect; + update(u); +} + +void NetHackQtMapWindow::clearMessages() +{ + messages = ""; + update(messages_rect); + messages_rect = QRect(); +} + +void NetHackQtMapWindow::putMessage(int attr, const char* text) +{ + if ( !messages.isEmpty() ) + messages += "\n"; + messages += text; + QFontMetrics fm = fontMetrics(); + messages_rect = fm.boundingRect(viewport.contentsX(),viewport.contentsY(),viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages); + update(messages_rect); +} + +void NetHackQtMapWindow::updateTiles() +{ + NetHackQtGlyphs& glyphs = qt_settings->glyphs(); + int gw = glyphs.width(); + int gh = glyphs.height(); + // Be exactly the size we want to be - full map... + resize(COLNO*gw,ROWNO*gh); + + viewport.verticalScrollBar()->setSteps(gh,gh); + viewport.horizontalScrollBar()->setSteps(gw,gw); + /* + viewport.setMaximumSize( + gw*COLNO + viewport.verticalScrollBar()->width(), + gh*ROWNO + viewport.horizontalScrollBar()->height() + ); + */ + viewport.updateScrollBars(); + + change.clear(); + change.add(0,0,COLNO,ROWNO); + delete rogue_font; rogue_font = 0; + Display(FALSE); + + emit resized(); +} + +NetHackQtMapWindow::~NetHackQtMapWindow() +{ + // Remove from viewport porthole, since that is a destructible member. + viewport.removeChild(this); + recreate(0,0,QPoint(0,0)); +} + +QWidget* NetHackQtMapWindow::Widget() +{ + return &viewport; +} + +void NetHackQtMapWindow::Scroll(int dx, int dy) +{ + if (viewport.horizontalScrollBar()->isVisible()) { + while (dx<0) { viewport.horizontalScrollBar()->subtractPage(); dx++; } + while (dx>0) { viewport.horizontalScrollBar()->addPage(); dx--; } + } + if (viewport.verticalScrollBar()->isVisible()) { + while (dy<0) { viewport.verticalScrollBar()->subtractPage(); dy++; } + while (dy>0) { viewport.verticalScrollBar()->addPage(); dy--; } + } +} + +void NetHackQtMapWindow::Clear() +{ + unsigned short stone=cmap_to_glyph(S_stone); + + for (int j=0; jexit_loop(); +} + +void NetHackQtMapWindow::mousePressEvent(QMouseEvent* event) +{ + clicksink.Put( + event->pos().x()/qt_settings->glyphs().width(), + event->pos().y()/qt_settings->glyphs().height(), + event->button()==LeftButton ? CLICK_1 : CLICK_2 + ); + qApp->exit_loop(); +} + +#ifdef TEXTCOLOR +static +const QPen& nhcolor_to_pen(int c) +{ + static QPen* pen=0; + if ( !pen ) { + pen = new QPen[17]; + pen[0] = Qt::black; + pen[1] = Qt::red; + pen[2] = QColor(0,191,0); + pen[3] = QColor(127,127,0); + pen[4] = Qt::blue; + pen[5] = Qt::magenta; + pen[6] = Qt::cyan; + pen[7] = Qt::gray; + pen[8] = Qt::white; // no color + pen[9] = QColor(255,127,0); + pen[10] = QColor(127,255,127); + pen[11] = Qt::yellow; + pen[12] = QColor(127,127,255); + pen[13] = QColor(255,127,255); + pen[14] = QColor(127,255,255); + pen[15] = Qt::white; + pen[16] = Qt::black; + } + + return pen[c]; +} +#endif + +void NetHackQtMapWindow::paintEvent(QPaintEvent* event) +{ + QRect area=event->rect(); + QRect garea; + garea.setCoords( + QMAX(0,area.left()/qt_settings->glyphs().width()), + QMAX(0,area.top()/qt_settings->glyphs().height()), + QMIN(COLNO-1,area.right()/qt_settings->glyphs().width()), + QMIN(ROWNO-1,area.bottom()/qt_settings->glyphs().height()) + ); + + QPainter painter; + + painter.begin(this); + + if ( +#ifdef REINCARNATION + Is_rogue_level(&u.uz) || +#endif + iflags.wc_ascii_map + ) + { + // You enter a VERY primitive world! + + painter.setClipRect( event->rect() ); // (normally we don't clip) + painter.fillRect( event->rect(), black ); + + if ( !rogue_font ) { + // Find font... + int pts = 5; + QString fontfamily = iflags.wc_font_map + ? iflags.wc_font_map : "Courier"; + bool bold = FALSE; + if ( fontfamily.right(5).lower() == "-bold" ) { + fontfamily.truncate(fontfamily.length()-5); + bold = TRUE; + } + while ( pts < 32 ) { + QFont f(fontfamily, pts, bold ? QFont::Bold : QFont::Normal); + painter.setFont(QFont(fontfamily, pts)); + QFontMetrics fm = painter.fontMetrics(); + if ( fm.width("M") > qt_settings->glyphs().width() ) + break; + if ( fm.height() > qt_settings->glyphs().height() ) + break; + pts++; + } + rogue_font = new QFont(fontfamily,pts-1); + } + painter.setFont(*rogue_font); + + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + uchar ch; + int color, och; + unsigned special; + + painter.setPen( green ); + /* map glyph to character and color */ + mapglyph(g, &och, &color, &special, i, j); + ch = (uchar)och; +#ifdef TEXTCOLOR + painter.setPen( nhcolor_to_pen(color) ); +#endif + painter.drawText( + i*qt_settings->glyphs().width(), + j*qt_settings->glyphs().height(), + qt_settings->glyphs().width(), + qt_settings->glyphs().height(), + AlignCenter, + (const char*)&ch, 1 + ); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + + painter.setFont(font()); + } else { + for (int j=garea.top(); j<=garea.bottom(); j++) { + for (int i=garea.left(); i<=garea.right(); i++) { + unsigned short g=Glyph(i,j); + qt_settings->glyphs().drawCell(painter, g, i, j); + if (glyph_is_pet(g) +#ifdef TEXTCOLOR + && ::iflags.hilite_pet +#endif + ) { + painter.drawPixmap(QPoint(i*qt_settings->glyphs().width(), j*qt_settings->glyphs().height()), pet_annotation); + } + } + } + } + + if (garea.contains(cursor)) { +#ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) { +#ifdef TEXTCOLOR + painter.setPen( white ); +#else + painter.setPen( green ); // REALLY primitive +#endif + } else +#endif + { + int hp100; + if (u.mtimedone) { + hp100=u.mhmax ? u.mh*100/u.mhmax : 100; + } else { + hp100=u.uhpmax ? u.uhp*100/u.uhpmax : 100; + } + + if (hp100 > 75) painter.setPen(white); + else if (hp100 > 50) painter.setPen(yellow); + else if (hp100 > 25) painter.setPen(QColor(0xff,0xbf,0x00)); // orange + else if (hp100 > 10) painter.setPen(red); + else painter.setPen(magenta); + } + + painter.drawRect( + cursor.x()*qt_settings->glyphs().width(),cursor.y()*qt_settings->glyphs().height(), + qt_settings->glyphs().width(),qt_settings->glyphs().height()); + } + + if (area.intersects(messages_rect)) { + painter.setPen(black); + painter.drawText(viewport.contentsX()+1,viewport.contentsY()+1, + viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages); + painter.setPen(white); + painter.drawText(viewport.contentsX(),viewport.contentsY(), + viewport.width(),0, WordBreak|AlignTop|AlignLeft|DontClip, messages); + } + + painter.end(); +} + +void NetHackQtMapWindow::Display(bool block) +{ + for (int i=0; iglyphs().width(), + ch.y()*qt_settings->glyphs().height(), + ch.width()*qt_settings->glyphs().width(), + ch.height()*qt_settings->glyphs().height(), + FALSE + ); + } + + change.clear(); + + if (block) { + yn_function("Press a key when done viewing",0,'\0'); + } +} + +void NetHackQtMapWindow::CursorTo(int x,int y) +{ + Changed(cursor.x(),cursor.y()); + cursor.setX(x); + cursor.setY(y); + Changed(cursor.x(),cursor.y()); +} + +void NetHackQtMapWindow::PutStr(int attr, const char* text) +{ + puts("unexpected PutStr in MapWindow"); +} + +void NetHackQtMapWindow::ClipAround(int x,int y) +{ + // Convert to pixel of center of tile + x=x*qt_settings->glyphs().width()+qt_settings->glyphs().width()/2; + y=y*qt_settings->glyphs().height()+qt_settings->glyphs().height()/2; + + // Then ensure that pixel is visible + viewport.center(x,y,0.45,0.45); +} + +void NetHackQtMapWindow::PrintGlyph(int x,int y,int glyph) +{ + Glyph(x,y)=glyph; + Changed(x,y); +} + +//void NetHackQtMapWindow::PrintGlyphCompose(int x,int y,int glyph1, int glyph2) +//{ + // TODO: composed graphics +//} + +void NetHackQtMapWindow::Changed(int x, int y) +{ + change.add(x,y); +} + + +class NetHackQtScrollText : public QTableView { + struct UData { + UData() : text(0), attr(0) { } + ~UData() { if (text) free(text); } + + char* text; + int attr; + }; +public: + int uncleared; + + NetHackQtScrollText(int maxlength) : + uncleared(0), + maxitems(maxlength), + first(0), + count(0), + item_cycle(maxlength) + { + setNumCols(1); + setCellWidth(200); + setCellHeight(fontMetrics().height()); + setBackgroundColor(white); + setTableFlags(Tbl_vScrollBar + |Tbl_autoHScrollBar + |Tbl_clipCellPainting + |Tbl_smoothScrolling); + } + + ~NetHackQtScrollText() + { + } + + void Scroll(int dx, int dy) + { + setXOffset(xOffset()+dx*viewWidth()); + setYOffset(yOffset()+dy*viewHeight()); + } + + void insertItem(int attr, const char* text) + { + setTopCell(count); + + setAutoUpdate(FALSE); + + int i; + if (count cellWidth()) { + // Get wider. + setCellWidth(w); + } + setTopCell(count); + + setAutoUpdate(TRUE); + + if (viewHeight() >= totalHeight()-cellHeight()) { + repaint(); + } else { + scroll(0,cellHeight()); + } + } + + virtual void setFont(const QFont& font) + { + QTableView::setFont(font); + setCellHeight(fontMetrics().height()); + } + +protected: + + UData& item(int i) + { + return item_cycle[(first+i)%maxitems]; + } + + const int maxitems; + int first, count; + QArray item_cycle; + + int datumWidth(const UData& uitem) + { + if (uitem.text) { + int width=fontMetrics().width(uitem.text)+3; + if (uitem.attr) { + // XXX Too expensive to do properly, because + // XXX we have to set the font of the widget + // XXX just to get the font metrics information! + // XXX Could hold a fake widget for that + // XXX purpose, but this hack is less ugly. + width+=width/10; + } + return width; + } else { + return 0; + } + } + + virtual void setupPainter(QPainter *p) + { + // XXX This shouldn't be needed - we set the bg in the constructor. + p->setBackgroundColor(white); + } + + virtual void paintCell(QPainter *p, int row, int col) + { + bool sel=FALSE; + UData& uitem=item(row); + + if (!sel && row < count-uncleared) { + p->setPen(darkGray); + } else { + p->setPen(black); + } + + if (uitem.attr) { + // XXX only bold + QFont bold(font().family(),font().pointSize(),QFont::Bold); + p->setFont(bold); + } + + p->drawText(3, 0, cellWidth(), cellHeight(), + AlignLeft|AlignVCenter, uitem.text); + + if (uitem.attr) { + p->setFont(font()); + } + } +}; + +NetHackQtMessageWindow::NetHackQtMessageWindow() : + list(new NetHackQtScrollText(::iflags.msg_history)) +{ + ::iflags.window_inited = 1; + map = 0; + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(updateFont())); + updateFont(); +} + +NetHackQtMessageWindow::~NetHackQtMessageWindow() +{ + ::iflags.window_inited = 0; + delete list; +} + +QWidget* NetHackQtMessageWindow::Widget() { return list; } + +void NetHackQtMessageWindow::setMap(NetHackQtMapWindow* m) +{ + map = m; + updateFont(); +} + +void NetHackQtMessageWindow::updateFont() +{ + list->setFont(qt_settings->normalFont()); + if ( map ) + map->setFont(qt_settings->normalFont()); +} + +void NetHackQtMessageWindow::Scroll(int dx, int dy) +{ + list->Scroll(dx,dy); +} + +void NetHackQtMessageWindow::Clear() +{ + if ( map ) + map->clearMessages(); + if (list->uncleared) { + list->uncleared=0; + changed=TRUE; + Display(FALSE); + } +} + +void NetHackQtMessageWindow::Display(bool block) +{ + if (changed) { + list->repaint(); + changed=FALSE; + } +} + +void NetHackQtMessageWindow::PutStr(int attr, const char* text) +{ +#ifdef USER_SOUNDS + play_sound_for_message(text); +#endif + + changed=TRUE; + list->uncleared++; + list->insertItem(attr,text); + + // Force scrollbar to bottom + // XXX list->setTopItem(list->count()); + + if ( map ) + map->putMessage(attr, text); +} + + + +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l) : + QWidget(parent), + low_is_good(FALSE), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(0) +{ + initHighlight(); +} +NetHackQtLabelledIcon::NetHackQtLabelledIcon(QWidget* parent, const char* l, const QPixmap& i) : + QWidget(parent), + low_is_good(FALSE), + prev_value(-123), + turn_count(-1), + label(new QLabel(l,this)), + icon(new QLabel(this)) +{ + setIcon(i); + initHighlight(); +} +void NetHackQtLabelledIcon::initHighlight() +{ + const QPalette& pal=palette(); + const QColorGroup& pa=pal.normal(); + //QColorGroup good(white,darkGreen,pa.light(),pa.dark(),pa.mid(),white,pa.base()); + QColorGroup good(black,green,pa.light(),pa.dark(),pa.mid(),black,pa.base()); + QColorGroup bad(white,red,pa.light(),pa.dark(),pa.mid(),white,pa.base()); + hl_good=pal.copy(); + hl_good.setNormal(good); + hl_good.setActive(good); + hl_bad=pal.copy(); + hl_bad.setNormal(bad); + hl_bad.setActive(bad); +} + +void NetHackQtLabelledIcon::setLabel(const char* t, bool lower) +{ + if (!label) { + label=new QLabel(this); + label->setFont(font()); + resizeEvent(0); + } + if (0!=strcmp(label->text(),t)) { + label->setText(t); + highlight(lower==low_is_good ? hl_good : hl_bad); + } +} +void NetHackQtLabelledIcon::setLabel(const char* t, long v, long cv, const char* tail) +{ + char buf[BUFSZ]; + if (v==NoNum) { + Sprintf(buf,"%s%s",t,tail); + } else { + Sprintf(buf,"%s%ld%s",t,v,tail); + } + setLabel(buf,cvsetPixmap(i); + else { icon=new QLabel(this); icon->setPixmap(i); resizeEvent(0); } + icon->resize(i.width(),i.height()); +} +void NetHackQtLabelledIcon::setFont(const QFont& f) +{ + QWidget::setFont(f); + if (label) label->setFont(f); +} +void NetHackQtLabelledIcon::show() +{ +#if QT_VERSION >= 300 + if (isHidden()) +#else + if (!isVisible()) +#endif + highlight(hl_bad); + QWidget::show(); +} +void NetHackQtLabelledIcon::highlightWhenChanging() +{ + turn_count=0; +} +void NetHackQtLabelledIcon::lowIsGood() +{ + low_is_good=TRUE; +} +void NetHackQtLabelledIcon::dissipateHighlight() +{ + if (turn_count>0) { + turn_count--; + if (!turn_count) + unhighlight(); + } +} +void NetHackQtLabelledIcon::highlight(const QPalette& hl) +{ + if (label) { // Surely it is?! + if (turn_count>=0) { + label->setPalette(hl); + turn_count=4; + // `4' includes this turn, so dissipates after + // 3 more keypresses. + } else { + label->setPalette(palette()); + } + } +} +void NetHackQtLabelledIcon::unhighlight() +{ + if (label) { // Surely it is?! + label->setPalette(palette()); + } +} +void NetHackQtLabelledIcon::resizeEvent(QResizeEvent*) +{ + setAlignments(); + + //int labw=label ? label->fontMetrics().width(label->text()) : 0; + int labh=label ? label->fontMetrics().height() : 0; + int icoh=icon ? icon->height() : 0; + int h=icoh+labh; + int icoy=(h>height() ? height()-labh-icoh : height()/2-h/2); + int laby=icoy+icoh; + if (icon) { + icon->setGeometry(0,icoy,width(),icoh); + } + if (label) { + label->setGeometry(0,laby,width(),labh); + } +} + +void NetHackQtLabelledIcon::setAlignments() +{ + if (label) label->setAlignment(AlignHCenter|AlignVCenter); + if (icon) icon->setAlignment(AlignHCenter|AlignVCenter); +} + +static void +tryload(QPixmap& pm, const char* fn) +{ + if (!pm.load(fn)) { + QString msg; + msg.sprintf("Cannot load \"%s\"", fn); + QMessageBox::warning(qApp->mainWidget(), "IO Error", msg); + } +} + +NetHackQtStatusWindow::NetHackQtStatusWindow() : + // Notes: + // Alignment needs -2 init value, because -1 is an alignment. + // Armor Class is an schar, so 256 is out of range. + // Blank value is 0 and should never change. + name(this,"(name)"), + dlevel(this,"(dlevel)"), + str(this,"STR"), + dex(this,"DEX"), + con(this,"CON"), + intel(this,"INT"), + wis(this,"WIS"), + cha(this,"CHA"), + gold(this,"Gold"), + hp(this,"Hit Points"), + power(this,"Power"), + ac(this,"Armour Class"), + level(this,"Level"), + exp(this,"Experience"), + align(this,"Alignment"), + time(this,"Time"), + score(this,"Score"), + hunger(this,""), + confused(this,"Confused"), + sick_fp(this,"Sick"), + sick_il(this,"Ill"), + blind(this,"Blind"), + stunned(this,"Stunned"), + hallu(this,"Hallu"), + encumber(this,""), + hline1(this), + hline2(this), + hline3(this), + first_set(TRUE) +{ + p_str = QPixmap(str_xpm); + p_str = QPixmap(str_xpm); + p_dex = QPixmap(dex_xpm); + p_con = QPixmap(cns_xpm); + p_int = QPixmap(int_xpm); + p_wis = QPixmap(wis_xpm); + p_cha = QPixmap(cha_xpm); + + p_chaotic = QPixmap(chaotic_xpm); + p_neutral = QPixmap(neutral_xpm); + p_lawful = QPixmap(lawful_xpm); + + p_satiated = QPixmap(satiated_xpm); + p_hungry = QPixmap(hungry_xpm); + + p_confused = QPixmap(confused_xpm); + p_sick_fp = QPixmap(sick_fp_xpm); + p_sick_il = QPixmap(sick_il_xpm); + p_blind = QPixmap(blind_xpm); + p_stunned = QPixmap(stunned_xpm); + p_hallu = QPixmap(hallu_xpm); + + p_encumber[0] = QPixmap(slt_enc_xpm); + p_encumber[1] = QPixmap(mod_enc_xpm); + p_encumber[2] = QPixmap(hvy_enc_xpm); + p_encumber[3] = QPixmap(ext_enc_xpm); + p_encumber[4] = QPixmap(ovr_enc_xpm); + + str.setIcon(p_str); + dex.setIcon(p_dex); + con.setIcon(p_con); + intel.setIcon(p_int); + wis.setIcon(p_wis); + cha.setIcon(p_cha); + + align.setIcon(p_neutral); + hunger.setIcon(p_hungry); + + confused.setIcon(p_confused); + sick_fp.setIcon(p_sick_fp); + sick_il.setIcon(p_sick_il); + blind.setIcon(p_blind); + stunned.setIcon(p_stunned); + hallu.setIcon(p_hallu); + + encumber.setIcon(p_encumber[0]); + + hline1.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline2.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline3.setFrameStyle(QFrame::HLine|QFrame::Sunken); + hline1.setLineWidth(1); + hline2.setLineWidth(1); + hline3.setLineWidth(1); + + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + doUpdate(); +} + +void NetHackQtStatusWindow::doUpdate() +{ + const QFont& large=qt_settings->largeFont(); + name.setFont(large); + dlevel.setFont(large); + + const QFont& normal=qt_settings->normalFont(); + str.setFont(normal); + dex.setFont(normal); + con.setFont(normal); + intel.setFont(normal); + wis.setFont(normal); + cha.setFont(normal); + gold.setFont(normal); + hp.setFont(normal); + power.setFont(normal); + ac.setFont(normal); + level.setFont(normal); + exp.setFont(normal); + align.setFont(normal); + time.setFont(normal); + score.setFont(normal); + hunger.setFont(normal); + confused.setFont(normal); + sick_fp.setFont(normal); + sick_il.setFont(normal); + blind.setFont(normal); + stunned.setFont(normal); + hallu.setFont(normal); + encumber.setFont(normal); + + updateStats(); +} + +QWidget* NetHackQtStatusWindow::Widget() { return this; } + +void NetHackQtStatusWindow::Clear() +{ +} +void NetHackQtStatusWindow::Display(bool block) +{ +} +void NetHackQtStatusWindow::CursorTo(int,int y) +{ + cursy=y; +} +void NetHackQtStatusWindow::PutStr(int attr, const char* text) +{ + // do a complete update when line 0 is done (as per X11 fancy status) + if (cursy==0) updateStats(); +} + +void NetHackQtStatusWindow::resizeEvent(QResizeEvent*) +{ + const float SP_name=0.13; // the (large) + const float SP_dlev=0.13; // Level 3 in The Dungeons of Doom (large) + const float SP_atr1=0.25; // STR DEX CON INT WIS CHA + const float SP_hln1=0.02; // --- + const float SP_atr2=0.09; // Au HP PW AC LVL EXP + const float SP_hln2=0.02; // --- + const float SP_time=0.09; // time score + const float SP_hln3=0.02; // --- + const float SP_stat=0.25; // Alignment, Poisoned, Hungry, Sick, etc. + + int h=height(); + int x=0,y=0; + + int iw; // Width of an item across line + int lh; // Height of a line of values + + lh=int(h*SP_name); + name.setGeometry(0,0,width(),lh); y+=lh; + lh=int(h*SP_dlev); + dlevel.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_hln1); + hline1.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr1); + iw=width()/6; + str.setGeometry(x,y,iw,lh); x+=iw; + dex.setGeometry(x,y,iw,lh); x+=iw; + con.setGeometry(x,y,iw,lh); x+=iw; + intel.setGeometry(x,y,iw,lh); x+=iw; + wis.setGeometry(x,y,iw,lh); x+=iw; + cha.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln2); + hline2.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_atr2); + iw=width()/6; + gold.setGeometry(x,y,iw,lh); x+=iw; + hp.setGeometry(x,y,iw,lh); x+=iw; + power.setGeometry(x,y,iw,lh); x+=iw; + ac.setGeometry(x,y,iw,lh); x+=iw; + level.setGeometry(x,y,iw,lh); x+=iw; + exp.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_hln3); + hline3.setGeometry(0,y,width(),lh); y+=lh; + + lh=int(h*SP_time); + iw=width()/3; x+=iw/2; + time.setGeometry(x,y,iw,lh); x+=iw; + score.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; + + lh=int(h*SP_stat); + iw=width()/9; + align.setGeometry(x,y,iw,lh); x+=iw; + hunger.setGeometry(x,y,iw,lh); x+=iw; + confused.setGeometry(x,y,iw,lh); x+=iw; + sick_fp.setGeometry(x,y,iw,lh); x+=iw; + sick_il.setGeometry(x,y,iw,lh); x+=iw; + blind.setGeometry(x,y,iw,lh); x+=iw; + stunned.setGeometry(x,y,iw,lh); x+=iw; + hallu.setGeometry(x,y,iw,lh); x+=iw; + encumber.setGeometry(x,y,iw,lh); x+=iw; + x=0; y+=lh; +} + + +/* + * Set all widget values to a null string. This is used after all spacings + * have been calculated so that when the window is popped up we don't get all + * kinds of funny values being displayed. + */ +void NetHackQtStatusWindow::nullOut() +{ +} + +void NetHackQtStatusWindow::fadeHighlighting() +{ + name.dissipateHighlight(); + dlevel.dissipateHighlight(); + + str.dissipateHighlight(); + dex.dissipateHighlight(); + con.dissipateHighlight(); + intel.dissipateHighlight(); + wis.dissipateHighlight(); + cha.dissipateHighlight(); + + gold.dissipateHighlight(); + hp.dissipateHighlight(); + power.dissipateHighlight(); + ac.dissipateHighlight(); + level.dissipateHighlight(); + exp.dissipateHighlight(); + align.dissipateHighlight(); + + time.dissipateHighlight(); + score.dissipateHighlight(); + + hunger.dissipateHighlight(); + confused.dissipateHighlight(); + sick_fp.dissipateHighlight(); + sick_il.dissipateHighlight(); + blind.dissipateHighlight(); + stunned.dissipateHighlight(); + hallu.dissipateHighlight(); + encumber.dissipateHighlight(); +} + +/* + * Update the displayed status. The current code in botl.c updates + * two lines of information. Both lines are always updated one after + * the other. So only do our update when we update the second line. + * + * Information on the first line: + * name, attributes, alignment, score + * + * Information on the second line: + * dlvl, gold, hp, power, ac, {level & exp or HD **} + * status (hunger, conf, halu, stun, sick, blind), time, encumbrance + * + * [**] HD is shown instead of level and exp if mtimedone is non-zero. + */ +void NetHackQtStatusWindow::updateStats() +{ + if (!parentWidget()) return; + + char buf[BUFSZ]; + + if (cursy != 0) return; /* do a complete update when line 0 is done */ + + if (ACURR(A_STR) > 118) { + Sprintf(buf,"STR:%d",ACURR(A_STR)-100); + } else if (ACURR(A_STR)==118) { + Sprintf(buf,"STR:18/**"); + } else if(ACURR(A_STR) > 18) { + Sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18); + } else { + Sprintf(buf,"STR:%d",ACURR(A_STR)); + } + str.setLabel(buf,NetHackQtLabelledIcon::NoNum,ACURR(A_STR)); + + dex.setLabel("DEX:",(long)ACURR(A_DEX)); + con.setLabel("CON:",(long)ACURR(A_CON)); + intel.setLabel("INT:",(long)ACURR(A_INT)); + wis.setLabel("WIS:",(long)ACURR(A_WIS)); + cha.setLabel("CHA:",(long)ACURR(A_CHA)); + const char* hung=hu_stat[u.uhs]; + if (hung[0]==' ') { + hunger.hide(); + } else { + hunger.setIcon(u.uhs ? p_hungry : p_satiated); + hunger.setLabel(hung); + hunger.show(); + } + if (Confusion) confused.show(); else confused.hide(); + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) { + sick_fp.show(); + } else { + sick_fp.hide(); + } + if (u.usick_type & SICK_NONVOMITABLE) { + sick_il.show(); + } else { + sick_il.hide(); + } + } else { + sick_fp.hide(); + sick_il.hide(); + } + if (Blind) blind.show(); else blind.hide(); + if (Stunned) stunned.show(); else stunned.hide(); + if (Hallucination) hallu.show(); else hallu.hide(); + const char* enc=enc_stat[near_capacity()]; + if (enc[0]==' ' || !enc[0]) { + encumber.hide(); + } else { + encumber.setIcon(p_encumber[near_capacity()-1]); + encumber.setLabel(enc); + encumber.show(); + } + Strcpy(buf, plname); + if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a'; + Strcat(buf, " the "); + if (u.mtimedone) { + char mname[BUFSZ]; + int k = 0; + + Strcpy(mname, mons[u.umonnum].mname); + while(mname[k] != 0) { + if ((k == 0 || (k > 0 && mname[k-1] == ' ')) + && 'a' <= mname[k] && mname[k] <= 'z') + { + mname[k] += 'A' - 'a'; + } + k++; + } + Strcat(buf, mname); + } else { + Strcat(buf, rank_of(u.ulevel, pl_character[0], ::flags.female)); + } + name.setLabel(buf,NetHackQtLabelledIcon::NoNum,u.ulevel); + + if (describe_level(buf)) { + dlevel.setLabel(buf,(bool)TRUE); + } else { + Sprintf(buf, "%s, level ", dungeons[u.uz.dnum].dname); + dlevel.setLabel(buf,(long)depth(&u.uz)); + } + +#ifndef GOLDOBJ + gold.setLabel("Au:", u.ugold); +#else + gold.setLabel("Au:", money_cnt(invent)); +#endif + if (u.mtimedone) { + // You're a monster! + + Sprintf(buf, "/%d", u.mhmax); + hp.setLabel("HP:",u.mh > 0 ? u.mh : 0,buf); + level.setLabel("HD:",(long)mons[u.umonnum].mlevel); + } else { + // You're normal. + + Sprintf(buf, "/%d", u.uhpmax); + hp.setLabel("HP:",u.uhp > 0 ? u.uhp : 0,buf); + level.setLabel("Level:",(long)u.ulevel); + } + Sprintf(buf, "/%d", u.uenmax); + power.setLabel("Pow:",u.uen,buf); + ac.setLabel("AC:",(long)u.uac); +#ifdef EXP_ON_BOTL + if (::flags.showexp) { + exp.setLabel("Exp:",(long)u.uexp); + } else +#endif + { + exp.setLabel(""); + } + if (u.ualign.type==A_CHAOTIC) { + align.setIcon(p_chaotic); + align.setLabel("Chaotic"); + } else if (u.ualign.type==A_NEUTRAL) { + align.setIcon(p_neutral); + align.setLabel("Neutral"); + } else { + align.setIcon(p_lawful); + align.setLabel("Lawful"); + } + + if (::flags.time) time.setLabel("Time:",(long)moves); + else time.setLabel(""); +#ifdef SCORE_ON_BOTL + if (::flags.showscore) { + score.setLabel("Score:",(long)botl_score()); + } else +#endif + { + score.setLabel(""); + } + + if (first_set) + { + first_set=FALSE; + + name.highlightWhenChanging(); + dlevel.highlightWhenChanging(); + + str.highlightWhenChanging(); + dex.highlightWhenChanging(); + con.highlightWhenChanging(); + intel.highlightWhenChanging(); + wis.highlightWhenChanging(); + cha.highlightWhenChanging(); + + gold.highlightWhenChanging(); + hp.highlightWhenChanging(); + power.highlightWhenChanging(); + ac.highlightWhenChanging(); ac.lowIsGood(); + level.highlightWhenChanging(); + exp.highlightWhenChanging(); + align.highlightWhenChanging(); + + //time.highlightWhenChanging(); + score.highlightWhenChanging(); + + hunger.highlightWhenChanging(); + confused.highlightWhenChanging(); + sick_fp.highlightWhenChanging(); + sick_il.highlightWhenChanging(); + blind.highlightWhenChanging(); + stunned.highlightWhenChanging(); + hallu.highlightWhenChanging(); + encumber.highlightWhenChanging(); + } +} + +/* + * Turn off hilighted status values after a certain amount of turns. + */ +void NetHackQtStatusWindow::checkTurnEvents() +{ +} + + + +NetHackQtMenuDialog::NetHackQtMenuDialog() : + QDialog(qApp->mainWidget(),0,FALSE) +{ +} + +void NetHackQtMenuDialog::resizeEvent(QResizeEvent*) +{ + emit Resized(); +} + +void NetHackQtMenuDialog::Accept() +{ + accept(); +} + +void NetHackQtMenuDialog::Reject() +{ + reject(); +} + +void NetHackQtMenuDialog::SetResult(int r) +{ + setResult(r); +} + +void NetHackQtMenuDialog::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +// Table view columns: +// +// [pick-count] [accel] [glyph] [string] +// +// Maybe accel should be near string. We'll see. +// pick-count normally blank. +// double-clicking or click-on-count gives pop-up entry +// string is green when selected +// +NetHackQtMenuWindow::NetHackQtMenuWindow(NetHackQtKeyBuffer& ks) : + QTableView(), + keysource(ks), + dialog(new NetHackQtMenuDialog()), + prompt(0), + pressed(-1) +{ + setNumCols(4); + setCellHeight(QMAX(qt_settings->glyphs().height()+1,fontMetrics().height())); + setBackgroundColor(lightGray); + setFrameStyle(Panel|Sunken); + setLineWidth(2); + + ok=new QPushButton("Ok",dialog); + connect(ok,SIGNAL(clicked()),dialog,SLOT(accept())); + + cancel=new QPushButton("Cancel",dialog); + connect(cancel,SIGNAL(clicked()),dialog,SLOT(reject())); + + all=new QPushButton("All",dialog); + connect(all,SIGNAL(clicked()),this,SLOT(All())); + + none=new QPushButton("None",dialog); + connect(none,SIGNAL(clicked()),this,SLOT(ChooseNone())); + + invert=new QPushButton("Invert",dialog); + connect(invert,SIGNAL(clicked()),this,SLOT(Invert())); + + search=new QPushButton("Search",dialog); + connect(search,SIGNAL(clicked()),this,SLOT(Search())); + + QPoint pos(0,ok->height()); + recreate(dialog,0,pos); + prompt.recreate(dialog,0,pos); + + setBackgroundColor(lightGray); + + connect(dialog,SIGNAL(Resized()),this,SLOT(Layout())); + + setTableFlags(Tbl_autoHScrollBar|Tbl_autoVScrollBar + |Tbl_smoothScrolling|Tbl_clipCellPainting); + setFocusPolicy(StrongFocus); +} + +NetHackQtMenuWindow::~NetHackQtMenuWindow() +{ + // Remove from dialog before we destruct it + recreate(0,0,QPoint(0,0)); + delete dialog; +} + +void NetHackQtMenuWindow::focusInEvent(QFocusEvent *) +{ + // Don't repaint at all, since nothing is using the focus colour +} +void NetHackQtMenuWindow::focusOutEvent(QFocusEvent *) +{ + // Don't repaint at all, since nothing is using the focus colour +} + +int NetHackQtMenuWindow::cellWidth(int col) +{ + switch (col) { + case 0: + return fontMetrics().width("All "); + break; case 1: + return fontMetrics().width(" m "); + break; case 2: + return qt_settings->glyphs().width(); + break; case 3: + return str_width; + } + impossible("Extra column (#%d) in MenuWindow",col); + return 0; +} + +QWidget* NetHackQtMenuWindow::Widget() { return dialog; } + +void NetHackQtMenuWindow::StartMenu() +{ + setNumRows((itemcount=0)); + str_width=200; + str_fixed=FALSE; + next_accel=0; + has_glyphs=FALSE; +} + +NetHackQtMenuWindow::MenuItem::MenuItem() : + str(0) +{ +} + +NetHackQtMenuWindow::MenuItem::~MenuItem() +{ + if (str) free((void*)str); +} + +#define STR_MARGIN 4 + +void NetHackQtMenuWindow::AddMenu(int glyph, const ANY_P* identifier, + char ch, char gch, int attr, const char* str, bool presel) +{ + if (!ch && identifier->a_void!=0) { + // Supply a keyboard accelerator. Limited supply. + static char accel[]="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + if (accel[next_accel]) { + ch=accel[next_accel++]; + } + } + + if ((int)item.size() < itemcount+1) { + item.resize(itemcount*4+10); + } + item[itemcount].glyph=glyph; + item[itemcount].identifier=*identifier; + item[itemcount].ch=ch; + item[itemcount].attr=attr; + item[itemcount].str=strdup(str); + item[itemcount].selected=presel; + item[itemcount].count=-1; + ++itemcount; + + str_fixed=str_fixed || strstr(str," "); + if (glyph!=NO_GLYPH) has_glyphs=TRUE; +} +void NetHackQtMenuWindow::EndMenu(const char* p) +{ + prompt.setText(p ? p : ""); +} +void NetHackQtMenuWindow::Layout() +{ + int butw=totalWidth()/6; // 6 buttons + int buth=fontMetrics().height()+8; // 8 for spacing & mitres + int prompth=(prompt.text().isNull() ? 0 : buth); + + prompt.setGeometry(6,buth,dialog->width()-6,prompth); + int h=dialog->height()-buth-prompth; + setGeometry(0,buth+prompth, dialog->width(), h); + + // Below, we take care to use up full width + int x=0; + ok->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/5; + cancel->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/4; + all->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/3; + none->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/2; + invert->setGeometry(x,0,butw,buth); x+=butw; butw=(dialog->width()-x)/1; + search->setGeometry(x,0,butw,buth); +} +int NetHackQtMenuWindow::SelectMenu(int h, MENU_ITEM_P **menu_list) +{ + setFont(str_fixed ? + qt_settings->normalFixedFont() : qt_settings->normalFont()); + + for (int i=0; iglyphs().height()+1,fontMetrics().height())); + setNumRows(itemcount); + + int buth=fontMetrics().height()+8; // 8 for spacing & mitres + + how=h; + + ok->setEnabled(how!=PICK_ONE);ok->setDefault(how!=PICK_ONE); + cancel->setEnabled(how!=PICK_NONE); + all->setEnabled(how==PICK_ANY); + none->setEnabled(how==PICK_ANY); + invert->setEnabled(how==PICK_ANY); + search->setEnabled(how!=PICK_NONE); + + dialog->SetResult(-1); + + // 20 allows for scrollbar or spacing + // 4 for frame borders + int mh = QApplication::desktop()->height()*3/5; + if ( qt_compact_mode && totalHeight() > mh ) { + // big, so make it fill + dialog->showMaximized(); + } else { + dialog->resize(totalWidth()+20, + QMIN(totalHeight(), mh)+buth+4+(prompt.text().isNull() ? 0 : buth)); + if ( dialog->width() > QApplication::desktop()->width() ) + dialog->resize(QApplication::desktop()->width(),dialog->height()+16); + centerOnMain(dialog); + dialog->show(); + } + + setFocus(); + while (dialog->result()<0) { + // changed the defaults below to the values in wintype.h 000119 - azy + if (!keysource.Empty()) { + char k=keysource.GetAscii(); + k=map_menu_cmd(k); /* added 000119 - azy */ + if (k=='\033') + dialog->Reject(); + else if (k=='\r' || k=='\n' || k==' ') + dialog->Accept(); + else if (k==MENU_SEARCH) + Search(); + else if (k==MENU_SELECT_ALL) + All(); + else if (k==MENU_INVERT_ALL) + Invert(); + else if (k==MENU_UNSELECT_ALL) + ChooseNone(); + else { + for (int i=0; iresult()<0) + qApp->enter_loop(); + } + //if ( (nhid != WIN_INVEN || !flags.perm_invent) ) // doesn't work yet + { + dialog->hide(); + } + int result=dialog->result(); + + // Consume ^M (which QDialog steals for default button) + while (!keysource.Empty() && + (keysource.TopAscii()=='\n' || keysource.TopAscii()=='\r')) + keysource.GetAscii(); + + *menu_list=0; + if (result>0 && how!=PICK_NONE) { + if (how==PICK_ONE) { + int i; + for (i=0; istate()&ShiftButton)) { + if (event->key()==Key_Prior) { + setYOffset(yOffset()-viewHeight()); + } else if (event->key()==Key_Next) { + setYOffset(yOffset()+viewHeight()); + } else { + event->ignore(); + } + } else { + event->ignore(); + } +} + +void NetHackQtMenuWindow::All() +{ + for (int i=0; iAccept(); + } + } +} + + +void NetHackQtMenuWindow::paintCell(QPainter* painter, int row, int col) +{ + // [pick-count] [accel] [glyph] [string] + + MenuItem& i = item[row]; + + painter->setPen(black); + painter->setFont(font()); + + if (i.selected) { + painter->setPen(darkGreen); + } + + switch (col) { + case 0: + if ( i.ch || i.attr!=ATR_INVERSE ) { + QString text; + if ( i.selected && i.count == -1 ) { + if ( i.str[0]>='0' && i.str[0]<='9' ) + text = "All"; + else + text = "*"; + } else if ( i.count<0 ) { + text = "-"; + } else { + text.sprintf("%d",i.count); + } + painter->drawText(0,0,cellWidth(col),cellHeight(), + AlignHCenter|AlignVCenter,text); + } + break; case 1: + if ((signed char)i.ch >= 0) { + char text[2]={i.ch,0}; + painter->drawText(0,0,cellWidth(col),cellHeight(), + AlignHCenter|AlignVCenter,text); + } + break; case 2: + if (i.glyph!=NO_GLYPH) { + // Centered in height + int y=(cellHeight()-qt_settings->glyphs().height())/2; + if (y<0) y=0; + qt_settings->glyphs().drawGlyph(*painter, i.glyph, 0, y); + } + break; case 3: + // XXX should qt_settings have ALL the various fonts + QFont newfont=font(); + + if (i.attr) { + switch(i.attr) { + case ATR_ULINE: + newfont.setUnderline(TRUE); + break; case ATR_BOLD: + painter->setPen(red); + break; case ATR_BLINK: + newfont.setItalic(TRUE); + break; case ATR_INVERSE: + newfont=qt_settings->largeFont(); + newfont.setWeight(QFont::Bold); + + if (i.selected) { + painter->setPen(blue); + } else { + painter->setPen(darkBlue); + } + } + } + painter->setFont(newfont); + + painter->drawText(STR_MARGIN,0,cellWidth(col),cellHeight(), + AlignLeft|AlignVCenter,i.str); + } +} + +void NetHackQtMenuWindow::mousePressEvent(QMouseEvent* event) +{ + int col=findCol(event->pos().x()); + int row=findRow(event->pos().y()); + + if (col<0 || row<0 || !item[row].Selectable()) return; + + if (how!=PICK_NONE) { + if (col==0) { + // Changing count. + NetHackQtStringRequestor requestor(keysource,"Count:"); + char buf[BUFSZ]; + + if (item[row].count>0) + Sprintf(buf,"%d", item[row].count); + else + Strcpy(buf, ""); + + requestor.SetDefault(buf); + if (requestor.Get(buf)) { + item[row].count=atoi(buf); + if (item[row].count==0) { + item[row].count=-1; + if (item[row].selected) ToggleSelect(row); + } else { + if (!item[row].selected) ToggleSelect(row); + } + updateCell(row,0); + } + } else { + pressed=row; + was_sel=item[row].selected; + ToggleSelect(row); + updateCell(row,0); + } + } +} +void NetHackQtMenuWindow::mouseReleaseEvent(QMouseEvent* event) +{ + if (pressed>=0) { + int p=pressed; + pressed=-1; + updateCell(p,3); + } +} +void NetHackQtMenuWindow::mouseMoveEvent(QMouseEvent* event) +{ + if (pressed>=0) { + int col=findCol(event->pos().x()); + int row=findRow(event->pos().y()); + + if (row>=0 && col>=0) { + if (pressed!=row) { + // reset to initial state + if (item[pressed].selected!=was_sel) + ToggleSelect(pressed); + } else { + // reset to new state + if (item[pressed].selected==was_sel) + ToggleSelect(pressed); + } + } + } +} + + +class NetHackQtTextListBox : public QListBox { +public: + NetHackQtTextListBox(QWidget* parent) : QListBox(parent) { } + + int TotalWidth() + { + doLayout(); + return contentsWidth(); + } + int TotalHeight() + { + doLayout(); + return contentsHeight(); + } + + virtual void setFont(const QFont &font) + { + QListBox::setFont(font); + } + void keyPressEvent(QKeyEvent* e) + { + QListBox::keyPressEvent(e); + } +}; + + +QPixmap* NetHackQtRIP::pixmap=0; + +NetHackQtRIP::NetHackQtRIP(QWidget* parent) : + QWidget(parent) +{ + if (!pixmap) { + pixmap=new QPixmap; + tryload(*pixmap, "rip.xpm"); + } + riplines=0; + resize(pixmap->width(),pixmap->height()); + setFont(QFont("times",12)); // XXX may need to be configurable +} + +void NetHackQtRIP::setLines(char** l, int n) +{ + line=l; + riplines=n; +} + +QSize NetHackQtRIP::sizeHint() const +{ + return pixmap->size(); +} + +void NetHackQtRIP::paintEvent(QPaintEvent* event) +{ + if ( riplines ) { + int pix_x=(width()-pixmap->width())/2; + int pix_y=(height()-pixmap->height())/2; + + // XXX positions based on RIP image + int rip_text_x=pix_x+156; + int rip_text_y=pix_y+67; + int rip_text_h=94/riplines; + + QPainter painter; + painter.begin(this); + painter.drawPixmap(pix_x,pix_y,*pixmap); + for (int i=0; imainWidget(),0,FALSE), + keysource(ks), + use_rip(FALSE), + str_fixed(FALSE), + ok("Dismiss",this), + search("Search",this), + lines(new NetHackQtTextListBox(this)), + rip(this) +{ + ok.setDefault(TRUE); + connect(&ok,SIGNAL(clicked()),this,SLOT(accept())); + connect(&search,SIGNAL(clicked()),this,SLOT(Search())); + connect(qt_settings,SIGNAL(fontChanged()),this,SLOT(doUpdate())); + + QVBoxLayout* vb = new QVBoxLayout(this); + vb->addWidget(&rip); + QHBoxLayout* hb = new QHBoxLayout(vb); + hb->addWidget(&ok); + hb->addWidget(&search); + vb->addWidget(lines); +} + +void NetHackQtTextWindow::doUpdate() +{ + update(); +} + + +NetHackQtTextWindow::~NetHackQtTextWindow() +{ + +} + +QWidget* NetHackQtTextWindow::Widget() +{ + return this; +} + +bool NetHackQtTextWindow::Destroy() +{ + return !isVisible(); +} + +void NetHackQtTextWindow::UseRIP(int how) +{ +// Code from X11 windowport +#define STONE_LINE_LEN 16 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + +static char** rip_line=0; + if (!rip_line) { + rip_line=new char*[YEAR_LINE+1]; + for (int i=0; i STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + strcpy(rip_line[line], dpx); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + Sprintf(rip_line[YEAR_LINE], "%4d", getyear()); + + rip.setLines(rip_line,YEAR_LINE+1); + + use_rip=TRUE; +} + +void NetHackQtTextWindow::Clear() +{ + lines->clear(); + use_rip=FALSE; + str_fixed=FALSE; +} + +void NetHackQtTextWindow::Display(bool block) +{ + if (str_fixed) { + lines->setFont(qt_settings->normalFixedFont()); + } else { + lines->setFont(qt_settings->normalFont()); + } + + int h=0; + if (use_rip) { + h+=rip.height(); + ok.hide(); + search.hide(); + rip.show(); + } else { + h+=ok.height()*2; + ok.show(); + search.show(); + rip.hide(); + } + int mh = QApplication::desktop()->height()*3/5; + if ( qt_compact_mode && lines->TotalHeight() > mh || use_rip ) { + // big, so make it fill + showMaximized(); + } else { + resize(QMAX(use_rip ? rip.width() : 200, + lines->TotalWidth()+24), + QMIN(mh, lines->TotalHeight()+h)); + centerOnMain(this); + show(); + } + if (block) { + setResult(-1); + while (result()==-1) { + qApp->enter_loop(); + if (result()==-1 && !keysource.Empty()) { + char k=keysource.GetAscii(); + if (k=='\033' || k==' ' || k=='\r' || k=='\n') { + accept(); + } else if (k=='/') { + Search(); + } + } + } + } +} + +void NetHackQtTextWindow::PutStr(int attr, const char* text) +{ + str_fixed=str_fixed || strstr(text," "); + lines->insertItem(text); +} + +void NetHackQtTextWindow::done(int i) +{ + setResult(i+1000); + hide(); + qApp->exit_loop(); +} + +void NetHackQtTextWindow::keyPressEvent(QKeyEvent* e) +{ + if ( e->ascii() != '\r' && e->ascii() != '\n' && e->ascii() != '\033' ) + lines->keyPressEvent(e); + else + QDialog::keyPressEvent(e); +} + +void NetHackQtTextWindow::Search() +{ + NetHackQtStringRequestor requestor(keysource,"Search for:"); + static char line[256]=""; + requestor.SetDefault(line); + if (requestor.Get(line)) { + int current=lines->currentItem(); + for (uint i=1; icount(); i++) { + int lnum=(i+current)%lines->count(); + QString str=lines->text(lnum); + if (str.contains(line)) { + lines->setCurrentItem(lnum); + lines->centerCurrentItem(); + return; + } + } + lines->setCurrentItem(-1); + } +} + + +NetHackQtDelay::NetHackQtDelay(int ms) : + msec(ms) +{ +} + +void NetHackQtDelay::wait() +{ + startTimer(msec); + qApp->enter_loop(); +} + +void NetHackQtDelay::timerEvent(QTimerEvent* timer) +{ + qApp->exit_loop(); + killTimers(); +} + +NetHackQtInvUsageWindow::NetHackQtInvUsageWindow(QWidget* parent) : + QWidget(parent) +{ +} + +void NetHackQtInvUsageWindow::drawWorn(QPainter& painter, obj* nhobj, int x, int y, bool canbe) +{ + short int glyph; + if (nhobj) + glyph=obj_to_glyph(nhobj); + else if (canbe) + glyph=cmap_to_glyph(S_room); + else + glyph=cmap_to_glyph(S_stone); + + qt_settings->glyphs().drawCell(painter,glyph,x,y); +} + +void NetHackQtInvUsageWindow::paintEvent(QPaintEvent*) +{ + // 012 + // + //0 WhB + //1 s"w + //2 gCg + //3 =A= + //4 T + //5 S + + QPainter painter; + painter.begin(this); + + // Blanks + drawWorn(painter,0,0,4,FALSE); + drawWorn(painter,0,0,5,FALSE); + drawWorn(painter,0,2,4,FALSE); + drawWorn(painter,0,2,5,FALSE); + + drawWorn(painter,uarm,1,3); // Armour + drawWorn(painter,uarmc,1,2); // Cloak + drawWorn(painter,uarmh,1,0); // Helmet + drawWorn(painter,uarms,0,1); // Shield + drawWorn(painter,uarmg,0,2); // Gloves - repeated + drawWorn(painter,uarmg,2,2); // Gloves - repeated +#ifdef TOURIST + drawWorn(painter,uarmf,1,5); // Shoes (feet) + drawWorn(painter,uarmu,1,4); // Undershirt +#else + drawWorn(painter,0 ,1,5,FALSE); + drawWorn(painter,uarmf,1,4); // Shoes (feet) +#endif + drawWorn(painter,uleft,0,3); // RingL + drawWorn(painter,uright,2,3); // RingR + + drawWorn(painter,uwep,2,1); // Weapon + drawWorn(painter,uswapwep,0,0); // Secondary weapon + drawWorn(painter,uamul,1,1); // Amulet + drawWorn(painter,ublindf,2,0); // Blindfold + + painter.end(); +} + +class SmallToolButton : public QToolButton { +public: + SmallToolButton(const QPixmap & pm, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char* slot, + QToolBar * parent) : + QToolButton(pm, textLabel, +#if QT_VERSION < 210 + QString::null, +#else + grouptext, +#endif + receiver, slot, parent) + { + } + + QSize sizeHint() const + { + // get just a couple more pixels for the map + return QToolButton::sizeHint()-QSize(0,2); + } +}; + + +NetHackQtMainWindow::NetHackQtMainWindow(NetHackQtKeyBuffer& ks) : + message(0), map(0), status(0), invusage(0), + keysink(ks), dirkey(0) +{ + QToolBar* toolbar = new QToolBar(this); +#if QT_VERSION >= 210 + setToolBarsMovable(FALSE); + toolbar->setHorizontalStretchable(TRUE); + toolbar->setVerticalStretchable(TRUE); +#endif + addToolBar(toolbar); + menubar = menuBar(); + + setCaption("Qt NetHack"); + if ( qt_compact_mode ) + setIcon(QPixmap(nh_icon_small)); + else + setIcon(QPixmap(nh_icon)); + + QPopupMenu* game=new QPopupMenu; + QPopupMenu* apparel=new QPopupMenu; + QPopupMenu* act1=new QPopupMenu; + QPopupMenu* act2 = qt_compact_mode ? new QPopupMenu : act1; + QPopupMenu* magic=new QPopupMenu; + QPopupMenu* info=new QPopupMenu; + + QPopupMenu *help; + +#ifdef KDE + help = kapp->getHelpMenu( TRUE, "" ); + help->insertSeparator(); +#else + help = qt_compact_mode ? info : new QPopupMenu; +#endif + + enum { OnDesktop=1, OnHandhelds=2 }; + struct Macro { + QPopupMenu* menu; + const char* name; + const char* action; + int flags; + } item[] = { + { game, 0, 0, 3}, + { game, "Version\tv", "v", 3}, + { game, "Compilation\tAlt+V", "\366", 3}, + { game, "History\tShift+V", "V", 3}, + { game, "Redraw\tCtrl+R", "\022", 0}, // useless + { game, "Options\tShift+O", "O", 3}, + { game, "Explore mode\tShift+X", "X", 3}, + { game, 0, 0, 3}, + { game, "Save\tSy", "Sy", 3}, + { game, "Quit\tAlt+Q", "\361", 3}, + + { apparel, "Apparel off\tShift+A", "A", 2}, + { apparel, "Remove many\tShift+A", "A", 1}, + { apparel, 0, 0, 3}, + { apparel, "Wield weapon\tw", "w", 3}, + { apparel, "Exchange weapons\tx", "x", 3}, + { apparel, "Two weapon combat\t#two", "#tw", 3}, + { apparel, "Load quiver\tShift+Q", "Q", 3}, + { apparel, 0, 0, 3}, + { apparel, "Wear armour\tShift+W", "W", 3}, + { apparel, "Take off armour\tShift+T", "T", 3}, + { apparel, 0, 0, 3}, + { apparel, "Put on non-armour\tShift+P", "P", 3}, + { apparel, "Remove non-armour\tShift+R", "R", 3}, + + { act1, "Again\tCtrl+A", "\001", 2}, + { act1, 0, 0, 3}, + { act1, "Apply\ta?", "a?", 3}, + { act1, "Chat\tAlt+C", "\343", 3}, + { act1, "Close door\tc", "c", 3}, + { act1, "Down\t>", ">", 3}, + { act1, "Drop many\tShift+D", "D", 2}, + { act1, "Drop\td?", "d?", 2}, + { act1, "Eat\te?", "e?", 2}, + { act1, "Engrave\tShift+E", "E", 3}, + { act1, "Fight\tShift+F", "F", 3}, + { act1, "Fire from quiver\tf", "f", 2}, + { act1, "Force\tAlt+F", "\346", 3}, + { act1, "Get\t,", ",", 2}, + { act1, "Jump\tAlt+J", "\352", 3}, + { act2, "Kick\tCtrl+D", "\004", 2}, + { act2, "Loot\tAlt+L", "\354", 3}, + { act2, "Open door\to", "o", 3}, + { act2, "Pay\tp", "p", 3}, + { act2, "Rest\t.", ".", 2}, + { act2, "Ride\t#ri", "#ri", 3}, + { act2, "Search\ts", "s", 3}, + { act2, "Sit\tAlt+S", "\363", 3}, + { act2, "Throw\tt", "t", 2}, + { act2, "Untrap\t#u", "#u", 3}, + { act2, "Up\t<", "<", 3}, + { act2, "Wipe face\tAlt+W", "\367", 3}, + + { magic, "Quaff potion\tq?", "q?", 3}, + { magic, "Read scroll/book\tr?", "r?", 3}, + { magic, "Zap wand\tz?", "z?", 3}, + { magic, "Zap spell\tShift+Z", "Z", 3}, + { magic, "Dip\tAlt+D", "\344", 3}, + { magic, "Rub\tAlt+R", "\362", 3}, + { magic, "Invoke\tAlt+I", "\351", 3}, + { magic, 0, 0, 3}, + { magic, "Offer\tAlt+O", "\357", 3}, + { magic, "Pray\tAlt+P", "\360", 3}, + { magic, 0, 0, 3}, + { magic, "Teleport\tCtrl+T", "\024", 3}, + { magic, "Monster action\tAlt+M", "\355", 3}, + { magic, "Turn undead\tAlt+T", "\364", 3}, + + { help, "Help\t?", "?", 3}, + { help, 0, 0, 3}, + { help, "What is here\t:", ":", 3}, + { help, "What is there\t;", ";", 3}, + { help, "What is...\t/y", "/y", 2}, + { help, 0, 0, 1}, + + { info, "Inventory\ti", "i", 3}, +#ifdef SLASHEM + { info, "Angbandish inventory\t*", "*", 3}, +#endif + { info, "Conduct\t#co", "#co", 3}, + { info, "Discoveries\t\\", "\\", 3}, + { info, "List/reorder spells\t+", "+", 3}, + { info, "Adjust letters\tAlt+A", "\341", 2}, + { info, 0, 0, 3}, + { info, "Name object\tAlt+N", "\356y?", 3}, + { info, "Name object type\tAlt+N", "\356n?", 3}, + { info, "Name creature\tShift+C", "C", 3}, + { info, 0, 0, 3}, + { info, "Qualifications\tAlt+E", "\345", 3}, + + { 0, 0, 0, 0 } + }; + + int i; + int count=0; + for (i=0; item[i].menu; i++) + if (item[i].name) count++; + + macro=new const char* [count]; + + game->insertItem("Qt settings...",1000); + help->insertItem("About Qt NetHack...",2000); + //help->insertItem("NetHack Guidebook...",3000); + help->insertSeparator(); + + count=0; + for (i=0; item[i].menu; i++) { + if ( item[i].flags & (qt_compact_mode ? 1 : 2) ) { + if (item[i].name) { + QString name = item[i].name; + if ( qt_compact_mode ) // accelerators aren't + name.replace(QRegExp("\t.*"),""); + item[i].menu->insertItem(name,count); + macro[count++]=item[i].action; + } else { + item[i].menu->insertSeparator(); + } + } + } + + menubar->insertItem("Game",game); + menubar->insertItem("Gear",apparel); + + if ( qt_compact_mode ) { + menubar->insertItem("A-J",act1); + menubar->insertItem("K-Z",act2); + menubar->insertItem("Magic",magic); + menubar->insertItem(QPixmap(info_xpm),info); + menubar->insertItem(QPixmap(map_xpm), this, SLOT(raiseMap())); + menubar->insertItem(QPixmap(msg_xpm), this, SLOT(raiseMessages())); + menubar->insertItem(QPixmap(stat_xpm), this, SLOT(raiseStatus())); + } else { + menubar->insertItem("Action",act1); + menubar->insertItem("Magic",magic); + menubar->insertItem("Info",info); + menubar->insertSeparator(); + menubar->insertItem("Help",help); + } + + QSignalMapper* sm = new QSignalMapper(this); + connect(sm, SIGNAL(mapped(const QString&)), this, SLOT(doKeys(const QString&))); + QToolButton* tb; + tb = new SmallToolButton( QPixmap(again_xpm),"Again","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "\001" ); + tb = new SmallToolButton( QPixmap(get_xpm),"Get","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "," ); + tb = new SmallToolButton( QPixmap(kick_xpm),"Kick","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "\004" ); + tb = new SmallToolButton( QPixmap(throw_xpm),"Throw","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "t" ); + tb = new SmallToolButton( QPixmap(fire_xpm),"Fire","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "f" ); + tb = new SmallToolButton( QPixmap(drop_xpm),"Drop","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "D" ); + tb = new SmallToolButton( QPixmap(eat_xpm),"Eat","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "e" ); + tb = new SmallToolButton( QPixmap(rest_xpm),"Rest","Action", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "." ); + tb = new SmallToolButton( QPixmap(cast_a_xpm),"Cast A","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Za" ); + tb = new SmallToolButton( QPixmap(cast_b_xpm),"Cast B","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Zb" ); + tb = new SmallToolButton( QPixmap(cast_c_xpm),"Cast C","Magic", sm, SLOT(map()), toolbar ); + sm->setMapping(tb, "Zc" ); + if ( !qt_compact_mode ) { + QWidget* filler = new QWidget(toolbar); + filler->setBackgroundMode(PaletteButton); + toolbar->setStretchableWidget(filler); + } + + connect(menubar,SIGNAL(activated(int)),this,SLOT(doMenuItem(int))); + +#ifdef KDE + setMenu (menubar); +#endif + + int x=0,y=0; + int w=QApplication::desktop()->width()-10; // XXX arbitrary extra space for frame + int h=QApplication::desktop()->height()-50; + + int maxwn; + int maxhn; + if (qt_tilewidth != NULL) { + maxwn = atoi(qt_tilewidth) * COLNO + 10; + } else { + maxwn = 1400; + } + if (qt_tileheight != NULL) { + maxhn = atoi(qt_tileheight) * ROWNO * 6/4; + } else { + maxhn = 1024; + } + + // Be exactly the size we want to be - full map... + if (w>maxwn) { + x+=(w-maxwn)/2; + w=maxwn; // Doesn't need to be any wider + } + if (h>maxhn) { + y+=(h-maxhn)/2; + h=maxhn; // Doesn't need to be any taller + } + + setGeometry(x,y,w,h); + + if ( qt_compact_mode ) { + stack = new QWidgetStack(this); + setCentralWidget(stack); + } else { + setCentralWidget(new QWidget(this)); + invusage = new NetHackQtInvUsageWindow(centralWidget()); + } +} + +void NetHackQtMainWindow::zoomMap() +{ + qt_settings->toggleGlyphSize(); +} + +void NetHackQtMainWindow::raiseMap() +{ + if ( stack->id(stack->visibleWidget()) == 0 ) { + zoomMap(); + } else { + stack->raiseWidget(0); + } +} + +void NetHackQtMainWindow::raiseMessages() +{ + stack->raiseWidget(1); +} + +void NetHackQtMainWindow::raiseStatus() +{ + stack->raiseWidget(2); +} + +class NetHackMimeSourceFactory : public QMimeSourceFactory { +public: + const QMimeSource* data(const QString& abs_name) const + { + const QMimeSource* r = 0; + if ( (NetHackMimeSourceFactory*)this == QMimeSourceFactory::defaultFactory() ) + r = QMimeSourceFactory::data(abs_name); + else + r = QMimeSourceFactory::defaultFactory()->data(abs_name); + if ( !r ) { + int sl = abs_name.length(); + do { + sl = abs_name.findRev('/',sl-1); + QString name = sl>=0 ? abs_name.mid(sl+1) : abs_name; + int dot = name.findRev('.'); + if ( dot >= 0 ) + name = name.left(dot); + if ( name == "map" ) + r = new QImageDrag(QImage(map_xpm)); + else if ( name == "msg" ) + r = new QImageDrag(QImage(msg_xpm)); + else if ( name == "stat" ) + r = new QImageDrag(QImage(stat_xpm)); + } while (!r && sl>0); + } + return r; + } +}; + +void NetHackQtMainWindow::doMenuItem(int id) +{ + switch (id) { + case 1000: + centerOnMain(qt_settings); + qt_settings->show(); + break; + case 2000: + QMessageBox::about(this, "About Qt NetHack", aboutMsg()); + break; + case 3000: { + QDialog dlg(this,0,TRUE); + (new QVBoxLayout(&dlg))->setAutoAdd(TRUE); + QTextBrowser browser(&dlg); + NetHackMimeSourceFactory ms; + browser.setMimeSourceFactory(&ms); + browser.setSource(QDir::currentDirPath()+"/Guidebook.html"); + if ( qt_compact_mode ) + dlg.showMaximized(); + dlg.exec(); + } + break; + default: + if ( id >= 0 ) + doKeys(macro[id]); + } +} + +void NetHackQtMainWindow::doKeys(const QString& k) +{ + keysink.Put(k); + qApp->exit_loop(); +} + +void NetHackQtMainWindow::AddMessageWindow(NetHackQtMessageWindow* window) +{ + message=window; + ShowIfReady(); +} + +void NetHackQtMainWindow::AddMapWindow(NetHackQtMapWindow* window) +{ + map=window; + ShowIfReady(); + connect(map,SIGNAL(resized()),this,SLOT(layout())); +} + +void NetHackQtMainWindow::AddStatusWindow(NetHackQtStatusWindow* window) +{ + status=window; + ShowIfReady(); +} + +void NetHackQtMainWindow::RemoveWindow(NetHackQtWindow* window) +{ + if (window==status) { + status=0; + ShowIfReady(); + } else if (window==map) { + map=0; + ShowIfReady(); + } else if (window==message) { + message=0; + ShowIfReady(); + } +} + +void NetHackQtMainWindow::updateInventory() +{ + if ( invusage ) + invusage->repaint(FALSE); +} + +void NetHackQtMainWindow::fadeHighlighting() +{ + if (status) { + status->fadeHighlighting(); + } +} + +void NetHackQtMainWindow::layout() +{ + if ( qt_compact_mode ) + return; + if (message && map && status) { + QSize maxs=map->Widget()->maximumSize(); + int maph=QMIN(height()*2/3,maxs.height()); + + QWidget* c = centralWidget(); + int h=c->height(); + int toph=h-maph; + int iuw=3*qt_settings->glyphs().width(); + int topw=(c->width()-iuw)/2; + + message->Widget()->setGeometry(0,0,topw,toph); + invusage->setGeometry(topw,0,iuw,toph); + status->Widget()->setGeometry(topw+iuw,0,topw,toph); + map->Widget()->setGeometry(QMAX(0,(c->width()-maxs.width())/2), + toph,c->width(),maph); + } +} + +void NetHackQtMainWindow::resizeEvent(QResizeEvent*) +{ + layout(); +#ifdef KDE + updateRects(); +#endif +} + +void NetHackQtMainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if ( dirkey ) { + doKeys(QString(QChar(dirkey))); + if ( !event->isAutoRepeat() ) + dirkey = 0; + } +} + +void NetHackQtMainWindow::keyPressEvent(QKeyEvent* event) +{ + // Global key controls + + // For desktop, arrow keys scroll map, since we don't want players + // to think that's the way to move. For handhelds, the normal way is to + // click-to-travel, so we allow the cursor keys for fine movements. + + // 321 + // 4 0 + // 567 + + if ( event->isAutoRepeat() && + event->key() >= Key_Left && event->key() <= Key_Down ) + return; + + const char* d = iflags.num_pad ? ndir : sdir; + switch (event->key()) { + case Key_Up: + if ( dirkey == d[0] ) + dirkey = d[1]; + else if ( dirkey == d[4] ) + dirkey = d[3]; + else + dirkey = d[2]; + break; case Key_Down: + if ( dirkey == d[0] ) + dirkey = d[7]; + else if ( dirkey == d[4] ) + dirkey = d[5]; + else + dirkey = d[6]; + break; case Key_Left: + if ( dirkey == d[2] ) + dirkey = d[1]; + else if ( dirkey == d[6] ) + dirkey = d[7]; + else + dirkey = d[0]; + break; case Key_Right: + if ( dirkey == d[2] ) + dirkey = d[3]; + else if ( dirkey == d[6] ) + dirkey = d[5]; + else + dirkey = d[4]; + break; case Key_Prior: + dirkey = 0; + if (message) message->Scroll(0,-1); + break; case Key_Next: + dirkey = 0; + if (message) message->Scroll(0,+1); + break; case Key_Space: + if ( flags.rest_on_space ) { + event->ignore(); + return; + } + case Key_Enter: + if ( map ) + map->clickCursor(); + break; default: + dirkey = 0; + event->ignore(); + } +} + +void NetHackQtMainWindow::closeEvent(QCloseEvent* e) +{ + if ( program_state.something_worth_saving ) { + switch ( QMessageBox::information( this, "NetHack", + "This will end your NetHack session", + "&Save", "&Cancel", 0, 1 ) ) + { + case 0: + // See dosave() function + if (dosave0()) { + u.uhp = -1; + NetHackQtBind::qt_exit_nhwindows(0); + terminate(EXIT_SUCCESS); + } + break; + case 1: + break; // ignore the event + } + } else { + e->accept(); + } +} + +void NetHackQtMainWindow::ShowIfReady() +{ + if (message && map && status) { + QPoint pos(0,0); + QWidget* p = qt_compact_mode ? stack : centralWidget(); + message->Widget()->recreate(p,0,pos); + map->Widget()->recreate(p,0,pos); + status->Widget()->recreate(p,0,pos); + if ( qt_compact_mode ) { + message->setMap(map); + stack->addWidget(map->Widget(), 0); + stack->addWidget(message->Widget(), 1); + stack->addWidget(status->Widget(), 2); + raiseMap(); + } else { + layout(); + } + showMaximized(); + } else if (isVisible()) { + hide(); + } +} + + +NetHackQtYnDialog::NetHackQtYnDialog(NetHackQtKeyBuffer& keysrc,const char* q,const char* ch,char df) : + QDialog(qApp->mainWidget(),0,FALSE), + question(q), choices(ch), def(df), + keysource(keysrc) +{ + setCaption("NetHack: Question"); +} + +char NetHackQtYnDialog::Exec() +{ + QString ch(choices); + int ch_per_line=6; + QString qlabel; + QString enable; + if ( qt_compact_mode && !choices ) { + // expand choices from prompt + // ##### why isn't choices set properly??? + const char* c=question; + while ( *c && *c != '[' ) + c++; + qlabel = QString(question).left(c-question); + if ( *c ) { + c++; + if ( *c == '-' ) + ch.append(*c++); + char from=0; + while ( *c && *c != ']' && *c != ' ' ) { + if ( *c == '-' ) { + from = c[-1]; + } else if ( from ) { + for (char f=from+1; f<=*c; f++) + ch.append(f); + from = 0; + } else { + ch.append(*c); + from = 0; + } + c++; + } + if ( *c == ' ' ) { + while ( *c && *c != ']' ) { + if ( *c == '*' || *c == '?' ) + ch.append(*c); + c++; + } + } + } + if ( strstr(question, "what direction") ) { + // We replace this regardless, since sometimes you get choices. + const char* d = iflags.num_pad ? ndir : sdir; + enable=ch; + ch=""; + ch.append(d[1]); + ch.append(d[2]); + ch.append(d[3]); + ch.append(d[0]); + ch.append('.'); + ch.append(d[4]); + ch.append(d[7]); + ch.append(d[6]); + ch.append(d[5]); + ch.append(d[8]); + ch.append(d[9]); + ch_per_line = 3; + def = ' '; + } else { + // Hmm... they'll have to use a virtual keyboard + } + } else { + qlabel = question; + } + if (!ch.isNull()) { + QVBoxLayout vb(this); + vb.setAutoAdd(TRUE); + bool bigq = qlabel.length()>40; + if ( bigq ) { + QLabel* q = new QLabel(qlabel,this); + q->setAlignment(AlignLeft|WordBreak); + q->setMargin(4); + } + QButtonGroup group(ch_per_line, Horizontal, + bigq ? QString::null : qlabel, this); + + int nchoices=ch.length(); + + bool allow_count=ch.contains('#'); + + const int margin=8; + const int gutter=8; + const int extra=fontMetrics().height(); // Extra for group + int x=margin, y=extra+margin; + int butsize=fontMetrics().height()*2+5; + + QPushButton* button; + for (int i=0; isetEnabled(FALSE); + } + button->setFixedSize(butsize,butsize); // Square + if (ch[i]==def) button->setDefault(TRUE); + if (i%10==9) { + // last in row + x=margin; + y+=butsize+gutter; + } else { + x+=butsize+gutter; + } + } + + connect(&group,SIGNAL(clicked(int)),this,SLOT(doneItem(int))); + + QLabel* lb=0; + QLineEdit* le=0; + + if (allow_count) { + QHBox *hb = new QHBox(this); + lb=new QLabel("Count: ",hb); + le=new QLineEdit(hb); + } + + adjustSize(); + centerOnMain(this); + show(); + char choice=0; + char ch_esc=0; + for (uint i=0; i= 1000 ) { + choice = ch[result() - 1000].latin1(); + } + if ( !choice ) + qApp->enter_loop(); + } + hide(); + if (allow_count && !le->text().isEmpty()) { + yn_number=atoi(le->text()); + choice='#'; + } + return choice; + } else { + QLabel label(qlabel,this); + QPushButton cancel("Dismiss",this); + label.setFrameStyle(QFrame::Box|QFrame::Sunken); + label.setAlignment(AlignCenter); + label.resize(fontMetrics().width(qlabel)+60,30+fontMetrics().height()); + cancel.move(width()/2-cancel.width()/2,label.geometry().bottom()+8); + connect(&cancel,SIGNAL(clicked()),this,SLOT(reject())); + centerOnMain(this); + setResult(-1); + show(); + while (result()<0 && keysource.Empty()) { + qApp->enter_loop(); + } + hide(); + if (keysource.Empty()) { + return '\033'; + } else { + return keysource.GetAscii(); + } + } +} +void NetHackQtYnDialog::keyPressEvent(QKeyEvent* event) +{ + // Don't want QDialog's Return/Esc behaviour + event->ignore(); +} + +void NetHackQtYnDialog::doneItem(int i) +{ + done(i+1000); +} + +void NetHackQtYnDialog::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +NetHackQtGlyphs::NetHackQtGlyphs() +{ + const char* tile_file = "nhtiles.bmp"; + if ( iflags.wc_tile_file ) + tile_file = iflags.wc_tile_file; + + if (!img.load(tile_file)) { + tile_file = "x11tiles"; + if (!img.load(tile_file)) { + QString msg; + msg.sprintf("Cannot load x11tiles or nhtiles.bmp"); + QMessageBox::warning(0, "IO Error", msg); + } else { + tiles_per_row = TILES_PER_ROW; + if (img.width()%tiles_per_row) { + impossible("Tile file \"%s\" has %d columns, not multiple of row count (%d)", + tile_file, img.width(), tiles_per_row); + } + } + } else { + tiles_per_row = 40; + } + + if ( iflags.wc_tile_width ) + tilefile_tile_W = iflags.wc_tile_width; + else + tilefile_tile_W = img.width() / tiles_per_row; + if ( iflags.wc_tile_height ) + tilefile_tile_H = iflags.wc_tile_height; + else + tilefile_tile_H = tilefile_tile_W; + + setSize(tilefile_tile_W, tilefile_tile_H); +} + +void NetHackQtGlyphs::drawGlyph(QPainter& painter, int glyph, int x, int y) +{ + int tile = glyph2tile[glyph]; + int px = (tile%tiles_per_row)*width(); + int py = tile/tiles_per_row*height(); + + painter.drawPixmap( + x, + y, + pm, + px,py, + width(),height() + ); +} +void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly) +{ + drawGlyph(painter,glyph,cellx*width(),celly*height()); +} +void NetHackQtGlyphs::setSize(int w, int h) +{ + if ( size == QSize(w,h) ) + return; + + bool was1 = size == pm1.size(); + size = QSize(w,h); + if (!w || !h) + return; // Still not decided + + if ( size == pm1.size() ) { + pm = pm1; + return; + } + if ( size == pm2.size() ) { + pm = pm2; + return; + } + + if (w==tilefile_tile_W && h==tilefile_tile_H) { + pm.convertFromImage(img); + } else { + QApplication::setOverrideCursor( Qt::waitCursor ); + QImage scaled = img.smoothScale( + w*img.width()/tilefile_tile_W, + h*img.height()/tilefile_tile_H + ); + pm.convertFromImage(scaled,Qt::ThresholdDither|Qt::PreferDither); + QApplication::restoreOverrideCursor(); + } + (was1 ? pm2 : pm1) = pm; +} + + +////////////////////////////////////////////////////////////// +// +// The ugly C binding classes... +// +////////////////////////////////////////////////////////////// + + +NetHackQtMenuOrTextWindow::NetHackQtMenuOrTextWindow(NetHackQtKeyBuffer& ks) : + actual(0), + keysource(ks) +{ +} + +QWidget* NetHackQtMenuOrTextWindow::Widget() +{ + if (!actual) impossible("Widget called before we know if Menu or Text"); + return actual->Widget(); +} + +// Text +void NetHackQtMenuOrTextWindow::Clear() +{ + if (!actual) impossible("Clear called before we know if Menu or Text"); + actual->Clear(); +} +void NetHackQtMenuOrTextWindow::Display(bool block) +{ + if (!actual) impossible("Display called before we know if Menu or Text"); + actual->Display(block); +} +bool NetHackQtMenuOrTextWindow::Destroy() +{ + if (!actual) impossible("Destroy called before we know if Menu or Text"); + return actual->Destroy(); +} + +void NetHackQtMenuOrTextWindow::PutStr(int attr, const char* text) +{ + if (!actual) actual=new NetHackQtTextWindow(keysource); + actual->PutStr(attr,text); +} + +// Menu +void NetHackQtMenuOrTextWindow::StartMenu() +{ + if (!actual) actual=new NetHackQtMenuWindow(keysource); + actual->StartMenu(); +} +void NetHackQtMenuOrTextWindow::AddMenu(int glyph, const ANY_P* identifier, char ch, char gch, int attr, + const char* str, bool presel) +{ + if (!actual) impossible("AddMenu called before we know if Menu or Text"); + actual->AddMenu(glyph,identifier,ch,gch,attr,str,presel); +} +void NetHackQtMenuOrTextWindow::EndMenu(const char* prompt) +{ + if (!actual) impossible("EndMenu called before we know if Menu or Text"); + actual->EndMenu(prompt); +} +int NetHackQtMenuOrTextWindow::SelectMenu(int how, MENU_ITEM_P **menu_list) +{ + if (!actual) impossible("SelectMenu called before we know if Menu or Text"); + return actual->SelectMenu(how,menu_list); +} + + +// XXX Should be from Options +// +// XXX Hmm. Tricky part is that perhaps some macros should only be active +// XXX when a key is about to be gotten. For example, the user could +// XXX define "-" to do "E-yyyyyyyy\r", but would still need "-" for +// XXX other purposes. Maybe just too bad. +// +struct { + int key; + int state; + const char* macro; +} key_macro[]={ + { Qt::Key_F1, 0, "n100." }, // Rest (x100) + { Qt::Key_F2, 0, "n20s" }, // Search (x20) + { Qt::Key_F3, 0, "o8o4o6o2o8o4o6o2o8o4o6o2" }, // Open all doors (x3) + { Qt::Key_Tab, 0, "\001" }, + { 0, 0, 0 } +}; + + +NetHackQtBind::NetHackQtBind(int& argc, char** argv) : +#ifdef KDE + KApplication(argc,argv) +#elif defined(QWS) // not quite the right condition + QPEApplication(argc,argv) +#else + QApplication(argc,argv) +#endif +{ + QPixmap pm("nhsplash.xpm"); + if ( iflags.wc_splash_screen && !pm.isNull() ) { + QVBox *vb = new QVBox(0,0, + WStyle_Customize | WStyle_NoBorder | nh_WX11BypassWM | WStyle_StaysOnTop ); + splash = vb; + QLabel *lsplash = new QLabel(vb); + lsplash->setAlignment(AlignCenter); + lsplash->setPixmap(pm); + QLabel* capt = new QLabel("Loading...",vb); + capt->setAlignment(AlignCenter); + if ( pm.mask() ) { + lsplash->setFixedSize(pm.size()); + lsplash->setMask(*pm.mask()); + } + splash->move((QApplication::desktop()->width()-pm.width())/2, + (QApplication::desktop()->height()-pm.height())/2); + //splash->setGeometry(0,0,100,100); + if ( qt_compact_mode ) { + splash->showMaximized(); + } else { + vb->setFrameStyle(QFrame::WinPanel|QFrame::Raised); + vb->setMargin(10); + splash->adjustSize(); + splash->show(); + } + + // force content refresh outside event loop + splash->repaint(FALSE); + lsplash->repaint(FALSE); + capt->repaint(FALSE); + qApp->flushX(); + + } else { + splash = 0; + } + main = new NetHackQtMainWindow(keybuffer); +#if defined(QWS) // not quite the right condition + showMainWidget(main); +#else + setMainWidget(main); +#endif + qt_settings=new NetHackQtSettings(main->width(),main->height()); +} + +void NetHackQtBind::qt_init_nhwindows(int* argc, char** argv) +{ +#ifdef UNIX +// Userid control +// +// Michael Hohmuth ... +// +// As the game runs setuid games, it must seteuid(getuid()) before +// calling XOpenDisplay(), and reset the euid afterwards. +// Otherwise, it can't read the $HOME/.Xauthority file and whines about +// not being able to open the X display (if a magic-cookie +// authorization mechanism is being used). + + uid_t gamesuid=geteuid(); + seteuid(getuid()); +#endif + + QApplication::setColorSpec(ManyColor); + instance=new NetHackQtBind(*argc,argv); + +#ifdef UNIX + seteuid(gamesuid); +#endif + +#ifdef _WS_WIN_ + // This nethack engine feature should be moved into windowport API + nt_kbhit = NetHackQtBind::qt_kbhit; +#endif +} + +int NetHackQtBind::qt_kbhit() +{ + return !keybuffer.Empty(); +} + +static bool have_asked = FALSE; + +void NetHackQtBind::qt_player_selection() +{ + if ( !have_asked ) + qt_askname(); +} + +NetHackQtSavedGameSelector::NetHackQtSavedGameSelector(const char** saved) : + QDialog(qApp->mainWidget(),"sgsel",TRUE) +{ + QVBoxLayout *vbl = new QVBoxLayout(this,6); + QHBox* hb; + + QLabel* logo = new QLabel(this); vbl->addWidget(logo); + logo->setAlignment(AlignCenter); + logo->setPixmap(QPixmap("nhsplash.xpm")); + QLabel* attr = new QLabel("by the NetHack DevTeam",this); + attr->setAlignment(AlignCenter); + vbl->addWidget(attr); + vbl->addStretch(2); + /* + QLabel* logo = new QLabel(hb); + hb = new QHBox(this); + vbl->addWidget(hb, AlignCenter); + logo->setPixmap(QPixmap(nh_icon)); + logo->setAlignment(AlignRight|AlignVCenter); + new QLabel(nh_attribution,hb); + */ + + hb = new QHBox(this); + vbl->addWidget(hb, AlignCenter); + QPushButton* q = new QPushButton("Quit",hb); + connect(q, SIGNAL(clicked()), this, SLOT(reject())); + QPushButton* c = new QPushButton("New Game",hb); + connect(c, SIGNAL(clicked()), this, SLOT(accept())); + c->setDefault(TRUE); + + QButtonGroup* bg = new QButtonGroup(3, Horizontal, "Saved Characters",this); + vbl->addWidget(bg); + connect(bg, SIGNAL(clicked(int)), this, SLOT(done(int))); + for (int i=0; saved[i]; i++) { + QPushButton* b = new QPushButton(saved[i],bg); + bg->insert(b, i+2); + } +} + +int NetHackQtSavedGameSelector::choose() +{ +#if defined(QWS) // probably safe with Qt 3, too (where show!=exec in QDialog). + if ( qt_compact_mode ) + showMaximized(); +#endif + return exec()-2; +} + +void NetHackQtBind::qt_askname() +{ + have_asked = TRUE; + + // We do it all here, and nothing in askname + + char** saved = get_saved_games(); + int ch = -1; + if ( saved && *saved ) { + if ( splash ) splash->hide(); + NetHackQtSavedGameSelector sgsel((const char**)saved); + ch = sgsel.choose(); + if ( ch >= 0 ) + strcpy(plname,saved[ch]); + } + free_saved_games(saved); + + switch (ch) { + case -1: + if ( splash ) splash->hide(); + if (NetHackQtPlayerSelector(keybuffer).Choose()) + return; + case -2: + break; + default: + return; + } + + // Quit + clearlocks(); + qt_exit_nhwindows(0); + terminate(0); +} + +void NetHackQtBind::qt_get_nh_event() +{ +} + +#if defined(QWS) +// Kludge to access lastWindowClosed() signal. +class TApp : public QApplication { +public: + TApp(int& c, char**v) : QApplication(c,v) {} + void lwc() { emit lastWindowClosed(); } +}; +#endif + +void NetHackQtBind::qt_exit_nhwindows(const char *) +{ +#if defined(QWS) + // Avoids bug in SHARP SL5500 + ((TApp*)qApp)->lwc(); + qApp->quit(); +#endif + + delete instance; // ie. qApp +} + +void NetHackQtBind::qt_suspend_nhwindows(const char *) +{ +} + +void NetHackQtBind::qt_resume_nhwindows() +{ +} + +static QArray id_to_window; + +winid NetHackQtBind::qt_create_nhwindow(int type) +{ + winid id; + for (id = 0; id < (winid) id_to_window.size(); id++) { + if ( !id_to_window[id] ) + break; + } + if ( id == (winid) id_to_window.size() ) + id_to_window.resize(id+1); + + NetHackQtWindow* window=0; + + switch (type) { + case NHW_MAP: { + NetHackQtMapWindow* w=new NetHackQtMapWindow(clickbuffer); + main->AddMapWindow(w); + window=w; + } break; case NHW_MESSAGE: { + NetHackQtMessageWindow* w=new NetHackQtMessageWindow; + main->AddMessageWindow(w); + window=w; + } break; case NHW_STATUS: { + NetHackQtStatusWindow* w=new NetHackQtStatusWindow; + main->AddStatusWindow(w); + window=w; + } break; case NHW_MENU: + window=new NetHackQtMenuOrTextWindow(keybuffer); + break; case NHW_TEXT: + window=new NetHackQtTextWindow(keybuffer); + } + + window->nhid = id; + + // Note: use of isHidden does not work with Qt 2.1 + if ( splash +#if QT_VERSION >= 300 + && !main->isHidden() +#else + && main->isVisible() +#endif + ) + { + delete splash; + splash = 0; + } + + id_to_window[id] = window; + return id; +} + +void NetHackQtBind::qt_clear_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->Clear(); +} + +void NetHackQtBind::qt_display_nhwindow(winid wid, BOOLEAN_P block) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->Display(block); +} + +void NetHackQtBind::qt_destroy_nhwindow(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + main->RemoveWindow(window); + if (window->Destroy()) + delete window; + id_to_window[wid] = 0; +} + +void NetHackQtBind::qt_curs(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->CursorTo(x,y); +} + +void NetHackQtBind::qt_putstr(winid wid, int attr, const char *text) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->PutStr(attr,text); +} + +void NetHackQtBind::qt_display_file(const char *filename, BOOLEAN_P must_exist) +{ + NetHackQtTextWindow* window=new NetHackQtTextWindow(keybuffer); + bool complain = FALSE; + +#ifdef DLB + { + dlb *f; + char buf[BUFSZ]; + char *cr; + + window->Clear(); + f = dlb_fopen(filename, "r"); + if (!f) { + complain = must_exist; + } else { + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; +#ifdef MSDOS + if ((cr = index(buf, '\r')) != 0) *cr = 0; +#endif + if (index(buf, '\t') != 0) (void) tabexpand(buf); + window->PutStr(ATR_NONE, buf); + } + window->Display(FALSE); + (void) dlb_fclose(f); + } + } +#else + QFile file(filename); + + if (file.open(IO_ReadOnly)) { + char line[128]; + while (file.readLine(line,127) >= 0) { + line[strlen(line)-1]=0;// remove newline + window->PutStr(ATR_NONE,line); + } + window->Display(FALSE); + } else { + complain = must_exist; + } +#endif + + if (complain) { + QString message; + message.sprintf("File not found: %s\n",filename); + QMessageBox::message("File Error", (const char*)message, "Ignore"); + } +} + +void NetHackQtBind::qt_start_menu(winid wid) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->StartMenu(); +} + +void NetHackQtBind::qt_add_menu(winid wid, int glyph, + const ANY_P * identifier, CHAR_P ch, CHAR_P gch, int attr, + const char *str, BOOLEAN_P presel) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->AddMenu(glyph, identifier, ch, gch, attr, str, presel); +} + +void NetHackQtBind::qt_end_menu(winid wid, const char *prompt) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->EndMenu(prompt); +} + +int NetHackQtBind::qt_select_menu(winid wid, int how, MENU_ITEM_P **menu_list) +{ + NetHackQtWindow* window=id_to_window[wid]; + return window->SelectMenu(how,menu_list); +} + +void NetHackQtBind::qt_update_inventory() +{ + if (main) + main->updateInventory(); + /* doesn't work yet + if (program_state.something_worth_saving && flags.perm_invent) + display_inventory(NULL, FALSE); + */ +} + +void NetHackQtBind::qt_mark_synch() +{ +} + +void NetHackQtBind::qt_wait_synch() +{ +} + +void NetHackQtBind::qt_cliparound(int x, int y) +{ + // XXXNH - winid should be a parameter! + qt_cliparound_window(WIN_MAP,x,y); +} + +void NetHackQtBind::qt_cliparound_window(winid wid, int x, int y) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->ClipAround(x,y); +} +void NetHackQtBind::qt_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph) +{ + NetHackQtWindow* window=id_to_window[wid]; + window->PrintGlyph(x,y,glyph); +} +//void NetHackQtBind::qt_print_glyph_compose(winid wid,XCHAR_P x,XCHAR_P y,int glyph1, int glyph2) +//{ + //NetHackQtWindow* window=id_to_window[wid]; + //window->PrintGlyphCompose(x,y,glyph1,glyph2); +//} + +void NetHackQtBind::qt_raw_print(const char *str) +{ + puts(str); +} + +void NetHackQtBind::qt_raw_print_bold(const char *str) +{ + puts(str); +} + +int NetHackQtBind::qt_nhgetch() +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key arrives. + // + while (keybuffer.Empty()) { + qApp->enter_loop(); + } + + return keybuffer.GetAscii(); +} + +int NetHackQtBind::qt_nh_poskey(int *x, int *y, int *mod) +{ + if (main) + main->fadeHighlighting(); + + // Process events until a key or map-click arrives. + // + while (keybuffer.Empty() && clickbuffer.Empty()) { + qApp->enter_loop(); + } + if (!keybuffer.Empty()) { + return keybuffer.GetAscii(); + } else { + *x=clickbuffer.NextX(); + *y=clickbuffer.NextY(); + *mod=clickbuffer.NextMod(); + clickbuffer.Get(); + return 0; + } +} + +void NetHackQtBind::qt_nhbell() +{ + QApplication::beep(); +} + +int NetHackQtBind::qt_doprev_message() +{ + // Don't need it - uses scrollbar + // XXX but could make this a shortcut + return 0; +} + +char NetHackQtBind::qt_yn_function(const char *question, const char *choices, CHAR_P def) +{ + if (qt_settings->ynInMessages() && WIN_MESSAGE!=WIN_ERR) { + // Similar to X11 windowport `slow' feature. + + char message[BUFSZ]; + char yn_esc_map='\033'; + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + Strcpy(choicebuf, choices); + if ((cb = index(choicebuf, '\033')) != 0) { + // anything beyond is hidden + *cb = '\0'; + } + Sprintf(message, "%s [%s] ", question, choicebuf); + if (def) Sprintf(eos(message), "(%c) ", def); + // escape maps to 'q' or 'n' or default, in that order + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + Strcpy(message, question); + } + +#ifdef USE_POPUPS + // Improve some special-cases (DIRKS 08/02/23) + if (strcmp (choices,"ynq") == 0) { + switch (QMessageBox::information (qApp->mainWidget(),"NetHack",question,"&Yes","&No","&Quit",0,2)) + { + case 0: return 'y'; + case 1: return 'n'; + case 2: return 'q'; + } + } + + if (strcmp (choices,"yn") == 0) { + switch (QMessageBox::information(qApp->mainWidget(),"NetHack",question,"&Yes", "&No",0,1)) + { + case 0: return 'y'; + case 1: return 'n'; + } + } +#endif + + NetHackQtBind::qt_putstr(WIN_MESSAGE, ATR_BOLD, message); + + int result=-1; + while (result<0) { + char ch=NetHackQtBind::qt_nhgetch(); + if (ch=='\033') { + result=yn_esc_map; + } else if (choices && !index(choices,ch)) { + if (def && (ch==' ' || ch=='\r' || ch=='\n')) { + result=def; + } else { + NetHackQtBind::qt_nhbell(); + // and try again... + } + } else { + result=ch; + } + } + + NetHackQtBind::qt_clear_nhwindow(WIN_MESSAGE); + + return result; + } else { + NetHackQtYnDialog dialog(keybuffer,question,choices,def); + return dialog.Exec(); + } +} + +void NetHackQtBind::qt_getlin(const char *prompt, char *line) +{ + NetHackQtStringRequestor requestor(keybuffer,prompt); + if (!requestor.Get(line)) { + line[0]=0; + } +} + +NetHackQtExtCmdRequestor::NetHackQtExtCmdRequestor(NetHackQtKeyBuffer& ks) : + QDialog(qApp->mainWidget(), "ext-cmd", FALSE), + keysource(ks) +{ + int marg=4; + QVBoxLayout *l = new QVBoxLayout(this,marg,marg); + + QPushButton* can = new QPushButton("Cancel", this); + can->setDefault(TRUE); + can->setMinimumSize(can->sizeHint()); + l->addWidget(can); + + QButtonGroup *group=new QButtonGroup("",0); + QGroupBox *grid=new QGroupBox("Extended commands",this); + l->addWidget(grid); + + int i; + int butw=50; + QFontMetrics fm = fontMetrics(); + for (i=0; extcmdlist[i].ef_txt; i++) { + butw = QMAX(butw,30+fm.width(extcmdlist[i].ef_txt)); + } + int ncols=4; + int nrows=(i+ncols-1)/ncols; + + QVBoxLayout* bl = new QVBoxLayout(grid,marg); + bl->addSpacing(fm.height()); + QGridLayout* gl = new QGridLayout(nrows,ncols,marg); + bl->addLayout(gl); + for (i=0; extcmdlist[i].ef_txt; i++) { + QPushButton* pb=new QPushButton(extcmdlist[i].ef_txt, grid); + pb->setMinimumSize(butw,pb->sizeHint().height()); + group->insert(pb); + gl->addWidget(pb,i/ncols,i%ncols); + } + connect(group,SIGNAL(clicked(int)),this,SLOT(done(int))); + + bl->activate(); + l->activate(); + resize(1,1); + + connect(can,SIGNAL(clicked()),this,SLOT(cancel())); +} + +void NetHackQtExtCmdRequestor::cancel() +{ + setResult(-1); + qApp->exit_loop(); +} + +void NetHackQtExtCmdRequestor::done(int i) +{ + setResult(i); + qApp->exit_loop(); +} + +int NetHackQtExtCmdRequestor::get() +{ + const int none = -10; + char str[32]; + int cursor=0; + resize(1,1); // pack + centerOnMain(this); + show(); + setResult(none); + while (result()==none) { + while (result()==none && !keysource.Empty()) { + char k=keysource.GetAscii(); + if (k=='\r' || k=='\n' || k==' ' || k=='\033') { + setResult(-1); + } else { + str[cursor++] = k; + int r=-1; + for (int i=0; extcmdlist[i].ef_txt; i++) { + if (qstrnicmp(str, extcmdlist[i].ef_txt, cursor)==0) { + if ( r == -1 ) + r = i; + else + r = -2; + } + } + if ( r == -1 ) { // no match! + QApplication::beep(); + cursor=0; + } else if ( r != -2 ) { // only one match + setResult(r); + } + } + } + if (result()==none) + qApp->enter_loop(); + } + hide(); + return result(); +} + + +int NetHackQtBind::qt_get_ext_cmd() +{ + NetHackQtExtCmdRequestor requestor(keybuffer); + return requestor.get(); +} + +void NetHackQtBind::qt_number_pad(int) +{ + // Ignore. +} + +void NetHackQtBind::qt_delay_output() +{ + NetHackQtDelay delay(15); + delay.wait(); +} + +void NetHackQtBind::qt_start_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_end_screen() +{ + // Ignore. +} + +void NetHackQtBind::qt_outrip(winid wid, int how) +{ + NetHackQtWindow* window=id_to_window[wid]; + + window->UseRIP(how); +} + +bool NetHackQtBind::notify(QObject *receiver, QEvent *event) +{ + // Ignore Alt-key navigation to menubar, it's annoying when you + // use Alt-Direction to move around. + if ( main && event->type()==QEvent::KeyRelease && main==receiver + && ((QKeyEvent*)event)->key() == Key_Alt ) + return TRUE; + + bool result=QApplication::notify(receiver,event); + if (event->type()==QEvent::KeyPress) { + QKeyEvent* key_event=(QKeyEvent*)event; + + if (!key_event->isAccepted()) { + const int k=key_event->key(); + bool macro=FALSE; + for (int i=0; !macro && key_macro[i].key; i++) { + if (key_macro[i].key==k + && ((key_macro[i].state&key_event->state())==key_macro[i].state)) + { + keybuffer.Put(key_macro[i].macro); + macro=TRUE; + } + } + char ch=key_event->ascii(); + if ( !ch && (key_event->state() & Qt::ControlButton) ) { + // On Mac, ascii control codes are not sent, force them. + if ( k>=Qt::Key_A && k<=Qt::Key_Z ) + ch = k - Qt::Key_A + 1; + } + if (!macro && ch) { + bool alt = (key_event->state()&AltButton) || + (k >= Key_0 && k <= Key_9 && (key_event->state()&ControlButton)); + keybuffer.Put(key_event->key(),ch + (alt ? 128 : 0), + key_event->state()); + key_event->accept(); + result=TRUE; + } + + if (ch || macro) { + qApp->exit_loop(); + } + } + } + return result; +} + +NetHackQtBind* NetHackQtBind::instance=0; +NetHackQtKeyBuffer NetHackQtBind::keybuffer; +NetHackQtClickBuffer NetHackQtBind::clickbuffer; +NetHackQtMainWindow* NetHackQtBind::main=0; +QWidget* NetHackQtBind::splash=0; + + +extern "C" struct window_procs Qt_procs; + +struct window_procs Qt_procs = { + "Qt", + WC_COLOR|WC_HILITE_PET| + WC_ASCII_MAP|WC_TILED_MAP| + WC_FONT_MAP|WC_TILE_FILE|WC_TILE_WIDTH|WC_TILE_HEIGHT| + WC_PLAYER_SELECTION|WC_SPLASH_SCREEN, + 0L, + NetHackQtBind::qt_init_nhwindows, + NetHackQtBind::qt_player_selection, + NetHackQtBind::qt_askname, + NetHackQtBind::qt_get_nh_event, + NetHackQtBind::qt_exit_nhwindows, + NetHackQtBind::qt_suspend_nhwindows, + NetHackQtBind::qt_resume_nhwindows, + NetHackQtBind::qt_create_nhwindow, + NetHackQtBind::qt_clear_nhwindow, + NetHackQtBind::qt_display_nhwindow, + NetHackQtBind::qt_destroy_nhwindow, + NetHackQtBind::qt_curs, + NetHackQtBind::qt_putstr, + NetHackQtBind::qt_display_file, + NetHackQtBind::qt_start_menu, + NetHackQtBind::qt_add_menu, + NetHackQtBind::qt_end_menu, + NetHackQtBind::qt_select_menu, + genl_message_menu, /* no need for X-specific handling */ + NetHackQtBind::qt_update_inventory, + NetHackQtBind::qt_mark_synch, + NetHackQtBind::qt_wait_synch, +#ifdef CLIPPING + NetHackQtBind::qt_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + NetHackQtBind::qt_print_glyph, + //NetHackQtBind::qt_print_glyph_compose, + NetHackQtBind::qt_raw_print, + NetHackQtBind::qt_raw_print_bold, + NetHackQtBind::qt_nhgetch, + NetHackQtBind::qt_nh_poskey, + NetHackQtBind::qt_nhbell, + NetHackQtBind::qt_doprev_message, + NetHackQtBind::qt_yn_function, + NetHackQtBind::qt_getlin, + NetHackQtBind::qt_get_ext_cmd, + NetHackQtBind::qt_number_pad, + NetHackQtBind::qt_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + NetHackQtBind::qt_start_screen, + NetHackQtBind::qt_end_screen, +#ifdef GRAPHIC_TOMBSTONE + NetHackQtBind::qt_outrip, +#else + genl_outrip, +#endif + genl_preference_update, +}; + +extern "C" void play_usersound(const char* filename, int volume) +{ +#ifdef USER_SOUNDS +#ifndef QT_NO_SOUND + QSound::play(filename); +#endif +#endif +} + +#include "qt_win.moc" +#ifndef KDE +#include "qt_kde0.moc" +#endif +#if QT_VERSION >= 300 +#include "qttableview.moc" +#endif diff --git a/win/Qt/qttableview.cpp b/win/Qt/qttableview.cpp new file mode 100644 index 0000000..abeda15 --- /dev/null +++ b/win/Qt/qttableview.cpp @@ -0,0 +1,2275 @@ +/********************************************************************** +** $Id: qttableview.cpp,v 1.2 2002/03/09 03:13:15 jwalz Exp $ +** +** Implementation of QtTableView class +** +** Created : 941115 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file contains a class moved out of the Qt GUI Toolkit API. It +** may be used, distributed and modified without limitation. +** +**********************************************************************/ + +#include "qttableview.h" +#if QT_VERSION >= 300 +#ifndef QT_NO_QTTABLEVIEW +#include +#include +#include +#include + +enum ScrollBarDirtyFlags { + verGeometry = 0x01, + verSteps = 0x02, + verRange = 0x04, + verValue = 0x08, + horGeometry = 0x10, + horSteps = 0x20, + horRange = 0x40, + horValue = 0x80, + verMask = 0x0F, + horMask = 0xF0 +}; + + +#define HSBEXT horizontalScrollBar()->sizeHint().height() +#define VSBEXT verticalScrollBar()->sizeHint().width() + + +class QCornerSquare : public QWidget // internal class +{ +public: + QCornerSquare( QWidget *, const char* = 0 ); + void paintEvent( QPaintEvent * ); +}; + +QCornerSquare::QCornerSquare( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ +} + +void QCornerSquare::paintEvent( QPaintEvent * ) +{ +} + + +// NOT REVISED +/*! + \class QtTableView qttableview.h + \brief The QtTableView class provides an abstract base for tables. + + \obsolete + + A table view consists of a number of abstract cells organized in rows + and columns, and a visible part called a view. The cells are identified + with a row index and a column index. The top-left cell is in row 0, + column 0. + + The behavior of the widget can be finely tuned using + setTableFlags(); a typical subclass will consist of little more than a + call to setTableFlags(), some table content manipulation and an + implementation of paintCell(). Subclasses that need cells with + variable width or height must reimplement cellHeight() and/or + cellWidth(). Use updateTableSize() to tell QtTableView when the + width or height has changed. + + When you read this documentation, it is important to understand the + distinctions among the four pixel coordinate systems involved. + + \list 1 + \i The \e cell coordinates. (0,0) is the top-left corner of a cell. + Cell coordinates are used by functions such as paintCell(). + + \i The \e table coordinates. (0,0) is the top-left corner of the cell at + row 0 and column 0. These coordinates are absolute; that is, they are + independent of what part of the table is visible at the moment. They are + used by functions such as setXOffset() or maxYOffset(). + + \i The \e widget coordinates. (0,0) is the top-left corner of the widget, + \e including the frame. They are used by functions such as repaint(). + + \i The \e view coordinates. (0,0) is the top-left corner of the view, \e + excluding the frame. This is the least-used coordinate system; it is used by + functions such as viewWidth(). \endlist + + It is rather unfortunate that we have to use four different + coordinate systems, but there was no alternative to provide a flexible and + powerful base class. + + Note: The row,column indices are always given in that order, + i.e., first the vertical (row), then the horizontal (column). This is + the opposite order of all pixel operations, which take first the + horizontal (x) and then the vertical (y). + + + + \warning the functions setNumRows(), setNumCols(), setCellHeight(), + setCellWidth(), setTableFlags() and clearTableFlags() may cause + virtual functions such as cellWidth() and cellHeight() to be called, + even if autoUpdate() is FALSE. This may cause errors if relevant + state variables are not initialized. + + \warning Experience has shown that use of this widget tends to cause + more bugs than expected and our analysis indicates that the widget's + very flexibility is the problem. If QScrollView or QListBox can + easily be made to do the job you need, we recommend subclassing + those widgets rather than QtTableView. In addition, QScrollView makes + it easy to have child widgets inside tables, which QtTableView + doesn't support at all. + + \sa QScrollView + \link guibooks.html#fowler GUI Design Handbook: Table\endlink +*/ + + +/*! + Constructs a table view. The \a parent, \a name and \f arguments + are passed to the QFrame constructor. + + The \link setTableFlags() table flags\endlink are all cleared (set to 0). + Set \c Tbl_autoVScrollBar or \c Tbl_autoHScrollBar to get automatic scroll + bars and \c Tbl_clipCellPainting to get safe clipping. + + The \link setCellHeight() cell height\endlink and \link setCellWidth() + cell width\endlink are set to 0. + + Frame line shapes (QFrame::HLink and QFrame::VLine) are disallowed; + see QFrame::setFrameStyle(). + + Note that the \a f argument is \e not \link setTableFlags() table + flags \endlink but rather \link QWidget::QWidget() widget + flags. \endlink + +*/ + +QtTableView::QtTableView( QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f ) +{ + nRows = nCols = 0; // zero rows/cols + xCellOffs = yCellOffs = 0; // zero offset + xCellDelta = yCellDelta = 0; // zero cell offset + xOffs = yOffs = 0; // zero total pixel offset + cellH = cellW = 0; // user defined cell size + tFlags = 0; + vScrollBar = hScrollBar = 0; // no scroll bars + cornerSquare = 0; + sbDirty = 0; + eraseInPaint = FALSE; + verSliding = FALSE; + verSnappingOff = FALSE; + horSliding = FALSE; + horSnappingOff = FALSE; + coveringCornerSquare = FALSE; + inSbUpdate = FALSE; +} + +/*! + Destroys the table view. +*/ + +QtTableView::~QtTableView() +{ + delete vScrollBar; + delete hScrollBar; + delete cornerSquare; +} + + +/*! + \internal + Reimplements QWidget::setBackgroundColor() for binary compatibility. + \sa setPalette() +*/ + +void QtTableView::setBackgroundColor( const QColor &c ) +{ + QWidget::setBackgroundColor( c ); +} + +/*!\reimp +*/ + +void QtTableView::setPalette( const QPalette &p ) +{ + QWidget::setPalette( p ); +} + +/*!\reimp +*/ + +void QtTableView::show() +{ + showOrHideScrollBars(); + QWidget::show(); +} + + +/*! + \overload void QtTableView::repaint( bool erase ) + Repaints the entire view. +*/ + +/*! + Repaints the table view directly by calling paintEvent() directly + unless updates are disabled. + + Erases the view area \a (x,y,w,h) if \a erase is TRUE. Parameters \a + (x,y) are in \e widget coordinates. + + If \a w is negative, it is replaced with width() - x. + If \a h is negative, it is replaced with height() - y. + + Doing a repaint() usually is faster than doing an update(), but + calling update() many times in a row will generate a single paint + event. + + At present, QtTableView is the only widget that reimplements \link + QWidget::repaint() repaint()\endlink. It does this because by + clearing and then repainting one cell at at time, it can make the + screen flicker less than it would otherwise. */ + +void QtTableView::repaint( int x, int y, int w, int h, bool erase ) +{ + if ( !isVisible() || testWState(WState_BlockUpdates) ) + return; + if ( w < 0 ) + w = width() - x; + if ( h < 0 ) + h = height() - y; + QRect r( x, y, w, h ); + if ( r.isEmpty() ) + return; // nothing to do + QPaintEvent e( r ); + if ( erase && backgroundMode() != NoBackground ) + eraseInPaint = TRUE; // erase when painting + paintEvent( &e ); + eraseInPaint = FALSE; +} + +/*! + \overload void QtTableView::repaint( const QRect &r, bool erase ) + Replaints rectangle \a r. If \a erase is TRUE draws the background + using the palette's background. +*/ + + +/*! + \fn int QtTableView::numRows() const + Returns the number of rows in the table. + \sa numCols(), setNumRows() +*/ + +/*! + Sets the number of rows of the table to \a rows (must be non-negative). + Does not change topCell(). + + The table repaints itself automatically if autoUpdate() is set. + + \sa numCols(), setNumCols(), numRows() +*/ + +void QtTableView::setNumRows( int rows ) +{ + if ( rows < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::setNumRows: (%s) Negative argument %d.", + name( "unnamed" ), rows ); +#endif + return; + } + if ( nRows == rows ) + return; + + if ( autoUpdate() && isVisible() ) { + int oldLastVisible = lastRowVisible(); + int oldTopCell = topCell(); + nRows = rows; + if ( autoUpdate() && isVisible() && + ( oldLastVisible != lastRowVisible() || oldTopCell != topCell() ) ) + repaint( oldTopCell != topCell() ); + } else { + // Be more careful - if destructing, bad things might happen. + nRows = rows; + } + updateScrollBars( verRange ); + updateFrameSize(); +} + +/*! + \fn int QtTableView::numCols() const + Returns the number of columns in the table. + \sa numRows(), setNumCols() +*/ + +/*! + Sets the number of columns of the table to \a cols (must be non-negative). + Does not change leftCell(). + + The table repaints itself automatically if autoUpdate() is set. + + \sa numCols(), numRows(), setNumRows() +*/ + +void QtTableView::setNumCols( int cols ) +{ + if ( cols < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::setNumCols: (%s) Negative argument %d.", + name( "unnamed" ), cols ); +#endif + return; + } + if ( nCols == cols ) + return; + int oldCols = nCols; + nCols = cols; + if ( autoUpdate() && isVisible() ) { + int maxCol = lastColVisible(); + if ( maxCol >= oldCols || maxCol >= nCols ) + repaint(); + } + updateScrollBars( horRange ); + updateFrameSize(); +} + + +/*! + \fn int QtTableView::topCell() const + Returns the index of the first row in the table that is visible in + the view. The index of the first row is 0. + \sa leftCell(), setTopCell() +*/ + +/*! + Scrolls the table so that \a row becomes the top row. + The index of the very first row is 0. + \sa setYOffset(), setTopLeftCell(), setLeftCell() +*/ + +void QtTableView::setTopCell( int row ) +{ + setTopLeftCell( row, -1 ); + return; +} + +/*! + \fn int QtTableView::leftCell() const + Returns the index of the first column in the table that is visible in + the view. The index of the very leftmost column is 0. + \sa topCell(), setLeftCell() +*/ + +/*! + Scrolls the table so that \a col becomes the leftmost + column. The index of the leftmost column is 0. + \sa setXOffset(), setTopLeftCell(), setTopCell() +*/ + +void QtTableView::setLeftCell( int col ) +{ + setTopLeftCell( -1, col ); + return; +} + +/*! + Scrolls the table so that the cell at row \a row and colum \a + col becomes the top-left cell in the view. The cell at the extreme + top left of the table is at position (0,0). + \sa setLeftCell(), setTopCell(), setOffset() +*/ + +void QtTableView::setTopLeftCell( int row, int col ) +{ + int newX = xOffs; + int newY = yOffs; + + if ( col >= 0 ) { + if ( cellW ) { + newX = col*cellW; + if ( newX > maxXOffset() ) + newX = maxXOffset(); + } else { + newX = 0; + while ( col ) + newX += cellWidth( --col ); // optimize using current! ### + } + } + if ( row >= 0 ) { + if ( cellH ) { + newY = row*cellH; + if ( newY > maxYOffset() ) + newY = maxYOffset(); + } else { + newY = 0; + while ( row ) + newY += cellHeight( --row ); // optimize using current! ### + } + } + setOffset( newX, newY ); +} + + +/*! + \fn int QtTableView::xOffset() const + + Returns the x coordinate in \e table coordinates of the pixel that is + currently on the left edge of the view. + + \sa setXOffset(), yOffset(), leftCell() */ + +/*! + Scrolls the table so that \a x becomes the leftmost pixel in the view. + The \a x parameter is in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapToHGrid + \endlink is tricky. + + \sa xOffset(), setYOffset(), setOffset(), setLeftCell() +*/ + +void QtTableView::setXOffset( int x ) +{ + setOffset( x, yOffset() ); +} + +/*! + \fn int QtTableView::yOffset() const + + Returns the y coordinate in \e table coordinates of the pixel that is + currently on the top edge of the view. + + \sa setYOffset(), xOffset(), topCell() +*/ + + +/*! + Scrolls the table so that \a y becomes the top pixel in the view. + The \a y parameter is in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapToVGrid + \endlink is tricky. + + \sa yOffset(), setXOffset(), setOffset(), setTopCell() +*/ + +void QtTableView::setYOffset( int y ) +{ + setOffset( xOffset(), y ); +} + +/*! + Scrolls the table so that \a (x,y) becomes the top-left pixel + in the view. Parameters \a (x,y) are in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapTo*Grid \endlink + is tricky. If \a updateScrBars is TRUE, the scroll bars are + updated. + + \sa xOffset(), yOffset(), setXOffset(), setYOffset(), setTopLeftCell() +*/ + +void QtTableView::setOffset( int x, int y, bool updateScrBars ) +{ + if ( (!testTableFlags(Tbl_snapToHGrid) || xCellDelta == 0) && + (!testTableFlags(Tbl_snapToVGrid) || yCellDelta == 0) && + (x == xOffs && y == yOffs) ) + return; + + if ( x < 0 ) + x = 0; + if ( y < 0 ) + y = 0; + + if ( cellW ) { + if ( x > maxXOffset() ) + x = maxXOffset(); + xCellOffs = x / cellW; + if ( !testTableFlags(Tbl_snapToHGrid) ) { + xCellDelta = (short)(x % cellW); + } else { + x = xCellOffs*cellW; + xCellDelta = 0; + } + } else { + int xn=0, xcd=0, col = 0; + while ( col < nCols-1 && x >= xn+(xcd=cellWidth(col)) ) { + xn += xcd; + col++; + } + xCellOffs = col; + if ( testTableFlags(Tbl_snapToHGrid) ) { + xCellDelta = 0; + x = xn; + } else { + xCellDelta = (short)(x-xn); + } + } + if ( cellH ) { + if ( y > maxYOffset() ) + y = maxYOffset(); + yCellOffs = y / cellH; + if ( !testTableFlags(Tbl_snapToVGrid) ) { + yCellDelta = (short)(y % cellH); + } else { + y = yCellOffs*cellH; + yCellDelta = 0; + } + } else { + int yn=0, yrd=0, row=0; + while ( row < nRows-1 && y >= yn+(yrd=cellHeight(row)) ) { + yn += yrd; + row++; + } + yCellOffs = row; + if ( testTableFlags(Tbl_snapToVGrid) ) { + yCellDelta = 0; + y = yn; + } else { + yCellDelta = (short)(y-yn); + } + } + int dx = (x - xOffs); + int dy = (y - yOffs); + xOffs = x; + yOffs = y; + if ( autoUpdate() && isVisible() ) + scroll( dx, dy ); + if ( updateScrBars ) + updateScrollBars( verValue | horValue ); +} + + +/*! + \overload int QtTableView::cellWidth() const + + Returns the column width in pixels. Returns 0 if the columns have + variable widths. + + \sa setCellWidth(), cellHeight() +*/ + +/*! + Returns the width of column \a col in pixels. + + This function is virtual and must be reimplemented by subclasses that + have variable cell widths. Note that if the total table width + changes, updateTableSize() must be called. + + \sa setCellWidth(), cellHeight(), totalWidth(), updateTableSize() +*/ + +int QtTableView::cellWidth( int ) +{ + return cellW; +} + + +/*! + Sets the width in pixels of the table cells to \a cellWidth. + + Setting it to 0 means that the column width is variable. When + set to 0 (this is the default) QtTableView calls the virtual function + cellWidth() to get the width. + + \sa cellWidth(), setCellHeight(), totalWidth(), numCols() +*/ + +void QtTableView::setCellWidth( int cellWidth ) +{ + if ( cellW == cellWidth ) + return; +#if defined(QT_CHECK_RANGE) + if ( cellWidth < 0 || cellWidth > SHRT_MAX ) { + qWarning( "QtTableView::setCellWidth: (%s) Argument out of range (%d)", + name( "unnamed" ), cellWidth ); + return; + } +#endif + cellW = (short)cellWidth; + + updateScrollBars( horSteps | horRange ); + if ( autoUpdate() && isVisible() ) + repaint(); + +} + +/*! + \overload int QtTableView::cellHeight() const + + Returns the row height, in pixels. Returns 0 if the rows have + variable heights. + + \sa setCellHeight(), cellWidth() +*/ + + +/*! + Returns the height of row \a row in pixels. + + This function is virtual and must be reimplemented by subclasses that + have variable cell heights. Note that if the total table height + changes, updateTableSize() must be called. + + \sa setCellHeight(), cellWidth(), totalHeight() +*/ + +int QtTableView::cellHeight( int ) +{ + return cellH; +} + +/*! + Sets the height in pixels of the table cells to \a cellHeight. + + Setting it to 0 means that the row height is variable. When set + to 0 (this is the default), QtTableView calls the virtual function + cellHeight() to get the height. + + \sa cellHeight(), setCellWidth(), totalHeight(), numRows() +*/ + +void QtTableView::setCellHeight( int cellHeight ) +{ + if ( cellH == cellHeight ) + return; +#if defined(QT_CHECK_RANGE) + if ( cellHeight < 0 || cellHeight > SHRT_MAX ) { + qWarning( "QtTableView::setCellHeight: (%s) Argument out of range (%d)", + name( "unnamed" ), cellHeight ); + return; + } +#endif + cellH = (short)cellHeight; + if ( autoUpdate() && isVisible() ) + repaint(); + updateScrollBars( verSteps | verRange ); +} + + +/*! + Returns the total width of the table in pixels. + + This function is virtual and should be reimplemented by subclasses that + have variable cell widths and a non-trivial cellWidth() function, or a + large number of columns in the table. + + The default implementation may be slow for very wide tables. + + \sa cellWidth(), totalHeight() */ + +int QtTableView::totalWidth() +{ + if ( cellW ) { + return cellW*nCols; + } else { + int tw = 0; + for( int i = 0 ; i < nCols ; i++ ) + tw += cellWidth( i ); + return tw; + } +} + +/*! + Returns the total height of the table in pixels. + + This function is virtual and should be reimplemented by subclasses that + have variable cell heights and a non-trivial cellHeight() function, or a + large number of rows in the table. + + The default implementation may be slow for very tall tables. + + \sa cellHeight(), totalWidth() +*/ + +int QtTableView::totalHeight() +{ + if ( cellH ) { + return cellH*nRows; + } else { + int th = 0; + for( int i = 0 ; i < nRows ; i++ ) + th += cellHeight( i ); + return th; + } +} + + +/*! + \fn uint QtTableView::tableFlags() const + + Returns the union of the table flags that are currently set. + + \sa setTableFlags(), clearTableFlags(), testTableFlags() +*/ + +/*! + \fn bool QtTableView::testTableFlags( uint f ) const + + Returns TRUE if any of the table flags in \a f are currently set, + otherwise FALSE. + + \sa setTableFlags(), clearTableFlags(), tableFlags() +*/ + +/*! + Sets the table flags to \a f. + + If a flag setting changes the appearance of the table, the table is + repainted if - and only if - autoUpdate() is TRUE. + + The table flags are mostly single bits, though there are some multibit + flags for convenience. Here is a complete list: + +
+
Tbl_vScrollBar
- The table has a vertical scroll bar. +
Tbl_hScrollBar
- The table has a horizontal scroll bar. +
Tbl_autoVScrollBar
- The table has a vertical scroll bar if + - and only if - the table is taller than the view. +
Tbl_autoHScrollBar
The table has a horizontal scroll bar if + - and only if - the table is wider than the view. +
Tbl_autoScrollBars
- The union of the previous two flags. +
Tbl_clipCellPainting
- The table uses QPainter::setClipRect() to + make sure that paintCell() will not draw outside the cell + boundaries. +
Tbl_cutCellsV
- The table will never show part of a + cell at the bottom of the table; if there is not space for all of + a cell, the space is left blank. +
Tbl_cutCellsH
- The table will never show part of a + cell at the right side of the table; if there is not space for all of + a cell, the space is left blank. +
Tbl_cutCells
- The union of the previous two flags. +
Tbl_scrollLastHCell
- When the user scrolls horizontally, + let him/her scroll the last cell left until it is at the left + edge of the view. If this flag is not set, the user can only scroll + to the point where the last cell is completely visible. +
Tbl_scrollLastVCell
- When the user scrolls vertically, let + him/her scroll the last cell up until it is at the top edge of + the view. If this flag is not set, the user can only scroll to the + point where the last cell is completely visible. +
Tbl_scrollLastCell
- The union of the previous two flags. +
Tbl_smoothHScrolling
- The table scrolls as smoothly as + possible when the user scrolls horizontally. When this flag is not + set, scrolling is done one cell at a time. +
Tbl_smoothVScrolling
- The table scrolls as smoothly as + possible when scrolling vertically. When this flag is not set, + scrolling is done one cell at a time. +
Tbl_smoothScrolling
- The union of the previous two flags. +
Tbl_snapToHGrid
- Except when the user is actually scrolling, + the leftmost column shown snaps to the leftmost edge of the view. +
Tbl_snapToVGrid
- Except when the user is actually + scrolling, the top row snaps to the top edge of the view. +
Tbl_snapToGrid
- The union of the previous two flags. +
+ + You can specify more than one flag at a time using bitwise OR. + + Example: + \code + setTableFlags( Tbl_smoothScrolling | Tbl_autoScrollBars ); + \endcode + + \warning The cutCells options (\c Tbl_cutCells, \c Tbl_cutCellsH and + Tbl_cutCellsV) may cause painting problems when scrollbars are + enabled. Do not combine cutCells and scrollbars. + + + \sa clearTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::setTableFlags( uint f ) +{ + f = (f ^ tFlags) & f; // clear flags already set + tFlags |= f; + + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if ( f & Tbl_vScrollBar ) { + setVerScrollBar( TRUE ); + } + if ( f & Tbl_hScrollBar ) { + setHorScrollBar( TRUE ); + } + if ( f & Tbl_autoVScrollBar ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_autoHScrollBar ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastHCell ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastVCell ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_snapToHGrid ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_snapToVGrid ) { + updateScrollBars( verRange ); + } + if ( f & Tbl_snapToGrid ) { // Note: checks for 2 flags + if ( (f & Tbl_snapToHGrid) != 0 && xCellDelta != 0 || //have to scroll? + (f & Tbl_snapToVGrid) != 0 && yCellDelta != 0 ) { + snapToGrid( (f & Tbl_snapToHGrid) != 0, // do snapping + (f & Tbl_snapToVGrid) != 0 ); + repaintMask |= Tbl_snapToGrid; // repaint table + } + } + + if ( updateOn ) { + setAutoUpdate( TRUE ); + updateScrollBars(); + if ( isVisible() && (f & repaintMask) ) + repaint(); + } + +} + +/*! + Clears the \link setTableFlags() table flags\endlink that are set + in \a f. + + Example (clears a single flag): + \code + clearTableFlags( Tbl_snapToGrid ); + \endcode + + The default argument clears all flags. + + \sa setTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::clearTableFlags( uint f ) +{ + f = (f ^ ~tFlags) & f; // clear flags that are already 0 + tFlags &= ~f; + + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if ( f & Tbl_vScrollBar ) { + setVerScrollBar( FALSE ); + } + if ( f & Tbl_hScrollBar ) { + setHorScrollBar( FALSE ); + } + if ( f & Tbl_scrollLastHCell ) { + int maxX = maxXOffset(); + if ( xOffs > maxX ) { + setOffset( maxX, yOffs ); + repaintMask |= Tbl_scrollLastHCell; + } + updateScrollBars( horRange ); + } + if ( f & Tbl_scrollLastVCell ) { + int maxY = maxYOffset(); + if ( yOffs > maxY ) { + setOffset( xOffs, maxY ); + repaintMask |= Tbl_scrollLastVCell; + } + updateScrollBars( verRange ); + } + if ( f & Tbl_smoothScrolling ) { // Note: checks for 2 flags + if ((f & Tbl_smoothHScrolling) != 0 && xCellDelta != 0 ||//must scroll? + (f & Tbl_smoothVScrolling) != 0 && yCellDelta != 0 ) { + snapToGrid( (f & Tbl_smoothHScrolling) != 0, // do snapping + (f & Tbl_smoothVScrolling) != 0 ); + repaintMask |= Tbl_smoothScrolling; // repaint table + } + } + if ( f & Tbl_snapToHGrid ) { + updateScrollBars( horRange ); + } + if ( f & Tbl_snapToVGrid ) { + updateScrollBars( verRange ); + } + if ( updateOn ) { + setAutoUpdate( TRUE ); + updateScrollBars(); // returns immediately if nothing to do + if ( isVisible() && (f & repaintMask) ) + repaint(); + } + +} + + +/*! + \fn bool QtTableView::autoUpdate() const + + Returns TRUE if the view updates itself automatically whenever it + is changed in some way. + + \sa setAutoUpdate() +*/ + +/*! + Sets the auto-update option of the table view to \a enable. + + If \a enable is TRUE (this is the default), the view updates itself + automatically whenever it has changed in some way (for example, when a + \link setTableFlags() flag\endlink is changed). + + If \a enable is FALSE, the view does NOT repaint itself or update + its internal state variables when it is changed. This can be + useful to avoid flicker during large changes and is singularly + useless otherwise. Disable auto-update, do the changes, re-enable + auto-update and call repaint(). + + \warning Do not leave the view in this state for a long time + (i.e., between events). If, for example, the user interacts with the + view when auto-update is off, strange things can happen. + + Setting auto-update to TRUE does not repaint the view; you must call + repaint() to do this. + + \sa autoUpdate(), repaint() +*/ + +void QtTableView::setAutoUpdate( bool enable ) +{ + if ( isUpdatesEnabled() == enable ) + return; + setUpdatesEnabled( enable ); + if ( enable ) { + showOrHideScrollBars(); + updateScrollBars(); + } +} + + +/*! + Repaints the cell at row \a row, column \a col if it is inside the view. + + If \a erase is TRUE, the relevant part of the view is cleared to the + background color/pixmap before the contents are repainted. + + \sa isVisible() +*/ + +void QtTableView::updateCell( int row, int col, bool erase ) +{ + int xPos, yPos; + if ( !colXPos( col, &xPos ) ) + return; + if ( !rowYPos( row, &yPos ) ) + return; + QRect uR = QRect( xPos, yPos, + cellW ? cellW : cellWidth(col), + cellH ? cellH : cellHeight(row) ); + repaint( uR.intersect(viewRect()), erase ); +} + + +/*! + \fn QRect QtTableView::cellUpdateRect() const + + This function should be called only from the paintCell() function in + subclasses. It returns the portion of a cell that actually needs to be + updated in \e cell coordinates. This is useful only for non-trivial + paintCell(). + +*/ + +/*! + Returns the rectangle that is the actual table, excluding any + frame, in \e widget coordinates. +*/ + +QRect QtTableView::viewRect() const +{ + return QRect( frameWidth(), frameWidth(), viewWidth(), viewHeight() ); +} + + +/*! + Returns the index of the last (bottom) row in the view. + The index of the first row is 0. + + If no rows are visible it returns -1. This can happen if the + view is too small for the first row and Tbl_cutCellsV is set. + + \sa lastColVisible() +*/ + +int QtTableView::lastRowVisible() const +{ + int cellMaxY; + int row = findRawRow( maxViewY(), &cellMaxY ); + if ( row == -1 || row >= nRows ) { // maxViewY() past end? + row = nRows - 1; // yes: return last row + } else { + if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() ) { + if ( row == yCellOffs ) // cut by right margin? + return -1; // yes, nothing in the view + else + row = row - 1; // cut by margin, one back + } + } + return row; +} + +/*! + Returns the index of the last (right) column in the view. + The index of the first column is 0. + + If no columns are visible it returns -1. This can happen if the + view is too narrow for the first column and Tbl_cutCellsH is set. + + \sa lastRowVisible() +*/ + +int QtTableView::lastColVisible() const +{ + int cellMaxX; + int col = findRawCol( maxViewX(), &cellMaxX ); + if ( col == -1 || col >= nCols ) { // maxViewX() past end? + col = nCols - 1; // yes: return last col + } else { + if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() ) { + if ( col == xCellOffs ) // cut by bottom margin? + return -1; // yes, nothing in the view + else + col = col - 1; // cell by margin, one back + } + } + return col; +} + +/*! + Returns TRUE if \a row is at least partially visible. + \sa colIsVisible() +*/ + +bool QtTableView::rowIsVisible( int row ) const +{ + return rowYPos( row, 0 ); +} + +/*! + Returns TRUE if \a col is at least partially visible. + \sa rowIsVisible() +*/ + +bool QtTableView::colIsVisible( int col ) const +{ + return colXPos( col, 0 ); +} + + +/*! + \internal + Called when both scroll bars are active at the same time. Covers the + bottom left corner between the two scroll bars with an empty widget. +*/ + +void QtTableView::coverCornerSquare( bool enable ) +{ + coveringCornerSquare = enable; + if ( !cornerSquare && enable ) { + cornerSquare = new QCornerSquare( this ); + Q_CHECK_PTR( cornerSquare ); + cornerSquare->setGeometry( maxViewX() + frameWidth() + 1, + maxViewY() + frameWidth() + 1, + VSBEXT, + HSBEXT); + } + if ( autoUpdate() && cornerSquare ) { + if ( enable ) + cornerSquare->show(); + else + cornerSquare->hide(); + } +} + + +/*! + \internal + Scroll the view to a position such that: + + If \a horizontal is TRUE, the leftmost column shown fits snugly + with the left edge of the view. + + If \a vertical is TRUE, the top row shown fits snugly with the top + of the view. + + You can achieve the same effect automatically by setting any of the + \link setTableFlags() Tbl_snapTo*Grid \endlink table flags. +*/ + +void QtTableView::snapToGrid( bool horizontal, bool vertical ) +{ + int newXCell = -1; + int newYCell = -1; + if ( horizontal && xCellDelta != 0 ) { + int w = cellW ? cellW : cellWidth( xCellOffs ); + if ( xCellDelta >= w/2 ) + newXCell = xCellOffs + 1; + else + newXCell = xCellOffs; + } + if ( vertical && yCellDelta != 0 ) { + int h = cellH ? cellH : cellHeight( yCellOffs ); + if ( yCellDelta >= h/2 ) + newYCell = yCellOffs + 1; + else + newYCell = yCellOffs; + } + setTopLeftCell( newYCell, newXCell ); //row,column +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table horizontally to offset \a val without updating the + scroll bar. +*/ + +void QtTableView::horSbValue( int val ) +{ + if ( horSliding ) { + horSliding = FALSE; + if ( horSnappingOff ) { + horSnappingOff = FALSE; + tFlags |= Tbl_snapToHGrid; + } + } + setOffset( val, yOffs, FALSE ); +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly horizontally even if \c Tbl_snapToHGrid is set. +*/ + +void QtTableView::horSbSliding( int val ) +{ + if ( testTableFlags(Tbl_snapToHGrid) && + testTableFlags(Tbl_smoothHScrolling) ) { + tFlags &= ~Tbl_snapToHGrid; // turn off snapping while sliding + setOffset( val, yOffs, FALSE ); + tFlags |= Tbl_snapToHGrid; // turn on snapping again + } else { + setOffset( val, yOffs, FALSE ); + } +} + +/*! + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::horSbSlidingDone( ) +{ + if ( testTableFlags(Tbl_snapToHGrid) && + testTableFlags(Tbl_smoothHScrolling) ) + snapToGrid( TRUE, FALSE ); +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table vertically to offset \a val without updating the + scroll bar. +*/ + +void QtTableView::verSbValue( int val ) +{ + if ( verSliding ) { + verSliding = FALSE; + if ( verSnappingOff ) { + verSnappingOff = FALSE; + tFlags |= Tbl_snapToVGrid; + } + } + setOffset( xOffs, val, FALSE ); +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly vertically even if \c Tbl_snapToVGrid is set. +*/ + +void QtTableView::verSbSliding( int val ) +{ + if ( testTableFlags(Tbl_snapToVGrid) && + testTableFlags(Tbl_smoothVScrolling) ) { + tFlags &= ~Tbl_snapToVGrid; // turn off snapping while sliding + setOffset( xOffs, val, FALSE ); + tFlags |= Tbl_snapToVGrid; // turn on snapping again + } else { + setOffset( xOffs, val, FALSE ); + } +} + +/*! + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::verSbSlidingDone( ) +{ + if ( testTableFlags(Tbl_snapToVGrid) && + testTableFlags(Tbl_smoothVScrolling) ) + snapToGrid( FALSE, TRUE ); +} + + +/*! + This virtual function is called before painting of table cells + is started. It can be reimplemented by subclasses that want to + to set up the painter in a special way and that do not want to + do so for each cell. +*/ + +void QtTableView::setupPainter( QPainter * ) +{ +} + +/*! + \fn void QtTableView::paintCell( QPainter *p, int row, int col ) + + This pure virtual function is called to paint the single cell at \a + (row,col) using \a p, which is open when paintCell() is called and + must remain open. + + The coordinate system is \link QPainter::translate() translated \endlink + so that the origin is at the top-left corner of the cell to be + painted, i.e. \e cell coordinates. Do not scale or shear the coordinate + system (or if you do, restore the transformation matrix before you + return). + + The painter is not clipped by default and for maximum efficiency. For safety, + call setTableFlags(Tbl_clipCellPainting) to enable clipping. + + \sa paintEvent(), setTableFlags() */ + + +/*! + Handles paint events, \a e, for the table view. + + Calls paintCell() for the cells that needs to be repainted. +*/ + +void QtTableView::paintEvent( QPaintEvent *e ) +{ + QRect updateR = e->rect(); // update rectangle + if ( sbDirty ) { + bool e = eraseInPaint; + updateScrollBars(); + eraseInPaint = e; + } + + QPainter paint( this ); + + if ( !contentsRect().contains( updateR, TRUE ) ) {// update frame ? + drawFrame( &paint ); + if ( updateR.left() < frameWidth() ) //### + updateR.setLeft( frameWidth() ); + if ( updateR.top() < frameWidth() ) + updateR.setTop( frameWidth() ); + } + + int maxWX = maxViewX(); + int maxWY = maxViewY(); + if ( updateR.right() > maxWX ) + updateR.setRight( maxWX ); + if ( updateR.bottom() > maxWY ) + updateR.setBottom( maxWY ); + + setupPainter( &paint ); // prepare for painting table + + int firstRow = findRow( updateR.y() ); + int firstCol = findCol( updateR.x() ); + int xStart, yStart; + if ( !colXPos( firstCol, &xStart ) || !rowYPos( firstRow, &yStart ) ) { + paint.eraseRect( updateR ); // erase area outside cells but in view + return; + } + int maxX = updateR.right(); + int maxY = updateR.bottom(); + int row = firstRow; + int col; + int yPos = yStart; + int xPos = maxX+1; // in case the while() is empty + int nextX; + int nextY; + QRect winR = viewRect(); + QRect cellR; + QRect cellUR; +#ifndef QT_NO_TRANSFORMATIONS + QWMatrix matrix; +#endif + + while ( yPos <= maxY && row < nRows ) { + nextY = yPos + (cellH ? cellH : cellHeight( row )); + if ( testTableFlags( Tbl_cutCellsV ) && nextY > ( maxWY + 1 ) ) + break; + col = firstCol; + xPos = xStart; + while ( xPos <= maxX && col < nCols ) { + nextX = xPos + (cellW ? cellW : cellWidth( col )); + if ( testTableFlags( Tbl_cutCellsH ) && nextX > ( maxWX + 1 ) ) + break; + + cellR.setRect( xPos, yPos, cellW ? cellW : cellWidth(col), + cellH ? cellH : cellHeight(row) ); + cellUR = cellR.intersect( updateR ); + if ( cellUR.isValid() ) { + cellUpdateR = cellUR; + cellUpdateR.moveBy( -xPos, -yPos ); // cell coordinates + if ( eraseInPaint ) + paint.eraseRect( cellUR ); + +#ifndef QT_NO_TRANSFORMATIONS + matrix.translate( xPos, yPos ); + paint.setWorldMatrix( matrix ); + if ( testTableFlags(Tbl_clipCellPainting) || + frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt + paint.setClipRect( cellUR ); + paintCell( &paint, row, col ); + paint.setClipping( FALSE ); + } else { + paintCell( &paint, row, col ); + } + matrix.reset(); + paint.setWorldMatrix( matrix ); +#else + paint.translate( xPos, yPos ); + if ( testTableFlags(Tbl_clipCellPainting) || + frameWidth() > 0 && !winR.contains( cellR ) ) { //##arnt + paint.setClipRect( cellUR ); + paintCell( &paint, row, col ); + paint.setClipping( FALSE ); + } else { + paintCell( &paint, row, col ); + } + paint.translate( -xPos, -yPos ); +#endif + } + col++; + xPos = nextX; + } + row++; + yPos = nextY; + } + + // while painting we have to erase any areas in the view that + // are not covered by cells but are covered by the paint event + // rectangle these must be erased. We know that xPos is the last + // x pixel updated + 1 and that yPos is the last y pixel updated + 1. + + // Note that this needs to be done regardless whether we do + // eraseInPaint or not. Reason: a subclass may implement + // flicker-freeness and encourage the use of repaint(FALSE). + // The subclass, however, cannot draw all pixels, just those + // inside the cells. So QtTableView is reponsible for all pixels + // outside the cells. + + QRect viewR = viewRect(); + const QColorGroup g = colorGroup(); + + if ( xPos <= maxX ) { + QRect r = viewR; + r.setLeft( xPos ); + r.setBottom( yPossetCursor( arrowCursor ); +#endif + sb->resize( sb->sizeHint() ); // height is irrelevant + Q_CHECK_PTR(sb); + sb->setTracking( FALSE ); + sb->setFocusPolicy( NoFocus ); + connect( sb, SIGNAL(valueChanged(int)), + SLOT(verSbValue(int))); + connect( sb, SIGNAL(sliderMoved(int)), + SLOT(verSbSliding(int))); + connect( sb, SIGNAL(sliderReleased()), + SLOT(verSbSlidingDone())); + sb->hide(); + that->vScrollBar = sb; + return sb; + } + return vScrollBar; +} + +/*! + Returns a pointer to the horizontal scroll bar mainly so you can + connect() to its signals. Note that the scroll bar works in pixel + values; use findCol() to translate to cell numbers. +*/ + +QScrollBar *QtTableView::horizontalScrollBar() const +{ + QtTableView *that = (QtTableView*)this; // semantic const + if ( !hScrollBar ) { + QScrollBar *sb = new QScrollBar( QScrollBar::Horizontal, that ); +#ifndef QT_NO_CURSOR + sb->setCursor( arrowCursor ); +#endif + sb->resize( sb->sizeHint() ); // width is irrelevant + sb->setFocusPolicy( NoFocus ); + Q_CHECK_PTR(sb); + sb->setTracking( FALSE ); + connect( sb, SIGNAL(valueChanged(int)), + SLOT(horSbValue(int))); + connect( sb, SIGNAL(sliderMoved(int)), + SLOT(horSbSliding(int))); + connect( sb, SIGNAL(sliderReleased()), + SLOT(horSbSlidingDone())); + sb->hide(); + that->hScrollBar = sb; + return sb; + } + return hScrollBar; +} + +/*! + Enables or disables the horizontal scroll bar, as required by + setAutoUpdate() and the \link setTableFlags() table flags\endlink. +*/ + +void QtTableView::setHorScrollBar( bool on, bool update ) +{ + if ( on ) { + tFlags |= Tbl_hScrollBar; + horizontalScrollBar(); // created + if ( update ) + updateScrollBars( horMask | verMask ); + else + sbDirty = sbDirty | (horMask | verMask); + if ( testTableFlags( Tbl_vScrollBar ) ) + coverCornerSquare( TRUE ); + if ( autoUpdate() ) + sbDirty = sbDirty | horMask; + } else { + tFlags &= ~Tbl_hScrollBar; + if ( !hScrollBar ) + return; + coverCornerSquare( FALSE ); + bool hideScrollBar = autoUpdate() && hScrollBar->isVisible(); + if ( hideScrollBar ) + hScrollBar->hide(); + if ( update ) + updateScrollBars( verMask ); + else + sbDirty = sbDirty | verMask; + if ( hideScrollBar && isVisible() ) + repaint( hScrollBar->x(), hScrollBar->y(), + width() - hScrollBar->x(), hScrollBar->height() ); + } + if ( update ) + updateFrameSize(); +} + + +/*! + Enables or disables the vertical scroll bar, as required by + setAutoUpdate() and the \link setTableFlags() table flags\endlink. +*/ + +void QtTableView::setVerScrollBar( bool on, bool update ) +{ + if ( on ) { + tFlags |= Tbl_vScrollBar; + verticalScrollBar(); // created + if ( update ) + updateScrollBars( verMask | horMask ); + else + sbDirty = sbDirty | (horMask | verMask); + if ( testTableFlags( Tbl_hScrollBar ) ) + coverCornerSquare( TRUE ); + if ( autoUpdate() ) + sbDirty = sbDirty | verMask; + } else { + tFlags &= ~Tbl_vScrollBar; + if ( !vScrollBar ) + return; + coverCornerSquare( FALSE ); + bool hideScrollBar = autoUpdate() && vScrollBar->isVisible(); + if ( hideScrollBar ) + vScrollBar->hide(); + if ( update ) + updateScrollBars( horMask ); + else + sbDirty = sbDirty | horMask; + if ( hideScrollBar && isVisible() ) + repaint( vScrollBar->x(), vScrollBar->y(), + vScrollBar->width(), height() - vScrollBar->y() ); + } + if ( update ) + updateFrameSize(); +} + + + + +int QtTableView::findRawRow( int yPos, int *cellMaxY, int *cellMinY, + bool goOutsideView ) const +{ + int r = -1; + if ( nRows == 0 ) + return r; + if ( goOutsideView || yPos >= minViewY() && yPos <= maxViewY() ) { + if ( yPos < minViewY() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::findRawRow: (%s) internal error: " + "yPos < minViewY() && goOutsideView " + "not supported. (%d,%d)", + name( "unnamed" ), yPos, yOffs ); +#endif + return -1; + } + if ( cellH ) { // uniform cell height + r = (yPos - minViewY() + yCellDelta)/cellH; // cell offs from top + if ( cellMaxY ) + *cellMaxY = (r + 1)*cellH + minViewY() - yCellDelta - 1; + if ( cellMinY ) + *cellMinY = r*cellH + minViewY() - yCellDelta; + r += yCellOffs; // absolute cell index + } else { // variable cell height + QtTableView *tw = (QtTableView *)this; + r = yCellOffs; + int h = minViewY() - yCellDelta; //##arnt3 + int oldH = h; + Q_ASSERT( r < nRows ); + while ( r < nRows ) { + oldH = h; + h += tw->cellHeight( r ); // Start of next cell + if ( yPos < h ) + break; + r++; + } + if ( cellMaxY ) + *cellMaxY = h - 1; + if ( cellMinY ) + *cellMinY = oldH; + } + } + return r; + +} + + +int QtTableView::findRawCol( int xPos, int *cellMaxX, int *cellMinX , + bool goOutsideView ) const +{ + int c = -1; + if ( nCols == 0 ) + return c; + if ( goOutsideView || xPos >= minViewX() && xPos <= maxViewX() ) { + if ( xPos < minViewX() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QtTableView::findRawCol: (%s) internal error: " + "xPos < minViewX() && goOutsideView " + "not supported. (%d,%d)", + name( "unnamed" ), xPos, xOffs ); +#endif + return -1; + } + if ( cellW ) { // uniform cell width + c = (xPos - minViewX() + xCellDelta)/cellW; //cell offs from left + if ( cellMaxX ) + *cellMaxX = (c + 1)*cellW + minViewX() - xCellDelta - 1; + if ( cellMinX ) + *cellMinX = c*cellW + minViewX() - xCellDelta; + c += xCellOffs; // absolute cell index + } else { // variable cell width + QtTableView *tw = (QtTableView *)this; + c = xCellOffs; + int w = minViewX() - xCellDelta; //##arnt3 + int oldW = w; + Q_ASSERT( c < nCols ); + while ( c < nCols ) { + oldW = w; + w += tw->cellWidth( c ); // Start of next cell + if ( xPos < w ) + break; + c++; + } + if ( cellMaxX ) + *cellMaxX = w - 1; + if ( cellMinX ) + *cellMinX = oldW; + } + } + return c; +} + + +/*! + Returns the index of the row at position \a yPos, where \a yPos is in + \e widget coordinates. Returns -1 if \a yPos is outside the valid + range. + + \sa findCol(), rowYPos() +*/ + +int QtTableView::findRow( int yPos ) const +{ + int cellMaxY; + int row = findRawRow( yPos, &cellMaxY ); + if ( testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY() ) + row = - 1; // cell cut by bottom margin + if ( row >= nRows ) + row = -1; + return row; +} + + +/*! + Returns the index of the column at position \a xPos, where \a xPos is + in \e widget coordinates. Returns -1 if \a xPos is outside the valid + range. + + \sa findRow(), colXPos() +*/ + +int QtTableView::findCol( int xPos ) const +{ + int cellMaxX; + int col = findRawCol( xPos, &cellMaxX ); + if ( testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX() ) + col = - 1; // cell cut by right margin + if ( col >= nCols ) + col = -1; + return col; +} + + +/*! + Computes the position in the widget of row \a row. + + Returns TRUE and stores the result in \a *yPos (in \e widget + coordinates) if the row is visible. Returns FALSE and does not modify + \a *yPos if \a row is invisible or invalid. + + \sa colXPos(), findRow() +*/ + +bool QtTableView::rowYPos( int row, int *yPos ) const +{ + int y; + if ( row >= yCellOffs ) { + if ( cellH ) { + int lastVisible = lastRowVisible(); + if ( row > lastVisible || lastVisible == -1 ) + return FALSE; + y = (row - yCellOffs)*cellH + minViewY() - yCellDelta; + } else { + //##arnt3 + y = minViewY() - yCellDelta; // y of leftmost cell in view + int r = yCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxY = maxViewY(); + while ( r < row && y <= maxY ) + y += tw->cellHeight( r++ ); + if ( y > maxY ) + return FALSE; + + } + } else { + return FALSE; + } + if ( yPos ) + *yPos = y; + return TRUE; +} + + +/*! + Computes the position in the widget of column \a col. + + Returns TRUE and stores the result in \a *xPos (in \e widget + coordinates) if the column is visible. Returns FALSE and does not + modify \a *xPos if \a col is invisible or invalid. + + \sa rowYPos(), findCol() +*/ + +bool QtTableView::colXPos( int col, int *xPos ) const +{ + int x; + if ( col >= xCellOffs ) { + if ( cellW ) { + int lastVisible = lastColVisible(); + if ( col > lastVisible || lastVisible == -1 ) + return FALSE; + x = (col - xCellOffs)*cellW + minViewX() - xCellDelta; + } else { + //##arnt3 + x = minViewX() - xCellDelta; // x of uppermost cell in view + int c = xCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxX = maxViewX(); + while ( c < col && x <= maxX ) + x += tw->cellWidth( c++ ); + if ( x > maxX ) + return FALSE; + } + } else { + return FALSE; + } + if ( xPos ) + *xPos = x; + return TRUE; +} + + +/*! + Moves the visible area of the table right by \a xPixels and + down by \a yPixels pixels. Both may be negative. + + \warning You might find that QScrollView offers a higher-level of + functionality than using QtTableView and this function. + + This function is \e not the same as QWidget::scroll(); in particular, + the signs of \a xPixels and \a yPixels have the reverse semantics. + + \sa setXOffset(), setYOffset(), setOffset(), setTopCell(), + setLeftCell() +*/ + +void QtTableView::scroll( int xPixels, int yPixels ) +{ + QWidget::scroll( -xPixels, -yPixels, contentsRect() ); +} + + +/*! + Returns the leftmost pixel of the table view in \e view + coordinates. This excludes the frame and any header. + + \sa maxViewY(), viewWidth(), contentsRect() +*/ + +int QtTableView::minViewX() const +{ + return frameWidth(); +} + + +/*! + Returns the top pixel of the table view in \e view + coordinates. This excludes the frame and any header. + + \sa maxViewX(), viewHeight(), contentsRect() +*/ + +int QtTableView::minViewY() const +{ + return frameWidth(); +} + + +/*! + Returns the rightmost pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels to the right of the visible table data. + + \sa maxViewY(), viewWidth(), contentsRect() +*/ + +int QtTableView::maxViewX() const +{ + return width() - 1 - frameWidth() + - (tFlags & Tbl_vScrollBar ? VSBEXT + : 0); +} + + +/*! + Returns the bottom pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels below the visible table data. + + \sa maxViewX(), viewHeight(), contentsRect() +*/ + +int QtTableView::maxViewY() const +{ + return height() - 1 - frameWidth() + - (tFlags & Tbl_hScrollBar ? HSBEXT + : 0); +} + + +/*! + Returns the width of the table view, as such, in \e view + coordinates. This does not include any header, scroll bar or frame, + but it does include background pixels to the right of the table data. + + \sa minViewX() maxViewX(), viewHeight(), contentsRect() viewRect() +*/ + +int QtTableView::viewWidth() const +{ + return maxViewX() - minViewX() + 1; +} + + +/*! + Returns the height of the table view, as such, in \e view + coordinates. This does not include any header, scroll bar or frame, + but it does include background pixels below the table data. + + \sa minViewY() maxViewY() viewWidth() contentsRect() viewRect() +*/ + +int QtTableView::viewHeight() const +{ + return maxViewY() - minViewY() + 1; +} + + +void QtTableView::doAutoScrollBars() +{ + int viewW = width() - frameWidth() - minViewX(); + int viewH = height() - frameWidth() - minViewY(); + bool vScrollOn = testTableFlags(Tbl_vScrollBar); + bool hScrollOn = testTableFlags(Tbl_hScrollBar); + int w = 0; + int h = 0; + int i; + + if ( testTableFlags(Tbl_autoHScrollBar) ) { + if ( cellW ) { + w = cellW*nCols; + } else { + i = 0; + while ( i < nCols && w <= viewW ) + w += cellWidth( i++ ); + } + if ( w > viewW ) + hScrollOn = TRUE; + else + hScrollOn = FALSE; + } + + if ( testTableFlags(Tbl_autoVScrollBar) ) { + if ( cellH ) { + h = cellH*nRows; + } else { + i = 0; + while ( i < nRows && h <= viewH ) + h += cellHeight( i++ ); + } + + if ( h > viewH ) + vScrollOn = TRUE; + else + vScrollOn = FALSE; + } + + if ( testTableFlags(Tbl_autoHScrollBar) && vScrollOn && !hScrollOn ) + if ( w > viewW - VSBEXT ) + hScrollOn = TRUE; + + if ( testTableFlags(Tbl_autoVScrollBar) && hScrollOn && !vScrollOn ) + if ( h > viewH - HSBEXT ) + vScrollOn = TRUE; + + setHorScrollBar( hScrollOn, FALSE ); + setVerScrollBar( vScrollOn, FALSE ); + updateFrameSize(); +} + + +/*! + \fn void QtTableView::updateScrollBars() + + Updates the scroll bars' contents and presence to match the table's + state. Generally, you should not need to call this. + + \sa setTableFlags() +*/ + +/*! + Updates the scroll bars' contents and presence to match the table's + state \c or \a f. + + \sa setTableFlags() +*/ + +void QtTableView::updateScrollBars( uint f ) +{ + sbDirty = sbDirty | f; + if ( inSbUpdate ) + return; + inSbUpdate = TRUE; + + if ( testTableFlags(Tbl_autoHScrollBar) && (sbDirty & horRange) || + testTableFlags(Tbl_autoVScrollBar) && (sbDirty & verRange) ) + // if range change and auto + doAutoScrollBars(); // turn scroll bars on/off if needed + + if ( !autoUpdate() ) { + inSbUpdate = FALSE; + return; + } + if ( yOffset() > 0 && testTableFlags( Tbl_autoVScrollBar ) && + !testTableFlags( Tbl_vScrollBar ) ) { + setYOffset( 0 ); + } + if ( xOffset() > 0 && testTableFlags( Tbl_autoHScrollBar ) && + !testTableFlags( Tbl_hScrollBar ) ) { + setXOffset( 0 ); + } + if ( !isVisible() ) { + inSbUpdate = FALSE; + return; + } + + if ( testTableFlags(Tbl_hScrollBar) && (sbDirty & horMask) != 0 ) { + if ( sbDirty & horGeometry ) + hScrollBar->setGeometry( 0,height() - HSBEXT, + viewWidth() + frameWidth()*2, + HSBEXT); + + if ( sbDirty & horSteps ) { + if ( cellW ) + hScrollBar->setSteps( QMIN(cellW,viewWidth()/2), viewWidth() ); + else + hScrollBar->setSteps( 16, viewWidth() ); + } + + if ( sbDirty & horRange ) + hScrollBar->setRange( 0, maxXOffset() ); + + if ( sbDirty & horValue ) + hScrollBar->setValue( xOffs ); + + // show scrollbar only when it has a sane geometry + if ( !hScrollBar->isVisible() ) + hScrollBar->show(); + } + + if ( testTableFlags(Tbl_vScrollBar) && (sbDirty & verMask) != 0 ) { + if ( sbDirty & verGeometry ) + vScrollBar->setGeometry( width() - VSBEXT, 0, + VSBEXT, + viewHeight() + frameWidth()*2 ); + + if ( sbDirty & verSteps ) { + if ( cellH ) + vScrollBar->setSteps( QMIN(cellH,viewHeight()/2), viewHeight() ); + else + vScrollBar->setSteps( 16, viewHeight() ); // fttb! ### + } + + if ( sbDirty & verRange ) + vScrollBar->setRange( 0, maxYOffset() ); + + if ( sbDirty & verValue ) + vScrollBar->setValue( yOffs ); + + // show scrollbar only when it has a sane geometry + if ( !vScrollBar->isVisible() ) + vScrollBar->show(); + } + if ( coveringCornerSquare && + ( (sbDirty & verGeometry ) || (sbDirty & horGeometry)) ) + cornerSquare->move( maxViewX() + frameWidth() + 1, + maxViewY() + frameWidth() + 1 ); + + sbDirty = 0; + inSbUpdate = FALSE; +} + + +void QtTableView::updateFrameSize() +{ + int rw = width() - ( testTableFlags(Tbl_vScrollBar) ? + VSBEXT : 0 ); + int rh = height() - ( testTableFlags(Tbl_hScrollBar) ? + HSBEXT : 0 ); + if ( rw < 0 ) + rw = 0; + if ( rh < 0 ) + rh = 0; + + if ( autoUpdate() ) { + int fh = frameRect().height(); + int fw = frameRect().width(); + setFrameRect( QRect(0,0,rw,rh) ); + + if ( rw != fw ) + update( QMIN(fw,rw) - frameWidth() - 2, 0, frameWidth()+4, rh ); + if ( rh != fh ) + update( 0, QMIN(fh,rh) - frameWidth() - 2, rw, frameWidth()+4 ); + } +} + + +/*! + Returns the maximum horizontal offset within the table of the + view's left edge in \e table coordinates. + + This is used mainly to set the horizontal scroll bar's range. + + \sa maxColOffset(), maxYOffset(), totalWidth() +*/ + +int QtTableView::maxXOffset() +{ + int tw = totalWidth(); + int maxOffs; + if ( testTableFlags(Tbl_scrollLastHCell) ) { + if ( nCols != 1) + maxOffs = tw - ( cellW ? cellW : cellWidth( nCols - 1 ) ); + else + maxOffs = tw - viewWidth(); + } else { + if ( testTableFlags(Tbl_snapToHGrid) ) { + if ( cellW ) { + maxOffs = tw - (viewWidth()/cellW)*cellW; + } else { + int goal = tw - viewWidth(); + int pos = tw; + int nextCol = nCols - 1; + int nextCellWidth = cellWidth( nextCol ); + while( nextCol > 0 && pos > goal + nextCellWidth ) { + pos -= nextCellWidth; + nextCellWidth = cellWidth( --nextCol ); + } + if ( goal + nextCellWidth == pos ) + maxOffs = goal; + else if ( goal < pos ) + maxOffs = pos; + else + maxOffs = 0; + } + } else { + maxOffs = tw - viewWidth(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + + +/*! + Returns the maximum vertical offset within the table of the + view's top edge in \e table coordinates. + + This is used mainly to set the vertical scroll bar's range. + + \sa maxRowOffset(), maxXOffset(), totalHeight() +*/ + +int QtTableView::maxYOffset() +{ + int th = totalHeight(); + int maxOffs; + if ( testTableFlags(Tbl_scrollLastVCell) ) { + if ( nRows != 1) + maxOffs = th - ( cellH ? cellH : cellHeight( nRows - 1 ) ); + else + maxOffs = th - viewHeight(); + } else { + if ( testTableFlags(Tbl_snapToVGrid) ) { + if ( cellH ) { + maxOffs = th - (viewHeight()/cellH)*cellH; + } else { + int goal = th - viewHeight(); + int pos = th; + int nextRow = nRows - 1; + int nextCellHeight = cellHeight( nextRow ); + while( nextRow > 0 && pos > goal + nextCellHeight ) { + pos -= nextCellHeight; + nextCellHeight = cellHeight( --nextRow ); + } + if ( goal + nextCellHeight == pos ) + maxOffs = goal; + else if ( goal < pos ) + maxOffs = pos; + else + maxOffs = 0; + } + } else { + maxOffs = th - viewHeight(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + + +/*! + Returns the index of the last column, which may be at the left edge + of the view. + + Depending on the \link setTableFlags() Tbl_scrollLastHCell\endlink flag, + this may or may not be the last column. + + \sa maxXOffset(), maxRowOffset() +*/ + +int QtTableView::maxColOffset() +{ + int mx = maxXOffset(); + if ( cellW ) + return mx/cellW; + else { + int xcd=0, col=0; + while ( col < nCols && mx > (xcd=cellWidth(col)) ) { + mx -= xcd; + col++; + } + return col; + } +} + + +/*! + Returns the index of the last row, which may be at the top edge of + the view. + + Depending on the \link setTableFlags() Tbl_scrollLastVCell\endlink flag, + this may or may not be the last row. + + \sa maxYOffset(), maxColOffset() +*/ + +int QtTableView::maxRowOffset() +{ + int my = maxYOffset(); + if ( cellH ) + return my/cellH; + else { + int ycd=0, row=0; + while ( row < nRows && my > (ycd=cellHeight(row)) ) { + my -= ycd; + row++; + } + return row; + } +} + + +void QtTableView::showOrHideScrollBars() +{ + if ( !autoUpdate() ) + return; + if ( vScrollBar ) { + if ( testTableFlags(Tbl_vScrollBar) ) { + if ( !vScrollBar->isVisible() ) + sbDirty = sbDirty | verMask; + } else { + if ( vScrollBar->isVisible() ) + vScrollBar->hide(); + } + } + if ( hScrollBar ) { + if ( testTableFlags(Tbl_hScrollBar) ) { + if ( !hScrollBar->isVisible() ) + sbDirty = sbDirty | horMask; + } else { + if ( hScrollBar->isVisible() ) + hScrollBar->hide(); + } + } + if ( cornerSquare ) { + if ( testTableFlags(Tbl_hScrollBar) && + testTableFlags(Tbl_vScrollBar) ) { + if ( !cornerSquare->isVisible() ) + cornerSquare->show(); + } else { + if ( cornerSquare->isVisible() ) + cornerSquare->hide(); + } + } +} + + +/*! + Updates the scroll bars and internal state. + + Call this function when the table view's total size is changed; + typically because the result of cellHeight() or cellWidth() have changed. + + This function does not repaint the widget. +*/ + +void QtTableView::updateTableSize() +{ + bool updateOn = autoUpdate(); + setAutoUpdate( FALSE ); + int xofs = xOffset(); + xOffs++; //so that setOffset will not return immediately + setOffset(xofs,yOffset(),FALSE); //to calculate internal state correctly + setAutoUpdate(updateOn); + + updateScrollBars( horSteps | horRange | + verSteps | verRange ); + showOrHideScrollBars(); +} + + +#endif +#endif diff --git a/win/Qt/tileedit.cpp b/win/Qt/tileedit.cpp new file mode 100644 index 0000000..faee82f --- /dev/null +++ b/win/Qt/tileedit.cpp @@ -0,0 +1,413 @@ +/* SCCS Id: @(#)tileedit.cpp 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ +/* +Build this little utility program if you want to use it to edit the tile +files. Move tileedit.cpp and tileedit.h to ../../util, add the +3 lines below to the Makefile there and "make tileedit". + +tileedit: tileedit.cpp $(TEXT_IO) + moc -o tileedit.moc tileedit.h + $(CC) -o tileedit -I../include -I$(QTDIR)/include -L$(QTDIR)/lib tileedit.cpp $(TEXT_IO) -lqt +*/ + + +#include "tileedit.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "config.h" +#include "tile.h" +extern const char *FDECL(tilename, (int, int)); +} + +#define TILES_ACROSS 20 + +TilePickerTab::TilePickerTab(const char* basename, int i, QWidget* parent) : + QWidget(parent) +{ + id = i; + filename = basename; + filename += ".txt"; + num = 0; + int index = 0; + for (int real=0; real<2; real++) { + if ( real ) { + image.create( TILES_ACROSS*TILE_X, + ((num+TILES_ACROSS-1)/TILES_ACROSS)*TILE_Y, 32 ); + } + if ( !fopen_text_file(filename.latin1(), RDTMODE) ) { + // XXX handle better + exit(1); + } + pixel p[TILE_Y][TILE_X]; + while ( read_text_tile(p) ) { + if ( real ) { + int ox = (index%TILES_ACROSS)*TILE_X; + int oy = (index/TILES_ACROSS)*TILE_Y; + for ( int y=0; yx()-e->x()%TILE_X; + int oy = e->y()-e->y()%TILE_Y; + QImage subimage = image.copy(ox,oy,TILE_X,TILE_Y); + if ( e->button() == RightButton ) { + setCurrent(subimage); + } else { + last_pick = ox/TILE_X + oy/TILE_Y*TILES_ACROSS; + } + emit pick(subimage); + emit pickName(tilename(id, last_pick)); +} + +void TilePickerTab::setCurrent(const QImage& i) +{ + int ox = last_pick%TILES_ACROSS * TILE_X; + int oy = last_pick/TILES_ACROSS * TILE_Y; + bitBlt( &image, ox, oy, &i ); + bitBlt( &pixmap, ox, oy, &i ); + repaint( ox, oy, TILE_X, TILE_Y, FALSE ); +} + +QSize TilePickerTab::sizeHint() const +{ + return pixmap.size(); +} + +void TilePickerTab::paintEvent( QPaintEvent* ) +{ + QPainter p(this); + p.drawPixmap(0,0,pixmap); +} + +static struct { + const char* name; + TilePickerTab* tab; +} tileset[] = { + { "monsters", 0 }, + { "objects", 0 }, + { "other", 0 }, + { 0 } +}; + +TilePicker::TilePicker(QWidget* parent) : + QTabWidget(parent) +{ + for (int i=0; tileset[i].name; i++) { + QString tabname = tileset[i].name; + tabname[0] = tabname[0].upper(); + tileset[i].tab = new TilePickerTab(tileset[i].name,i+1,this); + addTab( tileset[i].tab, tabname ); + connect( tileset[i].tab, SIGNAL(pick(const QImage&)), + this, SIGNAL(pick(const QImage&)) ); + connect( tileset[i].tab, SIGNAL(pickName(const QString&)), + this, SIGNAL(pickName(const QString&)) ); + } +} + +void TilePicker::setCurrent(const QImage& i) +{ + ((TilePickerTab*)currentPage())->setCurrent(i); +} + +void TilePicker::save() +{ + for (int i=0; tileset[i].tab; i++) { + tileset[i].tab->save(); + } +} + +TrivialTileEditor::TrivialTileEditor( QWidget* parent ) : + QWidget(parent) +{ +} + +const QImage& TrivialTileEditor::image() const +{ + return img; +} + +void TrivialTileEditor::setColor( QRgb rgb ) +{ + pen = rgb; + for (penpixel = 0; + penpixelrect(); + QPoint tl = imagePoint(r.topLeft()); + QPoint br = imagePoint(r.bottomRight()); + r = QRect(tl,br).intersect(img.rect()); + QPainter painter(this); + for (int y=r.top(); y<=r.bottom(); y++) { + for (int x=r.left(); x<=r.right(); x++) { + paintPoint(painter,QPoint(x,y)); + } + } +} + +void TrivialTileEditor::paintPoint(QPainter& painter, QPoint p) +{ + QPoint p1 = screenPoint(p); + QPoint p2 = screenPoint(p+QPoint(1,1)); + QColor c = img.color(img.scanLine(p.y())[p.x()]); + painter.fillRect(QRect(p1,p2-QPoint(1,1)), c); +} + +void TrivialTileEditor::mousePressEvent(QMouseEvent* e) +{ + QPoint p = imagePoint(e->pos()); + if ( !img.rect().contains(p) ) + return; + uchar& pixel = img.scanLine(p.y())[p.x()]; + if ( e->button() == LeftButton ) { + pixel = penpixel; + QPainter painter(this); + paintPoint(painter,p); + } else if ( e->button() == RightButton ) { + emit pick( img.color(pixel) ); + } else if ( e->button() == MidButton ) { + QPainter painter(this); + if ( pixel != penpixel ) + fill(painter,p,pixel); + } +} + +void TrivialTileEditor::fill(QPainter& painter, QPoint p, uchar from) +{ + if ( img.rect().contains(p) ) { + uchar& pixel = img.scanLine(p.y())[p.x()]; + if ( pixel == from ) { + pixel = penpixel; + paintPoint(painter,p); + fill(painter, p+QPoint(-1,0), from); + fill(painter, p+QPoint(+1,0), from); + fill(painter, p+QPoint(0,-1), from); + fill(painter, p+QPoint(0,+1), from); + } + } +} + +void TrivialTileEditor::mouseReleaseEvent(QMouseEvent* e) +{ + emit edited(image()); +} + +void TrivialTileEditor::mouseMoveEvent(QMouseEvent* e) +{ + QPoint p = imagePoint(e->pos()); + if ( !img.rect().contains(p) ) + return; + uchar& pixel = img.scanLine(p.y())[p.x()]; + pixel = penpixel; + QPainter painter(this); + paintPoint(painter,p); +} + +QPoint TrivialTileEditor::imagePoint(QPoint p) const +{ + return QPoint(p.x()*TILE_X/width(), p.y()*TILE_Y/height()); +} + +QPoint TrivialTileEditor::screenPoint(QPoint p) const +{ + return QPoint(p.x()*width()/TILE_X, p.y()*height()/TILE_Y); +} + +QSizePolicy TrivialTileEditor::sizePolicy() const +{ + return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding, TRUE ); +} + +QSize TrivialTileEditor::sizeHint() const +{ + return sizeForWidth(-1); +} + +QSize TrivialTileEditor::sizeForWidth(int w) const +{ + if ( w < 0 ) + return QSize(TILE_X*32,TILE_Y*32); + else + return QSize(w,w*TILE_Y/TILE_X); +} + + +TilePalette::TilePalette( QWidget* parent ) : + QWidget(parent) +{ + num = 0; + rgb = 0; +} + +TilePalette::~TilePalette() +{ + delete rgb; +} + +void TilePalette::setFromImage( const QImage& i ) +{ + num = i.numColors(); + rgb = new QRgb[num]; + memcpy(rgb, i.colorTable(), num*sizeof(QRgb)); + repaint(FALSE); +} + +void TilePalette::setColor(QRgb c) +{ + for (int i=0; ix()*num/width(); + emit pick(rgb[c]); +} + +TileEditor::TileEditor(QWidget* parent) : + QVBox(parent), + editor(this), + palette(this) +{ + connect( &palette, SIGNAL(pick(QRgb)), + &editor, SLOT(setColor(QRgb)) ); + connect( &editor, SIGNAL(pick(QRgb)), + &palette, SLOT(setColor(QRgb)) ); + connect( &editor, SIGNAL(edited(const QImage&)), + this, SIGNAL(edited(const QImage&)) ); +} + +void TileEditor::edit(const QImage& i) +{ + editor.setImage(i); + palette.setFromImage(i); +} + +const QImage& TileEditor::image() const +{ + return editor.image(); +} + +class Main : public QMainWindow { +public: + Main() : + central(this), + editor(¢ral), + picker(¢ral) + { + QPopupMenu* file = new QPopupMenu(menuBar()); + file->insertItem("&Save", &picker, SLOT(save()), CTRL+Key_S); + file->insertSeparator(); + file->insertItem("&Exit", qApp, SLOT(quit()), CTRL+Key_Q); + menuBar()->insertItem("&File", file); + + connect( &picker, SIGNAL(pick(const QImage&)), + &editor, SLOT(edit(const QImage&)) ); + connect( &picker, SIGNAL(pickName(const QString&)), + statusBar(), SLOT(message(const QString&)) ); + connect( &editor, SIGNAL(edited(const QImage&)), + &picker, SLOT(setCurrent(const QImage&)) ); + + setCentralWidget(¢ral); + } + +private: + QHBox central; + TileEditor editor; + TilePicker picker; +}; + +main(int argc, char** argv) +{ + QApplication app(argc,argv); + Main m; + app.setMainWidget(&m); + m.show(); + return app.exec(); +} + +#include "tileedit.moc" diff --git a/win/Qt/tileedit.h b/win/Qt/tileedit.h new file mode 100644 index 0000000..8d2d2cd --- /dev/null +++ b/win/Qt/tileedit.h @@ -0,0 +1,128 @@ +/* SCCS Id: @(#)tileedit.h 3.4 1999/11/19 */ +/* Copyright (c) Warwick Allison, 1999. */ +/* NetHack may be freely redistributed. See license for details. */ +#ifndef QNHTILEEDIT_H +#define QNHTILEEDIT_H + +#include +#include +#include +#include + +class TilePickerTab : public QWidget { + Q_OBJECT +public: + TilePickerTab(const char* basename, int id, QWidget* parent); + + bool save(); + int numTiles(); + +signals: + void pick(const QImage&); + void pickName(const QString&); + +public slots: + void setCurrent(const QImage&); + +protected: + void paintEvent( QPaintEvent* ); + QSize sizeHint() const; + void mousePressEvent(QMouseEvent*); + +private: + QString filename; + int id; + int last_pick; + int num; + QPixmap pixmap; + QImage image; +}; + +class TilePicker : public QTabWidget { + Q_OBJECT +public: + TilePicker(QWidget* parent); + + void setTile(int tilenum, const QImage&); + +signals: + void pick(const QImage&); + void pickName(const QString&); + +public slots: + void setCurrent(const QImage&); + void save(); +}; + +class TrivialTileEditor : public QWidget { + Q_OBJECT +public: + TrivialTileEditor( QWidget* parent ); + const QImage& image() const; + +signals: + void edited(const QImage&); + void pick(QRgb); + +public slots: + void setColor(QRgb); + void setImage( const QImage& ); + +protected: + void paintEvent( QPaintEvent* ); + void mousePressEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + QSize sizeHint() const; + QSize sizeForWidth(int) const; + QSizePolicy sizePolicy() const; + +private: + void fill(QPainter& painter, QPoint p, uchar from); + QImage img; + QColor pen; + int penpixel; + void paintPoint(QPainter& painter, QPoint p); + QPoint screenPoint(QPoint) const; + QPoint imagePoint(QPoint) const; +}; + +class TilePalette : public QWidget { + Q_OBJECT +public: + TilePalette( QWidget* parent ); + ~TilePalette(); + void setFromImage( const QImage& ); +protected: + void paintEvent( QPaintEvent* ); + void mousePressEvent(QMouseEvent*); + QSize sizeHint() const; + QSizePolicy sizePolicy() const; +signals: + void pick(QRgb); +public slots: + void setColor(QRgb); +private: + int num; + QRgb *rgb; +}; + +class TileEditor : public QVBox { + Q_OBJECT +public: + TileEditor(QWidget* parent); + + const QImage& image() const; + +signals: + void edited(const QImage&); + +public slots: + void edit(const QImage&); + +private: + TrivialTileEditor editor; + TilePalette palette; +}; + +#endif diff --git a/win/X11/Install.X11 b/win/X11/Install.X11 new file mode 100644 index 0000000..b4b7a43 --- /dev/null +++ b/win/X11/Install.X11 @@ -0,0 +1,202 @@ + +This document describes the installation of NetHack with an X11 interface. + +There are no explicit UNIX dependencies in this code, but we have only +tested it under UNIX, using X11R4, X11R5, or X11R6. We have two reports +that the code also works under DesqView/X on MS-DOS with djgpp, but you +will have to add dependencies for the X code to that makefile before you +can use it. Other X11R4+ platforms may work as well, with some tweaking +likely. Follow WIN* in sys/unix/Makefile.src for compilation hints. + +(If you try to compile it with X11R3 or earlier, you will get many errors, +starting with complaints about XtPointer not being declared. If you get +around the compilation problems, you will still need a recent library of +Athena Widgets to link against. Once compiled, you can probably run it +under an R3 X server, though.) + +The reason this uses the Athena widget set is that the Athena widgets come +free from MIT (like X11). Unfortunately, the companies that resell X11 +(value subtracted er, added software; yea, yea, that's the ticket) usually +discourage its use by either omitting the set or putting it on the +"unsupported" portion of their tape. If you do not have the Athena +widgets, you may obtain them via anonymous ftp from ftp.x.org. + + +To use this code, define X11_GRAPHICS in include/config.h. (You can comment +out TTY_GRAPHICS or change DEFAULT_WINDOW_SYS if you want to, but there's +little reason to do so. The X11 version almost requires a config file +for full effect, so you can just as well set windowtype there; also, you +or someone else might just possibly be stuck in a situation where you can't +use the X version -- over a non-blindingly-fast modem, say.) You may also +want to define USE_XPM or GRAPHIC_TOMBSTONE as discussed below. + +In src/Makefile, add $(WINX11SRC), $(WINX11OBJ), and $(WINX11LIB) to +WINSRC, WINOBJ, and WINLIB respectively, and compile. This will give you +an executable supporting both X11 and tty windowing. + + +If you want to use the optional tiles (multicolored pictures instead of a +replacement font), you will need to have the win/share files and change +the VARDATND setting in the top Makefile to contain the tile files before +you do your 'make all'. + +If you get a linker error referring to `substitute_tiles' then most +likely you have overlooked the WINSRC, WINOBJ, WINLIB step above. +Alternatively, you are building with more than one non-tty interface +specified but haven't followed the direction in src/Makefile to remove +all but one instance of tile.o from the WINxxxOBJ values used for WINOBJ. + +When using tiles, you have the option of defining USE_XPM in config.h. +This causes NetHack to use the XPM file format for the "x11tiles" file +rather than a custom format. Since the XPM format can be processed by +existing tools such as PBMPlus and XV, you can modify the tiles to suit +your environment. However, you need to make sure the number of tiles +in each row of the image remains the same (currently 40), as the code +depends on this to calculate the size of each tile. For example, you may +magnify them for display on high-resolution screens using the following +command: + xpmtoppm x11tiles | pnmscale -xscale 1 -yscale 1.6875 | + pnmdepth 255 | ppmquant 100 | ppmtoxpm >x11tiles_big.xpm +To use XPM, you must have the free XPM libraries installed in your system. +Official xpm releases can be found by ftp on: + ftp.x.org (198.4.202.8) contrib/libraries (Boston, USA) + avahi.inria.fr (138.96.12.1) pub/xpm (Sophia Antipolis, France) +If you do choose to define USE_XPM, be sure to add "-lXpm" to WINX11LIB +in src/Makefile. + +If you define USE_XPM in config.h, you may also define GRAPHIC_TOMBSTONE +which causes the closing tombstone to be displayed from the image file +specified by the "tombstone" X resource (rip.xpm by default). In this +case, make sure the top Makefile VARDATND also contains rip.xpm. + + +Whether or not you install tile support, you can provide support for +special graphics symbols via alternate fonts. (The fonts and tiles +cannot be used at the same time, but the same executable handles both.) +The two included X11 fonts use the general NetHack map area remapping +to represent object/dungeon/trap/effect characters (see win/X11/nethack.rc +and the Guidebook) as monocolored symbols and monsters as monocolored +letters. For instance, a ruby potion will show up as a potion symbol in +red. It's easier to see the difference between fonts and tiles than to +describe it. :-) Unless you are the only one using your executable and +you already know which you prefer, we suggest installing the optional files +for both possibilities and letting each person decide for themselves. + +To use the included fonts, you will need to install one or both of them +and then use the symbol mappings found in nethack.rc. The fonts are found +in nh10.bdf and ibm.bdf. You first need to convert the bdf files to whatever +form your X11 server recognizes (usually using a command called bdftosnf +for R4 servers or bdftopcf for R5 servers). Then run mkfontdir on the +directory containing your font files (you might want to copy them to GAMEDIR, +from the top Makefile, after you've done "make install"). If these commands +aren't familiar, talk to your local X11 guru and read the man pages. +Finally, add that directory to your font search path (e.g. xset fp+ GAMEDIR, +setting GAMEDIR to the value of GAMEDIR in the top Makefile). Alternatively, +you (assuming you are a system administrator) can install the fonts in your +standard X11 font directory. If you do not install the fonts in the standard +X11 font directory, all persons playing nethack must add that "xset fp+" +command to their .xinitrc file, or whatever file they execute when starting +X11. Adding the "xset" command to the nethack.sh is also an alternative, +though it may clutter your X11 font search path after playing several games, +so this method is not recommended. See the note below for the alternative +installation procedure for Sun's OpenWindows. + + +If your X11 include files and libraries are not installed in a standard +place (i.e. /usr/include/X11 and /usr/lib respectively) you will need to +prepend an appropriate -I parameter to CFLAGS and a +-L parameter to LFLAGS, setting to the place to +find the include and library files for X11. + + +Finally, to ensure NetHack's windows look the way they were intended to +look, make sure the top Makefile VARDATND also contains NetHack.ad. If it +does, running nethack will automatically take the appropriate steps to +cause this file to be used to initialize NetHack's X11 resources. + + +Three icon suggestions to the window manager are supported: nh72, nh56, +and nh32. Data for them comes from the source files nh72icon, nh56icon, +and nh32icon; they are compiled into the program via #includes in winX.c. +Selection between them is controlled by the "icon" resource in NetHack.ad; +the default is nh72. + + +Sorry, an Imakefile is not included. Unlike many X11 programs, X11 +support is only a small, optional, part of nethack, and the Makefile is +needed for systems that don't use X11. + + +Notes for Sun's OpenWindows: + 1. For OpenWindows 3.0 (NOT 2.x), define OPENWINBUG in include/unixconf.h. + The library bug from SunOS 4.1.x is fixed in Solaris 2.x (or when + proper Sun patches have been applied to 4.1.x), so it is also + unnecessary there. (Defining it when unnecessary causes the same + problem being avoided when it is necessary. :-) + + 2. In addition to the changes suggested by the comments in src/Makefile, + + -- for OpenWindows 2.x and 3.0 (NOT 3.1) (i.e., versions for SunOS 4.x), + add -I/usr/openwin/include to CFLAGS, -L/usr/openwin/lib to LFLAGS, + and -lm to WINX11LIB in src/Makefile. + + -- for OpenWindows 3.1 (i.e., versions for Solaris 2.x), add + -I/usr/openwin/include to CFLAGS, -L/usr/openwin/lib -L/usr/ccs/lib + -R/usr/openwin/lib to LFLAGS, and -lsocket -lnsl -lm + to WINX11LIB in src/Makefile. + + (Naturally, if your OpenWindows is installed elsewhere, adapt the + openwin paths.) This will allow you to create a game executable. + + 3. Run the fonts through convertfont and run bldfamily on the directory. + Now you must let your X server know where to find the fonts. For a + personal installation, the simplest thing is to include the directory + of the fonts in the environment variable FONTPATH, as set in your + .profile or .login before starting the server. For a multi-user + installation, you have the various "xset fp+" options outlined + above for standard X. + + 4. Something must still be done with the NetHack.ad file -- all three + of the possibilities mentioned for standard X11 should work. + + +Notes for AIX 3.2: + 1. AIX 3.2 includes the Athena Widget Toolkit library (and other things) + under the /usr/lpp/X11/Xamples tree, so you will have to add + -L/usr/lpp/X11/Xamples/lib to LFLAGS. If you can't find libXaw.a on + your first build, go into /usr/lib/X11/Xamples, read the README file, + and build the library. + +Notes for XFree86 - (on linux and BSD386 platforms) + + 1. Edit src/Makefile for linux/BSD386. Even though you use the + Open Look Window manager, do not define OPENWINBUG. Use the + standard X11 object and library options. + + 2. Follow the standard installation directions defined above. + + +File Description +--------- --------------------------------------------------------------- +nethack.rc - A sample configuration file for fonts nh10 and ibm. +nh10.bdf - A modified version of the 10x20 standard font. +ibm.bdf - A modified version of one of the ibm (8x14) nethack font. + Must be used in conjunction with NetHack.ad or nethack.rc. +nh32icon - A 32x32 icon bitmap for use with window managers. +nh56icon - A 56x56 icon bitmap for use with window managers. +nh72icon - A 72x72 icon bitmap for use with window managers. +nh_icon.xpm - A color icon for use with window managers. +NetHack.ad - A sample .Xdefaults for a color screen. +../../include/Window.h +../../include/WindowP.h +Window.c - A bare-bones widget that has 16 colors and a drawing canvas. +../../include/winX.h + - Defines for the X window-port. +win*.c - Code for the X window-port +dialogs.c - A better dialog widget. Original code (modified slightly + by Dean Luick) distributed under the X copyright by Tim + Theisen. This is from his Ghostview program (which is under + the GNU public license, v2 or higher). +pet_mark.xbm - A pet indicator bitmap for tiles. +rip.xpm - A graphical tombstone. +tile2x11.c - Converts win/share tiles for X11 use. diff --git a/win/X11/NetHack.ad b/win/X11/NetHack.ad new file mode 100644 index 0000000..d7e9ed3 --- /dev/null +++ b/win/X11/NetHack.ad @@ -0,0 +1,190 @@ +! The display_file, tombstone, and menu windows are all formatted assuming +! a fixed width font. Text windows may or may not be formatted as above. +! The rip window applies if the GRAPHIC_TOMBSTONE option is turned on, and +! requires a 12 pixel font for correct appearance. +! +NetHack*font: variable +NetHack*display_file*font: fixed +NetHack*tombstone*font: fixed +NetHack*text*rip*font: -*-times-medium-r-*-*-12-*-*-*-*-*-*-* +NetHack*menu*font: fixed +NetHack*text*font: fixed +NetHack*map*font: nh10 + +! To use full-color tiles for the map, uncomment the tile file name. +! If you use a 100dpi (or greater) monitor you may wish to double the +! tile size so you can see the figures. If NetHack was compiled to +! use XPM (USE_XPM in config.h), the tile_file is a standard XPM file. +! Otherwise, it is a custom format. double_tile_size only applies to +! the custom format - to enlarge an XPM file, use processing tools +! such as XV or preferably PBMplus. +! +!NetHack.tile_file: x11tiles +!NetHack.double_tile_size: True +! +! The annotation of pets. +!NetHack.pet_mark_bitmap: pet_mark.xbm +!NetHack.pet_mark_color: Red + +! Tombstone +! The image file +!NetHack.tombstone: rip.xpm +! Text starts at (tombtext_x, tombtext_y) and subsequent lines +! are displaced by (tombtext_dx, tombtext_dy) pixels. If you +!NetHack.tombtext_x: 155 +!NetHack.tombtext_y: 78 +!NetHack.tombtext_dx: 0 +!NetHack.tombtext_dy: 13 +! The color to use for the text on the hero's tombstone +NetHack*rip*foreground: black + +! Translation tables. There are currently several actions in nethack, but +! the only one you should be using is "input()", which, with no parameters, +! uses XLookupString to translate your keypress into a command. You +! can optionally give it parameters to change the behavior, see the example +! below. Note that you have to specify the translations in every appropriate +! window. +NetHack*message*translations: : input() +! +! Example extra translations for the map window. +! +!NetHack*map*translations: #override \ +! !Left: input(h) \n\ +! !Right: input(l) \n\ +! !Up: input(k) \n\ +! !Down: input(j) +! +! The icon to use; supported values are nh72, nh56, and nh32; nh72 is the +! default. Some window managers may not support the larger icon sizes. +! It is not guaranteed that the window manager will honor the icon selection. +!NetHack*icon: nh56 +! +! If True, the default, a popup for single character prompts such as y/n +! questions is _not_ used. +NetHack*slow: True + +! The number of lines the message window will show without scrolling. +!NetHack*message_lines: 12 +! +! If True, the message window has a line that seperates old and new messages. +!NetHack*message_line: True +! +! If True, force keyboard to attach to popup windows. Some window managers +! enforce a click-to-focus-keyboard policy (e.g. the DECwindows wm). NetHack +! has a lot of popups and is almost unplayable without some kind of autofocus. +!NetHack*autofocus: True +! +! Specify the number of rows and columns of the map window. The default +! is the standard 80x21 window. Note: this _does_not_ change nethack's +! level size, only what you see of it. +!NetHack*map*rows: 21 +!NetHack*map*columns: 80 + +! Parts of the fancy status display. +! +NetHack*status_condition.borderWidth: 0 +NetHack*status_info*borderWidth: 0 + +! Sample color screen entries. +! +NetHack*nethack.background: wheat +NetHack*map*yellow: gold +NetHack*map*brown: tan +NetHack*map*gray: grey85 +NetHack*map*foreground: wheat +NetHack*map*background: grey40 + +NetHack*fancy_status.skipAdjust: True +NetHack*fancy_status.background: wheat +NetHack*status_info*foreground: Sienna +NetHack*status_info*background: wheat +NetHack*status_info.background: wheat +NetHack*status_attributes*foreground: black +NetHack*status_attributes*background: white +NetHack*status_condition*foreground: red +NetHack*status_condition*background: wheat +NetHack*Scrollbar*foreground: Sienna +NetHack*Scrollbar*background: wheat +NetHack*status_info*showGrip: False +NetHack*status_attributes*showGrip: False + +NetHack*player_selection*random.borderColor: blue +NetHack*player_selection*random.borderWidth: 2 +NetHack*player_selection*random.foreground: blue +NetHack*player_selection*random.accelerators: #override\n\ + Return: set() notify() unset() +NetHack*player_selection*quit.borderColor: blue +NetHack*player_selection*quit.foreground: blue +NetHack*player_selection*Command.borderColor: red +NetHack*player_selection*Command.foreground: red +NetHack*player_selection*quit.accelerators: #override\n\ + Escape: set() notify() unset() + +NetHack*race_selection*random.borderColor: blue +NetHack*race_selection*random.borderWidth: 2 +NetHack*race_selection*random.foreground: blue +NetHack*race_selection*random.accelerators: #override\n\ + Return: set() notify() unset() +NetHack*race_selection*quit.borderColor: blue +NetHack*race_selection*quit.foreground: blue +NetHack*race_selection*Command.borderColor: red +NetHack*race_selection*Command.foreground: red +NetHack*race_selection*quit.accelerators: #override\n\ + Escape: set() notify() unset() + +NetHack*gender_selection*random.borderColor: blue +NetHack*gender_selection*random.borderWidth: 2 +NetHack*gender_selection*random.foreground: blue +NetHack*gender_selection*random.accelerators: #override\n\ + Return: set() notify() unset() +NetHack*gender_selection*quit.borderColor: blue +NetHack*gender_selection*quit.foreground: blue +NetHack*gender_selection*Command.borderColor: red +NetHack*gender_selection*Command.foreground: red +NetHack*gender_selection*quit.accelerators: #override\n\ + Escape: set() notify() unset() + +NetHack*alignment_selection*random.borderColor: blue +NetHack*alignment_selection*random.borderWidth: 2 +NetHack*alignment_selection*random.foreground: blue +NetHack*alignment_selection*random.accelerators: #override\n\ + Return: set() notify() unset() +NetHack*alignment_selection*quit.borderColor: blue +NetHack*alignment_selection*quit.foreground: blue +NetHack*alignment_selection*Command.borderColor: red +NetHack*alignment_selection*Command.foreground: red +NetHack*alignment_selection*quit.accelerators: #override\n\ + Escape: set() notify() unset() + +NetHack*extended_commands*dismiss.borderColor: blue +NetHack*extended_commands*dismiss.foreground: blue +NetHack*extended_commands*help.borderColor: blue +NetHack*extended_commands*help.foreground: blue +NetHack*extended_commands*Command.borderColor: red +NetHack*extended_commands*Command.foreground: red +NetHack*extended_commands*help.accelerators: #override\n\ + :?: set() notify() unset() +NetHack*extended_commands*dismiss.accelerators: #override\n\ + Escape: set() notify() unset() +! +! +! The following are the default 15 colors that the nethack map uses. +! If they don't look good on your screen, change them. +! +! The foreground color is used as "no color". +! +!NetHack*map*black: black +!NetHack*map*red: red +!NetHack*map*green: pale green +!NetHack*map*brown: brown +!NetHack*map*blue: blue +!NetHack*map*magenta: magenta +!NetHack*map*cyan: light cyan +!NetHack*map*gray: gray +!NetHack*map*orange: orange +!NetHack*map*bright_green: green +!NetHack*map*yellow: yellow +!NetHack*map*bright_blue: royal blue +!NetHack*map*bright_magenta: violet +!NetHack*map*bright_cyan: cyan +!NetHack*map*white: white diff --git a/win/X11/Window.c b/win/X11/Window.c new file mode 100644 index 0000000..c21c8f2 --- /dev/null +++ b/win/X11/Window.c @@ -0,0 +1,170 @@ +/* SCCS Id: @(#)Window.c 3.4 1993/02/02 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Data structures and support routines for the Window widget. This is a + * drawing canvas with 16 colors and one font. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#ifdef MSDOS /* from compiler */ +#define SHORT_FILENAMES +#endif + +#ifdef SHORT_FILENAMES +#include +#else +#include +#endif +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "xwindowp.h" + +#include "config.h" + +static XtResource resources[] = { +#define offset(field) XtOffset(WindowWidget, window.field) + /* {name, class, type, size, offset, default_type, default_addr}, */ + { XtNrows, XtCRows, XtRDimension, sizeof(Dimension), + offset(rows), XtRImmediate, (XtPointer) 21}, + { XtNcolumns, XtCColumns, XtRDimension, sizeof(Dimension), + offset(columns), XtRImmediate, (XtPointer) 80}, + { XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(foreground), XtRString, XtDefaultForeground }, + + { XtNblack, XtCColor, XtRPixel, sizeof(Pixel), + offset(black), XtRString, "black"}, + { XtNred, XtCColor, XtRPixel, sizeof(Pixel), + offset(red), XtRString, "red" }, + { XtNgreen, XtCColor, XtRPixel, sizeof(Pixel), + offset(green), XtRString, "pale green" }, + { XtNbrown, XtCColor, XtRPixel, sizeof(Pixel), + offset(brown), XtRString, "brown" }, + { XtNblue, XtCColor, XtRPixel, sizeof(Pixel), + offset(blue), XtRString, "blue" }, + { XtNmagenta, XtCColor, XtRPixel, sizeof(Pixel), + offset(magenta), XtRString, "magenta" }, + { XtNcyan, XtCColor, XtRPixel, sizeof(Pixel), + offset(cyan), XtRString, "light cyan" }, + { XtNgray, XtCColor, XtRPixel, sizeof(Pixel), + offset(gray), XtRString, "gray" }, + { XtNorange, XtCColor, XtRPixel, sizeof(Pixel), + offset(orange), XtRString, "orange" }, + { XtNbright_green, XtCColor, XtRPixel, sizeof(Pixel), + offset(bright_green), XtRString, "green" }, + { XtNyellow, XtCColor, XtRPixel, sizeof(Pixel), + offset(yellow), XtRString, "yellow" }, + { XtNbright_blue, XtCColor, XtRPixel, sizeof(Pixel), + offset(bright_blue), XtRString, "royal blue" }, + { XtNbright_magenta, XtCColor, XtRPixel, sizeof(Pixel), + offset(bright_magenta), XtRString, "violet" }, + { XtNbright_cyan, XtCColor, XtRPixel, sizeof(Pixel), + offset(bright_cyan), XtRString, "cyan" }, + { XtNwhite, XtCColor, XtRPixel, sizeof(Pixel), + offset(white), XtRString, "white" }, + + { XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), + offset(font), XtRString, XtDefaultFont }, + { XtNexposeCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), + offset(expose_callback), XtRCallback, (char *)0 }, + { XtNcallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), + offset(input_callback), XtRCallback, (char *)0 }, + { XtNresizeCallback, XtCCallback, XtRCallback, sizeof(XtCallbackList), + offset(resize_callback), XtRCallback, (char *)0 }, +#undef offset +}; + +/* ARGSUSED */ +static void no_op(w, event, params, num_params) + Widget w; /* unused */ + XEvent *event; /* unused */ + String *params; /* unused */ + Cardinal *num_params; /* unused */ +{ +} + +static XtActionsRec actions[] = +{ + {"no-op", no_op}, +}; + +static char translations[] = +": input() \ +"; + +/* ARGSUSED */ +static void Redisplay(w, event, region) + Widget w; + XEvent *event; + Region region; /* unused */ +{ + /* This isn't correct - we need to call the callback with region. */ + XtCallCallbacks(w, XtNexposeCallback, (caddr_t) event); +} + +/* ARGSUSED */ +static void Resize(w) + Widget w; +{ + XtCallCallbacks(w, XtNresizeCallback, (caddr_t) 0); +} + + +WindowClassRec windowClassRec = { + { /* core fields */ + /* superclass */ (WidgetClass) &widgetClassRec, + /* class_name */ "Window", + /* widget_size */ sizeof(WindowRec), + /* class_initialize */ 0, + /* class_part_initialize */ 0, + /* class_inited */ FALSE, + /* initialize */ 0, + /* initialize_hook */ 0, + /* realize */ XtInheritRealize, + /* actions */ actions, + /* num_actions */ XtNumber(actions), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave */ TRUE, + /* visible_interest */ FALSE, + /* destroy */ 0, + /* resize */ Resize, + /* expose */ Redisplay, + /* set_values */ 0, + /* set_values_hook */ 0, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ 0, + /* accept_focus */ 0, + /* version */ XtVersion, + /* callback_private */ 0, + /* tm_table */ translations, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ 0 + }, + { /* window fields */ + /* empty */ 0 + } +}; + +WidgetClass windowWidgetClass = (WidgetClass)&windowClassRec; + +Font +WindowFont(w) Widget w; { return ((WindowWidget)w)->window.font->fid; } + +XFontStruct * +WindowFontStruct(w) Widget w; { return ((WindowWidget)w)->window.font; } diff --git a/win/X11/dialogs.c b/win/X11/dialogs.c new file mode 100644 index 0000000..a887ef0 --- /dev/null +++ b/win/X11/dialogs.c @@ -0,0 +1,340 @@ +/* + * Copyright 1991 University of Wisconsin-Madison + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the University of Wisconsin-Madison not + * be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The University of + * Wisconsin-Madison makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE UNIVERSITY OF WISCONSIN-MADISON DISCLAIMS ALL WARRANTIES WITH REGARD TO + * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF WISCONSIN-MADISON BE LIABLE FOR + * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Tim Theisen Department of Computer Sciences + * tim@cs.wisc.edu University of Wisconsin-Madison + * uwvax!tim 1210 West Dayton Street + * (608)262-0438 Madison, WI 53706 + * + * + * Modified 12/91 by Dean Luick. Tim graciously donated this piece of code + * from his program ghostview, an X11 front end for ghostscript. + * + * + Make the cancel button optional. + * + Put an #ifdef SPECIAL_CMAP around code to fix a colormap bug. + * We don't need it here. + * + Add the function positionpopup() from another part of ghostview + * to this code. + * + * Modified 2/93, Various. + * + Added workaround for SYSV include problem. + * + Changed the default width response text widget to be as wide as the + * window itself. Suggestion from David E. Wexelblat, dwex@goblin.org. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "config.h" /* #define for const for non __STDC__ compilers */ + +/* ":" added to both translations below to allow limited redefining of + * keysyms before testing for keysym values -- dlc */ +static const char okay_accelerators[] = + "#override\n\ + :Return: set() notify() unset()\n"; + +static const char cancel_accelerators[] = + "#override\n\ + :Escape: set() notify() unset()\n\ + :[: set() notify() unset()\n"; /* for keyboards w/o an ESC */ + + +/* Create a dialog widget. It is just a form widget with + * a label prompt + * a text response + * an okay button + * an optional cancel button + */ +Widget +CreateDialog(parent, name, okay_callback, cancel_callback) + Widget parent; + String name; + XtCallbackProc okay_callback; + XtCallbackProc cancel_callback; +{ + Widget form, prompt, response, okay, cancel; + Arg args[20]; + Cardinal num_args; + + num_args = 0; +#ifdef SPECIAL_CMAP + if (special_cmap) { + XtSetArg(args[num_args], XtNbackground, white); num_args++; + } +#endif + form = XtCreateManagedWidget(name, formWidgetClass, parent, args, num_args); + + num_args = 0; +#ifdef SPECIAL_CMAP + if (special_cmap) { + XtSetArg(args[num_args], XtNforeground, black); num_args++; + XtSetArg(args[num_args], XtNbackground, white); num_args++; + } +#endif + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + prompt = XtCreateManagedWidget("prompt", labelWidgetClass, + form, args, num_args); + + num_args = 0; +#ifdef SPECIAL_CMAP + if (special_cmap) { + XtSetArg(args[num_args], XtNforeground, black); num_args++; + XtSetArg(args[num_args], XtNbackground, white); num_args++; + } +#endif + XtSetArg(args[num_args], XtNfromVert, prompt); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++; + XtSetArg(args[num_args], XtNresize, XawtextResizeWidth); num_args++; + XtSetArg(args[num_args], XtNstring, ""); num_args++; + response = XtCreateManagedWidget("response", asciiTextWidgetClass, + form, args, num_args); + + num_args = 0; +#ifdef SPECIAL_CMAP + if (special_cmap) { + XtSetArg(args[num_args], XtNforeground, black); num_args++; + XtSetArg(args[num_args], XtNbackground, white); num_args++; + } +#endif + XtSetArg(args[num_args], XtNfromVert, response); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNaccelerators, + XtParseAcceleratorTable(okay_accelerators)); num_args++; + okay = XtCreateManagedWidget("okay", commandWidgetClass, + form, args, num_args); + XtAddCallback(okay, XtNcallback, okay_callback, form); + + /* Only create cancel button if there is a callback for it. */ + if (cancel_callback) { + num_args = 0; +#ifdef SPECIAL_CMAP + if (special_cmap) { + XtSetArg(args[num_args], XtNforeground, black); num_args++; + XtSetArg(args[num_args], XtNbackground, white); num_args++; + } +#endif + XtSetArg(args[num_args], XtNfromVert, response); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, okay); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNaccelerators, + XtParseAcceleratorTable(cancel_accelerators)); num_args++; + cancel = XtCreateManagedWidget("cancel", commandWidgetClass, + form, args, num_args); + XtAddCallback(cancel, XtNcallback, cancel_callback, form); + XtInstallAccelerators(response, cancel); + } + + XtInstallAccelerators(response, okay); + XtSetKeyboardFocus(form, response); + + return form; +} + +#if 0 +/* get the prompt from the dialog box. Used a startup time to + * save away the initial prompt */ +String +GetDialogPrompt(w) + Widget w; +{ + Arg args[1]; + Widget label; + String s; + + label = XtNameToWidget(w, "prompt"); + XtSetArg(args[0], XtNlabel, &s); + XtGetValues(label, args, ONE); + return XtNewString(s); +} +#endif + +/* set the prompt. This is used to put error information in the prompt */ +void +SetDialogPrompt(w, newprompt) + Widget w; + String newprompt; +{ + Arg args[1]; + Widget label; + + label = XtNameToWidget(w, "prompt"); + XtSetArg(args[0], XtNlabel, newprompt); + XtSetValues(label, args, ONE); +} + +/* get what the user typed; caller must free the response */ +String +GetDialogResponse(w) + Widget w; +{ + Arg args[1]; + Widget response; + String s; + + response = XtNameToWidget(w, "response"); + XtSetArg(args[0], XtNstring, &s); + XtGetValues(response, args, ONE); + return XtNewString(s); +} + +#define max(a,b) (((a) > (b)) ? (a) : (b)) +/* set the default reponse */ +void +SetDialogResponse(w, s) + Widget w; + String s; +{ + Arg args[4]; + Widget response; + XFontStruct *font; + Dimension width, nwidth, leftMargin, rightMargin; + + response = XtNameToWidget(w, "response"); + XtSetArg(args[0], XtNfont, &font); + XtSetArg(args[1], XtNleftMargin, &leftMargin); + XtSetArg(args[2], XtNrightMargin, &rightMargin); + XtSetArg(args[3], XtNwidth, &width); + XtGetValues(response, args, FOUR); + /* width includes margins as per Xaw documentation */ + nwidth = (font->max_bounds.width * strlen(s))+leftMargin+rightMargin; + if (nwidth < width) + nwidth = width; + + XtSetArg(args[0], XtNstring, s); + XtSetArg(args[1], XtNwidth, nwidth); + XtSetValues(response, args, TWO); + XawTextSetInsertionPoint(response, strlen(s)); +} + +#if 0 +/* clear the response */ +void +ClearDialogResponse(w) + Widget w; +{ + Arg args[2]; + Widget response; + + response = XtNameToWidget(w, "response"); + XtSetArg(args[0], XtNstring, ""); + XtSetArg(args[1], XtNwidth, 100); + XtSetValues(response, args, TWO); +} +#endif + + +/* Not a part of the original dialogs.c from ghostview --------------------- */ + +/* position popup window under the cursor */ +void +positionpopup(w, bottom) + Widget w; + boolean bottom; /* position y on bottom? */ +{ + Arg args[3]; + Cardinal num_args; + Dimension width, height, b_width; + int x, y, max_x, max_y; + Window root, child; + XSizeHints *hints; + int dummyx, dummyy; + unsigned int dummymask; + extern Widget toplevel; + + /* following line deals with a race condition w/brain-damaged WM's -dlc */ + XtUnrealizeWidget(w); + + XQueryPointer(XtDisplay(toplevel), XtWindow(toplevel), &root, &child, + &x, &y, &dummyx, &dummyy, &dummymask); + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &width); num_args++; + XtSetArg(args[num_args], XtNheight, &height); num_args++; + XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++; + XtGetValues(w, args, num_args); + + /* position so that the cursor is center,center or center,bottom */ + width += 2 * b_width; + x -= ( (Position) width/2 ); + if (x < 0) x = 0; + if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x; + + if (bottom) { + y -= (height+b_width-1); + height += 2 * b_width; + } else { + height += 2 * b_width; + y -= ( (Position) height/2 ); + } + if (y < 0) y = 0; + if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y; + + + num_args = 0; + XtSetArg(args[num_args], XtNx, x); num_args++; + XtSetArg(args[num_args], XtNy, y); num_args++; + XtSetValues(w, args, num_args); + + /* Some older window managers ignore XtN{x,y}; hint the same values */ + /* The {x,y} are not used by newer window managers; older ones need them */ + XtRealizeWidget(w); + hints = XAllocSizeHints(); + hints->flags = USPosition; + hints->x = x; + hints->y = y; + XSetWMNormalHints(XtDisplay(w), XtWindow(w), hints); + XFree(hints); +} diff --git a/win/X11/ibm.bdf b/win/X11/ibm.bdf new file mode 100644 index 0000000..f2b77f3 --- /dev/null +++ b/win/X11/ibm.bdf @@ -0,0 +1,5404 @@ +STARTFONT 2.1 +COMMENT (null) +FONT -Misc-Fixed-Medium-R-Normal-IBMPC-14-120-75-75-C-80-ISO8859-1 +SIZE 13 78 78 +FONTBOUNDINGBOX 8 14 0 -3 +STARTPROPERTIES 19 +FONTNAME_REGISTRY "" +FOUNDRY "Misc" +FAMILY_NAME "Fixed" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "IBMPC" +PIXEL_SIZE 14 +POINT_SIZE 120 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +AVERAGE_WIDTH 80 +CHARSET_REGISTRY "ISO8859" +CHARSET_ENCODING "1" +DEFAULT_CHAR 0 +FONT_DESCENT 3 +FONT_ASCENT 11 +COPYRIGHT "Public domain font. Share and enjoy." +ENDPROPERTIES +CHARS 256 +STARTCHAR C000 +ENCODING 0 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C001 +ENCODING 1 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7e00 +8100 +a500 +8100 +8100 +bd00 +9900 +8100 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C002 +ENCODING 2 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7e00 +ff00 +db00 +ff00 +ff00 +c300 +e700 +ff00 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C003 +ENCODING 3 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +6c00 +ee00 +fe00 +fe00 +fe00 +7c00 +3800 +1000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C004 +ENCODING 4 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1000 +3800 +7c00 +fe00 +7c00 +3800 +1000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C005 +ENCODING 5 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1000 +3800 +1000 +6c00 +ee00 +6c00 +1000 +3800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C006 +ENCODING 6 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1000 +3800 +7c00 +7c00 +fe00 +fe00 +6c00 +1000 +3800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C007 +ENCODING 7 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +1800 +3c00 +3c00 +1800 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C010 +ENCODING 8 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +ff00 +ff00 +ff00 +ff00 +ff00 +e700 +c300 +c300 +e700 +ff00 +ff00 +ff00 +ff00 +ff00 +ENDCHAR +STARTCHAR C011 +ENCODING 9 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +1800 +3c00 +6600 +6600 +3c00 +1800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C012 +ENCODING 10 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +ff00 +ff00 +ff00 +ff00 +e700 +c300 +9900 +9900 +c300 +e700 +ff00 +ff00 +ff00 +ff00 +ENDCHAR +STARTCHAR C013 +ENCODING 11 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1e00 +0e00 +1e00 +3600 +7800 +cc00 +cc00 +cc00 +7800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C014 +ENCODING 12 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +6600 +6600 +6600 +3c00 +1800 +7e00 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C015 +ENCODING 13 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1e00 +1a00 +1e00 +1800 +1800 +1800 +7800 +f800 +7000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C016 +ENCODING 14 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3e00 +3600 +3e00 +3600 +3600 +7600 +f600 +6600 +0e00 +1e00 +0c00 +0000 +0000 +ENDCHAR +STARTCHAR C017 +ENCODING 15 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1800 +db00 +7e00 +3c00 +6600 +6600 +3c00 +7e00 +db00 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C020 +ENCODING 16 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +8000 +e000 +f000 +fc00 +fe00 +fc00 +f000 +e000 +8000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C021 +ENCODING 17 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0200 +0e00 +3e00 +7e00 +fe00 +7e00 +3e00 +0e00 +0200 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C022 +ENCODING 18 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +3c00 +7e00 +1800 +1800 +1800 +7e00 +3c00 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C023 +ENCODING 19 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6600 +6600 +6600 +6600 +6600 +6600 +0000 +6600 +6600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C024 +ENCODING 20 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7f00 +db00 +db00 +db00 +7b00 +1b00 +1b00 +1b00 +1b00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C025 +ENCODING 21 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +7c00 +c600 +c600 +6000 +7c00 +f600 +de00 +7c00 +0c00 +c600 +c600 +7c00 +0000 +ENDCHAR +STARTCHAR C026 +ENCODING 22 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fe00 +fe00 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C027 +ENCODING 23 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +3c00 +7e00 +1800 +1800 +7e00 +3c00 +1800 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C030 +ENCODING 24 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +3c00 +7e00 +1800 +1800 +1800 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C031 +ENCODING 25 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +1800 +1800 +1800 +1800 +1800 +7e00 +3c00 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C032 +ENCODING 26 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0c00 +0e00 +ff00 +0e00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C033 +ENCODING 27 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +3000 +7000 +fe00 +7000 +3000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C034 +ENCODING 28 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +c000 +c000 +c000 +fe00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C035 +ENCODING 29 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +2400 +6600 +ff00 +6600 +2400 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C036 +ENCODING 30 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1000 +3800 +3800 +3800 +7c00 +7c00 +fe00 +fe00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C037 +ENCODING 31 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +fe00 +7c00 +7c00 +7c00 +3800 +3800 +1000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C040 +ENCODING 32 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ! +ENCODING 33 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +3c00 +3c00 +3c00 +1800 +1800 +0000 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR " +ENCODING 34 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3600 +3600 +3600 +1400 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR # +ENCODING 35 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6c00 +6c00 +6c00 +fe00 +6c00 +6c00 +fe00 +6c00 +6c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR $ +ENCODING 36 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1800 +1800 +7c00 +c600 +c000 +7800 +3c00 +0600 +c600 +7c00 +1800 +1800 +0000 +ENDCHAR +STARTCHAR % +ENCODING 37 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +6200 +6600 +0c00 +1800 +3000 +6600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR & +ENCODING 38 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3800 +6c00 +3800 +3800 +7600 +f600 +ce00 +cc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ' +ENCODING 39 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0c00 +0c00 +0c00 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ( +ENCODING 40 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1800 +3000 +3000 +3000 +3000 +3000 +1800 +0c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ) +ENCODING 41 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0c00 +0c00 +0c00 +0c00 +1800 +3000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR * +ENCODING 42 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +6c00 +3800 +fe00 +3800 +6c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR + +ENCODING 43 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +1800 +1800 +7e00 +1800 +1800 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR , +ENCODING 44 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +0c00 +1800 +0000 +0000 +ENDCHAR +STARTCHAR - +ENCODING 45 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +fe00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR . +ENCODING 46 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR / +ENCODING 47 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0200 +0600 +0c00 +1800 +3000 +6000 +c000 +8000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 0 +ENCODING 48 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +ce00 +de00 +f600 +e600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 1 +ENCODING 49 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +7800 +1800 +1800 +1800 +1800 +1800 +1800 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 2 +ENCODING 50 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +0c00 +1800 +3000 +6000 +c600 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 3 +ENCODING 51 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +0600 +0600 +3c00 +0600 +0600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 4 +ENCODING 52 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1c00 +3c00 +6c00 +cc00 +fe00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 5 +ENCODING 53 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +c000 +c000 +c000 +fc00 +0600 +0600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 6 +ENCODING 54 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c000 +c000 +fc00 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 7 +ENCODING 55 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +c600 +0c00 +1800 +3000 +3000 +3000 +3000 +3000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 8 +ENCODING 56 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c600 +7c00 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 9 +ENCODING 57 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c600 +7e00 +0600 +0600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR : +ENCODING 58 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ; +ENCODING 59 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +0000 +0c00 +0c00 +0c00 +1800 +0000 +0000 +ENDCHAR +STARTCHAR < +ENCODING 60 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1800 +3000 +6000 +c000 +6000 +3000 +1800 +0c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR = +ENCODING 61 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +fe00 +0000 +fe00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR > +ENCODING 62 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6000 +3000 +1800 +0c00 +0600 +0c00 +1800 +3000 +6000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ? +ENCODING 63 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +0c00 +1800 +1800 +0000 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR @ +ENCODING 64 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +de00 +de00 +de00 +dc00 +c000 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3800 +6c00 +c600 +c600 +c600 +fe00 +c600 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fc00 +6600 +6600 +6600 +7c00 +6600 +6600 +6600 +fc00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +6600 +c000 +c000 +c000 +c000 +c000 +6600 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +f800 +6c00 +6600 +6600 +6600 +6600 +6600 +6c00 +f800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +6600 +6000 +6000 +7c00 +6000 +6000 +6600 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +6600 +6000 +6000 +7c00 +6000 +6000 +6000 +f000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c000 +c000 +ce00 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +c600 +c600 +fe00 +c600 +c600 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +1800 +1800 +1800 +1800 +1800 +d800 +d800 +7000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +cc00 +d800 +f000 +f000 +d800 +cc00 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +f000 +6000 +6000 +6000 +6000 +6000 +6200 +6600 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +ee00 +fe00 +d600 +d600 +d600 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +e600 +e600 +f600 +de00 +ce00 +ce00 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c600 +c600 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fc00 +6600 +6600 +6600 +7c00 +6000 +6000 +6000 +f000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c600 +c600 +c600 +c600 +d600 +7c00 +0600 +0000 +0000 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fc00 +6600 +6600 +6600 +7c00 +7800 +6c00 +6600 +e600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c000 +6000 +3800 +0c00 +0600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7e00 +5a00 +1800 +1800 +1800 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +c600 +c600 +c600 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +c600 +c600 +c600 +c600 +6c00 +3800 +1000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +d600 +d600 +d600 +fe00 +ee00 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +6c00 +3800 +3800 +3800 +6c00 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6600 +6600 +6600 +6600 +3c00 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +c600 +8c00 +1800 +3000 +6000 +c200 +c600 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR [ +ENCODING 91 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR \ +ENCODING 92 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +8000 +c000 +6000 +3000 +1800 +0c00 +0600 +0200 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ] +ENCODING 93 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ^ +ENCODING 94 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1000 +3800 +6c00 +c600 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR _ +ENCODING 95 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ff00 +0000 +ENDCHAR +STARTCHAR ` +ENCODING 96 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1800 +1800 +1800 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7800 +0c00 +7c00 +cc00 +dc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +e000 +6000 +6000 +7c00 +6600 +6600 +6600 +6600 +fc00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7c00 +c600 +c000 +c000 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1c00 +0c00 +0c00 +7c00 +cc00 +cc00 +cc00 +cc00 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7c00 +c600 +fe00 +c000 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1c00 +3600 +3000 +3000 +fc00 +3000 +3000 +3000 +7800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7600 +ce00 +c600 +c600 +7e00 +0600 +c600 +7c00 +0000 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +e000 +6000 +6000 +6c00 +7600 +6600 +6600 +6600 +e600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +1800 +0000 +3800 +1800 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +0c00 +0000 +1c00 +0c00 +0c00 +0c00 +0c00 +cc00 +cc00 +7800 +0000 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +e000 +6000 +6000 +6600 +6c00 +7800 +6c00 +6600 +e600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +6c00 +fe00 +d600 +d600 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +dc00 +6600 +6600 +6600 +6600 +6600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7c00 +c600 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +dc00 +6600 +6600 +6600 +7c00 +6000 +6000 +f000 +0000 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7600 +cc00 +cc00 +cc00 +7c00 +0c00 +0c00 +1e00 +0000 +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +dc00 +6600 +6000 +6000 +6000 +f000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7c00 +c600 +7000 +1c00 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3000 +3000 +3000 +fc00 +3000 +3000 +3000 +3600 +1c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +cc00 +cc00 +cc00 +cc00 +cc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +c600 +c600 +c600 +6c00 +3800 +1000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +c600 +c600 +d600 +d600 +fe00 +6c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +c600 +6c00 +3800 +3800 +6c00 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +c600 +c600 +c600 +ce00 +7600 +0600 +c600 +7c00 +0000 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +fe00 +8c00 +1800 +3000 +6200 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR { +ENCODING 123 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0e00 +1800 +1800 +1800 +7000 +1800 +1800 +1800 +0e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR | +ENCODING 124 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1800 +1800 +1800 +1800 +0000 +1800 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR } +ENCODING 125 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7000 +1800 +1800 +1800 +0e00 +1800 +1800 +1800 +7000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ~ +ENCODING 126 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7600 +dc00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C177 +ENCODING 127 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +1000 +3800 +3800 +6c00 +6c00 +fe00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vwall +ENCODING 128 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR hwall +ENCODING 129 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ff00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR tlcorn +ENCODING 130 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1f00 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR trcorn +ENCODING 131 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +f800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR blcorn +ENCODING 132 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1f00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR brcorn +ENCODING 133 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +f800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR crwall +ENCODING 134 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ff00 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR tuwall +ENCODING 135 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ff00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR tdwall +ENCODING 136 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ff00 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR tlwall +ENCODING 137 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +18 +18 +18 +18 +18 +18 +18 +f8 +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR trwall +ENCODING 138 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +18 +18 +18 +18 +18 +18 +18 +1f +18 +18 +18 +18 +18 +18 +ENDCHAR +STARTCHAR ndoor +ENCODING 139 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1800 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vodoor +ENCODING 140 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +0000 +0000 +0000 +7e00 +0000 +0000 +0000 +1800 +1800 +1800 +ENDCHAR +STARTCHAR hodoor +ENCODING 141 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1800 +1800 +1800 +1800 +9900 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vcdoor +ENCODING 142 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1800 +1800 +1800 +7e00 +7e00 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR hcdoor +ENCODING 143 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1800 +1800 +1800 +7e00 +7e00 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR room +ENCODING 144 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1800 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dark corr +ENCODING 145 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1100 +4400 +1100 +4400 +1100 +4400 +1100 +4400 +1100 +4400 +1100 +4400 +1100 +4400 +ENDCHAR +STARTCHAR lit corr +ENCODING 146 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +5500 +aa00 +5500 +aa00 +5500 +aa00 +5500 +aa00 +5500 +aa00 +5500 +aa00 +5500 +aa00 +ENDCHAR +STARTCHAR upstair +ENCODING 147 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1800 +3000 +6000 +c000 +6000 +3000 +1800 +0c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dnstair +ENCODING 148 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6000 +3000 +1800 +0c00 +0600 +0c00 +1800 +3000 +6000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR trap +ENCODING 149 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1000 +3800 +6c00 +c600 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR web +ENCODING 150 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0100 +0f00 +3c00 +c600 +c200 +6f00 +3a00 +3200 +7300 +5700 +8c00 +db00 +7f00 +2500 +ENDCHAR +STARTCHAR pool +ENCODING 151 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +7700 +0000 +ee00 +bb00 +0000 +7700 +dd00 +0000 +bb00 +ee00 +0000 +dd00 +7700 +0000 +ENDCHAR +STARTCHAR fountain +ENCODING 152 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1400 +0000 +3400 +4a00 +4900 +8a00 +2800 +0200 +5900 +1800 +3c00 +3c00 +ff00 +ff00 +ENDCHAR +STARTCHAR sink +ENCODING 153 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +2400 +2400 +7e00 +2400 +2400 +7e00 +2400 +2400 +0000 +0000 +0000 +ENDCHAR +STARTCHAR throne +ENCODING 154 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0400 +0400 +0400 +0400 +3c00 +1400 +3c00 +2400 +2400 +ff00 +ff00 +ENDCHAR +STARTCHAR upladder +ENCODING 155 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1800 +3000 +6000 +c000 +6000 +3000 +1800 +0c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dnladder +ENCODING 156 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +6000 +3000 +1800 +0c00 +0600 +0c00 +1800 +3000 +6000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dbvwall +ENCODING 157 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +3c00 +7e00 +7e00 +3c00 +3c00 +3c00 +3c00 +3c00 +3c00 +7e00 +7e00 +3c00 +1800 +ENDCHAR +STARTCHAR dbhwall +ENCODING 158 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +2400 +7e00 +ff00 +7e00 +2400 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ice +ENCODING 159 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +2400 +1800 +1800 +2400 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR lava +ENCODING 160 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0200 +4000 +2000 +2000 +0400 +1000 +9500 +5500 +5500 +5e00 +ff00 +ff00 +ENDCHAR +STARTCHAR vbeam +ENCODING 161 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1000 +1c00 +0800 +1000 +2000 +4000 +3000 +1800 +0400 +0800 +1000 +2000 +7000 +1000 +ENDCHAR +STARTCHAR hbeam +ENCODING 162 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +4000 +6000 +d100 +0a00 +0400 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR lslant +ENCODING 163 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +8000 +6000 +2000 +2000 +2000 +1000 +1000 +0800 +0c00 +0200 +0200 +0200 +0200 +0100 +ENDCHAR +STARTCHAR rslant +ENCODING 164 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0100 +0100 +0200 +0200 +0400 +0400 +0400 +1800 +2000 +2000 +4000 +4000 +4000 +8000 +ENDCHAR +STARTCHAR dig beam +ENCODING 165 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +5500 +0a00 +1400 +2800 +5000 +a000 +4100 +8200 +0500 +0a00 +1400 +2800 +5000 +aa00 +ENDCHAR +STARTCHAR camera flash +ENCODING 166 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c800 +4b00 +0200 +2000 +0400 +d800 +1b00 +2000 +4400 +5200 +0a00 +0000 +ENDCHAR +STARTCHAR boom open left +ENCODING 167 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +2000 +1000 +0800 +0400 +0200 +0400 +0800 +1000 +2000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR boom open right +ENCODING 168 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0200 +0400 +0800 +1000 +2000 +1000 +0800 +0400 +0200 +0000 +0000 +0000 +ENDCHAR +STARTCHAR magic shield 1 +ENCODING 169 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +5000 +1200 +4200 +0800 +1000 +3a00 +1000 +4400 +5000 +2c00 +0400 +2800 +ENDCHAR +STARTCHAR magic shield 2 +ENCODING 170 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +3600 +a800 +5a00 +da00 +8400 +f400 +d500 +3a00 +d700 +a500 +fd00 +6f00 +3800 +3600 +ENDCHAR +STARTCHAR magic sheild 3 +ENCODING 171 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +1c00 +4300 +0000 +0800 +2200 +0000 +4000 +0800 +0200 +9a00 +cc00 +2200 +0000 +ENDCHAR +STARTCHAR magic shield 4 +ENCODING 172 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +6000 +1000 +6c00 +5200 +4000 +1b00 +4600 +6b00 +cd00 +a200 +a800 +5600 +3400 +0000 +ENDCHAR +STARTCHAR sw top left +ENCODING 173 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0300 +0400 +0400 +0800 +0800 +1000 +1000 +ENDCHAR +STARTCHAR sw top center +ENCODING 174 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +3c00 +c300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR sw top right +ENCODING 175 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +c000 +2000 +2000 +1000 +1000 +0800 +0800 +ENDCHAR +STARTCHAR sw middle left +ENCODING 176 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1000 +1000 +2000 +2000 +2000 +2000 +2000 +4000 +2000 +2000 +2000 +2000 +1000 +1000 +ENDCHAR +STARTCHAR sw middle right +ENCODING 177 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0800 +0800 +0400 +0400 +0400 +0400 +0400 +0200 +0400 +0400 +0400 +0400 +0800 +0800 +ENDCHAR +STARTCHAR sw bottom left +ENCODING 178 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1000 +1000 +0800 +0800 +0400 +0400 +0300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR sw bottom center +ENCODING 179 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +c300 +3c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR sw bottom right +ENCODING 180 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0800 +0800 +1000 +1000 +2000 +2000 +c000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR explosion1 +ENCODING 181 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0100 +0e00 +3100 +ce00 +3100 +4600 +9800 +6700 +9800 +2300 +4c00 +9300 +2400 +ENDCHAR +STARTCHAR explosion2 +ENCODING 182 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +ff00 +0000 +ff00 +0000 +ff00 +0000 +fe00 +0100 +fe00 +0100 +fe00 +0100 +7c00 +ENDCHAR +STARTCHAR explosion3 +ENCODING 183 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +e000 +1800 +e600 +1900 +c400 +3300 +cd00 +3200 +8900 +6400 +9200 +4900 +ENDCHAR +STARTCHAR explosion4 +ENCODING 184 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +4900 +5600 +a500 +aa00 +4a00 +5500 +5500 +5500 +5500 +5500 +4a00 +aa00 +a500 +5600 +ENDCHAR +STARTCHAR explosion5 +ENCODING 185 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +8300 +7c00 +8300 +7c00 +8200 +3900 +4500 +5500 +4500 +3900 +8200 +7c00 +8300 +7c00 +ENDCHAR +STARTCHAR explosion6 +ENCODING 186 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +2500 +d400 +4a00 +aa00 +a500 +5500 +5500 +5500 +5500 +5500 +a500 +aa00 +4a00 +d400 +ENDCHAR +STARTCHAR explosion7 +ENCODING 187 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +4900 +2400 +9300 +4c00 +2300 +9800 +6700 +b800 +4e00 +3100 +ce00 +3100 +0e00 +0100 +ENDCHAR +STARTCHAR explosion8 +ENCODING 188 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +8300 +7c00 +0100 +fe00 +0100 +fe00 +0100 +fe00 +0000 +ff00 +0000 +ff00 +0000 +ff00 +ENDCHAR +STARTCHAR explosion9 +ENCODING 189 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +2500 +4900 +9200 +6400 +8900 +3200 +cc00 +3b00 +e400 +1900 +e600 +1800 +e000 +0000 +ENDCHAR +STARTCHAR C276 +ENCODING 190 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +f800 +1800 +f800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C203 +ENCODING 191 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3000 +7800 +cc00 +0000 +7800 +0c00 +7c00 +cc00 +dc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C204 +ENCODING 192 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +cc00 +cc00 +0000 +7800 +0c00 +7c00 +cc00 +dc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C207 +ENCODING 193 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +7c00 +c600 +c000 +c000 +c600 +7c00 +1800 +6c00 +3800 +0000 +ENDCHAR +STARTCHAR C210 +ENCODING 194 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3000 +7800 +cc00 +0000 +7c00 +c600 +fe00 +c000 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C303 +ENCODING 195 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1f00 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR C201 +ENCODING 196 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +c600 +c600 +0000 +c600 +c600 +c600 +c600 +ce00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C206 +ENCODING 197 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3800 +6c00 +3800 +0000 +7800 +0c00 +7c00 +cc00 +dc00 +7600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C200 +ENCODING 198 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +6600 +c000 +c000 +c000 +c600 +6600 +3c00 +1800 +cc00 +3800 +0000 +ENDCHAR +STARTCHAR C264 +ENCODING 199 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +f800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR C333 +ENCODING 200 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ENDCHAR +STARTCHAR C334 +ENCODING 201 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ENDCHAR +STARTCHAR C335 +ENCODING 202 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +f000 +ENDCHAR +STARTCHAR C336 +ENCODING 203 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +0f00 +ENDCHAR +STARTCHAR C337 +ENCODING 204 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +ff00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C272 +ENCODING 205 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR +STARTCHAR C273 +ENCODING 206 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +fe00 +0600 +f600 +3600 +3600 +3600 +3600 +3600 +3600 +ENDCHAR +STARTCHAR C236 +ENCODING 207 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +fc00 +c600 +fc00 +c000 +cc00 +de00 +cc00 +cc00 +cc00 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR amulet +ENCODING 208 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3c00 +4200 +4200 +4200 +2200 +2200 +1c00 +3e00 +3e00 +3e00 +1c00 +0000 +0000 +ENDCHAR +STARTCHAR food +ENCODING 209 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +7c00 +7c00 +3800 +1000 +1000 +1000 +7c00 +0000 +ENDCHAR +STARTCHAR weapon +ENCODING 210 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +2000 +3000 +2800 +2800 +2800 +2800 +2800 +2800 +3800 +fe00 +3000 +3000 +3000 +3000 +ENDCHAR +STARTCHAR tool +ENCODING 211 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +3c00 +4200 +4200 +7e00 +7e00 +7e00 +7e00 +0000 +0000 +ENDCHAR +STARTCHAR ball +ENCODING 212 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1c00 +3e00 +3e00 +7e00 +ad00 +7600 +ENDCHAR +STARTCHAR chain +ENCODING 213 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6600 +9900 +6600 +ENDCHAR +STARTCHAR rock +ENCODING 214 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3c00 +6600 +5200 +8500 +d200 +a900 +a000 +4500 +9300 +bf00 +6a00 +fe00 +7f00 +ENDCHAR +STARTCHAR armor +ENCODING 215 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7c00 +6c00 +5400 +6c00 +5400 +2800 +1000 +0000 +0000 +ENDCHAR +STARTCHAR potion +ENCODING 216 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3c00 +1800 +1800 +1800 +1800 +3c00 +6e00 +7a00 +5e00 +7600 +3c00 +0000 +ENDCHAR +STARTCHAR scroll +ENCODING 217 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7e00 +ff00 +7e00 +2a00 +5400 +2a00 +5400 +2a00 +7e00 +ff00 +7e00 +0000 +ENDCHAR +STARTCHAR wand +ENCODING 218 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +2200 +2400 +1500 +0200 +0800 +0800 +1000 +1000 +1000 +2000 +2000 +2000 +4000 +0000 +ENDCHAR +STARTCHAR ring +ENCODING 219 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +4200 +2400 +8300 +3800 +4500 +4400 +4400 +3900 +8200 +2900 +4800 +0000 +0000 +ENDCHAR +STARTCHAR gem +ENCODING 220 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7c00 +c600 +4400 +3800 +1000 +0000 +ENDCHAR +STARTCHAR gold +ENCODING 221 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +5400 +5400 +a500 +a900 +8a00 +0000 +6000 +be00 +fb00 +df00 +3800 +ENDCHAR +STARTCHAR venom +ENCODING 222 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1800 +2400 +4400 +5800 +2000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR spbook +ENCODING 223 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +7c00 +4600 +6600 +4600 +4600 +4600 +6600 +4600 +7e00 +7e00 +0000 +ENDCHAR +STARTCHAR C262 +ENCODING 224 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +dd00 +7700 +dd00 +7700 +dd00 +7700 +dd00 +7700 +dd00 +7700 +dd00 +7700 +dd00 +7700 +ENDCHAR +STARTCHAR C341 +ENCODING 225 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +7800 +cc00 +d800 +fc00 +c600 +e600 +dc00 +c000 +c000 +0000 +ENDCHAR +STARTCHAR C342 +ENCODING 226 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +6600 +6200 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C343 +ENCODING 227 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +fe00 +6c00 +6c00 +6c00 +6c00 +6c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C344 +ENCODING 228 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +c600 +6200 +3000 +1800 +3000 +6200 +c600 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C345 +ENCODING 229 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7e00 +d800 +cc00 +cc00 +cc00 +7800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C346 +ENCODING 230 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +6600 +6600 +6600 +6600 +7c00 +6000 +c000 +8000 +0000 +0000 +ENDCHAR +STARTCHAR C347 +ENCODING 231 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7600 +dc00 +1800 +1800 +1800 +1800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C350 +ENCODING 232 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +fe00 +3800 +6c00 +c600 +c600 +c600 +6c00 +3800 +fe00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C351 +ENCODING 233 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3800 +6c00 +c600 +c600 +fe00 +c600 +c600 +6c00 +3800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C352 +ENCODING 234 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3800 +6c00 +c600 +c600 +c600 +6c00 +6c00 +6c00 +ee00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C353 +ENCODING 235 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3e00 +6000 +3000 +3c00 +6600 +c600 +c600 +cc00 +7800 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C354 +ENCODING 236 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +7e00 +db00 +db00 +7e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C355 +ENCODING 237 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0600 +0c00 +7c00 +de00 +f600 +e600 +7c00 +6000 +c000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C356 +ENCODING 238 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +1c00 +3000 +6000 +6000 +7c00 +6000 +6000 +3000 +1c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C357 +ENCODING 239 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7c00 +c600 +c600 +c600 +c600 +c600 +c600 +c600 +c600 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C360 +ENCODING 240 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +fe00 +0000 +fe00 +0000 +fe00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C361 +ENCODING 241 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +1800 +1800 +7e00 +1800 +1800 +0000 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C362 +ENCODING 242 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0600 +0c00 +1800 +3000 +0000 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C363 +ENCODING 243 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0c00 +1800 +3000 +6000 +3000 +1800 +0c00 +0000 +7e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C364 +ENCODING 244 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0c00 +1e00 +1a00 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +ENDCHAR +STARTCHAR C365 +ENCODING 245 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +1800 +5800 +7800 +3000 +0000 +0000 +ENDCHAR +STARTCHAR C366 +ENCODING 246 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1800 +1800 +0000 +7e00 +0000 +1800 +1800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C225 +ENCODING 247 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +3000 +1800 +0c00 +0000 +7c00 +c600 +c600 +c600 +c600 +7c00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C370 +ENCODING 248 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +7800 +cc00 +cc00 +7800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C371 +ENCODING 249 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1800 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C372 +ENCODING 250 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C373 +ENCODING 251 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +1f00 +1800 +1800 +1800 +1800 +d800 +7800 +3800 +1800 +0000 +0000 +ENDCHAR +STARTCHAR C374 +ENCODING 252 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +d800 +6c00 +6c00 +6c00 +6c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C375 +ENCODING 253 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +7000 +d800 +3000 +6000 +f800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C376 +ENCODING 254 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +7e00 +7e00 +7e00 +7e00 +7e00 +7e00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C377 +ENCODING 255 +SWIDTH 666 0 +DWIDTH 8 0 +BBX 8 14 0 -3 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +ENDFONT diff --git a/win/X11/nethack.rc b/win/X11/nethack.rc new file mode 100644 index 0000000..529ed40 --- /dev/null +++ b/win/X11/nethack.rc @@ -0,0 +1,79 @@ +# +# Nethack configuration file. +# +# Naming this file $(HOME)/.nethackrc (for UNIX) or setting the environment +# variable NETHACKOPTIONS to point to its full path name elsewhere tells +# NetHack to use X11 windowing and fonts (provided the executable was +# compiled with that ability). +# +# +OPTIONS=windowtype:x11,toptenwin,hilite_pet +OPTIONS=confirm,male,fixinv,noautopickup,safe_pet,sortpack,tombstone +OPTIONS=verbose,news,fruit:pineapple +OPTIONS=dogname:Dhairrhuwyth +OPTIONS=catname:Ghisteslwchlohm +# +# There are 17 object symbols and various graphics symbols. +# The descriptions of these symbols can be found in dat/opthelp. +# +# +# Font: nh10 (10x20) +# +OBJECTS= 180 183 188 192 181 184 182 189 190 196 \ + 191 194 193 187 185 186 195 +# +DUNGEON= 032 025 018 013 012 014 011 015 023 024 \ + 022 021 128 129 130 131 132 035 035 133 \ + 134 135 136 137 145 146 144 124 143 142 \ + 141 140 149 150 031 031 147 148 031 161 \ + 140 +# +TRAPS= 138 138 138 138 138 138 138 138 138 138 \ + 138 138 138 138 138 139 138 138 138 138 \ + 138 138 +# +EFFECTS= 151 152 153 154 155 156 157 158 \ + 159 160 161 162 \ + 163 164 165 166 167 168 169 170 \ + 171 172 173 174 175 176 177 178 179 +# +# +# Font: ibm (8x14) +# +#OBJECTS= 207 210 215 219 208 211 209 216 217 223 \ +# 218 221 220 214 212 213 222 +# +#DUNGEON= 032 128 129 130 131 132 133 134 135 136 \ +# 137 138 139 045 124 142 143 035 035 144 \ +# 145 146 147 148 155 156 227 124 154 153 \ +# 152 151 159 160 200 200 157 158 250 170 \ +# 151 +# +#TRAPS= 149 149 149 149 149 149 149 149 149 149 \ +# 149 149 149 149 149 150 149 149 149 149 \ +# 149 149 +# +#EFFECTS= 161 162 163 164 165 166 167 168 \ +# 169 170 171 172 \ +# 173 174 175 176 177 178 179 180 \ +# 181 182 183 184 185 186 187 188 189 +# +# +# Font: a "standard" font like 6x13 +# Note that this version is unlikely to work on a tty on a Unix system because +# many of these characters are also control characters. +# +#DUNGEON = 032 025 018 013 012 014 011 015 023 024 \ +# 022 021 031 045 124 043 043 035 035 031 \ +# 035 001 060 062 060 062 019 124 092 035 \ +# 123 125 031 125 046 046 035 035 046 127 \ +# 125 +# +#TRAPS= 094 094 094 094 094 094 094 094 094 094 \ +# 094 094 094 094 094 002 094 094 094 094 \ +# 094 094 +# +#EFFECTS= 124 045 092 047 042 033 041 040 \ +# 048 035 064 042 \ +# 047 045 092 124 124 092 045 047 \ +# 047 064 092 064 064 064 092 064 047 diff --git a/win/X11/nh10.bdf b/win/X11/nh10.bdf new file mode 100644 index 0000000..200ff99 --- /dev/null +++ b/win/X11/nh10.bdf @@ -0,0 +1,6940 @@ +STARTFONT 2.1 +COMMENT Nethack 10x20 font. Based on the font 10x20. +FONT nh10 +SIZE 20 75 75 +FONTBOUNDINGBOX 10 20 0 -5 +STARTPROPERTIES 19 +FONTNAME_REGISTRY "" +FOUNDRY "Misc" +FAMILY_NAME "Fixed" +WEIGHT_NAME "Medium" +SLANT "R" +SETWIDTH_NAME "Normal" +ADD_STYLE_NAME "NetHack" +PIXEL_SIZE 20 +POINT_SIZE 200 +RESOLUTION_X 75 +RESOLUTION_Y 75 +SPACING "C" +AVERAGE_WIDTH 100 +CHARSET_REGISTRY "ISO8859" +CHARSET_ENCODING "1" +DEFAULT_CHAR 0 +FONT_DESCENT 5 +FONT_ASCENT 15 +COPYRIGHT "Copyright 1989-1991 Network Computing Devices, Inc." +ENDPROPERTIES +CHARS 256 +STARTCHAR C000 +ENCODING 0 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C001 +ENCODING 1 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0c00 +1e00 +3f00 +7f80 +7f80 +3f00 +1e00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C002 +ENCODING 2 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +ENDCHAR +STARTCHAR C003 +ENCODING 3 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +6600 +6600 +6600 +7e00 +6600 +6600 +6600 +0000 +1f80 +0600 +0600 +0600 +0600 +0600 +0000 +0000 +ENDCHAR +STARTCHAR C004 +ENCODING 4 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +7c00 +6000 +6000 +7800 +6000 +6000 +6000 +0f80 +0c00 +0c00 +0f00 +0c00 +0c00 +0c00 +0000 +0000 +ENDCHAR +STARTCHAR C005 +ENCODING 5 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +3c00 +6600 +6000 +6000 +6600 +3c00 +0000 +1f00 +1980 +1980 +1f00 +1e00 +1b00 +1980 +0000 +0000 +ENDCHAR +STARTCHAR C006 +ENCODING 6 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +3000 +3000 +3000 +3000 +3000 +3e00 +0000 +0f80 +0c00 +0c00 +0f00 +0c00 +0c00 +0c00 +0000 +0000 +ENDCHAR +STARTCHAR C007 +ENCODING 7 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +3300 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C010 +ENCODING 8 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +7f80 +0c00 +0c00 +0000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C011 +ENCODING 9 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +6600 +7600 +7e00 +7e00 +6e00 +6600 +0000 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0f80 +0000 +0000 +ENDCHAR +STARTCHAR C012 +ENCODING 10 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +6600 +6600 +6600 +3c00 +3c00 +1800 +1800 +0000 +1f80 +0600 +0600 +0600 +0600 +0600 +0000 +0000 +ENDCHAR +STARTCHAR C013 +ENCODING 11 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +fc00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C014 +ENCODING 12 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +fc00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C015 +ENCODING 13 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0fc0 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C016 +ENCODING 14 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0fc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C017 +ENCODING 15 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ffc0 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C020 +ENCODING 16 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +ffc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C021 +ENCODING 17 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +ffc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C022 +ENCODING 18 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C023 +ENCODING 19 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C024 +ENCODING 20 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffc0 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C025 +ENCODING 21 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0fc0 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C026 +ENCODING 22 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +fc00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C027 +ENCODING 23 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ffc0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C030 +ENCODING 24 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ffc0 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C031 +ENCODING 25 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR C032 +ENCODING 26 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +01c0 +0700 +1c00 +7000 +1c00 +0700 +01c0 +0000 +0000 +7fc0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C033 +ENCODING 27 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +7000 +1c00 +0700 +01c0 +0700 +1c00 +7000 +0000 +0000 +7fc0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C034 +ENCODING 28 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0080 +3f80 +5b00 +1b00 +1b00 +1b00 +1b00 +3300 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C035 +ENCODING 29 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0180 +0300 +7fc0 +0600 +0c00 +7fc0 +1800 +3000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C036 +ENCODING 30 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0f00 +1980 +1980 +1800 +1800 +1800 +7e00 +1800 +1800 +1800 +7c00 +56c0 +7380 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C037 +ENCODING 31 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR space +ENCODING 32 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ! +ENCODING 33 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR " +ENCODING 34 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +3300 +1200 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR # +ENCODING 35 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0d80 +0d80 +0d80 +3fc0 +1b00 +1b00 +1b00 +7f80 +3600 +3600 +3600 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR $ +ENCODING 36 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +3f00 +6d80 +6c00 +6c00 +6c00 +3f00 +0d80 +0d80 +0d80 +6d80 +3f00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR % +ENCODING 37 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +3980 +6d80 +6f00 +3b00 +0600 +0600 +0c00 +0c00 +1b80 +1ec0 +36c0 +3380 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR & +ENCODING 38 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1c00 +3600 +3600 +3600 +3c00 +1800 +3800 +6c00 +66c0 +6380 +6300 +7780 +3cc0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ' +ENCODING 39 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0f00 +0e00 +1800 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ( +ENCODING 40 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0300 +0600 +0c00 +0c00 +1800 +1800 +1800 +1800 +1800 +0c00 +0c00 +0600 +0300 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ) +ENCODING 41 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0c00 +0600 +0600 +0600 +0600 +0600 +0c00 +0c00 +1800 +3000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR * +ENCODING 42 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +3300 +3300 +1e00 +7f80 +1e00 +3300 +3300 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR + +ENCODING 43 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +0c00 +7f80 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR , +ENCODING 44 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0e00 +0e00 +1800 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR - +ENCODING 45 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7f80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR . +ENCODING 46 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0e00 +0e00 +0e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR / +ENCODING 47 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0180 +0180 +0300 +0300 +0600 +0600 +0c00 +0c00 +1800 +1800 +3000 +3000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 0 +ENCODING 48 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +3300 +1e00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 1 +ENCODING 49 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1c00 +3c00 +6c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 2 +ENCODING 50 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +0180 +0180 +0300 +0e00 +1800 +3000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 3 +ENCODING 51 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +0180 +0300 +0e00 +0300 +0180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 4 +ENCODING 52 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0100 +0300 +0700 +0f00 +1b00 +3300 +6300 +6300 +7f80 +0300 +0300 +0300 +0300 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 5 +ENCODING 53 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +6000 +6000 +6000 +6000 +6e00 +7300 +0180 +0180 +0180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 6 +ENCODING 54 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6100 +6000 +6000 +6e00 +7300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 7 +ENCODING 55 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +0180 +0180 +0300 +0300 +0600 +0600 +0c00 +0c00 +1800 +1800 +3000 +3000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 8 +ENCODING 56 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +3300 +1e00 +3300 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR 9 +ENCODING 57 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3380 +1d80 +0180 +0180 +2180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR : +ENCODING 58 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0e00 +0e00 +0000 +0000 +0000 +0000 +0e00 +0e00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ; +ENCODING 59 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0e00 +0e00 +0000 +0000 +0000 +0000 +0e00 +0e00 +1c00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR < +ENCODING 60 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0100 +0300 +0600 +0c00 +1800 +3000 +6000 +3000 +1800 +0c00 +0600 +0300 +0100 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR = +ENCODING 61 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +7f80 +0000 +0000 +0000 +0000 +7f80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR > +ENCODING 62 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +2000 +3000 +1800 +0c00 +0600 +0300 +0180 +0300 +0600 +0c00 +1800 +3000 +2000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ? +ENCODING 63 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +0300 +0600 +0c00 +0c00 +0c00 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR @ +ENCODING 64 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6780 +6f80 +6d80 +6d80 +6d80 +6f00 +6600 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR A +ENCODING 65 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +3300 +6180 +6180 +6180 +7f80 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR B +ENCODING 66 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7c00 +6600 +6300 +6300 +6300 +6600 +7e00 +6300 +6180 +6180 +6180 +6300 +7e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C +ENCODING 67 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR D +ENCODING 68 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7e00 +6300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6300 +7e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR E +ENCODING 69 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +6000 +6000 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR F +ENCODING 70 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +6000 +6000 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR G +ENCODING 71 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6000 +6000 +6000 +6780 +6180 +6180 +6180 +6180 +3380 +1e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR H +ENCODING 72 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +7f80 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR I +ENCODING 73 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR J +ENCODING 74 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0f80 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR K +ENCODING 75 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +6300 +6300 +6600 +6600 +7c00 +6600 +6600 +6300 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR L +ENCODING 76 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR M +ENCODING 77 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +7380 +7380 +7f80 +6d80 +6d80 +6d80 +6d80 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR N +ENCODING 78 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +7180 +7180 +7980 +7980 +6d80 +6d80 +6780 +6780 +6380 +6380 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR O +ENCODING 79 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR P +ENCODING 80 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7e00 +6300 +6180 +6180 +6180 +6180 +6300 +7e00 +6000 +6000 +6000 +6000 +6000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Q +ENCODING 81 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6d80 +6780 +3300 +1f00 +0180 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR R +ENCODING 82 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7e00 +6300 +6180 +6180 +6180 +6180 +6300 +7e00 +6600 +6300 +6300 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR S +ENCODING 83 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6000 +6000 +3000 +1e00 +0300 +0180 +0180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR T +ENCODING 84 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR U +ENCODING 85 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR V +ENCODING 86 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +6180 +6180 +3300 +3300 +3300 +1e00 +1e00 +1e00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR W +ENCODING 87 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6d80 +6d80 +6d80 +6d80 +7380 +7380 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR X +ENCODING 88 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +3300 +3300 +1e00 +1e00 +0c00 +1e00 +1e00 +3300 +3300 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Y +ENCODING 89 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6180 +6180 +3300 +3300 +1e00 +1e00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Z +ENCODING 90 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7f80 +0180 +0180 +0300 +0600 +0600 +0c00 +1800 +1800 +3000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR [ +ENCODING 91 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3f00 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3000 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR \ +ENCODING 92 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +3000 +3000 +1800 +1800 +0c00 +0c00 +0600 +0600 +0300 +0300 +0180 +0180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ] +ENCODING 93 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3f00 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +0300 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ^ +ENCODING 94 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +6180 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR _ +ENCODING 95 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7fc0 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ` +ENCODING 96 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3c00 +1c00 +0600 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR a +ENCODING 97 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1f00 +3180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR b +ENCODING 98 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6e00 +7300 +6180 +6180 +6180 +6180 +7300 +6e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR c +ENCODING 99 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1f00 +3180 +6000 +6000 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR d +ENCODING 100 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0180 +0180 +0180 +0180 +0180 +1d80 +3380 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR e +ENCODING 101 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +3300 +6180 +7f80 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR f +ENCODING 102 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0f00 +1980 +1980 +1800 +1800 +1800 +1800 +7e00 +1800 +1800 +1800 +1800 +1800 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR g +ENCODING 103 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3e80 +6380 +6300 +6300 +6300 +3e00 +6000 +3f00 +6180 +6180 +6180 +3f00 +0000 +ENDCHAR +STARTCHAR h +ENCODING 104 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6e00 +7300 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR i +ENCODING 105 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR j +ENCODING 106 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0180 +0180 +0000 +0780 +0180 +0180 +0180 +0180 +0180 +0180 +0180 +3180 +3180 +3180 +1f00 +0000 +ENDCHAR +STARTCHAR k +ENCODING 107 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +6000 +6000 +6000 +6000 +6000 +6300 +6600 +6c00 +7800 +7c00 +6600 +6300 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR l +ENCODING 108 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR m +ENCODING 109 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +5b00 +7f80 +6d80 +6d80 +6d80 +6d80 +6d80 +6d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR n +ENCODING 110 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6e00 +7300 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR o +ENCODING 111 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR p +ENCODING 112 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6e00 +7300 +6180 +6180 +6180 +6180 +7300 +6e00 +6000 +6000 +6000 +6000 +0000 +ENDCHAR +STARTCHAR q +ENCODING 113 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1d80 +3380 +6180 +6180 +6180 +6180 +3380 +1d80 +0180 +0180 +0180 +0180 +0000 +ENDCHAR +STARTCHAR r +ENCODING 114 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6f00 +3980 +3000 +3000 +3000 +3000 +3000 +3000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR s +ENCODING 115 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3f00 +6180 +6000 +3f00 +0180 +0180 +6180 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR t +ENCODING 116 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +1800 +1800 +1800 +7e00 +1800 +1800 +1800 +1800 +1800 +1980 +0f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR u +ENCODING 117 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR v +ENCODING 118 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +3300 +3300 +1e00 +1e00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR w +ENCODING 119 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6d80 +6d80 +6d80 +7f80 +3300 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR x +ENCODING 120 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +3300 +1e00 +0c00 +0c00 +1e00 +3300 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR y +ENCODING 121 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0180 +6180 +3300 +1e00 +0000 +ENDCHAR +STARTCHAR z +ENCODING 122 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3f80 +0180 +0300 +0600 +0c00 +1800 +3000 +3f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR { +ENCODING 123 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0780 +0c00 +0c00 +0c00 +0c00 +0c00 +7800 +0c00 +0c00 +0c00 +0c00 +0c00 +0780 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR bar +ENCODING 124 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR } +ENCODING 125 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7800 +0c00 +0c00 +0c00 +0c00 +0c00 +0780 +0c00 +0c00 +0c00 +0c00 +0c00 +7800 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ~ +ENCODING 126 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3980 +6d80 +6700 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR C177 +ENCODING 127 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR doorway +ENCODING 128 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +1200 +1200 +1e00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vodoor +ENCODING 129 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +7f80 +7f80 +0000 +0000 +0000 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR codoor +ENCODING 130 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +0c00 +8c40 +8c40 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR vcdoor +ENCODING 131 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +3300 +2100 +2100 +2100 +2100 +2100 +2100 +3300 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR hcdoor +ENCODING 132 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7f80 +4080 +8040 +8040 +4080 +7f80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR room +ENCODING 133 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dark corridor +ENCODING 134 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +4440 +1100 +ENDCHAR +STARTCHAR lit corridor +ENCODING 135 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +5540 +aa80 +ENDCHAR +STARTCHAR upstair +ENCODING 136 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0100 +0200 +0400 +0800 +1000 +2000 +1000 +0800 +0400 +0200 +0100 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR dnstair +ENCODING 137 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +2000 +1000 +0800 +0400 +0200 +0100 +0200 +0400 +0800 +1000 +2000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR trap +ENCODING 138 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +ffc0 +c0c0 +e1c0 +b340 +9e40 +8c40 +9e40 +b340 +e1c0 +c0c0 +ffc0 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR web +ENCODING 139 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +4980 +f900 +4f00 +c9c0 +9940 +9300 +f240 +9e40 +9340 +32c0 +a640 +fcc0 +2f80 +2580 +e4c0 +ec80 +7f80 +4bc0 +4940 +c900 +ENDCHAR +STARTCHAR pool +ENCODING 140 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +b6c0 +0000 +2480 +db40 +0000 +9240 +6d80 +0000 +4900 +b6c0 +0000 +2480 +db40 +0000 +9240 +6d80 +0000 +4900 +b6c0 +0000 +ENDCHAR +STARTCHAR fountain +ENCODING 141 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +1000 +0a00 +4200 +0c00 +3000 +0280 +6800 +0100 +4800 +2900 +4800 +0900 +9c00 +1400 +ff80 +ff80 +ff80 +ENDCHAR +STARTCHAR sink +ENCODING 142 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2100 +2100 +2100 +2100 +1e00 +1e00 +1200 +ENDCHAR +STARTCHAR throne +ENCODING 143 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0100 +0100 +0100 +0100 +0100 +1f00 +0900 +0900 +3f00 +2100 +2100 +ffc0 +ENDCHAR +STARTCHAR altar +ENCODING 144 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +7f80 +7f80 +3300 +3300 +3300 +3300 +ENDCHAR +STARTCHAR up ladder +ENCODING 145 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +9000 +f000 +9000 +9000 +f000 +9100 +9380 +f540 +9100 +9100 +f100 +9100 +9100 +f000 +9000 +9000 +f000 +9000 +9000 +f000 +ENDCHAR +STARTCHAR down ladder +ENCODING 146 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +9000 +f000 +9000 +9000 +f000 +9100 +9100 +f100 +9100 +9100 +f540 +9380 +9100 +f000 +9000 +9000 +f000 +9000 +9000 +f000 +ENDCHAR +STARTCHAR dbvwall +ENCODING 147 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0c00 +0c00 +0c00 +0c00 +0c00 +3f00 +3f00 +3f00 +3f00 +3f00 +3f00 +3f00 +3f00 +3f00 +3f00 +0c00 +0c00 +0c00 +0c00 +0c00 +ENDCHAR +STARTCHAR dbhwall +ENCODING 148 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3f00 +3f00 +ffc0 +ffc0 +3f00 +3f00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ice +ENCODING 149 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1200 +0c00 +0c00 +1200 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR lava +ENCODING 150 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +8800 +0480 +0080 +3700 +1300 +9900 +0940 +4140 +0840 +2900 +8900 +a840 +4540 +ffc0 +ffc0 +ffc0 +ENDCHAR +STARTCHAR vbeam +ENCODING 151 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0800 +0800 +0e00 +0400 +0800 +1000 +2000 +1000 +0800 +0800 +0800 +0800 +0400 +0200 +0400 +0800 +1000 +3000 +0800 +0800 +ENDCHAR +STARTCHAR hbeam +ENCODING 152 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2000 +3000 +e8c0 +0500 +0200 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR lslant +ENCODING 153 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +8000 +4000 +4000 +4000 +3000 +0800 +0800 +0800 +0800 +0800 +0800 +0400 +0400 +0200 +0200 +0200 +0200 +0200 +0180 +0040 +ENDCHAR +STARTCHAR rslant +ENCODING 154 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0040 +0080 +0080 +0080 +0080 +0080 +0300 +0400 +0400 +0400 +0800 +0800 +1000 +2000 +4000 +4000 +4000 +4000 +4000 +8000 +ENDCHAR +STARTCHAR dig beam +ENCODING 155 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +8040 +4080 +2100 +1200 +0c00 +0c00 +1200 +2100 +4080 +8040 +8040 +4080 +2100 +1200 +0c00 +0c00 +1200 +2100 +4080 +8040 +ENDCHAR +STARTCHAR camera flash +ENCODING 156 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +4200 +0600 +6400 +0100 +0940 +4840 +0400 +2100 +1e00 +5ec0 +1e80 +1e00 +2500 +5440 +2000 +0d80 +2840 +c000 +9280 +0040 +ENDCHAR +STARTCHAR boomerang left +ENCODING 157 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +3000 +2800 +1400 +0a00 +0500 +0300 +0500 +0a00 +1400 +2800 +3000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR boomerang right +ENCODING 158 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0300 +0500 +0a00 +1400 +2800 +3000 +2800 +1400 +0a00 +0500 +0300 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR shield 1 +ENCODING 159 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0200 +76c0 +fa40 +8a00 +4680 +f900 +ea00 +e7c0 +ddc0 +5480 +3100 +b300 +e8c0 +4cc0 +6c00 +6300 +4480 +a180 +1400 +0780 +ENDCHAR +STARTCHAR shield 2 +ENCODING 160 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +6100 +b340 +0040 +08c0 +77c0 +d240 +b980 +8380 +a100 +2700 +4c40 +31c0 +bf40 +c200 +ca00 +3380 +4180 +1b80 +0780 +ENDCHAR +STARTCHAR shield 3 +ENCODING 161 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +d000 +0600 +5e80 +4e80 +4c80 +2000 +2800 +3e80 +5f80 +e640 +5080 +47c0 +8380 +c880 +a940 +9840 +0dc0 +1300 +6080 +3200 +ENDCHAR +STARTCHAR shield 4 +ENCODING 162 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +4200 +d480 +95c0 +1c40 +3c80 +9b80 +2d80 +5280 +5900 +5400 +6500 +0680 +2780 +9c80 +af80 +2180 +6080 +2340 +4580 +3100 +ENDCHAR +STARTCHAR swallow top left +ENCODING 163 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +00c0 +0100 +0100 +0100 +0200 +0200 +0200 +0400 +0400 +0400 +ENDCHAR +STARTCHAR swallow top center +ENCODING 164 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +6180 +8040 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR swallow top right +ENCODING 165 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +c000 +2000 +2000 +2000 +1000 +1000 +1000 +0800 +0800 +0800 +ENDCHAR +STARTCHAR swallow mid left +ENCODING 166 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0400 +0400 +0400 +0800 +0800 +0800 +1000 +1000 +1000 +1000 +1000 +1000 +1000 +1000 +0800 +0800 +0800 +0400 +0400 +0400 +ENDCHAR +STARTCHAR swallow mid right +ENCODING 167 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0800 +0800 +0800 +0400 +0400 +0400 +0200 +0200 +0200 +0200 +0200 +0200 +0200 +0200 +0400 +0400 +0400 +0800 +0800 +0800 +ENDCHAR +STARTCHAR swallow bot left +ENCODING 168 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0400 +0400 +0400 +0200 +0200 +0200 +0100 +0100 +0100 +00c0 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR swallow bot center +ENCODING 169 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +8040 +6180 +1e00 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR swallow bot right +ENCODING 170 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0800 +0800 +0800 +1000 +1000 +1000 +2000 +2000 +2000 +c000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR explosion 1 +ENCODING 171 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +01c0 +0e00 +31c0 +ce00 +31c0 +c600 +18c0 +6700 +98c0 +2300 +ccc0 +3300 +4c40 +9380 +2440 +4980 +b640 +2880 +5300 +ENDCHAR +STARTCHAR explosion 2 +ENCODING 172 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f780 +ENDCHAR +STARTCHAR explosion 3 +ENCODING 173 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +c000 +3800 +c600 +3980 +c640 +3180 +8c40 +7300 +8cc0 +6200 +9980 +6640 +1900 +e480 +1240 +c900 +3680 +8a40 +6500 +ENDCHAR +STARTCHAR explosion 4 +ENCODING 174 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +a4c0 +2900 +4a40 +5480 +a500 +aa40 +aa80 +aa80 +aa80 +5540 +aa80 +aa80 +aa80 +aa40 +a500 +5480 +4a40 +2900 +a4c0 +5300 +ENDCHAR +STARTCHAR explosion 5 +ENCODING 175 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0840 +f780 +0840 +7700 +8880 +7740 +8880 +b680 +aa80 +5540 +aa80 +b680 +8880 +7740 +8880 +7700 +0840 +f780 +0840 +f780 +ENDCHAR +STARTCHAR explosion 6 +ENCODING 176 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +9280 +4a40 +2940 +9540 +5280 +2a80 +aa80 +aa80 +aa80 +5540 +aa80 +aa80 +aa80 +2a80 +5280 +9540 +2940 +4a40 +9280 +6500 +ENDCHAR +STARTCHAR explosion 7 +ENCODING 177 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +2880 +b640 +4980 +2440 +9380 +4c40 +3300 +ccc0 +2300 +98c0 +6700 +18c0 +c600 +31c0 +ce00 +31c0 +0e00 +01c0 +0000 +0000 +ENDCHAR +STARTCHAR explosion 8 +ENCODING 178 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +f7c0 +0800 +0000 +ENDCHAR +STARTCHAR explosion 9 +ENCODING 179 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +8a40 +3680 +c900 +1240 +e480 +1900 +6640 +9980 +6200 +8cc0 +7300 +8c40 +3180 +c640 +3980 +c600 +3800 +c000 +0000 +0000 +ENDCHAR +STARTCHAR illegal object +ENCODING 180 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3b80 +4640 +4440 +4c40 +3b80 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR amulet +ENCODING 181 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +1e00 +2100 +2100 +2100 +1100 +1100 +0e00 +1f00 +1f00 +0e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR food +ENCODING 182 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3f00 +3f00 +3f00 +1e00 +0c00 +0c00 +0c00 +3f00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR weapon +ENCODING 183 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1000 +1800 +1400 +1200 +1200 +1200 +1200 +1200 +1200 +1200 +7f80 +1c00 +1c00 +1c00 +1c00 +1c00 +0000 +0000 +ENDCHAR +STARTCHAR tool +ENCODING 184 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +2100 +2100 +3f00 +3f00 +3f00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ball +ENCODING 185 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0e00 +1f00 +1f00 +1f00 +eac0 +5780 +ENDCHAR +STARTCHAR chain +ENCODING 186 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +6d80 +9240 +6d80 +ENDCHAR +STARTCHAR rock +ENCODING 187 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +3100 +2900 +4280 +6900 +5480 +5000 +2280 +4980 +5f80 +3500 +ff40 +bfc0 +ENDCHAR +STARTCHAR armor +ENCODING 188 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +2e80 +3f80 +2a80 +3580 +2a80 +3580 +2a80 +1500 +0a00 +0400 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR potion +ENCODING 189 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +1e00 +0c00 +0c00 +0c00 +0c00 +1e00 +3700 +3d00 +2f00 +3b00 +1e00 +0000 +0000 +0000 +ENDCHAR +STARTCHAR scroll +ENCODING 190 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +3f00 +7f80 +3f00 +2a00 +1500 +2a00 +1500 +2a00 +3f00 +7f80 +3f00 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR wand +ENCODING 191 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1100 +1500 +1240 +0a80 +0100 +0400 +0400 +0800 +0800 +0800 +1000 +1000 +1000 +2000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ring +ENCODING 192 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +2900 +1200 +4180 +1c00 +2280 +2200 +2200 +1c80 +4100 +1480 +2400 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR gem +ENCODING 193 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3e00 +6300 +2200 +1c00 +0800 +0000 +0000 +ENDCHAR +STARTCHAR gold +ENCODING 194 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2a00 +2a00 +5280 +5480 +4500 +0000 +3000 +5f00 +7d80 +6f80 +1c00 +ENDCHAR +STARTCHAR venom +ENCODING 195 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0c00 +1200 +2200 +2c00 +1000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR spbook +ENCODING 196 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +3e00 +2300 +3300 +2300 +2300 +2300 +3300 +2300 +3f00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Aring +ENCODING 197 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1200 +1200 +0c00 +0c00 +1e00 +3300 +6180 +6180 +7f80 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR AE +ENCODING 198 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0f80 +1e00 +3600 +3600 +6600 +6600 +7f80 +6600 +6600 +6600 +6600 +6600 +6780 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ccedilla +ENCODING 199 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1e00 +3300 +6180 +6000 +6000 +6000 +6000 +6000 +6000 +6000 +6180 +3300 +1e00 +0c00 +0400 +1200 +0c00 +0000 +ENDCHAR +STARTCHAR Egrave +ENCODING 200 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +7f80 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Eacute +ENCODING 201 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +7f80 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ecircumflex +ENCODING 202 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +7f80 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Edieresis +ENCODING 203 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +7f80 +6000 +6000 +6000 +7e00 +6000 +6000 +6000 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Igrave +ENCODING 204 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +3f00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Iacute +ENCODING 205 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +3f00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Icircumflex +ENCODING 206 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +3f00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Idieresis +ENCODING 207 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +3f00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +3f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Eth +ENCODING 208 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +7e00 +6300 +6180 +6180 +6180 +6180 +f980 +6180 +6180 +6180 +6180 +6300 +7e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ntilde +ENCODING 209 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1900 +3f00 +2600 +0000 +4180 +6180 +7180 +7980 +7d80 +6f80 +6780 +6380 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ograve +ENCODING 210 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Oacute +ENCODING 211 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0300 +0600 +0c00 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ocircumflex +ENCODING 212 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Otilde +ENCODING 213 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1900 +3f00 +2600 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Odieresis +ENCODING 214 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR multiply +ENCODING 215 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +2080 +3180 +1b00 +0e00 +0e00 +1b00 +3180 +2080 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Oslash +ENCODING 216 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0080 +1f80 +3300 +6380 +6580 +6580 +6980 +6980 +7180 +3300 +7e00 +4000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ugrave +ENCODING 217 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Uacute +ENCODING 218 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0300 +0600 +0c00 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Ucircumflex +ENCODING 219 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Udieresis +ENCODING 220 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Yacute +ENCODING 221 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0300 +0600 +0c00 +0000 +4080 +6180 +3300 +1e00 +0c00 +0c00 +0c00 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR Thorn +ENCODING 222 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +3c00 +1800 +1f00 +1980 +1980 +1980 +1f00 +1800 +1800 +1800 +3c00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR germandbls +ENCODING 223 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +1c00 +3e00 +7300 +6300 +6300 +6600 +6c00 +6600 +6300 +6100 +6300 +6e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR agave +ENCODING 224 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +0000 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR aacute +ENCODING 225 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR acircumflex +ENCODING 226 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +0000 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR atilde +ENCODING 227 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1900 +3f00 +2600 +0000 +0000 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR adieresis +ENCODING 228 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR aring +ENCODING 229 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0c00 +1200 +1200 +0c00 +3f00 +6180 +0180 +3f80 +6180 +6180 +6180 +3e80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ae +ENCODING 230 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3b00 +4d80 +0d80 +0f00 +3c00 +6c00 +6c80 +3700 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ccedilla +ENCODING 231 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1f00 +3180 +6000 +6000 +6000 +6000 +3180 +1f00 +0c00 +0400 +1200 +0c00 +0000 +ENDCHAR +STARTCHAR egrave +ENCODING 232 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +0000 +1e00 +3300 +6180 +7f80 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR eacute +ENCODING 233 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +1e00 +3300 +6180 +7f80 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ecircumflex +ENCODING 234 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +0000 +1e00 +3300 +6180 +7f80 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR edieresis +ENCODING 235 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +1e00 +3300 +6180 +7f80 +6000 +6000 +3180 +1f00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR igrave +ENCODING 236 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR iacute +ENCODING 237 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR icircumflex +ENCODING 238 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR idieresis +ENCODING 239 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +3c00 +0c00 +0c00 +0c00 +0c00 +0c00 +0c00 +7f80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR eth +ENCODING 240 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +4400 +6c00 +3800 +3800 +6c00 +4600 +1f00 +3380 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ntilde +ENCODING 241 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1900 +3f00 +2600 +0000 +0000 +6e00 +7300 +6180 +6180 +6180 +6180 +6180 +6180 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ograve +ENCODING 242 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR oacute +ENCODING 243 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ocircumflex +ENCODING 244 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR otilde +ENCODING 245 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +1900 +3f00 +2600 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR odieresis +ENCODING 246 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +1e00 +3300 +6180 +6180 +6180 +6180 +3300 +1e00 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR divide +ENCODING 247 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0c00 +0c00 +0000 +0000 +7f80 +7f80 +0000 +0000 +0c00 +0c00 +0000 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR oslash +ENCODING 248 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +1e80 +3380 +6380 +6780 +6d80 +7980 +3300 +7e00 +4000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ugrave +ENCODING 249 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3000 +1800 +0c00 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR uacute +ENCODING 250 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ucircumflex +ENCODING 251 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0c00 +1e00 +3300 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR udieresis +ENCODING 252 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR yacute +ENCODING 253 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0600 +0c00 +1800 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0180 +6180 +3300 +1e00 +0000 +ENDCHAR +STARTCHAR thorn +ENCODING 254 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +0000 +0000 +0000 +0000 +0000 +3800 +1e00 +1b00 +1b00 +1e00 +1800 +1800 +3800 +0000 +0000 +0000 +0000 +0000 +ENDCHAR +STARTCHAR ydieresis +ENCODING 255 +SWIDTH 878 0 +DWIDTH 10 0 +BBX 10 20 0 -5 +BITMAP +0000 +0000 +3300 +3300 +0000 +0000 +0000 +0000 +6180 +6180 +6180 +6180 +6180 +3380 +1d80 +0180 +6180 +3300 +1e00 +0000 +ENDCHAR +ENDFONT diff --git a/win/X11/nh32icon b/win/X11/nh32icon new file mode 100644 index 0000000..47fd0b9 --- /dev/null +++ b/win/X11/nh32icon @@ -0,0 +1,20 @@ +/* SCCS Id: @(#)nh32icon 3.4 2002/02/12 */ +/* Copyright (C) 1993,1995 by Robert Patrick Rankin */ +/* NetHack may be freely redistributed. See license for details. */ + +/* 32x32 X11 icon for NetHack. */ + +#define nh32icon_width 32 +#define nh32icon_height 32 +static unsigned char nh32icon_bits[] = { + 0xff, 0x7f, 0xfe, 0xff, 0x01, 0xc0, 0x03, 0x80, 0x01, 0x00, 0x40, 0x82, + 0x21, 0x25, 0xc0, 0x83, 0x61, 0x25, 0x80, 0x81, 0xe1, 0x3d, 0x80, 0x81, + 0xa1, 0x25, 0x80, 0x81, 0x21, 0x25, 0x80, 0x81, 0x01, 0x00, 0xe0, 0x87, + 0x71, 0x48, 0x90, 0x89, 0x81, 0x48, 0x80, 0x81, 0x61, 0x78, 0x80, 0x81, + 0x81, 0x40, 0x80, 0x81, 0x71, 0x42, 0x84, 0x81, 0x03, 0x00, 0x8a, 0xc1, + 0x02, 0x00, 0x84, 0x41, 0x32, 0x67, 0x80, 0x41, 0xf3, 0x7f, 0x80, 0xc1, + 0xf1, 0x7f, 0x84, 0x81, 0x71, 0x77, 0x8a, 0x81, 0xb1, 0x68, 0x84, 0x81, + 0x71, 0x77, 0x80, 0x81, 0x71, 0x77, 0x80, 0x81, 0xb1, 0x68, 0x84, 0x81, + 0x71, 0x77, 0x8a, 0x81, 0xf1, 0x7f, 0x84, 0x81, 0xe1, 0x3f, 0x80, 0x81, + 0xc1, 0x1f, 0x80, 0x81, 0x81, 0x0f, 0x80, 0x81, 0x01, 0x07, 0x00, 0x81, + 0x01, 0xc0, 0x03, 0x80, 0xff, 0x7f, 0xfe, 0xff}; diff --git a/win/X11/nh56icon b/win/X11/nh56icon new file mode 100644 index 0000000..b58a605 --- /dev/null +++ b/win/X11/nh56icon @@ -0,0 +1,42 @@ +/* SCCS Id: @(#)nh56icon 3.4 2002/02/12 */ +/* Copyright (c) 1993,1995 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* 56x56 X11 icon for NetHack. */ + +#define nh56icon_width 56 +#define nh56icon_height 56 +static unsigned char nh56icon_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x30, 0x38, 0x18, 0x00, + 0xc0, 0x03, 0x00, 0x78, 0x28, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xf8, 0x6c, + 0x3e, 0x00, 0xc0, 0x03, 0x00, 0xf8, 0x55, 0x3f, 0x00, 0xc0, 0x23, 0x22, + 0xfc, 0xc7, 0x7f, 0x88, 0xc8, 0x43, 0x10, 0xfc, 0xd7, 0x7f, 0x10, 0xc4, + 0x03, 0x07, 0xfe, 0xc7, 0xff, 0xc0, 0xc1, 0x83, 0x0d, 0xfe, 0xd7, 0xff, + 0x60, 0xc3, 0xa3, 0x28, 0xfe, 0xc7, 0xff, 0x28, 0xca, 0x83, 0x0d, 0xfe, + 0xd7, 0xff, 0x60, 0xc3, 0x03, 0x07, 0xfe, 0xc7, 0xff, 0xc0, 0xc1, 0x43, + 0x10, 0xfc, 0xd7, 0x7f, 0x10, 0xc4, 0x23, 0x22, 0xfc, 0xc7, 0x7f, 0x88, + 0xc8, 0x03, 0x00, 0xf8, 0x55, 0x3f, 0x00, 0xc0, 0x03, 0x00, 0xf8, 0x44, + 0x3e, 0x00, 0xc0, 0x03, 0x00, 0x78, 0x54, 0x3c, 0x00, 0xc0, 0x03, 0x00, + 0x30, 0x6c, 0x18, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x6c, 0x00, 0x00, 0xc0, + 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x38, 0x00, + 0x00, 0xc0, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, + 0x38, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, 0xc0, 0x03, + 0x00, 0x00, 0x38, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, + 0xc0, 0x43, 0x10, 0x21, 0x38, 0x70, 0x20, 0xc2, 0xc3, 0x10, 0x21, 0x38, + 0x88, 0x20, 0xc2, 0xc3, 0x10, 0x21, 0x38, 0x80, 0x20, 0xc2, 0x43, 0x11, + 0x21, 0x38, 0x80, 0x20, 0xc2, 0x43, 0x12, 0x3f, 0x38, 0x70, 0xe0, 0xc3, + 0x43, 0x12, 0x21, 0x38, 0x80, 0x00, 0xc2, 0x43, 0x14, 0x21, 0x38, 0x80, + 0x00, 0xc2, 0x43, 0x18, 0x21, 0x38, 0x80, 0x00, 0xc2, 0x43, 0x18, 0x21, + 0x38, 0x88, 0x0c, 0xc2, 0x43, 0x10, 0x21, 0x38, 0x70, 0x0c, 0xc2, 0x03, + 0x00, 0x00, 0x38, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x38, 0x00, 0x00, + 0xc0, 0xfb, 0xff, 0xff, 0x39, 0xff, 0xff, 0xdf, 0x0b, 0x00, 0x80, 0x7c, + 0x02, 0x00, 0xd0, 0x0b, 0x00, 0x80, 0xee, 0x02, 0x00, 0xd0, 0xfb, 0xff, + 0xff, 0xd6, 0xfe, 0xff, 0xdf, 0x0b, 0x00, 0x80, 0xaa, 0x02, 0x00, 0xd0, + 0x0b, 0x00, 0x80, 0x54, 0x02, 0x00, 0xd0, 0xfb, 0xff, 0xff, 0x39, 0xff, + 0xff, 0xdf, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; diff --git a/win/X11/nh72icon b/win/X11/nh72icon new file mode 100644 index 0000000..75dfe84 --- /dev/null +++ b/win/X11/nh72icon @@ -0,0 +1,63 @@ +/* SCCS Id: @(#)nh72icon 3.4 1993/01/21 */ +/* Copyright (c) 1993 by M. Stephenson */ +/* NetHack may be freely redistributed. See license for details. */ + +/* 72x72 X11 icon for NetHack. */ + +#define nh72icon_width 72 +#define nh72icon_height 72 +static unsigned char nh72icon_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x10, 0x00, 0xe0, 0x07, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0xe0, + 0x07, 0x00, 0x78, 0x00, 0x00, 0x00, 0x1e, 0x00, 0xe0, 0x07, 0x3e, 0xc8, + 0x07, 0x00, 0xf8, 0x13, 0x7c, 0xe0, 0x07, 0x22, 0x08, 0xfc, 0xc1, 0x0f, + 0x10, 0x44, 0xe0, 0x07, 0x36, 0x08, 0x00, 0x7f, 0x00, 0x10, 0x6c, 0xe0, + 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x14, 0x08, + 0x00, 0x00, 0x00, 0x10, 0x28, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, + 0x10, 0x38, 0xe0, 0x07, 0x14, 0x08, 0x00, 0x00, 0x00, 0x10, 0x28, 0xe0, + 0x07, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x14, 0x08, + 0x00, 0x00, 0x00, 0x10, 0x28, 0xe0, 0x87, 0xff, 0x08, 0x10, 0x00, 0x10, + 0x10, 0xff, 0xe1, 0xc7, 0xff, 0x09, 0x38, 0x10, 0x38, 0x90, 0xff, 0xe3, + 0x47, 0x1c, 0x09, 0x78, 0x38, 0x3c, 0x90, 0x38, 0xe2, 0x07, 0x1c, 0x08, + 0xfc, 0x39, 0x7f, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0xfc, 0xbb, 0x7f, + 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0xfe, 0xff, 0xff, 0x10, 0x38, 0xe0, + 0x07, 0x1c, 0x08, 0xfe, 0xff, 0xff, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, + 0xfe, 0xff, 0xff, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0xfe, 0xff, 0xff, + 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0xfe, 0xff, 0xff, 0x10, 0x38, 0xe0, + 0x07, 0x1c, 0x08, 0xfc, 0xbb, 0x7f, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, + 0xfc, 0x39, 0x7f, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x78, 0x38, 0x3c, + 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x38, 0x38, 0x38, 0x10, 0x38, 0xe0, + 0x07, 0x1c, 0x08, 0x10, 0x38, 0x10, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, + 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, + 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, + 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, + 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, + 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, + 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, + 0x00, 0x28, 0x00, 0x10, 0x38, 0xe0, 0x07, 0x1c, 0x08, 0x00, 0x38, 0x00, + 0x10, 0x38, 0xe0, 0x07, 0x08, 0x18, 0x00, 0x28, 0x00, 0x10, 0x10, 0xe0, + 0x07, 0x08, 0x30, 0x00, 0x38, 0x00, 0x18, 0x10, 0xe0, 0x07, 0x00, 0x60, + 0x00, 0x28, 0x00, 0x0c, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x00, 0x38, 0x00, + 0x06, 0x00, 0xe0, 0x07, 0x00, 0x80, 0x01, 0x28, 0x00, 0x03, 0x00, 0xe0, + 0x07, 0x00, 0x00, 0x03, 0x38, 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0x00, + 0x06, 0x6c, 0xc0, 0x00, 0x10, 0xe0, 0x07, 0x08, 0x00, 0x0c, 0x44, 0x60, + 0x00, 0x92, 0xe0, 0x07, 0x49, 0x00, 0x18, 0x7c, 0x30, 0x00, 0x6c, 0xe0, + 0x07, 0x36, 0x00, 0x30, 0x00, 0x18, 0x00, 0x44, 0xe0, 0x07, 0x22, 0x00, + 0x60, 0x00, 0x0c, 0x00, 0x83, 0xe1, 0x87, 0xc1, 0x00, 0xc0, 0x00, 0x06, + 0x00, 0x44, 0xe0, 0x07, 0x22, 0x00, 0x80, 0x01, 0x03, 0x00, 0x6c, 0xe0, + 0x07, 0x36, 0x00, 0x00, 0x83, 0x01, 0x00, 0x92, 0xe0, 0x07, 0x49, 0x00, + 0x00, 0xfe, 0x00, 0x00, 0x10, 0xe0, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; diff --git a/win/X11/nh_icon.xpm b/win/X11/nh_icon.xpm new file mode 100644 index 0000000..6e9bbd1 --- /dev/null +++ b/win/X11/nh_icon.xpm @@ -0,0 +1,49 @@ +/* XPM */ +static char * nh_icon[] = { +"40 40 6 1", +" s None c none", +". c #ffffff", +"X c #dadab6", +"o c #6c91b6", +"O c #476c6c", +"+ c #000000", +" ", +" ", +" ", +" . .X..XX.XX X ", +" .. .....X.XXXXXX XX ", +" ... ....X..XX.XXXXX XXX ", +" .. ..........X.XXXXXXXXXXX XX ", +" .... ........X..XX.XXXXXXXXX XXXX ", +" .... ..........X.XXXXXXXXXXX XXXX ", +" ooOOO..ooooooOooOOoOOOOOOOXX+++OO++ ", +" ooOOO..ooooooooOoOOOOOOOOOXX+++OO++ ", +" ....O..ooooooOooOOoOOOOOOOXX+XXXX++ ", +" ....O..ooooooooOoOOOOOOOOOXX+XXXX++ ", +" ..OOO..ooooooOooOOoOOOOOOOXX+++XX++ ", +" ++++..ooooooooOoOOOOOOOOOXX+++ +++ ", +" +++..ooooooOooOOoOOOOOOOXX+++ + ", +" ++..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..ooooooOooOOoOOOOOOOXX+++ ", +" ..ooooooooOoOOOOOOOOOXX+++ ", +" ..oooooOooOOoOOOOOOXX+++ ", +" ..oooooooOoOOOOOOOOXX+++ ", +" ..ooooOooOOoOOOOOXX+++ ", +" ..ooooooOoOOOOOOOXX++++ ", +" ..o..oooOooOOoOOOOXX+XX+++ ", +" ...o..oooooOoOOOOOXX++XXX++ ", +" ....OO..ooOooOOoOOXX+++XXXX++ ", +" ...oo..+..oooOoOOOXX++XXooXXX++ ", +" ...ooo..++..OooOOoXX+++XXooOXXX+ ", +" ..oooOOXX+++....XXXX++++XXOOoOOXX+ ", +" ..oooOOXX+++ ...XXX+++++XXOOooOXX++ ", +" ..oooOXXX+++ ..XX+++ +XXOOooOXX++ ", +" .....XXX++++ XXXXXXX++ ", +" ....XX++++ XXXXXXX+ ", +" ...XX+++ XXXXX++ ", +" ", +" ", +" ", +" "}; diff --git a/win/X11/pet_mark.xbm b/win/X11/pet_mark.xbm new file mode 100644 index 0000000..69b4cf0 --- /dev/null +++ b/win/X11/pet_mark.xbm @@ -0,0 +1,6 @@ +#define pet_mark_width 16 +#define pet_mark_height 16 +static unsigned char pet_mark_bits[] = { + 0x00, 0x00, 0x6c, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0x7c, 0x00, 0x38, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/win/X11/rip.xpm b/win/X11/rip.xpm new file mode 100644 index 0000000..e7919ed --- /dev/null +++ b/win/X11/rip.xpm @@ -0,0 +1,297 @@ +/* XPM */ +static char *rip_xpm[] = { +/* width height ncolors chars_per_pixel */ +"400 200 90 1", +/* colors */ +" c #36362E", +". c #414146", +"X c #2F2F31", +"o c #353523", +"O c #034702", +"+ c #152E0A", +"@ c #30311E", +"# c #29292B", +"$ c #233511", +"% c #22221A", +"& c #A6A6A2", +"* c #3A3A38", +"= c #75746B", +"- c #191A14", +"; c #7F7E78", +": c #12120D", +"> c #1F2713", +", c #16330A", +"< c #033E01", +"1 c #181816", +"2 c #033A01", +"3 c #043802", +"4 c #161614", +"5 c #46464D", +"6 c #242425", +"7 c #0C0C0A", +"8 c #15150C", +"9 c #454545", +"0 c #4B4B37", +"q c #1F300F", +"w c #1F2E0F", +"e c #3F3F3F", +"r c #3D3D3D", +"t c #1B1B15", +"y c #1A2E0D", +"u c #888783", +"i c #2D2D2D", +"p c #292929", +"a c #807F7B", +"s c #272F13", +"d c #313134", +"f c #4D4D56", +"g c #18180E", +"h c #B4B4B1", +"j c #989892", +"k c #252621", +"l c #90908A", +"z c #1C1C18", +"x c #83827D", +"c c #6D6C64", +"v c #20201F", +"b c #797873", +"n c #2C2C2E", +"m c #42424A", +"M c #434341", +"N c #1F1F17", +"B c #273B22", +"V c #2E2E1F", +"C c #7E7D74", +"Z c #8B8B84", +"A c #1D1D18", +"S c #85857E", +"D c #4B4B4F", +"F c #CECECC", +"G c #034301", +"H c #034101", +"J c #272728", +"K c #043302", +"L c #353539", +"P c #282B15", +"I c #5A5B50", +"U c #24241B", +"Y c #C0C0BE", +"T c #30302A", +"R c #5E5E61", +"E c #1F330F", +"W c #DFDFDC", +"Q c #8C8C87", +"! c #043C01", +"~ c #053602", +"^ c #38383B", +"/ c #053002", +"( c #343437", +") c #44444A", +"_ c #52525B", +"` c #132909", +"' c #060603", +"] c #292922", +"[ c #424237", +"{ c #3B3B3A", +/* pixels */ +"'''''''''''''''''7777777v7''''''''''''''''''''v''k'7777777777777777:7:7:7:7::::::4::4:444444444411111z1zzzzzzvddvvvvvvvvvvv6J#JJJJJ6JJ66JJJJJJ####nnnnXXdL^^^fRLLLLL^^^^^rrr.rm..mmmmmm5m....c_m__.mm.m.mmm.m.mm.mmm.m_).mmmm)_Df555555D5D5D5D5DDDDDfffff_____________cR______c_______c_____R_R_R______R__uffDDDDDDDDDR5D5D5D5D5D5D55R55D55555555555555Ra555555555)5)))))))))))))))))))5)5))5)5)5)))))))5))55555", +"'''''''''''''''''''776777'7''''''''''''''''''7'7kv7'7777777777777:77:7:::::::::::::4:44:44444411111111zzzdzzvzvvzvvvvvvvv66JJ##66JJJ6J6JJ6JJJ#####nnnXXXdL^^^^^^LL^^^r^.R^.^.Rmmmm55m5m55m..m_mm)))mmmmmmm))55)mm.m.mm9mmmmmm)555fffff5fffffDDDDfDffffff_______________R_c_cR_____R__cR_______R__R_R_____fRDfDfDD5DD555D5D5555555555R555555D5555555555555555555)))))))))mmmmmmmm))))))))))))))))))))))))))))))))", +"''''''''v''''''''77'7777777v''''''''''''''7'7'77'77777777777:7:7:7::7:7::e:::#.:4:44:4:444444X41111zzz1v1vzvzvzvvvzvvvv666J####JJ6#66J6JJJJ#####nnnnXXXXdL^^m^^^^L^^^.RfRm^m.mmm5m555555mmmmm)mm)))))))))5555555mmm_cmmm_)))))5fff5fffffff_ffffffff_b__________________cbRR__c_RR___________R__________ffff5DD5D5D55DD5555555555555555555555555555555555)5_5)))))))mmmmmm9m9m9mmmmmmmmmmmmmmmmmmmm))))))))))))))", +"''''''''''''''''v''7777777vv('''''''''''''7'77'77777777777:7:7:::7:7:::::#7474::4:4:44444n9n491111z11v1v1vzvdvvvvvvvv6666######JJJ6J6JJJJJ#J###n#nnXnXXdLL^^^^^^^^^^.^f...f..mmm55555f5_mmmmm)))))))55))55ff5ff55m)m))mR_)))555f5bffff_R_fff________R___________R_____bR____________________________ffffDf5fDDD5f5D55555555555R5555555555555)5)5c))))))))5_)))))mmm).mm)_m.mm)))))m.m.m.m.m9__.).).m.mmmmmm)))))", +"''''''''''''''''v'777777777''''''''''''7'7'77'777777777:7:7:7:7:747474:::4:4:4744:44444444411111z11v11v1v1vvvzvvvvv666JJ#######6J6JJJJJJJ#J####nnnnXXXXd(L^mLm^m^.^^.^.^.^m..mmm55555mmmmmm))))555555555D5fff5ff55))))))_D555555fffffffffff_f______________R__R_____R___R_c_R__R__________________Rfff5f5DD5D55D555555555555555D5D5555))))))))))_5))))))))))mmmmm))m5mmm)mm.m555))m.........9.9..m.m.m.m9mmmmmmm", +"''''''''''7''7''''v'777777777''''''''''7'777777777777:7:7::7:747474::474:474:444:444:444444111111v11v1v1vvvvv6vvv66J6#6._###^#JJ6JJJJJJJJ#####nnnnXnXdddLLLf^Lm^m^m^^......m^.mm5m5555m)_))))55555555555ff5fff5RfD555_l55555555fff_ffff__ffffff___________________R_________c__________f_fff_f_f_RfffDfDDf5D5D5555555555)55mRD5fRf5555)))5_)))))5)))))))m)))m).m555555)mmmm555555mm........._..m..._.....__m9m9m", +"''''''''''''''7'7'c'7'77777'777777777'7'77'77777777:67:7:7:::::::474::4:4:44:4:4447n4444441111dv11v1v1vvv666666#6##J###__.6##66#6JJJJ#######nnnnnXXXXXdddLL^^mLm^m^m.m^m^.^m....mmmmmmm5))555_55555555Dfffffff5f5f=555Rm55555555fffffffff_f_ff_f_______R___________________________f_f_ff_fffffffff5f5f5D5D555555555))))5_DDf5f55f5f555)mm)mmmmmmmmmmm555555)))5555555mm)555555555m....................m..9.m.mm", +"'''''''''''''''v'''7''7'7777777776777{v7777777777:777JJ::74747474:47444474:44444444n4444411)1d5vvvvvv66666666#66666####nR^666#JJ##J##J######nnnnnXnXdddLdLL^LLLm^mm^m^m.^..m^..mmmmmm5)))5555D55D5D5D5ffffffRff5ffb5f5555555DD5f5fffffff_fff_ff__ff______________R________b_R____f_fff_RfffffffffR5ffDD5D5D5555555)555555=5f55555555555)m9_mm)m9mmc.m5555f5555_5f_5f55mmm55mmmm555m......................mmmmm))", +"''''''''''''''''v''''''777777767777777777777777:77:::J:74J.::4:4:444744444447444444n444111191zvvvvv6v6vvvvvv6v6666666######J#JJ##J##J####ennnnnXXXXdddd5LLLL^m^mmLm^mmm^m^m..m.Rmmm5m5)55555555D55DDDD5f_fffb55f5f5f5f55555D5D5=5f5ffffff_f_f_ff_f____________cc____________bRbfffff_fffffffffDf5ff55DD5D5Rc5555))))555f5f5m555mmm5f5555mm_.mmmm.mmm)55f555555555_5m55mm5mmmmmmm555mm...mm..e.e.e......mmmmmm555", +"'''''''''''''''''''''''''77777^6477777777777r:77::7:7:::::#74:47447444.4#4444444444441111111z16vvvvvv1vvvvvvvv6v6666666JJJJ#6###J#####n#nnnnnXnXXXXdd(ddLLLLLmLm^m^m^m^.......m5mm5m5)5555555D5DDDD5f5ffffffff5f5fffRfD555D5D5ff5ff5fffff___ff_f__f____R___________c_____f_ff_ff_fffffffffffDDfDfDDDf55D5555555))55555f555mmmmmmmmm5cf55mm.mm)mm...m5555mm55_5555m5m55m55mm....mmm5m5mmmmm..._e.f.f.e.mm55555555", +"''''''''''''v'''''''''7'77777'67774777777::::::::::::4:444:44:44444444#44444444444111111111zdvvv1v1v1vvvvvv6v6666666##J6#J^#########n#nnn#nnnXXXXdddddLLLLLLLLm^mmLm^mm^m...m.mm)))5)555555D55DD5fDDDfff_ffff5f5f5fffff5DDDDf5f5fffffff____f________________R__________f_fff_bRfffRfffDffDDRRf5DD5D55D5555555))55555f5a55mmmm.....mmm555m.mmmmmm..m55m_5mmmmm5mmm5mmmm_mmm..rer.mmmmm5m5m5m........e..m5_5mc55__", +"''''''''''''('''''''''''77777777447:7::7:74:474744:4:44:4447444444#44444444114114111111111v1dvvvvv1vvv1vv6d6v6v6666666#J###########n#nnnnnnXnXXXdddd(((LLL^L^Lm^mLm^m^.mm..m.mm))5)555555c55DD5DDRf5fffff_fffff555ffffff5f5D5fDffDffffff_____fb___________R_________f_fbf_ffff____RfDfDfDDDfD5fDD5f5D55555_)5_R555f5555mm..........mm555m.mmmm...m5_mmmmmmmmmm55mmmmmmm..r.r..^.e_f.mmm5mmmmmmm.mm..mm5mm.m_m...", +"N''''''''''''''''''''''777777747474474_::474:4444#47444444444444444444111111111X1111111v111vvv6vv1vv16vvvv566v6666##J#J#########n.n#nnnnnXnX._Xdddd()L(LLL^^^^^m^m^m^m...mm.mm))))555555D5fDD5fDf5=ffffRR_ff555f5fffffffffDfDDfDfffRf_f_f__f__bf___Ra__R________R____fff_f_f_______fffDDfDDD5DR55D555555555555f5f55mmmm_m..e....e...mm555mmm.mmmm55mmmmm..fR5mmm5mm..^.^r.^.^.rrr.re.emmmmmmmmmmmmmmmm_m.ee.r.ee", +"'''''''N''''N'''''''''7'7747447474474444J44744744n_4444444444444444111111111111115111v11v1v1vv66vvvvvvLvvv6v66666666JJ#J####.##n#nnnnnnnXnXXdXd.e_xalLLLL^^LmLm^m^m^mblabbbmm)))55555555D5D5ff=Rffbff____ff5ff5555fff_fffffRDfffffff_f________b__________R_________fff__f__________ffffDDD5DD5555555D_5))5555f55mmmmm....r..e.Rr..^..mm55mmmmmmm5mmm....r.mmmmmmmm..^.^.^.^.^.^.^.^.^.^r....mmmmmmmmmm..r.r.r.e.", +"'''''''v'''NL'''''7'7''7'777774474744447447444444441441411444444441111111v11v11v1d1v111v11vvvv666vvvvvv6v666v6666#6###J######nnnnnnnnnXnX._e=&hY&&jjh^L^^^^^^mLmmfmmmRFYYFWW&lRc555555DDafffffffffff____ff5f55f5f5ffRf__ffffffDffffR__f____________R__c__________ff_f__________ff__f_fD5D5f555D5555555_5)5fff5_5mm....r...r.....r...rmmm_5m5mm5mmm.r.^.^.^.mmmmm_f.f^^r^R.^^.^.^.^rrr^..^.^..........^er.^.^.^.^", +"''''''''''''N''''''''77'777774747444#m444447474444441111111111111111111v11Rv11v111v11v1v1v1v166666vvvv6v66v66666#6##J###.##nnnnnnnnXnXXDRjYYhYh&j&&jj^^^^^.^.LmmmRmmmRWFYYFWFWWWhjRR5D55Rff_____ff___fffff5f5f5f5fmff__f__ffDffffR_bR_________________c_______f_f_ff________f_fffff_ffff5f555555555))5))55555f5m....r..r.r.^.^.^.r.^..m__mm5mmmmm....^.^^.^mmmmmmm^L.^.^.^^.^^r^^.^.^.^^^.^.^m^.^^.^rr.^.Rr.r.ee", +"'''''''''''''''''7'77'7777777744444447444444444444411v111111111v1v1v1v11v111v1v1vv15v1v1v1vv66666vvvv6v6v66666J6J#J#J#####nn#n.nnnXne=&&hYYhh&&l&h&jQL^^^^^.^fmmmmmmmbFFFYYFWWWFYhYFl=fRfff__________fff5ff5f5f5f5fff_____f_ffffRR_f___________R__R___RR_______fffff______fffffR_ffffff5fffDD5555))))))555f555mm...r..^m^...r..RRm..r.mm_mmmmm....^.f.^.^..m.mmm^.^^^^^^^r^^r.^.^^.^^.f.^^r^.^.^r^^.^.Rrrfe^.^rr", +"'''v'''''''''''7'7'7vv7'7777777744744444444444441111v11v11v1v1v11v1111v1v1vv11v1v11v1v1v1vv1666666L6v6v6666666#JJJJ####._nn__nnn.rR&YYhY&hYh&jlQjh&Qarn^^^.Lm^.mmmmmmbWWFYFYWWWYYhhFFYY&bff_______ffff5fff5fff5f5f5f___f___f_ff__R____cR_______c______a____f_ff_fff_____ffff5f5ff55fffffff5ff5f55cc))m)555555_m...em^..bf.r^.rr..^.^.^mm5mmm...^..mm^.m^m^m^mLm^m^^.^r^.^^^.^^^^r^^.^^^^^.^.^.^.^.rfr^.^rr^.^.^r", +"'''''''''''''''''77'7{77777777474744744111144444411v11111111111vv1v1v1v1v1v1vv1v1vv1v1vvv1v6v66666m66666666^#6#6########nn..nnXRbhhhhYYYhhh&&hjuQhjQbee^.^.^.^mfmmmmmbFFWFYYFWWWhYYhFFWWY&R____ffffffffffff5f5fff5fff___R_____f__R________R__R______a_____f_f____ff_____ff5f55f55D5fffff5555ff5fm_mmmm5555555mmme.r..^.frrf.^..^.r.^...mmmm..^..m.^m.^m.^mmm^mmL^^fL^^^^^.^^^^^.^^r^^.^r^^^^^^^^^^^r^^^.^.^^^r^.", +"''*'''''N'''''7'7'777777777774777474441111144444n111v1v11v1v1v11vv1v1vv1vv1vv1vvvv1vvvv1vvvv66666666v666666#6#6##J####nn#nnnXDlhh&h&hhYh&&j&h&&Qj&&ubr5r^.^.f.mmmmmmmQWWFWYYhWWWhhhYhYFWWFY&uR_fbffffRf=5fDfffDf5fff______u__R_f_________________________f_f__________ffRff5fR5555555f5f555mm55f5)mmmm555f5f_mm..r.^.^.^.^.^.^.^.^ff.^^.m.m^.^^m^mm.^mLm^m^^m^Lm^^R^^^^^^^^^^^^^^.^^^^^^.^.^.^.rf^.^^.r^^^^^.^r^", +"''v'''''v''7'7'777vk77r77:74774474747441v11v4444111v111v1dd111v11vv1v1vvdvvv1vv1vvvv1vvvvvvv66#66v66666666#6########nn#nnnnDjhhhh&YFWYYYhjulljjjjh&jQr)dr^m^....mmmmmjWWWWYYYYWWFhYY&hhhFWWYhhaRbffffRfRffffDfDffDfff___________________R___________bR_f______________ff5f5555D555555555m5m5mm5f5mm..m55mmm5_5m.^.^.RR.r.^.^.^fr^.^rff.^.^.^.^.^^f^^m^^mLmfmLmLLL^L^^^^^^^^^^^^^^^^.^^.^^^^^^^^^^^^^^^^^^.^^^^r^", +"'''''''v''7'777'77{777777477J4747474411111v1v11111v11v1111111v11v1vv1vv5mv1vvvvvvv1vvvvvvvvv66#6666666666#6#J#J#####n#nn.f&FFhhhh&hWFWWYY&lQQxl&YYYYQM9drm^.m^..mmmmmbWWWFFYFYhWYhYFYhhYhWWWWYFWRRfffRffffffffffDfff_________f_f_______RR___________________R____f_ffff5f55f5D5555555m5m))mmmm5555mmmmm5m55_5m.^.r.rmRr^.^^.^^.^.rrfDf^.^^^.^m^LmRm^^^f^^LLLLLLL^LL^LLLLL^L^^^^^f^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^", +"'''''v'''7777777k77k7:7::7474747474744L_mL1d#ddvv11161v1v1dd1vdv1vv1vdvvvvvvv1vvdLvvvvvvvvb666#6666666#######^.###nnnnncjYYYYFhYY&FFFWFYjj&lQj&&Yhu_^^9re.......mmm55f_lFhYhYhjFYYYFFYhFYFWWWWWFF&R_ff_fffffDfffffDff___R______________Rc___c__R_____R_R_______ffffff5f55f55D555555)5)))mmm.m.m5555)5555m5mm5m^.r.^^.^.^^.^^.^^.^^.^r^^^^.L^mL^mL^L^mLRmL^^^^LLLLLLL^^^^L^LD^^^^R^^^^^^^^^^^^f^^^^^^^^^^^^^^^^^^", +"''''''''7'77777777777:7:7477474746mu&&h&hj&jQb_Lvvvv1161v1v1v1v1v1vvvv1vvvvvvvvvvvvvvvvv6v6v6##66666#6#6#6######n#n#nnRYFYYhYFYYFhhFWWF&l&&j&hhh&fv6#M9r.^.^...mm_m5m566duhhY&&hYYYWFFhFFFWWWWWWWWFx_fffffffffffffffff___R_R__________R_____________bR________fffff5f55DD5D555555)))))mm).m...mmmm555m5m55mm_mr.^^.^.^r^.r^.^.^^r^^fr^r^^^^^Lm^LLm^^^LbL^^LLLLLLLLLLLLLLLLLDLL^^L^^^^^^^^^^^^D^^^^^^^^^^^^^^^^^^", +"'''''7'77777777777:7::::474474#5bhYYYYhhhh&&j&jlu_dvvv1611v1v1vvvvv1vvvvvvvvvvvvvvv6vvLLv6vL66##6#6#6##########n#nnnrjYYFWFYYhFFhYhYYY&auj&j&YYYmz666XM.r.....m.m_55c5Xnvd&hYhhFhYFhFYYhFYFWWWWWWYWFYR_fffffffffffff_f___R_RR____cRR________f__f__________f_Rffff5f5D5f55D555555))))mm.m.........mmmmmmmmmmm.^fr.^^.^^.^^^^^^^.^^.DD^r^^^^^^m^mL^LL^^^L^^^^LLLLLLLLLL(LLLLLLLLLL^L^^^^^^^^^^^L^^^^^^^^^LLLLLLLLL", +"'''7''777777777::::4:44744744^jhFFYYYhFYYYhhhh&&jQu_L66161vv1vv1v1vvvvvvvvvvvvv6v6vv6v66666L#####6#6#########nnnnnnbhFYFYFWFFYYFFhhh&juaj&&hhFFjJv#pkJe.r.....mma_5_R5Lv66uhYhFYFFYYYFhYhFWWWWWWFYhWYFQf_f_f_ffffffff____R___R______cR_____________R_____ffffff5DD5D5D5555R55555_))m).m..........f....m^....^..^^.^^.^^^.^.^.^^^^^^^^^^^^^L.^^mLm^^^LLm^^LLLLLL5L(((((((((LLLLLLL^LLLD^^^^L^LL^5LLLLLLL^LLLLLLLL", +"''7'77'7777:7:J::44#14444444uFhYFFFFYFhFFYYhY&Ql&ujua_66vvvvvvv6L6vvvvvvvv6v66vv6v6v6v6v66666#d######6#fm##nn#nnn.xFFYYFFFFYhFFFFFFhjbbj&&&YYYYh#v#kvp*e.&YFYYhhj55555m6vvjFFhYFFFYWFYYFFYFYFWWWWYYhhFW&=f_ff_f_f_fR______bRb___________R_ff______R____fff_ffff5D5D5555555__5))5))mmm........fe.r.r.rrr.^.^^.^^.^^.^^^.^^^f^^^r^^^^^^^^^L^^Lm^LmL^mLLLLL^LLLLdd5d(d(d(d(((((LLLRLLL^LLLLLLLL^LLLLLLLLLLLLLLL((((", +"'7'7'7777:7::7:744444m1111#uWFFWWWFWFFhYFYYYYhj&jQjQQuRLvvv6v6v666666v6v6vv6vv6v6v6v66666666#n###6########nn#nn#.YYYFYFFFWYFhhFFFYWYlaQQ&lYFWhYF)6piiTpmRhYhFFYhhcR5D5Rk#^FWFYFFYFhWFYhWWWYYFWWWWYYYYFFFhR_f_f_f_f__f___R__R_RR__R___________R__R_____f_ffffcf55D555555555D5)5))))mm........e..r.e.^..^.^.^.^.^^.^^^.^^^^^^^^^^^^^^^^^LLfLL^^LmLLmLLLLfLLLLdLd((((((((((d((((((RLLLLLLLLLLLLLLLLLLLLL(((((((((((", +"'7'7777777::74:444444m_dv_&YFYYWWFWFWYFFFYYYhhhj&&j&ajabfv6v666666666666L66v66666666666666###n##########nn#nnnnfYFYFYFFFFFYFFhhFYFFY&j&jj&FWWhhFbL]TJp#)&YYhhYFhhj55ffD^djFhhYYYYYhWFYYFWWFYhYYWWYFYYFWFFhu__f_f_f________RRR_R_________f______R______ffffff55D555555555))))5)))mm.m......e.eee.^.fer^.^.^.^^r^.LrfD^^r^^^^^^^^^^^^^L^LLLLL^fmLL^LL^LLRLLLLdmdddddd9RXddd((((((((((LLLLLLLLLLL((((((((((((((((99", +"77'77777:7::4744414444114hFFWWWWWWFWFFFFFYFYYhYjjl&j&juub_6LLL6#6###666666v66v6v6v6666666####d########nn#nnnnnRYFFFFFFFFFFhYFYh&&YF&hh&h&&hhYhYYYhcR)ca&hYFYhYhhhYYlRR=lhFF&hFhYhWYFWYhFWWWFhhYhFFFYhYWFWYFuR__fb_f_f___RR_R_R_R________f___R______f_fffff5f5D555555_))))))))))mm)5mm...e..r.r.^.eR.^.^.^r^f^.^^^^^^^^^^^^^^^^^f^^LLLLLLLLLmLLLmLmLmLLLLLddLc()dddddddd(dddd()((((((((((((((((((((95((((((d(dd(d", +"77777777::47444411144444aFFWWFWWFWWWFWFWYFYFFY&&&h&Q&&uaaRf6###6######6#6^66666666666666###dd#######n_nnnnnnnuhYYFFFWWFYh&h&FFF&&hh&YFh&&&hhYFhhYYFYYYFYYFFYhFFYYhhjjhFFYhhuYWYYhWWWWWYWWWWWFFYYYYFYYYYWFFFFu___________b_RR_R_R_____fRf__________fffff5f5f5555555)5))c_5))))55)))55mmm..r.r.^.rrr.^.^.^.^.^r^^r^r^^^^^^^^^^^^LLLLLLLL(LL^^LmLLLLLLLLLLLdddddddddddddddddd(dd9((((((((((((((((((((((d(dddddddddd", +"777k77:7:744744n1114441RYWWFWWWWWFWWFWWFFFYhFF&&&hh&juabaaRL6#6#########m####666666666#6##dnnn##^m#n#nnnnnnnaFYYFFFFFYh&YYYhhFFY&&jhFYFYhhhhFWFYhYYFFYYYYYFFYhYYh&ja&hhFhhlQhhhYYWWWWFYWWWWWWWYYYYWYFhYYFFFYFuR____________RR_R_R____f_ff_a________fff5f55555555))))))_)mmm)5555555555mmr.rf.r.^.^r^.^r^^^^r^^^RR^^^^^^^^^^^LL^LLL(R(()5LLmLLLLmLLLLLLLdddddddddddddddddddddddR9d(9(d((((((((((d(d(ddd(ddddddddd", +"7U76::7:4:44444n1v44444&WWFWFWWWWWWWWWFWFWFYYYh&Yhhh&buaubbRL66#########^##n##.6J6JJ^^##ndnn###n##nnnnnnXnXuFFFFFh&hFF&hYYFF&hhFYj&YhFFYFFYYWFFFhhYFFFFhhhYYFYYYhxba&Yhhh&jhYYFYYFWWWWYWWWWWWWYYYFWFYYYhFFWFYYQR_________RRR_R_RRb__fff__________R_ff555R55555)))))mm))m.)))55555555555mm..r.^.fr.^.^^.^.^^^.^^^^^^^^^^^L^LL^LLL((((d((d(LLL^mLLLLddddddddddddd9XXXXXXdddddddd9_(ddddd9d(ddddddddddddddddddddddX", +"77::::4:444n4444v11444^FFWWWWWWWWWWWWFWWFYFFYYYh&&hhQuuQabbb_6#########6#####mRn######mRmn#_###nnnnnnnXnndRFhh&&&h&&hhhYFYYYhj&&&j&&hFYFWFFFYYWWYYFFFFYhYYhhFYY&jaujYhWY&&hhFFFFYYFWWFFWWWWWWWYFFYFWFYYhhFFYhhYQ_________R_R_R_R______ff______fffbfff5f55555)))))m.m.m..9_mm55f5555mmm_mmm^.r.^f^^^^r^^^^^.^^^^^^^^^^D^^LLLLLL(((((((d(ddLLLLLLLLddddddddddXXXXdXXXXdXXXXdXddddddddd(d(ddd(d(d(ddddddddddddXXXXX", +"7:::4:444441111v11v411bFWWWWWWWWWWWWWFWWFFFYFhYYh&&&QuluuaRbbL6#####66#6#L6#J#mnnndndndd#f_m#nn#nnnmmnnXdaFhhh&&h&YYhYYYFYhYhhhlQ&hhYFYhFYFYYhFFFhFFFFFYYWYhYF&jaua&h&YhhhYhFWFFWYYWWFFWWWWWWWYFFYYFFFWFYhYYYhFY______R___RR_R_R_R__________ffffff5f5555555))))mm.c_....mm)5555m5m5_cmm5m..^.^.^.r.^^.^^^^^^^^^^RfL^L^LLLLLL((((((ddddddddLLLddddddnddndndndXXXXXXXXXXXXXXXXdXdddddddddddddddddddddddddXXcdXdXdX", +":647444441111v1mv1v11XFFWWWWWWWWWWFWWWWWFFFYYYYhhh&lujjjuaQRbR########6#6##6#JJ##n#n#####m####nnnnnnndd(5&Yh&&&&&&hYYhYYYh&hh&&l;xxxjjQajlullahFWYYFFFFhYW&jljbbau&&&&ljjxlQjjj&&&jjFYYWWWWFYYYFWFYYYYYYFYWWFYFF&___________R__R__________ffff5f55555f555_)))m.m.m.......mmm5555mmmm_mmmm.^ff.^r^^L.^^^f^^^^^D^^^L^LLLLD5LL((((((dddddddddLdLdddndnddndndcdXXXXXXXXXXXXXXXdXXXXXdXdddddd9(ddddddddddddXXddXXXXXX", +"4:44444411v1v1v1v1v1v.YWFWWWWWWWWWWWWFWWYWFYFYFYYh&&jujuQQbbRbf########6#6#^###J#^#########nnnndndndmLLm&&&&&YYhh&h&&&hh&hh&&&&css@so$@$s@$@$@sZWWhFYhhhhWc$o$ocujhh&hb@@@@$oooo$oo$@@ChWWWYFWFFFWFYYFWFWWWWWYFFWl____________R_________f_ff55fmf555555555))mmm...........mm55mmmm_mmmmmmm.rrr^r^.^^^r^^^^^^^^LLL^LLLLLLL((((((dddddddddXdddddnddnmndXmXmmnXXXXXXXXXX.cXXXXXXdXXXXXXddddddddddddddXXXXXXXXXXXXXX", +"44n44#nd1111v111vvvv1fFFWWWWWWWWWWWWWWWFWYYYFFhh&&h&Q&uQubuRbb_#####6#6##J###J#J.#######n#nnnndnnnddLLmQhYh&hYWFY&hhhhhFYFY&&&&Iossoossssssssss$0FFFYh&hhFcosss=jhFWFYCo0ssssssssssssso0&FWFFFYYFFFYYWWWWWWWFYYWWF=________b____________ffff5f555555555)))_.)........e_e...mm5mmm._m.mmmm..f^.^.^^f^^^^^^^^^LLL^LLLLLLL((((((9dddddddd9_dXdndnddnd_dnmnXnnXnXnXnXnXnXnXXXXXXXXXXdXdXXdXXddddddXdXXX9dXXXXXXXXXXX", +"44n11111v1v1d16v1v1vv_FYWWWWWWWWWWWWWFWYFFFFWFj&&hhhuuQQaaabRbRmd#######6##########n#nnnnnnnnXnddddLLLc&hhYY&FFWF&hhYYhFWWh&&&hI000000000000000ss0FYYWYY&h00000Z&hWWWY=000000000000000os0YFYFFhYYYYFYWWWWWWWYFWFFFh_____________ff_ff_ffff55f_f555555_))))9m.......e.efee..mmmmmm.._...mm.^_.^^^^^^^^^^^RDLL^^LLLLLL((((5(((dd(ddddXXXXXXXXdndnndnXmmnXnXXnXnnnnnnnXXX_XXXXXXXXXXXXMdXXXXX9XXXXXXdX_XXMXXXXXXnXi", +"44111111mv1161v161vv1cYYWWWWWWWWWWWWFWWWFFYFFh&h&h&h&jQQQbuRbbbd######6###########n#nnnnnnnXndddnddLLf&&hhh&FFWFY&&&YYhYFFh&h&&I00000I0c0c0I00000@lYYFWFhY0000oj&hYFWWI0000II==ICC==0000scFWFFhYYYFYYWWWWWWWWYFWWWW&_RR____f_fffffffff5f55f5555555555_))).m......e.e.e.e.e.mm5mam..f.mmm....^..^r^^^R^^^LLLL5LLLLL(((((((d(ddd99ddXdXXXXXXnmdnnXnXnnn.nXnnXnnnnXnXnnniMiXnXXXXXXXX9MXXXXXXXXdXdXXXXXXXXXXXiXiXnn", +"#44111vdn1vv1v16161vvRFFWWWWWWWWWWWWWWWWYFFYF&h&&&&hhuulaaabRbRm###6##########._#nnnnnnnnndndddXdLLLL&&&hh&YWFFhhhhhhhhhYFhhhhl00000jjj&j&hYYj00000jhh&&YY00000ljhhhWFc0000jFFhYFWWWj0000oWWWWYYFYYFFFWWWFWWWFFFFFFWa___ffffffffff55f55f_f555555555)))).)........e.reeer..e.mmmmm.....m.^.^^.^^.L^^^^^^LL^LL(L((((((((((d(ddddddXXXXXXXXnXnmnXnnXnnnnnXnnXninnnnniinnnnnnXXnXXXXXXXXXXXXXXXXMXXX_XXXXXXiXinnnnMi", +"#44111111v1v1vvvv1v1vRYFFFWWWWWWWWFFFFFWFYFFF&h&&YhhhubQuabcbcb_L6##########nn##nnnnnnXnXXXddLdLLLLLR&hY&&&&hYhYYYYhYhhhYFhhjjZ00000CZjh&h&YFF00000ljl&h&l0000oZjYhhYYZ0000jYFFFFWWWW0000oWWWFYYFFFYWWWWFFFYFYFYYFWW&_ff_ffffffff5ff55f_R55555555))))).m......e.r.r.r.^.r^.mmmmm......mmm^.m^.^^^^D^^^LLLL(5(((((((((d(ddddd9ddX9XXXXXnXXXnnnnXnmXnnXnnnnnnnnnnnnenniXnXnniXXnXnXXXXXXXXXXXXXXXXXXXXiiXnnnniXnni", +"444411v1v1v161611v16vfhWFWFWWWWWWWFFFWFFFYhFFY&hhhjhjlbbuabRbcbRn########n.#n#nnnnnnnXXdddLLLLLL_^Lmu&hYYh&hhhhhYhh&hYh&YFY&j&j00000cCZ&h&YFFC00000CCj&hjl00000ShY&&jYC0000&FFFYFWWW&00000WWWYYFFYFYFWWWWYWFFYWFYFFFWbfffffffffmf5f55f55R5_D55)))))m.).._cRm..e.r.^.^.r^..b.mmm.m.^m..^mmm^Rm^^^^^^^LLLLLL(5(((((((ddddd9dddddddXXXXXXXnXn.nXnnm.#nnnnnnnnnnnnnnnnnnnn_iinnnnXXXXnXnXXXXXXXXXXXiXiXinniinXinnnnn", +"7441111v1v16161v61v1v#&FFFFFFFFFWWFWWWYYFFYhhYY&j&&h&uuRbbbbbRbR########n#_nnnnnnX.XXdddLLLLLLLLRmLbQ&&h&h&&hhh&lhhFY&&j&hYjllj0000000c0II=c000000cCx&&&Sj0000o&&Y&jhY=0000I=cC==Cj=00000=WWWYhFFFWFWWWWFFYFWWYFYYYWFYffffR_f5ff5f5f55555555))))))m9m._..._.e.r.r.r.^.^.^.^mm..m.m^.^..m^mm_^.^^^RLLLLLL((9R((((d(d(dddddddXdddXXXXXXnXnXnXnnnn#nnnnnnnnnnnnnnnnninnnnninniXninXiXXXnMiXiiXiXiXiXnniXninnnnnnnnn", +"4:441v1v1v1616v1v11v1vjYYYFWFFWYYFFWFFYFYYYYhYF&j&&jQu&ubaRbRbc_######n#nnnnnnnXXddXd(LLLLLL^LLLLL_aj&hh&&&h&&&alh&&hh&jjhlalj&00000o0o0000o000000jjj&lxS&00000&h&hYhhc000000000000o00000YWWWFYFFFFWFWWWFYhFFWFFFYYYWWcfffffffmf555_R5555)5)))))m.)........e..e.^.^rrrr^r^m.mf_m...^.^^L.^mmffL^LDL^LL(((((d(d(ddddddddddXXXXdXXXXXnXnnninnnnnnnnnnnnnnn#########nniiinnniiiinnnnnnnn_innnnnnnniiininnnnnniiiiin", +":44111111vvvvv161vv1vdbYFWYFFFFWYFFYYYFFYYYYhhFjj&au&Qjuacbcbc=5#####n#nnnnninXd(d(LL5LLLL^^LLLLL^RQ&&hhhhh&&&juj&QjjjQauQaajjl00000000000000000IjjjjljZh&00000YhhFWFY=00000000000000000&WWWWYFWFFFFYWWWYhYFFWWFFWFYWWjffffff5f55f5D5555)5))))))_).......e...^.r.R^.^.^.^.^.mmmmm^^.^^^.Lm^^.^L^LLL(5(((((d(ddddddddddXdXXXXXXXXXnXnnnXnnnnnnXXnnnnn#n#n#nnnnnnni##n#inne_nnnniXniXniMiiiXii_Mninnnnnnn#p_#n#n#n", +"4444411v1v1v11v11v1dvvXhYYFFYFYFFWWYFYYYYYYY&hYjj&ujjjjjaabRbcb^.^###nnnnnninXXdLLL(LLLLLLLLLLLLLmbuul&h&&h&&lujull&&jbaaljQ&jC0000000000000000C&h&ljlhYFj00000FYFWWWW=00000000000000IZFYYWWWWWFFYYYFFWWWYhYWFWWWFWFWFY_f5_f55f555555555)))))).)..........ee.e.^.r.^r^.^^^mm.m.m..^^.^^.^m^m^^^LLLL(9(((d(ddddddddddX9XdXXXXXXXnXnnnXinnnnnXddXXXnnn#in##########ne#nniniiinniiiiiiiiiinnnnniinnnni_p#in#i#i#n#n", +"4444411nv1v1111v1v1dvzvChYYYhFYYYYYFYYYYhhhhhhhjQ&jjjjQauRbcbcc^n##nn#nnninnXd(LLdddddLLLLLLLLdLdRbajhhYYY&uualjQ&hYY&uauu&j&S=00000CZ=000000YYhhY&jFFFWYh00000FFFWWWWC0000CZ&ZCjZZZFFWYFFWFWYWYYYFYFFWWWYYYFWFFWWFWWFhbRfR5f55f5fDDD555))))m.).......e.ee..r.^.^rr^.r^r^.^m^.m^m^m^.^m^mm^m^^^LLL((((d(dddddddddXXXX9ddXXXXXiXnnnninnnnnXXdddddXnnn##############i#ne#iiinnnnnennnnnnnnnnii_in#enpi#i#pi###i#i#", +"44444411111161v1v1vvvvvn&&hhFYYFYFhYYYYY&Y&hhYhxQjljjullabRbRbRdnnnn#nnnnnXXddLLdddddddLLLLLdLLL5bcbjh&h&&aabj&&hYWFWFjQujhh&xc00000&hYc000o0SFhYFhhWWWFYF00000WWWFWFW00000&YF&&hh&FWWYhYhhhhWFWFYWFWYWWWWYYYFWWFFWWFFhQf55ff5f_R5f5ffD555)))m..9......e..rr.^.rr.^.^^.^^..mm^.^m^m^m^m^mL^L^^LLL(((((ddddddddXXXXdXdddddXXXnnninXnnnnnnndddddddXXnn####i##i##i###e#nn_pniii_einnnnniiii#n##p##i##_rp###########", +"4444411111v1111v1v1v1vv(ZhhFYYYYFFFhhhYYhh&hF&&&jlQa&ulabRbcbc_nXnnnnnnXnXnXdLLLdddd(ddLLdLLLLLLfbRbj&&&jucRa&&hFWFWFFhhhYFYjZI00000&jYFI000soZYhFYhYWFWFh00000WWFFFWWC0000hYhj&j&hWWWWYFYFYYFYWWFWWFFFWWWFYFWWFWYYFhYhhR5f55fffffffff5f55555)m......e.e.^..rr.^.^r^^f^^^.^m^m^^^^m^mm^m^^^^^^LLL((((dd9dddd9XXXdXXddddddddXniMnnnnnninnXdddddddXXnn####p##p##########pi#n##Iiininn#n#n#ni#i#i###prp###eepip#p##", +"44m411111v41v1v1616vvvvv{jhhhhhYYYhF&hh&hh&hjj&jj&QQluabRbcbRbdnXnn_nnnXXXXd(LLdd(ddddLdLLdLLLLLfRbajj&jQbbu&&hFWFWFFFFFFFYYj;c00000hhhhh0000o0lhhFFYWWWFY00000WWWFFWW00000hY&ljjhhWWWWWFWY&&YhWWFWWWYYFWWWFFFFWFYYhYFYFbfffffff5_5555f5f5R_f5)m..e.e.e.r.^er.^.^rr^.^^r.^m.^^.^^m^m^^mLL^LRLLL(((dddddddXXX9XXXXXddddd(((dXXnMnnnnnn#nXdddddXdddnn#####rr###p########i#een#nnnXXXnn#n#ir######ip#p#p#p###p#####", +"11n1111v14v1v1vv161vvvvvv*&&&&hh&hhh&h&h&h&hhQlljhbQabbbcbcc=f#nnnnnnXXXXXXdLLdddddd((dLdLLLLLL^_cbujuuQubaljhhFFFFWFFFFWWWYucI00000hhhh&&0000s@ZYFYFFWWWj00000FWWWFFWC0000hY&Zj&YYWWWWWF&&&&hYWWWWWWWYFFWWFFFWFFYhhYFYF&Rfff5f55555555f5555555mm...r.er.r_^.^.^r^.^^r^.^mmLm^L^^^^mff5^^^L5LL(((d(dddddXXXXXXXXXXXddd(d(d(dXXnnnnXXXXXdXXXdndXXdnn###p####p####pi##i###i#nnn.XXXXnn###pfni###pppppppppppppppprr", +"111111v41v1v1v1vvvvvvv6vvv9&&&hhh&&h&&&hh&&&&bjj&jbbbbRbRbRbRXnnnnXXXXXXXXdLLLLddd((((LLLLL5^^^RRbbuQuQjuaj&&YFFFFWFFFFFYFFhacc0000IhhhhYhj0000@@hFhWWWWFh00000YWWFWFWI0000&&ljl&hYWWWWFhjj&hYYWWWWWWWFFFYFYYhYh&hYYYYYYWfff5f555)))m)5mm5m5m5555mm.ee.^effrrr^rr.^.f.^mm^^^^L^^^^m^^^^^LLLLLL((ddddddXXXXMXXXXXXdd)d(d(dddddX_inXdXddddndXnddndXXn##pppppppp#p##p##p#####nnXXXXXnncen#pi#fppppppppppppppppppppp", +"1z11v111v1v1v1vv1vvvvvvv6kJ0&j&h&&&&hjjh&&&j&j&xxbaabRbcbRbcmnnnXnXiXXXXXX(LLL(dd((((((LLLL^^^^Rbabu&h&jujQhFFFFFFFWFFFFYFFj;Ic0000IhhYh&&jj0000sohYWWWFh&00000hFWWFFWC0000ZSCjhFYFWWWFh&&lYFhYWWWWWWWWYFYhWhj&&hFFFYFFYYbff5)5)))).)..m...mm55mm5mm..^.rer^.^.^.^..^.m^m^m^^^^^^5Lm^.^^^LL(5((((dddXXXXXXXXXXXnXdddLddXXndddd.ddddXXXXmdddnnddndnn#pppppppppppr##########neXXXXnXnnnnn##iipJJJJJJJJJJJJJJJJJJJJ", +"z1z11v1v1v1vvvvvvvvvv6v666669ljj&&&j&&&&jZ&Z&Qjaxuubbccccbcmnn.nnX.MXXXXdd(^5L(((((((LLL^^^^^^Dbxauj&Y&QbauYWFFWFFFFFFFYYYYQcc=0000IhY&&jj&jC0000olYWWFh&&0000=YYWWFFF=0000Z=lhYWWFWWWhjl&&WWYYYWWWWWWWFYY&&QjhhhYFFFYYYhR_5_5))cm.9......_..m.mmmmmm.rrrrre^r^r^mf^fm^mL^^L^LLLL^^mL^^^L^LL5(((dddXdXXXXXXXdXXX.Xd(ddnnnnnXXXdXXXnnnnXXXXdXmnddXmnn#pppJppppppppp#p#p####nXdXdnXnnXdXnnnn##JpJJJJJJJ{{JpJpJpJpJ", +"z1v1v1v1v1vv1v1vvvv6vv6v6vk6kD;ZjS&hjjjlj&jQljabac=bRbRbccmnnXnXXXXXXXddddL^5L(((((LLLL^^^^^^r^luu&hhh&ubauYFFFFFFFFFYYFYY&=RClZj&j&YYhhj&&&&jjjZS&YFYh&&YZj&j&YhYWWYW&hZjC=lYYFWWWWWWjZjhWWWYhFFWWWWWWYh&&QlYFYhFYWFYYYhxff5)))_mm.......e....mmm5mm...^.rr^.^^m^fffm^^^^^L^L^LLLL^Lm^^^^^LL(dddXXXXXXXiXnXd.XXdddXXnnnnnnn#nXnnnnn##nnnndnndnndnXXn#JJrJJJppppiRpppprp##nXXXXXnXXXnnn.nn#pJJJJJJJJJJJJJJJJJJJJ", +"zzzv1vvv1vv16vvvvvvv6v666666k**blj&llZjjljlljxuauaccbcbRRm#XnXXXXXdddddd(LDDL((((((LL^^^^^^^r^c&&&&&&h&ubQjhFFFFFFFFYFYYYYQRcC&YFFFhFFY&Y&&YYYFYYhhhhhjYFWFhhYYFhhYWWWFhYY&jhFFFWWWWWF=jhWWWWFYYYFWWWWWY&&hjhWFYYYYWYYYFhj55m).).9........ee....mmmmm.^.^.^^r^^.^.r.^^^^L^LLLLLLLLL.L^^^^L^^L(ddXXdXXXiXM_idddd)dddnn#n#n#####n########nnnnnnnndnXnXn##prJJJJJJpJppppprp#nX._XnXXmnnrnneXnnJJJ6JJJJJJJJDJJJJJJJJ", +"v1v1v1v1vvvvvvvvvvv6v6v6k66666JJ9;xxlCuSSxax;abaccbcccc_mnnXXXXXdXddddd(L^^LL(((LLL^^^^^^^Dr^.R&hhYhh&hQQ&j&FFFFFFFFYFFFYhuRSZhYYFYYYFY&hhYYhhYYY&hj&&&FFWWWFFYFFYYWWWYYWh&YFYYYWWWWFZlYWWWWWFYFYYWWFFh&&hYhhWWFYYYFYhYhYh55)m9.mmm.....ee.e....mmmm...^..^r^^^.^m^m^.^LLLLLLLLLLL5LLLm^^^LL((ddXXXXXXiiiindd(ddXXn#n#n######################n#nnnXnnn#pJJJpJpJJJpJJpppp#nnXXnnnnn#n#nn#nn##JJJJ##pJ66J]J666666*", +"zvzvvvvvvddvvvv6v6666D66k66JkJ6{##{I;;;x=auabccc=cccb_LnnXXXXXddddddd((L^r^^L(LLL*^^^^^DD{r^r^a&&YFF&h&jhh&hhFFFFFFFFFYFY&bS&jhYFYYWYFFYhYYFFFYYh&jjhhhWWFWWWFYFWYhFWFFWWhYWYWFYFWWWhlhWWWWWWWFhhYFW&QjhYYFYYWFWWYYFYYYFFYfm5)m))....e.e..r.e......m.^..^r^.^r^.^m^.L^LLLLL((()_((5LLLLL^L^^L(dddd9XiiinnnXXdXXXn##############ppppppJJ#####^###nnd#####JJJJJJJpJJJJJJJ##nnnnnXXn########nn##Jp###rJJ6666J]6J]6J", +"zvv1vXvvvvdvvvvvL66JJrJJJJ]6J6J#nnnn^RRb=b==cc=c=RR5dnXnXXXXXdXdddd(LL^^^.^^LLL*L^^^^^^^r^rr^mj&hhYYh&&jhYh&hYFFFFWFFFFYh&bjj&FYFYYFWYFYYWYWWWWWhZS&Y&FWWWWWWWhFFFYYWWWFFFWWYFYYWWWh&hWWWWWWWWFYFYWhuahWFYFFFYWWWFFFYYhFFY5c55))m...e_feefr.r.re^.^.....m^.^.^.^.^m^^LLL(((((d(((d(dLLLLL^LLLL((dddXXXnii_Xdddnnn########p#pppppppJJJ#JJJJ6J^JJ#nn#nJ#ppJJJJJJJJJJp{JpJ##nnnnnnnn#######r#n######rr###JJJ666k666", +"vvvvvvvv66vv6v6666######J{DJJJ##nnnnXXnnLm.f__mmdXXnXnXXXXXddddddd(L5^^r^^^^L^L^^^^^^^r^rrrr.R&YYhYFYY&j&hhh&hYFWFWFFFFh&lujjhYYFYYWWhFYhFWWWWWYjZj&YYWWWWWWWWYFWWFYWWWFWWWWYYYYWWh&hWWWWWWWWWWYWF&QbjWWFYWWFFYWWWWYFYYYhY555555)..e.ee.eerr^rrrrr.^...^m.ff^.^^^mL^LLL((d(d(dddddd(dmLdddLLLLLL(dddX.XXXXXXdnX###p#ppppppppppJJJJJpJJJJJJJJJJJ##d###JJJJJJJJJJJJJJJJ###nnnnnn###JJ6JJJJJJ##n##nn##rr##JJ66666k6", +"vvvvvvv6666666666###nnen##JJJp#nnnnXXXnnnXddX_.nnnnXnXnXXXXddd d(d(L^^^f^^^L^^^^^^^^{r^r^rrf^^hFFYFFFYh&FFjf#f^R5Rf_v))eDLXJ900I0000I0I00000c0IPoo0 0IIcI0=0c0000II0IcIc0Ic0sIIocIIIc cR_=;c=IDf.(nX(D_=5__D_aFWWWWWYYhYYY_55m555m..e.e..r.^.^.^^^^^.^^..^.^.^m^m^^^LLd(d(dddddddddddLddddXXdddd(dddddXdXXXXXn#cpppppppppJJJJJJpJJJJJJJJJJ^JJJ6##nn#f####JJJJJpJpJJp#####nnnn###JJ66666666JJJJ#J#######JJJJJJJJJ", +"vvvvvv6v6666########n#nn#n#rDinnnnXXdnnnXXdXXdnnnXnXXXXXXXdddddd(((L^ff^^^^^^^=^^^{^r^rrer.f..&hFFFhYYh&YW&77'''''''''''''''''''778788888787787''''''''''77878788777888888777'7''''''''''''''''''''''''''''''5YWWWWWWYYhYh5mmmmmm)m.eb..r.^r^^^r^f^^^^.L^m^.^mL^^^^LL(ddddddddddXdXdXXddXnXXXnXXddddXXXnXnnnn#J^JJJJJJJJJJpJJJJJJJJJJJJJJJJ6J6J6#n#n.#nnn#####n######nn#nnb###J6J666v6v6v666666JJ###############", +"vvvv666666###6#######nnnnnnnennnXnXdXnnnnXddXXdXXXnXnXXXdddd d((d(LL^^^R^^L^^^D^{{rrrrrrrrrrD.YhYFF&&hllYFjbuxx=aac=bZbZSCbaSa;;C=C=SxxSxZSCZSZQQullllSljlZSSSSuSZS=lSSbSZxljjulQllQuQaQujQQaQQalQQjuQcQQlujlfYWWWWWWWYYYY_...e..mm..r^..^.^r^^^^D^^^^L^^^Lm^^^Lm^L((ddddddddXdXdXXXdXXnXXnXnnnnnnnnnnn#n#n##JJJJJJJ{JJJJJJJJJJJJJ]J666666J6J6J6##nnnnn#nnnrnnnnnnnnnnnnn####JJ666v6k6kvkvvv6666J#######JJJJ6JJJ", +"666666J####6#666#J####nnXXXnnenXndXdXXXnXXd(dddddXXXXXXdddddd(d(((L^^r.^^^^^^^^{r^r^rf^.r.r.e.&&&YY&&jua&FluuaaabaaSxQZu;bbaS;aSxaxSuxlul;xZlSlulSSQQjjZllSxxuuZZuuSjlSSujlljllQQjlZQljujljuQQQllQQujjljjlljjIYYWWWWWWWWYYfrre...mm.^f.r^.^^^^^^^^^^^^^^^^^^^^L^LLLddddddddXXXXXXXXXXXXXnXnnnnnn######J###JJJJJJJJJJJJJJJJJJJ]J66J6J6JJJ]666J6J6J6##^nn#n#n#n#nnn#########6666DLD 6vvvvvvkvkvv66J6J6JJJ6JJJ66666", +"66J6J####J#6666J66J6J#nnnnXXXXXXXXddddXXXXddd(d(mdddXdddd d(d( (((^^^.^^^^^^^{r^rr^rrrrrrrr.rm&&hh&h&laajhRbbbbaQ;QSuQZQb;a;;xxxaaxxSalZxxZllaxZQuSujQjlZlSlSSuuuSZjjjQljQQlQQQlljQQlQujlQjuQQllljjjjjjjjjlQQ)YWWWWWWWWWWFD.r......e.^rrr^^^^^^^^xLLL^Lf^^LLLL^5LLddddddXXXXXXXXXXXXiXnXnnnnnin###ppJJJJJJJJJJ6J666J6J66J*666J6J6J6J6**666J66666666Jr#Jr#######J###J#J#JJJ^6v6Lvkvvkvv6vvv(vvvv6v6666666L66vvvvv", +"JJ#####J6666J6#6JJ#JJ#rrnXXXXnXXXXdddm.XiXXdd((L((ddd(dddddd((((((^r.^^^^^^^{^r^r^.^r.r.r..r..hhYYh&&&bbj&RbbbauaxxauZZS;bQbaxxxaaxxSaZlxaZxSxxlxuuQlxSxxSSxSxSSuSujljljllljljjjuZQQQjjjQjjjQQQQQQjljjjQjlllQ9FFWWWWWWWWWW_rr.^.rf_.^r^^^^^^^^^^LLLLLLL5LLLLLLLLLddddXXXXXXXXXXXiXiXXiiininin####pJJJJJJJ{J6J6{6J]6J6]6J**J6J6J6J6]6666666kR]6666J66666J6JJJJJ6J6666JJ^666D66vk6vvvvvvvvvvvvvvvvvvv6v66v6vvvvvvv", +"##n###JJJ6J6JJ6#JJJp#pr#nnnXdddddddLddXXXXXXXdd(LLLL((( d( (d(((LL^^^.^^^^^^r^rrre^.rrr.r.r..fYhh&h&&&abQ&RbbbuxxSaxuZxab;;bxaxxxxaaxual;lluuaauuuljljuSuxxuSSulZuujjjlluululQuZjZjjjjlluQjjQlQlljjjjuQQllllQ9YFWWWWWWWWWW_r^...r.f..^^^^^^^^LLLL(((((L(LLLd(dLLdddXXXXXXXXiXiXiXiiiiiinni#n#p#ppJJJJJJJ66JD**{666666666666J6]6]666***kJk6k66666666666666R6666666k6666*6D66k6vv66vvvvvvvvvvvvvvvdvvvvvvvvvv%R vv", +"r####JJJ6^^J#J#J#p#pp#Di#nnnXd((((d(dddXXXXXdd((LL^^L^LL(d((((9DL^r.^R^^^^r{rr^rrrerr.r.r...ebhY&&h&&&u_u&RQauuaSauuSQuba;baxxxxxaa;lQSuauxuuxaSuQlQjllljjSxQxSjuuujjjQZSZuuZuuujljQQQjjlljjlQQuQjjjQljjljQlQ9YWFWWWWWWWWWRr.^.^.^.^r^^^^^^LLLL(L((((((((dmdddLddXXXXXXXiXiXiMiiiiiinnnii#i###ppJJJJJ66J6Jk666666666666J6J66666666**66666666k6k6k6k6k6k*k6k6k6kv6v6 DUv(Dk6v6vkvv6vvvvvvvv vvvvvNvvvvvvvNvv9Nvvv", +"nn###JJJpppJpJpppppiperiiiiinnXXXX(d(dddXdddddd((LLL^^^^5LLLL^^^^^.^r^^^{^^^r^e^.^.r.r.e.e.._c&&&&&hYY&RuhcuuabaQxaxxQxbu;axSxxxa;;lauuaa;xuxxxlululluSluxuQxuuSSuujZllSuuuZujQljjjljjljllljjjjljjjjllljlllQQ9YYFWWWWWWWWWRRfm^..^..^^^^^LLLL((((((d(dd(dddddd(dXXXXXXiXiiiiiiiinnniiiii##p#n#JJJJ6JkJJJJJ666666kkk6*k6k6k6]666666666k6k6k6k6k6k6k6k66*(*6k6v6v6kkv6vvkvk66vvvvvk9vv9vAvAdcvAAvNvvNvNvNvvAv vAvA", +"nnn##pJJppppppp##i##ireiiiiiiiiXXXXdddXddXMddd((dd(((L^^5*L^^^Dr.r^^^^r^r^rrrrr.rr.re.r......c&&&&&hFF&u&&RabaxaQubxQu;aub;xxxxuaalZQaaaaaxSuSxlZluulluxSxSSuZSuuSulZuSuSuuujQllujlQQQQuQQQllQjjjujjQljllllll9YYYYWWWWWWWWme^.^.^.^^.L^LLL((((((((d(ddddddddd(dXXXXiXiiiiiiinnniiii#nnXninnnnn#JJ6JJ#####JJk6JJJJJJJ666666666k6kk6k66666k6k6kRk6k6k6kkv6 6vkkvkvvvkvkvvvv6vvvvvvvv vAvAAvAdAdAAvAvAvAvAvAvAAvAvA", +"Xnnn#pDpppp#pipi#riiiiiiiiiiXXMXT_XXddd ddd (99d(((((L^r^^^^^r^.r^r^^^^r^rrrrrrrr.re.e.e....mb&&&&h&hYj&hYbuabaxuuaxuxCub;xxxuxaauull;axZaxuuxxxljuujjxSuuuQxuuuQuSuZQuuQQZllQjjQuuuuuuuQjljjljuQQjljQlljljllDFFYYWWWWWWWWR.^^^^^m^.^^L((((((((( ddddd(ddXdXdXXXiXiiiiiinniiiin####nXdXdXXXn#n#JJ6J#######J6J6JJJJJ{6k6k6k6k6*66666k6k6666v6666v6v6v6v6v6kvv6vvvkvvvvvvvvvvvvvvAvzvAvAvAATXAAAAAAAAzvzvAAzvAAAAA", +"Xnnp#ppipipi#iiiiiiiiiiiiXXXXXXXXdX dddd(d(d(((d9(((((^^r^r^.r.^^r^^DD{rr^rrr.r.r.e.e....M..mQ&&&YFYFY&&YY_uaaau;Z;Sxbbaa;aaxxaa;lu;Qa;QlluaaaxalZxjuxuSSuuuuSuuuuZQuuQuQQuujjljuZuQuZuQljQjlQQllQQQjQQQlQlll9FYYWWWWWWWWFDLL^^^^^^^R^L(((((ddddddd ddXdXXXXXXXiiiiinniii######i#iinXXXXXnnXnn#r#J###JJJJ##JJJJJJ#r^J66k66666*k6k6k66JJJJJ6J6666666kvkkvvvkvvvvvvvvvvvvvvvvvvNvvAvzAAAAAzzAzzzzzvzzvzzAAvzAAvz9z", +"enipiDipiiiiiiiiiiiiiiXXXMXXXXXdd ddd (d d((( (((((I((L^^.^rr^^r^^^r^^r^rre^.rrreee.e.....9m5Rh&&&YYYhhYhh_bab;aSaua;b;Cbaaa;ZZQZxaa;;ZlQQuulQuljuuuxuuuSuuxuxuuuuuuZQQQQQQuuQQlZuuljjjjljjjjQuQQjlllllluQQQQcWWWWWWWWWWWWRLLLLLL^^^^((((d( (d(dddddXdXXXXXXXXinecniee##rrei#i#nXnXXXnXnnnne.nn#####J666J###JJ6JJ##JJ6J6k6k6k66k66*66JJJ#JJJJJJJ66666vvvkvvvvvvvvvvvvvvvvvv9AvAAAAzvzvzzzzzzzzzzzzzzzzzzzzzzzzzz", +"XnpiiTiiiiiiiieiiieXXXTXXMX Xd ddd9I(d(((((((((*(*(LL*LL^L^r^.^^^rr^rRDrrrr.re.....e..M.9.)))uh&&hYhYYFhhh_bbxuxxQab;bbb;;;;Q;;CS;;aaZxaxaSaauujQljSuuuxuuxuSSSuuZuZuQuQQQlllQulljjjjjlQjjjljuQlQujlQlljj&jjjeWWWWWWWWWWWWbL(((LL(LLL(dd(ddddd dddXdXXXXXXTXiiiin#i##iri###p#iinXXXnnnnn######nnnn#J66666JJ##JJJJ#JJJJJJ66J6k6k6kk66JJ#J#J6J6JJJJJJ6*6kvvRvv vvvv%vvvNvNvNvA9AvAAzzzzzzzzzzzzzzzzzzzzzzzXzzzzzzz", +"iiiiiiiiiTiiieeTXXXTXXXdXMM_ d(ddd((((((9( ((*((LL*L*L^*^^^^^^^r^r^rrrrerr.rr.eeee......)9))5ch&&hFYYYh&h&RaubaSSab;bbb;;abxux;uZu;xaal;xSuSxxulSjxuuSSuxSxuQQjZQuuQuuQlllllQujQjjjjlQQlQlllQQuQlQjjj&ljjjjllRWWWWWWWWWWWW=(d(dd(_d(dd(dddddddXdXdXXXTXXiXiiiinii#i#ipr##i##nnXXXnn#######pJJ##rr#{J66v6k666J##{{#JrJJJJJJJ666kk6v66{JJJJ666666JJ6JJJ66vv%vv(vvvvvvvAvvvAvAAXAAzzzzzzzzzzz1z1z1z1z11zztztztzzzzz", +"iiTiiTiiiXTXiXXXXXXXdTdX dd ddd ((d(( (*(*L*LL*L*L**^**5^^^^^r^rr^rrrrre.re.e.e.....9.99))95cuhhh&h&hh&hY&Raxxbbb;;bbauubaSuQluSlxaxxaaaxSuuSxQQlSxxxuuQllQjjljSSQuuQQuQQQQuQQljjjjljjQQlQQuQQuQQQQQQlljjjjjlrWWWWWWWWWWWWfdd(ddddddddd ddddddMdXXXXdXXTXiiiiii##ip#p#nnnnnnXnn#n#ppJ##pJJJ6J6JJ6J6*k6kvkv66JJJJJ#JJJJJJJ{J66kv6vk666JJJJ66666J6J66666JJ6v6v9vvNvAAvvAAAvzAAAzzzXzzz1z11z1z1111z1tzt1t1tttzzzzzz", +"iiiiiiXiXXXTXXXXXX Xdd d(ddd( (( (( (L((*LLLL***L*^^*^*D*D{{r^r^rrrrrr.ree.e.e....M..b9)9)555u&hhh&hhYYFhjRbbabab;bbxu;xZZQuQZxuQaxaaaaxSuuuSxljlZaSlxxluQlljljxuuuuQuQuQQQQlQZujjljjlQljllQQQQQQulllQjjljjlQDWWWWWWWWWWWW_dddd dddddddXXdTXXMXXXTXXMXXiiiiiii#i#p#pnnXn.XXXXn#ppJJJJp#JJJJ6k6k6*6k6vkv6v6v6JJ#JJJ6JJJJJ66666kvkvkvk6666J6666J6J6666v6v666666vvvvvvvzvAAAAzzzzzzztz1z1zt111-1t1--1-111I11iz1zvzv", +"iiTXiXXMIeXdd X X dd ddd ( (((((((*LL*L*L*9*^*^^^*^*^^{^{^{^rrrrrrrr.ree.e.e...M..99)m)_55555uhh&hYYYYYh&Qcbbbbb;bbauaSQZu;uC;lQuuxaS;axSuuuuxxujuajQlSljjljjlZuSuSQQQlQQQuQQQQQQlZjjluuQlQQQQQQQQQQuQQjjjllQIFFWWWWWWWWWWRcdXdXX XXXXXXXXXdXMXXXXXiXeiiiiii##ipppiinXnXnnn#n#JJJJJJJJJJJ6k66*66 kvkv6kvvk666JJJ666JJ6JJJ6k6kv6kvvvvvv666D6666J6^6vvvvdvvv666v6vvvAAAAAzzzzzzzz1t1tt11-1--111111111--11--TzzzvXv", +"iXXXXTXXXdXXd(ddddddd (d((((((((*(L*9L**L*D^*^*^{{{{{{{{{{rrrrrrrD.eeee.e....M.9.9)9)95959D55&h&hhhYYYh&&QbaaSxxaabuauabCaa;;SSSu;x;xaaxxuuuuuxxaSQlljQjQljllljuuQQuullQQlllQlQQllujjlQuQuQQQuuQljjlQljjjQjuj6WYFWWWFWWWWW9XXXXXXXXXXXTXTXTXiXXTXXiXieiiiii#pippppnnXnn####p#JpJJJ]R]6JJJJk6k6k6k66vk6vkvv66^#JJJ66666JJ666vkvvvkvkvkvv66vk666666vvNvvNvvvvvv6v6vvvvvzzzzzzzz1tti1-1---114---------1111111zzvzvz", +"XXTXX XTIX d9(( ((( ((d (9( L*(*(*L*IRL*^**^{{{{{^r{{^r^rrrrrrreeeee..e...M..9.99)9))595D5D5DYYh&&&&&&&&&abuxxxxbQx;SC;a;Ca;;uuulxaZaaaaSuuuuSxxZlQlllljlQxxSuSQlllQQQuQQlllllQllQQjjljjjjQuljjjljjjlQjjjlljlDWYWFFYFWFFWWbXXXTXXXTXiiXiiiiiXiiiiXiiiiiiXiii#pp#nnnnXn#pJJJJJJ]J]JJ6J6k6Jk666k6kvkvk(v66vk66JJJJ66J6J66666kvvkvkvvvvvvvvv66666666vvvAAvAIvvvvv6vv(vvzzzzzzz1ttt1---1111--44444444444g----1zzzzzz", +"TXdTdXddddd d(((( ((((((*_IL*(*L***^^^*^{{{{{{{{{r^rrrrrrrrrreeeeee.ee..M..9)99)))95595D9D_D5WF&h&&&&&h&&abaxa;bbauub;bbC;alQQllQ;l;aa;xSxxxxSxSZQZljZulxxSuuuQuQQQulQlQllluujjuQQQZuuujjljujllQljjjjjQjjjjQlcWFWYhjhFFFFWcXXiXiiiiiieiiiiiiiiiXiiXXddd9ddXnnninXXXnXn#pJJJJ]JJJkD*k6]6kk*k6k66k6vkvvkvk666JJJ6JJJ666666k6vvkvvvvvvvvvvvvv666v6vvAAAAvAzvzAzvvvvvvvzvzzzz1ttt1--i14-1g444g4g4g4g4g4g4414111z1vzz", +"XdTd d d ddM( ((((( ( L(*(*L**L*^*^*^{{{{^{r{^rrrrrrrrrrrreeee.e.e....M.M9)55)55D5DDDD5DDDDDDFWY&h&h&&&&&aaaxabauQuZZZQZb;bZuuSQSQaaaaxxaSxxxaxuxuuQluulluuSlQSuuuQujljuQujjlQujjjQlljjQljluQlljjQllQjjjjjlQQRWWY&jhhYYYYYRXiiiiiiiiiiiiiiiiXieiXiXdd.XXeXXXXXnXnnnnnnnpJkJ]6Jk6Jk6]J6k66k66J666k6vkvvvvkv66JJJJJJ6J66kvv6kvvvvvUvvv%vvvv6666vvvAvAAvzAAvXvAvvvvvv1vz1z1tt111-11p-44444g444g4g4444444-1-11z1v111", +"X dddddddd( (( (( (L(*(*L***^****^{{{{^{{r{rrrrrrrrrrreeer.r.ee.e..MM..99)D_DDDDDDDDDDDDDDDRDFFFh&&hh&hhh=axabbaaZuSuxQalQbCZQQZux;aaxaaxxZuxaaaQluuuuuQjSxulQSxuuQujQlQuullQjllQQljjjjuQlluQljuljjQjjluQuQujuWWhjjhFWWWWW_iiiiiiiiiiiiiiniiXXXXXXdXXiiiiinnnnnn#pp##nnpJJJJ6kJkk6k66J66kk66666666kvkvkv666JJJ6J6JJ666v66v6kvvUvv vvv%vvvv66vvAAAAAXAvAXvzvvvvvvvzzzztt111--4--4g4g4g444g484g484gg441111z1z1z14", +"ddX d d ( (9((( LL*(*L***L*^*^{^{{{{{r{rrrrrrrrrDDeeeeeee.ee.e...M..99.9)5D_DDDfDDDfDffffffDDFYFh&&h&hYFYRaaabbabbZ;aa;ub;;;blZQZC;axxxxxaZQllaSjljlluSQluSxSuQlSSSuQlulQjjjuujluljjjjjjjjuuQuuujjllQjQllllQjRWY&u&YFWWWWW=niiiiiiip#pp#iXXXXdddXXn#p##ppp##pp##JJJpp##JJJJJJ66k6k6k66k6vvkkkkk6vvvkvvvk6*J666666J6666k6k6kvvvvvvvvvNvvvvv6v6vvvAAzzAvzzAvvvvzvvzvzz1z1---111-44g444g4gg48484884844411111zzz1114", +"d d d( d(( (( (*(*L****^**^{{{^IR{{{{rDrrrrrreeeeeeeee.e.ee.e.M.M.999))55D5DDD5DfffffRDffff_cFhY&&hhh&&YFRabbubbbCx;ZubCCbbxSxZlSu;;xxSaxxalujQulllQllljjjjlQljjlSuuujjQljuQQQuuuuljjjjjuQlllllQjjjjjujlljlQj9Yj&j&hYWWWWWRi##ppppppippinXXXXnini#ppippppppppJJpJ6JJp##pJ#pJJ6k6kkk6kkkvk6vkvvvvkvkvvvk(6J666666DDJ6666v6vvvvvv%d vvvvvvvvvvvvvvAvzzz1vzzzvvvvzvzvzzz1t--1-zt1-14444484:48484848441111izz1111148", +"ddd( (d ((((*L*(****9*^*{{{{{{rr^rrrrrrrrrreeeeeeee.efeMeR.M.M.M.9.)55DDD55955DDDfffDfff____cFYY&hY&h&&&hRabubbbbbuSCb;bbu;CuaQxuu;;xxaSxauluQjljljZlQjjlQQjjjllZuuSuulljuQQQQQuuljjljlZQQlllQQlQujjjlQljllljTjjl&YFFYYWWWRripipipppppp#nXnnnpDpppppppppJJJJJ]JkJkpprDr#pJpJJk6k6kvkv6vkvvvvkvkkvvvvvvv66666vv666k666kvvvvvvv%vAvvvvvvvvvvvv6vvvXzAzzzzzvzvzvzv1vzvzzzzzzzzzzzzz1t11444888848848441111i111n44448", +"d ( d( (( L *(**L*^*{{{{{{{{r{r{rrrrrrrereeeeeee.e.eMM..Mf..M9999)955DD5DD9D9D5DDDfff_f_ff_faFYY&YYh&hhhhRbbQbbbbbu;CQZuxuQxQSQaQu;;xSxSxSuuxZlQlllljQQjjjQxxxluxuxuSQjjQljjuuuQuuZljjZuulQluuQQuljjjQQQQlllj{j&&hFWFFFWYYrpppppppppppi#nnppp{{{]p]JJJJ]J]J]66*6kJJJp*JJJ66J6kkvkvk(kvkvvk%RvvvvvkkvUvv66666v6666666 v6vvvvvvvAvvv6v66666vvvv6vvAAzzvzvzzzvzvzvvzvzvzvvzzzzzzz1zzzti4g44444:8484441114444448:884", +"(d (( ((*(*L**L***{{{{{{rrrrrrrrrrrrrereeeee.e.Me.M.M.MMf99M9.9.95DDDDDD5D55D5DDDDfR__f_____QFFh&YYh&YFFYfaubb;bCZbCQuxuZQCCZSZalQa;xxxxuZlSlSSjSuuQluljQZjuSSllllQjljjjljQjuuZQZuQjjljjQlQQjjQjljQj&uQQllQQlMYhhFFWWWFhYYR]pppp]pp]JJ]#nnJJ]J]JJJJJkJkJ]6*6]kkkkkJJJk6k6k6k6vkvkvv vvvUvvv v 9kvvvvvvk66L66666666669vvvv%v%vNvvvvv66v(vvvvvvvvvvzvzzvzvzvzv1v1v1v1z111zz111111111zi14444884444441114484488848:8", +"( (( (( *9(*L***^{{{D{rrrrrrrrrDDeeeeeeeee.eMMMfRM.M.M.99M9)99)9D_DDDDD5DD9DD9DDDffff_______aFh&&YYYhYWWFRxxS;bbZxbQSSxxC;;CluQSZQQlaxxalQSllauQuljllljQulxuuululjlljljlljQQQuQQullljlQluQjjjjjjuujjjQujlQQulRFhFFFWFWWFYYIpp{*pJ]J{]JpnnpJ]J]JJ]JkJ]6]6k**k6k666k6k(6T* IivvvvTvkvvvUvvvv(IvUvvvUvvUvvv6k66666vvvv666vvvvAvvvvvv66(vv6vvvvvvzvvvzvzvzvzvzzzv1v1vz11111144444111111111114444g-D4411448488:8k8::8", +" (( L(*L(I9****{{{{D{rrrDrrrreeeeeeeeee.eMMM..MMMMMM.9999999)99)5DfDDD5DDDDDDDDDDfffR_______uFhh&hYYhYWFW_SxbuSbSSaZub;;;;;aZZlSxaaxu;a;QZllaxujZllZluuulSuZSQQljuQuQuQZjjjQjuZujjjjjljQQllQuuQZZuuljluulQuljbFFFWFFWWWWFFf]J]JJJJJ*JJ#nnp6666k66kkkk6k6kk6k6kkUvkv MD9I99*Uv9j&RTT*] vv%vvvvvvvvvvvvvv%vvvvv66vvvv96v(vvAvAvvvvvvv vvvvvvvvzvzvvvzv1vzvz1vvzv1v111144444g4g4g4414111111144844]-441444]8]::::8::", +"(( ((* (******{{{{{rrrrrereeeeeeeeeD.eMM.M..MMM.9.999999)9)99595DRDDDDD9DD9DDDDDDfDfIff_____jFY&&hYYhFFWFcSuSCZZaZCZCxC;;bxaZZulQQ;allQQQllQxauQlaxxSaxxSSZuSZljjxuuQjllQjjjjllljjjQjZuZQQQuQjjlQQQZljjjjjjljRFFFFFFFFWFWWR]J]J]JkJ]Jp#n#p]kkk6kkkkk6k6kkkkvkvvkXII9D9ID9Mpvv&&hu {{r r%v%%v%%vN v%%%%vvv%vvvvvvvvv(vv6vvvvvvv(vvvvvAvvvvvvvvvvvzvzzzzz1zv1111v1z1444g4g848488444444444411444414441444J8:::8::::", +"( L* L**L****{{{rrrrrrreeeeeeeeDMeMDMM.M.M99M_9999999)999)95595DDfDDDDDDDDDDDIDffIffDfIf___f&FY&&&hhYhFFFRSuu;ZuxaSS;ubC;;bCQQlSuZ;;ZlZlQaxZaSZSQxSSxSxxSuuuSjljZuSSlQQjjljSQZljjuuuuuZuuZuuuQZQQQQQQjjjjuQlj WWWWFWFWWFFWS]6666kkk6JJ###J6k6kk6k6 (kvkvvkvkvU9=IDIM0ID[ v%v&j&&r9[D[MrpTvNvvAvXNvvAvANNvAvvvvvvvAMdvvvvvvvvvvvAAzzzzzzvvzvzzvzvzzzzz1zz1v1zz1114gg84g:488848:g4848484411414144144448]:::*::::*", +" L (L*9*****{{{rrrreDeeeeeeeeMMMD.MMM.M99.M_9M99999)995=95959D9DDfDDD9DDDDDDDfDDDfDfDRfD_f__jFY&&hYYYYhFYR=;u;aZZ;xQZSu;;;C;CuluQZZZQZQxxxSaSxSuuaSuSxxuuuSuxjllZuSSSSuZljjjjuuljjQZQZQZullZQuuQuQQQjjjjjjuQZDWWWWWFFFWWWWR6kkk]6]6kkJ###Jkkkkkvkvkk9vvvkvvv9xbIRIID9D TekvNTljlc{[* 9D*** vNNXNNvNAvNvAXAAAAvvvvvvvvvvvvdvvvzzzzzzzttzzzzvzzzzzvzvzz1111z1v1z444g4:4884884:8::88888:484414444444448]]k:::::::::", +"L*L* *9****{{{rrrrreDeeeeMMMMeMMMM.MMMM9_M9999999999595_D9D9DD9DfDDDDDDDDDDDDDIDIfDIDDDDD_ffjFFY&&hh&YYhYRb=ub;x;a;xaxSabbb;;CuuQaaaQxxSZxSxxxaxQuSuSSuxuSuSjulllSSuuuZuljQjjjQlZQQuZQlQZljjlQjjjjjljjjjjjjuQIWWWWWWFFFFWW=kk6k6k6kk6JJpJk(kvvkvvvvUUvUvvvT=&=IRI9DI909D*NMMkIDk%kTM[9D[9 9kAvTvAAvAAAAANANAvvvvvvvAAvAvdMvAMtttttt---tz(ztppTiznD=bXJv1z1z11148:88488488:8848k8:8::::8444141p14888:::::::::::::", +"( *9******{{{rrrrreeMeeeMMeM.MM.MMM.9999M_99999959595I99D9D9D9DDffDDDDDDDDbIDfDDDDDDDDDDDDDf&FFYY&&hY&YYhI=bSb;aZaSZZuZab;b;;CxQQQaaxaxalaxxxxaaSaSxSSxuSSSxllxSlZuSuZuuljlQlljjulluQQlQulljjjjjlQlQQQljjljuQIWWWWWWWWFFWWRkkkkkkkkkkRpJ6kUvkvvUvUvvvvvvvTu&bRRIcf0RDDDIM9]]9DI9I;cMM[0De[* AAAvAAAvNAvAAAAAAvvvvvAvAAvAAzzT-ttttt--k 9MTTTT]]R&&jbMTTTv11144848488::8:::88k8::::::8::884444g484:::::::878787::", +"********{{{{rrrreeeeeMMMMM.MMMMM999999999=9)59599D9D95DD9DDDDDDDffDDDDDDDDcDDDDDDDDDDDDDDDDRhYhhYh&hhh&hlRbbbb;;x;SSbaCbCbb;b;CZuSSSuaZuuaaxxxxSSSuSSxSxxuajlSSQjZlQuQuZuQlljZjSlQluuZjjjjjuujjjjjjQQlllQjlZQRWWWWWFWWWWFWbkkkvkvvkv(kJ6kvvUvvUvvv%v%%vNTl&bRRIRIIDI[IDIDI* 9IDclQM[MIeD09 M9*TAAAAAAATAAAAzzTvAvvvzzzzzzzz--------T9cRVk]Tp]]p9SQjlDT T4Tz14888:*8:8:8:g::::::8:8:::::84444848::::::::787878787", +"*(*****{{{{rrrreeeeeReMMDMMM9M999M99999999)I99D9D9D9D9D9DD9D9DDD_DDDDDDIDDRDDDDDDDDcDDDDDDDDj&&&&&&&hYh&&IbSu;abaZxbbbbbCb;b;;CxQlQuSZuuul;axxSuSSSxSuSSxSjljuljjlljSluQZZZjlQuuZjulujjujjlQjjuuQZjQullllZjjQkWWWYYYYYWWWWRkvvkvkkvkv6J9vUvvv%vv%vTNXNNix&lcRIRIcDII9D0DID9DM0DDDDDDMrIDDMp[9[*TAAAAAAAAzzzzzzAAAzAzzztzti-------gDabRTi% *]]] ah&SM]]k% iA8::88888:8:::]k8:8:::::8:8:::88::8:::::::87888%8o7:U", +"******{{{{rrrreeReMeDMMMfMM9M99M9999999)_I)9D959D9D9DD9DD9DDDDDIf_IDDDDDDDDDDDDDDDD9D99D999D&&&&&&h&hYYhQcuaaSxbbxbbCbC;bbC;;;;aQZZllSuZlu;u;aaxxxSSxxxlljlQllljlljSuQQZQQSjQQjlZlQjjjjuullljlluQulZZQlllQjlZDWWYYYYYYYFWWcvkvUvUvUvUk6*vvv%v%XNNNvANAX=&hcRcIIRIRD0000D0D0D[DIII0I[M[I90{ *[DMkzzzzzzzzzztttttzzet-----4[4-g4g4IaSSc p]i ]g%RD[A%A] kTTkNt88:::::::8: k8:::8::::88:8::::8::::V9@9oNUVDCjjjU%", +"*****{{{{rrrreeeDeeMMMMM9M999999999995999959D9DD9D9DD9DDDIIDDDDfD_DDDDDDDDDDDDDD9D9D9D9D9D9)&Y&jh&hhhhYhbcCuSuZbZSb;;bCS;bbb;;C;;lQQluuZlQQSaaaxxuxuSxQjlZuuulSSQllSSuZQZuulQZuZjjjjQjuuuZlZZjllQujlZZQllQlQZIWWYYFWFYYYFWlv v%vvvvvv66vT %vNNNvAAMTAzIhhaRIRRIIcIIsoo$oo0ID0ooID0o@[[MIDM {MM[Mtttttttttttttt-------4g-g4-g4g4U=;uc* *TT]T* p]VTiI=u]p ]kpT-::8:8:8:::8::::::::::::::::::::tT9c=M]kT%T=hY&&]T", +"****{{{{rrrrreMeMeMMMMM9M99M9999999999D9D9D9D9D9DDDD9DDDDDDDDDDDIfDRDDDDD9D0D99DD9D9D99c9999&&j&hhYhjhhhubCaxxxux;C;;C;bb;Qx;;a;;aZulSuQZuxlSaa;uSaauuuQQluQZuSSlSxSuZZuuuSlljjulZZQlluuuuuZZujlZQujjjZulQlulRWWYFWFWFFYYYRUvv%%%v%%vk6%%vANvAAAAAAAzz&YjRcRIIIfRc0oo000oo00Mo0IIoo$s@oo0[9[*D * ttttttttt---------g-g-g4gg4ggkcllbT* T]kpTT*]]k 9cIkUkpk]T]-8::::::::::::::::::::::::::::: Rbx= kU]T]TI&hYh]]", +"***{{{{r{rrreMeMDMMMMM9M9999999999599D99D9D9D9DD9D9DDDDDDDDDDDDDffDDDD0D9D9D9DD9DD9D9D99999)h&h&&&hljhYYxxSSuSbbbb;a;;abSbZSb;a;;xlllSuuuSxlulZZQuxSuulllllZuxSSSSuuuuuuuuullQQluZuulZjQlluZQZuuulZlQjljZZQjj*WWYFYFYFFYFYbv%v%vN%Nvv6vANTAATAAAzzzt-IYYaIIccIIIIc000I000o0I0oo0Io0000oo[D9D[M [e%--tpT----t------]-g4g4gg4g4:cl&&DT *TTUT]TpU T T]%iN]T]T]]-]-U::::::::88788o788o878878:8 b;;cTkkUkpU[&jZSIkU", +"*{{{{rrrreeeeeMeMMMMM9M9999999999D9D99D9D9DDDDD0DDDD0DDDDDDDIDDDIfDD0DDD9D9I5D9DD9D9D999999RY&h&Yhuxhh&Y=axSZbb=ubbaabCbb;bxC;;aaCSaluaZlaxxxSSSxSuSuullllxxxxxSuuZuuuuSuSjljZjuuZuuljlljuZQQQujjSujjljjlujjj9WWYYYhhYYhYYRUvNNNvvvvvkNAAAAAAAAttttttlFhIDIDRRIIDIIoso0oo0IIIooIIo0000oo00I[ TT*9*]t-t--t-----g4g-g4gggg4g8g8I&lj=T * ]ps ]TTUT ( TvN]TkpT%k]]TT:87887887878788878:88788>[xSlS ]]kTT]N]U]V]IR[", +"*{{{r{[rrr[DeMeMMMMM9M999999I99D99999D9D9DD0D9DDDIDDDDD0DDDDDDDDDDDDD9D999D5D9DDDD9D9999999cY&&hY&au&j&&u=ab;bbbubaaC;;aCbCb;S;C;;xSxlSaZQxxxxxxuulQllxSlaSSSxxSuuuuuSQljlllZjuluZuSQZuZuuZQQZulljjjjujlulQljIFFFhFYYhhYhhIvvvNAANv vAAAAAztttttt--TFFlRIIIIRIIIIcoo0o0o0000o0000o@sooo00D*T *[ 9U--------]-4--g4gg4g48g8R8]hY&&9TT* *Tossss@s okosss@s %]Tk TV7878787878787878787788789SljuIT]kkT]]]%]TTpC&I", +"{{r{r[rr[eeD[eMMMMMMM99999999999D9DDD9D0DDIDDDDDDDDDDDDDDDIDDDDDDfRDDD9D9D9D5DD9D9D9999M9MMR&&jhhjbxba&&=b==bbbbabbaaa;;;bbbC;;;bC;ZlllxZQlxxxxuZulllQuQxSSxQZxxSSuSxQjjjlluQlQuuuSjjQuZQQQZZQuQuuuulQQllllZQMFYYYYFYYYYYYIzvvNvNvvvvAAAztttTtt-----cFYlII_DIIcRIIIo0I0oo0I00oo000o000o000I*[{M T9[4-g-g-g--gggggg4gg8g88*88DY&Yu TT*** Vs@ooPs@TVs@]@ssssso]Tk TTV78787878787878787887878=bbSckT]kUTT]TA%]]Tk[p", +"{r{[rrereeeMeMMMM9DR9M999999999D99D0D9DDDRDD0DD0DDDI0DD0DDDDDDDDDDDDDDDD9DDD9D95955999MMMMMRj&jhhju=_=lu_S;bb=CbbZbaa;;a;bbC;;bCb;;;CaluQlSuu;x;lllQljSxaxlQSZxxxllZjuuuQjjSZljuuZulQluQQQuZuQulQuZZZllZjjjlZIFFYhFFFFYhFYcvvvvvvvvvMzttttttt-------=hhbccIIcDIDIIco0IIo00900oo0I0o0000IMIr[MM[Mr0Dgggg-g-ggg-g4]gg8g8488888jY&hI[]TV* *Ps@oPP@] @@VPs@ooPo T]k]kUV8787878787878787878787IIRxuM]UT-%TVTTNp]Tk-T]", +"r{rrr[re[eMDMMMMMM9M999999IR9D99D9D9D0DD9DDDDDDDD0DDD9=_DDDD9D9D9D9D9D99D99999999959MMMMMMMR&&&hj&&ubRc=DCx==;bbbxbx;;C;;CbQSuSuS;;;C;SZZQSQZZQlSlll;SSZQllllxSSlllQuSQQlQlxZuuuZujQjuSZQuuuSulQQZuuujlljjlQQ_FYhYFFYFFFFYIvvvv%vNvTtTtttt--{----g-glhYccI_RDDI9[DIo0000oo00[ooo00o00II0ID[M[{*M 99kg-g4g-g4g4ggg848g888888]Fhhh9**TT *[Us@s@ssoT s@]so@os@@V]%NNAT]87878787878>87888788VcR;xRPNUTt%]s@ss@T]stV@", +"[r[erreeeeIDMMMM9MM9M99999999D9D0DD9DDD0DD0D9D0D9D999D99D0D9D9DD9D9DD9D59D999999599MMMMMeeecj&FYhh&j=R=bM==xubCb=Sbxa;;a;baQuQuu;xbCC;a;;;;xQlQQZ;;alQuSxlQljZllZuujljjxlSuZljSuuuQuSuSuZZulZZZQuZZuujQllljQjDWFhhFFFYYYFhRvvvNAAttTt-Vt---tg--ggg-U&YYIDIIcIID [I0I00II00 [0o0oo0o00IIID0M9[*[*T[ g]ggg4ggggg]8g8g8888888R&hYh T*[]TT]o@ooo@oV ooV@o@@@@sV]N-NUtTN88888888887888787887[cS&&Dk-]UtTVs@@@sT@stos", +"rrrr[e[eMMM[MMMMM999999999D90D9ID9D0D9I_D0D9D9D999D9D0D99999D999D9D9999995999999999MMMReMee;j&h&h&&ucbuuI==bbbbbbxb;a;babb;alaaSQQQC;;aaxaa;uuQlZxllZuulQllZllZuSZjllQlxSxlljSuuSjjuQuSZuuuuljululuZZlQZlljQjIWYhYYYYYYYYhcNNAztttttt------g-g]-g4gg&&jRIIRIRcIDIIII0IIII0 MI00[[[o0I90D[D9[M[D**[MT]]g4ggggggg*8g888888888=YhF; [TTT T] so@oo@@V @oVosPPo@g%gz]k]]]:78787878788888I8>7Pxl&hj U]U]NUUsoPPs@V@oV@", +"r[e[eeMeM[MMMMMM9MM99999990DR99ID9IDDDDD9D9D999II09999999DI90999959999I9999)999999MMeeDeeee;uxl&h&lbbub;_;=a=bbb=Sb;aCaaCbCbZQZSxSSZlQQuQQQZQZaSuZlSZZjlllQSQlQZSQluxSSSSjQlSSlulullSuuQQQuSujjjZuQZQlZlljljQgYhYYhYhYWWFhbTAttttt--------g-gggggggk&l&RRIRI*M[[D90T00II0[MIII9[*[M0DID9ID[IM[[I **[]4ggggg48g888g888888:8:cjYYZ*[kk]T%N[os0o@@ o@ Vo@VPoVNk%]TTTV]P878878888>88888:888&h&&;%t]%kNTo@s@s@V@s]@s", +"eeeee[eM[MMMMMM9M9909I99D9D99D9D9RD9D9D9D9909999959I999999M9999II99999.9999999M9MMeeee[r[rDQlQlQj&lcbucc9a=b=b=bbub;aC;axCaxZZSuZQQaaxaSuQSxxuZuxxxQ;xx;;lSSQZluullSSQSZZuxSulljQZjlluQllZZuZjlZSZZQZQQZZuQluIYYYhYhYFWWWYbAtt-----g[g]*]ggg-gg4gg8 jj&IIIID T U]T]]UNTTk%%t]]%k]k]oTo* [* r[{ *[4-g4g4g8g8gg88888888888cZh&S ]TN%ktU [oo[os@o @@oV@oo] U Tk ]kkk88888>8788788888880h&hj=Vkt]UN]VsPso@oVsPU@", +"e[e[eM[MMMMMMMMM9M9999990990D9IID0D0DD0D90D9D999999M9909MMMMMMM9999MMMMMMMMMMMMDeeee[eeer[r&h&&j&&u=abcc9b=bb=;b=ab;;Caa;xCSZx;SZlZuaaaxSluZZQuxlSaSxxxaZlZaaxuSZuZlSxuZuSZuljlujxjSSullZuuZljllZZQZSlQluuujZMFYFYYhhYFWWF=*Ntt-tg--g-g-gg-gggggg8gT&jhcIRID TTTTT ]TTTT]]]T]]]]]kUUNNNztk]M0D{t--ggg8g8g8888888 :888UlSj&cTT{] N ToV [0oo@@oooVV]]TTUTT U] ]U87878>88888888888-IYhh&=]]tTT-UosP@@soos@Ps", +"ee[MMMMMMMMMMM990999IM9D9D9D9D999D9DI9D9I5999D099999DMMMMMMMMMM99MMeMMMMeeeeeeeMDMrereeeeerjh&hhh&u=cRcjf=b=bSbCCb=b;b;aCS;uZuCCuQ;uZlZQQQ;;CaC;;;axa;;x;axxxQluZlSuZxxxllllluSljxSZuQQuSuSujllljuZuuljQllSSl[WWWFhhYhYhYFa *%-gV-g-gg]ggggg8g888g8 j&&ccRcI TTTTTTTT]TTV]T]]V]]Vp]]]]kkk]kU M9I --gggg--88g8888888888glSh&RV {T%]]kVT]TVoT[ TVTT ]ViV ]kTT NN]T]g888888888888gg8ggRhhhYlVkTV]%UU@@P@@@os@PV", +"M[MM[M[MM[MMM90M999M990990D0D0D9D9DMI9D9I9D9D99999MIDMMMMMMMMMMMMMeeeeeeeeMeeMeeer[r[re[eerh&&YFFhQbcRQ&Rb==C;SS;=ba;C;CSZZuZCbCbxxSSQQCC;axaa;;;axxa;;;xx;a;QZxljSZZllSZlZjZZZZZZuZSuuuSSuQlZlllSSuSjlljjSSjRWWWWFhYhhYFFa[ *U--g-g-gggg4ggg88g98gM&jjIIRIR T TTTVTT]TT]T]]T]]]]]]]]Uk]UUUVD{0DA--]]9[U]VDujkt]8 88g48&&&h=] [kggg888:88:8:g8:g:gg4gtANU]kUtk]]V88888888888gggggg-0hhhh;VTU]]NTVPPoPVVoooUU", +"M[M[MMMMMMMMMM999M9099D99D99DDDD9D99999D99D99999999MMD0MM[MMMMMMMee[ee[e[rr{eree[rr{{rrre[9YY&YFFhjQuu&&I=b=SxSSCbb;aabba;;xSbCb;aCbxuSCCa;;;;;xxxxa;xxSxxuSaxxSSulZjlZQSSxxSSuSSSuSSSuxluuZluQjZjjxljZQljSQjIWWWWFFFFFYFWl[[* Ugggggg9ggg8g888k g8Ujj&RDIcI TT TT T T] T]T]]T]]]T]]]kk]]]kkkTMIDD ]DcMV]kVN9&j T]T%gg-gg;&h&=]]kkg4gg8g:8888:888:8::8:::8:::::UTUUV88888888gggg]gg--9h&&hSV]%U]%VUUNUV]V]VUVU", +"M[MMMMM[MMMM0M9M90999909909D99999IID9D9999MR999999999MMMMeeeMeMMM[Drr[r{{{{{[r[rr{r[{[{[{r*YYYhYFYjQjl&Yc=ab;Sbaa=bbbCbabCxZxbbCauaZSa;CbC;;;a;xaxSxxSxxxuZux;aaaSlllZxSSSxSuuuuuSSxlllSujQlZllQjuuQuZSjjlSxl FWWFFFFWFFFWj{ [ -g-gg-0gggg888g8888gl&&cRRIR T T TTTTT TT]TV]]V]]]]]V]kkUkUU [*IDoRc[VTT]]g]I[ T%]-%4g-gSSjjR]%]]gg4g48g4888g8888:88::88:::8::UUA]]888888ggg--gggggg0&&&jZ]U]7:7::g7:g:gtNN]]", +"MM[M[MMMM0M9M9099990999D059D0D99DMMM99M9990999D9ID99MMMDD[[ee[eDer[r[r{[{[**{*{{r{[{{{9r***YYhhYFFjQ&&FhR=Sx=;CCaSSC;aC;aSuuuuZa;u;SCbbCCCa;;aSaxaa;;;xxxSuuSSx;aualSSSuSuuuZuuuuuuuSjujjjlZQQlljulQZZZljlluSTYFFWFYFFFWWWj[[[[MTggggggg8g88g8888888llj=ccRI T TTTTTVTpT]T]T]Vp]]]]U]]]k][[0D=C*T%]TUVAkNcj[Tog]kgggccC&=V]T g4gg8ggg8g8888:88:888:8:88:8:Nk]kV88888P8ggggggkggg[j&jjZVAN:78778787:787:7:", +"M0DIMM[MMMM0MM9M909999099D99990990DM90MM0DMM90DMMMMM9MMM[eee[eee[r{r{{********[{[{{{r{[***[FY&jhFY&&jYF&c=bS=uSbu==b=ZSbbaZSaSZZuZxuu;bbub;;;xxS;;llSZlxSSSSx;lQluSxuZuuuuuuuSxxSuuSSllulljujZljjjlullQZuQluS[YYFWFFWFWWWhS[*M[[ Ngggggggg8888888888jl&cccII* TT TTTTTVTT TT]]T]]]]V]]kk]k]UT[D{0boUT%]]U]]TU]k]TTT]]]g8cbbZl T]]-gggg4g84884888:88:88::88:8:8t]k]T888PoUgggg-ggggg8Pjj&jjUUUg7878787878787::", +"IMM[MM0MMMMM90M90990999D9090MMMM[MM[MMM[MIMMM[[M[M[MMMM[Me[ee[e[r{[**[*0** [ ****{{[****** hh&&&FhYhhFFjR==babx;xxCbabbCZSS;xS;SubbZZZ;CbCCCbC;SxxZQQuQ;SuuxaSZZZaSuZSSuuuSSSSSSxSuxlljZjllululjlZuZQjQQuluSZTYhhFFWWWWWhla] *[Tgggggg88g888888gggjj&=cRI9 * * TTTTTTTTV]TVTVi]]T]]]V]k]k]M0D[IkVUT]sssVU@PUT]tVTUg]g[SZjj[*TT-g4gg-gg8ggg88g8888888888:8888V8VTg888UgggggggggUggojjlZZ N%N878787878878878", +"[M[M[DMMM0M0MM9MMMM909909MMMMM[M[M[e[[e[e[R[[e[[[[e[[9eeeeeeer[**[*** *** * ******* * *[ *[&hhhhYYYhFYF&c====;;;CauuCCbbbbxbbb;ZSCZZxCb;;b;SSaauZC;;;ClZax;ZaSQQ;SSaaSSSSxxxxxxSxaSZQllllSSjllllluuulQZlujZjl YhhhYWWWWhQjl ]V M[ggggg88g8888ggggg-lj&b=IID T TT T TTTTTT]T]]TVT]]V]]]]]UT[90M ]kVVPsP@sPs%PsssTU%kkUDjlQj*[ g-g4gg4gg488gggg88888:8:88888:]%%UgggggggggUggggPggP&jl=S*tNP787878787878787", +"M[M0M[[M0MMMM0M0M0MM999MM[D[[[[e[[[[e[[[[[[[{[r[{[{{[[[90r[[r9**[* [** 0 ** [ [ * * * *IjhhYhYYYhFFYZbb===;=CxCSbbbbbb;CC;bCbbbbZCxZ;;auSQubCCC;;aa;uQZxZSlxaSZxaxaaxxxxaxalZxxjluZSjjSuujxlZjlZuQjuZZujuQ]FFhhhYFWW&Zhj** [ [[ ggggggggggg-g--- ;Cj=cR[I* ToT T oT TVT]]]]T]]]]k]]]]90D* ]]%]Usss@osVUs@sso]UV][ljjjD[[T-gggg-ggggg4gg:8g8g88888888888UkgTggggggggggggggggPgZSCCZIg%]888878888788788", +"M[MM[MM0DMM0M[DMMMMMMM0M[[[[[e[[[9r[[r[{[{[{[*[*[*[[***[*[**M0[*[** * * * * D[c&jj&YhhhYYYYuCbb======xbbS;a;bbbC;;;;;CCbbbQZuCC;C;aaa;aaxaxCZSSu;;aSxaSx;a;axaxxxaxllZSuuSlQulSlljllZjZulllQQQQjlDFFYYhYhWW&lFj[[[[ [*Uggggg-t---g-gU]gUcSZb=IDc * * * T TT VTVTTTV]]T]V]]]]kV[{ 0[TV]oo@@sPPsVP@@s@UV]UVDSZjjI [Tg---g-g4ggggg8gg8g88g888888888V]t@ggggggggggggggggggSljjj0UNV78788787887887:", +"[M[M[M[M0M[MMM0IM0MM0[[[[e[[[[[r[[[{[[{[*[*[*[******0** I[* [ [[* [ 0* [ [ 0Icj&&j&Y&&Yhhh=bb;;bbb==bCabC;Zbbuabb;;;CbbC;C;;;CCbCCbCCC;xxaCSSuCa;xa;lCxxx;xxx;xSxuSSuZuSQjxxljlZlZlllullZjlllSZZcYYYYhYhhW&&F& * [oNNttgtgggggggggggbulCcRII[* [ o T o TVTT]TT]VV]]]]UV]U] [* N%TTPsosoVPVUsPPPV]]]V[jj&l0 ]]tgtg-ggggggg4gg488g88888888888kV%]gggggggggggggggggg=jjl&cNNU888788878788788", +"[[M[[[[[[M[M0M9[MMM[e[e[[[[[{[*[[[D*[*[*[*[ **[0[ [[[[ [ [ * * * * I&&j&&hhhhh&&Zbbbb;bb=S;SSSbS=bCSSxCbCZSxaCbC;;;CCaSSZlQu;xxa;Cal;C;Z;ZaSSSxx;;xxuSxZxuuQuQxSxSuluSlZZZQSjllQZllxuZgYYYYYYhhY&YWjUUN%T * ToVggggggggggggggZjQ;cRI0 T TTTTTTTTTVTTT]V]]]]]]]][D[MTUUoTo@o@VVVU@PPo]]]]V%[&ljjI UTtgttgt---g--ggggggg8g8g8888888]]]Pg>ggggggg>gg>gg>gsjjjjjRtUP878887888878888", +"[[0I[M[M[[MMM[M[[[[[[[[*[*[*[*[**00[* [ [ *[ [ [* [ * *[ [0 [ [ R&jj&&YYhYY&habbbbb===bSSa;;abbb=b=bbbbCxxxaabC;uuSxa;uxC;;uCx;lx;allCxSSSuSaSSSxSSuuxSxQlSxxxlZluulZjSuZjlZlZjxSSl%YYhhhhhh&hFWjUUUNV[[*@]Tggggggg>gggggVSllCcc99* [* TToTT@TTVTVTVTVT]]TV]V]]][[MD[V]kUNUVVU]U%VVU]]VVV%V &&ljI]* UN-gtgtgg-ggggggggggggg8g88888V]k]gggwgwg>ggg>gg>gg>I&&llc>TV888788878888787", +"[[[[[[[[[M[[[[[[e[[[*[*[*[*[[ [ [*[ [ [ [ [ [ [ [ [ *0 o o o o o [c&j&&hYFYYh&&R==b=====SSSxxS=bbbbCbbbC;;CCCbbCbuS;SZSZbC;aCQCxx;CClx;xSSSxxSxSxxxxSClZxSaSZa;lSZlxuxSSxxSllljZSSSlZth&hhhYhhjYWW&UU%Uk[M[TVVVggg>gggggg>ggljl;Ic0I[ [ TTTTo T]TVT]]TV]T]]]]][[0[*T888888888888888888-VT@l&ZjM[ *UtgNgtgttggg-gggggggggggg88g88T]UP>g>ggggggPgg>gg>ggclSCcIU]N888888888888888", +"[[{[[[[[[[[[[[[[[[[*[ [ [ [ [ [ [ [ [ [ [ [o [ o o o [o o o o o o Cjj&&j&YYYYh&0====abbbx=b;Cb====bbCbbbbCb;bC;bbCCZZZZZZx;Cu;;a;a;CZ;;SSSa;;xxaSxxxx;llxZ;x;SlZlluZZSxxxuZQllSZjZSZlThhhhhhYhjhWWjPUUUV[[ VVTNg>Pgggg>gggg>=jlcc9II[* oToT TVTTVTVTTTV]V]]]]U[M[M[P88888888888888888888]V;lZS[ okttgttgtgtg-ggg-ggg-ggg8ggggggU]TUUg>gPgPgPg>gPg>gPgoc==cIPVt888888888888888", +"*[[[*[[[D0*[*[ [*[*[ [ [ [o[ [ [ [ [ [[[ [ [ o o o o oo[o o o o o o[ o ooo*b&jjj&j&&&Yh&_c==CbbbSxS;bbx==b=bbCbbbCbC;;;Cb;xSZx;;aZCC;Cxxaaa;ClS;aSxS;CxaxSxxaxxuQxSx;SQllllQluSuSuZZjlllZllxQZ]hhhhhYh&jFFFjUUUUV*[[TVVNPggg>g>gg>g>gZSb=0cRI[ [[ T T TTTTTTVTTVVT]V]VV]VT[[ [V88888888888888888888]VSZl=0[ T@tNNgtgttgtgtg-g-gggggggggg8ggU]V%PPgPgPg>gPg>gPg>g>oc=S=R%%N888888888888888", +"[ [ [[*[[*[ [ [ [ [o[o [o [o[ [o o o oo o[oo o o o o oo o o o ooooooT ooTo oobjjh&hhhh&jjC[==bbCCCbS=SSCSS=====bbbbbbb=bCbbCuS;xu;CaCxZCSxC;Sua;xxaC;SC;C;x;S;xCCSlx;;luZlSS;llluuulllZZZllZ;lQZ]FFYYYYhlhFWWjVUUUV[[[ VkVVPN>g>gPg>g>gcCcIcRIIM o oToTVToTTV]TV]T]]]V]T9o[*Vggggg8g888888888888NVVSjScI[*oTtNgNNgNgNgtgtg-gggggggggggggg] UVVg>gPgPgPgPgPgPgPg[=CS=c>gV888888888888888", +" [ [ [ [ [ [ [ [ [o[ [o[o[o[o o[o o[o[o o[o[o oo o o o o[ o o o o oTo To oooToToToQ&&j&&&&hhScc bbb=Cb;=bSb=xSab=bSS==uaSS;;CSS;uaZCSS;ZbC;CC;CuSSSZ;;;auCuSC;C;xaSxa;ClZ;;ZS;Z;SlZllZZllllZxZllu;;lZVFWFFFYQlFWWWjUPUU] [ Nggo]VP>g>PgPgPg>ISCcccII0 [ * o o oT T oTT VTTVVTV]VV]V]V[9M[Vgg8g8g8ggggg8g888888]V=lZxI*[ Vg>tNgNgNgtNgtttgggttggggggggggoUTVPgPgPgPgPgPgPgPgPs==C=Io]V@88888888888888", +" [o[o[o[ [o[o[o[o[o o[o o o [o[o o o o [oooo oo oo o oooo o o o o o o o oooTooTooooTox&j&jj&j&lcRbrbx==CbbC==bC;xCC;b;axSC;;;SxS;SxSSSSuSZZZaSZxuZZZbbC;;ZSxZxZxCC;;xSSxaaaQ;aZxxxxSlZZZlZllZlZZllZCxlZZ*YFWFF&lhFWFWjVUUPV[0 ggtVoVUgPgPgPggPgClj;cIII* [ * T o T TVTTVTT]V]TVpVTV]*II[Tggggggggg8ggggggggg8P]ClSCc [TNtNgN>gNg>gNgNgttgtggggggggggg]%VVNPgPgPgPgPgPgPgPPgC=cCC0V]V88888888888888", +"[o[o[ [ [o[ [ [ [o[o[o o[ o[ooo[ o[o[o ooo o o oo ooo o o ooo ooooooooo*o ooooooVooooQh&&&jjjl==ajIb==C=b=;=;C=;C;aCaCabbCSSZ;bbx;SS;CC;bbbbbxSuSxxZb;aaCZZx;auCC;;;;xSS;xCZSCuau;ZZZxZZ;;ZZlllSlSxx;SaZVYFFWYl&YWFWWjPU]]][[ g>NoU]V@gPgPgPPg@CjjlRc=I[[[ [ [ o o TTo oTV VTVTVTVVV]T 0[0[Pggggggggggggggg8gggg@VcjZZc[ [ N>gNNgNNgNtNgNg>gtggtgtgggggggU]UVgPgPPgPPPgPPPP@PgPZZCCZDVVV88888888888888", +" [o[ [o[o[o[o[o[o[o [o o[oo oo o oo ooo o o ooo oo o o o oooooo ooo o o o o[oooToVoVToTo[j&hh&jjjuaSj&9=c;=bC;;====xSCb;xCbbbbSSS;bCCub=bbCCCbbxZCuCSC;ZCCCC;SZZSaZCCC;;;xSxS;CSZ;lxxQZlS;xlxxClllZZSZSCSSlZ hYWFjjYFFWFFjVUPUoT otgNV%PPVPgPgPgPP>cljScIRI0[ [ [oo T oToTTT oTVTVTVVTV]TVVTD0e[Vgggggggggggggggggggg@VISS=C[ [ >NNNg>Ng>Ngg>gNggNg>gggtgtgtggUVVoPPPPPPgPgPgPgPgPPgcZZCSIVTVgggggggggggg8g", +"[ [[[[ [ [o[o[ [ o ooo[o[o[o[[o o o[o o o o o[o o oooooo oo@oooTo[ ooToo TooooTooToo@j&j&h&jjuSjjlDcC=;x;xa===bCCx;C;bbC;xx=;b;uSb=Cx=bC=xSSSS;ZZSCZZ;ubSuuZZxCS;C;;;xxxCx;;QSCSlZSxuS;ZSSCSZCClZZZSSllSVhh&&jhFWFFWFjP]VU][ og>googV@gPPPgPPg>cllC0ccc[ [ [ o o ToToToTVTVTTVVTVTV 90D[@gggggggggggggggggggg@oI=ccCToo NNg>NNgNNg>NNgNgNgNggNg>gtgtgtUoT]PgPogPPgPPPPgPPgPsIZZSSIVVV8g8gggggggg8gg", +" [o[o o[o[o[o o[oo[oo[o ooooooooo o oooo o oo ooo oooooo ooToooVoo@o ooooooooTooooTo@T@j&&jjjjlxuh&jIc=abC======CCCxbbbCabSSSCbxbbSSCCCS=bbSx;SCCbZCbbuxSCCSCZZCC;CC;xax;x;xCCZSCSZZSxlSSxSlCxSCC;SZ;ZZZlZ]&lSjhFWWFFFFYP]P]@ 0oN>N@oNPVPgPgPgPPg0lZcIcII0 [o [ oTo ToTVTVTVToVTTVV]V I00 @tg>gg>gg>gg>gg>ggggg@VIC=c=V]VV%NNNN>N>gNgNg>Ng>gNgNgNgg>g>ggVo]VPNPPPgPPgPgPgPgPgPICCCZc@Vogggggggggggggg", +"[ [o[ [o[ [o[o[[ o oo o o o o o oo o oo o o ooooToooo@oooVoVo@oo@T@TVoVoooToVo@@@V&jj&jj&jZajjZI=CCC=bc===bCaCxxCCbCa==CCb=bC;==bCb=x=CSCSZCxbbCbSZSubSCZCCCbbC;x;C;;x;xCQZCCSlZZZZZZS;ZZllZZZSSZCCCS jjul&hYFFFFW&P]V]o[0ot>tooNo@gPgPPPgP@IcIc0ccIM[ [[ o o o o oToToTTVTVTT@TVTVo DM[ @>gg>gg>gg>gg>gggwg>gPV0=Cc=o oN>N>NNNNN>Ng>NgNg>gNgNg>gNgg>gTo @NPNPgP@PgPgPPgPPPo0C=CCCVV@gggggggggggggg", +" [o[o[ [o[o [o[oo[ooo o[oo o o oo o oo[o o o oo ooooooooT@T@oo@oVT@@@@ooToV o@T@oV@[jjjljjlu;au&&cCCC==Cc=cCxxC==C;CCb=;;=b===Cbbb=b=b=bb=SxCbb=bCCx=;CbbZSbbb;bC;;x;C;;xSaCZZCxZZSZZZZSSSSZSZZSlSxC;;Z[jljjj&hYYYFFF@P]PUV[ >N>VVg@VPPgPgPgPgIIcCII0D[ [[o o o o oToT@ToTVTVTVTVT 00[ ogg>ggPg>ggPggPg>gg>g@V0cZC=[[[o%N>NN>gPgNN>gN>N>gNg>gNgNg>gNgoTooPNPPPNPgPPPgPPgPgP0CSCC=ooVggg`ggg`ggg`gg", +"o[ o[o[ o[o[[o[0o ooo[o oooo oo oo o o o oo oo o oooooooVoVo@o@oVoVo@Vo@oV@@@@o@oooVV@oV&jjjjlbcal&&jRCCc=;=bCaaCCb==;;x==b=======b===b===b=b=Z==bbCCbx;CbCbCZxCbCbbb;aC;CCC;xx;SZZZSZxZZZZZZSxSSZZZSZ;CClSMQj&hhh&&hhYFhVVVVV[[oN>NVPNPPPgPPPgPPPcSZ=I0II0 [ o ToTo ToToTo VooTVVoVT@I0D[@og>gPggPg>gPggPgg>gN@T0ClCC[[*oNPNPgPNNNPgPN>NNgN>N>g>g>Ng>gNV oT@PgPNPPPgPgPPgPgPP0CSZ=C@T@g>ggg>ggg>gggg", +" o[ o [o o o [o oooo o o [oo oo o0o o oo oo oooooVT@oVo@oVoV@T@@@oVoVVT@@V@V@VV@@@VIj&&jju=bj&&&Scb==;b=bc=;xx;bc========Cb===b=bb=bbb=CSSSCSbbb=bSSCCCbC;;SbbbCC;;aCbC;CCCCCSCCZCxZSZSZZSCCSSC;lZSCSZS l&&hYYYh&YhF&@VV]V[ [>NP@oPVPNPPPgPgPgcZZ=III0*o[[o[o [ [o o oo @ToVToTToTo]oVT0I0 o@>gPggPgPgPgPggPgPg>Po SCC=[ [ PNNNPgPNNgPgNNgPNg>gNPgPgN>gNgPVooVUPNPNPPPPPPgPP@Pg0=S==CoPPgg>g>gg>gg>g>g", +" o[oo[o[ o o[o[[o o o o o oooo[ o0[ ooooo oo oo oo[oToVooo@oTVo@Vo@@@T@VVVV@@@T@@V@T@VV@@DjljQj;=Sj&j&Q==b=c=cccC;c================b===b=b=xx;Cbb=;Cb=xSxSb;;=ZC;bCbCCbbCCCCCCbCbCCSCZbxZSZZZSS;;;ZCx;CxSZZSZt&hhjhYhhh&&YYVVVPVo0oPPN@VNP@PgPgPPPgPIZZCI0IR0[ [ [ o[ o o oo oT oTooToTVoTV@TVVT0D0 o@PgPgPgPgPggPgPgPg>N@@@=C=C0[o NPNPNNNPNPNNPNPgPNPgPgNNgPgN>NVVo]VPNPPgPNPgPgPPPP@Ps=CCCC[VVogg>gg>gPgg>gg", +" oo oooo o [ oo o[o ooo o ooooooooo ooo oo [o o oo@ oVoV@@@T@oVV@@@@@@@To@@@@@@VVVVoV@IjjjZ;bx&hhhhxcccccb=C;b=====================b===CbCx===SSS;CbSbS=;bCSbCbCCCCbbbbbbbCbbSbCuZuCCCxSSxCx;;;SuCx;SS;xxSVhhh&hYYhhh&Fh@VVVV[ oPPP@@PPPVPPPgPPPP0SSCcIIc0[[o[ o[ o [o oo o oToToVToToVToVVo00Doo@NPgPgPgPgPPgPgPgPgP@Vo====0 oP%PNPNPNPgPNNPgPNgNNgPgPNgPgPgVo VVNPNPPgoP@PNP@NPPPPII=ccoVVV>gPgPgggPg>gP", +"o[ o[ o0 ooo o[o ooo o o ooooooooooooo o ooooooooooooVoVo@o@VVV@T@V@TV@@@@@VVVVV@V@V@VIjja=cC&hYYYh=c==cc==CbCc==c=====b===b===;b==CCb;======bC;SSSCCxSbbbCCS;bbbbbb;CbCbbbCCSxSSZCCbCCSS;;;;CaSZSZZZ;C;;x hhhhYYhhYhhFh@VVVP[[ PNPo@N@@NPPgPPPgP0=SCIcIII[o [o[o[ o o o o oooTooToToTooVo@TVVe00 0ogPPgPPgPgPgPPgPPgP>@oo====0[o P%NPNPNPNNPgPNNPgPPgPNPgPN>NNNoV@ooNPgPgPPNPgPgogPgP@II=cI >V@>gPggPgPgPg>g", +" oooooo oooo ooo oo o oo oooo oooooo ooooooo ooooToVT@V@oVoVoVVoo@@@@@@@VVVVVVV@V@VVVVVVVcllcRbZhhYhYYC===ccc=cC======c=====b==bc;aSC;a;==bbbb====CSS=;SCSC=b=S;C=CbCC=C=SCbbCbbCSZxS=CbbSSCCS;C;a;SZSSS;xSxxc&hhhhYhhh&hF&@@VPV[o[PPPo@P@@PNPPgPgPg0cSCcIII0 [o o o o oo oo oToTooVoVooToVT@VT0D[ [@PPgPPgPPPgPPgPPgPPgP@@ccc=0 [oUPNPNPNPNPNPNPgPNNNPgPgPgPgPgPt@oVoPPPgPgPgPPgPPPgPPPIc==c0@VPgPgPgPgPgPgPg", +" o o o o o o o o o o o o oooo oTooooToo ooToToo@oo@o@Vo@VV@VV@T@VV VVV@V@V@VVVVVVVVVVVcl=I=SjYhYYhYCc=ccc=ccc=cc==c===c====c=;;=bC===b==b==bCb===CCSS;=SbCCCb=C=bbb==b;b=b=C=xZSSbCb=CSS;C;x;SZSS;xSxSxxSS j&hhhhhhh&&hj@VV@@o@oNP%ooPVPPNPNPPgPPIcC=cIcII[o[o[ o[o oo o ooooToToTVoTVoTVV00[0[ogPPPgPPPgPPgPPgPPgPPPoIIIcI [ooUPPNPNPNPNPNPNPNPPNPNPNPgPNP>NoVVogPgPPgPPgPPgPgPPgP0ccc=IP@VPgPgPgPgPgPg>", +"o ooo o o ooooooo o o oo[ooooo0ooooooToooooTooooVoV@@T@T@VV@@T@@VVVV@o@VVVVVVV@@VVVVVVVVP=ScRblhhYhhYhR=c===c==cbccc=c==cc==c=====C=======bbb=b=bbCxCCSSSCSCCbb=b=C=b=bb=Z=CbCC;xCCS=;Cb=SSCxS;SZS;SCSSSCS;SCI&&&&hh&h&&&ll@@@@@o [PPP@VN@VNPg@PgPPg0=C=IIII0 o o[o oo o oo o oTooooo@T@oo@oV[[o[ @PPgPPgPgPPgPPgPPgPPg@@0I00Io[ooUP%PNPNPNPNPNPNPgPgPgPNPNPgPNPoVVoPNoPgoPgPgPPgPgPP0IcIIc0o@VgPgPgPgPgPgPP", +" o[o oo0 oo o o oooooo oooooooTooTooo@[o@TVooToT@oT@o@@@@@@T@@VVVV@VVVV@@@@VVVVVVVVVVVVVoI=IcC&&hh&hhY=c=ccccc=cbc==c=cc=c=====c=C======bb=bCCbbb==;bCSC;bS=CbS===SS=b=b=;==bCSSSSxCbCCSSSCxZCCSZSS;CSS=C=C=b &&j&&&hh&&lCc@@@V@[ooPPP@VP@@PNPPNPPgPo=CCIIII0[o[o[oo[oo o oo ooooooooooToo@oTVV@ [o [@PNPPgPPPgPPgPPgPPgPPPo0III0o PP%PPPPPPPPNPPNPPPNPPPgPNPPNPNV@V@gPPgPPPgPPPPgPPgPP0I=III@@@PgPPPgPPPgP>N", +"ooooo oo[o[oooooo o oooooooooooooo@o[@oVooo@To0VoV@@T@V [@@@VVVo@VV@@@VoVVVVVVVVVVVVPVVVVIIR;Sh&h&&hhYc===cccc=cCccccbcc====c===c=x=======bbbbbb===bbC==S=x=S=;==CbxSS==C=;C=CSSSCCSCCSSSCSSSSCbCSCSSS=CbC=CbI&&jhhhh&hYZ=C@@@@P[o[PNPNPgPPPPPgPPPPPo==cIIII0[oo[o[ooo o o o ooo oo ooToooVoT@@@V0[0[oPPPgPPPgPPgPPPgPPPPgPP@0cIIIoo0oPUPPUPNPNPPPNPPNPNPPNPPPNPgPPNP@PoPgPPgPgPgPPPPgPPgPo00cI0o@@PPgPgPPgPPgPg", +" o o ooo ooooo ooooooooooooToTooVoooVo@oVoVoooTVo@ o@V@@@VVVVV@VV@VoVVVoVVVVVVVVVPVVVVPVV[I=S&&&&h&&Yhccc=cccc=cc=ccC=bc=b====c==b=c============b=bCC;CCx=C==Cx===CCxCS=bbSCCCCSSCCCCSS=SCCCS=SSCS=C===CbbC=CIh&&jhYhhh&Sj&P@>>P>PwPwPP>PP>>PwPPsgwso==III0I0o[oo oo[ o[o oooo ooooo@ooo@oo@@@V@V0[[[[P>PPPgPPPPPPgPPPPgPPN@@0cII0@oVoPPPUPUPPPNPNPPPPPPNPPNPNPPPNPPPVoPPPPPPPgPPPgPgPPgPP0I0II0PP@PgPPPgPPgPP>P", +"s@s@s@ssssPPs@sPs@sP@PPPPPPPPPPPPPPPPPPPPPPPPPsPPPPPPPPPPPPsPPPPPPPPPPPPPsPsPsPsPPPsPPPsPIcbljjh&&&&hhcccccccccc=cb=Cccb=cc=c===c===c=======bb=b===CCS=S=CC===b;===CCS;CCb=CCCxCSCC=CCCSCSC;CbSC====CCb=bbbC=[&&&&j&Y&hj&h&w``+``g``sw>swwPw>PgswPswoIcc0II0Ioo [o[ooooooooo oooooooooooVo@oo@@o@o[[[oswswPsPwPP>PPPgPPPPgPgg@ssPggggggNggggw>gwgw>ggwwgwggwggggwgPP@o@oPwPgsPPPgPPPPPPPPP@00I00@P@PPggPgPgPggg>", +"PsPPsPs@P@PsPPPPPs@sPsPsPPsPsPssPPsPPP@PsPs@sPPPPPsPPsPsPs@sPPsPsPPsPsPs@sP@sPPPsPsPPPPPP=SQljl&h&hhhhIcccccccccc=ccccCCccc==cccCCc=====c====b====C==C=;CCCC==C;====;CSC=C=SS=CSCCbbbCCCC=CCCS=C==b=b=b===C=bP&&&&ljj&&hYFY+y,,+,+````+```g`y````g`w$00I0I0I00o[oooo[o oo ooooooooooo@ooVoooo@oV@o0[00wPwPwwPggg>gggwggggwggwg>gwg`>`wwywqwwwywyw`yyw``yyywyyww`yyygyy>yg`gww>ggPgPPPgPgPPPoossPPgPggPgPgPgPgPPN", +"PPsPPPPPsPsPPsPsPPPPs@PPPPPPPPPPsPPssPsP@PPPPsPsPsPPs@Ps@sPP@sPPPPPsPPPPPPPPPsPPP@sPsPsPsujl&ZZlh&hhhhIcccccccccccbCCCcccc=cccccbC=Ccc============c=CC==C==b==cC===CSCS==b=CSCSCCCCS=CSSCb=C=====b=C=b==CCCC=Nh&jjZCCjh&&&j,,++,`,+++,+,``++,`y`+,++`,E,E$@ooooo[oooooooooooo ooooooo@ooo@@@@o@@o@[o@@>gwgg>gw>gwgw>gwgwg>gwgggwyqyyw`>``wy``w+yywyyyywgygy+yyyyyyy`>``yygw`y`wgwwggwwwgwgywwwgPgPgPPgPgPgPgPgg>", +"PsPPPPsPPPPPs@sP@s@sPPsPsPsPsPPsP@sVs@sPssssPPPPPPPPPPPPss@sssPsssPsPPPsPsPsPPPssPPPPPPPs&h&lZZjjh&hhhIccRccccccccc=cccccc==bC==Cc=bc=cc=cc=======c==C===C;=c==C==;==CC===b=C=CCCCC=;CCb=bCCxb=========CC====0YYhjSSSj&&jh&,,,++,+,```,+`+,,+```,++,+,,+,,+,,+,$y+[ooo[ooooooooooooo@ooo@@oo@osg>>>`>wgwgwgwg>gwgwggwg>gwgwgwEEyyyyyyyyy>w`wyyyy>`w`yyyyyq,yyy+yy+yyyyyw`w`w`ygw`y`w`ygyyyw``ywwg`wwgwwPgPPgPgP>", +"PPPsPsP@ss@sPsP@sssPPPPPPPPPPPPPsPsPsPs@s@s@sPsPPPPsPsPPPPPPPs@s@s@sPssPsPsPPPPPPPsPPsPs0&&hjjllZjh&h&IcIccRccccccc==cccbCbc=cccccbcc=c=cc===cc=c=CCc=c=C=C=c====cCS=======c;CCC==C======CCC=;SSCCC==b==cSCCCUYFhjZCjhh&&Yj,,,,,+++,,,++``,,++`+,`+`,,++,++`++``+,+,y,++qoooooooooooo@ooo>>>N>>>g>g>>g>gPgPgPgw>wgwgPgwgwwwq,Eyyq,wyywyyyyqyyyyyyyy>`w,yy`wyyyyyyyyy,y`wyyy`wyyyy`>`wyy``y`>y`y`wy`gy`wwwgwwgwg>", +"sPPPPPsPPss@s@sssPs@sssssPsPsPsPsPsPsPsPsPsPss@ssssPsPsssPsPsPsPsPsPsPPPsPs@ssss@sPss@sso&h&h&lZZj&&h&Icc=cIccIccI=c=======c===b==cc=c=cc=c=c=c=ccCcC==CC=C=C=c=c=C=cCC=====c====bCC=======CCCc=============CohYhjSZ&&&j&Yj,,,,,,,,,+,+,,+,,``+++,,`,+,`,+,,``,,+`+`+++,,,+ywy,oooo@s>>>>>>g>>>>w>sg>>>wg>>wgwgwgPgwgwwwww,EE,qywyqywyyqyEyyyyyyEyq,wyyw,wyyyyyyyyyyyywyy,ywyyyyyw`yyw`wwwyywyww`wywywygw`w`w`wy", +"sPssPsPPsPPssPsPPsPPsP@s@ssPsPs@sP@sP@sPsPsPPsPsPPsPsPsPPsP@sP@sPs@s@s@s@s@sPPPssPsPPsPsI&&&&&jZZjjh&hIc=cccIccccc=cccccccc=c===ccccccccccc=cc==cc==bc======cbcb=cb=====ccc==c=C=C=C=CCC==CbbC==c===CC===CCCC0Yh&Sjjj&&&j&&,,,,,,,,+,,,,,,,,,,,,,,++````,,,+,,+,``,`+,`,`,`,```y+++y>>>>>>P>>>>>>g>w>gPgswgw>gPgwwwwEEwqyqwqyqwyEE,EEyE,qyyqywyq,EyE,wyyyy,Ey,Eyyyyyyyyyyyyyyyyyyyww`y>+y`w+yy`wyyyy`w`wy`ww`>`w", +"sPs@s@sPs@sPs@Ps@s@s@PsPsPPsPsPsPsPsPsPsPsPs@s@s@s@s@s@s@s@ss@sPsPsPsPssPsPsPssPPPPPs@ss0&&&&h&jZljl&lIccccccccIc==ccc==ccc==ccccccccc=ccc=cccc=ccc=====c==c=c=Ccc=cc==cc==ccc=CC====CCC=C=C====CC;CCCC;CCC==oYhjZhhjj&&j&l,,,,O,,,+,+,,,!,,,,,,+++,,,,,,+,,,++++,+,,+,+,+`++`++++````gy>Pg>>w>>PwPgwPw>wgPg>w>wEwqqEEEEEEEyqEEwwyEEEywwyqEywyE,EEyyq,qyEEE,Eyqyyyywyyyw,Eyyyyygyyyywyyw`yyyyyyyyyyyyyw`ywyyywgy", +"PsPsPs@s@s@s@ss@sssss@ss@s@sPsPPsPsPsPsPsPsPsssssPs@ss@ss@s@ss@s@s@s@s@ssssPssPsssssssss0&j&&&&jZSjZlZIcI==cc===ccccc=cc===ccccccccccccccccccc=cccc==c==C=cC=cc=====c==c=c=c==cC==C=c===CCCC=CCCCCC====CC=c== hjS&hhhj&&h&j,,,,,y,,,,,,,,,,,,,,,,,,,,,,,,!,,,,+`+,,,++,,,+,`,++++++,++g,`+,``PgPwgPPPgP>>PwP>swq$$E$EEEEEEwqEEEEEEEEEEEEEE,qEEEE,wwEEE,qyyEEE,EyqEywyqyyyyyyyyE,wyyyyyyqwyyyyyq,y,wyyyyw`wyyy`wy", +"sPsPPsssssPsss@s@s@@s@s@s@ss@s@s@s@s@ssssssssPsPsPssPssPsssss@s@s@ss@ss@s@s@s@s@s@s@s@s@0&&&j&hjlSZZS=IcI==cc==ccccc=c=cc=cccIccccccccccccccccccccc==cccCcb==ccc=Cc==c=ccccccccC====CC==CcC=CCC==CCCCCCCcc=c=0&CZhYhhh&&&&&,,,,,+,,,,,,,,,,,,,H,,,H,,,,,,,,,```,,,`+`,,+,+``+,+,+++,`,+`,y``y`w``Psgws>w>PgwwwwE$wwq$EEEEEEEEEEEEEEqEqywqEEEyEEEEEyE,EqwEE,E,EEEyyqyEqyEqyEEyEyEyyEywyEyyyywyyywyEyyqyyyyw`ywwyy", +"ssssssPsPssPsPssssssssss@s@s@ss@ss@ss@s@s@s@s@s@s@ssPsPsPPsPsssssssPssPsPsss@s@s@s@s@ss@0&&j&j&jSCZjZcIcIcccccc=ccccccccIccIccccccccccccccccccccccccC==c=cC=cccccCcccc==ccccc=c====C==ccc=c===c=C===CC===cc==[==jhhhhhj&h&j,,,,,,,,,,,,,,,,,OE++,,+,,,,,,,,,,`,+,,,+``,,,+`+,``,,`,,,`++,,```y``y`y`y>PgPss$$$$$$$$E$E$EEEEEEEEEEEEEEEEEEEEEEEEEEEEqwEEEEqqwEEyEEEEEyywyEE,qE,EywEyE,EyywyqyE,wyq,qyyqqqEywwy`w>", +"s@s@s@s@s@PsPsPsPPPPPPPssssssssssPssPsss@s@s@s@ss@s@@s@@s@@s@s@s@s@ssssssPsssssssssssss$IZj&j&j&ZZZjjIIcI=IcIcccccIIcIcIccIccccccIccIcccccIc==cccc==c==c====ccccc=cc=ccCccccccc===cccccc=ccCc==c===c====c=c==ocC&&&&hh&ll&&O,$O,,,,,,O,,,,,,,,,,,,,,,,,,,,,,,,,,,,+,,,,,,,`+,+,``+,++,`+,+y`+`y`y`y`+y`w`y$$$$$qs$E$$E$$$sE$$E$qq$EEE$EwEEEEEEEEEEqEEEEyEEEEqyEEEEEqwyEEqywqyEEEyw,qwywwyEyyyEEw,wywyq,yyEyywwwy", +"s@s@s@s@s@s@@s@@s@s@s@s@s@sssPsPsssssssssssssssssssssssssss@s@s@s@s@@s@@@s@@s@s@s@s@@s$$IccSSCl&CI=CZIIIcccccccIcIcccccIcIccIcIcccccccIccICcccc=c===c=c=ccccccccc=cccc=ccccccc=====ccc=cccc=====C===c==cC===cocj&&j&&hhjjj&$,,E,OE,,,E,,,,,,,,,OE,,,,,H++,,,,,,H+`,,,,,,+,+,,,,,,,,,,`+,++`+y`++`y+`y++````y`w$$$$$$$$$E$E$$E$$$$Eq$$Ewwqwq$EEqEEEEEEEqEEEqqEqEEEEEEwwqyEEEEEEEEEEyEywywE,EqqyyywqE,qEEEyywqyywy", +"s@s@ss@s@s@ss@s@sssssssssss@ssss@Ps@sPsPsssssssss@s@s@s@ss@s@s@s@G~G!!H~H!!H!HH!!~!!GH!O!GGGOOOOOOOOOOO,!!!O!G!O,O$$OBBBBB0000000IcIIccIcccIcc=c=c=cc=cccc=cccIc=cccccc==ccccc====ccccccccC=Cc=c==C=C===c====@=&&&&&&&h&Zlj$O$O,$,,,,,,,,EO,,,,E,O,,,,,,,,,!,,,,,,`,,,,,,,,,++`,+,,++,,,+`,+,+,+++`y``y`yyy+w```+$$$$$$$$s$$$$$$$$$$$$$qsqqqE$E$E$E$qwwEE$EEEEEEEEEqEwwEEEEEEEqwyqwwEEEEEEEEqEEEEEEwwyyyyEEEEy>w", +"sssss@s@s@s@s@ss@s@@s@@s@@s@s@@s@s@s@s@s@sPsG!!,!!!/~!OGO!HGG!!G!!!!!H!!!!!GOG!G!!!~~~HOH,!,!,!,O,G,!,!!!,!,~,!HG!OOOOOOOOOOOOOH!~~~!,~,$OBBBBB0IIccccccc=cIccccc==ccccccccccc==c=cc=c======cccc===c=cccc===c[=jh&&j&&&&&&j,$,$,,,O$,O$O$,,,,,,O$,,,,,+,,,,,,,,,,,,+,,,H,,,,,,+,,,,,,++,,++,,+,,,y++,y`y+++``yy`yy`yy$$$sE$$$$$$$$$$$$$s$$$$$$$$$$EEE$$Eq$E$E$E$E$EEwwqEEEEEEEEEqEEEEqwEEEEywqEqqqEyEwwEEEEyw>yE", +"@s@s@s@s@$@s@$@s@s@s@s@s@ss@sEE!/!HHHGH/~O!OOGH!O~/~~~!,OOOH!HG!!H,OOOGOOOOO,!~+//`/K+~`//!!!!!!!~!OG~~~!!////H!!~,O,OGO,O!~~,~!,H,!~!~~/`,OOOOGOOO$OBBI0IcccIccc=IcIcccccccIcc===cc====cIc=c=====Icc=ccccc==oSj&h&&j&&&&&j$O,$O,,E,,$,,,,,,,,,E,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+,,,,,,,,,+,+y`,++y`,,+y`+yy,`y`+yyy$$$$$$$$$$$$$$$$$$$$$$$$$$$$$E$$$E$E$E$$E$$$$qEEE$E$$EEqEEwwwwwqEEEEEEEEEEEEqEEEEqEEqwww", +"ssssssssss@$$s$s$O,G!G!GO~`~HOO!!!!OG!H!!!!,~!HGOOOOGGOOGOO,OGGG!!GO,O!G,O!HGG!HHOOG!!H!!33!3!!!!!~!!!H!~!~3!H!,!!!O!,~~/~//~~~!GO!~~~////!O,G,!~!O!,OOOG!~$$B0I0ccccIccI=cc=cc=cccccccccccIccccc==cccIcI====0Slj&&&&&&&&j&$,$O$$OE,,,,,,,,,O$,,,,,,,,,OE,,O$OE,,,,,,,,,,,,+,,,,,,,++,,,++,,,,,,,,,`,,,y`y`yy,+`yy+y+y+y+yy,$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$E$$$$$E$q$EEq$EEE$EEEEEEEEqEEEqqEEEEqEEEEEwwwqEqwqw", +"s$ssO$OOO,GHOOO!!OOO!HH~/OG!!~~~~!!!!~!GOGH!HGGOOG!HOOOOOG!!!!!!!!~~!!!GG!!GOO!!~HHHHHGGGGOGH!~!,H!!!!,!G!~~~/~~!!!~~~~~/~~~~~!~~~!G!!!~//`/~!OG!~,~!~~/+~K,OOOOOO$$B00cIcccccc=IcIccIc=IccccIcccccccIcc=cccc0ZZZjjZjj&&&&&$O,$,,E,$O,$O,,,,$,$O,,,,,$O$,,,E,,EO,EO,,,,,,,,,,,,,,,,,,,+,,,+,,++,,,yy,,,,,,,,,+yy`,`y,y`,yy`yyyyy$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$q$$$$$Esw$$$qw$E$$$$$qqEwwE$E$EEqwqEqqEEEEEEqqqqE", +",,,,,,y,,y,```yy,,y,,+y,,,,,,,,,,,,+,,,,,,,,,,,,,,,+,,,y,yy,,,y,y,y,+y,yy,yy`yyyyyy$$$$$$$$$$$$$$$$$$$$$$$sq$$$$$$$$$$ws$$$$$$q$$$E$$$w$$$EEqE$wqEE$ww$EEqwEEEEE", +"OOH!/~~~!OHHG,OOOOOOG,!OOOOOOOOOOOOOOOOH!~~////~~~H,GOOG`yyy`y`yy+y+`y++``y+y+``y++`>`y`yy`yy+y,yyy`>+y+y+y+`y,,,,,,,,,y,y,,,,,,y+,,y,,yy+,y,y,yyyyyyyy`>yyyyy$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$sqs$$$$$$$$q$$$wq$$sq$q$$w$wq", +"~~HOOOOOOOOOOOOGH!,~~!!!OOOOOGHHH`yyyyy`y+y`yy,y+yy`yy`y`y`+++yy`+y+y`y+y`+y``+y``+y``+y+y`yy`y`y,y`+y,+,+,,y,y,+,yyyyy,y,y,,y+y,+yyyyyyyyyyyyEyw$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ws$$$$$$$$q$$$$qsq$q", +"OH!!OOOOGHOOOO!HHGOOOOOOH,!~~!GOOOOOOOOOOOH!HGOOH!!!!!!!!H!!!~!~!~!!!!~~K/~!!!H~/~//~!!GOOOGOOOH!~~3HHOOOOOGGH!~//~~~//~!HGHHH!!!!!!~~!!!HH!3KK~!!3~!OGOH!~!~!,!~H33~~~!,//~!~/!G~~~,!`,!OOO,O0cCCSZCCZSZCSlZCZCZZCCZCZCCZl$wyyyyyyyy`yyy`yyy+`>+`yyyy`yy,yy,`y+`y`y+y``yg,+y`y+y+y+``y,`y`y+y+y`y+y+y`yy,,,E,,y+y+,,y,y,yy,E,yy,yyyyyyyyyyyy>qy$$$$$$$$$$$$$$$$q$$$$$$$$$$$$$$q$$$$$$$$$$$qs$$$$qsws$$sqsqs$$$$", +"!!~!!OOHO,!!G,~~!!OOOOOOOOOOOOOGOOOOOOOOOOOOOOOOGH!!H!HO!HH!!~~~!!!!3~~K~K33~~~~///~~!!!!HG`yyyy`yyyyy``y`+y```yyy+y``y`++y``gy+yyyygyyy+y,y,,yy,y,,,y,y,yy,yy,yyyyy,yy>yyyywyw$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$q$$$$$$$$$$$$$$$$$$q$$", +"K///`/`////~~H!!H!!HGOOOOOOOOOO!~~~!!GOOGHHH,G!~~~H!HGGGHG!HH!!3~~~!!!!!!!!!~!~~~~~~~~/~~HH!3!HGGOOOH`y`yyy`y`yyyyyy`y,y`>+y`yg``yyy+y`yy`yy`yyy`>`yy`yyyyyyyyyy+gy`y,,y+y`yy,yy+,E,E,E,y,yyy,y,y,yyyyEyyyyyyyy>ywwww$$$$qs$$$$$$$$$$$$$$$$$$$$q$$$$$$$$s$$$$ws$$$ws$$$$$$$$$$", +"33~K~~K~3y>yy>`>y>yyyyyy,yyyy,yyyyy``y`y`y`yy``yy`yyyyyyyyy+y,yyy,y+y+y+y,yyyy`y`y`ygyyyyyyy,yy,y,y,y,yyyyyyEy,yyyyyEEyyEyyy>ywqqq$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Esq$$$w$$$$$$$$", +"OOGHGGOOGHH!HOOOOOOOOOOOGOOOH!HGHHH!HGOOO!~~~~~!~!OOOHHH!,3!!!!~~3!!GOGOOH3/~~33!!!!GOOGH!~~~///K~3K~~~~!GO!!`yyyyygyy>gyyyyyyyyyy,yyyyy>yyyyyyyy`yyy+y``y+y+y`yy+yy+yyy+yy+y``yyyyyyy,y`yyy`yyyy,yyyyyE,y,E,E,EyyEyyE`yyEyEywyEywywwww$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ws$s$$$$$$", +"GHyy>`wyyyy>`yy`yyyyyyy,yyyyy`y,yy+yyyyyyyyy,yy,yyyyyyyyyyy,yyy`yyyyyy`>`yy`yyyyyyyyyyyyyyyyyyyyyyEyy,E,E,EyyE,EyyEyyyEEyqyw>yqw>qE$B$$$$$$$$$$$$$sq$$$$$$$$$$$$$$$q$$$$$$$$$$$$$$$", +"OOOOOG<<ywyyyyyy>`>yyyyyyyyyyyyyyyyy,yyy,yyy,yyyyyyyyyyy``>,yyy,yyyyy,yyyyyyyyyyyy,yyyyyyyyyyyyyy>`>ygyyyyyyEyyE,EyEyqyyEyyyyqwywwwEwwwwwE$$$$$$$$$$$$$$$$$q$$$$$$$$$$ws$$$$$$$$$$$$$$$", +"H!<222~333yyyyyyyyyyyyyyyyyyyyyyyyyyyyy>`>yyyyyyEyEwyywywwyEwwywyqwww>Eqwwq$$$$$$$$$$$$$$$sw$$$$$$$$$$$$$$$$$$sqsws$", +"2322!HOOOOOH!ywyyEyy>yyyyyyyyEy>yyyyyyyy,E,Ey>`yyyyyyy,y>`wyyyyyyyy,yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyEyyyyyy>ywywyEyyEEEyqqyqyqywEqEywqw>wwwwqw$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$q$$$$$$$", +"OOOOOGH!!23!wyyy>wyy>>EyEyyyyEE,EyyEyyyEyyyyyyyyyyyy`yyyyyyyyyyyyyyyyyE,yyyyyyyyy>yyyyyyyyyyyyyyyyyyyyyyyywywyyyywyyyyyyyEyEyEEqEEEwwwqwqEwwwwww$>$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$q$$$", +"HH<yyEyyE,y>`wyyyyyyEyyqyyqyEqyqyw>yqywywyyqqq>wwqywEEEEqEEEywqEEwwwwww>www>$sw$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"GGGHHHHHHyqyywyyEqyyEEyyyyyEEyyyEyEyyEEyyyEyyyyEyy,yyEyyyy>yyy+>yyyyyyyyqyyyy>yEyEwyyqyyyqyyqyyyyqywywqwywyywywywywyEqyEqwEqwwEqww$q>qsqwqPqs$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"OOOOOGHGOOOGHGHHyqyw>ywqywyqyywyqywywyyyEyE,qEyyEyyyyyEyyyyE,EE,yyyyyyyyyyyyyyyyyywyy>ywy>`wyEywyywEyyqyywwqEywqyqyqwqqqwwwq>wwqqwwqEqwwEEwqqqqwwwww>wsqs$$$$$$$$$$$$$$$$$$$$$$$$$$$$sq", +"OOOOOOOGHHGOOOOOOOOOOOOOOOOGHH!2~K~3332<22<<ywyywyyqywyyyEyyEqEyyEEyEywyyEy>y>>yEEEqyEEqywyEyy>y>yyywyyEyqy>yEEqywqEywywqywqwywyqwww>wwwwwwwww>wwqwqw$E$w$Esw$qsw$qsws$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"KK~2HGGGOOOHOOOOOHH!!!HOOOOOOOH>yyyyyq,EyyyyqEEyEyEyEyyyyyyEyEyEyywyyEyEyyEywyqEEyEEyEywyqqywwywwywwqqqwwwyqqywqwqwwww>qqwqqqwEEEqEqE$wwq$>wswsws$s$$$$$$$$$$$$$$$$$$$$$ws", +"~22wwwwwwwqwwwwwEwwwwEwwEqwwqwwqww$E$w$Esqsq$Esqs>qwsw$$$$$B$$$$$$$$$$$$$$$", +"~22wEyEEEEEEEqywwyqywyEEEyqyqEEwyqEEyqEEywEyqEEEyEEyEEEyqyEwqyqyEyqqywqEEwwyqEwyEEqqqw>>yEw>wqww>w>wqw>Ewqwwwww$wswwwww>w$>sEs$$w$$qswssws$$$$$$$$$$$$$$$$$$$", +"wwwqEEEEwwwwwqwwwwwwwwq>wEqwwqwqwwsqwwwwws$sqswwwEsw$sw$sw$qw$wsw$$$$$$$$$$$$$$$$", +"GOOOOOOGOGGGHH!232!22wwwEEEqqqqqw>w>wqqwwEEqEEEEqEqqEEqqqEqqEwwwwEEwqEEEEEEEwqEqEqEwwwqqqwwqwwwwqEqwwwEqqEwqE$wwsw$wswq$ws>swqwsq$sws$$q$swsws$swswswsq$$$$$$$$$$$$$", +"OOOOOOOOHH!33qEEqqwwywwy>>yqEqqwqqEqEEEEEEEEEqEEEqEqwwwEwwEEEqwwqEwwwwEqEEqEqEEwwqqwwqEwEwwEwqq$qwwq$Eww$wwEsw$wswwsqswsqswsqs$$$s$sw$sqsqsqs>$sw$$$$$$$$$", +"OOOGHHwEEwwwqEEwwqEEEEqEEwqww$Ew>wwwwsE$wwwsws>sww$wsws$wsqs>w$ws>$wswqwssw$qsw$$w$swswsq$$$$$$", +"sws$sws$s$qs$swswEswswsw$$$s", +"HHGGGGGOOGGHHHHGOOOOOGOHHs$qs$q$wswwswswswsqs$qq$swswswsswsswsqsws$$ws$$qwssw$s", +"OOOOOOOOOHHHHHGHH<<$wssE", +"OOOOOOOGOOOOOOOOOOGOOOOH<<2!$sq$s$$$swsqsswsw$swsqsqsw$sqwsqsqsws$sq$sw$w$", +"OOOOOOOOOOOOOOOOOOOOOH22!<q$$$E$$$w$$$$q$$$q$$E$ws$q$sqs$$sE$q$$ss$sws$$$$s$sqsww$sEswsEsqswsqswPwsws$qsqsqsws>sqsq>sws>$s$sq", +"OOOOOOOOOOOOOOOOGH!<<<<swsswswswsqsqsqswswsq", +"OOOOOOOOOOOOGH$ws$sqsqswswsqsqsws", +"OOOOOOOOOOOGGGOOOOOOOH<<$s$$$q$$$s$$$$$wsq$q$Eswsw$qsw$$$$$sw$sq$$qsqsqsw$s$s$wPws$q$$ssEs$$q$$Eswswsw$$w$ws$qsqwsw$$swsq$$wsqsq" +}; diff --git a/win/X11/tile2x11.c b/win/X11/tile2x11.c new file mode 100644 index 0000000..a254213 --- /dev/null +++ b/win/X11/tile2x11.c @@ -0,0 +1,235 @@ +/* + * Convert the given input files into an output file that is expected + * by nethack. + * + * Assumptions: + * + Two dimensional byte arrays are in row order and are not padded + * between rows (x11_colormap[][]). + */ +#include "hack.h" /* for MAX_GLYPH */ +#include "tile.h" +#include "tile2x11.h" /* x11 output file header structure */ + +#define OUTNAME "x11tiles" /* output file name */ +/* #define PRINT_COLORMAP */ /* define to print the colormap */ + + +x11_header header; +unsigned char tile_bytes[TILE_X*TILE_Y*(MAX_GLYPH+TILES_PER_ROW)]; +unsigned char *curr_tb = tile_bytes; +unsigned char x11_colormap[MAXCOLORMAPSIZE][3]; + + +/* Look up the given pixel and return its colormap index. */ +static unsigned char +pix_to_colormap(pix) + pixel pix; +{ + int i; + + for (i = 0; i < header.ncolors; i++) { + if (pix.r == ColorMap[CM_RED][i] && + pix.g == ColorMap[CM_GREEN][i] && + pix.b == ColorMap[CM_BLUE][i]) + break; + } + + if (i == header.ncolors) { + Fprintf(stderr, "can't find color: [%u,%u,%u]\n", pix.r, pix.g, pix.b); + exit(1); + } + return (unsigned char) (i & 0xFF); +} + + +/* Convert the tiles in the file to our format of bytes. */ +static unsigned long +convert_tiles(tb_ptr, total) + unsigned char **tb_ptr; /* pointer to a tile byte pointer */ + unsigned long total; /* total tiles so far */ +{ + unsigned char *tb = *tb_ptr; + unsigned long count = 0; + pixel tile[TILE_Y][TILE_X]; + int x, y; + + while (read_text_tile(tile)) { + count++; + total++; + for (y = 0; y < TILE_Y; y++) { + for (x = 0; x < TILE_X; x++) + tb[x] = pix_to_colormap(tile[y][x]); + tb += TILE_X * header.per_row; + } + + /* repoint at the upper-left corner of the next tile */ + *tb_ptr += TILE_X; + if (header.per_row == 1 || (total % header.per_row) == 0) + *tb_ptr += TILE_X * (TILE_Y - 1) * header.per_row; + tb = *tb_ptr; + } + + return count; +} + + +/* Merge the current text colormap (ColorMap) with ours (x11_colormap). */ +static void +merge_text_colormap() +{ + int i, j; + + for (i = 0; i < colorsinmap; i++) { + for (j = 0; j < header.ncolors; j++) + if (x11_colormap[j][CM_RED] == ColorMap[CM_RED][i] && + x11_colormap[j][CM_GREEN] == ColorMap[CM_GREEN][i] && + x11_colormap[j][CM_BLUE] == ColorMap[CM_BLUE][i]) + break; + + if (j >= MAXCOLORMAPSIZE) { + Fprintf(stderr, "colormap overflow\n"); + exit(1); + } + + if (j == header.ncolors) { /* couldn't find it */ +#ifdef PRINT_COLORMAP + printf("color %2d: %3d %3d %3d\n", header.ncolors, + ColorMap[CM_RED][i], ColorMap[CM_GREEN][i], + ColorMap[CM_BLUE][i]); +#endif + + x11_colormap[j][CM_RED] = ColorMap[CM_RED][i]; + x11_colormap[j][CM_GREEN] = ColorMap[CM_GREEN][i]; + x11_colormap[j][CM_BLUE] = ColorMap[CM_BLUE][i]; + header.ncolors++; + } + } +} + + +/* Open the given file, read & merge the colormap, convert the tiles. */ +static void +process_file(fname) + char *fname; +{ + unsigned long count; + + if (!fopen_text_file(fname, RDTMODE)) { + Fprintf(stderr, "can't open file \"%s\"\n", fname); + exit(1); + } + merge_text_colormap(); + count = convert_tiles(&curr_tb, header.ntiles); + Fprintf(stderr, "%s: %lu tiles\n", fname, count); + header.ntiles += count; + fclose_text_file(); +} + + +#ifdef USE_XPM +static int +xpm_write(fp) +FILE *fp; +{ + int i, j, n; + + if (header.ncolors > 64) { + Fprintf(stderr, "Sorry, only configured for up to 64 colors\n"); + exit(1); + /* All you need to do is add more char per color - below */ + } + + Fprintf(fp, "/* XPM */\n"); + Fprintf(fp, "static char* nhtiles[] = {\n"); + Fprintf(fp, "\"%lu %lu %lu %d\",\n", + header.tile_width*header.per_row, + (header.tile_height*header.ntiles)/header.per_row, + header.ncolors, + 1 /* char per color */); + for (i = 0; i < header.ncolors; i++) + Fprintf(fp, "\"%c c #%02x%02x%02x\",\n", + i+'0', /* just one char per color */ + x11_colormap[i][0], + x11_colormap[i][1], + x11_colormap[i][2]); + + n = 0; + for (i = 0; i < (header.tile_height*header.ntiles)/header.per_row; i++) { + Fprintf(fp, "\""); + for (j = 0; j < header.tile_width*header.per_row; j++) { + /* just one char per color */ + fputc(tile_bytes[n++]+'0', fp); + } + + Fprintf(fp, "\",\n"); + } + + return fprintf(fp, "};\n") >= 0; +} +#endif /* USE_XPM */ + +int +main(argc, argv) + int argc; + char **argv; +{ + FILE *fp; + int i; + + header.version = 2; /* version 1 had no per_row field */ + header.ncolors = 0; + header.tile_width = TILE_X; + header.tile_height = TILE_Y; + header.ntiles = 0; /* updated as we read in files */ + header.per_row = TILES_PER_ROW; + + if (argc == 1) { + Fprintf(stderr, "usage: %s txt_file1 [txt_file2 ...]\n", argv[0]); + exit(1); + } + + fp = fopen(OUTNAME, "w"); + if (!fp) { + Fprintf(stderr, "can't open output file\n"); + exit(1); + } + + /* don't leave garbage at end of partial row */ + (void) memset((genericptr_t)tile_bytes, 0, sizeof(tile_bytes)); + + for (i = 1; i < argc; i++) + process_file(argv[i]); + Fprintf(stderr, "Total tiles: %ld\n", header.ntiles); + + /* round size up to the end of the row */ + if ((header.ntiles % header.per_row) != 0) { + header.ntiles += header.per_row - (header.ntiles % header.per_row); + } + +#ifdef USE_XPM + if (xpm_write(fp) == 0) { + Fprintf(stderr, "can't write XPM file\n"); + exit(1); + } +#else + if (fwrite((char *)&header, sizeof(x11_header), 1, fp) == 0) { + Fprintf(stderr, "can't open output header\n"); + exit(1); + } + + if (fwrite((char *)x11_colormap, 1, header.ncolors*3, fp) == 0) { + Fprintf(stderr, "can't write output colormap\n"); + exit(1); + } + + if (fwrite((char *)tile_bytes, 1, + (int) header.ntiles*header.tile_width*header.tile_height, fp) == 0) { + + Fprintf(stderr, "can't write tile bytes\n"); + exit(1); + } +#endif + + fclose(fp); + return 0; +} diff --git a/win/X11/winX.c b/win/X11/winX.c new file mode 100644 index 0000000..14f0e90 --- /dev/null +++ b/win/X11/winX.c @@ -0,0 +1,2076 @@ +/* SCCS Id: @(#)winX.c 3.4 1999/12/21 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * "Main" file for the X window-port. This contains most of the interface + * routines. Please see doc/window.doc for an description of the window + * interface. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#ifdef MSDOS /* from compiler */ +#define SHORT_FILENAMES +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* for color support */ +#ifdef SHORT_FILENAMES +#include +#else +#include +#endif + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#ifdef SHORT_FILENAMES +#undef SHORT_FILENAMES /* hack.h will reset via global.h if necessary */ +#endif + +#include "hack.h" +#include "winX.h" +#include "dlb.h" +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +/* Should be defined in but you never know */ +#ifndef XtSpecificationRelease +#define XtSpecificationRelease 0 +#endif + +/* + * Icons. + */ +#include "../win/X11/nh72icon" +#include "../win/X11/nh56icon" +#include "../win/X11/nh32icon" + +static struct icon_info { + const char *name; + unsigned char *bits; + unsigned width, height; +} icon_data[] = { + { "nh72", nh72icon_bits, nh72icon_width, nh72icon_height }, + { "nh56", nh56icon_bits, nh56icon_width, nh56icon_height }, + { "nh32", nh32icon_bits, nh32icon_width, nh32icon_height }, + { (const char *)0, (unsigned char *)0, 0, 0 } +}; + +/* + * Private global variables (shared among the window port files). + */ +struct xwindow window_list[MAX_WINDOWS]; +AppResources appResources; +void FDECL((*input_func), (Widget,XEvent *,String *,Cardinal *)); +int click_x, click_y, click_button; /* Click position on a map window */ + /* (filled by set_button_values()). */ +int updated_inventory; + + +/* Interface definition, for windows.c */ +struct window_procs X11_procs = { + "X11", + WC_COLOR|WC_HILITE_PET, + 0L, + X11_init_nhwindows, + X11_player_selection, + X11_askname, + X11_get_nh_event, + X11_exit_nhwindows, + X11_suspend_nhwindows, + X11_resume_nhwindows, + X11_create_nhwindow, + X11_clear_nhwindow, + X11_display_nhwindow, + X11_destroy_nhwindow, + X11_curs, + X11_putstr, + X11_display_file, + X11_start_menu, + X11_add_menu, + X11_end_menu, + X11_select_menu, + genl_message_menu, /* no need for X-specific handling */ + X11_update_inventory, + X11_mark_synch, + X11_wait_synch, +#ifdef CLIPPING + X11_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + X11_print_glyph, + X11_raw_print, + X11_raw_print_bold, + X11_nhgetch, + X11_nh_poskey, + X11_nhbell, + X11_doprev_message, + X11_yn_function, + X11_getlin, + X11_get_ext_cmd, + X11_number_pad, + X11_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + X11_start_screen, + X11_end_screen, +#ifdef GRAPHIC_TOMBSTONE + X11_outrip, +#else + genl_outrip, +#endif + genl_preference_update, +}; + +/* + * Local functions. + */ +static void FDECL(dismiss_file, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(delete_file, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(yn_key, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(yn_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(askname_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(getline_delete, (Widget, XEvent*, String*, Cardinal*)); +static void FDECL(X11_hangup, (Widget, XEvent*, String*, Cardinal*)); +static int FDECL(input_event, (int)); +static void FDECL(win_visible, (Widget,XtPointer,XEvent *,Boolean *)); +static void NDECL(init_standard_windows); + + +/* + * Local variables. + */ +static boolean x_inited = FALSE; /* TRUE if window system is set up. */ +static winid message_win = WIN_ERR, /* These are the winids of the */ + map_win = WIN_ERR, /* message, map, and status */ + status_win = WIN_ERR; /* windows, when they are created */ + /* in init_windows(). */ +static Pixmap icon_pixmap = None; /* Pixmap for icon. */ + +/* + * Find the window structure that corresponds to the given widget. Note + * that this is not the popup widget, nor the viewport, but the child. + */ +struct xwindow * +find_widget(w) + Widget w; +{ + int windex; + struct xwindow *wp; + + /* Search to find the corresponding window. Look at the main widget, */ + /* popup, the parent of the main widget, then parent of the widget. */ + for (windex = 0, wp = window_list; windex < MAX_WINDOWS; windex++, wp++) + if (wp->type != NHW_NONE && + (wp->w == w || wp->popup == w || (wp->w && (XtParent(wp->w)) == w) + || (wp->popup == XtParent(w)))) + break; + + if (windex == MAX_WINDOWS) panic("find_widget: can't match widget"); + return wp; +} + +/* + * Find a free window slot for use. + */ +static winid +find_free_window() +{ + int windex; + struct xwindow *wp; + + for (windex = 0, wp = &window_list[0]; windex < MAX_WINDOWS; windex++, wp++) + if (wp->type == NHW_NONE) break; + + if (windex == MAX_WINDOWS) + panic("find_free_window: no free windows!"); + return (winid) windex; +} + +/* + * Color conversion. The default X11 color converters don't try very + * hard to find matching colors in PseudoColor visuals. If they can't + * allocate the exact color, they puke and give you something stupid. + * This is an attempt to find some close readonly cell and use it. + */ +XtConvertArgRec const nhcolorConvertArgs[] = { + {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.screen), + sizeof(Screen *)}, + {XtWidgetBaseOffset, (XtPointer)XtOffset(Widget, core.colormap), + sizeof(Colormap)} +}; + +#define done(type, value) \ + { \ + if (toVal->addr != 0) { \ + if (toVal->size < sizeof(type)) { \ + toVal->size = sizeof(type); \ + return False; \ + } \ + *(type*)(toVal->addr) = (value); \ + } \ + else { \ + static type static_val; \ + static_val = (value); \ + toVal->addr = (genericptr_t)&static_val; \ + } \ + toVal->size = sizeof(type); \ + return True; \ + } + +/* decl.h declares these, but it screws up structure references -dlc */ +#undef red +#undef green +#undef blue + +/* + * Find a color that approximates the color named in "str". The "str" color + * may be a color name ("red") or number ("#7f0000"). If str == NULL, then + * "color" is assumed to contain the RGB color wanted. + * The approximate color found is returned in color as well. + * Return True if something close was found. + */ +Boolean +nhApproxColor(screen, colormap, str, color) +Screen *screen; /* screen to use */ +Colormap colormap; /* the colormap to use */ +char *str; /* color name */ +XColor *color; /* the X color structure; changed only if successful */ +{ + int ncells; + long cdiff = 16777216; /* 2^24; hopefully our map is smaller */ + XColor tmp; + static XColor *table = 0; + register int i, j; + register long tdiff; + + /* if the screen doesn't have a big colormap, don't waste our time */ + /* or if it's huge, and _some_ match should have been possible */ + if((ncells = CellsOfScreen(screen)) < 256 || ncells > 4096) + return False; + + if (str != (char *)0) { + if (!XParseColor(DisplayOfScreen(screen), colormap, str, &tmp)) + return False; + } else { + tmp = *color; + tmp.flags = 7; /* force to use all 3 of RGB */ + } + + if (!table) { + table = (XColor *) XtCalloc(ncells, sizeof(XColor)); + for(i=0; iaddr; + XColor screenColor; + XColor exactColor; + Screen *screen; + XtAppContext app = XtDisplayToApplicationContext(dpy); + Colormap colormap; + Status status; + String params[1]; + Cardinal num_params=1; + + if (*num_args != 2) { + XtAppWarningMsg(app, "wrongParameters", "cvtStringToPixel", + "XtToolkitError", + "String to pixel conversion needs screen and colormap arguments", + (String *)0, (Cardinal *)0); + return False; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + + /* If Xt colors, use the Xt routine and hope for the best */ +#if (XtSpecificationRelease >= 5) + if ((strcmpi(str, XtDefaultBackground) == 0) || + (strcmpi(str, XtDefaultForeground) == 0)) { + return + XtCvtStringToPixel(dpy, args, num_args, fromVal, toVal, closure_ret); + } +#else + if (strcmpi(str, XtDefaultBackground) == 0) { + *closure_ret = (char*)False; + done(Pixel, WhitePixelOfScreen(screen)); + } + if (strcmpi(str, XtDefaultForeground) == 0) { + *closure_ret = (char*)False; + done(Pixel, BlackPixelOfScreen(screen)); + } +#endif + + status = XAllocNamedColor(DisplayOfScreen(screen), colormap, + (char*)str, &screenColor, &exactColor); + if (status == 0) { + String msg, type; + + /* some versions of XAllocNamedColor don't allow #xxyyzz names */ + if (str[0] == '#' && + XParseColor(DisplayOfScreen(screen), colormap, str, &exactColor) && + XAllocColor(DisplayOfScreen(screen), colormap, &exactColor)) { + *closure_ret = (char*)True; + done(Pixel, exactColor.pixel); + } + + params[0] = str; + /* Server returns a specific error code but Xlib discards it. Ugh */ + if (XLookupColor(DisplayOfScreen(screen), colormap, (char*)str, + &exactColor, &screenColor)) { + /* try to find another color that will do */ + if (nhApproxColor(screen, colormap, (char*) str, &screenColor)) { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } + type = "noColormap"; + msg = "Cannot allocate colormap entry for \"%s\""; + } + else { + /* some versions of XLookupColor also don't allow #xxyyzz names */ + if(str[0] == '#' && + (nhApproxColor(screen, colormap, (char*) str, &screenColor))) { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } + type = "badValue"; + msg = "Color name \"%s\" is not defined"; + } + + XtAppWarningMsg(app, type, "cvtStringToPixel", + "XtToolkitError", msg, params, &num_params); + *closure_ret = False; + return False; + } else { + *closure_ret = (char*)True; + done(Pixel, screenColor.pixel); + } +} + +/* ARGSUSED */ +static void +nhFreePixel(app, toVal, closure, args, num_args) +XtAppContext app; +XrmValuePtr toVal; +XtPointer closure; +XrmValuePtr args; +Cardinal *num_args; +{ + Screen *screen; + Colormap colormap; + + if (*num_args != 2) { + XtAppWarningMsg(app, "wrongParameters", + "freePixel", "XtToolkitError", + "Freeing a pixel requires screen and colormap arguments", + (String *)0, (Cardinal *)0); + return; + } + + screen = *((Screen **) args[0].addr); + colormap = *((Colormap *) args[1].addr); + + if (closure) { + XFreeColors( DisplayOfScreen(screen), colormap, + (unsigned long*)toVal->addr, 1, (unsigned long)0 + ); + } +} + +/* [ALI] Utility function to ask Xaw for font height, since the previous + * assumption of ascent + descent is not always valid. + */ +Dimension +nhFontHeight(w) +Widget w; +#ifdef _XawTextSink_h +{ + Widget sink; + XawTextPosition pos = 0; + int resWidth, resHeight; + Arg args[1]; + + XtSetArg(args[0], XtNtextSink, &sink); + XtGetValues(w, args, 1); + + XawTextSinkFindPosition(sink, pos, 0, 0, 0, &pos, &resWidth, &resHeight); + return resHeight; +} +#else +{ + XFontStruct *fs; + Arg args[1]; + + XtSetArg(args[0], XtNfont, &fs); + XtGetValues(w, args, 1); + + /* Assume font height is ascent + descent. */ + return = fs->ascent + fs->descent; +} +#endif + +/* Global Functions ======================================================== */ +void +X11_raw_print(str) + const char *str; +{ + (void) puts(str); +} + +void +X11_raw_print_bold(str) + const char *str; +{ + (void) puts(str); +} + +void +X11_curs(window, x, y) + winid window; + int x, y; +{ + check_winid(window); + + if (x < 0 || x >= COLNO) { + impossible("curs: bad x value [%d]", x); + x = 0; + } + if (y < 0 || y >= ROWNO) { + impossible("curs: bad y value [%d]", y); + y = 0; + } + + window_list[window].cursx = x; + window_list[window].cursy = y; +} + +void +X11_putstr(window, attr, str) + winid window; + int attr; + const char *str; +{ + winid new_win; + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MESSAGE: + (void) strncpy(toplines, str, TBUFSZ); /* for Norep(). */ + toplines[TBUFSZ - 1] = 0; + append_message(wp, str); + break; + case NHW_STATUS: + adjust_status(wp, str); + break; + case NHW_MAP: + impossible("putstr: called on map window \"%s\"", str); + break; + case NHW_MENU: + if (wp->menu_information->is_menu) { + impossible( + "putstr: called on a menu window, \"%s\" discarded", + str); + break; + } + /* + * Change this menu window into a text window by creating a + * new text window, then copying it to this winid. + */ + new_win = X11_create_nhwindow(NHW_TEXT); + X11_destroy_nhwindow(window); + *wp = window_list[new_win]; + window_list[new_win].type = NHW_NONE; /* allow re-use */ + /* fall though to add text */ + case NHW_TEXT: + add_to_text_window(wp, attr, str); + break; + default: + impossible("putstr: unknown window type [%d] \"%s\"", + wp->type, str); + } +} + +/* We do event processing as a callback, so this is a null routine. */ +void X11_get_nh_event() { return; } + +int +X11_nhgetch() +{ + return input_event(EXIT_ON_KEY_PRESS); +} + + +int +X11_nh_poskey(x, y, mod) + int *x, *y, *mod; +{ + int val = input_event(EXIT_ON_KEY_OR_BUTTON_PRESS); + + if (val == 0) { /* user clicked on a map window */ + *x = click_x; + *y = click_y; + *mod = click_button; + } + return val; +} + + +winid +X11_create_nhwindow(type) + int type; +{ + winid window; + struct xwindow *wp; + + if (!x_inited) + panic("create_nhwindow: windows not initialized"); + + /* + * We have already created the standard message, map, and status + * windows in the window init routine. The first window of that + * type to be created becomes the standard. + * + * A better way to do this would be to say that init_nhwindows() + * has already defined these three windows. + */ + if (type == NHW_MAP && map_win != WIN_ERR) { + window = map_win; + map_win = WIN_ERR; + return window; + } + if (type == NHW_MESSAGE && message_win != WIN_ERR) { + window = message_win; + message_win = WIN_ERR; + return window; + } + if (type == NHW_STATUS && status_win != WIN_ERR) { + window = status_win; + status_win = WIN_ERR; + return window; + } + + window = find_free_window(); + wp = &window_list[window]; + + /* The create routines will set type, popup, w, and Win_info. */ + wp->prevx = wp->prevy = wp->cursx = wp->cursy = + wp->pixel_width = wp->pixel_height = 0; + wp->keep_window = FALSE; + + switch (type) { + case NHW_MAP: + create_map_window(wp, TRUE, (Widget) 0); + break; + case NHW_MESSAGE: + create_message_window(wp, TRUE, (Widget) 0); + break; + case NHW_STATUS: + create_status_window(wp, TRUE, (Widget) 0); + break; + case NHW_MENU: + create_menu_window(wp); + break; + case NHW_TEXT: + create_text_window(wp); + break; + default: + panic("create_nhwindow: unknown type [%d]", type); + break; + } + return window; +} + +void +X11_clear_nhwindow(window) + winid window; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MAP: clear_map_window(wp); break; + case NHW_TEXT: clear_text_window(wp); break; + case NHW_STATUS: + case NHW_MENU: + case NHW_MESSAGE: + /* do nothing for these window types */ + break; + default: + panic("clear_nhwindow: unknown window type [%d]", wp->type); + break; + } +} + +void +X11_display_nhwindow(window, blocking) + winid window; + boolean blocking; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + switch (wp->type) { + case NHW_MAP: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + display_map_window(wp); /* flush map */ + /* + * We need to flush the message window here due to the way the tty + * port is set up. To flush a window, you need to call this + * routine. However, the tty port _pauses_ with a --more-- if we + * do a display_nhwindow(WIN_MESSAGE, FALSE). Thus, we can't call + * display_nhwindow(WIN_MESSAGE,FALSE) in parse() because then we + * get a --more-- after every line. + * + * Perhaps the window document should mention that when the map + * is flushed, everything on the three main windows should be + * flushed. Note: we don't need to flush the status window + * because we don't buffer changes. + */ + if (WIN_MESSAGE != WIN_ERR) + display_message_window(&window_list[WIN_MESSAGE]); + if (blocking) + (void) x_event(EXIT_ON_KEY_OR_BUTTON_PRESS); + break; + case NHW_MESSAGE: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + display_message_window(wp); /* flush messages */ + break; + case NHW_STATUS: + if (wp->popup) + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + break; /* no flushing necessary */ + case NHW_MENU: { + int n; + menu_item *selected; + + /* pop up menu */ + n = X11_select_menu(window, PICK_NONE, &selected); + if (n) { + impossible("perminvent: %d selected??", n); + free((genericptr_t)selected); + } + break; + } + case NHW_TEXT: + display_text_window(wp, blocking); /* pop up text window */ + break; + default: + panic("display_nhwindow: unknown window type [%d]", wp->type); + break; + } +} + +void +X11_destroy_nhwindow(window) + winid window; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + /* + * "Zap" known windows, but don't destroy them. We need to keep the + * toplevel widget popped up so that later windows (e.g. tombstone) + * are visible on DECWindow systems. This is due to the virtual + * roots that the DECWindow wm creates. + */ + if (window == WIN_MESSAGE) { + wp->keep_window = TRUE; + WIN_MESSAGE = WIN_ERR; + iflags.window_inited = 0; + } else if (window == WIN_MAP) { + wp->keep_window = TRUE; + WIN_MAP = WIN_ERR; + } else if (window == WIN_STATUS) { + wp->keep_window = TRUE; + WIN_STATUS = WIN_ERR; + } else if (window == WIN_INVEN) { + /* don't need to keep this one */ + WIN_INVEN = WIN_ERR; + } + + switch (wp->type) { + case NHW_MAP: + destroy_map_window(wp); + break; + case NHW_MENU: + destroy_menu_window(wp); + break; + case NHW_TEXT: + destroy_text_window(wp); + break; + case NHW_STATUS: + destroy_status_window(wp); + break; + case NHW_MESSAGE: + destroy_message_window(wp); + break; + default: + panic("destroy_nhwindow: unknown window type [%d]", wp->type); + break; + } +} + +void +X11_update_inventory() +{ + if (x_inited && window_list[WIN_INVEN].menu_information->is_up) { + updated_inventory = 1; /* hack to avoid mapping&raising window */ + (void) display_inventory((char *)0, FALSE); + updated_inventory = 0; + } +} + +/* The current implementation has all of the saved lines on the screen. */ +int X11_doprev_message() { return 0; } + +void +X11_nhbell() +{ + /* We can't use XBell until toplevel has been initialized. */ + if (x_inited) + XBell(XtDisplay(toplevel), 0); + /* else print ^G ?? */ +} + +void X11_mark_synch() +{ + if (x_inited) { + /* + * The window document is unclear about the status of text + * that has been pline()d but not displayed w/display_nhwindow(). + * Both the main and tty code assume that a pline() followed + * by mark_synch() results in the text being seen, even if + * display_nhwindow() wasn't called. Duplicate this behavior. + */ + if (WIN_MESSAGE != WIN_ERR) + display_message_window(&window_list[WIN_MESSAGE]); + XSync(XtDisplay(toplevel), False); + } +} + +void X11_wait_synch() { if (x_inited) XFlush(XtDisplay(toplevel)); } + + +/* Both resume_ and suspend_ are called from ioctl.c and unixunix.c. */ +void X11_resume_nhwindows() { return; } + +/* ARGSUSED */ +void X11_suspend_nhwindows(str) const char *str; { return; } + +/* Under X, we don't need to initialize the number pad. */ +/* ARGSUSED */ +void X11_number_pad(state) int state; { return; } /* called from options.c */ + + +void X11_start_screen() { return; } /* called from setftty() in unixtty.c */ +void X11_end_screen() { return; } /* called from settty() in unixtty.c */ + +#ifdef GRAPHIC_TOMBSTONE +void X11_outrip(window, how) + winid window; + int how; +{ + struct xwindow *wp; + + check_winid(window); + wp = &window_list[window]; + + if (wp->type == NHW_TEXT) { + wp->text_information->is_rip = TRUE; + } else { + panic("ripout on non-text window (window type [%d])", wp->type); + } + + calculate_rip_text(how); +} +#endif + +/* init and exit nhwindows ------------------------------------------------- */ + +XtAppContext app_context; /* context of application */ +Widget toplevel = (Widget) 0; /* toplevel widget */ +Atom wm_delete_window; /* pop down windows */ + +static XtActionsRec actions[] = { + {"dismiss_file", dismiss_file}, /* action for file viewing widget */ + {"delete_file", delete_file}, /* action for file delete-window */ + {"dismiss_text", dismiss_text}, /* button action for text widget */ + {"delete_text", delete_text}, /* delete action for text widget */ + {"key_dismiss_text",key_dismiss_text},/* key action for text widget */ +#ifdef GRAPHIC_TOMBSTONE + {"rip_dismiss_text",rip_dismiss_text},/* action for rip in text widget */ +#endif + {"menu_key", menu_key}, /* action for menu accelerators */ + {"yn_key", yn_key}, /* action for yn accelerators */ + {"yn_delete", yn_delete}, /* action for yn delete-window */ + {"askname_delete", askname_delete},/* action for askname delete-window */ + {"getline_delete", getline_delete},/* action for getline delete-window */ + {"menu_delete", menu_delete}, /* action for menu delete-window */ + {"ec_key", ec_key}, /* action for extended commands */ + {"ec_delete", ec_delete}, /* action for ext-com menu delete */ + {"ps_key", ps_key}, /* action for player selection */ + {"race_key", race_key}, /* action for race selection */ + {"gend_key", gend_key}, /* action for gender selection */ + {"algn_key", algn_key}, /* action for alignment selection */ + {"X11_hangup", X11_hangup}, /* action for delete of top-level */ + {"input", map_input}, /* action for key input */ + {"scroll", nh_keyscroll}, /* action for scrolling by keys */ +}; + +static XtResource resources[] = { + { "slow", "Slow", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,slow), XtRString, "True" }, + { "autofocus", "AutoFocus", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,autofocus), XtRString, "False" }, + { "message_line", "Message_line", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,message_line), XtRString, "False" }, + { "double_tile_size", "Double_tile_size", XtRBoolean, sizeof(Boolean), + XtOffset(AppResources *,double_tile_size), XtRString, "False" }, + { "tile_file", "Tile_file", XtRString, sizeof(String), + XtOffset(AppResources *,tile_file), XtRString, "" }, + { "icon", "Icon", XtRString, sizeof(String), + XtOffset(AppResources *,icon), XtRString, "nh72" }, + { "message_lines", "Message_lines", XtRInt, sizeof(int), + XtOffset(AppResources *,message_lines), XtRString, "12" }, + { "pet_mark_bitmap", "Pet_mark_bitmap", XtRString, sizeof(String), + XtOffset(AppResources *,pet_mark_bitmap), XtRString, "pet_mark.xbm" }, + { "pet_mark_color", "Pet_mark_color", XtRPixel, sizeof(XtRPixel), + XtOffset(AppResources *,pet_mark_color), XtRString, "Red" }, +#ifdef GRAPHIC_TOMBSTONE + { "tombstone", "Tombstone", XtRString, sizeof(String), + XtOffset(AppResources *,tombstone), XtRString, "rip.xpm" }, + { "tombtext_x", "Tombtext_x", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_x), XtRString, "155" }, + { "tombtext_y", "Tombtext_y", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_y), XtRString, "78" }, + { "tombtext_dx", "Tombtext_dx", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_dx), XtRString, "0" }, + { "tombtext_dy", "Tombtext_dy", XtRInt, sizeof(int), + XtOffset(AppResources *,tombtext_dy), XtRString, "13" }, +#endif +}; + +void +X11_init_nhwindows(argcp,argv) +int* argcp; +char** argv; +{ + static const char *banner_text[] = { + COPYRIGHT_BANNER_A, + COPYRIGHT_BANNER_B, + COPYRIGHT_BANNER_C, + "", + "", + 0 + }; + register const char **pp; + int i; + Cardinal num_args; + Arg args[4]; + uid_t savuid; + + /* Init windows to nothing. */ + for (i = 0; i < MAX_WINDOWS; i++) + window_list[i].type = NHW_NONE; + + /* + * setuid hack: make sure that if nethack is setuid, to use real uid + * when opening X11 connections, in case the user is using xauth, since + * the "games" or whatever user probably doesn't have permission to open + * a window on the user's display. This code is harmless if the binary + * is not installed setuid. See include/system.h on compilation failures. + */ + savuid = geteuid(); + (void) seteuid(getuid()); + + XSetIOErrorHandler((XIOErrorHandler) hangup); + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + toplevel = XtAppInitialize( + &app_context, + "NetHack", /* application class */ + (XrmOptionDescList)0, 0, /* options list */ + argcp, (String *)argv, /* command line args */ + (String *)0, /* fallback resources */ + (ArgList)args, num_args); + XtOverrideTranslations(toplevel, + XtParseTranslationTable("WM_PROTOCOLS: X11_hangup()")); + + /* We don't need to realize the top level widget. */ + +#ifdef TEXTCOLOR + /* add new color converter to deal with overused colormaps */ + XtSetTypeConverter(XtRString, XtRPixel, nhCvtStringToPixel, + (XtConvertArgList)nhcolorConvertArgs, + XtNumber(nhcolorConvertArgs), + XtCacheByDisplay, nhFreePixel); +#endif /* TEXTCOLOR */ + + /* Register the actions mentioned in "actions". */ + XtAppAddActions(app_context, actions, XtNumber(actions)); + + /* Get application-wide resources */ + XtGetApplicationResources(toplevel, (XtPointer)&appResources, + resources, XtNumber(resources), + (ArgList)0, ZERO); + + /* Initialize other things. */ + init_standard_windows(); + + /* Give the window manager an icon to use; toplevel must be realized. */ + if (appResources.icon && *appResources.icon) { + struct icon_info *ip; + + for (ip = icon_data; ip->name; ip++) + if (!strcmp(appResources.icon, ip->name)) { + icon_pixmap = XCreateBitmapFromData(XtDisplay(toplevel), + XtWindow(toplevel), + (genericptr_t)ip->bits, ip->width, ip->height); + if (icon_pixmap != None) { + XWMHints hints; + + (void) memset((genericptr_t)&hints, 0, sizeof(XWMHints)); + hints.flags = IconPixmapHint; + hints.icon_pixmap = icon_pixmap; + XSetWMHints(XtDisplay(toplevel), + XtWindow(toplevel), &hints); + } + break; + } + } + + /* end of setuid hack: reset uid back to the "games" uid */ + (void) seteuid(savuid); + + x_inited = TRUE; /* X is now initialized */ + + /* Display the startup banner in the message window. */ + for (pp = banner_text; *pp; pp++) + X11_putstr(WIN_MESSAGE, 0, *pp); +} + +/* + * All done. + */ +/* ARGSUSED */ +void X11_exit_nhwindows(dummy) + const char *dummy; +{ + extern Pixmap tile_pixmap; /* from winmap.c */ + + /* explicitly free the icon and tile pixmaps */ + if (icon_pixmap != None) { + XFreePixmap(XtDisplay(toplevel), icon_pixmap); + icon_pixmap = None; + } + if (tile_pixmap != None) { + XFreePixmap(XtDisplay(toplevel), tile_pixmap); + tile_pixmap = None; + } + if (WIN_INVEN != WIN_ERR) + X11_destroy_nhwindow(WIN_INVEN); + if (WIN_STATUS != WIN_ERR) + X11_destroy_nhwindow(WIN_STATUS); + if (WIN_MAP != WIN_ERR) + X11_destroy_nhwindow(WIN_MAP); + if (WIN_MESSAGE != WIN_ERR) + X11_destroy_nhwindow(WIN_MESSAGE); +} + + +/* delay_output ------------------------------------------------------------ */ + +/* + * Timeout callback for delay_output(). Send a fake message to the map + * window. + */ +/* ARGSUSED */ +static void +d_timeout(client_data, id) + XtPointer client_data; + XtIntervalId *id; +{ + XEvent event; + XClientMessageEvent *mesg; + + /* Set up a fake message to the event handler. */ + mesg = (XClientMessageEvent *) &event; + mesg->type = ClientMessage; + mesg->message_type = XA_STRING; + mesg->format = 8; + XSendEvent(XtDisplay(window_list[WIN_MAP].w), + XtWindow(window_list[WIN_MAP].w), + False, + NoEventMask, + (XEvent*) mesg); +} + +/* + * Delay for 50ms. This is not implemented asynch. Maybe later. + * Start the timeout, then wait in the event loop. The timeout + * function will send an event to the map window which will be waiting + * for a sent event. + */ +void +X11_delay_output() +{ + if (!x_inited) return; + + (void) XtAppAddTimeOut(app_context, 30L, d_timeout, (XtPointer) 0); + + /* The timeout function will enable the event loop exit. */ + (void) x_event(EXIT_ON_SENT_EVENT); +} + +/* X11_hangup -------------------------------------------------------------- */ +/* ARGSUSED */ +static void +X11_hangup(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + hangup(1); /* 1 is commonly SIGHUP, but ignored anyway */ +} + +/* askname ----------------------------------------------------------------- */ +/* ARGSUSED */ +static void +askname_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + nh_XtPopdown(w); + (void) strcpy(plname, "Mumbles"); /* give them a name... ;-) */ + exit_x_event = TRUE; +} + +/* Callback for askname dialog widget. */ +/* ARGSUSED */ +static void +askname_done(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + int len; + char *s; + Widget dialog = (Widget) client_data; + + s = (char *) GetDialogResponse(dialog); + + len = strlen(s); + if (len == 0) { + X11_nhbell(); + return; + } + + /* Truncate name if necessary */ + if (len >= sizeof(plname)-1) + len = sizeof(plname)-1; + + (void) strncpy(plname, s, len); + plname[len] = '\0'; + XtFree(s); + + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + +void +X11_askname() +{ + Widget popup, dialog; + Arg args[1]; + + XtSetArg(args[0], XtNallowShellResize, True); + + popup = XtCreatePopupShell("askname", transientShellWidgetClass, + toplevel, args, ONE); + XtOverrideTranslations(popup, + XtParseTranslationTable("WM_PROTOCOLS: askname_delete()")); + + dialog = CreateDialog(popup, "dialog", + askname_done, (XtCallbackProc) 0); + + SetDialogPrompt(dialog, "What is your name?"); /* set prompt */ + SetDialogResponse(dialog, ""); /* set default answer */ + + XtRealizeWidget(popup); + positionpopup(popup, TRUE); /* center,bottom */ + + nh_XtPopup(popup, (int)XtGrabExclusive, dialog); + + /* The callback will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); +} + + +/* getline ----------------------------------------------------------------- */ +/* This uses Tim Theisen's dialog widget set (from GhostView). */ + +static Widget getline_popup, getline_dialog; + +#define CANCEL_STR "\033" +static char *getline_input; + + +/* Callback for getline dialog widget. */ +/* ARGSUSED */ +static void +done_button(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + int len; + char *s; + Widget dialog = (Widget) client_data; + + s = (char *) GetDialogResponse(dialog); + len = strlen(s); + + /* Truncate input if necessary */ + if (len >= BUFSZ) len = BUFSZ - 1; + + (void) strncpy(getline_input, s, len); + getline_input[len] = '\0'; + XtFree(s); + + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + +/* ARGSUSED */ +static void +getline_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Strcpy(getline_input, CANCEL_STR); + nh_XtPopdown(w); + exit_x_event = TRUE; +} + +/* Callback for getline dialog widget. */ +/* ARGSUSED */ +static void +abort_button(w, client_data, call_data) + Widget w; + XtPointer client_data; + XtPointer call_data; +{ + Widget dialog = (Widget) client_data; + + Strcpy(getline_input, CANCEL_STR); + nh_XtPopdown(XtParent(dialog)); + exit_x_event = TRUE; +} + + +void +X11_getlin(question, input) + const char *question; + char *input; +{ + static boolean need_to_init = True; + + getline_input = input; + + flush_screen(1); + if (need_to_init) { + Arg args[1]; + + need_to_init = False; + + XtSetArg(args[0], XtNallowShellResize, True); + + getline_popup = XtCreatePopupShell("getline",transientShellWidgetClass, + toplevel, args, ONE); + XtOverrideTranslations(getline_popup, + XtParseTranslationTable("WM_PROTOCOLS: getline_delete()")); + + getline_dialog = CreateDialog(getline_popup, "dialog", + done_button, abort_button); + + XtRealizeWidget(getline_popup); + XSetWMProtocols(XtDisplay(getline_popup), XtWindow(getline_popup), + &wm_delete_window, 1); + } + SetDialogPrompt(getline_dialog, (String)question); /* set prompt */ + SetDialogResponse(getline_dialog, ""); /* set default answer */ + positionpopup(getline_popup, TRUE); /* center,bottom */ + + nh_XtPopup(getline_popup, (int)XtGrabExclusive, getline_dialog); + + /* The callback will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); +} + + +/* Display file ------------------------------------------------------------ */ +static const char display_translations[] = + "#override\n\ + q: dismiss_file()\n\ + Escape: dismiss_file()\n\ + : dismiss_file()"; + + +/* WM_DELETE_WINDOW callback for file dismissal. */ +/*ARGSUSED*/ +static void +delete_file(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + nh_XtPopdown(w); + XtDestroyWidget(w); +} + +/* Callback for file dismissal. */ +/*ARGSUSED*/ +static void +dismiss_file(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Widget popup = XtParent(w); + nh_XtPopdown(popup); + XtDestroyWidget(popup); +} + +void +X11_display_file(str, complain) + const char *str; + boolean complain; +{ + dlb *fp; + Arg args[12]; + Cardinal num_args; + Widget popup, dispfile; + Position top_margin, bottom_margin, left_margin, right_margin; + XFontStruct *fs; + int new_width, new_height; +#define LLEN 128 + char line[LLEN]; + int num_lines; + char *textlines; + int charcount; + + /* Use the port-independent file opener to see if the file exists. */ + fp = dlb_fopen(str, RDTMODE); + + if (!fp) { + if(complain) pline("Cannot open %s. Sorry.", str); + + return; /* it doesn't exist, ignore */ + } + + /* + * Count the number of lines and characters in the file. + */ + num_lines = 0; + charcount = 1; + while (dlb_fgets(line, LLEN, fp)) { + num_lines++; + charcount += strlen(line); + } + + (void) dlb_fclose(fp); + + /* Ignore empty files */ + if (num_lines == 0) return; + + /* If over the max window size, truncate the window size to the max */ + if (num_lines >= DISPLAY_FILE_SIZE) + num_lines = DISPLAY_FILE_SIZE; + + /* + * Re-open the file and read the data into a buffer. Cannot use + * the XawAsciiFile type of widget, because that is not DLB-aware. + */ + textlines = (char *) alloc((unsigned int) charcount); + textlines[0] = '\0'; + + fp = dlb_fopen(str, RDTMODE); + + while (dlb_fgets(line, LLEN, fp)) { + (void) strcat(textlines, line); + } + + (void) dlb_fclose(fp); + + num_args = 0; + XtSetArg(args[num_args], XtNtitle, str); num_args++; + + popup = XtCreatePopupShell("display_file", topLevelShellWidgetClass, + toplevel, args, num_args); + XtOverrideTranslations(popup, + XtParseTranslationTable("WM_PROTOCOLS: delete_file()")); + + num_args = 0; + XtSetArg(args[num_args], XtNscrollHorizontal, + XawtextScrollWhenNeeded); num_args++; + XtSetArg(args[num_args], XtNscrollVertical, + XawtextScrollWhenNeeded); num_args++; + XtSetArg(args[num_args], XtNtype, XawAsciiString); num_args++; + XtSetArg(args[num_args], XtNstring, textlines); num_args++; + XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(display_translations)); num_args++; + + dispfile = XtCreateManagedWidget( + "text", /* name */ + asciiTextWidgetClass, + popup, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + /* Get font and border information. */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &fs); num_args++; + XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++; + XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++; + XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++; + XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++; + XtGetValues(dispfile, args, num_args); + + /* + * The data files are currently set up assuming an 80 char wide window + * and a fixed width font. Soo.. + */ + new_height = num_lines * nhFontHeight(dispfile) + + top_margin + bottom_margin; + new_width = 80 * fs->max_bounds.width + left_margin + right_margin; + + /* Set the new width and height. */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, new_width); num_args++; + XtSetArg(args[num_args], XtNheight, new_height); num_args++; + XtSetValues(dispfile, args, num_args); + + nh_XtPopup(popup, (int)XtGrabNone, (Widget)0); + free(textlines); +} + + +/* yn_function ------------------------------------------------------------- */ +/* (not threaded) */ + +static const char *yn_quitchars = " \n\r"; +static const char *yn_choices; /* string of acceptable input */ +static char yn_def; +static char yn_return; /* return value */ +static char yn_esc_map; /* ESC maps to this char. */ +static Widget yn_popup; /* popup for the yn fuction (created once) */ +static Widget yn_label; /* label for yn function (created once) */ +static boolean yn_getting_num; /* TRUE if accepting digits */ +static int yn_ndigits; /* digit count */ +static long yn_val; /* accumulated value */ + +static const char yn_translations[] = + "#override\n\ + : yn_key()"; + +/* + * Convert the given key event into a character. If the key maps to + * more than one character only the first is returned. If there is + * no conversion (i.e. just the CTRL key hit) a NUL is returned. + */ +char +key_event_to_char(key) + XKeyEvent *key; +{ + char keystring[MAX_KEY_STRING]; + int nbytes; + boolean meta = !!(key->state & Mod1Mask); + + nbytes = XLookupString(key, keystring, MAX_KEY_STRING, + (KeySym *)0, (XComposeStatus *)0); + + /* Modifier keys return a zero lengh string when pressed. */ + if (nbytes == 0) return '\0'; + + return (char) (((int) keystring[0]) + (meta ? 0x80 : 0)); +} + +/* + * Called when we get a WM_DELETE_WINDOW event on a yn window. + */ +/* ARGSUSED */ +static void +yn_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + yn_getting_num = FALSE; + /* Only use yn_esc_map if we have choices. Otherwise, return ESC. */ + yn_return = yn_choices ? yn_esc_map : '\033'; + exit_x_event = TRUE; /* exit our event handler */ +} + +/* + * Called when we get a key press event on a yn window. + */ +/* ARGSUSED */ +static void +yn_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch; + + if(appResources.slow && !input_func) + map_input(w, event, params, num_params); + + ch = key_event_to_char((XKeyEvent *) event); + + if (ch == '\0') { /* don't accept nul char or modifier event */ + /* no bell */ + return; + } + + if (!yn_choices) { /* accept any input */ + yn_return = ch; + } else { + ch = lowc(ch); /* move to lower case */ + + if (ch == '\033') { + yn_getting_num = FALSE; + yn_return = yn_esc_map; + } else if (index(yn_quitchars, ch)) { + yn_return = yn_def; + } else if (index(yn_choices, ch)) { + if (ch == '#') { + if (yn_getting_num) { /* don't select again */ + X11_nhbell(); + return; + } + yn_getting_num = TRUE; + yn_ndigits = 0; + yn_val = 0; + return; /* wait for more input */ + } + yn_return = ch; + if (ch != 'y') yn_getting_num = FALSE; + } else { + if (yn_getting_num) { + if (digit(ch)) { + yn_ndigits++; + yn_val = (yn_val * 10) + (long) (ch - '0'); + return; /* wait for more input */ + } + if (yn_ndigits && (ch == '\b' || ch == 127/*DEL*/)) { + yn_ndigits--; + yn_val = yn_val/ 10; + return; /* wait for more input */ + } + } + X11_nhbell(); /* no match */ + return; + } + + if (yn_getting_num) { + yn_return = '#'; + if (yn_val < 0) yn_val = 0; + yn_number = yn_val; /* assign global */ + } + } + exit_x_event = TRUE; /* exit our event handler */ +} + + +char +X11_yn_function(ques, choices, def) + const char *ques; + const char *choices; + char def; +{ + static Boolean need_to_init = True; + char buf[QBUFSZ]; + Arg args[4]; + Cardinal num_args; + + yn_choices = choices; /* set up globals for callback to use */ + yn_def = def; + + /* + * This is sort of a kludge. There are quite a few places in the main + * nethack code where a pline containing information is followed by a + * call to yn_function(). There is no flush of the message window + * (it is implicit in the tty window port), so the line never shows + * up for us! Solution: do our own flush. + */ + if (WIN_MESSAGE != WIN_ERR) + display_message_window(&window_list[WIN_MESSAGE]); + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + + Strcpy(choicebuf, choices); /* anything beyond is hidden */ + if ((cb = index(choicebuf, '\033')) != 0) *cb = '\0'; + /* ques [choices] (def) */ + if ((int)(1 + strlen(ques) + 2 + strlen(choicebuf) + 4) >= QBUFSZ) + panic("yn_function: question too long"); + Sprintf(buf, "%s [%s] ", ques, choicebuf); + if (def) Sprintf(eos(buf), "(%c) ", def); + + /* escape maps to 'q' or 'n' or default, in that order */ + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : + def)); + } else { + if ((int)(1 + strlen(ques)) >= QBUFSZ) + panic("yn_function: question too long"); + Strcpy(buf, ques); + } + + if (!appResources.slow && need_to_init) { + need_to_init = False; + + XtSetArg(args[0], XtNallowShellResize, True); + yn_popup = XtCreatePopupShell("query", transientShellWidgetClass, + toplevel, args, ONE); + XtOverrideTranslations(yn_popup, + XtParseTranslationTable("WM_PROTOCOLS: yn_delete()")); + + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(yn_translations)); num_args++; + yn_label = XtCreateManagedWidget("yn_label", + labelWidgetClass, + yn_popup, + args, num_args); + + XtRealizeWidget(yn_popup); + XSetWMProtocols(XtDisplay(yn_popup), XtWindow(yn_popup), + &wm_delete_window, 1); + } + + if(appResources.slow) + input_func = yn_key; + + num_args = 0; + XtSetArg(args[num_args], XtNlabel, buf); num_args++; + XtSetValues(yn_label, args, num_args); + + if(!appResources.slow) { + /* + * Due to some kind of weird bug in the X11R4 and X11R5 shell, we + * need to set the label twice to get the size to change. + */ + num_args = 0; + XtSetArg(args[num_args], XtNlabel, buf); num_args++; + XtSetValues(yn_label, args, num_args); + + positionpopup(yn_popup, TRUE); + nh_XtPopup(yn_popup, (int)XtGrabExclusive, yn_label); + } + + yn_getting_num = FALSE; + (void) x_event(EXIT_ON_EXIT); + + if(appResources.slow) { + input_func = 0; + num_args = 0; + XtSetArg(args[num_args], XtNlabel, " "); num_args++; + XtSetValues(yn_label, args, num_args); + } else { + nh_XtPopdown(yn_popup); /* this removes the event grab */ + } + + return yn_return; +} + +/* End global functions ==================================================== */ + +/* + * Before we wait for input via nhgetch() and nh_poskey(), we need to + * do some pre-processing. + */ +static int +input_event(exit_condition) + int exit_condition; +{ + if (WIN_STATUS != WIN_ERR) /* hilighting on the fancy status window */ + check_turn_events(); + if (WIN_MAP != WIN_ERR) /* make sure cursor is not clipped */ + check_cursor_visibility(&window_list[WIN_MAP]); + if (WIN_MESSAGE != WIN_ERR) /* reset pause line */ + set_last_pause(&window_list[WIN_MESSAGE]); + + return x_event(exit_condition); +} + + +/*ARGSUSED*/ +void +msgkey(w, data, event) + Widget w; + XtPointer data; + XEvent *event; +{ + Cardinal num = 0; + map_input(window_list[WIN_MAP].w, event, (String*) 0, &num); +} + +/*ARGSUSED*/ +static void +win_visible(w, data, event, flag) /* only called for autofocus */ + Widget w; + XtPointer data; /* client_data not used */ + XEvent *event; + Boolean *flag; /* continue_to_dispatch flag not used */ +{ + XVisibilityEvent *vis_event = (XVisibilityEvent *)event; + + if (vis_event->state != VisibilityFullyObscured) { + /* one-time operation; cancel ourself */ + XtRemoveEventHandler(toplevel, VisibilityChangeMask, False, + win_visible, (XtPointer) 0); + /* grab initial input focus */ + XSetInputFocus(XtDisplay(w), XtWindow(w), RevertToNone, CurrentTime); + } +} + +/* + * Set up the playing console. This has three major parts: the + * message window, the map, and the status window. + */ +static void +init_standard_windows() +{ + Widget form, message_viewport, map_viewport, status; + Arg args[8]; + Cardinal num_args; + Dimension message_vp_width, map_vp_width, status_width, max_width; + int map_vp_hd, status_hd; + struct xwindow *wp; + + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + form = XtCreateManagedWidget("nethack", + panedWidgetClass, + toplevel, args, num_args); + + XtAddEventHandler(form, KeyPressMask, False, + (XtEventHandler) msgkey, (XtPointer) 0); + + if (appResources.autofocus) + XtAddEventHandler(toplevel, VisibilityChangeMask, False, + win_visible, (XtPointer) 0); + + /* + * Create message window. + */ + WIN_MESSAGE = message_win = find_free_window(); + wp = &window_list[message_win]; + wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; + wp->popup = (Widget) 0; + create_message_window(wp, FALSE, form); + message_viewport = XtParent(wp->w); + + + /* Tell the form that contains it that resizes are OK. */ + num_args = 0; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetValues(message_viewport, args, num_args); + + if(appResources.slow) { + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(yn_translations)); num_args++; + yn_label = XtCreateManagedWidget("yn_label", + labelWidgetClass, + form, + args, num_args); + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, message_viewport); num_args++; + XtSetArg(args[num_args], XtNjustify, XtJustifyLeft); num_args++; + XtSetArg(args[num_args], XtNresizable, True); num_args++; + XtSetArg(args[num_args], XtNlabel, " "); num_args++; + XtSetValues(yn_label, args, num_args); + } + + /* + * Create the map window & viewport and chain the viewport beneath the + * message_viewport. + */ + map_win = find_free_window(); + wp = &window_list[map_win]; + wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; + wp->popup = (Widget) 0; + create_map_window(wp, FALSE, form); + map_viewport = XtParent(wp->w); + + /* Chain beneath message_viewport or yn window. */ + num_args = 0; + if(appResources.slow) { + XtSetArg(args[num_args], XtNfromVert, yn_label); num_args++; + } else { + XtSetArg(args[num_args], XtNfromVert, message_viewport);num_args++; + } + XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; + XtSetValues(map_viewport, args, num_args); + + /* Create the status window, with the form as it's parent. */ + status_win = find_free_window(); + wp = &window_list[status_win]; + wp->cursx = wp->cursy = wp->pixel_width = wp->pixel_height = 0; + wp->popup = (Widget) 0; + create_status_window(wp, FALSE, form); + status = wp->w; + + /* + * Chain the status window beneath the viewport. Mark the left and right + * edges so that they stay a fixed distance from the left edge of the + * parent, as well as the top and bottom edges so that they stay a fixed + * distance from the bottom of the parent. We do this so that the status + * will never expand or contract. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, map_viewport); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainBottom); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; + XtSetValues(status, args, num_args); + + + /* + * Realize the popup so that the status widget knows it's size. + * + * If we unset MappedWhenManaged then the DECwindow driver doesn't + * attach the nethack toplevel to the highest virtual root window. + * So don't do it. + */ + /* XtSetMappedWhenManaged(toplevel, False); */ + XtRealizeWidget(toplevel); + wm_delete_window = XInternAtom(XtDisplay(toplevel), + "WM_DELETE_WINDOW", False); + XSetWMProtocols(XtDisplay(toplevel), XtWindow(toplevel), + &wm_delete_window, 1); + + /* + * Resize to at most full-screen. + */ + { +#define TITLEBAR_SPACE 18 /* Leave SOME screen for window decorations */ + + int screen_width = WidthOfScreen(XtScreen(wp->w)); + int screen_height = HeightOfScreen(XtScreen(wp->w)) - TITLEBAR_SPACE; + Dimension form_width, form_height; + + XtSetArg(args[0], XtNwidth, &form_width); + XtSetArg(args[1], XtNheight, &form_height); + XtGetValues(toplevel, args, TWO); + + if (form_width > screen_width || form_height > screen_height) { + XtSetArg(args[0], XtNwidth, min(form_width,screen_width)); + XtSetArg(args[1], XtNheight, min(form_height,screen_height)); + XtSetValues(toplevel, args, TWO); + XMoveWindow(XtDisplay(toplevel),XtWindow(toplevel), + 0, TITLEBAR_SPACE); + } +#undef TITLEBAR_SPACE + } + + post_process_tiles(); /* after toplevel is realized */ + + /* + * Now get the default widths of the windows. + */ + XtSetArg(args[0], XtNwidth, &message_vp_width); + XtGetValues(message_viewport, args, ONE); + XtSetArg(args[0], XtNwidth, &map_vp_width); + XtSetArg(args[1], XtNhorizDistance, &map_vp_hd); + XtGetValues(map_viewport, args, TWO); + XtSetArg(args[0], XtNwidth, &status_width); + XtSetArg(args[1], XtNhorizDistance, &status_hd); + XtGetValues(status, args, TWO); + + /* + * Adjust positions and sizes. The message viewport widens out to the + * widest width. Both the map and status are centered by adjusting + * their horizDistance. + */ + if (map_vp_width < status_width || map_vp_width < message_vp_width) { + if (status_width > message_vp_width) { + XtSetArg(args[0], XtNwidth, status_width); + XtSetValues(message_viewport, args, ONE); + max_width = status_width; + } else { +/***** The status display looks better when left justified. + XtSetArg(args[0], XtNhorizDistance, + status_hd+((message_vp_width-status_width)/2)); + XtSetValues(status, args, ONE); +*****/ + max_width = message_vp_width; + } + XtSetArg(args[0], XtNhorizDistance, map_vp_hd+((int)(max_width-map_vp_width)/2)); + XtSetValues(map_viewport, args, ONE); + + } else { /* map is widest */ + XtSetArg(args[0], XtNwidth, map_vp_width); + XtSetValues(message_viewport, args, ONE); + +/***** The status display looks better when left justified. + XtSetArg(args[0], XtNhorizDistance, + status_hd+((map_vp_width-status_width)/2)); + + XtSetValues(status, args, ONE); +*****/ + } + /* + * Clear all data values on the fancy status widget so that the values + * used for spacing don't appear. This needs to be called some time + * after the fancy status widget is realized (above, with the game popup), + * but before it is popped up. + */ + null_out_status(); + /* + * Set the map size to its standard size. As with the message window + * above, the map window needs to be set to its constrained size until + * its parent (the viewport widget) was realized. + * + * Move the message window's slider to the bottom. + */ + set_map_size(&window_list[map_win], COLNO, ROWNO); + set_message_slider(&window_list[message_win]); + + /* attempt to catch fatal X11 errors before the program quits */ + (void) XtAppSetErrorHandler(app_context, (XtErrorHandler) hangup); + + /* We can now print to the message window. */ + iflags.window_inited = 1; +} + + +void +nh_XtPopup(w, g, childwid) + Widget w; /* widget */ + int g; /* type of grab */ + Widget childwid; /* child to recieve focus (can be None) */ +{ + XtPopup(w, (XtGrabKind)g); + XSetWMProtocols(XtDisplay(w), XtWindow(w), &wm_delete_window, 1); + if (appResources.autofocus) XtSetKeyboardFocus(toplevel, childwid); +} + +void +nh_XtPopdown(w) + Widget w; +{ + XtPopdown(w); + if (appResources.autofocus) XtSetKeyboardFocus(toplevel, None); +} + +void +win_X11_init() +{ +#ifdef OPENWINBUG + /* With the OpenWindows 3.0 libraries and the SunOS 4.1.2 ld, these + * two routines will not be found when linking. An apparently correct + * executable is produced, along with nasty messages and a failure code + * returned to make. The routines are in the static libXmu.a and + * libXmu.sa.4.0, but not in libXmu.so.4.0. Rather than fiddle with + * static linking, we do this. + */ + if (rn2(2) > 2) { + /* i.e., FALSE that an optimizer probably can't find */ + get_wmShellWidgetClass(); + get_applicationShellWidgetClass(); + } +#endif + return; +} + +/* Callback + * Scroll a viewport, using standard NH 1,2,3,4,6,7,8,9 directions. + */ +/*ARGSUSED*/ +void +nh_keyscroll(viewport, event, params, num_params) + Widget viewport; + XEvent *event; + String *params; + Cardinal *num_params; +{ + Arg arg[2]; + Widget horiz_sb, vert_sb; + float top, shown; + Boolean do_call; + int direction; + Cardinal in_nparams = (num_params ? *num_params : 0); + + if (in_nparams != 1) return; /* bad translation */ + + direction=atoi(params[0]); + + horiz_sb = XtNameToWidget(viewport, "*horizontal"); + vert_sb = XtNameToWidget(viewport, "*vertical"); + + if (!horiz_sb && !vert_sb) { + /* Perhaps the widget enclosing this has scrollbars (could use while) */ + Widget parent=XtParent(viewport); + if (parent) { + horiz_sb = XtNameToWidget(parent, "horizontal"); + vert_sb = XtNameToWidget(parent, "vertical"); + } + } + +#define H_DELTA 0.25 /* distance of horiz shift */ + /* vert shift is half of curr distance */ +/* The V_DELTA is 1/2 the value of shown. */ + + if (horiz_sb) { + XtSetArg(arg[0], XtNshown, &shown); + XtSetArg(arg[1], XtNtopOfThumb, &top); + XtGetValues(horiz_sb, arg, TWO); + + do_call = True; + + switch (direction) { + case 1: case 4: case 7: + top -= H_DELTA; + if (top < 0.0) top = 0.0; + break; case 3: case 6: case 9: + top += H_DELTA; + if (top + shown > 1.0) top = 1.0 - shown; + break; default: + do_call = False; + } + + if (do_call) { + XtCallCallbacks(horiz_sb, XtNjumpProc, &top); + } + } + + if (vert_sb) { + XtSetArg(arg[0], XtNshown, &shown); + XtSetArg(arg[1], XtNtopOfThumb, &top); + XtGetValues(vert_sb, arg, TWO); + + do_call = True; + + switch (direction) { + case 7: case 8: case 9: + top -= shown / 2.0; + if (top < 0.0) top = 0; + break; case 1: case 2: case 3: + top += shown / 2.0; + if (top + shown > 1.0) top = 1.0 - shown; + break; default: + do_call = False; + } + + if (do_call) { + XtCallCallbacks(vert_sb, XtNjumpProc, &top); + } + } +} + +/*winX.c*/ diff --git a/win/X11/winmap.c b/win/X11/winmap.c new file mode 100644 index 0000000..ce5455b --- /dev/null +++ b/win/X11/winmap.c @@ -0,0 +1,1654 @@ +/* SCCS Id: @(#)winmap.c 3.4 1996/04/05 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file contains: + * + global functions print_glyph() and cliparound() + * + the map window routines + * + the char and pointer input routines + * + * Notes: + * + We don't really have a good way to get the compiled ROWNO and + * COLNO as defaults. They are hardwired to the current "correct" + * values in the Window widget. I am _not_ in favor of including + * some nethack include file for Window.c. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "xwindow.h" /* map widget declarations */ + +#include "hack.h" +#include "dlb.h" +#include "winX.h" + +#ifdef USE_XPM +#include +#endif + + +/* from tile.c */ +extern short glyph2tile[]; +extern int total_tiles_used; + +/* Define these if you really want a lot of junk on your screen. */ +/* #define VERBOSE */ /* print various info & events as they happen */ +/* #define VERBOSE_UPDATE */ /* print screen update bounds */ +/* #define VERBOSE_INPUT */ /* print input events */ + + +#define USE_WHITE /* almost always use white as a tile cursor border */ + + +static boolean FDECL(init_tiles, (struct xwindow *)); +static void FDECL(set_button_values, (Widget,int,int,unsigned)); +static void FDECL(map_check_size_change, (struct xwindow *)); +static void FDECL(map_update, (struct xwindow *,int,int,int,int,BOOLEAN_P)); +static void FDECL(init_text, (struct xwindow *)); +static void FDECL(map_exposed, (Widget,XtPointer,XtPointer)); +static void FDECL(set_gc, (Widget,Font,char *,Pixel,GC *,GC *)); +static void FDECL(get_text_gc, (struct xwindow *,Font)); +static void FDECL(get_char_info, (struct xwindow *)); +static void FDECL(display_cursor, (struct xwindow *)); + +/* Global functions ======================================================== */ + +void +X11_print_glyph(window, x, y, glyph) + winid window; + xchar x, y; + int glyph; +{ + struct map_info_t *map_info; + boolean update_bbox; + + check_winid(window); + if (window_list[window].type != NHW_MAP) { + impossible("print_glyph: can (currently) only print to map windows"); + return; + } + map_info = window_list[window].map_information; + + if (map_info->is_tile) { + unsigned short *t_ptr; + + t_ptr = &map_info->mtype.tile_map->glyphs[y][x]; + + if (*t_ptr != glyph) { + *t_ptr = glyph; + update_bbox = TRUE; + } else + update_bbox = FALSE; + + } else { + uchar ch; + register unsigned char *ch_ptr; + int color,och; + unsigned special; +#ifdef TEXTCOLOR + register unsigned char *co_ptr; +#endif + /* map glyph to character and color */ + mapglyph(glyph, &och, &color, &special, x, y); + ch = (uchar)och; + + /* Only update if we need to. */ + ch_ptr = &map_info->mtype.text_map->text[y][x]; + +#ifdef TEXTCOLOR + co_ptr = &map_info->mtype.text_map->colors[y][x]; + if (*ch_ptr != ch || *co_ptr != color) +#else + if (*ch_ptr != ch) +#endif + { + *ch_ptr = ch; +#ifdef TEXTCOLOR + *co_ptr = color; +#endif + update_bbox = TRUE; + } else + update_bbox = FALSE; + } + + if (update_bbox) { /* update row bbox */ + if ((uchar) x < map_info->t_start[y]) map_info->t_start[y] = x; + if ((uchar) x > map_info->t_stop[y]) map_info->t_stop[y] = x; + } +} + +#ifdef CLIPPING +/* + * The is the tty clip call. Since X can resize at any time, we can't depend + * on this being defined. + */ +/*ARGSUSED*/ +void X11_cliparound(x, y) int x, y; { } +#endif /* CLIPPING */ + +/* End global functions ==================================================== */ + +#include "tile2x11.h" + +/* + * We're expecting to never read more than one tile file per session. + * If this is false, then we can make an array of this information, + * or just keep it on a per-window basis. + */ +Pixmap tile_pixmap = None; +static int tile_width; +static int tile_height; +static int tile_count; +static XImage *tile_image = 0; + +/* + * This structure is used for small bitmaps that are used for annotating + * tiles. For example, a "heart" annotates pets. + */ +struct tile_annotation { + Pixmap bitmap; + Pixel foreground; + unsigned int width, height; + int hotx, hoty; /* not currently used */ +}; + +static struct tile_annotation pet_annotation; + +static void +init_annotation(annotation, filename, colorpixel) +struct tile_annotation *annotation; +char *filename; +Pixel colorpixel; +{ + Display *dpy = XtDisplay(toplevel); + + if (0!=XReadBitmapFile(dpy, XtWindow(toplevel), filename, + &annotation->width, &annotation->height, &annotation->bitmap, + &annotation->hotx, &annotation->hoty)) { + char buf[BUFSZ]; + Sprintf(buf, "Failed to load %s", filename); + X11_raw_print(buf); + } + + annotation->foreground = colorpixel; +} + +/* + * Put the tile image on the server. + * + * We can't send the image to the server until the top level + * is realized. When the tile file is first processed, the top + * level is not realized. This routine is called after we + * realize the top level, but before we start resizing the + * map viewport. + */ +void +post_process_tiles() +{ + Display *dpy = XtDisplay(toplevel); + unsigned int width, height; + + if (tile_image == 0) return; /* no tiles */ + + height = tile_image->height; + width = tile_image->width; + + tile_pixmap = XCreatePixmap(dpy, XtWindow(toplevel), + width, + height, + DefaultDepth(dpy, DefaultScreen(dpy))); + + XPutImage(dpy, tile_pixmap, + DefaultGC(dpy, DefaultScreen(dpy)), + tile_image, + 0,0, 0,0, /* src, dest top left */ + width, + height); + + XDestroyImage(tile_image); /* data bytes free'd also */ + tile_image = 0; + + init_annotation(&pet_annotation, + appResources.pet_mark_bitmap, appResources.pet_mark_color); +} + + +/* + * Open and read the tile file. Return TRUE if there were no problems. + * Return FALSE otherwise. + */ +static boolean +init_tiles(wp) + struct xwindow *wp; +{ +#ifdef USE_XPM + XpmAttributes attributes; + int errorcode; +#else + FILE *fp = (FILE *)0; + x11_header header; + unsigned char *cp, *colormap = (unsigned char *)0; + unsigned char *tb, *tile_bytes = (unsigned char *)0; + int size; + XColor *colors = (XColor *)0; + int i, x, y; + int bitmap_pad; + int ddepth; +#endif + char buf[BUFSZ]; + Display *dpy = XtDisplay(toplevel); + Screen *screen = DefaultScreenOfDisplay(dpy); + struct map_info_t *map_info = (struct map_info_t *)0; + struct tile_map_info_t *tile_info = (struct tile_map_info_t *)0; + unsigned int image_height = 0, image_width = 0; + boolean result = TRUE; + XGCValues values; + XtGCMask mask; + + /* already have tile information */ + if (tile_pixmap != None) goto tiledone; + + map_info = wp->map_information; + tile_info = map_info->mtype.tile_map = + (struct tile_map_info_t *) alloc(sizeof(struct tile_map_info_t)); + (void) memset((genericptr_t) tile_info, 0, + sizeof(struct tile_map_info_t)); + +#ifdef USE_XPM + attributes.valuemask = XpmCloseness; + attributes.closeness = 25000; + + errorcode = XpmReadFileToImage(dpy, appResources.tile_file, + &tile_image, 0, &attributes); + + if (errorcode == XpmColorFailed) { + Sprintf(buf, "Insufficient colors available to load %s.", + appResources.tile_file); + X11_raw_print(buf); + X11_raw_print("Try closing other colorful applications and restart."); + X11_raw_print("Attempting to load with inferior colors."); + attributes.closeness = 50000; + errorcode = XpmReadFileToImage(dpy, appResources.tile_file, + &tile_image, 0, &attributes); + } + + if (errorcode != XpmSuccess) { + if (errorcode == XpmColorFailed) { + Sprintf(buf, "Insufficient colors available to load %s.", + appResources.tile_file); + X11_raw_print(buf); + } else { + Sprintf(buf, "Failed to load %s: %s", appResources.tile_file, + XpmGetErrorString(errorcode)); + X11_raw_print(buf); + } + result = FALSE; + X11_raw_print("Switching to text-based mode."); + goto tiledone; + } + + /* assume a fixed number of tiles per row */ + if (tile_image->width % TILES_PER_ROW != 0 || + tile_image->width <= TILES_PER_ROW) { + Sprintf(buf, + "%s is not a multiple of %d (number of tiles/row) pixels wide", + appResources.tile_file, TILES_PER_ROW); + X11_raw_print(buf); + XDestroyImage(tile_image); + tile_image = 0; + result = FALSE; + goto tiledone; + } + + /* infer tile dimensions from image size and TILES_PER_ROW */ + image_width = tile_image->width; + image_height = tile_image->height; + + tile_count = total_tiles_used; + if ((tile_count % TILES_PER_ROW) != 0) { + tile_count += TILES_PER_ROW - (tile_count % TILES_PER_ROW); + } + tile_width = image_width / TILES_PER_ROW; + tile_height = image_height / (tile_count / TILES_PER_ROW); +#else + /* any less than 16 colours makes tiles useless */ + ddepth = DefaultDepthOfScreen(screen); + if (ddepth < 4) { + X11_raw_print("need a screen depth of at least 4"); + result = FALSE; + goto tiledone; + } + + fp = fopen_datafile(appResources.tile_file, RDBMODE, FALSE); + if (!fp) { + X11_raw_print("can't open tile file"); + result = FALSE; + goto tiledone; + } + + if (fread((char *) &header, sizeof(header), 1, fp) != 1) { + X11_raw_print("read of header failed"); + result = FALSE; + goto tiledone; + } + + if (header.version != 2) { + Sprintf(buf, "Wrong tile file version, expected 2, got %lu", + header.version); + X11_raw_print(buf); + result = FALSE; + goto tiledone; + } + +# ifdef VERBOSE + fprintf(stderr, "X11 tile file:\n version %ld\n ncolors %ld\n tile width %ld\n tile height %ld\n per row %ld\n ntiles %ld\n", + header.version, + header.ncolors, + header.tile_width, + header.tile_height, + header.per_row, + header.ntiles); +# endif + + size = 3*header.ncolors; + colormap = (unsigned char *) alloc((unsigned)size); + if (fread((char *) colormap, 1, size, fp) != size) { + X11_raw_print("read of colormap failed"); + result = FALSE; + goto tiledone; + } + +/* defined in decl.h - these are _not_ good defines to have */ +#undef red +#undef green +#undef blue + + colors = (XColor *) alloc(sizeof(XColor) * (unsigned)header.ncolors); + for (i = 0; i < header.ncolors; i++) { + cp = colormap + (3 * i); + colors[i].red = cp[0] * 256; + colors[i].green = cp[1] * 256; + colors[i].blue = cp[2] * 256; + colors[i].flags = 0; + colors[i].pixel = 0; + + if (!XAllocColor(dpy, DefaultColormapOfScreen(screen), &colors[i]) && + !nhApproxColor(screen, DefaultColormapOfScreen(screen), + (char *)0, &colors[i])) { + Sprintf(buf, "%dth out of %ld color allocation failed", + i, header.ncolors); + X11_raw_print(buf); + result = FALSE; + goto tiledone; + } + } + + size = header.tile_height * header.tile_width; + /* + * This alloc() and the one below require 32-bit ints, since tile_bytes + * is currently ~200k and alloc() takes an int + */ + tile_count = header.ntiles; + if ((tile_count % header.per_row) != 0) { + tile_count += header.per_row - (tile_count % header.per_row); + } + tile_bytes = (unsigned char *) alloc((unsigned)tile_count*size); + if (fread((char *) tile_bytes, size, tile_count, fp) != tile_count) { + X11_raw_print("read of tile bytes failed"); + result = FALSE; + goto tiledone; + } + + if (header.ntiles < total_tiles_used) { + Sprintf(buf, "tile file incomplete, expecting %d tiles, found %lu", + total_tiles_used, header.ntiles); + X11_raw_print(buf); + result = FALSE; + goto tiledone; + } + + + if (appResources.double_tile_size) { + tile_width = 2*header.tile_width; + tile_height = 2*header.tile_height; + } else { + tile_width = header.tile_width; + tile_height = header.tile_height; + } + + image_height = tile_height * tile_count / header.per_row; + image_width = tile_width * header.per_row; + + /* calculate bitmap_pad */ + if (ddepth > 16) + bitmap_pad = 32; + else if (ddepth > 8) + bitmap_pad = 16; + else + bitmap_pad = 8; + + tile_image = XCreateImage(dpy, DefaultVisualOfScreen(screen), + ddepth, /* depth */ + ZPixmap, /* format */ + 0, /* offset */ + 0, /* data */ + image_width, /* width */ + image_height, /* height */ + bitmap_pad, /* bit pad */ + 0); /* bytes_per_line */ + + if (!tile_image) + impossible("init_tiles: insufficient memory to create image"); + + /* now we know the physical memory requirements, we can allocate space */ + tile_image->data = + (char *) alloc((unsigned)tile_image->bytes_per_line * image_height); + + if (appResources.double_tile_size) { + unsigned long *expanded_row = + (unsigned long *)alloc(sizeof(unsigned long)*(unsigned)image_width); + + tb = tile_bytes; + for (y = 0; y < image_height; y++) { + for (x = 0; x < image_width/2; x++) + expanded_row[2*x] = + expanded_row[(2*x)+1] = colors[*tb++].pixel; + + for (x = 0; x < image_width; x++) + XPutPixel(tile_image, x, y, expanded_row[x]); + + y++; /* duplicate row */ + for (x = 0; x < image_width; x++) + XPutPixel(tile_image, x, y, expanded_row[x]); + } + free((genericptr_t)expanded_row); + + } else { + + for (tb = tile_bytes, y = 0; y < image_height; y++) + for (x = 0; x < image_width; x++, tb++) + XPutPixel(tile_image, x, y, colors[*tb].pixel); + } +#endif /* USE_XPM */ + + /* fake an inverted tile by drawing a border around the edges */ +#ifdef USE_WHITE + /* use white or black as the border */ + mask = GCFunction | GCForeground | GCGraphicsExposures; + values.graphics_exposures = False; + values.foreground = WhitePixelOfScreen(screen); + values.function = GXcopy; + tile_info->white_gc = XtGetGC(wp->w, mask, &values); + values.graphics_exposures = False; + values.foreground = BlackPixelOfScreen(screen); + values.function = GXcopy; + tile_info->black_gc = XtGetGC(wp->w, mask, &values); +#else + /* + * Use xor so we don't have to check for special colors. Xor white + * against the upper left pixel of the corridor so that we have a + * white rectangle when in a corridor. + */ + mask = GCFunction | GCForeground | GCGraphicsExposures; + values.graphics_exposures = False; + values.foreground = WhitePixelOfScreen(screen) ^ + XGetPixel(tile_image, 0, tile_height*glyph2tile[cmap_to_glyph(S_corr)]); + values.function = GXxor; + tile_info->white_gc = XtGetGC(wp->w, mask, &values); + + mask = GCFunction | GCGraphicsExposures; + values.function = GXCopy; + values.graphics_exposures = False; + tile_info->black_gc = XtGetGC(wp->w, mask, &values); +#endif /* USE_WHITE */ + +tiledone: +#ifndef USE_XPM + if (fp) (void) fclose(fp); + if (colormap) free((genericptr_t)colormap); + if (tile_bytes) free((genericptr_t)tile_bytes); + if (colors) free((genericptr_t)colors); +#endif + + if (result) { /* succeeded */ + map_info->square_height = tile_height; + map_info->square_width = tile_width; + map_info->square_ascent = 0; + map_info->square_lbearing = 0; + tile_info->image_width = image_width; + tile_info->image_height = image_height; + } else { + if (tile_info) free((genericptr_t)tile_info); + tile_info = 0; + } + + return result; +} + + +/* + * Make sure the map's cursor is always visible. + */ +void +check_cursor_visibility(wp) + struct xwindow *wp; +{ + Arg arg[2]; + Widget viewport, horiz_sb, vert_sb; + float top, shown, cursor_middle; + Boolean do_call, adjusted = False; +#ifdef VERBOSE + char *s; +#endif + + viewport = XtParent(wp->w); + horiz_sb = XtNameToWidget(viewport, "horizontal"); + vert_sb = XtNameToWidget(viewport, "vertical"); + +/* All values are relative to currently visible area */ + +#define V_BORDER 0.3 /* if this far from vert edge, shift */ +#define H_BORDER 0.3 /* if this from from horiz edge, shift */ + +#define H_DELTA 0.4 /* distance of horiz shift */ +#define V_DELTA 0.4 /* distance of vert shift */ + + if (horiz_sb) { + XtSetArg(arg[0], XtNshown, &shown); + XtSetArg(arg[1], XtNtopOfThumb, &top); + XtGetValues(horiz_sb, arg, TWO); + + /* [ALI] Don't assume map widget is the same size as actual map */ + cursor_middle = (wp->cursx + 0.5) * wp->map_information->square_width / + wp->pixel_width; + do_call = True; + +#ifdef VERBOSE + if (cursor_middle < top) { + s = " outside left"; + } else if (cursor_middle < top + shown*H_BORDER) { + s = " close to left"; + } else if (cursor_middle > (top + shown)) { + s = " outside right"; + } else if (cursor_middle > (top + shown - shown*H_BORDER)) { + s = " close to right"; + } else { + s = ""; + } + printf("Horiz: shown = %3.2f, top = %3.2f%s", shown, top, s); +#endif + + if (cursor_middle < top) { + top = cursor_middle - shown*H_DELTA; + if (top < 0.0) top = 0.0; + } else if (cursor_middle < top + shown*H_BORDER) { + top -= shown*H_DELTA; + if (top < 0.0) top = 0.0; + } else if (cursor_middle > (top + shown)) { + top = cursor_middle - shown*H_DELTA; + if (top < 0.0) top = 0.0; + if (top + shown > 1.0) top = 1.0 - shown; + } else if (cursor_middle > (top + shown - shown*H_BORDER)) { + top += shown*H_DELTA; + if (top + shown > 1.0) top = 1.0 - shown; + } else { + do_call = False; + } + + if (do_call) { + XtCallCallbacks(horiz_sb, XtNjumpProc, &top); + adjusted = True; + } + } + + if (vert_sb) { + XtSetArg(arg[0], XtNshown, &shown); + XtSetArg(arg[1], XtNtopOfThumb, &top); + XtGetValues(vert_sb, arg, TWO); + + cursor_middle = (wp->cursy + 0.5) * wp->map_information->square_height / + wp->pixel_height; + do_call = True; + +#ifdef VERBOSE + if (cursor_middle < top) { + s = " above top"; + } else if (cursor_middle < top + shown*V_BORDER) { + s = " close to top"; + } else if (cursor_middle > (top + shown)) { + s = " below bottom"; + } else if (cursor_middle > (top + shown - shown*V_BORDER)) { + s = " close to bottom"; + } else { + s = ""; + } + printf("%sVert: shown = %3.2f, top = %3.2f%s", + horiz_sb ? "; " : "", shown, top, s); +#endif + + if (cursor_middle < top) { + top = cursor_middle - shown*V_DELTA; + if (top < 0.0) top = 0.0; + } else if (cursor_middle < top + shown*V_BORDER) { + top -= shown*V_DELTA; + if (top < 0.0) top = 0.0; + } else if (cursor_middle > (top + shown)) { + top = cursor_middle - shown*V_DELTA; + if (top < 0.0) top = 0.0; + if (top + shown > 1.0) top = 1.0 - shown; + } else if (cursor_middle > (top + shown - shown*V_BORDER)) { + top += shown*V_DELTA; + if (top + shown > 1.0) top = 1.0 - shown; + } else { + do_call = False; + } + + if (do_call) { + XtCallCallbacks(vert_sb, XtNjumpProc, &top); + adjusted = True; + } + } + + /* make sure cursor is displayed during dowhatis.. */ + if (adjusted) display_cursor(wp); + +#ifdef VERBOSE + if (horiz_sb || vert_sb) printf("\n"); +#endif +} + + +/* + * Check to see if the viewport has grown smaller. If so, then we want to make + * sure that the cursor is still on the screen. We do this to keep the cursor + * on the screen when the user resizes the nethack window. + */ +static void +map_check_size_change(wp) + struct xwindow *wp; +{ + struct map_info_t *map_info = wp->map_information; + Arg arg[2]; + Dimension new_width, new_height; + Widget viewport; + + viewport = XtParent(wp->w); + + XtSetArg(arg[0], XtNwidth, &new_width); + XtSetArg(arg[1], XtNheight, &new_height); + XtGetValues(viewport, arg, TWO); + + /* Only do cursor check if new size is smaller. */ + if (new_width < map_info->viewport_width + || new_height < map_info->viewport_height) { + /* [ALI] If the viewport was larger than the map (and so the map + * widget was contrained to be larger than the actual map) then we + * may be able to shrink the map widget as the viewport shrinks. + */ + wp->pixel_width = map_info->square_width * COLNO; + if (wp->pixel_width < new_width) + wp->pixel_width = new_width; + wp->pixel_height = map_info->square_height * ROWNO; + if (wp->pixel_height < new_height) + wp->pixel_height = new_height; + XtSetArg(arg[0], XtNwidth, wp->pixel_width); + XtSetArg(arg[1], XtNheight, wp->pixel_height); + XtSetValues(wp->w, arg, TWO); + + check_cursor_visibility(wp); + } + + map_info->viewport_width = new_width; + map_info->viewport_height = new_height; + + /* [ALI] These may have changed if the user has re-sized the viewport */ + XtSetArg(arg[0], XtNwidth, &wp->pixel_width); + XtSetArg(arg[1], XtNheight, &wp->pixel_height); + XtGetValues(wp->w, arg, TWO); +} + +/* + * Fill in parameters "regular" and "inverse" with newly created GCs. + * Using the given background pixel and the foreground pixel optained + * by querying the widget with the resource name. + */ +static void +set_gc(w, font, resource_name, bgpixel, regular, inverse) + Widget w; + Font font; + char *resource_name; + Pixel bgpixel; + GC *regular, *inverse; +{ + XGCValues values; + XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont; + Pixel curpixel; + Arg arg[1]; + + XtSetArg(arg[0], resource_name, &curpixel); + XtGetValues(w, arg, ONE); + + values.foreground = curpixel; + values.background = bgpixel; + values.function = GXcopy; + values.font = font; + *regular = XtGetGC(w, mask, &values); + values.foreground = bgpixel; + values.background = curpixel; + values.function = GXcopy; + values.font = font; + *inverse = XtGetGC(w, mask, &values); +} + +/* + * Create the GC's for each color. + * + * I'm not sure if it is a good idea to have a GC for each color (and + * inverse). It might be faster to just modify the foreground and + * background colors on the current GC as needed. + */ +static void +get_text_gc(wp, font) + struct xwindow *wp; + Font font; +{ + struct map_info_t *map_info = wp->map_information; + Pixel bgpixel; + Arg arg[1]; + + /* Get background pixel. */ + XtSetArg(arg[0], XtNbackground, &bgpixel); + XtGetValues(wp->w, arg, ONE); + +#ifdef TEXTCOLOR +#define set_color_gc(nh_color, resource_name) \ + set_gc(wp->w, font, resource_name, bgpixel, \ + &map_info->mtype.text_map->color_gcs[nh_color], \ + &map_info->mtype.text_map->inv_color_gcs[nh_color]); + + set_color_gc(CLR_BLACK, XtNblack); + set_color_gc(CLR_RED, XtNred); + set_color_gc(CLR_GREEN, XtNgreen); + set_color_gc(CLR_BROWN, XtNbrown); + set_color_gc(CLR_BLUE, XtNblue); + set_color_gc(CLR_MAGENTA, XtNmagenta); + set_color_gc(CLR_CYAN, XtNcyan); + set_color_gc(CLR_GRAY, XtNgray); + set_color_gc(NO_COLOR, XtNforeground); + set_color_gc(CLR_ORANGE, XtNorange); + set_color_gc(CLR_BRIGHT_GREEN, XtNbright_green); + set_color_gc(CLR_YELLOW, XtNyellow); + set_color_gc(CLR_BRIGHT_BLUE, XtNbright_blue); + set_color_gc(CLR_BRIGHT_MAGENTA, XtNbright_magenta); + set_color_gc(CLR_BRIGHT_CYAN, XtNbright_cyan); + set_color_gc(CLR_WHITE, XtNwhite); +#else + set_gc(wp->w, font, XtNforeground, bgpixel, + &map_info->mtype.text_map->copy_gc, + &map_info->mtype.text_map->inv_copy_gc); +#endif +} + + +/* + * Display the cursor on the map window. + */ +static void +display_cursor(wp) + struct xwindow *wp; +{ + /* Redisplay the cursor location inverted. */ + map_update(wp, wp->cursy, wp->cursy, wp->cursx, wp->cursx, TRUE); +} + + +/* + * Check if there are any changed characters. If so, then plaster them on + * the screen. + */ +void +display_map_window(wp) + struct xwindow *wp; +{ + register int row; + struct map_info_t *map_info = wp->map_information; + + /* + * If the previous cursor position is not the same as the current + * cursor position, then update the old cursor position. + */ + if (wp->prevx != wp->cursx || wp->prevy != wp->cursy) { + register unsigned int x = wp->prevx, y = wp->prevy; + if (x < map_info->t_start[y]) map_info->t_start[y] = x; + if (x > map_info->t_stop[y]) map_info->t_stop[y] = x; + } + + for (row = 0; row < ROWNO; row++) { + if (map_info->t_start[row] <= map_info->t_stop[row]) { + map_update(wp, row, row, + (int) map_info->t_start[row], + (int) map_info->t_stop[row], FALSE); + map_info->t_start[row] = COLNO-1; + map_info->t_stop[row] = 0; + } + } + display_cursor(wp); + wp->prevx = wp->cursx; /* adjust old cursor position */ + wp->prevy = wp->cursy; +} + +/* + * Set all map tiles to S_stone + */ +static void +map_all_stone(map_info) +struct map_info_t *map_info; +{ + int i; + unsigned short *sp, stone; + stone = cmap_to_glyph(S_stone); + + for (sp = (unsigned short *) map_info->mtype.tile_map->glyphs, i = 0; + i < ROWNO*COLNO; sp++, i++) + + *sp = stone; +} + +/* + * Fill the saved screen characters with the "clear" tile or character. + * + * Flush out everything by resetting the "new" bounds and calling + * display_map_window(). + */ +void +clear_map_window(wp) + struct xwindow *wp; +{ + struct map_info_t *map_info = wp->map_information; + + if (map_info->is_tile) { + map_all_stone(map_info); + } else { + /* Fill text with spaces, and update */ + (void) memset((genericptr_t) map_info->mtype.text_map->text, ' ', + sizeof(map_info->mtype.text_map->text)); +#ifdef TEXTCOLOR + (void) memset((genericptr_t) map_info->mtype.text_map->colors, NO_COLOR, + sizeof(map_info->mtype.text_map->colors)); +#endif + } + + /* force a full update */ + (void) memset((genericptr_t) map_info->t_start, (char) 0, + sizeof(map_info->t_start)); + (void) memset((genericptr_t) map_info->t_stop, (char) COLNO-1, + sizeof(map_info->t_stop)); + display_map_window(wp); +} + +/* + * Retreive the font associated with the map window and save attributes + * that are used when updating it. + */ +static void +get_char_info(wp) + struct xwindow *wp; +{ + XFontStruct *fs; + struct map_info_t *map_info = wp->map_information; + + fs = WindowFontStruct(wp->w); + map_info->square_width = fs->max_bounds.width; + map_info->square_height = fs->max_bounds.ascent + fs->max_bounds.descent; + map_info->square_ascent = fs->max_bounds.ascent; + map_info->square_lbearing = -fs->min_bounds.lbearing; + +#ifdef VERBOSE + printf("Font information:\n"); + printf("fid = %ld, direction = %d\n", fs->fid, fs->direction); + printf("first = %d, last = %d\n", + fs->min_char_or_byte2, fs->max_char_or_byte2); + printf("all chars exist? %s\n", fs->all_chars_exist?"yes":"no"); + printf("min_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n", + fs->min_bounds.lbearing, fs->min_bounds.rbearing, + fs->min_bounds.width, fs->min_bounds.ascent, + fs->min_bounds.descent, fs->min_bounds.attributes); + printf("max_bounds:lb=%d rb=%d width=%d asc=%d des=%d attr=%d\n", + fs->max_bounds.lbearing, fs->max_bounds.rbearing, + fs->max_bounds.width, fs->max_bounds.ascent, + fs->max_bounds.descent, fs->max_bounds.attributes); + printf("per_char = 0x%lx\n", (unsigned long) fs->per_char); + printf("Text: (max) width = %d, height = %d\n", + map_info->square_width, map_info->square_height); +#endif + + if (fs->min_bounds.width != fs->max_bounds.width) + X11_raw_print("Warning: map font is not monospaced!"); +} + +/* + * keyhit buffer + */ +#define INBUF_SIZE 64 +int inbuf[INBUF_SIZE]; +int incount = 0; +int inptr = 0; /* points to valid data */ + + +/* + * Keyboard and button event handler for map window. + */ +void +map_input(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + XKeyEvent *key; + XButtonEvent *button; + boolean meta = FALSE; + int i, nbytes; + Cardinal in_nparams = (num_params ? *num_params : 0); + char c; + char keystring[MAX_KEY_STRING]; + + switch (event->type) { + case ButtonPress: + button = (XButtonEvent *) event; +#ifdef VERBOSE_INPUT + printf("button press\n"); +#endif + if (in_nparams > 0 && + (nbytes = strlen(params[0])) < MAX_KEY_STRING) { + Strcpy(keystring, params[0]); + key = (XKeyEvent *) event; /* just in case */ + goto key_events; + } + if (w != window_list[WIN_MAP].w) { +#ifdef VERBOSE_INPUT + printf("map_input called from wrong window\n"); +#endif + X11_nhbell(); + return; + } + set_button_values(w, button->x, button->y, button->button); + break; + case KeyPress: +#ifdef VERBOSE_INPUT + printf("key: "); +#endif + if(appResources.slow && input_func) { + (*input_func)(w, event, params, num_params); + break; + } + + /* + * Don't use key_event_to_char() because we want to be able + * to allow keys mapped to multiple characters. + */ + key = (XKeyEvent *) event; + if (in_nparams > 0 && + (nbytes = strlen(params[0])) < MAX_KEY_STRING) { + Strcpy(keystring, params[0]); + } else { + /* + * Assume that mod1 is really the meta key. + */ + meta = !!(key->state & Mod1Mask); + nbytes = + XLookupString(key, keystring, MAX_KEY_STRING, + (KeySym *)0, (XComposeStatus *)0); + } + key_events: + /* Modifier keys return a zero length string when pressed. */ + if (nbytes) { +#ifdef VERBOSE_INPUT + printf("\""); +#endif + for (i = 0; i < nbytes; i++) { + c = keystring[i]; + + if (incount < INBUF_SIZE) { + inbuf[(inptr+incount)%INBUF_SIZE] = + ((int) c) + (meta ? 0x80 : 0); + incount++; + } else { + X11_nhbell(); + } +#ifdef VERBOSE_INPUT + if (meta) /* meta will print as M */ + (void) putchar('M'); + if (c < ' ') { /* ctrl will print as ^ */ + (void) putchar('^'); + c += '@'; + } + (void) putchar(c); +#endif + } +#ifdef VERBOSE_INPUT + printf("\" [%d bytes]\n", nbytes); +#endif + } + break; + + default: + impossible("unexpected X event, type = %d\n", (int) event->type); + break; + } +} + +static void +set_button_values(w, x, y, button) + Widget w; + int x; + int y; + unsigned int button; +{ + struct xwindow *wp; + struct map_info_t *map_info; + + wp = find_widget(w); + map_info = wp->map_information; + + click_x = x / map_info->square_width; + click_y = y / map_info->square_height; + + /* The values can be out of range if the map window has been resized */ + /* to be larger than the max size. */ + if (click_x >= COLNO) click_x = COLNO-1; + if (click_y >= ROWNO) click_x = ROWNO-1; + + /* Map all buttons but the first to the second click */ + click_button = (button == Button1) ? CLICK_1 : CLICK_2; +} + +/* + * Map window expose callback. + */ +/*ARGSUSED*/ +static void +map_exposed(w, client_data, widget_data) + Widget w; + XtPointer client_data; /* unused */ + XtPointer widget_data; /* expose event from Window widget */ +{ + int x, y; + struct xwindow *wp; + struct map_info_t *map_info; + unsigned width, height; + int start_row, stop_row, start_col, stop_col; + XExposeEvent *event = (XExposeEvent *) widget_data; + int t_height, t_width; /* tile/text height & width */ + + if (!XtIsRealized(w) || event->count > 0) return; + + wp = find_widget(w); + map_info = wp->map_information; + if (wp->keep_window && !map_info) return; + /* + * The map is sent an expose event when the viewport resizes. Make sure + * that the cursor is still in the viewport after the resize. + */ + map_check_size_change(wp); + + if (event) { /* called from button-event */ + x = event->x; + y = event->y; + width = event->width; + height = event->height; + } else { + x = 0; + y = 0; + width = wp->pixel_width; + height= wp->pixel_height; + } + /* + * Convert pixels into INCLUSIVE text rows and columns. + */ + t_height = map_info->square_height; + t_width = map_info->square_width; + start_row = y / t_height; + stop_row = ((y + height) / t_height) + + ((((y + height) % t_height) == 0) ? 0 : 1) - 1; + + start_col = x / t_width; + stop_col = ((x + width) / t_width) + + ((((x + width) % t_width) == 0) ? 0 : 1) - 1; + +#ifdef VERBOSE + printf("map_exposed: x = %d, y = %d, width = %d, height = %d\n", + x, y, width, height); + printf("chars %d x %d, rows %d to %d, columns %d to %d\n", + map_info->square_height, map_info->square_width, + start_row, stop_row, start_col, stop_col); +#endif + + /* Out of range values are possible if the map window is resized to be */ + /* bigger than the largest expected value. */ + if (stop_row >= ROWNO) stop_row = ROWNO-1; + if (stop_col >= COLNO) stop_col = COLNO-1; + + map_update(wp, start_row, stop_row, start_col, stop_col, FALSE); + display_cursor(wp); /* make sure cursor shows up */ +} + +/* + * Do the actual work of the putting characters onto our X window. This + * is called from the expose event routine, the display window (flush) + * routine, and the display cursor routine. The later is a kludge that + * involves the inverted parameter of this function. A better solution + * would be to double the color count, with any color above CLR_MAX + * being inverted. + * + * This works for rectangular regions (this includes one line rectangles). + * The start and stop columns are *inclusive*. + */ +static void +map_update(wp, start_row, stop_row, start_col, stop_col, inverted) + struct xwindow *wp; + int start_row, stop_row, start_col, stop_col; + boolean inverted; +{ + int win_start_row, win_start_col; + struct map_info_t *map_info = wp->map_information; + int row; + register int count; + + if (start_row < 0 || stop_row >= ROWNO) { + impossible("map_update: bad row range %d-%d\n", start_row, stop_row); + return; + } + if (start_col < 0 || stop_col >=COLNO) { + impossible("map_update: bad col range %d-%d\n", start_col, stop_col); + return; + } + +#ifdef VERBOSE_UPDATE + printf("update: [0x%x] %d %d %d %d\n", + (int) wp->w, start_row, stop_row, start_col, stop_col); +#endif + win_start_row = start_row; + win_start_col = start_col; + + if (map_info->is_tile) { + struct tile_map_info_t *tile_map = map_info->mtype.tile_map; + int cur_col; + Display* dpy = XtDisplay(wp->w); + Screen* screen = DefaultScreenOfDisplay(dpy); + + for (row = start_row; row <= stop_row; row++) { + for (cur_col = start_col; cur_col <= stop_col; cur_col++) { + int glyph = tile_map->glyphs[row][cur_col]; + int tile = glyph2tile[glyph]; + int src_x, src_y; + int dest_x = cur_col * map_info->square_width; + int dest_y = row * map_info->square_height; + + src_x = (tile % TILES_PER_ROW) * tile_width; + src_y = (tile / TILES_PER_ROW) * tile_height; + XCopyArea(dpy, tile_pixmap, XtWindow(wp->w), + tile_map->black_gc, /* no grapics_expose */ + src_x, src_y, + tile_width, tile_height, + dest_x, dest_y); + + if (glyph_is_pet(glyph) && iflags.hilite_pet) { + /* draw pet annotation (a heart) */ + XSetForeground(dpy, tile_map->black_gc, pet_annotation.foreground); + XSetClipOrigin(dpy, tile_map->black_gc, dest_x, dest_y); + XSetClipMask(dpy, tile_map->black_gc, pet_annotation.bitmap); + XCopyPlane( + dpy, + pet_annotation.bitmap, + XtWindow(wp->w), + tile_map->black_gc, + 0,0, + pet_annotation.width,pet_annotation.height, + dest_x,dest_y, + 1 + ); + XSetClipOrigin(dpy, tile_map->black_gc, 0, 0); + XSetClipMask(dpy, tile_map->black_gc, None); + XSetForeground(dpy, tile_map->black_gc, BlackPixelOfScreen(screen)); + } + } + } + + if (inverted) { + XDrawRectangle(XtDisplay(wp->w), XtWindow(wp->w), +#ifdef USE_WHITE + /* kludge for white square... */ + tile_map->glyphs[start_row][start_col] == + cmap_to_glyph(S_ice) ? + tile_map->black_gc : tile_map->white_gc, +#else + tile_map->white_gc, +#endif + start_col * map_info->square_width, + start_row * map_info->square_height, + map_info->square_width-1, + map_info->square_height-1); + } + } else { + struct text_map_info_t *text_map = map_info->mtype.text_map; + +#ifdef TEXTCOLOR + if (iflags.use_color) { + register char *c_ptr; + char *t_ptr; + int cur_col, color, win_ystart; + + for (row = start_row; row <= stop_row; row++) { + win_ystart = map_info->square_ascent + + (row * map_info->square_height); + + t_ptr = (char *) &(text_map->text[row][start_col]); + c_ptr = (char *) &(text_map->colors[row][start_col]); + cur_col = start_col; + while (cur_col <= stop_col) { + color = *c_ptr++; + count = 1; + while ((cur_col + count) <= stop_col && *c_ptr == color) { + count++; + c_ptr++; + } + + XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w), + inverted ? text_map->inv_color_gcs[color] : + text_map->color_gcs[color], + map_info->square_lbearing + (map_info->square_width * cur_col), + win_ystart, + t_ptr, count); + + /* move text pointer and column count */ + t_ptr += count; + cur_col += count; + } /* col loop */ + } /* row loop */ + } else +#endif /* TEXTCOLOR */ + { + int win_row, win_xstart; + + /* We always start at the same x window position and have */ + /* the same character count. */ + win_xstart = map_info->square_lbearing + + (win_start_col * map_info->square_width); + count = stop_col - start_col + 1; + + for (row = start_row, win_row = win_start_row; + row <= stop_row; row++, win_row++) { + + XDrawImageString(XtDisplay(wp->w), XtWindow(wp->w), + inverted ? text_map->inv_copy_gc : text_map->copy_gc, + win_xstart, + map_info->square_ascent + (win_row * map_info->square_height), + (char *) &(text_map->text[row][start_col]), count); + } + } + } +} + +/* Adjust the number of rows and columns on the given map window */ +void +set_map_size(wp, cols, rows) + struct xwindow *wp; + Dimension cols, rows; +{ + Arg args[4]; + Cardinal num_args; + + wp->pixel_width = wp->map_information->square_width * cols; + wp->pixel_height = wp->map_information->square_height * rows; + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++; + XtSetValues(wp->w, args, num_args); +} + + +static void +init_text(wp) + struct xwindow *wp; +{ + + struct map_info_t *map_info = wp->map_information; + struct text_map_info_t *text_map; + + map_info->is_tile = FALSE; + text_map = map_info->mtype.text_map = + (struct text_map_info_t *) alloc(sizeof(struct text_map_info_t)); + + (void) memset((genericptr_t) text_map->text, ' ', sizeof(text_map->text)); +#ifdef TEXTCOLOR + (void) memset((genericptr_t) text_map->colors, NO_COLOR, + sizeof(text_map->colors)); +#endif + + get_char_info(wp); + get_text_gc(wp, WindowFont(wp->w)); +} + +static char map_translations[] = +"#override\n\ + Left: scroll(4)\n\ + Right: scroll(6)\n\ + Up: scroll(8)\n\ + Down: scroll(2)\n\ + : input() \ +"; + +/* + * The map window creation routine. + */ +void +create_map_window(wp, create_popup, parent) + struct xwindow *wp; + boolean create_popup; /* parent is a popup shell that we create */ + Widget parent; +{ + struct map_info_t *map_info; /* map info pointer */ + Widget map, viewport; + Arg args[16]; + Cardinal num_args; + Dimension rows, columns; +#if 0 + int screen_width, screen_height; +#endif + + wp->type = NHW_MAP; + + if (create_popup) { + /* + * Create a popup that accepts key and button events. + */ + num_args = 0; + XtSetArg(args[num_args], XtNinput, False); num_args++; + + wp->popup = parent = XtCreatePopupShell("nethack", + topLevelShellWidgetClass, + toplevel, args, num_args); + /* + * If we're here, then this is an auxiliary map window. If we're + * cancelled via a delete window message, we should just pop down. + */ + } + + num_args = 0; + XtSetArg(args[num_args], XtNallowHoriz, True); num_args++; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + /* XtSetArg(args[num_args], XtNforceBars, True); num_args++; */ + XtSetArg(args[num_args], XtNuseBottom, True); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(map_translations)); num_args++; + viewport = XtCreateManagedWidget( + "map_viewport", /* name */ + viewportWidgetClass, /* widget class from Window.h */ + parent, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + /* + * Create a map window. We need to set the width and height to some + * value when we create it. We will change it to the value we want + * later + */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, 100); num_args++; + XtSetArg(args[num_args], XtNheight, 100); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(map_translations)); num_args++; + + wp->w = map = XtCreateManagedWidget( + "map", /* name */ + windowWidgetClass, /* widget class from Window.h */ + viewport, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + XtAddCallback(map, XtNexposeCallback, map_exposed, (XtPointer) 0); + + map_info = wp->map_information = + (struct map_info_t *) alloc(sizeof(struct map_info_t)); + + map_info->viewport_width = map_info->viewport_height = 0; + + /* reset the "new entry" indicators */ + (void) memset((genericptr_t) map_info->t_start, (char) COLNO, + sizeof(map_info->t_start)); + (void) memset((genericptr_t) map_info->t_stop, (char) 0, + sizeof(map_info->t_stop)); + + /* we probably want to restrict this to the 1st map window only */ + if (appResources.tile_file[0] && init_tiles(wp)) { + map_info->is_tile = TRUE; + } else { + init_text(wp); + map_info->is_tile = FALSE; + } + + + /* + * Initially, set the map widget to be the size specified by the + * widget rows and columns resources. We need to do this to + * correctly set the viewport window size. After the viewport is + * realized, then the map can resize to its normal size. + */ + num_args = 0; + XtSetArg(args[num_args], XtNrows, &rows); num_args++; + XtSetArg(args[num_args], XtNcolumns, &columns); num_args++; + XtGetValues(wp->w, args, num_args); + + /* Don't bother with windows larger than ROWNOxCOLNO. */ + if (columns > COLNO) columns = COLNO; + if (rows > ROWNO) rows = ROWNO; + +#if 0 /* This is insufficient. We now resize final window in winX.c */ + /* + * Check for overrunning the size of the screen. This does an ad hoc + * job. + * + * Width: We expect that there is nothing but borders on either side + * of the map window. Use some arbitrary width to decide + * when to shrink. + * + * Height: if the map takes up more than 1/2 of the screen height, start + * reducing its size. + */ + screen_height = HeightOfScreen(XtScreen(wp->w)); + screen_width = WidthOfScreen(XtScreen(wp->w)); + +#define WOFF 50 + if ((int)(columns*map_info->square_width) > screen_width-WOFF) { + columns = (screen_width-WOFF) / map_info->square_width; + if (columns == 0) columns = 1; + } + + if ((int)(rows*map_info->square_height) > screen_height/2) { + rows = screen_height / (2*map_info->square_height); + if (rows == 0) rows = 1; + } +#endif + + set_map_size(wp, columns, rows); + + + /* + * If we have created our own popup, then realize it so that the + * viewport is also realized. Then resize the map window. + */ + if (create_popup) { + XtRealizeWidget(wp->popup); + XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup), + &wm_delete_window, 1); + set_map_size(wp, COLNO, ROWNO); + } + + if (map_info->is_tile) { + map_all_stone(map_info); + } +} + +/* + * Destroy this map window. + */ +void +destroy_map_window(wp) + struct xwindow *wp; +{ + struct map_info_t *map_info = wp->map_information; + + if (wp->popup) + nh_XtPopdown(wp->popup); + + if (map_info) { + struct text_map_info_t *text_map = map_info->mtype.text_map; + + /* Free allocated GCs. */ + if (!map_info->is_tile) { +#ifdef TEXTCOLOR + int i; + + for (i = 0; i < CLR_MAX; i++) { + XtReleaseGC(wp->w, text_map->color_gcs[i]); + XtReleaseGC(wp->w, text_map->inv_color_gcs[i]); + } +#else + XtReleaseGC(wp->w, text_map->copy_gc); + XtReleaseGC(wp->w, text_map->inv_copy_gc); +#endif + } + /* free alloc'ed text information */ + free((genericptr_t)text_map), map_info->mtype.text_map = 0; + + /* Free malloc'ed space. */ + free((genericptr_t)map_info), wp->map_information = 0; + } + + /* Destroy map widget. */ + if (wp->popup && !wp->keep_window) + XtDestroyWidget(wp->popup), wp->popup = (Widget)0; + + if (wp->keep_window) + XtRemoveCallback(wp->w, XtNexposeCallback, map_exposed, (XtPointer)0); + else + wp->type = NHW_NONE; /* allow re-use */ +} + + + +boolean exit_x_event; /* exit condition for the event loop */ +/******* +pkey(k) + int k; +{ + printf("key = '%s%c'\n", (k<32) ? "^":"", (k<32) ? '@'+k : k); +} +******/ + +/* + * Main X event loop. Here we accept and dispatch X events. We only exit + * under certain circumstances. + */ +int +x_event(exit_condition) + int exit_condition; +{ + XEvent event; + int retval = 0; + boolean keep_going = TRUE; + + /* Hold globals so function is re-entrant */ + boolean hold_exit_x_event = exit_x_event; + + click_button = NO_CLICK; /* reset click exit condition */ + exit_x_event = FALSE; /* reset callback exit condition */ + + /* + * Loop until we get a sent event, callback exit, or are accepting key + * press and button press events and we receive one. + */ + if((exit_condition == EXIT_ON_KEY_PRESS || + exit_condition == EXIT_ON_KEY_OR_BUTTON_PRESS) && incount) + goto try_test; + + do { + XtAppNextEvent(app_context, &event); + XtDispatchEvent(&event); + + /* See if we can exit. */ + try_test: + switch (exit_condition) { + case EXIT_ON_SENT_EVENT: { + XAnyEvent *any = (XAnyEvent *) &event; + if (any->send_event) { + retval = 0; + keep_going = FALSE; + } + break; + } + case EXIT_ON_EXIT: + if (exit_x_event) { + incount = 0; + retval = 0; + keep_going = FALSE; + } + break; + case EXIT_ON_KEY_PRESS: + if (incount != 0) { + /* get first pressed key */ + --incount; + retval = inbuf[inptr]; + inptr = (inptr+1) % INBUF_SIZE; + /* pkey(retval); */ + keep_going = FALSE; + } + break; + case EXIT_ON_KEY_OR_BUTTON_PRESS: + if (incount != 0 || click_button != NO_CLICK) { + if (click_button != NO_CLICK) { /* button press */ + /* click values are already set */ + retval = 0; + } else { /* key press */ + /* get first pressed key */ + --incount; + retval = inbuf[inptr]; + inptr = (inptr+1) % INBUF_SIZE; + /* pkey(retval); */ + } + keep_going = FALSE; + } + break; + default: + panic("x_event: unknown exit condition %d", exit_condition); + break; + } + } while (keep_going); + + /* Restore globals */ + exit_x_event = hold_exit_x_event; + + return retval; +} + +/*winmap.c*/ diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c new file mode 100644 index 0000000..0d2640f --- /dev/null +++ b/win/X11/winmenu.c @@ -0,0 +1,1146 @@ +/* SCCS Id: @(#)winmenu.c 3.4 1996/08/15 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * File for creating menus. + * + * + Global functions: start_menu, add_menu, end_menu, select_menu + */ +/*#define USE_FWF*/ /* use FWF's list widget */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_FWF +#include +#else +#include +#endif +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" +#include "winX.h" +#include + + +static void FDECL(menu_select, (Widget, XtPointer, XtPointer)); +static void FDECL(invert_line, (struct xwindow *,x11_menu_item *,int,long)); +static void FDECL(menu_ok, (Widget, XtPointer, XtPointer)); +static void FDECL(menu_cancel, (Widget, XtPointer, XtPointer)); +static void FDECL(menu_all, (Widget, XtPointer, XtPointer)); +static void FDECL(menu_none, (Widget, XtPointer, XtPointer)); +static void FDECL(menu_invert, (Widget, XtPointer, XtPointer)); +static void FDECL(menu_search, (Widget, XtPointer, XtPointer)); +static void FDECL(select_all, (struct xwindow *)); +static void FDECL(select_none, (struct xwindow *)); +static void FDECL(select_match, (struct xwindow *, char*)); +static void FDECL(invert_all, (struct xwindow *)); +static void FDECL(invert_match, (struct xwindow *, char*)); +static void FDECL(menu_popdown, (struct xwindow *)); +#ifdef USE_FWF +static void FDECL(sync_selected, (struct menu_info_t *, int, int *)); +#endif + +static void FDECL(move_menu, (struct menu *, struct menu *)); +static void FDECL(free_menu, (struct menu *)); +static void FDECL(reset_menu_to_default, (struct menu *)); +static void FDECL(clear_old_menu, (struct xwindow *)); +static char *FDECL(copy_of, (const char *)); + +#define reset_menu_count(mi) ((mi)->counting = FALSE, (mi)->menu_count = 0L) + + +static const char menu_translations[] = + "#override\n\ + Left: scroll(4)\n\ + Right: scroll(6)\n\ + Up: scroll(8)\n\ + Down: scroll(2)\n\ + : menu_key()"; + +/* + * Menu callback. + */ +/* ARGSUSED */ +static void +menu_select(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + struct xwindow *wp; + struct menu_info_t *menu_info; +#ifdef USE_FWF + XfwfMultiListReturnStruct *lrs = (XfwfMultiListReturnStruct *) call_data; +#else + XawListReturnStruct *lrs = (XawListReturnStruct *) call_data; + int i; + x11_menu_item *curr; +#endif + long how_many; + + wp = find_widget(w); + menu_info = wp->menu_information; + how_many = menu_info->counting ? menu_info->menu_count : -1L; + reset_menu_count(menu_info); + +#ifdef USE_FWF + /* if we've reached here, we've found our selected item */ + switch (lrs->action) { + case XfwfMultiListActionNothing: + pline("menu_select: nothing action?"); + break; + case XfwfMultiListActionStatus: + pline("menu_select: status action?"); + break; + case XfwfMultiListActionHighlight: + case XfwfMultiListActionUnhighlight: + sync_selected(menu_info,lrs->num_selected,lrs->selected_items); + break; + } +#else + for (i = 0, curr = menu_info->curr_menu.base; i < lrs->list_index; i++) { + if (!curr) panic("menu_select: out of menu items!"); + curr = curr->next; + } + XawListUnhighlight(w); /* unhilight item */ + + /* if the menu is not active or don't have an identifier, try again */ + if (!menu_info->is_active || curr->identifier.a_void == 0) { + X11_nhbell(); + return; + } + + /* if we've reached here, we've found our selected item */ + curr->selected = !curr->selected; + if (curr->selected) { + curr->str[2] = (how_many != -1L) ? '#' : '+'; + curr->pick_count = how_many; + } else { + curr->str[2] = '-'; + curr->pick_count = -1L; + } + XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); +#endif + + if (menu_info->how == PICK_ONE) + menu_popdown(wp); +} + +/* + * Called when menu window is deleted. + */ +/* ARGSUSED */ +void +menu_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + menu_cancel((Widget)None, (XtPointer) find_widget(w), (XtPointer) 0); +} + +/* + * Invert the count'th line (curr) in the given window. + */ +/*ARGSUSED*/ +static void +invert_line(wp, curr, which, how_many) + struct xwindow *wp; + x11_menu_item *curr; + int which; + long how_many; +{ + reset_menu_count(wp->menu_information); + curr->selected = !curr->selected; + if (curr->selected) { +#ifdef USE_FWF + XfwfMultiListHighlightItem((XfwfMultiListWidget)wp->w, which); +#else + curr->str[2] = (how_many != -1) ? '#' : '+'; +#endif + curr->pick_count = how_many; + } else { +#ifdef USE_FWF + XfwfMultiListUnhighlightItem((XfwfMultiListWidget)wp->w, which); +#else + curr->str[2] = '-'; +#endif + curr->pick_count = -1L; + } +} + +/* + * Called when we get a key press event on a menu window. + */ +/* ARGSUSED */ +void +menu_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + struct menu_info_t *menu_info; + x11_menu_item *curr; + struct xwindow *wp; + char ch; + int count; + + wp = find_widget(w); + menu_info = wp->menu_information; + + ch = key_event_to_char((XKeyEvent *) event); + + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + + if (menu_info->is_active) { /* waiting for input */ + ch = map_menu_cmd(ch); + if (ch == '\033') { /* quit */ + if (menu_info->counting) { + /* when there's a count in progress, ESC discards it + rather than dismissing the whole menu */ + reset_menu_count(menu_info); + return; + } + select_none(wp); + } else if (ch == '\n' || ch == '\r') { + ; /* accept */ + } else if (isdigit(ch)) { + /* special case: '0' is also the default ball class */ + if (ch == '0' && !menu_info->counting && + index(menu_info->curr_menu.gacc, ch)) + goto group_accel; + menu_info->menu_count *= 10L; + menu_info->menu_count += (long)(ch - '0'); + if (menu_info->menu_count != 0L) /* ignore leading zeros */ + menu_info->counting = TRUE; + return; + } else if (ch == MENU_SEARCH) { /* search */ + if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) { + char buf[BUFSZ]; + X11_getlin("Search for:", buf); + if (!*buf || *buf == '\033') return; + if (menu_info->how == PICK_ANY) { + invert_match(wp, buf); + return; + } else { + select_match(wp, buf); + } + } else { + X11_nhbell(); + return; + } + } else if (ch == MENU_SELECT_ALL) { /* select all */ + if (menu_info->how == PICK_ANY) + select_all(wp); + else + X11_nhbell(); + return; + } else if (ch == MENU_UNSELECT_ALL) { /* unselect all */ + if (menu_info->how == PICK_ANY) + select_none(wp); + else + X11_nhbell(); + return; + } else if (ch == MENU_INVERT_ALL) { /* invert all */ + if (menu_info->how == PICK_ANY) + invert_all(wp); + else + X11_nhbell(); + return; + } else if (index(menu_info->curr_menu.gacc, ch)) { + group_accel: + /* matched a group accelerator */ + if (menu_info->how == PICK_ANY || menu_info->how == PICK_ONE) { + for (count = 0, curr = menu_info->curr_menu.base; curr; + curr = curr->next, count++) { + if (curr->identifier.a_void != 0 && curr->gselector == ch) { + invert_line(wp, curr, count, -1L); + /* for PICK_ONE, a group accelerator will + only be included in gacc[] if it matches + exactly one entry, so this must be it... */ + if (menu_info->how == PICK_ONE) + goto menu_done; /* pop down */ + } + } +#ifndef USE_FWF + XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); +#endif + } else + X11_nhbell(); + return; + } else { + boolean selected_something = FALSE; + for (count = 0, curr = menu_info->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0 && curr->selector == ch) break; + + if (curr) { + invert_line(wp, curr, count, + menu_info->counting ? menu_info->menu_count : -1L); +#ifndef USE_FWF + XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, True); +#endif + selected_something = curr->selected; + } else { + X11_nhbell(); /* no match */ + } + if (!(selected_something && menu_info->how == PICK_ONE)) + return; /* keep going */ + } + /* pop down */ + } else { /* permanent inventory window */ + if (ch != '\033') { + X11_nhbell(); + return; + } + /* pop down on ESC */ + } + + menu_done: + menu_popdown(wp); +} + +/* ARGSUSED */ +static void +menu_ok(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + struct xwindow *wp = (struct xwindow *) client_data; + + menu_popdown(wp); +} + +/* ARGSUSED */ +static void +menu_cancel(w, client_data, call_data) + Widget w; /* don't use - may be None */ + XtPointer client_data, call_data; +{ + struct xwindow *wp = (struct xwindow *) client_data; + + if (wp->menu_information->is_active) { + select_none(wp); + wp->menu_information->cancelled = TRUE; + } + menu_popdown(wp); +} + +/* ARGSUSED */ +static void +menu_all(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + select_all((struct xwindow *) client_data); +} + +/* ARGSUSED */ +static void +menu_none(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + select_none((struct xwindow *) client_data); +} + +/* ARGSUSED */ +static void +menu_invert(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + invert_all((struct xwindow *) client_data); +} + +/* ARGSUSED */ +static void +menu_search(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + struct xwindow *wp = (struct xwindow *) client_data; + struct menu_info_t *menu_info = wp->menu_information; + + char buf[BUFSZ]; + X11_getlin("Search for:", buf); + if (!*buf || *buf == '\033') return; + + if (menu_info->how == PICK_ANY) + invert_match(wp, buf); + else + select_match(wp, buf); + + if (menu_info->how == PICK_ONE) + menu_popdown(wp); +} + +static void +select_all(wp) + struct xwindow *wp; +{ + x11_menu_item *curr; + int count; + boolean changed = FALSE; + + reset_menu_count(wp->menu_information); + for (count = 0, curr = wp->menu_information->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0) + if (!curr->selected) { + invert_line(wp, curr, count, -1L); + changed = TRUE; + } + +#ifndef USE_FWF + if (changed) + XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, + 0, 0, True); +#endif +} + +static void +select_none(wp) + struct xwindow *wp; +{ + x11_menu_item *curr; + int count; + boolean changed = FALSE; + + reset_menu_count(wp->menu_information); + for (count = 0, curr = wp->menu_information->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0) + if (curr->selected) { + invert_line(wp, curr, count, -1L); + changed = TRUE; + } + +#ifndef USE_FWF + if (changed) + XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, + 0, 0, True); +#endif +} + +static void +invert_all(wp) + struct xwindow *wp; +{ + x11_menu_item *curr; + int count; + + reset_menu_count(wp->menu_information); + for (count = 0, curr = wp->menu_information->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0) + invert_line(wp, curr, count, -1L); + +#ifndef USE_FWF + XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, + 0, 0, True); +#endif +} + +static void +invert_match(wp, match) + struct xwindow *wp; + char *match; +{ + x11_menu_item *curr; + int count; + boolean changed = FALSE; + + reset_menu_count(wp->menu_information); + for (count = 0, curr = wp->menu_information->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0 && strstri(curr->str, match)) { + invert_line(wp, curr, count, -1L); + changed = TRUE; + } + +#ifndef USE_FWF + if (changed) + XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, + 0, 0, True); +#endif +} + +static void +select_match(wp, match) + struct xwindow *wp; + char *match; +{ + x11_menu_item *curr; + int count; + + reset_menu_count(wp->menu_information); + for (count = 0, curr = wp->menu_information->curr_menu.base; curr; + curr = curr->next, count++) + if (curr->identifier.a_void != 0 && strstri(curr->str, match)) { + if (!curr->selected) { + invert_line(wp, curr, count, -1L); +#ifndef USE_FWF + XawListChange(wp->w, wp->menu_information->curr_menu.list_pointer, + 0, 0, True); +#endif + } + return; + } + + /* no match */ + X11_nhbell(); +} + +static void +menu_popdown(wp) + struct xwindow *wp; +{ + nh_XtPopdown(wp->popup); /* remove the event grab */ + if (wp->menu_information->is_active) + exit_x_event = TRUE; /* exit our event handler */ + wp->menu_information->is_up = FALSE; /* menu is down */ +} + +#ifdef USE_FWF +/* + * Make sure our idea of selected matches the FWF Multilist's idea of what + * is currently selected. The MultiList's selected list can change without + * notifying us if one or more items are selected and then another is + * selected (not toggled). Then the items that were selected are deselected + * but we are not notified. + */ +static void +sync_selected(menu_info, num_selected, items) + struct menu_info_t *menu_info; + int num_selected; + int *items; +{ + int i, j, *ip; + x11_menu_item *curr; + Boolean found; + + for (i=0, curr = menu_info->curr_menu.base; curr; i++, curr = curr->next) { + found = False; + for (j = 0, ip = items; j < num_selected; j++, ip++) + if (*ip == i) { + found = True; + break; + } +#if 0 + if (curr->selected && !found) + printf("sync: deselecting %s\n", curr->str); + else if (!curr->selected && found) + printf("sync: selecting %s\n", curr->str); +#endif + curr->selected = found ? TRUE : FALSE; + } +} +#endif /* USE_FWF */ + + +/* Global functions ======================================================== */ + +void +X11_start_menu(window) + winid window; +{ + struct xwindow *wp; + check_winid(window); + + wp = &window_list[window]; + + if (wp->menu_information->is_menu) { + /* make sure we'ere starting with a clean slate */ + free_menu(&wp->menu_information->new_menu); + } else { + wp->menu_information->is_menu = TRUE; + } +} + +/*ARGSUSED*/ +void +X11_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected) + winid window; + int glyph; /* unused (for now) */ + const anything *identifier; + char ch; + char gch; /* group accelerator (0 = no group) */ + int attr; + const char *str; + boolean preselected; +{ + x11_menu_item *item; + struct menu_info_t *menu_info; + + check_winid(window); + menu_info = window_list[window].menu_information; + if (!menu_info->is_menu) { + impossible("add_menu: called before start_menu"); + return; + } + + item = (x11_menu_item *) alloc((unsigned)sizeof(x11_menu_item)); + item->next = (x11_menu_item *) 0; + item->identifier = *identifier; + item->attr = attr; +/* item->selected = preselected; */ + item->selected = FALSE; + item->pick_count = -1L; + + if (identifier->a_void) { + char buf[4+BUFSZ]; + int len = strlen(str); + + if (!ch) { + /* Supply a keyboard accelerator. Only the first 52 get one. */ + + if (menu_info->new_menu.curr_selector) { + ch = menu_info->new_menu.curr_selector++; + if (ch == 'z') + menu_info->new_menu.curr_selector = 'A'; + else if (ch == 'Z') + menu_info->new_menu.curr_selector = 0; /* out */ + } + } + + if (len >= BUFSZ) { + /* We *think* everything's coming in off at most BUFSZ bufs... */ + impossible("Menu item too long (%d).", len); + len = BUFSZ - 1; + } + Sprintf(buf, "%c - ", ch ? ch : ' '); + (void) strncpy(buf+4, str, len); + buf[4+len] = '\0'; + item->str = copy_of(buf); + } else { + /* no keyboard accelerator */ + item->str = copy_of(str); + ch = 0; + } + + item->selector = ch; + item->gselector = gch; + + if (menu_info->new_menu.last) { + menu_info->new_menu.last->next = item; + } else { + menu_info->new_menu.base = item; + } + menu_info->new_menu.last = item; + menu_info->new_menu.count++; +} + +void +X11_end_menu(window, query) + winid window; + const char *query; +{ + struct menu_info_t *menu_info; + + check_winid(window); + menu_info = window_list[window].menu_information; + if (!menu_info->is_menu) { + impossible("end_menu: called before start_menu"); + return; + } + menu_info->new_menu.query = copy_of(query); +} + +int +X11_select_menu(window, how, menu_list) + winid window; + int how; + menu_item **menu_list; +{ + x11_menu_item *curr; + struct xwindow *wp; + struct menu_info_t *menu_info; + Arg args[10]; + Cardinal num_args; + String *ptr; + int retval; + Dimension v_pixel_width, v_pixel_height; + boolean labeled; + Widget viewport_widget, form, label, ok, cancel, all, none, invert, search; + Boolean sens; +#ifdef USE_FWF + Boolean *boolp; +#endif + char gacc[QBUFSZ], *ap; + + *menu_list = (menu_item *) 0; + check_winid(window); + wp = &window_list[window]; + menu_info = wp->menu_information; + if (!menu_info->is_menu) { + impossible("select_menu: called before start_menu"); + return 0; + } + + menu_info->how = (short) how; + + /* collect group accelerators; for PICK_NONE, they're ignored; + for PICK_ONE, only those which match exactly one entry will be + accepted; for PICK_ANY, those which match any entry are okay */ + gacc[0] = '\0'; + if (menu_info->how != PICK_NONE) { + int i, n, gcnt[128]; +#define GSELIDX(c) ((c) & 127) /* guard against `signed char' */ + + for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0; + for (n = 0, curr = menu_info->new_menu.base; curr; curr = curr->next) + if (curr->gselector && curr->gselector != curr->selector) { + ++n; + ++gcnt[GSELIDX(curr->gselector)]; + } + + if (n > 0) /* at least one group accelerator found */ + for (ap = gacc, curr = menu_info->new_menu.base; + curr; curr = curr->next) + if (curr->gselector && !index(gacc, curr->gselector) && + (menu_info->how == PICK_ANY || + gcnt[GSELIDX(curr->gselector)] == 1)) { + *ap++ = curr->gselector; + *ap = '\0'; /* re-terminate for index() */ + } + } + menu_info->new_menu.gacc = copy_of(gacc); + reset_menu_count(menu_info); + + /* + * Create a string and sensitive list for the new menu. + */ + menu_info->new_menu.list_pointer = ptr = (String *) + alloc((unsigned) (sizeof(String) * (menu_info->new_menu.count+1))); + for (curr = menu_info->new_menu.base; curr; ptr++, curr = curr->next) + *ptr = (String) curr->str; + *ptr = 0; /* terminate list with null */ + +#ifdef USE_FWF + menu_info->new_menu.sensitive = boolp = (Boolean *) + alloc((unsigned) (sizeof(Boolean) * (menu_info->new_menu.count))); + for (curr = menu_info->new_menu.base; curr; boolp++, curr = curr->next) + *boolp = (curr->identifier.a_void != 0); +#else + menu_info->new_menu.sensitive = (Boolean *) 0; +#endif + labeled = (menu_info->new_menu.query && *(menu_info->new_menu.query)) + ? TRUE : FALSE; + + /* + * Menus don't appear to size components correctly, except + * when first created. For 3.2.0 release, just recreate + * each time. + */ + if (menu_info->valid_widgets + && (window != WIN_INVEN || !flags.perm_invent)) { + XtDestroyWidget(wp->popup); + menu_info->valid_widgets = FALSE; + menu_info->is_up = FALSE; + } + + if (!menu_info->valid_widgets) { + Dimension row_spacing; + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + wp->popup = XtCreatePopupShell( + window == WIN_INVEN ? "inventory" : "menu", + how == PICK_NONE ? topLevelShellWidgetClass: + transientShellWidgetClass, + toplevel, args, num_args); + XtOverrideTranslations(wp->popup, + XtParseTranslationTable("WM_PROTOCOLS: menu_delete()")); + + + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(menu_translations)); num_args++; + form = XtCreateManagedWidget("mform", + formWidgetClass, + wp->popup, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + + if (labeled) + label = XtCreateManagedWidget(menu_info->new_menu.query, + labelWidgetClass, + form, + args, num_args); + else label = NULL; + + /* + * Create ok, cancel, all, none, invert, and search buttons.. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + ok = XtCreateManagedWidget("OK", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(ok, XtNcallback, menu_ok, (XtPointer) wp); + + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, ok); num_args++; + XtSetArg(args[num_args], XtNsensitive, how!=PICK_NONE); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + cancel = XtCreateManagedWidget("cancel", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(cancel, XtNcallback, menu_cancel, (XtPointer) wp); + + sens = (how == PICK_ANY); + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, cancel); num_args++; + XtSetArg(args[num_args], XtNsensitive, sens); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + all = XtCreateManagedWidget("all", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(all, XtNcallback, menu_all, (XtPointer) wp); + + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, all); num_args++; + XtSetArg(args[num_args], XtNsensitive, sens); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + none = XtCreateManagedWidget("none", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(none, XtNcallback, menu_none, (XtPointer) wp); + + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, none); num_args++; + XtSetArg(args[num_args], XtNsensitive, sens); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + invert = XtCreateManagedWidget("invert", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(invert, XtNcallback, menu_invert, (XtPointer) wp); + + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, invert); num_args++; + XtSetArg(args[num_args], XtNsensitive, how!=PICK_NONE); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainLeft); num_args++; + search = XtCreateManagedWidget("search", + commandWidgetClass, + form, + args, num_args); + XtAddCallback(search, XtNcallback, menu_search, (XtPointer) wp); + + num_args = 0; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; + XtSetArg(args[num_args], XtNuseBottom, True); num_args++; + XtSetArg(args[num_args], XtNuseRight, True); num_args++; +/* + XtSetArg(args[num_args], XtNforceBars, True); num_args++; +*/ + XtSetArg(args[num_args], XtNfromVert, all); num_args++; + XtSetArg(args[num_args], XtNtop, XtChainTop); num_args++; + XtSetArg(args[num_args], XtNbottom, XtChainBottom); num_args++; + XtSetArg(args[num_args], XtNleft, XtChainLeft); num_args++; + XtSetArg(args[num_args], XtNright, XtChainRight); num_args++; + viewport_widget = XtCreateManagedWidget( + "menu_viewport", /* name */ + viewportWidgetClass, + form, /* parent widget */ + args, num_args); /* values, and number of values */ + + /* make new menu the current menu */ + move_menu(&menu_info->new_menu, &menu_info->curr_menu); + + num_args = 0; + XtSetArg(args[num_args], XtNforceColumns, True); num_args++; + XtSetArg(args[num_args], XtNcolumnSpacing, 1); num_args++; + XtSetArg(args[num_args], XtNdefaultColumns, 1); num_args++; + XtSetArg(args[num_args], XtNlist, + menu_info->curr_menu.list_pointer); num_args++; +#ifdef USE_FWF + XtSetArg(args[num_args], XtNsensitiveArray, + menu_info->curr_menu.sensitive); num_args++; + XtSetArg(args[num_args], XtNmaxSelectable, + menu_info->curr_menu.count); num_args++; +#endif + wp->w = XtCreateManagedWidget( + "menu_list", /* name */ +#ifdef USE_FWF + xfwfMultiListWidgetClass, +#else + listWidgetClass, +#endif + viewport_widget, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + XtAddCallback(wp->w, XtNcallback, menu_select, (XtPointer) 0); + + /* Get the font and margin information. */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &menu_info->fs); num_args++; + XtSetArg(args[num_args], XtNinternalHeight, + &menu_info->internal_height); num_args++; + XtSetArg(args[num_args], XtNinternalWidth, + &menu_info->internal_width); num_args++; + XtSetArg(args[num_args], XtNrowSpacing, &row_spacing); num_args++; + XtGetValues(wp->w, args, num_args); + + /* font height is ascent + descent */ + menu_info->line_height = + menu_info->fs->max_bounds.ascent + + menu_info->fs->max_bounds.descent + row_spacing; + + menu_info->valid_widgets = TRUE; + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++; + XtGetValues(wp->w, args, num_args); + } else { + Dimension len; + + viewport_widget = XtParent(wp->w); + + /* get the longest string on new menu */ + v_pixel_width = 0; + for (ptr = menu_info->new_menu.list_pointer; *ptr; ptr++) { + len = XTextWidth(menu_info->fs, *ptr, strlen(*ptr)); + if (len > v_pixel_width) v_pixel_width = len; + } + + /* add viewport internal border */ + v_pixel_width += 2 * menu_info->internal_width; + v_pixel_height = (2 * menu_info->internal_height) + + (menu_info->new_menu.count * menu_info->line_height); + + /* make new menu the current menu */ + move_menu(&menu_info->new_menu, &menu_info->curr_menu); +#ifdef USE_FWF + XfwfMultiListSetNewData((XfwfMultiListWidget)wp->w, + menu_info->curr_menu.list_pointer, 0, 0, TRUE, + menu_info->curr_menu.sensitive); +#else + XawListChange(wp->w, menu_info->curr_menu.list_pointer, 0, 0, TRUE); +#endif + } + + /* if viewport will be bigger than the screen, limit its height */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &v_pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, &v_pixel_height); num_args++; + XtGetValues(wp->w, args, num_args); + if ((Dimension) XtScreen(wp->w)->height * 5 / 6 < v_pixel_height) { + /* scrollbar is 14 pixels wide. Widen the form to accommodate it. */ + v_pixel_width += 14; + + /* shrink to fit vertically */ + v_pixel_height = XtScreen(wp->w)->height * 5 / 6; + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, v_pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, v_pixel_height); num_args++; + XtSetValues(wp->w, args, num_args); + } + XtRealizeWidget(wp->popup); /* need to realize before we position */ + + /* if menu is not up, position it */ + if (!menu_info->is_up) positionpopup(wp->popup, FALSE); + + menu_info->is_up = TRUE; + if (window == WIN_INVEN && how == PICK_NONE) { + /* cant use nh_XtPopup() because it may try to grab the focus */ + XtPopup(wp->popup, (int)XtGrabNone); + if (!updated_inventory) + XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup)); + XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup), + &wm_delete_window, 1); + retval = 0; + } else { + menu_info->is_active = TRUE; /* waiting for user response */ + menu_info->cancelled = FALSE; + nh_XtPopup(wp->popup, (int)XtGrabExclusive, wp->w); + (void) x_event(EXIT_ON_EXIT); + menu_info->is_active = FALSE; + if (menu_info->cancelled) + return -1; + + retval = 0; + for (curr = menu_info->curr_menu.base; curr; curr = curr->next) + if (curr->selected) retval++; + + if (retval) { + menu_item *mi; + + *menu_list = mi = (menu_item *) alloc(retval * sizeof(menu_item)); + for (curr = menu_info->curr_menu.base; curr; curr = curr->next) + if (curr->selected) { + mi->item = curr->identifier; + mi->count = curr->pick_count; + mi++; + } + } + } + + return retval; +} + +/* End global functions ==================================================== */ + +/* + * Allocate a copy of the given string. If null, return a string of + * zero length. + * + * This is an exact duplicate of copy_of() in tty/wintty.c. + */ +static char * +copy_of(s) + const char *s; +{ + if (!s) s = ""; + return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s); +} + + +static void +move_menu(src_menu, dest_menu) + struct menu *src_menu, *dest_menu; +{ + free_menu(dest_menu); /* toss old menu */ + *dest_menu = *src_menu; /* make new menu current */ + /* leave no dangling ptrs */ + reset_menu_to_default(src_menu); +} + + +static void +free_menu(mp) + struct menu *mp; +{ + while (mp->base) { + mp->last = mp->base; + mp->base = mp->base->next; + + free((genericptr_t)mp->last->str); + free((genericptr_t)mp->last); + } + if (mp->query) free((genericptr_t) mp->query); + if (mp->gacc) free((genericptr_t) mp->gacc); + if (mp->list_pointer) free((genericptr_t) mp->list_pointer); + if (mp->sensitive) free((genericptr_t) mp->sensitive); + reset_menu_to_default(mp); +} + +static void +reset_menu_to_default(mp) + struct menu *mp; +{ + mp->base = mp->last = (x11_menu_item *)0; + mp->query = (const char *)0; + mp->gacc = (const char *)0; + mp->count = 0; + mp->list_pointer = (String *)0; + mp->sensitive = (Boolean *)0; + mp->curr_selector = 'a'; /* first accelerator */ +} + +static void +clear_old_menu(wp) + struct xwindow *wp; +{ + struct menu_info_t *menu_info = wp->menu_information; + + free_menu(&menu_info->curr_menu); + free_menu(&menu_info->new_menu); + + if (menu_info->valid_widgets) { + nh_XtPopdown(wp->popup); + menu_info->is_up = FALSE; + XtDestroyWidget(wp->popup); + menu_info->valid_widgets = FALSE; + wp->w = wp->popup = (Widget) 0; + } +} + +void +create_menu_window(wp) + struct xwindow *wp; +{ + wp->type = NHW_MENU; + wp->menu_information = + (struct menu_info_t *) alloc(sizeof(struct menu_info_t)); + (void) memset((genericptr_t) wp->menu_information, '\0', + sizeof(struct menu_info_t)); + reset_menu_to_default(&wp->menu_information->curr_menu); + reset_menu_to_default(&wp->menu_information->new_menu); + reset_menu_count(wp->menu_information); + wp->w = wp->popup = (Widget) 0; +} + +void +destroy_menu_window(wp) + struct xwindow *wp; +{ + clear_old_menu(wp); /* this will also destroy the widgets */ + free((genericptr_t) wp->menu_information); + wp->menu_information = (struct menu_info_t *) 0; + wp->type = NHW_NONE; /* allow re-use */ +} + +/*winmenu.c*/ diff --git a/win/X11/winmesg.c b/win/X11/winmesg.c new file mode 100644 index 0000000..63dac96 --- /dev/null +++ b/win/X11/winmesg.c @@ -0,0 +1,625 @@ +/* SCCS Id: @(#)winmesg.c 3.4 1996/04/05 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Message window routines. + * + * Global functions: + * create_message_window() + * destroy_message_window() + * display_message_window() + * append_message() + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "xwindow.h" /* Window widget declarations */ + +#include "hack.h" +#include "winX.h" + +static struct line_element *FDECL(get_previous, (struct line_element *)); +static void FDECL(set_circle_buf, (struct mesg_info_t *,int)); +static char *FDECL(split, (char *,XFontStruct *,DIMENSION_P)); +static void FDECL(add_line, (struct mesg_info_t *,const char *)); +static void FDECL(redraw_message_window, (struct xwindow *)); +static void FDECL(mesg_check_size_change, (struct xwindow *)); +static void FDECL(mesg_exposed, (Widget,XtPointer,XtPointer)); +static void FDECL(get_gc, (Widget,struct mesg_info_t *)); +static void FDECL(mesg_resized, (Widget,XtPointer,XtPointer)); + +static char mesg_translations[] = +"#override\n\ + : input() \ +"; + +/* Move the message window's vertical scrollbar's slider to the bottom. */ +void +set_message_slider(wp) + struct xwindow *wp; +{ + Widget scrollbar; + float top; + + scrollbar = XtNameToWidget(XtParent(wp->w), "vertical"); + + if (scrollbar) { + top = 1.0; + XtCallCallbacks(scrollbar, XtNjumpProc, &top); + } +} + + +void +create_message_window(wp, create_popup, parent) + struct xwindow *wp; /* window pointer */ + boolean create_popup; + Widget parent; +{ + Arg args[8]; + Cardinal num_args; + Widget viewport; + struct mesg_info_t *mesg_info; + + wp->type = NHW_MESSAGE; + + wp->mesg_information = mesg_info = + (struct mesg_info_t *) alloc(sizeof(struct mesg_info_t)); + + mesg_info->fs = 0; + mesg_info->num_lines = 0; + mesg_info->head = mesg_info->line_here = mesg_info->last_pause = + mesg_info->last_pause_head = (struct line_element *) 0; + mesg_info->dirty = False; + mesg_info->viewport_width = mesg_info->viewport_height = 0; + + if (iflags.msg_history < appResources.message_lines) + iflags.msg_history = appResources.message_lines; + if (iflags.msg_history > MAX_HISTORY) /* a sanity check */ + iflags.msg_history = MAX_HISTORY; + + set_circle_buf(mesg_info, (int) iflags.msg_history); + + /* Create a popup that becomes the parent. */ + if (create_popup) { + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + + wp->popup = parent = XtCreatePopupShell("message_popup", + topLevelShellWidgetClass, + toplevel, args, num_args); + /* + * If we're here, then this is an auxiliary message window. If we're + * cancelled via a delete window message, we should just pop down. + */ + } + + /* + * Create the viewport. We only want the vertical scroll bar ever to be + * visible. If we allow the horizontal scrollbar to be visible it will + * always be visible, due to the stupid way the Athena viewport operates. + */ + num_args = 0; + XtSetArg(args[num_args], XtNallowVert, True); num_args++; + viewport = XtCreateManagedWidget( + "mesg_viewport", /* name */ + viewportWidgetClass, /* widget class from Window.h */ + parent, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + /* + * Create a message window. We will change the width and height once + * we know what font we are using. + */ + num_args = 0; + if (!create_popup) { + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(mesg_translations)); num_args++; + } + wp->w = XtCreateManagedWidget( + "message", /* name */ + windowWidgetClass, /* widget class from Window.h */ + viewport, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + XtAddCallback(wp->w, XtNexposeCallback, mesg_exposed, (XtPointer) 0); + + /* + * Now adjust the height and width of the message window so that it + * is appResources.message_lines high and DEFAULT_MESSAGE_WIDTH wide. + */ + + /* Get the font information. */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &mesg_info->fs); num_args++; + XtGetValues(wp->w, args, num_args); + + /* Save character information for fast use later. */ + mesg_info->char_width = mesg_info->fs->max_bounds.width; + mesg_info->char_height = mesg_info->fs->max_bounds.ascent + + mesg_info->fs->max_bounds.descent; + mesg_info->char_ascent = mesg_info->fs->max_bounds.ascent; + mesg_info->char_lbearing = -mesg_info->fs->min_bounds.lbearing; + + get_gc(wp->w, mesg_info); + + wp->pixel_height = ((int)iflags.msg_history) * mesg_info->char_height; + + /* If a variable spaced font, only use 2/3 of the default size */ + if (mesg_info->fs->min_bounds.width != mesg_info->fs->max_bounds.width) { + wp->pixel_width = ((2*DEFAULT_MESSAGE_WIDTH)/3) * + mesg_info->fs->max_bounds.width; + } else + wp->pixel_width = (DEFAULT_MESSAGE_WIDTH * + mesg_info->fs->max_bounds.width); + + /* Set the new width and height. */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++; + XtSetValues(wp->w, args, num_args); + + /* make sure viewport height makes sense before realizing it */ + num_args = 0; + mesg_info->viewport_height = + appResources.message_lines * mesg_info->char_height; + XtSetArg(args[num_args], XtNheight, mesg_info->viewport_height);num_args++; + XtSetValues(viewport, args, num_args); + + XtAddCallback(wp->w, XtNresizeCallback, mesg_resized, (XtPointer) 0); + + /* + * If we have created our own popup, then realize it so that the + * viewport is also realized. + */ + if (create_popup) { + XtRealizeWidget(wp->popup); + XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup), + &wm_delete_window, 1); + } +} + + +void +destroy_message_window(wp) + struct xwindow *wp; +{ + if (wp->popup) { + nh_XtPopdown(wp->popup); + if (!wp->keep_window) + XtDestroyWidget(wp->popup), wp->popup = (Widget)0; + } + if (wp->mesg_information) { + set_circle_buf(wp->mesg_information, 0); /* free buffer list */ + free((genericptr_t)wp->mesg_information), wp->mesg_information = 0; + } + if (wp->keep_window) + XtRemoveCallback(wp->w, XtNexposeCallback, mesg_exposed, (XtPointer)0); + else + wp->type = NHW_NONE; +} + + +/* Redraw message window if new lines have been added. */ +void +display_message_window(wp) + struct xwindow *wp; +{ + if (wp->mesg_information->dirty) redraw_message_window(wp); +} + + +/* + * Append a line of text to the message window. Split the line if the + * rendering of the text is too long for the window. + */ +void +append_message(wp, str) + struct xwindow *wp; + const char *str; +{ + char *mark, *remainder, buf[BUFSZ]; + + if (!str) return; + + Strcpy(buf, str); /* we might mark it up */ + + remainder = buf; + do { + mark = remainder; + remainder = split(mark, wp->mesg_information->fs, wp->pixel_width); + add_line(wp->mesg_information, mark); + } while (remainder); +} + +/* private functions ======================================================= */ + +/* + * Return the element in the circular linked list just before the given + * element. + */ +static struct line_element * +get_previous(mark) + struct line_element *mark; +{ + struct line_element *curr; + + if (!mark) return (struct line_element *) 0; + + for (curr = mark; curr->next != mark; curr = curr->next) + ; + return curr; +} + + +/* + * Set the information buffer size to count lines. We do this by creating + * a circular linked list of elements, each of which represents a line of + * text. New buffers are created as needed, old ones are freed if they + * are no longer used. + */ +static void +set_circle_buf(mesg_info, count) + struct mesg_info_t *mesg_info; + int count; +{ + int i; + struct line_element *tail, *curr, *head; + + if (count < 0) panic("set_circle_buf: bad count [= %d]", count); + if (count == mesg_info->num_lines) return; /* no change in size */ + + if (count < mesg_info->num_lines) { + /* + * Toss num_lines - count line entries from our circular list. + * + * We lose lines from the front (top) of the list. We _know_ + * the list is non_empty. + */ + tail = get_previous(mesg_info->head); + for (i = mesg_info->num_lines - count; i > 0; i--) { + curr = mesg_info->head; + mesg_info->head = curr->next; + if (curr->line) free((genericptr_t)curr->line); + free((genericptr_t)curr); + } + if (count == 0) { + /* make sure we don't have a dangling pointer */ + mesg_info->head = (struct line_element *) 0; + } else { + tail->next = mesg_info->head; /* link the tail to the head */ + } + } else { + /* + * Add count - num_lines blank lines to the head of the list. + * + * Create a separate list, keeping track of the tail. + */ + for (head = tail = 0, i = 0; i < count - mesg_info->num_lines; i++) { + curr = (struct line_element *) alloc(sizeof(struct line_element)); + curr->line = 0; + curr->buf_length = 0; + curr->str_length = 0; + if (tail) { + tail->next = curr; + tail = curr; + } else { + head = tail = curr; + } + } + /* + * Complete the circle by making the new tail point to the old head + * and the old tail point to the new head. If our line count was + * zero, then make the new list circular. + */ + if (mesg_info->num_lines) { + curr = get_previous(mesg_info->head);/* get end of old list */ + + tail->next = mesg_info->head; /* new tail -> old head */ + curr->next = head; /* old tail -> new head */ + } else { + tail->next = head; + } + mesg_info->head = head; + } + + mesg_info->num_lines = count; + /* Erase the line on a resize. */ + mesg_info->last_pause = (struct line_element *) 0; +} + + +/* + * Make sure the given string is shorter than the given pixel width. If + * not, back up from the end by words until we find a place to split. + */ +static char * +split(s, fs, pixel_width) + char *s; + XFontStruct *fs; /* Font for the window. */ + Dimension pixel_width; +{ + char save, *end, *remainder; + + save = '\0'; + remainder = 0; + end = eos(s); /* point to null at end of string */ + + /* assume that if end == s, XXXXXX returns 0) */ + while ((Dimension) XTextWidth(fs, s, (int) strlen(s)) > pixel_width) { + *end-- = save; + while (*end != ' ') { + if (end == s) panic("split: eos!"); + --end; + } + save = *end; + *end = '\0'; + remainder = end + 1; + } + return remainder; +} + +/* + * Add a line of text to the window. The first line in the curcular list + * becomes the last. So all we have to do is copy the new line over the + * old one. If the line buffer is too small, then allocate a new, larger + * one. + */ +static void +add_line(mesg_info, s) + struct mesg_info_t *mesg_info; + const char *s; +{ + register struct line_element *curr = mesg_info->head; + register int new_line_length = strlen(s); + + if (new_line_length + 1 > curr->buf_length) { + if (curr->line) free(curr->line); /* free old line */ + + curr->buf_length = new_line_length + 1; + curr->line = (char *) alloc((unsigned)curr->buf_length); + } + + Strcpy(curr->line, s); /* copy info */ + curr->str_length = new_line_length; /* save string length */ + + mesg_info->head = mesg_info->head->next; /* move head to next line */ + mesg_info->dirty = True; /* we have undrawn lines */ +} + + +/* + * Save a position in the text buffer so we can draw a line to seperate + * text from the last time this function was called. + * + * Save the head position, since it is the line "after" the last displayed + * line in the message window. The window redraw routine will draw a + * line above this saved pointer. + */ +void +set_last_pause(wp) + struct xwindow *wp; +{ + register struct mesg_info_t *mesg_info = wp->mesg_information; + +#ifdef ERASE_LINE + /* + * If we've erased the pause line and haven't added any new lines, + * don't try to erase the line again. + */ + if (!mesg_info->last_pause + && mesg_info->last_pause_head == mesg_info->head) + return; + + if (mesg_info->last_pause == mesg_info->head) { + /* No new messages in last turn. Redraw window to erase line. */ + mesg_info->last_pause = (struct line_element *) 0; + mesg_info->last_pause_head = mesg_info->head; + redraw_message_window(wp); + } else { +#endif + mesg_info->last_pause = mesg_info->head; +#ifdef ERASE_LINE + } +#endif +} + + +static void +redraw_message_window(wp) + struct xwindow *wp; +{ + struct mesg_info_t *mesg_info = wp->mesg_information; + register struct line_element *curr; + register int row, y_base; + + /* + * Do this the cheap and easy way. Clear the window and just redraw + * the whole thing. + * + * This could be done more effecently with one call to XDrawText() instead + * of many calls to XDrawString(). Maybe later. + * + * Only need to clear if window has new text. + */ + if (mesg_info->dirty) { + XClearWindow(XtDisplay(wp->w), XtWindow(wp->w)); + mesg_info->line_here = mesg_info->last_pause; + } + + /* For now, just update the whole shootn' match. */ + for (y_base = row = 0, curr = mesg_info->head; + row < mesg_info->num_lines; + row++, y_base += mesg_info->char_height, curr = curr->next) { + + XDrawString(XtDisplay(wp->w), XtWindow(wp->w), + mesg_info->gc, + mesg_info->char_lbearing, + mesg_info->char_ascent + y_base, + curr->line, + curr->str_length); + /* + * This draws a line at the _top_ of the line of text pointed to by + * mesg_info->last_pause. + */ + if (appResources.message_line && curr == mesg_info->line_here) { + XDrawLine(XtDisplay(wp->w), XtWindow(wp->w), + mesg_info->gc, + 0, y_base, wp->pixel_width, y_base); + } + } + + mesg_info->dirty = False; +} + + +/* + * Check the size of the viewport. If it has shrunk, then we want to + * move the vertical slider to the bottom. + */ +static void +mesg_check_size_change(wp) + struct xwindow *wp; +{ + struct mesg_info_t *mesg_info = wp->mesg_information; + Arg arg[2]; + Dimension new_width, new_height; + Widget viewport; + + viewport = XtParent(wp->w); + + XtSetArg(arg[0], XtNwidth, &new_width); + XtSetArg(arg[1], XtNheight, &new_height); + XtGetValues(viewport, arg, TWO); + + /* Only move slider to bottom if new size is smaller. */ + if (new_width < mesg_info->viewport_width + || new_height < mesg_info->viewport_height) { + set_message_slider(wp); + } + + mesg_info->viewport_width = new_width; + mesg_info->viewport_height = new_height; +} + + +/* Event handler for message window expose events. */ +/*ARGSUSED*/ +static void +mesg_exposed(w, client_data, widget_data) + Widget w; + XtPointer client_data; /* unused */ + XtPointer widget_data; /* expose event from Window widget */ +{ + XExposeEvent *event = (XExposeEvent *) widget_data; + + if (XtIsRealized(w) && event->count == 0) { + struct xwindow *wp; + Display *dpy; + Window win; + XEvent evt; + + /* + * Drain all pending expose events for the message window; + * we'll redraw the whole thing at once. + */ + dpy = XtDisplay(w); + win = XtWindow(w); + while (XCheckTypedWindowEvent(dpy, win, Expose, &evt)) continue; + + wp = find_widget(w); + if (wp->keep_window && !wp->mesg_information) return; + mesg_check_size_change(wp); + redraw_message_window(wp); + } +} + + +static void +get_gc(w, mesg_info) + Widget w; + struct mesg_info_t *mesg_info; +{ + XGCValues values; + XtGCMask mask = GCFunction | GCForeground | GCBackground | GCFont; + Pixel fgpixel, bgpixel; + Arg arg[2]; + + XtSetArg(arg[0], XtNforeground, &fgpixel); + XtSetArg(arg[1], XtNbackground, &bgpixel); + XtGetValues(w, arg, TWO); + + values.foreground = fgpixel; + values.background = bgpixel; + values.function = GXcopy; + values.font = WindowFont(w); + mesg_info->gc = XtGetGC(w, mask, &values); +} + +/* + * Handle resizes on a message window. Correct saved pixel height and width. + * Adjust circle buffer to accomidate the new size. + * + * Problem: If the resize decreases the width of the window such that + * some lines are now longer than the window, they will be cut off by + * X itself. All new lines will be split to the new size, but the ends + * of the old ones will not be seen again unless the window is lengthened. + * I don't deal with this problem because it isn't worth the trouble. + */ +/* ARGSUSED */ +static void +mesg_resized(w, client_data, call_data) + Widget w; + XtPointer call_data, client_data; +{ + Arg args[4]; + Cardinal num_args; + Dimension pixel_width, pixel_height; + struct xwindow *wp; +#ifdef VERBOSE + int old_lines; + + old_lines = wp->mesg_information->num_lines;; +#endif + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, &pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, &pixel_height); num_args++; + XtGetValues(w, args, num_args); + + wp = find_widget(w); + wp->pixel_width = pixel_width; + wp->pixel_height = pixel_height; + + set_circle_buf(wp->mesg_information, + (int) pixel_height / wp->mesg_information->char_height); + +#ifdef VERBOSE + printf("Message resize. Pixel: width = %d, height = %d; Lines: old = %d, new = %d\n", + pixel_width, + pixel_height, + old_lines, + wp->mesg_information->num_lines); +#endif +} + +/*winmesg.c*/ diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c new file mode 100644 index 0000000..4699c68 --- /dev/null +++ b/win/X11/winmisc.c @@ -0,0 +1,928 @@ +/* SCCS Id: @(#)winmisc.c 3.4 2000/05/21 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Misc. popup windows: player selection and extended commands. + * + * + Global functions: player_selection() and get_ext_cmd(). + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include /* for index() */ +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" +#include "func_tab.h" +#include "winX.h" + + +static Widget extended_command_popup = 0; +static Widget extended_command_form; +static Widget *extended_commands = 0; +static int extended_command_selected; /* index of the selected command; */ +static int ps_selected; /* index of selected role */ +#define PS_RANDOM (-50) +#define PS_QUIT (-75) +static const char ps_randchars[] = "*@"; +static const char ps_quitchars[] = "\033qQ"; + +#define EC_NCHARS 32 +static boolean ec_active = FALSE; +static int ec_nchars = 0; +static char ec_chars[EC_NCHARS]; +static Time ec_time; + +static const char extended_command_translations[] = + "#override\n\ + : ec_key()"; + +static const char player_select_translations[] = + "#override\n\ + : ps_key()"; +static const char race_select_translations[] = + "#override\n\ + : race_key()"; +static const char gend_select_translations[] = + "#override\n\ + : gend_key()"; +static const char algn_select_translations[] = + "#override\n\ + : algn_key()"; + +static void FDECL(popup_delete, (Widget, XEvent*, String*, Cardinal*)); +static void NDECL(ec_dismiss); +static Widget FDECL(make_menu, (const char *,const char *,const char *, + const char *,XtCallbackProc, + const char *,XtCallbackProc, + int,const char **, Widget **, + XtCallbackProc,Widget *)); +static void NDECL(init_extended_commands_popup); +static void FDECL(ps_quit, (Widget,XtPointer,XtPointer)); +static void FDECL(ps_random, (Widget,XtPointer,XtPointer)); +static void FDECL(ps_select, (Widget,XtPointer,XtPointer)); + + +/* Player Selection -------------------------------------------------------- */ +/* ARGSUSED */ +static void +ps_quit(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = PS_QUIT; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +ps_random(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = PS_RANDOM; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +ps_select(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = (int) client_data; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +void +ps_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + char rolechars[QBUFSZ]; + int i; + + (void)memset(rolechars, '\0', sizeof rolechars); /* for index() */ + for (i = 0; roles[i].name.m; ++i) { + ch = lowc(*roles[i].name.m); + /* if (flags.female && roles[i].name.f) ch = lowc(*roles[i].name.f); */ + /* this supports at most two roles with the same first letter */ + if (index(rolechars, ch)) ch = highc(ch); + rolechars[i] = ch; + } + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(rolechars, ch); + if (!mark) mark = index(rolechars, lowc(ch)); + if (!mark) mark = index(rolechars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(ps_quitchars, ch)) + ps_selected = PS_QUIT; + else { + X11_nhbell(); /* no such class */ + return; + } + } else + ps_selected = (int)(mark - rolechars); + exit_x_event = TRUE; +} + +/* ARGSUSED */ +void +race_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + char racechars[QBUFSZ]; + int i; + + (void)memset(racechars, '\0', sizeof racechars); /* for index() */ + for (i = 0; races[i].noun; ++i) { + ch = lowc(*races[i].noun); + /* this supports at most two races with the same first letter */ + if (index(racechars, ch)) ch = highc(ch); + racechars[i] = ch; + } + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(racechars, ch); + if (!mark) mark = index(racechars, lowc(ch)); + if (!mark) mark = index(racechars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(ps_quitchars, ch)) + ps_selected = PS_QUIT; + else { + X11_nhbell(); /* no such race */ + return; + } + } else + ps_selected = (int)(mark - racechars); + exit_x_event = TRUE; +} + +/* ARGSUSED */ +void +gend_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + static char gendchars[] = "mf"; + + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(gendchars, ch); + if (!mark) mark = index(gendchars, lowc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(ps_quitchars, ch)) + ps_selected = PS_QUIT; + else { + X11_nhbell(); /* no such gender */ + return; + } + } else + ps_selected = (int)(mark - gendchars); + exit_x_event = TRUE; +} + +/* ARGSUSED */ +void +algn_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + static char algnchars[] = "LNC"; + + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(algnchars, ch); + if (!mark) mark = index(algnchars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(ps_quitchars, ch)) + ps_selected = PS_QUIT; + else { + X11_nhbell(); /* no such alignment */ + return; + } + } else + ps_selected = (int)(mark - algnchars); + exit_x_event = TRUE; +} + +/* Global functions ========================================================= */ +void +X11_player_selection() +{ + int num_roles, num_races, num_gends, num_algns, + i, availcount, availindex; + Widget popup, player_form; + const char **choices; + char qbuf[QBUFSZ], plbuf[QBUFSZ]; + + /* avoid unnecessary prompts further down */ + rigid_role_checks(); + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + while (flags.initrole < 0) { + if (flags.initrole == ROLE_RANDOM || flags.randomall) { + flags.initrole = pick_role(flags.initrace, + flags.initgend, flags.initalign, PICK_RANDOM); + break; + } + + /* select a role */ + for (num_roles = 0; roles[num_roles].name.m; ++num_roles) continue; + choices = (const char **)alloc(sizeof(char *) * num_roles); + for (;;) { + availcount = 0; + for (i = 0; i < num_roles; i++) { + choices[i] = 0; + if (ok_role(i, flags.initrace, + flags.initgend, flags.initalign)) { + choices[i] = roles[i].name.m; + if (flags.initgend >= 0 && flags.female && roles[i].name.f) + choices[i] = roles[i].name.f; + ++availcount; + } + } + if (availcount > 0) break; + else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ + else if (flags.initgend >= 0) flags.initgend = -1; + else if (flags.initrace >= 0) flags.initrace = -1; + else panic("no available ROLE+race+gender+alignment combinations"); + } + Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf)); + popup = make_menu("player_selection", qbuf, + player_select_translations, + "quit", ps_quit, + "random", ps_random, + num_roles, choices, (Widget **)0, ps_select, &player_form); + + ps_selected = -1; + positionpopup(popup, FALSE); + nh_XtPopup(popup, (int)XtGrabExclusive, player_form); + + /* The callbacks will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + nh_XtPopdown(popup); + XtDestroyWidget(popup); + free((genericptr_t)choices), choices = 0; + + if (ps_selected == PS_QUIT) { + clearlocks(); + X11_exit_nhwindows((char *)0); + terminate(0); + } else if (ps_selected == PS_RANDOM) { + flags.initrole = ROLE_RANDOM; + } else if (ps_selected < 0 || ps_selected >= num_roles) { + panic("player_selection: bad role select value %d", ps_selected); + } else { + flags.initrole = ps_selected; + } + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + while (!validrace(flags.initrole, flags.initrace)) { + if (flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, + flags.initgend, flags.initalign, PICK_RANDOM); + break; + } + /* select a race */ + for (num_races = 0; races[num_races].noun; ++num_races) continue; + choices = (const char **)alloc(sizeof(char *) * num_races); + for (;;) { + availcount = availindex = 0; + for (i = 0; i < num_races; i++) { + choices[i] = 0; + if (ok_race(flags.initrole, i, + flags.initgend, flags.initalign)) { + choices[i] = races[i].noun; + ++availcount; + availindex = i; /* used iff only one */ + } + } + if (availcount > 0) break; + else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ + else if (flags.initgend >= 0) flags.initgend = -1; + else panic("no available role+RACE+gender+alignment combinations"); + } + + if (availcount == 1) { + flags.initrace = availindex; + free((genericptr_t)choices), choices = 0; + } else { + Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf)); + popup = make_menu("race_selection", qbuf, + race_select_translations, + "quit", ps_quit, + "random", ps_random, + num_races, choices, (Widget **)0, + ps_select, &player_form); + + ps_selected = -1; + positionpopup(popup, FALSE); + nh_XtPopup(popup, (int)XtGrabExclusive, player_form); + + /* The callbacks will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + nh_XtPopdown(popup); + XtDestroyWidget(popup); + free((genericptr_t)choices), choices = 0; + + if (ps_selected == PS_QUIT) { + clearlocks(); + X11_exit_nhwindows((char *)0); + terminate(0); + } else if (ps_selected == PS_RANDOM) { + flags.initrace = ROLE_RANDOM; + } else if (ps_selected < 0 || ps_selected >= num_races) { + panic("player_selection: bad race select value %d", ps_selected); + } else { + flags.initrace = ps_selected; + } + } /* more than one race choice available */ + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + while (!validgend(flags.initrole, flags.initrace, flags.initgend)) { + if (flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + break; + } + /* select a gender */ + num_gends = 2; /* genders[2] isn't allowed */ + choices = (const char **)alloc(sizeof(char *) * num_gends); + for (;;) { + availcount = availindex = 0; + for (i = 0; i < num_gends; i++) { + choices[i] = 0; + if (ok_gend(flags.initrole, flags.initrace, + i, flags.initalign)) { + choices[i] = genders[i].adj; + ++availcount; + availindex = i; /* used iff only one */ + } + } + if (availcount > 0) break; + else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ + else panic("no available role+race+GENDER+alignment combinations"); + } + + if (availcount == 1) { + flags.initgend = availindex; + free((genericptr_t)choices), choices = 0; + } else { + Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf)); + popup = make_menu("gender_selection", qbuf, + gend_select_translations, + "quit", ps_quit, + "random", ps_random, + num_gends, choices, (Widget **)0, + ps_select, &player_form); + + ps_selected = -1; + positionpopup(popup, FALSE); + nh_XtPopup(popup, (int)XtGrabExclusive, player_form); + + /* The callbacks will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + nh_XtPopdown(popup); + XtDestroyWidget(popup); + free((genericptr_t)choices), choices = 0; + + if (ps_selected == PS_QUIT) { + clearlocks(); + X11_exit_nhwindows((char *)0); + terminate(0); + } else if (ps_selected == PS_RANDOM) { + flags.initgend = ROLE_RANDOM; + } else if (ps_selected < 0 || ps_selected >= num_gends) { + panic("player_selection: bad gender select value %d", ps_selected); + } else { + flags.initgend = ps_selected; + } + } /* more than one gender choice available */ + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + while (!validalign(flags.initrole, flags.initrace, flags.initalign)) { + if (flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + break; + } + /* select an alignment */ + num_algns = 3; /* aligns[3] isn't allowed */ + choices = (const char **)alloc(sizeof(char *) * num_algns); + for (;;) { + availcount = availindex = 0; + for (i = 0; i < num_algns; i++) { + choices[i] = 0; + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + choices[i] = aligns[i].adj; + ++availcount; + availindex = i; /* used iff only one */ + } + } + if (availcount > 0) break; + else panic("no available role+race+gender+ALIGNMENT combinations"); + } + + if (availcount == 1) { + flags.initalign = availindex; + free((genericptr_t)choices), choices = 0; + } else { + Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf)); + popup = make_menu("alignment_selection", qbuf, + algn_select_translations, + "quit", ps_quit, + "random", ps_random, + num_algns, choices, (Widget **)0, + ps_select, &player_form); + + ps_selected = -1; + positionpopup(popup, FALSE); + nh_XtPopup(popup, (int)XtGrabExclusive, player_form); + + /* The callbacks will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + nh_XtPopdown(popup); + XtDestroyWidget(popup); + free((genericptr_t)choices), choices = 0; + + if (ps_selected == PS_QUIT) { + clearlocks(); + X11_exit_nhwindows((char *)0); + terminate(0); + } else if (ps_selected == PS_RANDOM) { + flags.initalign = ROLE_RANDOM; + } else if (ps_selected < 0 || ps_selected >= num_algns) { + panic("player_selection: bad alignment select value %d", ps_selected); + } else { + flags.initalign = ps_selected; + } + } /* more than one alignment choice available */ + } +} + + +int +X11_get_ext_cmd() +{ + static Boolean initialized = False; + + if (!initialized) { + init_extended_commands_popup(); + initialized = True; + } + + extended_command_selected = -1; /* reset selected value */ + + positionpopup(extended_command_popup, FALSE); /* center on cursor */ + nh_XtPopup(extended_command_popup, (int)XtGrabExclusive, + extended_command_form); + + /* The callbacks will enable the event loop exit. */ + (void) x_event(EXIT_ON_EXIT); + + return extended_command_selected; +} + +/* End global functions ===================================================== */ + +/* Extended Command -------------------------------------------------------- */ +/* ARGSUSED */ +static void +extend_select(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + int selected = (int) client_data; + + if (extended_command_selected != selected) { + /* visibly deselect old one */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + + /* select new one */ + swap_fg_bg(extended_commands[selected]); + extended_command_selected = selected; + } + + nh_XtPopdown(extended_command_popup); + /* reset colors while popped down */ + swap_fg_bg(extended_commands[extended_command_selected]); + ec_active = FALSE; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +extend_dismiss(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ec_dismiss(); +} + +/* ARGSUSED */ +static void +extend_help(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + /* We might need to make it known that we already have one listed. */ + (void) doextlist(); +} + +/* ARGSUSED */ +void +ec_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + if (w == extended_command_popup) { + ec_dismiss(); + } else { + popup_delete(w, event, params, num_params); + } +} + +/* ARGSUSED */ +static void +popup_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + ps_selected = PS_QUIT; + nh_XtPopdown(w); + exit_x_event = TRUE; /* leave event loop */ +} + +static void +ec_dismiss() +{ + /* unselect while still visible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = -1; /* dismiss */ + nh_XtPopdown(extended_command_popup); + ec_active = FALSE; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +void +ec_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch; + int i; + XKeyEvent *xkey = (XKeyEvent *) event; + + ch = key_event_to_char(xkey); + + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } else if (index("\033\n\r", ch)) { + if (ch == '\033') { + /* unselect while still visible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = -1; /* dismiss */ + } + + nh_XtPopdown(extended_command_popup); + /* unselect while invisible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + + exit_x_event = TRUE; /* leave event loop */ + ec_active = FALSE; + return; + } + + /* too much time has elapsed */ + if ((xkey->time - ec_time) > 500) + ec_active = FALSE; + + if (!ec_active) { + ec_nchars = 0; + ec_active = TRUE; + } + + ec_time = xkey->time; + ec_chars[ec_nchars++] = ch; + if (ec_nchars >= EC_NCHARS) + ec_nchars = EC_NCHARS-1; /* don't overflow */ + + for (i = 0; extcmdlist[i].ef_txt; i++) { + if (extcmdlist[i].ef_txt[0] == '?') continue; + + if (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)) { + if (extended_command_selected != i) { + /* I should use set() and unset() actions, but how do */ + /* I send the an action to the widget? */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = i; + swap_fg_bg(extended_commands[extended_command_selected]); + } + break; + } + } +} + +/* + * Use our own home-brewed version menu because simpleMenu is designed to + * be used from a menubox. + */ +static void +init_extended_commands_popup() +{ + int i, num_commands; + const char **command_list; + + /* count commands */ + for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++) + ; /* do nothing */ + + /* If the last entry is "help", don't use it. */ + if (strcmp(extcmdlist[num_commands-1].ef_txt, "?") == 0) + --num_commands; + + command_list = + (const char **) alloc((unsigned)num_commands * sizeof(char *)); + + for (i = 0; i < num_commands; i++) + command_list[i] = extcmdlist[i].ef_txt; + + extended_command_popup = make_menu("extended_commands", + "Extended Commands", + extended_command_translations, + "dismiss", extend_dismiss, + "help", extend_help, + num_commands, command_list, &extended_commands, + extend_select, &extended_command_form); + + free((char *)command_list); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Create a popup widget of the following form: + * + * popup_label + * ----------- ------------ + * |left_name| |right_name| + * ----------- ------------ + * ------------------------ + * | name1 | + * ------------------------ + * ------------------------ + * | name2 | + * ------------------------ + * . + * . + * ------------------------ + * | nameN | + * ------------------------ + */ +static Widget +make_menu(popup_name, popup_label, popup_translations, + left_name, left_callback, + right_name, right_callback, + num_names, widget_names, command_widgets, name_callback, formp) + const char *popup_name; + const char *popup_label; + const char *popup_translations; + const char *left_name; + XtCallbackProc left_callback; + const char *right_name; + XtCallbackProc right_callback; + int num_names; + const char **widget_names; /* return array of command widgets */ + Widget **command_widgets; + XtCallbackProc name_callback; + Widget *formp; /* return */ +{ + Widget popup, form, label, above, left, right; + Widget *commands, *curr; + int i; + Arg args[8]; + Cardinal num_args; + Dimension width, max_width; + int distance, skip; + + + commands = (Widget *) alloc((unsigned)num_names * sizeof(Widget)); + + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + + popup = XtCreatePopupShell(popup_name, + transientShellWidgetClass, + toplevel, args, num_args); + XtOverrideTranslations(popup, + XtParseTranslationTable("WM_PROTOCOLS: ec_delete()")); + + num_args = 0; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(popup_translations)); num_args++; + *formp = form = XtCreateManagedWidget("menuform", + formWidgetClass, + popup, + args, num_args); + + /* Get the default distance between objects in the form widget. */ + num_args = 0; + XtSetArg(args[num_args], XtNdefaultDistance, &distance); num_args++; + XtGetValues(form, args, num_args); + + /* + * Create the label. + */ + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + label = XtCreateManagedWidget(popup_label, + labelWidgetClass, + form, + args, num_args); + + /* + * Create the left button. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; +/* + XtSetArg(args[num_args], XtNshapeStyle, + XmuShapeRoundedRectangle); num_args++; +*/ + left = XtCreateManagedWidget(left_name, + commandWidgetClass, + form, + args, num_args); + XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0); + skip = 3*distance; /* triple the spacing */ + if(!skip) skip = 3; + + /* + * Create right button. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromHoriz, left); num_args++; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; +/* + XtSetArg(args[num_args], XtNshapeStyle, + XmuShapeRoundedRectangle); num_args++; +*/ + right = XtCreateManagedWidget(right_name, + commandWidgetClass, + form, + args, num_args); + XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0); + + XtInstallAccelerators(form, left); + XtInstallAccelerators(form, right); + + /* + * Create and place the command widgets. + */ + for (i = 0, above = left, curr = commands; i < num_names; i++) { + if (!widget_names[i]) continue; + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, above); num_args++; + if (above == left) { + /* if first, we are farther apart */ + XtSetArg(args[num_args], XtNvertDistance, skip); num_args++; + } + + *curr = XtCreateManagedWidget(widget_names[i], + commandWidgetClass, + form, + args, num_args); + XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) i); + above = *curr++; + } + + /* + * Now find the largest width. Start with the width dismiss + help + * buttons, since they are adjacent. + */ + XtSetArg(args[0], XtNwidth, &max_width); + XtGetValues(left, args, ONE); + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(right, args, ONE); + max_width = max_width + width + distance; + + /* Next, the title. */ + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(label, args, ONE); + if (width > max_width) max_width = width; + + /* Finally, the commands. */ + for (i = 0, curr = commands; i < num_names; i++) { + if (!widget_names[i]) continue; + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(*curr, args, ONE); + if (width > max_width) max_width = width; + curr++; + } + + /* + * Finally, set all of the single line widgets to the largest width. + */ + XtSetArg(args[0], XtNwidth, max_width); + XtSetValues(label, args, ONE); + + for (i = 0, curr = commands; i < num_names; i++) { + if (!widget_names[i]) continue; + XtSetArg(args[0], XtNwidth, max_width); + XtSetValues(*curr, args, ONE); + curr++; + } + + if (command_widgets) + *command_widgets = commands; + else + free((char *) commands); + + XtRealizeWidget(popup); + XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1); + + return popup; +} diff --git a/win/X11/winstat.c b/win/X11/winstat.c new file mode 100644 index 0000000..d9bd7ad --- /dev/null +++ b/win/X11/winstat.c @@ -0,0 +1,991 @@ +/* SCCS Id: @(#)winstat.c 3.4 1996/04/05 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Status window routines. This file supports both the "traditional" + * tty status display and a "fancy" status display. A tty status is + * made if a popup window is requested, otherewise a fancy status is + * made. This code assumes that only one fancy status will ever be made. + * Currently, only one status window (of any type) is _ever_ made. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" +#include "winX.h" + +extern const char *hu_stat[]; /* from eat.c */ +extern const char *enc_stat[]; /* from botl.c */ + +static void FDECL(update_fancy_status, (struct xwindow *)); +static Widget FDECL(create_fancy_status, (Widget,Widget)); +static void FDECL(destroy_fancy_status, (struct xwindow *)); + +void +create_status_window(wp, create_popup, parent) + struct xwindow *wp; /* window pointer */ + boolean create_popup; + Widget parent; +{ + XFontStruct *fs; + Arg args[8]; + Cardinal num_args; + Position top_margin, bottom_margin, left_margin, right_margin; + + wp->type = NHW_STATUS; + + if (!create_popup) { + /* + * If we are not creating a popup, then we must be the "main" status + * window. + */ + if (!parent) + panic("create_status_window: no parent for fancy status"); + wp->status_information = 0; + wp->w = create_fancy_status(parent, (Widget) 0); + return; + } + + wp->status_information = + (struct status_info_t *) alloc(sizeof(struct status_info_t)); + + init_text_buffer(&wp->status_information->text); + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, False); num_args++; + XtSetArg(args[num_args], XtNinput, False); num_args++; + + wp->popup = parent = XtCreatePopupShell("status_popup", + topLevelShellWidgetClass, + toplevel, args, num_args); + /* + * If we're here, then this is an auxiliary status window. If we're + * cancelled via a delete window message, we should just pop down. + */ + + num_args = 0; + XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++; + XtSetArg(args[num_args], XtNscrollHorizontal, + XawtextScrollWhenNeeded); num_args++; + XtSetArg(args[num_args], XtNscrollVertical, + XawtextScrollWhenNeeded); num_args++; + + wp->w = XtCreateManagedWidget( + "status", /* name */ + asciiTextWidgetClass, + parent, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + /* + * Adjust the height and width of the message window so that it + * is two lines high and COLNO of the widest characters wide. + */ + + /* Get the font and margin information. */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &fs); num_args++; + XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++; + XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++; + XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++; + XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++; + XtGetValues(wp->w, args, num_args); + + wp->pixel_height = 2 * nhFontHeight(wp->w) + top_margin + bottom_margin; + wp->pixel_width = COLNO * fs->max_bounds.width + + left_margin + right_margin; + + /* Set the new width and height. */ + num_args = 0; + XtSetArg(args[num_args], XtNwidth, wp->pixel_width); num_args++; + XtSetArg(args[num_args], XtNheight, wp->pixel_height); num_args++; + XtSetValues(wp->w, args, num_args); +} + +void +destroy_status_window(wp) + struct xwindow *wp; +{ + /* If status_information is defined, then it a "text" status window. */ + if (wp->status_information) { + if (wp->popup) { + nh_XtPopdown(wp->popup); + if (!wp->keep_window) + XtDestroyWidget(wp->popup), wp->popup = (Widget)0; + } + free((genericptr_t)wp->status_information); + wp->status_information = 0; + } else { + destroy_fancy_status(wp); + } + if (!wp->keep_window) + wp->type = NHW_NONE; +} + + +/* + * This assumes several things: + * + Status has only 2 lines + * + That both lines are updated in succession in line order. + * + We didn't set stringInPlace on the widget. + */ +void +adjust_status(wp, str) + struct xwindow *wp; + const char *str; +{ + Arg args[2]; + Cardinal num_args; + + if (!wp->status_information) { + update_fancy_status(wp); + return; + } + + if (wp->cursy == 0) { + clear_text_buffer(&wp->status_information->text); + append_text_buffer(&wp->status_information->text, str, FALSE); + return; + } + append_text_buffer(&wp->status_information->text, str, FALSE); + + /* Set new buffer as text. */ + num_args = 0; + XtSetArg(args[num_args], XtNstring, wp->status_information->text.text); + num_args++; + XtSetValues(wp->w, args, num_args); +} + + +/* Fancy Status -------------------------------------------------------------*/ +static int hilight_time = 1; /* number of turns to hilight a changed value */ + +struct X_status_value { + char *name; /* text name */ + int type; /* status type */ + Widget w; /* widget of name/value pair */ + long last_value; /* value displayed */ + int turn_count; /* last time the value changed */ + boolean set; /* if hilighed */ + boolean after_init; /* don't hilight on first change (init) */ +}; + +/* valid type values */ +#define SV_VALUE 0 /* displays a label:value pair */ +#define SV_LABEL 1 /* displays a changable label */ +#define SV_NAME 2 /* displays an unchangeable name */ + +static void FDECL(hilight_label, (Widget)); +static void FDECL(update_val, (struct X_status_value *,long)); +static const char *FDECL(width_string, (int)); +static void FDECL(create_widget, (Widget,struct X_status_value *,int)); +static void FDECL(get_widths, (struct X_status_value *,int *,int *)); +static void FDECL(set_widths, (struct X_status_value *,int,int)); +static Widget FDECL(init_column, (char *,Widget,Widget,Widget,int *)); +static Widget FDECL(init_info_form, (Widget,Widget,Widget)); + +/* + * Form entry storage indices. + */ +#define F_STR 0 +#define F_DEX 1 +#define F_CON 2 +#define F_INT 3 +#define F_WIS 4 +#define F_CHA 5 + +#define F_NAME 6 +#define F_DLEVEL 7 +#define F_GOLD 8 +#define F_HP 9 +#define F_MAXHP 10 +#define F_POWER 11 +#define F_MAXPOWER 12 +#define F_AC 13 +#define F_LEVEL 14 +#define F_EXP 15 +#define F_ALIGN 16 +#define F_TIME 17 +#define F_SCORE 18 + +#define F_HUNGER 19 +#define F_CONFUSED 20 +#define F_SICK 21 +#define F_BLIND 22 +#define F_STUNNED 23 +#define F_HALLU 24 +#define F_ENCUMBER 25 + +#define NUM_STATS 26 + +/* + * Notes: + * + Alignment needs a different init value, because -1 is an alignment. + * + Armor Class is an schar, so 256 is out of range. + * + Blank value is 0 and should never change. + */ +static struct X_status_value shown_stats[NUM_STATS] = { + { "Strength", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 0*/ + { "Dexterity", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Constitution", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Intelligence", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Wisdom", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Charisma", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /* 5*/ + + { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* name */ + { "", SV_LABEL, (Widget) 0, -1, 0, FALSE, FALSE }, /* dlvl */ + { "Gold", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Hit Points", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Max HP", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*10*/ + { "Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Max Power", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Armor Class", SV_VALUE, (Widget) 0,256, 0, FALSE, FALSE }, + { "Level", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Experience", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, /*15*/ + { "Alignment", SV_VALUE, (Widget) 0, -2, 0, FALSE, FALSE }, + { "Time", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + { "Score", SV_VALUE, (Widget) 0, -1, 0, FALSE, FALSE }, + + { "", SV_NAME, (Widget) 0, -1, 0, FALSE, TRUE }, /* hunger*/ + { "Confused", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*20*/ + { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /* sick */ + { "Blind", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Stunned", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "Hallucinating", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, + { "", SV_NAME, (Widget) 0, 0, 0, FALSE, TRUE }, /*encumbr*/ +}; + + +/* + * Set all widget values to a null string. This is used after all spacings + * have been calculated so that when the window is popped up we don't get all + * kinds of funny values being displayed. + */ +void +null_out_status() +{ + int i; + struct X_status_value *sv; + Arg args[1]; + + for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) { + switch (sv->type) { + case SV_VALUE: + set_value(sv->w, ""); + break; + + case SV_LABEL: + case SV_NAME: + XtSetArg(args[0], XtNlabel, ""); + XtSetValues(sv->w, args, ONE); + break; + + default: + impossible("null_out_status: unknown type %d\n", sv->type); + break; + } + } +} + +/* This is almost an exact duplicate of hilight_value() */ +static void +hilight_label(w) + Widget w; /* label widget */ +{ + Arg args[2]; + Pixel fg, bg; + + XtSetArg(args[0], XtNforeground, &fg); + XtSetArg(args[1], XtNbackground, &bg); + XtGetValues(w, args, TWO); + + XtSetArg(args[0], XtNforeground, bg); + XtSetArg(args[1], XtNbackground, fg); + XtSetValues(w, args, TWO); +} + + +static void +update_val(attr_rec, new_value) + struct X_status_value *attr_rec; + long new_value; +{ + char buf[BUFSZ]; + Arg args[4]; + + if (attr_rec->type == SV_LABEL) { + + if (attr_rec == &shown_stats[F_NAME]) { + + Strcpy(buf, plname); + if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a'; + Strcat(buf, " the "); + if (u.mtimedone) { + char mname[BUFSZ]; + int k = 0; + + Strcpy(mname, mons[u.umonnum].mname); + while(mname[k] != 0) { + if ((k == 0 || (k > 0 && mname[k-1] == ' ')) && + 'a' <= mname[k] && mname[k] <= 'z') + mname[k] += 'A' - 'a'; + k++; + } + Strcat(buf, mname); + } else + Strcat(buf, rank_of(u.ulevel, pl_character[0], flags.female)); + + } else if (attr_rec == &shown_stats[F_DLEVEL]) { + if (!describe_level(buf)) { + Strcpy(buf, dungeons[u.uz.dnum].dname); + Sprintf(eos(buf), ", level %d", depth(&u.uz)); + } + } else { + impossible("update_val: unknown label type \"%s\"", + attr_rec->name); + return; + } + + if (strcmp(buf, attr_rec->name) == 0) return; /* same */ + + /* Set the label. */ + Strcpy(attr_rec->name, buf); + XtSetArg(args[0], XtNlabel, buf); + XtSetValues(attr_rec->w, args, ONE); + + } else if (attr_rec->type == SV_NAME) { + + if (attr_rec->last_value == new_value) return; /* no change */ + + attr_rec->last_value = new_value; + + /* special cases: hunger, encumbrance, sickness */ + if (attr_rec == &shown_stats[F_HUNGER]) { + XtSetArg(args[0], XtNlabel, hu_stat[new_value]); + } else if (attr_rec == &shown_stats[F_ENCUMBER]) { + XtSetArg(args[0], XtNlabel, enc_stat[new_value]); + } else if (attr_rec == &shown_stats[F_SICK]) { + buf[0] = 0; + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) + Strcat(buf, "FoodPois"); + if (u.usick_type & SICK_NONVOMITABLE) { + if (u.usick_type & SICK_VOMITABLE) + Strcat(buf, " "); + Strcat(buf, "Ill"); + } + } + XtSetArg(args[0], XtNlabel, buf); + } else if (new_value) { + XtSetArg(args[0], XtNlabel, attr_rec->name); + } else { + XtSetArg(args[0], XtNlabel, ""); + } + XtSetValues(attr_rec->w, args, ONE); + + } else { /* a value pair */ + boolean force_update = FALSE; + + /* special case: time can be enabled & disabled */ + if (attr_rec == &shown_stats[F_TIME]) { + static boolean flagtime = TRUE; + + if(flags.time && !flagtime) { + set_name(attr_rec->w, shown_stats[F_TIME].name); + force_update = TRUE; + flagtime = flags.time; + } else if(!flags.time && flagtime) { + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + flagtime = flags.time; + } + if(!flagtime) return; + } + + /* special case: exp can be enabled & disabled */ + else if (attr_rec == &shown_stats[F_EXP]) { + static boolean flagexp = TRUE; +#ifdef EXP_ON_BOTL + + if (flags.showexp && !flagexp) { + set_name(attr_rec->w, shown_stats[F_EXP].name); + force_update = TRUE; + flagexp = flags.showexp; + } else if(!flags.showexp && flagexp) { + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + flagexp = flags.showexp; + } + if (!flagexp) return; +#else + if (flagexp) { + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + flagexp = FALSE; + } + return; /* don't show it at all */ +#endif + } + + /* special case: score can be enabled & disabled */ + else if (attr_rec == &shown_stats[F_SCORE]) { + static boolean flagscore = TRUE; +#ifdef SCORE_ON_BOTL + + if(flags.showscore && !flagscore) { + set_name(attr_rec->w, shown_stats[F_SCORE].name); + force_update = TRUE; + flagscore = flags.showscore; + } else if(!flags.showscore && flagscore) { + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + flagscore = flags.showscore; + } + if(!flagscore) return; +#else + if (flagscore) { + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + flagscore = FALSE; + } + return; +#endif + } + + /* special case: when polymorphed, show "HD", disable exp */ + else if (attr_rec == &shown_stats[F_LEVEL]) { + static boolean lev_was_poly = FALSE; + + if (u.mtimedone && !lev_was_poly) { + force_update = TRUE; + set_name(attr_rec->w, "HD"); + lev_was_poly = TRUE; + } else if (!u.mtimedone && lev_was_poly) { + force_update = TRUE; + set_name(attr_rec->w, shown_stats[F_LEVEL].name); + lev_was_poly = FALSE; + } + } else if (attr_rec == &shown_stats[F_EXP]) { + static boolean exp_was_poly = FALSE; + + if (u.mtimedone && !exp_was_poly) { + force_update = TRUE; + set_name(attr_rec->w, ""); + set_value(attr_rec->w, ""); + exp_was_poly = TRUE; + } else if (!u.mtimedone && exp_was_poly) { + force_update = TRUE; + set_name(attr_rec->w, shown_stats[F_EXP].name); + exp_was_poly = FALSE; + } + if (u.mtimedone) return; /* no display for exp when poly */ + } + + if (attr_rec->last_value == new_value && !force_update) /* same */ + return; + + attr_rec->last_value = new_value; + + /* Special cases: strength, alignment and "clear". */ + if (attr_rec == &shown_stats[F_STR]) { + if(new_value > 18) { + if (new_value > 118) + Sprintf(buf,"%ld", new_value-100); + else if(new_value < 118) + Sprintf(buf, "18/%02ld", new_value-18); + else + Strcpy(buf, "18/**"); + } else { + Sprintf(buf, "%ld", new_value); + } + } else if (attr_rec == &shown_stats[F_ALIGN]) { + + Strcpy(buf, (new_value == A_CHAOTIC) ? "Chaotic" : + (new_value == A_NEUTRAL) ? "Neutral" : + "Lawful" ); + } else { + Sprintf(buf, "%ld", new_value); + } + set_value(attr_rec->w, buf); + } + + /* + * Now hilight the changed information. Names, time and score don't + * hilight. If first time, don't hilight. If already lit, don't do + * it again. + */ + if (attr_rec->type != SV_NAME && attr_rec != &shown_stats[F_TIME]) { + if (attr_rec->after_init) { + if(!attr_rec->set) { + if (attr_rec->type == SV_LABEL) + hilight_label(attr_rec->w); + else + hilight_value(attr_rec->w); + attr_rec->set = TRUE; + } + attr_rec->turn_count = 0; + } else { + attr_rec->after_init = TRUE; + } + } +} + +/* + * Update the displayed status. The current code in botl.c updates + * two lines of information. Both lines are always updated one after + * the other. So only do our update when we update the second line. + * + * Information on the first line: + * name, attributes, alignment, score + * + * Information on the second line: + * dlvl, gold, hp, power, ac, {level & exp or HD **} + * status (hunger, conf, halu, stun, sick, blind), time, encumbrance + * + * [**] HD is shown instead of level and exp if mtimedone is non-zero. + */ +static void +update_fancy_status(wp) + struct xwindow *wp; +{ + struct X_status_value *sv; + long val; + int i; + + if (wp->cursy != 0) return; /* do a complete update when line 0 is done */ + + for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) { + switch (i) { + case F_STR: val = (long) ACURR(A_STR); break; + case F_DEX: val = (long) ACURR(A_DEX); break; + case F_CON: val = (long) ACURR(A_CON); break; + case F_INT: val = (long) ACURR(A_INT); break; + case F_WIS: val = (long) ACURR(A_WIS); break; + case F_CHA: val = (long) ACURR(A_CHA); break; + /* + * Label stats. With the exceptions of hunger, encumbrance, sick + * these are either on or off. Pleae leave the ternary operators + * the way they are. I want to specify 0 or 1, not a boolean. + */ + case F_HUNGER: val = (long) u.uhs; break; + case F_CONFUSED: val = (long) Confusion ? 1L : 0L; break; + case F_SICK: val = (long) Sick ? (long)u.usick_type + : 0L; break; + case F_BLIND: val = (long) Blind ? 1L : 0L; break; + case F_STUNNED: val = (long) Stunned ? 1L : 0L; break; + case F_HALLU: val = (long) Hallucination ? 1L : 0L; break; + case F_ENCUMBER: val = (long) near_capacity(); break; + + case F_NAME: val = (long) 0L; break; /* special */ + case F_DLEVEL: val = (long) 0L; break; /* special */ +#ifndef GOLDOBJ + case F_GOLD: val = (long) u.ugold; break; +#else + case F_GOLD: val = money_cnt(invent); break; +#endif + case F_HP: val = (long) (u.mtimedone ? + (u.mh > 0 ? u.mh : 0): + (u.uhp > 0 ? u.uhp : 0)); break; + case F_MAXHP: val = (long) (u.mtimedone ? u.mhmax : + u.uhpmax); break; + case F_POWER: val = (long) u.uen; break; + case F_MAXPOWER: val = (long) u.uenmax; break; + case F_AC: val = (long) u.uac; break; + case F_LEVEL: val = (long) (u.mtimedone ? + mons[u.umonnum].mlevel : + u.ulevel); break; +#ifdef EXP_ON_BOTL + case F_EXP: val = flags.showexp ? u.uexp : 0L; break; +#else + case F_EXP: val = 0L; break; +#endif + case F_ALIGN: val = (long) u.ualign.type; break; + case F_TIME: val = flags.time ? (long) moves : 0L; break; +#ifdef SCORE_ON_BOTL + case F_SCORE: val = flags.showscore ? botl_score():0L; break; +#else + case F_SCORE: val = 0L; break; +#endif + default: + { + /* + * There is a possible infinite loop that occurs with: + * + * impossible->pline->flush_screen->bot->bot{1,2}-> + * putstr->adjust_status->update_other->impossible + * + * Break out with this. + */ + static boolean active = FALSE; + if (!active) { + active = TRUE; + impossible("update_other: unknown shown value"); + active = FALSE; + } + val = 0; + break; + } + } + update_val(sv, val); + } +} + +/* + * Turn off hilighted status values after a certain amount of turns. + */ +void +check_turn_events() +{ + int i; + struct X_status_value *sv; + + for (sv = shown_stats, i = 0; i < NUM_STATS; i++, sv++) { + if (!sv->set) continue; + + if (sv->turn_count++ >= hilight_time) { + if (sv->type == SV_LABEL) + hilight_label(sv->w); + else + hilight_value(sv->w); + sv->set = FALSE; + } + } +} + +/* Initialize alternate status ============================================= */ + +/* Return a string for the initial width. */ +static const char * +width_string(sv_index) + int sv_index; +{ + switch (sv_index) { + case F_STR: return "018/**"; + case F_DEX: + case F_CON: + case F_INT: + case F_WIS: + case F_CHA: return "088"; /* all but str never get bigger */ + + case F_HUNGER: return shown_stats[F_HUNGER].name; + case F_CONFUSED:return shown_stats[F_CONFUSED].name; + case F_SICK: return shown_stats[F_SICK].name; + case F_BLIND: return shown_stats[F_BLIND].name; + case F_STUNNED: return shown_stats[F_STUNNED].name; + case F_HALLU: return shown_stats[F_HALLU].name; + case F_ENCUMBER:return shown_stats[F_ENCUMBER].name; + + case F_NAME: + case F_DLEVEL: return ""; + case F_HP: + case F_MAXHP: return "9999"; + case F_POWER: + case F_MAXPOWER:return "999"; + case F_AC: return "-99"; + case F_LEVEL: return "99"; + case F_GOLD: + case F_EXP: return "4294967295"; /* max ulong */ + case F_ALIGN: return "Neutral"; + case F_TIME: return "4294967295"; /* max ulong */ + case F_SCORE: return "4294967295"; /* max ulong */ + } + impossible("width_string: unknown index %d\n", sv_index); + return ""; +} + +static void +create_widget(parent, sv, sv_index) + Widget parent; + struct X_status_value *sv; + int sv_index; +{ + Arg args[4]; + Cardinal num_args; + + switch (sv->type) { + case SV_VALUE: + sv->w = create_value(parent, sv->name); + set_value(sv->w, width_string(sv_index)); + break; + case SV_LABEL: + /* Labels get their own buffer. */ + sv->name = (char *) alloc(BUFSZ); + sv->name[0] = '\0'; + + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++; + sv->w = XtCreateManagedWidget( + sv_index == F_NAME ? "name" : "dlevel", + labelWidgetClass, + parent, + args, num_args); + break; + case SV_NAME: + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++; + sv->w = XtCreateManagedWidget(sv->name, + labelWidgetClass, + parent, + args, num_args); + break; + default: + panic("create_widget: unknown type %d", sv->type); + } +} + +/* + * Get current width of value. width2p is only valid for SV_LABEL types. + */ +static void +get_widths(sv, width1p, width2p) + struct X_status_value *sv; + int *width1p, *width2p; +{ + Arg args[1]; + Dimension width; + + switch (sv->type) { + case SV_VALUE: + *width1p = get_name_width(sv->w); + *width2p = get_value_width(sv->w); + break; + case SV_LABEL: + case SV_NAME: + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(sv->w, args, ONE); + *width1p = width; + *width2p = 0; + break; + default: + panic("get_widths: unknown type %d", sv->type); + } +} + +static void +set_widths(sv, width1, width2) + struct X_status_value *sv; + int width1, width2; +{ + Arg args[1]; + + switch (sv->type) { + case SV_VALUE: + set_name_width(sv->w, width1); + set_value_width(sv->w, width2); + break; + case SV_LABEL: + case SV_NAME: + XtSetArg(args[0], XtNwidth, (width1+width2)); + XtSetValues(sv->w, args, ONE); + break; + default: + panic("set_widths: unknown type %d", sv->type); + } +} + +static Widget +init_column(name, parent, top, left, col_indices) + char *name; + Widget parent, top, left; + int *col_indices; +{ + Widget form; + Arg args[4]; + Cardinal num_args; + int max_width1, width1, max_width2, width2; + int *ip; + struct X_status_value *sv; + + num_args = 0; + if (top != (Widget) 0) { + XtSetArg(args[num_args], XtNfromVert, top); num_args++; + } + if (left != (Widget) 0) { + XtSetArg(args[num_args], XtNfromHoriz, left); num_args++; + } + XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++; + form = XtCreateManagedWidget(name, + formWidgetClass, + parent, args, num_args); + + max_width1 = max_width2 = 0; + for (ip = col_indices; *ip >= 0; ip++) { + sv = &shown_stats[*ip]; + create_widget(form, sv, *ip); /* will set init width */ + if (ip != col_indices) { /* not first */ + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, shown_stats[*(ip-1)].w); + num_args++; + XtSetValues(sv->w, args, num_args); + } + get_widths(sv, &width1, &width2); + if (width1 > max_width1) max_width1 = width1; + if (width2 > max_width2) max_width2 = width2; + } + for (ip = col_indices; *ip >= 0 ; ip++) { + set_widths(&shown_stats[*ip], max_width1, max_width2); + } + + /* There is room behind the end marker for the two widths. */ + *++ip = max_width1; + *++ip = max_width2; + + return form; +} + +/* + * These are the orders of the displayed columns. Change to suit. The -1 + * indicates the end of the column. The two numbers after that are used + * to store widths that are calculated at run-time. + */ +static int attrib_indices[] = { F_STR,F_DEX,F_CON,F_INT,F_WIS,F_CHA, -1,0,0 }; +static int status_indices[] = { F_HUNGER, F_CONFUSED, F_SICK, F_BLIND, + F_STUNNED, F_HALLU, F_ENCUMBER, -1,0,0 }; + +static int col2_indices[] = { F_MAXHP, F_ALIGN, F_TIME, F_EXP, + F_MAXPOWER, -1,0,0 }; +static int col1_indices[] = { F_HP, F_AC, F_GOLD, F_LEVEL, + F_POWER, F_SCORE, -1,0,0 }; + + +/* + * Produce a form that looks like the following: + * + * name + * dlevel + * col1_indices[0] col2_indices[0] + * col1_indices[1] col2_indices[1] + * . . + * . . + * col1_indices[n] col2_indices[n] + */ +static Widget +init_info_form(parent, top, left) + Widget parent, top, left; +{ + Widget form, col1; + struct X_status_value *sv_name, *sv_dlevel; + Arg args[6]; + Cardinal num_args; + int total_width, *ip; + + num_args = 0; + if (top != (Widget) 0) { + XtSetArg(args[num_args], XtNfromVert, top); num_args++; + } + if (left != (Widget) 0) { + XtSetArg(args[num_args], XtNfromHoriz, left); num_args++; + } + XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++; + form = XtCreateManagedWidget("status_info", + formWidgetClass, + parent, + args, num_args); + + /* top of form */ + sv_name = &shown_stats[F_NAME]; + create_widget(form, sv_name, F_NAME); + + /* second */ + sv_dlevel = &shown_stats[F_DLEVEL]; + create_widget(form, sv_dlevel, F_DLEVEL); + + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, sv_name->w); num_args++; + XtSetValues(sv_dlevel->w, args, num_args); + + /* two columns beneath */ + col1 = init_column("name_col1", form, sv_dlevel->w, + (Widget) 0, col1_indices); + (void) init_column("name_col2", form, sv_dlevel->w, + col1, col2_indices); + + /* Add calculated widths. */ + for (ip = col1_indices; *ip >= 0; ip++) + ; /* skip to end */ + total_width = *++ip; + total_width += *++ip; + for (ip = col2_indices; *ip >= 0; ip++) + ; /* skip to end */ + total_width += *++ip; + total_width += *++ip; + + XtSetArg(args[0], XtNwidth, total_width); + XtSetValues(sv_name->w, args, ONE); + XtSetArg(args[0], XtNwidth, total_width); + XtSetValues(sv_dlevel->w, args, ONE); + + return form; +} + +/* + * Create the layout for the fancy status. Return a form widget that + * contains everything. + */ +static Widget +create_fancy_status(parent, top) + Widget parent, top; +{ + Widget form; /* The form that surrounds everything. */ + Widget w; + Arg args[8]; + Cardinal num_args; + + num_args = 0; + if (top != (Widget) 0) { + XtSetArg(args[num_args], XtNfromVert, top); num_args++; + } + XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNorientation, XtorientHorizontal); num_args++; + form = XtCreateManagedWidget("fancy_status", + panedWidgetClass, + parent, + args, num_args); + + w = init_info_form(form, (Widget) 0, (Widget) 0); + w = init_column("status_attributes",form, (Widget) 0, w, attrib_indices); + (void) init_column("status_condition", form, (Widget) 0, w, status_indices); + return form; +} + +static void +destroy_fancy_status(wp) +struct xwindow *wp; +{ + int i; + struct X_status_value *sv; + + if (!wp->keep_window) + XtDestroyWidget(wp->w), wp->w = (Widget)0; + + for (i = 0, sv = shown_stats; i < NUM_STATS; i++, sv++) + if (sv->type == SV_LABEL) { + free((genericptr_t)sv->name); + sv->name = 0; + } +} + +/*winstat.c*/ diff --git a/win/X11/wintext.c b/win/X11/wintext.c new file mode 100644 index 0000000..84cafe6 --- /dev/null +++ b/win/X11/wintext.c @@ -0,0 +1,626 @@ +/* SCCS Id: @(#)wintext.c 3.4 1996/04/05 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * File for dealing with text windows. + * + * + No global functions. + */ + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" +#include "winX.h" +#include "xwindow.h" + +#ifdef GRAPHIC_TOMBSTONE +#include +#endif + + +#define TRANSIENT_TEXT /* text window is a transient window (no positioning) */ + +static const char text_translations[] = + "#override\n\ + : dismiss_text()\n\ + : key_dismiss_text()"; + +#ifdef GRAPHIC_TOMBSTONE +static const char rip_translations[] = + "#override\n\ + : rip_dismiss_text()\n\ + : rip_dismiss_text()"; + +static Widget FDECL(create_ripout_widget, (Widget)); +#endif + +/*ARGSUSED*/ +void +delete_text(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + struct xwindow *wp; + struct text_info_t *text_info; + + wp = find_widget(w); + text_info = wp->text_information; + + nh_XtPopdown(wp->popup); + + if (text_info->blocked) { + exit_x_event = TRUE; + } else if (text_info->destroy_on_ack) { + destroy_text_window(wp); + } +} + +/* + * Callback used for all text windows. The window is poped down on any key + * or button down event. It is destroyed if the main nethack code is done + * with it. + */ +/*ARGSUSED*/ +void +dismiss_text(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + struct xwindow *wp; + struct text_info_t *text_info; + + wp = find_widget(w); + text_info = wp->text_information; + + nh_XtPopdown(wp->popup); + + if (text_info->blocked) { + exit_x_event = TRUE; + } else if (text_info->destroy_on_ack) { + destroy_text_window(wp); + } +} + +/* Dismiss when a non-modifier key pressed. */ +void +key_dismiss_text(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch = key_event_to_char((XKeyEvent *) event); + if (ch) dismiss_text(w, event, params, num_params); +} + +#ifdef GRAPHIC_TOMBSTONE +/* Dismiss from clicking on rip image. */ +void +rip_dismiss_text(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + dismiss_text(XtParent(w), event, params, num_params); +} +#endif + + +/* ARGSUSED */ +void +add_to_text_window(wp, attr, str) + struct xwindow *wp; + int attr; /* currently unused */ + const char *str; +{ + struct text_info_t *text_info = wp->text_information; + int width; + + append_text_buffer(&text_info->text, str, FALSE); + + /* Calculate text width and save longest line */ + width = XTextWidth(text_info->fs, str, (int) strlen(str)); + if (width > text_info->max_width) + text_info->max_width = width; +} + +void +display_text_window(wp, blocking) + struct xwindow *wp; + boolean blocking; +{ + struct text_info_t *text_info; + Arg args[8]; + Cardinal num_args; + Dimension width, height, font_height; + int nlines; + + text_info = wp->text_information; + width = text_info->max_width + text_info->extra_width; + text_info->blocked = blocking; + text_info->destroy_on_ack = FALSE; + font_height = nhFontHeight(wp->w); + + /* + * Calculate the number of lines to use. First, find the number of + * lines that would fit on the screen. Next, remove four of these + * lines to give room for a possible window manager titlebar (some + * wm's put a titlebar on transient windows). Make sure we have + * _some_ lines. Finally, use the number of lines in the text if + * there are fewer than the max. + */ + nlines = (XtScreen(wp->w)->height - text_info->extra_height) / font_height; + nlines -= 4; + + if (nlines > text_info->text.num_lines) + nlines = text_info->text.num_lines; + if (nlines <= 0) nlines = 1; + + height = nlines * font_height + text_info->extra_height; + + num_args = 0; + + if (nlines < text_info->text.num_lines) { + /* add on width of scrollbar. Really should look this up, + * but can't until the window is realized. Chicken-and-egg problem. + */ + width += 20; + } + +#ifdef GRAPHIC_TOMBSTONE + if (text_info->is_rip) { + Widget rip = create_ripout_widget(XtParent(wp->w)); + XtSetArg(args[num_args], XtNfromVert, rip); num_args++; + } +#endif + + if (width > (Dimension) XtScreen(wp->w)->width) { /* too wide for screen */ + /* Back off some amount - we really need to back off the scrollbar */ + /* width plus some extra. */ + width = XtScreen(wp->w)->width - 20; + } + XtSetArg(args[num_args], XtNstring, text_info->text.text); num_args++; + XtSetArg(args[num_args], XtNwidth, width); num_args++; + XtSetArg(args[num_args], XtNheight, height); num_args++; + XtSetValues(wp->w, args, num_args); + +#ifdef TRANSIENT_TEXT + XtRealizeWidget(wp->popup); + XSetWMProtocols(XtDisplay(wp->popup), XtWindow(wp->popup), + &wm_delete_window, 1); + positionpopup(wp->popup, FALSE); +#endif + + nh_XtPopup(wp->popup, (int)XtGrabNone, wp->w); + + /* Kludge alert. Scrollbars are not sized correctly by the Text widget */ + /* if added before the window is displayed, so do it afterward. */ + num_args = 0; + if (nlines < text_info->text.num_lines) { /* add vert scrollbar */ + XtSetArg(args[num_args], XtNscrollVertical, XawtextScrollAlways); + num_args++; + } + if (width >= (Dimension) (XtScreen(wp->w)->width-20)) { /* too wide */ + XtSetArg(args[num_args], XtNscrollHorizontal, XawtextScrollAlways); + num_args++; + } + if (num_args) XtSetValues(wp->w, args, num_args); + + /* We want the user to acknowlege. */ + if (blocking) { + (void) x_event(EXIT_ON_EXIT); + nh_XtPopdown(wp->popup); + } +} + + +void +create_text_window(wp) + struct xwindow *wp; +{ + struct text_info_t *text_info; + Arg args[8]; + Cardinal num_args; + Position top_margin, bottom_margin, left_margin, right_margin; + Widget form; + + wp->type = NHW_TEXT; + + wp->text_information = text_info = + (struct text_info_t *) alloc(sizeof(struct text_info_t)); + + init_text_buffer(&text_info->text); + text_info->max_width = 0; + text_info->extra_width = 0; + text_info->extra_height = 0; + text_info->blocked = FALSE; + text_info->destroy_on_ack = TRUE; /* Ok to destroy before display */ +#ifdef GRAPHIC_TOMBSTONE + text_info->is_rip = FALSE; +#endif + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(text_translations)); num_args++; + +#ifdef TRANSIENT_TEXT + wp->popup = XtCreatePopupShell("text", transientShellWidgetClass, + toplevel, args, num_args); +#else + wp->popup = XtCreatePopupShell("text", topLevelShellWidgetClass, + toplevel, args, num_args); +#endif + XtOverrideTranslations(wp->popup, + XtParseTranslationTable("WM_PROTOCOLS: delete_text()")); + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; + form = XtCreateManagedWidget("form", formWidgetClass, wp->popup, + args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNdisplayCaret, False); num_args++; + XtSetArg(args[num_args], XtNresize, XawtextResizeBoth); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(text_translations)); num_args++; + + wp->w = XtCreateManagedWidget( + killer && WIN_MAP == WIN_ERR ? + "tombstone" : "text_text", /* name */ + asciiTextWidgetClass, + form, /* parent widget */ + args, /* set some values */ + num_args); /* number of values to set */ + + /* Get the font and margin information. */ + num_args = 0; + XtSetArg(args[num_args], XtNfont, &text_info->fs); num_args++; + XtSetArg(args[num_args], XtNtopMargin, &top_margin); num_args++; + XtSetArg(args[num_args], XtNbottomMargin, &bottom_margin); num_args++; + XtSetArg(args[num_args], XtNleftMargin, &left_margin); num_args++; + XtSetArg(args[num_args], XtNrightMargin, &right_margin); num_args++; + XtGetValues(wp->w, args, num_args); + + text_info->extra_width = left_margin + right_margin; + text_info->extra_height = top_margin + bottom_margin; +} + +void +destroy_text_window(wp) + struct xwindow *wp; +{ + /* Don't need to pop down, this only called from dismiss_text(). */ + + struct text_info_t *text_info = wp->text_information; + + /* + * If the text window was blocked, then the user has already ACK'ed + * it and we are free to really destroy the window. Otherwise, don't + * destroy until the user dismisses the window via a key or button + * press. + */ + if (text_info->blocked || text_info->destroy_on_ack) { + XtDestroyWidget(wp->popup); + free_text_buffer(&text_info->text); + free((genericptr_t)text_info), wp->text_information = 0; + wp->type = NHW_NONE; /* allow reuse */ + } else { + text_info->destroy_on_ack = TRUE; /* destroy on next ACK */ + } +} + +void +clear_text_window(wp) + struct xwindow *wp; +{ + clear_text_buffer(&wp->text_information->text); +} + + +/* text buffer routines ---------------------------------------------------- */ + +/* Append a line to the text buffer. */ +void +append_text_buffer(tb, str, concat) + struct text_buffer *tb; + const char *str; + boolean concat; +{ + char *copy; + int length; + + if (!tb->text) panic("append_text_buffer: null text buffer"); + + if (str) { + length = strlen(str); + } else { + length = 0; + } + + if (length + tb->text_last + 1 >= tb->text_size) { + /* we need to go to a bigger buffer! */ +#ifdef VERBOSE + printf("append_text_buffer: text buffer growing from %d to %d bytes\n", + tb->text_size, 2*tb->text_size); +#endif + copy = (char *) alloc((unsigned)tb->text_size*2); + (void) memcpy(copy, tb->text, tb->text_last); + free(tb->text); + tb->text = copy; + tb->text_size *= 2; + } + + if (tb->num_lines) { /* not first --- append a newline */ + char appchar = '\n'; + + if(concat && !index("!.?'\")", tb->text[tb->text_last-1])) { + appchar = ' '; + tb->num_lines--; /* offset increment at end of function */ + } + + *(tb->text + tb->text_last) = appchar; + tb->text_last++; + } + + if (str) { + (void) memcpy((tb->text+tb->text_last), str, length+1); + if(length) { + /* Remove all newlines. Otherwise we have a confused line count. */ + copy = (tb->text+tb->text_last); + while ((copy = index(copy, '\n')) != (char*)0) + *copy = ' '; + } + + tb->text_last += length; + } + tb->text[tb->text_last] = '\0'; + tb->num_lines++; +} + +/* Initialize text buffer. */ +void +init_text_buffer(tb) + struct text_buffer *tb; +{ + tb->text = (char *) alloc(START_SIZE); + tb->text[0] = '\0'; + tb->text_size = START_SIZE; + tb->text_last = 0; + tb->num_lines = 0; +} + +/* Empty the text buffer */ +void +clear_text_buffer(tb) + struct text_buffer *tb; +{ + tb->text_last = 0; + tb->text[0] = '\0'; + tb->num_lines = 0; +} + +/* Free up allocated memory. */ +void +free_text_buffer(tb) + struct text_buffer *tb; +{ + free(tb->text); + tb->text = (char *) 0; + tb->text_size = 0; + tb->text_last = 0; + tb->num_lines = 0; +} + + +#ifdef GRAPHIC_TOMBSTONE + +static void FDECL(rip_exposed, (Widget,XtPointer,XtPointer)); + +static XImage* rip_image=0; + + +#define STONE_LINE_LEN 16 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + +static char rip_line[YEAR_LINE+1][STONE_LINE_LEN+1]; + +extern const char *killed_by_prefix[]; + +void +calculate_rip_text(int how) +{ + /* Follows same algorithm as genl_outrip() */ + + char buf[BUFSZ]; + char *dpx; + int line; + + /* Put name on stone */ + Sprintf(rip_line[NAME_LINE], "%s", plname); + + /* Put $ on stone */ + Sprintf(rip_line[GOLD_LINE], "%ld Au", +#ifndef GOLDOBJ + u.ugold); +#else + done_money); +#endif + /* Put together death description */ + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + + /* Put death type on stone */ + for (line=DEATH_LINE, dpx = buf; line STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + strcpy(rip_line[line], dpx); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + + /* Put year on stone */ + Sprintf(rip_line[YEAR_LINE], "%4d", getyear()); +} + + +/* + * RIP image expose callback. + */ +/*ARGSUSED*/ +static void +rip_exposed(w, client_data, widget_data) + Widget w; + XtPointer client_data; /* unused */ + XtPointer widget_data; /* expose event from Window widget */ +{ + XExposeEvent *event = (XExposeEvent *) widget_data; + Display* dpy=XtDisplay(w); + Arg args[8]; + XGCValues values; + XtGCMask mask; + GC gc; + static Pixmap rip_pixmap=None; + int i, x, y; + + if (!XtIsRealized(w) || event->count > 0) return; + + if (rip_pixmap == None && rip_image) { + rip_pixmap = XCreatePixmap(dpy, XtWindow(w), + rip_image->width, + rip_image->height, + DefaultDepth(dpy, DefaultScreen(dpy))); + XPutImage(dpy, rip_pixmap, + DefaultGC(dpy, DefaultScreen(dpy)), + rip_image, + 0,0, 0,0, /* src, dest top left */ + rip_image->width, + rip_image->height); + XDestroyImage(rip_image); /* data bytes free'd also */ + } + + mask = GCFunction | GCForeground | GCGraphicsExposures | GCFont; + values.graphics_exposures = False; + XtSetArg(args[0], XtNforeground, &values.foreground); + XtGetValues(w, args, 1); + values.function = GXcopy; + values.font = WindowFont(w); + gc = XtGetGC(w, mask, &values); + + if (rip_pixmap != None) { + XCopyArea(dpy, rip_pixmap, XtWindow(w), gc, + event->x, event->y, event->width, event->height, + event->x, event->y); + } + + x=appResources.tombtext_x; + y=appResources.tombtext_y; + for (i=0; i<=YEAR_LINE; i++) { + int len=strlen(rip_line[i]); + XFontStruct* font=WindowFontStruct(w); + int width=XTextWidth(font, rip_line[i], len); + XDrawString(dpy, XtWindow(w), gc, + x-width/2, y, rip_line[i], len); + x+=appResources.tombtext_dx; + y+=appResources.tombtext_dy; + } + + XtReleaseGC(w, gc); +} + +/* + * The ripout window creation routine. + */ +static Widget +create_ripout_widget(Widget parent) +{ + Widget imageport; + Arg args[16]; + Cardinal num_args; + + static int rip_width, rip_height; + + if (!rip_image) { + XpmAttributes attributes; + int errorcode; + + attributes.valuemask = XpmCloseness; + attributes.closeness = 65535; /* Try anything */ + errorcode = XpmReadFileToImage(XtDisplay(parent), appResources.tombstone, &rip_image, 0, &attributes); + if (errorcode != XpmSuccess) { + char buf[BUFSZ]; + Sprintf(buf, "Failed to load %s: %s", appResources.tombstone, + XpmGetErrorString(errorcode)); + X11_raw_print(buf); + } + rip_width = rip_image->width; + rip_height = rip_image->height; + } + + num_args = 0; + XtSetArg(args[num_args], XtNwidth, rip_width); num_args++; + XtSetArg(args[num_args], XtNheight, rip_height); num_args++; + XtSetArg(args[num_args], XtNtranslations, + XtParseTranslationTable(rip_translations)); num_args++; + + imageport = XtCreateManagedWidget("rip", windowWidgetClass, + parent, args, num_args); + + XtAddCallback(imageport, XtNexposeCallback, rip_exposed, (XtPointer) 0); + + return imageport; +} + +#endif /* GRAPHIC_TOMBSTONE */ + +/*wintext.c*/ diff --git a/win/X11/winval.c b/win/X11/winval.c new file mode 100644 index 0000000..3f2307e --- /dev/null +++ b/win/X11/winval.c @@ -0,0 +1,177 @@ +/* SCCS Id: @(#)winval.c 3.4 1992/3/7 */ +/* Copyright (c) Dean Luick, 1992 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Routines that define a name-value label widget pair that fit inside a + * form widget. + */ +#include + +#ifndef SYSV +#define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ +#endif + +#include +#include +#include +#include +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" /* #define for const for non __STDC__ compilers */ +#include "winX.h" + +#define WNAME "name" +#define WVALUE "value" + + +Widget +create_value(parent, name_value) + Widget parent; + const char *name_value; +{ + Widget form, name; + Arg args[8]; + Cardinal num_args; + + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNdefaultDistance, 0); num_args++; + form = XtCreateManagedWidget(name_value, + formWidgetClass, + parent, args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNjustify, XtJustifyRight); num_args++; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNlabel, name_value); num_args++; + XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++; + name = XtCreateManagedWidget(WNAME, + labelWidgetClass, + form, args, num_args); + + num_args = 0; + XtSetArg(args[num_args], XtNjustify, XtJustifyRight); num_args++; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + XtSetArg(args[num_args], XtNfromHoriz, name); num_args++; + XtSetArg(args[num_args], XtNinternalHeight, 0); num_args++; + (void) XtCreateManagedWidget(WVALUE, + labelWidgetClass, + form, args, num_args); + return form; +} + +void +set_name(w, new_label) + Widget w; + char *new_label; +{ + Arg args[1]; + Widget name; + + name = XtNameToWidget(w, WNAME); + XtSetArg(args[0], XtNlabel, new_label); + XtSetValues(name, args, ONE); +} + +void +set_name_width(w, new_width) + Widget w; + int new_width; +{ + Arg args[1]; + Widget name; + + name = XtNameToWidget(w, WNAME); + XtSetArg(args[0], XtNwidth, new_width); + XtSetValues(name, args, ONE); +} + +int +get_name_width(w) + Widget w; +{ + Arg args[1]; + Dimension width; + Widget name; + + name = XtNameToWidget(w, WNAME); + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(name, args, ONE); + return (int) width; +} + + +void +set_value(w, new_value) + Widget w; + const char *new_value; +{ + Arg args[1]; + Widget val; + + val = XtNameToWidget(w, WVALUE); + XtSetArg(args[0], XtNlabel, new_value); + XtSetValues(val, args, ONE); +} + +void +set_value_width(w, new_width) + Widget w; + int new_width; +{ + Arg args[1]; + Widget val; + + val = XtNameToWidget(w, WVALUE); + XtSetArg(args[0], XtNwidth, new_width); + XtSetValues(val, args, ONE); +} + +int +get_value_width(w) + Widget w; +{ + Arg args[1]; + Widget val; + Dimension width; + + val = XtNameToWidget(w, WVALUE); + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(val, args, ONE); + return (int) width; +} + +/* Swap foreground and background colors (this is the best I can do with */ +/* a label widget, unless I can get some init hook in there). */ +void +hilight_value(w) + Widget w; +{ + swap_fg_bg(XtNameToWidget(w, WVALUE)); +} + +/* Swap the foreground and background colors of the given widget */ +void +swap_fg_bg(w) + Widget w; +{ + Arg args[2]; + Pixel fg, bg; + + XtSetArg(args[0], XtNforeground, &fg); + XtSetArg(args[1], XtNbackground, &bg); + XtGetValues(w, args, TWO); + + XtSetArg(args[0], XtNforeground, bg); + XtSetArg(args[1], XtNbackground, fg); + XtSetValues(w, args, TWO); +} + diff --git a/win/gem/Install.gem b/win/gem/Install.gem new file mode 100644 index 0000000..e341237 --- /dev/null +++ b/win/gem/Install.gem @@ -0,0 +1,38 @@ +Hi, + +This is nethack3.4.1 for Atari Gem and tty +Windowing System. + +It is by far not complete or perfect. +(My english too :-)) + +You need at least 2Meg free RAM, 16 colors and +3 Meg free Disk space. +In fact it works also with monochrome, but you +have to create a nh2.img (and title2.img) on your own. + +Atari windowport changes from 3.3.0: +added a ASCII-Mode in GEM -> F2 +the cursor is switchable -> F3 +added inventory/menu search -> : +removed the redraw problem +removed almost all flicker (except with NOVA-Card :-() +placed the GEM-dialogues more pleasent +tty corner windows (i.e. inv) display now correct in a vt52-win +greyed out old messages +placed the GEM-windows more convient +... + +Feel free to contact me about Issues and Errors. +e-mail: gaston@cs.tu-berlin.de + +You use this program at your own risk, I can't +guarantee it will work or do you no harm. + +Look at the nethack licence too. + +As you may have noticed the look and feel is from +Warwick Allisons nethack3.1.3d Gem Version +and I have used E_Gem2.2.0 from Christian Grunenberg. + +Marvin diff --git a/win/gem/bitmfile.c b/win/gem/bitmfile.c new file mode 100644 index 0000000..13e0073 --- /dev/null +++ b/win/gem/bitmfile.c @@ -0,0 +1,340 @@ +/****************************\ +* Bitmap mit Farbtabelle als * +* Graphik-Datei speichern * +* Autor: Gabriel Schmidt * +* (c) 1992 by MAXON-Computer * +* Modifiziert von Sebastian * +* Bieber, Dez. 1994 * +* -> Programmcode * +\****************************/ + +#include +#include +#include +#include +#include + +#include "bitmfile.h" + +/* --- (X) IMG-Implementation ----------------- */ + +#define IMG_COMPRESSED + +typedef struct + { + UWORD img_version; + UWORD img_headlen; + UWORD img_nplanes; + UWORD img_patlen; + UWORD img_pixw; + UWORD img_pixh; + UWORD img_w; + UWORD img_h; + } IMG_HEADER; + +typedef enum {NONE, SOLID0, SOLID1, PATRUN, BITSTR} IMG_MODE; + +typedef UBYTE IMG_SOLID; + +typedef enum { RGB=0, CMY=1, Pantone=2 } XIMG_COLMODEL; + +typedef struct + { + ULONG img_ximg; + XIMG_COLMODEL img_colmodel; + } XIMG_HEADER; + +typedef struct RGB XIMG_RGB; + + +int bitmap_to_img(FILE_TYP typ, int ww, int wh, + unsigned int pixw, unsigned int pixh, + unsigned int planes, unsigned int colors, + const char *filename, + void(*get_color)(unsigned int colind, struct RGB *rgb), + void(*get_pixel)(int x, int y, unsigned int *colind) ) + { + int file, error, cnt; + IMG_HEADER header; + XIMG_HEADER xheader; + XIMG_RGB xrgb; + IMG_MODE mode; + UBYTE *line_buf, *write_buf; + register UBYTE *startpnt, *bufpnt; + unsigned int colind, line_len, line, bit; + register unsigned int byte; + register UBYTE count; + + /* fill in (X) IMG-Header */ + + header.img_version = 1; + header.img_headlen = (UWORD) sizeof(header) /2; + if (typ == XIMG) + header.img_headlen += (UWORD)(sizeof(xheader)+colors*sizeof(xrgb))/2; + + header.img_nplanes = planes; + header.img_patlen = 2; + header.img_pixw = pixw; + header.img_pixh = pixh; + header.img_w = ww; + header.img_h = wh; + + xheader.img_ximg = XIMG_MAGIC; + xheader.img_colmodel= RGB; + + /* calculate linelength, allocate buffer. */ + + line_len = (ww+7)/8; + + line_buf = malloc((size_t)planes*line_len); + if (line_buf == NULL) + return(ENOMEM); + + /* Worst case: the bufferd line could grow to max. 3 times the length */ + /* of the original! */ + + write_buf = malloc((size_t)3*line_len); + if (write_buf == NULL) + { + free(line_buf); + return(ENOMEM); + }; + + /* open file */ + + file = open(filename, O_WRONLY | O_CREAT | O_TRUNC); + if (file<0) + { + error = errno; + free(line_buf); + free(write_buf); + return(error); + }; + + /* write Header */ + + if (write (file, &header, sizeof(header)) != sizeof(header) || + (typ == XIMG && write (file, &xheader, sizeof(xheader) ) != sizeof(xheader))) + { + error = errno; + close(file); + free(line_buf); + free(write_buf); + return(error); + }; + + /* save the colortable if possible */ + + if ( typ == XIMG ) + for (cnt=0; cnt>= 1; + }; + }; + }; + + /* compress bitstrings in buffer */ + /* and write it to file */ + + for (cnt=0; cnt0; count--) + *(bufpnt++) = *(startpnt++); + break; + }; + }; + + if (write(file,write_buf,bufpnt-write_buf) != (bufpnt-write_buf)) + { + error = errno; + close(file); + free(line_buf); + free(write_buf); + return(error); + }; + + }; + }; + + /*close file, free buffer. */ + + close(file); + free(line_buf); + free(write_buf); + return(0); + +} + +/*---filetype-dispatcher--------------------*/ + +const char *get_file_ext(FILE_TYP typ) + { + switch (typ) + { + case IMG: + case XIMG: + return("IMG"); + default: + return(""); + }; +} + + +int bitmap_to_file(FILE_TYP typ, int ww, int wh, + unsigned int pwx, unsigned int pwy, + unsigned int planes, unsigned int colors, + const char *filename, + void (*get_color)(unsigned int colind, struct RGB *rgb), + void (*get_pixel)(int x, int y, unsigned int *colind)) + { + + switch (typ) + { + case IMG: + case XIMG: + return(bitmap_to_img(typ,ww,wh,pwx,pwy,planes,colors,filename,get_color,get_pixel)); + default: + return(-1); + }; +} + diff --git a/win/gem/gem_rsc.uu b/win/gem/gem_rsc.uu new file mode 100644 index 0000000..32b3900 --- /dev/null +++ b/win/gem/gem_rsc.uu @@ -0,0 +1,230 @@ +begin 777 GEM_RSC.RSC +M $29@^Z$B(/N@^Z #0)&@^Z)]X Y0 , !8 @ "@. +M "!.151(04-+ "!'86UE "!(97)E "!4:&5R90 @271E;0 @07!P +M87)E; @36%G:6, ("!!8F]U="!.971H86-K+BXN "TM+2TM+2TM+2TM+2TM +M+2TM+2TM+2T ("!$97-K($%C8V5S2 Q(" ("!$97-K($%C8V5S2 R(" ("!$97-K($%C8V5S2 S(" ("!$97-K($%C8V5S2 T +M(" ("!$97-K($%C8V5S2 U(" ("!$97-K($%C8V5S2 V(" +M("!(96QP+BXN(" @(" @(" @/P @(%=H870@:7,N+BX@(" @(" O " @0V]M +M;6%N9"!H96QP+BXN("8 +2TM+2TM+2TM+2TM+2TM+2TM+2TM " @3W!T:6]N +M" @ +M(" @( 5L " @16YG00 M+2TM +M+2TM+2TM+2TM+2TM " @3&]O:R!A=" @(" [ " @4V5A " @2G5M<" @(" @( 5J " @ +M0VAA=" @(" @( 5C " @1FEG:'0@(" @("!& " @4VAO=R!A;&P@(" @("!I +M " @4VAO=R!K:6YD(" @("!) " @1&ES8V]V97)I97,@("!< "TM+2TM+2TM +M+2TM+2TM+2TM+2T ("!%870@(" @(" @(" @(&4 ("!$2 @(" @(" @(&$ ("!5 @($9I;&P@<75I=F5R(" @ +M(" @40 @($9I2 @(% ("!296UO +M=F4@86-C97-S;W)Y(%( ("!7;W)N(')I;F=S(" @(" @(#T ("!7;W)N(&%M +M=6QE=" @(" @("( ("!,:7-T('-P96QL@ @($-A'#P)'$< X#@0/YS^"!P' . X$#_=_@@?GF @ !@)__^8"0 F D 59@) "8"0!5F D )@) 168"0 +MF D%!9@$@ $X!($5, 2 3 "0%9P D "8 $@5. D G $A3@ D)P $DX +M F< $. G ' #SP\\ \\//@/ +M___X#___^ ____@/ #X#U55^ \ /@/557X#P ^ ]55?@/ #X#U55^ > +M ?@'U57P!X !\ /55_ #P /@ ?47X #P#\ ?5^ #P_ ??@ #_P ?X +M #\ < '0 !V\ =P 4 !@ "$8 /__ +M "\ 0 !W$ >1 'D@ % 8 A& #__P @ $ >: 'HP !Z0 +M P & 0 __\ "0 ! 'V0 !]L ?E , !@ "$8 /__ ( "@ +M!^H ?O '^P # 8 A& #__P % P @ ("0 " H P & 0 +M __\ "0 ! ("P "$$ AW , !@ "$8 /__ #8 -@ "(, B% +M (A@ # 8 A$ #__@ " $ B' (B0 "(H P & (1 __X +M @ ! (BP "(T B. , !@ "$0 /_^ ( 0 "(\ B1 (D@ # +M 8 A$ #__@ " $ B3 (E0 ")8 P & (1 __X @ ! ( +MEP ")D B: , !@ "$0 /_^ ( 0 ")L B= (G@ # 8 A$ +M #__@ " $ B? (H0 "*( P & (1 __X @ ! (HP "*4 +M BF , !@ "$0 /_^ ( 0 "*< BN (KP % 8 A& #__P ' +M $ BP (M0 "+8 !0 & (1@ __\ !0 ! (MP "- C1 , +M!@ "$P, /__ !D 0 "-( C= (Z # 8 I!Y #__P + L CJ +M ([0 ".X P & (0> $ P ! ([P "0H D+ , !@ "$0$ +M /__ !L 0 "^H D: &URD 4 !( @ \Z +M .N@ "1$0 !P " ( ! " +0 (__\ 0 * !D +M : 9 H @ " !0 1 : ,! $ P ) !D +M " ,@,! 3_____ " - "0,! 7_____ " +M /0 ) !@,! ;_____ " 0P / !@,! ?_____ +M " 20 5 !P,! C_____ " 4 < !@,! G_ +M____ " 5@ B "0,! +_____ " 7P K !P,! +M "P!< !D P$ 40 3 !0 # 3 !0 /\1 " +M%@ ( W_____ !P 9@ %@ ! [_____ !P ( >0 +M $ %@ ! ______ !P D ( %@ ! !#_____ !P +MI0 , %@ ! !'_____ !P N@ 0 %@ ! !+_____ !P +M SP 4 %@ ! !/_____ !P Y 8 %@ ! O_____ !P +M ^0 < %@ ! !\ %0 > !0 /\1 + %0 * !;_____ +M !P !#@ %0 ! !?_____ !P !(@ $ %0 ! !C_ +M____ !P !-@ ( %0 ! !G_____ !P ( !2@ , %0 ! +M !K_____ !P !8 0 %0 ! !O_____ !P != 4 +M%0 ! !S_____ !P ( !B 8 %0 ! !W_____ !P !G@ +M < %0 ! ![_____ !P ( !L@ @ %0 ! !3_____ !P ! +MR D %0 ! "X ( M !0 /\1 1 % . "'_____ !P +M !W % ! "+_____ !P ![P $ % ! "/_____ !P +M " @ ( % ! "3_____ !P "%0 , % ! "7_____ +M !P "* 0 % ! ";_____ !P ".P 4 % ! "?_ +M____ !P "3@ 8 % ! "C_____ !P ( "80 < % ! +M "G_____ !P "=@ @ % ! "K_____ !P "B0 D +M% ! "O_____ !P ( "G H % ! "S_____ !P "L0 +M L % ! "W_____ !P "Q P % ! !______ !P " +MUP T % ! #L +P Z !0 /\1 7 $ , ##_____ !P +M "Z@ $ ! #'_____ !P ( "^0 $ $ ! #+_____ !P +M #"@ ( $ ! #/_____ !P #&0 , $ ! #3_____ +M !P #* 0 $ ! #7_____ !P #-P 4 $ ! #;_ +M____ !P #1@ 8 $ ! #?_____ !P #50 < $ ! +M #C_____ !P #9 @ $ ! #G_____ !P #

$P / #W_____ !P # +MH $P ! #[_____ !P #L@ $ $P ! #______ !P +M #Q ( $P ! $#_____ !P ( #U@ , $P ! $'_____ !P +M #Z@ 0 $P ! $+_____ !P #_ 4 $P ! $/_____ +M !P $#@ 8 $P ! $3_____ !P $( < $P ! $7_ +M____ !P $,@ @ $P ! $;_____ !P $1 D $P ! +M $?_____ !P $5@ H $P ! $C_____ !P $: L +M$P ! $G_____ !P ( $>@ P $P ! $K_____ !P $C@ +M T $P ! #O_____ !P $H X $P ! %P 3 !; !0 /\1 +M D %@ 0 $W_____ !P $L@ %@ ! $[_____ !P +M $QP $ %@ ! $______ !P $W ( %@ ! %#_____ !P +M $\0 , %@ ! %'_____ !P %!@ 0 %@ ! %+_____ +M !P %&P 4 %@ ! %/_____ !P ( %, 8 %@ ! %3_ +M____ !P %1P < %@ ! %7_____ !P %7 @ %@ ! +M %;_____ !P %<0 D %@ ! %?_____ !P %A@ H +M%@ ! %C_____ !P ( %FP L %@ ! %G_____ !P %L@ +M P %@ ! %K_____ !P %QP T %@ ! %O_____ !P % +MW X %@ ! $O_____ !P %\0 \ %@ ! H 70!H !0 +M /\1 M $@ , %[_____ !P &!@ $@ ! %______ !P +M ( &%P $ $@ ! &#_____ !P &*@ ( $@ ! &'_____ +M !P &.P , $@ ! &+_____ !P &3 0 $@ ! &/_ +M____ !P &70 4 $@ ! &3_____ !P &;@ 8 $@ ! +M &7_____ !P &?P < $@ ! &;_____ !P &D @ +M$@ ! &?_____ !P &H0 D $@ ! &C_____ !P &L@ +M H $@ ! %S_____ !P ( &PP L $@ !__\ 0 ! !0 1 +M 4 3P " #_____"Q0 8 $1>0 @ "__\ 0 ! !0 +M $1 " $ H 5 #_____ !D ( $A 1 , @ !__\ 0 *"Q0 +M 0 (1 # $ +@ * +_____ !H,!P &U I @$ P8! 4 P $ +M !0 /\1 0 !" #0 ' 3_____ !\ 2(@ "" !0 % +_ +M____ !P &V #" 4 !P ! ;_____ !P( &X / $ %0 ! +M ?_____ !P &]@ / , '@ ! C_____ !P '%0 / 0 +M$0 ! G_____ !P ')P / 8 & ! K_____ !4 /N@ ! +M# <$(@ ! #_____ !4 ( /U@ !" @ (0 !__\ 0 ""Q0 0 /X1 +M> ! ( 3P@6 +_____$QH,!P 'DP " !4 " ! #_____ !@ ( +M ) ( @ &3@ 4__\ 0 ["Q0 0 (1> ! $ + ) +_____ !4( +M /\@ " $ " ! #< P U !0 $1>0 " , * @# 4 ! $ +M !H,!0 'I0 !" @ ! /_____!1D $1 0 ! < +M!@ & !H,!0 'IP $" @ ! 7_____!1D $1 0 ! +M D " ( !H,!0 'J0 '" @ ! ?_____!1D $1 +M 0 ! L "@ * !H,!0 'JP *" @ ! G_____!1D $1 +M 0 ! T # , !H,!0 'K0 -" @ ! O_____!1D $1 +M 0 ! \ #@ . !H,!0 'KP 0" @ ! W_____!1D +M $1 0 ! !$ $ 0 !H,!0 'L0 3" @ ! ______!1D +M $1 0 ! !, $@ 2 !H,!0 'LP 6" @ ! !'_____ +M!1D $1 0 ! !4 % 4 !H,!0 'M0 9" @ ! !/_ +M____!1D $1 0 ! !< %@ 6 !H,!0 'MP <" @ ! +M !7_____!1D $1 0 ! !D & 8 !H,!0 'N0 ?" +M @ ! !?_____!1D $1 0 ! !L &@ : !H,!0 'NP B +M" @ ! !G_____!1D $1 0 ! !T ' < !H,!0 ' +MO0 E" @ ! !O_____!1D $1 0 ! !\ '@ > !H,!0 +M 'OP ! ( @ ! !W_____!1D $1 0 ! "$ ( @ !H, +M!0 'P0 $ ( @ ! !______!1D $1 0 ! ", (@ B +M !H,!0 'PP ' ( @ ! "'_____!1D $1 0 ! "4 +M) D !H,!0 'Q0 * ( @ ! "/_____!1D $1 0 ! +M "< )@ F !H,!0 'QP - ( @ ! "7_____!1D $1 +M 0 ! "D * H !H,!0 'R0 0 ( @ ! "?_____!1D $1 +M 0 ! "L *@ J !H,!0 'RP 3 ( @ ! "G_____!1D $1 +M 0 ! "T + L !H,!0 'S0 6 ( @ ! "O_____!1D +M $1 0 ! "\ +@ N !H,!0 'SP 9 ( @ ! "W_____!1D +M $1 0 ! #$ , P !H,!0 'T0 < ( @ ! "______ +M!1D $1 0 ! #, ,@ R !H,!0 'TP ? ( @ ! #'_ +M____!1D $1 0 ! #4 - T !H,!0 'U0 B ( @ ! +M #/_____!1D $1 0 ! ( -@ V !H,!0 'UP E ( +M @ ! #7_____!1D $1 0 ! #H . Y !0 $1>0 # +M , %@ # #G_____ !X(" 0#@ ! $ "P ! #?_____ !H,!P ' +MYP - $ !P ! #O_____ !X(" 0*@ # < # ! #_____ !H,)0 +M '_0 1 < !@ !__\ 0 $"Q0( 0 (1> ! $ .P ' +_____ !4( +M 01@ " $ -P ! /_____ !X(" 08@ " , -P ! 3_____ +M$QH$!0 (>0 # 4 " ! #_____ !H,)P (@ P 4 " !__\ +M 0 9"Q0 0 1> ( 0 # & +_____ !L 0 6> % @ ! +M /_____ !L ! 6> " ( @ ! 3_____ !L P 6> *" ( +M @ ! 7_____ !L @ 6> % 4 @ ! < !@ & !8,!0 0?@ " +M 0 @ ! 7_____!1D $1 0 ! D " ( !8,!0 0 +MF@ % 0 @ ! ?_____!1D $1 0 ! L "@ * !8,!0 +M 0M@ ( 0 @ ! G_____!1D $1 0 ! T # , !8, +M!0 0T@ "" ( @ ! O_____!1D $1 0 ! \ #@ . +M !8,!0 0[@ %" ( @ ! W_____!1D $1 0 ! !$ +M$ 0 !8,!0 1"@ (" ( @ ! ______!1D $1 0 ! +M !, $@ 2 !8,!0 1)@ " $ @ ! !'_____!1D $1 +M 0 ! !4 % 4 !8,!0 10@ % $ @ ! !/_____!1D $1 +M 0 ! !< %@ 6 !8,!0 17@ ( $ @ ! !7_____!1D $1 +M 0 ! !D & 8 !8,!0 1>@ '! 4 !0P !?_____!1D +M $1 ! 0P &@ : !8,!0 1E@ ' !0P !G_____!1D +M( $1 ! 0P __\ 0 $ !0 1 ! 4 3P # +_____ +M !L, 0 0 6>0 @ ! /_____"Q0( $1>0 $ @ ! 3_ +M____ !L, 0 @ 6>0 ( @ ! #_____ !@ ( + " 30 # +M__\ 0 $"Q0 0 (1>0 ! $ + @4 +_____ !4( 1L@ ! +M+ ! /_____"AX " 1S@ . !( $ ! 3_____ !8,!P 1Z@ A +M !( "0@! #_____ !8 ( 2!@ " $ * @,__\ 0 !"Q0 $1 +M<@ # , " " #_____ !H,)P )# !" !@ !__\ 0 ! !0 0 @ +M /\1>0 " , !@ # #_____ !\ ( 21 P # 29@ '#X +G !QN N +end diff --git a/win/gem/gem_rso.uu b/win/gem/gem_rso.uu new file mode 100644 index 0000000..cd555da --- /dev/null +++ b/win/gem/gem_rso.uu @@ -0,0 +1,22 @@ +begin 777 GEM_RSC.RSO +M4E-/2 1( @ " ! !#__P __\ +M +M '___^!____@ +M #__P #__P +M +M /_ '___^!____@ :1F]1)Y2KX@ +M (\TJP "!$U%3E4 P'1$]!0D]55 '@9$3U%5250 __\ 0 #"E-4 +M05154TQ)3D4 $*1U)!0E-405154P#__P " ,&34%05TE. !DU!4$)/ +M6 0E-05!#55)33U( __\ P #!4%"3U54 "$9,64%"3U54 !!T]+ +M04)/550 ,+3D542$%#2TE-1S __\ ! #!4Q)3D53 "$9,64Q)3D53 +M !!5%,24Y% ""4Q)3D533$E35 #__P % ,(64Y#2$])0T4 +1DQ9 +M64Y#2$])0T4 $(64Y04D]-4%0 ()4T]-14-(05)3 # UE.,0 -0-9 +M3DX #<'04Y90TA!4@ . A#2$]314Y#2 .@5#3U5.5 .P193D]+ /__ +M 8 P=,24Y%1T54 "D9,64Q)3D5'150 $(3$=04D]-4%0 ('3$=2 +M15!,60 P-13$< 0$3$=/2P#__P ' ,)1$E214-424]. #$9,641) +M4D5#5$E/3@ !01$25(Q 5!$1)4CD !<'1$E21$]73@ &05$25)54 #_ +M_P ( ,&35-'5TE. !!55035-' ""D=204)-4T=724X ,%1$Y-4T< +M 0(35-'3$E.15, __\ "0 #!TY!345'150 *1DQ93D%-14=%5 @90 +M3$Y!344 0.3D542$%#2U!)0U154D4 __\ "@ #!5!!1T52 "$9,65!! +@1T52 !!E%004=%4@#__P + ,&3DA)0T]. /____\ +end diff --git a/win/gem/gr_rect.c b/win/gem/gr_rect.c new file mode 100644 index 0000000..0dbe49a --- /dev/null +++ b/win/gem/gr_rect.c @@ -0,0 +1,181 @@ +/* SCCS Id: @(#)gr_rect.c 3.4 2001/12/10 */ +/* Copyright (c) Christian Bressler, 2001 */ +/* NetHack may be freely redistributed. See license for details. */ +/* This is an almost exact copy of qt_clust.cpp */ +/* gr_rect.c */ +#include +#include +#include +#include "gr_rect.h" +dirty_rect *new_dirty_rect(int size){ + dirty_rect *new=NULL; + if(size>0){ + new=(dirty_rect *)calloc(1L,sizeof(dirty_rect)); + if(new){ + new->rects=(GRECT *)calloc((long)size,sizeof(GRECT)); + if(new->rects==NULL){ + free(new); + return(NULL); + } + new->max=size; + } + } + return(new); +} +void delete_dirty_rect(dirty_rect *this){ + if(this==NULL) + return; + if(this->rects) + free(this->rects); + /* In case the Pointer is reused wrongly */ + this->rects=NULL; + this->max=0; + this->used=0; + free(this); +} +static int gc_inside(GRECT *frame,GRECT *test); +static int gc_touch(GRECT *frame,GRECT *test); +static void gc_combine(GRECT *frame,GRECT *test); +static long gc_area(GRECT *area); +int add_dirty_rect(dirty_rect *dr,GRECT *area){ + int cursor; + long lowestcost=9999999L; + int cheapest=-1; + int cheapestmerge1=-1; + int cheapestmerge2=-1; + int merge1; + int merge2; + for (cursor=0; cursorused; cursor++) { + if (gc_inside(&dr->rects[cursor],area)) { + /* Wholly contained already. */ + return(TRUE); + } + } + for (cursor=0; cursorused; cursor++) { + if (gc_touch(&dr->rects[cursor],area)) { + GRECT larger=dr->rects[cursor]; + long cost; + gc_combine(&larger,area); + cost=gc_area(&larger)-gc_area(&dr->rects[cursor]); + if (cost < lowestcost) { + int bad=FALSE,c; + for (c=0; cused && !bad; c++) { + bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; + } + if (!bad) { + cheapest=cursor; + lowestcost=cost; + } + } + } + } + if (cheapest>=0) { + gc_combine(&dr->rects[cheapest],area); + return(TRUE); + } + if (dr->used < dr->max) { + dr->rects[dr->used++]=*area; + return(TRUE); + } + // Do cheapest of: + // add to closest cluster + // do cheapest cluster merge, add to new cluster + lowestcost=9999999L; + cheapest=-1; + for (cursor=0; cursorused; cursor++) { + GRECT larger=dr->rects[cursor]; + long cost; + gc_combine(&larger,area); + cost=gc_area(&larger)-gc_area(&dr->rects[cursor]); + if (cost < lowestcost) { + int bad=FALSE, c; + for (c=0; cused && !bad; c++) { + bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; + } + if (!bad) { + cheapest=cursor; + lowestcost=cost; + } + } + } + // XXX could make an heuristic guess as to whether we + // XXX need to bother looking for a cheap merge. + for (merge1=0; merge1used; merge1++) { + for (merge2=0; merge2used; merge2++) { + if (merge1!=merge2) { + GRECT larger=dr->rects[merge1]; + long cost; + gc_combine(&larger,&dr->rects[merge2]); + cost=gc_area(&larger)-gc_area(&dr->rects[merge1])-gc_area(&dr->rects[merge2]); + if (cost < lowestcost) { + int bad=FALSE, c; + for (c=0; cused && !bad; c++) { + bad=gc_touch(&dr->rects[c],&larger) && c!=cursor; + } + if (!bad) { + cheapestmerge1=merge1; + cheapestmerge2=merge2; + lowestcost=cost; + } + } + } + } + } + if (cheapestmerge1>=0) { + gc_combine(&dr->rects[cheapestmerge1],&dr->rects[cheapestmerge2]); + dr->rects[cheapestmerge2]=dr->rects[dr->used-1]; + dr->rects[dr->used-1]=*area; + } else { + gc_combine(&dr->rects[cheapest],area); + } + // NB: clusters do not intersect (or intersection will + // overwrite). This is a result of the above algorithm, + // given the assumption that (x,y) are ordered topleft + // to bottomright. + return(TRUE); +} +int get_dirty_rect(dirty_rect* dr,GRECT *area){ + if(dr==NULL || area==NULL || dr->rects==NULL || dr->used<=0 || dr->max<=0) + return(FALSE); + *area=dr->rects[--dr->used]; + return(TRUE); +} +int clear_dirty_rect(dirty_rect *dr){ + if(dr) + dr->used=0; + return(TRUE); +} +int resize_dirty_rect(dirty_rect *dr,int new_size){ + return(FALSE); +} +static int gc_inside(GRECT *frame,GRECT *test){ + if(frame && test && frame->g_x<=test->g_x && frame->g_y<=test->g_y && + frame->g_x+frame->g_w>=test->g_x+test->g_w && + frame->g_y+frame->g_h>=test->g_y+test->g_h + ) + return(TRUE); + return(FALSE); +} +static int gc_touch(GRECT *frame,GRECT *test){ + GRECT tmp={test->g_x-1,test->g_y-1,test->g_w+2,test->g_h+2}; + return(rc_intersect(frame,&tmp)); +} +static void gc_combine(GRECT *frame,GRECT *test){ + if(!frame || !test) + return; + if(frame->g_x>test->g_x){ + frame->g_w+=frame->g_x-test->g_x; + frame->g_x=test->g_x; + } + if(frame->g_y>test->g_y){ + frame->g_h+=frame->g_y-test->g_y; + frame->g_y=test->g_y; + } + if(frame->g_x+frame->g_wg_x+test->g_w) + frame->g_w=test->g_x+test->g_w-frame->g_x; + if(frame->g_y+frame->g_hg_y+test->g_h) + frame->g_h=test->g_y+test->g_h-frame->g_y; +} +static long gc_area(GRECT *area){ + return((long)area->g_h*(long)area->g_w); +} diff --git a/win/gem/gr_rect.h b/win/gem/gr_rect.h new file mode 100644 index 0000000..0e4a0bd --- /dev/null +++ b/win/gem/gr_rect.h @@ -0,0 +1,14 @@ +/* gr_rect.h */ +#include +/********** structs **********/ +typedef struct { + GRECT *rects; + int max,used; +} dirty_rect; +/********* functions ************/ +dirty_rect *new_dirty_rect(int size); +void delete_dirty_rect(dirty_rect *this); +int add_dirty_rect(dirty_rect *dr,GRECT *area); +int get_dirty_rect(dirty_rect* dr,GRECT *area); +int clear_dirty_rect(dirty_rect *dr); +int resize_dirty_rect(dirty_rect *dr,int new_size); diff --git a/win/gem/load_img.c b/win/gem/load_img.c new file mode 100644 index 0000000..16d8fe8 --- /dev/null +++ b/win/gem/load_img.c @@ -0,0 +1,289 @@ +#define __TCC_COMPAT__ +#include +#include +#include +#include +#include +#include +#include +#include +#include "load_img.h" + +#ifndef FALSE +#define FALSE 0 +#define TRUE !FALSE +#endif + +/* VDI <-> Device palette order conversion matrixes: */ +/* Four-plane vdi-device */ +int vdi2dev4[]={0,15,1,2,4,6,3,5,7,8,9,10,12,14,11,13}; +/* Two-plane vdi-device */ +int vdi2dev2[]={0,3,1,2}; + +void get_colors(int handle, short *palette, int col){ + int i, idx; + + /* get current color palette */ + for(i=0; i < col; i ++){ + /* device->vdi->device palette order */ + switch(planes){ + case 1: + idx=i;break; + case 2: + idx=vdi2dev2[i];break; + case 4: + idx=vdi2dev4[i];break; + default: + if(i<16) + idx=vdi2dev4[i]; + else + idx= i==255 ? 1 : i; + } + vq_color(handle, i, 0, (int *)palette + idx * 3); + } +} + +void img_set_colors(int handle, short *palette, int col){ + int i, idx, end; + + /* set color palette */ + end=min(1<fd_addr; + + /* initialize some variables and zero temp. line buffer */ + mplanes=min(image->fd_nplanes, planes); + /* convert image */ + line_addr=image_addr; + buf_addr=screen_addr; + if(mplanes>1){ + /* cut/pad color planes into temp buf */ + for(plane=0; planefd_addr); + /* convert image line in temp into current device raster format */ + if((new1_addr=(char *) calloc(1,new_size)) == NULL) + return(FALSE); + dev_form.fd_addr=new1_addr; + vr_trnfm(x_handle, &tmp, &dev_form); + free(new_addr); + + /* change image description */ + image->fd_stand=0; /* device format */ + image->fd_addr=new1_addr; + image->fd_nplanes=planes; + return(TRUE); +} + +int transform_img(MFDB *image){ /* return FALSE if transform_img fails */ + int success; + long size; + + if(!image->fd_addr) + return(FALSE); + + size=(long)((long)image->fd_wdwidth * (long)image->fd_h); + success=convert(image, size); /* Use vr_trfm(), which needs quite a lot memory. */ + if(success) return(TRUE); +/* else show_error(ERR_ALLOC); */ + return(FALSE); +} + + +/* Loads & depacks IMG (0 if succeded, else error). */ +/* Bitplanes are one after another in address IMG_HEADER.addr. */ +int depack_img(char *name, IMG_header *pic){ + int b, line, plane, width, word_aligned, opcode, patt_len, pal_size, + byte_repeat, patt_repeat, scan_repeat, error = FALSE; + char *pattern, *to, *endline, *puffer, sol_pat; + long size; + FILE *fp; + + if((fp = fopen(name, "rb")) == NULL) + return(ERR_FILE); + + setvbuf(fp,NULL,_IOLBF,BUFSIZ); + + /* read header info (bw & ximg) into image structure */ + fread((char *)&(pic->version), 2, 8 + 3, fp); + + /* only 2-256 color imgs */ + if(pic->planes < 1 || pic->planes > 8){ + error = ERR_COLOR; + goto end_depack; + } + + /* if XIMG, read info */ + if(pic->magic == XIMG && pic->paltype == 0){ + pal_size = (1 << pic->planes) * 3 * 2; + if((pic->palette = (short *)calloc(1,pal_size))){ + fread((char *)pic->palette, 1, pal_size, fp); + } + }else{ + pic->palette = NULL; + } + + /* width in bytes word aliged */ + word_aligned = (pic->img_w + 15) >> 4; + word_aligned <<= 1; + + /* width byte aligned */ + width = (pic->img_w + 7) >> 3; + + /* allocate memory for the picture */ + free(pic->addr); + size = (long)((long)word_aligned * (long)pic->img_h * (long)pic->planes); /*MAR*/ + + /* check for header validity & malloc long... */ + if (pic->length > 7 && pic->planes < 33 && pic->img_w > 0 && pic->img_h > 0){ + if(!(pic->addr=(char *)calloc(1,size))){ + error = ERR_ALLOC; + goto end_depack; + } + }else{ + error = ERR_HEADER; + goto end_depack; + } + + patt_len = pic->pat_len; + + /* jump over the header and possible (XIMG) info */ + fseek(fp, (long) pic->length * 2L, SEEK_SET); + + for(line=0,to=pic->addr; lineimg_h; line+=scan_repeat){ /* depack whole img */ + for(plane=0,scan_repeat=1; planeplanes; plane++){ /* depack one scan line */ + puffer=to=pic->addr+(long)(line+plane*pic->img_h)*(long)word_aligned; + endline=puffer+width; + do{ /* depack one line in one bitplane */ + switch((opcode= fgetc(fp))){ + case 0: /* pattern or scan repeat */ + if((patt_repeat=fgetc(fp))){ /* repeat a pattern */ + fread(to,patt_len,1,fp); + pattern=to; + to+=patt_len; + while(--patt_repeat){ /* copy pattern */ + memcpy(to,pattern,patt_len); + to+=patt_len; + } + }else{ /* repeat a line */ + if(fgetc(fp)==0xFF) + scan_repeat=fgetc(fp); + else{ + error = ERR_DEPACK; + goto end_depack; + } + } + break; + case 0x80: /* Literal */ + byte_repeat=fgetc(fp); + fread(to,byte_repeat,1,fp); + to+=byte_repeat; + break; + default: /* Solid run */ + byte_repeat = opcode & 0x7F; + sol_pat = opcode&0x80 ? 0xFF : 0x00; + while(byte_repeat--) *to++=sol_pat; + } + }while(to pic->img_h) + scan_repeat = pic->img_h - line; + /* copy line to image buffer */ + if(scan_repeat>1){ + /* calculate address of a current line in a current bitplane */ +/* to=pic->addr+(long)(line+1+plane*pic->img_h)*(long)word_aligned;*/ + for(b=scan_repeat-1; b; --b){ + memcpy(to, puffer, width); + to+=word_aligned; + } + } + }else{ + error = ERR_DEPACK; + goto end_depack; + } + } + } + + end_depack: + fclose(fp); + return(error); +} + +int half_img(MFDB *s, MFDB *d){ + int pxy[8], i, j; + MFDB tmp; + + mfdb(&tmp,NULL,s->fd_w/2,s->fd_h,s->fd_stand,s->fd_nplanes); + tmp.fd_w=s->fd_w/2; + tmp.fd_addr=calloc(1,mfdb_size(&tmp)); + if(!tmp.fd_addr) + return(FALSE); + + pxy[1]=pxy[5]=0; + pxy[3]=pxy[7]=s->fd_h-1; + for(i=0;ifd_w/2;i++){ + pxy[0]=pxy[2]=2*i; + pxy[4]=pxy[6]=i; + vro_cpyfm(x_handle,S_ONLY,pxy,s,&tmp); + } + pxy[0]=pxy[4]=0; + pxy[2]=pxy[6]=s->fd_w/2-1; + for(j=0;jfd_h/2;j++){ + pxy[1]=pxy[3]=2*j; + pxy[5]=pxy[7]=j; + vro_cpyfm(x_handle,S_ONLY,pxy,&tmp,d); + } + free(tmp.fd_addr); + return(TRUE); +} diff --git a/win/gem/tile2img.c b/win/gem/tile2img.c new file mode 100644 index 0000000..fa52fed --- /dev/null +++ b/win/gem/tile2img.c @@ -0,0 +1,156 @@ +/* SCCS Id: @(#)tile2bmp.c 3.2 95/09/06 */ +/* Copyright (c) NetHack PC Development Team 1995 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Edit History: + * + * Initial Creation M.Allison 94/01/11 + * Marvin was here Marvin 97/01/11 + * + */ + +/* #include */ +#include "hack.h" +#include "tile.h" +#include "bitmfile.h" + +/* #define COLORS_IN_USE MAXCOLORMAPSIZE /* 256 colors */ +#define COLORS_IN_USE 16 /* 16 colors */ + + +extern char *FDECL(tilename, (int, int)); +static void FDECL(build_ximgtile,(pixel (*)[TILE_X])); +void get_color(unsigned int colind, struct RGB *rgb); +void get_pixel(int x, int y, unsigned int *colind); + +#if COLORS_IN_USE==16 +#define MAX_X 320 /* 2 per byte, 4 bits per pixel */ +#else +#define MAX_X 640 +#endif +#define MAX_Y 1200 + +FILE *tibfile2; + +pixel tilepixels[TILE_Y][TILE_X]; + +char *tilefiles[] = { "..\\win\\share\\monsters.txt", + "..\\win\\share\\objects.txt", + "..\\win\\share\\other.txt"}; + +unsigned int **Bild_daten; +int num_colors = 0; +int tilecount; +int max_tiles_in_row = 40; +int tiles_in_row; +int filenum; +int initflag; +int yoffset,xoffset; +char bmpname[128]; +FILE *fp; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int i; + + if (argc != 2) { + Fprintf(stderr, "usage: tile2img outfile.img\n"); + exit(EXIT_FAILURE); + } else + strcpy(bmpname, argv[1]); + +#ifdef OBSOLETE + bmpfile2 = fopen(NETHACK_PACKED_TILEFILE, WRBMODE); + if (bmpfile2 == (FILE *)0) { + Fprintf(stderr, "Unable to open output file %s\n", + NETHACK_PACKED_TILEFILE); + exit(EXIT_FAILURE); + } +#endif + + tilecount = 0; + xoffset = yoffset = 0; + initflag = 0; + filenum = 0; + fp = fopen(bmpname,"wb"); + if (!fp) { + printf("Error creating tile file %s, aborting.\n",bmpname); + exit(1); + } + fclose(fp); + + Bild_daten=(unsigned int **)malloc(MAX_Y*sizeof(unsigned int *)); + for(i=0;i 62) { + Fprintf(stderr, "too many colors (%d)\n", num_colors); + exit(EXIT_FAILURE); + } + while (read_text_tile(tilepixels)) { + build_ximgtile(tilepixels); + tilecount++; + xoffset += TILE_X; + if (xoffset >= MAX_X) { + yoffset += TILE_Y; + xoffset = 0; + } + } + (void) fclose_text_file(); + ++filenum; + } + Fprintf(stderr, "Total of %d tiles in memory.\n",tilecount); + + bitmap_to_file(XIMG, MAX_X, (tilecount/20+1)*16, 372, 372, 4, 16, bmpname, get_color, get_pixel ) ; + + Fprintf(stderr, "Total of %d tiles written to %s.\n",tilecount, bmpname); + + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +void get_color(unsigned int colind, struct RGB *rgb){ + rgb->r=(1000L*(long)ColorMap[CM_RED][colind])/0xFF; + rgb->g=(1000L*(long)ColorMap[CM_GREEN][colind])/0xFF; + rgb->b=(1000L*(long)ColorMap[CM_BLUE][colind])/0xFF; +} + +void get_pixel(int x, int y, unsigned int *colind){ + *colind=Bild_daten[y][x]; +} + +static void +build_ximgtile(pixels) +pixel (*pixels)[TILE_X]; +{ + int cur_x, cur_y, cur_color; + int x,y; + + for (cur_y = 0; cur_y < TILE_Y; cur_y++) { + for (cur_x = 0; cur_x < TILE_X; cur_x++) { + for (cur_color = 0; cur_color < num_colors; cur_color++) { + if (ColorMap[CM_RED][cur_color] == pixels[cur_y][cur_x].r && + ColorMap[CM_GREEN][cur_color] == pixels[cur_y][cur_x].g && + ColorMap[CM_BLUE][cur_color] == pixels[cur_y][cur_x].b) + break; + } + if (cur_color >= num_colors) + Fprintf(stderr, "color not in colormap!\n"); + y = cur_y + yoffset; + x = cur_x + xoffset; + Bild_daten[y][x] =cur_color; + } + } +} diff --git a/win/gem/title.uu b/win/gem/title.uu new file mode 100644 index 0000000..9fda441 --- /dev/null +++ b/win/gem/title.uu @@ -0,0 +1,426 @@ +begin 777 title.img +M $ .P $ $!= %T 4 R%A)34< /H ^@#Z /H &K ^@ /H ^@ +M #Z /H #Z LH#Z &K CT"R@$: :L!JP+* 1H CT /H +M :L (] 1H -; UL"R@/H LH"/0 _PDH*"BH!H ) ?_@'_^_ +M___'A( /_#_\!__X _P#__P?__P!B@HAH )_@ ?X ! X!( / \ #^ ' +M__ /\ /@ /A@>!@ ?@#_^____'@X 0W_@?^ /_\ __ ?_X!__P 8H!X ( +M?\ '_Q___X.#@ ^/\ _P ?_@ ?P ?_ #_\'AX (?]_W_U___[N#@!"O]^_W +M_?_O_\?S^?_?[_\_A@> "& P!@,8 #& X !V( .&!@8 8 P '@.$8# # ,' +M* > "#_@ _X/__^#@X /C_ /\ #_X ?_ #_@ ?^!X> "* O^@+H "Z X / +MJ!?H%_Z +_^8#>Z O_0"AP> "& P P8, #& X /S# ,, & 8 #@ Q&!P P& +M!R@'@ @_X '\!___@X. #X?@!^ _\ ?_X _X '_ >'@ B@+_T%] N@. +M#[0O]"_^@%__8 +N@;_T!8<'@ @P, &&# Q@. #\PP## !@& !P &Q@X , +M# "- O_H7T "Z X /M"_T+_Z 7_Z (Z" +M_^@7AP6 "O@ ,!X!A_P /X#@!#\/XP_@8!_ P \8/^&#_\!@B !P8 ? +M #@#@ !N@O_07X<%@!U\ # ? 8?L/_S^ +M?P'X_#\,/P, /@<'_#&#_&!_X 8%@ H0 ' '@ X X ', \ #P #@6 +M!/P '^ &!8 *' ?]P#]Y___NX. $+?O!^\!_^X!_A_@_OP_W^ &A8 <[__0 +M%_Z%]#_\NG\!^+0O]"_] "_Y!APN@O^@7X<%@!T\ # / 8?L?__^_X/__#\, +M/P, /@8/_CN#^&#_P 8%@ H0 # '@ X X 0, \ #P #@ !X H ^ _ +MP 8%@!T< !_[ /WGP .[@/X'M^\'[P'_[@/YX^K^^#^_P :%@!SO_] +_H7T +M0 .Z@(('M"_T+_T +_H)XBZ"_Z"_AP6 "#P , \!A^Q_@X 2@__\/PP_ P ^ +M!A__/X/PP/^ !@6 '1@ , > ?_#A^ /@P#P / . ?X#@#P #^ !@6 +M'1P '_L _>??_+M^_OBW[P?O ?_N _?Y[O[P?[^ !H6 '.?_T O^A?1?_+M^ +M@OBT+_0O_0 O^A?Y+H+_0+^'!8 ??_SI^_O\W[P?O ?_N ^_^[O[P_W\'A8 ?_[A^ /^P#P / ' !__#@#P /X'!8 <'X ?^X#]Y]__N'[^ +M_[?O!^\!^_<'W_]N_O#^_@>%@!OG_] +_H7T7_^Y_H+_M"_T+_T*%_1?_VZ" +M_H*(!8 ??__A^_O_W[P?O _OW +M!]__KO[Q_OX'A8 ;Y__0!?Z%]%__^?Z"__0O]"_Z"A?T7_^N@OT"B 6 '#_ +M, ?!A^Q___Q_@__\/PP_!@X?#/_A_X/V!_P'!8 <&\ < !X!__^'X __ / +M \ < /^'. / !_ <%@!P?P!_]P/WGW__X?O[_]^\'[P/[]P>_X<[^\_W\ +M!X6 &^?_T 7^A?1?__O^@O_T+_0O^@H7]+__SH+Z!8@%@!P_P# 'P8?L?@ \ +M>8/P?#\,/P8>'QS_@'^#_ _\!P6 '!O ' > > #QX /!P#P / ' #^ +M;@#P _P'!8 <'\ ?_<#]Y]X /'C^\'?O!^\#\_<'OX!N_O?[_ >%@!OG_] % +M_H7T7____H+_]"_T+_H2%^2__^Z"] N(!8 ?> QP_O '[P?O _7W#W\ 'O[W^_@'A8 ;Y__0 OZ%]%____Z" +M__0O]"_Z%1?I?__^@O0+B 6 '#_ , /!A^Q^ 1AH_ ,/_P_!A\?F?X #X/X +M'_ '!8 -&\ , !X!X !& \ . # \ ! > ?@ . . '\ <%@!P?P!_^P/WG +MW@ $8-[P!^ '[P/U]X]^ [^[_?P!X6 &^?_T +^A?1?___^@O_T( 0O^A47 +MZ7___H+H%X@%@!P_P# #X8?L?@ 88/P#!_X/PP?#YG^ >#\#_P!P6 #1O +M #@ > > !@ / #@ P/ 0#@'X !@# #_ '!8 <'\ ?_N#]Y]X &#^\ ?_ +M_^\']?N/?@ &_M_O\ >%@!OG_] "_H7T7____H+_]!_X+_05"^E___Z"T"^( +M!8 ?> ! GO '___O!_7[CWP +M O[?[^ 'A8 ;Y__0 OZ%]%____Z"__0 "_T%0OI?__^@M OB 6 '#_ , 'A +MA^Q^ !Z_ , _##\/F?P 8/@?\ '!8 (&\ & !X!X$@ 'P X &#P , +M X!\!( "'\ '!8 ('\ ?_V#]Y]X#@!&6\ ?__^\'[?N/? _C_?P >%@!OG +M_] !?H7T7____H+_] +_0M"^E___Z"(%^(!8 >!( !\ . !@\ #@. > 2 C^ !P6 "!_ 'O]P_>?> +M X 1_O '___O!^[[GW@ /__OX 'A8 ;Y__2@7Z%]%____Z"__0 "_T+HO1 +M?__^@ "_B 6 &W_ ,X#QA^Q^! !F_ , !_##^'\?@ 8 !_P@%@ @[P +M, '@'@2 ? #@ 8/ X!P'@$@ %_" 6 "#_ 'O^P_>?> X 0YO '__^O!^[] +MWW@ /__?PB%@!O'_]* OH7T7_O__H+_] +_0NA=%___Z 7^(@ $.!(& +M&L S@/&'['X, '#\ P #\8/X?S^ !@ '_"( !#@2 "#O P > >!( ! +M\ . !@\ #@' ^ 2 7\(!8 (/\ >_[#]Y]X#@!"^\ ?__^\/[OW>^ __]_ +M"(6 &\?_TH"^A?1?\__^@O_T OZ"Z%TO___H !?XB (!^ !_\ SP/F' +M['X\ &+\ P?^'\8?X?S^ !@ /^"( "'X #@ A[P . '@'@2 ? #@ 8/ +M !X!P/@$@ '^" 6 &W_ 'G^X_>?>" ]O '__^O#][]WO@ /_^_@B%@!J' +M_]) OH7T7\O__H+_]!_X+^A>A=+___Z HF 3.$@!O\ #/ P88,8?P << +M#+_\,!AAAC, & 8 #@ )Y7@. 3^$@ 'X'8 "!_X#@ $,!( 5_ >?X#\ +M!\ X "Z =@!^ /P/P> X($@ )__@.%@!4$ !) @(0$0#@ (( !" $( A +MA!(#@ * 02 G@!@X @;X . #-@888,?\P ;\ ## ,,!AAAC, &! +M@8 %@ -UP " 7^$@ & 'X ##\ @!H0?____? 'C_ _ ? ^ P@ 'X ?@ +M#\#\'@."!H #?\ A( 6_G $B! A 1 R @@ $( 0@"$"$$@. H&!!H # +M<#__@ %?A * &3-@888,/PP 8, #K ,82 +M 7 @@ (# ( !( . %@.( !X_P/P'__@ /X !6 'H _ ?AX#@ /^?X &@ (/ +M (2 %OSX !(@0(0$/P@ (( !" $( A 0A(#@ ."0( &@ (,_X2 '/X #,P +M888, P ?, #+ -L!A@PS, &#X, '@ ' @ '@@ 55555^^"& 4 $@!8' +M! >'\#\!__X ". =@!F /P'X> X #_C^ !X !P(2 %OF, !(00(0$ @ +M (( !" $( A 0A(#@ ."(( '@ &_A( <_ ,S QA@P C !XP ,, SP-,## +M,P 8-@P > 3" < #@ (=O"& 1 $@!8& @ >'^#\!_]X "> ?@!R ; +M@'X> X #_C^ !X !,(2 %OL& !(0((0$ @ (( !" $(!" 0A(#@ ."(( ' +M@ $OA( 6_ '_\_@_A_P)#___T__\/_T_\O_#\X. X/P?X> ?" < #@ (- +M_"($@!P& __>[^[]]_;[__ZN__?O]N_=OW[>___^_M_?AX !\(2 '/L'_]+H +M+H7T O__H+_]"_T+]"_0M+___Z"T%^(A( 6Z '_\_@_A_P'[___@__\?_R_ +M^?_#\X. X/P?X> >" 8 #@!P)_?_ X X!\ #__X __ /\ _ /P# ___^ +M , ?AX !X 2 '!(#_][O[OWW__O__O[_]Z_W;]:_?M[___[^W]^'@ '@A8 5 +M!@ 2"""$! ?H "" 0@!" 0@$(2 X #@A! !X !'X& #*JJJH !__/X/Z?\ +M/X. "L?__'_\__?_P_.#@ .#\'^'@ $P@ & !( ;^?_ X X!\ #__X __ / +M\ _ /P# ___^ , ?AX !, & 'U5557D%_][O[MWW^!O__KK_]Z_W+]B_?M[_ +M__[^W]^'@ $PA8 5C@ 2"""$!#@8 "" 0@!" 0@$(2 X #@A! !X& 5\$ +M@ <#__/\/[?\A( *@__]/_X_\/_!\X. X/X/X: L7\!8 ;<__ \ 8!\ ?C +M__X __ /\ _ !P! ___^ . /AH "Q?R 2"#@!S\B__>]];-]T?C__[^__;O +M]>_?AW]>___^_N_OAH "Q?R%@ C\ !($$(0$0 . "H( !" $(!" 01(#@ ." +M"" &@ (__X !! 2 " ?_]_R?C_Q_@X *@__]O_T_X7_A\X. \/X/X2 !/P) +M__X%@!L'_\#P!@'P'_O__@#_\ _P#X $#___X X ^$@ 3\"?_^@ %[@X < +M_G?_VO=V]???^__^_O_V;_;OOOA?7O___K[O[X2 !/P)__Z%@ CX !($$(0$ +M0 . "H( !" $("!X01(#@ .""" $@ $#@P6 "(__^_X_E_Q_@X *S___/_^_ +MZQ_I]X. YOX/X. H"?@P6 &XO_P/@& ? ?___^ /_P#_ /@ 0/___@#@ +M#X. H"?@X !/X2 &X__UOO6[???___^LO_T[_1OM.>76O___N;O[X. H"? +M@X6 "'0 $@(0A 1 X *@@ $( 0@( >!$@. X((( . 7^$ 8 !?X6 !?/_ +M#^?]A( *Q__\/_T_?VS1\X. \/\'X> ?X!@!5____^>__ ^ (!\!____X +M__ /\ \#@ = ___^ / 'AX !_H !'X. '/Y__][Z^IWV7____KK_]^_V[R"3 +M+U[___Z^]_>'@ '^A8 ;A__2^@J%]%____Z"__0O]"^@ %2___^@O07B( ! +M 8: !O/_G__\?X. "OO__+__?W:.>_.#@ .C_!^'@ 'X@ $!A( 1.__ _ (! +M\!____X __ /\ \#@ = ___^ / 'AX !^( !#X2 &S__WOUJA???___^AO_W +M;_2O*7&%7O___M[W]X> ?B%@!O'_]+]"H7T7____H+_]"_T+Z 5+___Z" +M]!>(@ $'AH %\_\OK_R'@ 5__/]]](6 X/^'X> :" 0>$@!&[_\#\ @'P +M'____@#_\ _P#P2"@ 3^ /@'AX !H( !!X2 &[__WOW:U?=?___^@O_TK_ :"%@!O'_]+]"H7T7____H+_]"_T+Z( )+___Z"^A>( +M@ $'B( #G\_]A( *B___/_]_>?WW]X. ^?^#X: F@ @ $'A( 1F__ _@(! +M\!____X __ /\ \$@H $_@#X X: F@ @ $'A( ;G__2_NJU]E____[V__3O +M]*\G_@B:___^FOO[AH ": "%@!OG_]+^BH7T7____H+_]"_TKZ/\ )K___Z" +M^@N(@ $#AH &]_^7__Q_@X %V___?_Z#@ +L]X2 OX/A( $[4 ( ! X2 +M$=O_P/X! ? ?___^ /_P#_ /!( &?__^ /@#A( $[4 ( ! X2 &]__VO[M +MA???___^IO_TK_4O0@/SF7___H+[^X2 !.U "%@!OG_]+^A87T7____H;_ +M]"_U+T(#X)E___Z"^@N(@ (!^ . '#_ ,_'GU^_^ !O_ -?PW_[__^_? +M ?_S3X '@ (!^ . "!O / > >!( *\ / \ _ @< . _ #@ > @'X +M X ('\ >\)LMY%X#@!'"\ :O!B]2_!&K< @O&[@ >%@!OG_]+^@P7T7___ +M_L+_]"_T+T+\$*E___Z"_0N( 8 !X . '#_ /_#5G^]^ !@_ ,/P[_U__Z +M__ ??S3X ' 8 !X . "!O / > >!( *\ / \ _\ @< . _ !@ %@!OG_]+_0 7T +M7____M[_]*_T+V+_T*E___Z"_06(!8 %@!OG_]+_0 7T7__]_NK_]"_T +M;T+_TDE___Z"_06(!8 %@!OG_]+_H 7T7__]_IK_]&_U;T+_T4M___Z" +M_H6(!8 @O""P >%@!OG_]+_H 7U7__Y_J;_]._U+M7_ZDE___Z"_H*(!8 % +M@!OG_]+_T 7W7__Y_H[_]B_V+L7_[%:___Z2_H*(!8 %@!SG_]+_T 7T +M7__U_O;_]:_U;HW_[-2___:2_T%_AP6 '#_ -_ V[^[^ XAB_ /OPS_K_ 8 +M?O@ ">_P]> '!8 6&\ \ !X!X " \ / X!\ 0. . _ 8 <%@!P? +MP!KP&17E7@ $(/;P!&\'+M7P#]6X "2\$M@!X6 '.?_TO_0%?1?__7^]O_T +M;_4NU?_N5;__]H+_07^'!8 9S_#[ +MX <%@!8;P #P '@'@ ( #P \ #@'P 8 X #\ !@!P6 '!_ $O +5>1> +M P@MO 'KP?N[? *IM@ ++P16 'A8 O]^[E_^JF +MW__FDO]!?X<%@!P_P#?P'=?O_@ V89/P##\,/R_@&O_X !G'\'7P!P6 %AO +M / > > !@ / #P , ^ "!@#@ /P # '!8 <'\ :\ HMY%X '&#N\ ?O +M!^W;X TJ6 NO JL >%@!SG_]+_Z@WT7__=_N[_]^_W[=O_[2I?_^::_ZB_ +MAP6 '#_ /_ /M^U^ .9AP_ ,/PP^#^ ,?WP .?_P?_ '!8 6&\ \ !X!X +M & \ / @#X (' . _ , <%@!P?P!+P!$WFW@ \8+[P!^\'Z_O@!ZK< +M !""\""P!X6 '.?_TO_T3?;?_SW^OO_W[_?K^__WJM__UH+_H+^'!8 => /S@_W /]P_W^^ 'Z^P ,.+P(+@' +MA8 <[__6__5=]U_\_?[_?^_W[_?[__ > #@ ' P > # 0# +M@ /P !@'!8 <'\ 2\ *EY]X#_.#_< _[#__]X ?M] !@WO 96 >%@!SO_]+_ +M^J7WWP/]_O]_[_OO__W_]^WW_V[>_]!?AP6 '/O /_ &!^S__ ?C ? __S__ +M_^ , ! !X $!( #\ 8!P6 +M'+O $O #_>=0__WA_[ < 0 /> 'U?@ X/[P$=@'A8 '@ X$!@0& \ !@2 _ ' <%@!S[P!KP ?WG +MS__]X?X /\ _P# #_;^ <#N\!3 #-K__?WWS__]_?X/@X. "\#_[_;^ +M =[N_]1?AP6 ' / ,_ #!^PP ?G__ _P#_?\ __\'^8Z/P'_P'!8 < \ +M\ !X > !\ #_ /]_P ' " / # <%@!P#P![P ?WG___]X@'P /\ +M_W_ ?=__\+>\ @L!X> "MK__?WW___]^@&'@ G!]W__WM[_Z"^'!8 < \ _ +M\ ,'[ #__\?^ '_@?__P '_< #'@_ ;/ <%@!P#P #P '@ !X!_X ?^! +M___ , 8 \ ,!P6 ' / $O !_>?___G@'_@!_X'__\ &S__AO[P#.P' +MAX )TO_]_??___G@B( )_AN__[[^_^QOAP6 ' ? ,_ !A^P ?__@'_@!_\'_ +M_^ _SX#CX/P#_X'!8 "-[__OWW__P'BH ( +MX<__?O[_]"^'!8 <#X _\ &'[ ___^ __ /_X___X '_O_\?@_ .W@<%@!P/ +M@ #P '@ /_X#_\ __C___@ ?^P !X \ &!P6 ' ^ $O _>?_@__@/_P# +M_^/__^ !_['\'O[P!38'AX 'TO_^_??_@XR !_'\_O[_]#>'!8 &#X S\ #' +MA( 2X#__!\ /P ?^?__\#\ Q^!P6 ' ^ / > ?__@/_\'P _ ! +M_YX#^ #P 8'!8 <#X >\ !][_!__^ __P? #\ '_G@/Y_O 'E@>'@ ?6 +M__]][_!_C( '_@/Y_O_WEX<'@ 0W\ #'A( #X'_@!H ) _^/__\!\ 9?!PB +M"O > /___@?^ &@ D#_X__\ !P <'!X +&O ?>P/___@?^ &@ D#_X__ +M]_]P [<'AX &VO__?>P/CX %]_]_^[>'!X %,_ 9_R#@ +A\ B "!_'___! +M\ :O!PB !/ <"#@ +A\ B "!_'_^ < #!P> !1[P #W @X "X? (@ @? +MQ__@_W #6P>'@ 7>__^]PY" !>#_?_M;AP> !3/P '_@@X !@ F " 'C__CY +M\ 8/!PB !/ <"#@ & "8 ( >/_^ P ,'!X %'O .<"#@ & "8 ( >/_ +M^ >P _L'AX %WO__N=^1@ 0'O_O[AP> "#/P #_@___ #(& !O ?\ 8O@ 8( +M@ ?P ?@___ #(& !O 0$ #@ 8'@ @>\ 'X/__P R!@ ;P$% #VX &AX $ +MWO__QY* !/!?^]N'!X '8? #^'_^ V !Q_ ?_ &!X &"( &< /X?_X#8 ' +M'\!_@ !@ 8'@ <_< /X?_X#8 ''\!_@ /]@ :'@ *_?Y6 X_[_8<'@ 9@ +M\ /X?\0@8 $^ P'@ 8(@ 40 _A_Q"!@ 3X & !@> !C^0 _A_Q"!@ 3X +M!_V !H> K^?EH "]_V'!X &8'@ !^/ #X & ?_X?P? !@J P?CP ^ !@'_ +M^ !P 8'@ 8_X 'X\ /@ 8!__@/_< &AX "O^>6@ */_8<'@ 7!_ 'XA& +M!0_\#\/ !@J @?B$8 %#_P , &!X %?^ !^(1@ 4/_ #^P :'@ )_XY: +M O#^AP> !<_P . $H $_@#[P 8(@ 00 . $H $_@ P 8'@ 5^$ #@!* +M!/X /L &AX "?A^7@ $^AP: P&_^!6 ! < ?^ &!X " ?@5@ 0' $!@!@> +M O'X%8 $!P!'8 :&@ +^\9B L=_A@: P/W_A>!@ '@!@> @?^%X "^" & +M!H # P?_@!6 +M @?^!P: ! ('_X 5@ ('_@>&@ +Z/YB OX?A@: ! X/_\ 6@8 !\ 8'@ ,/ +M_\ 6@8 !P 8'@ ,/_\ 6@8 !P :&@ 'QFH !SX8'@ (/_A> @_P!@> @_^ +M%X "#_ &!X "#_X7@ (/\ :H!X "'^ 7@ (#^ 8'@ (?X!> @/X!@> A_@ +M%X " _@&J > 3\9@ %\!@> 3\9@ %\!@> 3\9@ %\!J@'@ %\&8 !'@8' +M@ %\&8 !'@8'@ %\&8 !'@:H!X !X!F 0,&!X !X!F 0,&!X !X!F 0,& +MJ"@H'8 !. JH*"@<@ (!_@JH"X "'X #@ (#P!8+@ '\!( " \ %@ '\$ N +M > $@ (#P 6 ?P$@ (#_PJ+@ +\?X. OP_E@N A^ X "#/ 6"H " _P$ +M@ (/\ 2 @'^$ J @/@!( "#_ $@ (#_P2 P/_@ F+@ +\?X. O /E@N +M A_ X "&?@6"H "!_P$@ (?^ 6 ?P0"H "!^ $@ (?^ 2 P?_@ . P?_ +M@ F+@ +\/X. N 'E@J P?^0 . A/X%@J @_@!( "'_@%@ '\!8 !_ H* +M@ $(!8 "'_@$@ ,'_X #@ ,'_X )BX "X;^#@ +@!Y8*@ ('_@2 B?\%@J +M A_@!( "/_P$@ (!_@6 ?P*"H !& 6 C_\!( #!_^ X #!_^ "8N >&$ +M@ + Y8*@ (/_ 2 B_\%@J A_P!( "/_P$@ (!_@2 @'^"@J 1 %@ (_ +M_ 2 P?_@ . P?_@ F+@ 'SA( "P .%@ $SA8 !,XH*@ (/_ 2 C_\!8 ! +M9 6 :@*"H "/_@$@ (__ 2 P??@ . @'>"@J C?@!( "/_P$@ ,'WX # +M@ ,'WX )BX !^X2 L #A8 !=X6 ;N*"H "'_P$@ (YG 6 2 %@ $@"@J +M C_X!( ".9P$@ (#WP2 @'>"@J B?P!( "/_P$@ ,#WX #@ ,'WX )BX ! +M^X2 L #E@J AZ\!( "/_P%@ $@!8 !( H*@ (^N 2 C_\!( " ]\$@ (! +MW@H*@ (O\ 2 C_\!( " ]\$@ ,'WX )BX !^X2 L9CE@J AUO 2 AB(!8 !> 6 7@*"H "/K@$@ (?^ 2 @&&!( " +M 88*"H "+_ $@ (7> 2 @&'!( #/X?@"8N ?N$@ +O]X6 8>%@ &'B@J +M A_\!( "*!06"H "/_@$@ (O] 6 ?P%@ '\"@J BO0!( "+_0%@ '^!( # +M/__P"8N ?N$@ +7ZY8*@ ([W 2 @O0!( # P. X $ 08 < @*@ (_^ 2 +M @PP!( # P. !( #? !P" J @PP!( "## %@ '\!( $/OWP< B+@ '[A( " +M_#^*@ /X_'^)"H ".]P$@ (($ 2 ! >'P#@#@ -^#_@("H "/#@$@ (/\ 2 +M! >'@#@#@ -\#_@("H "## $@ (/\ 6 "7@ . /_W_^ B*@ +\.XR ;^# +M@ /X_'^)"H "/#P$@ (,, 2 "@__X/P 0$#_X("H "/_ $@ (/\ 2 ! O_ +M@/P$@ (/_@@*@ (+T 2 @_P!X '_ ._O__@B+@ 'SBH #^_^?@X "^'B* +M"H 6/_\/_ !^ !^ '__]_@ '@#__@@*@!8_\P_\ '__X'X 8 W^ +M 8 _^" J "@PS#_P ?__@?@$@ @-_@ ?\/_@B+@ 'S@X $_@?@?X. +M _@ #X. _@!#XD*@ T__[_^ #__X?\ _@P* !#\!__\("H 6/^.__@ ' +M___G_ ,_\'_P . /_P@*@ H'X[_^ ?__^?\!( "!_\#@ /^#_\(BX ! +MXX. !/@'X!^#@ /S_P>#@ /X @^)"H !/X,!@ @"__]O_ ?X,"@ 1_P___ +M" J %C@'__\ !O__;_P &/_!_\ & !_\("X ) ___ ;__V_\!( "!_\# +M@ ,\!_\(BX !YX. !/D#P)^#@ /C_P>#@ /@ >)"H !#X. "8 #?_[__ +M^X,"A @)@!__?_\ #Z___^ #_\___" F %P/_^#__@ ^__?_\ #8_@'^ #/X /_ +M" F $0,8 #__@ ^__?_\ # '^ X #% /_"(N "/@____P0 (/@X #V_X! +M@X #S_0#B0F %P'G'__[@ ??^__\ 'Z___X '_\__^" F %P?_X!_[@ _? +M^__\ ' _$'X '/X '^" F $088X!_[@ _?^__\ # $'X X #% '^"(N +M". ?___P( 0/@X #P_Q!@X #S_0!B0F %P/P'__Q@ >O=?_X '#/^/@ ![ +M]_G\" F %P__X _Q@ ^O]?_X ' ''W@ '; .?\" F %PP/X _Q@ ^O]?_X +M # 'W@ &8]^?\"(N #N /___QT N/___YPQQ]@X #W_?GB0F %P_\/__@ +M _7<__X 8_W_'X C /'\" F %P__P _@ _7\__X ' #'_X 'C /_\ +M" J %@/ #^ #]?S__@ !__@?_@ > __P(BX (P ____'H#X^$@ +L?X0! +MB@F 0^#@!/ _7\__P "'_/\ #D//\" F %P__^@_ _7\__P #@ +M#'_\ ##@/_\" J %D "#\ #]?S__ ./@?_P ,!W__P(BX (^@____'H +M#X^#@ +[[(6 ?>*"8 7",?_'^ , ##\ +___P 'R__P("8 +#__[ +M[^ #\ #_\ #@ D(?_P '@__P("8 7!_@#[^ #\ #_\ , ?_P (5 +M__P(BX "^^^)@ /[SW^$@ 'UB@J %L__#^ "/'#^ +___@ (1__@( +M"8 +#__C_^ #R/'_^ $@ A_^ @'_^ @)@! #3@ __ +MG^/ ?O_^ #@H '^ _/_^ @'@!H@ 8_Z__CP 7[__@ #_'_X # +MX/_X' <'@!,&' 8P __@ 7[__@ # '_X X #%/_X"(N !^O__#__Z!>% +M@ +\?X2 !/3___>'!X -. '___[_ ]__X ."@ ?X #\__X" > &B +M #_C_^_P #O?_^ /\/_@ /@?_C\!P> $P<\ " #_^ #O?_^ , +M/_@#@ ,4?_@(BX 'X__QC__$(X6 OP_A( $]'__(X<'@ 0X /@X &^ , +M/__ X* !_@ 'S__@(!X :( ?^O___@ /#__P !_P_^ _!_^_P' +M!X 9!_P < /_X /#__P !P _^ @Q_^ B+@ ?K_^!'_\/#A8 "_#^$ +M@ 3\?_PGAP> &3 #__\]_X !O[^\ '__[@ '__S@(!X :( ?^/S +MW_@ ._O[P #_X?N _P_/_P'!X 9#_P 0 /SP ._O[P\ #@ ?O^ +M @ _. B+@ ?C_^!'_\6'A8 "_A^$@ 3\/_@?AP> &3 #__\]WX ?[^\ +M ?__S@( '__B (!X :( __WSW_@ )_O[P #_\/. @ !_X>/_ '!X 9 +M#_P P 'SP@ )_O[S_ " //_@ !@ >( B+@ ?]_^H7_]F'AH !#X2 !/X? +MX!^'!X 9, ?__YV)@ '[?YQ40 "__^)P0 __^( @'@!H@ '_\?G?^ _ +MM_G ! ?_PX@! /_PX__ <&@!H(#_P1@ 'YQV /[?YVKP % ../P # . +M( B+@ ?Q_^=G_\/+AH !#X6 P_@/X<&@ !_X /^_\"PN " !#'^ !@ !@F+@ +Z?X. @/ AH !AX6 X?!^X<&@!D/ +M__P#___X 0 _[_P#!$ +__\& $ #___"0: &P& ___+@?_ '_O_ 0!_ +M_\0 0!__\$__ <(@!<#_ .!F< ?^_\'/O % !&?_ ' 0F+@ C\O_N_ +M_@/ ?X6 <>%@ /!P:.'!H 9!\ !?__\ -$ ?\_\$ %___ @#___PD& +M@ T!P ?__T0'_P!_S_P X +?__H " ___PO_T'!X :/__Z 0&MP!_S_P +M/_\ ( __^ P @ $'BX (_1____X#P'^%@ 'KA8 #\, #AP> "#?\!___ +M ")$!( %0 " !_\$@ 4/_^" 0<'@ @P ?__P ?^ 2 #A_X@ ?_ !_X __ +MX/__!P> 7@%@ (]N 2 ";__@ ?__@ . L [!Y^ D ZAP> DSD!( " +M 8(&@ & !8 &0 /@ "$!P> 4 %@ (^?@2 S_\@ . "#_\0 /@'_^!P: +M P'S& 2 CY^!( )\\^ !\\_ X "7WH'AX "_.>6@ +1QX<'@ *Q* 6 +M 0($@ ,-,( #@ ,-,$ #@ (@0 <'@ *MS 2 C_^!( #/_R X #/_Q X " +M<<8'!X "[]P$@ (__@2 ">RW@ >RWP . ENN!X> OWOEH "\<>'!X " +M4" $@ (GY 2 P(@@ . T(@0 2 40'!X "3]P$@ (X' 2 S_<@ . S_< +M0 . G_^!P> N_-@ +X'YD'@ &P +M!8 ", P$@ ."@( #@ /"@$ #@ )@!@<'@ *O_ 2 B_T!( #/_R X #/_Q +M X "?_X'!H # >_\!( "+_0$@ E__X $__\ #@ )__@>?@ *__8<'@ ,1 +M! P#@ 0,, #@!( '@ L 0 . !# D X%!X ##O@, X )>]X X '_B +M X #/_Q X $/]P #@4'@ ,N^ P#@ ][W@#@ #__X $__\ #@ 0OW . +M!8> ?Z8@ '?AP> "ACP#A@ &?D _@#@ P /@4'@ H' +M" X8 &?^X/X X " 8 #@ )\/@2 !!X< #X%!X 5!P@.& !__^#^ ?_^ +M !?#_P X $'A0 /@6(@ $/D( "_#^$@ +^'X<'@!0, ]\ &#R ?X !# +MR8'\ +'X@2 !!@,!'X%!X * _ /? "?_?G^ . !P&!_ >!X$@ 0?_ 1^ +M!0> %0/P#WP ___Y_@ #PW@?P 7@?\ . !!?\!'X%DX "_#^$@ +X'XT' +M@!P&( ?^ (!D _\ "@P/\ +%8@/P ,/ S^!0> ' ' !_X !?_O[_P +M :C _P 'U^ _ _\#/X%!X < < '_@ '___O_ /K\#_ !?7_S\ +M"]P,_@63@ +^OX2 OU_C0: !0'__ __ X 5@ _\ @0 !_^ +CQ _X . +M< _\!0: !P'V( __ ^#@!/\ _!P__^ #\/$_X __ _\!0> ' 8CC_\ +M#_]___P !^__[_X #?P___@ 6@#_P%F8 "_#^$@ +'XX<&@!T#__P?_P " +M !/^ $ 0'_@ D P'_ #_ /_ 4&@ <##R ?_P /@X 3^ ?X ?__@ ( +M'_Q/_ __\/_ 4'@!P&(]__ W__[_X !_O__O^ ]____\ #'XP_\!9^ +M L?CAP: '0?__!__ $ @(?X 0# /^ "8& /\ #P!_\!0: !P8#P!__ +M !^#@!/X #_P#__^ #P?^._\ '__Y_\!0> # /#__\ 'O]_?_@ /X. #?X +M/W____P !A_@ #^ !_ 2 C_X!0: !PP __@!^#@ /P '^#@ G^ 'X/X'_\ +M .#@ 'X!0> 0>#@!B '_Z___ ?W__G_X ?_?___P \ #__@%GX "P!^' +M!H =#_P"#__@$>(G#\ !X P?P C_@0?P % */_H%!H '"$ "#__@'X. +M#\ ?7____P ?C@ ?_P X. ?H%!X !0X. &. .W=OWP !X?_\__ !]P!^_ +M_ #T O_^@6?@ +0'X<&@!T/]@,/^> 3\ ^?X #@ '!_ #_^'!_ \ \? +M_@4'@ ;P P_YX!^#@ /@ '^#@ G\ '_X ?_\ >#@ '^!0> ',G___G@#>_W +M;^ ??__O_P ?< ?O_P !_ /__X%GX "\#^'!H =#_,#C_@ ?"/W^ > # +M@?P !__CX?P / /'_X%!H ' ? #C_@ #X. #^ ?_OO__P ?_@#__P !X. +M ?X%!H = < ?G_P !_ /__X%GX "\#^'!H = +M#_&#C_@ ? /C_ &? #D_@ 0?_C@_@ _ /W_@%!H ' ? #C_@ #X. #_ +M/^____@ ?_@#__@ !X. ?@%!H ' /\ 0__D/^ !_^.#^ T \?X 4& +M@!T/L /_\ /X ?_\ ?X '_^ _^ /_^ 'W___X 4&@!T/CS__\ /_Q__ +M\ /@ ?W^ _P!__^ 'T __X 6?@ +P?X<'@!8P&(!X 6,#^/ +M\ !P..'\ __,/^ !_^./^ . P,_P 4&@!T., /_\ ?X ?_\ /\ /_ +M^ /^ /_^ #/___P 4&@!T.#\/_\ ?_Q__\ /P __^ /P!__^ # /_ +MP 6?@ '#B > !# ?__ #@ \#A^ #_CG^ #_C_^ #@ .#_< %!X <, /_ +M\ >, __X #. /_X . /_X /__]P 4'@! /X__P !X____@ , '__@ +M X #'__@ X #@_W !9^ <>(!X $, /]\ . %0./P /^/_X /^/_X +M "@___? +M#C___\ #@ ,?_^ #@ D?_^ '#^< %GX !QX@'@ 00!_G X 5!__ ? +MX__ _X__ !P_' !0> '! '^< #Y__\ #@'_\ #@#_\ #__ +M\< %!X 0#__YP /_O_P (!__P . "1__P '!_[ +M\< "&'\\ /#\< /#_< '#\\ %!X <'_OQP /__SP /__Q +MP /__]P /__SP 4'@!P/__' ?^_/ ___' ___W !P_/ +M!9^ <>(!X <$<)QP (8?SP \/QP \/YP ' __\< !_[\\ #__\< +M #__^< '#\\ %GX !QX@'@!SYP!A #P_' #P_# #P_# ! +MP_& !0> ' ' &$ #__\< #__\, #__\, #__\8 %!X &!H 6 8$ " ?\?\ _]_^ _]_ +M^ . U\:X 8'@!5_/'@ '?&_ -7:O@ -7:O@#@ -;"N &J : ! &! +M!^ #@ .G!7 #@ .I%3 #@ .I%3 #@ ,& . &!H " 8$$@! !_Q_P #_W_P +M #_W_P X #_A_@!@> %7\X$ 5\:\ U=J\ U=J\ . _X?X :H +M #_ B@H**@/@ '@#H " > (#X !X Z @_@" ^ > .@ (/^ B>@ +P!X@. +M@ (!\ . 3X*@ (#P @.@ (!\ Z C_ " Z @'P X " ? )@ (_\ B>@ + +M#X@.@ (!\ . 7 *@ (/P @.@ (!\ Z G_ " Z @'P X "#_@)@ )_X B> +M@ * 'X@.@ ("X . 6 *@ %_"0Z @+@#H$)#H " ^ #@ (?^ F!@ '@")X! +M@ $?B B R 0 . C^@ X !8 6 ?P$@ %^"0Z C^@"8 !_ . @'^"0B +M R 0 . C_@ X "'_@(@ ,!_^ (F8 ! X. _X 'X@(@ -@ & #@8 !P . +M 6 $@ ,'_X #@0D.@8 !P B !P?_@ ?\)"( #8 !@ X& < #@ (?^ B +M P'_P B8@ CX '____X /X@(@ AP & '_P . 6 $@ ,.+\ #@8 !@ @- +M@ ,!_\ (@ @.+\ /_@ @(@ AP & '_P . A_X"( # __ "(J 7^- +M@ CP #____Q^/X@(@ H__N /_X !@X +_@ $/_@ !_X (#8 # __@ +M"( ($/_@ #_X ("( (/X;@ #_^ -@ ,#_\ (B( "O@:.@ C@ !____Q_ +M/X@(@ A__^ ?_\ B "!?NX >N " J !B ?_\ B "!?NX ^N +M" B "G\#X !__P .$!X # __ "(B _P#WXV ". '____/\_B B "#__ +MP !__P"( (/__P !U8 ("H &0 !__P"( (/__P #U8 ("( */@' +M '__ 8. ?X'@ ,#_\ (B( #^ &_C8 (P /___\_S^("( "#_\#@ @. +M ' '__P. ""@ \ >N " V P__\ . A;1 X (+__P #ZX ("( ! +M# 2 P__\ 2 0$#@ ('_P. P/_P B(@ +P (B _X6T8. ",?_#____/\_ +MB B @__ X (# P !XQX#@ AQ2G '_@ @-@ ,/__ #@ )_^@. "'XQ +M\ _^ " B 0@$@ ,/__ #@ ( #O< /_ :8@ @'_X____S_/X@(@ (((0. P0 ( . #PA$ +M8 N !P #_[#__@8(@ (&<@. P?_X . #W>\8 O__P #_[#__@8( +M@ ('W@. P?_X . #W>\8 !_^ #P_#__@:(@ +^_@X (!_^/ +M___\_S^("( ""#$#@ ,"'$ #@ ]'B?P8 ;1X\ _\[__X&"( "!_X#@ ,# +MX\ #@ ]X>?P8 ;>'\ _\[__X&"( "!\X#@ ,#X\ #@ =X>?P8 .' X % +M __[__X&CH !XX2 #?A____^ X ( +M 'X /X #@X ,_@ ( !@X .OX #\#__@#'_G__ +M #^#@ ' !HF #Q____X ?@!___P/P/__^(6 \ X@(@ 4&9@& (2 $=@% +MG_G__@XX_X'_ !Y\ ?^ !@B "P>> 8 #P _]@'@X ,_@X _ '_ '[^ ?^ +M!@B !0>> 8 A( -V /P'_^ # 8 $ ___P84#B F &/ _^ ?__C_P #_-!_^>@// +M ?^ __X _ <(@ L____X .0 )__ 'X. "_Y[X ?_X#__@#\!PB %3____@ +MY__G_\ ? /__@'\,/__@(. ?P'B( (P/ ____D "># 8 $ ___@84#B B +M&7 '__PG! Y_\ #Y_\?_]X;SV'_@!_^,/X'"( %0_P?__"$@ + 'X2 "M_X +M '__@/_^-_X'"( !#X. $?!O__;_P!\ ___@>PP__^ @X !_@>(@ +P (. +M _P /X,!@ 0#__^AA8 #^ WB B &? #__@GBBY_\ #YO\?_\X;CF'_@!/\ +M./X'"( %@?@/_^"$@ + 'X2 "L_X '__@//\/_X'"( !#X. %>!O__;_P!\ +M ___@>1Q__^ \____@>(@ +P (. _P /X,!@ 4#__^Q]X2 _P /X@'@!H! +M\ G_P ,1##_@ .?SQ__GCP \?^ _YX_P<)@ 3P!_\ A( "@!^$@ J?_ #_ +M_X#C_G__!PB !0__W_\ A( (@!\ ___@<>#@ * XX,'B( "\ "#@ /\ #^# +M 8 % ___X>>$@ /\ '^(!X : _@!V_\ #((P_X !FX!__^ P8,'B( "^ &#@ /\ #^# 8 % ___^>^$@ /^ '^(!X : _P#^_X +M#1%P_X #^?\?\8P^ ?'_ #^?_X'"( %? /C_@"$@ * 'X. "_&-_@'__P ! +M_G_^!PB !7__Y_X A( (@!\ __Q@<># 8 $ ?___@>(@ +\ X. _P /X,! +M@ 4#___SYX2 _X ?X@'@!H#_ /__@ . +#_@ /_]Q_P #ZD\?P 'P_[@<( +M@ 5\ ^/^ (2 H ?@X +\ '^ /_\ '\/^X'"( %?__C_@"$@!" 'X '__ ! +M]UO__ !___N!XB OP#@X #_ _@X "@ >#@ 'WA( #_@ _B B !@__P_X +M X,"@ Y@!__@ #7^__@ /Z_[@<'@ 4#R!'__@. C__ X -!__@ #0 __@ +M _Z_[@<'@!H#R '__@ #@#__ ?___@ 8!__@ ___[@>)@ 'O@X #_ _ +MB( !SX2 _P /X@)@!@XP_P ____@ 8 ?_X '___X '^/\P'!X % \ X +M__P#@ (__@. #0?_X $ /_X /^/\P'!X : \_'__P X(__@ '___X $ +M /_X /__\P'B8 !QX. _P /XV _P /X@)@!@8X_@ ]___@ 8 ?_P # +MMW_X #^O\ '!X % X 8__@#@ (__@. #0?_P ,'_X ?^O\ '!X : X_G +M__@ Z(__@ '___P $>/_X ?__\ 'B8 !YX. _P@/XV _@ /X@(@!@/ +MQ__P /?_YP #,'YX ![-^. #_C\("( 8" #_\ '#^< !##^> P +M?C@ !_X_" F %SC_\ #OC^< !/_^> !\_C@ !___"(V _P\/XV _@ +M/X@(@!@/S__P /__YP ,'YX ![M^> #_]\("( 8" #_\ '#^< !_ +M_^> X?G@ #__?" F %S#_\ #GC^< !__^> !\_G@ #___"(V _P< +M/XV _ 'X@(@!D/___ /__YP ,'XX !7A^> '_Q^ !PF &&#_P +M'#^< !__^. +Y?G@ '_\?@ <)@!A@_\ YX_G ?__C@ "__YX !__ +M_X 'C8 #_!P_C8 #X ?B B "#___P '___ X # P#@ X (<'XP !?_I\ ' +M"8 "\#\#@!,\!P ___X !W/^, !__Z? !PF &/ _ ?__\ /__^ +M ?W_C ?___P >-@ /@/ >-@ . >("( (?\_\ ?\_\#@ ,# . #@ A@ +M'@ /__+P <)@ + ' . $S ' #___@ ?Y_X /__R\ '"8 4P!P !_S +M_P ___X '^?^ "#@ ' !XV ^ \!XT"@ $#B B "'_'_ '_'_ X # +M#@#@ X '8 X %__T0@(@ ,!P!P#@!)P!P _X_X /^?^ /__]$("( 5 +M < < ?\?\ /^/^ #_G_@ #@PB-@ /@?@>,@ 3\ !B!V W__\ @= +4@ -___ ('8 #?__P")V X #X@ +end diff --git a/win/gem/wingem.c b/win/gem/wingem.c new file mode 100644 index 0000000..ceb59d7 --- /dev/null +++ b/win/gem/wingem.c @@ -0,0 +1,1242 @@ +/* SCCS Id: @(#)wingem.c 3.4 1999/12/10 */ +/* Copyright (c) Christian Bressler, 1999 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "func_tab.h" +#include "dlb.h" +#include +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +#ifdef GEM_GRAPHICS +#include "wingem.h" + +static char nullstr[] = "", winpanicstr[] = "Bad window id %d"; +static int curr_status_line; + +static char *FDECL(copy_of, (const char *)); +static void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */ + +extern int mar_set_tile_mode(int); +extern void mar_set_font(int,const char*,int); +extern void mar_set_margin(int); +extern void mar_set_msg_visible(int); +extern void mar_set_status_align(int); +extern void mar_set_msg_align(int); +extern void mar_set_tilefile(char *); +extern void mar_set_tilex(int); +extern void mar_set_tiley(int); +extern short glyph2tile[MAX_GLYPH]; /* from tile.c */ +extern void mar_display_nhwindow(winid); /* from wingem1.c */ + +void Gem_outrip(winid,int); +void Gem_preference_update(const char *); +/* Interface definition, for windows.c */ +struct window_procs Gem_procs = { + "Gem", + WC_COLOR|WC_HILITE_PET|WC_ALIGN_MESSAGE|WC_ALIGN_STATUS| + WC_INVERSE|WC_SCROLL_MARGIN| + WC_FONT_MESSAGE|WC_FONT_STATUS|WC_FONT_MENU|WC_FONT_TEXT|WC_FONT_MAP| + WC_FONTSIZ_MESSAGE|WC_FONTSIZ_STATUS|WC_FONTSIZ_MENU|WC_FONTSIZ_TEXT|WC_FONTSIZ_MAP| + WC_TILE_WIDTH|WC_TILE_HEIGHT|WC_TILE_FILE|WC_VARY_MSGCOUNT|WC_ASCII_MAP, + 0L, + Gem_init_nhwindows, + Gem_player_selection, + Gem_askname, + Gem_get_nh_event, + Gem_exit_nhwindows, + Gem_suspend_nhwindows, + Gem_resume_nhwindows, + Gem_create_nhwindow, + Gem_clear_nhwindow, + Gem_display_nhwindow, + Gem_destroy_nhwindow, + Gem_curs, + Gem_putstr, + Gem_display_file, + Gem_start_menu, + Gem_add_menu, + Gem_end_menu, + Gem_select_menu, + genl_message_menu, + Gem_update_inventory, + Gem_mark_synch, + Gem_wait_synch, +#ifdef CLIPPING + Gem_cliparound, +#endif +#ifdef POSITIONBAR + Gem_update_positionbar, +#endif + Gem_print_glyph, + Gem_raw_print, + Gem_raw_print_bold, + Gem_nhgetch, + Gem_nh_poskey, + Gem_nhbell, + Gem_doprev_message, + Gem_yn_function, + Gem_getlin, + Gem_get_ext_cmd, + Gem_number_pad, + Gem_delay_output, +#ifdef CHANGE_COLOR /* the Mac uses a palette device */ + Gem_change_color, +#ifdef MAC + Gem_change_background, + Gem_set_font_name, +#endif + Gem_get_color_string, +#endif + + /* other defs that really should go away (they're tty specific) */ + Gem_start_screen, + Gem_end_screen, + Gem_outrip, + Gem_preference_update +}; + +#ifdef MAC +void * +Gem_change_background(dummy) +int dummy; +{} + +short * +Gem_set_font_name(foo,bar) +winid foo; +char *bar; +{} +#endif + +/*************************** Proceduren *************************************/ + +int mar_hp_query(void){ + if(Upolyd) + return(u.mh ? u.mhmax/u.mh : -1); + return(u.uhp ? u.uhpmax/u.uhp : -1); +} + +int +mar_iflags_numpad() +{ + return(iflags.num_pad ? 1 : 0); +} + +int +mar_get_msg_history() +{ + return(iflags.msg_history); +} + +int +mar_get_msg_visible() +{ + return(iflags.wc_vary_msgcount); +} +/* clean up and quit */ +static void +bail(mesg) +const char *mesg; +{ + clearlocks(); + Gem_exit_nhwindows(mesg); + terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +/*$$$*/ +#define DEF_CLIPAROUND_MARGIN -1 +#ifndef TILE_X +#define TILE_X 16 +#endif +#define TILE_Y 16 +#define TILES_PER_LINE 20 +#define NHFONT_DEFAULT_SIZE 10 +#define NHFONT_SIZE_MIN 3 +#define NHFONT_SIZE_MAX 20 +/*$$$*/ +/*ARGSUSED*/ +void +Gem_init_nhwindows(argcp,argv) +int* argcp; +char** argv; +{ + argv=argv, argcp=argcp; + colors_changed=TRUE; + + set_wc_option_mod_status( + WC_ALIGN_MESSAGE | + WC_ALIGN_STATUS | + WC_TILE_WIDTH | + WC_TILE_HEIGHT | + WC_TILE_FILE, + DISP_IN_GAME); + set_wc_option_mod_status( + WC_HILITE_PET | + WC_SCROLL_MARGIN | + WC_FONT_MESSAGE | + WC_FONT_MAP | + WC_FONT_STATUS | + WC_FONT_MENU | + WC_FONT_TEXT | + WC_FONTSIZ_MESSAGE | + WC_FONTSIZ_MAP | + WC_FONTSIZ_STATUS | + WC_FONTSIZ_MENU | + WC_FONTSIZ_TEXT | + WC_VARY_MSGCOUNT, + SET_IN_GAME + ); + if( iflags.wc_align_message==0 ) iflags.wc_align_message = ALIGN_TOP; + if( iflags.wc_align_status==0 ) iflags.wc_align_status = ALIGN_BOTTOM; + if( iflags.wc_scroll_margin==0 ) iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN; + if( iflags.wc_tile_width==0 ) iflags.wc_tile_width = TILE_X; + if( iflags.wc_tile_height==0 ) iflags.wc_tile_height = TILE_Y; + if(iflags.wc_tile_file && *iflags.wc_tile_file) + mar_set_tilefile(iflags.wc_tile_file); + if( iflags.wc_vary_msgcount==0 ) iflags.wc_vary_msgcount = 3; + mar_set_tile_mode(!iflags.wc_ascii_map); /* MAR -- 17.Mar 2002 True is tiles */ + mar_set_tilex(iflags.wc_tile_width); + mar_set_tiley(iflags.wc_tile_height); + mar_set_msg_align(iflags.wc_align_message-ALIGN_BOTTOM); + mar_set_status_align(iflags.wc_align_status-ALIGN_BOTTOM); + if(mar_gem_init()==0){ + bail((char *)0); + /*NOTREACHED*/ + } + iflags.window_inited = TRUE; + + CO=80; /* MAR -- whatsoever */ + LI=25; + + add_menu_cmd_alias(' ', MENU_NEXT_PAGE); + mar_set_no_glyph(NO_GLYPH); +} + +void +Gem_player_selection() +{ + int i, k, n; + char pick4u = 'n', pbuf[QBUFSZ], lastch=0, currch; + winid win; + anything any; + menu_item *selected=NULL; + + /* avoid unnecessary prompts further down */ + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (!flags.randomall && + (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || + flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) { +/* pick4u = yn_function("Shall I pick a character for you? [ynq]",ynqchars,'n');*/ + pick4u = yn_function( + build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,flags.initrace, + flags.initgend, flags.initalign) + ,ynqchars,'n' + ); + if(pick4u=='q'){ +give_up: /* Just quit */ + if (selected) free((genericptr_t) selected); + bail((char *)0); + /*NOTREACHED*/ + return; + } + } + + /* Select a role, if necessary */ + if (flags.initrole < 0) { + + /* Process the choice */ + if(pick4u=='y' || flags.initrole == ROLE_RANDOM || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + mar_add_message("Incompatible role!"); + mar_display_nhwindow(WIN_MESSAGE); + flags.initrole = randrole(); + } + }else{ + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + currch = lowc(roles[i].name.m[0]); + if(currch == lastch) + currch = highc(currch); + add_menu(win, roles[i].malenum, &any, currch, + 0, ATR_NONE, an(roles[i].name.m), MENU_UNSELECTED); + lastch = currch; + } + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole()+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + end_menu(win, "Pick a role"); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + mar_add_message("Incompatible race!"); + mar_display_nhwindow(WIN_MESSAGE); + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, races[i].noun[0], + 0, ATR_NONE, races[i].noun, MENU_UNSELECTED); + } + any.a_int = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrace(flags.initrole)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the race of your %s", + roles[flags.initrole].name.m); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initrace = k; + } + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + mar_add_message("Incompatible gender!"); + mar_display_nhwindow(WIN_MESSAGE); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], + 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s %s", + races[flags.initrace].adj, + roles[flags.initrole].name.m); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + mar_add_message("Incompatible alignment!"); + mar_display_nhwindow(WIN_MESSAGE); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, + i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], + 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s %s %s", + genders[flags.initgend].adj, + races[flags.initrace].adj, + (flags.initgend && roles[flags.initrole].name.f) ? + roles[flags.initrole].name.f : + roles[flags.initrole].name.m); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initalign = k; + } + } + + /* Success! */ + return; +} + +/* + * plname is filled either by an option (-u Player or -uPlayer) or + * explicitly (by being the wizard) or by askname. + * It may still contain a suffix denoting pl_character. + * Always called after init_nhwindows() and before display_gamewindows(). + */ + +void +Gem_askname() +{ + strncpy(plname,mar_ask_name(),PL_NSIZ); +} + +void +Gem_get_nh_event() +{} + +void +Gem_suspend_nhwindows(str) + const char *str; +{ + const char *foo; + + foo=str; /* MAR -- And the compiler whines no more ... */ +} + +void +Gem_resume_nhwindows() +{} + +void +Gem_end_screen() +{} + +void +Gem_start_screen() +{} + +extern void mar_exit_nhwindows(void); +extern boolean run_from_desktop; + +void +Gem_exit_nhwindows(str) + const char *str; +{ + if(str) Gem_raw_print(str); + mar_exit_nhwindows(); + if(flags.toptenwin) + run_from_desktop=FALSE; + iflags.window_inited = 0; +} + +winid +Gem_create_nhwindow(type) + int type; +{ + winid newid; + + switch(type) { + case NHW_MESSAGE: + if(iflags.msg_history < 20) iflags.msg_history = 20; + else if(iflags.msg_history > 60) iflags.msg_history = 60; + break; + case NHW_STATUS: + case NHW_MAP: + case NHW_MENU: + case NHW_TEXT: + break; + default: + panic("Tried to create window type %d\n", (int) type); + return WIN_ERR; + } + + newid=mar_create_window(type); + + if(newid == MAXWIN) { + panic("No window slots!"); + /* NOTREACHED */ + } + + return newid; +} + +void +Gem_nhbell() +{ + if (flags.silent) return; + putchar('\007'); + fflush(stdout); +} + +extern void mar_clear_map(void); + +void +Gem_clear_nhwindow(window) + winid window; +{ + if(window == WIN_ERR) + panic(winpanicstr, window); + + switch(mar_hol_win_type(window)) { + case NHW_MESSAGE: + mar_clear_messagewin(); + break; + case NHW_MAP: + mar_clear_map(); + break; + case NHW_STATUS: + case NHW_MENU: + case NHW_TEXT: + break; + } +} + +extern void mar_more(void); + +/*ARGSUSED*/ +void +Gem_display_nhwindow(window, blocking) + winid window; + boolean blocking; +{ + if(window == WIN_ERR) + panic(winpanicstr, window); + + mar_display_nhwindow(window); + + switch(mar_hol_win_type(window)){ + case NHW_MESSAGE: + if(blocking) mar_more(); + break; + case NHW_MAP: + if(blocking) Gem_display_nhwindow(WIN_MESSAGE, TRUE); + break; + case NHW_STATUS: + case NHW_TEXT: + case NHW_MENU: + default: + break; + } +} + +void +Gem_destroy_nhwindow(window) + winid window; +{ + if(window == WIN_ERR) /* MAR -- test existence */ + panic(winpanicstr, window); + + mar_destroy_nhwindow(window); +} + +extern void mar_curs(int,int); /* mar_curs is only for map */ + +void +Gem_curs(window, x, y) +winid window; +register int x, y; +{ + if(window == WIN_ERR) /* MAR -- test existence */ + panic(winpanicstr, window); + + if(window==WIN_MAP) + mar_curs(x-1,y); /*$$$*/ + else if(window==WIN_STATUS) + curr_status_line=y; +} + +extern void mar_add_status_str(const char *, int); +extern void mar_putstr_text(winid, int, const char *); + +void +Gem_putstr(window, attr, str) + winid window; + int attr; + const char *str; +{ + int win_type; + + if(window == WIN_ERR) { + Gem_raw_print(str); + return; + } + + if(str == (const char*)0) + return; + + switch((win_type=mar_hol_win_type(window))) { + case NHW_MESSAGE: + mar_add_message(str); + break; + + case NHW_STATUS: + mar_status_dirty(); + mar_add_status_str(str,curr_status_line); + if(curr_status_line) + mar_display_nhwindow(WIN_STATUS); + break; + + case NHW_MAP: + if(strcmp(str,".")) + Gem_putstr(WIN_MESSAGE,0,str); + else + mar_map_curs_weiter(); + mar_display_nhwindow(WIN_MESSAGE); + mar_display_nhwindow(WIN_STATUS); + break; + + case NHW_MENU: + mar_change_menu_2_text(window); + /* Fallthru */ + case NHW_TEXT: + mar_putstr_text(window,attr,str); + break; + } /* endswitch win_type */ +} + +void +Gem_display_file(fname, complain) +const char *fname; +boolean complain; +{ + dlb *f; + char buf[BUFSZ]; + char *cr; + + f = dlb_fopen(fname, "r"); + if (!f) { + if(complain) + pline("Cannot open \"%s\".", fname); + } else { + winid datawin; + + datawin = Gem_create_nhwindow(NHW_TEXT); + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; + if (index(buf, '\t') != 0) (void) tabexpand(buf); + Gem_putstr(datawin, 0, buf); + } + (void) dlb_fclose(f); + Gem_display_nhwindow(datawin, FALSE); + Gem_destroy_nhwindow(datawin); + } +} + +/*ARGSUSED*/ +/* + * Add a menu item to the beginning of the menu list. This list is reversed + * later. + */ +void +Gem_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected) + winid window; /* window to use, must be of type NHW_MENU */ + int glyph; /* glyph to display with item (unused) */ + const anything *identifier; /* what to return if selected */ + char ch; /* keyboard accelerator (0 = pick our own) */ + char gch; /* group accelerator (0 = no group) */ + int attr; /* attribute for string (like Gem_putstr()) */ + const char *str; /* menu string */ + boolean preselected; /* item is marked as selected */ +{ + Gem_menu_item *G_item; + const char *newstr; + char buf[QBUFSZ]; + + if (str == (const char*) 0) + return; + + if (window == WIN_ERR) /* MAR -- test existence */ + panic(winpanicstr, window); + + if (identifier->a_void) + Sprintf(buf, "%c - %s", ch ? ch : '?', str); + else + Sprintf(buf, "%s", str); + newstr = buf; + + G_item = (Gem_menu_item *) alloc(sizeof(Gem_menu_item)); + G_item->Gmi_identifier = (long)identifier->a_void; + G_item->Gmi_glyph = glyph!=NO_GLYPH ? glyph2tile[glyph] : NO_GLYPH; + G_item->Gmi_count = -1L; + G_item->Gmi_selected = preselected ? 1 : 0; + G_item->Gmi_accelerator = ch; + G_item->Gmi_groupacc = gch; + G_item->Gmi_attr = attr; + G_item->Gmi_str = copy_of(newstr); + mar_add_menu(window, G_item); +} + +/* + * End a menu in this window, window must a type NHW_MENU. + * We assign the keyboard accelerators as needed. + */ +void +Gem_end_menu(window, prompt) + winid window; /* menu to use */ + const char *prompt; /* prompt to for menu */ +{ + if(window == WIN_ERR || mar_hol_win_type(window) != NHW_MENU) + panic(winpanicstr, window); + + /* Reverse the list so that items are in correct order. */ + mar_reverse_menu(); + + /* Put the prompt at the beginning of the menu. */ + mar_set_menu_title(prompt); + + mar_set_accelerators(); +} + +int +Gem_select_menu(window, how, menu_list) + winid window; + int how; + menu_item **menu_list; +{ + Gem_menu_item *Gmit; + menu_item *mi; + int n; + + if(window == WIN_ERR || mar_hol_win_type(window) != NHW_MENU) + panic(winpanicstr, window); + + *menu_list = (menu_item *) 0; + mar_set_menu_type(how); + Gem_display_nhwindow(window, TRUE); + + for (n = 0, Gmit = mar_hol_inv(); Gmit; Gmit = Gmit->Gmi_next) + if (Gmit->Gmi_selected) n++; + + if (n > 0) { + *menu_list = (menu_item *) alloc(n * sizeof(menu_item)); + for (mi = *menu_list, Gmit = mar_hol_inv(); Gmit; Gmit = Gmit->Gmi_next) + if (Gmit->Gmi_selected) { + mi->item = (anything)(genericptr_t)Gmit->Gmi_identifier; + mi->count = Gmit->Gmi_count; + mi++; + } + } + + return n; +} + +void +Gem_update_inventory() +{} + +void +Gem_mark_synch() +{ + mar_display_nhwindow(WIN_MESSAGE); + mar_display_nhwindow(WIN_MAP); + mar_display_nhwindow(WIN_STATUS); +} + +void +Gem_wait_synch() +{ + mar_display_nhwindow(WIN_MESSAGE); + mar_display_nhwindow(WIN_MAP); + mar_display_nhwindow(WIN_STATUS); +} + +#ifdef CLIPPING +extern void mar_cliparound(void); +void +Gem_cliparound(x, y) +int x, y; +{ + mar_curs(x-1,y); + mar_cliparound(); +} +#endif /* CLIPPING */ + +/* + * Gem_print_glyph + * + * Print the glyph to the output device. Don't flush the output device. + * + * Since this is only called from show_glyph(), it is assumed that the + * position and glyph are always correct (checked there)! + */ + +void mar_print_gl_char(winid,xchar,xchar,int); + +#ifdef REINCARNATION +extern int mar_set_rogue(int); +#endif + +extern void mar_add_pet_sign(winid,int,int); + +void +Gem_print_glyph(window, x, y, glyph) + winid window; + xchar x, y; + int glyph; +{ + /* Move the cursor. */ + Gem_curs(window, x,y); + +# ifdef REINCARNATION + mar_set_rogue(Is_rogue_level(&u.uz) ? TRUE : FALSE); +# endif + + x--; /* MAR -- because x ranges from 1 to COLNO */ + if(mar_set_tile_mode(-1)){ + mar_print_glyph(window,x,y,glyph2tile[glyph]); + if( +#ifdef TEXTCOLOR + iflags.hilite_pet && +#endif + glyph_is_pet(glyph) + ) + mar_add_pet_sign(window,x,y); + }else + mar_print_gl_char(window,x,y,glyph); +} + +void mar_print_char(winid,xchar,xchar,char,int); + +void mar_print_gl_char(window, x, y, glyph) + winid window; + xchar x, y; + int glyph; +{ + int ch; + int color; + unsigned special; + + /* map glyph to character and color */ + mapglyph(glyph, &ch, &color, &special, x, y); + +#ifdef TEXTCOLOR + /* Turn off color if rogue level. */ +# ifdef REINCARNATION + if (Is_rogue_level(&u.uz)) color = NO_COLOR; +# endif +#endif /* TEXTCOLOR */ + + mar_print_char(window,x,y,ch,color); +} + +extern void mar_raw_print(const char *); +extern void mar_raw_print_bold(const char *); + +void +Gem_raw_print(str) + const char *str; +{ + if(str && *str){ + if(iflags.window_inited) mar_raw_print(str); + else printf("%s\n",str); + } +} + +void +Gem_raw_print_bold(str) + const char *str; +{ + if(str && *str){ + if(iflags.window_inited) mar_raw_print_bold(str); + else printf("%s\n",str); + } +} + +extern void mar_update_value(void); /* wingem1.c */ + +int +Gem_nhgetch() +{ + int i; + + mar_update_value(); + i = tgetch(); + if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ + + return i; +} + +/* Get a extended command in windowport specific way. + returns index of the ext_cmd or -1. + called after '#'. + It's a menu with all the possibilities. */ +int +Gem_get_ext_cmd() +{ + winid wind; + int i, count, what,too_much=FALSE; + menu_item *selected=NULL; + anything any; + char accelerator=0, tmp_acc=0; + const char *ptr; + + wind=Gem_create_nhwindow(NHW_MENU); + Gem_start_menu(wind); + for(i=0;(ptr=extcmdlist[i].ef_txt);i++){ + any.a_int=i; + accelerator=*ptr; + if(tmp_acc==accelerator){ + if(too_much) + accelerator='&'; /* MAR -- poor choice, anyone? */ + else + accelerator+='A'-'a'; + too_much=TRUE; + }else + too_much=FALSE; + tmp_acc=*ptr; + Gem_add_menu(wind,NO_GLYPH,&any,accelerator,0,ATR_NONE,ptr,FALSE); + } + Gem_end_menu(wind,"What extended command?"); + count=Gem_select_menu(wind,PICK_ONE,&selected); + what = count ? selected->item.a_int : -1; + if(selected) free(selected); + Gem_destroy_nhwindow(wind); + return(what); +} + +void +Gem_number_pad(state) +int state; +{ + state=state; +} + +void +win_Gem_init() +{} + +#ifdef POSITIONBAR +void +Gem_update_positionbar(posbar) +char *posbar; +{} +#endif + +/** Gem_outrip **/ +void mar_set_text_to_rip(winid); +char** rip_line=0; +extern const char *killed_by_prefix[]; +void +Gem_outrip(w, how) +winid w; +int how; +{ +/* Code from X11 windowport */ +#define STONE_LINE_LEN 15 /* # chars that fit on one line */ +#define NAME_LINE 0 /* line # for player name */ +#define GOLD_LINE 1 /* line # for amount of gold */ +#define DEATH_LINE 2 /* line # for death description */ +#define YEAR_LINE 6 /* line # for year */ + char buf[BUFSZ]; + char *dpx; + int line; + if (!rip_line) { + int i; + rip_line= (char **)malloc((YEAR_LINE+1)*sizeof(char *)); + for (i=0; i STONE_LINE_LEN) { + for(i = STONE_LINE_LEN; + ((i0 > STONE_LINE_LEN) && i); i--) + if(dpx[i] == ' ') i0 = i; + if(!i) i0 = STONE_LINE_LEN; + } + tmpchar = dpx[i0]; + dpx[i0] = 0; + strcpy(rip_line[line], dpx); + if (tmpchar != ' ') { + dpx[i0] = tmpchar; + dpx= &dpx[i0]; + } else dpx= &dpx[i0+1]; + } + /* Put year on stone */ + Sprintf(rip_line[YEAR_LINE], "%4d", getyear()); + mar_set_text_to_rip(w); + for(line=0;line<13;line++) + putstr(w, 0, ""); +} +void +mar_get_font(type,p_fname,psize) +int type; +char **p_fname; +int *psize; +{ + switch(type){ + case NHW_MESSAGE: + *p_fname=iflags.wc_font_message; + *psize=iflags.wc_fontsiz_message; + break; + case NHW_MAP: + *p_fname=iflags.wc_font_map; + *psize=iflags.wc_fontsiz_map; + break; + case NHW_STATUS: + *p_fname=iflags.wc_font_status; + *psize=iflags.wc_fontsiz_status; + break; + case NHW_MENU: + *p_fname=iflags.wc_font_menu; + *psize=iflags.wc_fontsiz_menu; + break; + case NHW_TEXT: + *p_fname=iflags.wc_font_text; + *psize=iflags.wc_fontsiz_text; + break; + default: + break; + } +} +void +Gem_preference_update(pref) +const char *pref; +{ + if( stricmp( pref, "font_message")==0 || + stricmp( pref, "font_size_message")==0 ) { + if( iflags.wc_fontsiz_messageNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE; + mar_set_font(NHW_MESSAGE,iflags.wc_font_message,iflags.wc_fontsiz_message); + return; + } + if( stricmp( pref, "font_map")==0 || + stricmp( pref, "font_size_map")==0 ) { + if( iflags.wc_fontsiz_mapNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_map = NHFONT_DEFAULT_SIZE; + mar_set_font(NHW_MAP,iflags.wc_font_map,iflags.wc_fontsiz_map); + return; + } + if( stricmp( pref, "font_status")==0 || + stricmp( pref, "font_size_status")==0 ) { + if( iflags.wc_fontsiz_statusNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE; + mar_set_font(NHW_STATUS,iflags.wc_font_status,iflags.wc_fontsiz_status); + return; + } + if( stricmp( pref, "font_menu")==0 || + stricmp( pref, "font_size_menu")==0 ) { + if( iflags.wc_fontsiz_menuNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE; + mar_set_font(NHW_MENU,iflags.wc_font_menu,iflags.wc_fontsiz_menu); + return; + } + if( stricmp( pref, "font_text")==0 || + stricmp( pref, "font_size_text")==0 ) { + if( iflags.wc_fontsiz_textNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE; + mar_set_font(NHW_TEXT,iflags.wc_font_text,iflags.wc_fontsiz_text); + return; + } + if( stricmp( pref, "scroll_margin")==0 ) { + mar_set_margin(iflags.wc_scroll_margin); + Gem_cliparound(u.ux, u.uy); + return; + } + if( stricmp( pref, "ascii_map")==0 ) { + mar_set_tile_mode(!iflags.wc_ascii_map); + doredraw(); + return; + } + if( stricmp( pref, "hilite_pet")==0 ){ + /* MAR -- works without doing something here. */ + return; + } + if( stricmp( pref, "align_message")==0){ + mar_set_msg_align(iflags.wc_align_message-ALIGN_BOTTOM); + return; + } + if(stricmp( pref, "align_status")==0 ){ + mar_set_status_align(iflags.wc_align_status-ALIGN_BOTTOM); + return; + } + if( stricmp( pref, "vary_msgcount")==0 ){ + mar_set_msg_visible(iflags.wc_vary_msgcount); + return; + } +} +/* + * Allocate a copy of the given string. If null, return a string of + * zero length. + * + * This is an exact duplicate of copy_of() in X11/winmenu.c. + */ +static char * +copy_of(s) + const char *s; +{ + if (!s) s = nullstr; + return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s); +} + +#endif /* GEM_GRAPHICS + +/*wingem.c*/ diff --git a/win/gem/wingem1.c b/win/gem/wingem1.c new file mode 100644 index 0000000..90be5eb --- /dev/null +++ b/win/gem/wingem1.c @@ -0,0 +1,2995 @@ +/* SCCS Id: @(#)wingem1.c 3.4 1999/12/10 */ +/* Copyright (c) Christian Bressler 1999 */ +/* NetHack may be freely redistributed. See license for details. */ + +#define __TCC_COMPAT__ + +#include +#include +#include +#include +#include +#include +#include + +#include "gem_rsc.h" +#include "load_img.h" +#include "gr_rect.h" + +#define genericptr_t void * +typedef signed char schar; +#include "wintype.h" +#undef genericptr_t + +#define NDECL(f) f(void) +#define FDECL(f,p) f p +#define CHAR_P char +#define SCHAR_P schar +#define UCHAR_P uchar +#define XCHAR_P xchar +#define SHORT_P short +#define BOOLEAN_P boolean +#define ALIGNTYP_P aligntyp +typedef signed char xchar; +#include "wingem.h" +#undef CHAR_P +#undef SCHAR_P +#undef UCHAR_P +#undef XCHAR_P +#undef SHORT_P +#undef BOOLEAN_P +#undef ALIGNTYP_P +#undef NDECL +#undef FDECL + +static char nullstr[]="", md[]="NetHack 3.4.3", strCancel[]="Cancel", strOk[]="Ok", strText[]="Text"; + +extern winid WIN_MESSAGE, WIN_MAP, WIN_STATUS, WIN_INVEN; + +#define MAXWIN 20 +#define ROWNO 21 +#define COLNO 80 +#define MSGLEN 100 + +#define MAP_GADGETS NAME|MOVER|CLOSER|FULLER|LFARROW|RTARROW|UPARROW|DNARROW|VSLIDE|HSLIDE|SIZER|SMALLER +#define DIALOG_MODE AUTO_DIAL|MODAL|NO_ICONIFY + +/* + * Keyboard translation tables. + */ +#define C(c) (0x1f & (c)) +#define M(c) (0x80 | (c)) + +#define KEYPADLO 0x61 +#define KEYPADHI 0x71 + +#define PADKEYS (KEYPADHI - KEYPADLO + 1) +#define iskeypad(x) (KEYPADLO <= (x) && (x) <= KEYPADHI) + +/* + * Keypad keys are translated to the normal values below. + * When iflags.BIOS is active, shifted keypad keys are translated to the + * shift values below. + */ +static const struct pad { + char normal, shift, cntrl; +} keypad[PADKEYS] = { + {C('['), 'Q', C('[')}, /* UNDO */ + {'?', '/', '?'}, /* HELP */ + {'(', 'a', '('}, /* ( */ + {')', 'w', ')'}, /* ) */ + {'/', '/', '/'}, /* / */ + {C('p'), '$', C('p')}, /* * */ + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'h', 'H', C('h')}, /* 4 */ + {'.', '.', '.'}, + {'l', 'L', C('l')}, /* 6 */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, numpad[PADKEYS] = { + {C('['), 'Q', C('[')} , /* UNDO */ + {'?', '/', '?'}, /* HELP */ + {'(', 'a', '('}, /* ( */ + {')', 'w', ')'}, /* ) */ + {'/', '/', '/'}, /* / */ + {C('p'), '$', C('p')}, /* * */ + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'4', M('4'), '4'}, /* 4 */ + {'.', '.', '.'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define TBUFSZ 300 +#define BUFSZ 256 +extern int yn_number; /* from decl.c */ +extern char toplines[TBUFSZ]; /* from decl.c */ +extern char mapped_menu_cmds[]; /* from options.c */ +extern int mar_iflags_numpad(void); /* from wingem.c */ +extern void Gem_raw_print(const char *); /* from wingem.c */ +extern int mar_hp_query(void); /* from wingem.c */ +extern int mar_get_msg_history(void); /* from wingem.c */ +extern int mar_get_msg_visible(void); /* from wingem.c */ +extern void mar_get_font(int,char **,int *);/* from wingem.c */ +extern int vdi2dev4[]; /* from load_img.c */ + +void recalc_msg_win(GRECT*); +void recalc_status_win(GRECT*); +void calc_std_winplace(int, GRECT *); +int (*v_mtext)(int,int,int,char*); +static int no_glyph; /* the int indicating there is no glyph */ +IMG_header tile_image, titel_image, rip_image; +MFDB Tile_bilder, Map_bild, Titel_bild, Rip_bild, Black_bild, Pet_Mark, FontCol_Bild; +static int Tile_width=16, Tile_heigth=16, Tiles_per_line=20; +char *Tilefile=NULL; +/* pet_mark Design by Warwick Allison warwick@troll.no */ +static int pet_mark_data[]={0x0000,0x3600,0x7F00,0x7F00,0x3E00,0x1C00,0x0800}; +static short *normal_palette=NULL; + +static struct gw{ + WIN *gw_window; + int gw_type, gw_dirty; + GRECT gw_place; +} Gem_nhwindow[MAXWIN]; + +typedef struct { + int id; + int size; + int cw, ch; + int prop; +} NHGEM_FONT; + +/*struct gemmapdata {*/ + GRECT dirty_map_area={COLNO-1,ROWNO,0,0}; + int map_cursx=0, map_cursy=0, curs_col=WHITE; + int draw_cursor=TRUE, scroll_margin=-1; + NHGEM_FONT map_font; + SCROLL scroll_map; + char **map_glyphs=NULL; + dirty_rect *dr_map; +/*};*/ + +/*struct gemstatusdata{*/ + char **status_line; + int Anz_status_lines, status_w, status_align=FALSE; + NHGEM_FONT status_font; + dirty_rect *dr_stat; +/*};*/ + +/*struct gemmessagedata{*/ + int mar_message_pause=TRUE; + int mar_esc_pressed=FALSE; + int messages_pro_zug=0; + char **message_line; + int *message_age; + int msg_pos=0, msg_max=0, msg_anz=0, msg_width=0, msg_vis=3, msg_align=TRUE; + NHGEM_FONT msg_font; + dirty_rect *dr_msg; +/*};*/ + +/*struct geminvdata {*/ + SCROLL scroll_menu; + Gem_menu_item *invent_list; + int Anz_inv_lines=0, Inv_breite=16; + NHGEM_FONT menu_font; + int Inv_how; +/*};*/ + +/*struct gemtextdata{*/ + char **text_lines; + int Anz_text_lines=0, text_width; + NHGEM_FONT text_font; + int use_rip=FALSE; + extern char** rip_line; +/*};*/ + +static OBJECT *zz_oblist[NHICON+1]; + +MITEM scroll_keys[]={ +/* menu, key, state, mode, msg */ + {FAIL,key(CTRLLEFT,0),K_CTRL,PAGE_LEFT,FAIL}, + {FAIL,key(CTRLRIGHT,0),K_CTRL,PAGE_RIGHT,FAIL}, + {FAIL,key(SCANUP,0),K_SHIFT,PAGE_UP,FAIL}, + {FAIL,key(SCANDOWN,0),K_SHIFT,PAGE_DOWN,FAIL}, + {FAIL,key(SCANLEFT,0),0,LINE_LEFT,FAIL}, + {FAIL,key(SCANRIGHT,0),0,LINE_RIGHT,FAIL}, + {FAIL,key(SCANUP,0),0,LINE_UP,FAIL}, + {FAIL,key(SCANDOWN,0),0,LINE_DOWN,FAIL}, + {FAIL,key(SCANLEFT,0),K_SHIFT,LINE_START,FAIL}, + {FAIL,key(SCANRIGHT,0),K_SHIFT,LINE_END,FAIL}, + {FAIL,key(SCANUP,0),K_CTRL,WIN_START,FAIL}, + {FAIL,key(SCANDOWN,0),K_CTRL,WIN_END,FAIL}, + {FAIL,key(SCANHOME,0),K_SHIFT,WIN_END,FAIL}, + {FAIL,key(SCANHOME,0),0,WIN_START,FAIL} +}; +#define SCROLL_KEYS 14 + +static DIAINFO *Inv_dialog; + +#define null_free(ptr) free(ptr), (ptr)=NULL +#define test_free(ptr) if(ptr) null_free(ptr) + +static char *Menu_title=NULL; + +void mar_display_nhwindow(winid); +void mar_check_hilight_status(void){} /* to be filled :-) */ +static char *mar_copy_of(const char *); + +extern void panic(const char *, ...); +void *m_alloc(size_t amt){ + void *ptr; + + ptr=malloc(amt); + if (!ptr) panic("Memory allocation failure; cannot get %lu bytes", amt); + return(ptr); +} + +void mar_clear_messagewin(void){ + int i, *ptr=message_age; + + if(WIN_MESSAGE==WIN_ERR) return; + for(i=msg_anz;--i>=0;ptr++){ + if(*ptr) + Gem_nhwindow[WIN_MESSAGE].gw_dirty=TRUE; + *ptr=FALSE; + } + mar_message_pause=FALSE; + + mar_display_nhwindow(WIN_MESSAGE); +} + +void clipbrd_save(void *data,int cnt,boolean append,boolean is_inv){ + char path[MAX_PATH],*text,*crlf="\r\n"; + long handle; + int i; + + if (data && cnt>0 && scrp_path(path,"scrap.txt") && (handle = append ? Fopen(path,1) : Fcreate(path,0))>0){ + if (append) + Fseek(0L,(int) handle,SEEK_END); + if(is_inv){ + Gem_menu_item *it=(Gem_menu_item *)data; + + for(;it;it=it->Gmi_next){ + text=it->Gmi_str; + Fwrite((int) handle,strlen(text),text); + Fwrite((int) handle,2L,crlf); + } + }else{ + for(i=0;icurr,&frame,&z_win->curr.g_x,&z_win->curr.g_y,NULL)) + window_size(z_win,&z_win->curr); + else + window_top(z_win); +} + +void message_handler(int x, int y){ + switch(objc_find(zz_oblist[MSGWIN],ROOT,MAX_DEPTH,x,y)){ + case UPMSG: + if(msg_pos>msg_vis-1){ + msg_pos--; + Gem_nhwindow[WIN_MESSAGE].gw_dirty=TRUE; + mar_display_nhwindow(WIN_MESSAGE); + } + Event_Timer(50,0,TRUE); + break; + case DNMSG: + if(msg_posob_x=p_w->work.g_x+p_w->work.g_w/2-p_obj->ob_width/2; + p_obj->ob_y=p_w->work.g_y+p_w->work.g_h/2-p_obj->ob_height/2; + return(DIA_LASTPOS); + } + return(DIA_CENTERED); +} + +/****************************** set_no_glyph *************************************/ + +void +mar_set_no_glyph(ng) +int ng; +{ + no_glyph=ng; +} + +void +mar_set_tilefile(name) +char* name; +{ + Tilefile=name; +} +void +mar_set_tilex(value) +int value; +{ + Min(&value,32); + Max(&value,1); + Tile_width=value; +} +void +mar_set_tiley(value) +int value; +{ + Min(&value,32); + Max(&value,1); + Tile_heigth=value; +} +/****************************** userdef_draw *************************************/ + +void rearrange_windows(void); +void mar_set_status_align(int sa){ + if(status_align!=sa){ + status_align=sa; + rearrange_windows(); + } +} +void mar_set_msg_align(int ma){ + if(msg_align!=ma){ + msg_align=ma; + rearrange_windows(); + } +} +void mar_set_msg_visible(int mv){ + if(mv!=msg_vis){ + Max(&mv,1); + Min(&mv,min(msg_anz,20)); + Min(&mv,desk.g_h/msg_font.ch/2); + msg_vis=mv; + rearrange_windows(); + } +} +/* size<0 cellheight; size>0 points */ +void mar_set_fontbyid(int type, int id, int size){ + int chardim[4]; + if(id<=0) + id=ibm_font_id; + if((size>-3 && size<3) || size<-20 || size>20) + size=-ibm_font; + /* MAR -- 17.Mar 2002 For now allow FNT_PROP only with NHW_TEXT */ + if(type!=NHW_TEXT && (FontInfo(id)->type & (FNT_PROP|FNT_ASCII))) + id=ibm_font_id; + switch(type){ + case NHW_MESSAGE: + if(msg_font.size==-size && msg_font.id==id) + break; + msg_font.size=-size; + msg_font.id=id; + msg_font.prop=FontInfo(id)->type & (FNT_PROP|FNT_ASCII); + v_set_text(msg_font.id,msg_font.size,BLACK,0,0,chardim); + msg_font.ch=chardim[3] ? chardim[3] : 1; + msg_font.cw=chardim[2] ? chardim[2] : 1; + msg_width=min(max_w/msg_font.cw-3,MSGLEN); + rearrange_windows(); + break; + case NHW_MAP: + if(map_font.size!=-size || map_font.id!=id){ + MFDB mtmp; + map_font.size=-size; + map_font.id=id; + map_font.prop=FontInfo(id)->type & (FNT_PROP|FNT_ASCII); + v_set_text(map_font.id,map_font.size,BLACK,0,0,chardim); + map_font.ch=chardim[3] ? chardim[3] : 1; + map_font.cw=chardim[2] ? chardim[2] : 1; + mfdb(&mtmp,NULL,(COLNO-1)*map_font.cw, ROWNO*map_font.ch, 0, planes); + if(mfdb_size(&mtmp)>mfdb_size(&FontCol_Bild) && mfdb_size(&mtmp)>mfdb_size(&Map_bild)){ + FontCol_Bild.fd_addr=Map_bild.fd_addr=(int *)realloc(Map_bild.fd_addr,mfdb_size(&mtmp)); + if(!Map_bild.fd_addr) /* FIXME -- Not really neccessary since the former space is still valid */ + panic("Not enough Space for the map."); + } + mfdb(&FontCol_Bild,FontCol_Bild.fd_addr,(COLNO-1)*map_font.cw, ROWNO*map_font.ch, 0, planes); + rearrange_windows(); + } + break; + case NHW_STATUS: + if(status_font.size==-size && status_font.id==id) + break; + status_font.size=-size; + status_font.id=id; + status_font.prop=FontInfo(id)->type & (FNT_PROP|FNT_ASCII); + v_set_text(status_font.id,status_font.size,BLACK,0,0,chardim); + status_font.ch=chardim[3] ? chardim[3] : 1; + status_font.cw=chardim[2] ? chardim[2] : 1; + rearrange_windows(); + break; + case NHW_MENU: + if(menu_font.size==-size && menu_font.id==id) + break; + menu_font.size=-size; + menu_font.id=id; + menu_font.prop=FontInfo(id)->type & (FNT_PROP|FNT_ASCII); + v_set_text(menu_font.id,menu_font.size,BLACK,0,0,chardim); + menu_font.ch=chardim[3] ? chardim[3] : 1; + menu_font.cw=chardim[2] ? chardim[2] : 1; + break; + case NHW_TEXT: + if(text_font.size==-size && text_font.id==id) + break; + text_font.size=-size; + text_font.id=id; + text_font.prop=FontInfo(id)->type & (FNT_PROP|FNT_ASCII); + v_set_text(text_font.id,text_font.size,BLACK,0,0,chardim); + text_font.ch=chardim[3] ? chardim[3] : 1; + text_font.cw=chardim[2] ? chardim[2] : 1; + break; + default: + break; + } +} +void mar_set_font(int type, const char *font_name, int size){ + int id=0; + /* MAR -- 17.Mar 2002 usual Gem behavior, use the Font-ID */ + if(font_name && *font_name){ + id=atoi(font_name); + if(id<=0){ + int i, tid; + char name[32]; + for(i=fonts_loaded;--i>=0;){ + tid=vqt_name(x_handle,i,name); + if(!stricmp(name,font_name)){ + id=tid; + break; + } + } + } + } + mar_set_fontbyid(type,id,size); +} +void rearrange_windows(void){ + GRECT area; + int todo=TRUE; + if(WIN_MAP != WIN_ERR && Gem_nhwindow[WIN_MAP].gw_window){ + scroll_map.px_hline=mar_set_tile_mode(FAIL)?Tile_width:map_font.cw; + scroll_map.px_vline=mar_set_tile_mode(FAIL)?Tile_heigth:map_font.ch; + if(todo){ + calc_std_winplace(FAIL,&area); + todo=FALSE; + } + calc_std_winplace(NHW_MAP,&area); + Gem_nhwindow[WIN_MAP].gw_window->max.g_w=area.g_w; + Gem_nhwindow[WIN_MAP].gw_window->max.g_h=area.g_h; + Gem_nhwindow[WIN_MAP].gw_window->max.g_w=area.g_w; + window_reinit(Gem_nhwindow[WIN_MAP].gw_window,md,md,NULL,FALSE,FALSE); + { + int buf[8]; + buf[3]=K_CTRL; + buf[4]=C('L'); + AvSendMsg(ap_id,AV_SENDKEY,buf); + } + } + if(WIN_MESSAGE != WIN_ERR && Gem_nhwindow[WIN_MESSAGE].gw_window){ + if(todo){ + calc_std_winplace(FAIL,&area); + todo=FALSE; + } + calc_std_winplace(NHW_MESSAGE,&area); + Gem_nhwindow[WIN_MESSAGE].gw_window->min_h=area.g_h; + window_size(Gem_nhwindow[WIN_MESSAGE].gw_window,&area); + redraw_window(Gem_nhwindow[WIN_MESSAGE].gw_window,NULL); + } + if(WIN_STATUS != WIN_ERR && Gem_nhwindow[WIN_STATUS].gw_window){ + if(todo){ + calc_std_winplace(FAIL,&area); + todo=FALSE; + } + calc_std_winplace(NHW_STATUS,&area); + Gem_nhwindow[WIN_STATUS].gw_window->min_h=area.g_h; + window_size(Gem_nhwindow[WIN_STATUS].gw_window,&area); + redraw_window(Gem_nhwindow[WIN_STATUS].gw_window,NULL); + } +} +void my_color_area(GRECT *area, int col){ + int pxy[4]; + + v_set_fill(col,1,IP_SOLID,0); + rc_grect_to_array(area,pxy); + v_bar(x_handle,pxy); +} + +void my_clear_area(GRECT *area){ + my_color_area(area, WHITE); +} + +int mar_set_tile_mode(int); + +static void win_draw_map(int first, WIN *win, GRECT *area){ + int pla[8], w=area->g_w-1, h=area->g_h-1; + int i, x, y; + GRECT back=*area; + + first=first; + + if(!mar_set_tile_mode(FAIL)){ + int start=(area->g_x-win->work.g_x)/map_font.cw+scroll_map.hpos; + int stop=(area->g_x+area->g_w+map_font.cw-1-win->work.g_x)/map_font.cw+scroll_map.hpos; + int starty=(area->g_y-win->work.g_y)/map_font.ch+scroll_map.vpos; + int stopy=min((area->g_y+area->g_h+map_font.ch-1-win->work.g_y)/map_font.ch+scroll_map.vpos,ROWNO); + char tmp; + v_set_text(map_font.id,map_font.size,WHITE,0,0,NULL); + v_set_mode(MD_TRANS); + + x=win->work.g_x-scroll_map.px_hpos+start*map_font.cw; + y=win->work.g_y-scroll_map.px_vpos+starty*map_font.ch; + pla[2]=pla[0]=scroll_map.px_hpos+area->g_x-win->work.g_x; + pla[3]=pla[1]=starty*map_font.ch; + pla[2]+=w; + pla[3]+=map_font.ch-1; + pla[6]=pla[4]=area->g_x; /* x_wert to */ + pla[7]=pla[5]=y; /* y_wert to */ + pla[6]+=w; + pla[7]+=map_font.ch-1; + back.g_h=map_font.ch; + for(i=starty;ig_x-win->work.g_x; + pla[3]=pla[1]=scroll_map.px_vpos+area->g_y-win->work.g_y; + pla[2]+=w; + pla[3]+=h; + pla[6]=pla[4]=area->g_x; /* x_wert to */ + pla[7]=pla[5]=area->g_y; /* y_wert to */ + pla[6]+=w; + pla[7]+=h; + vro_cpyfm(x_handle, S_ONLY, pla, &Map_bild, screen); + } + + if(draw_cursor){ + v_set_line(curs_col,1,1,0,0); + pla[0]=pla[2]=win->work.g_x+scroll_map.px_hline*(map_cursx-scroll_map.hpos); + pla[1]=pla[3]=win->work.g_y+scroll_map.px_vline*(map_cursy-scroll_map.vpos); + pla[2]+=scroll_map.px_hline-1; + pla[3]+=scroll_map.px_vline-1; + v_rect(pla[0],pla[1],pla[2],pla[3]); + } +} + +static int draw_titel(PARMBLK *pb){ + static int pla[8]; + GRECT work=*(GRECT *) &pb->pb_x; + + if(rc_intersect((GRECT *)&pb->pb_xc,&work)){ + pla[0]=pla[1]=0; + pla[2]=pb->pb_w-1; + pla[3]=pb->pb_h-1; + pla[6]=pla[4]=pb->pb_x; /* x_wert to */ + pla[7]=pla[5]=pb->pb_y; /* y_wert to */ + pla[6]+=pb->pb_w-1; + pla[7]+=pb->pb_h-1; + + vro_cpyfm(x_handle, S_ONLY, pla, &Titel_bild, screen); + } + + return(0); +} + +static int draw_lines(PARMBLK *pb){ + GRECT area=*(GRECT *) &pb->pb_x; + + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + char **ptr; + int x=pb->pb_x,y=pb->pb_y,start_line=(area.g_y-y); + + v_set_mode((text_font.cw&7)==0 && text_font.prop==0 ? MD_REPLACE : MD_TRANS); + +/* void v_set_text(int font,int height,int color,int effect,int rotate,int out[4]) */ + v_set_text(text_font.id,text_font.size,BLACK,0,0,NULL); + start_line /= text_font.ch; + y+=start_line*text_font.ch; + x-=(int)scroll_menu.px_hpos; + ptr=&text_lines[start_line+=scroll_menu.vpos]; + start_line = min((area.g_y-y+area.g_h+text_font.ch-1)/text_font.ch,Anz_text_lines-start_line); + area.g_h=text_font.ch; + Vsync(); +/* x=(x+7) & ~7;*/ + for(;--start_line>=0;y+=text_font.ch){ + area.g_y=y; + my_clear_area(&area); + if(**ptr-1){ + v_set_text(FAIL,0,BLUE,0x01,0,NULL); + (*v_mtext)(x_handle,x,y,(*ptr++)+1); + v_set_text(FAIL,0,BLACK,0x00,0,NULL); + }else + (*v_mtext)(x_handle,x,y,(*ptr++)+1); + } + } + return(0); +} + +static int draw_rip(PARMBLK *pb){ + GRECT area=*(GRECT *) &pb->pb_x; + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + char **ptr; + int x=pb->pb_x,y=pb->pb_y,start_line=(area.g_y-y), chardim[4], pla[8],i; + v_set_mode(MD_REPLACE); +/* void v_set_text(int font,int height,int color,int effect,int rotate,int out[4]) */ + v_set_text(text_font.id,text_font.size,BLACK,0,0,chardim); + start_line /= text_font.ch; + y+=start_line*text_font.ch; + x-=scroll_menu.px_hpos; + ptr=&text_lines[start_line+=scroll_menu.vpos]; + start_line = min((area.g_y-y+area.g_h+text_font.ch-1)/text_font.ch,Anz_text_lines-start_line); + area.g_h=text_font.ch; + Vsync(); + x=(x+7) & ~7; + for(;--start_line>=0;y+=text_font.ch){ + area.g_y=y; + my_clear_area(&area); + if(**ptr-1){ + v_set_text(FAIL,0,BLUE,0x01,0,NULL); + (*v_mtext)(x_handle,x,y,(*ptr++)+1); + v_set_text(FAIL,0,BLACK,0x00,0,NULL); + }else + (*v_mtext)(x_handle,x,y,(*ptr++)+1); + } + pla[0]=pla[1]=0; + pla[2]=min(pb->pb_w-1,Rip_bild.fd_w-1); + pla[3]=min(pb->pb_h-1,Rip_bild.fd_h-1); + pla[6]=pla[4]=pb->pb_x+(pb->pb_w-Rip_bild.fd_w)/2; /* x_wert to */ + pla[7]=pla[5]=pb->pb_y; /* y_wert to */ + pla[6]+=pla[2]; + pla[7]+=pla[3]; + vro_cpyfm(x_handle, S_ONLY, pla, &Rip_bild, screen); + v_set_mode(MD_TRANS); + vst_alignment(x_handle,1,5,&i,&i); + pla[5]+=64; + for(i=0;i<7;i++,pla[5]+=chardim[3]){ + v_set_text(text_font.id,(i==0 || i==6) ? text_font.size : 12,WHITE,1,0,chardim); + (*v_mtext)(x_handle,pla[4]+157,pla[5],rip_line[i]); + v_set_text(text_font.id,(i==0 || i==6) ? text_font.size : 12,BLACK,0,0,chardim); + (*v_mtext)(x_handle,pla[4]+157,pla[5],rip_line[i]); + } + vst_alignment(x_handle,0,5,&i,&i); + } + return(0); +} + +static int draw_msgline(PARMBLK *pb){ + GRECT area=*(GRECT *) &pb->pb_x; + + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + int x=pb->pb_x, y=pb->pb_y+(msg_vis-1)*msg_font.ch, foo, i; + char **ptr=&message_line[msg_pos], tmp; + int startx, stopx, starty, stopy; + + x=(x+7) & ~7; /* Byte alignment speeds output up */ + + v_set_mode(MD_REPLACE); + +/* void v_set_text(int font,int height,int color,int effect,int rotate,int out[4]) */ + v_set_text(msg_font.id,msg_font.size,FAIL, FAIL,0,NULL); + vst_alignment(x_handle,0,5,&foo,&foo); + stopy=min(msg_pos,msg_vis); +/* Vsync();*/ + startx=(area.g_x-x)/msg_font.cw-1; /* MAR 06.02.2001 -- because italic covers the next char */ + Max(&startx,0); + stopx=(area.g_x+area.g_w+msg_font.cw-x-1)/msg_font.cw; + x+=startx*msg_font.cw; + for(i=0;ipb_x; + + area.g_x+=2*status_font.cw-2; + area.g_w-=2*status_font.cw-2; + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + int x=pb->pb_x, y=pb->pb_y, startx, stopx, starty, stopy, i; + char tmp; + +/* void v_set_text(int font,int height,int color,int effect,int rotate,int out[4]) */ + v_set_mode(MD_REPLACE); + v_set_text(status_font.id,status_font.size,BLACK,0,0,NULL); + x = (x+2*status_font.cw+6) & ~7; + + startx=(area.g_x-x)/status_font.cw; + starty=(area.g_y-y)/status_font.ch; + stopx=(area.g_x+area.g_w+status_font.ch-1-x)/status_font.cw; + stopy=(area.g_y+area.g_h+status_font.ch-1-y)/status_font.ch; + Max(&startx,0); /* MAR -- Hmm, area.g_x could end up 1 below x */ + Max(&stopx,0); + x+=startx*status_font.cw; + y+=starty*status_font.ch; +/* Vsync();*/ + area.g_h=status_font.ch; + for(i=starty;ipb_x; + + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + int gl, i, x=pb->pb_x, y=pb->pb_y,start_line=area.g_y-y; + Gem_menu_item *it; + + v_set_mode(MD_REPLACE); + v_set_text(menu_font.id,menu_font.size,BLACK,0,0,NULL); + + start_line /= menu_font.ch; + y+=start_line*menu_font.ch; + x-=scroll_menu.px_hpos; + start_line+=scroll_menu.vpos; + + for(it=invent_list,i=start_line; --i>=0 && it; it=it->Gmi_next); + + i = min((area.g_y-y+area.g_h+menu_font.ch-1)/menu_font.ch,Anz_inv_lines-start_line); + + Vsync(); + area.g_h=menu_font.ch; + + for(;(--i>=0) && it;it=it->Gmi_next,y+=menu_font.ch){ + if(it->Gmi_attr) + v_set_text(FAIL,FALSE,BLUE,1,FAIL,NULL); /* Bold */ + else + v_set_text(FAIL,FALSE,BLACK,0,FAIL,NULL); + + area.g_y=y; + my_clear_area(&area); + if((gl=it->Gmi_glyph) != no_glyph){ + int pla[8], h=min(menu_font.ch,Tile_heigth)-1; + + pla[0]=pla[2]=(gl%Tiles_per_line)*Tile_width; /* x_wert from */ + pla[1]=pla[3]=(gl/Tiles_per_line)*Tile_heigth; /* y_wert from */ + pla[4]=pla[6]=x; /* x_wert to */ + pla[5]=pla[7]=y; /* y_wert to */ + pla[2]+=Tile_width-1; + pla[3]+=h; + pla[6]+=Tile_heigth-1; + pla[7]+=h; + + vro_cpyfm(x_handle,S_ONLY,pla,&Tile_bilder,screen); + } + if(it->Gmi_identifier) + it->Gmi_str[2]=it->Gmi_selected ? (it->Gmi_count == -1L ? '+' : '#') : '-'; + (*v_mtext)(x_handle,(x+23) & ~7,y,it->Gmi_str); + } + } + return(0); +} + +static int draw_prompt(PARMBLK *pb){ + GRECT area=*(GRECT *) &pb->pb_x; + + if(rc_intersect((GRECT *)&pb->pb_xc,&area)){ + char **ptr=(char **)pb->pb_parm; + int x=pb->pb_x, y=pb->pb_y, chardim[4]; + +/* void v_set_text(int font,int height,int color,int effect,int rotate,int out[4]) */ + v_set_mode(MD_TRANS); + v_set_text(ibm_font_id,ibm_font,WHITE,0,0,chardim); + Vsync(); + if(planes<4){ + int pxy[4]; + v_set_fill(BLACK,2,4,0); + rc_grect_to_array(&area,pxy); + v_bar(x_handle,pxy); + }else + my_color_area(&area,LWHITE); + (*v_mtext)(x_handle,x,y,*(ptr++)); + if(*ptr) + (*v_mtext)(x_handle,x,y+chardim[3],*ptr); + } + return(0); +} + +static USERBLK ub_lines={draw_lines, 0L}, ub_msg={draw_msgline, 0L}, + ub_inventory={draw_inventory, 0L}, ub_titel={draw_titel, 0L}, + ub_status={draw_status, 0L}, ub_prompt={draw_prompt, 0L}; + +/**************************** rsc_funktionen *****************************/ + +void my_close_dialog(DIAINFO *dialog,boolean shrink_box){ + close_dialog(dialog,shrink_box); + Event_Timer(0,0,TRUE); +} + +void +mar_get_rsc_tree(obj_number, z_ob_obj) +int obj_number; +OBJECT **z_ob_obj; +{ + rsrc_gaddr( R_TREE, obj_number, z_ob_obj ); + fix_objects(*z_ob_obj,SCALING,0,0); +} + +void mar_clear_map(void); + +void +img_error(errnumber) +int errnumber; +{ + char buf[BUFSZ]; + + switch(errnumber){ + case ERR_HEADER : + sprintf(buf,"%s","[1][ Image Header | corrupt. ][ Oops ]"); + break; + case ERR_ALLOC : + sprintf(buf,"%s","[1][ Not enough | memory for | an image. ][ Oops ]"); + break; + case ERR_FILE : + sprintf(buf,"%s","[1][ The Image-file | is not available ][ Oops ]"); + break; + case ERR_DEPACK : + sprintf(buf,"%s","[1][ The Image-file | is corrupt ][ Oops ]"); + break; + case ERR_COLOR : + sprintf(buf,"%s","[1][ Number of colors | not supported ][ Oops ]"); + break; + default: + sprintf(buf,"[1][ img_error | strange error | number: %i ][ Hmm ]",errnumber); + break; + } + form_alert(1,buf); +} + +void mar_change_button_char(OBJECT *z_ob, int nr, char ch){ + *ob_get_text(z_ob,nr,0)=ch; + ob_set_hotkey(z_ob,nr,ch); +} + +void +mar_set_dir_keys() +{ + static int mi_numpad=FAIL; + char mcmd[]="bjnh.lyku", npcmd[]="123456789", *p_cmd; + + if(mi_numpad!=mar_iflags_numpad()){ + OBJECT *z_ob=zz_oblist[DIRECTION]; + int i; + mi_numpad=mar_iflags_numpad(); + ob_set_hotkey(z_ob,DIRDOWN,'>'); + ob_set_hotkey(z_ob,DIRUP,'<'); + p_cmd= mi_numpad ? npcmd : mcmd; + for(i=0;i<9;i++) + mar_change_button_char(z_ob,DIR1+2*i,p_cmd[i]); + } +} + +extern int total_tiles_used; /* tile.c */ + +int +mar_gem_init() +{ + int i, bild_fehler=FALSE, fsize; + char *fname; + static MITEM wish_workaround= {FAIL,key(0,'J'),K_CTRL,W_CYCLE,FAIL}; + OBJECT *z_ob; + + if((i=open_rsc("gem_rsc.rsc",NULL,md,md,md,0,0,0))<=0){ + graf_mouse(M_OFF,NULL); + if(i<0) + form_alert(1,"[3][| Fatal Error | File: GEM_RSC.RSC | not found. ][ grumble ]"); + else + form_alert(1,"[3][| Fatal Error | GEM initialisation | failed. ][ a pity ]"); + return(0); + } + if(planes<1 || planes>8){ + form_alert(1,"[3][ Color-depth | not supported, | try 2-256 colors. ][ Ok ]"); + return(0); + } + MouseBee(); + + /* MAR -- 17.Mar 2002 NVDI 3.0 or better uses v_ftext */ + v_mtext= speedo==3 ? &v_ftext : &v_gtext; + for(i=0;i0 && planes<9){ + normal_palette=(short *)m_alloc(3*colors*sizeof(short)); + get_colors(x_handle,normal_palette, colors); + } + +loadimg: + bild_fehler=depack_img(Tilefile?Tilefile:(planes>=4)?"NH16.IMG":"NH2.IMG",&tile_image); + if(bild_fehler){ + z_ob=zz_oblist[ABOUT]; + ob_undraw_dialog(z_ob,0,0,0,0); + ob_hide(z_ob,OKABOUT,FALSE); + img_error(bild_fehler); + return(0); + } + if(tile_image.img_w%Tile_width || tile_image.img_h%Tile_heigth){ + Tilefile=NULL; + Tile_width=Tile_heigth=16; + printf("size didn't match.\n"); + goto loadimg; + } + if((tile_image.img_w/Tile_width)*(tile_image.img_h/Tile_heigth)=4){ + if(tile_image.planes>1) + img_set_colors(x_handle, tile_image.palette, tile_image.planes); +#if 0 + else{ + int mypalette[]={}; + img_set_colors(x_handle, mypalette, 4); + } +#endif + } + + mfdb(&Tile_bilder, (int *)tile_image.addr, tile_image.img_w, tile_image.img_h, 1, tile_image.planes); + transform_img(&Tile_bilder); + + mfdb(&Map_bild,NULL,(COLNO-1)*Tile_width, ROWNO*Tile_heigth, 0, planes); + mfdb(&FontCol_Bild,NULL,(COLNO-1)*map_font.cw, ROWNO*map_font.ch, 0, planes); + Map_bild.fd_addr=(int *)m_alloc(mfdb_size(&Map_bild)>mfdb_size(&FontCol_Bild)?mfdb_size(&Map_bild):mfdb_size(&FontCol_Bild)); + FontCol_Bild.fd_addr=Map_bild.fd_addr; + + mfdb(&Pet_Mark,pet_mark_data,8, 7, 1, 1); + vr_trnfm(x_handle,&Pet_Mark,&Pet_Mark); + + mfdb(&Black_bild,NULL,16, 32, 1, 1); /* MAR -- 17.Mar 2002 that should cover the biggest map-font */ + Black_bild.fd_addr=(int *)m_alloc(mfdb_size(&Black_bild)); + memset(Black_bild.fd_addr,255,mfdb_size(&Black_bild)); + vr_trnfm(x_handle,&Black_bild,&Black_bild); + + for(i=0;i=0;) + if(Gem_nhwindow[i].gw_type) + mar_destroy_nhwindow(i); + + if(normal_palette){ + img_set_colors(x_handle,normal_palette,tile_image.planes); + null_free(normal_palette); + } + test_free(tile_image.palette); + test_free(tile_image.addr); + test_free(titel_image.palette); + test_free(titel_image.addr); +} + +/************************* mar_curs *******************************/ + +void +mar_curs(x,y) +int x, y; +{ + Min(&dirty_map_area.g_x,x); + Min(&dirty_map_area.g_y,y); + Max(&dirty_map_area.g_w,x); + Max(&dirty_map_area.g_h,y); + Min(&dirty_map_area.g_x,map_cursx); + Min(&dirty_map_area.g_y,map_cursy); + Max(&dirty_map_area.g_w,map_cursx); + Max(&dirty_map_area.g_h,map_cursy); + + map_cursx=x; + map_cursy=y; + + if(WIN_MAP!=WIN_ERR) + Gem_nhwindow[WIN_MAP].gw_dirty=TRUE; +} + +void mar_cliparound(void); +void mar_map_curs_weiter(void) +{ + static int once=TRUE; + + if(once){ + redraw_window(Gem_nhwindow[WIN_STATUS].gw_window,NULL); + redraw_window(Gem_nhwindow[WIN_MESSAGE].gw_window,NULL); + once=FALSE; + } + mar_curs(map_cursx+1,map_cursy); + mar_cliparound(); +} + +/************************* about *******************************/ + +void +mar_about() +{ + xdialog(zz_oblist[ABOUT], md, NULL, NULL, DIA_CENTERED, FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); +} + +/************************* ask_name *******************************/ + +char * +mar_ask_name() +{ + OBJECT *z_ob=zz_oblist[NAMEGET]; + int bild_fehler; + char who_are_you[] = "Who are you? "; + + bild_fehler=depack_img(planes<4 ? "TITLE2.IMG" : "TITLE.IMG", &titel_image); + if(bild_fehler ){ /* MAR -- this isn't lethal */ + ob_set_text(z_ob,NETHACKPICTURE,"missing title.img."); + }else{ + mfdb(&Titel_bild, (int *)titel_image.addr, titel_image.img_w, titel_image.img_h, 1, titel_image.planes); + transform_img(&Titel_bild); + z_ob[NETHACKPICTURE].ob_type=G_USERDEF; + z_ob[NETHACKPICTURE].ob_spec.userblk=&ub_titel; + } + + ob_clear_edit(z_ob); + xdialog(z_ob,who_are_you, NULL, NULL, DIA_CENTERED, FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); + + test_free(titel_image.palette); + test_free(titel_image.addr); + test_free(Titel_bild.fd_addr); + return(ob_get_text(z_ob,PLNAME,0)); +} + +/************************* more *******************************/ + +void +send_key(int key) +{ + int buf[8]; + + buf[3]=0; /* No Shift/Ctrl/Alt */ + buf[4]=key; + AvSendMsg(ap_id,AV_SENDKEY,buf); +} + +void +send_return() +{ + send_key(key(SCANRET,0)); +} + +int +K_Init(xev,availiable) +XEVENT *xev; +int availiable; +{ + xev=xev; + return(MU_KEYBD&availiable); +} + +int +KM_Init(xev,availiable) +XEVENT *xev; +int availiable; +{ + xev=xev; + return((MU_KEYBD|MU_MESAG)&availiable); +} + +int +M_Init(xev,availiable) +XEVENT *xev; +int availiable; +{ + xev=xev; + return(MU_MESAG&availiable); +} + +#define More_Init K_Init + +int +More_Handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + + if(ev&MU_KEYBD){ + char ch=(char)(xev->ev_mkreturn&0x00FF); + DIAINFO *dinf; + WIN *w; + + switch(ch){ + case '\033': /* no more more more */ + case ' ': + if((w=get_top_window()) && (dinf=(DIAINFO *)w->dialog) && dinf->di_tree==zz_oblist[PAGER]){ + if(ch=='\033') + mar_esc_pressed=TRUE; + send_return(); + break; + } + /* Fall thru */ + default: + ev &= ~MU_KEYBD; /* unknown key */ + break; + } + } + return(ev); +} + +void +mar_more() +{ + if(!mar_esc_pressed){ + OBJECT *z_ob=zz_oblist[PAGER]; + WIN *p_w; + + Event_Handler(More_Init,More_Handler); + dial_colors(7,RED,BLACK,RED,RED,BLACK,BLACK,BLACK,BLACK,WHITE,WHITE,WHITE,WHITE,TRUE,TRUE); + if(WIN_MESSAGE!=WIN_ERR && (p_w=Gem_nhwindow[WIN_MESSAGE].gw_window)){ + z_ob->ob_x=p_w->work.g_x; + z_ob->ob_y=p_w->curr.g_y+p_w->curr.g_h+gr_ch; + } + xdialog(z_ob,NULL, NULL, NULL, DIA_LASTPOS, FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); + Event_Handler(NULL,NULL); + + if(planes<4) + dial_colors(4,BLACK,WHITE,RED,RED,WHITE,BLACK,BLACK,BLACK,FAIL,FAIL,FAIL,FAIL,TRUE,TRUE); + else + dial_colors(7,LWHITE,BLACK,RED,RED,BLACK,BLACK,BLACK,BLACK,WHITE,WHITE,WHITE,WHITE,TRUE,TRUE); + } +} + +/************************* Gem_start_menu *******************************/ +void +Gem_start_menu(win) +winid win; +{ + win=win; + if(invent_list){ + Gem_menu_item *curr, *next; + + for(curr=invent_list;curr;curr=next){ + next=curr->Gmi_next; + free(curr->Gmi_str); + free(curr); + } + } + invent_list=NULL; + Anz_inv_lines=0; + Inv_breite=16; +} + +/************************* mar_add_menu *******************************/ + +void +mar_add_menu(win, item) +winid win; +Gem_menu_item *item; +{ + win=win; + item->Gmi_next = invent_list; + invent_list = item; + Anz_inv_lines++; +} + +void +mar_reverse_menu() +{ + Gem_menu_item *next, *head = 0, *curr=invent_list; + + while (curr) { + next = curr->Gmi_next; + curr->Gmi_next = head; + head = curr; + curr = next; + } + invent_list=head; +} + +void +mar_set_accelerators() +{ + char ch='a'; + Gem_menu_item *curr; + + for(curr=invent_list;curr;curr=curr->Gmi_next){ + int extent[8]; + v_set_text(menu_font.id,menu_font.size,BLACK,0,0,NULL); + vqt_extent(x_handle,curr->Gmi_str,extent); + Max(&Inv_breite,extent[4]+Tile_width+menu_font.cw); + if(ch && curr->Gmi_accelerator==0 && curr->Gmi_identifier){ + curr->Gmi_accelerator=ch; + curr->Gmi_str[0]=ch; + if(ch=='z') ch='A'; + else if(ch=='Z') ch=0; + else ch++; + } + } +} + +Gem_menu_item * +mar_hol_inv() +{ + return(invent_list); +} + +/************************* mar_putstr_text *********************/ + +void mar_raw_print(const char *); + +void mar_set_text_to_rip(winid w){ + use_rip=TRUE; +} +void +mar_putstr_text(winid window, int attr, const char *str) +{ + static int zeilen_frei=0; + int breite; + char *ptr; + + window=window; + if(!text_lines){ + text_lines=(char **)m_alloc(12*sizeof(char *)); + zeilen_frei=12; + } + if(!zeilen_frei){ + text_lines=(char **)realloc(text_lines,(Anz_text_lines+12)*sizeof(char *)); + zeilen_frei=12; + } + if(!text_lines){ + mar_raw_print("No room for Text"); + return; + } + + if(str) + breite=strlen(str); + Min(&breite,80); + ptr=text_lines[Anz_text_lines]=(char *)m_alloc(breite*sizeof(char)+2); + *ptr=(char)(attr+1); /* avoid 0 */ + strncpy(ptr+1,str,breite); + ptr[breite+1]=0; + Anz_text_lines++; + zeilen_frei--; +} + +int +mar_set_inv_win(Anzahl, Breite) +int Anzahl, Breite; +{ + OBJECT *z_ob=zz_oblist[LINES]; + int retval=WIN_DIAL|MODAL|NO_ICONIFY; + + scroll_menu.hsize=0; + scroll_menu.vpage= (desk.g_h-3*gr_ch)/scroll_menu.px_vline; + if(Anzahl>scroll_menu.vpage){ + retval |= WD_VSLIDER; + if(Breite>max_w-3*scroll_menu.px_hline){ + retval|=WD_HSLIDER; + scroll_menu.hpage=(max_w-3*scroll_menu.px_hline)/scroll_menu.px_hline; + scroll_menu.hpos=0; + scroll_menu.hsize=Breite/scroll_menu.px_hline; + scroll_menu.vpage=(desk.g_h-4*gr_ch-1)/scroll_menu.px_vline; + } + Anzahl=scroll_menu.vpage; + }else{ + if(Breite>max_w-scroll_menu.px_hline){ + retval|=WD_HSLIDER; + scroll_menu.hpage=(max_w-scroll_menu.px_hline)/scroll_menu.px_hline; + scroll_menu.hpos=0; + scroll_menu.hsize=Breite/scroll_menu.px_hline; + scroll_menu.vpage= (desk.g_h-4*gr_ch-1)/scroll_menu.px_vline; + if(Anzahl>scroll_menu.vpage){ + retval |= WD_VSLIDER; + Anzahl=scroll_menu.vpage; + } + } + scroll_menu.vpage=Anzahl; + } + if((scroll_menu.hmax=scroll_menu.hsize-scroll_menu.hpage)<0) + scroll_menu.hmax=0; + if((scroll_menu.vmax=scroll_menu.vsize-scroll_menu.vpage)<0) + scroll_menu.vmax=0; + + /* left/right/up 2 pixel border down 2gr_ch toolbar */ + z_ob[ROOT].ob_width=z_ob[LINESLIST].ob_width=Breite; + z_ob[ROOT].ob_height= + z_ob[QLINE].ob_y= + z_ob[LINESLIST].ob_height=scroll_menu.px_vline*Anzahl; + z_ob[QLINE].ob_y+=gr_ch/2; + z_ob[ROOT].ob_width+=4; + z_ob[ROOT].ob_height+=2*gr_ch+2; + + return(retval); +} + +/************************* mar_status_dirty *******************************/ + +void +mar_status_dirty() +{ + int ccol; + + ccol=mar_hp_query(); + + if(ccol<2) curs_col=WHITE; /* 50-100% : 0 */ + else if(ccol<3) curs_col=YELLOW; /* 33-50% : 6 */ + else if(ccol<5) curs_col=LYELLOW; /* 20-33% : 14*/ + else if(ccol<10) curs_col=RED; /* 10-20% : 2 */ + else curs_col=MAGENTA; /* <10% : 7*/ +} + +/************************* mar_add_message *******************************/ + +void +mar_add_message(str) +const char *str; +{ + int i, mesg_hist=mar_get_msg_history(); + char *tmp, *rest, buf[TBUFSZ]; + + if(WIN_MESSAGE == WIN_ERR) + return; + + if(!mar_message_pause){ + mar_message_pause=TRUE; + messages_pro_zug=0; + msg_pos=msg_max; + } + + if(msg_max>mesg_hist-2){ + msg_max=mesg_hist-2; + msg_pos--; + if(msg_pos<0) msg_pos=0; + tmp=message_line[0]; + for(i=0;i=msg_width){ + int pos=msg_width; + tmp=toplines+msg_width; + while(*tmp!=' ' && pos>=0){ + tmp--; + pos--; + } + if(pos<=0) pos=msg_width; /* Mar -- Oops, what a word :-) */ + message_age[msg_max]=TRUE; + strncpy(message_line[msg_max],toplines,pos); + message_line[msg_max][pos]=0; + rest=strcpy(buf,toplines+pos); + }else{ + message_age[msg_max]=TRUE; + strncpy(message_line[msg_max],toplines,msg_width); + rest=0; + } + + Gem_nhwindow[WIN_MESSAGE].gw_dirty=TRUE; + if(messages_pro_zug>=mesg_hist){ /* MAR -- Greater then should never happen */ + messages_pro_zug=mesg_hist; + mar_display_nhwindow(WIN_MESSAGE); + } + + if(rest) + mar_add_message(rest); +} + +/************************* mar_add_status_str *******************************/ + +void +mar_add_status_str(str,line) +const char *str; +int line; +{ + int i,last_diff=-1; + GRECT area={0,line*status_font.ch,status_font.cw,status_font.ch}; + for(i=0;(i=0){ + add_dirty_rect(dr_stat,&area); + last_diff=-1; + area.g_w=status_font.cw; + } + for(;i=0) + add_dirty_rect(dr_stat,&area); +} + +/************************* mar_set_menu_title *******************************/ + +void +mar_set_menu_title(str) +const char *str; +{ + test_free(Menu_title); /* just in case */ + Menu_title=mar_copy_of(str ? str : nullstr); +} + +/************************* mar_set_menu_type *******************************/ + +void +mar_set_menu_type(how) +int how; +{ + Inv_how=how; +} + +/************************* Inventory Utils *******************************/ + +void +set_all_on_page(start, page) +int start, page; +{ + Gem_menu_item *curr; + + if(start<0 || page<0) + return; + + for(curr=invent_list; start--&&curr; curr=curr->Gmi_next); + for(; page--&&curr; curr=curr->Gmi_next) + if (curr->Gmi_identifier && !curr->Gmi_selected) + curr->Gmi_selected = TRUE; +} + +void +unset_all_on_page(start, page) +int start, page; +{ + Gem_menu_item *curr; + + if(start<0 || page<0) + return; + + for(curr=invent_list; start--&&curr; curr=curr->Gmi_next); + for(; page--&&curr; curr=curr->Gmi_next) + if (curr->Gmi_identifier && curr->Gmi_selected) { + curr->Gmi_selected = FALSE; + curr->Gmi_count = -1L; + } +} + +void +invert_all_on_page(start, page, acc) +int start, page; +char acc; +{ + Gem_menu_item *curr; + + if(start<0 || page<0) + return; + + for(curr=invent_list; start--&&curr; curr=curr->Gmi_next); + for(; page--&&curr; curr=curr->Gmi_next) + if (curr->Gmi_identifier && (acc == 0 || curr->Gmi_groupacc == acc)) { + if (curr->Gmi_selected) { + curr->Gmi_selected = FALSE; + curr->Gmi_count = -1L; + } else + curr->Gmi_selected = TRUE; + } +} + +/************************* Inv_Handler and Inv_Init *******************************/ + +int scroll_top_dialog(char ch){ + WIN *w; + DIAINFO *dinf; + + if((w=get_top_window()) && (dinf=(DIAINFO *)w->dialog) && dinf->di_tree==zz_oblist[LINES]){ + switch(ch){ + case ' ': + if(scroll_menu.vpos==scroll_menu.vmax){ + send_return(); + break; + } + /* Fall thru */ + case MENU_NEXT_PAGE: + scroll_window(w,PAGE_DOWN,NULL); + break; + case MENU_PREVIOUS_PAGE: + scroll_window(w,PAGE_UP,NULL); + break; + case MENU_FIRST_PAGE: + scroll_window(w,WIN_START,NULL); + break; + case MENU_LAST_PAGE: + scroll_window(w,WIN_END,NULL); + break; + default: + return(FALSE); + } + return(TRUE); + } + return(FALSE); +} + +#define Text_Init KM_Init + +int +Text_Handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + + if(ev&MU_MESAG){ + int *buf=xev->ev_mmgpbuf, y_wo, i; + if(*buf==FONT_CHANGED){ + if(buf[3]>=0){ + mar_set_fontbyid(NHW_TEXT,buf[4],buf[5]); + FontAck(buf[1],1); + } + } + } + if(ev&MU_KEYBD){ + char ch=(char)(xev->ev_mkreturn&0x00FF); + + if(!scroll_top_dialog(ch)) + switch(ch){ + case '\033': + send_return(); /* just closes the textwin */ + break; + case C('c'): + clipbrd_save(text_lines,Anz_text_lines,xev->ev_mmokstate&K_SHIFT,FALSE); + break; + default: + ev &= ~MU_KEYBD; /* unknown key */ + break; + } + } + return(ev); +} + +#define Inv_Init KM_Init + +static long count=0; +int +Inv_Handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + Gem_menu_item *it; + GRECT area; + OBJECT *z_ob=zz_oblist[LINES]; + + ob_pos(z_ob,LINESLIST,&area); + if(ev&MU_MESAG){ + int *buf=xev->ev_mmgpbuf, y_wo, i; + + if(*buf==FONT_CHANGED){ + if(buf[3]>=0){ + mar_set_fontbyid(NHW_MENU,buf[4],buf[5]); + FontAck(buf[1],1); + } + }else + if(*buf==OBJC_CHANGED && buf[3]==LINESLIST){ + ob_undostate(z_ob,LINESLIST,SELECTED); + mouse(NULL,&y_wo); + y_wo=(y_wo-area.g_y)/menu_font.ch+scroll_menu.vpos; + for(it=invent_list,i=0;iGmi_next,i++); + if(it->Gmi_identifier){ + it->Gmi_selected=!it->Gmi_selected; + it->Gmi_count= count==0L ? -1L : count; + count = 0L; + if(Inv_how!=PICK_ANY){ + /*my_close_dialog(Inv_dialog,TRUE);*/ + send_return(); + }else{ + area.g_x=(area.g_x+23+2*menu_font.cw) & ~7; + area.g_w=menu_font.cw; + area.g_h=menu_font.ch; + area.g_y+=(y_wo-scroll_menu.vpos)*menu_font.ch; + ob_draw_chg(Inv_dialog,LINESLIST,&area,FAIL); + } /* how != PICK_ANY */ + } /* identifier */ + }else /* LINESLIST changed */ + ev &= ~MU_MESAG; /* unknown message not used */ + } /* MU_MESAG */ + + if(ev&MU_KEYBD){ + char ch=(char)(xev->ev_mkreturn&0x00FF); + + if(!scroll_top_dialog(ch)){ + switch(ch){ + case '0': /* special 0 is also groupaccelerator for balls */ + if(count<=0) + goto find_acc; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if(Inv_how==PICK_NONE) + goto find_acc; + count = (count * 10L) + (long) (ch - '0'); + break; + case '\033': /* cancel - from counting or loop */ + if(count>0L) + count=0L; + else{ + unset_all_on_page(0, (int)scroll_menu.vsize); + my_close_dialog(Inv_dialog,TRUE); + return(ev); + } + break; + case '\0': /* finished (commit) */ + case '\n': + case '\r': + break; + case MENU_SELECT_PAGE: + if(Inv_how==PICK_NONE) + goto find_acc; + if (Inv_how == PICK_ANY) + set_all_on_page((int)scroll_menu.vpos, scroll_menu.vpage); + break; + case MENU_SELECT_ALL: + if(Inv_how==PICK_NONE) + goto find_acc; + if (Inv_how == PICK_ANY) + set_all_on_page(0, (int)scroll_menu.vsize); + break; + case MENU_UNSELECT_PAGE: + unset_all_on_page((int)scroll_menu.vpos, scroll_menu.vpage); + break; + case MENU_UNSELECT_ALL: + unset_all_on_page(0, (int)scroll_menu.vsize); + break; + case MENU_INVERT_PAGE: + if(Inv_how==PICK_NONE) + goto find_acc; + if (Inv_how == PICK_ANY) + invert_all_on_page((int)scroll_menu.vpos, scroll_menu.vpage, 0); + break; + case MENU_INVERT_ALL: + if(Inv_how==PICK_NONE) + goto find_acc; + if (Inv_how == PICK_ANY) + invert_all_on_page(0, (int)scroll_menu.vsize, 0); + break; + case MENU_SEARCH: + if(Inv_how!=PICK_NONE){ + char buf[BUFSZ]; + Gem_getlin("Search for:",buf); + if(!*buf || buf[0]=='\033') + break; + for(it=invent_list;it;it=it->Gmi_next){ + if(it->Gmi_identifier && strstr(it->Gmi_str,buf)){ + it->Gmi_selected=TRUE; + if(Inv_how!=PICK_ANY){ + my_close_dialog(Inv_dialog,FALSE); + break; + } + } + } + } + break; + case C('c'): + clipbrd_save(invent_list,Anz_inv_lines,xev->ev_mmokstate&K_SHIFT,TRUE); + break; + default: + find_acc: + if(Inv_how==PICK_NONE) + my_close_dialog(Inv_dialog,TRUE); + else + for(it=invent_list;it;it=it->Gmi_next){ + if(it->Gmi_identifier && (it->Gmi_accelerator==ch || it->Gmi_groupacc==ch)){ + it->Gmi_selected=!it->Gmi_selected; + it->Gmi_count= count==0L ? -1L : count; + count = 0L; + if(Inv_how!=PICK_ANY) + my_close_dialog(Inv_dialog,TRUE); + } + } + break; + } /* end switch(ch) */ + if(Inv_how==PICK_ANY){ + area.g_x=(area.g_x+23+2*menu_font.cw) & ~7; + area.g_w=menu_font.cw; + ob_draw_chg(Inv_dialog,LINESLIST,&area,FAIL); + } + } /* !scroll_Inv_dialog */ + } /* MU_KEYBD */ + + if(Inv_how==PICK_ANY){ + ob_set_text(Inv_dialog->di_tree,QLINE,strCancel); + for(it=invent_list;it;it=it->Gmi_next) + if(it->Gmi_identifier && it->Gmi_selected){ + ob_set_text(Inv_dialog->di_tree,QLINE,strOk); + break; + } + ob_draw_chg(Inv_dialog,QLINE,NULL,FAIL); + } + return(ev); +} + +/************************* draw_window *******************************/ + +static void +mar_draw_window( first, win, area) +int first; +WIN *win; +GRECT *area; +{ + OBJECT *obj=(OBJECT *)win->para; + + if(obj){ + if(first){ + obj->ob_x=win->work.g_x; + obj->ob_y=win->work.g_y; + } + if(area==NULL) + area=&(win->work); + objc_draw(obj, ROOT, MAX_DEPTH, area->g_x, area->g_y, area->g_w, area->g_h); + } +} + +/************************* mar_display_nhwindow *******************************/ + +void redraw_winwork(WIN *w,GRECT *area){ + area->g_x+=w->work.g_x; + area->g_y+=w->work.g_y; + redraw_window(w,area); +} +void mar_menu_set_slider(WIN *p_win){ + if(p_win){ + SCROLL *sc=p_win->scroll; + + if(!sc) + return; + + if(p_win->gadgets&HSLIDE){ + long hsize=1000l; + + if(sc->hsize>0 && sc->hpage>0){ + hsize *= sc->hpage; + hsize /= sc->hsize; + } + window_slider(p_win,HOR_SLIDER,0,(int)hsize); + } + if(p_win->gadgets&VSLIDE){ + long vsize=1000l; + + if(sc->vsize>0 && sc->vpage>0){ + vsize *= sc->vpage; + vsize /= sc->vsize; + } + window_slider(p_win,VERT_SLIDER,0,(int)vsize); + } + } +} + +void recalc_msg_win(GRECT *area){ + OBJECT *z_ob; + z_ob=zz_oblist[MSGWIN]; + z_ob[MSGLINES].ob_spec.userblk=&ub_msg; + z_ob[MSGLINES].ob_width= + z_ob[ROOT].ob_width= (msg_width+3)*msg_font.cw; + z_ob[MSGLINES].ob_width-=z_ob[UPMSG].ob_width; + z_ob[ROOT].ob_height= + z_ob[GRABMSGWIN].ob_height= + z_ob[MSGLINES].ob_height=msg_vis*msg_font.ch; + z_ob[DNMSG].ob_y=z_ob[GRABMSGWIN].ob_height-z_ob[DNMSG].ob_height; + window_border(0,0,0,z_ob->ob_width,z_ob->ob_height, area); +} +void recalc_status_win(GRECT *area){ + OBJECT *z_ob; + z_ob=zz_oblist[STATUSLINE]; + z_ob[ROOT].ob_type=G_USERDEF; + z_ob[ROOT].ob_spec.userblk=&ub_status; + z_ob[ROOT].ob_width=(status_w+2)*status_font.cw; + z_ob[ROOT].ob_height= + z_ob[GRABSTATUS].ob_height=2*status_font.ch; + z_ob[GRABSTATUS].ob_width=2*status_font.cw-2; + window_border(0,0,0,z_ob->ob_width,z_ob->ob_height,area); +} +void calc_std_winplace(int which, GRECT *place){ + static int todo=TRUE; + static GRECT me, ma, st; + + if(todo || which<0){ + OBJECT *z_ob; + int map_h_off, foo; + + /* First the messagewin */ + recalc_msg_win(&me); + + /* Now the map */ + wind_calc(WC_BORDER,MAP_GADGETS,0,0,scroll_map.px_hline*(COLNO-1),scroll_map.px_vline*ROWNO,&foo,&foo,&foo,&map_h_off); + map_h_off-=scroll_map.px_vline*ROWNO; + window_border(MAP_GADGETS,0,0,scroll_map.px_hline*(COLNO-1),scroll_map.px_vline*ROWNO, &ma); + + /* Next the statuswin */ + recalc_status_win(&st); + + /* And last but not least a final test */ + ma.g_h=map_h_off+scroll_map.px_vline*ROWNO; + while(me.g_h+ma.g_h+st.g_h>=desk.g_h) + ma.g_h-=scroll_map.px_vline; + /* stack the windows */ + ma.g_y=me.g_y=st.g_y=desk.g_y; + if(status_align){ + ma.g_y+=st.g_h; + if(msg_align){ + st.g_y+=me.g_h; + ma.g_y+=me.g_h; + }else{ + me.g_y+=st.g_h+ma.g_h; + } + }else{ + if(msg_align){ + ma.g_y+=me.g_h; + }else{ + me.g_y+=ma.g_h; + } + st.g_y+=me.g_h+ma.g_h; + } + + if(which) todo=FALSE; + } + switch(which){ + case NHW_MESSAGE: + *place=me; + break; + case NHW_MAP: + *place=ma; + break; + case NHW_STATUS: + *place=st; + break; + default: + break; + } +} + +void +mar_display_nhwindow(wind) +winid wind; +{ + DIAINFO *dlg_info; + OBJECT *z_ob; + int d_exit=W_ABANDON, i, breite, mar_di_mode, tmp_magx=magx; + GRECT g_mapmax, area; + char *tmp_button; + struct gw *p_Gw; + + if(wind == WIN_ERR) return; + + p_Gw=&Gem_nhwindow[wind]; + switch(p_Gw->gw_type){ + case NHW_TEXT: + if(WIN_MESSAGE != WIN_ERR && Gem_nhwindow[WIN_MESSAGE].gw_window) + mar_display_nhwindow(WIN_MESSAGE); + z_ob=zz_oblist[LINES]; + scroll_menu.vsize=Anz_text_lines; + scroll_menu.vpos=0; + if(use_rip){ + if(!depack_img(planes<4 ? "RIP2.IMG" : "RIP.IMG", &rip_image)){ + mfdb(&Rip_bild, (int *)rip_image.addr, rip_image.img_w, rip_image.img_h, 1, rip_image.planes); + transform_img(&Rip_bild); + } + ub_lines.ub_code=draw_rip; + }else + ub_lines.ub_code=draw_lines; + z_ob[LINESLIST].ob_spec.userblk=&ub_lines; + breite=16; + v_set_text(text_font.id,text_font.size,BLACK,0,0,NULL); + for(i=0;idi_win; + + ptr_win->scroll=&scroll_menu; + mar_menu_set_slider(ptr_win); + WindowItems(ptr_win,SCROLL_KEYS,scroll_keys); + if((d_exit=X_Form_Do(NULL))!=W_ABANDON){ + my_close_dialog(dlg_info,FALSE); + if(d_exit!=W_CLOSED) + ob_undostate(z_ob,d_exit&NO_CLICK,SELECTED); + } + } + Event_Handler(NULL,NULL); + ob_set_text(z_ob,QLINE,tmp_button); + break; + case NHW_MENU: + if(WIN_MESSAGE != WIN_ERR && Gem_nhwindow[WIN_MESSAGE].gw_window) + mar_display_nhwindow(WIN_MESSAGE); + z_ob=zz_oblist[LINES]; + scroll_menu.vsize=Anz_inv_lines; + scroll_menu.vpos=0; + z_ob[LINESLIST].ob_spec.userblk=&ub_inventory; + if((Menu_title)&&(wind!=WIN_INVEN)) /* because I sets no Menu_title */ + Max(&Inv_breite,gr_cw*strlen(Menu_title)+16); + scroll_menu.px_vline=menu_font.ch; + scroll_menu.px_hline=menu_font.cw; + mar_di_mode=mar_set_inv_win(Anz_inv_lines, Inv_breite, NHW_MENU); + tmp_button=ob_get_text(z_ob,QLINE,0); + ob_set_text(z_ob,QLINE,Inv_how!=PICK_NONE ? strCancel : strOk ); + ob_doflag(z_ob,LINESLIST,TOUCHEXIT); + Event_Handler(Inv_Init, Inv_Handler); + if((Inv_dialog=open_dialog(z_ob,(wind==WIN_INVEN) ? "Inventory" : (Menu_title ? Menu_title : "Staun"), NULL, NULL, mar_ob_mapcenter(z_ob), FALSE, mar_di_mode, FAIL, NULL, NULL))!=NULL){ + WIN *ptr_win=Inv_dialog->di_win; + + ptr_win->scroll=&scroll_menu; + mar_menu_set_slider(ptr_win); + WindowItems(ptr_win,SCROLL_KEYS,scroll_keys); + do{ + int y_wo,x_wo,ru_w=1,ru_h=1; + GRECT oarea; + Gem_menu_item *it; + d_exit=X_Form_Do(NULL); + if((d_exit&NO_CLICK)==LINESLIST){ + ob_pos(z_ob,LINESLIST,&oarea); + if(mouse(&x_wo,&y_wo) && Inv_how==PICK_ANY){ + graf_rt_rubberbox(FALSE,x_wo,y_wo,FAIL,FAIL,&oarea,&ru_w,&ru_h,NULL); + invert_all_on_page((int)((y_wo-oarea.g_y)/menu_font.ch+scroll_menu.vpos),(ru_h+menu_font.ch-1)/menu_font.ch,0); + }else{ + for(it=invent_list,i=0;i<((y_wo-oarea.g_y)/menu_font.ch+scroll_menu.vpos) && it;it=it->Gmi_next,i++); + if(it && it->Gmi_identifier){ + it->Gmi_selected=!it->Gmi_selected; + it->Gmi_count= count==0L ? -1L : count; + count = 0L; + if(Inv_how!=PICK_ANY) + break; + } /* identifier */ + } + oarea.g_x=(oarea.g_x+23+2*menu_font.cw) & ~7; + oarea.g_y=y_wo-(y_wo-oarea.g_y)%menu_font.ch; + oarea.g_w=menu_font.cw; + oarea.g_h=((ru_h+menu_font.ch-1)/menu_font.ch)*menu_font.ch; + ob_draw_chg(Inv_dialog,LINESLIST,&oarea,FAIL); + } + if(Inv_how==PICK_ANY){ + ob_set_text(Inv_dialog->di_tree,QLINE,strCancel); + for(it=invent_list;it;it=it->Gmi_next) + if(it->Gmi_identifier && it->Gmi_selected){ + ob_set_text(Inv_dialog->di_tree,QLINE,strOk); + break; + } + ob_draw_chg(Inv_dialog,QLINE,NULL,FAIL); + } + }while((d_exit&NO_CLICK)==LINESLIST); + if(d_exit!=W_ABANDON){ + my_close_dialog(Inv_dialog,FALSE); + if(d_exit!=W_CLOSED) + ob_undostate(z_ob,d_exit&NO_CLICK,SELECTED); + } + } + Event_Handler(NULL,NULL); + ob_set_text(z_ob,QLINE,tmp_button); + break; + case NHW_MAP: + if(p_Gw->gw_window==NULL){ + calc_std_winplace(NHW_MAP,&p_Gw->gw_place); + window_border(MAP_GADGETS,0,0,Tile_width*(COLNO-1),Tile_heigth*ROWNO, &g_mapmax); + p_Gw->gw_window=open_window(md, md, NULL, zz_oblist[NHICON], MAP_GADGETS, TRUE, 128, 128, &g_mapmax, &p_Gw->gw_place, &scroll_map, win_draw_map, NULL, XM_TOP|XM_BOTTOM|XM_SIZE); + WindowItems(p_Gw->gw_window,SCROLL_KEYS-1,scroll_keys); /* ClrHome centers on u */ + mar_clear_map(); + } + if(p_Gw->gw_dirty){ + area.g_x=p_Gw->gw_window->work.g_x+scroll_map.px_hline*(dirty_map_area.g_x-scroll_map.hpos); + area.g_y=p_Gw->gw_window->work.g_y+scroll_map.px_vline*(dirty_map_area.g_y-scroll_map.vpos); + area.g_w=(dirty_map_area.g_w-dirty_map_area.g_x+1)*scroll_map.px_hline; + area.g_h=(dirty_map_area.g_h-dirty_map_area.g_y+1)*scroll_map.px_vline; + + redraw_window(p_Gw->gw_window,&area); + + dirty_map_area.g_x=COLNO-1; + dirty_map_area.g_y=ROWNO; + dirty_map_area.g_w= + dirty_map_area.g_h=0; + } + break; + case NHW_MESSAGE: + if(p_Gw->gw_window==NULL){ + calc_std_winplace(NHW_MESSAGE,&p_Gw->gw_place); + z_ob=zz_oblist[MSGWIN]; + magx=0; /* MAR -- Fake E_GEM to remove Backdropper */ + p_Gw->gw_window=open_window(NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL, &p_Gw->gw_place, NULL, mar_draw_window, z_ob, XM_TOP|XM_BOTTOM|XM_SIZE); + magx=tmp_magx; + window_size(p_Gw->gw_window,&p_Gw->gw_window->curr); + p_Gw->gw_dirty=TRUE; + } + + if(p_Gw->gw_dirty){ + ob_pos(zz_oblist[MSGWIN],MSGLINES,&area); + while(messages_pro_zug>3){ + messages_pro_zug-=3; + msg_pos+=3; + redraw_window(p_Gw->gw_window,&area); + mar_more(); + } + msg_pos+=messages_pro_zug; + messages_pro_zug=0; + if(msg_pos>msg_max) msg_pos=msg_max; + redraw_window(p_Gw->gw_window,&area); + mar_message_pause=FALSE; + } + break; + case NHW_STATUS: + if(p_Gw->gw_window==NULL){ + z_ob=zz_oblist[STATUSLINE]; + calc_std_winplace(NHW_STATUS,&p_Gw->gw_place); + magx=0; /* MAR -- Fake E_GEM to remove Backdropper */ + p_Gw->gw_window=open_window(NULL, NULL, NULL, NULL, 0, FALSE, 0, 0, NULL, &p_Gw->gw_place, NULL, mar_draw_window, z_ob, XM_TOP|XM_BOTTOM|XM_SIZE); + magx=tmp_magx; + /* Because 2*status_font.ch is smaller then e_gem expects the minimum win_height */ + p_Gw->gw_window->min_h=z_ob[ROOT].ob_height; + window_size(p_Gw->gw_window,&p_Gw->gw_place); + p_Gw->gw_dirty=TRUE; + add_dirty_rect(dr_stat,&p_Gw->gw_place); + } + while(get_dirty_rect(dr_stat,&area)){ + area.g_x=(area.g_x+p_Gw->gw_window->work.g_x+2*status_font.cw+6)&~7; + area.g_y+=p_Gw->gw_window->work.g_y; + redraw_window(p_Gw->gw_window,&area); + } + break; + default: + if(p_Gw->gw_dirty) + redraw_window(p_Gw->gw_window,NULL); + } + p_Gw->gw_dirty=FALSE; +} + +/************************* create_window *******************************/ + +int mar_hol_win_type(window) +winid window; +{ + return(Gem_nhwindow[window].gw_type); +} + +winid +mar_create_window(type) +int type; +{ + winid newid; + static char name[]="Gem"; + int i; + struct gw *p_Gw=&Gem_nhwindow[0]; + + for(newid = 0; p_Gw->gw_type && newid < MAXWIN; newid++, p_Gw++); + + switch(type){ + case NHW_MESSAGE: + message_line=(char **)m_alloc(msg_anz*sizeof(char *)); + message_age=(int *)m_alloc(msg_anz*sizeof(int)); + for(i=0;igw_window=open_window("Sonst", name, NULL, NULL, NAME|MOVER|CLOSER, 0, 0, 0, NULL, &p_Gw->gw_place, NULL, NULL, NULL, XM_TOP|XM_BOTTOM|XM_SIZE); + break; + } + + p_Gw->gw_type=type; + + return(newid); +} + +void +mar_change_menu_2_text(win) +winid win; +{ + Gem_nhwindow[win].gw_type=NHW_TEXT; +} + +/************************* mar_clear_map *******************************/ + +void +mar_clear_map() +{ + int pla[8]; + int x,y; + + pla[0]=pla[1]=pla[4]=pla[5]=0; + pla[2]=pla[6]=scroll_map.px_hline*(COLNO-1)-1; + pla[3]=pla[7]=scroll_map.px_vline*ROWNO-1; + for(y=0;y0 ? scroll_margin : max(scroll_map.hpage/4,1), + hoehe=scroll_margin>0 ? scroll_margin : max(scroll_map.vpage/4,1), + adjust_needed; + adjust_needed=FALSE; + if ((map_cursx < scroll_map.hpos + breite) || (map_cursx >= scroll_map.hpos + scroll_map.hpage - breite)){ + scroll_map.hpos=map_cursx - scroll_map.hpage/2; + adjust_needed=TRUE; + } + if ((map_cursy < scroll_map.vpos + hoehe) || (map_cursy >= scroll_map.vpos + scroll_map.vpage - hoehe)){ + scroll_map.vpos=map_cursy - scroll_map.vpage/2; + adjust_needed=TRUE; + } + if(adjust_needed) + scroll_window(Gem_nhwindow[WIN_MAP].gw_window,WIN_SCROLL,NULL); + } +} + +void +mar_update_value() +{ + if(WIN_MESSAGE!=WIN_ERR){ + mar_message_pause=FALSE; + mar_esc_pressed=FALSE; + mar_display_nhwindow(WIN_MESSAGE); + } + + if(WIN_MAP!=WIN_ERR) + mar_cliparound(); + + if(WIN_STATUS!=WIN_ERR){ + mar_check_hilight_status(); + mar_display_nhwindow(WIN_STATUS); + } +} + +int +Main_Init(xev,availiable) +XEVENT *xev; +int availiable; +{ + xev->ev_mb1mask= + xev->ev_mb1state=1; + xev->ev_mb1clicks= + xev->ev_mb2clicks= + xev->ev_mb2mask= + xev->ev_mb2state=2; + return((MU_KEYBD|MU_BUTTON1|MU_BUTTON2|MU_MESAG)&availiable); +} + +/* + * return a key, or 0, in which case a mouse button was pressed + * mouse events should be returned as character postitions in the map window. + */ +/*ARGSUSED*/ +int +mar_nh_poskey(x, y, mod) + int *x, *y, *mod; +{ + static XEVENT xev; + int retval, ev; + + xev.ev_mflags=Main_Init(&xev,0xFFFF); + ev=Event_Multi(&xev); + + retval=FAIL; + + if(ev&MU_KEYBD){ + char ch = xev.ev_mkreturn&0x00FF; + char scan = (xev.ev_mkreturn & 0xff00) >> 8; + int shift = xev.ev_mmokstate; + const struct pad *kpad; + + /* Translate keypad keys */ + if (iskeypad(scan)) { + kpad = mar_iflags_numpad()==1 ? numpad : keypad; + if (shift & K_SHIFT) + ch = kpad[scan - KEYPADLO].shift; + else if (shift & K_CTRL){ + if(scan>=0x67 && scan<=0x6f && scan!=0x6b){ + send_key(kpad[scan - KEYPADLO].normal); + ch = 'g'; + }else{ + ch = kpad[scan - KEYPADLO].cntrl; + } + }else + ch = kpad[scan - KEYPADLO].normal; + } + if(scan==SCANHOME) + mar_cliparound(); + else if(scan == SCANF1) + retval='h'; + else if(scan == SCANF2){ + mar_set_tile_mode(!mar_set_tile_mode(FAIL)); + retval=C('l'); /* trigger full-redraw */ + }else if(scan == SCANF3){ + draw_cursor=!draw_cursor; + mar_curs(map_cursx,map_cursy); + mar_display_nhwindow(WIN_MAP); + }else if(scan == SCANF4){ /* Font-Selector */ + if(!CallFontSelector(0,FAIL,FAIL,FAIL,FAIL)){ + xalert(1,1,X_ICN_ALERT,NULL,SYS_MODAL,BUTTONS_RIGHT,TRUE,"Hello","Fontselector not available!",NULL); + } + }else if(!ch && shift&K_CTRL && scan==-57){ + /* MAR -- nothing ignore Ctrl-Alt-Clr/Home == MagiC's restore screen */ + }else{ + if(!ch) + ch=(char)M(tolower(scan_2_ascii(xev.ev_mkreturn,shift))); + if(((int)ch)==-128) + ch='\033'; + retval=ch; + } + } + + if(ev&MU_BUTTON1 || ev&MU_BUTTON2){ + int ex=xev.ev_mmox, ey=xev.ev_mmoy; + WIN *akt_win=window_find(ex,ey); + + if(WIN_MAP != WIN_ERR && akt_win==Gem_nhwindow[WIN_MAP].gw_window){ + *x=max(min((ex-akt_win->work.g_x)/scroll_map.px_hline+scroll_map.hpos,COLNO-1),0)+1; + *y=max(min((ey-akt_win->work.g_y)/scroll_map.px_vline+scroll_map.vpos,ROWNO),0); + *mod=xev.ev_mmobutton; + retval=0; + }else if(WIN_STATUS != WIN_ERR && akt_win==Gem_nhwindow[WIN_STATUS].gw_window){ + move_win(akt_win); + }else if(WIN_MESSAGE != WIN_ERR && akt_win==Gem_nhwindow[WIN_MESSAGE].gw_window){ + message_handler(ex,ey); + } + } + + if(ev&MU_MESAG){ + int *buf=xev.ev_mmgpbuf; + char *str; + OBJECT *z_ob=zz_oblist[MENU]; + + switch(*buf) { + case MN_SELECTED : + menu_tnormal(z_ob,buf[3],TRUE); /* unselect menu header */ + str=ob_get_text(z_ob,buf[4],0); + str+=strlen(str)-2; + switch(*str){ + case ' ': /* just that command */ + retval=str[1]; + break; + case '\005': /* Alt command */ + case '\007': + retval=M(str[1]); + break; + case '^': /* Ctrl command */ + retval=C(str[1]); + break; + case 'f': /* Func Key */ + switch(str[1]){ + case '1': + retval='h'; + break; + case '2': + mar_set_tile_mode(!mar_set_tile_mode(FAIL)); + retval=C('l'); /* trigger full-redraw */ + break; + case '3': + draw_cursor=!draw_cursor; + mar_curs(map_cursx,map_cursy); + mar_display_nhwindow(WIN_MAP); + break; + default: + } + break; + default: + mar_about(); + break; + } + break; /* MN_SELECTED */ + case WM_CLOSED: + WindowHandler(W_ICONIFYALL,NULL,NULL); + break; + case AP_TERM: + retval='S'; + break; + case FONT_CHANGED: + if(buf[3]>=0){ + if(buf[3]==Gem_nhwindow[WIN_MESSAGE].gw_window->handle){ + mar_set_fontbyid(NHW_MESSAGE,buf[4],buf[5]); + mar_display_nhwindow(WIN_MESSAGE); + }else if(buf[3]==Gem_nhwindow[WIN_MAP].gw_window->handle){ + mar_set_fontbyid(NHW_MAP,buf[4],buf[5]); + mar_display_nhwindow(WIN_MAP); + }else if(buf[3]==Gem_nhwindow[WIN_STATUS].gw_window->handle){ + mar_set_fontbyid(NHW_STATUS,buf[4],buf[5]); + mar_display_nhwindow(WIN_STATUS); + } + FontAck(buf[1],1); + } + break; + default: + break; + } + } /* MU_MESAG */ + + if(retval==FAIL) + retval=mar_nh_poskey(x,y,mod); + + return(retval); +} + +int +Gem_nh_poskey(x, y, mod) + int *x, *y, *mod; +{ + mar_update_value(); + return(mar_nh_poskey(x, y, mod)); +} + +void +Gem_delay_output() +{ + Event_Timer(50,0,FALSE); /* wait 50ms */ +} + +int +Gem_doprev_message() +{ + if(msg_pos>2){ + msg_pos--; + if(WIN_MESSAGE != WIN_ERR) + Gem_nhwindow[WIN_MESSAGE].gw_dirty=TRUE; + mar_display_nhwindow(WIN_MESSAGE); + } + return(0); +} + +/************************* print_glyph *******************************/ + +int mar_set_rogue(int); + +int +mar_set_tile_mode(tiles) +int tiles; +{ + static int tile_mode=TRUE; + static GRECT prev; + WIN *z_w=WIN_MAP!=WIN_ERR ? Gem_nhwindow[WIN_MAP].gw_window : NULL; + + if(tiles<0) + return(tile_mode); + else if(!z_w) + tile_mode=tiles; + else if(tile_mode==tiles || (mar_set_rogue(FAIL) && tiles)) + return(FAIL); + else{ + GRECT tmp; + + tile_mode=tiles; + scroll_map.px_hline= tiles ? Tile_width : map_font.cw; + scroll_map.px_vline= tiles ? Tile_heigth : map_font.ch; + window_border(MAP_GADGETS,0,0,scroll_map.px_hline*(COLNO-1),scroll_map.px_vline*ROWNO, &tmp); + z_w->max.g_w=tmp.g_w; + z_w->max.g_h=tmp.g_h; + if(tiles) + z_w->curr=prev; + else + prev=z_w->curr; + + window_reinit(z_w,md,md,NULL,FALSE,FALSE); + } + return(FAIL); +} + +int +mar_set_rogue(what) +int what; +{ + static int rogue=FALSE, prev_mode=TRUE; + + if(what<0) return(rogue); + if(what!=rogue){ + rogue=what; + if(rogue){ + prev_mode=mar_set_tile_mode(FAIL); + mar_set_tile_mode(FALSE); + }else + mar_set_tile_mode(prev_mode); + } + return(FAIL); +} + +void +mar_add_pet_sign(window,x,y) +winid window; +int x, y; +{ + if(window != WIN_ERR && window==WIN_MAP){ + static int pla[8]={0,0,7,7,0,0,0,0}, colindex[2]={RED,WHITE}; + + pla[4]=pla[6]=scroll_map.px_hline*x; + pla[5]=pla[7]=scroll_map.px_vline*y; + pla[6]+=7; + pla[7]+=6; + vrt_cpyfm(x_handle,MD_TRANS,pla,&Pet_Mark,&Map_bild,colindex); + } +} + +void +mar_print_glyph(window, x, y, gl) +winid window; +int x, y, gl; +{ + if(window != WIN_ERR && window==WIN_MAP){ + static int pla[8]; + + pla[2]=pla[0]=(gl%Tiles_per_line)*Tile_width; + pla[3]=pla[1]=(gl/Tiles_per_line)*Tile_heigth; + pla[2]+=Tile_width-1; + pla[3]+=Tile_heigth-1; + pla[6]=pla[4]=Tile_width*x; /* x_wert to */ + pla[7]=pla[5]=Tile_heigth*y; /* y_wert to */ + pla[6]+=Tile_width-1; + pla[7]+=Tile_heigth-1; + + vro_cpyfm(x_handle, gl!=-1 ? S_ONLY : ALL_BLACK, pla, &Tile_bilder, &Map_bild); + } + } + +void +mar_print_char(window, x, y, ch, col) +winid window; +int x, y; +char ch; +int col; +{ + if(window != WIN_ERR && window==WIN_MAP){ + static int gem_color[16]={ 9, 2,11,10, 4, 7, 8, 15,0,14, 3, 6, 5, 13,15, 0}; + int pla[8], colindex[2]; + + map_glyphs[y][x]=ch; + + pla[0]= + pla[1]=0; + pla[2]=map_font.cw-1; + pla[3]=map_font.ch-1; + pla[6]=pla[4]=map_font.cw*x; + pla[7]=pla[5]=map_font.ch*y; + pla[6]+=map_font.cw-1; + pla[7]+=map_font.ch-1; + colindex[0]=gem_color[col]; + colindex[1]=WHITE; + vrt_cpyfm(x_handle,MD_REPLACE,pla,&Black_bild,&FontCol_Bild,colindex); + } +} + +/************************* getlin *******************************/ + +void +Gem_getlin(ques, input) +const char *ques; +char *input; +{ + OBJECT *z_ob=zz_oblist[LINEGET]; + int d_exit, length; + char *pr[2], *tmp; + + if(WIN_MESSAGE != WIN_ERR && Gem_nhwindow[WIN_MESSAGE].gw_window) + mar_display_nhwindow(WIN_MESSAGE); + + z_ob[LGPROMPT].ob_type=G_USERDEF; + z_ob[LGPROMPT].ob_spec.userblk=&ub_prompt; + z_ob[LGPROMPT].ob_height=2*gr_ch; + + length=z_ob[LGPROMPT].ob_width/gr_cw; + if(strlen(ques)>length){ + tmp=ques+length; + while(*tmp!=' ' && tmp>=ques){ + tmp--; + } + if(tmp<=ques) tmp=ques+length; /* Mar -- Oops, what a word :-) */ + pr[0]=ques; + *tmp=0; + pr[1]=++tmp; + }else{ + pr[0]=ques; + pr[1]=NULL; + } + ub_prompt.ub_parm=(long)pr; + + ob_clear_edit(z_ob); + d_exit=xdialog(z_ob, nullstr, NULL, NULL, mar_ob_mapcenter(z_ob), FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); + + if(d_exit==W_CLOSED || d_exit==W_ABANDON || (d_exit&NO_CLICK)==QLG){ + *input='\033'; + input[1]=0; + }else + strncpy(input,ob_get_text(z_ob,LGREPLY, 0),length); +} + +/************************* ask_direction *******************************/ + +#define Dia_Init K_Init + +int +Dia_Handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + char ch=(char)(xev->ev_mkreturn&0x00FF); + + if(ev&MU_KEYBD){ + WIN *w; + DIAINFO *dinf; + + switch(ch){ + case 's': + send_key((int)(mar_iflags_numpad() ? '5' : '.')); + break; + case '.': + send_key('5'); /* MAR -- '.' is a button if numpad isn't set */ + break; + case '\033': /*ESC*/ + if((w=get_top_window()) && (dinf=(DIAINFO *)w->dialog) && dinf->di_tree==zz_oblist[DIRECTION]){ + my_close_dialog(dinf,FALSE); + break; + } + /* Fall thru */ + default: + ev &= ~MU_KEYBD; /* let the dialog handle it */ + break; + } + } + return(ev); +} + +int +mar_ask_direction() +{ + int d_exit; + OBJECT *z_ob=zz_oblist[DIRECTION]; + + Event_Handler(Dia_Init,Dia_Handler); + mar_set_dir_keys(); + d_exit=xdialog(z_ob, nullstr, NULL, NULL, mar_ob_mapcenter(z_ob), FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); + Event_Handler(NULL,NULL); + + if(d_exit==W_CLOSED || d_exit==W_ABANDON) + return('\033'); + if((d_exit&NO_CLICK)==DIRDOWN) + return('>'); + if((d_exit&NO_CLICK)==DIRUP) + return('<'); + if((d_exit&NO_CLICK)==(DIR1+8)) /* 5 or . */ + return('.'); + return(*ob_get_text(z_ob,d_exit&NO_CLICK,0)); +} + +/************************* yn_function *******************************/ + + +#define any_init M_Init + +static int +any_handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + + if(ev&MU_MESAG){ + int *buf=xev->ev_mmgpbuf; + + if(*buf==OBJC_EDITED) + my_close_dialog(*(DIAINFO **)&buf[4], FALSE); + else + ev &= ~MU_MESAG; + } + return(ev); +} + +int +send_yn_esc(char ch) +{ + static char esc_char=0; + + if(ch<0){ + if(esc_char){ + send_key((int)esc_char); + return(TRUE); + } + return(FALSE); + }else + esc_char=ch; + return(TRUE); +} + +#define single_init K_Init + +static int +single_handler(xev) +XEVENT *xev; +{ + int ev=xev->ev_mwich; + + if(ev&MU_KEYBD){ + char ch=(char)xev->ev_mkreturn&0x00FF; + WIN *w; + DIAINFO *dinf; + + switch(ch){ + case ' ': + send_return(); + break; + case '\033': + if((w=get_top_window()) && (dinf=(DIAINFO *)w->dialog) && dinf->di_tree==zz_oblist[YNCHOICE]){ + if(!send_yn_esc(FAIL)) + my_close_dialog(dinf,FALSE); + break; + } + /* Fall thru */ + default: + ev &= ~MU_MESAG; + } + } + return(ev); +} + +char +Gem_yn_function(query,resp, def) +const char *query,*resp; +char def; +{ + OBJECT *z_ob=zz_oblist[YNCHOICE]; + int d_exit, i, len; + long anzahl; + char *tmp; + const char *ptr; + + if(WIN_MESSAGE != WIN_ERR && Gem_nhwindow[WIN_MESSAGE].gw_window) + mar_display_nhwindow(WIN_MESSAGE); + + /* if query for direction the special dialog */ + if(strstr(query,"irect")) + return(mar_ask_direction()); + + len=min(strlen(query),(max_w-8*gr_cw)/gr_cw); + z_ob[ROOT].ob_width=(len+8)*gr_cw; + z_ob[YNPROMPT].ob_width=gr_cw*len+8; + tmp=ob_get_text(z_ob,YNPROMPT,0); + ob_set_text(z_ob,YNPROMPT,mar_copy_of(query)); + + if(resp){ /* single inputs */ + ob_hide(z_ob,SOMECHARS,FALSE); + ob_hide(z_ob,ANYCHAR,TRUE); + + if(strchr(resp,'q')) send_yn_esc('q'); + else if(strchr(resp,'n')) send_yn_esc('n'); + else send_yn_esc(def); /* strictly def should be returned, but in trad. I it's 0 */ + + if(strchr(resp,'#')){ /* count possible */ + ob_hide(z_ob,YNOK,FALSE); + ob_hide(z_ob,COUNT,FALSE); + }else{ /* no count */ + ob_hide(z_ob,YNOK,TRUE); + ob_hide(z_ob,COUNT,TRUE); + } + + if((anzahl=(long)strchr(resp,'\033'))){ + anzahl-=(long)resp; + }else{ + anzahl=strlen(resp); + } + for(i=0,ptr=resp;i<2*anzahl;i+=2,ptr++){ + ob_hide(z_ob,YN1+i,FALSE); + mar_change_button_char(z_ob,YN1+i,*ptr); + ob_undoflag(z_ob,YN1+i,DEFAULT); + if(*ptr==def) + ob_doflag(z_ob,YN1+i,DEFAULT); + } + + z_ob[SOMECHARS].ob_width=z_ob[YN1+i].ob_x+8; + z_ob[SOMECHARS].ob_height=z_ob[YN1+i].ob_y+gr_ch+gr_ch/2; + Max((int *)&z_ob[ROOT].ob_width,z_ob[SOMECHARS].ob_width+4*gr_cw); + z_ob[ROOT].ob_height=z_ob[SOMECHARS].ob_height+4*gr_ch; + if(strchr(resp,'#')) + z_ob[ROOT].ob_height=z_ob[YNOK].ob_y+2*gr_ch; + + for(i+=YN1;i<(YNN+1);i+=2){ + ob_hide(z_ob,i,TRUE); + } + Event_Handler(single_init,single_handler); + }else{ /* any input */ + ob_hide(z_ob,SOMECHARS,TRUE); + ob_hide(z_ob,ANYCHAR,FALSE); + ob_hide(z_ob,YNOK,TRUE); + ob_hide(z_ob,COUNT,TRUE); + z_ob[ANYCHAR].ob_height=2*gr_ch; + z_ob[CHOSENCH].ob_y= + z_ob[CHOSENCH+1].ob_y=gr_ch/2; + z_ob[ROOT].ob_width=max(z_ob[YNPROMPT].ob_width+z_ob[YNPROMPT].ob_x,z_ob[ANYCHAR].ob_width+z_ob[ANYCHAR].ob_x)+2*gr_cw; + z_ob[ROOT].ob_height=z_ob[ANYCHAR].ob_height+z_ob[ANYCHAR].ob_y+gr_ch/2; + *ob_get_text(z_ob,CHOSENCH,0)='?'; + Event_Handler(any_init,any_handler); + } + + d_exit=xdialog(z_ob, nullstr, NULL, NULL, mar_ob_mapcenter(z_ob), FALSE, DIALOG_MODE); + Event_Timer(0,0,TRUE); + Event_Handler(NULL,NULL); + /* display of count is missing (through the core too) */ + + free(ob_get_text(z_ob,YNPROMPT,0)); + ob_set_text(z_ob,YNPROMPT,tmp); + + if(resp && (d_exit==W_CLOSED || d_exit==W_ABANDON)) + return('\033'); + if((d_exit&NO_CLICK)==YNOK){ + yn_number=atol(ob_get_text(z_ob,COUNT,0)); + return('#'); + } + if(!resp) + return(*ob_get_text(z_ob,CHOSENCH,0)); + return(*ob_get_text(z_ob,d_exit&NO_CLICK,0)); +} + +/* + * Allocate a copy of the given string. If null, return a string of + * zero length. + * + * This is an exact duplicate of copy_of() in X11/winmenu.c. + */ +static char * +mar_copy_of(s) + const char *s; +{ + if (!s) s = nullstr; + return strcpy((char *) m_alloc((unsigned) (strlen(s) + 1)), s); +} + +const char *strRP="raw_print", *strRPB="raw_print_bold"; + +void +mar_raw_print(str) +const char *str; +{ + xalert(1,FAIL,X_ICN_INFO,NULL,APPL_MODAL,BUTTONS_CENTERED,TRUE,strRP,str,NULL); +} + +void +mar_raw_print_bold(str) +const char *str; +{ + char buf[BUFSZ]; + + sprintf(buf,"!%s",str); + xalert(1,FAIL,X_ICN_INFO,NULL,APPL_MODAL,BUTTONS_CENTERED,TRUE,strRPB,buf,NULL); +} + +/*wingem1.c*/ diff --git a/win/gem/xpm2img.c b/win/gem/xpm2img.c new file mode 100644 index 0000000..605d5db --- /dev/null +++ b/win/gem/xpm2img.c @@ -0,0 +1,164 @@ +/* SCCS Id: @(#)xpm2img.c 3.4 2002/03/17 */ +/* Copyright (c) Christian Bressler 2002 */ +/* NetHack may be freely redistributed. See license for details. */ +/* This is mainly a reworked tile2bmp.c + xpm2iff.c -- Marvin */ +#include +#include +#include +#include "bitmfile.h" +#define TRUE 1 +#define FALSE 0 +void get_color(unsigned int colind, struct RGB *rgb); +void get_pixel(int x, int y, unsigned int *colind); +char *xpmgetline(); +unsigned int **Bild_daten; +/* translation table from xpm characters to RGB and colormap slots */ +struct Ttable { + char flag; + struct RGB col; + int slot; /* output colortable index */ +}ttable[256]; +struct RGB *ColorMap; +int num_colors = 0; +int width=0, height=0; +int initflag; +FILE *fp; +int +main(argc, argv) +int argc; +char *argv[]; +{ + int i; + int row, col, planeno; + int farben, planes; + if (argc != 3) { + fprintf(stderr, "usage: tile2img infile.xpm outfile.img\n"); + exit(EXIT_FAILURE); + } + initflag = 0; + fp = fopen(argv[2],"wb"); + if (!fp) { + printf("Error creating IMG-file %s, aborting.\n",argv[2]); + exit(EXIT_FAILURE); + } + fclose(fp); + if(fopen_xpm_file(argv[1],"r")!=TRUE){ + printf("Error reading xpm-file %s, aborting.\n",argv[1]); + exit(EXIT_FAILURE); + } + Bild_daten=(unsigned int **)malloc((long)height*sizeof(unsigned int *)); + for(i=0;i256){ + fprintf(stderr,"ERROR: zuviele Farben\n"); + exit(EXIT_FAILURE); + }else if(num_colors>16){ + farben=256; + planes=8; + }else if(num_colors>2){ + farben=16; + planes=4; + }else{ + farben=2; + planes=1; + } + bitmap_to_file(XIMG, width, height, 372, 372, planes, farben, argv[2], get_color, get_pixel ); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} +void get_color(unsigned int colind, struct RGB *rgb){ + rgb->r=(1000L*(long)ColorMap[colind].r)/0xFF; + rgb->g=(1000L*(long)ColorMap[colind].g)/0xFF; + rgb->b=(1000L*(long)ColorMap[colind].b)/0xFF; +} +void get_pixel(int x, int y, unsigned int *colind){ + *colind=Bild_daten[y][x]; +} +FILE *xpmfh = 0; +char initbuf[200]; +char *xpmbuf = initbuf; +/* version 1. Reads the raw xpm file, NOT the compiled version. This is + * not a particularly good idea but I don't have time to do the right thing + * at this point, even if I was absolutely sure what that was. */ +fopen_xpm_file(const char *fn, const char *mode){ + int temp; + char *xb; + if(strcmp(mode, "r"))return FALSE; /* no choice now */ + if(xpmfh)return FALSE; /* one file at a time */ + xpmfh = fopen(fn, mode); + if(!xpmfh)return FALSE; /* I'm hard to please */ + /* read the header */ + xb = xpmgetline(); + if(xb == 0)return FALSE; + if(4 != sscanf(xb,"%d %d %d %d", + &width, &height,&num_colors, &temp)) + return FALSE; /* bad header */ + /* replace the original buffer with one big enough for + * the real data + */ +/* XXX */ + xpmbuf = malloc(width * 2); + if(!xpmbuf){ + fprintf(stderr,"ERROR: Can't allocate line buffer\n"); + exit(1); + } + if(temp != 1)return FALSE; /* limitation of this code */ + { + /* read the colormap and translation table */ + int ccount = -1; + ColorMap = (struct RGB *)malloc((long)num_colors*sizeof(struct RGB)); + while(ccount++ < (num_colors-1)){ + char index; + int r, g, b; + xb = xpmgetline(); + if(xb==0)return FALSE; + if(4 != sscanf(xb,"%c c #%2x%2x%2x",&index,&r,&g,&b)){ + fprintf(stderr,"Bad color entry: %s\n",xb); + return FALSE; + } + ttable[index].flag = 1; /* this color is valid */ + ttable[index].col.r = r; + ttable[index].col.g = g; + ttable[index].col.b = b; + ttable[index].slot = ccount; + ColorMap[ccount].r=r; + ColorMap[ccount].g=g; + ColorMap[ccount].b=b; + } + } + return TRUE; +} +/* This deserves better. Don't read it too closely - you'll get ill. */ +#define bufsz 2048 +char buf[bufsz]; +char * +xpmgetline(){ + char *bp; + do { + if(fgets(buf, bufsz, xpmfh) == 0)return 0; + } while(buf[0] != '"'); + /* strip off the trailing <",> if any */ + for(bp = buf;*bp;bp++); + bp--; + while(isspace(*bp))bp--; + if(*bp==',')bp--; + if(*bp=='"')bp--; + bp++; + *bp = '\0'; + return &buf[1]; +} diff --git a/win/gnome/README b/win/gnome/README new file mode 100644 index 0000000..3f7ec20 --- /dev/null +++ b/win/gnome/README @@ -0,0 +1,53 @@ +This directory contains the windowing code written for GnomeHack. The NetHack +devteam is in the process of making it part of the normal distribution. +It should be noted that this is still work in progress and that there are +still problems with this code. So use at your own risk. Of course any +contributions, especially bug fixes, are more than welcome! + +These files are based on the files from GnomeHack 1.0.5 by Erik Andersen. +Some files have been renamed to fit into 8.3 name constraints (yuk!). +These are: + + GnomeHack.h gnomeprv.h + GnomeHackAskStringDialog.c gnaskstr.c + GnomeHackAskStringDialog.h gnaskstr.h + GnomeHackBind.c gnbind.c + GnomeHackBind.h gnbind.h + GnomeHackGlyph.c gnglyph.c + GnomeHackGlyph.h gnglyph.h + GnomeHackMainWindow.c gnmain.c + GnomeHackMainWindow.h gnmain.h + GnomeHackMapWindow.c gnmap.c + GnomeHackMapWindow.h gnmap.h + GnomeHackMenuWindow.c gnmenu.c + GnomeHackMenuWindow.h gnmenu.h + GnomeHackMessageWindow.c gnmesg.c + GnomeHackMessageWindow.h gnmesg.h + GnomeHackPlayerSelDialog.c gnplayer.c + GnomeHackPlayerSelDialog.h gnplayer.h + GnomeHackSettings.c gnopts.c + GnomeHackSettings.h gnopts.h + GnomeHackSignals.c gnsignal.c + GnomeHackSignals.h gnsignal.h + GnomeHackStatusWindow.c gnstatus.c + GnomeHackStatusWindow.h gnstatus.h + GnomeHackTextWindow.c gntext.c + GnomeHackTextWindow.h gntext.h + GnomeHackYesNoDialog.c gnyesno.c + GnomeHackYesNoDialog.h gnyesno.h + +Other files have been removed because we don't or can't use them (yet). + + Makefile.am + Makefile.in + gnomehack.desktop + gnomehack.desktop.in + +NetHack currently doesn't use autoconf, so the setup for that has not +made the translation. + +Note: All loss in style, elegance, and readability is entirely our fault +and not Erik's. + + + diff --git a/win/gnome/gn_xpms.h b/win/gnome/gn_xpms.h new file mode 100644 index 0000000..28408a0 --- /dev/null +++ b/win/gnome/gn_xpms.h @@ -0,0 +1,1450 @@ +/* SCCS Id: @(#)gnxpms.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ +/* These XPMs are the artwork of Warwick Allison + * . They have been borrowed from + * the most excellent NetHackQt, until such time as + * we can come up with something better. + * + * More information about NetHackQt can be had from: + * http://www.troll.no/~warwick/nethack/ + */ + +/* XPM */ +static char *blind_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 5 1", +/* colors */ +" c #000000", +". c None", +"X c #909090", +"o c #606060", +"O c #303030", +/* pixels */ +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"....ooooooooooooooooooooooooooooooooX...", +".... o...", +".... o...", +".... o...", +".... o...", +"......o ..o ......", +"......X O..X O......", +"....... o... o......", +".......o ....o .......", +"........O X.....O X.......", +".........O X.......O X........", +"..........o OX.........o OX.........", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................", +"........................................" +}; +/* XPM */ +static char *cha_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 14 1", +/* colors */ +" c #F85848", +". c #949E9E", +"X c #F8B090", +"o c #E00028", +"O c #D4D4D4", +"+ c None", +"@ c #B0B0B0", +"# c #F82C24", +"$ c #F89E6C", +"% c #FF0000", +"& c #909090", +"* c #FFFFFF", +"= c #CEAA90", +"- c #DADAB6", +/* pixels */ +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"+++++++++++++++=#%#=+=#%% ++++++++++++++", +"++++++++++++++ %O%%%#%$$%o%=++++++++++++", +"+++++++++++++# +#%%o%%o%%%%% +++++++++++", +"+++++++++++ %%%%%%%%%%%%%%%%o#=+++++++++", +"+++++++++ o%%%%%%%%%%%%%%%%%%%%# +++++++", +"++++++ #%%%%%%o%%%o%%o%%o%o%%%%%o%o +++", +"++=#%%o%%%#= =*+**O*+**O*+- = =%%%%#@+++", +"++++ %=++*+*+**O****O****O*O*O*OO%=+++++", +"+++++.%=OO+*O*OO****+****+*O*+O&%=@+++++", +"++++++=%=*OO+**O**O*O**O*O*OO+$%=+++++++", +"+++++++#% +*OOOO****+****@O+*#%=++++++++", +"++++++++#%#*+**+O+OO+O+OOO*O#o#+++++++++", +"+++++++++o% O**+****O****O*#%%=+++++++++", +"+++++++++ %%#O*O****+****+ %o#++++++++++", +"++++++++++o%% XO*O**O*O**#%%%+++++++++++", +"++++++++++ %%%o%$-**+**$%%%%=+++++++++++", +"+++++++++++o%%$X$%%%%%%#= o#++++++++++++", +"++++++++++@ %%%o#O$$+$$$%%%=++++++++++++", +"++++++++++++#o%%%%%%%%o%%%=@++++++++++++", +"+++++++++++++ %%%%%%%%%%o=++++++++++++++", +"+++++++++++++++= & & @++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++" +}; +/* XPM */ +static char *chaotic_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 9 1", +/* colors */ +" c #000000", +". c #5C7A7A", +"X c None", +"o c #B0B0B0", +"O c #909090", +"+ c #788C8C", +"@ c #606060", +"# c #FFFFFF", +"$ c #303030", +/* pixels */ +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXX@$ @XXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXX$$+#X$ $XXXXXXXXXX", +"XXXXXXXXXXXXXXXXXX@$#o @XXXXXXXXX", +"XXXXXXXXXXXXXXXXXX$XX OXXXXXXXX", +"XXXXXXXXXXXXXXXXX@ # $@$ $XXXXXXXX", +"XXXXXXXXXXXXXXXXX@.+ $XXXO @XXXXXXX", +"XXXXXXXXXXXXXXXXX O@ XXXXX@ @XXXXXXX", +"XXXXXXXXXXXXXXXXX @O $XXXXX@$ @XXXXXXX", +"XXXXXXXXXXXXXXXXX O+ @XXXXO++ @XXXXXXX", +"XXXXXXXXXXXXXXXXX @+ $@OXO$#$ XXXXXXXX", +"XXXXXXXXXXXXXXXXX O@ $ @$Xo $XXXXXXXX", +"XXXXXXXXXXXXXXXXX +O $X##+ $XXXXXXXXX", +"XXXXXXXXXXXXXXXXX +@ $XXXXXXXXXX", +"XXXXXXXXXXXXXXXXX oO $XXXXXXXXXXX", +"XXXXXXXXO@@@@@ +# $XXXXXXXXXXXX", +"XXXXXXO +o########$ $@XXXXXXXXX", +"XXXXXX +#+.$XXXXXXXX", +"XXXXXX @O @XXXXXXX", +"XXXXXX$ $@ $@@$ @XXXXXXX", +"XXXXXXX@@@@XXXXXX + @XXXX@$ OXXXXXXX", +"XXXXXXXXXXXXXXXX@ # @XXXXXXX@@OXXXXXXXX", +"XXXXXXXXXXXXXXXX@.+ @XXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX$O@ XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX @O XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX #$ @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@ # @XXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@ # OXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX@.X XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX ++ XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX @+ XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX O@ @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX +O @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX @XXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXX$ OXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXX@@OXXXXXXXXXXXXXXXXXXXXX", +"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +}; +/* XPM */ +static char *cns_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 19 1", +/* colors */ +" c #000000", +". c #F85848", +"X c #949E9E", +"o c #F8B090", +"O c #E00028", +"+ c #7C3400", +"@ c None", +"# c #B0B0B0", +"$ c #F82C24", +"% c #F89E6C", +"& c #FF0000", +"* c #B64700", +"= c #909090", +"- c #788C8C", +"; c #606060", +": c #C80050", +"> c #CEAA90", +", c #303030", +"< c #FFB691", +/* pixels */ +"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@.oo.o$ ;@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@>.o.%%O,@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@$oo.o. ,@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@.oo$oo+ =@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@..o&oo$ ,@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@#.o.oo. =@.$%@@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o..oo& O.%ooo@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o.&%o.$oo%O++;@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o.+$%$o.@@@@@@@@@@@", +"@@@@@@@@@@@@@.oo++o%$$ ,@@$.oo@@@@@@@@@@", +"@@@@@@@@@@@@>.oo+Oo$o%.@@$oo..-@@@@@@@@@", +"@@@@@@@@@@@@..o%;.o&%.$..o%O ++>@@@@@@@@", +"@@@@@@@@@@@@>.$O:%o.O::::O* $oooo@@@@@@@", +"@@@@@@@@@@@@::::::$$:OO&OO::oo%.;=@@@@@@", +"@@@@@@@@@@@.::::::::O&&&&&O::++ ,@@@@@@", +"@@@@@@@@@@>:::O&&OO&&&&&&&&:: ;@@@@@", +"@@@@@@@@@@=::O&&&&&O:O&&&&&O: ,=@@@@@@@", +"@@@@@@@@@@:::&&&&&&&&:&&&&&O: ;@@@@@@@@", +"@@@@@@@@@@::O&&&&&&&&:&O&&&O:, ;@@@@@@@@", +"@@@@@@@@@@::O&&&&O&O&OO&O&&O:+ ;@@@@@@@@", +"@@@@@@@@@@::&&&O&&&&&O:&&&&O:, @@@@@@@@", +"@@@@@@@@@@::O&&&&&O&&&:O&O&::+ @@@@@@@@", +"@@@@@@@@@@::O&&O&&&&O&OO&&&:: @@@@@@@@", +"@@@@@@@@@@=::O&&&&O&&&O:&&&:: @@@@@@@@", +"@@@@@@@@@@.:::O&&O&&&&&:&OO:: @@@@@@@@", +"@@@@@@@@@@@:::::&&&&O&O:&&O:, @@@@@@@@", +"@@@@@@@@@@.>:::::O&&&&&:&&::+ ;@@@@@@@@", +"@@@@@@@@@@>.<::::O&&O&O:&&:: @@@@@@@@@", +"@@@@@@@@@@@.o%,:::O&&&O:&O:, @@@@@@@@@", +"@@@@@@@@@@@$o. :::OO&OO&::, ;@@@@@@@@@", +"@@@@@@@@@@@&o%+ ,::O&OO&O:: =@@@@@@@@@", +"@@@@@@@@@@@.oo+ :::OO::: ,@@@@@@@@@@", +"@@@@@@@@@@@..oO +::::: =@@@@@@@@@@", +"@@@@@@@@@@@@.<.+ ,+, ,@@@@@@@@@@@", +"@@@@@@@@@@@@Oo<+ @X, ,@@@@@@@@@@@@", +"@@@@@@@@@@@@.%o$ @@@@@;, ;@@@@@@@@@@@@@", +"@@@@@@@@@@@@@.o., =@@@@@@@@@@@@@@@@@@@@@", +"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" +}; +/* XPM */ +static char *confused_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO.=.+OO=.+O.OO+O+OO.+OOOOOO", +"OOOOOOOOOOO++=====O=====+=O+==++=O+OOOOO", +"OOOOOOOOOOO+=.=====.=++++===OO==+O=+OOOO", +"OOOOOOOOOOO=+===.+=o==o===+&OoO======OOO", +"OOOOOOOO+O+====OO+=o&&&&Oo==o&oO+==+=.O.", +"OOOO+.+=+O==+&&o=oooOo&o&ooo=&oooO==O=+=", +"OOOOOOOO++O===oo=oo&=&o&&oo=o==&o+==++==", +"OOOOOOOO=o.=O====o&OO&o&oo&o&&oo=======O", +"OOOOOOOo===+=O=O=ooO=ooooOOo=o&O=====OOO", +"OOOOOOOOO+==+=======O=oo====O=o=O===+OOO", +"OOOOOOOOO.=#=X=+====O========O======OOOO", +"OOOOOOO.#Xo++.=#%====O==========OO==+OOO", +"OOOOOO+Xo#+#+.#=.==X====+====O=+=+==+OOO", +"OOOOO.+.+O===##.#=X.====oX##===o+OO.OOOO", +"OOOOO#+####O#O##o.#+==#X#O#+...=OOo=+OOO", +"OOOO++#o+#+X++++#.#O.#+#X.#+X+==+OO=oOOO", +"OOOO#+.+..X+.##X++#++#..+XX#+##+..OOOOOO", +"OOOO##....O+#++#+.++#+X+#+#X..+#+#OOOOOO", +"OOOO++#+.+.#+#O+X#X#XX#.++##.#++.X$OOOOO", +"OOOOO#+#+.+++#++.+++##+X###+X+X##+**OOOO", +"OOOOO#..#OO#+.##o###.+..++.+#X+#+#* @OOO", +"OOOOO+#.#O+#+#O.+++.###+##++###+.#* $OOO", +"OOOOOOXX+#+#+#o..X##++#+..##.#+### *OOO", +"OOOOOOOX#.#X+#+#+#+.#+..+####%XX%% OOO", +"OOOOOOOO.%%X.#+#+#.++#+#+#+.X++=.% *OOO", +"OOOOOOOOO.* *##+#+.O####.+XX%%%%#% $OOO", +"OOOOOOOOOOO. %X.+.#+++XXX=.+++#X $OOO", +"OOOOOOOOOOOO.* %%X..#X%=.####%X* $OOO", +"OOOOOOOOOOOOOO.$ *XX%%%=.#X%###=* OOOO", +"OOOOOOOOOOOOOOOOOO+%%%=%%#.+.#=* @OOOO", +"OOOOOOOOOOOOOOOOOOo=%%%==X##X%* OOOOO", +"OOOOOOOOOOOOOOOOOOO+X%%%%X=%* @OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X *@OOOOOO", +"OOOOOOOOOOOOOOOOOOOO=%%%X* *$$OOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+X%%= .OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOX%%% OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO=%%* $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO=%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO+%%% $OOOOOOOOOOOOO" +}; +/* XPM */ +static char *dex_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 19 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #F8B090", +"o c #5C7A7A", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #B64700", +"* c #909090", +"= c #606060", +"- c #CEAA90", +"; c #DADAB6", +": c #303030", +"> c #F86800", +", c #FFB691", +"< c #F88C48", +/* pixels */ +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"###############-%-######################", +"##############-%X<-#####################", +"#########-<<-#-%XX+==###################", +"#########%,X< :<,X%@ :##################", +"#########-XX%: @;X%+ *#################", +"##########<,X& :<<%+: :#################", +"######->+#-%%%: <,XX@ #################", +"######%X%@ <,,& @XXX+ :++-#############", +"######-: +XX+ #########", +"#####+%%@@,X%<,XXXXXXX<:@XXX<: =########", +"####$%XX< <,<,XX& ########", +"#####$%<%X@%XXXX<%XX< =#######", +"#######<;X%XXXXX<<,XXXXXX%,XXXXXXXXX%@ #######", +"##########- c #788C8C", +", c #606060", +"< c #406868", +"1 c #C80050", +"2 c #FFFFFF", +"3 c #FFFF00", +"4 c #00B6FF", +"5 c #CEAA90", +"6 c #DADAB6", +"7 c #F86800", +"8 c #FFB691", +"9 c #6C91B6", +"0 c #F88C48", +"q c #0000FF", +/* pixels */ +"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$353333335*$$$$$$$$$$$$$$$", +"$$$$$$$$$$$$*33333333#7@3335$$$$$$$$$$$$", +"$$$$$$$$$65333333333@7777#333*$$$$$$$$$$", +"$$$$$$$$$3333333333377777733333===%$$$$$", +"$$$$$$$533333333333#7777777333%=====$$$$", +"$$$$$$ #3333333333o>7777773330======%$$$", +"$$$$5---O#33333o3944077777333*=======$$$", +"$$$$-----O333333>4444.77333330======%$$$", +"$$$ ---O--;3333344444443333333:====5$$$$", +"$$$ O-----733333444444433333333 ==035$$$", +"$$$3--O--O333333>44444>33333333333333$$$", +"$$533---O33333333944493333#333333333356$", +"$$33867733333o33333:o333333o3333333333$$", +"$532+2233333#333333333333oooo3#3333333%$", +"6522222+33333333333333333oooooo33o3333*$", +"$+22+22263333333o3333333ooooooo333333356", +"662222+2533333333333333#ooooooo33333333$", +"$32+22223333o3#33333o333ooooooo3#333333%", +"$33222233333333333#333333ooooo333333333$", +"$33368333333333333330626*oooo#333333o33%", +"%333335== 33oo333333222223#333333333333$", +"$3333=====:ooooo333+22+2263333333.>o333%", +"$5333=====oooooo33322222223333339444935$", +"$*33 ====>ooooooo3362+222633333.44444>3$", +"$%330====:ooooooo333222+23333334444444$$", +"$$333177 =oooXoo#333*626333333;4444444$$", +"$$53##777&3oooo3333333333333#--,444449$$", +"$$$3;77777#3o333333333333333O---94449$$$", +"$$%*@77777#33333333333333337O----O:o3$$$", +"$$$5777777333 333333333333;---O-O73$$$$", +"$$$$#7777730====#:.,33333333------3$$$$$", +"$$$$$577333=====qqqq<0333333#O---35$$$$$", +"$$$$$%53335====qqqqqq.33o333337735$$$$$$", +"$$$$$$$533 ====qqqqqqq3333333333%$$$$$$$", +"$$$$$$$$%33====qqqqqqq333333333%$$$$$$$$", +"$$$$$$$$$$50===qqqqqq,3333333:$$$$$$$$$$", +"$$$$$$$$$%6%5503,qqq<333#335%$$$$$$$$$$$", +"$$$$$$$$$$$$$%$*53,03335o$%%$$$$$$$$$$$$", +"$$$$$$$$$$$$$$$$$$$$$%$$+$$$$$$$$$$$$$$$" +}; +/* XPM */ +static char *hungry_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 15 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #CEAA90", +"= c #DADAB6", +"- c #303030", +"; c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO========OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO=============OOOOOOOOOOOOO", +"OO;XX;@OOOOO================OOOOOOOO;XOO", +"OO;%-;$OOOO==================OOOOOOO;XOO", +"OO;-%;$OOO========@$#@========OOOOO+;;$O", +"OO;%-;$OO=======- -*======*OOOO.;;$O", +"OO;-%;$O======* @====.$$&=====@OOO.;;$O", +"OO;X%;$O====== -========*@=====*.OO+;;$O", +"OO;;;X$o====* -==========@======$OO;;;$O", +"OO+;;-+o====- =============o====#@O+;;$O", +"OOO;%$O===== @=============&====*$O;;;$O", +"OOO+%OO====@ ==============&=====-OO;;$O", +"OOo;-Oo====$ ==============o&==== OO;;$O", +"OOO+%OO====@ ==============&===== O+;;#O", +"OOO;-Oo====$-==============&&==== O+;;-O", +"OOO;;+O=====$*============&&====* OO;;%+", +"OOO;;$o=====$.============&&====X-OO;;$O", +"OOO;;$O======*.===&======&&=====-$=O;;$O", +"OOO;;$Oo=====.==========&&=====* @O+;;$O", +"OOO;;$OO=======oo=====&&&======$-OOO;;$O", +"OOO;;$OOo=======&o&&&&&&======$ @OOO;;$O", +"OOO;;$OOOO========&=&========* $OOOO;;$O", +"OO+;;$OOOOo=================* -OOOOO#;$O", +"OOO;;$OOOOO=*==============@ -=OOOOO;;$O", +"OOO;;$OOOOOOO+*==========*- $OOOOOOO;;$O", +"OOOX-$OOOOOOOO@X@*====*#- -.OOOOOOOOX-$O", +"OOOOOOOOOOOOOO=*@$- -$.=OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOO=O==O=O=OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *hvy_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOO&&&&&&&&&&&&&&&.OOOOOOOOOOOOO", +"OOOOOOOOOOOo==============X*OOOOOOOOOOOO", +"OOOOOOOOOOoO===X====X=====X**OOOOOOOOOOO", +"OOOOOOOOOO&============X===% $OOOOOOOOOO", +"OOOOOOOOOoo===*%====***%===%* OOOOOOOOOO", +"OOOOOOOOOoO==% %===* %==X* @OOOOOOOOO", +"OOOOOOOOO&===% *==% X==**===% $OOOOOOOOO", +"OOOOOOOOoo===% %==% ===% ===X OOOOOOOOO", +"OOOOOOOOoO==== *== *==== *==X* @OOOOOOOO", +"OOOOOOOO&===== %== %==== %===% $OOOOOOOO", +"OOOOOOOoo===== *== *==== *===%* OOOOOOOO", +"OOOOOOOoO===== %==% ===* ====X* @OOOOOOO", +"OOOOOOO&===X== *==% X==**=====% $OOOOOOO", +"OOOOOOoo===== *==* %=====X OOOOOOO", +"OOOOOOoO=====*%%X===*%*X======%* @OOOOOO", +"OOOOOOo====================X===* $OOOOOO", +"OOOOOOO=%X%XXXX%XXXXXXXXX%X=%X% OOOOOO", +"OOOOOOO.=********************** OOOOOO", +"OOOOOOOOO OOOOOO", +"OOOOOOOOO. @OOOOOO", +"OOOOOOOOOOOoOOoOoOoOoOoOoOOoOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *int_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 12 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #303030", +"* c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOO+#.X.##@#OOOOOOOOOOOOOOOOOO", +"OOOOOOOO+##@X#O++.#+#.##OOOOOOOOOOOOOOOO", +"OOOOOO+#Xo++#X#%#+##o#O#.#+OOOOOOOOOOOOO", +"OOOOO.Xo#+#++##+.XX#..+.+..XOOOOOOOOOOOO", +"OOOO++.+O.+O##+#.X###..OX#.+X+OOOOOOOOOO", +"OOOO#+####O#O##o##+###X#+#+.#..OOOOOOOOO", +"OOO.+#o+#+X++++#.#O+#+#X.#+X++X+OOOOOOOO", +"OOO.+.+..X+.##X++#++#..+XX#+#X+..OOOOOOO", +"OOO##....O+#++#+.++#+X+#+#X..+#+#OOOOOOO", +"OOO++#+.+.#+#O+X#X#XX#.++##.#++.X$OOOOOO", +"OOOO#+#+.+++#++.+++##+X###+X+X##+&&OOOOO", +"OOOO#..#OO#+.##o###.+..++.+#X+#+#& @OOOO", +"OOOO.#.#O+#+#O.+++.###+##++###+.# $OOOO", +"OOOOOXX+#+#+#o..X##++#+..##.#+### &OOOO", +"OOOOOOX#.#X+#+#+#+.#+..+####XX%X% OOOO", +"OOOOOOO.%%X.#+#+#.++#+#+#+.%++*+% &OOOO", +"OOOOOOOO@& &##+#+.O####.+XXX%%%#% $OOOO", +"OOOOOOOOOO. %X.+.#+++XXX*.+++#% $OOOO", +"OOOOOOOOOOO@& %%X..#XXX.####%%& $OOOO", +"OOOOOOOOOOOOO@$ &XX%%%*.#X%###*& OOOOO", +"OOOOOOOOOOOOOOOOO+%%%*%%#.+.#*& @OOOOO", +"OOOOOOOOOOOOOOOOOO*%%%*.X##XX& OOOOOO", +"OOOOOOOOOOOOOOOOOOOX%%%%X*%& @OOOOOO", +"OOOOOOOOOOOOOOOOOOOX%%%%% &@OOOOOOO", +"OOOOOOOOOOOOOOOOOOO*%%%X& &$$OOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO+%%%* .OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO+*%%% OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%& $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+%%& $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOoOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *lawful_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 10 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #606060", +"$ c #FFFFFF", +"% c #303030", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOo$$$$$$oOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO$$o$$o$$$$$OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOo$$$$$$$o$$ooOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO$o$$$o$$$$$$$oOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$$+ .o$$$$$oOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$+%OOOO$o$$$oOOOOOOO", +"OOOOOOOOOOOOOOOOO$$o$X@OOOOo$$$ooOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$%OOOOOo$$$..OOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$@OOOOo$$oo##OOOOOOO", +"OOOOOOOOOOOOOOOO+$$o$$ooOoo$$$o OOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$$$$o$$$$o#%OOOOOOOO", +"OOOOOOOOOOOOOOOO+$$o$$o$$$$$o@%OOOOOOOOO", +"OOOOOOOOOOOOOOOOO$$$$$$$$o$o.%OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOo$$$o$$oo@#%OOOOOOOOOOO", +"OOOOOOOOoooooo$$$$$$$$$$$% %OOOOOOOOOOOO", +"OOOOOOO$$$$$$$$$$$$o$$o$$$$$$$oOOOOOOOOO", +"OOOOOO$$$$$$$$$o$$$$$$$$$$$$o$$oOOOOOOOO", +"OOOOOO$$o$ooooo##+o$$+##@oo$$$$$oOOOOOOO", +"OOOOOOo$$#% %#$$$+%##%%#ooo$O#OOOOOOO", +"OOOOOOOo@##OOOOO+$$$##OOOO#%%##%@OOOOOOO", +"OOOOOOOOOOOOOOOOo$$$##OOOOOOO##@OOOOOOOO", +"OOOOOOOOOOOOOOOOo$$o##OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$oo OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+$$$o OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$o$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$$##OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$o%@OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$o OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$oo OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$$o OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$$##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$$##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO$$o##OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo$# @OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO.#@OOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *mod_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOo&&&&&&&&&oXOOOOOOOOOOOOOOO", +"OOOOOOOOOOOO+&=========X%@OOOOOOOOOOOOOO", +"OOOOOOOOOOOOO&=====X====% @OOOOOOOOOOOOO", +"OOOOOOOOOOOOoO==X=======X* OOOOOOOOOOOOO", +"OOOOOOOOOOOO&====*%*%*===* $OOOOOOOOOOOO", +"OOOOOOOOOOO+&===X ===% *OOOOOOOOOOOO", +"OOOOOOOOOOOoO===**=======X* OOOOOOOOOOOO", +"OOOOOOOOOOO&===% %=======X% $OOOOOOOOOOO", +"OOOOOOOOOOO&===% %*%%=====% *OOOOOOOOOOO", +"OOOOOOOOOOoO===* ====X* OOOOOOOOOOO", +"OOOOOOOOOO&=========* X===X% $OOOOOOOOOO", +"OOOOOOOOO+&=========% *====% *OOOOOOOOOO", +"OOOOOOOOOoO===% %=== %====%* OOOOOOOOOO", +"OOOOOOOOO&====* *==X===% $OOOOOOOOO", +"OOOOOOOOO&======*%*%X=======% *OOOOOOOOO", +"OOOOOOOOOo==X===============% OOOOOOOOO", +"OOOOOOOOO=XXXXXXXXXX%X%X%X%%% $OOOOOOOO", +"OOOOOOOOOO=%**************** $OOOOOOOO", +"OOOOOOOOOOO$ $OOOOOOOO", +"OOOOOOOOOOOO* *OOOOOOOOO", +"OOOOOOOOOOOOOoOOoOoOoOoOoOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *neutral_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 14 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #00B6FF", +"= c #303030", +"- c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO.------.OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOO-+O&o.-----OOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+-&o--------.OOOOOOOOO", +"OOOOOOOOOOOOOOOOOO-oo----------+OOOOOOOO", +"OOOOOOOOOOOOOOOOO+-&--% #-------OOOOOOOO", +"OOOOOOOOOOOOOOOOO-OO-X=OOO.-----+OOOOOOO", +"OOOOOOOOOOOOOOOOO-oO-%#OOOO.-----OOOOOOO", +"OOOOOOOOOOOOOOOOO--O-=OOOOO+---X#OOOOOOO", +"OOOOOOOOOOOOOOOOO-oO-XOOOO+OO--=$OOOOOOO", +"OOOOOOOOOOOOOOOOO-OO--++OO-&--- OOOOOOOO", +"OOOOOOOOOOOOOOOOO-OO-----+oo--%=OOOOOOOO", +"OOOOOOOOOOOOOOOOO--O--+o&&o--%=OOOOOOOOO", +"OOOOOOOOOOOOOOOOO-oo*-------%=OOOOOOOOOO", +"OOOOOOOOOOOOOOOOO-oO------%%=OOOOOOOOOOO", +"OOOOOOOO+.+-+.---O&------= =OOOOOOOOOOOO", +"OOOOOO+-oo&&&&&&&&------------.OOOOOOOOO", +"OOOOOO---------------X-----O&Oo-OOOOOOOO", +"OOOOOO---------%=%---%%=%----OO-.OOOOOOO", +"OOOOOO---== =%---%=%%===----%XOOOOOOO", +"OOOOOOO-#$%OOOOOO-+-%$OOOO%===%=@OOOOOOO", +"OOOOOOOOOOOOOOOO.-&-=%OOOOOOO%%#OOOOOOOO", +"OOOOOOOOOOOOOOOo-O+-%$OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-oO- OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-OO- OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-%%OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-%$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO-&-=$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO--o-%$OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+-&- .OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-Oo- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-OO- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-oO- OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-OO%%OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO-o-%$OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO---%$OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO--% #OOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOX$@OOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *ovr_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOoO+=+OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOo=#===+OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo=.OO@X=OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo#OO* #X @OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO+=.XX+=#* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO+=O=.=OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOO#.=+OO@ $OOOOOOOOOOOOOO", +"OOOOOOOOOooooooooo&O.#+#XooooOOOOOOOOOOO", +"OOOOOOOOO&OOoOoOoOOOOOOOOOoO%@OOOOOOOOOO", +"OOOOOOOOoO==================X*@OOOOOOOOO", +"OOOOOOOO&===================X% @OOOOOOOO", +"OOOOOOOO&==%*%*%*%*%*%*%*%*==% *OOOOOOOO", +"OOOOOOOoO==%*%%*%%*%%*%*%*%==X* OOOOOOOO", +"OOOOOOO&======================* $OOOOOOO", +"OOOOOO+&=== ===% *OOOOOOO", +"OOOOOOoO======================X* OOOOOOO", +"OOOOOO&=======================X% $OOOOOO", +"OOOOOOo========================% *OOOOOO", +"OOOOOoO===*%X=====%%======%%===X* OOOOOO", +"OOOOO&==% %==% *==== %==* $OOOOO", +"OOOOO&== *==**== **% *=X% %%* ==% *OOOOO", +"OOOOoO==%%==* =* ===% == %=== %=X* OOOOO", +"OOOO&=======% =**===% %X %X==* =X% $OOOO", +"OOOO&======% %= %==== %% ====* ==% *OOOO", +"OOOoO=====% *== *==== %* ====% ==X* OOOO", +"OOO&====XX *===**===% X% X=== *===* $OOO", +"OO+&====X *====* ===% == *=== %===% *OOO", +"OOoO===% %*%*== *** %==% %** ====X* OOO", +"OO&====% ==X *====* %====X% $OO", +"OO&================================% *OO", +"OOo===X============================% OO", +"OO=XXXXXXXXXXXX%XXXX%X%X%XXXXX%X%X%% $O", +"OOO=%****************************** $O", +"OOOO$ $O", +"OOOOO* *OO", +"OOOOOOOOOOoOOoOOoOOoOOoOOoOOoOOoOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *satiated_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 23 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #F8B090", +"o c #5C7A7A", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #914700", +"* c #B64700", +"= c #909090", +"- c #788C8C", +"; c #606060", +": c #406868", +"> c #FFFFFF", +", c #CEAA90", +"< c #DADAB6", +"1 c #303030", +"2 c #FFB691", +"3 c #6C91B6", +"4 c #F88C48", +/* pixels */ +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"########################################", +"################<<<<<<<<################", +"##############<<<<<<<<<<$#$$$###########", +"############<<<<<<<<<<$:31:3:###########", +"###########<<<<<<%42<<#:3:133-##########", +"##########<<<<<%<<;;=o$131:33;##########", +"#########<<<<<<,1 ::31:33;,#########", +"########<<<<<<, =<<<<.13:133;<=########", +"########<<<%2, 1<<<<<<#333:33;<,=#######", +"#######<<<3-=<33;<<@o######", +"#######O<<<<<,#<<<<<<<<.3:<3-;<, =######", +"########<<<<2<<<<<<<<<>#31<33o<11#######", +"########O<<<<44<>O>>>>>#3:<3.;- =#######", +"##########<<<4<<<<><><<$3:<331 ;<#######", +"##########<<<<<<<%2<<<<$3:<33 1#########", +"###########O,<<<<<<<<<<#31<331##########", +"#############.<<<<<<<<<$3:133;##########", +"##############=;=,<<<<,o 1;;=##########", +"###############<=;1 1;=##############", +"#################<# c #B64700", +", c #909090", +"< c #788C8C", +"1 c #606060", +"2 c #406868", +"3 c #FFFFFF", +"4 c #CEAA90", +"5 c #DADAB6", +"6 c #303030", +"7 c #F86800", +"8 c #FFB691", +"9 c #6C91B6", +"0 c #F88C48", +"q c #0000FF", +/* pixels */ +"****************************************", +"*************#333333333#****************", +"***********##33333#333333#**************", +"**********#33333#33333#33*==************", +"*********#33##33-;-3#3333399************", +"********#33#33#3-@ 33333#33=.***********", +"********#3*#33-;;;;;-33333#99***********", +"*******#3*3333-;;;;@ 33#333#9=**********", +"*******#333#33#3-;-33#*##33399**********", +"******#3#3333333-@-#333#9933*9=*********", +"******#333#33#3333333#333*9999=*********", +"******#333333333#3#33333333*999*********", +"******#3#33#33333333#33#3333#9=*********", +"******#333334>&&:&&>::44,3#33#9*********", +"******#33*::&41OOO6:4O 0::4433=*********", +"******#3:>,0:O0O1O+O:OXO,O+2+OOo4<+1104:>:#*********", +"******.&:1OOO,14X2O48:O80,440:,*********", +"******4::>OOO%8-X4O4%O,84+O0X&>=********", +"******.::>,O 99*X+<$,+.o*1O4&0:*********", +"******>:0&4O5qq9#10OO3qq9,+X:1:*********", +"****=>,,::,O4qq9X+O>O-qq9O2X0,>*********", +"******4:>OOOO48882OOOO+4OOO07*4*********", +"******4*,4OO+OXX3O5************", +"*********=0%,OO,>:>>O +1OO4*************", +"**********=%+OO:::1:::6+:7**************", +"***********7&OO:O+O,O1OO+1**************", +"***********40OO,O4:OOO11O<5*************", +"**********=4 +O1O2+O2+O0O***************", +"************72O+1+21-OOO%5**************", +"************0%1OOOO+O+174***************", +"*************%%O,OO1407-=***************", +"**************-$>%0%:74*****************", +"****************54044*=*****************", +"*****************=*=********************" +}; +/* XPM */ +static char *sick_il_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 23 1", +/* colors */ +" c #F85848", +". c #949E9E", +"X c #F8B090", +"o c #E00028", +"O c #D4D4D4", +"+ c #F87A24", +"@ c #7C3400", +"# c None", +"$ c #B0B0B0", +"% c #F89E6C", +"& c #FF0000", +"* c #914700", +"= c #B64700", +"- c #909090", +"; c #606060", +": c #FFFFFF", +"> c #CEAA90", +", c #DADAB6", +"< c #F86800", +"1 c #FFB691", +"2 c #6C91B6", +"3 c #F88C48", +"4 c #0000FF", +/* pixels */ +"########################################", +"#############O:::::::::O################", +"###########OO:::::O::::::O##############", +"##########O:::::O:::::O::#$$############", +"#########O::OO::%&%:O:::::22############", +"########O::O::O:%o :::::O::$.###########", +"########O:#O::%&&&&&%:::::O22###########", +"#######O:#::::%&&&&o ::O:::O2$##########", +"#######O:::O::O:%&%::O#OO:::22##########", +"######O:O:::::::%o%O:::O22::#2$#########", +"######O:::O::O:::::::O:::#2222$#########", +"######O:::::::::O:O::::::::#222#########", +"######O:O::O::::::::O::O::::O2$#########", +"######O:::::>=@@=**=**>>-:O::O2#########", +"######O::#**@3>%* ;=>=3;<@>>::$#########", +"######O:** >=>XXXX1X >>+>%*%*;O#########", +"######O3@*,X%XXXXXXX>X%XX >*=*O#########", +"######.@@3XXXXXXXXXXXXXXX>X>3*-#########", +"######>***>X% >XXXXX3XXXXXX%>*=>########", +"######.***> 22#XXX<%X22#XXX@+;#########", +"######=*3@X>O442OXX==%XX11111O1+%X111XX%<#>#########", +"######.,;XXXXXX1O1X%3XXXXX%+3###########", +"########3=XXXXXX:XXXXXXXXX+<>$##########", +"########>+XXXXXX%-3->XXXX%+<############", +"#########%3XXXXXX>- -%XXX%<%$###########", +"#########$############", +"##########+%XXXXXXXXXXXX%+<#############", +"##########%3XXX>=****3XX%<%#############", +"##########>+XXX**=3-*@3>3+##############", +"###########<%XX >XX%X;%X3+##############", +"###########%3XX>XX++XXXX<%$#############", +"##########$>+XXXXXXXXXXX<###############", +"############<%XXXXXXXXX3+###############", +"###########$%+XXXXXXXX%<>###############", +"#############++XXXXXX%<%$###############", +"#############$%<<3333<%#################", +"#################%3>>$##################", +"#################$#$####################" +}; +/* XPM */ +static char *slt_enc_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #FFFFFF", +"* c #303030", +"= c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOoO+OOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOoOXX==OOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoO=OO+==OOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOoXOO.*$=$OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO=+# *.X *OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOO==.OO=+@ $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOXO==.OO $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOO+=@$@* @OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOO&&&&&&&X @OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOo======X*OOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOoO======X**OOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOO&====X===% $OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOoo==%* %==%* OOOOOOOOOOOOOO", +"OOOOOOOOOOOOOoO=% % =X* @OOOOOOOOOOOOO", +"OOOOOOOOOOOOO&==**==% %=% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOoo==%%==* %=X OOOOOOOOOOOOO", +"OOOOOOOOOOOOoO=====* X==X* @OOOOOOOOOOOO", +"OOOOOOOOOOOO&=====* %====% $OOOOOOOOOOOO", +"OOOOOOOOOOOoo==== X=====%* OOOOOOOOOOOO", +"OOOOOOOOOOOo+===* *%*%%==X* @OOOOOOOOOOO", +"OOOOOOOOOOO&==== %===% $OOOOOOOOOOO", +"OOOOOOOOOOO&==============% OOOOOOOOOOO", +"OOOOOOOOOOO==============X% @OOOOOOOOOO", +"OOOOOOOOOOO+%%%%%%%%%%%%%% $OOOOOOOOOO", +"OOOOOOOOOOOOO% $OOOOOOOOOO", +"OOOOOOOOOOOOO@ @OOOOOOOOOO", +"OOOOOOOOOOOOOO@$$$$$$$$$$$$$@OOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" +}; +/* XPM */ +static char *str_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 17 1", +/* colors */ +" c #000000", +". c #F8B090", +"X c #5C7A7A", +"o c #F87A24", +"O c #7C3400", +"+ c None", +"@ c #B0B0B0", +"# c #F89E6C", +"$ c #B64700", +"% c #909090", +"& c #606060", +"* c #CEAA90", +"= c #DADAB6", +"- c #303030", +"; c #F86800", +": c #FFB691", +"> c #F88C48", +/* pixels */ +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++++++++++++++", +"+++++++++++++++++++*>*>#++++++++++++++++", +"++++++++++++++++*#o>..*#o*++++++++++++++", +"+++++++++++++++o#.#>.....o++++++++++++++", +"+++++++++++++++;>;#.o.>..#$X++++++++++++", +"+++++++++++++++o#>.o.>:...o %++++++++++", +"++++++++++++++o##>>#o##>..#O -++++++++++", +"++++++++++++++>#.oo#>..>...O ++++++++++", +"++++++++++++++*o##.>>;o#...o ++++++++++", +"+++++++++++++++*;o#........>- &+++++++++", +"+++++++++++++++++#>>;o......O -+++++++++", +"+++++++++++++++++@+@+o>.....$ +++++++++", +"+++++++++++++++++++++*;.#...>- %++++++++", +"++++++++++++++++++++++;>o....$ &++++++++", +"++++++++++++++++++++++#>>....>- %+++++++", +"+++++++++++++++++++++++;#>....; -+++++++", +"+++++++++++++++++++++++o#>....>O %++++++", +"+++++++++++++++++++++++*>o.....; -++++++", +"+++++++++++++#>**+++++++;#.....>O %+++++", +"+o#+++++++*o;>>>>o#+++++o##.....; -+++++", +"+:#o*++++oo#..*..*>;*+++#>#.....>O %++++", +"+:=#o#+*;>.:==:....#;*++@o.......; &++++", +"+::..>;o#.=::::......o*++;.......>O ++++", +"+.....#o.:.=:.........o#+;........$ ++++", +"+......#o..:...........#o;>.......o &+++", +"+........#..............*>o......:o- +++", +"+..................#o>#...#o.......O +++", +"+...............>o>#.......#>......O &++", +"+..................................o -++", +"+..................................> ++", +"+..................................> ++", +"+.................................#$ &+", +"+................................>$ &+", +"+..#>$o>#..............#>;>>>oOOO- ++", +"+...#O OOOOO$>>>>>>>$OO %++", +"+...o -&&++++", +"+..#O -&&%++++++++++", +"++++++++++++++++++++++++++++++++++++++++" +}; +/* XPM */ +static char *stunned_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 12 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c #D4D4D4", +"O c None", +"+ c #B0B0B0", +"@ c #909090", +"# c #788C8C", +"$ c #606060", +"% c #406868", +"& c #303030", +"* c #6C91B6", +/* pixels */ +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", +"OOOOOOOOOOOO&$OOOO@OOO@@OO@OOOOOOOOOOOOO", +"OOOOOOOOOOO@& $OO@&&$$@ O@$$OOOOOOOOOOOO", +"OOOOOOOOOOOO$$ @@@$ &&OOO@$OOOOOOOOOOOO", +"OOOOOOOOOOOOO@@&$$$$&O$OO$O &@O@OOOOOOOO", +"OOOOOO@@@@@@OO@$$O$&$@@OO& &&$O&OOOOOOO", +"OOOOOO&&&& & $ &&@$ &O@$& &&&$ & $OOOOOO", +"OOOOOO$&OO &&&$ $$ $& $$&$&&&OOOOOOO", +"OOOOOO@@O@$ &+ # &O$$ $$&O@OOOOO", +"OOOOOOOO@X%$ %& %% & && $$@@@@OOOO", +"OOOOOOO+$$@+ &%%%&%& & &@OOO&&OOO", +"OOOOOO.Xo%+ &&%%%%%&& & OO@$&&OOO", +"OOOOO++ $$&&$ && %&%%& &O@&$&OOOO", +"OOOOO####$ X&&& && &%& & &&OOOO", +"OOOO++#.+## $&# %& & & &$ OOOO", +"OOOO#+++.@&%&& &#&%& & $ @OOOOOO", +"OOOO##....#+$#@%#& $%$&@&$$% & X##$@OOOO", +"OOOO.+#+.+@#+#+$&$X#%&%.+& %&#++.$&OOOOO", +"OOOOO#+#+.+++#$$%&++&X+X#&#+&+&##+ &OOOO", +"OOOOO#..#OO#+@%#o##X.@..++.+$&+#+#& @OOO", +"OOOOO+#.#O+#+#O@++@$$##+##++###+.#& $OOO", +"OOOOOOXX+#+#+#o.@%&$++#+..##.#+### &OOO", +"OOOOOOOX#.#X+#+#+##&#+..+####%XX%% OOO", +"OOOOOOOO+%%X.#+#+#.++#+#+#+.X++*.% &OOO", +"OOOOOOOOO@& &##+#+.O####.+XX%%%%#% $OOO", +"OOOOOOOOOOO. %X.+.#+++XXX*.+++#X $OOO", +"OOOOOOOOOOOO@& %%X..#X%#.####%X& $OOO", +"OOOOOOOOOOOOOO@$ &XX%%%*.#X%###*& OOOO", +"OOOOOOOOOOOOOOOOOO+%%%*%%#.+.#*& @OOOO", +"OOOOOOOOOOOOOOOOOOO*%%%**X##X%& OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X*X& @OOOOO", +"OOOOOOOOOOOOOOOOOOOOX%%%%X &@OOOOOO", +"OOOOOOOOOOOOOOOOOOOO*%%%X& &$$OOOOOOOO", +"OOOOOOOOOOOOOOOOOOOO+X%%* @OOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOOX%%& OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO*%%% $OOOOOOOOOOOOO", +"OOOOOOOOOOOOOOOOOOOOO.X%& $OOOOOOOOOOOOO" +}; +/* XPM */ +static char *wis_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c None", +"O c #B0B0B0", +"+ c #909090", +"@ c #788C8C", +"# c #606060", +"$ c #406868", +"% c #FFFFFF", +"& c #303030", +"* c #6C91B6", +"= c #0000FF", +/* pixels */ +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooo+#& &#oooooooooooooooooooo", +"oooooooooooo+& #oooooooooooooooooo", +"ooooooooooo+ &====&& &ooooooooooooooooo", +"oooooooooo+ &==& ===%& +ooooooooooooooo", +"ooooooooo+&%=== ===%%o&&oooooooooooooo", +"oooooooo.&%%===& ===%o& #+ooooooooooo", +"oooo&###&&%%*=======$#&ooo#& #+oooooooo", +"ooooo###o+&X$=====& #oo##oooo+######oooo", +"oooooooooooo######@oo##ooooooooooooooooo", +"oooooooooooooOoOoOo##ooooooooooooooooooo", +"ooooooooooooooooo+#+ooo+&#oooooooooooooo", +"ooooooooooooooooooooooo#oooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo" +}; +/* XPM */ +static char *nothing_xpm[] = { +/* width height ncolors chars_per_pixel */ +"40 40 13 1", +/* colors */ +" c #000000", +". c #949E9E", +"X c #5C7A7A", +"o c None", +"O c #B0B0B0", +"+ c #909090", +"@ c #788C8C", +"# c #606060", +"$ c #406868", +"% c #FFFFFF", +"& c #303030", +"* c #6C91B6", +"= c #0000FF", +/* pixels */ +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo", +"oooooooooooooooooooooooooooooooooooooooo" +}; + diff --git a/win/gnome/gnaskstr.c b/win/gnome/gnaskstr.c new file mode 100644 index 0000000..b982896 --- /dev/null +++ b/win/gnome/gnaskstr.c @@ -0,0 +1,61 @@ +/* SCCS Id: @(#)gnaskstr.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnaskstr.h" +#include "gnmain.h" +#include + + +static +void ghack_ask_string_callback(gchar * string, gpointer data) +{ + char **user_text = (char **) data; + + g_assert(user_text != NULL); + + *user_text = string; /* note - value must be g_free'd */ +} + + +int ghack_ask_string_dialog(const char *szMessageStr, + const char *szDefaultStr, const char *szTitleStr, + char *buffer) +{ + int i; + GtkWidget* dialog; + gchar *user_text = NULL; + + dialog = gnome_request_dialog(FALSE, szMessageStr, + szDefaultStr, 0, + ghack_ask_string_callback, + &user_text, NULL); + g_assert(dialog != NULL); + + gtk_window_set_title(GTK_WINDOW(dialog), szTitleStr); + + gnome_dialog_set_default( GNOME_DIALOG(dialog), 0); + gtk_window_set_modal( GTK_WINDOW(dialog), TRUE); + gnome_dialog_set_parent (GNOME_DIALOG (dialog), + GTK_WINDOW (ghack_get_main_window ()) ); + + i = gnome_dialog_run_and_close (GNOME_DIALOG (dialog)); + + /* Quit */ + if ( i != 0 || user_text == NULL ) { + if (user_text) + g_free(user_text); + return -1; + } + + if ( *user_text == 0 ) { + g_free(user_text); + return -1; + } + + g_assert(strlen(user_text) > 0); + strcpy (buffer, user_text); + g_free(user_text); + return 0; +} + diff --git a/win/gnome/gnaskstr.h b/win/gnome/gnaskstr.h new file mode 100644 index 0000000..9cde69b --- /dev/null +++ b/win/gnome/gnaskstr.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)gnaskstr.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackAskStringDialog_h +#define GnomeHackAskStringDialog_h + + +int ghack_ask_string_dialog(const char *szMessageStr, + const char *szDefaultStr, const char *szTitleStr, + char *buffer); + +#endif /* GnomeHackAskStringDialog_h */ + diff --git a/win/gnome/gnbind.c b/win/gnome/gnbind.c new file mode 100644 index 0000000..a6f0ed8 --- /dev/null +++ b/win/gnome/gnbind.c @@ -0,0 +1,1197 @@ +/* SCCS Id: @(#)gnbind.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file implements the interface between the window port specific + * code in the Gnome port and the rest of the nethack game engine. +*/ + +#include "gnbind.h" +#include "gnmain.h" +#include "gnmenu.h" +#include "gnaskstr.h" +#include "gnyesno.h" + +GNHWinData gnome_windowlist[MAXWINDOWS]; +winid WIN_WORN = WIN_ERR; + +extern void tty_raw_print(const char *); +extern void tty_raw_print_bold(const char *); + + +/* Interface definition, for windows.c */ +struct window_procs Gnome_procs = { + "Gnome", + WC_COLOR|WC_HILITE_PET|WC_INVERSE, + 0L, + gnome_init_nhwindows, + gnome_player_selection, + gnome_askname, + gnome_get_nh_event, + gnome_exit_nhwindows, + gnome_suspend_nhwindows, + gnome_resume_nhwindows, + gnome_create_nhwindow, + gnome_clear_nhwindow, + gnome_display_nhwindow, + gnome_destroy_nhwindow, + gnome_curs, + gnome_putstr, + gnome_display_file, + gnome_start_menu, + gnome_add_menu, + gnome_end_menu, + gnome_select_menu, + genl_message_menu, /* no need for X-specific handling */ + gnome_update_inventory, + gnome_mark_synch, + gnome_wait_synch, +#ifdef CLIPPING + gnome_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + gnome_print_glyph, + gnome_raw_print, + gnome_raw_print_bold, + gnome_nhgetch, + gnome_nh_poskey, + gnome_nhbell, + gnome_doprev_message, + gnome_yn_function, + gnome_getlin, + gnome_get_ext_cmd, + gnome_number_pad, + gnome_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + donull, + donull, +#endif + /* other defs that really should go away (they're tty specific) */ + gnome_start_screen, + gnome_end_screen, + gnome_outrip, + genl_preference_update, +}; + +/* +init_nhwindows(int* argcp, char** argv) + -- Initialize the windows used by NetHack. This can also + create the standard windows listed at the top, but does + not display them. + -- Any commandline arguments relevant to the windowport + should be interpreted, and *argcp and *argv should + be changed to remove those arguments. + -- When the message window is created, the variable + iflags.window_inited needs to be set to TRUE. Otherwise + all plines() will be done via raw_print(). + ** Why not have init_nhwindows() create all of the "standard" + ** windows? Or at least all but WIN_INFO? -dean +*/ +void gnome_init_nhwindows(int* argc, char** argv) +{ + /* Main window */ + ghack_init_main_window( *argc, argv); + ghack_init_signals( ); + +#ifdef HACKDIR + //if (ghack_init_glyphs(HACKDIR "/t32-1024.xpm")) + if (ghack_init_glyphs(HACKDIR "/x11tiles")) + g_error ("ERROR: Could not initialize glyphs.\n"); +#else +# error HACKDIR is not defined! +#endif + + // gnome/gtk is not reentrant + set_option_mod_status("ignintr", DISP_IN_GAME); + flags.ignintr = TRUE; + + iflags.window_inited = TRUE; + + /* gnome-specific window creation */ + WIN_WORN = gnome_create_nhwindow(NHW_WORN); +} + + +/* Do a window-port specific player type selection. If player_selection() + offers a Quit option, it is its responsibility to clean up and terminate + the process. You need to fill in pl_character[0]. +*/ +void +gnome_player_selection() +{ + int n, i, sel; + const char** choices; + int* pickmap; + + /* prevent an unnecessary prompt */ + rigid_role_checks(); + + if (!flags.randomall && flags.initrole < 0) { + + /* select a role */ + for (n = 0; roles[n].name.m; n++) continue; + choices = (const char **)alloc(sizeof(char *) * (n+1)); + pickmap = (int*)alloc(sizeof(int) * (n+1)); + for (;;) { + for (n = 0, i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, + flags.initgend, flags.initalign)) { + if (flags.initgend >= 0 && flags.female && roles[i].name.f) + choices[n] = roles[i].name.f; + else + choices[n] = roles[i].name.m; + pickmap[n++] = i; + } + } + if (n > 0) break; + else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ + else if (flags.initgend >= 0) flags.initgend = -1; + else if (flags.initrace >= 0) flags.initrace = -1; + else panic("no available ROLE+race+gender+alignment combinations"); + } + choices[n] = (const char *) 0; + if (n > 1) + sel = ghack_player_sel_dialog(choices, + _("Player selection"), _("Choose one of the following roles:")); + else sel = 0; + if (sel >= 0) sel = pickmap[sel]; + else if (sel == ROLE_NONE) { /* Quit */ + clearlocks(); + gnome_exit_nhwindows(0); + } + free(choices); + free(pickmap); + } else if (flags.initrole < 0) sel = ROLE_RANDOM; + else sel = flags.initrole; + + if (sel == ROLE_RANDOM) { /* Random role */ + sel = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (sel < 0) sel = randrole(); + } + + flags.initrole = sel; + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + if (flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) flags.initrace = randrace(flags.initrole); + } else { + /* Count the number of valid races */ + n = 0; /* number valid */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) + n++; + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) n++; + } + } + + choices = (const char **)alloc(sizeof(char *) * (n+1)); + pickmap = (int*)alloc(sizeof(int) * (n + 1)); + for (n = 0, i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + choices[n] = races[i].noun; + pickmap[n++] = i; + } + } + choices[n] = (const char *) 0; + /* Permit the user to pick, if there is more than one */ + if (n > 1) + sel = ghack_player_sel_dialog(choices, _("Race selection"), + _("Choose one of the following races:")); + else sel = 0; + if (sel >= 0) sel = pickmap[sel]; + else if (sel == ROLE_NONE) { /* Quit */ + clearlocks(); + gnome_exit_nhwindows(0); + } + flags.initrace = sel; + free(choices); + free(pickmap); + } + if (flags.initrace == ROLE_RANDOM) { /* Random role */ + sel = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (sel < 0) sel = randrace(flags.initrole); + flags.initrace = sel; + } + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || + !validgend(flags.initrole, flags.initrace, flags.initgend)) { + if (flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) + flags.initgend = randgend(flags.initrole, flags.initrace); + } else { + /* Count the number of valid genders */ + n = 0; /* number valid */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) + n++; + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) n++; + } + } + + choices = (const char **)alloc(sizeof(char *) * (n+1)); + pickmap = (int*)alloc(sizeof(int) * (n + 1)); + for (n = 0, i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + choices[n] = genders[i].adj; + pickmap[n++] = i; + } + } + choices[n] = (const char *) 0; + /* Permit the user to pick, if there is more than one */ + if (n > 1) + sel = ghack_player_sel_dialog(choices, _("Gender selection"), + _("Choose one of the following genders:")); + else sel = 0; + if (sel >= 0) sel = pickmap[sel]; + else if (sel == ROLE_NONE) { /* Quit */ + clearlocks(); + gnome_exit_nhwindows(0); + } + flags.initgend = sel; + free(choices); + free(pickmap); + } + if (flags.initgend == ROLE_RANDOM) { /* Random gender */ + sel = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (sel < 0) sel = randgend(flags.initrole, flags.initrace); + flags.initgend = sel; + } + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || + !validalign(flags.initrole, flags.initrace, flags.initalign)) { + if (flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) + flags.initalign = randalign(flags.initrole, flags.initrace); + } else { + /* Count the number of valid alignments */ + n = 0; /* number valid */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) + n++; + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) + if (validalign(flags.initrole, flags.initrace, i)) n++; + } + + choices = (const char **)alloc(sizeof(char *) * (n+1)); + pickmap = (int*)alloc(sizeof(int) * (n + 1)); + for (n = 0, i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, + flags.initrace, flags.initgend, i)) { + choices[n] = aligns[i].adj; + pickmap[n++] = i; + } + } + choices[n] = (const char *) 0; + /* Permit the user to pick, if there is more than one */ + if (n > 1) + sel = ghack_player_sel_dialog(choices, _("Alignment selection"), + _("Choose one of the following alignments:")); + else sel = 0; + if (sel >= 0) sel = pickmap[sel]; + else if (sel == ROLE_NONE) { /* Quit */ + clearlocks(); + gnome_exit_nhwindows(0); + } + flags.initalign = sel; + free(choices); + free(pickmap); + } + if (flags.initalign == ROLE_RANDOM) { + sel = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (sel < 0) sel = randalign(flags.initrole, flags.initrace); + flags.initalign = sel; + } + } +} + + +/* Ask the user for a player name. */ +void gnome_askname() +{ + int ret; + + g_message("Asking name...."); + + /* Ask for a name and stuff the response into plname, a nethack global */ + ret = ghack_ask_string_dialog("What is your name?", "gandalf", + "GnomeHack", plname); + + /* Quit if they want to quit... */ + if (ret==-1) + { + gnome_exit_nhwindows(0); + } +} + + +/* Does window event processing (e.g. exposure events). + A noop for the tty and X window-ports. +*/ +void gnome_get_nh_event() +{ + /* We handle our own events. */ + return; +} + +/* Exits the window system. This should dismiss all windows, + except the "window" used for raw_print(). str is printed if possible. +*/ +void gnome_exit_nhwindows(const char *str) +{ + gtk_exit (0); + terminate(EXIT_SUCCESS); +} + +/* Prepare the window to be suspended. */ +void gnome_suspend_nhwindows(const char *str) +{ + /* I don't think we need to do anything here... */ + return; +} + + +/* Restore the windows after being suspended. */ +void gnome_resume_nhwindows() +{ + /* Do Nothing. Un-necessary since the GUI will refresh itself. */ + return; +} + +/* Create a window of type "type" which can be + NHW_MESSAGE (top line) + NHW_STATUS (bottom lines) + NHW_MAP (main dungeon) + NHW_MENU (inventory or other "corner" windows) + NHW_TEXT (help/text, full screen paged window) +*/ +winid +gnome_create_nhwindow(int type) +{ + + winid i = 0; + +/* Return the next available winid + */ + + for (i=0; i= 0) && + (wid < MAXWINDOWS) && + (gnome_windowlist[wid].win != NULL)) + { + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_PUTSTR], + (guint) attr, + text); + } +} + +/* Display the file named str. Complain about missing files + iff complain is TRUE. +*/ +void gnome_display_file(const char *filename,BOOLEAN_P must_exist) +{ + /* Strange -- for some reason it makes us create a new text window + * instead of reusing any existing ones -- perhaps we can work out + * some way to reuse stuff -- but for now just make and destroy new + * ones each time */ + + dlb *f; + + f = dlb_fopen(filename, "r"); + if (!f) { + if (must_exist) { + GtkWidget *box; + char message[90]; + sprintf(message, "Warning! Could not find file: %s\n",filename); + + box = gnome_message_box_new (_(message), + GNOME_MESSAGE_BOX_ERROR, + GNOME_STOCK_BUTTON_OK, + NULL); + gnome_dialog_set_default( GNOME_DIALOG(box), 0); + gnome_dialog_set_parent (GNOME_DIALOG (box), + GTK_WINDOW (ghack_get_main_window ()) ); + gtk_window_set_modal( GTK_WINDOW(box), TRUE); + gtk_widget_show (box); + } + } + else { + GtkWidget *txtwin, *gless, *frametxt; +#define LLEN 128 + char line[LLEN], *textlines; + int num_lines, charcount; + + txtwin = gnome_dialog_new("Text Window", GNOME_STOCK_BUTTON_OK, + NULL); + gtk_widget_set_usize(GTK_WIDGET(txtwin), 500, 400); + gtk_window_set_policy(GTK_WINDOW(txtwin), TRUE, TRUE, FALSE); + gtk_window_set_title(GTK_WINDOW(txtwin), "Text Window"); + gnome_dialog_set_default( GNOME_DIALOG(txtwin), 0); + gtk_window_set_modal( GTK_WINDOW(txtwin), TRUE); + frametxt = gtk_frame_new (""); + gtk_widget_show (frametxt); + + /* + * Count the number of lines and characters in the file. + */ + num_lines = 0; + charcount = 1; + while (dlb_fgets(line, LLEN, f)) { + num_lines++; + charcount += strlen(line); + } + (void) dlb_fclose(f); + + /* Ignore empty files */ + if (num_lines == 0) return; + + /* + * Re-open the file and read the data into a buffer. + */ + textlines = (char *) alloc((unsigned int) charcount); + textlines[0] = '\0'; + f = dlb_fopen( filename, RDTMODE); + + while (dlb_fgets(line, LLEN, f)) { + (void) strcat(textlines, line); + } + (void) dlb_fclose(f); + + gless = gnome_less_new (); + gnome_less_show_string (GNOME_LESS (gless), textlines); + gtk_container_add (GTK_CONTAINER (frametxt), gless); + gtk_box_pack_start(GTK_BOX (GNOME_DIALOG (txtwin)->vbox), frametxt, + TRUE, TRUE, 0); + gtk_widget_show_all( txtwin); + gtk_window_set_modal( GTK_WINDOW(txtwin), TRUE); + gnome_dialog_set_parent (GNOME_DIALOG (txtwin), + GTK_WINDOW (ghack_get_main_window ()) ); + gnome_dialog_run_and_close (GNOME_DIALOG (txtwin)); + free(textlines); + } +} + +/* Start using window as a menu. You must call start_menu() + before add_menu(). After calling start_menu() you may not + putstr() to the window. Only windows of type NHW_MENU may + be used for menus. +*/ +void gnome_start_menu(winid wid) +{ + if (wid != -1) + { + if (gnome_windowlist[wid].win == NULL && gnome_windowlist[wid].type != 0) + { + gnome_create_nhwindow_by_id(gnome_windowlist[wid].type, wid); + } + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_START_MENU]); + } +} + +/* +add_menu(windid window, int glyph, const anything identifier, + char accelerator, char groupacc, + int attr, char *str, boolean preselected) + -- Add a text line str to the given menu window. If identifier + is 0, then the line cannot be selected (e.g. a title). + Otherwise, identifier is the value returned if the line is + selected. Accelerator is a keyboard key that can be used + to select the line. If the accelerator of a selectable + item is 0, the window system is free to select its own + accelerator. It is up to the window-port to make the + accelerator visible to the user (e.g. put "a - " in front + of str). The value attr is the same as in putstr(). + Glyph is an optional glyph to accompany the line. If + window port cannot or does not want to display it, this + is OK. If there is no glyph applicable, then this + value will be NO_GLYPH. + -- All accelerators should be in the range [A-Za-z]. + -- It is expected that callers do not mix accelerator + choices. Either all selectable items have an accelerator + or let the window system pick them. Don't do both. + -- Groupacc is a group accelerator. It may be any character + outside of the standard accelerator (see above) or a + number. If 0, the item is unaffected by any group + accelerator. If this accelerator conflicts with + the menu command (or their user defined alises), it loses. + The menu commands and aliases take care not to interfere + with the default object class symbols. + -- If you want this choice to be preselected when the + menu is displayed, set preselected to TRUE. +*/ +void gnome_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel) +{ + GHackMenuItem item; + item.glyph = glyph; + item.identifier = identifier; + item.accelerator = accelerator; + item.group_accel = group_accel; + item.attr = attr; + item.str = str; + item.presel = presel; + + if (wid != -1 && gnome_windowlist[wid].win != NULL) + { + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_ADD_MENU], + &item); + } +} + +/* +end_menu(window, prompt) + -- Stop adding entries to the menu and flushes the window + to the screen (brings to front?). Prompt is a prompt + to give the user. If prompt is NULL, no prompt will + be printed. + ** This probably shouldn't flush the window any more (if + ** it ever did). That should be select_menu's job. -dean +*/ +void gnome_end_menu(winid wid, const char *prompt) +{ + if (wid != -1 && gnome_windowlist[wid].win != NULL) + { + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_END_MENU], + prompt); + } +} + +/* +int select_menu(windid window, int how, menu_item **selected) + -- Return the number of items selected; 0 if none were chosen, + -1 when explicitly cancelled. If items were selected, then + selected is filled in with an allocated array of menu_item + structures, one for each selected line. The caller must + free this array when done with it. The "count" field + of selected is a user supplied count. If the user did + not supply a count, then the count field is filled with + -1 (meaning all). A count of zero is equivalent to not + being selected and should not be in the list. If no items + were selected, then selected is NULL'ed out. How is the + mode of the menu. Three valid values are PICK_NONE, + PICK_ONE, and PICK_N, meaning: nothing is selectable, + only one thing is selectable, and any number valid items + may selected. If how is PICK_NONE, this function should + never return anything but 0 or -1. + -- You may call select_menu() on a window multiple times -- + the menu is saved until start_menu() or destroy_nhwindow() + is called on the window. + -- Note that NHW_MENU windows need not have select_menu() + called for them. There is no way of knowing whether + select_menu() will be called for the window at + create_nhwindow() time. +*/ +int gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected) +{ + int nReturned = -1; + + if (wid != -1 && gnome_windowlist[wid].win != NULL && + gnome_windowlist[wid].type == NHW_MENU) + { + nReturned=ghack_menu_window_select_menu (gnome_windowlist[wid].win, + selected, how); + } + + return nReturned; +} + +/* + -- Indicate to the window port that the inventory has been changed. + -- Merely calls display_inventory() for window-ports that leave the + window up, otherwise empty. +*/ +void gnome_update_inventory() +{ + ghack_main_window_update_inventory(); +} + +/* +mark_synch() -- Don't go beyond this point in I/O on any channel until + all channels are caught up to here. Can be an empty call + for the moment +*/ +void gnome_mark_synch() +{ + /* Do nothing */ +} + +/* +wait_synch() -- Wait until all pending output is complete (*flush*() for + streams goes here). + -- May also deal with exposure events etc. so that the + display is OK when return from wait_synch(). +*/ +void gnome_wait_synch() +{ + /* Do nothing */ +} + +/* +cliparound(x, y)-- Make sure that the user is more-or-less centered on the + screen if the playing area is larger than the screen. + -- This function is only defined if CLIPPING is defined. +*/ +void gnome_cliparound(int x, int y) +{ + /* FIXME!!! winid should be a parameter!!! + * Call a function that Does The Right Thing(tm). + */ + gnome_cliparound_proper(WIN_MAP,x,y); +} + +void gnome_cliparound_proper(winid wid, int x, int y) +{ + if (wid != -1 && gnome_windowlist[wid].win != NULL) + { + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_CLIPAROUND], + (guint) x, + (guint) y); + } +} + +/* +print_glyph(window, x, y, glyph) + -- Print the glyph at (x,y) on the given window. Glyphs are + integers at the interface, mapped to whatever the window- + port wants (symbol, font, color, attributes, ...there's + a 1-1 map between glyphs and distinct things on the map). +*/ +void gnome_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph) +{ + if (wid != -1 && gnome_windowlist[wid].win != NULL) + { + GdkImlibImage *im; + + im = ghack_image_from_glyph( glyph, FALSE); + + gtk_signal_emit (GTK_OBJECT (gnome_windowlist[wid].win), + ghack_signals[GHSIG_PRINT_GLYPH], + (guint) x, + (guint) y, + im, + NULL); + } +} + +/* +raw_print(str) -- Print directly to a screen, or otherwise guarantee that + the user sees str. raw_print() appends a newline to str. + It need not recognize ASCII control characters. This is + used during startup (before windowing system initialization + -- maybe this means only error startup messages are raw), + for error messages, and maybe other "msg" uses. E.g. + updating status for micros (i.e, "saving"). +*/ +void gnome_raw_print(const char *str) +{ + tty_raw_print(str); +} + +/* +raw_print_bold(str) + -- Like raw_print(), but prints in bold/standout (if +possible). +*/ +void gnome_raw_print_bold(const char *str) +{ + tty_raw_print_bold(str); +} + +/* +int nhgetch() -- Returns a single character input from the user. + -- In the tty window-port, nhgetch() assumes that tgetch() + will be the routine the OS provides to read a character. + Returned character _must_ be non-zero. +*/ +int gnome_nhgetch() +{ + int key; + GList *theFirst; + gtk_signal_emit (GTK_OBJECT (gnome_windowlist[WIN_STATUS].win), + ghack_signals[GHSIG_FADE_HIGHLIGHT]); + + g_askingQuestion = 1; + /* Process events until a key press event arrives. */ + while ( g_numKeys == 0 ) + gtk_main_iteration(); + + theFirst = g_list_first( g_keyBuffer); + g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst); + key = GPOINTER_TO_INT( theFirst->data); + g_list_free_1( theFirst); + g_numKeys--; + g_askingQuestion = 0; + return ( key); +} + +/* +int nh_poskey(int *x, int *y, int *mod) + -- Returns a single character input from the user or a + a positioning event (perhaps from a mouse). If the + return value is non-zero, a character was typed, else, + a position in the MAP window is returned in x, y and mod. + mod may be one of + + CLICK_1 -- mouse click type 1 + CLICK_2 -- mouse click type 2 + + The different click types can map to whatever the + hardware supports. If no mouse is supported, this + routine always returns a non-zero character. +*/ +int gnome_nh_poskey(int *x, int *y, int *mod) +{ + gtk_signal_emit (GTK_OBJECT (gnome_windowlist[WIN_STATUS].win), + ghack_signals[GHSIG_FADE_HIGHLIGHT]); + + g_askingQuestion = 0; + /* Process events until a key or map-click arrives. */ + while ( g_numKeys == 0 && g_numClicks == 0 ) + gtk_main_iteration(); + + if (g_numKeys > 0) { + int key; + GList *theFirst; + + theFirst = g_list_first( g_keyBuffer); + g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst); + key = GPOINTER_TO_INT( theFirst->data); + g_list_free_1( theFirst); + g_numKeys--; + return ( key); + } + else { + GHClick *click; + GList *theFirst; + + theFirst = g_list_first( g_clickBuffer); + g_clickBuffer = g_list_remove_link(g_clickBuffer, theFirst); + click = (GHClick*) theFirst->data; + *x=click->x; + *y=click->y; + *mod=click->mod; + g_free( click); + g_list_free_1( theFirst); + g_numClicks--; + return ( 0); + } +} + +/* +nhbell() -- Beep at user. [This will exist at least until sounds are + redone, since sounds aren't attributable to windows anyway.] +*/ +void gnome_nhbell() +{ + /* FIXME!!! Play a cool GNOME sound instead */ + gdk_beep(); +} + +/* +doprev_message() + -- Display previous messages. Used by the ^P command. + -- On the tty-port this scrolls WIN_MESSAGE back one line. +*/ +int gnome_doprev_message() +{ + /* Do Nothing. They can read old messages using the scrollbar. */ + return 0; +} + +/* +char yn_function(const char *ques, const char *choices, char default) + -- Print a prompt made up of ques, choices and default. + Read a single character response that is contained in + choices or default. If choices is NULL, all possible + inputs are accepted and returned. This overrides + everything else. The choices are expected to be in + lower case. Entering ESC always maps to 'q', or 'n', + in that order, if present in choices, otherwise it maps + to default. Entering any other quit character (SPACE, + RETURN, NEWLINE) maps to default. + -- If the choices string contains ESC, then anything after + it is an acceptable response, but the ESC and whatever + follows is not included in the prompt. + -- If the choices string contains a '#' then accept a count. + Place this value in the global "yn_number" and return '#'. + -- This uses the top line in the tty window-port, other + ports might use a popup. +*/ +char gnome_yn_function(const char *question, const char *choices, + CHAR_P def) +{ + int ch; + int result=-1; + char message[BUFSZ]; + char yn_esc_map='\033'; + GtkWidget *mainWnd = ghack_get_main_window(); + + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + Strcpy(choicebuf, choices); + if ((cb = index(choicebuf, '\033')) != 0) { + /* anything beyond is hidden */ + *cb = '\0'; + } + sprintf(message, "%s [%s] ", question, choicebuf); + if (def) sprintf(eos(message), "(%c) ", def); + /* escape maps to 'q' or 'n' or default, in that order */ + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + Strcpy(message, question); + } + + + gnome_putstr(WIN_MESSAGE, ATR_BOLD, message); + if (mainWnd != NULL && choices && !index(choices,ch)) { + return(ghack_yes_no_dialog( question, choices, def)); + } + + /* Only here if main window is not present */ + while (result<0) { + ch=gnome_nhgetch(); + if (ch=='\033') { + result=yn_esc_map; + } else if (choices && !index(choices,ch)) { + /* FYI: ch==-115 is for KP_ENTER */ + if (def && (ch==' ' || ch=='\r' || ch=='\n' || ch==-115)) { + result=def; + } else { + gnome_nhbell(); + /* and try again... */ + } + } else { + result=ch; + } + } + return result; +} + +/* +getlin(const char *ques, char *input) + -- Prints ques as a prompt and reads a single line of text, + up to a newline. The string entered is returned without the + newline. ESC is used to cancel, in which case the string + "\033\000" is returned. + -- getlin() must call flush_screen(1) before doing anything. + -- This uses the top line in the tty window-port, other + ports might use a popup. +*/ +void gnome_getlin(const char *question, char *input) +{ + int ret; + + ret = ghack_ask_string_dialog(question, "", "nethack", input); + + if (ret == -1) + input[0] = 0; +} + +/* +int get_ext_cmd(void) + -- Get an extended command in a window-port specific way. + An index into extcmdlist[] is returned on a successful + selection, -1 otherwise. +*/ +int gnome_get_ext_cmd() +{ + return ghack_menu_ext_cmd(); +} + + +/* +number_pad(state) + -- Initialize the number pad to the given state. +*/ +void gnome_number_pad(int state) +{ + /* Do Nothing */ +} + +/* +delay_output() -- Causes a visible delay of 50ms in the output. + Conceptually, this is similar to wait_synch() followed + by a nap(50ms), but allows asynchronous operation. +*/ +void gnome_delay_output() +{ + if (gnome_windowlist[WIN_MESSAGE].win != NULL) { + gtk_signal_emit( GTK_OBJECT (gnome_windowlist[WIN_MESSAGE].win), + ghack_signals[GHSIG_DELAY], + (guint) 50); + } +} + +/* +start_screen() -- Only used on Unix tty ports, but must be declared for + completeness. Sets up the tty to work in full-screen + graphics mode. Look at win/tty/termcap.c for an + example. If your window-port does not need this function + just declare an empty function. +*/ +void gnome_start_screen() +{ + /* Do Nothing */ +} + +/* +end_screen() -- Only used on Unix tty ports, but must be declared for + completeness. The complement of start_screen(). +*/ +void gnome_end_screen() +{ + /* Do Nothing */ +} + +/* +outrip(winid, int) + -- The tombstone code. If you want the traditional code use + genl_outrip for the value and check the #if in rip.c. +*/ +void gnome_outrip(winid wid, int how) +{ + /* Follows roughly the same algorithm as genl_outrip() */ + char buf[BUFSZ]; + char ripString[BUFSZ]="\0"; + extern const char *killed_by_prefix[]; + + /* Put name on stone */ + Sprintf(buf, "%s\n", plname); + Strcat(ripString, buf); + + /* Put $ on stone */ + Sprintf(buf, "%ld Au\n", +#ifndef GOLDOBJ + u.ugold); +#else + done_money); +#endif + Strcat(ripString, buf); + + /* Put together death description */ + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + /* Put death type on stone */ + Strcat(ripString, buf); + Strcat(ripString, "\n"); + + /* Put year on stone */ + Sprintf(buf, "%4d\n", getyear()); + Strcat(ripString, buf); + + ghack_text_window_rip_string( ripString); +} diff --git a/win/gnome/gnbind.h b/win/gnome/gnbind.h new file mode 100644 index 0000000..cca6429 --- /dev/null +++ b/win/gnome/gnbind.h @@ -0,0 +1,94 @@ +/* SCCS Id: @(#)gnbind.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackBind_h +#define GnomeHackBind_h + +/* + * This header files defines the interface between the window port specific + * code in the Gnome port and the rest of the nethack game engine. +*/ + +#include +#include + +#include "gnomeprv.h" +#include "gnmain.h" +#include "gnmap.h" +#include "gnmenu.h" +#include "gnplayer.h" +#include "gnsignal.h" +#include "gnstatus.h" +#include "gntext.h" +#include "gnmesg.h" +#include "gnyesno.h" +#include "gnglyph.h" +#include "gnworn.h" + + +/* Create an array to keep track of the various windows */ + +#ifndef MAXWINDOWS +#define MAXWINDOWS 15 +#endif + +typedef struct gnome_nhwindow_data { + GtkWidget* win; + int type; +} GNHWinData; + + +/* Some prototypes */ +void gnome_init_nhwindows(int* argc, char** argv); +void gnome_player_selection(void); +void gnome_askname(void); +void gnome_get_nh_event(void); +void gnome_exit_nhwindows(const char *); +void gnome_suspend_nhwindows(const char *); +void gnome_resume_nhwindows(void); +winid gnome_create_nhwindow(int type); +void gnome_create_nhwindow_by_id(int type, winid i); +void gnome_clear_nhwindow(winid wid); +void gnome_display_nhwindow(winid wid, BOOLEAN_P block); +void gnome_destroy_nhwindow(winid wid); +void gnome_curs(winid wid, int x, int y); +void gnome_putstr(winid wid, int attr, const char *text); +void gnome_display_file(const char *filename,BOOLEAN_P must_exist); +void gnome_start_menu(winid wid); +void gnome_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel); +void gnome_end_menu(winid wid, const char *prompt); +int gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected); +/* No need for message_menu -- we'll use genl_message_menu instead */ +void gnome_update_inventory(void); +void gnome_mark_synch(void); +void gnome_wait_synch(void); +void gnome_cliparound(int x, int y); +/* The following function does the right thing. The nethack + * gnome_cliparound (which lacks the winid) simply calls this funtion. +*/ +void gnome_cliparound_proper(winid wid, int x, int y); +void gnome_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph); +void gnome_raw_print(const char *str); +void gnome_raw_print_bold(const char *str); +int gnome_nhgetch(void); +int gnome_nh_poskey(int *x, int *y, int *mod); +void gnome_nhbell(void); +int gnome_doprev_message(void); +char gnome_yn_function(const char *question, const char *choices, + CHAR_P def); +void gnome_getlin(const char *question, char *input); +int gnome_get_ext_cmd(void); +void gnome_number_pad(int state); +void gnome_delay_output(void); +void gnome_start_screen(void); +void gnome_end_screen(void); +void gnome_outrip(winid wid, int how); +void gnome_delete_nhwindow_by_reference( GtkWidget *menuWin); + + +#endif /* GnomeHackBind_h */ + + diff --git a/win/gnome/gnglyph.c b/win/gnome/gnglyph.c new file mode 100644 index 0000000..bb08abc --- /dev/null +++ b/win/gnome/gnglyph.c @@ -0,0 +1,230 @@ +/* SCCS Id: @(#)gnglyph.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnglyph.h" +#include "tile2x11.h" + +/* from tile.c */ +extern int total_tiles_used; + +static GHackGlyphs ghack_glyphs; +static GdkImlibImage** ghack_tiles = NULL; + +/* NAME: + * ghack_init_glyphs(char* xpm_file) + * + * ARGUMENTS: + * char *xpm_file -- The name of the image file. + * May be any image format imlib recognizes. + * Does not have to be XPM. + * + * RETURNS: + * TRUE upon successful loading of the glyphs. + * FALSE upon failure. + * + * PURPOSE: + * Constructor for the Glyph object. Well, really each glyph + * object is a collection of glyphs, or tiles. This constructor + * takes a single argument: the name of the image file that contains + * the tile images. + * + * NOTES: + * The glyphs (tiles) must be in the image in a certain way: the + * glyphs must be stacked such that the resultant image is + * TILE_X * TILES_PER_ROW wide, and + * TILE_Y * (number of glyphs) / TILES_PER_ROW high (rounded up). + * In this sense, TILE_X == TILE_Y, and can be any reasonable integer + * say, 16 <= TILE_X <= 64. Because the glyph number is tightly + * coupled to the Nethack object it represents, the order of the + * glyphs in the image is imporant: Glyph 1 is at the top of the + * image, while Glyph N (the last glyph) is at the bottom. + * + * What's the difference between a glyph and a tile? Well, a + * tile is just an image. A glyph is a tile that knows its + * place in line. + * + * This initializer relies heavily on gdk_imlib. Thanks, Rasterman. + */ + +int +ghack_init_glyphs(const char *xpmFile) +{ + ghack_glyphs.im = gdk_imlib_load_image((char *) xpmFile); + if ( ! ghack_glyphs.im ) { + g_error("Couldn't load required xpmFile!"); + return -1; + } + + gdk_imlib_render(ghack_glyphs.im, ghack_glyphs.im->rgb_width, + ghack_glyphs.im->rgb_height); + + if ((ghack_glyphs.im->rgb_width % TILES_PER_ROW) != 0 || + ghack_glyphs.im->rgb_width <= TILES_PER_ROW) { + g_error("%s is not a multiple of %d (number of tiles/row) pixels wide", + xpmFile, TILES_PER_ROW); + return -1; + } + ghack_glyphs.count = total_tiles_used; + if ((ghack_glyphs.count % TILES_PER_ROW) != 0) { + ghack_glyphs.count += + TILES_PER_ROW - (ghack_glyphs.count % TILES_PER_ROW); + } + ghack_glyphs.width = ghack_glyphs.im->rgb_width / TILES_PER_ROW; + ghack_glyphs.height = + ghack_glyphs.im->rgb_height / (ghack_glyphs.count / TILES_PER_ROW); + + + /* Assume the tiles are organized in rows of TILES_PER_ROW */ + ghack_tiles = g_new0( GdkImlibImage*, ghack_glyphs.count ); + return (ghack_tiles == NULL) ? -1 : 0; +} + +void +ghack_free_glyphs( ) +{ + int i; + for ( i=0 ; i= ghack_glyphs.count || tile < 0 ) + { + g_warning("Aiiee! I've was asked for a tile outside the allowed range!\n" + "Email this to other-gnomehack@lists.debian.org"); + g_warning("Max tile: %d Tile asked for: %d", + ghack_glyphs.count, tile); + return NULL; + } + + if (ghack_glyphs.im == NULL) + { + g_warning("Aiiee! I've been asked to clone from a null image.\n" + "Email this to other-gnomehack@lists.debian.org"); + g_warning( "making image from tile %d, force=%s\n", tile, + (force==TRUE)? "TRUE": "FALSE"); + } + + if (force == TRUE) + { + g_warning("Aiiee! I've been asked to force rendering.\n" + "Email this to other-gnomehack@lists.debian.org"); + g_warning( "making image from tile %d, force=%s\n", tile, + (force==TRUE)? "TRUE" : "FALSE"); + } + + if (!ghack_tiles[tile] || force) { + int src_x, src_y; +#if 0 + fprintf( stderr, "crop_and_clone: glyph=%d, tile=%d, ptr=%p, x=%d, y=%d, w=%d, h=%d\n", glyph, tile, + (void*)&(ghack_tiles[tile]), 0, + tile * ghack_glyphs.width, + ghack_glyphs.height, + ghack_glyphs.width); +#endif + if (ghack_glyphs.im->pixmap == NULL) + g_warning( "Aiiee! ghack_glyphs.im->pixmap==NULL!!!!\n"); + src_x = (tile % TILES_PER_ROW) * ghack_glyphs.width; + src_y = (tile / TILES_PER_ROW) * ghack_glyphs.height; + ghack_tiles[tile] = gdk_imlib_crop_and_clone_image(ghack_glyphs.im, + src_x, src_y, + ghack_glyphs.width, + ghack_glyphs.height); + } + + if (ghack_tiles[tile] && (!ghack_tiles[tile]->pixmap || force)) + { + if ( gdk_imlib_render(ghack_tiles[tile], + ghack_tiles[tile]->rgb_width, + ghack_tiles[tile]->rgb_height) == 0) { + g_error("GLYPH: couldn't create tile # %d", tile); + } + if ( !ghack_tiles[tile]->pixmap ) + g_error("Strange, tile # %d didn't get rendered???", tile); + } + + return ghack_tiles[tile]; +} diff --git a/win/gnome/gnglyph.h b/win/gnome/gnglyph.h new file mode 100644 index 0000000..e2b30da --- /dev/null +++ b/win/gnome/gnglyph.h @@ -0,0 +1,42 @@ +/* SCCS Id: @(#)gnglyph.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackGlyph_h +#define GnomeHackGlyph_h + +#include "config.h" +#include "global.h" + +/* the prototypes in system headers contain useless argument names + that trigger spurious warnings if gcc's `-Wshadow' option is used */ +#undef index +#define index _hide_index_ +#define time _hide_time_ + +#include +#include + +#undef index +#define index strchr +#undef time + + +extern short glyph2tile[]; /* From tile.c */ + +typedef struct { + GdkImlibImage* im; + int count; + int width; + int height; +} GHackGlyphs; + +extern int ghack_init_glyphs( const char *); +extern void ghack_free_glyphs( void); +extern void ghack_dispose_glyphs( void); +extern int ghack_glyph_count( void); +extern GdkImlibImage* ghack_image_from_glyph( int, gboolean); +extern int ghack_glyph_height( void); +extern int ghack_glyph_width( void); + +#endif /* GnomeHackGlyph_h */ diff --git a/win/gnome/gnmain.c b/win/gnome/gnmain.c new file mode 100644 index 0000000..98b2f91 --- /dev/null +++ b/win/gnome/gnmain.c @@ -0,0 +1,839 @@ +/* SCCS Id: @(#)gnmain.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnmain.h" +#include "gnsignal.h" +#include "gnbind.h" +#include "gnopts.h" +#include +#include +#include +#include +#include +#include +#include "hack.h" +#include "date.h" + +static GtkWidget* mainWindow=NULL; +static GtkWidget *about=NULL; +static GtkWidget* hBoxFirstRow; +static GtkWidget* vBoxMain; + +int restarted = 0; +int os_x = 0, os_y = 0, os_w = 0, os_h = 0; + + +static GnomeClient *session_id; + +static +void ghack_quit_game(GtkWidget *widget, int button) +{ + gtk_widget_hide(widget); + if (button == 0) { + gnome_exit_nhwindows(0); + gtk_object_unref(GTK_OBJECT(session_id)); + } +} + +static +void ghack_quit_game_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget *box; + box = gnome_message_box_new(_("Do you really want to quit?"), + GNOME_MESSAGE_BOX_QUESTION, GNOME_STOCK_BUTTON_YES, + GNOME_STOCK_BUTTON_NO, NULL); + gnome_dialog_set_default( GNOME_DIALOG(box), 1); + gnome_dialog_set_parent (GNOME_DIALOG (box), + GTK_WINDOW (ghack_get_main_window ()) ); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 1, 'n', 0); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 0, 'y', 0); + + gtk_window_set_modal( GTK_WINDOW(box), TRUE); + gtk_signal_connect( GTK_OBJECT(box), "clicked", + (GtkSignalFunc)ghack_quit_game, NULL); + gtk_widget_show(box); +} + +static +void ghack_save_game(GtkWidget *widget, int button) +{ + gtk_widget_hide(widget); + if (button == 0) { + if(dosave0()) { + /* make sure they see the Saving message */ + display_nhwindow(WIN_MESSAGE, TRUE); + gnome_exit_nhwindows("Be seeing you..."); + } else (void)doredraw(); + } +} + +void ghack_save_game_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget *box; + box = gnome_message_box_new(_("Quit and save the current game?"), + GNOME_MESSAGE_BOX_QUESTION, GNOME_STOCK_BUTTON_YES, + GNOME_STOCK_BUTTON_NO, NULL); + gnome_dialog_set_default( GNOME_DIALOG(box), 1); + gnome_dialog_set_parent (GNOME_DIALOG (box), + GTK_WINDOW (ghack_get_main_window ()) ); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 1, 'n', 0); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 0, 'y', 0); + + gtk_window_set_modal( GTK_WINDOW(box), TRUE); + gtk_signal_connect( GTK_OBJECT(box), "clicked", + (GtkSignalFunc)ghack_save_game, NULL); + gtk_widget_show(box); +} + +static +void ghack_new_game(GtkWidget *widget, int button) +{ + if (button == 0) { + g_message("This feature is not yet implemented. Sorry."); + } +} + +static +void ghack_new_game_cb(GtkWidget *widget, gpointer data) +{ + GtkWidget *box; + box = gnome_message_box_new(_("Start a new game?"), + GNOME_MESSAGE_BOX_QUESTION, GNOME_STOCK_BUTTON_YES, + GNOME_STOCK_BUTTON_NO, NULL); + gnome_dialog_set_default( GNOME_DIALOG(box), 1); + gnome_dialog_set_parent (GNOME_DIALOG (box), + GTK_WINDOW (ghack_get_main_window ()) ); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 1, 'n', 0); + gnome_dialog_set_accelerator (GNOME_DIALOG(box), 0, 'y', 0); + + gtk_window_set_modal( GTK_WINDOW(box), TRUE); + gtk_signal_connect( GTK_OBJECT(box), "clicked", + (GtkSignalFunc)ghack_new_game, NULL); + gtk_widget_show(box); +} + +static void +about_destroy_callback (void) +{ + about = NULL; +} + +static void +ghack_about_cb(GtkWidget *widget, gpointer data) +{ + char buf[BUFSZ]="\0"; + char buf1[BUFSZ]="\0"; + const gchar *authors[] = {"Erik Andersen", "Anthony Taylor", + "Jeff Garzik", "The Nethack Dev Team", NULL}; + + if (about) { + gdk_window_raise (about->window); + return; + } + + getversionstring(buf); + strcat( buf1, VERSION_STRING); + strcat( buf, + _("\nSend comments and bug reports to: nethack-bugs@nethack.org\n" + "This game is free software. See License for details.")); + about = gnome_about_new(_("Nethack"), + buf1, "Copyright (C) 1985-2002 Mike Stephenson", + (const char **)authors, buf, + NULL); + + gtk_signal_connect (GTK_OBJECT (about), "destroy", + (GtkSignalFunc) about_destroy_callback, NULL); + + gtk_widget_show(about); +} + +static void +ghack_settings_cb(GtkWidget *widget, gpointer data) +{ + ghack_settings_dialog(); +} + +static void +ghack_accelerator_selected (GtkWidget *widget, gpointer data) +{ + GdkEventKey event; + int key = GPOINTER_TO_INT( data); + /* g_message("An accelerator for \"%c\" was selected", key); */ + /* stuff a key directly into the keybuffer */ + event.state=0; + event.keyval=key; + ghack_handle_key_press(NULL, &event, NULL); +} + +#ifndef M +# ifndef NHSTDC +# define M(c) (0x80 | (c)) +# else +# define M(c) ((c) - 128) +# endif /* NHSTDC */ +#endif +#ifndef C +#define C(c) (0x1f & (c)) +#endif + + +GnomeUIInfo game_tree[] = +{ + { + GNOME_APP_UI_ITEM, N_ ("_Change Settings..."), + N_("Change Game Settings"), ghack_settings_cb, NULL, NULL, + GNOME_APP_PIXMAP_NONE, NULL, 0,0, NULL + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Version"), NULL, + ghack_accelerator_selected, GINT_TO_POINTER('v'), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT, 'v',0 + }, + { + GNOME_APP_UI_ITEM, N_("History..."), NULL, + ghack_accelerator_selected, GINT_TO_POINTER('V'), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT, 'V',GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Compilation..."), NULL, + ghack_accelerator_selected, GINT_TO_POINTER(M('v')), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT,'v',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Options..."), NULL, + ghack_accelerator_selected, GINT_TO_POINTER('O'), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_PREF, 'O', GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Explore Mode..."), NULL, + ghack_accelerator_selected, GINT_TO_POINTER('X'), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_QUIT, 'X', GDK_SHIFT_MASK + }, + GNOMEUIINFO_SEPARATOR, + GNOMEUIINFO_MENU_NEW_GAME_ITEM(ghack_new_game_cb, NULL), + GNOMEUIINFO_MENU_SAVE_ITEM(ghack_save_game_cb, NULL), + { + GNOME_APP_UI_ITEM, N_("Exit"), NULL, + ghack_quit_game_cb, GINT_TO_POINTER(M('Q')), NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT, 'Q', GDK_MOD1_MASK + }, + GNOMEUIINFO_END +}; + + + +GnomeUIInfo edit_menu[] = { + { + GNOME_APP_UI_ITEM, N_("Inventory"), + N_("Edit/View your Inventory"), ghack_accelerator_selected, + GINT_TO_POINTER('i'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'i', 0 + }, + { + GNOME_APP_UI_ITEM, N_("Discoveries"), + N_("Edit/View your Discoveries"), + ghack_accelerator_selected, + GINT_TO_POINTER('\\'), NULL, GNOME_APP_PIXMAP_NONE, NULL, '\\',0 + }, + { + GNOME_APP_UI_ITEM, N_("List/reorder your spells"), + N_("List/reorder your spells"), ghack_accelerator_selected, + GINT_TO_POINTER('x'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'x', 0 + }, + { + GNOME_APP_UI_ITEM, N_("Adjust letters"), + N_("Adjust letter for items in your Inventory"), ghack_accelerator_selected, + GINT_TO_POINTER(M('a')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'a', GDK_MOD1_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Name object"), + N_("Assign a name to an object"), ghack_accelerator_selected, + GINT_TO_POINTER(M('n')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'n', GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Name creature"), + N_("Assign a name to a creature"), ghack_accelerator_selected, + GINT_TO_POINTER('C'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'C', GDK_SHIFT_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Qualifications"), + N_("Edit your Qualifications"), ghack_accelerator_selected, + GINT_TO_POINTER(M('e')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'e',GDK_MOD1_MASK + }, + GNOMEUIINFO_END +}; + + +GnomeUIInfo apparel_menu[] = { + { + GNOME_APP_UI_ITEM, N_("Wield Weapon"), + N_("Select a weapon to fight with"), + ghack_accelerator_selected, + GINT_TO_POINTER('w'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'w',0 + }, + { + GNOME_APP_UI_ITEM, N_("Remove Apparel..."), + N_("Remove apparel dialog bog"), ghack_accelerator_selected, + GINT_TO_POINTER('A'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'A',GDK_SHIFT_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Wear Armor"), + N_("Put on armor"), ghack_accelerator_selected, + GINT_TO_POINTER('W'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'W',GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Take off Armor"), + N_("Take off armor you are wearing"), ghack_accelerator_selected, + GINT_TO_POINTER('T'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'T',GDK_SHIFT_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Put on non-armor"), + N_("Put on non-armor apparel"), ghack_accelerator_selected, + GINT_TO_POINTER('P'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'P',GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Remove non-armor"), + N_("Remove non-armor apparel you are wearing"), ghack_accelerator_selected, + GINT_TO_POINTER('R'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'R',GDK_SHIFT_MASK + }, + GNOMEUIINFO_END +}; + +GnomeUIInfo action_menu[] = { + { + GNOME_APP_UI_ITEM, N_("Get"), + N_("Pick up things at the current location"), + ghack_accelerator_selected, + GINT_TO_POINTER(','), NULL, GNOME_APP_PIXMAP_NONE, NULL, ',',0 + }, + { + GNOME_APP_UI_ITEM, N_("Loot"), + N_("loot a box on the floor"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('l')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'l',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Sit"), + N_("sit down"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('s')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 's',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Force"), + N_("force a lock"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('f')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'f',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Kick"), + N_("kick something (usually a door)"), + ghack_accelerator_selected, + GINT_TO_POINTER(C('d')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'd',GDK_CONTROL_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Jump"), + N_("jump to another location"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('j')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'j',GDK_MOD1_MASK + }, +#ifdef STEED + { + GNOME_APP_UI_ITEM, N_("Ride"), + N_("Ride (or stop riding) a monster"), + doride, + GINT_TO_POINTER(M('r')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'R',GDK_MOD1_MASK + }, +#endif + { + GNOME_APP_UI_ITEM, N_("Wipe face"), + N_("wipe off your face"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('w')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'w',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Throw/Shoot"), + N_("throw or shoot a weapon"), + ghack_accelerator_selected, + GINT_TO_POINTER('t'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 't',0 + }, + { + GNOME_APP_UI_ITEM, N_("Quiver/Ready"), + N_("ready or quiver some ammunition"), + ghack_accelerator_selected, + GINT_TO_POINTER('Q'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'Q',GDK_SHIFT_MASK, + }, + { + GNOME_APP_UI_ITEM, N_("Open Door"), + N_("open a door"), + ghack_accelerator_selected, + GINT_TO_POINTER('o'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'o',0 + }, + { + GNOME_APP_UI_ITEM, N_("Close Door"), + N_("open a door"), + ghack_accelerator_selected, + GINT_TO_POINTER('c'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'c',0 + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Drop"), + N_("drop an object"), + ghack_accelerator_selected, + GINT_TO_POINTER('d'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'd',0 + }, + { + GNOME_APP_UI_ITEM, N_("Drop Many"), + N_("drop selected types of objects"), + ghack_accelerator_selected, + GINT_TO_POINTER('D'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'D',GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Eat"), + N_("eat something"), + ghack_accelerator_selected, + GINT_TO_POINTER('e'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'e',0 + }, + { + GNOME_APP_UI_ITEM, N_("Engrave"), + N_("write a message in the dust on the floor"), + ghack_accelerator_selected, + GINT_TO_POINTER('E'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'E',GDK_SHIFT_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Apply"), + N_("apply or use a tool (pick-axe, key, camera, etc.)"), + ghack_accelerator_selected, + GINT_TO_POINTER('a'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'a',0 + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Up"), + N_("go up the stairs"), + ghack_accelerator_selected, + GINT_TO_POINTER('<'), NULL, GNOME_APP_PIXMAP_NONE, NULL, '<',0 + }, + { + GNOME_APP_UI_ITEM, N_("Down"), + N_("go down the stairs"), + ghack_accelerator_selected, + GINT_TO_POINTER('>'), NULL, GNOME_APP_PIXMAP_NONE, NULL, '>',0 + }, + { + GNOME_APP_UI_ITEM, N_("Rest"), + N_("wait for a moment"), + ghack_accelerator_selected, + GINT_TO_POINTER('.'), NULL, GNOME_APP_PIXMAP_NONE, NULL, '.',0 + }, + { + GNOME_APP_UI_ITEM, N_("Search"), + N_("search for secret doors, hidden traps and monsters"), + ghack_accelerator_selected, + GINT_TO_POINTER('s'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 's',0 + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Chat"), + N_("talk to someone"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('c')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'c',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Pay"), + N_("pay your bill to the shopkeeper"), + ghack_accelerator_selected, + GINT_TO_POINTER('p'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'p',0 + }, + GNOMEUIINFO_END +}; + +GnomeUIInfo magic_menu[] = { + { + GNOME_APP_UI_ITEM, N_("Quaff potion"), + N_("drink a potion"), + ghack_accelerator_selected, + GINT_TO_POINTER('q'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'q',0 + }, + { + GNOME_APP_UI_ITEM, N_("Read Book/Scroll"), + N_("read a spell book or a scroll"), + ghack_accelerator_selected, + GINT_TO_POINTER('r'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'r',0 + }, + { + GNOME_APP_UI_ITEM, N_("Zap Wand"), + N_("zap a wand"), + ghack_accelerator_selected, + GINT_TO_POINTER('z'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'z',0 + }, + { + GNOME_APP_UI_ITEM, N_("Zap Spell"), + N_("cast a spell"), + ghack_accelerator_selected, + GINT_TO_POINTER('Z'), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'Z',GDK_SHIFT_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Dip"), + N_("dip an object into something"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('d')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'd',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Rub"), + N_("Rub something (i.e. a lamp)"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('r')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'r',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Invoke"), + N_("invoke an object's special powers"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('i')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'i',GDK_MOD1_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Offer"), + N_("offer a sacrifice to the gods"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('o')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'o',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Pray"), + N_("pray to the gods for help"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('p')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'p',GDK_MOD1_MASK + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("Teleport"), + N_("teleport (if you can)"), + ghack_accelerator_selected, + GINT_TO_POINTER(C('t')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 't',GDK_CONTROL_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Monster Action"), + N_("use a monster's special ability"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('m')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 'm',GDK_MOD1_MASK + }, + { + GNOME_APP_UI_ITEM, N_("Turn Undead"), + N_("turn undead"), + ghack_accelerator_selected, + GINT_TO_POINTER(M('t')), NULL, GNOME_APP_PIXMAP_NONE, NULL, 't',GDK_MOD1_MASK + }, + GNOMEUIINFO_END +}; + +GnomeUIInfo help_menu[] = { + { + GNOME_APP_UI_ITEM, N_("About..."), + N_("About GnomeHack"), ghack_about_cb, NULL, NULL, + GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT, 0, 0, NULL + }, + { + GNOME_APP_UI_ITEM, N_("Help"), NULL, + ghack_accelerator_selected, + GINT_TO_POINTER('?'), NULL, GNOME_APP_PIXMAP_STOCK, + GNOME_STOCK_MENU_ABOUT, '?', 0 + }, + GNOMEUIINFO_SEPARATOR, + { + GNOME_APP_UI_ITEM, N_("What is here"), + N_("Check what items occupy the current location"), + ghack_accelerator_selected, + GINT_TO_POINTER(':'), NULL, GNOME_APP_PIXMAP_STOCK, + GNOME_STOCK_MENU_ABOUT, ':',0 + }, + { + GNOME_APP_UI_ITEM, N_("What is that"), + N_("Identify an object"), + ghack_accelerator_selected, + GINT_TO_POINTER(';'), NULL, GNOME_APP_PIXMAP_STOCK, + GNOME_STOCK_MENU_ABOUT, ';',0 + }, + { + GNOME_APP_UI_ITEM, N_("Identify a map symbol"), + N_("Identify a map symbol"), + ghack_accelerator_selected, + GINT_TO_POINTER('/'), NULL, GNOME_APP_PIXMAP_STOCK, + GNOME_STOCK_MENU_ABOUT, '/',0 + }, + GNOMEUIINFO_END +}; + +GnomeUIInfo mainmenu[] = { + GNOMEUIINFO_MENU_GAME_TREE(game_tree), + GNOMEUIINFO_MENU_EDIT_TREE(edit_menu), + { GNOME_APP_UI_SUBTREE, N_("Apparel"), NULL, apparel_menu, NULL, + NULL, 0, NULL, 0, 0, NULL }, + { GNOME_APP_UI_SUBTREE, N_("Action"), NULL, action_menu, NULL, + NULL, 0, NULL, 0, 0, NULL }, + { GNOME_APP_UI_SUBTREE, N_("Magic"), NULL, magic_menu, NULL, + NULL, 0, NULL, 0, 0, NULL }, + GNOMEUIINFO_MENU_HELP_TREE(help_menu), + GNOMEUIINFO_END +}; + +static void +ghack_main_window_key_press(GtkWidget *widget, GdkEventKey *event, + gpointer data) +{ + /* First, turn off the key press propogation. We've got the + * key, but we don't wan't the underlying Gtk widgets to get it, + * since they do the wrong thing with the arrow keys (shift focus)... */ + gtk_signal_emit_stop_by_name( GTK_OBJECT(mainWindow), "key_press_event"); + + /* stuff the key event into the keybuffer */ + ghack_handle_key_press(widget, event, data); +} + + +/* parsing args */ +void +parse_args (int argc, char *argv[]) +{ + gint ch; + + struct option options[] = { + /* Default args */ + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'v' }, + + { NULL, 0, NULL, 0 } + }; + + gchar *id = NULL; + + /* initialize getopt */ + optarg = NULL; + optind = 0; + optopt = 0; + + while( (ch = getopt_long(argc, argv, "hv", options, NULL)) != EOF ) + { + switch(ch) + { + case 'h': + g_print ( + _("%s: A gnomified 'Hello World' program\n\n" + "Usage: %s [--help] [--version]\n\n" + "Options:\n" + " --help display this help and exit\n" + " --version output version information and exit\n"), + argv[0], argv[0]); + exit(0); + break; + case 'v': + g_print (_("NetHack %s.\n"), VERSION_STRING); + exit(0); + break; + case ':': + case '?': + g_print (_("Options error\n")); + exit(0); + break; + } + } + + /* SM stuff */ + session_id = gnome_client_new (); +#if 0 + session_id = gnome_client_new ( + /* callback to save the state and parameter for it */ + save_state, argv[0], + /* callback to die and parameter for it */ + NULL, NULL, + /* id from the previous session if restarted, NULL otherwise */ + id); +#endif + /* set the program name */ + gnome_client_set_program (session_id, argv[0]); + g_free(id); + + return; +} + +/* + * [ALI] Gnome installs its own handler(s) for SIGBUS, SIGFPE and SIGSEGV. + * These handlers will fork and exec a helper program. When that helper + * comes to initialize GTK+, it may fail if setuid/setgid. We solve this + * by dropping privileges before passing the signal along the chain. + * Note: We don't need to either drop or mask the saved ID since this + * will be reset when the child process performs the execve() anyway. + */ + +static struct { + int signum; + void (*handler)(int); +} ghack_chain[] = { + {SIGBUS}, + {SIGFPE}, + {SIGSEGV}, + {SIGILL} /* Not currently handled by Gnome */ +}; + +static void ghack_sig_handler(int signum) +{ + int i; + uid_t uid, euid; + gid_t gid, egid; + uid = getuid(); + euid = geteuid(); + gid = getgid(); + egid = getegid(); + if (gid != egid) + setgid(gid); + if (uid != euid) + setuid(uid); + for(i = SIZE(ghack_chain) - 1; i >= 0; i--) + if (ghack_chain[i].signum == signum) { + ghack_chain[i].handler(signum); + break; + } + if (i < 0) + impossible("Unhandled ghack signal"); + if (uid != euid) + setuid(euid); + if (gid != egid) + setgid(egid); +} + +/* initialize gnome and fir up the main window */ +void ghack_init_main_window( int argc, char** argv) +{ + int i; + struct timeval tv; + uid_t uid, euid; + + /* It seems that the authors of gnome_score_init() drop group + * priveledges. We need group priveledges, so until we change the + * way we save games to do things the gnome way(???), this stays + * commented out. (after hours of frusteration...) + * -Erik + */ + /* gnome_score_init("gnomehack"); */ + + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + + uid = getuid(); + euid = geteuid(); + if (uid != euid) + setuid(uid); + hide_privileges(TRUE); + /* XXX gnome_init must print nethack options for --help, but does not */ + gnome_init ("nethack", VERSION_STRING, argc, argv); + hide_privileges(FALSE); + parse_args (argc, argv); + + /* Initialize the i18n stuff (not that gnomehack supperts it yet...) */ +#if 0 + textdomain (PACKAGE); +#endif + gdk_imlib_init(); + + /* Main window */ + mainWindow = gnome_app_new((char *) "nethack", + (char *) N_("Nethack for Gnome")); + gtk_widget_realize(mainWindow); + if (restarted) { + gtk_widget_set_uposition (mainWindow, os_x, os_y); + gtk_widget_set_usize (mainWindow, os_w, os_h); + } + gtk_window_set_default_size( GTK_WINDOW(mainWindow), 800, 600); + gtk_window_set_policy(GTK_WINDOW(mainWindow), FALSE, TRUE, TRUE); + gnome_app_create_menus(GNOME_APP(mainWindow), mainmenu); + gtk_signal_connect(GTK_OBJECT(mainWindow), "key_press_event", + GTK_SIGNAL_FUNC(ghack_main_window_key_press), NULL); + gtk_signal_connect(GTK_OBJECT(mainWindow), "delete_event", + GTK_SIGNAL_FUNC(ghack_quit_game_cb), NULL); + + /* Put some stuff into our main window */ + vBoxMain = gtk_vbox_new (FALSE, 0); + hBoxFirstRow = gtk_hbox_new (FALSE, 0); + + /* pack Boxes into other boxes to produce the right structure */ + gtk_box_pack_start (GTK_BOX (vBoxMain), hBoxFirstRow, FALSE, TRUE, 0); + + /* pack vBoxMain which contains all our widgets into the main window. */ + gnome_app_set_contents(GNOME_APP(mainWindow), vBoxMain); + + /* DONT show the main window yet, due to a Gtk bug that causes it + * to not refresh the window when adding widgets after the window + * has already been shown */ + if (uid != euid) + setuid(euid); + for(i = 0; i < SIZE(ghack_chain); i++) + ghack_chain[i].handler = + signal(ghack_chain[i].signum, ghack_sig_handler); +} + +void ghack_main_window_add_map_window(GtkWidget* win) +{ + GtkWidget *vBox; + + vBox= gtk_vbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (vBox), win, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (vBoxMain), vBox, TRUE, TRUE, 2); + gtk_widget_show_all(vBox); + /* Ok, now show the main window -- now that we have added in + * all the windows (relys on nethack displaying the map window last + * (This is an ugly kludge, BTW) + */ + gtk_widget_show_all(mainWindow); +} + +void +ghack_main_window_add_message_window(GtkWidget* win) +{ + gtk_box_pack_start (GTK_BOX (hBoxFirstRow), win, TRUE, TRUE, 2); + gtk_widget_show_all(win); +} + +void +ghack_main_window_add_status_window(GtkWidget* win) +{ + gtk_box_pack_start (GTK_BOX (hBoxFirstRow), win, FALSE, TRUE, 2); + gtk_widget_show_all(win); +} + +void +ghack_main_window_add_worn_window(GtkWidget* win) +{ + gtk_box_pack_end (GTK_BOX (hBoxFirstRow), win, FALSE, TRUE, 2); + gtk_widget_show_all(win); +} + +void +ghack_main_window_add_text_window(GtkWidget *win) +{ + g_warning("Fixme!!! AddTextWindow is not yet implemented"); +} + +void +ghack_main_window_remove_window(GtkWidget *win) +{ + g_warning("Fixme!!! RemoveWindow is not yet implemented"); +} + +void +ghack_main_window_update_inventory() +{ +/* For now, do very little. Eventually we may allow the inv. window + to stay active. When we do this, we'll need to implement this... + g_warning("Fixme!!! updateInventory is not yet implemented"); +*/ + gnome_display_nhwindow(WIN_WORN, FALSE); +} + +GtkWidget* +ghack_get_main_window() +{ + return( GTK_WIDGET(mainWindow) ); +} diff --git a/win/gnome/gnmain.h b/win/gnome/gnmain.h new file mode 100644 index 0000000..b2ffc3d --- /dev/null +++ b/win/gnome/gnmain.h @@ -0,0 +1,26 @@ +/* SCCS Id: @(#)gnmain.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackMainWindow_h +#define GnomeHackMainWindow_h + +#include +#include + + +void ghack_init_main_window( int argc, char** argv); +void ghack_main_window_add_map_window(GtkWidget* win); +void ghack_main_window_add_message_window(GtkWidget* win); +void ghack_main_window_add_status_window(GtkWidget* win); +void ghack_main_window_add_text_window(GtkWidget *); +void ghack_main_window_add_worn_window(GtkWidget* win); +void ghack_main_window_remove_window(GtkWidget *); +void ghack_main_window_update_inventory(); +void ghack_save_game_cb(GtkWidget *widget, gpointer data); +GtkWidget* ghack_get_main_window(); + + + +#endif /* GnomeHackMainWindow_h */ + diff --git a/win/gnome/gnmap.c b/win/gnome/gnmap.c new file mode 100644 index 0000000..abaf0ce --- /dev/null +++ b/win/gnome/gnmap.c @@ -0,0 +1,619 @@ +/* SCCS Id: @(#)gnmap.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* Copyright (C) 1998 by Anthony Taylor */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnmap.h" +#include "gnglyph.h" +#include "gnsignal.h" +#include "hack.h" + +#ifndef ROWNO +#define ROWNO 21 +#define COLNO 80 +#endif + +/* globals static to this file go here */ +struct { + GnomeCanvas *canvas; + GnomeCanvasImage *map[(ROWNO + 1) * COLNO]; + GnomeCanvasImage *overlay[(ROWNO + 1) * COLNO]; + double zoom; + GtkWidget *frame; +} ghack_map; + +static GdkImlibImage *background; +static GdkImlibImage *petmark; +static GnomeCanvasGroup *myCanvasGroup; + +/* static function declarations -- local to this file go here */ +void ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data); +void ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data); +void ghack_map_print_glyph( GtkObject *win, guint x, guint y, GdkImlibImage *im, gpointer data); +void ghack_map_clear( GtkWidget *win, gpointer data); +static void ghack_map_display( GtkWidget *win, boolean block, gpointer data); +static void ghack_map_cliparound( GtkWidget *win, int x, int y, gpointer data); +static void ghack_map_window_zoom( GtkAdjustment *adj, gpointer data); + + +/* The following XPM is the artwork of Warwick Allison + * . It has been borrowed from + * the most excellent NetHackQt, until such time as + * we can come up with something better. + * + * More information about NetHackQt can be had from: + * http://www.troll.no/~warwick/nethack/ + */ + +/* XPM */ +static char *pet_mark_xpm[] = { +/* width height ncolors chars_per_pixel */ +"8 7 2 1", +/* colors */ +". c None", +" c #FF0000", +/* pixels */ +"........", +".. . .", +". ", +". ", +".. .", +"... ..", +".... ..." +}; + + +/* NAME: + * ghack_init_map_window( ) + * + * ARGUMENTS: + * NONE + * + * RETURNS: + * GtkWidget* + * + * PURPOSE: + * Create the basic map necessities. Create a canvas; + * give it a background. Attach all the right signals + * to all the right places. Generally prepare the map + * to behave properly. +*/ + +GtkWidget* +ghack_init_map_window ( ) +{ + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *table; + GtkWidget *frame; + GtkWidget *w; + GtkWidget *hSeparator; + GtkAdjustment *adj; + GnomeCanvasImage *bg; + double width, height, x, y; + int i; + + width = COLNO * ghack_glyph_width(); + height = ROWNO * ghack_glyph_height(); + + vbox = gtk_vbox_new (FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + gtk_widget_show (vbox); + + /* Add in a horiz seperator */ + hSeparator = gtk_hseparator_new (); + gtk_box_pack_start (GTK_BOX (vbox), hSeparator, FALSE, FALSE, 2); + gtk_widget_show ( hSeparator); + + hbox = gtk_hbox_new (FALSE, 4); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + /* Create the Zoom spinbutton. + */ + ghack_map.zoom = 1.0; + w = gtk_label_new ("Zoom:"); + gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); + gtk_widget_show (w); + adj = GTK_ADJUSTMENT (gtk_adjustment_new (1.00, 0.5, 3.00, 0.05, 0.50, 0.50)); + w = gtk_spin_button_new (adj, 0.5, 2); + gtk_widget_set_usize (w, 50, 0); + gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0); + gtk_widget_show (w); + + /* Canvas and scrollbars + */ + gtk_widget_push_visual (gdk_imlib_get_visual ()); + gtk_widget_push_colormap (gdk_imlib_get_colormap ()); + ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new()); + //gtk_widget_push_visual(gdk_rgb_get_visual()); + //gtk_widget_push_colormap(gdk_rgb_get_cmap()); + //ghack_map.canvas = GNOME_CANVAS (gnome_canvas_new_aa()); + + gtk_widget_pop_colormap(); + gtk_widget_pop_visual(); + gtk_widget_show (GTK_WIDGET(ghack_map.canvas)); + + table = gtk_table_new (2, 2, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 4); + gtk_table_set_col_spacings (GTK_TABLE (table), 4); + gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0); + gtk_widget_show (table); + + frame = gtk_frame_new (NULL); + ghack_map.frame = frame; + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_table_attach (GTK_TABLE (table), frame, + 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, + 0, 0); + gtk_widget_show (frame); + + gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET(ghack_map.canvas)); + gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0, + width+2*ghack_glyph_width(), height+2*ghack_glyph_height()); + + gnome_canvas_set_pixels_per_unit (GNOME_CANVAS(ghack_map.canvas), 1.0); + + w = gtk_hscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->hadjustment); + gtk_table_attach (GTK_TABLE (table), w, + 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, + GTK_FILL, + 0, 0); + gtk_widget_show (w); + + w = gtk_vscrollbar_new (GTK_LAYOUT (ghack_map.canvas)->vadjustment); + gtk_table_attach (GTK_TABLE (table), w, + 1, 2, 0, 1, GTK_FILL, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, + 0, 0); + gtk_widget_show (w); + + myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new ( + gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)), + gnome_canvas_group_get_type (), + "x", 0.0, + "y", 0.0, + NULL) ); + + /* Tile the map background with a pretty image */ + background = gdk_imlib_load_image((char *) "mapbg.xpm"); + if (background == NULL) { + g_warning("Bummer! Failed to load the map background image (mapbg.xpm)!"); + } + else { + gdk_imlib_render(background, background->rgb_width, + background->rgb_height); + + /* Tile the map background */ + for (y = 0; y < height+background->rgb_height; y+=background->rgb_height) + { + for (x = 0; x < width+background->rgb_width; x+=background->rgb_width) + { + bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new ( + myCanvasGroup, gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) background->rgb_width, + "height", (double) background->rgb_height, + "image", background, + "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER, + NULL) ); + gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg)); + } + } + } + + /* ghack_map.map is an array of canvas images. Each cell of + * the array will contain one tile. Here, we create the + * space for the cells and then create the cells for easy + * access later. + */ + for (i=0, y = 0; y < height; y+=ghack_glyph_height()) + { + for (x = 0; x < width; x+=ghack_glyph_width()) + { + ghack_map.map[i++] = GNOME_CANVAS_IMAGE( + gnome_canvas_item_new ( + myCanvasGroup, + gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) ghack_glyph_width(), + "height", (double) ghack_glyph_height(), + "anchor", GTK_ANCHOR_NORTH_WEST, + NULL) ); + } + } + + /* Set up the pet mark image */ + petmark = gdk_imlib_create_image_from_xpm_data( pet_mark_xpm); + if (petmark == NULL) { + g_warning("Bummer! Failed to load the pet_mark image!"); + } + else { + gdk_imlib_render(petmark, petmark->rgb_width, petmark->rgb_height); + + /* ghack_map.overlay is an array of canvas images used to + * overlay tile images... + */ + for (i=0, y = 0; y < height; y+=ghack_glyph_height()) + { + for (x = 0; x < width; x+=ghack_glyph_width()) + { + ghack_map.overlay[i] = GNOME_CANVAS_IMAGE( + gnome_canvas_item_new ( + myCanvasGroup, + gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) petmark->rgb_width, + "height", (double) petmark->rgb_height, + "image", petmark, + "anchor", GTK_ANCHOR_NORTH_WEST, + NULL) ); + gnome_canvas_item_lower_to_bottom ( + GNOME_CANVAS_ITEM( ghack_map.overlay[i++])); + } + } + } + + /* Resize the canvas when the spinbutton changes + */ + gtk_signal_connect (GTK_OBJECT (adj), + "value_changed", + (GtkSignalFunc) ghack_map_window_zoom, + ghack_map.canvas); + + /* Game signals + */ + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_curs", + GTK_SIGNAL_FUNC (ghack_map_cursor_to), + NULL); + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_putstr", + GTK_SIGNAL_FUNC (ghack_map_putstr), + NULL); + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_print_glyph", + GTK_SIGNAL_FUNC (ghack_map_print_glyph), + NULL); + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_clear", + GTK_SIGNAL_FUNC (ghack_map_clear), + NULL); + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_display", + GTK_SIGNAL_FUNC (ghack_map_display), + NULL); + gtk_signal_connect (GTK_OBJECT (vbox), + "ghack_cliparound", + GTK_SIGNAL_FUNC (ghack_map_cliparound), + NULL); + gtk_signal_connect (GTK_OBJECT (ghack_map.canvas), + "button_press_event", + GTK_SIGNAL_FUNC (ghack_handle_button_press), + NULL); + gtk_signal_connect(GTK_OBJECT (ghack_map.canvas), + "gnome_delay_output", + GTK_SIGNAL_FUNC(ghack_delay), + NULL); + + return GTK_WIDGET(vbox); +} + + +/* NAME: + * ghack_map_window_zoom + * + * ARGUMENTS: + * double zoom -- The zoom factor + * + * RETURNS: + * Nothing. + * + * PURPOSE: + * Zoom the map image in and out. This should allow the user to + * dynamically scale the map. Ideally, the background should + * *NOT* scale, but this may be impractical. +*/ + +static void +ghack_map_window_zoom( GtkAdjustment *adj, gpointer data) +{ + if ( adj->value > 3.0 ) + adj->value = 3.0; + if ( adj->value < 0.5 ) + adj->value = 0.5; + ghack_map.zoom = adj->value; + gnome_canvas_set_pixels_per_unit (data, adj->value); +} + + + +void +ghack_map_cursor_to( GtkWidget *win, int x, int y, gpointer data) +{ + GnomeCanvasGroup *group; + static GnomeCanvasRE *cursor = NULL; + + double x1, y1, x2, y2; + float hp; + guint r, g, b; + + x1 = x * ghack_glyph_width() - 1; + y1 = y * ghack_glyph_height() - 1; + x2 = x1 + ghack_glyph_width() + 2; + y2 = y1 + ghack_glyph_height() + 2; + hp = u.mtimedone + ? (u.mhmax ? (float)u.mh/u.mhmax : 1) + : (u.uhpmax ? (float)u.uhp/u.uhpmax : 1); + + r = 255; + g = (hp >= 0.75) ? 255 : (hp >= 0.25 ? 255*2*(hp-0.25) : 0); + b = (hp >= 0.75) ? 255*4*(hp-0.75) : (hp >= 0.25 ? 0 : 255*4*(0.25-hp)); + + group = gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)); + + if (!cursor) { + cursor = GNOME_CANVAS_RE (gnome_canvas_item_new (group, + gnome_canvas_rect_get_type (), + "width_units", 1.0, NULL)); + } + gnome_canvas_item_set (GNOME_CANVAS_ITEM (cursor), + "outline_color_rgba", GNOME_CANVAS_COLOR(r, g, b), + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + NULL); + + gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( cursor)); + gnome_canvas_item_show( GNOME_CANVAS_ITEM(cursor)); +} + + +void +ghack_map_putstr( GtkWidget *win, int attr, const char* text, gpointer data) +{ + g_warning("Fixme!!! ghack_map_putstr is not implemented"); +} + +/* NAME: + * ghack_map_print_glyph( ) + * + * ARGUMENTS: + * XCHAR_P x, y -- The coordinates where which to print the glyph + * GdkImlibImage* glyph -- The glyph image to print + * + * RETURNS: + * Nothing. + * + * PURPOSE: + * Draw the glyph-tile at the specified coordinates. +*/ + +void +ghack_map_print_glyph( GtkObject *win, + guint x, + guint y, + GdkImlibImage *im, + gpointer data) +{ + GnomeCanvasGroup *group; + int i = y * COLNO + x; + int glyph = glyph_at(x,y); + GnomeCanvasImage *canvas_image = GNOME_CANVAS_IMAGE( ghack_map.map[i]); + + group = gnome_canvas_root (GNOME_CANVAS (ghack_map.canvas)); + + gnome_canvas_item_set (GNOME_CANVAS_ITEM ( canvas_image), + "image", im, NULL); + gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image)); + + canvas_image = GNOME_CANVAS_IMAGE( ghack_map.overlay[i]); + + if (x==u.ux && y==u.uy) + ghack_map_cliparound(NULL, x, y, NULL); + + if (glyph_is_pet(glyph) +#ifdef TEXTCOLOR + && iflags.hilite_pet +#endif + ) { + gnome_canvas_item_raise_to_top( GNOME_CANVAS_ITEM( canvas_image)); + gnome_canvas_item_show( GNOME_CANVAS_ITEM( canvas_image)); + } + else { + gnome_canvas_item_hide( GNOME_CANVAS_ITEM( canvas_image)); + } +} + + +/* NAME: + * ghack_map_clear( ) + * + * ARGUMENTS: + * NONE + * + * RETURNS: + * Nothing. + * + * PURPOSE: + * Clear the map by hiding all the map tiles. +*/ + +void +ghack_map_clear( GtkWidget *win, gpointer data) +{ + int i; + + for (i = 0; i < ROWNO * COLNO; i++) + { + if (GNOME_IS_CANVAS_IMAGE(ghack_map.map[i])) + { + gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.map[i])); + } + if (GNOME_IS_CANVAS_IMAGE(ghack_map.overlay[i])) + { + gnome_canvas_item_hide( GNOME_CANVAS_ITEM (ghack_map.overlay[i])); + } + } + gnome_canvas_update_now ( GNOME_CANVAS(ghack_map.canvas)); +} + + +void +ghack_map_display( GtkWidget *win, boolean block, gpointer data) +{ + gtk_widget_show_all( GTK_WIDGET(win)); +} + + +void +ghack_map_cliparound( GtkWidget *win, + int x, + int y, + gpointer data) +{ + int map_width, map_height; + int to_x, to_y; + int cur_x, cur_y; + int width, height, half_width, half_height; + + x *= ghack_glyph_width() * ghack_map.zoom; + y *= ghack_glyph_height() * ghack_map.zoom; + map_width = COLNO * ghack_glyph_width() * ghack_map.zoom; + map_height = ROWNO * ghack_glyph_height() * ghack_map.zoom; + + gdk_window_get_size( GTK_LAYOUT (ghack_map.canvas)->bin_window, + &width, &height); + gnome_canvas_get_scroll_offsets( ghack_map.canvas, &cur_x, &cur_y); + + half_width = width * 0.5; + half_height = height * 0.5; + + if ( ((x - cur_x) < (width * 0.25) ) || ( (x - cur_x) > (width * 0.75) ) ) { + to_x = ((x-half_width) > 0)? x - half_width : 0; + to_x = ((x+half_width) > map_width)? map_width - 2 * half_width : to_x; + } + else { + to_x = cur_x; + } + + if ( ((y - cur_y) < (height * 0.25) ) || ( (y - cur_y) > (height * 0.75) ) ) { + to_y = ((y-half_height) > 0)? y - half_height : 0; + to_y = ((y+half_height) > map_height)? map_height - 2 * half_height : to_y; + } + else { + to_y = cur_y; + } + + if (to_x != cur_x || to_y != cur_y) + gnome_canvas_scroll_to( ghack_map.canvas, to_x, to_y); + //gnome_canvas_update_now ( ghack_map.canvas); + +} + + + +void +ghack_reinit_map_window ( ) +{ + GnomeCanvasImage *bg; + double width, height, x, y; + int i; + + /* ghack_map_clear(NULL, NULL); */ + + width = COLNO * ghack_glyph_width(); + height = ROWNO * ghack_glyph_height(); + + gnome_canvas_set_scroll_region (GNOME_CANVAS(ghack_map.canvas), 0, 0, + width+2*ghack_glyph_width(), height+2*ghack_glyph_height()); + + /* remove everything currently in the canvas map */ + gtk_object_destroy( GTK_OBJECT (myCanvasGroup)); + + /* Put some groups back */ + myCanvasGroup = GNOME_CANVAS_GROUP ( gnome_canvas_item_new ( + gnome_canvas_root(GNOME_CANVAS(ghack_map.canvas)), + gnome_canvas_group_get_type (), + "x", 0.0, + "y", 0.0, + NULL) ); + + /* Tile the map background with a pretty image */ + if (background != NULL) { + /* Tile the map background */ + for (y = 0; y < height+background->rgb_height; y+=background->rgb_height) + { + for (x = 0; x < width+background->rgb_width; x+=background->rgb_width) + { + bg = GNOME_CANVAS_IMAGE( gnome_canvas_item_new ( + myCanvasGroup, gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) background->rgb_width, + "height", (double) background->rgb_height, + "image", background, + "anchor", (GtkAnchorType) GTK_ANCHOR_CENTER, + NULL) ); + gnome_canvas_item_lower_to_bottom (GNOME_CANVAS_ITEM( bg)); + } + } + } + + /* ghack_map.map is an array of canvas images. Each cell of + * the array will contain one tile. Here, we create the + * space for the cells and then create the cells for easy + * access later. + */ + for (i=0, y = 0; y < height; y+=ghack_glyph_height()) { + for (x = 0; x < width; x+=ghack_glyph_width()) { + ghack_map.map[i++] = GNOME_CANVAS_IMAGE( + gnome_canvas_item_new ( + myCanvasGroup, + gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) ghack_glyph_width(), + "height", (double) ghack_glyph_height(), + "anchor", GTK_ANCHOR_NORTH_WEST, + NULL) ); + } + } + + if (petmark != NULL) { + /* ghack_map.overlay is an array of canvas images used to + * overlay tile images... + */ + for (i=0, y = 0; y < height; y+=ghack_glyph_height()) { + for (x = 0; x < width; x+=ghack_glyph_width()) { + ghack_map.overlay[i] = GNOME_CANVAS_IMAGE( + gnome_canvas_item_new ( + myCanvasGroup, + gnome_canvas_image_get_type (), + "x", (double) x, + "y", (double) y, + "width", (double) petmark->rgb_width, + "height", (double) petmark->rgb_height, + "image", petmark, + "anchor", GTK_ANCHOR_NORTH_WEST, + NULL) ); + gnome_canvas_item_lower_to_bottom ( + GNOME_CANVAS_ITEM( ghack_map.overlay[i++])); + } + } + } + + ghack_map_cliparound(NULL, u.ux, u.uy, NULL); + ghack_map_cursor_to(NULL, u.ux, u.uy, NULL); + gnome_canvas_update_now ( ghack_map.canvas); + doredraw(); +} + + diff --git a/win/gnome/gnmap.h b/win/gnome/gnmap.h new file mode 100644 index 0000000..83bc849 --- /dev/null +++ b/win/gnome/gnmap.h @@ -0,0 +1,16 @@ +/* SCCS Id: @(#)gnmap.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackMapWindow_h +#define GnomeHackMapWindow_h + +#include +#include +#include "config.h" +#include "global.h" + +GtkWidget *ghack_init_map_window(void); +void ghack_reinit_map_window(void); + +#endif /* GnomeHackMapWindow_h */ diff --git a/win/gnome/gnmenu.c b/win/gnome/gnmenu.c new file mode 100644 index 0000000..d3fa6da --- /dev/null +++ b/win/gnome/gnmenu.c @@ -0,0 +1,755 @@ +/* SCCS Id: @(#)gnmenu.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include +#include "gnmenu.h" +#include "gnmain.h" +#include "gnbind.h" +#include "func_tab.h" + +typedef enum { + MenuUnknown = 0, + MenuText, + MenuMenu +} MenuWinType; + +typedef struct { + ANY_P identifier; + gchar accelerator[BUFSZ]; + int itemNumber; + int selected; +} menuItem; + +typedef struct { + int curItem; + int numRows; + int charIdx; + guint32 lastTime; +} extMenu; + +static GdkColor color_blue = { 0, 0, 0, 0xffff }; + + +static void +ghack_menu_window_key(GtkWidget *menuWin, GdkEventKey *event, gpointer data) +{ + int i, numRows; + menuItem* item; + MenuWinType isMenu; + + isMenu = (MenuWinType) GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT (menuWin), "isMenu")); + + if (isMenu == MenuMenu) { + GtkWidget *clist; + gint selection_mode; + + clist = GTK_WIDGET(gtk_object_get_data (GTK_OBJECT (menuWin), "clist")); + g_assert (clist != NULL); + numRows = GPOINTER_TO_INT + (gtk_object_get_data(GTK_OBJECT(clist), "numRows")); + selection_mode = GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT(clist), "selection_mode")); + for (i = 0; i <= numRows; ++i) { + item = (menuItem*) gtk_clist_get_row_data(GTK_CLIST(clist), i); + if (item == NULL) continue; + if (!strcmp(item->accelerator, "")) continue; + + if ((!strcmp(item->accelerator, event->string)) || + ((selection_mode == GTK_SELECTION_MULTIPLE) && + (event->keyval == ','))) { + if (item->selected) { + gtk_clist_unselect_row( GTK_CLIST (clist), + item->itemNumber, 0); + item->selected = FALSE; + } else { + gtk_clist_select_row(GTK_CLIST (clist), + item->itemNumber, 0); + if (gtk_clist_row_is_visible(GTK_CLIST(clist), + item->itemNumber) != GTK_VISIBILITY_FULL) + gtk_clist_moveto(GTK_CLIST(clist), + item->itemNumber, 0, 0.5, 0); + item->selected = TRUE; + } + } + } + } +} + + +static void +ghack_menu_row_selected (GtkCList *clist, int row, int col, GdkEvent *event) +{ + /* FIXME: Do something */ +} + + +void +ghack_menu_window_clear(GtkWidget *menuWin, gpointer data) +{ + MenuWinType isMenu; + int i, numRows; + menuItem* item; + + isMenu = (MenuWinType) GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT (menuWin), "isMenu")); + + if (isMenu == MenuMenu) { + GtkWidget *clist; + + clist = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (menuWin), "clist")); + g_assert (clist != NULL); + + /* destroy existing menu data, if any */ + if (clist) { + /* destroy all the row_data we stored in the clist */ + numRows = GPOINTER_TO_INT( gtk_object_get_data( + GTK_OBJECT(clist), "numRows") ); + for( i=0; itext), 0, 0); + } + +} + +void +ghack_menu_window_display(GtkWidget *menuWin, gboolean blocking, + gpointer data) +{ + //if(blocking) { + gnome_dialog_close_hides (GNOME_DIALOG (menuWin), TRUE); + gnome_dialog_set_close (GNOME_DIALOG (menuWin), TRUE); + gnome_dialog_run_and_close(GNOME_DIALOG (menuWin)); + //} + //else { + //gtk_widget_show(menuWin); + //} +} + +gint +ghack_menu_hide( GtkWidget *menuWin, GdkEvent *event, gpointer data ) +{ + gtk_widget_hide (menuWin); + return FALSE; /* FIXME: what is correct result here? */ +} + + +void +ghack_menu_window_start_menu (GtkWidget *menuWin, gpointer data) +{ + GtkWidget *frame1, *swin, *clist; + MenuWinType isMenu; + + g_assert (menuWin != NULL); + g_assert (data == NULL); + + /* destroy existing menu data, if any */ + frame1 = gtk_object_get_data (GTK_OBJECT (menuWin), "frame1"); + if (frame1) + gtk_widget_destroy (frame1); + + isMenu = MenuMenu; + gtk_object_set_data (GTK_OBJECT (menuWin), "isMenu", + GINT_TO_POINTER (isMenu)); + + gtk_widget_set_usize (GTK_WIDGET (menuWin), 500, 400); + gtk_window_set_policy (GTK_WINDOW (menuWin), TRUE, TRUE, FALSE); + + frame1 = gtk_frame_new ("Make your selection"); + g_assert (frame1 != NULL); + gtk_object_set_data (GTK_OBJECT(menuWin), "frame1", frame1); + gtk_widget_show (GTK_WIDGET (frame1)); + gtk_container_set_border_width (GTK_CONTAINER (frame1), 5); + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG(menuWin)->vbox), frame1, + TRUE, TRUE, 0); + + swin = gtk_scrolled_window_new (NULL, NULL); + g_assert (swin != NULL); + gtk_object_set_data (GTK_OBJECT(menuWin), "swin", swin); + gtk_widget_show (GTK_WIDGET (swin)); + gtk_container_add (GTK_CONTAINER (frame1), swin); + + clist = gtk_clist_new (4); + g_assert (clist != NULL); + gtk_object_set_data (GTK_OBJECT(menuWin), "clist", clist); + gtk_widget_show (GTK_WIDGET (clist)); + gtk_container_add (GTK_CONTAINER (swin), clist); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (ghack_menu_row_selected), NULL); + gtk_object_set_data (GTK_OBJECT (clist), "numItems", + GINT_TO_POINTER (-1)); +} + + +int +ghack_menu_window_select_menu (GtkWidget *menuWin, + MENU_ITEM_P **_selected, gint how) +{ + gint rc; + guint num_sel, i, idx; + GtkWidget *clist; + GList *cur; + MENU_ITEM_P *selected = NULL; + menuItem* item; + + g_assert (_selected != NULL); + *_selected = NULL; + + + if (how == PICK_NONE) { + gnome_dialog_close_hides (GNOME_DIALOG (menuWin), TRUE); + rc = gnome_dialog_run_and_close (GNOME_DIALOG (menuWin)); + return( rc == 1 ? -1 : 0); + } + + clist = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (menuWin), "clist")); + g_assert (clist != NULL); + + gtk_object_set_data (GTK_OBJECT (clist), "selection_mode", + GINT_TO_POINTER ((how == PICK_ANY)? + GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE)); + gtk_clist_set_selection_mode (GTK_CLIST (clist), + (how == PICK_ANY)? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE); + gnome_dialog_close_hides (GNOME_DIALOG (menuWin), TRUE); + rc = gnome_dialog_run_and_close (GNOME_DIALOG (menuWin)); + if ((rc == 1) || (GTK_CLIST (clist)->selection == NULL)) { + return(-1); + } + + num_sel = g_list_length (GTK_CLIST (clist)->selection); + if (num_sel < 1) { + return(-1); + } + + /* fill in array with selections from clist */ + selected = g_new0( MENU_ITEM_P, num_sel); + g_assert (selected != NULL); + cur = GTK_CLIST (clist)->selection; + i = 0; + while (cur) { + g_assert (i < num_sel); + + /* grab row number from clist selection list */ + idx = GPOINTER_TO_INT (cur->data); + + item = (menuItem*) gtk_clist_get_row_data( GTK_CLIST (clist), idx); + selected[i].item = item->identifier; + selected[i].count = -1; + cur = g_list_next(cur); + i++; + } + + *_selected = selected; + + return( (int) num_sel); +} + +void +ghack_menu_window_add_menu( GtkWidget *menuWin, gpointer menu_item, + gpointer data) +{ + GHackMenuItem* item; + GtkWidget *clist; + gchar buf[BUFSZ]="", accelBuf[BUFSZ]=""; + gchar *pbuf; + char *text[4] = { buf, NULL, NULL, NULL }; + gint nCurrentRow = -1, numItems = -1; + MenuWinType isMenu; + GtkStyle *bigStyle = NULL; + gboolean item_selectable; + GdkImlibImage* image; + static gboolean special; + + g_assert (menu_item != NULL); + item = (GHackMenuItem*) menu_item; + item_selectable = ( item->identifier->a_int == 0)? FALSE : TRUE; + isMenu = (MenuWinType) GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT (menuWin), "isMenu")); + + clist = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (menuWin), "clist")); + g_assert (clist != NULL); + /* This is a special kludge to make the special hidden help menu item work as designed */ + if ( special==TRUE ) { + special=FALSE; + item_selectable=TRUE; + } + if ( ! strcmp( item->str, "The NetHack license.")) { + special=TRUE; + } + + if (item->str) { + + /* First, make a new blank entry in the clist */ + nCurrentRow = gtk_clist_append (GTK_CLIST (clist), text); + + if (item->glyph != NO_GLYPH) { + image = ghack_image_from_glyph( item->glyph, FALSE); + if (image==NULL || image->pixmap==NULL) { + g_warning("Bummer -- having to force rendering for glyph %d!", item->glyph); + /* wierd -- pixmap is NULL so retry rendering it */ + image = ghack_image_from_glyph( item->glyph, TRUE); + } + if (image==NULL || image->pixmap==NULL) { + g_error("Aiiee! glyph is still NULL for item\n\"%s\"", + item->str); + } + else + gtk_clist_set_pixmap (GTK_CLIST (clist), + nCurrentRow, 1, + gdk_imlib_move_image( image), + gdk_imlib_move_mask( image)); + } + if (item->accelerator) { + /* FIXME: handle accelerator, */ + g_snprintf(accelBuf, sizeof(accelBuf), "%c ", item->accelerator); + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 0, accelBuf); + g_snprintf(buf, sizeof(buf), "%s", item->str); + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 2, buf); + } else { + if (item->group_accel) { + /* FIXME: maybe some day I should try to handle + * group accelerators... */ + } + if (( (item->attr == 0) && (item->identifier->a_int != 0)) || (special ==TRUE) ) { + numItems = GPOINTER_TO_INT( gtk_object_get_data( + GTK_OBJECT(clist), "numItems") )+1; + + /* Ok, now invent a unique accelerator */ + if ( ('a'+numItems) <= 'z' ) { + g_snprintf(accelBuf, sizeof(accelBuf), "%c ", 'a'+numItems); + gtk_clist_set_text(GTK_CLIST(clist), nCurrentRow, 0, accelBuf); + } + else if ( ('A'+numItems-26)<='Z') { + g_snprintf(accelBuf, sizeof(accelBuf), "%c ", 'A'+numItems-26); + gtk_clist_set_text(GTK_CLIST(clist), nCurrentRow, 0, accelBuf); + } else { + accelBuf[0] = buf[0] = 0; + } + g_snprintf(buf, sizeof(buf), "%s", item->str); + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 2, buf); + gtk_object_set_data (GTK_OBJECT (clist), "numItems", + GINT_TO_POINTER (numItems)); + + /* This junk is to specially handle the options menu */ + pbuf = strstr( buf, " ["); + if (pbuf == NULL) { + pbuf = strstr( buf, "\t["); + } + if (pbuf != NULL) { + *pbuf=0; + pbuf++; + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 3, pbuf); + } + } + /* FIXME: handle more than 26*2 accelerators (but how? + * since I only have so many keys to work with???) + else + { + foo(); + } + */ + else { + g_snprintf(buf, sizeof(buf), "%s", item->str); + pbuf = strstr( buf, " ["); + if (pbuf == NULL) { + pbuf = strstr( buf, "\t["); + } + if (pbuf != NULL) { + *pbuf=0; + pbuf++; + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 3, pbuf); + } + gtk_clist_set_text (GTK_CLIST (clist), nCurrentRow, 2, buf); + + } + } + + if (item->attr) { + switch(item->attr) { + case ATR_ULINE: + case ATR_BOLD: + case ATR_BLINK: + case ATR_INVERSE: + bigStyle = gtk_style_copy (GTK_WIDGET (clist)->style); + g_assert (bigStyle != NULL); + gdk_font_unref (bigStyle->font); + bigStyle->font = gdk_font_load ( + "-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"); + bigStyle->fg[GTK_STATE_NORMAL] = color_blue; + gtk_clist_set_cell_style (GTK_CLIST (clist), + nCurrentRow, 2, bigStyle); + item_selectable = FALSE; + } + } + + + g_assert (nCurrentRow >= 0); + gtk_clist_set_selectable (GTK_CLIST (clist), nCurrentRow, + item_selectable); + + if ( item_selectable==TRUE && item->presel== TRUE) { + /* pre-select this item */ + gtk_clist_select_row( GTK_CLIST (clist), nCurrentRow, 0); + } + + gtk_object_set_data (GTK_OBJECT (clist), "numRows", + GINT_TO_POINTER (nCurrentRow)); + + /* We have to allocate memory here, since the menu_item currently + * lives on the stack, and will otherwise go to the great bit bucket + * in the sky as soon as this function exits, which would leave a + * pointer to crap in the row_data. Use g_memdup to make a private, + * persistant copy of the item identifier. + * + * We need to arrange to blow away this memory somewhere (like + * ghack_menu_destroy and ghack_menu_window_clear for example). + * + * -Erik + */ + { + menuItem newItem; + menuItem *pNewItem; + + newItem.identifier = *item->identifier; + newItem.itemNumber=nCurrentRow; + newItem.selected=FALSE; + newItem.accelerator[0]=0; + /* only copy 1 char, since accel keys are by definition 1 char */ + if (accelBuf[0]) { + strncpy(newItem.accelerator, accelBuf, 1); + } + newItem.accelerator[1]=0; + + pNewItem = g_memdup(&newItem, sizeof( menuItem)); + gtk_clist_set_row_data (GTK_CLIST (clist), nCurrentRow, + (gpointer) pNewItem); + } + } + /* Now adjust the column widths to match the contents */ + gtk_clist_columns_autosize (GTK_CLIST (clist)); +} + +void +ghack_menu_window_end_menu (GtkWidget *menuWin, gpointer data) +{ + const char* p = (const char*) data; + + if ((p) && (*p)) { + GtkWidget *frame1 = gtk_object_get_data (GTK_OBJECT (menuWin), "frame1"); + g_assert (frame1 != NULL); + + gtk_frame_set_label (GTK_FRAME(frame1), p); + } + +} + + +void ghack_menu_window_put_string(GtkWidget *menuWin, int attr, + const char* text, gpointer data) +{ + GnomeLess *gless; + MenuWinType isMenu; + + if (text == NULL) + return; + + isMenu = (MenuWinType) GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT (menuWin), "isMenu")); + + if (isMenu == MenuText) { + gless = GNOME_LESS (gtk_object_get_data (GTK_OBJECT (menuWin), "gless")); + g_assert (gless != NULL); + g_assert (gless->text != NULL); + g_assert (GTK_IS_TEXT (gless->text)); + + /* Don't bother with attributes yet */ + gtk_text_insert (GTK_TEXT (gless->text), NULL, NULL, NULL, text, -1); + gtk_text_insert (GTK_TEXT (gless->text), NULL, NULL, NULL, "\n", -1); + + } + + else if (isMenu == MenuUnknown) { + isMenu = MenuText; + gtk_object_set_data (GTK_OBJECT (menuWin), "isMenu", + GINT_TO_POINTER (isMenu)); + + gtk_widget_set_usize (GTK_WIDGET (menuWin), 500, 400); + gtk_window_set_policy (GTK_WINDOW (menuWin), TRUE, TRUE, FALSE); + + gless = GNOME_LESS (gnome_less_new ()); + g_assert (gless != NULL); + gtk_object_set_data (GTK_OBJECT (menuWin), "gless", gless); + gtk_widget_show (GTK_WIDGET (gless)); + + gnome_less_show_string (gless, text); + gtk_text_insert (GTK_TEXT (gless->text), NULL, NULL, NULL, "\n", -1); + + gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (menuWin)->vbox), + GTK_WIDGET (gless), TRUE, TRUE, 0); + } +} + + +void +ghack_menu_destroy (GtkWidget *menuWin, gpointer data) +{ + MenuWinType isMenu; + + isMenu = (MenuWinType) GPOINTER_TO_INT + (gtk_object_get_data (GTK_OBJECT (menuWin), "isMenu")); + + if (isMenu == MenuText) { + GnomeLess *gless; + + gless = GNOME_LESS (gtk_object_get_data (GTK_OBJECT (menuWin), "gless")); + g_assert (gless != NULL); + g_assert (gless->text != NULL); + g_assert (GTK_IS_TEXT (gless->text)); + gtk_widget_destroy(GTK_WIDGET(gless)); + } + + else if (isMenu == MenuMenu) { + GtkWidget *frame1, *swin, *clist; + + /* destroy existing menu data, if any */ + clist = gtk_object_get_data (GTK_OBJECT (menuWin), "clist"); + if (clist) { + /* destroy all the row_data we stored in the clist */ + int i, numRows; + menuItem* item; + numRows = GPOINTER_TO_INT( gtk_object_get_data( + GTK_OBJECT(clist), "numRows") ); + for( i=0; istring[0]; + + clist = GTK_WIDGET(gtk_object_get_data(GTK_OBJECT(menuWin), "clist")); + g_assert(clist != NULL); + + /* if too long between keystrokes, reset to initial state */ + if (event->time - info->lastTime > 500) goto init_state; + + /* see if current item continue to match */ + if (info->charIdx > 0) { + if (extcmdlist[info->curItem].ef_txt[info->charIdx] == c) { + ++info->charIdx; + goto found; + } + } + + /* see if the prefix matches a later command in the list */ + if (info->curItem >= 0) { + for (i = info->curItem + 1; i < info->numRows; ++i) { + if (!strncmp(extcmdlist[info->curItem].ef_txt, + extcmdlist[i].ef_txt, info->charIdx)) { + if (extcmdlist[i].ef_txt[info->charIdx] == c) { + ++info->charIdx; + info->curItem = i; + goto found; + } + } + } + } + +init_state: + /* reset to initial state, look for matching 1st character */ + for (i = 0; i < info->numRows; ++i) { + if (extcmdlist[i].ef_txt[0] == c) { + info->charIdx = 1; + info->curItem = i; + goto found; + } + } + + /* no match: leave prior, if any selection in place */ + return; + +found: + info->lastTime = event->time; + gtk_clist_select_row(GTK_CLIST(clist), info->curItem, 0); + if (gtk_clist_row_is_visible(GTK_CLIST(clist), + info->curItem) != GTK_VISIBILITY_FULL) + gtk_clist_moveto(GTK_CLIST(clist), info->curItem, 0, 0.5, 0); +} + +int +ghack_menu_ext_cmd(void) +{ + int n; + GtkWidget* dialog; + GtkWidget* swin; + GtkWidget* frame1; + GtkWidget* clist; + extMenu info; + + dialog = gnome_dialog_new("Extended Commands", + GNOME_STOCK_BUTTON_OK, + GNOME_STOCK_BUTTON_CANCEL, + NULL); + gnome_dialog_close_hides(GNOME_DIALOG(dialog), FALSE); + gtk_signal_connect(GTK_OBJECT(dialog), "key_press_event", + GTK_SIGNAL_FUNC(ghack_ext_key_hit), &info); + + frame1 = gtk_frame_new("Make your selection"); + gtk_object_set_data(GTK_OBJECT(dialog), "frame1", frame1); + gtk_widget_show(frame1); + gtk_container_border_width(GTK_CONTAINER(frame1), 3); + + swin = gtk_scrolled_window_new(NULL, NULL); + clist = gtk_clist_new(2); + gtk_object_set_data(GTK_OBJECT(dialog), "clist", clist); + gtk_widget_set_usize(clist, 500, 400); + gtk_container_add(GTK_CONTAINER(swin), clist); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_signal_connect(GTK_OBJECT(clist), "select_row", + GTK_SIGNAL_FUNC(ghack_menu_row_selected), NULL); + + gtk_container_add(GTK_CONTAINER(frame1), swin); + gtk_box_pack_start_defaults(GTK_BOX(GNOME_DIALOG(dialog)->vbox), frame1); + + /* Add the extended commands into the list here... */ + for (n = 0; extcmdlist[n].ef_txt; ++n) { + const char *text[3]={extcmdlist[n].ef_txt,extcmdlist[n].ef_desc,NULL}; + gtk_clist_insert(GTK_CLIST(clist), n, (char**) text); + } + + /* fill in starting info fields */ + info.curItem = -1; + info.numRows = n; + info.charIdx = 0; + info.lastTime = 0; + + gtk_clist_columns_autosize(GTK_CLIST(clist)); + gtk_widget_show_all(swin); + + /* Center the dialog over over parent */ + gnome_dialog_set_default(GNOME_DIALOG(dialog), 0); + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gnome_dialog_set_parent(GNOME_DIALOG(dialog), + GTK_WINDOW(ghack_get_main_window())); + + /* Run the dialog -- returning whichever button was pressed */ + n = gnome_dialog_run_and_close(GNOME_DIALOG(dialog)); + + /* Quit on button 2 or error */ + return (n != 0) ? -1 : info.curItem; +} diff --git a/win/gnome/gnmenu.h b/win/gnome/gnmenu.h new file mode 100644 index 0000000..7055aa4 --- /dev/null +++ b/win/gnome/gnmenu.h @@ -0,0 +1,32 @@ +/* SCCS Id: @(#)gnmenu.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackMenuWindow_h +#define GnomeHackMenuWindow_h + +#include +#include "config.h" +#include "global.h" +#include "gnomeprv.h" + +GtkWidget* ghack_init_menu_window( void ); + +struct _GHackMenuItem +{ + int glyph; + const ANY_P *identifier; + CHAR_P accelerator; + CHAR_P group_accel; + int attr; + const char* str; + BOOLEAN_P presel; +}; + +typedef struct _GHackMenuItem GHackMenuItem; + +int ghack_menu_window_select_menu (GtkWidget *menuWin, + MENU_ITEM_P **_selected, gint how); +int ghack_menu_ext_cmd(void); + +#endif /* GnomeHackMenuWindow_h */ diff --git a/win/gnome/gnmesg.c b/win/gnome/gnmesg.c new file mode 100644 index 0000000..337a21b --- /dev/null +++ b/win/gnome/gnmesg.c @@ -0,0 +1,102 @@ +/* SCCS Id: @(#)gnmesg.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnmesg.h" +#include "gnsignal.h" + +/* Pick an arbitrary number of chars such as 80 col X 40 rows text = 3200 chars */ +#define nCharsBeforeDeletingStuff 3200 + +/* Message Window widgets */ +GtkWidget *MW_table; +GtkWidget *MW_text; +GtkWidget *MW_scrollbar; + + +void ghack_message_window_clear(GtkWidget *widget, gpointer data) +{ + /* Seems nethack calls this after every move -- we don't want + * to really clear the window at all though. Ignore the request */ + gint len; + + len = gtk_text_get_length(GTK_TEXT(MW_text)); + + if(len < nCharsBeforeDeletingStuff) + return; + + gtk_text_freeze(GTK_TEXT(MW_text)); + gtk_text_set_point(GTK_TEXT(MW_text), 0); + gtk_text_forward_delete(GTK_TEXT(MW_text), len-((guint)(nCharsBeforeDeletingStuff*0.5))); + gtk_text_set_point(GTK_TEXT(MW_text), (guint)(nCharsBeforeDeletingStuff*0.5)); + gtk_text_thaw(GTK_TEXT(MW_text)); +} + +void ghack_message_window_destroy( GtkWidget *win, gpointer data) +{ + +} + +void ghack_message_window_display(GtkWidget *widget, boolean block, + gpointer data) +{ + +} + +void ghack_message_window_put_string(GtkWidget *widget, int attr, + const char* text, gpointer data) +{ + + if(text == NULL) + return; + + /* Don't bother with attributes yet */ + gtk_text_insert(GTK_TEXT(MW_text), NULL, NULL, NULL, text, -1); + gtk_text_insert(GTK_TEXT(MW_text), NULL, NULL, NULL, "\n", -1); +} + +void ghack_message_window_use_RIP(int how) +{ + +} + +void ghack_message_window_scroll(int dx, int dy) +{ + +} + +GtkWidget* ghack_init_message_window(void) +{ + + MW_table = gtk_table_new(2, 1, FALSE); + gtk_table_set_row_spacing(GTK_TABLE(MW_table), 0, 2); + + MW_text = gtk_text_new(NULL, NULL); + gtk_text_set_editable(GTK_TEXT(MW_text), FALSE); + gtk_text_set_word_wrap(GTK_TEXT(MW_text), TRUE); + gtk_table_attach(GTK_TABLE(MW_table), MW_text, 0, 1, 0, 1, + (GTK_EXPAND | GTK_FILL), + (GTK_EXPAND | GTK_FILL), + 0, 0); + + MW_scrollbar = gtk_vscrollbar_new(GTK_TEXT(MW_text)->vadj); + gtk_table_attach(GTK_TABLE(MW_table), MW_scrollbar, 1, 2, 0, 1, + GTK_FILL, (GTK_EXPAND | GTK_FILL), 0, 0); + + gtk_signal_connect(GTK_OBJECT(MW_table), "ghack_putstr", + GTK_SIGNAL_FUNC(ghack_message_window_put_string), + NULL); + + gtk_signal_connect(GTK_OBJECT(MW_table), "ghack_clear", + GTK_SIGNAL_FUNC(ghack_message_window_clear), + NULL); + + gtk_signal_connect(GTK_OBJECT(MW_table), "gnome_delay_output", + GTK_SIGNAL_FUNC(ghack_delay), NULL); + + gtk_widget_show_all(MW_table); + + return GTK_WIDGET(MW_table); +} + + diff --git a/win/gnome/gnmesg.h b/win/gnome/gnmesg.h new file mode 100644 index 0000000..139e63a --- /dev/null +++ b/win/gnome/gnmesg.h @@ -0,0 +1,23 @@ +/* SCCS Id: @(#)gnmesg.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackMessageWindow_h +#define GnomeHackMessageWindow_h + +#include +#include "config.h" + +GtkWidget* ghack_init_message_window ( /* GnomeHackKeyBuffer g_keybuffer, + GnomeHackClickBuffer g_clickbuffer */ ); +void ghack_message_window_clear(GtkWidget *widget, gpointer data); +void ghack_message_window_destroy(); +void ghack_message_window_display(GtkWidget *widget, boolean block, + gpointer data); +void ghack_message_window_put_string(GtkWidget *widget, int attr, + const char* text, gpointer data); +void ghack_message_window_use_RIP(int how); +void ghack_message_window_scroll(int dx, int dy); + + +#endif /* GnomeHackMessageWindow_h */ diff --git a/win/gnome/gnomeprv.h b/win/gnome/gnomeprv.h new file mode 100644 index 0000000..515437f --- /dev/null +++ b/win/gnome/gnomeprv.h @@ -0,0 +1,18 @@ +/* SCCS Id: @(#)gnomeprv.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHack_h +#define GnomeHack_h + +/* These are the base nethack include files */ +#include "hack.h" +#include "dlb.h" +#include "patchlevel.h" + +#include "winGnome.h" + +#endif /* GnomeHack_h */ + + + diff --git a/win/gnome/gnopts.c b/win/gnome/gnopts.c new file mode 100644 index 0000000..04d0073 --- /dev/null +++ b/win/gnome/gnopts.c @@ -0,0 +1,117 @@ +/* SCCS Id: @(#)gnopts.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnopts.h" +#include "gnglyph.h" +#include "gnmain.h" +#include "gnmap.h" +#include +#include +#include "hack.h" + +static gint tileset; +static GtkWidget* clist; +const char* tilesets[] = { "Traditional (16x16)", "Big (32x32)", 0 }; + +static void +opt_sel_key_hit(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + int i; + for (i = 0; tilesets[i] != 0; ++i) { + if (tilesets[i][0] == toupper(event->keyval)) { + tileset = i; + gtk_clist_select_row( GTK_CLIST (clist), i, 0); + } + } +} + +static void +opt_sel_row_selected(GtkCList *cList, int row, int col, GdkEvent *event) +{ + tileset = row; +} + +void +ghack_settings_dialog() +{ + int i; + static GtkWidget* dialog; + static GtkWidget* swin; + static GtkWidget* frame1; + + dialog = gnome_dialog_new (_("GnomeHack Settings"), + GNOME_STOCK_BUTTON_OK, + GNOME_STOCK_BUTTON_CANCEL, + NULL); + gnome_dialog_close_hides (GNOME_DIALOG (dialog), FALSE); + gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event", + GTK_SIGNAL_FUNC (opt_sel_key_hit), tilesets ); + + frame1 = gtk_frame_new (_("Choose one of the following tilesets:")); + gtk_object_set_data (GTK_OBJECT (dialog), "frame1", frame1); + gtk_widget_show (frame1); + gtk_container_border_width (GTK_CONTAINER (frame1), 3); + + swin = gtk_scrolled_window_new (NULL, NULL); + clist = gtk_clist_new (2); + gtk_clist_column_titles_hide (GTK_CLIST (clist)); + gtk_widget_set_usize (GTK_WIDGET (clist), 100, 180); + gtk_container_add (GTK_CONTAINER (swin), clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (opt_sel_row_selected), NULL ); + + gtk_container_add (GTK_CONTAINER (frame1), swin); + gtk_box_pack_start_defaults (GTK_BOX (GNOME_DIALOG (dialog)->vbox), frame1); + + /* Add the tilesets into the list here... */ + for (i=0; tilesets[i]; i++) { + gchar accelBuf[BUFSZ]; + const char *text[3]={accelBuf, tilesets[i],NULL}; + sprintf( accelBuf, "%c ", tolower(tilesets[i][0])); + gtk_clist_insert (GTK_CLIST (clist), i, (char**)text); + } + + + gtk_clist_columns_autosize (GTK_CLIST (clist)); + gtk_widget_show_all (swin); + + /* Center the dialog over over parent */ + gnome_dialog_set_default( GNOME_DIALOG(dialog), 0); + gtk_window_set_modal( GTK_WINDOW(dialog), TRUE); + gnome_dialog_set_parent (GNOME_DIALOG (dialog), + GTK_WINDOW (ghack_get_main_window ()) ); + + /* Run the dialog -- returning whichever button was pressed */ + i = gnome_dialog_run (GNOME_DIALOG (dialog)); + gnome_dialog_close (GNOME_DIALOG (dialog)); + + /* They hit Quit or error */ + if (i != 0 ) { + return; + } + switch (tileset) { + case 0: + /* They selected traditional */ + ghack_free_glyphs(); + if (ghack_init_glyphs(HACKDIR "/x11tiles")) + g_error ("ERROR: Could not initialize glyphs.\n"); + ghack_reinit_map_window(); + break; + case 1: + ghack_free_glyphs(); + if (ghack_init_glyphs(HACKDIR "/t32-1024.xpm")) + g_error ("ERROR: Could not initialize glyphs.\n"); + ghack_reinit_map_window(); + + /* They selected big */ + break; + default: + /* This shouldn't happen */ + g_warning("This shouldn't happen\n"); + } +} + diff --git a/win/gnome/gnopts.h b/win/gnome/gnopts.h new file mode 100644 index 0000000..83ff37b --- /dev/null +++ b/win/gnome/gnopts.h @@ -0,0 +1,13 @@ +/* SCCS Id: @(#)gnopts.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackSettings_h +#define GnomeHackSettings_h + + +void ghack_settings_dialog( void); + + +#endif /* GnomeHackSettings.h */ + diff --git a/win/gnome/gnplayer.c b/win/gnome/gnplayer.c new file mode 100644 index 0000000..f10fcd2 --- /dev/null +++ b/win/gnome/gnplayer.c @@ -0,0 +1,103 @@ +/* SCCS Id: @(#)gnplayer.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include +#include "gnplayer.h" +#include "gnmain.h" +#include "hack.h" + +static gint role_number; +static GtkWidget* clist; + +static void +player_sel_key_hit (GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + const char** roles = data; + int i; + for (i = 0; roles[i] != 0; ++i) { + if (tolower(roles[i][0]) == tolower(event->keyval)) { + role_number = i; + gtk_clist_select_row( GTK_CLIST (clist), i, 0); + if (gtk_clist_row_is_visible(GTK_CLIST(clist), + i) != GTK_VISIBILITY_FULL) + gtk_clist_moveto(GTK_CLIST(clist), i, 0, 0.5, 0); + } + } +} + +static void +player_sel_row_selected (GtkCList *clist, int row, int col, GdkEvent *event) +{ + role_number = row; +} + +int +ghack_player_sel_dialog(const char** choices, + const gchar* title, + const gchar* prompt) +{ + int i; + static GtkWidget* dialog; + static GtkWidget* swin; + static GtkWidget* frame1; + + dialog = gnome_dialog_new(title, + GNOME_STOCK_BUTTON_OK, + _("Random"), + GNOME_STOCK_BUTTON_CANCEL, + NULL); + gnome_dialog_close_hides (GNOME_DIALOG (dialog), FALSE); + gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event", + GTK_SIGNAL_FUNC (player_sel_key_hit), choices ); + + frame1 = gtk_frame_new(prompt); + gtk_object_set_data (GTK_OBJECT (dialog), "frame1", frame1); + gtk_widget_show (frame1); + gtk_container_border_width (GTK_CONTAINER (frame1), 3); + + swin = gtk_scrolled_window_new (NULL, NULL); + clist = gtk_clist_new (2); + gtk_clist_column_titles_hide (GTK_CLIST (clist)); + gtk_widget_set_usize (GTK_WIDGET (clist), 100, 180); + gtk_container_add (GTK_CONTAINER (swin), clist); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + + gtk_signal_connect (GTK_OBJECT (clist), "select_row", + GTK_SIGNAL_FUNC (player_sel_row_selected), NULL ); + + gtk_container_add (GTK_CONTAINER (frame1), swin); + gtk_box_pack_start_defaults (GTK_BOX (GNOME_DIALOG (dialog)->vbox), frame1); + + /* Add the roles into the list here... */ + for (i=0; choices[i]; i++) { + gchar accelBuf[BUFSZ]; + const char *text[3]={accelBuf, choices[i],NULL}; + sprintf( accelBuf, "%c ", tolower(choices[i][0])); + gtk_clist_insert (GTK_CLIST (clist), i, (char**)text); + } + + gtk_clist_columns_autosize (GTK_CLIST (clist)); + gtk_widget_show_all (swin); + + /* Center the dialog over over parent */ + gnome_dialog_set_default( GNOME_DIALOG(dialog), 0); + gtk_window_set_modal( GTK_WINDOW(dialog), TRUE); + gnome_dialog_set_parent (GNOME_DIALOG (dialog), + GTK_WINDOW (ghack_get_main_window ()) ); + + /* Run the dialog -- returning whichever button was pressed */ + i = gnome_dialog_run_and_close(GNOME_DIALOG(dialog)); + + /* Quit on button 2 or error */ + if (i < 0 || i > 1) { + return(ROLE_NONE); + } + /* Random is button 1*/ + if (i == 1 ) { + return(ROLE_RANDOM); + } + return ( role_number); +} diff --git a/win/gnome/gnplayer.h b/win/gnome/gnplayer.h new file mode 100644 index 0000000..73929de --- /dev/null +++ b/win/gnome/gnplayer.h @@ -0,0 +1,10 @@ +/* SCCS Id: @(#)gnplayer.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackPlayerSelDialog_h +#define GnomeHackPlayerSelDialog_h + +int ghack_player_sel_dialog(const char **, const gchar*, const gchar*); + +#endif /* GnomeHackPlayerSelDialog_h */ diff --git a/win/gnome/gnsignal.c b/win/gnome/gnsignal.c new file mode 100644 index 0000000..2e5b621 --- /dev/null +++ b/win/gnome/gnsignal.c @@ -0,0 +1,442 @@ +/* SCCS Id: @(#)gnsignal.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Anthony Taylor */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnsignal.h" +#include "gnmain.h" +#include + +GList *g_keyBuffer; +GList *g_clickBuffer; +int g_numKeys=0; +int g_numClicks=0; +int g_askingQuestion=0; +static int s_done=FALSE; + +/* + * ghack_init_signals + * + * Create some signals and attach them to the GtkWidget class. + * These are the signals: + * + * ghack_curs : NONE:INT,INT + * INT 1 = x + * INT 2 = y + * + * ghack_putstr : NONE:INT,POINTER + * INT = attribute + * POINTER = char* string to print + * + * ghack_print_glyph : NONE:INT,INT,POINTER + * INT 1 = x + * INT 2 = y + * INT 3 = GtkPixmap* to rendered glyph + * + * ghack_clear : NONE:NONE + * + * ghack_display : NONE:BOOL + * BOOL = blocking flag + * + * ghack_start_menu : NONE:NONE + * + * ghack_add_menu : NONE:POINTER + * POINTER = GHackMenuItem* + * + * ghack_end_menu : NONE:POINTER + * POINTER = char* to closing string + * + * ghack_select_menu : NONE:POINTER,INT,POINTER + * POINTER = int pointer-- filled with number + * of selected items on return + * INT = number of items selected + * POINTER = structure to fill + * + * ghack_cliparound : NONE:INT,INT + * INT 1 = x + * INT 2 = y +*/ + +void +ghack_init_signals( void) +{ + ghack_signals[GHSIG_CURS] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_curs", + GTK_RUN_FIRST, + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, + 2, + GTK_TYPE_INT, + GTK_TYPE_INT); + + ghack_signals[GHSIG_PUTSTR] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_putstr", + GTK_RUN_FIRST, + gtk_marshal_NONE__INT_POINTER, + GTK_TYPE_NONE, + 2, + GTK_TYPE_INT, + GTK_TYPE_POINTER); + + ghack_signals[GHSIG_PRINT_GLYPH] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_print_glyph", + GTK_RUN_FIRST, + gtk_marshal_NONE__INT_INT_POINTER, + GTK_TYPE_NONE, + 3, + GTK_TYPE_INT, + GTK_TYPE_INT, + GTK_TYPE_POINTER); + + ghack_signals[GHSIG_CLEAR] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_clear", + GTK_RUN_FIRST, + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, + 0); + + ghack_signals[GHSIG_DISPLAY] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_display", + GTK_RUN_FIRST, + gtk_marshal_NONE__BOOL, + GTK_TYPE_NONE, + 1, + GTK_TYPE_BOOL); + + ghack_signals[GHSIG_START_MENU] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_start_menu", + GTK_RUN_FIRST, + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, + 0); + + ghack_signals[GHSIG_ADD_MENU] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_add_menu", + GTK_RUN_FIRST, + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, + 1, + GTK_TYPE_POINTER); + + ghack_signals[GHSIG_END_MENU] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_end_menu", + GTK_RUN_FIRST, + gtk_marshal_NONE__POINTER, + GTK_TYPE_NONE, + 1, + GTK_TYPE_POINTER); + + ghack_signals[GHSIG_SELECT_MENU] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_select_menu", + GTK_RUN_FIRST, + gtk_marshal_NONE__POINTER_INT_POINTER, + GTK_TYPE_NONE, + 3, + GTK_TYPE_POINTER, + GTK_TYPE_INT, + GTK_TYPE_POINTER); + + ghack_signals[GHSIG_CLIPAROUND] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_cliparound", + GTK_RUN_FIRST, + gtk_marshal_NONE__INT_INT, + GTK_TYPE_NONE, + 2, + GTK_TYPE_INT, + GTK_TYPE_INT); + + ghack_signals[GHSIG_FADE_HIGHLIGHT] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "ghack_fade_highlight", + GTK_RUN_FIRST, + gtk_marshal_NONE__NONE, + GTK_TYPE_NONE, + 0); + + ghack_signals[GHSIG_DELAY] = + gtk_object_class_user_signal_new (gtk_type_class (gtk_widget_get_type ()), + "gnome_delay_output", + GTK_RUN_FIRST, + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, + 1, + GTK_TYPE_INT); + +} + +/* For want of a better place, I'm putting the delay output stuff here + * -Erik + */ +static gint timeout_callback(gpointer data) +{ + s_done=TRUE; + return FALSE; +} + +void +ghack_delay( GtkWidget *win, int numMillisecs, gpointer data) +{ + s_done=FALSE; + gtk_timeout_add( (unsigned int) numMillisecs, + timeout_callback, NULL); + while( s_done==FALSE) + gtk_main_iteration(); +} + + +void +ghack_handle_button_press(GtkWidget *widget, GdkEventButton *event, + gpointer data) +{ + GHClick *click; + double x1, y1; + + if (event->type != GDK_BUTTON_PRESS) + return; + + gnome_canvas_window_to_world( GNOME_CANVAS( widget), event->x, + event->y, &x1, &y1); +/* + g_message("I got a click at %f,%f with button %d \n", + x1, y1, event->button); +*/ + + /* We allocate storage here, so we need to remember if (g_numClicks>0) + * to blow this away when closing the app using something like + * while (g_clickBuffer) + * { + * g_free((GHClick)g_clickBuffer->data); + * g_clickBuffer = g_clickBuffer->next; + * } + * g_list_free( g_clickBuffer ); + * + */ + click = g_new( GHClick, 1); + + click->x=(int)x1/ghack_glyph_width(); + click->y=(int)y1/ghack_glyph_height(); + click->mod=(event->button == 1)? CLICK_1 : CLICK_2; + + g_clickBuffer = g_list_prepend (g_clickBuffer, click); + /* Could use g_list_length(), but it is stupid and just + * traverses the list while counting, so we'll just do + * the counting ourselves in advance. */ + g_numClicks++; +} + +#ifndef M +# ifndef NHSTDC +# define M(c) (0x80 | (c)) +# else +# define M(c) ((c) - 128) +# endif /* NHSTDC */ +#endif +#ifndef C +#define C(c) (0x1f & (c)) +#endif + + +void +ghack_handle_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) +{ + static int was_pound = 0; + int key = 0; + int ctl = GDK_CONTROL_MASK; + int alt = GDK_MOD1_MASK; + + /* Turn this on to debug key events */ +#if 0 + g_message("I got a \"%s\" key (%d) %s%s", + gdk_keyval_name (event->keyval), event->keyval, + (event->state&ctl)? "+CONTROL":"", (event->state&alt)? "+ALT":""); +#endif + + switch (event->keyval) { + /* special keys to do stuff with */ + + /* Set up the direction keys */ + + /* First handle the arrow keys -- these always mean move */ + case GDK_Right: + case GDK_rightarrow: + if (iflags.num_pad) key='6'; else key='l'; break; + case GDK_Left: + case GDK_leftarrow: + if (iflags.num_pad) key='4'; else key='h'; break; + case GDK_Up: + case GDK_uparrow: + if (iflags.num_pad) key='8'; else key='k'; break; + case GDK_Down: + case GDK_downarrow: + if (iflags.num_pad) key='2'; else key='j'; break; + case GDK_Home: + if (iflags.num_pad) key='7'; else key='y'; break; + case GDK_End: + if (iflags.num_pad) key='1'; else key='b'; break; + case GDK_Page_Down: + if (iflags.num_pad) key='3'; else key='n'; break; + case GDK_Page_Up: + if (iflags.num_pad) key='9'; else key='u'; break; + case ' ': key='.'; break; + + /* Now, handle the numberpad (move or numbers) */ + case GDK_KP_Right: + case GDK_KP_6: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_6; + else + key='6'; + break; + + case GDK_KP_Left: + case GDK_KP_4: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_4; + else + key='4'; + break; + + case GDK_KP_Up: + case GDK_KP_8: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_8; + else + key='8'; + break; + + case GDK_KP_Down: + case GDK_KP_2: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_2; + else + key='2'; + break; + + /* Move Top-Left */ + case GDK_KP_Home: + case GDK_KP_7: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_7; + else + key='7'; + break; + + case GDK_KP_Page_Up: + case GDK_KP_9: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_9; + else + key='9'; + break; + + case GDK_KP_End: + case GDK_KP_1: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_1; + else + key='1'; + break; + + case GDK_KP_Page_Down: + case GDK_KP_3: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_3; + else + key='3'; + break; + + + case GDK_KP_5: + if (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) && iflags.num_pad) + key = GDK_KP_5; + else + key='5'; + break; + + case GDK_KP_Delete: + case GDK_KP_Decimal: + key='.'; + break; + + /* can't just ignore "#", it's a core feature */ + case GDK_numbersign: + key='#'; + break; + + /* We will probably want to do something with these later... */ + case GDK_KP_Begin: + case GDK_KP_F1: + case GDK_F1: + case GDK_KP_F2: + case GDK_F2: + case GDK_KP_F3: + case GDK_F3: + case GDK_KP_F4: + case GDK_F4: + case GDK_F5: + case GDK_F6: + case GDK_F7: + case GDK_F8: + case GDK_F9: + case GDK_F10: + case GDK_F11: + case GDK_F12: + break; + /* various keys to ignore */ + case GDK_KP_Insert: + case GDK_Insert: + case GDK_Delete: + case GDK_Print: + case GDK_BackSpace: + case GDK_Pause: + case GDK_Scroll_Lock: + case GDK_Shift_Lock: + case GDK_Num_Lock: + case GDK_Caps_Lock: + case GDK_Control_L: + case GDK_Control_R: + case GDK_Shift_L: + case GDK_Shift_R: + case GDK_Alt_L: + case GDK_Alt_R: + case GDK_Meta_L: + case GDK_Meta_R: + case GDK_Mode_switch: + case GDK_Multi_key: + return; + + default: + key = event->keyval; + break; + } + + if ((event->state & alt) || was_pound) { + key=M(event->keyval); + } else if (event->state & ctl) { + key=C(event->keyval); + } + if (was_pound) { + was_pound = 0; + } + + /* Ok, here is where we do clever stuff to overide the default + * game behavior */ + if (g_askingQuestion == 0) { + + if (key == 'S' || key == M('S') || key == C('S')) { + ghack_save_game_cb( NULL, NULL); + return; + } + } + g_keyBuffer = g_list_prepend (g_keyBuffer, GINT_TO_POINTER( key)); + g_numKeys++; +} diff --git a/win/gnome/gnsignal.h b/win/gnome/gnsignal.h new file mode 100644 index 0000000..52fa299 --- /dev/null +++ b/win/gnome/gnsignal.h @@ -0,0 +1,56 @@ +/* SCCS Id: @(#)gnsignal.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Anthony Taylor */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackSignals_h +#define GnomeHackSignals_h + +#include +#include +#include "gnomeprv.h" +#include "gnglyph.h" + +/* The list of custom signals */ + +enum { + GHSIG_CURS, + GHSIG_PUTSTR, + GHSIG_PRINT_GLYPH, + GHSIG_CLEAR, + GHSIG_DISPLAY, + GHSIG_START_MENU, + GHSIG_ADD_MENU, + GHSIG_END_MENU, + GHSIG_SELECT_MENU, + GHSIG_CLIPAROUND, + GHSIG_FADE_HIGHLIGHT, + GHSIG_DELAY, + GHSIG_LAST_SIG +}; + +guint ghack_signals[GHSIG_LAST_SIG]; + +extern void ghack_init_signals( void); + + +void ghack_handle_key_press(GtkWidget *widget, GdkEventKey *event, + gpointer data); +void ghack_handle_button_press(GtkWidget *widget, GdkEventButton *event, + gpointer data); + +typedef struct { + int x, y, mod; +} GHClick; + +extern GList *g_keyBuffer; +extern GList *g_clickBuffer; +extern int g_numKeys; +extern int g_numClicks; + +extern int g_askingQuestion; + +void ghack_delay( GtkWidget *win, int numMillisecs, gpointer data); + + +#endif /* GnomeHackSignals_h */ + diff --git a/win/gnome/gnstatus.c b/win/gnome/gnstatus.c new file mode 100644 index 0000000..1075a5e --- /dev/null +++ b/win/gnome/gnstatus.c @@ -0,0 +1,941 @@ +/* SCCS Id: @(#)gnstatus.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnstatus.h" +#include "gnsignal.h" +#include "gn_xpms.h" +#include "gnomeprv.h" + + +extern const char *hu_stat[]; /* from eat.c */ +extern const char *enc_stat[]; /* from botl.c */ + +void ghack_status_window_update_stats(); +void ghack_status_window_clear(GtkWidget *win, gpointer data); +void ghack_status_window_destroy(GtkWidget *win, gpointer data); +void ghack_status_window_display(GtkWidget *win, boolean block, gpointer data); +void ghack_status_window_cursor_to(GtkWidget *win, int x, int y, gpointer data); +void ghack_status_window_put_string(GtkWidget *win, int attr, const char* text, gpointer data); + +static void ghack_fade_highlighting(); +static void ghack_highlight_widget( GtkWidget* widget, GtkStyle* oldStyle, + GtkStyle* newStyle); + +/* some junk to handle when to fade the highlighting */ +#define NUM_TURNS_HIGHLIGHTED 3 + +static GList *s_HighLightList; + +typedef struct { + GtkWidget* widget; + GtkStyle* oldStyle; + int nTurnsLeft; +} Highlight; + + +/* Ok, now for a LONG list of widgets... */ +static GtkWidget* statTable = NULL; +static GtkWidget* titleLabel = NULL; +static GtkWidget* dgnLevelLabel = NULL; +static GtkWidget* strPix = NULL; +static GtkWidget* strLabel = NULL; +static GtkWidget* dexPix = NULL; +static GtkWidget* dexLabel = NULL; +static GtkWidget* intPix = NULL; +static GtkWidget* intLabel = NULL; +static GtkWidget* wisPix = NULL; +static GtkWidget* wisLabel = NULL; +static GtkWidget* conPix = NULL; +static GtkWidget* conLabel = NULL; +static GtkWidget* chaPix = NULL; +static GtkWidget* chaLabel = NULL; +static GtkWidget* goldLabel = NULL; +static GtkWidget* hpLabel = NULL; +static GtkWidget* powLabel = NULL; +static GtkWidget* acLabel = NULL; +static GtkWidget* levlLabel = NULL; +static GtkWidget* expLabel = NULL; +static GtkWidget* timeLabel = NULL; +static GtkWidget* scoreLabel = NULL; +static GtkWidget* alignPix = NULL; +static GtkWidget* alignLabel = NULL; +static GtkWidget* hungerPix = NULL; +static GtkWidget* hungerLabel = NULL; +static GtkWidget* sickPix = NULL; +static GtkWidget* sickLabel = NULL; +static GtkWidget* blindPix = NULL; +static GtkWidget* blindLabel = NULL; +static GtkWidget* stunPix = NULL; +static GtkWidget* stunLabel = NULL; +static GtkWidget* halluPix = NULL; +static GtkWidget* halluLabel = NULL; +static GtkWidget* confuPix = NULL; +static GtkWidget* confuLabel = NULL; +static GtkWidget* encumbPix = NULL; +static GtkWidget* encumbLabel = NULL; + + +static GtkStyle* normalStyle = NULL; +static GtkStyle* bigStyle = NULL; +static GtkStyle* redStyle = NULL; +static GtkStyle* greenStyle = NULL; +static GtkStyle* bigRedStyle = NULL; +static GtkStyle* bigGreenStyle = NULL; + +/* Pure red */ +static GdkColor color_red = { 0, 0xff00, 0, 0 }; +/* ForestGreen (looks better than just pure green) */ +static GdkColor color_green = { 0, 0x2200, 0x8b00, 0x2200 }; + +static int lastDepth; +static int lastStr; +static int lastInt; +static int lastWis; +static int lastDex; +static int lastCon; +static int lastCha; +static long lastAu; +static int lastHP; +static int lastMHP; +static int lastLevel; +static int lastPOW; +static int lastMPOW; +static int lastAC; +static int lastExp; +static aligntyp lastAlignment; +static unsigned lastHungr; +static long lastConf; +static long lastBlind; +static long lastStun; +static long lastHalu; +static long lastSick; +static int lastEncumb; + +void ghack_status_window_clear( GtkWidget *win, gpointer data) +{ + /* Don't think we need this at all */ +} + +void ghack_status_window_destroy(GtkWidget *win, gpointer data) +{ + while (s_HighLightList) { + g_free( (Highlight*)s_HighLightList->data); + s_HighLightList = s_HighLightList->next; + } + g_list_free( s_HighLightList ); +} + +void ghack_status_window_display( GtkWidget *win, boolean block, gpointer data) +{ + gtk_widget_show_all( GTK_WIDGET(win)); +} + +void ghack_status_window_cursor_to( GtkWidget *win, int x, int y, gpointer data) +{ + /* Don't think we need this at all */ +} + +void ghack_status_window_put_string( GtkWidget *win, int attr, const char* text, gpointer data) +{ + ghack_status_window_update_stats(); +} + + + +GtkWidget* ghack_init_status_window () +{ + GtkWidget *horizSep0, *horizSep1, *horizSep2, *horizSep3; + GtkWidget *statsHBox, *strVBox, *dexVBox, *intVBox, *statHBox; + GtkWidget *wisVBox, *conVBox, *chaVBox; + GtkWidget *alignVBox, *hungerVBox, *sickVBox, *blindVBox; + GtkWidget *stunVBox, *halluVBox, *confuVBox, *encumbVBox; + + /* Set up a (ridiculous) initial state */ + lastDepth = 9999; + lastStr = 9999; + lastInt = 9999; + lastWis = 9999; + lastDex = 9999; + lastCon = 9999; + lastCha = 9999; + lastAu = 9999; + lastHP = 9999; + lastMHP = 9999; + lastLevel = 9999; + lastPOW = 9999; + lastMPOW = 9999; + lastAC = 9999; + lastExp = 9999; + lastAlignment = A_NEUTRAL; /* start off guessing neutral */ + lastHungr = 9999; + lastConf = 9999; + lastBlind = 9999; + lastStun = 9999; + lastHalu = 9999; + lastSick = 9999; + lastEncumb = 9999; + + statTable = gtk_table_new( 10, 8, FALSE); + gtk_table_set_row_spacings( GTK_TABLE( statTable), 1); + gtk_table_set_col_spacings( GTK_TABLE( statTable), 1); + + + /* Begin the first row of the table -- the title */ + titleLabel = gtk_label_new( _("GnomeHack!")); + gtk_table_attach( GTK_TABLE( statTable), titleLabel, + 0, 8, 0, 1, GTK_FILL, 0, 0, 0); + if (!normalStyle) + normalStyle = gtk_style_copy ( + gtk_widget_get_style (GTK_WIDGET (titleLabel))); + + /* Set up some styles to draw stuff with */ + if (!redStyle) { + g_assert (greenStyle == NULL); + g_assert (bigStyle == NULL); + g_assert (bigRedStyle == NULL); + g_assert (bigGreenStyle == NULL); + + greenStyle = gtk_style_copy (normalStyle); + redStyle = gtk_style_copy (normalStyle); + bigRedStyle = gtk_style_copy (normalStyle); + bigGreenStyle = gtk_style_copy (normalStyle); + bigStyle = gtk_style_copy (normalStyle); + + greenStyle->fg[GTK_STATE_NORMAL] = color_green; + redStyle->fg[GTK_STATE_NORMAL] = color_red; + bigRedStyle->fg[GTK_STATE_NORMAL] = color_red; + bigGreenStyle->fg[GTK_STATE_NORMAL] = color_green; + + gdk_font_unref (bigRedStyle->font); + gdk_font_unref (bigGreenStyle->font); + bigRedStyle->font = gdk_font_load("-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"); + bigGreenStyle->font = gdk_font_load("-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"); + + gdk_font_unref (bigStyle->font); + bigStyle->font = gdk_font_load ("-misc-fixed-*-*-*-*-20-*-*-*-*-*-*-*"); + } + gtk_widget_set_style (GTK_WIDGET (titleLabel), bigStyle); + + /* Begin the second row */ + dgnLevelLabel = gtk_label_new (_ ("Nethack for Gnome")); + gtk_table_attach (GTK_TABLE (statTable), dgnLevelLabel, + 0, 8, 1, 2, GTK_FILL, 0, 0, 0); + gtk_widget_set_style (GTK_WIDGET (dgnLevelLabel), bigStyle); + + /* Begin the third row */ + horizSep0 = gtk_hseparator_new (); + gtk_table_attach (GTK_TABLE (statTable), horizSep0, + 0, 8, 2, 3, GTK_FILL, GTK_FILL, 0, 0); + + + /* Begin the fourth row */ + statsHBox = gtk_hbox_new (TRUE, 0); + + strVBox = gtk_vbox_new (FALSE, 0); + strPix = gnome_pixmap_new_from_xpm_d( str_xpm); + strLabel = gtk_label_new( "STR: "); + gtk_box_pack_start (GTK_BOX (strVBox), strPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (strVBox), strLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(strVBox), TRUE, TRUE, 2); + + dexVBox = gtk_vbox_new (FALSE, 0); + dexPix = gnome_pixmap_new_from_xpm_d( dex_xpm); + dexLabel = gtk_label_new( "DEX: "); + gtk_box_pack_start (GTK_BOX (dexVBox), dexPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (dexVBox), dexLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(dexVBox), TRUE, TRUE, 2); + + conVBox = gtk_vbox_new (FALSE, 0); + conPix = gnome_pixmap_new_from_xpm_d( cns_xpm); + conLabel = gtk_label_new( "CON: "); + gtk_box_pack_start (GTK_BOX (conVBox), conPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (conVBox), conLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(conVBox), TRUE, TRUE, 2); + + intVBox = gtk_vbox_new (FALSE, 0); + intPix = gnome_pixmap_new_from_xpm_d( int_xpm); + intLabel = gtk_label_new( "INT: "); + gtk_box_pack_start (GTK_BOX (intVBox), intPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (intVBox), intLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(intVBox), TRUE, TRUE, 2); + + + wisVBox = gtk_vbox_new (FALSE, 0); + wisPix = gnome_pixmap_new_from_xpm_d( wis_xpm); + wisLabel = gtk_label_new( "WIS: "); + gtk_box_pack_start (GTK_BOX (wisVBox), wisPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (wisVBox), wisLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(wisVBox), TRUE, TRUE, 2); + + + chaVBox = gtk_vbox_new (FALSE, 0); + chaPix = gnome_pixmap_new_from_xpm_d( cha_xpm); + chaLabel = gtk_label_new( "CHA: "); + gtk_box_pack_start (GTK_BOX (chaVBox), chaPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (chaVBox), chaLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statsHBox), GTK_WIDGET(chaVBox), TRUE, TRUE, 2); + + gtk_table_attach( GTK_TABLE( statTable), GTK_WIDGET(statsHBox), + 0, 8, 3, 4, GTK_FILL, 0, 0, 0); + + /* Begin the fifth row */ + horizSep1 = gtk_hseparator_new(); + gtk_table_attach( GTK_TABLE( statTable), horizSep1, + 0, 8, 4, 5, GTK_FILL, GTK_FILL, 0, 0); + + /* Begin the sixth row */ + hpLabel = gtk_label_new( "HP: "); + gtk_table_attach( GTK_TABLE( statTable), hpLabel, + 0, 1, 5, 6, GTK_FILL, 0, 0, 0); + + acLabel = gtk_label_new( "AC: "); + gtk_table_attach( GTK_TABLE( statTable), acLabel, + 2, 3, 5, 6, GTK_FILL, 0, 0, 0); + + powLabel = gtk_label_new( "Power: "); + gtk_table_attach( GTK_TABLE( statTable), powLabel, + 4, 5, 5, 6, GTK_FILL, 0, 0, 0); + + goldLabel = gtk_label_new( "Au: "); + gtk_table_attach( GTK_TABLE( statTable), goldLabel, + 6, 7, 5, 6, GTK_FILL, 0, 0, 0); + + + /* Begin the seventh row */ + horizSep2 = gtk_hseparator_new(); + gtk_table_attach( GTK_TABLE( statTable), horizSep2, + 0, 8, 6, 7, GTK_FILL, GTK_FILL, 0, 0); + + + /* Begin the eigth row */ + levlLabel = gtk_label_new( "Level: "); + gtk_table_attach( GTK_TABLE( statTable), levlLabel, + 0, 1, 7, 8, GTK_FILL, 0, 0, 0); + + expLabel = gtk_label_new( "Exp: "); + gtk_table_attach( GTK_TABLE( statTable), expLabel, + 2, 3, 7, 8, GTK_FILL, 0, 0, 0); + + timeLabel = gtk_label_new( "Time: "); + gtk_table_attach( GTK_TABLE( statTable), timeLabel, + 4, 5, 7, 8, GTK_FILL, 0, 0, 0); + + scoreLabel = gtk_label_new( "Score: "); + gtk_table_attach( GTK_TABLE( statTable), scoreLabel, + 6, 7, 7, 8, GTK_FILL, 0, 0, 0); + + /* Begin the ninth row */ + horizSep3 = gtk_hseparator_new(); + gtk_table_attach( GTK_TABLE( statTable), horizSep3, + 0, 8, 8, 9, GTK_FILL, GTK_FILL, 0, 0); + + /* Begin the tenth and last row */ + statHBox = gtk_hbox_new (FALSE, 0); + + alignVBox = gtk_vbox_new (FALSE, 0); + alignPix = gnome_pixmap_new_from_xpm_d( neutral_xpm); + alignLabel = gtk_label_new( "Neutral"); + gtk_box_pack_start (GTK_BOX (alignVBox), alignPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (alignVBox), alignLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(alignVBox), TRUE, FALSE, 2); + + hungerVBox = gtk_vbox_new (FALSE, 0); + hungerPix = gnome_pixmap_new_from_xpm_d( hungry_xpm); + hungerLabel = gtk_label_new( "Hungry"); + gtk_box_pack_start (GTK_BOX (hungerVBox), hungerPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (hungerVBox), hungerLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(hungerVBox), TRUE, FALSE, 2); + + sickVBox = gtk_vbox_new (FALSE, 0); + sickPix = gnome_pixmap_new_from_xpm_d( sick_fp_xpm); + sickLabel = gtk_label_new( "FoodPois"); + gtk_box_pack_start (GTK_BOX (sickVBox), sickPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (sickVBox), sickLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(sickVBox), TRUE, FALSE, 2); + + blindVBox = gtk_vbox_new (FALSE, 0); + blindPix = gnome_pixmap_new_from_xpm_d( blind_xpm); + blindLabel = gtk_label_new( "Blind"); + gtk_box_pack_start (GTK_BOX (blindVBox), blindPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (blindVBox), blindLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(blindVBox), TRUE, FALSE, 2); + + stunVBox = gtk_vbox_new (FALSE, 0); + stunPix = gnome_pixmap_new_from_xpm_d( stunned_xpm); + stunLabel = gtk_label_new( "Stun"); + gtk_box_pack_start (GTK_BOX (stunVBox), stunPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (stunVBox), stunLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(stunVBox), TRUE, FALSE, 2); + + confuVBox = gtk_vbox_new (FALSE, 0); + confuPix = gnome_pixmap_new_from_xpm_d( confused_xpm); + confuLabel = gtk_label_new( "Confused"); + gtk_box_pack_start (GTK_BOX (confuVBox), confuPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (confuVBox), confuLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(confuVBox), TRUE, FALSE, 2); + + halluVBox = gtk_vbox_new (FALSE, 0); + halluPix = gnome_pixmap_new_from_xpm_d( hallu_xpm); + halluLabel = gtk_label_new( "Hallu"); + gtk_box_pack_start (GTK_BOX (halluVBox), halluPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (halluVBox), halluLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(halluVBox), TRUE, FALSE, 2); + + encumbVBox = gtk_vbox_new (FALSE, 0); + encumbPix = gnome_pixmap_new_from_xpm_d( slt_enc_xpm); + encumbLabel = gtk_label_new( "Burdened"); + gtk_box_pack_start (GTK_BOX (encumbVBox), encumbPix, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX (encumbVBox), encumbLabel, TRUE, TRUE, 2); + gtk_box_pack_start (GTK_BOX(statHBox), GTK_WIDGET(encumbVBox), TRUE, FALSE, 2); + + gtk_table_attach( GTK_TABLE( statTable), GTK_WIDGET(statHBox), + 0, 8, 9, 10, GTK_FILL, GTK_FILL, 0, 0); + + /* Set up the necessary signals */ + gtk_signal_connect (GTK_OBJECT (statTable), + "ghack_fade_highlight", + GTK_SIGNAL_FUNC (ghack_fade_highlighting), + NULL); + + gtk_signal_connect (GTK_OBJECT (statTable), + "ghack_putstr", + GTK_SIGNAL_FUNC (ghack_status_window_put_string), + NULL); + + gtk_signal_connect (GTK_OBJECT (statTable), + "ghack_clear", + GTK_SIGNAL_FUNC (ghack_status_window_clear), + NULL); + + gtk_signal_connect (GTK_OBJECT (statTable), + "ghack_curs", + GTK_SIGNAL_FUNC (ghack_status_window_cursor_to), + NULL); + gtk_signal_connect(GTK_OBJECT (statTable), + "gnome_delay_output", + GTK_SIGNAL_FUNC(ghack_delay), + NULL); + + /* Lastly, show the status window and everything in it */ + gtk_widget_show_all( statTable); + + return GTK_WIDGET(statTable); +} + + + +void ghack_status_window_update_stats() +{ + char buf[BUFSZ]; + gchar *buf1; + const char* hung; + const char* enc; + static int firstTime=TRUE; +#ifdef GOLDOBJ + long umoney; +#endif + + /* First, fill in the player name and the dungeon level */ + strcpy(buf, plname); + if ('a' <= buf[0] && buf[0] <= 'z') buf[0] += 'A'-'a'; + strcat(buf, " the "); + if (u.mtimedone) { + char mname[BUFSZ]; + int k = 0; + + strcpy(mname, mons[u.umonnum].mname); + while(mname[k] != 0) { + if ((k == 0 || (k > 0 && mname[k-1] == ' ')) + && 'a' <= mname[k] && mname[k] <= 'z') + { + mname[k] += 'A' - 'a'; + } + k++; + } + strcat(buf, mname); + } else { + strcat(buf, rank_of(u.ulevel, pl_character[0], flags.female)); + } + gtk_label_get( GTK_LABEL( titleLabel), &buf1); + if (strcmp( buf1, buf) != 0 && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( titleLabel, bigStyle, bigGreenStyle); + } + gtk_label_set( GTK_LABEL( titleLabel), buf); + + + if (In_endgame(&u.uz)) { + strcpy(buf, (Is_astralevel(&u.uz) ? "Astral Plane":"End Game")); + } else { + sprintf(buf, "%s, level %d", dungeons[u.uz.dnum].dname, depth(&u.uz)); + } + if (lastDepth > depth(&u.uz) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( dgnLevelLabel, bigStyle, bigRedStyle); + } + else if (lastDepth < depth(&u.uz) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( dgnLevelLabel, bigStyle, bigGreenStyle); + } + lastDepth = depth(&u.uz); + gtk_label_set( GTK_LABEL( dgnLevelLabel), buf); + + /* Next, fill in the player's stats */ + if (ACURR(A_STR) > 118) { + sprintf(buf,"STR:%d",ACURR(A_STR)-100); + } else if (ACURR(A_STR)==118) { + sprintf(buf,"STR:18/**"); + } else if(ACURR(A_STR) > 18) { + sprintf(buf,"STR:18/%02d",ACURR(A_STR)-18); + } else { + sprintf(buf,"STR:%d",ACURR(A_STR)); + } + if (lastStr < ACURR(A_STR) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( strLabel, normalStyle, greenStyle); + } + else if (lastStr > ACURR(A_STR) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( strLabel, normalStyle, redStyle); + } + lastStr = ACURR(A_STR); + gtk_label_set( GTK_LABEL( strLabel), buf); + + sprintf(buf,"INT:%d",ACURR(A_INT)); + if (lastInt < ACURR(A_INT) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( intLabel, normalStyle, greenStyle); + } + else if (lastInt > ACURR(A_INT) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( intLabel, normalStyle, redStyle); + } + lastInt = ACURR(A_INT); + gtk_label_set( GTK_LABEL( intLabel), buf); + + sprintf(buf,"WIS:%d",ACURR(A_WIS)); + if (lastWis < ACURR(A_WIS) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( wisLabel, normalStyle, greenStyle); + } + else if (lastWis > ACURR(A_WIS) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( wisLabel, normalStyle, redStyle); + } + lastWis = ACURR(A_WIS); + gtk_label_set( GTK_LABEL( wisLabel), buf); + + sprintf(buf,"DEX:%d",ACURR(A_DEX)); + if (lastDex < ACURR(A_DEX) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( dexLabel, normalStyle, greenStyle); + } + else if (lastDex > ACURR(A_DEX) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( dexLabel, normalStyle, redStyle); + } + lastDex = ACURR(A_DEX); + gtk_label_set( GTK_LABEL( dexLabel), buf); + + sprintf(buf,"CON:%d",ACURR(A_CON)); + if (lastCon < ACURR(A_CON) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( conLabel, normalStyle, greenStyle); + } + else if (lastCon > ACURR(A_CON) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( conLabel, normalStyle, redStyle); + } + lastCon = ACURR(A_CON); + gtk_label_set( GTK_LABEL( conLabel), buf); + + sprintf(buf,"CHA:%d",ACURR(A_CHA)); + if (lastCha < ACURR(A_CHA) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( chaLabel, normalStyle, greenStyle); + } + else if (lastCha > ACURR(A_CHA) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( chaLabel, normalStyle, redStyle); + } + lastCha = ACURR(A_CHA); + gtk_label_set( GTK_LABEL( chaLabel), buf); + + /* Now do the non-pixmaped stats (gold and such) */ +#ifndef GOLDOBJ + sprintf(buf,"Au:%ld", u.ugold); + if (lastAu < u.ugold && firstTime==FALSE) { +#else + umoney = money_cnt(invent); + sprintf(buf,"Au:%ld", umoney); + if (lastAu < umoney && firstTime==FALSE) { +#endif + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( goldLabel, normalStyle, greenStyle); + } +#ifndef GOLDOBJ + else if (lastAu > u.ugold && firstTime==FALSE) { +#else + else if (lastAu > umoney && firstTime==FALSE) { +#endif + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( goldLabel, normalStyle, redStyle); + } +#ifndef GOLDOBJ + lastAu = u.ugold; +#else + lastAu = umoney; +#endif + gtk_label_set( GTK_LABEL( goldLabel), buf); + + if (u.mtimedone) { + /* special case: when polymorphed, show "HD", disable exp */ + sprintf(buf,"HP:%d/%d", ( (u.mh > 0)? u.mh : 0), u.mhmax); + if ((lastHP < u.mh || lastMHP < u.mhmax ) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( hpLabel, normalStyle, greenStyle); + } + else if ((lastHP > u.mh || lastMHP > u.mhmax ) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( hpLabel, normalStyle, redStyle); + } + lastHP = u.mh; + lastMHP = u.mhmax; + } else { + sprintf(buf,"HP:%d/%d", ( (u.uhp > 0)? u.uhp : 0), u.uhpmax); + if ((lastHP < u.uhp || lastMHP < u.uhpmax ) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( hpLabel, normalStyle, greenStyle); + } + else if ((lastHP > u.uhp || lastMHP > u.uhpmax ) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( hpLabel, normalStyle, redStyle); + } + lastHP = u.uhp; + lastMHP = u.uhpmax; + } + gtk_label_set( GTK_LABEL( hpLabel), buf); + + if (u.mtimedone) { + /* special case: when polymorphed, show "HD", disable exp */ + sprintf(buf,"HD:%d", mons[u.umonnum].mlevel); + if (lastLevel < mons[u.umonnum].mlevel && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( levlLabel, normalStyle, greenStyle); + } + else if (lastLevel > mons[u.umonnum].mlevel && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( levlLabel, normalStyle, redStyle); + } + lastLevel = mons[u.umonnum].mlevel; + } else { + sprintf(buf,"Level:%d", u.ulevel); + if (lastLevel < u.ulevel && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( levlLabel, normalStyle, greenStyle); + } + else if (lastLevel > u.ulevel && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( levlLabel, normalStyle, redStyle); + } + lastLevel = u.ulevel; + } + gtk_label_set( GTK_LABEL( levlLabel), buf); + + sprintf(buf,"Power:%d/%d", u.uen, u.uenmax); + if ((lastPOW < u.uen || lastMPOW < u.uenmax) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( powLabel, normalStyle, greenStyle); + } + if ((lastPOW > u.uen || lastMPOW > u.uenmax) && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( powLabel, normalStyle, redStyle); + } + lastPOW = u.uen; + lastMPOW = u.uenmax; + gtk_label_set( GTK_LABEL( powLabel), buf); + + sprintf(buf,"AC:%d", u.uac); + if (lastAC > u.uac && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( acLabel, normalStyle, greenStyle); + } + else if (lastAC < u.uac && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( acLabel, normalStyle, redStyle); + } + lastAC = u.uac; + gtk_label_set( GTK_LABEL( acLabel), buf); + +#ifdef EXP_ON_BOTL + if (flags.showexp) { + sprintf(buf,"Exp:%ld", u.uexp); + if (lastExp < u.uexp && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( expLabel, normalStyle, greenStyle); + } + else if (lastExp > u.uexp && firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( expLabel, normalStyle, redStyle); + } + lastExp = u.uexp; + gtk_label_set( GTK_LABEL( expLabel), buf); + } else +#endif + { + gtk_label_set( GTK_LABEL( expLabel), ""); + } + + if (flags.time) { + sprintf(buf,"Time:%ld", moves); + gtk_label_set( GTK_LABEL( timeLabel), buf); + } + else + gtk_label_set( GTK_LABEL( timeLabel), ""); +#ifdef SCORE_ON_BOTL + if (flags.showscore) { + sprintf(buf,"Score:%ld", botl_score()); + gtk_label_set( GTK_LABEL( scoreLabel), buf); + } else + gtk_label_set( GTK_LABEL( scoreLabel), ""); +#else + { + gtk_label_set( GTK_LABEL( scoreLabel), ""); + } +#endif + + /* See if their alignment has changed */ + if (lastAlignment != u.ualign.type) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( alignLabel, normalStyle, redStyle); + } + + lastAlignment = u.ualign.type; + /* looks like their alignment has changed -- change out the icon */ + if (u.ualign.type==A_CHAOTIC) { + gtk_label_set( GTK_LABEL( alignLabel), "Chaotic"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(alignPix), chaotic_xpm); + } else if (u.ualign.type==A_NEUTRAL) { + gtk_label_set( GTK_LABEL( alignLabel), "Neutral"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(alignPix), neutral_xpm); + } else { + gtk_label_set( GTK_LABEL( alignLabel), "Lawful"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(alignPix), lawful_xpm); + } + } + + hung=hu_stat[u.uhs]; + if (lastHungr != u.uhs) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( hungerLabel, normalStyle, redStyle); + } + + lastHungr = u.uhs; + if (hung[0]==' ') { + gtk_label_set( GTK_LABEL( hungerLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(hungerPix), nothing_xpm); + } else + if (u.uhs == 0 /* SATIATED */) { + gtk_label_set( GTK_LABEL( hungerLabel), hung); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(hungerPix), satiated_xpm); + } else { + gtk_label_set( GTK_LABEL( hungerLabel), hung); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(hungerPix), hungry_xpm); + } + } + + if (lastConf != Confusion) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( confuLabel, normalStyle, redStyle); + } + + lastConf = Confusion; + if (Confusion) { + gtk_label_set( GTK_LABEL( confuLabel), "Confused"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(confuPix), confused_xpm); + } + else { + gtk_label_set( GTK_LABEL( confuLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(confuPix), nothing_xpm); + } + } + + if (lastBlind != Blind) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( blindLabel, normalStyle, redStyle); + } + + lastBlind = Blind; + if (Blind) { + gtk_label_set( GTK_LABEL( blindLabel), "Blind"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(blindPix), blind_xpm); + } + else { + gtk_label_set( GTK_LABEL( blindLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(blindPix), nothing_xpm); + } + } + if (lastStun != Stunned) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( stunLabel, normalStyle, redStyle); + } + + lastStun = Stunned; + if (Stunned) { + gtk_label_set( GTK_LABEL( stunLabel), "Stun"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(stunPix), stunned_xpm); + } + else { + gtk_label_set( GTK_LABEL( stunLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(stunPix), nothing_xpm); + } + } + + if (lastHalu != Hallucination) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( halluLabel, normalStyle, redStyle); + } + + lastHalu = Hallucination; + if (Hallucination) { + gtk_label_set( GTK_LABEL( halluLabel), "Hallu"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(halluPix), hallu_xpm); + } + else { + gtk_label_set( GTK_LABEL( halluLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(halluPix), nothing_xpm); + } + } + + if (lastSick != Sick) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( sickLabel, normalStyle, redStyle); + } + + lastSick = Sick; + if (Sick) { + if (u.usick_type & SICK_VOMITABLE) { + gtk_label_set( GTK_LABEL( sickLabel), "FoodPois"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(sickPix), sick_fp_xpm); + } else if (u.usick_type & SICK_NONVOMITABLE) { + gtk_label_set( GTK_LABEL( sickLabel), "Ill"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(sickPix), sick_il_xpm); + } else { + gtk_label_set( GTK_LABEL( sickLabel), "FoodPois"); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(sickPix), sick_fp_xpm); + } + } else { + gtk_label_set( GTK_LABEL( sickLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(sickPix), nothing_xpm); + } + } + + enc=enc_stat[near_capacity()]; + if (lastEncumb != near_capacity()) { + if (firstTime==FALSE) { + /* Ok, this changed so add it to the highlighing list */ + ghack_highlight_widget( encumbLabel, normalStyle, redStyle); + } + + lastEncumb = near_capacity(); + switch ( lastEncumb ) { + case 0: + gtk_label_set( GTK_LABEL( encumbLabel), " "); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), nothing_xpm); + break; + case 1: + gtk_label_set( GTK_LABEL( encumbLabel), enc); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), slt_enc_xpm); + break; + case 2: + gtk_label_set( GTK_LABEL( encumbLabel), enc); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), mod_enc_xpm); + break; + case 3: + gtk_label_set( GTK_LABEL( encumbLabel), enc); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), hvy_enc_xpm); + break; + case 4: + gtk_label_set( GTK_LABEL( encumbLabel), enc); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), ext_enc_xpm); + break; + case 5: + gtk_label_set( GTK_LABEL( encumbLabel), enc); + gnome_pixmap_load_xpm_d( GNOME_PIXMAP(encumbPix), ovr_enc_xpm); + } + } + firstTime=FALSE; +} + +static void ghack_fade_highlighting() +{ + GList *item; + Highlight *highlt; + + /* Remove any items from the queue if their time is up */ + for (item = g_list_first( s_HighLightList) ; item ; ) { + highlt = (Highlight*) item->data; + if (highlt) { + if ( highlt->nTurnsLeft <= 0) { + gtk_widget_set_style( GTK_WIDGET( highlt->widget), + highlt->oldStyle); + s_HighLightList = g_list_remove_link(s_HighLightList, item); + g_free( highlt); + g_list_free_1( item); + item = g_list_first( s_HighLightList); + continue; + } else + (highlt->nTurnsLeft)--; + } + if (item) + item=item->next; + else + break; + } +} + +/* Widget changed, so add it to the highlighing list */ +static void ghack_highlight_widget( GtkWidget* widget, GtkStyle* oldStyle, + GtkStyle* newStyle) +{ + Highlight *highlt; + GList *item; + + /* Check if this widget is already in the queue. If so then + * remove it, so we will only have the new entry in the queue */ + for (item = g_list_first( s_HighLightList) ; item ; ) { + highlt = (Highlight*) item->data; + if (highlt) { + if ( highlt->widget == widget) { + s_HighLightList = g_list_remove_link(s_HighLightList, item); + g_free( highlt); + g_list_free_1( item); + break; + } + } + if (item) + item=item->next; + else + break; + } + + /* Ok, now highlight this widget and add it into the fade + * highlighting queue */ + highlt = g_new( Highlight, 1); + highlt->nTurnsLeft=NUM_TURNS_HIGHLIGHTED; + highlt->oldStyle=oldStyle; + highlt->widget=widget; + s_HighLightList = g_list_prepend (s_HighLightList, highlt); + gtk_widget_set_style( GTK_WIDGET( widget), newStyle); + +} + + diff --git a/win/gnome/gnstatus.h b/win/gnome/gnstatus.h new file mode 100644 index 0000000..d5008a5 --- /dev/null +++ b/win/gnome/gnstatus.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)gnstatus.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackStatusWindow_h +#define GnomeHackStatusWindow_h + +#include +#include "config.h" +#include "global.h" + +GtkWidget* ghack_init_status_window (); + +#endif /* GnomeHackStatusWindow_h */ diff --git a/win/gnome/gntext.c b/win/gnome/gntext.c new file mode 100644 index 0000000..275a4a1 --- /dev/null +++ b/win/gnome/gntext.c @@ -0,0 +1,158 @@ +/* SCCS Id: @(#)gntext.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gntext.h" +#include "gnmain.h" +#include + +/* include the standard RIP window (win/X11/rip.xpm) */ +#include "gn_rip.h" + +/* dimensions of the pixmap */ +#define RIP_IMAGE_WIDTH 400 +#define RIP_IMAGE_HEIGHT 200 + +/* dimensions and location of area where we can draw text on the pixmap */ +#define RIP_DRAW_WIDTH 84 +#define RIP_DRAW_HEIGHT 89 +#define RIP_DRAW_X 114 +#define RIP_DRAW_Y 69 + + + +/* Text Window widgets */ +GtkWidget *RIP = NULL; +GtkWidget *RIPlabel = NULL; +GtkWidget *TW_window = NULL; +GnomeLess *gless; + +static int showRIP = 0; + + + + +void ghack_text_window_clear(GtkWidget *widget, gpointer data) +{ + g_assert (gless != NULL); + gtk_editable_delete_text (GTK_EDITABLE (gless->text), 0, 0); +} + +void ghack_text_window_destroy() +{ + TW_window = NULL; +} + +void ghack_text_window_display(GtkWidget *widget, boolean block, + gpointer data) +{ + if(showRIP == 1) { + gtk_widget_show (GTK_WIDGET ( RIP)); + gtk_window_set_title(GTK_WINDOW( TW_window), "Rest In Peace"); + } + + gtk_signal_connect (GTK_OBJECT (TW_window), "destroy", + (GtkSignalFunc) ghack_text_window_destroy, NULL); + if (block) + gnome_dialog_run(GNOME_DIALOG(TW_window)); + else + gnome_dialog_run_and_close(GNOME_DIALOG(TW_window)); + + if(showRIP == 1) { + showRIP = 0; + gtk_widget_hide (GTK_WIDGET ( RIP)); + gtk_window_set_title(GTK_WINDOW(TW_window), "Text Window"); + } +} + +void ghack_text_window_put_string(GtkWidget *widget, int attr, + const char* text, gpointer data) +{ + if(text == NULL) + return; + + /* Don't bother with attributes yet */ + gtk_text_insert (GTK_TEXT (gless->text), NULL, NULL, NULL, text, -1); + gtk_text_insert (GTK_TEXT (gless->text), NULL, NULL, NULL, "\n", -1); +} + + +GtkWidget* ghack_init_text_window ( ) +{ + GtkWidget *pixmap; + if(TW_window) + return(GTK_WIDGET(TW_window)); + + TW_window = gnome_dialog_new("Text Window", GNOME_STOCK_BUTTON_OK, NULL); + gtk_window_set_default_size( GTK_WINDOW(TW_window), 500, 400); + gtk_window_set_policy(GTK_WINDOW(TW_window), TRUE, TRUE, FALSE); + gtk_window_set_title(GTK_WINDOW(TW_window), "Text Window"); + + /* create GNOME pixmap object */ + pixmap = gnome_pixmap_new_from_xpm_d (rip_xpm); + g_assert (pixmap != NULL); + gtk_widget_show (GTK_WIDGET (pixmap)); + + /* create label with our "death message", sized to fit into the + * tombstone */ + RIPlabel = gtk_label_new ("RIP"); + g_assert (RIPlabel != NULL); + /* gtk_label_set_justify is broken? */ + gtk_label_set_justify (GTK_LABEL (RIPlabel), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (RIPlabel), TRUE); + gtk_widget_set_usize (RIPlabel, RIP_DRAW_WIDTH, RIP_DRAW_HEIGHT); + gtk_widget_show (RIPlabel); + + /* create a fixed sized widget for the RIP pixmap */ + RIP = gtk_fixed_new (); + g_assert (RIP != NULL); + gtk_widget_set_usize (RIP, RIP_IMAGE_WIDTH, RIP_IMAGE_HEIGHT); + gtk_fixed_put (GTK_FIXED (RIP), pixmap, 0, 0); + gtk_fixed_put (GTK_FIXED (RIP), RIPlabel, RIP_DRAW_X, RIP_DRAW_Y); + gtk_widget_show (RIP); + gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(TW_window)->vbox), RIP, + TRUE, TRUE, 0); + + /* create a gnome Less widget for the text stuff */ + gless = GNOME_LESS (gnome_less_new ()); + g_assert (gless != NULL); + gtk_widget_show (GTK_WIDGET (gless)); + gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(TW_window)->vbox), + GTK_WIDGET (gless), TRUE, TRUE, 0); + + /* Hook up some signals */ + gtk_signal_connect(GTK_OBJECT(TW_window), "ghack_putstr", + GTK_SIGNAL_FUNC(ghack_text_window_put_string), + NULL); + + gtk_signal_connect(GTK_OBJECT(TW_window), "ghack_clear", + GTK_SIGNAL_FUNC(ghack_text_window_clear), + NULL); + + gtk_signal_connect(GTK_OBJECT(TW_window), "ghack_display", + GTK_SIGNAL_FUNC(ghack_text_window_display), + NULL); + + /* Center the dialog over over parent */ + gnome_dialog_set_parent( GNOME_DIALOG (TW_window), + GTK_WINDOW(ghack_get_main_window()) ); + + gtk_window_set_modal( GTK_WINDOW(TW_window), TRUE); + gtk_widget_show_all(TW_window); + gtk_widget_hide (GTK_WIDGET ( RIP)); + gnome_dialog_close_hides (GNOME_DIALOG (TW_window), TRUE); + + return GTK_WIDGET(TW_window); + +} + + +void ghack_text_window_rip_string( const char* string) +{ + /* This is called to specify that the next message window will + * be a RIP window, which will include this text */ + + showRIP = 1; + gtk_label_set( GTK_LABEL( RIPlabel), string); +} + diff --git a/win/gnome/gntext.h b/win/gnome/gntext.h new file mode 100644 index 0000000..c957dc2 --- /dev/null +++ b/win/gnome/gntext.h @@ -0,0 +1,22 @@ +/* SCCS Id: @(#)gntext.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackTextWindow_h +#define GnomeHackTextWindow_h + +#include +#include "config.h" +#include "global.h" + +GtkWidget* ghack_init_text_window ( ); +void ghack_text_window_clear(GtkWidget *widget, gpointer data); +void ghack_text_window_destroy(); +void ghack_text_window_display(GtkWidget *widget, boolean block, + gpointer data); +void ghack_text_window_put_string(GtkWidget *widget, int attr, + const char* text, gpointer data); +void ghack_text_window_rip_string( const char* ripString); + + +#endif /* GnomeHackTextWindow_h */ diff --git a/win/gnome/gnworn.c b/win/gnome/gnworn.c new file mode 100644 index 0000000..8e45486 --- /dev/null +++ b/win/gnome/gnworn.c @@ -0,0 +1,105 @@ +/* SCCS Id: @(#)gnbind.c 3.4 2002/04/15 */ +/* Copyright (C) 2002, Dylan Alex Simon */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnworn.h" +#include "gnglyph.h" +#include "gnsignal.h" +#include "gnomeprv.h" + +#define WORN_WIDTH 3 +#define WORN_HEIGHT 6 + +#ifdef TOURIST +#define WORN_OBJECT_LIST /* struct obj *[WORN_HEIGHT][WORN_WIDTH] = */ { \ + { uquiver, uarmh, u.twoweap ? NULL : uswapwep }, \ + { u.twoweap ? uswapwep : NULL, ublindf, uwep }, \ + { uleft, uamul, uright }, \ + { uarms, uarmc, uarmg }, \ + { uarmu, uarm, uskin }, \ + { uball, uarmf, uchain } \ +} +#else +#define WORN_OBJECT_LIST /* struct obj *[WORN_HEIGHT][WORN_WIDTH] = */ { \ + { uquiver, uarmh, u.twoweap ? NULL : uswapwep }, \ + { u.twoweap ? uswapwep : NULL, ublindf, uwep }, \ + { uleft, uamul, uright }, \ + { uarms, uarmc, uarmg }, \ + { NULL, uarm, uskin }, \ + { uball, uarmf, uchain } \ +} +#endif + +static GtkWidget *worn_contents[WORN_HEIGHT][WORN_WIDTH]; +static struct obj *last_worn_objects[WORN_HEIGHT][WORN_WIDTH]; + +GdkImlibImage *image_of_worn_object(struct obj *o); +void ghack_worn_display(GtkWidget *win, boolean block, gpointer data); + +GtkWidget* +ghack_init_worn_window() +{ + GtkWidget *top; + GtkWidget *table; + GtkWidget *tablealign; + GtkWidget *label; + int i,j; + + top = gtk_vbox_new(FALSE, 2); + + table = gtk_table_new(WORN_HEIGHT, WORN_WIDTH, TRUE); + for (i = 0; i < WORN_HEIGHT; i++) { + for (j = 0; j < WORN_WIDTH; j++) { + worn_contents[i][j] = + gnome_pixmap_new_from_imlib(image_of_worn_object(NULL)); + last_worn_objects[i][j] = NULL; /* a pointer that will never be */ + gtk_table_attach(GTK_TABLE(table), GTK_WIDGET(worn_contents[i][j]), + j, j+1, i, i+1, 0, 0, 0, 0); + } + } + tablealign = gtk_alignment_new(0.5, 0.0, 0.0, 1.0); + gtk_box_pack_start(GTK_BOX(top), tablealign, FALSE, FALSE, 0); + gtk_container_add(GTK_CONTAINER(tablealign), table); + + label = gtk_label_new("Equipment"); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start(GTK_BOX(top), label, FALSE, FALSE, 0); + + gtk_signal_connect(GTK_OBJECT(top), "ghack_display", + GTK_SIGNAL_FUNC(ghack_worn_display), NULL); + + return top; +} + +GdkImlibImage* +image_of_worn_object(struct obj *o) +{ + int glyph; + GdkImlibImage *im; + + if (o) + glyph = obj_to_glyph(o); + else + glyph = cmap_to_glyph(S_stone); + + im = ghack_image_from_glyph(glyph, FALSE); + + return im; +} + +void +ghack_worn_display(GtkWidget *win, boolean block, gpointer data) +{ + int i, j; + struct obj *worn_objects[WORN_HEIGHT][WORN_WIDTH] = WORN_OBJECT_LIST; + + for (i = 0; i < WORN_HEIGHT; i++) { + for (j = 0; j < WORN_WIDTH; j++) { + if (worn_objects[i][j] != last_worn_objects[i][j]) { + last_worn_objects[i][j] = worn_objects[i][j]; + gnome_pixmap_load_imlib(GNOME_PIXMAP(worn_contents[i][j]), + image_of_worn_object(worn_objects[i][j])); + } + } + } +} diff --git a/win/gnome/gnworn.h b/win/gnome/gnworn.h new file mode 100644 index 0000000..ef384b0 --- /dev/null +++ b/win/gnome/gnworn.h @@ -0,0 +1,14 @@ +/* SCCS Id: @(#)gnbind.c 3.4 2002/04/15 */ +/* Copyright (C) 2002 by Dylan Alex Simon */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackWornWindow_h +#define GnomeHackWornWindow_h + +#include +#include "config.h" +#include "global.h" + +GtkWidget* ghack_init_worn_window(); + +#endif /* GnomeHackWornWindow_h */ diff --git a/win/gnome/gnyesno.c b/win/gnome/gnyesno.c new file mode 100644 index 0000000..ecf3bad --- /dev/null +++ b/win/gnome/gnyesno.c @@ -0,0 +1,76 @@ +/* SCCS Id: @(#)gnyesno.c 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "gnbind.h" +#include "gnyesno.h" + + + +int ghack_yes_no_dialog( const char *question, + const char *choices, int def) +{ + int i=0, ret; + gchar button_name[BUFSZ]; + GtkWidget *box; + GtkWidget* mainWnd=NULL; + + box = gnome_message_box_new ( question, GNOME_MESSAGE_BOX_QUESTION, NULL); + /* add buttons for each choice */ + if (!strcmp(GNOME_STOCK_BUTTON_OK, choices)) { + gnome_dialog_append_button ( GNOME_DIALOG(box), GNOME_STOCK_BUTTON_OK); + gnome_dialog_set_default( GNOME_DIALOG(box), 0); + gnome_dialog_set_accelerator( GNOME_DIALOG(box), 0, 'o', 0); +#if 0 + g_print("Setting accelerator '%c' for button %d\n", 'o', 0); +#endif + } + else { + for( ; choices[i]!='\0'; i++) { + if (choices[i]=='y') { + sprintf( button_name, GNOME_STOCK_BUTTON_YES); + } + else if (choices[i]=='n') { + sprintf( button_name, GNOME_STOCK_BUTTON_NO); + } + else if (choices[i] == 'q') { + sprintf( button_name, "Quit"); + } else { + sprintf( button_name, "%c", choices[i]); + } + if (def==choices[i]) + gnome_dialog_set_default( GNOME_DIALOG(box), i); + gnome_dialog_append_button ( GNOME_DIALOG(box), button_name); + gnome_dialog_set_accelerator( GNOME_DIALOG(box), i, choices[i], 0); +#if 0 + g_print("Setting accelerator '%c' for button %d\n", choices[i], i); +#endif + } + } +#if 0 + /* Perhaps add in a quit game button, like this... */ + gnome_dialog_append_button ( GNOME_DIALOG(box), GNOME_STOCK_BUTTON_CLOSE); + gnome_dialog_set_accelerator( GNOME_DIALOG(box), i, choices[i], 0); + g_print("Setting accelerator '%c' for button %d\n", 'Q', i); +#endif + + gnome_dialog_set_close(GNOME_DIALOG (box), TRUE); + mainWnd = ghack_get_main_window (); + gtk_window_set_modal( GTK_WINDOW(box), TRUE); + gtk_window_set_title( GTK_WINDOW(box), "GnomeHack"); + if ( mainWnd != NULL ) { + gnome_dialog_set_parent (GNOME_DIALOG (box), + GTK_WINDOW ( mainWnd) ); + } + + ret=gnome_dialog_run_and_close ( GNOME_DIALOG (box)); + +#if 0 + g_print("You selected button %d\n", ret); +#endif + + if (ret==-1) + return( '\033'); + else + return( choices[ret]); +} diff --git a/win/gnome/gnyesno.h b/win/gnome/gnyesno.h new file mode 100644 index 0000000..dd0728f --- /dev/null +++ b/win/gnome/gnyesno.h @@ -0,0 +1,12 @@ +/* SCCS Id: @(#)gnyesno.h 3.4 2000/07/16 */ +/* Copyright (C) 1998 by Erik Andersen */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef GnomeHackYesNoDialog_h +#define GnomeHackYesNoDialog_h + +int ghack_yes_no_dialog( const char* szQuestionStr, + const char* szChoicesStr, int nDefault); + + +#endif diff --git a/win/gnome/mapbg.xpm b/win/gnome/mapbg.xpm new file mode 100644 index 0000000..09759b4 --- /dev/null +++ b/win/gnome/mapbg.xpm @@ -0,0 +1,353 @@ +/* XPM */ +static char * mapbg_xpm[] = { +"96 96 254 2", +" c None", +". c #BC6C33", +"+ c #AC6324", +"@ c #B46B2B", +"# c #B47734", +"$ c #AC6B2C", +"% c #B46C3D", +"& c #BC7945", +"* c #A4774B", +"= c #B48643", +"- c #AC7F40", +"; c #AC773A", +"> c #C49054", +", c #C48044", +"' c #B4642C", +") c #B45E21", +"! c #BC642B", +"~ c #BD723E", +"{ c #C4884E", +"] c #C48F4B", +"^ c #C49753", +"/ c #CC9052", +"( c #CC7234", +"_ c #B4804F", +": c #C4985D", +"< c #946232", +"[ c #AC6431", +"} c #C46637", +"| c #945222", +"1 c #8C5435", +"2 c #844C29", +"3 c #945836", +"4 c #AC653D", +"5 c #A45E2B", +"6 c #AC5E2A", +"7 c #A44D21", +"8 c #A45823", +"9 c #A4592C", +"0 c #B46C34", +"a c #B46536", +"b c #AC7240", +"c c #AC7231", +"d c #944C1B", +"e c #8C5223", +"f c #945722", +"g c #AC6B34", +"h c #944C24", +"i c #9C5222", +"j c #B47234", +"k c #C4783B", +"l c #BC7833", +"m c #BC7F3B", +"n c #BC8044", +"o c #BC783C", +"p c #A46332", +"q c #9C6331", +"r c #B4773C", +"s c #B4723D", +"t c #A46B33", +"u c #B48043", +"v c #CC814C", +"w c #B46422", +"x c #C47946", +"y c #C48743", +"z c #BC6D3D", +"A c #BC6536", +"B c #94522E", +"C c #A4643E", +"D c #B45E2F", +"E c #A45223", +"F c #9C4C1B", +"G c #B47845", +"H c #C4804D", +"I c #9C5723", +"J c #AC5E35", +"K c #A45935", +"L c #9B5E29", +"M c #BE7234", +"N c #BC8744", +"O c #AC5924", +"P c #A45E35", +"Q c #9C582C", +"R c #AC501F", +"S c #9C5935", +"T c #8C4D28", +"U c #643D21", +"V c #845437", +"W c #B47E31", +"X c #844126", +"Y c #B45823", +"Z c #954627", +"` c #9C4C24", +" . c #7C4124", +".. c #B4592D", +"+. c #9C5E37", +"@. c #945644", +"#. c #AC6C3E", +"$. c #8C4627", +"%. c #9C643E", +"&. c #744021", +"*. c #A4723C", +"=. c #844628", +"-. c #743819", +";. c #844019", +">. c #944D2D", +",. c #A4512C", +"'. c #B45227", +"). c #C4622A", +"!. c #9C522E", +"~. c #7C3A1A", +"{. c #BC5E24", +"]. c #CC8243", +"^. c #9C664C", +"/. c #AC5934", +"(. c #9C4E2E", +"_. c #7C4B29", +":. c #8C532C", +"<. c #A46423", +"[. c #7C422C", +"}. c #743214", +"|. c #AC592C", +"1. c #8C4224", +"2. c #C47F3C", +"3. c #8C421C", +"4. c #8C410F", +"5. c #8C5D3E", +"6. c #8C5B32", +"7. c #88461A", +"8. c #663626", +"9. c #AC6B4C", +"0. c #94411A", +"a. c #845C30", +"b. c #744B20", +"c. c #7C4D34", +"d. c #CC874A", +"e. c #D49053", +"f. c #C46C32", +"g. c #BC814D", +"h. c #CC7842", +"i. c #A45A44", +"j. c #8C4B1A", +"k. c #843204", +"l. c #945E37", +"m. c #7C564C", +"n. c #74422C", +"o. c #95461B", +"p. c #AC7747", +"q. c #745634", +"r. c #94572C", +"s. c #7C4621", +"t. c #CC7A54", +"u. c #B47654", +"v. c #BC5E2C", +"w. c #B49056", +"x. c #C4915C", +"y. c #B4874E", +"z. c #BC884D", +"A. c #A46C45", +"B. c #D48749", +"C. c #BC5A30", +"D. c #CCA06C", +"E. c #BCA26C", +"F. c #BC8F54", +"G. c #BC895C", +"H. c #BC8F4C", +"I. c #BC995E", +"J. c #C46D3D", +"K. c #D4985A", +"L. c #C4562C", +"M. c #843A16", +"N. c #C49F63", +"O. c #BC9652", +"P. c #B48F4C", +"Q. c #CC915C", +"R. c #BC5824", +"S. c #B49A60", +"T. c #CC9F60", +"U. c #DC995F", +"V. c #AC8A54", +"W. c #B4875F", +"X. c #C4885C", +"Y. c #BC905E", +"Z. c #CC975B", +"`. c #D4A167", +" + c #7C5334", +".+ c #9C6B39", +"++ c #845323", +"@+ c #B46D4C", +"#+ c #A46B3C", +"$+ c #CC9864", +"%+ c #8C4E34", +"&+ c #743A24", +"*+ c #6C3D2E", +"=+ c #C45E30", +"-+ c #AC7E52", +";+ c #9C6C47", +">+ c #6C3C1B", +",+ c #CC8954", +"'+ c #542810", +")+ c #7C5E3C", +"!+ c #84532C", +"~+ c #946C45", +"{+ c #946340", +"]+ c #BCA870", +"^+ c #B49E5C", +"/+ c #C47E5C", +"(+ c #AC8E44", +"_+ c #8C633A", +":+ c #C4A86C", +"<+ c #CCA76E", +"[+ c #CCA961", +"}+ c #7A462D", +"|+ c #8C6644", +"1+ c #744623", +"2+ c #A47248", +"3+ c #94624C", +"4+ c #845E3C", +"5+ c #7C5329", +"6+ c #9C7249", +"7+ c #844D34", +"8+ c #6C4C30", +"9+ c #A46E54", +"0+ c #A4624C", +"a+ c #6C3E24", +"b+ c #5C3224", +"c+ c #D4A66C", +"d+ c #744B30", +"e+ c #BC926C", +"f+ c #AC7954", +"g+ c #AC522C", +"h+ c #6C4526", +"i+ c #A47A54", +"j+ c #BC6644", +"k+ c #BC6E4C", +"l+ c #A48650", +"m+ c #AC825C", +"n+ c #CC6A34", +"o+ c #9C7E5C", +"p+ c #7C623C", +"q+ c #5C3A2C", +"r+ c #644A34", +"s+ c #4C3624", +"t+ c #844B1B", +"u+ c #7C4018", +"v+ c #D49A68", +"w+ c #7C4B1C", +"x+ c #742E04", +"y+ c #CC8157", +"z+ c #745A44", +"A+ c #B46644", +"B+ c #643614", +"C+ c #C4A65C", +"D+ c #843A04", +"E+ c #6C2E14", +". + @ # @ $ % & * = - ; > , ' . ) ! ~ { ] ^ ] / ( ' , _ : < [ } [ | 1 2 3 4 5 ! 6 7 8 ' 8 9 0 a b c d | [ 0 % e 8 f 5 ~ g h i % & % 5 a ' 8 ' a < j k l m { n o . ! 6 . 0 % p p q g r s 0 @ 0 . ", +"j + $ # @ t g s u > u c { v @ ! w ' ~ x y y m u . ) z r > f 5 A [ B 3 2 B C 6 @ D E E 8 F i 6 0 g G 0 o H , j d I I [ & % i f [ . @ . 6 J K 2 3 L $ . M o N # j D ' O % [ 0 p P L g & l 0 0 . % ", +"4 Q 5 % j $ @ g u / G t M k ' R 9 9 J A . M p 3 . O ! M { L 3 4 [ Q S T B Q 6 ' 6 6 J a a 9 9 a c & 0 ~ ~ z 0 L 5 8 5 [ [ d 8 & M . x i B 3 U V 3 4 . l & N m W 6 6 8 % [ g 8 5 q % & o j [ [ 0 ", +"3 X h [ 0 0 0 0 ~ n j p 0 z Y 7 T Z ` Y ! ' Q .a ..! . , t +.@.#.[ C S S 9 [ . D a J a z J 8 % , o + 8 8 [ @ ' J 9 Q 6 5 5 [ x P [ 4 $.1 %.&.V S p ~ o b *.; c 9 6 5 % [ [ 5 p p b & o 0 $ [ 6 ", +"=.-.;.9 a . j j . 0 [ @ z . Y O >.h ,.'.).! !.~.P D {.! ].& ^.T & & s % 4 [ A . 4 /.` Z (.d Q , H #.8 E 8 [ [ [ a 9 /.% z 0 . ~ _.1 :.T S C S | < <.M 0 | 2 T >.Q Q | [ 5 [ p 0 0 % # s j % 5 !.", +"[.}.~.!.' 0 @ @ . 6 O 0 ~ a D Y P K |.... . K 1.8 D {.! 2.y b Q o x % % a 6 . A ,.` 3.1.1.4.i ~ & 5 i d 8 J 9 [ A J |.D a D a a 5.3 .;.h 8 a I 6.f $ g 7.}.1.$.T T T +.Q 0 g 0 % % 0 0 0 % 5 h ", +"_.8.;.E 6 ' ' @ ' O 8 ' @ . a ..+ D 9 9 0 o ~ p 6 ' ! . , n 0 . 0 o [ A a |.a 6 ` 3.Z (.>.$.I 0 #.e h d !.J I C a D E F F 7 |.D 9.>.>.Z 0.9 a 8 a.b.Q [ =.;.!.K 2 T 7.S 5 #.[ 0 g [ g g % g J Q ", +"c.-.;.8 D ) @ w . ' w [ a ~ ~ Y O O 9 S c d.e.d.f.A . H g.# @ h.$ ~ [ % z |.. 6 /.Z ` i.Z d [ o %.j.S P 4 % Q p E 8 O 7 F Z E /.O k.a a 9 & #.l.m.n.B C j.T 5 ) :.:.2 q p % [ [ [ [ [ [ [ 4 p Q ", +"o.F i I 9 [ 0 0 ' 6 8 6 # n o 0 @ ) L W d.].0 [ + @ M k m ; $ @ $ 0 j 0 0 0 0 a c g G p.q f p s ' 0 d !.Q h & M Y '.7 E 7 7 7 R [ @ ) |.[ 4 3 b.q.b.2 L % g P r.j.I 6 a a 6 + [ [ p [ [ g & & I ", +"I d d E 0 ~ 0 6 ' O E 8 j n m . ( j # N 2.@ O !.@ @ ! M r # # M # m n , & r % s m r & o p I [ [ $ @ 7.Q P Q o j f.. O 8 F 7 O ..6 ' ' ..a a q 2 _.s.;.j.F o.o.j.I 5 p O 8 O @ M [ 0 0 g 0 o ~ L ", +"[ 9 F 9 z t.. O ' ) O w r n , ~ A % u.n o w |.|.a ' D ' o o n ~ j l , N n r G _ u 0 #.a |.[ 0 # n r | 9 P 8 0 [ l o o % g [ D 6 7 O ..Y a A 4 p C #.s ' O 6 D a g % 0 a [ 6 [ 0 @ , k o o n o c ", +"[ [ [ ' a . a |.8 O Y 0 o ~ . v./.r.S #.z . D D 5 6 ..! s & o ~ # o m r u _ w.x.y.p.[ J 6 % y z.] n p g a 6 @ [ # m y { m ~ @ @ 9 |.D ' A ~ % [ A.G x ~ ' ! 0 z 0 0 0 % . 0 [ [ [ o o $ M # 0 o ", +"g s ~ 0 0 a ' ' O ' ~ B.{ o ' C./.$.;.B ' A R ` Z ,...A . o & ~ y m N N ] : : D.E.F.G.g.G _ H.I.y N s % 0 a z M r u m & o o m m & & ~ ~ l j 0 <.t s G j $ 0 # j % [ 8 [ 0 % . ~ @ o o 0 r j # y ", +"o o l s o & , x 0 J.B.e.K., . L.a >.M.d 6 ! E 3.M.o...! . & n r 2.y n { > : : F.I.I.N.: y.F.O.P.n o a ' D 6 ' 0 $ c % o & o o m & G # j # r c [ q p L I I L <.p . ' [ 0 o % 0 % o k o o o o o , ", +"k n o o & { / Q., l ~ , { m ! R.D !.h 6 . . ) P 7.i ..w M m { n m n N z.z.z.y.= y.z.I.S.w.I.x.g.o ' 6 Y R O 8 [ [ a [ a z o r # s j j j @ 0 . 0 ~ . 0 0 0 0 0 o ! . o H o [ [ a g 6 [ s 0 g g g ", +"@ o o r c u > T.U.H # n ] n J.).D 6 0 k ( M ~ & +.[ D w l y / g.{ { { { N z.] > y z.w.w.V.W.X.& ~ 6 Y R Y ! ' 0 ~ a E 7 9 0 m n o k k k . ' D } D D A z z ' [ A 6 ' 0 0 p L [ x a I [ & & & s g ", +"I E D ! 0 s z.F.T.H.o o h.d., c j m / Y.Z.`.n t ' ' $ , y y N m m y y ] { z.n N n { ; > H.r / z.~ p E O D 6 ' . @ 5 i d F 5 g r [ j o 0 g % 0 [ ' 6 5 p I 4.4.o.o.d e U +c.6..+[ i j d.# m { p ", +"0 5 i O a g.Q.F.T.^ N o & ].n @ # m { b p.Q.N c ! a M , / { m m W m N z.z.n N y n { ; z.] u ] r 0 + 8 ..a D w @ @ ' 8 d T r.g r @ 0 g [ <.[ [ p 5 5 [ p Q Q 5 z 6 p :.b.V _.l.p s 5 0 m @ # { G ", +"H ~ [ a & H g._ > Z.: ] z.] , o . o ~ f Q H H j . . x y d., 2.o y N z.z.z.N n & { { ; z.> u { s 0 a D 6 v.a ' w Y O >. .&.2 q $ g 0 [ 5 5 [ [ [ 0 [ | 7.>.Q 9 h !.P ++2 5.=.4 @ C | Q P Q 5 0 k ", +"H , s % @+#.#+#+#.z.$+N.: Z.H k 0 a a E 9 & x ~ z k x , , l M k y ] > ^ > G.n m { N u ] ^ { y s k . a |.6 ! ' + {.|.%+&+*+V p l [ $ @ [ $ 0 0 p % S 7.$.B C Q T ;.3 T 2 @.%+a @ +.T $.>.K 9 [ k ", +"n & q B +.+.S p :.#+y.Y.w.H.{ , J.a a /.a z a o x k , o ~ . . f.# n ] : Z.: > { o n u > ^ { n o ~ J.a 9 6 0 . @ =+O T 8.8.1 [ 0 $ 0 s j % % g 0 +.Q S !.B h h f ~.4 t %.3 3 4 6 r.T X $.i.J a k ", +"~ % B e ^.%.e T 3 q * -+_ u H / x a a a A 0 [ & , { , n l M f.. . o z.> : : ] H o n N F.z.n o J.~ a [ [ 0 % M M w |.Z }.-.T L O j ~ o 0 $ [ [ % p 5 9 K i j.j.| 4.% G p 1 S B r.| T X Z K a [ . ", +"' % +.l.* ;+2 2 ;+%.q s b b u x.x % 0 . 0 M j n N z.y n o o f.! ).. & z.F.> z.n n n z.H.z.n 0 . a [ [ b & r g j @ 0 [ >.` K a ' 0 r o % [ 5 5 g $ p 8 9 a ~ o # D @ o #+T @.>+1 K 5 5 [ % ~ z J ", +"E a +.1 ;+;+6.A.* %.+.@+% p #+= #.g 0 0 M , r y u N N n o k . {.R.v.0 u G.F.{ z.,+y > H.H.G ' ' @ [ g n z.# $ <.<.0 a |.E a A 0 <.0 x o g L 8 [ <.% 0 8 8 @ k m B.M o g V 1 '+)+h Q @ j M 0 J o.", +"_..+^.!+6.l.l.l.~+;+6.V {+:.:.A.r.e q G F.N.]+]+^+I.^ K.d.' 6 z @ M % a /+{ y.H.(+T.^ u ,+2.a ' [ <.t y.x.y.b t L $ @ ' [ 0 # g j j r % [ I p g [ 0 O Y ) w 2.y l j $ @ 5 I P i I <.0 0 0 0 5 e ", +":.%.< :.1 {+l.3 ~+;+_+6.l.!+s.5.3 T q g.> > :+<+:+: H.y ].. ) A + ' 6 9 [ G z.> P.[+^ N { d.0 . . @ s g.z.n 0 @ j o o 0 @ M r # $ j o #.[ <.g #.j . 6 . . 0 , y j l k , g 8 p 5 [ g a z % a 5 i ", +"A.%.%.+.l.C #+%.{+~+{+{+5.}+n.}+3 6.#.H { z.> T.<+Z.n o , ~ ! a w ' D 9 !.[ n ] O.<+N.= ] y o l M . o o & s 0 . n n o 0 $ j r # $ j s 0 #.s s # g [ 9 0 z 5 g #+2.o , ,+d.H & 0 s % a a a [ J 5 ", +"b S %.C +.^.9.A.5.{+~+^.|+1+*+n.c.1 #.H x o H ,+> { n s x k . . . ! A A O a v y I.[+N.N H.y m 2.h.l l & o r ~ o & m r g g s o G j g b G G r r & r #.[ % 4 | S q o 5 <.~ , d.o $ z a 6 a a /.4 a ", +"#.3 +.%.r.+.2+b 5.5.3+;+5.c.n.n.8.T #.~ 0 z x & % & & o x M . z ! D A A D o d.d.: T.: H.z.] y ] 2.m n n z.N N n m G r # r r m r n g c g.{ G j o H r [ #.9 7.e q [ | [ $ g o o o % a [ J [ [ a a ", +"C p %.C {+l.;+;+{+4+3+;+l.%+2 .2 Q @+z J 6 K Q [ M & & , s 0 l ~ ' a ' ' o , n F.F.> H.m N N m N N u y.z.H.z.N n n n n z.n G o , s r G.F.r # & o & [ g L j.Q p o 5 0 [ 0 , o x M . s g % % % a ", +"C A.#+A.A.;+5.1 ~+5.{+;+^.S +.Q #.g ~ x 6 h $.=.. [ [ r g.o # , , H o 0 j o m N = N { N u u m r N N N n _ u = = & u u _ N _ n r & & n z.z.n o # x x j o o $ o r ].L + [ 0 d.g g 0 n , n u n % p ", +"q p.2+A.* ;+5+1+6+a.< A.#+#+~ g v ' . h.a $. .7+. + I #.{ { m / H e.{ k d.y N ] N N ] / n n y m N { { { G G u _ r b r u = u G r ~ g.{ u y.z.n @ 0 0 0 o 2.y ].m , + , o s , 0 & @ , { N , y o 5 ", +"k @ j j . H & 6.a.6+a.6+;+s ~ [ 2.j C C !.j.b.8+[ K j.l r H.N.P.= z.K.N Z.; N > r z.H.N ] r t o u u n n u u u V.<.G I p ;.g g u = V.H.u { # o g $ g G z.z.m m , N = = ] # y m # W <.o ~ ; t r t ", +". + $ [ ' ~ & .+l.;+l.2+9+#.~ a n q 1 2 T &.1+q.C [ % { y T.^ I.> z./ o e.r n z.# u N N { u # l u u m r r r p.u s % [ J I 5 r { z.u > u { r r 0 j ~ b g t #.o d.H.y N K.y { o r n g k o g.N { j ", +"D 9 + + O ' % b S 0+@.9+C C 4 z A.}+a+b+V &._.7+e p { { { c+F.<+Z.N n $ y 0 ~ % j $ r y n # ~ 0 u m n G r s G n & $ % 5 4 j.t n F.G.] g.{ r n 0 d.n p $.X I # 2.y n u y g 0 0 , 2.' . % & { e.. ", +"..|.6 a D D a j S @.2 3+1 5.S #.S [.d+U l.T +.B e b X.g.> D.F.N.x.z.r 6 M D ' |.0 [ o , r j ~ $ r n g.& & r r & s p % P % j.<.p.> z.z._ / n n o y & S ~.~.Q j m / H H v 5 I |.x M ' A /.5 s x a ", +"C./.9 9 D v.' . %.7+a+c.d+c.2 q !.1 3+2 #+9 % S u.Z.G.y.e+x.z.f+> { & [ . ....... 0 , v % . ~ ' [ b & n & s ~ s t #.& G & b _ g.z._ z.g./ g.{ H m g Q X >.s { y 5 L p , [ a [ x . ! A |.4.Q z {.", +"..g+d 0.7 ..' z %.c.U }+h+5+s.l.$.%+3 h ' ' z g G.$+-+_ Y.* f+r._ H & w D O ..D A z ~ o j % 0 a I [ s G r s b s G x.H X.G X.G.z.G.= G.z.x.z.> > z.u #.C b H > N <.| d % % h.~ ~ . } v./.~.d D v.", +"..|.d ;.F ..D . C T _.c.[.B $.P }+ .%+K z ' s _ z.i+-+i+_ 2+;+e t r z [ 6 |.|.A 6 a D [ 0 ~ 0 [ E [ s g.G G r u > : z._ p.G.F.P.Y.F.F.F.: y.> Z.Z.> g.n g.> ] > U.,+o , [ ~ a ! f.{...J 3.| D ! ", +"v.J.p h /.A A J.i.B B @.=.i.(.j+a.c.@.k+t.~ A.l+F..+e+m+W.f+;+:.P s % ' D a a z ..D 8 I ~ & 0 a /.@+x z.z.= y.z.x.y.p.A.b y.I.N.: Y.N.: : y.F.N.x.F.N y ] ] : T.r n n , 5 5 [ ' M ! /.k+B 4 . n+", +"] u 6 R ! A z g.I #.=.%+l.%+J a B T p [ & C k+g.u ; g.e+o+p+ +3 {+q #.[ 6 . o g [ H [ n { > N j D 6 a & { : I.w._ _ !+_.+.9.Y.N.]+S.I.F.Z.u n n u u - H.Z.N # y I l o u j j m l l l + $ K.] u : ", +"z.m @ ' f.~ ~ g.& & :.1 @.S % a %.+.C % @+[ u.& u b _ m+~+)+_+^.#+#.% @ [ . & c s d.g.> ] { z.N ~ z s u G.x.O.F.G -+c.&.1 ^.y.I.]+S.N.G.> n n N ] = ; W Z.] u n @ n o m n m , $ l 2.j k e.{ N Z.", +"n , j . k o n > { G e T 3 C H a P 0+J % [ [ % & n G _ f+|+4+2+f+p 4 % a ' ~ , s & % { { F.N z.z., n & n z.z.F.y.s 9+b.U c.@._ O.S.I.N.G.H.n { { N ] H.= H.y m y , , s j g j & . j M . M & [ 5 r ", +"n o j . o y z.T.,+_ r.:.:.#.,+a 3 K !.4 8 a % x u G u.A._+|+9+#.4 #.z a [ ~ x % G f u u > ^ z.N N u u g.z.z.g.G p 3 8+q+*+V p.] w.I.T.z.G.z.{ { u ] Z.> z.= r #.~ ].r g <.<.~ o ~ x k z A O i a ", +"& o j @ , N ] : K.{ p.#+p #.H g Q P Q a 6 ~ ~ H _ g.& _ p.-+p.#+% % @+0 0 % & #.& | G y.Z.> H.y.> z.u u g.g.G b i 2 r+s+8.7+#+r -+F.T.F.> g.{ g.> ] = u y.g.t t++ & & , r g j 0 o ~ z z z J D z ", +"0 o o o n & _ G.x.> { _ G g.H & #.s % s z H H & W.u.g.X.G.e+g.A.i.3 p % % 0 ~ z ~ #.g.> > y.u n Z.> u G r r s #.S 2 +}+n.3 C c u z.Z.> > g.{ & > N y.u p.u #.| g ~ r n H & & 0 r b g a a 6 ' a ", +"g o x o @+p +.@+= G.> { ,+{ g.{ g.g., o H v H #.6.1 %.u.-+i+2+l.:.u+!+#.s g M x % & G g.g.G ; u { z.{ n s % g 0 S 2 %.l.Q @+r G G u { G.> _ n G N u > > u p.u.g & G ; r u r u s F.= n & 0 . a a ", +"g o n % 9 $.>.0+z.> Z.Q.v+,+n ,+z.n n 0 , & u.I 8+&.1 A.* i+;++.5.&.++b r [ @ ~ % /+5 G g.g.n G c r { H & [ [ p 3 2 C t a ].{ u g.G z.z.: r _ & > u G.x.z._ b r.b n z.> N b G r P.- u n o ~ M 0 ", +";.P 9 ~ #.u+7.6.t u F.y.g.o k m . . a ~ I ~ j p ++s.w+;+{+< _ ;+7+l.2 _ 4 s ' M c c # & s g 0 k @ o u u r g 8 |.2 &.t+b P.w.y.r ; { z.H.; z.o s t ; z.: F.z.n ; # m r t #.c r & u H.; y o ~ ~ Q ", +"h T x+K & | r.r.% & y.u s s z s a 9 p [ g & n <.l.>+&.b 2+%.p.f+V 1 3 #.% g % o m u n & & % $ [ p 0 r r n g 9 6 +.e Q u F.P.N r n n ; = u { r s ; r y.x.y.N u # u N o c s s G g.] Z.= ] % a J T ", +"X S 3.j.9 K 4 Q A s s % % a 4 +.+.e Q r.G & d.g b 7.:.p.G p.b p.6.6.4 [ x % > z.z.{ { { y n o % $ j r u g.r [ a 3 Q 4 & _ y.n % p.n u g.; ; s H , n z.{ z.g.n o o , n s r & G H = F.= H 9 7 (.T ", +"~.P S 3.0.i a K J.~ & & u.@+^.l.l.1 3 e #.G { & u.r.C #.p.H G c .+t % J y+u.T.y.G.g.F.] { r & H ~ o G , ,+& [ [ T K % G ; - & g #.#.s g.s #.s ,+y 2.{ H r b & r #.& o s r o & n z.] z.{ 9 /.,.(.", +"S 1.M.,.|./.a a a u.n g.@++.c.8+V {+6.l.< G { / G +.p q #.{ r s u m % % x g.Y.l+X.#.#.u n g [ k+o ~ g & { & [ [ S a k+x p.G & 4 #.r.3 #.& & @+s r s & r 5 | Q p 9 #.s s o n G H H.F.z.n E ,.Z Z ", +"4 ` ` |.A ~ z J P #.s r t e q+s+c.~+5.q l.g.g.K.{ % t % s b 4 s r , 0 n H : : w./+r.7.+.; #.[ . s [ I [ & G p 4 S 4 k+n G u g.% 3 t+T Q u.#.#.t C p g % | 4.7.T >.C s r n N n z.F.z.z.G 7 ..Z >.", +"J a J.D |.J.~ E 4 C #.o z +.d+r+!+;+%.%.p.g.g.Q.K.X.r G s +.Q %.a o b > : T.T.: ~ Q u+=.P @+~ f.s [ i <.& & @+r | !.[ s G u & @+2 s.:.B #.p #+t 3 !.C a S h B :.$.P G n ] z.N F.Z.x.^ { [ } ..a ", +"D R D D O a 6 h (.h i ' a 4 5.z+6.%.A..+G.z.g.u z.K.G #.%.r.r.T 6 ~ n Z.N.F.> { . [ >.X T J . f.H g 8 #.y+H g.H 3 i 9 % ; p.b [ 6.s.=.T #.#.#+l.=.X 3 4 [ +.Q 3 $.C g.{ ^ ^ H.F.x.F.: 2.8 v.R [ ", +"E a D 8 8 6 5 I 5+5+!+B [ ~ C 5+#+* L u.#.s p #.0 [ #.3 +.1 1 =.p u.r Z./ n G H % 5 B 2 T L 0 ! $ l W m { n s o p.b g G ~ #.P +.l.w+w+f .+G r L 1+s.f K [ 6 9 o a P g & m m g.z.u m { , 0 O D ..", +"R |.O E 8 ' [ [ ++!+1 Q ..a [ {+G -+r.#+4 #+p g 8 5 C e 1 2 2 =.q r #.Q.{ r #.z.% p q L +.#.~ . j 2.m 2.{ { n l b p.n H g 5 +.#.b T j.+.% 0 0 @ 2 T S 5 [ 6 I 4 a 6 0 x & r n g.m u m G @ [ a |.", +"D D O O D 0 z ~ Q S K /...D % & _ f+f +.p #.p +.| +.C 2 2 2 5.2 p s g e.{ # c H & % #.b t #.4 ` K % m o g.H g.# p.s s % #.p S S K S J 4 x a [ D r.r.q [ % a i 5 a 6 0 o g [ #.#.o # 0 0 0 z . 6 ", +"A ..J D J J z ~ J P J a A ! z g.2+A.e P % @+C r.T l.A.7+2 7++.:.p G g / , n & H /+& u ] / Q.@+1.9 ~ ,+g.G z.,+y & p.#.g g 4 9 T 1.P @+/.~ 4 D 9 +.r.P [ o ~ I 8 a [ a 0 [ 5 P [ 0 g 0 g z , , [ ", +"v.O D 4 (.` /.a [ S 3 J . a #.A.l.C Q 5 k+s A.:.=.3 ^.s._.2 3 r.I ~ 0 m o , o s +.B e t z.Q.u.$.1.[ & <.j.r.G # s g.g.r #.[ P !.2 %.#.B C S P 4 P 9 p [ , & 5 8 [ p [ 0 [ [ a a ~ ' [ @ ~ v x 0 ", +"v./.D P Z h K 7 P r.T p s ~ q :.6.#+C 9 z C C :._.1 l.s.2 :.q !.Q ~ [ o k , ~ [ C B e L g.`.v+G @+H / #.t+Q & , s & n o r s [ d !+!+1 < 3 7.j.C [ /.[ g H , [ I S I p [ [ % z z % [ 0 z ~ k k 0 ", +"! |.P S X B J ` /.K S C z & q _.:.#+% |.4 Q +.V T 7+1 2 3 S [ 5 9 [ Q ~ H v % [ +.r.3 r.+._ : z.g.n y u I p ~ ' g o o r n , a d 2 s.2 b 3 T ;.C D J [ g H H [ 9 T T f [ g . % [ [ 5 ' o . 0 . . ", +"' E !.T u+S 4 h g+/.K J z z S . .C & D a >.S 7+%+2 1 2 +.p [ 5 i (.4.~ v x [ 0 +.C 9.+.3 b H.H.,+N ] / G x x 8 8 l Z./ m o ~ 6 :.T T 9.$.+.K A+/.|.[ g , ,+g 5 2 2 +.g % o 0 8 I | [ k % ) A k ", +"O O 8 Z h K p p <.+.r.B Q q :.&.&+S @+#.9 B S {+I f r.T 2 l.l.e ;.q 9 x x k ~ 9 q.B+1 0+S C s / 2.N ^ C+O.; [ v.Y 0 M ,+m n [ ' I I 5 % 4 t r r [ [ 0 0 , n { ; R O @ p.; #.p 9 7.Q #.% a O O z ", +"i 9 9 i !.L S r.< 3 >.:.Q +.:. .}+3 4 4 6 Q Q +.$ Q L 3 1 :.r.e | p 5 ~ 0 s [ 5 5.s.c.^.0+r.p n H m u H.z.j @ J.+ [ t r r n o k [ i S 4 #.s r # j 0 o 0 , o n n . ' <.p p r.!.Q Q P [ 4 % [ !.9 ", +"2 B P J 9 S :.c._.2 =.%+P #.4 @.T >.P 4 J 5 P %.[ % C 3 2 B l.T 9 [ [ . 6 z [ #._.>+'+c.{+_.Q #.,+# j m N r o , e./ ] u = n n s [ 3.3.P u.g.n N o c o 0 ~ g n { , 0 [ a [ B $.2 d Q Q Q C C B T ", +"&.1 P [ 6 3 2 1+c.V %+3 4 @+P >.%+%+B J [ K 9 P a P +.B 2 :.%.%.a [ z . 9 % r.C i.@.>+ +|+2 >.6 r M j ~ x d.z.N > Z.T.z.^ > / n J ;.u+Q u.{ { N G 0 % 0 0 5 0 n m j . a ..!.T u+7.5 4 r.>.1 2 =.", +"h+7++.J +.r.c.d+_.!+1 C #.o % S T =.>.S K Q Q J 4 7.X 1 1 2 C H ~ % x z 5 [ T B 0+3 V 1+4+l.B a <.0 z ' . , y r - N Z.= H.z./ z.& S | #.G n z.u N j o 0 o O [ [ N , % E ` !.S :.I % @+Q 2 _.n.n.", +"n.7+3 3 r.B !+V b.++3 +.j o 0 S =.2 T B >.>.(.K $.T _.&._.A.& ~ b s % & g #.L q d 2 1 a+u+%.I % 6 M ~ w [ o , r n { > n u u N z.,+G s G g.y.z.z.n r r 0 . 8 8 I o d.& |.i J 4 S 6 g p B 1 2 n.a+", +"=.=.7+V 6.1 1 r.l.{+S +.5 p r.;.c.7+7+2 T >.>.(. .1 +U _._ ,+6 l.C L s s & g.X.s #.#.C Q p g $ . J.. @ [ . ~ . 0 o o n n z.z.> > , n z.n z.> z.n r r [ 0 O 6 E + o , G & n % I [ [ !.T :.7+}+}+", +"$.=._. +V 6.3 9 !+1 %+%+r.C S %+_.7+7+[.=.B (.T 1 n.h+5+|+p.o @ ++l.s.t #.& X.Q.N g.x y+k+0 ,+W A ' @ 0 0 + D ) O [ [ x 0 u ; u y N N u - z.F._ u r o [ ' ' . . E [ r N { y 0 R % 4 p r.B =. .7+", +"2 s.7+1 &+1 .:._.1+1+2 1 %+7+1.5+2 X $.[.n._.3 >.Q i [ M g #.p 2 e :.l.f #.[ a g p #.% 0 % z . $ m 8 x |.v.' w *.$ # @ j l p f + , k s d f z.n u ; o # $ o l 2.m $ <.j x o [ L @ j % [ h $.P 1.", +"1 2 2 %+ .S T :._._._.2 7+2 %+@.6.T %+X [.n. .T B K 8 6 [ 0 #.p 2 2 !+e T #+Q 9 t p g % [ ' a 5 r.<.I . /.a 6 w p #.~ M z ~ [ 9 $ r g.g f t n z., u m , c , # 2.2.l j s s [ <.[ $ j s % | >.K >.", +"S T T T $.%.3 T c.c.2 _.}+=.1 +.+.S B }+n.h+s.1.!.Q 6 |.' o #.p 2 1 :.2 2 #.3 | r.p #.a 9 [ [ I &.2 <.' a A J [ d Q J z z z A+a g s { #.C _ b G.m & r { $ y g 0 j o o g <.[ s o $ & r & 4 L p S ", +"p Q S >.>.C P | 2 2 V 2 2 2 3 C P 4 S 2 h+1+7+Z 3 ` |.D J.~ [ p 1 3 :.5+2 2++.T :.p @+[ 9 J [ f 8.>+t + . a 9 P 0.` F |.a D a D I #.& A.G _ *.n n s s n 0 , g @ t s s #.p g r o s & s o & g [ g ", +"a [ J K S S 4 r.%+1 3 3 3 S %.C 9 a J %+&.2 S /.+.>.E D } z K S B 3 :._._.A.1 2 3 #.& a 9 9 C r.b+>+#.5 0 J Q B 0 a 8 a z . . a 4.P L #+f+b G g.x g x % & & , m 0 o s [ p #.t <.#.G $ s m % g p ", +"5 P 4 P Q T +.Q S P P 4 4 #.p 9 B K D i T S J A+#.0+E D A ..4 q S S 3 2 s.%.7+2 1 +.@+a /.[ #.l.8.1+#.8 6 !.>.2 6 i D+| I F + 8 E #.L #.p.t n & & P & 5 o s H n o s % C p q q f @+#.g r r s b p ", +"8 9 P J S M.+.S P [ % 4 [ 4 J Q j.Q ..'.|.D a j+l.u.9 D D E s +.J C p 3 2 3 2 %+2 >.K a a #.4 l.>+1 P O ,.Z r.w+S Q -.r.p L & . z a [ G G ; o j a i a Q 5 p o G M j $ p S r.3 %.t #.s m s b & S ", +"9 i 4 4 0+u+%.%.' % . [ 6 |.9 Q w+(.7 L.=+A A ) >+p.J D |.` G T 4 % 4 +.2 1 [.1 3 :.!.a % 4 l.s.&.q 9 /.,.$.r.++T =.E+2 @.T #.% } F % G & { s @ P ` K I d p [ % l M g q | 2 r.A.+.t G n t #.u.B ", +"t r.J @+P B 9 ' a [ J |.J p B ;.T Q [ D v.. . + ; l w . 4 T #+e t #+6.!+!+:.f P K S r.S 0 % S &+s.f P Q ` Q 5 Q <.+ | <.0 5 7.l.P I 8 ' . @ 0 % a Q i i ` [ % I 6 & P #.| >+3 S !+p q c b - C C ", +"t r.P A+q p % A % [ [ 9 6 P S =.T Q a D A M k m m 2.@ f.P T #+q 4 [ r.T 3 S P [ K S S #.% 4 B }+e L +.Q ` 9 P 6 # l j k o g t+| P !.8 D ' 6 J [ [ 8 9 5 5 5 6 I i 0 g r q u+r.@.6.p p 0 b & #.a ", +"#+q J 4 P #+% a [ J O 8 9 !.>.2 ` 9 /...! k ].].y / M M 5 T #.[ z |.K =.C K A+z P S +.4 [ !.>.7+3 3 | ` ` 8 6 a j o l l 0 s p C 9 ` 7 ,.|.8 9 |.K 9 5 a @+6 i 9 i @ j o t j.:.e ++S 9 P p g [ A ", +"q C J P p % % |.' [ 6 9 !.| >.T ,.9 |.|.' . k 2.N 2.k k P T 4 g z J p ;.C B 4 A+[ 5 +.P 8 ` >.V T T h (.8 |.D z y 2.o k $ o g I ,.` 7 ,.9 g+9 |.|.[ [ a z [ i 9 I [ [ 0 L T r.:.1 +.S Q +.q P A+", +":.+.J P #.g.% a a a [ P 9 Q !.S |./.|.|.D z M ~ r o h.k [ Q C [ s p #+s.3 s.3 +.5 #.4 9 O 6 3 7+[.T Q |.6 a A A #.o 2.B., d.~ L ,.,.,.K /.K g+/.8 a 6 9 4 4 Q Q Q 9 d h h T 1 V 5+6.1 2 _._.++3 ", +":.C A+4 s & ~ a a p P P K K 9 |.,.,.,.|.D a ' a [ @ f.k #.C [ [ s #+%.b.c.h+_.:.| C 4 6 J a !.&.X T K 5 6 a [ 9 f p [ ~ k y+o a 9 K K K K 9 9 |.6 a |.i 9 [ 9 | Z $.0.0.Z >.%+s.b.++e &.h+U h+_.", +"1 #.k+#.s r #.z Q Q B r.K 9 O ..8 !.E 9 O |.Y Y a O . . b b b g #.#+l._.d+&.&.2 | 9 4 J ..D B &.$.B I i 5 g p T Q 4 8 [ 8 [ I [ 9 K 9 Q Q Q 9 [ ' [ [ 6 9 S Q h h !.(.` h >.=.s.++l.+.s.c.U 8+c.", +"_.4 @+[ s b a x S T :.S P 6 D v.J K |.|.|...g+'.A O v.. #._ G r a C B }+[.}+ .7+I +.p D D |.3 V B S I d 5 G 4 2 7.C 5 a a ' i % 8 9 Q :.| I p ' . 6 A ~ P I Q B p 6 [ E F d t+w+t+h K .1+b+*+}+", +"r.q S f q r z.- !.i Q L g ' ! ).O A O z [ 4.+ ' #.I z ..a K r.#+| T 2 i.[ i [ L h i 7 J I f | Q ` 5 + 0 @ c b f e $ c m j H r #.Q I f [ Q Q 0+T r.q t % [ 6 |...p t m y r L T ;.t+:.r.p u+&.h+q.", +"< A.b p g u _ - #.p 5 6 0 . . ! 6 |.d 6 % 6 . O 5 d 9 9 J Q Q q [ S %+C P E [ f T P 9 9 h p +.S 6 6 5 j j j & g o , j $ I g G v #+#.f 5 [ #.g u+f p n r @ [ a a g 0 r o c [ Q T 7.T K a Q | e 6.", +"#+G g._ p.u N u f+#.p 6 A . . @ @ [ g % % a A A [ K Q r.r.;.p @+~ C r.S K 8 9 B u+Q ,.` ;.l.I h 8 6 6 2.v # o s . z 0 o o & p | f G & s s s G [ r # 2.<.l l , 0 & n o j c #.p h $.3.` /.a 4 a C ", +"f+g.z.n u u y.z.u r a v.A M o # o o , & a . a } z x G p.< u+4 t.~ p r.S 9 ,.Q T ;.P /.8 7.q +.B 8 ' ' , ,+o z g D 8 E 0 x o 5 T D+u.,+].o 0 l { ,+0 @ I k o n p g & o g j ~ #.5 3 1.h F /.J x ~ ", +"n u G G u n z.] y.n o . f.k 2.m o j s 0 a % ....a x x.x.G.f % j+0 Q r.S 9 6 9 h T P /.J Q q Q K ' J.[ [ o ~ . ' ,.,.|.6 a 6 4 z r.[ % 0 o l $ n s 5 0 8 o I Q e d [ #.g o n & ~ 2+Q q h p [ & o ", +"& r b c u n N n N , x M ~ k 2.m j M ~ s [ ` 8 |.z % & z.Z.% z /.0 I S S /.z a Q B 9 g+a Q 5 B K D z I I g #.5 F d (.a [ % [ a a #.Q I 5 z o j y & p [ 6 ~ !.B T j.[ 0 g x & ~ , g.p.G #.g c o j ", +"~ @ j r m G o o m m # j 2.d.2.@ @ 2.& v % 7.6 ~ x + 0 s v & a a a Q +.P J ~ ' g J J '.A 6 % [ z 9 [ h T #.P F 4.K S 9 d 6 J % D C Q % 0 [ M & { / & 0 0 ~ 0 [ +.9 @+[ g x o $ o r & G g.#.o o c ", +". @ j n , r 0 0 x l # # y K.].. M ~ <.y+H 5 & x 2.0 ].x ].z O ' a B P S O ~ . g 5 ..R ..O % a A z 4 u+7.A.+.` E :.p u.9 P 8 A+a 9 9 v ~ [ s % % N ] B.B.k z g g 4 s J [ & M [ o + 0 0 & 0 & l @ "}; diff --git a/win/share/gifread.c b/win/share/gifread.c new file mode 100644 index 0000000..9757227 --- /dev/null +++ b/win/share/gifread.c @@ -0,0 +1,706 @@ +/* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing + * following copyright notice: + */ + +/* +-------------------------------------------------------------------+ */ +/* | Copyright 1990, David Koblas. | */ +/* | Permission to use, copy, modify, and distribute this software | */ +/* | and its documentation for any purpose and without fee is hereby | */ +/* | granted, provided that the above copyright notice appear in all | */ +/* | copies and that both that copyright notice and this permission | */ +/* | notice appear in supporting documentation. This software is | */ +/* | provided "as is" without express or implied warranty. | */ +/* +-------------------------------------------------------------------+ */ + + +#include "config.h" +#include "tile.h" + +#ifndef MONITOR_HEAP +extern long *FDECL(alloc, (unsigned int)); +#endif + +#define PPM_ASSIGN(p,red,grn,blu) do { (p).r = (red); (p).g = (grn); (p).b = (blu); } while ( 0 ) + +#define MAX_LWZ_BITS 12 + +#define INTERLACE 0x40 +#define LOCALCOLORMAP 0x80 +#define BitSet(byte, bit) (((byte) & (bit)) == (bit)) + +#define ReadOK(file,buffer,len) (fread((genericptr_t)buffer, (int)len, 1, file) != 0) + +#define LM_to_uint(a,b) (((b)<<8)|(a)) + +struct gifscreen { + int Width; + int Height; + int Colors; + int ColorResolution; + int Background; + int AspectRatio; + int Interlace; +} GifScreen; + +struct { + int transparent; + int delayTime; + int inputFlag; + int disposal; +} Gif89 = { -1, -1, -1, 0 }; + +int ZeroDataBlock = FALSE; + +static FILE *gif_file; +static int tiles_across, tiles_down, curr_tiles_across, curr_tiles_down; +static pixel **image; +static unsigned char input_code_size; + +static int FDECL(GetDataBlock, (FILE *fd, unsigned char *buf)); +static void FDECL(DoExtension, (FILE *fd, int label)); +static boolean FDECL(ReadColorMap, (FILE *fd, int number)); +static void FDECL(read_header, (FILE *fd)); +static int FDECL(GetCode, (FILE *fd, int code_size, int flag)); +static int FDECL(LWZReadByte, (FILE *fd, int flag, int input_code_size)); +static void FDECL(ReadInterleavedImage, (FILE *fd, int len, int height)); +static void FDECL(ReadTileStrip, (FILE *fd, int len)); + +/* These should be in gif.h, but there isn't one. */ +boolean FDECL(fopen_gif_file, (const char *, const char *)); +boolean FDECL(read_gif_tile, (pixel(*)[])); +int NDECL(fclose_gif_file); + +static int +GetDataBlock(fd, buf) +FILE *fd; +unsigned char *buf; +{ + unsigned char count; + + if (!ReadOK(fd,&count,1)) { + Fprintf(stderr, "error in getting DataBlock size\n"); + return -1; + } + + ZeroDataBlock = (count == 0); + + if ((count != 0) && (!ReadOK(fd, buf, count))) { + Fprintf(stderr, "error in reading DataBlock\n"); + return -1; + } + + return count; +} + +static void +DoExtension(fd, label) +FILE *fd; +int label; +{ + static char buf[256]; + char *str; + + switch (label) { + case 0x01: /* Plain Text Extension */ + str = "Plain Text Extension"; +#ifdef notdef + if (GetDataBlock(fd, (unsigned char*) buf) == 0) + ; + + lpos = LM_to_uint(buf[0], buf[1]); + tpos = LM_to_uint(buf[2], buf[3]); + width = LM_to_uint(buf[4], buf[5]); + height = LM_to_uint(buf[6], buf[7]); + cellw = buf[8]; + cellh = buf[9]; + foreground = buf[10]; + background = buf[11]; + + while (GetDataBlock(fd, (unsigned char*) buf) != 0) { + PPM_ASSIGN(image[ypos][xpos], + cmap[CM_RED][v], + cmap[CM_GREEN][v], + cmap[CM_BLUE][v]); + ++index; + } + + return; +#else + break; +#endif + case 0xff: /* Application Extension */ + str = "Application Extension"; + break; + case 0xfe: /* Comment Extension */ + str = "Comment Extension"; + while (GetDataBlock(fd, (unsigned char*) buf) != 0) { + Fprintf(stderr, "gif comment: %s\n", buf ); + } + return; + case 0xf9: /* Graphic Control Extension */ + str = "Graphic Control Extension"; + (void) GetDataBlock(fd, (unsigned char*) buf); + Gif89.disposal = (buf[0] >> 2) & 0x7; + Gif89.inputFlag = (buf[0] >> 1) & 0x1; + Gif89.delayTime = LM_to_uint(buf[1],buf[2]); + if ((buf[0] & 0x1) != 0) + Gif89.transparent = buf[3]; + + while (GetDataBlock(fd, (unsigned char*) buf) != 0) + ; + return; + default: + str = buf; + Sprintf(buf, "UNKNOWN (0x%02x)", label); + break; + } + + Fprintf(stderr, "got a '%s' extension\n", str); + + while (GetDataBlock(fd, (unsigned char*) buf) != 0) + ; +} + +static +boolean +ReadColorMap(fd,number) +FILE *fd; +int number; +{ + int i; + unsigned char rgb[3]; + + for (i = 0; i < number; ++i) { + if (!ReadOK(fd, rgb, sizeof(rgb))) { + return(FALSE); + } + + ColorMap[CM_RED][i] = rgb[0] ; + ColorMap[CM_GREEN][i] = rgb[1] ; + ColorMap[CM_BLUE][i] = rgb[2] ; + } + colorsinmap = number; + return TRUE; +} + +/* + * Read gif header, including colormaps. We expect only one image per + * file, so if that image has a local colormap, overwrite the global one. + */ +static void +read_header(fd) +FILE *fd; +{ + unsigned char buf[16]; + unsigned char c; + char version[4]; + + if (!ReadOK(fd,buf,6)) { + Fprintf(stderr, "error reading magic number\n"); + exit(EXIT_FAILURE); + } + + if (strncmp((genericptr_t)buf,"GIF",3) != 0) { + Fprintf(stderr, "not a GIF file\n"); + exit(EXIT_FAILURE); + } + + (void) strncpy(version, (char *)buf + 3, 3); + version[3] = '\0'; + + if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) { + Fprintf(stderr, "bad version number, not '87a' or '89a'\n"); + exit(EXIT_FAILURE); + } + + if (!ReadOK(fd,buf,7)) { + Fprintf(stderr, "failed to read screen descriptor\n"); + exit(EXIT_FAILURE); + } + + GifScreen.Width = LM_to_uint(buf[0],buf[1]); + GifScreen.Height = LM_to_uint(buf[2],buf[3]); + GifScreen.Colors = 2<<(buf[4]&0x07); + GifScreen.ColorResolution = (((buf[4]&0x70)>>3)+1); + GifScreen.Background = buf[5]; + GifScreen.AspectRatio = buf[6]; + + if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */ + if (!ReadColorMap(fd, GifScreen.Colors)) { + Fprintf(stderr, "error reading global colormap\n"); + exit(EXIT_FAILURE); + } + } + + if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) { + Fprintf(stderr, "warning - non-square pixels\n"); + } + + for (;;) { + if (!ReadOK(fd,&c,1)) { + Fprintf(stderr, "EOF / read error on image data\n"); + exit(EXIT_FAILURE); + } + + if (c == ';') { /* GIF terminator */ + return; + } + + if (c == '!') { /* Extension */ + if (!ReadOK(fd,&c,1)) { + Fprintf(stderr, + "EOF / read error on extension function code\n"); + exit(EXIT_FAILURE); + } + DoExtension(fd, (int)c); + continue; + } + + if (c != ',') { /* Not a valid start character */ + Fprintf(stderr, + "bogus character 0x%02x, ignoring\n", (int) c); + continue; + } + + if (!ReadOK(fd,buf,9)) { + Fprintf(stderr, "couldn't read left/top/width/height\n"); + exit(EXIT_FAILURE); + } + + if (BitSet(buf[8], LOCALCOLORMAP)) { + /* replace global color map with local */ + GifScreen.Colors = 1<<((buf[8]&0x07)+1); + if (!ReadColorMap(fd, GifScreen.Colors)) { + Fprintf(stderr, "error reading local colormap\n"); + exit(EXIT_FAILURE); + } + + } + if (GifScreen.Width != LM_to_uint(buf[4],buf[5])) { + Fprintf(stderr, "warning: widths don't match\n"); + GifScreen.Width = LM_to_uint(buf[4],buf[5]); + } + if (GifScreen.Height != LM_to_uint(buf[6],buf[7])) { + Fprintf(stderr, "warning: heights don't match\n"); + GifScreen.Height = LM_to_uint(buf[6],buf[7]); + } + GifScreen.Interlace = BitSet(buf[8], INTERLACE); + return; + } +} + +static int +GetCode(fd, code_size, flag) +FILE *fd; +int code_size; +int flag; +{ + static unsigned char buf[280]; + static int curbit, lastbit, done, last_byte; + int i, j, ret; + unsigned char count; + + if (flag) { + curbit = 0; + lastbit = 0; + done = FALSE; + return 0; + } + + if ((curbit+code_size) >= lastbit) { + if (done) { + if (curbit >= lastbit) + Fprintf(stderr, "ran off the end of my bits\n"); + return -1; + } + buf[0] = buf[last_byte-2]; + buf[1] = buf[last_byte-1]; + + if ((count = GetDataBlock(fd, &buf[2])) == 0) + done = TRUE; + + last_byte = 2 + count; + curbit = (curbit - lastbit) + 16; + lastbit = (2+count)*8 ; + } + + ret = 0; + for (i = curbit, j = 0; j < code_size; ++i, ++j) + ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j; + + curbit += code_size; + + return ret; +} + +static int +LWZReadByte(fd, flag, input_code_size) +FILE *fd; +int flag; +int input_code_size; +{ + static int fresh = FALSE; + int code, incode; + static int code_size, set_code_size; + static int max_code, max_code_size; + static int firstcode, oldcode; + static int clear_code, end_code; + static int table[2][(1<< MAX_LWZ_BITS)]; + static int stack[(1<<(MAX_LWZ_BITS))*2], *sp; + register int i; + + if (flag) { + set_code_size = input_code_size; + code_size = set_code_size+1; + clear_code = 1 << set_code_size ; + end_code = clear_code + 1; + max_code_size = 2*clear_code; + max_code = clear_code+2; + + (void) GetCode(fd, 0, TRUE); + + fresh = TRUE; + + for (i = 0; i < clear_code; ++i) { + table[0][i] = 0; + table[1][i] = i; + } + for (; i < (1< stack) + return *--sp; + + while ((code = GetCode(fd, code_size, FALSE)) >= 0) { + if (code == clear_code) { + for (i = 0; i < clear_code; ++i) { + table[0][i] = 0; + table[1][i] = i; + } + for (; i < (1< 0) + ; + + if (count != 0) + Fprintf(stderr, + "missing EOD in data stream (common occurrence)\n"); + return -2; + } + + incode = code; + + if (code >= max_code) { + *sp++ = firstcode; + code = oldcode; + } + + while (code >= clear_code) { + *sp++ = table[1][code]; + if (code == table[0][code]) { + Fprintf(stderr, "circular table entry BIG ERROR\n"); + exit(EXIT_FAILURE); + } + code = table[0][code]; + } + + *sp++ = firstcode = table[1][code]; + + if ((code = max_code) <(1<= max_code_size) && + (max_code_size < (1< stack) + return *--sp; + } + return code; +} + + +static void +ReadInterleavedImage(fd, len, height) +FILE *fd; +int len, height; +{ + int v; + int xpos = 0, ypos = 0, pass = 0; + + while ((v = LWZReadByte(fd,FALSE,(int)input_code_size)) >= 0 ) { + PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v], + ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]); + + ++xpos; + if (xpos == len) { + xpos = 0; + switch (pass) { + case 0: + case 1: + ypos += 8; break; + case 2: + ypos += 4; break; + case 3: + ypos += 2; break; + } + + if (ypos >= height) { + ++pass; + switch (pass) { + case 1: + ypos = 4; break; + case 2: + ypos = 2; break; + case 3: + ypos = 1; break; + default: + goto fini; + } + } + } + if (ypos >= height) + break; + } + +fini: + if (LWZReadByte(fd,FALSE,(int)input_code_size)>=0) + Fprintf(stderr, "too much input data, ignoring extra...\n"); +} + +static +void +ReadTileStrip(fd,len) +FILE *fd; +int len; +{ + int v; + int xpos = 0, ypos = 0; + + while ((v = LWZReadByte(fd,FALSE,(int)input_code_size)) >= 0 ) { + PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v], + ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]); + + ++xpos; + if (xpos == len) { + xpos = 0; + ++ypos; + } + if (ypos >= TILE_Y) + break; + } +} + + + + +boolean +fopen_gif_file(filename, type) +const char *filename; +const char *type; +{ + int i; + + if (strcmp(type, RDBMODE)) { + Fprintf(stderr, "using reading routine for non-reading?\n"); + return FALSE; + } + gif_file = fopen(filename, type); + if (gif_file == (FILE *)0) { + Fprintf(stderr, "cannot open gif file %s\n", filename); + return FALSE; + } + + read_header(gif_file); + if (GifScreen.Width % TILE_X) { + Fprintf(stderr, "error: width %d not divisible by %d\n", + GifScreen.Width, TILE_X); + exit(EXIT_FAILURE); + } + tiles_across = GifScreen.Width / TILE_X; + curr_tiles_across = 0; + if (GifScreen.Height % TILE_Y) { + Fprintf(stderr, "error: height %d not divisible by %d\n", + GifScreen.Height, TILE_Y); + /* exit(EXIT_FAILURE) */; + } + tiles_down = GifScreen.Height / TILE_Y; + curr_tiles_down = 0; + + if (GifScreen.Interlace) { + /* sigh -- hope this doesn't happen on micros */ + image = (pixel **)alloc(GifScreen.Height * sizeof(pixel *)); + for (i = 0; i < GifScreen.Height; i++) { + image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel)); + } + } else { + image = (pixel **)alloc(TILE_Y * sizeof(pixel *)); + for (i = 0; i < TILE_Y; i++) { + image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel)); + } + } + + /* + ** Initialize the Compression routines + */ + if (!ReadOK(gif_file,&input_code_size,1)) { + Fprintf(stderr, "EOF / read error on image data\n"); + exit(EXIT_FAILURE); + } + + if (LWZReadByte(gif_file, TRUE, (int)input_code_size) < 0) { + Fprintf(stderr, "error reading image\n"); + exit(EXIT_FAILURE); + } + + /* read first section */ + if (GifScreen.Interlace) { + ReadInterleavedImage(gif_file, + GifScreen.Width,GifScreen.Height); + } else { + ReadTileStrip(gif_file,GifScreen.Width); + } + return TRUE; +} + +/* Read a tile. Returns FALSE when there are no more tiles */ +boolean +read_gif_tile(pixels) +pixel (*pixels)[TILE_X]; +{ + int i, j; + + if (curr_tiles_down >= tiles_down) return FALSE; + if (curr_tiles_across == tiles_across) { + curr_tiles_across = 0; + curr_tiles_down++; + if (curr_tiles_down >= tiles_down) return FALSE; + if (!GifScreen.Interlace) + ReadTileStrip(gif_file,GifScreen.Width); + } + if (GifScreen.Interlace) { + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + pixels[j][i] = image[curr_tiles_down*TILE_Y + j] + [curr_tiles_across*TILE_X + i]; + } + } + } else { + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + pixels[j][i] = image[j][curr_tiles_across*TILE_X + i]; + } + } + } + curr_tiles_across++; + + /* check for "filler" tile */ + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X && i < 4; i += 2) { + if (pixels[j][i].r != ColorMap[CM_RED][0] || + pixels[j][i].g != ColorMap[CM_GREEN][0] || + pixels[j][i].b != ColorMap[CM_BLUE][0] || + pixels[j][i+1].r != ColorMap[CM_RED][1] || + pixels[j][i+1].g != ColorMap[CM_GREEN][1] || + pixels[j][i+1].b != ColorMap[CM_BLUE][1]) + return TRUE; + } + } + return FALSE; +} + +int +fclose_gif_file() +{ + int i; + + if (GifScreen.Interlace) { + for (i = 0; i < GifScreen.Height; i++) { + free((genericptr_t)image[i]); + } + free((genericptr_t)image); + } else { + for (i = 0; i < TILE_Y; i++) { + free((genericptr_t)image[i]); + } + free((genericptr_t)image); + } + return(fclose(gif_file)); +} + +#ifndef AMIGA +static char *std_args[] = { "tilemap", /* dummy argv[0] */ + "monsters.gif", "monsters.txt", + "objects.gif", "objects.txt", + "other.gif", "other.txt" }; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + pixel pixels[TILE_Y][TILE_X]; + + if (argc == 1) { + argc = SIZE(std_args); + argv = std_args; + } else if (argc != 3) { + Fprintf(stderr, "usage: gif2txt giffile txtfile\n"); + exit(EXIT_FAILURE); + } + + while (argc > 1) { + if (!fopen_gif_file(argv[1], RDBMODE)) + exit(EXIT_FAILURE); + + init_colormap(); + + if (!fopen_text_file(argv[2], WRTMODE)) { + (void) fclose_gif_file(); + exit(EXIT_FAILURE); + } + + while (read_gif_tile(pixels)) + (void) write_text_tile(pixels); + + (void) fclose_gif_file(); + (void) fclose_text_file(); + + argc -= 2; + argv += 2; + } + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} +#endif diff --git a/win/share/monsters.txt b/win/share/monsters.txt new file mode 100644 index 0000000..2df3321 --- /dev/null +++ b/win/share/monsters.txt @@ -0,0 +1,7502 @@ +A = (0, 0, 0) +B = (0, 182, 255) +C = (255, 108, 0) +D = (255, 0, 0) +E = (0, 0, 255) +F = (0, 145, 0) +G = (108, 255, 0) +H = (255, 255, 0) +I = (255, 0, 255) +J = (145, 71, 0) +K = (204, 79, 0) +L = (255, 182, 145) +M = (71, 108, 108) +N = (255, 255, 255) +O = (218, 218, 182) +P = (108, 145, 182) +# tile 0 (giant ant) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJAJKKAMMM + MMMMMJAAAKJJJAMM + MMMMMAKJJAJJAAMM + MMMKKAJJJAAAMMMM + MMBJJAAAAAJJAMMM + MMJBJAJAJAAMMMMM + MMMMMAJAMJAMMMMM + MMMMMMJAMJAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 1 (killer bee) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MPPPMMMMMPPMMMMM + PPPPPMMMPBPPMMMM + PBPPPMMPBPPPMMMM + MPPBPMPPLPLLMMMM + MMMPPMPLLALHAHMM + MMMAKKKLAHAAHHMM + BBJJJJJJJAHHAAMM + ABJBBJJJJAHAHHMM + MJJABJAJMJMHHMMM + MMMMMMMJMJMMMMMM + MMMMMMMMMMMMMMMM + MMMAAAAAAAAAAAMM + MMMMMAAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 2 (soldier ant) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJAJKKAMMM + MMMMMJAAAKJJJAMM + MMMMMAKJJAJJAAMM + MJJKKAJJJAAAMMMM + JBJJJAAAAAJJAMMM + JJJBJAJAJAAMMMMM + JAAJJAJAMJAMMMMM + MMJJAAJAMJAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 3 (fire ant) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMDACCCAMMM + MMMMMDAAACDDDAMM + MMMMMACDDADDAAMM + MMMCCADDDAAAMMMM + MMGDDAAAAADDAMMM + MMDGDADADAAMMMMM + MMMMMADAMDAMMMMM + MMMMMMDAMDAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 4 (giant beetle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKKDKKMMMMM + MMMMKACLCJJDMMMM + MMMKCLCJJDDDKMMM + MMMDCCJDADDADMMM + MMMADJDDDDDDDMMM + MMMBAKDDAADKAAMM + MMMABAKDDKMMMMMM + MMMMMMAAMAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 5 (queen bee) +{ + MMMMMMMMMMMMMMMM + MPPPMMMMMPPMMMMM + PPPPPMMMPPPPMMMM + PPPPPMMPPBPPMMMM + PBPPPMMPBPPPMMMM + MPPBPMPPLPLLMMMM + MMMPPMPLLALHAHMM + MMMAKKKLAHAAHHMM + BBJJJJJJJAHHAAHM + ABJBBJJJJAHAHHHM + MJJABJAJMJHHHAAM + MMMJMMMJMJAAAHHM + MMJJMMJJMJMHHAHM + MMJAAAJAAJMMHHMM + MMMMMAAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 6 (acid blob) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMKDDAMMMMMMM + MMMDDKDDKAMMMMMM + MDDDIIIDJDAMMMMM + DMIIOOIIDAIAMMMM + MDKNNNODIDAMMIAM + IDJNANOJDDJAMMMM + ADIDNOIDIDDDAMMM + MMMJDIKIADKIAMMM + MIAMIDIDMJDAMMMM + MMMIMDDAMMMMDAMM + MMMMMMMMMIAMMMMM + MMIAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 7 (quivering blob) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPMMMMMMM + MMMMMMMMMPMMMMMM + MPMOOOPPEMMAAAMM + PMOPBBBPOEAEAAMM + PMPBNNNPMOEAAEAM + PMPNNNNNPOEEAEAM + POPNAANNEOMOAEAM + MOPNAANNEMMOAAAM + MOPBNNNEPPMOEAAM + BPOPEEEBBPOMEEAM + BBBPBBBBBBPPPPAM + MMBBBBBBBBBBPAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 8 (gelatinous cube) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMLLLMMMMMM + MMMMMLLLLLLLLMMM + MMMLLLLLLLLLDMMM + MMMCLLLLLLLDDAMM + MMMCGGCLLLDDDAAM + MMMCAGCGGDDDDAAA + MMMCCCCAGDDDAAAA + MMMMMCCCCDDAAAAM + MMMMMMMCCDAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 9 (chickatrice) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMOOMMMMMMM + MMMMMMHAOOMMMMMM + MMMMMHHOOHMHHAMM + MMMMMMMMOOHOAMMM + MMMMMMMMOOFAMMMM + MMMMMMMMFGGFAMMM + MMMMMMMMAGFGAAMM + MMMMMMMMMMMGAMMM + MMMMMMMFMMFFAMMM + MMMMMMMAFFAAMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 10 (cockatrice) +{ + MMMMMMMMMMMMMMMM + MMMDMDDMMMMMMMMM + MMMMDDMMMMMMMMMM + MMMMNLMMAAMMMMMM + MMHHANMAAAMMMMMM + MHHMNOMMAAAAMMMM + MMMAOOLFFFAAMMMM + MMMOOLKGGFFAAMMM + MMMAOAGGFGFFAAMM + MMMMMMMGFFGFAAMM + MMMMMMMMMFGGAAMM + MMMMMFAMMMFFAMMM + MMMMFAMMMMFFAMMM + MMMMFAMMFFFAMMMM + MMMMMFFFFAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 11 (pyrolisk) +{ + MMMMMMMMMMMMMMMM + MMMDMDDMMMMMMMMM + MMMMDDMMMMMMMMMM + MMMMNBMMAAMMMMMM + MMHHANMAAAMMMMMM + MHHMNBMMAAAAMMMM + MMMAPBBJJJAAMMMM + MMMPPPKDDKJAAMMM + MMMAPADDKDKJAAMM + MMMMMMMDJKDJAAMM + MMMMMMMMMJDDAAMM + MMMMMJAMMMJJAMMM + MMMMJAMMMMJJAMMM + MMMMKAMMKKKAMMMM + MMMMMJKKKAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 12 (jackal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOMMOMMMMMMMMM + MMMOMOOMMMOMMMMM + MMOOOOMMMMMOMMMM + MMIOIOOMMMMOMAMM + MOLLOOOLMMMOMAMM + DOOOAOOOOOOOAAMM + MMAAOOOOOOOOAAMM + MMMMOJOOOOOLAAMM + MMMMOJOLKALKAAMM + MMMOOAOAAAOAAMMM + MMMMMOOMMOOAMMMM + MMMMMMMMMMMMMMMM +} +# tile 13 (fox) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCMMMMMMMMMMM + MMMMCMMMMMMMMMMM + MMCCACMMMCCCMMMM + MMACCCCCCACCCMMM + MMMAACCCCMACCMMM + MMMMACAACMMAAMMM + MMMACMACAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 14 (coyote) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKMMKMMMMMMMMM + MMMKMKKMMMMMMKKK + MMKKKKMMMMMMKKKA + MMNCNKMKKKKKAAAM + MKCCKKKKKKKKKMMM + KKCKAKKKKKKKKMMM + DKKKAKAKKKKAKMMM + MMAAKAAKAAAAKMMM + MMMMAKAKAAAAKMMM + MMMAKKAKAMAKKMMM + MMMMMAKKMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 15 (werejackal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOMMOMMMMMMMMM + MMMOMOOMMMOMMMMM + MMOOOOMMMMMOMMMM + MMIOIOOMMMMOMAMM + MOLLOOOLMMMOMAMM + DOOOALLOOOOOAAMM + MMAALLLLOOOOAAMM + MMMMLJLLLOOLAAMM + MMMMLJLLKALKAAMM + MMLLLALAAAOAAMMM + MMMLMLLMMOOAMMMM + MMMMMMLMMMMMMMMM +} +# tile 16 (little dog) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJMMJMMMMMMMMM + MMMJMJJMMMKMMMMM + MMJJJJMMMMMKMMMM + MMNJNKKMMMMKMAMM + MJJJCKKMMMMKMAMM + MPJJAKCKKCKKAAMM + MDDAACKKCKKKAAMM + MMMMKJKJCJKJAAMM + MMMMKJKJJAJJAAMM + MMMKKAKAAAKAAMMM + MMMMMKKMMMKAMMMM + MMMMMMMMMMMMMMMM +} +# tile 17 (dog) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJMMJMMMMMKMMM + MMMJMJJMMMMMMKMM + MMJJJJMMMMMMMKMM + MMNJNKKMMMMMMKMA + MJJJCKKKKMMMMKMA + MPJCAKCKKKKCKKAA + MDDAACKKKKCKKKAA + MMAAKJKKJJCJKJAA + MMMMKJKKJJJAJJAA + MMMMKAKJAAAAKJAM + MMMKKAKAAAAAKAAM + MMMMMKKAAMMMKAMM + MMMMMMMMMMMMMMMM +} +# tile 18 (large dog) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJMMJMMMMMKMMM + MMMJMJJMMMMMMKMM + MMJJJJMMMMMMMKMM + MMNJNKKKMMMMMKMA + MJJJCKKKKKMMJKMA + MPJCAKCKKKKCKKAA + MDDAACKKKKCKKKAA + MMAAKJKKJJCJKJAA + MMMJKJKKJJJAJJAA + MMMMKAKJAAAAKJAM + MMMMKAKJAAAAKJAM + MMMKKAKAAAAAKAAM + MMMMMKKAAMMMKAMM + MMMMMMMMMMMMMMMM +} +# tile 19 (dingo) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMCMM + MMMCMMCMMMMMMMCM + MMMCMCCMMMMMMMCA + MMCCCCKMMMMMMMCA + MMACACCKCCCCCCAA + MMLLCCCKCCCLCCCA + MKCCACKCLLLLACCA + MMAACAACLLAAACCA + MMMMACACAAAAAACA + MMMMCCACAAAMMCCA + MMMMMACCAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 20 (wolf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPMMPMMMMMPMMM + MMMPMPPMMMMMMPMM + MMPPPPMMMMMMMPMM + MMNMNPPMMMMMMPMA + MPMMPPPPPMMMMPMA + PPMPAPPPPPPPPPAA + DPPPAPPPPPPPPPAA + MMAAPMPPMMPMPMAA + MMMMPMPPMMMAMPAA + MMMMPAPMAAAAPMAM + MMMPPAPAAAAAPAAM + MMMMMPPAAMMPPAMM + MMMMMMMMMMMMMMMM +} +# tile 21 (werewolf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPMMPMMMMMPMMM + MMMPMPPMMMMMMPMM + MMPPPPMMMMMMMPMM + MMNMNPPMMMMMMPMA + MPMMPPPPPMMMMPMA + PPMPAPPPPPPPPPAA + DPPPALPPPPPPPPAA + MMAALLPLMMPMPMAA + MMMMLMLLMMMAMPAA + MMMMLALMAAAAPMAM + MMLLLALAAAAAPPAM + MMMLMLLAAMMPPMMM + MMMMMLLMMMMMMMMM +} +# tile 22 (warg) +{ + MMMMMMMMMMMMMMMM + MMMPMMPMMMMPPMMM + MMMPMPPMMMMMMPMM + MMPPPPMMMMMMMPMA + MMNMNPPMMMMMMPMA + MPMMPPPPPMMMMPMA + PPPPDPPPPPPPPPAA + DPPNDPPPPPPPPPAA + MMDDDPPPPPPPPPAA + PNDNPMPPPPPPPPAA + MPPPPAPPPPAPPMAM + MMMPAAPPAAAAPPAA + MMMPAAPMAAAAPMAM + MPPPAAPAAAAAPAAM + MMMMPPPAAMPPPAMM + MMMMMMMMMMMMMMMM +} +# tile 23 (winter wolf cub) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMNMMMMMMMMM + MMMNMNBMMMNMMMMM + MMNNNNMMMMMNMMMM + MMDNDNBMMMMNMAMM + MMNNNNBMMMMNMAMM + MNNNNNNNNNNNAAMM + MDNBBNNNNNNBAAMM + MMMMNNNNNNNBAAMM + MMMMNNNBBANBAAMM + MMMNBANAAANAAMMM + MMMMMNBMMNBAMMMM + MMMMMMMMMMMMMMMM +} +# tile 24 (winter wolf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMNMMMMMNMMM + MMMNMNNMMMMMMNMM + MMNNNNMMMMMMMNMM + MMDODNNMMMMMMNMA + MNOONNNNNMMMMNMA + NNONANNNNNNNNNAA + DNNBANNNNNNNNNAA + MMAANNNNNNNBNNAA + MMMMNBNNNBBANNAA + MMMMNANBAAAANBAM + MMMNNANAAAAANAAM + MMMMMNNAAMMNNAMM + MMMMMMMMMMMMMMMM +} +# tile 25 (hell hound pup) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMCMMCMMMMMMMMM + MMMCMCCMMMCMMMMM + MMCCCCMMMMMCMMMM + MMDCDCCMMMMCMAMM + MCCCCCCMMMMCMAMM + MPCCACCCCCCCAAMM + MCHAACCCCCCCAAMM + CHCMCCCCCCCCAAMM + MDMMCCCCCACCAAMM + MMMCCACAAACAAMMM + MMMMMCCMMMCAMMMM + MMMMMMMMMMMMMMMM +} +# tile 26 (hell hound) +{ + MMMMMMMMMMMMMMMM + MMMCMMCMMMMCCMMM + MMMCMCCMMMMMMCMM + MMCCCCMMMMMMMCMA + MMDJDCCMMMMMMCMA + MCCCCCCCCMMMMCMA + CCCCDCCCCCCCCCAA + MCHCMCCCCCCCCCAA + CHCMMCCCCCCCCCAA + MDMMCCCCCCCCCCAA + MMMCCACCCCACCMAM + MMMCAACCAAAACCAA + MMMCAACMAAAACMAM + MCCCAACAAAAACAAM + MMMMCCCAAMCCCAMM + MMMMMMMMMMMMMMMM +} +# tile 27 (Cerberus) +{ + MMMMMMMMMMMMMMMM + MMJMMJMMMMMMMMMM + MMJJJJMJMMJMMMMM + MNJNJMMJMJJMMMMM + JJJJKAJJJJMMMMJM + PJJJJANJNKKMMMJM + DJKJAJJJKJJKMMJM + MMAAJPJKAJKJKJJM + MMJJJDDAJKJJJJJA + MMJJJAAJJJJJJJJA + MJKKKJJJKJJKJJAA + MJJAAKJJKJJJKJAA + JJAAAAJJAAAAJJAM + JAAAAAJAAAAAJAAM + MMMMMJJAAMMMJAMM + MMMMMMMMMMMMMMMM +} +# tile 28 (gas spore) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPFGGFPMMMMM + MMMMPGFFFFFPMMMM + MMMPFFFFFGGFPMMM + MMMFFGGFFGGFFMMM + MMMGFGGFFFFFGMMM + MMMGFFFFFFFFGMMM + MMMFFFFGGFFFFMMM + MMMPGGFGGFGGPMMM + MMMMPGFFFFGPMMMM + MMMMMPFGGFPMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 29 (floating eye) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMONNNOMMMMM + MMMMPNNNNNNNPMMM + MMMMNNNNNNNNNMMM + MMMONNBBBBNNNOMM + MMMNNBBEEBBNNNMM + MMMNNBEAAEBNNNMM + MMMONBEAAEBNNOMM + MMMMNBBEEBBNNMMM + MMMMPNBBBBNNPAAM + MMMMMMONNNOAAAAM + MMMMMMAAAAAAAAAM + MMMMMMMAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 30 (freezing sphere) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPBBBPMMMMM + MMMMPBBBBBBBPMMM + MMMMBBBBBBBBBMMM + MMMPBPPPBBBBBPMM + MMMBBNNBBPPPBBMM + MMMBBANPBNNBBBMM + MMMPBBPPBANPBPMM + MMMMBBBBBBPPBMMM + MMMMPBBBBBBBPAAM + MMMMMMPBBBPAAAAM + MMMMMMAAAAAAAAAM + MMMMMMMAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 31 (flaming sphere) +{ + MMMMMMMMMMMMMMMM + MMMMCMMMHMMMMMMM + MMMMCMMMMOMMMCMM + MMMCMMHMCCMMMCMM + MMMCMMCMCCMMCCMM + MMMDCAMDDCMACCMM + MAHCDCADDCAADCMM + MAACDCDDDDDADDMM + MMACDDDJJJDDDDMM + MMADDCAKDDACDDMM + MMMADAKDDCDADMMM + MMMADADHCHCADMMM + MMMMDADDDCDADMMM + MMMMMDADDDADMMMM + MMMMMMJJJJJMMMMM + MMMMMMMMMMMMMMMM +} +# tile 32 (shocking sphere) +{ + MMMMMMMMMMMMMMMM + MMMMMPPPPPPMMMMM + MMMPPIAAADAPPMMM + MMPAAAIAAADDAPMM + MMPHAAAPBOAAAPMM + MPAHHAPBBBOAAAPM + MPHAAAPBBBBAHHPM + MPAAAAPPBBBAAAPM + MPAAAIAPPPAAAIPM + MPIIIAAAAAAIIAPM + MMPIAANAAPAAAIPM + MMPIAANAAAPAAPMM + MMMPANANAPAPAPMM + MMMMPPAIAPAPPMMM + MMMMMMPPPPPMMMMM + MMMMMMMMMMMMMMMM +} +# tile 33 (beholder) +{ + MMMMOAMMOAMMMMMM + MMOAMDADAMOAMOAM + MMMDAMDADADADDMM + MMOADDOOOODDADOM + MMMDDHOAAOHDDAMM + MMMJDHOAAOHDDJMM + MMMDDDOOOODDDDMM + MMMDDDDDDDDDDDMM + MMMJDAOAAAOADJMM + MMMMDDAAOAADDMMM + MMMMPDDDDDDDPAAM + MMMMMMJDDDJAAAAM + MMMMMMAAAAAAAAAM + MMMMMMMAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 34 (kitten) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMKMMMM + MMMMMMMMMMMMCMMM + MMMMCMCMMMMMLMAM + MMMCCCCJMMMMCMAM + MMMNCNCJCCLCLMAM + MMMCCCCJCCLCCAAM + MMMMIACCLLCCCAAM + MMMMMCACCJCCJAMM + MMMMMMCCAMMCAMMM + MMMMMMMMMMMMMMMM +} +# tile 35 (housecat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMKMMMM + MMMMMMMMMMMMCMMM + MMMCMCMMMMMMLMAM + MMCCCCJMMMMMCMAM + MCNCNCJCLCLCLMAM + MCCCCCJCLCLCCAAM + MMCICJCCLCLCLAAM + MMMAACCLCLCCCAAM + MMMCCACCJJCCJAMM + MMMMMCCAMMMCAMMM + MMMMMMMMMMMMMMMM +} +# tile 36 (jaguar) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMCMMCMMMMMMCMMM + MMCCMCJMMMMMMCMA + MCCCCCJMMMMMMCMA + MGCGCCJCAACCJCMA + MCCCCCJCCCCCCCAA + MCDDDJCAACCAJCAA + MMCCACCAJCCAACAA + MMMMCACCJJJCCJAM + MMMMCACAAAAACJAM + MMMCKACAAAAACAAM + MMMMMCCAAMMMCAMM + MMMMMMMMMMMMMMMM +} +# tile 37 (lynx) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + OMMMMOMMMMMMMMMM + ACMCCAMMMMMMMMMM + MCCCAMMMMMMMMCAM + MGCGCOAKKKKKMLAM + MCKCCAJCCCCCKAMM + LLDDLLACLLLCCAAM + MMCCMAACLLLCCAAM + MMMMMMMCAAAACAAM + MMMMCACAAAACCAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 38 (panther) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMAAMM + MMMMMMMMMMMMMMAM + MMMMMMMMMMMMMMAM + MAMMMAMMMMMMMMAM + MEAMAEMMMMMMMMAM + MAAAAAEAAAAAAAMM + MAAAAAEAAAAAAAMM + MHAHAAMAAAAAAAAM + MAAAAMAAAAAEAAAM + MAAAMMAAAAAEAAAM + MMMMMAAMMMMAAAMM + MMAAAAMMAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 39 (large cat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMKMMM + MMMMMMMMMMMMMCMM + MMMCMCMMMMMMMLMA + MMCCCCJMMMMMMCMA + MCNCNCJCLCCLCLMA + MCCCCCJCLCCLCCAA + MMCDCJCCLCCLCLAA + MMMAACCLCCLCCCAA + MMMMCACCJJJCCJAM + MMMCKALAAAAACAAM + MMMMMCCAAMMMCAMM + MMMMMMMMMMMMMMMM +} +# tile 40 (tiger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMCMMCMMMMMMCMMM + MCCJCCMMMMMMMCMA + MCAACCJMMMMMMAMA + MGAGCCJACACAJCMA + MCCCCCACACACACAA + MODOCACCACACACAA + MOCOACCJACACACAA + MMMMCACJAJAJCAAM + MMMMAACAAAAAAJAM + MMMCKACAAAAACAAM + MMMMMCCAAMMCCAMM + MMMMMMMMMMMMMMMM +} +# tile 41 (gremlin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + GGGAMMMMAGGGMMMM + MGGGFAAAGGGMMMMM + MMFFFFFFFFMMMMMM + MMMNDFFDNAMMMMMM + MMMGNFFNGAMMMMMM + MMMGFFFFGAMMAAMM + MMMAGFFFAFAAAAAM + MMGFAGFAFFFAAAAM + MGFGFAAFFAFAAAAM + MGFMGFAGAAFAAAAM + MMMMFFAGFAAMAAMM + MMMGFAMFGAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 42 (gargoyle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPAPPPPAPMMMMM + MMPAMMMMMMAPMMMM + MMPMDDMMDDAPMMMM + MMMMPDMMDPAMMMMM + MMMMPMMMMPAMMAAM + MMMMAPMMMAMAAAAA + MMMPMAPMAMMMAAAA + MMPMPMAAMMAMAAAA + MMPAMPMAPAAMAAAA + MMPAMPMMMMAMAAMM + MMMMMPMMMMAAAAMM + MMMMMPMAPMAAMMMM + MMMMPFAMFPAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 43 (winged gargoyle) +{ + MMMKMMMMMMKMMMMM + MMMKJMMMMKJMMMMM + MMKJAPPPPAJJMMMM + MMKJMMMMMMAJMMMM + MMKJDDMMDDAJMMMM + MKJAPDMMDPAJJMMM + MKJAPMMMMPAAJAAM + KJAMAPMMMAMAJJAA + JMMPMAPMAMMMAJAA + MMPMPMAAMMAMAAAA + MMPAMPMAPAAMAAAA + MMPAMPMMMMAMAAMM + MMMMMPMMMMAAAAMM + MMMMMPMAPMAAMMMM + MMMMPFAMFPAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 44 (hobbit) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJAMMMMMMM + MMMMMJJJJAMMMMMM + MMMMJLFLFJMMMMMM + MMMMJLLLLJMMMMMM + MMMMJKLLKJJMAAMM + MMMCLLLLLLCAAAMM + MMCLALLLLALCAMMM + MMLLAJJKJALLAMMM + MMMLMLKJLALAAMMM + MMMMMLLALLAMAMMM + MMMMLLLMLLLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 45 (dwarf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBMMMMMMMMM + MMMMMBEEMMMMMMMM + MMMMBBEEEMMMMMMM + MMMMBLLLEMMMMMMM + MMMMMOLOMMMAAAMM + MMMBBOOOEEAAAAMM + MMMBABOEAEAAAAMM + MMMMLBBELAAAAMMM + MMMMEBAEEAAMMMMM + MMMMBEAEBMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 46 (bugbear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKMMMMMMMMM + MKMMKKKMMMMMMMMM + MKKKKKKMMMMMMMMM + KADKADKKKMMMMMMM + KKKKKKKJKKMMMMMM + KAPAPAKJJKJMMMMM + KAAAAAKKJKJJMMMM + MKKKKKJKAKKJMMMM + MMKAJJCAKKKJMAAM + MMKKMKKKKKJJAAMM + MMMCMMKJAKJAAMMM + MMMMMCCAAKJAMMMM + MMMMMMMMCCAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 47 (dwarf lord) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBMMMMMMMMM + MMMMMBEEMMMMMMMM + MMMMHHHHHMMMMMMM + MMMMBLLLEMMMMMMM + MMMMBOLOEMMAAAMM + MMMBBOOOEEAAAAMM + MMMBABOEAEAAAAMM + MMMMLBBELAAAAMMM + MMMMEBAEEAAMMMMM + MMMMBEAEBMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 48 (dwarf king) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMHMCMHMMMMMMM + MMMMHCHCHMMMMMMM + MMMMHHHHHMMMMMMM + MMMMBLLLEMMMAMMM + MMMMMOLOMMMAAAAM + MMMEBOOOEEAAAAMM + MMMBABOEAEAAAAMM + MMMMLBBELAAAAMMM + MMMMEBAEEAAMMMMM + MMMMBEAEBMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 49 (mind flayer) +{ + MMMMMMMMMMMMMMMM + MMMMMMMIIIICMMMM + MMMMMIIIIIICMMMM + MMMMIIIIIIICMMMM + MMMIGIIIIGCMMMMM + MMMIIGINGICMMMMM + MMMMIIIIICMMMMMM + MMMMIAIAIFMMMMMM + MMMMIAIAIFMMMMMM + MMMMIAIAIFIMMMMM + MMMMMFIFIFICMAAM + MMMMCBIBBFMCAAAM + MMMIIIBBFFACAAAM + MMMMMMBBFCAAAAMM + MMMMMMCFFCAAMMMM + MMMMIICMIIAMMMMM +} +# tile 50 (master mind flayer) +{ + MMMMMMMMMMMMMMMM + MMMMMMMIIIICMMMM + MMMMMIIIIIICMMMM + MMMMIIIIIIICMMMM + MMMIGIIIIGCMMMMM + MMMIIGINGICMMMMM + MEEEIIIIICEEEEMM + MMEEIAIAIEEEEMMM + MMMEIAIAIEEEMMMM + MMMMIAIAIEEEMMMM + MMMMEFIFIEEEMAAM + MMMMCBIBBEEEAAAM + MMMIIIBBEEEEAAAM + MMMMEEBEEEEAAAMM + MMMEEECEEEAAMMMM + MMMMIICMIIAMMMMM +} +# tile 51 (manes) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPPMMMMMMMMMM + MMMPPPPMMMMMMMMM + MMPAPAPMMMMMMMMM + MMPPPPPMPPPMPMMM + MMPMMPPPPMMMMMMM + MMPMMPPPPPMMMPMM + MMPPPPPMPPPMMMMM + MMPMPMPMPPMPMMMM + MPMMMPMPPPPMMMMM + MPMMMMPPPMPPMMMM + MMPMMMMPMPMPMPMM + MMMMMMMMPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 52 (homunculus) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJMMMMMMM + MMMMMMLLCMMMMMMM + MMMMMMLLCMMMMMMM + MMMMMBBPPPAAMMMM + MMMMLMBPPACAAMMM + MMMMMMBAPAAAAMMM + MMMMMLLALCAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 53 (imp) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOMDMOMMMMMM + MMMMMOCDDOMMMMMM + MMMMMMCDDMMMMMMM + MMMMMGGFFFAAMMMM + MMMMCMGFFADAAMMM + MMMMMMGAFAAAAMMM + MMMMMCDADDAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 54 (lemure) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPPMMMMMMMMMM + MMMPPPPMMMMMMMMM + MMPAPAPMMMMPMMMM + MMPPPPPMMPPMPMMM + MMPMMPPPPPPPPMMM + MMPMMPPPPPPPMMMM + MMPPPPPPPPPMMMMM + MMMMPPPPPPPPMMMM + MMPPPPPPPPPPMMMM + MMMPPPPPPPPPPMMM + MMPMPMMPPPPMPPMM + MMMMMMMMPMPPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 55 (quasit) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOMDMOMMMMMM + MMMMMOCDDODMMMMM + MMMMMMCDDMDMMMMM + MMMMMGGFFFAAMMMM + MMMMCMGFFAAADAMM + MMMMCMGFFJJDAMMM + MMMMMMGAFAAAAMMM + MMMMMCDADDAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 56 (tengu) +{ + MMMMMMMMMMMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMPPPPMMMMMM + MMMMMDPPNPMMMMMM + MMMMDDDPPPMMMMMM + MMMMDDPPPPMMMMMM + MMMMDMMPPAMMMMMM + MMMMMPIAAIPMAAAM + MMMMPPPIIPPPAAAM + MMMMPAPPPPAPAAAM + MMMMPAHHHHAPAAAM + MMMMLAPPPPALAAAM + MMMMMMPPPPAAAAMM + MMMMMMPPPPAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 57 (blue jelly) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMOBEMMMMMMM + MMMBEMOBEBOEMMMM + MMOBEOBBEOBBEMMM + MMOBEOBBEOBBEMMM + MMBBBGGBGGBEEEMM + MMBBBAGBAGEEEEAM + MMBBBBBBEBEEEEAA + MMMBBBBBBBEEEAAA + MMMMBBBBBEEEAAAM + MMMMMMBBBEEAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 58 (spotted jelly) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMOBEMMMMMMM + MMMBEMOCEBOEMMMM + MMOCEOCCEOBCEMMM + MMOCEOBCEOCBDMMM + MMBBBHHBHHBEDDMM + MMCCBAHBAHEEEEAM + MMBBCBBBEBEEDEAA + MMMBBBCBBBEEDAAA + MMMMBCCCBEEDAAAM + MMMMMMCCBEEAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 59 (ochre jelly) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMDMMMMMMMM + MMMMMMLCDMMMMMMM + MMMCDMLCDCLDMMMM + MMLCDLCCDLCCDMMM + MMLCDLCCDLCCDMMM + MMCCCGGCGGCDDDMM + MMCCCAGCAGDDDDAM + MMCCCCCCDCDDDDAA + MMMCCCCCCCDDDAAA + MMMMCCCCCDDDAAAM + MMMMMMCCCDDAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 60 (kobold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNBPBNMMMMMMMM + MMMBABABMMMMMMMM + MMMMBBPAMMAMMMMM + MMMBBABPAMAAMAMM + MMBPBBBBPAAAAAMM + MMBAPBPAPAAAAAMM + MMMMPBPAAAAAAMMM + MMMMBABAAAAMMMMM + MMMBBABBAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 61 (large kobold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNBPBNMMMMMMMM + MMMBABABMMMMMMMM + MMMMBBPAMMAMMMMM + MMMBBABPAMAAMMMM + MMBPBBBBPAAAMAMM + MMBAPBPAPAAAAAMM + MMBAPBPAPAAAAAMM + MMMMPBPAAAAAAMMM + MMMMBABAAAAMMMMM + MMMBBABBAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 62 (kobold lord) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNCCCNMMMMMMMM + MMMCABACMMMMMMMM + MMMCBBPCMMAMMMMM + MMCCBABCCMAAMMMM + MMCCBBBCCAAAMAMM + MMBCCBCCPAAAAAMM + MMBACBCAPAAAAAMM + MMMMBBBAAAAAAMMM + MMMMBABAAAAMMMMM + MMMBBABBAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 63 (kobold shaman) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNHHHNMMMMMMMM + MMMHABAHMMMMMMMM + MMMHBBPHMMAMMMMM + MMHHBABHHMAAMMMM + MHHHBBBHHHAAMAMM + MHBHHBHHPHAAAAMM + MMBAHBHAPAAAAAMM + MMMMBBBAAAAAAMMM + MMMMBABAAAAMMMMM + MMMBBABBAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 64 (leprechaun) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMMFMMMMKMMMM + MMMMMGFFMMKLKMMM + MMMMGFFFFMMKMMMM + MMMMMKLKAMGLAAMM + MMMFGFJFFFAKAMAM + MMMGAGFFAAAKMAAM + MMMMLKHKKJAKAAMM + MMMMGFAGKJAKAMMM + MMMGFAAMGFAKMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 65 (small mimic) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPOPMMMMMMM + MMMMMMNNOMMMMMMM + MMMMMMNNOMMMMMMM + MMMMMMMOAMMMMMMM + MMMMMNNNNNMMAAMM + MMMMOONNNOOMAAMM + MMMMNANOOANAAAMM + MMMMMMNAOAAAAMMM + MMMMMMNAOAAMAMMM + MMMMMNNMOOAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 66 (large mimic) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMLLOAMMMMMM + MMMMMMNNOAMMMMMM + MMMMMMNNOAMMMMMM + MMMMMMONOAMMMMMM + MMMMMNNNNOMMAAAM + MMMMOONNNOOMAAAM + MMMNOANNNAOOAAAM + MMMNAONONOANAAAM + MMMMMNOMNOAAAAMM + MMMMMNOMNOAAMAMM + MMMMNNOMNOOAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 67 (giant mimic) +{ + MMMMMMMMMMMMMMMM + MMMMMMNNOMMMMMMM + MMMMMNNNNOAMMMMM + MMMMMNNNNOAMMMMM + MMMMMNNNNOAMMMMM + MMMMMONNNOAMMMMM + MMPONNOOONOOPAAA + MPONONNNNOONOPAA + MONOANNNNOAONOAA + MNNOANNNNOAOOOAA + MMMAANNONNAAAAAA + MMMMPNOMNNPAAAAA + MMMMONOMNNOAAMMA + MMMMNNOMNNOAAMMA + MMMNNNOMNNOOAMMM + MMMMMMMMMMMMMMMM +} +# tile 68 (wood nymph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOHMMMMMMMMMMM + MMOHHLMMMMMMMMMM + MMOHLLMMMMMMMMMM + MMHHLAMMMMMMMMMM + MOHLLFKKKKLAMMMM + MHKFLFKKKKAMMMMM + OHKJFKKJJKMAMMMM + HKKLJJGKAAAAAAAM + MJJAJGDKJAAAAAMM + MMJAMKKJJAAAAMMM + MMMJMKKKKJAAMMMM + MMMMJKKKKKJAMMMM + MMMMKJKJKJKJMMMM + MMMMMMMMMMMMMMMM +} +# tile 69 (water nymph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOHMMMMMMMMMMM + MMOHHLMMMMMMMMMM + MMOHLLMMMMMMMMMM + MMHHLAMMMMMMMMMM + MOHLLJBBBBLAMMMM + MHBJLJBBBBAMMMMM + OHBPJBBPPBMAMMMM + HBBLPPNBAAAAAAAM + MPPAPNDBMAAAAAMM + MMPAMBBPPAAAAMMM + MMMPMBBBBPAAMMMM + MMMMPBBBBBPAMMMM + MMMMBPBPBPBPMMMM + MMMMMMMMMMMMMMMM +} +# tile 70 (mountain nymph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOHMMMMMMMMMMM + MMOHHLMMMMMMMMMM + MMOHLLMMMMMMMMMM + MMHHLAMMMMMMMMMM + MOHLLCOOOOLAMMMM + MHOCLCOOOOAMMMMM + OHOLCOOLLOMAMMMM + HOOKLLIOAAAAAAAM + MLLALIBOKAAAAAMM + MMLAMOOLLAAAAMMM + MMMLMOOOOLAAMMMM + MMMMLOOOOOLAMMMM + MMMMOLOLOLOLMMMM + MMMMMMMMMMMMMMMM +} +# tile 71 (goblin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMLKMMMMMMMMMM + MMMCJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MJJAMIIKMMMAAMMM + MIKMIGIGIJAAMMMM + JMICKIIIJKMMMMMM + MMMIIJJJKMAMMMMM + MMMMKICJAAAAAMMM + MMMMICKKJAMMMMMM + MMMMIKAIJAMMMMMM + MMMIKAAMIKMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 72 (hobgoblin) +{ + MMMMMMMMMMMMMMMM + MMMMMLKMMMMMMMMM + MMMMCKAMMMMMMMMM + MMMCJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MJJAMIIKMMMAAMMM + MIKMIHIHIJAAMMMM + JMICKIIIJKMMMMMM + MMMIIJJJKMAMMMMM + MMMMKICCAAAAAMMM + MMMMIIIIJAMMMMMM + MMMMICKKJAMMMMMM + MMMMIKAIJAMMMMMM + MMMIKAAMIKMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 73 (orc) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMLPLAMMMMMMMM + MMMMMPMAMMMMMMMM + MMKCCAKKKAMAAMMM + MMBPCKJMPMAAAMMM + MMBAGGFAAPNOMMMM + MMBAJJPNOAAAMMMM + MMMMBNOJAAAAAAMM + MMMBJACPAAAAMMMM + MMBPPABPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 74 (hill orc) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMLKLAMMMMMMMM + MMMMMKMAMMMMMMMM + MMKGGAFFKAMAAMMM + MMJKGFFMKMAAAMMM + MMJAHHFAAKNOMMMM + MMJAGFFNOAAAMMMM + MMMMGNNFAAAAAAMM + MMMGGAGFAAAAMMMM + MMKJJAKJJAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 75 (Mordor orc) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMLPLAMMMMMMMM + MMMMMPMAMMMMMMMM + MMKIIAIIKAMAAMMM + MMBPIDDMPMAAAMMM + MMBAGGFAAPMOMMMM + MMBAIDDNOAAAMMMM + MMMMBNOJAAAAAAMM + MMMBIAIPAAAAMMMM + MMBPPABPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 76 (Uruk-hai) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMLPLAMMMMMMMM + MMMMMPMAMMMMMMMM + MMIIIAIIIAMMMMMM + MMBPIKIMBAAAAMMM + MMBIGMPPPPAAAMMM + MNBADMPDDPAAMMMM + MMBNNJPDDPAAMMMM + MMMMINPPPPAAAAMM + MMMBIAKMAAAAMMMM + MMBPPABPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 77 (orc shaman) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMLPLAMMMMMMMM + MMMMMPMAMMMMMMMM + MMCCCACCCAMMMMMM + MMBPCKCMBAAAAMMM + MMBCGGFJBAAAAMMM + MMBAJJCJBAAAMMMM + MMBAJJCJBAAAMMMM + MMMMCACJAAAAAAMM + MMMBCACPAAAAMMMM + MMBPPABPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 78 (orc-captain) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMNNOOPAMMMMMMM + MMMMLPLAMMMMMMMM + MMMIPPPAMMMMMMMM + MMDIIPADDAMAAMMM + MMBPIADMPMAAAMMM + MMBAGGFAAPMOMMMM + MMBAGGFAAPMOMMMM + MMBAJJPNOAAAMMMM + MMMMBNOJAAAAAAMM + MMMBDAIPAAAAMMMM + MMBPPABPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 79 (rock piercer) +{ + MJKKKKKKKKJAAAMM + MMJJGKGKJJAAAAMM + MMMJKKKJJAAAAMMM + MMMJJKKJJAAAAMMM + MMMMJKKJAAAAMMMM + MMMMJJKJMAAAMMMM + MMMMJJKJMAAAMMMM + MMMMJJJJMMAMMMMM + MMMMMJJMMMAMMMMM + MMMMMJJMMMAMMMMM + MMMMMJJMMMMMMMMM + MMMMMJJMMMMMMMMM + MMMMMJMMMMMMMMMM + MMMMMJMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 80 (iron piercer) +{ + MBPPPPPPPPMAAAMM + MMBBDPDPMMAAAAMM + MMMBPPPMMAAAAMMM + MMMPBPPMMAAAAMMM + MMMMBPPMAAAAMMMM + MMMMBBPMAAAAMMMM + MMMMPBPMAAAAMMMM + MMMMPBPMMMAMMMMM + MMMMMBPMMMAMMMMM + MMMMMBPMMMAMMMMM + MMMMMBPMMMMMMMMM + MMMMMBPMMMMMMMMM + MMMMMBMMMMMMMMMM + MMMMMPMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 81 (glass piercer) +{ + MNBBBBBBBBPAAAMM + MMNNDBDBPPAAAAMM + MMMNBBBPPAAAAMMM + MMMPNBBPPAAAAMMM + MMMMNBBPAAAAMMMM + MMMMNNBPAAAAMMMM + MMMMPNBPAAAAMMMM + MMMMPNBPMMAMMMMM + MMMMMNBMMMAMMMMM + MMMMMNBMMMAMMMMM + MMMMMNBMMMMMMMMM + MMMMMNBMMMMMMMMM + MMMMMNMMMMMMMMMM + MMMMMPMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 82 (rothe) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMKMMMM + MMMMMMMMMMMMKMMM + MMMMMMMMMMMMKMMM + MMMMMMMJJJKKJMMM + MMMMMJKKKKKKKMMM + MMAAAKKKKKKKKMMM + MAAAAAKKKKKKKAMM + AAKKAAKKKKKAKAMM + MKEKKAKKKJAAKAMM + MKKKJAKKAJAAKMMM + MMKJAAAKAAAMMMMM + MMAAKAMKAMMMMMMM + MMAMMAMKMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 83 (mumak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMPMMMM + MPPMMMMMMMMMPMMM + PPPMMMPPPPPMPMMM + PPPPPPPPPMPPMMMM + PPPPBPPBBPMPPMMM + PPPBPPPPPPMPPMMM + MPDPPDDPPMPPPMMM + MMBPPDDPMPPPPAMM + MMPPPPPPPPPPPAMM + MMPPPPOMMPAPPAMM + MOOPPOOAPPAPPAMM + OOPPOOAAPPAMMMMM + MPPPAPAMPPMMMMMM + PPPAMMMMMMMMMMMM + MAAMMMMMMMMMMMMM +} +# tile 84 (leocrotta) +{ + MMMMMMMMMMMMMMMM + MMAMMAMMMMMMMMMM + MMAOOAMMMMJMMMMM + MMAOOAAMMMMJMMMM + MAPOAFAMMMMJMMMM + MAPOAAAMJJJJMMMM + MAOPAAJKKKKKJMMM + MAOAAKJJKKJKJAMM + MMMJKKKKKJAKKAMM + MMMJKJKKJAAKKAMM + MMJKJAKKAAPAPAMM + MMKKAAKKAAPAPAMM + MPAPAAPAPAMMMMMM + MPAPAMPAPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 85 (wumpus) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMBMMM + MMMMMMMMMMMMMBMM + MMMMMMMBBBBBMBMM + MMMMBBBPPBBBBMMM + MMMBOOBBBPBBBBMM + MMMOOBBBBBPBBBMM + MMDABBAABBPBBBAM + MBOOBBDABEBBEBAA + MBOBBBBBBEBEBBAA + MBBBBBBBEBBABBAA + MEBBBBBEABBABBAA + MMEEEEEAABBAMMMM + MMMMMBBAMBBMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 86 (titanothere) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMPPPMPM + MMMMMMMPPPPPPPAP + MMMMMPPPPPPPPPMA + MMPMPMPPPPPPPPMA + MMPPPMPPPPPPPPPA + MMPPPPMPPPPPPPPA + MPPPPPMPPPPPPPMM + MPPEPPMPPPPMPPAM + PBPPPMPPPMAAPPAM + PPPPMAAPPAAMMMMM + MPPMPPAPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 87 (baluchitherium) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMPPPMPM + MMMMMMMPPPPPPPAP + MMMMMPPPPPPPPPMA + MMPMPMPPPPPPPPMA + MMPPPMPPPPPPPPPA + MMPPPPMPPPPPPPPA + BPPPPPMPPPPPPPMM + BMPEPPMPPPPMPPAM + PBMPPMPPPMAAPPAM + PPPPMAAPPAAMMMMM + MPPMPPAPPAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 88 (mastodon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMOMMMOMMMMMMMMM + MNMMPOPMPMMMMMMM + NMMPNPPPPPMMMMMM + OMPNPPPPPPMPPMMM + OMPOPEPPPMPPPPPM + MOPOPOPMMPPPPPAP + MMPPOPPMPPPPPPAA + MMPPAAAPPPPAPPAM + MMPMMMAPPAAAPPAM + MMMMMMMPPAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 89 (sewer rat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKMMKMJKKKKMMMM + MMKKKKJKKJKKKMMM + MMJAKAKJJJKKKJMM + MMGKGKJKAKKAKKAM + MKKJJJJKAKAAKKAM + MPJJAAKKAJAJKAMM + MMAAMKKAMMJKAMMM + MMMMMMMMMJJAMMMM + MMMMMMMMMMMMMMMM +} +# tile 90 (giant rat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKMMKMJKKKKKMMM + MMKKKKJKKKJKKKMM + MMJAKAKJKJJKKKJM + MMGAGAKJKJJKKKKM + MMAKAKJKKAKKAKKA + MKKJJJJKAJKAAKKA + MPJJAAKKAJJAJKAM + MMAAMKKAAMMJKAMM + MMMMMMMMMMJJAMMM + MMMMMMMMMMMMMMMM +} +# tile 91 (rabid rat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKMMKMJKKKKKMMM + MMKKKKJKKKJKKKMM + MMJAKAKJKJJKKKJM + MMGAGAKJKJJKKKKM + MMAKAKJKKAKKAKKA + MKKJOOOKAJKAAKKA + MPJOOAKKAJJAJKAM + MMAOOOKAAMMJKAMM + MOOOOOOOOMJJAMMM + MMMMMMMMMMMMMMMM +} +# tile 92 (wererat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKMMKMJKKKKKMMM + MMKKKKJKKKJKKKMM + MMLLLLKJKJJKKKJM + MMFLFLLJKJJKKKKM + MMLLLLJKKAKKAKKA + MKKJJJJKAJKAAKKA + MPJJAAKKAJJAJKAM + MMAAMKKAAMMJKAMM + MMMMMMMMMMJJAMMM + MMMMMMMMMMMMMMMM +} +# tile 93 (rock mole) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMAAAAAMMMM + MMMAAAAAAAAAAMMM + MMAAAAAAAAAAAAMM + MMJAJAAAAAAAAAAM + MAAAAAAAAAAAAAAM + ANMNAAAAAAAAAAAM + AMMMAAAAMMMAAAMM + ANMNAAMAAMMMAAMM + MAAAAMMMMMMMMMMM +} +# tile 94 (woodchuck) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMNKKNAMMMMM + MMMMMMKNOJAMMMMM + MMMMMMKNOJAMMMMM + MMMMMKKKKKJAMMMM + MMMMJJKLLJJJAAMM + MMMMMMKLLJAAAAAM + MMMMMMKJJJAAAAMM + MMMMMJJAAJJAAMMM + MMMMMMMMMMMMMMMM +} +# tile 95 (cave spider) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPAMMMMMM + MMMMMMMPAMMMMMMM + MMMMMMPAPBBAMMMM + MMMPAMAPBPPPAMMM + MMMABBPPAPPAAMPA + MMMGPPPPAAAPPPAA + MMMPPGPAAPPAAAAM + MMDMPAPAPAAPPAMM + MMMMDMPAAPAMAPAM + MMMMMPAAMAPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 96 (centipede) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPBPPMMMMMM + MMMMBBPAAAMMMMMM + MMPPBAAAMMMMMMMM + MPAPBBBPPPPMMMMM + MMPAAPPBBBAMMMMM + MMMMPAAPAPBPPMMM + MMMMMMAABBPPMMMM + MMMMMMBBMPPAPMMM + MMMMPBBPPAPMAMMM + MMMGPPPAPMAPMMMM + MMMPPGPAAPMMMMMM + MMBMPAAMMMMMMMMM + MMMBMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 97 (giant spider) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMJAMMMMMM + MMMMMMMJAMMMMMMM + MMMMMMJAJKKAMMMM + MMMJAMAJKJJJAMMM + MMMAKKJJAJJAAMJA + MMMGJJJJAAAJJJAA + MMMJJGJAAJJAAAAM + MMDMJAJAJAAJJAMM + MMMMDMJAAJAMAJAM + MMMMMJAAMAJAMMMM + MMMMMMMMMMMMMMMM +} +# tile 98 (scorpion) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJKJKJAAMM + MMMMMMJAMJKJKKAM + MMMMMMMKAMMMJJJA + MMMMMMJAMMMMKKJA + MMMMMMMMMMMJJJKA + MMMMMMMAJKKAJJAM + MMMMMAAJKJJJAAMM + MMMAKKJJAJJAAMMM + MMMGJJJJAAAJJJAM + MMMJJGJAAJJAAAJM + MMDMJAJAJAAJJAMM + MMMMDMJAAJAMJAAM + MMMMMMMJAAJAMMMM + MMMMMMMMMMMMMMMM +} +# tile 99 (lurker above) +{ + MAAAAAAAAAAAAAAA + MMMAAGFAAGFAAAMM + MMMAAAAAAAAAAAMM + MMMMAODODODOAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 100 (trapper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAODODODOAMMM + MMMAAAAAAAAAAAMM + MMMAAGFAAGFAAAMM + MAAAAAAAAAAAAAAA +} +# tile 101 (white unicorn) +{ + MMMMMMMMMMMMMMMM + MMHPMMMMMMMMMMMM + MMPHOMNNMMMMMMMM + MMMPHNNBMMMMMMMM + MMMONENBMMMMMMMM + MMONNNNBMMMMMMMM + MMNOANNBAAMMMMMM + MMMAONNBAMMMMMMM + MMMONNNONNNMMMMM + MMNONNNNONNOAAAM + MMNMONONNONAOAMM + MMOAANAAAAOAAMMM + MMMLAOAAOAOAMMMM + MMMMMOAALAOMMMMM + MMMMMLAMMMLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 102 (gray unicorn) +{ + MMMMMMMMMMMMMMMM + MMHPMMMMMMMMMMMM + MMPHOMPPMMMMMMMM + MMMPHPPNMMMMMMMM + MMMMPGPNMMMMMMMM + MMMPPPPNMMMMMMMM + MMPPAPPNAAMMMMMM + MMMAPPPNAMMMMMMM + MMMMPPPMPPPMMMMM + MMPMPPPPMPPMAAAM + MMPMMPMPPMPAMAMM + MMPAAPAAAAMAAMMM + MMMLAMAAMAMAMMMM + MMMMMMAALAMMMMMM + MMMMMLAMMMLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 103 (black unicorn) +{ + MMMMMMMMMMMMMMMM + MMHPMMMMMMMMMMMM + MMPHOMAAMMMMMMMM + MMMPHAAJMMMMMMMM + MMMAADAJMMMMMMMM + MMAAAAAJMMMMMMMM + MMAAPAAJPPMMMMMM + MMMPAAAJPMMMMMMM + MMMAAAAAAAAMMMMM + MMAAAAAAAAAAPPPM + MMAMAAAAAAAPAPMM + MMAPPAPPAPAPPMMM + MMMLPAPPAPAPMMMM + MMMMMAPPLPAMMMMM + MMMMMLPMMMLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 104 (pony) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMJJMMMMMMMMM + MMMMKKKJMMMMMMMM + MMMKKEKJMMMMMMMM + MMKKJKKJMMMMMMMM + MMJJAKKJAAMMMMMM + MMMAKKKJAMMMMMMM + MMMKKKKKKKJMMMMM + MMMKKKKKKKJJAAAM + MMMKJKJJJJJAJAMM + MMMJAJAAAAJAAMMM + MMMJAJAAJAJAMMMM + MMMLMJAALAJMMMMM + MMMMMLAMMMLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 105 (horse) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMJJMMMMMMMMM + MMMMKKKJMMMMMMMM + MMKKKEKJMMMMMMMM + MKKKJKKJAAMMMMMM + MJJJAKKJAAMMMMMM + MMMAKKKJAMMMMMMM + MMMKKKKKKKKJAMMM + MMKKKKKKKKKKJAMM + MMKJJKJJJJJKAJAM + MMJAAJAAAAAJAJAM + MMJAAJAAAJAJAAMM + MMLAMJAAMLMJAMMM + MMMMMLAMMMMLMMMM + MMMMMMMMMMMMMMMM +} +# tile 106 (warhorse) +{ + MMMMMMMMMMMMMMMM + MMMMMJJJMMMMMMMM + MMMKKKKJJMMMMMMM + MKKKKEKJJMMMMMMM + KKKKKKKJJAAMMMMM + JKKKJKKJJAAMMMMM + MJJJAKKJJAAMMMMM + MMMAKKKJJAMMMMMM + MMMKKKKKKKKKJAMM + MMKKKKKKKKKKKJAM + MMKKJKKJKKJKKJJA + MMKJAKJAKJAKJAJA + MMKJAKJAKJAKJAMM + MMLCMKJALCMKJMMM + MMMMMLCMMMMLCMMM + MMMMMMMMMMMMMMMM +} +# tile 107 (fog cloud) +{ + MMMMMMMPMMMMMMMM + MMMMPMMPMMMMMMMM + MMMMMPMPMMMPMMMM + MMMPMMMMMMMPMMMM + MMPMMPMPMPMMMMMM + MMMMPPMPPMPMPMMM + MPMMAPAPPPMMPMMM + MMMPMPPPPMPPMMMM + MMMMMMPPPPPMPMPM + MMMPMPPPPPMMPMMM + MMMMPMMPMPPMPMMM + MPMMMPMPMMMPPPMM + MMPMPMMMMPPMMMMM + MMMMMMMPMMMMPMMM + MMPMMPMPMMPMMMMM + MMMMMMMMMMMMMMMM +} +# tile 108 (dust vortex) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKMMKKKKMMMMM + MMMKMMKKJJJKMMMM + MMKMMKJJJJMMKMMM + MKJMKJJMJKKMMKMM + MKJJKJMJJJJKMMMM + MKJJJJMMMMJJKMMM + MKKJMJMMMJMJKKMM + MMKJJMMMMJJJJKMM + MMMKJJJJMJKJJKMM + MKMMKKJMJJKMJKMM + MMKMMJJJJKMMKMMM + MMMKJJJKKMMKMMMM + MMMMKKKKMMKMMMMM + MMMMMMMMMMMMMMMM +} +# tile 109 (ice vortex) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNMMNNNNMMMMM + MMMNMMNNOOONMMMM + MMNMMNOOOOMMNMMM + MNOMNOOMONNMMNMM + MNOONOMOOOONMMMM + MNOOOOMMMMOONMMM + MNNOMOMMMOMONNMM + MMNOOMMMMOOOONMM + MMMNOOOOMONOONMM + MNMMNNOMOONMONMM + MMNMMOOOONMMNMMM + MMMNOOONNMMNMMMM + MMMMNNNNMMNMMMMM + MMMMMMMMMMMMMMMM +} +# tile 110 (energy vortex) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEMMEEEEMMMMM + MMMEMMEEAAAEMMMM + MMEMMEAAAAMMEMMM + MEAMEAAAAIEMMEMM + MEAAIAAAAAAEMMMM + MEAAAAAAAAAAEMMM + MEEAAAAAAAAAEEMM + MMEAAAAAAAAAAEMM + MMMEAAAAAAIAAEMM + MEMMEIAAAAEMAEMM + MMEMMAAAAEMMEMMM + MMMEAAAEEMMEMMMM + MMMMEEEEMMEMMMMM + MMMMMMMMMMMMMMMM +} +# tile 111 (steam vortex) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPMMPPPPMMMMM + MMMPMMPPBBBPMMMM + MMPMMPBBBBMMPMMM + MPBMPBBPBPPMMPMM + MPBBPBPBBBBPMMMM + MPBBBBPMPPBBPMMM + MPPBPBMMMBPBPPMM + MMPBBPPMPBBBBPMM + MMMPBBBBPBPBBPMM + MPMMPPBPBBPMBPMM + MMPMMBBBBPMMPMMM + MMMPBBBPPMMPMMMM + MMMMPPPPMMPMMMMM + MMMMMMMMMMMMMMMM +} +# tile 112 (fire vortex) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMDMMDDDDMMMMM + MMMDMMDDCCCDMMMM + MMDMMDCCCCMMDMMM + MDCMDCCHCDDMMDMM + MDCCDCHCCCCDMMMM + MDCCCCHHHHCCDMMM + MDDCHCHHHCHCDDMM + MMDCCHHHHCCCCDMM + MMMDCCCCHCDCCDMM + MDMMDDCHCCDMCDMM + MMDMMCCCCDMMDMMM + MMMDCCCDDMMDMMMM + MMMMDDDDMMDMMMMM + MMMMMMMMMMMMMMMM +} +# tile 113 (baby long worm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMCLCMMMMMMM + MMMMMMLLLMMMMMMM + MMMMMGGAGGMAMMMM + MMMMMGGAGGAAAMMM + MMMMMMLLLAAAMCMM + MMMMMMLLLAAMCCMM + MMMMMMCLLCCCCAMM + MMMMMMMLLLCCAMMM + MMMMMMMMCLLMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 114 (baby purple worm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMIMMMMMMMM + MMMMMMIIIMMMMMMM + MMMMMMIIIMMMMMMM + MMMMMGGAGGMAMMMM + MMMMMGGAGGAAAMMM + MMMMMMIIIAAAMDMM + MMMMMMIIIAAMDDMM + MMMMMMIIIDDDDAMM + MMMMMMMIIIDDAMMM + MMMMMMMMIIIMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 115 (long worm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMCLCMMMMMMMM + MMMMCLLLCMMMMMMM + MMMMLLLLLMMMMMMM + MMMGGGLGGGAAMMMM + MMMGAGLGAGAAAMMM + MMMGGGLGGGAAAMMM + MMMMLLLLLAAACCMM + MMMMLLLLLAACCCMM + MMMMCLLLLCCCCAMM + MMMMMLLLLLCCAMMM + MMMMMMCLLLLMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 116 (purple worm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMDIDMMMMMMMM + MMMMDIIIDMMMMMMM + MMMMIIIIIMMMMMMM + MMMGGGIGGGAAMMMM + MMMGAGIGAGAAAMMM + MMMGGGIGGGAAAMMM + MMMMIIIIIAAADDMM + MMMMIIIIIAADDDMM + MMMMDIIIIDDDDAMM + MMMMMIIIIIDDAMMM + MMMMMMDIIIIMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 117 (grid bug) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMDMMMMNHCNMMDMM + MDMDMMNHDNCNDEDM + DMDMDMNNHCNDMDED + MDMMMDMNNHDNDMMM + MDDDMMENNGMDMDEM + MMDDDDEEEEGDMMDE + DMMMMMDEHEEMDMMM + MDMMMMMMMHMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 118 (xan) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMGGMMMM + MMMHHHMMMGOGGMMM + MMMMMHHMMGGGGMMM + MMMHHHHHMGGGMMMM + MMMMMMMGGMMMAAAM + MMMMMGOGGHHAAAAM + MMMMGOGGMMHHAAAM + NNNGOGGMAAHHHAMM + NANGGGGMAAHAHMMM + NNNGGNNNAAAAAAMM + MMGGGNANAAMAAAMM + MGGGANNNAAMAMAMM + MMGMMAAAAAAMMMMM + MMMMMMAAMAAMMMMM +} +# tile 119 (yellow light) +{ + MMMMMMMMMMMMMMMM + MMMMMMNAMMMMMMMM + MMMMMMHAMMMMMMMM + MMNAMNHNAMNAMMMM + MMMLALHLALAMMMMM + MMMMNHHHNAMMMMMM + MMNLHHHHHLNAMMMM + NHHHHHHHHHHHNAMM + MMNLHHHHHLNAMMMM + MMMMNHHHNAMMMMMM + MMMLALHLALAMMMMM + MMNAMNNNAMNAMMMM + MMMMMMHAMMMMMMMM + MMMMMMNAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 120 (black light) +{ + MMMMMMMMMMMMMMMM + MMMMMMAAMMMMMMMM + MMMMMMAAMMMMMMMM + MMAAMAAAAMAAMMMM + MMMAAAAAAAAMMMMM + MMMMAAAAAAMMMMMM + MMAAAAAAAAAAMMMM + AAAAAAAAAAAAAAMM + MMAAAAAAAAAAMMMM + MMMMAAAAAAMMMMMM + MMMAAAAAAAAMMMMM + MMAAMAAAAMAAMMMM + MMMMMMAAMMMMMMMM + MMMMMMAAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 121 (zruty) +{ + MMMMMMMMMMMMMMMM + MMMMMMFFGFMMMMMM + MMMMOOFGFFFFMMMM + MMMAOFGFOOKFFMMM + MMMFFGFAOAJKKFMM + MMFFFFFFJAAJKKMM + MMODOFFJAJJKKJAM + MMDDDDJAJJKJJAAM + MMJODOAJJJAJJAAA + MKKJAJJJKJAJJAAA + MKKAAJKKKKJAAAAA + MMMAJJKKKKJJAAAA + MMMKJJAAAAKJAAAM + MMJKJJJAAJJJJMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 122 (couatl) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMIMMMMIMM + MMMMKKAIIIMMIIIM + MMMNAOJAKIMIIIII + MMMKKJAJJKKKMMII + MMMKKAAIJJJJJMMI + MMMFAAMIMMMKJMMI + MMFAFAMMAAAKJAAM + MMMMMMMAAAJJAAAM + MMMMMMAKKJJAAAMM + MMMMMMKJAAAAAJAM + MMMMMJJAAMMMJAMM + MMMMMMJJJJJJAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 123 (Aleax) +{ + MMMMMMMMMMMMMMMM + MMMMMMBBBBMMIMMM + MMIMMBFMMMBMMMMM + MMMMBFMHHAMBMMMM + MMMBFMHHHHAMBMIM + MMMBFMLFLFAMFBMM + MIMBFMLLLLAMFBMM + MMMBFMALLAMFBMMM + MMBFMLLAALLMABAM + MBFMLLLLLLLLAFBM + MBFMLALLLLALAFBM + MBFMLAJJKJALAFBM + MMBFMMLJJLAAABAM + MMMBFMLLALAABAMM + MMBFMLLAALLAFBMM + MMMMMMMMMMMMMMMM +} +# tile 124 (Angel) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMHHHHMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMCLLCMMAAMM + MMMMMMPLLPMMMMAM + MMMMMMNPPPAMAMMM + MMMMMBBLLPPAAAMM + MMMMMNNLLPPAAAMM + MMMMMMBNNPAAAAMM + MMMMMMBNNPAAAAMM + MMMMMBNNNPAAMAMM + MMMMMBNNNNPAMMMM + MMMMBNNNNNNPMMMM + MMMMMMMMMMMMMMMM +} +# tile 125 (ki-rin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMLPMMMMMMMMMMMM + MMPLOMCMCMMMMMMM + MMMPLCCDMMMMMMMM + MMMKCIKDMMMMMMMM + MMKCCCCDMMMMMMMM + MMCKACCDAMMMMMMM + MMMACCCCCCMMMAMM + MMMKCCCKCCKAAMMM + MMCAKCKCKCAKAMMM + MMCAAKAKAKAMMMMM + MMMLMKALAKMMMMMM + MMMMMLAMMLMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 126 (Archon) +{ + MMMMMMMMMMMMMMMM + MMMMMMOOOOMMMMMM + MMMMMOOOOOOMMMMM + MMMMMOJLLJOMMMMM + MMMMMOLLLLOMMMMM + MMMMOOJLLJOOMMMM + MMMMMMAJJAMMMMMM + MMMMMAAAAAAAMMMM + MMMMAAAAAAAAAMMM + MMMOAAOAAAJLJMMM + MMOOAOAAAACJCMMM + MMMMLAAAACCJCCMM + MMMMMAAAAAJJJMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 127 (bat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJJJCACJJJMMMM + MMJJAAHJHAAJJMMM + MMJAMMMJAMMAJMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMAAAAMMMMMM + MMMMAAAAAAAAMMMM + MMMAAAMAAMAAAMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 128 (giant bat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJKMJMJMJKMMMM + MMKJJJCACJJKJMMM + MJJJAAHJHAAJJKMM + MKJAMMMJAMMAJJMM + MMJAMMMMMMMAJMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAAMMMMM + MMMAAAAAAAAAAMMM + MMAAAAMAAMAAAAMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 129 (raven) +{ + MMAAAAMMMAAAMMMM + MAAAAAAMAAAMMMMM + AAAAAAAAAAAMAAMM + AMMMAAAAAAAAAAAM + MMMMMMAAAAAAAAAM + MMMMMAAAAMMMMMAA + MMMMMADAMMMMMMMA + MMMMMPAMMMMMMMMM + MMMMMPMMMMMMMMMM + MMMMMMMMMPMPMPMM + MMMMMMMMPMPMPMPM + MMMMMMMPMPMPMMMM + MMMMMMMMPMPMPMMM + MMMMMMMMMMMPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 130 (vampire bat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMAAMAMAMAAMMMM + MMAAAAAAAAAAAMMM + MAAAAMDADMAAAAMM + MAAAMMMAMMMAAAMM + MMAMMMMMMMMMAMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAAMMMMM + MMMAAAAAAAAAAMMM + MMAAAAMAAMAAAAMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 131 (plains centaur) +{ + MMMMMMMMMMMMMMMM + MMMKKAMMMMMMMMMM + MMMLLAAMMMMMMMMM + MAAKKAAMMMMMMMMM + MLLAALLAMMMMMMMM + LALLLLALAMMMMMMM + LALLLKALAMAMMMMM + MMLKLKAAAAAMMMMM + MMKLKJKJJKAAMMMM + MKJKJKJKJAKAAAAM + MKAKJJJJKJAAAMAM + MKAAKAAAAKAAMMMM + MMCAKAAJAKAMMMMM + MMMMKAAKAKMMMMMM + MMMMCAMMMCMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 132 (forest centaur) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKKAMMMMMMMMMM + LAMLLAALAMMMMMMM + LAALLAALAMMMMMMM + MLLAALLAMMMMMMMM + MMLLLLAMAMMMMMMM + MMLKLKAAAAAMMMMM + MMKLKJKJJKAAMMMM + MKJKJKJKJAKAMAMM + MKAKJJJJKJAAAAMM + MKAAKAAJAKAMMMMM + MMCMKAAKAKMMMMMM + MMMMCAMMMCMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 133 (mountain centaur) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKKAMMMMMMMMMM + MMMLLAAMMMMMMMMM + MMAKKAAMMMMMMMMM + MLJJJJLAMMMMMMMM + LAJKKJALAMAMMMMM + LAKKKKALAAAMMMMM + MMJJJJKJJKAAMMMM + MKJJJKJKJAKAAAAM + MKAKJJJJKJAAAMAM + MKAAKAAAAKAAMMMM + MMCAKAAJAKAMMMMM + MMMMKAAKAKMMMMMM + MMMMCAMMMCMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 134 (baby gray dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBBBAMMMMMMM + MMMMNPNPPAMMMMMM + MMMBPPPPPAMMMMMM + MMCHHPABPAMAAAMM + MCHCDAMBPAAAAAAM + MMDMMBPPAAAAAAAM + MMMMBBPPPPPAAAAM + MMMBOOPPPPPPAAAM + MMBPOBPPPPPPPAAM + MMBPPBPPOBPAPPAM + MMBPABPMABPAPPAM + MMMMMBPAAMMPPAAM + MMMMMMMMMMMPAAMM +} +# tile 135 (baby silver dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPAMMMMMMM + MMMMOBOBBAMMMMMM + MMMPBBBBBAMMMMMM + MMCHHBAPBAMAAAMM + MCHCDAMPBAAAAAAM + MMDMMPBBAAAAAAAM + MMMMPPBBBBBAAAAM + MMMPNNBBBBBBAAAM + MMPBNPBBBBBBBAAM + MMPBBPBBNPBABBAM + MMPBAPBMAPBABBAM + MMMMMPBAAMMBBAAM + MMMMMMMMMMMBAAMM +} +# tile 136 (baby shimmering dragon) +{ + MIMMMMMMMMMMMMMM + MMMBBBBBBBMIMMMM + MMBFMFFFMFBMMMIM + MBFMMBBBAMFBMMMM + BFMMNPNPPAFBMIMM + BFMBPPPPPAMBMMMM + BMCHHPABPAFBAAIM + BCHCDAMBPAAFBAAM + BMDMMBPPAAAAFBAM + BMMMBBPPPPPAAFBM + BFMBOOPPPPPPAAAB + BFBPOBPPPPPPPAFB + BFBPPBPPOBPAPPFB + BMBPABPMABPAPPFB + BMMMMBPAAMMPPAAB + MBMMMMMMMMMPAABM +} +# tile 137 (baby red dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMIIIAMMMMMMM + MMMMNDNDDAMMMMMM + MMMIDDDDDAMMMMMM + MMCHHDAIDAMAAAMM + MCHCDAMIDAAAAAAM + MMDMMIDDAAAAAAAM + MMMMIIDDDDDAAAAM + MMMIHHDDDDDDAAAM + MMIDHIDDDDDDDAAM + MMIDDIDDHIDADDAM + MMIDAIDMAIDADDAM + MMMMMIDAAMMDDAAM + MMMMMMMMMMMDAAMM +} +# tile 138 (baby white dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMNNNAMMMMMMM + MMMMIOIOOAMMMMMM + MMMNOOOOOAMMMMMM + MMCHHOANOAMAAAMM + MCHCDAMNOAAAAAAM + MMDMMNOOAAAAAAAM + MMMMNNOOOOOAAAAM + MMMNOOOOOOOOAAAM + MMNOONOOOOOOOAAM + MMNOONOOONOAOOAM + MMNOANOMANOAOOAM + MMMMMNOAAMMOOAAM + MMMMMMMMMMMOAAMM +} +# tile 139 (baby orange dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLLLAMMMMMMM + MMMMNCNCCAMMMMMM + MMMLCCCCCAMMMMMM + MMCHHCALCAMAAAMM + MCHCDAMLCAAAAAAM + MMDMMLCCAAAAAAAM + MMMMLLCCCCCAAAAM + MMMLOOCCCCCCAAAM + MMLCOLCCCCCCCAAM + MMLCCLCCOLCACCAM + MMLCALCMALCACCAM + MMMMMLCAAMMCCAAM + MMMMMMMMMMMCAAMM +} +# tile 140 (baby black dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAMMMMMMMM + MMMMNANAAMMMMMMM + MMMAAAAAAMMMMMMM + MMCHHAMAAMMPPPMM + MCHCDMMAAMPPPPPM + MMDMMAAAPPPMPPPM + MMMMAAAAAAAPPPPM + MMMAAAAAAAAAPPPM + MMAAAAAAAAAAAPPM + MMAAAAAAAAAPAAPM + MMAAPAAMPAAPAAPM + MMMMMAAPMMMAAPPM + MMMMMMMMMMMAPPMM +} +# tile 141 (baby blue dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBBBAMMMMMMM + MMMMNENEEAMMMMMM + MMMBEEEEEAMMMMMM + MMCHHEABEAMAAAMM + CCHCDAMBEAAAAAAM + MMDMMBEEAAAAAAAM + MMMMBBEEEEEAAAAM + MMMBOOEEEEEEAAAM + MMBEOBEEEEEEEAAM + MMBEEBEEOBEAEEAM + MMBEABEMABEAEEAM + MMMMMBEAAMMEEAAM + MMMMMMMMMMMEAAMM +} +# tile 142 (baby green dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMGGGAMMMMMMM + MMMMNFNFFAMMMMMM + MMMGFFFFFAMMMMMM + MMCHHFAGFAMAAAMM + MCHCDAMGFAAAAAAM + MMDMMGFFAAAAAAAM + MMMMGGFFFFFAAAAM + MMMGOOFFFFFFAAAM + MMGFOGFFFFFFFAAM + MMGFFGFFOGFAFFAM + MMGFAGFMAGFAFFAM + MMMMMGFAAMMFFAAM + MMMMMMMMMMMFAAMM +} +# tile 143 (baby yellow dragon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMNNNAMMMMMMM + MMMMDHDHHAMMMMMM + MMMNHHHHHAMMMMMM + MMCHHHANHAMAAAMM + MCHCDAMNHAAAAAAM + MMDMMNHHAAAAAAAM + MMMMNNHHHHHAAAAM + MMMNOOHHHHHHAAAM + MMNHONHHHHHHHAAM + MMNHHNHHONHAHHAM + MMNHANHMANHAHHAM + MMMMMNHAAMMHHAAM + MMMMMMMMMMMHAAMM +} +# tile 144 (gray dragon) +{ + MMMMMMBBBPAMMMMM + MMMMMNPNPPPAMMMM + MMMMBPPPPPPAMMMM + MMDCHHPMMPPAMMMM + CHCHCDMMBPPAMMMM + HDMDMMMBPPAMMMMM + MMMMMMOBPAAAAAAM + MMMMBOBPAAAAAAAA + MMBOOBPAMPPMAAAM + MBOOOBPPPPPPMAAM + MBOOOBPPPPPPPAAM + PPOOBBPPPPPPPPAM + BPMOBPPOOPPMPMAM + BPAABPMAAPPAPPAM + MMMMBPAAMMMPPMAM + MMMMMMMMPPPPMAMM +} +# tile 145 (silver dragon) +{ + MMMMMMPPPBAMMMMM + MMMMMOBOBBBAMMMM + MMMMPBBBBBBAMMMM + MMDCHHBMMBBAMMMM + CHCHCDMMPBBAMMMM + HDMDMMMPBBAMMMMM + MMMMMMNPBAAAAAAM + MMMMPNPBAAAAAAAA + MMPNNPBAMBBMAAAM + MPNNNPBBBBBBMAAM + MPNNNPBBBBBBBAAM + BBNNPPBBBBBBBBAM + PBMNPBBNNBBMBMAM + PBAAPBMAABBABBAM + MMMMPBAAMMMBBMAM + MMMMMMMMBBBBMAMM +} +# tile 146 (shimmering dragon) +{ + MIMBFMBBBPAFBMMM + MMBFMNPNPPPAFBMI + MBFMBPPPPPPAFBMM + MBDCHHPMMPPAFBIM + CBCHCDMMBPPAFBMM + HDBBMMMBPPAMBMMI + MMBFMMOBPAAAABAM + MBFMBOBPAAAAAFBA + BFBOOBPAMPPMAAFB + MBOOOBPPPPPPMAFB + MBOOOBPPPPPPPAFB + PPOOBBPPPPPPPPFB + BPMOBPPOOPPMPMFB + BPAABPMAAPPAPPFB + MMMMBPAAMMMPPMAB + MMMMMMMMPPPPMABM +} +# tile 147 (red dragon) +{ + MMMMMMIIIDAMMMMM + MMMMMNDNDDDAMMMM + MMMMIDDDDDDAMMMM + MMDCHHDMMDDAMMMM + CHCHCDMMIDDAMMMM + HDMDMMMIDDAMMMMM + MMMMMMHIDAAAAAAM + MMMMIHIDAAAAAAAA + MMIHHIDAJDDJAAAM + MIHHHIDDDDDDJAAM + MIHHHIDDDDDDDAAM + DDHHIIDDDDDDDDAM + IDMHIDDHHDDJDJAM + IDAAIDMAADDADDAM + MMMMIDAAJJJDDJAM + MMMMMMMMDDDDJAMM +} +# tile 148 (white dragon) +{ + MMMMMMNNNOAMMMMM + MMMMMIOIOOOAMMMM + MMMMNOOOOOOAMMMM + MMDCHHOMMOOAMMMM + CHCHCDMMNOOAMMMM + HDMDMMMNOOAMMMMM + MMMMMMONOAAAAAAM + MMMMNONOAAAAAAAA + MMNOONOAMOOMAAAM + MNOOONOOOOOOJAAM + MNOOONOOOOOOOAAM + OOOONNOOOOOOOOAM + NOMONOOOOOOMOJAM + NOAANOMAAOOAOOAM + MMMMNOAAMMMOOJAM + MMMMMMMMOOOOJAMM +} +# tile 149 (orange dragon) +{ + MMMMMMLLLCAMMMMM + MMMMMNCNCCCAMMMM + MMMMLCCCCCCAMMMM + MMDCHHCMMCCAMMMM + CHCHCDMMLCCAMMMM + HDMDMMMLCCAMMMMM + MMMMMMOLCAAAAAAM + MMMMLOLCAAAAAAAA + MMLOOLCAMCCKAAAM + MLOOOLCCCCCCJAAM + MLOOOLCCCCCCCAAM + CCOOLLCCCCCCCCAM + LCMOLCCOOCCKCJAM + LCAALCMAACCACCAM + MMMMLCAAMKKCCJAM + MMMMMMMMCCCCJAMM +} +# tile 150 (black dragon) +{ + MMMMMMAAAAMMMMMM + MMMMMNANAAAMMMMM + MMMMAAAAAAAMMMMM + MMDCHHAMMAAMMMMM + CHCHCDMMAAAMMMMM + HDMDMMMAAAMMMMMM + MMMMMMAAAMMPPPPM + MMMMAAAAPPPPPPPP + MMAAAAAAAAAMPPPM + MAAAAAAAAAAAAPPM + MAAAAAAAAAAAAPPM + AAAAAAAAAAAAAAPM + AAMAAAAAAAAMAAPM + AAPPAAMPPAAPAAPM + MMMMAAPPMMMAAAPM + MMMMMMMMAAAAAMMM +} +# tile 151 (blue dragon) +{ + MMMMMMBBBEAMMMMM + MMMMMNENEEEAMMMM + MMMMBEEEEEEAMMMM + MMDCHHEMMEEAMMMM + CHCHCDMMBEEAMMMM + HDMDMMMBEEAMMMMM + MMMMMMOBEAAAAAAM + MMMMBOBEAAAAAAAA + MMBOOBEAMEEMAAAM + MBOOOBEEEEEEJAAM + MBOOOBEEEEEEEAAM + EEOOBBEEEEEEEEAM + BEMOBEEOOEEMEJAM + BEAABEMAAEEAEEAM + MMMMBEAAMMMEEJAM + MMMPMMMMEEEEJAMM +} +# tile 152 (green dragon) +{ + MMMMMMGGGFAMMMMM + MMMMMNFNFFFAMMMM + MMMMGFFFFFFAMMMM + MMDCHHFMMFFAMMMM + CHCHCDMMGFFAMMMM + HDMDMMMGFFAMMMMM + MMMMMMOGFAAAAAAM + MMMMGOGFAAAAAAAA + MMGOOGFAMFFMAAAM + MGOOOGFFFFFFJAAM + MGOOOGFFFFFFFAAM + FFOOGGFFFFFFFFAM + GFMOGFFOOFFMFJAM + GFAAGFMAAFFAFFAM + MMMMGFAAMMMFFJAM + MMMMMMMMFFFFJAMM +} +# tile 153 (yellow dragon) +{ + MMMMMMNNNHAMMMMM + MMMMMDHDHHHAMMMM + MMMMNHHHHHHAMMMM + MMDCHHHMMHHAMMMM + CHCHCDMMNHHAMMMM + HDMDMMMNHHAMMMMM + MMMMMMONHAAAAAAM + MMMMNONHAAAAAAAA + MMNOONHAJHHJAAAM + MNOOONHHHHHHJAAM + MNOOONHHHHHHHAAM + HHOONNHHHHHHHHAM + NHMONHHOOHHJHJAM + NHAANHMAAHHAHHAM + MMMMNHAAJJJHHJAM + MMMMMMMMHHHHJAMM +} +# tile 154 (stalker) +{ + MMMMMMMMMMMMMMMM + MMMMMMMPPPMMMMMM + MMMMMMPMPMPMMMMM + MMMMMPPPPPPMMMMM + MMMMMPPMMPPPMMMM + MMMMPPPPPPMPMMMM + MMMMPMPPPPMPMMMM + MMMMPMPPPMMPMMMM + MMMMPMMPPMMPMMMM + MMMMPMPPPPMPMMMM + MMMMPMPMMPMPMMMM + MMMMPMPMMPMPMMMM + MMMMMMPMMPMMMMMM + MMMMMMPMMPMMMMMM + MMMMMPPMMPPMMMMM + MMMMMMMMMMMMMMMM +} +# tile 155 (air elemental) +{ + MMMMMMMMMMMMMMMM + MMMPMPPPMMPMMMMM + MMPMPAPAMPMMMMMM + PMMPPPPPPMMPMMMM + MPMPPAAPPPMMMPMM + MMPPPAAPMPMPMMMM + MMPAPAAPAPMMMMMM + PMPAPPPMAPMPMAAM + MMPAMPPMAPMAAAAM + MMPAPPPPAPAAAAMM + MMPAPMAPAPAAAAMM + MMPAPMAPAPAAAAAM + MMMMPMAPAAAAAAAM + MMPMPMAPPAAAAAAM + MMMPPMAPPPAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 156 (fire elemental) +{ + MMMMMMMMMMMMMMMM + MHMMLDDDMMMMMMMM + MMMLDADACMHMMMMM + HMMDDDDDDMMHMHMM + MMLDDAADDDMMMMMM + MMDDDAADCDMHMMMM + MMDADAACADMMMMMM + HMDADDDCADMMMAAM + MMDACDDCADMAAAAM + MMDADDDDADAAAAMM + MMDADCADADAAAAMM + HMDADCADADAAAAAM + MMMMDCADAAAAAAAM + MHMLDCADDAAAAAAM + MMLDDCADDDAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 157 (earth elemental) +{ + MMFMMMMMMMMMMMMM + MMMMCKKKMMFMMMMM + MMMCKAKAJMMMMFMM + MMMKKKKKKMMMMMMM + MMCKKAAKKKMFMMFM + MFKKKAAKJKMMMMMM + MMKAKAAJAKMMFMMM + MMKAKJJJAKMMMAAM + FMKAJKKJAKMAAAAM + MMKAKKKKAKAAAAMM + MMKAKJAKAKAAAAMM + MMKAKJAKAKAAAAAM + MMMMKJAKAAAAAAAM + MFMCKJAKKAAAAAAM + MMCKKJAKKKAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 158 (water elemental) +{ + MMMMMMMMMMMMMMMM + MMMMPBBBMMEMMMMM + MEMPBABAEMMMEMMM + MMMBBBBBBMMMMMMM + MMPBBAABBBMEMMEM + EMBBBAABEBMMMMMM + MMBABAABEBMEMMMM + MMBABBBBEBMMMAAM + MMBAPBBEABMAAAAM + EMBABBBBABAAAAMM + MMBABEABABAAAAMM + MMBABEABABAAAAAM + MMMMBEABAAAAAAAM + MEMPBEABBAAAAAAM + MMPBBEABBBAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 159 (lichen) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMFFFMMMFFFMMMM + MMFCFFFFFCCFAMMM + MFCOOFFFCOFFFAMM + MFCOOFFFCFFFFAMM + MMFFFFFFFFFFAMMM + MMMAFFFCCFFFAMMM + MMMFFFFCOFFAAMMM + MMFCCFFCOFCFAMMM + MMFCOFFCFFOCFAMM + MMFFCFFFCFFFFAMM + MMMFFFAAFFFFFAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 160 (brown mold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJJJMMMJJJMMMM + MMJKJJJJJKKJAMMM + MJKCCJJJKCJJJAMM + MJKCCJJJKJJJJAMM + MMJJJJJJJJJJAMMM + MMMAJJJKKJJJAMMM + MMMJJJJKCJJAAMMM + MMJKKJJKCJKJAMMM + MMJKCJJKJJCKJAMM + MMJJKJJJKJJJJAMM + MMMJJJAAJJJJJAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 161 (yellow mold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMHHHMMMHHHMMMM + MMHHHHHHHNHHAMMM + MHHNNOHHNNOHHAMM + MHHNNOOHHOOHHAMM + MMHHOOHHHHHHAMMM + MMMAHHHHHHHHAMMM + MMMHHHHNNOHAAMMM + MMHHHHHNNOHHAMMM + MMHNNOHHHONOHAMM + MMHHOHHHHHOOHAMM + MMMHHHAAHHHHHAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 162 (green mold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMFFFMMMFFFMMMM + MMFGFFFFFGGFAMMM + MFGOOFFFGOFFFAMM + MFGOOFFFGFFFFAMM + MMFFFFFFFFFFAMMM + MMMAFFFGGFFFAMMM + MMMFFFFGOFFAAMMM + MMFGGFFGOFGFAMMM + MMFGOFFGFFOGFAMM + MMFFGFFFGFFFFAMM + MMMFFFAAFFFFFAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 163 (red mold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMDDDMMMDDDMMMM + MMDCDDDDDCCDAMMM + MDLLCDDDCLDDDAMM + MDCCCDDDCDDDDAMM + MMDDDDDDDDDDAMMM + MMMADDDCCDDDAMMM + MMMDDDDCLDDAAMMM + MMDCCDDCLDCDAMMM + MMDCLDDCDDLCDAMM + MMDDCDDDCDDDDAMM + MMMDDDAADDDDDAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 164 (shrieker) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMGGGGFFMMMMM + MMMGGGGIGIDFFMMM + MGGGIIGGGIIFFFFM + GIIGIIGGGGGGGDDF + GIIGGGGIIGIIGIDF + GGGGIGGIIGIIGGFF + MMGGGGGGGGGGGMMM + MMMMMMFFFMMAAAAA + MMMMAGGGFFAAAAAA + MMMAGGGGGGFAAAAM + MMMAAAAAAAAAAMMM + MMMMMMMMMMMMMMMM +} +# tile 165 (violet fungus) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMIIIMMMIIIMMMM + MMILIIIIILLIAMMM + MIOOLIIILOIIIAMM + MILLLIIILIIIIAMM + MMIIIIIIIIIIAMMM + MMMAIIILLIIIAMMM + MMMIIIILOIIAAMMM + MMILLIILOILIAMMM + MMILOIILIIOLIAMM + MMIILIIILIIIIAMM + MMMIIIAAIIIIIAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 166 (gnome) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMDFMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMGFFMMMMMMMM + MMMMGGFFFMMMMMMM + MMMMGLLLFMMMMMMM + MMMMMOLOMMMAAAMM + MMMFGOOOFFAAAAMM + MMMGAGOFAFAAAAMM + MMMMLKNKFAAAAMMM + MMMMFGAFFAAMMMMM + MMMMGFAFGMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 167 (gnome lord) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMDMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMGFFMMMMMMMM + MMMMHHHHHMMMMMMM + MMMMGLLLFMMMMMAM + MMMMMOLOMMMAAAMM + MMMFGOOOFFAAAAMM + MMMGAGOFAFAAAAMM + MMMMLKNKFAAAAMMM + MMMMFGAFFAAMMMMM + MMMMGFAFGMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 168 (gnomish wizard) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMGFFMMMMMMMM + MMMMGGFFFMMMMMMM + MMMMGLLLFMMMMMMM + MMMFFOLOFFMAAAMM + MMMGFOOOFFAAAAMM + MMMFAGOFAFAAAAMM + MMMGLKNKFFAAAMMM + MMMFFGFFFFAMMMMM + MMMGFFFFGFAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 169 (gnome king) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMHMCMHMMMMMMM + MMMMHCHCHMMMMMMM + MMMMHHHHHMMMMMMM + MMMMGLLLFMMMAMMM + MMMMMOLOMMMAAAAM + MMMFGOOOFFAAAAMM + MMMGAGOFAFAAAAMM + MMMMLKNKFAAAAMMM + MMMMFGAFFAAMMMMM + MMMMGFAFGMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 170 (giant) +{ + MMMMMMJJJJAAMMMM + MMMMJJJJJJJJAMMM + MMMMJJLLLLJJAMMM + MMMMJFFLLFFJAMMM + MMMMJLLLLLLJAMMM + MMMMALLAALLAAAMM + MMMMMALLLLJAAAAM + MMCCLLJJJJLLCCAA + MCLLLCCKCKCLLLCA + MLLLKLKCKCLKLLLA + MLLAALLCCLLAALLA + MLLAAJJJKKJAALLA + MCLCMJJJJJKKCLAA + MMLLMCLJACLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJMCLLLKAA +} +# tile 171 (stone giant) +{ + MMMMMMJJJJAAMMMM + MMMMJJJJJJJJAMMM + MMMMJJLLLLJJAMMM + MMMMJFFLLFFJAMMM + MMMMJLLLLLLJAMMM + MMMMALLAALLAAAMM + MMMMMALLLLJAAAAM + MMCCLLJJJJLLCCAA + MCLLLCCKCKCLLLCA + MLLLAAKCKCLKLLLA + MLLPPPACCLLAALLA + MLLPPPPAKKJAALLA + MCLCPPPAJJKKCLAA + MMLLPPALACLJLLAA + MMMMMALJACLJAAAA + MMMLLLLJMCLLLKAA +} +# tile 172 (hill giant) +{ + MMMMMMJJJJAAMMMM + MMMMJJJJJJJJAMMM + MMMMJJLLLLJJAMMM + MMMMJFFLLFFJAMMM + MMMMJLLLLLLJAMMM + MMMMALLAALLAAAMM + MMMMMALLLLJAAAAM + MMMJJKJJJJJJJAAM + MMLJJCCKCKCJJLAM + MMJLKKKCKCKKLJAM + MMLAAKKCCKJAALAA + MMLAAJJJKKJAALAA + MMLCMJJJJJKKCLAA + MMLLMCLJACLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJMLLLLKAA +} +# tile 173 (fire giant) +{ + MMMMPPDDDDAAMMMM + MMMMPDDDDDDDAMMM + MMMPPDLLLLDDAMMM + MMMPDPFLLFFDAMMM + MMMPPPLLLLLDAMMM + MMMMPLLAALLAAAMM + MMMPPALLLLJAAAAM + MMMJPDJJJJJJJAAM + MMLDDHDKCKCJJLAM + MMJLHDDCKCKKLJAM + MMLAHDHCCKJAALAA + JLAADDHJKKJAALAA + JJLJDHHJJJKKCLAA + MMLLJJJJACLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJMLLLLKAA +} +# tile 174 (frost giant) +{ + MMMMMKJJJJAAMMMM + MMMMKJJJJJJJAMMM + MMMMJJLLLLJJAMMM + MMMMJEELLEEJAMMM + MMMMJLLLLLLJAMMM + MMMMAKJJJJJAAAMM + MMMMMKJAAJJAAAAM + MMMMKKJJJJAJAAAA + MMMKJKJJJJAJJAAA + MMKJKKJJJJJKJJAA + MMKAAJKJJAJAAJAA + MMJAAJKKAKJAAJAA + MMLCMJJJJJKKCLAA + MMLLMCJJAJLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJMCLLLKAA +} +# tile 175 (storm giant) +{ + MMMMMMJJJJAAMMMM + MMMMJJJJJJJJAMMM + MMMMJJLLLLJJAMMM + MMMMJFFLLFFJAMMM + MMMMJLLLLLLJAMMM + MMMMALLAALLAAHMM + MMMMMALLLLJAHAAM + MMMJJKJJJJJHHAAM + MMLJJCCKCKHHLAAM + MMJLKKKCKHHHHHHM + MMLAAKKCCKJAHHAA + MMLAAJJJKKJHHALA + MMLCMJJJJJKHAAAA + MMLLMCLJACHAAAAA + MMMMMCLJACCJAAAA + MMMLLLLJMLLLLKAA +} +# tile 176 (ettin) +{ + MMMMNNMMONOPMMMM + MMNNOOPNNOOPPMMM + MMNPPMMNPPMMPMMM + MMALPPLALPPLAMMM + MMAPPPPAPPPPAMMM + MMAPAAPAPAAPAAMM + MMAPPPPAPPPPAAAM + MMBIIIIJJJIIIBAA + MBPPPIIIIIIPPPBA + MPPPFPIIIIPFPPPA + MPPAAPIIIIPAAPPA + MPPAAIIIIIIAAPPA + MBPBMIIFFIIABPAA + MMPPMBPAABPAPPAA + MMMMMBPAABPAAAAA + MMMPPPPAMBPPPFAA +} +# tile 177 (titan) +{ + MMMMMAAAAAAAMMMM + MMMMAALLLLAAAMMM + MMMMAMMLLMMAAMMM + MMMMALLLLLLAAMMM + MMMMALLAALLAAAMM + MMMMMALLLLJAAAAM + MMCCJJJJJJJJCCAM + MCLLLCCKCKCLLLCA + MLLLKJKCKCJKLLLA + MLLAAJJCCJJAALLA + MLLAAJJCCJJAALLA + MLLAAJJJKKJAALLA + MCLCMJJJJJKKCLAA + MMLLMCLJACLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJMCLLLKAA +} +# tile 178 (minotaur) +{ + MMMMMMMMMMMMMMMM + MOMMMMMMMMMMOMMM + MOOOJJJJJJOOOMMM + MMOOJJKJJJOOMMMM + MMMJGAKJGAJAMMMM + MMMJJJKJJJJAMMMM + MMMMJJKJJJAAAMMM + MMMMJKKKJAAAAMMM + MMCLJAJAKALCAAMA + MCLLJJJJJALLCAAA + MLLCLAAAALCLLAAM + MLAACLLLLCAALAAM + MLLMJJJJJJJLLAAA + MLLMJJJJJJJLLAAA + MMMMCLCACLCAAAAA + MMLLLLLMLLLLLAAM +} +# tile 179 (jabberwock) +{ + MMMMMMMMMMMMMMMM + MMMDPMMMMMMMMMMM + MMMMDPMADOOMMMMM + MMDAIDADIPADMMMM + MMMDIAPIPAMMMMMM + MMMDBDDDAMMMMMMM + MMIBBDADDAMMAAMM + MDDDDAODDIAAAAMM + MMOAOAMDDAIAAAMM + MMIOAODDAAADDAAM + MMDDDADDDDAIDAMM + MMMAAADDIDIDDDMM + MMMMIDDAIDDDDAMM + MMMMIDAAIDAAMMMM + MMMIDAAMMIDMMMMM + MMMMMMMMMMMMMMMM +} +# tile 180 (vorpal jabberwock) +{ + MMMMMMMMMMMMMMMM + MMMGPMMMMMMMMMMM + MMMMGPMAGOOMMMMM + MMGAFGAGFPAGMMMM + MMMGFAPFPAMMMMMM + MMMGHGGGAMMMMMMM + MMFHHGAGGAMMAAMM + MGGGGAOGGFAAAAMM + MMOAOAMGGAFAAAMM + MMFOAOGGAAAGGAAM + MMGGGAGGGGAFGAMM + MMMAAAGGFGFGGGMM + MMMMFGGAFGGGGAMM + MMMMFGAAFGAAMMMM + MMMFGAAMMFGMMMMM + MMMMMMMMMMMMMMMM +} +# tile 181 (Keystone Kop) +{ + MMMMMMMMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMAAAAMMMMMMMMM + MMMAOAAMMMCMMMMM + MMAAAAAAMMCMMMMM + MMMLLLLMMMCMMMMM + MMMMLLMMMMCMMMMM + MMMAAAAMAAAMMMMM + MMAAAAAAAACMPMMM + MAAMAAAAAMPPPPMM + MMAAAAAAMPPPPPPM + MMMMAAAAPPPAPPMM + MAMAAAAAAPAAAMMM + AAAAAMPAAAAAMMMM + MMAAMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 182 (Kop Sergeant) +{ + MMMMMMMMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMAOOAMMMMMMMMM + MMMAOOAMMMCMMMMM + MMAAAAAAMMCMMMMM + MMMLLLLMMMCMMMMM + MMMMLLMMMMCMMMMM + MMMAAAAMAAAMMMMM + MMAAAAAAAACMPMMM + MAAMAAAAAMCPPPMM + MMAAAAAAMPPPPPPM + MMMMAAAAPPPAPPMM + MAMAAAAAAPAAAMMM + AAAAAMPAAAAAMMMM + MMAAMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 183 (Kop Lieutenant) +{ + MMMMMMMMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMAOOAMMMCMMMMM + MMMAOOAMMMCMMMMM + MMAAAAAAMMCMMMMM + MMMLLLLMMMCMMMMM + MMMMLLMMMMCMMMMM + MMOAAAOMAAAMMMMM + MOAAAAAMAACMPMMM + MAAMAAAAAMCPPPMM + MMAAAAAAMPPPPPPM + MMMMAAAAPPPAPPMM + MAMAAAAAAPAAAMMM + AAAAAMPAAAAAMMMM + MMAAMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 184 (Kop Kaptain) +{ + MMMMMMMMMMMMMMMM + MMMMAAMMMMCMMMMM + MMMAHHAMMMCMMMMM + MMMAHHAMMMCMMMMM + MMAAAAAAMMCMMMMM + MMMLLLLMMMCMMMMM + MMMMLLMMMMCMMMMM + MHHAAAAHHAAMMMMM + MAAAAHAAAACCCMMM + MAAMAHAAAMCPPPMM + MMAAAHAAMPCPPPPM + MMMMAAAAPPPAPPMM + MAMAAAAAAPAAAMMM + AAAAAMPAAAAAMMMM + MMAAMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 185 (lich) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOOOMMMMMMMMMM + MMAOAOOMMMMMMMMM + MMOOOOOMMMMMMMMM + MMOOMOMMMMMMMMMM + MMMMMPPPMMMMMMMM + MMMOOPPPMMMMAAAM + MMOMPPPPPAMMAAAM + MOMMMPPPPMAAAAAM + MMMMOMPPPAAAAMAM + MMMMMMPPPAAAMAMM + MMMMMOPAPMAMMMMM + MMMOOOAMOAMMMMMM + MMMMMMOOOMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 186 (demilich) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOOOMMMMMMMMMM + MMAOAOOMMMMMMMMM + MMOOOOOMMMMMMMMM + MMOOMOMMMMMMMMMM + MMMMMPPPMMMMMMMM + MMMOOPPPMMMMAAAM + MMOMPPPPPAMMAAAM + MOMMMPPPPMAAAAAM + MMMMLMPPPAAAAMAM + MMMMMMLPPAAAMAMM + MMMMMLLALMAMMMMM + MMMLLLAMLAMMMMMM + MMMMMMLLLMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 187 (master lich) +{ + MMMHMMMMMMMMMMMM + MMMHCHMHMMMMMMMM + MMMHHHCHMMMMMMMM + MMAOAOHHMMMMMMMM + MMOOOOOMMMMMMMMM + MMOOMOMMMMMMMMMM + MMMMMPPPMMMMMMMM + MMMPPPPPMMMMAAAM + MMPPPPPPPAMMAAAM + MOMMPPPPPMAAAAAM + MMMMOPPPPAAAAMAM + MMMMMMPPPAAAMAMM + MMMMMOPAPMAMMMMM + MMMOOOAMOAMMMMMM + MMMMMMOOOMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 188 (arch-lich) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOOOMMMMMMMMMM + MMDODOOMMMMMMMMM + MMOOOOOMMMMMMMMM + HMOOMOMMMMMMMMMM + AMMMMPPPMMMMMMMM + AMMOOPPPMMMMAAAM + MAOMPPPPPAMMAAAM + MOMMMPPPPMAAAAAM + MAMMOMPPPAAAAMAM + MMAMMMPPPAAAMAMM + MMAMMOPAPMAMMMMM + MMAOOOAMOAMMMMMM + MMMMMMOOOMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 189 (kobold mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNONONOMMMMMMM + MMMOAOAOMOPMMMMM + MMMMONNMMMAMMMMM + MMMONODNAMAAMMMM + MMOLONNONAAAMAMM + MMNALONANAAAAAMM + MMNAOLOAOAAAAAMM + MMMMNOLAAAAAAMMM + MMMMNANAAAAMMMMM + MMMOOANOAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 190 (gnome mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMGGFOMMMMMMM + MMMMGGFFOOPMMMMM + MMMMGDODFMMMMMMM + MMMMMONOMMMAAAMM + MMMNONOONOAAAAMM + MMMOANLOAOAAAAMM + MMMMNNOLOAAAAMMM + MMMMNOANDAAMMMMM + MMMMNOAOOMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 191 (orc mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOOPMMMMMMMM + MMMMDODPOPMMMMMM + MMMMOOOAMMMMMMMM + MMOOOOOOOAMAAMMM + MMOOOOOOOMAAAMMM + MMOAOOOAACCCMMMM + MMOAOOOCCAAAMMMM + MMMMOCCOAAAAAAMM + MMMCCAOOAAAAMMMM + MMOOOAOOOAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 192 (dwarf mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBMMMMMMMMM + MMMMMBBEOMMMMMMM + MMMMBBEEEOPMMMMM + MMMMBDODEMMMMMMM + MMMMMONOMMMAAAMM + MMMNONOONOAAAAMM + MMMOANLOAOAAAAMM + MMMMNNOLOAAAAMMM + MMMMNOANDAAMMMMM + MMMMNOAOOMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 193 (elf mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMOMMMMMM + MMMMMMMOOOPMMMMM + MMMMMMOOOOPMMMMM + MMMMMMOEOEAPMMMM + MMMMMMOOOOAMMMMM + MMMMMMAOOAMMMMAM + MMMMMMOAAOMMAAAM + MMMMMOOOOOOAAAAM + MMMMOALOOLAOAAAM + MMMMOADOOOAOAAMM + MMMMMMOOAOAAMAMM + MMMMMOOAMOOAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 194 (human mummy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMONNOMMMMMMMMM + MMMNNNNOPMMMMMMM + MMPANANMOPPMMMMM + MMPNNNNMMMMMMMMM + MMONOONNPMMMMMMM + MONLNNOOOMMMMMMM + MNJNOOOOOMMAAAMM + MOJOOOODNMAAAAMM + MNJOLNOAOAAAAAAM + MOCNOMNKNAAAAAMM + MNMOOMOLAAAAAAAM + MMMOOAOOAAAMMMMM + MMNNNMNNNAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 195 (ettin mummy) +{ + MMMMMNNMMONOOMMM + MMMNNOOONNOOOOMM + MMMNOOOONOOOOOOM + MMMOFOFOLOFOFOMO + MMMOOOOOLOOOOOMP + MMMNOOOOLNNOOOAM + MMMONOOOLOOOOAAA + MMOOOONNNNNNNOAA + MONNNNNNOONOOOOA + MONODNNOOOOOOOOA + MNNAAONNONOAAOOA + MNOAAONOONLAAOOA + MOOOMONOONOOOOAA + MMOOMNNOANOLOOAA + MMMMMNOOANOOAAAA + MMMOOOOOMOOOOKAA +} +# tile 196 (giant mummy) +{ + MMMMMMONOOAAMMMM + MMMMONNNOOOOAMMM + MMMMNNOOOOOOOMMM + MMMMNFFOOFFOOPMM + MMMMNONOOOOOAOPM + MMMMAONOOOOAAAPM + MMMMMANOOODAAAAM + MMOOOONOOONNNOAA + MONNNNNOOOOOOOOA + MONODNNLOOOOOOOA + MNNAAONOLNOAAOOA + MNOAAONOONLAAOOA + MOOOMONOONOOOOAA + MMOOMNNOANOLOOAA + MMMMMNOOANOOAAAA + MMMOOOOOMOOOOKAA +} +# tile 197 (red naga hatchling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKMMMMMMMMMMM + MMMKLKMMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMALAAMMMMMMMMM + MMMLALAMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMLLLAAMMMMMMMM + MMMLDLAAMMMMMMMM + MMMIDDAAAAADAMMM + MMMIDDDAAADDAMMM + MMMMIIDDDDDAMMMM + MMMMMMMMMMMMMMMM +} +# tile 198 (black naga hatchling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKMMMMMMMMMMM + MMMKLKMMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMALAAMMMMMMMMM + MMMLALAMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMLALAAMMMMMMMM + MMMAAAPAMMPAAMMM + MMMAAAAPPPAAAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 199 (golden naga hatchling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKMMMMMMMMMMM + MMMKLKMMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMALAAMMMMMMMMM + MMMLALAMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMLLLAAMMMMMMMM + MMMLHLAAMMMMMMMM + MMMNHHAAAAAHAMMM + MMMNHHHAAAHHAMMM + MMMMNNHHHHHAMMMM + MMMMMMMMMMMMMMMM +} +# tile 200 (guardian naga hatchling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKMMMMMMMMMMM + MMMKLKMMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMALAAMMMMMMMMM + MMMLALAMMMMMMMMM + MMMLLLAMMMMMMMMM + MMMLLLAAMMMMMMMM + MMMLFLAAMMMMMMMM + MMMGFFAAAAAFAMMM + MMMGFFFAAAFFAMMM + MMMMGGFFFFFAMMMM + MMMMMMMMMMMMMMMM +} +# tile 201 (red naga) +{ + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMKLLKMMMMMMMMM + MMMLLLLAMMMMMMMM + MMMALLAAMMMMMMMM + MMMLAALAMMMMMMMM + MMMLLLLAMMMMMMMM + MMMLLLDAMMAAMMMM + MMMLDLDAMAAAAMMM + MMMIDDDAAAIIAMMM + MMMIDDDAIDDDIDAM + MMMIDDDIDDDDDDDA + MMMIDDDDDDAAMDDA + MMMMDDDDDAMMDDAM + MMMMMDDAAMMDDAMM + MMMMMMMMMMDAMMMM +} +# tile 202 (black naga) +{ + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMKLLKMMMMMMMMM + MMMLLLLAMMMMMMMM + MMMALLAAMMMMMMMM + MMMLAALAMMMMMMMM + MMMLLLLAMMMMMMMM + MMMLLLAAMMMMMMMM + MMMLALAAMMPPPMMM + MMMAAAAPPPAAPMMM + MMMAAAAPAAAAAAPM + MMMAAAAAAAAAAAAP + MMMAAAAAAAPPMAAP + MMMMAAAAAPMMAAPM + MMMMMAAPPMMAAPMM + MMMMMMMMMMAPMMMM +} +# tile 203 (golden naga) +{ + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMKLLKMMMMMMMMM + MMMLLLLAMMMMMMMM + MMMALLAAMMMMMMMM + MMMLAALAMMMMMMMM + MMMLLLLAMMMMMMMM + MMMLLLHAMMAAMMMM + MMMLHLHAMAAAAMMM + MMMNHHHAAANNAMMM + MMMNHHHANHHHNHAM + MMMNHHHNHHHHHHHA + MMMNHHHHHHAAMHHA + MMMMHHHHHAMMHHAM + MMMMMHHAAMMHHAMM + MMMMMMMMMMHAMMMM +} +# tile 204 (guardian naga) +{ + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMKLLKMMMMMMMMM + MMMLLLLAMMMMMMMM + MMMALLAAMMMMMMMM + MMCLAALCMMMMMMMM + MMLLLLLLMMMMMMMM + MMCLLCLCMMAAMMMM + MMMCLLCAMAAAAMMM + MMMGLFCAAAGGAMMM + MMMGFFFAAFFFGFAM + MMMGFFFGFFFFFFFA + MMMGFFFFFFAAMFFA + MMMMFFFFFAMMFFAM + MMMMMFFAAMMFFAMM + MMMMMMMMMMFAMMMM +} +# tile 205 (ogre) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCLLLCMMMMMMM + MMLCKKLKKCLMMMMM + MMMLAALAALJAMMMM + MMMCLLLLLCJAMMMM + MMMMCAAACJAAAMMM + MMMMLDDDLAAAAMMM + MMCLJLLLKALCAAMA + MCLLAJJJJALLCAAA + MLLCLAAAALCLLAAM + MLAACLLLLCAALAAM + MLCMHHHBHHACLAAA + MLLMJJJJJJALLAAA + MMMMCJJJCLAAAAAA + MMLLLLLMLLLLLAAM +} +# tile 206 (ogre lord) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCLLLCMMMMMMM + MMLCKKLKKCLMMMMM + MMMLAALAALJAMMMM + MMMCLLLLLCJAMMMM + MMMMCLLLCJAAAMMM + MMMMLAAALAAAAMMM + MMJKJLLLKAKJAAMA + MCLKAJJJJAKLCAAA + MLLJKAAAAKJLLAAM + MLAAJKKKKJAALAAM + MLCMHHHBHHACLAAA + MLLMJJJJJJALLAAA + MMMMCJJJCLAAAAAA + MMLLLLLMLLLLLAAM +} +# tile 207 (ogre king) +{ + MMMHMMCMMHMMMMMM + MMMHDCHCDHMMMMMM + MMMHHHHHHHMMMMMM + MMLCKKLKKCLMMMMM + MMMLAALAALJAMMMM + MMMCLLLLLCJAMMMM + MMMMCLLLCJAAAMMM + MMMMLAAALAAAAMMM + MMJKJLLLKAKJAAMA + MCJKAJJJJAKJCAAA + MLJJKAAAAKJJLAAM + MLAAJKKKKJAALAAM + MLCMHHHBHHACLAAA + MLLMJJJJJJALLAAA + MMMMCJJJCLAAAAAA + MMLLLLLMLLLLLAAM +} +# tile 208 (gray ooze) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPBPAMMMMMMM + MMMPBBBPBAMMMMMM + MMBBNNBPPBAAMMMM + MBBNNPPPBBPAAAMM + PBBBPPPBPBBAAAMM + BBBPBPPPPPBPAAAM + BBPBPPPPPPPBAAAM + PBPPBPPPBBPPAAAM + MPBBPPPBAAPPPAAM + MMMPBBBAAAKPPJMM + MMMMMMMMACPPAKMM + MMMMMMMMMKCCCJMM + MMMMMMMMMMMMMMMM +} +# tile 209 (brown pudding) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMJMMM + MMMMJKKJJJMMJJMM + MMMKKKCJJJJJMMMM + MMKKNNJNNJJJMMMM + MMKJANJANJJJAMMM + MMKKJJJJJCJJAAMM + MMJKJJCJJJJJAAMM + MMMJJJJJJJJAAAMM + MMMMJJJJJJAAAMMM + MMMMMAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 210 (black pudding) +{ + MMMMMMMMAMMMMMMM + MMMMMMMMAAMMMMMM + MMMMMAAAMMMMMMMM + MMMMAAAAAMMMMMMM + MMMMCACAAMMMMMMM + MMMMDADAAMMMMMMM + MMMMAAAAAMMMMMMM + MMMMAAAAAMMMMMMM + MMMMAAAAAMMMMMMM + MMMMMAAAAAMMMMMM + MMMMMAAAAAMMMMMM + MMMMMMAAAAAMMMMM + MMMMMMAAAAAAMMMM + MMMMMMMAAAAAAMAM + MMMMMMMMAAAAAAAA + MMMMMMMMMMAAAAMM +} +# tile 211 (green slime) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMGMMM + MMMMMGMMMMMMGMMG + MGMMMMGMMGMNGNMG + MMNGMNGGNGGGGGGG + MMGGNGGNGGGFGGFM + GGNGGGGGGGGGFFMG + MNGGGGGFGFGMMMMM + NGGGGFGGFGMGMMGF + NGGFGGFMMGFMMMMM + MMGGFMMGGFMMGMMM +} +# tile 212 (quantum mechanic) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLMMMMMM + MMMFGGCLCGGFMMMM + MMMGNNGLGNNGLMMM + BMBGANGGGANGLMMM + BMBFGGCLCGGFLMMM + BIBMMCLLLCCLMMMM + BILNMLLAALLAAAAM + BILNNMLLLLJAAAAM + BIBMNNJJJJNAAAMM + BIBMBNNNONNNAAMM + MBMMMNNNONNLAAMM + MMMMMNBEBENAMAMM + MMMMMNBEBENNMMMM + MMMMNAAEBAANNMMM + MMMMMMMMMMMMMMMM +} +# tile 213 (rust monster) +{ + MMMMMMMMMMMMMMMM + MMMMEEEEMMMMMMMM + MMMEEHEHEMMMMMMM + MMMEEEAEEMMMMMMM + MMMEEPAPEMMMMMMM + MMMEPEAEPMMMEMMM + MMMMEEEEMAAEEMMM + MMMMEEEEEAAEEEMM + MMMEEEEEEEAEAEEM + MMEEEEEEAAAAEMMM + MMEEAEEEEAEEEAMM + MMAAEEEEEEEEEAAM + MMMMAEEEEEEEAAAM + MMMMMEEEEEAAMMAM + MMMMMMAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 214 (disenchanter) +{ + MMMMMMMMMMMMMMMM + MMMMPPPPMMMMMMMM + MMMPPDPDPMMMMMMM + MMMPPPAPPMMMMMMM + MMMPPOAOPMMMMMMM + MMMPOPAPOMMMPMMM + MMMMPPPPMAAPPMMM + MMMMPPPPPAAPPPMM + MMMPPPPPPPAPAPPM + MMPPPPPPAAAAPMMM + MMPPAPPPPAPPPAMM + MMAAPPPPPPPPPAAM + MMMMAPPPPPPPAAAM + MMMMMPPPPPAAMMAM + MMMMMMAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 215 (garter snake) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKAMMMMMMMMM + MMMNAOKAMMMMMMMM + MMMKKAKAMMMMMMMM + MMMKKAKAMMMMKAMM + MMMMAPKAPMMKAPPM + MMMMMPKAPPMKAPMM + MMMMMKAAPMMKAPMM + MMMMKAAPMMKAAPMM + MMMMKAAPPKAAPMMM + MMMMKAAKKAAPMMMM + MMMMMKAAAAPMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 216 (snake) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKAMMMMMMMMM + MMMNAOJAMMMMMMMM + MMMKKJAJMMMMMMMM + MMMKKAAJMMMMKKMM + MMMFAAKJMMMKJAAM + MMFAFAKJAAMKJAMM + MMMMMKJAAMMKJAMM + MMMMKJAAMMKJJAMM + MMMMKJAAAKJJAMMM + MMMMKJJJJJJAMMMM + MMMMMKJJJJAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 217 (water moccasin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMMMMMMMM + MMMAAAAAMMMAAMMM + MMMOAOAAAMAAMMMM + MMMAAAMAAMAAMMMM + MMMDAMMAAMAAMMMM + MMDMDMAAAMMAAMMM + MMMMMAAAMMMAAMMM + MMMMMAAAMMMAAMMM + MMMMAAAMMMAAAMMM + MMMMAAAAAAAAMMMM + MMMMAAAAAAAMMMMM + MMMMMAAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 218 (pit viper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMMMMMMMM + MMMAAAAAMMMAAAMM + MMMOAAAAAMAAMAAM + MMMAAMMAAMAAMAAM + MMMDMMMAAMAAMMAM + MMMMMMAAAMMAAMAM + MMMMMMAAMMMAAMMM + MMMMMAAAMMMAAMMM + MMMMMAAMMMAAAMMM + MMMMMAAMMAAAMMMM + MMMMMAAAAAAMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 219 (python) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKKKAMMMMMJJMM + MMMGAGJAMMMJJJMM + MMJKKJAJMMKJJMMM + MMKKKAJJMMKJJMAA + MMKKAAKJAMKKJAAM + MMMMMAKJAAMKJAAM + MMMMJKJAAMMKJJAM + MMMJJJAAMMKJJJAM + MMMJJJAAAKJJJAMM + MMMJJJJJJJJJAAMM + MMMMJJJJJJJAAMMM + MMMMMJJJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 220 (cobra) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMMMMMMMM + MMMAAAAAAMMMMMMM + MMPAMMAAAAMMMMMM + MMAMMAAAAAMMAAMM + MMDMMAAAAAMMMMAM + MMMMMMAAAMMMMMAM + MMMMMMAAMMAAAAMM + MMMMMAAMMAAMMMMM + MMMMAAMMMAAMMMMM + MMMMAAMMMMAAAAMM + MMMMAAAMMMMMMAAM + MMMMMAAAAAAAAAMM + MMMMMMMMMMMMMMMM +} +# tile 221 (troll) +{ + MMMMMMMMMMMMMMMM + MMAAAAAAMMMMMMMM + AAAANANAMMMMMMMM + MAAKKKJJAMMMMMMM + AAAKAAAKAMMMMMMM + AKAKAAAKAAMMMMMM + KJJAKKKAJKAMMMMM + KJAJAAAJJJAMMMMM + KJAJKKJJKJAMMMMM + MKJJAFGFJJAAAAMM + MMKJAMPFJAAAAMMM + MMMAFAGFAAAAAAMM + MMMGFAGPMAAMAAMM + MMKJJAKJAAMAAMMM + MMKJAMMKAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 222 (ice troll) +{ + MMMMMMMMMMMMMMMM + MMOONOOOMMMMMMMM + NONOAOAOOMMMMMMM + MNOKKKJJOMMMMMMM + NOJKAAAKOOMMMMMM + OKJKAAAKAOMMMMMM + KJJAKKKAJKAMMMMM + KJAJAAAJJJAMMMMM + KJAJKKJJKJAMMMMM + MKJJAEBEJJAAAAMM + MMKJAMPEJAAAAMMM + MMMAEABEAAAAAAMM + MMMBEABPMAAMAAMM + MMKJJAKJAAMAAMMM + MMKJAMMKAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 223 (rock troll) +{ + MMMMMMMMMMMMMMMM + MMMAAAAAMMMMMMMM + MAAANANAAAAMMMMM + MAAKKKJJAAMMMMMM + AAAKAAAKAAAAMMMM + AKAKAAAKAAAMMMMM + KJJAKKKAJKAAMMMM + KJAJAAAJJJAMMMMM + KJAPAKJJKJAMMMMM + PKJPPAGFJJAAAAMM + PPPPPAFFJAAAAMMM + MPPPAAGFAAAAAAMM + MMMAFAGFMAAMAAMM + MMKJJAKJAAMAAMMM + MMKJAMMKAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 224 (water troll) +{ + MMMMMMMMMMMMMMMM + MMMAAAAAMMMMMMMM + MMAANANAAMMMMMMM + MAAKKKJJAAMMMMMM + MAAKAAAKAAMMMMMM + MKAKAAAKAAMMMMMM + KJJAKKKAJKAMMMMM + KJAJAAAJJJAMMMMM + KJAJKKJJKJAMMMMM + MKJJAEBEJJAAAAMM + MMKJAMPEJAAAAMMM + MMMAEABEAAAAAAMM + MMMBEABPMAAMAAMM + MMKJJAKJAAMAAMMM + MMKJAMMKAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 225 (Olog-hai) +{ + MMMPPPPPMMMMMMMM + MMPPAAAPPMMMMMMM + MMPANANAPMMMMMMM + MMPAAAAAPMMMMMMM + MPPAAAAAPPMMMMMM + PPPAAAAAPPPMMMMM + AAPPAAAPPAAMMMMM + AAAPPPPPAAAMMMMM + AAAPPPPPAAAMMMMM + MAAAAPPPAAAPPPMM + PONNNNNNAACPPMMM + MMMAPAPPAPPPPPMM + MMMPPAPPMPPMPPMM + MMAAAAAAAPMPPMMM + MMAAAMAAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 226 (umber hulk) +{ + MMMMMMMMMMMMMMMM + MMMAAAAAMMMMMMMM + MMAAAAAAAMMMMMMM + MADADADADAMMMMMM + MAAAAAAAAAMAMMMM + MAAAOAOAAAMAAMMM + MAMAOAOAMAAAMMMM + MAMAAAAAPAAMMMMM + MAAAAAAAPMMMMMMM + MAMAAAAAMPPPPPPM + MMMAAMAAMPPPPPMM + MMMAAMAAPPPPPPPM + MMMAAPAAPPPPMMMM + MMAAAPAAAPPMMMMM + MAAAPMMAAPMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 227 (vampire) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAMMMMMMMM + MMMMAAOAAMMMMMMM + MADDAGAGADAMMMMM + MMADALLOADMMMMMM + MAAAAAAAAAMMMMMM + MMAAAAAAAAMMMMMM + MMMAAAAAAAMMMMMM + MMADAAAAAAPPPPPM + MMADDAAAAAPPPPMM + MMAAAAAAAAPPPMMM + MMMMMAAPAAPPMMMM + MMMMAAPMMAPMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 228 (vampire lord) +{ + MMMMMMMMMMMMMMMM + MMMMMAAAAMMMMMMM + MNMMAAOOAAMMMMMM + MNDDAGAAGADAMMMM + MNADALLLOADMMMMM + AAAAAAAAAAAMMMMM + AAAAAAAAAAAMMMMM + MAAAAAAAAAAMMMMM + MNAAAAAAAAAMMMMM + MNMAAAAAAAAPPPPP + MNADAAAAAAAPPPPP + MNADDAAAAAAPPPPM + MNAAAAAAAAAPPPMM + MNMMMAAAPAAPPMMM + MNMMAAPPMMAPMMMM + MMMMMMMMMMMMMMMM +} +# tile 229 (vampire mage) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAMMMMMMMM + MMMMAAOAAMMMMMMM + MADDAGAGADAMMMMM + MMADALLOADMMMMMM + MAAAAMAMAAMMMMMM + MMAAAACAAAMMMMMM + MMMAAADAAAMMMMMM + MMADAAAAAAPPPPPM + MMADDAAAAAPPPPMM + MMAAAAAAAAPPPMMM + MMMMMAAPAAPPMMMM + MMMMAAPMMAPMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 230 (Vlad the Impaler) +{ + MMMMMMMMMMMMMMMM + MMNMMAAAAMMMMMMM + ADNDAAOOAADDDAMM + MANDAGAAGADDAMMM + MMNDALLLOADAMMMM + MMNAAAAAAAAAAAAA + MHHHDAAAAADDDDDA + HEHEHJAAAADDDAAM + LLLLLJAAAADDAAPP + MLLLJAAAAADDAPPP + MMNJDAAAAADAPPPP + MANDDAAAAAAAPPPP + MANAAAAAAAAPPPPM + MMNMMAAAPAAPPPMM + MMNMAAPPMMAPMMMM + MMMMMMMMMMMMMMMM +} +# tile 231 (barrow wight) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMLLOMMMMMMMMMM + MMDLDLOMMMMMMMMM + MMLLLLOMMMMMMMMM + MMLJLMMPMMMMMMMM + MMOOOMPPMMMAAAAM + MMPOOMPPMAAAAAAM + MLPOOMPPMPAAAAMM + MJJOOMPPMPAAAAMM + MJMOMMPLMPPAAAMM + MJMOMPPPPPPAAAMM + MJMMMPPPPPPPAAMM + MJMMLLPPPPPPAMMM + MJMMMMMLLAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 232 (wraith) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPPPPPMMMMMMMM + MMMPAAPPPMMMMMMM + MMMMPAAPPMMMMMMM + MMMMPAAPPMMMMMMM + MMPPMPPPMPMMMMMM + MOLAPPPMPPMMMMMM + MMAAPPPPAPMMAAAM + MMPPPPPOLPAAAAAM + MMMAMPPAAAAPPAMM + MMMMMPPAPPPPAMMM + MMMMMMPPPPAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 233 (Nazgul) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPPPPPMMMMMMMM + MMMPAAPPPMMMMMMM + MMMMPAAPPMMMMMMM + MMMMPAAPPMMMMMMM + MMPPMPPPMPMMMMMM + MOLAPPPMPPMMMMMM + MOPAPPPPAPMMAAAM + MMPAPPPPAPMMAAAM + MMPAMPPPLPMMAAAM + MMPPMPPOLPAAAAAM + MMMAAPPOAAAPPAMM + MMMMMPPAPPPPAMMM + MMMMMMPPPPAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 234 (xorn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOBMOPMBPMMMMM + MMMBBBBPPPPMMMMM + MMMBMMMMMMMMMMMM + MMMDDBBMBDDAMAMM + MMMDDBBMPDDAAAAM + MMMBMMMMMMMAAAAM + MMMBOBMBPPMAAAAM + MMMBBBMPPPMAAAAM + MMMBMMMMMMMAAAMM + MMMBOBBPPBPAAMMM + MMMBOMBPMPPAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 235 (monkey) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKKAMMMMMM + MMMMMMKLLJAMMMMM + MMMMMMKLLJAMMMMM + MMMMMMMKJAMMMMMM + MMMMMKKKKKJAAMMM + MMMMKJKLLJJJAAMM + MMMMLAKLLJALAAMM + MMMMMMKJJJAAAAMM + MMMMMMJAAJAAAMMM + MMMMMJJAMJJAMMMM + MMMMMMMMMMMMMMMM +} +# tile 236 (ape) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKKKJMMMMMM + MMMMMJJJJJJMMMMM + MMMMKCELECJJMAAM + MMMMKLLLLCAJAAAA + MMMKKCLACCAJJAAA + MMKKKKCCCAJKJJAA + MMKKAKJAAJJAJJAA + MMKAAKJJJJJAAJAA + MMLCMKJJJJJKCLAA + MMLLMCJJAKLJLLAA + MMMMMCLJACLJAAAA + MMMLLLLJACLLLKAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 237 (owlbear) +{ + MMMMMMMMMMMMMMMM + MMMMKMMMMMKMMMMM + MMMMCKMMMKKMMMMM + MMMMCKKKKKKMMMMM + MMMKOOOKOOOKMMMM + MMMKOOOKOOOKAMMA + MMMKOOAJAOOKAAAA + MMCKCJJHJJKAKAAA + MCKKKCKLKKAJJKAA + MKJJJJCKKJJJJJAA + MKJJJPAKJPJJJJAA + MMKJJJAKJJJJJAAA + MMMJJPAKJPJJAAAM + MMMJAAJAJAAJAAMM + MMMPJPJAJPJPAMMM + MMMMMMMMMMMMMMMM +} +# tile 238 (yeti) +{ + MMMMMMMMMMMMMMMM + MMMMBNNNMMMMMMMM + MMMBNANAPMMMMMMM + MMBNNNNNNPMMMMMM + MMNNNADANNMMMMMM + MMNNNNNNPNMMMMMM + MMNMNNBPPNPMMMMM + MMNMNNNNANPMMAAM + MMNNBNNNPNMAAAAM + MMMMNNNNPMKAAAMM + MMMMNNMNPAKKAAMM + MMMMNBMNPAACKAAM + MMMMNNANPAAKKJAM + MMMBNNANNPAACKAM + MMBNNAMMNNAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 239 (carnivorous ape) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKKKJMMMMMM + MMMMMJJJJJJMMMMM + MMMMKCELECJJMAAM + MMMMKLLLLCAJAAAA + MMMKKCAAACAJJAAA + MMKKKCDDDCAKJJAA + MMKKAKCCCAJAJJAA + MMKAAKJAAJJAAJAA + MMLCMAJJJJJKCLAA + MMLLDDAJAKLJLLAA + MMMDDALJACLJAAAA + MMDDALLJACLLLKAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 240 (sasquatch) +{ + MMMMMMMMMMMMMMMM + MMMMCCCCCMMMMMMM + MMMCGAJGAJMMMMMM + MMCKKKKJJJMMMMMM + MMCKKAAAKJJMMMMM + MCKKKAAAKKJMMMMM + MCKJKKKKKKKJMMMM + MCKAJJJJJAKJMMMM + MCKAJKKKJAKJMAAM + MCKJAJKJACKJAAAA + MCKJAJJJACKJAAAM + MMJMAJAKKAJAAAAA + MMMCKJAKKJAAAAAM + MKCKJJACKJKJAAMM + MCJJJKACKJJKAMMM + MMMMMMMMMMMMMMMM +} +# tile 241 (kobold zombie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMNMMMMMMMM + MMMNGFBNMMMMMMMM + MMMGABABMMMMMMMM + MMMMGBFAMMAMMMMM + MMMGBABFAMAAMMMM + MMGFBBBIKAAAMAMM + MMBAFBFFEAAAAAMM + MMBAFBFFAAAAAAMM + MMMMFBFAAAAAAMMM + MMMMBABAAAAMMMMM + MMMBBABBAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 242 (gnome zombie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMGMMMMMMMMM + MMMMMGFFMMMMMMMM + MMMMGGFFFMMMMMMM + MMMMGDFDFMMMMMMM + MMMMMPFPMMMAAAMM + MMMFGFPFEGAAAAMM + MMGAAGFFFAGAAAMM + MMMMAKNKFAAAAMMM + MMMMFGAFFAAMMMMM + MMMMGFAFGMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 243 (orc zombie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMMMMMMM + MMMMNOPAMMMMMMMM + MMMMGPGAMMMMMMMM + MMMMMPFAMMMMMMMM + MMKCCAKKKAMAAMMM + MBBPCKJMBBAAAMMM + BBMAGGFAABBAMMMM + BMMAJJPAAABAMMMM + MMMMBAPPPAAAAAMM + MMMBJAAEPAAAMMMM + MMBPPAAAPPMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 244 (dwarf zombie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBMMMMMMMMM + MMMMMBEEMMMMMMMM + MMMMBBEEEMMMMMMM + MMMMBFFFEMMMMMMM + MMMMMPFPMMMAAAMM + MMMBBPPPEEAAAAMM + MMFBABPEAEAAAAMM + MMFMEBBEFAAAAMMM + MMMMEBAEEAAMMMMM + MMMMBEAEBMAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 245 (elf zombie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMGMMMMMM + MMMMMMMGGFMMMMMM + MMMMMMGGGGAMMMMM + MMMMMMFEFEAMMMMM + MMMMMMFFFFAMMMMM + MMMMMMAFDAMMMMAM + MMMMMMGAAGMMAAAM + MMMMFFGGGFFFAAAM + MMMFAAAGFAAAFAAM + MMMMMAGGGFAAAAMM + MMMMMMGFAFAAMAMM + MMMMMKDAMFKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 246 (human zombie) +{ + MMMMMMAAAMMMMMMM + MMMMMFFGAAMMMMMM + MMMMMAGAFAMMMMMM + MMMMMFFGFAMMMMMM + MMMMFKFMMJJMMMMM + MMMMJJJFJKJMMMMM + MMMFJMKJJAKJMMMM + MMFKMMKFJFFJMMMM + MMGMMMKKJGMMMMMM + MMMMMBPMBPAAAAAM + MMMMMFPAPFAAAAMM + MMMMMBFABFAAAAMM + MMMMMPFABPAAMMMM + MMMMMBFABFAMMMMM + MMMMGGAGGAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 247 (ettin zombie) +{ + MMMMNNMMONOPMMMM + MMNNOOPNNOOPPMMM + MMNFFMMNFFMMPMMM + MMADFFDADFFDAMMM + MMAFFFFAFFFFAMMM + MMAFAAFAFAAFAAMM + MMAFFFFAFFFFAAAM + MMGIIIIJJJIIIGAA + MGFFFIIIIIIFFFGA + MGFFFFIIIIFFFFFA + MFFAAFIIIIFAAFFA + MFFAAIIIIIIAAFFA + MFFFMIIFFIIAGFAA + MMFFMGFAAGFAFFAA + MMMMMGFAAGFAAAAA + MMMFFFFAMGFFFFAA +} +# tile 248 (giant zombie) +{ + MMMMMMJJJJAAMMMM + MMMMJJJJJJJJAMMM + MMMMJJFFFFJJAMMM + MMMMJDDFFDDJAMMM + MMMMJFFFFFFJAMMM + MMMMAFFAAFFAAAMM + MMMMMAFFFFJAAAAM + MMGGFFJJJJFFGGAA + MGFFFGGFFFFFFFCA + MFFFKFFFFFFKFFFA + FFFAAFFFFFFAAFFA + FFAAMJJJKKJAAFFA + FFAMMJJJJJKAGFAA + MMMMMGFAGFFAFFAA + MMMMGFFAGFFAAAAA + MMMGFFAAGFFFAAAA +} +# tile 249 (ghoul) +{ + MMMMMMAAAMMMMMMM + MMMMMOOOAAMMMMMM + MMMMMDODOAMMMMMM + MMMMMOOOOAMMMMMM + MMMMPPOOOPPMMMMM + MMMMPPPPPPPMMMMM + MMMPPMPPPAPPMMMM + MMPPMMPPPOOPMMMM + MMOMMMPPPOMMMMMM + MMMMMPPMPPAAAAAM + MMMMMPPAPPAAAAMM + MMMMMPPAPPAAAAMM + MMMMMPPAPPAAMMMM + MMMMMPPAPPAMMMMM + MMMMOOAOOAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 250 (skeleton) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMOOOMMMMMMMMMM + MMAOAOOMMMMMMMMM + MMOOOOOMMMMMMMMM + MMOOMOMMMMMMMMMM + MMMMMMOMMMMMMMMM + MMMMOOOOOMMMAAAM + MMMOAMOOOAMMAAAM + MMOAMOAAOMAAAAAM + MMMMOAMOOOAAAMAM + MMMMMMOOOAAAMAMM + MMMMMOMAOMAMMMMM + MMMOOOAMOAMMMMMM + MMMMMMOOOMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 251 (straw golem) +{ + MMMMMMLJMMMMMMMM + MMMMMMLJHJMMMMMM + MMMMHJLJHMMMAMMM + MMMMAAAAALMMMMMA + MMMMDAADALMAMAAM + MMMMLJHJLMMAAAAM + MMMMLLHJLHHCHHLM + MMMHHLJLMAJLJLMA + MHHJJLLLLAAAMAAM + MMJLMMLALHAAAAAA + MLLMMCJLHJHAAAAA + MMMMMHJLAHJHAAAM + MMMMMHJAACALHAAA + MMMMCJLAAJHALHAM + MMMMHALAMAHAALMM + MMMMMMMMMMLMMMMM +} +# tile 252 (paper golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMMOAMMMMMMM + MMMMMNNNOAMMMMMM + MMMMMONNNOAMMMMM + MMMMMMONNOAMMMMM + MMMMMMMNOAMMMMMM + MMNNOAMNOAMMMMMM + MMNONOAOAONNOAMM + MMOAOANNOAOOOAMM + MMMMMMNNNOAAAAAM + MMMMMMONOAAAAAAM + MMMMMMMOOAAAAAAM + MMMMMNOAAOAAAAAM + MMMMMNOAMMNOAAMM + MMMMOOAMMMOOAMMM + MMMMMMMMMMMMMMMM +} +# tile 253 (rope golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMOOOMMMMMMM + MMMMMOMMMOMMMMMM + MMMMMOMMMOMMMAAM + MOOMMMOMMOMMAMMA + OMMOMMMLLMMOOMMA + MMMOMMMLOOOMAOAM + MMMMOOOOOAAAAOMM + MMMMMMMOOMMAAMAM + MMMMMMMLOMAAMMMA + MMMMMMOMMOOAAMMA + MMMMOOMMAMMOMAMM + MMMOMMAAMMMOMAMM + MMMOMAMMMMOAAMMM + MMMMOAMMMMMMMMMM +} +# tile 254 (gold golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMHNHMMMMMMM + MMMMMMDNDMMMNMMM + MMMMMMHNHMMMJCMM + MMMMMMHNHMMMNCMM + MMMHHHAAAAHANCMM + MMHNJNHNHNJNACMA + MMHHHHHHHHHHHAMA + MNCACCCCCCCCAMMA + MHHMAAAAAAAAAMAA + MNHMANCMAHNCMAAA + MNJMAHCMAHHCMAAA + MMHMANCMAHNCMAAA + MMMMHJCMAHJCMAAM + HMMMHNCMAHNCMAMH + MMHMMMMMMMMMMMMM +} +# tile 255 (leather golem) +{ + MMMMMMKKKKMMMMMM + MMMMMKHKHJMMMMMM + MMMMKAKKJJAMMMMM + MMMKKAJJJJAJMMMM + MMKKJJAJAAKJJMMM + MKKKJJAAKKKJJJMM + MMKKJJJAKKJJJJMM + MMMKKJJJAMAAAMAM + MMMMAAAAMKJAAAAM + MMMMKJAAAKKAAAAA + MMMKJJAAMKJJAAAM + MMMKKJAMMKKJAAAM + MMKKJJJAKKJJJAAM + MMKKKJJAKKKJJAMM + MMMKJJAMMKJJAMMM + MMMMKAMMMMKAMMMM +} +# tile 256 (wood golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMKCKJMMMMMM + MMMMMMHCHJMMCMMM + MMMMMMKCKJMMCKMM + MMMMMMKCKJMMCKJM + MMMKKKAAAAKACKJM + MMKCCCCCCCCCAKJA + MMKKKKKKKKKKKAJA + MCJAJJJJJJJJAMMA + MCKJAAAAAAAAAMAA + MCKJACKJAKCKJAAA + MCKJACKJAKCKJAAA + MMKJACKJAKCKJAAA + MMMMKCKJAKCKJAAM + MMMMKCKJAKCKJAMM + MMMMMMMMMMMMMMMM +} +# tile 257 (flesh golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMDMMDDCMMMMMMM + MMMMMDLDDLMMMMMM + MMDDMMLLLLCMDMMM + MMMCDLMLLLLADLMM + MMCLDLAMCLLAMLLM + MMLLLAAMLLCAMMLM + MCLLAAMCLLAAMMCA + MLLAMMCLLALLAAAA + MMLAMCLCAALCAAAA + MMMAMMLLCACLCAAA + MMMMLLALLAALLAAA + MMCLLLAAAADLLAMM + MMLLDDDMMDDDDDMM + MMMMMMMMMMMMMMMM +} +# tile 258 (clay golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMLCCCCKMMMMMM + MMMLKKKKKKKMMMMM + MMMCKAKKAKKMMMMM + MMMCKAKKAKKMMMMM + MLCKKKKKKKKLCMMM + CKKJKKKKKKCKKJMM + KKKJKKJJKKJKKJAA + MJJAKKJAKKAJJAAA + MMAKKKJAKKKAAAAA + MMCKKKKKKKKKAAAA + MMCKKKAACKKKAAMM + MMCKKKAMCKKKAAMM + MMMMMMMMMMMMMMMM +} +# tile 259 (stone golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMBBBPPMMMMMMM + MMMBBPPPPPMMMMMM + MMMBHAPHAPMMMMMM + MMMPPPPPPPMMMMMM + MBBPPPPPPMBPAMMM + BPPPPMMMMPPPPAAM + BPPMBPPPPMPPPAAA + MPPMBPMPPMPPPAAA + MMMBPPMPPPAAAAAA + MMBPPPAPPPAAAAAM + MMPPPPAPPPPAAAMM + MMMPPAAMPPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 260 (glass golem) +{ + MMMMMMMMMMMMMMMM + MMMMMBBBBBBAMMMM + MMMMMBPNPPPAMMMM + MMMMMBNPPPNAMMMM + MMMMMNPPPNPAMMMM + MMMMMBPPNPPAMMMM + MMMMMMMBAMMMBAMM + MBNBBABPBAMBPNAM + MNPPNABPNBBANPBA + MMMMMBPNPPPAABAA + MMMMMBNPPPAAAAAA + MMMMMMBPPNAAAAAA + MMMMBNABNABPAAAM + MMMBNPAMMMBNAAAM + MMBNPAMMMMNPAAMM + MMMBAMMMMMBPAMMM +} +# tile 261 (iron golem) +{ + MMMMMMMMMMMMMMMM + MMMMMMPBPMMMMMMM + MMMMMMHBHMMMBMMM + MMMMMMPBPMMMJPMM + MMMMMMPBPMMMBPMM + MMMPPPAAAAPABPMM + MMPBJBBBBBJBAPMA + MMPPPPPPPPPPPAMA + MBMAMMMMMMMMAMMA + MBPMAAAAAAAAAMAA + MBPMABPMAPBPMAAA + MBJMABPMAPBPMAAA + MMPMABPMAPBPMAAA + MMMMPJPMAPJPMAAM + MMMMPBPMAPBPMAMM + MMMMMMMMMMMMMMMM +} +# tile 262 (human) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMELELAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCLAALCMAAAM + MMMMCLLLLLLCAAAM + MMMMLACLLCALAAAM + MMMMLAJJKJALAAAM + MMMMMMJJJKAAAAMM + MMMMMMJJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 263 (wererat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMGJGJAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCLAALCMAAAM + MMMMCLLLLLLCAAAM + MMMMLACLLCALAAAM + MMMMLAJJKJALAAAM + MMMMMMJJJKAAAAMM + MMMMMMJJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 264 (werejackal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMIPIPAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCLAALCMAAAM + MMMMCLLLLLLCAAAM + MMMMLACLLCALAAAM + MMMMLAJJKJALAAAM + MMMMMMJJJKAAAAMM + MMMMMMJJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 265 (werewolf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJAMMMMMMM + MMMMMJJJJAMMMMMM + MMMMMNJNJAMMMMMM + MMMMMLLLLAMMMMMM + MMMMMALLAMMMMMMM + MMMMCLAALCMAAAMM + MMMCLLLLLLCAAAMM + MMMLACLLCALAAAMM + MMMLAJJKJALAAAMM + MMMMMJJJKAAAAMMM + MMMMMJJAJAAMAMMM + MMMMKLAMLKAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 266 (elf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMGMMMMMM + MMMMMMMGGFMMMMMM + MMMMMMGGGGAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMAM + MMMMMMGAAGMMAAAM + MMMMMLGGGFLAAAAM + MMMMLAAGFAALAAAM + MMMMLAGGGFALAAMM + MMMMMMGFAFAAMAMM + MMMMMMGFAFAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 267 (Woodland-elf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMKMMMMMM + MMMMMMMKKJMMMMMM + MMMMMMKKKKAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMAM + MMMMMMKAAKMMAAAM + MMMMMLKKKJLAAAAM + MMMMLAPPJAALAAAM + MMKKLKKKKJALAAMM + MMMMMMPPAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 268 (Green-elf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMGMMMMMM + MMMMMMMGGFMMMMMM + MMMMMMGGGGAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMAM + MMMMMMGAAGMMAAAM + MMMMMLGGGFLAAAAM + MMMMLAAGFAALAAAM + MMMMLAGGGFALAAMM + MMMMMMGFAFAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 269 (Grey-elf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMPMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMPPPPAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMAM + MMMMMMPAAPMMAAAM + MMMMMLPPPMLAAAAM + MMMMLAAPMAALAAAM + MMMMLAPPPMALAAMM + MMMMMMPMAMAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 270 (elf-lord) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMIIMMMMMM + MMMMMMMHGFMMMMMM + MMMMMMHGGGAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMAM + MMMMMMHAAHMMAAAM + MMMMMLHHHFLAAAAM + MMMMLAAIIAALAAAM + MMMMLAHGGFALAAMM + MMMMMMHFAFAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 271 (Elvenking) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMHMMHMMMMMM + MMMMMMHCHHMMMMMM + MMMMMMHHHHAMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMIALLAIMMMAM + MMMMIIIAAIDIAAAM + MMMMMLIIGDLAAAAA + MMMMLADIFDALAAAA + MMMMLAIIGDALAAAM + MMMMMIIFAFDAAAMM + MMMIIKLAILKDIMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 272 (doppelganger) +{ + MMMMMMMMMMMMMMMM + MMMMMMCCCCMMIMMM + MMIMMCDMMMCMMMMM + MMMMCDMHHAMCMMMM + MMMCDMHHHHAMCMIM + MMMCDMLFLFAMDCMM + MIMCDMLLLLAMDCMM + MMMCDMALLAMDCMMM + MMCDMLLAALLMACAM + MCDMLLLLLLLLADCM + MCDMLALLLLALADCM + MCDMLAJJKJALADCM + MMCDMMLJJLAAACAM + MMMCDMLLALAACAMM + MMCDMLLAALLADCMM + MMMMMMMMMMMMMMMM +} +# tile 273 (nurse) +{ + MMMMMMMMMMMMMMMM + MMMMMMMNOMMMMMMM + MMMMMMNDDOMMMMMM + MMMMMMNNOOAMMMMM + MMMMMDBLBLDMMMMM + MMMMMCLLLLDCMMMM + MMMMMDALLACDMMMM + MMMMMCNAAODCAAAM + MMMMMNNNOOLAAAAM + MMMMLANNDOALAAAM + MMMMLANNOOALAAAM + MMMMMMNNOOAAAAMM + MMMMMMNNAOAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 274 (shopkeeper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMAAAAAAMMMMM + MMMMMMJLJLMMMMMM + MMMMMMLLLLMMMMMM + MMMMMMALLAMMMMMM + MMMMMEBAABEAMAAM + MMMMEBBBBBBEAAAM + MMMMBAEBBEABAAAM + MMMMLAGFFFALAAAM + MMMMMMGFAFAAAAMM + MMMMMMGFAFAAMAMM + MMMMMJJAMJJAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 275 (guard) +{ + MMMMMMMMMMMMMMMM + MMMMMBBPPPAAMMMM + MMMMBNPPPPPPAMMM + MMMMBPPBPPPPAMMM + MMMMBAABPAAPAMMM + MMMMBCLBPCLPAMMM + MMMMAKCPPCJAAAMM + MMMMBKJJJJAPAAAA + MMMBPKJAAJAPPAAA + MMBPPKJJJJAPPPAA + MMPPABKJJAPPAPPA + MMPPABPKAPPPAPPA + MMLCMBPPPPPPCLAA + MMLLMBPPABPPLLAA + MMMMMBPPABPPAAAA + MMMMBPPPMBPPPAAA +} +# tile 276 (prisoner) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMNOAMMMMMM + MMMMMMMLLAMMMMMM + MMMMMMMLLAMMMMMM + MMMMMMMNOAMMMMMM + MMMMMMNOONAMMMMM + MMMMMNOOOONAMMMM + MMMMNANOOOANAAMM + MMMMPANOOOAPAAAM + MMMMLANOOOALAAAM + MMMMMMNOOOAAAAMM + MMMMMMNAANAAAMMM + MMMMMMPAAPAAMMMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 277 (Oracle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMNNMMMMMMM + LLLMMMGLLGMMMLLL + MMLMMNLLLLNMMLMM + MMMLMNLAALNMLMMM + MMMMLNLLLLNLMMMM + MMMMMLNLLNLMMAAM + MMMMMNBBEENMAAAA + MMMMMMBBEEAAAAAA + MLLAMMBBBEAAALLM + MLLLLBBBEBELLLLA + MMLLCLBLLELLCLAA + MMMCLLLLLLLCLAAM + MMMMLELLLLELAAMM + MMMMMMMMMMMMMMMM +} +# tile 278 (aligned priest) +{ + MMMMMMMMMMMMMMMM + INIMMMMMMMMMMMMM + IIIMMKCCKMMMMMMM + MJMMKCCCCKMMMMMM + MJMMCAAKCCMMMMMM + MLCMCAAACCMMMMMM + CLLCMCAACJKCMMMM + CJLACCCCJKCCCMMM + MJAACCJJCKCCCKMM + MJKCCCJCCJCCKMAA + MJMMCCJCCLJCAAAA + MJMMCCJCLLCAAAAM + MJMMKCJCCCJAAAAM + MJMACCJCCCJAAAMM + MJACCCJJCCCAAMMM + MMMMMMMMMMMMMMMM +} +# tile 279 (high priest) +{ + MINIMMMMMMMMMMMM + IIIIIMKCCKMMMMMM + MIHIMKCAACKMMMMM + MMHMMCGAGACMMMMM + MMLCMCAAAACMMMMM + MCLLCMCAACJCKMMM + MCHLACCCCJCCCKMM + MMHAACCJJCCCCCKM + MMHCCCCJCCJCCCMA + MMHMMCCJCCLJCAAA + MMHMMCCJCLLCAAAA + MMHMMKCJCCCJAAAA + MMHMMKCJCCCJAAAA + MMHMACCJCCCJAAAM + MMHACCCJJCCCAAMM + MMMMMMMMMMMMMMMM +} +# tile 280 (soldier) +{ + MMMMMJMMMMMMMMMM + MMMMMJAAAMMMMMMM + MMMMMALLLAMMMMMM + MMMMMLLLLCMMMMMM + MMMMMJLLCMMMMMMM + MMMMMJFMMFMMMMMM + MMMMFJFFFFFMMAMM + MMMMFJFFFAFMAMMM + MMMMFLFFFFFAAAMM + MMMMFJFFAAAAAAAM + MMMMMLFAFFAAAAMM + MMMMMFFAFMAAAAMM + MMMMMMFAFMAAMMMM + MMMMMFFAFFAMMMMM + MMMMMJJMJJMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 281 (sergeant) +{ + MMMMMJMMMMMMMMMM + MMMMMJFFFMMMMMMM + MMMMFFFFFFMMMMMM + MMMMMLLLLCMMMMMM + MMMMMJLLCMMMMMMM + MMMMMJFMMGMMMMMM + MMMMFJFFFFFMMAMM + MMMMFJFFFAFMAMMM + MMMMFLFFFFFAAAMM + MMMMFJFFAAAAAAAM + MMMMMLFAFFAAAAMM + MMMMMFFAFMAAAAMM + MMMMMMFAFMAAMMMM + MMMMMFFAFFAMMMMM + MMMMMAAMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 282 (lieutenant) +{ + MMMMMMMMMMMMMMMM + MMMMMMMFFFMMMMMM + MMMMMFFFFFFMMMMM + MMMMMMLLLLMMMMMM + MMMMMMMLLCAMMMMM + MMMMMGFMFGAMMMMM + MMMMFFFFFFFFMMMM + MMMMFAFFFFAFAAAM + MMMMFAFFFFAFAAAA + MMMMFAFFFFAFAAAM + MMMMLFFAFFJLJAMM + MMMMMFFAFFJJJAMM + MMMMMMFAFMAAMMMM + MMMMMFFAFFAMMMMM + MMMMMAAMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 283 (captain) +{ + MMMMMMMMMMMMMMMM + MMMMMMFHHFMMMMMM + MMMMMFFFFFFMMMMM + MMMMMMLLLLMMMMMM + MMMMMMMLLCAMMMMM + MMMMMHFMFHFMMMMM + MMMMFFFFFFFFAAMM + MMMMFAFFIFAFAAAA + MMMMFAFFFFAFAAAM + MMMMFAFFFFAFAAAM + MMMMLFFAFFJLJAMM + MMMMMFFAFFJJJAMM + MMMMMMFAFMJJJAMM + MMMMMFFAFFJJJAMM + MMMMMAAMAAAAAMMM + MMMMMMMMMMMMMMMM +} +# tile 284 (watchman) +{ + MMMMMMMMMMMMMMMM + MMMMMMPPPMMMMMMM + MMMMPPPPPPMMMMMM + MMMMMLLLLCMMMMMM + MMMMMMLLCMMMMMMM + MMMMMPPMMPMMMMMM + MMMMPPPPPPPMMMMM + MMMMPAPPHAPPAMMM + MMMMPAPPPANNAAAM + MMMMPJPPAPNNAAAM + MMMMJLPAPPAAAAMM + MMMMMJPAPMAAAAMM + MMMMMMPAPMAAMMMM + MMMMMPPAPPAMMMMM + MMMMJJJMJJJMMMMM + MMMMMMMMMMMMMMMM +} +# tile 285 (watch captain) +{ + MMMMMMPPPMMMMMMM + MMMMMPHHHPMMMMMM + MMMMPPPPPPPMMMMM + MMMMMLLLLCMMMMMM + MMMMMMLLCMMMMMMM + MMMMMHPMMHMMMMMM + MMMMPPPPPPPMMMMM + MMMMPAPPHAPPAMMM + MMMMPAPPPANNAAAM + MMMMPJPPAPNNAAAM + MMMMJLPAPPAAAAMM + MMMMMJPAPMAAAAMM + MMMMMJPAPMAAMMMM + MMMMMPPAPPAMMMMM + MMMMJJJMJJJMMMMM + MMMMMMMMMMMMMMMM +} +# tile 286 (Medusa) +{ + MMMMMMMMMMMMMMMM + MMGAMMMGAMMMMMMM + MMMFAMFAMMGAMMMM + MMMMFJFFFFMMMMMM + MMFAFLLFAMMMMAMM + MGAFLLLLAMMAMMAM + MMMMJLLKAMAMAMAM + MMMKBLLBKAAAAAMM + MMKIIBBIIIAAAMAM + MMIIIKKILLIAAAMM + MMKIILLIALIAAMMM + MMMKIALKAAIAAMMM + MMMIKAAKIIAAAMMM + MMMIIKKIIIAAMMMM + MMIIKIKIKIAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 287 (Wizard of Yendor) +{ + MEEEMMMMMMMEEEMM + EFFAEMMEMMEAFFEM + EAAAEMEEEMEAAAEM + EAAAEEEAEEEAAAEM + EEAAEEDADEEAAEEM + MEEEEAAAAAEEEEMM + MMEEEEAAAEEEEMMM + MMEEEEEEEEEEMMMM + MMMEEEEEEEEMMMMM + MMMEEEEEEEEMMMMA + MMMMEEEEEEMMMAAA + MMMMEEEEEEAAAAAA + MMMEEEEEEEEAAAAA + MMEEEEEEEEEAAAAA + MEEEEEEEEEEEAAAM + EEEEEEEEEEEEEEAM +} +# tile 288 (Croesus) +{ + MMMMHMMHMMHMMMMM + MMMMHCHEHCHMMMMM + MMMMHHHHHHHMMMMM + MMMMALLLLLAMMMMM + MMMMLLALALLMMMMM + MMMMMLLLLLMMMMMM + MMMMHLLDLLHMMMMM + MMMHIALLLAIHMAMA + MMMHIHAAAHIHAAAA + MMIIIEHHHIIIIAAA + MMIIIIEHIIIIIAAM + MMILLIHHHILLIAAA + MMMLIIKHIIILAAAA + MMGIIIKJIIIIGAAM + MGIIIKJJKKIIIGGM + MMMMMMMMMMMMMMMM +} +# tile 289 (Charon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMJMMMMMMMM + MMMMMMJJJMMMMMMM + MMMMJJJAJJJMMMMM + MMMMJJDADJJMMMMM + MMMJJAAAAAJJMMMM + MMMJJJAAAJJJMMMM + MMJJJJJJJJJJMMMM + MJJJJJJJJJJJJMMM + JJJJJJJJJJJJJJMA + MOOMJJJJJJMOOAAA + MMMMJJJJJJAAAAAA + MMMJJJJJJJJAAAAA + MMJJJJJJJJJAAAAA + MJJJJJJJJJJJAAAM + JJJJJJJJJJJJJJAM +} +# tile 290 (ghost) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNNNMMMMMMMMMM + MMNANANNMMMMMMMM + MNNNNNNNNNMMMMMM + MNNPAAPNNNNNMMMM + MNNAAAANNNOONOMM + MNNAAAANONNNPNNO + MNNPAAPNONNOOOPM + MNNNNNNONOPNPPOM + MNNONNOPNNOPOOPM + MNOPNNOOOPPOOPMM + MOOOPOPPOPPPMPPM + MPPMPPOPPPMMMPMM + MOMMMPMMMMPMMMMM + MMMMMMMMPMMMMMMM +} +# tile 291 (shade) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMAAAAAAAM + MMMMAAAAAAAAMMMM + MMAAAAAAAAAAAAMM + AAAAAAAAAAAAAAAM + MMAAAAAAAAAAAAAA + MAAAAAAAAAAAAAMM + AAAAMAAAAJAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 292 (water demon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMEEMMMMMEEMMMMM + MEMEEMMMEEMEMMMM + MMMMEEEEEMMMMMMM + MMMMEBEBEMMAMMMM + MMMEEEEEEEAAAAMM + MMEEEMMMEEEAAMAM + MMEEEEEEEEEAAAMM + MMEEAEEEAEEAAAMM + MMMAAEEEAAAAAMMM + MMMMEEAEEAAMMMMM + MMMMEEAEEAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 293 (horned devil) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMOMMMMMMMOMMMMM + MMOOMMMMMOOMMMMM + MMMLOCDCOLMMMMMM + MMMCDDDDDCMMMMMM + MMMDAADAADAMMDMM + MMMDDDDDDDAMDMMM + MMCCDDFDDCCADMAM + MMCDKDDDKCDADAMM + MMCDAKKKACDAAAAM + MMDDADDDADDAAAAM + MMMMCDDDKAAAAAMM + MMMCDDADDKAAMMMM + MMCDDAAMDDKMMMMM + MMMMMMMMMMMMMMMM +} +# tile 294 (succubus) +{ + DDMOHHDMMMMMMMMM + DDOHHDGDMMMMMMMM + DDOHDDDDDMMMMMMM + DDHHDDDAMMMMMMMM + DHHHDJADDDDMMMMM + MHHJJDDDJDDDMMMM + OHDDCDCDDJDDMMMM + HHHCDDCDLJMDDMMM + HHHCDLJJJAADDAMM + HHHDJJDDDAADAAMM + HHHHDDAADAAAAMMM + HHHHMDKJDDAAMMMM + HMHMMDDAADDAAMMM + MMHMMDDAAMDAAMMM + MMMMDDAAMMDDAAMM + MMMDDJAMMMDDDAMM +} +# tile 295 (incubus) +{ + DDMOHHDMMMMMMMMM + DDOHHDGDMMMMMMMM + DDOHDDDDDMMMMMMM + DDHHDDDAMMMMMMMM + DDDHDJADDDDMMMMM + DDDJDDDDDDDDMMMM + MDDDDDCDDMDDDMMM + MDDCDDDKKMMDDMMM + MMDDKKDDDAADDAMM + MMMDDDDDDAAAAAMM + MMMMDDDDDDDDDAMM + MMMMDDJDJJAAAAAA + MMMMJDJJADKAAMMM + MMMMDDKAADDKAMMM + MMMDDKAAMMDDAAMM + MMDDKAAMMMDDDAMM +} +# tile 296 (erinys) +{ + MMGAMMMGAMMMMMMM + MMMFAMFAMMGAMMMM + MMMMFJFFFFMMMMMM + MMFAFLLFAMMMMMMM + MGAFDLDLAMMAMAMM + MMMMLLLEAMAMAMAM + MMMEBLLBEAAAAMAM + MMEBBBBBBBAAAAMM + MMBBBEBBLLBAAMAM + MMEBBLLBALBAAAMM + MMMEBALBAABAAMMM + MMMBEAABBBAAAMMM + MMMBBBBBBBAAAMMM + MMMBBBBBBBAAMMMM + MMBBEBEBEBAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 297 (barbed devil) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMOMMMMMMMOMMMMM + MMOOMMMMMOOMMMMM + MOMLOCDCOLMOMMMM + MMMCDDDDDCMMMMDD + MMMDAADAADAMMDMD + MMMDDDDDDDAMDMMM + MMCCDDFDDCCADMAM + MMCDKDDDKCDADAMM + MCMDAKKKACDKAAAM + MMDDADDDADDAAAAM + MMMMCDDDKAAAAAMM + MKMCDDADDKAAKMMM + MCCDDAAMDDKKMMMM + MMMMMMMMMMMMMMMM +} +# tile 298 (marilith) +{ + MDMMHHHMMMMMDMMM + DDMHHHHHAMMMDDMM + MDCHDDDHHAADMMAM + MMHDBDBDHDDAAAAM + MCHKDDDKHAAADDMM + CDDDKKKDHDDDDFFM + DMMCDDCDKAAFFFAA + DMKDDKDDDDDDFAAM + DMDKDDKDKAFDFAAM + MMDMGDDFAAFDFFAA + MDMGGFFFAAAFFFFA + MMMGFFFFFAAAFFFA + MMFGFFFFFFAFFFFA + MMFGFFFFFFFFFFAM + MMFGFFFFFFFFFAMM + MMMMGFFFFFFAAMMM +} +# tile 299 (vrock) +{ + MMMMMMMMMMMMMMMM + MMMMMMOPPMOMMMMM + MMMMMMPPPPMMMMMM + MMMMMCPPDPMMMMMM + MMMMCCCPPPMMMMMM + MMMMCCPPPPMMMMMM + MMMMCMMPPAMMMMMM + MMMMMDDAADDMAAAM + MMMMDDDDDDDDAAAM + MMMMDADDDDADAAAM + MMMMDADDDDADAAAM + MMMMDADDDDADAAAM + MMMMMMDAADAAAAMM + MMMMMMDAADAAMAMM + MMMMMDDAMDDAMMMM + MMMMMMMMMMMMMMMM +} +# tile 300 (hezrou) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMGGGFFMMMMMMM + MMNGFFNNFFFMMMMM + MDFFFDDNFFMFMMMM + MGFFFDNFFMFFFAAM + MAFAFFFFFFFFFFAA + MGFFFFMFFFMFGFAA + MGAAAFFMMLFGFFAA + MMFFFFMFFLFGFFFA + MMLLAMFLLLJJGMFA + MLLAFFLLFJJGFFFA + MMLAFLLLAAGFMFAA + MMMMMLMLAGFFFFAA + MMMMMMMMMMMFFFAM + MMMMMMMMMMMMMMMM +} +# tile 301 (bone devil) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMOMMMMMMMOMMMMM + MMOOMMMMMOOMMOMM + MMMLNLOLOLMMMMOM + MMMLOOOOOLMMMMOM + MMMNAAOAAOAMMOMM + MMMNOOOOOOAMOMMM + MMNOOOFOOOOAOMAM + MMOOKNOOKOOALAMM + MMOOANOOAOOAKAAM + MMLLANOOALLAAAAM + MMMMNOOOLAAAAAMM + MMMNOOAOOLAAMMMM + MMNOOAAMOOLMMMMM + MMMMMMMMMMMMMMMM +} +# tile 302 (ice devil) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMNMMMMMMMNMMMMM + MMNNMMMMMNNMMMMM + MMMPBPNPNPMMBNBM + MMMPNNNNNPMMNMNM + MMMBAANAANAMMMNM + MMMBNNNNNNAMBNBM + MMBNNNFNNNNANMAM + MMNNKBNNKNNABAMM + MMNNABNNANNAMAAM + MMPPABNNAPPAAAAM + MMMMBNNNPAAAAAMM + MMMBNNANNPAAMMMM + MMBNNAAMNNPMMMMM + MMMMMMMMMMMMMMMM +} +# tile 303 (nalfeshnee) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBBMMMBBMMM + MMKKKKKBBMBBMMMM + MKADKADKKKBMMMMM + MKKKKKKKDKKMMMMM + MOKDKOKKDDKDMMMM + MOKDKOKKKDKDDMMM + MKAAAKKDKAKKDMMM + MMKKKDDLAKKKDMAM + MMMKKMKKKKKDDAMM + MMMMLMMKDAKDAAMM + MMMMMMLLAAKDAMMM + MMMMMMMMMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 304 (pit fiend) +{ + MMMMMMMMMMMMMMMM + MKMOMMMMMMMOMKMM + MKMOOMMMMMOOMKJM + KJJMLOCDCOLMKJJM + KJJJKDDDDDKKJJJM + KJJCKDNDNDKCJJJA + JJCCKDDDDDJCCJJA + JJCCCKDIDJDCCJJA + JACCDDKJJDDCDAJA + JACCKDDDDDKCDAJA + MMMCDKDDDKCDDAAA + MMMMDKDDDKCDAAAA + MMMMMCDDDKAAAAMM + MMMMCDDADDKAMMMM + MMMCDDAAMDDKMMMM + MMMMMMMMMMMMMMMM +} +# tile 305 (balrog) +{ + MMMMMMMMMMMMMMMM + MKMMOMMMMMOMMKMM + MKJMOMMMMMOMKJMM + KJJLOOCDCOOLJJJM + JJJDDDDDDDDDDKJM + JCCDDDNDNDDDCCJA + CCDKDDDDDDDJCCCA + CDDKKKDIDJJDCCDA + CDDKDDKJJDDDCDDA + CCDKDDDDDDDKCDDA + JCCDKKDDDKKCDDDA + JJCDKKDDDKKCDDJA + JJMMCCDDDKKAAJJA + JMMCDDDADDDKAAJA + MMCDDDAAMDDDKMAA + MMMMMMMMMMMMMMMM +} +# tile 306 (Juiblex) +{ + MMMMMMMMMMMMMMMM + DDMMMMMMMMMDDMMM + NDCMKKKKJMCNDMMM + MCCKCCCKKCCAAMMM + MMKCCCCKCCJAAMMM + MMKCCCCCKMJJAAMM + MMKCCFCCKMMJAAMM + MMFKCFCCKKMJAAAM + MFKKFCCKFKMMJAAM + MFKCFCCFKFKMJAAM + MFCFCCKFCFKMJAAM + FKCFCFCFCKKMJAAM + CKFCCFCCKKFKJJAM + CCKKCFCKFFKKKKAM + MCCCCCCCCCCCKAMM + MMMMMMMMMMMMMMMM +} +# tile 307 (Yeenoghu) +{ + MMMMBMHHPMMMMMMM + MMMMBPPPPMMMMMMM + MMMBPLCPPHMMMMMM + MKBPPCCPPHMMMMMM + MPPPPPPPMHMMMMMM + MPPMMMPMPHMMMMMM + MMMMBPPPPPAAAMMM + MMBPPPPPPPPAAAAM + MBPMPPPPPAPPAAAA + MBMMMBPPMAAPAAAA + MBMMMBPPMAAPAAAM + MMMMMBPPAAAAAMAM + MMMMBPMPPAAMMMAM + MMMBPMAAPPAMMAMM + MMMBPAAMPPAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 308 (Orcus) +{ + MMMMMMMMMMMMMMMM + MKMMOMMMMMOMMKMM + KJJOMMBBBMMOMKJM + KJJLOBPPPPOLKJJM + JKJJMPGPGPMJJKJA + JJKJKPPPPPJJKJJJ + JBPPBMBPPABBPPJJ + PJJPPMBPPAPPJJPA + PJBPPPMAAPPPPJPA + MJBPMPPPPMPMPJAA + MJBPPPPMPPPPPJAA + MMPPPMPPPMPPPAAA + MMMPMPPPPPPPMPMA + MMMBPPPAPPPPAAPA + MMMOOPPAOOPPAGAM + MMMMMMMMMMMMMMMM +} +# tile 309 (Geryon) +{ + MKMMMMMMMMMMMKMM + MKMMMMJJJMMMMKJM + KJJMMJJJJJMMKJJM + KJJJKLLLLLKKJJJM + KJJJKLBLBLKJJJJA + JJJJKLLLLLJJJJJA + JJALLKLLLJLLAJJA + JAMLLLKJJLLLAAJA + MMMLJLLLLLKLAAAA + MMMLCKLLLKCLAFGF + MMMLLGLLFALLFFFA + MMMMMGFFFAAAFFAA + MMMMGGGGFFAAFFAA + MMMMGFFFFFAAFGFA + MMMMFGGGFFFFFFFA + MMMMMFFFFFFFFFAM +} +# tile 310 (Dispater) +{ + MMMMMMMMMMMMMMMM + MMMMMMOJJOMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMBLBLAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCKAAKKMAAAM + MMMMCKKKKKKKAAAM + MMMMKACKKJAKAAAM + MMMMKACKKJAKAAJA + MMMMKACKKJAKAAJA + MMMMLACKKJJLAJAM + MMMMMMCKAJAJJAMM + MMMMMMCKAPAAAAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 311 (Baalzebub) +{ + MMMMMMFMMMFMMMMM + MMMMMMMFMFMMMMMM + MMMMMMBFFFBMMMMM + MMMMMBPPFBPPMMMM + MMMMMPPPFPPPMMMM + MMMMMMPPFPPMMMMM + MMMMMCAFFFAKMMMM + MMMMCKKAFAKKKAAM + MMMMCAKJFAKAKAAM + MMMMFACJDAJAFAAM + MMMMFACKJJJAFAAM + MMMMFACKKKJAFAAM + MMMMMMCKKKJAAAMM + MMMMMMCKAKJAMAMM + MMMMMFFAMMFFMMMM + MMMMMMMMMMMMMMMM +} +# tile 312 (Asmodeus) +{ + MMMMMMMMMMMMMMMM + MMMMMMOJJOMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMBLBLAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCKAAKKMAAAM + MMMMCKKKKKKKAAAM + MMMKKCKKKKJKKAAM + MMMKKAKKKJAKKAJA + MMMKAMCKKJAAKAJA + MMMLAMCKKJJALJAM + MMMMMMCKAJAJJAMM + MMMMMMCKAPAAAAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 313 (Demogorgon) +{ + MMMKKKMMKKKMMMMM + MMKBKBKMBKBKMMMM + MMKDKKKMKKDKMMMM + MMDKKFAMGKKDMMMM + MMMMGFAAGFAAAMMM + MMMGFFFJFFFAMAAA + MMGFAGFFFAFFAAAA + MGJFAGJFJAFFFAAM + MGFAAGFFFAAFJAMM + MGJAMGFJFAAFFAAM + MGFAMGFFFAMFJAAM + MGJAMGJFJAMFFAAM + MGFAGFAAFFAFFAMM + MMGAGJAAJFAFAAMM + MMGAGFAFFFAFAMMM + MMMMMMMMMMMMMMMM +} +# tile 314 (Death) +{ + MBBBBMMMMJJJMMMM + MBPPPPMJJJJMMMMM + MCMMMMJJJJJMMMMM + MCMMMMJAAAJMMMMM + MCMMMJADADAJMAAA + OOJMMJAAAAAJAAAM + OOOJJJAAAAAJJJAM + OOJJJJAAAAJOOJJA + MCJJJJAAAJOOOOJA + MCMJJAAAAJAOAAJA + MCMMJAAAAJAOAAJA + MCMMJAAAAAJOAJAA + MCMMJAAAAAJJJAAA + MCMJAAAAAAAJJAAM + MCJJAAAAAAAAJJAM + ACJAAAAAAAAAAAJM +} +# tile 315 (Pestilence) +{ + FMMMMMMMMJJJMMMM + MMFMMMMJJJJMMMMM + BMMMFMJJJJJMMMMM + MMMBMMJAAAJMMMMM + MFMMMJADADAJMMMM + MMMFMJAAAAAJMAAM + MBMMJFAAAAFJAAMM + MMMFJJAFABJJAAMM + MMFMJFFBAJJJAAAM + MMMMFAFFJJJJAAAM + MMMMJABAJJJJAAAM + MMMFJFFJJJJJJAAM + MMMJJBFJJJJJJAAM + MMMJAABFBJJJJAAM + MMJJFBFAFFAJJJAM + MJJAAFAFAAAAAAJM +} +# tile 316 (Famine) +{ + MMMMMMMMMJJJMMMM + MMMMMMMJJJMMMMMM + KMMMMMJJJJJMMMMM + KMMMMMJAAAJMMMMM + KMMMMJADADAJMMMM + KMMMMJAAAAAJMMMA + KMMMMMJAAAJJMMAA + OOJJJJJJAAJAJMAA + KMMMJJJAAJJAJAAA + KMMMMMJAJJJOJAAM + KMMMMMJAOOOAAAMM + KMMMMMJAJJAAAMMM + KMMMMMJAJJAAMMMM + KMMMMMJAAJAAMMMM + KMMMJJAAAJJAAMMM + KMMJJAAAAAJJJAMM +} +# tile 317 (mail daemon) +{ + MMMOPMBEEEMPOMMM + MMMOOEBEEEEOODMM + MMDLOBEEEEOOLDDM + MDDDLDDDDDDLDDDM + MCCDDDNDDNDDDCCM + CCDKDDDDDDDDJCCC + CDDKKDDIIDDJJCCD + CDDKMKDAADJJECDD + CCDKEEKKKJEEKCDD + MCCDKMEEEEMMCDDD + MCCDAEMEENNNCDDM + MDDDAEEEENDNDDDM + MMMMBBEEENNNNNMM + MMMBEEEAANNNNNAM + MMCDDDAAAMDDDKMM + MMMMMMMMMMMMMMMM +} +# tile 318 (djinni) +{ + MLLMMNNNMMLLMMMM + MMLMMNGNAMLMMMMM + MMLAALLLAALAMMMM + MMLAAKKKAALAMMMM + MMMLCKKKCLAMMMMM + MMMMLCKCLAMMMMMM + MMMMLICLIAMMAMMM + MMMMIIIIEAMAAMMA + MMMMEIEEIAMAAAAA + MMMMIEFEAAAAAAAA + MMMMMDEAIAAAAAMM + MMMMMIGIFAAAAMMM + MMMMMMMFEDAAAMMM + MMMMMMMIMGEAMAMM + MMMMMMMMMIFEDMMM + MMMMMMMMMMMMMMMM +} +# tile 319 (sandestin) +{ + MMMMMCCCCCMMIMMM + MIMMCDMMMMCMMMMM + MMMCDMDDDMMCMMIM + MMCDMDDDDDMMCMMM + MMCDMDNDNDAMDCMM + IMCDMDDDDDAMDCMM + MMCDMADDDAMDCMMA + MCDMDDAAADDMDCAA + CDMDDDDDDDDDADCA + CDMDADDDDDADADCA + CDMDADDDDDADADCA + CDMDADDDDDADADCM + MCDMMDDJDDAADCAM + MMCDMDDADDADCAMM + MCDMDDAADDDADCMM + MMMMMMMMMMMMMMMM +} +# tile 320 (jellyfish) +{ + MMMMMMMMMMMMMMMM + MMMMMPBPAMMMMMMM + MMMPBBBPBAMMMMMM + MMBBNNBPPBAAMMMM + MBBNNPPPBBPAAMMM + PBBBPPPBPBBAAAMM + BBBPBPPPPPBPAAAM + BBPBPPPPPPPBAAAM + PBPPBPPPPEPEEEMM + MPBBPPPEPEPPEEMM + MPEPBBEEPEEPEEMM + MPEEEPEEPEEPEMMM + MMPEEPEMMPEMPMMM + MMPMEPMEEPMMPMMM + MMPEEMPMEMMEMMMM + MPMMEMPMMEMMMMMM +} +# tile 321 (piranha) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOPPMMMMMMMMM + MMMMMOPPMMMMMMMM + MMOMMPPAPMMMMMEM + MMPOPPPPPAMMMEEE + MMPPPPPMPPAMEMMM + MMMPPMMPPPAAAEEM + MMEMPPPPPPPPPEMM + MMEMMMPPPPPAAMEM + MMMMMEMMPPAEMMMM + MMMMMEMMPMMMMMMM + MMMMEMMEMMMEMMMM +} +# tile 322 (shark) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMP + MMMMMMMMPMMMMMPP + MMMMEMMPPMMMMPPM + MMMEMMMPPAMMPPPM + MMEEEEPPPAMPPPPP + MEMEEPPPMPPPPPPE + MMMEPMPMPPPPPEEE + MMPPPPPPPPPPEEEM + MAPPPPPPPPEEEEME + PPPPPPPMMEEMMMMM + NDPPAPEPPPMEMMEE + PDNPPEEMMEEMMMMM + MPPPEMMEEEMMEMMM +} +# tile 323 (giant eel) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMMMMMMMM + MMMAAOAAMMMAAAMM + MMAAAAAAAMAAMAAM + MMAAAAMAAMAAMAAM + MMMAAMMAAMAAMMAM + MMMMMMAAAMMAAMAM + MMMMMMAAMMMAAMMM + MMMMMAAAMEMAAMEM + MMMEMAAEEMAAAEMM + MMMEMAAEEAAAEEMM + MMEMMAAAAAAEMEMM + MMMEEMAAAAEMEMMM + MMEMMMEEMEMMMEMM +} +# tile 324 (electric eel) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMMMMMMMM + MMMAAOAAMMMMMMMM + MMAAAAAAAMMMMMMM + MMAAAAMAAMMMMMMM + MMMAAMMAAMMMDDMM + MMMMMMAAAMMDDMMM + MMMMMMAAMMMDDMMM + MMMMMAAAMEMDDMEM + MMMEMAAEEMDDMEMM + MMMEMAAEEDDMEEMM + MMEMMAAADDDEMEMM + MMMEEMAAADEMEMMM + MMEMMMEEMEMMMEMM +} +# tile 325 (kraken) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMFFMMFFMMMMMMMM + MMDDFDDFMMMMMMMM + MMFFFFFMMMMMGGMM + MMNCNFFMMMMMMGFA + MMCCMFFMMMMMEGFA + MMMMGFAAMGFAEGFE + MMMGFFAGFFFFAGFE + MMMGFAAFFAGFAEEE + MMGFFAGFFAGFEEEM + EEGFFAGFFAEEEEME + MEGFFAGFFEEMMMMM + EEGFFEEEEEMEMMEE + EEEEEEEMMEEMMMMM + MMEEEMMEEEMMEMMM +} +# tile 326 (newt) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJKKJMMMMMM + MMMMMCLCCLLCMMMM + MMMMLAAAACCLCAMM + MMMMMMMLCCLLLCAM + MMMMLCCCLLLAALAM + MMMMCALLLLAAAAMM + MMMLLLLCLAAAMMMM + MMMLLAAALLAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 327 (gecko) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMLLPMM + MMMMMMMMMMLLOOAM + MMMMMMPOMLLOOOAM + MMMMMMOOALOOOAMM + MMMMMMMLLOOOAMMM + MMMPOALOOOAAMMMM + MMMOOLOOOOOOAMMM + MMMMALOOOAOPAMMM + MMMDOOOOAMAAMMMM + MMMOOOAOOAMMMMMM + MMMOODAOPAMMMMMM + MMFMAAMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 328 (iguana) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMGPGPGGPFMMMM + MMGPAAAAFFGPFMMM + MGPAAMPFFGPPPAAM + MMMMMMFGGPPFPPAA + MPFGGGGPPPFAAPPA + MFGPAPPPPFAAAAAM + MGPPPPFPFAAAMAMM + MPPFFAFPPAAMMMMM + DMAAAAAAPPAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 329 (baby crocodile) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMFFOFOFAMMMM + MMMMFOGFGGOFFMMM + MMMMGAAAAFOGFAMM + MMMFAAMGFOGGGFAM + MMMGFOOOGGGFAGAM + MMMFGAGGGGFAAAMM + MMFGGGGFGFAAMMMM + MMGGDFAFGGAMMMMM + MMMDMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 330 (lizard) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFFFFGFJMMMMM + MMFFAAAJJGFJMMMM + MMMMMMJGFFJFAAMM + MMMJGGGFFJAAFAMM + MMJFAFFFJAAAAMMM + MMFFFFJJAAAMMMMM + MJFAAAAFAAMMMMMM + MDMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 331 (chameleon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMGGGGMMMMMMM + MMMGGGGGGGGMMMMM + MMMGGFFFFGGFAMMM + MMGPAAAGFFGGFAMM + MGPAAMPFFGGGGAAM + MMMGGGGGGGGFGGAA + MPGGBBGGGGFAAGGA + MFGGABGGGFAAAAAM + MGGGGGFGFAAAMAMM + MDGFFAFGGAAMMMMM + DMAAAAAAGGAMMMMM + MDDMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 332 (crocodile) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFFOFOOFAMMMM + MMMFOGFGFGOFFAMM + MMFGAAAAFFOGFFAM + MFFAAMGFFOGGGGFA + MMMMMMFOOGGGGGGA + MGMOOOOGGGGFAGGA + MFOGAGGGGGFAAAAM + FGGGGGFGGFAAMMMM + GGDDFAFGGGAMMMMM + GDDFAAAAGGAMMMMM + MDFMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 333 (salamander) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMCCCMMMMMMM + MMMMCCCCCCCMMMMM + MMMCCDDDDCCDAMMM + MMMMAAAADDCCDAMM + MMMMMMKDDCCCCAAM + MMLLLCCCCCCDCCAA + MKLCCCLLLCDAACCA + MDCEECLKKDAAAAAM + DCCAECDKDAAAMAMM + CCCCCCDCCAAMMMMM + MDAADAAACCAMMMMM + MMACCAMMMMMMMMMM + MMMAAAMMMMMMMMMM +} +# tile 334 (long worm tail) +{ + MMMMMMMMILLLLMMM + MMMMMMIILLAAMMMM + MMMMMILLAAMMMMMM + MMMMMILAMMMMMMMM + MMMMMILAMMMMMMMM + MMMMMMLLAMMMIIMM + MMMMMMMLLIIILLLL + MMMMMMMMILLAAAML + MMMIIIILLALLMMMM + MILLLLLAAMMLLMMM + ILLAAAAMMMMMLAMM + ILAMMMMMMMMMLAMM + LLAMMMMMMMMLLAMM + LILAMMMMMMLLAMMM + MLLLIIIILLLAMMMM + MMMLLLLLAAAMMMMM +} +# tile 335 (archeologist) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKJMJMMMMMM + MMMMMMKJJJMMMMMM + MMMMKCKKKJJJMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCKAAKJMAAAM + MMMMCKKKJJJJAAAM + MMMMKACKJJAJAAAM + MMMMLACJKJALAAAM + MMMMMMCJJKAAAAMM + MMMMMKCJAJJAMAMM + MMMMMCJJMJKJMMMM + MMMMMMMMMMMMMMMM +} +# tile 336 (barbarian) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMHHAMMMMMM + MMMMMMHHHHAMMMMM + MMMMMMLFLFAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMLLAALLMAAAM + MMMMLLLLLLLLAAAM + MMMMLALLLLALAAAM + MMMMLAALLAALAAAM + MMMMLAJJKJALAAAM + MMMMMMLJJLAAAAMM + MMMMMMLLALAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 337 (caveman) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMJFLFLJMMMMM + MMMMMJLLLLJMMMMM + MMMMMJJDDJAMMMMM + MMMMLLAJJALLAAAM + MMMLLLLAALLLLAAM + MMMLLALLLLALLAAM + MMMMLACKKJALAAAM + MMMMMMCKKJAAAAMM + MMMMMMLAMLAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 338 (cavewoman) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMJFLFLJMMMMM + MMMMMJLLLLJMMMMM + MMMMMJLDDLAJMMMM + MMMMLJALLAJLAAAM + MMMLLJCAAJJLLAAM + MMMLLACKKJALLAAM + MMMMLACKKJALAAAM + MMMMMMCKKJAAAAMM + MMMMMMLAMLAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 339 (healer) +{ + MMMMMMMMMMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNDDOMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMELEPMMMMMM + MMMMMMLLLPMMMMMM + MMMMMMMLLPMMMMMM + MMMMMMOMMPAMAAAM + MMMMMNNOOPPAAAAM + MMMMOOONOPPPAAMM + MMMMLANOOPALAAMM + MMMMMMNOOPAAAAMM + MMMMMNOOOPAAMAMM + MMMMNOOOOOPAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 340 (knight) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMBPAMMMMMM + MMMMMMBPPPAMMMMM + MMMMMMPEEPAMMMMM + MMMMMMPLLPAMMMMM + MMMMMMALLAAMMMMM + MMMMMBBAABBMAAAM + MMMMBPPPPPPPAAAM + MMMMPABPPPAPAAAM + MMMMLAMPPMALAAAM + MMMMMMBPMPAAAAMM + MMMMMMBPAPAAMAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 341 (monk) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCCCMMMMMM + MMMMMMJCJJJAMMMM + MMMMMMCAAAJAMMMM + MMMMMMCAAAJAMMMM + MMMMMMCKLKCAAAAM + MMMMMCDDDDDDAAAA + MMMMCDDLALDDDAAA + MMMMDALLALLADAAM + MMMMDDDDCDDDDAAM + MMMMMAACCCDAAAAM + MMMMMCDCCCDDAMAM + MMMMCCCCCCCDDMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 342 (priest) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJLLJMMMMMM + MMMMMMJLLJAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLJAMMMMM + MMMMMMIJJIAAAAMM + MMMMMODDDDDAAAAM + MMMMIDNDDDDDAAAM + MMMNLNNNDDALAAMM + MMMMMMNDDDAAAAMM + MMMMMMDIIDAAAAMM + MMMMMDDIIDDAMAMM + MMMMDIIIIIDDMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 343 (priestess) +{ + MMMMMMMMMMMMMMMM + MMMMMMMJJMMMMMMM + MMMMMMJJJJMMMMMM + MMMMMMJLLJAMMMMM + MMMMMMJLLJJMMMMM + MMMMMJJLLJJMMMMM + MMMMMJEJJEJAAAMM + MMMMMODEEDDAAAAM + MMMMIDINDDDDAAAM + MMMMLMNNNDALAAMM + MMMMMMINDDAAAAMM + MMMMMMINDDAAAAMM + MMMMMDDIIDDAMAMM + MMMMDIIIIIDDMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 344 (ranger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMCJAMMMMM + MMMMMMMCJJJAMMMM + MMMMMMMJEEJAMMMM + MMMMMMMJLLJAMMMM + MMMMMMMALLAAMMMM + MMMMMMGGAAGGMAAA + MMMMMBPFFFFPPAAA + MMMMMPAGFFFAPAAA + MMMMMLAMFFMALAAA + MMMMMMMBPMPAAAAM + MMMMMMMBPAPAAMAM + MMMMMMPPAMPPAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 345 (rogue) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOAMMMOAMMMM + MMMMMOOIDPPAMMMM + MMMMMMIDDDAMMMMM + MMMMMMLKLKAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMMBAABAAMMAM + MMMMMKEBBEJAAAAM + MMMMKAAEEAAKAAMM + MMMMLAJJHJALAAMM + MMMMMMKKJKAAAAMM + MMMMMKKAMKKAMMMM + MMMMMMMMMMMMMMMM +} +# tile 346 (samurai) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMAAMMMMM + MMMMMMMAAAMMMMMM + MMMMMMAAAAAMMMMM + MMMMMALFLFAMMMMM + MMMMMALLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMIIIAAIIIAAAM + MMMMLDIIIIDLAAAM + MMMMLABBBBALAAAM + MMMMLABBBBALAAAM + MMMMMMIDDDAAAAMM + MMMMMMIDADAAMAMM + MMMMMIIAMIIAMMMM + MMMMMMMMMMMMMMMM +} +# tile 347 (tourist) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJKJJAMMMMM + MMMMMMKJJJAMMMMM + MMMJJJJJJJJJJMMM + MMMMMMLFLFAAMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMHGAAGHMAAAM + MMMMLLGHHGLLAAAM + MMMMLAHGHGALAAAM + MMMMLAHHGHALAAAM + MMMMMMJJJKAAAAMM + MMMMMMLLALAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 348 (valkyrie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMLHHLMMMMMM + MMMMMHHHHHLMMMMM + MMMMLHELELHMMMMM + MMMMHHLLLLHMMMMM + MMMHHHALLAMMMMMM + MMMHJKJAAKJJAAAM + MMHHLJJKKJJLAAAM + MMHMLACKJCALAAAM + MMMMLAAKKAALAAAM + MMMMMMKKJKAAAAMM + MMMMMMKJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 349 (wizard) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMBPMMMMM + MMMMMMMBBPEMMMMM + MMMMMMBPPEAMMMMM + MMMMMMBAAEAMMMMM + MMMMMMBAAEAMMMMM + MMMMMMPLLEMMMMMM + MMMMMMPAAEAMAAAM + MMMMMBBPBEEAAAAM + MMMMPPPBEEEEAAMM + MMMMLABPPEALAAMM + MMMMMMBPPEAAAAMM + MMMMMBPPPEAAMAMM + MMMMMBPPPPEAMMMM + MMMMBPPPPPPEMMMM + MMMMMMMMMMMMMMMM +} +# tile 350 (Lord Carnarvon) +{ + MMMMMMMJJMMMMMMM + MMMMMMKJJJMMMMMM + MMMMKCKKKJJJMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCIAAIKMAAAM + MMMMCKKIIKKKAAAM + MMMKKCKKHKJKKAAM + MMMKKAKHKJAKKAAM + MMMKAIHKKJIAKAMM + MMMLAICKKJIALAMM + MMMMMICKAJIAAAMM + MMMMMMCKAPAAAAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 351 (Pelias) +{ + MMMMMMMMMMMMMMMM + MMMMMMMJJMMMMMMM + MMMMMMKKKJMMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCKAAKKMAAAM + MMMMCKKKKKKKAAAM + MMMKKCKKKKJKKAAM + MMMKKAKKKKAKKAAM + MMMKAMCKKJAAKAMM + MMMLAMCKAJAALAMM + MMMMMMCKAJAAAAMM + MMMMMMCKAPAAAAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 352 (Shaman Karnov) +{ + MMMMMMMMMMMMMMMM + MMMMMMMJJAMMMMMM + MMMMMMJJJJAMMMMM + MMMMMJFLFLJMMMMM + MMMMMJLLLLJMMMMM + MMMMMJJDDJAMMMMM + MMMMLHAJJAHLAAMM + MMMLLLHAAHLLLAAM + MMMLLLLHHLLLLAAM + MMMLLALHHLALLAAM + MMMLLALLLLALLAAM + MMMMLACKKJALAAAM + MMMMMMCKKJAAAAMM + MMMMMMLAMLAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 353 (Earendil) +{ + MMMMMMMMMGMMMMMM + MMMMBMMGGFMMBMMM + MMMBBMGGGGABBMMM + MMBPBPLELEABPBMM + MMBBBPLLLLABBBMM + MMPBPPALLAPPPMMM + MMMPPBGAAGBBBMMM + MMMBBLGGGFLBBBMM + MMBBLAAGFAALBBMM + MMMBLAGGGFALBMMM + MMMMMMGFAFMMMMMM + MMMMMMLMMLMAAAMM + MMMMMMAAAAAAAAMM + MMMMAAAAAAAAMMMM + MMMMMAAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 354 (Elwing) +{ + MMMMMMMMMGMMMMMM + MMMMBMMGGFMMBMMM + MMMBBMGGGGABBMMM + MMBPBHLELEHBPBMM + MMBBBHLLLLHBBBMM + MMPBHHALLAHHPMMM + MMMPHHGAAGHHBMMM + MMMBBLGGGFLBBBMM + MMBBLAAGFAALBBMM + MMMBLAGGGFALBMMM + MMMMMMGFAFMMMMMM + MMMMMMLMMLMAAAMM + MMMMMMAAAAAAAAMM + MMMMAAAAAAAAMMMM + MMMMMAAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 355 (Hippocrates) +{ + MMMMMMMMMMMMMMMM + MMMMLLLCCDMMMMMM + MMMLLCCDDAMMMMMM + MMMLAAAADAMMMMMM + MMMLBABADAMMMMMM + MMMLAAAADAMMMMMM + MMMCCLLDDMBMMMMM + MMMMCKKDDFBFAAAM + MMLLLCLDDDBFAAAM + MCCCCLDDDFBAAAMM + MLALLCCDDFBDAAMM + MMMLCCCCDABFAAMM + MMMLCCCCDABAAAMM + MMLLCCCCDAAMAAMM + MLCCCCCCCDAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 356 (King Arthur) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMOHHAMMMMMM + MMMMMOHHHHAMMMMM + MMMMMHBLBHAMMMMM + MMMMMHLLLHAMMMMM + MMMMMALLLAAMMMMM + MMMMBBAAABBMAAAM + MMMBPPPPPPPPAAAM + MMMPABPPPPACPAAM + MMNNNNNNNNNCLCAM + MMMMMBPPMPACAAMM + MMMMMBPAPPAAMAMM + MMMMMBPAPPAAMAMM + MMMMPPAAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 357 (Grand Master) +{ + MMMMMMMMMMMMMMMM + MMMMMMMLLMMMMMMM + MMMMMMLLLLMMMMMM + MMMMMMLLLLMMMMMM + MMLCMCALLACMMMMM + MCLLCMCAACCCCMMM + MCJLACCCCCCCCCMM + MMJAACCCCCCCCCCM + MMJCCCCCCCCCCCMA + MMJMMPPPPPLCCAAA + MMJMMCCCCLLCAAAA + MMJMMCCCCCCCAAAA + MMJMMCCCCCCCAAAA + MMJMACCCCCCCAAAM + MMJACCCCCCCCAAMM + MMMMMMMMMMMMMMMM +} +# tile 358 (Arch Priest) +{ + MMNMMMMMMMMMMMMM + MNNNMMJLLJMMMMMM + MMNMMMJLLJMMMMMM + MMNMMMLLLLMMMMMM + MMLCMCALLACMMMMM + MCLLCMCAACJDKMMM + MCHLACCCCJCCDKMM + MMHAACCJJCCCCDKM + MMHCCCCJCCJCCCMA + MMHMMDCJCCLJCAAA + MMHMMDCJCLLCAAAA + MMHMMKCJCCDJAAAA + MMHMMKCJCCDJAAAA + MMHMACCJCCDJAAAM + MMHACCCJJCCCAAMM + MMMMMMMMMMMMMMMM +} +# tile 359 (Orion) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCJAMMMMMM + MMMMMMCJJJAMMMMM + MMMMMMJEEJAMMMMM + MMMMMMJLLJAMMMMM + MMMMMMALLAAMMMMM + MMMMMGGAAGGMMMMM + MMMMBGFFFFFPMMMM + MMMMBPFFFFPPAAAM + MMMMPAGFFFAPAAAM + MMMMLANNNNALAAAM + MMMMMMBPMPAAAAAM + MMMMMMBPMPAAAAMM + MMMMMMBPAPAAMAMM + MMMMMPPAMPPAMMMM +} +# tile 360 (Master of Thieves) +{ + MMMMMMMMMMMMMMMM + MMMHMMMMMHMMMMMM + MMMHHIDKHHMMMMMM + MMMMIDDDDMMMMMMM + MMMMLLLLLAMMMMMM + MMMMLBLBLAMMMMMM + MMMMLLLLLAMMMMMM + MMMMMLLLAMMMMMMM + MMMMBMAABAAMMMMM + MMMKEBBBEJAAAMMM + MMKAEEEEEAJAAAMM + MMLAJJHHJALAAAMM + MMMMJKKKJAAAAAMM + MMMMKJAJKAAAAMMM + MMMJJAMMJJAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 361 (Lord Sato) +{ + MMMMMAAAMMMMMMMM + MMMMMAAAMMMMMMMM + MMMAAAAAAAMMMMMM + MMAALLLLLAAMMMMM + MMALFFLFFLAMMMMM + MMALLLLLLLAMMMMM + MMMAALLLAMMMMMMM + IIIIIAAAIIIIAAAM + LLDIIIIIIDLLAAAM + LLABBBBBBALLAAAM + LLABBBBBBALLAAAM + LLABBBBBBALLAAAM + MMMIIDDDDAAAAAAM + MMMIIAAIDAAAMMAM + MMIIIAMIIIAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 362 (Twoflower) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJKJJAMMMMM + MMMMMMKJJJAMMMMM + MMMMJJJJJJJJMMMM + MMMMMNNLNNAMMMMM + MMMMNALNALNAMMMM + MMMMMNNANNAAMMMM + MMMMMAAAAAAMAAAM + MMMMLLHGHGLLAAAM + MMMMLAGGGGALAAAM + MMMMLAHGHGALAAAM + MMMMMMJJJKAAAAMM + MMMMMMJJAKAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 363 (Norn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMNNNMMMMMMM + MMMMMNNNNNMMMMMM + MMMMMNELELNMMMMM + MMMMNNLLLLNMMMMM + MMMNNNALLAMMMMMM + MMMNJKJAAKJJAAAM + MMNNLJJKKJJLAAAM + MMNMLACKJCALAAAM + MMMMLAKKKKALAAAM + MMMMMMKKJKAAAAMM + MMMMMMKJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 364 (Neferet the Green) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMGGGMMMMMMM + MMMMMGFFFGMMMMMM + MMMMGFEFEGMMMMMM + MMMMGFFFFEGMMMMM + MNMGPEFFFEEMMMMM + MIMMBBEAAEAMAAMM + MIMBBPPBBEEAAAAM + MIGBPPPPPEEEAAMM + MIMPPMEPEAAGAAMM + MIMMMBPPPAAAAAMM + MNMMMBPPPEAAMAMM + MMMMBPPPPPEAMMMM + MMMBPPPPPPPEMMMM + MMMMMMMMMMMMMMMM +} +# tile 365 (Minion of Huhetotl) +{ + MMMOPMMMMMMPOMMM + MMMOODDDDDDOODMM + MMDLOOCDDCOOLDDM + MDDDLDDDDDDLDDDM + MCCDDDNDDNDDDCCM + CCDKDDDDDDDDJCCC + CDDKKDDIIDDJJCCD + CDDKKKDAADJJDCDD + CCDKDDKKKJDDKCDD + MCCDKKDDDDKKCDDD + MCCDADKDDKDACDDM + MDDDADDDDDDADDDM + MMMMCCDDDDKKAAMM + MMMCDDDAADDDKAAM + MMCDDDAAAMDDDKMM + MMMMMMMMMMMMMMMM +} +# tile 366 (Thoth Amon) +{ + MMMMMMMMMMMMMMMM + MMMMMMOJJOMMMMMM + MMMMMMJJJJAMMMMM + MMMMMMBLBLAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMBPAAPPMAAAM + MMMMBPPPPPPPAAAM + MMMPPBPPPPJPPAAM + MMMPPAPPPMAPPAMA + MMMPAMBPPMAAPAMA + MMMLAMBPPMMALMAM + MMMMMMBPAMAMMAMM + MMMMMMBPAPAAAAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 367 (Chromatic Dragon) +{ + MMMMMMGGGFAMMMMM + MMMMMNFNFEEAMMMM + MMMMGFFFEECAMMMM + MMDCHHFMMCCAMMMM + CHCHCDMMBCCAMMMM + HDMDMMMBFFAMMMMM + MMMMMMOBFAAAAAAM + MMMMHOGFAAAAAAAA + MMHOOIEAMEFMAAAM + MHOOOIEEEEFFJAAM + MHOOOIEEFFFDDAAM + HBOOIIEFFFDDCCAM + HBMOIEFOODDMCJAM + HBAAGEMAADDACCAM + MMMMGFAAMMMCCJAM + MMMMMMMMFFFFJAMM +} +# tile 368 (Goblin King) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MHMMHMMMHMMMMMMM + CLCMHCHCHMMMMMMM + CLCMHHHHHMMMMMMM + MHMMIIIIIMMMMMMM + MHKMIHIHIMIMMMMM + MHICKIIIJKKMMMMM + MHMIIJJJKMAAMMMM + MHMMJICJJAAAAAMM + MHMMIIIIJAAAAAMM + MMMMJIIJJAAMMMMM + MMMMIJKJJAMMMMMM + MMMIKAAMIKMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 369 (Cyclops) +{ + MMMMMMMMMMMMMMMM + MMMMLLLLLMMMMMMM + MMMCLLLLLCMMMMMM + MMMLBABNNLMMMMMM + MMMLBBBNNLJAMMMM + MMMCLNNNLCJAMMMM + MMMMLLLLLJAAAMMM + MMMMLAALLAAAAMMM + MMJKJLLLKAKJAAMA + MCLKAJJJJAKLCAAA + MLLJKAAAAKJLLAAM + MLAAJKKKKJAALAAM + MLCMGGGHGGACLAAA + MLLMJJJJJJALLAAA + MMMMCJJJCLAAAAAA + MMLLLLLMLLLLLAAM +} +# tile 370 (Ixoth) +{ + MMMMOMMMMMMOMMMM + MMMMOMMMMMMOMMMM + MMMLOOCDDCOOLMMM + MMMDDDDDDDDDDDMM + MCCDDDGDDGDDDCCM + CCDKDDDDDDDDJCCC + CDDKKKDIIDJJDCCD + CDDKDDKJJJDDDCDD + CCDKDDDDDDDDKCDD + MCCDKKDDDDKKCDDD + MCCDADKDDKDACDDM + MDDDADDDDDDADDDM + MMMMCCDDDDKKAAMM + MMMCDDDAADDDKAAM + MMCDDDAAAMDDDKMM + MMMMMMMMMMMMMMMM +} +# tile 371 (Master Kaen) +{ + MMMMMMMMMMMMMMMM + MMMMMMMKKAMMMMMM + MMMMMMKJJKAMMMMM + MMKKAMKAAKAMKKAM + MKAAKAKDDKAKAAKA + MKOOJKKOOKKKOOJA + MKJJJJJJJJJKJJJA + MMKJJJJJJJJJJJAM + MMMMKJJJJJJJAMMM + MMMMMMKJJJAAAAAA + MMMMMKJJJJKAAAAA + MMMMMKJJJJJAAAAA + MMMMKJJJJJJKAAAM + MMMKJJJJJJJJKAAM + MMMKJJJJJJJJJAMM + MMMMMMMMMMMMMMMM +} +# tile 372 (Nalzok) +{ + MMMMOMMMMMMOMMMM + MMMMOMMMMMMOMMMM + MMMLOOCDDCOOLMMM + MMMDDDDDDDDDDDMM + MCCDDDBDDBDDDCCM + CCDKDDDDDDDDJCCC + CDDKKKDIIDJJDCCD + CDDKDDKJJJDDDCDD + CCDKDDDDDDDDKCDD + MCCDKKDDDDKKCDDD + MCCDADKDDKDACDDM + MDDDADDDDDDADDDM + MMMMCCDDDDKKAAMM + MMMCDDDAADDDKAAM + MMCDDDAAAMDDDKMM + MMMMMMMMMMMMMMMM +} +# tile 373 (Scorpius) +{ + MMMMMJLJLJAAMMMM + MMMMJAMJCJCKAAMM + MMMMAJMMMMMJJJAM + MMMMLAMMMMMMLCKA + MJAKJAMMMMMMJJJA + MMJJAMMMMMMALCJA + MMMMMMMALLAJCJKA + MMMMJJALCCAAJJAM + MJJALLAJCJJJAAMM + JAMLCCAJAJJAAAAM + MMJACJJJAAACCJAA + GGJJJJJAACCAAAJA + MJJGGAJACAAJJAAA + DMJJAAJAACAMJAAM + MMMDMMMJAAJAMJJM + MMMMMMMMJAMJAMMM +} +# tile 374 (Master Assassin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMABLBLAMMMMM + MMMMMAAAAAAMMMMM + MMMMMMAAAAMMMMMM + MMMMMAAAAAAMMPPM + MMMMAAAAAAAAPPPM + MMMMAAAAAAAAPPPM + MMMMLAAAAAALPPPM + MMMMMMAAAAAPPPMM + MMMMMMAAAAAPMPMM + MMMMMAAAMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 375 (Ashikaga Takauji) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMAAMMMMMMMM + MMMMMMMAAAMMMMMM + MMMMMMAAAAAMMMMM + MMMMMALFLFAMMMMM + MMMMMALLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMIIIAAIIIAAAM + MMMMLDIIIIDLAAAM + MMMMLAIIIIALAAAM + MMMMLALHHLALAAAM + MMMMMMIIIIAAAAMM + MMMMMMIIAIAAMAMM + MMMMMIIAMIIAMMMM + MMMMMMMMMMMMMMMM +} +# tile 376 (Lord Surtur) +{ + MMMMPPDDDDAAMMMM + MMMMPDDDDDDDAMMM + MMMPPDLLLLDDAMMM + MMMPDPFLLFFDAMMM + MMMPPPLLLLLDAMMM + MMMMPLLAALLAAAMM + MMMPPALLLLBAAAAM + MMMPPDBBBBBBBAAM + MMBDDHDPBPPPPPAM + MMPPHDDPBPPPPPAM + MMLAHDHPBPPAALAA + JLAADDHBBBBAALAA + JJLJDHHPBPPPCLAA + MMLLJBPPABPPLLAA + MMMMMBPPABPPAAAA + MMMLLLLJMBLLLKAA +} +# tile 377 (Dark One) +{ + MMMMMMMMMMMMMMMM + MMMMMMAAAMMMMMMM + MMMMMAAAAAMMMMMM + MMMMMADADAMMMMMM + MMMMMAAAAAMMMMMM + MMMMAAAAAAAMMMMM + MMMMAAAAAAAMMMMM + MMMAAAAAAAAAMMMM + MMMAAAAAAAAAMMMM + MMAAAAAAAAAAAMMM + MMAAAAAAAAAAAMMM + MMMAAAAAAAAAMMMM + MMMAAAAAAAAAAMMM + MMAAAAAAAAAAAAMM + AAAAAAAAAAAAAAAA + MMMMMMMMMMMMMMMM +} +# tile 378 (student) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMGGFGGMMMMMM + MMMMMMMGMMMMMMMM + MMMMMMNDNDMMMMMM + MMMDDDDDDDDMMMMM + MMMMMMLELEAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMCKAAKJMAAAM + MMMMCKKKJJJJAAAM + MMMMKACKJJAJAAAM + MMMMLACJKJALAAAM + MMMMMKCJAJJAMAMM + MMMMMCJJMJKJMMMM + MMMMMMMMMMMMMMMM +} +# tile 379 (chieftain) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMHHAMMMMMM + MMMMMMHHHHAMMMMM + MMMMMMLFLFAMMMMM + MMMMMMLLLLAMMMMM + MMMMMHALLAHMMMMM + MMMMLLHAAHLLAAAM + MMMMLLLIILLLAAAM + MMMMLALIILALAAAM + MMMMLAALLAALAAAM + MMMMLAJJKJALAAAM + MMMMMMLJJLAAAAMM + MMMMMMLLALAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 380 (neanderthal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJJMMMMMM + MMMMMJJJJJJMMMMM + MMMMMJFLFLJMMMMM + MMMMMJLLLLJMMMMM + MMMMMJJDDJAMMMMM + MMMMJJAJJAJJMAAM + MMMJLLJAAJLLJAAM + MMMLLALJJLALLAAM + MMMMLALCCLALAAAM + MMMMMMLAMLAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 381 (High-elf) +{ + MMMMMMMMMGMMMMMM + MMMMMMMGGFMMMMMM + MMMMMMGGGGAMMMMM + MMMMMMLILIAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMMGAAGMMAAMM + MMMMMLGGGFLAAAAM + MMMMLAAGFAALAAAM + MMMMLAMGFAALAAMM + MMMMLAMGFAALAAMM + MMMMLAGGGFALMAMM + MMMMMMGFAFAAMAMM + MMMMMMGFAFAAMMMM + MMMMMMGFAFAAMMMM + MMMMMKLAMLKAMMMM +} +# tile 382 (attendant) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJMMMMMMM + MMMMMJLLLJMMMMMM + MMMMMMBLBMMMMMMM + MMMMMCLLLDMMMMMM + MMMMMCCKKDAMAAAM + MMMMMLLCLDDAAAAM + MMMMCCCLDDDDAAMM + MMMMLALCCDALAAMM + MMMMMMLCCDAAAAMM + MMMMMLCCCDAAMAMM + MMMMLCCCCCDAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 383 (page) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMBPAMMMMMM + MMMMMMBPPPAMMMMM + MMMMMMPEEPAMMMMM + MMMMMMPLLPAMMMMM + MMMMMMMLLAAMMMMM + MMMMMMBAABAMAAAM + MMMMMBPPPPPAAAAM + MMMMPABPPPAPAAAM + MMMMLAMPPMALAAAM + MMMMMMBPMPAAAAMM + MMMMMMLLALAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 384 (abbot) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKLKMMMMMMM + MMMMMMLLLMMMMMMM + MMMMCCLLLJJMMMMM + MMMMKCKLKKJAAAMM + MMMMCDDDDDDAAAAM + MMMCDDLALDDDAAAM + MMMDALLALLADAAMM + MMMDDDDCDDDDAAMM + MMMMAACCCDAAAAMM + MMMMCDCCCDDAMAMM + MMMCCCCCCCDDMMMM +} +# tile 385 (acolyte) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJJMMMMMM + MMMMMMJLLJAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLJAMMMMM + MMMMMMCJJCAAAAMM + MMMMMLDDDDDAAAAM + MMMMCDCCDDDDAAAM + MMMMLMLCCDALAAMM + MMMMMMLCCDAAAAMM + MMMMMMLCCDAAAAMM + MMMMMLDCCDDAMAMM + MMMMLCCCCCDDMMMM + MMMMMMMMMMMMMMMM +} +# tile 386 (hunter) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJMMCJAMMMMMM + MMMJMMCJJJAMMMMM + MMMJMMJEEJAMMMMM + MMJMMMJLLJAMMMMM + MMJMMMALLAAMMMMM + MMJMMGGAAGGMAAAM + MMLPBPFFFFPPAAAM + MMJMMAGFFFAPAAAM + MMJMMMMFFMALAAAM + MMMJMMBPMPAAAAMM + MMMJMMBPAPAAMAMM + MMMMJPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 387 (thug) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMIDMMMMMMM + MMMMMMIDDDAMMMMM + MMMMMMLKLKAMMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMKKAAKKAMMAM + MMMMKKJKKJJKAAAM + MMMMKAAJJAAKAAMM + MMMMLAJJJJALAAMM + MMMMMMKKJKAAAAMM + MMMMMMKAAKAAAAMM + MMMMMKKAMKKAMMMM + MMMMMMMMMMMMMMMM +} +# tile 388 (ninja) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMAAMMMMM + MMMMMMMAAAMMMMMM + MMMMMMAAAAAMMMMM + MMMMMAFLFLAMMMMM + MMMMMAAAAAAMMMMM + MMMMMMAAAAMMMMMM + MMMMAAAAAAAAMPPM + MMMMAAAAAAAAPPPM + MMMMAAAAAAAAPPPM + MMMMLAAAAAALPPPM + MMMMMMAAAAAPPPMM + MMMMMMAAAAAPMPMM + MMMMMAAAMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 389 (roshi) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMAAAAAMMMM + MMMMMMAAAAAMMMMM + MMMMMALFLFAMMMMM + MMMMMALLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMPPPAAPPPAAAM + MMMMLMPPPPMLAAAM + MMMMLAOOOOALAAAM + MMMMLAOOOOALAAAM + MMMMMMPMMMAAAAMM + MMMMMMPMAMAAMAMM + MMMMMPPAMPPAMMMM + MMMMMMMMMMMMMMMM +} +# tile 390 (guide) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJKJJAMMMMM + MMMMMMKJJJAMMMMM + MMMMJJJJJJJJMMMM + MMMMMMLFLFAAMMMM + MMMMMMLLLLAMMMMM + MMMMMMALLAMMMMMM + MMMMMHHAAHHMAAAM + MMMMLLHHHHLLAAAM + MMMMLAHHHHALAAAM + MMMMLAHHHHALAAAM + MMMMMMJJJKAAAAMM + MMMMMMJJAKAAMAMM + MMMMMLLAMLLAMMMM + MMMMMMMMMMMMMMMM +} +# tile 391 (warrior) +{ + MMMMMOMMMMOMMMMM + MMMMMNOMMONMMMMM + MMMMMMNPPNMMMMMM + MMMMMPPPPPPMMMMM + MMMMMPELELPMMMMM + MMMMHHLLLLHMMMMM + MMMHHHALLAMMMMMM + MMMHJKJAAKJJAAAM + MMHHLJJKKJJLAAAM + MMHMLACKJCALAAAM + MMMMLAAKKAALAAAM + MMMMMMKKJKAAAAMM + MMMMMMKJAJAAMAMM + MMMMMMKJAJAAMAMM + MMMMMKLAMLKAMMMM + MMMMMMMMMMMMMMMM +} +# tile 392 (apprentice) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJMMMMMMM + MMMMMJLLLJMMMMMM + MMMMMMGLGMMMMMMM + MMMMMBLLLEMMMMMM + MMMMMBBEEEAMAAAM + MMMMMBBPBEEAAAAM + MMMMPPPBEEEEAAMM + MMMMLABPPEALAAMM + MMMMMMBPPEAAAAMM + MMMMMBPPPEAAMAMM + MMMMMBPPPPEAMMMM + MMMMBPPPPPPEMMMM + MMMMMMMMMMMMMMMM +} +# tile 393 (invisible monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMNNNNMMMMMMM + MMMMNNNNNNMMMMMM + MMMNNAAAANNMMMMM + MMMNNAMMMNNAMMMM + MMMMAAMMMNNAMMMM + MMMMMMMMNNAAMMMM + MMMMMMMNNAAMMMMM + MMMMMMNNAAMMMMMM + MMMMMMNNAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMNNMMMMMMMM + MMMMMMNNAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} diff --git a/win/share/objects.txt b/win/share/objects.txt new file mode 100644 index 0000000..4592352 --- /dev/null +++ b/win/share/objects.txt @@ -0,0 +1,8281 @@ +A = (0, 0, 0) +B = (0, 182, 255) +C = (255, 108, 0) +D = (255, 0, 0) +E = (0, 0, 255) +F = (0, 145, 0) +G = (108, 255, 0) +H = (255, 255, 0) +I = (255, 0, 255) +J = (145, 71, 0) +K = (204, 79, 0) +L = (255, 182, 145) +M = (71, 108, 108) +N = (255, 255, 255) +O = (218, 218, 182) +P = (108, 145, 182) +# tile 0 (strange object) +{ + MMMMMMMMMMMMMMMM + MMMCJKKKCJKKKCMM + MCKJKKCKJKKCJJKM + MKJKKKKJKKKKJJJM + CJKKKCJKKKCJJJJM + CJKKKCJKKKCJJAJM + CCCCCHHCCCCJAJJM + AADDAAAADDAAJJJM + CCCCCHHCCCCJJJJM + CJJJHJJHJJJJJJJM + CJKKKHHKKKCJJJJA + CJKKKHHKKKCJJJJA + CJKKKCJKKKCJJJAA + CJCCCCJCCCCJJAAM + CKKKKKKKKKKJAAMM + MAAAAAAAAAAAAMMM +} +# tile 1 (arrow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMPPMMMM + MMMMMMMMMPBPOMMM + MMMMMMMMMBPOBLMM + MMMMMMMMMPOBLAMM + MMMMMMMMJPAAAMMM + MMMMMMMJKAMMMMMM + MMMMMMJKAMMMMMMM + MMMMMJKAMMMMMMMM + MMMMJKAMMMMMMMMM + MMMJKAMMMMMMMMMM + MOOCAMMMMMMMMMMM + MONNAMMMMMMMMMMM + MNNOAMMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 2 (runed arrow / elven arrow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMFFMMMM + MMMMMMMMMFGFOMMM + MMMMMMMMMGFOGHMM + MMMMMMMMMFOGHAMM + MMMMMMMMJFAAAMMM + MMMMMMMJKAMMMMMM + MMMMMMJKAMMMMMMM + MMMMMJKAMMMMMMMM + MMMMJKAMMMMMMMMM + MMMJKAMMMMMMMMMM + MOOCAMMMMMMMMMMM + MONNAMMMMMMMMMMM + MNNOAMMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 3 (crude arrow / orcish arrow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMPPMMMM + MMMMMMMMMPBPOMMM + MMMMMMMMMBPOBLMM + MMMMMMMMMPOBLAMM + MMMMMMMMJKAAAMMM + MMMMMMMJKAMMMMMM + MMMMMMJKAMMMMMMM + MMMMMJKAMMMMMMMM + MMMMJKAMMMMMMMMM + MMMJKAMMMMMMMMMM + MOJCAMMMMMMMMMMM + MONAAMMMMMMMMMMM + MNNOAMMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 4 (silver arrow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMDPMMMM + MMMMMMMMMDBDOMMM + MMMMMMMMMBDOBDMM + MMMMMMMMMDOBDAMM + MMMMMMMMPPAAAMMM + MMMMMMMPPAMMMMMM + MMMMMMPPAMMMMMMM + MMMMMPPAMMMMMMMM + MMMMPPAMMMMMMMMM + MMMPPAMMMMMMMMMM + MOPPAMMMMMMMMMMM + MOPPAMMMMMMMMMMM + MNNOAMMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 5 (bamboo arrow / ya) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMBPMMMM + MMMMMMMMMBBPOMMM + MMMMMMMMBBPOBLMM + MMMMMMMMMPOBLAMM + MMMMMMMMCJBLAMMM + MMMMMMMCJAMAMMMM + MMMMMMCJAMMMMMMM + MMMMMCJAMMMMMMMM + MMMMCJAMMMMMMMMM + MMMCJAMMMMMMMMMM + MMCJAMMMMMMMMMMM + OCJAMMMMMMMMMMMM + ONAMMMMMMMMMMMMM + NNPAMMMMMMMMMMMM + MAAMMMMMMMMMMMMM +} +# tile 6 (crossbow bolt) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMMMMMMMMMM + MMAONMMMMMMMMMMM + MMMAOKMMMMMMMMMM + MMMMAJKMMMMMMMMM + MMMMMAJKMMMMMMMM + MMMMMMAJKMMMMMMM + MMMMMMMAJKMMMMMM + MMMMMMMMAJKMMMMM + MMMMMMMMMAJKMMMM + MMMMMMMMMMAJJMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 7 (dart) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMOOOMM + MMMMMCLCLMOBBAMM + MNNNCJDJDCBAAAMM + MAAAAJDJDAOBBMMM + MMMMMAAAAMMOOOMM + MMMMMMMMMMMAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 8 (throwing star / shuriken) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNMMMMMMMMM + MMMMMNAPNNPMMMMM + MMMMMNNNNANAMMMM + MMMMMPNNNPAAMMMM + MMMMNMNNNNMMMMMM + MMMMPNNPANAMMMMM + MMMMMAAANPAMMMMM + MMMMMMMMMAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 9 (boomerang) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMMKHKAMMMMMMMM + MMMMMKHKAMMMMMMM + MMMMMMKKKAMMMMMM + MMMMMMMKKKAMMMMM + MMMMMMMMKDKAMMMM + MMMMMMMMJDKAMMMM + MMMMMMMJKDJAMMMM + MMMMMMJKDJAMMMMM + MMMMMJHDJAMMMMMM + MMMMJHDJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 10 (spear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMNOM + MMMMMMMMMMMMNOAM + MMMMMMMMMMMNOAAM + MMMMMMMMMMKJAAMM + MMMMMMMMMKJAAMMM + MMMMMMMMKJLAMMMM + MMMMMMMKJAAJAMMM + MMMMMMKJAAKJAMMM + MMMMMKJAAMCJAMMM + MMMMKJAAMMLAMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MMJAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 11 (runed spear / elven spear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMNOM + MMMMMMMMMMMMNOAM + MMMMMMMMMMMNOAAM + MMMMMMMMMMKJAAMM + MMMMMMMMMKJAAMMM + MMMMMMMMKJLAMMMM + MMMMMMMKJAAFAMMM + MMMMMMKJAAFFAMMM + MMMMMKJAAMGFAMMM + MMMMKJAAMMHAMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MMJAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 12 (crude spear / orcish spear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMNOM + MMMMMMMMMMMMNOAM + MMMMMMMMMMMKJAAM + MMMMMMMMMMKJAAMM + MMMMMMMMMLJAAMMM + MMMMMMMMKJLAMMMM + MMMMMMMKJAAJAMMM + MMMMMMKJAKKJAMMM + MMMMMKJAAMCJAMMM + MMMMKJAAMMLAMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MMJAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 13 (stout spear / dwarvish spear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMNMM + MMMMMMMMMMMMNOMM + MMMMMMMMMMMNOAMM + MMMMMMMMMMNOAAMM + MMMMMMMMMKJAAMMM + MMMMMMMMKJAAMMMM + MMMMMMMKJLAMMMMM + MMMMMMKJAAJAMMMM + MMMMMKJAACJAMMMM + MMMMKJAAMLAMMMMM + MMMKJAAMMMMMMMMM + MMJJAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 14 (silver spear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMOA + MMMMMMMMMMMMMOAM + MMMMMMMMMMMMPAMM + MMMMMMMMMMMPAMMM + MMMMMMMMMMPAMMMM + MMMMMMMMMPAMMMMM + MMMMMMMMPAMMMMMM + MMMMMMMBAMMMMMMM + MMMMMMBAMMMMMMMM + MMMMMPAMMMMMMMMM + MMMMPAMMMMMMMMMM + MMMPAMMMMMMMMMMM + MMOAMMMMMMMMMMMM + MOAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 15 (throwing spear / javelin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMOA + MMMMMMMMMMMMMOAM + MMMMMMMMMMMMIAMM + MMMMMMMMMMMIAMMM + MMMMMMMMMMIAMMMM + MMMMMMMMMIAMMMMM + MMMMMMMMIAMMMMMM + MMMMMMMLAMMMMMMM + MMMMMMLAMMMMMMMM + MMMMMIAMMMMMMMMM + MMMMIAMMMMMMMMMM + MMMIAMMMMMMMMMMM + MMOAMMMMMMMMMMMM + MOAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 16 (trident) +{ + MMMMMMMMMMMMMMMM + MMMPAMMMMMMMMMMM + MMMMPAMMMMMMMMMM + MMPAMPAMMMMMMMMM + PAMPAMPAMMMMMMMM + MPAMPAPAMMMMMMMM + MMPAMPPAMMMMMMMM + MMMPPPPAMMMMMMMM + MMMMMMMPAMMMMMMM + MMMMMMMMPAMMMMMM + MMMMMMMMMPAMMMMM + MMMMMMMMMMPKAMMM + MMMMMMMMMMJPKAMM + MMMMMMMMMMMJPKAM + MMMMMMMMMMMMJAMM + MMMMMMMMMMMMMMMM +} +# tile 17 (dagger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNOAMMMMM + MMMMMMMNOAAMMMMM + MMMMOONOAAMMMMMM + MMMMMKOOAMMMMMMM + MMMMKJAOAMMMMMMM + MMMKJAAAAMMMMMMM + MMMAAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 18 (runed dagger / elven dagger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNOAMMMMM + MMMMMMMNOAAMMMMM + MMMMOONOAAMMMMMM + MMMMMKOOAMMMMMMM + MMMMKFAOAMMMMMMM + MMMKFAAAAMMMMMMM + MMMAAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 19 (crude dagger / orcish dagger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNOAMMMMM + MMMMMMMNOAAMMMMM + MMMMOMNOAAMMMMMM + MMMMMCOAAMMMMMMM + MMMMKJAOAMMMMMMM + MMMKJAAAAMMMMMMM + MMMAAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 20 (silver dagger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNPAMMMMM + MMMMMMMNPAAMMMMM + MMMMBBNPAAMMMMMM + MMMMMKPPAMMMMMMM + MMMMKJAPAMMMMMMM + MMMKJAAAAMMMMMMM + MMMAAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 21 (athame) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMPMM + MMMMMMMMMMMMNPAM + MMMMMMMMMMMPPAMM + MMMMMMMMMMNOAAMM + MMMMMMMMMPPAMMMM + MMMMMMMMNOAAMMMM + MMMMMMPPPAMMMMMM + MMMMPONOPAMMMMMM + MMMMMKPBAMMMMMMM + MMMMKJAOAMMMMMMM + MMMKJAMMMMMMMMMM + MMMMAMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 22 (scalpel) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNAMMMMMMMMMM + MMMMONAMMMMMMMMM + MMMMMPBAMMMMMMMM + MMMMMMMPAMMMMMMM + MMMMMMMMPAMMMMMM + MMMMMMMMMPAMMMMM + MMMMMMMMMMPAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 23 (knife) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNOAMMMMM + MMMMMMMNOAAMMMMM + MMMMMMNOAAMMMMMM + MMMMMKLAAMMMMMMM + MMMMKLAAMMMMMMMM + MMMKLAAMMMMMMMMM + MMMAAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 24 (stiletto) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMOAMMM + MMMMMMMMMMOAMMMM + MMMMMMMMPOAMMMMM + MMMMMMMPOAMMMMMM + MMMMMMOOAMMMMMMM + MMMOPNOAMMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAOPMMMMMMMM + MMKJAAMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 25 (worm tooth) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMOMMMMMMMMM + MMMMMMNAMMMMMMMM + MMMMMMNOAMMMMMMM + MMMMMMPOAMMMMMMM + MMMMMMMOAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 26 (crysknife) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMNOAMMM + MMMMMMMMMPPAAMMM + MMMMMMMMNOAAMMMM + MMMMMMPPPAMMMMMM + MMMMPONOPAMMMMMM + MMMMMKPBAMMMMMMM + MMMMKJAOAMMMMMMM + MMMKJAMMMMMMMMMM + MMMMAMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 27 (axe) +{ + MMMMMMMMMMMMMMMM + MMMMMOPAMMMMMMMM + MMMMOPPPAMMMMMMM + MMMOPPPPPAMMMMMM + MMMKPKPPPAMMMMMM + MMMMKPPPAMMMMMMM + MMMMMKAAMMMMMMMM + MMMMMMKAMMMMMMMM + MMMMMMMCAMMMMMMM + MMMMMMMMCAMMMMMM + MMMMMMMMMCAMMMMM + MMMMMMMMMMCAMMMM + MMMMMMMMMMMCAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 28 (double-headed axe / battle-axe) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOPPAMMMMMMMM + MMMMOPPPAMMMMMMM + MMMKPPPPAMMMMMMM + MOPPKPPPAMMMMMMM + MOPPPKAAMMMMMMMM + MPPPPAKAMMMMMMMM + MMPPPAMCAMMMMMMM + MMMAAMMMCAMMMMMM + MMMMMMMMMCAMMMMM + MMMMMMMMMMCAMMMM + MMMMMMMMMMMCAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 29 (short sword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMAONMMMMMMMMMM + MMMMAONMMMMMMMMM + MMMMMAONMMMMMMMM + MMMMMMAONAPOMMMM + MMMMMMMAOOKMMMMM + MMMMMMAPOAJKMMMM + MMMMMMAAAAAJKMMM + MMMMMMMMMMAAJMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 30 (runed short sword / elven short sword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMAONMMMMMMMMMM + MMMMAONMMMMMMMMM + MMMMMAONMMMMMMMM + MMMMMMAONAPOMMMM + MMMMMMMAOOMMMMMM + MMMMMMAPOFGMMMMM + MMMMMMAAAAFGMMMM + MMMMMMMMMMAFGMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 31 (crude short sword / orcish short sword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMAONMMMMMMMMMM + MMMMAONMMMMMMMMM + MMMMMAONMMMMMMMM + MMMMMAAONAPOMMMM + MMMMMMMAOOKMMMMM + MMMMMMAPOBJKMMMM + MMMMMMAAAABJKMMM + MMMMMMMMMMABJMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 32 (broad short sword / dwarvish short sword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMAONMMMMMMMMMM + MMMMAONMMMMMMMMM + MMMMMAONPMMMMMMM + MMMMMAAONAPOMMMM + MMMMMMMAOOMMMMMM + MMMMMMAPOJJMMMMM + MMMMMMAAAAJJMMMM + MMMMMMMMMMAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 33 (curved sword / scimitar) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MANMMMMMMMMMMMMM + MAPNMMMMMMMMMMMM + MMAOOMMMMMMMMMMM + MMAANOMMMMMMMMMM + MMMAPNOMMMMMMMMM + MMMMAPNNMMMMMMMM + MMMMMAAONPOMMMMM + MMMMMMMAPOAMMMMM + MMMMMMAPOJKJMMMM + MMMMMMMMAAJKJMMM + MMMMMMMMMMAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 34 (silver saber) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNAMMM + MMMMMMMMMMPOPAMM + MMMMMMMMMPNOPAMM + MMMMMMMMPNNPAMMM + MMMMPPANNOPAMMMM + MMMMMOOOAAMMMMMM + MMMMMJJOPAMMMMMM + MMMKJJAAAMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 35 (broadsword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMMMMMMMMMM + MMANNMMMMMMMMMMM + MMAONNMMMMMMMMMM + MMMAONNMMMMMMMMM + MMMMAONNMMMMMMMM + MMMMMAONNMMMMMMM + MMMMMMAONNPOMMMM + MMMMMMMAOOKMMMMM + MMMMMMAPOAJKMMMM + MMMMMMAAAAAJKMMM + MMMMMMMMMMAAJMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 36 (runed broadsword / elven broadsword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMMMMMMMMMMMM + MMANNMMMMMMMMMMM + MMAONNMMMMMMMMMM + MMMAONNMMMMMMMMM + MMMMAONNMMMMMMMM + MMMMMAONNMMMMMMM + MMMMMMAONNPOMMMM + MMMMMMMAOOGMMMMM + MMMMMMAPOFFGMMMM + MMMMMMAAAAAFGMMM + MMMMMMMMMMAAJMMM + MMMMMMMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 37 (long sword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMOMM + MMMMMMMMMMMMOPAM + MMMMMMMMMMMOPAAM + MMMMMMMMMMNPAAMM + MMMMMMMMMNPAAMMM + MMMMMMMMNPAAMMMM + MMMMMMMNPAAMMMMM + MMMMMMNOAAMMMMMM + MMMMMNOAAMMMMMMM + MMOPNOAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAOPMMMMMMMMM + MNDAAMMMMMMMMMMM + MAAAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 38 (two-handed sword) +{ + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMNOM + MMMMMMMMMMMMNOAM + MMMMMMMMMMMNOAAM + MMMMMMMMMMNOAAMM + MMMMMMMMMNOAAMMM + MMMMMMMMNOAAMMMM + MMMMMMMNNAAMMMMM + MMMMOPNNAAMMMMMM + MMMMMKIIAMMMMMMM + MMMMKJAOPMMMMMMM + MMMKJAAAAMMMMMMM + MMKJAAMMMMMMMMMM + MNDAAMMMMMMMMMMM + MAAAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 39 (samurai sword / katana) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMONM + MMMMMMMMMMMMPNAM + MMMMMMMMMMMMNPAM + MMMMMMMMMMMOOAMM + MMMMMMMMMMONAAMM + MMMMMMMMMONPAMMM + MMMMMMMMNNPAMMMM + MMMMMOPNOAAMMMMM + MMMMMAOPAMMMMMMM + MMMMJKJOPAMMMMMM + MMMJKJAAMMMMMMMM + MMJJAAMMMMMMMMMM + MMAAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 40 (long samurai sword / tsurugi) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMNM + MMMMMMMMMMMMMNPM + MMMMMMMMMMMMNOAM + MMMMMMMMMMMOOAMM + MMMMMMMMMMONAMMM + MMMMMMMMMONAMMMM + MMMMMMMMNNAMMMMM + MMMMMOPNOAMMMMMM + MMMMMAOPAMMMMMMM + MMMMJKJOPAMMMMMM + MMMJKJAAMMMMMMMM + MMJJAAMMMMMMMMMM + MMAAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 41 (runed broadsword / runesword) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMAMM + MMMMMMMMMMMMAAPM + MMMMMMMMMMMAAPPM + MMMMMMMMMMAAPPMM + MMMMMMMMMAAPPMMM + MMMMMMMMAAPPMMMM + MMMMMMMAAPPMMMMM + MMMMMMAAPPMMMMMM + MMMMMAAPPMMMMMMM + MMOPAAPPMMMMMMMM + MMMNNPPMMMMMMMMM + MMNNPOPMMMMMMMMM + MNNPPMMMMMMMMMMM + MPPPMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 42 (vulgar polearm / partisan) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMPOM + MMMMMMMMMMMMPNPM + MMMMMMMMMMMONPAM + MMMMMMMMMMONOAAM + MMMMMMMMMMNOAAMM + MMMMMMMMMKJAAMMM + MMMMMMMMKJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MJJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM +} +# tile 43 (hilted polearm / ranseur) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMPNM + MMMMMMMMMMMMPNPM + MMMMMMMMMMMPNOAM + MMMMMMMPOPPNOAAM + MMMMMMMOAONOAMMM + MMMMMMMAAKOOPMMM + MMMMMMMMKJAAOAMM + MMMMMMMKJAAOPAMM + MMMMMMKJAAMAAMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MJJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM +} +# tile 44 (forked polearm / spetum) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMPNM + MMMMMMMMMMMMPNPM + MMMMMMMMPOMPNOAM + MMMMMMMMOPANOAAM + MMMMMMMMAONOAMMM + MMMMMMMMAKOOPOAM + MMMMMMMMKJAAOPAM + MMMMMMMKJAAAAAMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MJJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM +} +# tile 45 (single-edged polearm / glaive) +{ + MMMMMMMMMMMMMKAM + MMMMMMMMMMMMKNOA + MMMMMMMMMMMKNNPA + MMMMMMMMMMKNNPAM + MMMMMMMMMKNNPAMM + MMMMMMMMKNNPAMMM + MMMMMMMKJAAAMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MKJAAMMMMMMMMMMM + KJAAMMMMMMMMMMMM + JAAMMMMMMMMMMMMM + AAMMMMMMMMMMMMMM +} +# tile 46 (lance) +{ + PAMMMMMMMMMMMMMM + MPAMMMMMMMMMMMMM + MMBAMMMMMMMMMMMM + MMMOAMMMMMMMMMMM + MMMMOAMMMMMMMMMM + MMMMMNAMMMMMMMMM + MMMMMMNAMMMMMMMM + MMMMMMPNAMMMMMMM + MMMMMMMPNAMMMMMM + MMMMMMMMBNAMMMMM + MMMMMMMMMONAMMMM + MMMMMMMMMMNNOOOA + MMMMMMMMMMOONOAM + MMMMMMMMMMOOONOA + MMMMMMMMMMOAMONO + MMMMMMMMMMMMMMON +} +# tile 47 (angled poleaxe / halberd) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMOOOAMMM + MMMMMMMMMPPPKKMM + MMMMMMMMMPPKKOOM + MMMMMMMMMMKJPPPA + MMMMMMMMMKJAPPPA + MMMMMMMMKJAMAAAM + MMMMMMMKJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MKJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM + JAAMMMMMMMMMMMMM +} +# tile 48 (long poleaxe / bardiche) +{ + MMMMMMMMMMMMMNAM + MMMMMMMMMMMMPOPA + MMMMMMMMMMMPNOPA + MMMMMMMMMMPNNPAM + MMMMMMMMMNNOPAMM + MMMMMMMMKOAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MKJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM + JAAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 49 (pole cleaver / voulge) +{ + MMMMMMMMMMMMMNAM + MMMMMMMMMMMMNNOA + MMMMMMMMMMMNNNPA + MMMMMMMMMMNNNPAM + MMMMMMMMMNNOPAMM + MMMMMMMMKOAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MKJAAMMMMMMMMMMM + KJAAMMMMMMMMMMMM + JAAMMMMMMMMMMMMM + AAMMMMMMMMMMMMMM +} +# tile 50 (broad pick / dwarvish mattock) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMONOAMMMMMM + MMMMMMMMPNOAMMMM + MMMMMMMMMNONAMMM + MMMMMMMMKJAANAMM + MMMMMMMKJAMMMNAM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMKJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MKJAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 51 (pole sickle / fauchard) +{ + MMMMMMMMMMNNNMMM + MMMMMMMMMNPPPNMM + MMMMMMMMMNPAAPNM + MMMMMMMMMNPAMANA + MMMMMMMMMMNAMMAA + MMMMMMMMMNPAMMMM + MMMMMMMMKJAMMMMM + MMMMMMMKJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMKJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MKJAMMMMMMMMMMMM + KJAMMMMMMMMMMMMM + MAMMMMMMMMMMMMMM +} +# tile 52 (pruning hook / guisarme) +{ + MMMMMMMMMMMMMNAM + MMMMMMMMMMMMPOPA + MMMMMMPNNPMPNOPA + MMMMMMNAANPNNPAM + MMMMMMMAMNNOPAMM + MMMMMMMMKOAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MKJAAMMMMMMMMMMM + KJAAMMMMMMMMMMMM + JAAMMMMMMMMMMMMM + AAMMMMMMMMMMMMMM +} +# tile 53 (hooked polearm / bill-guisarme) +{ + MMMMMMMMMMMMMPNM + MMMMMMMMMMMMPNAM + MMMMMMMMMMMMPOPA + MMMMMMPNNPMPNOPA + MMMMMMNAANPNNPAM + MMMMMMMAMNNOPAMM + MMMMMMMMKOAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMONJAMMMMMMMMM + MMONOAAMMMMMMMMM + MPNOAAMMMMMMMMMM + PNPAAMMMMMMMMMMM + OPAAMMMMMMMMMMMM +} +# tile 54 (pronged polearm / lucern hammer) +{ + MMMMMMMMMNPMMMMM + MMMMMMMMMPNPMMMM + MMMMMMMMMAPPPKMM + MMMMMMMMMMAPKPPO + MMMMMMMMMMMKPPPO + MMMMMMMMMMKAPPPP + MMMMMMMMMKJAPPPA + MMMMMMMMKJAMAAAM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MJJAAMMMMMMMMMMM + JJAAMMMMMMMMMMMM +} +# tile 55 (beaked polearm / bec de corbin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPNMMMMMMMM + MMMMMPNPAMMMMMMM + MMMKPPPAAMMMMMMM + AOPPKPAAMMMMMMMM + MOPPPKAMMMMMMMMM + MPPPPAKAMMMMMMMM + MMPPPAMCAMMMMMMM + MMMAAMMMCAMMMMMM + MMMMMMMMMCAMMMMM + MMMMMMMMMMCAMMMM + MMMMMMMMMMMCAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 56 (mace) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMBABMMMM + MMMMMMMMBMOMPAMM + MMMMMMMMAOMBMAMM + MMMMMMMMBMBMPAMM + MMMMMMMMAPMPAAMM + MMMMMMMKJAAAAMMM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMKJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MMMAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 57 (morning star) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMBABMMM + MMMMMMMMMBMOMPAM + MMMMMMMMMAOMBMAM + MMMPPMMMMBMBMPAM + MMPAAPMMMAPMPAAM + MMPAAPMMPPAAAAMM + MMPAMAPPAAMMMMMM + MMPAMMAAMMMMMMMM + MMMKAMMMMMMMMMMM + MMMMKAMMMMMMMMMM + MMMMMKAMMMMMMMMM + MMMMMMKAMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 58 (war hammer) +{ + MMMMMMMMMMOMMMMM + MMMMMMMMMPPOAMMM + MMMMMMMMMAPPKKMM + MMMMMMMMMMAKKOOM + MMMMMMMMMMKJAPPO + MMMMMMMMMKJAAAPA + MMMMMMMMKJAMMAAM + MMMMMMMKJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMKJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MKJAMMMMMMMMMMMM + MJAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 59 (club) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMKKMMMM + MMMMMMMMJCKKAMMM + MMMMMMMKKKKKAMMM + MMMMMMJKKKKJAMMM + MMMMMMKKKCKAAMMM + MMMMMJCKKKAAMMMM + MMMMJKKKJAAMMMMM + MMMJKKJAAMMMMMMM + MMMKJAAMMMMMMMMM + MMKJAMMMMMMMMMMM + MKJAMMMMMMMMMMMM + MJAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 60 (rubber hose) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMFGFMMMMMM + MMMMMFGAAAGFMMMM + MMMMGAAMMMAAAMMM + MMMMGAMMMMMMMMMM + MMMMMGAMMMMMMMMM + MMMMMMFGAMMMMMMM + MMMMMMMMGAMMMMMM + MMMMMMMMFAMMMMMM + MMMMMMMGAMMMMMMM + MMMGGGFAMMMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 61 (staff / quarterstaff) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMJKM + MMMMMMMMMMMMJCJA + MMMMMMMMMMMJKJAM + MMMMMMMMMMJCJAAM + MMMMMMMMMJCJAAMM + MMMMMMMMJKJAAMMM + MMMMMMMJKJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMJJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMJJAAMMMMMMMMM + MMKJAAMMMMMMMMMM + MJJAAMMMMMMMMMMM + MMAMMMMMMMMMMMMM +} +# tile 62 (thonged club / aklys) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMKJJJMMMM + MMMMMMMMKJJAAMMM + MMMMMMMKJJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMMKAAMMMMMMMM + MMMMKAAMMMMMMMMM + MMMMMAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 63 (flail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMKJMM + MMMMMMMMMMMKJAMM + MMMMMMMMMMKJAMMM + MMMMMMMMMKJAMMMM + MMMMMMMMKJAMMMMM + MMMMMMMKJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMPAMMMMMMMMMM + MMMKJAMMMMMMMMMM + MMMKAMMMMMMMMMMM + MMKJAMMMMMMMMMMM + MJKAMMMMMMMMMMMM + MMJAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 64 (bullwhip) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKKKKKKMMM + MMMMMKKAAAAAAKAM + MMMMKAAMMMMMMKAM + MMMKAMMMMMMMKAMM + MMMKAMMMMMMMJAMM + MMMKAMMMMMMMMJAM + MMMMKKKKKKMMMMMM + MMMMMAAAAAKAMMMM + MPMMMMMMMMKAMMMM + MAPPMMMMMMKAMMMM + MMMAPKMMMKAMMMMM + MMMMMAKKKAMMMMMM + MMMMMMAAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 65 (bow) +{ + MMMMMMMMMMMMMMMM + MMMMMKMMMMMMMMMM + MMMMMKPMMMMMMMMM + MMMMMPKMMMMMMMMM + MMMMMPKJMMMMMMMM + MMMMMPMKJMMMMMMM + MMMMMPMMKMMMMMMM + MMMMMOMMLMMMMMMM + MMMMMPMMOMMMMMMM + MMMMMPMMJMMAAMMM + MMMMMPMKJMMAMMMM + MMMMMPKJMMAAMMMM + MMMMMPKMMAAMMMMM + MMMMMKPAAAMMMMMM + MMMMMKAAMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 66 (runed bow / elven bow) +{ + MMMMMKMMMMMMMMMM + MMMMMKPMMMMMMMMM + MMMMMPKMMMMMMMMM + MMMMMPKJMMMMMMMM + MMMMMPMKJMMMMMMM + MMMMMPMMKMMMMMMM + MMMMMPMMKJMMMMMM + MMMMMPMMJLMMMMMM + MMMMMOMMJJMMMMMM + MMMMMPMMJLMMMMMM + MMMMMPMMJMMAAMMM + MMMMMPMKJMMAMMMM + MMMMMPKJMMAAMMMM + MMMMMPKMMAAMMMMM + MMMMMKPAAAMMMMMM + MMMMMKAAMMMMMMMM +} +# tile 67 (crude bow / orcish bow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKPMMMMMMMMM + MMMMMPKMMMMMMMMM + MMMMMPKJMMMMMMMM + MMMMMPMKJMMMMMMM + MMMMMPMMKMMMMMMM + MMMMMOMMLMMMMMMM + MMMMMPMMOMMMAMMM + MMMMMPMMJMMAAMMM + MMMMMPMKJMMAMMMM + MMMMMPKJMMAAMMMM + MMMMMPKMAAAMMMMM + MMMMKKPAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 68 (long bow / yumi) +{ + MMMMMLMMMMMMMMMM + MMMMMLPMMMMMMMMM + MMMMMPLMMMMMMMMM + MMMMMPLOMMMMMMMM + MMMMMPMLOMMMMMMM + MMMMMPMMLMMMMMMM + MMMMMPMMLOMMMMMM + MMMMMPMMOLMMMMMM + MMMMMPMMOOMMMMMM + MMMMMPMMOLMMMMMM + MMMMMPMMOMMAAMMM + MMMMMPMLOMMAMMMM + MMMMMPLOMMAAMMMM + MMMMMPLMMAAMMMMM + MMMMMLPAAAMMMMMM + MMMMMLAAMMMMMMMM +} +# tile 69 (sling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMJJJJMMMMMM + MMMMMJAAAAJKMMMM + MMMMJAMMMMMKKMMM + MMMMMMMMMMJAKAMM + MMMMMMJJKKAKKAMM + MMMMJJAAAKKJJAMM + MMMJAAMMMAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 70 (crossbow) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCJJMMMMMM + MMMMMMMDJJMMMMMM + MMMMMDKJJJKMMMMM + MMMDKJJJJJJJKMMM + MMDJMMMDJJMMJKMM + MMMMPMMCJJMPMMMM + MMMMMPMCJJPMMMMM + MMMMMMPCJPAAAAMM + MMMMMMMPPAMAAAAM + MMMMMMMCJJAAMMAM + MMMMMMMOAOAMMMMM + MMMMMMMMOAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 71 (leather hat / elven leather helm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMJKKJMMMMMMM + MMMMJCKKKJMMMMMM + MMMJCKKJJJAMMMMM + MMMJKKJJJJAMMMMM + MMMJAJJAAJAMMMMM + MMMJAJJAMJAMMMMM + MMMJAMAMJAMMMMMM + MMMMJAMJAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 72 (iron skull cap / orcish helm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPBBPMMMMMMM + MMMMPNBBBPAMMMMM + MMMPNBBPPPAMMMMM + MMMPBBPPPPAMMMMM + MMMJAPPAAJAMMMMM + MMMJAPPAMJAMMMMM + MMMJAMAMJAMMMMMM + MMMMJAMJAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 73 (hard hat / dwarvish iron helm) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPMMMMMMMM + MMMMPBBPPAMMMMMM + MMMPBNPPPPAMMMMM + MMMPNPPPPPAMMMMM + MMMPPPPPPPAMMMMM + MMMPAPPAAPAMMMMM + MMMJAPPAMJAMMMMM + MMMJAMAMJAMMMMMM + MMMMJAMJAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 74 (fedora) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMAAAAAAMMMMM + MMMAAMAAAAAMMMMM + MMMAAAMAAAMAMMMM + MMMMAAAMMMAAAMMM + MMMMKMAAAAAAMMMM + MMMKAMMMKAMMMMMM + MMMMKAMMKAMMMMMM + MMMMMMMKAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 75 (conical hat / cornuthaum) +{ + MMMMMMMMMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMMBMMMMMMMM + MMMMMMEBEMMMMMMM + MMMMMMEBEMMMMAMM + MMMMMMPBEMMMMAMM + MMMMMEBBEEMMAAMM + MMMMMEBBPEMAAAMM + MMMMMPNBPEAAAMMM + MMMMEBNBBEEAAMMM + MMMMENNBBPEAAMMM + MMMEBBBBPEAEAMMM + MMMEEMMAMAEEMMMM + MMMMEEEEEEEMMMMM + MMMMMMMMMMMMMMMM +} +# tile 76 (conical hat / dunce cap) +{ + MMMMMMMMMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMMEMMMMMMMM + MMMMMMMBMMMMMMMM + MMMMMMEBEMMMMMMM + MMMMMMEBEMMMMAMM + MMMMMMPBEMMMMAMM + MMMMMEBBEEMMAAMM + MMMMMEBBPEMAAAMM + MMMMMPNBPEAAAMMM + MMMMEBNBBEEAAMMM + MMMMENNBBPEAAMMM + MMMEBBBBPEAEAMMM + MMMEEMMAMAEEMMMM + MMMMEEEEEEEMMMMM + MMMMMMMMMMMMMMMM +} +# tile 77 (dented pot) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMJJMM + MMMMMMMMMMMJJMMM + MMMMMMMMMMJJMMMM + MMMMMMMMMJJMMMAA + MMMMMBPPPMJMMAAM + MMMMBAAAMMPMAAMM + MMMMBPAMMPMAAMMM + MMMMBBPPPMMAAMMM + MMMMBPPPMMMAAMMM + MMMMMBBPPPAAAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 78 (plumed helmet / helmet) +{ + MMMMMMMDIDMMMMMM + MMMMMMDIBIMMMMMM + MMMMMMIBIBMMMMMM + MMMMMMDIBMMMMMMM + MMMMMMMIMMMMMMMM + MMMMMMPIPMMMMMMM + MMMMMPNIPPAMMMMM + MMMMPNBPPPPAMMMM + MMMMPBPPPPPAMMMM + MMMMPPPPPPPAMMMM + MMMMPAPPAAPAMMMM + MMMMJAPPAMJAMMMM + MMMMMMMAAMMAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 79 (etched helmet / helm of brilliance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPPPMMMMMMM + MMMMMPNBPPAMMMMM + MMMMMNBPPPMAMMMM + MMMMPMPMPMPAMMMM + MMMMPPPPPPPAMMMM + MMMMPAPPAAPAMMMM + MMMMJAPPAMJAMMMM + MMMMMMMAAMMAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 80 (crested helmet / helm of opposite alignment) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMHHHMMMMMMM + MMMMMHHHMMMMMMMM + MMMMMHHMMPAMMMMM + MMMMPPHMMPPAMMMM + MMMMPPPMPPPAMMMM + MMMMPPPPPPPAMMMM + MMMMPAPPAAPAMMMM + MMMMJAPPAMJAMMMM + MMMMMMMAAMMAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 81 (visored helmet / helm of telepathy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPPPMMMMMMM + MMMMMPNNPPAMMMMM + MMMMPNNPPPPAMMMM + MMMMPBBBBBPAMMMM + MMMMPPPPPPPAMMMM + MMMMPAAAAAPAMMMM + MMMMJBPPPPJAMMMM + MMMMMMPPAAAAMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 82 (gray dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNMMPMMMPMOAOA + MNMNMPMMMPMMOAOA + MMANPMMMPMMMOAMA + MMMNMMMPMMMPOAMM + MMMNMMPMMMPMOAMM + MMMMNPMMMPMOAAMM + MMMNMNMMPMOAOAMM + MMNNNANPMOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 83 (silver dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOBOOMOM + MNMNOBNOOBNOBAOA + MNMNBNOOBNOBBAOA + MMANNOOBNOBBBAMA + MMMNOOBNOBBNBAMM + MMMNOBNOBBNBBAMM + MMMMNNOBBNBBAAMM + MMMNMNBBNBBAOAMM + MMNNNANNBBAOOOAM + MNNNBAMNBAAOOOOA + MMNBAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 84 (shimmering dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMBMBBBOOOMOMMM + MBMBBBBBOOOOOMOM + MBMBEEFFGGHHOAOA + MBMBEFFGGHHCOACA + MMABFFGGHHCDOAMA + MMMBFGGHHCDDOAMM + MMMBGGHHCDDKOAMM + MMMMBHHCDDKOAAMM + MMMBMBCDDKOAOAMM + MMBBBABDKOAOOCAM + MBBBOAMBBAAOOOCA + MMBOAAMMMMMMKCAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 85 (red dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNDDIDDDIDOAOA + MNMNDIDDDIDDOAOA + MMANIDDDIDDDOAMA + MMMNDDDIDDDIOAMM + MMMNDDIDDDIDOAMM + MMMMNIDDDIDOAAMM + MMMNMNDDIDOAOAMM + MMNNNANIDOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 86 (white dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNOONOOONOOAOA + MNMNONOOONOOOAOA + MMANNOOONOOOOAMA + MMMNOOONOOONOAMM + MMMNOONOOONOOAMM + MMMMNNOOONOOAAMM + MMMNMNOONOOAOAMM + MMNNNANNOOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 87 (orange dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNCCLCCCLCOAOA + MNMNCLCCCLCCOAOA + MMANLCCCLCCCOAMA + MMMNCCCLCCCLOAMM + MMMNCCLCCCLCOAMM + MMMMNLCCCLCOAAMM + MMMNMNCCLCOAOAMM + MMNNNANLCOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 88 (black dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNAAMAAAMAOAOA + MNMNAMAAAMAAOAOA + MMANMAAAMAAAOAMA + MMMNAAAMAAAMOAMM + MMMNAAMAAAMAOAMM + MMMMNMAAAMAOAAMM + MMMNMNAAMAOAOAMM + MMNNNANMAOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 89 (blue dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNEEBEEEBEOAOA + MNMNEBEEEBEEOAOA + MMANBEEEBEEEOAMA + MMMNEEEBEEEBOAMM + MMMNEEBEEEBEOAMM + MMMMNBEEEBEOAAMM + MMMNMNEEBEOAOAMM + MMNNNANBEOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 90 (green dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNFFGFFFGFOAOA + MNMNFGFFFGFFOAOA + MMANGFFFGFFFOAMA + MMMNFFFGFFFGOAMM + MMMNFFGFFFGFOAMM + MMMMNGFFFGFOAAMM + MMMNMNFFGFOAOAMM + MMNNNANGFOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 91 (yellow dragon scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNHHNHHHNHOAOA + MNMNHNHHHNHHOAOA + MMANNHHHNHHHOAMA + MMMNHHHNHHHNOAMM + MMMNHHNHHHNHOAMM + MMMMNNHHHNHOAAMM + MMMNMNHHNHOAOAMM + MMNNNANNHOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 92 (gray dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPMMMMMMM + MMMMMMMPPPMMMMMM + MMMMPMPPPPAMMMMM + MMMPPPMPPAAPMMMM + MMPPPPAPAAPPAMMM + MMMPPAAPPAPAAMMM + MMMMAAPPAAAAMMMM + MMMMMMMAAPAMMMMM + MMMMMMMPPAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 93 (silver dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMNMMMMMMM + MMMMMMMNNNMMMMMM + MMMMNMNNNNAMMMMM + MMMNNNMNNAANMMMM + MMNNNNANAANNAMMM + MMMNNAANNANAAMMM + MMMMAANNAAAAMMMM + MMMMMMMAANAMMMMM + MMMMMMMNNAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 94 (shimmering dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPMMMMMMM + MMMMMMMPPPMMMMMM + MMMMEMPPFPAMMMMM + MMMEBBMFFAAHMMMM + MMEBBPAFAAHDAMMM + MMMEBAAGGADAAMMM + MMMMAAGGAAAAMMMM + MMMMMMMAADAMMMMM + MMMMMMMGHAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 95 (red dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMDMMMMMMM + MMMMMMMDDDMMMMMM + MMMMDMDDDDAMMMMM + MMMDDDMDDAADMMMM + MMDDDDADAADDAMMM + MMMDDAADDADAAMMM + MMMMAADDAAAAMMMM + MMMMMMMAADAMMMMM + MMMMMMMDDAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 96 (white dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMOMMMMMMM + MMMMMMMOOOMMMMMM + MMMMOMOOOOAMMMMM + MMMOOOMOOAAOMMMM + MMOOOOAOAAOOAMMM + MMMOOAAOOAOAAMMM + MMMMAAOOAAAAMMMM + MMMMMMMAAOAMMMMM + MMMMMMMOOAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 97 (orange dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMCMMMMMMM + MMMMMMMCCCMMMMMM + MMMMCMCCCCAMMMMM + MMMCCCMCCAACMMMM + MMCCCCACAACCAMMM + MMMCCAACCACAAMMM + MMMMAACCAAAAMMMM + MMMMMMMAACAMMMMM + MMMMMMMCCAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 98 (black dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMAMMMMMMM + MMMMMMMAAAMMMMMM + MMMMAMAAAAPMMMMM + MMMAAAMAAPAAMMMM + MMAAAAPAPAAAPMMM + MMMAAPAAAPAPPMMM + MMMMPAAAPAPPMMMM + MMMMMMMPAAPMMMMM + MMMMMMMAAPPMMMMM + MMMMMMMMPPMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 99 (blue dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMEMMMMMMM + MMMMMMMEEEMMMMMM + MMMMEMEEEEAMMMMM + MMMEEEMEEAAEMMMM + MMEEEEAEAAEEAMMM + MMMEEAAEEAEAAMMM + MMMMAAEEAAAAMMMM + MMMMMMMAAEAMMMMM + MMMMMMMEEAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 100 (green dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMFMMMMMMM + MMMMMMMFFFMMMMMM + MMMMFMFFFFAMMMMM + MMMFFFMFFAAFMMMM + MMFFFFAFAAFFAMMM + MMMFFAAFFAFAAMMM + MMMMAAFFAAAAMMMM + MMMMMMMAAFAMMMMM + MMMMMMMFFAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 101 (yellow dragon scales) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMHMMMMMMM + MMMMMMMHHHMMMMMM + MMMMHMHHHHAMMMMM + MMMHHHMHHAAHMMMM + MMHHHHAHAAHHAMMM + MMMHHAAHHAHAAMMM + MMMMAAHHAAAAMMMM + MMMMMMMAAHAMMMMM + MMMMMMMHHAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 102 (plate mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNNNNNOOOOOAOA + MNMNNNNONOOOOAOA + MMANNNOONNOOOAMA + MMMNNNOONNOOOAMM + MMMNNNNONOOOOAMM + MMMMNNNNOOOOAAMM + MMMNMNNNOOOAOAMM + MMNNNANNOOAOOOAM + MNNNOAMNOAAOOOOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 103 (crystal plate mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNBBBMBMMM + MNMNNNNNBBBBBMBM + MNMNNNNNBBBBBABA + MNMNNNNBNBBBBABA + MMANNNBBNNBBBAMA + MMMNNNBBNNBBBAMM + MMMNNNNBNBBBBAMM + MMMMNNNNBBBBAAMM + MMMNMNNNBBBABAMM + MMNNNANNBBABBBAM + MNNNBAMNBAABBBBA + MMNBAAMMMMMMBBAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 104 (bronze plate mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMLMLLLCCCMCMMM + MLMLLLLLCCCCCMCM + MLMLLLLLCCCCCACA + MLMLLLLCLCCCCACA + MMALLLCCLLCCCAMA + MMMLLLCCLLCCCAMM + MMMLLLLCLCCCCAMM + MMMMLLLLCCCCAAMM + MMMLMLLLCCCACAMM + MMLLLALLCCACCCAM + MLLLCAMLCAACCCCA + MMLCAAMMMMMMCCAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 105 (splint mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MJMNPPPPMMMMOAJA + MNMNKKKKJJJJOAOA + MMANPPPPMMMMOAMA + MMMNKKKKJJJJOAMM + MMMNPPPPMMMMOAMM + MMMMNKKKJJJOAAMM + MMMNMNPPMMOAOAMM + MMNPNANKJOAOPOAM + MNPJOAMNOAAOJPOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 106 (banded mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MPMNPNPPPPOPOAPA + MNMNMMMMMMMMOAOA + MMANPNPPPPOPOAMA + MMMNMMMMMMMMOAMM + MMMNPNPPPPOPOAMM + MMMMNMMMMMMOAAMM + MMMNMNPNOPOAOAMM + MMNPNANMMOAOPOAM + MNPMOAMNOAAOMPOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 107 (dwarvish mithril-coat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNNNNNOOOOOMMM + MNMNANANAPAPOMOM + MNMNNANAPAPAOAOA + MNMNANANAPAPOAOA + MMANNANAPAPAOAMA + MMMNANANAPAPOAMM + MMMNNANAPAPAOAMM + MMMMNNANAPAOAAMM + MMMNMNNAPAOAOAMM + MMNPNANNAOAOMOAM + MNPNOAMNOAAOOMOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 108 (elven mithril-coat) +{ + MMMMMMMMMMMMMMMM + MMMNMNMNOMOMOMMM + MMMNNNNNOOOOOMMM + MNMNANANAOAOOMOM + MNMONANAPAPAOAOA + MNMNANANAPAPOAOA + MMAONANAPAPAOAMA + MMMNANANAPAPOAMM + MMMONANAPAPAOAMM + MMMMONANAPAOAAMM + MMMNMONAPAOAOAMM + MMNPNAONAOAOMOAM + MNPNOAMOOAAOOMOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 109 (chain mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MNMNPAPAMAMAOAOA + MNMNAPAPAMAMOAOA + MMANPAPAMAMAOAMA + MMMNAPAPAMAMOAMM + MMMNPAPAMAMAOAMM + MMMMNPAPAMAOAAMM + MMMNMNPAMAOAOAMM + MMNPNANPAOAOMOAM + MNPNOAMNOAAOOMOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 110 (crude chain mail / orcish chain mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKMKKKJJJMJMMM + MKMKKKKKJJJJJMJM + MKMKPAPAMAMAJAJA + MKMKAPAPAMAMJAJA + MMAKPAPAMAMAJAMA + MMMKAPAPAMAMJAMM + MMMKPAPAMAMAJAMM + MMMMKPAPAMAJAAMM + MMMKMKPAMAJAJAMM + MMKKKAKPAJAJJJAM + MKKKJAMKJAAJJJJA + MMKJAAMMMMMMJJAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 111 (scale mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MPMNPAPAAMAMOAMA + MNMNAPPPMMMAOAOA + MMANPAPAAMAMOAMA + MMMNPPAPMAMMOAMM + MMMNPAPAAMAMOAMM + MMMMNPPPMMMOAAMM + MMMNMNPAAMOAOAMM + MMNPNANPMOAOMOAM + MNPPOAMNOAAOMMOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 112 (studded leather armor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MKMNKKKKJJJJOAKA + MKMNKNKNOJOJOAKA + MMANKKKKJJJJOAMA + MMMNKNKNOJOJOAMM + MMMNKKKKJJJJOAMM + MMMMNKNKJOJOAAMM + MMMNMNKKJJOAOAMM + MMNKKANKJOAOKKAM + MNKKJAMNOAAOKKKA + MMKJAAMMMMMMKKAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 113 (ring mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MNMNNNNNOOOOOMOM + MPMNPAAPMAAMOAMA + MNMNABPAAMPAOAOA + MMANAPBAAPMAOAMA + MMMNPAAPMAAMOAMM + MMMNAPBAAPMAOAMM + MMMMNBPAAMPOAAMM + MMMNMNAPMAOAOAMM + MMNPNANAAOAOMOAM + MNPBOAMNOAAOPMOA + MMNOAAMMMMMMOOAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 114 (crude ring mail / orcish ring mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKMKKKJJJMJMMM + MKMKKKKKJJJJJMJM + MBMKPAAPMAAMJAPA + MKMKABPAAMPAJAJA + MMAKAPBAAPMAJAMA + MMMKPAAPMAAMJAMM + MMMKAPBAAPMAJAMM + MMMMKBPAAMPJAAMM + MMMKMKAPMAJAJAMM + MMKPKAKAAJAJMJAM + MKPBJAMKJAAJPMJA + MMKJAAMMMMMMJJAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 115 (leather armor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MKMNNNNNOOOOOMJM + MKMNKKKKJJJJOAJA + MKMNKKKJKJJJOAJA + MMANKKJJKKJJOAMA + MMMNKKJJKKJJOAMM + MMMNKKKJKJJJOAMM + MMMMNKKKJJJOAAMM + MMMKMNKKJJOAJAMM + MMKKKANKJOAJJJAM + MKKKJAMNOAAJJJJA + MMKJAAMMMMMMJJAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 116 (leather jacket) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKAMJAMMMMM + MMMMKKKKJJJJAMMM + MMMKKKKKJLJJJAMM + MMKKKKKKJJJJJJAM + MMMKAKKKJLJAJAAM + MMMMMKKKJJJAMAMM + MMMMMKKKJLJAMMMM + MMMMMKKKJJJAMMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 117 (Hawaiian shirt) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCMMMMMCMMMMM + MMMGCGGGGGCHMMMM + MMGGHCGGGCHHHMMM + MHHHJHCCCHJHHHMM + MMHHJHHCHHJHHAAM + MMMMGHHCGGHAAAMM + MMMMHHHCGGHAMMMM + MMMMGGHCHHHAMMMM + MMMMGGHCHGGAMMMM + MMMMHHHCHHHAMMMM + MMMMMAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 118 (T-shirt) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPMMMMMPMMMMM + MMMNPOOOOOPNMMMM + MMNNNPOOOPNNNMMM + MNNNONPPPNONNNMM + MMNNONNNNNONNAAM + MMMMNDODODNAAAMM + MMMMNNNNNNNAMMMM + MMMMNDODODNAMMMM + MMMMNNNNNNNAMMMM + MMMMNNNNNNNAMMMM + MMMMMAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 119 (mummy wrapping) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMOMM + MMMMMMMNMMNMMOMM + MMMMOMONOMNNANMM + MMMMMNONOMANNOMM + OMMMONOOODNNOAAM + MOOODNNONNNAOOAM + MMAMNNOOAOAAOAAM + MMMOODOOAAOOOAMM + MMNMAANMNAMOOMMM + MMMMMMMMMMMMMMMM +} +# tile 120 (faded pall / elven cloak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOFFMMFOMMMMM + MMMMMOFFFOAAMMMM + MMMMMMOFOAAMMMMM + MMMMMOGAGFAMMMMM + MMMMOGOAOFFAMMMM + MMMOGOAAGFFFAMMM + MMOGOGAAOFFFFAMM + MMOOGAAFFOFFFAMM + MMOGOAAFFGFFFAMM + MMMOGAFFFFGFAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 121 (coarse mantelet / orcish cloak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCJKMKKCMMMMM + MMMMMCJKKCAAMMMM + MMMMMMCKCAAMMMMM + MMMMMCCACKAMMMMM + MMMMCCCACKKAMMMM + MMMCCCAACKKKAMMM + MMCCCCAACKKKKAMM + MMCCCAAJJCKKKAMM + MMCCCAAJJCKKKAMM + MMMCCAJJJJCKAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 122 (hooded cloak / dwarvish cloak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBBPMMMMMMM + MMMMMBBBBPMMMMMM + MMMMBPAAAPPMMMMM + MMMMMBPAPPAAMMMM + MMMMMMBPPAAMMMMM + MMMMMBBABPAMMMMM + MMMMBBBABPPAMMMM + MMMBBBAABPPPAMMM + MMBBBBAMMBPPPAMM + MMMBBAMMMMBPAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 123 (slippery cloak / oilskin cloak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJAAMAAJMMMMM + MMMMMJAAAJAAMMMM + MMMMMMJAJAAMMMMM + MMMMMJJAJAAMMMMM + MMMMJJJAJAAAMMMM + MMMJJJAAJAAAAMMM + MMJJJJAAJAAAAAMM + MMJJJAAAAJAAAAMM + MMJJJAAAAJAAAAMM + MMMJJAAAAAJAAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 124 (robe) +{ + MMMMMMMMMMMMMMMM + MMMMMMCCCMMMMMMM + MMMMMCAAAKAMMMMM + MMMMCCAJJKKAMMMM + MMMMMCAJJKAAMMMM + MMMMMCCJKKAAMMMM + MMMMCCCAKKKAMMMM + MMMMCCCCAKKAAMMM + MMMCCCCCCAKKAMMM + MMMCCCCCCAKKAMMM + MMMCCCCCCCAKAMMM + MMMCCCCCCCAKAMMM + MMMCCCCCCCAKAMMM + MMMCCCCCCCAKAMMM + MMMACCCCCCAAAMMM + MMMMAAAAAAAAMMMM +} +# tile 125 (apron / alchemy smock) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMNOMMNOMMMMM + MMMMMNAOMNAOMMMM + MMMMMNAOMNAOAMMM + MMMMMNNNNNAOAMMM + MMMMMNNNNNOOAMMM + MMMMMNNNNNOOAMMM + MMMMONNNNNOAAMMM + MMMMONNNNNNAAMMM + MMMMNNNNNNNAAMMM + MMMMNNNNNNNAAMMM + MMMMNNNNNNNAAMMM + MMMMNNNNNNNAAMMM + MMMMMNNNNNAAMMMM + MMMMMMAAAAAMMMMM +} +# tile 126 (leather cloak) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKJJMJJKMMMMM + MMMMMKJJJKAAMMMM + MMMMMMKJKAAMMMMM + MMMMMKKAKJAMMMMM + MMMMKKKAKJJAMMMM + MMMKKKAAKJJJAMMM + MMKKKKAAKJJJJAMM + MMKKKAAJJKJJJAMM + MMKKKAAJJKJJJAMM + MMMKKAJJJJKJAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 127 (tattered cape / cloak of protection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMPOMMMMM + MMMOOOPPPOAAMMMM + MMMMMMOPOAAMMMMM + MMMMMOOAOPAMMMMM + MMMMOOOAOPPAMMMM + MMMOOOAAOPPPAMMM + MMOOOOAAOPPPPAMM + MMOOOAAPPOPPAMMM + MMMOOAAPPOPAMMMM + MMOOOAMMAPOPAMMM + MMMAAMMMMAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 128 (opera cloak / cloak of invisibility) +{ + MMMMMMMMMMMMMMMM + MMMMMMNNNMMMMMMM + MMMMMNAAAOMMMMMM + MMMMNNAAAOOAMMMM + MMMMMNAAAOAMMMMM + MMMMMNNAOOAAMMMM + MMMMNNNAOOOAAMMM + MMMNNNNNAOOOAAMM + MMNNNNNNNAOOOAAM + MMNNNNNNNAOOOOAM + MNNNNNNNNNAOOOAM + MNNNNNNNNNAOOOAM + MNNNNNNNNNNAOOAM + MNNNNNNNNNNAOOAM + MMAANNNNNNAOOAAM + MMMMAAAAAAAAAAMM +} +# tile 129 (ornamental cope / cloak of magic resistance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAMMAAMMMMM + MMMMMAAAAAPPMMMM + MMMMMMAAAPPMMMMM + MMMMMAAAAAPMMMMM + MMMMAAAAAAAPMMMM + MMMAAAAAAAAAPMMM + MMMAAAAAAAAAPPMM + MMAAAAAAAAAAAPMM + MMAAAAAAAAAAAPMM + MMAAAAAAAAAAAPMM + MMAAAAAAAAAAAPMM + MMAAAAAAAAAAAPMM + MMMPPPPPPPPPPPMM +} +# tile 130 (piece of cloth / cloak of displacement) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPMMMMMMM + MMMMMMMPPPAMMMMM + MMMMMMPPPBPAAMMM + MMMMMPBPBPPPAAMM + MMMMPPPBPPPPBAAM + MMMPPPBPPBPBPPAA + MMPBPBPPPPBPPPPA + MPPPBPPPPBPBPPPA + MPPBPBPPBPPPBPAA + MMBPPPPBPPPPPAAM + MMMPPPBPBPPPAAMM + MMMMPBPPPBPAAMMM + MMMMMPPPPPAAMMMM + MMMMMMMPPAAMMMMM +} +# tile 131 (small shield) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMCMCJJMJMMMMMM + MMMCCKKJJJAMMMMM + MMMCKKKJJJAMMMMM + MMMCKKJJJJAMMMMM + MMMCKKJJJJAMMMMM + MMMMCKKJJAAMMMMM + MMMMMCKJAAMMMMMM + MMMMMMCAAMMMMMMM + MMMMMMMAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 132 (blue and green shield / elven shield) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNNMNMMMMM + MMMNNNGFNNNAMMMM + MMMNGGGFFFNAMMMM + MMMNGGFFFFNAMMMM + MMMNGGFFFFNAMMMM + MMMNGGFFFFNAMMMM + MMMMNGGFFNAAMMMM + MMMMMNGFNAAMMMMM + MMMMMMNNAAMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 133 (white-handed shield / Uruk-hai shield) +{ + MMMMMMMMMMMMMMMM + MMMKMKKKJJJMJMMM + MMMKKKKKJJJJJMMM + MMMKPPPPMMMMJAMM + MMMKPPPNOOMMJAMM + MMMKPPPNOOOMJAMM + MMMKPNPNOOOMJAMM + MMMKPNNNOOOMJAMM + MMMKPPNNOOMMJAMM + MMMKPPPNOMMMJAMM + MMMMKPPNOMMJAAMM + MMMMMKPPMMJAAMMM + MMMMMMKPMJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 134 (red-eyed shield / orcish shield) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMKMKKKJJJMJMMM + MMMKKKKKJJJJJMMM + MMMKPPPPMMMMJAMM + MMMKPPDDDDMMJAMM + MMMKPDDAADDMJAMM + MMMKPDDAADDMJAMM + MMMKPPDDDDMMJAMM + MMMMKPPPMMMJAAMM + MMMMMKPPMMJAAMMM + MMMMMMKPMJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 135 (large shield) +{ + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MMMNNNNNOOOOOMMM + MMMNPPPPPMMMOAMM + MMMNPPPMMMMMOAMM + MMMNPPPMMMMMOAMM + MMMNPPPPPMMMOAMM + MMMNPPPMMMMMOAMM + MMMNPPPMMMMMOAMM + MMMNPPPPPMMMOAMM + MMMMNPPMMMMOAAMM + MMMMMNPMMMOAAMMM + MMMMMMNPPOAAMMMM + MMMMMMMNOAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 136 (large round shield / dwarvish roundshield) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBBBPMMMMMMM + MMMMBKKKKPMMMMMM + MMMBKKKKKJPMMMMM + MMMBKKKJJJPAMMMM + MMMBKKJJJJPAMMMM + MMMBKKJJJJPAMMMM + MMMMPJJJJPAAMMMM + MMMMMPPPPAAMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 137 (polished silver shield / shield of reflection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNMNNNOOOMOMMM + MMMNNNNNOOOOOMMM + MMMNPPPPPPPPOAMM + MMMNPNNPPPPPOAMM + MMMNPNNPPPPPOAMM + MMMNPPPPPPPPOAMM + MMMNPPPPPPPPOAMM + MMMMNPPPPPPOAAMM + MMMMMNPPPPOAAMMM + MMMMMMNPPOAAMMMM + MMMMMMMNOAAMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 138 (old gloves / leather gloves) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKJMMMMMMM + MMMMKJMKJMKJMMMM + MMMMKJAJJAKJMMMM + MMKJMJJAJJAJJMMM + MMKJAJJAJJAJJAMM + MMMJJAJKJJKJJAMM + MMMMJKJJJJJJJAMM + MMMKAJJJJJJJJAMM + MMMKJJJJJJJJJJAM + MMMMJJJJJJJJJJAM + MMMMMAAAJJJJJAAM + MMMMMMMMAJJJAAMM + MMMMMMMMMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 139 (padded gloves / gauntlets of fumbling) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKJMMMMMMM + MMMMKJMKJMKJMMMM + MMMMKJAJJAKJMMMM + MMKJMKJAKJAKJMMM + MMKJAJKAKJAKJAMM + MMMKJAJKKJKKJAMM + MMMMKKJJKJJKJAMM + MMMKAJKKKKKKJAMM + MMMKKKKKKKKKJJAM + MMMMJJJJJKKKKJAM + MMMMMAAAJJKKKAAM + MMMMMMMMAJJKAAMM + MMMMMMMMMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 140 (riding gloves / gauntlets of power) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKJJAMMMMMMM + MMMKJJJJJAMMMMMM + MMKJJJJJJJAMMMMM + MMKJJJJJJJAMMMMM + MMMJJJJJJJJAMMMM + MMMMJKJJJJJJAMMM + MMMKAJJJJJJJAMMM + MMMKJJJJJJJJJAMM + MMMMJJJJJJJJJJAM + MMMMMAAAJJJJJAAM + MMMMMMMMAJJJAAMM + MMMMMMMMMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 141 (fencing gloves / gauntlets of dexterity) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMNOMMMMMMM + MMMMNOMNOMNOMMMM + MMMMNOAOOANOMMMM + MMNOMOOAOOAOOMMM + MMNOAOOAOOAOOAMM + MMMOOAONOONOOAMM + MMMMONOOOOOOOAMM + MMMNAOOOOOOOOAMM + MMMNOOOOOOOOOOAM + MMMMOOOOOOOOOOAM + MMMMMAAAOOOOOAAM + MMMMMMMMAOOOAAMM + MMMMMMMMMAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 142 (walking shoes / low boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMKJKKJMMM + MMMCKLKLKJJKJAMM + MMCKKLKLKJKKJAMM + MMKKKKKKKKKKJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 143 (hard shoes / iron shoes) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPPBBPMMM + MMMMMMMMPNPPPAMM + MMMMMMMMPPPPMAMM + MMMOPNPNMPPPMAMM + MMOPPPPNPMMMMAMM + MMPPPPPPPPPPMAMM + MMMPMMMMMMMMMAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 144 (jackboots / high boots) +{ + MMMMMMMCCKKKKMMM + MMMMMMCKAAAAJJMM + MMMMMMKKKKKKKJAM + MMMMMMMJKKKKKJAM + MMMMMMMKJKKKKJAM + MMMMMMMMJKKKKAMM + MMMMMMMMJKKKKAMM + MMMMMMMMJKKKKAMM + MMMMMMMMKJKKJAMM + MMMCKLKLKJJKJAMM + MMCKKLKLKJKKJAMM + MMKKKKKKKKKKJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 145 (combat boots / speed boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMGGFFFFMMM + MMMMMMGFAAAAMMMM + MMMMMMFFFFFFFMAM + MMMMMMMMFFFFFMAM + MMMMMMMKMFFFFMAM + MMMMMMMMMFFFFAMM + MMMMMMMMFMFFMAMM + MMMGFLFLFMMFMAMM + MMGFFLFLFMFFMAMM + MMFFFFFFFFFFMAMM + MMJMMMMMMMMMMAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 146 (jungle boots / water walking boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMGGGGGGMMM + MMMMMMGFAAAAJJMM + MMMMMMFFFFFFFJAM + MMMMMMMGFFFFFJAM + MMMMMMMKGFFFFJAM + MMMMMMMMGFFFFAMM + MMMMMMMMFGFFJAMM + MMMGFLFLFGGFJAMM + MMGFFLFLFGFFJAMM + MMFFFFFFFFFFJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 147 (hiking boots / jumping boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMCCKKKJAM + MMMMMMMCAAAAKJAM + MMMMMMMKJKKKKJAM + MMMMMMMMJKKKKAMM + MMMMMMMMKJKKJAMM + MMMCKLKLKJJKJAMM + MMCKKLKLKJKKJAMM + MMKKKKKKKKKKJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 148 (mud boots / elven boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCCKKKKMMM + MMMMMMCKAAAAJJMM + MMMMMMKKKKKKKJAM + MMMMMMMJKKKKKJAM + MMMMMMMKJKKKKJAM + MMMMMMMMJKKKKAMM + MMMMMMMMKJKKJAMM + MMMCKLKLKJJKJAMM + MMCKKLKLKJKKJAMM + MMKKKKKKKKKKJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 149 (buckled boots / kicking boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMCCKKKKMMM + MMMMMMCKAAAAJJMM + MMMMMMKKKKKKKJAM + MMMMMMMJKKKKKJAM + MMMMMMMCJKKKKJAM + MMMMMMHOOKKKCAMM + MMMCKHKKHCCCCAMM + MMCKKLOHCCCCJAMM + MCKKKKKKKJKKJAMM + MCKKKKKKKKKKJAMM + MMJJJJJJJJJJJAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 150 (riding boots / fumble boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMLLLLLLMMM + MMMMMMLAAAAAALMM + MMMMMMLAAAAAALPM + MMMMMMMLLLLLLAPM + MMMMMMMAAAAAAAPM + MMMMMMMMAAAAAPMM + MMMMMMMMAAAAAPMM + MMMAALALAAAAAPMM + MMAAALALAAAAAPMM + MMAAAAAAAAAAAPMM + MMMAAAAAAAAAAPMM + MMMPPPPPPPPPPPMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 151 (snow boots / levitation boots) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMNNLLLLMMM + MMMMMMNOAAAALLMM + MMMMMMOOOOOOOLAM + MMMMMMMLOOOOOLAM + MMMMMMMLLOOOOLAM + MMMMMMMMLOOOOAMM + MMMMMMMMOLOOLAMM + MMMNOAOAOLLOLAMM + MMNOOAOAOLOOOLAM + MOOOOOOOOOOOOOAM + MOOOOOOOOOOOOOAM + MMLALALALALALAAM + MMMAMAMAMAMAMAMM + MMMMMMMMMMMMMMMM +} +# tile 152 (wooden / adornment) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMKCCKJMMMMMM + MMMMKJAAAKJMMMMM + MMMMCAMMMAKAMMMM + MMMMCAMMMMKAMMMM + MMMMJKAMMKJAMMMM + MMMMMJKKKJAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 153 (granite / gain strength) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPBPBPPPMMMMM + MMMPPPAAAPBPMMMM + MMMPBAAAAAPPAMMM + MMMPPAAMMMPPAMMM + MMMBPPAMMPPBAMMM + MMMMPPPPPBPAAMMM + MMMMMPBPPPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 154 (opal / gain constitution) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMOIOMMMMMMM + MMMMMOIOBOAMMMMM + MMMMMOOOBOAMMMMM + MMMMMCOGOCAMMMMM + MMMMHHAAAHNAMMMM + MMMMHAAMMMHAMMMM + MMMMNHAMMHNAMMMM + MMMMMHNHNHAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 155 (clay / increase accuracy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLLLLLMMMMMM + MMMMLCCCCLLMMMMM + MMMLCCAAALLCMMMM + MMMLCAAAAALCAMMM + MMMLCAAMMMLCAMMM + MMMLCCAMMLLCAMMM + MMMMLLLLLCCAAMMM + MMMMMCCCCCAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 156 (coral / increase damage) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOOOOMMMMMM + MMMMOOAAAOOMMMMM + MMMMOAAMMMNAMMMM + MMMMOAMMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 157 (black onyx / protection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMANAAAMMMMMM + MMMMMAAAAAMMMMMM + MMMMMMAAAMMMMMMM + MMMMMOLLLNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 158 (moonstone / regeneration) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMOBOMMMMMMM + MMMMMOBOOOAMMMMM + MMMMMOOOOOAMMMMM + MMMMMCCCCCAMMMMM + MMMMHHAAAHNAMMMM + MMMMHAAMMMHAMMMM + MMMMNHAMMHNAMMMM + MMMMMHNHNHAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 159 (tiger eye / searching) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMKCACKAMMMMM + MMMMMCCACCAMMMMM + MMMMMACCCAMMMMMM + MMMMMOCOCNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 160 (jade / stealth) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMNFFMMMMMMM + MMMMMFFFFFAMMMMM + MMMMMFFFFFAMMMMM + MMMMMOLLLNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 161 (bronze / sustain ability) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMCHCCCMMMMMM + MMMMCHAAACCMMMMM + MMMMCAAMMACAMMMM + MMMMCAMMMMCAMMMM + MMMMCCAMMCCAMMMM + MMMMMCCCCCAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 162 (agate / levitation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNOOPAMMMMM + MMMMMOIOIOAMMMMM + MMMMMAOIOAMMMMMM + MMMMMLHHHLAMMMMM + MMMMLHAAAHLAMMMM + MMMMHAAMMMHAMMMM + MMMMLHAMMHLAMMMM + MMMMMLHHHLAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 163 (topaz / hunger) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNBBPAMMMMM + MMMMMBOOOBAMMMMM + MMMMMABBBAMMMMMM + MMMMMOLLLNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 164 (sapphire / aggravate monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMNEEMMMMMMM + MMMMMEEEEFAMMMMM + MMMMMAEEFAMMMMMM + MMMMMOFEFNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 165 (ruby / conflict) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMJLDDJAMMMMM + MMMMMDDDDDAMMMMM + MMMMMADDDAMMMMMM + MMMMMOLLLNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 166 (diamond / warning) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNNNPAMMMMM + MMMMMNNNNBAMMMMM + MMMMMANNBAMMMMMM + MMMMMPPPPPAMMMMM + MMMMPPAAAPPAMMMM + MMMMPAAMMMPAMMMM + MMMMPPAMMPPAMMMM + MMMMMPPPPPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 167 (pearl / poison resistance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMNNNMMMMMMM + MMMMMNONNNAMMMMM + MMMMMNNNNNAMMMMM + MMMMMANNNAMMMMMM + MMMMMLHHHLAMMMMM + MMMMLHAAAHLAMMMM + MMMMHAAMMMHAMMMM + MMMMLHAMMHLAMMMM + MMMMMLHHHLAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 168 (iron / fire resistance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPPAAAPPMMMMM + MMMMPAMMMMPAMMMM + MMMMPAMMMMPAMMMM + MMMMPPAMMPPAMMMM + MMMMMPPPPPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 169 (brass / cold resistance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMHHHCCMMMMMM + MMMMHCAAACCMMMMM + MMMMHAAMMMCAMMMM + MMMMCAMMMMHAMMMM + MMMMCCAMMHCAMMMM + MMMMMCCCHCAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 170 (copper / shock resistance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMCCCCCMMMMMM + MMMMCCAAACCMMMMM + MMMMCAAMMACAMMMM + MMMMCAMMMMCAMMMM + MMMMCCAMMCCAMMMM + MMMMMCCCCCAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 171 (twisted / free action) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAPPBPMMMMMM + MMMMPPAAAAPMMMMM + MMMMPAAMMAPAMMMM + MMMMPAMMMMBAMMMM + MMMMBPAMMAPAMMMM + MMMMMAPBPPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 172 (steel / slow digestion) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPBBPPMMMMMM + MMMMPBAAAPPMMMMM + MMMMPAAMMAPAMMMM + MMMMPAMMMMPAMMMM + MMMMPPAMMPPAMMMM + MMMMMPPPPPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 173 (silver / teleportation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNBPPMMMMMM + MMMMPBAAAPPMMMMM + MMMMBAAMMAPAMMMM + MMMMBAMMMMPAMMMM + MMMMPPAMMPPAMMMM + MMMMMPPPBPAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 174 (gold / teleport control) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLHHHLMMMMMM + MMMMLHAAAHLMMMMM + MMMMHAAMMAHAMMMM + MMMMHAMMMMHAMMMM + MMMMLHAMMHLAMMMM + MMMMMLHHHLAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 175 (ivory / polymorph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOONOMMMMMM + MMMMOOAAAOOMMMMM + MMMMNAAMMANAMMMM + MMMMOAMMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 176 (emerald / polymorph control) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPGFFPAMMMMM + MMMMMFGFFFAMMMMM + MMMMMAFFFAMMMMMM + MMMMMOCECNAMMMMM + MMMMOOAAAONAMMMM + MMMMOAAMMMOAMMMM + MMMMNOAMMONAMMMM + MMMMMONONOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 177 (wire / invisibility) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPPAAAPPMMMMM + MMMMPAAMMMPAMMMM + MMMMPAMMMMMPAMMM + MMMMPAMMMMMPAMMM + MMMMPAAPPPPAMMMM + MMMMMPPPAAAMMMMM + MMMMMMAAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 178 (engagement / see invisible) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPNNNPAMMMMM + MMMMMNNNNNAMMMMM + MMMMMANNNAMMMMMM + MMMMMLHHHLAMMMMM + MMMMLHAAAHLAMMMM + MMMMHAAMMMHAMMMM + MMMMLHAMMHLAMMMM + MMMMMLHHHLAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 179 (shiny / protection from shape changers) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMNNNNNMMMMMM + MMMMNNAAANNMMMMM + MMMMNAMMMMNAMMMM + MMMMNAMMMMNAMMMM + MMMMNNAMMNNAMMMM + MMMMMNNNNNAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 180 (circular / amulet of ESP) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMACCCAMMMMM + MMMMMMCKKKKAMMMM + MMMMMMCKKKKAMMMM + MMMMMMMKKKAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 181 (spherical / amulet of life saving) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMAKKKAAMMMM + MMMMMMKHCCKAAMMM + MMMMMMKCCCKAAMMM + MMMMMMAKKKAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 182 (oval / amulet of strangulation) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLLAAMM + MMMMMLAAMMMMLAAM + MMMMLAAMMMMMMLAA + MMMMLAAMMMMMMLAA + MMMMLAAMMMMMMLAA + MMMMLAAMMMMMMLAA + MMMMMLAAMMMMLAAM + MMMMMMLAAAMLAAMM + MMMMMMACCCCAMMMM + MMMMMMCKKKKKAMMM + MMMMMMCKKKKKAMMM + MMMMMMAKKKKAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 183 (triangular / amulet of restful sleep) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMALCLAAMMMM + MMMMMMMCKKAMMMMM + MMMMMMCKKKKAMMMM + MMMMMCKKKKKKAMMM + MMMMMMMAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 184 (pyramidal / amulet versus poison) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMALCLAAMMMM + MMMMMMMCCKAAMMMM + MMMMMMCCCKKAMMMM + MMMMMCCCKKKKAMMM + MMMMCCKJJJJKKAMM + MMMMKJJJJJJJJAMM + MMMMMMMAAAAAAAMM +} +# tile 185 (square / amulet of change) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMALALAAMMMM + MMMMMMCCCCCAMMMM + MMMMMMCKKKKAMMMM + MMMMMMCKKKKAMMMM + MMMMMMCKKKKAMMMM + MMMMMMMAAAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 186 (concave / amulet of unchanging) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMAJJKAAMMMM + MMMMMMJJJCKAAMMM + MMMMMJJJKKCCAMMM + MMMMMJJKKCLCAMMM + MMMMMMJJCLCAAMMM + MMMMMMMKCCAAMMMM + MMMMMMMMAAAMMMMM +} +# tile 187 (hexagonal / amulet of reflection) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAMMLAAMMM + MMMMMMACCCAAMMMM + MMMMMMCKKKKAAMMM + MMMMMCKKKKKKAMMM + MMMMMMKKKKKAAMMM + MMMMMMMKKKAAMMMM + MMMMMMMMAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 188 (octagonal / amulet of magical breathing) +{ + MMMMMMMMMMMMMMMM + MMMMMMLLLLLAAMMM + MMMMMLAAMMMLAAMM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMLAAMMMMMLAAM + MMMMMLAAMMMLAAMM + MMMMMMLAAMLAAMMM + MMMMMMACCCAAMMMM + MMMMMMCKKKKAAMMM + MMMMMCKKKKKKAMMM + MMMMMCKKKKKKAMMM + MMMMMMKKKKKAAMMM + MMMMMMMKKKAAMMMM + MMMMMMMMAAAMMMMM +} +# tile 189 (Amulet of Yendor / cheap plastic imitation of the Amulet of Yendor) +{ + MMMMMMMMMMMMMMMM + MMMMMMHHHHHAAMMM + MMMMMHAAMMMHAAMM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMMHAAMMMHAAMM + MMMMMMHAAMHAAMMM + MMMMMMAHCCAAMMMM + MMMMMMBCDDPAAMMM + MMMMMMBCDDPAAMMM + MMMMMMABPPAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 190 (Amulet of Yendor / Amulet of Yendor) +{ + MMMMMMMMMMMMMMMM + MMMMMMHHHHHAAMMM + MMMMMHAAMMMHAAMM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMHAAMMMMMHAAM + MMMMMHAAMMMHAAMM + MMMMMMHAAMHAAMMM + MMMMMMAHCCAAMMMM + MMMMMMBCDDPAAMMM + MMMMMMBCDDPAAMMM + MMMMMMABPPAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 191 (large box) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCCCCCCCCCCJM + MMMCKKKKCKKKCJJM + MMCKJJKCJJJCJJJM + MCJJJKCJJJCJJJJM + CCCCCCCCCCCJJJJM + CJJJJJJJJJJJJJJA + CJKKKCJKKKCJJJJA + CJKKKHHKKKCJJJJA + CJKKKCJKKKCJJJAA + CJCCCCJCCCCJJAAM + CKKKKKKKKKKJAAMM + MAAAAAAAAAAAAMMM +} +# tile 192 (chest) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMCJKKKCJKKKCMM + MCKJKKCKJKKCJJKM + MKJKKKKJKKKKJJJM + CJKKKCJKKKCJJJJM + CJKKKCJKKKCJJJJM + CCCCCHHCCCCJJJJM + CJJJHJJHJJJJJJJM + CJKKKHHKKKCJJJJA + CJKKKHHKKKCJJJJA + CJKKKCJKKKCJJJAA + CJCCCCJCCCCJJAAM + CKKKKKKKKKKJAAMM + MAAAAAAAAAAAAMMM +} +# tile 193 (ice box) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMNNNNNNNNNNNM + MMMNPBBBBBBBBNPM + MMNPPBBBBBBBNPPM + MNPPPBBBBBBNPPPM + NNNNNNNNNNNPPPPM + NBBBBBBBBBBPPPPA + NNNNBBBBBBBPPPPA + NBBBBBBBBBBPPPPA + NNBBBBBBBBBPPPAA + NBBBBBBBBBBPPAAM + NBBBBBBBBBBPAAMM + MAAAAAAAAAAAAMMM +} +# tile 194 (bag / sack) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCJKKKKJMMMMM + MMMMMCJKKJAAMMMM + MMMMOOOOOAAMMMMM + MMMOMOKKKJAMMMMM + MMMMCOKKKKJAMMMM + MMMCCCKKKKKJAMMM + MMCCCKKKKKKJJAMM + MMCCCKKKKKKJJAMM + MMCCKKKKKKJJJAMM + MMMCKKKKJJJJAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 195 (bag / oilskin sack) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCJKKKKJMMMMM + MMMMMCJKKJAAMMMM + MMMMOOOOOAAMMMMM + MMMOMOKKKJAMMMMM + MMMMCOKKKKJAMMMM + MMMCCCKKKKKJAMMM + MMCCCKKKKKKJJAMM + MMCCCKKKKKKJJAMM + MMCCKKKKKKJJJAMM + MMMCKKKKJJJJAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 196 (bag / bag of holding) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCJKKKKJMMMMM + MMMMMCJKKJAAMMMM + MMMMOOOOOAAMMMMM + MMMOMOKKKJAMMMMM + MMMMCOKKKKJAMMMM + MMMCCCKKKKKJAMMM + MMCCCKKKKKKJJAMM + MMCCCKKKKKKJJAMM + MMCCKKKKKKJJJAMM + MMMCKKKKJJJJAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 197 (bag / bag of tricks) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMCJKKKKJMMMMM + MMMMMCJKKJAAMMMM + MMMMOOOOOAAMMMMM + MMMOMOKKKJAMMMMM + MMMMCOKKKKJAMMMM + MMMCCCKKKKKJAMMM + MMCCCKKKKKKJJAMM + MMCCCKKKKKKJJAMM + MMCCKKKKKKJJJAMM + MMMCKKKKJJJJAMMM + MMMMAAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 198 (key / skeleton key) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMOOMMMMMMMMMMMM + MOMAOOOOOOOOOOMM + MOAMOAAAAOAOAOAM + MOAMOAMMMOAMAOAM + MMOOMAMMMMAMMMAM + MMMAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 199 (lock pick) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MBBBPPPPPMMMMMMM + MBPPPPPPPPPPPAMM + MBPPAAAAAAAAAAAM + MBPPAMMMMMMMMMMM + MMAAAMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 200 (credit card) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMPPPPPPPPPPPPMM + MPPPDDDDPCCCPPPM + MPPDDDDPCCCCCPPA + MPPDDDDPCCCCCPPA + MPPDDDDPCCCCCPPA + MPPPDDDDPCCCPPPA + MMPPPPPPPPPPPPAA + MMMAAAAAAAAAAAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 201 (candle / tallow candle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNNAAAMMMMMMMM + MMMMMNPOAMMMMMMM + MMMMMPNNOAMMMMMM + MMMMMOOONOAMMMMM + MMMMMMOOONOAMMMM + MMMMMMMOOONOAMMM + MMMMMMMMOOONOAMM + MMMMMMMMMOOONOAM + MMMMMMMMMMOOOPAM + MMMMMMMMMMMOPAMM + MMMMMMMMMMMMAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 202 (candle / wax candle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMNNAAAMMMMMMMM + MMMMMNPOAMMMMMMM + MMMMMPNNOAMMMMMM + MMMMMOOONOAMMMMM + MMMMMMOOONOAMMMM + MMMMMMMOOONOAMMM + MMMMMMMMOOONOAMM + MMMMMMMMMOOONOAM + MMMMMMMMMMOOOPAM + MMMMMMMMMMMOPAMM + MMMMMMMMMMMMAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 203 (brass lantern) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMCCCMMMMMMM + MMMMMCMAACMMMMMM + MMMMMCAMMCAMMMMM + MMMMMLHHHLAMMMMM + MMMMLNNHNNLAMMMM + MMMMHNNHNNHAMMMM + MMMMHNNHNNHAMMMM + MMMMHNNHNNHAMMMM + MMMMLHHHHHLAMMMM + MMMMMLHHHLAMMMMM + MMMMMLHHHLAMMMMM + MMMMLHHHHHLMMMMM + MMMMMAAAAAAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 204 (lamp / oil lamp) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMHAMMMMMM + MLHCMMLNLLLAMMMM + MMMHHHNHHHHHHHMM + MMMMCLHHHHHLCHAM + MMMMMACLHLCCAAAM + MMMMMMLHHHLAAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 205 (lamp / magic lamp) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMHAMMMMMM + MLHCMMLNLLLAMMMM + MMMHHHNHHHHHHHMM + MMMMCLHHHHHLCHAM + MMMMMACLHLCCAAAM + MMMMMMLHHHLAAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 206 (expensive camera) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMAAAMMMMMMM + MMMAMAAAAAAAAAMM + MMAAAAAAAAABBAMM + MMAAAAPPPAABBAMM + MMAAAPPBPPAAAAPM + MMAAAPBBBPAAAAPM + MMAAAPPBPPAAAAPM + MMAAAAPPPAAAAAPM + MMAAAAAAAAAAAAPM + MMMPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM +} +# tile 207 (looking glass / mirror) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMBBBBAMMMMMMM + MMMBPPPPBAMMMMMM + MMBPPPPPPBAMMMMM + MMBPPPPPPPBAMMMM + MMBPPPPPPPBAMMMM + MMBPPPPPPPBAMMMM + MMMBPPPPPPBAMMMM + MMMMBPPPPBBAMMMM + MMMMMBBBBBBBAMMM + MMMMMMMMMMBBBAMM + MMMMMMMMMMMBBBAM + MMMMMMMMMMMMBBAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 208 (glass orb / crystal ball) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBBBBMMMMMMM + MMMMBNBBBBMMMMMM + MMMMBBBBBBMMMMMM + MMMMBBBBBBMAAMMM + MMMMBBBBBBAAAAMM + MMMMCBBBBJAAAMMM + MMMCKKKKKJJAMMMM + MMMMKKKKJJAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 209 (lenses) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPMMMMMPMMM + MMMMPMMPAMMPMPAM + MMMPMMAAMMPMAAMM + MMPNBAAMNBPAAMMM + MMNAABPNAABAAMMM + MMBAMPABAMPAAMMM + MMMBPAAMBPAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 210 (blindfold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOOOOOOOOMMMM + MMMOOOOOOOOOOMMM + MMMOOOOOOOOOOAMM + MMOAOOOAAOOOAOAM + MMOAAAAAAAAAAOAM + MMMOOAMMMMMMMOAM + MMMMMOAMMMMMOAMM + MMMMMOAMMMMMOAMM + MMMMMMOOAMMOAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 211 (towel) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMPPPPPPPM + MMMMMMMMOOOOOOOM + MMMMMMMMOOOOOOOM + MMMOOOOOOBOOOOOM + MMOOOOOOOPPOOOAM + MMOOOOOOOPPOOOAM + MMOOOOOOOPPPOAMM + MOOOOOOOPPPPPAMM + MOOOOOOOAAAAAMMM + MPPPPPPPAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 212 (saddle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJJAMMMJJAMMM + MMMMJKJJJJKJAMMM + MMMMMJCCKKJAMMMM + MMMMMJKKKKJAMMMM + MMMMMMJKKJAMMMMM + MMMMMMMJJAAMMMMM + MMMMMMMJAAMAMMMM + MMMMMMMPAMMAMMMM + MMMMMMPAPAAMMMMM + MMMMMMPPPAMMMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 213 (leash) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMKKJMMMMMM + MMMMMMJJJJAMMMMM + MMMMMJAAAAAMMMMM + MMMMKAAMMMMMMMMM + MMMMJAMMMMMMMMMM + MMMMJAMMMMMMMMMM + MMMMMJJJJMMMMMMM + MMMMMMAAAJMMMMMM + MMMMMMMMMAJMMMMM + MMMPPPPPMMKAMMMM + MMPPAAAPPKAAMMMM + MMMPPPPPAAAMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 214 (stethoscope) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPNMMMNPMMMMMM + MMPAAMMMMAPAMMMM + MMPAMMMMMMPAMMMM + MMMPAMMMMPAMMMMM + MMMPAMMMMPAMMMMM + MMMMPAMMPAMMMMMM + MMMMPAMMPAPPPMMM + MMMMMPPPAMJJPAMM + MMMMMPJPAMJPPAMM + MMMMMMJAMMJAAAMM + MMMMMMJAMJJAMMMM + MMMMMMMJJJAMMMMM + MMMMMMMMAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 215 (tinning kit) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPOOOOOOOOPM + MMMMPPOOOOOOOPPM + MMMPPPAAAAAAPPPM + MMOOOOOOOOOOPPPA + MMOOOOBBBOOOPPPA + MMOOOBPPPMOOPPAA + MMOOOBPBPMOOPAAM + MMOOOOBPMMOOAAMM + MMMAAOOOPMAAAMMM + MMMMMBOOPMAMMMMM + MMMMMBPPPMAMMMMM + MMMMMABPMAMMMMMM + MMMMMMAAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 216 (tin opener) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMBAMMMMMM + MMMMMMMMBPAMMMMM + MMMMMMPMBPPAMMMM + MMMMMMPABPPAMMMM + MMMMMMBPPPAAMMMM + MMMMMMMBPAAMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMKJAMMMMMM + MMMMMMMMAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 217 (can of grease) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBPPPPMMMMMM + MMMMBAAAAAPMMMMM + MMMMBPAAAPMAMMMM + MMMMBBPPPMMAAMMM + MMMMBBPPPMMAAAMM + MMMMBBPPPMMAAMMM + MMMMMBPPPMAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 218 (figurine) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMJJMMMMMM + MMMMMMMKCJJMMMMM + MMMMMMMKKJJMMMMM + MMMMCKMCJMMMMMMM + MMMMJJCKJMCJMMMM + MMMMMJCKJJJJAAMM + MMMMMMCKAAMAAAAM + MMMMKCKKAAAAAAMM + MMMJCCKKAJJAAMMM + MMMJKKKKJJJJAMMM + MMMMJJKKJJJAAMMM + MMMMMJJJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 219 (magic marker) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMBMMMMM + MMMMMMMMMBIEMMMM + MMMMMMMMBIEAAMMM + MMMMMMMBIEAAMMMM + MMMMMMBIEAAMMMMM + MMMMMBIEAAMMMMMM + MMMMNNEAAMMMMMMM + MMMMNNAAMMMMMMMM + MMMMMAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 220 (land mine) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMAMMMMMMMM + MMMMMFFAFFMMMMMM + MMMMFMAAAMFAMMMM + MMMMFGFFFFFAAMMM + MMMMMGFFFFAAMMMM + MMMMMMAAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 221 (beartrap) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPOMOPMMMMMM + MMMMPAMMMAPMMMMM + MMMMPPOMOPPAMMMM + MMMMPAMMMAPAMMMM + MMMMPPOAOPPAAMMM + MMMMPAAAAAPAAMMM + MMMMPAAMMMPAAMMM + MMMMPPAMMPPAMMMM + MMMMMPPPPPAAMMMM + MMMMMMPPPAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 222 (whistle / tin whistle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMBBABBBBBBBMMMM + MBPPPPPPPPPPAMMM + MPPPPPPAAAAAAMMM + MPPPPPPAMMMMMMMM + MMPPPPAAMMMMMMMM + MMMAAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 223 (whistle / magic whistle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMBBABBBBBBBMMMM + MBPPPPPPPPPPAMMM + MPPPPPPAAAAAAMMM + MPPPPPPAMMMMMMMM + MMPPPPAAMMMMMMMM + MMMAAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 224 (flute / wooden flute) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMJMMM + MMMMMMMMMMMAJMMM + MMMMMMMMMMKJAMMM + MMMMMMMMMKJAMMMM + MMMMMMMMKJAMMMMM + MMMMMMMJJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMJJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMJJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MJJAMMMMMMMMMMMM + MMAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 225 (flute / magic flute) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMJMMM + MMMMMMMMMMMAJMMM + MMMMMMMMMMKJAMMM + MMMMMMMMMKJAMMMM + MMMMMMMMJJAMMMMM + MMMMMMMJJAMMMMMM + MMMMMMKJAMMMMMMM + MMMMMJJAMMMMMMMM + MMMMKJAMMMMMMMMM + MMMJJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MJJAMMMMMMMMMMMM + MMAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 226 (horn / tooled horn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNOOMMM + MMMMMMMMMNOOMMMM + MMMMMMMNNOOPMMMM + MMMMNNPOOOOMMMMM + MMMNKKNPOOPMAMMM + MMNKJAANPOMAAAMM + MMNKAAANPPAAAMMM + MMNKJAJNPAAAMMMM + MMMNKKNPAAAMMMMM + MMMMNNPAAAMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 227 (horn / frost horn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNOOMMM + MMMMMMMMMNOOMMMM + MMMMMMMNNOOPMMMM + MMMMNNPOOOOMMMMM + MMMNKKNPOOPMAMMM + MMNKJAANPOMAAAMM + MMNKAAANPPAAAMMM + MMNKJAJNPAAAMMMM + MMMNKKNPAAAMMMMM + MMMMNNPAAAMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 228 (horn / fire horn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNOOMMM + MMMMMMMMMNOOMMMM + MMMMMMMNNOOPMMMM + MMMMNNPOOOOMMMMM + MMMNKKNPOOPMAMMM + MMNKJAANPOMAAAMM + MMNKAAANPPAAAMMM + MMNKJAJNPAAAMMMM + MMMNKKNPAAAMMMMM + MMMMNNPAAAMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 229 (horn / horn of plenty) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNOOMMM + MMMMMMMMMNOOMMMM + MMMMMMMNNOOPMMMM + MMMMNNPOOOOMMMMM + MMMNKKNPOOPMAMMM + MMNKJAANPOMAAAMM + MMNKAAANPPAAAMMM + MMNKJAJNPAAAMMMM + MMMNKKNPAAAMMMMM + MMMMNNPAAAMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 230 (harp / wooden harp) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKKKMMMMMMKKKMM + MMKAKPPPPPPKAKAM + MMMAKAMMMMMKAMAM + MMMKPPPPPPPPKMMM + MMMKAMMMMMMMKAMM + MMMKPPPPPPPPKAMM + MMMKAMMMMMMMKAMM + MMMMKPPPPPPKMAMM + MMMMMKKKKKKMAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 231 (harp / magic harp) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKKKMMMMMMKKKMM + MMKAKPPPPPPKAKAM + MMMAKAMMMMMKAMAM + MMMKPPPPPPPPKMMM + MMMKAMMMMMMMKAMM + MMMKPPPPPPPPKAMM + MMMKAMMMMMMMKAMM + MMMMKPPPPPPKMAMM + MMMMMKKKKKKMAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 232 (bell) +{ + MMMMMMMMMMMMMMMM + MMMMMMMKAMMMMMMM + MMMMMMKAKAMMMMMM + MMMMMMMKAMMMMMMM + MMMMMMLCKAMMMMMM + MMMMMLCCCKAMMMMM + MMMMMLCCCKAMMMMM + MMMMMLCCCKAAMMMM + MMMMCLCCCKCAMMMM + MMMCLLCCCKLKAMMM + MMMCCLLLLLCKAMMM + MMMMCCCCCCKAAMMM + MMMMMAAKKAAAMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 233 (bugle) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMLMMMMMMMMMMMMM + MKCLMMMMMMMMMMMM + MKCCLMMMMMMMMLLM + MKCCCLCCCCCCCLAM + MKCCKAAKKKKKAACA + MKCKAAJCAAACJAAM + MMKAAMMJCCCJAAMM + MMMAMMMMAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 234 (drum / leather drum) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPOOOOPMMMMM + MMMMOOOOOOOOMMMM + MMMOOOOOOOOOOMMM + MMMLOOOOOOOOLAAM + MMMLKOOOOOOJLAAA + MMMKKLKKKKLJJAAA + MMMJKLKKKKLJJAAM + MMMMJKKKKKKJAAMM + MMMMMJJJJJJAAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 235 (drum / drum of earthquake) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPOOOOPMMMMM + MMMMOOOOOOOOMMMM + MMMOOOOOOOOOOMMM + MMMLOOOOOOOOLAAM + MMMLKOOOOOOJLAAM + MMMKKLKKKKLJJAAM + MMMJKLKKKKLJJAAM + MMMMJKKKKKKJAAMM + MMMMMJJJJJJAAMMM + MMMMMMAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 236 (pick-axe) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMBBAMMMMMMM + MMMMMBPPBAMMMMMM + MMMMMMPPPBAMMMMM + MMMMMMMKPPPAMMMM + MMMMMMKJAMPPAMMM + MMMMMKJAMMMPAMMM + MMMMKJAMMMMMMMMM + MMMKJAMMMMMMMMMM + MMKJAMMMMMMMMMMM + MKJAMMMMMMMMMMMM + MMAMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 237 (iron hook / grappling hook) +{ + MMMMMMMMMMMMMNMM + MMMMMMMMMMMMMMPM + MMMMMMMMMMMNPAPA + MMMMMMMMMMMMMPAA + MMMMMMMOOEPPPPAM + MMMMMOOAAAAAAAPA + MMMMOAAMMMMMMMPA + MMMMOAMMMMMMMNAM + MMMMOAMMMMMMMMMM + MMMMMOOAAMMMMMMM + MMMMMMMOOOAMMMMM + MMMMMMMMMMOAMMMM + MMMMMMMMMMOAMMMM + MOAMMMMMMMOAMMMM + MMOOAMMOOOAMMMMM + MMMMOOOAAMMMMMMM +} +# tile 238 (unicorn horn) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMHMMMMMMMMMMMMM + MMMLAMMMMMMMMMMM + MMMLHLAMMMMMMMMM + MMMMHLHAAMMMMMMM + MMMMMLHLHAMMMMMM + MMMMMMHLHLAAMMMM + MMMMMMLLHLHHAMMM + MMMMMMMHHLHDAMMM + MMMMMMMMLHDDAMMM + MMMMMMMMHDDAMMMM + MMMMMMMMMAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 239 (candelabrum / Candelabrum of Invocation) +{ + MMMMMMMNMMMMMMMM + MMMMMMMDMMMMMMMM + MMMMMMMDMMMNMMMM + MMMNMMMDMMMDMMMM + MMMDMMMDMMMDMMMM + MMMDMMHDHMMDMMMM + MMHDHMMHMMHDHMMM + MMMHMMMHMMAHMMMM + MMMOHHHHHHHOMAMM + MMMMMMOHOAAMAMMM + MMMMMMMHMMAAAMMA + MMMMMMOHOAAAAAAM + MMMMMOHHHHAMMAMM + MMMMMMHHHAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 240 (silver bell / Bell of Opening) +{ + MMMMMMMMMMMMMMMM + MMMMMMMOAMMMMMMM + MMMMMMOAOAMMMMMM + MMMMMMMOAMMMMMMM + MMMMMMNOPAMMMMMM + MMMMMNOOOPAMMMMM + MMMMMNOOOPAMMMMM + MMMMMNOOOPAAMMMM + MMMMONOOOPOAMMMM + MMMONNOOOPNPAMMM + MMMOONNNNNOPAMMM + MMMMOOOOOOPAAMMM + MMMMMAAPPAAAMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 241 (tripe ration) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOOOMMMMMMM + MMMMDODDDOOMMMMM + MMMODODOOODAMMMM + MMMOOOOODDDAMMMM + MMMMDODOOOOAMMMM + MMMMMODOODAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 242 (corpse) +{ + MMMMMMMMMMMMMMMM + MMMMMDMDPLNMMMMM + MMMNOMDDLNOAMMMM + MMPOOADMMOONNMMM + MONONNMDLNNNNMMM + MNPANDCCCNAAAAMM + MMAADCCCCAADMMMM + MMDDDCCCDADDDDMM + MMDMNCDCCNMDMMMM + MPNNNOAADNNPNNMM + MONPONMDDMNONOAM + MMMPNNAMDMPNAAAM + MMMLNAAMDMNPAMMM + MMMMAAMMMMMAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 243 (egg) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPONOPMMMMMMM + MMMMONNNOMMMMMMM + MMMPNNNNNPMMMMMM + MMMONNNNNOMAAAMM + MMMNNNNNNOAAAAMM + MMMONNNNOOAAAAMM + MMMPONNOOPAAAMMM + MMMMPOOOPAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 244 (meatball) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMKKCKMMMMMMM + MMMMKCDJDJMMMMMM + MMMMCDJDJJAAAMMM + MMMMDCDJAJAAAMMM + MMMMKDDJJJAAAMMM + MMMMMJJAJAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 245 (meat stick) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMCDMMM + MMMMMMMMMMCDAAMM + MMMMMMMMMLDAAMMM + MMMMMMMDLKAAMMMM + MMMMMMLCKAAMMMMM + MMMMMLCKAAMMMMMM + MMMMLLKAAMMMMMMM + MMMDCKAAMMMMMMMM + MMMCKAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 246 (huge chunk of meat) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMKDKKMMMMMMM + MMMKKJJJJKDMMMMM + MMKCCKKCJJJKMMMM + MMKCKKCJJJJKAMMM + MKKKKJJJDDJJKAAM + MKKKJJJJJJJJKAAA + MKCKDJJJDJJJJAAA + MKKJDJJDJJAJAAAA + MKKKKJJDKKDJJAAA + MDKDJJJKKKKAJAAM + DKKKKAJJAJDAJJAM + KKKKKKKKKJJJAJAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 247 (meat ring) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOOAMMMMMMM + MMMMOAAMOOAMMMMM + MMMMOAAMMMOAMMMM + MMMMMOAAMMMOAMMM + MMMMMOAAMMMOAMMM + MMMMMOAMMMMOAMMM + MMMMOAAAMMOAAMMM + MMMMMOOOOOAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 248 (kelp frond) +{ + MMMMFAMMMMMMMMMM + MMMMFFAMMMMMMMMM + MMMMMFFAMMMMMMMM + MMMMMMMFFAMMMMMM + MMMMMMMMFAMMMMMM + MMMMMMMMFFAMMMMM + MMMMMMMMFFAMMMMM + MMMMMMMMFFAMMMMM + MMMMMMMFFAMMMMMM + MMMMMMMFFAMMMMMM + MMMMMMFFAMMMMMMM + MMMMMFFFAMMMMMMM + MMMMMFFAMMMMMMMM + MMMMMFFFAMMMMMMM + MMMMMFFFFAMMMMMM + MMMMMMFFFFAMMMMM +} +# tile 249 (eucalyptus leaf) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMLMMMMMMMM + MMMMMMMLAMMMMMMM + MMMMMMMLHMMMMMMM + MMMMMMGOHAMMMMMM + MMMMMMGOOAMMMMMM + MMMMMGOOAMMMMMMM + MMMMMGOHAMMMMMMM + MMMMMOOAMMMMMMMM + MMMMOHAMMMMMMMMM + MMMMOAMMMMMMMMMM + MMMOAMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 250 (apple) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFFMMMMMMMMMM + MMMMMMFMMMMMMMMM + MMMMCCFFCCMMMMMM + MMMCLLKCDDKMMMMM + MMMCLDDDCDKAMMMM + MMMCDDDDDDKAAMMM + MMMDDDDDDDKAAMMM + MMMDDDDDDKKAAMMM + MMMMDDKDKKAAMMMM + MMMMMKAAKAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 251 (orange) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFFFMMMMMMMMM + MMMMMMMFGGMMMMMM + MMMMMCCFCCDMMMMM + MMMMCGGCCCCDMMMM + MMMDCCCCCCCDAMMM + MMMCCCCCCCCDKAMM + MMMCCCCCCCDDKAMM + MMMDCCCCCDDKJAMM + MMMMDCCCDDKKAAMM + MMMMMDDDDKKAAMMM + MMMMMMAKKJAAMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 252 (pear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMFFCCMMM + MMMMMMMMFGCFMMMM + MMMMMMMFGGGFAAMM + MMMMMFFGHGFAAMMM + MMMFGHHHGFFAAMMM + MMFGHHGGGFFAMMMM + MMFHHGGGGFFAMMMM + MMFHHGGGGFMAMMMM + MMMFGGGGFMAAMMMM + MMMMFFFMMAAMMMMM + MMMMMAAAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 253 (melon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMFFFFMMMMMM + MMMMFGGGGGFMMMMM + MMMFGGGGGGGFAMMM + MMMGGGGGGGGFAAMM + MMFGGGGGGGGFAAMM + MMFGGGGGGGGFAAMM + MMFGGGGGGGGAAMMM + MMFGGKAGGGFAAMMM + MMMFKAGGGFAAMMMM + MMMMMFFFAAAMMMMM + MMMMMMAAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 254 (banana) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMJMMM + MMMMMMMMMMMMHAMM + MMMMMMMMMMMMHHMM + MMMMMMMMMMMHOHAM + MMMMMMMMMMMHOHAM + MMMMMMMMMMHOHHAM + MMMMMMMMMMHOHJAM + MMMMMMMMMHOHHAAM + MMMMMMMMHOHHJAMM + MMMMMMHHOHHJAAMM + MMJHOOOOHHJAAMMM + MMMMHHHHJAAAMMMM + MMMMMAAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 255 (carrot) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMFMMFMM + MMMMMMMMMMGFGAAM + MMMMMMMMMMFFAMMM + MMMMMMMMMMGFFFMM + MMMMMMMCCGFAAMMM + MMMMMMCCCCAMMMMM + MMMMMMDCCCMMMMMM + MMMMMCCDCAMMMMMM + MMMMCCCCAAMMMMMM + MMMMDCAAAMMMMMMM + MMMCCAAMMMMMMMMM + MMCDAAMMMMMMMMMM + MMCAAMMMMMMMMMMM + MMMAMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 256 (sprig of wolfsbane) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMHHMMM + MMMMMMMMMMHMMHMM + MMMHHHFMMMFAAMMM + MMHMAAAFMFAMMAMM + MMHAAMMMFAMMMMMM + MMMAMMMFFAMMMMMM + MMMMMMFFAAMMMMMM + MMMMMFFAAMMMMMMM + MMMFFFAAMMMMMMMM + MMMMAAAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 257 (clove of garlic) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOMMMMMMMMMM + MMMMOOOAAMMMMMMM + MMMMOOOOAAMMMMMM + MMMMHOOOOAAMMMMM + MMMMHHOOOOAAMMMM + MMMMMHHHOOOAAMMM + MMMMMMMHOOOAAMMM + MMMMMMMMAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 258 (slime mold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMJMMMM + MMMMMMMMFGJFMMMM + MMMMMMFFGGGFMMMM + MMMMFGGGGGGFAMMM + MMMFGGGGGCGCAMMM + MMMGGGGGGGCCAMMM + MMFGGGGCGCCKAMMM + MMFGGCGGCCKJAMMM + MMMCGGCCCKJAMMMM + MMMMCCCKJAAAMMMM + MMMMMAAAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 259 (lump of royal jelly) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOHMMMMMMMMM + MMMMONHHAAAMMMMM + MMMONOHHHAAAMMMM + MMHOOOOHHHHAMMMM + MMMMHOOHHAAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 260 (cream pie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOOOOOOOOMMMM + MMMOONNNNOONOAMM + MMKNONOOONNNNJAM + MMMKKKKKKKKJJAAM + MMMMJJJJJJJJAAMM + MMMMMMAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 261 (candy bar) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMIAMMMMMM + MMMMMMMBIIAMMMMM + MMMMMMIBBIIAMMMM + MMMMMNIIBBAAMMMM + MMMMNNNIIAAMMMMM + MMMNNCNNAAMMMMMM + MMIINNNAAMMMMMMM + MIIIINAAMMMMMMMM + MMIIIAAMMMMMMMMM + MMMIAAMMMMMMMMMM + MMMMAMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 262 (fortune cookie) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLLMMMMMMMMM + MMMMLLLLLLMMMMMM + MMMLLLLLLLLMMMMM + MMMLLLKJJKAAMMMM + MMMLLLLLJAAMMMMM + MMMMLLLLAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 263 (pancake) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOOOOOMMMMM + MMMMOOOOOOOOMMMM + MMMMOOOOKOOOOMMM + MMMOOKOOOOOOOAMM + MMOOOOOOOKOOOAMM + MMOOOOOOOOOOOAMM + MMOOKOOOOOOOOAMM + MMOOOOOOOKOOAAMM + MMMOOOKOOOOOAAMM + MMMMOOOOOOOAAMMM + MMMMMAAAAAAAMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 264 (lembas wafer) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMOOMMMMMMM + MMMMMMOOLOOMMMMM + MMMMMOOLOOLOOMMM + MMMMOOLOOLOOLOOM + MMMOOLOOLOOLOOAM + MMOOLOOLOOLOOAMM + MMMAOOLOOLOOAMMM + MMMMMAOOLOOAMMMM + MMMMMMMAOOAMMMMM + MMMMMMMMMAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 265 (cram ration) +{ + MMMMMMMMMMMMMMMM + MMMJKAMMMMMMMMMM + MMMPBAMMMMMMMMMM + MMMPBAMMMMMMMMMM + MMMPBPAMMMMMMMMM + MMMBBPAKKKKKAMMM + MMMBBPKKKKKKKAMM + MMMOPKKKKKKKAMMM + MMMOKKKKKKKJJAMM + MMMBPKLLOKJJJAMM + MMMPKLOLOLKJJAMM + MMMAKOLOLLKJAAMM + MMMMKLOLLOKAAMMM + MMMMKKKKKKKAMMMM + MMMMMAAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 266 (food ration) +{ + MMMJJAMMMMMMMMMM + MMMBPAMMMMMMMMMM + MMMBPAMMMMMMMMMM + MMBBBPAMMMMMMMMM + MMBBBPAKKKKKKKAM + MBBBBPKKKKKKKKKA + MBOOPKKKKKKKKKAM + MBOOKKKKKKKKKJJA + MBOOPKLLLLOKJJJA + MBPPKLOLLLOLKJJA + MMPAKOLOOOLLKJJA + MPPPKLOLLLOLKJJA + MMMAKOLOOOLLKJAA + MMMMKLOLLLLOKAAM + MMMMKKKKKKKKKAMM + MMMMMAAAAAAAAMMM +} +# tile 267 (K-ration) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJJAMMMMMMMMMM + MMMBPAMMMMMMMMMM + MMBBBPAMMMMMMMMM + MMBBBPKKKKKKKAMM + MMOOPKKKKKKKKKAM + MMOOKKKKKKKKKJAM + MMOOPKLLLLOKJJAM + MMPPKLLELEOLKJAM + MMPAKOLEELLLKJAM + MMPPKLLEELOLKJAM + MMMAKOLELELLKJAM + MMMMKLLLLLLOKAAM + MMMMKKKKKKKKKAMM + MMMMMAAAAAAAAMMM +} +# tile 268 (C-ration) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMJJAMMMMMMMMMM + MMMBPAMMMMMMMMMM + MMBBBPAMMMMMMMMM + MMBBBPAMMMMMMMMM + MMOOPKKKKKKKKAMM + MMOOKKKKKKKKKJAM + MMOOPKLLLLOKJJAM + MMPPKLOEEEOLKJAM + MMPAKOLEOOLLKJAM + MMPPKLOELLOLKJAM + MMMAKOLEEELLKJAM + MMMMKLOLLLLOKAAM + MMMMKKKKKKKKKAMM + MMMMMAAAAAAAAMMM +} +# tile 269 (tin) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMBBBBMMMMMMM + MMMMBPPPPPMAMMMM + MMMMBPBBPMAAAMMM + MMMMOBPPMMAAAAMM + MMMMOOOPMMAAAAMM + MMMMOOOPMMAAAMMM + MMMMBOOPMMAAMMMM + MMMMBBPPMMAMMMMM + MMMMMBPPMAMMMMMM + MMMMMMAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 270 (ruby / gain ability) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMODDDAMMMMMMM + MMMMMCDAMMMMMMMM + MMMMCDDDAMMMMMMM + MMMCNCDDDAMAAMMM + MMMCDDDDDAAAAMMM + MMMCDDDDDAAMMMMM + MMMMCDDDAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 271 (pink / restore ability) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOIIIAMMMMMMM + MMMMMLIAMMMMMMMM + MMMMLIIIAMMMMMMM + MMMLNLIIIAMAAMMM + MMMLIIIIIAAAAMMM + MMMLIIIIIAAMMMMM + MMMMLIIIAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 272 (orange / confusion) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOCCCAMMMMMMM + MMMMMKCAMMMMMMMM + MMMMKCCCAMMMMMMM + MMMKNKCCCAMAAMMM + MMMKCCCCCAAAAMMM + MMMKCCCCCAAMMMMM + MMMMKCCCAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 273 (yellow / blindness) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOHHHAMMMMMMM + MMMMMOHAMMMMMMMM + MMMMOHHHAMMMMMMM + MMMONOHHHAMAAMMM + MMMOHHHHHAAAAMMM + MMMOHHHHHAAMMMMM + MMMMOHHHAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 274 (emerald / paralysis) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOFFFAMMMMMMM + MMMMMGFAMMMMMMMM + MMMMGFFFAMMMMMMM + MMMGNGFFFAMAAMMM + MMMGFFFFFAAAAMMM + MMMGFFFFFAAMMMMM + MMMMGFFFAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 275 (dark green / speed) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOFFMAMMMMMMM + MMMMMFMAMMMMMMMM + MMMMFMFMAMMMMMMM + MMMFNFMFMAMAAMMM + MMMFFMFMMAAAAMMM + MMMFMFMMMAAMMMMM + MMMMFMFMAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 276 (cyan / levitation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMBBBEAMMMMMMM + MMMMMBEAMMMMMMMM + MMMMBBBEAMMMMMMM + MMMBNBBEEAMAAMMM + MMMBBBBEEAAAAMMM + MMMBBBBEEAAMMMMM + MMMMBBEEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 277 (sky blue / hallucination) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMNBBBAMMMMMMM + MMMMMNBAMMMMMMMM + MMMMNBBBAMMMMMMM + MMMNBBBBBAMAAMMM + MMMNBBBBBAAAAMMM + MMMNBBBBBAAMMMMM + MMMMNBBBAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 278 (brilliant blue / invisibility) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOEEEAMMMMMMM + MMMMMBEAMMMMMMMM + MMMMBEEEAMMMMMMM + MMMBNBEEEAMAAMMM + MMMBEEEEEAAAAMMM + MMMBEEEEEAAMMMMM + MMMMBEEEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 279 (magenta / see invisible) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMDIIDAMMMMMMM + MMMMMDIAMMMMMMMM + MMMMDIIDAMMMMMMM + MMMDLDIIDAMAAMMM + MMMDIIIIDAAAAMMM + MMMDDIIDDAAMMMMM + MMMMDDDDAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 280 (purple-red / healing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOEDEAMMMMMMM + MMMMMDEAMMMMMMMM + MMMMDEDEAMMMMMMM + MMMDNDEDEAMAAMMM + MMMDDEDEEAAAAMMM + MMMDEDEDEAAMMMMM + MMMMDEDEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 281 (puce / extra healing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOKKJAMMMMMMM + MMMMMIKAMMMMMMMM + MMMMIKDJAMMMMMMM + MMMINDKJJAMAAMMM + MMMIKKDJJAAAAMMM + MMMDKDKJJAAMMMMM + MMMMDKKJAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 282 (milky / gain level) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOOOOAMMMMMMM + MMMMMNOAMMMMMMMM + MMMMNOOOAMMMMMMM + MMMNOOOOOAMAAMMM + MMMNOOOOOAAAAMMM + MMMNOOOOOAAMMMMM + MMMMNOOOAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 283 (swirly / enlightenment) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMNOOOAMMMMMMM + MMMMMNOAMMMMMMMM + MMMMNOIIAMMMMMMM + MMMNOIIOOAMAAMMM + MMMIIOOOIAAAAMMM + MMMNOOIIOAAMMMMM + MMMMIIOOAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 284 (bubbly / monster detection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOFFFAMMMMMMM + MMMMMNFAMMMMMMMM + MMMMFFFNAMMMMMMM + MMMNFNFFFAMAAMMM + MMMFFFFFNAAAAMMM + MMMFNFFFFAAMMMMM + MMMMFFNFAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 285 (smoky / object detection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOPPPAMMMMMMM + MMMMMOPAMMMMMMMM + MMMMOPPPAMMMMMMM + MMMONPPPPAMAAMMM + MMMOPPPPPAAAAMMM + MMMOPPPPPAAMMMMM + MMMMOPPPAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 286 (cloudy / gain energy) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOEEEAMMMMMMM + MMMMMBPAMMMMMMMM + MMMMBEEPAMMMMMMM + MMMBEPPPEAMAAMMM + MMMBEEPEEAAAAMMM + MMMBEPPPPAAMMMMM + MMMMBPEEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 287 (effervescent / sleeping) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOEEOAMMMMMMM + MMMMMNNAMMMMMMMM + MMMMOFNNAMMMMMMM + MMMBFNNFOAMAAMMM + MMMOFNFFOAAAAMMM + MMMBFFFNEAAMMMMM + MMMMBNFFAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 288 (black / full healing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJMMMMMMMMM + MMMMMKJMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMMAAMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMANAAAAMPPPMMM + MMMAAAAAAPPPPMMM + MMMAAAAAAPPMMMMM + MMMAAAAAAPMMMMMM + MMMMAAAAPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 289 (golden / polymorph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMLHHHAMMMMMMM + MMMMMLHAMMMMMMMM + MMMMLHHCAMMMMMMM + MMMLNLHHCAMAAMMM + MMMLHHHHCAAAAMMM + MMMLHHHCCAAMMMMM + MMMMLCCCAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 290 (brown / booze) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMGFAMMMMMMMM + MMMMMFFAMMMMMMMM + MMMMCJJJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMKJJJAMMMMMMM + MMMKCKJJJAMAAMMM + MMMKJJJJJAAAAMMM + MMMKJJJJJAAMMMMM + MMMMKJJJAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 291 (fizzy / sickness) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMOEEOAMMMMMMM + MMMMMOOAMMMMMMMM + MMMMOEOOAMMMMMMM + MMMBEOOEOAMAAMMM + MMMOEOEEOAAAAMMM + MMMBEEEOEAAMMMMM + MMMMBOEEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 292 (dark / fruit juice) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJMMMMMMMMM + MMMMMKJMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMMAAMMMMMMMMM + MMMMAJJAMMMMMMMM + MMMANJJJAMPPPMMM + MMMAJJAAAPPPPMMM + MMMAJAAAAPPMMMMM + MMMAAAAAAPMMMMMM + MMMMAAAAPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 293 (white / acid) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMNNNNAMMMMMMM + MMMMMNNAMMMMMMMM + MMMMNNNNAMMMMMMM + MMMNPNNNNAMAAMMM + MMMNNNNNNAAAAMMM + MMMNNNNNNAAMMMMM + MMMMNNNNAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 294 (murky / oil) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJMMMMMMMMM + MMMMMKJMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMMAAMMMMMMMMM + MMMMAFJAMMMMMMMM + MMMANJFJAMPPPMMM + MMMAJFAFAPPPPMMM + MMMAFAFAAPPMMMMM + MMMAAFAFAPMMMMMM + MMMMAAAAPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 295 (clear / water) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMLJAMMMMMMMM + MMMMMKJAMMMMMMMM + MMMMBPPPAMMMMMMM + MMMMMBPAMMMMMMMM + MMMMBPPPAMMMMMMM + MMMBPPPPPAMAAMMM + MMMBEEEEEAAAAMMM + MMMBEEEEEAAMMMMM + MMMMBEEEAAMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 296 (ZELGO MER / enchant armor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 297 (JUYED AWK YACC / destroy armor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 298 (NR 9 / confuse monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 299 (XIXAXA XOXAXA XUXAXA / scare monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 300 (PRATYAVAYAH / remove curse) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 301 (DAIYEN FOOELS / enchant weapon) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 302 (LEP GEX VEN ZEA / create monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 303 (PRIRUTSENIE / taming) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 304 (ELBIB YLOH / genocide) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 305 (VERR YED HORRE / light) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 306 (VENZAR BORGAVVE / teleportation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 307 (THARR / gold detection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 308 (YUM YUM / food detection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 309 (KERNOD WEL / identify) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 310 (ELAM EBOW / magic mapping) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 311 (DUAM XNAHT / amnesia) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 312 (ANDOVA BEGARIN / fire) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 313 (KIRJE / earth) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 314 (VE FORBRYDERNE / punishment) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 315 (HACKEM MUCHE / charging) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 316 (VELOX NEB / stinking cloud) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 317 (FOOBIE BLETCH) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 318 (TEMOV) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 319 (GARVEN DEH) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 320 (READ ME) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMNJNJAOMAMMM + MMMMMLLLLLLPAMMM + MMMMMLLJLJLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 321 (stamped / mail) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MOOOOOOOOOEEOMMM + MOOOOOOOOOEEOMMM + MOOOOOOOOOOOOAMM + MOOOJLJJLJOOOAMM + MOOOOOOOOOOOOAMM + MOOOJJLJOOOOOAMM + MOOOOOOOOOOOOAMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 322 (unlabeled / blank paper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAPAMMM + MMMMMLLLLLLPAMMM + MMMMMLLLLLLPAMMM + MMMMMLLLLLLPAMMM + MMMMMLLLLLLPAMMM + MMMMPOOOOOPKAMMM + MMMDOOOOOOKDJMMM + MMMMPOOOOOPJAAMM + MMMMMAAAAAAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 323 (parchment / dig) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 324 (vellum / magic missile) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 325 (ragged / fireball) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKJJAMM + MKKKKKKKKKCJAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJOOOOOJAMMMM + MMMMMMOOJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 326 (dog eared / cone of cold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKOMMMMM + MMMKKHHKKKJOCMMM + MMJKKKHHKKJOOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 327 (mottled / sleep) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCLAAMM + MJOOKKKKKKOJAMMM + MMJJLLOKKCOAAMMM + MMMMJJJOLLJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 328 (stained / finger of death) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHCJJJJMMM + MMMKKHHKKCJJCMMM + MMJJJJCHKJJJLAMM + MMKJCJJHHKKCLAAM + MJKJJCCCKKKOJAMM + MKKKJJJJKKCOAAMM + MJOOKJJKKKOJAMMM + MMJJOLOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 329 (cloth / light) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEPPPMMMMMMMM + MMMMPPPEPPMMMMMM + MMMEPPHHHPEPPMMM + MMMPPHHPPHPPEMMM + MMEPPPHHPPPPOAMM + MMPPHPPHHEPEOAAM + MEPPPHHHPPPOPAMM + MPPPEPPPPPEOAAMM + MPOOPPPEPPOPAMMM + MMPPOOOPPEOAAMMM + MMMMPPPOOOPAMMMM + MMMMMMMPPPAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 330 (leather / detect monsters) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 331 (white / healing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPNNPMMMMMMMM + MMMMNNNNNPMMMMMM + MMMPNNAAANNNPMMM + MMMNNAANNANNNMMM + MMPNNNAANNNNOAMM + MMNNANNAANNNOAAM + MPNNNAAANNNONAMM + MNNNNNNNNNNOAAMM + MNOONNNNNNONAMMM + MMPNOOONNNOAAMMM + MMMMPNNOOONAMMMM + MMMMMMMPNNAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 332 (pink / knock) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMDIIDMMMMMMMM + MMMMIIIIIDMMMMMM + MMMDIIHHHIIIDMMM + MMMIIHHIIHIICMMM + MMDIIIHHIIIIOAMM + MMIIHIIHHIICOAAM + MDIIIHHHIIIOIAMM + MIIIIIIIIICOAAMM + MIOOIIIIIIOIAMMM + MMIIOOOIICOAAMMM + MMMMIIIOOOIAMMMM + MMMMMMMIIIAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 333 (red / force bolt) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKDDKMMMMMMMM + MMMMDDDDDKMMMMMM + MMMKDDHHHDDDKMMM + MMMDDHHDDHDDCMMM + MMKDDDHHDDDDOAMM + MMDDHDDHHDDCOAAM + MKDDDHHHDDDODAMM + MDDDDDDDDDCOAAMM + MDOODDDDDDODAMMM + MMDDOOODDCOAAMMM + MMMMDDDOOODAMMMM + MMMMMMMDDDAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 334 (orange / confuse monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKCCKMMMMMMMM + MMMMCCCCCKMMMMMM + MMMKCCAAACCCKMMM + MMMCCAACCACCLMMM + MMKCCCAACCCCOAMM + MMCCACCAACCLOAAM + MKCCCAAACCCOCAMM + MCCCCCCCCCLOAAMM + MCOOCCCCCCOCAMMM + MMCCOOOCCLOAAMMM + MMMMCCCOOOCAMMMM + MMMMMMMCCCAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 335 (yellow / cure blindness) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMOHHOMMMMMMMM + MMMMHHHHHOMMMMMM + MMMOHHAAAHHHOMMM + MMMHHAAHHAHHLMMM + MMOHHHAAHHHHOAMM + MMHHAHHAAHHLOAAM + MOHHHAAAHHHOHAMM + MHHHHHHHHHLOAAMM + MHOOHHHHHHOHAMMM + MMHHOOOHHLOAAMMM + MMMMHHHOOOHAMMMM + MMMMMMMHHHAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 336 (velvet / drain life) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMIIIIMMMMMMMM + MMMMIEEEEEMMMMMM + MMMIEENNNEEEEMMM + MMMIENNEENEEBMMM + MMIEEENNEEEIOAMM + MMEENEENNEIBOAAM + MIEEENNNEEIOEAMM + MEEEEEEEEIBOAAMM + MEOOEEEEIIOEAMMM + MMEEOOOIIBOAAMMM + MMMMEEEOOOEAMMMM + MMMMMMMEEEAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 337 (light green / slow monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFGGFMMMMMMMM + MMMMGGGGGFMMMMMM + MMMFGGMMMGGGFMMM + MMMGGMMGGMGGHMMM + MMFGGGMMGGGGOAMM + MMGGMGGMMGGHOAAM + MFGGGMMMGGGOGAMM + MGGGGGGGGGHOAAMM + MGOOGGGGGGOGAMMM + MMGGOOOGGHOAAMMM + MMMMGGGOOOGAMMMM + MMMMMMMGGGAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 338 (dark green / wizard lock) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFFFFMMMMMMMM + MMMMFFFFFFMMMMMM + MMMFFFHHHFFFFMMM + MMMFFHHFFHFFGMMM + MMFFFFHHFFFFOAMM + MMFFHFFHHFFGOAAM + MFFFFHHHFFFOFAMM + MFFFFFFFFFGOAAMM + MFOOFFFFFFOFAMMM + MMFFOOOFFGOAAMMM + MMMMFFFOOOFAMMMM + MMMMMMMFFFAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 339 (turquoise / create monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMFBBFMMMMMMMM + MMMMBBBBBFMMMMMM + MMMFBBGGGBBBFMMM + MMMBBGGBBGBBBMMM + MMFBBBGGBBBBOAMM + MMBBGBBGGBBBOAAM + MFBBBGGGBBBOBAMM + MBBBBBBBBBBOAAMM + MFOOBBBBBBOBAMMM + MMBBOOOBBBOAAMMM + MMMMFBBOOOBAMMMM + MMMMMMMFBBAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 340 (cyan / detect food) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMBBBBMMMMMMMM + MMMMBBBBBBMMMMMM + MMMBBBGGGBBBBMMM + MMMBBGGBBGBBBMMM + MMBBBBGGBBBBOAMM + MMBBGBBGGBBBOAAM + MBBBBGGGBBBOBAMM + MBBBBBBBBBBOAAMM + MBOOBBBBBBOBAMMM + MMBBOOOBBBOAAMMM + MMMMBBBOOOBAMMMM + MMMMMMMBBBAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 341 (light blue / cause fear) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEBBEMMMMMMMM + MMMMBBBBBEMMMMMM + MMMEBBAAABBBEMMM + MMMBBAABBABBBMMM + MMEBBBAABBBBOAMM + MMBBABBAABBBOAAM + MEBBBAAABBBOBAMM + MBBBBBBBBBBOAAMM + MBOOBBBBBBOBAMMM + MMBBOOOBBBOAAMMM + MMMMBBBOOOBAMMMM + MMMMMMMBBBAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 342 (dark blue / clairvoyance) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEEEEMMMMMMMM + MMMMEEEEEEMMMMMM + MMMEEEHHHEEEEMMM + MMMEEHHEEHEEBMMM + MMEEEEHHEEEEOAMM + MMEEHEEHHEEBOAAM + MEEEEHHHEEEOEAMM + MEEEEEEEEEBOAAMM + MEOOEEEEEEOEAMMM + MMEEOOOEEBOAAMMM + MMMMEEEOOOEAMMMM + MMMMMMMEEEAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 343 (indigo / cure sickness) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEEEEMMMMMMMM + MMMMEEEEEEMMMMMM + MMMEEEIIIEEEEMMM + MMMEEIIEEIEEBMMM + MMEEEEIIEEEEOAMM + MMEEIEEIIEEBOAAM + MEEEEIIIEEEOEAMM + MEEEEEEEEEBOAAMM + MEOOEEEEEEOEAMMM + MMEEOOOEEBOAAMMM + MMMMEEEOOOEAMMMM + MMMMMMMEEEAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 344 (magenta / charm monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKIIKMMMMMMMM + MMMMIIIIIKMMMMMM + MMMKIIEEEIIIKMMM + MMMIIEEIIEIICMMM + MMKIIIEEIIIIOAMM + MMIIEIIEEIICOAAM + MKIIIEEEIIIOIAMM + MIIIIIIIIICOAAMM + MIOOIIIIIIOIAMMM + MMIIOOOIICOAAMMM + MMMMIIIOOOIAMMMM + MMMMMMMIIIAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 345 (purple / haste self) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMEDEDMMMMMMMM + MMMMDEDEDEMMMMMM + MMMDEDHHHDEDMMMM + MMMEDHHEDHDECMMM + MMEDEDHHEDEDOAMM + MMDEHEDHHEDCOAAM + MDEDEHHHEDEOIAMM + MEDEDEDEDECOAAMM + MDOOEDEDEDOIAMMM + MMIIOOOEDCOAAMMM + MMMMIIIOOOIAMMMM + MMMMMMMIIIAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 346 (violet / detect unseen) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJBIJMMMMMMMM + MMMMBIBIBJMMMMMM + MMMJIBAAABIBJMMM + MMMIBAAIBABICMMM + MMJBIBAAIBIBOAMM + MMBIAIBAAIBCOAAM + MJIBIAAAIBIOIAMM + MIBIBIBIBICOAAMM + MIOOIBIBIBOIAMMM + MMIIOOOIBCOAAMMM + MMMMIIIOOOIAMMMM + MMMMMMMIIIAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 347 (tan / levitation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJCKJMMMMMMMM + MMMMCKCKCJMMMMMM + MMMJKCHHHCKCJMMM + MMMKCHHKCHCKCMMM + MMJCKCHHKCKCOAMM + MMCKHKCHHKCCOAAM + MJKCKHHHKCKOKAMM + MKCKCKCKCKCOAAMM + MJOOKCKCKCOKAMMM + MMKKOOOKCCOAAMMM + MMMMKKKOOOKAMMMM + MMMMMMMKKKAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 348 (plaid / extra healing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJEEJMMMMMMMM + MMMMDEEDDJMMMMMM + MMMJDFHHHFFDJMMM + MMMDDHHDDHFDCMMM + MMJDDEHHDEEDOAMM + MMEDHEEHHEECOAAM + MJFDDHHHDFFOEAMM + MFFDDFFDDFCOAAMM + MJOODEEDDEODAMMM + MMEDOOODDCOAAMMM + MMMMEFDOOOFAMMMM + MMMMMMMEFDAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 349 (light brown / restore ability) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 350 (dark brown / invisibility) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJJJJMMMMMMMM + MMMMJJJJJJMMMMMM + MMMJJJHHHJJJJMMM + MMMJJHHJJHJJKMMM + MMJJJJHHJJJJOAMM + MMJJHJJHHJJKOAAM + MJJJJHHHJJJOJAMM + MJJJJJJJJJKOAAMM + MJOOJJJJJJOJAMMM + MMJJOOOJJKOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 351 (gray / detect treasure) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPPPPMMMMMMMM + MMMMPPPPPPMMMMMM + MMMPPPAAAPPPPMMM + MMMPPAAPPAPPBMMM + MMPPPPAAPPPPOAMM + MMPPAPPAAPPBOAAM + MPPPPAAAPPPOPAMM + MPPPPPPPPPBOAAMM + MPOOPPPPPPOPAMMM + MMPPOOOPPBOAAMMM + MMMMPPPOOOPAMMMM + MMMMMMMPPPAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 352 (wrinkled / remove curse) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKMMMMMMM + MMMMKKAAAKKKJMMM + MMMKKAAKKAKKKMMM + MMJKKKAAKKKKOAMM + MMKKAKKAAKKKOAAM + MJKKKAAAKKKOKAMM + MKKKKKKKKKLOAAMM + MKOOLKKKKKOKAMMK + MMJKOOKKKKOAAMMM + MMMMJKLOOOKAMMMM + MMMMMMJJKKAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 353 (dusty / magic mapping) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMKMMMMMMMKAMMMM + MMKAJKKJMMMMMMMM + MMMMKKKKKJMMKMMM + MKAJKKHHHKKKJMKA + MMMKKHHKKHKKCMMM + MMJKAKHHKKAKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOJAMM + MKKKKKKKKKCOAAMM + AJOOKKKKAKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MKAKAMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 354 (bronze / identify) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKCCKMMMMMMMM + MMMMCCCCCKMMMMMM + MMMKCCHHHCCCKMMM + MMMCCHHCCHCCCMMM + MMKCCCHHCCCCOAMM + MMCCHCCHHCCCOAAM + MKCCCHHHCCCOCAMM + MCCCCCCCCCCOAAMM + MKOOCCCCCCOCAMMM + MMCCOOOCCCOAAMMM + MMMMCCCOOOCAMMMM + MMMMMMMCCCAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 355 (copper / turn undead) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKCKCKJMMMMMM + MMMJCKHHHKCKJMMM + MMMCKHHCKHKCCMMM + MMJKCKHHCKCKOAMM + MMKCHCKHHCKCOAAM + MJCKCHHHCKCOJAMM + MCKCKCKCKCCOAAMM + MJOOCKCKCKOJAMMM + MMCJOOOCKCOAAMMM + MMMMJCJOOOJAMMMM + MMMMMMMJCJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 356 (silver / polymorph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPPPPMMMMMMMM + MMMMPPPPPPMMMMMM + MMMPPPNNNPPPPMMM + MMMPPNNPPNPPBMMM + MMPPPPNNPPPPOAMM + MMPPNPPNNPPBOAAM + MPPPPNNNPPPOPAMM + MPPPPPPPPPBOAAMM + MPOOPPPPPPOPAMMM + MMPPOOOPPBOAAMMM + MMMMPPPOOOPAMMMM + MMMMMMMPPPAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 357 (gold / teleport away) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMLHHLMMMMMMMM + MMMMHHHHHLMMMMMM + MMMLHHCCCHHHLMMM + MMMHHCCHHCHHNMMM + MMLHHHCCHHHHOAMM + MMHHCHHCCHHNOAAM + MLHHHCCCHHHOHAMM + MHHHHHHHHHNOAAMM + MLOOHHHHHHOHAMMM + MMHHOOOHHNOAAMMM + MMMMHHHOOOHAMMMM + MMMMMMMHHHAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 358 (glittering / create familiar) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMNMMMMMMMMM + MMMNPPPPMMNMMMMM + MMMMPPPPPPMMMNMM + MMMPPPHHHPPPPMMM + MMNPPHHPPHPPBMMM + MMPPPPHHPPPPONMM + MNPPHPPHHPPBOAAM + MPPPPHHHPPPOPAMM + NPPPPPPPPPBONAMM + MPOONPPPPPOPAMMM + MMPPOONPPBOAAMMM + MMMNMPPOOOPAMMMM + MMMMMMMPPPANMMMM + MMMMMMMNMMMMMMMM +} +# tile 359 (shining / cancellation) +{ + MMMMNMMMMMMMMMMM + MMMMMMMNMMMMMMMM + MMNMMMMMMMMMMMMM + MMMMPNPPMMMMMMMM + MMMMPNNNPPMMMMMM + MMMPNNAAAPPPPMMM + MMMPNAAPPAPPBMMM + MMPNNPAAPPPPOAMM + MMPNAPPAAPPBOAAM + MPNPPAAAPPPOPAMM + MPPPPPPPPPBOAAMM + MPOOPPPPPPOPAMMM + MMPPOOOPPBOAAMMM + MMMMPPPOOOPAMMMM + MMMMMMMPPPAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 360 (dull / protection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKCCCKKKJMMM + MMMKKCCKKCKKJMMM + MMJKKKCCKKKKPAMM + MMKKCKKCCKKJPAAM + MJKKKCCCKKKPJAMM + MKKKKKKKKKJPAAMM + MJPPKKKKKKPJAMMM + MMJJPPPKKJPAAMMM + MMMMJJJPPPJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 361 (thin / jumping) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMJKJMMMMMMMM + MMMMJKKKKKJMMMMM + MMMMKKHHHKKKKMMM + MMMJKHHKKHKKOAMM + MMMKKKHHKKKKOAAM + MMJKHKKHHKKCJAMM + MMKKKHHHKKKOAAMM + MJOCKKKKKKCJAMMM + MMJJOOCKKKOAAMMM + MMMMJJJOCOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 362 (thick / stone to flesh) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKHHHKKKJMMM + MMMKKHHKKHKKCMMM + MMJKKKHHKKKKOAMM + MMKKHKKHHKKCOAAM + MJKKKHHHKKKOLAMM + MKKKKKKKKKCOJAMM + MJOOKKKKKKOLAMMM + MJLLOOOKKCOJAMMM + MMJJLLLOOOLAAMMM + MMMMJJJLLLJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 363 (plain / blank paper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMJKKJMMMMMMMM + MMMMKKKKKJMMMMMM + MMMJKKKKKKKKJMMM + MMMKKKKKKKKKCMMM + MMJKKKKKKKKKOAMM + MMKKKKKKKKKCOAAM + MJKKKKKKKKKOJAMM + MKKKKKKKKKCOAAMM + MJOOKKKKKKOJAMMM + MMJJOOOKKCOAAMMM + MMMMJJJOOOJAMMMM + MMMMMMMJJJAAMMMM + MMMMMMMMMMMMMMMM +} +# tile 364 (papyrus / Book of the Dead) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMAAAAAAMMMMMM + MMMAAAOOOAAAAMMM + MMMAAOAOOOAAAMMM + MMAAAAOOAOAAOMMM + MMAAAOAOOAAAOMMM + MAAAAOOOAAAOAMMM + MAAAAAAAAAAOMMMM + MAOOAAAAAAOAMMMM + MMAAOOOAAAOMMMMM + MMMMAAAOOOAMMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 365 (glass / light) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMNBMMM + MMMMMMMMMMNBAAMM + MMMMMMMMMNBAAMMM + MMMMMMMMNBAAMMMM + MMMMMMMNBAAMMMMM + MMMMMMNBAAMMMMMM + MMMMMNBAAMMMMMMM + MMMMNBAAMMMMMMMM + MMMNBAAMMMMMMMMM + MMMBAFMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 366 (balsa / secret door detection) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMOKMMM + MMMMMMMMMMOKAAMM + MMMMMMMMMLKAAMMM + MMMMMMMMLKAAMMMM + MMMMMMMLKAAMMMMM + MMMMMMLKAAMMMMMM + MMMMMLKAAMMMMMMM + MMMMOKAAMMMMMMMM + MMMOKAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 367 (crystal / enlightenment) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNBMMMM + MMMMMMMMMNBAMMMM + MMMMMMMMNBAMMMMM + MMMMMMMNBAMMMMMM + MMMMMMNBAMMMMMMM + MMMMMNBAMMMMMMMM + MMMMNBAMMMMMMMMM + MMMMBAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 368 (maple / create monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMKJAAMM + MMMMMMMMMKJAAMMM + MMMMMMMMKJAAMMMM + MMMMMMMKJAAMMMMM + MMMMMMKJAAMMMMMM + MMMMMKJAAMMMMMMM + MMMMKJAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 369 (pine / wishing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMLLAAMM + MMMMMMMMMLLAAMMM + MMMMMMMMLLAAMMMM + MMMMMMMLLAAMMMMM + MMMMMMLLAAMMMMMM + MMMMMLLAAMMMMMMM + MMMMLLAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 370 (oak / nothing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMNNOMMM + MMMMMMMMMJKOAAMM + MMMMMMMMJJJAAMMM + MMMMMMMJJKAAMMMM + MMMMMMJKJAAMMMMM + MMMMMJJJAAMMMMMM + MMMMJJKAAMMMMMMM + MMMOKJAAMMMMMMMM + MMNNOAAMMMMMMMMM + MMMNAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 371 (ebony / striking) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMAJAAMM + MMMMMMMMMAJAAMMM + MMMMMMMMAJAAMMMM + MMMMMMMAJAAMMMMM + MMMMMMAJAAMMMMMM + MMMMMAJAAMMMMMMM + MMMMAJAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 372 (marble / make invisible) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMIOAAMM + MMMMMMMMMNIAAMMM + MMMMMMMMNNAAMMMM + MMMMMMMIIAAMMMMM + MMMMMMNNAAMMMMMM + MMMMMINAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 373 (tin / slow monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMPPAAMM + MMMMMMMMMPPAAMMM + MMMMMMMMPPAAMMMM + MMMMMMMPPAAMMMMM + MMMMMMPPAAMMMMMM + MMMMMPPAAMMMMMMM + MMMMPPAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 374 (brass / speed monster) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMHCAAMM + MMMMMMMMMHCAAMMM + MMMMMMMMHCAAMMMM + MMMMMMMHCAAMMMMM + MMMMMMHCAAMMMMMM + MMMMMHCAAMMMMMMM + MMMMHCAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 375 (copper / undead turning) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMCCAAMM + MMMMMMMMMCCAAMMM + MMMMMMMMCCAAMMMM + MMMMMMMCCAAMMMMM + MMMMMMCCAAMMMMMM + MMMMMCCAAMMMMMMM + MMMMCCAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 376 (silver / polymorph) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMNPAAMM + MMMMMMMMMNPAAMMM + MMMMMMMMNPAAMMMM + MMMMMMMNPAAMMMMM + MMMMMMNPAAMMMMMM + MMMMMNPAAMMMMMMM + MMMMNPAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 377 (platinum / cancellation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMNOAAMM + MMMMMMMMMNBAAMMM + MMMMMMMMNBAAMMMM + MMMMMMMNBAAMMMMM + MMMMMMNBAAMMMMMM + MMMMMNBAAMMMMMMM + MMMMNOAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 378 (iridium / teleportation) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMNOAAMM + MMMMMMMMMPIAAMMM + MMMMMMMMPIAAMMMM + MMMMMMMPIAAMMMMM + MMMMMMPIAAMMMMMM + MMMMMPIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 379 (zinc / opening) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMLPAAMM + MMMMMMMMMLPAAMMM + MMMMMMMMLPAAMMMM + MMMMMMMLPAAMMMMM + MMMMMMLPAAMMMMMM + MMMMMLPAAMMMMMMM + MMMMLPAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 380 (aluminum / locking) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMPPAAMM + MMMMMMMMMBPAAMMM + MMMMMMMMPPAAMMMM + MMMMMMMBPAAMMMMM + MMMMMMPPAAMMMMMM + MMMMMBPAAMMMMMMM + MMMMPPAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 381 (uranium / probing) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMPNOMMM + MMMMMMMMMPPPAAMM + MMMMMMMMHHPAAMMM + MMMMMMMHAHAAMMMM + MMMMMMPAHAAMMMMM + MMMMMPPPAAMMMMMM + MMMMPPPAAMMMMMMM + MMMPPPAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 382 (iron / digging) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMKJMMM + MMMMMMMMMMKPAAMM + MMMMMMMMMPPAAMMM + MMMMMMMMPPAAMMMM + MMMMMMMPPAAMMMMM + MMMMMMPPAAMMMMMM + MMMMMPPAAMMMMMMM + MMMMKPAAMMMMMMMM + MMMKJAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 383 (steel / magic missile) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMKPMMM + MMMMMMMMMMOPAAMM + MMMMMMMMMOPAAMMM + MMMMMMMMOPAAMMMM + MMMMMMMOPAAMMMMM + MMMMMMPPAAMMMMMM + MMMMMPPAAMMMMMMM + MMMMKPAAMMMMMMMM + MMMKKAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 384 (hexagonal / fire) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMPNMMMM + MMMMMMMMMPNMAAMM + MMMMMMMMPNMAAMMM + MMMMMMMPNMAAMMMM + MMMMMMPNMAAMMMMM + MMMMMPNMAAMMMMMM + MMMMPNMAAMMMMMMM + MMMPNMAAMMMMMMMM + MMMNMAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 385 (short / cold) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNOMMMMM + MMMMMMMMNOMMMMMM + MMMMMMMDIAAMMMMM + MMMMMMDIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 386 (runed / sleep) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMNOMMM + MMMMMMMMMMNOAAMM + MMMMMMMMMDIAAMMM + MMMMMMMMAIAAMMMM + MMMMMMMDIAAMMMMM + MMMMMMAIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 387 (long / death) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMNOM + MMMMMMMMMMMMNNMM + MMMMMMMMMMMDIMMM + MMMMMMMMMMDIAAMM + MMMMMMMMMDIAAMMM + MMMMMMMMDIAAMMMM + MMMMMMMDIAAMMMMM + MMMMMMDIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMDIAAMMMMMMMM + MMMNIAAMMMMMMMMM + MMNOAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 388 (curved / lightning) +{ + MMMMMMMMMMMMMMMM + MMMMMMMNOMMMMMMM + MMMMMMMNOAMMMMMM + MMMMMMMCIAMMMMMM + MMMMMMMDIAMMMMMM + MMMMMMMDIAMMMMMM + MMMMMMJIKAMMMMMM + MMMMMMKIJAMMMMMM + MMMMMMDIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 389 (forked) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMNMMMMMM + MMMMMMMMNMMNOMMM + MMMMMMMDMMNOAAMM + MMMMMMMDMDIAAMOM + MMMMMMMDDIAAMOAM + MMMMMMMDIIIIIAMM + MMMMMMDIAAAAAMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 390 (spiked) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMNNOMMM + MMMMMMMMMMDIOMMM + MMMMMMMMMDIAPMMM + MMMMMMMMDIAAMMMM + MMMMMMMDIAAMMMMM + MMMMMMDIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 391 (jeweled) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMNNBMMM + MMMMMMMMMMNBBMMM + MMMMMMMMMMBBBAMM + MMMMMMMMMDIAAMMM + MMMMMMMMDIAAMMMM + MMMMMMMDIAAMMMMM + MMMMMMDIAAMMMMMM + MMMMMDIAAMMMMMMM + MMMMNIAAMMMMMMMM + MMMNOAAMMMMMMMMM + MMMMAAMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 392 (gold piece) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MJKKKJMJJMMMMMMM + JKCKKKJKKKJMMMMM + KCKKKJKAAAKAMMMM + KKKKKJAHALKAAMMM + KKKKJKHLLHAHAMMM + MKKJJKLHCALAMMMM + MAAAJKLALHCAHAMM + MMMAAKAHAHMMMMMM + MMMMAAAALAHAMHAM + MMMMMMHAMMMMMMMM + MMMMMMMMMHAMMMMM + MMMMMMMMMMMHAMMM +} +# tile 393 (white / dilithium crystal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMONOOOMMMMMM + MMMMONOOOBOMMMMM + MMMNNNONONBBMMMM + MMMMONOOOBOAAMMM + MMMMMONOBOAAAAMM + MMMMMMOOOAAAMMMM + MMMMMMMOAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 394 (white / diamond) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMONOOOMMMMMM + MMMMONOOOBOMMMMM + MMMNNNONONBBMMMM + MMMMONOOOBOAAMMM + MMMMMONOBOAAAAMM + MMMMMMOOOAAAMMMM + MMMMMMMOAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 395 (red / ruby) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMDNDDDMMMMMM + MMMMDNDDDKDMMMMM + MMMDNDDKDKKKMMMM + MMMMDCDDDKDAAMMM + MMMMMDCDKDAAAAMM + MMMMMMDDDAAAMMMM + MMMMMMMDAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 396 (orange / jacinth) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMCNCCCMMMMMM + MMMMCNCCCKCMMMMM + MMMCNCCKCKKKMMMM + MMMMCLCCCKCAAMMM + MMMMMCLCKCAAAAMM + MMMMMMCCCAAAMMMM + MMMMMMMCAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 397 (blue / sapphire) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMBNBBBMMMMMM + MMMMBNBBBEBMMMMM + MMMNNNBEBEEEMMMM + MMMMBNBBBEBAAMMM + MMMMMBNBEBAAAAMM + MMMMMMBBBAAAMMMM + MMMMMMMBAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 398 (black / black opal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAMMMMMM + MMMMANAAAAAMMMMM + MMMANAAAAAAAMMMM + MMMAAAAAAAAAPMMM + MMMMAAAAAAAPPPMM + MMMMMMAAAPPPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 399 (green / emerald) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMFGFFFMMMMMM + MMMMFGFFFFFMMMMM + MMMGGFGFGFFFMMMM + MMMMFGFFFFFAAMMM + MMMMMFGFFFAAAAMM + MMMMMMFGFAAAMMMM + MMMMMMMFAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 400 (green / turquoise) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMFGFFFMMMMMM + MMMMFGFFFFFMMMMM + MMMGGFGFGFFFMMMM + MMMMFGFFFFFAAMMM + MMMMMFGFFFAAAAMM + MMMMMMFGFAAAMMMM + MMMMMMMFAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 401 (yellow / citrine) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMHNHHHMMMMMM + MMMMHNHHHCHMMMMM + MMMHNHHCHCCCMMMM + MMMMHLHHHCHAAMMM + MMMMMHLHCHAAAAMM + MMMMMMHHHAAAMMMM + MMMMMMMHAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 402 (green / aquamarine) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMFGFFFMMMMMM + MMMMFGFFFFFMMMMM + MMMGGFGFGFFFMMMM + MMMMFGFFFFFAAMMM + MMMMMFGFFFAAAAMM + MMMMMMFGFAAAMMMM + MMMMMMMFAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 403 (yellowish brown / amber) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMKCKKKMMMMMM + MMMMKCKKKJKMMMMM + MMMCCCKJKJJJMMMM + MMMMKCKKKJKAAMMM + MMMMMKCKJKAAAAMM + MMMMMMKKKAAAMMMM + MMMMMMMKAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 404 (yellowish brown / topaz) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMKCKKKMMMMMM + MMMMKCKKKJKMMMMM + MMMCCCKJKJJJMMMM + MMMMKCKKKJKAAMMM + MMMMMKCKJKAAAAMM + MMMMMMKKKAAAMMMM + MMMMMMMKAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 405 (black / jet) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAMMMMMM + MMMMANAAAAAMMMMM + MMMANAAAAAAAMMMM + MMMAAAAAAAAAPMMM + MMMMAAAAAAAPPPMM + MMMMMMAAAPPPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 406 (white / opal) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMONOOOMMMMMM + MMMMONOOOBOMMMMM + MMMNNNONONBBMMMM + MMMMONOOOBOAAMMM + MMMMMONOBOAAAAMM + MMMMMMOOOAAAMMMM + MMMMMMMOAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 407 (yellow / chrysoberyl) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMHNHHHMMMMMM + MMMMHNHHHCHMMMMM + MMMHNHHCHCCCMMMM + MMMMHLHHHCHAAMMM + MMMMMHLHCHAAAAMM + MMMMMMHHHAAAMMMM + MMMMMMMHAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 408 (red / garnet) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMDNDDDMMMMMM + MMMMDNDDDKDMMMMM + MMMDNDDKDKKKMMMM + MMMMDCDDDKDAAMMM + MMMMMDCDKDAAAAMM + MMMMMMDDDAAAMMMM + MMMMMMMDAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 409 (violet / amethyst) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMINIIIMMMMMM + MMMMINIIIEIMMMMM + MMMNNNININEIMMMM + MMMMINIIIEIAAMMM + MMMMMINIEIAAAAMM + MMMMMMIIIAAAMMMM + MMMMMMMIAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 410 (red / jasper) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMDNDDDMMMMMM + MMMMDNDDDKDMMMMM + MMMDNDDKDKKKMMMM + MMMMDCDDDKDAAMMM + MMMMMDCDKDAAAAMM + MMMMMMDDDAAAMMMM + MMMMMMMDAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 411 (violet / fluorite) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMINIIIMMMMMM + MMMMINIIIEIMMMMM + MMMNNNININEIMMMM + MMMMINIIIEIAAMMM + MMMMMINIEIAAAAMM + MMMMMMIIIAAAMMMM + MMMMMMMIAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 412 (black / obsidian) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAMMMMMM + MMMMANAAAAAMMMMM + MMMANAAAAAAAMMMM + MMMAAAAAAAAAPMMM + MMMMAAAAAAAPPPMM + MMMMMMAAAPPPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 413 (orange / agate) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMCNCCCMMMMMM + MMMMCNCCCKCMMMMM + MMMCNCCKCKKKMMMM + MMMMCLCCCKCAAMMM + MMMMMCLCKCAAAAMM + MMMMMMCCCAAAMMMM + MMMMMMMCAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 414 (green / jade) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMFGFFFMMMMMM + MMMMFGFFFFFMMMMM + MMMGGFGFGFFFMMMM + MMMMFGFFFFFAAMMM + MMMMMFGFFFAAAAMM + MMMMMMFGFAAAMMMM + MMMMMMMFAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 415 (white / worthless piece of white glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMONOOOMMMMMM + MMMMONOOOBOMMMMM + MMMNNNONONBBMMMM + MMMMONOOOBOAAMMM + MMMMMONOBOAAAAMM + MMMMMMOOOAAAMMMM + MMMMMMMOAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 416 (blue / worthless piece of blue glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMBNBBBMMMMMM + MMMMBNBBBEBMMMMM + MMMNNNBEBEEEMMMM + MMMMBNBBBEBAAMMM + MMMMMBNBEBAAAAMM + MMMMMMBBBAAAMMMM + MMMMMMMBAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 417 (red / worthless piece of red glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMDNDDDMMMMMM + MMMMDNDDDKDMMMMM + MMMDNDDKDKKKMMMM + MMMMDCDDDKDAAMMM + MMMMMDCDKDAAAAMM + MMMMMMDDDAAAMMMM + MMMMMMMDAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 418 (yellowish brown / worthless piece of yellowish brown glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMKCKKKMMMMMM + MMMMKCKKKJKMMMMM + MMMCCCKJKJJJMMMM + MMMMKCKKKJKAAMMM + MMMMMKCKJKAAAAMM + MMMMMMKKKAAAMMMM + MMMMMMMKAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 419 (orange / worthless piece of orange glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMCNCCCMMMMMM + MMMMCNCCCKCMMMMM + MMMCNCCKCKKKMMMM + MMMMCLCCCKCAAMMM + MMMMMCLCKCAAAAMM + MMMMMMCCCAAAMMMM + MMMMMMMCAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 420 (yellow / worthless piece of yellow glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMHNHHHMMMMMM + MMMMHNHHHCHMMMMM + MMMHNHHCHCCCMMMM + MMMMHLHHHCHAAMMM + MMMMMHLHCHAAAAMM + MMMMMMHHHAAAMMMM + MMMMMMMHAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 421 (black / worthless piece of black glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMAAAAAMMMMMM + MMMMANAAAAAMMMMM + MMMANAAAAAAAMMMM + MMMAAAAAAAAAPMMM + MMMMAAAAAAAPPPMM + MMMMMMAAAPPPMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 422 (green / worthless piece of green glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMFGFFFMMMMMM + MMMMFGFFFFFMMMMM + MMMGGFGFGFFFMMMM + MMMMFGFFFFFAAMMM + MMMMMFGFFFAAAAMM + MMMMMMFGFAAAMMMM + MMMMMMMFAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 423 (violet / worthless piece of violet glass) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPMMMMMMMM + MMPMMMMPMMMMPMMM + MMMPMMMMMMMPMMMM + MMMMMINIIIMMMMMM + MMMMINIIIEIMMMMM + MMMNNNININEIMMMM + MMMMINIIIEIAAMMM + MMMMMINIEIAAAAMM + MMMMMMIIIAAAMMMM + MMMMMMMIAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 424 (gray / luckstone) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPNPPPPPMMMMM + MMMPNPPPPPPPAMMM + MMPPPPPPPPPPPAAM + MMPPPPPPPPPPPAAA + MMMPPPPPPPPPAAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 425 (gray / loadstone) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPNPPPPPMMMMM + MMMPNPPPPPPPAMMM + MMPPPPPPPPPPPAAM + MMPPPPPPPPPPPAAA + MMMPPPPPPPPPAAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 426 (gray / touchstone) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPNPPPPPMMMMM + MMMPNPPPPPPPAMMM + MMPPPPPPPPPPPAAM + MMPPPPPPPPPPPAAA + MMMPPPPPPPPPAAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 427 (gray / flint) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMPPPPPMMMMMM + MMMMPNPPPPPMMMMM + MMMPNPPPPPPPAMMM + MMPPPPPPPPPPPAAM + MMPPPPPPPPPPPAAA + MMMPPPPPPPPPAAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 428 (rock) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKKKMMMMMMM + MMMMMKLCKKMMMMMM + MMMMMKCCKJAMMMMM + MMMMMKKKKJAMMMMM + MMMMMMKJJAAMMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 429 (boulder) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMKKKJMMMMMM + MMMMKKKKKKJJMMMM + MMMKCCKKKKKJJMMM + MMMKCKKKCKKKJAMM + MMKKKKKKKJKKJJAM + MMKKKKKKKKKKJJAA + MMKJKKKCKKKKJJAA + MMKKKKKKJKKKJJAA + MMMKKKKKKKKKJAAA + MMMJJKKKKKJJJAAM + MMMMJJJJJJJJAAMM + MMMMMMJJJJAAAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 430 (statue) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMJJMMMMMM + MMMMMMMKCJJMMMMM + MMMMMMMKKJJMMMMM + MMMMCKMCKJMMMMMM + MMMMJJCKKJMCJMMM + MMMMMJCKJJJJJMMM + MMMMMJCKJJJMAAMM + MMMMMMCKJMMMAAAM + MMMMMMCKJAAAAAAA + MMMMKCKKJAAAAAAM + MMMJCCKKJAJJAAMM + MMMJKKKKJJJJJAMM + MMMMJJKKKJJJAAMM + MMMMMJJJJJJAAMMM + MMMMMMMMMMMMMMMM +} +# tile 431 (heavy iron ball) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMPPPMMMMMMM + MMMMMPOBPPMMMMMM + MMMMMPBBPMAMMMMM + MMMMMPPPPMAMMMMM + MMMMMMPMMAAMMMMM + MMMMMMMAAAMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 432 (iron chain) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MPPMPPMPPMMMMMMM + PMAPMAPMAPPMMMMM + MPPAPPAPPPAPMMMM + MMAAMAAMAPAPAMMM + MMMMMMMMMMPAMMMM + MMMMMMMMMPAPMMMM + MMMMMMMMMPAPPMPM + MMMMMMMMMMPMAPAP + MMMMMMMMMMMPPMPA + MMMMMMMMMMMMAAMM +} +# tile 433 (splash of venom / blinding venom) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMHHHHMMMMM + MMMMMHHAAAAHHMMM + MMMMHAAAAAAAHMMM + MMMMHAAAAAAAHMMM + MMMMHAAAAHHHMMMM + MMMMMHHHHMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 434 (splash of venom / acid venom) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMHHHHMMMMM + MMMMMHHGGGGHHMMM + MMMMHGGGGGGGHMMM + MMMMHGGGGGGGHMMM + MMMMHGGGGHHHMMMM + MMMMMHHHHMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} diff --git a/win/share/other.txt b/win/share/other.txt new file mode 100644 index 0000000..14ec940 --- /dev/null +++ b/win/share/other.txt @@ -0,0 +1,4348 @@ +A = (0, 0, 0) +B = (0, 182, 255) +C = (255, 108, 0) +D = (255, 0, 0) +E = (0, 0, 255) +F = (0, 145, 0) +G = (108, 255, 0) +H = (255, 255, 0) +I = (255, 0, 255) +J = (145, 71, 0) +K = (204, 79, 0) +L = (255, 182, 145) +M = (71, 108, 108) +N = (255, 255, 255) +O = (218, 218, 182) +P = (108, 145, 182) +# tile 0 (dark part of a room) +{ + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA +} +# tile 1 (wall) +{ + ANNOAMPPPPPMNNOA + AOOOAMPMPPMMOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AOOOAMMPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPMPMOPMA + ANNOAMPPPPPMNNOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AOOOAMMPMPMMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 2 (wall) +{ + AAANOOAAAAANOOAA + OOONOOAMOOONOOAM + POOMMMAMPOOMMMAM + MMMMMMMMMMMMMMMM + PPMMMPPPPPMMMPPP + PPNOOPPPPPNOOPPP + OONOOAMOOONOOAMO + OOMMMAMPOOMMMAMP + MMMMMMMMMMMMMMMM + PPMOPPPPMOPPPMOP + PPMPPPPPMPPPPMPP + MMMMMMMMMMMMMMMM + MOPPPPMOPPPPMOPP + MPPPPPMPPPPPMPPP + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 3 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 4 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 5 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + PPMOPPPPMOPPPMOP + PPMPPPPPMPPPPMPP + MMMMMMMMMMMMMMMM + MOPPPPMOPPPPMOPP + MPPPPPMPPPPPMPPP + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 6 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + PPMOPPPPMOPPPMOP + PPMPPPPPMPPPPMPP + MMMMMMMMMMMMMMMM + MOPPPPMOPPPPMOPP + MPPPPPMPPPPPMPPP + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 7 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 8 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + PPMOPPPPMOPPPMOP + PPMPPPPPMPPPPMPP + MMMMMMMMMMMMMMMM + MOPPPPMOPPPPMOPP + MPPPPPMPPPPPMPPP + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 9 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 10 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 11 (wall) +{ + AAANNNNOOOOOOAAA + NNNNAAMMPPPPOMMM + OOONAMMPPPBBOMMM + OOOOOOOOOOOOOMMM + OOOPPPPPPPPPPMMM + OOOPPPPPPPPPPMMM + OOPPPPPPPPPPPPMM + OPPPPPPPPPPPPPPM + MMMMMMMMMMMMMMMM + APPMMMMMMMMMPPPM + AOOOAAAAAAAAOOOA + AOOOAMPPPPPMOOOA + AOOOAMPPPPPMOOOA + AMAAAMMPPPPMMAMA + AMMMAMMPPPPMMMMA + AOPPAMMPPPPMOPMA +} +# tile 12 (doorway) +{ + AAAAAAAAAAAAAAAA + AAMMMMMMMMMMMMAA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AAMMMMMMMMMMMMAA + AAAAAAAAAAAAAAAA +} +# tile 13 (open door) +{ + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAJJMJJJJJJJJJAA + AJKJJKJJKJJJJJJA + AAAAAAAAAAAAAAAA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AMMMMMMMMMMMMMMA + AAAAAAAAAAAAAAAA +} +# tile 14 (open door) +{ + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AKMMMMMMMMMMMMAA + ACKMMMMMMMMMMMMA + APJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AJJMMMMMMMMMMMMA + APJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AMJMMMMMMMMMMMMA + AKMMMMMMMMMMMMMA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA +} +# tile 15 (closed door) +{ + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AACCKKKKKKKKJJAA + ACKJJJJJJJJJJKJA + APJJPKJJKJJJJJJA + AMPPMPPPPPMBPPJA + AMMMMMMMMMBPPMJA + AMJJMJJJJJPAPAJA + AJJJJKJJJJMPMAJA + APKJPKJJJJJAAAJA + AMPPMPPPPPPMMPJA + AMMMMMMMMMMMMMJA + AMJJMJJJJJJJJJJA + AJKJJKJJKJJJJJJA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA +} +# tile 16 (closed door) +{ + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AACCKKKKKKKKJJAA + ACKJJJJJJJJJJKJA + APJJPKJJKJJJJJJA + AMPPMPPPPPMBPPJA + AMMMMMMMMMBPPMJA + AMJJMJJJJJPAPAJA + AJJJJKJJJJMPMAJA + APKJPKJJJJJAAAJA + AMPPMPPPPPPMMPJA + AMMMMMMMMMMMMMJA + AMJJMJJJJJJJJJJA + AJKJJKJJKJJJJJJA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA +} +# tile 17 (iron bars) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMBBMMMMMMM + MMMMBBMBPMBPMMMM + MBBMBPMBPMPPMBPM + MBPMBPMBPMPPMPPM + MBPMBPMBPMPPMPPM + MBPMBPMBPMPPAPPM + MBPMBPMBPAPPAPPM + MBPMBPABPAPPAPPM + MBPABPABPAPPAPPA + MBPABPABPAPPAPPA + MBPABPABPAPPAPPA + MBPABPABPAPPAPPA + MBPMBPMBPMPPMPPM + MMMMMMMMMMMMMMMM +} +# tile 18 (tree) +{ + MMMMMMMMMMMMMMMM + MMMMMFFFFFFMMMMM + MMMFFFFFFFFFFMMM + MMFFFFFFFFFFFFMM + MMFFFFFFFFFFFFMM + MMFFFFJFFFJFFFMM + MMMFFFFJFJFFFMMM + MMMMFFJJJJFFMMMM + MMMMMMMKJJMAAAAM + MMMMMMMKJMAAAAAA + MMMMMMMJJMAAAAAA + MMMMMMMKJMAAAAAM + MMMMMMMKJAAAMMMM + MMMMMMKJJJAAMMMM + MMMJJJJMJMJJJAMM + MMMMMMMMMMMMMMMM +} +# tile 19 (floor of a room) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 20 (corridor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMPMMPMMMMMM + MMMMMMPMMPMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 21 (lit corridor) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMPPPPMMMMMM + MMMMMMPPPPMMMMMM + MMMMMMMPPMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 22 (staircase up) +{ + AAAAAAAAAAAAAAMA + AADJJJJJJJJJDAMA + AACDDJKNKKDDCAMA + AAAAAANNNAAAAMMA + AAADJNNNNNJDAMMA + AAACNOONONNCAMMA + AAAAAAOONAAAMMMA + AAAAKDOOOKJAMMMA + AAAACKPONKCAMMMA + AAAAAAOPOAAMMMMA + AAAAAKPPPJAMMMMA + AAAAACKJJKAMMMMA + AAAAAAAAAAAAMMMA + AAAAAAAAAAAAAMMA + AAAAAAAAAAAAAAMA + AAAAAAAAAAAAAAAA +} +# tile 23 (staircase down) +{ + AAAAAAAAAAAAAAMA + AADJJJJJJJJJDAMA + AACDDJNNNKDDCAMA + AAAAAANNNAAAAMMA + AAADJJNONJJDAMMA + AAACDDONNKDCAMMA + AAAAAAONOAAAMMMA + AAAAPOOONONAMMMA + AAAACPOOOPCAMMMA + AAAAAANPNAAMMMMA + AAAAAKKOJJAMMMMA + AAAAACKJJKAMMMMA + AAAAAAAAAAAAMMMA + AAAAAAAAAAAAAMMA + AAAAAAAAAAAAAAMA + AAAAAAAAAAAAAAAA +} +# tile 24 (ladder up) +{ + ADAAAAAAAAAAADMA + AADAAAANAAAADAMA + AACCCCNNNCCCCAMA + AADAANANANAADMMA + AAADAAANAAADAMMA + AAACDDDNDDDCAMMA + AAADAAAOAAADMMMA + AAAAKAAOAAJAMMMA + AAAACKKOKKCAMMMA + AAAADAAPAADMMMMA + AAAAAKJPJJAMMMMA + AAAAADAAAKAMMMMA + AAAAAAAAAAAAMMMA + AAAAAAAAAAAAAMMA + AAAAAAAAAAAAAAMA + AAAAAAAAAAAAAAAA +} +# tile 25 (ladder down) +{ + ADAAAAAAAAAAADMA + AADAAAANAAAADAMA + AACCCCCNCCCCCAMA + AADAAAANAAAADMMA + AAADAAANAAADAMMA + AAACDDDNDDDCAMMA + AAADAAAOAAADMMMA + AAAAKAAOAAJAMMMA + AAAACOKOKOCAMMMA + AAAADAOOOADMMMMA + AAAAAKJOJJAMMMMA + AAAAADAAAKAMMMMA + AAAAAAAAAAAAMMMA + AAAAAAAAAAAAAMMA + AAAAAAAAAAAAAAMA + AAAAAAAAAAAAAAAA +} +# tile 26 (altar) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMPDPDPDPDPMMMM + MMPPPDPDDDPPPMMM + MMMBBPPPDPPMAAMM + MMMBPPPPPPMMAMMM + MMPPMMMMMMMPPMMM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM +} +# tile 27 (grave) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMONOPPMMMMMM + MMMONPPPPPPMMMMM + MMOPPPPPPPPPMMMM + MOPPPPPPPPPPPMMM + MOPAAPPAPAAPPMMM + MOPAPAPAPAPAPAAM + MOPAAPPAPAAPPAAA + MOPAPAPAPAPPPAAA + MOPAPAPAPAPPPAAA + MOPPPPPPPPPPPAAA + MOPPPPPPPFPPPAAM + MOPPFMMPPFMPPAMM + FFFFFFFFFFFFFFFM + MMMMMMMMMMMMMMMM +} +# tile 28 (opulent throne) +{ + MMMMMMMMMMMMMMMM + MMMMMHHHHHMMMMMM + MMMMHCCCDDHMMMMM + MMMMHCCEDDHMMMMM + MMMMHCEEEDHMMMMM + MMMMHCCEDDHMMMMM + MMMMHCCEDDHMMAAM + MMMMMHCDDHAAAAAM + MMCCOHCDDHOCCAAA + MHHHDHHHHHCHHHAA + MHMHODDDCCOHAHAA + MMMHHHHHHHHHAAAM + MMMHALAAALAHAAMM + MMHAMAAAAAAAHMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 29 (sink) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMPMMMMPMMMMMM + MMNPPPPPPPPNMMMM + MNOMPMPPMPMONMMM + MNOBMMPPMMBONMMM + MNOBBBPPBBBONAAM + MNOBBBBBBBBONAAA + MNOOOOOOOOOONAAA + MMNNNNNNNNNNAAAA + MMMMMOOOOAAAAAAA + MMMMMOOOOAAAAAMM + MMMMOOOOOOAMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 30 (fountain) +{ + MMMMEMMMMEMMMMMM + MMEEEEMMEEEMMMMM + MMEMMMEEEMEEMMMM + MMMEMEEEEMEMMMMM + MMEMMEEOEMEEMAMM + MEEMEENOEMEMEAAM + MEMMEMNOMEMEAAAM + MMMPPPNOPPPAAAMM + MMPEEENOEEEPAAMM + MOEENNNOOOEEPAMM + MNOEEEEEEEEONAAM + MPNOOOOOOOONPAAM + MMPNNNNNNNNPAAAM + MMMPPPPPPPPAAAMM + MMMMAAAAAAAAAMMM + MMMMMMMMMMMMMMMM +} +# tile 31 (water) +{ + MMMMMMMMMMNNNMMM + MEEEEMMMMNEMENMM + MMMMEEMEEEMMMMMM + MMMMMEEEMMMMMMMM + MNNMMMMMMEEMMMMM + EMENMMMMEMEEMMEE + MMMENMEMMMMEMMEM + MMMMEEMMMMMMEEMM + MMMMMMMMMMMMMMMM + NMMEEMMMEEEEMMMM + NNEMMEMEMMMEEMMN + MEMMMMEMNNMMEENM + MMMMMMMNEEEEMMMM + MMEEEMMEMMMEEEMM + MEMMEEMMMEMMEEEM + EEMMMMEEEMMMMMEE +} +# tile 32 (ice) +{ + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNMMNNNNNNN + NNNNNNMNNMNNNNNN + NNNNNNMNNMNNNNNN + NNNNNNNMMNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN +} +# tile 33 (molten lava) +{ + DDDDDDCDDDDDDDDD + DDDDDCDKDDDDDDDD + DDCCDDKDDDDCCCDD + DCJJKDDDDDCKJJKD + DCJDKDDDDDCJKDKD + DDKKDDDDDDCJDKKD + DDDDDDDCDDDKKKDD + DDDCDDDDDDDDDDDD + DDDDJDCDDDDCDDDD + DDDDDDDDDDCJKDDD + DDDDDDDDDDDKDDDC + DDDDCCDDDDDDDDDD + DDDCJJKDDDDDDDDD + CDDCJDKDDDDDDJDD + DDDDKKDDDDCDDDDD + DDDDDDDDDDDKDDDD +} +# tile 34 (lowered drawbridge) +{ + EKKAKKKKKKKAKKAE + EJKKKKKKKKKKKJAA + EEJJJJJJJJJJJAAA + EJKKKKKKKKKKKJAA + EKKAKKKKKKKAKKAE + EJKKKKKKKKKKKJAA + EEJJJJJJJJJJKAAA + EJKKKKKKKKKKKJAA + EKKAKKKKKKKAKKAE + EJKKKKKKKKKKKJAA + EEJJJJJJJJJJJAAA + EJKKKKKKKKKKKJAA + EKKAKKKKKKKAKKAE + EJKKKKKKKKKKKJAA + EEJJJJJJJJJJJAAA + EJKKKKKKKKKKKJAA +} +# tile 35 (lowered drawbridge) +{ + EEEEEEEEEEEEEEEE + JEJKJEJKJEJKJEJK + KJKKKJKKKJKKKJKK + KJKAKJKAKJKAKJKA + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKKKJKKKJKKKJKK + KJKAKJKAKJKAKJKA + KJKKKJKKKKKKKJKK + JAJKJAJKJAJKJAJK + AAAAAAAAAAAAAAAA + AAAEAAAEAAAEAAAE +} +# tile 36 (raised drawbridge) +{ + MMMMMMMMMMMMMMMM + MMJKJMJKJMJKJMMM + MJKKKJKKKJKKKJMM + MJKAKJKAKJKAKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKKKJKKKJKKKJAM + MJKAKJKAKJKAKJAM + MJKKKJKKKKKKKJAM + MMJKJAJKJAJKJAAM + MMMAAAMAAAMAAAMM + MMMMMMMMMMMMMMMM +} +# tile 37 (raised drawbridge) +{ + MMMMMMMMMMMMMMMM + MMJJJJJJJJJJJMMM + MJKKKKKKKKKKKJMM + MKKAKKKKKKKAKKAM + MJKKKKKKKKKKKJAM + MMJJJJJJJJJJJAAM + MJKKKKKKKKKKKJMM + MKKAKKKKKKKAKKAM + MJKKKKKKKKKKKJAM + MMJJJJJJJJJJKAAM + MJKKKKKKKKKKKJMM + MKKAKKKKKKKAKKAM + MJKKKKKKKKKKKJAM + MMJJJJJJJJJJJAAM + MMMAAAAAAAAAAAMM + MMMMMMMMMMMMMMMM +} +# tile 38 (air) +{ + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB +} +# tile 39 (cloud) +{ + BBBBBBBBBBBBBBBB + BBBBBNNNNNNNBBBB + BBBNNNNNNNNNNBBB + BBNNNNNNNNNONNBB + BBNNNNNNNNNNNNNB + BNNNNNNNNNNNNONB + NNNONNNNNNNNONNN + NNNNNNNNNNOONNNN + NNNNNNNNNNNNNONN + NNOONNNNNNNOONNO + NNNNNOOOOONNNNOO + BONNNNNNNNNNOOOB + BBOOONNNNOOOOOOB + BBBOOOOOOOOOOBBB + BBBBBBOOOOBBBBBB + BBBBBBBBBBBBBBBB +} +# tile 40 (water) +{ + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE + EEEEEEEEEEEEEEEE +} +# tile 41 (arrow trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCACDDMMMMM + MMMDCCAAACCDMMMM + MMMCCAAAAACDAMMM + MMDCCHHAHHCDDAMM + MMDCHHNANHHCDAMM + MMDCHNNANNHCDAMM + MMDCHNNANNHCDAMM + MMDCCHAAAHCCDAMM + MMMCCAAAAACCAAMM + MMMDCAAAAACDAMMM + MMMMDAACAADAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 42 (dart trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCACDDMMMMM + MMMDCCCACCCDMMMM + MMMCCCHAHCCDAMMM + MMDCCHAAAHCDDMMM + MMDCHHAAAHHCDAMM + MMDCHNAAANHCDAMM + MMDCHNNANNHCDAMM + MMDCCAAAAACDDAMM + MMMCCAACAACDAAMM + MMMDCACACACDAMMM + MMMMDDCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 43 (falling rock trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDAACDDMMMMM + MMMDCCCCCCCDMMMM + MMMCCCAAHCCDAMMM + MMDCCHHHHHCDDAMM + MMDCHHAAHHHCDAMM + MMDCHHHHHHHCDAMM + MMDCHHAAHHHCDAMM + MMDCHAHAAHHCDAMM + MMMCCAAAAHCCAAMM + MMMDCCAAHCCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 44 (squeaky board) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCCCCCCDMMMM + MMMCCCHHHCCDAMMM + MMDCCHHNHHCDDAMM + MMDAAAAAAAAADAMM + MMDAAAAAAAAADAMM + MMDAAAAAAAAADAMM + MMDAAAAAAAAADAMM + MMMCCHHNHHCCAAMM + MMMDCCHHHCCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 45 (bear trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDAAPCPAADMMMM + MMMDAHHHHHADAMMM + MMDDAAPNPAACDMMM + MMDCANNNNNACDAMM + MMDCAAPNPAACDAMM + MMDCANNNNNACDAMM + MMDDAHHHHHACDAMM + MMMDAACHCAADAAMM + MMMDCAAAAACDAMMM + MMMMDDAAADDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 46 (land mine) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCCCCCCDMMMM + MMMCCCHHHCCDAMMM + MMDCCHHAHHCDDAMM + MMDCHHAAAHHCDAMM + MMDCAAAAAAACDAMM + MMDCAAAAAAACDAMM + MMDCHHNNNHHCDAMM + MMMCCHHNHHCCAAMM + MMMDCCHHHCCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 47 (rolling boulder trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCCCCCCDMMMM + MMMCCCHHHCCDAMMM + MMDCCAAAAHCDDAMM + MMDCAAHAAAHCDAMM + MMDCAHAAAAHCDAMM + MMDCAAAAAAHCDAMM + MMDCAAAAAAHCDAMM + MMMCCAAAAHCCAAMM + MMMDCCHHHCCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 48 (sleeping gas trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCHHHCCDMMMM + MMMCAAAAAAACAMMM + MMDCAANNAAACDAMM + MMDCHNNAAAHCDAMM + MMDCHNAAANHCDAMM + MMDCHAAANHHCDAMM + MMDCAAAHHHACDAMM + MMMCAAAAAAACAAMM + MMMDCCHHHCCDAMMM + MMMMDDCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 49 (rust trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCCCCCCDMMMM + MMMCCCHHHCCDAMMM + MMDCCAAAAACDDAMM + MMDCAAEEEAACDAMM + MMDCAEEEEEACDAMM + MMDCAEEEEEACDAMM + MMDCAAEEEAACDAMM + MMMCCAAAAACCAAMM + MMMDCCHHHCCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 50 (fire trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCCAAACCDMMMM + MMMCCAADAACDAMMM + MMDCAADDAAADDAMM + MMDCAADDDAACDAMM + MMDCADCHCDACDAMM + MMDCADHNHDACDAMM + MMDCAAHNHAACDAMM + MMMCCAANAACCAAMM + MMMDCCAAACCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 51 (pit) +{ + AAAAAAAAAAAAAAAA + AMAAAAAAAAAAAABA + AMMAAAAAAAAAABBA + AMMMAAAAAAAABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMMMMMABBBA + AMMMAAAAAAAABBBA + AMMMPPPPPPPPPBBA + AMMPPPPPPPPPPPBA + AMPPPPPPPPPPPPPA + AAAAAAAAAAAAAAAA +} +# tile 52 (spiked pit) +{ + AAAAAAAAAAAAAAAA + AMAAAAAAAAAAAABA + AMMAAAAAAAAAABBA + AMMMAAAAAAAABBBA + AMMMAMMMMMMABBBA + AMMMAMNMNMMABBBA + AMMMAMMMMMMABBBA + AMMMAMMNMNMABBBA + AMMMAMMMMMMABBBA + AMMMAMNMNMMABBBA + AMMMAMMMMMMABBBA + AMMMAAAAAAAABBBA + AMMMPPPPPPPPPBBA + AMMPPPPPPPPPPPBA + AMPPPPPPPPPPPPPA + AAAAAAAAAAAAAAAA +} +# tile 53 (hole) +{ + MMMMMMMMMMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMAAAAAABMMMM + MMMMAAAAAAABBMMM + MMMAAAAAAAAABBBM + MMMAAAAAAAAAABBM + MMMAAAAAAAAAABBM + MMMAAAAAAAAAABBM + MMMAAAAAAAAAABBM + MMMMAAAAAAAAABBM + MMMMMAAAAAAAABBM + MMMMMAAAAAAABBMM + MMMMMMAAAAABMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 54 (trap door) +{ + AAAAAAAAAAAAAAAA + AMAAAAAAAAAAAABA + AMMAAAAAAAAAABBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMAAAAAAAABBBA + AMMMPPPPPPPPPBBA + AMMPPPPPPPPPPPBA + AMPPPPPPPPPPPPPA + AAAAAAAAAAAAAAAA +} +# tile 55 (teleportation trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCACDDMMMMM + MMMDCCAAACCDMMMM + MMMDCAAAAACCAMMM + MMDDCHHAHHCCDMMM + MMDCOHAAAHOCDAMM + MMDCHOAAAOHCDAMM + MMDCHOAAAOHCDAMM + MMDDCHHAHHCCDAMM + MMMDCAAAAACCAAMM + MMMDCCAAACCDAMMM + MMMMDDCACDDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 56 (level teleporter) +{ + MMMMMMMMMMMMMMMM + MMMMMDDADDMMMMMM + MMMMDDAAADDMMMMM + MMMDCAAAAADDMMMM + MMMDCCCACCCCAMMM + MMDDCHAAAHCCDMMM + MMDCOAAOAAOCDAMM + MMDCHAOOOAHCDAMM + MMDCHAAOAAHCDAMM + MMDDCHAAAHCCDAMM + MMMDCCCACCCCAAMM + MMMDCAAAAACDAMMM + MMMMDDAAADDAAMMM + MMMMMDDADDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 57 (magic portal) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCACDDMMMMM + MMMDCCAAACCDMMMM + MMMDCAAAAACCAMMM + MMDDCHHAHHHADMMM + MMDDCHOAOAACDAMM + MMDDOAAAAAOCDAMM + MMDDAAOAOOCCDAMM + MMDAHHHAHHCCDAMM + MMMDCAAAAACCAAMM + MMMDCCAAACCDAMMM + MMMMDDCACDDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 58 (web) +{ + OAOAMOAMMMOMMMMO + MOMNNNNMNOAMMOOA + OONMNOMNAMMOOAMM + MNMMOOMNMNOAMMMM + MNMOMMMNOAMMMMMM + ONOMOMNAMMMMMMMM + MONNNNAMMMMMMMMM + MMOMNAMMMMMMMMMM + MOAMOAMMMMMMMMMM + MOONAMMMMMMMMMMM + OAMOAMMMMMMMMMMM + MMOAMMMMMMMMMMMM + MMOAMMMMMMMMMMMM + MOAMMMMMMMMMMMMM + MOAMMMMMMMMMMMMM + OAMMMMMMMMMMMMMM +} +# tile 59 (statue trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCAADDMMMMM + MMMDCCAJJACDMMMM + MMMCCAKCJJADAMMM + MMDAAAKKJJADDAMM + MMACKACKJAAADAMM + MMAJJCKKJACJAAMM + MMDAJCKJJJJJAAMM + MMDAJCKJJJAADAMM + MMMCACKJAACCAAMM + MMMDACKJACCDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 60 (magic trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDAMMMMM + MMMDCCCCCCADMMMM + MMMCCCHHHAADAMMM + MMDCAHHNHAADDAMM + MMDCAANNAAACDAMM + MMDCAAAAAAACDAMM + MMDCAAANNAACDAMM + MMDCAANNNHACDAMM + MMMCAAHNHHCCAAMM + MMMDACHHHCCDAMMM + MMMMACCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 61 (anti-magic field) +{ + MMMMMMMMMMMMMMMM + MMMMMMDDDDDMMMMM + MMMMMADCCCDDMMMM + MMMMDACCCCCCDMMM + MMMADAAHHHCCCMMM + MMADDAAHNHHACDMM + MMADCAAANNAACDMM + MMADCAAAAAAACDMM + MMADCAANNAAACDMM + MMADCAHNNNAACDMM + MMAACCHHNHAACMMM + MMMADCCHHHCADMMM + MMMAADCCCCCAMMMM + MMMMAADDDDDMMMMM + MMMMMAAAAAMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 62 (polymorph trap) +{ + MMMMMMMMMMMMMMMM + MMMMMDDDDDMMMMMM + MMMMDDCCCDDMMMMM + MMMDCAAAPCCDMMMM + MMMCCAAPHCCDAMMM + MMDCCAPAAHCDDAMM + MMDCHPNAAHHCDAMM + MMDCHNPAPNHCDAMM + MMDCHNAANPHCDAMM + MMDCHHAAPAHCDAMM + MMMCCHHPAACCAAMM + MMMDCCPAAACDAMMM + MMMMDCCCCCDAAMMM + MMMMMDDDDDAAMMMM + MMMMMMAAAAAMMMMM + MMMMMMMMMMMMMMMM +} +# tile 63 (wall) +{ + MMMMMMMNNMMMMMMM + MMMMMMNNMMMMMMMM + MMMMMNNMMMMMMMMM + MMMMMMNNMMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMMNNMMMMMM + MMMMMMMMMNNMMMMM + MMMMMMMMNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNMMMMMMMM + MMMMMNNMMMMMMMMM + MMMMMMNNMMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMMNNMMMMMM + MMMMMMMMMNNMMMMM + MMMMMMMMNNMMMMMM +} +# tile 64 (wall) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMNMMMMMMMNMMMMM + MNNNMMMMMNNNMMMM + NNMNNMMMNNMNNMMM + NMMMNNMNNMMMNNMN + MMMMMNNNMMMMMNNN + MMMMMMNMMMMMMMNM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 65 (wall) +{ + NNNNNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNNNNNNNNNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNNNN +} +# tile 66 (wall) +{ + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMMMMMMMMMNMMM + MMMMNNNNNNNNNMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + MMMMNMMMMMMMMMMM + NNNNNMMMMMMMMMMM +} +# tile 67 (cmap 67) +{ + MMMMAAAAMMMMMMMM + MMAMMMMAAMMAAMMM + MAAMAAAMMMMMMAAM + MAAMAMMMMAMMAAMM + MMAMMMMAMAMAMAAM + MAMMAAAMAMMMMAAM + MAMAAMMMMMAMAAMM + MMMAAMMMMAAMMMMM + MAMAMMMAMMMAMAMM + MMMAMMMMMMAAMAAM + MAMAAAMAAAAMAMAM + MAAMMAAMMMMMMMAM + MMAMMMAAMMAMMAAM + MMAAAAAMMAAAAAMM + MMMMAAMMMMAAAMMM + MMMMMMMMMMMMMMMM +} +# tile 68 (cmap 68) +{ + MMMMMMMMMMMMMMMM + MMMMMNNNNNNMMMMM + MMMNNNNNNNNNNMMM + MMNNNNNNNNNNNNMM + MMNNNNNNNNNNNNMM + MNNNNNNNNNNNNNNM + MNNNNNNNNNNNNNNM + MNNNNNNNNNNNNNNM + MNNNNNNNNNNNNNNM + MNNNNNNNNNNNNNNM + MNNNNNNNNNNNNNNM + MMNNNNNNNNNNNNMM + MMNNNNNNNNNNNNMM + MMMNNNNNNNNNNMMM + MMMMMNNNNNNMMMMM + MMMMMMMMMMMMMMMM +} +# tile 69 (cmap 69) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMKKMMMMMMMMMM + MMMMKHKAMMMMMMMM + MMMMMKHKAMMMMMMM + MMMMMMKKKAMMMMMM + MMMMMMMKKKAMMMMM + MMMMMMMMKDKAMMMM + MMMMMMMMJDKAMMMM + MMMMMMMJKDJAMMMM + MMMMMMJKDJAMMMMM + MMMMMJHDJAMMMMMM + MMMMJHDJAMMMMMMM + MMMMMKJAMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 70 (cmap 70) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMJKMMMMM + MMMMMMMMJDHJMMMM + MMMMMMMJDHJAMMMM + MMMMMMJDKJAMMMMM + MMMMMJDKJAMMMMMM + MMMMMKDJAMMMMMMM + MMMMMKDKAMMMMMMM + MMMMMMKKKAMMMMMM + MMMMMMMKKKAMMMMM + MMMMMMMMKHKAMMMM + MMMMMMMMMKHKAMMM + MMMMMMMMMMKKAMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 71 (cmap 71) +{ + MMMMMMMMMMMMMMMM + MMMMMIMMMMIMMMMM + MMMMIMMMMMMIMMMM + MMMIMMMMMMMMIMMM + MMIMMMIIIIMMMIMM + MIMMMMMMMMMMMMIM + MMMMIMMIIMMIMMMM + MMMMIMIIIIMIMMMM + MMMMIMIIIIMIMMMM + MMMMIMMIIMMIMMMM + MIMMMMMMMMMMMMIM + MMIMMMIIIIMMMIMM + MMMIMMMMMMMMIMMM + MMMMIMMMMMMIMMMM + MMMMMIMMMMIMMMMM + MMMMMMMMMMMMMMMM +} +# tile 72 (cmap 72) +{ + MMMMMMMMMMMMMMMM + MCCCCCCCCCCCCCCC + MCMMMMMMMMMMMMMM + MCMCCCCCCCCCCCCM + MCMCMMMMMMMMMMCM + MCMCMCCCCCCCCMCM + MCMCMCMMMMMMCMCM + MCMCMCMCCCCMCMCM + MCMCMCMCCMCMCMCM + MCMCMCMMMMCMCMCM + MCMCMCCCCCCMCMCM + MCMCMMMMMMMMCMCM + MCMCCCCCCCCCCMCM + MCMMMMMMMMMMMMCM + MCCCCCCCCCCCCCCM + MMMMMMMMMMMMMMMM +} +# tile 73 (cmap 73) +{ + MMMMMMMHHMMMMMMM + MMMMMMMHHMMMMMMM + MMMMHHMHHMHHMMMM + MMMHMMMHHMMMHMMM + MMHMHMMMMMMHMHMM + MMHMMHMMMMHMMHMM + MMMMMMHMMHMMMMMM + HHHHMMMHHMMMHHHH + HHHHMMMHHMMMHHHH + MMMMMMHMMHMMMMMM + MMHMMHMMMMHMMHMM + MMHMHMMMMMMHMHMM + MMMHMMMHHMMMHMMM + MMMMHHMHHMHHMMMM + MMMMMMMHHMMMMMMM + MMMMMMMHHMMMMMMM +} +# tile 74 (cmap 74) +{ + MMMMMMMMMMMMMMMM + MMMMMMNNNNNMMMMM + MMMMMMMMNMMMMMMM + MMMNNNNMNMNNNMMM + MMMNMMNMNMNMNMMM + MNMNNNNMNMNMNMMM + MNMMMMMMNMNNNMNM + MNNNNNNNNMMMMMNM + MNMMMMMNNNNNNNNM + MNMNNNMNMMMMMMNM + MMMNMNMNMNNNNMNM + MMMNMNMNMNMMNMMM + MMMNNNMNMNNNNMMM + MMMMMMMNMMMMMMMM + MMMMMNNNNNMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 75 (cmap 75) +{ + AAAAAAADDDDDDAAA + AAAAADDDDDDDDDDD + AAAADDDDDDDDDDDD + AAADDDDDDCCDDDDD + AAADDDCCCCCCDDDD + AADDDDCCCCCDDDDD + AADDDCCCCCDDDDDD + AADDDCCCCDDDDDMM + AADDDCCCDDDDMMMM + AADDDCCCDDDDMMMM + AADDDDDDDDDMMMMM + AAADDDDDDDMMMMMM + AAADDDDDDDMMMMMM + AAAADDDDDDMMMMMM + AAAADDDDDDMMMMMM + AAAADDDDDDMMMMMM +} +# tile 76 (cmap 76) +{ + AAAAAAAAAAAAAAAA + DDAAAAAAAAAAAAAA + DDDDAAAAAAAAAADD + DDDDDDDDDDDDDDDD + DDDDDDDDDDDDDDDD + DDDDDDDDDDDDDDDD + DDDDDDDCCCCCCDDD + MDDDDDDDDCCCCCDD + MMMDDDDDDDDDDDDD + MMMMDDDDDDDDDDDD + MMMMMMDDDDDDDDMM + MMMMMMMDDDDMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 77 (cmap 77) +{ + AAAAAAAAAAAAAAAA + AAADDDDDAAAAAAAA + DDDDDDDDDDDAAAAA + DDDDDDDDDDDDAAAA + DDDDDCCCDDDDDAAA + DDDDDCCCCDDDDDAA + DDDDDCCCCDDDDDDA + DDDDDCCCCCDDDDDD + DDDDDDCCCCCDDDDD + DDDDDDDCCCCDDDDD + MMMDDDDDCCCDDDDD + MMMDDDDDDCCDDDDD + MMMMDDDDDDDDDDDD + MMMMDDDDDDDDDDDD + MMMMDDDDDDDDDDDD + MMMMDDDDDDDDDDDA +} +# tile 78 (cmap 78) +{ + AAAADDDDDDMMMMMM + AAAADDDDDDDMMMMM + AAAADDDDDDDMMMMM + AAAADDDDDDDDMMMM + AAAADDCCCDDDMMMM + AAADDDDCCDDDMMMM + AADDDDCCCDDDMMMM + AADDDDCCCDDDMMMM + AADDDDCDDDDDMMMM + ADDDDDDDDDDMMMMM + ADDDDDDDDDMMMMMM + DDDDDDDDDMMMMMMM + DDDDDDDDMMMMMMMM + DDDDDDDMMMMMMMMM + DDDDDDDMMMMMMMMM + DDCCDDDMMMMMMMMM +} +# tile 79 (cmap 79) +{ + MMMMDDDDDDDDDDDA + MMMMDDDDDDDDDDDA + MMMMDDDDDDDDDDAA + MMMMDDDDDDDDDDAA + MMMDDDDDDDDDDDAA + MMDDDCCDDDDDDAAA + MMDDDCCDDDDDDAAA + MMDDDCCDDDDDAAAA + MMDDDCCDDDDDAAAA + MMDDDDDDDDDDAAAA + MMMDDDDDDDDDAAAA + MMMDDDDDDDDDAAAA + MMMMDDDDDDDDDAAA + MMMMMDDDDDDDDAAA + MMMMMMDDDDDDDDAA + MMMMMMMDDDDDDDAA +} +# tile 80 (cmap 80) +{ + DDDCDDDMMMMMMMMM + DDDCDDDMMMMMMMMM + ADDCDDDMMMMMMMMM + ADDDDDDDMMMMMMMM + ADDDDDDDDDDDDMMM + AADDDDDDDDDDDDDD + AADDDDDDDDDDDDDD + AAADDDDDDDDCDDDD + AAAAADDDDDDCCCCD + AAAAAAADDDDDCCCD + AAAAAAAAADDDDCCC + AAAAAAAAAADDDDCC + AAAAAAAAAAADDDDD + AAAAAAAAAAADDDDD + AAAAAAAAAAAADDDD + AAAAAAAAAAAAAADD +} +# tile 81 (cmap 81) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMDDD + MMMMMMMMMMMMDDDD + DMMMMMMMMMMMDDDD + DDMMMMMMMMMMDDDC + DDDMMMMMMMMMDDDC + DDDDMMMMMMMMDDDC + DDDDDDMMMMDDDDDC + DDDDDDDDDDDDDDDC + DDDDDDDDDDDDDDDC + DDDDDDDDDDDDDDDD + DDDDDDDDDDDDDDDD + DDDDDDDDDDDDDDAA +} +# tile 82 (cmap 82) +{ + MMMMMMDDDDDDDDAA + MMMMMMDDDDDDDDDA + MMMMMDDDDDDDDDDA + MMMMDDDDDDDDDDDA + DDDDDDDDDDDDDDDA + DDDDDDDDDDDDDDDA + CCCDDDDDDDDDDDAA + CCCCDDDDDDDDDDAA + CCCDDDDDDDDDDDAA + CCDDDDDDDDDDDAAA + CCDDDDDDAAAAAAAA + CDDDDDAAAAAAAAAA + CDDDDAAAAAAAAAAA + DDDDAAAAAAAAAAAA + DDAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA +} +# tile 83 (explosion dark 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMAAA + MMMMMMAMMMAAAAAA + MMMMMMMMMAAAAAAA + MMMMMMMMMAAAAAAM + MMMMMMMMAAAAMMMA + MMMAAAMAAAAMMMAA + MAMMMAAAAAAMAAAA + MMMMMAAAAAAMAAAA + MMMMAAAAAAAMAAAA + MMMMMMMMAAMMAAMM + MMMMAAAAAAAAAAMM + MMMAAAAMMAAAMMMM + MMAAAAMMAAAAMMMM + MMAAAMMAAAAAMMMM +} +# tile 84 (explosion dark 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + AMMMMMMMMAAAAAAM + AAAAAAMAAAAAAAAM + AAAAAAMMMMMAAAAA + MMMAAAMAAAAAAAAA + AAAAAAMAAAAAAAAA + AAAAAAMAAAAAAAAM + AAMMMMMMMMMMMAMM + MMAAAMAMMMMMAAAA + MAMAMAMMMAMMMAAA + MPPAMMMAAMMMMMAA + MAMMMMMAMMMMAAAA + MMMMMAMMAMMMPAAA + MMMMMMMMAMAMAPAA + MPAMMMAPAAAAAAAA +} +# tile 85 (explosion dark 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMAMMMAMMM + AAAAAMMMMMMMMMMM + AAAAAAMMMAMMAMMM + AAAAAAAMMMMMMMMM + MMMMMMAAAAMMMMMM + AAAAAMMAAAAMMMMM + MMAAAAAAAAAAMMMM + AAMAAMMMMAAAMMMM + AAMAAAAAMAAAMMMM + AAPAAMAAAAAAMAMM + AAAMMMMMAAAAMMMM + AAMAMMAMAAAAMMMM + AMAAMMAMAAAAAMMM +} +# tile 86 (explosion dark 3) +{ + MMAAAMAMAAAMMMMM + MMAAAMAAAAAMAPMM + MMMAAMAAMMMMMMMA + MMMAAAAAAMMMMPPM + MMAAAMAAMMAMMPAP + MAAAMMMAMMMMPMAP + MAAAMAAAMAMMPMMP + MAAAMAAAMMMAAAAP + MAAAMAAAMMMMPAAA + MAAAMMAAMMMMPPAA + MMAAAMMAAMMMPAAP + MMMAAAMMMAPMPPPP + MMMMAAAMMMPMMPMA + MMMMMAAMMMMMAAAM + MMAMAMMMAAPMMMPA + MMMMAMMMMMMMMMPA +} +# tile 87 (explosion dark 4) +{ + APAAAMMPPAPAAAAA + MAPAMMAMAAAPAAAM + AAPMPAAMMAAAAAAM + PAPPPAAAMMAAPAMM + AAAPPAAAAPAAPMMA + AAPPMPPPAAAAAMMA + AAPAAAAAAAAAAAMM + APPAAAAAAAAAAAMM + AAAAAAAAAAAAPAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAMAA + AAAAAAAAAAAMHHMM +} +# tile 88 (explosion dark 5) +{ + MMAAAMAMAMAAAAMM + MMAAAAAMAMAAAAMM + MMAAAAAMAMAAAAMM + PMAAAAAMMMAAAAMM + PMAAPAPAAMAAAAMM + PMMMPMMAMMAAAMMM + MMMAPMMAAAAAAMMM + MPAAPAAAAAAAAMMM + AAPMMMPAAAAAMMMM + MMAAMPPAAMAMMMAM + AMMMPPMMMMMMMMMM + MAAAAPPAAMMMAMMM + MMAAMPPMAAAMMMMM + AMMMMMMAAAAAMMMM + AAAMMMAAAAAAAMMM + MMMMPPAAAAAAAAMM +} +# tile 89 (explosion dark 6) +{ + MMMMAMMMMMMMAMMP + MMMMAMMMMMMMAAMM + MAMMAAMMMPMMAMAM + MMMAAAMAMMPMMAAP + MMMAAAAMMAAPMMAM + MMMAAAAMMMMPMMMA + MMMAAAAMMAMMAMMA + MMMAAAAAMAAAMMMM + MMMAAAAAMMAAMMMM + MMMMAAAAAAAAAMMM + MMMMMAAAAAAAAAAA + MMMAMMAAAAAAAAAA + MMMMMMMMAAMAAAAA + MMMAMMMMMMMMAAAA + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 90 (explosion dark 7) +{ + PPPAAAAAPAAAMAMM + AAPPAAPPPPAMAMMM + PPPPPPPPPPAMMAPP + MPPAAMAAMAPAAMMM + MMAAAAMAMMAAAPAA + APPAPAPMAMAMPAAM + AMMPPAAAMMMMMMMP + AAMMMMMMMAMMMMMM + PAPAMAAAAAAAAAAP + AAMAPAAAAMMMMMMA + AAAMAMPPMMMAAAAA + AAAAAAMAMMAAAAAA + AAAAAAAAAAAAAAAA + AAAAMMMMMMMMMMAA + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 91 (explosion dark 8) +{ + MMMMPMMAAAAAAAMM + MMAMMAMAAMAMAAMM + PPMMAMMAAMAMAAMM + MMMAAAMAAMAMAAMM + MAAAAAMAAAMMAAMM + MMPAAMMAAAMAAAMM + MAAAMMAAAMMAAAMM + MAMAAMAAAAAAAAMM + AAMAAAAAAMAAAAMM + MMMAAAAAAAAAAAMM + MAAAAAAAAMAAAMMM + AAAAAAAAMMMMMMMM + AAAAAAMMMMAMMMMM + AAAMMMAMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 92 (explosion noxious 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMFFF + MMMMMMFMMMFFFFFF + MMMMMMMMMFFFFFFF + MMMMMMMMMFFFFFFM + MMMMMMMMFFFFMMMF + MMMFFFMFFFFMMMFF + MFMMMFFFFFFMFFFF + MMMMMFFFFFFMFFFF + MMMMFFFFFFFMFFFF + MMMMMMMMFFMMFFMM + MMMMFFFFFFFFFFMM + MMMFFFFMMFFFMMMM + MMFFFFMMFFFFMMMM + MMFFFMMFFFFFMMMM +} +# tile 93 (explosion noxious 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + FMMMMMMMMFFFFFFM + FFFFFFMFFFFFFFFM + FFFFFFMMMMMFFFFF + MMMFFFMFFFFFFFFF + FFFFFFMFFFFFFFFF + FFFFFFMFFFFFFFFM + FFMMMMMMMMMMMFMM + MMFFFMFMMMMMFFFF + MHMFMFMMMFMMMFFF + MGGFMMMFFMMMMMFF + MHMMMMMFMMMMFFFF + MMMMMFMMFMMMGFFF + MMMMMMMMFMFMFGFF + MGHMMMHGHHFFFFFF +} +# tile 94 (explosion noxious 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMFMMMFMMM + FFFFFMMMMMMMMMMM + FFFFFFMMMFMMFMMM + FFFFFFFMMMMMMMMM + MMMMMMFFFFMMMMMM + FFFFFMMFFFFMMMMM + MMFFFFFFFFFFMMMM + FFMFFMMMMFFFMMMM + FFMFFFFFMFFFMMMM + FFGFFMFFFFFFMFMM + FFFMMMMMFFFFMMMM + FFMFMMFMFFFFMMMM + FMFFMMFMFFFFFMMM +} +# tile 95 (explosion noxious 3) +{ + MMFFFMFMFFFMMMMM + MMFFFMFFFFFMFGMM + MMMFFMFFMMMMMMMF + MMMFFFFFFMMMMGGM + MMFFFMFFMMFMMGFG + MFFFMMMFMMMMGMFG + MFFFMFFFMHMMGMMG + MFFFMFFFMMMFFFFG + MFFFMFFFMMMMGFHH + MFFFMMFFMMMMGGHH + MMFFFMMFFMMMGHHG + MMMFFFMMMFGMGGGG + MMMMFFFMMMGMMGMH + MMMMMFFMMMMMHHFM + MMFMFMMMFHGMMMGH + MMMMFMMMMMMMMMGH +} +# tile 96 (explosion noxious 4) +{ + FGHFFMMGGFGHFFFF + MHGHMMFMFFHGFHFM + HFGMGHFMMHHHFFHM + GFGGGHHHMMHHGFMM + HHHGGHHHHGHHGMMF + HHGGMGGGHHHHFMMF + HHGHHHHHHHHHHFMM + HGGHHHHHHHHHHHMM + HHHHHHNNNNHHGHNH + GHHHHHHHNHHHHHNH + GHHGHGNNNNHHHHGF + GGNHNHNNNNHHHGGF + HHHHNHNNNHHHGGGG + HGGNGHNNNHHHGGGF + HHHHNHNHNMGGGMGF + GGGGNHHHGGGMHHMM +} +# tile 97 (explosion noxious 5) +{ + MMFFFMFMFMFFFFMM + MMFFFFFMFMFFFFMM + MMFFFFFMFMFFFFMM + GMFFFFFMMMFFFFMM + GMFFGFGFFMFFFFMM + GMMMGMGFMMFFFMMM + MMMFGMMFFFFFFMMM + MGFFFFFFFFFFFMMM + FFGMMMGFFFFFMMMM + MMFFMGGFFMFMMMFM + FMMMGGMMMMMMMMMM + MFFFFGGFFMMMFMMM + MMHFMGGMFFFMMMMM + HMMMMMMFFFFFMMMM + HFHMMMFFFFFFFMMM + MMMMGGFFFFFFFFMM +} +# tile 98 (explosion noxious 6) +{ + MMMMFMMMMMMMHMMG + MMMMFMMMMMMMFHMM + MFMMFFMMMGMMFMHM + MMMFFFMFMMGMMFFG + MMMFFFFMMFFGMMFM + MMMFFFFMMMMGMMMF + MMMFFFFMMFMMHMMF + MMMFFFFFMFFFMMMM + MMMFFFFFMMFFMMMM + MMMMFFFFFFFFFMMM + MMMMMFFFHFFFFFFF + MMMFMMFFFFFFFFFF + MMMMMMMMFFMFFFFF + MMMFMMMMMMMMFFFF + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 99 (explosion noxious 7) +{ + GGGHHHHHGHHHMHMM + HHGGHHGGGGHMFMMM + GGGGGGGGGGFMMFGG + MGGHFMFFMHGFFMMM + MMHHHHMFMMFFHGHF + HGGFGFGMHMHMGFFM + FMMGGFFHMMMMMMMG + HFMMMMMMMFMMMMMM + GHGFMFFFFFFFFFFG + HFMHGHFFFMMMMMMF + FFFMFMGGMMMFFFFF + FFFFFFMFMMFFFFFF + FFFFFFFFFFFFFFFF + FFFFMMMMMMMMMMFF + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 100 (explosion noxious 8) +{ + MMMMGMMFFFFFFFMM + MMFMMFMFFMFMFFMM + GGMMFMMFFMFMFFMM + MMMFFFMFFMFMFFMM + MFFFFFMFFFMMFFMM + MMGFFMMFFFMFFFMM + MFFFMMFFFMMFFFMM + MFMFFMFFFFFFFFMM + FFMFFFFFFMFFFFMM + MMMFFFFFFFFFFFMM + MFFFFFFFFMFFFMMM + FFFFFFFFMMMMMMMM + FFFFFFMMMMFMMMMM + FFFMMMFMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 101 (explosion muddy 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMJJJ + MMMMMMJMMMJJJJJJ + MMMMMMMMMJJJJJJJ + MMMMMMMMMJJJJJJK + MMMMMMMMJJJJKKKJ + MMMJJJMJJJJKKKJJ + MJMMMJJJJJJKJJJJ + MMMMMJJJJJJKJJJJ + MMMMJJJJJJJKJJJJ + MMMMMMMKJJKKJJKK + MMMMJJJJJJJJJJKK + MMMJJJJKKJJJKKKK + MMJJJJKKJJJJKKKK + MMJJJKKJJJJJKKKK +} +# tile 102 (explosion muddy 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + JMMMMMMMMJJJJJJM + JJJJJJKJJJJJJJJM + JJJJJJKKKKKJJJJJ + KKKJJJKJJJJJJJJJ + JJJJJJKJJJJJJJJJ + JJJJJJKJJJJJJJJK + JJKKKKKKKKKKKJKK + KKJJJKJKKKKKJJJJ + KLKJKJKKKJKKKJJJ + KCCJKKKJJKKKKKJJ + KLKKKKKJKKKKJJJJ + KKKKKJKKJKKKCJJJ + KKKKKKKKJKJKJCJJ + KCLKKKLCLLJJJJJJ +} +# tile 103 (explosion muddy 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMJMMMJMMM + JJJJJMMMMMMMMMMM + JJJJJJMMMJMMJMMM + JJJJJJJMMMMMMMMM + KKKKKKJJJJMMMMMM + JJJJJKKJJJJMMMMM + KKJJJJJJJJJJMMMM + JJKJJKKKKJJJMMMM + JJKJJJJJKJJJMMMM + JJCJJKJJJJJJMJMM + JJJKKKKKJJJJMMMM + JJKJKKJKJJJJMMMM + JKJJKKJKJJJJJMMM +} +# tile 104 (explosion muddy 3) +{ + MMJJJKJKJJJKKKKK + MMJJJKJJJJJKJCKK + MMMJJKJJKKKKKKKJ + MMMJJJJJJKKKKCCK + MMJJJKJJKKJKKCJC + MJJJKKKJKKKKCKJC + MJJJKJJJKLKKCKKC + MJJJKJJJKKKJJJJC + MJJJKJJJKKKKCJLL + MJJJKKJJKKKKCCLL + MMJJJKKJJKKKCLLC + MMMJJJKKKJCKCCCC + MMMMJJJKKKCKKCKL + MMMMMJJKKKKKLLJK + MMJMJKKKJLCKKKCL + MMMMJKKKKKKKKKCL +} +# tile 105 (explosion muddy 4) +{ + JCLJJKKCCJCLJJJJ + KLCLKKJKJJLCJLJK + LJCKCLJKKLLLJJLK + CJCCCLLLKKLLCJKK + LLLCCLLLLCLLCKKJ + LLCCKCCCLLLLJKKJ + LLCLLLLLLLLLLJKK + LCCLLLLLLLLLLLKK + LLLLLLCCCCLLCLCL + CLLLLLLLCLLLLLCL + CLLCLCCCCCLLLLCJ + CCCLCLCCCCLLLCCJ + LLLLCLCCCLLLCCCC + LCCCCLCCCLLLCCCJ + LLLLCLCLCKCCCKCJ + CCCCCLLLCCCKLLKK +} +# tile 106 (explosion muddy 5) +{ + KKJJJKJKJKJJJJMM + KKJJJJJKJKJJJJMM + KKJJJJJKJKJJJJMM + CKJJJJJKKKJJJJMM + CKJJCJCJJKJJJJMM + CKKKCKKJKKJJJMMM + KKKJCKKJJJJJJMMM + KCJJCJJJJJJJJMMM + JJCKKKCJJJJJMMMM + KKJJKCCJJKJMMMJM + JKKKCCKKKKMMMMMM + KJJJJCCJJKMMJMMM + KKLJKCCKJJJMMMMM + LKKKKKKJJJJJMMMM + LJLKKKJJJJJJJMMM + KKKKCCJJJJJJJJMM +} +# tile 107 (explosion muddy 6) +{ + MMMMJKKKKKKKLKKC + MMMMJKKKKKKKJLKK + MJMMJJKKKCKKJKLK + MMMJJJKJKKCKKJJC + MMMJJJJKKJJCKKJK + MMMJJJJKKKKCKKKJ + MMMJJJJKKJKKLKKJ + MMMJJJJJKJJJKKKK + MMMJJJJJKKJJKKKK + MMMMJJJJJJJJJKKK + MMMMMJJJLJJJJJJJ + MMMJMMJJJJJJJJJJ + MMMMMMMMJJMJJJJJ + MMMJMMMMMMMMJJJJ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 108 (explosion muddy 7) +{ + CCCLLLLLCLLLKLKK + LLCCLLCCCCLKJKKK + CCCCCCCCCCJKKJCC + KCCLJKJJKLCJJKKK + KKLLLLKJKKJJLCLJ + LCCJCJCKLKLKCJJK + JKKCCJJLKKKKKKKC + LJKKKKKKKJKKKKKK + CLCJKJJJJJJJJJJC + LJKLCLJJJKKKKKKJ + JJJKJKCCKKKJJJJJ + JJJJJJKJKKJJJJJJ + JJJJJJJJJJJJJJJJ + JJJJMMMMMMMMMMJJ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 109 (explosion muddy 8) +{ + KKKKCKKJJJJJJJMM + KKJKKJKJJKJKJJMM + CCKKJKKJJKJKJJMM + KKKJJJKJJKJKJJMM + KJJJJJKJJJKKJJMM + KKCJJKKJJJKJJJMM + KJJJKKJJJKKJJJMM + KJKJJKJJJJJJJJMM + JJKJJJJJJKJJJJMM + KKKJJJJJJJJJJJMM + KJJJJJJJJMJJJMMM + JJJJJJJJMMMMMMMM + JJJJJJMMMMJMMMMM + JJJMMMJMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 110 (explosion wet 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMEEE + MMMMMMEMMMEEEEEE + MMMMMMMMMEEEEEEE + MMMMMMMMMEEEEEEP + MMMMMMMMEEEEPPPE + MMMEEEMEEEEPPPEE + MEMMMEEEEEEPEEEE + MMMMMEEEEEEPEEEE + MMMMEEEEEEEPEEEE + MMMMMMMPEEPPEEPP + MMMMEEEEEEEEEEPP + MMMEEEEPPEEEPPPP + MMEEEEPPEEEEPPPP + MMEEEPPEEEEEPPPP +} +# tile 111 (explosion wet 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + EMMMMMMMMEEEEEEM + EEEEEEPEEEEEEEEM + EEEEEEPPPPPEEEEE + PPPEEEPEEEEEEEEE + EEEEEEPEEEEEEEEE + EEEEEEPEEEEEEEEP + EEPPPPPPPPPPPEPP + PPEEEPEPPPPPEEEE + PNPEPEPPPEPPPEEE + PBBEPPPEEPPPPPEE + PNPPPPPEPPPPEEEE + PPPPPEPPEPPPBEEE + PPPPPPPPEPEPEBEE + PBNPPPNBEEEEEEEE +} +# tile 112 (explosion wet 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMEMMMEMMM + EEEEEMMMMMMMMMMM + EEEEEEMMMEMMEMMM + EEEEEEEMMMMMMMMM + PPPPPPEEEEMMMMMM + EEEEEPPEEEEMMMMM + PPEEEEEEEEEEMMMM + EEPEEPPPPEEEMMMM + EEPEEEEEPEEEMMMM + EEBEEPEEEEEEMEMM + EEEPPPPPEEEEMMMM + EEPEPPEPEEEEMMMM + EPEEPPEPEEEEEMMM +} +# tile 113 (explosion wet 3) +{ + MMEEEPEPEEEPPPPP + MMEEEPEEEEEPEBPP + MMMEEPEEPPPPPPPE + MMMEEEEEEPPPPBBP + MMEEEPEEPPEPPBEB + MEEEPPPEPPPPBPEB + MEEEPEEEPNPPBPPB + MEEEPEEEPPPEEEEB + MEEEPEEEPPPPBEEE + MEEEPPEEPPPPBBEE + MMEEEPPEEPPPBEEB + MMMEEEPPPEBPBBBB + MMMMEEEPPPBPPBPN + MMMMMEEPPPPPNNEP + MMEMEPPPENBPPPBE + MMMMEPPPPPPPPPBE +} +# tile 114 (explosion wet 4) +{ + EBNEEPPBBEBNEEEE + PNBNPPEPEEEBENEP + NEBPBEEPPEEEEENP + BEBBBEEEPPEEBEPP + EEEBBEEEEBEEBPPE + EEBBPBBBEEEEEPPE + EEBEEEEEEEEEEEPP + EBBEEEEEEEEEEEPP + EEEEEEEEEEEEBEEE + BEEEEEEEEEEEEEEE + BEEBEBEEEEEEEEBE + BBEEEEEEEEEEEBBE + EEEEEEEEEEEEBBBB + EBBEBEEEEEEEBBBE + EEEEEEEEEPBBBPBE + BBBBEEEEBBBPNNPP +} +# tile 115 (explosion wet 5) +{ + PPEEEPEPEPEEEEMM + PPEEEEEPEPEEEEMM + PPEEEEEPEPEEEEMM + BPEEEEEPPPEEEEMM + BPEEBEBEEPEEEEMM + BPPPBPPEPPEEEMMM + PPPEBPPEEEEEEMMM + PBEEBEEEEEEEEMMM + EEBPPPBEEEEEMMMM + PPEEPBBEEPEMMMEM + EPPPBBPPPPMMMMMM + PEEEEBBEEPMMEMMM + PPNEPBBPEEEMMMMM + NPPPPPPEEEEEMMMM + NENPPPEEEEEEEMMM + PPPPBBEEEEEEEEMM +} +# tile 116 (explosion wet 6) +{ + MMMMEPPPPPPPNPPB + MMMMEPPPPPPPENPP + MEMMEEPPPBPPEPNP + MMMEEEPEPPBPPEEB + MMMEEEEPPEEBPPEP + MMMEEEEPPPPBPPPE + MMMEEEEPPEPPNPPE + MMMEEEEEPEEEPPPP + MMMEEEEEPPEEPPPP + MMMMEEEEEEEEEPPP + MMMMMEEENEEEEEEE + MMMEMMEEEEEEEEEE + MMMMMMMMEEMEEEEE + MMMEMMMMMMMMEEEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 117 (explosion wet 7) +{ + BBBEEEEEBEEEPEPP + EEBBEEBBBBEPEPPP + BBBBBBBBBBEPPEBB + PBBEEPEEPNBEEPPP + PPEEEEPEPPEENBNE + NBBEBEBPNPNPBEEP + EPPBBEENPPPPPPPB + NEPPPPPPPEPPPPPP + BNBEPEEEEEEEEEEB + NEPNBNEEEPPPPPPE + EEEPEPBBPPPEEEEE + EEEEEEPEPPEEEEEE + EEEEEEEEEEEEEEEE + EEEEMMMMMMMMMMEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 118 (explosion wet 8) +{ + PPPPBPPEEEEEEEMM + PPEPPEPEEPEPEEMM + BBPPEPPEEPEPEEMM + PPPEEEPEEPEPEEMM + PEEEEEPEEEPPEEMM + PPBEEPPEEEPEEEMM + PEEEPPEEEPPEEEMM + PEPEEPEEEEEEEEMM + EEPEEEEEEPEEEEMM + PPPEEEEEEEEEEEMM + PEEEEEEEEMEEEMMM + EEEEEEEEMMMMMMMM + EEEEEEMMMMEMMMMM + EEEMMMEMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 119 (explosion magical 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMEEE + MMMMMMEMMMEEEEEE + MMMMMMMMMEEEEEEE + MMMMMMMMMEEEEEEC + MMMMMMMMEEEEIIIE + MMMEEEMEEEEIIIEE + MEMMMEEEEEEIEEEE + MMMMMEEEEEEIEEEE + MMMMEEEEEEEIEEEE + MMMMMMMIEEIIEEII + MMMMEEEEEEEEEEII + MMMEEEEIIEEEIIII + MMEEEEIIEEEEIIII + MMEEEIIEEEEEIIII +} +# tile 120 (explosion magical 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + EMMMMMMMMEEEEEEM + EEEEEEIEEEEEEEEM + EEEEEEIIIIIEEEEE + IIIEEEIEEEEEEEEE + EEEEEEIEEEEEEEEE + EEEEEEIEEEEEEEEI + EEIIIIIIIIIIIEII + IIEEEIEIIIIIEEEE + IHIEIEIIIEIIIEEE + ILLEIIIEEIIIIIEE + IHIIIIIEIIIIEEEE + IIIIIEIIEIIILEEE + IIIIIIIIEIEIELEE + ILHIIIHLHHEEEEEE +} +# tile 121 (explosion magical 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMEMMMEMMM + EEEEEMMMMMMMMMMM + EEEEEEMMMEMMEMMM + EEEEEEEMMMMMMMMM + IIIIIIEEEEMMMMMM + EEEEEIIEEEEMMMMM + IIEEEEEEEEEEMMMM + EEIEEIIIIEEEMMMM + EEIEEEEEIEEEMMMM + EELEEIEEEEEEMEMM + EEEIIIIIEEEEMMMM + EEIEIIEIEEEEMMMM + EIEEIIEIEEEEEMMM +} +# tile 122 (explosion magical 3) +{ + MMEEEIEIEEEIIIII + MMEEEIEEEEEIEIII + MMMEEIEEIIIIIIIE + MMMEEEEEEIIIIIII + MMEEEIEEIIEIIIEI + MEEEIIIEIIIIIIEI + MEEEIEEEINIIIIII + MEEEIEEEIIIEEEEI + MEEEIEEEIIIIIENN + MEEEIIEEIIIIIINN + MMEEEIIEEIIIINNI + MMMEEEIIIEIIIIII + MMMMEEEIIIIIIIIN + MMMMMEEIIIIINNEI + MMEMEIIIENIIIIIN + MMMMEIIIIIIIIIIN +} +# tile 123 (explosion magical 4) +{ + EINEEIIIIEINEEEE + ININIIEIEENIENEI + NEIIINEIINNNEENI + IEIIINNNIINNIEII + NNNIINNNNINNIIIE + NNIIIIIINNNNEIIE + NNINNNNNNNNNNEII + NIINNNNNNNNNNNII + NNNNNNNNNNNNINNN + INNNNNNNNNNNNNNN + INNININNNNNNNNIE + IINNNNNNNNNNNIIE + NNNNNNNNNNNNIIII + NIININNNNNNNIIIE + NNNNNNNNNIIIIIIE + IIIINNNNIIIINNII +} +# tile 124 (explosion magical 5) +{ + IIEEEIEIEIEEEEMM + IIEEEEEIEIEEEEMM + IIEEEEEIEIEEEEMM + IIEEEEEIIIEEEEMM + IIEEIEIEEIEEEEMM + IIIIIIIEIIEEEMMM + IIIEIIIEEEEEEMMM + IIEEIEEEEEEEEMMM + EEIIIIIEEEEEMMMM + IIEEIIIEEIEMMMEM + EIIIIIIIIIMMMMMM + IEEEEIIEEIMMEMMM + IINEIIIIEEEMMMMM + NIIIIIIEEEEEMMMM + NENIIIEEEEEEEMMM + IIIIIIEEEEEEEEMM +} +# tile 125 (explosion magical 6) +{ + MMMMEIIIIIIIHIII + MMMMEIIIIIIIEHII + MEMMEEIIIIIIEIHI + MMMEEEIEIIIIIEEI + MMMEEEEIIEEIIIEI + MMMEEEEIIIIIIIIE + MMMEEEEIIEIIHIIE + MMMEEEEEIEEEIIII + MMMEEEEEIIEEIIII + MMMMEEEEEEEEEIII + MMMMMEEEHEEEEEEE + MMMEMMEEEEEEEEEE + MMMMMMMMEEMEEEEE + MMMEMMMMMMMMEEEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 126 (explosion magical 7) +{ + IIINNNNNINNNINII + NNIINNIIIINIEIII + IIIIIIIIIIEIIEII + IIINEIEEINIEEIII + IINNNNIEIIEENINE + NIIEIEIININIIEEI + EIIIIEENIIIIIIII + NEIIIIIIIEIIIIII + INIEIEEEEEEEEEEI + NEININEEEIIIIIIE + EEEIEIIIIIIEEEEE + EEEEEEIEIIEEEEEE + EEEEEEEEEEEEEEEE + EEEEMMMMMMMMMMEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 127 (explosion magical 8) +{ + IIIIIIIEEEEEEEMM + IIEIIEIEEIEIEEMM + IIIIEIIEEIEIEEMM + IIIEEEIEEIEIEEMM + IEEEEEIEEEIIEEMM + IIIEEIIEEEIEEEMM + IEEEIIEEEIIEEEMM + IEIEEIEEEEEEEEMM + EEIEEEEEEIEEEEMM + IIIEEEEEEEEEEEMM + IEEEEEEEEMEEEMMM + EEEEEEEEMMMMMMMM + EEEEEEMMMMEMMMMM + EEEMMMEMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 128 (explosion fiery 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMDDD + MMMMMMDMMMDDDDDD + MMMMMMMMMDDDDDDD + MMMMMMMMMDDDDDDC + MMMMMMMMDDDDCCCD + MMMDDDMDDDDCCCDD + MDMMMDDDDDDCDDDD + MMMMMDDDDDDCDDDD + MMMMDDDDDDDCDDDD + MMMMMMMCDDCCDDCC + MMMMDDDDDDDDDDCC + MMMDDDDCCDDDCCCC + MMDDDDCCDDDDCCCC + MMDDDCCDDDDDCCCC +} +# tile 129 (explosion fiery 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + DMMMMMMMMDDDDDDM + DDDDDDCDDDDDDDDM + DDDDDDCCCCCDDDDD + CCCDDDCDDDDDDDDD + DDDDDDCDDDDDDDDD + DDDDDDCDDDDDDDDC + DDCCCCCCCCCCCDCC + CCDDDCDCCCCCDDDD + CHCDCDCCCDCCCDDD + CLLDCCCDDCCCCCDD + CHCCCCCDCCCCDDDD + CCCCCDCCDCCCLDDD + CCCCCCCCDCDCDLDD + CLHCCCHLHHDDDDDD +} +# tile 130 (explosion fiery 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMDMMMDMMM + DDDDDMMMMMMMMMMM + DDDDDDMMMDMMDMMM + DDDDDDDMMMMMMMMM + CCCCCCDDDDMMMMMM + DDDDDCCDDDDMMMMM + CCDDDDDDDDDDMMMM + DDCDDCCCCDDDMMMM + DDCDDDDDCDDDMMMM + DDLDDCDDDDDDMDMM + DDDCCCCCDDDDMMMM + DDCDCCDCDDDDMMMM + DCDDCCDCDDDDDMMM +} +# tile 131 (explosion fiery 3) +{ + MMDDDCDCDDDCCCCC + MMDDDCDDDDDCDLCC + MMMDDCDDCCCCCCCD + MMMDDDDDDCCCCLLC + MMDDDCDDCCDCCLDL + MDDDCCCDCCCCLCDL + MDDDCDDDCHCCLCCL + MDDDCDDDCCCDDDDL + MDDDCDDDCCCCLDHH + MDDDCCDDCCCCLLHH + MMDDDCCDDCCCLHHL + MMMDDDCCCDLCLLLL + MMMMDDDCCCLCCLCH + MMMMMDDCCCCCHHDC + MMDMDCCCDHLCCCLH + MMMMDCCCCCCCCCLH +} +# tile 132 (explosion fiery 4) +{ + DLHDDCCLLDLHDDDD + CHLHCCDCDDHLDHDC + HDLCLHDCCHHHDDHC + LDLLLHHHCCHHLDCC + HHHLLHHHHLHHLCCD + HHLLCLLLHHHHDCCD + HHLHHHHHHHHHHDCC + HLLHHHHHHHHHHHCC + HHHHHHNNNNHHLHNH + LHHHHHHHNHHHHHNH + LHHLHLNNNNHHHHLD + LLNHNHNNNNHHHLLD + HHHHNHNNNHHHLLLL + HLLNLHNNNHHHLLLD + HHHHNHNHNCLLLCLD + LLLLNHHHLLLCHHCC +} +# tile 133 (explosion fiery 5) +{ + CCDDDCDCDCDDDDMM + CCDDDDDCDCDDDDMM + CCDDDDDCDCDDDDMM + LCDDDDDCCCDDDDMM + LCDDLDLDDCDDDDMM + LCCCLCCDCCDDDMMM + CCCDLCCDDDDDDMMM + CLDDLDDDDDDDDMMM + DDLCCCLDDDDDMMMM + CCDDCLLDDCDMMMDM + DCCCLLCCCCMMMMMM + CDDDDLLDDCMMDMMM + CCHDCLLCDDDMMMMM + HCCCCCCDDDDDMMMM + HDHCCCDDDDDDDMMM + CCCCLLDDDDDDDDMM +} +# tile 134 (explosion fiery 6) +{ + MMMMDCCCCCCCHCCL + MMMMDCCCCCCCDHCC + MDMMDDCCCLCCDCHC + MMMDDDCDCCLCCDDL + MMMDDDDCCDDLCCDC + MMMDDDDCCCCLCCCD + MMMDDDDCCDCCHCCD + MMMDDDDDCDDDCCCC + MMMDDDDDCCDDCCCC + MMMMDDDDDDDDDCCC + MMMMMDDDHDDDDDDD + MMMDMMDDDDDDDDDD + MMMMMMMMDDMDDDDD + MMMDMMMMMMMMDDDD + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 135 (explosion fiery 7) +{ + LLLHHHHHLHHHCHCC + HHLLHHLLLLHCDCCC + LLLLLLLLLLDCCDLL + CLLHDCDDCHLDDCCC + CCHHHHCDCCDDHLHD + HLLDLDLCHCHCLDDC + DCCLLDDHCCCCCCCL + HDCCCCCCCDCCCCCC + LHLDCDDDDDDDDDDL + HDCHLHDDDCCCCCCD + DDDCDCLLCCCDDDDD + DDDDDDCDCCDDDDDD + DDDDDDDDDDDDDDDD + DDDDMMMMMMMMMMDD + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 136 (explosion fiery 8) +{ + CCCCLCCDDDDDDDMM + CCDCCDCDDCDCDDMM + LLCCDCCDDCDCDDMM + CCCDDDCDDCDCDDMM + CDDDDDCDDDCCDDMM + CCLDDCCDDDCDDDMM + CDDDCCDDDCCDDDMM + CDCDDCDDDDDDDDMM + DDCDDDDDDCDDDDMM + CCCDDDDDDDDDDDMM + CDDDDDDDDMDDDMMM + DDDDDDDDMMMMMMMM + DDDDDDMMMMDMMMMM + DDDMMMDMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 137 (explosion frosty 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMEEE + MMMMMNEMMMEEEEEE + MMNMMMNMMENBEEEE + MNMNMNMMMEEEEEEP + MMMMNMMMEEEEPPPE + MMMNENMNEEEPPPEE + MENMMENEEEEPNBEE + MMMNMEEEEEEPEEEE + MMMMEENBEEEPEEEE + MMMMMMMPEEPPEEPP + MMMMEEEEEEEEEEPP + MMMEEEEPPEEEPPPP + MMEEEEPPNBEEPPPP + MMEEEPPEEEEEPPPP +} +# tile 138 (explosion frosty 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + EMMMMMMMMEEEEEEM + ENBEENPEEEENBEEM + EEEEEENPNPNEEEEE + PPBEEEPENEEEEEEE + EEEEEENNNNNEEEEE + EEEEEEPENEEENBEP + EEPPPPNPNPNPPPPP + PPEEENEPPPPNEEEE + PNPEPEPPPEPPPEEE + PBBEPPPEEPPNBPEE + PNPPPPPEPPPPEEEE + PPPPPEPPEPPPBEEE + PPPPPPPPEPEPEBEE + PBNPPPNBNNEEEEEE +} +# tile 139 (explosion frosty 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMEMMMEMMM + EEEEEMMMMMMMMMMM + EEEEEEMMMEMMNMMM + EEEEEEENMMNNMMMM + PPPPPPENENMMMMMM + EEEEEPPENENMNMMM + PPEEEEEEEEENMNMM + EEPEEPPPPNNEMNMM + EEPEEEEENEEEMMMM + EEBEEPEEEEEEMEMM + EEEPPPPPEEEEMMMM + EEPEPPEPEEEEMMMM + EPEEPPEPEEEEEMMM +} +# tile 140 (explosion frosty 3) +{ + MMEEEPEPEEEPPPPP + MMEEEPEEEEEPEBPP + MMMEEPEEPPPPPPPE + MMMENENEEPPPPBBP + MMEEENEEPPEPPBEB + MEEENENEPPPPBPEB + MEEEPEEEPNPPBPPB + MEEEPEEEPPPEEEEB + MEEEPEEEPPPPBENN + MEEEPPEEPPPPBBNN + MMEEEPNEEPPPBNNB + MMMEENNNPEBPBBBB + MMMMEENPPPBPPBPN + MMMMMEEPPPPPNNEP + MMEMEPPPENBPPPBN + MMMMEPPPPPPPPPBN +} +# tile 141 (explosion frosty 4) +{ + EBNEEPPBBEBNEEEE + PNBNPPEPEENBENEP + NEBPBNEPPNNNEENP + BEBBBNNNPPNNBEPP + NNNBBNNNNBNNBPPE + NNBBPBBBNNNNEPPE + NNBNNNNNNNNNNEPP + NBBNNNNNNNNNNNPP + NNNNNNNNNNNNBNNN + BNNNNNNNNNNNNNNN + BNNBNBNNNNNNNNBE + BBNNNNNNNNNNNBBE + NNNNNNNNNNNNBBBB + NBBNBNNNNNNNBBBE + NNNNNNNNNPBBBPBE + BBBBNNNNBBBPNNPP +} +# tile 142 (explosion frosty 5) +{ + PPEEEPEPEPEEEEMM + PPEEEEEPEPEEEEMM + PPEEEEEPEPEEEEMM + BPEEEEEPPPEEEEMM + BPEEBEBEEPEEEEMM + BPPPBPPEPPEEEMMM + PPPEBPPENEEENMMM + PBEEBEEEENENEMMM + EEBPPPBNNNNNNNMM + PPEEPBBEENENMMEM + EPPPBBPPNPMMNMMM + PEEEEBBEEPMMEMMM + PPNEPBBPEEEMMMMM + NPPPPPPEEEEEMMMM + NENPPPEEEEEEEMMM + PPPPBBEEEEEEEEMM +} +# tile 143 (explosion frosty 6) +{ + MMMMEPPPPPPPNPPB + MMMMEPPPPPPPENPP + MEMMEEPPPBPPEPNP + MMMEEEPEPPBPPEEB + MMMEENEPPEEBPPEP + MMMNEEENPPPBPPPE + MMNENPNPNEPPNPPE + MMMNNENNPEEEPPPP + MNMPEEEPPNEEPPPP + MMMNNENNEEEEEPPP + MMNMNPNENEEEEEEE + MMMNMMENEEEEEEEE + MMMMMNMMEEMEEEEE + MMMEMMMMMMMMEEEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 144 (explosion frosty 7) +{ + BBBNNNNNBNNNPNPP + NNBBNNBBBBNPEPPP + BBBBBBBBBBEPPEBB + PBBNEPEEPNBEEPPP + PPNNNNPEPPEENBNE + NBBEBEBPNPNPBEEP + EPPBBEENPPPPPPPB + NEPPPPPPPEPPPPPP + BNBEPEEEEEEEEEEB + NEPNBNEEEPNENPPE + EEEPEPBBPPPNEEEE + EEEEEEPEPNNNNNEE + EEEEEEEEEEENEEEE + EEEEMMMMMMNMNMEE + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 145 (explosion frosty 8) +{ + PPPPBPPEEEEEEEMM + PPEPPEPEEPEPEEMM + BBPPEPPEEPEPNENM + PPPEEEPEEPEPENMM + PEEEEEPEEEPPNENM + PPBEEPNENEPEEEMM + PEEEPPENEPPEEEMM + PEPEENNNNNEEEEMM + EEPEEEENEPEEEEMM + PPPEEENENEEEEEMM + PEEEEEEEEMEEEMMM + EEEEEEEEMMMMMMMM + EEEEEEMMMMENMMMM + EEEMMMEMMMNMNMMM + MMMMMMMMMMMNMMMM + MMMMMMMMMMMMMMMM +} +# tile 146 (zap 0 0) +{ + MMMMMMMIIMMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMMIIMMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMIIIIMMMMMM + MMMMMMMIIMMMMMMM +} +# tile 147 (zap 0 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MIIMMIIMMIIMMIIM + IIIIIIIIIIIIIIII + IIIIIIIIIIIIIIII + MIIMMIIMMIIMMIIM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 148 (zap 0 2) +{ + IIIMMMMMMMMMMMMM + IIIIMMMMMMMMMMMM + IIIIMMMMMMMMMMMM + MIIIIMMMMMMMMMMM + MMMIIIIMMMMMMMMM + MMMMIIIIMMMMMMMM + MMMMIIIIMMMMMMMM + MMMMMIIIIMMMMMMM + MMMMMMMIIIIMMMMM + MMMMMMMMIIIIMMMM + MMMMMMMMIIIIMMMM + MMMMMMMMMIIIIMMM + MMMMMMMMMMMIIIIM + MMMMMMMMMMMMIIII + MMMMMMMMMMMMIIII + MMMMMMMMMMMMMIII +} +# tile 149 (zap 0 3) +{ + MMMMMMMMMMMMMIII + MMMMMMMMMMMMIIII + MMMMMMMMMMMMIIII + MMMMMMMMMMMIIIIM + MMMMMMMMMIIIIMMM + MMMMMMMMIIIIMMMM + MMMMMMMMIIIIMMMM + MMMMMMMIIIIMMMMM + MMMMMIIIIMMMMMMM + MMMMIIIIMMMMMMMM + MMMMIIIIMMMMMMMM + MMMIIIIMMMMMMMMM + MIIIIMMMMMMMMMMM + IIIIMMMMMMMMMMMM + IIIIMMMMMMMMMMMM + IIIMMMMMMMMMMMMM +} +# tile 150 (zap 1 0) +{ + MMMMMMMCCMMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMMCCMMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMCCCCMMMMMM + MMMMMMMCCMMMMMMM +} +# tile 151 (zap 1 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MCCMMCCMMCCMMCCM + CCCCCCCCCCCCCCCC + CCCCCCCCCCCCCCCC + MCCMMCCMMCCMMCCM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 152 (zap 1 2) +{ + CCCMMMMMMMMMMMMM + CCCCMMMMMMMMMMMM + CCCCMMMMMMMMMMMM + MCCCCMMMMMMMMMMM + MMMCCCCMMMMMMMMM + MMMMCCCCMMMMMMMM + MMMMCCCCMMMMMMMM + MMMMMCCCCMMMMMMM + MMMMMMMCCCCMMMMM + MMMMMMMMCCCCMMMM + MMMMMMMMCCCCMMMM + MMMMMMMMMCCCCMMM + MMMMMMMMMMMCCCCM + MMMMMMMMMMMMCCCC + MMMMMMMMMMMMCCCC + MMMMMMMMMMMMMCCC +} +# tile 153 (zap 1 3) +{ + MMMMMMMMMMMMMCCC + MMMMMMMMMMMMCCCC + MMMMMMMMMMMMCCCC + MMMMMMMMMMMCCCCM + MMMMMMMMMCCCCMMM + MMMMMMMMCCCCMMMM + MMMMMMMMCCCCMMMM + MMMMMMMCCCCMMMMM + MMMMMCCCCMMMMMMM + MMMMCCCCMMMMMMMM + MMMMCCCCMMMMMMMM + MMMCCCCMMMMMMMMM + MCCCCMMMMMMMMMMM + CCCCMMMMMMMMMMMM + CCCCMMMMMMMMMMMM + CCCMMMMMMMMMMMMM +} +# tile 154 (zap 2 0) +{ + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM +} +# tile 155 (zap 2 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MNNMMNNMMNNMMNNM + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + MNNMMNNMMNNMMNNM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 156 (zap 2 2) +{ + NNNMMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + MNNNNMMMMMMMMMMM + MMMNNNNMMMMMMMMM + MMMMNNNNMMMMMMMM + MMMMNNNNMMMMMMMM + MMMMMNNNNMMMMMMM + MMMMMMMNNNNMMMMM + MMMMMMMMNNNNMMMM + MMMMMMMMNNNNMMMM + MMMMMMMMMNNNNMMM + MMMMMMMMMMMNNNNM + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMMNNN +} +# tile 157 (zap 2 3) +{ + MMMMMMMMMMMMMNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMNNNNM + MMMMMMMMMNNNNMMM + MMMMMMMMNNNNMMMM + MMMMMMMMNNNNMMMM + MMMMMMMNNNNMMMMM + MMMMMNNNNMMMMMMM + MMMMNNNNMMMMMMMM + MMMMNNNNMMMMMMMM + MMMNNNNMMMMMMMMM + MNNNNMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNMMMMMMMMMMMMM +} +# tile 158 (zap 3 0) +{ + MMMMMMMBBMMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMMBBMMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMBBBBMMMMMM + MMMMMMMBBMMMMMMM +} +# tile 159 (zap 3 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MBBMMBBMMBBMMBBM + BBBBBBBBBBBBBBBB + BBBBBBBBBBBBBBBB + MBBMMBBMMBBMMBBM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 160 (zap 3 2) +{ + BBBMMMMMMMMMMMMM + BBBBMMMMMMMMMMMM + BBBBMMMMMMMMMMMM + MBBBBMMMMMMMMMMM + MMMBBBBMMMMMMMMM + MMMMBBBBMMMMMMMM + MMMMBBBBMMMMMMMM + MMMMMBBBBMMMMMMM + MMMMMMMBBBBMMMMM + MMMMMMMMBBBBMMMM + MMMMMMMMBBBBMMMM + MMMMMMMMMBBBBMMM + MMMMMMMMMMMBBBBM + MMMMMMMMMMMMBBBB + MMMMMMMMMMMMBBBB + MMMMMMMMMMMMMBBB +} +# tile 161 (zap 3 3) +{ + MMMMMMMMMMMMMBBB + MMMMMMMMMMMMBBBB + MMMMMMMMMMMMBBBB + MMMMMMMMMMMBBBBM + MMMMMMMMMBBBBMMM + MMMMMMMMBBBBMMMM + MMMMMMMMBBBBMMMM + MMMMMMMBBBBMMMMM + MMMMMBBBBMMMMMMM + MMMMBBBBMMMMMMMM + MMMMBBBBMMMMMMMM + MMMBBBBMMMMMMMMM + MBBBBMMMMMMMMMMM + BBBBMMMMMMMMMMMM + BBBBMMMMMMMMMMMM + BBBMMMMMMMMMMMMM +} +# tile 162 (zap 4 0) +{ + MMMMMMMAAMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMAAAAMMMMMM + MMMMMMMAAMMMMMMM +} +# tile 163 (zap 4 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MAAMMAAMMAAMMAAM + AAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAA + MAAMMAAMMAAMMAAM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 164 (zap 4 2) +{ + AAAMMMMMMMMMMMMM + AAAAMMMMMMMMMMMM + AAAAMMMMMMMMMMMM + MAAAAMMMMMMMMMMM + MMMAAAAMMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMAAAAMMMMMMMM + MMMMMAAAAMMMMMMM + MMMMMMMAAAAMMMMM + MMMMMMMMAAAAMMMM + MMMMMMMMAAAAMMMM + MMMMMMMMMAAAAMMM + MMMMMMMMMMMAAAAM + MMMMMMMMMMMMAAAA + MMMMMMMMMMMMAAAA + MMMMMMMMMMMMMAAA +} +# tile 165 (zap 4 3) +{ + MMMMMMMMMMMMMAAA + MMMMMMMMMMMMAAAA + MMMMMMMMMMMMAAAA + MMMMMMMMMMMAAAAM + MMMMMMMMMAAAAMMM + MMMMMMMMAAAAMMMM + MMMMMMMMAAAAMMMM + MMMMMMMAAAAMMMMM + MMMMMAAAAMMMMMMM + MMMMAAAAMMMMMMMM + MMMMAAAAMMMMMMMM + MMMAAAAMMMMMMMMM + MAAAAMMMMMMMMMMM + AAAAMMMMMMMMMMMM + AAAAMMMMMMMMMMMM + AAAMMMMMMMMMMMMM +} +# tile 166 (zap 5 0) +{ + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMMNNMMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMNNNNMMMMMM + MMMMMMMNNMMMMMMM +} +# tile 167 (zap 5 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MNNMMNNMMNNMMNNM + NNNNNNNNNNNNNNNN + NNNNNNNNNNNNNNNN + MNNMMNNMMNNMMNNM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 168 (zap 5 2) +{ + NNNMMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + MNNNNMMMMMMMMMMM + MMMNNNNMMMMMMMMM + MMMMNNNNMMMMMMMM + MMMMNNNNMMMMMMMM + MMMMMNNNNMMMMMMM + MMMMMMMNNNNMMMMM + MMMMMMMMNNNNMMMM + MMMMMMMMNNNNMMMM + MMMMMMMMMNNNNMMM + MMMMMMMMMMMNNNNM + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMMNNN +} +# tile 169 (zap 5 3) +{ + MMMMMMMMMMMMMNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMMNNNN + MMMMMMMMMMMNNNNM + MMMMMMMMMNNNNMMM + MMMMMMMMNNNNMMMM + MMMMMMMMNNNNMMMM + MMMMMMMNNNNMMMMM + MMMMMNNNNMMMMMMM + MMMMNNNNMMMMMMMM + MMMMNNNNMMMMMMMM + MMMNNNNMMMMMMMMM + MNNNNMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNNMMMMMMMMMMMM + NNNMMMMMMMMMMMMM +} +# tile 170 (zap 6 0) +{ + MMMMMMMFFMMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMMFFMMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMFFFFMMMMMM + MMMMMMMFFMMMMMMM +} +# tile 171 (zap 6 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MFFMMFFMMFFMMFFM + FFFFFFFFFFFFFFFF + FFFFFFFFFFFFFFFF + MFFMMFFMMFFMMFFM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 172 (zap 6 2) +{ + FFFMMMMMMMMMMMMM + FFFFMMMMMMMMMMMM + FFFFMMMMMMMMMMMM + MFFFFMMMMMMMMMMM + MMMFFFFMMMMMMMMM + MMMMFFFFMMMMMMMM + MMMMFFFFMMMMMMMM + MMMMMFFFFMMMMMMM + MMMMMMMFFFFMMMMM + MMMMMMMMFFFFMMMM + MMMMMMMMFFFFMMMM + MMMMMMMMMFFFFMMM + MMMMMMMMMMMFFFFM + MMMMMMMMMMMMFFFF + MMMMMMMMMMMMFFFF + MMMMMMMMMMMMMFFF +} +# tile 173 (zap 6 3) +{ + MMMMMMMMMMMMMFFF + MMMMMMMMMMMMFFFF + MMMMMMMMMMMMFFFF + MMMMMMMMMMMFFFFM + MMMMMMMMMFFFFMMM + MMMMMMMMFFFFMMMM + MMMMMMMMFFFFMMMM + MMMMMMMFFFFMMMMM + MMMMMFFFFMMMMMMM + MMMMFFFFMMMMMMMM + MMMMFFFFMMMMMMMM + MMMFFFFMMMMMMMMM + MFFFFMMMMMMMMMMM + FFFFMMMMMMMMMMMM + FFFFMMMMMMMMMMMM + FFFMMMMMMMMMMMMM +} +# tile 174 (zap 7 0) +{ + MMMMMMMGGMMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMMGGMMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMGGGGMMMMMM + MMMMMMMGGMMMMMMM +} +# tile 175 (zap 7 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MGGMMGGMMGGMMGGM + GGGGGGGGGGGGGGGG + GGGGGGGGGGGGGGGG + MGGMMGGMMGGMMGGM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 176 (zap 7 2) +{ + GGGMMMMMMMMMMMMM + GGGGMMMMMMMMMMMM + GGGGMMMMMMMMMMMM + MGGGGMMMMMMMMMMM + MMMGGGGMMMMMMMMM + MMMMGGGGMMMMMMMM + MMMMGGGGMMMMMMMM + MMMMMGGGGMMMMMMM + MMMMMMMGGGGMMMMM + MMMMMMMMGGGGMMMM + MMMMMMMMGGGGMMMM + MMMMMMMMMGGGGMMM + MMMMMMMMMMMGGGGM + MMMMMMMMMMMMGGGG + MMMMMMMMMMMMGGGG + MMMMMMMMMMMMMGGG +} +# tile 177 (zap 7 3) +{ + MMMMMMMMMMMMMGGG + MMMMMMMMMMMMGGGG + MMMMMMMMMMMMGGGG + MMMMMMMMMMMGGGGM + MMMMMMMMMGGGGMMM + MMMMMMMMGGGGMMMM + MMMMMMMMGGGGMMMM + MMMMMMMGGGGMMMMM + MMMMMGGGGMMMMMMM + MMMMGGGGMMMMMMMM + MMMMGGGGMMMMMMMM + MMMGGGGMMMMMMMMM + MGGGGMMMMMMMMMMM + GGGGMMMMMMMMMMMM + GGGGMMMMMMMMMMMM + GGGMMMMMMMMMMMMM +} +# tile 178 (warning 0) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOOOOMMMMMMM + MMMMOOOOOOMMMMMM + MMMOOAAAAOOMMMMM + MMMOOAMMMOOAMMMM + MMMMAAMMMOOAMMMM + MMMMMMMMOOAAMMMM + MMMMMMMOOAAMMMMM + MMMMMMOOAAMMMMMM + MMMMMMOOAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMOOMMMMMMMM + MMMMMMOOAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 179 (warning 1) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMOCOCMMMMMMM + MMMMOCOCOCMMMMMM + MMMOCAAAAOCMMMMM + MMMCOAMMMCOAMMMM + MMMMAAMMMOCAMMMM + MMMMMMMMOCAAMMMM + MMMMMMMOCAAMMMMM + MMMMMMOCAAMMMMMM + MMMMMMCOAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMCOMMMMMMMM + MMMMMMOCAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 180 (warning 2) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMCCCCMMMMMMM + MMMMCCCCCCMMMMMM + MMMCCAAAACCMMMMM + MMMCCAMMMCCAMMMM + MMMMAAMMMCCAMMMM + MMMMMMMMCCAAMMMM + MMMMMMMCCAAMMMMM + MMMMMMCCAAMMMMMM + MMMMMMCCAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMCCMMMMMMMM + MMMMMMCCAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 181 (warning 3) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMDDDDMMMMMMM + MMMMDDDDDDMMMMMM + MMMDDAAAADDMMMMM + MMMDDAMMMDDAMMMM + MMMMAAMMMDDAMMMM + MMMMMMMMDDAAMMMM + MMMMMMMDDAAMMMMM + MMMMMMDDAAMMMMMM + MMMMMMDDAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMDDMMMMMMMM + MMMMMMDDAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 182 (warning 4) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMIIIIMMMMMMM + MMMMIIIIIIMMMMMM + MMMIIAAAAIIMMMMM + MMMIIAMMMIIAMMMM + MMMMAAMMMIIAMMMM + MMMMMMMMIIAAMMMM + MMMMMMMIIAAMMMMM + MMMMMMIIAAMMMMMM + MMMMMMIIAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMIIMMMMMMMM + MMMMMMIIAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 183 (warning 5) +{ + MMMMMMMMMMMMMMMM + MMMMMMMMMMMMMMMM + MMMMMIEIEMMMMMMM + MMMMIEIEIEMMMMMM + MMMIEAAAAIEMMMMM + MMMEIAMMMEIAMMMM + MMMMAAMMMIEAMMMM + MMMMMMMMIEAAMMMM + MMMMMMMIEAAMMMMM + MMMMMMIEAAMMMMMM + MMMMMMEIAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMEIMMMMMMMM + MMMMMMIEAMMMMMMM + MMMMMMMAAMMMMMMM + MMMMMMMMMMMMMMMM +} +# tile 184 (sub mine walls 0) +{ + AJJKKKACJAAJJJAA + AJKKKACLJJAJJJJA + AJKKACLCKJJAJJJA + AAKACLCKJJJJAAAA + AAACLLKKJJJJJJAA + AACLLKKKAAJJJJJA + ACKKKKKACJAJJJJA + AKKKKKACLJJAJJJA + AAKKKACLKJJJAJAA + AAKKACLKKJJJJJAA + AACCCKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AAKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 185 (sub mine walls 1) +{ + AJAAAAAAJJAAAJAA + JJJAAAJJJJJAAAAJ + JJJACJAJJJACCJAJ + AAACLJJAJACCLJJA + AACLCKJJACCLCKJJ + ACKCKKJJJCKCKKJA + CJKKKKJJCKCKJJAC + KJJKCJJCKKKJJJCK + KJKKKJJKJKJJJJKK + JJJKCJKKJKJJJCKJ + JJJJJJKJKKJJJKKJ + JJJJJKJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 186 (sub mine walls 2) +{ + AAAAAAKCCKKJAAAA + AAAAKKCLCJKJJAAA + AAKKKCLCKJJJKKAA + AKKKCLCKJJJJJJKA + AKKCLLKKJJJJJJJA + AKCLLKKKAAJJJJJJ + ACKKKKKACJAJJJJJ + AKKKKKACLJJAAJJJ + AAKKKCLLKJJJAJAJ + AAKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AAKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 187 (sub mine walls 3) +{ + AAAAAAKCCKKJAAAA + AAAAKKCLCJKJJAAA + AAKKKCLCKJJJKKAA + AKKCCLCKJJJJJJKA + KKCCLLKKJJJJJJJA + KKCLKKKKAAJJJJJA + KCLKKKKACJAJJJJA + CKKKKKACLJJAAJJA + KAKKKCLLKJJJAJAA + AKKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AKKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 188 (sub mine walls 4) +{ + AKKKAAKKKKAAJJJA + AKKAAKCCCJJJAAJA + AKKAALLJJJACCJAA + AKAACLJJJACCLJJA + AAACCKJJACLLCKJJ + AAACLKJJJCKCKKJA + AACLKKJJCKCKJJAC + AAKKCJJCKKKJJJCK + ACKKKJJKJKJJJJKK + ACKKCJKKJKJJJCKJ + AKKKJJKJKKJJJKKJ + ACKJJKJJJJJJJJJJ + AKJJJJJJJJJJJJJJ + AKJJJJJJJJJJJJJJ + AJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 189 (sub mine walls 5) +{ + AKKAAAKKAAAAJJJA + AKAAKKLCKAAAAAJA + AAKKCLCJJACCAJJA + AKKCLJJJACCCLAJA + AKCKKKJACCCLCAAA + ACKKKKJACCKCKKAA + CJKKKKACCKCKJJAA + KJJKCJJCKKKJJJAA + KJKKKJJKJKJJJJJA + JJJKCJKKJKJJJJJA + JJJJJJKJKKJJJJJA + JJJJJKJJJJJJJJJA + JJJJJJJJJJJJJJJA + JJJJJJJJJJJJJJJA + JJJJJJJJJJJJJJJA + AAAAAAAAAAAAAAAA +} +# tile 190 (sub mine walls 6) +{ + AAAAAAKCCKKJAAAA + AAAAKCCLCJKJJAAA + AAKKCCLCKJJJKKAA + AKKKCLCKJJJJJJKA + KKKCLKKKJJJJJJJA + KKCLKKKKAAJJJJJJ + KCKLKKKACJAJJJJJ + CKKKKKACLJJAAJJJ + KAKKKCLLKJJJAJAJ + AKKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AKKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 191 (sub mine walls 7) +{ + AKKAAAKKKKAAJJJA + AKAAKKLCCJJJAAJA + AAKKCLCJJJACCJAA + AKKCLJJJJACCLJJA + AKCLCKJJACCLCKJJ + ACKCKKJJJCKCKKJA + CJKKKKJJCKCKJJAC + KJJKCJJCKKKJJJCK + KJKKKJJKJKJJJJKK + JJJKCJKKJKJJJCKJ + JJJJJJKJKKJJJKKJ + JJJJJKJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 192 (sub mine walls 8) +{ + AAAAAAKCCKKJAAAA + AAAAKCCLCJKJJAAA + AAKKKCLCKJJJKKAA + AKKKCLCKJJJJJJKA + KKCCLLKKJJJJJJJA + KKCLLKKKAAJJJJJJ + KCLLKKKACJAJJJJJ + CKLKKKACLJJAAJJJ + KAKKKCLLKJJJAJAJ + AKKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AKKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 193 (sub mine walls 9) +{ + AKKAACKCCKKJAJJA + AKACKKKLLJKJJAJA + AAKKKCCCKJJJKKAA + AKKKCCLKJJJJJJKA + AKKCLLKKJJJJJJJA + AKCLLKKKAAJJJJJJ + ACLKKKKACJAJJJJJ + AKKKKKACLJJAAJJJ + AAKKKCLLKJJJAJAJ + AAKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AAKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 194 (sub mine walls 10) +{ + AKKAACKCCKKJAJJA + AKACKKCLCJKJJAJA + AAKKCCLCKJJJKKAA + AKKCLLCKJJJJJJKA + KKCCLLKKJJJJJJJA + KCKLLKKKAAJJJJJA + CCKKKKKACJAJJJJA + CKKKKKACLJJAAJJA + KAKKKCLLKJJJAJAA + AKKKCCCKKJJJAJAA + AACCKKKAAJJJJJJA + ACCKKKACJAJJJJJA + ACKKKACLJJAJJJJA + AKKKACLCKJJAJJAA + AAJACKCKKJJJAJAA + AAJCKKJAAAJJJJJA +} +# tile 195 (sub gehennom walls 0) +{ + ALLDAJMMMMMJLLDA + ADDDAJMJMMJJDDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + ADDDAJJMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMJMJDMJA + ALLDAJMMMMMJLLDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + ADDDAJJMJMJJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 196 (sub gehennom walls 1) +{ + AAALDDAAAAALDDAA + DDDLDDAJDDDLDDAJ + MDDJJJAJMDDJJJAJ + JJJJJJJJJJJJJJJJ + MMJJJMMMMMJJJMMM + MMLDDMMMMMLDDMMM + DDLDDAJDDDLDDAJD + DDJJJAJMDDJJJAJM + JJJJJJJJJJJJJJJJ + MMJDMMMMJDMMMJDM + MMJMMMMMJMMMMJMM + JJJJJJJJJJJJJJJJ + JDMMMMJDMMMMJDMM + JMMMMMJMMMMMJMMM + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 197 (sub gehennom walls 2) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 198 (sub gehennom walls 3) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 199 (sub gehennom walls 4) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + MMJDMMMMJDMMMJDM + MMJMMMMMJMMMMJMM + JJJJJJJJJJJJJJJJ + JDMMMMJDMMMMJDMM + JMMMMMJMMMMMJMMM + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 200 (sub gehennom walls 5) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + MMJDMMMMJDMMMJDM + MMJMMMMMJMMMMJMM + JJJJJJJJJJJJJJJJ + JDMMMMJDMMMMJDMM + JMMMMMJMMMMMJMMM + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 201 (sub gehennom walls 6) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 202 (sub gehennom walls 7) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + MMJDMMMMJDMMMJDM + MMJMMMMMJMMMMJMM + JJJJJJJJJJJJJJJJ + JDMMMMJDMMMMJDMM + JMMMMMJMMMMMJMMM + JJJJJJJJJJJJJJJJ + AAAAAAAAAAAAAAAA +} +# tile 203 (sub gehennom walls 8) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 204 (sub gehennom walls 9) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 205 (sub gehennom walls 10) +{ + AAALLLLDDDDDDAAA + LLLLAAJJMMMMDJJJ + DDDLAJJMMMCCDJJJ + DDDDDDDDDDDDDJJJ + DDDMMMMMMMMMMJJJ + DDDMMMMMMMMMMJJJ + DDMMMMMMMMMMMMJJ + DMMMMMMMMMMMMMMJ + JJJJJJJJJJJJJJJJ + AMMJJJJJJJJJMMMJ + ADDDAAAAAAAADDDA + ADDDAJMMMMMJDDDA + ADDDAJMMMMMJDDDA + AJAAAJJMMMMJJAJA + AJJJAJJMMMMJJJJA + ADMMAJJMMMMJDMJA +} +# tile 206 (sub knox walls 0) +{ + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AJJJACLCKJJAJJJA + AAAAALCKJJJAAAAA + AAJAAAKKJJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 207 (sub knox walls 1) +{ + AJAAAJAAAJAAAJAA + JJJAAAJAJJJAAAJA + JJJACJAAJJJACJAA + AAACLJJAJJACLJJA + AACLCKJJAACLCKJJ + AAACKKJAAAACKKJA + AJAAAJAAAJAAAJAA + KJJACJJAKJJACJJA + KJJAKJJAKJJACJJA + KJJACJAAKJJAKJAA + CJJACJAACJJAKJJA + KJAACJJACJJACJJA + KJAACJJACJAAKJJA + KJJAKJJAKJJACJAA + KJJACJJAKJJACJJA + AAAAAAAAAAAAAAAA +} +# tile 208 (sub knox walls 2) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 209 (sub knox walls 3) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 210 (sub knox walls 4) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AACCCAKJJAJJJAKA + AJACAJAJAJAJAJAA + KJJACJJAKJJACJJA + KJJAKJJAKJJACJJA + KJJACJAAKJJAKJAA + CJJACJAACJJAKJJA + KJAACJJACJJACJJA + KJAACJJACJAAKJJA + KJJAKJJAKJJACJAA + KJJACJJAKJJACJJA + AAAAAAAAAAAAAAAA +} +# tile 211 (sub knox walls 5) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AACCCAKJJAJJJAKA + AJACAJAJAJAJAJAA + KJJACJJAKJJACJJA + KJJAKJJAKJJACJJA + KJJACJAAKJJAKJAA + CJJACJAACJJAKJJA + KJAACJJACJJACJJA + KJAACJJACJAAKJJA + KJJAKJJAKJJACJAA + KJJACJJAKJJACJJA + AAAAAAAAAAAAAAAA +} +# tile 212 (sub knox walls 6) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 213 (sub knox walls 7) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AACCCAKJJAJJJAKA + AJACAJAJAJAJAJAA + KJJACJJAKJJACJJA + KJJAKJJAKJJACJJA + KJJACJAAKJJAKJAA + CJJACJAACJJAKJJA + KJAACJJACJJACJJA + KJAACJJACJAAKJJA + KJJAKJJAKJJACJAA + KJJACJJAKJJACJJA + AAAAAAAAAAAAAAAA +} +# tile 214 (sub knox walls 8) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 215 (sub knox walls 9) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 216 (sub knox walls 10) +{ + AAAAAAKCJKAAAAAA + AAAAKKCLKJKKAAAA + AAKKKCLCKJJKKKAA + AKKKCLCKKJJJKKKA + AKKCCCKKJJJJJKKA + AKCCCKKAAJJJJJKA + AJJCKKACJAJJJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA + AJJJAAACJAAAJJJA + AJJJAACLJJAAJJJA + AAAAACLCKJJAAAAA + AAJAAACKKJAAAJAA + ACJJAAAAAAAACJJA +} +# tile 217 (sub sokoban walls 0) +{ + ANNBAMEEEEEMNNBA + ABBBAMEMEEMMBBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + ABBBAMMEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEMEMBEMA + ANNBAMEEEEEMNNBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + ABBBAMMEMEMMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 218 (sub sokoban walls 1) +{ + AAANBBAAAAANBBAA + BBBNBBAMBBBNBBAM + EBBMMMAMEBBMMMAM + MMMMMMMMMMMMMMMM + EEMMMEEEEEMMMEEE + EENBBEEEEENBBEEE + BBNBBAMBBBNBBAMB + BBMMMAMEBBMMMAME + MMMMMMMMMMMMMMMM + EEMBEEEEMBEEEMBE + EEMEEEEEMEEEEMEE + MMMMMMMMMMMMMMMM + MBEEEEMBEEEEMBEE + MEEEEEMEEEEEMEEE + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 219 (sub sokoban walls 2) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 220 (sub sokoban walls 3) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 221 (sub sokoban walls 4) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + EEMBEEEEMBEEEMBE + EEMEEEEEMEEEEMEE + MMMMMMMMMMMMMMMM + MBEEEEMBEEEEMBEE + MEEEEEMEEEEEMEEE + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 222 (sub sokoban walls 5) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + EEMBEEEEMBEEEMBE + EEMEEEEEMEEEEMEE + MMMMMMMMMMMMMMMM + MBEEEEMBEEEEMBEE + MEEEEEMEEEEEMEEE + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 223 (sub sokoban walls 6) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 224 (sub sokoban walls 7) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + EEMBEEEEMBEEEMBE + EEMEEEEEMEEEEMEE + MMMMMMMMMMMMMMMM + MBEEEEMBEEEEMBEE + MEEEEEMEEEEEMEEE + MMMMMMMMMMMMMMMM + AAAAAAAAAAAAAAAA +} +# tile 225 (sub sokoban walls 8) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 226 (sub sokoban walls 9) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} +# tile 227 (sub sokoban walls 10) +{ + AAANNNNBBBBBBAAA + NNNNAAMMEEEEBMMM + BBBNAMMEEENNBMMM + BBBBBBBBBBBBBMMM + BBBEEEEEEEEEEMMM + BBBEEEEEEEEEEMMM + BBEEEEEEEEEEEEMM + BEEEEEEEEEEEEEEM + MMMMMMMMMMMMMMMM + AEEMMMMMMMMMEEEM + ABBBAAAAAAAABBBA + ABBBAMEEEEEMBBBA + ABBBAMEEEEEMBBBA + AMAAAMMEEEEMMAMA + AMMMAMMEEEEMMMMA + ABEEAMMEEEEMBEMA +} diff --git a/win/share/ppmwrite.c b/win/share/ppmwrite.c new file mode 100644 index 0000000..ced5ffc --- /dev/null +++ b/win/share/ppmwrite.c @@ -0,0 +1,171 @@ +/* this produces a raw ppm file, with a 15-character header of + * "P6 3-digit-width 3-digit-height 255\n" + */ + +#include "config.h" +#include "tile.h" + +#ifndef MONITOR_HEAP +extern long *FDECL(alloc, (unsigned int)); +#endif + +FILE *ppm_file; + +struct ppmscreen { + int Width; + int Height; +} PpmScreen; + +static int tiles_across, tiles_down, curr_tiles_across; +static pixel **image; + +static void NDECL(write_header); +static void NDECL(WriteTileStrip); + +static void +write_header() +{ + (void) fprintf(ppm_file, "P6 %03d %03d 255\n", + PpmScreen.Width, PpmScreen.Height); +} + +static void +WriteTileStrip() +{ + int i, j; + + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < PpmScreen.Width; i++) { + (void) fputc((char)image[j][i].r, ppm_file); + (void) fputc((char)image[j][i].g, ppm_file); + (void) fputc((char)image[j][i].b, ppm_file); + } + } +} + +boolean +fopen_ppm_file(filename, type) +const char *filename; +const char *type; +{ + int i; + + if (strcmp(type, WRBMODE)) { + Fprintf(stderr, "using writing routine for non-writing?\n"); + return FALSE; + } + ppm_file = fopen(filename, type); + if (ppm_file == (FILE *)0) { + Fprintf(stderr, "cannot open ppm file %s\n", filename); + return FALSE; + } + + if (!colorsinmainmap) { + Fprintf(stderr, "no colormap set yet\n"); + return FALSE; + } + + tiles_across = 20; + curr_tiles_across = 0; + PpmScreen.Width = 20 * TILE_X; + + tiles_down = 0; + PpmScreen.Height = 0; /* will be rewritten later */ + + write_header(); + + image = (pixel **)alloc(TILE_Y * sizeof(pixel *)); + for (i = 0; i < TILE_Y; i++) { + image[i] = (pixel *) alloc(PpmScreen.Width * sizeof(pixel)); + } + + return TRUE; +} + +boolean +write_ppm_tile(pixels) +pixel (*pixels)[TILE_X]; +{ + int i, j; + + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + image[j][curr_tiles_across*TILE_X + i] = pixels[j][i]; + } + } + curr_tiles_across++; + if (curr_tiles_across == tiles_across) { + WriteTileStrip(); + curr_tiles_across = 0; + tiles_down++; + } + return TRUE; +} + +int +fclose_ppm_file() +{ + int i, j; + + if (curr_tiles_across) { /* partial row */ + /* fill with checkerboard, for lack of a better idea */ + for (j = 0; j < TILE_Y; j++) { + for (i = curr_tiles_across * TILE_X; + i < PpmScreen.Width; i += 2 ) { + image[j][i].r = MainColorMap[CM_RED][0]; + image[j][i].g = MainColorMap[CM_GREEN][0]; + image[j][i].b = MainColorMap[CM_BLUE][0]; + image[j][i+1].r = MainColorMap[CM_RED][1]; + image[j][i+1].g = MainColorMap[CM_GREEN][1]; + image[j][i+1].b = MainColorMap[CM_BLUE][1]; + } + } + WriteTileStrip(); + curr_tiles_across = 0; + tiles_down++; + } + + for (i = 0; i < TILE_Y; i++) { + free((genericptr_t)image[i]); + } + free((genericptr_t)image); + + PpmScreen.Height = tiles_down * TILE_Y; + rewind(ppm_file); + write_header(); /* update size */ + + return(fclose(ppm_file)); +} + + +int +main(argc, argv) +int argc; +char *argv[]; +{ + pixel pixels[TILE_Y][TILE_X]; + + if (argc != 3) { + Fprintf(stderr, "usage: txt2ppm txtfile ppmfile\n"); + exit(EXIT_FAILURE); + } + + if (!fopen_text_file(argv[1], RDTMODE)) + exit(EXIT_FAILURE); + + init_colormap(); + + if (!fopen_ppm_file(argv[2], WRBMODE)) { + (void) fclose_text_file(); + exit(EXIT_FAILURE); + } + + while (read_text_tile(pixels)) + (void) write_ppm_tile(pixels); + + (void) fclose_text_file(); + (void) fclose_ppm_file(); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} diff --git a/win/share/thintile.c b/win/share/thintile.c new file mode 100644 index 0000000..9e4dc33 --- /dev/null +++ b/win/share/thintile.c @@ -0,0 +1,129 @@ +/* SCCS Id: @(#)thintile.c 3.4 1995/11/26 */ +/* Copyright (c) NetHack Development Team 1995 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Create a set of overview tiles by eliminating even pixels in original */ + +#include "config.h" +#include "tile.h" + +#ifdef __GO32__ +#include +#endif + +static char pixels[TILE_Y][TILE_X]; + +static char *tilefiles[] = { "../win/share/monsters.txt", + "../win/share/objects.txt", + "../win/share/other.txt"}; + +static char *thinfiles[] = { "../win/share/monthin.txt", + "../win/share/objthin.txt", + "../win/share/oththin.txt"}; +static FILE *infile, *outfile; +static int tilecount; +static int tilecount_per_file; +static int filenum; +static char comment[BUFSZ]; + +static void +copy_colormap() +{ + int r, g, b; + char c[2]; + + while (fscanf(infile, "%[A-Za-z0-9] = (%d, %d, %d) ", c, &r, &g, &b) + == 4) { + Fprintf(outfile, "%c = (%d, %d, %d)\n", c[0], r, g, b); + } +} + +static boolean +read_txttile() +{ + int i, j; + char buf[BUFSZ]; + char buf2[BUFSZ]; + + char c[2]; + + + if (fscanf(infile, "# %s %d (%[^)])", buf2, &i, buf) <= 0) + return FALSE; + + Sprintf(comment,"# tile %d (%s)", i, buf); + + /* look for non-whitespace at each stage */ + if (fscanf(infile, "%1s", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + if (c[0] != '{') { + Fprintf(stderr, "didn't find expected '{'\n"); + return FALSE; + } + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + if (fscanf(infile, "%1s", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + pixels[j][i] = c[0]; + } + } + if (fscanf(infile, "%1s ", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + if (c[0] != '}') { + Fprintf(stderr, "didn't find expected '}'\n"); + return FALSE; + } + return TRUE; +} + +static void +write_thintile() +{ + int i, j; + + + Fprintf(outfile, "%s\n", comment); + Fprintf(outfile, "{\n"); + for (j = 0; j < TILE_Y; j++) { + Fprintf(outfile, " "); + for (i = 0; i < TILE_X; i += 2) { + (void) fputc(pixels[j][i], outfile); + } + Fprintf(outfile, "\n"); + } + Fprintf(outfile, "}\n"); +} +int +main(argc, argv) +int argc; +char *argv[]; +{ + while (filenum < 3) { + tilecount_per_file = 0; + infile = fopen(tilefiles[filenum], RDTMODE); + outfile = fopen(thinfiles[filenum], WRTMODE); + copy_colormap(); + while (read_txttile()) { + write_thintile(); + tilecount_per_file++; + tilecount++; + } + fclose(outfile); + fclose(infile); + printf("%d tiles processed from %s\n", + tilecount_per_file, tilefiles[filenum]); + ++filenum; + } + printf("Grand total of %d tiles processed.\n", tilecount); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +/*thintile.c*/ diff --git a/win/share/tile.doc b/win/share/tile.doc new file mode 100644 index 0000000..a6d8f58 --- /dev/null +++ b/win/share/tile.doc @@ -0,0 +1,140 @@ +Window ports can optionally make use of the tiles (pictures for NetHack +symbols) found in this directory. They are distributed in a text format +with routines to help in converting them to a system's preferred format +and using them there. The original tiles were provided by Warwick Allison. + +The tile distribution format for monsters.txt, objects.txt, and other.txt +starts with a palette header like: + +A = (0, 0, 0) +... +P = (254, 254, 254) + +and then each tile has an entry like: + +# tile 292 (comment identifying tile) +{ + AAAAGHPAAAAACDAA + AAAFGDEMLOCNAAAA +... +} + +Each port can convert these .txt files to whatever format it wants the +game executable to use, probably providing only one merged output file. +See the tilemap.c discussion at the bottom for more hints on adding tiles. + + +Shared code provided for conversion utilities: + +tile.h contains shared declarations. + +tiletext.c defines the external variables from tile.h and supplies +the external routines for reading and writing the defined text format. + +Each conversion utility is expected to use tiletext.c and provide code of +its own for reading and/or writing another format. The important global +variables implement a colormap shared between tiletext.c and the other +half of utilities. As an example of conversion utilities, we provide +txt2ppm (tiletext.c + ppmwrite.c) and gif2txt (tiletext.c + gifread.c). +(Sorry, we're not paying Unisys patent royalties for the right to provide +you with a gifwrite.c, which would necessarily use the LZW compression +algorithm they claim.) + +The text I/O routines are: + +boolean fopen_text_file(const char *filename, const char *type); + select file for subsequent tile I/O + "type" a la fopen + returns FALSE if file not opened, otherwise reads/writes header + (including colormap) and sets up to decode/encode tiles +int fclose_text_file(); + close file +boolean read_text_tile(pixel[TILE_Y][TILE_X]); + returns FALSE if no next tile in current file + otherwise TRUE and insert the tile in the provided array +boolean write_text_tile(pixel[TILE_Y][TILE_X]); + writes tile + +There are two additional shared routines provided for writers: + +void init_colormap(); + initialize the output colormap from the input one + must be called before opening output file as colormap is part of header +void merge_colormap(); + merge the current input colormap into the output one + +Due to the amount of state being kept, only one text or gif file can be +open at a time. If you are combining multiple files into one other-format +file with a single common colormap, you may need to open each source file +and merge their colormaps into a common colormap before processing any tiles. + +Although there are expected to be only 16 colors in the distribution tiles, +conversion programs should be prepared to accept up to MAXCOLORMAPSIZE +colors and map them to a smaller number if their port requires it. + + +Expected sequence for editing tiles: + edit foo.txt + + -or- + + run txt2ppm foo.txt foo.ppm + convert ppm to gif, either via ppmtogif from pbmplus/netpbm or + stripping the first 15 bytes of foo.ppm (containing the + size of the image) and feeding the rest to any raw-24bit- + image-reading program + edit tiles with gif-editing program + run gif2txt foo.gif foo.txt + + +When converted to ppm, monsters.ppm, objects.ppm, and other.ppm are: + each a single ppm format (rgb triples with header) + 20 tiles across, however many down (need "blank" tile to fill in + extras on last row -- currently alternating pixels in + first and second colors) + allows looking at tiles en masse for comparison or whatever + +The gif reading routines accept further variations so long as the gif is +n*TILE_X pixels across. + +The gif I/O routines are: + +boolean fopen_gif_file(const char *filename, const char *type); + select file for subsequent tile I/O + "type" a la fopen + returns FALSE if file not opened, otherwise reads gif header + (including colormap) and sets up to decode tiles +int fclose_gif_file(); + tear down decode mechanism + close file +boolean read_gif_tile(pixel[TILE_Y][TILE_X]); + returns FALSE if no next tile in current file (including when any + remaining tiles are "blank"), + otherwise TRUE and insert the tile in the provided array + + +Array provided by shared code for NetHack use, by compiling and running +tilemap.c to form tile.c: + +short glyph2tile[MAXGLYPH]; + maps glyph number to tile number for display purposes, assuming + (non-blank) tiles are numbered sequentially through + monsters/objects/other + +tilemap.c (shudder) accounts for things disappearing due to compilation +options -- there should be a tile for everything appearing under any +supported option, but under some options some tiles won't be referenced. +Therefore, tilemap.c has the knowledge to provide the comments for gif2txt +and is compiled with GIF2TXT to link in there, along with the various +strings for things that are compiled in (monst.o etc.). + +If you add monsters/objects/other things to NetHack and need to add tiles +to go with them, just add an entry in the right place in the appropriate +.txt file, and one to tilemap.c if the new item is conditionally compiled. +While the "comment identifying tile" in the .txt file must be correct, +the number of the tile need not be, and can just be a duplicate of the +tile on either side (or any other integer, for that matter). In an +official release, the tiles in a .txt file will be numbered consecutively +so that you may cross-reference with a graphics format, but the conversion +code does not care about the numbering. (In fact, running txt2ppm, ppmtogif, +and gif2txt gives you a consecutively numbered version of the .txt file.) diff --git a/win/share/tile.h b/win/share/tile.h new file mode 100644 index 0000000..4dd7633 --- /dev/null +++ b/win/share/tile.h @@ -0,0 +1,46 @@ +typedef unsigned char pixval; + +typedef struct { + pixval r, g, b; +} pixel; + +#define MAXCOLORMAPSIZE 256 + +#define CM_RED 0 +#define CM_GREEN 1 +#define CM_BLUE 2 + +/* shared between reader and writer */ +extern pixval ColorMap[3][MAXCOLORMAPSIZE]; +extern int colorsinmap; +/* writer's accumulated colormap */ +extern pixval MainColorMap[3][MAXCOLORMAPSIZE]; +extern int colorsinmainmap; + +#include "dlb.h" /* for MODEs */ + +/* size of tiles */ +#ifndef TILE_X +#define TILE_X 16 +#endif +#ifndef TILE_Y +#define TILE_Y 16 +#endif + +#define Fprintf (void) fprintf + + +extern boolean FDECL(fopen_text_file, (const char *, const char *)); +extern boolean FDECL(read_text_tile, (pixel (*)[TILE_X])); +extern boolean FDECL(write_text_tile, (pixel (*)[TILE_X])); +extern int NDECL(fclose_text_file); + +extern void NDECL(init_colormap); +extern void NDECL(merge_colormap); + +#if defined(MICRO) || defined(WIN32) +#undef exit +# if !defined(MSDOS) && !defined(WIN32) +extern void FDECL(exit, (int)); +# endif +#endif diff --git a/win/share/tile2bmp.c b/win/share/tile2bmp.c new file mode 100644 index 0000000..9b3dbaf --- /dev/null +++ b/win/share/tile2bmp.c @@ -0,0 +1,340 @@ +/* SCCS Id: @(#)tile2bmp.c 3.4 2002/03/14 */ +/* Copyright (c) NetHack PC Development Team 1995 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Edit History: + * + * Initial Creation M.Allison 1994/01/11 + * + */ + +/* #pragma warning(4103:disable) */ + +#include "hack.h" +#include "tile.h" +#ifndef __GNUC__ +#include "win32api.h" +#endif + +/* #define COLORS_IN_USE MAXCOLORMAPSIZE /* 256 colors */ +#if (TILE_X==32) +#define COLORS_IN_USE 256 +#else +#define COLORS_IN_USE 16 /* 16 colors */ +#endif + +#define BITCOUNT 8 + +extern char *FDECL(tilename, (int, int)); + +#if BITCOUNT==4 +#define MAX_X 320 /* 2 per byte, 4 bits per pixel */ +#define MAX_Y 480 +#else +# if (TILE_X==32) +#define MAX_X (32 * 40) +#define MAX_Y 960 +# else +#define MAX_X 640 /* 1 per byte, 8 bits per pixel */ +#define MAX_Y 480 +# endif +#endif + +/* GCC fix by Paolo Bonzini 1999/03/28 */ +#ifdef __GNUC__ +#define PACK __attribute__((packed)) +#else +#define PACK +#endif + +static short leshort(short x) +{ +#ifdef __BIG_ENDIAN__ + return ((x&0xff)<<8)|((x>>8)&0xff); +#else + return x; +#endif +} + + +static long lelong(long x) +{ +#ifdef __BIG_ENDIAN__ + return ((x&0xff)<<24)|((x&0xff00)<<8)|((x>>8)&0xff00)|((x>>24)&0xff); +#else + return x; +#endif +} + +#ifdef __GNUC__ +typedef struct tagBMIH { + unsigned long biSize; + long biWidth; + long biHeight; + unsigned short biPlanes; + unsigned short biBitCount; + unsigned long biCompression; + unsigned long biSizeImage; + long biXPelsPerMeter; + long biYPelsPerMeter; + unsigned long biClrUsed; + unsigned long biClrImportant; +} PACK BITMAPINFOHEADER; + +typedef struct tagBMFH { + unsigned short bfType; + unsigned long bfSize; + unsigned short bfReserved1; + unsigned short bfReserved2; + unsigned long bfOffBits; +} PACK BITMAPFILEHEADER; + +typedef struct tagRGBQ { + unsigned char rgbBlue; + unsigned char rgbGreen; + unsigned char rgbRed; + unsigned char rgbReserved; +} PACK RGBQUAD; +#define UINT unsigned int +#define DWORD unsigned long +#define LONG long +#define WORD unsigned short +#define BI_RGB 0L +#define BI_RLE8 1L +#define BI_RLE4 2L +#define BI_BITFIELDS 3L +#endif /* __GNUC__ */ + +#pragma pack(1) +struct tagBMP{ + BITMAPFILEHEADER bmfh; + BITMAPINFOHEADER bmih; +#if BITCOUNT==4 +#define RGBQUAD_COUNT 16 + RGBQUAD bmaColors[RGBQUAD_COUNT]; +#else +#if (TILE_X==32) +#define RGBQUAD_COUNT 256 +#else +#define RGBQUAD_COUNT 16 +#endif + RGBQUAD bmaColors[RGBQUAD_COUNT]; +#endif +#if (COLORS_IN_USE==16) + uchar packtile[MAX_Y][MAX_X]; +#else + uchar packtile[MAX_Y][MAX_X]; +/* uchar packtile[TILE_Y][TILE_X]; */ +#endif +} PACK bmp; +#pragma pack() + +#define BMPFILESIZE (sizeof(struct tagBMP)) + +FILE *tibfile2; + +pixel tilepixels[TILE_Y][TILE_X]; + +static void FDECL(build_bmfh,(BITMAPFILEHEADER *)); +static void FDECL(build_bmih,(BITMAPINFOHEADER *)); +static void FDECL(build_bmptile,(pixel (*)[TILE_X])); + +char *tilefiles[] = { +#if (TILE_X == 32) + "../win/share/mon32.txt", + "../win/share/obj32.txt", + "../win/share/oth32.txt" +#else + "../win/share/monsters.txt", + "../win/share/objects.txt", + "../win/share/other.txt" +#endif +}; + +int num_colors = 0; +int tilecount; +int max_tiles_in_row = 40; +int tiles_in_row; +int filenum; +int initflag; +int yoffset,xoffset; +char bmpname[128]; +FILE *fp; + +int +main(argc, argv) +int argc; +char *argv[]; +{ + int i, j; + + if (argc != 2) { + Fprintf(stderr, "usage: %s outfile.bmp\n", argv[0]); + exit(EXIT_FAILURE); + } else + strcpy(bmpname, argv[1]); + +#ifdef OBSOLETE + bmpfile2 = fopen(NETHACK_PACKED_TILEFILE, WRBMODE); + if (bmpfile2 == (FILE *)0) { + Fprintf(stderr, "Unable to open output file %s\n", + NETHACK_PACKED_TILEFILE); + exit(EXIT_FAILURE); + } +#endif + + tilecount = 0; + xoffset = yoffset = 0; + initflag = 0; + filenum = 0; + fp = fopen(bmpname,"wb"); + if (!fp) { + printf("Error creating tile file %s, aborting.\n",bmpname); + exit(1); + } + while (filenum < (sizeof(tilefiles) / sizeof(char *))) { + if (!fopen_text_file(tilefiles[filenum], RDTMODE)) { + Fprintf(stderr, + "usage: tile2bmp (from the util directory)\n"); + exit(EXIT_FAILURE); + } + num_colors = colorsinmap; + if (num_colors > 62) { + Fprintf(stderr, "too many colors (%d)\n", num_colors); + exit(EXIT_FAILURE); + } + if (!initflag) { + build_bmfh(&bmp.bmfh); + build_bmih(&bmp.bmih); + for (i = 0; i < MAX_Y; ++i) + for (j = 0; j < MAX_X; ++j) + bmp.packtile[i][j] = (uchar)0; + for (i = 0; i < num_colors; i++) { + bmp.bmaColors[i].rgbRed = ColorMap[CM_RED][i]; + bmp.bmaColors[i].rgbGreen = ColorMap[CM_GREEN][i]; + bmp.bmaColors[i].rgbBlue = ColorMap[CM_BLUE][i]; + bmp.bmaColors[i].rgbReserved = 0; + } + initflag = 1; + } +/* printf("Colormap initialized\n"); */ + while (read_text_tile(tilepixels)) { + build_bmptile(tilepixels); + tilecount++; +#if BITCOUNT==4 + xoffset += (TILE_X / 2); +#else + xoffset += TILE_X; +#endif + if (xoffset >= MAX_X) { + yoffset += TILE_Y; + xoffset = 0; + } + } + (void) fclose_text_file(); + ++filenum; + } + fwrite(&bmp, sizeof(bmp), 1, fp); + fclose(fp); + Fprintf(stderr, "Total of %d tiles written to %s.\n", + tilecount, bmpname); + + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + + +static void +build_bmfh(pbmfh) +BITMAPFILEHEADER *pbmfh; +{ + pbmfh->bfType = leshort(0x4D42); + pbmfh->bfSize = lelong(BMPFILESIZE); + pbmfh->bfReserved1 = (UINT)0; + pbmfh->bfReserved2 = (UINT)0; + pbmfh->bfOffBits = lelong(sizeof(bmp.bmfh) + sizeof(bmp.bmih) + + (RGBQUAD_COUNT * sizeof(RGBQUAD))); +} + +static void +build_bmih(pbmih) +BITMAPINFOHEADER *pbmih; +{ + WORD cClrBits; + int w,h; + pbmih->biSize = lelong(sizeof(bmp.bmih)); +#if BITCOUNT==4 + pbmih->biWidth = lelong(w = MAX_X * 2); +#else + pbmih->biWidth = lelong(w = MAX_X); +#endif + pbmih->biHeight = lelong(h = MAX_Y); + pbmih->biPlanes = leshort(1); +#if BITCOUNT==4 + pbmih->biBitCount = leshort(4); + cClrBits = 4; +#else + pbmih->biBitCount = leshort(8); + cClrBits = 8; +#endif + if (cClrBits == 1) + cClrBits = 1; + else if (cClrBits <= 4) + cClrBits = 4; + else if (cClrBits <= 8) + cClrBits = 8; + else if (cClrBits <= 16) + cClrBits = 16; + else if (cClrBits <= 24) + cClrBits = 24; + else cClrBits = 32; + pbmih->biCompression = lelong(BI_RGB); + pbmih->biXPelsPerMeter = lelong(0); + pbmih->biYPelsPerMeter = lelong(0); +#if (TILE_X==32) + if (cClrBits < 24) + pbmih->biClrUsed = lelong(1<biClrUsed = lelong(RGBQUAD_COUNT); +#endif + +#if (TILE_X==16) + pbmih->biSizeImage = lelong(0); +#else + pbmih->biSizeImage = lelong(((w * cClrBits +31) & ~31) /8 * h); +#endif + pbmih->biClrImportant = (DWORD)0; +} + +static void +build_bmptile(pixels) +pixel (*pixels)[TILE_X]; +{ + int cur_x, cur_y, cur_color; + int x,y; + + for (cur_y = 0; cur_y < TILE_Y; cur_y++) { + for (cur_x = 0; cur_x < TILE_X; cur_x++) { + for (cur_color = 0; cur_color < num_colors; cur_color++) { + if (ColorMap[CM_RED][cur_color] == pixels[cur_y][cur_x].r && + ColorMap[CM_GREEN][cur_color]== pixels[cur_y][cur_x].g && + ColorMap[CM_BLUE][cur_color] == pixels[cur_y][cur_x].b) + break; + } + if (cur_color >= num_colors) + Fprintf(stderr, "color not in colormap!\n"); + y = (MAX_Y - 1) - (cur_y + yoffset); +#if BITCOUNT==4 + x = (cur_x / 2) + xoffset; + bmp.packtile[y][x] = cur_x%2 ? + (uchar)(bmp.packtile[y][x] | cur_color) : + (uchar)(cur_color<<4); +#else + x = cur_x + xoffset; + bmp.packtile[y][x] = (uchar)cur_color; +#endif + } + } +} diff --git a/win/share/tilemap.c b/win/share/tilemap.c new file mode 100644 index 0000000..bca662b --- /dev/null +++ b/win/share/tilemap.c @@ -0,0 +1,529 @@ +/* SCCS Id: @(#)tilemap.c 3.4 2000/06/04 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This source file is compiled twice: + * once without TILETEXT defined to make tilemap.{o,obj}, + * then again with it defined to produce tiletxt.{o,obj}. + */ + +#include "hack.h" + +const char * FDECL(tilename, (int, int)); +void NDECL(init_tilemap); +void FDECL(process_substitutions, (FILE *)); + +#if defined(MICRO) || defined(WIN32) +#undef exit +#if !defined(MSDOS) && !defined(WIN32) +extern void FDECL(exit, (int)); +#endif +#endif + +#define MON_GLYPH 1 +#define OBJ_GLYPH 2 +#define OTH_GLYPH 3 /* fortunately unnecessary */ + +/* note that the ifdefs here should be the opposite sense from monst.c/ + * objects.c/rm.h + */ + +struct conditionals { + int sequence, predecessor; + const char *name; +} conditionals[] = { +#ifndef CHARON /* not supported yet */ + { MON_GLYPH, PM_HELL_HOUND, "Cerberus" }, +#endif + /* commented out in monst.c at present */ + { MON_GLYPH, PM_SHOCKING_SPHERE, "beholder" }, + { MON_GLYPH, PM_BABY_SILVER_DRAGON, "baby shimmering dragon" }, + { MON_GLYPH, PM_SILVER_DRAGON, "shimmering dragon" }, + { MON_GLYPH, PM_JABBERWOCK, "vorpal jabberwock" }, +#ifndef KOPS + { MON_GLYPH, PM_JABBERWOCK, "Keystone Kop" }, + { MON_GLYPH, PM_JABBERWOCK, "Kop Sergeant" }, + { MON_GLYPH, PM_JABBERWOCK, "Kop Lieutenant" }, + { MON_GLYPH, PM_JABBERWOCK, "Kop Kaptain" }, +#endif + { MON_GLYPH, PM_VAMPIRE_LORD, "vampire mage" }, +#ifndef CHARON /* not supported yet */ + { MON_GLYPH, PM_CROESUS, "Charon" }, +#endif +#ifndef MAIL + { MON_GLYPH, PM_FAMINE, "mail daemon" }, +#endif +#ifndef TOURIST + { MON_GLYPH, PM_SAMURAI, "tourist" }, +#endif + /* commented out in monst.c at present */ + { MON_GLYPH, PM_SHAMAN_KARNOV, "Earendil" }, + { MON_GLYPH, PM_SHAMAN_KARNOV, "Elwing" }, +#ifndef TOURIST + { MON_GLYPH, PM_LORD_SATO, "Twoflower" }, +#endif + /* commented out in monst.c at present */ + { MON_GLYPH, PM_CHROMATIC_DRAGON, "Goblin King" }, + { MON_GLYPH, PM_NEANDERTHAL, "High-elf" }, +#ifndef TOURIST + { MON_GLYPH, PM_ROSHI, "guide" }, +#endif +#ifndef KOPS + { OBJ_GLYPH, CLUB, "rubber hose" }, +#endif + /* objects commented out in objects.c at present */ + { OBJ_GLYPH, SILVER_DRAGON_SCALE_MAIL, "shimmering dragon scale mail" }, + { OBJ_GLYPH, SILVER_DRAGON_SCALES, "shimmering dragon scales" }, +#ifndef TOURIST + { OBJ_GLYPH, LEATHER_JACKET, "Hawaiian shirt" }, + { OBJ_GLYPH, LEATHER_JACKET, "T-shirt" }, + { OBJ_GLYPH, LOCK_PICK, "credit card" }, + { OBJ_GLYPH, MAGIC_LAMP, "expensive camera" }, +#endif +#ifndef STEED + { OBJ_GLYPH, TOWEL, "saddle" }, +#endif + /* allow slime mold to look like slice of pizza, since we + * don't know what a slime mold should look like when renamed anyway + */ +#ifndef MAIL + { OBJ_GLYPH, SCR_STINKING_CLOUD+4, "stamped / mail" }, +#endif + { 0, 0, 0} +}; + + +/* + * Some entries in glyph2tile[] should be substituted for on various levels. + * The tiles used for the substitute entries will follow the usual ones in + * other.til in the order given here, which should have every substitution + * for the same set of tiles grouped together. You will have to change + * more code in process_substitutions()/substitute_tiles() if the sets + * overlap in the future. + */ +struct substitute { + int first_glyph, last_glyph; + const char *sub_name; /* for explanations */ + const char *level_test; +} substitutes[] = { + { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall, + "mine walls", "In_mines(plev)" }, + { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall, + "gehennom walls", "In_hell(plev)" }, + { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall, + "knox walls", "Is_knox(plev)" }, + { GLYPH_CMAP_OFF + S_vwall, GLYPH_CMAP_OFF + S_trwall, + "sokoban walls", "In_sokoban(plev)" } +}; + + +#ifdef TILETEXT + +/* + * entry is the position of the tile within the monsters/objects/other set + */ +const char * +tilename(set, entry) +int set, entry; +{ + int i, j, condnum, tilenum; + static char buf[BUFSZ]; + + /* Note: these initializers don't do anything except guarantee that + we're linked properly. + */ + monst_init(); + objects_init(); + (void) def_char_to_objclass(']'); + + condnum = tilenum = 0; + + for (i = 0; i < NUMMONS; i++) { + if (set == MON_GLYPH && tilenum == entry) + return mons[i].mname; + tilenum++; + while (conditionals[condnum].sequence == MON_GLYPH && + conditionals[condnum].predecessor == i) { + if (set == MON_GLYPH && tilenum == entry) + return conditionals[condnum].name; + condnum++; + tilenum++; + } + } + if (set == MON_GLYPH && tilenum == entry) + return "invisible monster"; + + tilenum = 0; /* set-relative number */ + for (i = 0; i < NUM_OBJECTS; i++) { + /* prefer to give the description - that's all the tile's + * appearance should reveal */ + if (set == OBJ_GLYPH && tilenum == entry) { + if ( !obj_descr[i].oc_descr ) + return obj_descr[i].oc_name; + if ( !obj_descr[i].oc_name ) + return obj_descr[i].oc_descr; + + Sprintf(buf, "%s / %s", + obj_descr[i].oc_descr, + obj_descr[i].oc_name); + return buf; + } + + tilenum++; + while (conditionals[condnum].sequence == OBJ_GLYPH && + conditionals[condnum].predecessor == i) { + if (set == OBJ_GLYPH && tilenum == entry) + return conditionals[condnum].name; + condnum++; + tilenum++; + } + } + + tilenum = 0; /* set-relative number */ + for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) { + if (set == OTH_GLYPH && tilenum == entry) { + if (*defsyms[i].explanation) + return defsyms[i].explanation; + else { + /* if SINKS are turned off, this + * string won't be there (and can't be there + * to prevent symbol-identification and + * special-level mimic appearances from + * thinking the items exist) + */ + switch (i) { + case S_sink: + Sprintf(buf, "sink"); + break; + default: + Sprintf(buf, "cmap %d", tilenum); + break; + } + return buf; + } + } + tilenum++; + while (conditionals[condnum].sequence == OTH_GLYPH && + conditionals[condnum].predecessor == i) { + if (set == OTH_GLYPH && tilenum == entry) + return conditionals[condnum].name; + condnum++; + tilenum++; + } + } + /* explosions */ + tilenum = MAXPCHARS - MAXEXPCHARS; + i = entry - tilenum; + if (i < (MAXEXPCHARS * EXPL_MAX)) { + if (set == OTH_GLYPH) { + static char *explosion_types[] = { /* hack.h */ + "dark", "noxious", "muddy", "wet", + "magical", "fiery", "frosty" + }; + Sprintf(buf, "explosion %s %d", + explosion_types[i / MAXEXPCHARS], i % MAXEXPCHARS); + return buf; + } + } + tilenum += (MAXEXPCHARS * EXPL_MAX); + + i = entry - tilenum; + if (i < (NUM_ZAP << 2)) { + if (set == OTH_GLYPH) { + Sprintf(buf, "zap %d %d", i/4, i%4); + return buf; + } + } + tilenum += (NUM_ZAP << 2); + + i = entry - tilenum; + if (i < WARNCOUNT) { + if (set == OTH_GLYPH) { + Sprintf(buf, "warning %d", i); + return buf; + } + } + tilenum += WARNCOUNT; + + for (i = 0; i < SIZE(substitutes); i++) { + j = entry - tilenum; + if (j <= substitutes[i].last_glyph - substitutes[i].first_glyph) { + if (set == OTH_GLYPH) { + Sprintf(buf, "sub %s %d", substitutes[i].sub_name, j); + return buf; + } + } + tilenum += substitutes[i].last_glyph + - substitutes[i].first_glyph + 1; + } + + Sprintf(buf, "unknown %d %d", set, entry); + return buf; +} + +#else /* TILETEXT */ + +#define TILE_FILE "tile.c" + +#ifdef AMIGA +# define SOURCE_TEMPLATE "NH:src/%s" +#else +# ifdef MAC +# define SOURCE_TEMPLATE ":src:%s" +# else +# define SOURCE_TEMPLATE "../src/%s" +# endif +#endif + +short tilemap[MAX_GLYPH]; +int lastmontile, lastobjtile, lastothtile; + +/* Number of tiles for invisible monsters */ +#define NUM_INVIS_TILES 1 + +/* + * set up array to map glyph numbers to tile numbers + * + * assumes tiles are numbered sequentially through monsters/objects/other, + * with entries for all supported compilation options + * + * "other" contains cmap and zaps (the swallow sets are a repeated portion + * of cmap), as well as the "flash" glyphs for the new warning system + * introduced in 3.3.1. + */ +void +init_tilemap() +{ + int i, j, condnum, tilenum; + int corpsetile, swallowbase; + + for (i = 0; i < MAX_GLYPH; i++) { + tilemap[i] = -1; + } + + corpsetile = NUMMONS + NUM_INVIS_TILES + CORPSE; + swallowbase= NUMMONS + NUM_INVIS_TILES + NUM_OBJECTS + S_sw_tl; + + /* add number compiled out */ + for (i = 0; conditionals[i].sequence; i++) { + switch (conditionals[i].sequence) { + case MON_GLYPH: + corpsetile++; + swallowbase++; + break; + case OBJ_GLYPH: + if (conditionals[i].predecessor < CORPSE) + corpsetile++; + swallowbase++; + break; + case OTH_GLYPH: + if (conditionals[i].predecessor < S_sw_tl) + swallowbase++; + break; + } + } + + condnum = tilenum = 0; + for (i = 0; i < NUMMONS; i++) { + tilemap[GLYPH_MON_OFF+i] = tilenum; + tilemap[GLYPH_PET_OFF+i] = tilenum; + tilemap[GLYPH_DETECT_OFF+i] = tilenum; + tilemap[GLYPH_RIDDEN_OFF+i] = tilenum; + tilemap[GLYPH_BODY_OFF+i] = corpsetile; + j = GLYPH_SWALLOW_OFF + 8*i; + tilemap[j] = swallowbase; + tilemap[j+1] = swallowbase+1; + tilemap[j+2] = swallowbase+2; + tilemap[j+3] = swallowbase+3; + tilemap[j+4] = swallowbase+4; + tilemap[j+5] = swallowbase+5; + tilemap[j+6] = swallowbase+6; + tilemap[j+7] = swallowbase+7; + tilenum++; + while (conditionals[condnum].sequence == MON_GLYPH && + conditionals[condnum].predecessor == i) { + condnum++; + tilenum++; + } + } + tilemap[GLYPH_INVISIBLE] = tilenum++; + lastmontile = tilenum - 1; + + for (i = 0; i < NUM_OBJECTS; i++) { + tilemap[GLYPH_OBJ_OFF+i] = tilenum; + tilenum++; + while (conditionals[condnum].sequence == OBJ_GLYPH && + conditionals[condnum].predecessor == i) { + condnum++; + tilenum++; + } + } + lastobjtile = tilenum - 1; + + for (i = 0; i < (MAXPCHARS - MAXEXPCHARS); i++) { + tilemap[GLYPH_CMAP_OFF+i] = tilenum; + tilenum++; + while (conditionals[condnum].sequence == OTH_GLYPH && + conditionals[condnum].predecessor == i) { + condnum++; + tilenum++; + } + } + + for (i = 0; i < (MAXEXPCHARS * EXPL_MAX); i++) { + tilemap[GLYPH_EXPLODE_OFF+i] = tilenum; + tilenum++; + while (conditionals[condnum].sequence == OTH_GLYPH && + conditionals[condnum].predecessor == (i + MAXPCHARS)) { + condnum++; + tilenum++; + } + } + + for (i = 0; i < NUM_ZAP << 2; i++) { + tilemap[GLYPH_ZAP_OFF+i] = tilenum; + tilenum++; + while (conditionals[condnum].sequence == OTH_GLYPH && + conditionals[condnum].predecessor == (i + MAXEXPCHARS)) { + condnum++; + tilenum++; + } + } + + for (i = 0; i < WARNCOUNT; i++) { + tilemap[GLYPH_WARNING_OFF+i] = tilenum; + tilenum++; + } + + lastothtile = tilenum - 1; +} + +const char *prolog[] = { + "", + "", + "void", + "substitute_tiles(plev)", + "d_level *plev;", + "{", + "\tint i;", + "" +}; + +const char *epilog[] = { + "}" +}; + +/* write out the substitutions in an easily-used form. */ +void +process_substitutions(ofp) +FILE *ofp; +{ + int i, j, k, span, start; + + fprintf(ofp, "\n\n"); + + j = 0; /* unnecessary */ + span = -1; + for (i = 0; i < SIZE(substitutes); i++) { + if (i == 0 + || substitutes[i].first_glyph != substitutes[j].first_glyph + || substitutes[i].last_glyph != substitutes[j].last_glyph) { + j = i; + span++; + fprintf(ofp, "short std_tiles%d[] = { ", span); + for (k = substitutes[i].first_glyph; + k < substitutes[i].last_glyph; k++) + fprintf(ofp, "%d, ", tilemap[k]); + fprintf(ofp, "%d };\n", + tilemap[substitutes[i].last_glyph]); + } + } + + for (i = 0; i < SIZE(prolog); i++) { + fprintf(ofp, "%s\n", prolog[i]); + } + j = -1; + span = -1; + start = lastothtile + 1; + for (i = 0; i < SIZE(substitutes); i++) { + if (i == 0 + || substitutes[i].first_glyph != substitutes[j].first_glyph + || substitutes[i].last_glyph != substitutes[j].last_glyph) { + if (i != 0) { /* finish previous span */ + fprintf(ofp, "\t} else {\n"); + fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n", + substitutes[j].first_glyph, + substitutes[j].last_glyph); + fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n", + span, substitutes[j].first_glyph); + fprintf(ofp, "\t}\n\n"); + } + j = i; + span++; + } + if (i != j) fprintf(ofp, "\t} else "); + fprintf(ofp, "\tif (%s) {\n", substitutes[i].level_test); + fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n", + substitutes[i].first_glyph, + substitutes[i].last_glyph); + fprintf(ofp, "\t\t\tglyph2tile[i] = %d + i - %d;\n", + start, substitutes[i].first_glyph); + start += substitutes[i].last_glyph - substitutes[i].first_glyph + 1; + } + /* finish last span */ + fprintf(ofp, "\t} else {\n"); + fprintf(ofp, "\t\tfor (i = %d; i <= %d; i++)\n", + substitutes[j].first_glyph, + substitutes[j].last_glyph); + fprintf(ofp, "\t\t\tglyph2tile[i] = std_tiles%d[i - %d];\n", + span, substitutes[j].first_glyph); + fprintf(ofp, "\t}\n\n"); + + for (i = 0; i < SIZE(epilog); i++) { + fprintf(ofp, "%s\n", epilog[i]); + } + + fprintf(ofp, "\nint total_tiles_used = %d;\n", start); + lastothtile = start - 1; +} + +int main() +{ + register int i; + char filename[30]; + FILE *ofp; + + init_tilemap(); + + /* + * create the source file, "tile.c" + */ + Sprintf(filename, SOURCE_TEMPLATE, TILE_FILE); + if (!(ofp = fopen(filename, "w"))) { + perror(filename); + exit(EXIT_FAILURE); + } + fprintf(ofp,"/* This file is automatically generated. Do not edit. */\n"); + fprintf(ofp,"\n#include \"hack.h\"\n\n"); + fprintf(ofp,"short glyph2tile[MAX_GLYPH] = {\n"); + + for (i = 0; i < MAX_GLYPH; i++) { + fprintf(ofp,"%2d,%c", tilemap[i], (i % 12) ? ' ' : '\n'); + } + fprintf(ofp,"%s};\n", (i % 12) ? "\n" : ""); + + process_substitutions(ofp); + + fprintf(ofp,"\n#define MAXMONTILE %d\n", lastmontile); + fprintf(ofp,"#define MAXOBJTILE %d\n", lastobjtile); + fprintf(ofp,"#define MAXOTHTILE %d\n", lastothtile); + + fprintf(ofp,"\n/*tile.c*/\n"); + + fclose(ofp); + exit(EXIT_SUCCESS); + /*NOTREACHED*/ + return 0; +} + +#endif /* TILETEXT */ diff --git a/win/share/tiletext.c b/win/share/tiletext.c new file mode 100644 index 0000000..8ac8abe --- /dev/null +++ b/win/share/tiletext.c @@ -0,0 +1,332 @@ +/* SCCS Id: @(#)tiletext.c 3.4 1999/10/24 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "config.h" +#include "tile.h" + +pixval ColorMap[3][MAXCOLORMAPSIZE]; +int colorsinmap; +pixval MainColorMap[3][MAXCOLORMAPSIZE]; +int colorsinmainmap; + +static short color_index[MAXCOLORMAPSIZE]; +static int num_colors; +static char charcolors[MAXCOLORMAPSIZE]; + +static int placeholder_init = 0; +static pixel placeholder[TILE_Y][TILE_X]; +static FILE *tile_file; +static int tile_set, tile_set_indx; +#if (TILE_X==8) +static const char *text_sets[] = { "monthin.txt", "objthin.txt", "oththin.txt" }; +#else +static const char *text_sets[] = { "monsters.txt", "objects.txt", "other.txt" }; +#endif + +extern const char *FDECL(tilename, (int, int)); +static void FDECL(read_text_colormap, (FILE *)); +static boolean FDECL(write_text_colormap, (FILE *)); +static boolean FDECL(read_txttile, (FILE *, pixel(*)[TILE_X])); +static void FDECL(write_txttile, (FILE *, pixel(*)[TILE_X])); + +/* Ugh. DICE doesn't like %[A-Z], so we have to spell it out... */ +#define FORMAT_STRING \ +"%[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789] = (%d, %d, %d) " + +static void +read_text_colormap(txtfile) +FILE *txtfile; +{ + int i, r, g, b; + char c[2]; + + for (i = 0; i < MAXCOLORMAPSIZE; i++) + color_index[i] = -1; + + num_colors = 0; + while (fscanf(txtfile, FORMAT_STRING, c, &r, &g, &b) == 4) { + color_index[(int) c[0]] = num_colors; + ColorMap[CM_RED][num_colors] = r; + ColorMap[CM_GREEN][num_colors] = g; + ColorMap[CM_BLUE][num_colors] = b; + num_colors++; + } + colorsinmap = num_colors; +} + +#undef FORMAT_STRING + +static boolean +write_text_colormap(txtfile) +FILE *txtfile; +{ + int i; + char c; + + num_colors = colorsinmainmap; + if (num_colors > 62) { + Fprintf(stderr, "too many colors (%d)\n", num_colors); + return FALSE; + } + for (i = 0; i < num_colors; i++) { + if (i < 26) c = 'A' + i; + else if (i < 52) c = 'a' + i - 26; + else c = '0' + i - 52; + + charcolors[i] = c; + Fprintf(txtfile, "%c = (%d, %d, %d)\n", c, + (int)MainColorMap[CM_RED][i], + (int)MainColorMap[CM_GREEN][i], + (int)MainColorMap[CM_BLUE][i]); + } + return TRUE; +} + +static boolean +read_txttile(txtfile, pixels) +FILE *txtfile; +pixel (*pixels)[TILE_X]; +{ + int ph, i, j, k; + char buf[BUFSZ], ttype[BUFSZ]; + const char *p; + char c[2]; + + if (fscanf(txtfile, "# %s %d (%[^)])", ttype, &i, buf) <= 0) + return FALSE; + + ph = strcmp(ttype, "placeholder") == 0; + + if (!ph && strcmp(ttype,"tile") != 0) + Fprintf(stderr, + "Keyword \"%s\" unexpected for entry %d\n", + ttype, i); + + if (tile_set != 0) { + /* check tile name, but not relative number, which will + * change when tiles are added + */ + p = tilename(tile_set, tile_set_indx); + if (p && strcmp(p, buf)) { + Fprintf(stderr, "warning: for tile %d (numbered %d) of %s,\n", + tile_set_indx, i, text_sets[tile_set-1]); + Fprintf(stderr, "\tfound '%s' while expecting '%s'\n", + buf, p); + } + } + tile_set_indx++; + + /* look for non-whitespace at each stage */ + if (fscanf(txtfile, "%1s", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + if (c[0] != '{') { + Fprintf(stderr, "didn't find expected '{'\n"); + return FALSE; + } + for (j = 0; j < TILE_Y; j++) { + for (i = 0; i < TILE_X; i++) { + if (fscanf(txtfile, "%1s", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + k = color_index[(int) c[0]]; + if (k == -1) + Fprintf(stderr, + "color %c not in colormap!\n", c[0]); + else { + pixels[j][i].r = ColorMap[CM_RED][k]; + pixels[j][i].g = ColorMap[CM_GREEN][k]; + pixels[j][i].b = ColorMap[CM_BLUE][k]; + } + } + } + if ( ph ) { + /* remember it for later */ + memcpy( placeholder, pixels, sizeof(placeholder) ); + } + if (fscanf(txtfile, "%1s ", c) < 0) { + Fprintf(stderr, "unexpected EOF\n"); + return FALSE; + } + if (c[0] != '}') { + Fprintf(stderr, "didn't find expected '}'\n"); + return FALSE; + } +#ifdef _DCC + /* DICE again... it doesn't seem to eat whitespace after the } like + * it should, so we have to do so manually. + */ + while ((*c = fgetc(txtfile)) != EOF && isspace(*c)) + ; + ungetc(*c, txtfile); +#endif + return TRUE; +} + +static void +write_txttile(txtfile, pixels) +FILE *txtfile; +pixel (*pixels)[TILE_X]; +{ + const char *p; + const char *type; + int i, j, k; + + if ( memcmp(placeholder, pixels, sizeof(placeholder)) == 0 ) + type = "placeholder"; + else + type = "tile"; + + if (tile_set == 0) + Fprintf(txtfile, "# %s %d (unknown)\n", type, tile_set_indx); + else { + p = tilename(tile_set, tile_set_indx); + if (p) + Fprintf(txtfile, "# %s %d (%s)\n", type, tile_set_indx, p); + else + Fprintf(txtfile, "# %s %d (null)\n", type, tile_set_indx); + } + tile_set_indx++; + + Fprintf(txtfile, "{\n"); + for (j = 0; j < TILE_Y; j++) { + Fprintf(txtfile, " "); + for (i = 0; i < TILE_X; i++) { + for (k = 0; k < num_colors; k++) { + if (ColorMap[CM_RED][k] == pixels[j][i].r && + ColorMap[CM_GREEN][k] == pixels[j][i].g && + ColorMap[CM_BLUE][k] == pixels[j][i].b) + break; + } + if (k >= num_colors) + Fprintf(stderr, "color not in colormap!\n"); + (void) fputc(charcolors[k], txtfile); + } + Fprintf(txtfile, "\n"); + } + Fprintf(txtfile, "}\n"); +} + +/* initialize main colormap from globally accessed ColorMap */ +void +init_colormap() +{ + int i; + + colorsinmainmap = colorsinmap; + for (i = 0; i < colorsinmap; i++) { + MainColorMap[CM_RED][i] = ColorMap[CM_RED][i]; + MainColorMap[CM_GREEN][i] = ColorMap[CM_GREEN][i]; + MainColorMap[CM_BLUE][i] = ColorMap[CM_BLUE][i]; + } +} + +/* merge new colors from ColorMap into MainColorMap */ +void +merge_colormap() +{ + int i, j; + + for (i = 0; i < colorsinmap; i++) { + for (j = 0; j < colorsinmainmap; j++) { + if (MainColorMap[CM_RED][j] == ColorMap[CM_RED][i] && + MainColorMap[CM_GREEN][j] == ColorMap[CM_GREEN][i] && + MainColorMap[CM_BLUE][j] == ColorMap[CM_BLUE][i]) + break; + } + if (j >= colorsinmainmap) { /* new color */ + if (colorsinmainmap >= MAXCOLORMAPSIZE) { + Fprintf(stderr, + "Too many colors to merge -- excess ignored.\n"); + } + j = colorsinmainmap; + MainColorMap[CM_RED][j] = ColorMap[CM_RED][i]; + MainColorMap[CM_GREEN][j] = ColorMap[CM_GREEN][i]; + MainColorMap[CM_BLUE][j] = ColorMap[CM_BLUE][i]; + colorsinmainmap++; + } + } +} + +boolean +fopen_text_file(filename, type) +const char *filename; +const char *type; +{ + const char *p; + int i; + + if (tile_file != (FILE *)0) { + Fprintf(stderr, "can only open one text file at at time\n"); + return FALSE; + } + + tile_file = fopen(filename, type); + if (tile_file == (FILE *)0) { + Fprintf(stderr, "cannot open text file %s\n", filename); + return FALSE; + } + + p = rindex(filename, '/'); + if (p) p++; + else p = filename; + + tile_set = 0; + for (i = 0; i < SIZE(text_sets); i++) { + if (!strcmp(p, text_sets[i])) + tile_set = i+1; + } + tile_set_indx = 0; + + if (!strcmp(type, RDTMODE)) { + /* Fill placeholder with noise */ + if ( !placeholder_init ) { + placeholder_init++; + for ( i=0; itoplin == 1 && !(cw->flags & WIN_STOP)) more(); + cw->flags &= ~WIN_STOP; + ttyDisplay->toplin = 3; /* special prompt state */ + ttyDisplay->inread++; + pline("%s ", query); + *obufp = 0; + for(;;) { + (void) fflush(stdout); + Sprintf(toplines, "%s ", query); + Strcat(toplines, obufp); + if((c = Getchar()) == EOF) { +#ifndef NEWAUTOCOMP + *bufp = 0; +#endif /* not NEWAUTOCOMP */ + break; + } + if(c == '\033') { + *obufp = c; + obufp[1] = 0; + break; + } + if (ttyDisplay->intr) { + ttyDisplay->intr--; + *bufp = 0; + } + if(c == '\020') { /* ctrl-P */ + if (iflags.prevmsg_window != 's') { + int sav = ttyDisplay->inread; + ttyDisplay->inread = 0; + (void) tty_doprev_message(); + ttyDisplay->inread = sav; + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + addtopl(query); + addtopl(" "); + *bufp = 0; + addtopl(obufp); + } else { + if (!doprev) + (void) tty_doprev_message();/* need two initially */ + (void) tty_doprev_message(); + doprev = 1; + continue; + } + } else if (doprev && iflags.prevmsg_window == 's') { + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + doprev = 0; + addtopl(query); + addtopl(" "); + *bufp = 0; + addtopl(obufp); + } + if(c == erase_char || c == '\b') { + if(bufp != obufp) { +#ifdef NEWAUTOCOMP + char *i; + +#endif /* NEWAUTOCOMP */ + bufp--; +#ifndef NEWAUTOCOMP + putsyms("\b \b");/* putsym converts \b */ +#else /* NEWAUTOCOMP */ + putsyms("\b"); + for (i = bufp; *i; ++i) putsyms(" "); + for (; i > bufp; --i) putsyms("\b"); + *bufp = 0; +#endif /* NEWAUTOCOMP */ + } else tty_nhbell(); +#if defined(apollo) + } else if(c == '\n' || c == '\r') { +#else + } else if(c == '\n') { +#endif +#ifndef NEWAUTOCOMP + *bufp = 0; +#endif /* not NEWAUTOCOMP */ + break; + } else if(' ' <= (unsigned char) c && c != '\177' && + (bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)) { + /* avoid isprint() - some people don't have it + ' ' is not always a printing char */ +#ifdef NEWAUTOCOMP + char *i = eos(bufp); + +#endif /* NEWAUTOCOMP */ + *bufp = c; + bufp[1] = 0; + putsyms(bufp); + bufp++; + if (hook && (*hook)(obufp)) { + putsyms(bufp); +#ifndef NEWAUTOCOMP + bufp = eos(bufp); +#else /* NEWAUTOCOMP */ + /* pointer and cursor left where they were */ + for (i = bufp; *i; ++i) putsyms("\b"); + } else if (i > bufp) { + char *s = i; + + /* erase rest of prior guess */ + for (; i > bufp; --i) putsyms(" "); + for (; s > bufp; --s) putsyms("\b"); +#endif /* NEWAUTOCOMP */ + } + } else if(c == kill_char || c == '\177') { /* Robert Viduya */ + /* this test last - @ might be the kill_char */ +#ifndef NEWAUTOCOMP + while(bufp != obufp) { + bufp--; + putsyms("\b \b"); + } +#else /* NEWAUTOCOMP */ + for (; *bufp; ++bufp) putsyms(" "); + for (; bufp != obufp; --bufp) putsyms("\b \b"); + *bufp = 0; +#endif /* NEWAUTOCOMP */ + } else + tty_nhbell(); + } + ttyDisplay->toplin = 2; /* nonempty, no --More-- required */ + ttyDisplay->inread--; + clear_nhwindow(WIN_MESSAGE); /* clean up after ourselves */ +} + +void +xwaitforspace(s) +register const char *s; /* chars allowed besides return */ +{ + register int c, x = ttyDisplay ? (int) ttyDisplay->dismiss_more : '\n'; + + morc = 0; + + while((c = tty_nhgetch()) != '\n') { + if(iflags.cbreak) { + if ((s && index(s,c)) || c == x) { + morc = (char) c; + break; + } + tty_nhbell(); + } + } + +} + +#endif /* OVL1 */ +#ifdef OVL2 + +/* + * Implement extended command completion by using this hook into + * tty_getlin. Check the characters already typed, if they uniquely + * identify an extended command, expand the string to the whole + * command. + * + * Return TRUE if we've extended the string at base. Otherwise return FALSE. + * Assumptions: + * + * + we don't change the characters that are already in base + * + base has enough room to hold our string + */ +STATIC_OVL boolean +ext_cmd_getlin_hook(base) + char *base; +{ + int oindex, com_index; + + com_index = -1; + for (oindex = 0; extcmdlist[oindex].ef_txt != (char *)0; oindex++) { + if (!strncmpi(base, extcmdlist[oindex].ef_txt, strlen(base))) { + if (com_index == -1) /* no matches yet */ + com_index = oindex; + else /* more than 1 match */ + return FALSE; + } + } + if (com_index >= 0) { + Strcpy(base, extcmdlist[com_index].ef_txt); + return TRUE; + } + + return FALSE; /* didn't match anything */ +} + +/* + * Read in an extended command, doing command line completion. We + * stop when we have found enough characters to make a unique command. + */ +int +tty_get_ext_cmd() +{ + int i; + char buf[BUFSZ]; + + if (iflags.extmenu) return extcmd_via_menu(); + /* maybe a runtime option? */ + /* hooked_tty_getlin("#", buf, flags.cmd_comp ? ext_cmd_getlin_hook : (getlin_hook_proc) 0); */ +#ifdef REDO + hooked_tty_getlin("#", buf, in_doagain ? (getlin_hook_proc)0 + : ext_cmd_getlin_hook); +#else + hooked_tty_getlin("#", buf, ext_cmd_getlin_hook); +#endif + (void) mungspaces(buf); + if (buf[0] == 0 || buf[0] == '\033') return -1; + + for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++) + if (!strcmpi(buf, extcmdlist[i].ef_txt)) break; + +#ifdef REDO + if (!in_doagain) { + int j; + for (j = 0; buf[j]; j++) + savech(buf[j]); + savech('\n'); + } +#endif + + if (extcmdlist[i].ef_txt == (char *)0) { + pline("%s: unknown extended command.", buf); + i = -1; + } + + return i; +} + +#endif /* OVL2 */ + +#endif /* TTY_GRAPHICS */ + +/*getline.c*/ diff --git a/win/tty/termcap.c b/win/tty/termcap.c new file mode 100644 index 0000000..706e203 --- /dev/null +++ b/win/tty/termcap.c @@ -0,0 +1,1185 @@ +/* SCCS Id: @(#)termcap.c 3.4 2000/07/10 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#if defined (TTY_GRAPHICS) && !defined(NO_TERMS) + +#include "wintty.h" + +#include "tcap.h" + + +#ifdef MICROPORT_286_BUG +#define Tgetstr(key) (tgetstr(key,tbuf)) +#else +#define Tgetstr(key) (tgetstr(key,&tbufptr)) +#endif /* MICROPORT_286_BUG **/ + +static char * FDECL(s_atr2str, (int)); +static char * FDECL(e_atr2str, (int)); + +void FDECL(cmov, (int, int)); +void FDECL(nocmov, (int, int)); +#if defined(TEXTCOLOR) && defined(TERMLIB) +# ifdef OVLB +# if !defined(UNIX) || !defined(TERMINFO) +# ifndef TOS +static void FDECL(analyze_seq, (char *, int *, int *)); +# endif +# endif +static void NDECL(init_hilite); +static void NDECL(kill_hilite); +# endif /* OVLB */ +#endif + +#ifdef OVLB + /* (see tcap.h) -- nh_CM, nh_ND, nh_CD, nh_HI,nh_HE, nh_US,nh_UE, + ul_hack */ +struct tc_lcl_data tc_lcl_data = { 0, 0, 0, 0,0, 0,0, FALSE }; +#endif /* OVLB */ + +STATIC_VAR char *HO, *CL, *CE, *UP, *XD, *BC, *SO, *SE, *TI, *TE; +STATIC_VAR char *VS, *VE; +STATIC_VAR char *ME; +STATIC_VAR char *MR; +#if 0 +STATIC_VAR char *MB, *MH; +STATIC_VAR char *MD; /* may already be in use below */ +#endif +#ifdef TERMLIB +# ifdef TEXTCOLOR +STATIC_VAR char *MD; +# endif +STATIC_VAR int SG; +#ifdef OVLB +STATIC_OVL char PC = '\0'; +#else /* OVLB */ +STATIC_DCL char PC; +#endif /* OVLB */ +STATIC_VAR char tbuf[512]; +#endif + +#ifdef TEXTCOLOR +# ifdef TOS +const char *hilites[CLR_MAX]; /* terminal escapes for the various colors */ +# else +char NEARDATA *hilites[CLR_MAX]; /* terminal escapes for the various colors */ +# endif +#endif + +#ifdef OVLB +static char *KS = (char *)0, *KE = (char *)0; /* keypad sequences */ +static char nullstr[] = ""; +#endif /* OVLB */ + +#if defined(ASCIIGRAPH) && !defined(NO_TERMS) +extern boolean HE_resets_AS; +#endif + +#ifndef TERMLIB +STATIC_VAR char tgotobuf[20]; +# ifdef TOS +#define tgoto(fmt, x, y) (Sprintf(tgotobuf, fmt, y+' ', x+' '), tgotobuf) +# else +#define tgoto(fmt, x, y) (Sprintf(tgotobuf, fmt, y+1, x+1), tgotobuf) +# endif +#endif /* TERMLIB */ + +#ifdef OVLB + +void +tty_startup(wid, hgt) +int *wid, *hgt; +{ + register int i; +#ifdef TERMLIB + register const char *term; + register char *tptr; + char *tbufptr, *pc; + +# ifdef VMS + term = verify_termcap(); + if (!term) +# endif + term = getenv("TERM"); + +# if defined(TOS) && defined(__GNUC__) + if (!term) + term = "builtin"; /* library has a default */ +# endif + if (!term) +#endif +#ifndef ANSI_DEFAULT + error("Can't get TERM."); +#else +# ifdef TOS + { + CO = 80; LI = 25; + TI = VS = VE = TE = nullstr; + HO = "\033H"; + CE = "\033K"; /* the VT52 termcap */ + UP = "\033A"; + nh_CM = "\033Y%c%c"; /* used with function tgoto() */ + nh_ND = "\033C"; + XD = "\033B"; + BC = "\033D"; + SO = "\033p"; + SE = "\033q"; + /* HI and HE will be updated in init_hilite if we're using color */ + nh_HI = "\033p"; + nh_HE = "\033q"; + *wid = CO; + *hgt = LI; + CL = "\033E"; /* last thing set */ + return; + } +# else /* TOS */ + { +# ifdef MICRO + get_scr_size(); +# ifdef CLIPPING + if(CO < COLNO || LI < ROWNO+3) + setclipped(); +# endif +# endif + HO = "\033[H"; +/* nh_CD = "\033[J"; */ + CE = "\033[K"; /* the ANSI termcap */ +# ifndef TERMLIB + nh_CM = "\033[%d;%dH"; +# else + nh_CM = "\033[%i%d;%dH"; +# endif + UP = "\033[A"; + nh_ND = "\033[C"; + XD = "\033[B"; +# ifdef MICRO /* backspaces are non-destructive */ + BC = "\b"; +# else + BC = "\033[D"; +# endif + nh_HI = SO = "\033[1m"; + nh_US = "\033[4m"; + MR = "\033[7m"; + TI = nh_HE = ME = SE = nh_UE = "\033[0m"; + /* strictly, SE should be 2, and nh_UE should be 24, + but we can't trust all ANSI emulators to be + that complete. -3. */ +# ifndef MICRO + AS = "\016"; + AE = "\017"; +# endif + TE = VS = VE = nullstr; +# ifdef TEXTCOLOR + for (i = 0; i < CLR_MAX / 2; i++) + if (i != CLR_BLACK) { + hilites[i|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm")); + Sprintf(hilites[i|BRIGHT], "\033[1;3%dm", i); + if (i != CLR_GRAY) +# ifdef MICRO + if (i == CLR_BLUE) hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT]; + else +# endif + { + hilites[i] = (char *) alloc(sizeof("\033[0;3%dm")); + Sprintf(hilites[i], "\033[0;3%dm", i); + } + } +# endif + *wid = CO; + *hgt = LI; + CL = "\033[2J"; /* last thing set */ + return; + } +# endif /* TOS */ +#endif /* ANSI_DEFAULT */ + +#ifdef TERMLIB + tptr = (char *) alloc(1024); + + tbufptr = tbuf; + if(!strncmp(term, "5620", 4)) + flags.null = FALSE; /* this should be a termcap flag */ + if(tgetent(tptr, term) < 1) { + char buf[BUFSZ]; + (void) strncpy(buf, term, + (BUFSZ - 1) - (sizeof("Unknown terminal type: . "))); + buf[BUFSZ-1] = '\0'; + error("Unknown terminal type: %s.", term); + } + if ((pc = Tgetstr("pc")) != 0) + PC = *pc; + + if(!(BC = Tgetstr("le"))) /* both termcap and terminfo use le */ +# ifdef TERMINFO + error("Terminal must backspace."); +# else + if(!(BC = Tgetstr("bc"))) { /* termcap also uses bc/bs */ +# ifndef MINIMAL_TERM + if(!tgetflag("bs")) + error("Terminal must backspace."); +# endif + BC = tbufptr; + tbufptr += 2; + *BC = '\b'; + } +# endif + +# ifdef MINIMAL_TERM + HO = (char *)0; +# else + HO = Tgetstr("ho"); +# endif + /* + * LI and CO are set in ioctl.c via a TIOCGWINSZ if available. If + * the kernel has values for either we should use them rather than + * the values from TERMCAP ... + */ +# ifndef MICRO + if (!CO) CO = tgetnum("co"); + if (!LI) LI = tgetnum("li"); +# else +# if defined(TOS) && defined(__GNUC__) + if (!strcmp(term, "builtin")) + get_scr_size(); + else { +# endif + CO = tgetnum("co"); + LI = tgetnum("li"); + if (!LI || !CO) /* if we don't override it */ + get_scr_size(); +# if defined(TOS) && defined(__GNUC__) + } +# endif +# endif +# ifdef CLIPPING + if(CO < COLNO || LI < ROWNO+3) + setclipped(); +# endif + nh_ND = Tgetstr("nd"); + if(tgetflag("os")) + error("NetHack can't have OS."); + if(tgetflag("ul")) + ul_hack = TRUE; + CE = Tgetstr("ce"); + UP = Tgetstr("up"); + /* It seems that xd is no longer supported, and we should use + a linefeed instead; unfortunately this requires resetting + CRMOD, and many output routines will have to be modified + slightly. Let's leave that till the next release. */ + XD = Tgetstr("xd"); +/* not: XD = Tgetstr("do"); */ + if(!(nh_CM = Tgetstr("cm"))) { + if(!UP && !HO) + error("NetHack needs CM or UP or HO."); + tty_raw_print("Playing NetHack on terminals without CM is suspect."); + tty_wait_synch(); + } + SO = Tgetstr("so"); + SE = Tgetstr("se"); + nh_US = Tgetstr("us"); + nh_UE = Tgetstr("ue"); + SG = tgetnum("sg"); /* -1: not fnd; else # of spaces left by so */ + if(!SO || !SE || (SG > 0)) SO = SE = nh_US = nh_UE = nullstr; + TI = Tgetstr("ti"); + TE = Tgetstr("te"); + VS = VE = nullstr; +# ifdef TERMINFO + VS = Tgetstr("eA"); /* enable graphics */ +# endif + KS = Tgetstr("ks"); /* keypad start (special mode) */ + KE = Tgetstr("ke"); /* keypad end (ordinary mode [ie, digits]) */ + MR = Tgetstr("mr"); /* reverse */ +# if 0 + MB = Tgetstr("mb"); /* blink */ + MD = Tgetstr("md"); /* boldface */ + MH = Tgetstr("mh"); /* dim */ +# endif + ME = Tgetstr("me"); /* turn off all attributes */ + if (!ME || (SE == nullstr)) ME = SE; /* default to SE value */ + + /* Get rid of padding numbers for nh_HI and nh_HE. Hope they + * aren't really needed!!! nh_HI and nh_HE are outputted to the + * pager as a string - so how can you send it NULs??? + * -jsb + */ + nh_HI = (char *) alloc((unsigned)(strlen(SO)+1)); + nh_HE = (char *) alloc((unsigned)(strlen(ME)+1)); + i = 0; + while (digit(SO[i])) i++; + Strcpy(nh_HI, &SO[i]); + i = 0; + while (digit(ME[i])) i++; + Strcpy(nh_HE, &ME[i]); + AS = Tgetstr("as"); + AE = Tgetstr("ae"); + nh_CD = Tgetstr("cd"); +# ifdef TEXTCOLOR + MD = Tgetstr("md"); +# endif +# ifdef TEXTCOLOR +# if defined(TOS) && defined(__GNUC__) + if (!strcmp(term, "builtin") || !strcmp(term, "tw52") || + !strcmp(term, "st52")) { + init_hilite(); + } +# else + init_hilite(); +# endif +# endif + *wid = CO; + *hgt = LI; + if (!(CL = Tgetstr("cl"))) /* last thing set */ + error("NetHack needs CL."); + if ((int)(tbufptr - tbuf) > (int)(sizeof tbuf)) + error("TERMCAP entry too big...\n"); + free((genericptr_t)tptr); +#endif /* TERMLIB */ +} + +/* note: at present, this routine is not part of the formal window interface */ +/* deallocate resources prior to final termination */ +void +tty_shutdown() +{ +#if defined(TEXTCOLOR) && defined(TERMLIB) + kill_hilite(); +#endif + /* we don't attempt to clean up individual termcap variables [yet?] */ + return; +} + +void +tty_number_pad(state) +int state; +{ + switch (state) { + case -1: /* activate keypad mode (escape sequences) */ + if (KS && *KS) xputs(KS); + break; + case 1: /* activate numeric mode for keypad (digits) */ + if (KE && *KE) xputs(KE); + break; + case 0: /* don't need to do anything--leave terminal as-is */ + default: + break; + } +} + +#ifdef TERMLIB +extern void NDECL((*decgraphics_mode_callback)); /* defined in drawing.c */ +static void NDECL(tty_decgraphics_termcap_fixup); + +/* + We call this routine whenever DECgraphics mode is enabled, even if it + has been previously set, in case the user manages to reset the fonts. + The actual termcap fixup only needs to be done once, but we can't + call xputs() from the option setting or graphics assigning routines, + so this is a convenient hook. + */ +static void +tty_decgraphics_termcap_fixup() +{ + static char ctrlN[] = "\016"; + static char ctrlO[] = "\017"; + static char appMode[] = "\033="; + static char numMode[] = "\033>"; + + /* these values are missing from some termcaps */ + if (!AS) AS = ctrlN; /* ^N (shift-out [graphics font]) */ + if (!AE) AE = ctrlO; /* ^O (shift-in [regular font]) */ + if (!KS) KS = appMode; /* ESC= (application keypad mode) */ + if (!KE) KE = numMode; /* ESC> (numeric keypad mode) */ + /* + * Select the line-drawing character set as the alternate font. + * Do not select NA ASCII as the primary font since people may + * reasonably be using the UK character set. + */ + if (iflags.DECgraphics) xputs("\033)0"); +#ifdef PC9800 + init_hilite(); +#endif + +#if defined(ASCIIGRAPH) && !defined(NO_TERMS) + /* some termcaps suffer from the bizarre notion that resetting + video attributes should also reset the chosen character set */ + { + const char *nh_he = nh_HE, *ae = AE; + int he_limit, ae_length; + + if (digit(*ae)) { /* skip over delay prefix, if any */ + do ++ae; while (digit(*ae)); + if (*ae == '.') { ++ae; if (digit(*ae)) ++ae; } + if (*ae == '*') ++ae; + } + /* can't use nethack's case-insensitive strstri() here, and some old + systems don't have strstr(), so use brute force substring search */ + ae_length = strlen(ae), he_limit = strlen(nh_he); + while (he_limit >= ae_length) { + if (strncmp(nh_he, ae, ae_length) == 0) { + HE_resets_AS = TRUE; + break; + } + ++nh_he, --he_limit; + } + } +#endif +} +#endif /* TERMLIB */ + +#if defined(ASCIIGRAPH) && defined(PC9800) +extern void NDECL((*ibmgraphics_mode_callback)); /* defined in drawing.c */ +#endif + +#ifdef PC9800 +extern void NDECL((*ascgraphics_mode_callback)); /* defined in drawing.c */ +static void NDECL(tty_ascgraphics_hilite_fixup); + +static void +tty_ascgraphics_hilite_fixup() +{ + register int c; + + for (c = 0; c < CLR_MAX / 2; c++) + if (c != CLR_BLACK) { + hilites[c|BRIGHT] = (char *) alloc(sizeof("\033[1;3%dm")); + Sprintf(hilites[c|BRIGHT], "\033[1;3%dm", c); + if (c != CLR_GRAY) { + hilites[c] = (char *) alloc(sizeof("\033[0;3%dm")); + Sprintf(hilites[c], "\033[0;3%dm", c); + } + } +} +#endif /* PC9800 */ + +void +tty_start_screen() +{ + xputs(TI); + xputs(VS); +#ifdef PC9800 + if (!iflags.IBMgraphics && !iflags.DECgraphics) + tty_ascgraphics_hilite_fixup(); + /* set up callback in case option is not set yet but toggled later */ + ascgraphics_mode_callback = tty_ascgraphics_hilite_fixup; +# ifdef ASCIIGRAPH + if (iflags.IBMgraphics) init_hilite(); + /* set up callback in case option is not set yet but toggled later */ + ibmgraphics_mode_callback = init_hilite; +# endif +#endif /* PC9800 */ + +#ifdef TERMLIB + if (iflags.DECgraphics) tty_decgraphics_termcap_fixup(); + /* set up callback in case option is not set yet but toggled later */ + decgraphics_mode_callback = tty_decgraphics_termcap_fixup; +#endif + if (iflags.num_pad) tty_number_pad(1); /* make keypad send digits */ +} + +void +tty_end_screen() +{ + clear_screen(); + xputs(VE); + xputs(TE); +} + +/* Cursor movements */ + +#endif /* OVLB */ + +#ifdef OVL0 +/* Note to OVLx tinkerers. The placement of this overlay controls the location + of the function xputc(). This function is not currently in trampoli.[ch] + files for what is deemed to be performance reasons. If this define is moved + and or xputc() is taken out of the ROOT overlay, then action must be taken + in trampoli.[ch]. */ + +void +nocmov(x, y) +int x,y; +{ + if ((int) ttyDisplay->cury > y) { + if(UP) { + while ((int) ttyDisplay->cury > y) { /* Go up. */ + xputs(UP); + ttyDisplay->cury--; + } + } else if(nh_CM) { + cmov(x, y); + } else if(HO) { + home(); + tty_curs(BASE_WINDOW, x+1, y); + } /* else impossible("..."); */ + } else if ((int) ttyDisplay->cury < y) { + if(XD) { + while((int) ttyDisplay->cury < y) { + xputs(XD); + ttyDisplay->cury++; + } + } else if(nh_CM) { + cmov(x, y); + } else { + while((int) ttyDisplay->cury < y) { + xputc('\n'); + ttyDisplay->curx = 0; + ttyDisplay->cury++; + } + } + } + if ((int) ttyDisplay->curx < x) { /* Go to the right. */ + if(!nh_ND) cmov(x, y); else /* bah */ + /* should instead print what is there already */ + while ((int) ttyDisplay->curx < x) { + xputs(nh_ND); + ttyDisplay->curx++; + } + } else if ((int) ttyDisplay->curx > x) { + while ((int) ttyDisplay->curx > x) { /* Go to the left. */ + xputs(BC); + ttyDisplay->curx--; + } + } +} + +void +cmov(x, y) +register int x, y; +{ + xputs(tgoto(nh_CM, x, y)); + ttyDisplay->cury = y; + ttyDisplay->curx = x; +} + +/* See note at OVLx ifdef above. xputc() is a special function. */ +void +xputc(c) +#if defined(apollo) +int c; +#else +char c; +#endif +{ + (void) putchar(c); +} + +void +xputs(s) +const char *s; +{ +# ifndef TERMLIB + (void) fputs(s, stdout); +# else +# if defined(NHSTDC) || defined(ULTRIX_PROTO) + tputs(s, 1, (int (*)())xputc); +# else + tputs(s, 1, xputc); +# endif +# endif +} + +void +cl_end() +{ + if(CE) + xputs(CE); + else { /* no-CE fix - free after Harold Rynes */ + /* this looks terrible, especially on a slow terminal + but is better than nothing */ + register int cx = ttyDisplay->curx+1; + + while(cx < CO) { + xputc(' '); + cx++; + } + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, + (int)ttyDisplay->cury); + } +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +clear_screen() +{ + /* note: if CL is null, then termcap initialization failed, + so don't attempt screen-oriented I/O during final cleanup. + */ + if (CL) { + xputs(CL); + home(); + } +} + +#endif /* OVLB */ +#ifdef OVL0 + +void +home() +{ + if(HO) + xputs(HO); + else if(nh_CM) + xputs(tgoto(nh_CM, 0, 0)); + else + tty_curs(BASE_WINDOW, 1, 0); /* using UP ... */ + ttyDisplay->curx = ttyDisplay->cury = 0; +} + +void +standoutbeg() +{ + if(SO) xputs(SO); +} + +void +standoutend() +{ + if(SE) xputs(SE); +} + +#if 0 /* if you need one of these, uncomment it (here and in extern.h) */ +void +revbeg() +{ + if(MR) xputs(MR); +} + +void +boldbeg() +{ + if(MD) xputs(MD); +} + +void +blinkbeg() +{ + if(MB) xputs(MB); +} + +void +dimbeg() +/* not in most termcap entries */ +{ + if(MH) xputs(MH); +} + +void +m_end() +{ + if(ME) xputs(ME); +} +#endif + +#endif /* OVL0 */ +#ifdef OVLB + +void +backsp() +{ + xputs(BC); +} + +void +tty_nhbell() +{ + if (flags.silent) return; + (void) putchar('\007'); /* curx does not change */ + (void) fflush(stdout); +} + +#endif /* OVLB */ +#ifdef OVL0 + +#ifdef ASCIIGRAPH +void +graph_on() { + if (AS) xputs(AS); +} + +void +graph_off() { + if (AE) xputs(AE); +} +#endif + +#endif /* OVL0 */ +#ifdef OVL1 + +#if !defined(MICRO) +# ifdef VMS +static const short tmspc10[] = { /* from termcap */ + 0, 2000, 1333, 909, 743, 666, 333, 166, 83, 55, 50, 41, 27, 20, 13, 10, + 5 +}; +# else +static const short tmspc10[] = { /* from termcap */ + 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5 +}; +# endif +#endif + +/* delay 50 ms */ +void +tty_delay_output() +{ +#if defined(MICRO) + register int i; +#endif +#ifdef TIMED_DELAY + if (flags.nap) { + (void) fflush(stdout); + msleep(50); /* sleep for 50 milliseconds */ + return; + } +#endif +#if defined(MICRO) + /* simulate the delay with "cursor here" */ + for (i = 0; i < 3; i++) { + cmov(ttyDisplay->curx, ttyDisplay->cury); + (void) fflush(stdout); + } +#else /* MICRO */ + /* BUG: if the padding character is visible, as it is on the 5620 + then this looks terrible. */ + if(flags.null) +# ifdef TERMINFO + /* cbosgd!cbcephus!pds for SYS V R2 */ +# ifdef NHSTDC + tputs("$<50>", 1, (int (*)())xputc); +# else + tputs("$<50>", 1, xputc); +# endif +# else +# if defined(NHSTDC) || defined(ULTRIX_PROTO) + tputs("50", 1, (int (*)())xputc); +# else + tputs("50", 1, xputc); +# endif +# endif + + else if(ospeed > 0 && ospeed < SIZE(tmspc10) && nh_CM) { + /* delay by sending cm(here) an appropriate number of times */ + register int cmlen = strlen(tgoto(nh_CM, ttyDisplay->curx, + ttyDisplay->cury)); + register int i = 500 + tmspc10[ospeed]/2; + + while(i > 0) { + cmov((int)ttyDisplay->curx, (int)ttyDisplay->cury); + i -= cmlen*tmspc10[ospeed]; + } + } +#endif /* MICRO */ +} + +#endif /* OVL1 */ +#ifdef OVLB + +void +cl_eos() /* free after Robert Viduya */ +{ /* must only be called with curx = 1 */ + + if(nh_CD) + xputs(nh_CD); + else { + register int cy = ttyDisplay->cury+1; + while(cy <= LI-2) { + cl_end(); + xputc('\n'); + cy++; + } + cl_end(); + tty_curs(BASE_WINDOW, (int)ttyDisplay->curx+1, + (int)ttyDisplay->cury); + } +} + +#if defined(TEXTCOLOR) && defined(TERMLIB) +# if defined(UNIX) && defined(TERMINFO) +/* + * Sets up color highlighting, using terminfo(4) escape sequences. + * + * Having never seen a terminfo system without curses, we assume this + * inclusion is safe. On systems with color terminfo, it should define + * the 8 COLOR_FOOs, and avoid us having to guess whether this particular + * terminfo uses BGR or RGB for its indexes. + * + * If we don't get the definitions, then guess. Original color terminfos + * used BGR for the original Sf (setf, Standard foreground) codes, but + * there was a near-total lack of user documentation, so some subsequent + * terminfos, such as early Linux ncurses and SCO UNIX, used RGB. Possibly + * as a result of the confusion, AF (setaf, ANSI Foreground) codes were + * introduced, but this caused yet more confusion. Later Linux ncurses + * have BGR Sf, RGB AF, and RGB COLOR_FOO, which appears to be the SVR4 + * standard. We could switch the colors around when using Sf with ncurses, + * which would help things on later ncurses and hurt things on early ncurses. + * We'll try just preferring AF and hoping it always agrees with COLOR_FOO, + * and falling back to Sf if AF isn't defined. + * + * In any case, treat black specially so we don't try to display black + * characters on the assumed black background. + */ + + /* `curses' is aptly named; various versions don't like these + macros used elsewhere within nethack; fortunately they're + not needed beyond this point, so we don't need to worry + about reconstructing them after the header file inclusion. */ +#undef delay_output +#undef TRUE +#undef FALSE +#define m_move curses_m_move /* Some curses.h decl m_move(), not used here */ + +#include + +#ifndef LINUX +extern char *tparm(); +#endif + +# ifdef COLOR_BLACK /* trust include file */ +#undef COLOR_BLACK +# else +# ifndef _M_UNIX /* guess BGR */ +#define COLOR_BLUE 1 +#define COLOR_GREEN 2 +#define COLOR_CYAN 3 +#define COLOR_RED 4 +#define COLOR_MAGENTA 5 +#define COLOR_YELLOW 6 +#define COLOR_WHITE 7 +# else /* guess RGB */ +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 +# endif +# endif +#define COLOR_BLACK COLOR_BLUE + +const int ti_map[8] = { + COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, + COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE }; + +static void +init_hilite() +{ + register int c; + char *setf, *scratch; + + for (c = 0; c < SIZE(hilites); c++) + hilites[c] = nh_HI; + hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0; + + if (tgetnum("Co") < 8 + || ((setf = tgetstr("AF", (char **)0)) == (char *)0 + && (setf = tgetstr("Sf", (char **)0)) == (char *)0)) + return; + + for (c = 0; c < CLR_MAX / 2; c++) { + scratch = tparm(setf, ti_map[c]); + if (c != CLR_GRAY) { + hilites[c] = (char *) alloc(strlen(scratch) + 1); + Strcpy(hilites[c], scratch); + } + if (c != CLR_BLACK) { + hilites[c|BRIGHT] = (char*) alloc(strlen(scratch)+strlen(MD)+1); + Strcpy(hilites[c|BRIGHT], MD); + Strcat(hilites[c|BRIGHT], scratch); + } + + } +} + +# else /* UNIX && TERMINFO */ + +# ifndef TOS +/* find the foreground and background colors set by nh_HI or nh_HE */ +static void +analyze_seq (str, fg, bg) +char *str; +int *fg, *bg; +{ + register int c, code; + int len; + +# ifdef MICRO + *fg = CLR_GRAY; *bg = CLR_BLACK; +# else + *fg = *bg = NO_COLOR; +# endif + + c = (str[0] == '\233') ? 1 : 2; /* index of char beyond esc prefix */ + len = strlen(str) - 1; /* length excluding attrib suffix */ + if ((c != 1 && (str[0] != '\033' || str[1] != '[')) || + (len - c) < 1 || str[len] != 'm') + return; + + while (c < len) { + if ((code = atoi(&str[c])) == 0) { /* reset */ + /* this also catches errors */ +# ifdef MICRO + *fg = CLR_GRAY; *bg = CLR_BLACK; +# else + *fg = *bg = NO_COLOR; +# endif + } else if (code == 1) { /* bold */ + *fg |= BRIGHT; +# if 0 + /* I doubt we'll ever resort to using blinking characters, + unless we want a pulsing glow for something. But, in case + we do... - 3. */ + } else if (code == 5) { /* blinking */ + *fg |= BLINK; + } else if (code == 25) { /* stop blinking */ + *fg &= ~BLINK; +# endif + } else if (code == 7 || code == 27) { /* reverse */ + code = *fg & ~BRIGHT; + *fg = *bg | (*fg & BRIGHT); + *bg = code; + } else if (code >= 30 && code <= 37) { /* hi_foreground RGB */ + *fg = code - 30; + } else if (code >= 40 && code <= 47) { /* hi_background RGB */ + *bg = code - 40; + } + while (digit(str[++c])); + c++; + } +} +# endif + +/* + * Sets up highlighting sequences, using ANSI escape sequences (highlight code + * found in print.c). The nh_HI and nh_HE sequences (usually from SO) are + * scanned to find foreground and background colors. + */ + +static void +init_hilite() +{ + register int c; +# ifdef TOS + extern unsigned long tos_numcolors; /* in tos.c */ + static char NOCOL[] = "\033b0", COLHE[] = "\033q\033b0"; + + if (tos_numcolors <= 2) { + return; + } +/* Under TOS, the "bright" and "dim" colors are reversed. Moreover, + * on the Falcon the dim colors are *really* dim; so we make most + * of the colors the bright versions, with a few exceptions where + * the dim ones look OK. + */ + hilites[0] = NOCOL; + for (c = 1; c < SIZE(hilites); c++) { + char *foo; + foo = (char *) alloc(sizeof("\033b0")); + if (tos_numcolors > 4) + Sprintf(foo, "\033b%c", (c&~BRIGHT)+'0'); + else + Strcpy(foo, "\033b0"); + hilites[c] = foo; + } + + if (tos_numcolors == 4) { + TI = "\033b0\033c3\033E\033e"; + TE = "\033b3\033c0\033J"; + nh_HE = COLHE; + hilites[CLR_GREEN] = hilites[CLR_GREEN|BRIGHT] = "\033b2"; + hilites[CLR_RED] = hilites[CLR_RED|BRIGHT] = "\033b1"; + } else { + sprintf(hilites[CLR_BROWN], "\033b%c", (CLR_BROWN^BRIGHT)+'0'); + sprintf(hilites[CLR_GREEN], "\033b%c", (CLR_GREEN^BRIGHT)+'0'); + + TI = "\033b0\033c\017\033E\033e"; + TE = "\033b\017\033c0\033J"; + nh_HE = COLHE; + hilites[CLR_WHITE] = hilites[CLR_BLACK] = NOCOL; + hilites[NO_COLOR] = hilites[CLR_GRAY]; + } + +# else /* TOS */ + + int backg, foreg, hi_backg, hi_foreg; + + for (c = 0; c < SIZE(hilites); c++) + hilites[c] = nh_HI; + hilites[CLR_GRAY] = hilites[NO_COLOR] = (char *)0; + + analyze_seq(nh_HI, &hi_foreg, &hi_backg); + analyze_seq(nh_HE, &foreg, &backg); + + for (c = 0; c < SIZE(hilites); c++) + /* avoid invisibility */ + if ((backg & ~BRIGHT) != c) { +# ifdef MICRO + if (c == CLR_BLUE) continue; +# endif + if (c == foreg) + hilites[c] = (char *)0; + else if (c != hi_foreg || backg != hi_backg) { + hilites[c] = (char *) alloc(sizeof("\033[%d;3%d;4%dm")); + Sprintf(hilites[c], "\033[%d", !!(c & BRIGHT)); + if ((c | BRIGHT) != (foreg | BRIGHT)) + Sprintf(eos(hilites[c]), ";3%d", c & ~BRIGHT); + if (backg != CLR_BLACK) + Sprintf(eos(hilites[c]), ";4%d", backg & ~BRIGHT); + Strcat(hilites[c], "m"); + } + } + +# ifdef MICRO + /* brighten low-visibility colors */ + hilites[CLR_BLUE] = hilites[CLR_BLUE|BRIGHT]; +# endif +# endif /* TOS */ +} +# endif /* UNIX */ + +static void +kill_hilite() +{ +# ifndef TOS + register int c; + + for (c = 0; c < CLR_MAX / 2; c++) { + if (hilites[c|BRIGHT] == hilites[c]) hilites[c|BRIGHT] = 0; + if (hilites[c] && (hilites[c] != nh_HI)) + free((genericptr_t) hilites[c]), hilites[c] = 0; + if (hilites[c|BRIGHT] && (hilites[c|BRIGHT] != nh_HI)) + free((genericptr_t) hilites[c|BRIGHT]), hilites[c|BRIGHT] = 0; + } +# endif + return; +} +#endif /* TEXTCOLOR */ + + +static char nulstr[] = ""; + +static char * +s_atr2str(n) +int n; +{ + switch (n) { + case ATR_ULINE: + if(nh_US) return nh_US; + case ATR_BOLD: + case ATR_BLINK: +#if defined(TERMLIB) && defined(TEXTCOLOR) + if (MD) return MD; +#endif + return nh_HI; + case ATR_INVERSE: + return MR; + } + return nulstr; +} + +static char * +e_atr2str(n) +int n; +{ + switch (n) { + case ATR_ULINE: + if(nh_UE) return nh_UE; + case ATR_BOLD: + case ATR_BLINK: + return nh_HE; + case ATR_INVERSE: + return ME; + } + return nulstr; +} + + +void +term_start_attr(attr) +int attr; +{ + if (attr) { + xputs(s_atr2str(attr)); + } +} + + +void +term_end_attr(attr) +int attr; +{ + if(attr) { + xputs(e_atr2str(attr)); + } +} + + +void +term_start_raw_bold() +{ + xputs(nh_HI); +} + + +void +term_end_raw_bold() +{ + xputs(nh_HE); +} + + +#ifdef TEXTCOLOR + +void +term_end_color() +{ + xputs(nh_HE); +} + + +void +term_start_color(color) +int color; +{ + xputs(hilites[color]); +} + + +int +has_color(color) +int color; +{ +#ifdef X11_GRAPHICS + /* XXX has_color() should be added to windowprocs */ + if (windowprocs.name != NULL && + !strcmpi(windowprocs.name, "X11")) return TRUE; +#endif +#ifdef GEM_GRAPHICS + /* XXX has_color() should be added to windowprocs */ + if (windowprocs.name != NULL && + !strcmpi(windowprocs.name, "Gem")) return TRUE; +#endif +#ifdef QT_GRAPHICS + /* XXX has_color() should be added to windowprocs */ + if (windowprocs.name != NULL && + !strcmpi(windowprocs.name, "Qt")) return TRUE; +#endif +#ifdef AMII_GRAPHICS + /* hilites[] not used */ + return iflags.use_color; +#endif + return hilites[color] != (char *)0; +} + +#endif /* TEXTCOLOR */ + +#endif /* OVLB */ + +#endif /* TTY_GRAPHICS */ + +/*termcap.c*/ diff --git a/win/tty/topl.c b/win/tty/topl.c new file mode 100644 index 0000000..1c62f3d --- /dev/null +++ b/win/tty/topl.c @@ -0,0 +1,467 @@ +/* SCCS Id: @(#)topl.c 3.4 1996/10/24 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +#ifdef TTY_GRAPHICS + +#include "tcap.h" +#include "wintty.h" +#include + +#ifndef C /* this matches src/cmd.c */ +#define C(c) (0x1f & (c)) +#endif + +STATIC_DCL void FDECL(redotoplin, (const char*)); +STATIC_DCL void FDECL(topl_putsym, (CHAR_P)); +STATIC_DCL void NDECL(remember_topl); +STATIC_DCL void FDECL(removetopl, (int)); + +#ifdef OVLB + +int +tty_doprev_message() +{ + register struct WinDesc *cw = wins[WIN_MESSAGE]; + + winid prevmsg_win; + int i; + if ((iflags.prevmsg_window != 's') && !ttyDisplay->inread) { /* not single */ + if(iflags.prevmsg_window == 'f') { /* full */ + prevmsg_win = create_nhwindow(NHW_MENU); + putstr(prevmsg_win, 0, "Message History"); + putstr(prevmsg_win, 0, ""); + cw->maxcol = cw->maxrow; + i = cw->maxcol; + do { + if(cw->data[i] && strcmp(cw->data[i], "") ) + putstr(prevmsg_win, 0, cw->data[i]); + i = (i + 1) % cw->rows; + } while (i != cw->maxcol); + putstr(prevmsg_win, 0, toplines); + display_nhwindow(prevmsg_win, TRUE); + destroy_nhwindow(prevmsg_win); + } else if (iflags.prevmsg_window == 'c') { /* combination */ + do { + morc = 0; + if (cw->maxcol == cw->maxrow) { + ttyDisplay->dismiss_more = C('p'); /* allowed at --More-- */ + redotoplin(toplines); + cw->maxcol--; + if (cw->maxcol < 0) cw->maxcol = cw->rows-1; + if (!cw->data[cw->maxcol]) + cw->maxcol = cw->maxrow; + } else if (cw->maxcol == (cw->maxrow - 1)){ + ttyDisplay->dismiss_more = C('p'); /* allowed at --More-- */ + redotoplin(cw->data[cw->maxcol]); + cw->maxcol--; + if (cw->maxcol < 0) cw->maxcol = cw->rows-1; + if (!cw->data[cw->maxcol]) + cw->maxcol = cw->maxrow; + } else { + prevmsg_win = create_nhwindow(NHW_MENU); + putstr(prevmsg_win, 0, "Message History"); + putstr(prevmsg_win, 0, ""); + cw->maxcol = cw->maxrow; + i = cw->maxcol; + do { + if(cw->data[i] && strcmp(cw->data[i], "") ) + putstr(prevmsg_win, 0, cw->data[i]); + i = (i + 1) % cw->rows; + } while (i != cw->maxcol); + putstr(prevmsg_win, 0, toplines); + display_nhwindow(prevmsg_win, TRUE); + destroy_nhwindow(prevmsg_win); + } + + } while (morc == C('p')); + ttyDisplay->dismiss_more = 0; + } else { /* reversed */ + morc = 0; + prevmsg_win = create_nhwindow(NHW_MENU); + putstr(prevmsg_win, 0, "Message History"); + putstr(prevmsg_win, 0, ""); + putstr(prevmsg_win, 0, toplines); + cw->maxcol=cw->maxrow-1; + if(cw->maxcol < 0) cw->maxcol = cw->rows-1; + do { + putstr(prevmsg_win, 0, cw->data[cw->maxcol]); + cw->maxcol--; + if (cw->maxcol < 0) cw->maxcol = cw->rows-1; + if (!cw->data[cw->maxcol]) + cw->maxcol = cw->maxrow; + } while (cw->maxcol != cw->maxrow); + + display_nhwindow(prevmsg_win, TRUE); + destroy_nhwindow(prevmsg_win); + cw->maxcol = cw->maxrow; + ttyDisplay->dismiss_more = 0; + } + } else if(iflags.prevmsg_window == 's') { /* single */ + ttyDisplay->dismiss_more = C('p'); /* allowed at --More-- */ + do { + morc = 0; + if (cw->maxcol == cw->maxrow) + redotoplin(toplines); + else if (cw->data[cw->maxcol]) + redotoplin(cw->data[cw->maxcol]); + cw->maxcol--; + if (cw->maxcol < 0) cw->maxcol = cw->rows-1; + if (!cw->data[cw->maxcol]) + cw->maxcol = cw->maxrow; + } while (morc == C('p')); + ttyDisplay->dismiss_more = 0; + } + return 0; +} + +#endif /* OVLB */ +#ifdef OVL1 + +STATIC_OVL void +redotoplin(str) + const char *str; +{ + int otoplin = ttyDisplay->toplin; + home(); + if(*str & 0x80) { + /* kludge for the / command, the only time we ever want a */ + /* graphics character on the top line */ + g_putch((int)*str++); + ttyDisplay->curx++; + } + end_glyphout(); /* in case message printed during graphics output */ + putsyms(str); + cl_end(); + ttyDisplay->toplin = 1; + if(ttyDisplay->cury && otoplin != 3) + more(); +} + +STATIC_OVL void +remember_topl() +{ + register struct WinDesc *cw = wins[WIN_MESSAGE]; + int idx = cw->maxrow; + unsigned len = strlen(toplines) + 1; + + if (len > (unsigned)cw->datlen[idx]) { + if (cw->data[idx]) free(cw->data[idx]); + len += (8 - (len & 7)); /* pad up to next multiple of 8 */ + cw->data[idx] = (char *)alloc(len); + cw->datlen[idx] = (short)len; + } + Strcpy(cw->data[idx], toplines); + cw->maxcol = cw->maxrow = (idx + 1) % cw->rows; +} + +void +addtopl(s) +const char *s; +{ + register struct WinDesc *cw = wins[WIN_MESSAGE]; + + tty_curs(BASE_WINDOW,cw->curx+1,cw->cury); + putsyms(s); + cl_end(); + ttyDisplay->toplin = 1; +} + +#endif /* OVL1 */ +#ifdef OVL2 + +void +more() +{ + struct WinDesc *cw = wins[WIN_MESSAGE]; + + /* avoid recursion -- only happens from interrupts */ + if(ttyDisplay->inmore++) + return; + + if(ttyDisplay->toplin) { + tty_curs(BASE_WINDOW, cw->curx+1, cw->cury); + if(cw->curx >= CO - 8) topl_putsym('\n'); + } + + if(flags.standout) + standoutbeg(); + putsyms(defmorestr); + if(flags.standout) + standoutend(); + + xwaitforspace("\033 "); + + if(morc == '\033') + cw->flags |= WIN_STOP; + + if(ttyDisplay->toplin && cw->cury) { + docorner(1, cw->cury+1); + cw->curx = cw->cury = 0; + home(); + } else if(morc == '\033') { + cw->curx = cw->cury = 0; + home(); + cl_end(); + } + ttyDisplay->toplin = 0; + ttyDisplay->inmore = 0; +} + +void +update_topl(bp) + register const char *bp; +{ + register char *tl, *otl; + register int n0; + int notdied = 1; + struct WinDesc *cw = wins[WIN_MESSAGE]; + + /* If there is room on the line, print message on same line */ + /* But messages like "You die..." deserve their own line */ + n0 = strlen(bp); + if ((ttyDisplay->toplin == 1 || (cw->flags & WIN_STOP)) && + cw->cury == 0 && + n0 + (int)strlen(toplines) + 3 < CO-8 && /* room for --More-- */ + (notdied = strncmp(bp, "You die", 7))) { + Strcat(toplines, " "); + Strcat(toplines, bp); + cw->curx += 2; + if(!(cw->flags & WIN_STOP)) + addtopl(bp); + return; + } else if (!(cw->flags & WIN_STOP)) { + if(ttyDisplay->toplin == 1) more(); + else if(cw->cury) { /* for when flags.toplin == 2 && cury > 1 */ + docorner(1, cw->cury+1); /* reset cury = 0 if redraw screen */ + cw->curx = cw->cury = 0;/* from home--cls() & docorner(1,n) */ + } + } + remember_topl(); + (void) strncpy(toplines, bp, TBUFSZ); + toplines[TBUFSZ - 1] = 0; + + for(tl = toplines; n0 >= CO; ){ + otl = tl; + for(tl+=CO-1; tl != otl && !isspace(*tl); --tl) ; + if(tl == otl) { + /* Eek! A huge token. Try splitting after it. */ + tl = index(otl, ' '); + if (!tl) break; /* No choice but to spit it out whole. */ + } + *tl++ = '\n'; + n0 = strlen(tl); + } + if(!notdied) cw->flags &= ~WIN_STOP; + if(!(cw->flags & WIN_STOP)) redotoplin(toplines); +} + +STATIC_OVL +void +topl_putsym(c) + char c; +{ + register struct WinDesc *cw = wins[WIN_MESSAGE]; + + if(cw == (struct WinDesc *) 0) panic("Putsym window MESSAGE nonexistant"); + + switch(c) { + case '\b': + if(ttyDisplay->curx == 0 && ttyDisplay->cury > 0) + tty_curs(BASE_WINDOW, CO, (int)ttyDisplay->cury-1); + backsp(); + ttyDisplay->curx--; + cw->curx = ttyDisplay->curx; + return; + case '\n': + cl_end(); + ttyDisplay->curx = 0; + ttyDisplay->cury++; + cw->cury = ttyDisplay->cury; +#ifdef WIN32CON + (void) putchar(c); +#endif + break; + default: + if(ttyDisplay->curx == CO-1) + topl_putsym('\n'); /* 1 <= curx <= CO; avoid CO */ +#ifdef WIN32CON + (void) putchar(c); +#endif + ttyDisplay->curx++; + } + cw->curx = ttyDisplay->curx; + if(cw->curx == 0) cl_end(); +#ifndef WIN32CON + (void) putchar(c); +#endif +} + +void +putsyms(str) + const char *str; +{ + while(*str) + topl_putsym(*str++); +} + +STATIC_OVL void +removetopl(n) +register int n; +{ + /* assume addtopl() has been done, so ttyDisplay->toplin is already set */ + while (n-- > 0) putsyms("\b \b"); +} + +extern char erase_char; /* from xxxtty.c; don't need kill_char */ + +char +tty_yn_function(query,resp, def) +const char *query,*resp; +char def; +/* + * Generic yes/no function. 'def' is the default (returned by space or + * return; 'esc' returns 'q', or 'n', or the default, depending on + * what's in the string. The 'query' string is printed before the user + * is asked about the string. + * If resp is NULL, any single character is accepted and returned. + * If not-NULL, only characters in it are allowed (exceptions: the + * quitchars are always allowed, and if it contains '#' then digits + * are allowed); if it includes an , anything beyond that won't + * be shown in the prompt to the user but will be acceptable as input. + */ +{ + register char q; + char rtmp[40]; + boolean digit_ok, allow_num; + struct WinDesc *cw = wins[WIN_MESSAGE]; + boolean doprev = 0; + char prompt[QBUFSZ]; + + if(ttyDisplay->toplin == 1 && !(cw->flags & WIN_STOP)) more(); + cw->flags &= ~WIN_STOP; + ttyDisplay->toplin = 3; /* special prompt state */ + ttyDisplay->inread++; + if (resp) { + char *rb, respbuf[QBUFSZ]; + + allow_num = (index(resp, '#') != 0); + Strcpy(respbuf, resp); + /* any acceptable responses that follow aren't displayed */ + if ((rb = index(respbuf, '\033')) != 0) *rb = '\0'; + Sprintf(prompt, "%s [%s] ", query, respbuf); + if (def) Sprintf(eos(prompt), "(%c) ", def); + pline("%s", prompt); + } else { + pline("%s ", query); + q = readchar(); + goto clean_up; + } + + do { /* loop until we get valid input */ + q = lowc(readchar()); + if (q == '\020') { /* ctrl-P */ + if (iflags.prevmsg_window != 's') { + int sav = ttyDisplay->inread; + ttyDisplay->inread = 0; + (void) tty_doprev_message(); + ttyDisplay->inread = sav; + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + addtopl(prompt); + } else { + if(!doprev) + (void) tty_doprev_message(); /* need two initially */ + (void) tty_doprev_message(); + doprev = 1; + } + q = '\0'; /* force another loop iteration */ + continue; + } else if (doprev) { + /* BUG[?]: this probably ought to check whether the + character which has just been read is an acceptable + response; if so, skip the reprompt and use it. */ + tty_clear_nhwindow(WIN_MESSAGE); + cw->maxcol = cw->maxrow; + doprev = 0; + addtopl(prompt); + q = '\0'; /* force another loop iteration */ + continue; + } + digit_ok = allow_num && digit(q); + if (q == '\033') { + if (index(resp, 'q')) + q = 'q'; + else if (index(resp, 'n')) + q = 'n'; + else + q = def; + break; + } else if (index(quitchars, q)) { + q = def; + break; + } + if (!index(resp, q) && !digit_ok) { + tty_nhbell(); + q = (char)0; + } else if (q == '#' || digit_ok) { + char z, digit_string[2]; + int n_len = 0; + long value = 0; + addtopl("#"), n_len++; + digit_string[1] = '\0'; + if (q != '#') { + digit_string[0] = q; + addtopl(digit_string), n_len++; + value = q - '0'; + q = '#'; + } + do { /* loop until we get a non-digit */ + z = lowc(readchar()); + if (digit(z)) { + value = (10 * value) + (z - '0'); + if (value < 0) break; /* overflow: try again */ + digit_string[0] = z; + addtopl(digit_string), n_len++; + } else if (z == 'y' || index(quitchars, z)) { + if (z == '\033') value = -1; /* abort */ + z = '\n'; /* break */ + } else if (z == erase_char || z == '\b') { + if (n_len <= 1) { value = -1; break; } + else { value /= 10; removetopl(1), n_len--; } + } else { + value = -1; /* abort */ + tty_nhbell(); + break; + } + } while (z != '\n'); + if (value > 0) yn_number = value; + else if (value == 0) q = 'n'; /* 0 => "no" */ + else { /* remove number from top line, then try again */ + removetopl(n_len), n_len = 0; + q = '\0'; + } + } + } while(!q); + + if (q != '#') { + Sprintf(rtmp, "%c", q); + addtopl(rtmp); + } + clean_up: + ttyDisplay->inread--; + ttyDisplay->toplin = 2; + if (ttyDisplay->intr) ttyDisplay->intr--; + if(wins[WIN_MESSAGE]->cury) + tty_clear_nhwindow(WIN_MESSAGE); + + return q; +} + +#endif /* OVL2 */ + +#endif /* TTY_GRAPHICS */ + +/*topl.c*/ diff --git a/win/tty/wintty.c b/win/tty/wintty.c new file mode 100644 index 0000000..9cba7d7 --- /dev/null +++ b/win/tty/wintty.c @@ -0,0 +1,2612 @@ +/* SCCS Id: @(#)wintty.c 3.4 2002/09/27 */ +/* Copyright (c) David Cohrs, 1991 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * Neither a standard out nor character-based control codes should be + * part of the "tty look" windowing implementation. + * h+ 930227 + */ + +#include "hack.h" +#include "dlb.h" +#ifdef SHORT_FILENAMES +#include "patchlev.h" +#else +#include "patchlevel.h" +#endif + +#ifdef TTY_GRAPHICS + +#ifdef MAC +# define MICRO /* The Mac is a MICRO only for this file, not in general! */ +# ifdef THINK_C +extern void msmsg(const char *,...); +# endif +#endif + + +#ifndef NO_TERMS +#include "tcap.h" +#endif + +#include "wintty.h" + +#ifdef CLIPPING /* might want SIGWINCH */ +# if defined(BSD) || defined(ULTRIX) || defined(AIX_31) || defined(_BULL_SOURCE) +#include +# endif +#endif + +extern char mapped_menu_cmds[]; /* from options.c */ + +/* Interface definition, for windows.c */ +struct window_procs tty_procs = { + "tty", +#ifdef MSDOS + WC_TILED_MAP|WC_ASCII_MAP| +#endif +#if defined(WIN32CON) + WC_MOUSE_SUPPORT| +#endif + WC_COLOR|WC_HILITE_PET|WC_INVERSE|WC_EIGHT_BIT_IN, + 0L, + tty_init_nhwindows, + tty_player_selection, + tty_askname, + tty_get_nh_event, + tty_exit_nhwindows, + tty_suspend_nhwindows, + tty_resume_nhwindows, + tty_create_nhwindow, + tty_clear_nhwindow, + tty_display_nhwindow, + tty_destroy_nhwindow, + tty_curs, + tty_putstr, + tty_display_file, + tty_start_menu, + tty_add_menu, + tty_end_menu, + tty_select_menu, + tty_message_menu, + tty_update_inventory, + tty_mark_synch, + tty_wait_synch, +#ifdef CLIPPING + tty_cliparound, +#endif +#ifdef POSITIONBAR + tty_update_positionbar, +#endif + tty_print_glyph, + tty_raw_print, + tty_raw_print_bold, + tty_nhgetch, + tty_nh_poskey, + tty_nhbell, + tty_doprev_message, + tty_yn_function, + tty_getlin, + tty_get_ext_cmd, + tty_number_pad, + tty_delay_output, +#ifdef CHANGE_COLOR /* the Mac uses a palette device */ + tty_change_color, +#ifdef MAC + tty_change_background, + set_tty_font_name, +#endif + tty_get_color_string, +#endif + + /* other defs that really should go away (they're tty specific) */ + tty_start_screen, + tty_end_screen, + genl_outrip, +#if defined(WIN32CON) + nttty_preference_update, +#else + genl_preference_update, +#endif +}; + +static int maxwin = 0; /* number of windows in use */ +winid BASE_WINDOW; +struct WinDesc *wins[MAXWIN]; +struct DisplayDesc *ttyDisplay; /* the tty display descriptor */ + +extern void FDECL(cmov, (int,int)); /* from termcap.c */ +extern void FDECL(nocmov, (int,int)); /* from termcap.c */ +#if defined(UNIX) || defined(VMS) +static char obuf[BUFSIZ]; /* BUFSIZ is defined in stdio.h */ +#endif + +static char winpanicstr[] = "Bad window id %d"; +char defmorestr[] = "--More--"; + +#ifdef CLIPPING +# if defined(USE_TILES) && defined(MSDOS) +boolean clipping = FALSE; /* clipping on? */ +int clipx = 0, clipxmax = 0; +# else +static boolean clipping = FALSE; /* clipping on? */ +static int clipx = 0, clipxmax = 0; +# endif +static int clipy = 0, clipymax = 0; +#endif /* CLIPPING */ + +#if defined(USE_TILES) && defined(MSDOS) +extern void FDECL(adjust_cursor_flags, (struct WinDesc *)); +#endif + +#if defined(ASCIIGRAPH) && !defined(NO_TERMS) +boolean GFlag = FALSE; +boolean HE_resets_AS; /* see termcap.c */ +#endif + +#if defined(MICRO) || defined(WIN32CON) +static const char to_continue[] = "to continue"; +#define getret() getreturn(to_continue) +#else +STATIC_DCL void NDECL(getret); +#endif +STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *, BOOLEAN_P)); +STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P)); +STATIC_DCL void FDECL(dmore,(struct WinDesc *, const char *)); +STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *)); +STATIC_DCL void FDECL(set_all_on_page, (winid,tty_menu_item *,tty_menu_item *)); +STATIC_DCL void FDECL(unset_all_on_page, (winid,tty_menu_item *,tty_menu_item *)); +STATIC_DCL void FDECL(invert_all_on_page, (winid,tty_menu_item *,tty_menu_item *, CHAR_P)); +STATIC_DCL void FDECL(invert_all, (winid,tty_menu_item *,tty_menu_item *, CHAR_P)); +STATIC_DCL void FDECL(process_menu_window, (winid,struct WinDesc *)); +STATIC_DCL void FDECL(process_text_window, (winid,struct WinDesc *)); +STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *)); +STATIC_DCL const char * FDECL(compress_str, (const char *)); +STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P)); +static char *FDECL(copy_of, (const char *)); +STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */ + +/* + * A string containing all the default commands -- to add to a list + * of acceptable inputs. + */ +static const char default_menu_cmds[] = { + MENU_FIRST_PAGE, + MENU_LAST_PAGE, + MENU_NEXT_PAGE, + MENU_PREVIOUS_PAGE, + MENU_SELECT_ALL, + MENU_UNSELECT_ALL, + MENU_INVERT_ALL, + MENU_SELECT_PAGE, + MENU_UNSELECT_PAGE, + MENU_INVERT_PAGE, + 0 /* null terminator */ +}; + + +/* clean up and quit */ +STATIC_OVL void +bail(mesg) +const char *mesg; +{ + clearlocks(); + tty_exit_nhwindows(mesg); + terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +#if defined(SIGWINCH) && defined(CLIPPING) +STATIC_OVL void +winch() +{ + int oldLI = LI, oldCO = CO, i; + register struct WinDesc *cw; + + getwindowsz(); + if((oldLI != LI || oldCO != CO) && ttyDisplay) { + ttyDisplay->rows = LI; + ttyDisplay->cols = CO; + + cw = wins[BASE_WINDOW]; + cw->rows = ttyDisplay->rows; + cw->cols = ttyDisplay->cols; + + if(iflags.window_inited) { + cw = wins[WIN_MESSAGE]; + cw->curx = cw->cury = 0; + + tty_destroy_nhwindow(WIN_STATUS); + WIN_STATUS = tty_create_nhwindow(NHW_STATUS); + + if(u.ux) { +#ifdef CLIPPING + if(CO < COLNO || LI < ROWNO+3) { + setclipped(); + tty_cliparound(u.ux, u.uy); + } else { + clipping = FALSE; + clipx = clipy = 0; + } +#endif + i = ttyDisplay->toplin; + ttyDisplay->toplin = 0; + docrt(); + bot(); + ttyDisplay->toplin = i; + flush_screen(1); + if(i) { + addtopl(toplines); + } else + for(i=WIN_INVEN; i < MAXWIN; i++) + if(wins[i] && wins[i]->active) { + /* cop-out */ + addtopl("Press Return to continue: "); + break; + } + (void) fflush(stdout); + if(i < 2) flush_screen(1); + } + } + } +} +#endif + +/*ARGSUSED*/ +void +tty_init_nhwindows(argcp,argv) +int* argcp; +char** argv; +{ + int wid, hgt; + + /* + * Remember tty modes, to be restored on exit. + * + * gettty() must be called before tty_startup() + * due to ordering of LI/CO settings + * tty_startup() must be called before initoptions() + * due to ordering of graphics settings + */ +#if defined(UNIX) || defined(VMS) + setbuf(stdout,obuf); +#endif + gettty(); + + /* to port dependant tty setup */ + tty_startup(&wid, &hgt); + setftty(); /* calls start_screen */ + + /* set up tty descriptor */ + ttyDisplay = (struct DisplayDesc*) alloc(sizeof(struct DisplayDesc)); + ttyDisplay->toplin = 0; + ttyDisplay->rows = hgt; + ttyDisplay->cols = wid; + ttyDisplay->curx = ttyDisplay->cury = 0; + ttyDisplay->inmore = ttyDisplay->inread = ttyDisplay->intr = 0; + ttyDisplay->dismiss_more = 0; +#ifdef TEXTCOLOR + ttyDisplay->color = NO_COLOR; +#endif + ttyDisplay->attrs = 0; + + /* set up the default windows */ + BASE_WINDOW = tty_create_nhwindow(NHW_BASE); + wins[BASE_WINDOW]->active = 1; + + ttyDisplay->lastwin = WIN_ERR; + +#if defined(SIGWINCH) && defined(CLIPPING) + (void) signal(SIGWINCH, winch); +#endif + + /* add one a space forward menu command alias */ + add_menu_cmd_alias(' ', MENU_NEXT_PAGE); + + tty_clear_nhwindow(BASE_WINDOW); + + tty_putstr(BASE_WINDOW, 0, ""); + tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_A); + tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_B); + tty_putstr(BASE_WINDOW, 0, COPYRIGHT_BANNER_C); + tty_putstr(BASE_WINDOW, 0, ""); + tty_display_nhwindow(BASE_WINDOW, FALSE); +} + +void +tty_player_selection() +{ + int i, k, n; + char pick4u = 'n', thisch, lastch = 0; + char pbuf[QBUFSZ], plbuf[QBUFSZ]; + winid win; + anything any; + menu_item *selected = 0; + + /* prevent an unnecessary prompt */ + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (!flags.randomall && + (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || + flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) { + int echoline; + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole, + flags.initrace, flags.initgend, flags.initalign); + + tty_putstr(BASE_WINDOW, 0, ""); + echoline = wins[BASE_WINDOW]->cury; + tty_putstr(BASE_WINDOW, 0, prompt); + do { + pick4u = lowc(readchar()); + if (index(quitchars, pick4u)) pick4u = 'y'; + } while(!index(ynqchars, pick4u)); + if ((int)strlen(prompt) + 1 < CO) { + /* Echo choice and move back down line */ + tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u); + tty_putstr(BASE_WINDOW, 0, ""); + } else + /* Otherwise it's hard to tell where to echo, and things are + * wrapping a bit messily anyway, so (try to) make sure the next + * question shows up well and doesn't get wrapped at the + * bottom of the window. + */ + tty_clear_nhwindow(BASE_WINDOW); + + if (pick4u != 'y' && pick4u != 'n') { +give_up: /* Quit */ + if (selected) free((genericptr_t) selected); + bail((char *)0); + /*NOTREACHED*/ + return; + } + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + /* Select a role, if necessary */ + /* we'll try to be compatible with pre-selected race/gender/alignment, + * but may not succeed */ + if (flags.initrole < 0) { + char rolenamebuf[QBUFSZ]; + /* Process the choice */ + if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); + flags.initrole = randrole(); + } + } else { + tty_clear_nhwindow(BASE_WINDOW); + tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + thisch = lowc(roles[i].name.m[0]); + if (thisch == lastch) thisch = highc(thisch); + if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) { + if (flags.initgend == 1 && roles[i].name.f) + Strcpy(rolenamebuf, roles[i].name.f); + else + Strcpy(rolenamebuf, roles[i].name.m); + } else { + if (roles[i].name.f) { + Strcpy(rolenamebuf, roles[i].name.m); + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } else + Strcpy(rolenamebuf, roles[i].name.m); + } + add_menu(win, NO_GLYPH, &any, thisch, + 0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED); + lastch = thisch; + } + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole()+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick a role for your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + tty_clear_nhwindow(BASE_WINDOW); + tty_putstr(BASE_WINDOW, 0, "Choosing Race"); + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, races[i].noun[0], + 0, ATR_NONE, races[i].noun, MENU_UNSELECTED); + } + any.a_int = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrace(flags.initrole)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the race of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initrace = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + tty_clear_nhwindow(BASE_WINDOW); + tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], + 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, + i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + tty_clear_nhwindow(BASE_WINDOW); + tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], + 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initalign = k; + } + } + /* Success! */ + tty_display_nhwindow(BASE_WINDOW, FALSE); +} + +/* + * plname is filled either by an option (-u Player or -uPlayer) or + * explicitly (by being the wizard) or by askname. + * It may still contain a suffix denoting the role, etc. + * Always called after init_nhwindows() and before display_gamewindows(). + */ +void +tty_askname() +{ + static char who_are_you[] = "Who are you? "; + register int c, ct, tryct = 0; + + tty_putstr(BASE_WINDOW, 0, ""); + do { + if (++tryct > 1) { + if (tryct > 10) bail("Giving up after 10 tries.\n"); + tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury - 1); + tty_putstr(BASE_WINDOW, 0, "Enter a name for your character..."); + /* erase previous prompt (in case of ESC after partial response) */ + tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury), cl_end(); + } + tty_putstr(BASE_WINDOW, 0, who_are_you); + tty_curs(BASE_WINDOW, (int)(sizeof who_are_you), + wins[BASE_WINDOW]->cury - 1); + ct = 0; + while((c = tty_nhgetch()) != '\n') { + if(c == EOF) error("End of input\n"); + if (c == '\033') { ct = 0; break; } /* continue outer loop */ +#if defined(WIN32CON) + if (c == '\003') bail("^C abort.\n"); +#endif + /* some people get confused when their erase char is not ^H */ + if (c == '\b' || c == '\177') { + if(ct) { + ct--; +#ifdef WIN32CON + ttyDisplay->curx--; +#endif +#if defined(MICRO) || defined(WIN32CON) +# if defined(WIN32CON) || defined(MSDOS) + backsp(); /* \b is visible on NT */ + (void) putchar(' '); + backsp(); +# else + msmsg("\b \b"); +# endif +#else + (void) putchar('\b'); + (void) putchar(' '); + (void) putchar('\b'); +#endif + } + continue; + } +#if defined(UNIX) || defined(VMS) + if(c != '-' && c != '@') + if(c < 'A' || (c > 'Z' && c < 'a') || c > 'z') c = '_'; +#endif + if (ct < (int)(sizeof plname) - 1) { +#if defined(MICRO) +# if defined(MSDOS) + if (iflags.grmode) { + (void) putchar(c); + } else +# endif + msmsg("%c", c); +#else + (void) putchar(c); +#endif + plname[ct++] = c; +#ifdef WIN32CON + ttyDisplay->curx++; +#endif + } + } + plname[ct] = 0; + } while (ct == 0); + + /* move to next line to simulate echo of user's */ + tty_curs(BASE_WINDOW, 1, wins[BASE_WINDOW]->cury + 1); +} + +void +tty_get_nh_event() +{ + return; +} + +#if !defined(MICRO) && !defined(WIN32CON) +STATIC_OVL void +getret() +{ + xputs("\n"); + if(flags.standout) + standoutbeg(); + xputs("Hit "); + xputs(iflags.cbreak ? "space" : "return"); + xputs(" to continue: "); + if(flags.standout) + standoutend(); + xwaitforspace(" "); +} +#endif + +void +tty_suspend_nhwindows(str) + const char *str; +{ + settty(str); /* calls end_screen, perhaps raw_print */ + if (!str) tty_raw_print(""); /* calls fflush(stdout) */ +} + +void +tty_resume_nhwindows() +{ + gettty(); + setftty(); /* calls start_screen */ + docrt(); +} + +void +tty_exit_nhwindows(str) + const char *str; +{ + winid i; + + tty_suspend_nhwindows(str); + /* Just forget any windows existed, since we're about to exit anyway. + * Disable windows to avoid calls to window routines. + */ + for(i=0; itype = type; + newwin->flags = 0; + newwin->active = FALSE; + newwin->curx = newwin->cury = 0; + newwin->morestr = 0; + newwin->mlist = (tty_menu_item *) 0; + newwin->plist = (tty_menu_item **) 0; + newwin->npages = newwin->plist_size = newwin->nitems = newwin->how = 0; + switch(type) { + case NHW_BASE: + /* base window, used for absolute movement on the screen */ + newwin->offx = newwin->offy = 0; + newwin->rows = ttyDisplay->rows; + newwin->cols = ttyDisplay->cols; + newwin->maxrow = newwin->maxcol = 0; + break; + case NHW_MESSAGE: + /* message window, 1 line long, very wide, top of screen */ + newwin->offx = newwin->offy = 0; + /* sanity check */ + if(iflags.msg_history < 20) iflags.msg_history = 20; + else if(iflags.msg_history > 60) iflags.msg_history = 60; + newwin->maxrow = newwin->rows = iflags.msg_history; + newwin->maxcol = newwin->cols = 0; + break; + case NHW_STATUS: + /* status window, 2 lines long, full width, bottom of screen */ + newwin->offx = 0; +#if defined(USE_TILES) && defined(MSDOS) + if (iflags.grmode) { + newwin->offy = ttyDisplay->rows-2; + } else +#endif + newwin->offy = min((int)ttyDisplay->rows-2, ROWNO+1); + newwin->rows = newwin->maxrow = 2; + newwin->cols = newwin->maxcol = min(ttyDisplay->cols, COLNO); + break; + case NHW_MAP: + /* map window, ROWNO lines long, full width, below message window */ + newwin->offx = 0; + newwin->offy = 1; + newwin->rows = ROWNO; + newwin->cols = COLNO; + newwin->maxrow = 0; /* no buffering done -- let gbuf do it */ + newwin->maxcol = 0; + break; + case NHW_MENU: + case NHW_TEXT: + /* inventory/menu window, variable length, full width, top of screen */ + /* help window, the same, different semantics for display, etc */ + newwin->offx = newwin->offy = 0; + newwin->rows = 0; + newwin->cols = ttyDisplay->cols; + newwin->maxrow = newwin->maxcol = 0; + break; + default: + panic("Tried to create window type %d\n", (int) type); + return WIN_ERR; + } + + for(newid = 0; newidmaxrow) { + newwin->data = + (char **) alloc(sizeof(char *) * (unsigned)newwin->maxrow); + newwin->datlen = + (short *) alloc(sizeof(short) * (unsigned)newwin->maxrow); + if(newwin->maxcol) { + for (i = 0; i < newwin->maxrow; i++) { + newwin->data[i] = (char *) alloc((unsigned)newwin->maxcol); + newwin->datlen[i] = newwin->maxcol; + } + } else { + for (i = 0; i < newwin->maxrow; i++) { + newwin->data[i] = (char *) 0; + newwin->datlen[i] = 0; + } + } + if(newwin->type == NHW_MESSAGE) + newwin->maxrow = 0; + } else { + newwin->data = (char **)0; + newwin->datlen = (short *)0; + } + + return newid; +} + +STATIC_OVL void +erase_menu_or_text(window, cw, clear) + winid window; + struct WinDesc *cw; + boolean clear; +{ + if(cw->offx == 0) + if(cw->offy) { + tty_curs(window, 1, 0); + cl_eos(); + } else if (clear) + clear_screen(); + else + docrt(); + else + docorner((int)cw->offx, cw->maxrow+1); +} + +STATIC_OVL void +free_window_info(cw, free_data) + struct WinDesc *cw; + boolean free_data; +{ + int i; + + if (cw->data) { + if (cw == wins[WIN_MESSAGE] && cw->rows > cw->maxrow) + cw->maxrow = cw->rows; /* topl data */ + for(i=0; imaxrow; i++) + if(cw->data[i]) { + free((genericptr_t)cw->data[i]); + cw->data[i] = (char *)0; + if (cw->datlen) cw->datlen[i] = 0; + } + if (free_data) { + free((genericptr_t)cw->data); + cw->data = (char **)0; + if (cw->datlen) free((genericptr_t)cw->datlen); + cw->datlen = (short *)0; + cw->rows = 0; + } + } + cw->maxrow = cw->maxcol = 0; + if(cw->mlist) { + tty_menu_item *temp; + while ((temp = cw->mlist) != 0) { + cw->mlist = cw->mlist->next; + if (temp->str) free((genericptr_t)temp->str); + free((genericptr_t)temp); + } + } + if (cw->plist) { + free((genericptr_t)cw->plist); + cw->plist = 0; + } + cw->plist_size = cw->npages = cw->nitems = cw->how = 0; + if(cw->morestr) { + free((genericptr_t)cw->morestr); + cw->morestr = 0; + } +} + +void +tty_clear_nhwindow(window) + winid window; +{ + register struct WinDesc *cw = 0; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + ttyDisplay->lastwin = window; + + switch(cw->type) { + case NHW_MESSAGE: + if(ttyDisplay->toplin) { + home(); + cl_end(); + if(cw->cury) + docorner(1, cw->cury+1); + ttyDisplay->toplin = 0; + } + break; + case NHW_STATUS: + tty_curs(window, 1, 0); + cl_end(); + tty_curs(window, 1, 1); + cl_end(); + break; + case NHW_MAP: + /* cheap -- clear the whole thing and tell nethack to redraw botl */ + flags.botlx = 1; + /* fall into ... */ + case NHW_BASE: + clear_screen(); + break; + case NHW_MENU: + case NHW_TEXT: + if(cw->active) + erase_menu_or_text(window, cw, TRUE); + free_window_info(cw, FALSE); + break; + } + cw->curx = cw->cury = 0; +} + +STATIC_OVL void +dmore(cw, s) + register struct WinDesc *cw; + const char *s; /* valid responses */ +{ + const char *prompt = cw->morestr ? cw->morestr : defmorestr; + int offset = (cw->type == NHW_TEXT) ? 1 : 2; + + tty_curs(BASE_WINDOW, + (int)ttyDisplay->curx + offset, (int)ttyDisplay->cury); + if(flags.standout) + standoutbeg(); + xputs(prompt); + ttyDisplay->curx += strlen(prompt); + if(flags.standout) + standoutend(); + + xwaitforspace(s); +} + +STATIC_OVL void +set_item_state(window, lineno, item) + winid window; + int lineno; + tty_menu_item *item; +{ + char ch = item->selected ? (item->count == -1L ? '+' : '#') : '-'; + tty_curs(window, 4, lineno); + term_start_attr(item->attr); + (void) putchar(ch); + ttyDisplay->curx++; + term_end_attr(item->attr); +} + +STATIC_OVL void +set_all_on_page(window, page_start, page_end) + winid window; + tty_menu_item *page_start, *page_end; +{ + tty_menu_item *curr; + int n; + + for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) + if (curr->identifier.a_void && !curr->selected) { + curr->selected = TRUE; + set_item_state(window, n, curr); + } +} + +STATIC_OVL void +unset_all_on_page(window, page_start, page_end) + winid window; + tty_menu_item *page_start, *page_end; +{ + tty_menu_item *curr; + int n; + + for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) + if (curr->identifier.a_void && curr->selected) { + curr->selected = FALSE; + curr->count = -1L; + set_item_state(window, n, curr); + } +} + +STATIC_OVL void +invert_all_on_page(window, page_start, page_end, acc) + winid window; + tty_menu_item *page_start, *page_end; + char acc; /* group accelerator, 0 => all */ +{ + tty_menu_item *curr; + int n; + + for (n = 0, curr = page_start; curr != page_end; n++, curr = curr->next) + if (curr->identifier.a_void && (acc == 0 || curr->gselector == acc)) { + if (curr->selected) { + curr->selected = FALSE; + curr->count = -1L; + } else + curr->selected = TRUE; + set_item_state(window, n, curr); + } +} + +/* + * Invert all entries that match the give group accelerator (or all if + * zero). + */ +STATIC_OVL void +invert_all(window, page_start, page_end, acc) + winid window; + tty_menu_item *page_start, *page_end; + char acc; /* group accelerator, 0 => all */ +{ + tty_menu_item *curr; + boolean on_curr_page; + struct WinDesc *cw = wins[window]; + + invert_all_on_page(window, page_start, page_end, acc); + + /* invert the rest */ + for (on_curr_page = FALSE, curr = cw->mlist; curr; curr = curr->next) { + if (curr == page_start) + on_curr_page = TRUE; + else if (curr == page_end) + on_curr_page = FALSE; + + if (!on_curr_page && curr->identifier.a_void + && (acc == 0 || curr->gselector == acc)) { + if (curr->selected) { + curr->selected = FALSE; + curr->count = -1; + } else + curr->selected = TRUE; + } + } +} + +STATIC_OVL void +process_menu_window(window, cw) +winid window; +struct WinDesc *cw; +{ + tty_menu_item *page_start, *page_end, *curr; + long count; + int n, curr_page, page_lines; + boolean finished, counting, reset_count; + char *cp, *rp, resp[QBUFSZ], gacc[QBUFSZ], + *msave, *morestr; + + curr_page = page_lines = 0; + page_start = page_end = 0; + msave = cw->morestr; /* save the morestr */ + cw->morestr = morestr = (char*) alloc((unsigned) QBUFSZ); + counting = FALSE; + count = 0L; + reset_count = TRUE; + finished = FALSE; + + /* collect group accelerators; for PICK_NONE, they're ignored; + for PICK_ONE, only those which match exactly one entry will be + accepted; for PICK_ANY, those which match any entry are okay */ + gacc[0] = '\0'; + if (cw->how != PICK_NONE) { + int i, gcnt[128]; +#define GSELIDX(c) (c & 127) /* guard against `signed char' */ + + for (i = 0; i < SIZE(gcnt); i++) gcnt[i] = 0; + for (n = 0, curr = cw->mlist; curr; curr = curr->next) + if (curr->gselector && curr->gselector != curr->selector) { + ++n; + ++gcnt[GSELIDX(curr->gselector)]; + } + + if (n > 0) /* at least one group accelerator found */ + for (rp = gacc, curr = cw->mlist; curr; curr = curr->next) + if (curr->gselector && !index(gacc, curr->gselector) && + (cw->how == PICK_ANY || + gcnt[GSELIDX(curr->gselector)] == 1)) { + *rp++ = curr->gselector; + *rp = '\0'; /* re-terminate for index() */ + } + } + + /* loop until finished */ + while (!finished) { + if (reset_count) { + counting = FALSE; + count = 0; + } else + reset_count = TRUE; + + if (!page_start) { + /* new page to be displayed */ + if (curr_page < 0 || (cw->npages > 0 && curr_page >= cw->npages)) + panic("bad menu screen page #%d", curr_page); + + /* clear screen */ + if (!cw->offx) { /* if not corner, do clearscreen */ + if(cw->offy) { + tty_curs(window, 1, 0); + cl_eos(); + } else + clear_screen(); + } + + rp = resp; + if (cw->npages > 0) { + /* collect accelerators */ + page_start = cw->plist[curr_page]; + page_end = cw->plist[curr_page + 1]; + for (page_lines = 0, curr = page_start; + curr != page_end; + page_lines++, curr = curr->next) { + if (curr->selector) + *rp++ = curr->selector; + + tty_curs(window, 1, page_lines); + if (cw->offx) cl_end(); + + (void) putchar(' '); + ++ttyDisplay->curx; + /* + * Don't use xputs() because (1) under unix it calls + * tputstr() which will interpret a '*' as some kind + * of padding information and (2) it calls xputc to + * actually output the character. We're faster doing + * this. + */ + term_start_attr(curr->attr); + for (n = 0, cp = curr->str; +#ifndef WIN32CON + *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols; + cp++, n++) +#else + *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols; + cp++, n++, ttyDisplay->curx++) +#endif + if (n == 2 && curr->identifier.a_void != 0 && + curr->selected) { + if (curr->count == -1L) + (void) putchar('+'); /* all selected */ + else + (void) putchar('#'); /* count selected */ + } else + (void) putchar(*cp); + term_end_attr(curr->attr); + } + } else { + page_start = 0; + page_end = 0; + page_lines = 0; + } + *rp = 0; + + /* corner window - clear extra lines from last page */ + if (cw->offx) { + for (n = page_lines + 1; n < cw->maxrow; n++) { + tty_curs(window, 1, n); + cl_end(); + } + } + + /* set extra chars.. */ + Strcat(resp, default_menu_cmds); + Strcat(resp, "0123456789\033\n\r"); /* counts, quit */ + Strcat(resp, gacc); /* group accelerators */ + Strcat(resp, mapped_menu_cmds); + + if (cw->npages > 1) + Sprintf(cw->morestr, "(%d of %d)", + curr_page + 1, (int) cw->npages); + else if (msave) + Strcpy(cw->morestr, msave); + else + Strcpy(cw->morestr, defmorestr); + + tty_curs(window, 1, page_lines); + cl_end(); + dmore(cw, resp); + } else { + /* just put the cursor back... */ + tty_curs(window, (int) strlen(cw->morestr) + 2, page_lines); + xwaitforspace(resp); + } + + morc = map_menu_cmd(morc); + switch (morc) { + case '0': + /* special case: '0' is also the default ball class */ + if (!counting && index(gacc, morc)) goto group_accel; + /* fall through to count the zero */ + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + count = (count * 10L) + (long) (morc - '0'); + /* + * It is debatable whether we should allow 0 to + * start a count. There is no difference if the + * item is selected. If not selected, then + * "0b" could mean: + * + * count starting zero: "zero b's" + * ignore starting zero: "select b" + * + * At present I don't know which is better. + */ + if (count != 0L) { /* ignore leading zeros */ + counting = TRUE; + reset_count = FALSE; + } + break; + case '\033': /* cancel - from counting or loop */ + if (!counting) { + /* deselect everything */ + for (curr = cw->mlist; curr; curr = curr->next) { + curr->selected = FALSE; + curr->count = -1L; + } + cw->flags |= WIN_CANCELLED; + finished = TRUE; + } + /* else only stop count */ + break; + case '\0': /* finished (commit) */ + case '\n': + case '\r': + /* only finished if we are actually picking something */ + if (cw->how != PICK_NONE) { + finished = TRUE; + break; + } + /* else fall through */ + case MENU_NEXT_PAGE: + if (cw->npages > 0 && curr_page != cw->npages - 1) { + curr_page++; + page_start = 0; + } else + finished = TRUE; /* questionable behavior */ + break; + case MENU_PREVIOUS_PAGE: + if (cw->npages > 0 && curr_page != 0) { + --curr_page; + page_start = 0; + } + break; + case MENU_FIRST_PAGE: + if (cw->npages > 0 && curr_page != 0) { + page_start = 0; + curr_page = 0; + } + break; + case MENU_LAST_PAGE: + if (cw->npages > 0 && curr_page != cw->npages - 1) { + page_start = 0; + curr_page = cw->npages - 1; + } + break; + case MENU_SELECT_PAGE: + if (cw->how == PICK_ANY) + set_all_on_page(window, page_start, page_end); + break; + case MENU_UNSELECT_PAGE: + unset_all_on_page(window, page_start, page_end); + break; + case MENU_INVERT_PAGE: + if (cw->how == PICK_ANY) + invert_all_on_page(window, page_start, page_end, 0); + break; + case MENU_SELECT_ALL: + if (cw->how == PICK_ANY) { + set_all_on_page(window, page_start, page_end); + /* set the rest */ + for (curr = cw->mlist; curr; curr = curr->next) + if (curr->identifier.a_void && !curr->selected) + curr->selected = TRUE; + } + break; + case MENU_UNSELECT_ALL: + unset_all_on_page(window, page_start, page_end); + /* unset the rest */ + for (curr = cw->mlist; curr; curr = curr->next) + if (curr->identifier.a_void && curr->selected) { + curr->selected = FALSE; + curr->count = -1; + } + break; + case MENU_INVERT_ALL: + if (cw->how == PICK_ANY) + invert_all(window, page_start, page_end, 0); + break; + default: + if (cw->how == PICK_NONE || !index(resp, morc)) { + /* unacceptable input received */ + tty_nhbell(); + break; + } else if (index(gacc, morc)) { + group_accel: + /* group accelerator; for the PICK_ONE case, we know that + it matches exactly one item in order to be in gacc[] */ + invert_all(window, page_start, page_end, morc); + if (cw->how == PICK_ONE) finished = TRUE; + break; + } + /* find, toggle, and possibly update */ + for (n = 0, curr = page_start; + curr != page_end; + n++, curr = curr->next) + if (morc == curr->selector) { + if (curr->selected) { + if (counting && count > 0) { + curr->count = count; + set_item_state(window, n, curr); + } else { /* change state */ + curr->selected = FALSE; + curr->count = -1L; + set_item_state(window, n, curr); + } + } else { /* !selected */ + if (counting && count > 0) { + curr->count = count; + curr->selected = TRUE; + set_item_state(window, n, curr); + } else if (!counting) { + curr->selected = TRUE; + set_item_state(window, n, curr); + } + /* do nothing counting&&count==0 */ + } + + if (cw->how == PICK_ONE) finished = TRUE; + break; /* from `for' loop */ + } + break; + } + + } /* while */ + cw->morestr = msave; + free((genericptr_t)morestr); +} + +STATIC_OVL void +process_text_window(window, cw) +winid window; +struct WinDesc *cw; +{ + int i, n, attr; + register char *cp; + + for (n = 0, i = 0; i < cw->maxrow; i++) { + if (!cw->offx && (n + cw->offy == ttyDisplay->rows - 1)) { + tty_curs(window, 1, n); + cl_end(); + dmore(cw, quitchars); + if (morc == '\033') { + cw->flags |= WIN_CANCELLED; + break; + } + if (cw->offy) { + tty_curs(window, 1, 0); + cl_eos(); + } else + clear_screen(); + n = 0; + } + tty_curs(window, 1, n++); + if (cw->offx) cl_end(); + if (cw->data[i]) { + attr = cw->data[i][0] - 1; + if (cw->offx) { + (void) putchar(' '); ++ttyDisplay->curx; + } + term_start_attr(attr); + for (cp = &cw->data[i][1]; +#ifndef WIN32CON + *cp && (int) ++ttyDisplay->curx < (int) ttyDisplay->cols; + cp++) +#else + *cp && (int) ttyDisplay->curx < (int) ttyDisplay->cols; + cp++, ttyDisplay->curx++) +#endif + (void) putchar(*cp); + term_end_attr(attr); + } + } + if (i == cw->maxrow) { + tty_curs(BASE_WINDOW, (int)cw->offx + 1, + (cw->type == NHW_TEXT) ? (int) ttyDisplay->rows - 1 : n); + cl_end(); + dmore(cw, quitchars); + if (morc == '\033') + cw->flags |= WIN_CANCELLED; + } +} + +/*ARGSUSED*/ +void +tty_display_nhwindow(window, blocking) + winid window; + boolean blocking; /* with ttys, all windows are blocking */ +{ + register struct WinDesc *cw = 0; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + if(cw->flags & WIN_CANCELLED) + return; + ttyDisplay->lastwin = window; + ttyDisplay->rawprint = 0; + + switch(cw->type) { + case NHW_MESSAGE: + if(ttyDisplay->toplin == 1) { + more(); + ttyDisplay->toplin = 1; /* more resets this */ + tty_clear_nhwindow(window); + } else + ttyDisplay->toplin = 0; + cw->curx = cw->cury = 0; + if(!cw->active) + iflags.window_inited = TRUE; + break; + case NHW_MAP: + end_glyphout(); + if(blocking) { + if(!ttyDisplay->toplin) ttyDisplay->toplin = 1; + tty_display_nhwindow(WIN_MESSAGE, TRUE); + return; + } + case NHW_BASE: + (void) fflush(stdout); + break; + case NHW_TEXT: + cw->maxcol = ttyDisplay->cols; /* force full-screen mode */ + /*FALLTHRU*/ + case NHW_MENU: + cw->active = 1; + /* avoid converting to uchar before calculations are finished */ + cw->offx = (uchar) (int) + max((int) 10, (int) (ttyDisplay->cols - cw->maxcol - 1)); + if(cw->type == NHW_MENU) + cw->offy = 0; + if(ttyDisplay->toplin == 1) + tty_display_nhwindow(WIN_MESSAGE, TRUE); + if(cw->offx == 10 || cw->maxrow >= (int) ttyDisplay->rows) { + cw->offx = 0; + if(cw->offy) { + tty_curs(window, 1, 0); + cl_eos(); + } else + clear_screen(); + ttyDisplay->toplin = 0; + } else + tty_clear_nhwindow(WIN_MESSAGE); + + if (cw->data || !cw->maxrow) + process_text_window(window, cw); + else + process_menu_window(window, cw); + break; + } + cw->active = 1; +} + +void +tty_dismiss_nhwindow(window) + winid window; +{ + register struct WinDesc *cw = 0; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + + switch(cw->type) { + case NHW_MESSAGE: + if (ttyDisplay->toplin) + tty_display_nhwindow(WIN_MESSAGE, TRUE); + /*FALLTHRU*/ + case NHW_STATUS: + case NHW_BASE: + case NHW_MAP: + /* + * these should only get dismissed when the game is going away + * or suspending + */ + tty_curs(BASE_WINDOW, 1, (int)ttyDisplay->rows-1); + cw->active = 0; + break; + case NHW_MENU: + case NHW_TEXT: + if(cw->active) { + if (iflags.window_inited) { + /* otherwise dismissing the text endwin after other windows + * are dismissed tries to redraw the map and panics. since + * the whole reason for dismissing the other windows was to + * leave the ending window on the screen, we don't want to + * erase it anyway. + */ + erase_menu_or_text(window, cw, FALSE); + } + cw->active = 0; + } + break; + } + cw->flags = 0; +} + +void +tty_destroy_nhwindow(window) + winid window; +{ + register struct WinDesc *cw = 0; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + + if(cw->active) + tty_dismiss_nhwindow(window); + if(cw->type == NHW_MESSAGE) + iflags.window_inited = 0; + if(cw->type == NHW_MAP) + clear_screen(); + + free_window_info(cw, TRUE); + free((genericptr_t)cw); + wins[window] = 0; +} + +void +tty_curs(window, x, y) +winid window; +register int x, y; /* not xchar: perhaps xchar is unsigned and + curx-x would be unsigned as well */ +{ + struct WinDesc *cw = 0; + int cx = ttyDisplay->curx; + int cy = ttyDisplay->cury; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + ttyDisplay->lastwin = window; + +#if defined(USE_TILES) && defined(MSDOS) + adjust_cursor_flags(cw); +#endif + cw->curx = --x; /* column 0 is never used */ + cw->cury = y; +#ifdef DEBUG + if(x<0 || y<0 || y >= cw->rows || x > cw->cols) { + const char *s = "[unknown type]"; + switch(cw->type) { + case NHW_MESSAGE: s = "[topl window]"; break; + case NHW_STATUS: s = "[status window]"; break; + case NHW_MAP: s = "[map window]"; break; + case NHW_MENU: s = "[corner window]"; break; + case NHW_TEXT: s = "[text window]"; break; + case NHW_BASE: s = "[base window]"; break; + } + impossible("bad curs positioning win %d %s (%d,%d)", window, s, x, y); + return; + } +#endif + x += cw->offx; + y += cw->offy; + +#ifdef CLIPPING + if(clipping && window == WIN_MAP) { + x -= clipx; + y -= clipy; + } +#endif + + if (y == cy && x == cx) + return; + + if(cw->type == NHW_MAP) + end_glyphout(); + +#ifndef NO_TERMS + if(!nh_ND && (cx != x || x <= 3)) { /* Extremely primitive */ + cmov(x, y); /* bunker!wtm */ + return; + } +#endif + + if((cy -= y) < 0) cy = -cy; + if((cx -= x) < 0) cx = -cx; + if(cy <= 3 && cx <= 3) { + nocmov(x, y); +#ifndef NO_TERMS + } else if ((x <= 3 && cy <= 3) || (!nh_CM && x < cx)) { + (void) putchar('\r'); + ttyDisplay->curx = 0; + nocmov(x, y); + } else if (!nh_CM) { + nocmov(x, y); +#endif + } else + cmov(x, y); + + ttyDisplay->curx = x; + ttyDisplay->cury = y; +} + +STATIC_OVL void +tty_putsym(window, x, y, ch) + winid window; + int x, y; + char ch; +{ + register struct WinDesc *cw = 0; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) + panic(winpanicstr, window); + + switch(cw->type) { + case NHW_STATUS: + case NHW_MAP: + case NHW_BASE: + tty_curs(window, x, y); + (void) putchar(ch); + ttyDisplay->curx++; + cw->curx++; + break; + case NHW_MESSAGE: + case NHW_MENU: + case NHW_TEXT: + impossible("Can't putsym to window type %d", cw->type); + break; + } +} + + +STATIC_OVL const char* +compress_str(str) +const char *str; +{ + static char cbuf[BUFSZ]; + /* compress in case line too long */ + if((int)strlen(str) >= CO) { + register const char *bp0 = str; + register char *bp1 = cbuf; + + do { +#ifdef CLIPPING + if(*bp0 != ' ' || bp0[1] != ' ') +#else + if(*bp0 != ' ' || bp0[1] != ' ' || bp0[2] != ' ') +#endif + *bp1++ = *bp0; + } while(*bp0++); + } else + return str; + return cbuf; +} + +void +tty_putstr(window, attr, str) + winid window; + int attr; + const char *str; +{ + register struct WinDesc *cw = 0; + register char *ob; + register const char *nb; + register int i, j, n0; + + /* Assume there's a real problem if the window is missing -- + * probably a panic message + */ + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0) { + tty_raw_print(str); + return; + } + + if(str == (const char*)0 || + ((cw->flags & WIN_CANCELLED) && (cw->type != NHW_MESSAGE))) + return; + if(cw->type != NHW_MESSAGE) + str = compress_str(str); + + ttyDisplay->lastwin = window; + + switch(cw->type) { + case NHW_MESSAGE: + /* really do this later */ +#if defined(USER_SOUNDS) && defined(WIN32CON) + play_sound_for_message(str); +#endif + update_topl(str); + break; + + case NHW_STATUS: + ob = &cw->data[cw->cury][j = cw->curx]; + if(flags.botlx) *ob = 0; + if(!cw->cury && (int)strlen(str) >= CO) { + /* the characters before "St:" are unnecessary */ + nb = index(str, ':'); + if(nb && nb > str+2) + str = nb - 2; + } + nb = str; + for(i = cw->curx+1, n0 = cw->cols; i < n0; i++, nb++) { + if(!*nb) { + if(*ob || flags.botlx) { + /* last char printed may be in middle of line */ + tty_curs(WIN_STATUS, i, cw->cury); + cl_end(); + } + break; + } + if(*ob != *nb) + tty_putsym(WIN_STATUS, i, cw->cury, *nb); + if(*ob) ob++; + } + + (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1); + cw->data[cw->cury][cw->cols-1] = '\0'; /* null terminate */ + cw->cury = (cw->cury+1) % 2; + cw->curx = 0; + break; + case NHW_MAP: + tty_curs(window, cw->curx+1, cw->cury); + term_start_attr(attr); + while(*str && (int) ttyDisplay->curx < (int) ttyDisplay->cols-1) { + (void) putchar(*str); + str++; + ttyDisplay->curx++; + } + cw->curx = 0; + cw->cury++; + term_end_attr(attr); + break; + case NHW_BASE: + tty_curs(window, cw->curx+1, cw->cury); + term_start_attr(attr); + while (*str) { + if ((int) ttyDisplay->curx >= (int) ttyDisplay->cols-1) { + cw->curx = 0; + cw->cury++; + tty_curs(window, cw->curx+1, cw->cury); + } + (void) putchar(*str); + str++; + ttyDisplay->curx++; + } + cw->curx = 0; + cw->cury++; + term_end_attr(attr); + break; + case NHW_MENU: + case NHW_TEXT: + if(cw->type == NHW_TEXT && cw->cury == ttyDisplay->rows-1) { + /* not a menu, so save memory and output 1 page at a time */ + cw->maxcol = ttyDisplay->cols; /* force full-screen mode */ + tty_display_nhwindow(window, TRUE); + for(i=0; imaxrow; i++) + if(cw->data[i]){ + free((genericptr_t)cw->data[i]); + cw->data[i] = 0; + } + cw->maxrow = cw->cury = 0; + } + /* always grows one at a time, but alloc 12 at a time */ + if(cw->cury >= cw->rows) { + char **tmp; + + cw->rows += 12; + tmp = (char **) alloc(sizeof(char *) * (unsigned)cw->rows); + for(i=0; imaxrow; i++) + tmp[i] = cw->data[i]; + if(cw->data) + free((genericptr_t)cw->data); + cw->data = tmp; + + for(i=cw->maxrow; irows; i++) + cw->data[i] = 0; + } + if(cw->data[cw->cury]) + free((genericptr_t)cw->data[cw->cury]); + n0 = strlen(str) + 1; + ob = cw->data[cw->cury] = (char *)alloc((unsigned)n0 + 1); + *ob++ = (char)(attr + 1); /* avoid nuls, for convenience */ + Strcpy(ob, str); + + if(n0 > cw->maxcol) + cw->maxcol = n0; + if(++cw->cury > cw->maxrow) + cw->maxrow = cw->cury; + if(n0 > CO) { + /* attempt to break the line */ + for(i = CO-1; i && str[i] != ' ' && str[i] != '\n';) + i--; + if(i) { + cw->data[cw->cury-1][++i] = '\0'; + tty_putstr(window, attr, &str[i]); + } + + } + break; + } +} + +void +tty_display_file(fname, complain) +const char *fname; +boolean complain; +{ +#ifdef DEF_PAGER /* this implies that UNIX is defined */ + { + /* use external pager; this may give security problems */ + register int fd = open(fname, 0); + + if(fd < 0) { + if(complain) pline("Cannot open %s.", fname); + else docrt(); + return; + } + if(child(1)) { + /* Now that child() does a setuid(getuid()) and a chdir(), + we may not be able to open file fname anymore, so make + it stdin. */ + (void) close(0); + if(dup(fd)) { + if(complain) raw_printf("Cannot open %s as stdin.", fname); + } else { + (void) execlp(catmore, "page", (char *)0); + if(complain) raw_printf("Cannot exec %s.", catmore); + } + if(complain) sleep(10); /* want to wait_synch() but stdin is gone */ + terminate(EXIT_FAILURE); + } + (void) close(fd); + } +#else /* DEF_PAGER */ + { + dlb *f; + char buf[BUFSZ]; + char *cr; + + tty_clear_nhwindow(WIN_MESSAGE); + f = dlb_fopen(fname, "r"); + if (!f) { + if(complain) { + home(); tty_mark_synch(); tty_raw_print(""); + perror(fname); tty_wait_synch(); + pline("Cannot open \"%s\".", fname); + } else if(u.ux) docrt(); + } else { + winid datawin = tty_create_nhwindow(NHW_TEXT); + boolean empty = TRUE; + + if(complain +#ifndef NO_TERMS + && nh_CD +#endif + ) { + /* attempt to scroll text below map window if there's room */ + wins[datawin]->offy = wins[WIN_STATUS]->offy+3; + if((int) wins[datawin]->offy + 12 > (int) ttyDisplay->rows) + wins[datawin]->offy = 0; + } + while (dlb_fgets(buf, BUFSZ, f)) { + if ((cr = index(buf, '\n')) != 0) *cr = 0; +#ifdef MSDOS + if ((cr = index(buf, '\r')) != 0) *cr = 0; +#endif + if (index(buf, '\t') != 0) (void) tabexpand(buf); + empty = FALSE; + tty_putstr(datawin, 0, buf); + if(wins[datawin]->flags & WIN_CANCELLED) + break; + } + if (!empty) tty_display_nhwindow(datawin, FALSE); + tty_destroy_nhwindow(datawin); + (void) dlb_fclose(f); + } + } +#endif /* DEF_PAGER */ +} + +void +tty_start_menu(window) + winid window; +{ + tty_clear_nhwindow(window); + return; +} + +/*ARGSUSED*/ +/* + * Add a menu item to the beginning of the menu list. This list is reversed + * later. + */ +void +tty_add_menu(window, glyph, identifier, ch, gch, attr, str, preselected) + winid window; /* window to use, must be of type NHW_MENU */ + int glyph; /* glyph to display with item (unused) */ + const anything *identifier; /* what to return if selected */ + char ch; /* keyboard accelerator (0 = pick our own) */ + char gch; /* group accelerator (0 = no group) */ + int attr; /* attribute for string (like tty_putstr()) */ + const char *str; /* menu string */ + boolean preselected; /* item is marked as selected */ +{ + register struct WinDesc *cw = 0; + tty_menu_item *item; + const char *newstr; + char buf[4+BUFSZ]; + + if (str == (const char*) 0) + return; + + if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 + || cw->type != NHW_MENU) + panic(winpanicstr, window); + + cw->nitems++; + if (identifier->a_void) { + int len = strlen(str); + if (len >= BUFSZ) { + /* We *think* everything's coming in off at most BUFSZ bufs... */ + impossible("Menu item too long (%d).", len); + len = BUFSZ - 1; + } + Sprintf(buf, "%c - ", ch ? ch : '?'); + (void) strncpy(buf+4, str, len); + buf[4+len] = '\0'; + newstr = buf; + } else + newstr = str; + + item = (tty_menu_item *) alloc(sizeof(tty_menu_item)); + item->identifier = *identifier; + item->count = -1L; + item->selected = preselected; + item->selector = ch; + item->gselector = gch; + item->attr = attr; + item->str = copy_of(newstr); + + item->next = cw->mlist; + cw->mlist = item; +} + +/* Invert the given list, can handle NULL as an input. */ +STATIC_OVL tty_menu_item * +reverse(curr) + tty_menu_item *curr; +{ + tty_menu_item *next, *head = 0; + + while (curr) { + next = curr->next; + curr->next = head; + head = curr; + curr = next; + } + return head; +} + +/* + * End a menu in this window, window must a type NHW_MENU. This routine + * processes the string list. We calculate the # of pages, then assign + * keyboard accelerators as needed. Finally we decide on the width and + * height of the window. + */ +void +tty_end_menu(window, prompt) + winid window; /* menu to use */ + const char *prompt; /* prompt to for menu */ +{ + struct WinDesc *cw = 0; + tty_menu_item *curr; + short len; + int lmax, n; + char menu_ch; + + if (window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 || + cw->type != NHW_MENU) + panic(winpanicstr, window); + + /* Reverse the list so that items are in correct order. */ + cw->mlist = reverse(cw->mlist); + + /* Put the promt at the beginning of the menu. */ + if (prompt) { + anything any; + + any.a_void = 0; /* not selectable */ + tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + tty_add_menu(window, NO_GLYPH, &any, 0, 0, ATR_NONE, prompt, MENU_UNSELECTED); + } + + lmax = min(52, (int)ttyDisplay->rows - 1); /* # lines per page */ + cw->npages = (cw->nitems + (lmax - 1)) / lmax; /* # of pages */ + + /* make sure page list is large enough */ + if (cw->plist_size < cw->npages+1 /*need 1 slot beyond last*/) { + if (cw->plist) free((genericptr_t)cw->plist); + cw->plist_size = cw->npages + 1; + cw->plist = (tty_menu_item **) + alloc(cw->plist_size * sizeof(tty_menu_item *)); + } + + cw->cols = 0; /* cols is set when the win is initialized... (why?) */ + menu_ch = '?'; /* lint suppression */ + for (n = 0, curr = cw->mlist; curr; n++, curr = curr->next) { + /* set page boundaries and character accelerators */ + if ((n % lmax) == 0) { + menu_ch = 'a'; + cw->plist[n/lmax] = curr; + } + if (curr->identifier.a_void && !curr->selector) { + curr->str[0] = curr->selector = menu_ch; + if (menu_ch++ == 'z') menu_ch = 'A'; + } + + /* cut off any lines that are too long */ + len = strlen(curr->str) + 2; /* extra space at beg & end */ + if (len > (int)ttyDisplay->cols) { + curr->str[ttyDisplay->cols-2] = 0; + len = ttyDisplay->cols; + } + if (len > cw->cols) cw->cols = len; + } + cw->plist[cw->npages] = 0; /* plist terminator */ + + /* + * If greater than 1 page, morestr is "(x of y) " otherwise, "(end) " + */ + if (cw->npages > 1) { + char buf[QBUFSZ]; + /* produce the largest demo string */ + Sprintf(buf, "(%d of %d) ", cw->npages, cw->npages); + len = strlen(buf); + cw->morestr = copy_of(""); + } else { + cw->morestr = copy_of("(end) "); + len = strlen(cw->morestr); + } + + if (len > (int)ttyDisplay->cols) { + /* truncate the prompt if its too long for the screen */ + if (cw->npages <= 1) /* only str in single page case */ + cw->morestr[ttyDisplay->cols] = 0; + len = ttyDisplay->cols; + } + if (len > cw->cols) cw->cols = len; + + cw->maxcol = cw->cols; + + /* + * The number of lines in the first page plus the morestr will be the + * maximum size of the window. + */ + if (cw->npages > 1) + cw->maxrow = cw->rows = lmax + 1; + else + cw->maxrow = cw->rows = cw->nitems + 1; +} + +int +tty_select_menu(window, how, menu_list) + winid window; + int how; + menu_item **menu_list; +{ + register struct WinDesc *cw = 0; + tty_menu_item *curr; + menu_item *mi; + int n, cancelled; + + if(window == WIN_ERR || (cw = wins[window]) == (struct WinDesc *) 0 + || cw->type != NHW_MENU) + panic(winpanicstr, window); + + *menu_list = (menu_item *) 0; + cw->how = (short) how; + morc = 0; + tty_display_nhwindow(window, TRUE); + cancelled = !!(cw->flags & WIN_CANCELLED); + tty_dismiss_nhwindow(window); /* does not destroy window data */ + + if (cancelled) { + n = -1; + } else { + for (n = 0, curr = cw->mlist; curr; curr = curr->next) + if (curr->selected) n++; + } + + if (n > 0) { + *menu_list = (menu_item *) alloc(n * sizeof(menu_item)); + for (mi = *menu_list, curr = cw->mlist; curr; curr = curr->next) + if (curr->selected) { + mi->item = curr->identifier; + mi->count = curr->count; + mi++; + } + } + + return n; +} + +/* special hack for treating top line --More-- as a one item menu */ +char +tty_message_menu(let, how, mesg) +char let; +int how; +const char *mesg; +{ + /* "menu" without selection; use ordinary pline, no more() */ + if (how == PICK_NONE) { + pline("%s", mesg); + return 0; + } + + ttyDisplay->dismiss_more = let; + morc = 0; + /* barebones pline(); since we're only supposed to be called after + response to a prompt, we'll assume that the display is up to date */ + tty_putstr(WIN_MESSAGE, 0, mesg); + /* if `mesg' didn't wrap (triggering --More--), force --More-- now */ + if (ttyDisplay->toplin == 1) { + more(); + ttyDisplay->toplin = 1; /* more resets this */ + tty_clear_nhwindow(WIN_MESSAGE); + } + /* normally means skip further messages, but in this case + it means cancel the current prompt; any other messages should + continue to be output normally */ + wins[WIN_MESSAGE]->flags &= ~WIN_CANCELLED; + ttyDisplay->dismiss_more = 0; + + return ((how == PICK_ONE && morc == let) || morc == '\033') ? morc : '\0'; +} + +void +tty_update_inventory() +{ + return; +} + +void +tty_mark_synch() +{ + (void) fflush(stdout); +} + +void +tty_wait_synch() +{ + /* we just need to make sure all windows are synch'd */ + if(!ttyDisplay || ttyDisplay->rawprint) { + getret(); + if(ttyDisplay) ttyDisplay->rawprint = 0; + } else { + tty_display_nhwindow(WIN_MAP, FALSE); + if(ttyDisplay->inmore) { + addtopl("--More--"); + (void) fflush(stdout); + } else if(ttyDisplay->inread > program_state.gameover) { + /* this can only happen if we were reading and got interrupted */ + ttyDisplay->toplin = 3; + /* do this twice; 1st time gets the Quit? message again */ + (void) tty_doprev_message(); + (void) tty_doprev_message(); + ttyDisplay->intr++; + (void) fflush(stdout); + } + } +} + +void +docorner(xmin, ymax) + register int xmin, ymax; +{ + register int y; + register struct WinDesc *cw = wins[WIN_MAP]; + + if (u.uswallow) { /* Can be done more efficiently */ + swallowed(1); + return; + } + +#if defined(SIGWINCH) && defined(CLIPPING) + if(ymax > LI) ymax = LI; /* can happen if window gets smaller */ +#endif + for (y = 0; y < ymax; y++) { + tty_curs(BASE_WINDOW, xmin,y); /* move cursor */ + cl_end(); /* clear to end of line */ +#ifdef CLIPPING + if (y<(int) cw->offy || y+clipy > ROWNO) + continue; /* only refresh board */ +#if defined(USE_TILES) && defined(MSDOS) + if (iflags.tile_view) + row_refresh((xmin/2)+clipx-((int)cw->offx/2),COLNO-1,y+clipy-(int)cw->offy); + else +#endif + row_refresh(xmin+clipx-(int)cw->offx,COLNO-1,y+clipy-(int)cw->offy); +#else + if (yoffy || y > ROWNO) continue; /* only refresh board */ + row_refresh(xmin-(int)cw->offx,COLNO-1,y-(int)cw->offy); +#endif + } + + end_glyphout(); + if (ymax >= (int) wins[WIN_STATUS]->offy) { + /* we have wrecked the bottom line */ + flags.botlx = 1; + bot(); + } +} + +void +end_glyphout() +{ +#if defined(ASCIIGRAPH) && !defined(NO_TERMS) + if (GFlag) { + GFlag = FALSE; + graph_off(); + } +#endif +#ifdef TEXTCOLOR + if(ttyDisplay->color != NO_COLOR) { + term_end_color(); + ttyDisplay->color = NO_COLOR; + } +#endif +} + +#ifndef WIN32 +void +g_putch(in_ch) +int in_ch; +{ + register char ch = (char)in_ch; + +# if defined(ASCIIGRAPH) && !defined(NO_TERMS) + if (iflags.IBMgraphics || iflags.eight_bit_tty) { + /* IBM-compatible displays don't need other stuff */ + (void) putchar(ch); + } else if (ch & 0x80) { + if (!GFlag || HE_resets_AS) { + graph_on(); + GFlag = TRUE; + } + (void) putchar((ch ^ 0x80)); /* Strip 8th bit */ + } else { + if (GFlag) { + graph_off(); + GFlag = FALSE; + } + (void) putchar(ch); + } + +#else + (void) putchar(ch); + +#endif /* ASCIIGRAPH && !NO_TERMS */ + + return; +} +#endif /* !WIN32 */ + +#ifdef CLIPPING +void +setclipped() +{ + clipping = TRUE; + clipx = clipy = 0; + clipxmax = CO; + clipymax = LI - 3; +} + +void +tty_cliparound(x, y) +int x, y; +{ + extern boolean restoring; + int oldx = clipx, oldy = clipy; + + if (!clipping) return; + if (x < clipx + 5) { + clipx = max(0, x - 20); + clipxmax = clipx + CO; + } + else if (x > clipxmax - 5) { + clipxmax = min(COLNO, clipxmax + 20); + clipx = clipxmax - CO; + } + if (y < clipy + 2) { + clipy = max(0, y - (clipymax - clipy) / 2); + clipymax = clipy + (LI - 3); + } + else if (y > clipymax - 2) { + clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2); + clipy = clipymax - (LI - 3); + } + if (clipx != oldx || clipy != oldy) { + if (on_level(&u.uz0, &u.uz) && !restoring) + (void) doredraw(); + } +} +#endif /* CLIPPING */ + + +/* + * tty_print_glyph + * + * Print the glyph to the output device. Don't flush the output device. + * + * Since this is only called from show_glyph(), it is assumed that the + * position and glyph are always correct (checked there)! + */ + +void +tty_print_glyph(window, x, y, glyph) + winid window; + xchar x, y; + int glyph; +{ + int ch; + boolean reverse_on = FALSE; + int color; + unsigned special; + +#ifdef CLIPPING + if(clipping) { + if(x <= clipx || y < clipy || x >= clipxmax || y >= clipymax) + return; + } +#endif + /* map glyph to character and color */ + mapglyph(glyph, &ch, &color, &special, x, y); + + /* Move the cursor. */ + tty_curs(window, x,y); + +#ifndef NO_TERMS + if (ul_hack && ch == '_') { /* non-destructive underscore */ + (void) putchar((char) ' '); + backsp(); + } +#endif + +#ifdef TEXTCOLOR + if (color != ttyDisplay->color) { + if(ttyDisplay->color != NO_COLOR) + term_end_color(); + ttyDisplay->color = color; + if(color != NO_COLOR) + term_start_color(color); + } +#endif /* TEXTCOLOR */ + + /* must be after color check; term_end_color may turn off inverse too */ + if (((special & MG_PET) && iflags.hilite_pet) || + ((special & MG_DETECT) && iflags.use_inverse)) { + term_start_attr(ATR_INVERSE); + reverse_on = TRUE; + } + +#if defined(USE_TILES) && defined(MSDOS) + if (iflags.grmode && iflags.tile_view) + xputg(glyph,ch,special); + else +#endif + g_putch(ch); /* print the character */ + + if (reverse_on) { + term_end_attr(ATR_INVERSE); +#ifdef TEXTCOLOR + /* turn off color as well, ATR_INVERSE may have done this already */ + if(ttyDisplay->color != NO_COLOR) { + term_end_color(); + ttyDisplay->color = NO_COLOR; + } +#endif + } + + wins[window]->curx++; /* one character over */ + ttyDisplay->curx++; /* the real cursor moved too */ +} + +void +tty_raw_print(str) + const char *str; +{ + if(ttyDisplay) ttyDisplay->rawprint++; +#if defined(MICRO) || defined(WIN32CON) + msmsg("%s\n", str); +#else + puts(str); (void) fflush(stdout); +#endif +} + +void +tty_raw_print_bold(str) + const char *str; +{ + if(ttyDisplay) ttyDisplay->rawprint++; + term_start_raw_bold(); +#if defined(MICRO) || defined(WIN32CON) + msmsg("%s", str); +#else + (void) fputs(str, stdout); +#endif + term_end_raw_bold(); +#if defined(MICRO) || defined(WIN32CON) + msmsg("\n"); +#else + puts(""); + (void) fflush(stdout); +#endif +} + +int +tty_nhgetch() +{ + int i; +#ifdef UNIX + /* kludge alert: Some Unix variants return funny values if getc() + * is called, interrupted, and then called again. There + * is non-reentrant code in the internal _filbuf() routine, called by + * getc(). + */ + static volatile int nesting = 0; + char nestbuf; +#endif + + (void) fflush(stdout); + /* Note: if raw_print() and wait_synch() get called to report terminal + * initialization problems, then wins[] and ttyDisplay might not be + * available yet. Such problems will probably be fatal before we get + * here, but validate those pointers just in case... + */ + if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE]) + wins[WIN_MESSAGE]->flags &= ~WIN_STOP; +#ifdef UNIX + i = ((++nesting == 1) ? tgetch() : + (read(fileno(stdin), (genericptr_t)&nestbuf,1) == 1 ? (int)nestbuf : + EOF)); + --nesting; +#else + i = tgetch(); +#endif + if (!i) i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ + if (ttyDisplay && ttyDisplay->toplin == 1) + ttyDisplay->toplin = 2; + return i; +} + +/* + * return a key, or 0, in which case a mouse button was pressed + * mouse events should be returned as character postitions in the map window. + * Since normal tty's don't have mice, just return a key. + */ +/*ARGSUSED*/ +int +tty_nh_poskey(x, y, mod) + int *x, *y, *mod; +{ +# if defined(WIN32CON) + int i; + (void) fflush(stdout); + /* Note: if raw_print() and wait_synch() get called to report terminal + * initialization problems, then wins[] and ttyDisplay might not be + * available yet. Such problems will probably be fatal before we get + * here, but validate those pointers just in case... + */ + if (WIN_MESSAGE != WIN_ERR && wins[WIN_MESSAGE]) + wins[WIN_MESSAGE]->flags &= ~WIN_STOP; + i = ntposkey(x, y, mod); + if (!i && mod && *mod == 0) + i = '\033'; /* map NUL to ESC since nethack doesn't expect NUL */ + if (ttyDisplay && ttyDisplay->toplin == 1) + ttyDisplay->toplin = 2; + return i; +# else + return tty_nhgetch(); +# endif +} + +void +win_tty_init() +{ +# if defined(WIN32CON) + nttty_open(); +# endif + return; +} + +#ifdef POSITIONBAR +void +tty_update_positionbar(posbar) +char *posbar; +{ +# ifdef MSDOS + video_update_positionbar(posbar); +# endif +} +#endif + +/* + * Allocate a copy of the given string. If null, return a string of + * zero length. + * + * This is an exact duplicate of copy_of() in X11/winmenu.c. + */ +static char * +copy_of(s) + const char *s; +{ + if (!s) s = ""; + return strcpy((char *) alloc((unsigned) (strlen(s) + 1)), s); +} + +#endif /* TTY_GRAPHICS */ + +/*wintty.c*/ diff --git a/win/win32/dgncomp.dsp b/win/win32/dgncomp.dsp new file mode 100644 index 0000000..815384c --- /dev/null +++ b/win/win32/dgncomp.dsp @@ -0,0 +1,297 @@ +# Microsoft Developer Studio Project File - Name="dgncomp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dgncomp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dgncomp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dgncomp.mak" CFG="dgncomp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dgncomp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dgncomp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dgncomp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "..\util" +# PROP BASE Intermediate_Dir "dgncomp___Win32_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\dgn_comp.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=dgncomp +PostBuild_Cmds=echo Building dungeon echo chdir ..\dat chdir ..\dat echo ..\util\dgn_comp.exe dungeon.pdf ..\util\dgn_comp.exe dungeon.pdf echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "dgncomp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "dgncomp___Win32_Debug" +# PROP BASE Intermediate_Dir "dgncomp___Win32_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\dgn_comp.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=dgncomp +PostBuild_Cmds=echo Building dungeon echo chdir ..\dat chdir ..\dat echo ..\util\dgn_comp.exe dungeon.pdf ..\util\dgn_comp.exe dungeon.pdf echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "dgncomp - Win32 Release" +# Name "dgncomp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_lex.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\dgn_yacc.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_comp.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\onames.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\pm.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/dgnstuff.dsp b/win/win32/dgnstuff.dsp new file mode 100644 index 0000000..ab1e95a --- /dev/null +++ b/win/win32/dgnstuff.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="dgnstuff" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=dgnstuff - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dgnstuff.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dgnstuff.mak" CFG="dgnstuff - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dgnstuff - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "dgnstuff - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "dgnstuff - Win32 Release" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f dgnstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "dgnstuff.exe" +# PROP BASE Bsc_Name "dgnstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "dgnstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\dgncomp.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "dgnstuff - Win32 Debug" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f dgnstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "dgnstuff.exe" +# PROP BASE Bsc_Name "dgnstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "dgnstuff___Win32_Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake /f "dgnstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\dgncomp.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "dgnstuff - Win32 Release" +# Name "dgnstuff - Win32 Debug" + +!IF "$(CFG)" == "dgnstuff - Win32 Release" + +!ELSEIF "$(CFG)" == "dgnstuff - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/dgnstuff.mak b/win/win32/dgnstuff.mak new file mode 100644 index 0000000..8e5aa81 --- /dev/null +++ b/win/win32/dgnstuff.mak @@ -0,0 +1,59 @@ +#Set all of these or none of them +#YACC = byacc.exe +#LEX = flex.exe +#YTABC = y_tab.c +#YTABH = y_tab.h +#LEXYYC = lexyy.c + +!IF "$(YACC)"!="" +@echo Yacc-alike set to $(YACC) +@echo YTABC set to $(YTABC) +@echo YTABH set to $(YTABH) +!ENDIF + +!IF "$(LEX)"!="" +@echo Lex-alike set to $(LEX) +@echo LEXYYC set to $(LEXYYC) +!ENDIF + +default: all + +all: ..\util\dgn_yacc.c ..\util\dgn_lex.c + +rebuild: clean all + +clean: + -del ..\util\dgn_lex.c + -del ..\util\dgn_yacc.c + -del ..\include\dgn_comp.h + +#========================================== +# Dungeon Compiler Stuff +#========================================== + +..\util\dgn_yacc.c ..\include\dgn_comp.h : ..\util\dgn_comp.y +!IF "$(YACC)"=="" + @echo Using pre-built dgn_yacc.c and dgn_comp.h + @copy ..\sys\share\dgn_yacc.c ..\util\dgn_yacc.c + @copy ..\sys\share\dgn_comp.h ..\include\dgn_comp.h +!ELSE + chdir ..\util + $(YACC) -d dgn_comp.y + copy $(YTABC) $@ + copy $(YTABH) ..\include\dgn_comp.h + @del $(YTABC) + @del $(YTABH) + chdir ..\build +!ENDIF + +..\util\dgn_lex.c: ..\util\dgn_comp.l +!IF "$(LEX)"=="" + @echo Using pre-built dgn_lex.c + @copy ..\sys\share\dgn_lex.c $@ +!ELSE + chdir ..\util + $(LEX) dgn_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir ..\build +!ENDIF diff --git a/win/win32/dlb_main.dsp b/win/win32/dlb_main.dsp new file mode 100644 index 0000000..8dff70f --- /dev/null +++ b/win/win32/dlb_main.dsp @@ -0,0 +1,179 @@ +# Microsoft Developer Studio Project File - Name="dlb_main" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=dlb_main - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dlb_main.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dlb_main.mak" CFG="dlb_main - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dlb_main - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "dlb_main - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dlb_main - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "DLB" /D "WIN32CON" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\dlb_main.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Packaging via DLB +PostBuild_Cmds=echo chdir ..\dat \ +chdir ..\dat \ +chdir \ + echo data >dlb.lst \ + echo oracles >>dlb.lst \ + if exist options echo options >>dlb.lst \ + if exist ttyoptions echo ttyoptions >>dlb.lst \ + if exist guioptions echo guioptions >>dlb.lst \ + if NOT exist porthelp copy ..\sys\winnt\porthelp porthelp \ + if exist porthelp echo porthelp >>dlb.lst \ + echo quest.dat >>dlb.lst \ + echo rumors >>dlb.lst \ + echo help >>dlb.lst \ + echo hh >>dlb.lst \ + echo cmdhelp >>dlb.lst \ + echo history >>dlb.lst \ + echo opthelp >>dlb.lst \ + echo wizhelp >>dlb.lst \ + echo dungeon >>dlb.lst \ + echo license >>dlb.lst \ + for %%N in (*.lev) do echo %%N >>dlb.lst \ + ..\util\dlb_main.exe cIf dlb.lst nhdat \ + echo chdir ..\build \ +chdir ..\build \ +echo if NOT exist ..\binary\*.* mkdir ..\binary \ + if NOT exist ..\binary\*.* mkdir ..\binary +# End Special Build Tool + +!ELSEIF "$(CFG)" == "dlb_main - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "DLB" /D "WIN32CON" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\dlb_main.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Packaging via dlb +PostBuild_Cmds=echo chdir ..\dat \ +chdir ..\dat \ +chdir \ + echo data >dlb.lst \ + echo oracles >>dlb.lst \ + if exist options echo options >>dlb.lst \ + if exist ttyoptions echo ttyoptions >>dlb.lst \ + if exist guioptions echo guioptions >>dlb.lst \ + if NOT exist porthelp copy ..\sys\winnt\porthelp porthelp \ + if exist porthelp echo porthelp >>dlb.lst \ + echo quest.dat >>dlb.lst \ + echo rumors >>dlb.lst \ + echo help >>dlb.lst \ + echo hh >>dlb.lst \ + echo cmdhelp >>dlb.lst \ + echo history >>dlb.lst \ + echo opthelp >>dlb.lst \ + echo wizhelp >>dlb.lst \ + echo dungeon >>dlb.lst \ + echo license >>dlb.lst \ + for %%N in (*.lev) do echo %%N >>dlb.lst \ + ..\util\dlb_main.exe cIf dlb.lst nhdat \ +echo chdir ..\build \ +chdir ..\build \ +echo if NOT exist ..\binary\*.* mkdir ..\binary \ +if NOT exist ..\binary\*.* mkdir ..\binary +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "dlb_main - Win32 Release" +# Name "dlb_main - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\dlb.c +# End Source File +# Begin Source File + +SOURCE=..\util\dlb_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\dlb.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/levcomp.dsp b/win/win32/levcomp.dsp new file mode 100644 index 0000000..7c1486e --- /dev/null +++ b/win/win32/levcomp.dsp @@ -0,0 +1,198 @@ +# Microsoft Developer Studio Project File - Name="levcomp" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=levcomp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "levcomp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "levcomp.mak" CFG="levcomp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "levcomp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "levcomp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "levcomp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=levcomp +PostBuild_Cmds=echo Building special levels echo chdir ..\dat chdir ..\dat \ +echo arch.des ..\util\levcomp.exe arch.des \ +echo barb.des ..\util\levcomp.exe barb.des \ +echo bigroom.des ..\util\levcomp.exe bigroom.des \ +echo castle.des ..\util\levcomp.exe castle.des \ +echo caveman.des ..\util\levcomp.exe caveman.des \ +echo endgame.des ..\util\levcomp.exe endgame.des \ +echo gehennom.des ..\util\levcomp.exe gehennom.des \ +echo healer.des ..\util\levcomp.exe healer.des \ +echo knight.des ..\util\levcomp.exe knight.des \ +echo knox.des ..\util\levcomp.exe knox.des \ +echo medusa.des ..\util\levcomp.exe medusa.des \ +echo mines.des ..\util\levcomp.exe mines.des \ +echo monk.des ..\util\levcomp.exe monk.des \ +echo oracle.des ..\util\levcomp.exe oracle.des \ +echo priest.des ..\util\levcomp.exe priest.des \ +echo ranger.des ..\util\levcomp.exe ranger.des \ +echo rogue.des ..\util\levcomp.exe rogue.des \ +echo samurai.des ..\util\levcomp.exe samurai.des \ +echo sokoban.des ..\util\levcomp.exe sokoban.des \ +echo tourist.des ..\util\levcomp.exe tourist.des \ +echo tower.des ..\util\levcomp.exe tower.des \ +echo valkyrie.des ..\util\levcomp.exe valkyrie.des \ +echo wizard .des ..\util\levcomp.exe wizard.des \ +echo yendor.des ..\util\levcomp.exe yendor.des \ +echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "levcomp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=levcomp +PostBuild_Cmds=echo Building special levels echo chdir ..\dat chdir ..\dat \ +echo arch.des ..\util\levcomp.exe arch.des \ +echo barb.des ..\util\levcomp.exe barb.des \ +echo bigroom.des ..\util\levcomp.exe bigroom.des \ +echo castle.des ..\util\levcomp.exe castle.des \ +echo caveman.des ..\util\levcomp.exe caveman.des \ +echo endgame.des ..\util\levcomp.exe endgame.des \ +echo gehennom.des ..\util\levcomp.exe gehennom.des \ +echo healer.des ..\util\levcomp.exe healer.des \ +echo knight.des ..\util\levcomp.exe knight.des \ +echo knox.des ..\util\levcomp.exe knox.des \ +echo medusa.des ..\util\levcomp.exe medusa.des \ +echo mines.des ..\util\levcomp.exe mines.des \ +echo monk.des ..\util\levcomp.exe monk.des \ +echo oracle.des ..\util\levcomp.exe oracle.des \ +echo priest.des ..\util\levcomp.exe priest.des \ +echo ranger.des ..\util\levcomp.exe ranger.des \ +echo rogue.des ..\util\levcomp.exe rogue.des \ +echo samurai.des ..\util\levcomp.exe samurai.des \ +echo sokoban.des ..\util\levcomp.exe sokoban.des \ +echo tourist.des ..\util\levcomp.exe tourist.des \ +echo tower.des ..\util\levcomp.exe tower.des \ +echo valkyrie.des ..\util\levcomp.exe valkyrie.des \ +echo wizard .des ..\util\levcomp.exe wizard.des \ +echo yendor.des ..\util\levcomp.exe yendor.des \ +echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "levcomp - Win32 Release" +# Name "levcomp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_lex.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_main.c +# End Source File +# Begin Source File + +SOURCE=..\util\lev_yacc.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\util\panic.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\lev_comp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/levstuff.dsp b/win/win32/levstuff.dsp new file mode 100644 index 0000000..276ad81 --- /dev/null +++ b/win/win32/levstuff.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="levstuff" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=levstuff - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "levstuff.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "levstuff.mak" CFG="levstuff - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "levstuff - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "levstuff - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "levstuff - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f levstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "levstuff.exe" +# PROP BASE Bsc_Name "levstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "levstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\lev_lex.c" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "levstuff - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "levstuff___Win32_Debug0" +# PROP BASE Intermediate_Dir "levstuff___Win32_Debug0" +# PROP BASE Cmd_Line "NMAKE /f levstuff.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "levstuff.exe" +# PROP BASE Bsc_Name "levstuff.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "levstuff___Win32_Debug0" +# PROP Intermediate_Dir "levstuff___Win32_Debug0" +# PROP Cmd_Line "nmake /f "levstuff.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\util\lev_lex.c" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "levstuff - Win32 Release" +# Name "levstuff - Win32 Debug" + +!IF "$(CFG)" == "levstuff - Win32 Release" + +!ELSEIF "$(CFG)" == "levstuff - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/levstuff.mak b/win/win32/levstuff.mak new file mode 100644 index 0000000..c7540c4 --- /dev/null +++ b/win/win32/levstuff.mak @@ -0,0 +1,59 @@ +#YACC = byacc.exe +#LEX = flex.exe +#YTABC = y_tab.c +#YTABH = y_tab.h +#LEXYYC = lexyy.c + +!IF "$(YACC)"!="" +@echo Yacc-alike set to $(YACC) +@echo YTABC set to $(YTABC) +@echo YTABH set to $(YTABH) +!ENDIF + +!IF "$(LEX)"!="" +@echo Lex-alike set to $(LEX) +@echo LEXYYC set to $(LEXYYC) +!ENDIF + + +default: all + +all: ..\util\lev_yacc.c ..\util\lev_lex.c + +rebuild: clean all + +clean: + -del ..\util\lev_lex.c + -del ..\util\lev_yacc.c + -del ..\include\lev_comp.h + +#========================================== +# Level Compiler Stuff +#========================================== +..\util\lev_yacc.c ..\include\lev_comp.h: ..\util\lev_comp.y +!IF "$(YACC)"=="" + @echo Using pre-built lev_yacc.c and lev_comp.h + @copy ..\sys\share\lev_yacc.c ..\util\lev_yacc.c + @copy ..\sys\share\lev_comp.h ..\include\lev_comp.h +!ELSE + chdir ..\util + $(YACC) -d lev_comp.y + copy $(YTABC) $@ + copy $(YTABH) ..\include\lev_comp.h + @del $(YTABC) + @del $(YTABH) + chdir ..\build +!ENDIF + +..\util\lev_lex.c: ..\util\lev_comp.l +!IF "$(LEX)"=="" + @echo Using pre-built lev_lex.c + @copy ..\sys\share\lev_lex.c $@ +!ELSE + chdir ..\util + $(LEX) lev_comp.l + copy $(LEXYYC) $@ + @del $(LEXYYC) + chdir ..\build +!ENDIF + diff --git a/win/win32/makedefs.dsp b/win/win32/makedefs.dsp new file mode 100644 index 0000000..5ecdc88 --- /dev/null +++ b/win/win32/makedefs.dsp @@ -0,0 +1,198 @@ +# Microsoft Developer Studio Project File - Name="makedefs" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=makedefs - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makedefs.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makedefs.mak" CFG="makedefs - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makedefs - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "makedefs - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "makedefs - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Running makedefs +PostBuild_Cmds=echo chdir ..\util chdir ..\util chdir \ +echo makedefs.exe -v makedefs.exe -v \ +echo makedefs.exe -o makedefs.exe -o \ +echo makedefs.exe -p makedefs.exe -p \ +echo makedefs.exe -m makedefs.exe -m \ +echo makedefs.exe -z makedefs.exe -z \ +echo chdir ..\dat chdir ..\dat chdir \ +echo Generating NetHack database echo ..\util\makedefs.exe -d ..\util\makedefs.exe -d \ +echo Generating rumors echo ..\util\makedefs.exe -r ..\util\makedefs.exe -r \ +echo Generating quests echo ..\util\makedefs.exe -q ..\util\makedefs.exe -q \ +echo Generating oracles echo ..\util\makedefs.exe -h ..\util\makedefs.exe -h \ +echo Generating dungeon.pdf echo ..\util\makedefs.exe -e ..\util\makedefs.exe -e \ +echo chdir ..\build chdir ..\build \ +copy ..\win\share\tilemap.c ..\win\share\tiletxt.c +# End Special Build Tool + +!ELSEIF "$(CFG)" == "makedefs - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "..\util" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "." /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Running makedefs +PostBuild_Cmds=echo chdir ..\util chdir ..\util chdir \ +echo makedefs.exe -v makedefs.exe -v \ +echo makedefs.exe -o makedefs.exe -o \ +echo makedefs.exe -p makedefs.exe -p \ +echo makedefs.exe -m makedefs.exe -m \ +echo makedefs.exe -z makedefs.exe -z \ +echo chdir ..\dat chdir ..\dat chdir \ +echo Generating NetHack database echo ..\util\makedefs.exe -d ..\util\makedefs.exe -d \ +echo Generating rumors echo ..\util\makedefs.exe -r ..\util\makedefs.exe -r \ +echo Generating quests echo ..\util\makedefs.exe -q ..\util\makedefs.exe -q \ +echo Generating oracles echo ..\util\makedefs.exe -h ..\util\makedefs.exe -h \ +echo Generating dungeon.pdf echo ..\util\makedefs.exe -e ..\util\makedefs.exe -e \ +echo chdir ..\build chdir ..\build \ +copy ..\win\share\tilemap.c ..\win\share\tiletxt.c +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "makedefs - Win32 Release" +# Name "makedefs - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\util\makedefs.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monflag.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\patchlevel.h +# End Source File +# Begin Source File + +SOURCE=..\include\qtext.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/mhaskyn.c b/win/win32/mhaskyn.c new file mode 100644 index 0000000..81be133 --- /dev/null +++ b/win/win32/mhaskyn.c @@ -0,0 +1,11 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include "winMS.h" +#include "mhaskyn.h" + +int mswin_yes_no_dialog( const char *question, const char *choices, int def) +{ + return '\032'; +} diff --git a/win/win32/mhaskyn.h b/win/win32/mhaskyn.h new file mode 100644 index 0000000..a386b09 --- /dev/null +++ b/win/win32/mhaskyn.h @@ -0,0 +1,11 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINAskYesNO_h +#define MSWINAskYesNO_h + +#include "winMS.h" + +int mswin_yes_no_dialog( const char *question, const char *choices, int def); + +#endif /* MSWINAskYesNO_h */ diff --git a/win/win32/mhdlg.c b/win/win32/mhdlg.c new file mode 100644 index 0000000..f4593bb --- /dev/null +++ b/win/win32/mhdlg.c @@ -0,0 +1,758 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* various dialog boxes are defined here */ + +#include "winMS.h" +#include "hack.h" +#include "func_tab.h" +#include "resource.h" +#include "mhdlg.h" + +/*---------------------------------------------------------------*/ +/* data for getlin dialog */ +struct getlin_data { + const char* question; + char* result; + size_t result_size; +}; + +BOOL CALLBACK GetlinDlgProc(HWND, UINT, WPARAM, LPARAM); + +int mswin_getlin_window ( + const char *question, + char *result, + size_t result_size +) +{ + int ret; + struct getlin_data data; + + /* initilize dialog data */ + ZeroMemory(&data, sizeof(data)); + data.question = question; + data.result = result; + data.result_size = result_size; + + /* create modal dialog window */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_GETLIN), + GetNHApp()->hMainWnd, + GetlinDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create getlin window"); + + return ret; +} + +BOOL CALLBACK GetlinDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct getlin_data* data; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + TCHAR wbuf[BUFSZ]; + HDC WindowDC; + HWND ControlHWND; + SIZE WindowExtents; + SIZE ViewPortExtents; + RECT ControlRect; + RECT ClientRect; + LONG Division; + LONG ButtonOffset; + + switch (message) + { + case WM_INITDIALOG: + data = (struct getlin_data*)lParam; + SetWindowText(hWnd, NH_A2W(data->question, wbuf, sizeof(wbuf))); + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* center dialog in the main window */ + GetWindowRect(hWnd, &dlg_rt); + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + WindowDC = GetWindowDC(hWnd); + + if (!GetWindowExtEx(WindowDC, &WindowExtents) || + !GetViewportExtEx(WindowDC, &ViewPortExtents) || + !GetTextExtentPoint32(GetWindowDC (hWnd), wbuf, _tcslen(wbuf), &dlg_sz)) + { + dlg_sz.cx = 0; + } + else + { + /* I think we need to do the following scaling */ + dlg_sz.cx *= ViewPortExtents.cx; dlg_sz.cx /= WindowExtents.cx; + /* Add the size of the various items in the caption bar */ + dlg_sz.cx += GetSystemMetrics(SM_CXSIZE) + + 2 * (GetSystemMetrics (SM_CXBORDER) + GetSystemMetrics(SM_CXFRAME)); + } + + if (dlg_sz.cx < dlg_rt.right - dlg_rt.left) + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* set focus and size of the edit control */ + ControlHWND = GetDlgItem(hWnd, IDC_GETLIN_EDIT); + SetFocus(ControlHWND); + GetClientRect (hWnd, &ClientRect); + GetWindowRect (ControlHWND, &ControlRect); + MoveWindow (ControlHWND, 0, 0, + ClientRect.right - ClientRect.left, + ControlRect.bottom - ControlRect.top, TRUE); + ButtonOffset = ControlRect.bottom - ControlRect.top; + + /* Now get the OK and CANCEL buttons */ + ControlHWND = GetDlgItem(hWnd, IDOK); + GetWindowRect (ControlHWND, &ControlRect); + Division = ((ClientRect.right - ClientRect.left) - + 2 * (ControlRect.right - ControlRect.left)) / 3; + MoveWindow (ControlHWND, Division, + ButtonOffset, + ControlRect.right - ControlRect.left, + ControlRect.bottom - ControlRect.top, TRUE); + ControlHWND = GetDlgItem(hWnd, IDCANCEL); + MoveWindow (ControlHWND, + Division * 2 + ControlRect.right - ControlRect.left, + ButtonOffset, + ControlRect.right - ControlRect.left, + ControlRect.bottom - ControlRect.top, TRUE); + + /* tell windows that we've set the focus */ + return FALSE; + break; + + case WM_COMMAND: + { + TCHAR wbuf[BUFSZ]; + + switch (LOWORD(wParam)) + { + /* OK button was pressed */ + case IDOK: + data = (struct getlin_data*)GetWindowLong(hWnd, GWL_USERDATA); + SendDlgItemMessage(hWnd, IDC_GETLIN_EDIT, WM_GETTEXT, (WPARAM)sizeof(wbuf), (LPARAM)wbuf ); + NH_W2A(wbuf, data->result, data->result_size); + + /* Fall through. */ + + /* cancel button was pressed */ + case IDCANCEL: + EndDialog(hWnd, wParam); + return TRUE; + } + } break; + + } /* end switch (message) */ + return FALSE; +} + + +/*---------------------------------------------------------------*/ +/* dialog data for the list of extended commands */ +struct extcmd_data { + int* selection; +}; + +BOOL CALLBACK ExtCmdDlgProc(HWND, UINT, WPARAM, LPARAM); + +int mswin_ext_cmd_window (int* selection) +{ + int ret; + struct extcmd_data data; + + /* init dialog data */ + ZeroMemory(&data, sizeof(data)); + *selection = -1; + data.selection = selection; + + /* create modal dialog window */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_EXTCMD), + GetNHApp()->hMainWnd, + ExtCmdDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create extcmd window"); + return ret; +} + +BOOL CALLBACK ExtCmdDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct extcmd_data* data; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + int i; + const char *ptr; + TCHAR wbuf[255]; + + switch (message) + { + case WM_INITDIALOG: + data = (struct extcmd_data*)lParam; + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hWnd, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* fill combobox with extended commands */ + for(i=0; (ptr=extcmdlist[i].ef_txt); i++) { + SendDlgItemMessage(hWnd, IDC_EXTCMD_LIST, LB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(ptr, wbuf, sizeof(wbuf)) ); + } + + /* set focus to the list control */ + SetFocus(GetDlgItem(hWnd, IDC_EXTCMD_LIST)); + + /* tell windows we set the focus */ + return FALSE; + break; + + case WM_COMMAND: + data = (struct extcmd_data*)GetWindowLong(hWnd, GWL_USERDATA); + switch (LOWORD(wParam)) + { + /* OK button ws clicked */ + case IDOK: + *data->selection = SendDlgItemMessage(hWnd, IDC_EXTCMD_LIST, LB_GETCURSEL, (WPARAM)0, (LPARAM)0 ); + if( *data->selection==LB_ERR ) + *data->selection = -1; + /* Fall through. */ + + /* CANCEL button ws clicked */ + case IDCANCEL: + EndDialog(hWnd, wParam); + return TRUE; + + /* list control events */ + case IDC_EXTCMD_LIST: + switch(HIWORD(wParam)) { + + case LBN_DBLCLK: + /* double click within the list + wParam + The low-order word is the list box identifier. + The high-order word is the notification message. + lParam + Handle to the list box + */ + *data->selection = SendMessage((HWND)lParam, LB_GETCURSEL, (WPARAM)0, (LPARAM)0); + if( *data->selection==LB_ERR ) + *data->selection = -1; + EndDialog(hWnd, IDOK); + return TRUE; + } + break; + } + } + return FALSE; +} + +/*---------------------------------------------------------------*/ +/* player selector dialog data */ +struct plsel_data { + int* selection; +}; + +BOOL CALLBACK PlayerSelectorDlgProc(HWND, UINT, WPARAM, LPARAM); +static void plselInitDialog(HWND hWnd); +static void plselAdjustLists(HWND hWnd, int changed_opt); +static int plselFinalSelection(HWND hWnd, int* selection); + +int mswin_player_selection_window ( int* selection ) +{ + int ret; + struct plsel_data data; + + /* init dialog data */ + ZeroMemory(&data, sizeof(data)); + data.selection = selection; + + /* create modal dialog */ + ret = DialogBoxParam( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_PLAYER_SELECTOR), + GetNHApp()->hMainWnd, + PlayerSelectorDlgProc, + (LPARAM)&data + ); + if( ret==-1 ) panic("Cannot create getlin window"); + + return ret; +} + +BOOL CALLBACK PlayerSelectorDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + struct plsel_data* data; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + + switch (message) + { + case WM_INITDIALOG: + data = (struct plsel_data*)lParam; + SetWindowLong(hWnd, GWL_USERDATA, lParam); + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hWnd, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hWnd, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + /* init dialog */ + plselInitDialog(hWnd); + + /* set focus on the role checkbox (random) field */ + SetFocus(GetDlgItem(hWnd, IDC_PLSEL_ROLE_RANDOM)); + + /* tell windows we set the focus */ + return FALSE; + break; + + case WM_COMMAND: + data = (struct plsel_data*)GetWindowLong(hWnd, GWL_USERDATA); + switch (LOWORD(wParam)) { + + /* OK button was clicked */ + case IDOK: + if( plselFinalSelection(hWnd, data->selection) ) { + EndDialog(hWnd, wParam); + } else { + NHMessageBox(hWnd, TEXT("Cannot match this role. Try something else."), MB_ICONSTOP | MB_OK ); + } + return TRUE; + + /* CANCEL button was clicked */ + case IDCANCEL: + *data->selection = -1; + EndDialog(hWnd, wParam); + return TRUE; + + /* following are events from dialog controls: + "random" checkboxes send BN_CLICKED messages; + role/race/... combo-boxes send CBN_SELENDOK + if something was selected; + */ + case IDC_PLSEL_ROLE_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + /* enable corresponding list window if "random" + checkbox was "unchecked" */ + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_RACE_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_GENDER_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_ALIGN_RANDOM: + if( HIWORD(wParam)==BN_CLICKED ) { + EnableWindow( + GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), + SendMessage((HWND)lParam, BM_GETCHECK, 0, 0)==BST_UNCHECKED + ); + } + break; + + case IDC_PLSEL_ROLE_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + /* filter out invalid options if + the selection was made */ + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_RACE_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_GENDER_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + + case IDC_PLSEL_ALIGN_LIST: + if( HIWORD(wParam)==CBN_SELENDOK ) { + plselAdjustLists( hWnd, LOWORD(wParam) ); + } + break; + } + break; + } + return FALSE; +} + +void setComboBoxValue(HWND hWnd, int combo_box, int value) +{ + int index_max = SendDlgItemMessage(hWnd, combo_box, CB_GETCOUNT, 0, 0); + int index; + int value_to_set = LB_ERR; + for (index = 0; index < index_max; index++) { + if (SendDlgItemMessage(hWnd, combo_box, CB_GETITEMDATA, (WPARAM)index, 0) == value) { + value_to_set = index; + break; + } + } + SendDlgItemMessage(hWnd, combo_box, CB_SETCURSEL, (WPARAM)value_to_set, 0); +} + +/* initialize player selector dialog */ +void plselInitDialog(HWND hWnd) +{ + TCHAR wbuf[BUFSZ]; + + /* set player name */ + SetDlgItemText(hWnd, IDC_PLSEL_NAME, NH_A2W(plname, wbuf, sizeof(wbuf))); + + /* check flags for consistency */ + if( flags.initrole>=0 ) { + if (flags.initrace>=0 && !validrace(flags.initrole, flags.initrace)) { + flags.initrace = ROLE_NONE; + } + + if (flags.initgend>=0 && !validgend(flags.initrole, flags.initrace, flags.initgend)) { + flags.initgend = ROLE_NONE; + } + + if (flags.initalign>=0 && !validalign(flags.initrole, flags.initrace, flags.initalign)) { + flags.initalign = ROLE_NONE; + } + } + + /* populate select boxes */ + plselAdjustLists(hWnd, -1); + + /* intialize roles list */ + if( flags.initrole<0 || !ok_role(flags.initrole, ROLE_NONE, ROLE_NONE, ROLE_NONE)) { + CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_ROLE_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_ROLE_LIST, flags.initrole); + } + + /* intialize races list */ + if( flags.initrace<0 || !ok_race(flags.initrole, flags.initrace, ROLE_NONE, ROLE_NONE) ) { + CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_RACE_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_RACE_LIST, flags.initrace); + } + + /* intialize genders list */ + if( flags.initgend<0 || !ok_gend(flags.initrole, flags.initrace, flags.initgend, ROLE_NONE)) { + CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_GENDER_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_GENDER_LIST, flags.initgend); + } + + /* intialize alignments list */ + if( flags.initalign<0 || !ok_align(flags.initrole, flags.initrace, flags.initgend, flags.initalign) ) { + CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_CHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), FALSE); + } else { + CheckDlgButton(hWnd, IDC_PLSEL_ALIGN_RANDOM, BST_UNCHECKED); + EnableWindow(GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST), TRUE); + setComboBoxValue(hWnd, IDC_PLSEL_ALIGN_LIST, flags.initalign); + } +} + +/* adjust role/race/alignment/gender list - filter out + invalid combinations + changed_sel points to the list where selection occured + (-1 if unknown) +*/ +void plselAdjustLists(HWND hWnd, int changed_sel) +{ + HWND control_role, control_race, control_gender, control_align; + int initrole, initrace, initgend, initalign; + int i; + int ind; + int valid_opt; + TCHAR wbuf[255]; + + /* get control handles */ + control_role = GetDlgItem(hWnd, IDC_PLSEL_ROLE_LIST); + control_race = GetDlgItem(hWnd, IDC_PLSEL_RACE_LIST); + control_gender = GetDlgItem(hWnd, IDC_PLSEL_GENDER_LIST); + control_align = GetDlgItem(hWnd, IDC_PLSEL_ALIGN_LIST); + + /* get current selections */ + ind = SendMessage(control_role, CB_GETCURSEL, 0, 0); + initrole = (ind==LB_ERR)? flags.initrole : SendMessage(control_role, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_race, CB_GETCURSEL, 0, 0); + initrace = (ind==LB_ERR)? flags.initrace : SendMessage(control_race, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_gender, CB_GETCURSEL, 0, 0); + initgend = (ind==LB_ERR)? flags.initgend : SendMessage(control_gender, CB_GETITEMDATA, ind, 0); + + ind = SendMessage(control_align, CB_GETCURSEL, 0, 0); + initalign = (ind==LB_ERR)? flags.initalign : SendMessage(control_align, CB_GETITEMDATA, ind, 0); + + /* intialize roles list */ + if( changed_sel==-1 ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_role, CB_RESETCONTENT, 0, 0); + for (i = 0; roles[i].name.m; i++) { + if (initgend>=0 && flags.female && roles[i].name.f) + ind = SendMessage(control_role, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(roles[i].name.f, wbuf, sizeof(wbuf)) ); + else + ind = SendMessage(control_role, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(roles[i].name.m, wbuf, sizeof(wbuf)) ); + + SendMessage(control_role, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initrole ) { + SendMessage(control_role, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected role + if it is still valid */ + if( !valid_opt ) { + initrole = ROLE_NONE; + initrace = ROLE_NONE; + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_role, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the races list */ + changed_sel=IDC_PLSEL_ROLE_LIST; + } + + /* intialize races list */ + if( changed_sel==IDC_PLSEL_ROLE_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_race, CB_RESETCONTENT, 0, 0); + for (i = 0; races[i].noun; i++) + if (ok_race(initrole, i, ROLE_NONE, ROLE_NONE)) { + ind = SendMessage(control_race, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(races[i].noun, wbuf, sizeof(wbuf)) ); + SendMessage(control_race, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initrace ) { + SendMessage(control_race, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected race + if it is still valid */ + if( !valid_opt ) { + initrace = ROLE_NONE; + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_race, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the genders list */ + changed_sel=IDC_PLSEL_RACE_LIST; + } + + /* intialize genders list */ + if( changed_sel==IDC_PLSEL_RACE_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_gender, CB_RESETCONTENT, 0, 0); + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(initrole, initrace, i, ROLE_NONE)) { + ind = SendMessage(control_gender, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(genders[i].adj, wbuf, sizeof(wbuf)) ); + SendMessage(control_gender, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initgend ) { + SendMessage(control_gender, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected gender + if it is still valid */ + if( !valid_opt ) { + initgend = ROLE_NONE; + initalign = ROLE_NONE; + SendMessage(control_gender, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + + /* trigger change of the alignments list */ + changed_sel=IDC_PLSEL_GENDER_LIST; + } + + /* intialize alignments list */ + if( changed_sel==IDC_PLSEL_GENDER_LIST ) { + valid_opt = 0; + + /* reset content and populate the list */ + SendMessage(control_align, CB_RESETCONTENT, 0, 0); + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(initrole, initrace, initgend, i)) { + ind = SendMessage(control_align, CB_ADDSTRING, (WPARAM)0, (LPARAM)NH_A2W(aligns[i].adj, wbuf, sizeof(wbuf)) ); + SendMessage(control_align, CB_SETITEMDATA, (WPARAM)ind, (LPARAM)i ); + if( i==initalign ) { + SendMessage(control_align, CB_SETCURSEL, (WPARAM)ind, (LPARAM)0 ); + valid_opt = 1; + } + } + + /* set selection to the previously selected alignment + if it is still valid */ + if( !valid_opt ) { + initalign = ROLE_NONE; + SendMessage(control_align, CB_SETCURSEL, (WPARAM)-1, (LPARAM)0 ); + } + } +} + +/* player made up his mind - get final selection here */ +int plselFinalSelection(HWND hWnd, int* selection) +{ + int ind; + + /* get current selections */ + if( SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initrole = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, CB_GETCURSEL, 0, 0); + flags.initrole = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_ROLE_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initrace = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, CB_GETCURSEL, 0, 0); + flags.initrace = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_RACE_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initgend = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, CB_GETCURSEL, 0, 0); + flags.initgend = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_GENDER_LIST, CB_GETITEMDATA, ind, 0); + } + + if( SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_RANDOM, BM_GETCHECK, 0, 0)==BST_CHECKED ) { + flags.initalign = ROLE_RANDOM; + } else { + ind = SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, CB_GETCURSEL, 0, 0); + flags.initalign = (ind==LB_ERR)? ROLE_RANDOM : SendDlgItemMessage(hWnd, IDC_PLSEL_ALIGN_LIST, CB_GETITEMDATA, ind, 0); + } + + + /* check the role */ + if( flags.initrole==ROLE_RANDOM ) { + flags.initrole = pick_role(flags.initrace, flags.initgend, flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + NHMessageBox(hWnd, TEXT("Incompatible role!"), MB_ICONSTOP | MB_OK); + return FALSE; + } + } + + /* Select a race, if necessary */ + /* force compatibility with role */ + if (flags.initrace==ROLE_RANDOM || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (flags.initrace == ROLE_RANDOM) { + flags.initrace = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM); + } + + if (flags.initrace < 0) { + NHMessageBox(hWnd, TEXT("Incompatible race!"), MB_ICONSTOP | MB_OK); + return FALSE; + } + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || + !validgend(flags.initrole, flags.initrace, flags.initgend)) { + /* pre-selected gender not valid */ + if (flags.initgend == ROLE_RANDOM) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, flags.initalign, PICK_RANDOM); + } + + if (flags.initgend < 0) { + NHMessageBox(hWnd, TEXT("Incompatible gender!"), MB_ICONSTOP | MB_OK); + return FALSE; + } + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || + !validalign(flags.initrole, flags.initrace, flags.initalign)) { + /* pre-selected alignment not valid */ + if (flags.initalign == ROLE_RANDOM) { + flags.initalign = pick_align(flags.initrole, flags.initrace, flags.initgend, PICK_RANDOM); + } else { + NHMessageBox(hWnd, TEXT("Incompatible alignment!"), MB_ICONSTOP | MB_OK); + return FALSE; + } + } + + return TRUE; +} diff --git a/win/win32/mhdlg.h b/win/win32/mhdlg.h new file mode 100644 index 0000000..b964838 --- /dev/null +++ b/win/win32/mhdlg.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINDlgWindow_h +#define MSWINDlgWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +int mswin_getlin_window (const char *question, char *result, size_t result_size); +int mswin_ext_cmd_window (int* selection); +int mswin_player_selection_window(int* selection); + +#endif /* MSWINDlgWindow_h */ diff --git a/win/win32/mhfont.c b/win/win32/mhfont.c new file mode 100644 index 0000000..5f05e76 --- /dev/null +++ b/win/win32/mhfont.c @@ -0,0 +1,205 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* font management and such */ + +#include "mhfont.h" + +#define MAXFONTS 64 + +/* font table - 64 fonts ought to be enough */ +static struct font_table_entry { + int code; + HFONT hFont; +} font_table[MAXFONTS] ; +static int font_table_size = 0; +HFONT version_splash_font; + +#define NHFONT_CODE(win, attr) (((attr&0xFF)<<8)|(win_type&0xFF)) + +static void __cdecl font_table_cleanup(void); + +void mswin_init_splashfonts(HWND hWnd) +{ + HDC hdc = GetDC(hWnd); + HFONT fnt = NULL; + LOGFONT lgfnt; + ZeroMemory( &lgfnt, sizeof(lgfnt) ); + lgfnt.lfHeight = -80; // height of font + lgfnt.lfWidth = 0; // average character width + lgfnt.lfEscapement = 0; // angle of escapement + lgfnt.lfOrientation = 0; // base-line orientation angle + lgfnt.lfWeight = FW_BOLD; // font weight + lgfnt.lfItalic = FALSE; // italic attribute option + lgfnt.lfUnderline = FALSE; // underline attribute option + lgfnt.lfStrikeOut = FALSE; // strikeout attribute option + lgfnt.lfCharSet = ANSI_CHARSET; // character set identifier + lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision + lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision + lgfnt.lfQuality = DEFAULT_QUALITY; // output quality + lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family + NH_A2W( "Times New Roman", lgfnt.lfFaceName, LF_FACESIZE); + version_splash_font = CreateFontIndirect(&lgfnt); + ReleaseDC(hWnd, hdc); +} + +void mswin_destroy_splashfonts() +{ + DeleteObject (version_splash_font); +} + +/* create font based on window type, charater attributes and + window device context */ +HGDIOBJ mswin_get_font(int win_type, int attr, HDC hdc, BOOL replace) +{ + HFONT fnt = NULL; + LOGFONT lgfnt; + int font_size; + int font_index; + static BOOL once = FALSE; + + if( !once ) { + once = TRUE; + atexit(font_table_cleanup); + } + + ZeroMemory( &lgfnt, sizeof(lgfnt) ); + + /* try find font in the table */ + for(font_index=0; font_index=MAXFONTS ) panic( "font table overflow!" ); + font_table_size++; + } else { + DeleteObject(font_table[font_index].hFont); + } + + font_table[font_index].code = NHFONT_CODE(win_type, attr); + font_table[font_index].hFont = fnt; + return fnt; +} + +UINT mswin_charset() +{ + CHARSETINFO cis; + if( iflags.IBMgraphics ) + if( TranslateCharsetInfo((DWORD*)GetOEMCP(), &cis, TCI_SRCCODEPAGE) ) + return cis.ciCharset; + else + return OEM_CHARSET; + else + if( TranslateCharsetInfo((DWORD*)GetACP(), &cis, TCI_SRCCODEPAGE) ) + return cis.ciCharset; + else + return ANSI_CHARSET; +} + +void __cdecl font_table_cleanup(void) +{ + int i; + for(i=0; i */ +/* NetHack may be freely redistributed. See license for details. */ + +/* font management functions */ + +#ifndef MSWINFont_h +#define MSWINFont_h + +#include "winMS.h" + +HGDIOBJ mswin_get_font(int win_type, int attr, HDC hdc, BOOL replace); +void mswin_init_splashfonts(HWND hWnd); +void mswin_destroy_splashfonts(void); +UINT mswin_charset(void); + +#endif /* MSWINFont_h */ diff --git a/win/win32/mhinput.c b/win/win32/mhinput.c new file mode 100644 index 0000000..77fd5ea --- /dev/null +++ b/win/win32/mhinput.c @@ -0,0 +1,85 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#include "winMS.h" +#include "mhinput.h" + +/* nethack input queue functions */ + +#define NH_INPUT_BUFFER_SIZE 64 + +/* as it stands right now we need only one slot + since events are processed almost the same time as + they occur but I like large round numbers */ + +static MSNHEvent nhi_input_buffer[NH_INPUT_BUFFER_SIZE]; +static int nhi_init_input = 0; +static int nhi_read_pos = 0; +static int nhi_write_pos = 0; + +/* initialize input queue */ +void mswin_nh_input_init(void) +{ + if( !nhi_init_input ) { + nhi_init_input = 1; + + ZeroMemory( nhi_input_buffer, sizeof(nhi_input_buffer) ); + nhi_read_pos = 0; + nhi_write_pos = 0; + } +} + +/* check for input */ +int mswin_have_input() +{ + return (nhi_read_pos!=nhi_write_pos); +} + +/* add event to the queue */ +void mswin_input_push(PMSNHEvent event) +{ + int new_write_pos; + + if( !nhi_init_input ) mswin_nh_input_init(); + + new_write_pos = (nhi_write_pos+1) % NH_INPUT_BUFFER_SIZE; + + if(new_write_pos!=nhi_read_pos) { + memcpy(nhi_input_buffer+nhi_write_pos, event, sizeof(*event)); + nhi_write_pos = new_write_pos; + } + +} + +/* get event from the queue and delete it */ +PMSNHEvent mswin_input_pop() +{ + PMSNHEvent retval; + + if( !nhi_init_input ) mswin_nh_input_init(); + + if( nhi_read_pos!=nhi_write_pos ) { + retval = &nhi_input_buffer[nhi_read_pos]; + nhi_read_pos = (nhi_read_pos+1) % NH_INPUT_BUFFER_SIZE; + } else { + retval = NULL; + } + + return retval; +} + +/* get event from the queue but leave it there */ +PMSNHEvent mswin_input_peek() +{ + PMSNHEvent retval; + + if( !nhi_init_input ) mswin_nh_input_init(); + + if( nhi_read_pos!=nhi_write_pos ) { + retval = &nhi_input_buffer[nhi_read_pos]; + } else { + retval = NULL; + } + return retval; +} diff --git a/win/win32/mhinput.h b/win/win32/mhinput.h new file mode 100644 index 0000000..23ed41b --- /dev/null +++ b/win/win32/mhinput.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINInput_h +#define MSWINInput_h + +/* nethack input queue - store/extract input events */ +#include "winMS.h" + +#define NHEVENT_CHAR 1 +#define NHEVENT_MOUSE 2 +typedef struct mswin_event { + int type; + union { + struct { + int ch; + } kbd; + + struct { + int mod; + int x, y; + } ms; + }; +} MSNHEvent, *PMSNHEvent; + +#define NHEVENT_KBD(c) { MSNHEvent e; e.type=NHEVENT_CHAR; e.kbd.ch=(c); mswin_input_push(&e); } +#define NHEVENT_MS(_mod, _x, _y) { MSNHEvent e; e.type=NHEVENT_MOUSE; e.ms.mod = (_mod); e.ms.x=(_x); e.ms.y=(_y); mswin_input_push(&e); } + +void mswin_nh_input_init(void); +int mswin_have_input(void); +void mswin_input_push(PMSNHEvent event); +PMSNHEvent mswin_input_pop(void); +PMSNHEvent mswin_input_peek(void); + +#endif /* MSWINInput_h */ diff --git a/win/win32/mhmain.c b/win/win32/mhmain.c new file mode 100644 index 0000000..e05d9e2 --- /dev/null +++ b/win/win32/mhmain.c @@ -0,0 +1,966 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "patchlevel.h" +#include "resource.h" +#include "mhmsg.h" +#include "mhinput.h" +#include "mhmain.h" +#include "mhmenu.h" +#include "mhstatus.h" +#include "mhmsgwnd.h" +#include "mhmap.h" + +typedef struct mswin_nethack_main_window { + int mapAcsiiModeSave; +} NHMainWindow, *PNHMainWindow; + +static TCHAR szMainWindowClass[] = TEXT("MSNHMainWndClass"); +static TCHAR szTitle[MAX_LOADSTRING]; +extern void mswin_display_splash_window(BOOL); + +LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); +static LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void register_main_window_class(void); +static int menuid2mapmode(int menuid); +static int mapmode2menuid(int map_mode); + +HWND mswin_init_main_window () { + static int run_once = 0; + HWND ret; + WINDOWPLACEMENT wp; + + /* register window class */ + if( !run_once ) { + LoadString(GetNHApp()->hApp, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + register_main_window_class( ); + run_once = 1; + } + + /* create the main window */ + ret = CreateWindow( + szMainWindowClass, /* registered class name */ + szTitle, /* window name */ + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, /* window style */ + CW_USEDEFAULT, /* horizontal position of window */ + CW_USEDEFAULT, /* vertical position of window */ + CW_USEDEFAULT, /* window width */ + CW_USEDEFAULT, /* window height */ + NULL, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL /* window-creation data */ + ); + + if( !ret ) panic("Cannot create main window"); + + + if (GetNHApp()->regMainMinX != CW_USEDEFAULT) + { + wp.length = sizeof(wp); + wp.showCmd = GetNHApp()->regMainShowState; + + wp.ptMinPosition.x = GetNHApp()->regMainMinX; + wp.ptMinPosition.y = GetNHApp()->regMainMinY; + + wp.ptMaxPosition.x = GetNHApp()->regMainMaxX; + wp.ptMaxPosition.y = GetNHApp()->regMainMaxY; + + wp.rcNormalPosition.left = GetNHApp()->regMainLeft; + wp.rcNormalPosition.top = GetNHApp()->regMainTop; + wp.rcNormalPosition.right = GetNHApp()->regMainRight; + wp.rcNormalPosition.bottom = GetNHApp()->regMainBottom; + SetWindowPlacement(ret, &wp); + } + else + ShowWindow(ret, SW_SHOWDEFAULT); + UpdateWindow(ret); + + return ret; +} + +void register_main_window_class() +{ + WNDCLASS wcex; + + ZeroMemory(&wcex, sizeof(wcex)); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = (WNDPROC)MainWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = LoadIcon(GetNHApp()->hApp, (LPCTSTR)IDI_NETHACKW); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = (TCHAR*)IDC_NETHACKW; + wcex.lpszClassName = szMainWindowClass; + + RegisterClass(&wcex); +} + +/* + * Keypad keys are translated to the normal values below. + * Shifted keypad keys are translated to the + * shift values below. + */ + +enum KEY_INDEXES { +KEY_NW, KEY_N, KEY_NE, KEY_MINUS, +KEY_W, KEY_GOINTERESTING, KEY_E, KEY_PLUS, +KEY_SW, KEY_S, KEY_SE, +KEY_INV, KEY_WAITLOOK, +KEY_LAST}; + +static const unsigned char +/* normal, shift, control */ +keypad[KEY_LAST][3] = { + {'y', 'Y', C('y')}, /* 7 */ + {'k', 'K', C('k')}, /* 8 */ + {'u', 'U', C('u')}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'h', 'H', C('h')}, /* 4 */ + {'g', 'G', 'g'}, /* 5 */ + {'l', 'L', C('l')}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'b', 'B', C('b')}, /* 1 */ + {'j', 'J', C('j')}, /* 2 */ + {'n', 'N', C('n')}, /* 3 */ + {'i', 'I', C('i')}, /* Ins */ + {'.', ':', ':'} /* Del */ +}, +numpad[KEY_LAST][3] = { + {'7', M('7'), '7'}, /* 7 */ + {'8', M('8'), '8'}, /* 8 */ + {'9', M('9'), '9'}, /* 9 */ + {'m', C('p'), C('p')}, /* - */ + {'4', M('4'), '4'}, /* 4 */ + {'5', M('5'), '5'}, /* 5 */ + {'6', M('6'), '6'}, /* 6 */ + {'+', 'P', C('p')}, /* + */ + {'1', M('1'), '1'}, /* 1 */ + {'2', M('2'), '2'}, /* 2 */ + {'3', M('3'), '3'}, /* 3 */ + {'0', M('0'), '0'}, /* Ins */ + {'.', ':', ':'} /* Del */ +}; + +#define STATEON(x) ((GetKeyState(x) & 0xFFFE) != 0) +#define KEYTABLE_REGULAR(x) ((iflags.num_pad ? numpad : keypad)[x][0]) +#define KEYTABLE_SHIFT(x) ((iflags.num_pad ? numpad : keypad)[x][1]) +#define KEYTABLE(x) (STATEON(VK_SHIFT) ? KEYTABLE_SHIFT(x) : KEYTABLE_REGULAR(x)) + +/* map mode macros */ +#define IS_MAP_FIT_TO_SCREEN(mode) ((mode)==MAP_MODE_ASCII_FIT_TO_SCREEN || \ + (mode)==MAP_MODE_TILES_FIT_TO_SCREEN ) + +#define IS_MAP_ASCII(mode) ((mode)!=MAP_MODE_TILES && (mode)!=MAP_MODE_TILES_FIT_TO_SCREEN) + +static const char *extendedlist = "acdefijlmnopqrstuvw?2"; + +#define SCANLO 0x02 +static const char scanmap[] = { /* ... */ + '1','2','3','4','5','6','7','8','9','0',0,0,0,0, + 'q','w','e','r','t','y','u','i','o','p','[',']', '\n', + 0, 'a','s','d','f','g','h','j','k','l',';','\'', '`', + 0, '\\', 'z','x','c','v','b','n','m',',','.','?' /* ... */ +}; + +/* +// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) +// +// PURPOSE: Processes messages for the main window. +*/ +LRESULT CALLBACK MainWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMainWindow data; + + switch (message) + { + case WM_CREATE: + /* set window data */ + data = (PNHMainWindow)malloc(sizeof(NHMainWindow)); + if( !data ) panic("out of memory"); + ZeroMemory(data, sizeof(NHMainWindow)); + data->mapAcsiiModeSave = MAP_MODE_ASCII12x16; + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + GetNHApp()->hMainWnd = hWnd; + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_KEYDOWN: + { + data = (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA); + + /* translate arrow keys into nethack commands */ + switch (wParam) + { + case VK_LEFT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line left */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_LINEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_W)); + } + return 0; + + case VK_RIGHT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line right */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_LINEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_E)); + } + return 0; + + case VK_UP: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line up */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_LINEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_N)); + } + return 0; + + case VK_DOWN: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one line down */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_LINEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_S)); + } + return 0; + + case VK_HOME: + if( STATEON(VK_CONTROL) ) { + /* scroll map window to upper left corner */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, 0), + (LPARAM)NULL + ); + + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_THUMBTRACK, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_NW)); + } + return 0; + + case VK_END: + if( STATEON(VK_CONTROL) ) { + /* scroll map window to lower right corner */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_THUMBTRACK, ROWNO), + (LPARAM)NULL + ); + + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_HSCROLL, + MAKEWPARAM(SB_THUMBTRACK, COLNO), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_SW)); + } + return 0; + + case VK_PRIOR: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one page up */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_PAGEUP, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_NE)); + } + return 0; + + case VK_NEXT: + if( STATEON(VK_CONTROL) ) { + /* scroll map window one page down */ + SendMessage( + mswin_hwnd_from_winid(WIN_MAP), + WM_VSCROLL, + MAKEWPARAM(SB_PAGEDOWN, 0), + (LPARAM)NULL + ); + } else { + NHEVENT_KBD(KEYTABLE(KEY_SE)); + } + return 0; + + case VK_DECIMAL: + case VK_DELETE: + NHEVENT_KBD(KEYTABLE(KEY_WAITLOOK)); + return 0; + + case VK_INSERT: + NHEVENT_KBD(KEYTABLE(KEY_INV)); + return 0; + + case VK_SUBTRACT: + NHEVENT_KBD(KEYTABLE(KEY_MINUS)); + return 0; + + case VK_ADD: + NHEVENT_KBD(KEYTABLE(KEY_PLUS)); + return 0; + + case VK_CLEAR: /* This is the '5' key */ + NHEVENT_KBD(KEYTABLE(KEY_GOINTERESTING)); + return 0; + + case VK_F4: + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + data->mapAcsiiModeSave : + MAP_MODE_TILES + ); + } else { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + MAP_MODE_ASCII_FIT_TO_SCREEN : + MAP_MODE_TILES_FIT_TO_SCREEN + ); + } + return 0; + + case VK_F5: + if( IS_MAP_ASCII(iflags.wc_map_mode) ) { + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode(MAP_MODE_TILES_FIT_TO_SCREEN); + } else { + mswin_select_map_mode(MAP_MODE_TILES); + } + } else { + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode(MAP_MODE_ASCII_FIT_TO_SCREEN); + } else { + mswin_select_map_mode(data->mapAcsiiModeSave); + } + } + return 0; + + default: { + WORD c; + BYTE kbd_state[256]; + + c = 0; + ZeroMemory(kbd_state, sizeof(kbd_state)); + GetKeyboardState(kbd_state); + + if( ToAscii( wParam, (lParam>>16)&0xFF, kbd_state, &c, 0) ) { + NHEVENT_KBD( c&0xFF ); + return 0; + } else { + return 1; + } + } + + } /* end switch */ + } break; + + case WM_SYSCHAR: /* Alt-char pressed */ + { + /* + If not nethackmode, don't handle Alt-keys here. + If no Alt-key pressed it can never be an extended command + */ + if (GetNHApp()->regNetHackMode && ((lParam & 1<<29) != 0)) + { + unsigned char c = (unsigned char)(wParam & 0xFF); + unsigned char scancode = (lParam >> 16) & 0xFF; + if (index(extendedlist, tolower(c)) != 0) + { + NHEVENT_KBD(M(tolower(c))); + } else if (scancode == (SCANLO + SIZE(scanmap)) - 1) { + NHEVENT_KBD(M('?')); + } + return 0; + } + return DefWindowProc(hWnd, message, wParam, lParam); + } + break; + + case WM_COMMAND: + /* process commands - menu commands mostly */ + if( onWMCommand(hWnd, wParam, lParam) ) + return DefWindowProc(hWnd, message, wParam, lParam); + else + return 0; + + case WM_MOVE: + case WM_SIZE: + { + WINDOWPLACEMENT wp; + + mswin_layout_main_window(NULL); + + wp.length = sizeof(wp); + if (GetWindowPlacement(hWnd, &wp)) { + GetNHApp()->regMainShowState = (wp.showCmd == SW_SHOWMAXIMIZED + ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); + + GetNHApp()->regMainMinX = wp.ptMinPosition.x; + GetNHApp()->regMainMinY = wp.ptMinPosition.y; + + GetNHApp()->regMainMaxX = wp.ptMaxPosition.x; + GetNHApp()->regMainMaxY = wp.ptMaxPosition.y; + + GetNHApp()->regMainLeft = wp.rcNormalPosition.left; + GetNHApp()->regMainTop = wp.rcNormalPosition.top; + GetNHApp()->regMainRight = wp.rcNormalPosition.right; + GetNHApp()->regMainBottom = wp.rcNormalPosition.bottom; + } + break; + } + case WM_SETFOCUS: + /* if there is a menu window out there - + transfer input focus to it */ + if( IsWindow( GetNHApp()->hPopupWnd ) ) { + SetFocus( GetNHApp()->hPopupWnd ); + } + break; + + case WM_CLOSE: + { + /* exit gracefully */ + if (program_state.gameover) + { + /* assume the user really meant this, as the game is already over... */ + /* to make sure we still save bones, just set stop printing flag */ + program_state.stopprint++; + NHEVENT_KBD('\033'); /* and send keyboard input as if user pressed ESC */ + /* additional code for this is done in menu and rip windows */ + } + else if (!program_state.something_worth_saving) + { + /* User exited before the game started, e.g. during splash display */ + /* Just get out. */ + bail((char *)0); + } + else + { + switch (NHMessageBox(hWnd, TEXT("Save?"), MB_YESNOCANCEL | MB_ICONQUESTION)) { + case IDYES: NHEVENT_KBD('y'); dosave(); break; + case IDNO: NHEVENT_KBD('q'); done(QUIT); break; + case IDCANCEL: break; + } + } + } return 0; + + case WM_DESTROY: + /* apparently we never get here + TODO: work on exit routines - need to send + WM_QUIT somehow */ + + /* clean up */ + free( (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA) ); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + + // PostQuitMessage(0); + exit(1); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + switch(wParam) { + + /* new window was just added */ + case MSNH_MSG_ADDWND: { + PMSNHMsgAddWnd msg_param = (PMSNHMsgAddWnd)lParam; + HWND child; + + if( GetNHApp()->windowlist[msg_param->wid].type == NHW_MAP ) + mswin_select_map_mode(iflags.wc_map_mode); + + child = GetNHApp()->windowlist[msg_param->wid].win; + if( child ) mswin_layout_main_window(child); + } break; + + } +} + +/* adjust windows to fit main window layout + --------------------------- + | Status | + +-------------------------+ + | | + | | + | MAP | + | | + | | + +-------------------------+ + | Messages | + --------------------------- +*/ +void mswin_layout_main_window(HWND changed_child) +{ + winid i; + POINT pt; + RECT client_rt, wnd_rect; + SIZE menu_size; + POINT status_org; + SIZE status_size; + POINT msg_org; + SIZE msg_size; + POINT map_org; + SIZE map_size; + HWND wnd_status, wnd_msg; + PNHMainWindow data; + + GetClientRect(GetNHApp()->hMainWnd, &client_rt); + data = (PNHMainWindow)GetWindowLong(GetNHApp()->hMainWnd, GWL_USERDATA); + + /* get sizes of child windows */ + wnd_status = mswin_hwnd_from_winid(WIN_STATUS); + if( IsWindow(wnd_status) ) { + mswin_status_window_size(wnd_status, &status_size); + } else { + status_size.cx = status_size.cy = 0; + } + + wnd_msg = mswin_hwnd_from_winid(WIN_MESSAGE); + if( IsWindow(wnd_msg) ) { + mswin_message_window_size(wnd_msg, &msg_size); + } else { + msg_size.cx = msg_size.cy = 0; + } + + /* set window positions */ + SetRect(&wnd_rect, client_rt.left, client_rt.top, client_rt.right, client_rt.bottom); + switch(iflags.wc_align_status) { + case ALIGN_LEFT: + status_size.cx = (wnd_rect.right-wnd_rect.left)/4; + status_size.cy = (wnd_rect.bottom-wnd_rect.top); // that won't look good + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.top; + wnd_rect.left += status_size.cx; + break; + + case ALIGN_RIGHT: + status_size.cx = (wnd_rect.right-wnd_rect.left)/4; + status_size.cy = (wnd_rect.bottom-wnd_rect.top); // that won't look good + status_org.x = wnd_rect.right - status_size.cx; + status_org.y = wnd_rect.top; + wnd_rect.right -= status_size.cx; + break; + + case ALIGN_TOP: + status_size.cx = (wnd_rect.right-wnd_rect.left); + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.top; + wnd_rect.top += status_size.cy; + break; + + case ALIGN_BOTTOM: + default: + status_size.cx = (wnd_rect.right-wnd_rect.left); + status_org.x = wnd_rect.left; + status_org.y = wnd_rect.bottom - status_size.cy; + wnd_rect.bottom -= status_size.cy; + break; + } + + switch(iflags.wc_align_message) { + case ALIGN_LEFT: + msg_size.cx = (wnd_rect.right-wnd_rect.left)/4; + msg_size.cy = (wnd_rect.bottom-wnd_rect.top); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.top; + wnd_rect.left += msg_size.cx; + break; + + case ALIGN_RIGHT: + msg_size.cx = (wnd_rect.right-wnd_rect.left)/4; + msg_size.cy = (wnd_rect.bottom-wnd_rect.top); + msg_org.x = wnd_rect.right - msg_size.cx; + msg_org.y = wnd_rect.top; + wnd_rect.right -= msg_size.cx; + break; + + case ALIGN_TOP: + msg_size.cx = (wnd_rect.right-wnd_rect.left); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.top; + wnd_rect.top += msg_size.cy; + break; + + case ALIGN_BOTTOM: + default: + msg_size.cx = (wnd_rect.right-wnd_rect.left); + msg_org.x = wnd_rect.left; + msg_org.y = wnd_rect.bottom - msg_size.cy; + wnd_rect.bottom -= msg_size.cy; + break; + } + + map_org.x = wnd_rect.left; + map_org.y = wnd_rect.top; + map_size.cx = wnd_rect.right - wnd_rect.left; + map_size.cy = wnd_rect.bottom - wnd_rect.top; + + /* go through the windows list and adjust sizes */ + for( i=0; iwindowlist[i].win && !GetNHApp()->windowlist[i].dead) { + switch( GetNHApp()->windowlist[i].type ) { + case NHW_STATUS: + MoveWindow(GetNHApp()->windowlist[i].win, + status_org.x, + status_org.y, + status_size.cx, + status_size.cy, + TRUE ); + break; + + case NHW_TEXT: // same as the map window + case NHW_MAP: + MoveWindow(GetNHApp()->windowlist[i].win, + map_org.x, + map_org.y, + map_size.cx, + map_size.cy, + TRUE ); + break; + + case NHW_MESSAGE: + MoveWindow(GetNHApp()->windowlist[i].win, + msg_org.x, + msg_org.y, + msg_size.cx, + msg_size.cy, + TRUE ); + break; + + case NHW_MENU: + mswin_menu_window_size(GetNHApp()->windowlist[i].win, &menu_size); + menu_size.cx = min(menu_size.cx, (client_rt.right-client_rt.left)); + + pt.x = map_org.x + max(0, (int)(map_size.cx-menu_size.cx)); + pt.y = map_org.y; + MoveWindow(GetNHApp()->windowlist[i].win, + pt.x, + pt.y, + min(menu_size.cx, map_size.cx), + map_size.cy, + TRUE ); + break; + } + ShowWindow(GetNHApp()->windowlist[i].win, SW_SHOW); + } + } +} + +LRESULT onWMCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + int wmId, wmEvent; + PNHMainWindow data; + + data = (PNHMainWindow)GetWindowLong(hWnd, GWL_USERDATA); + wmId = LOWORD(wParam); + wmEvent = HIWORD(wParam); + + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + mswin_display_splash_window(TRUE); + break; + + case IDM_EXIT: + done2(); + break; + + case IDM_SAVE: + if (!program_state.gameover && !program_state.done_hup) dosave(); + else MessageBeep(0); + break; + + case IDM_MAP_TILES: + case IDM_MAP_ASCII4X6: + case IDM_MAP_ASCII6X8: + case IDM_MAP_ASCII8X8: + case IDM_MAP_ASCII16X8: + case IDM_MAP_ASCII7X12: + case IDM_MAP_ASCII8X12: + case IDM_MAP_ASCII12X16: + case IDM_MAP_ASCII16X12: + case IDM_MAP_ASCII10X18: + mswin_select_map_mode(menuid2mapmode(wmId)); + break; + + case IDM_MAP_FIT_TO_SCREEN: + if( IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode) ) { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + data->mapAcsiiModeSave : + MAP_MODE_TILES + ); + } else { + mswin_select_map_mode( + IS_MAP_ASCII(iflags.wc_map_mode)? + MAP_MODE_ASCII_FIT_TO_SCREEN : + MAP_MODE_TILES_FIT_TO_SCREEN + ); + } + break; + + case IDM_NHMODE: + { + GetNHApp()->regNetHackMode = GetNHApp()->regNetHackMode ? 0 : 1; + mswin_menu_check_intf_mode(); + break; + } + case IDM_CLEARSETTINGS: + { + mswin_destroy_reg(); + /* Notify the user that windows settings will not be saved this time. */ + NHMessageBox(GetNHApp()->hMainWnd, + "Your Windows Settings will not be stored when you exit this time.", + MB_OK | MB_ICONINFORMATION); + break; + } + case IDM_HELP_LONG: + display_file(HELP, TRUE); + break; + + case IDM_HELP_COMMANDS: + display_file(SHELP, TRUE); + break; + + case IDM_HELP_HISTORY: + (void) dohistory(); + break; + + case IDM_HELP_INFO_CHAR: + (void) dowhatis(); + break; + + case IDM_HELP_INFO_KEY: + (void) dowhatdoes(); + break; + + case IDM_HELP_OPTIONS: + option_help(); + break; + + case IDM_HELP_OPTIONS_LONG: + display_file(OPTIONFILE, TRUE); + break; + + case IDM_HELP_EXTCMD: + (void) doextlist(); + break; + + case IDM_HELP_LICENSE: + display_file(LICENSE, TRUE); + break; + + case IDM_HELP_PORTHELP: + display_file(PORT_HELP, TRUE); + break; + + default: + return 1; + } + return 0; +} + +// Mesage handler for about box. +LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char buf[BUFSZ]; + TCHAR wbuf[BUFSZ]; + RECT main_rt, dlg_rt; + SIZE dlg_sz; + + switch (message) + { + case WM_INITDIALOG: + getversionstring(buf); + SetDlgItemText(hDlg, IDC_ABOUT_VERSION, NH_A2W(buf, wbuf, sizeof(wbuf))); + + SetDlgItemText(hDlg, IDC_ABOUT_COPYRIGHT, + NH_A2W( + COPYRIGHT_BANNER_A "\n" + COPYRIGHT_BANNER_B "\n" + COPYRIGHT_BANNER_C, + wbuf, + BUFSZ + ) ); + + + /* center dialog in the main window */ + GetWindowRect(GetNHApp()->hMainWnd, &main_rt); + GetWindowRect(hDlg, &dlg_rt); + dlg_sz.cx = dlg_rt.right - dlg_rt.left; + dlg_sz.cy = dlg_rt.bottom - dlg_rt.top; + + dlg_rt.left = (main_rt.left+main_rt.right-dlg_sz.cx)/2; + dlg_rt.right = dlg_rt.left + dlg_sz.cx; + dlg_rt.top = (main_rt.top+main_rt.bottom-dlg_sz.cy)/2; + dlg_rt.bottom = dlg_rt.top + dlg_sz.cy; + MoveWindow( hDlg, + (main_rt.left+main_rt.right-dlg_sz.cx)/2, + (main_rt.top+main_rt.bottom-dlg_sz.cy)/2, + dlg_sz.cx, + dlg_sz.cy, + TRUE ); + + return TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return TRUE; + } + break; + } + return FALSE; +} + +void mswin_menu_check_intf_mode() +{ + HMENU hMenu = GetMenu(GetNHApp()->hMainWnd); + + if (GetNHApp()->regNetHackMode) + CheckMenuItem(hMenu, IDM_NHMODE, MF_CHECKED); + else + CheckMenuItem(hMenu, IDM_NHMODE, MF_UNCHECKED); + +} + +void mswin_select_map_mode(int mode) +{ + PNHMainWindow data; + winid map_id; + + map_id = WIN_MAP; + data = (PNHMainWindow)GetWindowLong(GetNHApp()->hMainWnd, GWL_USERDATA); + + /* override for Rogue level */ +#ifdef REINCARNATION + if( Is_rogue_level(&u.uz) && !IS_MAP_ASCII(mode) ) return; +#endif + + /* set map mode menu mark */ + if( IS_MAP_ASCII(mode) ) { + CheckMenuRadioItem( + GetMenu(GetNHApp()->hMainWnd), + IDM_MAP_TILES, + IDM_MAP_ASCII10X18, + mapmode2menuid( IS_MAP_FIT_TO_SCREEN(mode)? data->mapAcsiiModeSave : mode ), + MF_BYCOMMAND); + } else { + CheckMenuRadioItem( + GetMenu(GetNHApp()->hMainWnd), + IDM_MAP_TILES, + IDM_MAP_ASCII10X18, + mapmode2menuid( MAP_MODE_TILES ), + MF_BYCOMMAND); + } + + /* set fit-to-screen mode mark */ + CheckMenuItem( + GetMenu(GetNHApp()->hMainWnd), + IDM_MAP_FIT_TO_SCREEN, + MF_BYCOMMAND | + (IS_MAP_FIT_TO_SCREEN(mode)? MF_CHECKED : MF_UNCHECKED) + ); + + if( IS_MAP_ASCII(iflags.wc_map_mode) && !IS_MAP_FIT_TO_SCREEN(iflags.wc_map_mode)) { + data->mapAcsiiModeSave = iflags.wc_map_mode; + } + + iflags.wc_map_mode = mode; + + /* + ** first, check if WIN_MAP has been inialized. + ** If not - attempt to retrieve it by type, then check it again + */ + if( map_id==WIN_ERR ) + map_id = mswin_winid_from_type(NHW_MAP); + if( map_id!=WIN_ERR ) + mswin_map_mode(mswin_hwnd_from_winid(map_id), mode); +} + +static struct t_menu2mapmode { + int menuID; + int mapMode; +} _menu2mapmode[] = +{ + { IDM_MAP_TILES, MAP_MODE_TILES }, + { IDM_MAP_ASCII4X6, MAP_MODE_ASCII4x6 }, + { IDM_MAP_ASCII6X8, MAP_MODE_ASCII6x8 }, + { IDM_MAP_ASCII8X8, MAP_MODE_ASCII8x8 }, + { IDM_MAP_ASCII16X8, MAP_MODE_ASCII16x8 }, + { IDM_MAP_ASCII7X12, MAP_MODE_ASCII7x12 }, + { IDM_MAP_ASCII8X12, MAP_MODE_ASCII8x12 }, + { IDM_MAP_ASCII12X16, MAP_MODE_ASCII12x16 }, + { IDM_MAP_ASCII16X12, MAP_MODE_ASCII16x12 }, + { IDM_MAP_ASCII10X18, MAP_MODE_ASCII10x18 }, + { IDM_MAP_FIT_TO_SCREEN, MAP_MODE_ASCII_FIT_TO_SCREEN }, + { -1, -1 } +}; + +int menuid2mapmode(int menuid) +{ + struct t_menu2mapmode* p; + for( p = _menu2mapmode; p->mapMode!=-1; p++ ) + if(p->menuID==menuid ) return p->mapMode; + return -1; +} + +int mapmode2menuid(int map_mode) +{ + struct t_menu2mapmode* p; + for( p = _menu2mapmode; p->mapMode!=-1; p++ ) + if(p->mapMode==map_mode ) return p->menuID; + return -1; +} diff --git a/win/win32/mhmain.h b/win/win32/mhmain.h new file mode 100644 index 0000000..3f40714 --- /dev/null +++ b/win/win32/mhmain.h @@ -0,0 +1,16 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMainWindow_h +#define MSWINMainWindow_h + +/* this is a main appliation window */ + +#include "winMS.h" + +HWND mswin_init_main_window (void); +void mswin_layout_main_window(HWND changed_child); +void mswin_select_map_mode(int map_mode); +void mswin_menu_check_intf_mode(void); + +#endif /* MSWINMainWindow_h */ diff --git a/win/win32/mhmap.c b/win/win32/mhmap.c new file mode 100644 index 0000000..de0f8b3 --- /dev/null +++ b/win/win32/mhmap.c @@ -0,0 +1,976 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "resource.h" +#include "mhmap.h" +#include "mhmsg.h" +#include "mhinput.h" +#include "mhfont.h" + +#include "patchlevel.h" + +#define NHMAP_FONT_NAME TEXT("Terminal") +#define MAXWINDOWTEXT 255 + +extern short glyph2tile[]; + +/* map window data */ +typedef struct mswin_nethack_map_window { + int map[COLNO][ROWNO]; /* glyph map */ + + int mapMode; /* current map mode */ + boolean bAsciiMode; /* switch ASCII/tiled mode */ + boolean bFitToScreenMode; /* switch Fit map to screen mode on/off */ + int xPos, yPos; /* scroll position */ + int xPageSize, yPageSize; /* scroll page size */ + int xCur, yCur; /* position of the cursor */ + int xScrTile, yScrTile; /* size of display tile */ + POINT map_orig; /* map origin point */ + + HFONT hMapFont; /* font for ASCII mode */ +} NHMapWindow, *PNHMapWindow; + +static TCHAR szNHMapWindowClass[] = TEXT("MSNethackMapWndClass"); +LRESULT CALLBACK MapWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_map_window_class(void); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onPaint(HWND hWnd); +static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut); +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) +static void nhglyph2charcolor(short glyph, uchar* ch, int* color); +#endif +static COLORREF nhcolor_to_RGB(int c); + +HWND mswin_init_map_window () { + static int run_once = 0; + HWND ret; + + if( !run_once ) { + register_map_window_class(); + run_once = 1; + } + + ret = CreateWindow( + szNHMapWindowClass, /* registered class name */ + NULL, /* window name */ + WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_CLIPSIBLINGS, /* window style */ + 0, /* horizontal position of window - set it later */ + 0, /* vertical position of window - set it later */ + 0, /* window width - set it later */ + 0, /* window height - set it later*/ + GetNHApp()->hMainWnd, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL ); /* window-creation data */ + if( !ret ) { + panic("Cannot create map window"); + } + return ret; +} + +void mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw) +{ + PNHMapWindow data; + RECT client_rt; + SCROLLINFO si; + SIZE wnd_size; + LOGFONT lgfnt; + + /* check arguments */ + if( !IsWindow(hWnd) || + !lpsz || + lpsz->cx<=0 || + lpsz->cy<=0 ) return; + + /* calculate window size */ + GetClientRect(hWnd, &client_rt); + wnd_size.cx = client_rt.right - client_rt.left; + wnd_size.cy = client_rt.bottom - client_rt.top; + + /* set new screen tile size */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + data->xScrTile = + max(1, (data->bFitToScreenMode? wnd_size.cx : lpsz->cx) / COLNO); + data->yScrTile = + max(1, (data->bFitToScreenMode? wnd_size.cy : lpsz->cy) / ROWNO); + + /* set map origin point */ + data->map_orig.x = max(0, client_rt.left + (wnd_size.cx - data->xScrTile*COLNO)/2 ); + data->map_orig.y = max(0, client_rt.top + (wnd_size.cy - data->yScrTile*ROWNO)/2 ); + + data->map_orig.x -= data->map_orig.x % data->xScrTile; + data->map_orig.y -= data->map_orig.y % data->yScrTile; + + /* adjust horizontal scroll bar */ + if( data->bFitToScreenMode ) + data->xPageSize = COLNO+1; /* disable scroll bar */ + else + data->xPageSize = wnd_size.cx/data->xScrTile; + + if( data->xPageSize >= COLNO ) { + data->xPos = 0; + GetNHApp()->bNoHScroll = TRUE; + } else { + GetNHApp()->bNoHScroll = FALSE; + data->xPos = max(0, min(COLNO-data->xPageSize+1, u.ux - data->xPageSize/2)); + } + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = COLNO; + si.nPage = data->xPageSize; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + + /* adjust vertical scroll bar */ + if( data->bFitToScreenMode ) + data->yPageSize = ROWNO+1; /* disable scroll bar */ + else + data->yPageSize = wnd_size.cy/data->yScrTile; + + if( data->yPageSize >= ROWNO ) { + data->yPos = 0; + GetNHApp()->bNoVScroll = TRUE; + } else { + GetNHApp()->bNoVScroll = FALSE; + data->yPos = max(0, min(ROWNO-data->yPageSize+1, u.uy - data->yPageSize/2)); + } + + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = ROWNO; + si.nPage = data->yPageSize; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + /* create font */ + if( data->hMapFont ) DeleteObject(data->hMapFont); + ZeroMemory(&lgfnt, sizeof(lgfnt)); + lgfnt.lfHeight = -data->yScrTile; // height of font + lgfnt.lfWidth = -data->xScrTile; // average character width + lgfnt.lfEscapement = 0; // angle of escapement + lgfnt.lfOrientation = 0; // base-line orientation angle + lgfnt.lfWeight = FW_NORMAL; // font weight + lgfnt.lfItalic = FALSE; // italic attribute option + lgfnt.lfUnderline = FALSE; // underline attribute option + lgfnt.lfStrikeOut = FALSE; // strikeout attribute option + lgfnt.lfCharSet = mswin_charset(); // character set identifier + lgfnt.lfOutPrecision = OUT_DEFAULT_PRECIS; // output precision + lgfnt.lfClipPrecision = CLIP_DEFAULT_PRECIS; // clipping precision + lgfnt.lfQuality = DEFAULT_QUALITY; // output quality + if( iflags.wc_font_map && + *iflags.wc_font_map ) { + lgfnt.lfPitchAndFamily = DEFAULT_PITCH; // pitch and family + NH_A2W(iflags.wc_font_map, lgfnt.lfFaceName, LF_FACESIZE); + } else { + lgfnt.lfPitchAndFamily = FIXED_PITCH; // pitch and family + NH_A2W(NHMAP_FONT_NAME, lgfnt.lfFaceName, LF_FACESIZE); + } + data->hMapFont = CreateFontIndirect(&lgfnt); + + mswin_cliparound(data->xCur, data->yCur); + + if(redraw) InvalidateRect(hWnd, NULL, TRUE); +} + +/* set map mode */ +int mswin_map_mode(HWND hWnd, int mode) +{ + PNHMapWindow data; + int oldMode; + SIZE mapSize; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( mode == data->mapMode ) return mode; + + oldMode = data->mapMode; + data->mapMode = mode; + + switch( data->mapMode ) { + + case MAP_MODE_ASCII4x6: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 4*COLNO; + mapSize.cy = 6*ROWNO; + break; + + case MAP_MODE_ASCII6x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 6*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII8x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 8*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII16x8: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 16*COLNO; + mapSize.cy = 8*ROWNO; + break; + + case MAP_MODE_ASCII7x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 7*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII8x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 8*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII16x12: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 16*COLNO; + mapSize.cy = 12*ROWNO; + break; + + case MAP_MODE_ASCII12x16: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 12*COLNO; + mapSize.cy = 16*ROWNO; + break; + + case MAP_MODE_ASCII10x18: + data->bAsciiMode = TRUE; + data->bFitToScreenMode = FALSE; + mapSize.cx = 10*COLNO; + mapSize.cy = 18*ROWNO; + break; + + case MAP_MODE_ASCII_FIT_TO_SCREEN: { + RECT client_rt; + GetClientRect(hWnd, &client_rt); + mapSize.cx = client_rt.right - client_rt.left; + mapSize.cy = client_rt.bottom - client_rt.top; + + data->bAsciiMode = TRUE; + data->bFitToScreenMode = TRUE; + } break; + + case MAP_MODE_TILES_FIT_TO_SCREEN: { + RECT client_rt; + GetClientRect(hWnd, &client_rt); + mapSize.cx = client_rt.right - client_rt.left; + mapSize.cy = client_rt.bottom - client_rt.top; + + data->bAsciiMode = FALSE; + data->bFitToScreenMode = TRUE; + } break; + + case MAP_MODE_TILES: + default: + data->bAsciiMode = FALSE; + data->bFitToScreenMode = FALSE; + mapSize.cx = GetNHApp()->mapTile_X*COLNO; + mapSize.cy = GetNHApp()->mapTile_Y*ROWNO; + break; + } + + mswin_map_stretch(hWnd, &mapSize, TRUE); + + return oldMode; +} + +/* register window class for map window */ +void register_map_window_class() +{ + WNDCLASS wcex; + ZeroMemory( &wcex, sizeof(wcex)); + + /* window class */ + wcex.style = CS_NOCLOSE | CS_DBLCLKS; + wcex.lpfnWndProc = (WNDPROC)MapWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = CreateSolidBrush(RGB(0, 0, 0)); /* set backgroup here */ + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szNHMapWindowClass; + + if( !RegisterClass(&wcex) ) { + panic("cannot register Map window class"); + } +} + +/* map window procedure */ +LRESULT CALLBACK MapWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_CREATE: + onCreate( hWnd, wParam, lParam ); + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_PAINT: + onPaint(hWnd); + break; + + case WM_SETFOCUS: + /* transfer focus back to the main window */ + SetFocus(GetNHApp()->hMainWnd); + break; + + case WM_HSCROLL: + onMSNH_HScroll(hWnd, wParam, lParam); + break; + + case WM_VSCROLL: + onMSNH_VScroll(hWnd, wParam, lParam); + break; + + case WM_SIZE: + { + SIZE size; + + if( data->bFitToScreenMode ) { + size.cx = LOWORD(lParam); + size.cy = HIWORD(lParam); + } else { + /* mapping factor is unchaged we just need to adjust scroll bars */ + size.cx = data->xScrTile*COLNO; + size.cy = data->yScrTile*ROWNO; + } + mswin_map_stretch(hWnd, &size, TRUE); + } + break; + + case WM_LBUTTONDOWN: + NHEVENT_MS( + CLICK_1, + max(0, min(COLNO, data->xPos + (LOWORD(lParam)-data->map_orig.x)/data->xScrTile)), + max(0, min(ROWNO, data->yPos + (HIWORD(lParam)-data->map_orig.y)/data->yScrTile)) + ); + return 0; + + case WM_LBUTTONDBLCLK : + NHEVENT_MS( + CLICK_2, + max(0, min(COLNO, data->xPos + (LOWORD(lParam)-data->map_orig.x)/data->xScrTile)), + max(0, min(ROWNO, data->yPos + (HIWORD(lParam)-data->map_orig.y)/data->yScrTile)) + ); + return 0; + + case WM_DESTROY: + if( data->hMapFont ) DeleteObject(data->hMapFont); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +/* on WM_COMMAND */ +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + RECT rt; + + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch(wParam) { + case MSNH_MSG_PRINT_GLYPH: + { + PMSNHMsgPrintGlyph msg_data = (PMSNHMsgPrintGlyph)lParam; + data->map[msg_data->x][msg_data->y] = msg_data->glyph; + + /* invalidate the update area */ + nhcoord2display(data, msg_data->x, msg_data->y, &rt); + InvalidateRect(hWnd, &rt, TRUE); + } + break; + + case MSNH_MSG_CLIPAROUND: + { + PMSNHMsgClipAround msg_data = (PMSNHMsgClipAround)lParam; + int x, y; + BOOL scroll_x, scroll_y; + int mcam = iflags.wc_scroll_margin; + + /* calculate if you should clip around */ + scroll_x = + !GetNHApp()->bNoHScroll && + ( msg_data->x<(data->xPos+mcam) || + msg_data->x>(data->xPos+data->xPageSize-mcam) ); + scroll_y = + !GetNHApp()->bNoVScroll && + ( msg_data->y<(data->yPos+mcam) || + msg_data->y>(data->yPos+data->yPageSize-mcam) ); + + mcam += iflags.wc_scroll_amount - 1; + /* get page size and center horizontally on x-position */ + if( scroll_x ) { + if( data->xPageSize<=2*mcam ) { + x = max(0, min(COLNO, msg_data->x - data->xPageSize/2)); + } else if( msg_data->x < data->xPos+data->xPageSize/2 ) { + x = max(0, min(COLNO, msg_data->x - mcam)); + } else { + x = max(0, min(COLNO, msg_data->x - data->xPageSize + mcam)); + } + SendMessage( hWnd, WM_HSCROLL, (WPARAM)MAKELONG(SB_THUMBTRACK, x), (LPARAM)NULL ); + } + + /* get page size and center vertically on y-position */ + if( scroll_y ) { + if( data->yPageSize<=2*mcam ) { + y = max(0, min(ROWNO, msg_data->y - data->yPageSize/2)); + } else if( msg_data->y < data->yPos+data->yPageSize/2 ) { + y = max(0, min(ROWNO, msg_data->y - mcam)); + } else { + y = max(0, min(ROWNO, msg_data->y - data->yPageSize + mcam)); + } + SendMessage( hWnd, WM_VSCROLL, (WPARAM)MAKELONG(SB_THUMBTRACK, y), (LPARAM)NULL ); + } + } + break; + + case MSNH_MSG_CLEAR_WINDOW: + { + int i, j; + for(i=0; imap[i][j] = -1; + } + InvalidateRect(hWnd, NULL, TRUE); + } break; + + case MSNH_MSG_CURSOR: + { + PMSNHMsgCursor msg_data = (PMSNHMsgCursor)lParam; + HDC hdc; + RECT rt; + + /* move focus rectangle at the cursor postion */ + hdc = GetDC(hWnd); + + nhcoord2display(data, data->xCur, data->yCur, &rt); + if( data->bAsciiMode ) { + PatBlt(hdc, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, DSTINVERT); + } else { + DrawFocusRect(hdc, &rt); + } + + data->xCur = msg_data->x; + data->yCur = msg_data->y; + + nhcoord2display(data, data->xCur, data->yCur, &rt); + if( data->bAsciiMode ) { + PatBlt(hdc, rt.left, rt.top, rt.right-rt.left, rt.bottom-rt.top, DSTINVERT); + } else { + DrawFocusRect(hdc, &rt); + } + + ReleaseDC(hWnd, hdc); + } break; + } +} + +/* on WM_CREATE */ +void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + int i,j; + + /* set window data */ + data = (PNHMapWindow)malloc(sizeof(NHMapWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHMapWindow)); + for(i=0; imap[i][j] = -1; + } + + data->bAsciiMode = FALSE; + + data->xScrTile = GetNHApp()->mapTile_X; + data->yScrTile = GetNHApp()->mapTile_Y; + + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); +} + +/* on WM_PAINT */ +void onPaint(HWND hWnd) +{ + PNHMapWindow data; + PAINTSTRUCT ps; + HDC hDC; + HDC tileDC; + HGDIOBJ saveBmp; + RECT paint_rt; + int i, j; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + hDC = BeginPaint(hWnd, &ps); + + /* calculate paint rectangle */ + if( !IsRectEmpty(&ps.rcPaint) ) { + /* calculate paint rectangle */ + paint_rt.left = max(data->xPos + (ps.rcPaint.left - data->map_orig.x)/data->xScrTile, 0); + paint_rt.top = max(data->yPos + (ps.rcPaint.top - data->map_orig.y)/data->yScrTile, 0); + paint_rt.right = min(data->xPos + (ps.rcPaint.right - data->map_orig.x)/data->xScrTile+1, COLNO); + paint_rt.bottom = min(data->yPos + (ps.rcPaint.bottom - data->map_orig.y)/data->yScrTile+1, ROWNO); + + if( data->bAsciiMode +#ifdef REINCARNATION + || Is_rogue_level(&u.uz) + /* You enter a VERY primitive world! */ +#endif + ) { + HGDIOBJ oldFont; + + oldFont = SelectObject(hDC, data->hMapFont); + SetBkMode(hDC, TRANSPARENT); + + /* draw the map */ + for(i=paint_rt.left; imap[i][j]>=0) { + char ch; + TCHAR wch; + RECT glyph_rect; + int color; + unsigned special; + int mgch; + HBRUSH back_brush; + COLORREF OldFg; + + nhcoord2display(data, i, j, &glyph_rect); + +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) + nhglyph2charcolor(data->map[i][j], &ch, &color); + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); +#else + /* rely on NetHack core helper routine */ + mapglyph(data->map[i][j], &mgch, &color, + &special, i, j); + ch = (char)mgch; + if (((special & MG_PET) && iflags.hilite_pet) || + ((special & MG_DETECT) && iflags.use_inverse)) { + back_brush = CreateSolidBrush(nhcolor_to_RGB(CLR_GRAY)); + FillRect (hDC, &glyph_rect, back_brush); + DeleteObject (back_brush); + switch (color) + { + case CLR_GRAY: + case CLR_WHITE: + OldFg = SetTextColor( hDC, nhcolor_to_RGB(CLR_BLACK)); + break; + default: + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); + } + } else { + OldFg = SetTextColor (hDC, nhcolor_to_RGB(color) ); + } +#endif + + DrawText(hDC, + NH_A2W(&ch, &wch, 1), + 1, + &glyph_rect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX + ); + SetTextColor (hDC, OldFg); + } + SelectObject(hDC, oldFont); + } else { + /* prepare tiles DC for mapping */ + tileDC = CreateCompatibleDC(hDC); + saveBmp = SelectObject(tileDC, GetNHApp()->bmpMapTiles); + + /* draw the map */ + for(i=paint_rt.left; imap[i][j]>=0) { + short ntile; + int t_x, t_y; + RECT glyph_rect; + + ntile = glyph2tile[ data->map[i][j] ]; + t_x = (ntile % GetNHApp()->mapTilesPerLine)*GetNHApp()->mapTile_X; + t_y = (ntile / GetNHApp()->mapTilesPerLine)*GetNHApp()->mapTile_Y; + + nhcoord2display(data, i, j, &glyph_rect); + + StretchBlt( + hDC, + glyph_rect.left, + glyph_rect.top, + data->xScrTile, + data->yScrTile, + tileDC, + t_x, + t_y, + GetNHApp()->mapTile_X, + GetNHApp()->mapTile_Y, + SRCCOPY + ); + if( glyph_is_pet(data->map[i][j]) && iflags.wc_hilite_pet ) { + /* apply pet mark transparently over + pet image */ + HDC hdcPetMark; + HBITMAP bmPetMarkOld; + + /* this is DC for petmark bitmap */ + hdcPetMark = CreateCompatibleDC(hDC); + bmPetMarkOld = SelectObject(hdcPetMark, GetNHApp()->bmpPetMark); + + nhapply_image_transparent( + hDC, + glyph_rect.left, + glyph_rect.top, + data->xScrTile, + data->yScrTile, + hdcPetMark, + 0, + 0, + TILE_X, + TILE_Y, + TILE_BK_COLOR + ); + SelectObject(hdcPetMark, bmPetMarkOld); + DeleteDC(hdcPetMark); + } + } + SelectObject(tileDC, saveBmp); + DeleteDC(tileDC); + } + + /* draw focus rect */ + nhcoord2display(data, data->xCur, data->yCur, &paint_rt); + if( data->bAsciiMode ) { + PatBlt( hDC, + paint_rt.left, paint_rt.top, + paint_rt.right-paint_rt.left, paint_rt.bottom-paint_rt.top, + DSTINVERT ); + } else { + DrawFocusRect(hDC, &paint_rt); + } + } + EndPaint(hWnd, &ps); +} + +/* on WM_VSCROLL */ +void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + SCROLLINFO si; + int yNewPos; + int yDelta; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch(LOWORD (wParam)) + { + /* User clicked shaft left of the scroll box. */ + case SB_PAGEUP: + yNewPos = data->yPos-data->yPageSize; + break; + + /* User clicked shaft right of the scroll box. */ + case SB_PAGEDOWN: + yNewPos = data->yPos+data->yPageSize; + break; + + /* User clicked the left arrow. */ + case SB_LINEUP: + yNewPos = data->yPos-1; + break; + + /* User clicked the right arrow. */ + case SB_LINEDOWN: + yNewPos = data->yPos+1; + break; + + /* User dragged the scroll box. */ + case SB_THUMBTRACK: + yNewPos = HIWORD(wParam); + break; + + default: + yNewPos = data->yPos; + } + + yNewPos = max(0, min(ROWNO-data->yPageSize+1, yNewPos)); + if( yNewPos == data->yPos ) return; + + yDelta = yNewPos - data->yPos; + data->yPos = yNewPos; + + ScrollWindowEx (hWnd, 0, -data->yScrTile * yDelta, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); +} + +/* on WM_HSCROLL */ +void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMapWindow data; + SCROLLINFO si; + int xNewPos; + int xDelta; + + /* get window data */ + data = (PNHMapWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch(LOWORD (wParam)) + { + /* User clicked shaft left of the scroll box. */ + case SB_PAGEUP: + xNewPos = data->xPos-data->xPageSize; + break; + + /* User clicked shaft right of the scroll box. */ + case SB_PAGEDOWN: + xNewPos = data->xPos+data->xPageSize; + break; + + /* User clicked the left arrow. */ + case SB_LINEUP: + xNewPos = data->xPos-1; + break; + + /* User clicked the right arrow. */ + case SB_LINEDOWN: + xNewPos = data->xPos+1; + break; + + /* User dragged the scroll box. */ + case SB_THUMBTRACK: + xNewPos = HIWORD(wParam); + break; + + default: + xNewPos = data->xPos; + } + + xNewPos = max(0, min(COLNO-data->xPageSize+1, xNewPos)); + if( xNewPos == data->xPos ) return; + + xDelta = xNewPos - data->xPos; + data->xPos = xNewPos; + + ScrollWindowEx (hWnd, -data->xScrTile * xDelta, 0, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); +} + +/* map nethack map coordinates to the screen location */ +void nhcoord2display(PNHMapWindow data, int x, int y, LPRECT lpOut) +{ + lpOut->left = (x - data->xPos)*data->xScrTile + data->map_orig.x; + lpOut->top = (y - data->yPos)*data->yScrTile + data->map_orig.y; + lpOut->right = lpOut->left + data->xScrTile; + lpOut->bottom = lpOut->top + data->yScrTile; +} + +#if (VERSION_MAJOR < 4) && (VERSION_MINOR < 4) && (PATCHLEVEL < 2) +/* map glyph to character/color combination */ +void nhglyph2charcolor(short g, uchar* ch, int* color) +{ + int offset; +#ifdef TEXTCOLOR + +#define zap_color(n) *color = iflags.use_color ? zapcolors[n] : NO_COLOR +#define cmap_color(n) *color = iflags.use_color ? defsyms[n].color : NO_COLOR +#define obj_color(n) *color = iflags.use_color ? objects[n].oc_color : NO_COLOR +#define mon_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define pet_color(n) *color = iflags.use_color ? mons[n].mcolor : NO_COLOR +#define warn_color(n) *color = iflags.use_color ? def_warnsyms[n].color : NO_COLOR + +# else /* no text color */ + +#define zap_color(n) +#define cmap_color(n) +#define obj_color(n) +#define mon_color(n) +#define pet_color(c) +#define warn_color(c) + *color = CLR_WHITE; +#endif + + if ((offset = (g - GLYPH_WARNING_OFF)) >= 0) { /* a warning flash */ + *ch = warnsyms[offset]; + warn_color(offset); + } else if ((offset = (g - GLYPH_SWALLOW_OFF)) >= 0) { /* swallow */ + /* see swallow_to_glyph() in display.c */ + *ch = (uchar) showsyms[S_sw_tl + (offset & 0x7)]; + mon_color(offset >> 3); + } else if ((offset = (g - GLYPH_ZAP_OFF)) >= 0) { /* zap beam */ + /* see zapdir_to_glyph() in display.c */ + *ch = showsyms[S_vbeam + (offset & 0x3)]; + zap_color((offset >> 2)); + } else if ((offset = (g - GLYPH_CMAP_OFF)) >= 0) { /* cmap */ + *ch = showsyms[offset]; + cmap_color(offset); + } else if ((offset = (g - GLYPH_OBJ_OFF)) >= 0) { /* object */ + *ch = oc_syms[(int)objects[offset].oc_class]; + obj_color(offset); + } else if ((offset = (g - GLYPH_BODY_OFF)) >= 0) { /* a corpse */ + *ch = oc_syms[(int)objects[CORPSE].oc_class]; + mon_color(offset); + } else if ((offset = (g - GLYPH_PET_OFF)) >= 0) { /* a pet */ + *ch = monsyms[(int)mons[offset].mlet]; + pet_color(offset); + } else { /* a monster */ + *ch = monsyms[(int)mons[g].mlet]; + mon_color(g); + } + // end of wintty code +} +#endif + +/* map nethack color to RGB */ +COLORREF nhcolor_to_RGB(int c) +{ + switch(c) { + case CLR_BLACK: return RGB(0x55, 0x55, 0x55); + case CLR_RED: return RGB(0xFF, 0x00, 0x00); + case CLR_GREEN: return RGB(0x00, 0x80, 0x00); + case CLR_BROWN: return RGB(0xA5, 0x2A, 0x2A); + case CLR_BLUE: return RGB(0x00, 0x00, 0xFF); + case CLR_MAGENTA: return RGB(0xFF, 0x00, 0xFF); + case CLR_CYAN: return RGB(0x00, 0xFF, 0xFF); + case CLR_GRAY: return RGB(0xC0, 0xC0, 0xC0); + case NO_COLOR: return RGB(0xFF, 0xFF, 0xFF); + case CLR_ORANGE: return RGB(0xFF, 0xA5, 0x00); + case CLR_BRIGHT_GREEN: return RGB(0x00, 0xFF, 0x00); + case CLR_YELLOW: return RGB(0xFF, 0xFF, 0x00); + case CLR_BRIGHT_BLUE: return RGB(0x00, 0xC0, 0xFF); + case CLR_BRIGHT_MAGENTA: return RGB(0xFF, 0x80, 0xFF); + case CLR_BRIGHT_CYAN: return RGB(0x80, 0xFF, 0xFF); /* something close to aquamarine */ + case CLR_WHITE: return RGB(0xFF, 0xFF, 0xFF); + default: return RGB(0x00, 0x00, 0x00); /* black */ + } +} + +/* apply bitmap pointed by sourceDc transparently over + bitmap pointed by hDC */ + +typedef BOOL (WINAPI* LPTRANSPARENTBLT)(HDC, int, int, int, int, HDC, int, int, int, int, UINT); +void nhapply_image_transparent( + HDC hDC, int x, int y, int width, int height, + HDC sourceDC, int s_x, int s_y, int s_width, int s_height, + COLORREF cTransparent +) +{ + /* Don't use TransparentBlt; According to Microsoft, it contains a memory leak in Window 95/98. */ + HDC hdcMem, hdcBack, hdcObject, hdcSave; + COLORREF cColor; + HBITMAP bmAndBack, bmAndObject, bmAndMem, bmSave; + HBITMAP bmBackOld, bmObjectOld, bmMemOld, bmSaveOld; + + /* Create some DCs to hold temporary data. */ + hdcBack = CreateCompatibleDC(hDC); + hdcObject = CreateCompatibleDC(hDC); + hdcMem = CreateCompatibleDC(hDC); + hdcSave = CreateCompatibleDC(hDC); + + /* this is bitmap for our pet image */ + bmSave = CreateCompatibleBitmap(hDC, width, height); + + /* Monochrome DC */ + bmAndBack = CreateBitmap(width, height, 1, 1, NULL); + bmAndObject = CreateBitmap(width, height, 1, 1, NULL); + + /* resulting bitmap */ + bmAndMem = CreateCompatibleBitmap(hDC, width, height); + + /* Each DC must select a bitmap object to store pixel data. */ + bmBackOld = SelectObject(hdcBack, bmAndBack); + bmObjectOld = SelectObject(hdcObject, bmAndObject); + bmMemOld = SelectObject(hdcMem, bmAndMem); + bmSaveOld = SelectObject(hdcSave, bmSave); + + /* copy source image because it is going to be overwritten */ + StretchBlt(hdcSave, 0, 0, width, height, sourceDC, s_x, s_y, s_width, s_height, SRCCOPY); + + /* Set the background color of the source DC to the color. + contained in the parts of the bitmap that should be transparent */ + cColor = SetBkColor(hdcSave, cTransparent); + + /* Create the object mask for the bitmap by performing a BitBlt + from the source bitmap to a monochrome bitmap. */ + BitBlt(hdcObject, 0, 0, width, height, hdcSave, 0, 0, SRCCOPY); + + /* Set the background color of the source DC back to the original + color. */ + SetBkColor(hdcSave, cColor); + + /* Create the inverse of the object mask. */ + BitBlt(hdcBack, 0, 0, width, height, hdcObject, 0, 0, NOTSRCCOPY); + + /* Copy background to the resulting image */ + BitBlt(hdcMem, 0, 0, width, height, hDC, x, y, SRCCOPY); + + /* Mask out the places where the source image will be placed. */ + BitBlt(hdcMem, 0, 0, width, height, hdcObject, 0, 0, SRCAND); + + /* Mask out the transparent colored pixels on the source image. */ + BitBlt(hdcSave, 0, 0, width, height, hdcBack, 0, 0, SRCAND); + + /* XOR the source image with the beckground. */ + BitBlt(hdcMem, 0, 0, width, height, hdcSave, 0, 0, SRCPAINT); + + /* blt resulting image to the screen */ + BitBlt( + hDC, + x, y, width, height, hdcMem, + 0, 0, SRCCOPY + ); + + /* cleanup */ + DeleteObject(SelectObject(hdcBack, bmBackOld)); + DeleteObject(SelectObject(hdcObject, bmObjectOld)); + DeleteObject(SelectObject(hdcMem, bmMemOld)); + DeleteObject(SelectObject(hdcSave, bmSaveOld)); + + DeleteDC(hdcMem); + DeleteDC(hdcBack); + DeleteDC(hdcObject); + DeleteDC(hdcSave); +} diff --git a/win/win32/mhmap.h b/win/win32/mhmap.h new file mode 100644 index 0000000..401561d --- /dev/null +++ b/win/win32/mhmap.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMapWindow_h +#define MSWINMapWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + + +HWND mswin_init_map_window (void); +void mswin_map_stretch(HWND hWnd, LPSIZE lpsz, BOOL redraw); +int mswin_map_mode(HWND hWnd, int mode); + +#define ROGUE_LEVEL_MAP_MODE MAP_MODE_ASCII12x16 + +#define DEF_CLIPAROUND_MARGIN 5 +#define DEF_CLIPAROUND_AMOUNT 1 + +#endif /* MSWINMapWindow_h */ diff --git a/win/win32/mhmenu.c b/win/win32/mhmenu.c new file mode 100644 index 0000000..94fc0cb --- /dev/null +++ b/win/win32/mhmenu.c @@ -0,0 +1,1448 @@ +/* SCCS Id: @(#)mhmenu.c 3.4 2002/03/06 */ +/* Copyright (c) Alex Kompel, 2002 */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include +#include "resource.h" +#include "mhmenu.h" +#include "mhmain.h" +#include "mhmsg.h" +#include "mhfont.h" +#include "mhdlg.h" + +#define MENU_MARGIN 0 +#define NHMENU_STR_SIZE BUFSZ +#define MIN_TABSTOP_SIZE 0 +#define NUMTABS 15 +#define TAB_SEPARATION 10 /* pixels between each tab stop */ + +#define DEFAULT_COLOR_BG_TEXT COLOR_WINDOW +#define DEFAULT_COLOR_FG_TEXT COLOR_WINDOWTEXT +#define DEFAULT_COLOR_BG_MENU COLOR_WINDOW +#define DEFAULT_COLOR_FG_MENU COLOR_WINDOWTEXT + +typedef struct mswin_menu_item { + int glyph; + ANY_P identifier; + CHAR_P accelerator; + CHAR_P group_accel; + int attr; + char str[NHMENU_STR_SIZE]; + BOOLEAN_P presel; + int count; + BOOL has_focus; +} NHMenuItem, *PNHMenuItem; + +typedef struct mswin_nethack_menu_window { + int type; /* MENU_TYPE_TEXT or MENU_TYPE_MENU */ + int how; /* for menus: PICK_NONE, PICK_ONE, PICK_ANY */ + + union { + struct menu_list { + int size; /* number of items in items[] */ + int allocated; /* number of allocated slots in items[] */ + PNHMenuItem items; /* menu items */ + char gacc[QBUFSZ]; /* group accelerators */ + BOOL counting; /* counting flag */ + char prompt[QBUFSZ]; /* menu prompt */ + int tab_stop_size[NUMTABS];/* tabstops to align option values */ + } menu; + + struct menu_text { + TCHAR* text; + } text; + }; + int result; + int done; + + HBITMAP bmpChecked; + HBITMAP bmpCheckedCount; + HBITMAP bmpNotChecked; +} NHMenuWindow, *PNHMenuWindow; + +extern short glyph2tile[]; + +static WNDPROC wndProcListViewOrig = NULL; +static WNDPROC editControlWndProc = NULL; + +#define NHMENU_IS_SELECTABLE(item) ((item).identifier.a_obj!=NULL) +#define NHMENU_IS_SELECTED(item) ((item).count!=0) + +BOOL CALLBACK MenuWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHMenuListWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHMenuTextWndProc(HWND, UINT, WPARAM, LPARAM); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam); +static BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void LayoutMenu(HWND hwnd); +static void SetMenuType(HWND hwnd, int type); +static void SetMenuListType(HWND hwnd, int now); +static HWND GetMenuControl(HWND hwnd); +static void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count); +static void reset_menu_count(HWND hwndList, PNHMenuWindow data); +static BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch); + +/*-----------------------------------------------------------------------------*/ +HWND mswin_init_menu_window (int type) { + HWND ret; + + ret = CreateDialog( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_MENU), + GetNHApp()->hMainWnd, + MenuWndProc + ); + if( !ret ) { + panic("Cannot create menu window"); + } + + SetMenuType(ret, type); + return ret; +} +/*-----------------------------------------------------------------------------*/ +int mswin_menu_window_select_menu (HWND hWnd, int how, MENU_ITEM_P ** _selected) +{ + PNHMenuWindow data; + int ret_val; + MENU_ITEM_P *selected = NULL; + int i; + char* ap; + + assert( _selected!=NULL ); + *_selected = NULL; + ret_val = -1; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + /* set menu type */ + SetMenuListType(hWnd, how); + + /* Ok, now give items a unique accelerators */ + if( data->type == MENU_TYPE_MENU ) { + char next_char = 'a'; + + data->menu.gacc[0] = '\0'; + ap = data->menu.gacc; + for( i=0; imenu.size; i++) { + if( data->menu.items[i].accelerator!=0 ) { + next_char = (char)(data->menu.items[i].accelerator+1); + } else if( NHMENU_IS_SELECTABLE(data->menu.items[i]) ) { + if ( (next_char>='a' && next_char<='z') || + (next_char>='A' && next_char<='Z') ) { + data->menu.items[i].accelerator = next_char; + } else { + if( next_char > 'z' ) next_char = 'A'; + else if ( next_char > 'Z' ) break; + + data->menu.items[i].accelerator = next_char; + } + + next_char ++; + } + } + + /* collect group accelerators */ + for( i=0; imenu.size; i++) { + if( data->how != PICK_NONE ) { + if( data->menu.items[i].group_accel && + !strchr(data->menu.gacc, data->menu.items[i].group_accel) ) { + *ap++ = data->menu.items[i].group_accel; + *ap = '\x0'; + } + } + } + + reset_menu_count(NULL, data); + } + + mswin_popup_display(hWnd, &data->done); + + /* get the result */ + if( data->result != -1 ) { + if(how==PICK_NONE) { + if(data->result>=0) ret_val=0; + else ret_val=-1; + } else if(how==PICK_ONE || how==PICK_ANY) { + /* count selected items */ + ret_val = 0; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + NHMENU_IS_SELECTED(data->menu.items[i]) ) { + ret_val++; + } + } + if( ret_val > 0 ) { + int sel_ind; + + selected = (MENU_ITEM_P*)malloc(ret_val*sizeof(MENU_ITEM_P)); + if( !selected ) panic("out of memory"); + + sel_ind = 0; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + NHMENU_IS_SELECTED(data->menu.items[i]) ) { + selected[sel_ind].item = data->menu.items[i].identifier; + selected[sel_ind].count = data->menu.items[i].count; + sel_ind++; + } + } + ret_val = sel_ind; + *_selected = selected; + } + } + } + + mswin_popup_destroy(hWnd); + + return ret_val; +} +/*-----------------------------------------------------------------------------*/ +BOOL CALLBACK MenuWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PNHMenuWindow data; + HWND control; + HDC hdc; + TCHAR title[MAX_LOADSTRING]; + + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_INITDIALOG: + data = (PNHMenuWindow)malloc(sizeof(NHMenuWindow)); + ZeroMemory(data, sizeof(NHMenuWindow)); + data->type = MENU_TYPE_TEXT; + data->how = PICK_NONE; + data->result = 0; + data->done = 0; + data->bmpChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL)); + data->bmpCheckedCount = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_SEL_COUNT)); + data->bmpNotChecked = LoadBitmap(GetNHApp()->hApp, MAKEINTRESOURCE(IDB_MENU_UNSEL)); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + /* set font for the text cotrol */ + control = GetDlgItem(hWnd, IDC_MENU_TEXT); + hdc = GetDC(control); + SendMessage(control, WM_SETFONT, (WPARAM)mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE), (LPARAM)0); + ReleaseDC(control, hdc); + + /* subclass edit control */ + editControlWndProc = (WNDPROC)GetWindowLong(control, GWL_WNDPROC); + SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuTextWndProc); + + /* Even though the dialog has no caption, you can still set the title + which shows on Alt-Tab */ + LoadString(GetNHApp()->hApp, IDS_APP_TITLE, title, MAX_LOADSTRING); + SetWindowText(hWnd, title); + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_SIZE: + LayoutMenu(hWnd); + return FALSE; + + case WM_CLOSE: + if (program_state.gameover) { + data->result = -1; + data->done = 1; + program_state.stopprint++; + return TRUE; + } else + return FALSE; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDCANCEL: + if( data->type == MENU_TYPE_MENU && + (data->how==PICK_ONE || data->how==PICK_ANY) && + data->menu.counting) { + HWND list; + int i; + + /* reset counter if counting is in progress */ + list = GetMenuControl(hWnd); + i = ListView_GetNextItem(list, -1, LVNI_FOCUSED); + if( i>=0 ) { + SelectMenuItem(list, data, i, 0); + } + return TRUE; + } else { + data->result = -1; + data->done = 1; + } + return TRUE; + + case IDOK: + data->done = 1; + data->result = 0; + return TRUE; + + case IDC_MENU_TEXT: + switch (HIWORD(wParam)) + { + case EN_SETFOCUS: + HideCaret((HWND)lParam); + return TRUE; + } + } + } break; + + case WM_NOTIFY: + { + LPNMHDR lpnmhdr = (LPNMHDR)lParam; + switch (LOWORD(wParam)) { + case IDC_MENU_LIST: + { + if( !data || data->type!=MENU_TYPE_MENU ) break; + + switch(lpnmhdr->code) { + case LVN_ITEMACTIVATE: + { + LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam; + if(data->how==PICK_ONE) { + if( lpnmlv->iItem>=0 && + lpnmlv->iItemmenu.size && + NHMENU_IS_SELECTABLE(data->menu.items[lpnmlv->iItem]) ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + -1 + ); + data->done = 1; + data->result = 0; + return TRUE; + } + } + } break; + + case NM_CLICK: { + LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam; + if( lpnmitem->iItem==-1 ) return 0; + if( data->how==PICK_ANY ) { + SelectMenuItem( + lpnmitem->hdr.hwndFrom, + data, + lpnmitem->iItem, + NHMENU_IS_SELECTED(data->menu.items[lpnmitem->iItem])? 0 : -1 + ); + } + } break; + + case LVN_ITEMCHANGED: + { + LPNMLISTVIEW lpnmlv = (LPNMLISTVIEW)lParam; + if( lpnmlv->iItem==-1 ) return 0; + if( !(lpnmlv->uChanged & LVIF_STATE) ) return 0; + + if( data->how==PICK_ONE || data->how==PICK_ANY ) { + data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED); + ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem); + } + + /* update count for single-selection menu (follow the listview selection) */ + if( data->how==PICK_ONE ) { + if( lpnmlv->uNewState & LVIS_SELECTED ) { + SelectMenuItem( + lpnmlv->hdr.hwndFrom, + data, + lpnmlv->iItem, + -1 + ); + } + } + + /* check item focus */ + if( data->how==PICK_ONE || data->how==PICK_ANY ) { + data->menu.items[lpnmlv->iItem].has_focus = !!(lpnmlv->uNewState & LVIS_FOCUSED); + ListView_RedrawItems(lpnmlv->hdr.hwndFrom, lpnmlv->iItem, lpnmlv->iItem); + } + } break; + + case NM_KILLFOCUS: + reset_menu_count(lpnmhdr->hwndFrom, data); + break; + + } + } break; + } + } break; + + case WM_SETFOCUS: + if( hWnd!=GetNHApp()->hPopupWnd ) { + SetFocus(GetNHApp()->hPopupWnd ); + } + break; + + case WM_MEASUREITEM: + if( wParam==IDC_MENU_LIST ) + return onMeasureItem(hWnd, wParam, lParam); + else + return FALSE; + + case WM_DRAWITEM: + if( wParam==IDC_MENU_LIST ) + return onDrawItem(hWnd, wParam, lParam); + else + return FALSE; + + case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */ + HDC hdcEdit = (HDC) wParam; + HWND hwndEdit = (HWND) lParam; + if( hwndEdit == GetDlgItem(hWnd, IDC_MENU_TEXT) ) { + SetBkColor(hdcEdit, + text_bg_brush ? text_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_TEXT) + ); + SetTextColor(hdcEdit, + text_fg_brush ? text_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_TEXT) + ); + return (BOOL)(text_bg_brush + ? text_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT)); + } + } return FALSE; + + case WM_DESTROY: + if( data ) { + DeleteObject(data->bmpChecked); + DeleteObject(data->bmpCheckedCount); + DeleteObject(data->bmpNotChecked); + if( data->type == MENU_TYPE_TEXT ) { + if( data->text.text ) free(data->text.text); + } + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } + return TRUE; + } + return FALSE; +} +/*-----------------------------------------------------------------------------*/ +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMenuWindow data; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: + { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + HWND text_view; + TCHAR wbuf[BUFSZ]; + size_t text_size; + + if( data->type!=MENU_TYPE_TEXT ) + SetMenuType(hWnd, MENU_TYPE_TEXT); + + if( !data->text.text ) { + text_size = strlen(msg_data->text) + 4; + data->text.text = (TCHAR*)malloc(text_size*sizeof(data->text.text[0])); + ZeroMemory(data->text.text, text_size*sizeof(data->text.text[0])); + } else { + text_size = _tcslen(data->text.text) + strlen(msg_data->text) + 4; + data->text.text = (TCHAR*)realloc(data->text.text, text_size*sizeof(data->text.text[0])); + } + if( !data->text.text ) break; + + _tcscat(data->text.text, NH_A2W(msg_data->text, wbuf, BUFSZ)); + _tcscat(data->text.text, TEXT("\r\n")); + + text_view = GetDlgItem(hWnd, IDC_MENU_TEXT); + if( !text_view ) panic("cannot get text view window"); + SetWindowText(text_view, data->text.text); + } break; + + case MSNH_MSG_STARTMENU: + { + int i; + if( data->type!=MENU_TYPE_MENU ) + SetMenuType(hWnd, MENU_TYPE_MENU); + + if( data->menu.items ) free(data->menu.items); + data->how = PICK_NONE; + data->menu.items = NULL; + data->menu.size = 0; + data->menu.allocated = 0; + data->done = 0; + data->result = 0; + for (i = 0; i < NUMTABS; ++i) + data->menu.tab_stop_size[i] = MIN_TABSTOP_SIZE; + } break; + + case MSNH_MSG_ADDMENU: + { + PMSNHMsgAddMenu msg_data = (PMSNHMsgAddMenu)lParam; + char *p, *p1; + int new_item; + HDC hDC; + int column; + HFONT saveFont; + + if( data->type!=MENU_TYPE_MENU ) break; + if( strlen(msg_data->str)==0 ) break; + + if( data->menu.size==data->menu.allocated ) { + data->menu.allocated += 10; + data->menu.items = (PNHMenuItem)realloc(data->menu.items, data->menu.allocated*sizeof(NHMenuItem)); + } + + new_item = data->menu.size; + ZeroMemory( &data->menu.items[new_item], sizeof(data->menu.items[new_item])); + data->menu.items[new_item].glyph = msg_data->glyph; + data->menu.items[new_item].identifier = *msg_data->identifier; + data->menu.items[new_item].accelerator = msg_data->accelerator; + data->menu.items[new_item].group_accel = msg_data->group_accel; + data->menu.items[new_item].attr = msg_data->attr; + strncpy(data->menu.items[new_item].str, msg_data->str, NHMENU_STR_SIZE); + data->menu.items[new_item].presel = msg_data->presel; + + /* calculate tabstop size */ + hDC = GetDC(hWnd); + saveFont = SelectObject(hDC, mswin_get_font(NHW_MENU, msg_data->attr, hDC, FALSE)); + p1 = data->menu.items[new_item].str; + p = strchr(data->menu.items[new_item].str, '\t'); + column = 0; + for (;;) { + TCHAR wbuf[BUFSZ]; + RECT drawRect; + SetRect ( &drawRect, 0, 0, 1, 1 ); + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(hDC, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &drawRect, + DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_EXPANDTABS | DT_SINGLELINE + ); + data->menu.tab_stop_size[column] = + max( data->menu.tab_stop_size[column], drawRect.right - drawRect.left ); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + + ++column; + p1 = p + 1; + p = strchr(p1, '\t'); + } + SelectObject(hDC, saveFont); + ReleaseDC(hWnd, hDC); + + /* increment size */ + data->menu.size++; + } break; + + case MSNH_MSG_ENDMENU: + { + PMSNHMsgEndMenu msg_data = (PMSNHMsgEndMenu)lParam; + if( msg_data->text ) { + strncpy( data->menu.prompt, msg_data->text, sizeof(data->menu.prompt)-1 ); + } else { + ZeroMemory(data->menu.prompt, sizeof(data->menu.prompt)); + } + } break; + + } +} +/*-----------------------------------------------------------------------------*/ +void LayoutMenu(HWND hWnd) +{ + PNHMenuWindow data; + HWND menu_ok; + HWND menu_cancel; + RECT clrt, rt; + POINT pt_elem, pt_ok, pt_cancel; + SIZE sz_elem, sz_ok, sz_cancel; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + menu_ok = GetDlgItem(hWnd, IDOK); + menu_cancel = GetDlgItem(hWnd, IDCANCEL); + + /* get window coordinates */ + GetClientRect(hWnd, &clrt ); + + /* set window placements */ + GetWindowRect(menu_ok, &rt); + sz_ok.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN; + sz_ok.cy = rt.bottom-rt.top; + pt_ok.x = clrt.left + MENU_MARGIN; + pt_ok.y = clrt.bottom - MENU_MARGIN - sz_ok.cy; + + GetWindowRect(menu_cancel, &rt); + sz_cancel.cx = (clrt.right - clrt.left)/2 - 2*MENU_MARGIN; + sz_cancel.cy = rt.bottom-rt.top; + pt_cancel.x = (clrt.left + clrt.right)/2 + MENU_MARGIN; + pt_cancel.y = clrt.bottom - MENU_MARGIN - sz_cancel.cy; + + pt_elem.x = clrt.left + MENU_MARGIN; + pt_elem.y = clrt.top + MENU_MARGIN; + sz_elem.cx = (clrt.right - clrt.left) - 2*MENU_MARGIN; + sz_elem.cy = min(pt_cancel.y, pt_ok.y) - 2*MENU_MARGIN; + + MoveWindow(GetMenuControl(hWnd), pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); + MoveWindow(menu_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE ); + MoveWindow(menu_cancel, pt_cancel.x, pt_cancel.y, sz_cancel.cx, sz_cancel.cy, TRUE ); +} +/*-----------------------------------------------------------------------------*/ +void SetMenuType(HWND hWnd, int type) +{ + PNHMenuWindow data; + HWND list, text; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + data->type = type; + + text = GetDlgItem(hWnd, IDC_MENU_TEXT); + list = GetDlgItem(hWnd, IDC_MENU_LIST); + if(data->type==MENU_TYPE_TEXT) { + ShowWindow(list, SW_HIDE); + EnableWindow(list, FALSE); + EnableWindow(text, TRUE); + ShowWindow(text, SW_SHOW); + SetFocus(text); + } else { + ShowWindow(text, SW_HIDE); + EnableWindow(text, FALSE); + EnableWindow(list, TRUE); + ShowWindow(list, SW_SHOW); + SetFocus(list); + } + LayoutMenu(hWnd); +} +/*-----------------------------------------------------------------------------*/ +void SetMenuListType(HWND hWnd, int how) +{ + PNHMenuWindow data; + RECT rt; + DWORD dwStyles; + char buf[BUFSZ]; + TCHAR wbuf[BUFSZ]; + int nItem; + int i; + HWND control; + LVCOLUMN lvcol; + LRESULT fnt; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data->type != MENU_TYPE_MENU ) return; + + data->how = how; + + switch(how) { + case PICK_NONE: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + case PICK_ONE: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + case PICK_ANY: + dwStyles = WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_CHILD + | WS_VSCROLL | WS_HSCROLL | LVS_REPORT + | LVS_OWNERDRAWFIXED | LVS_SINGLESEL; + break; + default: panic("how should be one of PICK_NONE, PICK_ONE or PICK_ANY"); + }; + + if( strlen(data->menu.prompt)==0 ) { + dwStyles |= LVS_NOCOLUMNHEADER ; + } + + GetWindowRect(GetDlgItem(hWnd, IDC_MENU_LIST), &rt); + DestroyWindow(GetDlgItem(hWnd, IDC_MENU_LIST)); + control = CreateWindow(WC_LISTVIEW, NULL, + dwStyles, + rt.left, + rt.top, + rt.right - rt.left, + rt.bottom - rt.top, + hWnd, + (HMENU)IDC_MENU_LIST, + GetNHApp()->hApp, + NULL ); + if( !control ) panic( "cannot create menu control" ); + + /* install the hook for the control window procedure */ + wndProcListViewOrig = (WNDPROC)GetWindowLong(control, GWL_WNDPROC); + SetWindowLong(control, GWL_WNDPROC, (LONG)NHMenuListWndProc); + + /* set control colors */ + ListView_SetBkColor(control, + menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU)); + ListView_SetTextBkColor(control, + menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU)); + ListView_SetTextColor(control, + menu_fg_brush ? menu_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MENU)); + + /* set control font */ + fnt = SendMessage(hWnd, WM_GETFONT, (WPARAM)0, (LPARAM)0); + SendMessage(control, WM_SETFONT, (WPARAM)fnt, (LPARAM)0); + + /* add column to the list view */ + ZeroMemory(&lvcol, sizeof(lvcol)); + lvcol.mask = LVCF_WIDTH | LVCF_TEXT; + lvcol.cx = GetSystemMetrics(SM_CXFULLSCREEN); + lvcol.pszText = NH_A2W(data->menu.prompt, wbuf, BUFSZ); + ListView_InsertColumn(control, 0, &lvcol); + + /* add items to the list view */ + for(i=0; imenu.size; i++ ) { + LVITEM lvitem; + ZeroMemory( &lvitem, sizeof(lvitem) ); + sprintf(buf, "%c - %s", max(data->menu.items[i].accelerator, ' '), data->menu.items[i].str ); + + lvitem.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; + lvitem.iItem = i; + lvitem.iSubItem = 0; + lvitem.state = data->menu.items[i].presel? LVIS_SELECTED : 0; + lvitem.pszText = NH_A2W(buf, wbuf, BUFSZ); + lvitem.lParam = (LPARAM)&data->menu.items[i]; + nItem = SendMessage(control, LB_ADDSTRING, (WPARAM)0, (LPARAM) buf); + if( ListView_InsertItem(control, &lvitem)==-1 ) { + panic("cannot insert menu item"); + } + } + SetFocus(control); +} +/*-----------------------------------------------------------------------------*/ +HWND GetMenuControl(HWND hWnd) +{ + PNHMenuWindow data; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + if(data->type==MENU_TYPE_TEXT) { + return GetDlgItem(hWnd, IDC_MENU_TEXT); + } else { + return GetDlgItem(hWnd, IDC_MENU_LIST); + } +} +/*-----------------------------------------------------------------------------*/ +BOOL onMeasureItem(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LPMEASUREITEMSTRUCT lpmis; + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC hdc; + PNHMenuWindow data; + RECT list_rect; + + lpmis = (LPMEASUREITEMSTRUCT) lParam; + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + GetClientRect(GetMenuControl(hWnd), &list_rect); + + hdc = GetDC(GetMenuControl(hWnd)); + saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + + /* Set the height of the list box items. */ + lpmis->itemHeight = max(tm.tmHeight, TILE_Y)+2; + lpmis->itemWidth = list_rect.right - list_rect.left; + + SelectObject(hdc, saveFont); + ReleaseDC(GetMenuControl(hWnd), hdc); + return TRUE; +} +/*-----------------------------------------------------------------------------*/ +BOOL onDrawItem(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + LPDRAWITEMSTRUCT lpdis; + PNHMenuItem item; + PNHMenuWindow data; + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC tileDC; + short ntile; + int t_x, t_y; + int x, y; + TCHAR wbuf[BUFSZ]; + RECT drawRect; + COLORREF OldBg, OldFg, NewBg; + char *p, *p1; + int column; + + lpdis = (LPDRAWITEMSTRUCT) lParam; + + /* If there are no list box items, skip this message. */ + if (lpdis->itemID == -1) return FALSE; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + item = &data->menu.items[lpdis->itemID]; + + tileDC = CreateCompatibleDC(lpdis->hDC); + saveFont = SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, item->attr, lpdis->hDC, FALSE)); + NewBg = menu_bg_brush ? menu_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MENU); + OldBg = SetBkColor(lpdis->hDC, NewBg); + OldFg = SetTextColor(lpdis->hDC, + menu_fg_brush ? menu_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MENU)); + + GetTextMetrics(lpdis->hDC, &tm); + + x = lpdis->rcItem.left + 1; + + /* print check mark and letter */ + if( NHMENU_IS_SELECTABLE(*item) ) { + char buf[2]; + if (data->how != PICK_NONE) { + HGDIOBJ saveBrush; + HBRUSH hbrCheckMark; + + switch(item->count) { + case -1: hbrCheckMark = CreatePatternBrush(data->bmpChecked); break; + case 0: hbrCheckMark = CreatePatternBrush(data->bmpNotChecked); break; + default: hbrCheckMark = CreatePatternBrush(data->bmpCheckedCount); break; + } + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; + SetBrushOrgEx(lpdis->hDC, x, y, NULL); + saveBrush = SelectObject(lpdis->hDC, hbrCheckMark); + PatBlt(lpdis->hDC, x, y, TILE_X, TILE_Y, PATCOPY); + SelectObject(lpdis->hDC, saveBrush); + DeleteObject(hbrCheckMark); + + } + x += TILE_X + 5; + if(item->accelerator!=0) { + buf[0] = item->accelerator; + buf[1] = '\x0'; + + SetRect( &drawRect, x, lpdis->rcItem.top, lpdis->rcItem.right, lpdis->rcItem.bottom ); + DrawText(lpdis->hDC, NH_A2W(buf, wbuf, 2), 1, &drawRect, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); + } + x += tm.tmAveCharWidth + tm.tmOverhang + 5; + } else { + x += TILE_X + tm.tmAveCharWidth + tm.tmOverhang + 10; + } + + /* print glyph if present */ + if( item->glyph != NO_GLYPH ) { + HGDIOBJ saveBmp; + + saveBmp = SelectObject(tileDC, GetNHApp()->bmpTiles); + ntile = glyph2tile[ item->glyph ]; + t_x = (ntile % TILES_PER_LINE)*TILE_X; + t_y = (ntile / TILES_PER_LINE)*TILE_Y; + + y = (lpdis->rcItem.bottom + lpdis->rcItem.top - TILE_Y) / 2; + + nhapply_image_transparent( + lpdis->hDC, x, y, TILE_X, TILE_Y, + tileDC, t_x, t_y, TILE_X, TILE_Y, TILE_BK_COLOR ); + SelectObject(tileDC, saveBmp); + } + + x += TILE_X + 5; + + /* draw item text */ + + p1 = item->str; + p = strchr(item->str, '\t'); + column = 0; + SetRect( &drawRect, x, lpdis->rcItem.top, min(x + data->menu.tab_stop_size[0], lpdis->rcItem.right), + lpdis->rcItem.bottom ); + for (;;) { + TCHAR wbuf[BUFSZ]; + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(lpdis->hDC, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &drawRect, + DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + + p1 = p + 1; + p = strchr(p1, '\t'); + drawRect.left = drawRect.right + TAB_SEPARATION; + ++column; + drawRect.right = min (drawRect.left + data->menu.tab_stop_size[column], lpdis->rcItem.right); + } + + /* draw focused item */ + if( item->has_focus + || (NHMENU_IS_SELECTABLE(*item) && + data->menu.items[lpdis->itemID].count!=-1)) { + RECT client_rt; + + GetClientRect(lpdis->hwndItem, &client_rt); + if( NHMENU_IS_SELECTABLE(*item) && + data->menu.items[lpdis->itemID].count!=0 && + item->glyph != NO_GLYPH ) { + if( data->menu.items[lpdis->itemID].count==-1 ) { + _stprintf(wbuf, TEXT("Count: All") ); + } else { + _stprintf(wbuf, TEXT("Count: %d"), data->menu.items[lpdis->itemID].count ); + } + + SelectObject(lpdis->hDC, mswin_get_font(NHW_MENU, ATR_BLINK, lpdis->hDC, FALSE)); + + /* calculate text rectangle */ + SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom ); + DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, + DT_CALCRECT | DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX ); + + /* erase text rectangle */ + drawRect.left = max(client_rt.left+1, client_rt.right - (drawRect.right - drawRect.left) - 10); + drawRect.right = client_rt.right-1; + drawRect.top = lpdis->rcItem.top; + drawRect.bottom = lpdis->rcItem.bottom; + FillRect(lpdis->hDC, &drawRect, + menu_bg_brush ? menu_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MENU)); + + /* draw text */ + DrawText(lpdis->hDC, wbuf, _tcslen(wbuf), &drawRect, + DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX ); + } + } + if (item->has_focus) { + /* draw focus rect */ + RECT client_rt; + + GetClientRect(lpdis->hwndItem, &client_rt); + SetRect( &drawRect, client_rt.left, lpdis->rcItem.top, client_rt.right, lpdis->rcItem.bottom ); + DrawFocusRect(lpdis->hDC, &drawRect); + } + + SetTextColor (lpdis->hDC, OldFg); + SetBkColor (lpdis->hDC, OldBg); + SelectObject(lpdis->hDC, saveFont); + DeleteDC(tileDC); + return TRUE; +} +/*-----------------------------------------------------------------------------*/ +BOOL onListChar(HWND hWnd, HWND hwndList, WORD ch) +{ + int i = 0; + PNHMenuWindow data; + int curIndex, topIndex, pageSize; + boolean is_accelerator = FALSE; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + + switch( ch ) { + case MENU_FIRST_PAGE: + i = 0; + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_LAST_PAGE: + i = max(0, data->menu.size-1); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_NEXT_PAGE: + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + /* Focus down one page */ + i = min(curIndex+pageSize, data->menu.size-1); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + /* Scrollpos down one page */ + i = min(topIndex+(2*pageSize - 1), data->menu.size-1); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + + case MENU_PREVIOUS_PAGE: + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + /* Focus up one page */ + i = max(curIndex-pageSize, 0); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + /* Scrollpos up one page */ + i = max(topIndex-pageSize, 0); + ListView_EnsureVisible(hwndList, i, FALSE); + break; + + case MENU_SELECT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem(hwndList, data, i, -1); + } + return -2; + } + break; + + case MENU_UNSELECT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem(hwndList, data, i, 0); + } + return -2; + } + break; + + case MENU_INVERT_ALL: + if( data->how == PICK_ANY ) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } + return -2; + } + break; + + case MENU_SELECT_PAGE: + if( data->how == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; ihow == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; ihow == PICK_ANY ) { + int from, to; + reset_menu_count(hwndList, data); + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + from = max(0, topIndex); + to = min(data->menu.size, from+pageSize); + for(i=from; imenu.items[i])? 0 : -1 + ); + } + return -2; + } + break; + + case MENU_SEARCH: + if( data->how==PICK_ANY || data->how==PICK_ONE ) { + char buf[BUFSZ]; + + reset_menu_count(hwndList, data); + if( mswin_getlin_window("Search for:", buf, BUFSZ)==IDCANCEL ) { + strcpy(buf, "\033"); + } + SetFocus(hwndList); // set focus back to the list control + if (!*buf || *buf == '\033') return -2; + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) + && strstr(data->menu.items[i].str, buf) ) { + if (data->how == PICK_ANY) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + break; + } + } + } + } else { + mswin_nhbell(); + } + return -2; + + case ' ': + { + if (GetNHApp()->regNetHackMode) { + /* NetHack mode: Scroll down one page, + ends menu when on last page. */ + SCROLLINFO si; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + GetScrollInfo(hwndList, SB_VERT, &si); + if ((si.nPos + (int)si.nPage) > (si.nMax - si.nMin)) { + /* We're at the bottom: dismiss. */ + data->done = 1; + data->result = 0; + return -2; + } + /* We're not at the bottom: page down. */ + topIndex = ListView_GetTopIndex( hwndList ); + pageSize = ListView_GetCountPerPage( hwndList ); + curIndex = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + /* Focus down one page */ + i = min(curIndex+pageSize, data->menu.size-1); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + /* Scrollpos down one page */ + i = min(topIndex+(2*pageSize - 1), data->menu.size-1); + ListView_EnsureVisible(hwndList, i, FALSE); + + return -2; + } else { + /* Windows mode: ends menu for PICK_ONE/PICK_NONE + select item for PICK_ANY */ + if( data->how==PICK_ONE || data->how==PICK_NONE ) { + data->done = 1; + data->result = 0; + return -2; + } else if( data->how==PICK_ANY ) { + i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + if( i>=0 ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } + } + } + } + break; + + default: + if( strchr(data->menu.gacc, ch) && + !(ch=='0' && data->menu.counting) ) { + /* matched a group accelerator */ + if (data->how == PICK_ANY || data->how == PICK_ONE) { + reset_menu_count(hwndList, data); + for(i=0; imenu.size; i++ ) { + if( NHMENU_IS_SELECTABLE(data->menu.items[i]) && + data->menu.items[i].group_accel == ch ) { + if( data->how == PICK_ANY ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + data->result = 0; + data->done = 1; + return -2; + } + } + } + return -2; + } else { + mswin_nhbell(); + return -2; + } + } + + if (isdigit(ch)) { + int count; + i = ListView_GetNextItem(hwndList, -1, LVNI_FOCUSED); + if( i>=0 ) { + count = data->menu.items[i].count; + if( count==-1 ) count=0; + count *= 10L; + count += (int)(ch - '0'); + if (count != 0) /* ignore leading zeros */ { + data->menu.counting = TRUE; + data->menu.items[i].count = min(100000, count); + ListView_RedrawItems( hwndList, i, i ); /* update count mark */ + } + } + return -2; + } + + is_accelerator = FALSE; + for(i=0; imenu.size; i++) { + if( data->menu.items[i].accelerator == ch ) { + is_accelerator = TRUE; + break; + } + } + + if( (ch>='a' && ch<='z') || + (ch>='A' && ch<='Z') || is_accelerator) { + if (data->how == PICK_ANY || data->how == PICK_ONE) { + for(i=0; imenu.size; i++ ) { + if( data->menu.items[i].accelerator == ch ) { + if( data->how == PICK_ANY ) { + SelectMenuItem( + hwndList, + data, + i, + NHMENU_IS_SELECTED(data->menu.items[i])? 0 : -1 + ); + ListView_SetItemState(hwndList, i, LVIS_FOCUSED, LVIS_FOCUSED); + ListView_EnsureVisible(hwndList, i, FALSE); + return -2; + } else if( data->how == PICK_ONE ) { + SelectMenuItem( + hwndList, + data, + i, + -1 + ); + data->result = 0; + data->done = 1; + return -2; + } + } + } + } + } + break; + } + + reset_menu_count(hwndList, data); + return -1; +} +/*-----------------------------------------------------------------------------*/ +void mswin_menu_window_size (HWND hWnd, LPSIZE sz) +{ + TEXTMETRIC tm; + HWND control; + HGDIOBJ saveFont; + HDC hdc; + PNHMenuWindow data; + int i; + RECT rt, wrt; + int extra_cx; + + GetClientRect(hWnd, &rt); + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + GetWindowRect(hWnd, &wrt); + extra_cx = (wrt.right-wrt.left) - sz->cx; + + data = (PNHMenuWindow)GetWindowLong(hWnd, GWL_USERDATA); + if(data) { + control = GetMenuControl(hWnd); + hdc = GetDC(control); + + if( data->type==MENU_TYPE_MENU ) { + /* Calculate the width of the list box. */ + saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + for(i=0; imenu.size; i++ ) { + LONG menuitemwidth = 0; + int column; + char *p, *p1; + + p1 = data->menu.items[i].str; + p = strchr(data->menu.items[i].str, '\t'); + column = 0; + for (;;) { + TCHAR wbuf[BUFSZ]; + RECT tabRect; + SetRect ( &tabRect, 0, 0, 1, 1 ); + if (p != NULL) *p = '\0'; /* for time being, view tab field as zstring */ + DrawText(hdc, + NH_A2W(p1, wbuf, BUFSZ), + strlen(p1), + &tabRect, + DT_CALCRECT | DT_LEFT | DT_VCENTER | DT_SINGLELINE + ); + /* it probably isn't necessary to recompute the tab width now, but do so + * just in case, honoring the previously computed value + */ + menuitemwidth += max(data->menu.tab_stop_size[column], + tabRect.right - tabRect.left); + if (p != NULL) *p = '\t'; + else /* last string so, */ break; + /* add the separation only when not the last item */ + /* in the last item, we break out of the loop, in the statement just above */ + menuitemwidth += TAB_SEPARATION; + ++column; + p1 = p + 1; + p = strchr(p1, '\t'); + } + + sz->cx = max(sz->cx, + (LONG)(2*TILE_X + menuitemwidth + tm.tmAveCharWidth*12 + tm.tmOverhang)); + } + SelectObject(hdc, saveFont); + } else { + /* Calculate the width of the text box. */ + RECT text_rt; + saveFont = SelectObject(hdc, mswin_get_font(NHW_MENU, ATR_NONE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + SetRect(&text_rt, 0, 0, sz->cx, sz->cy); + DrawText(hdc, data->text.text, _tcslen(data->text.text), &text_rt, DT_CALCRECT | DT_TOP | DT_LEFT | DT_NOPREFIX); + sz->cx = max(sz->cx, text_rt.right - text_rt.left + 5*tm.tmAveCharWidth + tm.tmOverhang); + SelectObject(hdc, saveFont); + } + sz->cx += extra_cx; + + ReleaseDC(control, hdc); + } +} +/*-----------------------------------------------------------------------------*/ +void SelectMenuItem(HWND hwndList, PNHMenuWindow data, int item, int count) +{ + int i; + + if( item<0 || item>=data->menu.size ) return; + + if( data->how==PICK_ONE && count!=0 ) { + for(i=0; imenu.size; i++) + if( item!=i && data->menu.items[i].count!=0 ) { + data->menu.items[i].count = 0; + ListView_RedrawItems( hwndList, i, i ); + }; + } + + data->menu.items[item].count = count; + ListView_RedrawItems( hwndList, item, item ); + reset_menu_count(hwndList, data); +} +/*-----------------------------------------------------------------------------*/ +void reset_menu_count(HWND hwndList, PNHMenuWindow data) +{ + int i; + data->menu.counting = FALSE; + if( IsWindow(hwndList) ) { + i = ListView_GetNextItem((hwndList), -1, LVNI_FOCUSED); + if( i>=0 ) ListView_RedrawItems( hwndList, i, i ); + } +} +/*-----------------------------------------------------------------------------*/ +/* List window Proc */ +LRESULT CALLBACK NHMenuListWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + BOOL bUpdateFocusItem; + + bUpdateFocusItem = FALSE; + + switch(message) { + + /* filter keyboard input for the control */ + case WM_KEYDOWN: + case WM_KEYUP: { + MSG msg; + BOOL processed; + + processed = FALSE; + if( PeekMessage(&msg, hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) ) { + if( onListChar(GetParent(hWnd), hWnd, (char)msg.wParam)==-2 ) { + processed = TRUE; + } + } + if( processed ) return 0; + + if( wParam==VK_LEFT || wParam==VK_RIGHT ) + bUpdateFocusItem = TRUE; + } break; + + case WM_SIZE: + case WM_HSCROLL: + bUpdateFocusItem = TRUE; + break; + + } + + if( bUpdateFocusItem ) { + int i; + RECT rt; + + /* invalidate the focus rectangle */ + i = ListView_GetNextItem(hWnd, -1, LVNI_FOCUSED); + if( i!=-1 ) { + ListView_GetItemRect(hWnd, i, &rt, LVIR_BOUNDS); + InvalidateRect(hWnd, &rt, TRUE); + } + } + + if( wndProcListViewOrig ) + return CallWindowProc(wndProcListViewOrig, hWnd, message, wParam, lParam); + else + return 0; +} +/*-----------------------------------------------------------------------------*/ +/* Text control window proc - implements scrolling without a cursor */ +LRESULT CALLBACK NHMenuTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) { + + case WM_KEYDOWN: + switch (wParam) + { + /* close on space in Windows mode + page down on space in NetHack mode */ + case VK_SPACE: + { + SCROLLINFO si; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + GetScrollInfo(hWnd, SB_VERT, &si); + /* If nethackmode and not at the end of the list */ + if (GetNHApp()->regNetHackMode && + (si.nPos + (int)si.nPage) <= (si.nMax - si.nMin)) + SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0); + else + PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0); + return 0; + } + case VK_NEXT: + SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0); + return 0; + case VK_PRIOR: + SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0); + return 0; + case VK_UP: + SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0); + return 0; + case VK_DOWN: + SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0); + return 0; + + } + break; + + } + + if( editControlWndProc ) + return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam); + else + return 0; +} +/*-----------------------------------------------------------------------------*/ diff --git a/win/win32/mhmenu.h b/win/win32/mhmenu.h new file mode 100644 index 0000000..aa9a90d --- /dev/null +++ b/win/win32/mhmenu.h @@ -0,0 +1,18 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMenuWindow_h +#define MSWINMenuWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +#define MENU_TYPE_TEXT 1 +#define MENU_TYPE_MENU 2 + +HWND mswin_init_menu_window ( int type ); +int mswin_menu_window_select_menu (HWND hwnd, int how, MENU_ITEM_P **); +void mswin_menu_window_size (HWND hwnd, LPSIZE sz); + +#endif /* MSWINTextWindow_h */ diff --git a/win/win32/mhmsg.h b/win/win32/mhmsg.h new file mode 100644 index 0000000..4f46974 --- /dev/null +++ b/win/win32/mhmsg.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MHNethackMessages_H +#define MHNethackMessages_H + +/* nethack messages */ +#define WM_MSNH_COMMAND (WM_APP+1) + +#define MSNH_MSG_ADDWND 100 +#define MSNH_MSG_PUTSTR 101 +#define MSNH_MSG_PRINT_GLYPH 102 +#define MSNH_MSG_CLEAR_WINDOW 103 +#define MSNH_MSG_CLIPAROUND 104 +#define MSNH_MSG_STARTMENU 105 +#define MSNH_MSG_ADDMENU 106 +#define MSNH_MSG_CURSOR 107 +#define MSNH_MSG_ENDMENU 108 +#define MSNH_MSG_DIED 109 +#define MSNH_MSG_CARET 110 + +typedef struct mswin_nhmsg_add_wnd { + winid wid; +} MSNHMsgAddWnd, *PMSNHMsgAddWnd; + +typedef struct mswin_nhmsg_putstr { + int attr; + const char* text; + boolean append; +} MSNHMsgPutstr, *PMSNHMsgPutstr; + +typedef struct mswin_nhmsg_print_glyph { + XCHAR_P x; + XCHAR_P y; + int glyph; +} MSNHMsgPrintGlyph, *PMSNHMsgPrintGlyph; + +typedef struct mswin_nhmsg_cliparound { + int x; + int y; +} MSNHMsgClipAround, *PMSNHMsgClipAround; + +typedef struct mswin_nhmsg_add_menu { + int glyph; + const ANY_P* identifier; + CHAR_P accelerator; + CHAR_P group_accel; + int attr; + const char * str; + BOOLEAN_P presel; +} MSNHMsgAddMenu, *PMSNHMsgAddMenu; + +typedef struct mswin_nhmsg_cursor { + int x; + int y; +} MSNHMsgCursor, *PMSNHMsgCursor; + +typedef struct mswin_nhmsg_end_menu { + const char* text; +} MSNHMsgEndMenu, *PMSNHMsgEndMenu; + +#endif diff --git a/win/win32/mhmsgwnd.c b/win/win32/mhmsgwnd.c new file mode 100644 index 0000000..7daca46 --- /dev/null +++ b/win/win32/mhmsgwnd.c @@ -0,0 +1,742 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhmsgwnd.h" +#include "mhmsg.h" +#include "mhfont.h" + +#define MSG_WRAP_TEXT + +#define MSG_VISIBLE_LINES max(iflags.wc_vary_msgcount, 2) +#define MAX_MSG_LINES 32 +#define MSG_LINES (int)min(iflags.msg_history, MAX_MSG_LINES) +#define MAXWINDOWTEXT TBUFSZ + +#define DEFAULT_COLOR_BG_MSG COLOR_WINDOW +#define DEFAULT_COLOR_FG_MSG COLOR_WINDOWTEXT + +#define MORE "--More--" + +struct window_line { + int attr; + char text[MAXWINDOWTEXT]; +}; + +typedef struct mswin_nethack_message_window { + size_t max_text; + struct window_line window_text[MAX_MSG_LINES]; +#ifdef MSG_WRAP_TEXT + int window_text_lines[MAX_MSG_LINES]; /* How much space this text line takes */ +#endif + int lines_last_turn; /* lines added during the last turn */ + int cleared; /* clear was called */ + int last_line; /* last line in the message history */ + struct window_line new_line; + int lines_not_seen; /* lines not yet seen by user after last turn or --More-- */ + int in_more; /* We are in a --More-- prompt */ + int nevermore; /* We want no more --More-- prompts */ + + int xChar; /* horizontal scrolling unit */ + int yChar; /* vertical scrolling unit */ + int xUpper; /* average width of uppercase letters */ + int xPos; /* current horizontal scrolling position */ + int yPos; /* current vertical scrolling position */ + int xMax; /* maximum horizontal scrolling position */ + int yMax; /* maximum vertical scrolling position */ + int xPage; /* page size of horizontal scroll bar */ + } NHMessageWindow, *PNHMessageWindow; + +static TCHAR szMessageWindowClass[] = TEXT("MSNHMessageWndClass"); +LRESULT CALLBACK NHMessageWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_message_window_class(void); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +#ifndef MSG_WRAP_TEXT +static void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam); +#endif +static COLORREF setMsgTextColor(HDC hdc, int gray); +static void onPaint(HWND hWnd); +static void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam); + +#ifdef USER_SOUNDS +extern void play_sound_for_message(const char* str); +#endif + +HWND mswin_init_message_window () { + static int run_once = 0; + HWND ret; + DWORD style; + + if( !run_once ) { + register_message_window_class( ); + run_once = 1; + } + +#ifdef MSG_WRAP_TEXT + style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL; +#else + style = WS_CHILD | WS_CLIPSIBLINGS | WS_VSCROLL | WS_HSCROLL; +#endif + + ret = CreateWindowEx( + WS_EX_CLIENTEDGE, + szMessageWindowClass, /* registered class name */ + NULL, /* window name */ + style, /* window style */ + 0, /* horizontal position of window */ + 0, /* vertical position of window */ + 0, /* window width */ + 0, /* window height - set it later */ + GetNHApp()->hMainWnd, /* handle to parent or owner window */ + NULL, /* menu handle or child identifier */ + GetNHApp()->hApp, /* handle to application instance */ + NULL ); /* window-creation data */ + + if( !ret ) panic("Cannot create message window"); + + return ret; +} + +void register_message_window_class() +{ + WNDCLASS wcex; + ZeroMemory( &wcex, sizeof(wcex)); + + wcex.style = CS_NOCLOSE; + wcex.lpfnWndProc = (WNDPROC)NHMessageWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = message_bg_brush ? message_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_MSG); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szMessageWindowClass; + + RegisterClass(&wcex); +} + +LRESULT CALLBACK NHMessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + onCreate( hWnd, wParam, lParam ); + break; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_PAINT: + onPaint(hWnd); + break; + + case WM_SETFOCUS: + SetFocus(GetNHApp()->hMainWnd); + break; + +#ifndef MSG_WRAP_TEXT + case WM_HSCROLL: + onMSNH_HScroll(hWnd, wParam, lParam); + break; +#endif + + case WM_VSCROLL: + onMSNH_VScroll(hWnd, wParam, lParam); + break; + + case WM_DESTROY: + { + PNHMessageWindow data; + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } break; + + case WM_SIZE: + { + SCROLLINFO si; + int xNewSize; + int yNewSize; + PNHMessageWindow data; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + xNewSize = LOWORD(lParam); + yNewSize = HIWORD(lParam); + + if( xNewSize>0 || yNewSize>0 ) { + +#ifndef MSG_WRAP_TEXT + data->xPage = xNewSize/data->xChar; + data->xMax = max(0, (int)(1 + data->max_text - data->xPage)); + data->xPos = min(data->xPos, data->xMax); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = 0; + si.nMax = data->max_text; + si.nPage = data->xPage; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); +#endif + + data->yMax = MSG_LINES-1; + data->yPos = min(data->yPos, data->yMax); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMin = MSG_VISIBLE_LINES; + si.nMax = data->yMax + MSG_VISIBLE_LINES - 1; + si.nPage = MSG_VISIBLE_LINES; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + } + } + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: + { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + SCROLLINFO si; + + if( msg_data->append == 1) { + /* Forcibly append to line, even if we pass the edge */ + strncat(data->window_text[data->last_line].text, msg_data->text, + MAXWINDOWTEXT - strlen(data->window_text[data->last_line].text)); + } else if( msg_data->append < 0) { + /* remove that many chars */ + int len = strlen(data->window_text[data->last_line].text); + int newend = max(len + msg_data->append, 0); + data->window_text[data->last_line].text[newend] = '\0'; + } else { + /* Try to append but move the whole message to the next line if + it doesn't fit */ + /* just schedule for displaying */ + data->new_line.attr = msg_data->attr; + strncpy(data->new_line.text, msg_data->text, MAXWINDOWTEXT); + } + + /* reset V-scroll position to display new text */ + data->yPos = data->yMax; + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + /* update window content */ + InvalidateRect(hWnd, NULL, TRUE); + +#ifdef USER_SOUNDS + play_sound_for_message(msg_data->text); +#endif + } + break; + + case MSNH_MSG_CLEAR_WINDOW: + { + data->cleared = 1; + data->lines_not_seen = 0; + /* do --More-- again if needed */ + data->nevermore = 0; + break; + } + case MSNH_MSG_CARET: + /* Create or destroy a caret */ + if (*(int *)lParam) + CreateCaret(hWnd, NULL, 0, data->yChar); + else { + DestroyCaret(); + /* this means we just did something interactive in this window, so we + don't need a --More-- for the lines above. + */ + data->lines_not_seen = 0; + } + break; + + + } +} + +void onMSNH_VScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + SCROLLINFO si; + int yInc; + + /* get window data */ + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE | SIF_POS; + GetScrollInfo(hWnd, SB_VERT, &si); + + switch(LOWORD (wParam)) + { + // User clicked the shaft above the scroll box. + + case SB_PAGEUP: + yInc = -(int)si.nPage; + break; + + // User clicked the shaft below the scroll box. + + case SB_PAGEDOWN: + yInc = si.nPage; + break; + + // User clicked the top arrow. + + case SB_LINEUP: + yInc = -1; + break; + + // User clicked the bottom arrow. + + case SB_LINEDOWN: + yInc = 1; + break; + + // User dragged the scroll box. + + case SB_THUMBTRACK: + yInc = HIWORD(wParam) - data->yPos; + break; + + default: + yInc = 0; + } + + // If applying the vertical scrolling increment does not + // take the scrolling position out of the scrolling range, + // increment the scrolling position, adjust the position + // of the scroll box, and update the window. UpdateWindow + // sends the WM_PAINT message. + + if (yInc = max( MSG_VISIBLE_LINES - data->yPos, + min(yInc, data->yMax - data->yPos))) + { + data->yPos += yInc; + /* ScrollWindowEx(hWnd, 0, -data->yChar * yInc, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + */ + InvalidateRect(hWnd, NULL, TRUE); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->yPos; + SetScrollInfo(hWnd, SB_VERT, &si, TRUE); + + UpdateWindow (hWnd); + } +} + +#ifndef MSG_WRAP_TEXT +void onMSNH_HScroll(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + SCROLLINFO si; + int xInc; + + /* get window data */ + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_PAGE; + GetScrollInfo(hWnd, SB_HORZ, &si); + + switch(LOWORD (wParam)) + { + // User clicked shaft left of the scroll box. + + case SB_PAGEUP: + xInc = - (int)si.nPage; + break; + + // User clicked shaft right of the scroll box. + + case SB_PAGEDOWN: + xInc = si.nPage; + break; + + // User clicked the left arrow. + + case SB_LINEUP: + xInc = -1; + break; + + // User clicked the right arrow. + + case SB_LINEDOWN: + xInc = 1; + break; + + // User dragged the scroll box. + + case SB_THUMBTRACK: + xInc = HIWORD(wParam) - data->xPos; + break; + + default: + xInc = 0; + + } + + + // If applying the horizontal scrolling increment does not + // take the scrolling position out of the scrolling range, + // increment the scrolling position, adjust the position + // of the scroll box, and update the window. + + if (xInc = max (-data->xPos, min (xInc, data->xMax - data->xPos))) + { + data->xPos += xInc; + ScrollWindowEx (hWnd, -data->xChar * xInc, 0, + (CONST RECT *) NULL, (CONST RECT *) NULL, + (HRGN) NULL, (LPRECT) NULL, SW_INVALIDATE | SW_ERASE); + + ZeroMemory(&si, sizeof(si)); + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = data->xPos; + SetScrollInfo(hWnd, SB_HORZ, &si, TRUE); + UpdateWindow (hWnd); + } +} +#endif // MSG_WRAP_TEXT + +COLORREF setMsgTextColor(HDC hdc, int gray) +{ + COLORREF fg, color1, color2; + if (gray) { + if (message_bg_brush) { + color1 = message_bg_color; + color2 = message_fg_color; + } else { + color1 = (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MSG); + color2 = (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MSG); + } + /* Make a "gray" color by taking the average of the individual R,G,B + components of two colors. Thanks to Jonathan del Strother */ + fg = RGB((GetRValue(color1)+GetRValue(color2))/2, + (GetGValue(color1)+GetGValue(color2))/2, + (GetBValue(color1)+GetBValue(color2))/2); + } else { + fg = message_fg_brush ? message_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_MSG); + } + + + return SetTextColor(hdc, fg); +} + + +void onPaint(HWND hWnd) +{ + PAINTSTRUCT ps; + HDC hdc; + PNHMessageWindow data; + RECT client_rt, draw_rt; + int FirstLine, LastLine; + int i, x, y; + HGDIOBJ oldFont; + TCHAR wbuf[MAXWINDOWTEXT+2]; + size_t wlen; + COLORREF OldBg, OldFg; + int do_more = 0; + + hdc = BeginPaint(hWnd, &ps); + + OldBg = SetBkColor(hdc, message_bg_brush ? message_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_MSG)); + OldFg = setMsgTextColor(hdc, 0); + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + + GetClientRect(hWnd, &client_rt); + + if( !IsRectEmpty(&ps.rcPaint) ) { + FirstLine = max (0, data->yPos - (client_rt.bottom - ps.rcPaint.top)/data->yChar + 1); + LastLine = min (MSG_LINES-1, data->yPos - (client_rt.bottom - ps.rcPaint.bottom)/data->yChar); + y = min( ps.rcPaint.bottom, client_rt.bottom ); + for (i=LastLine; i>=FirstLine; i--) { + int lineidx = (data->last_line + 1 + i) % MSG_LINES; + x = data->xChar * (2 - data->xPos); + + draw_rt.left = x; + draw_rt.right = client_rt.right; + draw_rt.top = y - data->yChar; + draw_rt.bottom = y; + + oldFont = SelectObject(hdc, mswin_get_font(NHW_MESSAGE, data->window_text[lineidx].attr, hdc, FALSE)); + + /* find out if we can concatenate the scheduled message without wrapping, + but only if no clear_nhwindow was done just before putstr'ing this one, + and only if not in a more prompt already (to prevent concatenating to + a line containing --More-- when resizing while --More-- is displayed.) + */ + if (i == MSG_LINES-1 + && strlen(data->new_line.text) > 0 + && !data->in_more) { + /* concatenate to the previous line if that is not empty, and + if it has the same attribute, and no clear was done. + */ + if (strlen(data->window_text[lineidx].text) > 0 + && (data->window_text[lineidx].attr + == data->new_line.attr) + && !data->cleared) { + RECT tmpdraw_rt = draw_rt; + /* assume this will never work when textsize is near MAXWINDOWTEXT */ + char tmptext[MAXWINDOWTEXT]; + TCHAR tmpwbuf[MAXWINDOWTEXT+2]; + + strcpy(tmptext, data->window_text[lineidx].text); + strncat(tmptext, " ", + MAXWINDOWTEXT - strlen(tmptext)); + strncat(tmptext, data->new_line.text, + MAXWINDOWTEXT - strlen(tmptext)); + /* Always keep room for a --More-- */ + strncat(tmptext, MORE, + MAXWINDOWTEXT - strlen(tmptext)); + NH_A2W(tmptext, tmpwbuf, sizeof(tmpwbuf)); + /* Find out how large the bounding rectangle of the text is */ + DrawText(hdc, tmpwbuf, _tcslen(tmpwbuf), &tmpdraw_rt, DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT); + if ((tmpdraw_rt.bottom - tmpdraw_rt.top) == (draw_rt.bottom - draw_rt.top) /* fits pixelwise */ + && (strlen(data->window_text[lineidx].text) + + strlen(data->new_line.text) < MAXWINDOWTEXT)) /* fits charwise */ + { + /* strip off --More-- of this combined line and make it so */ + tmptext[strlen(tmptext) - strlen(MORE)] = '\0'; + strcpy(data->window_text[lineidx].text, tmptext); + data->new_line.text[0] = '\0'; + i++; /* Start from the last line again */ + continue; + } + } + if (strlen(data->new_line.text) > 0) { + /* if we get here, the new line was not concatenated. Add it on a new line, + but first check whether we should --More--. */ + RECT tmpdraw_rt = draw_rt; + TCHAR tmpwbuf[MAXWINDOWTEXT+2]; + HGDIOBJ oldFont; + int new_screen_lines; + int screen_lines_not_seen = 0; + /* Count how many screen lines we haven't seen yet. */ +#ifdef MSG_WRAP_TEXT + { + int n; + for (n = data->lines_not_seen - 1; n >= 0; n--) { + screen_lines_not_seen += + data->window_text_lines[(data->last_line - n + MSG_LINES) % MSG_LINES]; + } + } +#else + screen_lines_not_seen = data->lines_not_seen; +#endif + /* Now find out how many screen lines we would like to add */ + NH_A2W(data->new_line.text, tmpwbuf, sizeof(tmpwbuf)); + /* Find out how large the bounding rectangle of the text is */ + oldFont = SelectObject(hdc, mswin_get_font(NHW_MESSAGE, data->window_text[lineidx].attr, hdc, FALSE)); + DrawText(hdc, tmpwbuf, _tcslen(tmpwbuf), &tmpdraw_rt, DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT); + SelectObject(hdc, oldFont); + new_screen_lines = (tmpdraw_rt.bottom - tmpdraw_rt.top) / data->yChar; + /* If this together is more than fits on the window, we must + --More--, unless: + - We are in --More-- already (the user is scrolling the window) + - The user pressed ESC + */ + if (screen_lines_not_seen + new_screen_lines > MSG_VISIBLE_LINES + && !data->in_more && !data->nevermore) { + data->in_more = 1; + /* Show --More-- on last line */ + strcat(data->window_text[data->last_line].text, MORE); + /* Go on drawing, but remember we must do a more afterwards */ + do_more = 1; + } else if (!data->in_more) { + data->last_line++; + data->last_line %= MSG_LINES; + data->window_text[data->last_line].attr = data->new_line.attr; + strncpy(data->window_text[data->last_line].text, data->new_line.text, MAXWINDOWTEXT); + data->new_line.text[0] = '\0'; + if (data->cleared) { + /* now we are drawing a new line, the old lines can be redrawn in grey.*/ + data->lines_last_turn = 0; + data->cleared = 0; + } + data->lines_last_turn++; + data->lines_not_seen++; + /* and start over */ + i++; /* Start from the last line again */ + continue; + } + } + } + /* convert to UNICODE */ + NH_A2W(data->window_text[lineidx].text, wbuf, sizeof(wbuf)); + wlen = _tcslen(wbuf); + setMsgTextColor(hdc, i < (MSG_LINES - data->lines_last_turn)); +#ifdef MSG_WRAP_TEXT + /* Find out how large the bounding rectangle of the text is */ + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT); + /* move that rectangle up, so that the bottom remains at the same height */ + draw_rt.top = y - (draw_rt.bottom - draw_rt.top); + draw_rt.bottom = y; + /* Remember the height of this line for subsequent --More--'s */ + data->window_text_lines[lineidx] = (draw_rt.bottom - draw_rt.top) / data->yChar; + /* Now really draw it */ + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX | DT_WORDBREAK); + + /* Find out the cursor (caret) position */ + if (i == MSG_LINES-1) { + int nnum, numfit; + SIZE size; + TCHAR *nbuf; + int nlen; + + nbuf = wbuf; + nlen = wlen; + while (nlen) { + /* Get the number of characters that fit on the line */ + GetTextExtentExPoint(hdc, nbuf, nlen, draw_rt.right - draw_rt.left, &numfit, NULL, &size); + /* Search back to a space */ + nnum = numfit; + if (numfit < nlen) { + while (nnum > 0 && nbuf[nnum] != ' ') + nnum--; + /* If no space found, break wherever */ + if (nnum == 0) + nnum = numfit; + } + nbuf += nnum; + nlen -= nnum; + if (*nbuf == ' ') { + nbuf++; + nlen--; + } + } + /* The last size is the size of the last line. Set the caret there. + This will fail automatically if we don't own the caret (i.e., + when not in a question.) + */ + SetCaretPos(draw_rt.left + size.cx, draw_rt.bottom - data->yChar); + } +#else + DrawText(hdc, wbuf, wlen, &draw_rt, DT_NOPREFIX ); + SetCaretPos(draw_rt.left + size.cx, draw_rt.bottom - data->yChar); +#endif + SelectObject(hdc, oldFont); + y -= draw_rt.bottom - draw_rt.top; + } + if (do_more) { + int okkey = 0; + int chop; + // @@@ Ok respnses + + while (!okkey) { + char c = mswin_nhgetch(); + + switch (c) + { + /* space or enter */ + case ' ': + case '\015': + okkey = 1; + break; + /* ESC */ + case '\033': + data->nevermore = 1; + okkey = 1; + break; + default: + break; + } + } + chop = strlen(data->window_text[data->last_line].text) + - strlen(MORE); + data->window_text[data->last_line].text[chop] = '\0'; + data->in_more = 0; + data->lines_not_seen = 0; + /* We did the --More--, reset the lines_not_seen; now draw that + new line. This is the easiest method */ + InvalidateRect(hWnd, NULL, TRUE); + } + } + SetTextColor (hdc, OldFg); + SetBkColor (hdc, OldBg); + EndPaint(hWnd, &ps); +} + +void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHMessageWindow data; + SIZE dummy; + + /* set window data */ + data = (PNHMessageWindow)malloc(sizeof(NHMessageWindow)); + if( !data ) panic("out of memory"); + ZeroMemory(data, sizeof(NHMessageWindow)); + data->max_text = MAXWINDOWTEXT; + SetWindowLong(hWnd, GWL_USERDATA, (LONG)data); + + /* re-calculate window size (+ font size) */ + mswin_message_window_size(hWnd, &dummy); +} + +void mswin_message_window_size (HWND hWnd, LPSIZE sz) +{ + HDC hdc; + HGDIOBJ saveFont; + TEXTMETRIC tm; + PNHMessageWindow data; + RECT rt, client_rt; + + data = (PNHMessageWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( !data ) return; + + /* -- Calculate the font size -- */ + /* Get the handle to the client area's device context. */ + hdc = GetDC(hWnd); + saveFont = SelectObject(hdc, mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, FALSE)); + + /* Extract font dimensions from the text metrics. */ + GetTextMetrics (hdc, &tm); + data->xChar = tm.tmAveCharWidth; + data->xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * data->xChar/2; + data->yChar = tm.tmHeight + tm.tmExternalLeading; + data->xPage = 1; + + /* Free the device context. */ + SelectObject(hdc, saveFont); + ReleaseDC (hWnd, hdc); + + /* -- calculate window size -- */ + GetWindowRect(hWnd, &rt); + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + /* set size to accomodate MSG_VISIBLE_LINES and + horizontal scroll bar (difference between window rect and client rect */ + GetClientRect(hWnd, &client_rt); + sz->cy = sz->cy - (client_rt.bottom - client_rt.top) + + data->yChar * MSG_VISIBLE_LINES; +} diff --git a/win/win32/mhmsgwnd.h b/win/win32/mhmsgwnd.h new file mode 100644 index 0000000..a4f6736 --- /dev/null +++ b/win/win32/mhmsgwnd.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINMessageWindow_h +#define MSWINMessageWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_message_window (void); +void mswin_message_window_size (HWND hWnd, LPSIZE sz); + + +#endif /* MSWINMessageWindow_h */ diff --git a/win/win32/mhrip.c b/win/win32/mhrip.c new file mode 100644 index 0000000..69f4916 --- /dev/null +++ b/win/win32/mhrip.c @@ -0,0 +1,265 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "resource.h" +#include "mhrip.h" +#include "mhmsg.h" +#include "mhfont.h" + +#define RIP_WIDTH 400 +#define RIP_HEIGHT 200 + +#define RIP_GRAVE_HEIGHT 120 +#define RIP_GRAVE_WIDTH 115 +#define RIP_GRAVE_X 90 +#define RIP_GRAVE_Y 60 + +#define RIP_OFFSET_X 10 +#define RIP_OFFSET_Y 10 + +PNHWinApp GetNHApp(void); + +typedef struct mswin_nethack_text_window { + HANDLE rip_bmp; + TCHAR* window_text; + TCHAR* rip_text; +} NHRIPWindow, *PNHRIPWindow; + +BOOL CALLBACK NHRIPWndProc(HWND, UINT, WPARAM, LPARAM); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); + +HWND mswin_init_RIP_window () { + HWND ret; + PNHRIPWindow data; + + ret = CreateDialog( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_NHRIP), + GetNHApp()->hMainWnd, + NHRIPWndProc + ); + if( !ret ) panic("Cannot create rip window"); + + data = (PNHRIPWindow)malloc(sizeof(NHRIPWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHRIPWindow)); + SetWindowLong(ret, GWL_USERDATA, (LONG)data); + return ret; +} + +void mswin_display_RIP_window (HWND hWnd) +{ + MSG msg; + RECT rt; + PNHRIPWindow data; + HWND mapWnd; + RECT riprt; + RECT clientrect; + RECT textrect; + HDC hdc; + HFONT OldFont; + + data = (PNHRIPWindow)GetWindowLong(hWnd, GWL_USERDATA); + + GetNHApp()->hPopupWnd = hWnd; + mapWnd = mswin_hwnd_from_winid(WIN_MAP); + if( !IsWindow(mapWnd) ) mapWnd = GetNHApp()->hMainWnd; + GetWindowRect(mapWnd, &rt); + GetWindowRect(hWnd, &riprt); + GetClientRect (hWnd, &clientrect); + textrect = clientrect; + textrect.top += RIP_OFFSET_Y; + textrect.left += RIP_OFFSET_X; + textrect.right -= RIP_OFFSET_X; + if (data->window_text) + { + hdc = GetDC (hWnd); + OldFont = SelectObject (hdc, mswin_get_font(NHW_TEXT, 0, hdc, FALSE)); + DrawText (hdc, data->window_text, strlen(data->window_text), &textrect, + DT_LEFT | DT_NOPREFIX | DT_CALCRECT); + SelectObject (hdc, OldFont); + ReleaseDC(hWnd, hdc); + } + if (textrect.right - textrect.left > RIP_WIDTH) + clientrect.right = textrect.right + RIP_OFFSET_X - clientrect.right; + else + clientrect.right = textrect.left + 2 * RIP_OFFSET_X + RIP_WIDTH - clientrect.right; + clientrect.bottom = textrect.bottom + RIP_HEIGHT + RIP_OFFSET_Y - clientrect.bottom; + GetWindowRect (GetDlgItem(hWnd, IDOK), &textrect); + textrect.right -= textrect.left; + textrect.bottom -= textrect.top; + clientrect.bottom += textrect.bottom + RIP_OFFSET_Y; + riprt.right -= riprt.left; + riprt.bottom -= riprt.top; + riprt.right += clientrect.right; + riprt.bottom += clientrect.bottom; + rt.left += (rt.right - rt.left - riprt.right) / 2; + rt.top += (rt.bottom - rt.top - riprt.bottom) / 2; + + MoveWindow(hWnd, rt.left, rt.top, riprt.right, riprt.bottom, TRUE); + GetClientRect (hWnd, &clientrect); + MoveWindow (GetDlgItem(hWnd, IDOK), + (clientrect.right - clientrect.left - textrect.right) / 2, + clientrect.bottom - textrect.bottom - RIP_OFFSET_Y, textrect.right, textrect.bottom, TRUE); + ShowWindow(hWnd, SW_SHOW); + + while( IsWindow(hWnd) && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if( !IsDialogMessage(hWnd, &msg) ) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + GetNHApp()->hPopupWnd = NULL; +} + +BOOL CALLBACK NHRIPWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + PNHRIPWindow data; + + data = (PNHRIPWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_INITDIALOG: + /* set text control font */ + hdc = GetDC(hWnd); + SendMessage(hWnd, WM_SETFONT, + (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hdc, FALSE), 0); + ReleaseDC(hWnd, hdc); + + SetFocus(GetDlgItem(hWnd, IDOK)); + return FALSE; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_PAINT: + { + int bitmap_offset; + RECT clientrect; + RECT textrect; + HDC hdcBitmap; + HANDLE OldBitmap; + PAINTSTRUCT ps; + HFONT OldFont; + + hdc = BeginPaint (hWnd, &ps); + OldFont = SelectObject (hdc, mswin_get_font(NHW_TEXT, 0, hdc, FALSE)); + hdcBitmap = CreateCompatibleDC(hdc); + SetBkMode (hdc, TRANSPARENT); + GetClientRect (hWnd, &clientrect); + textrect = clientrect; + textrect.top += RIP_OFFSET_Y; + textrect.left += RIP_OFFSET_X; + textrect.right -= RIP_OFFSET_X; + if (data->window_text) + { + DrawText (hdc, data->window_text, strlen(data->window_text), &textrect, + DT_LEFT | DT_NOPREFIX | DT_CALCRECT); + DrawText (hdc, data->window_text, strlen(data->window_text), &textrect, + DT_LEFT | DT_NOPREFIX); + } + OldBitmap = SelectObject(hdcBitmap, GetNHApp()->bmpRip); + SetBkMode (hdc, OPAQUE); + bitmap_offset = (textrect.right - textrect.left - RIP_WIDTH) / 2; + BitBlt (hdc, textrect.left + bitmap_offset, textrect.bottom, RIP_WIDTH, + RIP_HEIGHT, hdcBitmap, 0, 0, SRCCOPY); + SetBkMode (hdc, TRANSPARENT); + if (data->rip_text) + { + textrect.left += RIP_GRAVE_X + bitmap_offset; + textrect.top = textrect.bottom + RIP_GRAVE_Y; + textrect.right = textrect.left + RIP_GRAVE_WIDTH; + textrect.bottom = textrect.top + RIP_GRAVE_HEIGHT; + DrawText (hdc, data->rip_text, strlen(data->rip_text), &textrect, + DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_WORDBREAK); + } + SelectObject (hdcBitmap, OldBitmap); + SelectObject (hdc, OldFont); + DeleteDC (hdcBitmap); + EndPaint (hWnd, &ps); + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + mswin_window_mark_dead(mswin_winid_from_handle(hWnd)); + if( GetNHApp()->hMainWnd==hWnd ) + GetNHApp()->hMainWnd=NULL; + DestroyWindow(hWnd); + SetFocus(GetNHApp()->hMainWnd); + return TRUE; + } + break; + + case WM_CLOSE: + /* if we get this here, we saved the bones so we can just force a quit */ + + mswin_window_mark_dead(mswin_winid_from_handle(hWnd)); + if( GetNHApp()->hMainWnd==hWnd ) + GetNHApp()->hMainWnd=NULL; + DestroyWindow(hWnd); + SetFocus(GetNHApp()->hMainWnd); + program_state.stopprint++; + return TRUE; + + case WM_DESTROY: + if( data ) { + if( data->window_text ) free(data->window_text); + if( data->rip_text ) free(data->rip_text); + if (data->rip_bmp != NULL) DeleteObject(data->rip_bmp); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } + break; + + } + return FALSE; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHRIPWindow data; + static int InRipText = 1; + data = (PNHRIPWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + TCHAR wbuf[BUFSZ]; + size_t text_size; + + if( !data->window_text ) { + text_size = strlen(msg_data->text) + 4; + data->window_text = (TCHAR*)malloc(text_size*sizeof(data->window_text[0])); + ZeroMemory(data->window_text, text_size*sizeof(data->window_text[0])); + } else { + text_size = _tcslen(data->window_text) + strlen(msg_data->text) + 4; + data->window_text = (TCHAR*)realloc(data->window_text, text_size*sizeof(data->window_text[0])); + } + if( !data->window_text ) break; + + _tcscat(data->window_text, NH_A2W(msg_data->text, wbuf, BUFSZ)); + _tcscat(data->window_text, TEXT("\r\n")); + break; + } + case MSNH_MSG_DIED: + { + data->rip_text = data->window_text; + data->window_text = NULL; + break; + } + + } +} + +void mswin_finish_rip_text(winid wid) +{ + SendMessage (mswin_hwnd_from_winid(wid), WM_MSNH_COMMAND, MSNH_MSG_DIED, 0); +} diff --git a/win/win32/mhrip.h b/win/win32/mhrip.h new file mode 100644 index 0000000..cc49355 --- /dev/null +++ b/win/win32/mhrip.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINRIPWindow_h +#define MSWINRIPWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +void mswin_finish_rip_text(winid wid); +HWND mswin_init_RIP_window (void); +void mswin_display_RIP_window (HWND hwnd); + +#endif /* MSWINRIPWindow_h */ diff --git a/win/win32/mhsplash.c b/win/win32/mhsplash.c new file mode 100644 index 0000000..3c974bc --- /dev/null +++ b/win/win32/mhsplash.c @@ -0,0 +1,248 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "resource.h" +#include "mhsplash.h" +#include "mhmsg.h" +#include "mhfont.h" +#include "patchlevel.h" +#include "dlb.h" + +#define LLEN 128 + +PNHWinApp GetNHApp(void); + +BOOL CALLBACK NHSplashWndProc(HWND, UINT, WPARAM, LPARAM); + +#define SPLASH_WIDTH 440 +#define SPLASH_HEIGHT 322 +#define SPLASH_VERSION_X 290 +#define SPLASH_VERSION_Y 10 +#define SPLASH_OFFSET_X 10 +#define SPLASH_OFFSET_Y 10 + +extern HFONT version_splash_font; + +void mswin_display_splash_window (BOOL show_ver) +{ + MSG msg; + HWND mapWnd; + int left, top; + RECT splashrt; + RECT clientrt; + RECT controlrt; + HWND hWnd; + int buttop; + int strsize = 0; + int bufsize = BUFSZ; + + char *buf = malloc(bufsize); + if (buf == NULL) + panic("out of memory"); + buf[0] = '\0'; + + hWnd = CreateDialog(GetNHApp()->hApp, MAKEINTRESOURCE(IDD_SPLASH), + GetNHApp()->hMainWnd, NHSplashWndProc); + if( !hWnd ) panic("Cannot create Splash window"); + mswin_init_splashfonts(hWnd); + GetNHApp()->hPopupWnd = hWnd; + mapWnd = mswin_hwnd_from_winid(WIN_MAP); + if( !IsWindow(mapWnd) ) mapWnd = GetNHApp()->hMainWnd; + /* Get control size */ + GetWindowRect (GetDlgItem(hWnd, IDOK), &controlrt); + controlrt.right -= controlrt.left; + controlrt.bottom -= controlrt.top; + /* Get current client area */ + GetClientRect (hWnd, &clientrt); + /* Get window size */ + GetWindowRect(hWnd, &splashrt); + splashrt.right -= splashrt.left; + splashrt.bottom -= splashrt.top; + /* Get difference between requested client area and current value */ + splashrt.right += SPLASH_WIDTH + SPLASH_OFFSET_X * 2 - clientrt.right; + splashrt.bottom += SPLASH_HEIGHT + controlrt.bottom + SPLASH_OFFSET_Y * 3 - clientrt.bottom; + /* Place the window centered */ + /* On the screen, not on the parent window */ + left = (GetSystemMetrics(SM_CXSCREEN) - splashrt.right) / 2; + top = (GetSystemMetrics(SM_CYSCREEN) - splashrt.bottom) / 2; + MoveWindow(hWnd, left, top, splashrt.right, splashrt.bottom, TRUE); + /* Place the OK control */ + GetClientRect (hWnd, &clientrt); + MoveWindow (GetDlgItem(hWnd, IDOK), + (clientrt.right - clientrt.left - controlrt.right) / 2, + clientrt.bottom - controlrt.bottom - SPLASH_OFFSET_Y, + controlrt.right, controlrt.bottom, TRUE); + buttop = clientrt.bottom - controlrt.bottom - SPLASH_OFFSET_Y; + /* Place the text control */ + GetWindowRect (GetDlgItem(hWnd, IDC_EXTRAINFO), &controlrt); + controlrt.right -= controlrt.left; + controlrt.bottom -= controlrt.top; + GetClientRect (hWnd, &clientrt); + MoveWindow (GetDlgItem(hWnd, IDC_EXTRAINFO), + clientrt.left + SPLASH_OFFSET_X, + buttop - controlrt.bottom - SPLASH_OFFSET_Y, + clientrt.right - 2 * SPLASH_OFFSET_X, controlrt.bottom, TRUE); + /* Fill the text control */ + Sprintf(buf, "%s\r\n%s\r\n%s\r\n\r\n", COPYRIGHT_BANNER_A, COPYRIGHT_BANNER_B, + COPYRIGHT_BANNER_C); + strsize = strlen(buf); + + if (show_ver) { + /* Show complete version information */ + dlb *f; + + getversionstring(buf + strsize); + strcat(buf, "\r\n\r\n"); + strsize = strlen(buf); + + /* Add compile options */ + f = dlb_fopen(OPTIONS_USED, RDTMODE); + if (f) { + char line[LLEN + 1]; + + while (dlb_fgets(line, LLEN, f)) { + size_t len; + len = strlen(line); + if (len > 0 && line[len - 1] == '\n') { + line[len - 1] = '\r'; + line[len] = '\n'; + line[len + 1] = '\0'; + len++; + } + if (strsize + (int)len + 1 > bufsize) + { + bufsize += BUFSZ; + buf = realloc(buf, bufsize); + if (buf == NULL) + panic("out of memory"); + } + strcat(buf, line); + strsize += len; + } + (void) dlb_fclose(f); + } + } else { + /* Show news, if any */ + if (iflags.news) { + FILE *nf; + + iflags.news = 0; /* prevent newgame() from re-displaying news */ + nf = fopen(NEWS, "r"); + if (nf != NULL) { + char line[LLEN + 1]; + + while (fgets(line, LLEN, nf)) { + size_t len; + len = strlen(line); + if (len > 0 && line[len - 1] == '\n') { + line[len - 1] = '\r'; + line[len] = '\n'; + line[len + 1] = '\0'; + len++; + } + if (strsize + (int)len + 1 > bufsize) + { + bufsize += BUFSZ; + buf = realloc(buf, bufsize); + if (buf == NULL) + panic("out of memory"); + } + strcat(buf, line); + strsize += len; + } + (void) fclose(nf); + } + else + { + strcat(buf, "No news."); + } + } + } + SetWindowText(GetDlgItem(hWnd, IDC_EXTRAINFO), buf); + free(buf); + ShowWindow(hWnd, SW_SHOW); + + while( IsWindow(hWnd) && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if( !IsDialogMessage(hWnd, &msg) ) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + GetNHApp()->hPopupWnd = NULL; + mswin_destroy_splashfonts(); +} + +BOOL CALLBACK NHSplashWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HDC hdc; + switch (message) + { + case WM_INITDIALOG: + /* set text control font */ + hdc = GetDC(hWnd); + SendMessage(hWnd, WM_SETFONT, + (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hdc, FALSE), 0); + ReleaseDC(hWnd, hdc); + + SetFocus(GetDlgItem(hWnd, IDOK)); + return FALSE; + + case WM_PAINT: + { + char VersionString[BUFSZ]; + RECT rt; + HDC hdcBitmap; + HANDLE OldBitmap; + HANDLE OldFont; + PAINTSTRUCT ps; + + hdc = BeginPaint (hWnd, &ps); + /* Show splash graphic */ + + hdcBitmap = CreateCompatibleDC(hdc); + SetBkMode (hdc, OPAQUE); + OldBitmap = SelectObject(hdcBitmap, GetNHApp()->bmpSplash); + nhapply_image_transparent(hdc, SPLASH_OFFSET_X, SPLASH_OFFSET_Y, + SPLASH_WIDTH, SPLASH_HEIGHT, + hdcBitmap, 0, 0, SPLASH_WIDTH, SPLASH_HEIGHT, + TILE_BK_COLOR); + + SelectObject (hdcBitmap, OldBitmap); + DeleteDC (hdcBitmap); + + SetBkMode (hdc, TRANSPARENT); + /* Print version number */ + + SetTextColor (hdc, RGB(0, 0, 0)); + rt.right = rt.left = SPLASH_VERSION_X; + rt.bottom = rt.top = SPLASH_VERSION_Y; + Sprintf (VersionString, "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, + PATCHLEVEL); + OldFont = SelectObject(hdc, version_splash_font); + DrawText (hdc, VersionString, strlen(VersionString), &rt, + DT_LEFT | DT_NOPREFIX | DT_CALCRECT); + DrawText (hdc, VersionString, strlen(VersionString), &rt, + DT_LEFT | DT_NOPREFIX); + + EndPaint (hWnd, &ps); + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + mswin_window_mark_dead(mswin_winid_from_handle(hWnd)); + if( GetNHApp()->hMainWnd==hWnd ) + GetNHApp()->hMainWnd=NULL; + DestroyWindow(hWnd); + SetFocus(GetNHApp()->hMainWnd); + return TRUE; + } + break; + } + return FALSE; +} diff --git a/win/win32/mhsplash.h b/win/win32/mhsplash.h new file mode 100644 index 0000000..9966c3d --- /dev/null +++ b/win/win32/mhsplash.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2002 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINSplashWindow_h +#define MSWINSplashWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +void mswin_display_splash_window (BOOL); + +#endif /* MSWINSplashWindow_h */ diff --git a/win/win32/mhstatus.c b/win/win32/mhstatus.c new file mode 100644 index 0000000..050b8ba --- /dev/null +++ b/win/win32/mhstatus.c @@ -0,0 +1,176 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "mhstatus.h" +#include "mhmsg.h" +#include "mhfont.h" + +#define NHSW_LINES 2 +#define MAXWINDOWTEXT BUFSZ + +typedef struct mswin_nethack_status_window { + int index; + char window_text[NHSW_LINES][MAXWINDOWTEXT+1]; +} NHStatusWindow, *PNHStatusWindow; + +static TCHAR szStatusWindowClass[] = TEXT("MSNHStatusWndClass"); +LRESULT CALLBACK StatusWndProc(HWND, UINT, WPARAM, LPARAM); +static void register_status_window_class(void); + +#define DEFAULT_COLOR_BG_STATUS COLOR_WINDOW +#define DEFAULT_COLOR_FG_STATUS COLOR_WINDOWTEXT + +HWND mswin_init_status_window () { + static int run_once = 0; + HWND ret; + NHStatusWindow* data; + + if( !run_once ) { + register_status_window_class( ); + run_once = 1; + } + + ret = CreateWindow( + szStatusWindowClass, + NULL, + WS_CHILD | WS_DISABLED | WS_CLIPSIBLINGS, + 0, /* x position */ + 0, /* y position */ + 0, /* x-size - we will set it later */ + 0, /* y-size - we will set it later */ + GetNHApp()->hMainWnd, + NULL, + GetNHApp()->hApp, + NULL ); + if( !ret ) panic("Cannot create status window"); + + EnableWindow(ret, FALSE); + + data = (PNHStatusWindow)malloc(sizeof(NHStatusWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHStatusWindow)); + SetWindowLong(ret, GWL_USERDATA, (LONG)data); + return ret; +} + +void register_status_window_class() +{ + WNDCLASS wcex; + ZeroMemory( &wcex, sizeof(wcex)); + + wcex.style = CS_NOCLOSE; + wcex.lpfnWndProc = (WNDPROC)StatusWndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetNHApp()->hApp; + wcex.hIcon = NULL; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = status_bg_brush + ? status_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_STATUS); + wcex.lpszMenuName = NULL; + wcex.lpszClassName = szStatusWindowClass; + + RegisterClass(&wcex); +} + + +LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + RECT rt; + PAINTSTRUCT ps; + HDC hdc; + PNHStatusWindow data; + + data = (PNHStatusWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_MSNH_COMMAND: { + switch( wParam ) { + case MSNH_MSG_PUTSTR: { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + strncpy(data->window_text[data->index], msg_data->text, + MAXWINDOWTEXT); + strncat(data->window_text[data->index], "\n", + MAXWINDOWTEXT-strlen(data->window_text[data->index])); + data->index = (data->index+1) % NHSW_LINES; + InvalidateRect(hWnd, NULL, TRUE); + break; + } + case MSNH_MSG_CLEAR_WINDOW: + data->index = 0; + ZeroMemory(data->window_text, sizeof(data->window_text)); + InvalidateRect(hWnd, NULL, TRUE); + break; + } + } break; + + case WM_PAINT: { + int i; + SIZE sz; + HGDIOBJ oldFont; + TCHAR wbuf[BUFSZ]; + COLORREF OldBg, OldFg; + + hdc = BeginPaint(hWnd, &ps); + GetClientRect(hWnd, &rt); + + oldFont = SelectObject(hdc, mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE)); + + OldBg = SetBkColor(hdc, status_bg_brush + ? status_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_STATUS)); + OldFg = SetTextColor(hdc, status_fg_brush + ? status_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_STATUS)); + + for(i=0; iwindow_text[i], wbuf, sizeof(wbuf)), strlen(data->window_text[i]), &sz); + NH_A2W(data->window_text[i], wbuf, BUFSZ); + DrawText(hdc, wbuf, strlen(data->window_text[i]), &rt, DT_LEFT | DT_END_ELLIPSIS); + rt.top += sz.cy; + } + + SelectObject(hdc, oldFont); + SetTextColor (hdc, OldFg); + SetBkColor (hdc, OldBg); + EndPaint(hWnd, &ps); + } break; + + case WM_DESTROY: + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + break; + + case WM_SETFOCUS: + SetFocus(GetNHApp()->hMainWnd); + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void mswin_status_window_size (HWND hWnd, LPSIZE sz) +{ + TEXTMETRIC tm; + HGDIOBJ saveFont; + HDC hdc; + PNHStatusWindow data; + RECT rt; + GetWindowRect(hWnd, &rt); + sz->cx = rt.right - rt.left; + sz->cy = rt.bottom - rt.top; + + data = (PNHStatusWindow)GetWindowLong(hWnd, GWL_USERDATA); + if(data) { + hdc = GetDC(hWnd); + saveFont = SelectObject(hdc, mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE)); + GetTextMetrics(hdc, &tm); + + sz->cy = tm.tmHeight * NHSW_LINES; + + SelectObject(hdc, saveFont); + ReleaseDC(hWnd, hdc); + } +} diff --git a/win/win32/mhstatus.h b/win/win32/mhstatus.h new file mode 100644 index 0000000..e073a80 --- /dev/null +++ b/win/win32/mhstatus.h @@ -0,0 +1,14 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINStatusWindow_h +#define MSWINStatusWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_status_window (void); +void mswin_status_window_size (HWND hWnd, LPSIZE sz); + +#endif /* MSWINStatusWindow_h */ diff --git a/win/win32/mhtext.c b/win/win32/mhtext.c new file mode 100644 index 0000000..3d541d5 --- /dev/null +++ b/win/win32/mhtext.c @@ -0,0 +1,254 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "winMS.h" +#include "resource.h" +#include "mhtext.h" +#include "mhmsg.h" +#include "mhfont.h" + +PNHWinApp GetNHApp(void); + +typedef struct mswin_nethack_text_window { + TCHAR* window_text; +} NHTextWindow, *PNHTextWindow; + +static WNDPROC editControlWndProc = 0; +#define DEFAULT_COLOR_BG_TEXT COLOR_WINDOW +#define DEFAULT_COLOR_FG_TEXT COLOR_WINDOWTEXT + +BOOL CALLBACK NHTextWndProc(HWND, UINT, WPARAM, LPARAM); +LRESULT CALLBACK NHEditHookWndProc(HWND, UINT, WPARAM, LPARAM); +static void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam); +static void LayoutText(HWND hwnd); + +HWND mswin_init_text_window () { + HWND ret; + PNHTextWindow data; + + ret = CreateDialog( + GetNHApp()->hApp, + MAKEINTRESOURCE(IDD_NHTEXT), + GetNHApp()->hMainWnd, + NHTextWndProc + ); + if( !ret ) panic("Cannot create text window"); + + data = (PNHTextWindow)malloc(sizeof(NHTextWindow)); + if( !data ) panic("out of memory"); + + ZeroMemory(data, sizeof(NHTextWindow)); + SetWindowLong(ret, GWL_USERDATA, (LONG)data); + return ret; +} + +void mswin_display_text_window (HWND hWnd) +{ + PNHTextWindow data; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + if( data && data->window_text ) { + HWND control; + control = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + SendMessage(control, EM_FMTLINES, 1, 0 ); + SetWindowText(GetDlgItem(hWnd, IDC_TEXT_CONTROL), data->window_text); + } + + mswin_popup_display(hWnd, NULL); + mswin_popup_destroy(hWnd); +} + +BOOL CALLBACK NHTextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND control; + HDC hdc; + PNHTextWindow data; + TCHAR title[MAX_LOADSTRING]; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch (message) + { + case WM_INITDIALOG: + /* set text control font */ + control = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + if( !control ) { + panic("cannot get text view window"); + } + + hdc = GetDC(control); + SendMessage(control, WM_SETFONT, (WPARAM)mswin_get_font(NHW_TEXT, ATR_NONE, hdc, FALSE), 0); + ReleaseDC(control, hdc); + + /* subclass edit control */ + editControlWndProc = (WNDPROC)GetWindowLong(control, GWL_WNDPROC); + SetWindowLong(control, GWL_WNDPROC, (LONG)NHEditHookWndProc); + + SetFocus(control); + + /* Even though the dialog has no caption, you can still set the title + which shows on Alt-Tab */ + LoadString(GetNHApp()->hApp, IDS_APP_TITLE, title, MAX_LOADSTRING); + SetWindowText(hWnd, title); + return FALSE; + + case WM_MSNH_COMMAND: + onMSNHCommand(hWnd, wParam, lParam); + break; + + case WM_SIZE: + LayoutText(hWnd); + return FALSE; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + case IDCANCEL: + mswin_window_mark_dead(mswin_winid_from_handle(hWnd)); + if( GetNHApp()->hMainWnd==hWnd ) + GetNHApp()->hMainWnd=NULL; + DestroyWindow(hWnd); + SetFocus(GetNHApp()->hMainWnd); + return TRUE; + case IDC_TEXT_CONTROL: + switch (HIWORD(wParam)) + { + case EN_SETFOCUS: + HideCaret((HWND)lParam); + return TRUE; + } + } + break; + + case WM_CTLCOLORSTATIC: { /* sent by edit control before it is drawn */ + HDC hdcEdit = (HDC) wParam; + HWND hwndEdit = (HWND) lParam; + if( hwndEdit == GetDlgItem(hWnd, IDC_TEXT_CONTROL) ) { + SetBkColor(hdcEdit, + text_bg_brush ? text_bg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_BG_TEXT) + ); + SetTextColor(hdcEdit, + text_fg_brush ? text_fg_color : (COLORREF)GetSysColor(DEFAULT_COLOR_FG_TEXT) + ); + return (BOOL)(text_bg_brush + ? text_bg_brush : SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_TEXT)); + } + } return FALSE; + + case WM_DESTROY: + if( data ) { + if( data->window_text ) free(data->window_text); + free(data); + SetWindowLong(hWnd, GWL_USERDATA, (LONG)0); + } + break; + + } + return FALSE; +} + +void onMSNHCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) +{ + PNHTextWindow data; + + data = (PNHTextWindow)GetWindowLong(hWnd, GWL_USERDATA); + switch( wParam ) { + case MSNH_MSG_PUTSTR: { + PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr)lParam; + TCHAR wbuf[BUFSZ]; + size_t text_size; + + if( !data->window_text ) { + text_size = strlen(msg_data->text) + 4; + data->window_text = (TCHAR*)malloc(text_size*sizeof(data->window_text[0])); + ZeroMemory(data->window_text, text_size*sizeof(data->window_text[0])); + } else { + text_size = _tcslen(data->window_text) + strlen(msg_data->text) + 4; + data->window_text = (TCHAR*)realloc(data->window_text, text_size*sizeof(data->window_text[0])); + } + if( !data->window_text ) break; + + _tcscat(data->window_text, NH_A2W(msg_data->text, wbuf, BUFSZ)); + _tcscat(data->window_text, TEXT("\r\n")); + break; + } + } +} + +void LayoutText(HWND hWnd) +{ + HWND btn_ok; + HWND text; + RECT clrt, rt; + POINT pt_elem, pt_ok; + SIZE sz_elem, sz_ok; + + text = GetDlgItem(hWnd, IDC_TEXT_CONTROL); + btn_ok = GetDlgItem(hWnd, IDOK); + + /* get window coordinates */ + GetClientRect(hWnd, &clrt ); + + /* set window placements */ + GetWindowRect(btn_ok, &rt); + sz_ok.cx = clrt.right - clrt.left; + sz_ok.cy = rt.bottom-rt.top; + pt_ok.x = clrt.left; + pt_ok.y = clrt.bottom - sz_ok.cy; + + pt_elem.x = clrt.left; + pt_elem.y = clrt.top; + sz_elem.cx = clrt.right - clrt.left; + sz_elem.cy = pt_ok.y; + + MoveWindow(text, pt_elem.x, pt_elem.y, sz_elem.cx, sz_elem.cy, TRUE ); + MoveWindow(btn_ok, pt_ok.x, pt_ok.y, sz_ok.cx, sz_ok.cy, TRUE ); +} + +/* Edit box hook */ +LRESULT CALLBACK NHEditHookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch(message) { + + case WM_KEYDOWN: + switch (wParam) + { + /* close on space in Windows mode + page down on space in NetHack mode */ + case VK_SPACE: + { + SCROLLINFO si; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + GetScrollInfo(hWnd, SB_VERT, &si); + /* If nethackmode and not at the end of the list */ + if (GetNHApp()->regNetHackMode && + (si.nPos + (int)si.nPage) <= (si.nMax - si.nMin)) + SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0); + else + PostMessage(GetParent(hWnd), WM_COMMAND, MAKELONG(IDOK, 0), 0); + return 0; + } + case VK_NEXT: + SendMessage(hWnd, EM_SCROLL, SB_PAGEDOWN, 0); + return 0; + case VK_PRIOR: + SendMessage(hWnd, EM_SCROLL, SB_PAGEUP, 0); + return 0; + case VK_UP: + SendMessage(hWnd, EM_SCROLL, SB_LINEUP, 0); + return 0; + case VK_DOWN: + SendMessage(hWnd, EM_SCROLL, SB_LINEDOWN, 0); + return 0; + + } + break; + } + + if( editControlWndProc ) + return CallWindowProc(editControlWndProc, hWnd, message, wParam, lParam); + else + return 0; +} diff --git a/win/win32/mhtext.h b/win/win32/mhtext.h new file mode 100644 index 0000000..64be03d --- /dev/null +++ b/win/win32/mhtext.h @@ -0,0 +1,14 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef MSWINTextWindow_h +#define MSWINTextWindow_h + +#include "winMS.h" +#include "config.h" +#include "global.h" + +HWND mswin_init_text_window (void); +void mswin_display_text_window (HWND hwnd); + +#endif /* MSWINTextWindow_h */ diff --git a/win/win32/mnsel.uu b/win/win32/mnsel.uu new file mode 100644 index 0000000..69c3965 --- /dev/null +++ b/win/win32/mnsel.uu @@ -0,0 +1,6 @@ +begin 600 mnsel.bmp +M0DU^`````````#X````H````$````!`````!``$``````$````!T$@``=!(` +M`````````````````/___P```````````#_\```W_```,_P``"7\```F_``` +D)WP``">\```_W```/^P``#_T```__```/_P````````````` +` +end diff --git a/win/win32/mnselcnt.uu b/win/win32/mnselcnt.uu new file mode 100644 index 0000000..72f1916 --- /dev/null +++ b/win/win32/mnselcnt.uu @@ -0,0 +1,6 @@ +begin 600 mnselcnt.bmp +M0DU^`````````#X````H````$````!`````!``$``````$````!T$@``=!(` +M`````````````````/___P```````````#_\```__```.]P``#`,```[W``` +D.]P``#O<```[W```,`P``#O<```__```/_P````````````` +` +end diff --git a/win/win32/mnunsel.uu b/win/win32/mnunsel.uu new file mode 100644 index 0000000..0e3d163 --- /dev/null +++ b/win/win32/mnunsel.uu @@ -0,0 +1,6 @@ +begin 600 mnunsel.bmp +M0DU^`````````#X````H````$````!`````!``$``````$````!T$@``=!(` +M`````````````````/___P```````````#_\```__```/_P``#_\```__``` +D/_P``#_\```__```/_P``#_\```__```/_P````````````` +` +end diff --git a/win/win32/mswproc.c b/win/win32/mswproc.c new file mode 100644 index 0000000..698c858 --- /dev/null +++ b/win/win32/mswproc.c @@ -0,0 +1,2353 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +/* + * This file implements the interface between the window port specific + * code in the mswin port and the rest of the nethack game engine. +*/ + +#include "hack.h" +#include "dlb.h" +#include "func_tab.h" /* for extended commands */ +#include "winMS.h" +#include "mhmap.h" +#include "mhstatus.h" +#include "mhtext.h" +#include "mhmsgwnd.h" +#include "mhmenu.h" +#include "mhsplash.h" +#include "mhmsg.h" +#include "mhinput.h" +#include "mhaskyn.h" +#include "mhdlg.h" +#include "mhrip.h" +#include "mhmain.h" +#include "mhfont.h" +#include "resource.h" + +#define LLEN 128 + +extern const char *killed_by_prefix[]; + +#ifdef _DEBUG +extern void logDebug(const char *fmt, ...); +#else +void logDebug(const char *fmt, ...) { } +#endif + +static void mswin_main_loop(void); +static BOOL initMapTiles(void); +static void mswin_color_from_string(char *colorstring, HBRUSH* brushptr, COLORREF *colorptr); +static void prompt_for_player_selection(void); + +#define TOTAL_BRUSHES 10 +HBRUSH brush_table[TOTAL_BRUSHES]; +int max_brush = 0; + +HBRUSH menu_bg_brush = NULL; +HBRUSH menu_fg_brush = NULL; +HBRUSH text_bg_brush = NULL; +HBRUSH text_fg_brush = NULL; +HBRUSH status_bg_brush = NULL; +HBRUSH status_fg_brush = NULL; +HBRUSH message_bg_brush = NULL; +HBRUSH message_fg_brush = NULL; + +COLORREF menu_bg_color = RGB(0, 0, 0); +COLORREF menu_fg_color = RGB(0xFF, 0xFF, 0xFF); +COLORREF text_bg_color = RGB(0, 0, 0); +COLORREF text_fg_color = RGB(0xFF, 0xFF, 0xFF); +COLORREF status_bg_color = RGB(0, 0, 0); +COLORREF status_fg_color = RGB(0xFF, 0xFF, 0xFF); +COLORREF message_bg_color = RGB(0, 0, 0); +COLORREF message_fg_color = RGB(0xFF, 0xFF, 0xFF); + +/* Interface definition, for windows.c */ +struct window_procs mswin_procs = { + "MSWIN", + WC_COLOR|WC_HILITE_PET|WC_ALIGN_MESSAGE|WC_ALIGN_STATUS| + WC_INVERSE|WC_SCROLL_AMOUNT|WC_SCROLL_MARGIN|WC_MAP_MODE| + WC_FONT_MESSAGE|WC_FONT_STATUS|WC_FONT_MENU|WC_FONT_TEXT|WC_FONT_MAP| + WC_FONTSIZ_MESSAGE|WC_FONTSIZ_STATUS|WC_FONTSIZ_MENU|WC_FONTSIZ_TEXT| + WC_TILE_WIDTH|WC_TILE_HEIGHT|WC_TILE_FILE|WC_VARY_MSGCOUNT| + WC_WINDOWCOLORS|WC_PLAYER_SELECTION|WC_SPLASH_SCREEN|WC_POPUP_DIALOG, + 0L, + mswin_init_nhwindows, + mswin_player_selection, + mswin_askname, + mswin_get_nh_event, + mswin_exit_nhwindows, + mswin_suspend_nhwindows, + mswin_resume_nhwindows, + mswin_create_nhwindow, + mswin_clear_nhwindow, + mswin_display_nhwindow, + mswin_destroy_nhwindow, + mswin_curs, + mswin_putstr, + mswin_display_file, + mswin_start_menu, + mswin_add_menu, + mswin_end_menu, + mswin_select_menu, + genl_message_menu, /* no need for X-specific handling */ + mswin_update_inventory, + mswin_mark_synch, + mswin_wait_synch, +#ifdef CLIPPING + mswin_cliparound, +#endif +#ifdef POSITIONBAR + donull, +#endif + mswin_print_glyph, + mswin_raw_print, + mswin_raw_print_bold, + mswin_nhgetch, + mswin_nh_poskey, + mswin_nhbell, + mswin_doprev_message, + mswin_yn_function, + mswin_getlin, + mswin_get_ext_cmd, + mswin_number_pad, + mswin_delay_output, +#ifdef CHANGE_COLOR /* only a Mac option currently */ + mswin, + mswin_change_background, +#endif + /* other defs that really should go away (they're tty specific) */ + mswin_start_screen, + mswin_end_screen, + mswin_outrip, + mswin_preference_update, +}; + + +/* +init_nhwindows(int* argcp, char** argv) + -- Initialize the windows used by NetHack. This can also + create the standard windows listed at the top, but does + not display them. + -- Any commandline arguments relevant to the windowport + should be interpreted, and *argcp and *argv should + be changed to remove those arguments. + -- When the message window is created, the variable + iflags.window_inited needs to be set to TRUE. Otherwise + all plines() will be done via raw_print(). + ** Why not have init_nhwindows() create all of the "standard" + ** windows? Or at least all but WIN_INFO? -dean +*/ +void mswin_init_nhwindows(int* argc, char** argv) +{ + logDebug("mswin_init_nhwindows()\n"); + +#ifdef _DEBUG + { + /* truncate trace file */ + FILE *dfp = fopen("nhtrace.log", "w"); + fclose(dfp); + } +#endif + mswin_nh_input_init(); + + /* set it to WIN_ERR so we can detect attempts to + use this ID before it is inialized */ + WIN_MAP = WIN_ERR; + + /* Read Windows settings from the reqistry */ + /* First set safe defaults */ + GetNHApp()->regMainMinX = CW_USEDEFAULT; + mswin_read_reg(); + /* Create the main window */ + GetNHApp()->hMainWnd = mswin_init_main_window(); + if (!GetNHApp()->hMainWnd) + { + panic("Cannot create main window"); + } + + /* Set menu check mark for interface mode */ + mswin_menu_check_intf_mode(); + + /* check default values */ + if( iflags.wc_fontsiz_statusNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_messageNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_textNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_fontsiz_menuNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE; + + if( iflags.wc_align_message==0 ) iflags.wc_align_message = ALIGN_TOP; + if( iflags.wc_align_status==0 ) iflags.wc_align_status = ALIGN_BOTTOM; + if( iflags.wc_scroll_margin==0 ) iflags.wc_scroll_margin = DEF_CLIPAROUND_MARGIN; + if( iflags.wc_scroll_amount==0 ) iflags.wc_scroll_amount = DEF_CLIPAROUND_AMOUNT; + if( iflags.wc_tile_width==0 ) iflags.wc_tile_width = TILE_X; + if( iflags.wc_tile_height==0 ) iflags.wc_tile_height = TILE_Y; + + if( iflags.wc_vary_msgcount==0 ) iflags.wc_vary_msgcount = 4; + + /* force tabs in menus */ + iflags.menu_tab_sep = 1; + + /* force toptenwin to be true. toptenwin is the option that decides whether to + * write output to a window or stdout. stdout doesn't make sense on Windows + * non-console applications + */ + flags.toptenwin = 1; + set_option_mod_status("toptenwin", SET_IN_FILE); + set_option_mod_status("perm_invent", SET_IN_FILE); + + /* initialize map tiles bitmap */ + initMapTiles(); + + /* set tile-related options to readonly */ + set_wc_option_mod_status( + WC_TILE_WIDTH|WC_TILE_HEIGHT|WC_TILE_FILE, + DISP_IN_GAME); + + /* set font-related options to change in the game */ + set_wc_option_mod_status( + WC_HILITE_PET | + WC_ALIGN_MESSAGE | + WC_ALIGN_STATUS | + WC_SCROLL_AMOUNT | + WC_SCROLL_MARGIN | + WC_MAP_MODE | + WC_FONT_MESSAGE | + WC_FONT_STATUS | + WC_FONT_MENU | + WC_FONT_TEXT | + WC_FONTSIZ_MESSAGE | + WC_FONTSIZ_STATUS | + WC_FONTSIZ_MENU | + WC_FONTSIZ_TEXT | + WC_VARY_MSGCOUNT, + SET_IN_GAME + ); + + mswin_color_from_string(iflags.wc_foregrnd_menu, &menu_fg_brush, &menu_fg_color); + mswin_color_from_string(iflags.wc_foregrnd_message, &message_fg_brush, &message_fg_color); + mswin_color_from_string(iflags.wc_foregrnd_status, &status_fg_brush, &status_fg_color); + mswin_color_from_string(iflags.wc_foregrnd_text, &text_fg_brush, &text_fg_color); + mswin_color_from_string(iflags.wc_backgrnd_menu, &menu_bg_brush, &menu_bg_color); + mswin_color_from_string(iflags.wc_backgrnd_message, &message_bg_brush, &message_bg_color); + mswin_color_from_string(iflags.wc_backgrnd_status, &status_bg_brush, &status_bg_color); + mswin_color_from_string(iflags.wc_backgrnd_text, &text_bg_brush, &text_bg_color); + + if (iflags.wc_splash_screen) mswin_display_splash_window(FALSE); + iflags.window_inited = TRUE; +} + + +/* Do a window-port specific player type selection. If player_selection() + offers a Quit option, it is its responsibility to clean up and terminate + the process. You need to fill in pl_character[0]. +*/ +void mswin_player_selection(void) +{ + int nRole; + + logDebug("mswin_player_selection()\n"); + + if (iflags.wc_player_selection == VIA_DIALOG) { + /* pick player type randomly (use pre-selected role/race/gender/alignment) */ + if( flags.randomall ) { + if (flags.initrole < 0) { + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + raw_print("Incompatible role!"); + flags.initrole = randrole(); + } + } + + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + raw_print("Incompatible race!"); + flags.initrace = randrace(flags.initrole); + } + } + + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + raw_print("Incompatible gender!"); + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } + + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + raw_print("Incompatible alignment!"); + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } + } else { + /* select a role */ + if( mswin_player_selection_window( &nRole ) == IDCANCEL ) { + bail(0); + } + } + } else { /* iflags.wc_player_selection == VIA_PROMPTS */ + prompt_for_player_selection(); + } +} + +void prompt_for_player_selection(void) +{ + int i, k, n; + char pick4u = 'n', thisch, lastch = 0; + char pbuf[QBUFSZ], plbuf[QBUFSZ]; + winid win; + anything any; + menu_item *selected = 0; + DWORD box_result; + + logDebug("prompt_for_player_selection()\n"); + + /* prevent an unnecessary prompt */ + rigid_role_checks(); + + /* Should we randomly pick for the player? */ + if (!flags.randomall && + (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || + flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE)) { + /* int echoline; */ + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole, + flags.initrace, flags.initgend, flags.initalign); + + /* tty_putstr(BASE_WINDOW, 0, ""); */ + /* echoline = wins[BASE_WINDOW]->cury; */ + box_result = NHMessageBox(NULL, prompt, + MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1); + pick4u = (box_result == IDYES) ? 'y' : (box_result == IDNO) ? 'n' : '\033'; + /* tty_putstr(BASE_WINDOW, 0, prompt); */ + do { + /* pick4u = lowc(readchar()); */ + if (index(quitchars, pick4u)) pick4u = 'y'; + } while(!index(ynqchars, pick4u)); + if ((int)strlen(prompt) + 1 < CO) { + /* Echo choice and move back down line */ + /* tty_putsym(BASE_WINDOW, (int)strlen(prompt)+1, echoline, pick4u); */ + /* tty_putstr(BASE_WINDOW, 0, ""); */ + } else + /* Otherwise it's hard to tell where to echo, and things are + * wrapping a bit messily anyway, so (try to) make sure the next + * question shows up well and doesn't get wrapped at the + * bottom of the window. + */ + /* tty_clear_nhwindow(BASE_WINDOW) */ ; + + if (pick4u != 'y' && pick4u != 'n') { +give_up: /* Quit */ + if (selected) free((genericptr_t) selected); + bail((char *)0); + /*NOTREACHED*/ + return; + } + } + + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + + /* Select a role, if necessary */ + /* we'll try to be compatible with pre-selected race/gender/alignment, + * but may not succeed */ + if (flags.initrole < 0) { + char rolenamebuf[QBUFSZ]; + /* Process the choice */ + if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) { + /* Pick a random role */ + flags.initrole = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrole < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); */ + flags.initrole = randrole(); + } + } else { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Character's Role"); */ + /* Prompt for a role */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + if (ok_role(i, flags.initrace, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + thisch = lowc(roles[i].name.m[0]); + if (thisch == lastch) thisch = highc(thisch); + if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) { + if (flags.initgend == 1 && roles[i].name.f) + Strcpy(rolenamebuf, roles[i].name.f); + else + Strcpy(rolenamebuf, roles[i].name.m); + } else { + if (roles[i].name.f) { + Strcpy(rolenamebuf, roles[i].name.m); + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } else + Strcpy(rolenamebuf, roles[i].name.m); + } + add_menu(win, NO_GLYPH, &any, thisch, + 0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED); + lastch = thisch; + } + } + any.a_int = pick_role(flags.initrace, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrole()+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick a role for your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + + /* Process the choice */ + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + flags.initrole = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a race, if necessary */ + /* force compatibility with role, try for compatibility with + * pre-selected gender/alignment */ + if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) { + /* pre-selected race not valid */ + if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) { + flags.initrace = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM); + if (flags.initrace < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); */ + flags.initrace = randrace(flags.initrole); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) { + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; races[i].noun; i++) { + if (validrace(flags.initrole, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Race"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; races[i].noun; i++) + if (ok_race(flags.initrole, i, flags.initgend, + flags.initalign)) { + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any, races[i].noun[0], + 0, ATR_NONE, races[i].noun, MENU_UNSELECTED); + } + any.a_int = pick_race(flags.initrole, flags.initgend, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randrace(flags.initrole)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the race of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initrace = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select a gender, if necessary */ + /* force compatibility with role/race, try for compatibility with + * pre-selected alignment */ + if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace, + flags.initgend)) { + /* pre-selected gender not valid */ + if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) { + flags.initgend = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM); + if (flags.initgend < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); */ + flags.initgend = randgend(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) { + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) { + if (validgend(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Gender"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(flags.initrole, flags.initrace, i, + flags.initalign)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, genders[i].adj[0], + 0, ATR_NONE, genders[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_gend(flags.initrole, flags.initrace, + flags.initalign, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randgend(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the gender of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initgend = k; + } + (void) root_plselection_prompt(plbuf, QBUFSZ - 1, + flags.initrole, flags.initrace, flags.initgend, flags.initalign); + } + + /* Select an alignment, if necessary */ + /* force compatibility with role/race/gender */ + if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace, + flags.initalign)) { + /* pre-selected alignment not valid */ + if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) { + flags.initalign = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM); + if (flags.initalign < 0) { + /* tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); */ + flags.initalign = randalign(flags.initrole, flags.initrace); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) { + if (ok_align(flags.initrole, flags.initrace, flags.initgend, + i)) { + n++; + k = i; + } + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) { + if (validalign(flags.initrole, flags.initrace, i)) { + n++; + k = i; + } + } + } + + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + /* tty_clear_nhwindow(BASE_WINDOW); */ + /* tty_putstr(BASE_WINDOW, 0, "Choosing Alignment"); */ + win = create_nhwindow(NHW_MENU); + start_menu(win); + any.a_void = 0; /* zero out all bits */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(flags.initrole, flags.initrace, + flags.initgend, i)) { + any.a_int = i+1; + add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], + 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED); + } + any.a_int = pick_align(flags.initrole, flags.initrace, + flags.initgend, PICK_RANDOM)+1; + if (any.a_int == 0) /* must be non-zero */ + any.a_int = randalign(flags.initrole, flags.initrace)+1; + add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE, + "Random", MENU_UNSELECTED); + any.a_int = i+1; /* must be non-zero */ + add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE, + "Quit", MENU_UNSELECTED); + Sprintf(pbuf, "Pick the alignment of your %s", plbuf); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + destroy_nhwindow(win); + if (n != 1 || selected[0].item.a_int == any.a_int) + goto give_up; /* Selected quit */ + + k = selected[0].item.a_int - 1; + free((genericptr_t) selected), selected = 0; + } + flags.initalign = k; + } + } + /* Success! */ + /* tty_display_nhwindow(BASE_WINDOW, FALSE); */ +} + +/* Ask the user for a player name. */ +void mswin_askname(void) +{ + logDebug("mswin_askname()\n"); + + if( mswin_getlin_window("Who are you?", plname, PL_NSIZ)==IDCANCEL ) { + bail("bye-bye"); + /* not reached */ + } +} + + +/* Does window event processing (e.g. exposure events). + A noop for the tty and X window-ports. +*/ +void mswin_get_nh_event(void) +{ + MSG msg; + + logDebug("mswin_get_nh_event()\n"); + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)!=0 ) { + if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + return; +} + +/* Exits the window system. This should dismiss all windows, + except the "window" used for raw_print(). str is printed if possible. +*/ +void mswin_exit_nhwindows(const char *str) +{ + logDebug("mswin_exit_nhwindows(%s)\n", str); + /* Write Window settings to the registry */ + mswin_write_reg(); + while (max_brush) + DeleteObject(brush_table[--max_brush]); +} + +/* Prepare the window to be suspended. */ +void mswin_suspend_nhwindows(const char *str) +{ + logDebug("mswin_suspend_nhwindows(%s)\n", str); + return; +} + + +/* Restore the windows after being suspended. */ +void mswin_resume_nhwindows() +{ + logDebug("mswin_resume_nhwindows()\n"); + return; +} + +/* Create a window of type "type" which can be + NHW_MESSAGE (top line) + NHW_STATUS (bottom lines) + NHW_MAP (main dungeon) + NHW_MENU (inventory or other "corner" windows) + NHW_TEXT (help/text, full screen paged window) +*/ +winid +mswin_create_nhwindow(int type) +{ + winid i = 0; + MSNHMsgAddWnd data; + + logDebug("mswin_create_nhwindow(%d)\n", type); + + /* Return the next available winid + */ + + for (i=1; iwindowlist[i].win == NULL && + !GetNHApp()->windowlist[i].dead) + break; + if (i == MAXWINDOWS) + panic ("ERROR: No windows available...\n"); + + switch (type) { + case NHW_MAP: + { + GetNHApp()->windowlist[i].win = mswin_init_map_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_MESSAGE: + { + GetNHApp()->windowlist[i].win = mswin_init_message_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_STATUS: + { + GetNHApp()->windowlist[i].win = mswin_init_status_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + case NHW_MENU: + { + GetNHApp()->windowlist[i].win = NULL; //will create later + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 1; + break; + } + case NHW_TEXT: + { + GetNHApp()->windowlist[i].win = mswin_init_text_window(); + GetNHApp()->windowlist[i].type = type; + GetNHApp()->windowlist[i].dead = 0; + break; + } + } + + ZeroMemory(&data, sizeof(data) ); + data.wid = i; + SendMessage( GetNHApp()->hMainWnd, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ADDWND, (LPARAM)&data ); + return i; +} + +/* Clear the given window, when asked to. */ +void mswin_clear_nhwindow(winid wid) +{ + logDebug("mswin_clear_nhwindow(%d)\n", wid); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { +#ifdef REINCARNATION + if( GetNHApp()->windowlist[wid].type == NHW_MAP ) { + if( Is_rogue_level(&u.uz) ) + mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP), ROGUE_LEVEL_MAP_MODE); + else + mswin_map_mode(mswin_hwnd_from_winid(WIN_MAP), iflags.wc_map_mode); + } +#endif + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CLEAR_WINDOW, (LPARAM)NULL ); + } +} + +/* -- Display the window on the screen. If there is data + pending for output in that window, it should be sent. + If blocking is TRUE, display_nhwindow() will not + return until the data has been displayed on the screen, + and acknowledged by the user where appropriate. + -- All calls are blocking in the tty window-port. + -- Calling display_nhwindow(WIN_MESSAGE,???) will do a + --more--, if necessary, in the tty window-port. +*/ +void mswin_display_nhwindow(winid wid, BOOLEAN_P block) +{ + logDebug("mswin_display_nhwindow(%d, %d)\n", wid, block); + if (GetNHApp()->windowlist[wid].win != NULL) + { + if (GetNHApp()->windowlist[wid].type == NHW_MENU) { + MENU_ITEM_P* p; + mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win, PICK_NONE, &p); + } if (GetNHApp()->windowlist[wid].type == NHW_TEXT) { + mswin_display_text_window(GetNHApp()->windowlist[wid].win); + } if (GetNHApp()->windowlist[wid].type == NHW_RIP) { + mswin_display_RIP_window(GetNHApp()->windowlist[wid].win); + } else { + if( !block ) { + UpdateWindow(GetNHApp()->windowlist[wid].win); + } else { + if ( GetNHApp()->windowlist[wid].type == NHW_MAP ) { + (void) mswin_nhgetch(); + } + } + } + SetFocus(GetNHApp()->hMainWnd); + } +} + + +HWND mswin_hwnd_from_winid(winid wid) +{ + if( wid>=0 && widwindowlist[wid].win; + } else { + return NULL; + } +} + +winid mswin_winid_from_handle(HWND hWnd) +{ + winid i = 0; + + for (i=1; iwindowlist[i].win == hWnd) + return i; + return -1; +} + +winid mswin_winid_from_type(int type) +{ + winid i = 0; + + for (i=1; iwindowlist[i].type == type) + return i; + return -1; +} + +void mswin_window_mark_dead(winid wid) +{ + if( wid>=0 && widwindowlist[wid].win = NULL; + GetNHApp()->windowlist[wid].dead = 1; + } +} + +/* Destroy will dismiss the window if the window has not + * already been dismissed. +*/ +void mswin_destroy_nhwindow(winid wid) +{ + logDebug("mswin_destroy_nhwindow(%d)\n", wid); + + if ((GetNHApp()->windowlist[wid].type == NHW_MAP) || + (GetNHApp()->windowlist[wid].type == NHW_MESSAGE) || + (GetNHApp()->windowlist[wid].type == NHW_STATUS)) { + /* main windows is going to take care of those */ + return; + } + + if (wid != -1) { + if( !GetNHApp()->windowlist[wid].dead && + GetNHApp()->windowlist[wid].win != NULL ) + DestroyWindow(GetNHApp()->windowlist[wid].win); + GetNHApp()->windowlist[wid].win = NULL; + GetNHApp()->windowlist[wid].type = 0; + GetNHApp()->windowlist[wid].dead = 0; + } +} + +/* Next output to window will start at (x,y), also moves + displayable cursor to (x,y). For backward compatibility, + 1 <= x < cols, 0 <= y < rows, where cols and rows are + the size of window. +*/ +void mswin_curs(winid wid, int x, int y) +{ + logDebug("mswin_curs(%d, %d, %d)\n", wid, x, y); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgCursor data; + data.x = x; + data.y = y; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CURSOR, (LPARAM)&data ); + } +} + +/* +putstr(window, attr, str) + -- Print str on the window with the given attribute. Only + printable ASCII characters (040-0126) must be supported. + Multiple putstr()s are output on separate lines. +Attributes + can be one of + ATR_NONE (or 0) + ATR_ULINE + ATR_BOLD + ATR_BLINK + ATR_INVERSE + If a window-port does not support all of these, it may map + unsupported attributes to a supported one (e.g. map them + all to ATR_INVERSE). putstr() may compress spaces out of + str, break str, or truncate str, if necessary for the + display. Where putstr() breaks a line, it has to clear + to end-of-line. + -- putstr should be implemented such that if two putstr()s + are done consecutively the user will see the first and + then the second. In the tty port, pline() achieves this + by calling more() or displaying both on the same line. +*/ +void mswin_putstr(winid wid, int attr, const char *text) +{ + logDebug("mswin_putstr(%d, %d, %s)\n", wid, attr, text); + + mswin_putstr_ex(wid, attr, text, 0); +} + +void mswin_putstr_ex(winid wid, int attr, const char *text, int app) +{ + if( (wid >= 0) && + (wid < MAXWINDOWS) ) + { + if( GetNHApp()->windowlist[wid].win==NULL && + GetNHApp()->windowlist[wid].type==NHW_MENU ) { + GetNHApp()->windowlist[wid].win = mswin_init_menu_window(MENU_TYPE_TEXT); + GetNHApp()->windowlist[wid].dead = 0; + } + + if (GetNHApp()->windowlist[wid].win != NULL) + { + MSNHMsgPutstr data; + ZeroMemory(&data, sizeof(data)); + data.attr = attr; + data.text = text; + data.append = app; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_PUTSTR, (LPARAM)&data ); + } + /* yield a bit so it gets done immediately */ + mswin_get_nh_event(); + } + else + { + // build text to display later in message box + GetNHApp()->saved_text = realloc(GetNHApp()->saved_text, strlen(text) + + strlen(GetNHApp()->saved_text) + 1); + strcat(GetNHApp()->saved_text, text); + } +} + +/* Display the file named str. Complain about missing files + iff complain is TRUE. +*/ +void mswin_display_file(const char *filename,BOOLEAN_P must_exist) +{ + dlb *f; + TCHAR wbuf[BUFSZ]; + + logDebug("mswin_display_file(%s, %d)\n", filename, must_exist); + + f = dlb_fopen(filename, RDTMODE); + if (!f) { + if (must_exist) { + TCHAR message[90]; + _stprintf(message, TEXT("Warning! Could not find file: %s\n"), NH_A2W(filename, wbuf, sizeof(wbuf))); + NHMessageBox(GetNHApp()->hMainWnd, message, MB_OK | MB_ICONEXCLAMATION ); + } + } else { + winid text; + char line[LLEN]; + + text = mswin_create_nhwindow(NHW_TEXT); + + while (dlb_fgets(line, LLEN, f)) { + size_t len; + len = strlen(line); + if( line[len-1]=='\n' ) line[len-1]='\x0'; + mswin_putstr(text, ATR_NONE, line); + } + (void) dlb_fclose(f); + + mswin_display_nhwindow(text, 1); + mswin_destroy_nhwindow(text); + } +} + +/* Start using window as a menu. You must call start_menu() + before add_menu(). After calling start_menu() you may not + putstr() to the window. Only windows of type NHW_MENU may + be used for menus. +*/ +void mswin_start_menu(winid wid) +{ + logDebug("mswin_start_menu(%d)\n", wid); + if( (wid >= 0) && + (wid < MAXWINDOWS) ) { + if( GetNHApp()->windowlist[wid].win==NULL && + GetNHApp()->windowlist[wid].type==NHW_MENU ) { + GetNHApp()->windowlist[wid].win = mswin_init_menu_window(MENU_TYPE_MENU); + GetNHApp()->windowlist[wid].dead = 0; + } + + if(GetNHApp()->windowlist[wid].win != NULL) { + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_STARTMENU, (LPARAM)NULL + ); + } + } +} + +/* +add_menu(windid window, int glyph, const anything identifier, + char accelerator, char groupacc, + int attr, char *str, boolean preselected) + -- Add a text line str to the given menu window. If identifier + is 0, then the line cannot be selected (e.g. a title). + Otherwise, identifier is the value returned if the line is + selected. Accelerator is a keyboard key that can be used + to select the line. If the accelerator of a selectable + item is 0, the window system is free to select its own + accelerator. It is up to the window-port to make the + accelerator visible to the user (e.g. put "a - " in front + of str). The value attr is the same as in putstr(). + Glyph is an optional glyph to accompany the line. If + window port cannot or does not want to display it, this + is OK. If there is no glyph applicable, then this + value will be NO_GLYPH. + -- All accelerators should be in the range [A-Za-z]. + -- It is expected that callers do not mix accelerator + choices. Either all selectable items have an accelerator + or let the window system pick them. Don't do both. + -- Groupacc is a group accelerator. It may be any character + outside of the standard accelerator (see above) or a + number. If 0, the item is unaffected by any group + accelerator. If this accelerator conflicts with + the menu command (or their user defined alises), it loses. + The menu commands and aliases take care not to interfere + with the default object class symbols. + -- If you want this choice to be preselected when the + menu is displayed, set preselected to TRUE. +*/ +void mswin_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel) +{ + logDebug("mswin_add_menu(%d, %d, %p, %c, %c, %d, %s, %d)\n", + wid, glyph, identifier, (char)accelerator, (char)group_accel, + attr, str, presel); + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgAddMenu data; + ZeroMemory(&data, sizeof(data)); + data.glyph = glyph; + data.identifier = identifier; + data.accelerator = accelerator; + data.group_accel = group_accel; + data.attr = attr; + data.str = str; + data.presel = presel; + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ADDMENU, (LPARAM)&data + ); + } +} + +/* +end_menu(window, prompt) + -- Stop adding entries to the menu and flushes the window + to the screen (brings to front?). Prompt is a prompt + to give the user. If prompt is NULL, no prompt will + be printed. + ** This probably shouldn't flush the window any more (if + ** it ever did). That should be select_menu's job. -dean +*/ +void mswin_end_menu(winid wid, const char *prompt) +{ + logDebug("mswin_end_menu(%d, %s)\n", wid, prompt); + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgEndMenu data; + ZeroMemory(&data, sizeof(data)); + data.text = prompt; + + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_ENDMENU, (LPARAM)&data + ); + } +} + +/* +int select_menu(windid window, int how, menu_item **selected) + -- Return the number of items selected; 0 if none were chosen, + -1 when explicitly cancelled. If items were selected, then + selected is filled in with an allocated array of menu_item + structures, one for each selected line. The caller must + free this array when done with it. The "count" field + of selected is a user supplied count. If the user did + not supply a count, then the count field is filled with + -1 (meaning all). A count of zero is equivalent to not + being selected and should not be in the list. If no items + were selected, then selected is NULL'ed out. How is the + mode of the menu. Three valid values are PICK_NONE, + PICK_ONE, and PICK_N, meaning: nothing is selectable, + only one thing is selectable, and any number valid items + may selected. If how is PICK_NONE, this function should + never return anything but 0 or -1. + -- You may call select_menu() on a window multiple times -- + the menu is saved until start_menu() or destroy_nhwindow() + is called on the window. + -- Note that NHW_MENU windows need not have select_menu() + called for them. There is no way of knowing whether + select_menu() will be called for the window at + create_nhwindow() time. +*/ +int mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected) +{ + int nReturned = -1; + + logDebug("mswin_select_menu(%d, %d)\n", wid, how); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + nReturned = mswin_menu_window_select_menu(GetNHApp()->windowlist[wid].win, how, selected); + } + return nReturned; +} + +/* + -- Indicate to the window port that the inventory has been changed. + -- Merely calls display_inventory() for window-ports that leave the + window up, otherwise empty. +*/ +void mswin_update_inventory() +{ + logDebug("mswin_update_inventory()\n"); +} + +/* +mark_synch() -- Don't go beyond this point in I/O on any channel until + all channels are caught up to here. Can be an empty call + for the moment +*/ +void mswin_mark_synch() +{ + logDebug("mswin_mark_synch()\n"); +} + +/* +wait_synch() -- Wait until all pending output is complete (*flush*() for + streams goes here). + -- May also deal with exposure events etc. so that the + display is OK when return from wait_synch(). +*/ +void mswin_wait_synch() +{ + logDebug("mswin_wait_synch()\n"); +} + +/* +cliparound(x, y)-- Make sure that the user is more-or-less centered on the + screen if the playing area is larger than the screen. + -- This function is only defined if CLIPPING is defined. +*/ +void mswin_cliparound(int x, int y) +{ + winid wid = WIN_MAP; + + logDebug("mswin_cliparound(%d, %d)\n", x, y); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgClipAround data; + data.x = x; + data.y = y; + SendMessage( + GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CLIPAROUND, (LPARAM)&data ); + } +} + +/* +print_glyph(window, x, y, glyph) + -- Print the glyph at (x,y) on the given window. Glyphs are + integers at the interface, mapped to whatever the window- + port wants (symbol, font, color, attributes, ...there's + a 1-1 map between glyphs and distinct things on the map). +*/ +void mswin_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph) +{ + logDebug("mswin_print_glyph(%d, %d, %d, %d)\n", wid, x, y, glyph); + + if ((wid >= 0) && + (wid < MAXWINDOWS) && + (GetNHApp()->windowlist[wid].win != NULL)) + { + MSNHMsgPrintGlyph data; + + ZeroMemory(&data, sizeof(data) ); + data.x = x; + data.y = y; + data.glyph = glyph; + SendMessage( GetNHApp()->windowlist[wid].win, + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_PRINT_GLYPH, (LPARAM)&data ); + } +} + +/* +raw_print(str) -- Print directly to a screen, or otherwise guarantee that + the user sees str. raw_print() appends a newline to str. + It need not recognize ASCII control characters. This is + used during startup (before windowing system initialization + -- maybe this means only error startup messages are raw), + for error messages, and maybe other "msg" uses. E.g. + updating status for micros (i.e, "saving"). +*/ +void mswin_raw_print(const char *str) +{ + TCHAR wbuf[255]; + logDebug("mswin_raw_print(%s)\n", str); + if( str && *str ) + NHMessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)), + MB_ICONINFORMATION | MB_OK ); +} + +/* +raw_print_bold(str) + -- Like raw_print(), but prints in bold/standout (if +possible). +*/ +void mswin_raw_print_bold(const char *str) +{ + TCHAR wbuf[255]; + logDebug("mswin_raw_print_bold(%s)\n", str); + if( str && *str ) + NHMessageBox(GetNHApp()->hMainWnd, NH_A2W(str, wbuf, sizeof(wbuf)), + MB_ICONINFORMATION | MB_OK ); +} + +/* +int nhgetch() -- Returns a single character input from the user. + -- In the tty window-port, nhgetch() assumes that tgetch() + will be the routine the OS provides to read a character. + Returned character _must_ be non-zero. +*/ +int mswin_nhgetch() +{ + PMSNHEvent event; + int key = 0; + + logDebug("mswin_nhgetch()\n"); + + + while( (event = mswin_input_pop()) == NULL || + event->type != NHEVENT_CHAR ) + mswin_main_loop(); + + key = event->kbd.ch; + return (key); +} + +/* +int nh_poskey(int *x, int *y, int *mod) + -- Returns a single character input from the user or a + a positioning event (perhaps from a mouse). If the + return value is non-zero, a character was typed, else, + a position in the MAP window is returned in x, y and mod. + mod may be one of + + CLICK_1 -- mouse click type 1 + CLICK_2 -- mouse click type 2 + + The different click types can map to whatever the + hardware supports. If no mouse is supported, this + routine always returns a non-zero character. +*/ +int mswin_nh_poskey(int *x, int *y, int *mod) +{ + PMSNHEvent event; + int key; + + logDebug("mswin_nh_poskey()\n"); + + while( (event = mswin_input_pop())==NULL ) mswin_main_loop(); + + if( event->type==NHEVENT_MOUSE ) { + *mod = event->ms.mod; + *x = event->ms.x; + *y = event->ms.y; + key = 0; + } else { + key = event->kbd.ch; + } + return (key); +} + +/* +nhbell() -- Beep at user. [This will exist at least until sounds are + redone, since sounds aren't attributable to windows anyway.] +*/ +void mswin_nhbell() +{ + logDebug("mswin_nhbell()\n"); +} + +/* +doprev_message() + -- Display previous messages. Used by the ^P command. + -- On the tty-port this scrolls WIN_MESSAGE back one line. +*/ +int mswin_doprev_message() +{ + logDebug("mswin_doprev_message()\n"); + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL); + return 0; +} + +/* +char yn_function(const char *ques, const char *choices, char default) + -- Print a prompt made up of ques, choices and default. + Read a single character response that is contained in + choices or default. If choices is NULL, all possible + inputs are accepted and returned. This overrides + everything else. The choices are expected to be in + lower case. Entering ESC always maps to 'q', or 'n', + in that order, if present in choices, otherwise it maps + to default. Entering any other quit character (SPACE, + RETURN, NEWLINE) maps to default. + -- If the choices string contains ESC, then anything after + it is an acceptable response, but the ESC and whatever + follows is not included in the prompt. + -- If the choices string contains a '#' then accept a count. + Place this value in the global "yn_number" and return '#'. + -- This uses the top line in the tty window-port, other + ports might use a popup. +*/ +char mswin_yn_function(const char *question, const char *choices, + CHAR_P def) +{ + char ch; + char yn_esc_map='\033'; + char message[BUFSZ]; + char res_ch[2]; + int createcaret; + boolean digit_ok, allow_num; + + logDebug("mswin_yn_function(%s, %s, %d)\n", question, choices, def); + + if (WIN_MESSAGE == WIN_ERR && choices == ynchars) { + char *text = realloc(strdup(GetNHApp()->saved_text), strlen(question) + + strlen(GetNHApp()->saved_text) + 1); + DWORD box_result; + strcat(text, question); + box_result = NHMessageBox(NULL, + NH_W2A(text, message, sizeof(message)), + MB_YESNOCANCEL | MB_ICONQUESTION | + ((def == 'y') ? MB_DEFBUTTON1 : + (def == 'n') ? MB_DEFBUTTON2 : MB_DEFBUTTON3)); + free(text); + GetNHApp()->saved_text = strdup(""); + return box_result == IDYES ? 'y' : box_result == IDNO ? 'n' : '\033'; + } + + if (choices) { + char *cb, choicebuf[QBUFSZ]; + + allow_num = (index(choices, '#') != 0); + + Strcpy(choicebuf, choices); + if ((cb = index(choicebuf, '\033')) != 0) { + /* anything beyond is hidden */ + *cb = '\0'; + } + sprintf(message, "%s [%s] ", question, choicebuf); + if (def) sprintf(eos(message), "(%c) ", def); + /* escape maps to 'q' or 'n' or default, in that order */ + yn_esc_map = (index(choices, 'q') ? 'q' : + (index(choices, 'n') ? 'n' : def)); + } else { + Strcpy(message, question); + Strcat(message, " "); + } + + createcaret = 1; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + + mswin_clear_nhwindow(WIN_MESSAGE); + mswin_putstr(WIN_MESSAGE, ATR_BOLD, message); + + /* Only here if main window is not present */ + ch = 0; + do { + ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + ch=mswin_nhgetch(); + HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + if (choices) ch = lowc(ch); + else break; /* If choices is NULL, all possible inputs are accepted and returned. */ + + digit_ok = allow_num && digit(ch); + if (ch=='\033') { + if (index(choices, 'q')) + ch = 'q'; + else if (index(choices, 'n')) + ch = 'n'; + else + ch = def; + break; + } else if (index(quitchars, ch)) { + ch = def; + break; + } else if (!index(choices, ch) && !digit_ok) { + mswin_nhbell(); + ch = (char)0; + /* and try again... */ + } else if (ch == '#' || digit_ok) { + char z, digit_string[2]; + int n_len = 0; + long value = 0; + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, ("#"), 1); n_len++; + digit_string[1] = '\0'; + if (ch != '#') { + digit_string[0] = ch; + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1); n_len++; + value = ch - '0'; + ch = '#'; + } + do { /* loop until we get a non-digit */ + z = lowc(readchar()); + if (digit(z)) { + value = (10 * value) + (z - '0'); + if (value < 0) break; /* overflow: try again */ + digit_string[0] = z; + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, 1); + n_len++; + } else if (z == 'y' || index(quitchars, z)) { + if (z == '\033') value = -1; /* abort */ + z = '\n'; /* break */ + } else if (z == '\b') { + if (n_len <= 1) { value = -1; break; } + else { value /= 10; mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, -1); n_len--; } + } else { + value = -1; /* abort */ + mswin_nhbell(); + break; + } + } while (z != '\n'); + if (value > 0) yn_number = value; + else if (value == 0) ch = 'n'; /* 0 => "no" */ + else { /* remove number from top line, then try again */ + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, digit_string, -n_len); n_len = 0; + ch = (char)0; + } + } + } while( !ch ); + + createcaret = 0; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + + /* display selection in the message window */ + if( isprint(ch) && ch!='#' ) { + res_ch[0] = ch; + res_ch[1] = '\x0'; + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, res_ch, 1); + } + + return ch; +} + +/* +getlin(const char *ques, char *input) + -- Prints ques as a prompt and reads a single line of text, + up to a newline. The string entered is returned without the + newline. ESC is used to cancel, in which case the string + "\033\000" is returned. + -- getlin() must call flush_screen(1) before doing anything. + -- This uses the top line in the tty window-port, other + ports might use a popup. +*/ +void mswin_getlin(const char *question, char *input) +{ + + logDebug("mswin_getlin(%s, %p)\n", question, input); + + if (!iflags.wc_popup_dialog) + { + char c; + int len; + int done; + int createcaret; + + createcaret = 1; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + + mswin_clear_nhwindow(WIN_MESSAGE); + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, question, 0); + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, " ", 1); + input[0] = '\0'; + len = 0; + ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + done = FALSE; + while (!done) + { + c = mswin_nhgetch(); + switch (c) + { + case VK_ESCAPE: + strcpy(input, "\033"); + done = TRUE; + break; + case '\n': + case '\r': + case -115: + done = TRUE; + break; + default: + if (input[0]) + mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, -len); + if (c == VK_BACK) { + if (len > 0) len--; + input[len] = '\0'; + } else { + + input[len++] = c; + input[len] = '\0'; + } + mswin_putstr_ex(WIN_MESSAGE, ATR_NONE, input, 1); + break; + } + } + HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + createcaret = 0; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + } + else + { + if( mswin_getlin_window(question, input, BUFSZ)==IDCANCEL ) { + strcpy(input, "\033"); + } + } +} + +/* +int get_ext_cmd(void) + -- Get an extended command in a window-port specific way. + An index into extcmdlist[] is returned on a successful + selection, -1 otherwise. +*/ +int mswin_get_ext_cmd() +{ + int ret; + logDebug("mswin_get_ext_cmd()\n"); + + if (!iflags.wc_popup_dialog) + { + char c; + char cmd[BUFSZ]; + int i, len; + int createcaret; + + createcaret = 1; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + + cmd[0] = '\0'; + i = -2; + mswin_clear_nhwindow(WIN_MESSAGE); + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, "#", 0); + len = 0; + ShowCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + while (i == -2) + { + int oindex, com_index; + c = mswin_nhgetch(); + switch (c) + { + case VK_ESCAPE: + i = -1; + break; + case '\n': + case '\r': + case -115: + for (i = 0; extcmdlist[i].ef_txt != (char *)0; i++) + if (!strcmpi(cmd, extcmdlist[i].ef_txt)) break; + + if (extcmdlist[i].ef_txt == (char *)0) { + pline("%s: unknown extended command.", cmd); + i = -1; + } + break; + default: + if (cmd[0]) + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd, -(int)strlen(cmd)); + if (c == VK_BACK) + { + if (len > 0) len--; + cmd[len] = '\0'; + } + else + { + + cmd[len++] = c; + cmd[len] = '\0'; + /* Find a command with this prefix in extcmdlist */ + com_index = -1; + for (oindex = 0; extcmdlist[oindex].ef_txt != (char *)0; oindex++) { + if (!strncmpi(cmd, extcmdlist[oindex].ef_txt, len)) { + if (com_index == -1) /* no matches yet */ + com_index = oindex; + else + com_index = -2; /* two matches, don't complete */ + } + } + if (com_index >= 0) { + Strcpy(cmd, extcmdlist[com_index].ef_txt); + } + } + mswin_putstr_ex(WIN_MESSAGE, ATR_BOLD, cmd, 1); + break; + } + } + HideCaret(mswin_hwnd_from_winid(WIN_MESSAGE)); + createcaret = 0; + SendMessage(mswin_hwnd_from_winid(WIN_MESSAGE), + WM_MSNH_COMMAND, (WPARAM)MSNH_MSG_CARET, (LPARAM)&createcaret ); + return i; + } + else + { + if(mswin_ext_cmd_window (&ret) == IDCANCEL) + return -1; + else + return ret; + } +} + + +/* +number_pad(state) + -- Initialize the number pad to the given state. +*/ +void mswin_number_pad(int state) +{ + /* Do Nothing */ + logDebug("mswin_number_pad(%d)\n", state); +} + +/* +delay_output() -- Causes a visible delay of 50ms in the output. + Conceptually, this is similar to wait_synch() followed + by a nap(50ms), but allows asynchronous operation. +*/ +void mswin_delay_output() +{ + logDebug("mswin_delay_output()\n"); + Sleep(50); +} + +void mswin_change_color() +{ + logDebug("mswin_change_color()\n"); +} + +char *mswin_get_color_string() +{ + logDebug("mswin_get_color_string()\n"); + return( "" ); +} + +/* +start_screen() -- Only used on Unix tty ports, but must be declared for + completeness. Sets up the tty to work in full-screen + graphics mode. Look at win/tty/termcap.c for an + example. If your window-port does not need this function + just declare an empty function. +*/ +void mswin_start_screen() +{ + /* Do Nothing */ + logDebug("mswin_start_screen()\n"); +} + +/* +end_screen() -- Only used on Unix tty ports, but must be declared for + completeness. The complement of start_screen(). +*/ +void mswin_end_screen() +{ + /* Do Nothing */ + logDebug("mswin_end_screen()\n"); +} + +/* +outrip(winid, int) + -- The tombstone code. If you want the traditional code use + genl_outrip for the value and check the #if in rip.c. +*/ +#define STONE_LINE_LEN 16 +void mswin_outrip(winid wid, int how) +{ + char buf[BUFSZ]; + + logDebug("mswin_outrip(%d)\n", wid, how); + if ((wid >= 0) && (wid < MAXWINDOWS) ) { + DestroyWindow(GetNHApp()->windowlist[wid].win); + GetNHApp()->windowlist[wid].win = mswin_init_RIP_window(); + GetNHApp()->windowlist[wid].type = NHW_RIP; + GetNHApp()->windowlist[wid].dead = 0; + } + + /* Put name on stone */ + Sprintf(buf, "%s", plname); + buf[STONE_LINE_LEN] = 0; + putstr(wid, 0, buf); + + /* Put $ on stone */ +#ifndef GOLDOBJ + Sprintf(buf, "%ld Au", u.ugold); +#else + Sprintf(buf, "%ld Au", done_money); +#endif + buf[STONE_LINE_LEN] = 0; /* It could be a *lot* of gold :-) */ + putstr(wid, 0, buf); + + /* Put together death description */ + switch (killer_format) { + default: impossible("bad killer format?"); + case KILLED_BY_AN: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, an(killer)); + break; + case KILLED_BY: + Strcpy(buf, killed_by_prefix[how]); + Strcat(buf, killer); + break; + case NO_KILLER_PREFIX: + Strcpy(buf, killer); + break; + } + + /* Put death type on stone */ + putstr(wid, 0, buf); + + /* Put year on stone */ + Sprintf(buf, "%4d", getyear()); + putstr(wid, 0, buf); + mswin_finish_rip_text(wid); +} + +/* handle options updates here */ +void mswin_preference_update(const char *pref) +{ + HDC hdc; + + if( stricmp( pref, "font_menu")==0 || + stricmp( pref, "font_size_menu")==0 ) { + if( iflags.wc_fontsiz_menuNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_menu = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_MENU, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_MENU, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( stricmp( pref, "font_status")==0 || + stricmp( pref, "font_size_status")==0 ) { + + if( iflags.wc_fontsiz_statusNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_status = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_STATUS, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_STATUS, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + InvalidateRect(mswin_hwnd_from_winid(WIN_STATUS), NULL, TRUE); + mswin_layout_main_window(NULL); + return; + } + + if( stricmp( pref, "font_message")==0 || + stricmp( pref, "font_size_message")==0 ) { + + if( iflags.wc_fontsiz_messageNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_message = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_MESSAGE, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_MESSAGE, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE); + mswin_layout_main_window(NULL); + return; + } + + if( stricmp( pref, "font_text")==0 || + stricmp( pref, "font_size_text")==0 ) { + + if( iflags.wc_fontsiz_textNHFONT_SIZE_MAX ) + iflags.wc_fontsiz_text = NHFONT_DEFAULT_SIZE; + + hdc = GetDC(GetNHApp()->hMainWnd); + mswin_get_font(NHW_TEXT, ATR_NONE, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_BOLD, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_DIM, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_ULINE, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_BLINK, hdc, TRUE); + mswin_get_font(NHW_TEXT, ATR_INVERSE, hdc, TRUE); + ReleaseDC(GetNHApp()->hMainWnd, hdc); + + mswin_layout_main_window(NULL); + return; + } + + if( stricmp( pref, "scroll_amount")==0 ) { + mswin_cliparound(u.ux, u.uy); + return; + } + + if( stricmp( pref, "scroll_margin")==0 ) { + mswin_cliparound(u.ux, u.uy); + return; + } + + if( stricmp( pref, "map_mode")==0 ) { + mswin_select_map_mode( iflags.wc_map_mode ); + return; + } + + if( stricmp( pref, "hilite_pet")==0 ) { + InvalidateRect(mswin_hwnd_from_winid(WIN_MAP), NULL, TRUE); + return; + } + + if( stricmp( pref, "align_message")==0 || + stricmp( pref, "align_status")==0 ) { + mswin_layout_main_window(NULL); + return; + } + + if( stricmp( pref, "vary_msgcount")==0 ) { + InvalidateRect(mswin_hwnd_from_winid(WIN_MESSAGE), NULL, TRUE); + mswin_layout_main_window(NULL); + return; + } + +} + + +void mswin_main_loop() +{ + MSG msg; + + while( !mswin_have_input() && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if (GetNHApp()->regNetHackMode || + !TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +} + +/* clean up and quit */ +void bail(const char *mesg) +{ + clearlocks(); + mswin_exit_nhwindows(mesg); + terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +BOOL initMapTiles(void) +{ + HBITMAP hBmp; + BITMAP bm; + TCHAR wbuf[MAX_PATH]; + int tl_num; + SIZE map_size; + extern int total_tiles_used; + + /* no file - no tile */ + if( !(iflags.wc_tile_file && *iflags.wc_tile_file) ) + return TRUE; + + /* load bitmap */ + hBmp = LoadImage( + GetNHApp()->hApp, + NH_A2W(iflags.wc_tile_file, wbuf, MAX_PATH), + IMAGE_BITMAP, + 0, + 0, + LR_LOADFROMFILE | LR_DEFAULTSIZE + ); + if( hBmp==NULL ) { + raw_print("Cannot load tiles from the file. Reverting back to default."); + return FALSE; + } + + /* calculate tile dimensions */ + GetObject(hBmp, sizeof(BITMAP), (LPVOID)&bm); + if( bm.bmWidth%iflags.wc_tile_width || + bm.bmHeight%iflags.wc_tile_height ) { + DeleteObject(hBmp); + raw_print("Tiles bitmap does not match tile_width and tile_height options. Reverting back to default."); + return FALSE; + } + + tl_num = (bm.bmWidth/iflags.wc_tile_width)* + (bm.bmHeight/iflags.wc_tile_height); + if( tl_numbmpMapTiles!=GetNHApp()->bmpTiles ) { + DeleteObject(GetNHApp()->bmpMapTiles); + } + + GetNHApp()->bmpMapTiles = hBmp; + GetNHApp()->mapTile_X = iflags.wc_tile_width; + GetNHApp()->mapTile_Y = iflags.wc_tile_height; + GetNHApp()->mapTilesPerLine = bm.bmWidth / iflags.wc_tile_width; + + map_size.cx = GetNHApp()->mapTile_X * COLNO; + map_size.cy = GetNHApp()->mapTile_Y * ROWNO; + mswin_map_stretch( + mswin_hwnd_from_winid(WIN_MAP), + &map_size, + TRUE + ); + return TRUE; +} + +void mswin_popup_display(HWND hWnd, int* done_indicator) +{ + MSG msg; + HWND hChild; + HMENU hMenu; + int mi_count; + int i; + + /* activate the menu window */ + GetNHApp()->hPopupWnd = hWnd; + + mswin_layout_main_window(hWnd); + + /* disable game windows */ + for( hChild=GetWindow(GetNHApp()->hMainWnd, GW_CHILD); + hChild; + hChild = GetWindow(hChild, GW_HWNDNEXT) ) { + if( hChild!= hWnd) EnableWindow(hChild, FALSE); + } + + /* disable menu */ + hMenu = GetMenu( GetNHApp()->hMainWnd ); + mi_count = GetMenuItemCount( hMenu ); + for( i=0; ihMainWnd ); + + /* bring menu window on top */ + SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + + /* go into message loop */ + while( IsWindow(hWnd) && + (done_indicator==NULL || !*done_indicator) && + GetMessage(&msg, NULL, 0, 0)!=0 ) { + if( !IsDialogMessage(hWnd, &msg) ) { + if (!TranslateAccelerator(msg.hwnd, GetNHApp()->hAccelTable, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } +} + +void mswin_popup_destroy(HWND hWnd) +{ + HWND hChild; + HMENU hMenu; + int mi_count; + int i; + + /* enable game windows */ + for( hChild=GetWindow(GetNHApp()->hMainWnd, GW_CHILD); + hChild; + hChild = GetWindow(hChild, GW_HWNDNEXT) ) { + if( hChild!= hWnd) { + EnableWindow(hChild, TRUE); + } + } + + /* enable menu */ + hMenu = GetMenu( GetNHApp()->hMainWnd ); + mi_count = GetMenuItemCount( hMenu ); + for( i=0; ihMainWnd ); + + SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + GetNHApp()->hPopupWnd = NULL; + mswin_window_mark_dead( mswin_winid_from_handle(hWnd) ); + DestroyWindow(hWnd); + + mswin_layout_main_window(hWnd); + + SetFocus(GetNHApp()->hMainWnd ); +} + +#ifdef _DEBUG +#include + +void +logDebug(const char *fmt, ...) +{ + FILE *dfp = fopen("nhtrace.log", "a"); + + if (dfp) { + va_list args; + + va_start(args, fmt); + vfprintf(dfp, fmt, args); + va_end(args); + fclose(dfp); + } +} + +#endif + + +/* Reading and writing settings from the registry. */ +#define CATEGORYKEY "Software" +#define COMPANYKEY "NetHack" +#define PRODUCTKEY "NetHack 3.4.3" +#define SETTINGSKEY "Settings" +#define MAINSHOWSTATEKEY "MainShowState" +#define MAINMINXKEY "MainMinX" +#define MAINMINYKEY "MainMinY" +#define MAINMAXXKEY "MainMaxX" +#define MAINMAXYKEY "MainMaxY" +#define MAINLEFTKEY "MainLeft" +#define MAINRIGHTKEY "MainRight" +#define MAINTOPKEY "MainTop" +#define MAINBOTTOMKEY "MainBottom" + +/* #define all the subkeys here */ +#define INTFKEY "Interface" + +void +mswin_read_reg() +{ + HKEY key; + DWORD size; + char keystring[MAX_PATH]; + + sprintf(keystring, "%s\\%s\\%s\\%s", + CATEGORYKEY, COMPANYKEY, PRODUCTKEY, SETTINGSKEY); + + /* Set the defaults here. The very first time the app is started, nothing is + read from the registry, so these defaults apply. */ + GetNHApp()->saveRegistrySettings = 1; /* Normally, we always save */ + GetNHApp()->regNetHackMode = 0; + + if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key) + != ERROR_SUCCESS) + return; + + size = sizeof(DWORD); + /* Read the keys here. */ + RegQueryValueEx(key, INTFKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regNetHackMode)), &size); + /* Main window placement */ + RegQueryValueEx(key, MAINSHOWSTATEKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainShowState)), &size); + RegQueryValueEx(key, MAINMINXKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainMinX)), &size); + RegQueryValueEx(key, MAINMINYKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainMinY)), &size); + RegQueryValueEx(key, MAINMAXXKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainMaxX)), &size); + RegQueryValueEx(key, MAINMAXYKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainMaxY)), &size); + RegQueryValueEx(key, MAINLEFTKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainLeft)), &size); + RegQueryValueEx(key, MAINRIGHTKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainRight)), &size); + RegQueryValueEx(key, MAINTOPKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainTop)), &size); + RegQueryValueEx(key, MAINBOTTOMKEY, 0, NULL, (unsigned char *)(&(GetNHApp()->regMainBottom)), &size); + + RegCloseKey(key); +} + +void +mswin_write_reg() +{ + HKEY key; + DWORD disposition; + + if (GetNHApp()->saveRegistrySettings) + { + char keystring[MAX_PATH]; + + sprintf(keystring, "%s\\%s\\%s\\%s", + CATEGORYKEY, COMPANYKEY, PRODUCTKEY, SETTINGSKEY); + + if (RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_WRITE, &key) != ERROR_SUCCESS) + { + RegCreateKeyEx(HKEY_CURRENT_USER, keystring, 0, "", + REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, &disposition); + } + + /* Write the keys here */ + RegSetValueEx(key, INTFKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regNetHackMode)), sizeof(DWORD)); + /* Main window placement */ + RegSetValueEx(key, MAINSHOWSTATEKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainShowState)), sizeof(DWORD)); + RegSetValueEx(key, MAINMINXKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainMinX)), sizeof(DWORD)); + RegSetValueEx(key, MAINMINYKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainMinY)), sizeof(DWORD)); + RegSetValueEx(key, MAINMAXXKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainMaxX)), sizeof(DWORD)); + RegSetValueEx(key, MAINMAXYKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainMaxY)), sizeof(DWORD)); + RegSetValueEx(key, MAINLEFTKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainLeft)), sizeof(DWORD)); + RegSetValueEx(key, MAINRIGHTKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainRight)), sizeof(DWORD)); + RegSetValueEx(key, MAINTOPKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainTop)), sizeof(DWORD)); + RegSetValueEx(key, MAINBOTTOMKEY, 0, REG_DWORD, (unsigned char *)(&(GetNHApp()->regMainBottom)), sizeof(DWORD)); + + RegCloseKey(key); + } +} + +void +mswin_destroy_reg() +{ + char keystring[MAX_PATH]; + HKEY key; + DWORD nrsubkeys; + + /* Delete keys one by one, as NT does not delete trees */ + sprintf(keystring, "%s\\%s\\%s\\%s", + CATEGORYKEY, COMPANYKEY, PRODUCTKEY, SETTINGSKEY); + RegDeleteKey(HKEY_CURRENT_USER, keystring); + sprintf(keystring, "%s\\%s\\%s", + CATEGORYKEY, COMPANYKEY, PRODUCTKEY); + RegDeleteKey(HKEY_CURRENT_USER, keystring); + /* The company key will also contain information about newer versions + of nethack (e.g. a subkey called NetHack 4.0), so only delete that + if it's empty now. */ + sprintf(keystring, "%s\\%s", CATEGORYKEY, COMPANYKEY); + /* If we cannot open it, we probably cannot delete it either... Just + go on and see what happens. */ + RegOpenKeyEx(HKEY_CURRENT_USER, keystring, 0, KEY_READ, &key); + nrsubkeys = 0; + RegQueryInfoKey(key, NULL, NULL, NULL, &nrsubkeys, NULL, NULL, NULL, + NULL, NULL, NULL, NULL); + RegCloseKey(key); + if (nrsubkeys == 0) + RegDeleteKey(HKEY_CURRENT_USER, keystring); + + /* Prevent saving on exit */ + GetNHApp()->saveRegistrySettings = 0; +} + +typedef struct ctv +{ + const char *colorstring; + COLORREF colorvalue; +} color_table_value; + +/* + * The color list here is a combination of: + * NetHack colors. (See mhmap.c) + * HTML colors. (See http://www.w3.org/TR/REC-html40/types.html#h-6.5 ) + */ + +static color_table_value color_table[] = { +/* NetHack colors */ + { "black", RGB(0x55, 0x55, 0x55)}, + { "red", RGB(0xFF, 0x00, 0x00)}, + { "green", RGB(0x00, 0x80, 0x00)}, + { "brown", RGB(0xA5, 0x2A, 0x2A)}, + { "blue", RGB(0x00, 0x00, 0xFF)}, + { "magenta", RGB(0xFF, 0x00, 0xFF)}, + { "cyan", RGB(0x00, 0xFF, 0xFF)}, + { "orange", RGB(0xFF, 0xA5, 0x00)}, + { "brightgreen", RGB(0x00, 0xFF, 0x00)}, + { "yellow", RGB(0xFF, 0xFF, 0x00)}, + { "brightblue", RGB(0x00, 0xC0, 0xFF)}, + { "brightmagenta", RGB(0xFF, 0x80, 0xFF)}, + { "brightcyan", RGB(0x80, 0xFF, 0xFF)}, + { "white", RGB(0xFF, 0xFF, 0xFF)}, +/* Remaining HTML colors */ + { "trueblack", RGB(0x00, 0x00, 0x00)}, + { "gray", RGB(0x80, 0x80, 0x80)}, + { "grey", RGB(0x80, 0x80, 0x80)}, + { "purple", RGB(0x80, 0x00, 0x80)}, + { "silver", RGB(0xC0, 0xC0, 0xC0)}, + { "maroon", RGB(0x80, 0x00, 0x00)}, + { "fuchsia", RGB(0xFF, 0x00, 0xFF)}, /* = NetHack magenta */ + { "lime", RGB(0x00, 0xFF, 0x00)}, /* = NetHack bright green */ + { "olive", RGB(0x80, 0x80, 0x00)}, + { "navy", RGB(0x00, 0x00, 0x80)}, + { "teal", RGB(0x00, 0x80, 0x80)}, + { "aqua", RGB(0x00, 0xFF, 0xFF)}, /* = NetHack cyan */ + { "", RGB(0x00, 0x00, 0x00)}, +}; + +typedef struct ctbv +{ + char *colorstring; + int syscolorvalue; +} color_table_brush_value; + +static color_table_brush_value color_table_brush[] = { + { "activeborder", COLOR_ACTIVEBORDER }, + { "activecaption", COLOR_ACTIVECAPTION }, + { "appworkspace", COLOR_APPWORKSPACE }, + { "background", COLOR_BACKGROUND }, + { "btnface", COLOR_BTNFACE }, + { "btnshadow", COLOR_BTNSHADOW }, + { "btntext", COLOR_BTNTEXT }, + { "captiontext", COLOR_CAPTIONTEXT }, + { "graytext", COLOR_GRAYTEXT }, + { "greytext", COLOR_GRAYTEXT }, + { "highlight", COLOR_HIGHLIGHT }, + { "highlighttext", COLOR_HIGHLIGHTTEXT }, + { "inactiveborder", COLOR_INACTIVEBORDER }, + { "inactivecaption", COLOR_INACTIVECAPTION }, + { "menu", COLOR_MENU }, + { "menutext", COLOR_MENUTEXT }, + { "scrollbar", COLOR_SCROLLBAR }, + { "window", COLOR_WINDOW }, + { "windowframe", COLOR_WINDOWFRAME }, + { "windowtext", COLOR_WINDOWTEXT }, + { "", -1 }, +}; + +static void mswin_color_from_string(char *colorstring, HBRUSH* brushptr, COLORREF *colorptr) +{ + color_table_value *ctv_ptr = color_table; + color_table_brush_value *ctbv_ptr = color_table_brush; + int red_value, blue_value, green_value; + static char *hexadecimals = "0123456789abcdef"; + + if (colorstring == NULL) return; + if (*colorstring == '#') { + if (strlen(++colorstring) != 6) return; + + red_value = index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + red_value *= 16; + red_value += index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + + green_value = index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + green_value *= 16; + green_value += index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + + blue_value = index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + blue_value *= 16; + blue_value += index(hexadecimals, tolower(*colorstring++)) - hexadecimals; + + *colorptr = RGB(red_value, green_value, blue_value); + } else { + while (*ctv_ptr->colorstring && stricmp(ctv_ptr->colorstring, colorstring)) + ++ctv_ptr; + if (*ctv_ptr->colorstring) { + *colorptr = ctv_ptr->colorvalue; + } else { + while (*ctbv_ptr->colorstring && stricmp(ctbv_ptr->colorstring, colorstring)) + ++ctbv_ptr; + if (*ctbv_ptr->colorstring) { + *brushptr = SYSCLR_TO_BRUSH(ctbv_ptr->syscolorvalue); + *colorptr = GetSysColor(ctbv_ptr->syscolorvalue); + } + } + } + if (max_brush > TOTAL_BRUSHES) panic("Too many colors!"); + *brushptr = CreateSolidBrush(*colorptr); + brush_table[max_brush++] = *brushptr; +} + +int NHMessageBox(HWND hWnd, LPCTSTR text, UINT type) +{ + TCHAR title[MAX_LOADSTRING]; + + LoadString(GetNHApp()->hApp, IDS_APP_TITLE_SHORT, title, MAX_LOADSTRING); + + return MessageBox(hWnd, text, title, type); +} diff --git a/win/win32/nethack.dsw b/win/win32/nethack.dsw new file mode 100644 index 0000000..ff30f52 --- /dev/null +++ b/win/win32/nethack.dsw @@ -0,0 +1,212 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "NetHackW"=.\build\NetHackW.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgncomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name dlb_main + End Project Dependency + Begin Project Dependency + Project_Dep_Name levcomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency + Begin Project Dependency + Project_Dep_Name tilemap + End Project Dependency + Begin Project Dependency + Project_Dep_Name tiles + End Project Dependency + Begin Project Dependency + Project_Dep_Name uudecode + End Project Dependency +}}} + +############################################################################### + +Project: "dgncomp"=.\build\dgncomp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgnstuff + End Project Dependency +}}} + +############################################################################### + +Project: "dgnstuff"=.\build\dgnstuff.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "dlb_main"=.\build\dlb_main.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name dgncomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name levcomp + End Project Dependency + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "levcomp"=.\build\levcomp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name levstuff + End Project Dependency +}}} + +############################################################################### + +Project: "levstuff"=.\build\levstuff.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency +}}} + +############################################################################### + +Project: "makedefs"=.\build\makedefs.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "recover"=.\build\recover.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name makedefs + End Project Dependency + Begin Project Dependency + Project_Dep_Name dlb_main + End Project Dependency +}}} + +############################################################################### + +Project: "tile2bmp"=.\build\tile2bmp.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tilemap"=.\build\tilemap.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Project: "tiles"=.\build\tiles.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name tile2bmp + End Project Dependency +}}} + +############################################################################### + +Project: "uudecode"=.\build\uudecode.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/win/win32/nethackw.dsp b/win/win32/nethackw.dsp new file mode 100644 index 0000000..03521aa --- /dev/null +++ b/win/win32/nethackw.dsp @@ -0,0 +1,1107 @@ +# Microsoft Developer Studio Project File - Name="NetHackW" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=NetHackW - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "NetHackW.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "NetHackW.mak" CFG="NetHackW - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "NetHackW - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "NetHackW - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "NetHackW - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Og /Oy /Ob1 /Gs /Gf /Gy /Oi- /Ot /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /Og /Oy /Ob1 /Gs /Gf /Gy /Oi- /Ot /I "..\win\win32" /I "..\include" /I "..\sys\winnt" /I "..\sys\share" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /map /debug /machine:I386 /MAPINFO:EXPORTS /MAPINFO:LINES +# SUBTRACT LINK32 /pdb:none +# Begin Special Build Tool +OutDir=.\Release +SOURCE="$(InputPath)" +PostBuild_Desc=Install exe +PostBuild_Cmds=copy $(OutDir)\NetHackW.exe ..\binary \ +copy ..\dat\nhdat ..\binary \ +copy ..\dat\license ..\binary \ +if exist tiles.bmp copy tiles.bmp ..\binary \ +if exist ..\doc\Guidebook.txt copy ..\doc\Guidebook.txt ..\binary\Guidebook.txt \ +if exist ..\doc\nethack.txt copy ..\doc\nethack.txt ..\binary\NetHack.txt \ +copy ..\sys\winnt\defaults.nh ..\binary\defaults.nh +# End Special Build Tool + +!ELSEIF "$(CFG)" == "NetHackW - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\win\win32" /I "..\include" /I "..\sys\winnt" /I "..\sys\share" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comctl32.lib advapi32.lib winmm.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Debug +SOURCE="$(InputPath)" +PostBuild_Desc=Install exe +PostBuild_Cmds=if NOT exist ..\binary\*.* mkdir ..\binary \ +copy $(OutDir)\NetHackW.exe ..\binary \ +copy ..\dat\nhdat ..\binary \ +copy ..\dat\license ..\binary \ +if exist tiles.bmp copy tiles.bmp ..\binary \ +if exist ..\doc\Guidebook.txt copy ..\doc\Guidebook.txt ..\binary\Guidebook.txt \ +if exist ..\doc\nethack.txt copy ..\doc\nethack.txt ..\binary\NetHack.txt \ +copy ..\sys\winnt\defaults.nh ..\binary\defaults.nh +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "NetHackW - Win32 Release" +# Name "NetHackW - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\allmain.c +# End Source File +# Begin Source File + +SOURCE=..\src\alloc.c +# End Source File +# Begin Source File + +SOURCE=..\src\apply.c +# End Source File +# Begin Source File + +SOURCE=..\src\artifact.c +# End Source File +# Begin Source File + +SOURCE=..\src\attrib.c +# End Source File +# Begin Source File + +SOURCE=..\src\ball.c +# End Source File +# Begin Source File + +SOURCE=..\src\bones.c +# End Source File +# Begin Source File + +SOURCE=..\src\botl.c +# End Source File +# Begin Source File + +SOURCE=..\src\cmd.c +# End Source File +# Begin Source File + +SOURCE=..\src\dbridge.c +# End Source File +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\detect.c +# End Source File +# Begin Source File + +SOURCE=..\src\dig.c +# End Source File +# Begin Source File + +SOURCE=..\src\display.c +# End Source File +# Begin Source File + +SOURCE=..\src\dlb.c +# End Source File +# Begin Source File + +SOURCE=..\src\do.c +# End Source File +# Begin Source File + +SOURCE=..\src\do_name.c +# End Source File +# Begin Source File + +SOURCE=..\src\do_wear.c +# End Source File +# Begin Source File + +SOURCE=..\src\dog.c +# End Source File +# Begin Source File + +SOURCE=..\src\dogmove.c +# End Source File +# Begin Source File + +SOURCE=..\src\dokick.c +# End Source File +# Begin Source File + +SOURCE=..\src\dothrow.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\src\dungeon.c +# End Source File +# Begin Source File + +SOURCE=..\src\eat.c +# End Source File +# Begin Source File + +SOURCE=..\src\end.c +# End Source File +# Begin Source File + +SOURCE=..\src\engrave.c +# End Source File +# Begin Source File + +SOURCE=..\src\exper.c +# End Source File +# Begin Source File + +SOURCE=..\src\explode.c +# End Source File +# Begin Source File + +SOURCE=..\src\extralev.c +# End Source File +# Begin Source File + +SOURCE=..\src\files.c +# End Source File +# Begin Source File + +SOURCE=..\src\fountain.c +# End Source File +# Begin Source File + +SOURCE=..\win\tty\getline.c +# End Source File +# Begin Source File + +SOURCE=..\src\hack.c +# End Source File +# Begin Source File + +SOURCE=..\src\hacklib.c +# End Source File +# Begin Source File + +SOURCE=..\src\invent.c +# End Source File +# Begin Source File + +SOURCE=..\src\light.c +# End Source File +# Begin Source File + +SOURCE=..\src\lock.c +# End Source File +# Begin Source File + +SOURCE=..\src\mail.c +# End Source File +# Begin Source File + +SOURCE=..\src\makemon.c +# End Source File +# Begin Source File + +SOURCE=..\src\mapglyph.c +# End Source File +# Begin Source File + +SOURCE=..\src\mcastu.c +# End Source File +# Begin Source File + +SOURCE=..\src\mhitm.c +# End Source File +# Begin Source File + +SOURCE=..\src\mhitu.c +# End Source File +# Begin Source File + +SOURCE=..\src\minion.c +# End Source File +# Begin Source File + +SOURCE=..\src\mklev.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkmap.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkmaze.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkobj.c +# End Source File +# Begin Source File + +SOURCE=..\src\mkroom.c +# End Source File +# Begin Source File + +SOURCE=..\src\mon.c +# End Source File +# Begin Source File + +SOURCE=..\src\mondata.c +# End Source File +# Begin Source File + +SOURCE=..\src\monmove.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\monstr.c +# End Source File +# Begin Source File + +SOURCE=..\src\mplayer.c +# End Source File +# Begin Source File + +SOURCE=..\src\mthrowu.c +# End Source File +# Begin Source File + +SOURCE=..\src\muse.c +# End Source File +# Begin Source File + +SOURCE=..\src\music.c +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\ntsound.c +# End Source File +# Begin Source File + +SOURCE=..\src\o_init.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\src\objnam.c +# End Source File +# Begin Source File + +SOURCE=..\src\options.c +# End Source File +# Begin Source File + +SOURCE=..\src\pager.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcmain.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcsys.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\pcunix.c +# End Source File +# Begin Source File + +SOURCE=..\src\pickup.c +# End Source File +# Begin Source File + +SOURCE=..\src\pline.c +# End Source File +# Begin Source File + +SOURCE=..\src\polyself.c +# End Source File +# Begin Source File + +SOURCE=..\src\potion.c +# End Source File +# Begin Source File + +SOURCE=..\src\pray.c +# End Source File +# Begin Source File + +SOURCE=..\src\priest.c +# End Source File +# Begin Source File + +SOURCE=..\src\quest.c +# End Source File +# Begin Source File + +SOURCE=..\src\questpgr.c +# End Source File +# Begin Source File + +SOURCE=..\sys\share\random.c +# End Source File +# Begin Source File + +SOURCE=..\src\read.c +# End Source File +# Begin Source File + +SOURCE=..\src\rect.c +# End Source File +# Begin Source File + +SOURCE=..\src\region.c +# End Source File +# Begin Source File + +SOURCE=..\src\restore.c +# End Source File +# Begin Source File + +SOURCE=..\src\rip.c +# End Source File +# Begin Source File + +SOURCE=..\src\rnd.c +# End Source File +# Begin Source File + +SOURCE=..\src\role.c +# End Source File +# Begin Source File + +SOURCE=..\src\rumors.c +# End Source File +# Begin Source File + +SOURCE=..\src\save.c +# End Source File +# Begin Source File + +SOURCE=..\src\shk.c +# End Source File +# Begin Source File + +SOURCE=..\src\shknam.c +# End Source File +# Begin Source File + +SOURCE=..\src\sit.c +# End Source File +# Begin Source File + +SOURCE=..\src\sounds.c +# End Source File +# Begin Source File + +SOURCE=..\src\sp_lev.c +# End Source File +# Begin Source File + +SOURCE=..\src\spell.c +# End Source File +# Begin Source File + +SOURCE=..\src\steal.c +# End Source File +# Begin Source File + +SOURCE=..\src\steed.c +# End Source File +# Begin Source File + +SOURCE=..\src\teleport.c +# End Source File +# Begin Source File + +SOURCE=..\src\tile.c +# End Source File +# Begin Source File + +SOURCE=..\src\timeout.c +# End Source File +# Begin Source File + +SOURCE=..\src\topten.c +# End Source File +# Begin Source File + +SOURCE=..\src\track.c +# End Source File +# Begin Source File + +SOURCE=..\src\trap.c +# End Source File +# Begin Source File + +SOURCE=..\src\u_init.c +# End Source File +# Begin Source File + +SOURCE=..\src\uhitm.c +# End Source File +# Begin Source File + +SOURCE=..\src\vault.c +# End Source File +# Begin Source File + +SOURCE=..\src\version.c +# End Source File +# Begin Source File + +SOURCE=..\src\vision.c +# End Source File +# Begin Source File + +SOURCE=..\src\weapon.c +# End Source File +# Begin Source File + +SOURCE=..\src\were.c +# End Source File +# Begin Source File + +SOURCE=..\src\wield.c +# End Source File +# Begin Source File + +SOURCE=..\src\windows.c +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\winnt.c +# End Source File +# Begin Source File + +SOURCE=..\win\tty\wintty.c +# End Source File +# Begin Source File + +SOURCE=..\src\wizard.c +# End Source File +# Begin Source File + +SOURCE=..\src\worm.c +# End Source File +# Begin Source File + +SOURCE=..\src\worn.c +# End Source File +# Begin Source File + +SOURCE=..\src\write.c +# End Source File +# Begin Source File + +SOURCE=..\src\zap.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\amiconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\artifact.h +# End Source File +# Begin Source File + +SOURCE=..\include\artilist.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\beconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\bitmfile.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\def_os2.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dlb.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\edog.h +# End Source File +# Begin Source File + +SOURCE=..\include\emin.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\epri.h +# End Source File +# Begin Source File + +SOURCE=..\include\eshk.h +# End Source File +# Begin Source File + +SOURCE=..\include\extern.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\func_tab.h +# End Source File +# Begin Source File + +SOURCE=..\include\gem_rsc.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\hack.h +# End Source File +# Begin Source File + +SOURCE=..\include\lev.h +# End Source File +# Begin Source File + +SOURCE=..\include\load_img.h +# End Source File +# Begin Source File + +SOURCE=..\include\macconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\macpopup.h +# End Source File +# Begin Source File + +SOURCE=..\include\mactty.h +# End Source File +# Begin Source File + +SOURCE=..\include\macwin.h +# End Source File +# Begin Source File + +SOURCE=..\include\mail.h +# End Source File +# Begin Source File + +SOURCE=..\include\mfndpos.h +# End Source File +# Begin Source File + +SOURCE=..\include\micro.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\mondata.h +# End Source File +# Begin Source File + +SOURCE=..\include\monflag.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\mttypriv.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\os2conf.h +# End Source File +# Begin Source File + +SOURCE=..\include\patchlevel.h +# End Source File +# Begin Source File + +SOURCE=..\include\pcconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_clust.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_kde0.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_win.h +# End Source File +# Begin Source File + +SOURCE=..\include\qt_xpms.h +# End Source File +# Begin Source File + +SOURCE=..\include\qtext.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\sp_lev.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\system.h +# End Source File +# Begin Source File + +SOURCE=..\include\tcap.h +# End Source File +# Begin Source File + +SOURCE=..\include\tile2x11.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tosconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\unixconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\vault.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\vmsconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\winami.h +# End Source File +# Begin Source File + +SOURCE=..\include\wingem.h +# End Source File +# Begin Source File + +SOURCE=..\include\winGnome.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\winX.h +# End Source File +# Begin Source File + +SOURCE=..\include\xwindow.h +# End Source File +# Begin Source File + +SOURCE=..\include\xwindowp.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\win\win32\bitmap1.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\bitmap2.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mnsel.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mnunsel.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\NETHACK.ICO +# End Source File +# Begin Source File + +SOURCE=..\win\win32\small.ico +# End Source File +# Begin Source File + +SOURCE=..\win\win32\tiles.bmp +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.ico +# End Source File +# End Group +# Begin Group "wnd" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\win\win32\mhaskyn.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhaskyn.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhdlg.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhdlg.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhfont.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhfont.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhinput.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhinput.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmain.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmain.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmap.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmap.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmenu.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmenu.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsg.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsgwnd.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhmsgwnd.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhsplash.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhrip.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhsplash.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhrip.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhstatus.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhstatus.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhtext.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mhtext.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\mswproc.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\resource.h +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.c +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winhack.rc +# End Source File +# Begin Source File + +SOURCE=..\win\win32\winMS.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\win\win32\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/win/win32/petmark.uu b/win/win32/petmark.uu new file mode 100644 index 0000000..deace34 --- /dev/null +++ b/win/win32/petmark.uu @@ -0,0 +1,9 @@ +begin 600 petmark.bmp +M0DWV`````````'8````H````$````!`````!``0``````(`````````````` +M````````````;&Q'````_P```````("``(````"``(``@(```,#`P`#`W,`` +M\,JF``0$!``("`@`#`P,`!$1$0`6%A8`'!P<```````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````````$````` +5````$1````````$1$0```````!`0 +` +end diff --git a/win/win32/recover.dsp b/win/win32/recover.dsp new file mode 100644 index 0000000..7052afa --- /dev/null +++ b/win/win32/recover.dsp @@ -0,0 +1,148 @@ +# Microsoft Developer Studio Project File - Name="recover" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=recover - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "recover.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "recover.mak" CFG="recover - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "recover - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "recover - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "recover - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Release +SOURCE="$(InputPath)" +PostBuild_Cmds=copy $(OutDir)\recover.exe ..\binary \ +if exist ..\doc\recover.txt copy ..\doc\recover.txt ..\binary\recover.txt +# End Special Build Tool + +!ELSEIF "$(CFG)" == "recover - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\include" /I "..\sys\winnt" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Debug +SOURCE="$(InputPath)" +PostBuild_Desc=install exe +PostBuild_Cmds=copy $(OutDir)\recover.exe ..\binary \ +if exist ..\doc\recover.txt copy ..\doc\recover.txt ..\binary\recover.txt +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "recover - Win32 Release" +# Name "recover - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\util\recover.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\sys\winnt\win32api.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/resource.h b/win/win32/resource.h new file mode 100644 index 0000000..00a29a6 --- /dev/null +++ b/win/win32/resource.h @@ -0,0 +1,151 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by winhack.rc +// +#define IDC_MYICON 2 +#define IDD_WINHACK_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDS_HELLO 106 +#define IDI_NETHACKW 107 +#define IDC_NETHACKW 109 +#define IDS_APP_TITLE_SHORT 110 +#define IDR_MAINFRAME 128 +#define IDB_TILES 129 +#define IDD_TEXT 130 +#define IDD_NHTEXT 130 +#define IDD_MENU 132 +#define IDB_MENU_SEL 133 +#define IDB_MENU_UNSEL 134 +#define IDD_COMMANDS 136 +#define IDD_GETLIN 138 +#define IDD_EXTCMD 139 +#define IDD_PLAYER_SELECTOR 141 +#define IDB_PETMARK 143 +#define IDB_MENU_SEL_COUNT 144 +#define IDD_NHRIP 145 +#define IDB_SPLASH 146 +#define IDB_RIP 147 +#define IDD_SPLASH 148 +#define IDC_TEXT_VIEW 1000 +#define IDC_TEXT_CONTROL 1000 +#define IDC_CMD_MOVE_NW 1001 +#define IDC_CMD_MOVE_N 1002 +#define IDC_MENU_LIST 1003 +#define IDC_CMD_MOVE_NE 1003 +#define IDC_MENU_TEXT 1004 +#define IDC_CMD_MOVE_W 1004 +#define IDC_CMD_MOVE_SELF 1005 +#define IDC_CMD_MOVE_E 1006 +#define IDC_CMD_MOVE_SW 1007 +#define IDC_CMD_MOVE_S 1008 +#define IDC_CMD_MOVE_SE 1009 +#define IDC_CMD_MOVE_UP 1010 +#define IDC_CMD_MOVE_DOWN 1011 +#define IDC_CMD_5 1012 +#define IDC_CMD_A 1013 +#define IDC_CMD_B 1014 +#define IDC_CMD_C 1015 +#define IDC_CMD_D 1016 +#define IDC_CMD_E 1017 +#define IDC_CMD_F 1018 +#define IDC_CMD_G 1019 +#define IDC_CMD_H 1020 +#define IDC_CMD_I 1021 +#define IDC_CMD_J 1022 +#define IDC_CMD_K 1023 +#define IDC_CMD_L 1024 +#define IDC_CMD_M 1025 +#define IDC_CMD_N 1026 +#define IDC_CMD_O 1027 +#define IDC_CMD_P 1028 +#define IDC_CMD_Q 1029 +#define IDC_CMD_R 1030 +#define IDC_CMD_S 1031 +#define IDC_CMD_T 1032 +#define IDC_CMD_U 1033 +#define IDC_CMD_V 1034 +#define IDC_CMD_W 1035 +#define IDC_CMD_X 1036 +#define IDC_CMD_Y 1037 +#define IDC_CMD_Z 1038 +#define IDC_CMD_AA 1039 +#define IDC_CMD_BB 1040 +#define IDC_CMD_CC 1041 +#define IDC_CMD_DD 1042 +#define IDC_CMD_EE 1043 +#define IDC_CMD_FF 1044 +#define IDC_CMD_GG 1045 +#define IDC_CMD_HH 1046 +#define IDC_CMD_II 1047 +#define IDC_CMD_JJ 1048 +#define IDC_CMD_KK 1049 +#define IDC_CMD_LL 1050 +#define IDC_CMD_MM 1051 +#define IDC_CMD_NN 1052 +#define IDC_CMD_OO 1053 +#define IDC_CMD_PP 1054 +#define IDC_CMD_QQ 1055 +#define IDC_CMD_RR 1056 +#define IDC_CMD_SS 1057 +#define IDC_CMD_TT 1058 +#define IDC_CMD_UU 1059 +#define IDC_CMD_VV 1060 +#define IDC_CMD_WW 1061 +#define IDC_CMD_XX 1062 +#define IDC_CMD_YY 1063 +#define IDC_CMD_ZZ 1064 +#define IDC_CMD_FIRST 1100 +#define IDC_CMD_LAST 1300 +#define IDC_GETLIN_EDIT 1309 +#define IDC_EXTCMD_LIST 1310 +#define IDC_PLSEL_NAME 1314 +#define IDC_PLSEL_ROLE_RANDOM 1315 +#define IDC_PLSEL_RACE_RANDOM 1318 +#define IDC_PLSEL_GENDER_RANDOM 1319 +#define IDC_PLSEL_ALIGN_RANDOM 1320 +#define IDC_PLSEL_ROLE_LIST 1323 +#define IDC_PLSEL_RACE_LIST 1324 +#define IDC_PLSEL_ALIGN_LIST 1325 +#define IDC_PLSEL_GENDER_LIST 1326 +#define IDC_ABOUT_VERSION 1327 +#define IDC_ABOUT_COPYRIGHT 1328 +#define IDC_EXTRAINFO 1331 +#define IDM_SAVE 32771 +#define IDM_HELP_LONG 32772 +#define IDM_HELP_COMMANDS 32773 +#define IDM_HELP_HISTORY 32774 +#define IDM_HELP_INFO_CHAR 32775 +#define IDM_HELP_INFO_KEY 32776 +#define IDM_HELP_OPTIONS 32777 +#define IDM_HELP_OPTIONS_LONG 32778 +#define IDM_HELP_EXTCMD 32779 +#define IDM_HELP_LICENSE 32780 +#define IDM_HELP_PORTHELP 32781 +#define IDM_MAP_TILES 32782 +#define IDM_MAP_ASCII4X6 32783 +#define IDM_MAP_ASCII6X8 32784 +#define IDM_MAP_ASCII8X8 32785 +#define IDM_MAP_ASCII16X8 32786 +#define IDM_MAP_ASCII7X12 32787 +#define IDM_MAP_ASCII8X12 32788 +#define IDM_MAP_ASCII16X12 32789 +#define IDM_MAP_ASCII12X16 32790 +#define IDM_MAP_ASCII10X18 32791 +#define IDM_MAP_FIT_TO_SCREEN 32792 +#define IDM_NHMODE 32794 +#define IDM_CLEARSETTINGS 32795 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 145 +#define _APS_NEXT_COMMAND_VALUE 32796 +#define _APS_NEXT_CONTROL_VALUE 1332 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/win/win32/rip.uu b/win/win32/rip.uu new file mode 100644 index 0000000..c1da8e6 --- /dev/null +++ b/win/win32/rip.uu @@ -0,0 +1,1805 @@ +begin 600 rip.bmp +M0DVV/`$``````#8$```H````D`$``,@````!``@``````(`X`0`2"P``$@L` +M```!`````0``+C8V`$9!00`Q+R\`(S4U``)'`P`*+A4`'C$P`"LI*0`1-2,` +M&B(B`**FI@`X.CH`:W1U`!0:&0!X?G\`#1(2`!,G'P`*,Q8``3X#`!88&``! +M.@,``C@$`!06%@!-1D8`)20D``H,#``,%14`145%`#=+2P`/,!\`#RX?`#\_ +M/P`]/3T`%1L;``TN&@"#AX@`+2TM`"DI*0![?X``$R\G`#0Q,0!634T`#A@8 +M`+&TM`"2F)@`(28E`(J0D``8'!P`?8*#`&1L;0`?("``8FP`)"GZ=`+"8 +MJ``I1<8`559;`,"RM0!(7,@`#@\0`%EOO`!_E\\`5B@>`#4_0`!_;WX`04F- +M`')P<@#5U-X`SVQ$`*FMKP"B/R<`?HF\`-?+S0#V_?X`XO+T`.#@YP#.VMT` +MX^KK`+-9/`"BAYD`)T2E`$AM@@#___\`,4)F`,3$T@!C?<``+#-K````0``` +M`(````"@````P````.````#_``!`````0$```$"```!`H```0,```$#@``!` +M_P``@````(!```"`@```@*```(#```"`X```@/\``*````"@0```H(```*"@ +M``"@P```H.```*#_``#`````P$```,"```#`H```P,```,#@``#`_P``X``` +M`.!```#@@```X*```.#```#@X```X/\``/\```#_0```_X```/^@``#_P``` +M_^```/__`$````!``$``0`"``$``H`!``,``0`#@`$``_P!`0```0$!``$!` +M@`!`0*``0$#``$!`X`!`0/\`0(```$"`0`!`@(``0("@`$"`P`!`@.``0(#_ +M`$"@``!`H$``0*"``$"@H`!`H,``0*#@`$"@_P!`P```0,!``$#`@`!`P*`` +M0,#``$#`X`!`P/\`0.```$#@0`!`X(``0."@`$#@P`!`X.``0.#_`$#_``!` +M_T``0/^``$#_H`!`_\``0/_@`$#__P"`````@`!``(``@`"``*``@`#``(`` +MX`"``/\`@$```(!`0`"`0(``@$"@`(!`P`"`0.``@$#_`("```"`@$``@("` +M`("`H`"`@,``@(#@`("`_P"`H```@*!``("@@`"`H*``@*#``("@X`"`H/\` +M@,```(#`0`"`P(``@,"@`(#`P`"`P.``@,#_`(#@``"`X$``@."``(#@H`"` +MX,``@.#@`(#@_P"`_P``@/]``(#_@`"`_Z``@/_``(#_X`"`__\`P````,`` +M0`#``(``P`"@`,``P`#``.``P`#_`,!```#`0$``P$"``,!`H`#`0,``P$#@ +M`,!`_P#`@```P(!``,"`@`#`@*``P(#``,"`X`#`@/\`P*```,"@0`#`H(`` +MP*"@`,"@P`#`H.``P*#_`,#```#`P$``P,"``,#`H`#`P,``P,#@`,#`_P#` +MX```P.!``,#@@`#`X*``P.#``,#@X`#`X/\`P/\``,#_0`#`_X``P/^@```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````(:(0`````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````&`X)```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````)`DA*Q8````6"1`````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```0"AHN"0``$"$*"0`````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````!@*#P\)`!8* +M#Q4)```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````DH```````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````$!T/#P,)"2(S+@D````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````6"AH)```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````D&`D"'P\/+`H""@\:"0`````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````D/(@D````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````!`*(AL*%1L/+A$=&P\H```````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````"1H;"0()```````````````````````````````````````````````` +M````````````````````````````````````````````````````&"(L#PH# +M&QL:#@H5%0(````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````A(ALK+A\) +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````0'AH/'@H5#QLB%2(;#A`8 +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````"1L;#P\/&Q8````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````!@."B(*"A,<'!T;&P\/'A`````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````)#RP;-BPB(1@````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````%!T*'1,3!@4"!@H:&S85&``````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````DB#QLL#P\;"0`````````````````````````````````````` +M```````````````````````````````````````````````````````````8 +M'@HA$0<&`CD""@HF%0HG```````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````"1L/ +M&PH5+#4G```````````````````````````````````````````````````` +M`````````````````````````````````````````````!8*+A$"*@8<$0($ +M!@8.$Q4)```````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````"+AHF$QL/&@X)```` +M```````````````````````````````````````````````````````````` +M````````````````````````````````$`H*(0(<#A,.!`<%.08."@X)```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````DP$P8"%1LF#S`H```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````0'A41!`83'`8"!@X<$1P<%0H6```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````@HJ`BH<(QP<$PH)```````````````````````````` +M```````````````````````````````````````````````````````````` +M`````!8.)0<%!`8@!@0&*AP@!!<5"A8````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```)"B,<`@8&!@(.%0D````````````````````````````````````````` +M```````````````````````````````````````````````````D`@($!0T& +M!@82!1$C(Q<2!B4=&``````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````!`3(P8@(Q,< +M!`8<)Q@````````````````````````````````````````````````````` +M`````````````````````````````````````"@*$P("!`@2(Q,$!B`@&1D7 +M$QP````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````%AP&$@8<(!(7!`8*"A`````` +M```````````````````````````````````````````````````````````` +M````````````````````````)PH +M'PX)````````````````````````````````%AX?%2"@(6```````````````````` +M````````````````````````````(1L"%@P,#0@+"!D9$AD+%P0,!`(0```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````%@X3!@@+%QD9&1D%"`@"#!`-'!4*"0D) +M)```````````"1H/`Q`4"2$.$1@````````````````````````````````` +M```````````````)'0X'!PT$"!<+&1D+"Q(+"P@$*A8````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````%`DJ'`8%"QD2&0L+!`@%!P<-`@T"'1X)```````````4 +M*QX""18)$!@)"1`````````````````````````````````````````````` +M`!@)*Q,"#`@(!`4("`L+%PL("`(<"0`````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````)`D*"AP$"Q<9&1D("P(-"`4'#`("!A$6`````````!`A#@('!P(4&!8* +M"A8```````````````````````````````````````````````D5%0(,#0@( +M"`L2%Q(+(`@$!A,)```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````%AX*!@8@ +M$AD7&0L2"`0%!`T,#`P-`@()`````````"@"!P81`A80`@H;(20````````` +M````````````````````````````````````&`H;#@<,#`8(!@L("PL+"P@" +M'0D````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````&!@)"B8J!A(+"Q<(%P8+ +M"`<%#`0%$Q,6````````(0X"$0X1`@("$1,*"0`````````````````````` +M````````````````````````%B$*!@P(#`@%"!(%"P8(!"HF"0`````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````!0)*`DE%1(+$@@+$@L7"P0&"`(.!B0` +M`````!8)$0(1`@("$0(1*AX)```````````````````````````````````` +M````````````%`D"#0((!0<&!1(+"P4$!@D8```````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````$!,>(`07"R`+"QD2"PL(!08\&````````"@A!A$A +M!P(1$1$"'"$)```````````````````````````````````````````````` +M%A$&!PT'!!$&"!(+!@8"%@`````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````4(1$&!`L$"Q<+&1<+!@0,!`D4```````8`AP"`@8"!A$"#00&%148 +M```````````````````````````````````````````````8'@H"#`4$!B`7 +M"PL&(PXD```````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````!@*%0X% +M#`@+"Q(9&1D+!00&`A8``````!0"`@(<`@("`@<"#1P;&Q`````````````` +M``````````````````````````````````D:(@('`@83(Q<+!@@E)18````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````8"2$))``````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````4(145`@<,#00+&0L9(`@% +M"!,<%```````%`(&`@("!0("!`P"!@H*(10````````````````````````` +M````````````````````"0\/`@D-!B4C(`L(%Q4*$``````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````$`(?&S`)$!`````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````6%A`0"2$5$P8%#`T(!0L($AD+!@0%"AT8```````8 +M`@8$#00-#`4"#`(-!B8*(1@4```````````````````````````````````` +M```````)&P\"!P(1(R`2%P@$&Q46```````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````"03 +M"B,3!A$*"A8````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````!`*#Q45"AP"#`((!PP$"Q<+"P8(`@05'A```````!0J'`("#`P)!PP) +M#`P'`A$*"A```````````````````````````````````````````!8/&@(" +M"0(<"R`+!`85&@D````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````D`@HC&0L$(Q4F"0D" +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````8"0<6"14:&Q4> +M!@P,"`@%"`@7!`4(!`8$`A4*$```````)`X3#0P6#`P,#0P,%!0,!Q45(18` +M````````````````````````````````````````&!H5`@<"$082"P@(!1L* +M"0`````````````````````````````````````````````````````````` +M````````````%`DA`@D6%A08$`D8$"<6%``````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````"<*(R`@&1<+!@83&@HA)``````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````$`D6``D*"A,A!B$&`@8-`@0(`@@$%P0+ +M"!<"$08"%1\)%@D0&!`)&B("#`P0#!00#`T,#`P,`AP=(1`8```````````` +M```````````````````````````0$R8"!0@$!@8+!0@",!X6```````````` +M````````````````````````````````````````````````````````%@D" +M'2X*#AX.(1$"$Q,K"@H)&!88```````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````"0H2&1D9$@L$!08<'A4G```````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````!@*&AX1!A,<'!$"!PT,!`@(!0@("!<("P42"`0"!@(>)@H> +M)1T>(AHS&PX'#`P-#`T,$!8'%A0,"1P5"A`````````````````````````` +M```````````````A!@4,"!<&!A(2"P0E$Q`````````````````````````` +M`````````````````````````````````````````!`*(AX3'`XJ(1P.$0X1 +M'`X3*Q,5"A`````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```))B,2(!<2"P4$#0(3%0D````````````````````````````````````` +M```````````````````````````````````````````````````````````8 +M$14;,!P"$0(A$08"!PT"#`@($@4(!`L$"P@&!@('#`83"C`*%14*&@\5(10' +M"10,$!0,#!0,#0<,!@\*$``````````````````````````````````````8 +M"0(%!P4("`0&(`0+`AXI&``````````````````````````````````````` +M````````````````````````&`D)$0H;$P(,`@<"!A$&`A$H$0X1$P\N%@`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````8``D>'BH<(08& +M!P<-"`83`A8````````````````````````````````````````````````` +M`````````!08&````!00"1```````````````````!08$"@*$P8.$1$.$0(" +M`A$�@-!@@7"`07"`4+"`4<`@T'!P4&!@8&`@8&`@('`@P-!P<-#!86!PD0 +M%`<)'!,)%`````````````````````````````````````(>`A8,!P@$(!P" +M#`T''"H4```````````````````````````````````````````````````` +M```````4%@DH"@H3$2H""`0"!`@"`@<"`@81`@(.'A,)%``````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````"1X)%BD3#@("$1,)&!`$!`8=#A0` +M````````````````````````````````````````````````````````"1X< +M`A`4"0(*"A8`````````````````"0H=#@8&`@(.#A$"`A$"`@("!`0("`@( +M"P@2"P8+!`8"!PT$!`(%!P0%`@@-!`($`@("`@("`A$"`B@"!PT-$0,)```` +M````````````````````````````````%@X&#`P(#0@"`@T'#`<"`A0````` +M`````````````````````````````````````````````````````!`#%1X. +M!AP&!P<"#0($!00,#`P'`AT*'BL&$PH)```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````)`D."@()$QX=$0D)#A88%`T%`B8*$1@````````````` +M````````````````````````````````````````&`DA"A,""0T'!AL5`A@` +M````````````)`D3"B8<#0(&$1$1(0<"`@(&`@0"#00+!@L&"P4+"P0"`@4' +M`@4"`@4"`@('`@4"!@T&!A$&$08"$0X1$0()`@DA%0X)%``````````````` +M````````````````%`D1$0T,"`<(#`('`@P,#`T'"1@````````````````` +M```````````````````````````````````````D(14/$P8"!PT"!00(!`@( +M#0P0"0D"*QX=#@<.'@D````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`!@""B(;(0D.`QX)"0D)$`$8#`0%`BH=$1`4```````````````````````` +M```````````````````````4%A@"$QX1#0P'"0()`@X*$184`````````!0) +M"A43!@8"'"$1#A$1`@<&`@8$`@4$%P@+"Q(+"P@+"`4$`@P-!P4$!`(&`@8" +M`@(1`@<"`A$A'!$.$0X1#A$.$0XK%1X)$!80)``````````````````````` +M```0'Q4"%@<-"`P"!P(("`T'#!$*$0`````````````````````````````` +M`````````````````````!@0%@(='`8&!P0%#`0%"P@%#08$`@(6$0(6$!@8 +M%!06```````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````*`H>"@\N`Q$. +M`@D)*!0!&!0D%@<%/!,5"A`````````````````````````````````````` +M`````````!`*%1P1`@P,$`<-"0T'!P85"A@`````````)PHE$P8-!!$<$1$" +M`@T"#0($!04(!P4$"Q(%"P42"P8(!`@""`T$#0(%!P4"`@(&`@4"!@(&$0X1 +M(1$1(1$1#@X1#A$.*R$3)@H)``````````````````````````DN%0<,"`P, +M"`T("`4$"P@-!@HG```````````````````````````````````````````` +M````````"0H.$08$!P(-"`@'"`@$!0<((!4;`PDK$Q@````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````"&BL."AHB`A`)`B@)&!80)!@0 +M"0($$1H;"0`````````````````````````````````````````````D(2(: +M#@P,#`D,#0P'"1`,`@H5`A@````````"+BH"!`81$0X.#@('`@0'`@(%$@8% +M$@4&"P@+"R`+"P@&"`@'!P4$!0((`@@%!@(&!P0"!!$A$1$.$0X1#@X1$0(. +M#B@1'"85%2$)"1@`````````````````````&!,=`@P,#`((!`4+$A<("P@& +M%0(`````````````````````````````````````````````````"0DA%1X& +M`@4""`0(!@L%"`@-$`8E'Q4>#@H*%@`````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````DB$R$1#@X0`10)"0D0)`$8)`$0`@("'A,)&``` +M````````````````````````````````````````%A$5*P('#10)#`P)$`T0 +M#10'!RH>(1``````$`D&!@<"#APA$1$1!P((`@T'!`0(!00&$@4+"Q(+"!<( +M"PL(`@T,!P($!`(%!`<"!P(&!P8%`@<1(1$A#A$1$0X1$Q$1$2$1#@(&$QX= +M"18D$````````````````!00`A$,#0<4#`P,"PL9"QD$""H*"0`````````` +M`````````````````````````````````!@0%@D>$Q$&!@4'"`0%"`8("P8' +M%A0-'B8)%B@=&QX8```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````*!43*Q4.`A@!$`X8$!@4`10D%A0)!PT"'!4)```````````````` +M`````````````````````````!8*"AP-$`D"`@<)%`P,$!0'"0P'"`X>`Q8` +M`"03'@("!!$1$0X1#@8"!0<"`@0%"P@+!!<+"!(9%PL2"P0%!`4'#`<(!04" +M#00"!04$!0(%!`($!P8"`A$1(0X.$1$"`A$"$0(<#@(&!A,"$0,)```````` +M````````"1X.#`<0#`D,!P42&1<9%PL&!@D6```````````````````````` +M````````````````````"0HK'"H"!0<&"`4,!1(+"P4("`P8`1`*%0D`&`D) +M"20````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````````D."BD3 +M(A$4$`$)(0D4&!@0&!8D&`('"0<&"@(````````````````````````````` +M``````````D)`A\5`@D,#0D0"0T'"10-%`T4#10,!R8P*!04*!,*!@<"`A$. +M$0X1`@0"!04'"`@%$@8%$@47$A<9"P8+"`L("`P'!08$`@0(`@4$`@<"!`@$ +M!0<%`@0"`A$1#A$.`A$A`@()$0X1$0('!0(3"B$)"0`````````````H&@(, +M%A06#`P,#`L9"QD+!B4.%@`````````````````````````````````````` +M)`D)"0D1"B4*@<,%`<,"0P-"`8+ +M(!(&%1T8````````````````````````````````````&!8)"AL*'"H&`@4" +M"`0-"`@+!`L7"PL%(!P")!`)"BX0```````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````D/#QX.'1X.$0D1(0()%B0)"188%!@D%!0" +M`@8*'A8``````````````````````````````!@='@(1(0()$0D)`A80%!`) +M#1`)"0P,#!8,#0P,!P(<$Q,""0("$1$1#@X1`@0%!`4(!1<(%P47!1D7!!D+ +M$@42!1((!P4$!`($#00"!0T$`@(%!`0%!`(%!@0"`A$.'"$"`A$"`@("`@D1 +M$081#@X'!08.#A,*'1``````"0H3$Q$0#`P-!PP,#`0,"`8E$P()`A@````` +M``````````````````````````````D*&AX.!BH&`@4$#00%"`P2%PL9"Q(( +M!`(P&PH.`@(""0`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````8$1\5$PX.#@XA*`X.*!`)$`())"0!`1@!"0T$`A4*%@`````` +M````````````````"100$!00"28*`A`)"0(""0()$!8-"0(1"0<-"106!Q80 +M#`<-!"H3$0T'*!$A`@X1(0("!0<%!0<$"!(7!`02"PL&"P4+"P@+!`4$"`4% +M#00"!00$"`(%!`8%`@0'!`0&!@(&$081$0X&(0("`@("`@D"`B$1`@("#A$A +M$14N(1`6)PDA%0H<#0P-!PT,#0P,#`4''!,K%@`````````````````````` +M````````$`DH"1`)"0H5$P0'#`0-!`<("`8""Q<+$@L+"`4,`ALQ&R$H#A@` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````&`(* +M$0D)*`(A$1$A'AX)`@D8$!`6`18!%`$0!P('*@X)%``````````)$`D)"1@) +M$`(**@(&`@81`@8)!P()$0(,%`T)`@X3"B83`@T'#0P,#`<-#`P-!P4)%`D" +M$1$1`@('`@0$!0@$#00%$@L+&0L9"PL+$@L%$A<(!0@'!`0$!0@$!0@"!PT" +M!`0%`@4"!@(&$0(J`A$"`A$1`@<-$0("!P("`@D"/`(1`A$.#A,*"2@5"A,. +M#0('`@P'"`<(!PT,!!,F(1@4```````````````````````4$!8)$!8)"AL* +M$QP"!B$&`@4"!00%%P42"P@2"QD+&00,"1`"%0HH&"@="0`````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````!8>"B$4&!06)!86"0DH +M"10)$`$6)!@0&"06`1@-`@T'`@H)````````"1HB&AH/#QHB%1X1!@0"!`8$ +M!PP"`@()"0<0`@H"$`DH(0H"$`T'!Q8,#0<,!P@-#`D-!PD-!P(,#0<%!04+ +M!!<(!@L+%P07&1<2%PL%"PL+!`@$!`4%`@4'!00%`@4%"`@%!@@'`@8'`@0" +M!`0"`@("$0X1`A$"`@<'`@()!PD"`A$1`B$1$2L1(1$.`@<"#`@""`4,!0P- +M#`(5%0X4`````````````````````````!@*(A\*'0XJ*@8&!@((!`8(!`4( +M!1($"PL7&0L7$@L(#`P0%`D""10`&`DD```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````4$14*`B04%!@4)!@0%A@D&!@6)`$4`0$4 +M`204&`<-!PT&%0(````````)"@\/,C(S-C,;,!,.!@0%!@0%"00"*!`-`@8= +M"@D````"+B$'"0P-#!`,#`T,!Q`,#`<'`@T"#`@""`<"!!<+"`4$%Q(+"PL9 +M"PL2"PL2!0@%"PL7!`@$#00%!`0'!`<&!`4$`@4&`@8'!0('!A$'`@('!@(" +M(1$"#0T"'`<-!PD'`@(H$1$"`A$1#0P'#`(-`@@(`@@'#`<-*@H*&``````` +M````````````&`()`@D)"1\;%1X3$P<'`@0-!`(%"`8(!!<$!!<2&0L2&1D+ +M$@T0"186`A`````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````)"$*)1P%!P("!PT6#!`)$!0D&"04&!`!&`$8%!@D#`T'!P(3 +M`A@``````!88"1$B-"P/%28;&Q4@'"`$!0<,#0<"`A$.%0H"``````D*#@P, +M`@('#0T'!PT,#0P-`@4'"`<"`@<-`@T'!P0+!`4+"Q<2"Q<+%QD7$@L+$@@& +M"`0-!@0%!`4%`@4(#04(`@@$!A(&!@(&!`4"!@(&!@4"`@81!@($$1P"`@(" +M`@D"!PT""0<"#0('"`(%!P8'`@<'"`T(!P(>$1`````````8"180&!`4%!`) +M"AH>$PX<(1P"!0(&!P(%!08+!`82!0L+"Q(2$AD9&1D9$@8@$0(0!PD3"0`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````!`3 +M'AP&`@(%"`4$`@0-`@P-&!`4&!0D`184)!`D)!84"0(-!Q$5"A8````````` +M%@X/,S4E(",5&R`2'`0%!P@-!!,N'P()"0``````&`(3#@T'#0<'%`T4$!0' +M#1`4#`("!04%!`($#0T(!0P$"P8+"PL9&1(9"Q(7"`8+"PL$%P0%$@L&!`0" +M`@4$!`0"!04%`@0"!@X&!@<&`@0$`@($`AP"*@(.$R$"!`(-!PP,!P<-!P(, +M!`4$"`@"`@((!0@"!`T.$R$8````````"14B&PH>'0X.(0(<$RH&!@0&`@<$ +M!P(%!`0$`@@%!00C(`L@&2`2&1D9&0L%!!,;&AT)(1LG```````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````&`DG"1XP$08$!`($`@0- +M!`($`@<6#!06`18D)`$8%!@8`1`,`@<"#2(5"0``````````&!T/&Q4C(R8F +M$A(@'`8�<-#@H*%@``````````&!T>`@<""186$!@4&!80#0D-!P0"!P($ +M#00-!`<'#0@("PL+$@L+%QD7"PL$"PL2!0@&!0@+!@L%!0<%!`@%#00"!`0% +M!`8)1,<'`8E'QT0%``4 +M%A@````````````6*R8""180%!`4$!84$`P(!PD4%@P,`@@%`@0(#0P(!P4$ +M$A<9&2`9$@L2"Q<+!0L$"`@&$@42!`8-!`T$!00'!00%!@@"!@X"!A$"!@0& +M"`4&"!<$`@81'`X&!A$&`@<-!PT'#0<%"`0("PL&`@(&!`((!0T$$PH>'BL3 +M#AX.`@8.!@X1'`8%!@(%`@@$`@0"!`0"#00&$@42!@82%PL9(R,@&1D@&1D+ +M"`@-"0T&`A@6)!08%``````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````"0HF$R41`@8-!`('!0T"!P<-`@4"!0($#00-"10!)!06)!@0 +M#1`"!`T&`A0``````````````!@)*`(0(14;%0(""0`````````````````` +M```)"0(0%A`-!P("!P<%`@(%#0<8)`T,`@0,"`8%!P4%!`4("Q(9&0L9%PL( +M$@L2"Q(%"PL+"`@%!`@%!`4'#0@&"!(%!`8@'`8&!@(&!@4$`A(%!`8(!@(& +M`@8$!`(-!`T'#0<"#00(%P@%"P4$`@<%#0($!P(.$PXJ`@8A'`("!`4"!0(" +M`@0-!P4$!00%!`4(!00(!1P7!`@2!B`9&1(E(R,9&1D2"P(-!PD'"1$)```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````````````````````````````````````````````````````````D. +M)B4@!B`&`@(""`((!`0"!0('!`<(!0<%`@0,&"04&`$4`0T0#0<"#`("*!0` +M```````````````````)"AH*%@`````````````````````D"0D0"0<4!PT( +M!00&!`T$#0<'`A08%`<(`@4""`0'"`4$"`(2"Q<2&1<+"PL7"Q<("`L&"P8+ +M!@(%!@4%!`T$!00+!0($'`8&$0("!`82!`(%"`0+!00$!@($!P4%`@($#0<" +M`@T$!0@%$@L$!`(&`@0'"`(%`@(1$00%!P(1'`8"!P@"!P4"!0(%!`8(`@(" +M!`8$!08&!@0($B`<&1D@$B,E("`9"PL%!PP)#0P"$PD````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````0"AL&!AP@$A("!@T$ +M!0@&"PL$!@("!@('`@4"`@T"$!8D$!@D%`D'"0P"!P8>"0`````````````` +M`````!@0"1@0```````````````8&!`6&`D*#A$-#`(-!`($#0("!0P$`@@- +M!`T4"0(%!P@"#`4'!`(%`@8%!@07$A(7$@L+$@8+!1(+!`4$"`0+$A(%!`@& +M!`4$"`8&'`X"'`8&!@8%"`0%!@@2!0@$!0(%!`($`@8"`@(1!@8"!`@($@8& +M!00%!P("#`<(`@(&`@<"!0("#0($!0($!00$`@0""`8$!00%!`L+"Q(@%Q(+ +M(R`@&1D2("`@"QD+!081`@<"#`X*`@`````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````!`="@(%!A(&"`4$`@P'"`L9&2`&`A$" +M!P4(!P4'!00,#10!%`$)#`P,#0<,`A,)```````````````````````````` +M`````````````!`*(AX1!`8&!`((!`("`@("!0($`@4-!`((!PT4%`<"!0@" +M!00%"`0&`@81!@8+$AD(!@L("PL2"!<(!`4%!@L%!@4""`@(`@0%(!$A$08< +M*CD"!@0+!`4$#`8(!0($!@L$!0@"`@<"$2$&'`8&$@(&`CD"!`(&!0T'`@4$ +M`@('!0@$!00%!`4-!`<%!P8%"!(%!`@%!AP7%P87!@8+%Q(@(R`9&1D2%Q(+ +M!`4$*AH/%1,"!PD8```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````$!,3`@0(#`P""`0("`4$!0L7"P81!@($`@<-!00"`@($ +M"18D%@P'`@T'#0P"*A``````````````````````````````````````$`D" +M*`H;,"H""`<"!`T"!0(&$1$$!`T(`@0$!00%!Q80#00(!@@'!`T$!@4$(1$. +M'`87$A<+"P87$@4+!`4+$@0($@0$!00&!@0%!002!AP"!B$#A8````` +M````````````````````````"0H*$R$&!@($!0("!A$&`@<&`@8-!00&`@<$ +M!0(%!`0%!`4-#`D-!`8$`@@"!`T%!00"!@X1!@(+&1(7&0L2"`L+$@07"`8$ +M!`4&"`8+!`4$"`4&!"`&!@87$@8&!@4&"P4$"PL&!@@"!@8'!@02"Q<2"P02 +M$@4&!00&`@0-!0P"!00"!`((`@0&!04$!04&!`4$"!<$PX< +M`@(&`@("`@0"!@0"`@8"!00$`@P(`@0"!`8""`T%!`<%!@@0%@<1`@@%!`4+ +M!00%#08A$087$@0+$A<+"PL2$@L2!@@%$@42"`8+(`L$!A<&$RH)``````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````8%@(E(!(+"Q(9&1<+"PL&!`0(!`4(`@@$!0($!P<$#`P'#0<$#0<- +M!PP'#0P'%@<-!PT,!PT'#0<-#0<'`@T"`@T$`@T"!`4'`@4$#00%`@4"!`4% +M!`<"!`4$!@@(!00$!`<$"`@""`((`@<"#0<,#`((!PT1)AL;'`@(!P4$$@4( +M!08(!`4-!P($!1(%"`0&$0X<#@X1$0X"`@(%!`4%!`L%!A$3'!,&%P0%!1(+ +M!@0$"`L("`(%!P4'!`@(!`@+!0L&"R`2"`L2"PL&"PL7$@L+"Q(+"Q(+$AD7 +M"Q<$!`(-`@("!`8$$Q,0%A`6$!8````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````&`<@ +M(!(7"Q<2&1(+!`@+!1(%$@0(`@0(!`T%!04"`@@-#0('`@@,%@P'#`T'#`P' +M#0<,`@P6#`P'%`(,#`T$#`P%!P<-!`T(`@<"!`4"!`4&"`(%!@@%!P0%"`8$ +M!0T$!04%!`4$!0('!@T'%A`4%`D1'1L/%24%#0(,#04("`<(#`T-!P0%`@@% +M!`("`@X1#A$1$2$.'`8%$@@(%P0(!083$RLZ#AP&`@07!@8"!@('!0<$#0P" +M!`4%!`4(!00+!!<$"`L%$@4+"PL+$@L2%QD7"Q<+"Q<@!B`&"P0"#0<'`A,@ +M!@8:+@D````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````!PC(`L9&0L9"Q<+ +M"P@$"P@(!@4(!@4"!`("!`(%!`<'#0('`@P,!PT'#`P)#`P,#`<,!PP'#0P- +M#`T'`@P-!P<(#0((!P0$!04"!`4$#00"!`0%!`(%!0($!0@$!08&$@L$!0P% +M!PT0!Q04`0D6#1T5#QLF!@@'"`<,!`@%"`T,#`0-!P4$`@('!@(1#A$1#@X1 +M(1$A!`@(!1(&"P4&!#H<'!,3$08"!@('$1P1!@("#04"!`4(!`@%!!(+!!<( +M%P@7"!(+"PL@"PL7"Q<2"Q(+$AD2!!P&#@0"#`4$#1`"'@XA'!4*$``````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````0'14C"PL7"Q(+"PL@"P4+!1(+"`0( +M`@0-!00$!04""`T$!PT$`@(,#`T-#`P,!P(,%@P"#`P,!PP$#`T,#`(,#`(( +M"`(-!0('!`@"!`($!04%"`<-!`0$!`@"!`4$!`8(!@4$"`0""106$!80!Q`1 +M(Q45*@4("`4,#0@-!`@%"`<"!`(%`@0"!A$1`B$"#@X"$0X1'`(%"`0""`@& +M!`4"`@X.$1$"#@("`@8"#@(1`@0(!`@(!`@%!`0("`87$@L2""`+%PL7$@L+ +M$@L@"PL7"Q(7"P47`CD'`@("`@("#2$5`@(>(188```````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````!@"#A42"!(9%QD2&0L+!`@+"`8%!`4("`<"!0((!A($ +M"P8%!PT,#`T'!PP'!PT,#`@'#`P,`@@-#`T,#`(,#`<(#`<""`0'"`(%#00% +M!0<%!P0%!@4%!@47!@@&"`("!0('"`T,$!0!%"0)"0(*%1P&!B`$!P@,#`<, +M!P@-"`<-#`@-!`0"$1$.`@(&/`(1`@X<(0("`@0"#`4"!0($`@4$$0(H#@X1 +M`@8"(3H<$PX&(`8%%P8+!@L"#`L%!`L%"P0+"PL2"PL+!A<2"PL+"Q(@!@8& +M!`<""0T.'283(08'`B@D&`D````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````))B4@"PL2%PL7&1<+$@4$!00(`@<"#0@$%Q(+"Q<+!`@"!`('`@T4 +M#`T,#`<-#`T"#`<,#`<,!PT'#`<-#`0-"`T$`@4(`@0$!00""`0%!`((!`0( +M!`0(!`4,#00'!PP4$!8!&!8)`A$.$145'`($#`T(!P4'"`@-"`0(`@P'#`(& +M`@8A$0X1`@(%`@(1$1$1`@(%!P4$`@4$`@4-!`(&!@X1.A,<'`8<$SHK'!P$ +M$@0$"`0%"`@(!!(+"!(7"R`7%PL2!1(7"PL7"Q(7"QP&.00%#0<'`@H5"@X. +M`@XJ%@`````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````!@)'14& +M%P@($@L$$A<+%PL2#00%"`@,`@@$%R`2("`@%Q(%!0<%#`<"$`P,#`P'#`@, +M#0T,!0P(#`P-#`@"#`T'!0<%!P((!0T&"`8(`@8("`4"#0<%"`4$#0<"#`T4 +M%!80"1`"#AX5%14F(P($"`@(#`<,#`@-#`<(#04(#`4'`@(1#A$1`A$$`@4% +M!`8"!@8"!0((`@0(!@@7!!<$"PL&(R43'B4F)1X3$QP3$P8<%PL7!`4$!@@7 +M"`07!`@2$@82%PL+"QD+$A<+"Q("!P(-`@("!0("%0HA$!0)'@H8```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````"00$Q4&"P@7"Q<("`@2 +M"`4%!`4%`@<%$A<$"R`5&B8<$0()#`<)#`T,#0(,#0P,`@P4!PP,#0<-!PP' +M#`P,!P@,!`4$`@4$!`@-!`4($@8(`@<$`@4'!PP,#`T0%!0D`0<.'1L/&P\; +M)@8%"`0,#0<,#0P%#`P,"`4'"`<$#0P"(1$.'`X.'`4+"`82!!((!!<2!1(7 +M$@L+"Q(+"`L+"PL7(",E$R8P)1P>$RL<(P87!`8("P@7!`L7$A<+"!<$%PL+ +M"Q(+%Q!@T,#0<("`4-"0<'#`@-#`@(#0@' +M`@P-`A$)"0<"!P8+%PL+$@L9"PL9"PL+$@L+!0L7$@L+&0L+&1D9!@8C'",1 +M.AP3(`87!@0%!`@(!0L+"R`+%PL+(`L&!`L%"`4'#0<-"0D'!P(J"0(P)A,3 +M$``````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``````D;+",7"`L+%PL$"`8(!A("!00%!08<)AL5)B`"!`("*!`4!PP'"`44 +M!PP'#0T6!PT,`@T,`@T4#`<'#`<%#00(!`((#`4-`@4(!!<%!`4(!`P%!Q80 +M"0<)$"<:#Q4;/28E!@T'$!08%`T$!PP'%@T'#`P'#0(,`@@-!`<'#`<,!PP, +M#`0("`4+%Q(7&2`+&0L9"QD2$@L$"Q<2"PL@%R`@("`7$@8@(!(%!`0&(`<- +M!`42%Q(%"Q(+$A<+"`0-!`(,`A8)!P(,!PP-`@X0)`D)"0D4```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````&!T*!@8$ +M!@L2!0@("P0+!0@"!`('!"45&R8C(`('`A$)%A86#`("!PT0#0(,!PT,!P(( +M!P('#`<"#0@"!P($`@<%#`@$`@0'"P@%!`8%!`4("`P,%@P)#`D4"2(Q#B4E +M'`8(!Q8,"100"`4(`@<)!PP,#0@'"`P-!`T"#`T-#`P-"1`-#`4,"`07"!(( +M"Q(7$@L2%PL+%Q(%"P8(%R`2(R,@$@L$%Q(&!A(%%P8$!0T("`L+"PL$%P07 +M"`@&!@<)#`('!PP-'`X&`@<&'0D````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````"2$"("`+"QD+"P0%"`@& +M!`4&!@0"'@H;)B4C`@8&$0(8!Q`"!`0-$`D"`@('`@('#0('!@4"`@T'`@(, +M`@<%`@($#0(1!@8%$@@+"`0%"`@-!Q0'`@<-!`D"&P\A`@0%!`($!`4'%@P, +M!@0-`@8$`@<,!PP-!`('"`((!PP0#0P,#`P'"`<-"`@,"`4("`@(!0L(!`@% +M"P0(!0@&(Q,3'"`%"Q(+"PL$%P0$!@0%!`8$%PL$%P@%!@8$"`<)#`<-!PT- +M%`(5&@H3!A,5"0`````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````!8*"@8%`@L9&00%"`0%$@@%"`<'#0()#B8; +M+@8&!B`"`A8-#00-`@P)!P0"`@T)`@<)`@(1`@T"$00"!P<%`@("!@4$!@8& +M$@0%"`8&"`@,!`P,%@P"!`('`A$:#R@$%P8&$@8"`@<'`@0,`@8"!P()"0D, +M"`((!04$#0@-%!8'#`(,!P@(#`@%!P4,!P@"!PP(#`(("`<,!0('`@@&'"`@ +M!@L$!0L@(`4(!00(!`("!@L&$A<$!0<'"`T)#`<-"18'`@('`A\3*"@5"@(8 +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````&!4:!@P,"!D+"P0""`@%"!(%#`4'!Q8"$QL5!QP<.1$)$`0- +M!Q8,"0T"#1$)!P<"#0D'`@("`@("`@("`@0"'`8"!`($PH*(0`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````8)A4C"R`+!@@$!`@9$A<(!0P$!@L9"Q<(!`4,!PT) +M#`P'`@4'#`(,!PT)#`T$#0(,!P(-"0P)#0<"#0<-`@(,!P<)#0T'#0(,"104 +M$!`0#`($`@<(!P(<&S0R-AL"$!0)%!@D$!`!#0D6%`T4$!@4%A0)%`P,#`0% +M!P0,!P<)$!0)%!`-$!06#!0)#`P6%!0,"100%`T'#0P)"0<'#0@,!PP"#`D4 +M%A`0$!0)!PD0#`<"!Q0-%34;'1,"`@8#"0$D`2$*(1$5"0`````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````!@)#A,2"PL+!0T%"`L9$@@"!0@(""`9&1((!PT'#0<-!PT$!PT'#0P6 +M$!0,$!`"`A80#1`6%"04%A`,%A`0%`T4%A`0&!0,!PP'#`D,#0('#0<%`@<) +M`A4V,C(L%0D!`10!%!@!%A0)"2<)&!84&"08$!80#`<-!PT'#0(%#`D6)!84 +M%A`6#"00%A`4$!80%A0D%!8'#!8'%!0-$`T0!Q8'%!0-$!0)%!8)%!81`A84 +M"18,"0D?,2PQ#Q4E`@(8`0$!"14>'!X)```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````)*@8% +M!`0%!`($!@L7"P4$"`@(&1D+"P4-!PT'#0<""`4$#0@($!04&!`6`A$1#0$4 +M`100%B06`1`8&!08%!`6`1@4%A$&$1PJ$00$`@0"!`('!@(3-C(S+"X>"18D +M&!@)&`DG"2"A,)`18D%B08%!`6%"04 +M"0D6"100%`D.$R8E%283'`8&!@8@#@XO+0,/+#$/#@D))PD8+R\W+R\O-S(0D)%`T."@H*"A\5"AH*"@H3*B4F +M%2(/&QH?$0D0&``````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````"0H?'@(6&!0,"`8(!@@%!`L7$@L2%P@+"PL+ +M!@@'#!0'%`T'!`4'#!@6)!0)"2$:(@DD%"04$!88%!@0&!`='@T8%!8)$!`) +M"1`)$1,@'!LL#QL#-R\#+0,M`S'PH#'B$0"1X*%0H;"@HB"A,J$Q,N&Q\/#P\/#P\:(@H" +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````4*`D) +M`B@)"0D8%A@)+@H=(204$`T$!`4%"`0+"P@+%P@+!@L2%PL%!`T-%`T0#`T" +M#104`104%@X1"0H*`@$6$`DA`A`6$`$8(14*"106"2$"%`$!`0<1*@0@%34S +M,0,M)RTM+2\M+S$QP&'1H/#QH;(@\:#P\V+"LG"1@````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````"<5'PH*'PH#'BD.`B$) +M"1`4%A8)!08E'!,@!@@&$@@($@4&$B`2$@@2#`<,%`T4!`4'%!`8)!@)%0H6 +M&`DA$`$)$PH>`@$8%"@3*!`"$!@H'@,8`20)"B4&"`8C(Q4#`RTO+2DO-R\# +M+R<)"2$J+R\O+S$RH&!A,*&QH/ +M(@\:#QH/#P\;(AH/#P\Q#P\B'QL/"A@````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````)'0D)%A06&!`8)!@4$!`8%@D)"0T$)145)2`$!00%"`0" +M*@H*%2`7&1<2"P@-!PT)%`$4%!@""@D````),!$)#@D)"B$-"0X.$``)"AP. +M#A@!)`D."B`($@L9"QD@+2T#`S(1`)(R,+&0L9 +M"`(,%`$0&"08)`D*"0````D*$QP*"1@)$R8A+@H0`"0)'1X)`0$""AX&!`L9 +M&1D9&2,#`RTM+2DG%@D8`@8"!Q@6&!08+P,:+A\:#@D"'PH)$"$>)Q`'#`<" +M#0<"!0("$2D.#BL1"0D1#AT.`PX1`@0"#BD.#A$)%!8)#ATK'`()#`D0`BL> +M'1XI#@X="@H#'@H3$Q,<(083'14/&B(5(AH;&@\B&B(;#R(/#P\/#P\/#P\/ +M#P\;(AH/#QLG"0D8$`D8$!@4```````````````0&`D=&PH6```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````%B$;+`\5)@X1 +M'!$1$0(H$0DG$0(H`@(C'",,"X;'R(;&B(:(ALB&AL:(AH/&AL:#P\:#QH/&@\/+`\L#S4T#R(: +M&AH:#P\/"A``````````````"0HJ!AP5"A8````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````%@HB&QL/)@H>$Q,K$Q$.$0() +M*!$"!B,E(R,<'`81$QL*&```&`H5)1D7&1<@#1@!`10D`0D)```````````` +M````%@HB"@D`````````)`(1)@8$$A(9&1D9$@8.+2TM+2DM(0X3#A,.)PD" +M"0D0`A4/&@H:#QL:"AT>&`$!`0$0`@('#0T"`@4-`@X3#@XK#@XK$QX.`PXJ +M!P'0,=$0<)$1X*'AT*'@H*"@,>#A$$`@8J,"X:#QH; +M&AHB&QH:'QH;&B(;&B(:#P\/#P\/#P\/#P\/#P\/,0\/+#4S-C,V,S0/#B$) +M(2@"(2@""0D""B43(R4K$``````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````%@DG%0\;%145%0H>$PX.#@X<(0XF&PH>'B4= +M"A4*$0D`````%A$*(Q<9&1('"0D0%B06'0X6```````````````D$`D6```` +M`````!0)'@H<"`L7&1D7$AD&!BD#+2DO"0(=#RP/-1HI#ATB)AT*%1L*'A,E +M"A\;)Q`!`0$!`183`@<,!`0$#`0-$2D.#AT.'2L.#BDJ!@(%"2$.`Q,.$PX. +M#A,K'0,3'AX##@(.`QT=`QX='A,3'"$&!@8@'0H5'QL:'RX?(A4:(AL;'P\B +M&AH/&P\/#QH/#P\/#R(/#P\L#P\L#S4T-0\T#S0U,0\/#P\U+#$L&R(5$P8< +M(R4F$18````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````G"2#BL.#ATK$Q$"!0<.*0,3*2L#*1X#'0,='AT#'1T. +M'1X#'@H>'0X&`@($!A,3'0H?+A\:'PH;&A45(AL:&@\:%1HB&QH;&B(:&R(: +M#P\;&@\/#P\/#P\/+`\/+`\U+`\T-2PS#S0S-C$N)2H$'#`<"0D8```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````%@H;&Q4>"@H*'PD`````````````````````````%`HF!B`@)1L5 +M(0$6$0H*%@````````````````````````````DK%08'"Q<+&1D+$@8J`R$O +M*2<0#`(J%30U&RX?*2D##S$L'BD3$PH5#QHB&@D!`0$!`0$6"@('!P(&!P8" +M#0X.#@X.#@XK$PX.#0("!@X=#BL3*1,.$RD3`Q,#`Q,>`QT*`QX=*1,.!@<( +M!08A'A4?'Q4:'QLN&@H?(AH?&ALB#R(;&@\:(AH/#P\/#P\/&@\/#P\/#P\T +M#P\Q#S$/,3$/-BPU,30U-BP/%1,"AXG)S\"$RD#&AL. +M(0(G"2@=&PH3"@H:'R(:%0\:$0$!`106$!$*!@T'!0("!`('#A,K$PX3$PX. +M`@<$!P(A*RL=*PX#'BD.'@,3'AT>*0,=*QX3#AP.*@(%!`8<'0H?"AL*(AH; +M'Q\5(A4B&ALB&A\;&B(B&ALB&AL/&@\/#P\/#P\L#QH/#S4/+`\L-2P/,RP/ +M-3$/,RP/&AX<(!PC%28)```````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````!@)'0\;%2@="B$0```````````````````` +M````````"0H3!@42&1D+&1(2!@,3#AP5'S<&'`,M`S$U&A$H"1`.'143'14: +M"AH*+AHB#QLG`0$!"0H/#Q4&!PT"!P4-!`T.#@XI*RD.#@($!0("*`XI#A,I +M'1,.'2LI$RD#*1X='A,J(1P"!`4"!!$3,`HN&BX?"@H:(A\;&Q\5'QHN&A\; +M&@\:#QL:#QL/(@\;#P\/#S$/#P\/#P\T#RPQ#P\/,34Q#S8T,S0Q&BL&!B,5 +M'P()"10````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````)%34;'AT>&`````````````````````````````D.%08( +M"!D+&1D+%P81'1$%`@H=*0XJ+2T#-38/*0()`AX5#QX>'PH?(A\5'QHB&@X) +M"0(A&S0S#QP,!P4$`@0"`A$.#@X.*PX"!P($#0(.'BL=*PX>#@,3*Q,#$PHI +M$RL=#A$&!`@%!`81$PH?&@H?%0H5"AL?&A\:(AHN&A\5&Q\/'R(:#Q\:(AH; +M&@\/&@\:#R(/#P\/#P\/#P\/#S,/#S,Q+#4/#Q4>#AP&(`H*$``````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`!0G+BP*)Q8``````````````````````````!@""A4&$A<+&1D9"P8&#AP& +M!@0<(SHJ*0,M+0,Q,RP;"1`.#S8;(@H?%1H5&@HB&QLB'QLB"B83#P\:`@T% +M!P(%#0<%#@X3#A,.`@("!@("#@X.*2L3*2LI'@XI$PX=*1,A#A$&!`0&"`8< +M$PH*"A4N"@H?"A\*'Q45+A\;"A\?&R(:'Q4:&QH;#P\;(AH/#R(/#P\/#P\T +M#P\/#S$U+`\Q+`\T#P\/&C`3*AP3)1,0%A8````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````)`@D0```` +M````````````````````````"1T3!@42&1D9$A<&!@<%"P8&$R4F'2T#+0,G +M-S,R-AL""1XS-"P:'Q4?"@H?'Q4?&AH;"A,5,`D"`A$'!P<'!@("!`<.*PXK +M$2$'!00""1$.#AX.$P,.'0X=#@,=`Q$A`@8Y`@(%"`83'3`?&A\?"A\*&BX* +M%1\;'R(5%1\*&QH:&B(;(@HB'QH:#QH/#P\;&@\B#QH/#P\/#QH/#P\/#S4Q +M-@\/+C`*AX3$`$8)PT$`@(A`@8-`B$.#@X1`@4'!P(A#@XI +M#@X.$PX>`RL.$PX.!@T(#0<&!`8&$P,*'PH5"@H5%0H*"A\*(@H:'Q4B"AH? +M(A4B%14:(AH;(ALB#P\/#QH/#P\;#P\/#P\/#P\/#S4L#P\/#PH3!@0&(R,3 +M"@D````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````!@"'14< +M"PL2&1(9"P0%!`4(!`81)3XP+#$B`P,#&@\R,C4/$RLN&QL:"C@*%1\N"@H* +M"@H3!A,*(1`8%B<*`@P%'!$"!PT"$2D<`@($"`(%*!$.*Q,.'2LI#A,I$R$A +M`@8$`@0$!081'1X="@H*"@H*"A\?+A\5"AH*'PH*(AH;"AL5&B(:(AH:&Q\: +M&AL/#P\:#P\/&AH/#P\/#P\/#P\Q#P\/&Q4J!@0&(!XF"0D0```````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````"1X3!A(9%QD9&1(%!`@% +M"`(6"08E%0\S#RDM+0\R,C(R,RP;#RPN%0H*%2(5'Q4?%0,1`B<>'QLA`1@> +M"@,"#`(5"@8$`@<.#A$"`@(%`@<.#@X3#BL.'0X<#@($`@('!@8+"`83*1X* +M`PH*'0H5"AT*"@H:"A\N"AL*&AL:+A\:(AHN%14B%2(5(@\B#QH:#R(;&AHB +M#P\:#P\/+`\/#P\L#QT.!@82(Q4""0D8```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````&`()``````````D.%08(!1D+&1D+"P<%!`@$"0<"(QX;-C(/ +M#BT#(C0S,C(R-C0U+!H*'2D*.`,?'0H=#@())PX*&R$0"1\B+0D,!P\;'`4' +M#2$.!@<%`@(-`@X>#@XI$PX.$0("`@4'!@($!`42#A,#"@H=%0H*'PH*"A\5 +M'PH*%0H?"@H5(A\:(A4?'QH?#QH*&AHB&AL:+B(/#P\B#QL/#P\/#P\Q#P\: +M(A\=!@@$("`C'0D````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````6 +M"B(1`````!`G"C``A08 +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````!@*+`H)````"0H3 +M'`4+%QD9"P42"PT)$`D1("8/,RPN"ALV-`\*'@HI+0,Q+`,U,2L""2<))R!@('(",C)AH*&``````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````8&S(L(0D)'A4J$@L2&1D9"Q(%#!0"#A,&(",3"@H? +M,`H*&A\5`RL>&BPB`RDM+2TM-R\W-R\M+RTO-RDO+R\O+0,M`RTI*0H*'`(3 +M#S85!@T'!@("#`4'`@("`@0'!0(%`AP$!@0&'!,=$QX*'PH='1\*'14="@,* +M'Q4*"AH*%0H*%0H5(@H5'PHB'QL?&RX:(A4B%1\;&AL:&B(:&P\?&QH;"AX" +M!@4&("`3"@HH&!`4```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````!8B,C,/&R(3(009&1<9$@0%"`"A\*'0H*'PH>'0H*"@H*"@H?"@H? +M"A\*&@H?%1H?&A\:'QHB&AH*&Q\/(B(/#QL:#QL*`RH"!0@+$B8]%1$""0`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````&`H/-30L +M&@8("QD9&0L7"P<"%!04!AP<$PH?'PH5.!\*"A\*'RX=$0H;"B,!T*'PH="@,*"AT*"A4?&PH.$1$A$0X.$0("$QT*`A`8"0D8"1@D +M%B$*&A\)%@D8```4$1P3+#8L(P<%"`<"!0<$$0(&!P4+!`4$!@X3`PX#`PH# +M'0H=`QXI"AT=`S@*"@H*"@H5"@H5'Q4?'PH5(A45'R(:'Q\:+AH;'R(5(AH/ +M&QLB%1\5'A,"!087(!,F%28)%@D````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````"143!A<+&1D9"PL+!B,5%1,<$QX*"AT* +M"@HX'0H?"@H*'0HB&P\5*PD)"04$$Q4""0D8````````````&`DA"0`````` +M`!@&$QXL,@\5'`((`@4$#0(&!`((!`@&!@X=#AT='AT='0H="AT*"AT>*0H= +M"@H*.`H>"@H*"@H*'Q4:'Q\5'R(:%1\5%2(5'QL:&BX:#P\B&@H<#A$<#@@+ +M!B8/%2$H"1@````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````))@(("QD9"Q<2!!($)0\;,`H3'PH*"@HX"@H*"@H*"@H= +M"A4/,P\;#A$"`@83"@D`````````````````````````````%`8<'BPR,Q4E +M`@4"!00%!`8"!`@%!AP.*Q,="@H='@,="AT#"AT#'0,*"@H*'0H5"@H*"A4* +M'PH*'PH?&Q\;&@H*'QL?&QHB'Q4B"A\;"A4*$P($!0(&("4C%1L>&``````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M``D3(!<9%QD+"`L"*@X<$Q4;%0HX"C@*'PH?"AT*'0H=`QX5+A4L#Q4>"AL5 +M*!00```````````````````````````````8$2HP#S0U#PH&`@0(`@@%!`@$ +M`@8I'0X='@,=$P,3`QT#"@H='AT*'0,='AT*'PH="@H?'PH*%0H*%0H*%1\N +M&B(:%1\:"AH5'QH*'0X.!@(%!@02'"`F%0H""1@````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````@HC(`L+!0@" +M*A,'AX*)@8"!04(!`P"`B$.'1,I'BDI'2D= +M"AT=%1T=`QT?"@H*"@H*"@H*,!\*'Q4?%0H*"@H?'Q\*'PH:+AHB&B(/(AT> +M$Q$&`@07"`L2(Q4;&A$""1@````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````H&R,2(!((`@0"'B$1$PH*"@HX +M"AT*'0,='0H3'1,K'`('!@\S#S,L"@\S#R$````````````````````````` +M``````````<"`AP1#A,3!@@$"`4$!1$"$1TK`P,3$P,>$P,="A4?"@H>"@H* +M.`HX"C`?'1T*"@H*'Q4B"@H?%0H5%0H*"@H?%0H;"A\=*08"!0<(!A(&("`C +M"AL*%@`````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````"<5"B,7"Q<�(&!CDJ$P,=*1T*$P,3$QX.!@X" +M!0(-`@83,S(U+`\=)AH/`@`````````````````````````````````4$1,Z +M$QP'0H?"AT*"A\="AT="AT="AT*"@H;"AH*&A4? +M(@H*"@X&*@("`@0%!`87"Q(E&Q4.!R<8```````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````````````$`H5 +M,"`7!!P&!`T""`T,#`T'#`<,#00(`@8&!A,C$R8N#S8L,S(T#Q,3"@D````` +M````````````````````````````%@H5$R8E&0L7"`0$!P0$(0(.#A$.#A,I +M$S@='1,#'1X#"@H*'AT="AT#"@H5"A\*'Q4?&A\*"@H?"@H*'2L3!@('"`4$ +M"`8&(",C("45%2@````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````$0,5(QP<(R`&%P4$ +M!04"`@0"`@0%!`8$!AXP%1X)%!`A%38V,C(S%1TH&``````````````````` +M```````````````8%0\5(R`9"QD+%P8$!0(1!@("`@8A#@X.#@XK*1X=*1T= +M'AT="AT*"@H*.`H?"AT*'1T>`Q,>'1,.#A$"!`8%!`4&"P87("`F%28)$`D6 +M)``````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````!@*%24F,`85#R85)24C)2,C)3`F,!4: +M"@()!P(G`@```"0)#C0R,BPI"10````````````````````````````````` +M`!@)#ALE$AD9&0L9&0L$!`8"!P0"#0`Q,I"@,*"AT*"AT* +M'0,=`QP<#AP"$0("`@(J!@4(!0L+"Q(@(QL/%0(H"20````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````8`AL;)0H3)PH;"B8>$R,C)1P3)AX*%1L*$``````````` +M```)+B(/&R@````````````````````````````````````````)"B4C$B`9 +M&1D9(!<2!`4""`4(`@0"`B$"`@X1$PX3`RD3*1TK#A,3*1,>$PX1`@("`@T$ +M#`4(!!PA!!(&&2`@(R,E"AH*&``````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````4 +M&`(3,"8>`@D4&`D6$!`4)!00%!`)%A8)"18````````````````6"0D6```` +M```````````````````````````````````````6$`DF)2,@&0L+&2`@"PL& +M"P8%"`4&`@8"!0("!@("!@("`@4'`@T%`@(A`@8'!00%"`4(!`8%(!P@%PLC +M"ALP"1`)%A`````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````"0.'A,;&R<````` +M```````````````````````````````````````````````````````````` +M````````````````````````````%@D)`A4C&1D9(!D9&0L+"P0%!`8"!`4$ +M!`($`@("$08$!0<%"`<$!0<<`@0(!0@2"PL9&1DE,"8"%`<)"0D8```````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````)`DH`AXP)1H*"0`````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````)"B,&)1(9&1(+&1D+%PL7"Q<2!0@%"`0%!`(&`@0% +M"`@&!0@(!@0&!!<2&2`7(R4@'",>'@D````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````!@),!43)285(1`````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````"00%@(5%24@&1D2&1D9&2`9"Q<9$A(+"PL&(!(2"Q(9"PL9&2`+%R`9 +M%P0@$Q,F%0H6%!80```````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M"0H3$R8A"0D8```````````````````````````````````````````````` +M````````````````````````````````````````````````````````&"@) +M"0(5&QLC)1(@$AD7&0L9&1D+&1(2&0L9$AD9&2`E(R,5%14"#1`'$`DH"1`` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````!81"B4E$PT````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````````$`H:%2,@!A<2 +M!1(C(R`2(Q(C(!(C(!(2$@@2("`3$R4F+@D````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````8$!@)$PHE%0H)```````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````%A`8$``4%!`6'AH5&Q45)B85 +M)AH5&PH,%``4"100"1`6```````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````!`*%1,C)A43"1@````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````!0)(0DG`@DA"0(H)PDG"20````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````D +M$1\;'B,J'B<````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````&"`A08```` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````"0>"B45)@(````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````8"0H5'A4>%@`````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M`````````````````````````````````````````````````````````!00 +M%@D3)3`F(1````````````````````````````````$!`0`````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````!`0$!`0$!```` +M```````````````````````````````````````````````````````````` +M````````````````````````````````````````````)PH +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tile2bmp - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tile2bmp.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tile2bmp.mak" CFG="tile2bmp - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tile2bmp - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tile2bmp - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\tile2bmp.exe" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\util\tile2bmp.exe" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "tile2bmp - Win32 Release" +# Name "tile2bmp - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\decl.c +# End Source File +# Begin Source File + +SOURCE=..\src\drawing.c +# End Source File +# Begin Source File + +SOURCE=..\src\monst.c +# End Source File +# Begin Source File + +SOURCE=..\src\objects.c +# End Source File +# Begin Source File + +SOURCE=..\win\share\tile2bmp.c + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# ADD CPP /D "PACKED_FILE" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\win\share\tiletext.c + +!IF "$(CFG)" == "tile2bmp - Win32 Release" + +!ELSEIF "$(CFG)" == "tile2bmp - Win32 Debug" + +# ADD CPP /Zi + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\win\share\tiletxt.c +# ADD CPP /D "TILETEXT" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/tilemap.dsp b/win/win32/tilemap.dsp new file mode 100644 index 0000000..37d71ef --- /dev/null +++ b/win/win32/tilemap.dsp @@ -0,0 +1,281 @@ +# Microsoft Developer Studio Project File - Name="tilemap" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=tilemap - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tilemap.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tilemap.mak" CFG="tilemap - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tilemap - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "tilemap - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "tilemap - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\include" /I "..\sys\winnt\include" /I "..\win\share" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x1009 /d "NDEBUG" +# ADD RSC /l 0x1009 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\tilemap.exe" +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Generating src\tile.c +PostBuild_Cmds=echo chdir ..\src chdir ..\src ..\util\tilemap.exe echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ELSEIF "$(CFG)" == "tilemap - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /I "..\include" /I "..\sys\winnt\include" /I "..\win\share" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "WIN32CON" /D "DLB" /D "MSWIN_GRAPHICS" /FD /GZ /c +# ADD BASE RSC /l 0x1009 /d "_DEBUG" +# ADD RSC /l 0x1009 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no /debug /machine:I386 /out:"..\util\tilemap.exe" /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Desc=Generating src\tile.c +PostBuild_Cmds=echo chdir ..\src chdir ..\src ..\util\tilemap.exe echo chdir ..\build chdir ..\build +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "tilemap - Win32 Release" +# Name "tilemap - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\win\share\tilemap.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\include\align.h +# End Source File +# Begin Source File + +SOURCE=..\include\attrib.h +# End Source File +# Begin Source File + +SOURCE=..\include\color.h +# End Source File +# Begin Source File + +SOURCE=..\include\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\config1.h +# End Source File +# Begin Source File + +SOURCE=..\include\coord.h +# End Source File +# Begin Source File + +SOURCE=..\include\decl.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_comp.h +# End Source File +# Begin Source File + +SOURCE=..\include\dgn_file.h +# End Source File +# Begin Source File + +SOURCE=..\include\display.h +# End Source File +# Begin Source File + +SOURCE=..\include\dungeon.h +# End Source File +# Begin Source File + +SOURCE=..\include\engrave.h +# End Source File +# Begin Source File + +SOURCE=..\include\flag.h +# End Source File +# Begin Source File + +SOURCE=..\include\global.h +# End Source File +# Begin Source File + +SOURCE=..\include\mkroom.h +# End Source File +# Begin Source File + +SOURCE=..\include\monattk.h +# End Source File +# Begin Source File + +SOURCE=..\include\monst.h +# End Source File +# Begin Source File + +SOURCE=..\include\monsym.h +# End Source File +# Begin Source File + +SOURCE=..\include\nhlan.h +# End Source File +# Begin Source File + +SOURCE=..\include\ntconf.h +# End Source File +# Begin Source File + +SOURCE=..\include\obj.h +# End Source File +# Begin Source File + +SOURCE=..\include\objclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\onames.h +# End Source File +# Begin Source File + +SOURCE=..\include\permonst.h +# End Source File +# Begin Source File + +SOURCE=..\include\pm.h +# End Source File +# Begin Source File + +SOURCE=..\include\prop.h +# End Source File +# Begin Source File + +SOURCE=..\include\quest.h +# End Source File +# Begin Source File + +SOURCE=..\include\rect.h +# End Source File +# Begin Source File + +SOURCE=..\include\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\rm.h +# End Source File +# Begin Source File + +SOURCE=..\include\skills.h +# End Source File +# Begin Source File + +SOURCE=..\include\spell.h +# End Source File +# Begin Source File + +SOURCE=..\include\timeout.h +# End Source File +# Begin Source File + +SOURCE=..\include\tradstdc.h +# End Source File +# Begin Source File + +SOURCE=..\include\trampoli.h +# End Source File +# Begin Source File + +SOURCE=..\include\trap.h +# End Source File +# Begin Source File + +SOURCE=..\include\vision.h +# End Source File +# Begin Source File + +SOURCE=..\include\winprocs.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintty.h +# End Source File +# Begin Source File + +SOURCE=..\include\wintype.h +# End Source File +# Begin Source File + +SOURCE=..\include\you.h +# End Source File +# Begin Source File + +SOURCE=..\include\youprop.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/tiles.dsp b/win/win32/tiles.dsp new file mode 100644 index 0000000..ab2207a --- /dev/null +++ b/win/win32/tiles.dsp @@ -0,0 +1,97 @@ +# Microsoft Developer Studio Project File - Name="tiles" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=tiles - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "tiles.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "tiles.mak" CFG="tiles - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "tiles - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE "tiles - Win32 Debug" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "tiles - Win32 Release" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Cmd_Line "NMAKE /f tiles.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "tiles.exe" +# PROP BASE Bsc_Name "tiles.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Cmd_Line "nmake /f "tiles.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\win\win32\tiles.bmp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ELSEIF "$(CFG)" == "tiles - Win32 Debug" + +# PROP BASE Use_MFC +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Cmd_Line "NMAKE /f tiles.mak" +# PROP BASE Rebuild_Opt "/a" +# PROP BASE Target_File "tiles.exe" +# PROP BASE Bsc_Name "tiles.bsc" +# PROP BASE Target_Dir "" +# PROP Use_MFC +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Cmd_Line "nmake /f "tiles.mak"" +# PROP Rebuild_Opt "/a" +# PROP Target_File "..\win\win32\tiles.bmp" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "tiles - Win32 Release" +# Name "tiles - Win32 Debug" + +!IF "$(CFG)" == "tiles - Win32 Release" + +!ELSEIF "$(CFG)" == "tiles - Win32 Debug" + +!ENDIF + +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/tiles.mak b/win/win32/tiles.mak new file mode 100644 index 0000000..26af1ea --- /dev/null +++ b/win/win32/tiles.mak @@ -0,0 +1,21 @@ +default: all + +all: ..\win\win32\tiles.bmp + +clean: + -del ..\src\win\win32\tiles.bmp + -del ..\win\win32\tiles.bmp + +#========================================== +# Building the tiles file tile.bmp +#========================================== + +..\src\tiles.bmp : ..\win\share\monsters.txt ..\win\share\objects.txt \ + ..\win\share\other.txt + chdir ..\src + ..\util\tile2bmp.exe tiles.bmp + chdir ..\build + +..\win\win32\tiles.bmp: ..\src\tiles.bmp + @copy ..\src\tiles.bmp ..\win\win32\tiles.bmp + diff --git a/win/win32/uudecode.dsp b/win/win32/uudecode.dsp new file mode 100644 index 0000000..a9cf1be --- /dev/null +++ b/win/win32/uudecode.dsp @@ -0,0 +1,146 @@ +# Microsoft Developer Studio Project File - Name="uudecode" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=uudecode - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "uudecode.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "uudecode.mak" CFG="uudecode - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "uudecode - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "uudecode - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "uudecode - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:console /machine:I386 /out:"..\util\uudecode.exe" +# SUBTRACT LINK32 /nodefaultlib +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=echo chdir ..\win\win32 chdir ..\win\win32 \ +echo decoding icon (nhico.uu to NetHack.ico) \ +..\..\util\uudecode.exe ../../sys/winnt/nhico.uu \ +echo decoding mnsel (mnsel.uu to mnsel.bmp) \ +..\..\util\uudecode.exe mnsel.uu \ +echo decoding mnselcnt (mnselcnt.uu to mnselcnt.bmp) \ +..\..\util\uudecode.exe mnselcnt.uu \ +echo decoding mnunsel (mnunsel.uu to mnunsel.bmp) \ +..\..\util\uudecode.exe mnunsel.uu \ +echo decoding petmark (petmark.uu to petmark.bmp) \ +..\..\util\uudecode.exe petmark.uu \ +echo decoding splash (splash.uu to splash.bmp) \ +..\..\util\uudecode.exe splash.uu \ +echo decoding tombstone (rip.uu to rip.bmp) \ +..\..\util\uudecode.exe rip.uu \ +chdir ..\..\binary + +# End Special Build Tool + +!ELSEIF "$(CFG)" == "uudecode - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /nologo /subsystem:console /debug /machine:I386 /out:"..\util\uudecode.exe" /pdbtype:sept +# SUBTRACT LINK32 /nodefaultlib +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=echo chdir ..\win\win32 chdir ..\win\win32 \ +echo decoding icon (nhico.uu to NetHack.ico) \ +..\..\util\uudecode.exe ../../sys/winnt/nhico.uu \ +echo decoding mnsel (mnsel.uu to mnsel.bmp) \ +..\..\util\uudecode.exe mnsel.uu \ +echo decoding mnselcnt (mnselcnt.uu to mnselcnt.bmp) \ +..\..\util\uudecode.exe mnselcnt.uu \ +echo decoding mnunsel (mnunsel.uu to mnunsel.bmp) \ +..\..\util\uudecode.exe mnunsel.uu \ +echo decoding petmark (petmark.uu to petmark.bmp) \ +..\..\util\uudecode.exe petmark.uu \ +echo decoding splash (splash.uu to splash.bmp) \ +..\..\util\uudecode.exe splash.uu \ +echo decoding tombstone (rip.uu to rip.bmp) \ +..\..\util\uudecode.exe rip.uu \ +chdir ..\..\binary + +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "uudecode - Win32 Release" +# Name "uudecode - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\sys\share\uudecode.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/win/win32/winMS.h b/win/win32/winMS.h new file mode 100644 index 0000000..f022468 --- /dev/null +++ b/win/win32/winMS.h @@ -0,0 +1,202 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +#ifndef WINMS_H +#define WINMS_H + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include "hack.h" + +/* Create an array to keep track of the various windows */ + +#ifndef MAXWINDOWS +#define MAXWINDOWS 15 +#endif + +#define NHW_RIP 32 + +#ifndef TILE_X +#define TILE_X 16 +#endif +#define TILE_Y 16 + +#define TILES_PER_LINE 40 + +/* tile background color */ +#define TILE_BK_COLOR RGB(71, 108, 108) + +/* minimum/maximum font size (in points - 1/72 inch) */ +#define NHFONT_DEFAULT_SIZE 9 +#define NHFONT_SIZE_MIN 3 +#define NHFONT_SIZE_MAX 20 + +#define MAX_LOADSTRING 100 + +typedef struct mswin_nhwindow_data { + HWND win; + int type; + int dead; +} MSNHWinData, *PMSNHWinData; + +typedef struct mswin_nhwindow_app { + HINSTANCE hApp; + HWND hMainWnd; + HACCEL hAccelTable; + HWND hPopupWnd; /* current popup window */ + + MSNHWinData windowlist[MAXWINDOWS]; + + HBITMAP bmpTiles; + HBITMAP bmpPetMark; + HBITMAP bmpMapTiles; /* custom tiles bitmap */ + HBITMAP bmpRip; + HBITMAP bmpSplash; + int mapTile_X; /* tile width */ + int mapTile_Y; /* tile height */ + int mapTilesPerLine; /* number of tile per row in the bitmap */ + + boolean bNoHScroll; /* disable cliparound for horizontal grid (map) */ + boolean bNoVScroll; /* disable cliparound for vertical grid (map) */ + + int mapDisplayModeSave; /* saved map display mode */ + + char* saved_text; + + DWORD saveRegistrySettings; /* Flag if we should save this time */ + DWORD regNetHackMode; /* NetHack mode means no Windows keys in some places */ + + LONG regMainMinX; + LONG regMainMinY; + LONG regMainMaxX; + LONG regMainMaxY; + LONG regMainLeft; + LONG regMainTop; + LONG regMainBottom; + LONG regMainRight; + DWORD regMainShowState; +} NHWinApp, *PNHWinApp; + +#define E extern + +E PNHWinApp GetNHApp(void); +E struct window_procs mswin_procs; + +#undef E + +/* Some prototypes */ +void mswin_init_nhwindows(int* argc, char** argv); +void mswin_player_selection(void); +void mswin_askname(void); +void mswin_get_nh_event(void); +void mswin_exit_nhwindows(const char *); +void mswin_suspend_nhwindows(const char *); +void mswin_resume_nhwindows(void); +winid mswin_create_nhwindow(int type); +void mswin_clear_nhwindow(winid wid); +void mswin_display_nhwindow(winid wid, BOOLEAN_P block); +void mswin_destroy_nhwindow(winid wid); +void mswin_curs(winid wid, int x, int y); +void mswin_putstr(winid wid, int attr, const char *text); +void mswin_putstr_ex(winid wid, int attr, const char *text, int); +void mswin_display_file(const char *filename,BOOLEAN_P must_exist); +void mswin_start_menu(winid wid); +void mswin_add_menu(winid wid, int glyph, const ANY_P * identifier, + CHAR_P accelerator, CHAR_P group_accel, int attr, + const char *str, BOOLEAN_P presel); +void mswin_end_menu(winid wid, const char *prompt); +int mswin_select_menu(winid wid, int how, MENU_ITEM_P **selected); +void mswin_update_inventory(void); +void mswin_mark_synch(void); +void mswin_wait_synch(void); +void mswin_cliparound(int x, int y); +void mswin_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph); +void mswin_raw_print(const char *str); +void mswin_raw_print_bold(const char *str); +int mswin_nhgetch(void); +int mswin_nh_poskey(int *x, int *y, int *mod); +void mswin_nhbell(void); +int mswin_doprev_message(void); +char mswin_yn_function(const char *question, const char *choices, + CHAR_P def); +void mswin_getlin(const char *question, char *input); +int mswin_get_ext_cmd(void); +void mswin_number_pad(int state); +void mswin_delay_output(void); +void mswin_change_color(void); +char *mswin_get_color_string(void); +void mswin_start_screen(void); +void mswin_end_screen(void); +void mswin_outrip(winid wid, int how); +void mswin_preference_update(const char *pref); + +/* helper function */ +HWND mswin_hwnd_from_winid(winid wid); +winid mswin_winid_from_type(int type); +winid mswin_winid_from_handle(HWND hWnd); +void mswin_window_mark_dead(winid wid); +void bail(const char *mesg); +void nhapply_image_transparent( + HDC hDC, int x, int y, int width, int height, + HDC sourceDC, int s_x, int s_y, int s_width, int s_height, + COLORREF cTransparent +); + +void mswin_popup_display(HWND popup, int* done_indicator); +void mswin_popup_destroy(HWND popup); + +void mswin_read_reg(void); +void mswin_destroy_reg(void); +void mswin_write_reg(void); + +int NHMessageBox(HWND hWnd, LPCTSTR text, UINT type); + +extern HBRUSH menu_bg_brush; +extern HBRUSH menu_fg_brush; +extern HBRUSH text_bg_brush; +extern HBRUSH text_fg_brush; +extern HBRUSH status_bg_brush; +extern HBRUSH status_fg_brush; +extern HBRUSH message_bg_brush; +extern HBRUSH message_fg_brush; + +extern COLORREF menu_bg_color; +extern COLORREF menu_fg_color; +extern COLORREF text_bg_color; +extern COLORREF text_fg_color; +extern COLORREF status_bg_color; +extern COLORREF status_fg_color; +extern COLORREF message_bg_color; +extern COLORREF message_fg_color; + + +#define SYSCLR_TO_BRUSH(x) ((HBRUSH)((x) + 1)) + +/* unicode stuff */ +#ifdef UNICODE + #define NH_W2A(w, a, cb) ( WideCharToMultiByte( \ + CP_ACP, \ + 0, \ + (w), \ + -1, \ + (a), \ + (cb), \ + NULL, \ + NULL), (a) ) + + #define NH_A2W(a, w, cb) ( MultiByteToWideChar( \ + CP_ACP, \ + 0, \ + (a), \ + -1, \ + (w), \ + (cb)), (w) ) +#else + #define NH_W2A(w, a, cb) (strncpy((a), (w), (cb))) + + #define NH_A2W(a, w, cb) (strncpy((w), (a), (cb))) +#endif + +#endif /* WINmswin_H */ diff --git a/win/win32/winhack.c b/win/win32/winhack.c new file mode 100644 index 0000000..b254f47 --- /dev/null +++ b/win/win32/winhack.c @@ -0,0 +1,273 @@ +/* Copyright (C) 2001 by Alex Kompel */ +/* NetHack may be freely redistributed. See license for details. */ + +// winhack.cpp : Defines the entry point for the application. +// + +#include +#include "winMS.h" +#include "hack.h" +#include "dlb.h" +#include "resource.h" +#include "mhmain.h" +#include "mhmap.h" + +#ifndef __BORLANDC__ +#include +#else /* Borland redefines "boolean" in shlwapi.h so just use the little bit we need */ +typedef struct _DLLVERSIONINFO +{ + DWORD cbSize; + DWORD dwMajorVersion; // Major version + DWORD dwMinorVersion; // Minor version + DWORD dwBuildNumber; // Build number + DWORD dwPlatformID; // DLLVER_PLATFORM_* +} DLLVERSIONINFO; + +// +// The caller should always GetProcAddress("DllGetVersion"), not +// implicitly link to it. +// + +typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *); + +#endif + +#ifdef OVL0 +#define SHARED_DCL +#else +#define SHARED_DCL extern +#endif + +/* Minimal common control library version +Version _WIN_32IE Platform/IE +======= ========= =========== +4.00 0x0200 Microsoft(r) Windows 95/Windows NT 4.0 +4.70 0x0300 Microsoft(r) Internet Explorer 3.x +4.71 0x0400 Microsoft(r) Internet Explorer 4.0 +4.72 0x0401 Microsoft(r) Internet Explorer 4.01 +...and probably going on infinitely... +*/ +#define MIN_COMCTLMAJOR 4 +#define MIN_COMCTLMINOR 71 +#define INSTALL_NOTES "http://www.nethack.org/v340/ports/download-win.html#cc" +/*#define COMCTL_URL "http://www.microsoft.com/msdownload/ieplatform/ie/comctrlx86.asp"*/ + +extern void FDECL(nethack_exit,(int)); +static TCHAR* _get_cmd_arg(TCHAR* pCmdLine); +static HRESULT GetComCtlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor); + + +// Global Variables: +NHWinApp _nethack_app; + +#ifdef __BORLANDC__ +#define _stricmp(s1,s2) stricmp(s1,s2) +#define _strdup(s1) strdup(s1) +#endif + +// Foward declarations of functions included in this code module: +extern void FDECL(pcmain, (int,char **)); +static void __cdecl mswin_moveloop(void *); + +#define MAX_CMDLINE_PARAM 255 + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + INITCOMMONCONTROLSEX InitCtrls; + int argc; + char* argv[MAX_CMDLINE_PARAM]; + size_t len; + TCHAR *p; + TCHAR wbuf[BUFSZ]; + char buf[BUFSZ]; + DWORD major, minor; + + + /* ensure that we don't access violate on a panic() */ + windowprocs.win_raw_print = mswin_raw_print; + windowprocs.win_raw_print_bold = mswin_raw_print_bold; + + /* init applicatio structure */ + _nethack_app.hApp = hInstance; + _nethack_app.hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_NETHACKW); + _nethack_app.hMainWnd = NULL; + _nethack_app.hPopupWnd = NULL; + _nethack_app.bmpTiles = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_TILES)); + if( _nethack_app.bmpTiles==NULL ) panic("cannot load tiles bitmap"); + _nethack_app.bmpPetMark = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_PETMARK)); + if( _nethack_app.bmpPetMark==NULL ) panic("cannot load pet mark bitmap"); + _nethack_app.bmpRip = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_RIP)); + if ( _nethack_app.bmpRip == NULL ) panic("cannot load rip bitmap"); + _nethack_app.bmpSplash = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_SPLASH)); + if ( _nethack_app.bmpSplash == NULL ) panic("cannot load splash bitmap"); + _nethack_app.bmpMapTiles = _nethack_app.bmpTiles; + _nethack_app.mapTile_X = TILE_X; + _nethack_app.mapTile_Y = TILE_Y; + _nethack_app.mapTilesPerLine = TILES_PER_LINE; + + _nethack_app.bNoHScroll = FALSE; + _nethack_app.bNoVScroll = FALSE; + _nethack_app.saved_text = strdup(""); + + // init controls + if (FAILED(GetComCtlVersion(&major, &minor))) + { + char buf[TBUFSZ]; + Sprintf(buf, "Cannot load common control library.\n%s\n%s", + "For further information, refer to the installation notes at", + INSTALL_NOTES); + panic(buf); + } + if (major < MIN_COMCTLMAJOR + || (major == MIN_COMCTLMAJOR && minor < MIN_COMCTLMINOR)) + { + char buf[TBUFSZ]; + Sprintf(buf, "Common control library is outdated.\n%s %d.%d\n%s\n%s", + "NetHack requires at least version ", + MIN_COMCTLMAJOR, MIN_COMCTLMINOR, + "For further information, refer to the installation notes at", + INSTALL_NOTES); + panic(buf); + } + ZeroMemory(&InitCtrls, sizeof(InitCtrls)); + InitCtrls.dwSize = sizeof(InitCtrls); + InitCtrls.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&InitCtrls); + + /* get command line parameters */ + p = _get_cmd_arg(GetCommandLine()); + p = _get_cmd_arg(NULL); /* skip first paramter - command name */ + for( argc = 1; p && argc0 ) { + argv[argc] = _strdup( NH_W2A(p, buf, BUFSZ) ); + } else { + argv[argc] = ""; + } + p = _get_cmd_arg(NULL); + } + GetModuleFileName(NULL, wbuf, BUFSZ); + argv[0] = _strdup(NH_W2A(wbuf, buf, BUFSZ)); + + if (argc == 2) { + TCHAR *savefile = strdup(argv[1]); + TCHAR *plname; + for (p = savefile; *p && *p != '-'; p++) + ; + if (*p) { + /* we found a '-' */ + plname = p + 1; + for (p = plname; *p && *p != '.'; p++) + ; + if (*p) { + if (strcmp(p + 1, "NetHack-saved-game") == 0) { + *p = '\0'; + argv[1] = "-u"; + argv[2] = _strdup(plname); + argc = 3; + } + } + } + free(savefile); + } + pcmain(argc,argv); + + moveloop(); + + return 0; +} + + +PNHWinApp GetNHApp() +{ + return &_nethack_app; +} + +TCHAR* _get_cmd_arg(TCHAR* pCmdLine) +{ + static TCHAR* pArgs = NULL; + TCHAR *pRetArg; + BOOL bQuoted; + + if( !pCmdLine && !pArgs ) return NULL; + if( !pArgs ) pArgs = pCmdLine; + + /* skip whitespace */ + for(pRetArg = pArgs; *pRetArg && _istspace(*pRetArg); pRetArg = CharNext(pRetArg)); + if( !*pRetArg ) { + pArgs = NULL; + return NULL; + } + + /* check for quote */ + if( *pRetArg==TEXT('"') ) { + bQuoted = TRUE; + pRetArg = CharNext(pRetArg); + pArgs = _tcschr(pRetArg, TEXT('"')); + } else { + /* skip to whitespace */ + for(pArgs = pRetArg; *pArgs && !_istspace(*pArgs); pArgs = CharNext(pArgs)); + } + + if( pArgs && *pArgs ) { + TCHAR* p; + p = pArgs; + pArgs = CharNext(pArgs); + *p = (TCHAR)0; + } else { + pArgs = NULL; + } + + return pRetArg; +} + +/* Get the version of the Common Control library on this machine. + Copied from the Microsoft SDK + */ +HRESULT GetComCtlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor) +{ + HINSTANCE hComCtl; + HRESULT hr = S_OK; + DLLGETVERSIONPROC pDllGetVersion; + + if(IsBadWritePtr(pdwMajor, sizeof(DWORD)) || + IsBadWritePtr(pdwMinor, sizeof(DWORD))) + return E_INVALIDARG; + //load the DLL + hComCtl = LoadLibrary(TEXT("comctl32.dll")); + if (!hComCtl) return E_FAIL; + + /* + You must get this function explicitly because earlier versions of the DLL + don't implement this function. That makes the lack of implementation of the + function a version marker in itself. + */ + pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hComCtl, TEXT("DllGetVersion")); + if(pDllGetVersion) { + DLLVERSIONINFO dvi; + ZeroMemory(&dvi, sizeof(dvi)); + dvi.cbSize = sizeof(dvi); + hr = (*pDllGetVersion)(&dvi); + if(SUCCEEDED(hr)) { + *pdwMajor = dvi.dwMajorVersion; + *pdwMinor = dvi.dwMinorVersion; + } else { + hr = E_FAIL; + } + } else { + /* + If GetProcAddress failed, then the DLL is a version previous to the one + shipped with IE 3.x. + */ + *pdwMajor = 4; + *pdwMinor = 0; + } + FreeLibrary(hComCtl); + return hr; +} + + diff --git a/win/win32/winhack.rc b/win/win32/winhack.rc new file mode 100644 index 0000000..f4dd40c --- /dev/null +++ b/win/win32/winhack.rc @@ -0,0 +1,380 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#if defined(__BORLANDC__) +LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US +#endif +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_NETHACKW ICON DISCARDABLE "NETHACK.ICO" + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDC_NETHACKW MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Save and Exit", IDM_SAVE + MENUITEM SEPARATOR + MENUITEM "&Quit", IDM_EXIT + END + POPUP "&Map" + BEGIN + MENUITEM "&0 - Use Tiles", IDM_MAP_TILES + MENUITEM "&1 - ASCII (4x6)", IDM_MAP_ASCII4X6 + MENUITEM "&2 - ASCII (6x8)", IDM_MAP_ASCII6X8 + MENUITEM "&3 - ASCII (8x8)", IDM_MAP_ASCII8X8 + MENUITEM "&4 - ASCII (16x8)", IDM_MAP_ASCII16X8 + MENUITEM "&5 - ASCII (7x12)", IDM_MAP_ASCII7X12 + MENUITEM "&6 - ASCII (8x12)", IDM_MAP_ASCII8X12 + MENUITEM "&7 - ASCII (16x12)", IDM_MAP_ASCII16X12 + MENUITEM "&8 - ASCII (12x16)", IDM_MAP_ASCII12X16 + MENUITEM "&9 - ASCII (10x18)", IDM_MAP_ASCII10X18 + MENUITEM SEPARATOR + MENUITEM "&Fit To Screen ", IDM_MAP_FIT_TO_SCREEN + END + POPUP "Windows &Settings" + BEGIN + MENUITEM "NetHack Mode", IDM_NHMODE + MENUITEM SEPARATOR + MENUITEM "&Clear All Settings", IDM_CLEARSETTINGS + END + POPUP "&Help" + BEGIN + MENUITEM "&About ...", IDM_ABOUT + MENUITEM "&Long description of the game", IDM_HELP_LONG + MENUITEM "List of &commands", IDM_HELP_COMMANDS + MENUITEM "&History of NetHack", IDM_HELP_HISTORY + MENUITEM "&Info on a character", IDM_HELP_INFO_CHAR + MENUITEM "Info on what a given &key does", IDM_HELP_INFO_KEY + MENUITEM "List of game &options", IDM_HELP_OPTIONS + MENUITEM "&Longer list of game options", IDM_HELP_OPTIONS_LONG + MENUITEM "List of e&xtended commands", IDM_HELP_EXTCMD + MENUITEM "The &NetHack license", IDM_HELP_LICENSE + MENUITEM "NetHack for &Windows help", IDM_HELP_PORTHELP + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDC_NETHACKW ACCELERATORS MOVEABLE PURE +BEGIN + "?", IDM_ABOUT, ASCII, ALT + "/", IDM_ABOUT, ASCII, ALT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75 +STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU +CAPTION "About" +FONT 8, "System" +BEGIN + LTEXT "NetHack",IDC_ABOUT_VERSION,10,10,170,15,SS_NOPREFIX + LTEXT "Copyright",IDC_ABOUT_COPYRIGHT,10,30,210,40 + DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP +END + +IDD_NHTEXT DIALOGEX 0, 0, 172, 178 +STYLE DS_SETFOREGROUND | WS_CHILD | WS_THICKFRAME +EXSTYLE WS_EX_STATICEDGE +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,54,163,50,14 + EDITTEXT IDC_TEXT_CONTROL,0,0,170,160,ES_MULTILINE | + ES_OEMCONVERT | ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +IDD_MENU DIALOGEX 0, 0, 187, 153 +STYLE WS_CHILD | WS_CLIPSIBLINGS | WS_THICKFRAME +EXSTYLE WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT | WS_EX_STATICEDGE +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,7,132,50,14,BS_FLAT + PUSHBUTTON "Cancel",IDCANCEL,130,132,50,14,BS_FLAT + LISTBOX IDC_MENU_LIST,10,10,170,55,LBS_SORT | WS_TABSTOP + EDITTEXT IDC_MENU_TEXT,10,70,170,60,ES_MULTILINE | ES_OEMCONVERT | + ES_READONLY | WS_VSCROLL | WS_HSCROLL +END + +IDD_GETLIN DIALOG DISCARDABLE 0, 0, 131, 29 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Question?" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,0,15,65,14 + PUSHBUTTON "Cancel",IDCANCEL,65,15,65,14 + EDITTEXT IDC_GETLIN_EDIT,0,0,130,13,ES_AUTOHSCROLL +END + +IDD_EXTCMD DIALOG DISCARDABLE 0, 0, 137, 117 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Extended Commands" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,80,7,50,14 + PUSHBUTTON "Cancel",IDCANCEL,80,24,50,14 + LISTBOX IDC_EXTCMD_LIST,7,7,65,103,LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_PLAYER_SELECTOR DIALOG DISCARDABLE 0, 0, 152, 169 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "What are you?" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Play",IDOK,7,148,66,14 + PUSHBUTTON "Quit",IDCANCEL,79,148,66,14 + LTEXT "Name:",IDC_STATIC,7,8,25,10 + EDITTEXT IDC_PLSEL_NAME,40,7,105,12,ES_AUTOHSCROLL | ES_READONLY | + NOT WS_TABSTOP + GROUPBOX "Role",IDC_STATIC,7,21,138,30 + CONTROL "Random",IDC_PLSEL_ROLE_RANDOM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,34,40,10 + COMBOBOX IDC_PLSEL_ROLE_LIST,63,33,75,50,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Race",IDC_STATIC,7,51,138,30 + CONTROL "Random",IDC_PLSEL_RACE_RANDOM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,14,63,40,10 + COMBOBOX IDC_PLSEL_RACE_LIST,63,62,75,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Gender",IDC_STATIC,7,81,138,30 + CONTROL "Random",IDC_PLSEL_GENDER_RANDOM,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,14,93,40,10 + COMBOBOX IDC_PLSEL_GENDER_LIST,63,92,75,40,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + GROUPBOX "Alignment",IDC_STATIC,7,111,138,30 + CONTROL "Random",IDC_PLSEL_ALIGN_RANDOM,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,13,123,40,10 + COMBOBOX IDC_PLSEL_ALIGN_LIST,63,122,75,45,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP +END + +IDD_NHRIP DIALOGEX 0, 0, 281, 209 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Here lies..." +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,82,188,50,14 +END + +IDD_SPLASH DIALOG DISCARDABLE 0, 0, 281, 257 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Welcome to NetHack" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,224,236,50,14 + EDITTEXT IDC_EXTRAINFO,7,176,267,52,ES_MULTILINE | ES_READONLY | + WS_VSCROLL +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#if defined(__BORLANDC__)\r\n" + "LANGUAGE LANG_ENGLISH,SUBLANG_ENGLISH_US\r\n" + "#endif\r\n" + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TILES BITMAP DISCARDABLE "tiles.bmp" +IDB_MENU_SEL BITMAP DISCARDABLE "mnsel.bmp" +IDB_MENU_UNSEL BITMAP DISCARDABLE "mnunsel.bmp" +IDB_PETMARK BITMAP DISCARDABLE "petmark.bmp" +IDB_MENU_SEL_COUNT BITMAP DISCARDABLE "mnselcnt.bmp" +IDB_RIP BITMAP DISCARDABLE "rip.bmp" +IDB_SPLASH BITMAP DISCARDABLE "splash.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_NHTEXT, DIALOG + BEGIN + BOTTOMMARGIN, 177 + END + + IDD_MENU, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 180 + TOPMARGIN, 7 + BOTTOMMARGIN, 146 + END + + IDD_GETLIN, DIALOG + BEGIN + BOTTOMMARGIN, 22 + END + + IDD_EXTCMD, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 130 + TOPMARGIN, 7 + BOTTOMMARGIN, 110 + END + + IDD_PLAYER_SELECTOR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 145 + TOPMARGIN, 7 + BOTTOMMARGIN, 162 + END + + IDD_NHRIP, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 274 + TOPMARGIN, 7 + BOTTOMMARGIN, 202 + END + + IDD_SPLASH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 274 + BOTTOMMARGIN, 250 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,4,3,0 + PRODUCTVERSION 3,4,3,0 + FILEFLAGSMASK 0x1fL +#ifdef _DEBUG + FILEFLAGS 0x9L +#else + FILEFLAGS 0x8L +#endif + FILEOS 0x4L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "NetHack for Windows - Graphical Interface\0" + VALUE "FileVersion", "3.4.3\0" + VALUE "InternalName", "NetHackW\0" + VALUE "LegalCopyright", "Copyright (C) 1985 - 2003. By Stichting Mathematisch Centrum and M. Stephenson. See license for details.\0" + VALUE "OriginalFilename", "NetHackW.exe\0" + VALUE "PrivateBuild", "031014\0" + VALUE "ProductName", "NetHack\0" + VALUE "ProductVersion", "3.4.3\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_APP_TITLE "NetHack for Windows - Graphical Interface" + IDC_NETHACKW "NETHACKW" + IDS_APP_TITLE_SHORT "NetHack for Windows" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + -- 2.11.0